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