[media] tm6000: rework and fix IR

Hi

This patch didn't kill Stefan's remotes and just for upload my good part of code.
1. Add some code for show IR activity
2. Add filter for IR remotes
3. Split remotes to different types.
4. Fix stop interrupt pipe when isoc pipe started.

When we decide general way of IR I'll add support our remotes.
For our customers I'll made custom temporary patch without this part.

Signed-off-by: Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
Dmitri Belimov 2010-12-22 05:57:46 -03:00 коммит произвёл Mauro Carvalho Chehab
Родитель db9285f79b
Коммит 641d21167f
5 изменённых файлов: 193 добавлений и 51 удалений

Просмотреть файл

@ -1,4 +1,6 @@
There a few things to do before putting this driver in production: There a few things to do before putting this driver in production:
- IR NEC with tm5600/6000 TV cards
- IR RC5 with tm5600/6000/6010 TV cards
- CodingStyle; - CodingStyle;
- Fix audio; - Fix audio;
- Fix some panic/OOPS conditions. - Fix some panic/OOPS conditions.

Просмотреть файл

@ -328,6 +328,47 @@ struct usb_device_id tm6000_id_table[] = {
{ }, { },
}; };
/* Control power led for show some activity */
void tm6000_flash_led(struct tm6000_core *dev, u8 state)
{
/* Power LED unconfigured */
if (!dev->gpio.power_led)
return;
/* ON Power LED */
if (state) {
switch (dev->model) {
case TM6010_BOARD_HAUPPAUGE_900H:
case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
case TM6010_BOARD_TWINHAN_TU501:
tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
dev->gpio.power_led, 0x00);
break;
case TM6010_BOARD_BEHOLD_WANDER:
case TM6010_BOARD_BEHOLD_VOYAGER:
tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
dev->gpio.power_led, 0x01);
break;
}
}
/* OFF Power LED */
else {
switch (dev->model) {
case TM6010_BOARD_HAUPPAUGE_900H:
case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
case TM6010_BOARD_TWINHAN_TU501:
tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
dev->gpio.power_led, 0x01);
break;
case TM6010_BOARD_BEHOLD_WANDER:
case TM6010_BOARD_BEHOLD_VOYAGER:
tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
dev->gpio.power_led, 0x00);
break;
}
}
}
/* Tuner callback to provide the proper gpio changes needed for xc5000 */ /* Tuner callback to provide the proper gpio changes needed for xc5000 */
int tm6000_xc5000_callback(void *ptr, int component, int command, int arg) int tm6000_xc5000_callback(void *ptr, int component, int command, int arg)
{ {

Просмотреть файл

@ -37,6 +37,10 @@ static unsigned int enable_ir = 1;
module_param(enable_ir, int, 0644); module_param(enable_ir, int, 0644);
MODULE_PARM_DESC(enable_ir, "enable ir (default is enable)"); MODULE_PARM_DESC(enable_ir, "enable ir (default is enable)");
/* number of 50ms for ON-OFF-ON power led */
/* show IR activity */
#define PWLED_OFF 2
#undef dprintk #undef dprintk
#define dprintk(fmt, arg...) \ #define dprintk(fmt, arg...) \
@ -59,6 +63,9 @@ struct tm6000_IR {
struct delayed_work work; struct delayed_work work;
u8 wait:1; u8 wait:1;
u8 key:1; u8 key:1;
u8 pwled:1;
u8 pwledcnt;
u16 key_addr;
struct urb *int_urb; struct urb *int_urb;
u8 *urb_data; u8 *urb_data;
@ -89,26 +96,49 @@ static int tm6000_ir_config(struct tm6000_IR *ir)
u8 buf[10]; u8 buf[10];
int rc; int rc;
/* hack */ switch (ir->rc_type) {
buf[0] = 0xff; case RC_TYPE_NEC:
buf[1] = 0xff; /* Setup IR decoder for NEC standard 12MHz system clock */
buf[2] = 0xf2; /* IR_LEADER_CNT = 0.9ms */
buf[3] = 0x2b; tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_LEADER1, 0xaa);
buf[4] = 0x20; tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_LEADER0, 0x30);
buf[5] = 0x35; /* IR_PULSE_CNT = 0.7ms */
buf[6] = 0x60; tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_PULSE_CNT1, 0x20);
buf[7] = 0x04; tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_PULSE_CNT0, 0xd0);
buf[8] = 0xc0; /* Remote WAKEUP = enable */
buf[9] = 0x08; tm6000_set_reg(dev, TM6010_REQ07_RE5_REMOTE_WAKEUP, 0xfe);
/* IR_WKUP_SEL = Low byte in decoded IR data */
tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_WAKEUP_SEL, 0xff);
/* IR_WKU_ADD code */
tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_WAKEUP_ADD, 0xff);
tm6000_flash_led(dev, 0);
msleep(100);
tm6000_flash_led(dev, 1);
break;
default:
/* hack */
buf[0] = 0xff;
buf[1] = 0xff;
buf[2] = 0xf2;
buf[3] = 0x2b;
buf[4] = 0x20;
buf[5] = 0x35;
buf[6] = 0x60;
buf[7] = 0x04;
buf[8] = 0xc0;
buf[9] = 0x08;
rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR | rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR |
USB_RECIP_DEVICE, REQ_00_SET_IR_VALUE, 0, 0, buf, 0x0a); USB_RECIP_DEVICE, REQ_00_SET_IR_VALUE, 0, 0, buf, 0x0a);
msleep(100); msleep(100);
if (rc < 0) { if (rc < 0) {
printk(KERN_INFO "IR configuration failed"); printk(KERN_INFO "IR configuration failed");
return rc; return rc;
}
break;
} }
return 0; return 0;
} }
@ -143,10 +173,21 @@ static int default_polling_getkey(struct tm6000_IR *ir,
return 0; return 0;
if (&dev->int_in) { if (&dev->int_in) {
if (ir->rc_type == RC_TYPE_RC5) switch (ir->rc_type) {
case RC_TYPE_RC5:
poll_result->rc_data = ir->urb_data[0]; poll_result->rc_data = ir->urb_data[0];
else break;
poll_result->rc_data = ir->urb_data[0] | ir->urb_data[1] << 8; case RC_TYPE_NEC:
if (ir->urb_data[1] == ((ir->key_addr >> 8) & 0xff)) {
poll_result->rc_data = ir->urb_data[0]
| ir->urb_data[1] << 8;
}
break;
default:
poll_result->rc_data = ir->urb_data[0]
| ir->urb_data[1] << 8;
break;
}
} else { } else {
tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 0); tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 0);
msleep(10); msleep(10);
@ -186,6 +227,7 @@ static int default_polling_getkey(struct tm6000_IR *ir,
static void tm6000_ir_handle_key(struct tm6000_IR *ir) static void tm6000_ir_handle_key(struct tm6000_IR *ir)
{ {
struct tm6000_core *dev = ir->dev;
int result; int result;
struct tm6000_ir_poll_result poll_result; struct tm6000_ir_poll_result poll_result;
@ -198,9 +240,21 @@ static void tm6000_ir_handle_key(struct tm6000_IR *ir)
dprintk("ir->get_key result data=%04x\n", poll_result.rc_data); dprintk("ir->get_key result data=%04x\n", poll_result.rc_data);
if (ir->pwled) {
if (ir->pwledcnt >= PWLED_OFF) {
ir->pwled = 0;
ir->pwledcnt = 0;
tm6000_flash_led(dev, 1);
} else
ir->pwledcnt += 1;
}
if (ir->key) { if (ir->key) {
rc_keydown(ir->rc, poll_result.rc_data, 0); rc_keydown(ir->rc, poll_result.rc_data, 0);
ir->key = 0; ir->key = 0;
ir->pwled = 1;
ir->pwledcnt = 0;
tm6000_flash_led(dev, 0);
} }
return; return;
} }
@ -234,19 +288,80 @@ int tm6000_ir_change_protocol(struct rc_dev *rc, u64 rc_type)
{ {
struct tm6000_IR *ir = rc->priv; struct tm6000_IR *ir = rc->priv;
if (!ir)
return 0;
if ((rc->rc_map.scan) && (rc_type == RC_TYPE_NEC))
ir->key_addr = ((rc->rc_map.scan[0].scancode >> 8) & 0xffff);
ir->get_key = default_polling_getkey; ir->get_key = default_polling_getkey;
ir->rc_type = rc_type;
tm6000_ir_config(ir); tm6000_ir_config(ir);
/* TODO */ /* TODO */
return 0; return 0;
} }
int tm6000_ir_int_start(struct tm6000_core *dev)
{
struct tm6000_IR *ir = dev->ir;
int pipe, size;
int err = -ENOMEM;
if (!ir)
return -ENODEV;
ir->int_urb = usb_alloc_urb(0, GFP_KERNEL);
pipe = usb_rcvintpipe(dev->udev,
dev->int_in.endp->desc.bEndpointAddress
& USB_ENDPOINT_NUMBER_MASK);
size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe));
dprintk("IR max size: %d\n", size);
ir->int_urb->transfer_buffer = kzalloc(size, GFP_KERNEL);
if (ir->int_urb->transfer_buffer == NULL) {
usb_free_urb(ir->int_urb);
return err;
}
dprintk("int interval: %d\n", dev->int_in.endp->desc.bInterval);
usb_fill_int_urb(ir->int_urb, dev->udev, pipe,
ir->int_urb->transfer_buffer, size,
tm6000_ir_urb_received, dev,
dev->int_in.endp->desc.bInterval);
err = usb_submit_urb(ir->int_urb, GFP_KERNEL);
if (err) {
kfree(ir->int_urb->transfer_buffer);
usb_free_urb(ir->int_urb);
return err;
}
ir->urb_data = kzalloc(size, GFP_KERNEL);
return 0;
}
void tm6000_ir_int_stop(struct tm6000_core *dev)
{
struct tm6000_IR *ir = dev->ir;
if (!ir)
return;
usb_kill_urb(ir->int_urb);
kfree(ir->int_urb->transfer_buffer);
usb_free_urb(ir->int_urb);
ir->int_urb = NULL;
kfree(ir->urb_data);
ir->urb_data = NULL;
}
int tm6000_ir_init(struct tm6000_core *dev) int tm6000_ir_init(struct tm6000_core *dev)
{ {
struct tm6000_IR *ir; struct tm6000_IR *ir;
struct rc_dev *rc; struct rc_dev *rc;
int err = -ENOMEM; int err = -ENOMEM;
int pipe, size;
if (!enable_ir) if (!enable_ir)
return -ENODEV; return -ENODEV;
@ -276,6 +391,9 @@ int tm6000_ir_init(struct tm6000_core *dev)
rc->driver_type = RC_DRIVER_SCANCODE; rc->driver_type = RC_DRIVER_SCANCODE;
ir->polling = 50; ir->polling = 50;
ir->pwled = 0;
ir->pwledcnt = 0;
snprintf(ir->name, sizeof(ir->name), "tm5600/60x0 IR (%s)", snprintf(ir->name, sizeof(ir->name), "tm5600/60x0 IR (%s)",
dev->name); dev->name);
@ -298,32 +416,10 @@ int tm6000_ir_init(struct tm6000_core *dev)
if (&dev->int_in) { if (&dev->int_in) {
dprintk("IR over int\n"); dprintk("IR over int\n");
ir->int_urb = usb_alloc_urb(0, GFP_KERNEL); err = tm6000_ir_int_start(dev);
pipe = usb_rcvintpipe(dev->udev, if (err)
dev->int_in.endp->desc.bEndpointAddress
& USB_ENDPOINT_NUMBER_MASK);
size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe));
dprintk("IR max size: %d\n", size);
ir->int_urb->transfer_buffer = kzalloc(size, GFP_KERNEL);
if (ir->int_urb->transfer_buffer == NULL) {
usb_free_urb(ir->int_urb);
goto out; goto out;
}
dprintk("int interval: %d\n", dev->int_in.endp->desc.bInterval);
usb_fill_int_urb(ir->int_urb, dev->udev, pipe,
ir->int_urb->transfer_buffer, size,
tm6000_ir_urb_received, dev,
dev->int_in.endp->desc.bInterval);
err = usb_submit_urb(ir->int_urb, GFP_KERNEL);
if (err) {
kfree(ir->int_urb->transfer_buffer);
usb_free_urb(ir->int_urb);
goto out;
}
ir->urb_data = kzalloc(size, GFP_KERNEL);
} }
/* ir register */ /* ir register */
@ -352,12 +448,7 @@ int tm6000_ir_fini(struct tm6000_core *dev)
rc_unregister_device(ir->rc); rc_unregister_device(ir->rc);
if (ir->int_urb) { if (ir->int_urb) {
usb_kill_urb(ir->int_urb); tm6000_ir_int_stop(dev);
kfree(ir->int_urb->transfer_buffer);
usb_free_urb(ir->int_urb);
ir->int_urb = NULL;
kfree(ir->urb_data);
ir->urb_data = NULL;
} }
kfree(ir); kfree(ir);

Просмотреть файл

@ -545,11 +545,16 @@ static int tm6000_prepare_isoc(struct tm6000_core *dev, unsigned int framesize)
/* De-allocates all pending stuff */ /* De-allocates all pending stuff */
tm6000_uninit_isoc(dev); tm6000_uninit_isoc(dev);
/* Stop interrupt USB pipe */
tm6000_ir_int_stop(dev);
usb_set_interface(dev->udev, usb_set_interface(dev->udev,
dev->isoc_in.bInterfaceNumber, dev->isoc_in.bInterfaceNumber,
dev->isoc_in.bAlternateSetting); dev->isoc_in.bAlternateSetting);
/* Start interrupt USB pipe */
tm6000_ir_int_start(dev);
pipe = usb_rcvisocpipe(dev->udev, pipe = usb_rcvisocpipe(dev->udev,
dev->isoc_in.endp->desc.bEndpointAddress & dev->isoc_in.endp->desc.bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK); USB_ENDPOINT_NUMBER_MASK);

Просмотреть файл

@ -266,6 +266,7 @@ struct tm6000_fh {
int tm6000_tuner_callback(void *ptr, int component, int command, int arg); int tm6000_tuner_callback(void *ptr, int component, int command, int arg);
int tm6000_xc5000_callback(void *ptr, int component, int command, int arg); int tm6000_xc5000_callback(void *ptr, int component, int command, int arg);
int tm6000_cards_setup(struct tm6000_core *dev); int tm6000_cards_setup(struct tm6000_core *dev);
void tm6000_flash_led(struct tm6000_core *dev, u8 state);
/* In tm6000-core.c */ /* In tm6000-core.c */
@ -332,6 +333,8 @@ int tm6000_queue_init(struct tm6000_core *dev);
int tm6000_ir_init(struct tm6000_core *dev); int tm6000_ir_init(struct tm6000_core *dev);
int tm6000_ir_fini(struct tm6000_core *dev); int tm6000_ir_fini(struct tm6000_core *dev);
void tm6000_ir_wait(struct tm6000_core *dev, u8 state); void tm6000_ir_wait(struct tm6000_core *dev, u8 state);
int tm6000_ir_int_start(struct tm6000_core *dev);
void tm6000_ir_int_stop(struct tm6000_core *dev);
/* Debug stuff */ /* Debug stuff */