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