linux/drivers/media/rc/ir-hix5hd2.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2014 Linaro Ltd.
   3 * Copyright (c) 2014 Hisilicon Limited.
   4 *
   5 * This program is free software; you can redistribute it and/or modify it
   6 * under the terms and conditions of the GNU General Public License,
   7 * version 2, as published by the Free Software Foundation.
   8 */
   9
  10#include <linux/clk.h>
  11#include <linux/delay.h>
  12#include <linux/interrupt.h>
  13#include <linux/mfd/syscon.h>
  14#include <linux/module.h>
  15#include <linux/of_device.h>
  16#include <linux/regmap.h>
  17#include <media/rc-core.h>
  18
  19#define IR_ENABLE               0x00
  20#define IR_CONFIG               0x04
  21#define CNT_LEADS               0x08
  22#define CNT_LEADE               0x0c
  23#define CNT_SLEADE              0x10
  24#define CNT0_B                  0x14
  25#define CNT1_B                  0x18
  26#define IR_BUSY                 0x1c
  27#define IR_DATAH                0x20
  28#define IR_DATAL                0x24
  29#define IR_INTM                 0x28
  30#define IR_INTS                 0x2c
  31#define IR_INTC                 0x30
  32#define IR_START                0x34
  33
  34/* interrupt mask */
  35#define INTMS_SYMBRCV           (BIT(24) | BIT(8))
  36#define INTMS_TIMEOUT           (BIT(25) | BIT(9))
  37#define INTMS_OVERFLOW          (BIT(26) | BIT(10))
  38#define INT_CLR_OVERFLOW        BIT(18)
  39#define INT_CLR_TIMEOUT         BIT(17)
  40#define INT_CLR_RCV             BIT(16)
  41#define INT_CLR_RCVTIMEOUT      (BIT(16) | BIT(17))
  42
  43#define IR_CLK                  0x48
  44#define IR_CLK_ENABLE           BIT(4)
  45#define IR_CLK_RESET            BIT(5)
  46
  47#define IR_CFG_WIDTH_MASK       0xffff
  48#define IR_CFG_WIDTH_SHIFT      16
  49#define IR_CFG_FORMAT_MASK      0x3
  50#define IR_CFG_FORMAT_SHIFT     14
  51#define IR_CFG_INT_LEVEL_MASK   0x3f
  52#define IR_CFG_INT_LEVEL_SHIFT  8
  53/* only support raw mode */
  54#define IR_CFG_MODE_RAW         BIT(7)
  55#define IR_CFG_FREQ_MASK        0x7f
  56#define IR_CFG_FREQ_SHIFT       0
  57#define IR_CFG_INT_THRESHOLD    1
  58/* symbol start from low to high, symbol stream end at high*/
  59#define IR_CFG_SYMBOL_FMT       0
  60#define IR_CFG_SYMBOL_MAXWIDTH  0x3e80
  61
  62#define IR_HIX5HD2_NAME         "hix5hd2-ir"
  63
  64struct hix5hd2_ir_priv {
  65        int                     irq;
  66        void __iomem            *base;
  67        struct device           *dev;
  68        struct rc_dev           *rdev;
  69        struct regmap           *regmap;
  70        struct clk              *clock;
  71        unsigned long           rate;
  72};
  73
  74static int hix5hd2_ir_enable(struct hix5hd2_ir_priv *dev, bool on)
  75{
  76        u32 val;
  77        int ret = 0;
  78
  79        if (dev->regmap) {
  80                regmap_read(dev->regmap, IR_CLK, &val);
  81                if (on) {
  82                        val &= ~IR_CLK_RESET;
  83                        val |= IR_CLK_ENABLE;
  84                } else {
  85                        val &= ~IR_CLK_ENABLE;
  86                        val |= IR_CLK_RESET;
  87                }
  88                regmap_write(dev->regmap, IR_CLK, val);
  89        } else {
  90                if (on)
  91                        ret = clk_prepare_enable(dev->clock);
  92                else
  93                        clk_disable_unprepare(dev->clock);
  94        }
  95        return ret;
  96}
  97
  98static int hix5hd2_ir_config(struct hix5hd2_ir_priv *priv)
  99{
 100        int timeout = 10000;
 101        u32 val, rate;
 102
 103        writel_relaxed(0x01, priv->base + IR_ENABLE);
 104        while (readl_relaxed(priv->base + IR_BUSY)) {
 105                if (timeout--) {
 106                        udelay(1);
 107                } else {
 108                        dev_err(priv->dev, "IR_BUSY timeout\n");
 109                        return -ETIMEDOUT;
 110                }
 111        }
 112
 113        /* Now only support raw mode, with symbol start from low to high */
 114        rate = DIV_ROUND_CLOSEST(priv->rate, 1000000);
 115        val = IR_CFG_SYMBOL_MAXWIDTH & IR_CFG_WIDTH_MASK << IR_CFG_WIDTH_SHIFT;
 116        val |= IR_CFG_SYMBOL_FMT & IR_CFG_FORMAT_MASK << IR_CFG_FORMAT_SHIFT;
 117        val |= (IR_CFG_INT_THRESHOLD - 1) & IR_CFG_INT_LEVEL_MASK
 118               << IR_CFG_INT_LEVEL_SHIFT;
 119        val |= IR_CFG_MODE_RAW;
 120        val |= (rate - 1) & IR_CFG_FREQ_MASK << IR_CFG_FREQ_SHIFT;
 121        writel_relaxed(val, priv->base + IR_CONFIG);
 122
 123        writel_relaxed(0x00, priv->base + IR_INTM);
 124        /* write arbitrary value to start  */
 125        writel_relaxed(0x01, priv->base + IR_START);
 126        return 0;
 127}
 128
 129static int hix5hd2_ir_open(struct rc_dev *rdev)
 130{
 131        struct hix5hd2_ir_priv *priv = rdev->priv;
 132        int ret;
 133
 134        ret = hix5hd2_ir_enable(priv, true);
 135        if (ret)
 136                return ret;
 137
 138        ret = hix5hd2_ir_config(priv);
 139        if (ret) {
 140                hix5hd2_ir_enable(priv, false);
 141                return ret;
 142        }
 143        return 0;
 144}
 145
 146static void hix5hd2_ir_close(struct rc_dev *rdev)
 147{
 148        struct hix5hd2_ir_priv *priv = rdev->priv;
 149
 150        hix5hd2_ir_enable(priv, false);
 151}
 152
 153static irqreturn_t hix5hd2_ir_rx_interrupt(int irq, void *data)
 154{
 155        u32 symb_num, symb_val, symb_time;
 156        u32 data_l, data_h;
 157        u32 irq_sr, i;
 158        struct hix5hd2_ir_priv *priv = data;
 159
 160        irq_sr = readl_relaxed(priv->base + IR_INTS);
 161        if (irq_sr & INTMS_OVERFLOW) {
 162                /*
 163                 * we must read IR_DATAL first, then we can clean up
 164                 * IR_INTS availably since logic would not clear
 165                 * fifo when overflow, drv do the job
 166                 */
 167                ir_raw_event_reset(priv->rdev);
 168                symb_num = readl_relaxed(priv->base + IR_DATAH);
 169                for (i = 0; i < symb_num; i++)
 170                        readl_relaxed(priv->base + IR_DATAL);
 171
 172                writel_relaxed(INT_CLR_OVERFLOW, priv->base + IR_INTC);
 173                dev_info(priv->dev, "overflow, level=%d\n",
 174                         IR_CFG_INT_THRESHOLD);
 175        }
 176
 177        if ((irq_sr & INTMS_SYMBRCV) || (irq_sr & INTMS_TIMEOUT)) {
 178                DEFINE_IR_RAW_EVENT(ev);
 179
 180                symb_num = readl_relaxed(priv->base + IR_DATAH);
 181                for (i = 0; i < symb_num; i++) {
 182                        symb_val = readl_relaxed(priv->base + IR_DATAL);
 183                        data_l = ((symb_val & 0xffff) * 10);
 184                        data_h =  ((symb_val >> 16) & 0xffff) * 10;
 185                        symb_time = (data_l + data_h) / 10;
 186
 187                        ev.duration = US_TO_NS(data_l);
 188                        ev.pulse = true;
 189                        ir_raw_event_store(priv->rdev, &ev);
 190
 191                        if (symb_time < IR_CFG_SYMBOL_MAXWIDTH) {
 192                                ev.duration = US_TO_NS(data_h);
 193                                ev.pulse = false;
 194                                ir_raw_event_store(priv->rdev, &ev);
 195                        } else {
 196                                ir_raw_event_set_idle(priv->rdev, true);
 197                        }
 198                }
 199
 200                if (irq_sr & INTMS_SYMBRCV)
 201                        writel_relaxed(INT_CLR_RCV, priv->base + IR_INTC);
 202                if (irq_sr & INTMS_TIMEOUT)
 203                        writel_relaxed(INT_CLR_TIMEOUT, priv->base + IR_INTC);
 204        }
 205
 206        /* Empty software fifo */
 207        ir_raw_event_handle(priv->rdev);
 208        return IRQ_HANDLED;
 209}
 210
 211static int hix5hd2_ir_probe(struct platform_device *pdev)
 212{
 213        struct rc_dev *rdev;
 214        struct device *dev = &pdev->dev;
 215        struct resource *res;
 216        struct hix5hd2_ir_priv *priv;
 217        struct device_node *node = pdev->dev.of_node;
 218        const char *map_name;
 219        int ret;
 220
 221        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 222        if (!priv)
 223                return -ENOMEM;
 224
 225        priv->regmap = syscon_regmap_lookup_by_phandle(node,
 226                                                       "hisilicon,power-syscon");
 227        if (IS_ERR(priv->regmap)) {
 228                dev_info(dev, "no power-reg\n");
 229                priv->regmap = NULL;
 230        }
 231
 232        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 233        priv->base = devm_ioremap_resource(dev, res);
 234        if (IS_ERR(priv->base))
 235                return PTR_ERR(priv->base);
 236
 237        priv->irq = platform_get_irq(pdev, 0);
 238        if (priv->irq < 0) {
 239                dev_err(dev, "irq can not get\n");
 240                return priv->irq;
 241        }
 242
 243        rdev = rc_allocate_device(RC_DRIVER_IR_RAW);
 244        if (!rdev)
 245                return -ENOMEM;
 246
 247        priv->clock = devm_clk_get(dev, NULL);
 248        if (IS_ERR(priv->clock)) {
 249                dev_err(dev, "clock not found\n");
 250                ret = PTR_ERR(priv->clock);
 251                goto err;
 252        }
 253        ret = clk_prepare_enable(priv->clock);
 254        if (ret)
 255                goto err;
 256        priv->rate = clk_get_rate(priv->clock);
 257
 258        rdev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
 259        rdev->priv = priv;
 260        rdev->open = hix5hd2_ir_open;
 261        rdev->close = hix5hd2_ir_close;
 262        rdev->driver_name = IR_HIX5HD2_NAME;
 263        map_name = of_get_property(node, "linux,rc-map-name", NULL);
 264        rdev->map_name = map_name ?: RC_MAP_EMPTY;
 265        rdev->device_name = IR_HIX5HD2_NAME;
 266        rdev->input_phys = IR_HIX5HD2_NAME "/input0";
 267        rdev->input_id.bustype = BUS_HOST;
 268        rdev->input_id.vendor = 0x0001;
 269        rdev->input_id.product = 0x0001;
 270        rdev->input_id.version = 0x0100;
 271        rdev->rx_resolution = US_TO_NS(10);
 272        rdev->timeout = US_TO_NS(IR_CFG_SYMBOL_MAXWIDTH * 10);
 273
 274        ret = rc_register_device(rdev);
 275        if (ret < 0)
 276                goto clkerr;
 277
 278        if (devm_request_irq(dev, priv->irq, hix5hd2_ir_rx_interrupt,
 279                             0, pdev->name, priv) < 0) {
 280                dev_err(dev, "IRQ %d register failed\n", priv->irq);
 281                ret = -EINVAL;
 282                goto regerr;
 283        }
 284
 285        priv->rdev = rdev;
 286        priv->dev = dev;
 287        platform_set_drvdata(pdev, priv);
 288
 289        return ret;
 290
 291regerr:
 292        rc_unregister_device(rdev);
 293        rdev = NULL;
 294clkerr:
 295        clk_disable_unprepare(priv->clock);
 296err:
 297        rc_free_device(rdev);
 298        dev_err(dev, "Unable to register device (%d)\n", ret);
 299        return ret;
 300}
 301
 302static int hix5hd2_ir_remove(struct platform_device *pdev)
 303{
 304        struct hix5hd2_ir_priv *priv = platform_get_drvdata(pdev);
 305
 306        clk_disable_unprepare(priv->clock);
 307        rc_unregister_device(priv->rdev);
 308        return 0;
 309}
 310
 311#ifdef CONFIG_PM_SLEEP
 312static int hix5hd2_ir_suspend(struct device *dev)
 313{
 314        struct hix5hd2_ir_priv *priv = dev_get_drvdata(dev);
 315
 316        clk_disable_unprepare(priv->clock);
 317        hix5hd2_ir_enable(priv, false);
 318
 319        return 0;
 320}
 321
 322static int hix5hd2_ir_resume(struct device *dev)
 323{
 324        struct hix5hd2_ir_priv *priv = dev_get_drvdata(dev);
 325        int ret;
 326
 327        ret = hix5hd2_ir_enable(priv, true);
 328        if (ret)
 329                return ret;
 330
 331        ret = clk_prepare_enable(priv->clock);
 332        if (ret) {
 333                hix5hd2_ir_enable(priv, false);
 334                return ret;
 335        }
 336
 337        writel_relaxed(0x01, priv->base + IR_ENABLE);
 338        writel_relaxed(0x00, priv->base + IR_INTM);
 339        writel_relaxed(0xff, priv->base + IR_INTC);
 340        writel_relaxed(0x01, priv->base + IR_START);
 341
 342        return 0;
 343}
 344#endif
 345
 346static SIMPLE_DEV_PM_OPS(hix5hd2_ir_pm_ops, hix5hd2_ir_suspend,
 347                         hix5hd2_ir_resume);
 348
 349static const struct of_device_id hix5hd2_ir_table[] = {
 350        { .compatible = "hisilicon,hix5hd2-ir", },
 351        {},
 352};
 353MODULE_DEVICE_TABLE(of, hix5hd2_ir_table);
 354
 355static struct platform_driver hix5hd2_ir_driver = {
 356        .driver = {
 357                .name = IR_HIX5HD2_NAME,
 358                .of_match_table = hix5hd2_ir_table,
 359                .pm     = &hix5hd2_ir_pm_ops,
 360        },
 361        .probe = hix5hd2_ir_probe,
 362        .remove = hix5hd2_ir_remove,
 363};
 364
 365module_platform_driver(hix5hd2_ir_driver);
 366
 367MODULE_DESCRIPTION("IR controller driver for hix5hd2 platforms");
 368MODULE_AUTHOR("Guoxiong Yan <yanguoxiong@huawei.com>");
 369MODULE_LICENSE("GPL v2");
 370MODULE_ALIAS("platform:hix5hd2-ir");
 371