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