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