Skip to content

Commit

Permalink
V4L/DVB (9607): em28xx: Properly implement poll support for IR's
Browse files Browse the repository at this point in the history
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: Mauro Carvalho Chehab <mchehab@redhat.com>
  • Loading branch information
Mauro Carvalho Chehab committed Dec 29, 2008
1 parent 1958578 commit a924a49
Show file tree
Hide file tree
Showing 5 changed files with 237 additions and 15 deletions.
2 changes: 2 additions & 0 deletions drivers/media/video/em28xx/em28xx-cards.c
Original file line number Diff line number Diff line change
Expand Up @@ -1843,4 +1843,6 @@ void em28xx_card_setup(struct em28xx *dev)
#endif

em28xx_config_tuner(dev);

em28xx_ir_init(dev);
}
233 changes: 219 additions & 14 deletions drivers/media/video/em28xx/em28xx-input.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,28 +38,58 @@ 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)
{
unsigned char b;

/* poll IR chip */
if (1 != i2c_master_recv(&ir->c, &b, 1)) {
dprintk("read error\n");
i2cdprintk("read error\n");
return -EIO;
}

/* it seems that 0xFE indicates that a button is still hold
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;
Expand All @@ -73,7 +103,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];
Expand All @@ -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]&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 */
Expand All @@ -114,11 +143,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;

Expand All @@ -128,6 +157,188 @@ 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);
}

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)
{
/* Poll the register and see if the button is depressed */
Expand Down Expand Up @@ -210,9 +421,3 @@ void em28xx_deregister_snapshot_button(struct em28xx *dev)
}
return;
}

/* ----------------------------------------------------------------------
* Local variables:
* c-basic-offset: 8
* End:
*/
7 changes: 7 additions & 0 deletions drivers/media/video/em28xx/em28xx-reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@
#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
Expand Down
4 changes: 4 additions & 0 deletions drivers/media/video/em28xx/em28xx-video.c
Original file line number Diff line number Diff line change
Expand Up @@ -1603,6 +1603,10 @@ static void em28xx_release_resources(struct em28xx *dev)
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);
Expand Down
6 changes: 5 additions & 1 deletion drivers/media/video/em28xx/em28xx.h
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,8 @@ struct em28xx {
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;

Expand Down Expand Up @@ -544,14 +546,16 @@ 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,
u32 *ir_raw);
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 {\
Expand Down

0 comments on commit a924a49

Please sign in to comment.