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/mod_devicetable.h>
19 #include <linux/platform_device.h>
23 #define TR_CONFIG 0x00
24 #define CLEAR_CEC_IRQ BIT(15)
26 #define CEC_CKGEN 0x04
27 #define CEC_32K_PDN BIT(19)
31 #define HDMI_PORD BIT(25)
32 #define HDMI_HTPLG BIT(24)
33 #define HDMI_PORD_INT_EN BIT(9)
34 #define HDMI_HTPLG_INT_EN BIT(8)
36 #define RX_GEN_WD 0x58
37 #define HDMI_PORD_INT_32K_STATUS BIT(26)
38 #define RX_RISC_INT_32K_STATUS BIT(25)
39 #define HDMI_HTPLG_INT_32K_STATUS BIT(24)
40 #define HDMI_PORD_INT_32K_CLR BIT(18)
41 #define RX_INT_32K_CLR BIT(17)
42 #define HDMI_HTPLG_INT_32K_CLR BIT(16)
43 #define HDMI_PORD_INT_32K_STA_MASK BIT(10)
44 #define RX_RISC_INT_32K_STA_MASK BIT(9)
45 #define HDMI_HTPLG_INT_32K_STA_MASK BIT(8)
46 #define HDMI_PORD_INT_32K_EN BIT(2)
47 #define RX_INT_32K_EN BIT(1)
48 #define HDMI_HTPLG_INT_32K_EN BIT(0)
50 #define NORMAL_INT_CTRL 0x5C
51 #define HDMI_HTPLG_INT_STA BIT(0)
52 #define HDMI_PORD_INT_STA BIT(1)
53 #define HDMI_HTPLG_INT_CLR BIT(16)
54 #define HDMI_PORD_INT_CLR BIT(17)
55 #define HDMI_FULL_INT_CLR BIT(20)
62 void (*hpd_event
)(bool hpd
, struct device
*dev
);
63 struct device
*hdmi_dev
;
67 static void mtk_cec_clear_bits(struct mtk_cec
*cec
, unsigned int offset
,
70 void __iomem
*reg
= cec
->regs
+ offset
;
78 static void mtk_cec_set_bits(struct mtk_cec
*cec
, unsigned int offset
,
81 void __iomem
*reg
= cec
->regs
+ offset
;
89 static void mtk_cec_mask(struct mtk_cec
*cec
, unsigned int offset
,
90 unsigned int val
, unsigned int mask
)
92 u32 tmp
= readl(cec
->regs
+ offset
) & ~mask
;
95 writel(val
, cec
->regs
+ offset
);
98 void mtk_cec_set_hpd_event(struct device
*dev
,
99 void (*hpd_event
)(bool hpd
, struct device
*dev
),
100 struct device
*hdmi_dev
)
102 struct mtk_cec
*cec
= dev_get_drvdata(dev
);
105 spin_lock_irqsave(&cec
->lock
, flags
);
106 cec
->hdmi_dev
= hdmi_dev
;
107 cec
->hpd_event
= hpd_event
;
108 spin_unlock_irqrestore(&cec
->lock
, flags
);
111 bool mtk_cec_hpd_high(struct device
*dev
)
113 struct mtk_cec
*cec
= dev_get_drvdata(dev
);
116 status
= readl(cec
->regs
+ RX_EVENT
);
118 return (status
& (HDMI_PORD
| HDMI_HTPLG
)) == (HDMI_PORD
| HDMI_HTPLG
);
121 static void mtk_cec_htplg_irq_init(struct mtk_cec
*cec
)
123 mtk_cec_mask(cec
, CEC_CKGEN
, 0 | CEC_32K_PDN
, PDN
| CEC_32K_PDN
);
124 mtk_cec_set_bits(cec
, RX_GEN_WD
, HDMI_PORD_INT_32K_CLR
|
125 RX_INT_32K_CLR
| HDMI_HTPLG_INT_32K_CLR
);
126 mtk_cec_mask(cec
, RX_GEN_WD
, 0, HDMI_PORD_INT_32K_CLR
| RX_INT_32K_CLR
|
127 HDMI_HTPLG_INT_32K_CLR
| HDMI_PORD_INT_32K_EN
|
128 RX_INT_32K_EN
| HDMI_HTPLG_INT_32K_EN
);
131 static void mtk_cec_htplg_irq_enable(struct mtk_cec
*cec
)
133 mtk_cec_set_bits(cec
, RX_EVENT
, HDMI_PORD_INT_EN
| HDMI_HTPLG_INT_EN
);
136 static void mtk_cec_htplg_irq_disable(struct mtk_cec
*cec
)
138 mtk_cec_clear_bits(cec
, RX_EVENT
, HDMI_PORD_INT_EN
| HDMI_HTPLG_INT_EN
);
141 static void mtk_cec_clear_htplg_irq(struct mtk_cec
*cec
)
143 mtk_cec_set_bits(cec
, TR_CONFIG
, CLEAR_CEC_IRQ
);
144 mtk_cec_set_bits(cec
, NORMAL_INT_CTRL
, HDMI_HTPLG_INT_CLR
|
145 HDMI_PORD_INT_CLR
| HDMI_FULL_INT_CLR
);
146 mtk_cec_set_bits(cec
, RX_GEN_WD
, HDMI_PORD_INT_32K_CLR
|
147 RX_INT_32K_CLR
| HDMI_HTPLG_INT_32K_CLR
);
149 mtk_cec_clear_bits(cec
, NORMAL_INT_CTRL
, HDMI_HTPLG_INT_CLR
|
150 HDMI_PORD_INT_CLR
| HDMI_FULL_INT_CLR
);
151 mtk_cec_clear_bits(cec
, TR_CONFIG
, CLEAR_CEC_IRQ
);
152 mtk_cec_clear_bits(cec
, RX_GEN_WD
, HDMI_PORD_INT_32K_CLR
|
153 RX_INT_32K_CLR
| HDMI_HTPLG_INT_32K_CLR
);
156 static void mtk_cec_hpd_event(struct mtk_cec
*cec
, bool hpd
)
158 void (*hpd_event
)(bool hpd
, struct device
*dev
);
159 struct device
*hdmi_dev
;
162 spin_lock_irqsave(&cec
->lock
, flags
);
163 hpd_event
= cec
->hpd_event
;
164 hdmi_dev
= cec
->hdmi_dev
;
165 spin_unlock_irqrestore(&cec
->lock
, flags
);
168 hpd_event(hpd
, hdmi_dev
);
171 static irqreturn_t
mtk_cec_htplg_isr_thread(int irq
, void *arg
)
173 struct device
*dev
= arg
;
174 struct mtk_cec
*cec
= dev_get_drvdata(dev
);
177 mtk_cec_clear_htplg_irq(cec
);
178 hpd
= mtk_cec_hpd_high(dev
);
180 if (cec
->hpd
!= hpd
) {
181 dev_dbg(dev
, "hotplug event! cur hpd = %d, hpd = %d\n",
184 mtk_cec_hpd_event(cec
, hpd
);
189 static int mtk_cec_probe(struct platform_device
*pdev
)
191 struct device
*dev
= &pdev
->dev
;
193 struct resource
*res
;
196 cec
= devm_kzalloc(dev
, sizeof(*cec
), GFP_KERNEL
);
200 platform_set_drvdata(pdev
, cec
);
201 spin_lock_init(&cec
->lock
);
203 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
204 cec
->regs
= devm_ioremap_resource(dev
, res
);
205 if (IS_ERR(cec
->regs
)) {
206 ret
= PTR_ERR(cec
->regs
);
207 dev_err(dev
, "Failed to ioremap cec: %d\n", ret
);
211 cec
->clk
= devm_clk_get(dev
, NULL
);
212 if (IS_ERR(cec
->clk
)) {
213 ret
= PTR_ERR(cec
->clk
);
214 dev_err(dev
, "Failed to get cec clock: %d\n", ret
);
218 cec
->irq
= platform_get_irq(pdev
, 0);
220 dev_err(dev
, "Failed to get cec irq: %d\n", cec
->irq
);
224 ret
= devm_request_threaded_irq(dev
, cec
->irq
, NULL
,
225 mtk_cec_htplg_isr_thread
,
226 IRQF_SHARED
| IRQF_TRIGGER_LOW
|
227 IRQF_ONESHOT
, "hdmi hpd", dev
);
229 dev_err(dev
, "Failed to register cec irq: %d\n", ret
);
233 ret
= clk_prepare_enable(cec
->clk
);
235 dev_err(dev
, "Failed to enable cec clock: %d\n", ret
);
239 mtk_cec_htplg_irq_init(cec
);
240 mtk_cec_htplg_irq_enable(cec
);
245 static int mtk_cec_remove(struct platform_device
*pdev
)
247 struct mtk_cec
*cec
= platform_get_drvdata(pdev
);
249 mtk_cec_htplg_irq_disable(cec
);
250 clk_disable_unprepare(cec
->clk
);
254 static const struct of_device_id mtk_cec_of_ids
[] = {
255 { .compatible
= "mediatek,mt8173-cec", },
259 struct platform_driver mtk_cec_driver
= {
260 .probe
= mtk_cec_probe
,
261 .remove
= mtk_cec_remove
,
263 .name
= "mediatek-cec",
264 .of_match_table
= mtk_cec_of_ids
,