root/drivers/media/usb/go7007/snd-go7007.c

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

DEFINITIONS

This source file includes following definitions.
  1. parse_audio_stream_data
  2. go7007_snd_hw_params
  3. go7007_snd_hw_free
  4. go7007_snd_capture_open
  5. go7007_snd_capture_close
  6. go7007_snd_pcm_prepare
  7. go7007_snd_pcm_trigger
  8. go7007_snd_pcm_pointer
  9. go7007_snd_pcm_page
  10. go7007_snd_free
  11. go7007_snd_init
  12. go7007_snd_remove

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Copyright (C) 2005-2006 Micronas USA Inc.
   4  */
   5 
   6 #include <linux/kernel.h>
   7 #include <linux/module.h>
   8 #include <linux/moduleparam.h>
   9 #include <linux/spinlock.h>
  10 #include <linux/delay.h>
  11 #include <linux/sched.h>
  12 #include <linux/vmalloc.h>
  13 #include <linux/time.h>
  14 #include <linux/mm.h>
  15 #include <linux/i2c.h>
  16 #include <linux/mutex.h>
  17 #include <linux/uaccess.h>
  18 #include <linux/slab.h>
  19 #include <sound/core.h>
  20 #include <sound/pcm.h>
  21 #include <sound/initval.h>
  22 
  23 #include "go7007-priv.h"
  24 
  25 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
  26 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
  27 static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
  28 
  29 module_param_array(index, int, NULL, 0444);
  30 module_param_array(id, charp, NULL, 0444);
  31 module_param_array(enable, bool, NULL, 0444);
  32 MODULE_PARM_DESC(index, "Index value for the go7007 audio driver");
  33 MODULE_PARM_DESC(id, "ID string for the go7007 audio driver");
  34 MODULE_PARM_DESC(enable, "Enable for the go7007 audio driver");
  35 
  36 struct go7007_snd {
  37         struct snd_card *card;
  38         struct snd_pcm *pcm;
  39         struct snd_pcm_substream *substream;
  40         spinlock_t lock;
  41         int w_idx;
  42         int hw_ptr;
  43         int avail;
  44         int capturing;
  45 };
  46 
  47 static const struct snd_pcm_hardware go7007_snd_capture_hw = {
  48         .info                   = (SNDRV_PCM_INFO_MMAP |
  49                                         SNDRV_PCM_INFO_INTERLEAVED |
  50                                         SNDRV_PCM_INFO_BLOCK_TRANSFER |
  51                                         SNDRV_PCM_INFO_MMAP_VALID),
  52         .formats                = SNDRV_PCM_FMTBIT_S16_LE,
  53         .rates                  = SNDRV_PCM_RATE_48000,
  54         .rate_min               = 48000,
  55         .rate_max               = 48000,
  56         .channels_min           = 2,
  57         .channels_max           = 2,
  58         .buffer_bytes_max       = (128*1024),
  59         .period_bytes_min       = 4096,
  60         .period_bytes_max       = (128*1024),
  61         .periods_min            = 1,
  62         .periods_max            = 32,
  63 };
  64 
  65 static void parse_audio_stream_data(struct go7007 *go, u8 *buf, int length)
  66 {
  67         struct go7007_snd *gosnd = go->snd_context;
  68         struct snd_pcm_runtime *runtime = gosnd->substream->runtime;
  69         int frames = bytes_to_frames(runtime, length);
  70         unsigned long flags;
  71 
  72         spin_lock_irqsave(&gosnd->lock, flags);
  73         gosnd->hw_ptr += frames;
  74         if (gosnd->hw_ptr >= runtime->buffer_size)
  75                 gosnd->hw_ptr -= runtime->buffer_size;
  76         gosnd->avail += frames;
  77         spin_unlock_irqrestore(&gosnd->lock, flags);
  78         if (gosnd->w_idx + length > runtime->dma_bytes) {
  79                 int cpy = runtime->dma_bytes - gosnd->w_idx;
  80 
  81                 memcpy(runtime->dma_area + gosnd->w_idx, buf, cpy);
  82                 length -= cpy;
  83                 buf += cpy;
  84                 gosnd->w_idx = 0;
  85         }
  86         memcpy(runtime->dma_area + gosnd->w_idx, buf, length);
  87         gosnd->w_idx += length;
  88         spin_lock_irqsave(&gosnd->lock, flags);
  89         if (gosnd->avail < runtime->period_size) {
  90                 spin_unlock_irqrestore(&gosnd->lock, flags);
  91                 return;
  92         }
  93         gosnd->avail -= runtime->period_size;
  94         spin_unlock_irqrestore(&gosnd->lock, flags);
  95         if (gosnd->capturing)
  96                 snd_pcm_period_elapsed(gosnd->substream);
  97 }
  98 
  99 static int go7007_snd_hw_params(struct snd_pcm_substream *substream,
 100                                 struct snd_pcm_hw_params *hw_params)
 101 {
 102         struct go7007 *go = snd_pcm_substream_chip(substream);
 103         unsigned int bytes;
 104 
 105         bytes = params_buffer_bytes(hw_params);
 106         if (substream->runtime->dma_bytes > 0)
 107                 vfree(substream->runtime->dma_area);
 108         substream->runtime->dma_bytes = 0;
 109         substream->runtime->dma_area = vmalloc(bytes);
 110         if (substream->runtime->dma_area == NULL)
 111                 return -ENOMEM;
 112         substream->runtime->dma_bytes = bytes;
 113         go->audio_deliver = parse_audio_stream_data;
 114         return 0;
 115 }
 116 
 117 static int go7007_snd_hw_free(struct snd_pcm_substream *substream)
 118 {
 119         struct go7007 *go = snd_pcm_substream_chip(substream);
 120 
 121         go->audio_deliver = NULL;
 122         if (substream->runtime->dma_bytes > 0)
 123                 vfree(substream->runtime->dma_area);
 124         substream->runtime->dma_bytes = 0;
 125         return 0;
 126 }
 127 
 128 static int go7007_snd_capture_open(struct snd_pcm_substream *substream)
 129 {
 130         struct go7007 *go = snd_pcm_substream_chip(substream);
 131         struct go7007_snd *gosnd = go->snd_context;
 132         unsigned long flags;
 133         int r;
 134 
 135         spin_lock_irqsave(&gosnd->lock, flags);
 136         if (gosnd->substream == NULL) {
 137                 gosnd->substream = substream;
 138                 substream->runtime->hw = go7007_snd_capture_hw;
 139                 r = 0;
 140         } else
 141                 r = -EBUSY;
 142         spin_unlock_irqrestore(&gosnd->lock, flags);
 143         return r;
 144 }
 145 
 146 static int go7007_snd_capture_close(struct snd_pcm_substream *substream)
 147 {
 148         struct go7007 *go = snd_pcm_substream_chip(substream);
 149         struct go7007_snd *gosnd = go->snd_context;
 150 
 151         gosnd->substream = NULL;
 152         return 0;
 153 }
 154 
 155 static int go7007_snd_pcm_prepare(struct snd_pcm_substream *substream)
 156 {
 157         return 0;
 158 }
 159 
 160 static int go7007_snd_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 161 {
 162         struct go7007 *go = snd_pcm_substream_chip(substream);
 163         struct go7007_snd *gosnd = go->snd_context;
 164 
 165         switch (cmd) {
 166         case SNDRV_PCM_TRIGGER_START:
 167                 /* Just set a flag to indicate we should signal ALSA when
 168                  * sound comes in */
 169                 gosnd->capturing = 1;
 170                 return 0;
 171         case SNDRV_PCM_TRIGGER_STOP:
 172                 gosnd->hw_ptr = gosnd->w_idx = gosnd->avail = 0;
 173                 gosnd->capturing = 0;
 174                 return 0;
 175         default:
 176                 return -EINVAL;
 177         }
 178 }
 179 
 180 static snd_pcm_uframes_t go7007_snd_pcm_pointer(struct snd_pcm_substream *substream)
 181 {
 182         struct go7007 *go = snd_pcm_substream_chip(substream);
 183         struct go7007_snd *gosnd = go->snd_context;
 184 
 185         return gosnd->hw_ptr;
 186 }
 187 
 188 static struct page *go7007_snd_pcm_page(struct snd_pcm_substream *substream,
 189                                         unsigned long offset)
 190 {
 191         return vmalloc_to_page(substream->runtime->dma_area + offset);
 192 }
 193 
 194 static const struct snd_pcm_ops go7007_snd_capture_ops = {
 195         .open           = go7007_snd_capture_open,
 196         .close          = go7007_snd_capture_close,
 197         .ioctl          = snd_pcm_lib_ioctl,
 198         .hw_params      = go7007_snd_hw_params,
 199         .hw_free        = go7007_snd_hw_free,
 200         .prepare        = go7007_snd_pcm_prepare,
 201         .trigger        = go7007_snd_pcm_trigger,
 202         .pointer        = go7007_snd_pcm_pointer,
 203         .page           = go7007_snd_pcm_page,
 204 };
 205 
 206 static int go7007_snd_free(struct snd_device *device)
 207 {
 208         struct go7007 *go = device->device_data;
 209 
 210         kfree(go->snd_context);
 211         go->snd_context = NULL;
 212         return 0;
 213 }
 214 
 215 static struct snd_device_ops go7007_snd_device_ops = {
 216         .dev_free       = go7007_snd_free,
 217 };
 218 
 219 int go7007_snd_init(struct go7007 *go)
 220 {
 221         static int dev;
 222         struct go7007_snd *gosnd;
 223         int ret;
 224 
 225         if (dev >= SNDRV_CARDS)
 226                 return -ENODEV;
 227         if (!enable[dev]) {
 228                 dev++;
 229                 return -ENOENT;
 230         }
 231         gosnd = kmalloc(sizeof(struct go7007_snd), GFP_KERNEL);
 232         if (gosnd == NULL)
 233                 return -ENOMEM;
 234         spin_lock_init(&gosnd->lock);
 235         gosnd->hw_ptr = gosnd->w_idx = gosnd->avail = 0;
 236         gosnd->capturing = 0;
 237         ret = snd_card_new(go->dev, index[dev], id[dev], THIS_MODULE, 0,
 238                            &gosnd->card);
 239         if (ret < 0) {
 240                 kfree(gosnd);
 241                 return ret;
 242         }
 243         ret = snd_device_new(gosnd->card, SNDRV_DEV_LOWLEVEL, go,
 244                         &go7007_snd_device_ops);
 245         if (ret < 0) {
 246                 kfree(gosnd);
 247                 return ret;
 248         }
 249         ret = snd_pcm_new(gosnd->card, "go7007", 0, 0, 1, &gosnd->pcm);
 250         if (ret < 0) {
 251                 snd_card_free(gosnd->card);
 252                 kfree(gosnd);
 253                 return ret;
 254         }
 255         strscpy(gosnd->card->driver, "go7007", sizeof(gosnd->card->driver));
 256         strscpy(gosnd->card->shortname, go->name, sizeof(gosnd->card->shortname));
 257         strscpy(gosnd->card->longname, gosnd->card->shortname,
 258                 sizeof(gosnd->card->longname));
 259 
 260         gosnd->pcm->private_data = go;
 261         snd_pcm_set_ops(gosnd->pcm, SNDRV_PCM_STREAM_CAPTURE,
 262                         &go7007_snd_capture_ops);
 263 
 264         ret = snd_card_register(gosnd->card);
 265         if (ret < 0) {
 266                 snd_card_free(gosnd->card);
 267                 kfree(gosnd);
 268                 return ret;
 269         }
 270 
 271         gosnd->substream = NULL;
 272         go->snd_context = gosnd;
 273         v4l2_device_get(&go->v4l2_dev);
 274         ++dev;
 275 
 276         return 0;
 277 }
 278 EXPORT_SYMBOL(go7007_snd_init);
 279 
 280 int go7007_snd_remove(struct go7007 *go)
 281 {
 282         struct go7007_snd *gosnd = go->snd_context;
 283 
 284         snd_card_disconnect(gosnd->card);
 285         snd_card_free_when_closed(gosnd->card);
 286         v4l2_device_put(&go->v4l2_dev);
 287         return 0;
 288 }
 289 EXPORT_SYMBOL(go7007_snd_remove);
 290 
 291 MODULE_LICENSE("GPL v2");

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