linux/drivers/media/rc/img-ir/img-ir-core.c
<<
>>
Prefs
   1/*
   2 * ImgTec IR Decoder found in PowerDown Controller.
   3 *
   4 * Copyright 2010-2014 Imagination Technologies Ltd.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by the
   8 * Free Software Foundation; either version 2 of the License, or (at your
   9 * option) any later version.
  10 *
  11 * This contains core img-ir code for setting up the driver. The two interfaces
  12 * (raw and hardware decode) are handled separately.
  13 */
  14
  15#include <linux/clk.h>
  16#include <linux/init.h>
  17#include <linux/interrupt.h>
  18#include <linux/io.h>
  19#include <linux/module.h>
  20#include <linux/platform_device.h>
  21#include <linux/slab.h>
  22#include <linux/spinlock.h>
  23#include "img-ir.h"
  24
  25static irqreturn_t img_ir_isr(int irq, void *dev_id)
  26{
  27        struct img_ir_priv *priv = dev_id;
  28        u32 irq_status;
  29
  30        spin_lock(&priv->lock);
  31        /* we have to clear irqs before reading */
  32        irq_status = img_ir_read(priv, IMG_IR_IRQ_STATUS);
  33        img_ir_write(priv, IMG_IR_IRQ_CLEAR, irq_status);
  34
  35        /* don't handle valid data irqs if we're only interested in matches */
  36        irq_status &= img_ir_read(priv, IMG_IR_IRQ_ENABLE);
  37
  38        /* hand off edge interrupts to raw decode handler */
  39        if (irq_status & IMG_IR_IRQ_EDGE && img_ir_raw_enabled(&priv->raw))
  40                img_ir_isr_raw(priv, irq_status);
  41
  42        /* hand off hardware match interrupts to hardware decode handler */
  43        if (irq_status & (IMG_IR_IRQ_DATA_MATCH |
  44                          IMG_IR_IRQ_DATA_VALID |
  45                          IMG_IR_IRQ_DATA2_VALID) &&
  46            img_ir_hw_enabled(&priv->hw))
  47                img_ir_isr_hw(priv, irq_status);
  48
  49        spin_unlock(&priv->lock);
  50        return IRQ_HANDLED;
  51}
  52
  53static void img_ir_setup(struct img_ir_priv *priv)
  54{
  55        /* start off with interrupts disabled */
  56        img_ir_write(priv, IMG_IR_IRQ_ENABLE, 0);
  57
  58        img_ir_setup_raw(priv);
  59        img_ir_setup_hw(priv);
  60
  61        if (!IS_ERR(priv->clk))
  62                clk_prepare_enable(priv->clk);
  63}
  64
  65static void img_ir_ident(struct img_ir_priv *priv)
  66{
  67        u32 core_rev = img_ir_read(priv, IMG_IR_CORE_REV);
  68
  69        dev_info(priv->dev,
  70                 "IMG IR Decoder (%d.%d.%d.%d) probed successfully\n",
  71                 (core_rev & IMG_IR_DESIGNER) >> IMG_IR_DESIGNER_SHIFT,
  72                 (core_rev & IMG_IR_MAJOR_REV) >> IMG_IR_MAJOR_REV_SHIFT,
  73                 (core_rev & IMG_IR_MINOR_REV) >> IMG_IR_MINOR_REV_SHIFT,
  74                 (core_rev & IMG_IR_MAINT_REV) >> IMG_IR_MAINT_REV_SHIFT);
  75        dev_info(priv->dev, "Modes:%s%s\n",
  76                 img_ir_hw_enabled(&priv->hw) ? " hardware" : "",
  77                 img_ir_raw_enabled(&priv->raw) ? " raw" : "");
  78}
  79
  80static int img_ir_probe(struct platform_device *pdev)
  81{
  82        struct img_ir_priv *priv;
  83        struct resource *res_regs;
  84        int irq, error, error2;
  85
  86        /* Get resources from platform device */
  87        irq = platform_get_irq(pdev, 0);
  88        if (irq < 0) {
  89                dev_err(&pdev->dev, "cannot find IRQ resource\n");
  90                return irq;
  91        }
  92
  93        /* Private driver data */
  94        priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
  95        if (!priv)
  96                return -ENOMEM;
  97
  98        platform_set_drvdata(pdev, priv);
  99        priv->dev = &pdev->dev;
 100        spin_lock_init(&priv->lock);
 101
 102        /* Ioremap the registers */
 103        res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 104        priv->reg_base = devm_ioremap_resource(&pdev->dev, res_regs);
 105        if (IS_ERR(priv->reg_base))
 106                return PTR_ERR(priv->reg_base);
 107
 108        /* Get core clock */
 109        priv->clk = devm_clk_get(&pdev->dev, "core");
 110        if (IS_ERR(priv->clk))
 111                dev_warn(&pdev->dev, "cannot get core clock resource\n");
 112
 113        /* Get sys clock */
 114        priv->sys_clk = devm_clk_get(&pdev->dev, "sys");
 115        if (IS_ERR(priv->sys_clk))
 116                dev_warn(&pdev->dev, "cannot get sys clock resource\n");
 117        /*
 118         * Enabling the system clock before the register interface is
 119         * accessed. ISR shouldn't get called with Sys Clock disabled,
 120         * hence exiting probe with an error.
 121         */
 122        if (!IS_ERR(priv->sys_clk)) {
 123                error = clk_prepare_enable(priv->sys_clk);
 124                if (error) {
 125                        dev_err(&pdev->dev, "cannot enable sys clock\n");
 126                        return error;
 127                }
 128        }
 129
 130        /* Set up raw & hw decoder */
 131        error = img_ir_probe_raw(priv);
 132        error2 = img_ir_probe_hw(priv);
 133        if (error && error2) {
 134                if (error == -ENODEV)
 135                        error = error2;
 136                goto err_probe;
 137        }
 138
 139        /* Get the IRQ */
 140        priv->irq = irq;
 141        error = request_irq(priv->irq, img_ir_isr, 0, "img-ir", priv);
 142        if (error) {
 143                dev_err(&pdev->dev, "cannot register IRQ %u\n",
 144                        priv->irq);
 145                error = -EIO;
 146                goto err_irq;
 147        }
 148
 149        img_ir_ident(priv);
 150        img_ir_setup(priv);
 151
 152        return 0;
 153
 154err_irq:
 155        img_ir_remove_hw(priv);
 156        img_ir_remove_raw(priv);
 157err_probe:
 158        if (!IS_ERR(priv->sys_clk))
 159                clk_disable_unprepare(priv->sys_clk);
 160        return error;
 161}
 162
 163static int img_ir_remove(struct platform_device *pdev)
 164{
 165        struct img_ir_priv *priv = platform_get_drvdata(pdev);
 166
 167        free_irq(priv->irq, priv);
 168        img_ir_remove_hw(priv);
 169        img_ir_remove_raw(priv);
 170
 171        if (!IS_ERR(priv->clk))
 172                clk_disable_unprepare(priv->clk);
 173        if (!IS_ERR(priv->sys_clk))
 174                clk_disable_unprepare(priv->sys_clk);
 175        return 0;
 176}
 177
 178static SIMPLE_DEV_PM_OPS(img_ir_pmops, img_ir_suspend, img_ir_resume);
 179
 180static const struct of_device_id img_ir_match[] = {
 181        { .compatible = "img,ir-rev1" },
 182        {}
 183};
 184MODULE_DEVICE_TABLE(of, img_ir_match);
 185
 186static struct platform_driver img_ir_driver = {
 187        .driver = {
 188                .name = "img-ir",
 189                .of_match_table = img_ir_match,
 190                .pm = &img_ir_pmops,
 191        },
 192        .probe = img_ir_probe,
 193        .remove = img_ir_remove,
 194};
 195
 196module_platform_driver(img_ir_driver);
 197
 198MODULE_AUTHOR("Imagination Technologies Ltd.");
 199MODULE_DESCRIPTION("ImgTec IR");
 200MODULE_LICENSE("GPL");
 201