xref: /linux/drivers/gpu/drm/drm_dumb_buffers.c (revision 570f7e331f5febb30f1384817463c7e42b65ca7d)
1 /*
2  * Copyright (c) 2006-2008 Intel Corporation
3  * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
4  * Copyright (c) 2008 Red Hat Inc.
5  * Copyright (c) 2016 Intel Corporation
6  *
7  * Permission to use, copy, modify, distribute, and sell this software and its
8  * documentation for any purpose is hereby granted without fee, provided that
9  * the above copyright notice appear in all copies and that both that copyright
10  * notice and this permission notice appear in supporting documentation, and
11  * that the name of the copyright holders not be used in advertising or
12  * publicity pertaining to distribution of the software without specific,
13  * written prior permission.  The copyright holders make no representations
14  * about the suitability of this software for any purpose.  It is provided "as
15  * is" without express or implied warranty.
16  *
17  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
18  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
19  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
20  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
22  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
23  * OF THIS SOFTWARE.
24  */
25 
26 #include <drm/drm_device.h>
27 #include <drm/drm_drv.h>
28 #include <drm/drm_dumb_buffers.h>
29 #include <drm/drm_fourcc.h>
30 #include <drm/drm_gem.h>
31 #include <drm/drm_mode.h>
32 #include <drm/drm_print.h>
33 
34 #include "drm_crtc_internal.h"
35 
36 /**
37  * DOC: overview
38  *
39  * The KMS API doesn't standardize backing storage object creation and leaves it
40  * to driver-specific ioctls. Furthermore actually creating a buffer object even
41  * for GEM-based drivers is done through a driver-specific ioctl - GEM only has
42  * a common userspace interface for sharing and destroying objects. While not an
43  * issue for full-fledged graphics stacks that include device-specific userspace
44  * components (in libdrm for instance), this limit makes DRM-based early boot
45  * graphics unnecessarily complex.
46  *
47  * Dumb objects partly alleviate the problem by providing a standard API to
48  * create dumb buffers suitable for scanout, which can then be used to create
49  * KMS frame buffers.
50  *
51  * To support dumb objects drivers must implement the &drm_driver.dumb_create
52  * and &drm_driver.dumb_map_offset operations (the latter defaults to
53  * drm_gem_dumb_map_offset() if not set). Drivers that don't use GEM handles
54  * additionally need to implement the &drm_driver.dumb_destroy operation. See
55  * the callbacks for further details.
56  *
57  * Note that dumb objects may not be used for gpu acceleration, as has been
58  * attempted on some ARM embedded platforms. Such drivers really must have
59  * a hardware-specific ioctl to allocate suitable buffer objects.
60  */
61 
62 static int drm_mode_align_dumb(struct drm_mode_create_dumb *args,
63 			       unsigned long hw_pitch_align,
64 			       unsigned long hw_size_align)
65 {
66 	u32 pitch = args->pitch;
67 	u32 size;
68 
69 	if (!pitch)
70 		return -EINVAL;
71 
72 	if (hw_pitch_align) {
73 		pitch = roundup(pitch, hw_pitch_align);
74 		if (pitch < hw_pitch_align)
75 			return -EINVAL;
76 	}
77 
78 	if (!hw_size_align)
79 		hw_size_align = PAGE_SIZE;
80 	else if (!IS_ALIGNED(hw_size_align, PAGE_SIZE))
81 		return -EINVAL; /* TODO: handle this if necessary */
82 
83 	if (check_mul_overflow(args->height, pitch, &size))
84 		return -EINVAL;
85 	size = roundup(size, hw_size_align);
86 	if (!size)
87 		return -EINVAL;
88 
89 	args->pitch = pitch;
90 	args->size = size;
91 
92 	return 0;
93 }
94 
95 /**
96  * drm_mode_size_dumb - Calculates the scanline and buffer sizes for dumb buffers
97  * @dev: DRM device
98  * @args: Parameters for the dumb buffer
99  * @hw_pitch_align: Hardware scanline alignment in bytes
100  * @hw_size_align: Hardware buffer-size alignment in bytes
101  *
102  * The helper drm_mode_size_dumb() calculates the size of the buffer
103  * allocation and the scanline size for a dumb buffer. Callers have to
104  * set the buffers width, height and color mode in the argument @arg.
105  * The helper validates the correctness of the input and tests for
106  * possible overflows. If successful, it returns the dumb buffer's
107  * required scanline pitch and size in &args.
108  *
109  * The parameter @hw_pitch_align allows the driver to specifies an
110  * alignment for the scanline pitch, if the hardware requires any. The
111  * calculated pitch will be a multiple of the alignment. The parameter
112  * @hw_size_align allows to specify an alignment for buffer sizes. The
113  * provided alignment should represent requirements of the graphics
114  * hardware. drm_mode_size_dumb() handles GEM-related constraints
115  * automatically across all drivers and hardware. For example, the
116  * returned buffer size is always a multiple of PAGE_SIZE, which is
117  * required by mmap().
118  *
119  * Returns:
120  * Zero on success, or a negative error code otherwise.
121  */
122 int drm_mode_size_dumb(struct drm_device *dev,
123 		       struct drm_mode_create_dumb *args,
124 		       unsigned long hw_pitch_align,
125 		       unsigned long hw_size_align)
126 {
127 	u64 pitch = 0;
128 	u32 fourcc;
129 
130 	/*
131 	 * The scanline pitch depends on the buffer width and the color
132 	 * format. The latter is specified as a color-mode constant for
133 	 * which we first have to find the corresponding color format.
134 	 *
135 	 * Different color formats can have the same color-mode constant.
136 	 * For example XRGB8888 and BGRX8888 both have a color mode of 32.
137 	 * It is possible to use different formats for dumb-buffer allocation
138 	 * and rendering as long as all involved formats share the same
139 	 * color-mode constant.
140 	 */
141 	fourcc = drm_driver_color_mode_format(dev, args->bpp);
142 	if (fourcc != DRM_FORMAT_INVALID) {
143 		const struct drm_format_info *info = drm_format_info(fourcc);
144 
145 		if (!info)
146 			return -EINVAL;
147 		pitch = drm_format_info_min_pitch(info, 0, args->width);
148 	} else if (args->bpp) {
149 		/*
150 		 * Some userspace throws in arbitrary values for bpp and
151 		 * relies on the kernel to figure it out. In this case we
152 		 * fall back to the old method of using bpp directly. The
153 		 * over-commitment of memory from the rounding is acceptable
154 		 * for compatibility with legacy userspace. We have a number
155 		 * of deprecated legacy values that are explicitly supported.
156 		 */
157 		switch (args->bpp) {
158 		default:
159 			drm_warn_once(dev,
160 				      "Unknown color mode %u; guessing buffer size.\n",
161 				      args->bpp);
162 			fallthrough;
163 		/*
164 		 * These constants represent various YUV formats supported by
165 		 * drm_gem_afbc_get_bpp().
166 		 */
167 		case 12: // DRM_FORMAT_YUV420_8BIT
168 		case 15: // DRM_FORMAT_YUV420_10BIT
169 		case 30: // DRM_FORMAT_VUY101010
170 			fallthrough;
171 		/*
172 		 * Used by Mesa and Gstreamer to allocate NV formats and others
173 		 * as RGB buffers. Technically, XRGB16161616F formats are RGB,
174 		 * but the dumb buffers are not supposed to be used for anything
175 		 * beyond 32 bits per pixels.
176 		 */
177 		case 10: // DRM_FORMAT_NV{15,20,30}, DRM_FORMAT_P010
178 		case 64: // DRM_FORMAT_{XRGB,XBGR,ARGB,ABGR}16161616F
179 			pitch = args->width * DIV_ROUND_UP(args->bpp, SZ_8);
180 			break;
181 		}
182 	}
183 
184 	if (!pitch || pitch > U32_MAX)
185 		return -EINVAL;
186 
187 	args->pitch = pitch;
188 
189 	return drm_mode_align_dumb(args, hw_pitch_align, hw_size_align);
190 }
191 EXPORT_SYMBOL(drm_mode_size_dumb);
192 
193 int drm_mode_create_dumb(struct drm_device *dev,
194 			 struct drm_mode_create_dumb *args,
195 			 struct drm_file *file_priv)
196 {
197 	u32 cpp, stride, size;
198 
199 	if (!dev->driver->dumb_create)
200 		return -ENOSYS;
201 	if (!args->width || !args->height || !args->bpp)
202 		return -EINVAL;
203 
204 	/* overflow checks for 32bit size calculations */
205 	if (args->bpp > U32_MAX - 8)
206 		return -EINVAL;
207 	cpp = DIV_ROUND_UP(args->bpp, 8);
208 	if (cpp > U32_MAX / args->width)
209 		return -EINVAL;
210 	stride = cpp * args->width;
211 	if (args->height > U32_MAX / stride)
212 		return -EINVAL;
213 
214 	/* test for wrap-around */
215 	size = args->height * stride;
216 	if (PAGE_ALIGN(size) == 0)
217 		return -EINVAL;
218 
219 	/*
220 	 * handle, pitch and size are output parameters. Zero them out to
221 	 * prevent drivers from accidentally using uninitialized data. Since
222 	 * not all existing userspace is clearing these fields properly we
223 	 * cannot reject IOCTL with garbage in them.
224 	 */
225 	args->handle = 0;
226 	args->pitch = 0;
227 	args->size = 0;
228 
229 	return dev->driver->dumb_create(file_priv, dev, args);
230 }
231 
232 int drm_mode_create_dumb_ioctl(struct drm_device *dev,
233 			       void *data, struct drm_file *file_priv)
234 {
235 	struct drm_mode_create_dumb *args = data;
236 	int err;
237 
238 	err = drm_mode_create_dumb(dev, args, file_priv);
239 	if (err) {
240 		args->handle = 0;
241 		args->pitch = 0;
242 		args->size = 0;
243 	}
244 	return err;
245 }
246 
247 static int drm_mode_mmap_dumb(struct drm_device *dev, struct drm_mode_map_dumb *args,
248 			      struct drm_file *file_priv)
249 {
250 	if (!dev->driver->dumb_create)
251 		return -ENOSYS;
252 
253 	if (dev->driver->dumb_map_offset)
254 		return dev->driver->dumb_map_offset(file_priv, dev, args->handle,
255 						    &args->offset);
256 	else
257 		return drm_gem_dumb_map_offset(file_priv, dev, args->handle,
258 					       &args->offset);
259 }
260 
261 /**
262  * drm_mode_mmap_dumb_ioctl - create an mmap offset for a dumb backing storage buffer
263  * @dev: DRM device
264  * @data: ioctl data
265  * @file_priv: DRM file info
266  *
267  * Allocate an offset in the drm device node's address space to be able to
268  * memory map a dumb buffer.
269  *
270  * Called by the user via ioctl.
271  *
272  * Returns:
273  * Zero on success, negative errno on failure.
274  */
275 int drm_mode_mmap_dumb_ioctl(struct drm_device *dev,
276 			     void *data, struct drm_file *file_priv)
277 {
278 	struct drm_mode_map_dumb *args = data;
279 	int err;
280 
281 	err = drm_mode_mmap_dumb(dev, args, file_priv);
282 	if (err)
283 		args->offset = 0;
284 	return err;
285 }
286 
287 int drm_mode_destroy_dumb(struct drm_device *dev, u32 handle,
288 			  struct drm_file *file_priv)
289 {
290 	if (!dev->driver->dumb_create)
291 		return -ENOSYS;
292 
293 	return drm_gem_handle_delete(file_priv, handle);
294 }
295 
296 int drm_mode_destroy_dumb_ioctl(struct drm_device *dev,
297 				void *data, struct drm_file *file_priv)
298 {
299 	struct drm_mode_destroy_dumb *args = data;
300 
301 	return drm_mode_destroy_dumb(dev, args->handle, file_priv);
302 }
303