ASoc: msm: Add low latency playback and recording support.
- Add lowlatency pcm driver for Playback and Recording.
- Add support in target board files
- Add Recording Path to Multimedia5 FE DAI
- Add support in routing, platform, machine drivers
- Add low latency interfaces support in ASM and ADM drivers.
Change-Id: I1beb11db9010534e5aa91179ac6040a41622185d
Signed-off-by: Jayasena Sangaraboina <jsanga@codeaurora.org>
diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c
index 68a85c0..ab9004f 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -2242,6 +2242,7 @@
&apq_lpa_pcm,
&apq_compr_dsp,
&apq_multi_ch_pcm,
+ &apq_lowlatency_pcm,
&apq_pcm_hostless,
&apq_cpudai_afe_01_rx,
&apq_cpudai_afe_01_tx,
diff --git a/arch/arm/mach-msm/board-8930.c b/arch/arm/mach-msm/board-8930.c
index d5014bd..a7147b5 100644
--- a/arch/arm/mach-msm/board-8930.c
+++ b/arch/arm/mach-msm/board-8930.c
@@ -2298,6 +2298,7 @@
&msm_cpudai_incall_record_tx,
&msm_pcm_hostless,
&msm_multi_ch_pcm,
+ &msm_lowlatency_pcm,
};
static void __init msm8930_i2c_init(void)
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index 22a5021..641c144 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -2695,6 +2695,7 @@
&android_usb_device,
&msm_pcm,
&msm_multi_ch_pcm,
+ &msm_lowlatency_pcm,
&msm_pcm_routing,
&msm_cpudai0,
&msm_cpudai1,
diff --git a/arch/arm/mach-msm/devices-8064.c b/arch/arm/mach-msm/devices-8064.c
index e5261b0..c6a0441 100644
--- a/arch/arm/mach-msm/devices-8064.c
+++ b/arch/arm/mach-msm/devices-8064.c
@@ -736,6 +736,11 @@
.id = -1,
};
+struct platform_device apq_lowlatency_pcm = {
+ .name = "msm-lowlatency-pcm-dsp",
+ .id = -1,
+};
+
struct platform_device apq_pcm_hostless = {
.name = "msm-pcm-hostless",
.id = -1,
diff --git a/arch/arm/mach-msm/devices-8960.c b/arch/arm/mach-msm/devices-8960.c
index ecfafa8..8a4b3f4 100644
--- a/arch/arm/mach-msm/devices-8960.c
+++ b/arch/arm/mach-msm/devices-8960.c
@@ -1954,6 +1954,11 @@
.id = -1,
};
+struct platform_device msm_lowlatency_pcm = {
+ .name = "msm-lowlatency-pcm-dsp",
+ .id = -1,
+};
+
struct platform_device msm_pcm_routing = {
.name = "msm-pcm-routing",
.id = -1,
diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h
index 55c1e03..3587672 100644
--- a/arch/arm/mach-msm/devices.h
+++ b/arch/arm/mach-msm/devices.h
@@ -206,6 +206,7 @@
extern struct platform_device msm_pcm;
extern struct platform_device msm_multi_ch_pcm;
+extern struct platform_device msm_lowlatency_pcm;
extern struct platform_device msm_pcm_routing;
extern struct platform_device msm_cpudai0;
extern struct platform_device msm_cpudai1;
@@ -269,6 +270,7 @@
extern struct platform_device apq_lpa_pcm;
extern struct platform_device apq_compr_dsp;
extern struct platform_device apq_multi_ch_pcm;
+extern struct platform_device apq_lowlatency_pcm;
extern struct platform_device apq_pcm_hostless;
extern struct platform_device apq_cpudai_afe_01_rx;
extern struct platform_device apq_cpudai_afe_01_tx;
diff --git a/include/sound/apr_audio.h b/include/sound/apr_audio.h
index 979db58..b7ddb08 100644
--- a/include/sound/apr_audio.h
+++ b/include/sound/apr_audio.h
@@ -561,6 +561,7 @@
#define ADM_CMD_COPP_CLOSE 0x00010305
#define ADM_CMD_MULTI_CHANNEL_COPP_OPEN 0x00010310
+#define ADM_CMD_MULTI_CHANNEL_COPP_OPEN_V3 0x00010333
struct adm_multi_ch_copp_open_command {
struct apr_hdr hdr;
u16 flags;
@@ -573,7 +574,6 @@
u32 rate;
u8 dev_channel_mapping[8];
} __packed;
-
#define ADM_CMD_MEMORY_MAP 0x00010C30
struct adm_cmd_memory_map{
struct apr_hdr hdr;
@@ -618,6 +618,14 @@
#define VPM_TX_DM_FLUENCE_COPP_TOPOLOGY 0x00010F72
#define VPM_TX_QMIC_FLUENCE_COPP_TOPOLOGY 0x00010F75
+#define LOWLATENCY_POPP_TOPOLOGY 0x00010C68
+#define LOWLATENCY_COPP_TOPOLOGY 0x00010312
+#define PCM_BITS_PER_SAMPLE 16
+
+#define ASM_OPEN_WRITE_PERF_MODE_BIT (1<<28)
+#define ASM_OPEN_READ_PERF_MODE_BIT (1<<29)
+#define ADM_MULTI_CH_COPP_OPEN_PERF_MODE_BIT (1<<13)
+
/* SRS TRUMEDIA GUIDS */
/* topology */
#define SRS_TRUMEDIA_TOPOLOGY_ID 0x00010D90
@@ -741,6 +749,7 @@
} __attribute__ ((packed));
#define ADM_CMDRSP_MULTI_CHANNEL_COPP_OPEN 0x00010311
+#define ADM_CMDRSP_MULTI_CHANNEL_COPP_OPEN_V3 0x00010334
#define ASM_STREAM_PRIORITY_NORMAL 0
@@ -1102,6 +1111,7 @@
/* Stream level commands */
#define ASM_STREAM_CMD_OPEN_READ 0x00010BCB
+#define ASM_STREAM_CMD_OPEN_READ_V2_1 0x00010DB2
struct asm_stream_cmd_open_read {
struct apr_hdr hdr;
u32 uMode;
@@ -1110,6 +1120,16 @@
u32 format;
} __attribute__((packed));
+struct asm_stream_cmd_open_read_v2_1 {
+ struct apr_hdr hdr;
+ u32 uMode;
+ u32 src_endpoint;
+ u32 pre_proc_top;
+ u32 format;
+ u16 bits_per_sample;
+ u16 reserved;
+} __packed;
+
/* Supported formats */
#define LINEAR_PCM 0x00010BE5
#define DTMF 0x00010BE6
@@ -1158,6 +1178,7 @@
} __packed;
#define ASM_STREAM_CMD_OPEN_WRITE 0x00010BCA
+#define ASM_STREAM_CMD_OPEN_WRITE_V2_1 0x00010DB1
struct asm_stream_cmd_open_write {
struct apr_hdr hdr;
u32 uMode;
diff --git a/include/sound/q6adm.h b/include/sound/q6adm.h
index 8e15955..676c4cb 100644
--- a/include/sound/q6adm.h
+++ b/include/sound/q6adm.h
@@ -27,7 +27,7 @@
int adm_open(int port, int path, int rate, int mode, int topology);
int adm_multi_ch_copp_open(int port, int path, int rate, int mode,
- int topology);
+ int topology, int perfmode);
int adm_memory_map_regions(uint32_t *buf_add, uint32_t mempool_id,
uint32_t *bufsz, uint32_t bufcnt);
diff --git a/include/sound/q6asm.h b/include/sound/q6asm.h
index ea77974..01f2fac 100644
--- a/include/sound/q6asm.h
+++ b/include/sound/q6asm.h
@@ -160,6 +160,7 @@
uint32_t io_mode;
uint64_t time_stamp;
atomic_t cmd_response;
+ bool perf_mode;
};
void q6asm_audio_client_free(struct audio_client *ac);
@@ -182,6 +183,7 @@
struct audio_client *ac);
int q6asm_open_read(struct audio_client *ac, uint32_t format);
+int q6asm_open_read_v2_1(struct audio_client *ac, uint32_t format);
int q6asm_open_read_compressed(struct audio_client *ac,
uint32_t frames_per_buffer, uint32_t meta_data_mode);
diff --git a/sound/soc/msm/Makefile b/sound/soc/msm/Makefile
index 43c678d..390c314 100644
--- a/sound/soc/msm/Makefile
+++ b/sound/soc/msm/Makefile
@@ -56,7 +56,7 @@
obj-$(CONFIG_SND_SOC_MSM_QDSP6_INTF) += qdsp6/
-snd-soc-qdsp6-objs := msm-dai-q6.o msm-pcm-q6.o msm-multi-ch-pcm-q6.o msm-pcm-routing.o msm-dai-fe.o msm-compr-q6.o msm-dai-stub.o
+snd-soc-qdsp6-objs := msm-dai-q6.o msm-pcm-q6.o msm-multi-ch-pcm-q6.o msm-lowlatency-pcm-q6.o msm-pcm-routing.o msm-dai-fe.o msm-compr-q6.o msm-dai-stub.o
obj-$(CONFIG_SND_SOC_MSM_QDSP6_HDMI_AUDIO) += msm-dai-q6-hdmi.o
obj-$(CONFIG_SND_SOC_VOICE) += msm-pcm-voice.o msm-pcm-voip.o
snd-soc-qdsp6-objs += msm-pcm-lpa.o msm-pcm-afe.o
diff --git a/sound/soc/msm/apq8064.c b/sound/soc/msm/apq8064.c
index 81bde3f..2959fe0 100644
--- a/sound/soc/msm/apq8064.c
+++ b/sound/soc/msm/apq8064.c
@@ -1678,6 +1678,37 @@
.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,
+ .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 = "MSM8960 LowLatency",
+ .stream_name = "MultiMedia5",
+ .cpu_dai_name = "MultiMedia5",
+ .platform_name = "msm-lowlatency-pcm-dsp",
+ .dynamic = 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,
+ },
/* Backend DAI Links */
{
.name = LPASS_BE_SLIMBUS_0_RX,
diff --git a/sound/soc/msm/msm-compr-q6.c b/sound/soc/msm/msm-compr-q6.c
index 35cbb5b..1dbd698 100644
--- a/sound/soc/msm/msm-compr-q6.c
+++ b/sound/soc/msm/msm-compr-q6.c
@@ -548,6 +548,7 @@
}
prtd = &compr->prtd;
prtd->substream = substream;
+ prtd->audio_client->perf_mode = false;
prtd->audio_client = q6asm_audio_client_alloc(
(app_cb)compr_event_handler, compr);
if (!prtd->audio_client) {
@@ -768,7 +769,9 @@
}
msm_pcm_routing_reg_phy_stream(
soc_prtd->dai_link->be_id,
- prtd->session_id, substream->stream);
+ prtd->audio_client->perf_mode,
+ prtd->session_id,
+ substream->stream);
break;
}
diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c
index 4cd4a2c..011ff29 100644
--- a/sound/soc/msm/msm-dai-fe.c
+++ b/sound/soc/msm/msm-dai-fe.c
@@ -203,6 +203,17 @@
.rate_min = 8000,
.rate_max = 48000,
},
+ .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,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
.ops = &msm_fe_Multimedia_dai_ops,
.name = "MultiMedia5",
},
diff --git a/sound/soc/msm/msm-lowlatency-pcm-q6.c b/sound/soc/msm/msm-lowlatency-pcm-q6.c
new file mode 100644
index 0000000..129f69f
--- /dev/null
+++ b/sound/soc/msm/msm-lowlatency-pcm-q6.c
@@ -0,0 +1,756 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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/android_pmem.h>
+#include <asm/dma.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 "msm-pcm-q6.h"
+#include "msm-pcm-routing.h"
+
+static struct audio_locks the_locks;
+
+struct snd_msm {
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+};
+
+struct snd_msm_volume {
+ struct msm_audio *prtd;
+ unsigned volume;
+};
+
+#define PLAYBACK_NUM_PERIODS 4
+#define PLAYBACK_MAX_PERIOD_SIZE 1024
+#define PLAYBACK_MIN_PERIOD_SIZE 512
+#define CAPTURE_NUM_PERIODS 4
+#define CAPTURE_MIN_PERIOD_SIZE 128
+#define CAPTURE_MAX_PERIOD_SIZE 1024
+
+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,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 8,
+ .buffer_bytes_max = CAPTURE_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
+ .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = CAPTURE_NUM_PERIODS,
+ .periods_max = CAPTURE_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,
+ .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 6,
+ .buffer_bytes_max = PLAYBACK_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE,
+ .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,
+ .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE,
+ .periods_min = PLAYBACK_NUM_PERIODS,
+ .periods_max = PLAYBACK_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
+};
+
+static uint32_t in_frame_info[CAPTURE_NUM_PERIODS][2];
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+
+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;
+ int i = 0;
+ uint32_t idx = 0;
+ uint32_t size = 0;
+
+ pr_debug("%s\n", __func__);
+ switch (opcode) {
+ case ASM_DATA_EVENT_WRITE_DONE: {
+ pr_debug("ASM_DATA_EVENT_WRITE_DONE\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)
+ 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_CMDRSP_EOS:
+ pr_debug("ASM_DATA_CMDRSP_EOS\n");
+ prtd->cmd_ack = 1;
+ wake_up(&the_locks.eos_wait);
+ break;
+ case ASM_DATA_EVENT_READ_DONE: {
+ pr_debug("ASM_DATA_EVENT_READ_DONE\n");
+ pr_debug("token = 0x%08x\n", token);
+ for (i = 0; i < 8; i++, ++ptrmem)
+ pr_debug("cmd[%d]=0x%08x\n", i, *ptrmem);
+ in_frame_info[token][0] = payload[2];
+ in_frame_info[token][1] = payload[3];
+ prtd->pcm_irq_pos += in_frame_info[token][0];
+ 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))
+ q6asm_read_nolock(prtd->audio_client);
+ break;
+ }
+ case APR_BASIC_RSP_RESULT: {
+ switch (payload[0]) {
+ case ASM_SESSION_CMD_RUN:
+ if (substream->stream
+ != SNDRV_PCM_STREAM_PLAYBACK) {
+ atomic_set(&prtd->start, 1);
+ break;
+ }
+ if (prtd->mmap_flag) {
+ pr_debug("%s:writing %d bytes 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 bytesto 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;
+ default:
+ break;
+ }
+ }
+ 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 msm_audio *prtd = runtime->private_data;
+ int ret;
+
+ 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;
+
+ ret = q6asm_media_format_block_multi_ch_pcm(prtd->audio_client,
+ runtime->rate, runtime->channels);
+ if (ret < 0)
+ pr_info("%s: CMD Format block failed\n", __func__);
+
+ atomic_set(&prtd->out_count, runtime->periods);
+
+ prtd->enabled = 1;
+ prtd->cmd_ack = 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;
+ int ret = 0;
+ int i = 0;
+ 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;
+
+ pr_debug("Samp_rate = %d\n", prtd->samp_rate);
+ pr_debug("Channel = %d\n", prtd->channel_mode);
+ ret = q6asm_enc_cfg_blk_pcm(prtd->audio_client, prtd->samp_rate,
+ prtd->channel_mode);
+ if (ret < 0)
+ pr_debug("%s: cmd cfg pcm was block failed", __func__);
+
+ for (i = 0; i < runtime->periods; i++)
+ q6asm_read(prtd->audio_client);
+ prtd->periods = runtime->periods;
+
+ prtd->enabled = 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 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__);
+ 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)
+ break;
+ prtd->cmd_ack = 0;
+ q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
+ 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;
+ int ret = 0;
+ pr_debug("%s lowlatency\n", __func__);
+ 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_err("%s: Could not allocate memory\n", __func__);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ prtd->audio_client->perf_mode = true;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ runtime->hw = msm_pcm_hardware_playback;
+ ret = q6asm_open_write(prtd->audio_client,
+ FORMAT_MULTI_CHANNEL_LINEAR_PCM);
+ if (ret < 0) {
+ pr_err("%s: pcm out open failed\n", __func__);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ }
+ /* Capture path */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw = msm_pcm_hardware_capture;
+ ret = q6asm_open_read_v2_1(prtd->audio_client,
+ FORMAT_LINEAR_PCM);
+ if (ret < 0) {
+ pr_err("%s: pcm in open failed\n", __func__);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ }
+
+ pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session);
+
+ prtd->session_id = prtd->audio_client->session;
+ msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
+ prtd->audio_client->perf_mode,
+ prtd->session_id, substream->stream);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ prtd->cmd_ack = 1;
+
+ 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_PLAYBACK) {
+ ret = snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ PLAYBACK_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE,
+ PLAYBACK_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_NUM_PERIODS * CAPTURE_MIN_PERIOD_SIZE,
+ CAPTURE_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE);
+ if (ret < 0) {
+ pr_err("constraint for buffer bytes min max ret = %d\n",
+ ret);
+ }
+ }
+
+ prtd->dsp_cnt = 0;
+ runtime->private_data = prtd;
+ pr_debug("substream->pcm->device = %d\n", substream->pcm->device);
+ pr_debug("soc_prtd->dai_link->be_id = %d\n", soc_prtd->dai_link->be_id);
+ 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;
+
+ 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));
+ ret = wait_event_timeout(the_locks.write_wait,
+ (atomic_read(&prtd->out_count)), 5 * HZ);
+ if (ret < 0) {
+ pr_err("%s: wait_event_timeout failed\n", __func__);
+ goto fail;
+ }
+
+ 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);
+ bufptr = data;
+ if (bufptr) {
+ pr_debug("%s:fbytes =%d: xfer=%d size=%d\n",
+ __func__, fbytes, xfer, size);
+ xfer = fbytes;
+ if (copy_from_user(bufptr, buf, xfer)) {
+ ret = -EFAULT;
+ 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;
+ goto fail;
+ }
+ } else
+ atomic_inc(&prtd->out_needed);
+ atomic_dec(&prtd->out_count);
+ }
+fail:
+ 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;
+ int dir = 0;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+
+ dir = IN;
+ ret = wait_event_timeout(the_locks.eos_wait,
+ prtd->cmd_ack, 5 * HZ);
+ if (ret < 0)
+ pr_err("%s: CMD_EOS failed\n", __func__);
+ q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+ q6asm_audio_client_buf_free_contiguous(dir,
+ prtd->audio_client);
+
+ msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ 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);
+
+ ret = wait_event_timeout(the_locks.read_wait,
+ (atomic_read(&prtd->in_count)), 5 * HZ);
+ if (ret < 0) {
+ pr_debug("%s: wait_event_timeout failed\n", __func__);
+ goto fail;
+ }
+ 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...%08x\n",
+ (unsigned int) 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 = in_frame_info[idx][1];
+ 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;
+ goto fail;
+ }
+ fbytes -= xfer;
+ size -= xfer;
+ in_frame_info[idx][1] += xfer;
+ pr_debug("%s:fbytes = %d: size=%d: xfer=%d\n",
+ __func__, fbytes, size, xfer);
+ pr_debug(" Sending next buffer to dsp\n");
+ memset(&in_frame_info[idx], 0,
+ sizeof(uint32_t) * 2);
+ atomic_dec(&prtd->in_count);
+ ret = q6asm_read(prtd->audio_client);
+ if (ret < 0) {
+ pr_err("q6asm read failed\n");
+ ret = -EFAULT;
+ 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;
+
+ pr_debug("%s\n", __func__);
+ q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+ q6asm_audio_client_buf_free_contiguous(dir,
+ prtd->audio_client);
+ msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
+ SNDRV_PCM_STREAM_CAPTURE);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+
+ 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)
+{
+ int result = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ pr_debug("%s\n", __func__);
+ prtd->mmap_flag = 1;
+
+ if (runtime->dma_addr && runtime->dma_bytes) {
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ result = remap_pfn_range(vma, vma->vm_start,
+ runtime->dma_addr >> PAGE_SHIFT,
+ runtime->dma_bytes,
+ vma->vm_page_prot);
+ } else {
+ pr_err("Physical address or size of buf is NULL");
+ return -EINVAL;
+ }
+
+ return result;
+}
+
+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;
+
+ /*
+ *TODO : Need to Add Async IO changes. All period
+ * size might not be supported.
+ */
+ 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;
+
+ pr_debug("%s:buf = %p\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_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,
+};
+
+static __devinit 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 struct platform_driver msm_pcm_driver = {
+ .driver = {
+ .name = "msm-lowlatency-pcm-dsp",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_pcm_probe,
+ .remove = __devexit_p(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("Multi channel PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm-multi-ch-pcm-q6.c b/sound/soc/msm/msm-multi-ch-pcm-q6.c
index ef58dd1..2d23b48 100644
--- a/sound/soc/msm/msm-multi-ch-pcm-q6.c
+++ b/sound/soc/msm/msm-multi-ch-pcm-q6.c
@@ -330,6 +330,7 @@
return -ENOMEM;
}
prtd->substream = substream;
+ prtd->audio_client->perf_mode = false;
prtd->audio_client = q6asm_audio_client_alloc(
(app_cb)event_handler, prtd);
if (!prtd->audio_client) {
@@ -364,8 +365,8 @@
prtd->session_id = prtd->audio_client->session;
msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
+ prtd->audio_client->perf_mode,
prtd->session_id, substream->stream);
-
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
prtd->cmd_ack = 1;
diff --git a/sound/soc/msm/msm-pcm-lpa.c b/sound/soc/msm/msm-pcm-lpa.c
index 269b49b..116ce3e 100644
--- a/sound/soc/msm/msm-pcm-lpa.c
+++ b/sound/soc/msm/msm-pcm-lpa.c
@@ -282,6 +282,7 @@
}
runtime->hw = msm_pcm_hardware;
prtd->substream = substream;
+ prtd->audio_client->perf_mode = false;
prtd->audio_client = q6asm_audio_client_alloc(
(app_cb)event_handler, prtd);
if (!prtd->audio_client) {
@@ -311,6 +312,7 @@
pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session);
prtd->session_id = prtd->audio_client->session;
msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
+ prtd->audio_client->perf_mode,
prtd->session_id, substream->stream);
ret = snd_pcm_hw_constraint_list(runtime, 0,
diff --git a/sound/soc/msm/msm-pcm-q6.c b/sound/soc/msm/msm-pcm-q6.c
index 942c3ea..74136dc 100644
--- a/sound/soc/msm/msm-pcm-q6.c
+++ b/sound/soc/msm/msm-pcm-q6.c
@@ -324,6 +324,7 @@
kfree(prtd);
return -ENOMEM;
}
+ prtd->audio_client->perf_mode = false;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
runtime->hw = msm_pcm_hardware_playback;
ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM);
@@ -338,6 +339,7 @@
prtd->audio_client->session);
prtd->session_id = prtd->audio_client->session;
msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
+ prtd->audio_client->perf_mode,
prtd->session_id, substream->stream);
prtd->cmd_ack = 1;
@@ -443,7 +445,7 @@
prtd->audio_client);
msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
- SNDRV_PCM_STREAM_PLAYBACK);
+ SNDRV_PCM_STREAM_PLAYBACK);
q6asm_audio_client_free(prtd->audio_client);
kfree(prtd);
return 0;
@@ -649,8 +651,9 @@
prtd->audio_client->session);
prtd->session_id = prtd->audio_client->session;
msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
+ prtd->audio_client->perf_mode,
prtd->session_id, substream->stream);
- }
+ }
ret = q6asm_audio_client_buf_alloc_contiguous(dir,
prtd->audio_client,
diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c
index 26dbb21..374357d 100644
--- a/sound/soc/msm/msm-pcm-routing.c
+++ b/sound/soc/msm/msm-pcm-routing.c
@@ -38,6 +38,7 @@
unsigned long port_sessions; /* track Tx BE ports -> Rx BE */
unsigned int sample_rate;
unsigned int channel;
+ bool perf_mode;
};
#define INVALID_SESSION -1
@@ -291,7 +292,8 @@
mutex_unlock(&routing_lock);
}
-void msm_pcm_routing_reg_phy_stream(int fedai_id, int dspst_id, int stream_type)
+void msm_pcm_routing_reg_phy_stream(int fedai_id, bool perf_mode, int dspst_id,
+ int stream_type)
{
int i, session_type, path_type, port_type;
struct route_payload payload;
@@ -321,6 +323,8 @@
if (eq_data[fedai_id].enable)
msm_send_eq_values(fedai_id);
for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+ if (test_bit(fedai_id, &msm_bedais[i].fe_sessions))
+ msm_bedais[i].perf_mode = perf_mode;
if (!is_be_dai_extproc(i) &&
(afe_get_port_type(msm_bedais[i].port_id) == port_type) &&
(msm_bedais[i].active) &&
@@ -329,12 +333,22 @@
channels = msm_bedais[i].channel;
if ((stream_type == SNDRV_PCM_STREAM_PLAYBACK) &&
+ ((channels == 1) || (channels == 2)) &&
+ msm_bedais[i].perf_mode) {
+ pr_debug("%s configure COPP to lowlatency mode",
+ __func__);
+ adm_multi_ch_copp_open(msm_bedais[i].port_id,
+ path_type,
+ msm_bedais[i].sample_rate,
+ msm_bedais[i].channel,
+ DEFAULT_COPP_TOPOLOGY, msm_bedais[i].perf_mode);
+ } else if ((stream_type == SNDRV_PCM_STREAM_PLAYBACK) &&
(channels > 2))
adm_multi_ch_copp_open(msm_bedais[i].port_id,
path_type,
msm_bedais[i].sample_rate,
msm_bedais[i].channel,
- DEFAULT_COPP_TOPOLOGY);
+ DEFAULT_COPP_TOPOLOGY, msm_bedais[i].perf_mode);
else
adm_open(msm_bedais[i].port_id,
path_type,
@@ -440,18 +454,32 @@
channels = msm_bedais[reg].channel;
- if ((session_type == SESSION_TYPE_RX) && (channels > 2))
+ if ((session_type == SESSION_TYPE_RX) &&
+ ((channels == 1) || (channels == 2))
+ && msm_bedais[reg].perf_mode) {
adm_multi_ch_copp_open(msm_bedais[reg].port_id,
path_type,
msm_bedais[reg].sample_rate,
channels,
- DEFAULT_COPP_TOPOLOGY);
+ DEFAULT_COPP_TOPOLOGY,
+ msm_bedais[reg].perf_mode);
+ pr_debug("%s:configure COPP to lowlatency mode",
+ __func__);
+ } else if ((session_type == SESSION_TYPE_RX)
+ && (channels > 2))
+ adm_multi_ch_copp_open(msm_bedais[reg].port_id,
+ path_type,
+ msm_bedais[reg].sample_rate,
+ channels,
+ DEFAULT_COPP_TOPOLOGY,
+ msm_bedais[reg].perf_mode);
else
adm_open(msm_bedais[reg].port_id,
path_type,
msm_bedais[reg].sample_rate, channels,
DEFAULT_COPP_TOPOLOGY);
+
msm_pcm_routing_build_matrix(val,
fe_dai_map[val][session_type], path_type);
srs_port_id = msm_bedais[reg].port_id;
@@ -1375,6 +1403,12 @@
msm_routing_put_audio_mixer),
};
+static const struct snd_kcontrol_new mmul5_mixer_controls[] = {
+ SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
static const struct snd_kcontrol_new mmul4_mixer_controls[] = {
SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_MI2S_TX,
@@ -2023,6 +2057,7 @@
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_UL4", "MultiMedia4 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL5", "MultiMedia5 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("VoLTE_DL", "VoLTE Playback", 0, 0, 0, 0),
@@ -2122,6 +2157,8 @@
mmul2_mixer_controls, ARRAY_SIZE(mmul2_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("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,
@@ -2280,6 +2317,7 @@
{"MultiMedia1 Mixer", "VOC_REC_UL", "INCALL_RECORD_TX"},
{"MultiMedia1 Mixer", "VOC_REC_DL", "INCALL_RECORD_RX"},
{"MultiMedia1 Mixer", "SLIM_4_TX", "SLIMBUS_4_TX"},
+ {"MultiMedia5 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
{"MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
{"MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
{"MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
@@ -2320,6 +2358,7 @@
{"MultiMedia2 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
{"MM_UL2", NULL, "MultiMedia2 Mixer"},
{"MM_UL4", NULL, "MultiMedia4 Mixer"},
+ {"MM_UL5", NULL, "MultiMedia5 Mixer"},
{"AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
{"AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
@@ -2553,6 +2592,7 @@
bedai->active = 0;
bedai->sample_rate = 0;
bedai->channel = 0;
+ bedai->perf_mode = false;
mutex_unlock(&routing_lock);
return 0;
@@ -2565,6 +2605,7 @@
int i, path_type, session_type;
struct msm_pcm_routing_bdai_data *bedai;
u32 channels;
+ bool playback, capture;
if (be_id >= MSM_BACKEND_DAI_MAX) {
pr_err("%s: unexpected be_id %d\n", __func__, be_id);
@@ -2592,18 +2633,29 @@
* is started.
*/
bedai->active = 1;
+ playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ capture = substream->stream == SNDRV_PCM_STREAM_CAPTURE;
+
for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MM_SIZE) {
if (fe_dai_map[i][session_type] != INVALID_SESSION) {
-
channels = bedai->channel;
- if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
- substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ if ((playback || capture)
+ && ((channels == 2) || (channels == 1)) &&
+ bedai->perf_mode) {
+ adm_multi_ch_copp_open(bedai->port_id,
+ path_type,
+ bedai->sample_rate,
+ channels,
+ DEFAULT_COPP_TOPOLOGY, bedai->perf_mode);
+ pr_debug("%s:configure COPP to lowlatency mode",
+ __func__);
+ } else if ((playback || capture)
&& (channels > 2))
adm_multi_ch_copp_open(bedai->port_id,
path_type,
bedai->sample_rate,
channels,
- DEFAULT_COPP_TOPOLOGY);
+ DEFAULT_COPP_TOPOLOGY, bedai->perf_mode);
else
adm_open(bedai->port_id,
path_type,
diff --git a/sound/soc/msm/msm-pcm-routing.h b/sound/soc/msm/msm-pcm-routing.h
index 45dbf40..6b87475 100644
--- a/sound/soc/msm/msm-pcm-routing.h
+++ b/sound/soc/msm/msm-pcm-routing.h
@@ -111,8 +111,8 @@
* dspst_id: DSP audio stream ID
* stream_type: playback or capture
*/
-void msm_pcm_routing_reg_phy_stream(int fedai_id, int dspst_id,
- int stream_type);
+void msm_pcm_routing_reg_phy_stream(int fedai_id, bool 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 enable);
diff --git a/sound/soc/msm/msm8930.c b/sound/soc/msm/msm8930.c
index e86db10..a577b6a 100644
--- a/sound/soc/msm/msm8930.c
+++ b/sound/soc/msm/msm8930.c
@@ -1015,6 +1015,53 @@
.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,
+ .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 = "SGLTE",
+ .stream_name = "SGLTE",
+ .cpu_dai_name = "SGLTE",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 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_SGLTE,
+ },
+ {
+ .name = "MSM8960 LowLatency",
+ .stream_name = "MultiMedia5",
+ .cpu_dai_name = "MultiMedia5",
+ .platform_name = "msm-lowlatency-pcm-dsp",
+ .dynamic = 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,
+ },
/* Backend DAI Links */
{
.name = LPASS_BE_SLIMBUS_0_RX,
diff --git a/sound/soc/msm/msm8960.c b/sound/soc/msm/msm8960.c
index b10a7ea..040bbe0 100644
--- a/sound/soc/msm/msm8960.c
+++ b/sound/soc/msm/msm8960.c
@@ -1348,6 +1348,21 @@
.codec_name = "snd-soc-dummy",
.be_id = MSM_FRONTEND_DAI_SGLTE,
},
+ {
+ .name = "MSM8960 LowLatency",
+ .stream_name = "MultiMedia5",
+ .cpu_dai_name = "MultiMedia5",
+ .platform_name = "msm-lowlatency-pcm-dsp",
+ .dynamic = 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,
+ },
/* Backend BT/FM DAI Links */
{
.name = LPASS_BE_INT_BT_SCO_RX,
@@ -1746,6 +1761,7 @@
msm8960_headset_gpios_configured = 1;
mutex_init(&cdc_mclk_mutex);
+
return ret;
}
diff --git a/sound/soc/msm/qdsp6/q6adm.c b/sound/soc/msm/qdsp6/q6adm.c
index 0327e4a..6724c54 100644
--- a/sound/soc/msm/qdsp6/q6adm.c
+++ b/sound/soc/msm/qdsp6/q6adm.c
@@ -294,7 +294,8 @@
switch (data->opcode) {
case ADM_CMDRSP_COPP_OPEN:
- case ADM_CMDRSP_MULTI_CHANNEL_COPP_OPEN: {
+ case ADM_CMDRSP_MULTI_CHANNEL_COPP_OPEN:
+ case ADM_CMDRSP_MULTI_CHANNEL_COPP_OPEN_V3: {
struct adm_copp_open_respond *open = data->payload;
if (open->copp_id == INVALID_COPP_ID) {
pr_err("%s: invalid coppid rxed %d\n",
@@ -707,7 +708,7 @@
int adm_multi_ch_copp_open(int port_id, int path, int rate, int channel_mode,
- int topology)
+ int topology, int perfmode)
{
struct adm_multi_ch_copp_open_command open;
int ret = 0;
@@ -745,7 +746,17 @@
open.hdr.pkt_size =
sizeof(struct adm_multi_ch_copp_open_command);
- open.hdr.opcode = ADM_CMD_MULTI_CHANNEL_COPP_OPEN;
+
+ if (perfmode) {
+ pr_debug("%s Performance mode", __func__);
+ open.hdr.opcode = ADM_CMD_MULTI_CHANNEL_COPP_OPEN_V3;
+ open.flags = ADM_MULTI_CH_COPP_OPEN_PERF_MODE_BIT;
+ open.reserved = PCM_BITS_PER_SAMPLE;
+ } else {
+ open.hdr.opcode = ADM_CMD_MULTI_CHANNEL_COPP_OPEN;
+ open.reserved = 0;
+ }
+
memset(open.dev_channel_mapping, 0, 8);
if (channel_mode == 1) {
@@ -779,8 +790,6 @@
channel_mode);
return -EINVAL;
}
-
-
open.hdr.src_svc = APR_SVC_ADM;
open.hdr.src_domain = APR_DOMAIN_APPS;
open.hdr.src_port = port_id;
diff --git a/sound/soc/msm/qdsp6/q6asm.c b/sound/soc/msm/qdsp6/q6asm.c
index 06be186..76940ee 100644
--- a/sound/soc/msm/qdsp6/q6asm.c
+++ b/sound/soc/msm/qdsp6/q6asm.c
@@ -209,6 +209,7 @@
session[ac->session] = 0;
mutex_unlock(&session_lock);
ac->session = 0;
+ ac->perf_mode = false;
return;
}
@@ -412,6 +413,7 @@
ac->cb = cb;
ac->priv = priv;
ac->io_mode = SYNC_IO_MODE;
+ ac->perf_mode = false;
ac->apr = apr_register("ADSP", "ASM", \
(apr_fn)q6asm_callback,\
((ac->session) << 8 | 0x0001),\
@@ -844,6 +846,7 @@
if (data->opcode == APR_BASIC_RSP_RESULT) {
token = data->token;
+ pr_debug("%s payload[0]:%x", __func__, payload[0]);
switch (payload[0]) {
case ASM_STREAM_CMD_SET_PP_PARAMS:
if (rtac_make_asm_callback(ac->session, payload,
@@ -863,7 +866,9 @@
return -EINVAL;
}
case ASM_STREAM_CMD_OPEN_READ:
+ case ASM_STREAM_CMD_OPEN_READ_V2_1:
case ASM_STREAM_CMD_OPEN_WRITE:
+ case ASM_STREAM_CMD_OPEN_WRITE_V2_1:
case ASM_STREAM_CMD_OPEN_READWRITE:
case ASM_DATA_CMD_MEDIA_FORMAT_UPDATE:
case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
@@ -871,8 +876,11 @@
case ASM_STREAM_CMD_OPEN_READ_COMPRESSED:
if (atomic_read(&ac->cmd_state) && wakeup_flag) {
atomic_set(&ac->cmd_state, 0);
- if (payload[1] == ADSP_EUNSUPPORTED)
+ if (payload[1] == ADSP_EUNSUPPORTED) {
+ pr_debug("paload[1]:%d unsupported",
+ payload[1]);
atomic_set(&ac->cmd_response, 1);
+ }
else
atomic_set(&ac->cmd_response, 0);
wake_up(&ac->cmd_wait);
@@ -1276,6 +1284,82 @@
return -EINVAL;
}
+int q6asm_open_read_v2_1(struct audio_client *ac,
+ uint32_t format)
+{
+ int rc = 0x00;
+ struct asm_stream_cmd_open_read_v2_1 open;
+#ifdef CONFIG_DEBUG_FS
+ in_cont_index = 0;
+#endif
+ if ((ac == NULL) || (ac->apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ pr_debug("%s:session[%d]", __func__, ac->session);
+
+ q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+ open.hdr.opcode = ASM_STREAM_CMD_OPEN_READ_V2_1;
+ open.src_endpoint = ASM_END_POINT_DEVICE_MATRIX;
+ open.pre_proc_top = get_asm_topology();
+ if (open.pre_proc_top == 0)
+ open.pre_proc_top = DEFAULT_POPP_TOPOLOGY;
+
+ switch (format) {
+ case FORMAT_LINEAR_PCM:
+ open.uMode = STREAM_PRIORITY_HIGH;
+ open.format = LINEAR_PCM;
+ break;
+ case FORMAT_MULTI_CHANNEL_LINEAR_PCM:
+ open.uMode = STREAM_PRIORITY_HIGH;
+ open.format = MULTI_CHANNEL_PCM;
+ break;
+ case FORMAT_MPEG4_AAC:
+ open.uMode = BUFFER_META_ENABLE | STREAM_PRIORITY_HIGH;
+ open.format = MPEG4_AAC;
+ break;
+ case FORMAT_V13K:
+ open.uMode = BUFFER_META_ENABLE | STREAM_PRIORITY_HIGH;
+ open.format = V13K_FS;
+ break;
+ case FORMAT_EVRC:
+ open.uMode = BUFFER_META_ENABLE | STREAM_PRIORITY_HIGH;
+ open.format = EVRC_FS;
+ break;
+ case FORMAT_AMRNB:
+ open.uMode = BUFFER_META_ENABLE | STREAM_PRIORITY_HIGH;
+ open.format = AMRNB_FS;
+ break;
+ case FORMAT_AMRWB:
+ open.uMode = BUFFER_META_ENABLE | STREAM_PRIORITY_HIGH;
+ open.format = AMRWB_FS;
+ break;
+ default:
+ pr_err("Invalid format[%d]\n", format);
+ goto fail_cmd;
+ }
+ open.uMode = ASM_OPEN_READ_PERF_MODE_BIT;
+ open.bits_per_sample = PCM_BITS_PER_SAMPLE;
+ open.reserved = 0;
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &open);
+ if (rc < 0) {
+ pr_err("open failed op[0x%x]rc[%d]\n", \
+ open.hdr.opcode, 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 OPEN_WRITE rc[%d]\n", __func__,
+ rc);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+
int q6asm_open_read_compressed(struct audio_client *ac,
uint32_t frames_per_buffer, uint32_t meta_data_mode)
{
@@ -1396,12 +1480,20 @@
q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
- open.hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE;
- open.uMode = STREAM_PRIORITY_HIGH;
- /* source endpoint : matrix */
- open.sink_endpoint = ASM_END_POINT_DEVICE_MATRIX;
- open.stream_handle = 0x00;
-
+ if (ac->perf_mode) {
+ pr_debug("%s In Performance/lowlatency mode", __func__);
+ open.hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE_V2_1;
+ open.uMode = ASM_OPEN_WRITE_PERF_MODE_BIT;
+ /* source endpoint : matrix */
+ open.sink_endpoint = ASM_END_POINT_DEVICE_MATRIX;
+ open.stream_handle = PCM_BITS_PER_SAMPLE;
+ } else {
+ open.hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE;
+ open.uMode = STREAM_PRIORITY_HIGH;
+ /* source endpoint : matrix */
+ open.sink_endpoint = ASM_END_POINT_DEVICE_MATRIX;
+ open.stream_handle = 0x00;
+ }
open.post_proc_top = get_asm_topology();
if (open.post_proc_top == 0)
open.post_proc_top = DEFAULT_POPP_TOPOLOGY;