1 /* 2 * ALSA PCM device for the 3 * ALSA interface to cx18 PCM capture streams 4 * 5 * Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net> 6 * Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com> 7 * 8 * Portions of this work were sponsored by ONELAN Limited. 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2 of the License, or 13 * (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 */ 20 21 #include <linux/init.h> 22 #include <linux/kernel.h> 23 #include <linux/vmalloc.h> 24 25 #include <media/v4l2-device.h> 26 27 #include <sound/core.h> 28 #include <sound/pcm.h> 29 30 #include "cx18-driver.h" 31 #include "cx18-queue.h" 32 #include "cx18-streams.h" 33 #include "cx18-fileops.h" 34 #include "cx18-alsa.h" 35 #include "cx18-alsa-pcm.h" 36 37 static unsigned int pcm_debug; 38 module_param(pcm_debug, int, 0644); 39 MODULE_PARM_DESC(pcm_debug, "enable debug messages for pcm"); 40 41 #define dprintk(fmt, arg...) do { \ 42 if (pcm_debug) \ 43 printk(KERN_INFO "cx18-alsa-pcm %s: " fmt, \ 44 __func__, ##arg); \ 45 } while (0) 46 47 static struct snd_pcm_hardware snd_cx18_hw_capture = { 48 .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | 49 SNDRV_PCM_INFO_MMAP | 50 SNDRV_PCM_INFO_INTERLEAVED | 51 SNDRV_PCM_INFO_MMAP_VALID, 52 53 .formats = SNDRV_PCM_FMTBIT_S16_LE, 54 55 .rates = SNDRV_PCM_RATE_48000, 56 57 .rate_min = 48000, 58 .rate_max = 48000, 59 .channels_min = 2, 60 .channels_max = 2, 61 .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */ 62 .period_bytes_min = 64, /* 12544/2, */ 63 .period_bytes_max = 12544, 64 .periods_min = 2, 65 .periods_max = 98, /* 12544, */ 66 }; 67 68 void cx18_alsa_announce_pcm_data(struct snd_cx18_card *cxsc, u8 *pcm_data, 69 size_t num_bytes) 70 { 71 struct snd_pcm_substream *substream; 72 struct snd_pcm_runtime *runtime; 73 unsigned int oldptr; 74 unsigned int stride; 75 int period_elapsed = 0; 76 int length; 77 78 dprintk("cx18 alsa announce ptr=%p data=%p num_bytes=%zu\n", cxsc, 79 pcm_data, num_bytes); 80 81 substream = cxsc->capture_pcm_substream; 82 if (substream == NULL) { 83 dprintk("substream was NULL\n"); 84 return; 85 } 86 87 runtime = substream->runtime; 88 if (runtime == NULL) { 89 dprintk("runtime was NULL\n"); 90 return; 91 } 92 93 stride = runtime->frame_bits >> 3; 94 if (stride == 0) { 95 dprintk("stride is zero\n"); 96 return; 97 } 98 99 length = num_bytes / stride; 100 if (length == 0) { 101 dprintk("%s: length was zero\n", __func__); 102 return; 103 } 104 105 if (runtime->dma_area == NULL) { 106 dprintk("dma area was NULL - ignoring\n"); 107 return; 108 } 109 110 oldptr = cxsc->hwptr_done_capture; 111 if (oldptr + length >= runtime->buffer_size) { 112 unsigned int cnt = 113 runtime->buffer_size - oldptr; 114 memcpy(runtime->dma_area + oldptr * stride, pcm_data, 115 cnt * stride); 116 memcpy(runtime->dma_area, pcm_data + cnt * stride, 117 length * stride - cnt * stride); 118 } else { 119 memcpy(runtime->dma_area + oldptr * stride, pcm_data, 120 length * stride); 121 } 122 snd_pcm_stream_lock(substream); 123 124 cxsc->hwptr_done_capture += length; 125 if (cxsc->hwptr_done_capture >= 126 runtime->buffer_size) 127 cxsc->hwptr_done_capture -= 128 runtime->buffer_size; 129 130 cxsc->capture_transfer_done += length; 131 if (cxsc->capture_transfer_done >= 132 runtime->period_size) { 133 cxsc->capture_transfer_done -= 134 runtime->period_size; 135 period_elapsed = 1; 136 } 137 138 snd_pcm_stream_unlock(substream); 139 140 if (period_elapsed) 141 snd_pcm_period_elapsed(substream); 142 } 143 144 static int snd_cx18_pcm_capture_open(struct snd_pcm_substream *substream) 145 { 146 struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream); 147 struct snd_pcm_runtime *runtime = substream->runtime; 148 struct v4l2_device *v4l2_dev = cxsc->v4l2_dev; 149 struct cx18 *cx = to_cx18(v4l2_dev); 150 struct cx18_stream *s; 151 struct cx18_open_id item; 152 int ret; 153 154 /* Instruct the cx18 to start sending packets */ 155 snd_cx18_lock(cxsc); 156 s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM]; 157 158 item.cx = cx; 159 item.type = s->type; 160 item.open_id = cx->open_id++; 161 162 /* See if the stream is available */ 163 if (cx18_claim_stream(&item, item.type)) { 164 /* No, it's already in use */ 165 snd_cx18_unlock(cxsc); 166 return -EBUSY; 167 } 168 169 if (test_bit(CX18_F_S_STREAMOFF, &s->s_flags) || 170 test_and_set_bit(CX18_F_S_STREAMING, &s->s_flags)) { 171 /* We're already streaming. No additional action required */ 172 snd_cx18_unlock(cxsc); 173 return 0; 174 } 175 176 177 runtime->hw = snd_cx18_hw_capture; 178 snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 179 cxsc->capture_pcm_substream = substream; 180 runtime->private_data = cx; 181 182 cx->pcm_announce_callback = cx18_alsa_announce_pcm_data; 183 184 /* Not currently streaming, so start it up */ 185 set_bit(CX18_F_S_STREAMING, &s->s_flags); 186 ret = cx18_start_v4l2_encode_stream(s); 187 snd_cx18_unlock(cxsc); 188 189 return ret; 190 } 191 192 static int snd_cx18_pcm_capture_close(struct snd_pcm_substream *substream) 193 { 194 struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream); 195 struct v4l2_device *v4l2_dev = cxsc->v4l2_dev; 196 struct cx18 *cx = to_cx18(v4l2_dev); 197 struct cx18_stream *s; 198 199 /* Instruct the cx18 to stop sending packets */ 200 snd_cx18_lock(cxsc); 201 s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM]; 202 cx18_stop_v4l2_encode_stream(s, 0); 203 clear_bit(CX18_F_S_STREAMING, &s->s_flags); 204 205 cx18_release_stream(s); 206 207 cx->pcm_announce_callback = NULL; 208 snd_cx18_unlock(cxsc); 209 210 return 0; 211 } 212 213 static int snd_cx18_pcm_ioctl(struct snd_pcm_substream *substream, 214 unsigned int cmd, void *arg) 215 { 216 struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream); 217 int ret; 218 219 snd_cx18_lock(cxsc); 220 ret = snd_pcm_lib_ioctl(substream, cmd, arg); 221 snd_cx18_unlock(cxsc); 222 return ret; 223 } 224 225 226 static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, 227 size_t size) 228 { 229 struct snd_pcm_runtime *runtime = subs->runtime; 230 231 dprintk("Allocating vbuffer\n"); 232 if (runtime->dma_area) { 233 if (runtime->dma_bytes > size) 234 return 0; 235 236 vfree(runtime->dma_area); 237 } 238 runtime->dma_area = vmalloc(size); 239 if (!runtime->dma_area) 240 return -ENOMEM; 241 242 runtime->dma_bytes = size; 243 244 return 0; 245 } 246 247 static int snd_cx18_pcm_hw_params(struct snd_pcm_substream *substream, 248 struct snd_pcm_hw_params *params) 249 { 250 dprintk("%s called\n", __func__); 251 252 return snd_pcm_alloc_vmalloc_buffer(substream, 253 params_buffer_bytes(params)); 254 } 255 256 static int snd_cx18_pcm_hw_free(struct snd_pcm_substream *substream) 257 { 258 struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream); 259 unsigned long flags; 260 261 spin_lock_irqsave(&cxsc->slock, flags); 262 if (substream->runtime->dma_area) { 263 dprintk("freeing pcm capture region\n"); 264 vfree(substream->runtime->dma_area); 265 substream->runtime->dma_area = NULL; 266 } 267 spin_unlock_irqrestore(&cxsc->slock, flags); 268 269 return 0; 270 } 271 272 static int snd_cx18_pcm_prepare(struct snd_pcm_substream *substream) 273 { 274 struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream); 275 276 cxsc->hwptr_done_capture = 0; 277 cxsc->capture_transfer_done = 0; 278 279 return 0; 280 } 281 282 static int snd_cx18_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 283 { 284 return 0; 285 } 286 287 static 288 snd_pcm_uframes_t snd_cx18_pcm_pointer(struct snd_pcm_substream *substream) 289 { 290 unsigned long flags; 291 snd_pcm_uframes_t hwptr_done; 292 struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream); 293 294 spin_lock_irqsave(&cxsc->slock, flags); 295 hwptr_done = cxsc->hwptr_done_capture; 296 spin_unlock_irqrestore(&cxsc->slock, flags); 297 298 return hwptr_done; 299 } 300 301 static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, 302 unsigned long offset) 303 { 304 void *pageptr = subs->runtime->dma_area + offset; 305 306 return vmalloc_to_page(pageptr); 307 } 308 309 static const struct snd_pcm_ops snd_cx18_pcm_capture_ops = { 310 .open = snd_cx18_pcm_capture_open, 311 .close = snd_cx18_pcm_capture_close, 312 .ioctl = snd_cx18_pcm_ioctl, 313 .hw_params = snd_cx18_pcm_hw_params, 314 .hw_free = snd_cx18_pcm_hw_free, 315 .prepare = snd_cx18_pcm_prepare, 316 .trigger = snd_cx18_pcm_trigger, 317 .pointer = snd_cx18_pcm_pointer, 318 .page = snd_pcm_get_vmalloc_page, 319 }; 320 321 int snd_cx18_pcm_create(struct snd_cx18_card *cxsc) 322 { 323 struct snd_pcm *sp; 324 struct snd_card *sc = cxsc->sc; 325 struct v4l2_device *v4l2_dev = cxsc->v4l2_dev; 326 struct cx18 *cx = to_cx18(v4l2_dev); 327 int ret; 328 329 ret = snd_pcm_new(sc, "CX23418 PCM", 330 0, /* PCM device 0, the only one for this card */ 331 0, /* 0 playback substreams */ 332 1, /* 1 capture substream */ 333 &sp); 334 if (ret) { 335 CX18_ALSA_ERR("%s: snd_cx18_pcm_create() failed with err %d\n", 336 __func__, ret); 337 goto err_exit; 338 } 339 340 spin_lock_init(&cxsc->slock); 341 342 snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE, 343 &snd_cx18_pcm_capture_ops); 344 sp->info_flags = 0; 345 sp->private_data = cxsc; 346 strlcpy(sp->name, cx->card_name, sizeof(sp->name)); 347 348 return 0; 349 350 err_exit: 351 return ret; 352 } 353