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