18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * tm6000-input.c - driver for TM5600/TM6000/TM6010 USB video capture devices 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010 Stefan Ringel <stefan.ringel@arcor.de> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/init.h> 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/input.h> 138c2ecf20Sopenharmony_ci#include <linux/usb.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <media/rc-core.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "tm6000.h" 188c2ecf20Sopenharmony_ci#include "tm6000-regs.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic unsigned int ir_debug; 218c2ecf20Sopenharmony_cimodule_param(ir_debug, int, 0644); 228c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ir_debug, "debug message level"); 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic unsigned int enable_ir = 1; 258c2ecf20Sopenharmony_cimodule_param(enable_ir, int, 0644); 268c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable_ir, "enable ir (default is enable)"); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic unsigned int ir_clock_mhz = 12; 298c2ecf20Sopenharmony_cimodule_param(ir_clock_mhz, int, 0644); 308c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ir_clock_mhz, "ir clock, in MHz"); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define URB_SUBMIT_DELAY 100 /* ms - Delay to submit an URB request on retrial and init */ 338c2ecf20Sopenharmony_ci#define URB_INT_LED_DELAY 100 /* ms - Delay to turn led on again on int mode */ 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#undef dprintk 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define dprintk(level, fmt, arg...) do {\ 388c2ecf20Sopenharmony_ci if (ir_debug >= level) \ 398c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \ 408c2ecf20Sopenharmony_ci } while (0) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistruct tm6000_ir_poll_result { 438c2ecf20Sopenharmony_ci u16 rc_data; 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistruct tm6000_IR { 478c2ecf20Sopenharmony_ci struct tm6000_core *dev; 488c2ecf20Sopenharmony_ci struct rc_dev *rc; 498c2ecf20Sopenharmony_ci char name[32]; 508c2ecf20Sopenharmony_ci char phys[32]; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci /* poll expernal decoder */ 538c2ecf20Sopenharmony_ci int polling; 548c2ecf20Sopenharmony_ci struct delayed_work work; 558c2ecf20Sopenharmony_ci u8 wait:1; 568c2ecf20Sopenharmony_ci u8 pwled:2; 578c2ecf20Sopenharmony_ci u8 submit_urb:1; 588c2ecf20Sopenharmony_ci struct urb *int_urb; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci /* IR device properties */ 618c2ecf20Sopenharmony_ci u64 rc_proto; 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_civoid tm6000_ir_wait(struct tm6000_core *dev, u8 state) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct tm6000_IR *ir = dev->ir; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (!dev->ir) 698c2ecf20Sopenharmony_ci return; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci dprintk(2, "%s: %i\n",__func__, ir->wait); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (state) 748c2ecf20Sopenharmony_ci ir->wait = 1; 758c2ecf20Sopenharmony_ci else 768c2ecf20Sopenharmony_ci ir->wait = 0; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic int tm6000_ir_config(struct tm6000_IR *ir) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct tm6000_core *dev = ir->dev; 828c2ecf20Sopenharmony_ci u32 pulse = 0, leader = 0; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci dprintk(2, "%s\n",__func__); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* 878c2ecf20Sopenharmony_ci * The IR decoder supports RC-5 or NEC, with a configurable timing. 888c2ecf20Sopenharmony_ci * The timing configuration there is not that accurate, as it uses 898c2ecf20Sopenharmony_ci * approximate values. The NEC spec mentions a 562.5 unit period, 908c2ecf20Sopenharmony_ci * and RC-5 uses a 888.8 period. 918c2ecf20Sopenharmony_ci * Currently, driver assumes a clock provided by a 12 MHz XTAL, but 928c2ecf20Sopenharmony_ci * a modprobe parameter can adjust it. 938c2ecf20Sopenharmony_ci * Adjustments are required for other timings. 948c2ecf20Sopenharmony_ci * It seems that the 900ms timing for NEC is used to detect a RC-5 958c2ecf20Sopenharmony_ci * IR, in order to discard such decoding 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci switch (ir->rc_proto) { 998c2ecf20Sopenharmony_ci case RC_PROTO_BIT_NEC: 1008c2ecf20Sopenharmony_ci leader = 900; /* ms */ 1018c2ecf20Sopenharmony_ci pulse = 700; /* ms - the actual value would be 562 */ 1028c2ecf20Sopenharmony_ci break; 1038c2ecf20Sopenharmony_ci default: 1048c2ecf20Sopenharmony_ci case RC_PROTO_BIT_RC5: 1058c2ecf20Sopenharmony_ci leader = 900; /* ms - from the NEC decoding */ 1068c2ecf20Sopenharmony_ci pulse = 1780; /* ms - The actual value would be 1776 */ 1078c2ecf20Sopenharmony_ci break; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci pulse = ir_clock_mhz * pulse; 1118c2ecf20Sopenharmony_ci leader = ir_clock_mhz * leader; 1128c2ecf20Sopenharmony_ci if (ir->rc_proto == RC_PROTO_BIT_NEC) 1138c2ecf20Sopenharmony_ci leader = leader | 0x8000; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci dprintk(2, "%s: %s, %d MHz, leader = 0x%04x, pulse = 0x%06x \n", 1168c2ecf20Sopenharmony_ci __func__, 1178c2ecf20Sopenharmony_ci (ir->rc_proto == RC_PROTO_BIT_NEC) ? "NEC" : "RC-5", 1188c2ecf20Sopenharmony_ci ir_clock_mhz, leader, pulse); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* Remote WAKEUP = enable, normal mode, from IR decoder output */ 1218c2ecf20Sopenharmony_ci tm6000_set_reg(dev, TM6010_REQ07_RE5_REMOTE_WAKEUP, 0xfe); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* Enable IR reception on non-busrt mode */ 1248c2ecf20Sopenharmony_ci tm6000_set_reg(dev, TM6010_REQ07_RD8_IR, 0x2f); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* IR_WKUP_SEL = Low byte in decoded IR data */ 1278c2ecf20Sopenharmony_ci tm6000_set_reg(dev, TM6010_REQ07_RDA_IR_WAKEUP_SEL, 0xff); 1288c2ecf20Sopenharmony_ci /* IR_WKU_ADD code */ 1298c2ecf20Sopenharmony_ci tm6000_set_reg(dev, TM6010_REQ07_RDB_IR_WAKEUP_ADD, 0xff); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci tm6000_set_reg(dev, TM6010_REQ07_RDC_IR_LEADER1, leader >> 8); 1328c2ecf20Sopenharmony_ci tm6000_set_reg(dev, TM6010_REQ07_RDD_IR_LEADER0, leader); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci tm6000_set_reg(dev, TM6010_REQ07_RDE_IR_PULSE_CNT1, pulse >> 8); 1358c2ecf20Sopenharmony_ci tm6000_set_reg(dev, TM6010_REQ07_RDF_IR_PULSE_CNT0, pulse); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (!ir->polling) 1388c2ecf20Sopenharmony_ci tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 0); 1398c2ecf20Sopenharmony_ci else 1408c2ecf20Sopenharmony_ci tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 1); 1418c2ecf20Sopenharmony_ci msleep(10); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* Shows that IR is working via the LED */ 1448c2ecf20Sopenharmony_ci tm6000_flash_led(dev, 0); 1458c2ecf20Sopenharmony_ci msleep(100); 1468c2ecf20Sopenharmony_ci tm6000_flash_led(dev, 1); 1478c2ecf20Sopenharmony_ci ir->pwled = 1; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic void tm6000_ir_keydown(struct tm6000_IR *ir, 1538c2ecf20Sopenharmony_ci const char *buf, unsigned int len) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci u8 device, command; 1568c2ecf20Sopenharmony_ci u32 scancode; 1578c2ecf20Sopenharmony_ci enum rc_proto protocol; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (len < 1) 1608c2ecf20Sopenharmony_ci return; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci command = buf[0]; 1638c2ecf20Sopenharmony_ci device = (len > 1 ? buf[1] : 0x0); 1648c2ecf20Sopenharmony_ci switch (ir->rc_proto) { 1658c2ecf20Sopenharmony_ci case RC_PROTO_BIT_RC5: 1668c2ecf20Sopenharmony_ci protocol = RC_PROTO_RC5; 1678c2ecf20Sopenharmony_ci scancode = RC_SCANCODE_RC5(device, command); 1688c2ecf20Sopenharmony_ci break; 1698c2ecf20Sopenharmony_ci case RC_PROTO_BIT_NEC: 1708c2ecf20Sopenharmony_ci protocol = RC_PROTO_NEC; 1718c2ecf20Sopenharmony_ci scancode = RC_SCANCODE_NEC(device, command); 1728c2ecf20Sopenharmony_ci break; 1738c2ecf20Sopenharmony_ci default: 1748c2ecf20Sopenharmony_ci protocol = RC_PROTO_OTHER; 1758c2ecf20Sopenharmony_ci scancode = RC_SCANCODE_OTHER(device << 8 | command); 1768c2ecf20Sopenharmony_ci break; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci dprintk(1, "%s, protocol: 0x%04x, scancode: 0x%08x\n", 1808c2ecf20Sopenharmony_ci __func__, protocol, scancode); 1818c2ecf20Sopenharmony_ci rc_keydown(ir->rc, protocol, scancode, 0); 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic void tm6000_ir_urb_received(struct urb *urb) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct tm6000_core *dev = urb->context; 1878c2ecf20Sopenharmony_ci struct tm6000_IR *ir = dev->ir; 1888c2ecf20Sopenharmony_ci char *buf; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci dprintk(2, "%s\n",__func__); 1918c2ecf20Sopenharmony_ci if (urb->status < 0 || urb->actual_length <= 0) { 1928c2ecf20Sopenharmony_ci printk(KERN_INFO "tm6000: IR URB failure: status: %i, length %i\n", 1938c2ecf20Sopenharmony_ci urb->status, urb->actual_length); 1948c2ecf20Sopenharmony_ci ir->submit_urb = 1; 1958c2ecf20Sopenharmony_ci schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY)); 1968c2ecf20Sopenharmony_ci return; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci buf = urb->transfer_buffer; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (ir_debug) 2018c2ecf20Sopenharmony_ci print_hex_dump(KERN_DEBUG, "tm6000: IR data: ", 2028c2ecf20Sopenharmony_ci DUMP_PREFIX_OFFSET,16, 1, 2038c2ecf20Sopenharmony_ci buf, urb->actual_length, false); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci tm6000_ir_keydown(ir, urb->transfer_buffer, urb->actual_length); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci usb_submit_urb(urb, GFP_ATOMIC); 2088c2ecf20Sopenharmony_ci /* 2098c2ecf20Sopenharmony_ci * Flash the led. We can't do it here, as it is running on IRQ context. 2108c2ecf20Sopenharmony_ci * So, use the scheduler to do it, in a few ms. 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_ci ir->pwled = 2; 2138c2ecf20Sopenharmony_ci schedule_delayed_work(&ir->work, msecs_to_jiffies(10)); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic void tm6000_ir_handle_key(struct work_struct *work) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct tm6000_IR *ir = container_of(work, struct tm6000_IR, work.work); 2198c2ecf20Sopenharmony_ci struct tm6000_core *dev = ir->dev; 2208c2ecf20Sopenharmony_ci int rc; 2218c2ecf20Sopenharmony_ci u8 buf[2]; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (ir->wait) 2248c2ecf20Sopenharmony_ci return; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci dprintk(3, "%s\n",__func__); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci rc = tm6000_read_write_usb(dev, USB_DIR_IN | 2298c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_DEVICE, 2308c2ecf20Sopenharmony_ci REQ_02_GET_IR_CODE, 0, 0, buf, 2); 2318c2ecf20Sopenharmony_ci if (rc < 0) 2328c2ecf20Sopenharmony_ci return; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* Check if something was read */ 2358c2ecf20Sopenharmony_ci if ((buf[0] & 0xff) == 0xff) { 2368c2ecf20Sopenharmony_ci if (!ir->pwled) { 2378c2ecf20Sopenharmony_ci tm6000_flash_led(dev, 1); 2388c2ecf20Sopenharmony_ci ir->pwled = 1; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci return; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci tm6000_ir_keydown(ir, buf, rc); 2448c2ecf20Sopenharmony_ci tm6000_flash_led(dev, 0); 2458c2ecf20Sopenharmony_ci ir->pwled = 0; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* Re-schedule polling */ 2488c2ecf20Sopenharmony_ci schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic void tm6000_ir_int_work(struct work_struct *work) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct tm6000_IR *ir = container_of(work, struct tm6000_IR, work.work); 2548c2ecf20Sopenharmony_ci struct tm6000_core *dev = ir->dev; 2558c2ecf20Sopenharmony_ci int rc; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci dprintk(3, "%s, submit_urb = %d, pwled = %d\n",__func__, ir->submit_urb, 2588c2ecf20Sopenharmony_ci ir->pwled); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (ir->submit_urb) { 2618c2ecf20Sopenharmony_ci dprintk(3, "Resubmit urb\n"); 2628c2ecf20Sopenharmony_ci tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 0); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci rc = usb_submit_urb(ir->int_urb, GFP_ATOMIC); 2658c2ecf20Sopenharmony_ci if (rc < 0) { 2668c2ecf20Sopenharmony_ci printk(KERN_ERR "tm6000: Can't submit an IR interrupt. Error %i\n", 2678c2ecf20Sopenharmony_ci rc); 2688c2ecf20Sopenharmony_ci /* Retry in 100 ms */ 2698c2ecf20Sopenharmony_ci schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY)); 2708c2ecf20Sopenharmony_ci return; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci ir->submit_urb = 0; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci /* Led is enabled only if USB submit doesn't fail */ 2768c2ecf20Sopenharmony_ci if (ir->pwled == 2) { 2778c2ecf20Sopenharmony_ci tm6000_flash_led(dev, 0); 2788c2ecf20Sopenharmony_ci ir->pwled = 0; 2798c2ecf20Sopenharmony_ci schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_INT_LED_DELAY)); 2808c2ecf20Sopenharmony_ci } else if (!ir->pwled) { 2818c2ecf20Sopenharmony_ci tm6000_flash_led(dev, 1); 2828c2ecf20Sopenharmony_ci ir->pwled = 1; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic int tm6000_ir_start(struct rc_dev *rc) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct tm6000_IR *ir = rc->priv; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci dprintk(2, "%s\n",__func__); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci schedule_delayed_work(&ir->work, 0); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic void tm6000_ir_stop(struct rc_dev *rc) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct tm6000_IR *ir = rc->priv; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci dprintk(2, "%s\n",__func__); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&ir->work); 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic int tm6000_ir_change_protocol(struct rc_dev *rc, u64 *rc_proto) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct tm6000_IR *ir = rc->priv; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (!ir) 3118c2ecf20Sopenharmony_ci return 0; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci dprintk(2, "%s\n",__func__); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci ir->rc_proto = *rc_proto; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci tm6000_ir_config(ir); 3188c2ecf20Sopenharmony_ci /* TODO */ 3198c2ecf20Sopenharmony_ci return 0; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic int __tm6000_ir_int_start(struct rc_dev *rc) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci struct tm6000_IR *ir = rc->priv; 3258c2ecf20Sopenharmony_ci struct tm6000_core *dev; 3268c2ecf20Sopenharmony_ci int pipe, size; 3278c2ecf20Sopenharmony_ci int err = -ENOMEM; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (!ir) 3308c2ecf20Sopenharmony_ci return -ENODEV; 3318c2ecf20Sopenharmony_ci dev = ir->dev; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci dprintk(2, "%s\n",__func__); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci ir->int_urb = usb_alloc_urb(0, GFP_ATOMIC); 3368c2ecf20Sopenharmony_ci if (!ir->int_urb) 3378c2ecf20Sopenharmony_ci return -ENOMEM; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci pipe = usb_rcvintpipe(dev->udev, 3408c2ecf20Sopenharmony_ci dev->int_in.endp->desc.bEndpointAddress 3418c2ecf20Sopenharmony_ci & USB_ENDPOINT_NUMBER_MASK); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe)); 3448c2ecf20Sopenharmony_ci dprintk(1, "IR max size: %d\n", size); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci ir->int_urb->transfer_buffer = kzalloc(size, GFP_ATOMIC); 3478c2ecf20Sopenharmony_ci if (!ir->int_urb->transfer_buffer) { 3488c2ecf20Sopenharmony_ci usb_free_urb(ir->int_urb); 3498c2ecf20Sopenharmony_ci return err; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci dprintk(1, "int interval: %d\n", dev->int_in.endp->desc.bInterval); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci usb_fill_int_urb(ir->int_urb, dev->udev, pipe, 3548c2ecf20Sopenharmony_ci ir->int_urb->transfer_buffer, size, 3558c2ecf20Sopenharmony_ci tm6000_ir_urb_received, dev, 3568c2ecf20Sopenharmony_ci dev->int_in.endp->desc.bInterval); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci ir->submit_urb = 1; 3598c2ecf20Sopenharmony_ci schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY)); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci return 0; 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic void __tm6000_ir_int_stop(struct rc_dev *rc) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci struct tm6000_IR *ir = rc->priv; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (!ir || !ir->int_urb) 3698c2ecf20Sopenharmony_ci return; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci dprintk(2, "%s\n",__func__); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci usb_kill_urb(ir->int_urb); 3748c2ecf20Sopenharmony_ci kfree(ir->int_urb->transfer_buffer); 3758c2ecf20Sopenharmony_ci usb_free_urb(ir->int_urb); 3768c2ecf20Sopenharmony_ci ir->int_urb = NULL; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ciint tm6000_ir_int_start(struct tm6000_core *dev) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci struct tm6000_IR *ir = dev->ir; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (!ir) 3848c2ecf20Sopenharmony_ci return 0; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci return __tm6000_ir_int_start(ir->rc); 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_civoid tm6000_ir_int_stop(struct tm6000_core *dev) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci struct tm6000_IR *ir = dev->ir; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci if (!ir || !ir->rc) 3948c2ecf20Sopenharmony_ci return; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci __tm6000_ir_int_stop(ir->rc); 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ciint tm6000_ir_init(struct tm6000_core *dev) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci struct tm6000_IR *ir; 4028c2ecf20Sopenharmony_ci struct rc_dev *rc; 4038c2ecf20Sopenharmony_ci int err = -ENOMEM; 4048c2ecf20Sopenharmony_ci u64 rc_proto; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (!enable_ir) 4078c2ecf20Sopenharmony_ci return -ENODEV; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci if (!dev->caps.has_remote) 4108c2ecf20Sopenharmony_ci return 0; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (!dev->ir_codes) 4138c2ecf20Sopenharmony_ci return 0; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci ir = kzalloc(sizeof(*ir), GFP_ATOMIC); 4168c2ecf20Sopenharmony_ci rc = rc_allocate_device(RC_DRIVER_SCANCODE); 4178c2ecf20Sopenharmony_ci if (!ir || !rc) 4188c2ecf20Sopenharmony_ci goto out; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci dprintk(2, "%s\n", __func__); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci /* record handles to ourself */ 4238c2ecf20Sopenharmony_ci ir->dev = dev; 4248c2ecf20Sopenharmony_ci dev->ir = ir; 4258c2ecf20Sopenharmony_ci ir->rc = rc; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* input setup */ 4288c2ecf20Sopenharmony_ci rc->allowed_protocols = RC_PROTO_BIT_RC5 | RC_PROTO_BIT_NEC; 4298c2ecf20Sopenharmony_ci /* Needed, in order to support NEC remotes with 24 or 32 bits */ 4308c2ecf20Sopenharmony_ci rc->scancode_mask = 0xffff; 4318c2ecf20Sopenharmony_ci rc->priv = ir; 4328c2ecf20Sopenharmony_ci rc->change_protocol = tm6000_ir_change_protocol; 4338c2ecf20Sopenharmony_ci if (dev->int_in.endp) { 4348c2ecf20Sopenharmony_ci rc->open = __tm6000_ir_int_start; 4358c2ecf20Sopenharmony_ci rc->close = __tm6000_ir_int_stop; 4368c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&ir->work, tm6000_ir_int_work); 4378c2ecf20Sopenharmony_ci } else { 4388c2ecf20Sopenharmony_ci rc->open = tm6000_ir_start; 4398c2ecf20Sopenharmony_ci rc->close = tm6000_ir_stop; 4408c2ecf20Sopenharmony_ci ir->polling = 50; 4418c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&ir->work, tm6000_ir_handle_key); 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci snprintf(ir->name, sizeof(ir->name), "tm5600/60x0 IR (%s)", 4458c2ecf20Sopenharmony_ci dev->name); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); 4488c2ecf20Sopenharmony_ci strlcat(ir->phys, "/input0", sizeof(ir->phys)); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci rc_proto = RC_PROTO_BIT_UNKNOWN; 4518c2ecf20Sopenharmony_ci tm6000_ir_change_protocol(rc, &rc_proto); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci rc->device_name = ir->name; 4548c2ecf20Sopenharmony_ci rc->input_phys = ir->phys; 4558c2ecf20Sopenharmony_ci rc->input_id.bustype = BUS_USB; 4568c2ecf20Sopenharmony_ci rc->input_id.version = 1; 4578c2ecf20Sopenharmony_ci rc->input_id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); 4588c2ecf20Sopenharmony_ci rc->input_id.product = le16_to_cpu(dev->udev->descriptor.idProduct); 4598c2ecf20Sopenharmony_ci rc->map_name = dev->ir_codes; 4608c2ecf20Sopenharmony_ci rc->driver_name = "tm6000"; 4618c2ecf20Sopenharmony_ci rc->dev.parent = &dev->udev->dev; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* ir register */ 4648c2ecf20Sopenharmony_ci err = rc_register_device(rc); 4658c2ecf20Sopenharmony_ci if (err) 4668c2ecf20Sopenharmony_ci goto out; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci return 0; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ciout: 4718c2ecf20Sopenharmony_ci dev->ir = NULL; 4728c2ecf20Sopenharmony_ci rc_free_device(rc); 4738c2ecf20Sopenharmony_ci kfree(ir); 4748c2ecf20Sopenharmony_ci return err; 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ciint tm6000_ir_fini(struct tm6000_core *dev) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci struct tm6000_IR *ir = dev->ir; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci /* skip detach on non attached board */ 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (!ir) 4848c2ecf20Sopenharmony_ci return 0; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci dprintk(2, "%s\n",__func__); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci if (!ir->polling) 4898c2ecf20Sopenharmony_ci __tm6000_ir_int_stop(ir->rc); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci tm6000_ir_stop(ir->rc); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci /* Turn off the led */ 4948c2ecf20Sopenharmony_ci tm6000_flash_led(dev, 0); 4958c2ecf20Sopenharmony_ci ir->pwled = 0; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci rc_unregister_device(ir->rc); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci kfree(ir); 5008c2ecf20Sopenharmony_ci dev->ir = NULL; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci return 0; 5038c2ecf20Sopenharmony_ci} 504