This tag contains the patches that add the new compute acceleration
subsystem, which is part of the DRM subsystem. The patches: - Add a new directory at drivers/accel. - Add a new major (261) for compute accelerators. - Add a new DRM minor type for compute accelerators. - Integrate the accel core code with DRM core code. - Add documentation for the accel subsystem. -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEE7TEboABC71LctBLFZR1NuKta54AFAmN8r2UACgkQZR1NuKta 54CJzwf/SkjfYoo8bLi1eedSSLZQZiNFtkqUqH0jho2UT5MG3yoPasXe/hovnf6+ SOE1fMO/VpfQPJ5xqjuHwUaGxXIFFi1vkMllhZrODljTP0dNjhgWc7WTTsKTj586 pMpYWA8bDfoF0SSGfEYbDi7qqiZFFVP6UeM4jvQgPhURNbLhF2dx69D3K1ZLvJJE 673ZzJkZcnZRdTjypCE2gCOcXpXclbNenFHqMkuaX6JfGu+8YPR0GGVWiBfo+FvO gNnxXDhBmYdn+Khzzxr+ssewOklHM+Moe2Iv20jZQgVsHot3c+bJx+7BPBHpg5Ib 4uqIrwMZENIQoH+FgYxSRMfEn0JI/w== =RCyB -----END PGP SIGNATURE----- Merge tag 'drm-accel-2022-11-22' of https://git.kernel.org/pub/scm/linux/kernel/git/ogabbay/accel into drm-next This tag contains the patches that add the new compute acceleration subsystem, which is part of the DRM subsystem. The patches: - Add a new directory at drivers/accel. - Add a new major (261) for compute accelerators. - Add a new DRM minor type for compute accelerators. - Integrate the accel core code with DRM core code. - Add documentation for the accel subsystem. Signed-off-by: Dave Airlie <airlied@redhat.com> some acks from the list (some are in the patch series): Acked-by: Daniel Stone <daniels@collabora.com> Acked-by: Sonal Santan <sonal.santan@amd.com> Acked-by: Maxime Ripard <maxime@cerno.tech> Acked-by: Jacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com> Tested-by: Jacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com> Acked-by: Alex Deucher <alexander.deucher@amd.com> Acked-by: Thomas Zimmermann <tzimmermann@suse.de> From: Oded Gabbay <ogabbay@kernel.org> Link: https://patchwork.freedesktop.org/patch/msgid/20221122112222.GA352082@ogabbay-vm-u20.habana-labs.com
This commit is contained in:
Коммит
795bd9bb21
|
@ -0,0 +1,17 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
====================
|
||||
Compute Accelerators
|
||||
====================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
introduction
|
||||
|
||||
.. only:: subproject and html
|
||||
|
||||
Indices
|
||||
=======
|
||||
|
||||
* :ref:`genindex`
|
|
@ -0,0 +1,110 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
============
|
||||
Introduction
|
||||
============
|
||||
|
||||
The Linux compute accelerators subsystem is designed to expose compute
|
||||
accelerators in a common way to user-space and provide a common set of
|
||||
functionality.
|
||||
|
||||
These devices can be either stand-alone ASICs or IP blocks inside an SoC/GPU.
|
||||
Although these devices are typically designed to accelerate
|
||||
Machine-Learning (ML) and/or Deep-Learning (DL) computations, the accel layer
|
||||
is not limited to handling these types of accelerators.
|
||||
|
||||
Typically, a compute accelerator will belong to one of the following
|
||||
categories:
|
||||
|
||||
- Edge AI - doing inference at an edge device. It can be an embedded ASIC/FPGA,
|
||||
or an IP inside a SoC (e.g. laptop web camera). These devices
|
||||
are typically configured using registers and can work with or without DMA.
|
||||
|
||||
- Inference data-center - single/multi user devices in a large server. This
|
||||
type of device can be stand-alone or an IP inside a SoC or a GPU. It will
|
||||
have on-board DRAM (to hold the DL topology), DMA engines and
|
||||
command submission queues (either kernel or user-space queues).
|
||||
It might also have an MMU to manage multiple users and might also enable
|
||||
virtualization (SR-IOV) to support multiple VMs on the same device. In
|
||||
addition, these devices will usually have some tools, such as profiler and
|
||||
debugger.
|
||||
|
||||
- Training data-center - Similar to Inference data-center cards, but typically
|
||||
have more computational power and memory b/w (e.g. HBM) and will likely have
|
||||
a method of scaling-up/out, i.e. connecting to other training cards inside
|
||||
the server or in other servers, respectively.
|
||||
|
||||
All these devices typically have different runtime user-space software stacks,
|
||||
that are tailored-made to their h/w. In addition, they will also probably
|
||||
include a compiler to generate programs to their custom-made computational
|
||||
engines. Typically, the common layer in user-space will be the DL frameworks,
|
||||
such as PyTorch and TensorFlow.
|
||||
|
||||
Sharing code with DRM
|
||||
=====================
|
||||
|
||||
Because this type of devices can be an IP inside GPUs or have similar
|
||||
characteristics as those of GPUs, the accel subsystem will use the
|
||||
DRM subsystem's code and functionality. i.e. the accel core code will
|
||||
be part of the DRM subsystem and an accel device will be a new type of DRM
|
||||
device.
|
||||
|
||||
This will allow us to leverage the extensive DRM code-base and
|
||||
collaborate with DRM developers that have experience with this type of
|
||||
devices. In addition, new features that will be added for the accelerator
|
||||
drivers can be of use to GPU drivers as well.
|
||||
|
||||
Differentiation from GPUs
|
||||
=========================
|
||||
|
||||
Because we want to prevent the extensive user-space graphic software stack
|
||||
from trying to use an accelerator as a GPU, the compute accelerators will be
|
||||
differentiated from GPUs by using a new major number and new device char files.
|
||||
|
||||
Furthermore, the drivers will be located in a separate place in the kernel
|
||||
tree - drivers/accel/.
|
||||
|
||||
The accelerator devices will be exposed to the user space with the dedicated
|
||||
261 major number and will have the following convention:
|
||||
|
||||
- device char files - /dev/accel/accel*
|
||||
- sysfs - /sys/class/accel/accel*/
|
||||
- debugfs - /sys/kernel/debug/accel/accel*/
|
||||
|
||||
Getting Started
|
||||
===============
|
||||
|
||||
First, read the DRM documentation at Documentation/gpu/index.rst.
|
||||
Not only it will explain how to write a new DRM driver but it will also
|
||||
contain all the information on how to contribute, the Code Of Conduct and
|
||||
what is the coding style/documentation. All of that is the same for the
|
||||
accel subsystem.
|
||||
|
||||
Second, make sure the kernel is configured with CONFIG_DRM_ACCEL.
|
||||
|
||||
To expose your device as an accelerator, two changes are needed to
|
||||
be done in your driver (as opposed to a standard DRM driver):
|
||||
|
||||
- Add the DRIVER_COMPUTE_ACCEL feature flag in your drm_driver's
|
||||
driver_features field. It is important to note that this driver feature is
|
||||
mutually exclusive with DRIVER_RENDER and DRIVER_MODESET. Devices that want
|
||||
to expose both graphics and compute device char files should be handled by
|
||||
two drivers that are connected using the auxiliary bus framework.
|
||||
|
||||
- Change the open callback in your driver fops structure to accel_open().
|
||||
Alternatively, your driver can use DEFINE_DRM_ACCEL_FOPS macro to easily
|
||||
set the correct function operations pointers structure.
|
||||
|
||||
External References
|
||||
===================
|
||||
|
||||
email threads
|
||||
-------------
|
||||
|
||||
* `Initial discussion on the New subsystem for acceleration devices <https://lkml.org/lkml/2022/7/31/83>`_ - Oded Gabbay (2022)
|
||||
* `patch-set to add the new subsystem <https://lkml.org/lkml/2022/10/22/544>`_ - Oded Gabbay (2022)
|
||||
|
||||
Conference talks
|
||||
----------------
|
||||
|
||||
* `LPC 2022 Accelerators BOF outcomes summary <https://airlied.blogspot.com/2022/09/accelerators-bof-outcomes-summary.html>`_ - Dave Airlie (2022)
|
|
@ -3080,6 +3080,11 @@
|
|||
...
|
||||
255 = /dev/osd255 256th OSD Device
|
||||
|
||||
261 char Compute Acceleration Devices
|
||||
0 = /dev/accel/accel0 First acceleration device
|
||||
1 = /dev/accel/accel1 Second acceleration device
|
||||
...
|
||||
|
||||
384-511 char RESERVED FOR DYNAMIC ASSIGNMENT
|
||||
Character devices that request a dynamic allocation of major
|
||||
number will take numbers starting from 511 and downward,
|
||||
|
|
|
@ -43,6 +43,7 @@ needed).
|
|||
input/index
|
||||
hwmon/index
|
||||
gpu/index
|
||||
accel/index
|
||||
security/index
|
||||
sound/index
|
||||
crypto/index
|
||||
|
|
|
@ -6836,6 +6836,15 @@ F: include/drm/drm*
|
|||
F: include/linux/vga*
|
||||
F: include/uapi/drm/drm*
|
||||
|
||||
DRM COMPUTE ACCELERATORS DRIVERS AND FRAMEWORK
|
||||
M: Oded Gabbay <ogabbay@kernel.org>
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
S: Maintained
|
||||
C: irc://irc.oftc.net/dri-devel
|
||||
T: git https://git.kernel.org/pub/scm/linux/kernel/git/ogabbay/accel.git
|
||||
F: Documentation/accel/
|
||||
F: drivers/accel/
|
||||
|
||||
DRM DRIVERS FOR ALLWINNER A10
|
||||
M: Maxime Ripard <mripard@kernel.org>
|
||||
M: Chen-Yu Tsai <wens@csie.org>
|
||||
|
|
|
@ -99,6 +99,8 @@ source "drivers/media/Kconfig"
|
|||
|
||||
source "drivers/video/Kconfig"
|
||||
|
||||
source "drivers/accel/Kconfig"
|
||||
|
||||
source "sound/Kconfig"
|
||||
|
||||
source "drivers/hid/Kconfig"
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# Compute Acceleration device configuration
|
||||
#
|
||||
# This framework provides support for compute acceleration devices, such
|
||||
# as, but not limited to, Machine-Learning and Deep-Learning acceleration
|
||||
# devices
|
||||
#
|
||||
menuconfig DRM_ACCEL
|
||||
bool "Compute Acceleration Framework"
|
||||
depends on DRM
|
||||
help
|
||||
Framework for device drivers of compute acceleration devices, such
|
||||
as, but not limited to, Machine-Learning and Deep-Learning
|
||||
acceleration devices.
|
||||
If you say Y here, you need to select the module that's right for
|
||||
your acceleration device from the list below.
|
||||
This framework is integrated with the DRM subsystem as compute
|
||||
accelerators and GPUs share a lot in common and can use almost the
|
||||
same infrastructure code.
|
||||
Having said that, acceleration devices will have a different
|
||||
major number than GPUs, and will be exposed to user-space using
|
||||
different device files, called accel/accel* (in /dev, sysfs
|
||||
and debugfs).
|
|
@ -0,0 +1,323 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/*
|
||||
* Copyright 2022 HabanaLabs, Ltd.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/idr.h>
|
||||
|
||||
#include <drm/drm_accel.h>
|
||||
#include <drm/drm_debugfs.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_file.h>
|
||||
#include <drm/drm_ioctl.h>
|
||||
#include <drm/drm_print.h>
|
||||
|
||||
static DEFINE_SPINLOCK(accel_minor_lock);
|
||||
static struct idr accel_minors_idr;
|
||||
|
||||
static struct dentry *accel_debugfs_root;
|
||||
static struct class *accel_class;
|
||||
|
||||
static struct device_type accel_sysfs_device_minor = {
|
||||
.name = "accel_minor"
|
||||
};
|
||||
|
||||
static char *accel_devnode(struct device *dev, umode_t *mode)
|
||||
{
|
||||
return kasprintf(GFP_KERNEL, "accel/%s", dev_name(dev));
|
||||
}
|
||||
|
||||
static int accel_sysfs_init(void)
|
||||
{
|
||||
accel_class = class_create(THIS_MODULE, "accel");
|
||||
if (IS_ERR(accel_class))
|
||||
return PTR_ERR(accel_class);
|
||||
|
||||
accel_class->devnode = accel_devnode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void accel_sysfs_destroy(void)
|
||||
{
|
||||
if (IS_ERR_OR_NULL(accel_class))
|
||||
return;
|
||||
class_destroy(accel_class);
|
||||
accel_class = NULL;
|
||||
}
|
||||
|
||||
static int accel_name_info(struct seq_file *m, void *data)
|
||||
{
|
||||
struct drm_info_node *node = (struct drm_info_node *) m->private;
|
||||
struct drm_minor *minor = node->minor;
|
||||
struct drm_device *dev = minor->dev;
|
||||
struct drm_master *master;
|
||||
|
||||
mutex_lock(&dev->master_mutex);
|
||||
master = dev->master;
|
||||
seq_printf(m, "%s", dev->driver->name);
|
||||
if (dev->dev)
|
||||
seq_printf(m, " dev=%s", dev_name(dev->dev));
|
||||
if (master && master->unique)
|
||||
seq_printf(m, " master=%s", master->unique);
|
||||
if (dev->unique)
|
||||
seq_printf(m, " unique=%s", dev->unique);
|
||||
seq_puts(m, "\n");
|
||||
mutex_unlock(&dev->master_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_info_list accel_debugfs_list[] = {
|
||||
{"name", accel_name_info, 0}
|
||||
};
|
||||
#define ACCEL_DEBUGFS_ENTRIES ARRAY_SIZE(accel_debugfs_list)
|
||||
|
||||
/**
|
||||
* accel_debugfs_init() - Initialize debugfs for accel minor
|
||||
* @minor: Pointer to the drm_minor instance.
|
||||
* @minor_id: The minor's id
|
||||
*
|
||||
* This function initializes the drm minor's debugfs members and creates
|
||||
* a root directory for the minor in debugfs. It also creates common files
|
||||
* for accelerators and calls the driver's debugfs init callback.
|
||||
*/
|
||||
void accel_debugfs_init(struct drm_minor *minor, int minor_id)
|
||||
{
|
||||
struct drm_device *dev = minor->dev;
|
||||
char name[64];
|
||||
|
||||
INIT_LIST_HEAD(&minor->debugfs_list);
|
||||
mutex_init(&minor->debugfs_lock);
|
||||
sprintf(name, "%d", minor_id);
|
||||
minor->debugfs_root = debugfs_create_dir(name, accel_debugfs_root);
|
||||
|
||||
drm_debugfs_create_files(accel_debugfs_list, ACCEL_DEBUGFS_ENTRIES,
|
||||
minor->debugfs_root, minor);
|
||||
|
||||
if (dev->driver->debugfs_init)
|
||||
dev->driver->debugfs_init(minor);
|
||||
}
|
||||
|
||||
/**
|
||||
* accel_set_device_instance_params() - Set some device parameters for accel device
|
||||
* @kdev: Pointer to the device instance.
|
||||
* @index: The minor's index
|
||||
*
|
||||
* This function creates the dev_t of the device using the accel major and
|
||||
* the device's minor number. In addition, it sets the class and type of the
|
||||
* device instance to the accel sysfs class and device type, respectively.
|
||||
*/
|
||||
void accel_set_device_instance_params(struct device *kdev, int index)
|
||||
{
|
||||
kdev->devt = MKDEV(ACCEL_MAJOR, index);
|
||||
kdev->class = accel_class;
|
||||
kdev->type = &accel_sysfs_device_minor;
|
||||
}
|
||||
|
||||
/**
|
||||
* accel_minor_alloc() - Allocates a new accel minor
|
||||
*
|
||||
* This function access the accel minors idr and allocates from it
|
||||
* a new id to represent a new accel minor
|
||||
*
|
||||
* Return: A new id on success or error code in case idr_alloc failed
|
||||
*/
|
||||
int accel_minor_alloc(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
int r;
|
||||
|
||||
spin_lock_irqsave(&accel_minor_lock, flags);
|
||||
r = idr_alloc(&accel_minors_idr, NULL, 0, ACCEL_MAX_MINORS, GFP_NOWAIT);
|
||||
spin_unlock_irqrestore(&accel_minor_lock, flags);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* accel_minor_remove() - Remove an accel minor
|
||||
* @index: The minor id to remove.
|
||||
*
|
||||
* This function access the accel minors idr and removes from
|
||||
* it the member with the id that is passed to this function.
|
||||
*/
|
||||
void accel_minor_remove(int index)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&accel_minor_lock, flags);
|
||||
idr_remove(&accel_minors_idr, index);
|
||||
spin_unlock_irqrestore(&accel_minor_lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* accel_minor_replace() - Replace minor pointer in accel minors idr.
|
||||
* @minor: Pointer to the new minor.
|
||||
* @index: The minor id to replace.
|
||||
*
|
||||
* This function access the accel minors idr structure and replaces the pointer
|
||||
* that is associated with an existing id. Because the minor pointer can be
|
||||
* NULL, we need to explicitly pass the index.
|
||||
*
|
||||
* Return: 0 for success, negative value for error
|
||||
*/
|
||||
void accel_minor_replace(struct drm_minor *minor, int index)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&accel_minor_lock, flags);
|
||||
idr_replace(&accel_minors_idr, minor, index);
|
||||
spin_unlock_irqrestore(&accel_minor_lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Looks up the given minor-ID and returns the respective DRM-minor object. The
|
||||
* refence-count of the underlying device is increased so you must release this
|
||||
* object with accel_minor_release().
|
||||
*
|
||||
* The object can be only a drm_minor that represents an accel device.
|
||||
*
|
||||
* As long as you hold this minor, it is guaranteed that the object and the
|
||||
* minor->dev pointer will stay valid! However, the device may get unplugged and
|
||||
* unregistered while you hold the minor.
|
||||
*/
|
||||
static struct drm_minor *accel_minor_acquire(unsigned int minor_id)
|
||||
{
|
||||
struct drm_minor *minor;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&accel_minor_lock, flags);
|
||||
minor = idr_find(&accel_minors_idr, minor_id);
|
||||
if (minor)
|
||||
drm_dev_get(minor->dev);
|
||||
spin_unlock_irqrestore(&accel_minor_lock, flags);
|
||||
|
||||
if (!minor) {
|
||||
return ERR_PTR(-ENODEV);
|
||||
} else if (drm_dev_is_unplugged(minor->dev)) {
|
||||
drm_dev_put(minor->dev);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
return minor;
|
||||
}
|
||||
|
||||
static void accel_minor_release(struct drm_minor *minor)
|
||||
{
|
||||
drm_dev_put(minor->dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* accel_open - open method for ACCEL file
|
||||
* @inode: device inode
|
||||
* @filp: file pointer.
|
||||
*
|
||||
* This function must be used by drivers as their &file_operations.open method.
|
||||
* It looks up the correct ACCEL device and instantiates all the per-file
|
||||
* resources for it. It also calls the &drm_driver.open driver callback.
|
||||
*
|
||||
* Return: 0 on success or negative errno value on failure.
|
||||
*/
|
||||
int accel_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct drm_device *dev;
|
||||
struct drm_minor *minor;
|
||||
int retcode;
|
||||
|
||||
minor = accel_minor_acquire(iminor(inode));
|
||||
if (IS_ERR(minor))
|
||||
return PTR_ERR(minor);
|
||||
|
||||
dev = minor->dev;
|
||||
|
||||
atomic_fetch_inc(&dev->open_count);
|
||||
|
||||
/* share address_space across all char-devs of a single device */
|
||||
filp->f_mapping = dev->anon_inode->i_mapping;
|
||||
|
||||
retcode = drm_open_helper(filp, minor);
|
||||
if (retcode)
|
||||
goto err_undo;
|
||||
|
||||
return 0;
|
||||
|
||||
err_undo:
|
||||
atomic_dec(&dev->open_count);
|
||||
accel_minor_release(minor);
|
||||
return retcode;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(accel_open);
|
||||
|
||||
static int accel_stub_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
const struct file_operations *new_fops;
|
||||
struct drm_minor *minor;
|
||||
int err;
|
||||
|
||||
minor = accel_minor_acquire(iminor(inode));
|
||||
if (IS_ERR(minor))
|
||||
return PTR_ERR(minor);
|
||||
|
||||
new_fops = fops_get(minor->dev->driver->fops);
|
||||
if (!new_fops) {
|
||||
err = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
replace_fops(filp, new_fops);
|
||||
if (filp->f_op->open)
|
||||
err = filp->f_op->open(inode, filp);
|
||||
else
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
accel_minor_release(minor);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct file_operations accel_stub_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = accel_stub_open,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
void accel_core_exit(void)
|
||||
{
|
||||
unregister_chrdev(ACCEL_MAJOR, "accel");
|
||||
debugfs_remove(accel_debugfs_root);
|
||||
accel_sysfs_destroy();
|
||||
idr_destroy(&accel_minors_idr);
|
||||
}
|
||||
|
||||
int __init accel_core_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
idr_init(&accel_minors_idr);
|
||||
|
||||
ret = accel_sysfs_init();
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("Cannot create ACCEL class: %d\n", ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
accel_debugfs_root = debugfs_create_dir("accel", NULL);
|
||||
|
||||
ret = register_chrdev(ACCEL_MAJOR, "accel", &accel_stub_fops);
|
||||
if (ret < 0)
|
||||
DRM_ERROR("Cannot register ACCEL major: %d\n", ret);
|
||||
|
||||
error:
|
||||
/*
|
||||
* Any cleanup due to errors will be done in drm_core_exit() that
|
||||
* will call accel_core_exit()
|
||||
*/
|
||||
return ret;
|
||||
}
|
|
@ -70,6 +70,7 @@ drm-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
|
|||
drm-$(CONFIG_DRM_PRIVACY_SCREEN) += \
|
||||
drm_privacy_screen.o \
|
||||
drm_privacy_screen_x86.o
|
||||
drm-$(CONFIG_DRM_ACCEL) += ../../accel/drm_accel.o
|
||||
obj-$(CONFIG_DRM) += drm.o
|
||||
|
||||
obj-$(CONFIG_DRM_PANEL_ORIENTATION_QUIRKS) += drm_panel_orientation_quirks.o
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/srcu.h>
|
||||
|
||||
#include <drm/drm_accel.h>
|
||||
#include <drm/drm_cache.h>
|
||||
#include <drm/drm_client.h>
|
||||
#include <drm/drm_color_mgmt.h>
|
||||
|
@ -90,6 +91,8 @@ static struct drm_minor **drm_minor_get_slot(struct drm_device *dev,
|
|||
return &dev->primary;
|
||||
case DRM_MINOR_RENDER:
|
||||
return &dev->render;
|
||||
case DRM_MINOR_ACCEL:
|
||||
return &dev->accel;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
@ -104,9 +107,13 @@ static void drm_minor_alloc_release(struct drm_device *dev, void *data)
|
|||
|
||||
put_device(minor->kdev);
|
||||
|
||||
spin_lock_irqsave(&drm_minor_lock, flags);
|
||||
idr_remove(&drm_minors_idr, minor->index);
|
||||
spin_unlock_irqrestore(&drm_minor_lock, flags);
|
||||
if (minor->type == DRM_MINOR_ACCEL) {
|
||||
accel_minor_remove(minor->index);
|
||||
} else {
|
||||
spin_lock_irqsave(&drm_minor_lock, flags);
|
||||
idr_remove(&drm_minors_idr, minor->index);
|
||||
spin_unlock_irqrestore(&drm_minor_lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
static int drm_minor_alloc(struct drm_device *dev, unsigned int type)
|
||||
|
@ -123,13 +130,17 @@ static int drm_minor_alloc(struct drm_device *dev, unsigned int type)
|
|||
minor->dev = dev;
|
||||
|
||||
idr_preload(GFP_KERNEL);
|
||||
spin_lock_irqsave(&drm_minor_lock, flags);
|
||||
r = idr_alloc(&drm_minors_idr,
|
||||
NULL,
|
||||
64 * type,
|
||||
64 * (type + 1),
|
||||
GFP_NOWAIT);
|
||||
spin_unlock_irqrestore(&drm_minor_lock, flags);
|
||||
if (type == DRM_MINOR_ACCEL) {
|
||||
r = accel_minor_alloc();
|
||||
} else {
|
||||
spin_lock_irqsave(&drm_minor_lock, flags);
|
||||
r = idr_alloc(&drm_minors_idr,
|
||||
NULL,
|
||||
64 * type,
|
||||
64 * (type + 1),
|
||||
GFP_NOWAIT);
|
||||
spin_unlock_irqrestore(&drm_minor_lock, flags);
|
||||
}
|
||||
idr_preload_end();
|
||||
|
||||
if (r < 0)
|
||||
|
@ -161,10 +172,14 @@ static int drm_minor_register(struct drm_device *dev, unsigned int type)
|
|||
if (!minor)
|
||||
return 0;
|
||||
|
||||
ret = drm_debugfs_init(minor, minor->index, drm_debugfs_root);
|
||||
if (ret) {
|
||||
DRM_ERROR("DRM: Failed to initialize /sys/kernel/debug/dri.\n");
|
||||
goto err_debugfs;
|
||||
if (minor->type == DRM_MINOR_ACCEL) {
|
||||
accel_debugfs_init(minor, minor->index);
|
||||
} else {
|
||||
ret = drm_debugfs_init(minor, minor->index, drm_debugfs_root);
|
||||
if (ret) {
|
||||
DRM_ERROR("DRM: Failed to initialize /sys/kernel/debug/dri.\n");
|
||||
goto err_debugfs;
|
||||
}
|
||||
}
|
||||
|
||||
ret = device_add(minor->kdev);
|
||||
|
@ -172,9 +187,13 @@ static int drm_minor_register(struct drm_device *dev, unsigned int type)
|
|||
goto err_debugfs;
|
||||
|
||||
/* replace NULL with @minor so lookups will succeed from now on */
|
||||
spin_lock_irqsave(&drm_minor_lock, flags);
|
||||
idr_replace(&drm_minors_idr, minor, minor->index);
|
||||
spin_unlock_irqrestore(&drm_minor_lock, flags);
|
||||
if (minor->type == DRM_MINOR_ACCEL) {
|
||||
accel_minor_replace(minor, minor->index);
|
||||
} else {
|
||||
spin_lock_irqsave(&drm_minor_lock, flags);
|
||||
idr_replace(&drm_minors_idr, minor, minor->index);
|
||||
spin_unlock_irqrestore(&drm_minor_lock, flags);
|
||||
}
|
||||
|
||||
DRM_DEBUG("new minor registered %d\n", minor->index);
|
||||
return 0;
|
||||
|
@ -194,9 +213,13 @@ static void drm_minor_unregister(struct drm_device *dev, unsigned int type)
|
|||
return;
|
||||
|
||||
/* replace @minor with NULL so lookups will fail from now on */
|
||||
spin_lock_irqsave(&drm_minor_lock, flags);
|
||||
idr_replace(&drm_minors_idr, NULL, minor->index);
|
||||
spin_unlock_irqrestore(&drm_minor_lock, flags);
|
||||
if (minor->type == DRM_MINOR_ACCEL) {
|
||||
accel_minor_replace(NULL, minor->index);
|
||||
} else {
|
||||
spin_lock_irqsave(&drm_minor_lock, flags);
|
||||
idr_replace(&drm_minors_idr, NULL, minor->index);
|
||||
spin_unlock_irqrestore(&drm_minor_lock, flags);
|
||||
}
|
||||
|
||||
device_del(minor->kdev);
|
||||
dev_set_drvdata(minor->kdev, NULL); /* safety belt */
|
||||
|
@ -603,6 +626,13 @@ static int drm_dev_init(struct drm_device *dev,
|
|||
/* no per-device feature limits by default */
|
||||
dev->driver_features = ~0u;
|
||||
|
||||
if (drm_core_check_feature(dev, DRIVER_COMPUTE_ACCEL) &&
|
||||
(drm_core_check_feature(dev, DRIVER_RENDER) ||
|
||||
drm_core_check_feature(dev, DRIVER_MODESET))) {
|
||||
DRM_ERROR("DRM driver can't be both a compute acceleration and graphics driver\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
drm_legacy_init_members(dev);
|
||||
INIT_LIST_HEAD(&dev->filelist);
|
||||
INIT_LIST_HEAD(&dev->filelist_internal);
|
||||
|
@ -628,16 +658,22 @@ static int drm_dev_init(struct drm_device *dev,
|
|||
|
||||
dev->anon_inode = inode;
|
||||
|
||||
if (drm_core_check_feature(dev, DRIVER_RENDER)) {
|
||||
ret = drm_minor_alloc(dev, DRM_MINOR_RENDER);
|
||||
if (drm_core_check_feature(dev, DRIVER_COMPUTE_ACCEL)) {
|
||||
ret = drm_minor_alloc(dev, DRM_MINOR_ACCEL);
|
||||
if (ret)
|
||||
goto err;
|
||||
} else {
|
||||
if (drm_core_check_feature(dev, DRIVER_RENDER)) {
|
||||
ret = drm_minor_alloc(dev, DRM_MINOR_RENDER);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = drm_minor_alloc(dev, DRM_MINOR_PRIMARY);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = drm_minor_alloc(dev, DRM_MINOR_PRIMARY);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = drm_legacy_create_map_hash(dev);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
@ -883,6 +919,10 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags)
|
|||
if (ret)
|
||||
goto err_minors;
|
||||
|
||||
ret = drm_minor_register(dev, DRM_MINOR_ACCEL);
|
||||
if (ret)
|
||||
goto err_minors;
|
||||
|
||||
ret = create_compat_control_link(dev);
|
||||
if (ret)
|
||||
goto err_minors;
|
||||
|
@ -902,12 +942,13 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags)
|
|||
driver->name, driver->major, driver->minor,
|
||||
driver->patchlevel, driver->date,
|
||||
dev->dev ? dev_name(dev->dev) : "virtual device",
|
||||
dev->primary->index);
|
||||
dev->primary ? dev->primary->index : dev->accel->index);
|
||||
|
||||
goto out_unlock;
|
||||
|
||||
err_minors:
|
||||
remove_compat_control_link(dev);
|
||||
drm_minor_unregister(dev, DRM_MINOR_ACCEL);
|
||||
drm_minor_unregister(dev, DRM_MINOR_PRIMARY);
|
||||
drm_minor_unregister(dev, DRM_MINOR_RENDER);
|
||||
out_unlock:
|
||||
|
@ -950,6 +991,7 @@ void drm_dev_unregister(struct drm_device *dev)
|
|||
drm_legacy_rmmaps(dev);
|
||||
|
||||
remove_compat_control_link(dev);
|
||||
drm_minor_unregister(dev, DRM_MINOR_ACCEL);
|
||||
drm_minor_unregister(dev, DRM_MINOR_PRIMARY);
|
||||
drm_minor_unregister(dev, DRM_MINOR_RENDER);
|
||||
}
|
||||
|
@ -1034,6 +1076,7 @@ static const struct file_operations drm_stub_fops = {
|
|||
static void drm_core_exit(void)
|
||||
{
|
||||
drm_privacy_screen_lookup_exit();
|
||||
accel_core_exit();
|
||||
unregister_chrdev(DRM_MAJOR, "drm");
|
||||
debugfs_remove(drm_debugfs_root);
|
||||
drm_sysfs_destroy();
|
||||
|
@ -1061,6 +1104,10 @@ static int __init drm_core_init(void)
|
|||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = accel_core_init();
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
drm_privacy_screen_lookup_init();
|
||||
|
||||
drm_core_init_complete = true;
|
||||
|
|
|
@ -326,7 +326,7 @@ static int drm_cpu_valid(void)
|
|||
* Creates and initializes a drm_file structure for the file private data in \p
|
||||
* filp and add it into the double linked list in \p dev.
|
||||
*/
|
||||
static int drm_open_helper(struct file *filp, struct drm_minor *minor)
|
||||
int drm_open_helper(struct file *filp, struct drm_minor *minor)
|
||||
{
|
||||
struct drm_device *dev = minor->dev;
|
||||
struct drm_file *priv;
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/kdev_t.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <drm/drm_accel.h>
|
||||
#include <drm/drm_connector.h>
|
||||
#include <drm/drm_device.h>
|
||||
#include <drm/drm_file.h>
|
||||
|
@ -471,19 +472,26 @@ struct device *drm_sysfs_minor_alloc(struct drm_minor *minor)
|
|||
struct device *kdev;
|
||||
int r;
|
||||
|
||||
if (minor->type == DRM_MINOR_RENDER)
|
||||
minor_str = "renderD%d";
|
||||
else
|
||||
minor_str = "card%d";
|
||||
|
||||
kdev = kzalloc(sizeof(*kdev), GFP_KERNEL);
|
||||
if (!kdev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
device_initialize(kdev);
|
||||
kdev->devt = MKDEV(DRM_MAJOR, minor->index);
|
||||
kdev->class = drm_class;
|
||||
kdev->type = &drm_sysfs_device_minor;
|
||||
|
||||
if (minor->type == DRM_MINOR_ACCEL) {
|
||||
minor_str = "accel%d";
|
||||
accel_set_device_instance_params(kdev, minor->index);
|
||||
} else {
|
||||
if (minor->type == DRM_MINOR_RENDER)
|
||||
minor_str = "renderD%d";
|
||||
else
|
||||
minor_str = "card%d";
|
||||
|
||||
kdev->devt = MKDEV(DRM_MAJOR, minor->index);
|
||||
kdev->class = drm_class;
|
||||
kdev->type = &drm_sysfs_device_minor;
|
||||
}
|
||||
|
||||
kdev->parent = minor->dev->dev;
|
||||
kdev->release = drm_sysfs_release;
|
||||
dev_set_drvdata(kdev, minor);
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* Copyright 2022 HabanaLabs, Ltd.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DRM_ACCEL_H_
|
||||
#define DRM_ACCEL_H_
|
||||
|
||||
#include <drm/drm_file.h>
|
||||
|
||||
#define ACCEL_MAJOR 261
|
||||
#define ACCEL_MAX_MINORS 256
|
||||
|
||||
/**
|
||||
* DRM_ACCEL_FOPS - Default drm accelerators file operations
|
||||
*
|
||||
* This macro provides a shorthand for setting the accelerator file ops in the
|
||||
* &file_operations structure. If all you need are the default ops, use
|
||||
* DEFINE_DRM_ACCEL_FOPS instead.
|
||||
*/
|
||||
#define DRM_ACCEL_FOPS \
|
||||
.open = accel_open,\
|
||||
.release = drm_release,\
|
||||
.unlocked_ioctl = drm_ioctl,\
|
||||
.compat_ioctl = drm_compat_ioctl,\
|
||||
.poll = drm_poll,\
|
||||
.read = drm_read,\
|
||||
.llseek = noop_llseek
|
||||
|
||||
/**
|
||||
* DEFINE_DRM_ACCEL_FOPS() - macro to generate file operations for accelerators drivers
|
||||
* @name: name for the generated structure
|
||||
*
|
||||
* This macro autogenerates a suitable &struct file_operations for accelerators based
|
||||
* drivers, which can be assigned to &drm_driver.fops. Note that this structure
|
||||
* cannot be shared between drivers, because it contains a reference to the
|
||||
* current module using THIS_MODULE.
|
||||
*
|
||||
* Note that the declaration is already marked as static - if you need a
|
||||
* non-static version of this you're probably doing it wrong and will break the
|
||||
* THIS_MODULE reference by accident.
|
||||
*/
|
||||
#define DEFINE_DRM_ACCEL_FOPS(name) \
|
||||
static const struct file_operations name = {\
|
||||
.owner = THIS_MODULE,\
|
||||
DRM_ACCEL_FOPS,\
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_ACCEL)
|
||||
|
||||
void accel_core_exit(void);
|
||||
int accel_core_init(void);
|
||||
void accel_minor_remove(int index);
|
||||
int accel_minor_alloc(void);
|
||||
void accel_minor_replace(struct drm_minor *minor, int index);
|
||||
void accel_set_device_instance_params(struct device *kdev, int index);
|
||||
int accel_open(struct inode *inode, struct file *filp);
|
||||
void accel_debugfs_init(struct drm_minor *minor, int minor_id);
|
||||
|
||||
#else
|
||||
|
||||
static inline void accel_core_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int __init accel_core_init(void)
|
||||
{
|
||||
/* Return 0 to allow drm_core_init to complete successfully */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void accel_minor_remove(int index)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int accel_minor_alloc(void)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline void accel_minor_replace(struct drm_minor *minor, int index)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void accel_set_device_instance_params(struct device *kdev, int index)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void accel_debugfs_init(struct drm_minor *minor, int minor_id)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* IS_ENABLED(CONFIG_DRM_ACCEL) */
|
||||
|
||||
#endif /* DRM_ACCEL_H_ */
|
|
@ -93,6 +93,9 @@ struct drm_device {
|
|||
/** @render: Render node */
|
||||
struct drm_minor *render;
|
||||
|
||||
/** @accel: Compute Acceleration node */
|
||||
struct drm_minor *accel;
|
||||
|
||||
/**
|
||||
* @registered:
|
||||
*
|
||||
|
|
|
@ -96,6 +96,14 @@ enum drm_driver_feature {
|
|||
* synchronization of command submission.
|
||||
*/
|
||||
DRIVER_SYNCOBJ_TIMELINE = BIT(6),
|
||||
/**
|
||||
* @DRIVER_COMPUTE_ACCEL:
|
||||
*
|
||||
* Driver supports compute acceleration devices. This flag is mutually exclusive with
|
||||
* @DRIVER_RENDER and @DRIVER_MODESET. Devices that support both graphics and compute
|
||||
* acceleration should be handled by two drivers that are connected using auxiliary bus.
|
||||
*/
|
||||
DRIVER_COMPUTE_ACCEL = BIT(7),
|
||||
|
||||
/* IMPORTANT: Below are all the legacy flags, add new ones above. */
|
||||
|
||||
|
|
|
@ -51,11 +51,15 @@ struct file;
|
|||
|
||||
/* Note that the order of this enum is ABI (it determines
|
||||
* /dev/dri/renderD* numbers).
|
||||
*
|
||||
* Setting DRM_MINOR_ACCEL to 32 gives enough space for more drm minors to
|
||||
* be implemented before we hit any future
|
||||
*/
|
||||
enum drm_minor_type {
|
||||
DRM_MINOR_PRIMARY,
|
||||
DRM_MINOR_CONTROL,
|
||||
DRM_MINOR_RENDER,
|
||||
DRM_MINOR_ACCEL = 32,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -70,7 +74,7 @@ enum drm_minor_type {
|
|||
struct drm_minor {
|
||||
/* private: */
|
||||
int index; /* Minor device number */
|
||||
int type; /* Control or render */
|
||||
int type; /* Control or render or accel */
|
||||
struct device *kdev; /* Linux device */
|
||||
struct drm_device *dev;
|
||||
|
||||
|
@ -397,7 +401,22 @@ static inline bool drm_is_render_client(const struct drm_file *file_priv)
|
|||
return file_priv->minor->type == DRM_MINOR_RENDER;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_is_accel_client - is this an open file of the compute acceleration node
|
||||
* @file_priv: DRM file
|
||||
*
|
||||
* Returns true if this is an open file of the compute acceleration node, i.e.
|
||||
* &drm_file.minor of @file_priv is a accel minor.
|
||||
*
|
||||
* See also the :ref:`section on accel nodes <drm_accel_node>`.
|
||||
*/
|
||||
static inline bool drm_is_accel_client(const struct drm_file *file_priv)
|
||||
{
|
||||
return file_priv->minor->type == DRM_MINOR_ACCEL;
|
||||
}
|
||||
|
||||
int drm_open(struct inode *inode, struct file *filp);
|
||||
int drm_open_helper(struct file *filp, struct drm_minor *minor);
|
||||
ssize_t drm_read(struct file *filp, char __user *buffer,
|
||||
size_t count, loff_t *offset);
|
||||
int drm_release(struct inode *inode, struct file *filp);
|
||||
|
|
Загрузка…
Ссылка в новой задаче