1efeeaefeSPaul Kocialkowski // SPDX-License-Identifier: GPL-2.0+
2efeeaefeSPaul Kocialkowski /*
3efeeaefeSPaul Kocialkowski * Copyright (C) 2019-2022 Bootlin
4efeeaefeSPaul Kocialkowski * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
5efeeaefeSPaul Kocialkowski */
6efeeaefeSPaul Kocialkowski
7efeeaefeSPaul Kocialkowski #include <linux/types.h>
8efeeaefeSPaul Kocialkowski
9efeeaefeSPaul Kocialkowski #include <drm/drm_atomic_helper.h>
10efeeaefeSPaul Kocialkowski #include <drm/drm_bridge.h>
11efeeaefeSPaul Kocialkowski #include <drm/drm_connector.h>
12efeeaefeSPaul Kocialkowski #include <drm/drm_drv.h>
13efeeaefeSPaul Kocialkowski #include <drm/drm_encoder.h>
14*4a83c26aSDanilo Krummrich #include <drm/drm_gem_dma_helper.h>
15efeeaefeSPaul Kocialkowski #include <drm/drm_modeset_helper_vtables.h>
16efeeaefeSPaul Kocialkowski #include <drm/drm_of.h>
17efeeaefeSPaul Kocialkowski #include <drm/drm_panel.h>
18efeeaefeSPaul Kocialkowski #include <drm/drm_print.h>
19efeeaefeSPaul Kocialkowski #include <drm/drm_probe_helper.h>
20efeeaefeSPaul Kocialkowski
21efeeaefeSPaul Kocialkowski #include "logicvc_crtc.h"
22efeeaefeSPaul Kocialkowski #include "logicvc_drm.h"
23efeeaefeSPaul Kocialkowski #include "logicvc_interface.h"
24efeeaefeSPaul Kocialkowski #include "logicvc_regs.h"
25efeeaefeSPaul Kocialkowski
26efeeaefeSPaul Kocialkowski #define logicvc_interface_from_drm_encoder(c) \
27efeeaefeSPaul Kocialkowski container_of(c, struct logicvc_interface, drm_encoder)
28efeeaefeSPaul Kocialkowski #define logicvc_interface_from_drm_connector(c) \
29efeeaefeSPaul Kocialkowski container_of(c, struct logicvc_interface, drm_connector)
30efeeaefeSPaul Kocialkowski
logicvc_encoder_enable(struct drm_encoder * drm_encoder)31efeeaefeSPaul Kocialkowski static void logicvc_encoder_enable(struct drm_encoder *drm_encoder)
32efeeaefeSPaul Kocialkowski {
33efeeaefeSPaul Kocialkowski struct logicvc_drm *logicvc = logicvc_drm(drm_encoder->dev);
34efeeaefeSPaul Kocialkowski struct logicvc_interface *interface =
35efeeaefeSPaul Kocialkowski logicvc_interface_from_drm_encoder(drm_encoder);
36efeeaefeSPaul Kocialkowski
37efeeaefeSPaul Kocialkowski regmap_update_bits(logicvc->regmap, LOGICVC_POWER_CTRL_REG,
38efeeaefeSPaul Kocialkowski LOGICVC_POWER_CTRL_VIDEO_ENABLE,
39efeeaefeSPaul Kocialkowski LOGICVC_POWER_CTRL_VIDEO_ENABLE);
40efeeaefeSPaul Kocialkowski
41efeeaefeSPaul Kocialkowski if (interface->drm_panel) {
42efeeaefeSPaul Kocialkowski drm_panel_prepare(interface->drm_panel);
43efeeaefeSPaul Kocialkowski drm_panel_enable(interface->drm_panel);
44efeeaefeSPaul Kocialkowski }
45efeeaefeSPaul Kocialkowski }
46efeeaefeSPaul Kocialkowski
logicvc_encoder_disable(struct drm_encoder * drm_encoder)47efeeaefeSPaul Kocialkowski static void logicvc_encoder_disable(struct drm_encoder *drm_encoder)
48efeeaefeSPaul Kocialkowski {
49efeeaefeSPaul Kocialkowski struct logicvc_interface *interface =
50efeeaefeSPaul Kocialkowski logicvc_interface_from_drm_encoder(drm_encoder);
51efeeaefeSPaul Kocialkowski
52efeeaefeSPaul Kocialkowski if (interface->drm_panel) {
53efeeaefeSPaul Kocialkowski drm_panel_disable(interface->drm_panel);
54efeeaefeSPaul Kocialkowski drm_panel_unprepare(interface->drm_panel);
55efeeaefeSPaul Kocialkowski }
56efeeaefeSPaul Kocialkowski }
57efeeaefeSPaul Kocialkowski
58efeeaefeSPaul Kocialkowski static const struct drm_encoder_helper_funcs logicvc_encoder_helper_funcs = {
59efeeaefeSPaul Kocialkowski .enable = logicvc_encoder_enable,
60efeeaefeSPaul Kocialkowski .disable = logicvc_encoder_disable,
61efeeaefeSPaul Kocialkowski };
62efeeaefeSPaul Kocialkowski
63efeeaefeSPaul Kocialkowski static const struct drm_encoder_funcs logicvc_encoder_funcs = {
64efeeaefeSPaul Kocialkowski .destroy = drm_encoder_cleanup,
65efeeaefeSPaul Kocialkowski };
66efeeaefeSPaul Kocialkowski
logicvc_connector_get_modes(struct drm_connector * drm_connector)67efeeaefeSPaul Kocialkowski static int logicvc_connector_get_modes(struct drm_connector *drm_connector)
68efeeaefeSPaul Kocialkowski {
69efeeaefeSPaul Kocialkowski struct logicvc_interface *interface =
70efeeaefeSPaul Kocialkowski logicvc_interface_from_drm_connector(drm_connector);
71efeeaefeSPaul Kocialkowski
72efeeaefeSPaul Kocialkowski if (interface->drm_panel)
73efeeaefeSPaul Kocialkowski return drm_panel_get_modes(interface->drm_panel, drm_connector);
74efeeaefeSPaul Kocialkowski
75efeeaefeSPaul Kocialkowski WARN_ONCE(1, "Retrieving modes from a native connector is not implemented.");
76efeeaefeSPaul Kocialkowski
77efeeaefeSPaul Kocialkowski return 0;
78efeeaefeSPaul Kocialkowski }
79efeeaefeSPaul Kocialkowski
80efeeaefeSPaul Kocialkowski static const struct drm_connector_helper_funcs logicvc_connector_helper_funcs = {
81efeeaefeSPaul Kocialkowski .get_modes = logicvc_connector_get_modes,
82efeeaefeSPaul Kocialkowski };
83efeeaefeSPaul Kocialkowski
84efeeaefeSPaul Kocialkowski static const struct drm_connector_funcs logicvc_connector_funcs = {
85efeeaefeSPaul Kocialkowski .reset = drm_atomic_helper_connector_reset,
86efeeaefeSPaul Kocialkowski .fill_modes = drm_helper_probe_single_connector_modes,
87efeeaefeSPaul Kocialkowski .destroy = drm_connector_cleanup,
88efeeaefeSPaul Kocialkowski .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
89efeeaefeSPaul Kocialkowski .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
90efeeaefeSPaul Kocialkowski };
91efeeaefeSPaul Kocialkowski
logicvc_interface_encoder_type(struct logicvc_drm * logicvc)92efeeaefeSPaul Kocialkowski static int logicvc_interface_encoder_type(struct logicvc_drm *logicvc)
93efeeaefeSPaul Kocialkowski {
94efeeaefeSPaul Kocialkowski switch (logicvc->config.display_interface) {
95efeeaefeSPaul Kocialkowski case LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS:
96efeeaefeSPaul Kocialkowski case LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS_CAMERA:
97efeeaefeSPaul Kocialkowski case LOGICVC_DISPLAY_INTERFACE_LVDS_3BITS:
98efeeaefeSPaul Kocialkowski return DRM_MODE_ENCODER_LVDS;
99efeeaefeSPaul Kocialkowski case LOGICVC_DISPLAY_INTERFACE_DVI:
100efeeaefeSPaul Kocialkowski return DRM_MODE_ENCODER_TMDS;
101efeeaefeSPaul Kocialkowski case LOGICVC_DISPLAY_INTERFACE_RGB:
102efeeaefeSPaul Kocialkowski return DRM_MODE_ENCODER_DPI;
103efeeaefeSPaul Kocialkowski default:
104efeeaefeSPaul Kocialkowski return DRM_MODE_ENCODER_NONE;
105efeeaefeSPaul Kocialkowski }
106efeeaefeSPaul Kocialkowski }
107efeeaefeSPaul Kocialkowski
logicvc_interface_connector_type(struct logicvc_drm * logicvc)108efeeaefeSPaul Kocialkowski static int logicvc_interface_connector_type(struct logicvc_drm *logicvc)
109efeeaefeSPaul Kocialkowski {
110efeeaefeSPaul Kocialkowski switch (logicvc->config.display_interface) {
111efeeaefeSPaul Kocialkowski case LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS:
112efeeaefeSPaul Kocialkowski case LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS_CAMERA:
113efeeaefeSPaul Kocialkowski case LOGICVC_DISPLAY_INTERFACE_LVDS_3BITS:
114efeeaefeSPaul Kocialkowski return DRM_MODE_CONNECTOR_LVDS;
115efeeaefeSPaul Kocialkowski case LOGICVC_DISPLAY_INTERFACE_DVI:
116efeeaefeSPaul Kocialkowski return DRM_MODE_CONNECTOR_DVID;
117efeeaefeSPaul Kocialkowski case LOGICVC_DISPLAY_INTERFACE_RGB:
118efeeaefeSPaul Kocialkowski return DRM_MODE_CONNECTOR_DPI;
119efeeaefeSPaul Kocialkowski default:
120efeeaefeSPaul Kocialkowski return DRM_MODE_CONNECTOR_Unknown;
121efeeaefeSPaul Kocialkowski }
122efeeaefeSPaul Kocialkowski }
123efeeaefeSPaul Kocialkowski
logicvc_interface_native_connector(struct logicvc_drm * logicvc)124efeeaefeSPaul Kocialkowski static bool logicvc_interface_native_connector(struct logicvc_drm *logicvc)
125efeeaefeSPaul Kocialkowski {
126efeeaefeSPaul Kocialkowski switch (logicvc->config.display_interface) {
127efeeaefeSPaul Kocialkowski case LOGICVC_DISPLAY_INTERFACE_DVI:
128efeeaefeSPaul Kocialkowski return true;
129efeeaefeSPaul Kocialkowski default:
130efeeaefeSPaul Kocialkowski return false;
131efeeaefeSPaul Kocialkowski }
132efeeaefeSPaul Kocialkowski }
133efeeaefeSPaul Kocialkowski
logicvc_interface_attach_crtc(struct logicvc_drm * logicvc)134efeeaefeSPaul Kocialkowski void logicvc_interface_attach_crtc(struct logicvc_drm *logicvc)
135efeeaefeSPaul Kocialkowski {
136efeeaefeSPaul Kocialkowski uint32_t possible_crtcs = drm_crtc_mask(&logicvc->crtc->drm_crtc);
137efeeaefeSPaul Kocialkowski
138efeeaefeSPaul Kocialkowski logicvc->interface->drm_encoder.possible_crtcs = possible_crtcs;
139efeeaefeSPaul Kocialkowski }
140efeeaefeSPaul Kocialkowski
logicvc_interface_init(struct logicvc_drm * logicvc)141efeeaefeSPaul Kocialkowski int logicvc_interface_init(struct logicvc_drm *logicvc)
142efeeaefeSPaul Kocialkowski {
143efeeaefeSPaul Kocialkowski struct logicvc_interface *interface;
144efeeaefeSPaul Kocialkowski struct drm_device *drm_dev = &logicvc->drm_dev;
145efeeaefeSPaul Kocialkowski struct device *dev = drm_dev->dev;
146efeeaefeSPaul Kocialkowski struct device_node *of_node = dev->of_node;
147efeeaefeSPaul Kocialkowski int encoder_type = logicvc_interface_encoder_type(logicvc);
148efeeaefeSPaul Kocialkowski int connector_type = logicvc_interface_connector_type(logicvc);
149efeeaefeSPaul Kocialkowski bool native_connector = logicvc_interface_native_connector(logicvc);
150efeeaefeSPaul Kocialkowski int ret;
151efeeaefeSPaul Kocialkowski
152efeeaefeSPaul Kocialkowski interface = devm_kzalloc(dev, sizeof(*interface), GFP_KERNEL);
153efeeaefeSPaul Kocialkowski if (!interface) {
154efeeaefeSPaul Kocialkowski ret = -ENOMEM;
155efeeaefeSPaul Kocialkowski goto error_early;
156efeeaefeSPaul Kocialkowski }
157efeeaefeSPaul Kocialkowski
158efeeaefeSPaul Kocialkowski ret = drm_of_find_panel_or_bridge(of_node, 0, 0, &interface->drm_panel,
159efeeaefeSPaul Kocialkowski &interface->drm_bridge);
160efeeaefeSPaul Kocialkowski if (ret == -EPROBE_DEFER)
161efeeaefeSPaul Kocialkowski goto error_early;
162efeeaefeSPaul Kocialkowski
163efeeaefeSPaul Kocialkowski ret = drm_encoder_init(drm_dev, &interface->drm_encoder,
164efeeaefeSPaul Kocialkowski &logicvc_encoder_funcs, encoder_type, NULL);
165efeeaefeSPaul Kocialkowski if (ret) {
166efeeaefeSPaul Kocialkowski drm_err(drm_dev, "Failed to initialize encoder\n");
167efeeaefeSPaul Kocialkowski goto error_early;
168efeeaefeSPaul Kocialkowski }
169efeeaefeSPaul Kocialkowski
170efeeaefeSPaul Kocialkowski drm_encoder_helper_add(&interface->drm_encoder,
171efeeaefeSPaul Kocialkowski &logicvc_encoder_helper_funcs);
172efeeaefeSPaul Kocialkowski
173efeeaefeSPaul Kocialkowski if (native_connector || interface->drm_panel) {
174efeeaefeSPaul Kocialkowski ret = drm_connector_init(drm_dev, &interface->drm_connector,
175efeeaefeSPaul Kocialkowski &logicvc_connector_funcs,
176efeeaefeSPaul Kocialkowski connector_type);
177efeeaefeSPaul Kocialkowski if (ret) {
178efeeaefeSPaul Kocialkowski drm_err(drm_dev, "Failed to initialize connector\n");
179efeeaefeSPaul Kocialkowski goto error_encoder;
180efeeaefeSPaul Kocialkowski }
181efeeaefeSPaul Kocialkowski
182efeeaefeSPaul Kocialkowski drm_connector_helper_add(&interface->drm_connector,
183efeeaefeSPaul Kocialkowski &logicvc_connector_helper_funcs);
184efeeaefeSPaul Kocialkowski
185efeeaefeSPaul Kocialkowski ret = drm_connector_attach_encoder(&interface->drm_connector,
186efeeaefeSPaul Kocialkowski &interface->drm_encoder);
187efeeaefeSPaul Kocialkowski if (ret) {
188efeeaefeSPaul Kocialkowski drm_err(drm_dev,
189efeeaefeSPaul Kocialkowski "Failed to attach connector to encoder\n");
190efeeaefeSPaul Kocialkowski goto error_encoder;
191efeeaefeSPaul Kocialkowski }
192efeeaefeSPaul Kocialkowski }
193efeeaefeSPaul Kocialkowski
194efeeaefeSPaul Kocialkowski if (interface->drm_bridge) {
195efeeaefeSPaul Kocialkowski ret = drm_bridge_attach(&interface->drm_encoder,
196efeeaefeSPaul Kocialkowski interface->drm_bridge, NULL, 0);
197efeeaefeSPaul Kocialkowski if (ret) {
198efeeaefeSPaul Kocialkowski drm_err(drm_dev,
199efeeaefeSPaul Kocialkowski "Failed to attach bridge to encoder\n");
200efeeaefeSPaul Kocialkowski goto error_encoder;
201efeeaefeSPaul Kocialkowski }
202efeeaefeSPaul Kocialkowski }
203efeeaefeSPaul Kocialkowski
204efeeaefeSPaul Kocialkowski logicvc->interface = interface;
205efeeaefeSPaul Kocialkowski
206efeeaefeSPaul Kocialkowski return 0;
207efeeaefeSPaul Kocialkowski
208efeeaefeSPaul Kocialkowski error_encoder:
209efeeaefeSPaul Kocialkowski drm_encoder_cleanup(&interface->drm_encoder);
210efeeaefeSPaul Kocialkowski
211efeeaefeSPaul Kocialkowski error_early:
212efeeaefeSPaul Kocialkowski return ret;
213efeeaefeSPaul Kocialkowski }
214