blob: b8ba70ea23e5c0e323872627db0d8fde6b01ce61 [file] [log] [blame]
Brad Bishop286d45c2018-10-02 15:21:57 -04001From c1bf9e8c50baa237b514715dcb9c8fd367694c93 Mon Sep 17 00:00:00 2001
2From: Jason Wu <jason.wu.misc@gmail.com>
3Date: Sun, 10 Apr 2016 13:14:13 +1000
4Subject: [PATCH 1/3] drm: xilinx: Add encoder for Digilent boards
5
6Add the dglnt_encoder driver that enables DRM support for the VGA and
7HDMI output ports found on many Digilent boards.
8
9Upstream-Status: Pending
10
11Signed-off-by: Sam Bobrowicz <sbobrowicz@digilentinc.com>
12Signed-off-by: Jason Wu <jason.wu.misc@gmail.com>
13---
14 .../bindings/drm/xilinx/dglnt_encoder.txt | 23 +++
15 drivers/gpu/drm/xilinx/Kconfig | 6 +
16 drivers/gpu/drm/xilinx/Makefile | 1 +
17 drivers/gpu/drm/xilinx/dglnt_encoder.c | 217 +++++++++++++++++++++
18 4 files changed, 247 insertions(+)
19 create mode 100644 Documentation/devicetree/bindings/drm/xilinx/dglnt_encoder.txt
20 create mode 100644 drivers/gpu/drm/xilinx/dglnt_encoder.c
21
22diff --git a/Documentation/devicetree/bindings/drm/xilinx/dglnt_encoder.txt b/Documentation/devicetree/bindings/drm/xilinx/dglnt_encoder.txt
23new file mode 100644
24index 0000000000..242b24e482
25--- /dev/null
26+++ b/Documentation/devicetree/bindings/drm/xilinx/dglnt_encoder.txt
27@@ -0,0 +1,23 @@
28+Device-Tree bindings for Digilent DRM Encoder Slave
29+
30+This driver provides support for VGA and HDMI outputs on Digilent FPGA boards.
31+The VGA or HDMI port must be connected to a Xilinx display pipeline via an
32+axi2vid IP core.
33+
34+Required properties:
35+ - compatible: Should be "digilent,drm-encoder".
36+
37+Optional properties:
38+ - dglnt,edid-i2c: The I2C device connected to the DDC bus on the video
39+ connector. This is used to obtain the supported resolutions
40+ of an attached monitor. If not defined, then a default
41+ set of resolutions is used and the display will initialize
42+ to 720p. Note most VGA connectors on Digilent boards do
43+ not have the DDC bus routed out.
44+
45+Example:
46+
47+ encoder_0: digilent_encoder {
48+ compatible = "digilent,drm-encoder";
49+ dglnt,edid-i2c = <&i2c1>;
50+ };
51diff --git a/drivers/gpu/drm/xilinx/Kconfig b/drivers/gpu/drm/xilinx/Kconfig
52index 12b548c2a9..c3e2562e53 100644
53--- a/drivers/gpu/drm/xilinx/Kconfig
54+++ b/drivers/gpu/drm/xilinx/Kconfig
55@@ -57,3 +57,9 @@ config DRM_XILINX_SDI
56 depends on DRM_XILINX
57 help
58 DRM driver for Xilinx Display Port Subsystem.
59+
60+config DRM_DIGILENT_ENCODER
61+ tristate "Digilent VGA/HDMI DRM Encoder Driver"
62+ depends on DRM_XILINX
63+ help
64+ DRM slave encoder for Video-out on Digilent boards.
65diff --git a/drivers/gpu/drm/xilinx/Makefile b/drivers/gpu/drm/xilinx/Makefile
66index 19bc1541ca..c2717e40ea 100644
67--- a/drivers/gpu/drm/xilinx/Makefile
68+++ b/drivers/gpu/drm/xilinx/Makefile
69@@ -7,6 +7,7 @@ xilinx_drm-y := xilinx_drm_crtc.o xilinx_drm_connector.o xilinx_drm_drv.o \
70 xilinx_drm_plane.o
71 xilinx_drm-y += xilinx_cresample.o xilinx_osd.o xilinx_rgb2yuv.o xilinx_vtc.o
72
73+obj-$(CONFIG_DRM_DIGILENT_ENCODER) += dglnt_encoder.o
74 obj-$(CONFIG_DRM_XILINX) += xilinx_drm.o
75 obj-$(CONFIG_DRM_XILINX_DP) += xilinx_drm_dp.o
76 obj-$(CONFIG_DRM_XILINX_DP_SUB) += xilinx_drm_dp_sub.o
77diff --git a/drivers/gpu/drm/xilinx/dglnt_encoder.c b/drivers/gpu/drm/xilinx/dglnt_encoder.c
78new file mode 100644
79index 0000000000..26a23986f9
80--- /dev/null
81+++ b/drivers/gpu/drm/xilinx/dglnt_encoder.c
82@@ -0,0 +1,217 @@
83+/*
84+ * dglnt_encoder.c - DRM slave encoder for Video-out on Digilent boards
85+ *
86+ * Copyright (C) 2015 Digilent
87+ * Author: Sam Bobrowicz <sbobrowicz@digilentinc.com>
88+ *
89+ * Based on udl_encoder.c and udl_connector.c, Copyright (C) 2012 Red Hat.
90+ * Also based on xilinx_drm_dp.c, Copyright (C) 2014 Xilinx, Inc.
91+ *
92+ * This software is licensed under the terms of the GNU General Public
93+ * License version 2, as published by the Free Software Foundation, and
94+ * may be copied, distributed, and modified under those terms.
95+ *
96+ * This program is distributed in the hope that it will be useful,
97+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
98+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
99+ * GNU General Public License for more details.
100+ */
101+
102+#include <drm/drmP.h>
103+#include <drm/drm_edid.h>
104+#include <drm/drm_encoder_slave.h>
105+
106+#include <linux/device.h>
107+#include <linux/module.h>
108+#include <linux/err.h>
109+#include <linux/i2c.h>
110+#include <linux/of.h>
111+#include <linux/of_platform.h>
112+#include <linux/platform_device.h>
113+
114+#define DGLNT_ENC_MAX_FREQ 150000
115+#define DGLNT_ENC_MAX_H 1920
116+#define DGLNT_ENC_MAX_V 1080
117+#define DGLNT_ENC_PREF_H 1280
118+#define DGLNT_ENC_PREF_V 720
119+
120+struct dglnt_encoder {
121+ struct drm_encoder *encoder;
122+ struct i2c_adapter *i2c_bus;
123+ bool i2c_present;
124+};
125+
126+static inline struct dglnt_encoder *to_dglnt_encoder(
127+ struct drm_encoder *encoder)
128+{
129+ return to_encoder_slave(encoder)->slave_priv;
130+}
131+
132+static bool dglnt_mode_fixup(struct drm_encoder *encoder,
133+ const struct drm_display_mode *mode,
134+ struct drm_display_mode *adjusted_mode)
135+{
136+ return true;
137+}
138+
139+static void dglnt_encoder_mode_set(struct drm_encoder *encoder,
140+ struct drm_display_mode *mode,
141+ struct drm_display_mode *adjusted_mode)
142+{
143+}
144+
145+static void
146+dglnt_encoder_dpms(struct drm_encoder *encoder, int mode)
147+{
148+}
149+
150+static void dglnt_encoder_save(struct drm_encoder *encoder)
151+{
152+}
153+
154+static void dglnt_encoder_restore(struct drm_encoder *encoder)
155+{
156+}
157+
158+static int dglnt_encoder_mode_valid(struct drm_encoder *encoder,
159+ struct drm_display_mode *mode)
160+{
161+ if (mode &&
162+ !(mode->flags & ((DRM_MODE_FLAG_INTERLACE |
163+ DRM_MODE_FLAG_DBLCLK) | DRM_MODE_FLAG_3D_MASK)) &&
164+ (mode->clock <= DGLNT_ENC_MAX_FREQ) &&
165+ (mode->hdisplay <= DGLNT_ENC_MAX_H) &&
166+ (mode->vdisplay <= DGLNT_ENC_MAX_V))
167+ return MODE_OK;
168+ return MODE_BAD;
169+}
170+
171+static int dglnt_encoder_get_modes(struct drm_encoder *encoder,
172+ struct drm_connector *connector)
173+{
174+ struct dglnt_encoder *dglnt = to_dglnt_encoder(encoder);
175+ struct edid *edid;
176+ int num_modes = 0;
177+
178+ if (dglnt->i2c_present) {
179+ edid = drm_get_edid(connector, dglnt->i2c_bus);
180+ drm_mode_connector_update_edid_property(connector, edid);
181+ if (edid) {
182+ num_modes = drm_add_edid_modes(connector, edid);
183+ kfree(edid);
184+ }
185+ } else {
186+ num_modes = drm_add_modes_noedid(connector, DGLNT_ENC_MAX_H,
187+ DGLNT_ENC_MAX_V);
188+ drm_set_preferred_mode(connector, DGLNT_ENC_PREF_H,
189+ DGLNT_ENC_PREF_V);
190+ }
191+ return num_modes;
192+}
193+
194+static enum drm_connector_status dglnt_encoder_detect(
195+ struct drm_encoder *encoder,
196+ struct drm_connector *connector)
197+{
198+ struct dglnt_encoder *dglnt = to_dglnt_encoder(encoder);
199+
200+ if (dglnt->i2c_present) {
201+ if (drm_probe_ddc(dglnt->i2c_bus))
202+ return connector_status_connected;
203+ return connector_status_disconnected;
204+ } else
205+ return connector_status_unknown;
206+}
207+
208+static struct drm_encoder_slave_funcs dglnt_encoder_slave_funcs = {
209+ .dpms = dglnt_encoder_dpms,
210+ .save = dglnt_encoder_save,
211+ .restore = dglnt_encoder_restore,
212+ .mode_fixup = dglnt_mode_fixup,
213+ .mode_valid = dglnt_encoder_mode_valid,
214+ .mode_set = dglnt_encoder_mode_set,
215+ .detect = dglnt_encoder_detect,
216+ .get_modes = dglnt_encoder_get_modes,
217+};
218+
219+static int dglnt_encoder_encoder_init(struct platform_device *pdev,
220+ struct drm_device *dev,
221+ struct drm_encoder_slave *encoder)
222+{
223+ struct dglnt_encoder *dglnt = platform_get_drvdata(pdev);
224+ struct device_node *sub_node;
225+
226+ encoder->slave_priv = dglnt;
227+ encoder->slave_funcs = &dglnt_encoder_slave_funcs;
228+
229+ dglnt->encoder = &encoder->base;
230+
231+ /* get i2c adapter for edid */
232+ dglnt->i2c_present = false;
233+ sub_node = of_parse_phandle(pdev->dev.of_node, "dglnt,edid-i2c", 0);
234+ if (sub_node) {
235+ dglnt->i2c_bus = of_find_i2c_adapter_by_node(sub_node);
236+ if (!dglnt->i2c_bus)
237+ DRM_INFO("failed to get the edid i2c adapter, using default modes\n");
238+ else
239+ dglnt->i2c_present = true;
240+ of_node_put(sub_node);
241+ }
242+
243+ return 0;
244+}
245+
246+static int dglnt_encoder_probe(struct platform_device *pdev)
247+{
248+ struct dglnt_encoder *dglnt;
249+
250+ dglnt = devm_kzalloc(&pdev->dev, sizeof(*dglnt), GFP_KERNEL);
251+ if (!dglnt)
252+ return -ENOMEM;
253+
254+ platform_set_drvdata(pdev, dglnt);
255+
256+ return 0;
257+}
258+
259+static int dglnt_encoder_remove(struct platform_device *pdev)
260+{
261+ return 0;
262+}
263+
264+static const struct of_device_id dglnt_encoder_of_match[] = {
265+ { .compatible = "digilent,drm-encoder", },
266+ { /* end of table */ },
267+};
268+MODULE_DEVICE_TABLE(of, dglnt_encoder_of_match);
269+
270+static struct drm_platform_encoder_driver dglnt_encoder_driver = {
271+ .platform_driver = {
272+ .probe = dglnt_encoder_probe,
273+ .remove = dglnt_encoder_remove,
274+ .driver = {
275+ .owner = THIS_MODULE,
276+ .name = "dglnt-drm-enc",
277+ .of_match_table = dglnt_encoder_of_match,
278+ },
279+ },
280+
281+ .encoder_init = dglnt_encoder_encoder_init,
282+};
283+
284+static int __init dglnt_encoder_init(void)
285+{
286+ return platform_driver_register(&dglnt_encoder_driver.platform_driver);
287+}
288+
289+static void __exit dglnt_encoder_exit(void)
290+{
291+ platform_driver_unregister(&dglnt_encoder_driver.platform_driver);
292+}
293+
294+module_init(dglnt_encoder_init);
295+module_exit(dglnt_encoder_exit);
296+
297+MODULE_AUTHOR("Digilent, Inc.");
298+MODULE_DESCRIPTION("DRM slave encoder for Video-out on Digilent boards");
299+MODULE_LICENSE("GPL v2");
300--
3012.14.2
302