dwc-pcm.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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 <[email protected]>
  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_soc_component *component,
  125. struct snd_pcm_substream *substream)
  126. {
  127. struct snd_pcm_runtime *runtime = substream->runtime;
  128. struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
  129. struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
  130. snd_soc_set_runtime_hwparams(substream, &dw_pcm_hardware);
  131. snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
  132. runtime->private_data = dev;
  133. return 0;
  134. }
  135. static int dw_pcm_close(struct snd_soc_component *component,
  136. struct snd_pcm_substream *substream)
  137. {
  138. synchronize_rcu();
  139. return 0;
  140. }
  141. static int dw_pcm_hw_params(struct snd_soc_component *component,
  142. struct snd_pcm_substream *substream,
  143. struct snd_pcm_hw_params *hw_params)
  144. {
  145. struct snd_pcm_runtime *runtime = substream->runtime;
  146. struct dw_i2s_dev *dev = runtime->private_data;
  147. switch (params_channels(hw_params)) {
  148. case 2:
  149. break;
  150. default:
  151. dev_err(dev->dev, "invalid channels number\n");
  152. return -EINVAL;
  153. }
  154. switch (params_format(hw_params)) {
  155. case SNDRV_PCM_FORMAT_S16_LE:
  156. dev->tx_fn = dw_pcm_tx_16;
  157. dev->rx_fn = dw_pcm_rx_16;
  158. break;
  159. case SNDRV_PCM_FORMAT_S24_LE:
  160. case SNDRV_PCM_FORMAT_S32_LE:
  161. dev->tx_fn = dw_pcm_tx_32;
  162. dev->rx_fn = dw_pcm_rx_32;
  163. break;
  164. default:
  165. dev_err(dev->dev, "invalid format\n");
  166. return -EINVAL;
  167. }
  168. return 0;
  169. }
  170. static int dw_pcm_trigger(struct snd_soc_component *component,
  171. struct snd_pcm_substream *substream, int cmd)
  172. {
  173. struct snd_pcm_runtime *runtime = substream->runtime;
  174. struct dw_i2s_dev *dev = runtime->private_data;
  175. int ret = 0;
  176. switch (cmd) {
  177. case SNDRV_PCM_TRIGGER_START:
  178. case SNDRV_PCM_TRIGGER_RESUME:
  179. case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
  180. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  181. WRITE_ONCE(dev->tx_ptr, 0);
  182. rcu_assign_pointer(dev->tx_substream, substream);
  183. } else {
  184. WRITE_ONCE(dev->rx_ptr, 0);
  185. rcu_assign_pointer(dev->rx_substream, substream);
  186. }
  187. break;
  188. case SNDRV_PCM_TRIGGER_STOP:
  189. case SNDRV_PCM_TRIGGER_SUSPEND:
  190. case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
  191. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  192. rcu_assign_pointer(dev->tx_substream, NULL);
  193. else
  194. rcu_assign_pointer(dev->rx_substream, NULL);
  195. break;
  196. default:
  197. ret = -EINVAL;
  198. break;
  199. }
  200. return ret;
  201. }
  202. static snd_pcm_uframes_t dw_pcm_pointer(struct snd_soc_component *component,
  203. struct snd_pcm_substream *substream)
  204. {
  205. struct snd_pcm_runtime *runtime = substream->runtime;
  206. struct dw_i2s_dev *dev = runtime->private_data;
  207. snd_pcm_uframes_t pos;
  208. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  209. pos = READ_ONCE(dev->tx_ptr);
  210. else
  211. pos = READ_ONCE(dev->rx_ptr);
  212. return pos < runtime->buffer_size ? pos : 0;
  213. }
  214. static int dw_pcm_new(struct snd_soc_component *component,
  215. struct snd_soc_pcm_runtime *rtd)
  216. {
  217. size_t size = dw_pcm_hardware.buffer_bytes_max;
  218. snd_pcm_set_managed_buffer_all(rtd->pcm,
  219. SNDRV_DMA_TYPE_CONTINUOUS,
  220. NULL, size, size);
  221. return 0;
  222. }
  223. static const struct snd_soc_component_driver dw_pcm_component = {
  224. .open = dw_pcm_open,
  225. .close = dw_pcm_close,
  226. .hw_params = dw_pcm_hw_params,
  227. .trigger = dw_pcm_trigger,
  228. .pointer = dw_pcm_pointer,
  229. .pcm_construct = dw_pcm_new,
  230. };
  231. int dw_pcm_register(struct platform_device *pdev)
  232. {
  233. return devm_snd_soc_register_component(&pdev->dev, &dw_pcm_component,
  234. NULL, 0);
  235. }