From 20873ae35cca55b14a5932093524e38945be87b9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 1 Jan 2008 15:43:02 -0200 Subject: Fix in-kernel ivtv compilation From: Mauro Carvalho Chehab Due to tuner-xc2028.h, some extra directories should be added at .h search. CC: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/ivtv/Makefile | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'linux/drivers/media') diff --git a/linux/drivers/media/video/ivtv/Makefile b/linux/drivers/media/video/ivtv/Makefile index e8eefd96d..a0389014f 100644 --- a/linux/drivers/media/video/ivtv/Makefile +++ b/linux/drivers/media/video/ivtv/Makefile @@ -6,3 +6,8 @@ ivtv-objs := ivtv-routing.o ivtv-cards.o ivtv-controls.o \ obj-$(CONFIG_VIDEO_IVTV) += ivtv.o obj-$(CONFIG_VIDEO_FB_IVTV) += ivtvfb.o + +EXTRA_CFLAGS += -Idrivers/media/video +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core +EXTRA_CFLAGS += -Idrivers/media/dvb/frontends + -- cgit v1.2.3 From 8b702294eb6b500645e914552b2f5077d3251ae7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 5 Jan 2008 10:53:54 -0200 Subject: Improve audio setup handling From: Mauro Carvalho Chehab It is possible to select audio inputs via em28xx or via ac97 functions. This patch allows configuring a board to use either one way. It also do some cleanups at audio setup configurations. Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-core.c | 87 ++++++++++++++++++++++--- linux/drivers/media/video/em28xx/em28xx-video.c | 21 ++---- linux/drivers/media/video/em28xx/em28xx.h | 35 ++++------ 3 files changed, 94 insertions(+), 49 deletions(-) (limited to 'linux/drivers/media') diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index f229a83da..98153d70d 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -303,7 +303,7 @@ int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val, * em28xx_write_ac97() * write a 16 bit value to the specified AC97 address (LSB first!) */ -int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 * val) +static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 *val) { int ret; u8 addr = reg & 0x7f; @@ -319,24 +319,91 @@ int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 * val) return 0; } +int em28xx_set_audio_source(struct em28xx *dev) +{ + static char *enable = "\x08\x08"; + static char *disable = "\x08\x88"; + char *video = enable, *line = disable; + int ret, no_ac97; + u8 input; + + if (dev->is_em2800) { + if (dev->ctl_ainput) + input = EM2800_AUDIO_SRC_LINE; + else + input = EM2800_AUDIO_SRC_TUNER; + + ret = em28xx_write_regs(dev, EM2800_AUDIOSRC_REG, &input, 1); + if (ret < 0) + return ret; + } + + if (dev->has_msp34xx) + input = EM28XX_AUDIO_SRC_TUNER; + else { + switch (dev->ctl_ainput) { + case EM28XX_AMUX_VIDEO: + input = EM28XX_AUDIO_SRC_TUNER; + no_ac97 = 1; + break; + case EM28XX_AMUX_LINE_IN: + input = EM28XX_AUDIO_SRC_LINE; + no_ac97 = 1; + break; + case EM28XX_AMUX_AC97_VIDEO: + input = EM28XX_AUDIO_SRC_LINE; + break; + case EM28XX_AMUX_AC97_LINE_IN: + input = EM28XX_AUDIO_SRC_LINE; + video = disable; + line = enable; + break; + } + } + + ret = em28xx_write_reg_bits(dev, AUDIOSRC_REG, input, 0xc0); + if (ret < 0) + return ret; + + if (no_ac97) + return 0; + + /* Sets AC97 mixer registers */ + + ret = em28xx_write_ac97(dev, VIDEO_AC97, video); + if (ret < 0) + return ret; + + ret = em28xx_write_ac97(dev, LINE_IN_AC97, line); + + return ret; +} + int em28xx_audio_analog_set(struct em28xx *dev) { + int ret; char s[2] = { 0x00, 0x00 }; + s[0] |= 0x1f - dev->volume; s[1] |= 0x1f - dev->volume; + if (dev->mute) s[1] |= 0x80; - return em28xx_write_ac97(dev, MASTER_AC97, s); -} + ret = em28xx_write_ac97(dev, MASTER_AC97, s); + if (ret < 0) + return ret; -#if 0 -int em28xx_audio_analog_mute(struct em28xx *dev, int mute) -{ - /* (un)mute master mixer with maximum volume level */ - return em28xx_write_ac97(dev, MASTER_AC97, - mute ? "\x00\x80" : "\x00\x00"); + ret = em28xx_write_reg_bits(dev, XCLK_REG, + dev->mute ? 0x00 : 0x80, 0x80); + if (ret < 0) + return ret; + + /* Selects the proper audio input */ + ret = em28xx_set_audio_source(dev); + + return ret; } -#endif +EXPORT_SYMBOL_GPL(em28xx_audio_analog_set); int em28xx_colorlevels_set_default(struct em28xx *dev) { diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 44621bafc..d9eb1bae0 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -142,11 +142,13 @@ static int em28xx_config(struct em28xx *dev) em28xx_write_regs_req(dev,0x00,0x11,"\x51",1); #endif - em28xx_audio_usb_mute(dev, 1); dev->mute = 1; /* maybe not the right place... */ dev->volume = 0x1f; + + /* Init XCLK_REG, audio muted */ + dev->em28xx_write_regs(dev, XCLK_REG, "\x87", 1); + em28xx_audio_analog_set(dev); - em28xx_audio_analog_setup(dev); em28xx_outfmt_set_yuv422(dev); em28xx_colorlevels_set_default(dev); em28xx_compression_disable(dev); @@ -188,7 +190,6 @@ static void em28xx_empty_framequeues(struct em28xx *dev) static void video_mux(struct em28xx *dev, int index) { - int ainput; struct v4l2_routing route; route.input = INPUT(index)->vmux; @@ -205,18 +206,9 @@ static void video_mux(struct em28xx *dev, int index) route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); /* Note: this is msp3400 specific */ em28xx_i2c_call_clients(dev, VIDIOC_INT_S_AUDIO_ROUTING, &route); - ainput = EM28XX_AUDIO_SRC_TUNER; - em28xx_audio_source(dev, ainput); - } else { - switch (dev->ctl_ainput) { - case 0: - ainput = EM28XX_AUDIO_SRC_TUNER; - break; - default: - ainput = EM28XX_AUDIO_SRC_LINE; - } - em28xx_audio_source(dev, ainput); } + + em28xx_set_audio_source(dev); } /* Usage lock check functions */ @@ -312,7 +304,6 @@ static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl) case V4L2_CID_AUDIO_MUTE: if (ctrl->value != dev->mute) { dev->mute = ctrl->value; - em28xx_audio_usb_mute(dev, ctrl->value); return em28xx_audio_analog_set(dev); } return 0; diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 5e5f4e7a9..0196371d6 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -154,10 +154,17 @@ enum enum28xx_itype { EM28XX_RADIO, }; +enum em28xx_amux { + EM28XX_AMUX_VIDEO, + EM28XX_AMUX_LINE_IN, + EM28XX_AMUX_AC97_VIDEO, + EM28XX_AMUX_AC97_LINE_IN, +}; + struct em28xx_input { enum enum28xx_itype type; unsigned int vmux; - unsigned int amux; + enum em28xx_amux amux; }; #define INPUT(nr) (&em28xx_boards[dev->model].input[nr]) @@ -328,8 +335,9 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len); int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val, u8 bitmask); -int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 * val); +int em28xx_set_audio_source(struct em28xx *dev); int em28xx_audio_analog_set(struct em28xx *dev); + int em28xx_colorlevels_set_default(struct em28xx *dev); int em28xx_capture_start(struct em28xx *dev, int start); int em28xx_outfmt_set_yuv422(struct em28xx *dev); @@ -401,6 +409,7 @@ extern const unsigned int em28xx_bcount; /* em202 registers */ #define MASTER_AC97 0x02 +#define LINE_IN_AC97 0x10 #define VIDEO_AC97 0x14 /* register settings */ @@ -425,28 +434,6 @@ extern const unsigned int em28xx_bcount; printk(KERN_WARNING "%s: "fmt,\ dev->name , ##arg); } while (0) -inline static int em28xx_audio_source(struct em28xx *dev, int input) -{ - if(dev->is_em2800){ - u8 tmp = EM2800_AUDIO_SRC_TUNER; - if(input == EM28XX_AUDIO_SRC_LINE) - tmp = EM2800_AUDIO_SRC_LINE; - em28xx_write_regs(dev, EM2800_AUDIOSRC_REG, &tmp, 1); - } - return em28xx_write_reg_bits(dev, AUDIOSRC_REG, input, 0xc0); -} - -inline static int em28xx_audio_usb_mute(struct em28xx *dev, int mute) -{ - return em28xx_write_reg_bits(dev, XCLK_REG, mute ? 0x00 : 0x80, 0x80); -} - -inline static int em28xx_audio_analog_setup(struct em28xx *dev) -{ - /* unmute video mixer with default volume level */ - return em28xx_write_ac97(dev, VIDEO_AC97, "\x08\x08"); -} - inline static int em28xx_compression_disable(struct em28xx *dev) { /* side effect of disabling scaler and mixer */ -- cgit v1.2.3 From 8071d802deb562fb1801a2f0809b11b9a7d6c3a0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 5 Jan 2008 10:54:58 -0200 Subject: HVR950 requires additional settings for audio to properly work From: Mauro Carvalho Chehab Thanks to Markus Rechberger for retriving those commands. Also, MTS firmware is required for audio to work on HVR950. Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 45 ++++++++++++++++++++++--- linux/drivers/media/video/em28xx/em28xx.h | 13 ++++--- 2 files changed, 48 insertions(+), 10 deletions(-) (limited to 'linux/drivers/media') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 809cc0a4a..34f5fab0b 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -180,6 +180,7 @@ struct em28xx_board em28xx_boards[] = { .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_XC2028, .has_tuner = 1, + .mts_firmware = 1, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -194,6 +195,9 @@ struct em28xx_board em28xx_boards[] = { .vmux = TVP5150_SVIDEO, .amux = 1, } }, + + /* gpio's 4, 1, 0 */ + .analog_gpio = 0x003d2d, }, [EM2880_BOARD_TERRATEC_HYBRID_XS] = { .name = "Terratec Hybrid XS", @@ -454,8 +458,18 @@ void em28xx_pre_card_setup(struct em28xx *dev) case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950: case EM2880_BOARD_TERRATEC_HYBRID_XS: - /* reset through GPIO? */ - em28xx_write_regs_req(dev, 0x00, 0x08, "\x7d", 1); + em28xx_write_regs(dev, XCLK_REG, "\x27", 1); + em28xx_write_regs(dev, I2C_CLK_REG, "\x40", 1); + em28xx_write_regs(dev, 0x08, "\xff", 1); + em28xx_write_regs(dev, 0x04, "\x00", 1); + msleep(100); + em28xx_write_regs(dev, 0x04, "\x08", 1); + msleep(100); + em28xx_write_regs(dev, 0x08, "\xff", 1); + msleep(50); + em28xx_write_regs(dev, 0x08, "\x2d", 1); + msleep(50); + em28xx_write_regs(dev, 0x08, "\x3d", 1); break; } } @@ -470,13 +484,31 @@ static int em28xx_tuner_callback(void *ptr, int command, int arg) switch (command) { case XC2028_TUNER_RESET: - /* FIXME: This is device-dependent */ + { + char gpio0, gpio1, gpio4; + + /* GPIO and initialization codes for analog TV */ + gpio0 = dev->analog_gpio & 0xff; + gpio1 = (dev->analog_gpio >> 8) & 0xff; + gpio4 = dev->analog_gpio >> 24; + dev->em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1); dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x67", 1); - msleep(140); + if (gpio4) { + dev->em28xx_write_regs(dev, 0x04, &gpio4, 1); + msleep(140); + } + + msleep(6); + dev->em28xx_write_regs(dev, 0x08, &gpio0, 1); + msleep(10); + dev->em28xx_write_regs(dev, 0x08, &gpio1, 1); + msleep(5); + break; } + } return rc; } @@ -609,6 +641,7 @@ static void em28xx_set_model(struct em28xx *dev) dev->tda9887_conf = em28xx_boards[dev->model].tda9887_conf; dev->decoder = em28xx_boards[dev->model].decoder; dev->video_inputs = em28xx_boards[dev->model].vchannels; + dev->analog_gpio = em28xx_boards[dev->model].analog_gpio; if (!em28xx_boards[dev->model].has_tuner) dev->tuner_type = UNSET; @@ -644,7 +677,9 @@ void em28xx_card_setup(struct em28xx *dev) if (tv.has_ir) request_module("ir-kbd-i2c"); #endif - /* FIXME: Should also retrieve decoder processor type */ + /* enable audio 12 mhz i2s */ + em28xx_write_regs(dev, XCLK_REG, "\xa7", 1); + msleep(10); break; } diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 0196371d6..4d318b3d5 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -181,12 +181,13 @@ struct em28xx_board { int tuner_type; /* i2c flags */ - unsigned int is_em2800; unsigned int tda9887_conf; + unsigned int is_em2800:1; unsigned int has_tuner:1; unsigned int has_msp34xx:1; unsigned int mts_firmware:1; + unsigned int analog_gpio; enum em28xx_decoder decoder; @@ -220,15 +221,16 @@ struct em28xx { char name[30]; /* name (including minor) of the device */ int model; /* index in the device_data struct */ int devno; /* marks the number of this device */ - unsigned int is_em2800; - int video_inputs; /* number of video inputs */ - struct list_head devlist; + unsigned int analog_gpio; + unsigned int is_em2800:1; unsigned int has_tuner:1; unsigned int has_msp34xx:1; unsigned int has_tda9887:1; - unsigned int stream_on:1; /* Locks streams */ + int video_inputs; /* number of video inputs */ + struct list_head devlist; + u32 i2s_speed; /* I2S speed for audio digital stream */ enum em28xx_decoder decoder; @@ -358,6 +360,7 @@ extern const unsigned int em28xx_bcount; #define EM2800_AUDIOSRC_REG 0x08 /* em28xx registers */ +#define I2C_CLK_REG 0x06 #define CHIPID_REG 0x0a #define USBSUSP_REG 0x0c /* */ -- cgit v1.2.3 From d4abb4a492ba3c224761ecae5fde94c14d2b462f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 5 Jan 2008 10:55:47 -0200 Subject: Adds em28xx-audio module From: Mauro Carvalho Chehab 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 Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-audio.c | 541 ++++++++++++++++++++++++ 1 file changed, 541 insertions(+) create mode 100644 linux/drivers/media/video/em28xx/em28xx-audio.c (limited to 'linux/drivers/media') diff --git a/linux/drivers/media/video/em28xx/em28xx-audio.c b/linux/drivers/media/video/em28xx/em28xx-audio.c new file mode 100644 index 000000000..03e752776 --- /dev/null +++ b/linux/drivers/media/video/em28xx/em28xx-audio.c @@ -0,0 +1,541 @@ +/* + * Empiatech em28x1 audio extension + * + * Copyright (C) 2006 Markus Rechberger + * + * This driver is based on my previous au600 usb pstn audio driver + * and inherits all the copyrights + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +//#include +#include +#include "em28xx.h" + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static int em28xx_cmd(struct em28xx *dev, int cmd,int arg); + + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) +static int snd_pcm_alloc_vmalloc_buffer(snd_pcm_substream_t *subs, size_t size) +#else +static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, size_t size) +#endif +{ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) + snd_pcm_runtime_t *runtime = subs->runtime; +#else + struct snd_pcm_runtime *runtime = subs->runtime; +#endif + 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; + runtime->dma_bytes = size; + return 0; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) +static snd_pcm_hardware_t snd_em28xx_hw_capture = { +#else +static struct snd_pcm_hardware snd_em28xx_hw_capture = { +#endif + .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_CONTINUOUS | SNDRV_PCM_RATE_KNOT, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 62720*8, /* just about the value in usbaudio.c */ + .period_bytes_min = 64, //12544/2, + .period_bytes_max = 12544, + .periods_min = 2, + .periods_max = 98, //12544, +}; + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) +static int snd_em28xx_capture_open(snd_pcm_substream_t *substream) +#else +static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) +#endif +{ + int ret = 0; + int mode; + struct em28xx *dev = snd_pcm_substream_chip(substream); +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) + snd_pcm_runtime_t *runtime = substream->runtime; +#else + struct snd_pcm_runtime *runtime = substream->runtime; +#endif + printk("opening radio device and trying to acquire exclusive lock\n"); + switch(dev->mode){ + case TUNER_STUB_DVBC_TV: + case TUNER_STUB_DVBT_TV: + case TUNER_STUB_ATSC_TV: + /* digital has no support for analog audio */ + if (ret != 0 ) { + printk("device is already in use by DVB-T\n"); + return -EINVAL; + } else { + struct v4l2_tuner tuner; + printk("switching device to FM mode\n"); + + mode = TUNER_STUB_RADIO; + memset(&tuner, 0x0, sizeof(struct v4l2_tuner)); + tuner.type = V4L2_TUNER_RADIO; + + /* enable GPIO for analog TV */ + dev->em28xx_gpio_control(dev, EM28XX_MODE, (void*)mode); + dev->mode = mode; + /* upload firmware */ + tuner_run_cmd(dev->tobj, TUNER_CMD_INIT, (void*)mode); + + /* required for devices which have kerneldriver dependencies */ +// em28xx_config(dev); +// em28xx_config_i2c(dev); + + /* this is moreover to switch the decoder to FM */ + em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, &tuner); + + dev->em28xx_write_regs(dev, 0x0f, "\x87", 1); + ret = dev->em28xx_acquire(dev, EM28XX_RADIO, 1); + em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, 0); + /* TODO switch to FM mode */ + + printk("em28xx-audio: %d mode\n", mode); + tuner_run_cmd(dev->tobj, TUNER_CMD_G_MODE, &mode); + printk("retrieved mode from tuner: %d\n",mode); + } + break; + + case TUNER_STUB_ANALOG_TV: + printk("em28xx-audio: device is currently in analog TV mode\n"); + /* unmute by default */ + dev->em28xx_write_regs(dev, 0x0f, "\x87", 1); + break; + case TUNER_STUB_RADIO: + /* check current mode and put a hard lock onto it */ + printk("em28xx-audio: device is currently in analogue FM mode\n"); + /* unmute by default here */ + dev->em28xx_write_regs(dev, 0x0f, "\x87", 1); + ret = dev->em28xx_acquire(dev, EM28XX_RADIO, 1); + if ( ret == 0 ) + printk("device is locked in fmradio mode now\n"); + break; + default: + printk("em28xx-audio: unhandled mode %d\n", dev->mode); + } + + runtime->hw = snd_em28xx_hw_capture; + if(dev->alt == 0 && dev->adev->users == 0 ) { + int errCode; + dev->alt = 7; + errCode = usb_set_interface(dev->udev, 0, 7); + printk("changing alternate number to 7\n"); + } + dev->adev->users++; + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + dev->adev->capture_pcm_substream = substream; + runtime->private_data = dev; + return 0; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) +static int snd_em28xx_pcm_close(snd_pcm_substream_t *substream) +#else +static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream) +#endif +{ + struct em28xx *dev = snd_pcm_substream_chip(substream); + int amode = 0; + dev->adev->users--; + + /* decrease audio reference */ + switch(dev->mode) { + case TUNER_STUB_ANALOG_TV: + amode = EM28XX_VIDEO; + break; + case TUNER_STUB_RADIO: + amode = EM28XX_RADIO; + break; + default: + printk("invalid mode: %d\n",dev->mode); + break; + } + + dev->em28xx_acquire(dev, amode, 0); + + if(dev->adev->users == 0 && dev->adev->shutdown == 1) { + printk("audio users: %d\n",dev->adev->users); + printk("disabling audio stream!\n"); + dev->adev->shutdown = 0; + printk("released lock\n"); + em28xx_cmd(dev,EM28XX_CAPTURE_STREAM_EN,0); + } + return 0; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) +static int snd_em28xx_hw_capture_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *hw_params) +#else +static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) +#endif +{ + unsigned int channels, rate, format; + int ret; + ret = snd_pcm_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); + format = params_format(hw_params); + rate = params_rate(hw_params); + channels = params_channels(hw_params); + /* TODO: set up em28xx audio chip to deliver the correct audio format, current default is 48000hz multiplexed => 96000hz mono + which shouldn't matter since analogue TV only supports mono*/ + return 0; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) +static int snd_em28xx_hw_capture_free(snd_pcm_substream_t *substream) +#else +static int snd_em28xx_hw_capture_free(struct snd_pcm_substream *substream) +#endif +{ + struct em28xx *dev = snd_pcm_substream_chip(substream); + if(dev->adev->capture_stream==STREAM_ON){ + em28xx_cmd(dev,EM28XX_CAPTURE_STREAM_EN,0); + } + return 0; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) +static int snd_em28xx_prepare(snd_pcm_substream_t *substream) +#else +static int snd_em28xx_prepare(struct snd_pcm_substream *substream) +#endif +{ + return 0; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) +static int snd_em28xx_capture_trigger(snd_pcm_substream_t *substream, int cmd) +#else +static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream, int cmd) +#endif +{ + struct em28xx *dev = snd_pcm_substream_chip(substream); + switch(cmd){ + case SNDRV_PCM_TRIGGER_START: + em28xx_cmd(dev,EM28XX_CAPTURE_STREAM_EN,1); + return 0; + case SNDRV_PCM_TRIGGER_STOP: + dev->adev->shutdown=1; + return 0; + default: + return -EINVAL; + } +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) +static void em28xx_audio_isocirq(struct urb *urb, struct pt_regs *regs) +#else +static void em28xx_audio_isocirq(struct urb *urb) +#endif +{ + struct em28xx *dev=urb->context; + int i; + unsigned int oldptr; + unsigned long flags; + int period_elapsed = 0; + int status; + unsigned char *cp; + unsigned int stride; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; +#else + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; +#endif + if(dev->adev->capture_pcm_substream){ + substream=dev->adev->capture_pcm_substream; + runtime=substream->runtime; + + stride = runtime->frame_bits >> 3; + for(i=0;inumber_of_packets;i++){ + int length=urb->iso_frame_desc[i].actual_length/stride; + cp=(unsigned char *) urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + if(!length) + continue; + + spin_lock_irqsave(&dev->adev->slock, flags); + oldptr = dev->adev->hwptr_done_capture; + dev->adev->hwptr_done_capture +=length; + if(dev->adev->hwptr_done_capture >= runtime->buffer_size) + dev->adev->hwptr_done_capture -= runtime->buffer_size; + + dev->adev->capture_transfer_done += length; + if(dev->adev->capture_transfer_done >= runtime->period_size){ + dev->adev->capture_transfer_done -= runtime->period_size; + period_elapsed=1; + } + spin_unlock_irqrestore(&dev->adev->slock, flags); + + if(oldptr + length >= runtime->buffer_size){ + unsigned int cnt = runtime->buffer_size-oldptr-1; + memcpy(runtime->dma_area+oldptr*stride, cp , cnt*stride); + memcpy(runtime->dma_area, cp + cnt, length*stride - cnt*stride); + } else { + memcpy(runtime->dma_area+oldptr*stride, cp, length*stride); + } + } + if(period_elapsed){ + snd_pcm_period_elapsed(substream); + } + } + urb->status = 0; + + if(dev->adev->shutdown) + return; + + if((status = usb_submit_urb(urb, GFP_ATOMIC))){ + em28xx_errdev("resubmit of audio urb failed (error=%i)\n", status); + } + return; +} + +static int em28xx_isoc_audio_deinit(struct em28xx *dev) +{ + int i; + for(i=0;iadev->urb[i]); + usb_free_urb(dev->adev->urb[i]); + dev->adev->urb[i]=NULL; + } + return 0; +} + +static int em28xx_init_audio_isoc(struct em28xx *dev) +{ + int i; + int errCode; + const int sb_size=EM28XX_NUM_AUDIO_PACKETS * EM28XX_AUDIO_MAX_PACKET_SIZE; + + + for(i=0;iadev->transfer_buffer[i]=kmalloc(sb_size,GFP_ATOMIC); + if(!dev->adev->transfer_buffer[i]){ + return -ENOMEM; + } + memset(dev->adev->transfer_buffer[i],0x80,sb_size); + urb = usb_alloc_urb(EM28XX_NUM_AUDIO_PACKETS,GFP_ATOMIC); + if(urb){ + urb->dev=dev->udev; + urb->context=dev; + urb->pipe=usb_rcvisocpipe(dev->udev,0x83); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = dev->adev->transfer_buffer[i]; + urb->interval=1; + urb->complete = em28xx_audio_isocirq; + urb->number_of_packets = EM28XX_NUM_AUDIO_PACKETS; + urb->transfer_buffer_length = sb_size; + for(j=k=0; jiso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length=EM28XX_AUDIO_MAX_PACKET_SIZE; + } + dev->adev->urb[i]=urb; + } else { + return -ENOMEM; + } + } + for(i=0;iadev->urb[i], GFP_ATOMIC); + if (errCode){ + em28xx_isoc_audio_deinit(dev); + return errCode; + } + } + return 0; +} + + +static int em28xx_cmd(struct em28xx *dev, int cmd,int arg) +{ + switch(cmd){ + case EM28XX_CAPTURE_STREAM_EN: + if(dev->adev->capture_stream == STREAM_OFF && arg==1){ + dev->adev->capture_stream=STREAM_ON; + em28xx_init_audio_isoc(dev); + } else if (dev->adev->capture_stream==STREAM_ON && arg==0){ + dev->adev->capture_stream=STREAM_OFF; + em28xx_isoc_audio_deinit(dev); + } else { + printk("An underrun occured very likely... ignoring it\n"); + } + return 0; + default: + return -EINVAL; + } +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) +static snd_pcm_uframes_t snd_em28xx_capture_pointer(snd_pcm_substream_t *substream) +#else +static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream *substream) +#endif +{ + struct em28xx *dev; + snd_pcm_uframes_t hwptr_done; + dev = snd_pcm_substream_chip(substream); + hwptr_done = dev->adev->hwptr_done_capture; + return hwptr_done; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) +static struct page *snd_pcm_get_vmalloc_page(snd_pcm_substream_t *subs, + unsigned long offset) +#else +static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, + unsigned long offset) +#endif +{ + void *pageptr = subs->runtime->dma_area + offset; + return vmalloc_to_page(pageptr); +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) +static snd_pcm_ops_t snd_em28xx_pcm_capture = { +#else +static struct snd_pcm_ops snd_em28xx_pcm_capture = { +#endif + .open = snd_em28xx_capture_open, + .close = snd_em28xx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_em28xx_hw_capture_params, + .hw_free = snd_em28xx_hw_capture_free, + .prepare = snd_em28xx_prepare, + .trigger = snd_em28xx_capture_trigger, + .pointer = snd_em28xx_capture_pointer, + .page = snd_pcm_get_vmalloc_page, +}; + + +static int em28xx_audio_init(struct em28xx *dev) +{ + struct em28xx_audio *adev; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) + snd_pcm_t *pcm; + snd_card_t *card; +#else + struct snd_pcm *pcm; + struct snd_card *card; +#endif + static int devnr; + int ret; + int err; + printk("em28xx-audio.c: probing for em28x1 non standard usbaudio\n"); + printk("em28xx-audio.c: Copyright (C) 2006 Markus Rechberger\n"); + adev=kzalloc(sizeof(*adev),GFP_KERNEL); + if(!adev){ + printk("em28xx-audio.c: out of memory\n"); + return -1; + } + card = snd_card_new(index[devnr], "Em28xx Audio", THIS_MODULE,0); + if(card==NULL){ + kfree(adev); + return -ENOMEM; + } + + spin_lock_init(&adev->slock); + ret=snd_pcm_new(card, "Em28xx Audio", 0, 0, 1, &pcm); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_em28xx_pcm_capture); + pcm->info_flags = 0; + pcm->private_data = dev; + strcpy(pcm->name,"Empia 28xx Capture"); + strcpy(card->driver, "Empia Em28xx Audio"); + strcpy(card->shortname, "Em28xx Audio"); + strcpy(card->longname,"Empia Em28xx Audio"); + + if((err = snd_card_register(card))<0){ + snd_card_free(card); + return -ENOMEM; + } + adev->sndcard=card; + adev->udev=dev->udev; + dev->adev=adev; + return 0; +} + +static int em28xx_audio_fini(struct em28xx *dev) +{ + if(dev==NULL) + return 0; + if(dev->adev){ + snd_card_free(dev->adev->sndcard); + kfree(dev->adev); + dev->adev=NULL; + } + return 0; +} + +static struct em28xx_ops audio_ops = { + .id = EM28XX_AUDIO, + .name = "Em28xx Audio Extension", + .init = em28xx_audio_init, + .fini = em28xx_audio_fini, +}; + +static int __init em28xx_alsa_register(void) +{ + request_module("em28xx"); + request_module("tuner"); + return em28xx_register_extension(&audio_ops); +} + +static void __exit em28xx_alsa_unregister(void) +{ + em28xx_unregister_extension(&audio_ops); +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Markus Rechberger "); +MODULE_DESCRIPTION("Em28xx Audio driver"); + +module_init(em28xx_alsa_register); +module_exit(em28xx_alsa_unregister); -- cgit v1.2.3 From d49826d9af46403c5029abfd34047b8d8b45a450 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 5 Jan 2008 10:56:24 -0200 Subject: Lindent em28xx-audio.c From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-audio.c | 393 +++++++++++++----------- 1 file changed, 208 insertions(+), 185 deletions(-) (limited to 'linux/drivers/media') diff --git a/linux/drivers/media/video/em28xx/em28xx-audio.c b/linux/drivers/media/video/em28xx/em28xx-audio.c index 03e752776..835633b83 100644 --- a/linux/drivers/media/video/em28xx/em28xx-audio.c +++ b/linux/drivers/media/video/em28xx/em28xx-audio.c @@ -44,13 +44,13 @@ #include "em28xx.h" static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; -static int em28xx_cmd(struct em28xx *dev, int cmd,int arg); - +static int em28xx_cmd(struct em28xx *dev, int cmd, int arg); #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -static int snd_pcm_alloc_vmalloc_buffer(snd_pcm_substream_t *subs, size_t size) +static int snd_pcm_alloc_vmalloc_buffer(snd_pcm_substream_t * subs, size_t size) #else -static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, size_t size) +static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, + size_t size) #endif { #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) @@ -58,13 +58,13 @@ static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, size_t s #else struct snd_pcm_runtime *runtime = subs->runtime; #endif - if(runtime->dma_area){ - if(runtime->dma_bytes > size) + if (runtime->dma_area) { + if (runtime->dma_bytes > size) return 0; vfree(runtime->dma_area); } runtime->dma_area = vmalloc(size); - if(!runtime ->dma_area) + if (!runtime->dma_area) return -ENOMEM; runtime->dma_bytes = size; return 0; @@ -75,22 +75,24 @@ static snd_pcm_hardware_t snd_em28xx_hw_capture = { #else static struct snd_pcm_hardware snd_em28xx_hw_capture = { #endif - .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP_VALID, + .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_CONTINUOUS | SNDRV_PCM_RATE_KNOT, .rate_min = 48000, .rate_max = 48000, .channels_min = 2, .channels_max = 2, - .buffer_bytes_max = 62720*8, /* just about the value in usbaudio.c */ - .period_bytes_min = 64, //12544/2, + .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */ + .period_bytes_min = 64, //12544/2, .period_bytes_max = 12544, .periods_min = 2, - .periods_max = 98, //12544, + .periods_max = 98, //12544, }; #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -static int snd_em28xx_capture_open(snd_pcm_substream_t *substream) +static int snd_em28xx_capture_open(snd_pcm_substream_t * substream) #else static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) #endif @@ -104,66 +106,68 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; #endif printk("opening radio device and trying to acquire exclusive lock\n"); - switch(dev->mode){ - case TUNER_STUB_DVBC_TV: - case TUNER_STUB_DVBT_TV: - case TUNER_STUB_ATSC_TV: - /* digital has no support for analog audio */ - if (ret != 0 ) { - printk("device is already in use by DVB-T\n"); - return -EINVAL; - } else { - struct v4l2_tuner tuner; - printk("switching device to FM mode\n"); - - mode = TUNER_STUB_RADIO; - memset(&tuner, 0x0, sizeof(struct v4l2_tuner)); - tuner.type = V4L2_TUNER_RADIO; - - /* enable GPIO for analog TV */ - dev->em28xx_gpio_control(dev, EM28XX_MODE, (void*)mode); - dev->mode = mode; - /* upload firmware */ - tuner_run_cmd(dev->tobj, TUNER_CMD_INIT, (void*)mode); - - /* required for devices which have kerneldriver dependencies */ -// em28xx_config(dev); -// em28xx_config_i2c(dev); - - /* this is moreover to switch the decoder to FM */ - em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, &tuner); - - dev->em28xx_write_regs(dev, 0x0f, "\x87", 1); - ret = dev->em28xx_acquire(dev, EM28XX_RADIO, 1); - em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, 0); - /* TODO switch to FM mode */ - - printk("em28xx-audio: %d mode\n", mode); - tuner_run_cmd(dev->tobj, TUNER_CMD_G_MODE, &mode); - printk("retrieved mode from tuner: %d\n",mode); - } - break; - - case TUNER_STUB_ANALOG_TV: - printk("em28xx-audio: device is currently in analog TV mode\n"); - /* unmute by default */ - dev->em28xx_write_regs(dev, 0x0f, "\x87", 1); - break; - case TUNER_STUB_RADIO: - /* check current mode and put a hard lock onto it */ - printk("em28xx-audio: device is currently in analogue FM mode\n"); - /* unmute by default here */ - dev->em28xx_write_regs(dev, 0x0f, "\x87", 1); - ret = dev->em28xx_acquire(dev, EM28XX_RADIO, 1); - if ( ret == 0 ) - printk("device is locked in fmradio mode now\n"); - break; - default: - printk("em28xx-audio: unhandled mode %d\n", dev->mode); + switch (dev->mode) { + case TUNER_STUB_DVBC_TV: + case TUNER_STUB_DVBT_TV: + case TUNER_STUB_ATSC_TV: + /* digital has no support for analog audio */ + if (ret != 0) { + printk("device is already in use by DVB-T\n"); + return -EINVAL; + } else { + struct v4l2_tuner tuner; + printk("switching device to FM mode\n"); + + mode = TUNER_STUB_RADIO; + memset(&tuner, 0x0, sizeof(struct v4l2_tuner)); + tuner.type = V4L2_TUNER_RADIO; + + /* enable GPIO for analog TV */ + dev->em28xx_gpio_control(dev, EM28XX_MODE, + (void *)mode); + dev->mode = mode; + /* upload firmware */ + tuner_run_cmd(dev->tobj, TUNER_CMD_INIT, (void *)mode); + + /* required for devices which have kerneldriver dependencies */ +// em28xx_config(dev); +// em28xx_config_i2c(dev); + + /* this is moreover to switch the decoder to FM */ + em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, &tuner); + + dev->em28xx_write_regs(dev, 0x0f, "\x87", 1); + ret = dev->em28xx_acquire(dev, EM28XX_RADIO, 1); + em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, 0); + /* TODO switch to FM mode */ + + printk("em28xx-audio: %d mode\n", mode); + tuner_run_cmd(dev->tobj, TUNER_CMD_G_MODE, &mode); + printk("retrieved mode from tuner: %d\n", mode); + } + break; + + case TUNER_STUB_ANALOG_TV: + printk("em28xx-audio: device is currently in analog TV mode\n"); + /* unmute by default */ + dev->em28xx_write_regs(dev, 0x0f, "\x87", 1); + break; + case TUNER_STUB_RADIO: + /* check current mode and put a hard lock onto it */ + printk + ("em28xx-audio: device is currently in analogue FM mode\n"); + /* unmute by default here */ + dev->em28xx_write_regs(dev, 0x0f, "\x87", 1); + ret = dev->em28xx_acquire(dev, EM28XX_RADIO, 1); + if (ret == 0) + printk("device is locked in fmradio mode now\n"); + break; + default: + printk("em28xx-audio: unhandled mode %d\n", dev->mode); } runtime->hw = snd_em28xx_hw_capture; - if(dev->alt == 0 && dev->adev->users == 0 ) { + if (dev->alt == 0 && dev->adev->users == 0) { int errCode; dev->alt = 7; errCode = usb_set_interface(dev->udev, 0, 7); @@ -177,7 +181,7 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) } #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -static int snd_em28xx_pcm_close(snd_pcm_substream_t *substream) +static int snd_em28xx_pcm_close(snd_pcm_substream_t * substream) #else static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream) #endif @@ -187,62 +191,66 @@ static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream) dev->adev->users--; /* decrease audio reference */ - switch(dev->mode) { - case TUNER_STUB_ANALOG_TV: - amode = EM28XX_VIDEO; - break; - case TUNER_STUB_RADIO: - amode = EM28XX_RADIO; - break; - default: - printk("invalid mode: %d\n",dev->mode); - break; + switch (dev->mode) { + case TUNER_STUB_ANALOG_TV: + amode = EM28XX_VIDEO; + break; + case TUNER_STUB_RADIO: + amode = EM28XX_RADIO; + break; + default: + printk("invalid mode: %d\n", dev->mode); + break; } - dev->em28xx_acquire(dev, amode, 0); + dev->em28xx_acquire(dev, amode, 0); - if(dev->adev->users == 0 && dev->adev->shutdown == 1) { - printk("audio users: %d\n",dev->adev->users); + if (dev->adev->users == 0 && dev->adev->shutdown == 1) { + printk("audio users: %d\n", dev->adev->users); printk("disabling audio stream!\n"); dev->adev->shutdown = 0; printk("released lock\n"); - em28xx_cmd(dev,EM28XX_CAPTURE_STREAM_EN,0); + em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 0); } return 0; } #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -static int snd_em28xx_hw_capture_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *hw_params) +static int snd_em28xx_hw_capture_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) #else -static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) +static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) #endif { unsigned int channels, rate, format; int ret; - ret = snd_pcm_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); + ret = + snd_pcm_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); format = params_format(hw_params); rate = params_rate(hw_params); channels = params_channels(hw_params); /* TODO: set up em28xx audio chip to deliver the correct audio format, current default is 48000hz multiplexed => 96000hz mono - which shouldn't matter since analogue TV only supports mono*/ + which shouldn't matter since analogue TV only supports mono */ return 0; } #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -static int snd_em28xx_hw_capture_free(snd_pcm_substream_t *substream) +static int snd_em28xx_hw_capture_free(snd_pcm_substream_t * substream) #else static int snd_em28xx_hw_capture_free(struct snd_pcm_substream *substream) #endif { struct em28xx *dev = snd_pcm_substream_chip(substream); - if(dev->adev->capture_stream==STREAM_ON){ - em28xx_cmd(dev,EM28XX_CAPTURE_STREAM_EN,0); + if (dev->adev->capture_stream == STREAM_ON) { + em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 0); } return 0; } #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -static int snd_em28xx_prepare(snd_pcm_substream_t *substream) +static int snd_em28xx_prepare(snd_pcm_substream_t * substream) #else static int snd_em28xx_prepare(struct snd_pcm_substream *substream) #endif @@ -251,21 +259,22 @@ static int snd_em28xx_prepare(struct snd_pcm_substream *substream) } #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -static int snd_em28xx_capture_trigger(snd_pcm_substream_t *substream, int cmd) +static int snd_em28xx_capture_trigger(snd_pcm_substream_t * substream, int cmd) #else -static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream, int cmd) +static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream, + int cmd) #endif { struct em28xx *dev = snd_pcm_substream_chip(substream); - switch(cmd){ - case SNDRV_PCM_TRIGGER_START: - em28xx_cmd(dev,EM28XX_CAPTURE_STREAM_EN,1); - return 0; - case SNDRV_PCM_TRIGGER_STOP: - dev->adev->shutdown=1; - return 0; - default: - return -EINVAL; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 1); + return 0; + case SNDRV_PCM_TRIGGER_STOP: + dev->adev->shutdown = 1; + return 0; + default: + return -EINVAL; } } @@ -275,7 +284,7 @@ static void em28xx_audio_isocirq(struct urb *urb, struct pt_regs *regs) static void em28xx_audio_isocirq(struct urb *urb) #endif { - struct em28xx *dev=urb->context; + struct em28xx *dev = urb->context; int i; unsigned int oldptr; unsigned long flags; @@ -290,50 +299,61 @@ static void em28xx_audio_isocirq(struct urb *urb) struct snd_pcm_substream *substream; struct snd_pcm_runtime *runtime; #endif - if(dev->adev->capture_pcm_substream){ - substream=dev->adev->capture_pcm_substream; - runtime=substream->runtime; + if (dev->adev->capture_pcm_substream) { + substream = dev->adev->capture_pcm_substream; + runtime = substream->runtime; stride = runtime->frame_bits >> 3; - for(i=0;inumber_of_packets;i++){ - int length=urb->iso_frame_desc[i].actual_length/stride; - cp=(unsigned char *) urb->transfer_buffer + urb->iso_frame_desc[i].offset; + for (i = 0; i < urb->number_of_packets; i++) { + int length = + urb->iso_frame_desc[i].actual_length / stride; + cp = (unsigned char *)urb->transfer_buffer + + urb->iso_frame_desc[i].offset; - if(!length) + if (!length) continue; spin_lock_irqsave(&dev->adev->slock, flags); oldptr = dev->adev->hwptr_done_capture; - dev->adev->hwptr_done_capture +=length; - if(dev->adev->hwptr_done_capture >= runtime->buffer_size) - dev->adev->hwptr_done_capture -= runtime->buffer_size; + dev->adev->hwptr_done_capture += length; + if (dev->adev->hwptr_done_capture >= + runtime->buffer_size) + dev->adev->hwptr_done_capture -= + runtime->buffer_size; dev->adev->capture_transfer_done += length; - if(dev->adev->capture_transfer_done >= runtime->period_size){ - dev->adev->capture_transfer_done -= runtime->period_size; - period_elapsed=1; + if (dev->adev->capture_transfer_done >= + runtime->period_size) { + dev->adev->capture_transfer_done -= + runtime->period_size; + period_elapsed = 1; } spin_unlock_irqrestore(&dev->adev->slock, flags); - if(oldptr + length >= runtime->buffer_size){ - unsigned int cnt = runtime->buffer_size-oldptr-1; - memcpy(runtime->dma_area+oldptr*stride, cp , cnt*stride); - memcpy(runtime->dma_area, cp + cnt, length*stride - cnt*stride); + if (oldptr + length >= runtime->buffer_size) { + unsigned int cnt = + runtime->buffer_size - oldptr - 1; + memcpy(runtime->dma_area + oldptr * stride, cp, + cnt * stride); + memcpy(runtime->dma_area, cp + cnt, + length * stride - cnt * stride); } else { - memcpy(runtime->dma_area+oldptr*stride, cp, length*stride); + memcpy(runtime->dma_area + oldptr * stride, cp, + length * stride); } } - if(period_elapsed){ + if (period_elapsed) { snd_pcm_period_elapsed(substream); } } urb->status = 0; - - if(dev->adev->shutdown) + + if (dev->adev->shutdown) return; - if((status = usb_submit_urb(urb, GFP_ATOMIC))){ - em28xx_errdev("resubmit of audio urb failed (error=%i)\n", status); + if ((status = usb_submit_urb(urb, GFP_ATOMIC))) { + em28xx_errdev("resubmit of audio urb failed (error=%i)\n", + status); } return; } @@ -341,10 +361,10 @@ static void em28xx_audio_isocirq(struct urb *urb) static int em28xx_isoc_audio_deinit(struct em28xx *dev) { int i; - for(i=0;iadev->urb[i]); usb_free_urb(dev->adev->urb[i]); - dev->adev->urb[i]=NULL; + dev->adev->urb[i] = NULL; } return 0; } @@ -353,40 +373,42 @@ static int em28xx_init_audio_isoc(struct em28xx *dev) { int i; int errCode; - const int sb_size=EM28XX_NUM_AUDIO_PACKETS * EM28XX_AUDIO_MAX_PACKET_SIZE; + const int sb_size = + EM28XX_NUM_AUDIO_PACKETS * EM28XX_AUDIO_MAX_PACKET_SIZE; - - for(i=0;iadev->transfer_buffer[i]=kmalloc(sb_size,GFP_ATOMIC); - if(!dev->adev->transfer_buffer[i]){ + int j, k; + dev->adev->transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC); + if (!dev->adev->transfer_buffer[i]) { return -ENOMEM; } - memset(dev->adev->transfer_buffer[i],0x80,sb_size); - urb = usb_alloc_urb(EM28XX_NUM_AUDIO_PACKETS,GFP_ATOMIC); - if(urb){ - urb->dev=dev->udev; - urb->context=dev; - urb->pipe=usb_rcvisocpipe(dev->udev,0x83); + memset(dev->adev->transfer_buffer[i], 0x80, sb_size); + urb = usb_alloc_urb(EM28XX_NUM_AUDIO_PACKETS, GFP_ATOMIC); + if (urb) { + urb->dev = dev->udev; + urb->context = dev; + urb->pipe = usb_rcvisocpipe(dev->udev, 0x83); urb->transfer_flags = URB_ISO_ASAP; urb->transfer_buffer = dev->adev->transfer_buffer[i]; - urb->interval=1; + urb->interval = 1; urb->complete = em28xx_audio_isocirq; urb->number_of_packets = EM28XX_NUM_AUDIO_PACKETS; urb->transfer_buffer_length = sb_size; - for(j=k=0; jiso_frame_desc[j].offset = k; - urb->iso_frame_desc[j].length=EM28XX_AUDIO_MAX_PACKET_SIZE; + urb->iso_frame_desc[j].length = + EM28XX_AUDIO_MAX_PACKET_SIZE; } - dev->adev->urb[i]=urb; + dev->adev->urb[i] = urb; } else { return -ENOMEM; } } - for(i=0;iadev->urb[i], GFP_ATOMIC); - if (errCode){ + if (errCode) { em28xx_isoc_audio_deinit(dev); return errCode; } @@ -394,30 +416,32 @@ static int em28xx_init_audio_isoc(struct em28xx *dev) return 0; } - -static int em28xx_cmd(struct em28xx *dev, int cmd,int arg) +static int em28xx_cmd(struct em28xx *dev, int cmd, int arg) { - switch(cmd){ - case EM28XX_CAPTURE_STREAM_EN: - if(dev->adev->capture_stream == STREAM_OFF && arg==1){ - dev->adev->capture_stream=STREAM_ON; - em28xx_init_audio_isoc(dev); - } else if (dev->adev->capture_stream==STREAM_ON && arg==0){ - dev->adev->capture_stream=STREAM_OFF; - em28xx_isoc_audio_deinit(dev); - } else { - printk("An underrun occured very likely... ignoring it\n"); - } - return 0; - default: - return -EINVAL; + switch (cmd) { + case EM28XX_CAPTURE_STREAM_EN: + if (dev->adev->capture_stream == STREAM_OFF && arg == 1) { + dev->adev->capture_stream = STREAM_ON; + em28xx_init_audio_isoc(dev); + } else if (dev->adev->capture_stream == STREAM_ON && arg == 0) { + dev->adev->capture_stream = STREAM_OFF; + em28xx_isoc_audio_deinit(dev); + } else { + printk + ("An underrun occured very likely... ignoring it\n"); + } + return 0; + default: + return -EINVAL; } } #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -static snd_pcm_uframes_t snd_em28xx_capture_pointer(snd_pcm_substream_t *substream) +static snd_pcm_uframes_t snd_em28xx_capture_pointer(snd_pcm_substream_t * + substream) #else -static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream *substream) +static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream + *substream) #endif { struct em28xx *dev; @@ -428,7 +452,7 @@ static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream *su } #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -static struct page *snd_pcm_get_vmalloc_page(snd_pcm_substream_t *subs, +static struct page *snd_pcm_get_vmalloc_page(snd_pcm_substream_t * subs, unsigned long offset) #else static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, @@ -455,7 +479,6 @@ static struct snd_pcm_ops snd_em28xx_pcm_capture = { .page = snd_pcm_get_vmalloc_page, }; - static int em28xx_audio_init(struct em28xx *dev) { struct em28xx_audio *adev; @@ -471,54 +494,54 @@ static int em28xx_audio_init(struct em28xx *dev) int err; printk("em28xx-audio.c: probing for em28x1 non standard usbaudio\n"); printk("em28xx-audio.c: Copyright (C) 2006 Markus Rechberger\n"); - adev=kzalloc(sizeof(*adev),GFP_KERNEL); - if(!adev){ + adev = kzalloc(sizeof(*adev), GFP_KERNEL); + if (!adev) { printk("em28xx-audio.c: out of memory\n"); return -1; } - card = snd_card_new(index[devnr], "Em28xx Audio", THIS_MODULE,0); - if(card==NULL){ + card = snd_card_new(index[devnr], "Em28xx Audio", THIS_MODULE, 0); + if (card == NULL) { kfree(adev); return -ENOMEM; } spin_lock_init(&adev->slock); - ret=snd_pcm_new(card, "Em28xx Audio", 0, 0, 1, &pcm); + ret = snd_pcm_new(card, "Em28xx Audio", 0, 0, 1, &pcm); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_em28xx_pcm_capture); pcm->info_flags = 0; pcm->private_data = dev; - strcpy(pcm->name,"Empia 28xx Capture"); + strcpy(pcm->name, "Empia 28xx Capture"); strcpy(card->driver, "Empia Em28xx Audio"); strcpy(card->shortname, "Em28xx Audio"); - strcpy(card->longname,"Empia Em28xx Audio"); + strcpy(card->longname, "Empia Em28xx Audio"); - if((err = snd_card_register(card))<0){ + if ((err = snd_card_register(card)) < 0) { snd_card_free(card); return -ENOMEM; } - adev->sndcard=card; - adev->udev=dev->udev; - dev->adev=adev; + adev->sndcard = card; + adev->udev = dev->udev; + dev->adev = adev; return 0; } static int em28xx_audio_fini(struct em28xx *dev) { - if(dev==NULL) + if (dev == NULL) return 0; - if(dev->adev){ + if (dev->adev) { snd_card_free(dev->adev->sndcard); kfree(dev->adev); - dev->adev=NULL; + dev->adev = NULL; } return 0; } static struct em28xx_ops audio_ops = { - .id = EM28XX_AUDIO, - .name = "Em28xx Audio Extension", - .init = em28xx_audio_init, - .fini = em28xx_audio_fini, + .id = EM28XX_AUDIO, + .name = "Em28xx Audio Extension", + .init = em28xx_audio_init, + .fini = em28xx_audio_fini, }; static int __init em28xx_alsa_register(void) -- cgit v1.2.3 From 05c059b96e21efe3087c55a1cc9d7ee598eaa92d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 5 Jan 2008 10:57:31 -0200 Subject: Integrates em28xx-audio.c into em28xx kernel module From: Mauro Carvalho Chehab Also fixes the remaining CodingStyle issues that Lindent didn't fix. Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/Kconfig | 15 + linux/drivers/media/video/em28xx/Makefile | 3 + linux/drivers/media/video/em28xx/em28xx-audio.c | 596 ++++++++++++------------ linux/drivers/media/video/em28xx/em28xx-video.c | 63 ++- linux/drivers/media/video/em28xx/em28xx.h | 45 ++ 5 files changed, 418 insertions(+), 304 deletions(-) (limited to 'linux/drivers/media') diff --git a/linux/drivers/media/video/em28xx/Kconfig b/linux/drivers/media/video/em28xx/Kconfig index 813077b6e..abbd38c1e 100644 --- a/linux/drivers/media/video/em28xx/Kconfig +++ b/linux/drivers/media/video/em28xx/Kconfig @@ -11,3 +11,18 @@ config VIDEO_EM28XX To compile this driver as a module, choose M here: the module will be called em28xx + +config VIDEO_EM28XX_ALSA + depends on VIDEO_EM28XX + tristate "Empia EM28xx ALSA audio module" + ---help--- + This is an ALSA driver for some Empia 28xx based TV cards. + + This is not required for em2800/em2820/em2821 boards. However, + newer em28xx devices uses Vendor Class for audio, instead of + implementing the USB Audio Class. For those chips, this module + will enable digital audio. + + To compile this driver as a module, choose M here: the + module will be called em28xx-alsa + diff --git a/linux/drivers/media/video/em28xx/Makefile b/linux/drivers/media/video/em28xx/Makefile index 7e7a93d75..092455099 100644 --- a/linux/drivers/media/video/em28xx/Makefile +++ b/linux/drivers/media/video/em28xx/Makefile @@ -1,7 +1,10 @@ em28xx-objs := em28xx-video.o em28xx-i2c.o em28xx-cards.o em28xx-core.o \ em28xx-input.o +em28xx-alsa-objs := em28xx-audio.o + obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o +obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o EXTRA_CFLAGS += -Idrivers/media/video EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core diff --git a/linux/drivers/media/video/em28xx/em28xx-audio.c b/linux/drivers/media/video/em28xx/em28xx-audio.c index 835633b83..8329ba287 100644 --- a/linux/drivers/media/video/em28xx/em28xx-audio.c +++ b/linux/drivers/media/video/em28xx/em28xx-audio.c @@ -3,6 +3,10 @@ * * Copyright (C) 2006 Markus Rechberger * + * Copyright (C) 2007 Mauro Carvalho Chehab + * - Port to work with the in-kernel driver + * - Several cleanups + * * This driver is based on my previous au600 usb pstn audio driver * and inherits all the copyrights * @@ -30,7 +34,7 @@ #include #include #include -#include +#include #include #include #include @@ -38,186 +42,316 @@ #include #include #include -//#include -//#include #include #include "em28xx.h" +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "activates debug info"); + +#define dprintk(fmt, arg...) do { \ + if (debug) \ + printk(KERN_INFO "em28xx-audio %s: " fmt, \ + __FUNCTION__, ##arg); \ + } while (0) + static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; -static int em28xx_cmd(struct em28xx *dev, int cmd, int arg); -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -static int snd_pcm_alloc_vmalloc_buffer(snd_pcm_substream_t * subs, size_t size) +static int em28xx_isoc_audio_deinit(struct em28xx *dev) +{ + int i; + + dprintk("Stopping isoc\n"); + for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { + usb_kill_urb(dev->adev->urb[i]); + usb_free_urb(dev->adev->urb[i]); + dev->adev->urb[i] = NULL; + } + + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) +static void em28xx_audio_isocirq(struct urb *urb, struct pt_regs *regs) +#else +static void em28xx_audio_isocirq(struct urb *urb) +#endif +{ + struct em28xx *dev = urb->context; + int i; + unsigned int oldptr; + unsigned long flags; + int period_elapsed = 0; + int status; + unsigned char *cp; + unsigned int stride; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; +#else + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; +#endif + if (dev->adev->capture_pcm_substream) { + substream = dev->adev->capture_pcm_substream; + runtime = substream->runtime; + stride = runtime->frame_bits >> 3; + + for (i = 0; i < urb->number_of_packets; i++) { + int length = + urb->iso_frame_desc[i].actual_length / stride; + cp = (unsigned char *)urb->transfer_buffer + + urb->iso_frame_desc[i].offset; + + if (!length) + continue; + + spin_lock_irqsave(&dev->adev->slock, flags); + + oldptr = dev->adev->hwptr_done_capture; + dev->adev->hwptr_done_capture += length; + if (dev->adev->hwptr_done_capture >= + runtime->buffer_size) + dev->adev->hwptr_done_capture -= + runtime->buffer_size; + + dev->adev->capture_transfer_done += length; + if (dev->adev->capture_transfer_done >= + runtime->period_size) { + dev->adev->capture_transfer_done -= + runtime->period_size; + period_elapsed = 1; + } + + spin_unlock_irqrestore(&dev->adev->slock, flags); + + if (oldptr + length >= runtime->buffer_size) { + unsigned int cnt = + runtime->buffer_size - oldptr - 1; + memcpy(runtime->dma_area + oldptr * stride, cp, + cnt * stride); + memcpy(runtime->dma_area, cp + cnt, + length * stride - cnt * stride); + } else { + memcpy(runtime->dma_area + oldptr * stride, cp, + length * stride); + } + } + if (period_elapsed) + snd_pcm_period_elapsed(substream); + } + urb->status = 0; + + if (dev->adev->shutdown) + return; + + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status < 0) { + em28xx_errdev("resubmit of audio urb failed (error=%i)\n", + status); + } + return; +} + +static int em28xx_init_audio_isoc(struct em28xx *dev) +{ + int i, errCode; + const int sb_size = EM28XX_NUM_AUDIO_PACKETS * + EM28XX_AUDIO_MAX_PACKET_SIZE; + + dprintk("Starting isoc transfers\n"); + + for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { + struct urb *urb; + int j, k; + + dev->adev->transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC); + if (!dev->adev->transfer_buffer[i]) + return -ENOMEM; + + memset(dev->adev->transfer_buffer[i], 0x80, sb_size); + urb = usb_alloc_urb(EM28XX_NUM_AUDIO_PACKETS, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + + urb->dev = dev->udev; + urb->context = dev; + urb->pipe = usb_rcvisocpipe(dev->udev, 0x83); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = dev->adev->transfer_buffer[i]; + urb->interval = 1; + urb->complete = em28xx_audio_isocirq; + urb->number_of_packets = EM28XX_NUM_AUDIO_PACKETS; + urb->transfer_buffer_length = sb_size; + + for (j = k = 0; j < EM28XX_NUM_AUDIO_PACKETS; + j++, k += EM28XX_AUDIO_MAX_PACKET_SIZE) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = + EM28XX_AUDIO_MAX_PACKET_SIZE; + } + dev->adev->urb[i] = urb; + } + + for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { + errCode = usb_submit_urb(dev->adev->urb[i], GFP_ATOMIC); + if (errCode) { + em28xx_isoc_audio_deinit(dev); + + return errCode; + } + } + + return 0; +} + +static int em28xx_cmd(struct em28xx *dev, int cmd, int arg) +{ + dprintk("%s transfer\n", (dev->adev->capture_stream == STREAM_ON)? + "stop" : "start"); + + switch (cmd) { + case EM28XX_CAPTURE_STREAM_EN: + if (dev->adev->capture_stream == STREAM_OFF && arg == 1) { + dev->adev->capture_stream = STREAM_ON; + em28xx_init_audio_isoc(dev); + } else if (dev->adev->capture_stream == STREAM_ON && arg == 0) { + dev->adev->capture_stream = STREAM_OFF; + em28xx_isoc_audio_deinit(dev); + } else { + printk(KERN_ERR "An underrun very likely occurred. " + "Ignoring it.\n"); + } + return 0; + default: + return -EINVAL; + } +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_pcm_alloc_vmalloc_buffer(snd_pcm_substream_t *subs, + size_t size) #else static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, size_t size) #endif { -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) - snd_pcm_runtime_t *runtime = subs->runtime; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_pcm_runtime_t *runtime = subs->runtime; #else struct snd_pcm_runtime *runtime = subs->runtime; #endif + + dprintk("Alocating vbuffer\n"); 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; + runtime->dma_bytes = size; + return 0; } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) static snd_pcm_hardware_t snd_em28xx_hw_capture = { #else static struct snd_pcm_hardware snd_em28xx_hw_capture = { #endif - .info = - SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP_VALID, + .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_CONTINUOUS | SNDRV_PCM_RATE_KNOT, + .rate_min = 48000, .rate_max = 48000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */ - .period_bytes_min = 64, //12544/2, + .period_bytes_min = 64, /* 12544/2, */ .period_bytes_max = 12544, .periods_min = 2, - .periods_max = 98, //12544, + .periods_max = 98, /* 12544, */ }; -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -static int snd_em28xx_capture_open(snd_pcm_substream_t * substream) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_em28xx_capture_open(snd_pcm_substream_t *substream) #else static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) #endif { - int ret = 0; - int mode; struct em28xx *dev = snd_pcm_substream_chip(substream); -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) snd_pcm_runtime_t *runtime = substream->runtime; #else struct snd_pcm_runtime *runtime = substream->runtime; #endif - printk("opening radio device and trying to acquire exclusive lock\n"); - switch (dev->mode) { - case TUNER_STUB_DVBC_TV: - case TUNER_STUB_DVBT_TV: - case TUNER_STUB_ATSC_TV: - /* digital has no support for analog audio */ - if (ret != 0) { - printk("device is already in use by DVB-T\n"); - return -EINVAL; - } else { - struct v4l2_tuner tuner; - printk("switching device to FM mode\n"); - - mode = TUNER_STUB_RADIO; - memset(&tuner, 0x0, sizeof(struct v4l2_tuner)); - tuner.type = V4L2_TUNER_RADIO; - - /* enable GPIO for analog TV */ - dev->em28xx_gpio_control(dev, EM28XX_MODE, - (void *)mode); - dev->mode = mode; - /* upload firmware */ - tuner_run_cmd(dev->tobj, TUNER_CMD_INIT, (void *)mode); - - /* required for devices which have kerneldriver dependencies */ -// em28xx_config(dev); -// em28xx_config_i2c(dev); - - /* this is moreover to switch the decoder to FM */ - em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, &tuner); - - dev->em28xx_write_regs(dev, 0x0f, "\x87", 1); - ret = dev->em28xx_acquire(dev, EM28XX_RADIO, 1); - em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, 0); - /* TODO switch to FM mode */ - - printk("em28xx-audio: %d mode\n", mode); - tuner_run_cmd(dev->tobj, TUNER_CMD_G_MODE, &mode); - printk("retrieved mode from tuner: %d\n", mode); - } - break; - - case TUNER_STUB_ANALOG_TV: - printk("em28xx-audio: device is currently in analog TV mode\n"); - /* unmute by default */ - dev->em28xx_write_regs(dev, 0x0f, "\x87", 1); - break; - case TUNER_STUB_RADIO: - /* check current mode and put a hard lock onto it */ - printk - ("em28xx-audio: device is currently in analogue FM mode\n"); - /* unmute by default here */ - dev->em28xx_write_regs(dev, 0x0f, "\x87", 1); - ret = dev->em28xx_acquire(dev, EM28XX_RADIO, 1); - if (ret == 0) - printk("device is locked in fmradio mode now\n"); - break; - default: - printk("em28xx-audio: unhandled mode %d\n", dev->mode); - } + int ret = 0; + + dprintk("opening device and trying to acquire exclusive lock\n"); + + /* Sets volume, mute, etc */ + dev->mute = 0; + ret = em28xx_audio_analog_set(dev); + if (ret < 0) + goto err; runtime->hw = snd_em28xx_hw_capture; if (dev->alt == 0 && dev->adev->users == 0) { int errCode; dev->alt = 7; errCode = usb_set_interface(dev->udev, 0, 7); - printk("changing alternate number to 7\n"); + dprintk("changing alternate number to 7\n"); } + dev->adev->users++; + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); dev->adev->capture_pcm_substream = substream; runtime->private_data = dev; + return 0; +err: + printk(KERN_ERR "Error while configuring em28xx mixer\n"); + return ret; } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -static int snd_em28xx_pcm_close(snd_pcm_substream_t * substream) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_em28xx_pcm_close(snd_pcm_substream_t *substream) #else static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream) #endif { struct em28xx *dev = snd_pcm_substream_chip(substream); - int amode = 0; dev->adev->users--; - /* decrease audio reference */ - switch (dev->mode) { - case TUNER_STUB_ANALOG_TV: - amode = EM28XX_VIDEO; - break; - case TUNER_STUB_RADIO: - amode = EM28XX_RADIO; - break; - default: - printk("invalid mode: %d\n", dev->mode); - break; - } + dprintk("closing device\n"); - dev->em28xx_acquire(dev, amode, 0); + dev->mute = 1; + em28xx_audio_analog_set(dev); if (dev->adev->users == 0 && dev->adev->shutdown == 1) { - printk("audio users: %d\n", dev->adev->users); - printk("disabling audio stream!\n"); + dprintk("audio users: %d\n", dev->adev->users); + dprintk("disabling audio stream!\n"); dev->adev->shutdown = 0; - printk("released lock\n"); + dprintk("released lock\n"); em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 0); } return 0; } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -static int snd_em28xx_hw_capture_params(snd_pcm_substream_t * substream, - snd_pcm_hw_params_t * hw_params) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_em28xx_hw_capture_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *hw_params) #else static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) @@ -225,32 +359,39 @@ static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream, { unsigned int channels, rate, format; int ret; - ret = - snd_pcm_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); + + dprintk("Setting capture parameters\n"); + + ret = snd_pcm_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); format = params_format(hw_params); rate = params_rate(hw_params); channels = params_channels(hw_params); - /* TODO: set up em28xx audio chip to deliver the correct audio format, current default is 48000hz multiplexed => 96000hz mono + + /* TODO: set up em28xx audio chip to deliver the correct audio format, + current default is 48000hz multiplexed => 96000hz mono which shouldn't matter since analogue TV only supports mono */ return 0; } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -static int snd_em28xx_hw_capture_free(snd_pcm_substream_t * substream) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_em28xx_hw_capture_free(snd_pcm_substream_t *substream) #else static int snd_em28xx_hw_capture_free(struct snd_pcm_substream *substream) #endif { struct em28xx *dev = snd_pcm_substream_chip(substream); - if (dev->adev->capture_stream == STREAM_ON) { + + dprintk("Stop capture, if needed\n"); + + if (dev->adev->capture_stream == STREAM_ON) em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 0); - } + return 0; } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -static int snd_em28xx_prepare(snd_pcm_substream_t * substream) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_em28xx_prepare(snd_pcm_substream_t *substream) #else static int snd_em28xx_prepare(struct snd_pcm_substream *substream) #endif @@ -258,14 +399,17 @@ static int snd_em28xx_prepare(struct snd_pcm_substream *substream) return 0; } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -static int snd_em28xx_capture_trigger(snd_pcm_substream_t * substream, int cmd) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_em28xx_capture_trigger(snd_pcm_substream_t *substream, int cmd) #else static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream, int cmd) #endif { struct em28xx *dev = snd_pcm_substream_chip(substream); + + dprintk("Should %s capture\n", (cmd == SNDRV_PCM_TRIGGER_START)? + "start": "stop"); switch (cmd) { case SNDRV_PCM_TRIGGER_START: em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 1); @@ -278,181 +422,25 @@ static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream, } } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -static void em28xx_audio_isocirq(struct urb *urb, struct pt_regs *regs) -#else -static void em28xx_audio_isocirq(struct urb *urb) -#endif -{ - struct em28xx *dev = urb->context; - int i; - unsigned int oldptr; - unsigned long flags; - int period_elapsed = 0; - int status; - unsigned char *cp; - unsigned int stride; -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) - snd_pcm_substream_t *substream; - snd_pcm_runtime_t *runtime; -#else - struct snd_pcm_substream *substream; - struct snd_pcm_runtime *runtime; -#endif - if (dev->adev->capture_pcm_substream) { - substream = dev->adev->capture_pcm_substream; - runtime = substream->runtime; - - stride = runtime->frame_bits >> 3; - for (i = 0; i < urb->number_of_packets; i++) { - int length = - urb->iso_frame_desc[i].actual_length / stride; - cp = (unsigned char *)urb->transfer_buffer + - urb->iso_frame_desc[i].offset; - - if (!length) - continue; - - spin_lock_irqsave(&dev->adev->slock, flags); - oldptr = dev->adev->hwptr_done_capture; - dev->adev->hwptr_done_capture += length; - if (dev->adev->hwptr_done_capture >= - runtime->buffer_size) - dev->adev->hwptr_done_capture -= - runtime->buffer_size; - - dev->adev->capture_transfer_done += length; - if (dev->adev->capture_transfer_done >= - runtime->period_size) { - dev->adev->capture_transfer_done -= - runtime->period_size; - period_elapsed = 1; - } - spin_unlock_irqrestore(&dev->adev->slock, flags); - - if (oldptr + length >= runtime->buffer_size) { - unsigned int cnt = - runtime->buffer_size - oldptr - 1; - memcpy(runtime->dma_area + oldptr * stride, cp, - cnt * stride); - memcpy(runtime->dma_area, cp + cnt, - length * stride - cnt * stride); - } else { - memcpy(runtime->dma_area + oldptr * stride, cp, - length * stride); - } - } - if (period_elapsed) { - snd_pcm_period_elapsed(substream); - } - } - urb->status = 0; - - if (dev->adev->shutdown) - return; - - if ((status = usb_submit_urb(urb, GFP_ATOMIC))) { - em28xx_errdev("resubmit of audio urb failed (error=%i)\n", - status); - } - return; -} - -static int em28xx_isoc_audio_deinit(struct em28xx *dev) -{ - int i; - for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { - usb_kill_urb(dev->adev->urb[i]); - usb_free_urb(dev->adev->urb[i]); - dev->adev->urb[i] = NULL; - } - return 0; -} - -static int em28xx_init_audio_isoc(struct em28xx *dev) -{ - int i; - int errCode; - const int sb_size = - EM28XX_NUM_AUDIO_PACKETS * EM28XX_AUDIO_MAX_PACKET_SIZE; - - for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { - struct urb *urb; - int j, k; - dev->adev->transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC); - if (!dev->adev->transfer_buffer[i]) { - return -ENOMEM; - } - memset(dev->adev->transfer_buffer[i], 0x80, sb_size); - urb = usb_alloc_urb(EM28XX_NUM_AUDIO_PACKETS, GFP_ATOMIC); - if (urb) { - urb->dev = dev->udev; - urb->context = dev; - urb->pipe = usb_rcvisocpipe(dev->udev, 0x83); - urb->transfer_flags = URB_ISO_ASAP; - urb->transfer_buffer = dev->adev->transfer_buffer[i]; - urb->interval = 1; - urb->complete = em28xx_audio_isocirq; - urb->number_of_packets = EM28XX_NUM_AUDIO_PACKETS; - urb->transfer_buffer_length = sb_size; - for (j = k = 0; j < EM28XX_NUM_AUDIO_PACKETS; - j++, k += EM28XX_AUDIO_MAX_PACKET_SIZE) { - urb->iso_frame_desc[j].offset = k; - urb->iso_frame_desc[j].length = - EM28XX_AUDIO_MAX_PACKET_SIZE; - } - dev->adev->urb[i] = urb; - } else { - return -ENOMEM; - } - } - for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { - errCode = usb_submit_urb(dev->adev->urb[i], GFP_ATOMIC); - if (errCode) { - em28xx_isoc_audio_deinit(dev); - return errCode; - } - } - return 0; -} - -static int em28xx_cmd(struct em28xx *dev, int cmd, int arg) -{ - switch (cmd) { - case EM28XX_CAPTURE_STREAM_EN: - if (dev->adev->capture_stream == STREAM_OFF && arg == 1) { - dev->adev->capture_stream = STREAM_ON; - em28xx_init_audio_isoc(dev); - } else if (dev->adev->capture_stream == STREAM_ON && arg == 0) { - dev->adev->capture_stream = STREAM_OFF; - em28xx_isoc_audio_deinit(dev); - } else { - printk - ("An underrun occured very likely... ignoring it\n"); - } - return 0; - default: - return -EINVAL; - } -} - -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -static snd_pcm_uframes_t snd_em28xx_capture_pointer(snd_pcm_substream_t * - substream) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static snd_pcm_uframes_t snd_em28xx_capture_pointer(snd_pcm_substream_t + *substream) #else static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream *substream) #endif { struct em28xx *dev; + snd_pcm_uframes_t hwptr_done; dev = snd_pcm_substream_chip(substream); hwptr_done = dev->adev->hwptr_done_capture; + return hwptr_done; } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -static struct page *snd_pcm_get_vmalloc_page(snd_pcm_substream_t * subs, +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static struct page *snd_pcm_get_vmalloc_page(snd_pcm_substream_t *subs, unsigned long offset) #else static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, @@ -460,43 +448,47 @@ static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, #endif { void *pageptr = subs->runtime->dma_area + offset; + return vmalloc_to_page(pageptr); } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) static snd_pcm_ops_t snd_em28xx_pcm_capture = { #else static struct snd_pcm_ops snd_em28xx_pcm_capture = { #endif - .open = snd_em28xx_capture_open, - .close = snd_em28xx_pcm_close, - .ioctl = snd_pcm_lib_ioctl, + .open = snd_em28xx_capture_open, + .close = snd_em28xx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_em28xx_hw_capture_params, - .hw_free = snd_em28xx_hw_capture_free, - .prepare = snd_em28xx_prepare, - .trigger = snd_em28xx_capture_trigger, - .pointer = snd_em28xx_capture_pointer, - .page = snd_pcm_get_vmalloc_page, + .hw_free = snd_em28xx_hw_capture_free, + .prepare = snd_em28xx_prepare, + .trigger = snd_em28xx_capture_trigger, + .pointer = snd_em28xx_capture_pointer, + .page = snd_pcm_get_vmalloc_page, }; static int em28xx_audio_init(struct em28xx *dev) { struct em28xx_audio *adev; -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) - snd_pcm_t *pcm; - snd_card_t *card; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_pcm_t *pcm; + snd_card_t *card; #else - struct snd_pcm *pcm; - struct snd_card *card; + struct snd_pcm *pcm; + struct snd_card *card; #endif - static int devnr; - int ret; - int err; - printk("em28xx-audio.c: probing for em28x1 non standard usbaudio\n"); - printk("em28xx-audio.c: Copyright (C) 2006 Markus Rechberger\n"); + static int devnr; + int ret, err; + + printk(KERN_INFO "em28xx-audio.c: probing for em28x1 " + "non standard usbaudio\n"); + printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus " + "Rechberger\n"); + adev = kzalloc(sizeof(*adev), GFP_KERNEL); if (!adev) { - printk("em28xx-audio.c: out of memory\n"); + printk(KERN_ERR "em28xx-audio.c: out of memory\n"); return -1; } card = snd_card_new(index[devnr], "Em28xx Audio", THIS_MODULE, 0); @@ -515,13 +507,15 @@ static int em28xx_audio_init(struct em28xx *dev) strcpy(card->shortname, "Em28xx Audio"); strcpy(card->longname, "Empia Em28xx Audio"); - if ((err = snd_card_register(card)) < 0) { + err = snd_card_register(card); + if (err < 0) { snd_card_free(card); return -ENOMEM; } adev->sndcard = card; adev->udev = dev->udev; dev->adev = adev; + return 0; } @@ -529,16 +523,18 @@ static int em28xx_audio_fini(struct em28xx *dev) { if (dev == NULL) return 0; + if (dev->adev) { snd_card_free(dev->adev->sndcard); kfree(dev->adev); dev->adev = NULL; } + return 0; } static struct em28xx_ops audio_ops = { - .id = EM28XX_AUDIO, + .id = EM28XX_AUDIO, .name = "Em28xx Audio Extension", .init = em28xx_audio_init, .fini = em28xx_audio_fini, @@ -547,7 +543,6 @@ static struct em28xx_ops audio_ops = { static int __init em28xx_alsa_register(void) { request_module("em28xx"); - request_module("tuner"); return em28xx_register_extension(&audio_ops); } @@ -558,6 +553,7 @@ static void __exit em28xx_alsa_unregister(void) MODULE_LICENSE("GPL"); MODULE_AUTHOR("Markus Rechberger "); +MODULE_AUTHOR("Mauro Carvalho Chehab "); MODULE_DESCRIPTION("Em28xx Audio driver"); module_init(em28xx_alsa_register); diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index d9eb1bae0..a550eeb89 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -1671,9 +1671,48 @@ static const struct video_device em28xx_video_template = { .current_norm = V4L2_STD_PAL, }; - /******************************** usb interface *****************************************/ + +static LIST_HEAD(em28xx_extension_devlist); +static DEFINE_MUTEX(em28xx_extension_devlist_lock); + +int em28xx_register_extension(struct em28xx_ops *ops) +{ + struct em28xx *h, *dev = NULL; + + list_for_each_entry(h, &em28xx_devlist, devlist) + dev = h; + + mutex_lock(&em28xx_extension_devlist_lock); + list_add_tail(&ops->next, &em28xx_extension_devlist); + if (dev) + ops->init(dev); + + printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name); + mutex_unlock(&em28xx_extension_devlist_lock); + + return 0; +} +EXPORT_SYMBOL(em28xx_register_extension); + +void em28xx_unregister_extension(struct em28xx_ops *ops) +{ + struct em28xx *h, *dev = NULL; + + list_for_each_entry(h, &em28xx_devlist, devlist) + dev = h; + + if (dev) + ops->fini(dev); + + mutex_lock(&em28xx_extension_devlist_lock); + printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name); + list_del(&ops->next); + mutex_unlock(&em28xx_extension_devlist_lock); +} +EXPORT_SYMBOL(em28xx_unregister_extension); + /* * em28xx_init_dev() * allocates and inits the device structs, registers i2c bus and v4l device @@ -1681,6 +1720,7 @@ static const struct video_device em28xx_video_template = { static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, int minor) { + struct em28xx_ops *ops = NULL; struct em28xx *dev = *devhandle; int retval = -ENOMEM; int errCode; @@ -1817,6 +1857,15 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN, dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN); + mutex_lock(&em28xx_extension_devlist_lock); + if (!list_empty(&em28xx_extension_devlist)) { + list_for_each_entry(ops, &em28xx_extension_devlist, next) { + if (ops->id) + ops->init(dev); + } + } + mutex_unlock(&em28xx_extension_devlist_lock); + return 0; } @@ -1937,6 +1986,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, static void em28xx_usb_disconnect(struct usb_interface *interface) { struct em28xx *dev; + struct em28xx_ops *ops = NULL; dev = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); @@ -1966,15 +2016,20 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) dev->state |= DEV_DISCONNECTED; em28xx_release_resources(dev); } - - mutex_unlock(&dev->lock); + mutex_lock(&em28xx_extension_devlist_lock); + if (!list_empty(&em28xx_extension_devlist)) { + list_for_each_entry(ops, &em28xx_extension_devlist, next) { + ops->fini(dev); + } + } + mutex_unlock(&em28xx_extension_devlist_lock); + if (!dev->users) { kfree(dev->alt_max_pkt_size); kfree(dev); } - } static struct usb_driver em28xx_usb_driver = { diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 4d318b3d5..ffe109077 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -215,6 +215,36 @@ enum em28xx_dev_state { DEV_MISCONFIGURED = 0x04, }; +#define EM28XX_AUDIO_BUFS 5 +#define EM28XX_NUM_AUDIO_PACKETS 64 +#define EM28XX_AUDIO_MAX_PACKET_SIZE 196 /* static value */ +#define EM28XX_CAPTURE_STREAM_EN 1 +#define EM28XX_AUDIO 0x10 + +struct em28xx_audio { + char name[50]; + char *transfer_buffer[EM28XX_AUDIO_BUFS]; + struct urb *urb[EM28XX_AUDIO_BUFS]; + struct usb_device *udev; + unsigned int capture_transfer_done; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_pcm_substream_t *capture_pcm_substream; +#else + struct snd_pcm_substream *capture_pcm_substream; +#endif + + unsigned int hwptr_done_capture; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_card_t *sndcard; +#else + struct snd_card *sndcard; +#endif + + int users, shutdown; + enum em28xx_stream_state capture_stream; + spinlock_t slock; +}; + /* main device struct */ struct em28xx { /* generic device properties */ @@ -269,6 +299,8 @@ struct em28xx { unsigned long hash; /* eeprom hash - for boards with generic ID */ unsigned long i2c_hash; /* i2c devicelist hash - for boards with generic ID */ + struct em28xx_audio *adev; + /* states */ enum em28xx_dev_state state; enum em28xx_stream_state stream; @@ -309,6 +341,15 @@ struct em28xx { struct em28xx_fh { struct em28xx *dev; unsigned int stream_on:1; /* Locks streams */ + int radio; +}; + +struct em28xx_ops { + struct list_head next; + char *name; + int id; + int (*init)(struct em28xx *); + int (*fini)(struct em28xx *); }; /* Provided by em28xx-i2c.c */ @@ -348,6 +389,10 @@ int em28xx_init_isoc(struct em28xx *dev); void em28xx_uninit_isoc(struct em28xx *dev); int em28xx_set_alternate(struct em28xx *dev); +/* Provided by em28xx-video.c */ +int em28xx_register_extension(struct em28xx_ops *dev); +void em28xx_unregister_extension(struct em28xx_ops *dev); + /* Provided by em28xx-cards.c */ extern int em2800_variant_detect(struct usb_device* udev,int model); extern void em28xx_pre_card_setup(struct em28xx *dev); -- cgit v1.2.3 From 55d8d49cc70bab1115e2ce34939ca5b5db5b3dfb Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 5 Jan 2008 10:59:03 -0200 Subject: Add code for autoloading em28xx-alsa, if needed From: Mauro Carvalho Chehab Older em28xx devices does implement standard Audio Class. However, on newer devices, this were replaced by a Vendor Class. This patch autodetects that an em28xx lacks Audio Class and auto-loads em28xx-alsa, for the devices that implements only a Vendor Class. For devices with Audio Class, snd-usb-audio module will provide an ALSA interface. Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-audio.c | 1 - linux/drivers/media/video/em28xx/em28xx-video.c | 53 +++++++++++++++++++++++++ linux/drivers/media/video/em28xx/em28xx.h | 3 ++ 3 files changed, 56 insertions(+), 1 deletion(-) (limited to 'linux/drivers/media') diff --git a/linux/drivers/media/video/em28xx/em28xx-audio.c b/linux/drivers/media/video/em28xx/em28xx-audio.c index 8329ba287..901eeeb5c 100644 --- a/linux/drivers/media/video/em28xx/em28xx-audio.c +++ b/linux/drivers/media/video/em28xx/em28xx-audio.c @@ -542,7 +542,6 @@ static struct em28xx_ops audio_ops = { static int __init em28xx_alsa_register(void) { - request_module("em28xx"); return em28xx_register_extension(&audio_ops); } diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index a550eeb89..1a269b643 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -1740,6 +1740,10 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, dev->em28xx_read_reg_req = em28xx_read_reg_req; dev->is_em2800 = em28xx_boards[dev->model].is_em2800; + errCode = em28xx_read_reg(dev, CHIPID_REG); + if (errCode >= 0) + em28xx_info("em28xx chip ID = %d\n", errCode); + em28xx_pre_card_setup(dev); errCode = em28xx_config(dev); @@ -1869,6 +1873,40 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, return 0; } +#if defined(CONFIG_MODULES) && defined(MODULE) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) +static void request_module_async(void *ptr) +{ + struct em28xx *dev = (struct em28xx *)ptr; +#else +static void request_module_async(struct work_struct *work) +{ + struct em28xx *dev = container_of(work, + struct em28xx, request_module_wk); +#endif + + if (!dev->has_audio_class) + request_module("em28xx-alsa"); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) +#define request_modules(dev) +#else +static void request_modules(struct em28xx *dev) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + INIT_WORK(&dev->request_module_wk, request_module_async, + (void *)dev); +#else + INIT_WORK(&dev->request_module_wk, request_module_async); +#endif + schedule_work(&dev->request_module_wk); +} +#endif +#else +#define request_modules(dev) +#endif /* CONFIG_MODULES */ + /* * em28xx_usb_probe() * checks for supported devices @@ -1939,6 +1977,18 @@ static int em28xx_usb_probe(struct usb_interface *interface, dev->devno = nr; dev->model = id->driver_info; + /* Checks if audio is provided by some interface */ + for (i = 0; i < udev->config->desc.bNumInterfaces; i++) { + uif = udev->config->interface[i]; + if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) { + dev->has_audio_class = 1; + break; + } + } + + printk(KERN_INFO DRIVER_NAME " %s usb audio class\n", + dev->has_audio_class ? "Has" : "Doesn't have"); + /* compute alternate max packet sizes */ uif = udev->actconfig->interface[0]; @@ -1975,6 +2025,9 @@ static int em28xx_usb_probe(struct usb_interface *interface, /* save our data pointer in this interface device */ usb_set_intfdata(interface, dev); + + request_modules(dev); + return 0; } diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index ffe109077..64a63b2c8 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -257,6 +257,7 @@ struct em28xx { unsigned int has_msp34xx:1; unsigned int has_tda9887:1; unsigned int stream_on:1; /* Locks streams */ + unsigned int has_audio_class:1; int video_inputs; /* number of video inputs */ struct list_head devlist; @@ -306,6 +307,8 @@ struct em28xx { enum em28xx_stream_state stream; enum em28xx_io_method io; + struct work_struct request_module_wk; + /* locks */ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) struct mutex lock; -- cgit v1.2.3 From 32c7f08554e7096d41c6dceb77833b25f68fde36 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 5 Jan 2008 17:37:04 -0200 Subject: Fix radio set frequency logic From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/tuner-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers/media') diff --git a/linux/drivers/media/video/tuner-core.c b/linux/drivers/media/video/tuner-core.c index f6608f1b2..d93a764aa 100644 --- a/linux/drivers/media/video/tuner-core.c +++ b/linux/drivers/media/video/tuner-core.c @@ -276,7 +276,7 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq) tuner_warn ("tuner type not set\n"); return; } - if (analog_ops->set_params) { + if (NULL == analog_ops->set_params) { tuner_warn ("tuner has no way to set radio frequency\n"); return; } -- cgit v1.2.3 From c8abe9596bde0bfdd4537b9635bebeb8b5acf88c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 5 Jan 2008 17:47:16 -0200 Subject: There isn't a MTS radio firmware From: Mauro Carvalho Chehab Try to load non-MTS firmware instead. Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/tuner-xc2028.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers/media') diff --git a/linux/drivers/media/video/tuner-xc2028.c b/linux/drivers/media/video/tuner-xc2028.c index cf54d24bb..dbaf0ce8d 100644 --- a/linux/drivers/media/video/tuner-xc2028.c +++ b/linux/drivers/media/video/tuner-xc2028.c @@ -678,7 +678,7 @@ static int check_firmware(struct dvb_frontend *fe, unsigned int type, return rc; } - if (priv->ctrl.mts) + if (priv->ctrl.mts && ! (type & FM) ) type |= MTS; retry: -- cgit v1.2.3 From 24a9538c24f678ac7dd1e53348bd49fdf4b71414 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 5 Jan 2008 18:01:41 -0200 Subject: Properly implement 12MHz I2S support From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 20 +++++++++----------- linux/drivers/media/video/em28xx/em28xx-core.c | 11 +++++++++-- linux/drivers/media/video/em28xx/em28xx-video.c | 7 +++---- linux/drivers/media/video/em28xx/em28xx.h | 3 +++ 4 files changed, 24 insertions(+), 17 deletions(-) (limited to 'linux/drivers/media') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 34f5fab0b..25344980a 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -175,13 +175,14 @@ struct em28xx_board em28xx_boards[] = { } }, }, [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950] = { - .name = "Hauppauge WinTV HVR 950", - .vchannels = 3, - .tda9887_conf = TDA9887_PRESENT, - .tuner_type = TUNER_XC2028, - .has_tuner = 1, - .mts_firmware = 1, - .decoder = EM28XX_TVP5150, + .name = "Hauppauge WinTV HVR 950", + .vchannels = 3, + .tda9887_conf = TDA9887_PRESENT, + .tuner_type = TUNER_XC2028, + .has_tuner = 1, + .mts_firmware = 1, + .has_12mhz_i2s = 1, + .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, @@ -642,6 +643,7 @@ static void em28xx_set_model(struct em28xx *dev) dev->decoder = em28xx_boards[dev->model].decoder; dev->video_inputs = em28xx_boards[dev->model].vchannels; dev->analog_gpio = em28xx_boards[dev->model].analog_gpio; + dev->has_12mhz_i2s = em28xx_boards[dev->model].has_12mhz_i2s; if (!em28xx_boards[dev->model].has_tuner) dev->tuner_type = UNSET; @@ -677,10 +679,6 @@ void em28xx_card_setup(struct em28xx *dev) if (tv.has_ir) request_module("ir-kbd-i2c"); #endif - /* enable audio 12 mhz i2s */ - em28xx_write_regs(dev, XCLK_REG, "\xa7", 1); - msleep(10); - break; } case EM2820_BOARD_KWORLD_PVRTV2800RF: diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index 98153d70d..cb87e7e41 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -383,6 +383,7 @@ int em28xx_audio_analog_set(struct em28xx *dev) { int ret; char s[2] = { 0x00, 0x00 }; + u8 xclk = 0x07; s[0] |= 0x1f - dev->volume; s[1] |= 0x1f - dev->volume; @@ -393,10 +394,16 @@ int em28xx_audio_analog_set(struct em28xx *dev) if (ret < 0) return ret; - ret = em28xx_write_reg_bits(dev, XCLK_REG, - dev->mute ? 0x00 : 0x80, 0x80); + if (dev->has_12mhz_i2s) + xclk |= 0x20; + + if (!dev->mute) + xclk |= 0x80; + + ret = em28xx_write_reg_bits(dev, XCLK_REG, xclk, 0xa7); if (ret < 0) return ret; + msleep(10); /* Selects the proper audio input */ ret = em28xx_set_audio_source(dev); diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 1a269b643..b7c8a1fa0 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -145,10 +145,6 @@ static int em28xx_config(struct em28xx *dev) dev->mute = 1; /* maybe not the right place... */ dev->volume = 0x1f; - /* Init XCLK_REG, audio muted */ - dev->em28xx_write_regs(dev, XCLK_REG, "\x87", 1); - - em28xx_audio_analog_set(dev); em28xx_outfmt_set_yuv422(dev); em28xx_colorlevels_set_default(dev); em28xx_compression_disable(dev); @@ -1760,6 +1756,9 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, /* Do board specific init and eeprom reading */ em28xx_card_setup(dev); + /* Configure audio */ + em28xx_audio_analog_set(dev); + /* configure the device */ em28xx_config_i2c(dev); diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 64a63b2c8..ff216a813 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -187,6 +187,8 @@ struct em28xx_board { unsigned int has_tuner:1; unsigned int has_msp34xx:1; unsigned int mts_firmware:1; + unsigned int has_12mhz_i2s:1; + unsigned int analog_gpio; enum em28xx_decoder decoder; @@ -258,6 +260,7 @@ struct em28xx { unsigned int has_tda9887:1; unsigned int stream_on:1; /* Locks streams */ unsigned int has_audio_class:1; + unsigned int has_12mhz_i2s:1; int video_inputs; /* number of video inputs */ struct list_head devlist; -- cgit v1.2.3 From 76a1e5c0858e7b49c63708f044276f5142534b4b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 5 Jan 2008 18:22:01 -0200 Subject: Add Radio support for em28xx From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-video.c | 315 +++++++++++++++++++----- linux/drivers/media/video/em28xx/em28xx.h | 2 + 2 files changed, 250 insertions(+), 67 deletions(-) (limited to 'linux/drivers/media') diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index b7c8a1fa0..77d547f81 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -66,26 +66,32 @@ static LIST_HEAD(em28xx_devlist); static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; -static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; +static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; +static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) -MODULE_PARM(card,"1-" __stringify(EM28XX_MAXBOARDS) "i"); -MODULE_PARM(video_nr,"1-" __stringify(EM28XX_MAXBOARDS) "i"); -MODULE_PARM(vbi_nr,"1-" __stringify(EM28XX_MAXBOARDS) "i"); +MODULE_PARM(card, "1-" __stringify(EM28XX_MAXBOARDS) "i"); +MODULE_PARM(video_nr, "1-" __stringify(EM28XX_MAXBOARDS) "i"); +MODULE_PARM(vbi_nr, "1-" __stringify(EM28XX_MAXBOARDS) "i"); +MODULE_PARM(radio_nr, "1-" __stringify(EM28XX_MAXBOARDS) "i"); #else #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) static int dummy; module_param_array(card, int, dummy, 0444); module_param_array(video_nr, int, dummy, 0444); module_param_array(vbi_nr, int, dummy, 0444); +module_param_array(radio_nr, int, dummy, 0444); #else module_param_array(card, int, NULL, 0444); module_param_array(video_nr, int, NULL, 0444); module_param_array(vbi_nr, int, NULL, 0444); +module_param_array(radio_nr, int, NULL, 0444); #endif #endif -MODULE_PARM_DESC(card,"card type"); -MODULE_PARM_DESC(video_nr,"video device numbers"); -MODULE_PARM_DESC(vbi_nr,"vbi device numbers"); +MODULE_PARM_DESC(card, "card type"); +MODULE_PARM_DESC(video_nr, "video device numbers"); +MODULE_PARM_DESC(vbi_nr, "vbi device numbers"); +MODULE_PARM_DESC(radio_nr, "radio device numbers"); static unsigned int video_debug = 0; module_param(video_debug,int,0644); @@ -811,7 +817,7 @@ static int vidioc_g_frequency(struct file *file, void *priv, struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - f->type = V4L2_TUNER_ANALOG_TV; + f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; f->frequency = dev->ctl_freq; return 0; @@ -831,7 +837,9 @@ static int vidioc_s_frequency(struct file *file, void *priv, if (0 != f->tuner) return -EINVAL; - if (V4L2_TUNER_ANALOG_TV != f->type) + if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV)) + return -EINVAL; + if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO)) return -EINVAL; mutex_lock(&dev->lock); @@ -1205,6 +1213,102 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) return 0; } +/* ----------------------------------------------------------- */ +/* RADIO ESPECIFIC IOCTLS */ +/* ----------------------------------------------------------- */ + +static int radio_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct em28xx *dev = ((struct em28xx_fh *)priv)->dev; + + strlcpy(cap->driver, "em28xx", sizeof(cap->driver)); + strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card)); + strlcpy(cap->bus_info, dev->udev->dev.bus_id, sizeof(cap->bus_info)); + + cap->version = EM28XX_VERSION_CODE; + cap->capabilities = V4L2_CAP_TUNER; + return 0; +} + +static int radio_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct em28xx *dev = ((struct em28xx_fh *)priv)->dev; + + if (unlikely(t->index > 0)) + return -EINVAL; + + strcpy(t->name, "Radio"); + t->type = V4L2_TUNER_RADIO; + + em28xx_i2c_call_clients(dev, VIDIOC_G_TUNER, t); + return 0; +} + +static int radio_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; + strcpy(i->name, "Radio"); + i->type = V4L2_INPUT_TYPE_TUNER; + + return 0; +} + +static int radio_g_audio(struct file *file, void *priv, struct v4l2_audio *a) +{ + if (unlikely(a->index)) + return -EINVAL; + + strcpy(a->name, "Radio"); + return 0; +} + +static int radio_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct em28xx *dev = ((struct em28xx_fh *)priv)->dev; + + if (0 != t->index) + return -EINVAL; + + em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, t); + + return 0; +} + +static int radio_s_audio(struct file *file, void *fh, + struct v4l2_audio *a) +{ + return 0; +} + +static int radio_s_input(struct file *file, void *fh, unsigned int i) +{ + return 0; +} + +static int radio_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + + if (qc->id < V4L2_CID_BASE || + qc->id >= V4L2_CID_LASTP1) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) { + if (qc->id && qc->id == em28xx_qctrl[i].id) { + memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc)); + return 0; + } + } + + return -EINVAL; +} + /* * em28xx_v4l2_open() * inits the device and starts isoc transfer @@ -1212,7 +1316,7 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) static int em28xx_v4l2_open(struct inode *inode, struct file *filp) { int minor = iminor(inode); - int errCode = 0; + int errCode = 0, radio = 0; struct em28xx *h,*dev = NULL; struct em28xx_fh *fh; @@ -1225,6 +1329,11 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) dev = h; dev->type = V4L2_BUF_TYPE_VBI_CAPTURE; } + if (h->radio_dev && + h->radio_dev->minor == minor) { + radio = 1; + dev = h; + } } if (NULL == dev) return -ENODEV; @@ -1240,6 +1349,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) } mutex_lock(&dev->lock); fh->dev = dev; + fh->radio = radio; filp->private_data = fh; if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) { @@ -1268,6 +1378,13 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) em28xx_empty_framequeues(dev); } + if (fh->radio) { + em28xx_videodbg("video_open: setting radio device\n"); +#if 0 + em28xx_start_radio(dev); +#endif + em28xx_i2c_call_clients(dev, AUDC_SET_RADIO, NULL); + } dev->users++; @@ -1290,16 +1407,30 @@ static void em28xx_release_resources(struct em28xx *dev) dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN, dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN); list_del(&dev->devlist); - video_unregister_device(dev->vdev); - video_unregister_device(dev->vbi_dev); + if (dev->radio_dev) { + if (-1 != dev->radio_dev->minor) + video_unregister_device(dev->radio_dev); + else + video_device_release(dev->radio_dev); + dev->radio_dev = NULL; + } + if (dev->vbi_dev) { + if (-1 != dev->vbi_dev->minor) + video_unregister_device(dev->vbi_dev); + else + video_device_release(dev->vbi_dev); + dev->vbi_dev = NULL; + } + if (dev->vdev) { + if (-1 != dev->vdev->minor) + video_unregister_device(dev->vdev); + else + video_device_release(dev->vdev); + dev->vdev = NULL; + } em28xx_i2c_unregister(dev); usb_put_dev(dev->udev); -#if 0 /* Fixme: disallocating these generates kernel hang */ - kfree (dev->vdev); - kfree (dev->vbi_dev); -#endif - /* Mark device as unused */ em28xx_devused&=~(1<devno); } @@ -1622,6 +1753,17 @@ static const struct file_operations em28xx_v4l_fops = { #endif }; +static const struct file_operations radio_fops = { + .owner = THIS_MODULE, + .open = em28xx_v4l2_open, + .release = em28xx_v4l2_close, + .ioctl = video_ioctl2, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) + .compat_ioctl = v4l_compat_ioctl32, +#endif + .llseek = no_llseek, +}; + static const struct video_device em28xx_video_template = { .fops = &em28xx_v4l_fops, .release = video_device_release, @@ -1667,6 +1809,25 @@ static const struct video_device em28xx_video_template = { .current_norm = V4L2_STD_PAL, }; +static struct video_device em28xx_radio_template = { + .name = "em28xx-radio", + .type = VID_TYPE_TUNER, + .fops = &radio_fops, + .minor = -1, + .vidioc_querycap = radio_querycap, + .vidioc_g_tuner = radio_g_tuner, + .vidioc_enum_input = radio_enum_input, + .vidioc_g_audio = radio_g_audio, + .vidioc_s_tuner = radio_s_tuner, + .vidioc_s_audio = radio_s_audio, + .vidioc_s_input = radio_s_input, + .vidioc_queryctrl = radio_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +}; + /******************************** usb interface *****************************************/ @@ -1709,6 +1870,31 @@ void em28xx_unregister_extension(struct em28xx_ops *ops) } EXPORT_SYMBOL(em28xx_unregister_extension); +struct video_device *em28xx_vdev_init(struct em28xx *dev, + const struct video_device *template, + const int type, + const char *type_name) +{ + struct video_device *vfd; + + vfd = video_device_alloc(); + if (NULL == vfd) + return NULL; + *vfd = *template; + vfd->minor = -1; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0) + vfd->dev = &dev->udev->dev; + vfd->release = video_device_release; +#endif + vfd->type = type; + + snprintf(vfd->name, sizeof(vfd->name), "%s %s", + dev->name, type_name); + + return vfd; +} + + /* * em28xx_init_dev() * allocates and inits the device structs, registers i2c bus and v4l device @@ -1782,40 +1968,55 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, errCode = em28xx_config(dev); + list_add_tail(&dev->devlist, &em28xx_devlist); + /* allocate and fill video video_device struct */ - dev->vdev = video_device_alloc(); + dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template, + VID_TYPE_CAPTURE, "video"); if (NULL == dev->vdev) { em28xx_errdev("cannot allocate video_device.\n"); - em28xx_devused&=~(1<devno); - kfree(dev); - return -ENOMEM; + goto fail_unreg; } - memcpy(dev->vdev, &em28xx_video_template, - sizeof(em28xx_video_template)); - dev->vdev->type = VID_TYPE_CAPTURE; if (dev->has_tuner) dev->vdev->type |= VID_TYPE_TUNER; - dev->vdev->dev = &dev->udev->dev; - snprintf(dev->vdev->name, sizeof(dev->vbi_dev->name), - "%s#%d %s", "em28xx", dev->devno, "video"); + + /* register v4l2 video video_device */ + retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER, + video_nr[dev->devno]); + if (retval) { + em28xx_errdev("unable to register video device (error=%i).\n", + retval); + goto fail_unreg; + } /* Allocate and fill vbi video_device struct */ - dev->vbi_dev = video_device_alloc(); - if (NULL == dev->vbi_dev) { - em28xx_errdev("cannot allocate video_device.\n"); - kfree(dev->vdev); - em28xx_devused&=~(1<devno); - kfree(dev); - return -ENOMEM; + dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template, + VFL_TYPE_VBI, "vbi"); + /* register v4l2 vbi video_device */ + if (video_register_device(dev->vbi_dev, VFL_TYPE_VBI, + vbi_nr[dev->devno]) < 0) { + em28xx_errdev("unable to register vbi device\n"); + retval = -ENODEV; + goto fail_unreg; + } + + if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) { + dev->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template, + VFL_TYPE_RADIO, "radio"); + if (NULL == dev->radio_dev) { + em28xx_errdev("cannot allocate video_device.\n"); + goto fail_unreg; + } + retval = video_register_device(dev->radio_dev, VFL_TYPE_RADIO, + radio_nr[dev->devno]); + if (retval < 0) { + em28xx_errdev("can't register radio device\n"); + goto fail_unreg; + } + em28xx_info("Registered radio device as /dev/radio%d\n", + dev->radio_dev->minor & 0x1f); } - memcpy(dev->vbi_dev, &em28xx_video_template, - sizeof(em28xx_video_template)); - dev->vbi_dev->type = VFL_TYPE_VBI; - dev->vbi_dev->dev = &dev->udev->dev; - snprintf(dev->vbi_dev->name, sizeof(dev->vbi_dev->name), - "%s#%d %s", "em28xx", dev->devno, "vbi"); - list_add_tail(&dev->devlist,&em28xx_devlist); #if 0 video_set_drvdata(dev->vbi_dev, dev); #endif @@ -1830,32 +2031,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, video_mux(dev, 0); - /* register v4l2 video video_device */ - if ((retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER, - video_nr[dev->devno]))) { - em28xx_errdev("unable to register video device (error=%i).\n", - retval); - mutex_unlock(&dev->lock); - list_del(&dev->devlist); - video_device_release(dev->vdev); - em28xx_devused&=~(1<devno); - kfree(dev); - return -ENODEV; - } - - /* register v4l2 vbi video_device */ - if (video_register_device(dev->vbi_dev, VFL_TYPE_VBI, - vbi_nr[dev->devno]) < 0) { - printk("unable to register vbi device\n"); - mutex_unlock(&dev->lock); - list_del(&dev->devlist); - video_device_release(dev->vbi_dev); - video_device_release(dev->vdev); - em28xx_devused&=~(1<devno); - kfree(dev); - return -ENODEV; - } - em28xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n", dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN, dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN); @@ -1870,6 +2045,12 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, mutex_unlock(&em28xx_extension_devlist_lock); return 0; + +fail_unreg: + em28xx_release_resources(dev); + mutex_unlock(&dev->lock); + kfree(dev); + return retval; } #if defined(CONFIG_MODULES) && defined(MODULE) diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index ff216a813..a483a3668 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -194,6 +194,7 @@ struct em28xx_board { enum em28xx_decoder decoder; struct em28xx_input input[MAX_EM28XX_INPUT]; + struct em28xx_input radio; }; struct em28xx_eeprom { @@ -322,6 +323,7 @@ struct em28xx { struct list_head inqueue, outqueue; wait_queue_head_t open, wait_frame, wait_stream; struct video_device *vbi_dev; + struct video_device *radio_dev; unsigned char eedata[256]; -- cgit v1.2.3