diff options
Diffstat (limited to 'drivers/media/rc/ir-hix5hd2.c')
| -rw-r--r-- | drivers/media/rc/ir-hix5hd2.c | 110 |
1 files changed, 72 insertions, 38 deletions
diff --git a/drivers/media/rc/ir-hix5hd2.c b/drivers/media/rc/ir-hix5hd2.c index abc4d6c1b323..edc46828509c 100644 --- a/drivers/media/rc/ir-hix5hd2.c +++ b/drivers/media/rc/ir-hix5hd2.c @@ -1,10 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014 Linaro Ltd. - * Copyright (c) 2014 Hisilicon Limited. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. + * Copyright (c) 2014 HiSilicon Limited. */ #include <linux/clk.h> @@ -12,7 +9,9 @@ #include <linux/interrupt.h> #include <linux/mfd/syscon.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/property.h> #include <linux/regmap.h> #include <media/rc-core.h> @@ -40,10 +39,13 @@ #define INT_CLR_RCV BIT(16) #define INT_CLR_RCVTIMEOUT (BIT(16) | BIT(17)) -#define IR_CLK 0x48 #define IR_CLK_ENABLE BIT(4) #define IR_CLK_RESET BIT(5) +/* IR_ENABLE register bits */ +#define IR_ENABLE_EN BIT(0) +#define IR_ENABLE_EN_EXTRA BIT(8) + #define IR_CFG_WIDTH_MASK 0xffff #define IR_CFG_WIDTH_SHIFT 16 #define IR_CFG_FORMAT_MASK 0x3 @@ -61,6 +63,23 @@ #define IR_HIX5HD2_NAME "hix5hd2-ir" +/* Need to set extra bit for enabling IR */ +#define HIX5HD2_FLAG_EXTRA_ENABLE BIT(0) + +struct hix5hd2_soc_data { + u32 clk_reg; + u32 flags; +}; + +static const struct hix5hd2_soc_data hix5hd2_data = { + .clk_reg = 0x48, +}; + +static const struct hix5hd2_soc_data hi3796cv300_data = { + .clk_reg = 0x60, + .flags = HIX5HD2_FLAG_EXTRA_ENABLE, +}; + struct hix5hd2_ir_priv { int irq; void __iomem *base; @@ -69,15 +88,17 @@ struct hix5hd2_ir_priv { struct regmap *regmap; struct clk *clock; unsigned long rate; + const struct hix5hd2_soc_data *socdata; }; -static int hix5hd2_ir_enable(struct hix5hd2_ir_priv *dev, bool on) +static int hix5hd2_ir_clk_enable(struct hix5hd2_ir_priv *dev, bool on) { + u32 clk_reg = dev->socdata->clk_reg; u32 val; int ret = 0; if (dev->regmap) { - regmap_read(dev->regmap, IR_CLK, &val); + regmap_read(dev->regmap, clk_reg, &val); if (on) { val &= ~IR_CLK_RESET; val |= IR_CLK_ENABLE; @@ -85,7 +106,7 @@ static int hix5hd2_ir_enable(struct hix5hd2_ir_priv *dev, bool on) val &= ~IR_CLK_ENABLE; val |= IR_CLK_RESET; } - regmap_write(dev->regmap, IR_CLK, val); + regmap_write(dev->regmap, clk_reg, val); } else { if (on) ret = clk_prepare_enable(dev->clock); @@ -95,12 +116,23 @@ static int hix5hd2_ir_enable(struct hix5hd2_ir_priv *dev, bool on) return ret; } +static inline void hix5hd2_ir_enable(struct hix5hd2_ir_priv *priv) +{ + u32 val = IR_ENABLE_EN; + + if (priv->socdata->flags & HIX5HD2_FLAG_EXTRA_ENABLE) + val |= IR_ENABLE_EN_EXTRA; + + writel_relaxed(val, priv->base + IR_ENABLE); +} + static int hix5hd2_ir_config(struct hix5hd2_ir_priv *priv) { int timeout = 10000; u32 val, rate; - writel_relaxed(0x01, priv->base + IR_ENABLE); + hix5hd2_ir_enable(priv); + while (readl_relaxed(priv->base + IR_BUSY)) { if (timeout--) { udelay(1); @@ -131,13 +163,13 @@ static int hix5hd2_ir_open(struct rc_dev *rdev) struct hix5hd2_ir_priv *priv = rdev->priv; int ret; - ret = hix5hd2_ir_enable(priv, true); + ret = hix5hd2_ir_clk_enable(priv, true); if (ret) return ret; ret = hix5hd2_ir_config(priv); if (ret) { - hix5hd2_ir_enable(priv, false); + hix5hd2_ir_clk_enable(priv, false); return ret; } return 0; @@ -147,7 +179,7 @@ static void hix5hd2_ir_close(struct rc_dev *rdev) { struct hix5hd2_ir_priv *priv = rdev->priv; - hix5hd2_ir_enable(priv, false); + hix5hd2_ir_clk_enable(priv, false); } static irqreturn_t hix5hd2_ir_rx_interrupt(int irq, void *data) @@ -164,7 +196,7 @@ static irqreturn_t hix5hd2_ir_rx_interrupt(int irq, void *data) * IR_INTS availably since logic would not clear * fifo when overflow, drv do the job */ - ir_raw_event_reset(priv->rdev); + ir_raw_event_overflow(priv->rdev); symb_num = readl_relaxed(priv->base + IR_DATAH); for (i = 0; i < symb_num; i++) readl_relaxed(priv->base + IR_DATAL); @@ -184,12 +216,12 @@ static irqreturn_t hix5hd2_ir_rx_interrupt(int irq, void *data) data_h = ((symb_val >> 16) & 0xffff) * 10; symb_time = (data_l + data_h) / 10; - ev.duration = US_TO_NS(data_l); + ev.duration = data_l; ev.pulse = true; ir_raw_event_store(priv->rdev, &ev); if (symb_time < IR_CFG_SYMBOL_MAXWIDTH) { - ev.duration = US_TO_NS(data_h); + ev.duration = data_h; ev.pulse = false; ir_raw_event_store(priv->rdev, &ev); } else { @@ -208,11 +240,17 @@ static irqreturn_t hix5hd2_ir_rx_interrupt(int irq, void *data) return IRQ_HANDLED; } +static const struct of_device_id hix5hd2_ir_table[] = { + { .compatible = "hisilicon,hix5hd2-ir", &hix5hd2_data, }, + { .compatible = "hisilicon,hi3796cv300-ir", &hi3796cv300_data, }, + {}, +}; +MODULE_DEVICE_TABLE(of, hix5hd2_ir_table); + static int hix5hd2_ir_probe(struct platform_device *pdev) { struct rc_dev *rdev; struct device *dev = &pdev->dev; - struct resource *res; struct hix5hd2_ir_priv *priv; struct device_node *node = pdev->dev.of_node; const char *map_name; @@ -222,6 +260,12 @@ static int hix5hd2_ir_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; + priv->socdata = device_get_match_data(dev); + if (!priv->socdata) { + dev_err(dev, "Unable to initialize IR data\n"); + return -ENODEV; + } + priv->regmap = syscon_regmap_lookup_by_phandle(node, "hisilicon,power-syscon"); if (IS_ERR(priv->regmap)) { @@ -229,16 +273,13 @@ static int hix5hd2_ir_probe(struct platform_device *pdev) priv->regmap = NULL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(dev, res); + priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); priv->irq = platform_get_irq(pdev, 0); - if (priv->irq < 0) { - dev_err(dev, "irq can not get\n"); + if (priv->irq < 0) return priv->irq; - } rdev = rc_allocate_device(RC_DRIVER_IR_RAW); if (!rdev) @@ -268,8 +309,8 @@ static int hix5hd2_ir_probe(struct platform_device *pdev) rdev->input_id.vendor = 0x0001; rdev->input_id.product = 0x0001; rdev->input_id.version = 0x0100; - rdev->rx_resolution = US_TO_NS(10); - rdev->timeout = US_TO_NS(IR_CFG_SYMBOL_MAXWIDTH * 10); + rdev->rx_resolution = 10; + rdev->timeout = IR_CFG_SYMBOL_MAXWIDTH * 10; ret = rc_register_device(rdev); if (ret < 0) @@ -299,13 +340,12 @@ err: return ret; } -static int hix5hd2_ir_remove(struct platform_device *pdev) +static void hix5hd2_ir_remove(struct platform_device *pdev) { struct hix5hd2_ir_priv *priv = platform_get_drvdata(pdev); clk_disable_unprepare(priv->clock); rc_unregister_device(priv->rdev); - return 0; } #ifdef CONFIG_PM_SLEEP @@ -314,7 +354,7 @@ static int hix5hd2_ir_suspend(struct device *dev) struct hix5hd2_ir_priv *priv = dev_get_drvdata(dev); clk_disable_unprepare(priv->clock); - hix5hd2_ir_enable(priv, false); + hix5hd2_ir_clk_enable(priv, false); return 0; } @@ -324,17 +364,18 @@ static int hix5hd2_ir_resume(struct device *dev) struct hix5hd2_ir_priv *priv = dev_get_drvdata(dev); int ret; - ret = hix5hd2_ir_enable(priv, true); + ret = hix5hd2_ir_clk_enable(priv, true); if (ret) return ret; ret = clk_prepare_enable(priv->clock); if (ret) { - hix5hd2_ir_enable(priv, false); + hix5hd2_ir_clk_enable(priv, false); return ret; } - writel_relaxed(0x01, priv->base + IR_ENABLE); + hix5hd2_ir_enable(priv); + writel_relaxed(0x00, priv->base + IR_INTM); writel_relaxed(0xff, priv->base + IR_INTC); writel_relaxed(0x01, priv->base + IR_START); @@ -346,12 +387,6 @@ static int hix5hd2_ir_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(hix5hd2_ir_pm_ops, hix5hd2_ir_suspend, hix5hd2_ir_resume); -static const struct of_device_id hix5hd2_ir_table[] = { - { .compatible = "hisilicon,hix5hd2-ir", }, - {}, -}; -MODULE_DEVICE_TABLE(of, hix5hd2_ir_table); - static struct platform_driver hix5hd2_ir_driver = { .driver = { .name = IR_HIX5HD2_NAME, @@ -367,4 +402,3 @@ module_platform_driver(hix5hd2_ir_driver); MODULE_DESCRIPTION("IR controller driver for hix5hd2 platforms"); MODULE_AUTHOR("Guoxiong Yan <yanguoxiong@huawei.com>"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:hix5hd2-ir"); |
