linux/drivers/media/rc/ir-rcmm-decoder.c

256 lines
5.3 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0+
// ir-rcmm-decoder.c - A decoder for the RCMM IR protocol
//
// Copyright (C) 2018 by Patrick Lerda <patrick9876@free.fr>
#include "rc-core-priv.h"
#include <linux/module.h>
media: rc: harmonize infrared durations to microseconds rc-core kapi uses nanoseconds for infrared durations for receiving, and microseconds for sending. The uapi already uses microseconds for both, so this patch does not change the uapi. Infrared durations do not need nanosecond resolution. IR protocols do not have durations shorter than about 100 microseconds. Some IR hardware offers 250 microseconds resolution, which is sufficient for most protocols. Better hardware has 50 microsecond resolution and is enough for every protocol I am aware off. Unify on microseconds everywhere. This simplifies the code since less conversion between microseconds and nanoseconds needs to be done. This affects: - rx_resolution member of struct rc_dev - timeout member of struct rc_dev - duration member in struct ir_raw_event Cc: "Bruno Prémont" <bonbons@linux-vserver.org> Cc: Hans Verkuil <hverkuil-cisco@xs4all.nl> Cc: Maxim Levitsky <maximlevitsky@gmail.com> Cc: Patrick Lerda <patrick9876@free.fr> Cc: Kevin Hilman <khilman@baylibre.com> Cc: Neil Armstrong <narmstrong@baylibre.com> Cc: Jerome Brunet <jbrunet@baylibre.com> Cc: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Cc: Sean Wang <sean.wang@mediatek.com> Cc: Matthias Brugger <matthias.bgg@gmail.com> Cc: Patrice Chotard <patrice.chotard@st.com> Cc: Maxime Ripard <mripard@kernel.org> Cc: Chen-Yu Tsai <wens@csie.org> Cc: "David Härdeman" <david@hardeman.nu> Cc: Benjamin Valentin <benpicco@googlemail.com> Cc: Antti Palosaari <crope@iki.fi> Signed-off-by: Sean Young <sean@mess.org> Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-08-23 17:23:05 +00:00
#define RCMM_UNIT 166 /* microseconds */
#define RCMM_PREFIX_PULSE 417 /* 166.666666666666*2.5 */
#define RCMM_PULSE_0 278 /* 166.666666666666*(1+2/3) */
#define RCMM_PULSE_1 444 /* 166.666666666666*(2+2/3) */
#define RCMM_PULSE_2 611 /* 166.666666666666*(3+2/3) */
#define RCMM_PULSE_3 778 /* 166.666666666666*(4+2/3) */
enum rcmm_state {
STATE_INACTIVE,
STATE_LOW,
STATE_BUMP,
STATE_VALUE,
STATE_FINISHED,
};
static bool rcmm_mode(const struct rcmm_dec *data)
{
return !((0x000c0000 & data->bits) == 0x000c0000);
}
static int rcmm_miscmode(struct rc_dev *dev, struct rcmm_dec *data)
{
switch (data->count) {
case 24:
if (dev->enabled_protocols & RC_PROTO_BIT_RCMM24) {
rc_keydown(dev, RC_PROTO_RCMM24, data->bits, 0);
data->state = STATE_INACTIVE;
return 0;
}
return -1;
case 12:
if (dev->enabled_protocols & RC_PROTO_BIT_RCMM12) {
rc_keydown(dev, RC_PROTO_RCMM12, data->bits, 0);
data->state = STATE_INACTIVE;
return 0;
}
return -1;
}
return -1;
}
/**
* ir_rcmm_decode() - Decode one RCMM pulse or space
* @dev: the struct rc_dev descriptor of the device
* @ev: the struct ir_raw_event descriptor of the pulse/space
*
* This function returns -EINVAL if the pulse violates the state machine
*/
static int ir_rcmm_decode(struct rc_dev *dev, struct ir_raw_event ev)
{
struct rcmm_dec *data = &dev->raw->rcmm;
u32 scancode;
u8 toggle;
int value;
if (!(dev->enabled_protocols & (RC_PROTO_BIT_RCMM32 |
media: rc: harmonize infrared durations to microseconds rc-core kapi uses nanoseconds for infrared durations for receiving, and microseconds for sending. The uapi already uses microseconds for both, so this patch does not change the uapi. Infrared durations do not need nanosecond resolution. IR protocols do not have durations shorter than about 100 microseconds. Some IR hardware offers 250 microseconds resolution, which is sufficient for most protocols. Better hardware has 50 microsecond resolution and is enough for every protocol I am aware off. Unify on microseconds everywhere. This simplifies the code since less conversion between microseconds and nanoseconds needs to be done. This affects: - rx_resolution member of struct rc_dev - timeout member of struct rc_dev - duration member in struct ir_raw_event Cc: "Bruno Prémont" <bonbons@linux-vserver.org> Cc: Hans Verkuil <hverkuil-cisco@xs4all.nl> Cc: Maxim Levitsky <maximlevitsky@gmail.com> Cc: Patrick Lerda <patrick9876@free.fr> Cc: Kevin Hilman <khilman@baylibre.com> Cc: Neil Armstrong <narmstrong@baylibre.com> Cc: Jerome Brunet <jbrunet@baylibre.com> Cc: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Cc: Sean Wang <sean.wang@mediatek.com> Cc: Matthias Brugger <matthias.bgg@gmail.com> Cc: Patrice Chotard <patrice.chotard@st.com> Cc: Maxime Ripard <mripard@kernel.org> Cc: Chen-Yu Tsai <wens@csie.org> Cc: "David Härdeman" <david@hardeman.nu> Cc: Benjamin Valentin <benpicco@googlemail.com> Cc: Antti Palosaari <crope@iki.fi> Signed-off-by: Sean Young <sean@mess.org> Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-08-23 17:23:05 +00:00
RC_PROTO_BIT_RCMM24 |
RC_PROTO_BIT_RCMM12)))
return 0;
if (!is_timing_event(ev)) {
if (ev.overflow)
data->state = STATE_INACTIVE;
return 0;
}
switch (data->state) {
case STATE_INACTIVE:
if (!ev.pulse)
break;
if (!eq_margin(ev.duration, RCMM_PREFIX_PULSE, RCMM_UNIT))
break;
data->state = STATE_LOW;
data->count = 0;
data->bits = 0;
return 0;
case STATE_LOW:
if (ev.pulse)
break;
if (!eq_margin(ev.duration, RCMM_PULSE_0, RCMM_UNIT))
break;
data->state = STATE_BUMP;
return 0;
case STATE_BUMP:
if (!ev.pulse)
break;
if (!eq_margin(ev.duration, RCMM_UNIT, RCMM_UNIT / 2))
break;
data->state = STATE_VALUE;
return 0;
case STATE_VALUE:
if (ev.pulse)
break;
if (eq_margin(ev.duration, RCMM_PULSE_0, RCMM_UNIT / 2))
value = 0;
else if (eq_margin(ev.duration, RCMM_PULSE_1, RCMM_UNIT / 2))
value = 1;
else if (eq_margin(ev.duration, RCMM_PULSE_2, RCMM_UNIT / 2))
value = 2;
else if (eq_margin(ev.duration, RCMM_PULSE_3, RCMM_UNIT / 2))
value = 3;
else
value = -1;
if (value == -1) {
if (!rcmm_miscmode(dev, data))
return 0;
break;
}
data->bits <<= 2;
data->bits |= value;
data->count += 2;
if (data->count < 32)
data->state = STATE_BUMP;
else
data->state = STATE_FINISHED;
return 0;
case STATE_FINISHED:
if (!ev.pulse)
break;
if (!eq_margin(ev.duration, RCMM_UNIT, RCMM_UNIT / 2))
break;
if (rcmm_mode(data)) {
toggle = !!(0x8000 & data->bits);
scancode = data->bits & ~0x8000;
} else {
toggle = 0;
scancode = data->bits;
}
if (dev->enabled_protocols & RC_PROTO_BIT_RCMM32) {
rc_keydown(dev, RC_PROTO_RCMM32, scancode, toggle);
data->state = STATE_INACTIVE;
return 0;
}
break;
}
dev_dbg(&dev->dev, "RC-MM decode failed at count %d state %d (%uus %s)\n",
media: rc: harmonize infrared durations to microseconds rc-core kapi uses nanoseconds for infrared durations for receiving, and microseconds for sending. The uapi already uses microseconds for both, so this patch does not change the uapi. Infrared durations do not need nanosecond resolution. IR protocols do not have durations shorter than about 100 microseconds. Some IR hardware offers 250 microseconds resolution, which is sufficient for most protocols. Better hardware has 50 microsecond resolution and is enough for every protocol I am aware off. Unify on microseconds everywhere. This simplifies the code since less conversion between microseconds and nanoseconds needs to be done. This affects: - rx_resolution member of struct rc_dev - timeout member of struct rc_dev - duration member in struct ir_raw_event Cc: "Bruno Prémont" <bonbons@linux-vserver.org> Cc: Hans Verkuil <hverkuil-cisco@xs4all.nl> Cc: Maxim Levitsky <maximlevitsky@gmail.com> Cc: Patrick Lerda <patrick9876@free.fr> Cc: Kevin Hilman <khilman@baylibre.com> Cc: Neil Armstrong <narmstrong@baylibre.com> Cc: Jerome Brunet <jbrunet@baylibre.com> Cc: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Cc: Sean Wang <sean.wang@mediatek.com> Cc: Matthias Brugger <matthias.bgg@gmail.com> Cc: Patrice Chotard <patrice.chotard@st.com> Cc: Maxime Ripard <mripard@kernel.org> Cc: Chen-Yu Tsai <wens@csie.org> Cc: "David Härdeman" <david@hardeman.nu> Cc: Benjamin Valentin <benpicco@googlemail.com> Cc: Antti Palosaari <crope@iki.fi> Signed-off-by: Sean Young <sean@mess.org> Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-08-23 17:23:05 +00:00
data->count, data->state, ev.duration, TO_STR(ev.pulse));
data->state = STATE_INACTIVE;
return -EINVAL;
}
static const int rcmmspace[] = {
RCMM_PULSE_0,
RCMM_PULSE_1,
RCMM_PULSE_2,
RCMM_PULSE_3,
};
static int ir_rcmm_rawencoder(struct ir_raw_event **ev, unsigned int max,
unsigned int n, u32 data)
{
int i;
int ret;
ret = ir_raw_gen_pulse_space(ev, &max, RCMM_PREFIX_PULSE, RCMM_PULSE_0);
if (ret)
return ret;
for (i = n - 2; i >= 0; i -= 2) {
const unsigned int space = rcmmspace[(data >> i) & 3];
ret = ir_raw_gen_pulse_space(ev, &max, RCMM_UNIT, space);
if (ret)
return ret;
}
return ir_raw_gen_pulse_space(ev, &max, RCMM_UNIT, RCMM_PULSE_3 * 2);
}
static int ir_rcmm_encode(enum rc_proto protocol, u32 scancode,
struct ir_raw_event *events, unsigned int max)
{
struct ir_raw_event *e = events;
int ret;
switch (protocol) {
case RC_PROTO_RCMM32:
ret = ir_rcmm_rawencoder(&e, max, 32, scancode);
break;
case RC_PROTO_RCMM24:
ret = ir_rcmm_rawencoder(&e, max, 24, scancode);
break;
case RC_PROTO_RCMM12:
ret = ir_rcmm_rawencoder(&e, max, 12, scancode);
break;
default:
ret = -EINVAL;
}
if (ret < 0)
return ret;
return e - events;
}
static struct ir_raw_handler rcmm_handler = {
.protocols = RC_PROTO_BIT_RCMM32 |
RC_PROTO_BIT_RCMM24 |
RC_PROTO_BIT_RCMM12,
.decode = ir_rcmm_decode,
.encode = ir_rcmm_encode,
.carrier = 36000,
.min_timeout = RCMM_PULSE_3 + RCMM_UNIT,
};
static int __init ir_rcmm_decode_init(void)
{
ir_raw_handler_register(&rcmm_handler);
pr_info("IR RCMM protocol handler initialized\n");
return 0;
}
static void __exit ir_rcmm_decode_exit(void)
{
ir_raw_handler_unregister(&rcmm_handler);
}
module_init(ir_rcmm_decode_init);
module_exit(ir_rcmm_decode_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Patrick Lerda");
MODULE_DESCRIPTION("RCMM IR protocol decoder");