1 /*
2  * i.MX drm driver - parallel display implementation
3  *
4  * Copyright (C) 2012 Sascha Hauer, Pengutronix
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  */
15 
16 #include <linux/component.h>
17 #include <linux/module.h>
18 #include <drm/drmP.h>
19 #include <drm/drm_fb_helper.h>
20 #include <drm/drm_crtc_helper.h>
21 #include <drm/drm_panel.h>
22 #include <linux/videodev2.h>
23 #include <video/of_display_timing.h>
24 #include <linux/of_graph.h>
25 
26 #include "imx-drm.h"
27 
28 #define con_to_imxpd(x) container_of(x, struct imx_parallel_display, connector)
29 #define enc_to_imxpd(x) container_of(x, struct imx_parallel_display, encoder)
30 
31 struct imx_parallel_display {
32 	struct drm_connector connector;
33 	struct drm_encoder encoder;
34 	struct device *dev;
35 	void *edid;
36 	int edid_len;
37 	u32 bus_format;
38 	int mode_valid;
39 	struct drm_display_mode mode;
40 	struct drm_panel *panel;
41 };
42 
imx_pd_connector_detect(struct drm_connector * connector,bool force)43 static enum drm_connector_status imx_pd_connector_detect(
44 		struct drm_connector *connector, bool force)
45 {
46 	return connector_status_connected;
47 }
48 
imx_pd_connector_get_modes(struct drm_connector * connector)49 static int imx_pd_connector_get_modes(struct drm_connector *connector)
50 {
51 	struct imx_parallel_display *imxpd = con_to_imxpd(connector);
52 	struct device_node *np = imxpd->dev->of_node;
53 	int num_modes = 0;
54 
55 	if (imxpd->panel && imxpd->panel->funcs &&
56 	    imxpd->panel->funcs->get_modes) {
57 		struct drm_display_info *di = &connector->display_info;
58 
59 		num_modes = imxpd->panel->funcs->get_modes(imxpd->panel);
60 		if (!imxpd->bus_format && di->num_bus_formats)
61 			imxpd->bus_format = di->bus_formats[0];
62 		if (num_modes > 0)
63 			return num_modes;
64 	}
65 
66 	if (imxpd->edid) {
67 		drm_mode_connector_update_edid_property(connector, imxpd->edid);
68 		num_modes = drm_add_edid_modes(connector, imxpd->edid);
69 	}
70 
71 	if (imxpd->mode_valid) {
72 		struct drm_display_mode *mode = drm_mode_create(connector->dev);
73 
74 		if (!mode)
75 			return -EINVAL;
76 		drm_mode_copy(mode, &imxpd->mode);
77 		mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
78 		drm_mode_probed_add(connector, mode);
79 		num_modes++;
80 	}
81 
82 	if (np) {
83 		struct drm_display_mode *mode = drm_mode_create(connector->dev);
84 
85 		if (!mode)
86 			return -EINVAL;
87 		of_get_drm_display_mode(np, &imxpd->mode, OF_USE_NATIVE_MODE);
88 		drm_mode_copy(mode, &imxpd->mode);
89 		mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
90 		drm_mode_probed_add(connector, mode);
91 		num_modes++;
92 	}
93 
94 	return num_modes;
95 }
96 
imx_pd_connector_best_encoder(struct drm_connector * connector)97 static struct drm_encoder *imx_pd_connector_best_encoder(
98 		struct drm_connector *connector)
99 {
100 	struct imx_parallel_display *imxpd = con_to_imxpd(connector);
101 
102 	return &imxpd->encoder;
103 }
104 
imx_pd_encoder_dpms(struct drm_encoder * encoder,int mode)105 static void imx_pd_encoder_dpms(struct drm_encoder *encoder, int mode)
106 {
107 	struct imx_parallel_display *imxpd = enc_to_imxpd(encoder);
108 
109 	if (mode != DRM_MODE_DPMS_ON)
110 		drm_panel_disable(imxpd->panel);
111 	else
112 		drm_panel_enable(imxpd->panel);
113 }
114 
imx_pd_encoder_mode_fixup(struct drm_encoder * encoder,const struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode)115 static bool imx_pd_encoder_mode_fixup(struct drm_encoder *encoder,
116 			   const struct drm_display_mode *mode,
117 			   struct drm_display_mode *adjusted_mode)
118 {
119 	return true;
120 }
121 
imx_pd_encoder_prepare(struct drm_encoder * encoder)122 static void imx_pd_encoder_prepare(struct drm_encoder *encoder)
123 {
124 	struct imx_parallel_display *imxpd = enc_to_imxpd(encoder);
125 
126 	imx_drm_set_bus_format(encoder, imxpd->bus_format);
127 }
128 
imx_pd_encoder_commit(struct drm_encoder * encoder)129 static void imx_pd_encoder_commit(struct drm_encoder *encoder)
130 {
131 	struct imx_parallel_display *imxpd = enc_to_imxpd(encoder);
132 
133 	drm_panel_prepare(imxpd->panel);
134 	drm_panel_enable(imxpd->panel);
135 }
136 
imx_pd_encoder_mode_set(struct drm_encoder * encoder,struct drm_display_mode * orig_mode,struct drm_display_mode * mode)137 static void imx_pd_encoder_mode_set(struct drm_encoder *encoder,
138 			 struct drm_display_mode *orig_mode,
139 			 struct drm_display_mode *mode)
140 {
141 }
142 
imx_pd_encoder_disable(struct drm_encoder * encoder)143 static void imx_pd_encoder_disable(struct drm_encoder *encoder)
144 {
145 	struct imx_parallel_display *imxpd = enc_to_imxpd(encoder);
146 
147 	drm_panel_disable(imxpd->panel);
148 	drm_panel_unprepare(imxpd->panel);
149 }
150 
151 static struct drm_connector_funcs imx_pd_connector_funcs = {
152 	.dpms = drm_helper_connector_dpms,
153 	.fill_modes = drm_helper_probe_single_connector_modes,
154 	.detect = imx_pd_connector_detect,
155 	.destroy = imx_drm_connector_destroy,
156 };
157 
158 static struct drm_connector_helper_funcs imx_pd_connector_helper_funcs = {
159 	.get_modes = imx_pd_connector_get_modes,
160 	.best_encoder = imx_pd_connector_best_encoder,
161 };
162 
163 static struct drm_encoder_funcs imx_pd_encoder_funcs = {
164 	.destroy = imx_drm_encoder_destroy,
165 };
166 
167 static struct drm_encoder_helper_funcs imx_pd_encoder_helper_funcs = {
168 	.dpms = imx_pd_encoder_dpms,
169 	.mode_fixup = imx_pd_encoder_mode_fixup,
170 	.prepare = imx_pd_encoder_prepare,
171 	.commit = imx_pd_encoder_commit,
172 	.mode_set = imx_pd_encoder_mode_set,
173 	.disable = imx_pd_encoder_disable,
174 };
175 
imx_pd_register(struct drm_device * drm,struct imx_parallel_display * imxpd)176 static int imx_pd_register(struct drm_device *drm,
177 	struct imx_parallel_display *imxpd)
178 {
179 	int ret;
180 
181 	ret = imx_drm_encoder_parse_of(drm, &imxpd->encoder,
182 				       imxpd->dev->of_node);
183 	if (ret)
184 		return ret;
185 
186 	/* set the connector's dpms to OFF so that
187 	 * drm_helper_connector_dpms() won't return
188 	 * immediately since the current state is ON
189 	 * at this point.
190 	 */
191 	imxpd->connector.dpms = DRM_MODE_DPMS_OFF;
192 
193 	drm_encoder_helper_add(&imxpd->encoder, &imx_pd_encoder_helper_funcs);
194 	drm_encoder_init(drm, &imxpd->encoder, &imx_pd_encoder_funcs,
195 			 DRM_MODE_ENCODER_NONE);
196 
197 	drm_connector_helper_add(&imxpd->connector,
198 			&imx_pd_connector_helper_funcs);
199 	drm_connector_init(drm, &imxpd->connector, &imx_pd_connector_funcs,
200 			   DRM_MODE_CONNECTOR_VGA);
201 
202 	if (imxpd->panel)
203 		drm_panel_attach(imxpd->panel, &imxpd->connector);
204 
205 	drm_mode_connector_attach_encoder(&imxpd->connector, &imxpd->encoder);
206 
207 	imxpd->connector.encoder = &imxpd->encoder;
208 
209 	return 0;
210 }
211 
imx_pd_bind(struct device * dev,struct device * master,void * data)212 static int imx_pd_bind(struct device *dev, struct device *master, void *data)
213 {
214 	struct drm_device *drm = data;
215 	struct device_node *np = dev->of_node;
216 	struct device_node *port;
217 	const u8 *edidp;
218 	struct imx_parallel_display *imxpd;
219 	int ret;
220 	const char *fmt;
221 
222 	imxpd = devm_kzalloc(dev, sizeof(*imxpd), GFP_KERNEL);
223 	if (!imxpd)
224 		return -ENOMEM;
225 
226 	edidp = of_get_property(np, "edid", &imxpd->edid_len);
227 	if (edidp)
228 		imxpd->edid = kmemdup(edidp, imxpd->edid_len, GFP_KERNEL);
229 
230 	ret = of_property_read_string(np, "interface-pix-fmt", &fmt);
231 	if (!ret) {
232 		if (!strcmp(fmt, "rgb24"))
233 			imxpd->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
234 		else if (!strcmp(fmt, "rgb565"))
235 			imxpd->bus_format = MEDIA_BUS_FMT_RGB565_1X16;
236 		else if (!strcmp(fmt, "bgr666"))
237 			imxpd->bus_format = MEDIA_BUS_FMT_RGB666_1X18;
238 		else if (!strcmp(fmt, "lvds666"))
239 			imxpd->bus_format = MEDIA_BUS_FMT_RGB666_1X24_CPADHI;
240 	}
241 
242 	/* port@1 is the output port */
243 	port = of_graph_get_port_by_id(np, 1);
244 	if (port) {
245 		struct device_node *endpoint, *remote;
246 
247 		endpoint = of_get_child_by_name(port, "endpoint");
248 		if (endpoint) {
249 			remote = of_graph_get_remote_port_parent(endpoint);
250 			if (remote)
251 				imxpd->panel = of_drm_find_panel(remote);
252 			if (!imxpd->panel)
253 				return -EPROBE_DEFER;
254 		}
255 	}
256 
257 	imxpd->dev = dev;
258 
259 	ret = imx_pd_register(drm, imxpd);
260 	if (ret)
261 		return ret;
262 
263 	dev_set_drvdata(dev, imxpd);
264 
265 	return 0;
266 }
267 
imx_pd_unbind(struct device * dev,struct device * master,void * data)268 static void imx_pd_unbind(struct device *dev, struct device *master,
269 	void *data)
270 {
271 	struct imx_parallel_display *imxpd = dev_get_drvdata(dev);
272 
273 	imxpd->encoder.funcs->destroy(&imxpd->encoder);
274 	imxpd->connector.funcs->destroy(&imxpd->connector);
275 
276 	kfree(imxpd->edid);
277 }
278 
279 static const struct component_ops imx_pd_ops = {
280 	.bind	= imx_pd_bind,
281 	.unbind	= imx_pd_unbind,
282 };
283 
imx_pd_probe(struct platform_device * pdev)284 static int imx_pd_probe(struct platform_device *pdev)
285 {
286 	return component_add(&pdev->dev, &imx_pd_ops);
287 }
288 
imx_pd_remove(struct platform_device * pdev)289 static int imx_pd_remove(struct platform_device *pdev)
290 {
291 	component_del(&pdev->dev, &imx_pd_ops);
292 
293 	return 0;
294 }
295 
296 static const struct of_device_id imx_pd_dt_ids[] = {
297 	{ .compatible = "fsl,imx-parallel-display", },
298 	{ /* sentinel */ }
299 };
300 MODULE_DEVICE_TABLE(of, imx_pd_dt_ids);
301 
302 static struct platform_driver imx_pd_driver = {
303 	.probe		= imx_pd_probe,
304 	.remove		= imx_pd_remove,
305 	.driver		= {
306 		.of_match_table = imx_pd_dt_ids,
307 		.name	= "imx-parallel-display",
308 	},
309 };
310 
311 module_platform_driver(imx_pd_driver);
312 
313 MODULE_DESCRIPTION("i.MX parallel display driver");
314 MODULE_AUTHOR("Sascha Hauer, Pengutronix");
315 MODULE_LICENSE("GPL");
316 MODULE_ALIAS("platform:imx-parallel-display");
317