Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
From 854ae91ceec110b553302ba5954996e0ca3996a3 Mon Sep 17 00:00:00 2001
From: Lucas Stach <l.stach@pengutronix.de>
Date: Fri, 1 Apr 2022 22:11:44 +0200
Subject: [PATCH 08/11] drm/imx: add bridge wrapper driver for i.MX8MP DWC HDMI
Add a simple wrapper driver for the DWC HDMI bridge driver that
implements the few bits that are necessary to abstract the i.MX8MP
SoC integration.
Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
Tested-by: Marek Vasut <marex@denx.de>
---
drivers/gpu/drm/bridge/imx/Kconfig | 9 ++
drivers/gpu/drm/bridge/imx/imx8mp-hdmi.c | 141 +++++++++++++++++++++++
2 files changed, 150 insertions(+)
create mode 100644 drivers/gpu/drm/bridge/imx/imx8mp-hdmi.c
--- a/drivers/gpu/drm/bridge/imx/Kconfig
+++ b/drivers/gpu/drm/bridge/imx/Kconfig
@@ -67,4 +67,13 @@ config DRM_IMX8MP_HDMI_PVI
Choose this to enable support for the internal HDMI TX Parallel
Video Interface found on the i.MX8MP SoC.
+config DRM_IMX8MP_DW_HDMI_BRIDGE
+ tristate "i.MX8MP HDMI bridge support"
+ depends on OF
+ depends on COMMON_CLK
+ select DRM_DW_HDMI
+ help
+ Choose this to enable support for the internal HDMI encoder found
+ on the i.MX8MP SoC.
+
endif # ARCH_MXC || COMPILE_TEST
--- /dev/null
+++ b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Copyright (C) 2022 Pengutronix, Lucas Stach <kernel@pengutronix.de>
+ */
+
+#include <drm/bridge/dw_hdmi.h>
+#include <drm/drm_modes.h>
+#include <linux/clk.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+struct imx_hdmi {
+ struct dw_hdmi_plat_data plat_data;
+ struct dw_hdmi *dw_hdmi;
+ struct clk *pixclk;
+ struct clk *fdcc;
+};
+
+static enum drm_mode_status
+imx8mp_hdmi_mode_valid(struct dw_hdmi *dw_hdmi, void *data,
+ const struct drm_display_info *info,
+ const struct drm_display_mode *mode)
+{
+ struct imx_hdmi *hdmi = (struct imx_hdmi *)data;
+
+ if (mode->clock < 13500)
+ return MODE_CLOCK_LOW;
+
+ if (mode->clock > 297000)
+ return MODE_CLOCK_HIGH;
+
+ if (clk_round_rate(hdmi->pixclk, mode->clock * 1000) !=
+ mode->clock * 1000)
+ return MODE_CLOCK_RANGE;
+
+ /* We don't support double-clocked and Interlaced modes */
+ if ((mode->flags & DRM_MODE_FLAG_DBLCLK) ||
+ (mode->flags & DRM_MODE_FLAG_INTERLACE))
+ return MODE_BAD;
+
+ return MODE_OK;
+}
+
+static int imx8mp_hdmi_phy_init(struct dw_hdmi *dw_hdmi, void *data,
+ const struct drm_display_info *display,
+ const struct drm_display_mode *mode)
+{
+ return 0;
+}
+
+static void imx8mp_hdmi_phy_disable(struct dw_hdmi *dw_hdmi, void *data)
+{
+}
+
+static const struct dw_hdmi_phy_ops imx8mp_hdmi_phy_ops = {
+ .init = imx8mp_hdmi_phy_init,
+ .disable = imx8mp_hdmi_phy_disable,
+ .read_hpd = dw_hdmi_phy_read_hpd,
+ .update_hpd = dw_hdmi_phy_update_hpd,
+ .setup_hpd = dw_hdmi_phy_setup_hpd,
+};
+
+static int imx_dw_hdmi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct dw_hdmi_plat_data *plat_data;
+ struct imx_hdmi *hdmi;
+ int ret;
+
+ hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
+ if (!hdmi)
+ return -ENOMEM;
+
+ plat_data = &hdmi->plat_data;
+
+ hdmi->pixclk = devm_clk_get(dev, "pix");
+ if (IS_ERR(hdmi->pixclk))
+ return dev_err_probe(dev, PTR_ERR(hdmi->pixclk),
+ "Unable to get pixel clock\n");
+
+ hdmi->fdcc = devm_clk_get(dev, "fdcc");
+ if (IS_ERR(hdmi->fdcc))
+ return dev_err_probe(dev, PTR_ERR(hdmi->fdcc),
+ "Unable to get FDCC clock\n");
+
+ ret = clk_prepare_enable(hdmi->fdcc);
+ if (ret)
+ return dev_err_probe(dev, ret, "Unable to enable FDCC clock\n");
+
+ plat_data->mode_valid = imx8mp_hdmi_mode_valid;
+ plat_data->phy_ops = &imx8mp_hdmi_phy_ops;
+ plat_data->phy_name = "SAMSUNG HDMI TX PHY";
+ plat_data->priv_data = hdmi;
+
+ hdmi->dw_hdmi = dw_hdmi_probe(pdev, plat_data);
+ if (IS_ERR(hdmi->dw_hdmi))
+ return PTR_ERR(hdmi->dw_hdmi);
+
+ /*
+ * Just release PHY core from reset, all other power management is done
+ * by the PHY driver.
+ */
+ dw_hdmi_phy_gen1_reset(hdmi->dw_hdmi);
+
+ platform_set_drvdata(pdev, hdmi);
+
+ return 0;
+}
+
+static int imx_dw_hdmi_remove(struct platform_device *pdev)
+{
+ struct imx_hdmi *hdmi = platform_get_drvdata(pdev);
+
+ dw_hdmi_remove(hdmi->dw_hdmi);
+
+ clk_disable_unprepare(hdmi->fdcc);
+
+ return 0;
+}
+
+static const struct of_device_id imx_dw_hdmi_of_table[] = {
+ { .compatible = "fsl,imx8mp-hdmi" },
+ { /* Sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, imx_dw_hdmi_of_table);
+
+static struct platform_driver im_dw_hdmi_platform_driver = {
+ .probe = imx_dw_hdmi_probe,
+ .remove = imx_dw_hdmi_remove,
+ .driver = {
+ .name = "imx-dw-hdmi",
+ .of_match_table = imx_dw_hdmi_of_table,
+ },
+};
+
+module_platform_driver(im_dw_hdmi_platform_driver);
+
+MODULE_DESCRIPTION("i.MX8M HDMI encoder driver");
+MODULE_LICENSE("GPL");