blob: ea4c9623458564d7a7cbc05cfd6a1982e131d6eb [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2012 Red Hat
* Copyright (c) 2015 - 2020 DisplayLink (UK) Ltd.
*
* Based on parts on udlfb.c:
* Copyright (C) 2009 its respective authors
*
* This file is subject to the terms and conditions of the GNU General Public
* License v2. See the file COPYING in the main directory of this archive for
* more details.
*/
#include <linux/version.h>
#if KERNEL_VERSION(5, 15, 0) <= LINUX_VERSION_CODE
#include <drm/drm_ioctl.h>
#include <drm/drm_file.h>
#include <drm/drm_drv.h>
#include <drm/drm_vblank.h>
#elif KERNEL_VERSION(5, 5, 0) <= LINUX_VERSION_CODE || defined(EL8)
#else
#include <drm/drmP.h>
#endif
#if KERNEL_VERSION(5, 1, 0) <= LINUX_VERSION_CODE || defined(EL8)
#include <drm/drm_probe_helper.h>
#endif
#if KERNEL_VERSION(5, 8, 0) <= LINUX_VERSION_CODE
#include <drm/drm_managed.h>
#endif
#include <drm/drm_atomic_helper.h>
#include "evdi_drm_drv.h"
#include "evdi_platform_drv.h"
#include "evdi_cursor.h"
#include "evdi_debug.h"
#include "evdi_drm.h"
static struct drm_driver driver;
struct drm_ioctl_desc evdi_painter_ioctls[] = {
DRM_IOCTL_DEF_DRV(EVDI_CONNECT, evdi_painter_connect_ioctl,
DRM_UNLOCKED),
DRM_IOCTL_DEF_DRV(EVDI_REQUEST_UPDATE,
evdi_painter_request_update_ioctl, DRM_UNLOCKED),
DRM_IOCTL_DEF_DRV(EVDI_GRABPIX, evdi_painter_grabpix_ioctl,
DRM_UNLOCKED),
DRM_IOCTL_DEF_DRV(EVDI_DDCCI_RESPONSE, evdi_painter_ddcci_response_ioctl,
DRM_UNLOCKED),
DRM_IOCTL_DEF_DRV(EVDI_ENABLE_CURSOR_EVENTS, evdi_painter_enable_cursor_events_ioctl,
DRM_UNLOCKED),
};
#if KERNEL_VERSION(5, 11, 0) <= LINUX_VERSION_CODE || defined(EL8)
#else
static const struct vm_operations_struct evdi_gem_vm_ops = {
.fault = evdi_gem_fault,
.open = drm_gem_vm_open,
.close = drm_gem_vm_close,
};
#endif
static const struct file_operations evdi_driver_fops = {
.owner = THIS_MODULE,
.open = drm_open,
.mmap = evdi_drm_gem_mmap,
.poll = drm_poll,
.read = drm_read,
.unlocked_ioctl = drm_ioctl,
.release = drm_release,
#ifdef CONFIG_COMPAT
.compat_ioctl = evdi_compat_ioctl,
#endif
.llseek = noop_llseek,
};
#if KERNEL_VERSION(5, 11, 0) <= LINUX_VERSION_CODE || defined(EL8)
#else
static int evdi_enable_vblank(__always_unused struct drm_device *dev,
__always_unused unsigned int pipe)
{
return 1;
}
static void evdi_disable_vblank(__always_unused struct drm_device *dev,
__always_unused unsigned int pipe)
{
}
#endif
static struct drm_driver driver = {
#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE || defined(EL8)
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
#else
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME
| DRIVER_ATOMIC,
#endif
.unload = evdi_drm_device_unload,
.open = evdi_driver_open,
.postclose = evdi_driver_postclose,
/* gem hooks */
#if KERNEL_VERSION(5, 11, 0) <= LINUX_VERSION_CODE || defined(EL8)
#elif KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE
.gem_free_object_unlocked = evdi_gem_free_object,
#else
.gem_free_object = evdi_gem_free_object,
#endif
#if KERNEL_VERSION(5, 11, 0) <= LINUX_VERSION_CODE || defined(EL8)
#else
.gem_vm_ops = &evdi_gem_vm_ops,
#endif
.dumb_create = evdi_dumb_create,
.dumb_map_offset = evdi_gem_mmap,
#if KERNEL_VERSION(5, 12, 0) <= LINUX_VERSION_CODE || defined(EL8)
#else
.dumb_destroy = drm_gem_dumb_destroy,
#endif
.ioctls = evdi_painter_ioctls,
.num_ioctls = ARRAY_SIZE(evdi_painter_ioctls),
.fops = &evdi_driver_fops,
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
.gem_prime_import = drm_gem_prime_import,
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
#if KERNEL_VERSION(5, 11, 0) <= LINUX_VERSION_CODE || defined(EL8)
#else
.preclose = evdi_driver_preclose,
.gem_prime_export = drm_gem_prime_export,
.gem_prime_get_sg_table = evdi_prime_get_sg_table,
.enable_vblank = evdi_enable_vblank,
.disable_vblank = evdi_disable_vblank,
#endif
.gem_prime_import_sg_table = evdi_prime_import_sg_table,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.date = DRIVER_DATE,
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
.patchlevel = DRIVER_PATCH,
};
#if KERNEL_VERSION(5, 8, 0) <= LINUX_VERSION_CODE
static void evdi_drm_device_release_cb(__always_unused struct drm_device *dev,
__always_unused void *ptr)
{
EVDI_INFO("Evdi drm_device removed.\n");
}
#endif
static int evdi_drm_device_setup(struct drm_device *dev)
{
struct evdi_device *evdi;
int ret;
EVDI_CHECKPT();
evdi = kzalloc(sizeof(struct evdi_device), GFP_KERNEL);
if (!evdi)
return -ENOMEM;
evdi->ddev = dev;
dev->dev_private = evdi;
evdi->dev_index = dev->primary->index;
evdi->cursor_events_enabled = false;
ret = evdi_cursor_init(&evdi->cursor);
if (ret)
goto err_free;
EVDI_CHECKPT();
evdi_modeset_init(dev);
#ifdef CONFIG_FB
ret = evdi_fbdev_init(dev);
if (ret)
goto err_cursor;
#endif /* CONFIG_FB */
ret = drm_vblank_init(dev, 1);
if (ret)
goto err_fb;
ret = evdi_painter_init(evdi);
if (ret)
goto err_fb;
drm_kms_helper_poll_init(dev);
#if KERNEL_VERSION(5, 8, 0) <= LINUX_VERSION_CODE
ret = drmm_add_action_or_reset(dev, evdi_drm_device_release_cb, NULL);
if (ret)
goto err_fb;
#endif
return 0;
err_fb:
#ifdef CONFIG_FB
evdi_fbdev_cleanup(dev);
err_cursor:
#endif /* CONFIG_FB */
evdi_cursor_free(evdi->cursor);
err_free:
EVDI_ERROR("Failed to setup drm device %d\n", ret);
kfree(evdi);
return ret;
}
void evdi_drm_device_unload(struct drm_device *dev)
{
struct evdi_device *evdi = dev->dev_private;
EVDI_CHECKPT();
drm_kms_helper_poll_fini(dev);
#ifdef CONFIG_FB
evdi_fbdev_unplug(dev);
#endif /* CONFIG_FB */
if (evdi->cursor)
evdi_cursor_free(evdi->cursor);
evdi_painter_cleanup(evdi->painter);
#ifdef CONFIG_FB
evdi_fbdev_cleanup(dev);
#endif /* CONFIG_FB */
evdi_modeset_cleanup(dev);
kfree(evdi);
}
int evdi_driver_open(struct drm_device *drm_dev, __always_unused struct drm_file *file)
{
struct evdi_device *evdi = drm_dev->dev_private;
char buf[100];
evdi_log_process(buf, sizeof(buf));
EVDI_INFO("(card%d) Opened by %s\n", evdi->dev_index, buf);
return 0;
}
static void evdi_driver_close(struct drm_device *drm_dev, struct drm_file *file)
{
struct evdi_device *evdi = drm_dev->dev_private;
EVDI_CHECKPT();
if (evdi)
evdi_painter_close(evdi, file);
}
void evdi_driver_preclose(struct drm_device *drm_dev, struct drm_file *file)
{
evdi_driver_close(drm_dev, file);
}
void evdi_driver_postclose(struct drm_device *drm_dev, struct drm_file *file)
{
struct evdi_device *evdi = drm_dev->dev_private;
char buf[100];
evdi_log_process(buf, sizeof(buf));
EVDI_INFO("(card%d) Closed by %s\n",
evdi->dev_index, buf);
evdi_driver_close(drm_dev, file);
}
struct drm_device *evdi_drm_device_create(struct device *parent)
{
struct drm_device *dev = NULL;
int ret;
dev = drm_dev_alloc(&driver, parent);
if (IS_ERR(dev))
return dev;
ret = evdi_drm_device_setup(dev);
if (ret)
goto err_free;
ret = drm_dev_register(dev, 0);
if (ret)
goto err_free;
return dev;
err_free:
drm_dev_put(dev);
return ERR_PTR(ret);
}
int evdi_drm_device_remove(struct drm_device *dev)
{
drm_dev_unplug(dev);
drm_atomic_helper_shutdown(dev);
drm_dev_put(dev);
return 0;
}