Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2a9c6ba1 authored by Eric Laurent's avatar Eric Laurent Committed by Banajit Goswami
Browse files

ASoC: msm: handle partial writes in compress driver



Compress driver might have to deal with arbitrary sized writes
in the middle of playback. To deal with it, accumulate fragment_size
worth data before sending to the DSP. Use the drain trigger to push
any pending buffer to the DSP.

Change-Id: I8d95a9945076142f16adb99a295f6a9e84039cbb
Signed-off-by: default avatarHaynes Mathew George <hgeorge@codeaurora.org>
Signed-off-by: default avatarEric Laurent <elaurent@google.com>
Git-commit: 6ac9bb553c1285266b2ac28800abec240cd76e17
Git-repo: https://android.googlesource.com/kernel/msm


[dhakumar@codeaurora.org: resolved merge conflicts]
Signed-off-by: default avatarDhananjay Kumar <dhakumar@codeaurora.org>
Signed-off-by: default avatarBanajit Goswami <bgoswami@codeaurora.org>
parent 3dd3be67
Loading
Loading
Loading
Loading
+169 −43
Original line number Diff line number Diff line
@@ -88,6 +88,7 @@ struct msm_compr_audio {
	atomic_t start;
	atomic_t eos;
	atomic_t drain;
	atomic_t xrun;

	wait_queue_head_t eos_wait;
	wait_queue_head_t drain_wait;
@@ -133,6 +134,13 @@ static int msm_compr_send_buffer(struct msm_compr_audio *prtd)
		pr_err("%s: stream is not in started state\n", __func__);
		return -EINVAL;
	}


	if (atomic_read(&prtd->xrun)) {
		WARN(1, "%s called while xrun is true", __func__);
		return -EPERM;
	}

	pr_debug("%s: bytes_received = %d copied_total = %d\n",
		__func__, prtd->bytes_received, prtd->copied_total);
	buffer_length = prtd->codec_param.buffer.fragment_size;
@@ -140,17 +148,16 @@ static int msm_compr_send_buffer(struct msm_compr_audio *prtd)
	if (bytes_available < prtd->codec_param.buffer.fragment_size)
		buffer_length = bytes_available;

	if (buffer_length == 0) {
		pr_debug("Received a zero length buffer-break out\n");
		if (atomic_read(&prtd->drain)) {
			prtd->drain_ready = 1;
			wake_up(&prtd->drain_wait);
			atomic_set(&prtd->drain, 0);
		}
		return 0;
	if (prtd->byte_offset + buffer_length > prtd->buffer_size) {
		buffer_length = (prtd->buffer_size - prtd->byte_offset);
		pr_debug("%s: wrap around situation, send partial data %d now",
			 __func__, buffer_length);
	}

	param.paddr	= prtd->buffer_paddr + prtd->byte_offset;
	WARN(param.paddr % 32 != 0, "param.paddr %lx not multiple of 32",
		param.paddr);

	param.len	= buffer_length;
	param.msw_ts	= 0;
	param.lsw_ts	= 0;
@@ -173,20 +180,50 @@ static void compr_event_handler(uint32_t opcode,
	struct snd_compr_stream *cstream = prtd->cstream;
	uint32_t chan_mode = 0;
	uint32_t sample_rate = 0;
	int bytes_available;

	pr_debug("%s opcode =%08x\n", __func__, opcode);
	switch (opcode) {
	case ASM_DATA_EVENT_WRITE_DONE_V2:
		pr_debug("ASM_DATA_EVENT_WRITE_DONE_V2\n");
		spin_lock_irq(&prtd->lock);

		if (payload[3]) {
			pr_err("WRITE FAILED w/ err 0x%x !, paddr 0x%x byte_offset=%d, copied_total=%d, token=%d\n",
			       payload[3],
			       payload[0],
				prtd->byte_offset, prtd->copied_total, token);
			atomic_set(&prtd->start, 0);
		} else {
			pr_debug("ASM_DATA_EVENT_WRITE_DONE_V2 offset %d, length %d\n",
				 prtd->byte_offset, token);
		}

		prtd->byte_offset += token;
		prtd->copied_total += token;
		if (prtd->byte_offset >= prtd->buffer_size)
			prtd->byte_offset -= prtd->buffer_size;

		snd_compr_fragment_elapsed(cstream);
		if (atomic_read(&prtd->start))

		if (!atomic_read(&prtd->start)) {
			spin_unlock_irq(&prtd->lock);
			break;
		}

		bytes_available = prtd->bytes_received - prtd->copied_total;
		if (bytes_available < cstream->runtime->fragment_size) {
			pr_debug("WRITE_DONE Insufficient data to send. break out\n");
			atomic_set(&prtd->xrun, 1);

			if (atomic_read(&prtd->drain)) {
				pr_debug("wake up on drain");
				prtd->drain_ready = 1;
				wake_up(&prtd->drain_wait);
				atomic_set(&prtd->drain, 0);
			}
		} else
			msm_compr_send_buffer(prtd);

		spin_unlock_irq(&prtd->lock);
		break;
	case ASM_DATA_EVENT_RENDERED_EOS:
@@ -215,8 +252,21 @@ static void compr_event_handler(uint32_t opcode,
		case ASM_SESSION_CMD_RUN_V2:
			/* check if the first buffer need to be sent to DSP */
			pr_debug("ASM_SESSION_CMD_RUN_V2\n");
			if (!prtd->copied_total)

			spin_lock_irq(&prtd->lock);
			/* FIXME: A state is a better way, dealing with this */
			if (!prtd->copied_total) {
				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");
					atomic_set(&prtd->xrun, 1);
				} else {
					msm_compr_send_buffer(prtd);
				}
			}
			spin_unlock_irq(&prtd->lock);
			break;
		case ASM_STREAM_CMD_FLUSH:
			pr_debug("ASM_STREAM_CMD_FLUSH\n");
@@ -409,6 +459,7 @@ static int msm_compr_open(struct snd_compr_stream *cstream)
	atomic_set(&prtd->eos, 0);
	atomic_set(&prtd->start, 0);
	atomic_set(&prtd->drain, 0);
	atomic_set(&prtd->xrun, 0);

	init_waitqueue_head(&prtd->eos_wait);
	init_waitqueue_head(&prtd->drain_wait);
@@ -548,6 +599,7 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
			snd_soc_platform_get_drvdata(rtd->platform);
	uint32_t *volume = pdata->volume[rtd->dai_link->be_id];
	int rc = 0;
	int bytes_to_write;

	if (cstream->direction != SND_COMPRESS_PLAYBACK) {
		pr_err("%s: Unsupported stream type\n", __func__);
@@ -580,8 +632,6 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
			atomic_set(&prtd->eos, 0);
		}

		/* Issue flush command only if any buffers are left with DSP */
		if (prtd->bytes_received > prtd->copied_total) {
		pr_debug("issue CMD_FLUSH\n");
		prtd->cmd_ack = 0;
		spin_unlock_irq(&prtd->lock);
@@ -593,10 +643,11 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
		}
		rc = wait_event_timeout(prtd->flush_wait,
					prtd->cmd_ack, 1 * HZ);
			if (!rc)
		if (!rc) {
			rc = -ETIMEDOUT;
			pr_err("Flush cmd timeout\n");
		} else
			spin_unlock_irq(&prtd->lock);
			rc = 0; /* prtd->cmd_status == OK? 0 : -EPERM */

		spin_lock_irq(&prtd->lock);
		/* FIXME. only reset if flush was successful */
@@ -604,6 +655,7 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
		prtd->copied_total = 0;
		prtd->app_pointer  = 0;
		prtd->bytes_received = 0;
		atomic_set(&prtd->xrun, 0);
		spin_unlock_irq(&prtd->lock);
		break;
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
@@ -638,15 +690,43 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
			spin_unlock_irq(&prtd->lock);
			pr_debug("%s: wait till all the data is sent to dsp\n",
				__func__);

			/*
			 * FIXME: Bug.
			 * Write(32767)
			 * Start
			 * Drain <- Indefinite wait
			 * 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);
		} else
			spin_unlock_irq(&prtd->lock);
						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,
				     "%s: last write %d cannot be > than fragment_size\n",
				     __func__, bytes_to_write);

				if (bytes_to_write > 0) {
					pr_debug("%s: send %d partial bytes at the end\n",
					       __func__, bytes_to_write);
					atomic_set(&prtd->xrun, 0);
					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;
		}

@@ -655,6 +735,8 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
		prtd->cmd_ack = 0;
		q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);

		spin_unlock_irq(&prtd->lock);

/*
		if (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN) {
			pr_err("PARTIAL DRAIN, do not wait for EOS ack");
@@ -667,7 +749,7 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
						(prtd->cmd_ack || prtd->cmd_interrupt));

		if (rc < 0)
			pr_err("%s: EOS cmd interrupted\n", __func__);
			pr_err("%s: EOS wait failed\n", __func__);

		pr_debug("%s: SNDRV_COMPRESS_DRAIN  out of wait for EOS\n",
				__func__);
@@ -675,6 +757,38 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
		if (prtd->cmd_interrupt)
			rc = -EINTR;

		/*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);
			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_lock_irq(&prtd->lock);
			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;
			 */
			atomic_set(&prtd->drain, 0);
			atomic_set(&prtd->xrun, 1);
			pr_debug("%s: issue CMD_RESUME", __func__);
			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:
@@ -733,6 +847,9 @@ static int msm_compr_ack(struct snd_compr_stream *cstream,
	void *src, *dstn;
	size_t copy;

	WARN(1, "This path is untested");
	return -EINVAL;

	pr_debug("%s: count = %d\n", __func__, count);
	if (!prtd->buffer) {
		pr_err("%s: Buffer is not allocated yet ??\n", __func__);
@@ -776,6 +893,7 @@ static int msm_compr_copy(struct snd_compr_stream *cstream,
	struct msm_compr_audio *prtd = runtime->private_data;
	void *dstn;
	size_t copy;
	size_t bytes_available = 0;

	pr_debug("%s: count = %d\n", __func__, count);
	if (!prtd->buffer) {
@@ -798,18 +916,26 @@ static int msm_compr_copy(struct snd_compr_stream *cstream,
	}

	/*
	 * If stream is started and all the bytes received were
	 * copied to DSP, the newly received bytes should be
	 * copied right away
	 * 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);

	if (atomic_read(&prtd->start) &&
		prtd->bytes_received == prtd->copied_total) {
	prtd->bytes_received += count;
	if (atomic_read(&prtd->start)) {
		if (atomic_read(&prtd->xrun)) {
			pr_debug("%s: in xrun, count = %d\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",
					 __func__, bytes_available);
				atomic_set(&prtd->xrun, 0);
				msm_compr_send_buffer(prtd);
	} else
		prtd->bytes_received += count;
			} /* else not sufficient data */
		} /* writes will continue on the next write_done */
	}

	spin_unlock_irq(&prtd->lock);