root/drivers/media/pci/cx18/cx18-alsa-pcm.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cx18_alsa_announce_pcm_data
  2. snd_cx18_pcm_capture_open
  3. snd_cx18_pcm_capture_close
  4. snd_cx18_pcm_ioctl
  5. snd_pcm_alloc_vmalloc_buffer
  6. snd_cx18_pcm_hw_params
  7. snd_cx18_pcm_hw_free
  8. snd_cx18_pcm_prepare
  9. snd_cx18_pcm_trigger
  10. snd_cx18_pcm_pointer
  11. snd_pcm_get_vmalloc_page
  12. snd_cx18_pcm_create

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

/* [<][>][^][v][top][bottom][index][help] */