blob: 6698615ea1a4f9225e7ceae3f4aa9e7e9a2d7a42 [file] [log] [blame]
/*
* drivers/amlogic/input/remote/remote_decoder_xmp.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include <linux/bitrev.h>
#include <linux/module.h>
#include "remote_meson.h"
#define XMP_UNIT 136000 /* ns */
#define XMP_LEADER 210000 /* ns */
#define XMP_NIBBLE_PREFIX 760000 /* ns */
#define XMP_HALFFRAME_SPACE 13800000 /* ns */
/* should be 80ms but not all dureation supliers can go that high */
#define XMP_TRAILER_SPACE 20000000
enum xmp_state {
STATE_INACTIVE,
STATE_LEADER_PULSE,
STATE_NIBBLE_SPACE,
};
struct nibble_win {
u8 value;
int min;
int max;
};
struct xmp_dec {
int state;
unsigned int count;
u32 durations[16];
};
static const struct nibble_win nibble_windows[] = {
{0, 690000, 826000}, /*758000*/
{1, 827000, 963000}, /*895000*/
{2, 964000, 1100000}, /*1032000*/
{3, 1110000, 1237000}, /*1169000*/
{4, 1238000, 1374000}, /*1306000*/
{5, 1375000, 1511000}, /*1443000*/
{6, 1512000, 1648000}, /*1580000*/
{7, 1649000, 1785000}, /*1717000*/
{8, 1786000, 1922000}, /*1854000*/
{9, 1923000, 2059000}, /*1991000*/
{0xa, 2060000, 2196000}, /*2128000*/
{0xb, 2197000, 2333000}, /*2265000*/
{0xc, 2334000, 2470000}, /*2402000*/
{0xd, 2471000, 2607000}, /*2539000*/
{0xe, 2608000, 2744000}, /*2676000*/
{0x0f, 2745000, 2881000}, /*2813000*/
{0xff, 11800000, 16800000} /*13800000 ,half frame space*/
};
int decode_xmp(struct remote_dev *dev,
struct xmp_dec *data, struct remote_raw_event *ev)
{
int i;
u8 addr, subaddr, subaddr2, toggle, oem, obc1, obc2, sum1, sum2;
u32 *n;
u32 scancode;
int custom_code;
int nb = 0;
char buf[512];
if (data->count != 16) {
sprintf(buf, "rx TRAILER c=%d, d=%d\n",
data->count, ev->duration);
debug_log_printk(dev, buf);
data->state = STATE_INACTIVE;
return -EINVAL;
}
n = data->durations;
for (i = 0; i < 16; i++) {
for (nb = 0; nb < 16; nb++) {
if (n[i] >= nibble_windows[nb].min &&
n[i] <= nibble_windows[nb].max) {
n[i] = nibble_windows[nb].value;
}
}
}
sum1 = (15 + n[0] + n[1] + n[2] + n[3] +
n[4] + n[5] + n[6] + n[7]) % 16;
sum2 = (15 + n[8] + n[9] + n[10] + n[11] +
n[12] + n[13] + n[14] + n[15]) % 16;
if (sum1 != 15 || sum2 != 15) {
debug_log_printk(dev, "checksum err\n");
data->state = STATE_INACTIVE;
return -EINVAL;
}
subaddr = n[0] << 4 | n[2];
subaddr2 = n[8] << 4 | n[11];
oem = n[4] << 4 | n[5];
addr = n[6] << 4 | n[7];
toggle = n[10];
obc1 = n[12] << 4 | n[13];
obc2 = n[14] << 4 | n[15];
if (subaddr != subaddr2) {
sprintf(buf, "s1!=s2\n");
debug_log_printk(dev, buf);
data->state = STATE_INACTIVE;
return -EINVAL;
}
scancode = obc1;
custom_code = oem << 8 | addr;
sprintf(buf, "custom_code=%d\n", custom_code);
debug_log_printk(dev, buf);
sprintf(buf, "scancode=0x%x,t=%d\n", scancode, toggle);
debug_log_printk(dev, buf);
dev->set_custom_code(dev, custom_code);
if (toggle == 0)
remote_keydown(dev, scancode, REMOTE_NORMAL);
else
remote_keydown(dev, scancode, REMOTE_REPEAT);
data->state = STATE_INACTIVE;
return 0;
}
/**
* ir_xmp_decode() - Decode one XMP pulse or space
* @dev: the struct rc_dev descriptor of the device
* @duration: the struct ir_raw_event descriptor of the pulse/space
*
* This function returns -EINVAL if the pulse violates the state machine
*/
static int ir_xmp_decode(struct remote_dev *dev, struct remote_raw_event ev,
void *data_dec)
{
struct xmp_dec *data = data_dec;
char buf[512];
if (ev.reset) {
data->state = STATE_INACTIVE;
return 0;
}
sprintf(buf, "dr:%d,s=%d, c=%d\n",
ev.duration, data->state, data->count);
debug_log_printk(dev, buf);
switch (data->state) {
case STATE_INACTIVE:
if (eq_margin(ev.duration, XMP_LEADER, XMP_UNIT / 2)) {
data->count = 0;
data->state = STATE_NIBBLE_SPACE;
}
return 0;
case STATE_LEADER_PULSE:
debug_log_printk(dev, "STATE_LEADER_PULSE\n");
if (eq_margin(ev.duration, XMP_LEADER, XMP_UNIT / 2))
data->state = STATE_NIBBLE_SPACE;
if (data->count == 16)
return decode_xmp(dev, data, &ev);
return 0;
case STATE_NIBBLE_SPACE:
if (geq_margin(ev.duration,
XMP_TRAILER_SPACE, XMP_NIBBLE_PREFIX)) {
return decode_xmp(dev, data, &ev);
} else if (geq_margin(ev.duration, XMP_HALFFRAME_SPACE,
XMP_NIBBLE_PREFIX)) {
/* Expect 8 or 16 nibble pulses. 16
* in case of 'final' frame
*/
if (data->count == 16) {
/*
* TODO: for now go back to half frame position
* so trailer can be found and key press
* can be handled.
*/
debug_log_printk(dev, "over pulses\n");
data->count = 8;
} else if (data->count != 8)
debug_log_printk(dev, "half frame\n");
data->state = STATE_LEADER_PULSE;
return 0;
} else if (geq_margin(ev.duration,
XMP_NIBBLE_PREFIX, XMP_UNIT)) {
/* store nibble raw data, decode after trailer */
if (data->count == 16) {
debug_log_printk(dev, "over pulses\n");
data->state = STATE_INACTIVE;
return -EINVAL;
}
data->durations[data->count] = ev.duration;
data->count++;
data->state = STATE_LEADER_PULSE;
return 0;
}
break;
}
debug_log_printk(dev, "dec failed\n");
data->state = STATE_INACTIVE;
return -EINVAL;
}
static struct remote_raw_handler xmp_handler = {
.protocols = REMOTE_TYPE_RAW_XMP_1,
.decode = ir_xmp_decode,
};
static int __init ir_xmp_decode_init(void)
{
xmp_handler.data = kzalloc(sizeof(struct xmp_dec), GFP_KERNEL);
if (!xmp_handler.data) {
pr_err("%s: ir_xmp_decode_init alloc xmp_dec failure\n",
DRIVER_NAME);
return -1;
}
remote_raw_handler_register(&xmp_handler);
pr_info("%s: IR XMP protocol handler initialized\n", DRIVER_NAME);
return 0;
}
static void __exit ir_xmp_decode_exit(void)
{
remote_raw_handler_unregister(&xmp_handler);
if (!xmp_handler.data)
kfree(xmp_handler.data);
}
module_init(ir_xmp_decode_init);
module_exit(ir_xmp_decode_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AMLOGIC");
MODULE_DESCRIPTION("XMP IR PROTOCOL DECODER");