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