upload tizen1.0 source
[kernel/linux-2.6.36.git] / drivers / media / video / tlg2300 / pd-alsa.c
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
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
27
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,
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
55 static 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
75 static 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
87 static 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
107 static 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
120 static 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
129 static int snd_pd_prepare(struct snd_pcm_substream *substream)
130 {
131         return 0;
132 }
133
134 #define AUDIO_TRAILER_SIZE      (16)
135 static 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
172 static 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
201 static 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
220 static 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
253 static snd_pcm_uframes_t
254 snd_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
261 static 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
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,
278 };
279
280 #ifdef CONFIG_PM
281 int pm_alsa_suspend(struct poseidon *p)
282 {
283         logpm(p);
284         audio_buf_free(p);
285         return 0;
286 }
287
288 int pm_alsa_resume(struct poseidon *p)
289 {
290         logpm(p);
291         fire_audio_urb(p);
292         return 0;
293 }
294 #endif
295
296 int 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
325 int 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 }