1269c11fbSAndy Walls /* 2269c11fbSAndy Walls * ALSA PCM device for the 3269c11fbSAndy Walls * ALSA interface to ivtv PCM capture streams 4269c11fbSAndy Walls * 5269c11fbSAndy Walls * Copyright (C) 2009,2012 Andy Walls <awalls@md.metrocast.net> 6269c11fbSAndy Walls * Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com> 7269c11fbSAndy Walls * 8269c11fbSAndy Walls * Portions of this work were sponsored by ONELAN Limited for the cx18 driver 9269c11fbSAndy Walls * 10269c11fbSAndy Walls * This program is free software; you can redistribute it and/or modify 11269c11fbSAndy Walls * it under the terms of the GNU General Public License as published by 12269c11fbSAndy Walls * the Free Software Foundation; either version 2 of the License, or 13269c11fbSAndy Walls * (at your option) any later version. 14269c11fbSAndy Walls * 15269c11fbSAndy Walls * This program is distributed in the hope that it will be useful, 16269c11fbSAndy Walls * but WITHOUT ANY WARRANTY; without even the implied warranty of 17269c11fbSAndy Walls * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18269c11fbSAndy Walls * GNU General Public License for more details. 19269c11fbSAndy Walls * 20269c11fbSAndy Walls * You should have received a copy of the GNU General Public License 21269c11fbSAndy Walls * along with this program; if not, write to the Free Software 22269c11fbSAndy Walls * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 23269c11fbSAndy Walls * 02111-1307 USA 24269c11fbSAndy Walls */ 25269c11fbSAndy Walls 26269c11fbSAndy Walls #include <linux/init.h> 27269c11fbSAndy Walls #include <linux/kernel.h> 28269c11fbSAndy Walls #include <linux/vmalloc.h> 29269c11fbSAndy Walls 30269c11fbSAndy Walls #include <media/v4l2-device.h> 31269c11fbSAndy Walls 32269c11fbSAndy Walls #include <sound/core.h> 33269c11fbSAndy Walls #include <sound/pcm.h> 34269c11fbSAndy Walls 35269c11fbSAndy Walls #include "ivtv-driver.h" 36269c11fbSAndy Walls #include "ivtv-queue.h" 37269c11fbSAndy Walls #include "ivtv-streams.h" 38269c11fbSAndy Walls #include "ivtv-fileops.h" 39269c11fbSAndy Walls #include "ivtv-alsa.h" 402aebbf67SMauro Carvalho Chehab #include "ivtv-alsa-pcm.h" 41269c11fbSAndy Walls 42269c11fbSAndy Walls static unsigned int pcm_debug; 43269c11fbSAndy Walls module_param(pcm_debug, int, 0644); 44269c11fbSAndy Walls MODULE_PARM_DESC(pcm_debug, "enable debug messages for pcm"); 45269c11fbSAndy Walls 46269c11fbSAndy Walls #define dprintk(fmt, arg...) \ 47269c11fbSAndy Walls do { \ 48269c11fbSAndy Walls if (pcm_debug) \ 49269c11fbSAndy Walls pr_info("ivtv-alsa-pcm %s: " fmt, __func__, ##arg); \ 50269c11fbSAndy Walls } while (0) 51269c11fbSAndy Walls 52269c11fbSAndy Walls static struct snd_pcm_hardware snd_ivtv_hw_capture = { 53269c11fbSAndy Walls .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | 54269c11fbSAndy Walls SNDRV_PCM_INFO_MMAP | 55269c11fbSAndy Walls SNDRV_PCM_INFO_INTERLEAVED | 56269c11fbSAndy Walls SNDRV_PCM_INFO_MMAP_VALID, 57269c11fbSAndy Walls 58269c11fbSAndy Walls .formats = SNDRV_PCM_FMTBIT_S16_LE, 59269c11fbSAndy Walls 60269c11fbSAndy Walls .rates = SNDRV_PCM_RATE_48000, 61269c11fbSAndy Walls 62269c11fbSAndy Walls .rate_min = 48000, 63269c11fbSAndy Walls .rate_max = 48000, 64269c11fbSAndy Walls .channels_min = 2, 65269c11fbSAndy Walls .channels_max = 2, 66269c11fbSAndy Walls .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */ 67269c11fbSAndy Walls .period_bytes_min = 64, /* 12544/2, */ 68269c11fbSAndy Walls .period_bytes_max = 12544, 69269c11fbSAndy Walls .periods_min = 2, 70269c11fbSAndy Walls .periods_max = 98, /* 12544, */ 71269c11fbSAndy Walls }; 72269c11fbSAndy Walls 732aebbf67SMauro Carvalho Chehab static void ivtv_alsa_announce_pcm_data(struct snd_ivtv_card *itvsc, 742aebbf67SMauro Carvalho Chehab u8 *pcm_data, 75269c11fbSAndy Walls size_t num_bytes) 76269c11fbSAndy Walls { 77269c11fbSAndy Walls struct snd_pcm_substream *substream; 78269c11fbSAndy Walls struct snd_pcm_runtime *runtime; 79269c11fbSAndy Walls unsigned int oldptr; 80269c11fbSAndy Walls unsigned int stride; 81269c11fbSAndy Walls int period_elapsed = 0; 82269c11fbSAndy Walls int length; 83269c11fbSAndy Walls 84269c11fbSAndy Walls dprintk("ivtv alsa announce ptr=%p data=%p num_bytes=%zd\n", itvsc, 85269c11fbSAndy Walls pcm_data, num_bytes); 86269c11fbSAndy Walls 87269c11fbSAndy Walls substream = itvsc->capture_pcm_substream; 88269c11fbSAndy Walls if (substream == NULL) { 89269c11fbSAndy Walls dprintk("substream was NULL\n"); 90269c11fbSAndy Walls return; 91269c11fbSAndy Walls } 92269c11fbSAndy Walls 93269c11fbSAndy Walls runtime = substream->runtime; 94269c11fbSAndy Walls if (runtime == NULL) { 95269c11fbSAndy Walls dprintk("runtime was NULL\n"); 96269c11fbSAndy Walls return; 97269c11fbSAndy Walls } 98269c11fbSAndy Walls 99269c11fbSAndy Walls stride = runtime->frame_bits >> 3; 100269c11fbSAndy Walls if (stride == 0) { 101269c11fbSAndy Walls dprintk("stride is zero\n"); 102269c11fbSAndy Walls return; 103269c11fbSAndy Walls } 104269c11fbSAndy Walls 105269c11fbSAndy Walls length = num_bytes / stride; 106269c11fbSAndy Walls if (length == 0) { 107269c11fbSAndy Walls dprintk("%s: length was zero\n", __func__); 108269c11fbSAndy Walls return; 109269c11fbSAndy Walls } 110269c11fbSAndy Walls 111269c11fbSAndy Walls if (runtime->dma_area == NULL) { 112269c11fbSAndy Walls dprintk("dma area was NULL - ignoring\n"); 113269c11fbSAndy Walls return; 114269c11fbSAndy Walls } 115269c11fbSAndy Walls 116269c11fbSAndy Walls oldptr = itvsc->hwptr_done_capture; 117269c11fbSAndy Walls if (oldptr + length >= runtime->buffer_size) { 118269c11fbSAndy Walls unsigned int cnt = 119269c11fbSAndy Walls runtime->buffer_size - oldptr; 120269c11fbSAndy Walls memcpy(runtime->dma_area + oldptr * stride, pcm_data, 121269c11fbSAndy Walls cnt * stride); 122269c11fbSAndy Walls memcpy(runtime->dma_area, pcm_data + cnt * stride, 123269c11fbSAndy Walls length * stride - cnt * stride); 124269c11fbSAndy Walls } else { 125269c11fbSAndy Walls memcpy(runtime->dma_area + oldptr * stride, pcm_data, 126269c11fbSAndy Walls length * stride); 127269c11fbSAndy Walls } 128269c11fbSAndy Walls snd_pcm_stream_lock(substream); 129269c11fbSAndy Walls 130269c11fbSAndy Walls itvsc->hwptr_done_capture += length; 131269c11fbSAndy Walls if (itvsc->hwptr_done_capture >= 132269c11fbSAndy Walls runtime->buffer_size) 133269c11fbSAndy Walls itvsc->hwptr_done_capture -= 134269c11fbSAndy Walls runtime->buffer_size; 135269c11fbSAndy Walls 136269c11fbSAndy Walls itvsc->capture_transfer_done += length; 137269c11fbSAndy Walls if (itvsc->capture_transfer_done >= 138269c11fbSAndy Walls runtime->period_size) { 139269c11fbSAndy Walls itvsc->capture_transfer_done -= 140269c11fbSAndy Walls runtime->period_size; 141269c11fbSAndy Walls period_elapsed = 1; 142269c11fbSAndy Walls } 143269c11fbSAndy Walls 144269c11fbSAndy Walls snd_pcm_stream_unlock(substream); 145269c11fbSAndy Walls 146269c11fbSAndy Walls if (period_elapsed) 147269c11fbSAndy Walls snd_pcm_period_elapsed(substream); 148269c11fbSAndy Walls } 149269c11fbSAndy Walls 150269c11fbSAndy Walls static int snd_ivtv_pcm_capture_open(struct snd_pcm_substream *substream) 151269c11fbSAndy Walls { 152269c11fbSAndy Walls struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream); 153269c11fbSAndy Walls struct snd_pcm_runtime *runtime = substream->runtime; 154269c11fbSAndy Walls struct v4l2_device *v4l2_dev = itvsc->v4l2_dev; 155269c11fbSAndy Walls struct ivtv *itv = to_ivtv(v4l2_dev); 156269c11fbSAndy Walls struct ivtv_stream *s; 157269c11fbSAndy Walls struct ivtv_open_id item; 158269c11fbSAndy Walls int ret; 159269c11fbSAndy Walls 160269c11fbSAndy Walls /* Instruct the CX2341[56] to start sending packets */ 161269c11fbSAndy Walls snd_ivtv_lock(itvsc); 162*deb29e90STakashi Iwai 163*deb29e90STakashi Iwai if (ivtv_init_on_first_open(itv)) { 164*deb29e90STakashi Iwai snd_ivtv_unlock(itvsc); 165*deb29e90STakashi Iwai return -ENXIO; 166*deb29e90STakashi Iwai } 167*deb29e90STakashi Iwai 168269c11fbSAndy Walls s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM]; 169269c11fbSAndy Walls 170269c11fbSAndy Walls v4l2_fh_init(&item.fh, s->vdev); 171269c11fbSAndy Walls item.itv = itv; 172269c11fbSAndy Walls item.type = s->type; 173269c11fbSAndy Walls 174269c11fbSAndy Walls /* See if the stream is available */ 175269c11fbSAndy Walls if (ivtv_claim_stream(&item, item.type)) { 176269c11fbSAndy Walls /* No, it's already in use */ 177269c11fbSAndy Walls snd_ivtv_unlock(itvsc); 178269c11fbSAndy Walls return -EBUSY; 179269c11fbSAndy Walls } 180269c11fbSAndy Walls 181269c11fbSAndy Walls if (test_bit(IVTV_F_S_STREAMOFF, &s->s_flags) || 182269c11fbSAndy Walls test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags)) { 183269c11fbSAndy Walls /* We're already streaming. No additional action required */ 184269c11fbSAndy Walls snd_ivtv_unlock(itvsc); 185269c11fbSAndy Walls return 0; 186269c11fbSAndy Walls } 187269c11fbSAndy Walls 188269c11fbSAndy Walls 189269c11fbSAndy Walls runtime->hw = snd_ivtv_hw_capture; 190269c11fbSAndy Walls snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 191269c11fbSAndy Walls itvsc->capture_pcm_substream = substream; 192269c11fbSAndy Walls runtime->private_data = itv; 193269c11fbSAndy Walls 194269c11fbSAndy Walls itv->pcm_announce_callback = ivtv_alsa_announce_pcm_data; 195269c11fbSAndy Walls 196269c11fbSAndy Walls /* Not currently streaming, so start it up */ 197269c11fbSAndy Walls set_bit(IVTV_F_S_STREAMING, &s->s_flags); 198269c11fbSAndy Walls ret = ivtv_start_v4l2_encode_stream(s); 199269c11fbSAndy Walls snd_ivtv_unlock(itvsc); 200269c11fbSAndy Walls 201269c11fbSAndy Walls return ret; 202269c11fbSAndy Walls } 203269c11fbSAndy Walls 204269c11fbSAndy Walls static int snd_ivtv_pcm_capture_close(struct snd_pcm_substream *substream) 205269c11fbSAndy Walls { 206269c11fbSAndy Walls struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream); 207269c11fbSAndy Walls struct v4l2_device *v4l2_dev = itvsc->v4l2_dev; 208269c11fbSAndy Walls struct ivtv *itv = to_ivtv(v4l2_dev); 209269c11fbSAndy Walls struct ivtv_stream *s; 210269c11fbSAndy Walls 211269c11fbSAndy Walls /* Instruct the ivtv to stop sending packets */ 212269c11fbSAndy Walls snd_ivtv_lock(itvsc); 213269c11fbSAndy Walls s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM]; 214269c11fbSAndy Walls ivtv_stop_v4l2_encode_stream(s, 0); 215269c11fbSAndy Walls clear_bit(IVTV_F_S_STREAMING, &s->s_flags); 216269c11fbSAndy Walls 217269c11fbSAndy Walls ivtv_release_stream(s); 218269c11fbSAndy Walls 219269c11fbSAndy Walls itv->pcm_announce_callback = NULL; 220269c11fbSAndy Walls snd_ivtv_unlock(itvsc); 221269c11fbSAndy Walls 222269c11fbSAndy Walls return 0; 223269c11fbSAndy Walls } 224269c11fbSAndy Walls 225269c11fbSAndy Walls static int snd_ivtv_pcm_ioctl(struct snd_pcm_substream *substream, 226269c11fbSAndy Walls unsigned int cmd, void *arg) 227269c11fbSAndy Walls { 228269c11fbSAndy Walls struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream); 229269c11fbSAndy Walls int ret; 230269c11fbSAndy Walls 231269c11fbSAndy Walls snd_ivtv_lock(itvsc); 232269c11fbSAndy Walls ret = snd_pcm_lib_ioctl(substream, cmd, arg); 233269c11fbSAndy Walls snd_ivtv_unlock(itvsc); 234269c11fbSAndy Walls return ret; 235269c11fbSAndy Walls } 236269c11fbSAndy Walls 237269c11fbSAndy Walls 238269c11fbSAndy Walls static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, 239269c11fbSAndy Walls size_t size) 240269c11fbSAndy Walls { 241269c11fbSAndy Walls struct snd_pcm_runtime *runtime = subs->runtime; 242269c11fbSAndy Walls 243269c11fbSAndy Walls dprintk("Allocating vbuffer\n"); 244269c11fbSAndy Walls if (runtime->dma_area) { 245269c11fbSAndy Walls if (runtime->dma_bytes > size) 246269c11fbSAndy Walls return 0; 247269c11fbSAndy Walls 248269c11fbSAndy Walls vfree(runtime->dma_area); 249269c11fbSAndy Walls } 250269c11fbSAndy Walls runtime->dma_area = vmalloc(size); 251269c11fbSAndy Walls if (!runtime->dma_area) 252269c11fbSAndy Walls return -ENOMEM; 253269c11fbSAndy Walls 254269c11fbSAndy Walls runtime->dma_bytes = size; 255269c11fbSAndy Walls 256269c11fbSAndy Walls return 0; 257269c11fbSAndy Walls } 258269c11fbSAndy Walls 259269c11fbSAndy Walls static int snd_ivtv_pcm_hw_params(struct snd_pcm_substream *substream, 260269c11fbSAndy Walls struct snd_pcm_hw_params *params) 261269c11fbSAndy Walls { 262269c11fbSAndy Walls dprintk("%s called\n", __func__); 263269c11fbSAndy Walls 264269c11fbSAndy Walls return snd_pcm_alloc_vmalloc_buffer(substream, 265269c11fbSAndy Walls params_buffer_bytes(params)); 266269c11fbSAndy Walls } 267269c11fbSAndy Walls 268269c11fbSAndy Walls static int snd_ivtv_pcm_hw_free(struct snd_pcm_substream *substream) 269269c11fbSAndy Walls { 270269c11fbSAndy Walls struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream); 271269c11fbSAndy Walls unsigned long flags; 272269c11fbSAndy Walls 273269c11fbSAndy Walls spin_lock_irqsave(&itvsc->slock, flags); 274269c11fbSAndy Walls if (substream->runtime->dma_area) { 275269c11fbSAndy Walls dprintk("freeing pcm capture region\n"); 276269c11fbSAndy Walls vfree(substream->runtime->dma_area); 277269c11fbSAndy Walls substream->runtime->dma_area = NULL; 278269c11fbSAndy Walls } 279269c11fbSAndy Walls spin_unlock_irqrestore(&itvsc->slock, flags); 280269c11fbSAndy Walls 281269c11fbSAndy Walls return 0; 282269c11fbSAndy Walls } 283269c11fbSAndy Walls 284269c11fbSAndy Walls static int snd_ivtv_pcm_prepare(struct snd_pcm_substream *substream) 285269c11fbSAndy Walls { 286269c11fbSAndy Walls struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream); 287269c11fbSAndy Walls 288269c11fbSAndy Walls itvsc->hwptr_done_capture = 0; 289269c11fbSAndy Walls itvsc->capture_transfer_done = 0; 290269c11fbSAndy Walls 291269c11fbSAndy Walls return 0; 292269c11fbSAndy Walls } 293269c11fbSAndy Walls 294269c11fbSAndy Walls static int snd_ivtv_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 295269c11fbSAndy Walls { 296269c11fbSAndy Walls return 0; 297269c11fbSAndy Walls } 298269c11fbSAndy Walls 299269c11fbSAndy Walls static 300269c11fbSAndy Walls snd_pcm_uframes_t snd_ivtv_pcm_pointer(struct snd_pcm_substream *substream) 301269c11fbSAndy Walls { 302269c11fbSAndy Walls unsigned long flags; 303269c11fbSAndy Walls snd_pcm_uframes_t hwptr_done; 304269c11fbSAndy Walls struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream); 305269c11fbSAndy Walls 306269c11fbSAndy Walls spin_lock_irqsave(&itvsc->slock, flags); 307269c11fbSAndy Walls hwptr_done = itvsc->hwptr_done_capture; 308269c11fbSAndy Walls spin_unlock_irqrestore(&itvsc->slock, flags); 309269c11fbSAndy Walls 310269c11fbSAndy Walls return hwptr_done; 311269c11fbSAndy Walls } 312269c11fbSAndy Walls 313269c11fbSAndy Walls static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, 314269c11fbSAndy Walls unsigned long offset) 315269c11fbSAndy Walls { 316269c11fbSAndy Walls void *pageptr = subs->runtime->dma_area + offset; 317269c11fbSAndy Walls 318269c11fbSAndy Walls return vmalloc_to_page(pageptr); 319269c11fbSAndy Walls } 320269c11fbSAndy Walls 321269c11fbSAndy Walls static struct snd_pcm_ops snd_ivtv_pcm_capture_ops = { 322269c11fbSAndy Walls .open = snd_ivtv_pcm_capture_open, 323269c11fbSAndy Walls .close = snd_ivtv_pcm_capture_close, 324269c11fbSAndy Walls .ioctl = snd_ivtv_pcm_ioctl, 325269c11fbSAndy Walls .hw_params = snd_ivtv_pcm_hw_params, 326269c11fbSAndy Walls .hw_free = snd_ivtv_pcm_hw_free, 327269c11fbSAndy Walls .prepare = snd_ivtv_pcm_prepare, 328269c11fbSAndy Walls .trigger = snd_ivtv_pcm_trigger, 329269c11fbSAndy Walls .pointer = snd_ivtv_pcm_pointer, 330269c11fbSAndy Walls .page = snd_pcm_get_vmalloc_page, 331269c11fbSAndy Walls }; 332269c11fbSAndy Walls 333269c11fbSAndy Walls int snd_ivtv_pcm_create(struct snd_ivtv_card *itvsc) 334269c11fbSAndy Walls { 335269c11fbSAndy Walls struct snd_pcm *sp; 336269c11fbSAndy Walls struct snd_card *sc = itvsc->sc; 337269c11fbSAndy Walls struct v4l2_device *v4l2_dev = itvsc->v4l2_dev; 338269c11fbSAndy Walls struct ivtv *itv = to_ivtv(v4l2_dev); 339269c11fbSAndy Walls int ret; 340269c11fbSAndy Walls 341269c11fbSAndy Walls ret = snd_pcm_new(sc, "CX2341[56] PCM", 342269c11fbSAndy Walls 0, /* PCM device 0, the only one for this card */ 343269c11fbSAndy Walls 0, /* 0 playback substreams */ 344269c11fbSAndy Walls 1, /* 1 capture substream */ 345269c11fbSAndy Walls &sp); 346269c11fbSAndy Walls if (ret) { 347269c11fbSAndy Walls IVTV_ALSA_ERR("%s: snd_ivtv_pcm_create() failed with err %d\n", 348269c11fbSAndy Walls __func__, ret); 349269c11fbSAndy Walls goto err_exit; 350269c11fbSAndy Walls } 351269c11fbSAndy Walls 352269c11fbSAndy Walls spin_lock_init(&itvsc->slock); 353269c11fbSAndy Walls 354269c11fbSAndy Walls snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE, 355269c11fbSAndy Walls &snd_ivtv_pcm_capture_ops); 356269c11fbSAndy Walls sp->info_flags = 0; 357269c11fbSAndy Walls sp->private_data = itvsc; 358269c11fbSAndy Walls strlcpy(sp->name, itv->card_name, sizeof(sp->name)); 359269c11fbSAndy Walls 360269c11fbSAndy Walls return 0; 361269c11fbSAndy Walls 362269c11fbSAndy Walls err_exit: 363269c11fbSAndy Walls return ret; 364269c11fbSAndy Walls } 365