xref: /linux/drivers/gpu/drm/omapdrm/omap_encoder.c (revision 44f57d78)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
4  * Author: Rob Clark <rob@ti.com>
5  */
6 
7 #include <linux/list.h>
8 
9 #include <drm/drm_crtc.h>
10 #include <drm/drm_modeset_helper_vtables.h>
11 #include <drm/drm_edid.h>
12 #include <drm/drm_panel.h>
13 
14 #include "omap_drv.h"
15 
16 /*
17  * encoder funcs
18  */
19 
20 #define to_omap_encoder(x) container_of(x, struct omap_encoder, base)
21 
22 /* The encoder and connector both map to same dssdev.. the encoder
23  * handles the 'active' parts, ie. anything the modifies the state
24  * of the hw, and the connector handles the 'read-only' parts, like
25  * detecting connection and reading edid.
26  */
27 struct omap_encoder {
28 	struct drm_encoder base;
29 	struct omap_dss_device *output;
30 };
31 
32 static void omap_encoder_destroy(struct drm_encoder *encoder)
33 {
34 	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
35 
36 	drm_encoder_cleanup(encoder);
37 	kfree(omap_encoder);
38 }
39 
40 static const struct drm_encoder_funcs omap_encoder_funcs = {
41 	.destroy = omap_encoder_destroy,
42 };
43 
44 static void omap_encoder_update_videomode_flags(struct videomode *vm,
45 						u32 bus_flags)
46 {
47 	if (!(vm->flags & (DISPLAY_FLAGS_DE_LOW |
48 			   DISPLAY_FLAGS_DE_HIGH))) {
49 		if (bus_flags & DRM_BUS_FLAG_DE_LOW)
50 			vm->flags |= DISPLAY_FLAGS_DE_LOW;
51 		else if (bus_flags & DRM_BUS_FLAG_DE_HIGH)
52 			vm->flags |= DISPLAY_FLAGS_DE_HIGH;
53 	}
54 
55 	if (!(vm->flags & (DISPLAY_FLAGS_PIXDATA_POSEDGE |
56 			   DISPLAY_FLAGS_PIXDATA_NEGEDGE))) {
57 		if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE)
58 			vm->flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE;
59 		else if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
60 			vm->flags |= DISPLAY_FLAGS_PIXDATA_NEGEDGE;
61 	}
62 
63 	if (!(vm->flags & (DISPLAY_FLAGS_SYNC_POSEDGE |
64 			   DISPLAY_FLAGS_SYNC_NEGEDGE))) {
65 		if (bus_flags & DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE)
66 			vm->flags |= DISPLAY_FLAGS_SYNC_POSEDGE;
67 		else if (bus_flags & DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE)
68 			vm->flags |= DISPLAY_FLAGS_SYNC_NEGEDGE;
69 	}
70 }
71 
72 static void omap_encoder_hdmi_mode_set(struct drm_connector *connector,
73 				       struct drm_encoder *encoder,
74 				       struct drm_display_mode *adjusted_mode)
75 {
76 	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
77 	struct omap_dss_device *dssdev = omap_encoder->output;
78 	bool hdmi_mode;
79 
80 	hdmi_mode = omap_connector_get_hdmi_mode(connector);
81 
82 	if (dssdev->ops->hdmi.set_hdmi_mode)
83 		dssdev->ops->hdmi.set_hdmi_mode(dssdev, hdmi_mode);
84 
85 	if (hdmi_mode && dssdev->ops->hdmi.set_infoframe) {
86 		struct hdmi_avi_infoframe avi;
87 		int r;
88 
89 		r = drm_hdmi_avi_infoframe_from_display_mode(&avi, connector,
90 							     adjusted_mode);
91 		if (r == 0)
92 			dssdev->ops->hdmi.set_infoframe(dssdev, &avi);
93 	}
94 }
95 
96 static void omap_encoder_mode_set(struct drm_encoder *encoder,
97 				  struct drm_display_mode *mode,
98 				  struct drm_display_mode *adjusted_mode)
99 {
100 	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
101 	struct omap_dss_device *output = omap_encoder->output;
102 	struct omap_dss_device *dssdev;
103 	struct drm_device *dev = encoder->dev;
104 	struct drm_connector *connector;
105 	struct drm_bridge *bridge;
106 	struct videomode vm = { 0 };
107 	u32 bus_flags;
108 
109 	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
110 		if (connector->encoder == encoder)
111 			break;
112 	}
113 
114 	drm_display_mode_to_videomode(adjusted_mode, &vm);
115 
116 	/*
117 	 * HACK: This fixes the vm flags.
118 	 * struct drm_display_mode does not contain the VSYNC/HSYNC/DE flags and
119 	 * they get lost when converting back and forth between struct
120 	 * drm_display_mode and struct videomode. The hack below goes and
121 	 * fetches the missing flags.
122 	 *
123 	 * A better solution is to use DRM's bus-flags through the whole driver.
124 	 */
125 	for (dssdev = output; dssdev; dssdev = dssdev->next)
126 		omap_encoder_update_videomode_flags(&vm, dssdev->bus_flags);
127 
128 	for (bridge = output->bridge; bridge; bridge = bridge->next) {
129 		if (!bridge->timings)
130 			continue;
131 
132 		bus_flags = bridge->timings->input_bus_flags;
133 		omap_encoder_update_videomode_flags(&vm, bus_flags);
134 	}
135 
136 	bus_flags = connector->display_info.bus_flags;
137 	omap_encoder_update_videomode_flags(&vm, bus_flags);
138 
139 	/* Set timings for all devices in the display pipeline. */
140 	dss_mgr_set_timings(output, &vm);
141 
142 	for (dssdev = output; dssdev; dssdev = dssdev->next) {
143 		if (dssdev->ops->set_timings)
144 			dssdev->ops->set_timings(dssdev, adjusted_mode);
145 	}
146 
147 	/* Set the HDMI mode and HDMI infoframe if applicable. */
148 	if (output->type == OMAP_DISPLAY_TYPE_HDMI)
149 		omap_encoder_hdmi_mode_set(connector, encoder, adjusted_mode);
150 }
151 
152 static void omap_encoder_disable(struct drm_encoder *encoder)
153 {
154 	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
155 	struct omap_dss_device *dssdev = omap_encoder->output;
156 	struct drm_device *dev = encoder->dev;
157 
158 	dev_dbg(dev->dev, "disable(%s)\n", dssdev->name);
159 
160 	/* Disable the panel if present. */
161 	if (dssdev->panel) {
162 		drm_panel_disable(dssdev->panel);
163 		drm_panel_unprepare(dssdev->panel);
164 	}
165 
166 	/*
167 	 * Disable the chain of external devices, starting at the one at the
168 	 * internal encoder's output.
169 	 */
170 	omapdss_device_disable(dssdev->next);
171 
172 	/*
173 	 * Disable the internal encoder. This will disable the DSS output. The
174 	 * DSI is treated as an exception as DSI pipelines still use the legacy
175 	 * flow where the pipeline output controls the encoder.
176 	 */
177 	if (dssdev->type != OMAP_DISPLAY_TYPE_DSI) {
178 		dssdev->ops->disable(dssdev);
179 		dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
180 	}
181 
182 	/*
183 	 * Perform the post-disable operations on the chain of external devices
184 	 * to complete the display pipeline disable.
185 	 */
186 	omapdss_device_post_disable(dssdev->next);
187 }
188 
189 static void omap_encoder_enable(struct drm_encoder *encoder)
190 {
191 	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
192 	struct omap_dss_device *dssdev = omap_encoder->output;
193 	struct drm_device *dev = encoder->dev;
194 
195 	dev_dbg(dev->dev, "enable(%s)\n", dssdev->name);
196 
197 	/* Prepare the chain of external devices for pipeline enable. */
198 	omapdss_device_pre_enable(dssdev->next);
199 
200 	/*
201 	 * Enable the internal encoder. This will enable the DSS output. The
202 	 * DSI is treated as an exception as DSI pipelines still use the legacy
203 	 * flow where the pipeline output controls the encoder.
204 	 */
205 	if (dssdev->type != OMAP_DISPLAY_TYPE_DSI) {
206 		dssdev->ops->enable(dssdev);
207 		dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
208 	}
209 
210 	/*
211 	 * Enable the chain of external devices, starting at the one at the
212 	 * internal encoder's output.
213 	 */
214 	omapdss_device_enable(dssdev->next);
215 
216 	/* Enable the panel if present. */
217 	if (dssdev->panel) {
218 		drm_panel_prepare(dssdev->panel);
219 		drm_panel_enable(dssdev->panel);
220 	}
221 }
222 
223 static int omap_encoder_atomic_check(struct drm_encoder *encoder,
224 				     struct drm_crtc_state *crtc_state,
225 				     struct drm_connector_state *conn_state)
226 {
227 	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
228 	enum drm_mode_status status;
229 
230 	status = omap_connector_mode_fixup(omap_encoder->output,
231 					   &crtc_state->mode,
232 					   &crtc_state->adjusted_mode);
233 	if (status != MODE_OK) {
234 		dev_err(encoder->dev->dev, "invalid timings: %d\n", status);
235 		return -EINVAL;
236 	}
237 
238 	return 0;
239 }
240 
241 static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs = {
242 	.mode_set = omap_encoder_mode_set,
243 	.disable = omap_encoder_disable,
244 	.enable = omap_encoder_enable,
245 	.atomic_check = omap_encoder_atomic_check,
246 };
247 
248 /* initialize encoder */
249 struct drm_encoder *omap_encoder_init(struct drm_device *dev,
250 				      struct omap_dss_device *output)
251 {
252 	struct drm_encoder *encoder = NULL;
253 	struct omap_encoder *omap_encoder;
254 
255 	omap_encoder = kzalloc(sizeof(*omap_encoder), GFP_KERNEL);
256 	if (!omap_encoder)
257 		goto fail;
258 
259 	omap_encoder->output = output;
260 
261 	encoder = &omap_encoder->base;
262 
263 	drm_encoder_init(dev, encoder, &omap_encoder_funcs,
264 			 DRM_MODE_ENCODER_TMDS, NULL);
265 	drm_encoder_helper_add(encoder, &omap_encoder_helper_funcs);
266 
267 	return encoder;
268 
269 fail:
270 	if (encoder)
271 		omap_encoder_destroy(encoder);
272 
273 	return NULL;
274 }
275