linux/drivers/media/rc/tango-ir.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2015 Mans Rullgard <mans@mansr.com>
   3 *
   4 * This program is free software; you can redistribute  it and/or modify it
   5 * under  the terms of  the GNU General  Public License as published by the
   6 * Free Software Foundation;  either version 2 of the  License, or (at your
   7 * option) any later version.
   8 */
   9
  10#include <linux/input.h>
  11#include <linux/module.h>
  12#include <linux/platform_device.h>
  13#include <linux/interrupt.h>
  14#include <linux/io.h>
  15#include <linux/clk.h>
  16#include <linux/of.h>
  17#include <media/rc-core.h>
  18
  19#define DRIVER_NAME "tango-ir"
  20
  21#define IR_NEC_CTRL     0x00
  22#define IR_NEC_DATA     0x04
  23#define IR_CTRL         0x08
  24#define IR_RC5_CLK_DIV  0x0c
  25#define IR_RC5_DATA     0x10
  26#define IR_INT          0x14
  27
  28#define NEC_TIME_BASE   560
  29#define RC5_TIME_BASE   1778
  30
  31#define RC6_CTRL        0x00
  32#define RC6_CLKDIV      0x04
  33#define RC6_DATA0       0x08
  34#define RC6_DATA1       0x0c
  35#define RC6_DATA2       0x10
  36#define RC6_DATA3       0x14
  37#define RC6_DATA4       0x18
  38
  39#define RC6_CARRIER     36000
  40#define RC6_TIME_BASE   16
  41
  42#define NEC_CAP(n)      ((n) << 24)
  43#define GPIO_SEL(n)     ((n) << 16)
  44#define DISABLE_NEC     (BIT(4) | BIT(8))
  45#define ENABLE_RC5      (BIT(0) | BIT(9))
  46#define ENABLE_RC6      (BIT(0) | BIT(7))
  47#define ACK_IR_INT      (BIT(0) | BIT(1))
  48#define ACK_RC6_INT     (BIT(31))
  49
  50#define NEC_ANY (RC_PROTO_BIT_NEC | RC_PROTO_BIT_NECX | RC_PROTO_BIT_NEC32)
  51
  52struct tango_ir {
  53        void __iomem *rc5_base;
  54        void __iomem *rc6_base;
  55        struct rc_dev *rc;
  56        struct clk *clk;
  57};
  58
  59static void tango_ir_handle_nec(struct tango_ir *ir)
  60{
  61        u32 v, code;
  62        enum rc_proto proto;
  63
  64        v = readl_relaxed(ir->rc5_base + IR_NEC_DATA);
  65        if (!v) {
  66                rc_repeat(ir->rc);
  67                return;
  68        }
  69
  70        code = ir_nec_bytes_to_scancode(v, v >> 8, v >> 16, v >> 24, &proto);
  71        rc_keydown(ir->rc, proto, code, 0);
  72}
  73
  74static void tango_ir_handle_rc5(struct tango_ir *ir)
  75{
  76        u32 data, field, toggle, addr, cmd, code;
  77
  78        data = readl_relaxed(ir->rc5_base + IR_RC5_DATA);
  79        if (data & BIT(31))
  80                return;
  81
  82        field = data >> 12 & 1;
  83        toggle = data >> 11 & 1;
  84        addr = data >> 6 & 0x1f;
  85        cmd = (data & 0x3f) | (field ^ 1) << 6;
  86
  87        code = RC_SCANCODE_RC5(addr, cmd);
  88        rc_keydown(ir->rc, RC_PROTO_RC5, code, toggle);
  89}
  90
  91static void tango_ir_handle_rc6(struct tango_ir *ir)
  92{
  93        u32 data0, data1, toggle, mode, addr, cmd, code;
  94
  95        data0 = readl_relaxed(ir->rc6_base + RC6_DATA0);
  96        data1 = readl_relaxed(ir->rc6_base + RC6_DATA1);
  97
  98        mode = data0 >> 1 & 7;
  99        if (mode != 0)
 100                return;
 101
 102        toggle = data0 & 1;
 103        addr = data0 >> 16;
 104        cmd = data1;
 105
 106        code = RC_SCANCODE_RC6_0(addr, cmd);
 107        rc_keydown(ir->rc, RC_PROTO_RC6_0, code, toggle);
 108}
 109
 110static irqreturn_t tango_ir_irq(int irq, void *dev_id)
 111{
 112        struct tango_ir *ir = dev_id;
 113        unsigned int rc5_stat;
 114        unsigned int rc6_stat;
 115
 116        rc5_stat = readl_relaxed(ir->rc5_base + IR_INT);
 117        writel_relaxed(rc5_stat, ir->rc5_base + IR_INT);
 118
 119        rc6_stat = readl_relaxed(ir->rc6_base + RC6_CTRL);
 120        writel_relaxed(rc6_stat, ir->rc6_base + RC6_CTRL);
 121
 122        if (!(rc5_stat & 3) && !(rc6_stat & BIT(31)))
 123                return IRQ_NONE;
 124
 125        if (rc5_stat & BIT(0))
 126                tango_ir_handle_rc5(ir);
 127
 128        if (rc5_stat & BIT(1))
 129                tango_ir_handle_nec(ir);
 130
 131        if (rc6_stat & BIT(31))
 132                tango_ir_handle_rc6(ir);
 133
 134        return IRQ_HANDLED;
 135}
 136
 137static int tango_change_protocol(struct rc_dev *dev, u64 *rc_type)
 138{
 139        struct tango_ir *ir = dev->priv;
 140        u32 rc5_ctrl = DISABLE_NEC;
 141        u32 rc6_ctrl = 0;
 142
 143        if (*rc_type & NEC_ANY)
 144                rc5_ctrl = 0;
 145
 146        if (*rc_type & RC_PROTO_BIT_RC5)
 147                rc5_ctrl |= ENABLE_RC5;
 148
 149        if (*rc_type & RC_PROTO_BIT_RC6_0)
 150                rc6_ctrl = ENABLE_RC6;
 151
 152        writel_relaxed(rc5_ctrl, ir->rc5_base + IR_CTRL);
 153        writel_relaxed(rc6_ctrl, ir->rc6_base + RC6_CTRL);
 154
 155        return 0;
 156}
 157
 158static int tango_ir_probe(struct platform_device *pdev)
 159{
 160        const char *map_name = RC_MAP_TANGO;
 161        struct device *dev = &pdev->dev;
 162        struct rc_dev *rc;
 163        struct tango_ir *ir;
 164        struct resource *rc5_res;
 165        struct resource *rc6_res;
 166        u64 clkrate, clkdiv;
 167        int irq, err;
 168        u32 val;
 169
 170        rc5_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 171        if (!rc5_res)
 172                return -EINVAL;
 173
 174        rc6_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 175        if (!rc6_res)
 176                return -EINVAL;
 177
 178        irq = platform_get_irq(pdev, 0);
 179        if (irq <= 0)
 180                return -EINVAL;
 181
 182        ir = devm_kzalloc(dev, sizeof(*ir), GFP_KERNEL);
 183        if (!ir)
 184                return -ENOMEM;
 185
 186        ir->rc5_base = devm_ioremap_resource(dev, rc5_res);
 187        if (IS_ERR(ir->rc5_base))
 188                return PTR_ERR(ir->rc5_base);
 189
 190        ir->rc6_base = devm_ioremap_resource(dev, rc6_res);
 191        if (IS_ERR(ir->rc6_base))
 192                return PTR_ERR(ir->rc6_base);
 193
 194        ir->clk = devm_clk_get(dev, NULL);
 195        if (IS_ERR(ir->clk))
 196                return PTR_ERR(ir->clk);
 197
 198        rc = devm_rc_allocate_device(dev, RC_DRIVER_SCANCODE);
 199        if (!rc)
 200                return -ENOMEM;
 201
 202        of_property_read_string(dev->of_node, "linux,rc-map-name", &map_name);
 203
 204        rc->device_name = DRIVER_NAME;
 205        rc->driver_name = DRIVER_NAME;
 206        rc->input_phys = DRIVER_NAME "/input0";
 207        rc->map_name = map_name;
 208        rc->allowed_protocols = NEC_ANY | RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RC6_0;
 209        rc->change_protocol = tango_change_protocol;
 210        rc->priv = ir;
 211        ir->rc = rc;
 212
 213        err = clk_prepare_enable(ir->clk);
 214        if (err)
 215                return err;
 216
 217        clkrate = clk_get_rate(ir->clk);
 218
 219        clkdiv = clkrate * NEC_TIME_BASE;
 220        do_div(clkdiv, 1000000);
 221
 222        val = NEC_CAP(31) | GPIO_SEL(12) | clkdiv;
 223        writel_relaxed(val, ir->rc5_base + IR_NEC_CTRL);
 224
 225        clkdiv = clkrate * RC5_TIME_BASE;
 226        do_div(clkdiv, 1000000);
 227
 228        writel_relaxed(DISABLE_NEC, ir->rc5_base + IR_CTRL);
 229        writel_relaxed(clkdiv, ir->rc5_base + IR_RC5_CLK_DIV);
 230        writel_relaxed(ACK_IR_INT, ir->rc5_base + IR_INT);
 231
 232        clkdiv = clkrate * RC6_TIME_BASE;
 233        do_div(clkdiv, RC6_CARRIER);
 234
 235        writel_relaxed(ACK_RC6_INT, ir->rc6_base + RC6_CTRL);
 236        writel_relaxed((clkdiv >> 2) << 18 | clkdiv, ir->rc6_base + RC6_CLKDIV);
 237
 238        err = devm_request_irq(dev, irq, tango_ir_irq, IRQF_SHARED,
 239                               dev_name(dev), ir);
 240        if (err)
 241                goto err_clk;
 242
 243        err = devm_rc_register_device(dev, rc);
 244        if (err)
 245                goto err_clk;
 246
 247        platform_set_drvdata(pdev, ir);
 248        return 0;
 249
 250err_clk:
 251        clk_disable_unprepare(ir->clk);
 252        return err;
 253}
 254
 255static int tango_ir_remove(struct platform_device *pdev)
 256{
 257        struct tango_ir *ir = platform_get_drvdata(pdev);
 258
 259        clk_disable_unprepare(ir->clk);
 260        return 0;
 261}
 262
 263static const struct of_device_id tango_ir_dt_ids[] = {
 264        { .compatible = "sigma,smp8642-ir" },
 265        { }
 266};
 267MODULE_DEVICE_TABLE(of, tango_ir_dt_ids);
 268
 269static struct platform_driver tango_ir_driver = {
 270        .probe  = tango_ir_probe,
 271        .remove = tango_ir_remove,
 272        .driver = {
 273                .name           = DRIVER_NAME,
 274                .of_match_table = tango_ir_dt_ids,
 275        },
 276};
 277module_platform_driver(tango_ir_driver);
 278
 279MODULE_DESCRIPTION("SMP86xx IR decoder driver");
 280MODULE_AUTHOR("Mans Rullgard <mans@mansr.com>");
 281MODULE_LICENSE("GPL");
 282