xf86drm: Add platform and host1x bus support

ARM SoCs usually have their DRM/KMS devices on the platform bus, so add
support for that to enable these devices to be used with the drmDevice
infrastructure.

NVIDIA Tegra SoCs have an additional level in the hierarchy and DRM/KMS
devices can also be on the host1x bus. This is mostly equivalent to the
platform bus.

v4:
- continue on error to process platform or host1x device

v3:
- guard Linux-specific sysfs parsing code with #ifdef __linux__

v2:
- be careful not to overflow the full name
- read compatible strings into device info

Reviewed-by: Emil Velikov <emil.l.velikov@gmail.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
main
Thierry Reding 2016-12-21 17:59:04 +01:00
parent f8484ccbd1
commit 7b1f37f474
2 changed files with 328 additions and 2 deletions

300
xf86drm.c
View File

@ -2953,6 +2953,12 @@ static int drmParseSubsystemType(int maj, int min)
if (strncmp(name, "/usb", 4) == 0) if (strncmp(name, "/usb", 4) == 0)
return DRM_BUS_USB; return DRM_BUS_USB;
if (strncmp(name, "/platform", 9) == 0)
return DRM_BUS_PLATFORM;
if (strncmp(name, "/host1x", 7) == 0)
return DRM_BUS_HOST1X;
return -EINVAL; return -EINVAL;
#elif defined(__OpenBSD__) #elif defined(__OpenBSD__)
return DRM_BUS_PCI; return DRM_BUS_PCI;
@ -3043,6 +3049,12 @@ static int drmCompareBusInfo(drmDevicePtr a, drmDevicePtr b)
case DRM_BUS_USB: case DRM_BUS_USB:
return memcmp(a->businfo.usb, b->businfo.usb, sizeof(drmUsbBusInfo)); return memcmp(a->businfo.usb, b->businfo.usb, sizeof(drmUsbBusInfo));
case DRM_BUS_PLATFORM:
return memcmp(a->businfo.platform, b->businfo.platform, sizeof(drmPlatformBusInfo));
case DRM_BUS_HOST1X:
return memcmp(a->businfo.host1x, b->businfo.host1x, sizeof(drmHost1xBusInfo));
default: default:
break; break;
} }
@ -3187,11 +3199,55 @@ static int drmParsePciDeviceInfo(int maj, int min,
#endif #endif
} }
static void drmFreePlatformDevice(drmDevicePtr device)
{
if (device->deviceinfo.platform) {
if (device->deviceinfo.platform->compatible) {
char **compatible = device->deviceinfo.platform->compatible;
while (*compatible) {
free(*compatible);
compatible++;
}
free(device->deviceinfo.platform->compatible);
}
}
}
static void drmFreeHost1xDevice(drmDevicePtr device)
{
if (device->deviceinfo.host1x) {
if (device->deviceinfo.host1x->compatible) {
char **compatible = device->deviceinfo.host1x->compatible;
while (*compatible) {
free(*compatible);
compatible++;
}
free(device->deviceinfo.host1x->compatible);
}
}
}
void drmFreeDevice(drmDevicePtr *device) void drmFreeDevice(drmDevicePtr *device)
{ {
if (device == NULL) if (device == NULL)
return; return;
if (*device) {
switch ((*device)->bustype) {
case DRM_BUS_PLATFORM:
drmFreePlatformDevice(*device);
break;
case DRM_BUS_HOST1X:
drmFreeHost1xDevice(*device);
break;
}
}
free(*device); free(*device);
*device = NULL; *device = NULL;
} }
@ -3393,6 +3449,220 @@ free_device:
return ret; return ret;
} }
static int drmParsePlatformBusInfo(int maj, int min, drmPlatformBusInfoPtr info)
{
#ifdef __linux__
char path[PATH_MAX + 1], *name;
snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
name = sysfs_uevent_get(path, "OF_FULLNAME");
if (!name)
return -ENOENT;
strncpy(info->fullname, name, DRM_PLATFORM_DEVICE_NAME_LEN);
info->fullname[DRM_PLATFORM_DEVICE_NAME_LEN - 1] = '\0';
free(name);
return 0;
#else
#warning "Missing implementation of drmParsePlatformBusInfo"
return -EINVAL;
#endif
}
static int drmParsePlatformDeviceInfo(int maj, int min,
drmPlatformDeviceInfoPtr info)
{
#ifdef __linux__
char path[PATH_MAX + 1], *value;
unsigned int count, i;
int err;
snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
value = sysfs_uevent_get(path, "OF_COMPATIBLE_N");
if (!value)
return -ENOENT;
sscanf(value, "%u", &count);
free(value);
info->compatible = calloc(count + 1, sizeof(*info->compatible));
if (!info->compatible)
return -ENOMEM;
for (i = 0; i < count; i++) {
value = sysfs_uevent_get(path, "OF_COMPATIBLE_%u", i);
if (!value) {
err = -ENOENT;
goto free;
}
info->compatible[i] = value;
}
return 0;
free:
while (i--)
free(info->compatible[i]);
free(info->compatible);
return err;
#else
#warning "Missing implementation of drmParsePlatformDeviceInfo"
return -EINVAL;
#endif
}
static int drmProcessPlatformDevice(drmDevicePtr *device,
const char *node, int node_type,
int maj, int min, bool fetch_deviceinfo,
uint32_t flags)
{
drmDevicePtr dev;
char *ptr;
int ret;
dev = drmDeviceAlloc(node_type, node, sizeof(drmPlatformBusInfo),
sizeof(drmPlatformDeviceInfo), &ptr);
if (!dev)
return -ENOMEM;
dev->bustype = DRM_BUS_PLATFORM;
dev->businfo.platform = (drmPlatformBusInfoPtr)ptr;
ret = drmParsePlatformBusInfo(maj, min, dev->businfo.platform);
if (ret < 0)
goto free_device;
if (fetch_deviceinfo) {
ptr += sizeof(drmPlatformBusInfo);
dev->deviceinfo.platform = (drmPlatformDeviceInfoPtr)ptr;
ret = drmParsePlatformDeviceInfo(maj, min, dev->deviceinfo.platform);
if (ret < 0)
goto free_device;
}
*device = dev;
return 0;
free_device:
free(dev);
return ret;
}
static int drmParseHost1xBusInfo(int maj, int min, drmHost1xBusInfoPtr info)
{
#ifdef __linux__
char path[PATH_MAX + 1], *name;
snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
name = sysfs_uevent_get(path, "OF_FULLNAME");
if (!name)
return -ENOENT;
strncpy(info->fullname, name, DRM_HOST1X_DEVICE_NAME_LEN);
info->fullname[DRM_HOST1X_DEVICE_NAME_LEN - 1] = '\0';
free(name);
return 0;
#else
#warning "Missing implementation of drmParseHost1xBusInfo"
return -EINVAL;
#endif
}
static int drmParseHost1xDeviceInfo(int maj, int min,
drmHost1xDeviceInfoPtr info)
{
#ifdef __linux__
char path[PATH_MAX + 1], *value;
unsigned int count, i;
int err;
snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
value = sysfs_uevent_get(path, "OF_COMPATIBLE_N");
if (!value)
return -ENOENT;
sscanf(value, "%u", &count);
free(value);
info->compatible = calloc(count + 1, sizeof(*info->compatible));
if (!info->compatible)
return -ENOMEM;
for (i = 0; i < count; i++) {
value = sysfs_uevent_get(path, "OF_COMPATIBLE_%u", i);
if (!value) {
err = -ENOENT;
goto free;
}
info->compatible[i] = value;
}
return 0;
free:
while (i--)
free(info->compatible[i]);
free(info->compatible);
return err;
#else
#warning "Missing implementation of drmParseHost1xDeviceInfo"
return -EINVAL;
#endif
}
static int drmProcessHost1xDevice(drmDevicePtr *device,
const char *node, int node_type,
int maj, int min, bool fetch_deviceinfo,
uint32_t flags)
{
drmDevicePtr dev;
char *ptr;
int ret;
dev = drmDeviceAlloc(node_type, node, sizeof(drmHost1xBusInfo),
sizeof(drmHost1xDeviceInfo), &ptr);
if (!dev)
return -ENOMEM;
dev->bustype = DRM_BUS_HOST1X;
dev->businfo.host1x = (drmHost1xBusInfoPtr)ptr;
ret = drmParseHost1xBusInfo(maj, min, dev->businfo.host1x);
if (ret < 0)
goto free_device;
if (fetch_deviceinfo) {
ptr += sizeof(drmHost1xBusInfo);
dev->deviceinfo.host1x = (drmHost1xDeviceInfoPtr)ptr;
ret = drmParseHost1xDeviceInfo(maj, min, dev->deviceinfo.host1x);
if (ret < 0)
goto free_device;
}
*device = dev;
return 0;
free_device:
free(dev);
return ret;
}
/* Consider devices located on the same bus as duplicate and fold the respective /* Consider devices located on the same bus as duplicate and fold the respective
* entries into a single one. * entries into a single one.
* *
@ -3576,6 +3846,20 @@ int drmGetDevice2(int fd, uint32_t flags, drmDevicePtr *device)
break; break;
case DRM_BUS_PLATFORM:
ret = drmProcessPlatformDevice(&d, node, node_type, maj, min, true, flags);
if (ret)
continue;
break;
case DRM_BUS_HOST1X:
ret = drmProcessHost1xDevice(&d, node, node_type, maj, min, true, flags);
if (ret)
continue;
break;
default: default:
continue; continue;
} }
@ -3716,6 +4000,22 @@ int drmGetDevices2(uint32_t flags, drmDevicePtr devices[], int max_devices)
break; break;
case DRM_BUS_PLATFORM:
ret = drmProcessPlatformDevice(&device, node, node_type, maj, min,
devices != NULL, flags);
if (ret)
goto free_devices;
break;
case DRM_BUS_HOST1X:
ret = drmProcessHost1xDevice(&device, node, node_type, maj, min,
devices != NULL, flags);
if (ret)
goto free_devices;
break;
default: default:
continue; continue;
} }

View File

@ -766,8 +766,10 @@ extern int drmPrimeFDToHandle(int fd, int prime_fd, uint32_t *handle);
extern char *drmGetPrimaryDeviceNameFromFd(int fd); extern char *drmGetPrimaryDeviceNameFromFd(int fd);
extern char *drmGetRenderDeviceNameFromFd(int fd); extern char *drmGetRenderDeviceNameFromFd(int fd);
#define DRM_BUS_PCI 0 #define DRM_BUS_PCI 0
#define DRM_BUS_USB 1 #define DRM_BUS_USB 1
#define DRM_BUS_PLATFORM 2
#define DRM_BUS_HOST1X 3
typedef struct _drmPciBusInfo { typedef struct _drmPciBusInfo {
uint16_t domain; uint16_t domain;
@ -794,6 +796,26 @@ typedef struct _drmUsbDeviceInfo {
uint16_t product; uint16_t product;
} drmUsbDeviceInfo, *drmUsbDeviceInfoPtr; } drmUsbDeviceInfo, *drmUsbDeviceInfoPtr;
#define DRM_PLATFORM_DEVICE_NAME_LEN 512
typedef struct _drmPlatformBusInfo {
char fullname[DRM_PLATFORM_DEVICE_NAME_LEN];
} drmPlatformBusInfo, *drmPlatformBusInfoPtr;
typedef struct _drmPlatformDeviceInfo {
char **compatible; /* NULL terminated list of compatible strings */
} drmPlatformDeviceInfo, *drmPlatformDeviceInfoPtr;
#define DRM_HOST1X_DEVICE_NAME_LEN 512
typedef struct _drmHost1xBusInfo {
char fullname[DRM_HOST1X_DEVICE_NAME_LEN];
} drmHost1xBusInfo, *drmHost1xBusInfoPtr;
typedef struct _drmHost1xDeviceInfo {
char **compatible; /* NULL terminated list of compatible strings */
} drmHost1xDeviceInfo, *drmHost1xDeviceInfoPtr;
typedef struct _drmDevice { typedef struct _drmDevice {
char **nodes; /* DRM_NODE_MAX sized array */ char **nodes; /* DRM_NODE_MAX sized array */
int available_nodes; /* DRM_NODE_* bitmask */ int available_nodes; /* DRM_NODE_* bitmask */
@ -801,10 +823,14 @@ typedef struct _drmDevice {
union { union {
drmPciBusInfoPtr pci; drmPciBusInfoPtr pci;
drmUsbBusInfoPtr usb; drmUsbBusInfoPtr usb;
drmPlatformBusInfoPtr platform;
drmHost1xBusInfoPtr host1x;
} businfo; } businfo;
union { union {
drmPciDeviceInfoPtr pci; drmPciDeviceInfoPtr pci;
drmUsbDeviceInfoPtr usb; drmUsbDeviceInfoPtr usb;
drmPlatformDeviceInfoPtr platform;
drmHost1xDeviceInfoPtr host1x;
} deviceinfo; } deviceinfo;
} drmDevice, *drmDevicePtr; } drmDevice, *drmDevicePtr;