Skip to content

Commit 8f83f26

Browse files
lourisqiupH5
authored andcommitted
drm/mediatek: Add HDMI support
This patch adds drivers for the HDMI bridge connected to the DPI0 display subsystem function block, for the HDMI DDC block, and for the HDMI PHY to support HDMI output. This includes an interface to the generic hdmi-codec driver to start or stop audio playback and to retrieve ELD (EDID like data) to limit the supported audio formats to the HDMI sink capabilities. Signed-off-by: Jie Qiu <jie.qiu@mediatek.com> Signed-off-by: Junzhi Zhao <junzhi.zhao@mediatek.com> Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
1 parent 7cdeb24 commit 8f83f26

File tree

9 files changed

+3255
-0
lines changed

9 files changed

+3255
-0
lines changed

drivers/gpu/drm/mediatek/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,11 @@ config DRM_MEDIATEK
1414
The module will be called mediatek-drm
1515
This driver provides kernel mode setting and
1616
buffer management to userspace.
17+
18+
config DRM_MEDIATEK_HDMI
19+
tristate "DRM HDMI Support for Mediatek SoCs"
20+
depends on DRM_MEDIATEK
21+
select SND_SOC_HDMI_CODEC if SND_SOC
22+
select GENERIC_PHY
23+
help
24+
DRM/KMS HDMI driver for Mediatek SoCs

drivers/gpu/drm/mediatek/Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,10 @@ mediatek-drm-y := mtk_disp_ovl.o \
1212
mtk_dpi.o
1313

1414
obj-$(CONFIG_DRM_MEDIATEK) += mediatek-drm.o
15+
16+
mediatek-drm-hdmi-objs := mtk_cec.o \
17+
mtk_hdmi.o \
18+
mtk_hdmi_ddc.o \
19+
mtk_mt8173_hdmi_phy.o
20+
21+
obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o

drivers/gpu/drm/mediatek/mtk_cec.c

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
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+
};

drivers/gpu/drm/mediatek/mtk_cec.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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+
#ifndef _MTK_CEC_H
15+
#define _MTK_CEC_H
16+
17+
#include <linux/types.h>
18+
19+
struct device;
20+
21+
void mtk_cec_set_hpd_event(struct device *dev,
22+
void (*hotplug_event)(bool hpd, struct device *dev),
23+
struct device *hdmi_dev);
24+
bool mtk_cec_hpd_high(struct device *dev);
25+
26+
#endif /* _MTK_CEC_H */

0 commit comments

Comments
 (0)