dwc-pcm.c 7.0 KB

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