• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Freescale i.MX drm driver
3  *
4  * Copyright (C) 2011 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/device.h>
18 #include <linux/dma-buf.h>
19 #include <linux/module.h>
20 #include <linux/platform_device.h>
21 #include <drm/drmP.h>
22 #include <drm/drm_atomic.h>
23 #include <drm/drm_atomic_helper.h>
24 #include <drm/drm_fb_helper.h>
25 #include <drm/drm_crtc_helper.h>
26 #include <drm/drm_gem_cma_helper.h>
27 #include <drm/drm_gem_framebuffer_helper.h>
28 #include <drm/drm_fb_cma_helper.h>
29 #include <drm/drm_plane_helper.h>
30 #include <drm/drm_of.h>
31 #include <video/imx-ipu-v3.h>
32 
33 #include "imx-drm.h"
34 #include "ipuv3-plane.h"
35 
36 #define MAX_CRTC	4
37 
38 #if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION)
39 static int legacyfb_depth = 16;
40 module_param(legacyfb_depth, int, 0444);
41 #endif
42 
43 DEFINE_DRM_GEM_CMA_FOPS(imx_drm_driver_fops);
44 
imx_drm_connector_destroy(struct drm_connector * connector)45 void imx_drm_connector_destroy(struct drm_connector *connector)
46 {
47 	drm_connector_unregister(connector);
48 	drm_connector_cleanup(connector);
49 }
50 EXPORT_SYMBOL_GPL(imx_drm_connector_destroy);
51 
imx_drm_encoder_destroy(struct drm_encoder * encoder)52 void imx_drm_encoder_destroy(struct drm_encoder *encoder)
53 {
54 	drm_encoder_cleanup(encoder);
55 }
56 EXPORT_SYMBOL_GPL(imx_drm_encoder_destroy);
57 
imx_drm_atomic_check(struct drm_device * dev,struct drm_atomic_state * state)58 static int imx_drm_atomic_check(struct drm_device *dev,
59 				struct drm_atomic_state *state)
60 {
61 	int ret;
62 
63 	ret = drm_atomic_helper_check_modeset(dev, state);
64 	if (ret)
65 		return ret;
66 
67 	ret = drm_atomic_helper_check_planes(dev, state);
68 	if (ret)
69 		return ret;
70 
71 	/*
72 	 * Check modeset again in case crtc_state->mode_changed is
73 	 * updated in plane's ->atomic_check callback.
74 	 */
75 	ret = drm_atomic_helper_check_modeset(dev, state);
76 	if (ret)
77 		return ret;
78 
79 	/* Assign PRG/PRE channels and check if all constrains are satisfied. */
80 	ret = ipu_planes_assign_pre(dev, state);
81 	if (ret)
82 		return ret;
83 
84 	return ret;
85 }
86 
87 static const struct drm_mode_config_funcs imx_drm_mode_config_funcs = {
88 	.fb_create = drm_gem_fb_create,
89 	.output_poll_changed = drm_fb_helper_output_poll_changed,
90 	.atomic_check = imx_drm_atomic_check,
91 	.atomic_commit = drm_atomic_helper_commit,
92 };
93 
imx_drm_atomic_commit_tail(struct drm_atomic_state * state)94 static void imx_drm_atomic_commit_tail(struct drm_atomic_state *state)
95 {
96 	struct drm_device *dev = state->dev;
97 	struct drm_plane *plane;
98 	struct drm_plane_state *old_plane_state, *new_plane_state;
99 	bool plane_disabling = false;
100 	int i;
101 
102 	drm_atomic_helper_commit_modeset_disables(dev, state);
103 
104 	drm_atomic_helper_commit_planes(dev, state,
105 				DRM_PLANE_COMMIT_ACTIVE_ONLY |
106 				DRM_PLANE_COMMIT_NO_DISABLE_AFTER_MODESET);
107 
108 	drm_atomic_helper_commit_modeset_enables(dev, state);
109 
110 	for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
111 		if (drm_atomic_plane_disabling(old_plane_state, new_plane_state))
112 			plane_disabling = true;
113 	}
114 
115 	/*
116 	 * The flip done wait is only strictly required by imx-drm if a deferred
117 	 * plane disable is in-flight. As the core requires blocking commits
118 	 * to wait for the flip it is done here unconditionally. This keeps the
119 	 * workitem around a bit longer than required for the majority of
120 	 * non-blocking commits, but we accept that for the sake of simplicity.
121 	 */
122 	drm_atomic_helper_wait_for_flip_done(dev, state);
123 
124 	if (plane_disabling) {
125 		for_each_old_plane_in_state(state, plane, old_plane_state, i)
126 			ipu_plane_disable_deferred(plane);
127 
128 	}
129 
130 	drm_atomic_helper_commit_hw_done(state);
131 }
132 
133 static const struct drm_mode_config_helper_funcs imx_drm_mode_config_helpers = {
134 	.atomic_commit_tail = imx_drm_atomic_commit_tail,
135 };
136 
137 
imx_drm_encoder_parse_of(struct drm_device * drm,struct drm_encoder * encoder,struct device_node * np)138 int imx_drm_encoder_parse_of(struct drm_device *drm,
139 	struct drm_encoder *encoder, struct device_node *np)
140 {
141 	uint32_t crtc_mask = drm_of_find_possible_crtcs(drm, np);
142 
143 	/*
144 	 * If we failed to find the CRTC(s) which this encoder is
145 	 * supposed to be connected to, it's because the CRTC has
146 	 * not been registered yet.  Defer probing, and hope that
147 	 * the required CRTC is added later.
148 	 */
149 	if (crtc_mask == 0)
150 		return -EPROBE_DEFER;
151 
152 	encoder->possible_crtcs = crtc_mask;
153 
154 	/* FIXME: this is the mask of outputs which can clone this output. */
155 	encoder->possible_clones = ~0;
156 
157 	return 0;
158 }
159 EXPORT_SYMBOL_GPL(imx_drm_encoder_parse_of);
160 
161 static const struct drm_ioctl_desc imx_drm_ioctls[] = {
162 	/* none so far */
163 };
164 
165 static struct drm_driver imx_drm_driver = {
166 	.driver_features	= DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME |
167 				  DRIVER_ATOMIC,
168 	.lastclose		= drm_fb_helper_lastclose,
169 	.gem_free_object_unlocked = drm_gem_cma_free_object,
170 	.gem_vm_ops		= &drm_gem_cma_vm_ops,
171 	.dumb_create		= drm_gem_cma_dumb_create,
172 
173 	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd,
174 	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle,
175 	.gem_prime_import	= drm_gem_prime_import,
176 	.gem_prime_export	= drm_gem_prime_export,
177 	.gem_prime_get_sg_table	= drm_gem_cma_prime_get_sg_table,
178 	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
179 	.gem_prime_vmap		= drm_gem_cma_prime_vmap,
180 	.gem_prime_vunmap	= drm_gem_cma_prime_vunmap,
181 	.gem_prime_mmap		= drm_gem_cma_prime_mmap,
182 	.ioctls			= imx_drm_ioctls,
183 	.num_ioctls		= ARRAY_SIZE(imx_drm_ioctls),
184 	.fops			= &imx_drm_driver_fops,
185 	.name			= "imx-drm",
186 	.desc			= "i.MX DRM graphics",
187 	.date			= "20120507",
188 	.major			= 1,
189 	.minor			= 0,
190 	.patchlevel		= 0,
191 };
192 
compare_of(struct device * dev,void * data)193 static int compare_of(struct device *dev, void *data)
194 {
195 	struct device_node *np = data;
196 
197 	/* Special case for DI, dev->of_node may not be set yet */
198 	if (strcmp(dev->driver->name, "imx-ipuv3-crtc") == 0) {
199 		struct ipu_client_platformdata *pdata = dev->platform_data;
200 
201 		return pdata->of_node == np;
202 	}
203 
204 	/* Special case for LDB, one device for two channels */
205 	if (of_node_cmp(np->name, "lvds-channel") == 0) {
206 		np = of_get_parent(np);
207 		of_node_put(np);
208 	}
209 
210 	return dev->of_node == np;
211 }
212 
imx_drm_bind(struct device * dev)213 static int imx_drm_bind(struct device *dev)
214 {
215 	struct drm_device *drm;
216 	int ret;
217 
218 	drm = drm_dev_alloc(&imx_drm_driver, dev);
219 	if (IS_ERR(drm))
220 		return PTR_ERR(drm);
221 
222 	/*
223 	 * enable drm irq mode.
224 	 * - with irq_enabled = true, we can use the vblank feature.
225 	 *
226 	 * P.S. note that we wouldn't use drm irq handler but
227 	 *      just specific driver own one instead because
228 	 *      drm framework supports only one irq handler and
229 	 *      drivers can well take care of their interrupts
230 	 */
231 	drm->irq_enabled = true;
232 
233 	/*
234 	 * set max width and height as default value(4096x4096).
235 	 * this value would be used to check framebuffer size limitation
236 	 * at drm_mode_addfb().
237 	 */
238 	drm->mode_config.min_width = 1;
239 	drm->mode_config.min_height = 1;
240 	drm->mode_config.max_width = 4096;
241 	drm->mode_config.max_height = 4096;
242 	drm->mode_config.funcs = &imx_drm_mode_config_funcs;
243 	drm->mode_config.helper_private = &imx_drm_mode_config_helpers;
244 	drm->mode_config.allow_fb_modifiers = true;
245 
246 	drm_mode_config_init(drm);
247 
248 	ret = drm_vblank_init(drm, MAX_CRTC);
249 	if (ret)
250 		goto err_kms;
251 
252 	dev_set_drvdata(dev, drm);
253 
254 	/* Now try and bind all our sub-components */
255 	ret = component_bind_all(dev, drm);
256 	if (ret)
257 		goto err_kms;
258 
259 	drm_mode_config_reset(drm);
260 
261 	/*
262 	 * All components are now initialised, so setup the fb helper.
263 	 * The fb helper takes copies of key hardware information, so the
264 	 * crtcs/connectors/encoders must not change after this point.
265 	 */
266 #if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION)
267 	if (legacyfb_depth != 16 && legacyfb_depth != 32) {
268 		dev_warn(dev, "Invalid legacyfb_depth.  Defaulting to 16bpp\n");
269 		legacyfb_depth = 16;
270 	}
271 	ret = drm_fb_cma_fbdev_init(drm, legacyfb_depth, MAX_CRTC);
272 	if (ret)
273 		goto err_unbind;
274 #endif
275 
276 	drm_kms_helper_poll_init(drm);
277 
278 	ret = drm_dev_register(drm, 0);
279 	if (ret)
280 		goto err_fbhelper;
281 
282 	return 0;
283 
284 err_fbhelper:
285 	drm_kms_helper_poll_fini(drm);
286 #if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION)
287 	drm_fb_cma_fbdev_fini(drm);
288 err_unbind:
289 #endif
290 	component_unbind_all(drm->dev, drm);
291 err_kms:
292 	drm_mode_config_cleanup(drm);
293 	drm_dev_put(drm);
294 
295 	return ret;
296 }
297 
imx_drm_unbind(struct device * dev)298 static void imx_drm_unbind(struct device *dev)
299 {
300 	struct drm_device *drm = dev_get_drvdata(dev);
301 
302 	drm_dev_unregister(drm);
303 
304 	drm_kms_helper_poll_fini(drm);
305 
306 	drm_fb_cma_fbdev_fini(drm);
307 
308 	drm_mode_config_cleanup(drm);
309 
310 	component_unbind_all(drm->dev, drm);
311 	dev_set_drvdata(dev, NULL);
312 
313 	drm_dev_put(drm);
314 }
315 
316 static const struct component_master_ops imx_drm_ops = {
317 	.bind = imx_drm_bind,
318 	.unbind = imx_drm_unbind,
319 };
320 
imx_drm_platform_probe(struct platform_device * pdev)321 static int imx_drm_platform_probe(struct platform_device *pdev)
322 {
323 	int ret = drm_of_component_probe(&pdev->dev, compare_of, &imx_drm_ops);
324 
325 	if (!ret)
326 		ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
327 
328 	return ret;
329 }
330 
imx_drm_platform_remove(struct platform_device * pdev)331 static int imx_drm_platform_remove(struct platform_device *pdev)
332 {
333 	component_master_del(&pdev->dev, &imx_drm_ops);
334 	return 0;
335 }
336 
337 #ifdef CONFIG_PM_SLEEP
imx_drm_suspend(struct device * dev)338 static int imx_drm_suspend(struct device *dev)
339 {
340 	struct drm_device *drm_dev = dev_get_drvdata(dev);
341 
342 	return drm_mode_config_helper_suspend(drm_dev);
343 }
344 
imx_drm_resume(struct device * dev)345 static int imx_drm_resume(struct device *dev)
346 {
347 	struct drm_device *drm_dev = dev_get_drvdata(dev);
348 
349 	return drm_mode_config_helper_resume(drm_dev);
350 }
351 #endif
352 
353 static SIMPLE_DEV_PM_OPS(imx_drm_pm_ops, imx_drm_suspend, imx_drm_resume);
354 
355 static const struct of_device_id imx_drm_dt_ids[] = {
356 	{ .compatible = "fsl,imx-display-subsystem", },
357 	{ /* sentinel */ },
358 };
359 MODULE_DEVICE_TABLE(of, imx_drm_dt_ids);
360 
361 static struct platform_driver imx_drm_pdrv = {
362 	.probe		= imx_drm_platform_probe,
363 	.remove		= imx_drm_platform_remove,
364 	.driver		= {
365 		.name	= "imx-drm",
366 		.pm	= &imx_drm_pm_ops,
367 		.of_match_table = imx_drm_dt_ids,
368 	},
369 };
370 
371 static struct platform_driver * const drivers[] = {
372 	&imx_drm_pdrv,
373 	&ipu_drm_driver,
374 };
375 
imx_drm_init(void)376 static int __init imx_drm_init(void)
377 {
378 	return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
379 }
380 module_init(imx_drm_init);
381 
imx_drm_exit(void)382 static void __exit imx_drm_exit(void)
383 {
384 	platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
385 }
386 module_exit(imx_drm_exit);
387 
388 MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
389 MODULE_DESCRIPTION("i.MX drm driver core");
390 MODULE_LICENSE("GPL");
391