linux/drivers/gpu/drm/mediatek/mtk_cec.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2014 MediaTek Inc.
   4 * Author: Jie Qiu <jie.qiu@mediatek.com>
   5 */
   6#include <linux/clk.h>
   7#include <linux/delay.h>
   8#include <linux/io.h>
   9#include <linux/interrupt.h>
  10#include <linux/module.h>
  11#include <linux/mod_devicetable.h>
  12#include <linux/platform_device.h>
  13
  14#include "mtk_cec.h"
  15
  16#define TR_CONFIG               0x00
  17#define CLEAR_CEC_IRQ                   BIT(15)
  18
  19#define CEC_CKGEN               0x04
  20#define CEC_32K_PDN                     BIT(19)
  21#define PDN                             BIT(16)
  22
  23#define RX_EVENT                0x54
  24#define HDMI_PORD                       BIT(25)
  25#define HDMI_HTPLG                      BIT(24)
  26#define HDMI_PORD_INT_EN                BIT(9)
  27#define HDMI_HTPLG_INT_EN               BIT(8)
  28
  29#define RX_GEN_WD               0x58
  30#define HDMI_PORD_INT_32K_STATUS        BIT(26)
  31#define RX_RISC_INT_32K_STATUS          BIT(25)
  32#define HDMI_HTPLG_INT_32K_STATUS       BIT(24)
  33#define HDMI_PORD_INT_32K_CLR           BIT(18)
  34#define RX_INT_32K_CLR                  BIT(17)
  35#define HDMI_HTPLG_INT_32K_CLR          BIT(16)
  36#define HDMI_PORD_INT_32K_STA_MASK      BIT(10)
  37#define RX_RISC_INT_32K_STA_MASK        BIT(9)
  38#define HDMI_HTPLG_INT_32K_STA_MASK     BIT(8)
  39#define HDMI_PORD_INT_32K_EN            BIT(2)
  40#define RX_INT_32K_EN                   BIT(1)
  41#define HDMI_HTPLG_INT_32K_EN           BIT(0)
  42
  43#define NORMAL_INT_CTRL         0x5C
  44#define HDMI_HTPLG_INT_STA              BIT(0)
  45#define HDMI_PORD_INT_STA               BIT(1)
  46#define HDMI_HTPLG_INT_CLR              BIT(16)
  47#define HDMI_PORD_INT_CLR               BIT(17)
  48#define HDMI_FULL_INT_CLR               BIT(20)
  49
  50struct mtk_cec {
  51        void __iomem *regs;
  52        struct clk *clk;
  53        int irq;
  54        bool hpd;
  55        void (*hpd_event)(bool hpd, struct device *dev);
  56        struct device *hdmi_dev;
  57        spinlock_t lock;
  58};
  59
  60static void mtk_cec_clear_bits(struct mtk_cec *cec, unsigned int offset,
  61                               unsigned int bits)
  62{
  63        void __iomem *reg = cec->regs + offset;
  64        u32 tmp;
  65
  66        tmp = readl(reg);
  67        tmp &= ~bits;
  68        writel(tmp, reg);
  69}
  70
  71static void mtk_cec_set_bits(struct mtk_cec *cec, unsigned int offset,
  72                             unsigned int bits)
  73{
  74        void __iomem *reg = cec->regs + offset;
  75        u32 tmp;
  76
  77        tmp = readl(reg);
  78        tmp |= bits;
  79        writel(tmp, reg);
  80}
  81
  82static void mtk_cec_mask(struct mtk_cec *cec, unsigned int offset,
  83                         unsigned int val, unsigned int mask)
  84{
  85        u32 tmp = readl(cec->regs + offset) & ~mask;
  86
  87        tmp |= val & mask;
  88        writel(val, cec->regs + offset);
  89}
  90
  91void mtk_cec_set_hpd_event(struct device *dev,
  92                           void (*hpd_event)(bool hpd, struct device *dev),
  93                           struct device *hdmi_dev)
  94{
  95        struct mtk_cec *cec = dev_get_drvdata(dev);
  96        unsigned long flags;
  97
  98        spin_lock_irqsave(&cec->lock, flags);
  99        cec->hdmi_dev = hdmi_dev;
 100        cec->hpd_event = hpd_event;
 101        spin_unlock_irqrestore(&cec->lock, flags);
 102}
 103
 104bool mtk_cec_hpd_high(struct device *dev)
 105{
 106        struct mtk_cec *cec = dev_get_drvdata(dev);
 107        unsigned int status;
 108
 109        status = readl(cec->regs + RX_EVENT);
 110
 111        return (status & (HDMI_PORD | HDMI_HTPLG)) == (HDMI_PORD | HDMI_HTPLG);
 112}
 113
 114static void mtk_cec_htplg_irq_init(struct mtk_cec *cec)
 115{
 116        mtk_cec_mask(cec, CEC_CKGEN, 0 | CEC_32K_PDN, PDN | CEC_32K_PDN);
 117        mtk_cec_set_bits(cec, RX_GEN_WD, HDMI_PORD_INT_32K_CLR |
 118                         RX_INT_32K_CLR | HDMI_HTPLG_INT_32K_CLR);
 119        mtk_cec_mask(cec, RX_GEN_WD, 0, HDMI_PORD_INT_32K_CLR | RX_INT_32K_CLR |
 120                     HDMI_HTPLG_INT_32K_CLR | HDMI_PORD_INT_32K_EN |
 121                     RX_INT_32K_EN | HDMI_HTPLG_INT_32K_EN);
 122}
 123
 124static void mtk_cec_htplg_irq_enable(struct mtk_cec *cec)
 125{
 126        mtk_cec_set_bits(cec, RX_EVENT, HDMI_PORD_INT_EN | HDMI_HTPLG_INT_EN);
 127}
 128
 129static void mtk_cec_htplg_irq_disable(struct mtk_cec *cec)
 130{
 131        mtk_cec_clear_bits(cec, RX_EVENT, HDMI_PORD_INT_EN | HDMI_HTPLG_INT_EN);
 132}
 133
 134static void mtk_cec_clear_htplg_irq(struct mtk_cec *cec)
 135{
 136        mtk_cec_set_bits(cec, TR_CONFIG, CLEAR_CEC_IRQ);
 137        mtk_cec_set_bits(cec, NORMAL_INT_CTRL, HDMI_HTPLG_INT_CLR |
 138                         HDMI_PORD_INT_CLR | HDMI_FULL_INT_CLR);
 139        mtk_cec_set_bits(cec, RX_GEN_WD, HDMI_PORD_INT_32K_CLR |
 140                         RX_INT_32K_CLR | HDMI_HTPLG_INT_32K_CLR);
 141        usleep_range(5, 10);
 142        mtk_cec_clear_bits(cec, NORMAL_INT_CTRL, HDMI_HTPLG_INT_CLR |
 143                           HDMI_PORD_INT_CLR | HDMI_FULL_INT_CLR);
 144        mtk_cec_clear_bits(cec, TR_CONFIG, CLEAR_CEC_IRQ);
 145        mtk_cec_clear_bits(cec, RX_GEN_WD, HDMI_PORD_INT_32K_CLR |
 146                           RX_INT_32K_CLR | HDMI_HTPLG_INT_32K_CLR);
 147}
 148
 149static void mtk_cec_hpd_event(struct mtk_cec *cec, bool hpd)
 150{
 151        void (*hpd_event)(bool hpd, struct device *dev);
 152        struct device *hdmi_dev;
 153        unsigned long flags;
 154
 155        spin_lock_irqsave(&cec->lock, flags);
 156        hpd_event = cec->hpd_event;
 157        hdmi_dev = cec->hdmi_dev;
 158        spin_unlock_irqrestore(&cec->lock, flags);
 159
 160        if (hpd_event)
 161                hpd_event(hpd, hdmi_dev);
 162}
 163
 164static irqreturn_t mtk_cec_htplg_isr_thread(int irq, void *arg)
 165{
 166        struct device *dev = arg;
 167        struct mtk_cec *cec = dev_get_drvdata(dev);
 168        bool hpd;
 169
 170        mtk_cec_clear_htplg_irq(cec);
 171        hpd = mtk_cec_hpd_high(dev);
 172
 173        if (cec->hpd != hpd) {
 174                dev_dbg(dev, "hotplug event! cur hpd = %d, hpd = %d\n",
 175                        cec->hpd, hpd);
 176                cec->hpd = hpd;
 177                mtk_cec_hpd_event(cec, hpd);
 178        }
 179        return IRQ_HANDLED;
 180}
 181
 182static int mtk_cec_probe(struct platform_device *pdev)
 183{
 184        struct device *dev = &pdev->dev;
 185        struct mtk_cec *cec;
 186        struct resource *res;
 187        int ret;
 188
 189        cec = devm_kzalloc(dev, sizeof(*cec), GFP_KERNEL);
 190        if (!cec)
 191                return -ENOMEM;
 192
 193        platform_set_drvdata(pdev, cec);
 194        spin_lock_init(&cec->lock);
 195
 196        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 197        cec->regs = devm_ioremap_resource(dev, res);
 198        if (IS_ERR(cec->regs)) {
 199                ret = PTR_ERR(cec->regs);
 200                dev_err(dev, "Failed to ioremap cec: %d\n", ret);
 201                return ret;
 202        }
 203
 204        cec->clk = devm_clk_get(dev, NULL);
 205        if (IS_ERR(cec->clk)) {
 206                ret = PTR_ERR(cec->clk);
 207                dev_err(dev, "Failed to get cec clock: %d\n", ret);
 208                return ret;
 209        }
 210
 211        cec->irq = platform_get_irq(pdev, 0);
 212        if (cec->irq < 0)
 213                return cec->irq;
 214
 215        ret = devm_request_threaded_irq(dev, cec->irq, NULL,
 216                                        mtk_cec_htplg_isr_thread,
 217                                        IRQF_SHARED | IRQF_TRIGGER_LOW |
 218                                        IRQF_ONESHOT, "hdmi hpd", dev);
 219        if (ret) {
 220                dev_err(dev, "Failed to register cec irq: %d\n", ret);
 221                return ret;
 222        }
 223
 224        ret = clk_prepare_enable(cec->clk);
 225        if (ret) {
 226                dev_err(dev, "Failed to enable cec clock: %d\n", ret);
 227                return ret;
 228        }
 229
 230        mtk_cec_htplg_irq_init(cec);
 231        mtk_cec_htplg_irq_enable(cec);
 232
 233        return 0;
 234}
 235
 236static int mtk_cec_remove(struct platform_device *pdev)
 237{
 238        struct mtk_cec *cec = platform_get_drvdata(pdev);
 239
 240        mtk_cec_htplg_irq_disable(cec);
 241        clk_disable_unprepare(cec->clk);
 242        return 0;
 243}
 244
 245static const struct of_device_id mtk_cec_of_ids[] = {
 246        { .compatible = "mediatek,mt8173-cec", },
 247        {}
 248};
 249MODULE_DEVICE_TABLE(of, mtk_cec_of_ids);
 250
 251struct platform_driver mtk_cec_driver = {
 252        .probe = mtk_cec_probe,
 253        .remove = mtk_cec_remove,
 254        .driver = {
 255                .name = "mediatek-cec",
 256                .of_match_table = mtk_cec_of_ids,
 257        },
 258};
 259