mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2025-01-18 09:56:18 +07:00
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: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
parent
1958578da6
commit
a924a499ad
@ -1843,4 +1843,6 @@ void em28xx_card_setup(struct em28xx *dev)
|
||||
#endif
|
||||
|
||||
em28xx_config_tuner(dev);
|
||||
|
||||
em28xx_ir_init(dev);
|
||||
}
|
||||
|
@ -38,12 +38,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)
|
||||
{
|
||||
@ -51,7 +81,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;
|
||||
}
|
||||
|
||||
@ -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. 0xfe sequences are sometimes interrupted by 0xFF */
|
||||
|
||||
dprintk("key %02x\n", b);
|
||||
i2cdprintk("key %02x\n", b);
|
||||
|
||||
if (b == 0xff)
|
||||
return 0;
|
||||
@ -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];
|
||||
@ -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 */
|
||||
@ -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;
|
||||
|
||||
@ -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 */
|
||||
@ -210,9 +421,3 @@ void em28xx_deregister_snapshot_button(struct em28xx *dev)
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
/* 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 +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_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 {\
|
||||
|
Loading…
Reference in New Issue
Block a user