diff options
Diffstat (limited to 'src/drm/cairo-drm.c')
-rw-r--r-- | src/drm/cairo-drm.c | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/src/drm/cairo-drm.c b/src/drm/cairo-drm.c new file mode 100644 index 0000000..9d227b3 --- /dev/null +++ b/src/drm/cairo-drm.c @@ -0,0 +1,362 @@ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Chris Wilson. + */ + +#include "cairoint.h" + +#include "cairo-drm-private.h" + +#define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE +#include <libudev.h> +#include <fcntl.h> +#include <unistd.h> /* open(), close() */ + +static cairo_drm_device_t *_cairo_drm_known_devices; +static cairo_drm_device_t *_cairo_drm_default_device; + +static const cairo_drm_device_t _nil_device = { + CAIRO_REFERENCE_COUNT_INVALID, + CAIRO_STATUS_NO_MEMORY +}; + +static const cairo_drm_device_t _invalid_device = { + CAIRO_REFERENCE_COUNT_INVALID, + CAIRO_STATUS_INVALID_CONTENT +}; + +cairo_drm_device_t * +_cairo_drm_device_create_in_error (cairo_status_t status) +{ + switch ((int) status) { + default: + ASSERT_NOT_REACHED; + case CAIRO_STATUS_NO_MEMORY: + return (cairo_drm_device_t *) &_nil_device; + case CAIRO_STATUS_INVALID_CONTENT: + return (cairo_drm_device_t *) &_invalid_device; + } +} + +static const char * +get_udev_property(struct udev_device *device, const char *name) +{ + struct udev_list_entry *entry; + + udev_list_entry_foreach (entry, + udev_device_get_properties_list_entry (device)) + { + if (strcmp (udev_list_entry_get_name (entry), name) == 0) + return udev_list_entry_get_value (entry); + } + + return NULL; +} + +cairo_drm_device_t * +_cairo_drm_device_init (cairo_drm_device_t *dev, + int fd, + dev_t devid, + int max_surface_size) +{ + CAIRO_REFERENCE_COUNT_INIT (&dev->ref_count, 1); + dev->status = CAIRO_STATUS_SUCCESS; + + dev->id = devid; + dev->fd = fd; + + dev->max_surface_size = max_surface_size; + + dev->prev = NULL; + dev->next = _cairo_drm_known_devices; + if (_cairo_drm_known_devices != NULL) + _cairo_drm_known_devices->prev = dev; + _cairo_drm_known_devices = dev; + + if (_cairo_drm_default_device == NULL) + _cairo_drm_default_device = cairo_drm_device_reference (dev); + + return dev; +} + +cairo_drm_device_t * +cairo_drm_device_get (struct udev_device *device) +{ + static const struct dri_driver_entry { + uint32_t vendor_id; + uint32_t chip_id; + cairo_drm_device_create_func_t create_func; + } driver_map[] = { + { 0x8086, ~0, _cairo_drm_intel_device_create }, + { 0x1002, ~0, _cairo_drm_radeon_device_create }, +#if CAIRO_HAS_GALLIUM_SURFACE + { ~0, ~0, _cairo_drm_gallium_device_create }, +#endif + }; + + cairo_drm_device_t *dev; + dev_t devid; + struct udev_device *parent; + const char *pci_id; + uint32_t vendor_id, chip_id; + const char *path; + int i, fd; + + devid = udev_device_get_devnum (device); + + CAIRO_MUTEX_LOCK (_cairo_drm_device_mutex); + for (dev = _cairo_drm_known_devices; dev != NULL; dev = dev->next) { + if (dev->id == devid) { + dev = cairo_drm_device_reference (dev); + goto DONE; + } + } + + dev = (cairo_drm_device_t *) &_nil_device; + parent = udev_device_get_parent (device); + pci_id = get_udev_property (parent, "PCI_ID"); + if (sscanf (pci_id, "%x:%x", &vendor_id, &chip_id) != 2) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + goto DONE; + } + +#if CAIRO_HAS_GALLIUM_SURFACE + if (getenv ("CAIRO_GALLIUM_FORCE")) + { + i = ARRAY_LENGTH (driver_map) - 1; + } + else +#endif + { + for (i = 0; i < ARRAY_LENGTH (driver_map); i++) { + if (driver_map[i].vendor_id == ~0U) + break; + + if (driver_map[i].vendor_id == vendor_id && + (driver_map[i].chip_id == ~0U || driver_map[i].chip_id == chip_id)) + break; + } + + if (i == ARRAY_LENGTH (driver_map)) { + /* XXX should be no driver or something*/ + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + goto DONE; + } + } + + path = udev_device_get_devnode (device); + if (path == NULL) + path = "/dev/dri/card0"; /* XXX buggy udev? */ + + fd = open (path, O_RDWR); + if (fd == -1) { + /* XXX more likely to be a permissions issue... */ + _cairo_error_throw (CAIRO_STATUS_FILE_NOT_FOUND); + goto DONE; + } + + dev = driver_map[i].create_func (fd, devid, vendor_id, chip_id); + if (dev == NULL) + close (fd); + + DONE: + CAIRO_MUTEX_UNLOCK (_cairo_drm_device_mutex); + + return dev; +} +slim_hidden_def (cairo_drm_device_get); + +cairo_drm_device_t * +cairo_drm_device_get_for_fd (int fd) +{ + struct stat st; + struct udev *udev; + struct udev_device *device; + cairo_drm_device_t *dev = NULL; + + if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode)) { + //_cairo_error_throw (CAIRO_STATUS_INVALID_DEVICE); + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_drm_device_t *) &_nil_device; + } + + udev = udev_new (); + + device = udev_device_new_from_devnum (udev, 'c', st.st_rdev); + if (device != NULL) { + dev = cairo_drm_device_get (device); + udev_device_unref (device); + } + + udev_unref (udev); + + return dev; +} + +cairo_drm_device_t * +cairo_drm_device_default (void) +{ + struct udev *udev; + struct udev_enumerate *e; + struct udev_list_entry *entry; + cairo_drm_device_t *dev; + + /* optimistic atomic pointer read */ + dev = _cairo_drm_default_device; + if (dev != NULL) + return dev; + + udev = udev_new(); + if (udev == NULL) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_drm_device_t *) &_nil_device; + } + + e = udev_enumerate_new (udev); + udev_enumerate_add_match_subsystem (e, "drm"); + udev_enumerate_scan_devices (e); + udev_list_entry_foreach (entry, udev_enumerate_get_list_entry (e)) { + struct udev_device *device; + + device = + udev_device_new_from_syspath (udev, + udev_list_entry_get_name (entry)); + + dev = cairo_drm_device_get (device); + + udev_device_unref (device); + + if (dev != NULL) { + if (dev->fd == -1) { /* try again, we may find a usable card */ + cairo_drm_device_destroy (dev); + dev = NULL; + } else + break; + } + } + udev_enumerate_unref (e); + udev_unref (udev); + + cairo_drm_device_destroy (dev); /* owned by _cairo_drm_default_device */ + return dev; +} +slim_hidden_def (cairo_drm_device_default); + +void +_cairo_drm_device_reset_static_data (void) +{ + if (_cairo_drm_default_device != NULL) { + cairo_drm_device_destroy (_cairo_drm_default_device); + _cairo_drm_default_device = NULL; + } +} + +cairo_drm_device_t * +cairo_drm_device_reference (cairo_drm_device_t *device) +{ + if (device == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) + { + return device; + } + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&device->ref_count)); + _cairo_reference_count_inc (&device->ref_count); + + return device; +} +slim_hidden_def (cairo_drm_device_reference); + +int +cairo_drm_device_get_fd (cairo_drm_device_t *device) +{ + if (device->status) + return -1; + + return device->fd; +} + +cairo_status_t +cairo_drm_device_status (cairo_drm_device_t *device) +{ + if (device == NULL) + return CAIRO_STATUS_NULL_POINTER; + + return device->status; +} + +void +_cairo_drm_device_fini (cairo_drm_device_t *device) +{ + CAIRO_MUTEX_LOCK (_cairo_drm_device_mutex); + if (device->prev != NULL) + device->prev->next = device->next; + else + _cairo_drm_known_devices = device->next; + if (device->next != NULL) + device->next->prev = device->prev; + CAIRO_MUTEX_UNLOCK (_cairo_drm_device_mutex); + + if (device->fd != -1) + close (device->fd); +} + +void +cairo_drm_device_destroy (cairo_drm_device_t *device) +{ + if (device == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) + { + return; + } + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&device->ref_count)); + if (! _cairo_reference_count_dec_and_test (&device->ref_count)) + return; + + device->device.destroy (device); +} +slim_hidden_def (cairo_drm_device_destroy); + +void +cairo_drm_device_throttle (cairo_drm_device_t *dev) +{ + cairo_status_t status; + + if (unlikely (dev->status)) + return; + + if (dev->device.throttle == NULL) + return; + + status = dev->device.throttle (dev); + if (unlikely (status)) + _cairo_status_set_error (&dev->status, status); +} |