2 * Copyright (C) 2016 Noralf Trønnes
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.
10 #include <drm/drm_atomic.h>
11 #include <drm/drm_atomic_helper.h>
12 #include <drm/drm_crtc_helper.h>
13 #include <drm/drm_gem_framebuffer_helper.h>
14 #include <drm/tinydrm/tinydrm.h>
15 #include <linux/device.h>
16 #include <linux/dma-buf.h>
21 * This library provides driver helpers for very simple display hardware.
23 * It is based on &drm_simple_display_pipe coupled with a &drm_connector which
24 * has only one fixed &drm_display_mode. The framebuffers are backed by the
25 * cma helper and have support for framebuffer flushing (dirty).
26 * fbdev support is also included.
33 * The driver allocates &tinydrm_device, initializes it using
34 * devm_tinydrm_init(), sets up the pipeline using tinydrm_display_pipe_init()
35 * and registers the DRM device using devm_tinydrm_register().
39 * tinydrm_lastclose - DRM lastclose helper
42 * This function ensures that fbdev is restored when drm_lastclose() is called
43 * on the last drm_release(). Drivers can use this as their
44 * &drm_driver->lastclose callback.
46 void tinydrm_lastclose(struct drm_device *drm)
48 struct tinydrm_device *tdev = drm->dev_private;
51 drm_fbdev_cma_restore_mode(tdev->fbdev_cma);
53 EXPORT_SYMBOL(tinydrm_lastclose);
56 * tinydrm_gem_cma_prime_import_sg_table - Produce a CMA GEM object from
57 * another driver's scatter/gather table of pinned pages
58 * @drm: DRM device to import into
59 * @attach: DMA-BUF attachment
60 * @sgt: Scatter/gather table of pinned pages
62 * This function imports a scatter/gather table exported via DMA-BUF by
63 * another driver using drm_gem_cma_prime_import_sg_table(). It sets the
64 * kernel virtual address on the CMA object. Drivers should use this as their
65 * &drm_driver->gem_prime_import_sg_table callback if they need the virtual
66 * address. tinydrm_gem_cma_free_object() should be used in combination with
70 * A pointer to a newly created GEM object or an ERR_PTR-encoded negative
71 * error code on failure.
73 struct drm_gem_object *
74 tinydrm_gem_cma_prime_import_sg_table(struct drm_device *drm,
75 struct dma_buf_attachment *attach,
78 struct drm_gem_cma_object *cma_obj;
79 struct drm_gem_object *obj;
82 vaddr = dma_buf_vmap(attach->dmabuf);
84 DRM_ERROR("Failed to vmap PRIME buffer\n");
85 return ERR_PTR(-ENOMEM);
88 obj = drm_gem_cma_prime_import_sg_table(drm, attach, sgt);
90 dma_buf_vunmap(attach->dmabuf, vaddr);
94 cma_obj = to_drm_gem_cma_obj(obj);
95 cma_obj->vaddr = vaddr;
99 EXPORT_SYMBOL(tinydrm_gem_cma_prime_import_sg_table);
102 * tinydrm_gem_cma_free_object - Free resources associated with a CMA GEM
104 * @gem_obj: GEM object to free
106 * This function frees the backing memory of the CMA GEM object, cleans up the
107 * GEM object state and frees the memory used to store the object itself using
108 * drm_gem_cma_free_object(). It also handles PRIME buffers which has the kernel
109 * virtual address set by tinydrm_gem_cma_prime_import_sg_table(). Drivers
110 * can use this as their &drm_driver->gem_free_object callback.
112 void tinydrm_gem_cma_free_object(struct drm_gem_object *gem_obj)
114 if (gem_obj->import_attach) {
115 struct drm_gem_cma_object *cma_obj;
117 cma_obj = to_drm_gem_cma_obj(gem_obj);
118 dma_buf_vunmap(gem_obj->import_attach->dmabuf, cma_obj->vaddr);
119 cma_obj->vaddr = NULL;
122 drm_gem_cma_free_object(gem_obj);
124 EXPORT_SYMBOL_GPL(tinydrm_gem_cma_free_object);
126 static struct drm_framebuffer *
127 tinydrm_fb_create(struct drm_device *drm, struct drm_file *file_priv,
128 const struct drm_mode_fb_cmd2 *mode_cmd)
130 struct tinydrm_device *tdev = drm->dev_private;
132 return drm_gem_fb_create_with_funcs(drm, file_priv, mode_cmd,
136 static const struct drm_mode_config_funcs tinydrm_mode_config_funcs = {
137 .fb_create = tinydrm_fb_create,
138 .atomic_check = drm_atomic_helper_check,
139 .atomic_commit = drm_atomic_helper_commit,
142 static int tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
143 const struct drm_framebuffer_funcs *fb_funcs,
144 struct drm_driver *driver)
146 struct drm_device *drm;
148 mutex_init(&tdev->dirty_lock);
149 tdev->fb_funcs = fb_funcs;
152 * We don't embed drm_device, because that prevent us from using
153 * devm_kzalloc() to allocate tinydrm_device in the driver since
154 * drm_dev_unref() frees the structure. The devm_ functions provide
155 * for easy error handling.
157 drm = drm_dev_alloc(driver, parent);
162 drm->dev_private = tdev;
163 drm_mode_config_init(drm);
164 drm->mode_config.funcs = &tinydrm_mode_config_funcs;
169 static void tinydrm_fini(struct tinydrm_device *tdev)
171 drm_mode_config_cleanup(tdev->drm);
172 mutex_destroy(&tdev->dirty_lock);
173 tdev->drm->dev_private = NULL;
174 drm_dev_unref(tdev->drm);
177 static void devm_tinydrm_release(void *data)
183 * devm_tinydrm_init - Initialize tinydrm device
184 * @parent: Parent device object
185 * @tdev: tinydrm device
186 * @fb_funcs: Framebuffer functions
187 * @driver: DRM driver
189 * This function initializes @tdev, the underlying DRM device and it's
190 * mode_config. Resources will be automatically freed on driver detach (devres)
191 * using drm_mode_config_cleanup() and drm_dev_unref().
194 * Zero on success, negative error code on failure.
196 int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
197 const struct drm_framebuffer_funcs *fb_funcs,
198 struct drm_driver *driver)
202 ret = tinydrm_init(parent, tdev, fb_funcs, driver);
206 ret = devm_add_action(parent, devm_tinydrm_release, tdev);
212 EXPORT_SYMBOL(devm_tinydrm_init);
214 static int tinydrm_register(struct tinydrm_device *tdev)
216 struct drm_device *drm = tdev->drm;
217 int bpp = drm->mode_config.preferred_depth;
218 struct drm_fbdev_cma *fbdev;
221 ret = drm_dev_register(tdev->drm, 0);
225 fbdev = drm_fbdev_cma_init_with_funcs(drm, bpp ? bpp : 32,
226 drm->mode_config.num_connector,
229 DRM_ERROR("Failed to initialize fbdev: %ld\n", PTR_ERR(fbdev));
231 tdev->fbdev_cma = fbdev;
236 static void tinydrm_unregister(struct tinydrm_device *tdev)
238 struct drm_fbdev_cma *fbdev_cma = tdev->fbdev_cma;
240 drm_atomic_helper_shutdown(tdev->drm);
241 /* don't restore fbdev in lastclose, keep pipeline disabled */
242 tdev->fbdev_cma = NULL;
243 drm_dev_unregister(tdev->drm);
245 drm_fbdev_cma_fini(fbdev_cma);
248 static void devm_tinydrm_register_release(void *data)
250 tinydrm_unregister(data);
254 * devm_tinydrm_register - Register tinydrm device
255 * @tdev: tinydrm device
257 * This function registers the underlying DRM device and fbdev.
258 * These resources will be automatically unregistered on driver detach (devres)
259 * and the display pipeline will be disabled.
262 * Zero on success, negative error code on failure.
264 int devm_tinydrm_register(struct tinydrm_device *tdev)
266 struct device *dev = tdev->drm->dev;
269 ret = tinydrm_register(tdev);
273 ret = devm_add_action(dev, devm_tinydrm_register_release, tdev);
275 tinydrm_unregister(tdev);
279 EXPORT_SYMBOL(devm_tinydrm_register);
282 * tinydrm_shutdown - Shutdown tinydrm
283 * @tdev: tinydrm device
285 * This function makes sure that the display pipeline is disabled.
286 * Used by drivers in their shutdown callback to turn off the display
287 * on machine shutdown and reboot.
289 void tinydrm_shutdown(struct tinydrm_device *tdev)
291 drm_atomic_helper_shutdown(tdev->drm);
293 EXPORT_SYMBOL(tinydrm_shutdown);
296 * tinydrm_suspend - Suspend tinydrm
297 * @tdev: tinydrm device
299 * Used in driver PM operations to suspend tinydrm.
300 * Suspends fbdev and DRM.
301 * Resume with tinydrm_resume().
304 * Zero on success, negative error code on failure.
306 int tinydrm_suspend(struct tinydrm_device *tdev)
308 struct drm_atomic_state *state;
310 if (tdev->suspend_state) {
311 DRM_ERROR("Failed to suspend: state already set\n");
315 drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 1);
316 state = drm_atomic_helper_suspend(tdev->drm);
318 drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 0);
319 return PTR_ERR(state);
322 tdev->suspend_state = state;
326 EXPORT_SYMBOL(tinydrm_suspend);
329 * tinydrm_resume - Resume tinydrm
330 * @tdev: tinydrm device
332 * Used in driver PM operations to resume tinydrm.
333 * Suspend with tinydrm_suspend().
336 * Zero on success, negative error code on failure.
338 int tinydrm_resume(struct tinydrm_device *tdev)
340 struct drm_atomic_state *state = tdev->suspend_state;
344 DRM_ERROR("Failed to resume: state is not set\n");
348 tdev->suspend_state = NULL;
350 ret = drm_atomic_helper_resume(tdev->drm, state);
352 DRM_ERROR("Error resuming state: %d\n", ret);
356 drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 0);
360 EXPORT_SYMBOL(tinydrm_resume);
362 MODULE_LICENSE("GPL");