linux/drivers/media/rc/zx-irdec.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2017 Sanechips Technology Co., Ltd.
   3 * Copyright 2017 Linaro Ltd.
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License version 2 as
   7 * published by the Free Software Foundation.
   8 */
   9
  10#include <linux/device.h>
  11#include <linux/err.h>
  12#include <linux/interrupt.h>
  13#include <linux/io.h>
  14#include <linux/module.h>
  15#include <linux/of_platform.h>
  16#include <linux/platform_device.h>
  17
  18#include <media/rc-core.h>
  19
  20#define DRIVER_NAME             "zx-irdec"
  21
  22#define ZX_IR_ENABLE            0x04
  23#define ZX_IREN                 BIT(0)
  24#define ZX_IR_CTRL              0x08
  25#define ZX_DEGL_MASK            GENMASK(21, 20)
  26#define ZX_DEGL_VALUE(x)        (((x) << 20) & ZX_DEGL_MASK)
  27#define ZX_WDBEGIN_MASK         GENMASK(18, 8)
  28#define ZX_WDBEGIN_VALUE(x)     (((x) << 8) & ZX_WDBEGIN_MASK)
  29#define ZX_IR_INTEN             0x10
  30#define ZX_IR_INTSTCLR          0x14
  31#define ZX_IR_CODE              0x30
  32#define ZX_IR_CNUM              0x34
  33#define ZX_NECRPT               BIT(16)
  34
  35struct zx_irdec {
  36        void __iomem *base;
  37        struct rc_dev *rcd;
  38};
  39
  40static void zx_irdec_set_mask(struct zx_irdec *irdec, unsigned int reg,
  41                              u32 mask, u32 value)
  42{
  43        u32 data;
  44
  45        data = readl(irdec->base + reg);
  46        data &= ~mask;
  47        data |= value & mask;
  48        writel(data, irdec->base + reg);
  49}
  50
  51static irqreturn_t zx_irdec_irq(int irq, void *dev_id)
  52{
  53        struct zx_irdec *irdec = dev_id;
  54        u8 address, not_address;
  55        u8 command, not_command;
  56        u32 rawcode, scancode;
  57        enum rc_proto rc_proto;
  58
  59        /* Clear interrupt */
  60        writel(1, irdec->base + ZX_IR_INTSTCLR);
  61
  62        /* Check repeat frame */
  63        if (readl(irdec->base + ZX_IR_CNUM) & ZX_NECRPT) {
  64                rc_repeat(irdec->rcd);
  65                goto done;
  66        }
  67
  68        rawcode = readl(irdec->base + ZX_IR_CODE);
  69        not_command = (rawcode >> 24) & 0xff;
  70        command = (rawcode >> 16) & 0xff;
  71        not_address = (rawcode >> 8) & 0xff;
  72        address = rawcode & 0xff;
  73
  74        scancode = ir_nec_bytes_to_scancode(address, not_address,
  75                                            command, not_command,
  76                                            &rc_proto);
  77        rc_keydown(irdec->rcd, rc_proto, scancode, 0);
  78
  79done:
  80        return IRQ_HANDLED;
  81}
  82
  83static int zx_irdec_probe(struct platform_device *pdev)
  84{
  85        struct device *dev = &pdev->dev;
  86        struct zx_irdec *irdec;
  87        struct resource *res;
  88        struct rc_dev *rcd;
  89        int irq;
  90        int ret;
  91
  92        irdec = devm_kzalloc(dev, sizeof(*irdec), GFP_KERNEL);
  93        if (!irdec)
  94                return -ENOMEM;
  95
  96        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  97        irdec->base = devm_ioremap_resource(dev, res);
  98        if (IS_ERR(irdec->base))
  99                return PTR_ERR(irdec->base);
 100
 101        irq = platform_get_irq(pdev, 0);
 102        if (irq < 0)
 103                return irq;
 104
 105        rcd = devm_rc_allocate_device(dev, RC_DRIVER_SCANCODE);
 106        if (!rcd) {
 107                dev_err(dev, "failed to allocate rc device\n");
 108                return -ENOMEM;
 109        }
 110
 111        irdec->rcd = rcd;
 112
 113        rcd->priv = irdec;
 114        rcd->input_phys = DRIVER_NAME "/input0";
 115        rcd->input_id.bustype = BUS_HOST;
 116        rcd->map_name = RC_MAP_ZX_IRDEC;
 117        rcd->allowed_protocols = RC_PROTO_BIT_NEC | RC_PROTO_BIT_NECX |
 118                                                        RC_PROTO_BIT_NEC32;
 119        rcd->driver_name = DRIVER_NAME;
 120        rcd->device_name = DRIVER_NAME;
 121
 122        platform_set_drvdata(pdev, irdec);
 123
 124        ret = devm_rc_register_device(dev, rcd);
 125        if (ret) {
 126                dev_err(dev, "failed to register rc device\n");
 127                return ret;
 128        }
 129
 130        ret = devm_request_irq(dev, irq, zx_irdec_irq, 0, NULL, irdec);
 131        if (ret) {
 132                dev_err(dev, "failed to request irq\n");
 133                return ret;
 134        }
 135
 136        /*
 137         * Initialize deglitch level and watchdog counter beginner as
 138         * recommended by vendor BSP code.
 139         */
 140        zx_irdec_set_mask(irdec, ZX_IR_CTRL, ZX_DEGL_MASK, ZX_DEGL_VALUE(0));
 141        zx_irdec_set_mask(irdec, ZX_IR_CTRL, ZX_WDBEGIN_MASK,
 142                          ZX_WDBEGIN_VALUE(0x21c));
 143
 144        /* Enable interrupt */
 145        writel(1, irdec->base + ZX_IR_INTEN);
 146
 147        /* Enable the decoder */
 148        zx_irdec_set_mask(irdec, ZX_IR_ENABLE, ZX_IREN, ZX_IREN);
 149
 150        return 0;
 151}
 152
 153static int zx_irdec_remove(struct platform_device *pdev)
 154{
 155        struct zx_irdec *irdec = platform_get_drvdata(pdev);
 156
 157        /* Disable the decoder */
 158        zx_irdec_set_mask(irdec, ZX_IR_ENABLE, ZX_IREN, 0);
 159
 160        /* Disable interrupt */
 161        writel(0, irdec->base + ZX_IR_INTEN);
 162
 163        return 0;
 164}
 165
 166static const struct of_device_id zx_irdec_match[] = {
 167        { .compatible = "zte,zx296718-irdec" },
 168        { },
 169};
 170MODULE_DEVICE_TABLE(of, zx_irdec_match);
 171
 172static struct platform_driver zx_irdec_driver = {
 173        .probe = zx_irdec_probe,
 174        .remove = zx_irdec_remove,
 175        .driver = {
 176                .name = DRIVER_NAME,
 177                .of_match_table = zx_irdec_match,
 178        },
 179};
 180module_platform_driver(zx_irdec_driver);
 181
 182MODULE_DESCRIPTION("ZTE ZX IR remote control driver");
 183MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
 184MODULE_LICENSE("GPL v2");
 185