Commit a924a499 authored by Mauro Carvalho Chehab's avatar Mauro Carvalho Chehab

V4L/DVB (9607): em28xx: Properly implement poll support for IR's

The first em28xx were based on i2c IR's. However, some newer designs
are coming with a polling-based IR. Those are done by reading a register
set at em28xx.

This patch adds core polling support for those devices. Later patches will
add support for some device-specific IR's.

This patch adds the same basic IR polling code used by bttv, cx88 and saa7134, and
shares the common getkey masks defined at ir-common.
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 1958578d
...@@ -1843,4 +1843,6 @@ void em28xx_card_setup(struct em28xx *dev) ...@@ -1843,4 +1843,6 @@ void em28xx_card_setup(struct em28xx *dev)
#endif #endif
em28xx_config_tuner(dev); em28xx_config_tuner(dev);
em28xx_ir_init(dev);
} }
...@@ -38,12 +38,42 @@ static unsigned int ir_debug; ...@@ -38,12 +38,42 @@ static unsigned int ir_debug;
module_param(ir_debug, int, 0644); module_param(ir_debug, int, 0644);
MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]");
#define dprintk(fmt, arg...) \ #define i2cdprintk(fmt, arg...) \
if (ir_debug) { \ if (ir_debug) { \
printk(KERN_DEBUG "%s/ir: " fmt, ir->c.name , ## arg); \ 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) int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
{ {
...@@ -51,7 +81,7 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) ...@@ -51,7 +81,7 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
/* poll IR chip */ /* poll IR chip */
if (1 != i2c_master_recv(&ir->c, &b, 1)) { if (1 != i2c_master_recv(&ir->c, &b, 1)) {
dprintk("read error\n"); i2cdprintk("read error\n");
return -EIO; return -EIO;
} }
...@@ -59,7 +89,7 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) ...@@ -59,7 +89,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, while 0xff indicates that no button is hold
down. 0xfe sequences are sometimes interrupted by 0xFF */ down. 0xfe sequences are sometimes interrupted by 0xFF */
dprintk("key %02x\n", b); i2cdprintk("key %02x\n", b);
if (b == 0xff) if (b == 0xff)
return 0; return 0;
...@@ -73,7 +103,6 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) ...@@ -73,7 +103,6 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
return 1; return 1;
} }
int em28xx_get_key_em_haup(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)
{ {
unsigned char buf[2]; unsigned char buf[2];
...@@ -97,7 +126,7 @@ int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) ...@@ -97,7 +126,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]&0x10)>>3) | /* 0000 0010 */
((buf[0]&0x20)>>5); /* 0000 0001 */ ((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]); code, buf[0]);
/* return key */ /* return key */
...@@ -114,11 +143,11 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, ...@@ -114,11 +143,11 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
/* poll IR chip */ /* poll IR chip */
if (3 != i2c_master_recv(&ir->c, buf, 3)) { if (3 != i2c_master_recv(&ir->c, buf, 3)) {
dprintk("read error\n"); i2cdprintk("read error\n");
return -EIO; return -EIO;
} }
dprintk("key %02x\n", buf[2]&0x3f); i2cdprintk("key %02x\n", buf[2]&0x3f);
if (buf[0] != 0x00) if (buf[0] != 0x00)
return 0; return 0;
...@@ -128,6 +157,188 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, ...@@ -128,6 +157,188 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
return 1; 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);
}
static void em28xx_ir_work(struct work_struct *work)
{
struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work);
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);
INIT_WORK(&ir->work, em28xx_ir_work);
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 (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);
input_dev->dev.parent = &dev->udev->dev;
/* 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
**********************************************************/
static void em28xx_query_sbutton(struct work_struct *work) static void em28xx_query_sbutton(struct work_struct *work)
{ {
/* Poll the register and see if the button is depressed */ /* Poll the register and see if the button is depressed */
...@@ -210,9 +421,3 @@ void em28xx_deregister_snapshot_button(struct em28xx *dev) ...@@ -210,9 +421,3 @@ void em28xx_deregister_snapshot_button(struct em28xx *dev)
} }
return; return;
} }
/* ----------------------------------------------------------------------
* Local variables:
* c-basic-offset: 8
* End:
*/
...@@ -71,6 +71,13 @@ ...@@ -71,6 +71,13 @@
#define EM28XX_R42_AC97ADDR 0x42 #define EM28XX_R42_AC97ADDR 0x42
#define EM28XX_R43_AC97BUSY 0x43 #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 */ /* em202 registers */
#define EM28XX_R02_MASTER_AC97 0x02 #define EM28XX_R02_MASTER_AC97 0x02
#define EM28XX_R10_LINE_IN_AC97 0x10 #define EM28XX_R10_LINE_IN_AC97 0x10
......
...@@ -1603,6 +1603,10 @@ static void em28xx_release_resources(struct em28xx *dev) ...@@ -1603,6 +1603,10 @@ static void em28xx_release_resources(struct em28xx *dev)
list_del(&dev->devlist); list_del(&dev->devlist);
if (dev->sbutton_input_dev) if (dev->sbutton_input_dev)
em28xx_deregister_snapshot_button(dev); em28xx_deregister_snapshot_button(dev);
if (dev->ir)
em28xx_ir_fini(dev);
if (dev->radio_dev) { if (dev->radio_dev) {
if (-1 != dev->radio_dev->minor) if (-1 != dev->radio_dev->minor)
video_unregister_device(dev->radio_dev); video_unregister_device(dev->radio_dev);
......
...@@ -384,6 +384,8 @@ struct em28xx { ...@@ -384,6 +384,8 @@ struct em28xx {
unsigned int has_snapshot_button:1; unsigned int has_snapshot_button:1;
unsigned int valid:1; /* report for validated boards */ unsigned int valid:1; /* report for validated boards */
struct em28xx_IR *ir;
/* Some older em28xx chips needs a waiting time after writing */ /* Some older em28xx chips needs a waiting time after writing */
unsigned int wait_after_write; unsigned int wait_after_write;
...@@ -544,7 +546,6 @@ void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir); ...@@ -544,7 +546,6 @@ void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir);
int em28xx_tuner_callback(void *ptr, int component, int command, int arg); int em28xx_tuner_callback(void *ptr, int component, int command, int arg);
/* Provided by em28xx-input.c */ /* 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_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_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, int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
...@@ -552,6 +553,9 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, ...@@ -552,6 +553,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_register_snapshot_button(struct em28xx *dev);
void em28xx_deregister_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 */ /* printk macros */
#define em28xx_err(fmt, arg...) do {\ #define em28xx_err(fmt, arg...) do {\
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment