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