cx18-alsa-pcm.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * ALSA PCM device for the
  4. * ALSA interface to cx18 PCM capture streams
  5. *
  6. * Copyright (C) 2009 Andy Walls <[email protected]>
  7. * Copyright (C) 2009 Devin Heitmueller <[email protected]>
  8. *
  9. * Portions of this work were sponsored by ONELAN Limited.
  10. */
  11. #include <linux/init.h>
  12. #include <linux/kernel.h>
  13. #include <media/v4l2-device.h>
  14. #include <sound/core.h>
  15. #include <sound/pcm.h>
  16. #include "cx18-driver.h"
  17. #include "cx18-queue.h"
  18. #include "cx18-streams.h"
  19. #include "cx18-fileops.h"
  20. #include "cx18-alsa.h"
  21. #include "cx18-alsa-pcm.h"
  22. static unsigned int pcm_debug;
  23. module_param(pcm_debug, int, 0644);
  24. MODULE_PARM_DESC(pcm_debug, "enable debug messages for pcm");
  25. #define dprintk(fmt, arg...) do { \
  26. if (pcm_debug) \
  27. printk(KERN_INFO "cx18-alsa-pcm %s: " fmt, \
  28. __func__, ##arg); \
  29. } while (0)
  30. static const struct snd_pcm_hardware snd_cx18_hw_capture = {
  31. .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
  32. SNDRV_PCM_INFO_MMAP |
  33. SNDRV_PCM_INFO_INTERLEAVED |
  34. SNDRV_PCM_INFO_MMAP_VALID,
  35. .formats = SNDRV_PCM_FMTBIT_S16_LE,
  36. .rates = SNDRV_PCM_RATE_48000,
  37. .rate_min = 48000,
  38. .rate_max = 48000,
  39. .channels_min = 2,
  40. .channels_max = 2,
  41. .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */
  42. .period_bytes_min = 64, /* 12544/2, */
  43. .period_bytes_max = 12544,
  44. .periods_min = 2,
  45. .periods_max = 98, /* 12544, */
  46. };
  47. void cx18_alsa_announce_pcm_data(struct snd_cx18_card *cxsc, u8 *pcm_data,
  48. size_t num_bytes)
  49. {
  50. struct snd_pcm_substream *substream;
  51. struct snd_pcm_runtime *runtime;
  52. unsigned int oldptr;
  53. unsigned int stride;
  54. int period_elapsed = 0;
  55. int length;
  56. dprintk("cx18 alsa announce ptr=%p data=%p num_bytes=%zu\n", cxsc,
  57. pcm_data, num_bytes);
  58. substream = cxsc->capture_pcm_substream;
  59. if (substream == NULL) {
  60. dprintk("substream was NULL\n");
  61. return;
  62. }
  63. runtime = substream->runtime;
  64. if (runtime == NULL) {
  65. dprintk("runtime was NULL\n");
  66. return;
  67. }
  68. stride = runtime->frame_bits >> 3;
  69. if (stride == 0) {
  70. dprintk("stride is zero\n");
  71. return;
  72. }
  73. length = num_bytes / stride;
  74. if (length == 0) {
  75. dprintk("%s: length was zero\n", __func__);
  76. return;
  77. }
  78. if (runtime->dma_area == NULL) {
  79. dprintk("dma area was NULL - ignoring\n");
  80. return;
  81. }
  82. oldptr = cxsc->hwptr_done_capture;
  83. if (oldptr + length >= runtime->buffer_size) {
  84. unsigned int cnt =
  85. runtime->buffer_size - oldptr;
  86. memcpy(runtime->dma_area + oldptr * stride, pcm_data,
  87. cnt * stride);
  88. memcpy(runtime->dma_area, pcm_data + cnt * stride,
  89. length * stride - cnt * stride);
  90. } else {
  91. memcpy(runtime->dma_area + oldptr * stride, pcm_data,
  92. length * stride);
  93. }
  94. snd_pcm_stream_lock(substream);
  95. cxsc->hwptr_done_capture += length;
  96. if (cxsc->hwptr_done_capture >=
  97. runtime->buffer_size)
  98. cxsc->hwptr_done_capture -=
  99. runtime->buffer_size;
  100. cxsc->capture_transfer_done += length;
  101. if (cxsc->capture_transfer_done >=
  102. runtime->period_size) {
  103. cxsc->capture_transfer_done -=
  104. runtime->period_size;
  105. period_elapsed = 1;
  106. }
  107. snd_pcm_stream_unlock(substream);
  108. if (period_elapsed)
  109. snd_pcm_period_elapsed(substream);
  110. }
  111. static int snd_cx18_pcm_capture_open(struct snd_pcm_substream *substream)
  112. {
  113. struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
  114. struct snd_pcm_runtime *runtime = substream->runtime;
  115. struct v4l2_device *v4l2_dev = cxsc->v4l2_dev;
  116. struct cx18 *cx = to_cx18(v4l2_dev);
  117. struct cx18_stream *s;
  118. struct cx18_open_id item;
  119. int ret;
  120. /* Instruct the cx18 to start sending packets */
  121. snd_cx18_lock(cxsc);
  122. s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM];
  123. item.cx = cx;
  124. item.type = s->type;
  125. item.open_id = cx->open_id++;
  126. /* See if the stream is available */
  127. if (cx18_claim_stream(&item, item.type)) {
  128. /* No, it's already in use */
  129. snd_cx18_unlock(cxsc);
  130. return -EBUSY;
  131. }
  132. if (test_bit(CX18_F_S_STREAMOFF, &s->s_flags) ||
  133. test_and_set_bit(CX18_F_S_STREAMING, &s->s_flags)) {
  134. /* We're already streaming. No additional action required */
  135. snd_cx18_unlock(cxsc);
  136. return 0;
  137. }
  138. runtime->hw = snd_cx18_hw_capture;
  139. snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
  140. cxsc->capture_pcm_substream = substream;
  141. runtime->private_data = cx;
  142. cx->pcm_announce_callback = cx18_alsa_announce_pcm_data;
  143. /* Not currently streaming, so start it up */
  144. set_bit(CX18_F_S_STREAMING, &s->s_flags);
  145. ret = cx18_start_v4l2_encode_stream(s);
  146. snd_cx18_unlock(cxsc);
  147. return ret;
  148. }
  149. static int snd_cx18_pcm_capture_close(struct snd_pcm_substream *substream)
  150. {
  151. struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
  152. struct v4l2_device *v4l2_dev = cxsc->v4l2_dev;
  153. struct cx18 *cx = to_cx18(v4l2_dev);
  154. struct cx18_stream *s;
  155. /* Instruct the cx18 to stop sending packets */
  156. snd_cx18_lock(cxsc);
  157. s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM];
  158. cx18_stop_v4l2_encode_stream(s, 0);
  159. clear_bit(CX18_F_S_STREAMING, &s->s_flags);
  160. cx18_release_stream(s);
  161. cx->pcm_announce_callback = NULL;
  162. snd_cx18_unlock(cxsc);
  163. return 0;
  164. }
  165. static int snd_cx18_pcm_prepare(struct snd_pcm_substream *substream)
  166. {
  167. struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
  168. cxsc->hwptr_done_capture = 0;
  169. cxsc->capture_transfer_done = 0;
  170. return 0;
  171. }
  172. static int snd_cx18_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
  173. {
  174. return 0;
  175. }
  176. static
  177. snd_pcm_uframes_t snd_cx18_pcm_pointer(struct snd_pcm_substream *substream)
  178. {
  179. unsigned long flags;
  180. snd_pcm_uframes_t hwptr_done;
  181. struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
  182. spin_lock_irqsave(&cxsc->slock, flags);
  183. hwptr_done = cxsc->hwptr_done_capture;
  184. spin_unlock_irqrestore(&cxsc->slock, flags);
  185. return hwptr_done;
  186. }
  187. static const struct snd_pcm_ops snd_cx18_pcm_capture_ops = {
  188. .open = snd_cx18_pcm_capture_open,
  189. .close = snd_cx18_pcm_capture_close,
  190. .prepare = snd_cx18_pcm_prepare,
  191. .trigger = snd_cx18_pcm_trigger,
  192. .pointer = snd_cx18_pcm_pointer,
  193. };
  194. int snd_cx18_pcm_create(struct snd_cx18_card *cxsc)
  195. {
  196. struct snd_pcm *sp;
  197. struct snd_card *sc = cxsc->sc;
  198. struct v4l2_device *v4l2_dev = cxsc->v4l2_dev;
  199. struct cx18 *cx = to_cx18(v4l2_dev);
  200. int ret;
  201. ret = snd_pcm_new(sc, "CX23418 PCM",
  202. 0, /* PCM device 0, the only one for this card */
  203. 0, /* 0 playback substreams */
  204. 1, /* 1 capture substream */
  205. &sp);
  206. if (ret) {
  207. CX18_ALSA_ERR("%s: snd_cx18_pcm_create() failed with err %d\n",
  208. __func__, ret);
  209. goto err_exit;
  210. }
  211. spin_lock_init(&cxsc->slock);
  212. snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE,
  213. &snd_cx18_pcm_capture_ops);
  214. snd_pcm_set_managed_buffer_all(sp, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
  215. sp->info_flags = 0;
  216. sp->private_data = cxsc;
  217. strscpy(sp->name, cx->card_name, sizeof(sp->name));
  218. return 0;
  219. err_exit:
  220. return ret;
  221. }