linux/drivers/media/video/tlg2300/pd-alsa.c
<<
>>
Prefs
   1#include <linux/kernel.h>
   2#include <linux/usb.h>
   3#include <linux/init.h>
   4#include <linux/sound.h>
   5#include <linux/spinlock.h>
   6#include <linux/soundcard.h>
   7#include <linux/vmalloc.h>
   8#include <linux/proc_fs.h>
   9#include <linux/module.h>
  10#include <linux/gfp.h>
  11#include <sound/core.h>
  12#include <sound/pcm.h>
  13#include <sound/pcm_params.h>
  14#include <sound/info.h>
  15#include <sound/initval.h>
  16#include <sound/control.h>
  17#include <media/v4l2-common.h>
  18#include "pd-common.h"
  19#include "vendorcmds.h"
  20
  21static void complete_handler_audio(struct urb *urb);
  22#define AUDIO_EP        (0x83)
  23#define AUDIO_BUF_SIZE  (512)
  24#define PERIOD_SIZE     (1024 * 8)
  25#define PERIOD_MIN      (4)
  26#define PERIOD_MAX      PERIOD_MIN
  27
  28static struct snd_pcm_hardware snd_pd_hw_capture = {
  29        .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
  30                SNDRV_PCM_INFO_MMAP           |
  31                SNDRV_PCM_INFO_INTERLEAVED |
  32                SNDRV_PCM_INFO_MMAP_VALID,
  33
  34        .formats = SNDRV_PCM_FMTBIT_S16_LE,
  35        .rates = SNDRV_PCM_RATE_48000,
  36
  37        .rate_min = 48000,
  38        .rate_max = 48000,
  39        .channels_min = 2,
  40        .channels_max = 2,
  41        .buffer_bytes_max = PERIOD_SIZE * PERIOD_MIN,
  42        .period_bytes_min = PERIOD_SIZE,
  43        .period_bytes_max = PERIOD_SIZE,
  44        .periods_min = PERIOD_MIN,
  45        .periods_max = PERIOD_MAX,
  46        /*
  47        .buffer_bytes_max = 62720 * 8,
  48        .period_bytes_min = 64,
  49        .period_bytes_max = 12544,
  50        .periods_min = 2,
  51        .periods_max = 98
  52        */
  53};
  54
  55static int snd_pd_capture_open(struct snd_pcm_substream *substream)
  56{
  57        struct poseidon *p = snd_pcm_substream_chip(substream);
  58        struct poseidon_audio *pa = &p->audio;
  59        struct snd_pcm_runtime *runtime = substream->runtime;
  60
  61        if (!p)
  62                return -ENODEV;
  63        pa->users++;
  64        pa->card_close          = 0;
  65        pa->capture_pcm_substream       = substream;
  66        runtime->private_data           = p;
  67
  68        runtime->hw = snd_pd_hw_capture;
  69        snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
  70        usb_autopm_get_interface(p->interface);
  71        kref_get(&p->kref);
  72        return 0;
  73}
  74
  75static int snd_pd_pcm_close(struct snd_pcm_substream *substream)
  76{
  77        struct poseidon *p = snd_pcm_substream_chip(substream);
  78        struct poseidon_audio *pa = &p->audio;
  79
  80        pa->users--;
  81        pa->card_close          = 1;
  82        usb_autopm_put_interface(p->interface);
  83        kref_put(&p->kref, poseidon_delete);
  84        return 0;
  85}
  86
  87static int snd_pd_hw_capture_params(struct snd_pcm_substream *substream,
  88                                        struct snd_pcm_hw_params *hw_params)
  89{
  90        struct snd_pcm_runtime *runtime = substream->runtime;
  91        unsigned int size;
  92
  93        size = params_buffer_bytes(hw_params);
  94        if (runtime->dma_area) {
  95                if (runtime->dma_bytes > size)
  96                        return 0;
  97                vfree(runtime->dma_area);
  98        }
  99        runtime->dma_area = vmalloc(size);
 100        if (!runtime->dma_area)
 101                return -ENOMEM;
 102        else
 103                runtime->dma_bytes = size;
 104        return 0;
 105}
 106
 107static int audio_buf_free(struct poseidon *p)
 108{
 109        struct poseidon_audio *pa = &p->audio;
 110        int i;
 111
 112        for (i = 0; i < AUDIO_BUFS; i++)
 113                if (pa->urb_array[i])
 114                        usb_kill_urb(pa->urb_array[i]);
 115        free_all_urb_generic(pa->urb_array, AUDIO_BUFS);
 116        logpm();
 117        return 0;
 118}
 119
 120static int snd_pd_hw_capture_free(struct snd_pcm_substream *substream)
 121{
 122        struct poseidon *p = snd_pcm_substream_chip(substream);
 123
 124        logpm();
 125        audio_buf_free(p);
 126        return 0;
 127}
 128
 129static int snd_pd_prepare(struct snd_pcm_substream *substream)
 130{
 131        return 0;
 132}
 133
 134#define AUDIO_TRAILER_SIZE      (16)
 135static inline void handle_audio_data(struct urb *urb, int *period_elapsed)
 136{
 137        struct poseidon_audio *pa = urb->context;
 138        struct snd_pcm_runtime *runtime = pa->capture_pcm_substream->runtime;
 139
 140        int stride      = runtime->frame_bits >> 3;
 141        int len         = urb->actual_length / stride;
 142        unsigned char *cp       = urb->transfer_buffer;
 143        unsigned int oldptr     = pa->rcv_position;
 144
 145        if (urb->actual_length == AUDIO_BUF_SIZE - 4)
 146                len -= (AUDIO_TRAILER_SIZE / stride);
 147
 148        /* do the copy */
 149        if (oldptr + len >= runtime->buffer_size) {
 150                unsigned int cnt = runtime->buffer_size - oldptr;
 151
 152                memcpy(runtime->dma_area + oldptr * stride, cp, cnt * stride);
 153                memcpy(runtime->dma_area, (cp + cnt * stride),
 154                                        (len * stride - cnt * stride));
 155        } else
 156                memcpy(runtime->dma_area + oldptr * stride, cp, len * stride);
 157
 158        /* update the statas */
 159        snd_pcm_stream_lock(pa->capture_pcm_substream);
 160        pa->rcv_position        += len;
 161        if (pa->rcv_position >= runtime->buffer_size)
 162                pa->rcv_position -= runtime->buffer_size;
 163
 164        pa->copied_position += (len);
 165        if (pa->copied_position >= runtime->period_size) {
 166                pa->copied_position -= runtime->period_size;
 167                *period_elapsed = 1;
 168        }
 169        snd_pcm_stream_unlock(pa->capture_pcm_substream);
 170}
 171
 172static void complete_handler_audio(struct urb *urb)
 173{
 174        struct poseidon_audio *pa = urb->context;
 175        struct snd_pcm_substream *substream = pa->capture_pcm_substream;
 176        int    period_elapsed = 0;
 177        int    ret;
 178
 179        if (1 == pa->card_close || pa->capture_stream != STREAM_ON)
 180                return;
 181
 182        if (urb->status != 0) {
 183                /*if (urb->status == -ESHUTDOWN)*/
 184                        return;
 185        }
 186
 187        if (substream) {
 188                if (urb->actual_length) {
 189                        handle_audio_data(urb, &period_elapsed);
 190                        if (period_elapsed)
 191                                snd_pcm_period_elapsed(substream);
 192                }
 193        }
 194
 195        ret = usb_submit_urb(urb, GFP_ATOMIC);
 196        if (ret < 0)
 197                log("audio urb failed (errcod = %i)", ret);
 198        return;
 199}
 200
 201static int fire_audio_urb(struct poseidon *p)
 202{
 203        int i, ret = 0;
 204        struct poseidon_audio *pa = &p->audio;
 205
 206        alloc_bulk_urbs_generic(pa->urb_array, AUDIO_BUFS,
 207                        p->udev, AUDIO_EP,
 208                        AUDIO_BUF_SIZE, GFP_ATOMIC,
 209                        complete_handler_audio, pa);
 210
 211        for (i = 0; i < AUDIO_BUFS; i++) {
 212                ret = usb_submit_urb(pa->urb_array[i], GFP_KERNEL);
 213                if (ret)
 214                        log("urb err : %d", ret);
 215        }
 216        log();
 217        return ret;
 218}
 219
 220static int snd_pd_capture_trigger(struct snd_pcm_substream *substream, int cmd)
 221{
 222        struct poseidon *p = snd_pcm_substream_chip(substream);
 223        struct poseidon_audio *pa = &p->audio;
 224
 225        if (debug_mode)
 226                log("cmd %d, audio stat : %d\n", cmd, pa->capture_stream);
 227
 228        switch (cmd) {
 229        case SNDRV_PCM_TRIGGER_RESUME:
 230        case SNDRV_PCM_TRIGGER_START:
 231                if (pa->capture_stream == STREAM_ON)
 232                        return 0;
 233
 234                pa->rcv_position = pa->copied_position = 0;
 235                pa->capture_stream = STREAM_ON;
 236
 237                if (in_hibernation(p))
 238                        return 0;
 239                fire_audio_urb(p);
 240                return 0;
 241
 242        case SNDRV_PCM_TRIGGER_SUSPEND:
 243                pa->capture_stream = STREAM_SUSPEND;
 244                return 0;
 245        case SNDRV_PCM_TRIGGER_STOP:
 246                pa->capture_stream = STREAM_OFF;
 247                return 0;
 248        default:
 249                return -EINVAL;
 250        }
 251}
 252
 253static snd_pcm_uframes_t
 254snd_pd_capture_pointer(struct snd_pcm_substream *substream)
 255{
 256        struct poseidon *p = snd_pcm_substream_chip(substream);
 257        struct poseidon_audio *pa = &p->audio;
 258        return pa->rcv_position;
 259}
 260
 261static struct page *snd_pcm_pd_get_page(struct snd_pcm_substream *subs,
 262                                             unsigned long offset)
 263{
 264        void *pageptr = subs->runtime->dma_area + offset;
 265        return vmalloc_to_page(pageptr);
 266}
 267
 268static struct snd_pcm_ops pcm_capture_ops = {
 269        .open      = snd_pd_capture_open,
 270        .close     = snd_pd_pcm_close,
 271        .ioctl     = snd_pcm_lib_ioctl,
 272        .hw_params = snd_pd_hw_capture_params,
 273        .hw_free   = snd_pd_hw_capture_free,
 274        .prepare   = snd_pd_prepare,
 275        .trigger   = snd_pd_capture_trigger,
 276        .pointer   = snd_pd_capture_pointer,
 277        .page      = snd_pcm_pd_get_page,
 278};
 279
 280#ifdef CONFIG_PM
 281int pm_alsa_suspend(struct poseidon *p)
 282{
 283        logpm(p);
 284        audio_buf_free(p);
 285        return 0;
 286}
 287
 288int pm_alsa_resume(struct poseidon *p)
 289{
 290        logpm(p);
 291        fire_audio_urb(p);
 292        return 0;
 293}
 294#endif
 295
 296int poseidon_audio_init(struct poseidon *p)
 297{
 298        struct poseidon_audio *pa = &p->audio;
 299        struct snd_card *card;
 300        struct snd_pcm *pcm;
 301        int ret;
 302
 303        ret = snd_card_create(-1, "Telegent", THIS_MODULE, 0, &card);
 304        if (ret != 0)
 305                return ret;
 306
 307        ret = snd_pcm_new(card, "poseidon audio", 0, 0, 1, &pcm);
 308        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
 309        pcm->info_flags   = 0;
 310        pcm->private_data = p;
 311        strcpy(pcm->name, "poseidon audio capture");
 312
 313        strcpy(card->driver, "ALSA driver");
 314        strcpy(card->shortname, "poseidon Audio");
 315        strcpy(card->longname, "poseidon ALSA Audio");
 316
 317        if (snd_card_register(card)) {
 318                snd_card_free(card);
 319                return -ENOMEM;
 320        }
 321        pa->card = card;
 322        return 0;
 323}
 324
 325int poseidon_audio_free(struct poseidon *p)
 326{
 327        struct poseidon_audio *pa = &p->audio;
 328
 329        if (pa->card)
 330                snd_card_free(pa->card);
 331        return 0;
 332}
 333