diff --git a/sound/soc/msm/Makefile b/sound/soc/msm/Makefile index 568fd40c326..3b257a6031c 100644 --- a/sound/soc/msm/Makefile +++ b/sound/soc/msm/Makefile @@ -56,7 +56,7 @@ obj-$(CONFIG_SND_SOC_MSM8X60) += snd-soc-lpass-dma.o obj-$(CONFIG_SND_SOC_MSM_QDSP6_INTF) += qdsp6/ -snd-soc-qdsp6-objs := msm-dai-q6.o msm-pcm-q6.o msm-pcm-routing.o msm-dai-fe.o +snd-soc-qdsp6-objs := msm-dai-q6.o msm-pcm-q6.o msm-pcm-routing.o msm-dai-fe.o msm-compr-q6.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 obj-$(CONFIG_SND_SOC_QDSP6) += snd-soc-qdsp6.o diff --git a/sound/soc/msm/msm-compr-q6.c b/sound/soc/msm/msm-compr-q6.c new file mode 100644 index 00000000000..cf6f1e7473b --- /dev/null +++ b/sound/soc/msm/msm-compr-q6.c @@ -0,0 +1,567 @@ +/* Copyright (c) 2011, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm-compr-q6.h" +#include "msm-pcm-routing.h" + +static struct audio_locks the_locks; + +static struct snd_pcm_hardware msm_compr_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, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = 1200 * 1024 * 2, + .period_bytes_min = 60 * 1024, + .period_bytes_max = 1200 * 1024, + .periods_min = 2, + .periods_max = 40, + .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 struct snd_pcm_hw_constraint_list constraints_sample_rates = { + .count = ARRAY_SIZE(supported_sample_rates), + .list = supported_sample_rates, + .mask = 0, +}; + +static void compr_event_handler(uint32_t opcode, + uint32_t token, uint32_t *payload, void *priv) +{ + struct compr_audio *compr = priv; + struct msm_audio *prtd = &compr->prtd; + struct snd_pcm_substream *substream = prtd->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + struct audio_aio_write_param param; + struct audio_buffer *buf = NULL; + int i = 0; + + pr_debug("%s opcode =%08x\n", __func__, opcode); + switch (opcode) { + case ASM_DATA_EVENT_WRITE_DONE: { + uint32_t *ptrmem = (uint32_t *)¶m; + 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)) { + prtd->pending_buffer = 1; + break; + } else + prtd->pending_buffer = 0; + + if (runtime->status->hw_ptr >= runtime->control->appl_ptr) + break; + buf = prtd->audio_client->port[IN].buf; + pr_debug("%s:writing %d bytes of buffer[%d] to dsp 2\n", + __func__, prtd->pcm_count, prtd->out_head); + pr_debug("%s:writing buffer[%d] from 0x%08x\n", + __func__, prtd->out_head, + ((unsigned int)buf[0].phys + + (prtd->out_head * prtd->pcm_count))); + + param.paddr = (unsigned long)buf[0].phys + + (prtd->out_head * prtd->pcm_count); + param.len = prtd->pcm_count; + param.msw_ts = 0; + param.lsw_ts = 0; + param.flags = NO_TIMESTAMP; + param.uid = (unsigned long)buf[0].phys + + (prtd->out_head * prtd->pcm_count); + for (i = 0; i < sizeof(struct audio_aio_write_param)/4; + i++, ++ptrmem) + pr_debug("cmd[%d]=0x%08x\n", i, *ptrmem); + if (q6asm_async_write(prtd->audio_client, + ¶m) < 0) + pr_err("%s:q6asm_async_write failed\n", + __func__); + else + prtd->out_head = + (prtd->out_head + 1) & (runtime->periods - 1); + 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 APR_BASIC_RSP_RESULT: { + switch (payload[0]) { + case ASM_SESSION_CMD_RUN: { + if (!prtd->pending_buffer && + !atomic_read(&prtd->start)) + break; + pr_debug("%s:writing %d bytes" + " of buffer[%d] to dsp\n", + __func__, prtd->pcm_count, prtd->out_head); + buf = prtd->audio_client->port[IN].buf; + pr_debug("%s:writing buffer[%d] from 0x%08x\n", + __func__, prtd->out_head, + ((unsigned int)buf[0].phys + + (prtd->out_head * prtd->pcm_count))); + param.paddr = (unsigned long)buf[prtd->out_head].phys; + param.len = prtd->pcm_count; + param.msw_ts = 0; + param.lsw_ts = 0; + param.flags = NO_TIMESTAMP; + param.uid = (unsigned long)buf[prtd->out_head].phys; + if (q6asm_async_write(prtd->audio_client, + ¶m) < 0) + pr_err("%s:q6asm_async_write failed\n", + __func__); + else + prtd->out_head = + (prtd->out_head + 1) + & (runtime->periods - 1); + } + break; + case ASM_STREAM_CMD_FLUSH: + pr_debug("ASM_STREAM_CMD_FLUSH\n"); + prtd->cmd_ack = 1; + wake_up(&the_locks.eos_wait); + break; + default: + break; + } + break; + } + default: + pr_debug("Not Supported Event opcode[0x%x]\n", opcode); + break; + } +} + +static int msm_compr_playback_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; + 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; + prtd->out_head = 0; + if (prtd->enabled) + return 0; + + ret = q6asm_media_format_block(prtd->audio_client, compr->codec); + 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_compr_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct compr_audio *compr = runtime->private_data; + struct msm_audio *prtd = &compr->prtd; + + pr_debug("%s\n", __func__); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + prtd->pcm_irq_pos = 0; + 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); + atomic_set(&prtd->start, 1); + break; + case SNDRV_PCM_TRIGGER_STOP: + pr_debug("SNDRV_PCM_TRIGGER_STOP\n"); + atomic_set(&prtd->start, 0); + 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 void populate_codec_list(struct compr_audio *compr, + struct snd_pcm_runtime *runtime) +{ + pr_debug("%s\n", __func__); + /* MP3 Block */ + compr->info.compr_cap.num_codecs = 1; + compr->info.compr_cap.min_fragment_size = runtime->hw.period_bytes_min; + compr->info.compr_cap.max_fragment_size = runtime->hw.period_bytes_max; + compr->info.compr_cap.min_fragments = runtime->hw.periods_min; + compr->info.compr_cap.max_fragments = runtime->hw.periods_max; + compr->info.compr_cap.codecs[0] = SND_AUDIOCODEC_MP3; + /* Add new codecs here */ +} + +static int msm_compr_open(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; + struct msm_audio *prtd; + int ret = 0; + + /* 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) { + pr_err("Failed to allocate memory for msm_audio\n"); + return -ENOMEM; + } + prtd = &compr->prtd; + prtd->substream = substream; + prtd->audio_client = q6asm_audio_client_alloc( + (app_cb)compr_event_handler, compr); + if (!prtd->audio_client) { + pr_info("%s: Could not allocate memory\n", __func__); + 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; + msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id, + prtd->session_id, substream->stream); + + prtd->cmd_ack = 1; + + 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"); + + prtd->dsp_cnt = 0; + prtd->pending_buffer = 1; + compr->codec = FORMAT_MP3; + populate_codec_list(compr, runtime); + runtime->private_data = compr; + + return 0; +} + +static int msm_compr_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 compr_audio *compr = runtime->private_data; + struct msm_audio *prtd = &compr->prtd; + int dir = 0; + + pr_debug("%s\n", __func__); + + dir = IN; + + 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_compr_close(struct snd_pcm_substream *substream) +{ + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_compr_playback_close(substream); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = EINVAL; + return ret; +} +static int msm_compr_prepare(struct snd_pcm_substream *substream) +{ + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_compr_playback_prepare(substream); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = EINVAL; + return ret; +} + +static snd_pcm_uframes_t msm_compr_pointer(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; + + 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_compr_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + int result = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct compr_audio *compr = runtime->private_data; + struct msm_audio *prtd = &compr->prtd; + + 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_compr_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct compr_audio *compr = runtime->private_data; + struct msm_audio *prtd = &compr->prtd; + struct snd_dma_buffer *dma_buf = &substream->dma_buffer; + struct audio_buffer *buf; + int dir, ret; + + pr_debug("%s\n", __func__); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dir = IN; + else + return -EINVAL; + + ret = q6asm_open_write(prtd->audio_client, compr->codec); + if (ret < 0) { + pr_err("%s: Session out open failed\n", __func__); + return -ENOMEM; + } + ret = q6asm_set_io_mode(prtd->audio_client, ASYNC_IO_MODE); + if (ret < 0) { + pr_err("%s: Set IO mode failed\n", __func__); + return -ENOMEM; + } + + ret = q6asm_audio_client_buf_alloc_contiguous(dir, + prtd->audio_client, + runtime->hw.period_bytes_min, + runtime->hw.periods_max); + 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 = runtime->hw.buffer_bytes_max; + if (!dma_buf->area) + return -ENOMEM; + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + return 0; +} + +static int msm_compr_ioctl(struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + int rc = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct compr_audio *compr = runtime->private_data; + struct msm_audio *prtd = &compr->prtd; + + switch (cmd) { + case SNDRV_COMPRESS_GET_CAPS: + pr_debug("SNDRV_COMPRESS_GET_CAPS\n"); + if (copy_to_user((void *) arg, &compr->info.compr_cap, + sizeof(struct snd_compr_caps))) { + rc = -EFAULT; + pr_err("%s: ERROR: copy to user\n", __func__); + return rc; + } + return 0; + case SNDRV_COMPRESS_SET_PARAMS: + pr_debug("SNDRV_COMPRESS_SET_PARAMS: "); + if (copy_from_user(&compr->info.codec_param, (void *) arg, + sizeof(struct snd_compr_params))) { + rc = -EFAULT; + pr_err("%s: ERROR: copy from user\n", __func__); + return rc; + } + switch (compr->info.codec_param.codec.id) { + case SND_AUDIOCODEC_MP3: + /* For MP3 we dont need any other parameter */ + pr_debug("SND_AUDIOCODEC_MP3\n"); + compr->codec = FORMAT_MP3; + break; + default: + pr_debug("FORMAT_LINEAR_PCM\n"); + compr->codec = FORMAT_LINEAR_PCM; + break; + } + return 0; + case SNDRV_PCM_IOCTL1_RESET: + prtd->cmd_ack = 0; + rc = q6asm_cmd(prtd->audio_client, CMD_FLUSH); + if (rc < 0) + pr_err("%s: flush cmd failed rc=%d\n", __func__, rc); + rc = wait_event_timeout(the_locks.eos_wait, + prtd->cmd_ack, 5 * HZ); + if (rc < 0) + pr_err("Flush cmd timeout\n"); + prtd->pcm_irq_pos = 0; + break; + default: + break; + } + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static struct snd_pcm_ops msm_compr_ops = { + .open = msm_compr_open, + .hw_params = msm_compr_hw_params, + .close = msm_compr_close, + .ioctl = msm_compr_ioctl, + .prepare = msm_compr_prepare, + .trigger = msm_compr_trigger, + .pointer = msm_compr_pointer, + .mmap = msm_compr_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_compr_ops, + .pcm_new = msm_asoc_pcm_new, +}; + +static __devinit int msm_compr_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_compr_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static struct platform_driver msm_compr_driver = { + .driver = { + .name = "msm-compr-dsp", + .owner = THIS_MODULE, + }, + .probe = msm_compr_probe, + .remove = __devexit_p(msm_compr_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_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("PCM module platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/msm/msm-compr-q6.h b/sound/soc/msm/msm-compr-q6.h new file mode 100644 index 00000000000..6dfbccedb9a --- /dev/null +++ b/sound/soc/msm/msm-compr-q6.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2011, 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. + */ + +#ifndef _MSM_COMPR_H +#define _MSM_COMPR_H +#include +#include +#include +#include +#include + +#include "msm-pcm-q6.h" + +struct compr_info { + struct snd_compr_caps compr_cap; + struct snd_compr_codec_caps codec_caps; + struct snd_compr_params codec_param; +}; + +struct compr_audio { + struct msm_audio prtd; + struct compr_info info; + uint32_t codec; +}; + +#endif /*_MSM_COMPR_H*/ diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c index 401c1a984fe..16d149e5053 100644 --- a/sound/soc/msm/msm-dai-fe.c +++ b/sound/soc/msm/msm-dai-fe.c @@ -151,6 +151,20 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .ops = &msm_fe_Multimedia_dai_ops, .name = "MultiMedia3", }, + { + .playback = { + .stream_name = "MultiMedia4 Playback", + .rates = (SNDRV_PCM_RATE_8000_48000 | + SNDRV_PCM_RATE_KNOT), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .name = "MultiMedia4", + }, /* FE DAIs created for hostless operation purpose */ { .playback = { diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c index 97d0760f4da..35731691a19 100644 --- a/sound/soc/msm/msm-pcm-routing.c +++ b/sound/soc/msm/msm-pcm-routing.c @@ -125,6 +125,8 @@ static int fe_dai_map[MSM_FRONTEND_DAI_MAX][2] = { {INVALID_SESSION, INVALID_SESSION}, /* MULTIMEDIA3 */ {INVALID_SESSION, INVALID_SESSION}, + /* MULTIMEDIA4 */ + {INVALID_SESSION, INVALID_SESSION}, }; static void msm_pcm_routing_build_matrix(int fedai_id, int dspst_id, @@ -615,6 +617,9 @@ static const struct snd_kcontrol_new pri_i2s_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_PRI_I2S_RX, MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new slimbus_rx_mixer_controls[] = { @@ -627,6 +632,9 @@ static const struct snd_kcontrol_new slimbus_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SLIMBUS_0_RX, MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new hdmi_mixer_controls[] = { @@ -639,6 +647,9 @@ static const struct snd_kcontrol_new hdmi_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_HDMI_RX, MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new int_bt_sco_rx_mixer_controls[] = { @@ -651,6 +662,9 @@ static const struct snd_kcontrol_new int_bt_sco_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_INT_BT_SCO_RX, MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new int_fm_rx_mixer_controls[] = { @@ -663,6 +677,9 @@ static const struct snd_kcontrol_new int_fm_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_INT_FM_RX, MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new afe_pcm_rx_mixer_controls[] = { @@ -675,6 +692,9 @@ static const struct snd_kcontrol_new afe_pcm_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_AFE_PCM_RX, MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new auxpcm_rx_mixer_controls[] = { @@ -687,6 +707,9 @@ static const struct snd_kcontrol_new auxpcm_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_AUXPCM_RX, MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new mmul1_mixer_controls[] = { @@ -1014,6 +1037,7 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { 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("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), @@ -1111,16 +1135,19 @@ 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_I2S_RX", NULL, "PRI_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", NULL, "SLIMBUS_0_RX Audio Mixer"}, {"HDMI Mixer", "MultiMedia1", "MM_DL1"}, {"HDMI Mixer", "MultiMedia2", "MM_DL2"}, {"HDMI Mixer", "MultiMedia3", "MM_DL3"}, + {"HDMI Mixer", "MultiMedia4", "MM_DL4"}, {"HDMI", NULL, "HDMI Mixer"}, {"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"}, @@ -1130,16 +1157,19 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_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"}, {"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"}, {"PCM_RX", NULL, "AFE_PCM_RX Audio Mixer"}, {"MultiMedia1 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, @@ -1153,6 +1183,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"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", NULL, "AUX_PCM_RX Audio Mixer"}, {"PRI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, diff --git a/sound/soc/msm/msm-pcm-routing.h b/sound/soc/msm/msm-pcm-routing.h index db7552daa5f..b7fc82a01d0 100644 --- a/sound/soc/msm/msm-pcm-routing.h +++ b/sound/soc/msm/msm-pcm-routing.h @@ -31,6 +31,7 @@ enum { MSM_FRONTEND_DAI_MULTIMEDIA1 = 0, MSM_FRONTEND_DAI_MULTIMEDIA2, MSM_FRONTEND_DAI_MULTIMEDIA3, + MSM_FRONTEND_DAI_MULTIMEDIA4, MSM_FRONTEND_DAI_CS_VOICE, MSM_FRONTEND_DAI_VOIP, MSM_FRONTEND_DAI_AFE_RX, diff --git a/sound/soc/msm/qdsp6/q6asm.c b/sound/soc/msm/qdsp6/q6asm.c index 268d2e59d3a..ab7f9f7d12b 100644 --- a/sound/soc/msm/qdsp6/q6asm.c +++ b/sound/soc/msm/qdsp6/q6asm.c @@ -1202,6 +1202,9 @@ int q6asm_open_write(struct audio_client *ac, uint32_t format) case FORMAT_WMA_V10PRO: open.format = WMA_V10PRO; break; + case FORMAT_MP3: + open.format = MP3; + break; default: pr_err("%s: Invalid format[%d]\n", __func__, format); goto fail_cmd; @@ -1865,7 +1868,26 @@ int q6asm_media_format_block(struct audio_client *ac, uint32_t format) q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE); fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FORMAT_UPDATE; - fmt.format = format; + switch (format) { + case FORMAT_V13K: + fmt.format = V13K_FS; + break; + case FORMAT_EVRC: + fmt.format = EVRC_FS; + break; + case FORMAT_AMRWB: + fmt.format = AMRWB_FS; + break; + case FORMAT_AMRNB: + fmt.format = AMRNB_FS; + break; + case FORMAT_MP3: + fmt.format = MP3; + break; + default: + pr_err("Invalid format[%d]\n", format); + goto fail_cmd; + } fmt.cfg_size = 0; rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);