designware_pcm.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  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. dw_pcm_tx_fn(16);
  41. dw_pcm_tx_fn(32);
  42. #undef dw_pcm_tx_fn
  43. static const struct snd_pcm_hardware dw_pcm_hardware = {
  44. .info = SNDRV_PCM_INFO_INTERLEAVED |
  45. SNDRV_PCM_INFO_MMAP |
  46. SNDRV_PCM_INFO_MMAP_VALID |
  47. SNDRV_PCM_INFO_BLOCK_TRANSFER,
  48. .rates = SNDRV_PCM_RATE_32000 |
  49. SNDRV_PCM_RATE_44100 |
  50. SNDRV_PCM_RATE_48000,
  51. .rate_min = 32000,
  52. .rate_max = 48000,
  53. .formats = SNDRV_PCM_FMTBIT_S16_LE |
  54. SNDRV_PCM_FMTBIT_S32_LE,
  55. .channels_min = 2,
  56. .channels_max = 2,
  57. .buffer_bytes_max = BUFFER_BYTES_MAX,
  58. .period_bytes_min = PERIOD_BYTES_MIN,
  59. .period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN,
  60. .periods_min = PERIODS_MIN,
  61. .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
  62. .fifo_size = 16,
  63. };
  64. void dw_pcm_push_tx(struct dw_i2s_dev *dev)
  65. {
  66. struct snd_pcm_substream *tx_substream;
  67. bool tx_active, period_elapsed;
  68. rcu_read_lock();
  69. tx_substream = rcu_dereference(dev->tx_substream);
  70. tx_active = tx_substream && snd_pcm_running(tx_substream);
  71. if (tx_active) {
  72. unsigned int tx_ptr = READ_ONCE(dev->tx_ptr);
  73. unsigned int new_tx_ptr = dev->tx_fn(dev, tx_substream->runtime,
  74. tx_ptr, &period_elapsed);
  75. cmpxchg(&dev->tx_ptr, tx_ptr, new_tx_ptr);
  76. if (period_elapsed)
  77. snd_pcm_period_elapsed(tx_substream);
  78. }
  79. rcu_read_unlock();
  80. }
  81. EXPORT_SYMBOL_GPL(dw_pcm_push_tx);
  82. static int dw_pcm_open(struct snd_pcm_substream *substream)
  83. {
  84. struct snd_pcm_runtime *runtime = substream->runtime;
  85. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  86. struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(rtd->cpu_dai);
  87. snd_soc_set_runtime_hwparams(substream, &dw_pcm_hardware);
  88. snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
  89. runtime->private_data = dev;
  90. return 0;
  91. }
  92. static int dw_pcm_close(struct snd_pcm_substream *substream)
  93. {
  94. synchronize_rcu();
  95. return 0;
  96. }
  97. static int dw_pcm_hw_params(struct snd_pcm_substream *substream,
  98. struct snd_pcm_hw_params *hw_params)
  99. {
  100. struct snd_pcm_runtime *runtime = substream->runtime;
  101. struct dw_i2s_dev *dev = runtime->private_data;
  102. int ret;
  103. switch (params_channels(hw_params)) {
  104. case 2:
  105. break;
  106. default:
  107. dev_err(dev->dev, "invalid channels number\n");
  108. return -EINVAL;
  109. }
  110. switch (params_format(hw_params)) {
  111. case SNDRV_PCM_FORMAT_S16_LE:
  112. dev->tx_fn = dw_pcm_tx_16;
  113. break;
  114. case SNDRV_PCM_FORMAT_S32_LE:
  115. dev->tx_fn = dw_pcm_tx_32;
  116. break;
  117. default:
  118. dev_err(dev->dev, "invalid format\n");
  119. return -EINVAL;
  120. }
  121. if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) {
  122. dev_err(dev->dev, "only playback is available\n");
  123. return -EINVAL;
  124. }
  125. ret = snd_pcm_lib_malloc_pages(substream,
  126. params_buffer_bytes(hw_params));
  127. if (ret < 0)
  128. return ret;
  129. else
  130. return 0;
  131. }
  132. static int dw_pcm_hw_free(struct snd_pcm_substream *substream)
  133. {
  134. return snd_pcm_lib_free_pages(substream);
  135. }
  136. static int dw_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
  137. {
  138. struct snd_pcm_runtime *runtime = substream->runtime;
  139. struct dw_i2s_dev *dev = runtime->private_data;
  140. int ret = 0;
  141. switch (cmd) {
  142. case SNDRV_PCM_TRIGGER_START:
  143. case SNDRV_PCM_TRIGGER_RESUME:
  144. case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
  145. WRITE_ONCE(dev->tx_ptr, 0);
  146. rcu_assign_pointer(dev->tx_substream, substream);
  147. break;
  148. case SNDRV_PCM_TRIGGER_STOP:
  149. case SNDRV_PCM_TRIGGER_SUSPEND:
  150. case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
  151. rcu_assign_pointer(dev->tx_substream, NULL);
  152. break;
  153. default:
  154. ret = -EINVAL;
  155. break;
  156. }
  157. return ret;
  158. }
  159. static snd_pcm_uframes_t dw_pcm_pointer(struct snd_pcm_substream *substream)
  160. {
  161. struct snd_pcm_runtime *runtime = substream->runtime;
  162. struct dw_i2s_dev *dev = runtime->private_data;
  163. snd_pcm_uframes_t pos = READ_ONCE(dev->tx_ptr);
  164. return pos < runtime->buffer_size ? pos : 0;
  165. }
  166. static int dw_pcm_new(struct snd_soc_pcm_runtime *rtd)
  167. {
  168. size_t size = dw_pcm_hardware.buffer_bytes_max;
  169. return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
  170. SNDRV_DMA_TYPE_CONTINUOUS,
  171. snd_dma_continuous_data(GFP_KERNEL), size, size);
  172. }
  173. static void dw_pcm_free(struct snd_pcm *pcm)
  174. {
  175. snd_pcm_lib_preallocate_free_for_all(pcm);
  176. }
  177. static const struct snd_pcm_ops dw_pcm_ops = {
  178. .open = dw_pcm_open,
  179. .close = dw_pcm_close,
  180. .ioctl = snd_pcm_lib_ioctl,
  181. .hw_params = dw_pcm_hw_params,
  182. .hw_free = dw_pcm_hw_free,
  183. .trigger = dw_pcm_trigger,
  184. .pointer = dw_pcm_pointer,
  185. };
  186. static const struct snd_soc_platform_driver dw_pcm_platform = {
  187. .pcm_new = dw_pcm_new,
  188. .pcm_free = dw_pcm_free,
  189. .ops = &dw_pcm_ops,
  190. };
  191. int dw_pcm_register(struct platform_device *pdev)
  192. {
  193. return devm_snd_soc_register_platform(&pdev->dev, &dw_pcm_platform);
  194. }
  195. EXPORT_SYMBOL_GPL(dw_pcm_register);