1 /*
2  * Copyright (C) 2011 Intel Corporation.
3  * Copyright (C) 2016-2020 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  * Author: Daniel van Vugt <daniel.van.vugt@canonical.com>
23  */
24 
25 #include "config.h"
26 
27 #include "backends/native/meta-drm-buffer-private.h"
28 
29 #include <drm_fourcc.h>
30 
31 #include "backends/native/meta-device-pool.h"
32 #include "backends/native/meta-kms-utils.h"
33 
34 #define INVALID_FB_ID 0U
35 
36 enum
37 {
38   PROP_0,
39 
40   PROP_DEVICE_FILE,
41 
42   N_PROPS
43 };
44 
45 static GParamSpec *obj_props[N_PROPS];
46 
47 typedef struct _MetaDrmBufferPrivate
48 {
49   MetaDeviceFile *device_file;
50   uint32_t fb_id;
51 } MetaDrmBufferPrivate;
52 
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(MetaDrmBuffer,meta_drm_buffer,G_TYPE_OBJECT)53 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (MetaDrmBuffer, meta_drm_buffer,
54                                      G_TYPE_OBJECT)
55 
56 MetaDeviceFile *
57 meta_drm_buffer_get_device_file (MetaDrmBuffer *buffer)
58 {
59   MetaDrmBufferPrivate *priv = meta_drm_buffer_get_instance_private (buffer);
60 
61   return priv->device_file;
62 }
63 
64 gboolean
meta_drm_buffer_ensure_fb_id(MetaDrmBuffer * buffer,gboolean use_modifiers,const MetaDrmFbArgs * fb_args,GError ** error)65 meta_drm_buffer_ensure_fb_id (MetaDrmBuffer        *buffer,
66                               gboolean              use_modifiers,
67                               const MetaDrmFbArgs  *fb_args,
68                               GError              **error)
69 {
70   MetaDrmBufferPrivate *priv = meta_drm_buffer_get_instance_private (buffer);
71   int fd;
72   MetaDrmFormatBuf tmp;
73   uint32_t fb_id;
74 
75   fd = meta_device_file_get_fd (priv->device_file);
76 
77   if (use_modifiers && fb_args->modifiers[0] != DRM_FORMAT_MOD_INVALID)
78     {
79       if (drmModeAddFB2WithModifiers (fd,
80                                       fb_args->width,
81                                       fb_args->height,
82                                       fb_args->format,
83                                       fb_args->handles,
84                                       fb_args->strides,
85                                       fb_args->offsets,
86                                       fb_args->modifiers,
87                                       &fb_id,
88                                       DRM_MODE_FB_MODIFIERS))
89         {
90           g_set_error (error,
91                        G_IO_ERROR,
92                        g_io_error_from_errno (errno),
93                        "drmModeAddFB2WithModifiers failed: %s",
94                        g_strerror (errno));
95           return FALSE;
96         }
97     }
98   else if (drmModeAddFB2 (fd,
99                           fb_args->width,
100                           fb_args->height,
101                           fb_args->format,
102                           fb_args->handles,
103                           fb_args->strides,
104                           fb_args->offsets,
105                           &fb_id,
106                           0))
107     {
108       if (fb_args->format != DRM_FORMAT_XRGB8888)
109         {
110           g_set_error (error,
111                        G_IO_ERROR,
112                        G_IO_ERROR_FAILED,
113                        "drmModeAddFB does not support format '%s' (0x%x)",
114                        meta_drm_format_to_string (&tmp, fb_args->format),
115                        fb_args->format);
116           return FALSE;
117         }
118 
119       if (drmModeAddFB (fd,
120                         fb_args->width,
121                         fb_args->height,
122                         24,
123                         32,
124                         fb_args->strides[0],
125                         fb_args->handles[0],
126                         &fb_id))
127         {
128           g_set_error (error,
129                        G_IO_ERROR,
130                        g_io_error_from_errno (errno),
131                        "drmModeAddFB failed: %s",
132                        g_strerror (errno));
133           return FALSE;
134         }
135     }
136 
137   priv->fb_id = fb_id;
138   return TRUE;
139 }
140 
141 static void
meta_drm_buffer_release_fb_id(MetaDrmBuffer * buffer)142 meta_drm_buffer_release_fb_id (MetaDrmBuffer *buffer)
143 {
144   MetaDrmBufferPrivate *priv = meta_drm_buffer_get_instance_private (buffer);
145   int fd;
146   int ret;
147 
148   fd = meta_device_file_get_fd (priv->device_file);
149   ret = drmModeRmFB (fd, priv->fb_id);
150   if (ret != 0)
151     g_warning ("drmModeRmFB: %s", g_strerror (-ret));
152 
153   priv->fb_id = 0;
154 }
155 
156 uint32_t
meta_drm_buffer_get_fb_id(MetaDrmBuffer * buffer)157 meta_drm_buffer_get_fb_id (MetaDrmBuffer *buffer)
158 {
159   MetaDrmBufferPrivate *priv = meta_drm_buffer_get_instance_private (buffer);
160 
161   return priv->fb_id;
162 }
163 
164 int
meta_drm_buffer_get_width(MetaDrmBuffer * buffer)165 meta_drm_buffer_get_width (MetaDrmBuffer *buffer)
166 {
167   return META_DRM_BUFFER_GET_CLASS (buffer)->get_width (buffer);
168 }
169 
170 int
meta_drm_buffer_get_height(MetaDrmBuffer * buffer)171 meta_drm_buffer_get_height (MetaDrmBuffer *buffer)
172 {
173   return META_DRM_BUFFER_GET_CLASS (buffer)->get_height (buffer);
174 }
175 
176 int
meta_drm_buffer_get_stride(MetaDrmBuffer * buffer)177 meta_drm_buffer_get_stride (MetaDrmBuffer *buffer)
178 {
179   return META_DRM_BUFFER_GET_CLASS (buffer)->get_stride (buffer);
180 }
181 
182 uint32_t
meta_drm_buffer_get_format(MetaDrmBuffer * buffer)183 meta_drm_buffer_get_format (MetaDrmBuffer *buffer)
184 {
185   return META_DRM_BUFFER_GET_CLASS (buffer)->get_format (buffer);
186 }
187 
188 gboolean
meta_drm_buffer_supports_fill_timings(MetaDrmBuffer * buffer)189 meta_drm_buffer_supports_fill_timings (MetaDrmBuffer *buffer)
190 {
191   return META_DRM_BUFFER_GET_CLASS (buffer)->fill_timings != NULL;
192 }
193 
194 gboolean
meta_drm_buffer_fill_timings(MetaDrmBuffer * buffer,CoglFrameInfo * info,GError ** error)195 meta_drm_buffer_fill_timings (MetaDrmBuffer  *buffer,
196                               CoglFrameInfo  *info,
197                               GError        **error)
198 {
199   if (!meta_drm_buffer_supports_fill_timings (buffer))
200     {
201       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
202                            "Buffer doesn't support filling timing info");
203       return FALSE;
204     }
205 
206   return META_DRM_BUFFER_GET_CLASS (buffer)->fill_timings (buffer, info, error);
207 }
208 
209 static void
meta_drm_buffer_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)210 meta_drm_buffer_get_property (GObject    *object,
211                               guint       prop_id,
212                               GValue     *value,
213                               GParamSpec *pspec)
214 {
215   MetaDrmBuffer *buffer = META_DRM_BUFFER (object);
216   MetaDrmBufferPrivate *priv = meta_drm_buffer_get_instance_private (buffer);
217 
218   switch (prop_id)
219     {
220     case PROP_DEVICE_FILE:
221       g_value_set_pointer (value, priv->device_file);
222       break;
223     default:
224       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
225       break;
226     }
227 }
228 
229 static void
meta_drm_buffer_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)230 meta_drm_buffer_set_property (GObject      *object,
231                               guint         prop_id,
232                               const GValue *value,
233                               GParamSpec   *pspec)
234 {
235   MetaDrmBuffer *buffer = META_DRM_BUFFER (object);
236   MetaDrmBufferPrivate *priv = meta_drm_buffer_get_instance_private (buffer);
237 
238   switch (prop_id)
239     {
240     case PROP_DEVICE_FILE:
241       priv->device_file = g_value_get_pointer (value);
242       break;
243     default:
244       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
245       break;
246     }
247 }
248 
249 static void
meta_drm_buffer_finalize(GObject * object)250 meta_drm_buffer_finalize (GObject *object)
251 {
252   MetaDrmBuffer *buffer = META_DRM_BUFFER (object);
253   MetaDrmBufferPrivate *priv = meta_drm_buffer_get_instance_private (buffer);
254 
255   if (priv->fb_id != INVALID_FB_ID)
256     meta_drm_buffer_release_fb_id (buffer);
257   meta_device_file_release (priv->device_file);
258 
259   G_OBJECT_CLASS (meta_drm_buffer_parent_class)->finalize (object);
260 }
261 
262 static void
meta_drm_buffer_constructed(GObject * object)263 meta_drm_buffer_constructed (GObject *object)
264 {
265   MetaDrmBuffer *buffer = META_DRM_BUFFER (object);
266   MetaDrmBufferPrivate *priv = meta_drm_buffer_get_instance_private (buffer);
267 
268   meta_device_file_acquire (priv->device_file);
269 
270   G_OBJECT_CLASS (meta_drm_buffer_parent_class)->constructed (object);
271 }
272 
273 static void
meta_drm_buffer_init(MetaDrmBuffer * buffer)274 meta_drm_buffer_init (MetaDrmBuffer *buffer)
275 {
276 }
277 
278 static void
meta_drm_buffer_class_init(MetaDrmBufferClass * klass)279 meta_drm_buffer_class_init (MetaDrmBufferClass *klass)
280 {
281   GObjectClass *object_class = G_OBJECT_CLASS (klass);
282 
283   object_class->get_property = meta_drm_buffer_get_property;
284   object_class->set_property = meta_drm_buffer_set_property;
285   object_class->constructed = meta_drm_buffer_constructed;
286   object_class->finalize = meta_drm_buffer_finalize;
287 
288   obj_props[PROP_DEVICE_FILE] =
289     g_param_spec_pointer ("device-file",
290                           "device file",
291                           "MetaDeviceFile",
292                           G_PARAM_READWRITE |
293                           G_PARAM_CONSTRUCT_ONLY |
294                           G_PARAM_STATIC_STRINGS);
295   g_object_class_install_properties (object_class, N_PROPS, obj_props);
296 }
297