The module uses ALSA for the audio, it will register
a new card for tlg2300.

Signed-off-by: Huang Shijie <shij...@gmail.com>
---
 drivers/media/video/tlg2300/pd-alsa.c |  379 +++++++++++++++++++++++++++++++++
 1 files changed, 379 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/tlg2300/pd-alsa.c

diff --git a/drivers/media/video/tlg2300/pd-alsa.c 
b/drivers/media/video/tlg2300/pd-alsa.c
new file mode 100644
index 0000000..54a5aaa
--- /dev/null
+++ b/drivers/media/video/tlg2300/pd-alsa.c
@@ -0,0 +1,379 @@
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/init.h>
+#include <linux/sound.h>
+#include <linux/spinlock.h>
+#include <linux/soundcard.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <media/v4l2-common.h>
+#include "pd-common.h"
+#include "vendorcmds.h"
+
+static void complete_handler_audio(struct urb *urb);
+#define ANOLOG_AUDIO_ID        (0)
+#define FM_ID          (1)
+#define AUDIO_EP       (0x83)
+#define AUDIO_BUF_SIZE (512)
+#define PERIOD_SIZE    (1024 * 8)
+#define PERIOD_MIN     (4)
+#define PERIOD_MAX     PERIOD_MIN
+
+static struct snd_pcm_hardware snd_pd_hw_capture = {
+       .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
+               SNDRV_PCM_INFO_MMAP           |
+               SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_MMAP_VALID,
+
+       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       .rates = SNDRV_PCM_RATE_48000,
+
+       .rate_min = 48000,
+       .rate_max = 48000,
+       .channels_min = 2,
+       .channels_max = 2,
+       .buffer_bytes_max = PERIOD_SIZE * PERIOD_MIN,
+       .period_bytes_min = PERIOD_SIZE,
+       .period_bytes_max = PERIOD_SIZE,
+       .periods_min = PERIOD_MIN,
+       .periods_max = PERIOD_MAX,
+       /*
+       .buffer_bytes_max = 62720 * 8,
+       .period_bytes_min = 64,
+       .period_bytes_max = 12544,
+       .periods_min = 2,
+       .periods_max = 98
+       */
+};
+
+static int snd_pd_capture_open(struct snd_pcm_substream *substream)
+{
+       struct poseidon *p = snd_pcm_substream_chip(substream);
+       struct poseidon_audio *pa = &p->audio;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       if (!p)
+               return -ENODEV;
+       pa->users++;
+       pa->card_close          = 0;
+       pa->capture_pcm_substream       = substream;
+       runtime->private_data           = p;
+
+       runtime->hw = snd_pd_hw_capture;
+       snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+       usb_autopm_get_interface(p->interface);
+       kref_get(&p->kref);
+       return 0;
+}
+
+static int snd_pd_pcm_close(struct snd_pcm_substream *substream)
+{
+       struct poseidon *p = snd_pcm_substream_chip(substream);
+       struct poseidon_audio *pa = &p->audio;
+
+       pa->users--;
+       pa->card_close          = 1;
+       kref_put(&p->kref, poseidon_delete);
+       usb_autopm_put_interface(p->interface);
+       return 0;
+}
+
+static int snd_pd_hw_capture_params(struct snd_pcm_substream *substream,
+                                       struct snd_pcm_hw_params *hw_params)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       unsigned int size;
+
+       size = params_buffer_bytes(hw_params);
+       if (runtime->dma_area) {
+               if (runtime->dma_bytes > size)
+                       return 0;
+               vfree(runtime->dma_area);
+       }
+       runtime->dma_area = vmalloc(size);
+       if (!runtime->dma_area)
+               return -ENOMEM;
+       else
+               runtime->dma_bytes = size;
+       return 0;
+}
+
+static int audio_buf_free(struct poseidon *p)
+{
+       struct poseidon_audio *pa = &p->audio;
+       int i;
+
+       for (i = 0; i < AUDIO_BUFS; i++) {
+               struct urb *urb = pa->urb[i];
+
+               if (!urb)
+                       continue;
+               usb_buffer_free(p->udev, urb->transfer_buffer_length,
+                               urb->transfer_buffer, urb->transfer_dma);
+               usb_free_urb(urb);
+               pa->urb[i] = NULL;
+       }
+       return 0;
+}
+
+static int audio_buf_init(struct poseidon *p)
+{
+       int       i, ret;
+       struct poseidon_audio *pa = &p->audio;
+
+       for (i = 0; i < AUDIO_BUFS; i++) {
+               struct urb *urb;
+               char *mem = NULL;
+
+               if (pa->urb[i])
+                       continue;
+
+               urb = usb_alloc_urb(0, GFP_ATOMIC);
+               if (!urb) {
+                       ret = -ENOMEM;
+                       goto init_err;
+               }
+               mem = usb_buffer_alloc(p->udev, AUDIO_BUF_SIZE,
+                                       GFP_ATOMIC, &urb->transfer_dma);
+               if (!mem) {
+                       usb_free_urb(urb);
+                       goto init_err;
+               }
+               usb_fill_bulk_urb(urb, p->udev,
+                               usb_rcvbulkpipe(p->udev, AUDIO_EP),
+                               mem, AUDIO_BUF_SIZE,
+                               complete_handler_audio,
+                               pa);
+               urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+               pa->urb[i] = urb;
+       }
+       return 0;
+
+init_err:
+       audio_buf_free(p);
+       return ret;
+}
+
+static int snd_pd_hw_capture_free(struct snd_pcm_substream *substream)
+{
+       struct poseidon *p = snd_pcm_substream_chip(substream);
+
+       audio_buf_free(p);
+       return 0;
+}
+
+static int snd_pd_prepare(struct snd_pcm_substream *substream)
+{
+       return 0;
+}
+
+#define AUDIO_TRAILER_SIZE     (16)
+static inline void handle_audio_data(struct urb *urb, int *period_elapsed)
+{
+       struct poseidon_audio *pa = urb->context;
+       struct snd_pcm_runtime *runtime = pa->capture_pcm_substream->runtime;
+
+       int stride      = runtime->frame_bits >> 3;
+       int len         = urb->actual_length / stride;
+       unsigned char *cp       = urb->transfer_buffer;
+       unsigned int oldptr     = pa->rcv_position;
+
+       if (urb->actual_length == AUDIO_BUF_SIZE - 4)
+               len -= (AUDIO_TRAILER_SIZE / stride);
+
+       /* do the copy */
+       if (oldptr + len >= runtime->buffer_size) {
+               unsigned int cnt = runtime->buffer_size - oldptr;
+
+               memcpy(runtime->dma_area + oldptr * stride, cp, cnt * stride);
+               memcpy(runtime->dma_area, (cp + cnt * stride),
+                                       (len * stride - cnt * stride));
+       } else
+               memcpy(runtime->dma_area + oldptr * stride, cp, len * stride);
+
+       /* update the statas */
+       snd_pcm_stream_lock(pa->capture_pcm_substream);
+       pa->rcv_position        += len;
+       if (pa->rcv_position >= runtime->buffer_size)
+               pa->rcv_position -= runtime->buffer_size;
+
+       pa->copied_position += (len);
+       if (pa->copied_position >= runtime->period_size) {
+               pa->copied_position -= runtime->period_size;
+               *period_elapsed = 1;
+       }
+       snd_pcm_stream_unlock(pa->capture_pcm_substream);
+}
+
+static void complete_handler_audio(struct urb *urb)
+{
+       struct poseidon_audio *pa = urb->context;
+       struct snd_pcm_substream *substream = pa->capture_pcm_substream;
+       int    period_elapsed = 0;
+       int    ret;
+
+       if (1 == pa->card_close || pa->capture_stream == STREAM_OFF)
+               return;
+
+       if (urb->status != 0) {
+               /*if (urb->status == -ESHUTDOWN)*/
+                       return;
+       }
+
+       if (pa->capture_stream == STREAM_ON && substream && !urb->status) {
+               if (urb->actual_length) {
+                       handle_audio_data(urb, &period_elapsed);
+                       if (period_elapsed)
+                               snd_pcm_period_elapsed(substream);
+               }
+       }
+
+       ret = usb_submit_urb(urb, GFP_ATOMIC);
+       if (ret < 0)
+               log("audio urb failed (errcod = %i)", ret);
+       return;
+}
+
+static int snd_pd_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct poseidon *p = snd_pcm_substream_chip(substream);
+       struct poseidon_audio *pa = &p->audio;
+       int i, ret;
+
+       if (debug_mode)
+               log("cmd %d\n", cmd);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_START:
+               if (audio_in_hibernate(p))
+                       return 0;
+               if (pa->capture_stream == STREAM_ON)
+                       return 0;
+
+               pa->rcv_position = pa->copied_position = 0;
+               pa->capture_stream = STREAM_ON;
+
+               audio_buf_init(p);
+               for (i = 0; i < AUDIO_BUFS; i++) {
+                       ret = usb_submit_urb(pa->urb[i], GFP_KERNEL);
+                       if (ret)
+                               log("urb err : %d", ret);
+               }
+               return 0;
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_STOP:
+               pa->capture_stream = STREAM_OFF;
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+static snd_pcm_uframes_t
+snd_pd_capture_pointer(struct snd_pcm_substream *substream)
+{
+       struct poseidon *p = snd_pcm_substream_chip(substream);
+       struct poseidon_audio *pa = &p->audio;
+       return pa->rcv_position;
+}
+
+static struct page *snd_pcm_pd_get_page(struct snd_pcm_substream *subs,
+                                            unsigned long offset)
+{
+       void *pageptr = subs->runtime->dma_area + offset;
+       return vmalloc_to_page(pageptr);
+}
+
+static struct snd_pcm_ops pcm_capture_ops = {
+       .open      = snd_pd_capture_open,
+       .close     = snd_pd_pcm_close,
+       .ioctl     = snd_pcm_lib_ioctl,
+       .hw_params = snd_pd_hw_capture_params,
+       .hw_free   = snd_pd_hw_capture_free,
+       .prepare   = snd_pd_prepare,
+       .trigger   = snd_pd_capture_trigger,
+       .pointer   = snd_pd_capture_pointer,
+       .page      = snd_pcm_pd_get_page,
+};
+
+#ifdef CONFIG_PM
+int pm_alsa_suspend(struct poseidon *p)
+{
+       struct poseidon_audio *pa = &p->audio;
+       int i;
+
+       snd_pcm_suspend(pa->capture_pcm_substream);
+
+       for (i = 0; i < AUDIO_BUFS; i++)
+               usb_kill_urb(pa->urb[i]);
+
+       audio_buf_free(p);
+       return 0;
+}
+
+int pm_alsa_resume(struct poseidon *p)
+{
+       struct poseidon_audio *pa = &p->audio;
+
+       if (audio_in_hibernate(p)) {
+               pa->pm_state = 0;
+               usb_autopm_get_interface(p->interface);
+       }
+       snd_pd_capture_trigger(pa->capture_pcm_substream,
+                               SNDRV_PCM_TRIGGER_START);
+       return 0;
+}
+#endif
+
+int poseidon_audio_init(struct poseidon *p)
+{
+       struct poseidon_audio *pa = &p->audio;
+       struct snd_card *card;
+       struct snd_pcm *pcm;
+       int ret;
+
+       if (audio_in_hibernate(p))
+               return 0;
+
+       ret = snd_card_create(-1, "poseidon_audio", THIS_MODULE, 0, &card);
+       if (ret != 0)
+               return ret;
+
+       ret = snd_pcm_new(card, "poseidon audio", 0, 0, 1, &pcm);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
+       pcm->info_flags   = 0;
+       pcm->private_data = p;
+       strcpy(pcm->name, "poseidon audio capture");
+
+       strcpy(card->driver, "ALSA driver");
+       strcpy(card->shortname, "poseidon Audio");
+       strcpy(card->longname, "poseidon ALSA Audio");
+
+       if (snd_card_register(card)) {
+               snd_card_free(card);
+               return -ENOMEM;
+       }
+       pa->card = card;
+       return 0;
+}
+
+int poseidon_audio_free(struct poseidon *p)
+{
+       struct poseidon_audio *pa = &p->audio;
+
+       if (audio_in_hibernate(p))
+               return 0;
+
+       if (pa->card)
+               snd_card_free(pa->card);
+       return 0;
+}
-- 
1.6.0.6

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to