1 // SPDX-License-Identifier: GPL-2.0
3 ******************************************************************************
5 * @author StarFive Technology
9 ******************************************************************************
12 * THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
13 * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
14 * TIME. AS A RESULT, STARFIVE SHALL NOT BE HELD LIABLE FOR ANY
15 * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
16 * FROM THE CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
17 * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
19 * <h2><center>© COPYRIGHT 20120 Shanghai StarFive Technology Co., Ltd. </center></h2>
23 #include <linux/rcupdate.h>
24 #include <sound/pcm.h>
25 #include <sound/pcm_params.h>
26 #include "starfive_spdif.h"
28 #define BUFFER_BYTES_MAX (3 * 2 * 8 * PERIOD_BYTES_MIN)
29 #define PERIOD_BYTES_MIN 4096
32 static unsigned int sf_spdif_pcm_tx(struct sf_spdif_dev *dev,
33 struct snd_pcm_runtime *runtime, unsigned int tx_ptr,
34 bool *period_elapsed, snd_pcm_format_t format)
37 unsigned int period_pos = tx_ptr % runtime->period_size;
40 /* two- channel and signal-channel mode */
42 const u16 (*p16)[2] = (void *)runtime->dma_area;
43 const u32 (*p32)[2] = (void *)runtime->dma_area;
45 for (i = 0; i < dev->fifo_th; i++) {
46 if (format == SNDRV_PCM_FORMAT_S16_LE) {
47 data[0] = p16[tx_ptr][0];
49 data[0] &= 0x00ffff00;
50 data[1] = p16[tx_ptr][1];
52 data[1] &= 0x00ffff00;
53 } else if (format == SNDRV_PCM_FORMAT_S24_LE) {
54 data[0] = p32[tx_ptr][0];
55 data[1] = p32[tx_ptr][1];
58 * To adapt S24_3LE and ALSA pass parameter of S24_LE.
59 * operation of S24_LE should be same to S24_3LE.
60 * So it would wrong when playback S24_LE file.
61 * when want to playback S24_LE file, should add in there:
62 * data[0] = data[0]>>8;
63 * data[1] = data[1]>>8;
66 data[0] &= 0x00ffffff;
67 data[1] &= 0x00ffffff;
68 } else if (format == SNDRV_PCM_FORMAT_S24_3LE) {
69 data[0] = p32[tx_ptr][0];
70 data[1] = p32[tx_ptr][1];
71 data[0] &= 0x00ffffff;
72 data[1] &= 0x00ffffff;
73 } else if (format == SNDRV_PCM_FORMAT_S32_LE) {
74 data[0] = p32[tx_ptr][0];
76 data[1] = p32[tx_ptr][1];
80 iowrite32(data[0], dev->spdif_base + SPDIF_FIFO_ADDR);
81 iowrite32(data[1], dev->spdif_base + SPDIF_FIFO_ADDR);
83 if (++tx_ptr >= runtime->buffer_size)
87 const u16 (*p16) = (void *)runtime->dma_area;
88 const u32 (*p32) = (void *)runtime->dma_area;
90 for (i = 0; i < dev->fifo_th; i++) {
91 if (format == SNDRV_PCM_FORMAT_S16_LE) {
92 data[0] = p16[tx_ptr];
94 data[0] &= 0x00ffff00;
95 } else if (format == SNDRV_PCM_FORMAT_S24_LE ||
96 format == SNDRV_PCM_FORMAT_S24_3LE) {
97 data[0] = p32[tx_ptr];
98 data[0] &= 0x00ffffff;
99 } else if (format == SNDRV_PCM_FORMAT_S32_LE) {
100 data[0] = p32[tx_ptr];
101 data[0] = data[0]>>8;
104 iowrite32(data[0], dev->spdif_base + SPDIF_FIFO_ADDR);
106 if (++tx_ptr >= runtime->buffer_size)
111 *period_elapsed = period_pos >= runtime->period_size;
115 static unsigned int sf_spdif_pcm_rx(struct sf_spdif_dev *dev,
116 struct snd_pcm_runtime *runtime, unsigned int rx_ptr,
117 bool *period_elapsed, snd_pcm_format_t format)
119 u16 (*p16)[2] = (void *)runtime->dma_area;
120 u32 (*p32)[2] = (void *)runtime->dma_area;
122 unsigned int period_pos = rx_ptr % runtime->period_size;
125 for (i = 0; i < dev->fifo_th; i++) {
126 data[0] = ioread32(dev->spdif_base + SPDIF_FIFO_ADDR);
127 data[1] = ioread32(dev->spdif_base + SPDIF_FIFO_ADDR);
128 if (format == SNDRV_PCM_FORMAT_S16_LE) {
129 p16[rx_ptr][0] = data[0]>>8;
130 p16[rx_ptr][1] = data[1]>>8;
131 } else if (format == SNDRV_PCM_FORMAT_S24_LE) {
132 p32[rx_ptr][0] = data[0];
133 p32[rx_ptr][1] = data[1];
134 } else if (format == SNDRV_PCM_FORMAT_S32_LE) {
135 p32[rx_ptr][0] = data[0]<<8;
136 p32[rx_ptr][1] = data[1]<<8;
140 if (++rx_ptr >= runtime->buffer_size)
144 *period_elapsed = period_pos >= runtime->period_size;
148 static const struct snd_pcm_hardware sf_pcm_hardware = {
149 .info = SNDRV_PCM_INFO_INTERLEAVED |
150 SNDRV_PCM_INFO_MMAP |
151 SNDRV_PCM_INFO_MMAP_VALID |
152 SNDRV_PCM_INFO_BLOCK_TRANSFER |
153 SNDRV_PCM_INFO_PAUSE |
154 SNDRV_PCM_INFO_RESUME,
155 .rates = SNDRV_PCM_RATE_8000 |
156 SNDRV_PCM_RATE_11025 |
157 SNDRV_PCM_RATE_16000 |
158 SNDRV_PCM_RATE_22050 |
159 SNDRV_PCM_RATE_32000 |
160 SNDRV_PCM_RATE_44100 |
161 SNDRV_PCM_RATE_48000,
164 .formats = SNDRV_PCM_FMTBIT_S16_LE |
165 SNDRV_PCM_FMTBIT_S24_LE |
166 SNDRV_PCM_FMTBIT_S24_3LE |
167 SNDRV_PCM_FMTBIT_S32_LE,
170 .buffer_bytes_max = BUFFER_BYTES_MAX,
171 .period_bytes_min = PERIOD_BYTES_MIN,
172 .period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN,
173 .periods_min = PERIODS_MIN,
174 .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
178 static void sf_spdif_pcm_transfer(struct sf_spdif_dev *dev, bool push)
180 struct snd_pcm_substream *substream;
181 bool active, period_elapsed;
185 substream = rcu_dereference(dev->tx_substream);
187 substream = rcu_dereference(dev->rx_substream);
188 active = substream && snd_pcm_running(substream);
191 unsigned int new_ptr;
194 ptr = READ_ONCE(dev->tx_ptr);
195 new_ptr = dev->tx_fn(dev, substream->runtime, ptr,
196 &period_elapsed, dev->format);
197 cmpxchg(&dev->tx_ptr, ptr, new_ptr);
199 ptr = READ_ONCE(dev->rx_ptr);
200 new_ptr = dev->rx_fn(dev, substream->runtime, ptr,
201 &period_elapsed, dev->format);
202 cmpxchg(&dev->rx_ptr, ptr, new_ptr);
206 snd_pcm_period_elapsed(substream);
211 void sf_spdif_pcm_push_tx(struct sf_spdif_dev *dev)
213 sf_spdif_pcm_transfer(dev, true);
216 void sf_spdif_pcm_pop_rx(struct sf_spdif_dev *dev)
218 sf_spdif_pcm_transfer(dev, false);
221 static int sf_pcm_open(struct snd_soc_component *component,
222 struct snd_pcm_substream *substream)
224 struct snd_pcm_runtime *runtime = substream->runtime;
225 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
226 struct sf_spdif_dev *dev = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
228 snd_soc_set_runtime_hwparams(substream, &sf_pcm_hardware);
229 snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
230 runtime->private_data = dev;
235 static int sf_pcm_close(struct snd_soc_component *component,
236 struct snd_pcm_substream *substream)
242 static int sf_pcm_hw_params(struct snd_soc_component *component,
243 struct snd_pcm_substream *substream,
244 struct snd_pcm_hw_params *hw_params)
246 struct snd_pcm_runtime *runtime = substream->runtime;
247 struct sf_spdif_dev *dev = runtime->private_data;
249 switch (params_channels(hw_params)) {
254 dev_err(dev->dev, "invalid channels number\n");
258 dev->format = params_format(hw_params);
259 switch (dev->format) {
260 case SNDRV_PCM_FORMAT_S16_LE:
261 case SNDRV_PCM_FORMAT_S24_LE:
262 case SNDRV_PCM_FORMAT_S24_3LE:
263 case SNDRV_PCM_FORMAT_S32_LE:
266 dev_err(dev->dev, "invalid format\n");
270 dev->tx_fn = sf_spdif_pcm_tx;
271 dev->rx_fn = sf_spdif_pcm_rx;
276 static int sf_pcm_trigger(struct snd_soc_component *component,
277 struct snd_pcm_substream *substream, int cmd)
279 struct snd_pcm_runtime *runtime = substream->runtime;
280 struct sf_spdif_dev *dev = runtime->private_data;
284 case SNDRV_PCM_TRIGGER_START:
285 case SNDRV_PCM_TRIGGER_RESUME:
286 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
287 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
288 WRITE_ONCE(dev->tx_ptr, 0);
289 rcu_assign_pointer(dev->tx_substream, substream);
291 WRITE_ONCE(dev->rx_ptr, 0);
292 rcu_assign_pointer(dev->rx_substream, substream);
295 case SNDRV_PCM_TRIGGER_STOP:
296 case SNDRV_PCM_TRIGGER_SUSPEND:
297 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
298 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
299 rcu_assign_pointer(dev->tx_substream, NULL);
301 rcu_assign_pointer(dev->rx_substream, NULL);
311 static snd_pcm_uframes_t sf_pcm_pointer(struct snd_soc_component *component,
312 struct snd_pcm_substream *substream)
314 struct snd_pcm_runtime *runtime = substream->runtime;
315 struct sf_spdif_dev *dev = runtime->private_data;
316 snd_pcm_uframes_t pos;
318 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
319 pos = READ_ONCE(dev->tx_ptr);
321 pos = READ_ONCE(dev->rx_ptr);
323 return pos < runtime->buffer_size ? pos : 0;
326 static int sf_pcm_new(struct snd_soc_component *component,
327 struct snd_soc_pcm_runtime *rtd)
329 size_t size = sf_pcm_hardware.buffer_bytes_max;
331 snd_pcm_set_managed_buffer_all(rtd->pcm,
332 SNDRV_DMA_TYPE_CONTINUOUS,
338 static const struct snd_soc_component_driver sf_pcm_component = {
340 .close = sf_pcm_close,
341 .hw_params = sf_pcm_hw_params,
342 .trigger = sf_pcm_trigger,
343 .pointer = sf_pcm_pointer,
344 .pcm_construct = sf_pcm_new,
347 int sf_spdif_pcm_register(struct platform_device *pdev)
349 return devm_snd_soc_register_component(&pdev->dev, &sf_pcm_component,