freebsd-dev/sys/dev/drm2/drm_os_freebsd.c
Jean-Sébastien Pédron 455fa6518a drm: Update the device-independent code to match Linux 3.8.13
This update brings few features:
    o  Support for the setmaster/dropmaster ioctls. For instance, they
       are used to run multiple X servers simultaneously.
    o  Support for minor devices. The only user-visible change is a new
       entry in /dev/dri but it is useless at the moment. This is a
       first step to support render nodes [1].

The main benefit is to greatly reduce the diff with Linux (at the
expense of an unreadable commit diff). Hopefully, next upgrades will be
easier.

No updates were made to the drivers, beside adapting them to API
changes.

[1] https://en.wikipedia.org/wiki/Direct_Rendering_Manager#Render_nodes

Tested by:	Many people
MFC after:	1 month
Relnotes:	yes
2015-03-17 18:50:33 +00:00

394 lines
9.3 KiB
C

#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <dev/drm2/drmP.h>
#include <dev/agp/agpreg.h>
#include <dev/pci/pcireg.h>
devclass_t drm_devclass;
MALLOC_DEFINE(DRM_MEM_DMA, "drm_dma", "DRM DMA Data Structures");
MALLOC_DEFINE(DRM_MEM_SAREA, "drm_sarea", "DRM SAREA Data Structures");
MALLOC_DEFINE(DRM_MEM_DRIVER, "drm_driver", "DRM DRIVER Data Structures");
MALLOC_DEFINE(DRM_MEM_MAGIC, "drm_magic", "DRM MAGIC Data Structures");
MALLOC_DEFINE(DRM_MEM_MINOR, "drm_minor", "DRM MINOR Data Structures");
MALLOC_DEFINE(DRM_MEM_IOCTLS, "drm_ioctls", "DRM IOCTL Data Structures");
MALLOC_DEFINE(DRM_MEM_MAPS, "drm_maps", "DRM MAP Data Structures");
MALLOC_DEFINE(DRM_MEM_BUFS, "drm_bufs", "DRM BUFFER Data Structures");
MALLOC_DEFINE(DRM_MEM_SEGS, "drm_segs", "DRM SEGMENTS Data Structures");
MALLOC_DEFINE(DRM_MEM_PAGES, "drm_pages", "DRM PAGES Data Structures");
MALLOC_DEFINE(DRM_MEM_FILES, "drm_files", "DRM FILE Data Structures");
MALLOC_DEFINE(DRM_MEM_QUEUES, "drm_queues", "DRM QUEUE Data Structures");
MALLOC_DEFINE(DRM_MEM_CMDS, "drm_cmds", "DRM COMMAND Data Structures");
MALLOC_DEFINE(DRM_MEM_MAPPINGS, "drm_mapping", "DRM MAPPING Data Structures");
MALLOC_DEFINE(DRM_MEM_BUFLISTS, "drm_buflists", "DRM BUFLISTS Data Structures");
MALLOC_DEFINE(DRM_MEM_AGPLISTS, "drm_agplists", "DRM AGPLISTS Data Structures");
MALLOC_DEFINE(DRM_MEM_CTXBITMAP, "drm_ctxbitmap",
"DRM CTXBITMAP Data Structures");
MALLOC_DEFINE(DRM_MEM_SGLISTS, "drm_sglists", "DRM SGLISTS Data Structures");
MALLOC_DEFINE(DRM_MEM_MM, "drm_sman", "DRM MEMORY MANAGER Data Structures");
MALLOC_DEFINE(DRM_MEM_HASHTAB, "drm_hashtab", "DRM HASHTABLE Data Structures");
MALLOC_DEFINE(DRM_MEM_KMS, "drm_kms", "DRM KMS Data Structures");
MALLOC_DEFINE(DRM_MEM_VBLANK, "drm_vblank", "DRM VBLANK Handling Data");
const char *fb_mode_option = NULL;
#define NSEC_PER_USEC 1000L
#define NSEC_PER_SEC 1000000000L
int64_t
timeval_to_ns(const struct timeval *tv)
{
return ((int64_t)tv->tv_sec * NSEC_PER_SEC) +
tv->tv_usec * NSEC_PER_USEC;
}
struct timeval
ns_to_timeval(const int64_t nsec)
{
struct timeval tv;
long rem;
if (nsec == 0) {
tv.tv_sec = 0;
tv.tv_usec = 0;
return (tv);
}
tv.tv_sec = nsec / NSEC_PER_SEC;
rem = nsec % NSEC_PER_SEC;
if (rem < 0) {
tv.tv_sec--;
rem += NSEC_PER_SEC;
}
tv.tv_usec = rem / 1000;
return (tv);
}
static drm_pci_id_list_t *
drm_find_description(int vendor, int device, drm_pci_id_list_t *idlist)
{
int i = 0;
for (i = 0; idlist[i].vendor != 0; i++) {
if ((idlist[i].vendor == vendor) &&
((idlist[i].device == device) ||
(idlist[i].device == 0))) {
return (&idlist[i]);
}
}
return (NULL);
}
/*
* drm_probe_helper: called by a driver at the end of its probe
* method.
*/
int
drm_probe_helper(device_t kdev, drm_pci_id_list_t *idlist)
{
drm_pci_id_list_t *id_entry;
int vendor, device;
vendor = pci_get_vendor(kdev);
device = pci_get_device(kdev);
if (pci_get_class(kdev) != PCIC_DISPLAY ||
(pci_get_subclass(kdev) != PCIS_DISPLAY_VGA &&
pci_get_subclass(kdev) != PCIS_DISPLAY_OTHER))
return (-ENXIO);
id_entry = drm_find_description(vendor, device, idlist);
if (id_entry != NULL) {
if (device_get_desc(kdev) == NULL) {
DRM_DEBUG("%s desc: %s\n",
device_get_nameunit(kdev), id_entry->name);
device_set_desc(kdev, id_entry->name);
}
return (0);
}
return (-ENXIO);
}
/*
* drm_attach_helper: called by a driver at the end of its attach
* method.
*/
int
drm_attach_helper(device_t kdev, drm_pci_id_list_t *idlist,
struct drm_driver *driver)
{
struct drm_device *dev;
int vendor, device;
int ret;
dev = device_get_softc(kdev);
vendor = pci_get_vendor(kdev);
device = pci_get_device(kdev);
dev->id_entry = drm_find_description(vendor, device, idlist);
ret = drm_get_pci_dev(kdev, dev, driver);
return (ret);
}
int
drm_generic_detach(device_t kdev)
{
struct drm_device *dev;
int i;
dev = device_get_softc(kdev);
drm_put_dev(dev);
/* Clean up PCI resources allocated by drm_bufs.c. We're not really
* worried about resource consumption while the DRM is inactive (between
* lastclose and firstopen or unload) because these aren't actually
* taking up KVA, just keeping the PCI resource allocated.
*/
for (i = 0; i < DRM_MAX_PCI_RESOURCE; i++) {
if (dev->pcir[i] == NULL)
continue;
bus_release_resource(dev->dev, SYS_RES_MEMORY,
dev->pcirid[i], dev->pcir[i]);
dev->pcir[i] = NULL;
}
if (pci_disable_busmaster(dev->dev))
DRM_ERROR("Request to disable bus-master failed.\n");
return (0);
}
int
drm_add_busid_modesetting(struct drm_device *dev, struct sysctl_ctx_list *ctx,
struct sysctl_oid *top)
{
struct sysctl_oid *oid;
snprintf(dev->busid_str, sizeof(dev->busid_str),
"pci:%04x:%02x:%02x.%d", dev->pci_domain, dev->pci_bus,
dev->pci_slot, dev->pci_func);
oid = SYSCTL_ADD_STRING(ctx, SYSCTL_CHILDREN(top), OID_AUTO, "busid",
CTLFLAG_RD, dev->busid_str, 0, NULL);
if (oid == NULL)
return (-ENOMEM);
dev->modesetting = (dev->driver->driver_features & DRIVER_MODESET) != 0;
oid = SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(top), OID_AUTO,
"modesetting", CTLFLAG_RD, &dev->modesetting, 0, NULL);
if (oid == NULL)
return (-ENOMEM);
return (0);
}
static int
drm_device_find_capability(struct drm_device *dev, int cap)
{
return (pci_find_cap(dev->dev, cap, NULL) == 0);
}
int
drm_pci_device_is_agp(struct drm_device *dev)
{
if (dev->driver->device_is_agp != NULL) {
int ret;
/* device_is_agp returns a tristate, 0 = not AGP, 1 = definitely
* AGP, 2 = fall back to PCI capability
*/
ret = (*dev->driver->device_is_agp)(dev);
if (ret != DRM_MIGHT_BE_AGP)
return ret;
}
return (drm_device_find_capability(dev, PCIY_AGP));
}
int
drm_pci_device_is_pcie(struct drm_device *dev)
{
return (drm_device_find_capability(dev, PCIY_EXPRESS));
}
static bool
dmi_found(const struct dmi_system_id *dsi)
{
char *hw_vendor, *hw_prod;
int i, slot;
bool res;
hw_vendor = kern_getenv("smbios.planar.maker");
hw_prod = kern_getenv("smbios.planar.product");
res = true;
for (i = 0; i < nitems(dsi->matches); i++) {
slot = dsi->matches[i].slot;
switch (slot) {
case DMI_NONE:
break;
case DMI_SYS_VENDOR:
case DMI_BOARD_VENDOR:
if (hw_vendor != NULL &&
!strcmp(hw_vendor, dsi->matches[i].substr)) {
break;
} else {
res = false;
goto out;
}
case DMI_PRODUCT_NAME:
case DMI_BOARD_NAME:
if (hw_prod != NULL &&
!strcmp(hw_prod, dsi->matches[i].substr)) {
break;
} else {
res = false;
goto out;
}
default:
res = false;
goto out;
}
}
out:
freeenv(hw_vendor);
freeenv(hw_prod);
return (res);
}
bool
dmi_check_system(const struct dmi_system_id *sysid)
{
const struct dmi_system_id *dsi;
bool res;
for (res = false, dsi = sysid; dsi->matches[0].slot != 0 ; dsi++) {
if (dmi_found(dsi)) {
res = true;
if (dsi->callback != NULL && dsi->callback(dsi))
break;
}
}
return (res);
}
int
drm_mtrr_add(unsigned long offset, unsigned long size, unsigned int flags)
{
int act;
struct mem_range_desc mrdesc;
mrdesc.mr_base = offset;
mrdesc.mr_len = size;
mrdesc.mr_flags = flags;
act = MEMRANGE_SET_UPDATE;
strlcpy(mrdesc.mr_owner, "drm", sizeof(mrdesc.mr_owner));
return (-mem_range_attr_set(&mrdesc, &act));
}
int
drm_mtrr_del(int handle __unused, unsigned long offset, unsigned long size,
unsigned int flags)
{
int act;
struct mem_range_desc mrdesc;
mrdesc.mr_base = offset;
mrdesc.mr_len = size;
mrdesc.mr_flags = flags;
act = MEMRANGE_SET_REMOVE;
strlcpy(mrdesc.mr_owner, "drm", sizeof(mrdesc.mr_owner));
return (-mem_range_attr_set(&mrdesc, &act));
}
void
drm_clflush_pages(vm_page_t *pages, unsigned long num_pages)
{
#if defined(__i386__) || defined(__amd64__)
pmap_invalidate_cache_pages(pages, num_pages);
#else
DRM_ERROR("drm_clflush_pages not implemented on this architecture");
#endif
}
void
drm_clflush_virt_range(char *addr, unsigned long length)
{
#if defined(__i386__) || defined(__amd64__)
pmap_invalidate_cache_range((vm_offset_t)addr,
(vm_offset_t)addr + length, TRUE);
#else
DRM_ERROR("drm_clflush_virt_range not implemented on this architecture");
#endif
}
#if DRM_LINUX
#include <sys/sysproto.h>
MODULE_DEPEND(DRIVER_NAME, linux, 1, 1, 1);
#define LINUX_IOCTL_DRM_MIN 0x6400
#define LINUX_IOCTL_DRM_MAX 0x64ff
static linux_ioctl_function_t drm_linux_ioctl;
static struct linux_ioctl_handler drm_handler = {drm_linux_ioctl,
LINUX_IOCTL_DRM_MIN, LINUX_IOCTL_DRM_MAX};
/* The bits for in/out are switched on Linux */
#define LINUX_IOC_IN IOC_OUT
#define LINUX_IOC_OUT IOC_IN
static int
drm_linux_ioctl(DRM_STRUCTPROC *p, struct linux_ioctl_args* args)
{
int error;
int cmd = args->cmd;
args->cmd &= ~(LINUX_IOC_IN | LINUX_IOC_OUT);
if (cmd & LINUX_IOC_IN)
args->cmd |= IOC_IN;
if (cmd & LINUX_IOC_OUT)
args->cmd |= IOC_OUT;
error = ioctl(p, (struct ioctl_args *)args);
return error;
}
#endif /* DRM_LINUX */
static int
drm_modevent(module_t mod, int type, void *data)
{
switch (type) {
case MOD_LOAD:
TUNABLE_INT_FETCH("drm.debug", &drm_debug);
TUNABLE_INT_FETCH("drm.notyet", &drm_notyet);
break;
}
return (0);
}
static moduledata_t drm_mod = {
"drmn",
drm_modevent,
0
};
DECLARE_MODULE(drmn, drm_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
MODULE_VERSION(drmn, 1);
MODULE_DEPEND(drmn, agp, 1, 1, 1);
MODULE_DEPEND(drmn, pci, 1, 1, 1);
MODULE_DEPEND(drmn, mem, 1, 1, 1);
MODULE_DEPEND(drmn, iicbus, 1, 1, 1);