blob: 158603495a1eb14f61c143ae0550a7e619a522f8 [file] [log] [blame]
Brad Bishopbec4ebc2022-08-03 09:55:16 -04001From 39e6b51150c36dd659b85de0c4339594da389da9 Mon Sep 17 00:00:00 2001
2From: Tushar Khandelwal <tushar.khandelwal@arm.com>
3Date: Tue, 16 Jun 2020 12:39:06 +0000
4Subject: [PATCH 01/22] drm: Add component-aware simple encoder
5
6This is a simple DRM encoder that gets its connector timings information
7from a OF subnode in the device tree and exposes that as a "discovered"
8panel. It can be used together with component-based DRM drivers in an
9emulated environment where no real encoder or connector hardware exists
10and the display output is configured outside the kernel.
11
12Signed-off-by: Tushar Khandelwal <tushar.khandelwal@arm.com>
13
14Upstream-Status: Backport [https://git.linaro.org/landing-teams/working/arm/kernel-release.git/commit/?h=latest-armlt&id=15283f7be4b1e586702551e85b4caf06531ac2fc]
15Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com>
16---
17 drivers/gpu/drm/Kconfig | 11 +
18 drivers/gpu/drm/Makefile | 2 +
19 drivers/gpu/drm/drm_virtual_encoder.c | 299 ++++++++++++++++++++++++++
20 3 files changed, 312 insertions(+)
21 create mode 100644 drivers/gpu/drm/drm_virtual_encoder.c
22
23diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
24index ca868271f4c4..6ae8ba3ca7b3 100644
25--- a/drivers/gpu/drm/Kconfig
26+++ b/drivers/gpu/drm/Kconfig
27@@ -300,6 +300,17 @@ config DRM_VKMS
28
29 If M is selected the module will be called vkms.
30
31+config DRM_VIRT_ENCODER
32+ tristate "Virtual OF-based encoder"
33+ depends on DRM && OF
34+ select VIDEOMODE_HELPERS
35+ help
36+ Choose this option to get a virtual encoder and its associated
37+ connector that will use the device tree to read the display
38+ timings information. If M is selected the module will be called
39+ drm_vencoder.
40+
41+
42 source "drivers/gpu/drm/exynos/Kconfig"
43
44 source "drivers/gpu/drm/rockchip/Kconfig"
45diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
46index 81569009f884..a3429152c613 100644
47--- a/drivers/gpu/drm/Makefile
48+++ b/drivers/gpu/drm/Makefile
49@@ -56,6 +56,8 @@ drm_kms_helper-$(CONFIG_DRM_DP_CEC) += drm_dp_cec.o
50
51 obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
52 obj-$(CONFIG_DRM_DEBUG_SELFTEST) += selftests/
53+drm_vencoder-y := drm_virtual_encoder.o
54+obj-$(CONFIG_DRM_VIRT_ENCODER) += drm_vencoder.o
55
56 obj-$(CONFIG_DRM) += drm.o
57 obj-$(CONFIG_DRM_MIPI_DBI) += drm_mipi_dbi.o
58diff --git a/drivers/gpu/drm/drm_virtual_encoder.c b/drivers/gpu/drm/drm_virtual_encoder.c
59new file mode 100644
60index 000000000000..2f65c6b47d00
61--- /dev/null
62+++ b/drivers/gpu/drm/drm_virtual_encoder.c
63@@ -0,0 +1,299 @@
64+/*
65+ * Copyright (C) 2016 ARM Limited
66+ * Author: Liviu Dudau <Liviu.Dudau@arm.com>
67+ *
68+ * Dummy encoder and connector that use the OF to "discover" the attached
69+ * display timings. Can be used in situations where the encoder and connector's
70+ * functionality are emulated and no setup steps are needed, or to describe
71+ * attached panels for which no driver exists but can be used without
72+ * additional hardware setup.
73+ *
74+ * The encoder also uses the component framework so that it can be a quick
75+ * replacement for existing drivers when testing in an emulated environment.
76+ *
77+ * This file is subject to the terms and conditions of the GNU General Public
78+ * License. See the file COPYING in the main directory of this archive
79+ * for more details.
80+ *
81+ */
82+
83+#include <drm/drm_crtc.h>
84+#include <drm/drm_atomic_helper.h>
85+#include <drm/drm_crtc_helper.h>
86+#include <drm/drm_probe_helper.h>
87+#include <drm/drm_print.h>
88+#include <linux/platform_device.h>
89+#include <drm/drm_of.h>
90+#include <linux/component.h>
91+#include <video/display_timing.h>
92+#include <video/of_display_timing.h>
93+#include <video/videomode.h>
94+
95+struct drm_virt_priv {
96+ struct drm_connector connector;
97+ struct drm_encoder encoder;
98+ struct display_timings *timings;
99+};
100+
101+#define connector_to_drm_virt_priv(x) \
102+ container_of(x, struct drm_virt_priv, connector)
103+
104+#define encoder_to_drm_virt_priv(x) \
105+ container_of(x, struct drm_virt_priv, encoder)
106+
107+static void drm_virtcon_destroy(struct drm_connector *connector)
108+{
109+ struct drm_virt_priv *conn = connector_to_drm_virt_priv(connector);
110+
111+ drm_connector_cleanup(connector);
112+ display_timings_release(conn->timings);
113+}
114+
115+static enum drm_connector_status
116+drm_virtcon_detect(struct drm_connector *connector, bool force)
117+{
118+ return connector_status_connected;
119+}
120+
121+static const struct drm_connector_funcs drm_virtcon_funcs = {
122+ .reset = drm_atomic_helper_connector_reset,
123+ .detect = drm_virtcon_detect,
124+ .fill_modes = drm_helper_probe_single_connector_modes,
125+ .destroy = drm_virtcon_destroy,
126+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
127+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
128+};
129+
130+static int drm_virtcon_get_modes(struct drm_connector *connector)
131+{
132+ struct drm_virt_priv *conn = connector_to_drm_virt_priv(connector);
133+ struct display_timings *timings = conn->timings;
134+ int i;
135+
136+ for (i = 0; i < timings->num_timings; i++) {
137+ struct drm_display_mode *mode = drm_mode_create(connector->dev);
138+ struct videomode vm;
139+
140+ if (videomode_from_timings(timings, &vm, i))
141+ break;
142+
143+ drm_display_mode_from_videomode(&vm, mode);
144+ mode->type = DRM_MODE_TYPE_DRIVER;
145+ if (timings->native_mode == i)
146+ mode->type = DRM_MODE_TYPE_PREFERRED;
147+
148+ drm_mode_set_name(mode);
149+ drm_mode_probed_add(connector, mode);
150+ }
151+
152+ return i;
153+}
154+
155+static int drm_virtcon_mode_valid(struct drm_connector *connector,
156+ struct drm_display_mode *mode)
157+{
158+ return MODE_OK;
159+}
160+
161+struct drm_encoder *drm_virtcon_best_encoder(struct drm_connector *connector)
162+{
163+ struct drm_virt_priv *priv = connector_to_drm_virt_priv(connector);
164+
165+ return &priv->encoder;
166+}
167+
168+struct drm_encoder *
169+drm_virtcon_atomic_best_encoder(struct drm_connector *connector,
170+ struct drm_connector_state *connector_state)
171+{
172+ struct drm_virt_priv *priv = connector_to_drm_virt_priv(connector);
173+
174+ return &priv->encoder;
175+}
176+
177+static const struct drm_connector_helper_funcs drm_virtcon_helper_funcs = {
178+ .get_modes = drm_virtcon_get_modes,
179+ .mode_valid = drm_virtcon_mode_valid,
180+ .best_encoder = drm_virtcon_best_encoder,
181+ .atomic_best_encoder = drm_virtcon_atomic_best_encoder,
182+};
183+
184+static void drm_vencoder_destroy(struct drm_encoder *encoder)
185+{
186+ drm_encoder_cleanup(encoder);
187+}
188+
189+static const struct drm_encoder_funcs drm_vencoder_funcs = {
190+ .destroy = drm_vencoder_destroy,
191+};
192+
193+static void drm_vencoder_dpms(struct drm_encoder *encoder, int mode)
194+{
195+ /* nothing needed */
196+}
197+
198+static bool drm_vencoder_mode_fixup(struct drm_encoder *encoder,
199+ const struct drm_display_mode *mode,
200+ struct drm_display_mode *adjusted_mode)
201+{
202+ /* nothing needed */
203+ return true;
204+}
205+
206+static void drm_vencoder_prepare(struct drm_encoder *encoder)
207+{
208+ drm_vencoder_dpms(encoder, DRM_MODE_DPMS_OFF);
209+}
210+
211+static void drm_vencoder_commit(struct drm_encoder *encoder)
212+{
213+ drm_vencoder_dpms(encoder, DRM_MODE_DPMS_ON);
214+}
215+
216+static void drm_vencoder_mode_set(struct drm_encoder *encoder,
217+ struct drm_display_mode *mode,
218+ struct drm_display_mode *adjusted_mode)
219+{
220+ /* nothing needed */
221+}
222+
223+static const struct drm_encoder_helper_funcs drm_vencoder_helper_funcs = {
224+ .dpms = drm_vencoder_dpms,
225+ .mode_fixup = drm_vencoder_mode_fixup,
226+ .prepare = drm_vencoder_prepare,
227+ .commit = drm_vencoder_commit,
228+ .mode_set = drm_vencoder_mode_set,
229+};
230+
231+static int drm_vencoder_bind(struct device *dev, struct device *master,
232+ void *data)
233+{
234+ struct drm_encoder *encoder;
235+ struct drm_virt_priv *con;
236+ struct drm_connector *connector;
237+ struct drm_device *drm = data;
238+ u32 crtcs = 0;
239+ int ret;
240+
241+ con = devm_kzalloc(dev, sizeof(*con), GFP_KERNEL);
242+ if (!con)
243+ return -ENOMEM;
244+
245+ dev_set_drvdata(dev, con);
246+ connector = &con->connector;
247+ encoder = &con->encoder;
248+
249+ if (dev->of_node) {
250+ struct drm_bridge *bridge;
251+ crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
252+ bridge = of_drm_find_bridge(dev->of_node);
253+ if (bridge) {
254+ ret = drm_bridge_attach(encoder, bridge, NULL, 0);
255+ if (ret) {
256+ DRM_ERROR("Failed to initialize bridge\n");
257+ return ret;
258+ }
259+ }
260+ con->timings = of_get_display_timings(dev->of_node);
261+ if (!con->timings) {
262+ dev_err(dev, "failed to get display panel timings\n");
263+ return ENXIO;
264+ }
265+ }
266+
267+ /* If no CRTCs were found, fall back to the old encoder's behaviour */
268+ if (crtcs == 0) {
269+ dev_warn(dev, "Falling back to first CRTC\n");
270+ crtcs = 1 << 0;
271+ }
272+
273+ encoder->possible_crtcs = crtcs ? crtcs : 1;
274+ encoder->possible_clones = 0;
275+
276+ ret = drm_encoder_init(drm, encoder, &drm_vencoder_funcs,
277+ DRM_MODE_ENCODER_VIRTUAL, NULL);
278+ if (ret)
279+ goto encoder_init_err;
280+
281+ drm_encoder_helper_add(encoder, &drm_vencoder_helper_funcs);
282+
283+ /* bogus values, pretend we're a 24" screen for DPI calculations */
284+ connector->display_info.width_mm = 519;
285+ connector->display_info.height_mm = 324;
286+ connector->interlace_allowed = false;
287+ connector->doublescan_allowed = false;
288+ connector->polled = 0;
289+
290+ ret = drm_connector_init(drm, connector, &drm_virtcon_funcs,
291+ DRM_MODE_CONNECTOR_VIRTUAL);
292+ if (ret)
293+ goto connector_init_err;
294+
295+ drm_connector_helper_add(connector, &drm_virtcon_helper_funcs);
296+
297+ drm_connector_register(connector);
298+
299+ ret = drm_connector_attach_encoder(connector, encoder);
300+ if (ret)
301+ goto attach_err;
302+
303+ return ret;
304+
305+attach_err:
306+ drm_connector_unregister(connector);
307+ drm_connector_cleanup(connector);
308+connector_init_err:
309+ drm_encoder_cleanup(encoder);
310+encoder_init_err:
311+ display_timings_release(con->timings);
312+
313+ return ret;
314+};
315+
316+static void drm_vencoder_unbind(struct device *dev, struct device *master,
317+ void *data)
318+{
319+ struct drm_virt_priv *con = dev_get_drvdata(dev);
320+
321+ drm_connector_unregister(&con->connector);
322+ drm_connector_cleanup(&con->connector);
323+ drm_encoder_cleanup(&con->encoder);
324+ display_timings_release(con->timings);
325+}
326+
327+static const struct component_ops drm_vencoder_ops = {
328+ .bind = drm_vencoder_bind,
329+ .unbind = drm_vencoder_unbind,
330+};
331+
332+static int drm_vencoder_probe(struct platform_device *pdev)
333+{
334+ return component_add(&pdev->dev, &drm_vencoder_ops);
335+}
336+
337+static int drm_vencoder_remove(struct platform_device *pdev)
338+{
339+ component_del(&pdev->dev, &drm_vencoder_ops);
340+ return 0;
341+}
342+
343+static const struct of_device_id drm_vencoder_of_match[] = {
344+ { .compatible = "drm,virtual-encoder", },
345+ {},
346+};
347+MODULE_DEVICE_TABLE(of, drm_vencoder_of_match);
348+
349+static struct platform_driver drm_vencoder_driver = {
350+ .probe = drm_vencoder_probe,
351+ .remove = drm_vencoder_remove,
352+ .driver = {
353+ .name = "drm_vencoder",
354+ .of_match_table = drm_vencoder_of_match,
355+ },
356+};
357+
358+module_platform_driver(drm_vencoder_driver);
359+
360+MODULE_AUTHOR("Liviu Dudau");
361+MODULE_DESCRIPTION("Virtual DRM Encoder");
362+MODULE_LICENSE("GPL v2");
363--
3642.17.1
365