1 /* Cairo - a vector graphics library with display and print output
2 *
3 * Copyright © 2009 Chris Wilson
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it either under the terms of the GNU Lesser General Public
7 * License version 2.1 as published by the Free Software Foundation
8 * (the "LGPL") or, at your option, under the terms of the Mozilla
9 * Public License Version 1.1 (the "MPL"). If you do not alter this
10 * notice, a recipient may use your version of this file under either
11 * the MPL or the LGPL.
12 *
13 * You should have received a copy of the LGPL along with this library
14 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
16 * You should have received a copy of the MPL along with this library
17 * in the file COPYING-MPL-1.1
18 *
19 * The contents of this file are subject to the Mozilla Public License
20 * Version 1.1 (the "License"); you may not use this file except in
21 * compliance with the License. You may obtain a copy of the License at
22 * http://www.mozilla.org/MPL/
23 *
24 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
25 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
26 * the specific language governing rights and limitations.
27 *
28 * The Original Code is the cairo graphics library.
29 *
30 * The Initial Developer of the Original Code is Chris Wilson.
31 */
32
33 #include "cairoint.h"
34
35 #include "cairo-drm-private.h"
36
37 #include "cairo-device-private.h"
38 #include "cairo-error-private.h"
39
40 #define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE
41 #include <libudev.h>
42 #include <fcntl.h>
43 #include <unistd.h> /* open(), close() */
44
45 static cairo_drm_device_t *_cairo_drm_known_devices;
46 static cairo_drm_device_t *_cairo_drm_default_device;
47
48 static const char *
get_udev_property(struct udev_device * device,const char * name)49 get_udev_property(struct udev_device *device, const char *name)
50 {
51 struct udev_list_entry *entry;
52
53 udev_list_entry_foreach (entry,
54 udev_device_get_properties_list_entry (device))
55 {
56 if (strcmp (udev_list_entry_get_name (entry), name) == 0)
57 return udev_list_entry_get_value (entry);
58 }
59
60 return NULL;
61 }
62
63 static void
_device_flush(void * abstract_device)64 _device_flush (void *abstract_device)
65 {
66 cairo_drm_device_t *device = abstract_device;
67
68 device->device.flush (device);
69 }
70
71 static void
_device_finish(void * abstract_device)72 _device_finish (void *abstract_device)
73 {
74 cairo_drm_device_t *device = abstract_device;
75
76 CAIRO_MUTEX_LOCK (_cairo_drm_device_mutex);
77 if (device->prev != NULL)
78 device->prev->next = device->next;
79 else
80 _cairo_drm_known_devices = device->next;
81 if (device->next != NULL)
82 device->next->prev = device->prev;
83
84 CAIRO_MUTEX_UNLOCK (_cairo_drm_device_mutex);
85
86 if (_cairo_atomic_ptr_cmpxchg (&_cairo_drm_default_device,
87 device, NULL))
88 {
89 cairo_device_destroy (&device->base);
90 }
91 }
92
93 static void
_device_destroy(void * abstract_device)94 _device_destroy (void *abstract_device)
95 {
96 cairo_drm_device_t *device = abstract_device;
97
98 device->device.destroy (device);
99 }
100
101 static const cairo_device_backend_t _cairo_drm_device_backend = {
102 CAIRO_DEVICE_TYPE_DRM,
103
104 NULL, NULL, /* lock, unlock */
105
106 _device_flush,
107 _device_finish,
108 _device_destroy,
109 };
110
111 cairo_drm_device_t *
_cairo_drm_device_init(cairo_drm_device_t * dev,int fd,dev_t devid,int vendor_id,int chip_id,int max_surface_size)112 _cairo_drm_device_init (cairo_drm_device_t *dev,
113 int fd,
114 dev_t devid,
115 int vendor_id,
116 int chip_id,
117 int max_surface_size)
118 {
119 assert (CAIRO_MUTEX_IS_LOCKED (_cairo_drm_device_mutex));
120
121 _cairo_device_init (&dev->base, &_cairo_drm_device_backend);
122
123 dev->id = devid;
124 dev->vendor_id = vendor_id;
125 dev->chip_id = chip_id;
126 dev->fd = fd;
127
128 dev->max_surface_size = max_surface_size;
129
130 dev->prev = NULL;
131 dev->next = _cairo_drm_known_devices;
132 if (_cairo_drm_known_devices != NULL)
133 _cairo_drm_known_devices->prev = dev;
134 _cairo_drm_known_devices = dev;
135
136 if (_cairo_drm_default_device == NULL)
137 _cairo_drm_default_device = (cairo_drm_device_t *) cairo_device_reference (&dev->base);
138
139 return dev;
140 }
141
142 cairo_device_t *
cairo_drm_device_get(struct udev_device * device)143 cairo_drm_device_get (struct udev_device *device)
144 {
145 static const struct dri_driver_entry {
146 uint32_t vendor_id;
147 uint32_t chip_id;
148 cairo_drm_device_create_func_t create_func;
149 } driver_map[] = {
150 { 0x8086, 0x29a2, _cairo_drm_i965_device_create }, /* I965_G */
151 { 0x8086, 0x2982, _cairo_drm_i965_device_create }, /* G35_G */
152 { 0x8086, 0x2992, _cairo_drm_i965_device_create }, /* I965_Q */
153 { 0x8086, 0x2972, _cairo_drm_i965_device_create }, /* I946_GZ */
154 { 0x8086, 0x2a02, _cairo_drm_i965_device_create }, /* I965_GM */
155 { 0x8086, 0x2a12, _cairo_drm_i965_device_create }, /* I965_GME */
156 { 0x8086, 0x2e02, _cairo_drm_i965_device_create }, /* IGD_E_G */
157 { 0x8086, 0x2e22, _cairo_drm_i965_device_create }, /* G45_G */
158 { 0x8086, 0x2e12, _cairo_drm_i965_device_create }, /* Q45_G */
159 { 0x8086, 0x2e32, _cairo_drm_i965_device_create }, /* G41_G */
160 { 0x8086, 0x2a42, _cairo_drm_i965_device_create }, /* GM45_GM */
161
162 { 0x8086, 0x2582, _cairo_drm_i915_device_create }, /* I915_G */
163 { 0x8086, 0x2592, _cairo_drm_i915_device_create }, /* I915_GM */
164 { 0x8086, 0x258a, _cairo_drm_i915_device_create }, /* E7221_G */
165 { 0x8086, 0x2772, _cairo_drm_i915_device_create }, /* I945_G */
166 { 0x8086, 0x27a2, _cairo_drm_i915_device_create }, /* I945_GM */
167 { 0x8086, 0x27ae, _cairo_drm_i915_device_create }, /* I945_GME */
168 { 0x8086, 0x29c2, _cairo_drm_i915_device_create }, /* G33_G */
169 { 0x8086, 0x29b2, _cairo_drm_i915_device_create }, /* Q35_G */
170 { 0x8086, 0x29d2, _cairo_drm_i915_device_create }, /* Q33_G */
171 { 0x8086, 0xa011, _cairo_drm_i915_device_create }, /* IGD_GM */
172 { 0x8086, 0xa001, _cairo_drm_i915_device_create }, /* IGD_G */
173
174 /* XXX i830 */
175
176 { 0x8086, ~0, _cairo_drm_intel_device_create },
177
178 { 0x1002, ~0, _cairo_drm_radeon_device_create },
179 #if CAIRO_HAS_GALLIUM_SURFACE
180 { ~0, ~0, _cairo_drm_gallium_device_create },
181 #endif
182 };
183
184 cairo_drm_device_t *dev;
185 dev_t devid;
186 struct udev_device *parent;
187 const char *pci_id;
188 uint32_t vendor_id, chip_id;
189 const char *path;
190 int i, fd;
191
192 devid = udev_device_get_devnum (device);
193
194 CAIRO_MUTEX_LOCK (_cairo_drm_device_mutex);
195 for (dev = _cairo_drm_known_devices; dev != NULL; dev = dev->next) {
196 if (dev->id == devid) {
197 dev = (cairo_drm_device_t *) cairo_device_reference (&dev->base);
198 goto DONE;
199 }
200 }
201
202 parent = udev_device_get_parent (device);
203 pci_id = get_udev_property (parent, "PCI_ID");
204 if (pci_id == NULL || sscanf (pci_id, "%x:%x", &vendor_id, &chip_id) != 2) {
205 dev = NULL;
206 goto DONE;
207 }
208
209 #if CAIRO_HAS_GALLIUM_SURFACE
210 if (getenv ("CAIRO_GALLIUM_FORCE"))
211 {
212 i = ARRAY_LENGTH (driver_map) - 1;
213 }
214 else
215 #endif
216 {
217 for (i = 0; i < ARRAY_LENGTH (driver_map); i++) {
218 if (driver_map[i].vendor_id == ~0U)
219 break;
220
221 if (driver_map[i].vendor_id == vendor_id &&
222 (driver_map[i].chip_id == ~0U || driver_map[i].chip_id == chip_id))
223 break;
224 }
225
226 if (i == ARRAY_LENGTH (driver_map)) {
227 dev = (cairo_drm_device_t *)
228 _cairo_device_create_in_error (CAIRO_STATUS_DEVICE_ERROR);
229 goto DONE;
230 }
231 }
232
233 path = udev_device_get_devnode (device);
234 if (path == NULL)
235 path = "/dev/dri/card0"; /* XXX buggy udev? */
236
237 fd = open (path, O_RDWR);
238 if (fd == -1) {
239 /* XXX more likely to be a permissions issue... */
240 _cairo_error_throw (CAIRO_STATUS_FILE_NOT_FOUND);
241 dev = NULL;
242 goto DONE;
243 }
244
245 dev = driver_map[i].create_func (fd, devid, vendor_id, chip_id);
246 if (dev == NULL)
247 close (fd);
248
249 DONE:
250 CAIRO_MUTEX_UNLOCK (_cairo_drm_device_mutex);
251
252 if (dev == NULL)
253 return _cairo_device_create_in_error (CAIRO_STATUS_DEVICE_ERROR);
254 else
255 return &dev->base;
256 }
257 slim_hidden_def (cairo_drm_device_get);
258
259 cairo_device_t *
cairo_drm_device_get_for_fd(int fd)260 cairo_drm_device_get_for_fd (int fd)
261 {
262 struct stat st;
263 struct udev *udev;
264 struct udev_device *device;
265 cairo_device_t *dev = NULL;
266
267 if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode)) {
268 //_cairo_error_throw (CAIRO_STATUS_INVALID_DEVICE);
269 return _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
270 }
271
272 udev = udev_new ();
273
274 device = udev_device_new_from_devnum (udev, 'c', st.st_rdev);
275 if (device != NULL) {
276 dev = cairo_drm_device_get (device);
277 udev_device_unref (device);
278 }
279
280 udev_unref (udev);
281
282 return dev;
283 }
284 slim_hidden_def (cairo_drm_device_get_for_fd);
285
286 cairo_device_t *
cairo_drm_device_default(void)287 cairo_drm_device_default (void)
288 {
289 struct udev *udev;
290 struct udev_enumerate *e;
291 struct udev_list_entry *entry;
292 cairo_device_t *dev;
293
294 /* optimistic atomic pointer read */
295 dev = &_cairo_drm_default_device->base;
296 if (dev != NULL)
297 return dev;
298
299 udev = udev_new();
300 if (udev == NULL)
301 return _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
302
303 e = udev_enumerate_new (udev);
304 udev_enumerate_add_match_subsystem (e, "drm");
305 udev_enumerate_scan_devices (e);
306 udev_list_entry_foreach (entry, udev_enumerate_get_list_entry (e)) {
307 struct udev_device *device;
308
309 device =
310 udev_device_new_from_syspath (udev,
311 udev_list_entry_get_name (entry));
312
313 dev = cairo_drm_device_get (device);
314
315 udev_device_unref (device);
316
317 if (dev != NULL) {
318 if (((cairo_drm_device_t *) dev)->fd == -1) {
319 /* try again, we may find a usable card */
320 cairo_device_destroy (dev);
321 dev = NULL;
322 } else
323 break;
324 }
325 }
326 udev_enumerate_unref (e);
327 udev_unref (udev);
328
329 cairo_device_destroy (dev); /* owned by _cairo_drm_default_device */
330 return dev;
331 }
332 slim_hidden_def (cairo_drm_device_default);
333
334 void
_cairo_drm_device_reset_static_data(void)335 _cairo_drm_device_reset_static_data (void)
336 {
337 if (_cairo_drm_default_device != NULL) {
338 cairo_device_t *device = &_cairo_drm_default_device->base;
339 _cairo_drm_default_device = NULL;
340 cairo_device_destroy (device);
341 }
342 }
343
344 int
cairo_drm_device_get_fd(cairo_device_t * abstract_device)345 cairo_drm_device_get_fd (cairo_device_t *abstract_device)
346 {
347 cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device;
348
349 if (device->base.status)
350 return -1;
351
352 return device->fd;
353 }
354
355 void
_cairo_drm_device_fini(cairo_drm_device_t * device)356 _cairo_drm_device_fini (cairo_drm_device_t *device)
357 {
358 if (device->fd != -1)
359 close (device->fd);
360 }
361
362 void
cairo_drm_device_throttle(cairo_device_t * abstract_device)363 cairo_drm_device_throttle (cairo_device_t *abstract_device)
364 {
365 cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device;
366 cairo_status_t status;
367
368 if (unlikely (device->base.status))
369 return;
370
371 if (device->device.throttle == NULL)
372 return;
373
374 status = device->device.throttle (device);
375 if (unlikely (status))
376 _cairo_status_set_error (&device->base.status, status);
377 }
378
379 cairo_bool_t
_cairo_drm_size_is_valid(cairo_device_t * abstract_device,int width,int height)380 _cairo_drm_size_is_valid (cairo_device_t *abstract_device,
381 int width, int height)
382 {
383 cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device;
384
385 if (unlikely (device->base.status))
386 return FALSE;
387
388 return width <= device->max_surface_size &&
389 height <= device->max_surface_size;
390 }
391