diff options
| author | Sudheer Papothi <spapothi@codeaurora.org> | 2016-02-25 09:23:39 +0530 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-23 20:11:13 -0700 |
| commit | 1155e5b8eaaebccc372bd7272bbbb9d211926ab3 (patch) | |
| tree | 5d18e78e578b94181f320e1bec4cece2396cd9bf | |
| parent | 2df0d98256faa3d6a95d85b3009e5f98f911c543 (diff) | |
ASoC: msm: qdsp6v2: Merge changes from kernel upgrade
Snapshot of msm-compress-q6-v2.c file from msm-3.14 kernel
at the below commit/AU level -
AU_LINUX_ANDROID_LA.HB.1.1.1.05.00.02.063.080
3bc54cf86bdc7affa7cd4bf7faa3c57fe8f8819d
(Merge "msm: camera: Add dummy sub module in sensor pipeline")
Signed-off-by: Banajit Goswami <bgoswami@codeaurora.org>
Signed-off-by: Sudheer Papothi <spapothi@codeaurora.org>
| -rwxr-xr-x[-rw-r--r--] | sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c | 2590 |
1 files changed, 2333 insertions, 257 deletions
diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c index 324fcf2fa861..e4e0e06b315c 100644..100755 --- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -28,6 +28,7 @@ #include <sound/control.h> #include <sound/q6asm-v2.h> #include <sound/pcm_params.h> +#include <sound/audio_effects.h> #include <asm/dma.h> #include <linux/dma-mapping.h> #include <linux/msm_audio_ion.h> @@ -40,10 +41,24 @@ #include <sound/compress_params.h> #include <sound/compress_offload.h> #include <sound/compress_driver.h> +#include <sound/msm-audio-effects-q6-v2.h> +#include <sound/msm-dts-eagle.h> #include "msm-pcm-routing-v2.h" #include "audio_ocmem.h" +#define DSP_PP_BUFFERING_IN_MSEC 25 +#define PARTIAL_DRAIN_ACK_EARLY_BY_MSEC 150 +#define MP3_OUTPUT_FRAME_SZ 1152 +#define AAC_OUTPUT_FRAME_SZ 1024 +#define AC3_OUTPUT_FRAME_SZ 1536 +#define EAC3_OUTPUT_FRAME_SZ 1536 +#define DSP_NUM_OUTPUT_FRAME_BUFFERED 2 +#define FLAC_BLK_SIZE_LIMIT 65535 + +/* decoder parameter length */ +#define DDP_DEC_MAX_NUM_PARAM 18 + /* Default values used if user space does not set */ #define COMPR_PLAYBACK_MIN_FRAGMENT_SIZE (8 * 1024) #define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024) @@ -54,10 +69,52 @@ const DECLARE_TLV_DB_LINEAR(msm_compr_vol_gain, 0, COMPRESSED_LR_VOL_MAX_STEPS); +/* + * LSB 8 bits is used as stream id for some DSP + * commands for compressed playback. + */ +#define STREAM_ID_FROM_TOKEN(i) (i & 0xFF) + +/* Stream id switches between 1 and 2 */ +#define NEXT_STREAM_ID(stream_id) ((stream_id & 1) + 1) + +#define STREAM_ARRAY_INDEX(stream_id) (stream_id - 1) + +#define MAX_NUMBER_OF_STREAMS 2 + +/* + * Max size for getting DTS EAGLE Param through kcontrol + * Safe for both 32 and 64 bit platforms + * 64 = size of kcontrol value array on 64 bit platform + * 4 = size of parameters Eagle expects before cast to 64 bits + * 40 = size of dts_eagle_param_desc + module_id cast to 64 bits + */ +#define DTS_EAGLE_MAX_PARAM_SIZE_FOR_ALSA ((64 * 4) - 40) + +struct msm_compr_gapless_state { + bool set_next_stream_id; + int32_t stream_opened[MAX_NUMBER_OF_STREAMS]; + uint32_t initial_samples_drop; + uint32_t trailing_samples_drop; + uint32_t min_blk_size; + uint32_t max_blk_size; + uint32_t gapless_transition; + bool use_dsp_gapless_mode; +}; + +static unsigned int supported_sample_rates[] = { + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, + 88200, 96000, 176400, 192000 +}; + struct msm_compr_pdata { atomic_t audio_ocmem_req; struct snd_compr_stream *cstream[MSM_FRONTEND_DAI_MAX]; uint32_t volume[MSM_FRONTEND_DAI_MAX][2]; /* For both L & R */ + struct msm_compr_audio_effects *audio_effects[MSM_FRONTEND_DAI_MAX]; + bool use_dsp_gapless_mode; + struct msm_compr_dec_params *dec_params[MSM_FRONTEND_DAI_MAX]; + struct msm_compr_ch_map *ch_map[MSM_FRONTEND_DAI_MAX]; }; struct msm_compr_audio { @@ -68,62 +125,179 @@ struct msm_compr_audio { struct audio_client *audio_client; uint32_t codec; + uint32_t compr_passthr; void *buffer; /* virtual address */ - uint32_t buffer_paddr; /* physical address */ + phys_addr_t buffer_paddr; /* physical address */ uint32_t app_pointer; uint32_t buffer_size; uint32_t byte_offset; - uint32_t copied_total; - uint32_t bytes_received; + uint32_t copied_total; /* bytes consumed by DSP */ + uint32_t bytes_received; /* from userspace */ + uint32_t bytes_sent; /* to DSP */ + + int32_t first_buffer; + int32_t last_buffer; + int32_t partial_drain_delay; uint16_t session_id; uint32_t sample_rate; uint32_t num_channels; + /* + * convention - commands coming from the same thread + * can use the common cmd_ack var. Others (e.g drain/EOS) + * must use separate vars to track command status. + */ uint32_t cmd_ack; uint32_t cmd_interrupt; uint32_t drain_ready; + uint32_t eos_ack; + + uint32_t stream_available; + uint32_t next_stream; + + uint64_t marker_timestamp; + + struct msm_compr_gapless_state gapless_state; atomic_t start; atomic_t eos; atomic_t drain; atomic_t xrun; + atomic_t close; + atomic_t wait_on_close; + atomic_t error; wait_queue_head_t eos_wait; wait_queue_head_t drain_wait; - wait_queue_head_t flush_wait; + wait_queue_head_t close_wait; + wait_queue_head_t wait_for_stream_avail; spinlock_t lock; }; +const u32 compr_codecs[] = {SND_AUDIOCODEC_AC3, SND_AUDIOCODEC_EAC3}; + +struct query_audio_effect { + uint32_t mod_id; + uint32_t parm_id; + uint32_t size; + uint32_t offset; + uint32_t device; +}; + +struct msm_compr_audio_effects { + struct bass_boost_params bass_boost; + struct pbe_params pbe; + struct virtualizer_params virtualizer; + struct reverb_params reverb; + struct eq_params equalizer; + struct soft_volume_params volume; + struct query_audio_effect query; +}; + +struct msm_compr_dec_params { + struct snd_dec_ddp ddp_params; +}; + +struct msm_compr_ch_map { + bool set_ch_map; + char channel_map[PCM_FORMAT_MAX_NUM_CHANNEL]; +}; + +static int msm_compr_send_dec_params(struct snd_compr_stream *cstream, + struct msm_compr_dec_params *dec_params, + int stream_id); + static int msm_compr_set_volume(struct snd_compr_stream *cstream, uint32_t volume_l, uint32_t volume_r) { struct msm_compr_audio *prtd; - int rc = 0; + int rc = 0, i; + uint32_t avg_vol, gain_list[VOLUME_CONTROL_MAX_CHANNELS]; + struct snd_soc_pcm_runtime *rtd; + struct msm_compr_pdata *pdata; + bool use_default = true; + u8 *chmap = NULL; pr_debug("%s: volume_l %d volume_r %d\n", __func__, volume_l, volume_r); + if (!cstream || !cstream->runtime) { + pr_err("%s: session not active\n", __func__); + return -EPERM; + } + rtd = cstream->private_data; prtd = cstream->runtime->private_data; - if (prtd && prtd->audio_client) { - if (volume_l != volume_r) { - pr_debug("%s: call q6asm_set_lrgain\n", __func__); - rc = q6asm_set_lrgain(prtd->audio_client, - volume_l, volume_r); - } else { - pr_debug("%s: call q6asm_set_volume\n", __func__); - rc = q6asm_set_volume(prtd->audio_client, volume_l); - } - if (rc < 0) { - pr_err("%s: Send Volume command failed rc=%d\n", - __func__, rc); - } + + if (!rtd || !rtd->platform || !prtd || !prtd->audio_client) { + pr_err("%s: invalid rtd, prtd or audio client", __func__); + return rc; + } + pdata = snd_soc_platform_get_drvdata(rtd->platform); + + if (prtd->compr_passthr != LEGACY_PCM) { + pr_debug("%s: No volume config for passthrough %d\n", + __func__, prtd->compr_passthr); + return rc; } + use_default = !(pdata->ch_map[rtd->dai_link->be_id]->set_ch_map); + chmap = pdata->ch_map[rtd->dai_link->be_id]->channel_map; + + if (prtd->num_channels > 2) { + /* + * Currently the left and right gains are averaged an applied + * to all channels. This might not be desirable. But currently, + * there exists no API in userspace to send a list of gains for + * each channel either. If such an API does become available, + * the mixer control must be updated to accept more than 2 + * channel gains. + * + */ + avg_vol = (volume_l + volume_r) / 2; + for (i = 0; i < prtd->num_channels; i++) + gain_list[i] = avg_vol; + + } else { + gain_list[0] = volume_l; + gain_list[1] = volume_r; + } + + rc = q6asm_set_multich_gain(prtd->audio_client, prtd->num_channels, + gain_list, chmap, use_default); + + if (rc < 0) + pr_err("%s: Send vol gain command failed rc=%d\n", + __func__, rc); + else + if (msm_dts_eagle_set_stream_gain(prtd->audio_client, + volume_l, volume_r)) + pr_debug("%s: DTS_EAGLE send stream gain failed\n", + __func__); + return rc; } +static int msm_compr_send_ddp_cfg(struct audio_client *ac, + struct snd_dec_ddp *ddp, + int stream_id) +{ + int i, rc; + pr_debug("%s\n", __func__); + for (i = 0; i < ddp->params_length; i++) { + rc = q6asm_ds1_set_stream_endp_params(ac, ddp->params_id[i], + ddp->params_value[i], + stream_id); + if (rc) { + pr_err("sending params_id: %d failed\n", + ddp->params_id[i]); + return rc; + } + } + return 0; +} + static int msm_compr_send_buffer(struct msm_compr_audio *prtd) { int buffer_length; @@ -143,6 +317,13 @@ static int msm_compr_send_buffer(struct msm_compr_audio *prtd) pr_debug("%s: bytes_received = %d copied_total = %d\n", __func__, prtd->bytes_received, prtd->copied_total); + if (prtd->first_buffer && prtd->gapless_state.use_dsp_gapless_mode && + prtd->compr_passthr == LEGACY_PCM) + q6asm_stream_send_meta_data(prtd->audio_client, + prtd->audio_client->stream_id, + prtd->gapless_state.initial_samples_drop, + prtd->gapless_state.trailing_samples_drop); + buffer_length = prtd->codec_param.buffer.fragment_size; bytes_available = prtd->bytes_received - prtd->copied_total; if (bytes_available < prtd->codec_param.buffer.fragment_size) @@ -153,8 +334,13 @@ static int msm_compr_send_buffer(struct msm_compr_audio *prtd) pr_debug("wrap around situation, send partial data %d now", buffer_length); } - param.paddr = prtd->buffer_paddr + prtd->byte_offset; - WARN(param.paddr % 32 != 0, "param.paddr %lx not multiple of 32", param.paddr); + if (buffer_length) { + param.paddr = prtd->buffer_paddr + prtd->byte_offset; + WARN(prtd->byte_offset % 32 != 0, "offset %x not multiple of 32", + prtd->byte_offset); + } + else + param.paddr = prtd->buffer_paddr; param.len = buffer_length; param.msw_ts = 0; @@ -162,11 +348,17 @@ static int msm_compr_send_buffer(struct msm_compr_audio *prtd) param.flags = NO_TIMESTAMP; param.uid = buffer_length; param.metadata_len = 0; + param.last_buffer = prtd->last_buffer; pr_debug("%s: sending %d bytes to DSP byte_offset = %d\n", __func__, buffer_length, prtd->byte_offset); - if (q6asm_async_write(prtd->audio_client, ¶m) < 0) + if (q6asm_async_write(prtd->audio_client, ¶m) < 0) { pr_err("%s:q6asm_async_write failed\n", __func__); + } else { + prtd->bytes_sent += buffer_length; + if (prtd->first_buffer) + prtd->first_buffer = 0; + } return 0; } @@ -175,15 +367,25 @@ static void compr_event_handler(uint32_t opcode, uint32_t token, uint32_t *payload, void *priv) { struct msm_compr_audio *prtd = priv; - struct snd_compr_stream *cstream = prtd->cstream; + struct snd_compr_stream *cstream; + struct audio_client *ac; uint32_t chan_mode = 0; uint32_t sample_rate = 0; - int bytes_available; + int bytes_available, stream_id; + uint32_t stream_index; + unsigned long flags; + + if (!prtd) { + pr_err("%s: prtd is NULL\n", __func__); + return; + } + cstream = prtd->cstream; + ac = prtd->audio_client; pr_debug("%s opcode =%08x\n", __func__, opcode); switch (opcode) { case ASM_DATA_EVENT_WRITE_DONE_V2: - spin_lock_irq(&prtd->lock); + spin_lock_irqsave(&prtd->lock, flags); if (payload[3]) { pr_err("WRITE FAILED w/ err 0x%x !, paddr 0x%x" @@ -208,7 +410,7 @@ static void compr_event_handler(uint32_t opcode, /* Writes must be restarted from _copy() */ pr_debug("write_done received while not started, treat as xrun"); atomic_set(&prtd->xrun, 1); - spin_unlock_irq(&prtd->lock); + spin_unlock_irqrestore(&prtd->lock, flags); break; } @@ -217,25 +419,57 @@ static void compr_event_handler(uint32_t opcode, pr_debug("WRITE_DONE Insufficient data to send. break out\n"); atomic_set(&prtd->xrun, 1); + if (prtd->last_buffer) + prtd->last_buffer = 0; if (atomic_read(&prtd->drain)) { - pr_debug("wake up on drain"); + pr_debug("wake up on drain\n"); prtd->drain_ready = 1; wake_up(&prtd->drain_wait); atomic_set(&prtd->drain, 0); } + } else if ((bytes_available == cstream->runtime->fragment_size) + && atomic_read(&prtd->drain)) { + prtd->last_buffer = 1; + msm_compr_send_buffer(prtd); + prtd->last_buffer = 0; } else msm_compr_send_buffer(prtd); - spin_unlock_irq(&prtd->lock); + spin_unlock_irqrestore(&prtd->lock, flags); break; case ASM_DATA_EVENT_RENDERED_EOS: - pr_debug("ASM_DATA_CMDRSP_EOS\n"); - if (atomic_read(&prtd->eos)) { + spin_lock_irqsave(&prtd->lock, flags); + pr_debug("%s: ASM_DATA_CMDRSP_EOS token 0x%x,stream id %d\n", + __func__, token, STREAM_ID_FROM_TOKEN(token)); + stream_id = STREAM_ID_FROM_TOKEN(token); + if (atomic_read(&prtd->eos) && + !prtd->gapless_state.set_next_stream_id) { pr_debug("ASM_DATA_CMDRSP_EOS wake up\n"); - prtd->cmd_ack = 1; + prtd->eos_ack = 1; wake_up(&prtd->eos_wait); - atomic_set(&prtd->eos, 0); } + atomic_set(&prtd->eos, 0); + stream_index = STREAM_ARRAY_INDEX(stream_id); + if (stream_index >= MAX_NUMBER_OF_STREAMS || + stream_index < 0) { + pr_err("%s: Invalid stream index %d", __func__, + stream_index); + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } + + if (prtd->gapless_state.set_next_stream_id && + prtd->gapless_state.stream_opened[stream_index]) { + pr_debug("%s: CMD_CLOSE stream_id %d\n", + __func__, stream_id); + q6asm_stream_cmd_nowait(ac, CMD_CLOSE, stream_id); + atomic_set(&prtd->close, 1); + prtd->gapless_state.stream_opened[stream_index] = 0; + prtd->gapless_state.set_next_stream_id = false; + } + if (prtd->gapless_state.gapless_transition) + prtd->gapless_state.gapless_transition = 0; + spin_unlock_irqrestore(&prtd->lock, flags); break; case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY: case ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY: { @@ -254,9 +488,9 @@ static void compr_event_handler(uint32_t opcode, /* check if the first buffer need to be sent to DSP */ pr_debug("ASM_SESSION_CMD_RUN_V2\n"); - spin_lock_irq(&prtd->lock); /* FIXME: A state is a better way, dealing with this*/ - if (!prtd->copied_total) { + spin_lock_irqsave(&prtd->lock, flags); + if (!prtd->bytes_sent) { bytes_available = prtd->bytes_received - prtd->copied_total; if (bytes_available < cstream->runtime->fragment_size) { pr_debug("CMD_RUN_V2 Insufficient data to send. break out\n"); @@ -264,12 +498,69 @@ static void compr_event_handler(uint32_t opcode, } else msm_compr_send_buffer(prtd); } - spin_unlock_irq(&prtd->lock); + + /* + * The condition below ensures playback finishes in the + * follow cornercase + * WRITE(last buffer) + * WAIT_FOR_DRAIN + * PAUSE + * WRITE_DONE(X) + * RESUME + */ + if ((prtd->copied_total == prtd->bytes_sent) && + atomic_read(&prtd->drain)) { + pr_debug("RUN ack, wake up & continue pending drain\n"); + + if (prtd->last_buffer) + prtd->last_buffer = 0; + + prtd->drain_ready = 1; + wake_up(&prtd->drain_wait); + atomic_set(&prtd->drain, 0); + } + + spin_unlock_irqrestore(&prtd->lock, flags); break; case ASM_STREAM_CMD_FLUSH: - pr_debug("ASM_STREAM_CMD_FLUSH\n"); + pr_debug("%s: ASM_STREAM_CMD_FLUSH:", __func__); + pr_debug("token 0x%x, stream id %d\n", token, + STREAM_ID_FROM_TOKEN(token)); prtd->cmd_ack = 1; - wake_up(&prtd->flush_wait); + break; + case ASM_DATA_CMD_REMOVE_INITIAL_SILENCE: + pr_debug("%s: ASM_DATA_CMD_REMOVE_INITIAL_SILENCE:", + __func__); + pr_debug("token 0x%x, stream id = %d\n", token, + STREAM_ID_FROM_TOKEN(token)); + break; + case ASM_DATA_CMD_REMOVE_TRAILING_SILENCE: + pr_debug("%s: ASM_DATA_CMD_REMOVE_TRAILING_SILENCE:", + __func__); + pr_debug("token = 0x%x, stream id = %d\n", token, + STREAM_ID_FROM_TOKEN(token)); + break; + case ASM_STREAM_CMD_CLOSE: + pr_debug("%s: ASM_DATA_CMD_CLOSE:", __func__); + pr_debug("token 0x%x, stream id %d\n", token, + STREAM_ID_FROM_TOKEN(token)); + /* + * wakeup wait for stream avail on stream 3 + * after stream 1 ends. + */ + if (prtd->next_stream) { + pr_debug("%s:CLOSE:wakeup wait for stream\n", + __func__); + prtd->stream_available = 1; + wake_up(&prtd->wait_for_stream_avail); + prtd->next_stream = 0; + } + if (atomic_read(&prtd->close) && + atomic_read(&prtd->wait_on_close)) { + prtd->cmd_ack = 1; + wake_up(&prtd->close_wait); + } + atomic_set(&prtd->close, 0); break; default: break; @@ -277,10 +568,26 @@ static void compr_event_handler(uint32_t opcode, break; } case ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3: - pr_debug("ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3\n"); + pr_debug("%s: ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3\n", + __func__); + break; + case RESET_EVENTS: + pr_err("%s: Received reset events CB, move to error state", + __func__); + spin_lock_irqsave(&prtd->lock, flags); + snd_compr_fragment_elapsed(cstream); + prtd->copied_total = prtd->bytes_received; + atomic_set(&prtd->error, 1); + wake_up(&prtd->drain_wait); + if (atomic_cmpxchg(&prtd->eos, 1, 0)) { + pr_debug("%s:unblock eos wait queues", __func__); + wake_up(&prtd->eos_wait); + } + spin_unlock_irqrestore(&prtd->lock, flags); break; default: - pr_debug("Not Supported Event opcode[0x%x]\n", opcode); + pr_debug("%s: Not Supported Event opcode[0x%x]\n", + __func__, opcode); break; } } @@ -297,33 +604,237 @@ static void populate_codec_list(struct msm_compr_audio *prtd) COMPR_PLAYBACK_MIN_NUM_FRAGMENTS; prtd->compr_cap.max_fragments = COMPR_PLAYBACK_MAX_NUM_FRAGMENTS; - prtd->compr_cap.num_codecs = 2; + prtd->compr_cap.num_codecs = 12; prtd->compr_cap.codecs[0] = SND_AUDIOCODEC_MP3; prtd->compr_cap.codecs[1] = SND_AUDIOCODEC_AAC; + prtd->compr_cap.codecs[2] = SND_AUDIOCODEC_AC3; + prtd->compr_cap.codecs[3] = SND_AUDIOCODEC_EAC3; + prtd->compr_cap.codecs[4] = SND_AUDIOCODEC_MP2; + prtd->compr_cap.codecs[5] = SND_AUDIOCODEC_PCM; + prtd->compr_cap.codecs[6] = SND_AUDIOCODEC_WMA; + prtd->compr_cap.codecs[7] = SND_AUDIOCODEC_WMA_PRO; + prtd->compr_cap.codecs[8] = SND_AUDIOCODEC_FLAC; + prtd->compr_cap.codecs[9] = SND_AUDIOCODEC_VORBIS; + prtd->compr_cap.codecs[10] = SND_AUDIOCODEC_ALAC; + prtd->compr_cap.codecs[11] = SND_AUDIOCODEC_APE; } -static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream) +static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream, + int stream_id) { struct snd_compr_runtime *runtime = cstream->runtime; struct msm_compr_audio *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct msm_compr_pdata *pdata = + snd_soc_platform_get_drvdata(rtd->platform); struct asm_aac_cfg aac_cfg; + struct asm_wma_cfg wma_cfg; + struct asm_wmapro_cfg wma_pro_cfg; + struct asm_flac_cfg flac_cfg; + struct asm_vorbis_cfg vorbis_cfg; + struct asm_alac_cfg alac_cfg; + struct asm_ape_cfg ape_cfg; + + u32 cfg; int ret = 0; + uint16_t bit_width = 16; + bool use_default_chmap = true; + char *chmap = NULL; switch (prtd->codec) { + case FORMAT_LINEAR_PCM: + pr_debug("SND_AUDIOCODEC_PCM\n"); + if (pdata->ch_map[rtd->dai_link->be_id]) { + use_default_chmap = + !(pdata->ch_map[rtd->dai_link->be_id]->set_ch_map); + chmap = + pdata->ch_map[rtd->dai_link->be_id]->channel_map; + } + if (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S24_LE) + bit_width = 24; + ret = q6asm_media_format_block_pcm_format_support_v2( + prtd->audio_client, + prtd->sample_rate, + prtd->num_channels, + bit_width, stream_id, + use_default_chmap, + chmap); + if (ret < 0) + pr_err("%s: CMD Format block failed\n", __func__); + + break; case FORMAT_MP3: + pr_debug("SND_AUDIOCODEC_MP3\n"); /* no media format block needed */ break; case FORMAT_MPEG4_AAC: + pr_debug("SND_AUDIOCODEC_AAC\n"); memset(&aac_cfg, 0x0, sizeof(struct asm_aac_cfg)); aac_cfg.aot = AAC_ENC_MODE_EAAC_P; - aac_cfg.format = 0x03; + if (prtd->codec_param.codec.format == + SND_AUDIOSTREAMFORMAT_MP4ADTS) + aac_cfg.format = 0x0; + else + aac_cfg.format = 0x03; aac_cfg.ch_cfg = prtd->num_channels; aac_cfg.sample_rate = prtd->sample_rate; - ret = q6asm_media_format_block_aac(prtd->audio_client, - &aac_cfg); + ret = q6asm_stream_media_format_block_aac(prtd->audio_client, + &aac_cfg, stream_id); if (ret < 0) pr_err("%s: CMD Format block failed\n", __func__); break; + case FORMAT_AC3: + pr_debug("SND_AUDIOCODEC_AC3\n"); + break; + case FORMAT_EAC3: + pr_debug("SND_AUDIOCODEC_EAC3\n"); + break; + case FORMAT_WMA_V9: + pr_debug("SND_AUDIOCODEC_WMA\n"); + memset(&wma_cfg, 0x0, sizeof(struct asm_wma_cfg)); + wma_cfg.format_tag = prtd->codec_param.codec.format; + wma_cfg.ch_cfg = prtd->codec_param.codec.ch_in; + wma_cfg.sample_rate = prtd->sample_rate; + wma_cfg.avg_bytes_per_sec = + prtd->codec_param.codec.bit_rate/8; + wma_cfg.block_align = + prtd->codec_param.codec.options.wma.super_block_align; + wma_cfg.valid_bits_per_sample = + prtd->codec_param.codec.options.wma.bits_per_sample; + wma_cfg.ch_mask = + prtd->codec_param.codec.options.wma.channelmask; + wma_cfg.encode_opt = + prtd->codec_param.codec.options.wma.encodeopt; + ret = q6asm_media_format_block_wma(prtd->audio_client, + &wma_cfg, stream_id); + if (ret < 0) + pr_err("%s: CMD Format block failed\n", __func__); + break; + case FORMAT_WMA_V10PRO: + pr_debug("SND_AUDIOCODEC_WMA_PRO\n"); + memset(&wma_pro_cfg, 0x0, sizeof(struct asm_wmapro_cfg)); + wma_pro_cfg.format_tag = prtd->codec_param.codec.format; + wma_pro_cfg.ch_cfg = prtd->codec_param.codec.ch_in; + wma_pro_cfg.sample_rate = + prtd->sample_rate; + wma_pro_cfg.avg_bytes_per_sec = + prtd->codec_param.codec.bit_rate/8; + wma_pro_cfg.block_align = + prtd->codec_param.codec.options.wma.super_block_align; + wma_pro_cfg.valid_bits_per_sample = + prtd->codec_param.codec.options.wma.bits_per_sample; + wma_pro_cfg.ch_mask = + prtd->codec_param.codec.options.wma.channelmask; + wma_pro_cfg.encode_opt = + prtd->codec_param.codec.options.wma.encodeopt; + wma_pro_cfg.adv_encode_opt = + prtd->codec_param.codec.options.wma.encodeopt1; + wma_pro_cfg.adv_encode_opt2 = + prtd->codec_param.codec.options.wma.encodeopt2; + ret = q6asm_media_format_block_wmapro(prtd->audio_client, + &wma_pro_cfg, stream_id); + if (ret < 0) + pr_err("%s: CMD Format block failed\n", __func__); + break; + case FORMAT_MP2: + pr_debug("%s: SND_AUDIOCODEC_MP2\n", __func__); + break; + case FORMAT_FLAC: + pr_debug("%s: SND_AUDIOCODEC_FLAC\n", __func__); + memset(&flac_cfg, 0x0, sizeof(struct asm_flac_cfg)); + flac_cfg.ch_cfg = prtd->num_channels; + flac_cfg.sample_rate = prtd->sample_rate; + flac_cfg.stream_info_present = 1; + flac_cfg.sample_size = + prtd->codec_param.codec.options.flac_dec.sample_size; + flac_cfg.min_blk_size = + prtd->codec_param.codec.options.flac_dec.min_blk_size; + flac_cfg.max_blk_size = + prtd->codec_param.codec.options.flac_dec.max_blk_size; + flac_cfg.max_frame_size = + prtd->codec_param.codec.options.flac_dec.max_frame_size; + flac_cfg.min_frame_size = + prtd->codec_param.codec.options.flac_dec.min_frame_size; + + ret = q6asm_stream_media_format_block_flac(prtd->audio_client, + &flac_cfg, stream_id); + if (ret < 0) + pr_err("%s: CMD Format block failed ret %d\n", + __func__, ret); + + break; + case FORMAT_VORBIS: + pr_debug("%s: SND_AUDIOCODEC_VORBIS\n", __func__); + memset(&vorbis_cfg, 0x0, sizeof(struct asm_vorbis_cfg)); + cfg = prtd->codec_param.codec.options.vorbis_dec.bit_stream_fmt; + vorbis_cfg.bit_stream_fmt = cfg; + + ret = q6asm_stream_media_format_block_vorbis( + prtd->audio_client, &vorbis_cfg, + stream_id); + if (ret < 0) + pr_err("%s: CMD Format block failed ret %d\n", + __func__, ret); + + break; + case FORMAT_ALAC: + pr_debug("%s: SND_AUDIOCODEC_ALAC\n", __func__); + memset(&alac_cfg, 0x0, sizeof(struct asm_alac_cfg)); + alac_cfg.num_channels = prtd->num_channels; + alac_cfg.sample_rate = prtd->sample_rate; + alac_cfg.frame_length = + prtd->codec_param.codec.options.alac.frame_length; + alac_cfg.compatible_version = + prtd->codec_param.codec.options.alac.compatible_version; + alac_cfg.bit_depth = + prtd->codec_param.codec.options.alac.bit_depth; + alac_cfg.pb = prtd->codec_param.codec.options.alac.pb; + alac_cfg.mb = prtd->codec_param.codec.options.alac.mb; + alac_cfg.kb = prtd->codec_param.codec.options.alac.kb; + alac_cfg.max_run = prtd->codec_param.codec.options.alac.max_run; + alac_cfg.max_frame_bytes = + prtd->codec_param.codec.options.alac.max_frame_bytes; + alac_cfg.avg_bit_rate = + prtd->codec_param.codec.options.alac.avg_bit_rate; + alac_cfg.channel_layout_tag = + prtd->codec_param.codec.options.alac.channel_layout_tag; + + ret = q6asm_media_format_block_alac(prtd->audio_client, + &alac_cfg, stream_id); + if (ret < 0) + pr_err("%s: CMD Format block failed ret %d\n", + __func__, ret); + break; + case FORMAT_APE: + pr_debug("%s: SND_AUDIOCODEC_APE\n", __func__); + memset(&ape_cfg, 0x0, sizeof(struct asm_ape_cfg)); + ape_cfg.num_channels = prtd->num_channels; + ape_cfg.sample_rate = prtd->sample_rate; + ape_cfg.compatible_version = + prtd->codec_param.codec.options.ape.compatible_version; + ape_cfg.compression_level = + prtd->codec_param.codec.options.ape.compression_level; + ape_cfg.format_flags = + prtd->codec_param.codec.options.ape.format_flags; + ape_cfg.blocks_per_frame = + prtd->codec_param.codec.options.ape.blocks_per_frame; + ape_cfg.final_frame_blocks = + prtd->codec_param.codec.options.ape.final_frame_blocks; + ape_cfg.total_frames = + prtd->codec_param.codec.options.ape.total_frames; + ape_cfg.bits_per_sample = + prtd->codec_param.codec.options.ape.bits_per_sample; + ape_cfg.seek_table_present = + prtd->codec_param.codec.options.ape.seek_table_present; + + ret = q6asm_media_format_block_ape(prtd->audio_client, + &ape_cfg, stream_id); + + if (ret < 0) + pr_err("%s: CMD Format block failed ret %d\n", + __func__, ret); + break; + default: pr_debug("%s, unsupported format, skip", __func__); break; @@ -331,6 +842,49 @@ static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream) return ret; } +static int msm_compr_init_pp_params(struct snd_compr_stream *cstream, + struct audio_client *ac) +{ + int ret = 0; + struct asm_softvolume_params softvol = { + .period = SOFT_VOLUME_PERIOD, + .step = SOFT_VOLUME_STEP, + .rampingcurve = SOFT_VOLUME_CURVE_LINEAR, + }; + + switch (ac->topology) { + case ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS: /* HPX + SA+ topology */ + + ret = q6asm_set_softvolume_v2(ac, &softvol, + SOFT_VOLUME_INSTANCE_1); + if (ret < 0) + pr_err("%s: Send SoftVolume Param failed ret=%d\n", + __func__, ret); + + ret = q6asm_set_softvolume_v2(ac, &softvol, + SOFT_VOLUME_INSTANCE_2); + if (ret < 0) + pr_err("%s: Send SoftVolume2 Param failed ret=%d\n", + __func__, ret); + /* + * HPX module init is trigerred from HAL using ioctl + * DTS_EAGLE_MODULE_ENABLE when stream starts + */ + break; + case ASM_STREAM_POSTPROC_TOPO_ID_DTS_HPX: /* HPX topology */ + break; + default: + ret = q6asm_set_softvolume_v2(ac, &softvol, + SOFT_VOLUME_INSTANCE_1); + if (ret < 0) + pr_err("%s: Send SoftVolume Param failed ret=%d\n", + __func__, ret); + + break; + } + return ret; +} + static int msm_compr_configure_dsp(struct snd_compr_stream *cstream) { struct snd_compr_runtime *runtime = cstream->runtime; @@ -338,6 +892,8 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream) struct snd_soc_pcm_runtime *soc_prtd = cstream->private_data; uint16_t bits_per_sample = 16; int dir = IN, ret = 0; + struct audio_client *ac = prtd->audio_client; + uint32_t stream_index; struct asm_softpause_params softpause = { .enable = SOFT_PAUSE_ENABLE, .period = SOFT_PAUSE_PERIOD, @@ -350,49 +906,86 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream) .rampingcurve = SOFT_VOLUME_CURVE_LINEAR, }; - pr_debug("%s\n", __func__); - ret = q6asm_open_write_v2(prtd->audio_client, - prtd->codec, bits_per_sample); - if (ret < 0) { - pr_err("%s: Session out open failed\n", __func__); - return -ENOMEM; - } + pr_debug("%s: stream_id %d\n", __func__, ac->stream_id); + if (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S24_LE) + bits_per_sample = 24; + else if (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S32_LE) + bits_per_sample = 32; + + if (prtd->compr_passthr != LEGACY_PCM) { + ret = q6asm_open_write_compressed(ac, prtd->codec, + prtd->compr_passthr); + if (ret < 0) { + pr_err("%s:ASM open write err[%d] for compr_type[%d]\n", + __func__, ret, prtd->compr_passthr); + return ret; + } + ret = msm_pcm_routing_reg_phy_compr_stream( + soc_prtd->dai_link->be_id, + ac->perf_mode, + prtd->session_id, + SNDRV_PCM_STREAM_PLAYBACK, + prtd->compr_passthr); + if (ret) { + pr_err("%s: compr stream reg failed:%d\n", __func__, + ret); + return ret; + } + } else { + pr_debug("%s: stream_id %d bits_per_sample %d\n", + __func__, ac->stream_id, bits_per_sample); + ret = q6asm_stream_open_write_v2(ac, + prtd->codec, bits_per_sample, + ac->stream_id, + prtd->gapless_state.use_dsp_gapless_mode); + if (ret < 0) { + pr_err("%s:ASM open write err[%d] for compr type[%d]\n", + __func__, ret, prtd->compr_passthr); + return -ENOMEM; + } - pr_debug("%s be_id %d\n", __func__, soc_prtd->dai_link->be_id); - msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id, - prtd->audio_client->perf_mode, + pr_debug("%s: be_id %d\n", __func__, soc_prtd->dai_link->be_id); + ret = msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id, + ac->perf_mode, prtd->session_id, SNDRV_PCM_STREAM_PLAYBACK); + if (ret) { + pr_err("%s: stream reg failed:%d\n", __func__, ret); + return ret; + } + } ret = msm_compr_set_volume(cstream, 0, 0); if (ret < 0) pr_err("%s : Set Volume failed : %d", __func__, ret); - ret = q6asm_set_softpause(prtd->audio_client, - &softpause); + ret = q6asm_set_softpause(ac, &softpause); if (ret < 0) pr_err("%s: Send SoftPause Param failed ret=%d\n", - __func__, ret); - - ret = q6asm_set_softvolume(prtd->audio_client, &softvol); + __func__, ret); + ret = q6asm_set_softvolume(ac, &softvol); if (ret < 0) pr_err("%s: Send SoftVolume Param failed ret=%d\n", - __func__, ret); + __func__, ret); - ret = q6asm_set_io_mode(prtd->audio_client, - (COMPRESSED_IO | ASYNC_IO_MODE)); + ret = q6asm_set_io_mode(ac, (COMPRESSED_STREAM_IO | ASYNC_IO_MODE)); if (ret < 0) { pr_err("%s: Set IO mode failed\n", __func__); return -EINVAL; } + stream_index = STREAM_ARRAY_INDEX(ac->stream_id); + if (stream_index >= MAX_NUMBER_OF_STREAMS || stream_index < 0) { + pr_err("%s: Invalid stream index:%d", __func__, stream_index); + return -EINVAL; + } + prtd->gapless_state.stream_opened[stream_index] = 1; runtime->fragments = prtd->codec_param.buffer.fragments; runtime->fragment_size = prtd->codec_param.buffer.fragment_size; pr_debug("allocate %d buffers each of size %d\n", runtime->fragments, runtime->fragment_size); - ret = q6asm_audio_client_buf_alloc_contiguous(dir, - prtd->audio_client, + ret = q6asm_audio_client_buf_alloc_contiguous(dir, ac, runtime->fragment_size, runtime->fragments); if (ret < 0) { @@ -404,12 +997,12 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream) prtd->copied_total = 0; prtd->app_pointer = 0; prtd->bytes_received = 0; - prtd->buffer = prtd->audio_client->port[dir].buf[0].data; - prtd->buffer_paddr = prtd->audio_client->port[dir].buf[0].phys; + prtd->bytes_sent = 0; + prtd->buffer = ac->port[dir].buf[0].data; + prtd->buffer_paddr = ac->port[dir].buf[0].phys; prtd->buffer_size = runtime->fragments * runtime->fragment_size; - ret = msm_compr_send_media_format_block(cstream); - + ret = msm_compr_send_media_format_block(cstream, ac->stream_id); if (ret < 0) { pr_err("%s, failed to send media format block\n", __func__); } @@ -432,12 +1025,34 @@ static int msm_compr_open(struct snd_compr_stream *cstream) return -ENOMEM; } + runtime->private_data = NULL; prtd->cstream = cstream; pdata->cstream[rtd->dai_link->be_id] = cstream; + pdata->audio_effects[rtd->dai_link->be_id] = + kzalloc(sizeof(struct msm_compr_audio_effects), GFP_KERNEL); + if (!pdata->audio_effects[rtd->dai_link->be_id]) { + pr_err("%s: Could not allocate memory for effects\n", __func__); + pdata->cstream[rtd->dai_link->be_id] = NULL; + kfree(prtd); + return -ENOMEM; + } + pdata->dec_params[rtd->dai_link->be_id] = + kzalloc(sizeof(struct msm_compr_dec_params), GFP_KERNEL); + if (!pdata->dec_params[rtd->dai_link->be_id]) { + pr_err("%s: Could not allocate memory for dec params\n", + __func__); + kfree(pdata->audio_effects[rtd->dai_link->be_id]); + pdata->cstream[rtd->dai_link->be_id] = NULL; + kfree(prtd); + return -ENOMEM; + } prtd->audio_client = q6asm_audio_client_alloc( (app_cb)compr_event_handler, prtd); if (!prtd->audio_client) { - pr_err("%s: Could not allocate memory\n", __func__); + pr_err("%s: Could not allocate memory for client\n", __func__); + kfree(pdata->audio_effects[rtd->dai_link->be_id]); + kfree(pdata->dec_params[rtd->dai_link->be_id]); + pdata->cstream[rtd->dai_link->be_id] = NULL; kfree(prtd); return -ENOMEM; } @@ -447,11 +1062,26 @@ static int msm_compr_open(struct snd_compr_stream *cstream) prtd->session_id = prtd->audio_client->session; prtd->codec = FORMAT_MP3; prtd->bytes_received = 0; + prtd->bytes_sent = 0; prtd->copied_total = 0; prtd->byte_offset = 0; prtd->sample_rate = 44100; prtd->num_channels = 2; prtd->drain_ready = 0; + prtd->last_buffer = 0; + prtd->first_buffer = 1; + prtd->partial_drain_delay = 0; + prtd->next_stream = 0; + memset(&prtd->gapless_state, 0, sizeof(struct msm_compr_gapless_state)); + prtd->gapless_state.min_blk_size = 65537; + prtd->gapless_state.max_blk_size = 65537; + /* + * Update the use_dsp_gapless_mode from gapless struture with the value + * part of platform data. + */ + prtd->gapless_state.use_dsp_gapless_mode = pdata->use_dsp_gapless_mode; + + pr_debug("%s: gapless mode %d", __func__, pdata->use_dsp_gapless_mode); spin_lock_init(&prtd->lock); @@ -459,10 +1089,14 @@ static int msm_compr_open(struct snd_compr_stream *cstream) atomic_set(&prtd->start, 0); atomic_set(&prtd->drain, 0); atomic_set(&prtd->xrun, 0); + atomic_set(&prtd->close, 0); + atomic_set(&prtd->wait_on_close, 0); + atomic_set(&prtd->error, 0); init_waitqueue_head(&prtd->eos_wait); init_waitqueue_head(&prtd->drain_wait); - init_waitqueue_head(&prtd->flush_wait); + init_waitqueue_head(&prtd->close_wait); + init_waitqueue_head(&prtd->wait_for_stream_avail); runtime->private_data = prtd; populate_codec_list(prtd); @@ -483,99 +1117,223 @@ static int msm_compr_open(struct snd_compr_stream *cstream) static int msm_compr_free(struct snd_compr_stream *cstream) { - struct snd_compr_runtime *runtime = cstream->runtime; - struct msm_compr_audio *prtd = runtime->private_data; - struct snd_soc_pcm_runtime *soc_prtd = cstream->private_data; - struct msm_compr_pdata *pdata = - snd_soc_platform_get_drvdata(soc_prtd->platform); - int dir = IN, ret = 0; + struct snd_compr_runtime *runtime; + struct msm_compr_audio *prtd; + struct snd_soc_pcm_runtime *soc_prtd; + struct msm_compr_pdata *pdata; + struct audio_client *ac; + int dir = IN, ret = 0, stream_id; + unsigned long flags; + uint32_t stream_index; pr_debug("%s\n", __func__); + + if (!cstream) { + pr_err("%s cstream is null\n", __func__); + return 0; + } + runtime = cstream->runtime; + soc_prtd = cstream->private_data; + if (!runtime || !soc_prtd || !(soc_prtd->platform)) { + pr_err("%s runtime or soc_prtd or platform is null\n", + __func__); + return 0; + } + prtd = runtime->private_data; + if (!prtd) { + pr_err("%s prtd is null\n", __func__); + return 0; + } + pdata = snd_soc_platform_get_drvdata(soc_prtd->platform); + ac = prtd->audio_client; + if (!pdata || !ac) { + pr_err("%s pdata or ac is null\n", __func__); + return 0; + } + if (atomic_read(&prtd->eos)) { + ret = wait_event_timeout(prtd->eos_wait, + prtd->eos_ack, 5 * HZ); + if (!ret) + pr_err("%s: CMD_EOS failed\n", __func__); + } + if (atomic_read(&prtd->close)) { + prtd->cmd_ack = 0; + atomic_set(&prtd->wait_on_close, 1); + ret = wait_event_timeout(prtd->close_wait, + prtd->cmd_ack, 5 * HZ); + if (!ret) + pr_err("%s: CMD_CLOSE failed\n", __func__); + } + + spin_lock_irqsave(&prtd->lock, flags); + stream_id = ac->stream_id; + stream_index = STREAM_ARRAY_INDEX(NEXT_STREAM_ID(stream_id)); + + if ((stream_index < MAX_NUMBER_OF_STREAMS && stream_index >= 0) && + (prtd->gapless_state.stream_opened[stream_index])) { + prtd->gapless_state.stream_opened[stream_index] = 0; + spin_unlock_irqrestore(&prtd->lock, flags); + pr_debug(" close stream %d", NEXT_STREAM_ID(stream_id)); + q6asm_stream_cmd(ac, CMD_CLOSE, NEXT_STREAM_ID(stream_id)); + spin_lock_irqsave(&prtd->lock, flags); + } + + stream_index = STREAM_ARRAY_INDEX(stream_id); + if ((stream_index < MAX_NUMBER_OF_STREAMS && stream_index >= 0) && + (prtd->gapless_state.stream_opened[stream_index])) { + prtd->gapless_state.stream_opened[stream_index] = 0; + spin_unlock_irqrestore(&prtd->lock, flags); + pr_debug("close stream %d", stream_id); + q6asm_stream_cmd(ac, CMD_CLOSE, stream_id); + spin_lock_irqsave(&prtd->lock, flags); + } + spin_unlock_irqrestore(&prtd->lock, flags); + pdata->cstream[soc_prtd->dai_link->be_id] = NULL; if (cstream->direction == SND_COMPRESS_PLAYBACK) { if (atomic_read(&pdata->audio_ocmem_req) > 1) atomic_dec(&pdata->audio_ocmem_req); else if (atomic_cmpxchg(&pdata->audio_ocmem_req, 1, 0)) audio_ocmem_process_req(AUDIO, false); - msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id, SNDRV_PCM_STREAM_PLAYBACK); } pr_debug("%s: ocmem_req: %d\n", __func__, atomic_read(&pdata->audio_ocmem_req)); + q6asm_audio_client_buf_free_contiguous(dir, ac); - if (atomic_read(&prtd->eos)) { - ret = wait_event_timeout(prtd->eos_wait, - prtd->cmd_ack, 5 * HZ); - if (!ret) - 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); - - q6asm_audio_client_free(prtd->audio_client); + q6asm_audio_client_free(ac); + kfree(pdata->audio_effects[soc_prtd->dai_link->be_id]); + pdata->audio_effects[soc_prtd->dai_link->be_id] = NULL; + kfree(pdata->dec_params[soc_prtd->dai_link->be_id]); + pdata->dec_params[soc_prtd->dai_link->be_id] = NULL; kfree(prtd); return 0; } +static bool msm_compr_validate_codec_compr(__u32 codec_id) +{ + int32_t i; + + for (i = 0; i < ARRAY_SIZE(compr_codecs); i++) { + if (compr_codecs[i] == codec_id) + return true; + } + return false; +} + /* compress stream operations */ static int msm_compr_set_params(struct snd_compr_stream *cstream, struct snd_compr_params *params) { struct snd_compr_runtime *runtime = cstream->runtime; struct msm_compr_audio *prtd = runtime->private_data; - int ret = 0; + int ret = 0, frame_sz = 0, delay_time_ms = 0; + int i, num_rates; pr_debug("%s\n", __func__); - memcpy(&prtd->codec_param, params, sizeof(struct snd_compr_params)); + num_rates = sizeof(supported_sample_rates)/sizeof(unsigned int); + for (i = 0; i < num_rates; i++) + if (params->codec.sample_rate == supported_sample_rates[i]) + break; + if (i == num_rates) + return -EINVAL; - /* ToDo: remove duplicates */ - prtd->num_channels = prtd->codec_param.codec.ch_in; + if (prtd->codec_param.codec.compr_passthr >= 0 && + prtd->codec_param.codec.compr_passthr <= 2) + prtd->compr_passthr = prtd->codec_param.codec.compr_passthr; + else + prtd->compr_passthr = LEGACY_PCM; + pr_debug("%s: compr_passthr = %d", __func__, prtd->compr_passthr); + if (prtd->compr_passthr != LEGACY_PCM) { + pr_debug("%s: Reset gapless mode playback for compr_type[%d]\n", + __func__, prtd->compr_passthr); + prtd->gapless_state.use_dsp_gapless_mode = 0; + if (!msm_compr_validate_codec_compr(params->codec.id)) { + pr_err("%s codec not supported in passthrough,id =%d\n", + __func__, params->codec.id); + return -EINVAL; + } + } + + switch (params->codec.id) { + case SND_AUDIOCODEC_PCM: { + pr_debug("SND_AUDIOCODEC_PCM\n"); + prtd->codec = FORMAT_LINEAR_PCM; + break; + } - switch (prtd->codec_param.codec.sample_rate) { - case SNDRV_PCM_RATE_8000: - prtd->sample_rate = 8000; + case SND_AUDIOCODEC_MP3: { + pr_debug("SND_AUDIOCODEC_MP3\n"); + prtd->codec = FORMAT_MP3; + frame_sz = MP3_OUTPUT_FRAME_SZ; break; - case SNDRV_PCM_RATE_11025: - prtd->sample_rate = 11025; + } + + case SND_AUDIOCODEC_AAC: { + pr_debug("SND_AUDIOCODEC_AAC\n"); + prtd->codec = FORMAT_MPEG4_AAC; + frame_sz = AAC_OUTPUT_FRAME_SZ; break; - /* ToDo: What about 12K and 24K sample rates ? */ - case SNDRV_PCM_RATE_16000: - prtd->sample_rate = 16000; + } + + case SND_AUDIOCODEC_AC3: { + pr_debug("SND_AUDIOCODEC_AC3\n"); + prtd->codec = FORMAT_AC3; + frame_sz = AC3_OUTPUT_FRAME_SZ; break; - case SNDRV_PCM_RATE_22050: - prtd->sample_rate = 22050; + } + + case SND_AUDIOCODEC_EAC3: { + pr_debug("SND_AUDIOCODEC_EAC3\n"); + prtd->codec = FORMAT_EAC3; + frame_sz = EAC3_OUTPUT_FRAME_SZ; break; - case SNDRV_PCM_RATE_32000: - prtd->sample_rate = 32000; + } + + case SND_AUDIOCODEC_MP2: { + pr_debug("SND_AUDIOCODEC_MP2\n"); + prtd->codec = FORMAT_MP2; break; - case SNDRV_PCM_RATE_44100: - prtd->sample_rate = 44100; + } + + case SND_AUDIOCODEC_WMA: { + pr_debug("SND_AUDIOCODEC_WMA\n"); + prtd->codec = FORMAT_WMA_V9; break; - case SNDRV_PCM_RATE_48000: - prtd->sample_rate = 48000; + } + + case SND_AUDIOCODEC_WMA_PRO: { + pr_debug("SND_AUDIOCODEC_WMA_PRO\n"); + prtd->codec = FORMAT_WMA_V10PRO; break; } - pr_debug("%s: sample_rate %d\n", __func__, prtd->sample_rate); + case SND_AUDIOCODEC_FLAC: { + pr_debug("%s: SND_AUDIOCODEC_FLAC\n", __func__); + prtd->codec = FORMAT_FLAC; + break; + } - switch (params->codec.id) { - case SND_AUDIOCODEC_MP3: { - pr_debug("SND_AUDIOCODEC_MP3\n"); - prtd->codec = FORMAT_MP3; + case SND_AUDIOCODEC_VORBIS: { + pr_debug("%s: SND_AUDIOCODEC_VORBIS\n", __func__); + prtd->codec = FORMAT_VORBIS; break; } - case SND_AUDIOCODEC_AAC: { - pr_debug("SND_AUDIOCODEC_AAC\n"); - prtd->codec = FORMAT_MPEG4_AAC; + case SND_AUDIOCODEC_ALAC: { + pr_debug("%s: SND_AUDIOCODEC_ALAC\n", __func__); + prtd->codec = FORMAT_ALAC; + break; + } + + case SND_AUDIOCODEC_APE: { + pr_debug("%s: SND_AUDIOCODEC_APE\n", __func__); + prtd->codec = FORMAT_APE; break; } @@ -584,11 +1342,91 @@ static int msm_compr_set_params(struct snd_compr_stream *cstream, return -EINVAL; } + delay_time_ms = ((DSP_NUM_OUTPUT_FRAME_BUFFERED * frame_sz * 1000) / + prtd->sample_rate) + DSP_PP_BUFFERING_IN_MSEC; + delay_time_ms = delay_time_ms > PARTIAL_DRAIN_ACK_EARLY_BY_MSEC ? + delay_time_ms - PARTIAL_DRAIN_ACK_EARLY_BY_MSEC : 0; + prtd->partial_drain_delay = delay_time_ms; + + memcpy(&prtd->codec_param, params, sizeof(struct snd_compr_params)); + + /* ToDo: remove duplicates */ + prtd->num_channels = prtd->codec_param.codec.ch_in; + prtd->sample_rate = prtd->codec_param.codec.sample_rate; + pr_debug("%s: sample_rate %d\n", __func__, prtd->sample_rate); ret = msm_compr_configure_dsp(cstream); return ret; } +static int msm_compr_drain_buffer(struct msm_compr_audio *prtd, + unsigned long *flags) +{ + int rc = 0; + + atomic_set(&prtd->drain, 1); + prtd->drain_ready = 0; + spin_unlock_irqrestore(&prtd->lock, *flags); + pr_debug("%s: wait for buffer to be drained\n", __func__); + rc = wait_event_interruptible(prtd->drain_wait, + prtd->drain_ready || + prtd->cmd_interrupt || + atomic_read(&prtd->xrun) || + atomic_read(&prtd->error)); + pr_debug("%s: out of buffer drain wait with ret %d\n", __func__, rc); + spin_lock_irqsave(&prtd->lock, *flags); + if (prtd->cmd_interrupt) { + pr_debug("%s: buffer drain interrupted by flush)\n", __func__); + rc = -EINTR; + prtd->cmd_interrupt = 0; + } + if (atomic_read(&prtd->error)) { + pr_err("%s: Got RESET EVENTS notification, return\n", + __func__); + rc = -ENETRESET; + } + return rc; +} + +static int msm_compr_wait_for_stream_avail(struct msm_compr_audio *prtd, + unsigned long *flags) +{ + int rc = 0; + pr_debug("next session is already in opened state\n"); + prtd->next_stream = 1; + prtd->cmd_interrupt = 0; + spin_unlock_irqrestore(&prtd->lock, *flags); + /* + * Wait for stream to be available, or the wait to be interrupted by + * commands like flush or till a timeout of one second. + */ + rc = wait_event_timeout(prtd->wait_for_stream_avail, + prtd->stream_available || prtd->cmd_interrupt, 1 * HZ); + pr_err("%s:prtd->stream_available %d, prtd->cmd_interrupt %d rc %d\n", + __func__, prtd->stream_available, prtd->cmd_interrupt, rc); + + spin_lock_irqsave(&prtd->lock, *flags); + if (rc == 0) { + pr_err("%s: wait_for_stream_avail timed out\n", + __func__); + rc = -ETIMEDOUT; + } else if (prtd->cmd_interrupt == 1) { + /* + * This scenario might not happen as we do not allow + * flush in transition state. + */ + pr_debug("%s: wait_for_stream_avail interrupted\n", __func__); + prtd->cmd_interrupt = 0; + prtd->stream_available = 0; + rc = -EINTR; + } else { + prtd->stream_available = 0; + rc = 0; + } + pr_debug("%s : rc = %d", __func__, rc); + return rc; +} + static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) { struct snd_compr_runtime *runtime = cstream->runtime; @@ -597,99 +1435,142 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) struct msm_compr_pdata *pdata = snd_soc_platform_get_drvdata(rtd->platform); uint32_t *volume = pdata->volume[rtd->dai_link->be_id]; + struct audio_client *ac = prtd->audio_client; + unsigned long fe_id = rtd->dai_link->be_id; int rc = 0; int bytes_to_write; + unsigned long flags; + int stream_id; + uint32_t stream_index; + uint16_t bits_per_sample = 16; if (cstream->direction != SND_COMPRESS_PLAYBACK) { pr_err("%s: Unsupported stream type\n", __func__); return -EINVAL; } + spin_lock_irqsave(&prtd->lock, flags); + if (atomic_read(&prtd->error)) { + pr_err("%s Got RESET EVENTS notification, return immediately", + __func__); + spin_unlock_irqrestore(&prtd->lock, flags); + return 0; + } + spin_unlock_irqrestore(&prtd->lock, flags); + switch (cmd) { case SNDRV_PCM_TRIGGER_START: pr_debug("%s: SNDRV_PCM_TRIGGER_START\n", __func__); atomic_set(&prtd->start, 1); - q6asm_run_nowait(prtd->audio_client, 0, 0, 0); - msm_compr_set_volume(cstream, volume[0], volume[1]); + /* set volume for the stream before RUN */ + rc = msm_compr_set_volume(cstream, volume[0], volume[1]); if (rc) pr_err("%s : Set Volume failed : %d\n", __func__, rc); + + rc = msm_compr_init_pp_params(cstream, ac); + if (rc) + pr_err("%s : init PP params failed : %d\n", + __func__, rc); + + /* issue RUN command for the stream */ + q6asm_run_nowait(prtd->audio_client, 0, 0, 0); break; case SNDRV_PCM_TRIGGER_STOP: - pr_debug("%s: SNDRV_PCM_TRIGGER_STOP\n", __func__); - spin_lock_irq(&prtd->lock); - + spin_lock_irqsave(&prtd->lock, flags); + pr_debug("%s: SNDRV_PCM_TRIGGER_STOP transition %d\n", __func__, + prtd->gapless_state.gapless_transition); + stream_id = ac->stream_id; atomic_set(&prtd->start, 0); + if (prtd->next_stream) { + pr_debug("%s: interrupt next track wait queues\n", + __func__); + prtd->cmd_interrupt = 1; + wake_up(&prtd->wait_for_stream_avail); + prtd->next_stream = 0; + } if (atomic_read(&prtd->eos)) { - pr_debug("%s: interrupt drain and eos wait queues", - __func__); + pr_debug("%s: interrupt eos wait queues", __func__); prtd->cmd_interrupt = 1; - prtd->drain_ready = 1; - wake_up(&prtd->drain_wait); wake_up(&prtd->eos_wait); atomic_set(&prtd->eos, 0); } - - pr_debug("issue CMD_FLUSH\n"); + if (atomic_read(&prtd->drain)) { + pr_debug("%s: interrupt drain wait queues", __func__); + prtd->cmd_interrupt = 1; + prtd->drain_ready = 1; + wake_up(&prtd->drain_wait); + atomic_set(&prtd->drain, 0); + } + prtd->last_buffer = 0; prtd->cmd_ack = 0; - spin_unlock_irq(&prtd->lock); - rc = q6asm_cmd(prtd->audio_client, CMD_FLUSH); - if (rc < 0) { - pr_err("%s: flush cmd failed rc=%d\n", - __func__, rc); - return rc; + if (!prtd->gapless_state.gapless_transition) { + pr_debug("issue CMD_FLUSH stream_id %d\n", stream_id); + spin_unlock_irqrestore(&prtd->lock, flags); + q6asm_stream_cmd( + prtd->audio_client, CMD_FLUSH, stream_id); + spin_lock_irqsave(&prtd->lock, flags); + } else { + prtd->first_buffer = 0; } - rc = wait_event_timeout(prtd->flush_wait, - prtd->cmd_ack, 1 * HZ); - if (!rc) { - rc = -ETIMEDOUT; - pr_err("Flush cmd timeout\n"); - } else - rc = 0; /* prtd->cmd_status == OK? 0 : -EPERM */ - - spin_lock_irq(&prtd->lock); /* FIXME. only reset if flush was successful */ prtd->byte_offset = 0; prtd->copied_total = 0; prtd->app_pointer = 0; prtd->bytes_received = 0; + prtd->bytes_sent = 0; + prtd->marker_timestamp = 0; + atomic_set(&prtd->xrun, 0); - spin_unlock_irq(&prtd->lock); + spin_unlock_irqrestore(&prtd->lock, flags); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - pr_debug("SNDRV_PCM_TRIGGER_PAUSE_PUSH\n"); - q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE); - atomic_set(&prtd->start, 0); + pr_debug("SNDRV_PCM_TRIGGER_PAUSE_PUSH transition %d\n", + prtd->gapless_state.gapless_transition); + if (!prtd->gapless_state.gapless_transition) { + pr_debug("issue CMD_PAUSE stream_id %d\n", + ac->stream_id); + q6asm_stream_cmd_nowait(ac, CMD_PAUSE, ac->stream_id); + atomic_set(&prtd->start, 0); + } break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - pr_debug("SNDRV_PCM_TRIGGER_PAUSE_RELEASE\n"); - atomic_set(&prtd->start, 1); - q6asm_run_nowait(prtd->audio_client, 0, 0, 0); + pr_debug("SNDRV_PCM_TRIGGER_PAUSE_RELEASE transition %d\n", + prtd->gapless_state.gapless_transition); + if (!prtd->gapless_state.gapless_transition) { + atomic_set(&prtd->start, 1); + q6asm_run_nowait(prtd->audio_client, 0, 0, 0); + } break; case SND_COMPR_TRIGGER_PARTIAL_DRAIN: pr_debug("%s: SND_COMPR_TRIGGER_PARTIAL_DRAIN\n", __func__); + if (!prtd->gapless_state.use_dsp_gapless_mode) { + pr_debug("%s: set partial drain as drain\n", __func__); + cmd = SND_COMPR_TRIGGER_DRAIN; + } case SND_COMPR_TRIGGER_DRAIN: pr_debug("%s: SNDRV_COMPRESS_DRAIN\n", __func__); /* Make sure all the data is sent to DSP before sending EOS */ - spin_lock_irq(&prtd->lock); + spin_lock_irqsave(&prtd->lock, flags); if (!atomic_read(&prtd->start)) { pr_err("%s: stream is not in started state\n", __func__); rc = -EPERM; - spin_unlock_irq(&prtd->lock); + spin_unlock_irqrestore(&prtd->lock, flags); break; } - atomic_set(&prtd->eos, 1); - if (prtd->bytes_received > prtd->copied_total) { - atomic_set(&prtd->drain, 1); - prtd->drain_ready = 0; - spin_unlock_irq(&prtd->lock); pr_debug("%s: wait till all the data is sent to dsp\n", __func__); - + rc = msm_compr_drain_buffer(prtd, &flags); + if (rc || !atomic_read(&prtd->start)) { + if (rc != -ENETRESET) + rc = -EINTR; + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } /* * FIXME: Bug. * Write(32767) @@ -698,95 +1579,272 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) * sol1 : if (prtd->copied_total) then wait? * sol2 : (prtd->cmd_interrupt || prtd->drain_ready || atomic_read(xrun) */ - rc = wait_event_interruptible(prtd->drain_wait, - prtd->cmd_interrupt || prtd->drain_ready || - atomic_read(&prtd->xrun)); - - spin_lock_irq(&prtd->lock); - if (!prtd->cmd_interrupt) { - bytes_to_write = prtd->bytes_received - prtd->copied_total; - WARN(bytes_to_write > runtime->fragment_size, - "last write %d cannot be > than fragment_size", - bytes_to_write); - - if (bytes_to_write > 0) { - pr_debug("%s: send %d partial bytes at the end", - __func__, bytes_to_write); - atomic_set(&prtd->xrun, 0); - msm_compr_send_buffer(prtd); - } + bytes_to_write = prtd->bytes_received + - prtd->copied_total; + WARN(bytes_to_write > runtime->fragment_size, + "last write %d cannot be > than fragment_size", + bytes_to_write); + + if (bytes_to_write > 0) { + pr_debug("%s: send %d partial bytes at the end", + __func__, bytes_to_write); + atomic_set(&prtd->xrun, 0); + prtd->last_buffer = 1; + msm_compr_send_buffer(prtd); } } - if (!atomic_read(&prtd->start) || prtd->cmd_interrupt) { - pr_err("%s: stream is not started\n", __func__); - rc = -EINTR; - prtd->cmd_interrupt = 0; - spin_unlock_irq(&prtd->lock); - break; - } + if ((cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN) && + (prtd->gapless_state.set_next_stream_id)) { + /* wait for the last buffer to be returned */ + + if (prtd->last_buffer) { + pr_debug("%s: last buffer drain\n", __func__); + rc = msm_compr_drain_buffer(prtd, &flags); + if (rc) { + spin_unlock_irqrestore(&prtd->lock, + flags); + break; + } + } + /* send EOS */ + prtd->eos_ack = 0; + pr_debug("issue CMD_EOS stream_id %d\n", ac->stream_id); + q6asm_stream_cmd_nowait(ac, CMD_EOS, ac->stream_id); + pr_info("PARTIAL DRAIN, do not wait for EOS ack\n"); + + /* send a zero length buffer */ + atomic_set(&prtd->xrun, 0); + msm_compr_send_buffer(prtd); - pr_debug("%s: CMD_EOS\n", __func__); + /* wait for the zero length buffer to be returned */ + pr_debug("%s: zero length buffer drain\n", __func__); + rc = msm_compr_drain_buffer(prtd, &flags); + if (rc) { + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } - prtd->cmd_ack = 0; - q6asm_cmd_nowait(prtd->audio_client, CMD_EOS); + /* sleep for additional duration partial drain */ + atomic_set(&prtd->drain, 1); + prtd->drain_ready = 0; + pr_debug("%s, additional sleep: %d\n", __func__, + prtd->partial_drain_delay); + spin_unlock_irqrestore(&prtd->lock, flags); + rc = wait_event_timeout(prtd->drain_wait, + prtd->drain_ready || prtd->cmd_interrupt, + msecs_to_jiffies(prtd->partial_drain_delay)); + pr_debug("%s: out of additional wait for low sample rate\n", + __func__); + spin_lock_irqsave(&prtd->lock, flags); + if (prtd->cmd_interrupt) { + pr_debug("%s: additional wait interrupted by flush)\n", + __func__); + rc = -EINTR; + prtd->cmd_interrupt = 0; + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } - spin_unlock_irq(&prtd->lock); + /* move to next stream and reset vars */ + pr_debug("%s: Moving to next stream in gapless\n", + __func__); + ac->stream_id = NEXT_STREAM_ID(ac->stream_id); + prtd->byte_offset = 0; + prtd->app_pointer = 0; + prtd->first_buffer = 1; + prtd->last_buffer = 0; + prtd->gapless_state.gapless_transition = 1; + prtd->gapless_state.min_blk_size = 65537; + prtd->gapless_state.max_blk_size = 65537; + prtd->marker_timestamp = 0; -/* - if (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN) { - pr_err("PARTIAL DRAIN, do not wait for EOS ack"); + /* + Don't reset these as these vars map to + total_bytes_transferred and total_bytes_available + directly, only total_bytes_transferred will be updated + in the next avail() ioctl + prtd->copied_total = 0; + prtd->bytes_received = 0; + */ + atomic_set(&prtd->drain, 0); + atomic_set(&prtd->xrun, 1); + pr_debug("%s: issue CMD_RUN", __func__); + q6asm_run_nowait(prtd->audio_client, 0, 0, 0); + spin_unlock_irqrestore(&prtd->lock, flags); break; } -*/ + /* + moving to next stream failed, so reset the gapless state + set next stream id for the same session so that the same + stream can be used for gapless playback + */ + prtd->gapless_state.set_next_stream_id = false; + pr_debug("%s:CMD_EOS stream_id %d\n", __func__, ac->stream_id); + + prtd->eos_ack = 0; + atomic_set(&prtd->eos, 1); + q6asm_stream_cmd_nowait(ac, CMD_EOS, ac->stream_id); + + spin_unlock_irqrestore(&prtd->lock, flags); + /* Wait indefinitely for DRAIN. Flush can also signal this*/ rc = wait_event_interruptible(prtd->eos_wait, - (prtd->cmd_ack || prtd->cmd_interrupt)); + (prtd->eos_ack || + prtd->cmd_interrupt || + atomic_read(&prtd->error))); if (rc < 0) pr_err("%s: EOS wait failed\n", __func__); pr_debug("%s: SNDRV_COMPRESS_DRAIN out of wait for EOS\n", - __func__); + __func__); if (prtd->cmd_interrupt) rc = -EINTR; + if (atomic_read(&prtd->error)) { + pr_err("%s: Got RESET EVENTS notification, return\n", + __func__); + rc = -ENETRESET; + } + /*FIXME : what if a flush comes while PC is here */ - if (rc == 0 && (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN)) { - spin_lock_irq(&prtd->lock); - pr_debug("%s: issue CMD_PAUSE ", __func__); - q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE); + if (rc == 0) { + /* + * Failed to open second stream in DSP for gapless + * so prepare the current stream in session + * for gapless playback + */ + spin_lock_irqsave(&prtd->lock, flags); + pr_debug("%s:issue CMD_PAUSE stream_id %d", + __func__, ac->stream_id); + q6asm_stream_cmd_nowait(ac, CMD_PAUSE, ac->stream_id); prtd->cmd_ack = 0; - spin_unlock_irq(&prtd->lock); - pr_debug("%s: issue CMD_FLUSH", __func__); - q6asm_cmd(prtd->audio_client, CMD_FLUSH); - wait_event_timeout(prtd->flush_wait, - prtd->cmd_ack, 1 * HZ / 4); + spin_unlock_irqrestore(&prtd->lock, flags); - spin_lock_irq(&prtd->lock); + /* + * Cache this time as last known time + */ + q6asm_get_session_time(prtd->audio_client, + &prtd->marker_timestamp); + spin_lock_irqsave(&prtd->lock, flags); + /* + * Don't reset these as these vars map to + * total_bytes_transferred and total_bytes_available. + * Just total_bytes_transferred will be updated + * in the next avail() ioctl. + * prtd->copied_total = 0; + * prtd->bytes_received = 0; + * do not reset prtd->bytes_sent as well as the same + * session is used for gapless playback + */ prtd->byte_offset = 0; + prtd->app_pointer = 0; - /* Don't reset these as these vars map - to total_bytes_transferred and total_bytes_available directly, - only total_bytes_transferred will be updated in the next avail() - ioctl - prtd->copied_total = 0; - prtd->bytes_received = 0; - */ + prtd->first_buffer = 1; + prtd->last_buffer = 0; atomic_set(&prtd->drain, 0); atomic_set(&prtd->xrun, 1); - pr_debug("%s: issue CMD_RESUME", __func__); + spin_unlock_irqrestore(&prtd->lock, flags); + + pr_debug("%s:issue CMD_FLUSH ac->stream_id %d", + __func__, ac->stream_id); + q6asm_stream_cmd(ac, CMD_FLUSH, ac->stream_id); + q6asm_run_nowait(prtd->audio_client, 0, 0, 0); - spin_unlock_irq(&prtd->lock); } - pr_debug("%s: out of drain", __func__); - prtd->cmd_interrupt = 0; break; case SND_COMPR_TRIGGER_NEXT_TRACK: + if (!prtd->gapless_state.use_dsp_gapless_mode) { + pr_debug("%s: ignore trigger next track\n", __func__); + rc = 0; + break; + } pr_debug("%s: SND_COMPR_TRIGGER_NEXT_TRACK\n", __func__); + spin_lock_irqsave(&prtd->lock, flags); + rc = 0; + /* next stream in gapless */ + stream_id = NEXT_STREAM_ID(ac->stream_id); + /* + * Wait if stream 1 has not completed before honoring next + * track for stream 3. Scenario happens if second clip is + * small and fills in one buffer so next track will be + * called immediately. + */ + stream_index = STREAM_ARRAY_INDEX(stream_id); + if (stream_index >= MAX_NUMBER_OF_STREAMS || + stream_index < 0) { + pr_err("%s: Invalid stream index: %d", __func__, + stream_index); + spin_unlock_irqrestore(&prtd->lock, flags); + rc = -EINVAL; + break; + } + + if (prtd->gapless_state.stream_opened[stream_index]) { + if (prtd->gapless_state.gapless_transition) { + rc = msm_compr_wait_for_stream_avail(prtd, + &flags); + } else { + /* + * If session is already opened break out if + * the state is not gapless transition. This + * is when seek happens after the last buffer + * is sent to the driver. Next track would be + * called again after last buffer is sent. + */ + pr_debug("next session is in opened state\n"); + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } + } + spin_unlock_irqrestore(&prtd->lock, flags); + if (rc < 0) { + /* + * if return type EINTR then reset to zero. Tiny + * compress treats EINTR as error and prevents PARTIAL + * DRAIN. EINTR is not an error. wait for stream avail + * is interrupted by some other command like FLUSH. + */ + if (rc == -EINTR) { + pr_debug("%s: EINTR reset rc to 0\n", __func__); + rc = 0; + } + break; + } + + if (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S24_LE) + bits_per_sample = 24; + else if (prtd->codec_param.codec.format == + SNDRV_PCM_FORMAT_S32_LE) + bits_per_sample = 32; + + pr_debug("%s: open_write stream_id %d bits_per_sample %d", + __func__, stream_id, bits_per_sample); + rc = q6asm_stream_open_write_v2(prtd->audio_client, + prtd->codec, bits_per_sample, + stream_id, + prtd->gapless_state.use_dsp_gapless_mode); + if (rc < 0) { + pr_err("%s: Session out open failed for gapless\n", + __func__); + break; + } + rc = msm_compr_send_media_format_block(cstream, stream_id); + if (rc < 0) { + pr_err("%s, failed to send media format block\n", + __func__); + break; + } + msm_compr_send_dec_params(cstream, pdata->dec_params[fe_id], + stream_id); + spin_lock_irqsave(&prtd->lock, flags); + prtd->gapless_state.stream_opened[stream_index] = 1; + prtd->gapless_state.set_next_stream_id = true; + spin_unlock_irqrestore(&prtd->lock, flags); break; } @@ -800,28 +1858,50 @@ static int msm_compr_pointer(struct snd_compr_stream *cstream, struct msm_compr_audio *prtd = runtime->private_data; struct snd_compr_tstamp tstamp; uint64_t timestamp = 0; - int rc = 0; + int rc = 0, first_buffer; + unsigned long flags; + uint32_t gapless_transition; pr_debug("%s\n", __func__); memset(&tstamp, 0x0, sizeof(struct snd_compr_tstamp)); - spin_lock_irq(&prtd->lock); + spin_lock_irqsave(&prtd->lock, flags); tstamp.sampling_rate = prtd->sample_rate; tstamp.byte_offset = prtd->byte_offset; tstamp.copied_total = prtd->copied_total; - spin_unlock_irq(&prtd->lock); + first_buffer = prtd->first_buffer; + if (atomic_read(&prtd->error)) { + pr_err("%s Got RESET EVENTS notification, return error", + __func__); + tstamp.pcm_io_frames = 0; + memcpy(arg, &tstamp, sizeof(struct snd_compr_tstamp)); + spin_unlock_irqrestore(&prtd->lock, flags); + return -ENETRESET; + } + + gapless_transition = prtd->gapless_state.gapless_transition; + spin_unlock_irqrestore(&prtd->lock, flags); /* Query timestamp from DSP if some data is with it. This prevents timeouts. */ - if (prtd->copied_total) { + if (!first_buffer || gapless_transition) { + if (gapless_transition) + pr_debug("%s session time in gapless transition", + __func__); + rc = q6asm_get_session_time(prtd->audio_client, ×tamp); if (rc < 0) { pr_err("%s: Get Session Time return value =%lld\n", __func__, timestamp); - return -EAGAIN; + if (atomic_read(&prtd->error)) + return -ENETRESET; + else + return -EAGAIN; } + } else { + timestamp = prtd->marker_timestamp; } /* DSP returns timestamp in usec */ @@ -840,11 +1920,12 @@ static int msm_compr_ack(struct snd_compr_stream *cstream, struct msm_compr_audio *prtd = runtime->private_data; void *src, *dstn; size_t copy; + unsigned long flags; WARN(1, "This path is untested"); return -EINVAL; - pr_debug("%s: count = %d\n", __func__, count); + pr_debug("%s: count = %zd\n", __func__, count); if (!prtd->buffer) { pr_err("%s: Buffer is not allocated yet ??\n", __func__); return -EINVAL; @@ -866,7 +1947,7 @@ static int msm_compr_ack(struct snd_compr_stream *cstream, * copied to DSP, the newly received bytes should be * sent right away */ - spin_lock_irq(&prtd->lock); + spin_lock_irqsave(&prtd->lock, flags); if (atomic_read(&prtd->start) && prtd->bytes_received == prtd->copied_total) { @@ -875,7 +1956,7 @@ static int msm_compr_ack(struct snd_compr_stream *cstream, } else prtd->bytes_received += count; - spin_unlock_irq(&prtd->lock); + spin_unlock_irqrestore(&prtd->lock, flags); return 0; } @@ -888,13 +1969,22 @@ static int msm_compr_copy(struct snd_compr_stream *cstream, void *dstn; size_t copy; size_t bytes_available = 0; + unsigned long flags; - pr_debug("%s: count = %d\n", __func__, count); + pr_debug("%s: count = %zd\n", __func__, count); if (!prtd->buffer) { pr_err("%s: Buffer is not allocated yet ??", __func__); return 0; } + spin_lock_irqsave(&prtd->lock, flags); + if (atomic_read(&prtd->error)) { + pr_err("%s Got RESET EVENTS notification", __func__); + spin_unlock_irqrestore(&prtd->lock, flags); + return -ENETRESET; + } + spin_unlock_irqrestore(&prtd->lock, flags); + dstn = prtd->buffer + prtd->app_pointer; if (count < prtd->buffer_size - prtd->app_pointer) { if (copy_from_user(dstn, buf, count)) @@ -913,15 +2003,14 @@ static int msm_compr_copy(struct snd_compr_stream *cstream, * If stream is started and there has been an xrun, * since the available bytes fits fragment_size, copy the data right away */ - spin_lock_irq(&prtd->lock); - + spin_lock_irqsave(&prtd->lock, flags); prtd->bytes_received += count; if (atomic_read(&prtd->start)) { if (atomic_read(&prtd->xrun)) { - pr_debug("%s: in xrun, count = %d\n", __func__, count); + pr_debug("%s: in xrun, count = %zd\n", __func__, count); bytes_available = prtd->bytes_received - prtd->copied_total; if (bytes_available >= runtime->fragment_size) { - pr_debug("%s: handle xrun, bytes_to_write = %d\n", + pr_debug("%s: handle xrun, bytes_to_write = %zd\n", __func__, bytes_available); atomic_set(&prtd->xrun, 0); @@ -930,7 +2019,7 @@ static int msm_compr_copy(struct snd_compr_stream *cstream, } /* writes will continue on the next write_done */ } - spin_unlock_irq(&prtd->lock); + spin_unlock_irqrestore(&prtd->lock, flags); return count; } @@ -940,11 +2029,17 @@ static int msm_compr_get_caps(struct snd_compr_stream *cstream, { struct snd_compr_runtime *runtime = cstream->runtime; struct msm_compr_audio *prtd = runtime->private_data; + int ret = 0; pr_debug("%s\n", __func__); - memcpy(arg, &prtd->compr_cap, sizeof(struct snd_compr_caps)); + if ((arg != NULL) && (prtd != NULL)) { + memcpy(arg, &prtd->compr_cap, sizeof(struct snd_compr_caps)); + } else { + ret = -EINVAL; + pr_err("%s: arg (0x%p), prtd (0x%p)\n", __func__, arg, prtd); + } - return 0; + return ret; } static int msm_compr_get_codec_caps(struct snd_compr_stream *cstream, @@ -956,7 +2051,11 @@ static int msm_compr_get_codec_caps(struct snd_compr_stream *cstream, case SND_AUDIOCODEC_MP3: codec->num_descriptors = 2; codec->descriptor[0].max_ch = 2; - codec->descriptor[0].sample_rates = SNDRV_PCM_RATE_8000_48000; + memcpy(codec->descriptor[0].sample_rates, + supported_sample_rates, + sizeof(supported_sample_rates)); + codec->descriptor[0].num_sample_rates = + sizeof(supported_sample_rates)/sizeof(unsigned int); codec->descriptor[0].bit_rate[0] = 320; /* 320kbps */ codec->descriptor[0].bit_rate[1] = 128; codec->descriptor[0].num_bitrates = 2; @@ -967,7 +2066,11 @@ static int msm_compr_get_codec_caps(struct snd_compr_stream *cstream, case SND_AUDIOCODEC_AAC: codec->num_descriptors = 2; codec->descriptor[1].max_ch = 2; - codec->descriptor[1].sample_rates = SNDRV_PCM_RATE_8000_48000; + memcpy(codec->descriptor[1].sample_rates, + supported_sample_rates, + sizeof(supported_sample_rates)); + codec->descriptor[1].num_sample_rates = + sizeof(supported_sample_rates)/sizeof(unsigned int); codec->descriptor[1].bit_rate[0] = 320; /* 320kbps */ codec->descriptor[1].bit_rate[1] = 128; codec->descriptor[1].num_bitrates = 2; @@ -977,6 +2080,18 @@ static int msm_compr_get_codec_caps(struct snd_compr_stream *cstream, (SND_AUDIOSTREAMFORMAT_MP4ADTS | SND_AUDIOSTREAMFORMAT_RAW); break; + case SND_AUDIOCODEC_AC3: + break; + case SND_AUDIOCODEC_EAC3: + break; + case SND_AUDIOCODEC_FLAC: + break; + case SND_AUDIOCODEC_VORBIS: + break; + case SND_AUDIOCODEC_ALAC: + break; + case SND_AUDIOCODEC_APE: + break; default: pr_err("%s: Unsupported audio codec %d\n", __func__, codec->codec); @@ -989,65 +2104,596 @@ static int msm_compr_get_codec_caps(struct snd_compr_stream *cstream, static int msm_compr_set_metadata(struct snd_compr_stream *cstream, struct snd_compr_metadata *metadata) { + struct msm_compr_audio *prtd; + struct audio_client *ac; + struct asm_flac_cfg flac_cfg; + int ret = 0; pr_debug("%s\n", __func__); if (!metadata || !cstream) return -EINVAL; + prtd = cstream->runtime->private_data; + if (!prtd || !prtd->audio_client) { + pr_err("%s: prtd or audio client is NULL\n", __func__); + return -EINVAL; + } + + if (prtd->compr_passthr != LEGACY_PCM) { + pr_debug("%s: No trailing silence for compress_type[%d]\n", + __func__, prtd->compr_passthr); + return 0; + } + ac = prtd->audio_client; if (metadata->key == SNDRV_COMPRESS_ENCODER_PADDING) { pr_debug("%s, got encoder padding %u", __func__, metadata->value[0]); + prtd->gapless_state.trailing_samples_drop = metadata->value[0]; } else if (metadata->key == SNDRV_COMPRESS_ENCODER_DELAY) { pr_debug("%s, got encoder delay %u", __func__, metadata->value[0]); + prtd->gapless_state.initial_samples_drop = metadata->value[0]; + } else if (metadata->key == SNDRV_COMPRESS_MIN_BLK_SIZE) { + pr_debug("%s, got min_blk_size %u", + __func__, metadata->value[0]); + prtd->gapless_state.min_blk_size = metadata->value[0]; + } else if (metadata->key == SNDRV_COMPRESS_MAX_BLK_SIZE) { + pr_debug("%s, got max_blk_size %u", + __func__, metadata->value[0]); + prtd->gapless_state.max_blk_size = metadata->value[0]; } + if ((prtd->codec == FORMAT_FLAC) && + (prtd->gapless_state.min_blk_size >= 0) && + (prtd->gapless_state.min_blk_size <= FLAC_BLK_SIZE_LIMIT) && + (prtd->gapless_state.max_blk_size >= 0) && + (prtd->gapless_state.max_blk_size <= FLAC_BLK_SIZE_LIMIT)) { + pr_debug("%s: SND_AUDIOCODEC_FLAC\n", __func__); + memset(&flac_cfg, 0x0, sizeof(struct asm_flac_cfg)); + flac_cfg.ch_cfg = prtd->num_channels; + flac_cfg.sample_rate = prtd->sample_rate; + flac_cfg.stream_info_present = 1; + flac_cfg.sample_size = + prtd->codec_param.codec.options.flac_dec.sample_size; + flac_cfg.min_blk_size = + prtd->gapless_state.min_blk_size; + flac_cfg.max_blk_size = + prtd->gapless_state.max_blk_size; + flac_cfg.max_frame_size = + prtd->codec_param.codec.options.flac_dec.max_frame_size; + flac_cfg.min_frame_size = + prtd->codec_param.codec.options.flac_dec.min_frame_size; + pr_debug("%s: min_blk_size %d max_blk_size %d\n", + __func__, flac_cfg.min_blk_size, flac_cfg.max_blk_size); + + ret = q6asm_stream_media_format_block_flac(ac, &flac_cfg, + ac->stream_id); + if (ret < 0) + pr_err("%s: CMD Format block failed ret %d\n", + __func__, ret); + } return 0; } static int msm_compr_volume_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; + unsigned long fe_id = kcontrol->private_value; struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) snd_soc_platform_get_drvdata(platform); - struct snd_compr_stream *cstream = pdata->cstream[mc->reg]; - uint32_t *volume = pdata->volume[mc->reg]; + struct snd_compr_stream *cstream = NULL; + uint32_t *volume = NULL; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %lu\n", + __func__, fe_id); + return -EINVAL; + } + + cstream = pdata->cstream[fe_id]; + volume = pdata->volume[fe_id]; volume[0] = ucontrol->value.integer.value[0]; volume[1] = ucontrol->value.integer.value[1]; - pr_debug("%s: mc->reg %d left_vol %d right_vol %d\n", - __func__, mc->reg, volume[0], volume[1]); + pr_debug("%s: fe_id %lu left_vol %d right_vol %d\n", + __func__, fe_id, volume[0], volume[1]); if (cstream) msm_compr_set_volume(cstream, volume[0], volume[1]); return 0; } static int msm_compr_volume_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = snd_soc_platform_get_drvdata(platform); - uint32_t *volume = pdata->volume[mc->reg]; - pr_debug("%s: mc->reg %d\n", __func__, mc->reg); + uint32_t *volume = NULL; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bound fe_id %lu\n", __func__, fe_id); + return -EINVAL; + } + + volume = pdata->volume[fe_id]; + pr_debug("%s: fe_id %lu\n", __func__, fe_id); ucontrol->value.integer.value[0] = volume[0]; ucontrol->value.integer.value[1] = volume[1]; return 0; } -/* System Pin has no volume control */ -static const struct snd_kcontrol_new msm_compr_volume_controls[] = { - SOC_DOUBLE_EXT_TLV("Compress Playback Volume", - MSM_FRONTEND_DAI_MULTIMEDIA4, - 0, 8, COMPRESSED_LR_VOL_MAX_STEPS, 0, - msm_compr_volume_get, - msm_compr_volume_put, - msm_compr_vol_gain), -}; +static int msm_compr_audio_effects_config_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_platform_get_drvdata(platform); + struct msm_compr_audio_effects *audio_effects = NULL; + struct snd_compr_stream *cstream = NULL; + struct msm_compr_audio *prtd = NULL; + long *values = &(ucontrol->value.integer.value[0]); + int effects_module; + + pr_debug("%s\n", __func__); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %lu\n", + __func__, fe_id); + return -EINVAL; + } + cstream = pdata->cstream[fe_id]; + audio_effects = pdata->audio_effects[fe_id]; + if (!cstream || !audio_effects) { + pr_err("%s: stream or effects inactive\n", __func__); + return -EINVAL; + } + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: cannot set audio effects\n", __func__); + return -EINVAL; + } + if (prtd->compr_passthr != LEGACY_PCM) { + pr_debug("%s: No effects for compr_type[%d]\n", + __func__, prtd->compr_passthr); + return 0; + } else { + pr_debug("%s: Effects supported for compr_type[%d]\n", + __func__, prtd->compr_passthr); + } + effects_module = *values++; + switch (effects_module) { + case VIRTUALIZER_MODULE: + pr_debug("%s: VIRTUALIZER_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top(effects_module, + prtd->audio_client->topology)) + msm_audio_effects_virtualizer_handler( + prtd->audio_client, + &(audio_effects->virtualizer), + values); + break; + case REVERB_MODULE: + pr_debug("%s: REVERB_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top(effects_module, + prtd->audio_client->topology)) + msm_audio_effects_reverb_handler(prtd->audio_client, + &(audio_effects->reverb), + values); + break; + case BASS_BOOST_MODULE: + pr_debug("%s: BASS_BOOST_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top(effects_module, + prtd->audio_client->topology)) + msm_audio_effects_bass_boost_handler(prtd->audio_client, + &(audio_effects->bass_boost), + values); + break; + case PBE_MODULE: + pr_debug("%s: PBE_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top(effects_module, + prtd->audio_client->topology)) + msm_audio_effects_pbe_handler(prtd->audio_client, + &(audio_effects->pbe), + values); + break; + case EQ_MODULE: + pr_debug("%s: EQ_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top(effects_module, + prtd->audio_client->topology)) + msm_audio_effects_popless_eq_handler(prtd->audio_client, + &(audio_effects->equalizer), + values); + break; + case DTS_EAGLE_MODULE: + pr_debug("%s: DTS_EAGLE_MODULE\n", __func__); + if (!msm_audio_effects_is_effmodule_supp_in_top(effects_module, + prtd->audio_client->topology)) + return 0; + msm_dts_eagle_handle_asm(NULL, (void *)values, true, + false, prtd->audio_client, NULL); + break; + case DTS_EAGLE_MODULE_ENABLE: + pr_debug("%s: DTS_EAGLE_MODULE_ENABLE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top(effects_module, + prtd->audio_client->topology)) + msm_dts_eagle_enable_asm(prtd->audio_client, + (bool)values[0], + AUDPROC_MODULE_ID_DTS_HPX_PREMIX); + + break; + case SOFT_VOLUME_MODULE: + pr_debug("%s: SOFT_VOLUME_MODULE\n", __func__); + break; + case SOFT_VOLUME2_MODULE: + pr_debug("%s: SOFT_VOLUME2_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top(effects_module, + prtd->audio_client->topology)) + msm_audio_effects_volume_handler_v2(prtd->audio_client, + &(audio_effects->volume), + values, SOFT_VOLUME_INSTANCE_2); + break; + default: + pr_err("%s Invalid effects config module\n", __func__); + return -EINVAL; + } + return 0; +} + +static int msm_compr_audio_effects_config_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_platform_get_drvdata(platform); + struct msm_compr_audio_effects *audio_effects = NULL; + struct snd_compr_stream *cstream = NULL; + struct msm_compr_audio *prtd = NULL; + long *values = &(ucontrol->value.integer.value[0]); + + pr_debug("%s\n", __func__); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %lu\n", + __func__, fe_id); + return -EINVAL; + } + cstream = pdata->cstream[fe_id]; + audio_effects = pdata->audio_effects[fe_id]; + if (!cstream || !audio_effects) { + pr_err("%s: stream or effects inactive\n", __func__); + return -EINVAL; + } + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: cannot set audio effects\n", __func__); + return -EINVAL; + } + + switch (audio_effects->query.mod_id) { + case DTS_EAGLE_MODULE: + pr_debug("%s: DTS_EAGLE_MODULE handling queued get\n", + __func__); + values[0] = (long)audio_effects->query.mod_id; + values[1] = (long)audio_effects->query.parm_id; + values[2] = (long)audio_effects->query.size; + values[3] = (long)audio_effects->query.offset; + values[4] = (long)audio_effects->query.device; + if (values[2] > DTS_EAGLE_MAX_PARAM_SIZE_FOR_ALSA) { + pr_err("%s: DTS_EAGLE_MODULE parameter's requested size (%li) too large (max size is %i)\n", + __func__, values[2], + DTS_EAGLE_MAX_PARAM_SIZE_FOR_ALSA); + return -EINVAL; + } + msm_dts_eagle_handle_asm(NULL, (void *)&values[1], + true, true, prtd->audio_client, NULL); + break; + default: + pr_err("%s: Invalid effects config module\n", __func__); + return -EINVAL; + } + return 0; +} + +static int msm_compr_query_audio_effect_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_platform_get_drvdata(platform); + struct msm_compr_audio_effects *audio_effects = NULL; + struct snd_compr_stream *cstream = NULL; + struct msm_compr_audio *prtd = NULL; + long *values = &(ucontrol->value.integer.value[0]); + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %lu\n", + __func__, fe_id); + return -EINVAL; + } + cstream = pdata->cstream[fe_id]; + audio_effects = pdata->audio_effects[fe_id]; + if (!cstream || !audio_effects) { + pr_err("%s: stream or effects inactive\n", __func__); + return -EINVAL; + } + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: cannot set audio effects\n", __func__); + return -EINVAL; + } + if (prtd->compr_passthr != LEGACY_PCM) { + pr_err("%s: No effects for compr_type[%d]\n", + __func__, prtd->compr_passthr); + return -EPERM; + } + audio_effects->query.mod_id = (u32)*values++; + audio_effects->query.parm_id = (u32)*values++; + audio_effects->query.size = (u32)*values++; + audio_effects->query.offset = (u32)*values++; + audio_effects->query.device = (u32)*values++; + return 0; +} + +static int msm_compr_query_audio_effect_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_platform_get_drvdata(platform); + struct msm_compr_audio_effects *audio_effects = NULL; + struct snd_compr_stream *cstream = NULL; + struct msm_compr_audio *prtd = NULL; + long *values = &(ucontrol->value.integer.value[0]); + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %lu\n", + __func__, fe_id); + return -EINVAL; + } + cstream = pdata->cstream[fe_id]; + audio_effects = pdata->audio_effects[fe_id]; + if (!cstream || !audio_effects) { + pr_debug("%s: stream or effects inactive\n", __func__); + return -EINVAL; + } + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: cannot set audio effects\n", __func__); + return -EINVAL; + } + values[0] = (long)audio_effects->query.mod_id; + values[1] = (long)audio_effects->query.parm_id; + values[2] = (long)audio_effects->query.size; + values[3] = (long)audio_effects->query.offset; + values[4] = (long)audio_effects->query.device; + return 0; +} + +static int msm_compr_send_dec_params(struct snd_compr_stream *cstream, + struct msm_compr_dec_params *dec_params, + int stream_id) +{ + + int rc = 0; + struct msm_compr_audio *prtd = NULL; + struct snd_dec_ddp *ddp = &dec_params->ddp_params; + + if (!cstream || !dec_params) { + pr_err("%s: stream or dec_params inactive\n", __func__); + rc = -EINVAL; + goto end; + } + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: cannot set dec_params\n", __func__); + rc = -EINVAL; + goto end; + } + switch (prtd->codec) { + case FORMAT_MP3: + case FORMAT_MPEG4_AAC: + pr_debug("%s: no runtime parameters for codec: %d\n", __func__, + prtd->codec); + break; + case FORMAT_AC3: + case FORMAT_EAC3: + if (prtd->compr_passthr != LEGACY_PCM) { + pr_debug("%s: No DDP param for compr_type[%d]\n", + __func__, prtd->compr_passthr); + break; + } + rc = msm_compr_send_ddp_cfg(prtd->audio_client, ddp, stream_id); + if (rc < 0) + pr_err("%s: DDP CMD CFG failed %d\n", __func__, rc); + break; + default: + break; + } +end: + return rc; + +} +static int msm_compr_dec_params_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_platform_get_drvdata(platform); + struct msm_compr_dec_params *dec_params = NULL; + struct snd_compr_stream *cstream = NULL; + struct msm_compr_audio *prtd = NULL; + long *values = &(ucontrol->value.integer.value[0]); + int rc = 0; + + pr_debug("%s\n", __func__); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %lu\n", + __func__, fe_id); + rc = -EINVAL; + goto end; + } + + cstream = pdata->cstream[fe_id]; + dec_params = pdata->dec_params[fe_id]; + + if (!cstream || !dec_params) { + pr_err("%s: stream or dec_params inactive\n", __func__); + rc = -EINVAL; + goto end; + } + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: cannot set dec_params\n", __func__); + rc = -EINVAL; + goto end; + } + + switch (prtd->codec) { + case FORMAT_MP3: + case FORMAT_MPEG4_AAC: + case FORMAT_FLAC: + case FORMAT_VORBIS: + case FORMAT_ALAC: + case FORMAT_APE: + pr_debug("%s: no runtime parameters for codec: %d\n", __func__, + prtd->codec); + break; + case FORMAT_AC3: + case FORMAT_EAC3: { + struct snd_dec_ddp *ddp = &dec_params->ddp_params; + int cnt; + if (prtd->compr_passthr != LEGACY_PCM) { + pr_debug("%s: No DDP param for compr_type[%d]\n", + __func__, prtd->compr_passthr); + break; + } + + ddp->params_length = (*values++); + if (ddp->params_length > DDP_DEC_MAX_NUM_PARAM) { + pr_err("%s: invalid num of params:: %d\n", __func__, + ddp->params_length); + rc = -EINVAL; + goto end; + } + for (cnt = 0; cnt < ddp->params_length; cnt++) { + ddp->params_id[cnt] = *values++; + ddp->params_value[cnt] = *values++; + } + prtd = cstream->runtime->private_data; + if (prtd && prtd->audio_client) + rc = msm_compr_send_dec_params(cstream, dec_params, + prtd->audio_client->stream_id); + break; + } + default: + break; + } +end: + pr_debug("%s: ret %d\n", __func__, rc); + return rc; +} + +static int msm_compr_dec_params_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* dummy function */ + return 0; +} + +static int msm_compr_app_type_cfg_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int app_type; + int acdb_dev_id; + int sample_rate = 48000; + + pr_debug("%s: fe_id- %llu\n", __func__, fe_id); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %llu\n", + __func__, fe_id); + return -EINVAL; + } + + app_type = ucontrol->value.integer.value[0]; + acdb_dev_id = ucontrol->value.integer.value[1]; + if (0 != ucontrol->value.integer.value[2]) + sample_rate = ucontrol->value.integer.value[2]; + pr_debug("%s: app_type- %d acdb_dev_id- %d sample_rate- %d\n", + __func__, app_type, acdb_dev_id, sample_rate); + msm_pcm_routing_reg_stream_app_type_cfg(fe_id, app_type, + acdb_dev_id, sample_rate); + + return 0; +} + +static int msm_compr_app_type_cfg_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return 0; +} + +static int msm_compr_channel_map_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + u64 fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_platform_get_drvdata(platform); + int rc = 0, i; + + pr_debug("%s: fe_id- %llu\n", __func__, fe_id); + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %llu\n", + __func__, fe_id); + rc = -EINVAL; + goto end; + } + + if (pdata->ch_map[fe_id]) { + pdata->ch_map[fe_id]->set_ch_map = true; + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++) + pdata->ch_map[fe_id]->channel_map[i] = + (char)(ucontrol->value.integer.value[i]); + } else { + pr_debug("%s: no memory for ch_map, default will be set\n", + __func__); + } +end: + pr_debug("%s: ret %d\n", __func__, rc); + return rc; +} + +static int msm_compr_channel_map_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + u64 fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_platform_get_drvdata(platform); + int rc = 0, i; + + pr_debug("%s: fe_id- %llu\n", __func__, fe_id); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s: Received out of bounds fe_id %llu\n", + __func__, fe_id); + rc = -EINVAL; + goto end; + } + if (pdata->ch_map[fe_id]) { + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++) + ucontrol->value.integer.value[i] = + pdata->ch_map[fe_id]->channel_map[i]; + } +end: + pr_debug("%s: ret %d\n", __func__, rc); + return rc; +} static int msm_compr_probe(struct snd_soc_platform *platform) { @@ -1067,9 +2713,439 @@ static int msm_compr_probe(struct snd_soc_platform *platform) for (i = 0; i < MSM_FRONTEND_DAI_MAX; i++) { pdata->volume[i][0] = COMPRESSED_LR_VOL_MAX_STEPS; pdata->volume[i][1] = COMPRESSED_LR_VOL_MAX_STEPS; + pdata->audio_effects[i] = NULL; + pdata->dec_params[i] = NULL; pdata->cstream[i] = NULL; + pdata->ch_map[i] = NULL; + } + + /* + * use_dsp_gapless_mode part of platform data(pdata) is updated from HAL + * through a mixer control before compress driver is opened. The mixer + * control is used to decide if dsp gapless mode needs to be enabled. + * Gapless is disabled by default. + */ + pdata->use_dsp_gapless_mode = false; + return 0; +} + +static int msm_compr_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = COMPRESSED_LR_VOL_MAX_STEPS; + return 0; +} + +static int msm_compr_audio_effects_config_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 128; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xFFFFFFFF; + return 0; +} + +static int msm_compr_query_audio_effect_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 128; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xFFFFFFFF; + return 0; +} + +static int msm_compr_dec_params_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 128; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xFFFFFFFF; + return 0; +} + +static int msm_compr_app_type_cfg_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 5; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xFFFFFFFF; + return 0; +} + +static int msm_compr_channel_map_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 8; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xFFFFFFFF; + return 0; +} + +static int msm_compr_add_volume_control(struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Compress Playback"; + const char *deviceNo = "NN"; + const char *suffix = "Volume"; + char *mixer_str = NULL; + int ctl_len; + struct snd_kcontrol_new fe_volume_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_compr_volume_info, + .tlv.p = msm_compr_vol_gain, + .get = msm_compr_volume_get, + .put = msm_compr_volume_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + return 0; + } + pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n", + __func__, rtd->dai_link->name, rtd->dai_link->be_id, + rtd->dai_link->cpu_dai_name, rtd->pcm->device); + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1 + + strlen(suffix) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + pr_err("failed to allocate mixer ctrl str of len %d", ctl_len); + return 0; + } + snprintf(mixer_str, ctl_len, "%s %d %s", mixer_ctl_name, + rtd->pcm->device, suffix); + fe_volume_control[0].name = mixer_str; + fe_volume_control[0].private_value = rtd->dai_link->be_id; + pr_debug("Registering new mixer ctl %s", mixer_str); + snd_soc_add_platform_controls(rtd->platform, fe_volume_control, + ARRAY_SIZE(fe_volume_control)); + kfree(mixer_str); + return 0; +} + +static int msm_compr_add_audio_effects_control(struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Audio Effects Config"; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len; + struct snd_kcontrol_new fe_audio_effects_config_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_compr_audio_effects_config_info, + .get = msm_compr_audio_effects_config_get, + .put = msm_compr_audio_effects_config_put, + .private_value = 0, + } + }; + + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + return 0; + } + + pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n", + __func__, rtd->dai_link->name, rtd->dai_link->be_id, + rtd->dai_link->cpu_dai_name, rtd->pcm->device); + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + + if (!mixer_str) { + pr_err("failed to allocate mixer ctrl str of len %d", ctl_len); + return 0; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + + fe_audio_effects_config_control[0].name = mixer_str; + fe_audio_effects_config_control[0].private_value = rtd->dai_link->be_id; + pr_debug("Registering new mixer ctl %s\n", mixer_str); + snd_soc_add_platform_controls(rtd->platform, + fe_audio_effects_config_control, + ARRAY_SIZE(fe_audio_effects_config_control)); + kfree(mixer_str); + return 0; +} + +static int msm_compr_add_query_audio_effect_control( + struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Query Audio Effect Param"; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len; + struct snd_kcontrol_new fe_query_audio_effect_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_compr_query_audio_effect_info, + .get = msm_compr_query_audio_effect_get, + .put = msm_compr_query_audio_effect_put, + .private_value = 0, + } + }; + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + return 0; + } + pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n", + __func__, rtd->dai_link->name, rtd->dai_link->be_id, + rtd->dai_link->cpu_dai_name, rtd->pcm->device); + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + pr_err("failed to allocate mixer ctrl str of len %d", ctl_len); + return 0; + } + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_query_audio_effect_control[0].name = mixer_str; + fe_query_audio_effect_control[0].private_value = rtd->dai_link->be_id; + pr_debug("%s: registering new mixer ctl %s\n", __func__, mixer_str); + snd_soc_add_platform_controls(rtd->platform, + fe_query_audio_effect_control, + ARRAY_SIZE(fe_query_audio_effect_control)); + kfree(mixer_str); + return 0; +} + +static int msm_compr_gapless_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_platform_get_drvdata(platform); + pdata->use_dsp_gapless_mode = ucontrol->value.integer.value[0]; + pr_debug("%s: value: %ld\n", __func__, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm_compr_gapless_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + struct msm_compr_pdata *pdata = + snd_soc_platform_get_drvdata(platform); + pr_debug("%s:gapless mode %d\n", __func__, pdata->use_dsp_gapless_mode); + ucontrol->value.integer.value[0] = pdata->use_dsp_gapless_mode; + + return 0; +} + +static const struct snd_kcontrol_new msm_compr_gapless_controls[] = { + SOC_SINGLE_EXT("Compress Gapless Playback", + 0, 0, 1, 0, + msm_compr_gapless_get, + msm_compr_gapless_put), +}; + +static int msm_compr_add_dec_runtime_params_control( + struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Audio Stream"; + const char *deviceNo = "NN"; + const char *suffix = "Dec Params"; + char *mixer_str = NULL; + int ctl_len; + struct snd_kcontrol_new fe_dec_params_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_compr_dec_params_info, + .get = msm_compr_dec_params_get, + .put = msm_compr_dec_params_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + return 0; + } + + pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n", + __func__, rtd->dai_link->name, rtd->dai_link->be_id, + rtd->dai_link->cpu_dai_name, rtd->pcm->device); + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1 + + strlen(suffix) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + + if (!mixer_str) { + pr_err("failed to allocate mixer ctrl str of len %d", ctl_len); + return 0; + } + + snprintf(mixer_str, ctl_len, "%s %d %s", mixer_ctl_name, + rtd->pcm->device, suffix); + + fe_dec_params_control[0].name = mixer_str; + fe_dec_params_control[0].private_value = rtd->dai_link->be_id; + pr_debug("Registering new mixer ctl %s", mixer_str); + snd_soc_add_platform_controls(rtd->platform, + fe_dec_params_control, + ARRAY_SIZE(fe_dec_params_control)); + kfree(mixer_str); + return 0; +} + +static int msm_compr_add_app_type_cfg_control(struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Audio Stream"; + const char *deviceNo = "NN"; + const char *suffix = "App Type Cfg"; + char *mixer_str = NULL; + int ctl_len; + struct snd_kcontrol_new fe_app_type_cfg_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_compr_app_type_cfg_info, + .put = msm_compr_app_type_cfg_put, + .get = msm_compr_app_type_cfg_get, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + return 0; + } + + pr_debug("%s: added new compr FE ctl with name %s, id %d, cpu dai %s, device no %d\n", + __func__, rtd->dai_link->name, rtd->dai_link->be_id, + rtd->dai_link->cpu_dai_name, rtd->pcm->device); + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1 + + strlen(suffix) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + + if (!mixer_str) { + pr_err("failed to allocate mixer ctrl str of len %d", ctl_len); + return 0; } + snprintf(mixer_str, ctl_len, "%s %d %s", mixer_ctl_name, + rtd->pcm->device, suffix); + fe_app_type_cfg_control[0].name = mixer_str; + fe_app_type_cfg_control[0].private_value = rtd->dai_link->be_id; + pr_debug("Registering new mixer ctl %s", mixer_str); + snd_soc_add_platform_controls(rtd->platform, + fe_app_type_cfg_control, + ARRAY_SIZE(fe_app_type_cfg_control)); + kfree(mixer_str); + return 0; +} + +static int msm_compr_add_channel_map_control(struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Playback Channel Map"; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + struct msm_compr_pdata *pdata = NULL; + int ctl_len; + struct snd_kcontrol_new fe_channel_map_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_compr_channel_map_info, + .get = msm_compr_channel_map_get, + .put = msm_compr_channel_map_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s: NULL rtd\n", __func__); + return -EINVAL; + } + + pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n", + __func__, rtd->dai_link->name, rtd->dai_link->be_id, + rtd->dai_link->cpu_dai_name, rtd->pcm->device); + + ctl_len = strlen(mixer_ctl_name) + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + + if (!mixer_str) { + pr_err("%s: failed to allocate mixer ctrl str of len %d\n", + __func__, ctl_len); + return -ENOMEM; + } + + snprintf(mixer_str, ctl_len, "%s%d", mixer_ctl_name, rtd->pcm->device); + + fe_channel_map_control[0].name = mixer_str; + fe_channel_map_control[0].private_value = rtd->dai_link->be_id; + pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str); + snd_soc_add_platform_controls(rtd->platform, + fe_channel_map_control, + ARRAY_SIZE(fe_channel_map_control)); + + pdata = snd_soc_platform_get_drvdata(rtd->platform); + pdata->ch_map[rtd->dai_link->be_id] = + kzalloc(sizeof(struct msm_compr_ch_map), GFP_KERNEL); + if (!pdata->ch_map[rtd->dai_link->be_id]) { + pr_err("%s: Could not allocate memory for channel map\n", + __func__); + kfree(mixer_str); + return -ENOMEM; + } + kfree(mixer_str); + return 0; +} + +static int msm_compr_new(struct snd_soc_pcm_runtime *rtd) +{ + int rc; + + rc = msm_compr_add_volume_control(rtd); + if (rc) + pr_err("%s: Could not add Compr Volume Control\n", __func__); + + rc = msm_compr_add_audio_effects_control(rtd); + if (rc) + pr_err("%s: Could not add Compr Audio Effects Control\n", + __func__); + + rc = msm_compr_add_query_audio_effect_control(rtd); + if (rc) + pr_err("%s: Could not add Compr Query Audio Effect Control\n", + __func__); + + rc = msm_compr_add_dec_runtime_params_control(rtd); + if (rc) + pr_err("%s: Could not add Compr Dec runtime params Control\n", + __func__); + rc = msm_compr_add_app_type_cfg_control(rtd); + if (rc) + pr_err("%s: Could not add Compr App Type Cfg Control\n", + __func__); + rc = msm_compr_add_channel_map_control(rtd); + if (rc) + pr_err("%s: Could not add Compr Channel Map Control\n", + __func__); return 0; } @@ -1089,14 +3165,14 @@ static struct snd_compr_ops msm_compr_ops = { static struct snd_soc_platform_driver msm_soc_platform = { .probe = msm_compr_probe, .compr_ops = &msm_compr_ops, - .controls = msm_compr_volume_controls, - .num_controls = ARRAY_SIZE(msm_compr_volume_controls), + .pcm_new = msm_compr_new, + .controls = msm_compr_gapless_controls, + .num_controls = ARRAY_SIZE(msm_compr_gapless_controls), + }; static int msm_compr_dev_probe(struct platform_device *pdev) { - if (pdev->dev.of_node) - dev_set_name(&pdev->dev, "%s", "msm-compress-dsp"); pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev)); return snd_soc_register_platform(&pdev->dev, |
