root/drivers/gpu/drm/mediatek/mtk_cec.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mtk_cec_clear_bits
  2. mtk_cec_set_bits
  3. mtk_cec_mask
  4. mtk_cec_set_hpd_event
  5. mtk_cec_hpd_high
  6. mtk_cec_htplg_irq_init
  7. mtk_cec_htplg_irq_enable
  8. mtk_cec_htplg_irq_disable
  9. mtk_cec_clear_htplg_irq
  10. mtk_cec_hpd_event
  11. mtk_cec_htplg_isr_thread
  12. mtk_cec_probe
  13. mtk_cec_remove

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

/* [<][>][^][v][top][bottom][index][help] */