xref: /dflybsd-src/sys/dev/drm/drm_dp_aux_dev.c (revision 1e12ee3baa16120663cde9c6c4c8e92b69b00794)
18621f407SFrançois Tigeot /*
28621f407SFrançois Tigeot  * Copyright © 2015 Intel Corporation
38621f407SFrançois Tigeot  *
48621f407SFrançois Tigeot  * Permission is hereby granted, free of charge, to any person obtaining a
58621f407SFrançois Tigeot  * copy of this software and associated documentation files (the "Software"),
68621f407SFrançois Tigeot  * to deal in the Software without restriction, including without limitation
78621f407SFrançois Tigeot  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
88621f407SFrançois Tigeot  * and/or sell copies of the Software, and to permit persons to whom the
98621f407SFrançois Tigeot  * Software is furnished to do so, subject to the following conditions:
108621f407SFrançois Tigeot  *
118621f407SFrançois Tigeot  * The above copyright notice and this permission notice (including the next
128621f407SFrançois Tigeot  * paragraph) shall be included in all copies or substantial portions of the
138621f407SFrançois Tigeot  * Software.
148621f407SFrançois Tigeot  *
158621f407SFrançois Tigeot  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
168621f407SFrançois Tigeot  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
178621f407SFrançois Tigeot  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
188621f407SFrançois Tigeot  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
198621f407SFrançois Tigeot  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
208621f407SFrançois Tigeot  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
218621f407SFrançois Tigeot  * IN THE SOFTWARE.
228621f407SFrançois Tigeot  *
238621f407SFrançois Tigeot  * Authors:
248621f407SFrançois Tigeot  *    Rafael Antognolli <rafael.antognolli@intel.com>
258621f407SFrançois Tigeot  *
268621f407SFrançois Tigeot  */
278621f407SFrançois Tigeot 
288621f407SFrançois Tigeot #include <linux/device.h>
298621f407SFrançois Tigeot #include <linux/fs.h>
308621f407SFrançois Tigeot #include <linux/slab.h>
31*1e12ee3bSFrançois Tigeot #include <linux/init.h>
328621f407SFrançois Tigeot #include <linux/kernel.h>
338621f407SFrançois Tigeot #include <linux/module.h>
348621f407SFrançois Tigeot #include <linux/uaccess.h>
358621f407SFrançois Tigeot #include <drm/drm_dp_helper.h>
368621f407SFrançois Tigeot #include <drm/drm_crtc.h>
37*1e12ee3bSFrançois Tigeot #include <drm/drmP.h>
388621f407SFrançois Tigeot 
391dedbd3bSFrançois Tigeot #include "drm_crtc_helper_internal.h"
401dedbd3bSFrançois Tigeot 
418621f407SFrançois Tigeot struct drm_dp_aux_dev {
428621f407SFrançois Tigeot 	unsigned index;
438621f407SFrançois Tigeot 	struct drm_dp_aux *aux;
448621f407SFrançois Tigeot 	struct device *dev;
458621f407SFrançois Tigeot 	struct kref refcount;
468621f407SFrançois Tigeot 	atomic_t usecount;
478621f407SFrançois Tigeot };
488621f407SFrançois Tigeot 
498621f407SFrançois Tigeot #define DRM_AUX_MINORS	256
508621f407SFrançois Tigeot #define AUX_MAX_OFFSET	(1 << 20)
518621f407SFrançois Tigeot #if 0
528621f407SFrançois Tigeot static DEFINE_IDR(aux_idr);
538621f407SFrançois Tigeot static DEFINE_MUTEX(aux_idr_mutex);
548621f407SFrançois Tigeot static struct class *drm_dp_aux_dev_class;
558621f407SFrançois Tigeot static int drm_dev_major = -1;
568621f407SFrançois Tigeot 
578621f407SFrançois Tigeot static struct drm_dp_aux_dev *drm_dp_aux_dev_get_by_minor(unsigned index)
588621f407SFrançois Tigeot {
598621f407SFrançois Tigeot 	struct drm_dp_aux_dev *aux_dev = NULL;
608621f407SFrançois Tigeot 
618621f407SFrançois Tigeot 	mutex_lock(&aux_idr_mutex);
628621f407SFrançois Tigeot 	aux_dev = idr_find(&aux_idr, index);
638621f407SFrançois Tigeot 	if (!kref_get_unless_zero(&aux_dev->refcount))
648621f407SFrançois Tigeot 		aux_dev = NULL;
658621f407SFrançois Tigeot 	mutex_unlock(&aux_idr_mutex);
668621f407SFrançois Tigeot 
678621f407SFrançois Tigeot 	return aux_dev;
688621f407SFrançois Tigeot }
698621f407SFrançois Tigeot 
708621f407SFrançois Tigeot static struct drm_dp_aux_dev *alloc_drm_dp_aux_dev(struct drm_dp_aux *aux)
718621f407SFrançois Tigeot {
728621f407SFrançois Tigeot 	struct drm_dp_aux_dev *aux_dev;
738621f407SFrançois Tigeot 	int index;
748621f407SFrançois Tigeot 
758621f407SFrançois Tigeot 	aux_dev = kzalloc(sizeof(*aux_dev), GFP_KERNEL);
768621f407SFrançois Tigeot 	if (!aux_dev)
778621f407SFrançois Tigeot 		return ERR_PTR(-ENOMEM);
788621f407SFrançois Tigeot 	aux_dev->aux = aux;
798621f407SFrançois Tigeot 	atomic_set(&aux_dev->usecount, 1);
808621f407SFrançois Tigeot 	kref_init(&aux_dev->refcount);
818621f407SFrançois Tigeot 
828621f407SFrançois Tigeot 	mutex_lock(&aux_idr_mutex);
838621f407SFrançois Tigeot 	index = idr_alloc_cyclic(&aux_idr, aux_dev, 0, DRM_AUX_MINORS,
848621f407SFrançois Tigeot 				 GFP_KERNEL);
858621f407SFrançois Tigeot 	mutex_unlock(&aux_idr_mutex);
868621f407SFrançois Tigeot 	if (index < 0) {
878621f407SFrançois Tigeot 		kfree(aux_dev);
888621f407SFrançois Tigeot 		return ERR_PTR(index);
898621f407SFrançois Tigeot 	}
908621f407SFrançois Tigeot 	aux_dev->index = index;
918621f407SFrançois Tigeot 
928621f407SFrançois Tigeot 	return aux_dev;
938621f407SFrançois Tigeot }
948621f407SFrançois Tigeot 
958621f407SFrançois Tigeot static void release_drm_dp_aux_dev(struct kref *ref)
968621f407SFrançois Tigeot {
978621f407SFrançois Tigeot 	struct drm_dp_aux_dev *aux_dev =
988621f407SFrançois Tigeot 		container_of(ref, struct drm_dp_aux_dev, refcount);
998621f407SFrançois Tigeot 
1008621f407SFrançois Tigeot 	kfree(aux_dev);
1018621f407SFrançois Tigeot }
1028621f407SFrançois Tigeot 
1038621f407SFrançois Tigeot static ssize_t name_show(struct device *dev,
1048621f407SFrançois Tigeot 			 struct device_attribute *attr, char *buf)
1058621f407SFrançois Tigeot {
1068621f407SFrançois Tigeot 	ssize_t res;
1078621f407SFrançois Tigeot 	struct drm_dp_aux_dev *aux_dev =
1088621f407SFrançois Tigeot 		drm_dp_aux_dev_get_by_minor(MINOR(dev->devt));
1098621f407SFrançois Tigeot 
1108621f407SFrançois Tigeot 	if (!aux_dev)
1118621f407SFrançois Tigeot 		return -ENODEV;
1128621f407SFrançois Tigeot 
1138621f407SFrançois Tigeot 	res = sprintf(buf, "%s\n", aux_dev->aux->name);
1148621f407SFrançois Tigeot 	kref_put(&aux_dev->refcount, release_drm_dp_aux_dev);
1158621f407SFrançois Tigeot 
1168621f407SFrançois Tigeot 	return res;
1178621f407SFrançois Tigeot }
1188621f407SFrançois Tigeot static DEVICE_ATTR_RO(name);
1198621f407SFrançois Tigeot 
1208621f407SFrançois Tigeot static struct attribute *drm_dp_aux_attrs[] = {
1218621f407SFrançois Tigeot 	&dev_attr_name.attr,
1228621f407SFrançois Tigeot 	NULL,
1238621f407SFrançois Tigeot };
1248621f407SFrançois Tigeot ATTRIBUTE_GROUPS(drm_dp_aux);
1258621f407SFrançois Tigeot 
1268621f407SFrançois Tigeot static int auxdev_open(struct inode *inode, struct file *file)
1278621f407SFrançois Tigeot {
1288621f407SFrançois Tigeot 	unsigned int minor = iminor(inode);
1298621f407SFrançois Tigeot 	struct drm_dp_aux_dev *aux_dev;
1308621f407SFrançois Tigeot 
1318621f407SFrançois Tigeot 	aux_dev = drm_dp_aux_dev_get_by_minor(minor);
1328621f407SFrançois Tigeot 	if (!aux_dev)
1338621f407SFrançois Tigeot 		return -ENODEV;
1348621f407SFrançois Tigeot 
1358621f407SFrançois Tigeot 	file->private_data = aux_dev;
1368621f407SFrançois Tigeot 	return 0;
1378621f407SFrançois Tigeot }
1388621f407SFrançois Tigeot 
1398621f407SFrançois Tigeot static loff_t auxdev_llseek(struct file *file, loff_t offset, int whence)
1408621f407SFrançois Tigeot {
1418621f407SFrançois Tigeot 	return fixed_size_llseek(file, offset, whence, AUX_MAX_OFFSET);
1428621f407SFrançois Tigeot }
1438621f407SFrançois Tigeot 
1448621f407SFrançois Tigeot static ssize_t auxdev_read(struct file *file, char __user *buf, size_t count,
1458621f407SFrançois Tigeot 			   loff_t *offset)
1468621f407SFrançois Tigeot {
1478621f407SFrançois Tigeot 	size_t bytes_pending, num_bytes_processed = 0;
1488621f407SFrançois Tigeot 	struct drm_dp_aux_dev *aux_dev = file->private_data;
1498621f407SFrançois Tigeot 	ssize_t res = 0;
1508621f407SFrançois Tigeot 
1518621f407SFrançois Tigeot 	if (!atomic_inc_not_zero(&aux_dev->usecount))
1528621f407SFrançois Tigeot 		return -ENODEV;
1538621f407SFrançois Tigeot 
1548621f407SFrançois Tigeot 	bytes_pending = min((loff_t)count, AUX_MAX_OFFSET - (*offset));
1558621f407SFrançois Tigeot 
1568621f407SFrançois Tigeot 	if (!access_ok(VERIFY_WRITE, buf, bytes_pending)) {
1578621f407SFrançois Tigeot 		res = -EFAULT;
1588621f407SFrançois Tigeot 		goto out;
1598621f407SFrançois Tigeot 	}
1608621f407SFrançois Tigeot 
1618621f407SFrançois Tigeot 	while (bytes_pending > 0) {
1628621f407SFrançois Tigeot 		uint8_t localbuf[DP_AUX_MAX_PAYLOAD_BYTES];
1638621f407SFrançois Tigeot 		ssize_t todo = min_t(size_t, bytes_pending, sizeof(localbuf));
1648621f407SFrançois Tigeot 
1658621f407SFrançois Tigeot 		if (signal_pending(current)) {
1668621f407SFrançois Tigeot 			res = num_bytes_processed ?
1678621f407SFrançois Tigeot 				num_bytes_processed : -ERESTARTSYS;
1688621f407SFrançois Tigeot 			goto out;
1698621f407SFrançois Tigeot 		}
1708621f407SFrançois Tigeot 
1718621f407SFrançois Tigeot 		res = drm_dp_dpcd_read(aux_dev->aux, *offset, localbuf, todo);
1728621f407SFrançois Tigeot 		if (res <= 0) {
1738621f407SFrançois Tigeot 			res = num_bytes_processed ? num_bytes_processed : res;
1748621f407SFrançois Tigeot 			goto out;
1758621f407SFrançois Tigeot 		}
1768621f407SFrançois Tigeot 		if (__copy_to_user(buf + num_bytes_processed, localbuf, res)) {
1778621f407SFrançois Tigeot 			res = num_bytes_processed ?
1788621f407SFrançois Tigeot 				num_bytes_processed : -EFAULT;
1798621f407SFrançois Tigeot 			goto out;
1808621f407SFrançois Tigeot 		}
1818621f407SFrançois Tigeot 		bytes_pending -= res;
1828621f407SFrançois Tigeot 		*offset += res;
1838621f407SFrançois Tigeot 		num_bytes_processed += res;
1848621f407SFrançois Tigeot 		res = num_bytes_processed;
1858621f407SFrançois Tigeot 	}
1868621f407SFrançois Tigeot 
1878621f407SFrançois Tigeot out:
1888621f407SFrançois Tigeot 	atomic_dec(&aux_dev->usecount);
1898621f407SFrançois Tigeot 	wake_up_atomic_t(&aux_dev->usecount);
1908621f407SFrançois Tigeot 	return res;
1918621f407SFrançois Tigeot }
1928621f407SFrançois Tigeot 
1938621f407SFrançois Tigeot static ssize_t auxdev_write(struct file *file, const char __user *buf,
1948621f407SFrançois Tigeot 			    size_t count, loff_t *offset)
1958621f407SFrançois Tigeot {
1968621f407SFrançois Tigeot 	size_t bytes_pending, num_bytes_processed = 0;
1978621f407SFrançois Tigeot 	struct drm_dp_aux_dev *aux_dev = file->private_data;
1988621f407SFrançois Tigeot 	ssize_t res = 0;
1998621f407SFrançois Tigeot 
2008621f407SFrançois Tigeot 	if (!atomic_inc_not_zero(&aux_dev->usecount))
2018621f407SFrançois Tigeot 		return -ENODEV;
2028621f407SFrançois Tigeot 
2038621f407SFrançois Tigeot 	bytes_pending = min((loff_t)count, AUX_MAX_OFFSET - *offset);
2048621f407SFrançois Tigeot 
2058621f407SFrançois Tigeot 	if (!access_ok(VERIFY_READ, buf, bytes_pending)) {
2068621f407SFrançois Tigeot 		res = -EFAULT;
2078621f407SFrançois Tigeot 		goto out;
2088621f407SFrançois Tigeot 	}
2098621f407SFrançois Tigeot 
2108621f407SFrançois Tigeot 	while (bytes_pending > 0) {
2118621f407SFrançois Tigeot 		uint8_t localbuf[DP_AUX_MAX_PAYLOAD_BYTES];
2128621f407SFrançois Tigeot 		ssize_t todo = min_t(size_t, bytes_pending, sizeof(localbuf));
2138621f407SFrançois Tigeot 
2148621f407SFrançois Tigeot 		if (signal_pending(current)) {
2158621f407SFrançois Tigeot 			res = num_bytes_processed ?
2168621f407SFrançois Tigeot 				num_bytes_processed : -ERESTARTSYS;
2178621f407SFrançois Tigeot 			goto out;
2188621f407SFrançois Tigeot 		}
2198621f407SFrançois Tigeot 
2208621f407SFrançois Tigeot 		if (__copy_from_user(localbuf,
2218621f407SFrançois Tigeot 				     buf + num_bytes_processed, todo)) {
2228621f407SFrançois Tigeot 			res = num_bytes_processed ?
2238621f407SFrançois Tigeot 				num_bytes_processed : -EFAULT;
2248621f407SFrançois Tigeot 			goto out;
2258621f407SFrançois Tigeot 		}
2268621f407SFrançois Tigeot 
2278621f407SFrançois Tigeot 		res = drm_dp_dpcd_write(aux_dev->aux, *offset, localbuf, todo);
2288621f407SFrançois Tigeot 		if (res <= 0) {
2298621f407SFrançois Tigeot 			res = num_bytes_processed ? num_bytes_processed : res;
2308621f407SFrançois Tigeot 			goto out;
2318621f407SFrançois Tigeot 		}
2328621f407SFrançois Tigeot 		bytes_pending -= res;
2338621f407SFrançois Tigeot 		*offset += res;
2348621f407SFrançois Tigeot 		num_bytes_processed += res;
2358621f407SFrançois Tigeot 		res = num_bytes_processed;
2368621f407SFrançois Tigeot 	}
2378621f407SFrançois Tigeot 
2388621f407SFrançois Tigeot out:
2398621f407SFrançois Tigeot 	atomic_dec(&aux_dev->usecount);
2408621f407SFrançois Tigeot 	wake_up_atomic_t(&aux_dev->usecount);
2418621f407SFrançois Tigeot 	return res;
2428621f407SFrançois Tigeot }
2438621f407SFrançois Tigeot 
2448621f407SFrançois Tigeot static int auxdev_release(struct inode *inode, struct file *file)
2458621f407SFrançois Tigeot {
2468621f407SFrançois Tigeot 	struct drm_dp_aux_dev *aux_dev = file->private_data;
2478621f407SFrançois Tigeot 
2488621f407SFrançois Tigeot 	kref_put(&aux_dev->refcount, release_drm_dp_aux_dev);
2498621f407SFrançois Tigeot 	return 0;
2508621f407SFrançois Tigeot }
2518621f407SFrançois Tigeot 
2528621f407SFrançois Tigeot static const struct file_operations auxdev_fops = {
2538621f407SFrançois Tigeot 	.owner		= THIS_MODULE,
2548621f407SFrançois Tigeot 	.llseek		= auxdev_llseek,
2558621f407SFrançois Tigeot 	.read		= auxdev_read,
2568621f407SFrançois Tigeot 	.write		= auxdev_write,
2578621f407SFrançois Tigeot 	.open		= auxdev_open,
2588621f407SFrançois Tigeot 	.release	= auxdev_release,
2598621f407SFrançois Tigeot };
2608621f407SFrançois Tigeot 
2618621f407SFrançois Tigeot #define to_auxdev(d) container_of(d, struct drm_dp_aux_dev, aux)
2628621f407SFrançois Tigeot 
2638621f407SFrançois Tigeot static struct drm_dp_aux_dev *drm_dp_aux_dev_get_by_aux(struct drm_dp_aux *aux)
2648621f407SFrançois Tigeot {
2658621f407SFrançois Tigeot 	struct drm_dp_aux_dev *iter, *aux_dev = NULL;
2668621f407SFrançois Tigeot 	int id;
2678621f407SFrançois Tigeot 
2688621f407SFrançois Tigeot 	/* don't increase kref count here because this function should only be
2698621f407SFrançois Tigeot 	 * used by drm_dp_aux_unregister_devnode. Thus, it will always have at
2708621f407SFrançois Tigeot 	 * least one reference - the one that drm_dp_aux_register_devnode
2718621f407SFrançois Tigeot 	 * created
2728621f407SFrançois Tigeot 	 */
2738621f407SFrançois Tigeot 	mutex_lock(&aux_idr_mutex);
2748621f407SFrançois Tigeot 	idr_for_each_entry(&aux_idr, iter, id) {
2758621f407SFrançois Tigeot 		if (iter->aux == aux) {
2768621f407SFrançois Tigeot 			aux_dev = iter;
2778621f407SFrançois Tigeot 			break;
2788621f407SFrançois Tigeot 		}
2798621f407SFrançois Tigeot 	}
2808621f407SFrançois Tigeot 	mutex_unlock(&aux_idr_mutex);
2818621f407SFrançois Tigeot 	return aux_dev;
2828621f407SFrançois Tigeot }
2838621f407SFrançois Tigeot 
2848621f407SFrançois Tigeot static int auxdev_wait_atomic_t(atomic_t *p)
2858621f407SFrançois Tigeot {
2868621f407SFrançois Tigeot 	schedule();
2878621f407SFrançois Tigeot 	return 0;
2888621f407SFrançois Tigeot }
2891dedbd3bSFrançois Tigeot 
2908621f407SFrançois Tigeot void drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux)
2918621f407SFrançois Tigeot {
2928621f407SFrançois Tigeot 	struct drm_dp_aux_dev *aux_dev;
2938621f407SFrançois Tigeot 	unsigned int minor;
2948621f407SFrançois Tigeot 
2958621f407SFrançois Tigeot 	aux_dev = drm_dp_aux_dev_get_by_aux(aux);
2968621f407SFrançois Tigeot 	if (!aux_dev) /* attach must have failed */
2978621f407SFrançois Tigeot 		return;
2988621f407SFrançois Tigeot 
2998621f407SFrançois Tigeot 	mutex_lock(&aux_idr_mutex);
3008621f407SFrançois Tigeot 	idr_remove(&aux_idr, aux_dev->index);
3018621f407SFrançois Tigeot 	mutex_unlock(&aux_idr_mutex);
3028621f407SFrançois Tigeot 
3038621f407SFrançois Tigeot 	atomic_dec(&aux_dev->usecount);
3048621f407SFrançois Tigeot 	wait_on_atomic_t(&aux_dev->usecount, auxdev_wait_atomic_t,
3058621f407SFrançois Tigeot 			 TASK_UNINTERRUPTIBLE);
3068621f407SFrançois Tigeot 
3078621f407SFrançois Tigeot 	minor = aux_dev->index;
3088621f407SFrançois Tigeot 	if (aux_dev->dev)
3098621f407SFrançois Tigeot 		device_destroy(drm_dp_aux_dev_class,
3108621f407SFrançois Tigeot 			       MKDEV(drm_dev_major, minor));
3118621f407SFrançois Tigeot 
3128621f407SFrançois Tigeot 	DRM_DEBUG("drm_dp_aux_dev: aux [%s] unregistering\n", aux->name);
3138621f407SFrançois Tigeot 	kref_put(&aux_dev->refcount, release_drm_dp_aux_dev);
3148621f407SFrançois Tigeot }
3158621f407SFrançois Tigeot 
3168621f407SFrançois Tigeot int drm_dp_aux_register_devnode(struct drm_dp_aux *aux)
3178621f407SFrançois Tigeot {
3188621f407SFrançois Tigeot 	struct drm_dp_aux_dev *aux_dev;
3198621f407SFrançois Tigeot 	int res;
3208621f407SFrançois Tigeot 
3218621f407SFrançois Tigeot 	aux_dev = alloc_drm_dp_aux_dev(aux);
3228621f407SFrançois Tigeot 	if (IS_ERR(aux_dev))
3238621f407SFrançois Tigeot 		return PTR_ERR(aux_dev);
3248621f407SFrançois Tigeot 
3258621f407SFrançois Tigeot 	aux_dev->dev = device_create(drm_dp_aux_dev_class, aux->dev,
3268621f407SFrançois Tigeot 				     MKDEV(drm_dev_major, aux_dev->index), NULL,
3278621f407SFrançois Tigeot 				     "drm_dp_aux%d", aux_dev->index);
3288621f407SFrançois Tigeot 	if (IS_ERR(aux_dev->dev)) {
3298621f407SFrançois Tigeot 		res = PTR_ERR(aux_dev->dev);
3308621f407SFrançois Tigeot 		aux_dev->dev = NULL;
3318621f407SFrançois Tigeot 		goto error;
3328621f407SFrançois Tigeot 	}
3338621f407SFrançois Tigeot 
3348621f407SFrançois Tigeot 	DRM_DEBUG("drm_dp_aux_dev: aux [%s] registered as minor %d\n",
3358621f407SFrançois Tigeot 		  aux->name, aux_dev->index);
3368621f407SFrançois Tigeot 	return 0;
3378621f407SFrançois Tigeot error:
3388621f407SFrançois Tigeot 	drm_dp_aux_unregister_devnode(aux);
3398621f407SFrançois Tigeot 	return res;
3408621f407SFrançois Tigeot }
3418621f407SFrançois Tigeot 
3428621f407SFrançois Tigeot int drm_dp_aux_dev_init(void)
3438621f407SFrançois Tigeot {
3448621f407SFrançois Tigeot 	int res;
3458621f407SFrançois Tigeot 
3468621f407SFrançois Tigeot 	drm_dp_aux_dev_class = class_create(THIS_MODULE, "drm_dp_aux_dev");
3478621f407SFrançois Tigeot 	if (IS_ERR(drm_dp_aux_dev_class)) {
3481dedbd3bSFrançois Tigeot 		return PTR_ERR(drm_dp_aux_dev_class);
3498621f407SFrançois Tigeot 	}
3508621f407SFrançois Tigeot 	drm_dp_aux_dev_class->dev_groups = drm_dp_aux_groups;
3518621f407SFrançois Tigeot 
3528621f407SFrançois Tigeot 	res = register_chrdev(0, "aux", &auxdev_fops);
3538621f407SFrançois Tigeot 	if (res < 0)
3548621f407SFrançois Tigeot 		goto out;
3558621f407SFrançois Tigeot 	drm_dev_major = res;
3568621f407SFrançois Tigeot 
3578621f407SFrançois Tigeot 	return 0;
3588621f407SFrançois Tigeot out:
3598621f407SFrançois Tigeot 	class_destroy(drm_dp_aux_dev_class);
3608621f407SFrançois Tigeot 	return res;
3618621f407SFrançois Tigeot }
3628621f407SFrançois Tigeot 
3638621f407SFrançois Tigeot void drm_dp_aux_dev_exit(void)
3648621f407SFrançois Tigeot {
3658621f407SFrançois Tigeot 	unregister_chrdev(drm_dev_major, "aux");
3668621f407SFrançois Tigeot 	class_destroy(drm_dp_aux_dev_class);
3678621f407SFrançois Tigeot }
368*1e12ee3bSFrançois Tigeot #endif	/* 0 */
369