Tizen 2.0 Release
[framework/graphics/cairo.git] / src / drm / cairo-drm.c
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 *
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
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
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
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 *
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 *
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 = (cairo_drm_device_t *)
206             _cairo_device_create_in_error (CAIRO_STATUS_DEVICE_ERROR);
207         goto DONE;
208     }
209
210 #if CAIRO_HAS_GALLIUM_SURFACE
211     if (getenv ("CAIRO_GALLIUM_FORCE"))
212     {
213         i = ARRAY_LENGTH (driver_map) - 1;
214     }
215     else
216 #endif
217     {
218         for (i = 0; i < ARRAY_LENGTH (driver_map); i++) {
219             if (driver_map[i].vendor_id == ~0U)
220                 break;
221
222             if (driver_map[i].vendor_id == vendor_id &&
223                 (driver_map[i].chip_id == ~0U || driver_map[i].chip_id == chip_id))
224                 break;
225         }
226
227         if (i == ARRAY_LENGTH (driver_map)) {
228             dev = (cairo_drm_device_t *)
229                 _cairo_device_create_in_error (CAIRO_STATUS_DEVICE_ERROR);
230             goto DONE;
231         }
232     }
233
234     path = udev_device_get_devnode (device);
235     if (path == NULL)
236         path = "/dev/dri/card0"; /* XXX buggy udev? */
237
238     fd = open (path, O_RDWR);
239     if (fd == -1) {
240         /* XXX more likely to be a permissions issue... */
241         _cairo_error_throw (CAIRO_STATUS_FILE_NOT_FOUND);
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     return &dev->base;
253 }
254 slim_hidden_def (cairo_drm_device_get);
255
256 cairo_device_t *
257 cairo_drm_device_get_for_fd (int fd)
258 {
259     struct stat st;
260     struct udev *udev;
261     struct udev_device *device;
262     cairo_device_t *dev = NULL;
263
264     if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode)) {
265         //_cairo_error_throw (CAIRO_STATUS_INVALID_DEVICE);
266         return _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
267     }
268
269     udev = udev_new ();
270
271     device = udev_device_new_from_devnum (udev, 'c', st.st_rdev);
272     if (device != NULL) {
273         dev = cairo_drm_device_get (device);
274         udev_device_unref (device);
275     }
276
277     udev_unref (udev);
278
279     return dev;
280 }
281 slim_hidden_def (cairo_drm_device_get_for_fd);
282
283 cairo_device_t *
284 cairo_drm_device_default (void)
285 {
286     struct udev *udev;
287     struct udev_enumerate *e;
288     struct udev_list_entry *entry;
289     cairo_device_t *dev;
290
291     /* optimistic atomic pointer read */
292     dev = &_cairo_drm_default_device->base;
293     if (dev != NULL)
294         return dev;
295
296     udev = udev_new();
297     if (udev == NULL)
298         return _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
299
300     e = udev_enumerate_new (udev);
301     udev_enumerate_add_match_subsystem (e, "drm");
302     udev_enumerate_scan_devices (e);
303     udev_list_entry_foreach (entry, udev_enumerate_get_list_entry (e)) {
304         struct udev_device *device;
305
306         device =
307             udev_device_new_from_syspath (udev,
308                     udev_list_entry_get_name (entry));
309
310         dev = cairo_drm_device_get (device);
311
312         udev_device_unref (device);
313
314         if (dev != NULL) {
315             if (((cairo_drm_device_t *) dev)->fd == -1) {
316                 /* try again, we may find a usable card */
317                 cairo_device_destroy (dev);
318                 dev = NULL;
319             } else
320                 break;
321         }
322     }
323     udev_enumerate_unref (e);
324     udev_unref (udev);
325
326     cairo_device_destroy (dev); /* owned by _cairo_drm_default_device */
327     return dev;
328 }
329 slim_hidden_def (cairo_drm_device_default);
330
331 void
332 _cairo_drm_device_reset_static_data (void)
333 {
334     if (_cairo_drm_default_device != NULL) {
335         cairo_device_t *device = &_cairo_drm_default_device->base;
336         _cairo_drm_default_device = NULL;
337         cairo_device_destroy (device);
338     }
339 }
340
341 int
342 cairo_drm_device_get_fd (cairo_device_t *abstract_device)
343 {
344     cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device;
345
346     if (device->base.status)
347         return -1;
348
349     return device->fd;
350 }
351
352 void
353 _cairo_drm_device_fini (cairo_drm_device_t *device)
354 {
355     if (device->fd != -1)
356         close (device->fd);
357 }
358
359 void
360 cairo_drm_device_throttle (cairo_device_t *abstract_device)
361 {
362     cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device;
363     cairo_status_t status;
364
365     if (unlikely (device->base.status))
366         return;
367
368     if (device->device.throttle == NULL)
369         return;
370
371     status = device->device.throttle (device);
372     if (unlikely (status))
373         _cairo_status_set_error (&device->base.status, status);
374 }
375
376 cairo_bool_t
377 _cairo_drm_size_is_valid (cairo_device_t *abstract_device,
378                           int width, int height)
379 {
380     cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device;
381
382     if (unlikely (device->base.status))
383         return FALSE;
384
385     return width  <= device->max_surface_size &&
386            height <= device->max_surface_size;
387 }