]> Git Repo - linux.git/blob - drivers/gpu/drm/tinydrm/core/tinydrm-core.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc
[linux.git] / drivers / gpu / drm / tinydrm / core / tinydrm-core.c
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/drm_gem_framebuffer_helper.h>
14 #include <drm/tinydrm/tinydrm.h>
15 #include <linux/device.h>
16 #include <linux/dma-buf.h>
17
18 /**
19  * DOC: overview
20  *
21  * This library provides driver helpers for very simple display hardware.
22  *
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.
27  *
28  */
29
30 /**
31  * DOC: core
32  *
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().
36  */
37
38 /**
39  * tinydrm_lastclose - DRM lastclose helper
40  * @drm: DRM device
41  *
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.
45  */
46 void tinydrm_lastclose(struct drm_device *drm)
47 {
48         struct tinydrm_device *tdev = drm->dev_private;
49
50         DRM_DEBUG_KMS("\n");
51         drm_fbdev_cma_restore_mode(tdev->fbdev_cma);
52 }
53 EXPORT_SYMBOL(tinydrm_lastclose);
54
55 /**
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
61  *
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
67  * this function.
68  *
69  * Returns:
70  * A pointer to a newly created GEM object or an ERR_PTR-encoded negative
71  * error code on failure.
72  */
73 struct drm_gem_object *
74 tinydrm_gem_cma_prime_import_sg_table(struct drm_device *drm,
75                                       struct dma_buf_attachment *attach,
76                                       struct sg_table *sgt)
77 {
78         struct drm_gem_cma_object *cma_obj;
79         struct drm_gem_object *obj;
80         void *vaddr;
81
82         vaddr = dma_buf_vmap(attach->dmabuf);
83         if (!vaddr) {
84                 DRM_ERROR("Failed to vmap PRIME buffer\n");
85                 return ERR_PTR(-ENOMEM);
86         }
87
88         obj = drm_gem_cma_prime_import_sg_table(drm, attach, sgt);
89         if (IS_ERR(obj)) {
90                 dma_buf_vunmap(attach->dmabuf, vaddr);
91                 return obj;
92         }
93
94         cma_obj = to_drm_gem_cma_obj(obj);
95         cma_obj->vaddr = vaddr;
96
97         return obj;
98 }
99 EXPORT_SYMBOL(tinydrm_gem_cma_prime_import_sg_table);
100
101 /**
102  * tinydrm_gem_cma_free_object - Free resources associated with a CMA GEM
103  *                               object
104  * @gem_obj: GEM object to free
105  *
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.
111  */
112 void tinydrm_gem_cma_free_object(struct drm_gem_object *gem_obj)
113 {
114         if (gem_obj->import_attach) {
115                 struct drm_gem_cma_object *cma_obj;
116
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;
120         }
121
122         drm_gem_cma_free_object(gem_obj);
123 }
124 EXPORT_SYMBOL_GPL(tinydrm_gem_cma_free_object);
125
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)
129 {
130         struct tinydrm_device *tdev = drm->dev_private;
131
132         return drm_gem_fb_create_with_funcs(drm, file_priv, mode_cmd,
133                                             tdev->fb_funcs);
134 }
135
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,
140 };
141
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)
145 {
146         struct drm_device *drm;
147
148         mutex_init(&tdev->dirty_lock);
149         tdev->fb_funcs = fb_funcs;
150
151         /*
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.
156          */
157         drm = drm_dev_alloc(driver, parent);
158         if (IS_ERR(drm))
159                 return PTR_ERR(drm);
160
161         tdev->drm = drm;
162         drm->dev_private = tdev;
163         drm_mode_config_init(drm);
164         drm->mode_config.funcs = &tinydrm_mode_config_funcs;
165
166         return 0;
167 }
168
169 static void tinydrm_fini(struct tinydrm_device *tdev)
170 {
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);
175 }
176
177 static void devm_tinydrm_release(void *data)
178 {
179         tinydrm_fini(data);
180 }
181
182 /**
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
188  *
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().
192  *
193  * Returns:
194  * Zero on success, negative error code on failure.
195  */
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)
199 {
200         int ret;
201
202         ret = tinydrm_init(parent, tdev, fb_funcs, driver);
203         if (ret)
204                 return ret;
205
206         ret = devm_add_action(parent, devm_tinydrm_release, tdev);
207         if (ret)
208                 tinydrm_fini(tdev);
209
210         return ret;
211 }
212 EXPORT_SYMBOL(devm_tinydrm_init);
213
214 static int tinydrm_register(struct tinydrm_device *tdev)
215 {
216         struct drm_device *drm = tdev->drm;
217         int bpp = drm->mode_config.preferred_depth;
218         struct drm_fbdev_cma *fbdev;
219         int ret;
220
221         ret = drm_dev_register(tdev->drm, 0);
222         if (ret)
223                 return ret;
224
225         fbdev = drm_fbdev_cma_init_with_funcs(drm, bpp ? bpp : 32,
226                                               drm->mode_config.num_connector,
227                                               tdev->fb_funcs);
228         if (IS_ERR(fbdev))
229                 DRM_ERROR("Failed to initialize fbdev: %ld\n", PTR_ERR(fbdev));
230         else
231                 tdev->fbdev_cma = fbdev;
232
233         return 0;
234 }
235
236 static void tinydrm_unregister(struct tinydrm_device *tdev)
237 {
238         struct drm_fbdev_cma *fbdev_cma = tdev->fbdev_cma;
239
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);
244         if (fbdev_cma)
245                 drm_fbdev_cma_fini(fbdev_cma);
246 }
247
248 static void devm_tinydrm_register_release(void *data)
249 {
250         tinydrm_unregister(data);
251 }
252
253 /**
254  * devm_tinydrm_register - Register tinydrm device
255  * @tdev: tinydrm device
256  *
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.
260  *
261  * Returns:
262  * Zero on success, negative error code on failure.
263  */
264 int devm_tinydrm_register(struct tinydrm_device *tdev)
265 {
266         struct device *dev = tdev->drm->dev;
267         int ret;
268
269         ret = tinydrm_register(tdev);
270         if (ret)
271                 return ret;
272
273         ret = devm_add_action(dev, devm_tinydrm_register_release, tdev);
274         if (ret)
275                 tinydrm_unregister(tdev);
276
277         return ret;
278 }
279 EXPORT_SYMBOL(devm_tinydrm_register);
280
281 /**
282  * tinydrm_shutdown - Shutdown tinydrm
283  * @tdev: tinydrm device
284  *
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.
288  */
289 void tinydrm_shutdown(struct tinydrm_device *tdev)
290 {
291         drm_atomic_helper_shutdown(tdev->drm);
292 }
293 EXPORT_SYMBOL(tinydrm_shutdown);
294
295 /**
296  * tinydrm_suspend - Suspend tinydrm
297  * @tdev: tinydrm device
298  *
299  * Used in driver PM operations to suspend tinydrm.
300  * Suspends fbdev and DRM.
301  * Resume with tinydrm_resume().
302  *
303  * Returns:
304  * Zero on success, negative error code on failure.
305  */
306 int tinydrm_suspend(struct tinydrm_device *tdev)
307 {
308         struct drm_atomic_state *state;
309
310         if (tdev->suspend_state) {
311                 DRM_ERROR("Failed to suspend: state already set\n");
312                 return -EINVAL;
313         }
314
315         drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 1);
316         state = drm_atomic_helper_suspend(tdev->drm);
317         if (IS_ERR(state)) {
318                 drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 0);
319                 return PTR_ERR(state);
320         }
321
322         tdev->suspend_state = state;
323
324         return 0;
325 }
326 EXPORT_SYMBOL(tinydrm_suspend);
327
328 /**
329  * tinydrm_resume - Resume tinydrm
330  * @tdev: tinydrm device
331  *
332  * Used in driver PM operations to resume tinydrm.
333  * Suspend with tinydrm_suspend().
334  *
335  * Returns:
336  * Zero on success, negative error code on failure.
337  */
338 int tinydrm_resume(struct tinydrm_device *tdev)
339 {
340         struct drm_atomic_state *state = tdev->suspend_state;
341         int ret;
342
343         if (!state) {
344                 DRM_ERROR("Failed to resume: state is not set\n");
345                 return -EINVAL;
346         }
347
348         tdev->suspend_state = NULL;
349
350         ret = drm_atomic_helper_resume(tdev->drm, state);
351         if (ret) {
352                 DRM_ERROR("Error resuming state: %d\n", ret);
353                 return ret;
354         }
355
356         drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 0);
357
358         return 0;
359 }
360 EXPORT_SYMBOL(tinydrm_resume);
361
362 MODULE_LICENSE("GPL");
This page took 0.052719 seconds and 4 git commands to generate.