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