Unverified Commit 28f95795 authored by Mark Brown's avatar Mark Brown
Browse files

Merge series "ASoC: qdsp6: add gapless compressed audio support" from Srinivas...

Merge series "ASoC: qdsp6: add gapless compressed audio support" from Srinivas Kandagatla <srinivas.kandagatla@linaro.org>:

This patchset adds gapless compressed audio support on q6asm.
Gapless on q6asm is implemented using 2 streams in a single q6asm session.

First few patches such as stream id per each command, gapless flags
and silence meta data are for preparedness for adding gapless support.
Last patch implements copy callback to allow finer control over buffer offsets,
specially in partial drain cases.

This patchset is tested on RB3 aka DB845c platform.

This patchset as it is will support gapless however QDSP can also
support switching decoders on a single stream. Patches to support such feature
are send in different patchset which involves adding generic interfaces.

Thanks,
srini

Changes since v2:(mostly suggested by Pierre)
- removed unnessary kernel style comments,
- moved TIMESTAMP flag to respective patch.
- move preparatory code from gapless support patch to new one.
- fix subject prefix of one patch.
- add comments to clarify valid stream_ids

Srinivas Kandagatla (10):
  ASoC: q6asm: rename misleading session id variable
  ASoC: q6asm: make commands specific to streams
  ASoC: q6asm: use flags directly from q6asm-dai
  ASoC: q6asm: add length to write command token
  ASoC: q6asm: add support to remove intial and trailing silence
  ASoC: q6asm: add support to gapless flag in q6asm open
  ASoC: q6asm-dai: add next track metadata support
  ASoC: q6asm-dai: prepare set params to accept profile change
  ASoC: q6asm-dai: add gapless support
  ASoC: q6asm-dai: add support to copy callback

 sound/soc/qcom/qdsp6/q6asm-dai.c | 414 +++++++++++++++++++++++--------
 sound/soc/qcom/qdsp6/q6asm.c     | 169 +++++++++----
 sound/soc/qcom/qdsp6/q6asm.h     |  49 ++--
 3 files changed, 469 insertions(+), 163 deletions(-)

--
2.21.0
parents 5ca8f9a5 a08cd56a
Loading
Loading
Loading
Loading
+317 −97
Original line number Diff line number Diff line
@@ -50,7 +50,7 @@ enum stream_state {
struct q6asm_dai_rtd {
	struct snd_pcm_substream *substream;
	struct snd_compr_stream *cstream;
	struct snd_compr_params codec_param;
	struct snd_codec codec;
	struct snd_dma_buffer dma_buffer;
	spinlock_t lock;
	phys_addr_t phys;
@@ -64,8 +64,14 @@ struct q6asm_dai_rtd {
	uint16_t bits_per_sample;
	uint16_t source; /* Encoding source bit mask */
	struct audio_client *audio_client;
	uint32_t next_track_stream_id;
	bool next_track;
	uint32_t stream_id;
	uint16_t session_id;
	enum stream_state state;
	uint32_t initial_samples_drop;
	uint32_t trailing_samples_drop;
	bool notify_on_drain;
};

struct q6asm_dai_data {
@@ -181,8 +187,8 @@ static void event_handler(uint32_t opcode, uint32_t token,
	switch (opcode) {
	case ASM_CLIENT_EVENT_CMD_RUN_DONE:
		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
			q6asm_write_async(prtd->audio_client,
				   prtd->pcm_count, 0, 0, NO_TIMESTAMP);
			q6asm_write_async(prtd->audio_client, prtd->stream_id,
				   prtd->pcm_count, 0, 0, 0);
		break;
	case ASM_CLIENT_EVENT_CMD_EOS_DONE:
		prtd->state = Q6ASM_STREAM_STOPPED;
@@ -191,8 +197,8 @@ static void event_handler(uint32_t opcode, uint32_t token,
		prtd->pcm_irq_pos += prtd->pcm_count;
		snd_pcm_period_elapsed(substream);
		if (prtd->state == Q6ASM_STREAM_RUNNING)
			q6asm_write_async(prtd->audio_client,
					   prtd->pcm_count, 0, 0, NO_TIMESTAMP);
			q6asm_write_async(prtd->audio_client, prtd->stream_id,
					   prtd->pcm_count, 0, 0, 0);

		break;
		}
@@ -200,7 +206,7 @@ static void event_handler(uint32_t opcode, uint32_t token,
		prtd->pcm_irq_pos += prtd->pcm_count;
		snd_pcm_period_elapsed(substream);
		if (prtd->state == Q6ASM_STREAM_RUNNING)
			q6asm_read(prtd->audio_client);
			q6asm_read(prtd->audio_client, prtd->stream_id);

		break;
	default:
@@ -233,7 +239,7 @@ static int q6asm_dai_prepare(struct snd_soc_component *component,
	/* rate and channels are sent to audio driver */
	if (prtd->state) {
		/* clear the previous setup if any  */
		q6asm_cmd(prtd->audio_client, CMD_CLOSE);
		q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE);
		q6asm_unmap_memory_regions(substream->stream,
					   prtd->audio_client);
		q6routing_stream_close(soc_prtd->dai_link->id,
@@ -252,10 +258,12 @@ static int q6asm_dai_prepare(struct snd_soc_component *component,
	}

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
		ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM,
				       0, prtd->bits_per_sample);
		ret = q6asm_open_write(prtd->audio_client, prtd->stream_id,
				       FORMAT_LINEAR_PCM,
				       0, prtd->bits_per_sample, false);
	} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
		ret = q6asm_open_read(prtd->audio_client, FORMAT_LINEAR_PCM,
		ret = q6asm_open_read(prtd->audio_client, prtd->stream_id,
				      FORMAT_LINEAR_PCM,
				      prtd->bits_per_sample);
	}

@@ -276,17 +284,19 @@ static int q6asm_dai_prepare(struct snd_soc_component *component,

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
		ret = q6asm_media_format_block_multi_ch_pcm(
				prtd->audio_client, runtime->rate,
				runtime->channels, NULL,
				prtd->audio_client, prtd->stream_id,
				runtime->rate, runtime->channels, NULL,
				prtd->bits_per_sample);
	} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
		ret = q6asm_enc_cfg_blk_pcm_format_support(prtd->audio_client,
					runtime->rate, runtime->channels,
							   prtd->stream_id,
							   runtime->rate,
							   runtime->channels,
							   prtd->bits_per_sample);

		/* Queue the buffers */
		for (i = 0; i < runtime->periods; i++)
			q6asm_read(prtd->audio_client);
			q6asm_read(prtd->audio_client, prtd->stream_id);

	}
	if (ret < 0)
@@ -308,15 +318,18 @@ static int q6asm_dai_trigger(struct snd_soc_component *component,
	case SNDRV_PCM_TRIGGER_START:
	case SNDRV_PCM_TRIGGER_RESUME:
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
		ret = q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
		ret = q6asm_run_nowait(prtd->audio_client, prtd->stream_id,
				       0, 0, 0);
		break;
	case SNDRV_PCM_TRIGGER_STOP:
		prtd->state = Q6ASM_STREAM_STOPPED;
		ret = q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
		ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id,
				       CMD_EOS);
		break;
	case SNDRV_PCM_TRIGGER_SUSPEND:
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
		ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
		ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id,
				       CMD_PAUSE);
		break;
	default:
		ret = -EINVAL;
@@ -361,6 +374,9 @@ static int q6asm_dai_open(struct snd_soc_component *component,
		return ret;
	}

	/* DSP expects stream id from 1 */
	prtd->stream_id = 1;

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
		runtime->hw = q6asm_dai_hardware_playback;
	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
@@ -427,7 +443,8 @@ static int q6asm_dai_close(struct snd_soc_component *component,

	if (prtd->audio_client) {
		if (prtd->state)
			q6asm_cmd(prtd->audio_client, CMD_CLOSE);
			q6asm_cmd(prtd->audio_client, prtd->stream_id,
				  CMD_CLOSE);

		q6asm_unmap_memory_regions(substream->stream,
					   prtd->audio_client);
@@ -493,14 +510,21 @@ static void compress_event_handler(uint32_t opcode, uint32_t token,
	struct q6asm_dai_rtd *prtd = priv;
	struct snd_compr_stream *substream = prtd->cstream;
	unsigned long flags;
	u32 wflags = 0;
	uint64_t avail;
	uint32_t bytes_written, bytes_to_write;
	bool is_last_buffer = false;

	switch (opcode) {
	case ASM_CLIENT_EVENT_CMD_RUN_DONE:
		spin_lock_irqsave(&prtd->lock, flags);
		if (!prtd->bytes_sent) {
			q6asm_write_async(prtd->audio_client, prtd->pcm_count,
					  0, 0, NO_TIMESTAMP);
			q6asm_stream_remove_initial_silence(prtd->audio_client,
						    prtd->stream_id,
						    prtd->initial_samples_drop);

			q6asm_write_async(prtd->audio_client, prtd->stream_id,
					  prtd->pcm_count, 0, 0, 0);
			prtd->bytes_sent += prtd->pcm_count;
		}

@@ -508,13 +532,37 @@ static void compress_event_handler(uint32_t opcode, uint32_t token,
		break;

	case ASM_CLIENT_EVENT_CMD_EOS_DONE:
		spin_lock_irqsave(&prtd->lock, flags);
		if (prtd->notify_on_drain) {
			if (substream->partial_drain) {
				/*
				 * Close old stream and make it stale, switch
				 * the active stream now!
				 */
				q6asm_cmd_nowait(prtd->audio_client,
						 prtd->stream_id,
						 CMD_CLOSE);
				/*
				 * vaild stream ids start from 1, So we are
				 * toggling this between 1 and 2.
				 */
				prtd->stream_id = (prtd->stream_id == 1 ? 2 : 1);
			}

			snd_compr_drain_notify(prtd->cstream);
			prtd->notify_on_drain = false;

		} else {
			prtd->state = Q6ASM_STREAM_STOPPED;
		}
		spin_unlock_irqrestore(&prtd->lock, flags);
		break;

	case ASM_CLIENT_EVENT_DATA_WRITE_DONE:
		spin_lock_irqsave(&prtd->lock, flags);

		prtd->copied_total += prtd->pcm_count;
		bytes_written = token >> ASM_WRITE_TOKEN_LEN_SHIFT;
		prtd->copied_total += bytes_written;
		snd_compr_fragment_elapsed(substream);

		if (prtd->state != Q6ASM_STREAM_RUNNING) {
@@ -523,13 +571,32 @@ static void compress_event_handler(uint32_t opcode, uint32_t token,
		}

		avail = prtd->bytes_received - prtd->bytes_sent;
		if (avail > prtd->pcm_count) {
			bytes_to_write = prtd->pcm_count;
		} else {
			if (substream->partial_drain || prtd->notify_on_drain)
				is_last_buffer = true;
			bytes_to_write = avail;
		}

		if (avail >= prtd->pcm_count) {
			q6asm_write_async(prtd->audio_client,
					   prtd->pcm_count, 0, 0, NO_TIMESTAMP);
			prtd->bytes_sent += prtd->pcm_count;
		if (bytes_to_write) {
			if (substream->partial_drain && is_last_buffer) {
				wflags |= ASM_LAST_BUFFER_FLAG;
				q6asm_stream_remove_trailing_silence(prtd->audio_client,
						     prtd->stream_id,
						     prtd->trailing_samples_drop);
			}

			q6asm_write_async(prtd->audio_client, prtd->stream_id,
					  bytes_to_write, 0, 0, wflags);

			prtd->bytes_sent += bytes_to_write;
		}

		if (prtd->notify_on_drain && is_last_buffer)
			q6asm_cmd_nowait(prtd->audio_client,
					 prtd->stream_id, CMD_EOS);

		spin_unlock_irqrestore(&prtd->lock, flags);
		break;

@@ -560,6 +627,9 @@ static int q6asm_dai_compr_open(struct snd_soc_component *component,
	if (!prtd)
		return -ENOMEM;

	/* DSP expects stream id from 1 */
	prtd->stream_id = 1;

	prtd->cstream = stream;
	prtd->audio_client = q6asm_audio_client_alloc(dev,
					(q6asm_cb)compress_event_handler,
@@ -606,8 +676,15 @@ static int q6asm_dai_compr_free(struct snd_soc_component *component,
	struct snd_soc_pcm_runtime *rtd = stream->private_data;

	if (prtd->audio_client) {
		if (prtd->state)
			q6asm_cmd(prtd->audio_client, CMD_CLOSE);
		if (prtd->state) {
			q6asm_cmd(prtd->audio_client, prtd->stream_id,
				  CMD_CLOSE);
			if (prtd->next_track_stream_id) {
				q6asm_cmd(prtd->audio_client,
					  prtd->next_track_stream_id,
					  CMD_CLOSE);
			}
		}

		snd_dma_free_pages(&prtd->dma_buffer);
		q6asm_unmap_memory_regions(stream->direction,
@@ -621,15 +698,13 @@ static int q6asm_dai_compr_free(struct snd_soc_component *component,
	return 0;
}

static int q6asm_dai_compr_set_params(struct snd_soc_component *component,
static int __q6asm_dai_compr_set_codec_params(struct snd_soc_component *component,
					      struct snd_compr_stream *stream,
				      struct snd_compr_params *params)
					      struct snd_codec *codec,
					      int stream_id)
{
	struct snd_compr_runtime *runtime = stream->runtime;
	struct q6asm_dai_rtd *prtd = runtime->private_data;
	struct snd_soc_pcm_runtime *rtd = stream->private_data;
	int dir = stream->direction;
	struct q6asm_dai_data *pdata;
	struct q6asm_flac_cfg flac_cfg;
	struct q6asm_wma_cfg wma_cfg;
	struct q6asm_alac_cfg alac_cfg;
@@ -643,52 +718,18 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component,
	struct snd_dec_alac *alac;
	struct snd_dec_ape *ape;

	codec_options = &(prtd->codec_param.codec.options);


	memcpy(&prtd->codec_param, params, sizeof(*params));

	pdata = snd_soc_component_get_drvdata(component);
	if (!pdata)
		return -EINVAL;

	if (!prtd || !prtd->audio_client) {
		dev_err(dev, "private data null or audio client freed\n");
		return -EINVAL;
	}
	codec_options = &(prtd->codec.options);

	prtd->periods = runtime->fragments;
	prtd->pcm_count = runtime->fragment_size;
	prtd->pcm_size = runtime->fragments * runtime->fragment_size;
	prtd->bits_per_sample = 16;
	if (dir == SND_COMPRESS_PLAYBACK) {
		ret = q6asm_open_write(prtd->audio_client, params->codec.id,
				params->codec.profile, prtd->bits_per_sample);
	memcpy(&prtd->codec, codec, sizeof(*codec));

		if (ret < 0) {
			dev_err(dev, "q6asm_open_write failed\n");
			q6asm_audio_client_free(prtd->audio_client);
			prtd->audio_client = NULL;
			return ret;
		}
	}

	prtd->session_id = q6asm_get_session_id(prtd->audio_client);
	ret = q6routing_stream_open(rtd->dai_link->id, LEGACY_PCM_MODE,
			      prtd->session_id, dir);
	if (ret) {
		dev_err(dev, "Stream reg failed ret:%d\n", ret);
		return ret;
	}

	switch (params->codec.id) {
	switch (codec->id) {
	case SND_AUDIOCODEC_FLAC:

		memset(&flac_cfg, 0x0, sizeof(struct q6asm_flac_cfg));
		flac = &codec_options->flac_d;

		flac_cfg.ch_cfg = params->codec.ch_in;
		flac_cfg.sample_rate =  params->codec.sample_rate;
		flac_cfg.ch_cfg = codec->ch_in;
		flac_cfg.sample_rate = codec->sample_rate;
		flac_cfg.stream_info_present = 1;
		flac_cfg.sample_size = flac->sample_size;
		flac_cfg.min_blk_size = flac->min_blk_size;
@@ -697,6 +738,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component,
		flac_cfg.min_frame_size = flac->min_frame_size;

		ret = q6asm_stream_media_format_block_flac(prtd->audio_client,
							   stream_id,
							   &flac_cfg);
		if (ret < 0) {
			dev_err(dev, "FLAC CMD Format block failed:%d\n", ret);
@@ -709,10 +751,10 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component,

		memset(&wma_cfg, 0x0, sizeof(struct q6asm_wma_cfg));

		wma_cfg.sample_rate =  params->codec.sample_rate;
		wma_cfg.num_channels = params->codec.ch_in;
		wma_cfg.bytes_per_sec = params->codec.bit_rate / 8;
		wma_cfg.block_align = params->codec.align;
		wma_cfg.sample_rate =  codec->sample_rate;
		wma_cfg.num_channels = codec->ch_in;
		wma_cfg.bytes_per_sec = codec->bit_rate / 8;
		wma_cfg.block_align = codec->align;
		wma_cfg.bits_per_sample = prtd->bits_per_sample;
		wma_cfg.enc_options = wma->encoder_option;
		wma_cfg.adv_enc_options = wma->adv_encoder_option;
@@ -726,7 +768,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component,
			return -EINVAL;

		/* check the codec profile */
		switch (params->codec.profile) {
		switch (codec->profile) {
		case SND_AUDIOPROFILE_WMA9:
			wma_cfg.fmtag = 0x161;
			wma_v9 = 1;
@@ -750,16 +792,18 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component,

		default:
			dev_err(dev, "Unknown WMA profile:%x\n",
				params->codec.profile);
				codec->profile);
			return -EIO;
		}

		if (wma_v9)
			ret = q6asm_stream_media_format_block_wma_v9(
					prtd->audio_client, &wma_cfg);
					prtd->audio_client, stream_id,
					&wma_cfg);
		else
			ret = q6asm_stream_media_format_block_wma_v10(
					prtd->audio_client, &wma_cfg);
					prtd->audio_client, stream_id,
					&wma_cfg);
		if (ret < 0) {
			dev_err(dev, "WMA9 CMD failed:%d\n", ret);
			return -EIO;
@@ -770,10 +814,10 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component,
		memset(&alac_cfg, 0x0, sizeof(alac_cfg));
		alac = &codec_options->alac_d;

		alac_cfg.sample_rate = params->codec.sample_rate;
		alac_cfg.avg_bit_rate = params->codec.bit_rate;
		alac_cfg.sample_rate = codec->sample_rate;
		alac_cfg.avg_bit_rate = codec->bit_rate;
		alac_cfg.bit_depth = prtd->bits_per_sample;
		alac_cfg.num_channels = params->codec.ch_in;
		alac_cfg.num_channels = codec->ch_in;

		alac_cfg.frame_length = alac->frame_length;
		alac_cfg.pb = alac->pb;
@@ -783,7 +827,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component,
		alac_cfg.compatible_version = alac->compatible_version;
		alac_cfg.max_frame_bytes = alac->max_frame_bytes;

		switch (params->codec.ch_in) {
		switch (codec->ch_in) {
		case 1:
			alac_cfg.channel_layout_tag = ALAC_CH_LAYOUT_MONO;
			break;
@@ -792,6 +836,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component,
			break;
		}
		ret = q6asm_stream_media_format_block_alac(prtd->audio_client,
							   stream_id,
							   &alac_cfg);
		if (ret < 0) {
			dev_err(dev, "ALAC CMD Format block failed:%d\n", ret);
@@ -803,8 +848,8 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component,
		memset(&ape_cfg, 0x0, sizeof(ape_cfg));
		ape = &codec_options->ape_d;

		ape_cfg.sample_rate = params->codec.sample_rate;
		ape_cfg.num_channels = params->codec.ch_in;
		ape_cfg.sample_rate = codec->sample_rate;
		ape_cfg.num_channels = codec->ch_in;
		ape_cfg.bits_per_sample = prtd->bits_per_sample;

		ape_cfg.compatible_version = ape->compatible_version;
@@ -816,6 +861,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component,
		ape_cfg.seek_table_present = ape->seek_table_present;

		ret = q6asm_stream_media_format_block_ape(prtd->audio_client,
							  stream_id,
							  &ape_cfg);
		if (ret < 0) {
			dev_err(dev, "APE CMD Format block failed:%d\n", ret);
@@ -827,6 +873,64 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component,
		break;
	}

	return 0;
}

static int q6asm_dai_compr_set_params(struct snd_soc_component *component,
				      struct snd_compr_stream *stream,
				      struct snd_compr_params *params)
{
	struct snd_compr_runtime *runtime = stream->runtime;
	struct q6asm_dai_rtd *prtd = runtime->private_data;
	struct snd_soc_pcm_runtime *rtd = stream->private_data;
	int dir = stream->direction;
	struct q6asm_dai_data *pdata;
	struct device *dev = component->dev;
	int ret;

	pdata = snd_soc_component_get_drvdata(component);
	if (!pdata)
		return -EINVAL;

	if (!prtd || !prtd->audio_client) {
		dev_err(dev, "private data null or audio client freed\n");
		return -EINVAL;
	}

	prtd->periods = runtime->fragments;
	prtd->pcm_count = runtime->fragment_size;
	prtd->pcm_size = runtime->fragments * runtime->fragment_size;
	prtd->bits_per_sample = 16;

	if (dir == SND_COMPRESS_PLAYBACK) {
		ret = q6asm_open_write(prtd->audio_client, prtd->stream_id, params->codec.id,
				params->codec.profile, prtd->bits_per_sample,
				true);

		if (ret < 0) {
			dev_err(dev, "q6asm_open_write failed\n");
			q6asm_audio_client_free(prtd->audio_client);
			prtd->audio_client = NULL;
			return ret;
		}
	}

	prtd->session_id = q6asm_get_session_id(prtd->audio_client);
	ret = q6routing_stream_open(rtd->dai_link->id, LEGACY_PCM_MODE,
			      prtd->session_id, dir);
	if (ret) {
		dev_err(dev, "Stream reg failed ret:%d\n", ret);
		return ret;
	}

	ret = __q6asm_dai_compr_set_codec_params(component, stream,
						 &params->codec,
						 prtd->stream_id);
	if (ret) {
		dev_err(dev, "codec param setup failed ret:%d\n", ret);
		return ret;
	}

	ret = q6asm_map_memory_regions(dir, prtd->audio_client, prtd->phys,
				       (prtd->pcm_size / prtd->periods),
				       prtd->periods);
@@ -841,6 +945,55 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component,
	return 0;
}

static int q6asm_dai_compr_set_metadata(struct snd_soc_component *component,
					struct snd_compr_stream *stream,
					struct snd_compr_metadata *metadata)
{
	struct snd_compr_runtime *runtime = stream->runtime;
	struct q6asm_dai_rtd *prtd = runtime->private_data;
	int ret = 0;

	switch (metadata->key) {
	case SNDRV_COMPRESS_ENCODER_PADDING:
		prtd->trailing_samples_drop = metadata->value[0];
		break;
	case SNDRV_COMPRESS_ENCODER_DELAY:
		prtd->initial_samples_drop = metadata->value[0];
		if (prtd->next_track_stream_id) {
			ret = q6asm_open_write(prtd->audio_client,
					       prtd->next_track_stream_id,
					       prtd->codec.id,
					       prtd->codec.profile,
					       prtd->bits_per_sample,
				       true);
			if (ret < 0) {
				dev_err(component->dev, "q6asm_open_write failed\n");
				return ret;
			}
			ret = __q6asm_dai_compr_set_codec_params(component, stream,
								 &prtd->codec,
								 prtd->next_track_stream_id);
			if (ret < 0) {
				dev_err(component->dev, "q6asm_open_write failed\n");
				return ret;
			}

			ret = q6asm_stream_remove_initial_silence(prtd->audio_client,
						    prtd->next_track_stream_id,
						    prtd->initial_samples_drop);
			prtd->next_track_stream_id = 0;

		}

		break;
	default:
		ret = -EINVAL;
		break;
	}

	return ret;
}

static int q6asm_dai_compr_trigger(struct snd_soc_component *component,
				   struct snd_compr_stream *stream, int cmd)
{
@@ -852,15 +1005,26 @@ static int q6asm_dai_compr_trigger(struct snd_soc_component *component,
	case SNDRV_PCM_TRIGGER_START:
	case SNDRV_PCM_TRIGGER_RESUME:
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
		ret = q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
		ret = q6asm_run_nowait(prtd->audio_client, prtd->stream_id,
				       0, 0, 0);
		break;
	case SNDRV_PCM_TRIGGER_STOP:
		prtd->state = Q6ASM_STREAM_STOPPED;
		ret = q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
		ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id,
				       CMD_EOS);
		break;
	case SNDRV_PCM_TRIGGER_SUSPEND:
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
		ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
		ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id,
				       CMD_PAUSE);
		break;
	case SND_COMPR_TRIGGER_NEXT_TRACK:
		prtd->next_track = true;
		prtd->next_track_stream_id = (prtd->stream_id == 1 ? 2 : 1);
		break;
	case SND_COMPR_TRIGGER_DRAIN:
	case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
		prtd->notify_on_drain = true;
		break;
	default:
		ret = -EINVAL;
@@ -888,16 +1052,71 @@ static int q6asm_dai_compr_pointer(struct snd_soc_component *component,
	return 0;
}

static int q6asm_dai_compr_ack(struct snd_soc_component *component,
			       struct snd_compr_stream *stream,
static int q6asm_compr_copy(struct snd_soc_component *component,
			    struct snd_compr_stream *stream, char __user *buf,
			    size_t count)
{
	struct snd_compr_runtime *runtime = stream->runtime;
	struct q6asm_dai_rtd *prtd = runtime->private_data;
	unsigned long flags;
	u32 wflags = 0;
	int avail, bytes_in_flight = 0;
	void *dstn;
	size_t copy;
	u32 app_pointer;
	u32 bytes_received;

	bytes_received = prtd->bytes_received;

	/**
	 * Make sure that next track data pointer is aligned at 32 bit boundary
	 * This is a Mandatory requirement from DSP data buffers alignment
	 */
	if (prtd->next_track)
		bytes_received = ALIGN(prtd->bytes_received, prtd->pcm_count);

	app_pointer = bytes_received/prtd->pcm_size;
	app_pointer = bytes_received -  (app_pointer * prtd->pcm_size);
	dstn = prtd->dma_buffer.area + app_pointer;

	if (count < prtd->pcm_size - app_pointer) {
		if (copy_from_user(dstn, buf, count))
			return -EFAULT;
	} else {
		copy = prtd->pcm_size - app_pointer;
		if (copy_from_user(dstn, buf, copy))
			return -EFAULT;
		if (copy_from_user(prtd->dma_buffer.area, buf + copy,
				   count - copy))
			return -EFAULT;
	}

	spin_lock_irqsave(&prtd->lock, flags);
	prtd->bytes_received += count;

	bytes_in_flight = prtd->bytes_received - prtd->copied_total;

	if (prtd->next_track) {
		prtd->next_track = false;
		prtd->copied_total = ALIGN(prtd->copied_total, prtd->pcm_count);
		prtd->bytes_sent = ALIGN(prtd->bytes_sent, prtd->pcm_count);
	}

	prtd->bytes_received = bytes_received + count;

	/* Kick off the data to dsp if its starving!! */
	if (prtd->state == Q6ASM_STREAM_RUNNING && (bytes_in_flight == 0)) {
		uint32_t bytes_to_write = prtd->pcm_count;

		avail = prtd->bytes_received - prtd->bytes_sent;

		if (avail < prtd->pcm_count)
			bytes_to_write = avail;

		q6asm_write_async(prtd->audio_client, prtd->stream_id,
				  bytes_to_write, 0, 0, wflags);
		prtd->bytes_sent += bytes_to_write;
	}

	spin_unlock_irqrestore(&prtd->lock, flags);

	return count;
@@ -954,12 +1173,13 @@ static struct snd_compress_ops q6asm_dai_compress_ops = {
	.open		= q6asm_dai_compr_open,
	.free		= q6asm_dai_compr_free,
	.set_params	= q6asm_dai_compr_set_params,
	.set_metadata	= q6asm_dai_compr_set_metadata,
	.pointer	= q6asm_dai_compr_pointer,
	.trigger	= q6asm_dai_compr_trigger,
	.get_caps	= q6asm_dai_compr_get_caps,
	.get_codec_caps	= q6asm_dai_compr_get_codec_caps,
	.mmap		= q6asm_dai_compr_mmap,
	.ack		= q6asm_dai_compr_ack,
	.copy		= q6asm_compr_copy,
};

static int q6asm_dai_pcm_new(struct snd_soc_component *component,
+118 −51

File changed.

Preview size limit exceeded, changes collapsed.

+34 −15
Original line number Diff line number Diff line
@@ -20,6 +20,9 @@
#define ASM_CLIENT_EVENT_CMD_RUN_DONE		0x1008
#define ASM_CLIENT_EVENT_DATA_WRITE_DONE	0x1009
#define ASM_CLIENT_EVENT_DATA_READ_DONE		0x100a
#define ASM_WRITE_TOKEN_MASK			GENMASK(15, 0)
#define ASM_WRITE_TOKEN_LEN_MASK		GENMASK(31, 16)
#define ASM_WRITE_TOKEN_LEN_SHIFT		16

enum {
	LEGACY_PCM_MODE = 0,
@@ -29,8 +32,8 @@ enum {
};

#define MAX_SESSIONS	8
#define NO_TIMESTAMP    0xFF00
#define FORMAT_LINEAR_PCM   0x0000
#define ASM_LAST_BUFFER_FLAG           BIT(30)

struct q6asm_flac_cfg {
        u32 sample_rate;
@@ -93,37 +96,53 @@ struct audio_client *q6asm_audio_client_alloc(struct device *dev,
					      q6asm_cb cb, void *priv,
					      int session_id, int perf_mode);
void q6asm_audio_client_free(struct audio_client *ac);
int q6asm_write_async(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
		       uint32_t lsw_ts, uint32_t flags);
int q6asm_open_write(struct audio_client *ac, uint32_t format,
		     u32 codec_profile, uint16_t bits_per_sample);
int q6asm_write_async(struct audio_client *ac, uint32_t stream_id, uint32_t len,
		      uint32_t msw_ts, uint32_t lsw_ts, uint32_t flags);
int q6asm_open_write(struct audio_client *ac, uint32_t stream_id,
		     uint32_t format, u32 codec_profile,
		     uint16_t bits_per_sample, bool is_gapless);

int q6asm_open_read(struct audio_client *ac, uint32_t format,
		     uint16_t bits_per_sample);
int q6asm_open_read(struct audio_client *ac, uint32_t stream_id,
		    uint32_t format, uint16_t bits_per_sample);
int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac,
		uint32_t rate, uint32_t channels, uint16_t bits_per_sample);
int q6asm_read(struct audio_client *ac);
					 uint32_t stream_id, uint32_t rate,
					 uint32_t channels,
					 uint16_t bits_per_sample);

int q6asm_read(struct audio_client *ac, uint32_t stream_id);

int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
					  uint32_t stream_id,
					  uint32_t rate, uint32_t channels,
					  u8 channel_map[PCM_MAX_NUM_CHANNEL],
					  uint16_t bits_per_sample);
int q6asm_stream_media_format_block_flac(struct audio_client *ac,
					 uint32_t stream_id,
					 struct q6asm_flac_cfg *cfg);
int q6asm_stream_media_format_block_wma_v9(struct audio_client *ac,
					   uint32_t stream_id,
					   struct q6asm_wma_cfg *cfg);
int q6asm_stream_media_format_block_wma_v10(struct audio_client *ac,
					    uint32_t stream_id,
					    struct q6asm_wma_cfg *cfg);
int q6asm_stream_media_format_block_alac(struct audio_client *ac,
					 uint32_t stream_id,
					 struct q6asm_alac_cfg *cfg);
int q6asm_stream_media_format_block_ape(struct audio_client *ac,
					uint32_t stream_id,
					struct q6asm_ape_cfg *cfg);
int q6asm_run(struct audio_client *ac, uint32_t flags, uint32_t msw_ts,
	      uint32_t lsw_ts);
int q6asm_run_nowait(struct audio_client *ac, uint32_t flags, uint32_t msw_ts,
		     uint32_t lsw_ts);
int q6asm_cmd(struct audio_client *ac, int cmd);
int q6asm_cmd_nowait(struct audio_client *ac, int cmd);
int q6asm_run(struct audio_client *ac, uint32_t stream_id, uint32_t flags,
	      uint32_t msw_ts, uint32_t lsw_ts);
int q6asm_run_nowait(struct audio_client *ac, uint32_t stream_id,
		     uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts);
int q6asm_stream_remove_initial_silence(struct audio_client *ac,
					uint32_t stream_id,
					uint32_t initial_samples);
int q6asm_stream_remove_trailing_silence(struct audio_client *ac,
					 uint32_t stream_id,
					 uint32_t trailing_samples);
int q6asm_cmd(struct audio_client *ac, uint32_t stream_id,  int cmd);
int q6asm_cmd_nowait(struct audio_client *ac, uint32_t stream_id,  int cmd);
int q6asm_get_session_id(struct audio_client *ac);
int q6asm_map_memory_regions(unsigned int dir,
			     struct audio_client *ac,