• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 Noralf Trønnes
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  */
9 
10 #include <drm/drm_atomic.h>
11 #include <drm/drm_atomic_helper.h>
12 #include <drm/drm_crtc_helper.h>
13 #include <drm/tinydrm/tinydrm.h>
14 #include <linux/device.h>
15 #include <linux/dma-buf.h>
16 
17 /**
18  * DOC: overview
19  *
20  * This library provides driver helpers for very simple display hardware.
21  *
22  * It is based on &drm_simple_display_pipe coupled with a &drm_connector which
23  * has only one fixed &drm_display_mode. The framebuffers are backed by the
24  * cma helper and have support for framebuffer flushing (dirty).
25  * fbdev support is also included.
26  *
27  */
28 
29 /**
30  * DOC: core
31  *
32  * The driver allocates &tinydrm_device, initializes it using
33  * devm_tinydrm_init(), sets up the pipeline using tinydrm_display_pipe_init()
34  * and registers the DRM device using devm_tinydrm_register().
35  */
36 
37 /**
38  * tinydrm_lastclose - DRM lastclose helper
39  * @drm: DRM device
40  *
41  * This function ensures that fbdev is restored when drm_lastclose() is called
42  * on the last drm_release(). Drivers can use this as their
43  * &drm_driver->lastclose callback.
44  */
tinydrm_lastclose(struct drm_device * drm)45 void tinydrm_lastclose(struct drm_device *drm)
46 {
47 	struct tinydrm_device *tdev = drm->dev_private;
48 
49 	DRM_DEBUG_KMS("\n");
50 	drm_fbdev_cma_restore_mode(tdev->fbdev_cma);
51 }
52 EXPORT_SYMBOL(tinydrm_lastclose);
53 
54 /**
55  * tinydrm_gem_cma_prime_import_sg_table - Produce a CMA GEM object from
56  *     another driver's scatter/gather table of pinned pages
57  * @drm: DRM device to import into
58  * @attach: DMA-BUF attachment
59  * @sgt: Scatter/gather table of pinned pages
60  *
61  * This function imports a scatter/gather table exported via DMA-BUF by
62  * another driver using drm_gem_cma_prime_import_sg_table(). It sets the
63  * kernel virtual address on the CMA object. Drivers should use this as their
64  * &drm_driver->gem_prime_import_sg_table callback if they need the virtual
65  * address. tinydrm_gem_cma_free_object() should be used in combination with
66  * this function.
67  *
68  * Returns:
69  * A pointer to a newly created GEM object or an ERR_PTR-encoded negative
70  * error code on failure.
71  */
72 struct drm_gem_object *
tinydrm_gem_cma_prime_import_sg_table(struct drm_device * drm,struct dma_buf_attachment * attach,struct sg_table * sgt)73 tinydrm_gem_cma_prime_import_sg_table(struct drm_device *drm,
74 				      struct dma_buf_attachment *attach,
75 				      struct sg_table *sgt)
76 {
77 	struct drm_gem_cma_object *cma_obj;
78 	struct drm_gem_object *obj;
79 	void *vaddr;
80 
81 	vaddr = dma_buf_vmap(attach->dmabuf);
82 	if (!vaddr) {
83 		DRM_ERROR("Failed to vmap PRIME buffer\n");
84 		return ERR_PTR(-ENOMEM);
85 	}
86 
87 	obj = drm_gem_cma_prime_import_sg_table(drm, attach, sgt);
88 	if (IS_ERR(obj)) {
89 		dma_buf_vunmap(attach->dmabuf, vaddr);
90 		return obj;
91 	}
92 
93 	cma_obj = to_drm_gem_cma_obj(obj);
94 	cma_obj->vaddr = vaddr;
95 
96 	return obj;
97 }
98 EXPORT_SYMBOL(tinydrm_gem_cma_prime_import_sg_table);
99 
100 /**
101  * tinydrm_gem_cma_free_object - Free resources associated with a CMA GEM
102  *                               object
103  * @gem_obj: GEM object to free
104  *
105  * This function frees the backing memory of the CMA GEM object, cleans up the
106  * GEM object state and frees the memory used to store the object itself using
107  * drm_gem_cma_free_object(). It also handles PRIME buffers which has the kernel
108  * virtual address set by tinydrm_gem_cma_prime_import_sg_table(). Drivers
109  * can use this as their &drm_driver->gem_free_object callback.
110  */
tinydrm_gem_cma_free_object(struct drm_gem_object * gem_obj)111 void tinydrm_gem_cma_free_object(struct drm_gem_object *gem_obj)
112 {
113 	if (gem_obj->import_attach) {
114 		struct drm_gem_cma_object *cma_obj;
115 
116 		cma_obj = to_drm_gem_cma_obj(gem_obj);
117 		dma_buf_vunmap(gem_obj->import_attach->dmabuf, cma_obj->vaddr);
118 		cma_obj->vaddr = NULL;
119 	}
120 
121 	drm_gem_cma_free_object(gem_obj);
122 }
123 EXPORT_SYMBOL_GPL(tinydrm_gem_cma_free_object);
124 
125 static struct drm_framebuffer *
tinydrm_fb_create(struct drm_device * drm,struct drm_file * file_priv,const struct drm_mode_fb_cmd2 * mode_cmd)126 tinydrm_fb_create(struct drm_device *drm, struct drm_file *file_priv,
127 		  const struct drm_mode_fb_cmd2 *mode_cmd)
128 {
129 	struct tinydrm_device *tdev = drm->dev_private;
130 
131 	return drm_fb_cma_create_with_funcs(drm, file_priv, mode_cmd,
132 					    tdev->fb_funcs);
133 }
134 
135 static const struct drm_mode_config_funcs tinydrm_mode_config_funcs = {
136 	.fb_create = tinydrm_fb_create,
137 	.atomic_check = drm_atomic_helper_check,
138 	.atomic_commit = drm_atomic_helper_commit,
139 };
140 
tinydrm_init(struct device * parent,struct tinydrm_device * tdev,const struct drm_framebuffer_funcs * fb_funcs,struct drm_driver * driver)141 static int tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
142 			const struct drm_framebuffer_funcs *fb_funcs,
143 			struct drm_driver *driver)
144 {
145 	struct drm_device *drm;
146 
147 	mutex_init(&tdev->dirty_lock);
148 	tdev->fb_funcs = fb_funcs;
149 
150 	/*
151 	 * We don't embed drm_device, because that prevent us from using
152 	 * devm_kzalloc() to allocate tinydrm_device in the driver since
153 	 * drm_dev_unref() frees the structure. The devm_ functions provide
154 	 * for easy error handling.
155 	 */
156 	drm = drm_dev_alloc(driver, parent);
157 	if (IS_ERR(drm))
158 		return PTR_ERR(drm);
159 
160 	tdev->drm = drm;
161 	drm->dev_private = tdev;
162 	drm_mode_config_init(drm);
163 	drm->mode_config.funcs = &tinydrm_mode_config_funcs;
164 
165 	return 0;
166 }
167 
tinydrm_fini(struct tinydrm_device * tdev)168 static void tinydrm_fini(struct tinydrm_device *tdev)
169 {
170 	drm_mode_config_cleanup(tdev->drm);
171 	mutex_destroy(&tdev->dirty_lock);
172 	tdev->drm->dev_private = NULL;
173 	drm_dev_unref(tdev->drm);
174 }
175 
devm_tinydrm_release(void * data)176 static void devm_tinydrm_release(void *data)
177 {
178 	tinydrm_fini(data);
179 }
180 
181 /**
182  * devm_tinydrm_init - Initialize tinydrm device
183  * @parent: Parent device object
184  * @tdev: tinydrm device
185  * @fb_funcs: Framebuffer functions
186  * @driver: DRM driver
187  *
188  * This function initializes @tdev, the underlying DRM device and it's
189  * mode_config. Resources will be automatically freed on driver detach (devres)
190  * using drm_mode_config_cleanup() and drm_dev_unref().
191  *
192  * Returns:
193  * Zero on success, negative error code on failure.
194  */
devm_tinydrm_init(struct device * parent,struct tinydrm_device * tdev,const struct drm_framebuffer_funcs * fb_funcs,struct drm_driver * driver)195 int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
196 		      const struct drm_framebuffer_funcs *fb_funcs,
197 		      struct drm_driver *driver)
198 {
199 	int ret;
200 
201 	ret = tinydrm_init(parent, tdev, fb_funcs, driver);
202 	if (ret)
203 		return ret;
204 
205 	ret = devm_add_action(parent, devm_tinydrm_release, tdev);
206 	if (ret)
207 		tinydrm_fini(tdev);
208 
209 	return ret;
210 }
211 EXPORT_SYMBOL(devm_tinydrm_init);
212 
tinydrm_register(struct tinydrm_device * tdev)213 static int tinydrm_register(struct tinydrm_device *tdev)
214 {
215 	struct drm_device *drm = tdev->drm;
216 	int bpp = drm->mode_config.preferred_depth;
217 	struct drm_fbdev_cma *fbdev;
218 	int ret;
219 
220 	ret = drm_dev_register(tdev->drm, 0);
221 	if (ret)
222 		return ret;
223 
224 	fbdev = drm_fbdev_cma_init_with_funcs(drm, bpp ? bpp : 32,
225 					      drm->mode_config.num_connector,
226 					      tdev->fb_funcs);
227 	if (IS_ERR(fbdev))
228 		DRM_ERROR("Failed to initialize fbdev: %ld\n", PTR_ERR(fbdev));
229 	else
230 		tdev->fbdev_cma = fbdev;
231 
232 	return 0;
233 }
234 
tinydrm_unregister(struct tinydrm_device * tdev)235 static void tinydrm_unregister(struct tinydrm_device *tdev)
236 {
237 	struct drm_fbdev_cma *fbdev_cma = tdev->fbdev_cma;
238 
239 	drm_atomic_helper_shutdown(tdev->drm);
240 	/* don't restore fbdev in lastclose, keep pipeline disabled */
241 	tdev->fbdev_cma = NULL;
242 	drm_dev_unregister(tdev->drm);
243 	if (fbdev_cma)
244 		drm_fbdev_cma_fini(fbdev_cma);
245 }
246 
devm_tinydrm_register_release(void * data)247 static void devm_tinydrm_register_release(void *data)
248 {
249 	tinydrm_unregister(data);
250 }
251 
252 /**
253  * devm_tinydrm_register - Register tinydrm device
254  * @tdev: tinydrm device
255  *
256  * This function registers the underlying DRM device and fbdev.
257  * These resources will be automatically unregistered on driver detach (devres)
258  * and the display pipeline will be disabled.
259  *
260  * Returns:
261  * Zero on success, negative error code on failure.
262  */
devm_tinydrm_register(struct tinydrm_device * tdev)263 int devm_tinydrm_register(struct tinydrm_device *tdev)
264 {
265 	struct device *dev = tdev->drm->dev;
266 	int ret;
267 
268 	ret = tinydrm_register(tdev);
269 	if (ret)
270 		return ret;
271 
272 	ret = devm_add_action(dev, devm_tinydrm_register_release, tdev);
273 	if (ret)
274 		tinydrm_unregister(tdev);
275 
276 	return ret;
277 }
278 EXPORT_SYMBOL(devm_tinydrm_register);
279 
280 /**
281  * tinydrm_shutdown - Shutdown tinydrm
282  * @tdev: tinydrm device
283  *
284  * This function makes sure that the display pipeline is disabled.
285  * Used by drivers in their shutdown callback to turn off the display
286  * on machine shutdown and reboot.
287  */
tinydrm_shutdown(struct tinydrm_device * tdev)288 void tinydrm_shutdown(struct tinydrm_device *tdev)
289 {
290 	drm_atomic_helper_shutdown(tdev->drm);
291 }
292 EXPORT_SYMBOL(tinydrm_shutdown);
293 
294 /**
295  * tinydrm_suspend - Suspend tinydrm
296  * @tdev: tinydrm device
297  *
298  * Used in driver PM operations to suspend tinydrm.
299  * Suspends fbdev and DRM.
300  * Resume with tinydrm_resume().
301  *
302  * Returns:
303  * Zero on success, negative error code on failure.
304  */
tinydrm_suspend(struct tinydrm_device * tdev)305 int tinydrm_suspend(struct tinydrm_device *tdev)
306 {
307 	struct drm_atomic_state *state;
308 
309 	if (tdev->suspend_state) {
310 		DRM_ERROR("Failed to suspend: state already set\n");
311 		return -EINVAL;
312 	}
313 
314 	drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 1);
315 	state = drm_atomic_helper_suspend(tdev->drm);
316 	if (IS_ERR(state)) {
317 		drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 0);
318 		return PTR_ERR(state);
319 	}
320 
321 	tdev->suspend_state = state;
322 
323 	return 0;
324 }
325 EXPORT_SYMBOL(tinydrm_suspend);
326 
327 /**
328  * tinydrm_resume - Resume tinydrm
329  * @tdev: tinydrm device
330  *
331  * Used in driver PM operations to resume tinydrm.
332  * Suspend with tinydrm_suspend().
333  *
334  * Returns:
335  * Zero on success, negative error code on failure.
336  */
tinydrm_resume(struct tinydrm_device * tdev)337 int tinydrm_resume(struct tinydrm_device *tdev)
338 {
339 	struct drm_atomic_state *state = tdev->suspend_state;
340 	int ret;
341 
342 	if (!state) {
343 		DRM_ERROR("Failed to resume: state is not set\n");
344 		return -EINVAL;
345 	}
346 
347 	tdev->suspend_state = NULL;
348 
349 	ret = drm_atomic_helper_resume(tdev->drm, state);
350 	if (ret) {
351 		DRM_ERROR("Error resuming state: %d\n", ret);
352 		return ret;
353 	}
354 
355 	drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 0);
356 
357 	return 0;
358 }
359 EXPORT_SYMBOL(tinydrm_resume);
360 
361 MODULE_LICENSE("GPL");
362