2 * Copyright (c) 2014 MediaTek Inc.
3 * Author: Jie Qiu <jie.qiu@mediatek.com>
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.
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.
14 #include <linux/clk.h>
15 #include <linux/delay.h>
17 #include <linux/interrupt.h>
18 #include <linux/platform_device.h>
22 #define TR_CONFIG 0x00
23 #define CLEAR_CEC_IRQ BIT(15)
25 #define CEC_CKGEN 0x04
26 #define CEC_32K_PDN BIT(19)
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)
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)
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)
61 void (*hpd_event
)(bool hpd
, struct device
*dev
);
62 struct device
*hdmi_dev
;
66 static void mtk_cec_clear_bits(struct mtk_cec
*cec
, unsigned int offset
,
69 void __iomem
*reg
= cec
->regs
+ offset
;
77 static void mtk_cec_set_bits(struct mtk_cec
*cec
, unsigned int offset
,
80 void __iomem
*reg
= cec
->regs
+ offset
;
88 static void mtk_cec_mask(struct mtk_cec
*cec
, unsigned int offset
,
89 unsigned int val
, unsigned int mask
)
91 u32 tmp
= readl(cec
->regs
+ offset
) & ~mask
;
94 writel(val
, cec
->regs
+ offset
);
97 void mtk_cec_set_hpd_event(struct device
*dev
,
98 void (*hpd_event
)(bool hpd
, struct device
*dev
),
99 struct device
*hdmi_dev
)
101 struct mtk_cec
*cec
= dev_get_drvdata(dev
);
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
);
110 bool mtk_cec_hpd_high(struct device
*dev
)
112 struct mtk_cec
*cec
= dev_get_drvdata(dev
);
115 status
= readl(cec
->regs
+ RX_EVENT
);
117 return (status
& (HDMI_PORD
| HDMI_HTPLG
)) == (HDMI_PORD
| HDMI_HTPLG
);
120 static void mtk_cec_htplg_irq_init(struct mtk_cec
*cec
)
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
);
130 static void mtk_cec_htplg_irq_enable(struct mtk_cec
*cec
)
132 mtk_cec_set_bits(cec
, RX_EVENT
, HDMI_PORD_INT_EN
| HDMI_HTPLG_INT_EN
);
135 static void mtk_cec_htplg_irq_disable(struct mtk_cec
*cec
)
137 mtk_cec_clear_bits(cec
, RX_EVENT
, HDMI_PORD_INT_EN
| HDMI_HTPLG_INT_EN
);
140 static void mtk_cec_clear_htplg_irq(struct mtk_cec
*cec
)
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
);
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
);
155 static void mtk_cec_hpd_event(struct mtk_cec
*cec
, bool hpd
)
157 void (*hpd_event
)(bool hpd
, struct device
*dev
);
158 struct device
*hdmi_dev
;
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
);
167 hpd_event(hpd
, hdmi_dev
);
170 static irqreturn_t
mtk_cec_htplg_isr_thread(int irq
, void *arg
)
172 struct device
*dev
= arg
;
173 struct mtk_cec
*cec
= dev_get_drvdata(dev
);
176 mtk_cec_clear_htplg_irq(cec
);
177 hpd
= mtk_cec_hpd_high(dev
);
179 if (cec
->hpd
!= hpd
) {
180 dev_dbg(dev
, "hotplug event! cur hpd = %d, hpd = %d\n",
183 mtk_cec_hpd_event(cec
, hpd
);
188 static int mtk_cec_probe(struct platform_device
*pdev
)
190 struct device
*dev
= &pdev
->dev
;
192 struct resource
*res
;
195 cec
= devm_kzalloc(dev
, sizeof(*cec
), GFP_KERNEL
);
199 platform_set_drvdata(pdev
, cec
);
200 spin_lock_init(&cec
->lock
);
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
);
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
);
217 cec
->irq
= platform_get_irq(pdev
, 0);
219 dev_err(dev
, "Failed to get cec irq: %d\n", cec
->irq
);
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
);
228 dev_err(dev
, "Failed to register cec irq: %d\n", ret
);
232 ret
= clk_prepare_enable(cec
->clk
);
234 dev_err(dev
, "Failed to enable cec clock: %d\n", ret
);
238 mtk_cec_htplg_irq_init(cec
);
239 mtk_cec_htplg_irq_enable(cec
);
244 static int mtk_cec_remove(struct platform_device
*pdev
)
246 struct mtk_cec
*cec
= platform_get_drvdata(pdev
);
248 mtk_cec_htplg_irq_disable(cec
);
249 clk_disable_unprepare(cec
->clk
);
253 static const struct of_device_id mtk_cec_of_ids
[] = {
254 { .compatible
= "mediatek,mt8173-cec", },
258 struct platform_driver mtk_cec_driver
= {
259 .probe
= mtk_cec_probe
,
260 .remove
= mtk_cec_remove
,
262 .name
= "mediatek-cec",
263 .of_match_table
= mtk_cec_of_ids
,