162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci// Copyright 2017-2021 NXP
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/dma-mapping.h>
562306a36Sopenharmony_ci#include <linux/slab.h>
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <linux/delay.h>
862306a36Sopenharmony_ci#include <linux/rpmsg.h>
962306a36Sopenharmony_ci#include <sound/core.h>
1062306a36Sopenharmony_ci#include <sound/pcm.h>
1162306a36Sopenharmony_ci#include <sound/pcm_params.h>
1262306a36Sopenharmony_ci#include <sound/dmaengine_pcm.h>
1362306a36Sopenharmony_ci#include <sound/soc.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "imx-pcm.h"
1662306a36Sopenharmony_ci#include "fsl_rpmsg.h"
1762306a36Sopenharmony_ci#include "imx-pcm-rpmsg.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic struct snd_pcm_hardware imx_rpmsg_pcm_hardware = {
2062306a36Sopenharmony_ci	.info = SNDRV_PCM_INFO_INTERLEAVED |
2162306a36Sopenharmony_ci		SNDRV_PCM_INFO_BLOCK_TRANSFER |
2262306a36Sopenharmony_ci		SNDRV_PCM_INFO_BATCH |
2362306a36Sopenharmony_ci		SNDRV_PCM_INFO_MMAP |
2462306a36Sopenharmony_ci		SNDRV_PCM_INFO_MMAP_VALID |
2562306a36Sopenharmony_ci		SNDRV_PCM_INFO_NO_PERIOD_WAKEUP |
2662306a36Sopenharmony_ci		SNDRV_PCM_INFO_PAUSE |
2762306a36Sopenharmony_ci		SNDRV_PCM_INFO_RESUME,
2862306a36Sopenharmony_ci	.buffer_bytes_max = IMX_DEFAULT_DMABUF_SIZE,
2962306a36Sopenharmony_ci	.period_bytes_min = 512,
3062306a36Sopenharmony_ci	.period_bytes_max = 65536,
3162306a36Sopenharmony_ci	.periods_min = 2,
3262306a36Sopenharmony_ci	.periods_max = 6000,
3362306a36Sopenharmony_ci	.fifo_size = 0,
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg,
3762306a36Sopenharmony_ci				      struct rpmsg_info *info)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	struct rpmsg_device *rpdev = info->rpdev;
4062306a36Sopenharmony_ci	int ret = 0;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	mutex_lock(&info->msg_lock);
4362306a36Sopenharmony_ci	if (!rpdev) {
4462306a36Sopenharmony_ci		dev_err(info->dev, "rpmsg channel not ready\n");
4562306a36Sopenharmony_ci		mutex_unlock(&info->msg_lock);
4662306a36Sopenharmony_ci		return -EINVAL;
4762306a36Sopenharmony_ci	}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	dev_dbg(&rpdev->dev, "send cmd %d\n", msg->s_msg.header.cmd);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	if (!(msg->s_msg.header.type == MSG_TYPE_C))
5262306a36Sopenharmony_ci		reinit_completion(&info->cmd_complete);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	ret = rpmsg_send(rpdev->ept, (void *)&msg->s_msg,
5562306a36Sopenharmony_ci			 sizeof(struct rpmsg_s_msg));
5662306a36Sopenharmony_ci	if (ret) {
5762306a36Sopenharmony_ci		dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret);
5862306a36Sopenharmony_ci		mutex_unlock(&info->msg_lock);
5962306a36Sopenharmony_ci		return ret;
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	/* No receive msg for TYPE_C command */
6362306a36Sopenharmony_ci	if (msg->s_msg.header.type == MSG_TYPE_C) {
6462306a36Sopenharmony_ci		mutex_unlock(&info->msg_lock);
6562306a36Sopenharmony_ci		return 0;
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	/* wait response from rpmsg */
6962306a36Sopenharmony_ci	ret = wait_for_completion_timeout(&info->cmd_complete,
7062306a36Sopenharmony_ci					  msecs_to_jiffies(RPMSG_TIMEOUT));
7162306a36Sopenharmony_ci	if (!ret) {
7262306a36Sopenharmony_ci		dev_err(&rpdev->dev, "rpmsg_send cmd %d timeout!\n",
7362306a36Sopenharmony_ci			msg->s_msg.header.cmd);
7462306a36Sopenharmony_ci		mutex_unlock(&info->msg_lock);
7562306a36Sopenharmony_ci		return -ETIMEDOUT;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	memcpy(&msg->r_msg, &info->r_msg, sizeof(struct rpmsg_r_msg));
7962306a36Sopenharmony_ci	memcpy(&info->msg[msg->r_msg.header.cmd].r_msg,
8062306a36Sopenharmony_ci	       &msg->r_msg, sizeof(struct rpmsg_r_msg));
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	/*
8362306a36Sopenharmony_ci	 * Reset the buffer pointer to be zero, actully we have
8462306a36Sopenharmony_ci	 * set the buffer pointer to be zero in imx_rpmsg_terminate_all
8562306a36Sopenharmony_ci	 * But if there is timer task queued in queue, after it is
8662306a36Sopenharmony_ci	 * executed the buffer pointer will be changed, so need to
8762306a36Sopenharmony_ci	 * reset it again with TERMINATE command.
8862306a36Sopenharmony_ci	 */
8962306a36Sopenharmony_ci	switch (msg->s_msg.header.cmd) {
9062306a36Sopenharmony_ci	case TX_TERMINATE:
9162306a36Sopenharmony_ci		info->msg[TX_POINTER].r_msg.param.buffer_offset = 0;
9262306a36Sopenharmony_ci		break;
9362306a36Sopenharmony_ci	case RX_TERMINATE:
9462306a36Sopenharmony_ci		info->msg[RX_POINTER].r_msg.param.buffer_offset = 0;
9562306a36Sopenharmony_ci		break;
9662306a36Sopenharmony_ci	default:
9762306a36Sopenharmony_ci		break;
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	dev_dbg(&rpdev->dev, "cmd:%d, resp %d\n", msg->s_msg.header.cmd,
10162306a36Sopenharmony_ci		info->r_msg.param.resp);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	mutex_unlock(&info->msg_lock);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	return 0;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic int imx_rpmsg_insert_workqueue(struct snd_pcm_substream *substream,
10962306a36Sopenharmony_ci				      struct rpmsg_msg *msg,
11062306a36Sopenharmony_ci				      struct rpmsg_info *info)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	unsigned long flags;
11362306a36Sopenharmony_ci	int ret = 0;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	/*
11662306a36Sopenharmony_ci	 * Queue the work to workqueue.
11762306a36Sopenharmony_ci	 * If the queue is full, drop the message.
11862306a36Sopenharmony_ci	 */
11962306a36Sopenharmony_ci	spin_lock_irqsave(&info->wq_lock, flags);
12062306a36Sopenharmony_ci	if (info->work_write_index != info->work_read_index) {
12162306a36Sopenharmony_ci		int index = info->work_write_index;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci		memcpy(&info->work_list[index].msg, msg,
12462306a36Sopenharmony_ci		       sizeof(struct rpmsg_s_msg));
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci		queue_work(info->rpmsg_wq, &info->work_list[index].work);
12762306a36Sopenharmony_ci		info->work_write_index++;
12862306a36Sopenharmony_ci		info->work_write_index %= WORK_MAX_NUM;
12962306a36Sopenharmony_ci	} else {
13062306a36Sopenharmony_ci		info->msg_drop_count[substream->stream]++;
13162306a36Sopenharmony_ci		ret = -EPIPE;
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->wq_lock, flags);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	return ret;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic int imx_rpmsg_pcm_hw_params(struct snd_soc_component *component,
13962306a36Sopenharmony_ci				   struct snd_pcm_substream *substream,
14062306a36Sopenharmony_ci				   struct snd_pcm_hw_params *params)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	struct rpmsg_info *info = dev_get_drvdata(component->dev);
14362306a36Sopenharmony_ci	struct rpmsg_msg *msg;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
14662306a36Sopenharmony_ci		msg = &info->msg[TX_HW_PARAM];
14762306a36Sopenharmony_ci		msg->s_msg.header.cmd = TX_HW_PARAM;
14862306a36Sopenharmony_ci	} else {
14962306a36Sopenharmony_ci		msg = &info->msg[RX_HW_PARAM];
15062306a36Sopenharmony_ci		msg->s_msg.header.cmd = RX_HW_PARAM;
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	msg->s_msg.param.rate = params_rate(params);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	switch (params_format(params)) {
15662306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_LE:
15762306a36Sopenharmony_ci		msg->s_msg.param.format   = RPMSG_S16_LE;
15862306a36Sopenharmony_ci		break;
15962306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S24_LE:
16062306a36Sopenharmony_ci		msg->s_msg.param.format   = RPMSG_S24_LE;
16162306a36Sopenharmony_ci		break;
16262306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_DSD_U16_LE:
16362306a36Sopenharmony_ci		msg->s_msg.param.format   = RPMSG_DSD_U16_LE;
16462306a36Sopenharmony_ci		break;
16562306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_DSD_U32_LE:
16662306a36Sopenharmony_ci		msg->s_msg.param.format   = RPMSG_DSD_U32_LE;
16762306a36Sopenharmony_ci		break;
16862306a36Sopenharmony_ci	default:
16962306a36Sopenharmony_ci		msg->s_msg.param.format   = RPMSG_S32_LE;
17062306a36Sopenharmony_ci		break;
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	switch (params_channels(params)) {
17462306a36Sopenharmony_ci	case 1:
17562306a36Sopenharmony_ci		msg->s_msg.param.channels = RPMSG_CH_LEFT;
17662306a36Sopenharmony_ci		break;
17762306a36Sopenharmony_ci	case 2:
17862306a36Sopenharmony_ci		msg->s_msg.param.channels = RPMSG_CH_STEREO;
17962306a36Sopenharmony_ci		break;
18062306a36Sopenharmony_ci	default:
18162306a36Sopenharmony_ci		msg->s_msg.param.channels = params_channels(params);
18262306a36Sopenharmony_ci		break;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	info->send_message(msg, info);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	return 0;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic snd_pcm_uframes_t imx_rpmsg_pcm_pointer(struct snd_soc_component *component,
19162306a36Sopenharmony_ci					       struct snd_pcm_substream *substream)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	struct rpmsg_info *info = dev_get_drvdata(component->dev);
19462306a36Sopenharmony_ci	struct rpmsg_msg *msg;
19562306a36Sopenharmony_ci	unsigned int pos = 0;
19662306a36Sopenharmony_ci	int buffer_tail = 0;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
19962306a36Sopenharmony_ci		msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM];
20062306a36Sopenharmony_ci	else
20162306a36Sopenharmony_ci		msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM];
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	buffer_tail = msg->r_msg.param.buffer_tail;
20462306a36Sopenharmony_ci	pos = buffer_tail * snd_pcm_lib_period_bytes(substream);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	return bytes_to_frames(substream->runtime, pos);
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic void imx_rpmsg_timer_callback(struct timer_list *t)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	struct stream_timer  *stream_timer =
21262306a36Sopenharmony_ci			from_timer(stream_timer, t, timer);
21362306a36Sopenharmony_ci	struct snd_pcm_substream *substream = stream_timer->substream;
21462306a36Sopenharmony_ci	struct rpmsg_info *info = stream_timer->info;
21562306a36Sopenharmony_ci	struct rpmsg_msg *msg;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
21862306a36Sopenharmony_ci		msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM];
21962306a36Sopenharmony_ci		msg->s_msg.header.cmd = TX_PERIOD_DONE;
22062306a36Sopenharmony_ci	} else {
22162306a36Sopenharmony_ci		msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM];
22262306a36Sopenharmony_ci		msg->s_msg.header.cmd = RX_PERIOD_DONE;
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	imx_rpmsg_insert_workqueue(substream, msg, info);
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic int imx_rpmsg_pcm_open(struct snd_soc_component *component,
22962306a36Sopenharmony_ci			      struct snd_pcm_substream *substream)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	struct rpmsg_info *info = dev_get_drvdata(component->dev);
23262306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
23362306a36Sopenharmony_ci	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
23462306a36Sopenharmony_ci	struct fsl_rpmsg *rpmsg = dev_get_drvdata(cpu_dai->dev);
23562306a36Sopenharmony_ci	struct snd_pcm_hardware pcm_hardware;
23662306a36Sopenharmony_ci	struct rpmsg_msg *msg;
23762306a36Sopenharmony_ci	int ret = 0;
23862306a36Sopenharmony_ci	int cmd;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
24162306a36Sopenharmony_ci		msg = &info->msg[TX_OPEN];
24262306a36Sopenharmony_ci		msg->s_msg.header.cmd = TX_OPEN;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci		/* reinitialize buffer counter*/
24562306a36Sopenharmony_ci		cmd = TX_PERIOD_DONE + MSG_TYPE_A_NUM;
24662306a36Sopenharmony_ci		info->msg[cmd].s_msg.param.buffer_tail = 0;
24762306a36Sopenharmony_ci		info->msg[cmd].r_msg.param.buffer_tail = 0;
24862306a36Sopenharmony_ci		info->msg[TX_POINTER].r_msg.param.buffer_offset = 0;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	} else {
25162306a36Sopenharmony_ci		msg = &info->msg[RX_OPEN];
25262306a36Sopenharmony_ci		msg->s_msg.header.cmd = RX_OPEN;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci		/* reinitialize buffer counter*/
25562306a36Sopenharmony_ci		cmd = RX_PERIOD_DONE + MSG_TYPE_A_NUM;
25662306a36Sopenharmony_ci		info->msg[cmd].s_msg.param.buffer_tail = 0;
25762306a36Sopenharmony_ci		info->msg[cmd].r_msg.param.buffer_tail = 0;
25862306a36Sopenharmony_ci		info->msg[RX_POINTER].r_msg.param.buffer_offset = 0;
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	info->send_message(msg, info);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	pcm_hardware = imx_rpmsg_pcm_hardware;
26462306a36Sopenharmony_ci	pcm_hardware.buffer_bytes_max = rpmsg->buffer_size;
26562306a36Sopenharmony_ci	pcm_hardware.period_bytes_max = pcm_hardware.buffer_bytes_max / 2;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	snd_soc_set_runtime_hwparams(substream, &pcm_hardware);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	ret = snd_pcm_hw_constraint_integer(substream->runtime,
27062306a36Sopenharmony_ci					    SNDRV_PCM_HW_PARAM_PERIODS);
27162306a36Sopenharmony_ci	if (ret < 0)
27262306a36Sopenharmony_ci		return ret;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	info->msg_drop_count[substream->stream] = 0;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	/* Create timer*/
27762306a36Sopenharmony_ci	info->stream_timer[substream->stream].info = info;
27862306a36Sopenharmony_ci	info->stream_timer[substream->stream].substream = substream;
27962306a36Sopenharmony_ci	timer_setup(&info->stream_timer[substream->stream].timer,
28062306a36Sopenharmony_ci		    imx_rpmsg_timer_callback, 0);
28162306a36Sopenharmony_ci	return ret;
28262306a36Sopenharmony_ci}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_cistatic int imx_rpmsg_pcm_close(struct snd_soc_component *component,
28562306a36Sopenharmony_ci			       struct snd_pcm_substream *substream)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
28862306a36Sopenharmony_ci	struct rpmsg_info *info = dev_get_drvdata(component->dev);
28962306a36Sopenharmony_ci	struct rpmsg_msg *msg;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	/* Flush work in workqueue to make TX_CLOSE is the last message */
29262306a36Sopenharmony_ci	flush_workqueue(info->rpmsg_wq);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
29562306a36Sopenharmony_ci		msg = &info->msg[TX_CLOSE];
29662306a36Sopenharmony_ci		msg->s_msg.header.cmd = TX_CLOSE;
29762306a36Sopenharmony_ci	} else {
29862306a36Sopenharmony_ci		msg = &info->msg[RX_CLOSE];
29962306a36Sopenharmony_ci		msg->s_msg.header.cmd = RX_CLOSE;
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	info->send_message(msg, info);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	del_timer(&info->stream_timer[substream->stream].timer);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	rtd->dai_link->ignore_suspend = 0;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	if (info->msg_drop_count[substream->stream])
30962306a36Sopenharmony_ci		dev_warn(rtd->dev, "Msg is dropped!, number is %d\n",
31062306a36Sopenharmony_ci			 info->msg_drop_count[substream->stream]);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	return 0;
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic int imx_rpmsg_pcm_prepare(struct snd_soc_component *component,
31662306a36Sopenharmony_ci				 struct snd_pcm_substream *substream)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
31962306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = substream->private_data;
32062306a36Sopenharmony_ci	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
32162306a36Sopenharmony_ci	struct fsl_rpmsg *rpmsg = dev_get_drvdata(cpu_dai->dev);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	/*
32462306a36Sopenharmony_ci	 * NON-MMAP mode, NONBLOCK, Version 2, enable lpa in dts
32562306a36Sopenharmony_ci	 * four conditions to determine the lpa is enabled.
32662306a36Sopenharmony_ci	 */
32762306a36Sopenharmony_ci	if ((runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ||
32862306a36Sopenharmony_ci	     runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) &&
32962306a36Sopenharmony_ci	     rpmsg->enable_lpa) {
33062306a36Sopenharmony_ci		/*
33162306a36Sopenharmony_ci		 * Ignore suspend operation in low power mode
33262306a36Sopenharmony_ci		 * M core will continue playback music on A core suspend.
33362306a36Sopenharmony_ci		 */
33462306a36Sopenharmony_ci		rtd->dai_link->ignore_suspend = 1;
33562306a36Sopenharmony_ci		rpmsg->force_lpa = 1;
33662306a36Sopenharmony_ci	} else {
33762306a36Sopenharmony_ci		rpmsg->force_lpa = 0;
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	return 0;
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic void imx_rpmsg_pcm_dma_complete(void *arg)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	struct snd_pcm_substream *substream = arg;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	snd_pcm_period_elapsed(substream);
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic int imx_rpmsg_prepare_and_submit(struct snd_soc_component *component,
35162306a36Sopenharmony_ci					struct snd_pcm_substream *substream)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	struct rpmsg_info *info = dev_get_drvdata(component->dev);
35462306a36Sopenharmony_ci	struct rpmsg_msg *msg;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
35762306a36Sopenharmony_ci		msg = &info->msg[TX_BUFFER];
35862306a36Sopenharmony_ci		msg->s_msg.header.cmd = TX_BUFFER;
35962306a36Sopenharmony_ci	} else {
36062306a36Sopenharmony_ci		msg = &info->msg[RX_BUFFER];
36162306a36Sopenharmony_ci		msg->s_msg.header.cmd = RX_BUFFER;
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	/* Send buffer address and buffer size */
36562306a36Sopenharmony_ci	msg->s_msg.param.buffer_addr = substream->runtime->dma_addr;
36662306a36Sopenharmony_ci	msg->s_msg.param.buffer_size = snd_pcm_lib_buffer_bytes(substream);
36762306a36Sopenharmony_ci	msg->s_msg.param.period_size = snd_pcm_lib_period_bytes(substream);
36862306a36Sopenharmony_ci	msg->s_msg.param.buffer_tail = 0;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	info->num_period[substream->stream] = msg->s_msg.param.buffer_size /
37162306a36Sopenharmony_ci					      msg->s_msg.param.period_size;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	info->callback[substream->stream] = imx_rpmsg_pcm_dma_complete;
37462306a36Sopenharmony_ci	info->callback_param[substream->stream] = substream;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	return imx_rpmsg_insert_workqueue(substream, msg, info);
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic int imx_rpmsg_async_issue_pending(struct snd_soc_component *component,
38062306a36Sopenharmony_ci					 struct snd_pcm_substream *substream)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	struct rpmsg_info *info = dev_get_drvdata(component->dev);
38362306a36Sopenharmony_ci	struct rpmsg_msg *msg;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
38662306a36Sopenharmony_ci		msg = &info->msg[TX_START];
38762306a36Sopenharmony_ci		msg->s_msg.header.cmd = TX_START;
38862306a36Sopenharmony_ci	} else {
38962306a36Sopenharmony_ci		msg = &info->msg[RX_START];
39062306a36Sopenharmony_ci		msg->s_msg.header.cmd = RX_START;
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	return imx_rpmsg_insert_workqueue(substream, msg, info);
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_cistatic int imx_rpmsg_restart(struct snd_soc_component *component,
39762306a36Sopenharmony_ci			     struct snd_pcm_substream *substream)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	struct rpmsg_info *info = dev_get_drvdata(component->dev);
40062306a36Sopenharmony_ci	struct rpmsg_msg *msg;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
40362306a36Sopenharmony_ci		msg = &info->msg[TX_RESTART];
40462306a36Sopenharmony_ci		msg->s_msg.header.cmd = TX_RESTART;
40562306a36Sopenharmony_ci	} else {
40662306a36Sopenharmony_ci		msg = &info->msg[RX_RESTART];
40762306a36Sopenharmony_ci		msg->s_msg.header.cmd = RX_RESTART;
40862306a36Sopenharmony_ci	}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	return imx_rpmsg_insert_workqueue(substream, msg, info);
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic int imx_rpmsg_pause(struct snd_soc_component *component,
41462306a36Sopenharmony_ci			   struct snd_pcm_substream *substream)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	struct rpmsg_info *info = dev_get_drvdata(component->dev);
41762306a36Sopenharmony_ci	struct rpmsg_msg *msg;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
42062306a36Sopenharmony_ci		msg = &info->msg[TX_PAUSE];
42162306a36Sopenharmony_ci		msg->s_msg.header.cmd = TX_PAUSE;
42262306a36Sopenharmony_ci	} else {
42362306a36Sopenharmony_ci		msg = &info->msg[RX_PAUSE];
42462306a36Sopenharmony_ci		msg->s_msg.header.cmd = RX_PAUSE;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	return imx_rpmsg_insert_workqueue(substream, msg, info);
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_cistatic int imx_rpmsg_terminate_all(struct snd_soc_component *component,
43162306a36Sopenharmony_ci				   struct snd_pcm_substream *substream)
43262306a36Sopenharmony_ci{
43362306a36Sopenharmony_ci	struct rpmsg_info *info = dev_get_drvdata(component->dev);
43462306a36Sopenharmony_ci	struct rpmsg_msg *msg;
43562306a36Sopenharmony_ci	int cmd;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
43862306a36Sopenharmony_ci		msg = &info->msg[TX_TERMINATE];
43962306a36Sopenharmony_ci		msg->s_msg.header.cmd = TX_TERMINATE;
44062306a36Sopenharmony_ci		/* Clear buffer count*/
44162306a36Sopenharmony_ci		cmd = TX_PERIOD_DONE + MSG_TYPE_A_NUM;
44262306a36Sopenharmony_ci		info->msg[cmd].s_msg.param.buffer_tail = 0;
44362306a36Sopenharmony_ci		info->msg[cmd].r_msg.param.buffer_tail = 0;
44462306a36Sopenharmony_ci		info->msg[TX_POINTER].r_msg.param.buffer_offset = 0;
44562306a36Sopenharmony_ci	} else {
44662306a36Sopenharmony_ci		msg = &info->msg[RX_TERMINATE];
44762306a36Sopenharmony_ci		msg->s_msg.header.cmd = RX_TERMINATE;
44862306a36Sopenharmony_ci		/* Clear buffer count*/
44962306a36Sopenharmony_ci		cmd = RX_PERIOD_DONE + MSG_TYPE_A_NUM;
45062306a36Sopenharmony_ci		info->msg[cmd].s_msg.param.buffer_tail = 0;
45162306a36Sopenharmony_ci		info->msg[cmd].r_msg.param.buffer_tail = 0;
45262306a36Sopenharmony_ci		info->msg[RX_POINTER].r_msg.param.buffer_offset = 0;
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	del_timer(&info->stream_timer[substream->stream].timer);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	return imx_rpmsg_insert_workqueue(substream, msg, info);
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_cistatic int imx_rpmsg_pcm_trigger(struct snd_soc_component *component,
46162306a36Sopenharmony_ci				 struct snd_pcm_substream *substream, int cmd)
46262306a36Sopenharmony_ci{
46362306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
46462306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = substream->private_data;
46562306a36Sopenharmony_ci	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
46662306a36Sopenharmony_ci	struct fsl_rpmsg *rpmsg = dev_get_drvdata(cpu_dai->dev);
46762306a36Sopenharmony_ci	int ret = 0;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	switch (cmd) {
47062306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
47162306a36Sopenharmony_ci		ret = imx_rpmsg_prepare_and_submit(component, substream);
47262306a36Sopenharmony_ci		if (ret)
47362306a36Sopenharmony_ci			return ret;
47462306a36Sopenharmony_ci		ret = imx_rpmsg_async_issue_pending(component, substream);
47562306a36Sopenharmony_ci		break;
47662306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
47762306a36Sopenharmony_ci		if (rpmsg->force_lpa)
47862306a36Sopenharmony_ci			break;
47962306a36Sopenharmony_ci		fallthrough;
48062306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
48162306a36Sopenharmony_ci		ret = imx_rpmsg_restart(component, substream);
48262306a36Sopenharmony_ci		break;
48362306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
48462306a36Sopenharmony_ci		if (!rpmsg->force_lpa) {
48562306a36Sopenharmony_ci			if (runtime->info & SNDRV_PCM_INFO_PAUSE)
48662306a36Sopenharmony_ci				ret = imx_rpmsg_pause(component, substream);
48762306a36Sopenharmony_ci			else
48862306a36Sopenharmony_ci				ret = imx_rpmsg_terminate_all(component, substream);
48962306a36Sopenharmony_ci		}
49062306a36Sopenharmony_ci		break;
49162306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
49262306a36Sopenharmony_ci		ret = imx_rpmsg_pause(component, substream);
49362306a36Sopenharmony_ci		break;
49462306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
49562306a36Sopenharmony_ci		ret = imx_rpmsg_terminate_all(component, substream);
49662306a36Sopenharmony_ci		break;
49762306a36Sopenharmony_ci	default:
49862306a36Sopenharmony_ci		return -EINVAL;
49962306a36Sopenharmony_ci	}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	if (ret)
50262306a36Sopenharmony_ci		return ret;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	return 0;
50562306a36Sopenharmony_ci}
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci/*
50862306a36Sopenharmony_ci * imx_rpmsg_pcm_ack
50962306a36Sopenharmony_ci *
51062306a36Sopenharmony_ci * Send the period index to M core through rpmsg, but not send
51162306a36Sopenharmony_ci * all the period index to M core, reduce some unnessesary msg
51262306a36Sopenharmony_ci * to reduce the pressure of rpmsg bandwidth.
51362306a36Sopenharmony_ci */
51462306a36Sopenharmony_cistatic int imx_rpmsg_pcm_ack(struct snd_soc_component *component,
51562306a36Sopenharmony_ci			     struct snd_pcm_substream *substream)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
51862306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = substream->private_data;
51962306a36Sopenharmony_ci	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
52062306a36Sopenharmony_ci	struct fsl_rpmsg *rpmsg = dev_get_drvdata(cpu_dai->dev);
52162306a36Sopenharmony_ci	struct rpmsg_info *info = dev_get_drvdata(component->dev);
52262306a36Sopenharmony_ci	snd_pcm_uframes_t period_size = runtime->period_size;
52362306a36Sopenharmony_ci	snd_pcm_sframes_t avail;
52462306a36Sopenharmony_ci	struct timer_list *timer;
52562306a36Sopenharmony_ci	struct rpmsg_msg *msg;
52662306a36Sopenharmony_ci	unsigned long flags;
52762306a36Sopenharmony_ci	int buffer_tail = 0;
52862306a36Sopenharmony_ci	int written_num;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	if (!rpmsg->force_lpa)
53162306a36Sopenharmony_ci		return 0;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
53462306a36Sopenharmony_ci		msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM];
53562306a36Sopenharmony_ci		msg->s_msg.header.cmd = TX_PERIOD_DONE;
53662306a36Sopenharmony_ci	} else {
53762306a36Sopenharmony_ci		msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM];
53862306a36Sopenharmony_ci		msg->s_msg.header.cmd = RX_PERIOD_DONE;
53962306a36Sopenharmony_ci	}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	msg->s_msg.header.type = MSG_TYPE_C;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	buffer_tail = (frames_to_bytes(runtime, runtime->control->appl_ptr) %
54462306a36Sopenharmony_ci		       snd_pcm_lib_buffer_bytes(substream));
54562306a36Sopenharmony_ci	buffer_tail = buffer_tail / snd_pcm_lib_period_bytes(substream);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	/* There is update for period index */
54862306a36Sopenharmony_ci	if (buffer_tail != msg->s_msg.param.buffer_tail) {
54962306a36Sopenharmony_ci		written_num = buffer_tail - msg->s_msg.param.buffer_tail;
55062306a36Sopenharmony_ci		if (written_num < 0)
55162306a36Sopenharmony_ci			written_num += runtime->periods;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci		msg->s_msg.param.buffer_tail = buffer_tail;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci		/* The notification message is updated to latest */
55662306a36Sopenharmony_ci		spin_lock_irqsave(&info->lock[substream->stream], flags);
55762306a36Sopenharmony_ci		memcpy(&info->notify[substream->stream], msg,
55862306a36Sopenharmony_ci		       sizeof(struct rpmsg_s_msg));
55962306a36Sopenharmony_ci		info->notify_updated[substream->stream] = true;
56062306a36Sopenharmony_ci		spin_unlock_irqrestore(&info->lock[substream->stream], flags);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
56362306a36Sopenharmony_ci			avail = snd_pcm_playback_hw_avail(runtime);
56462306a36Sopenharmony_ci		else
56562306a36Sopenharmony_ci			avail = snd_pcm_capture_hw_avail(runtime);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci		timer = &info->stream_timer[substream->stream].timer;
56862306a36Sopenharmony_ci		/*
56962306a36Sopenharmony_ci		 * If the data in the buffer is less than one period before
57062306a36Sopenharmony_ci		 * this fill, which means the data may not enough on M
57162306a36Sopenharmony_ci		 * core side, we need to send message immediately to let
57262306a36Sopenharmony_ci		 * M core know the pointer is updated.
57362306a36Sopenharmony_ci		 * if there is more than one period data in the buffer before
57462306a36Sopenharmony_ci		 * this fill, which means the data is enough on M core side,
57562306a36Sopenharmony_ci		 * we can delay one period (using timer) to send the message
57662306a36Sopenharmony_ci		 * for reduce the message number in workqueue, because the
57762306a36Sopenharmony_ci		 * pointer may be updated by ack function later, we can
57862306a36Sopenharmony_ci		 * send latest pointer to M core side.
57962306a36Sopenharmony_ci		 */
58062306a36Sopenharmony_ci		if ((avail - written_num * period_size) <= period_size) {
58162306a36Sopenharmony_ci			imx_rpmsg_insert_workqueue(substream, msg, info);
58262306a36Sopenharmony_ci		} else if (rpmsg->force_lpa && !timer_pending(timer)) {
58362306a36Sopenharmony_ci			int time_msec;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci			time_msec = (int)(runtime->period_size * 1000 / runtime->rate);
58662306a36Sopenharmony_ci			mod_timer(timer, jiffies + msecs_to_jiffies(time_msec));
58762306a36Sopenharmony_ci		}
58862306a36Sopenharmony_ci	}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	return 0;
59162306a36Sopenharmony_ci}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_cistatic int imx_rpmsg_pcm_new(struct snd_soc_component *component,
59462306a36Sopenharmony_ci			     struct snd_soc_pcm_runtime *rtd)
59562306a36Sopenharmony_ci{
59662306a36Sopenharmony_ci	struct snd_card *card = rtd->card->snd_card;
59762306a36Sopenharmony_ci	struct snd_pcm *pcm = rtd->pcm;
59862306a36Sopenharmony_ci	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
59962306a36Sopenharmony_ci	struct fsl_rpmsg *rpmsg = dev_get_drvdata(cpu_dai->dev);
60062306a36Sopenharmony_ci	int ret;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
60362306a36Sopenharmony_ci	if (ret)
60462306a36Sopenharmony_ci		return ret;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_WC,
60762306a36Sopenharmony_ci					    pcm->card->dev, rpmsg->buffer_size);
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_cistatic const struct snd_soc_component_driver imx_rpmsg_soc_component = {
61162306a36Sopenharmony_ci	.name		= IMX_PCM_DRV_NAME,
61262306a36Sopenharmony_ci	.pcm_construct	= imx_rpmsg_pcm_new,
61362306a36Sopenharmony_ci	.open		= imx_rpmsg_pcm_open,
61462306a36Sopenharmony_ci	.close		= imx_rpmsg_pcm_close,
61562306a36Sopenharmony_ci	.hw_params	= imx_rpmsg_pcm_hw_params,
61662306a36Sopenharmony_ci	.trigger	= imx_rpmsg_pcm_trigger,
61762306a36Sopenharmony_ci	.pointer	= imx_rpmsg_pcm_pointer,
61862306a36Sopenharmony_ci	.ack		= imx_rpmsg_pcm_ack,
61962306a36Sopenharmony_ci	.prepare	= imx_rpmsg_pcm_prepare,
62062306a36Sopenharmony_ci};
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_cistatic void imx_rpmsg_pcm_work(struct work_struct *work)
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci	struct work_of_rpmsg *work_of_rpmsg;
62562306a36Sopenharmony_ci	bool is_notification = false;
62662306a36Sopenharmony_ci	struct rpmsg_info *info;
62762306a36Sopenharmony_ci	struct rpmsg_msg msg;
62862306a36Sopenharmony_ci	unsigned long flags;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	work_of_rpmsg = container_of(work, struct work_of_rpmsg, work);
63162306a36Sopenharmony_ci	info = work_of_rpmsg->info;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	/*
63462306a36Sopenharmony_ci	 * Every work in the work queue, first we check if there
63562306a36Sopenharmony_ci	 * is update for period is filled, because there may be not
63662306a36Sopenharmony_ci	 * enough data in M core side, need to let M core know
63762306a36Sopenharmony_ci	 * data is updated immediately.
63862306a36Sopenharmony_ci	 */
63962306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock[TX], flags);
64062306a36Sopenharmony_ci	if (info->notify_updated[TX]) {
64162306a36Sopenharmony_ci		memcpy(&msg, &info->notify[TX], sizeof(struct rpmsg_s_msg));
64262306a36Sopenharmony_ci		info->notify_updated[TX] = false;
64362306a36Sopenharmony_ci		spin_unlock_irqrestore(&info->lock[TX], flags);
64462306a36Sopenharmony_ci		info->send_message(&msg, info);
64562306a36Sopenharmony_ci	} else {
64662306a36Sopenharmony_ci		spin_unlock_irqrestore(&info->lock[TX], flags);
64762306a36Sopenharmony_ci	}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock[RX], flags);
65062306a36Sopenharmony_ci	if (info->notify_updated[RX]) {
65162306a36Sopenharmony_ci		memcpy(&msg, &info->notify[RX], sizeof(struct rpmsg_s_msg));
65262306a36Sopenharmony_ci		info->notify_updated[RX] = false;
65362306a36Sopenharmony_ci		spin_unlock_irqrestore(&info->lock[RX], flags);
65462306a36Sopenharmony_ci		info->send_message(&msg, info);
65562306a36Sopenharmony_ci	} else {
65662306a36Sopenharmony_ci		spin_unlock_irqrestore(&info->lock[RX], flags);
65762306a36Sopenharmony_ci	}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	/* Skip the notification message for it has been processed above */
66062306a36Sopenharmony_ci	if (work_of_rpmsg->msg.s_msg.header.type == MSG_TYPE_C &&
66162306a36Sopenharmony_ci	    (work_of_rpmsg->msg.s_msg.header.cmd == TX_PERIOD_DONE ||
66262306a36Sopenharmony_ci	     work_of_rpmsg->msg.s_msg.header.cmd == RX_PERIOD_DONE))
66362306a36Sopenharmony_ci		is_notification = true;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	if (!is_notification)
66662306a36Sopenharmony_ci		info->send_message(&work_of_rpmsg->msg, info);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	/* update read index */
66962306a36Sopenharmony_ci	spin_lock_irqsave(&info->wq_lock, flags);
67062306a36Sopenharmony_ci	info->work_read_index++;
67162306a36Sopenharmony_ci	info->work_read_index %= WORK_MAX_NUM;
67262306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->wq_lock, flags);
67362306a36Sopenharmony_ci}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_cistatic int imx_rpmsg_pcm_probe(struct platform_device *pdev)
67662306a36Sopenharmony_ci{
67762306a36Sopenharmony_ci	struct snd_soc_component *component;
67862306a36Sopenharmony_ci	struct rpmsg_info *info;
67962306a36Sopenharmony_ci	int ret, i;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
68262306a36Sopenharmony_ci	if (!info)
68362306a36Sopenharmony_ci		return -ENOMEM;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	platform_set_drvdata(pdev, info);
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	info->rpdev = container_of(pdev->dev.parent, struct rpmsg_device, dev);
68862306a36Sopenharmony_ci	info->dev = &pdev->dev;
68962306a36Sopenharmony_ci	/* Setup work queue */
69062306a36Sopenharmony_ci	info->rpmsg_wq = alloc_ordered_workqueue(info->rpdev->id.name,
69162306a36Sopenharmony_ci						 WQ_HIGHPRI |
69262306a36Sopenharmony_ci						 WQ_UNBOUND |
69362306a36Sopenharmony_ci						 WQ_FREEZABLE);
69462306a36Sopenharmony_ci	if (!info->rpmsg_wq) {
69562306a36Sopenharmony_ci		dev_err(&pdev->dev, "workqueue create failed\n");
69662306a36Sopenharmony_ci		return -ENOMEM;
69762306a36Sopenharmony_ci	}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	/* Write index initialize 1, make it differ with the read index */
70062306a36Sopenharmony_ci	info->work_write_index = 1;
70162306a36Sopenharmony_ci	info->send_message = imx_rpmsg_pcm_send_message;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	for (i = 0; i < WORK_MAX_NUM; i++) {
70462306a36Sopenharmony_ci		INIT_WORK(&info->work_list[i].work, imx_rpmsg_pcm_work);
70562306a36Sopenharmony_ci		info->work_list[i].info = info;
70662306a36Sopenharmony_ci	}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	/* Initialize msg */
70962306a36Sopenharmony_ci	for (i = 0; i < MSG_MAX_NUM; i++) {
71062306a36Sopenharmony_ci		info->msg[i].s_msg.header.cate  = IMX_RPMSG_AUDIO;
71162306a36Sopenharmony_ci		info->msg[i].s_msg.header.major = IMX_RMPSG_MAJOR;
71262306a36Sopenharmony_ci		info->msg[i].s_msg.header.minor = IMX_RMPSG_MINOR;
71362306a36Sopenharmony_ci		info->msg[i].s_msg.header.type  = MSG_TYPE_A;
71462306a36Sopenharmony_ci		info->msg[i].s_msg.param.audioindex = 0;
71562306a36Sopenharmony_ci	}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	init_completion(&info->cmd_complete);
71862306a36Sopenharmony_ci	mutex_init(&info->msg_lock);
71962306a36Sopenharmony_ci	spin_lock_init(&info->lock[TX]);
72062306a36Sopenharmony_ci	spin_lock_init(&info->lock[RX]);
72162306a36Sopenharmony_ci	spin_lock_init(&info->wq_lock);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	ret = devm_snd_soc_register_component(&pdev->dev,
72462306a36Sopenharmony_ci					      &imx_rpmsg_soc_component,
72562306a36Sopenharmony_ci					      NULL, 0);
72662306a36Sopenharmony_ci	if (ret)
72762306a36Sopenharmony_ci		goto fail;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	component = snd_soc_lookup_component(&pdev->dev, NULL);
73062306a36Sopenharmony_ci	if (!component) {
73162306a36Sopenharmony_ci		ret = -EINVAL;
73262306a36Sopenharmony_ci		goto fail;
73362306a36Sopenharmony_ci	}
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	/* platform component name is used by machine driver to link with */
73662306a36Sopenharmony_ci	component->name = info->rpdev->id.name;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
73962306a36Sopenharmony_ci	component->debugfs_prefix = "rpmsg";
74062306a36Sopenharmony_ci#endif
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	return 0;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_cifail:
74562306a36Sopenharmony_ci	if (info->rpmsg_wq)
74662306a36Sopenharmony_ci		destroy_workqueue(info->rpmsg_wq);
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	return ret;
74962306a36Sopenharmony_ci}
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_cistatic void imx_rpmsg_pcm_remove(struct platform_device *pdev)
75262306a36Sopenharmony_ci{
75362306a36Sopenharmony_ci	struct rpmsg_info *info = platform_get_drvdata(pdev);
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	if (info->rpmsg_wq)
75662306a36Sopenharmony_ci		destroy_workqueue(info->rpmsg_wq);
75762306a36Sopenharmony_ci}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci#ifdef CONFIG_PM
76062306a36Sopenharmony_cistatic int imx_rpmsg_pcm_runtime_resume(struct device *dev)
76162306a36Sopenharmony_ci{
76262306a36Sopenharmony_ci	struct rpmsg_info *info = dev_get_drvdata(dev);
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	cpu_latency_qos_add_request(&info->pm_qos_req, 0);
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	return 0;
76762306a36Sopenharmony_ci}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_cistatic int imx_rpmsg_pcm_runtime_suspend(struct device *dev)
77062306a36Sopenharmony_ci{
77162306a36Sopenharmony_ci	struct rpmsg_info *info = dev_get_drvdata(dev);
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	cpu_latency_qos_remove_request(&info->pm_qos_req);
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	return 0;
77662306a36Sopenharmony_ci}
77762306a36Sopenharmony_ci#endif
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
78062306a36Sopenharmony_cistatic int imx_rpmsg_pcm_suspend(struct device *dev)
78162306a36Sopenharmony_ci{
78262306a36Sopenharmony_ci	struct rpmsg_info *info = dev_get_drvdata(dev);
78362306a36Sopenharmony_ci	struct rpmsg_msg *rpmsg_tx;
78462306a36Sopenharmony_ci	struct rpmsg_msg *rpmsg_rx;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	rpmsg_tx = &info->msg[TX_SUSPEND];
78762306a36Sopenharmony_ci	rpmsg_rx = &info->msg[RX_SUSPEND];
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	rpmsg_tx->s_msg.header.cmd = TX_SUSPEND;
79062306a36Sopenharmony_ci	info->send_message(rpmsg_tx, info);
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	rpmsg_rx->s_msg.header.cmd = RX_SUSPEND;
79362306a36Sopenharmony_ci	info->send_message(rpmsg_rx, info);
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	return 0;
79662306a36Sopenharmony_ci}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_cistatic int imx_rpmsg_pcm_resume(struct device *dev)
79962306a36Sopenharmony_ci{
80062306a36Sopenharmony_ci	struct rpmsg_info *info = dev_get_drvdata(dev);
80162306a36Sopenharmony_ci	struct rpmsg_msg *rpmsg_tx;
80262306a36Sopenharmony_ci	struct rpmsg_msg *rpmsg_rx;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	rpmsg_tx = &info->msg[TX_RESUME];
80562306a36Sopenharmony_ci	rpmsg_rx = &info->msg[RX_RESUME];
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	rpmsg_tx->s_msg.header.cmd = TX_RESUME;
80862306a36Sopenharmony_ci	info->send_message(rpmsg_tx, info);
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	rpmsg_rx->s_msg.header.cmd = RX_RESUME;
81162306a36Sopenharmony_ci	info->send_message(rpmsg_rx, info);
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	return 0;
81462306a36Sopenharmony_ci}
81562306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_cistatic const struct dev_pm_ops imx_rpmsg_pcm_pm_ops = {
81862306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(imx_rpmsg_pcm_runtime_suspend,
81962306a36Sopenharmony_ci			   imx_rpmsg_pcm_runtime_resume,
82062306a36Sopenharmony_ci			   NULL)
82162306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(imx_rpmsg_pcm_suspend,
82262306a36Sopenharmony_ci				imx_rpmsg_pcm_resume)
82362306a36Sopenharmony_ci};
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_cistatic struct platform_driver imx_pcm_rpmsg_driver = {
82662306a36Sopenharmony_ci	.probe  = imx_rpmsg_pcm_probe,
82762306a36Sopenharmony_ci	.remove_new = imx_rpmsg_pcm_remove,
82862306a36Sopenharmony_ci	.driver = {
82962306a36Sopenharmony_ci		.name = IMX_PCM_DRV_NAME,
83062306a36Sopenharmony_ci		.pm = &imx_rpmsg_pcm_pm_ops,
83162306a36Sopenharmony_ci	},
83262306a36Sopenharmony_ci};
83362306a36Sopenharmony_cimodule_platform_driver(imx_pcm_rpmsg_driver);
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ciMODULE_DESCRIPTION("Freescale SoC Audio RPMSG PCM interface");
83662306a36Sopenharmony_ciMODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
83762306a36Sopenharmony_ciMODULE_ALIAS("platform:" IMX_PCM_DRV_NAME);
83862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
839