xref: /OK3568_Linux_fs/kernel/sound/soc/dwc/dwc-pcm.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * ALSA SoC Synopsys PIO PCM for I2S driver
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * sound/soc/dwc/designware_pcm.c
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * Copyright (C) 2016 Synopsys
7*4882a593Smuzhiyun  * Jose Abreu <joabreu@synopsys.com>
8*4882a593Smuzhiyun  *
9*4882a593Smuzhiyun  * This file is licensed under the terms of the GNU General Public
10*4882a593Smuzhiyun  * License version 2. This program is licensed "as is" without any
11*4882a593Smuzhiyun  * warranty of any kind, whether express or implied.
12*4882a593Smuzhiyun  */
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #include <linux/io.h>
15*4882a593Smuzhiyun #include <linux/rcupdate.h>
16*4882a593Smuzhiyun #include <sound/pcm.h>
17*4882a593Smuzhiyun #include <sound/pcm_params.h>
18*4882a593Smuzhiyun #include "local.h"
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun #define BUFFER_BYTES_MAX	(3 * 2 * 8 * PERIOD_BYTES_MIN)
21*4882a593Smuzhiyun #define PERIOD_BYTES_MIN	4096
22*4882a593Smuzhiyun #define PERIODS_MIN		2
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun #define dw_pcm_tx_fn(sample_bits) \
25*4882a593Smuzhiyun static unsigned int dw_pcm_tx_##sample_bits(struct dw_i2s_dev *dev, \
26*4882a593Smuzhiyun 		struct snd_pcm_runtime *runtime, unsigned int tx_ptr, \
27*4882a593Smuzhiyun 		bool *period_elapsed) \
28*4882a593Smuzhiyun { \
29*4882a593Smuzhiyun 	const u##sample_bits (*p)[2] = (void *)runtime->dma_area; \
30*4882a593Smuzhiyun 	unsigned int period_pos = tx_ptr % runtime->period_size; \
31*4882a593Smuzhiyun 	int i; \
32*4882a593Smuzhiyun \
33*4882a593Smuzhiyun 	for (i = 0; i < dev->fifo_th; i++) { \
34*4882a593Smuzhiyun 		iowrite32(p[tx_ptr][0], dev->i2s_base + LRBR_LTHR(0)); \
35*4882a593Smuzhiyun 		iowrite32(p[tx_ptr][1], dev->i2s_base + RRBR_RTHR(0)); \
36*4882a593Smuzhiyun 		period_pos++; \
37*4882a593Smuzhiyun 		if (++tx_ptr >= runtime->buffer_size) \
38*4882a593Smuzhiyun 			tx_ptr = 0; \
39*4882a593Smuzhiyun 	} \
40*4882a593Smuzhiyun 	*period_elapsed = period_pos >= runtime->period_size; \
41*4882a593Smuzhiyun 	return tx_ptr; \
42*4882a593Smuzhiyun }
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun #define dw_pcm_rx_fn(sample_bits) \
45*4882a593Smuzhiyun static unsigned int dw_pcm_rx_##sample_bits(struct dw_i2s_dev *dev, \
46*4882a593Smuzhiyun 		struct snd_pcm_runtime *runtime, unsigned int rx_ptr, \
47*4882a593Smuzhiyun 		bool *period_elapsed) \
48*4882a593Smuzhiyun { \
49*4882a593Smuzhiyun 	u##sample_bits (*p)[2] = (void *)runtime->dma_area; \
50*4882a593Smuzhiyun 	unsigned int period_pos = rx_ptr % runtime->period_size; \
51*4882a593Smuzhiyun 	int i; \
52*4882a593Smuzhiyun \
53*4882a593Smuzhiyun 	for (i = 0; i < dev->fifo_th; i++) { \
54*4882a593Smuzhiyun 		p[rx_ptr][0] = ioread32(dev->i2s_base + LRBR_LTHR(0)); \
55*4882a593Smuzhiyun 		p[rx_ptr][1] = ioread32(dev->i2s_base + RRBR_RTHR(0)); \
56*4882a593Smuzhiyun 		period_pos++; \
57*4882a593Smuzhiyun 		if (++rx_ptr >= runtime->buffer_size) \
58*4882a593Smuzhiyun 			rx_ptr = 0; \
59*4882a593Smuzhiyun 	} \
60*4882a593Smuzhiyun 	*period_elapsed = period_pos >= runtime->period_size; \
61*4882a593Smuzhiyun 	return rx_ptr; \
62*4882a593Smuzhiyun }
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun dw_pcm_tx_fn(16);
65*4882a593Smuzhiyun dw_pcm_tx_fn(32);
66*4882a593Smuzhiyun dw_pcm_rx_fn(16);
67*4882a593Smuzhiyun dw_pcm_rx_fn(32);
68*4882a593Smuzhiyun 
69*4882a593Smuzhiyun #undef dw_pcm_tx_fn
70*4882a593Smuzhiyun #undef dw_pcm_rx_fn
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun static const struct snd_pcm_hardware dw_pcm_hardware = {
73*4882a593Smuzhiyun 	.info = SNDRV_PCM_INFO_INTERLEAVED |
74*4882a593Smuzhiyun 		SNDRV_PCM_INFO_MMAP |
75*4882a593Smuzhiyun 		SNDRV_PCM_INFO_MMAP_VALID |
76*4882a593Smuzhiyun 		SNDRV_PCM_INFO_BLOCK_TRANSFER,
77*4882a593Smuzhiyun 	.rates = SNDRV_PCM_RATE_32000 |
78*4882a593Smuzhiyun 		SNDRV_PCM_RATE_44100 |
79*4882a593Smuzhiyun 		SNDRV_PCM_RATE_48000,
80*4882a593Smuzhiyun 	.rate_min = 32000,
81*4882a593Smuzhiyun 	.rate_max = 48000,
82*4882a593Smuzhiyun 	.formats = SNDRV_PCM_FMTBIT_S16_LE |
83*4882a593Smuzhiyun 		SNDRV_PCM_FMTBIT_S24_LE |
84*4882a593Smuzhiyun 		SNDRV_PCM_FMTBIT_S32_LE,
85*4882a593Smuzhiyun 	.channels_min = 2,
86*4882a593Smuzhiyun 	.channels_max = 2,
87*4882a593Smuzhiyun 	.buffer_bytes_max = BUFFER_BYTES_MAX,
88*4882a593Smuzhiyun 	.period_bytes_min = PERIOD_BYTES_MIN,
89*4882a593Smuzhiyun 	.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN,
90*4882a593Smuzhiyun 	.periods_min = PERIODS_MIN,
91*4882a593Smuzhiyun 	.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
92*4882a593Smuzhiyun 	.fifo_size = 16,
93*4882a593Smuzhiyun };
94*4882a593Smuzhiyun 
dw_pcm_transfer(struct dw_i2s_dev * dev,bool push)95*4882a593Smuzhiyun static void dw_pcm_transfer(struct dw_i2s_dev *dev, bool push)
96*4882a593Smuzhiyun {
97*4882a593Smuzhiyun 	struct snd_pcm_substream *substream;
98*4882a593Smuzhiyun 	bool active, period_elapsed;
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun 	rcu_read_lock();
101*4882a593Smuzhiyun 	if (push)
102*4882a593Smuzhiyun 		substream = rcu_dereference(dev->tx_substream);
103*4882a593Smuzhiyun 	else
104*4882a593Smuzhiyun 		substream = rcu_dereference(dev->rx_substream);
105*4882a593Smuzhiyun 	active = substream && snd_pcm_running(substream);
106*4882a593Smuzhiyun 	if (active) {
107*4882a593Smuzhiyun 		unsigned int ptr;
108*4882a593Smuzhiyun 		unsigned int new_ptr;
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun 		if (push) {
111*4882a593Smuzhiyun 			ptr = READ_ONCE(dev->tx_ptr);
112*4882a593Smuzhiyun 			new_ptr = dev->tx_fn(dev, substream->runtime, ptr,
113*4882a593Smuzhiyun 					&period_elapsed);
114*4882a593Smuzhiyun 			cmpxchg(&dev->tx_ptr, ptr, new_ptr);
115*4882a593Smuzhiyun 		} else {
116*4882a593Smuzhiyun 			ptr = READ_ONCE(dev->rx_ptr);
117*4882a593Smuzhiyun 			new_ptr = dev->rx_fn(dev, substream->runtime, ptr,
118*4882a593Smuzhiyun 					&period_elapsed);
119*4882a593Smuzhiyun 			cmpxchg(&dev->rx_ptr, ptr, new_ptr);
120*4882a593Smuzhiyun 		}
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 		if (period_elapsed)
123*4882a593Smuzhiyun 			snd_pcm_period_elapsed(substream);
124*4882a593Smuzhiyun 	}
125*4882a593Smuzhiyun 	rcu_read_unlock();
126*4882a593Smuzhiyun }
127*4882a593Smuzhiyun 
dw_pcm_push_tx(struct dw_i2s_dev * dev)128*4882a593Smuzhiyun void dw_pcm_push_tx(struct dw_i2s_dev *dev)
129*4882a593Smuzhiyun {
130*4882a593Smuzhiyun 	dw_pcm_transfer(dev, true);
131*4882a593Smuzhiyun }
132*4882a593Smuzhiyun 
dw_pcm_pop_rx(struct dw_i2s_dev * dev)133*4882a593Smuzhiyun void dw_pcm_pop_rx(struct dw_i2s_dev *dev)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun 	dw_pcm_transfer(dev, false);
136*4882a593Smuzhiyun }
137*4882a593Smuzhiyun 
dw_pcm_open(struct snd_soc_component * component,struct snd_pcm_substream * substream)138*4882a593Smuzhiyun static int dw_pcm_open(struct snd_soc_component *component,
139*4882a593Smuzhiyun 		       struct snd_pcm_substream *substream)
140*4882a593Smuzhiyun {
141*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
142*4882a593Smuzhiyun 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
143*4882a593Smuzhiyun 	struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 	snd_soc_set_runtime_hwparams(substream, &dw_pcm_hardware);
146*4882a593Smuzhiyun 	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
147*4882a593Smuzhiyun 	runtime->private_data = dev;
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun 	return 0;
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun 
dw_pcm_close(struct snd_soc_component * component,struct snd_pcm_substream * substream)152*4882a593Smuzhiyun static int dw_pcm_close(struct snd_soc_component *component,
153*4882a593Smuzhiyun 			struct snd_pcm_substream *substream)
154*4882a593Smuzhiyun {
155*4882a593Smuzhiyun 	synchronize_rcu();
156*4882a593Smuzhiyun 	return 0;
157*4882a593Smuzhiyun }
158*4882a593Smuzhiyun 
dw_pcm_hw_params(struct snd_soc_component * component,struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)159*4882a593Smuzhiyun static int dw_pcm_hw_params(struct snd_soc_component *component,
160*4882a593Smuzhiyun 			    struct snd_pcm_substream *substream,
161*4882a593Smuzhiyun 			    struct snd_pcm_hw_params *hw_params)
162*4882a593Smuzhiyun {
163*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
164*4882a593Smuzhiyun 	struct dw_i2s_dev *dev = runtime->private_data;
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 	switch (params_channels(hw_params)) {
167*4882a593Smuzhiyun 	case 2:
168*4882a593Smuzhiyun 		break;
169*4882a593Smuzhiyun 	default:
170*4882a593Smuzhiyun 		dev_err(dev->dev, "invalid channels number\n");
171*4882a593Smuzhiyun 		return -EINVAL;
172*4882a593Smuzhiyun 	}
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun 	switch (params_format(hw_params)) {
175*4882a593Smuzhiyun 	case SNDRV_PCM_FORMAT_S16_LE:
176*4882a593Smuzhiyun 		dev->tx_fn = dw_pcm_tx_16;
177*4882a593Smuzhiyun 		dev->rx_fn = dw_pcm_rx_16;
178*4882a593Smuzhiyun 		break;
179*4882a593Smuzhiyun 	case SNDRV_PCM_FORMAT_S24_LE:
180*4882a593Smuzhiyun 	case SNDRV_PCM_FORMAT_S32_LE:
181*4882a593Smuzhiyun 		dev->tx_fn = dw_pcm_tx_32;
182*4882a593Smuzhiyun 		dev->rx_fn = dw_pcm_rx_32;
183*4882a593Smuzhiyun 		break;
184*4882a593Smuzhiyun 	default:
185*4882a593Smuzhiyun 		dev_err(dev->dev, "invalid format\n");
186*4882a593Smuzhiyun 		return -EINVAL;
187*4882a593Smuzhiyun 	}
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 	return 0;
190*4882a593Smuzhiyun }
191*4882a593Smuzhiyun 
dw_pcm_trigger(struct snd_soc_component * component,struct snd_pcm_substream * substream,int cmd)192*4882a593Smuzhiyun static int dw_pcm_trigger(struct snd_soc_component *component,
193*4882a593Smuzhiyun 			  struct snd_pcm_substream *substream, int cmd)
194*4882a593Smuzhiyun {
195*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
196*4882a593Smuzhiyun 	struct dw_i2s_dev *dev = runtime->private_data;
197*4882a593Smuzhiyun 	int ret = 0;
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun 	switch (cmd) {
200*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_START:
201*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_RESUME:
202*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
203*4882a593Smuzhiyun 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
204*4882a593Smuzhiyun 			WRITE_ONCE(dev->tx_ptr, 0);
205*4882a593Smuzhiyun 			rcu_assign_pointer(dev->tx_substream, substream);
206*4882a593Smuzhiyun 		} else {
207*4882a593Smuzhiyun 			WRITE_ONCE(dev->rx_ptr, 0);
208*4882a593Smuzhiyun 			rcu_assign_pointer(dev->rx_substream, substream);
209*4882a593Smuzhiyun 		}
210*4882a593Smuzhiyun 		break;
211*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_STOP:
212*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_SUSPEND:
213*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
214*4882a593Smuzhiyun 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
215*4882a593Smuzhiyun 			rcu_assign_pointer(dev->tx_substream, NULL);
216*4882a593Smuzhiyun 		else
217*4882a593Smuzhiyun 			rcu_assign_pointer(dev->rx_substream, NULL);
218*4882a593Smuzhiyun 		break;
219*4882a593Smuzhiyun 	default:
220*4882a593Smuzhiyun 		ret = -EINVAL;
221*4882a593Smuzhiyun 		break;
222*4882a593Smuzhiyun 	}
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun 	return ret;
225*4882a593Smuzhiyun }
226*4882a593Smuzhiyun 
dw_pcm_pointer(struct snd_soc_component * component,struct snd_pcm_substream * substream)227*4882a593Smuzhiyun static snd_pcm_uframes_t dw_pcm_pointer(struct snd_soc_component *component,
228*4882a593Smuzhiyun 					struct snd_pcm_substream *substream)
229*4882a593Smuzhiyun {
230*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
231*4882a593Smuzhiyun 	struct dw_i2s_dev *dev = runtime->private_data;
232*4882a593Smuzhiyun 	snd_pcm_uframes_t pos;
233*4882a593Smuzhiyun 
234*4882a593Smuzhiyun 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
235*4882a593Smuzhiyun 		pos = READ_ONCE(dev->tx_ptr);
236*4882a593Smuzhiyun 	else
237*4882a593Smuzhiyun 		pos = READ_ONCE(dev->rx_ptr);
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 	return pos < runtime->buffer_size ? pos : 0;
240*4882a593Smuzhiyun }
241*4882a593Smuzhiyun 
dw_pcm_new(struct snd_soc_component * component,struct snd_soc_pcm_runtime * rtd)242*4882a593Smuzhiyun static int dw_pcm_new(struct snd_soc_component *component,
243*4882a593Smuzhiyun 		      struct snd_soc_pcm_runtime *rtd)
244*4882a593Smuzhiyun {
245*4882a593Smuzhiyun 	size_t size = dw_pcm_hardware.buffer_bytes_max;
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun 	snd_pcm_set_managed_buffer_all(rtd->pcm,
248*4882a593Smuzhiyun 			SNDRV_DMA_TYPE_CONTINUOUS,
249*4882a593Smuzhiyun 			NULL, size, size);
250*4882a593Smuzhiyun 	return 0;
251*4882a593Smuzhiyun }
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun static const struct snd_soc_component_driver dw_pcm_component = {
254*4882a593Smuzhiyun 	.open		= dw_pcm_open,
255*4882a593Smuzhiyun 	.close		= dw_pcm_close,
256*4882a593Smuzhiyun 	.hw_params	= dw_pcm_hw_params,
257*4882a593Smuzhiyun 	.trigger	= dw_pcm_trigger,
258*4882a593Smuzhiyun 	.pointer	= dw_pcm_pointer,
259*4882a593Smuzhiyun 	.pcm_construct	= dw_pcm_new,
260*4882a593Smuzhiyun };
261*4882a593Smuzhiyun 
dw_pcm_register(struct platform_device * pdev)262*4882a593Smuzhiyun int dw_pcm_register(struct platform_device *pdev)
263*4882a593Smuzhiyun {
264*4882a593Smuzhiyun 	return devm_snd_soc_register_component(&pdev->dev, &dw_pcm_component,
265*4882a593Smuzhiyun 					       NULL, 0);
266*4882a593Smuzhiyun }
267