diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c index 68a85c0c8ee..ab9004f882a 100644 --- a/arch/arm/mach-msm/board-8064.c +++ b/arch/arm/mach-msm/board-8064.c @@ -2242,6 +2242,7 @@ static struct platform_device *common_devices[] __initdata = { &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 d5014bd0179..a7147b5405b 100644 --- a/arch/arm/mach-msm/board-8930.c +++ b/arch/arm/mach-msm/board-8930.c @@ -2298,6 +2298,7 @@ static struct platform_device *cdp_devices[] __initdata = { &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 22a50210c9f..641c14456d8 100644 --- a/arch/arm/mach-msm/board-8960.c +++ b/arch/arm/mach-msm/board-8960.c @@ -2695,6 +2695,7 @@ static struct platform_device *cdp_devices[] __initdata = { &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 e5261b0fff9..c6a0441d967 100644 --- a/arch/arm/mach-msm/devices-8064.c +++ b/arch/arm/mach-msm/devices-8064.c @@ -736,6 +736,11 @@ struct platform_device apq_multi_ch_pcm = { .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 ecfafa880b3..8a4b3f4c5e1 100644 --- a/arch/arm/mach-msm/devices-8960.c +++ b/arch/arm/mach-msm/devices-8960.c @@ -1954,6 +1954,11 @@ struct platform_device msm_multi_ch_pcm = { .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 55c1e038ac8..3587672323b 100644 --- a/arch/arm/mach-msm/devices.h +++ b/arch/arm/mach-msm/devices.h @@ -206,6 +206,7 @@ extern struct platform_device msm_device_vidc_720p; 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_voip; 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 979db581281..b7ddb082537 100644 --- a/include/sound/apr_audio.h +++ b/include/sound/apr_audio.h @@ -561,6 +561,7 @@ struct adm_copp_open_command { #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 @@ struct adm_multi_ch_copp_open_command { 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 @@ struct adm_cmd_memory_unmap_regions{ #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 @@ struct adm_copp_open_respond { } __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 @@ struct asm_frame_meta_info { /* 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 @@ struct asm_stream_cmd_open_read { 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 @@ struct asm_stream_cmd_open_read_compressed { } __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 8e15955235d..676c4cb13d1 100644 --- a/include/sound/q6adm.h +++ b/include/sound/q6adm.h @@ -27,7 +27,7 @@ struct route_payload { 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 ea779743149..01f2faca40f 100644 --- a/include/sound/q6asm.h +++ b/include/sound/q6asm.h @@ -160,6 +160,7 @@ struct audio_client { 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 @@ int q6asm_audio_client_buf_free_contiguous(unsigned int dir, 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 43c678d76f4..390c314c355 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-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 81bde3f0c1c..2959fe04194 100644 --- a/sound/soc/msm/apq8064.c +++ b/sound/soc/msm/apq8064.c @@ -1678,6 +1678,37 @@ static struct snd_soc_dai_link msm_dai[] = { .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 35cbb5bbf5d..1dbd6986ad6 100644 --- a/sound/soc/msm/msm-compr-q6.c +++ b/sound/soc/msm/msm-compr-q6.c @@ -548,6 +548,7 @@ static int msm_compr_open(struct snd_pcm_substream *substream) } 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 @@ static int msm_compr_hw_params(struct snd_pcm_substream *substream, } 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 4cd4a2c910d..011ff296fbf 100644 --- a/sound/soc/msm/msm-dai-fe.c +++ b/sound/soc/msm/msm-dai-fe.c @@ -203,6 +203,17 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .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 00000000000..129f69faac8 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 ef58dd16d6b..2d23b4839c5 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 @@ static int msm_pcm_open(struct snd_pcm_substream *substream) 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 @@ static int msm_pcm_open(struct snd_pcm_substream *substream) 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 269b49b1a29..116ce3e760d 100644 --- a/sound/soc/msm/msm-pcm-lpa.c +++ b/sound/soc/msm/msm-pcm-lpa.c @@ -282,6 +282,7 @@ static int msm_pcm_open(struct snd_pcm_substream *substream) } 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 @@ static int msm_pcm_open(struct snd_pcm_substream *substream) 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 942c3ea31aa..74136dc0e8a 100644 --- a/sound/soc/msm/msm-pcm-q6.c +++ b/sound/soc/msm/msm-pcm-q6.c @@ -324,6 +324,7 @@ static int msm_pcm_open(struct snd_pcm_substream *substream) 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 @@ static int msm_pcm_open(struct snd_pcm_substream *substream) 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 @@ static int msm_pcm_playback_close(struct snd_pcm_substream *substream) 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 @@ static int msm_pcm_hw_params(struct snd_pcm_substream *substream, 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 26dbb21993e..374357dabc5 100644 --- a/sound/soc/msm/msm-pcm-routing.c +++ b/sound/soc/msm/msm-pcm-routing.c @@ -38,6 +38,7 @@ struct msm_pcm_routing_bdai_data { 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 @@ void msm_pcm_routing_reg_psthr_stream(int fedai_id, int dspst_id, 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 @@ void msm_pcm_routing_reg_phy_stream(int fedai_id, int dspst_id, int stream_type) 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 @@ void msm_pcm_routing_reg_phy_stream(int fedai_id, int dspst_id, int stream_type) 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 @@ static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set) 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 @@ static const struct snd_kcontrol_new mmul2_mixer_controls[] = { 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 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { 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 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { 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 @@ static const struct snd_soc_dapm_route intercon[] = { {"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 @@ static const struct snd_soc_dapm_route intercon[] = { {"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 @@ static int msm_pcm_routing_close(struct snd_pcm_substream *substream) bedai->active = 0; bedai->sample_rate = 0; bedai->channel = 0; + bedai->perf_mode = false; mutex_unlock(&routing_lock); return 0; @@ -2565,6 +2605,7 @@ static int msm_pcm_routing_prepare(struct snd_pcm_substream *substream) 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 @@ static int msm_pcm_routing_prepare(struct snd_pcm_substream *substream) * 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 45dbf40199b..6b8747502af 100644 --- a/sound/soc/msm/msm-pcm-routing.h +++ b/sound/soc/msm/msm-pcm-routing.h @@ -111,8 +111,8 @@ enum { * 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 e86db109e0d..a577b6ac01b 100644 --- a/sound/soc/msm/msm8930.c +++ b/sound/soc/msm/msm8930.c @@ -1015,6 +1015,53 @@ static struct snd_soc_dai_link msm8930_dai[] = { .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 b10a7ea525e..040bbe046b5 100644 --- a/sound/soc/msm/msm8960.c +++ b/sound/soc/msm/msm8960.c @@ -1348,6 +1348,21 @@ static struct snd_soc_dai_link msm8960_dai_common[] = { .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 @@ static int __init msm8960_audio_init(void) 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 0327e4a6196..6724c545c55 100644 --- a/sound/soc/msm/qdsp6/q6adm.c +++ b/sound/soc/msm/qdsp6/q6adm.c @@ -294,7 +294,8 @@ static int32_t adm_callback(struct apr_client_data *data, void *priv) 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 @@ fail_cmd: 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 @@ int adm_multi_ch_copp_open(int port_id, int path, int rate, int channel_mode, 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 @@ int adm_multi_ch_copp_open(int port_id, int path, int rate, int channel_mode, 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 06be186096f..76940eec944 100644 --- a/sound/soc/msm/qdsp6/q6asm.c +++ b/sound/soc/msm/qdsp6/q6asm.c @@ -209,6 +209,7 @@ static void q6asm_session_free(struct audio_client *ac) session[ac->session] = 0; mutex_unlock(&session_lock); ac->session = 0; + ac->perf_mode = false; return; } @@ -412,6 +413,7 @@ struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv) 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 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) 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 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) 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 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) 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 @@ fail_cmd: 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 @@ int q6asm_open_write(struct audio_client *ac, uint32_t format) 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;