xref: /OK3568_Linux_fs/kernel/drivers/media/rc/ir-rcmm-decoder.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun // ir-rcmm-decoder.c - A decoder for the RCMM IR protocol
3*4882a593Smuzhiyun //
4*4882a593Smuzhiyun // Copyright (C) 2018 by Patrick Lerda <patrick9876@free.fr>
5*4882a593Smuzhiyun 
6*4882a593Smuzhiyun #include "rc-core-priv.h"
7*4882a593Smuzhiyun #include <linux/module.h>
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun #define RCMM_UNIT		166  /* microseconds */
10*4882a593Smuzhiyun #define RCMM_PREFIX_PULSE	417  /* 166.666666666666*2.5 */
11*4882a593Smuzhiyun #define RCMM_PULSE_0            278  /* 166.666666666666*(1+2/3) */
12*4882a593Smuzhiyun #define RCMM_PULSE_1            444  /* 166.666666666666*(2+2/3) */
13*4882a593Smuzhiyun #define RCMM_PULSE_2            611  /* 166.666666666666*(3+2/3) */
14*4882a593Smuzhiyun #define RCMM_PULSE_3            778  /* 166.666666666666*(4+2/3) */
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun enum rcmm_state {
17*4882a593Smuzhiyun 	STATE_INACTIVE,
18*4882a593Smuzhiyun 	STATE_LOW,
19*4882a593Smuzhiyun 	STATE_BUMP,
20*4882a593Smuzhiyun 	STATE_VALUE,
21*4882a593Smuzhiyun 	STATE_FINISHED,
22*4882a593Smuzhiyun };
23*4882a593Smuzhiyun 
rcmm_mode(const struct rcmm_dec * data)24*4882a593Smuzhiyun static bool rcmm_mode(const struct rcmm_dec *data)
25*4882a593Smuzhiyun {
26*4882a593Smuzhiyun 	return !((0x000c0000 & data->bits) == 0x000c0000);
27*4882a593Smuzhiyun }
28*4882a593Smuzhiyun 
rcmm_miscmode(struct rc_dev * dev,struct rcmm_dec * data)29*4882a593Smuzhiyun static int rcmm_miscmode(struct rc_dev *dev, struct rcmm_dec *data)
30*4882a593Smuzhiyun {
31*4882a593Smuzhiyun 	switch (data->count) {
32*4882a593Smuzhiyun 	case 24:
33*4882a593Smuzhiyun 		if (dev->enabled_protocols & RC_PROTO_BIT_RCMM24) {
34*4882a593Smuzhiyun 			rc_keydown(dev, RC_PROTO_RCMM24, data->bits, 0);
35*4882a593Smuzhiyun 			data->state = STATE_INACTIVE;
36*4882a593Smuzhiyun 			return 0;
37*4882a593Smuzhiyun 		}
38*4882a593Smuzhiyun 		return -1;
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun 	case 12:
41*4882a593Smuzhiyun 		if (dev->enabled_protocols & RC_PROTO_BIT_RCMM12) {
42*4882a593Smuzhiyun 			rc_keydown(dev, RC_PROTO_RCMM12, data->bits, 0);
43*4882a593Smuzhiyun 			data->state = STATE_INACTIVE;
44*4882a593Smuzhiyun 			return 0;
45*4882a593Smuzhiyun 		}
46*4882a593Smuzhiyun 		return -1;
47*4882a593Smuzhiyun 	}
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun 	return -1;
50*4882a593Smuzhiyun }
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun /**
53*4882a593Smuzhiyun  * ir_rcmm_decode() - Decode one RCMM pulse or space
54*4882a593Smuzhiyun  * @dev:	the struct rc_dev descriptor of the device
55*4882a593Smuzhiyun  * @ev:		the struct ir_raw_event descriptor of the pulse/space
56*4882a593Smuzhiyun  *
57*4882a593Smuzhiyun  * This function returns -EINVAL if the pulse violates the state machine
58*4882a593Smuzhiyun  */
ir_rcmm_decode(struct rc_dev * dev,struct ir_raw_event ev)59*4882a593Smuzhiyun static int ir_rcmm_decode(struct rc_dev *dev, struct ir_raw_event ev)
60*4882a593Smuzhiyun {
61*4882a593Smuzhiyun 	struct rcmm_dec *data = &dev->raw->rcmm;
62*4882a593Smuzhiyun 	u32 scancode;
63*4882a593Smuzhiyun 	u8 toggle;
64*4882a593Smuzhiyun 	int value;
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun 	if (!(dev->enabled_protocols & (RC_PROTO_BIT_RCMM32 |
67*4882a593Smuzhiyun 					RC_PROTO_BIT_RCMM24 |
68*4882a593Smuzhiyun 					RC_PROTO_BIT_RCMM12)))
69*4882a593Smuzhiyun 		return 0;
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun 	if (!is_timing_event(ev)) {
72*4882a593Smuzhiyun 		if (ev.reset)
73*4882a593Smuzhiyun 			data->state = STATE_INACTIVE;
74*4882a593Smuzhiyun 		return 0;
75*4882a593Smuzhiyun 	}
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun 	switch (data->state) {
78*4882a593Smuzhiyun 	case STATE_INACTIVE:
79*4882a593Smuzhiyun 		if (!ev.pulse)
80*4882a593Smuzhiyun 			break;
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun 		if (!eq_margin(ev.duration, RCMM_PREFIX_PULSE, RCMM_UNIT))
83*4882a593Smuzhiyun 			break;
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 		data->state = STATE_LOW;
86*4882a593Smuzhiyun 		data->count = 0;
87*4882a593Smuzhiyun 		data->bits  = 0;
88*4882a593Smuzhiyun 		return 0;
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun 	case STATE_LOW:
91*4882a593Smuzhiyun 		if (ev.pulse)
92*4882a593Smuzhiyun 			break;
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 		if (!eq_margin(ev.duration, RCMM_PULSE_0, RCMM_UNIT))
95*4882a593Smuzhiyun 			break;
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun 		data->state = STATE_BUMP;
98*4882a593Smuzhiyun 		return 0;
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun 	case STATE_BUMP:
101*4882a593Smuzhiyun 		if (!ev.pulse)
102*4882a593Smuzhiyun 			break;
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 		if (!eq_margin(ev.duration, RCMM_UNIT, RCMM_UNIT / 2))
105*4882a593Smuzhiyun 			break;
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun 		data->state = STATE_VALUE;
108*4882a593Smuzhiyun 		return 0;
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun 	case STATE_VALUE:
111*4882a593Smuzhiyun 		if (ev.pulse)
112*4882a593Smuzhiyun 			break;
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 		if (eq_margin(ev.duration, RCMM_PULSE_0, RCMM_UNIT / 2))
115*4882a593Smuzhiyun 			value = 0;
116*4882a593Smuzhiyun 		else if (eq_margin(ev.duration, RCMM_PULSE_1, RCMM_UNIT / 2))
117*4882a593Smuzhiyun 			value = 1;
118*4882a593Smuzhiyun 		else if (eq_margin(ev.duration, RCMM_PULSE_2, RCMM_UNIT / 2))
119*4882a593Smuzhiyun 			value = 2;
120*4882a593Smuzhiyun 		else if (eq_margin(ev.duration, RCMM_PULSE_3, RCMM_UNIT / 2))
121*4882a593Smuzhiyun 			value = 3;
122*4882a593Smuzhiyun 		else
123*4882a593Smuzhiyun 			value = -1;
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun 		if (value == -1) {
126*4882a593Smuzhiyun 			if (!rcmm_miscmode(dev, data))
127*4882a593Smuzhiyun 				return 0;
128*4882a593Smuzhiyun 			break;
129*4882a593Smuzhiyun 		}
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun 		data->bits <<= 2;
132*4882a593Smuzhiyun 		data->bits |= value;
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 		data->count += 2;
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun 		if (data->count < 32)
137*4882a593Smuzhiyun 			data->state = STATE_BUMP;
138*4882a593Smuzhiyun 		else
139*4882a593Smuzhiyun 			data->state = STATE_FINISHED;
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 		return 0;
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 	case STATE_FINISHED:
144*4882a593Smuzhiyun 		if (!ev.pulse)
145*4882a593Smuzhiyun 			break;
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 		if (!eq_margin(ev.duration, RCMM_UNIT, RCMM_UNIT / 2))
148*4882a593Smuzhiyun 			break;
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 		if (rcmm_mode(data)) {
151*4882a593Smuzhiyun 			toggle = !!(0x8000 & data->bits);
152*4882a593Smuzhiyun 			scancode = data->bits & ~0x8000;
153*4882a593Smuzhiyun 		} else {
154*4882a593Smuzhiyun 			toggle = 0;
155*4882a593Smuzhiyun 			scancode = data->bits;
156*4882a593Smuzhiyun 		}
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 		if (dev->enabled_protocols & RC_PROTO_BIT_RCMM32) {
159*4882a593Smuzhiyun 			rc_keydown(dev, RC_PROTO_RCMM32, scancode, toggle);
160*4882a593Smuzhiyun 			data->state = STATE_INACTIVE;
161*4882a593Smuzhiyun 			return 0;
162*4882a593Smuzhiyun 		}
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 		break;
165*4882a593Smuzhiyun 	}
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 	dev_dbg(&dev->dev, "RC-MM decode failed at count %d state %d (%uus %s)\n",
168*4882a593Smuzhiyun 		data->count, data->state, ev.duration, TO_STR(ev.pulse));
169*4882a593Smuzhiyun 	data->state = STATE_INACTIVE;
170*4882a593Smuzhiyun 	return -EINVAL;
171*4882a593Smuzhiyun }
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun static const int rcmmspace[] = {
174*4882a593Smuzhiyun 	RCMM_PULSE_0,
175*4882a593Smuzhiyun 	RCMM_PULSE_1,
176*4882a593Smuzhiyun 	RCMM_PULSE_2,
177*4882a593Smuzhiyun 	RCMM_PULSE_3,
178*4882a593Smuzhiyun };
179*4882a593Smuzhiyun 
ir_rcmm_rawencoder(struct ir_raw_event ** ev,unsigned int max,unsigned int n,u32 data)180*4882a593Smuzhiyun static int ir_rcmm_rawencoder(struct ir_raw_event **ev, unsigned int max,
181*4882a593Smuzhiyun 			      unsigned int n, u32 data)
182*4882a593Smuzhiyun {
183*4882a593Smuzhiyun 	int i;
184*4882a593Smuzhiyun 	int ret;
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	ret = ir_raw_gen_pulse_space(ev, &max, RCMM_PREFIX_PULSE, RCMM_PULSE_0);
187*4882a593Smuzhiyun 	if (ret)
188*4882a593Smuzhiyun 		return ret;
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun 	for (i = n - 2; i >= 0; i -= 2) {
191*4882a593Smuzhiyun 		const unsigned int space = rcmmspace[(data >> i) & 3];
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 		ret = ir_raw_gen_pulse_space(ev, &max, RCMM_UNIT, space);
194*4882a593Smuzhiyun 		if (ret)
195*4882a593Smuzhiyun 			return ret;
196*4882a593Smuzhiyun 	}
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 	return ir_raw_gen_pulse_space(ev, &max, RCMM_UNIT, RCMM_PULSE_3 * 2);
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun 
ir_rcmm_encode(enum rc_proto protocol,u32 scancode,struct ir_raw_event * events,unsigned int max)201*4882a593Smuzhiyun static int ir_rcmm_encode(enum rc_proto protocol, u32 scancode,
202*4882a593Smuzhiyun 			  struct ir_raw_event *events, unsigned int max)
203*4882a593Smuzhiyun {
204*4882a593Smuzhiyun 	struct ir_raw_event *e = events;
205*4882a593Smuzhiyun 	int ret;
206*4882a593Smuzhiyun 
207*4882a593Smuzhiyun 	switch (protocol) {
208*4882a593Smuzhiyun 	case RC_PROTO_RCMM32:
209*4882a593Smuzhiyun 		ret = ir_rcmm_rawencoder(&e, max, 32, scancode);
210*4882a593Smuzhiyun 		break;
211*4882a593Smuzhiyun 	case RC_PROTO_RCMM24:
212*4882a593Smuzhiyun 		ret = ir_rcmm_rawencoder(&e, max, 24, scancode);
213*4882a593Smuzhiyun 		break;
214*4882a593Smuzhiyun 	case RC_PROTO_RCMM12:
215*4882a593Smuzhiyun 		ret = ir_rcmm_rawencoder(&e, max, 12, scancode);
216*4882a593Smuzhiyun 		break;
217*4882a593Smuzhiyun 	default:
218*4882a593Smuzhiyun 		ret = -EINVAL;
219*4882a593Smuzhiyun 	}
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun 	if (ret < 0)
222*4882a593Smuzhiyun 		return ret;
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun 	return e - events;
225*4882a593Smuzhiyun }
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun static struct ir_raw_handler rcmm_handler = {
228*4882a593Smuzhiyun 	.protocols	= RC_PROTO_BIT_RCMM32 |
229*4882a593Smuzhiyun 			  RC_PROTO_BIT_RCMM24 |
230*4882a593Smuzhiyun 			  RC_PROTO_BIT_RCMM12,
231*4882a593Smuzhiyun 	.decode		= ir_rcmm_decode,
232*4882a593Smuzhiyun 	.encode         = ir_rcmm_encode,
233*4882a593Smuzhiyun 	.carrier        = 36000,
234*4882a593Smuzhiyun 	.min_timeout	= RCMM_PULSE_3 + RCMM_UNIT,
235*4882a593Smuzhiyun };
236*4882a593Smuzhiyun 
ir_rcmm_decode_init(void)237*4882a593Smuzhiyun static int __init ir_rcmm_decode_init(void)
238*4882a593Smuzhiyun {
239*4882a593Smuzhiyun 	ir_raw_handler_register(&rcmm_handler);
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun 	pr_info("IR RCMM protocol handler initialized\n");
242*4882a593Smuzhiyun 	return 0;
243*4882a593Smuzhiyun }
244*4882a593Smuzhiyun 
ir_rcmm_decode_exit(void)245*4882a593Smuzhiyun static void __exit ir_rcmm_decode_exit(void)
246*4882a593Smuzhiyun {
247*4882a593Smuzhiyun 	ir_raw_handler_unregister(&rcmm_handler);
248*4882a593Smuzhiyun }
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun module_init(ir_rcmm_decode_init);
251*4882a593Smuzhiyun module_exit(ir_rcmm_decode_exit);
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun MODULE_LICENSE("GPL");
254*4882a593Smuzhiyun MODULE_AUTHOR("Patrick Lerda");
255*4882a593Smuzhiyun MODULE_DESCRIPTION("RCMM IR protocol decoder");
256