xref: /linux/drivers/gpu/drm/drm_dumb_buffers.c (revision 52e6b198833411564e0b9ce6e96bbd3d72f961e7)
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 
33 #include "drm_crtc_internal.h"
34 #include "drm_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 
75 	if (!hw_size_align)
76 		hw_size_align = PAGE_SIZE;
77 	else if (!IS_ALIGNED(hw_size_align, PAGE_SIZE))
78 		return -EINVAL; /* TODO: handle this if necessary */
79 
80 	if (check_mul_overflow(args->height, pitch, &size))
81 		return -EINVAL;
82 	size = ALIGN(size, hw_size_align);
83 	if (!size)
84 		return -EINVAL;
85 
86 	args->pitch = pitch;
87 	args->size = size;
88 
89 	return 0;
90 }
91 
92 /**
93  * drm_mode_size_dumb - Calculates the scanline and buffer sizes for dumb buffers
94  * @dev: DRM device
95  * @args: Parameters for the dumb buffer
96  * @hw_pitch_align: Hardware scanline alignment in bytes
97  * @hw_size_align: Hardware buffer-size alignment in bytes
98  *
99  * The helper drm_mode_size_dumb() calculates the size of the buffer
100  * allocation and the scanline size for a dumb buffer. Callers have to
101  * set the buffers width, height and color mode in the argument @arg.
102  * The helper validates the correctness of the input and tests for
103  * possible overflows. If successful, it returns the dumb buffer's
104  * required scanline pitch and size in &args.
105  *
106  * The parameter @hw_pitch_align allows the driver to specifies an
107  * alignment for the scanline pitch, if the hardware requires any. The
108  * calculated pitch will be a multiple of the alignment. The parameter
109  * @hw_size_align allows to specify an alignment for buffer sizes. The
110  * provided alignment should represent requirements of the graphics
111  * hardware. drm_mode_size_dumb() handles GEM-related constraints
112  * automatically across all drivers and hardware. For example, the
113  * returned buffer size is always a multiple of PAGE_SIZE, which is
114  * required by mmap().
115  *
116  * Returns:
117  * Zero on success, or a negative error code otherwise.
118  */
119 int drm_mode_size_dumb(struct drm_device *dev,
120 		       struct drm_mode_create_dumb *args,
121 		       unsigned long hw_pitch_align,
122 		       unsigned long hw_size_align)
123 {
124 	u64 pitch = 0;
125 	u32 fourcc;
126 
127 	/*
128 	 * The scanline pitch depends on the buffer width and the color
129 	 * format. The latter is specified as a color-mode constant for
130 	 * which we first have to find the corresponding color format.
131 	 *
132 	 * Different color formats can have the same color-mode constant.
133 	 * For example XRGB8888 and BGRX8888 both have a color mode of 32.
134 	 * It is possible to use different formats for dumb-buffer allocation
135 	 * and rendering as long as all involved formats share the same
136 	 * color-mode constant.
137 	 */
138 	fourcc = drm_driver_color_mode_format(dev, args->bpp);
139 	if (fourcc != DRM_FORMAT_INVALID) {
140 		const struct drm_format_info *info = drm_format_info(fourcc);
141 
142 		if (!info)
143 			return -EINVAL;
144 		pitch = drm_format_info_min_pitch(info, 0, args->width);
145 	} else if (args->bpp) {
146 		/*
147 		 * Some userspace throws in arbitrary values for bpp and
148 		 * relies on the kernel to figure it out. In this case we
149 		 * fall back to the old method of using bpp directly. The
150 		 * over-commitment of memory from the rounding is acceptable
151 		 * for compatibility with legacy userspace. We have a number
152 		 * of deprecated legacy values that are explicitly supported.
153 		 */
154 		switch (args->bpp) {
155 		default:
156 			drm_warn_once(dev,
157 				      "Unknown color mode %u; guessing buffer size.\n",
158 				      args->bpp);
159 			fallthrough;
160 		/*
161 		 * These constants represent various YUV formats supported by
162 		 * drm_gem_afbc_get_bpp().
163 		 */
164 		case 12: // DRM_FORMAT_YUV420_8BIT
165 		case 15: // DRM_FORMAT_YUV420_10BIT
166 		case 30: // DRM_FORMAT_VUY101010
167 			fallthrough;
168 		/*
169 		 * Used by Mesa and Gstreamer to allocate NV formats and others
170 		 * as RGB buffers. Technically, XRGB16161616F formats are RGB,
171 		 * but the dumb buffers are not supposed to be used for anything
172 		 * beyond 32 bits per pixels.
173 		 */
174 		case 10: // DRM_FORMAT_NV{15,20,30}, DRM_FORMAT_P010
175 		case 64: // DRM_FORMAT_{XRGB,XBGR,ARGB,ABGR}16161616F
176 			pitch = args->width * DIV_ROUND_UP(args->bpp, SZ_8);
177 			break;
178 		}
179 	}
180 
181 	if (!pitch || pitch > U32_MAX)
182 		return -EINVAL;
183 
184 	args->pitch = pitch;
185 
186 	return drm_mode_align_dumb(args, hw_pitch_align, hw_size_align);
187 }
188 EXPORT_SYMBOL(drm_mode_size_dumb);
189 
190 int drm_mode_create_dumb(struct drm_device *dev,
191 			 struct drm_mode_create_dumb *args,
192 			 struct drm_file *file_priv)
193 {
194 	u32 cpp, stride, size;
195 
196 	if (!dev->driver->dumb_create)
197 		return -ENOSYS;
198 	if (!args->width || !args->height || !args->bpp)
199 		return -EINVAL;
200 
201 	/* overflow checks for 32bit size calculations */
202 	if (args->bpp > U32_MAX - 8)
203 		return -EINVAL;
204 	cpp = DIV_ROUND_UP(args->bpp, 8);
205 	if (cpp > U32_MAX / args->width)
206 		return -EINVAL;
207 	stride = cpp * args->width;
208 	if (args->height > U32_MAX / stride)
209 		return -EINVAL;
210 
211 	/* test for wrap-around */
212 	size = args->height * stride;
213 	if (PAGE_ALIGN(size) == 0)
214 		return -EINVAL;
215 
216 	/*
217 	 * handle, pitch and size are output parameters. Zero them out to
218 	 * prevent drivers from accidentally using uninitialized data. Since
219 	 * not all existing userspace is clearing these fields properly we
220 	 * cannot reject IOCTL with garbage in them.
221 	 */
222 	args->handle = 0;
223 	args->pitch = 0;
224 	args->size = 0;
225 
226 	return dev->driver->dumb_create(file_priv, dev, args);
227 }
228 
229 int drm_mode_create_dumb_ioctl(struct drm_device *dev,
230 			       void *data, struct drm_file *file_priv)
231 {
232 	struct drm_mode_create_dumb *args = data;
233 	int err;
234 
235 	err = drm_mode_create_dumb(dev, args, file_priv);
236 	if (err) {
237 		args->handle = 0;
238 		args->pitch = 0;
239 		args->size = 0;
240 	}
241 	return err;
242 }
243 
244 static int drm_mode_mmap_dumb(struct drm_device *dev, struct drm_mode_map_dumb *args,
245 			      struct drm_file *file_priv)
246 {
247 	if (!dev->driver->dumb_create)
248 		return -ENOSYS;
249 
250 	if (dev->driver->dumb_map_offset)
251 		return dev->driver->dumb_map_offset(file_priv, dev, args->handle,
252 						    &args->offset);
253 	else
254 		return drm_gem_dumb_map_offset(file_priv, dev, args->handle,
255 					       &args->offset);
256 }
257 
258 /**
259  * drm_mode_mmap_dumb_ioctl - create an mmap offset for a dumb backing storage buffer
260  * @dev: DRM device
261  * @data: ioctl data
262  * @file_priv: DRM file info
263  *
264  * Allocate an offset in the drm device node's address space to be able to
265  * memory map a dumb buffer.
266  *
267  * Called by the user via ioctl.
268  *
269  * Returns:
270  * Zero on success, negative errno on failure.
271  */
272 int drm_mode_mmap_dumb_ioctl(struct drm_device *dev,
273 			     void *data, struct drm_file *file_priv)
274 {
275 	struct drm_mode_map_dumb *args = data;
276 	int err;
277 
278 	err = drm_mode_mmap_dumb(dev, args, file_priv);
279 	if (err)
280 		args->offset = 0;
281 	return err;
282 }
283 
284 int drm_mode_destroy_dumb(struct drm_device *dev, u32 handle,
285 			  struct drm_file *file_priv)
286 {
287 	if (!dev->driver->dumb_create)
288 		return -ENOSYS;
289 
290 	return drm_gem_handle_delete(file_priv, handle);
291 }
292 
293 int drm_mode_destroy_dumb_ioctl(struct drm_device *dev,
294 				void *data, struct drm_file *file_priv)
295 {
296 	struct drm_mode_destroy_dumb *args = data;
297 
298 	return drm_mode_destroy_dumb(dev, args->handle, file_priv);
299 }
300