xref: /linux/sound/soc/fsl/imx-pcm-rpmsg.c (revision ed901560)
13c00ecebSShengjiu Wang // SPDX-License-Identifier: GPL-2.0+
23c00ecebSShengjiu Wang // Copyright 2017-2021 NXP
33c00ecebSShengjiu Wang 
43c00ecebSShengjiu Wang #include <linux/dma-mapping.h>
53c00ecebSShengjiu Wang #include <linux/slab.h>
63c00ecebSShengjiu Wang #include <linux/module.h>
73c00ecebSShengjiu Wang #include <linux/delay.h>
83c00ecebSShengjiu Wang #include <linux/rpmsg.h>
93c00ecebSShengjiu Wang #include <sound/core.h>
103c00ecebSShengjiu Wang #include <sound/pcm.h>
113c00ecebSShengjiu Wang #include <sound/pcm_params.h>
123c00ecebSShengjiu Wang #include <sound/dmaengine_pcm.h>
133c00ecebSShengjiu Wang #include <sound/soc.h>
143c00ecebSShengjiu Wang 
153c00ecebSShengjiu Wang #include "imx-pcm.h"
163c00ecebSShengjiu Wang #include "fsl_rpmsg.h"
173c00ecebSShengjiu Wang #include "imx-pcm-rpmsg.h"
183c00ecebSShengjiu Wang 
19*ed901560SKrzysztof Kozlowski static const struct snd_pcm_hardware imx_rpmsg_pcm_hardware = {
203c00ecebSShengjiu Wang 	.info = SNDRV_PCM_INFO_INTERLEAVED |
213c00ecebSShengjiu Wang 		SNDRV_PCM_INFO_BLOCK_TRANSFER |
222f942690SShengjiu Wang 		SNDRV_PCM_INFO_BATCH |
233c00ecebSShengjiu Wang 		SNDRV_PCM_INFO_MMAP |
243c00ecebSShengjiu Wang 		SNDRV_PCM_INFO_MMAP_VALID |
253c00ecebSShengjiu Wang 		SNDRV_PCM_INFO_NO_PERIOD_WAKEUP |
263c00ecebSShengjiu Wang 		SNDRV_PCM_INFO_PAUSE |
273c00ecebSShengjiu Wang 		SNDRV_PCM_INFO_RESUME,
283c00ecebSShengjiu Wang 	.buffer_bytes_max = IMX_DEFAULT_DMABUF_SIZE,
293c00ecebSShengjiu Wang 	.period_bytes_min = 512,
303c00ecebSShengjiu Wang 	.period_bytes_max = 65536,
313c00ecebSShengjiu Wang 	.periods_min = 2,
323c00ecebSShengjiu Wang 	.periods_max = 6000,
333c00ecebSShengjiu Wang 	.fifo_size = 0,
343c00ecebSShengjiu Wang };
353c00ecebSShengjiu Wang 
imx_rpmsg_pcm_send_message(struct rpmsg_msg * msg,struct rpmsg_info * info)363c00ecebSShengjiu Wang static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg,
373c00ecebSShengjiu Wang 				      struct rpmsg_info *info)
383c00ecebSShengjiu Wang {
393c00ecebSShengjiu Wang 	struct rpmsg_device *rpdev = info->rpdev;
403c00ecebSShengjiu Wang 	int ret = 0;
413c00ecebSShengjiu Wang 
423c00ecebSShengjiu Wang 	mutex_lock(&info->msg_lock);
433c00ecebSShengjiu Wang 	if (!rpdev) {
443c00ecebSShengjiu Wang 		dev_err(info->dev, "rpmsg channel not ready\n");
453c00ecebSShengjiu Wang 		mutex_unlock(&info->msg_lock);
463c00ecebSShengjiu Wang 		return -EINVAL;
473c00ecebSShengjiu Wang 	}
483c00ecebSShengjiu Wang 
493c00ecebSShengjiu Wang 	dev_dbg(&rpdev->dev, "send cmd %d\n", msg->s_msg.header.cmd);
503c00ecebSShengjiu Wang 
513c00ecebSShengjiu Wang 	if (!(msg->s_msg.header.type == MSG_TYPE_C))
523c00ecebSShengjiu Wang 		reinit_completion(&info->cmd_complete);
533c00ecebSShengjiu Wang 
543c00ecebSShengjiu Wang 	ret = rpmsg_send(rpdev->ept, (void *)&msg->s_msg,
553c00ecebSShengjiu Wang 			 sizeof(struct rpmsg_s_msg));
563c00ecebSShengjiu Wang 	if (ret) {
573c00ecebSShengjiu Wang 		dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret);
583c00ecebSShengjiu Wang 		mutex_unlock(&info->msg_lock);
593c00ecebSShengjiu Wang 		return ret;
603c00ecebSShengjiu Wang 	}
613c00ecebSShengjiu Wang 
623c00ecebSShengjiu Wang 	/* No receive msg for TYPE_C command */
633c00ecebSShengjiu Wang 	if (msg->s_msg.header.type == MSG_TYPE_C) {
643c00ecebSShengjiu Wang 		mutex_unlock(&info->msg_lock);
653c00ecebSShengjiu Wang 		return 0;
663c00ecebSShengjiu Wang 	}
673c00ecebSShengjiu Wang 
683c00ecebSShengjiu Wang 	/* wait response from rpmsg */
693c00ecebSShengjiu Wang 	ret = wait_for_completion_timeout(&info->cmd_complete,
703c00ecebSShengjiu Wang 					  msecs_to_jiffies(RPMSG_TIMEOUT));
713c00ecebSShengjiu Wang 	if (!ret) {
723c00ecebSShengjiu Wang 		dev_err(&rpdev->dev, "rpmsg_send cmd %d timeout!\n",
733c00ecebSShengjiu Wang 			msg->s_msg.header.cmd);
743c00ecebSShengjiu Wang 		mutex_unlock(&info->msg_lock);
753c00ecebSShengjiu Wang 		return -ETIMEDOUT;
763c00ecebSShengjiu Wang 	}
773c00ecebSShengjiu Wang 
783c00ecebSShengjiu Wang 	memcpy(&msg->r_msg, &info->r_msg, sizeof(struct rpmsg_r_msg));
793c00ecebSShengjiu Wang 	memcpy(&info->msg[msg->r_msg.header.cmd].r_msg,
803c00ecebSShengjiu Wang 	       &msg->r_msg, sizeof(struct rpmsg_r_msg));
813c00ecebSShengjiu Wang 
823c00ecebSShengjiu Wang 	/*
833c00ecebSShengjiu Wang 	 * Reset the buffer pointer to be zero, actully we have
843c00ecebSShengjiu Wang 	 * set the buffer pointer to be zero in imx_rpmsg_terminate_all
853c00ecebSShengjiu Wang 	 * But if there is timer task queued in queue, after it is
863c00ecebSShengjiu Wang 	 * executed the buffer pointer will be changed, so need to
873c00ecebSShengjiu Wang 	 * reset it again with TERMINATE command.
883c00ecebSShengjiu Wang 	 */
893c00ecebSShengjiu Wang 	switch (msg->s_msg.header.cmd) {
903c00ecebSShengjiu Wang 	case TX_TERMINATE:
913c00ecebSShengjiu Wang 		info->msg[TX_POINTER].r_msg.param.buffer_offset = 0;
923c00ecebSShengjiu Wang 		break;
933c00ecebSShengjiu Wang 	case RX_TERMINATE:
943c00ecebSShengjiu Wang 		info->msg[RX_POINTER].r_msg.param.buffer_offset = 0;
953c00ecebSShengjiu Wang 		break;
963c00ecebSShengjiu Wang 	default:
973c00ecebSShengjiu Wang 		break;
983c00ecebSShengjiu Wang 	}
993c00ecebSShengjiu Wang 
1003c00ecebSShengjiu Wang 	dev_dbg(&rpdev->dev, "cmd:%d, resp %d\n", msg->s_msg.header.cmd,
1013c00ecebSShengjiu Wang 		info->r_msg.param.resp);
1023c00ecebSShengjiu Wang 
1033c00ecebSShengjiu Wang 	mutex_unlock(&info->msg_lock);
1043c00ecebSShengjiu Wang 
1053c00ecebSShengjiu Wang 	return 0;
1063c00ecebSShengjiu Wang }
1073c00ecebSShengjiu Wang 
imx_rpmsg_insert_workqueue(struct snd_pcm_substream * substream,struct rpmsg_msg * msg,struct rpmsg_info * info)1083c00ecebSShengjiu Wang static int imx_rpmsg_insert_workqueue(struct snd_pcm_substream *substream,
1093c00ecebSShengjiu Wang 				      struct rpmsg_msg *msg,
1103c00ecebSShengjiu Wang 				      struct rpmsg_info *info)
1113c00ecebSShengjiu Wang {
1123c00ecebSShengjiu Wang 	unsigned long flags;
1133c00ecebSShengjiu Wang 	int ret = 0;
1143c00ecebSShengjiu Wang 
1153c00ecebSShengjiu Wang 	/*
1163c00ecebSShengjiu Wang 	 * Queue the work to workqueue.
1173c00ecebSShengjiu Wang 	 * If the queue is full, drop the message.
1183c00ecebSShengjiu Wang 	 */
1193c00ecebSShengjiu Wang 	spin_lock_irqsave(&info->wq_lock, flags);
1203c00ecebSShengjiu Wang 	if (info->work_write_index != info->work_read_index) {
1213c00ecebSShengjiu Wang 		int index = info->work_write_index;
1223c00ecebSShengjiu Wang 
1233c00ecebSShengjiu Wang 		memcpy(&info->work_list[index].msg, msg,
1243c00ecebSShengjiu Wang 		       sizeof(struct rpmsg_s_msg));
1253c00ecebSShengjiu Wang 
1263c00ecebSShengjiu Wang 		queue_work(info->rpmsg_wq, &info->work_list[index].work);
1273c00ecebSShengjiu Wang 		info->work_write_index++;
1283c00ecebSShengjiu Wang 		info->work_write_index %= WORK_MAX_NUM;
1293c00ecebSShengjiu Wang 	} else {
1303c00ecebSShengjiu Wang 		info->msg_drop_count[substream->stream]++;
1313c00ecebSShengjiu Wang 		ret = -EPIPE;
1323c00ecebSShengjiu Wang 	}
1333c00ecebSShengjiu Wang 	spin_unlock_irqrestore(&info->wq_lock, flags);
1343c00ecebSShengjiu Wang 
1353c00ecebSShengjiu Wang 	return ret;
1363c00ecebSShengjiu Wang }
1373c00ecebSShengjiu Wang 
imx_rpmsg_pcm_hw_params(struct snd_soc_component * component,struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)1383c00ecebSShengjiu Wang static int imx_rpmsg_pcm_hw_params(struct snd_soc_component *component,
1393c00ecebSShengjiu Wang 				   struct snd_pcm_substream *substream,
1403c00ecebSShengjiu Wang 				   struct snd_pcm_hw_params *params)
1413c00ecebSShengjiu Wang {
1423c00ecebSShengjiu Wang 	struct rpmsg_info *info = dev_get_drvdata(component->dev);
1433c00ecebSShengjiu Wang 	struct rpmsg_msg *msg;
1443c00ecebSShengjiu Wang 
1453c00ecebSShengjiu Wang 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
1463c00ecebSShengjiu Wang 		msg = &info->msg[TX_HW_PARAM];
1473c00ecebSShengjiu Wang 		msg->s_msg.header.cmd = TX_HW_PARAM;
1483c00ecebSShengjiu Wang 	} else {
1493c00ecebSShengjiu Wang 		msg = &info->msg[RX_HW_PARAM];
1503c00ecebSShengjiu Wang 		msg->s_msg.header.cmd = RX_HW_PARAM;
1513c00ecebSShengjiu Wang 	}
1523c00ecebSShengjiu Wang 
1533c00ecebSShengjiu Wang 	msg->s_msg.param.rate = params_rate(params);
1543c00ecebSShengjiu Wang 
1553c00ecebSShengjiu Wang 	switch (params_format(params)) {
1563c00ecebSShengjiu Wang 	case SNDRV_PCM_FORMAT_S16_LE:
1573c00ecebSShengjiu Wang 		msg->s_msg.param.format   = RPMSG_S16_LE;
1583c00ecebSShengjiu Wang 		break;
1593c00ecebSShengjiu Wang 	case SNDRV_PCM_FORMAT_S24_LE:
1603c00ecebSShengjiu Wang 		msg->s_msg.param.format   = RPMSG_S24_LE;
1613c00ecebSShengjiu Wang 		break;
1623c00ecebSShengjiu Wang 	case SNDRV_PCM_FORMAT_DSD_U16_LE:
163a387040aSShengjiu Wang 		msg->s_msg.param.format   = RPMSG_DSD_U16_LE;
1643c00ecebSShengjiu Wang 		break;
1653c00ecebSShengjiu Wang 	case SNDRV_PCM_FORMAT_DSD_U32_LE:
166a387040aSShengjiu Wang 		msg->s_msg.param.format   = RPMSG_DSD_U32_LE;
1673c00ecebSShengjiu Wang 		break;
1683c00ecebSShengjiu Wang 	default:
1693c00ecebSShengjiu Wang 		msg->s_msg.param.format   = RPMSG_S32_LE;
1703c00ecebSShengjiu Wang 		break;
1713c00ecebSShengjiu Wang 	}
1723c00ecebSShengjiu Wang 
1733c00ecebSShengjiu Wang 	switch (params_channels(params)) {
1743c00ecebSShengjiu Wang 	case 1:
1753c00ecebSShengjiu Wang 		msg->s_msg.param.channels = RPMSG_CH_LEFT;
1763c00ecebSShengjiu Wang 		break;
1773c00ecebSShengjiu Wang 	case 2:
1783c00ecebSShengjiu Wang 		msg->s_msg.param.channels = RPMSG_CH_STEREO;
1793c00ecebSShengjiu Wang 		break;
1803c00ecebSShengjiu Wang 	default:
181bdc0f6caSChancel Liu 		msg->s_msg.param.channels = params_channels(params);
1823c00ecebSShengjiu Wang 		break;
1833c00ecebSShengjiu Wang 	}
1843c00ecebSShengjiu Wang 
1853c00ecebSShengjiu Wang 	info->send_message(msg, info);
1863c00ecebSShengjiu Wang 
18788541443SDeepak R Varma 	return 0;
1883c00ecebSShengjiu Wang }
1893c00ecebSShengjiu Wang 
imx_rpmsg_pcm_pointer(struct snd_soc_component * component,struct snd_pcm_substream * substream)1903c00ecebSShengjiu Wang static snd_pcm_uframes_t imx_rpmsg_pcm_pointer(struct snd_soc_component *component,
1913c00ecebSShengjiu Wang 					       struct snd_pcm_substream *substream)
1923c00ecebSShengjiu Wang {
1933c00ecebSShengjiu Wang 	struct rpmsg_info *info = dev_get_drvdata(component->dev);
1943c00ecebSShengjiu Wang 	struct rpmsg_msg *msg;
1953c00ecebSShengjiu Wang 	unsigned int pos = 0;
1963c00ecebSShengjiu Wang 	int buffer_tail = 0;
1973c00ecebSShengjiu Wang 
1983c00ecebSShengjiu Wang 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
1993c00ecebSShengjiu Wang 		msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM];
2003c00ecebSShengjiu Wang 	else
2013c00ecebSShengjiu Wang 		msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM];
2023c00ecebSShengjiu Wang 
2033c00ecebSShengjiu Wang 	buffer_tail = msg->r_msg.param.buffer_tail;
2043c00ecebSShengjiu Wang 	pos = buffer_tail * snd_pcm_lib_period_bytes(substream);
2053c00ecebSShengjiu Wang 
2063c00ecebSShengjiu Wang 	return bytes_to_frames(substream->runtime, pos);
2073c00ecebSShengjiu Wang }
2083c00ecebSShengjiu Wang 
imx_rpmsg_timer_callback(struct timer_list * t)2093c00ecebSShengjiu Wang static void imx_rpmsg_timer_callback(struct timer_list *t)
2103c00ecebSShengjiu Wang {
2113c00ecebSShengjiu Wang 	struct stream_timer  *stream_timer =
2123c00ecebSShengjiu Wang 			from_timer(stream_timer, t, timer);
2133c00ecebSShengjiu Wang 	struct snd_pcm_substream *substream = stream_timer->substream;
2143c00ecebSShengjiu Wang 	struct rpmsg_info *info = stream_timer->info;
2153c00ecebSShengjiu Wang 	struct rpmsg_msg *msg;
2163c00ecebSShengjiu Wang 
2173c00ecebSShengjiu Wang 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
2183c00ecebSShengjiu Wang 		msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM];
2193c00ecebSShengjiu Wang 		msg->s_msg.header.cmd = TX_PERIOD_DONE;
2203c00ecebSShengjiu Wang 	} else {
2213c00ecebSShengjiu Wang 		msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM];
2223c00ecebSShengjiu Wang 		msg->s_msg.header.cmd = RX_PERIOD_DONE;
2233c00ecebSShengjiu Wang 	}
2243c00ecebSShengjiu Wang 
2253c00ecebSShengjiu Wang 	imx_rpmsg_insert_workqueue(substream, msg, info);
2263c00ecebSShengjiu Wang }
2273c00ecebSShengjiu Wang 
imx_rpmsg_pcm_open(struct snd_soc_component * component,struct snd_pcm_substream * substream)2283c00ecebSShengjiu Wang static int imx_rpmsg_pcm_open(struct snd_soc_component *component,
2293c00ecebSShengjiu Wang 			      struct snd_pcm_substream *substream)
2303c00ecebSShengjiu Wang {
2313c00ecebSShengjiu Wang 	struct rpmsg_info *info = dev_get_drvdata(component->dev);
23214ec63f6SKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
23314ec63f6SKuninori Morimoto 	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
23482770b76SChancel Liu 	struct fsl_rpmsg *rpmsg = dev_get_drvdata(cpu_dai->dev);
23582770b76SChancel Liu 	struct snd_pcm_hardware pcm_hardware;
2363c00ecebSShengjiu Wang 	struct rpmsg_msg *msg;
2373c00ecebSShengjiu Wang 	int ret = 0;
2383c00ecebSShengjiu Wang 	int cmd;
2393c00ecebSShengjiu Wang 
2403c00ecebSShengjiu Wang 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
2413c00ecebSShengjiu Wang 		msg = &info->msg[TX_OPEN];
2423c00ecebSShengjiu Wang 		msg->s_msg.header.cmd = TX_OPEN;
2433c00ecebSShengjiu Wang 
2443c00ecebSShengjiu Wang 		/* reinitialize buffer counter*/
2453c00ecebSShengjiu Wang 		cmd = TX_PERIOD_DONE + MSG_TYPE_A_NUM;
2463c00ecebSShengjiu Wang 		info->msg[cmd].s_msg.param.buffer_tail = 0;
2473c00ecebSShengjiu Wang 		info->msg[cmd].r_msg.param.buffer_tail = 0;
2483c00ecebSShengjiu Wang 		info->msg[TX_POINTER].r_msg.param.buffer_offset = 0;
2493c00ecebSShengjiu Wang 
2503c00ecebSShengjiu Wang 	} else {
2513c00ecebSShengjiu Wang 		msg = &info->msg[RX_OPEN];
2523c00ecebSShengjiu Wang 		msg->s_msg.header.cmd = RX_OPEN;
2533c00ecebSShengjiu Wang 
2543c00ecebSShengjiu Wang 		/* reinitialize buffer counter*/
2553c00ecebSShengjiu Wang 		cmd = RX_PERIOD_DONE + MSG_TYPE_A_NUM;
2563c00ecebSShengjiu Wang 		info->msg[cmd].s_msg.param.buffer_tail = 0;
2573c00ecebSShengjiu Wang 		info->msg[cmd].r_msg.param.buffer_tail = 0;
2583c00ecebSShengjiu Wang 		info->msg[RX_POINTER].r_msg.param.buffer_offset = 0;
2593c00ecebSShengjiu Wang 	}
2603c00ecebSShengjiu Wang 
2613c00ecebSShengjiu Wang 	info->send_message(msg, info);
2623c00ecebSShengjiu Wang 
26382770b76SChancel Liu 	pcm_hardware = imx_rpmsg_pcm_hardware;
26482770b76SChancel Liu 	pcm_hardware.buffer_bytes_max = rpmsg->buffer_size;
26582770b76SChancel Liu 	pcm_hardware.period_bytes_max = pcm_hardware.buffer_bytes_max / 2;
2663c00ecebSShengjiu Wang 
26782770b76SChancel Liu 	snd_soc_set_runtime_hwparams(substream, &pcm_hardware);
2683c00ecebSShengjiu Wang 
2693c00ecebSShengjiu Wang 	ret = snd_pcm_hw_constraint_integer(substream->runtime,
2703c00ecebSShengjiu Wang 					    SNDRV_PCM_HW_PARAM_PERIODS);
2713c00ecebSShengjiu Wang 	if (ret < 0)
2723c00ecebSShengjiu Wang 		return ret;
2733c00ecebSShengjiu Wang 
2743c00ecebSShengjiu Wang 	info->msg_drop_count[substream->stream] = 0;
2753c00ecebSShengjiu Wang 
2763c00ecebSShengjiu Wang 	/* Create timer*/
2773c00ecebSShengjiu Wang 	info->stream_timer[substream->stream].info = info;
2783c00ecebSShengjiu Wang 	info->stream_timer[substream->stream].substream = substream;
2793c00ecebSShengjiu Wang 	timer_setup(&info->stream_timer[substream->stream].timer,
2803c00ecebSShengjiu Wang 		    imx_rpmsg_timer_callback, 0);
2813c00ecebSShengjiu Wang 	return ret;
2823c00ecebSShengjiu Wang }
2833c00ecebSShengjiu Wang 
imx_rpmsg_pcm_close(struct snd_soc_component * component,struct snd_pcm_substream * substream)2843c00ecebSShengjiu Wang static int imx_rpmsg_pcm_close(struct snd_soc_component *component,
2853c00ecebSShengjiu Wang 			       struct snd_pcm_substream *substream)
2863c00ecebSShengjiu Wang {
28714ec63f6SKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
2883c00ecebSShengjiu Wang 	struct rpmsg_info *info = dev_get_drvdata(component->dev);
2893c00ecebSShengjiu Wang 	struct rpmsg_msg *msg;
2903c00ecebSShengjiu Wang 
2913c00ecebSShengjiu Wang 	/* Flush work in workqueue to make TX_CLOSE is the last message */
2923c00ecebSShengjiu Wang 	flush_workqueue(info->rpmsg_wq);
2933c00ecebSShengjiu Wang 
2943c00ecebSShengjiu Wang 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
2953c00ecebSShengjiu Wang 		msg = &info->msg[TX_CLOSE];
2963c00ecebSShengjiu Wang 		msg->s_msg.header.cmd = TX_CLOSE;
2973c00ecebSShengjiu Wang 	} else {
2983c00ecebSShengjiu Wang 		msg = &info->msg[RX_CLOSE];
2993c00ecebSShengjiu Wang 		msg->s_msg.header.cmd = RX_CLOSE;
3003c00ecebSShengjiu Wang 	}
3013c00ecebSShengjiu Wang 
3023c00ecebSShengjiu Wang 	info->send_message(msg, info);
3033c00ecebSShengjiu Wang 
3043c00ecebSShengjiu Wang 	del_timer(&info->stream_timer[substream->stream].timer);
3053c00ecebSShengjiu Wang 
3063c00ecebSShengjiu Wang 	rtd->dai_link->ignore_suspend = 0;
3073c00ecebSShengjiu Wang 
3083c00ecebSShengjiu Wang 	if (info->msg_drop_count[substream->stream])
3093c00ecebSShengjiu Wang 		dev_warn(rtd->dev, "Msg is dropped!, number is %d\n",
3103c00ecebSShengjiu Wang 			 info->msg_drop_count[substream->stream]);
3113c00ecebSShengjiu Wang 
31288541443SDeepak R Varma 	return 0;
3133c00ecebSShengjiu Wang }
3143c00ecebSShengjiu Wang 
imx_rpmsg_pcm_prepare(struct snd_soc_component * component,struct snd_pcm_substream * substream)3153c00ecebSShengjiu Wang static int imx_rpmsg_pcm_prepare(struct snd_soc_component *component,
3163c00ecebSShengjiu Wang 				 struct snd_pcm_substream *substream)
3173c00ecebSShengjiu Wang {
3183c00ecebSShengjiu Wang 	struct snd_pcm_runtime *runtime = substream->runtime;
3193c00ecebSShengjiu Wang 	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
32014ec63f6SKuninori Morimoto 	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
3213c00ecebSShengjiu Wang 	struct fsl_rpmsg *rpmsg = dev_get_drvdata(cpu_dai->dev);
3223c00ecebSShengjiu Wang 
3233c00ecebSShengjiu Wang 	/*
3243c00ecebSShengjiu Wang 	 * NON-MMAP mode, NONBLOCK, Version 2, enable lpa in dts
3253c00ecebSShengjiu Wang 	 * four conditions to determine the lpa is enabled.
3263c00ecebSShengjiu Wang 	 */
3273c00ecebSShengjiu Wang 	if ((runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ||
3283c00ecebSShengjiu Wang 	     runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) &&
3293c00ecebSShengjiu Wang 	     rpmsg->enable_lpa) {
3303c00ecebSShengjiu Wang 		/*
3313c00ecebSShengjiu Wang 		 * Ignore suspend operation in low power mode
3323c00ecebSShengjiu Wang 		 * M core will continue playback music on A core suspend.
3333c00ecebSShengjiu Wang 		 */
3343c00ecebSShengjiu Wang 		rtd->dai_link->ignore_suspend = 1;
3353c00ecebSShengjiu Wang 		rpmsg->force_lpa = 1;
3363c00ecebSShengjiu Wang 	} else {
3373c00ecebSShengjiu Wang 		rpmsg->force_lpa = 0;
3383c00ecebSShengjiu Wang 	}
3393c00ecebSShengjiu Wang 
3403c00ecebSShengjiu Wang 	return 0;
3413c00ecebSShengjiu Wang }
3423c00ecebSShengjiu Wang 
imx_rpmsg_pcm_dma_complete(void * arg)3433c00ecebSShengjiu Wang static void imx_rpmsg_pcm_dma_complete(void *arg)
3443c00ecebSShengjiu Wang {
3453c00ecebSShengjiu Wang 	struct snd_pcm_substream *substream = arg;
3463c00ecebSShengjiu Wang 
3473c00ecebSShengjiu Wang 	snd_pcm_period_elapsed(substream);
3483c00ecebSShengjiu Wang }
3493c00ecebSShengjiu Wang 
imx_rpmsg_prepare_and_submit(struct snd_soc_component * component,struct snd_pcm_substream * substream)3503c00ecebSShengjiu Wang static int imx_rpmsg_prepare_and_submit(struct snd_soc_component *component,
3513c00ecebSShengjiu Wang 					struct snd_pcm_substream *substream)
3523c00ecebSShengjiu Wang {
3533c00ecebSShengjiu Wang 	struct rpmsg_info *info = dev_get_drvdata(component->dev);
3543c00ecebSShengjiu Wang 	struct rpmsg_msg *msg;
3553c00ecebSShengjiu Wang 
3563c00ecebSShengjiu Wang 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
3573c00ecebSShengjiu Wang 		msg = &info->msg[TX_BUFFER];
3583c00ecebSShengjiu Wang 		msg->s_msg.header.cmd = TX_BUFFER;
3593c00ecebSShengjiu Wang 	} else {
3603c00ecebSShengjiu Wang 		msg = &info->msg[RX_BUFFER];
3613c00ecebSShengjiu Wang 		msg->s_msg.header.cmd = RX_BUFFER;
3623c00ecebSShengjiu Wang 	}
3633c00ecebSShengjiu Wang 
3643c00ecebSShengjiu Wang 	/* Send buffer address and buffer size */
3653c00ecebSShengjiu Wang 	msg->s_msg.param.buffer_addr = substream->runtime->dma_addr;
3663c00ecebSShengjiu Wang 	msg->s_msg.param.buffer_size = snd_pcm_lib_buffer_bytes(substream);
3673c00ecebSShengjiu Wang 	msg->s_msg.param.period_size = snd_pcm_lib_period_bytes(substream);
3683c00ecebSShengjiu Wang 	msg->s_msg.param.buffer_tail = 0;
3693c00ecebSShengjiu Wang 
3703c00ecebSShengjiu Wang 	info->num_period[substream->stream] = msg->s_msg.param.buffer_size /
3713c00ecebSShengjiu Wang 					      msg->s_msg.param.period_size;
3723c00ecebSShengjiu Wang 
3733c00ecebSShengjiu Wang 	info->callback[substream->stream] = imx_rpmsg_pcm_dma_complete;
3743c00ecebSShengjiu Wang 	info->callback_param[substream->stream] = substream;
3753c00ecebSShengjiu Wang 
3763c00ecebSShengjiu Wang 	return imx_rpmsg_insert_workqueue(substream, msg, info);
3773c00ecebSShengjiu Wang }
3783c00ecebSShengjiu Wang 
imx_rpmsg_async_issue_pending(struct snd_soc_component * component,struct snd_pcm_substream * substream)3793c00ecebSShengjiu Wang static int imx_rpmsg_async_issue_pending(struct snd_soc_component *component,
3803c00ecebSShengjiu Wang 					 struct snd_pcm_substream *substream)
3813c00ecebSShengjiu Wang {
3823c00ecebSShengjiu Wang 	struct rpmsg_info *info = dev_get_drvdata(component->dev);
3833c00ecebSShengjiu Wang 	struct rpmsg_msg *msg;
3843c00ecebSShengjiu Wang 
3853c00ecebSShengjiu Wang 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
3863c00ecebSShengjiu Wang 		msg = &info->msg[TX_START];
3873c00ecebSShengjiu Wang 		msg->s_msg.header.cmd = TX_START;
3883c00ecebSShengjiu Wang 	} else {
3893c00ecebSShengjiu Wang 		msg = &info->msg[RX_START];
3903c00ecebSShengjiu Wang 		msg->s_msg.header.cmd = RX_START;
3913c00ecebSShengjiu Wang 	}
3923c00ecebSShengjiu Wang 
3933c00ecebSShengjiu Wang 	return imx_rpmsg_insert_workqueue(substream, msg, info);
3943c00ecebSShengjiu Wang }
3953c00ecebSShengjiu Wang 
imx_rpmsg_restart(struct snd_soc_component * component,struct snd_pcm_substream * substream)3963c00ecebSShengjiu Wang static int imx_rpmsg_restart(struct snd_soc_component *component,
3973c00ecebSShengjiu Wang 			     struct snd_pcm_substream *substream)
3983c00ecebSShengjiu Wang {
3993c00ecebSShengjiu Wang 	struct rpmsg_info *info = dev_get_drvdata(component->dev);
4003c00ecebSShengjiu Wang 	struct rpmsg_msg *msg;
4013c00ecebSShengjiu Wang 
4023c00ecebSShengjiu Wang 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
4033c00ecebSShengjiu Wang 		msg = &info->msg[TX_RESTART];
4043c00ecebSShengjiu Wang 		msg->s_msg.header.cmd = TX_RESTART;
4053c00ecebSShengjiu Wang 	} else {
4063c00ecebSShengjiu Wang 		msg = &info->msg[RX_RESTART];
4073c00ecebSShengjiu Wang 		msg->s_msg.header.cmd = RX_RESTART;
4083c00ecebSShengjiu Wang 	}
4093c00ecebSShengjiu Wang 
4103c00ecebSShengjiu Wang 	return imx_rpmsg_insert_workqueue(substream, msg, info);
4113c00ecebSShengjiu Wang }
4123c00ecebSShengjiu Wang 
imx_rpmsg_pause(struct snd_soc_component * component,struct snd_pcm_substream * substream)4133c00ecebSShengjiu Wang static int imx_rpmsg_pause(struct snd_soc_component *component,
4143c00ecebSShengjiu Wang 			   struct snd_pcm_substream *substream)
4153c00ecebSShengjiu Wang {
4163c00ecebSShengjiu Wang 	struct rpmsg_info *info = dev_get_drvdata(component->dev);
4173c00ecebSShengjiu Wang 	struct rpmsg_msg *msg;
4183c00ecebSShengjiu Wang 
4193c00ecebSShengjiu Wang 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
4203c00ecebSShengjiu Wang 		msg = &info->msg[TX_PAUSE];
4213c00ecebSShengjiu Wang 		msg->s_msg.header.cmd = TX_PAUSE;
4223c00ecebSShengjiu Wang 	} else {
4233c00ecebSShengjiu Wang 		msg = &info->msg[RX_PAUSE];
4243c00ecebSShengjiu Wang 		msg->s_msg.header.cmd = RX_PAUSE;
4253c00ecebSShengjiu Wang 	}
4263c00ecebSShengjiu Wang 
4273c00ecebSShengjiu Wang 	return imx_rpmsg_insert_workqueue(substream, msg, info);
4283c00ecebSShengjiu Wang }
4293c00ecebSShengjiu Wang 
imx_rpmsg_terminate_all(struct snd_soc_component * component,struct snd_pcm_substream * substream)4303c00ecebSShengjiu Wang static int imx_rpmsg_terminate_all(struct snd_soc_component *component,
4313c00ecebSShengjiu Wang 				   struct snd_pcm_substream *substream)
4323c00ecebSShengjiu Wang {
4333c00ecebSShengjiu Wang 	struct rpmsg_info *info = dev_get_drvdata(component->dev);
4343c00ecebSShengjiu Wang 	struct rpmsg_msg *msg;
4353c00ecebSShengjiu Wang 	int cmd;
4363c00ecebSShengjiu Wang 
4373c00ecebSShengjiu Wang 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
4383c00ecebSShengjiu Wang 		msg = &info->msg[TX_TERMINATE];
4393c00ecebSShengjiu Wang 		msg->s_msg.header.cmd = TX_TERMINATE;
4403c00ecebSShengjiu Wang 		/* Clear buffer count*/
4413c00ecebSShengjiu Wang 		cmd = TX_PERIOD_DONE + MSG_TYPE_A_NUM;
4423c00ecebSShengjiu Wang 		info->msg[cmd].s_msg.param.buffer_tail = 0;
4433c00ecebSShengjiu Wang 		info->msg[cmd].r_msg.param.buffer_tail = 0;
4443c00ecebSShengjiu Wang 		info->msg[TX_POINTER].r_msg.param.buffer_offset = 0;
4453c00ecebSShengjiu Wang 	} else {
4463c00ecebSShengjiu Wang 		msg = &info->msg[RX_TERMINATE];
4473c00ecebSShengjiu Wang 		msg->s_msg.header.cmd = RX_TERMINATE;
4483c00ecebSShengjiu Wang 		/* Clear buffer count*/
4493c00ecebSShengjiu Wang 		cmd = RX_PERIOD_DONE + MSG_TYPE_A_NUM;
4503c00ecebSShengjiu Wang 		info->msg[cmd].s_msg.param.buffer_tail = 0;
4513c00ecebSShengjiu Wang 		info->msg[cmd].r_msg.param.buffer_tail = 0;
4523c00ecebSShengjiu Wang 		info->msg[RX_POINTER].r_msg.param.buffer_offset = 0;
4533c00ecebSShengjiu Wang 	}
4543c00ecebSShengjiu Wang 
4553c00ecebSShengjiu Wang 	del_timer(&info->stream_timer[substream->stream].timer);
4563c00ecebSShengjiu Wang 
4573c00ecebSShengjiu Wang 	return imx_rpmsg_insert_workqueue(substream, msg, info);
4583c00ecebSShengjiu Wang }
4593c00ecebSShengjiu Wang 
imx_rpmsg_pcm_trigger(struct snd_soc_component * component,struct snd_pcm_substream * substream,int cmd)4603c00ecebSShengjiu Wang static int imx_rpmsg_pcm_trigger(struct snd_soc_component *component,
4613c00ecebSShengjiu Wang 				 struct snd_pcm_substream *substream, int cmd)
4623c00ecebSShengjiu Wang {
4633c00ecebSShengjiu Wang 	struct snd_pcm_runtime *runtime = substream->runtime;
4643c00ecebSShengjiu Wang 	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
46514ec63f6SKuninori Morimoto 	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
4663c00ecebSShengjiu Wang 	struct fsl_rpmsg *rpmsg = dev_get_drvdata(cpu_dai->dev);
4673c00ecebSShengjiu Wang 	int ret = 0;
4683c00ecebSShengjiu Wang 
4693c00ecebSShengjiu Wang 	switch (cmd) {
4703c00ecebSShengjiu Wang 	case SNDRV_PCM_TRIGGER_START:
4713c00ecebSShengjiu Wang 		ret = imx_rpmsg_prepare_and_submit(component, substream);
4723c00ecebSShengjiu Wang 		if (ret)
4733c00ecebSShengjiu Wang 			return ret;
4743c00ecebSShengjiu Wang 		ret = imx_rpmsg_async_issue_pending(component, substream);
4753c00ecebSShengjiu Wang 		break;
4763c00ecebSShengjiu Wang 	case SNDRV_PCM_TRIGGER_RESUME:
4773c00ecebSShengjiu Wang 		if (rpmsg->force_lpa)
4783c00ecebSShengjiu Wang 			break;
4793c00ecebSShengjiu Wang 		fallthrough;
4803c00ecebSShengjiu Wang 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
4813c00ecebSShengjiu Wang 		ret = imx_rpmsg_restart(component, substream);
4823c00ecebSShengjiu Wang 		break;
4833c00ecebSShengjiu Wang 	case SNDRV_PCM_TRIGGER_SUSPEND:
4843c00ecebSShengjiu Wang 		if (!rpmsg->force_lpa) {
4853c00ecebSShengjiu Wang 			if (runtime->info & SNDRV_PCM_INFO_PAUSE)
4863c00ecebSShengjiu Wang 				ret = imx_rpmsg_pause(component, substream);
4873c00ecebSShengjiu Wang 			else
4883c00ecebSShengjiu Wang 				ret = imx_rpmsg_terminate_all(component, substream);
4893c00ecebSShengjiu Wang 		}
4903c00ecebSShengjiu Wang 		break;
4913c00ecebSShengjiu Wang 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
4923c00ecebSShengjiu Wang 		ret = imx_rpmsg_pause(component, substream);
4933c00ecebSShengjiu Wang 		break;
4943c00ecebSShengjiu Wang 	case SNDRV_PCM_TRIGGER_STOP:
4953c00ecebSShengjiu Wang 		ret = imx_rpmsg_terminate_all(component, substream);
4963c00ecebSShengjiu Wang 		break;
4973c00ecebSShengjiu Wang 	default:
4983c00ecebSShengjiu Wang 		return -EINVAL;
4993c00ecebSShengjiu Wang 	}
5003c00ecebSShengjiu Wang 
5013c00ecebSShengjiu Wang 	if (ret)
5023c00ecebSShengjiu Wang 		return ret;
5033c00ecebSShengjiu Wang 
5043c00ecebSShengjiu Wang 	return 0;
5053c00ecebSShengjiu Wang }
5063c00ecebSShengjiu Wang 
5073c00ecebSShengjiu Wang /*
5083c00ecebSShengjiu Wang  * imx_rpmsg_pcm_ack
5093c00ecebSShengjiu Wang  *
5103c00ecebSShengjiu Wang  * Send the period index to M core through rpmsg, but not send
5113c00ecebSShengjiu Wang  * all the period index to M core, reduce some unnessesary msg
5123c00ecebSShengjiu Wang  * to reduce the pressure of rpmsg bandwidth.
5133c00ecebSShengjiu Wang  */
imx_rpmsg_pcm_ack(struct snd_soc_component * component,struct snd_pcm_substream * substream)5143c00ecebSShengjiu Wang static int imx_rpmsg_pcm_ack(struct snd_soc_component *component,
5153c00ecebSShengjiu Wang 			     struct snd_pcm_substream *substream)
5163c00ecebSShengjiu Wang {
5173c00ecebSShengjiu Wang 	struct snd_pcm_runtime *runtime = substream->runtime;
5183c00ecebSShengjiu Wang 	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
51914ec63f6SKuninori Morimoto 	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
5203c00ecebSShengjiu Wang 	struct fsl_rpmsg *rpmsg = dev_get_drvdata(cpu_dai->dev);
5213c00ecebSShengjiu Wang 	struct rpmsg_info *info = dev_get_drvdata(component->dev);
5223c00ecebSShengjiu Wang 	snd_pcm_uframes_t period_size = runtime->period_size;
5233c00ecebSShengjiu Wang 	snd_pcm_sframes_t avail;
5243c00ecebSShengjiu Wang 	struct timer_list *timer;
5253c00ecebSShengjiu Wang 	struct rpmsg_msg *msg;
5263c00ecebSShengjiu Wang 	unsigned long flags;
5273c00ecebSShengjiu Wang 	int buffer_tail = 0;
528cccc16dcSPierre-Louis Bossart 	int written_num;
5293c00ecebSShengjiu Wang 
5303c00ecebSShengjiu Wang 	if (!rpmsg->force_lpa)
5313c00ecebSShengjiu Wang 		return 0;
5323c00ecebSShengjiu Wang 
5333c00ecebSShengjiu Wang 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
5343c00ecebSShengjiu Wang 		msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM];
5353c00ecebSShengjiu Wang 		msg->s_msg.header.cmd = TX_PERIOD_DONE;
5363c00ecebSShengjiu Wang 	} else {
5373c00ecebSShengjiu Wang 		msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM];
5383c00ecebSShengjiu Wang 		msg->s_msg.header.cmd = RX_PERIOD_DONE;
5393c00ecebSShengjiu Wang 	}
5403c00ecebSShengjiu Wang 
5413c00ecebSShengjiu Wang 	msg->s_msg.header.type = MSG_TYPE_C;
5423c00ecebSShengjiu Wang 
5433c00ecebSShengjiu Wang 	buffer_tail = (frames_to_bytes(runtime, runtime->control->appl_ptr) %
5443c00ecebSShengjiu Wang 		       snd_pcm_lib_buffer_bytes(substream));
5453c00ecebSShengjiu Wang 	buffer_tail = buffer_tail / snd_pcm_lib_period_bytes(substream);
5463c00ecebSShengjiu Wang 
5473c00ecebSShengjiu Wang 	/* There is update for period index */
5483c00ecebSShengjiu Wang 	if (buffer_tail != msg->s_msg.param.buffer_tail) {
5493c00ecebSShengjiu Wang 		written_num = buffer_tail - msg->s_msg.param.buffer_tail;
5503c00ecebSShengjiu Wang 		if (written_num < 0)
5513c00ecebSShengjiu Wang 			written_num += runtime->periods;
5523c00ecebSShengjiu Wang 
5533c00ecebSShengjiu Wang 		msg->s_msg.param.buffer_tail = buffer_tail;
5543c00ecebSShengjiu Wang 
5553c00ecebSShengjiu Wang 		/* The notification message is updated to latest */
5563c00ecebSShengjiu Wang 		spin_lock_irqsave(&info->lock[substream->stream], flags);
5573c00ecebSShengjiu Wang 		memcpy(&info->notify[substream->stream], msg,
5583c00ecebSShengjiu Wang 		       sizeof(struct rpmsg_s_msg));
5593c00ecebSShengjiu Wang 		info->notify_updated[substream->stream] = true;
5603c00ecebSShengjiu Wang 		spin_unlock_irqrestore(&info->lock[substream->stream], flags);
5613c00ecebSShengjiu Wang 
5623c00ecebSShengjiu Wang 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
5633c00ecebSShengjiu Wang 			avail = snd_pcm_playback_hw_avail(runtime);
5643c00ecebSShengjiu Wang 		else
5653c00ecebSShengjiu Wang 			avail = snd_pcm_capture_hw_avail(runtime);
5663c00ecebSShengjiu Wang 
5673c00ecebSShengjiu Wang 		timer = &info->stream_timer[substream->stream].timer;
5683c00ecebSShengjiu Wang 		/*
5693c00ecebSShengjiu Wang 		 * If the data in the buffer is less than one period before
5703c00ecebSShengjiu Wang 		 * this fill, which means the data may not enough on M
5713c00ecebSShengjiu Wang 		 * core side, we need to send message immediately to let
5723c00ecebSShengjiu Wang 		 * M core know the pointer is updated.
5733c00ecebSShengjiu Wang 		 * if there is more than one period data in the buffer before
5743c00ecebSShengjiu Wang 		 * this fill, which means the data is enough on M core side,
5753c00ecebSShengjiu Wang 		 * we can delay one period (using timer) to send the message
5763c00ecebSShengjiu Wang 		 * for reduce the message number in workqueue, because the
5773c00ecebSShengjiu Wang 		 * pointer may be updated by ack function later, we can
5783c00ecebSShengjiu Wang 		 * send latest pointer to M core side.
5793c00ecebSShengjiu Wang 		 */
5803c00ecebSShengjiu Wang 		if ((avail - written_num * period_size) <= period_size) {
5813c00ecebSShengjiu Wang 			imx_rpmsg_insert_workqueue(substream, msg, info);
5823c00ecebSShengjiu Wang 		} else if (rpmsg->force_lpa && !timer_pending(timer)) {
5833c00ecebSShengjiu Wang 			int time_msec;
5843c00ecebSShengjiu Wang 
5853c00ecebSShengjiu Wang 			time_msec = (int)(runtime->period_size * 1000 / runtime->rate);
5863c00ecebSShengjiu Wang 			mod_timer(timer, jiffies + msecs_to_jiffies(time_msec));
5873c00ecebSShengjiu Wang 		}
5883c00ecebSShengjiu Wang 	}
5893c00ecebSShengjiu Wang 
5903c00ecebSShengjiu Wang 	return 0;
5913c00ecebSShengjiu Wang }
5923c00ecebSShengjiu Wang 
imx_rpmsg_pcm_new(struct snd_soc_component * component,struct snd_soc_pcm_runtime * rtd)5933c00ecebSShengjiu Wang static int imx_rpmsg_pcm_new(struct snd_soc_component *component,
5943c00ecebSShengjiu Wang 			     struct snd_soc_pcm_runtime *rtd)
5953c00ecebSShengjiu Wang {
5963c00ecebSShengjiu Wang 	struct snd_card *card = rtd->card->snd_card;
5973c00ecebSShengjiu Wang 	struct snd_pcm *pcm = rtd->pcm;
59814ec63f6SKuninori Morimoto 	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
5993c00ecebSShengjiu Wang 	struct fsl_rpmsg *rpmsg = dev_get_drvdata(cpu_dai->dev);
6003c00ecebSShengjiu Wang 	int ret;
6013c00ecebSShengjiu Wang 
6023c00ecebSShengjiu Wang 	ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
6033c00ecebSShengjiu Wang 	if (ret)
6043c00ecebSShengjiu Wang 		return ret;
6053c00ecebSShengjiu Wang 
6060e1b598fSTakashi Iwai 	return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_WC,
6070e1b598fSTakashi Iwai 					    pcm->card->dev, rpmsg->buffer_size);
6083c00ecebSShengjiu Wang }
6093c00ecebSShengjiu Wang 
6103c00ecebSShengjiu Wang static const struct snd_soc_component_driver imx_rpmsg_soc_component = {
6113c00ecebSShengjiu Wang 	.name		= IMX_PCM_DRV_NAME,
6123c00ecebSShengjiu Wang 	.pcm_construct	= imx_rpmsg_pcm_new,
6133c00ecebSShengjiu Wang 	.open		= imx_rpmsg_pcm_open,
6143c00ecebSShengjiu Wang 	.close		= imx_rpmsg_pcm_close,
6153c00ecebSShengjiu Wang 	.hw_params	= imx_rpmsg_pcm_hw_params,
6163c00ecebSShengjiu Wang 	.trigger	= imx_rpmsg_pcm_trigger,
6173c00ecebSShengjiu Wang 	.pointer	= imx_rpmsg_pcm_pointer,
6183c00ecebSShengjiu Wang 	.ack		= imx_rpmsg_pcm_ack,
6193c00ecebSShengjiu Wang 	.prepare	= imx_rpmsg_pcm_prepare,
6203c00ecebSShengjiu Wang };
6213c00ecebSShengjiu Wang 
imx_rpmsg_pcm_work(struct work_struct * work)6223c00ecebSShengjiu Wang static void imx_rpmsg_pcm_work(struct work_struct *work)
6233c00ecebSShengjiu Wang {
6243c00ecebSShengjiu Wang 	struct work_of_rpmsg *work_of_rpmsg;
6253c00ecebSShengjiu Wang 	bool is_notification = false;
6263c00ecebSShengjiu Wang 	struct rpmsg_info *info;
6273c00ecebSShengjiu Wang 	struct rpmsg_msg msg;
6283c00ecebSShengjiu Wang 	unsigned long flags;
6293c00ecebSShengjiu Wang 
6303c00ecebSShengjiu Wang 	work_of_rpmsg = container_of(work, struct work_of_rpmsg, work);
6313c00ecebSShengjiu Wang 	info = work_of_rpmsg->info;
6323c00ecebSShengjiu Wang 
6333c00ecebSShengjiu Wang 	/*
6343c00ecebSShengjiu Wang 	 * Every work in the work queue, first we check if there
6353c00ecebSShengjiu Wang 	 * is update for period is filled, because there may be not
6363c00ecebSShengjiu Wang 	 * enough data in M core side, need to let M core know
6373c00ecebSShengjiu Wang 	 * data is updated immediately.
6383c00ecebSShengjiu Wang 	 */
6393c00ecebSShengjiu Wang 	spin_lock_irqsave(&info->lock[TX], flags);
6403c00ecebSShengjiu Wang 	if (info->notify_updated[TX]) {
6413c00ecebSShengjiu Wang 		memcpy(&msg, &info->notify[TX], sizeof(struct rpmsg_s_msg));
6423c00ecebSShengjiu Wang 		info->notify_updated[TX] = false;
6433c00ecebSShengjiu Wang 		spin_unlock_irqrestore(&info->lock[TX], flags);
6443c00ecebSShengjiu Wang 		info->send_message(&msg, info);
6453c00ecebSShengjiu Wang 	} else {
6463c00ecebSShengjiu Wang 		spin_unlock_irqrestore(&info->lock[TX], flags);
6473c00ecebSShengjiu Wang 	}
6483c00ecebSShengjiu Wang 
6493c00ecebSShengjiu Wang 	spin_lock_irqsave(&info->lock[RX], flags);
6503c00ecebSShengjiu Wang 	if (info->notify_updated[RX]) {
6513c00ecebSShengjiu Wang 		memcpy(&msg, &info->notify[RX], sizeof(struct rpmsg_s_msg));
6523c00ecebSShengjiu Wang 		info->notify_updated[RX] = false;
6533c00ecebSShengjiu Wang 		spin_unlock_irqrestore(&info->lock[RX], flags);
6543c00ecebSShengjiu Wang 		info->send_message(&msg, info);
6553c00ecebSShengjiu Wang 	} else {
6563c00ecebSShengjiu Wang 		spin_unlock_irqrestore(&info->lock[RX], flags);
6573c00ecebSShengjiu Wang 	}
6583c00ecebSShengjiu Wang 
6593c00ecebSShengjiu Wang 	/* Skip the notification message for it has been processed above */
6603c00ecebSShengjiu Wang 	if (work_of_rpmsg->msg.s_msg.header.type == MSG_TYPE_C &&
6613c00ecebSShengjiu Wang 	    (work_of_rpmsg->msg.s_msg.header.cmd == TX_PERIOD_DONE ||
6623c00ecebSShengjiu Wang 	     work_of_rpmsg->msg.s_msg.header.cmd == RX_PERIOD_DONE))
6633c00ecebSShengjiu Wang 		is_notification = true;
6643c00ecebSShengjiu Wang 
6653c00ecebSShengjiu Wang 	if (!is_notification)
6663c00ecebSShengjiu Wang 		info->send_message(&work_of_rpmsg->msg, info);
6673c00ecebSShengjiu Wang 
6683c00ecebSShengjiu Wang 	/* update read index */
6693c00ecebSShengjiu Wang 	spin_lock_irqsave(&info->wq_lock, flags);
6703c00ecebSShengjiu Wang 	info->work_read_index++;
6713c00ecebSShengjiu Wang 	info->work_read_index %= WORK_MAX_NUM;
6723c00ecebSShengjiu Wang 	spin_unlock_irqrestore(&info->wq_lock, flags);
6733c00ecebSShengjiu Wang }
6743c00ecebSShengjiu Wang 
imx_rpmsg_pcm_probe(struct platform_device * pdev)6753c00ecebSShengjiu Wang static int imx_rpmsg_pcm_probe(struct platform_device *pdev)
6763c00ecebSShengjiu Wang {
6773c00ecebSShengjiu Wang 	struct snd_soc_component *component;
6783c00ecebSShengjiu Wang 	struct rpmsg_info *info;
6793c00ecebSShengjiu Wang 	int ret, i;
6803c00ecebSShengjiu Wang 
6813c00ecebSShengjiu Wang 	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
6823c00ecebSShengjiu Wang 	if (!info)
6833c00ecebSShengjiu Wang 		return -ENOMEM;
6843c00ecebSShengjiu Wang 
6853c00ecebSShengjiu Wang 	platform_set_drvdata(pdev, info);
6863c00ecebSShengjiu Wang 
6873c00ecebSShengjiu Wang 	info->rpdev = container_of(pdev->dev.parent, struct rpmsg_device, dev);
6883c00ecebSShengjiu Wang 	info->dev = &pdev->dev;
6893c00ecebSShengjiu Wang 	/* Setup work queue */
690b2c2a947SChancel Liu 	info->rpmsg_wq = alloc_ordered_workqueue(info->rpdev->id.name,
6913c00ecebSShengjiu Wang 						 WQ_HIGHPRI |
6923c00ecebSShengjiu Wang 						 WQ_UNBOUND |
6933c00ecebSShengjiu Wang 						 WQ_FREEZABLE);
6943c00ecebSShengjiu Wang 	if (!info->rpmsg_wq) {
6953c00ecebSShengjiu Wang 		dev_err(&pdev->dev, "workqueue create failed\n");
6963c00ecebSShengjiu Wang 		return -ENOMEM;
6973c00ecebSShengjiu Wang 	}
6983c00ecebSShengjiu Wang 
6993c00ecebSShengjiu Wang 	/* Write index initialize 1, make it differ with the read index */
7003c00ecebSShengjiu Wang 	info->work_write_index = 1;
7013c00ecebSShengjiu Wang 	info->send_message = imx_rpmsg_pcm_send_message;
7023c00ecebSShengjiu Wang 
7033c00ecebSShengjiu Wang 	for (i = 0; i < WORK_MAX_NUM; i++) {
7043c00ecebSShengjiu Wang 		INIT_WORK(&info->work_list[i].work, imx_rpmsg_pcm_work);
7053c00ecebSShengjiu Wang 		info->work_list[i].info = info;
7063c00ecebSShengjiu Wang 	}
7073c00ecebSShengjiu Wang 
7083c00ecebSShengjiu Wang 	/* Initialize msg */
7093c00ecebSShengjiu Wang 	for (i = 0; i < MSG_MAX_NUM; i++) {
7103c00ecebSShengjiu Wang 		info->msg[i].s_msg.header.cate  = IMX_RPMSG_AUDIO;
7113c00ecebSShengjiu Wang 		info->msg[i].s_msg.header.major = IMX_RMPSG_MAJOR;
7123c00ecebSShengjiu Wang 		info->msg[i].s_msg.header.minor = IMX_RMPSG_MINOR;
7133c00ecebSShengjiu Wang 		info->msg[i].s_msg.header.type  = MSG_TYPE_A;
7143c00ecebSShengjiu Wang 		info->msg[i].s_msg.param.audioindex = 0;
7153c00ecebSShengjiu Wang 	}
7163c00ecebSShengjiu Wang 
7173c00ecebSShengjiu Wang 	init_completion(&info->cmd_complete);
7183c00ecebSShengjiu Wang 	mutex_init(&info->msg_lock);
7193c00ecebSShengjiu Wang 	spin_lock_init(&info->lock[TX]);
7203c00ecebSShengjiu Wang 	spin_lock_init(&info->lock[RX]);
7213c00ecebSShengjiu Wang 	spin_lock_init(&info->wq_lock);
7223c00ecebSShengjiu Wang 
7233c00ecebSShengjiu Wang 	ret = devm_snd_soc_register_component(&pdev->dev,
7243c00ecebSShengjiu Wang 					      &imx_rpmsg_soc_component,
7253c00ecebSShengjiu Wang 					      NULL, 0);
7263c00ecebSShengjiu Wang 	if (ret)
7273c00ecebSShengjiu Wang 		goto fail;
7283c00ecebSShengjiu Wang 
729b2c2a947SChancel Liu 	component = snd_soc_lookup_component(&pdev->dev, NULL);
7303c00ecebSShengjiu Wang 	if (!component) {
7313c00ecebSShengjiu Wang 		ret = -EINVAL;
7323c00ecebSShengjiu Wang 		goto fail;
7333c00ecebSShengjiu Wang 	}
734b2c2a947SChancel Liu 
7353c00ecebSShengjiu Wang #ifdef CONFIG_DEBUG_FS
7363c00ecebSShengjiu Wang 	component->debugfs_prefix = "rpmsg";
7373c00ecebSShengjiu Wang #endif
7383c00ecebSShengjiu Wang 
7393c00ecebSShengjiu Wang 	return 0;
7403c00ecebSShengjiu Wang 
7413c00ecebSShengjiu Wang fail:
7423c00ecebSShengjiu Wang 	if (info->rpmsg_wq)
7433c00ecebSShengjiu Wang 		destroy_workqueue(info->rpmsg_wq);
7443c00ecebSShengjiu Wang 
7453c00ecebSShengjiu Wang 	return ret;
7463c00ecebSShengjiu Wang }
7473c00ecebSShengjiu Wang 
imx_rpmsg_pcm_remove(struct platform_device * pdev)748a0649456SUwe Kleine-König static void imx_rpmsg_pcm_remove(struct platform_device *pdev)
7493c00ecebSShengjiu Wang {
7503c00ecebSShengjiu Wang 	struct rpmsg_info *info = platform_get_drvdata(pdev);
7513c00ecebSShengjiu Wang 
7523c00ecebSShengjiu Wang 	if (info->rpmsg_wq)
7533c00ecebSShengjiu Wang 		destroy_workqueue(info->rpmsg_wq);
7543c00ecebSShengjiu Wang }
7553c00ecebSShengjiu Wang 
7563c00ecebSShengjiu Wang #ifdef CONFIG_PM
imx_rpmsg_pcm_runtime_resume(struct device * dev)7573c00ecebSShengjiu Wang static int imx_rpmsg_pcm_runtime_resume(struct device *dev)
7583c00ecebSShengjiu Wang {
7593c00ecebSShengjiu Wang 	struct rpmsg_info *info = dev_get_drvdata(dev);
7603c00ecebSShengjiu Wang 
7613c00ecebSShengjiu Wang 	cpu_latency_qos_add_request(&info->pm_qos_req, 0);
7623c00ecebSShengjiu Wang 
7633c00ecebSShengjiu Wang 	return 0;
7643c00ecebSShengjiu Wang }
7653c00ecebSShengjiu Wang 
imx_rpmsg_pcm_runtime_suspend(struct device * dev)7663c00ecebSShengjiu Wang static int imx_rpmsg_pcm_runtime_suspend(struct device *dev)
7673c00ecebSShengjiu Wang {
7683c00ecebSShengjiu Wang 	struct rpmsg_info *info = dev_get_drvdata(dev);
7693c00ecebSShengjiu Wang 
7703c00ecebSShengjiu Wang 	cpu_latency_qos_remove_request(&info->pm_qos_req);
7713c00ecebSShengjiu Wang 
7723c00ecebSShengjiu Wang 	return 0;
7733c00ecebSShengjiu Wang }
7743c00ecebSShengjiu Wang #endif
7753c00ecebSShengjiu Wang 
7763c00ecebSShengjiu Wang #ifdef CONFIG_PM_SLEEP
imx_rpmsg_pcm_suspend(struct device * dev)7773c00ecebSShengjiu Wang static int imx_rpmsg_pcm_suspend(struct device *dev)
7783c00ecebSShengjiu Wang {
7793c00ecebSShengjiu Wang 	struct rpmsg_info *info = dev_get_drvdata(dev);
7803c00ecebSShengjiu Wang 	struct rpmsg_msg *rpmsg_tx;
7813c00ecebSShengjiu Wang 	struct rpmsg_msg *rpmsg_rx;
7823c00ecebSShengjiu Wang 
7833c00ecebSShengjiu Wang 	rpmsg_tx = &info->msg[TX_SUSPEND];
7843c00ecebSShengjiu Wang 	rpmsg_rx = &info->msg[RX_SUSPEND];
7853c00ecebSShengjiu Wang 
7863c00ecebSShengjiu Wang 	rpmsg_tx->s_msg.header.cmd = TX_SUSPEND;
7873c00ecebSShengjiu Wang 	info->send_message(rpmsg_tx, info);
7883c00ecebSShengjiu Wang 
7893c00ecebSShengjiu Wang 	rpmsg_rx->s_msg.header.cmd = RX_SUSPEND;
7903c00ecebSShengjiu Wang 	info->send_message(rpmsg_rx, info);
7913c00ecebSShengjiu Wang 
7923c00ecebSShengjiu Wang 	return 0;
7933c00ecebSShengjiu Wang }
7943c00ecebSShengjiu Wang 
imx_rpmsg_pcm_resume(struct device * dev)7953c00ecebSShengjiu Wang static int imx_rpmsg_pcm_resume(struct device *dev)
7963c00ecebSShengjiu Wang {
7973c00ecebSShengjiu Wang 	struct rpmsg_info *info = dev_get_drvdata(dev);
7983c00ecebSShengjiu Wang 	struct rpmsg_msg *rpmsg_tx;
7993c00ecebSShengjiu Wang 	struct rpmsg_msg *rpmsg_rx;
8003c00ecebSShengjiu Wang 
8013c00ecebSShengjiu Wang 	rpmsg_tx = &info->msg[TX_RESUME];
8023c00ecebSShengjiu Wang 	rpmsg_rx = &info->msg[RX_RESUME];
8033c00ecebSShengjiu Wang 
8043c00ecebSShengjiu Wang 	rpmsg_tx->s_msg.header.cmd = TX_RESUME;
8053c00ecebSShengjiu Wang 	info->send_message(rpmsg_tx, info);
8063c00ecebSShengjiu Wang 
8073c00ecebSShengjiu Wang 	rpmsg_rx->s_msg.header.cmd = RX_RESUME;
8083c00ecebSShengjiu Wang 	info->send_message(rpmsg_rx, info);
8093c00ecebSShengjiu Wang 
8103c00ecebSShengjiu Wang 	return 0;
8113c00ecebSShengjiu Wang }
8123c00ecebSShengjiu Wang #endif /* CONFIG_PM_SLEEP */
8133c00ecebSShengjiu Wang 
8143c00ecebSShengjiu Wang static const struct dev_pm_ops imx_rpmsg_pcm_pm_ops = {
8153c00ecebSShengjiu Wang 	SET_RUNTIME_PM_OPS(imx_rpmsg_pcm_runtime_suspend,
8163c00ecebSShengjiu Wang 			   imx_rpmsg_pcm_runtime_resume,
8173c00ecebSShengjiu Wang 			   NULL)
8183c00ecebSShengjiu Wang 	SET_SYSTEM_SLEEP_PM_OPS(imx_rpmsg_pcm_suspend,
8193c00ecebSShengjiu Wang 				imx_rpmsg_pcm_resume)
8203c00ecebSShengjiu Wang };
8213c00ecebSShengjiu Wang 
82241f96cd5SChancel Liu static const struct platform_device_id imx_rpmsg_pcm_id_table[] = {
82341f96cd5SChancel Liu 	{ .name	= "rpmsg-audio-channel" },
82441f96cd5SChancel Liu 	{ .name	= "rpmsg-micfil-channel" },
82541f96cd5SChancel Liu 	{ },
82641f96cd5SChancel Liu };
82741f96cd5SChancel Liu MODULE_DEVICE_TABLE(platform, imx_rpmsg_pcm_id_table);
82841f96cd5SChancel Liu 
8293c00ecebSShengjiu Wang static struct platform_driver imx_pcm_rpmsg_driver = {
8303c00ecebSShengjiu Wang 	.probe  = imx_rpmsg_pcm_probe,
831a0649456SUwe Kleine-König 	.remove_new = imx_rpmsg_pcm_remove,
83241f96cd5SChancel Liu 	.id_table = imx_rpmsg_pcm_id_table,
8333c00ecebSShengjiu Wang 	.driver = {
8343c00ecebSShengjiu Wang 		.name = IMX_PCM_DRV_NAME,
8353c00ecebSShengjiu Wang 		.pm = &imx_rpmsg_pcm_pm_ops,
8363c00ecebSShengjiu Wang 	},
8373c00ecebSShengjiu Wang };
8383c00ecebSShengjiu Wang module_platform_driver(imx_pcm_rpmsg_driver);
8393c00ecebSShengjiu Wang 
8403c00ecebSShengjiu Wang MODULE_DESCRIPTION("Freescale SoC Audio RPMSG PCM interface");
8413c00ecebSShengjiu Wang MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
8423c00ecebSShengjiu Wang MODULE_ALIAS("platform:" IMX_PCM_DRV_NAME);
8433c00ecebSShengjiu Wang MODULE_LICENSE("GPL v2");
844