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