348 lines
7.7 KiB
C
348 lines
7.7 KiB
C
|
/*
|
||
|
* (C) Copyright 2017 Rockchip Electronics Co., Ltd
|
||
|
*
|
||
|
* SPDX-License-Identifier: GPL-2.0+
|
||
|
*/
|
||
|
|
||
|
#include <common.h>
|
||
|
#include <clk.h>
|
||
|
#include <dm.h>
|
||
|
#include <dm/pinctrl.h>
|
||
|
#include <errno.h>
|
||
|
#include <rc.h>
|
||
|
#include <rockchip_ir.h>
|
||
|
#include <irq-generic.h>
|
||
|
#include <irq-platform.h>
|
||
|
|
||
|
#include <linux/bitrev.h>
|
||
|
#include <linux/input.h>
|
||
|
|
||
|
#include <asm/arch/periph.h>
|
||
|
#include <asm/io.h>
|
||
|
#include <dm/ofnode.h>
|
||
|
DECLARE_GLOBAL_DATA_PTR;
|
||
|
|
||
|
static struct nec_dec nec;
|
||
|
static struct rc_map *rc_map;
|
||
|
|
||
|
static int rockchip_ir_get_keycode(struct udevice *dev)
|
||
|
{
|
||
|
struct rockchip_ir_priv *priv = dev_get_priv(dev);
|
||
|
|
||
|
return priv->keycode;
|
||
|
}
|
||
|
|
||
|
static int rockchip_ir_get_repeat(struct udevice *dev)
|
||
|
{
|
||
|
struct rockchip_ir_priv *priv = dev_get_priv(dev);
|
||
|
|
||
|
return priv->repeat;
|
||
|
}
|
||
|
|
||
|
static int ir_lookup_by_scancode(struct rockchip_ir_priv *priv,
|
||
|
u32 usercode,
|
||
|
u32 scancode)
|
||
|
{
|
||
|
int i, j;
|
||
|
|
||
|
for (i = 0; i < priv->num; i++) {
|
||
|
if (rc_map[i].usercode == usercode)
|
||
|
break;
|
||
|
}
|
||
|
for (j = 0; i < priv->num && j < rc_map[i].nbuttons; j++) {
|
||
|
if (rc_map[i].scan[j].scancode == scancode) {
|
||
|
if (priv->keycode == rc_map[i].scan[j].keycode)
|
||
|
priv->repeat++;
|
||
|
else
|
||
|
priv->repeat = 0;
|
||
|
priv->keycode = rc_map[i].scan[j].keycode;
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
priv->keycode = KEY_RESERVED;
|
||
|
priv->repeat = 0;
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static int ir_parse_keys(struct udevice *dev)
|
||
|
{
|
||
|
int i, j;
|
||
|
int len;
|
||
|
int ret;
|
||
|
u32 val;
|
||
|
ofnode node;
|
||
|
|
||
|
i = 0;
|
||
|
dev_for_each_subnode(node, dev) {
|
||
|
ret = ofnode_read_u32(node, "rockchip,usercode", &val);
|
||
|
if (ret) {
|
||
|
debug("unable to get usercode\n");
|
||
|
return -1;
|
||
|
}
|
||
|
rc_map[i].usercode = val;
|
||
|
if (rc_map[i].usercode == 0) {
|
||
|
debug("missing usercode property in the dts\n");
|
||
|
return -1;
|
||
|
}
|
||
|
debug("add new usercode:0x%x\n", rc_map[i].usercode);
|
||
|
len = ofnode_read_size(node, "rockchip,key_table");
|
||
|
len /= sizeof(u32);
|
||
|
debug("len:%d\n", len);
|
||
|
rc_map[i].nbuttons = len / 2;
|
||
|
|
||
|
ret = ofnode_read_u32_array(node, "rockchip,key_table",
|
||
|
(u32 *)rc_map[i].scan, len);
|
||
|
if (ret) {
|
||
|
debug("missing key_table property in the dts\n");
|
||
|
return -1;
|
||
|
}
|
||
|
for (j = 0; j < (len / 2); j++) {
|
||
|
debug("[%d],usercode=0x%x scancode=0x%x keycode=0x%x\n",
|
||
|
i,
|
||
|
rc_map[i].usercode,
|
||
|
rc_map[i].scan[j].scancode,
|
||
|
rc_map[i].scan[j].keycode);
|
||
|
}
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ir_nec_decode() - Decode one NEC pulse or space
|
||
|
* @duration: the struct ir_raw_event descriptor of the pulse/space
|
||
|
*/
|
||
|
static int ir_nec_decode(struct rockchip_ir_priv *priv, struct ir_raw_event *ev)
|
||
|
{
|
||
|
int ret;
|
||
|
u32 usercode;
|
||
|
u32 scancode;
|
||
|
u8 __maybe_unused address, not_address, command, not_command;
|
||
|
struct nec_dec *data = &nec;
|
||
|
|
||
|
switch (data->state) {
|
||
|
case STATE_INACTIVE:
|
||
|
if (!ev->pulse)
|
||
|
break;
|
||
|
|
||
|
if (!eq_margin(ev->duration, NEC_HEADER_PULSE, NEC_UNIT * 2))
|
||
|
break;
|
||
|
|
||
|
data->count = 0;
|
||
|
data->state = STATE_HEADER_SPACE;
|
||
|
return 0;
|
||
|
|
||
|
case STATE_HEADER_SPACE:
|
||
|
if (ev->pulse)
|
||
|
break;
|
||
|
|
||
|
if (eq_margin(ev->duration, NEC_HEADER_SPACE, NEC_UNIT)) {
|
||
|
data->state = STATE_BIT_PULSE;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case STATE_BIT_PULSE:
|
||
|
if (!ev->pulse)
|
||
|
break;
|
||
|
|
||
|
if (!eq_margin(ev->duration, NEC_BIT_PULSE, NEC_UNIT / 2))
|
||
|
break;
|
||
|
|
||
|
data->state = STATE_BIT_SPACE;
|
||
|
return 0;
|
||
|
|
||
|
case STATE_BIT_SPACE:
|
||
|
if (ev->pulse)
|
||
|
break;
|
||
|
|
||
|
data->bits <<= 1;
|
||
|
if (eq_margin(ev->duration, NEC_BIT_1_SPACE, NEC_UNIT / 2)) {
|
||
|
data->bits |= 1;
|
||
|
} else if (!eq_margin(ev->duration, NEC_BIT_0_SPACE,
|
||
|
NEC_UNIT / 2)) {
|
||
|
break;
|
||
|
}
|
||
|
data->count++;
|
||
|
|
||
|
if (data->count == NEC_NBITS) {
|
||
|
address = ((data->bits >> 24) & 0xff);
|
||
|
not_address = ((data->bits >> 16) & 0xff);
|
||
|
command = ((data->bits >> 8) & 0xff);
|
||
|
not_command = ((data->bits >> 0) & 0xff);
|
||
|
|
||
|
if ((command ^ not_command) != 0xff) {
|
||
|
debug("NEC checksum error: received 0x%08x\n",
|
||
|
data->bits);
|
||
|
}
|
||
|
usercode = address << 8 | not_address;
|
||
|
scancode = command << 8 | not_command;
|
||
|
|
||
|
/* change to dts format */
|
||
|
usercode = bitrev16(usercode);
|
||
|
scancode = (bitrev16(scancode) >> 8) & 0xFF;
|
||
|
debug("usercode 0x%04x scancode 0x%04x\n",
|
||
|
usercode, scancode);
|
||
|
|
||
|
data->state = STATE_INACTIVE;
|
||
|
ret = ir_lookup_by_scancode(priv, usercode, scancode);
|
||
|
if (!ret)
|
||
|
debug("keycode 0x%02x repeat 0x%x\n",
|
||
|
priv->keycode, priv->repeat);
|
||
|
else
|
||
|
debug("ir lookup by scancode failed\n");
|
||
|
} else {
|
||
|
data->state = STATE_BIT_PULSE;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
debug("NEC decode failed at count %d state %d (%uus %s)\n",
|
||
|
data->count, data->state, TO_US(ev->duration), TO_STR(ev->pulse));
|
||
|
data->state = STATE_INACTIVE;
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static void rockchip_ir_irq(int irq, void *data)
|
||
|
{
|
||
|
u32 val;
|
||
|
u32 cycle_hpr, cycle_lpr, cycle;
|
||
|
struct ir_raw_event ev;
|
||
|
struct rockchip_ir_priv *priv = (struct rockchip_ir_priv *)data;
|
||
|
|
||
|
val = readl(priv->base + PWM_STA_REG(priv->id));
|
||
|
cycle_hpr = readl(priv->base + PWM_HPR_REG);
|
||
|
cycle_lpr = readl(priv->base + PWM_LPR_REG);
|
||
|
if (val & PWM_CH_POL(priv->id)) {
|
||
|
cycle = cycle_hpr;
|
||
|
ev.pulse = 0;
|
||
|
} else {
|
||
|
cycle = cycle_lpr;
|
||
|
ev.pulse = 1;
|
||
|
}
|
||
|
writel(PWM_CH_INT(priv->id),
|
||
|
priv->base + PWM_STA_REG(priv->id));
|
||
|
|
||
|
ev.duration = cycle * priv->period;
|
||
|
ir_nec_decode(priv, &ev);
|
||
|
}
|
||
|
|
||
|
static void rockchip_ir_hw_init(struct udevice *dev)
|
||
|
{
|
||
|
unsigned long tmp;
|
||
|
struct rockchip_ir_priv *priv = dev_get_priv(dev);
|
||
|
|
||
|
/* Enable capture mode, non-scaled clock, prescale 1 */
|
||
|
writel(REG_CTL_MD, priv->base + PWM_CTL_REG);
|
||
|
|
||
|
/* Clear Interrupt Status */
|
||
|
writel(PWM_CH_INT(priv->id),
|
||
|
priv->base + PWM_STA_REG(priv->id));
|
||
|
|
||
|
/* Enable IRQ */
|
||
|
writel(PWM_CH_INT(priv->id),
|
||
|
priv->base + PWM_INT_REG(priv->id));
|
||
|
|
||
|
/* Enable IR Module */
|
||
|
tmp = readl(priv->base + PWM_CTL_REG);
|
||
|
writel(tmp | REG_CTL_EN, priv->base + PWM_CTL_REG);
|
||
|
}
|
||
|
|
||
|
static int rockchip_ir_ofdata_to_platdata(struct udevice *dev)
|
||
|
{
|
||
|
ofnode node;
|
||
|
int ret;
|
||
|
int subnode_num = 0;
|
||
|
u32 val;
|
||
|
struct rockchip_ir_priv *priv = dev_get_priv(dev);
|
||
|
|
||
|
dev_for_each_subnode(node, dev) {
|
||
|
ret = ofnode_read_u32(node, "rockchip,usercode", &val);
|
||
|
if (!ret)
|
||
|
subnode_num++;
|
||
|
}
|
||
|
|
||
|
priv->num = subnode_num;
|
||
|
|
||
|
if (priv->num == 0) {
|
||
|
debug("no ir map in dts\n");
|
||
|
return -1;
|
||
|
}
|
||
|
priv->base = dev_read_addr(dev);
|
||
|
priv->id = (priv->base >> 4) & 0xF;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int rockchip_ir_probe(struct udevice *dev)
|
||
|
{
|
||
|
int ret;
|
||
|
struct clk clk;
|
||
|
struct udevice *pinctrl;
|
||
|
struct rockchip_ir_priv *priv = dev_get_priv(dev);
|
||
|
|
||
|
rc_map = calloc(1, priv->num * sizeof(struct rc_map));
|
||
|
if (!rc_map) {
|
||
|
debug("%s: failed to calloc\n", __func__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
ret = ir_parse_keys(dev);
|
||
|
if (ret) {
|
||
|
debug("%s: failed to parse keys\n", __func__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
/*
|
||
|
* The PWM does not have decicated interrupt number in dts and can
|
||
|
* not get periph_id by pinctrl framework, so let's init then here.
|
||
|
*/
|
||
|
ret = uclass_get_device(UCLASS_PINCTRL, 0, &pinctrl);
|
||
|
if (ret) {
|
||
|
debug("%s: can't find pinctrl device\n", __func__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
ret = clk_get_by_index(dev, 0, &clk);
|
||
|
if (ret) {
|
||
|
debug("%s get clock fail!\n", __func__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
priv->freq = clk_get_rate(&clk);
|
||
|
debug("%s pwm clk = %lu\n", __func__, priv->freq);
|
||
|
priv->period = 1000000000 / priv->freq;
|
||
|
|
||
|
irq_install_handler(IRQ_PWM,
|
||
|
(interrupt_handler_t *)rockchip_ir_irq, priv);
|
||
|
irq_handler_enable(IRQ_PWM);
|
||
|
|
||
|
rockchip_ir_hw_init(dev);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static const struct dm_rc_ops rockchip_ir_ops = {
|
||
|
.get_keycode = rockchip_ir_get_keycode,
|
||
|
.get_repeat = rockchip_ir_get_repeat,
|
||
|
};
|
||
|
|
||
|
static const struct udevice_id rockchip_ir_ids[] = {
|
||
|
{ .compatible = "rockchip,remotectl-pwm" },
|
||
|
{ }
|
||
|
};
|
||
|
|
||
|
U_BOOT_DRIVER(rockchip_ir) = {
|
||
|
.name = "rockchip_ir",
|
||
|
.id = UCLASS_RC,
|
||
|
.of_match = rockchip_ir_ids,
|
||
|
.ofdata_to_platdata = rockchip_ir_ofdata_to_platdata,
|
||
|
.probe = rockchip_ir_probe,
|
||
|
.ops = &rockchip_ir_ops,
|
||
|
.priv_auto_alloc_size = sizeof(struct rockchip_ir_priv),
|
||
|
};
|