18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* ir-sharp-decoder.c - handle Sharp IR Pulse/Space protocol 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2013-2014 Imagination Technologies Ltd. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Based on NEC decoder: 78c2ecf20Sopenharmony_ci * Copyright (C) 2010 by Mauro Carvalho Chehab 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/bitrev.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include "rc-core-priv.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define SHARP_NBITS 15 158c2ecf20Sopenharmony_ci#define SHARP_UNIT 40 /* us */ 168c2ecf20Sopenharmony_ci#define SHARP_BIT_PULSE (8 * SHARP_UNIT) /* 320us */ 178c2ecf20Sopenharmony_ci#define SHARP_BIT_0_PERIOD (25 * SHARP_UNIT) /* 1ms (680us space) */ 188c2ecf20Sopenharmony_ci#define SHARP_BIT_1_PERIOD (50 * SHARP_UNIT) /* 2ms (1680us space) */ 198c2ecf20Sopenharmony_ci#define SHARP_BIT_0_SPACE (17 * SHARP_UNIT) /* 680us space */ 208c2ecf20Sopenharmony_ci#define SHARP_BIT_1_SPACE (42 * SHARP_UNIT) /* 1680us space */ 218c2ecf20Sopenharmony_ci#define SHARP_ECHO_SPACE (1000 * SHARP_UNIT) /* 40 ms */ 228c2ecf20Sopenharmony_ci#define SHARP_TRAILER_SPACE (125 * SHARP_UNIT) /* 5 ms (even longer) */ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cienum sharp_state { 258c2ecf20Sopenharmony_ci STATE_INACTIVE, 268c2ecf20Sopenharmony_ci STATE_BIT_PULSE, 278c2ecf20Sopenharmony_ci STATE_BIT_SPACE, 288c2ecf20Sopenharmony_ci STATE_TRAILER_PULSE, 298c2ecf20Sopenharmony_ci STATE_ECHO_SPACE, 308c2ecf20Sopenharmony_ci STATE_TRAILER_SPACE, 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/** 348c2ecf20Sopenharmony_ci * ir_sharp_decode() - Decode one Sharp pulse or space 358c2ecf20Sopenharmony_ci * @dev: the struct rc_dev descriptor of the device 368c2ecf20Sopenharmony_ci * @ev: the struct ir_raw_event descriptor of the pulse/space 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * This function returns -EINVAL if the pulse violates the state machine 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_cistatic int ir_sharp_decode(struct rc_dev *dev, struct ir_raw_event ev) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct sharp_dec *data = &dev->raw->sharp; 438c2ecf20Sopenharmony_ci u32 msg, echo, address, command, scancode; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if (!is_timing_event(ev)) { 468c2ecf20Sopenharmony_ci if (ev.reset) 478c2ecf20Sopenharmony_ci data->state = STATE_INACTIVE; 488c2ecf20Sopenharmony_ci return 0; 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "Sharp decode started at state %d (%uus %s)\n", 528c2ecf20Sopenharmony_ci data->state, ev.duration, TO_STR(ev.pulse)); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci switch (data->state) { 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci case STATE_INACTIVE: 578c2ecf20Sopenharmony_ci if (!ev.pulse) 588c2ecf20Sopenharmony_ci break; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci if (!eq_margin(ev.duration, SHARP_BIT_PULSE, 618c2ecf20Sopenharmony_ci SHARP_BIT_PULSE / 2)) 628c2ecf20Sopenharmony_ci break; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci data->count = 0; 658c2ecf20Sopenharmony_ci data->pulse_len = ev.duration; 668c2ecf20Sopenharmony_ci data->state = STATE_BIT_SPACE; 678c2ecf20Sopenharmony_ci return 0; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci case STATE_BIT_PULSE: 708c2ecf20Sopenharmony_ci if (!ev.pulse) 718c2ecf20Sopenharmony_ci break; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (!eq_margin(ev.duration, SHARP_BIT_PULSE, 748c2ecf20Sopenharmony_ci SHARP_BIT_PULSE / 2)) 758c2ecf20Sopenharmony_ci break; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci data->pulse_len = ev.duration; 788c2ecf20Sopenharmony_ci data->state = STATE_BIT_SPACE; 798c2ecf20Sopenharmony_ci return 0; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci case STATE_BIT_SPACE: 828c2ecf20Sopenharmony_ci if (ev.pulse) 838c2ecf20Sopenharmony_ci break; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci data->bits <<= 1; 868c2ecf20Sopenharmony_ci if (eq_margin(data->pulse_len + ev.duration, SHARP_BIT_1_PERIOD, 878c2ecf20Sopenharmony_ci SHARP_BIT_PULSE * 2)) 888c2ecf20Sopenharmony_ci data->bits |= 1; 898c2ecf20Sopenharmony_ci else if (!eq_margin(data->pulse_len + ev.duration, 908c2ecf20Sopenharmony_ci SHARP_BIT_0_PERIOD, SHARP_BIT_PULSE * 2)) 918c2ecf20Sopenharmony_ci break; 928c2ecf20Sopenharmony_ci data->count++; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (data->count == SHARP_NBITS || 958c2ecf20Sopenharmony_ci data->count == SHARP_NBITS * 2) 968c2ecf20Sopenharmony_ci data->state = STATE_TRAILER_PULSE; 978c2ecf20Sopenharmony_ci else 988c2ecf20Sopenharmony_ci data->state = STATE_BIT_PULSE; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci return 0; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci case STATE_TRAILER_PULSE: 1038c2ecf20Sopenharmony_ci if (!ev.pulse) 1048c2ecf20Sopenharmony_ci break; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (!eq_margin(ev.duration, SHARP_BIT_PULSE, 1078c2ecf20Sopenharmony_ci SHARP_BIT_PULSE / 2)) 1088c2ecf20Sopenharmony_ci break; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (data->count == SHARP_NBITS) { 1118c2ecf20Sopenharmony_ci /* exp,chk bits should be 1,0 */ 1128c2ecf20Sopenharmony_ci if ((data->bits & 0x3) != 0x2 && 1138c2ecf20Sopenharmony_ci /* DENON variant, both chk bits 0 */ 1148c2ecf20Sopenharmony_ci (data->bits & 0x3) != 0x0) 1158c2ecf20Sopenharmony_ci break; 1168c2ecf20Sopenharmony_ci data->state = STATE_ECHO_SPACE; 1178c2ecf20Sopenharmony_ci } else { 1188c2ecf20Sopenharmony_ci data->state = STATE_TRAILER_SPACE; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci return 0; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci case STATE_ECHO_SPACE: 1238c2ecf20Sopenharmony_ci if (ev.pulse) 1248c2ecf20Sopenharmony_ci break; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (!eq_margin(ev.duration, SHARP_ECHO_SPACE, 1278c2ecf20Sopenharmony_ci SHARP_ECHO_SPACE / 4)) 1288c2ecf20Sopenharmony_ci break; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci data->state = STATE_BIT_PULSE; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci return 0; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci case STATE_TRAILER_SPACE: 1358c2ecf20Sopenharmony_ci if (ev.pulse) 1368c2ecf20Sopenharmony_ci break; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (!geq_margin(ev.duration, SHARP_TRAILER_SPACE, 1398c2ecf20Sopenharmony_ci SHARP_BIT_PULSE / 2)) 1408c2ecf20Sopenharmony_ci break; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* Validate - command, ext, chk should be inverted in 2nd */ 1438c2ecf20Sopenharmony_ci msg = (data->bits >> 15) & 0x7fff; 1448c2ecf20Sopenharmony_ci echo = data->bits & 0x7fff; 1458c2ecf20Sopenharmony_ci if ((msg ^ echo) != 0x3ff) { 1468c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, 1478c2ecf20Sopenharmony_ci "Sharp checksum error: received 0x%04x, 0x%04x\n", 1488c2ecf20Sopenharmony_ci msg, echo); 1498c2ecf20Sopenharmony_ci break; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci address = bitrev8((msg >> 7) & 0xf8); 1538c2ecf20Sopenharmony_ci command = bitrev8((msg >> 2) & 0xff); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci scancode = address << 8 | command; 1568c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "Sharp scancode 0x%04x\n", scancode); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci rc_keydown(dev, RC_PROTO_SHARP, scancode, 0); 1598c2ecf20Sopenharmony_ci data->state = STATE_INACTIVE; 1608c2ecf20Sopenharmony_ci return 0; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "Sharp decode failed at count %d state %d (%uus %s)\n", 1648c2ecf20Sopenharmony_ci data->count, data->state, ev.duration, TO_STR(ev.pulse)); 1658c2ecf20Sopenharmony_ci data->state = STATE_INACTIVE; 1668c2ecf20Sopenharmony_ci return -EINVAL; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic const struct ir_raw_timings_pd ir_sharp_timings = { 1708c2ecf20Sopenharmony_ci .header_pulse = 0, 1718c2ecf20Sopenharmony_ci .header_space = 0, 1728c2ecf20Sopenharmony_ci .bit_pulse = SHARP_BIT_PULSE, 1738c2ecf20Sopenharmony_ci .bit_space[0] = SHARP_BIT_0_SPACE, 1748c2ecf20Sopenharmony_ci .bit_space[1] = SHARP_BIT_1_SPACE, 1758c2ecf20Sopenharmony_ci .trailer_pulse = SHARP_BIT_PULSE, 1768c2ecf20Sopenharmony_ci .trailer_space = SHARP_ECHO_SPACE, 1778c2ecf20Sopenharmony_ci .msb_first = 1, 1788c2ecf20Sopenharmony_ci}; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci/** 1818c2ecf20Sopenharmony_ci * ir_sharp_encode() - Encode a scancode as a stream of raw events 1828c2ecf20Sopenharmony_ci * 1838c2ecf20Sopenharmony_ci * @protocol: protocol to encode 1848c2ecf20Sopenharmony_ci * @scancode: scancode to encode 1858c2ecf20Sopenharmony_ci * @events: array of raw ir events to write into 1868c2ecf20Sopenharmony_ci * @max: maximum size of @events 1878c2ecf20Sopenharmony_ci * 1888c2ecf20Sopenharmony_ci * Returns: The number of events written. 1898c2ecf20Sopenharmony_ci * -ENOBUFS if there isn't enough space in the array to fit the 1908c2ecf20Sopenharmony_ci * encoding. In this case all @max events will have been written. 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_cistatic int ir_sharp_encode(enum rc_proto protocol, u32 scancode, 1938c2ecf20Sopenharmony_ci struct ir_raw_event *events, unsigned int max) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct ir_raw_event *e = events; 1968c2ecf20Sopenharmony_ci int ret; 1978c2ecf20Sopenharmony_ci u32 raw; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci raw = (((bitrev8(scancode >> 8) >> 3) << 8) & 0x1f00) | 2008c2ecf20Sopenharmony_ci bitrev8(scancode); 2018c2ecf20Sopenharmony_ci ret = ir_raw_gen_pd(&e, max, &ir_sharp_timings, SHARP_NBITS, 2028c2ecf20Sopenharmony_ci (raw << 2) | 2); 2038c2ecf20Sopenharmony_ci if (ret < 0) 2048c2ecf20Sopenharmony_ci return ret; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci max -= ret; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci raw = (((bitrev8(scancode >> 8) >> 3) << 8) & 0x1f00) | 2098c2ecf20Sopenharmony_ci bitrev8(~scancode); 2108c2ecf20Sopenharmony_ci ret = ir_raw_gen_pd(&e, max, &ir_sharp_timings, SHARP_NBITS, 2118c2ecf20Sopenharmony_ci (raw << 2) | 1); 2128c2ecf20Sopenharmony_ci if (ret < 0) 2138c2ecf20Sopenharmony_ci return ret; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci return e - events; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic struct ir_raw_handler sharp_handler = { 2198c2ecf20Sopenharmony_ci .protocols = RC_PROTO_BIT_SHARP, 2208c2ecf20Sopenharmony_ci .decode = ir_sharp_decode, 2218c2ecf20Sopenharmony_ci .encode = ir_sharp_encode, 2228c2ecf20Sopenharmony_ci .carrier = 38000, 2238c2ecf20Sopenharmony_ci .min_timeout = SHARP_ECHO_SPACE + SHARP_ECHO_SPACE / 4, 2248c2ecf20Sopenharmony_ci}; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic int __init ir_sharp_decode_init(void) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci ir_raw_handler_register(&sharp_handler); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci pr_info("IR Sharp protocol handler initialized\n"); 2318c2ecf20Sopenharmony_ci return 0; 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic void __exit ir_sharp_decode_exit(void) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci ir_raw_handler_unregister(&sharp_handler); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cimodule_init(ir_sharp_decode_init); 2408c2ecf20Sopenharmony_cimodule_exit(ir_sharp_decode_exit); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2438c2ecf20Sopenharmony_ciMODULE_AUTHOR("James Hogan <jhogan@kernel.org>"); 2448c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Sharp IR protocol decoder"); 245