aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/em28xx/em28xx-audio.c
diff options
context:
space:
mode:
authorMarkus Rechberger <mrechberger@gmail.com>2008-01-05 07:55:47 -0500
committerMauro Carvalho Chehab <mchehab@infradead.org>2008-01-25 16:04:31 -0500
commita52932b405f23082f78ff12f4dd3e1741fcaab6f (patch)
treec9855ad115bc735cfa3cab5693c45b47ea81ddd6 /drivers/media/video/em28xx/em28xx-audio.c
parent74f38a82376fb1b289d0957429ba45349f0cad62 (diff)
V4L/DVB (6949): Adds em28xx-audio module
em28xx-audio module exports em28xx Vendor Class audio as an -alsa driver. This module were written based on usbaudio driver by Markus Rechberger. Recently, he acked to allow us to merge it on kernel: http://lists-archives.org/video4linux/20408-supporting-prolink-pixelview-405-dvd-maker.html Thanks to Markus Rechberger <mrechberger@gmail.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers/media/video/em28xx/em28xx-audio.c')
-rw-r--r--drivers/media/video/em28xx/em28xx-audio.c474
1 files changed, 474 insertions, 0 deletions
diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c
new file mode 100644
index 000000000000..e1e1eda517a7
--- /dev/null
+++ b/drivers/media/video/em28xx/em28xx-audio.c
@@ -0,0 +1,474 @@
1/*
2 * Empiatech em28x1 audio extension
3 *
4 * Copyright (C) 2006 Markus Rechberger <mrechberger@gmail.com>
5 *
6 * This driver is based on my previous au600 usb pstn audio driver
7 * and inherits all the copyrights
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24#include <linux/kernel.h>
25#include <linux/usb.h>
26#include <linux/init.h>
27#include <linux/sound.h>
28#include <linux/spinlock.h>
29#include <linux/soundcard.h>
30#include <linux/slab.h>
31#include <linux/vmalloc.h>
32#include <linux/proc_fs.h>
33#include <linux/moduleparam.h>
34#include <sound/driver.h>
35#include <sound/core.h>
36#include <sound/pcm.h>
37#include <sound/pcm_params.h>
38#include <sound/info.h>
39#include <sound/initval.h>
40#include <sound/control.h>
41//#include <linux/video_decoder.h>
42//#include <media/tuner.h>
43#include <media/v4l2-common.h>
44#include "em28xx.h"
45
46static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
47static int em28xx_cmd(struct em28xx *dev, int cmd,int arg);
48
49
50static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, size_t size)
51{
52 struct snd_pcm_runtime *runtime = subs->runtime;
53 if(runtime->dma_area){
54 if(runtime->dma_bytes > size)
55 return 0;
56 vfree(runtime->dma_area);
57 }
58 runtime->dma_area = vmalloc(size);
59 if(!runtime ->dma_area)
60 return -ENOMEM;
61 runtime->dma_bytes = size;
62 return 0;
63}
64
65static struct snd_pcm_hardware snd_em28xx_hw_capture = {
66 .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP_VALID,
67 .formats = SNDRV_PCM_FMTBIT_S16_LE,
68 .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT,
69 .rate_min = 48000,
70 .rate_max = 48000,
71 .channels_min = 2,
72 .channels_max = 2,
73 .buffer_bytes_max = 62720*8, /* just about the value in usbaudio.c */
74 .period_bytes_min = 64, //12544/2,
75 .period_bytes_max = 12544,
76 .periods_min = 2,
77 .periods_max = 98, //12544,
78};
79
80static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
81{
82 int ret = 0;
83 int mode;
84 struct em28xx *dev = snd_pcm_substream_chip(substream);
85 struct snd_pcm_runtime *runtime = substream->runtime;
86 printk("opening radio device and trying to acquire exclusive lock\n");
87 switch(dev->mode){
88 case TUNER_STUB_DVBC_TV:
89 case TUNER_STUB_DVBT_TV:
90 case TUNER_STUB_ATSC_TV:
91 /* digital has no support for analog audio */
92 if (ret != 0 ) {
93 printk("device is already in use by DVB-T\n");
94 return -EINVAL;
95 } else {
96 struct v4l2_tuner tuner;
97 printk("switching device to FM mode\n");
98
99 mode = TUNER_STUB_RADIO;
100 memset(&tuner, 0x0, sizeof(struct v4l2_tuner));
101 tuner.type = V4L2_TUNER_RADIO;
102
103 /* enable GPIO for analog TV */
104 dev->em28xx_gpio_control(dev, EM28XX_MODE, (void*)mode);
105 dev->mode = mode;
106 /* upload firmware */
107 tuner_run_cmd(dev->tobj, TUNER_CMD_INIT, (void*)mode);
108
109 /* required for devices which have kerneldriver dependencies */
110// em28xx_config(dev);
111// em28xx_config_i2c(dev);
112
113 /* this is moreover to switch the decoder to FM */
114 em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, &tuner);
115
116 dev->em28xx_write_regs(dev, 0x0f, "\x87", 1);
117 ret = dev->em28xx_acquire(dev, EM28XX_RADIO, 1);
118 em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, 0);
119 /* TODO switch to FM mode */
120
121 printk("em28xx-audio: %d mode\n", mode);
122 tuner_run_cmd(dev->tobj, TUNER_CMD_G_MODE, &mode);
123 printk("retrieved mode from tuner: %d\n",mode);
124 }
125 break;
126
127 case TUNER_STUB_ANALOG_TV:
128 printk("em28xx-audio: device is currently in analog TV mode\n");
129 /* unmute by default */
130 dev->em28xx_write_regs(dev, 0x0f, "\x87", 1);
131 break;
132 case TUNER_STUB_RADIO:
133 /* check current mode and put a hard lock onto it */
134 printk("em28xx-audio: device is currently in analogue FM mode\n");
135 /* unmute by default here */
136 dev->em28xx_write_regs(dev, 0x0f, "\x87", 1);
137 ret = dev->em28xx_acquire(dev, EM28XX_RADIO, 1);
138 if ( ret == 0 )
139 printk("device is locked in fmradio mode now\n");
140 break;
141 default:
142 printk("em28xx-audio: unhandled mode %d\n", dev->mode);
143 }
144
145 runtime->hw = snd_em28xx_hw_capture;
146 if(dev->alt == 0 && dev->adev->users == 0 ) {
147 int errCode;
148 dev->alt = 7;
149 errCode = usb_set_interface(dev->udev, 0, 7);
150 printk("changing alternate number to 7\n");
151 }
152 dev->adev->users++;
153 snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
154 dev->adev->capture_pcm_substream = substream;
155 runtime->private_data = dev;
156 return 0;
157}
158
159static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream)
160{
161 struct em28xx *dev = snd_pcm_substream_chip(substream);
162 int amode = 0;
163 dev->adev->users--;
164
165 /* decrease audio reference */
166 switch(dev->mode) {
167 case TUNER_STUB_ANALOG_TV:
168 amode = EM28XX_VIDEO;
169 break;
170 case TUNER_STUB_RADIO:
171 amode = EM28XX_RADIO;
172 break;
173 default:
174 printk("invalid mode: %d\n",dev->mode);
175 break;
176 }
177
178 dev->em28xx_acquire(dev, amode, 0);
179
180 if(dev->adev->users == 0 && dev->adev->shutdown == 1) {
181 printk("audio users: %d\n",dev->adev->users);
182 printk("disabling audio stream!\n");
183 dev->adev->shutdown = 0;
184 printk("released lock\n");
185 em28xx_cmd(dev,EM28XX_CAPTURE_STREAM_EN,0);
186 }
187 return 0;
188}
189
190static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params)
191{
192 unsigned int channels, rate, format;
193 int ret;
194 ret = snd_pcm_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params));
195 format = params_format(hw_params);
196 rate = params_rate(hw_params);
197 channels = params_channels(hw_params);
198 /* TODO: set up em28xx audio chip to deliver the correct audio format, current default is 48000hz multiplexed => 96000hz mono
199 which shouldn't matter since analogue TV only supports mono*/
200 return 0;
201}
202
203static int snd_em28xx_hw_capture_free(struct snd_pcm_substream *substream)
204{
205 struct em28xx *dev = snd_pcm_substream_chip(substream);
206 if(dev->adev->capture_stream==STREAM_ON){
207 em28xx_cmd(dev,EM28XX_CAPTURE_STREAM_EN,0);
208 }
209 return 0;
210}
211
212static int snd_em28xx_prepare(struct snd_pcm_substream *substream)
213{
214 return 0;
215}
216
217static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream, int cmd)
218{
219 struct em28xx *dev = snd_pcm_substream_chip(substream);
220 switch(cmd){
221 case SNDRV_PCM_TRIGGER_START:
222 em28xx_cmd(dev,EM28XX_CAPTURE_STREAM_EN,1);
223 return 0;
224 case SNDRV_PCM_TRIGGER_STOP:
225 dev->adev->shutdown=1;
226 return 0;
227 default:
228 return -EINVAL;
229 }
230}
231
232static void em28xx_audio_isocirq(struct urb *urb)
233{
234 struct em28xx *dev=urb->context;
235 int i;
236 unsigned int oldptr;
237 unsigned long flags;
238 int period_elapsed = 0;
239 int status;
240 unsigned char *cp;
241 unsigned int stride;
242 struct snd_pcm_substream *substream;
243 struct snd_pcm_runtime *runtime;
244 if(dev->adev->capture_pcm_substream){
245 substream=dev->adev->capture_pcm_substream;
246 runtime=substream->runtime;
247
248 stride = runtime->frame_bits >> 3;
249 for(i=0;i<urb->number_of_packets;i++){
250 int length=urb->iso_frame_desc[i].actual_length/stride;
251 cp=(unsigned char *) urb->transfer_buffer + urb->iso_frame_desc[i].offset;
252
253 if(!length)
254 continue;
255
256 spin_lock_irqsave(&dev->adev->slock, flags);
257 oldptr = dev->adev->hwptr_done_capture;
258 dev->adev->hwptr_done_capture +=length;
259 if(dev->adev->hwptr_done_capture >= runtime->buffer_size)
260 dev->adev->hwptr_done_capture -= runtime->buffer_size;
261
262 dev->adev->capture_transfer_done += length;
263 if(dev->adev->capture_transfer_done >= runtime->period_size){
264 dev->adev->capture_transfer_done -= runtime->period_size;
265 period_elapsed=1;
266 }
267 spin_unlock_irqrestore(&dev->adev->slock, flags);
268
269 if(oldptr + length >= runtime->buffer_size){
270 unsigned int cnt = runtime->buffer_size-oldptr-1;
271 memcpy(runtime->dma_area+oldptr*stride, cp , cnt*stride);
272 memcpy(runtime->dma_area, cp + cnt, length*stride - cnt*stride);
273 } else {
274 memcpy(runtime->dma_area+oldptr*stride, cp, length*stride);
275 }
276 }
277 if(period_elapsed){
278 snd_pcm_period_elapsed(substream);
279 }
280 }
281 urb->status = 0;
282
283 if(dev->adev->shutdown)
284 return;
285
286 if((status = usb_submit_urb(urb, GFP_ATOMIC))){
287 em28xx_errdev("resubmit of audio urb failed (error=%i)\n", status);
288 }
289 return;
290}
291
292static int em28xx_isoc_audio_deinit(struct em28xx *dev)
293{
294 int i;
295 for(i=0;i<EM28XX_AUDIO_BUFS;i++){
296 usb_kill_urb(dev->adev->urb[i]);
297 usb_free_urb(dev->adev->urb[i]);
298 dev->adev->urb[i]=NULL;
299 }
300 return 0;
301}
302
303static int em28xx_init_audio_isoc(struct em28xx *dev)
304{
305 int i;
306 int errCode;
307 const int sb_size=EM28XX_NUM_AUDIO_PACKETS * EM28XX_AUDIO_MAX_PACKET_SIZE;
308
309
310 for(i=0;i<EM28XX_AUDIO_BUFS;i++){
311 struct urb *urb;
312 int j,k;
313 dev->adev->transfer_buffer[i]=kmalloc(sb_size,GFP_ATOMIC);
314 if(!dev->adev->transfer_buffer[i]){
315 return -ENOMEM;
316 }
317 memset(dev->adev->transfer_buffer[i],0x80,sb_size);
318 urb = usb_alloc_urb(EM28XX_NUM_AUDIO_PACKETS,GFP_ATOMIC);
319 if(urb){
320 urb->dev=dev->udev;
321 urb->context=dev;
322 urb->pipe=usb_rcvisocpipe(dev->udev,0x83);
323 urb->transfer_flags = URB_ISO_ASAP;
324 urb->transfer_buffer = dev->adev->transfer_buffer[i];
325 urb->interval=1;
326 urb->complete = em28xx_audio_isocirq;
327 urb->number_of_packets = EM28XX_NUM_AUDIO_PACKETS;
328 urb->transfer_buffer_length = sb_size;
329 for(j=k=0; j<EM28XX_NUM_AUDIO_PACKETS;j++,k+=EM28XX_AUDIO_MAX_PACKET_SIZE){
330 urb->iso_frame_desc[j].offset = k;
331 urb->iso_frame_desc[j].length=EM28XX_AUDIO_MAX_PACKET_SIZE;
332 }
333 dev->adev->urb[i]=urb;
334 } else {
335 return -ENOMEM;
336 }
337 }
338 for(i=0;i<EM28XX_AUDIO_BUFS;i++){
339 errCode = usb_submit_urb(dev->adev->urb[i], GFP_ATOMIC);
340 if (errCode){
341 em28xx_isoc_audio_deinit(dev);
342 return errCode;
343 }
344 }
345 return 0;
346}
347
348
349static int em28xx_cmd(struct em28xx *dev, int cmd,int arg)
350{
351 switch(cmd){
352 case EM28XX_CAPTURE_STREAM_EN:
353 if(dev->adev->capture_stream == STREAM_OFF && arg==1){
354 dev->adev->capture_stream=STREAM_ON;
355 em28xx_init_audio_isoc(dev);
356 } else if (dev->adev->capture_stream==STREAM_ON && arg==0){
357 dev->adev->capture_stream=STREAM_OFF;
358 em28xx_isoc_audio_deinit(dev);
359 } else {
360 printk("An underrun occured very likely... ignoring it\n");
361 }
362 return 0;
363 default:
364 return -EINVAL;
365 }
366}
367
368static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream *substream)
369{
370 struct em28xx *dev;
371 snd_pcm_uframes_t hwptr_done;
372 dev = snd_pcm_substream_chip(substream);
373 hwptr_done = dev->adev->hwptr_done_capture;
374 return hwptr_done;
375}
376
377static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
378 unsigned long offset)
379{
380 void *pageptr = subs->runtime->dma_area + offset;
381 return vmalloc_to_page(pageptr);
382}
383
384static struct snd_pcm_ops snd_em28xx_pcm_capture = {
385 .open = snd_em28xx_capture_open,
386 .close = snd_em28xx_pcm_close,
387 .ioctl = snd_pcm_lib_ioctl,
388 .hw_params = snd_em28xx_hw_capture_params,
389 .hw_free = snd_em28xx_hw_capture_free,
390 .prepare = snd_em28xx_prepare,
391 .trigger = snd_em28xx_capture_trigger,
392 .pointer = snd_em28xx_capture_pointer,
393 .page = snd_pcm_get_vmalloc_page,
394};
395
396
397static int em28xx_audio_init(struct em28xx *dev)
398{
399 struct em28xx_audio *adev;
400 struct snd_pcm *pcm;
401 struct snd_card *card;
402 static int devnr;
403 int ret;
404 int err;
405 printk("em28xx-audio.c: probing for em28x1 non standard usbaudio\n");
406 printk("em28xx-audio.c: Copyright (C) 2006 Markus Rechberger\n");
407 adev=kzalloc(sizeof(*adev),GFP_KERNEL);
408 if(!adev){
409 printk("em28xx-audio.c: out of memory\n");
410 return -1;
411 }
412 card = snd_card_new(index[devnr], "Em28xx Audio", THIS_MODULE,0);
413 if(card==NULL){
414 kfree(adev);
415 return -ENOMEM;
416 }
417
418 spin_lock_init(&adev->slock);
419 ret=snd_pcm_new(card, "Em28xx Audio", 0, 0, 1, &pcm);
420 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_em28xx_pcm_capture);
421 pcm->info_flags = 0;
422 pcm->private_data = dev;
423 strcpy(pcm->name,"Empia 28xx Capture");
424 strcpy(card->driver, "Empia Em28xx Audio");
425 strcpy(card->shortname, "Em28xx Audio");
426 strcpy(card->longname,"Empia Em28xx Audio");
427
428 if((err = snd_card_register(card))<0){
429 snd_card_free(card);
430 return -ENOMEM;
431 }
432 adev->sndcard=card;
433 adev->udev=dev->udev;
434 dev->adev=adev;
435 return 0;
436}
437
438static int em28xx_audio_fini(struct em28xx *dev)
439{
440 if(dev==NULL)
441 return 0;
442 if(dev->adev){
443 snd_card_free(dev->adev->sndcard);
444 kfree(dev->adev);
445 dev->adev=NULL;
446 }
447 return 0;
448}
449
450static struct em28xx_ops audio_ops = {
451 .id = EM28XX_AUDIO,
452 .name = "Em28xx Audio Extension",
453 .init = em28xx_audio_init,
454 .fini = em28xx_audio_fini,
455};
456
457static int __init em28xx_alsa_register(void)
458{
459 request_module("em28xx");
460 request_module("tuner");
461 return em28xx_register_extension(&audio_ops);
462}
463
464static void __exit em28xx_alsa_unregister(void)
465{
466 em28xx_unregister_extension(&audio_ops);
467}
468
469MODULE_LICENSE("GPL");
470MODULE_AUTHOR("Markus Rechberger <mrechberger@gmail.com>");
471MODULE_DESCRIPTION("Em28xx Audio driver");
472
473module_init(em28xx_alsa_register);
474module_exit(em28xx_alsa_unregister);