summaryrefslogtreecommitdiff
path: root/linux/drivers/media/video/em28xx
diff options
context:
space:
mode:
Diffstat (limited to 'linux/drivers/media/video/em28xx')
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-audio.c12
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-cards.c56
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-core.c26
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-i2c.c11
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-input.c260
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-reg.h20
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-video.c75
-rw-r--r--linux/drivers/media/video/em28xx/em28xx.h13
8 files changed, 427 insertions, 46 deletions
diff --git a/linux/drivers/media/video/em28xx/em28xx-audio.c b/linux/drivers/media/video/em28xx/em28xx-audio.c
index bec42349e..b86b0870d 100644
--- a/linux/drivers/media/video/em28xx/em28xx-audio.c
+++ b/linux/drivers/media/video/em28xx/em28xx-audio.c
@@ -496,11 +496,12 @@ static int em28xx_audio_init(struct em28xx *dev)
struct snd_card *card;
#endif
static int devnr;
- int ret, err;
+ int err;
- if (dev->has_audio_class) {
+ if (dev->has_alsa_audio != 1) {
/* This device does not support the extension (in this case
- the device is expecting the snd-usb-audio module) */
+ the device is expecting the snd-usb-audio module or
+ doesn't have analog audio support at all) */
return 0;
}
@@ -552,9 +553,10 @@ static int em28xx_audio_fini(struct em28xx *dev)
if (dev == NULL)
return 0;
- if (dev->has_audio_class) {
+ if (dev->has_alsa_audio != 1) {
/* This device does not support the extension (in this case
- the device is expecting the snd-usb-audio module */
+ the device is expecting the snd-usb-audio module or
+ doesn't have analog audio support at all) */
return 0;
}
diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c
index 4f078da31..8d92cb05b 100644
--- a/linux/drivers/media/video/em28xx/em28xx-cards.c
+++ b/linux/drivers/media/video/em28xx/em28xx-cards.c
@@ -1088,6 +1088,20 @@ struct em28xx_board em28xx_boards[] = {
.amux = EM28XX_AMUX_LINE_IN,
} },
},
+ [EM2874_BOARD_PINNACLE_PCTV_80E] = {
+ .name = "Pinnacle PCTV HD Mini",
+ .vchannels = 0,
+ .tuner_type = TUNER_ABSENT,
+ .has_dvb = 1,
+ .decoder = EM28XX_NODECODER,
+#ifdef DJH_DEBUG
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+#endif
+ },
};
const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
@@ -1185,6 +1199,8 @@ struct usb_device_id em28xx_id_table [] = {
.driver_info = EM2882_BOARD_PINNACLE_HYBRID_PRO },
{ USB_DEVICE(0x2304, 0x0227),
.driver_info = EM2880_BOARD_PINNACLE_PCTV_HD_PRO },
+ { USB_DEVICE(0x2304, 0x023f),
+ .driver_info = EM2874_BOARD_PINNACLE_PCTV_80E },
{ USB_DEVICE(0x0413, 0x6023),
.driver_info = EM2800_BOARD_LEADTEK_WINFAST_USBII },
{ USB_DEVICE(0x093b, 0xa005),
@@ -1260,6 +1276,17 @@ static struct em28xx_reg_seq em2882_terratec_hybrid_xs_digital[] = {
{ -1, -1, -1, -1},
};
+/* Pinnacle PCTV HD Mini (80e) GPIOs
+ 0-5: not used
+ 6: demod reset, active low
+ 7: LED on, active high */
+static struct em28xx_reg_seq em2874_pinnacle_80e_digital[] = {
+ {EM28XX_R06_I2C_CLK, 0x45, 0xff, 10}, /*400 KHz*/
+ {EM2874_R80_GPIO, 0x80, 0xff, 100},/*Demod reset*/
+ {EM2874_R80_GPIO, 0xc0, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+
/*
* EEPROM hash table for devices with generic USB IDs
*/
@@ -1317,17 +1344,25 @@ void em28xx_pre_card_setup(struct em28xx *dev)
{
int rc;
- rc = em28xx_read_reg(dev, EM2880_R04_GPO);
- if (rc >= 0)
- dev->reg_gpo = rc;
+ /* Set the default GPO/GPIO for legacy devices */
+ dev->reg_gpo_num = EM2880_R04_GPO;
+ dev->reg_gpio_num = EM28XX_R08_GPIO;
dev->wait_after_write = 5;
+
+ /* Based on the Chip ID, set the device configuration */
rc = em28xx_read_reg(dev, EM28XX_R0A_CHIPID);
if (rc > 0) {
+ dev->chip_id = rc;
switch (rc) {
case CHIP_ID_EM2860:
em28xx_info("chip ID is em2860\n");
break;
+ case CHIP_ID_EM2874:
+ em28xx_info("chip ID is em2874\n");
+ dev->reg_gpio_num = EM2874_R80_GPIO;
+ dev->wait_after_write = 0;
+ break;
case CHIP_ID_EM2883:
em28xx_info("chip ID is em2882/em2883\n");
dev->wait_after_write = 0;
@@ -1336,6 +1371,12 @@ void em28xx_pre_card_setup(struct em28xx *dev)
em28xx_info("em28xx chip ID = %d\n", rc);
}
}
+
+ /* Prepopulate cached GPO register content */
+ rc = em28xx_read_reg(dev, dev->reg_gpo_num);
+ if (rc >= 0)
+ dev->reg_gpo = rc;
+
em28xx_set_model(dev);
/* request some modules */
@@ -1509,6 +1550,13 @@ void em28xx_pre_card_setup(struct em28xx *dev)
/* enables audio for that device */
em28xx_write_regs_req(dev, 0x00, 0x08, "\xfd", 1);
break;
+
+ case EM2874_BOARD_PINNACLE_PCTV_80E:
+ /* Set 400 KHz clock */
+ em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x45", 1);
+
+ dev->digital_gpio = em2874_pinnacle_80e_digital;
+ break;
}
em28xx_gpio_set(dev, dev->tun_analog_gpio);
@@ -1800,4 +1848,6 @@ void em28xx_card_setup(struct em28xx *dev)
#endif
em28xx_config_tuner(dev);
+
+ em28xx_ir_init(dev);
}
diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c
index 1a30e361c..48c2caabe 100644
--- a/linux/drivers/media/video/em28xx/em28xx-core.c
+++ b/linux/drivers/media/video/em28xx/em28xx-core.c
@@ -175,9 +175,9 @@ int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len)
Not sure what happens on reading GPO register.
*/
if (rc >= 0) {
- if (reg == EM2880_R04_GPO)
+ if (reg == dev->reg_gpo_num)
dev->reg_gpo = buf[0];
- else if (reg == EM28XX_R08_GPIO)
+ else if (reg == dev->reg_gpio_num)
dev->reg_gpio = buf[0];
}
@@ -196,9 +196,9 @@ static int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
u8 newval;
/* Uses cache for gpo/gpio registers */
- if (reg == EM2880_R04_GPO)
+ if (reg == dev->reg_gpo_num)
oldval = dev->reg_gpo;
- else if (reg == EM28XX_R08_GPIO)
+ else if (reg == dev->reg_gpio_num)
oldval = dev->reg_gpio;
else
oldval = em28xx_read_reg(dev, reg);
@@ -359,6 +359,24 @@ int em28xx_colorlevels_set_default(struct em28xx *dev)
int em28xx_capture_start(struct em28xx *dev, int start)
{
int rc;
+
+ if (dev->chip_id == CHIP_ID_EM2874) {
+ /* The Transport Stream Enable Register moved in em2874 */
+ if (!start) {
+ rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE,
+ 0x00,
+ EM2874_TS1_CAPTURE_ENABLE);
+ return rc;
+ }
+
+ /* Enable Transport Stream */
+ rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE,
+ EM2874_TS1_CAPTURE_ENABLE,
+ EM2874_TS1_CAPTURE_ENABLE);
+ return rc;
+ }
+
+
/* FIXME: which is the best order? */
/* video registers are sampled by VREF */
rc = em28xx_write_reg_bits(dev, EM28XX_R0C_USBSUSP,
diff --git a/linux/drivers/media/video/em28xx/em28xx-i2c.c b/linux/drivers/media/video/em28xx/em28xx-i2c.c
index 3bab56b99..51ecdcd34 100644
--- a/linux/drivers/media/video/em28xx/em28xx-i2c.c
+++ b/linux/drivers/media/video/em28xx/em28xx-i2c.c
@@ -332,6 +332,17 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len)
struct em28xx_eeprom *em_eeprom = (void *)eedata;
int i, err, size = len, block;
+ if (dev->chip_id == CHIP_ID_EM2874) {
+ /* Empia switched to a 16-bit addressable eeprom in newer
+ devices. While we could certainly write a routine to read
+ the eeprom, there is nothing of use in there that cannot be
+ accessed through registers, and there is the risk that we
+ could corrupt the eeprom (since a 16-bit read call is
+ interpreted as a write call by 8-bit eeproms).
+ */
+ return 0;
+ }
+
dev->i2c_client.addr = 0xa0 >> 1;
/* Check if board has eeprom */
diff --git a/linux/drivers/media/video/em28xx/em28xx-input.c b/linux/drivers/media/video/em28xx/em28xx-input.c
index 8d21eaad9..c3c9cba30 100644
--- a/linux/drivers/media/video/em28xx/em28xx-input.c
+++ b/linux/drivers/media/video/em28xx/em28xx-input.c
@@ -39,12 +39,42 @@ static unsigned int ir_debug;
module_param(ir_debug, int, 0644);
MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]");
-#define dprintk(fmt, arg...) \
+#define i2cdprintk(fmt, arg...) \
if (ir_debug) { \
printk(KERN_DEBUG "%s/ir: " fmt, ir->c.name , ## arg); \
}
-/* ----------------------------------------------------------------------- */
+#define dprintk(fmt, arg...) \
+ if (ir_debug) { \
+ printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \
+ }
+
+/**********************************************************
+ Polling structure used by em28xx IR's
+ **********************************************************/
+
+struct em28xx_IR {
+ struct em28xx *dev;
+ struct input_dev *input;
+ struct ir_input_state ir;
+ char name[32];
+ char phys[32];
+
+ /* poll external decoder */
+ int polling;
+ struct work_struct work;
+ struct timer_list timer;
+ u32 last_gpio;
+ u32 mask_keycode;
+ u32 mask_keydown;
+ u32 mask_keyup;
+
+ int (*get_key)(struct em28xx_IR *);
+};
+
+/**********************************************************
+ I2C IR based get keycodes - should be used with ir-kbd-i2c
+ **********************************************************/
int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
{
@@ -52,7 +82,7 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
/* poll IR chip */
if (1 != i2c_master_recv(&ir->c, &b, 1)) {
- dprintk("read error\n");
+ i2cdprintk("read error\n");
return -EIO;
}
@@ -60,7 +90,7 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
down, while 0xff indicates that no button is hold
down. 0xfe sequences are sometimes interrupted by 0xFF */
- dprintk("key %02x\n", b);
+ i2cdprintk("key %02x\n", b);
if (b == 0xff)
return 0;
@@ -74,7 +104,6 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
return 1;
}
-
int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
{
unsigned char buf[2];
@@ -103,7 +132,7 @@ int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
((buf[0]&0x10)>>3) | /* 0000 0010 */
((buf[0]&0x20)>>5); /* 0000 0001 */
- dprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x)\n",
+ i2cdprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x)\n",
code, buf[0]);
/* return key */
@@ -120,11 +149,11 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
/* poll IR chip */
if (3 != i2c_master_recv(&ir->c, buf, 3)) {
- dprintk("read error\n");
+ i2cdprintk("read error\n");
return -EIO;
}
- dprintk("key %02x\n", buf[2]&0x3f);
+ i2cdprintk("key %02x\n", buf[2]&0x3f);
if (buf[0] != 0x00)
return 0;
@@ -134,6 +163,215 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
return 1;
}
+/**********************************************************
+ Poll based get keycode functions
+ **********************************************************/
+
+static int default_polling_getkey(struct em28xx_IR *ir)
+{
+ struct em28xx *dev = ir->dev;
+ int rc;
+ u32 msg;
+
+ /* Read key toggle, brand, and key code */
+ rc = dev->em28xx_read_reg_req_len(dev, 0, EM28XX_R45_IR,
+ (u8 *)&msg, sizeof(msg));
+ if (rc < 0)
+ return rc;
+
+ return (int)(msg & 0x7fffffffl);
+}
+
+/**********************************************************
+ Polling code for em28xx
+ **********************************************************/
+
+static void em28xx_ir_handle_key(struct em28xx_IR *ir)
+{
+ int gpio;
+ u32 data;
+
+ /* read gpio value */
+ gpio = ir->get_key(ir);
+ if (gpio < 0)
+ return;
+
+ if (gpio == ir->last_gpio)
+ return;
+ ir->last_gpio = gpio;
+
+ /* extract data */
+ data = ir_extract_bits(gpio, ir->mask_keycode);
+ dprintk("irq gpio=0x%x code=%d | poll%s%s\n",
+ gpio, data,
+ (gpio & ir->mask_keydown) ? " down" : "",
+ (gpio & ir->mask_keyup) ? " up" : "");
+
+ /* Generate keyup/keydown events */
+ if (ir->mask_keydown) {
+ /* bit set on keydown */
+ if (gpio & ir->mask_keydown)
+ ir_input_keydown(ir->input, &ir->ir, data, data);
+ else
+ ir_input_nokey(ir->input, &ir->ir);
+ } else if (ir->mask_keyup) {
+ /* bit cleared on keydown */
+ if (!(gpio & ir->mask_keyup))
+ ir_input_keydown(ir->input, &ir->ir, data, data);
+ else
+ ir_input_nokey(ir->input, &ir->ir);
+ } else {
+ /* can't distinguish keydown/up :-/ */
+ ir_input_keydown(ir->input, &ir->ir, data, data);
+ ir_input_nokey(ir->input, &ir->ir);
+ }
+}
+
+static void ir_timer(unsigned long data)
+{
+ struct em28xx_IR *ir = (struct em28xx_IR *)data;
+
+ schedule_work(&ir->work);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+static void em28xx_ir_work(void *data)
+#else
+static void em28xx_ir_work(struct work_struct *work)
+#endif
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+ struct em28xx_IR *ir = data;
+#else
+ struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work);
+#endif
+
+ em28xx_ir_handle_key(ir);
+ mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling));
+}
+
+void em28xx_ir_start(struct em28xx_IR *ir)
+{
+ setup_timer(&ir->timer, ir_timer, (unsigned long)ir);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+ INIT_WORK(&ir->work, em28xx_ir_work, ir);
+#else
+ INIT_WORK(&ir->work, em28xx_ir_work);
+#endif
+ schedule_work(&ir->work);
+}
+
+static void em28xx_ir_stop(struct em28xx_IR *ir)
+{
+ del_timer_sync(&ir->timer);
+ flush_scheduled_work();
+}
+
+int em28xx_ir_init(struct em28xx *dev)
+{
+ struct em28xx_IR *ir;
+ struct input_dev *input_dev;
+ IR_KEYTAB_TYPE *ir_codes = NULL;
+ int ir_type = IR_TYPE_OTHER;
+ int err = -ENOMEM;
+
+ ir = kzalloc(sizeof(*ir), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ir || !input_dev)
+ goto err_out_free;
+
+ ir->input = input_dev;
+
+ /* */
+ ir->get_key = default_polling_getkey;
+ ir->polling = 50; /* ms */
+
+ /* detect & configure */
+ switch (dev->model) {
+#if 0
+ /* dummy entry, just as a reference, while we don't add
+ other entries here
+ */
+ case EM2820_BOARD_UNKNOWN:
+ ir_type = IR_TYPE_OTHER;
+ ir_codes = ir_codes_empty;
+ ir->mask_keycode = 0x007f0000;
+ break;
+ }
+#endif
+ }
+
+ if (NULL == ir_codes) {
+ err = -ENODEV;
+ goto err_out_free;
+ }
+
+ /* Get the current key status, to avoid adding an
+ unexistent key code */
+ ir->last_gpio = ir->get_key(ir);
+
+ /* init input device */
+ snprintf(ir->name, sizeof(ir->name), "em28xx IR (%s)",
+ dev->name);
+
+ usb_make_path(dev->udev, ir->phys, sizeof(ir->phys));
+ strlcat(ir->phys, "/input0", sizeof(ir->phys));
+
+ ir_input_init(input_dev, &ir->ir, ir_type, ir_codes);
+ input_dev->name = ir->name;
+ input_dev->phys = ir->phys;
+ input_dev->id.bustype = BUS_USB;
+ input_dev->id.version = 1;
+ input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
+ input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
+ input_dev->dev.parent = &dev->udev->dev;
+#else
+ input_dev->cdev.dev = &dev->udev->dev;
+#endif
+ /* record handles to ourself */
+ ir->dev = dev;
+ dev->ir = ir;
+
+ em28xx_ir_start(ir);
+
+ /* all done */
+ err = input_register_device(ir->input);
+ if (err)
+ goto err_out_stop;
+
+ return 0;
+ err_out_stop:
+ em28xx_ir_stop(ir);
+ dev->ir = NULL;
+ err_out_free:
+ input_free_device(input_dev);
+ kfree(ir);
+ return err;
+}
+
+int em28xx_ir_fini(struct em28xx *dev)
+{
+ struct em28xx_IR *ir = dev->ir;
+
+ /* skip detach on non attached boards */
+ if (!ir)
+ return 0;
+
+ em28xx_ir_stop(ir);
+ input_unregister_device(ir->input);
+ kfree(ir);
+
+ /* done */
+ dev->ir = NULL;
+ return 0;
+}
+
+/**********************************************************
+ Handle Webcam snapshot button
+ **********************************************************/
+
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
static void em28xx_query_sbutton(void *data)
#else
@@ -232,9 +470,3 @@ void em28xx_deregister_snapshot_button(struct em28xx *dev)
}
return;
}
-
-/* ----------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/linux/drivers/media/video/em28xx/em28xx-reg.h b/linux/drivers/media/video/em28xx/em28xx-reg.h
index fac1ab23f..f67955a1b 100644
--- a/linux/drivers/media/video/em28xx/em28xx-reg.h
+++ b/linux/drivers/media/video/em28xx/em28xx-reg.h
@@ -71,11 +71,30 @@
#define EM28XX_R42_AC97ADDR 0x42
#define EM28XX_R43_AC97BUSY 0x43
+#define EM28XX_R45_IR 0x45
+ /* 0x45 bit 7 - parity bit
+ bits 6-0 - count
+ 0x46 IR brand
+ 0x47 IR data
+ */
+
/* em202 registers */
#define EM28XX_R02_MASTER_AC97 0x02
#define EM28XX_R10_LINE_IN_AC97 0x10
#define EM28XX_R14_VIDEO_AC97 0x14
+/* em2874 registers */
+#define EM2874_R5F_TS_ENABLE 0x5f
+#define EM2874_R80_GPIO 0x80
+
+/* em2874 Transport Stream Enable Register (0x5f) */
+#define EM2874_TS1_CAPTURE_ENABLE (1 << 0)
+#define EM2874_TS1_FILTER_ENABLE (1 << 1)
+#define EM2874_TS1_NULL_DISCARD (1 << 2)
+#define EM2874_TS2_CAPTURE_ENABLE (1 << 4)
+#define EM2874_TS2_FILTER_ENABLE (1 << 5)
+#define EM2874_TS2_NULL_DISCARD (1 << 6)
+
/* register settings */
#define EM2800_AUDIO_SRC_TUNER 0x0d
#define EM2800_AUDIO_SRC_LINE 0x0c
@@ -86,4 +105,5 @@
enum em28xx_chip_id {
CHIP_ID_EM2860 = 34,
CHIP_ID_EM2883 = 36,
+ CHIP_ID_EM2874 = 65,
};
diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c
index a4493da8a..09886d567 100644
--- a/linux/drivers/media/video/em28xx/em28xx-video.c
+++ b/linux/drivers/media/video/em28xx/em28xx-video.c
@@ -1656,11 +1656,13 @@ static void em28xx_release_resources(struct em28xx *dev)
/*FIXME: I2C IR should be disconnected */
- em28xx_info("V4L2 devices /dev/video%d and /dev/vbi%d deregistered\n",
- dev->vdev->num, dev->vbi_dev->num);
list_del(&dev->devlist);
if (dev->sbutton_input_dev)
em28xx_deregister_snapshot_button(dev);
+
+ if (dev->ir)
+ em28xx_ir_fini(dev);
+
if (dev->radio_dev) {
if (-1 != dev->radio_dev->minor)
video_unregister_device(dev->radio_dev);
@@ -1669,6 +1671,8 @@ static void em28xx_release_resources(struct em28xx *dev)
dev->radio_dev = NULL;
}
if (dev->vbi_dev) {
+ em28xx_info("V4L2 device /dev/vbi%d deregistered\n",
+ dev->vbi_dev->num);
if (-1 != dev->vbi_dev->minor)
video_unregister_device(dev->vbi_dev);
else
@@ -1676,6 +1680,8 @@ static void em28xx_release_resources(struct em28xx *dev)
dev->vbi_dev = NULL;
}
if (dev->vdev) {
+ em28xx_info("V4L2 device /dev/video%d deregistered\n",
+ dev->vdev->num);
if (-1 != dev->vdev->minor)
video_unregister_device(dev->vdev);
else
@@ -1982,6 +1988,19 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev,
return vfd;
}
+int em28xx_supports_audio_extension(struct em28xx *dev)
+{
+ /* The chip dictates whether we support the Empia analog audio
+ extension */
+ switch (dev->chip_id) {
+ case CHIP_ID_EM2874:
+ /* Either a digital-only device or provides AC97 audio */
+ return 0;
+ case CHIP_ID_EM2883:
+ default:
+ return 1;
+ }
+}
/*
* em28xx_init_dev()
@@ -2171,7 +2190,7 @@ static void request_module_async(struct work_struct *work)
if (dev->has_audio_class)
request_module("snd-usb-audio");
- else
+ else if (dev->has_alsa_audio)
request_module("em28xx-alsa");
if (dev->has_dvb)
@@ -2204,7 +2223,7 @@ static int em28xx_usb_probe(struct usb_interface *interface,
struct usb_interface *uif;
struct em28xx *dev = NULL;
int retval = -ENODEV;
- int i, nr, ifnum;
+ int i, nr, ifnum, isoc_pipe;
udev = usb_get_dev(interface_to_usbdev(interface));
ifnum = interface->altsetting[0].desc.bInterfaceNumber;
@@ -2231,19 +2250,29 @@ static int em28xx_usb_probe(struct usb_interface *interface,
ifnum,
interface->altsetting[0].desc.bInterfaceClass);
- endpoint = &interface->cur_altsetting->endpoint[1].desc;
+ endpoint = &interface->cur_altsetting->endpoint[0].desc;
/* check if the device has the iso in endpoint at the correct place */
- if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
- USB_ENDPOINT_XFER_ISOC) {
- em28xx_err(DRIVER_NAME " probing error: endpoint is non-ISO endpoint!\n");
- em28xx_devused &= ~(1<<nr);
- return -ENODEV;
- }
- if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) {
- em28xx_err(DRIVER_NAME " probing error: endpoint is ISO OUT endpoint!\n");
- em28xx_devused &= ~(1<<nr);
- return -ENODEV;
+ if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_ISOC &&
+ (interface->altsetting[1].endpoint[0].desc.wMaxPacketSize == 940))
+ {
+ /* It's a newer em2874/em2875 device */
+ isoc_pipe = 0;
+ } else {
+ isoc_pipe = 1;
+ endpoint = &interface->cur_altsetting->endpoint[1].desc;
+ if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
+ USB_ENDPOINT_XFER_ISOC) {
+ em28xx_err(DRIVER_NAME " probing error: endpoint is non-ISO endpoint!\n");
+ em28xx_devused &= ~(1<<nr);
+ return -ENODEV;
+ }
+ if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) {
+ em28xx_err(DRIVER_NAME " probing error: endpoint is ISO OUT endpoint!\n");
+ em28xx_devused &= ~(1<<nr);
+ return -ENODEV;
+ }
}
if (nr >= EM28XX_MAXBOARDS) {
@@ -2275,9 +2304,6 @@ static int em28xx_usb_probe(struct usb_interface *interface,
}
}
- 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];
@@ -2294,7 +2320,7 @@ static int em28xx_usb_probe(struct usb_interface *interface,
}
for (i = 0; i < dev->num_alt ; i++) {
- u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[1].desc.
+ u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.
wMaxPacketSize);
dev->alt_max_pkt_size[i] =
(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
@@ -2312,6 +2338,17 @@ static int em28xx_usb_probe(struct usb_interface *interface,
em28xx_info("Found %s\n", em28xx_boards[dev->model].name);
+ if (dev->has_audio_class == 0) {
+ /* We don't have a USB audio class, let's see if we support
+ ALSA Audio */
+ dev->has_alsa_audio = em28xx_supports_audio_extension(dev);
+ if (dev->has_alsa_audio)
+ printk(KERN_INFO DRIVER_NAME " supports alsa audio\n");
+ } else {
+ printk(KERN_INFO DRIVER_NAME " has usb audio class\n");
+ }
+
+
/* save our data pointer in this interface device */
usb_set_intfdata(interface, dev);
diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h
index d73b8c983..1bccb9ee4 100644
--- a/linux/drivers/media/video/em28xx/em28xx.h
+++ b/linux/drivers/media/video/em28xx/em28xx.h
@@ -98,6 +98,7 @@
#define EM2882_BOARD_PINNACLE_HYBRID_PRO 56
#define EM2883_BOARD_KWORLD_HYBRID_A316 57
#define EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU 58
+#define EM2874_BOARD_PINNACLE_PCTV_80E 59
/* Limits minimum and default number of buffers */
#define EM28XX_MIN_BUF 4
@@ -269,6 +270,7 @@ struct em28xx_input {
#define INPUT(nr) (&em28xx_boards[dev->model].input[nr])
enum em28xx_decoder {
+ EM28XX_NODECODER,
EM28XX_TVP5150,
EM28XX_SAA7113,
EM28XX_SAA7114
@@ -375,17 +377,21 @@ 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 */
+ enum em28xx_chip_id chip_id;
unsigned int is_em2800:1;
unsigned int has_msp34xx:1;
unsigned int has_tda9887:1;
unsigned int stream_on:1; /* Locks streams */
unsigned int has_audio_class:1;
+ unsigned int has_alsa_audio:1;
unsigned int has_12mhz_i2s:1;
unsigned int max_range_640_480:1;
unsigned int has_dvb:1;
unsigned int has_snapshot_button:1;
unsigned int valid:1; /* report for validated boards */
+ struct em28xx_IR *ir;
+
/* Some older em28xx chips needs a waiting time after writing */
unsigned int wait_after_write;
@@ -472,6 +478,9 @@ struct em28xx {
enum em28xx_mode mode;
+ /* register numbers for GPO/GPIO registers */
+ u16 reg_gpo_num, reg_gpio_num;
+
/* Caches GPO and GPIO registers */
unsigned char reg_gpo, reg_gpio;
@@ -544,7 +553,6 @@ void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir);
int em28xx_tuner_callback(void *ptr, int component, int command, int arg);
/* Provided by em28xx-input.c */
-/* TODO: Check if the standard get_key handlers on ir-common can be used */
int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw);
int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw);
int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
@@ -552,6 +560,9 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
void em28xx_register_snapshot_button(struct em28xx *dev);
void em28xx_deregister_snapshot_button(struct em28xx *dev);
+int em28xx_ir_init(struct em28xx *dev);
+int em28xx_ir_fini(struct em28xx *dev);
+
/* printk macros */
#define em28xx_err(fmt, arg...) do {\