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_fb_helper.h>
14 #include <drm/drm_gem_framebuffer_helper.h>
15 #include <drm/tinydrm/tinydrm.h>
16 #include <linux/device.h>
17 #include <linux/dma-buf.h>
18 
19 /**
20  * DOC: overview
21  *
22  * This library provides driver helpers for very simple display hardware.
23  *
24  * It is based on &drm_simple_display_pipe coupled with a &drm_connector which
25  * has only one fixed &drm_display_mode. The framebuffers are backed by the
26  * cma helper and have support for framebuffer flushing (dirty).
27  * fbdev support is also included.
28  *
29  */
30 
31 /**
32  * DOC: core
33  *
34  * The driver allocates &tinydrm_device, initializes it using
35  * devm_tinydrm_init(), sets up the pipeline using tinydrm_display_pipe_init()
36  * and registers the DRM device using devm_tinydrm_register().
37  */
38 
39 /**
40  * tinydrm_gem_cma_prime_import_sg_table - Produce a CMA GEM object from
41  *     another driver's scatter/gather table of pinned pages
42  * @drm: DRM device to import into
43  * @attach: DMA-BUF attachment
44  * @sgt: Scatter/gather table of pinned pages
45  *
46  * This function imports a scatter/gather table exported via DMA-BUF by
47  * another driver using drm_gem_cma_prime_import_sg_table(). It sets the
48  * kernel virtual address on the CMA object. Drivers should use this as their
49  * &drm_driver->gem_prime_import_sg_table callback if they need the virtual
50  * address. tinydrm_gem_cma_free_object() should be used in combination with
51  * this function.
52  *
53  * Returns:
54  * A pointer to a newly created GEM object or an ERR_PTR-encoded negative
55  * error code on failure.
56  */
57 struct drm_gem_object *
tinydrm_gem_cma_prime_import_sg_table(struct drm_device * drm,struct dma_buf_attachment * attach,struct sg_table * sgt)58 tinydrm_gem_cma_prime_import_sg_table(struct drm_device *drm,
59 				      struct dma_buf_attachment *attach,
60 				      struct sg_table *sgt)
61 {
62 	struct drm_gem_cma_object *cma_obj;
63 	struct drm_gem_object *obj;
64 	void *vaddr;
65 
66 	vaddr = dma_buf_vmap(attach->dmabuf);
67 	if (!vaddr) {
68 		DRM_ERROR("Failed to vmap PRIME buffer\n");
69 		return ERR_PTR(-ENOMEM);
70 	}
71 
72 	obj = drm_gem_cma_prime_import_sg_table(drm, attach, sgt);
73 	if (IS_ERR(obj)) {
74 		dma_buf_vunmap(attach->dmabuf, vaddr);
75 		return obj;
76 	}
77 
78 	cma_obj = to_drm_gem_cma_obj(obj);
79 	cma_obj->vaddr = vaddr;
80 
81 	return obj;
82 }
83 EXPORT_SYMBOL(tinydrm_gem_cma_prime_import_sg_table);
84 
85 /**
86  * tinydrm_gem_cma_free_object - Free resources associated with a CMA GEM
87  *                               object
88  * @gem_obj: GEM object to free
89  *
90  * This function frees the backing memory of the CMA GEM object, cleans up the
91  * GEM object state and frees the memory used to store the object itself using
92  * drm_gem_cma_free_object(). It also handles PRIME buffers which has the kernel
93  * virtual address set by tinydrm_gem_cma_prime_import_sg_table(). Drivers
94  * can use this as their &drm_driver->gem_free_object_unlocked callback.
95  */
tinydrm_gem_cma_free_object(struct drm_gem_object * gem_obj)96 void tinydrm_gem_cma_free_object(struct drm_gem_object *gem_obj)
97 {
98 	if (gem_obj->import_attach) {
99 		struct drm_gem_cma_object *cma_obj;
100 
101 		cma_obj = to_drm_gem_cma_obj(gem_obj);
102 		dma_buf_vunmap(gem_obj->import_attach->dmabuf, cma_obj->vaddr);
103 		cma_obj->vaddr = NULL;
104 	}
105 
106 	drm_gem_cma_free_object(gem_obj);
107 }
108 EXPORT_SYMBOL_GPL(tinydrm_gem_cma_free_object);
109 
110 static struct drm_framebuffer *
tinydrm_fb_create(struct drm_device * drm,struct drm_file * file_priv,const struct drm_mode_fb_cmd2 * mode_cmd)111 tinydrm_fb_create(struct drm_device *drm, struct drm_file *file_priv,
112 		  const struct drm_mode_fb_cmd2 *mode_cmd)
113 {
114 	struct tinydrm_device *tdev = drm->dev_private;
115 
116 	return drm_gem_fb_create_with_funcs(drm, file_priv, mode_cmd,
117 					    tdev->fb_funcs);
118 }
119 
120 static const struct drm_mode_config_funcs tinydrm_mode_config_funcs = {
121 	.fb_create = tinydrm_fb_create,
122 	.atomic_check = drm_atomic_helper_check,
123 	.atomic_commit = drm_atomic_helper_commit,
124 };
125 
tinydrm_init(struct device * parent,struct tinydrm_device * tdev,const struct drm_framebuffer_funcs * fb_funcs,struct drm_driver * driver)126 static int tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
127 			const struct drm_framebuffer_funcs *fb_funcs,
128 			struct drm_driver *driver)
129 {
130 	struct drm_device *drm;
131 
132 	mutex_init(&tdev->dirty_lock);
133 	tdev->fb_funcs = fb_funcs;
134 
135 	/*
136 	 * We don't embed drm_device, because that prevent us from using
137 	 * devm_kzalloc() to allocate tinydrm_device in the driver since
138 	 * drm_dev_unref() frees the structure. The devm_ functions provide
139 	 * for easy error handling.
140 	 */
141 	drm = drm_dev_alloc(driver, parent);
142 	if (IS_ERR(drm))
143 		return PTR_ERR(drm);
144 
145 	tdev->drm = drm;
146 	drm->dev_private = tdev;
147 	drm_mode_config_init(drm);
148 	drm->mode_config.funcs = &tinydrm_mode_config_funcs;
149 
150 	return 0;
151 }
152 
tinydrm_fini(struct tinydrm_device * tdev)153 static void tinydrm_fini(struct tinydrm_device *tdev)
154 {
155 	drm_mode_config_cleanup(tdev->drm);
156 	mutex_destroy(&tdev->dirty_lock);
157 	tdev->drm->dev_private = NULL;
158 	drm_dev_unref(tdev->drm);
159 }
160 
devm_tinydrm_release(void * data)161 static void devm_tinydrm_release(void *data)
162 {
163 	tinydrm_fini(data);
164 }
165 
166 /**
167  * devm_tinydrm_init - Initialize tinydrm device
168  * @parent: Parent device object
169  * @tdev: tinydrm device
170  * @fb_funcs: Framebuffer functions
171  * @driver: DRM driver
172  *
173  * This function initializes @tdev, the underlying DRM device and it's
174  * mode_config. Resources will be automatically freed on driver detach (devres)
175  * using drm_mode_config_cleanup() and drm_dev_unref().
176  *
177  * Returns:
178  * Zero on success, negative error code on failure.
179  */
devm_tinydrm_init(struct device * parent,struct tinydrm_device * tdev,const struct drm_framebuffer_funcs * fb_funcs,struct drm_driver * driver)180 int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
181 		      const struct drm_framebuffer_funcs *fb_funcs,
182 		      struct drm_driver *driver)
183 {
184 	int ret;
185 
186 	ret = tinydrm_init(parent, tdev, fb_funcs, driver);
187 	if (ret)
188 		return ret;
189 
190 	ret = devm_add_action(parent, devm_tinydrm_release, tdev);
191 	if (ret)
192 		tinydrm_fini(tdev);
193 
194 	return ret;
195 }
196 EXPORT_SYMBOL(devm_tinydrm_init);
197 
tinydrm_register(struct tinydrm_device * tdev)198 static int tinydrm_register(struct tinydrm_device *tdev)
199 {
200 	struct drm_device *drm = tdev->drm;
201 	int ret;
202 
203 	ret = drm_dev_register(tdev->drm, 0);
204 	if (ret)
205 		return ret;
206 
207 	ret = drm_fbdev_generic_setup(drm, 0);
208 	if (ret)
209 		DRM_ERROR("Failed to initialize fbdev: %d\n", ret);
210 
211 	return 0;
212 }
213 
tinydrm_unregister(struct tinydrm_device * tdev)214 static void tinydrm_unregister(struct tinydrm_device *tdev)
215 {
216 	drm_atomic_helper_shutdown(tdev->drm);
217 	drm_dev_unregister(tdev->drm);
218 }
219 
devm_tinydrm_register_release(void * data)220 static void devm_tinydrm_register_release(void *data)
221 {
222 	tinydrm_unregister(data);
223 }
224 
225 /**
226  * devm_tinydrm_register - Register tinydrm device
227  * @tdev: tinydrm device
228  *
229  * This function registers the underlying DRM device and fbdev.
230  * These resources will be automatically unregistered on driver detach (devres)
231  * and the display pipeline will be disabled.
232  *
233  * Returns:
234  * Zero on success, negative error code on failure.
235  */
devm_tinydrm_register(struct tinydrm_device * tdev)236 int devm_tinydrm_register(struct tinydrm_device *tdev)
237 {
238 	struct device *dev = tdev->drm->dev;
239 	int ret;
240 
241 	ret = tinydrm_register(tdev);
242 	if (ret)
243 		return ret;
244 
245 	ret = devm_add_action(dev, devm_tinydrm_register_release, tdev);
246 	if (ret)
247 		tinydrm_unregister(tdev);
248 
249 	return ret;
250 }
251 EXPORT_SYMBOL(devm_tinydrm_register);
252 
253 /**
254  * tinydrm_shutdown - Shutdown tinydrm
255  * @tdev: tinydrm device
256  *
257  * This function makes sure that the display pipeline is disabled.
258  * Used by drivers in their shutdown callback to turn off the display
259  * on machine shutdown and reboot.
260  */
tinydrm_shutdown(struct tinydrm_device * tdev)261 void tinydrm_shutdown(struct tinydrm_device *tdev)
262 {
263 	drm_atomic_helper_shutdown(tdev->drm);
264 }
265 EXPORT_SYMBOL(tinydrm_shutdown);
266 
267 MODULE_LICENSE("GPL");
268