1 /*
2 * Copyright (C) 2011 Intel Corporation.
3 * Copyright (C) 2016 Red Hat
4 * Copyright (C) 2018 DisplayLink (UK) Ltd.
5 * Copyright (C) 2018 Canonical Ltd.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 * 02111-1307, USA.
21 *
22 */
23
24 #include "config.h"
25
26 #include "backends/native/meta-drm-buffer-dumb.h"
27
28 #include <gio/gio.h>
29 #include <xf86drm.h>
30 #include <fcntl.h>
31 #include <sys/mman.h>
32
33 #include "backends/native/meta-device-pool.h"
34
35 struct _MetaDrmBufferDumb
36 {
37 MetaDrmBuffer parent;
38
39 uint32_t handle;
40 void *map;
41 uint64_t map_size;
42 int width;
43 int height;
44 int stride_bytes;
45 uint32_t drm_format;
46 int dmabuf_fd;
47 };
48
G_DEFINE_TYPE(MetaDrmBufferDumb,meta_drm_buffer_dumb,META_TYPE_DRM_BUFFER)49 G_DEFINE_TYPE (MetaDrmBufferDumb, meta_drm_buffer_dumb, META_TYPE_DRM_BUFFER)
50
51 static int
52 meta_drm_buffer_dumb_get_width (MetaDrmBuffer *buffer)
53 {
54 MetaDrmBufferDumb *buffer_dumb = META_DRM_BUFFER_DUMB (buffer);
55
56 return buffer_dumb->width;
57 }
58
59 static int
meta_drm_buffer_dumb_get_height(MetaDrmBuffer * buffer)60 meta_drm_buffer_dumb_get_height (MetaDrmBuffer *buffer)
61 {
62 MetaDrmBufferDumb *buffer_dumb = META_DRM_BUFFER_DUMB (buffer);
63
64 return buffer_dumb->height;
65 }
66
67 static int
meta_drm_buffer_dumb_get_stride(MetaDrmBuffer * buffer)68 meta_drm_buffer_dumb_get_stride (MetaDrmBuffer *buffer)
69 {
70 MetaDrmBufferDumb *buffer_dumb = META_DRM_BUFFER_DUMB (buffer);
71
72 return buffer_dumb->stride_bytes;
73 }
74
75 static uint32_t
meta_drm_buffer_dumb_get_format(MetaDrmBuffer * buffer)76 meta_drm_buffer_dumb_get_format (MetaDrmBuffer *buffer)
77 {
78 MetaDrmBufferDumb *buffer_dumb = META_DRM_BUFFER_DUMB (buffer);
79
80 return buffer_dumb->drm_format;
81 }
82
83 static int
handle_to_dmabuf_fd(MetaDrmBufferDumb * buffer_dumb,GError ** error)84 handle_to_dmabuf_fd (MetaDrmBufferDumb *buffer_dumb,
85 GError **error)
86 {
87 MetaDrmBuffer *buffer = META_DRM_BUFFER (buffer_dumb);
88 MetaDeviceFile *device_file;
89 int fd;
90 int ret;
91 int dmabuf_fd;
92
93 device_file = meta_drm_buffer_get_device_file (buffer);
94 fd = meta_device_file_get_fd (device_file);
95
96 ret = drmPrimeHandleToFD (fd, buffer_dumb->handle, DRM_CLOEXEC,
97 &dmabuf_fd);
98 if (ret)
99 {
100 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
101 "drmPrimeHandleToFd: %s", g_strerror (-ret));
102 return -1;
103 }
104
105 return dmabuf_fd;
106 }
107
108 int
meta_drm_buffer_dumb_ensure_dmabuf_fd(MetaDrmBufferDumb * buffer_dumb,GError ** error)109 meta_drm_buffer_dumb_ensure_dmabuf_fd (MetaDrmBufferDumb *buffer_dumb,
110 GError **error)
111 {
112 if (buffer_dumb->dmabuf_fd != -1)
113 return buffer_dumb->dmabuf_fd;
114
115 buffer_dumb->dmabuf_fd = handle_to_dmabuf_fd (buffer_dumb, error);
116 return buffer_dumb->dmabuf_fd;
117 }
118
119 void *
meta_drm_buffer_dumb_get_data(MetaDrmBufferDumb * buffer_dumb)120 meta_drm_buffer_dumb_get_data (MetaDrmBufferDumb *buffer_dumb)
121 {
122 return buffer_dumb->map;
123 }
124
125 static gboolean
init_dumb_buffer(MetaDrmBufferDumb * buffer_dumb,int width,int height,uint32_t format,GError ** error)126 init_dumb_buffer (MetaDrmBufferDumb *buffer_dumb,
127 int width,
128 int height,
129 uint32_t format,
130 GError **error)
131 {
132 MetaDrmBuffer *buffer = META_DRM_BUFFER (buffer_dumb);
133 MetaDeviceFile *device_file;
134 int fd;
135 struct drm_mode_create_dumb create_arg;
136 struct drm_mode_destroy_dumb destroy_arg;
137 struct drm_mode_map_dumb map_arg;
138 void *map;
139 MetaDrmFbArgs fb_args;
140
141 device_file = meta_drm_buffer_get_device_file (buffer);
142 fd = meta_device_file_get_fd (device_file);
143
144 create_arg = (struct drm_mode_create_dumb) {
145 .bpp = 32, /* RGBX8888 */
146 .width = width,
147 .height = height
148 };
149 if (drmIoctl (fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_arg) != 0)
150 {
151 g_set_error (error, G_IO_ERROR,
152 G_IO_ERROR_FAILED,
153 "Failed to create dumb drm buffer: %s",
154 g_strerror (errno));
155 goto err_ioctl;
156 }
157
158 fb_args = (MetaDrmFbArgs) {
159 .width = width,
160 .height = height,
161 .format = format,
162 .handles = { create_arg.handle },
163 .strides = { create_arg.pitch },
164 };
165 if (!meta_drm_buffer_ensure_fb_id (buffer, FALSE, &fb_args, error))
166 goto err_add_fb;
167
168 map_arg = (struct drm_mode_map_dumb) {
169 .handle = create_arg.handle
170 };
171 if (drmIoctl (fd, DRM_IOCTL_MODE_MAP_DUMB,
172 &map_arg) != 0)
173 {
174 g_set_error (error, G_IO_ERROR,
175 G_IO_ERROR_FAILED,
176 "Failed to map dumb drm buffer: %s",
177 g_strerror (errno));
178 goto err_map_dumb;
179 }
180
181 map = mmap (NULL, create_arg.size, PROT_WRITE, MAP_SHARED,
182 fd, map_arg.offset);
183 if (map == MAP_FAILED)
184 {
185 g_set_error (error, G_IO_ERROR,
186 G_IO_ERROR_FAILED,
187 "Failed to mmap dumb drm buffer memory: %s",
188 g_strerror (errno));
189 goto err_mmap;
190 }
191
192 buffer_dumb->handle = create_arg.handle;
193 buffer_dumb->map = map;
194 buffer_dumb->map_size = create_arg.size;
195 buffer_dumb->width = width;
196 buffer_dumb->height = height;
197 buffer_dumb->stride_bytes = create_arg.pitch;
198 buffer_dumb->drm_format = format;
199
200 return TRUE;
201
202 err_mmap:
203 err_map_dumb:
204 err_add_fb:
205 destroy_arg = (struct drm_mode_destroy_dumb) {
206 .handle = create_arg.handle
207 };
208 drmIoctl (fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg);
209
210 err_ioctl:
211 return FALSE;
212 }
213
214 MetaDrmBufferDumb *
meta_drm_buffer_dumb_new(MetaDeviceFile * device_file,int width,int height,uint32_t format,GError ** error)215 meta_drm_buffer_dumb_new (MetaDeviceFile *device_file,
216 int width,
217 int height,
218 uint32_t format,
219 GError **error)
220 {
221 MetaDrmBufferDumb *buffer_dumb;
222
223 buffer_dumb = g_object_new (META_TYPE_DRM_BUFFER_DUMB,
224 "device-file", device_file,
225 NULL);
226
227 if (!init_dumb_buffer (buffer_dumb, width, height, format, error))
228 {
229 g_object_unref (buffer_dumb);
230 return NULL;
231 }
232
233 return buffer_dumb;
234 }
235
236 static void
destroy_dumb_buffer(MetaDrmBufferDumb * buffer_dumb)237 destroy_dumb_buffer (MetaDrmBufferDumb *buffer_dumb)
238 {
239 MetaDrmBuffer *buffer = META_DRM_BUFFER (buffer_dumb);
240 MetaDeviceFile *device_file;
241 int fd;
242 struct drm_mode_destroy_dumb destroy_arg;
243
244 device_file = meta_drm_buffer_get_device_file (buffer);
245 fd = meta_device_file_get_fd (device_file);
246
247 munmap (buffer_dumb->map, buffer_dumb->map_size);
248
249 destroy_arg = (struct drm_mode_destroy_dumb) {
250 .handle = buffer_dumb->handle
251 };
252 drmIoctl (fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg);
253
254 if (buffer_dumb->dmabuf_fd != -1)
255 close (buffer_dumb->dmabuf_fd);
256 }
257
258 static void
meta_drm_buffer_dumb_finalize(GObject * object)259 meta_drm_buffer_dumb_finalize (GObject *object)
260 {
261 MetaDrmBufferDumb *buffer_dumb = META_DRM_BUFFER_DUMB (object);
262
263 if (buffer_dumb->handle)
264 destroy_dumb_buffer (buffer_dumb);
265
266 G_OBJECT_CLASS (meta_drm_buffer_dumb_parent_class)->finalize (object);
267 }
268
269 static void
meta_drm_buffer_dumb_init(MetaDrmBufferDumb * buffer_dumb)270 meta_drm_buffer_dumb_init (MetaDrmBufferDumb *buffer_dumb)
271 {
272 buffer_dumb->dmabuf_fd = -1;
273 }
274
275 static void
meta_drm_buffer_dumb_class_init(MetaDrmBufferDumbClass * klass)276 meta_drm_buffer_dumb_class_init (MetaDrmBufferDumbClass *klass)
277 {
278 GObjectClass *object_class = G_OBJECT_CLASS (klass);
279 MetaDrmBufferClass *buffer_class = META_DRM_BUFFER_CLASS (klass);
280
281 object_class->finalize = meta_drm_buffer_dumb_finalize;
282
283 buffer_class->get_width = meta_drm_buffer_dumb_get_width;
284 buffer_class->get_height = meta_drm_buffer_dumb_get_height;
285 buffer_class->get_stride = meta_drm_buffer_dumb_get_stride;
286 buffer_class->get_format = meta_drm_buffer_dumb_get_format;
287 }
288