18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for the Conexant CX23885/7/8 PCIe bridge 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Infrared remote control input device 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Most of this file is 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * However, the cx23885_input_{init,fini} functions contained herein are 128c2ecf20Sopenharmony_ci * derived from Linux kernel files linux/media/video/.../...-input.c marked as: 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Copyright (C) 2008 <srinivasa.deevi at conexant dot com> 158c2ecf20Sopenharmony_ci * Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it> 168c2ecf20Sopenharmony_ci * Markus Rechberger <mrechberger@gmail.com> 178c2ecf20Sopenharmony_ci * Mauro Carvalho Chehab <mchehab@kernel.org> 188c2ecf20Sopenharmony_ci * Sascha Sommer <saschasommer@freenet.de> 198c2ecf20Sopenharmony_ci * Copyright (C) 2004, 2005 Chris Pascoe 208c2ecf20Sopenharmony_ci * Copyright (C) 2003, 2004 Gerd Knorr 218c2ecf20Sopenharmony_ci * Copyright (C) 2003 Pavel Machek 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "cx23885.h" 258c2ecf20Sopenharmony_ci#include "cx23885-input.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <linux/slab.h> 288c2ecf20Sopenharmony_ci#include <media/rc-core.h> 298c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define MODULE_NAME "cx23885" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic void cx23885_input_process_measurements(struct cx23885_dev *dev, 348c2ecf20Sopenharmony_ci bool overrun) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct cx23885_kernel_ir *kernel_ir = dev->kernel_ir; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci ssize_t num; 398c2ecf20Sopenharmony_ci int count, i; 408c2ecf20Sopenharmony_ci bool handle = false; 418c2ecf20Sopenharmony_ci struct ir_raw_event ir_core_event[64]; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci do { 448c2ecf20Sopenharmony_ci num = 0; 458c2ecf20Sopenharmony_ci v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) ir_core_event, 468c2ecf20Sopenharmony_ci sizeof(ir_core_event), &num); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci count = num / sizeof(struct ir_raw_event); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 518c2ecf20Sopenharmony_ci ir_raw_event_store(kernel_ir->rc, 528c2ecf20Sopenharmony_ci &ir_core_event[i]); 538c2ecf20Sopenharmony_ci handle = true; 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci } while (num != 0); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci if (overrun) 588c2ecf20Sopenharmony_ci ir_raw_event_reset(kernel_ir->rc); 598c2ecf20Sopenharmony_ci else if (handle) 608c2ecf20Sopenharmony_ci ir_raw_event_handle(kernel_ir->rc); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_civoid cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct v4l2_subdev_ir_parameters params; 668c2ecf20Sopenharmony_ci int overrun, data_available; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (dev->sd_ir == NULL || events == 0) 698c2ecf20Sopenharmony_ci return; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci switch (dev->board) { 728c2ecf20Sopenharmony_ci case CX23885_BOARD_HAUPPAUGE_HVR1270: 738c2ecf20Sopenharmony_ci case CX23885_BOARD_HAUPPAUGE_HVR1850: 748c2ecf20Sopenharmony_ci case CX23885_BOARD_HAUPPAUGE_HVR1290: 758c2ecf20Sopenharmony_ci case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: 768c2ecf20Sopenharmony_ci case CX23885_BOARD_TEVII_S470: 778c2ecf20Sopenharmony_ci case CX23885_BOARD_HAUPPAUGE_HVR1250: 788c2ecf20Sopenharmony_ci case CX23885_BOARD_MYGICA_X8507: 798c2ecf20Sopenharmony_ci case CX23885_BOARD_TBS_6980: 808c2ecf20Sopenharmony_ci case CX23885_BOARD_TBS_6981: 818c2ecf20Sopenharmony_ci case CX23885_BOARD_DVBSKY_T9580: 828c2ecf20Sopenharmony_ci case CX23885_BOARD_DVBSKY_T980C: 838c2ecf20Sopenharmony_ci case CX23885_BOARD_DVBSKY_S950C: 848c2ecf20Sopenharmony_ci case CX23885_BOARD_TT_CT2_4500_CI: 858c2ecf20Sopenharmony_ci case CX23885_BOARD_DVBSKY_S950: 868c2ecf20Sopenharmony_ci case CX23885_BOARD_DVBSKY_S952: 878c2ecf20Sopenharmony_ci case CX23885_BOARD_DVBSKY_T982: 888c2ecf20Sopenharmony_ci case CX23885_BOARD_HAUPPAUGE_HVR1265_K4: 898c2ecf20Sopenharmony_ci /* 908c2ecf20Sopenharmony_ci * The only boards we handle right now. However other boards 918c2ecf20Sopenharmony_ci * using the CX2388x integrated IR controller should be similar 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_ci break; 948c2ecf20Sopenharmony_ci default: 958c2ecf20Sopenharmony_ci return; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci overrun = events & (V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN | 998c2ecf20Sopenharmony_ci V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci data_available = events & (V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED | 1028c2ecf20Sopenharmony_ci V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (overrun) { 1058c2ecf20Sopenharmony_ci /* If there was a FIFO overrun, stop the device */ 1068c2ecf20Sopenharmony_ci v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms); 1078c2ecf20Sopenharmony_ci params.enable = false; 1088c2ecf20Sopenharmony_ci /* Mitigate race with cx23885_input_ir_stop() */ 1098c2ecf20Sopenharmony_ci params.shutdown = atomic_read(&dev->ir_input_stopping); 1108c2ecf20Sopenharmony_ci v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms); 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (data_available) 1148c2ecf20Sopenharmony_ci cx23885_input_process_measurements(dev, overrun); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (overrun) { 1178c2ecf20Sopenharmony_ci /* If there was a FIFO overrun, clear & restart the device */ 1188c2ecf20Sopenharmony_ci params.enable = true; 1198c2ecf20Sopenharmony_ci /* Mitigate race with cx23885_input_ir_stop() */ 1208c2ecf20Sopenharmony_ci params.shutdown = atomic_read(&dev->ir_input_stopping); 1218c2ecf20Sopenharmony_ci v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms); 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic int cx23885_input_ir_start(struct cx23885_dev *dev) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct v4l2_subdev_ir_parameters params; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (dev->sd_ir == NULL) 1308c2ecf20Sopenharmony_ci return -ENODEV; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci atomic_set(&dev->ir_input_stopping, 0); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms); 1358c2ecf20Sopenharmony_ci switch (dev->board) { 1368c2ecf20Sopenharmony_ci case CX23885_BOARD_HAUPPAUGE_HVR1270: 1378c2ecf20Sopenharmony_ci case CX23885_BOARD_HAUPPAUGE_HVR1850: 1388c2ecf20Sopenharmony_ci case CX23885_BOARD_HAUPPAUGE_HVR1290: 1398c2ecf20Sopenharmony_ci case CX23885_BOARD_HAUPPAUGE_HVR1250: 1408c2ecf20Sopenharmony_ci case CX23885_BOARD_MYGICA_X8507: 1418c2ecf20Sopenharmony_ci case CX23885_BOARD_DVBSKY_T9580: 1428c2ecf20Sopenharmony_ci case CX23885_BOARD_DVBSKY_T980C: 1438c2ecf20Sopenharmony_ci case CX23885_BOARD_DVBSKY_S950C: 1448c2ecf20Sopenharmony_ci case CX23885_BOARD_TT_CT2_4500_CI: 1458c2ecf20Sopenharmony_ci case CX23885_BOARD_DVBSKY_S950: 1468c2ecf20Sopenharmony_ci case CX23885_BOARD_DVBSKY_S952: 1478c2ecf20Sopenharmony_ci case CX23885_BOARD_DVBSKY_T982: 1488c2ecf20Sopenharmony_ci case CX23885_BOARD_HAUPPAUGE_HVR1265_K4: 1498c2ecf20Sopenharmony_ci /* 1508c2ecf20Sopenharmony_ci * The IR controller on this board only returns pulse widths. 1518c2ecf20Sopenharmony_ci * Any other mode setting will fail to set up the device. 1528c2ecf20Sopenharmony_ci */ 1538c2ecf20Sopenharmony_ci params.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; 1548c2ecf20Sopenharmony_ci params.enable = true; 1558c2ecf20Sopenharmony_ci params.interrupt_enable = true; 1568c2ecf20Sopenharmony_ci params.shutdown = false; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* Setup for baseband compatible with both RC-5 and RC-6A */ 1598c2ecf20Sopenharmony_ci params.modulation = false; 1608c2ecf20Sopenharmony_ci /* RC-5: 2,222,222 ns = 1/36 kHz * 32 cycles * 2 marks * 1.25*/ 1618c2ecf20Sopenharmony_ci /* RC-6A: 3,333,333 ns = 1/36 kHz * 16 cycles * 6 marks * 1.25*/ 1628c2ecf20Sopenharmony_ci params.max_pulse_width = 3333333; /* ns */ 1638c2ecf20Sopenharmony_ci /* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */ 1648c2ecf20Sopenharmony_ci /* RC-6A: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */ 1658c2ecf20Sopenharmony_ci params.noise_filter_min_width = 333333; /* ns */ 1668c2ecf20Sopenharmony_ci /* 1678c2ecf20Sopenharmony_ci * This board has inverted receive sense: 1688c2ecf20Sopenharmony_ci * mark is received as low logic level; 1698c2ecf20Sopenharmony_ci * falling edges are detected as rising edges; etc. 1708c2ecf20Sopenharmony_ci */ 1718c2ecf20Sopenharmony_ci params.invert_level = true; 1728c2ecf20Sopenharmony_ci break; 1738c2ecf20Sopenharmony_ci case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: 1748c2ecf20Sopenharmony_ci case CX23885_BOARD_TEVII_S470: 1758c2ecf20Sopenharmony_ci case CX23885_BOARD_TBS_6980: 1768c2ecf20Sopenharmony_ci case CX23885_BOARD_TBS_6981: 1778c2ecf20Sopenharmony_ci /* 1788c2ecf20Sopenharmony_ci * The IR controller on this board only returns pulse widths. 1798c2ecf20Sopenharmony_ci * Any other mode setting will fail to set up the device. 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_ci params.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; 1828c2ecf20Sopenharmony_ci params.enable = true; 1838c2ecf20Sopenharmony_ci params.interrupt_enable = true; 1848c2ecf20Sopenharmony_ci params.shutdown = false; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* Setup for a standard NEC protocol */ 1878c2ecf20Sopenharmony_ci params.carrier_freq = 37917; /* Hz, 455 kHz/12 for NEC */ 1888c2ecf20Sopenharmony_ci params.carrier_range_lower = 33000; /* Hz */ 1898c2ecf20Sopenharmony_ci params.carrier_range_upper = 43000; /* Hz */ 1908c2ecf20Sopenharmony_ci params.duty_cycle = 33; /* percent, 33 percent for NEC */ 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* 1938c2ecf20Sopenharmony_ci * NEC max pulse width: (64/3)/(455 kHz/12) * 16 nec_units 1948c2ecf20Sopenharmony_ci * (64/3)/(455 kHz/12) * 16 nec_units * 1.375 = 12378022 ns 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_ci params.max_pulse_width = 12378022; /* ns */ 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* 1998c2ecf20Sopenharmony_ci * NEC noise filter min width: (64/3)/(455 kHz/12) * 1 nec_unit 2008c2ecf20Sopenharmony_ci * (64/3)/(455 kHz/12) * 1 nec_units * 0.625 = 351648 ns 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_ci params.noise_filter_min_width = 351648; /* ns */ 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci params.modulation = false; 2058c2ecf20Sopenharmony_ci params.invert_level = true; 2068c2ecf20Sopenharmony_ci break; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms); 2098c2ecf20Sopenharmony_ci return 0; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic int cx23885_input_ir_open(struct rc_dev *rc) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct cx23885_kernel_ir *kernel_ir = rc->priv; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (kernel_ir->cx == NULL) 2178c2ecf20Sopenharmony_ci return -ENODEV; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci return cx23885_input_ir_start(kernel_ir->cx); 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic void cx23885_input_ir_stop(struct cx23885_dev *dev) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct v4l2_subdev_ir_parameters params; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (dev->sd_ir == NULL) 2278c2ecf20Sopenharmony_ci return; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci /* 2308c2ecf20Sopenharmony_ci * Stop the sd_ir subdevice from generating notifications and 2318c2ecf20Sopenharmony_ci * scheduling work. 2328c2ecf20Sopenharmony_ci * It is shutdown this way in order to mitigate a race with 2338c2ecf20Sopenharmony_ci * cx23885_input_rx_work_handler() in the overrun case, which could 2348c2ecf20Sopenharmony_ci * re-enable the subdevice. 2358c2ecf20Sopenharmony_ci */ 2368c2ecf20Sopenharmony_ci atomic_set(&dev->ir_input_stopping, 1); 2378c2ecf20Sopenharmony_ci v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms); 2388c2ecf20Sopenharmony_ci while (params.shutdown == false) { 2398c2ecf20Sopenharmony_ci params.enable = false; 2408c2ecf20Sopenharmony_ci params.interrupt_enable = false; 2418c2ecf20Sopenharmony_ci params.shutdown = true; 2428c2ecf20Sopenharmony_ci v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms); 2438c2ecf20Sopenharmony_ci v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms); 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci flush_work(&dev->cx25840_work); 2468c2ecf20Sopenharmony_ci flush_work(&dev->ir_rx_work); 2478c2ecf20Sopenharmony_ci flush_work(&dev->ir_tx_work); 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic void cx23885_input_ir_close(struct rc_dev *rc) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci struct cx23885_kernel_ir *kernel_ir = rc->priv; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (kernel_ir->cx != NULL) 2558c2ecf20Sopenharmony_ci cx23885_input_ir_stop(kernel_ir->cx); 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ciint cx23885_input_init(struct cx23885_dev *dev) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct cx23885_kernel_ir *kernel_ir; 2618c2ecf20Sopenharmony_ci struct rc_dev *rc; 2628c2ecf20Sopenharmony_ci char *rc_map; 2638c2ecf20Sopenharmony_ci u64 allowed_protos; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci int ret; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci /* 2688c2ecf20Sopenharmony_ci * If the IR device (hardware registers, chip, GPIO lines, etc.) isn't 2698c2ecf20Sopenharmony_ci * encapsulated in a v4l2_subdev, then I'm not going to deal with it. 2708c2ecf20Sopenharmony_ci */ 2718c2ecf20Sopenharmony_ci if (dev->sd_ir == NULL) 2728c2ecf20Sopenharmony_ci return -ENODEV; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci switch (dev->board) { 2758c2ecf20Sopenharmony_ci case CX23885_BOARD_HAUPPAUGE_HVR1270: 2768c2ecf20Sopenharmony_ci case CX23885_BOARD_HAUPPAUGE_HVR1850: 2778c2ecf20Sopenharmony_ci case CX23885_BOARD_HAUPPAUGE_HVR1290: 2788c2ecf20Sopenharmony_ci case CX23885_BOARD_HAUPPAUGE_HVR1250: 2798c2ecf20Sopenharmony_ci case CX23885_BOARD_HAUPPAUGE_HVR1265_K4: 2808c2ecf20Sopenharmony_ci /* Integrated CX2388[58] IR controller */ 2818c2ecf20Sopenharmony_ci allowed_protos = RC_PROTO_BIT_ALL_IR_DECODER; 2828c2ecf20Sopenharmony_ci /* The grey Hauppauge RC-5 remote */ 2838c2ecf20Sopenharmony_ci rc_map = RC_MAP_HAUPPAUGE; 2848c2ecf20Sopenharmony_ci break; 2858c2ecf20Sopenharmony_ci case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: 2868c2ecf20Sopenharmony_ci /* Integrated CX23885 IR controller */ 2878c2ecf20Sopenharmony_ci allowed_protos = RC_PROTO_BIT_ALL_IR_DECODER; 2888c2ecf20Sopenharmony_ci /* The grey Terratec remote with orange buttons */ 2898c2ecf20Sopenharmony_ci rc_map = RC_MAP_NEC_TERRATEC_CINERGY_XS; 2908c2ecf20Sopenharmony_ci break; 2918c2ecf20Sopenharmony_ci case CX23885_BOARD_TEVII_S470: 2928c2ecf20Sopenharmony_ci /* Integrated CX23885 IR controller */ 2938c2ecf20Sopenharmony_ci allowed_protos = RC_PROTO_BIT_ALL_IR_DECODER; 2948c2ecf20Sopenharmony_ci /* A guess at the remote */ 2958c2ecf20Sopenharmony_ci rc_map = RC_MAP_TEVII_NEC; 2968c2ecf20Sopenharmony_ci break; 2978c2ecf20Sopenharmony_ci case CX23885_BOARD_MYGICA_X8507: 2988c2ecf20Sopenharmony_ci /* Integrated CX23885 IR controller */ 2998c2ecf20Sopenharmony_ci allowed_protos = RC_PROTO_BIT_ALL_IR_DECODER; 3008c2ecf20Sopenharmony_ci /* A guess at the remote */ 3018c2ecf20Sopenharmony_ci rc_map = RC_MAP_TOTAL_MEDIA_IN_HAND_02; 3028c2ecf20Sopenharmony_ci break; 3038c2ecf20Sopenharmony_ci case CX23885_BOARD_TBS_6980: 3048c2ecf20Sopenharmony_ci case CX23885_BOARD_TBS_6981: 3058c2ecf20Sopenharmony_ci /* Integrated CX23885 IR controller */ 3068c2ecf20Sopenharmony_ci allowed_protos = RC_PROTO_BIT_ALL_IR_DECODER; 3078c2ecf20Sopenharmony_ci /* A guess at the remote */ 3088c2ecf20Sopenharmony_ci rc_map = RC_MAP_TBS_NEC; 3098c2ecf20Sopenharmony_ci break; 3108c2ecf20Sopenharmony_ci case CX23885_BOARD_DVBSKY_T9580: 3118c2ecf20Sopenharmony_ci case CX23885_BOARD_DVBSKY_T980C: 3128c2ecf20Sopenharmony_ci case CX23885_BOARD_DVBSKY_S950C: 3138c2ecf20Sopenharmony_ci case CX23885_BOARD_DVBSKY_S950: 3148c2ecf20Sopenharmony_ci case CX23885_BOARD_DVBSKY_S952: 3158c2ecf20Sopenharmony_ci case CX23885_BOARD_DVBSKY_T982: 3168c2ecf20Sopenharmony_ci /* Integrated CX23885 IR controller */ 3178c2ecf20Sopenharmony_ci allowed_protos = RC_PROTO_BIT_ALL_IR_DECODER; 3188c2ecf20Sopenharmony_ci rc_map = RC_MAP_DVBSKY; 3198c2ecf20Sopenharmony_ci break; 3208c2ecf20Sopenharmony_ci case CX23885_BOARD_TT_CT2_4500_CI: 3218c2ecf20Sopenharmony_ci /* Integrated CX23885 IR controller */ 3228c2ecf20Sopenharmony_ci allowed_protos = RC_PROTO_BIT_ALL_IR_DECODER; 3238c2ecf20Sopenharmony_ci rc_map = RC_MAP_TT_1500; 3248c2ecf20Sopenharmony_ci break; 3258c2ecf20Sopenharmony_ci default: 3268c2ecf20Sopenharmony_ci return -ENODEV; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* cx23885 board instance kernel IR state */ 3308c2ecf20Sopenharmony_ci kernel_ir = kzalloc(sizeof(struct cx23885_kernel_ir), GFP_KERNEL); 3318c2ecf20Sopenharmony_ci if (kernel_ir == NULL) 3328c2ecf20Sopenharmony_ci return -ENOMEM; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci kernel_ir->cx = dev; 3358c2ecf20Sopenharmony_ci kernel_ir->name = kasprintf(GFP_KERNEL, "cx23885 IR (%s)", 3368c2ecf20Sopenharmony_ci cx23885_boards[dev->board].name); 3378c2ecf20Sopenharmony_ci if (!kernel_ir->name) { 3388c2ecf20Sopenharmony_ci ret = -ENOMEM; 3398c2ecf20Sopenharmony_ci goto err_out_free; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci kernel_ir->phys = kasprintf(GFP_KERNEL, "pci-%s/ir0", 3438c2ecf20Sopenharmony_ci pci_name(dev->pci)); 3448c2ecf20Sopenharmony_ci if (!kernel_ir->phys) { 3458c2ecf20Sopenharmony_ci ret = -ENOMEM; 3468c2ecf20Sopenharmony_ci goto err_out_free_name; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* input device */ 3508c2ecf20Sopenharmony_ci rc = rc_allocate_device(RC_DRIVER_IR_RAW); 3518c2ecf20Sopenharmony_ci if (!rc) { 3528c2ecf20Sopenharmony_ci ret = -ENOMEM; 3538c2ecf20Sopenharmony_ci goto err_out_free_phys; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci kernel_ir->rc = rc; 3578c2ecf20Sopenharmony_ci rc->device_name = kernel_ir->name; 3588c2ecf20Sopenharmony_ci rc->input_phys = kernel_ir->phys; 3598c2ecf20Sopenharmony_ci rc->input_id.bustype = BUS_PCI; 3608c2ecf20Sopenharmony_ci rc->input_id.version = 1; 3618c2ecf20Sopenharmony_ci if (dev->pci->subsystem_vendor) { 3628c2ecf20Sopenharmony_ci rc->input_id.vendor = dev->pci->subsystem_vendor; 3638c2ecf20Sopenharmony_ci rc->input_id.product = dev->pci->subsystem_device; 3648c2ecf20Sopenharmony_ci } else { 3658c2ecf20Sopenharmony_ci rc->input_id.vendor = dev->pci->vendor; 3668c2ecf20Sopenharmony_ci rc->input_id.product = dev->pci->device; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci rc->dev.parent = &dev->pci->dev; 3698c2ecf20Sopenharmony_ci rc->allowed_protocols = allowed_protos; 3708c2ecf20Sopenharmony_ci rc->priv = kernel_ir; 3718c2ecf20Sopenharmony_ci rc->open = cx23885_input_ir_open; 3728c2ecf20Sopenharmony_ci rc->close = cx23885_input_ir_close; 3738c2ecf20Sopenharmony_ci rc->map_name = rc_map; 3748c2ecf20Sopenharmony_ci rc->driver_name = MODULE_NAME; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci /* Go */ 3778c2ecf20Sopenharmony_ci dev->kernel_ir = kernel_ir; 3788c2ecf20Sopenharmony_ci ret = rc_register_device(rc); 3798c2ecf20Sopenharmony_ci if (ret) 3808c2ecf20Sopenharmony_ci goto err_out_stop; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci return 0; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cierr_out_stop: 3858c2ecf20Sopenharmony_ci cx23885_input_ir_stop(dev); 3868c2ecf20Sopenharmony_ci dev->kernel_ir = NULL; 3878c2ecf20Sopenharmony_ci rc_free_device(rc); 3888c2ecf20Sopenharmony_cierr_out_free_phys: 3898c2ecf20Sopenharmony_ci kfree(kernel_ir->phys); 3908c2ecf20Sopenharmony_cierr_out_free_name: 3918c2ecf20Sopenharmony_ci kfree(kernel_ir->name); 3928c2ecf20Sopenharmony_cierr_out_free: 3938c2ecf20Sopenharmony_ci kfree(kernel_ir); 3948c2ecf20Sopenharmony_ci return ret; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_civoid cx23885_input_fini(struct cx23885_dev *dev) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci /* Always stop the IR hardware from generating interrupts */ 4008c2ecf20Sopenharmony_ci cx23885_input_ir_stop(dev); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (dev->kernel_ir == NULL) 4038c2ecf20Sopenharmony_ci return; 4048c2ecf20Sopenharmony_ci rc_unregister_device(dev->kernel_ir->rc); 4058c2ecf20Sopenharmony_ci kfree(dev->kernel_ir->phys); 4068c2ecf20Sopenharmony_ci kfree(dev->kernel_ir->name); 4078c2ecf20Sopenharmony_ci kfree(dev->kernel_ir); 4088c2ecf20Sopenharmony_ci dev->kernel_ir = NULL; 4098c2ecf20Sopenharmony_ci} 410