mirror of
https://github.com/hardkernel/linux.git
synced 2026-04-02 03:03:00 +09:00
PD#138714: initial add remote driver temporarily disable the feature which reporting event after system resume, and it will be enabled after the 'amlogic/pm' driver is available. Change-Id: I57312621557f050401b5028b7607144cb9a9351f Signed-off-by: Xingyu Chen <xingyu.chen@amlogic.com>
257 lines
6.3 KiB
C
257 lines
6.3 KiB
C
/*
|
|
* 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");
|
|
|
|
|