Add linux-next specific files for 20110831
[linux-2.6/next.git] / drivers / media / video / tlg2300 / pd-alsa.c
blob9f8b7da56b671fe2eeee8f942e490dbb8b65c154
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"
21 static 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
28 static 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,
34 .formats = SNDRV_PCM_FMTBIT_S16_LE,
35 .rates = SNDRV_PCM_RATE_48000,
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,
47 .buffer_bytes_max = 62720 * 8,
48 .period_bytes_min = 64,
49 .period_bytes_max = 12544,
50 .periods_min = 2,
51 .periods_max = 98
55 static int snd_pd_capture_open(struct snd_pcm_substream *substream)
57 struct poseidon *p = snd_pcm_substream_chip(substream);
58 struct poseidon_audio *pa = &p->audio;
59 struct snd_pcm_runtime *runtime = substream->runtime;
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;
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;
75 static int snd_pd_pcm_close(struct snd_pcm_substream *substream)
77 struct poseidon *p = snd_pcm_substream_chip(substream);
78 struct poseidon_audio *pa = &p->audio;
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;
87 static int snd_pd_hw_capture_params(struct snd_pcm_substream *substream,
88 struct snd_pcm_hw_params *hw_params)
90 struct snd_pcm_runtime *runtime = substream->runtime;
91 unsigned int size;
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);
99 runtime->dma_area = vmalloc(size);
100 if (!runtime->dma_area)
101 return -ENOMEM;
102 else
103 runtime->dma_bytes = size;
104 return 0;
107 static int audio_buf_free(struct poseidon *p)
109 struct poseidon_audio *pa = &p->audio;
110 int i;
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;
120 static int snd_pd_hw_capture_free(struct snd_pcm_substream *substream)
122 struct poseidon *p = snd_pcm_substream_chip(substream);
124 logpm();
125 audio_buf_free(p);
126 return 0;
129 static int snd_pd_prepare(struct snd_pcm_substream *substream)
131 return 0;
134 #define AUDIO_TRAILER_SIZE (16)
135 static inline void handle_audio_data(struct urb *urb, int *period_elapsed)
137 struct poseidon_audio *pa = urb->context;
138 struct snd_pcm_runtime *runtime = pa->capture_pcm_substream->runtime;
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;
145 if (urb->actual_length == AUDIO_BUF_SIZE - 4)
146 len -= (AUDIO_TRAILER_SIZE / stride);
148 /* do the copy */
149 if (oldptr + len >= runtime->buffer_size) {
150 unsigned int cnt = runtime->buffer_size - oldptr;
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);
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;
164 pa->copied_position += (len);
165 if (pa->copied_position >= runtime->period_size) {
166 pa->copied_position -= runtime->period_size;
167 *period_elapsed = 1;
169 snd_pcm_stream_unlock(pa->capture_pcm_substream);
172 static void complete_handler_audio(struct urb *urb)
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;
179 if (1 == pa->card_close || pa->capture_stream != STREAM_ON)
180 return;
182 if (urb->status != 0) {
183 /*if (urb->status == -ESHUTDOWN)*/
184 return;
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);
195 ret = usb_submit_urb(urb, GFP_ATOMIC);
196 if (ret < 0)
197 log("audio urb failed (errcod = %i)", ret);
198 return;
201 static int fire_audio_urb(struct poseidon *p)
203 int i, ret = 0;
204 struct poseidon_audio *pa = &p->audio;
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);
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);
216 log();
217 return ret;
220 static int snd_pd_capture_trigger(struct snd_pcm_substream *substream, int cmd)
222 struct poseidon *p = snd_pcm_substream_chip(substream);
223 struct poseidon_audio *pa = &p->audio;
225 if (debug_mode)
226 log("cmd %d, audio stat : %d\n", cmd, pa->capture_stream);
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;
234 pa->rcv_position = pa->copied_position = 0;
235 pa->capture_stream = STREAM_ON;
237 if (in_hibernation(p))
238 return 0;
239 fire_audio_urb(p);
240 return 0;
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;
253 static snd_pcm_uframes_t
254 snd_pd_capture_pointer(struct snd_pcm_substream *substream)
256 struct poseidon *p = snd_pcm_substream_chip(substream);
257 struct poseidon_audio *pa = &p->audio;
258 return pa->rcv_position;
261 static struct page *snd_pcm_pd_get_page(struct snd_pcm_substream *subs,
262 unsigned long offset)
264 void *pageptr = subs->runtime->dma_area + offset;
265 return vmalloc_to_page(pageptr);
268 static 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,
280 #ifdef CONFIG_PM
281 int pm_alsa_suspend(struct poseidon *p)
283 logpm(p);
284 audio_buf_free(p);
285 return 0;
288 int pm_alsa_resume(struct poseidon *p)
290 logpm(p);
291 fire_audio_urb(p);
292 return 0;
294 #endif
296 int poseidon_audio_init(struct poseidon *p)
298 struct poseidon_audio *pa = &p->audio;
299 struct snd_card *card;
300 struct snd_pcm *pcm;
301 int ret;
303 ret = snd_card_create(-1, "Telegent", THIS_MODULE, 0, &card);
304 if (ret != 0)
305 return ret;
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");
313 strcpy(card->driver, "ALSA driver");
314 strcpy(card->shortname, "poseidon Audio");
315 strcpy(card->longname, "poseidon ALSA Audio");
317 if (snd_card_register(card)) {
318 snd_card_free(card);
319 return -ENOMEM;
321 pa->card = card;
322 return 0;
325 int poseidon_audio_free(struct poseidon *p)
327 struct poseidon_audio *pa = &p->audio;
329 if (pa->card)
330 snd_card_free(pa->card);
331 return 0;