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