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