ASoC: msm: Add compressed TX support
There is use case that the HDMI input goes through MI2S
TX interface to ADSP. Add compressed TX support for
this use case.
Change-Id: I510e3e63b68ea1887e4c99ebf1c6f76112abbed5
Signed-off-by: Subhash Chandra Bose Naripeddy <snariped@codeaurora.org>
diff --git a/include/sound/apr_audio.h b/include/sound/apr_audio.h
index 431dedf..610c66d 100644
--- a/include/sound/apr_audio.h
+++ b/include/sound/apr_audio.h
@@ -1145,6 +1145,13 @@
#define ASM_ENCDEC_IMMDIATE_DECODE 0x00010C14
#define ASM_ENCDEC_CFG_BLK 0x00010C2C
+#define ASM_STREAM_CMD_OPEN_READ_COMPRESSED 0x00010D95
+struct asm_stream_cmd_open_read_compressed {
+ struct apr_hdr hdr;
+ u32 uMode;
+ u32 frame_per_buf;
+} __packed;
+
#define ASM_STREAM_CMD_OPEN_WRITE 0x00010BCA
struct asm_stream_cmd_open_write {
struct apr_hdr hdr;
@@ -1185,6 +1192,17 @@
u16 afe_port_id;
} __packed;
+#define ADM_CMD_CONNECT_AFE_PORT_V2 0x00010332
+
+struct adm_cmd_connect_afe_port_v2 {
+ struct apr_hdr hdr;
+ u8 mode; /*mode represent the interface is for RX or TX*/
+ u8 session_id; /*ASM session ID*/
+ u16 afe_port_id;
+ u32 num_channels;
+ u32 sampleing_rate;
+} __packed;
+
#define ASM_STREAM_CMD_SET_ENCDEC_PARAM 0x00010C10
#define ASM_STREAM_CMD_GET_ENCDEC_PARAM 0x00010C11
#define ASM_ENCDEC_CFG_BLK_ID 0x00010C2C
diff --git a/include/sound/compress_offload.h b/include/sound/compress_offload.h
index 9769dea..e59d29c 100644
--- a/include/sound/compress_offload.h
+++ b/include/sound/compress_offload.h
@@ -123,6 +123,16 @@
};
/**
+ * struct snd_compr_audio_info: compressed input audio information
+ * @frame_size: legth of the encoded frame with valid data
+ * @reserved: reserved for furture use
+ */
+struct snd_compr_audio_info {
+ uint32_t frame_size;
+ uint32_t reserved[15];
+};
+
+/**
* compress path ioctl definitions
* SNDRV_COMPRESS_GET_CAPS: Query capability of DSP
* SNDRV_COMPRESS_GET_CODEC_CAPS: Query capability of a codec
diff --git a/include/sound/q6asm.h b/include/sound/q6asm.h
index 1e647a2..85714fe 100644
--- a/include/sound/q6asm.h
+++ b/include/sound/q6asm.h
@@ -180,6 +180,8 @@
int q6asm_open_read(struct audio_client *ac, uint32_t format);
+int q6asm_open_read_compressed(struct audio_client *ac, uint32_t format);
+
int q6asm_open_write(struct audio_client *ac, uint32_t format);
int q6asm_open_write_compressed(struct audio_client *ac, uint32_t format);
diff --git a/sound/soc/msm/mpq8064.c b/sound/soc/msm/mpq8064.c
index 391c5f3..6685ce5 100644
--- a/sound/soc/msm/mpq8064.c
+++ b/sound/soc/msm/mpq8064.c
@@ -850,7 +850,7 @@
static void msm_mi2s_shutdown(struct snd_pcm_substream *substream)
{
if (mi2s_bit_clk) {
- clk_disable(mi2s_bit_clk);
+ clk_disable_unprepare(mi2s_bit_clk);
clk_put(mi2s_bit_clk);
mi2s_bit_clk = NULL;
}
@@ -892,7 +892,7 @@
if (IS_ERR(mi2s_bit_clk))
return PTR_ERR(mi2s_bit_clk);
clk_set_rate(mi2s_bit_clk, 0);
- ret = clk_enable(mi2s_bit_clk);
+ ret = clk_prepare_enable(mi2s_bit_clk);
if (IS_ERR_VALUE(ret)) {
pr_err("Unable to enable mi2s_bit_clk\n");
clk_put(mi2s_bit_clk);
diff --git a/sound/soc/msm/msm-compr-q6.c b/sound/soc/msm/msm-compr-q6.c
index 2455128..1ba2dab 100644
--- a/sound/soc/msm/msm-compr-q6.c
+++ b/sound/soc/msm/msm-compr-q6.c
@@ -34,6 +34,13 @@
#include "msm-compr-q6.h"
#include "msm-pcm-routing.h"
+#define COMPRE_CAPTURE_NUM_PERIODS 16
+/* Allocate the worst case frame size for compressed audio */
+#define COMPRE_CAPTURE_HEADER_SIZE (sizeof(struct snd_compr_audio_info))
+#define COMPRE_CAPTURE_MAX_FRAME_SIZE (6144)
+#define COMPRE_CAPTURE_PERIOD_SIZE (COMPRE_CAPTURE_MAX_FRAME_SIZE + \
+ COMPRE_CAPTURE_HEADER_SIZE)
+
struct snd_msm {
struct msm_audio *prtd;
unsigned volume;
@@ -42,6 +49,27 @@
static struct audio_locks the_locks;
+static struct snd_pcm_hardware msm_compr_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 =
+ COMPRE_CAPTURE_PERIOD_SIZE * COMPRE_CAPTURE_NUM_PERIODS ,
+ .period_bytes_min = COMPRE_CAPTURE_PERIOD_SIZE,
+ .period_bytes_max = COMPRE_CAPTURE_PERIOD_SIZE,
+ .periods_min = COMPRE_CAPTURE_NUM_PERIODS,
+ .periods_max = COMPRE_CAPTURE_NUM_PERIODS,
+ .fifo_size = 0,
+};
+
static struct snd_pcm_hardware msm_compr_hardware_playback = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -81,7 +109,9 @@
struct snd_pcm_substream *substream = prtd->substream;
struct snd_pcm_runtime *runtime = substream->runtime;
struct audio_aio_write_param param;
+ struct audio_aio_read_param read_param;
struct audio_buffer *buf = NULL;
+ uint32_t *ptrmem = (uint32_t *)payload;
int i = 0;
pr_debug("%s opcode =%08x\n", __func__, opcode);
@@ -138,9 +168,53 @@
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("buf = %p, data = 0x%X, *data = %p,\n"
+ "prtd->pcm_irq_pos = %d\n",
+ prtd->audio_client->port[OUT].buf,
+ *(uint32_t *)prtd->audio_client->port[OUT].buf->data,
+ prtd->audio_client->port[OUT].buf->data,
+ prtd->pcm_irq_pos);
+
+ memcpy(prtd->audio_client->port[OUT].buf->data +
+ prtd->pcm_irq_pos, (ptrmem + 2),
+ COMPRE_CAPTURE_HEADER_SIZE);
+ pr_debug("buf = %p, updated data = 0x%X, *data = %p\n",
+ prtd->audio_client->port[OUT].buf,
+ *(uint32_t *)(prtd->audio_client->port[OUT].buf->data +
+ prtd->pcm_irq_pos),
+ prtd->audio_client->port[OUT].buf->data);
+ if (!atomic_read(&prtd->start))
+ break;
+ pr_debug("frame size=%d, buffer = 0x%X\n", ptrmem[2],
+ ptrmem[1]);
+ if (ptrmem[2] > COMPRE_CAPTURE_MAX_FRAME_SIZE) {
+ pr_err("Frame length exceeded the max length");
+ break;
+ }
+ buf = prtd->audio_client->port[OUT].buf;
+ pr_debug("pcm_irq_pos=%d, buf[0].phys = 0x%X\n",
+ prtd->pcm_irq_pos, (uint32_t)buf[0].phys);
+ read_param.len = prtd->pcm_count - COMPRE_CAPTURE_HEADER_SIZE ;
+ read_param.paddr = (unsigned long)(buf[0].phys) +
+ prtd->pcm_irq_pos + COMPRE_CAPTURE_HEADER_SIZE;
+ prtd->pcm_irq_pos += prtd->pcm_count;
+
+ if (atomic_read(&prtd->start))
+ snd_pcm_period_elapsed(substream);
+
+ q6asm_async_read(prtd->audio_client, &read_param);
+ 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 (!atomic_read(&prtd->pending_buffer))
break;
pr_debug("%s:writing %d bytes"
@@ -286,6 +360,44 @@
return 0;
}
+static int msm_compr_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct compr_audio *compr = runtime->private_data;
+ struct msm_audio *prtd = &compr->prtd;
+ struct audio_buffer *buf = prtd->audio_client->port[OUT].buf;
+ struct audio_aio_read_param read_param;
+ int ret = 0;
+ int i;
+ 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 ret;
+ read_param.len = prtd->pcm_count - COMPRE_CAPTURE_HEADER_SIZE;
+ pr_debug("%s: Samp_rate = %d, Channel = %d, pcm_size = %d,\n"
+ "pcm_count = %d, periods = %d\n",
+ __func__, prtd->samp_rate, prtd->channel_mode,
+ prtd->pcm_size, prtd->pcm_count, runtime->periods);
+
+ for (i = 0; i < runtime->periods; i++) {
+ read_param.uid = i;
+ read_param.paddr = ((unsigned long)(buf[i].phys) +
+ COMPRE_CAPTURE_HEADER_SIZE);
+ q6asm_async_read(prtd->audio_client, &read_param);
+ }
+ prtd->periods = runtime->periods;
+
+ prtd->enabled = 1;
+
+ return ret;
+}
+
static int msm_compr_trigger(struct snd_pcm_substream *substream, int cmd)
{
int ret = 0;
@@ -298,8 +410,15 @@
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
prtd->pcm_irq_pos = 0;
- if (compr->info.codec_param.codec.id ==
- SND_AUDIOCODEC_AC3_PASS_THROUGH) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (compr->info.codec_param.codec.id ==
+ SND_AUDIOCODEC_AC3_PASS_THROUGH) {
+ msm_pcm_routing_reg_psthr_stream(
+ soc_prtd->dai_link->be_id,
+ prtd->session_id, substream->stream,
+ 1);
+ }
+ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
msm_pcm_routing_reg_psthr_stream(
soc_prtd->dai_link->be_id,
prtd->session_id, substream->stream, 1);
@@ -312,11 +431,19 @@
break;
case SNDRV_PCM_TRIGGER_STOP:
pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
- if (compr->info.codec_param.codec.id ==
- SND_AUDIOCODEC_AC3_PASS_THROUGH) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (compr->info.codec_param.codec.id ==
+ SND_AUDIOCODEC_AC3_PASS_THROUGH) {
+ msm_pcm_routing_reg_psthr_stream(
+ soc_prtd->dai_link->be_id,
+ prtd->session_id, substream->stream,
+ 0);
+ }
+ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
msm_pcm_routing_reg_psthr_stream(
soc_prtd->dai_link->be_id,
- prtd->session_id, substream->stream, 0);
+ prtd->session_id, substream->stream,
+ 0);
}
atomic_set(&prtd->start, 0);
break;
@@ -370,10 +497,6 @@
.rampingcurve = SOFT_VOLUME_CURVE_LINEAR,
};
- /* Capture path */
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- return -EINVAL;
-
pr_debug("%s\n", __func__);
compr = kzalloc(sizeof(struct compr_audio), GFP_KERNEL);
if (compr == NULL) {
@@ -389,13 +512,18 @@
kfree(prtd);
return -ENOMEM;
}
- runtime->hw = msm_compr_hardware_playback;
pr_info("%s: session ID %d\n", __func__, prtd->audio_client->session);
prtd->session_id = prtd->audio_client->session;
- prtd->cmd_ack = 1;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ runtime->hw = msm_compr_hardware_playback;
+ prtd->cmd_ack = 1;
+ } else {
+ runtime->hw = msm_compr_hardware_capture;
+ }
+
ret = snd_pcm_hw_constraint_list(runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
@@ -410,7 +538,8 @@
prtd->dsp_cnt = 0;
atomic_set(&prtd->pending_buffer, 1);
- compr->codec = FORMAT_MP3;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ compr->codec = FORMAT_MP3;
populate_codec_list(compr, runtime);
runtime->private_data = compr;
compressed_audio.prtd = &compr->prtd;
@@ -473,6 +602,27 @@
return 0;
}
+static int msm_compr_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 compr_audio *compr = runtime->private_data;
+ struct msm_audio *prtd = &compr->prtd;
+ int dir = OUT;
+
+ pr_debug("%s\n", __func__);
+ atomic_set(&prtd->pending_buffer, 0);
+ 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_compr_close(struct snd_pcm_substream *substream)
{
int ret = 0;
@@ -480,7 +630,7 @@
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
ret = msm_compr_playback_close(substream);
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- ret = EINVAL;
+ ret = msm_compr_capture_close(substream);
return ret;
}
static int msm_compr_prepare(struct snd_pcm_substream *substream)
@@ -490,7 +640,7 @@
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
ret = msm_compr_playback_prepare(substream);
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- ret = EINVAL;
+ ret = msm_compr_capture_prepare(substream);
return ret;
}
@@ -504,7 +654,10 @@
if (prtd->pcm_irq_pos >= prtd->pcm_size)
prtd->pcm_irq_pos = 0;
- pr_debug("pcm_irq_pos = %d\n", prtd->pcm_irq_pos);
+ pr_debug("%s: pcm_irq_pos = %d, pcm_size = %d, sample_bits = %d,\n"
+ "frame_bits = %d\n", __func__, prtd->pcm_irq_pos,
+ prtd->pcm_size, runtime->sample_bits,
+ runtime->frame_bits);
return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
}
@@ -546,28 +699,44 @@
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
dir = IN;
else
- return -EINVAL;
+ dir = OUT;
- switch (compr->info.codec_param.codec.id) {
- case SND_AUDIOCODEC_AC3_PASS_THROUGH:
- ret = q6asm_open_write_compressed(prtd->audio_client,
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (compr->info.codec_param.codec.id) {
+ case SND_AUDIOCODEC_AC3_PASS_THROUGH:
+ ret = q6asm_open_write_compressed(prtd->audio_client,
compr->codec);
+
+ if (ret < 0) {
+ pr_err("%s: Session out open failed\n",
+ __func__);
+ return -ENOMEM;
+ }
+ break;
+ default:
+ ret = q6asm_open_write(prtd->audio_client,
+ compr->codec);
+ if (ret < 0) {
+ pr_err("%s: Session out open failed\n",
+ __func__);
+ return -ENOMEM;
+ }
+ msm_pcm_routing_reg_phy_stream(
+ soc_prtd->dai_link->be_id,
+ prtd->session_id, substream->stream);
+
+ break;
+ }
+ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ ret = q6asm_open_read_compressed(prtd->audio_client,
+ compr->codec);
+
if (ret < 0) {
pr_err("%s: compressed Session out open failed\n",
- __func__);
+ __func__);
return -ENOMEM;
}
- break;
- default:
- ret = q6asm_open_write(prtd->audio_client, compr->codec);
- if (ret < 0) {
- pr_err("%s: Session out open failed\n", __func__);
- return -ENOMEM;
- }
- msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
- prtd->session_id, substream->stream);
-
- break;
}
ret = q6asm_set_io_mode(prtd->audio_client, ASYNC_IO_MODE);
if (ret < 0) {
@@ -586,13 +755,17 @@
}
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 = runtime->hw.buffer_bytes_max;
+
+ pr_debug("%s: buf[%p]dma_buf->area[%p]dma_buf->addr[%p]\n"
+ "dma_buf->bytes[%d]\n", __func__,
+ (void *)buf, (void *)dma_buf->area,
+ (void *)dma_buf->addr, dma_buf->bytes);
if (!dma_buf->area)
return -ENOMEM;
diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c
index 8a49f1b..be0951c 100644
--- a/sound/soc/msm/msm-dai-fe.c
+++ b/sound/soc/msm/msm-dai-fe.c
@@ -176,6 +176,17 @@
.rate_min = 8000,
.rate_max = 48000,
},
+ .capture = {
+ .stream_name = "MultiMedia4 Capture",
+ .aif_name = "MM_UL4",
+ .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 = "MultiMedia4",
},
diff --git a/sound/soc/msm/msm-dai-q6.c b/sound/soc/msm/msm-dai-q6.c
index b3e5120..147316e 100644
--- a/sound/soc/msm/msm-dai-q6.c
+++ b/sound/soc/msm/msm-dai-q6.c
@@ -40,9 +40,14 @@
union afe_port_config port_config;
};
+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_dai_data tx_dai;
- struct msm_dai_q6_dai_data rx_dai;
+ struct msm_dai_q6_mi2s_dai_config tx_dai;
+ struct msm_dai_q6_mi2s_dai_config rx_dai;
struct snd_pcm_hw_constraint_list rate_constraint;
struct snd_pcm_hw_constraint_list bitwidth_constraint;
};
@@ -86,8 +91,8 @@
static const char *mi2s_format[] = {
"LPCM",
"Compr",
- "60958-LPCM",
- "60958-Compr"};
+ "LPCM-60958",
+ "Compr-60958"};
static const struct soc_enum mi2s_config_enum[] = {
SOC_ENUM_SINGLE_EXT(4, mi2s_format),
@@ -143,21 +148,63 @@
{
struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
dev_get_drvdata(dai->dev);
- struct msm_dai_q6_dai_data *dai_data =
+ 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;
dai_data->channels = params_channels(params);
switch (dai_data->channels) {
- case 2:
- dai_data->port_config.mi2s.channel = MSM_AFE_STEREO;
+ case 8:
+ case 7:
+ if (mi2s_dai_config->pdata_mi2s_lines < AFE_I2S_8CHS)
+ goto error_invalid_data;
+ dai_data->port_config.mi2s.line = AFE_I2S_8CHS;
break;
+ case 6:
+ case 5:
+ if (mi2s_dai_config->pdata_mi2s_lines < AFE_I2S_6CHS)
+ goto error_invalid_data;
+ dai_data->port_config.mi2s.line = AFE_I2S_6CHS;
+ break;
+ case 4:
+ case 3:
+ if (mi2s_dai_config->pdata_mi2s_lines < AFE_I2S_QUAD01)
+ goto error_invalid_data;
+ if (mi2s_dai_config->pdata_mi2s_lines == AFE_I2S_QUAD23)
+ dai_data->port_config.mi2s.line =
+ mi2s_dai_config->pdata_mi2s_lines;
+ else
+ dai_data->port_config.mi2s.line = AFE_I2S_QUAD01;
+ break;
+ case 2:
case 1:
- dai_data->port_config.mi2s.channel = MSM_AFE_MONO;
+ if (mi2s_dai_config->pdata_mi2s_lines < AFE_I2S_SD0)
+ goto error_invalid_data;
+ switch (mi2s_dai_config->pdata_mi2s_lines) {
+ case AFE_I2S_SD0:
+ case AFE_I2S_SD1:
+ case AFE_I2S_SD2:
+ case AFE_I2S_SD3:
+ dai_data->port_config.mi2s.line =
+ mi2s_dai_config->pdata_mi2s_lines;
+ break;
+ case AFE_I2S_QUAD01:
+ case AFE_I2S_6CHS:
+ case AFE_I2S_8CHS:
+ dai_data->port_config.mi2s.line = AFE_I2S_SD0;
+ break;
+ case AFE_I2S_QUAD23:
+ dai_data->port_config.mi2s.line = AFE_I2S_SD2;
+ break;
+ }
+ if (dai_data->channels == 2)
+ dai_data->port_config.mi2s.channel = MSM_AFE_STEREO;
+ else
+ dai_data->port_config.mi2s.channel = MSM_AFE_MONO;
break;
default:
- pr_warn("greater than stereo has not been validated");
- break;
+ goto error_invalid_data;
}
dai_data->rate = params_rate(params);
dai_data->port_config.mi2s.bitwidth = 16;
@@ -166,7 +213,14 @@
mi2s_dai_data->rate_constraint.list = &dai_data->rate;
mi2s_dai_data->bitwidth_constraint.list = &dai_data->bitwidth;
}
+
+ pr_debug("%s: dai_data->channels = %d, line = %d\n", __func__,
+ dai_data->channels, dai_data->port_config.mi2s.line);
return 0;
+error_invalid_data:
+ pr_err("%s: dai_data->channels = %d, line = %d\n", __func__,
+ dai_data->channels, dai_data->port_config.mi2s.line);
+ return -EINVAL;
}
static int msm_dai_q6_mi2s_get_lineconfig(u16 sd_lines, u16 *config_ptr,
@@ -276,7 +330,9 @@
}
if (ch_cnt) {
- dai_data->rx_dai.port_config.mi2s.line = sdline_config;
+ dai_data->rx_dai.mi2s_dai_data.port_config.mi2s.line =
+ sdline_config;
+ dai_data->rx_dai.pdata_mi2s_lines = sdline_config;
dai_driver->playback.channels_min = 1;
dai_driver->playback.channels_max = ch_cnt << 1;
} else {
@@ -292,7 +348,9 @@
}
if (ch_cnt) {
- dai_data->tx_dai.port_config.mi2s.line = sdline_config;
+ dai_data->tx_dai.mi2s_dai_data.port_config.mi2s.line =
+ sdline_config;
+ dai_data->tx_dai.pdata_mi2s_lines = sdline_config;
dai_driver->capture.channels_min = 1;
dai_driver->capture.channels_max = ch_cnt << 1;
} else {
@@ -301,8 +359,8 @@
}
dev_info(&pdev->dev, "%s: playback sdline %x capture sdline %x\n",
- __func__, dai_data->rx_dai.port_config.mi2s.line,
- dai_data->tx_dai.port_config.mi2s.line);
+ __func__, dai_data->rx_dai.pdata_mi2s_lines,
+ dai_data->tx_dai.pdata_mi2s_lines);
dev_info(&pdev->dev, "%s: playback ch_max %d capture ch_mx %d\n",
__func__, dai_driver->playback.channels_max,
dai_driver->capture.channels_max);
@@ -315,8 +373,10 @@
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.status_mask) ||
- test_bit(STATUS_PORT_STARTED, mi2s_dai_data->rx_dai.status_mask)) {
+ 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;
@@ -324,12 +384,12 @@
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
- mi2s_dai_data->rx_dai.port_config.mi2s.ws = 1;
- mi2s_dai_data->tx_dai.port_config.mi2s.ws = 1;
+ mi2s_dai_data->rx_dai.mi2s_dai_data.port_config.mi2s.ws = 1;
+ mi2s_dai_data->tx_dai.mi2s_dai_data.port_config.mi2s.ws = 1;
break;
case SND_SOC_DAIFMT_CBM_CFM:
- mi2s_dai_data->rx_dai.port_config.mi2s.ws = 0;
- mi2s_dai_data->tx_dai.port_config.mi2s.ws = 0;
+ mi2s_dai_data->rx_dai.mi2s_dai_data.port_config.mi2s.ws = 0;
+ mi2s_dai_data->tx_dai.mi2s_dai_data.port_config.mi2s.ws = 0;
break;
default:
return -EINVAL;
@@ -345,7 +405,8 @@
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->tx_dai);
+ &mi2s_dai_data->rx_dai.mi2s_dai_data :
+ &mi2s_dai_data->tx_dai.mi2s_dai_data);
int rc = 0;
if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
@@ -364,7 +425,8 @@
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->tx_dai);
+ &mi2s_dai_data->rx_dai.mi2s_dai_data :
+ &mi2s_dai_data->tx_dai.mi2s_dai_data);
u16 port_id = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
MI2S_RX : MI2S_TX);
int rc = 0;
@@ -406,7 +468,8 @@
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->tx_dai);
+ &mi2s_dai_data->rx_dai.mi2s_dai_data :
+ &mi2s_dai_data->tx_dai.mi2s_dai_data);
u16 port_id = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
MI2S_RX : MI2S_TX);
int rc = 0;
@@ -418,8 +481,10 @@
clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
}
- if (!test_bit(STATUS_PORT_STARTED, mi2s_dai_data->rx_dai.status_mask) &&
- !test_bit(STATUS_PORT_STARTED, mi2s_dai_data->rx_dai.status_mask)) {
+ 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.status_mask)) {
mi2s_dai_data->rate_constraint.list = NULL;
mi2s_dai_data->bitwidth_constraint.list = NULL;
}
@@ -1268,9 +1333,9 @@
struct snd_kcontrol *kcontrol = NULL;
int rc = 0;
- if (mi2s_dai_data->rx_dai.port_config.mi2s.line) {
+ if (mi2s_dai_data->rx_dai.mi2s_dai_data.port_config.mi2s.line) {
kcontrol = snd_ctl_new1(&mi2s_config_controls[0],
- &mi2s_dai_data->rx_dai);
+ &mi2s_dai_data->rx_dai.mi2s_dai_data);
rc = snd_ctl_add(dai->card->snd_card, kcontrol);
if (IS_ERR_VALUE(rc)) {
@@ -1279,10 +1344,10 @@
}
}
- if (mi2s_dai_data->tx_dai.port_config.mi2s.line) {
+ if (mi2s_dai_data->tx_dai.mi2s_dai_data.port_config.mi2s.line) {
rc = snd_ctl_add(dai->card->snd_card,
- snd_ctl_new1(&mi2s_config_controls[2],
- &mi2s_dai_data->tx_dai));
+ snd_ctl_new1(&mi2s_config_controls[2],
+ &mi2s_dai_data->tx_dai.mi2s_dai_data));
if (IS_ERR_VALUE(rc)) {
if (kcontrol)
@@ -1302,19 +1367,21 @@
int rc;
/* If AFE port is still up, close it */
- if (test_bit(STATUS_PORT_STARTED, mi2s_dai_data->rx_dai.status_mask)) {
+ 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.status_mask);
+ mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask);
}
- if (test_bit(STATUS_PORT_STARTED, mi2s_dai_data->tx_dai.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.status_mask);
+ mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask);
}
kfree(mi2s_dai_data);
snd_soc_unregister_dai(dai->dev);
@@ -1966,6 +2033,8 @@
return 0;
err_pdata:
+
+ dev_err(&pdev->dev, "fail to msm_dai_q6_mi2s_dev_probe\n");
kfree(dai_data);
rtn:
return rc;
diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c
index 239c904..387fe44 100644
--- a/sound/soc/msm/msm-pcm-routing.c
+++ b/sound/soc/msm/msm-pcm-routing.c
@@ -1352,6 +1352,13 @@
msm_routing_put_audio_mixer),
};
+
+static const struct snd_kcontrol_new mmul4_mixer_controls[] = {
+ SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
static const struct snd_kcontrol_new pri_rx_voice_mixer_controls[] = {
SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_PRI_I2S_RX,
MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
@@ -1983,6 +1990,7 @@
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_UL4", "MultiMedia4 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),
@@ -2080,6 +2088,8 @@
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("MultiMedia4 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul4_mixer_controls, ARRAY_SIZE(mmul4_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,
@@ -2247,6 +2257,7 @@
{"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"},
{"MultiMedia1 Mixer", "MI2S_TX", "MI2S_TX"},
{"MultiMedia2 Mixer", "MI2S_TX", "MI2S_TX"},
+ {"MultiMedia4 Mixer", "MI2S_TX", "MI2S_TX"},
{"MultiMedia1 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
{"MultiMedia1 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
{"MultiMedia1 Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
@@ -2276,6 +2287,7 @@
{"MM_UL1", NULL, "MultiMedia1 Mixer"},
{"MultiMedia2 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
{"MM_UL2", NULL, "MultiMedia2 Mixer"},
+ {"MM_UL4", NULL, "MultiMedia4 Mixer"},
{"AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
{"AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
diff --git a/sound/soc/msm/qdsp6/q6asm.c b/sound/soc/msm/qdsp6/q6asm.c
index 50011a1..a80ff29 100644
--- a/sound/soc/msm/qdsp6/q6asm.c
+++ b/sound/soc/msm/qdsp6/q6asm.c
@@ -737,6 +737,9 @@
cnt++;
}
ac->port[dir].max_buf_cnt = cnt;
+
+ pr_debug("%s ac->port[%d].max_buf_cnt[%d]\n", __func__, dir,
+ ac->port[dir].max_buf_cnt);
mutex_unlock(&ac->cmd_lock);
rc = q6asm_memory_map(ac, buf[0].phys, dir, bufsz, cnt);
if (rc < 0) {
@@ -857,6 +860,7 @@
case ASM_DATA_CMD_MEDIA_FORMAT_UPDATE:
case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
case ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED:
+ case ASM_STREAM_CMD_OPEN_READ_COMPRESSED:
if (atomic_read(&ac->cmd_state)) {
atomic_set(&ac->cmd_state, 0);
wake_up(&ac->cmd_wait);
@@ -1260,6 +1264,42 @@
return -EINVAL;
}
+int q6asm_open_read_compressed(struct audio_client *ac, uint32_t format)
+{
+ int rc = 0x00;
+ struct asm_stream_cmd_open_read_compressed 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_COMPRESSED;
+ /* hardcoded as following*/
+ open.frame_per_buf = 1;
+ open.uMode = 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_READ_COMPRESSED rc[%d]\n",
+ __func__, rc);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
int q6asm_open_write_compressed(struct audio_client *ac, uint32_t format)
{
int rc = 0x00;