18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright © 2015 Intel Corporation 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 58c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 68c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 78c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 88c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 98c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the next 128c2ecf20Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 138c2ecf20Sopenharmony_ci * Software. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 168c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 178c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 188c2ecf20Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 198c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 208c2ecf20Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 218c2ecf20Sopenharmony_ci * IN THE SOFTWARE. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * Authors: 248c2ecf20Sopenharmony_ci * Rafael Antognolli <rafael.antognolli@intel.com> 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci */ 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <linux/device.h> 298c2ecf20Sopenharmony_ci#include <linux/fs.h> 308c2ecf20Sopenharmony_ci#include <linux/init.h> 318c2ecf20Sopenharmony_ci#include <linux/kernel.h> 328c2ecf20Sopenharmony_ci#include <linux/module.h> 338c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 348c2ecf20Sopenharmony_ci#include <linux/slab.h> 358c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 368c2ecf20Sopenharmony_ci#include <linux/uio.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h> 398c2ecf20Sopenharmony_ci#include <drm/drm_dp_helper.h> 408c2ecf20Sopenharmony_ci#include <drm/drm_dp_mst_helper.h> 418c2ecf20Sopenharmony_ci#include <drm/drm_print.h> 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#include "drm_crtc_helper_internal.h" 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistruct drm_dp_aux_dev { 468c2ecf20Sopenharmony_ci unsigned index; 478c2ecf20Sopenharmony_ci struct drm_dp_aux *aux; 488c2ecf20Sopenharmony_ci struct device *dev; 498c2ecf20Sopenharmony_ci struct kref refcount; 508c2ecf20Sopenharmony_ci atomic_t usecount; 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define DRM_AUX_MINORS 256 548c2ecf20Sopenharmony_ci#define AUX_MAX_OFFSET (1 << 20) 558c2ecf20Sopenharmony_cistatic DEFINE_IDR(aux_idr); 568c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(aux_idr_mutex); 578c2ecf20Sopenharmony_cistatic struct class *drm_dp_aux_dev_class; 588c2ecf20Sopenharmony_cistatic int drm_dev_major = -1; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic struct drm_dp_aux_dev *drm_dp_aux_dev_get_by_minor(unsigned index) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct drm_dp_aux_dev *aux_dev = NULL; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci mutex_lock(&aux_idr_mutex); 658c2ecf20Sopenharmony_ci aux_dev = idr_find(&aux_idr, index); 668c2ecf20Sopenharmony_ci if (aux_dev && !kref_get_unless_zero(&aux_dev->refcount)) 678c2ecf20Sopenharmony_ci aux_dev = NULL; 688c2ecf20Sopenharmony_ci mutex_unlock(&aux_idr_mutex); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci return aux_dev; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic struct drm_dp_aux_dev *alloc_drm_dp_aux_dev(struct drm_dp_aux *aux) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct drm_dp_aux_dev *aux_dev; 768c2ecf20Sopenharmony_ci int index; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci aux_dev = kzalloc(sizeof(*aux_dev), GFP_KERNEL); 798c2ecf20Sopenharmony_ci if (!aux_dev) 808c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 818c2ecf20Sopenharmony_ci aux_dev->aux = aux; 828c2ecf20Sopenharmony_ci atomic_set(&aux_dev->usecount, 1); 838c2ecf20Sopenharmony_ci kref_init(&aux_dev->refcount); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci mutex_lock(&aux_idr_mutex); 868c2ecf20Sopenharmony_ci index = idr_alloc(&aux_idr, aux_dev, 0, DRM_AUX_MINORS, GFP_KERNEL); 878c2ecf20Sopenharmony_ci mutex_unlock(&aux_idr_mutex); 888c2ecf20Sopenharmony_ci if (index < 0) { 898c2ecf20Sopenharmony_ci kfree(aux_dev); 908c2ecf20Sopenharmony_ci return ERR_PTR(index); 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci aux_dev->index = index; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return aux_dev; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic void release_drm_dp_aux_dev(struct kref *ref) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct drm_dp_aux_dev *aux_dev = 1008c2ecf20Sopenharmony_ci container_of(ref, struct drm_dp_aux_dev, refcount); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci kfree(aux_dev); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic ssize_t name_show(struct device *dev, 1068c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci ssize_t res; 1098c2ecf20Sopenharmony_ci struct drm_dp_aux_dev *aux_dev = 1108c2ecf20Sopenharmony_ci drm_dp_aux_dev_get_by_minor(MINOR(dev->devt)); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (!aux_dev) 1138c2ecf20Sopenharmony_ci return -ENODEV; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci res = sprintf(buf, "%s\n", aux_dev->aux->name); 1168c2ecf20Sopenharmony_ci kref_put(&aux_dev->refcount, release_drm_dp_aux_dev); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci return res; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(name); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic struct attribute *drm_dp_aux_attrs[] = { 1238c2ecf20Sopenharmony_ci &dev_attr_name.attr, 1248c2ecf20Sopenharmony_ci NULL, 1258c2ecf20Sopenharmony_ci}; 1268c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(drm_dp_aux); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic int auxdev_open(struct inode *inode, struct file *file) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci unsigned int minor = iminor(inode); 1318c2ecf20Sopenharmony_ci struct drm_dp_aux_dev *aux_dev; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci aux_dev = drm_dp_aux_dev_get_by_minor(minor); 1348c2ecf20Sopenharmony_ci if (!aux_dev) 1358c2ecf20Sopenharmony_ci return -ENODEV; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci file->private_data = aux_dev; 1388c2ecf20Sopenharmony_ci return 0; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic loff_t auxdev_llseek(struct file *file, loff_t offset, int whence) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci return fixed_size_llseek(file, offset, whence, AUX_MAX_OFFSET); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic ssize_t auxdev_read_iter(struct kiocb *iocb, struct iov_iter *to) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct drm_dp_aux_dev *aux_dev = iocb->ki_filp->private_data; 1498c2ecf20Sopenharmony_ci loff_t pos = iocb->ki_pos; 1508c2ecf20Sopenharmony_ci ssize_t res = 0; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (!atomic_inc_not_zero(&aux_dev->usecount)) 1538c2ecf20Sopenharmony_ci return -ENODEV; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci iov_iter_truncate(to, AUX_MAX_OFFSET - pos); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci while (iov_iter_count(to)) { 1588c2ecf20Sopenharmony_ci uint8_t buf[DP_AUX_MAX_PAYLOAD_BYTES]; 1598c2ecf20Sopenharmony_ci ssize_t todo = min(iov_iter_count(to), sizeof(buf)); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci if (signal_pending(current)) { 1628c2ecf20Sopenharmony_ci res = -ERESTARTSYS; 1638c2ecf20Sopenharmony_ci break; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci res = drm_dp_dpcd_read(aux_dev->aux, pos, buf, todo); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (res <= 0) 1698c2ecf20Sopenharmony_ci break; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (copy_to_iter(buf, res, to) != res) { 1728c2ecf20Sopenharmony_ci res = -EFAULT; 1738c2ecf20Sopenharmony_ci break; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci pos += res; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (pos != iocb->ki_pos) 1808c2ecf20Sopenharmony_ci res = pos - iocb->ki_pos; 1818c2ecf20Sopenharmony_ci iocb->ki_pos = pos; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&aux_dev->usecount)) 1848c2ecf20Sopenharmony_ci wake_up_var(&aux_dev->usecount); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci return res; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic ssize_t auxdev_write_iter(struct kiocb *iocb, struct iov_iter *from) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct drm_dp_aux_dev *aux_dev = iocb->ki_filp->private_data; 1928c2ecf20Sopenharmony_ci loff_t pos = iocb->ki_pos; 1938c2ecf20Sopenharmony_ci ssize_t res = 0; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (!atomic_inc_not_zero(&aux_dev->usecount)) 1968c2ecf20Sopenharmony_ci return -ENODEV; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci iov_iter_truncate(from, AUX_MAX_OFFSET - pos); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci while (iov_iter_count(from)) { 2018c2ecf20Sopenharmony_ci uint8_t buf[DP_AUX_MAX_PAYLOAD_BYTES]; 2028c2ecf20Sopenharmony_ci ssize_t todo = min(iov_iter_count(from), sizeof(buf)); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci if (signal_pending(current)) { 2058c2ecf20Sopenharmony_ci res = -ERESTARTSYS; 2068c2ecf20Sopenharmony_ci break; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (!copy_from_iter_full(buf, todo, from)) { 2108c2ecf20Sopenharmony_ci res = -EFAULT; 2118c2ecf20Sopenharmony_ci break; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci res = drm_dp_dpcd_write(aux_dev->aux, pos, buf, todo); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (res <= 0) 2178c2ecf20Sopenharmony_ci break; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci pos += res; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (pos != iocb->ki_pos) 2238c2ecf20Sopenharmony_ci res = pos - iocb->ki_pos; 2248c2ecf20Sopenharmony_ci iocb->ki_pos = pos; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&aux_dev->usecount)) 2278c2ecf20Sopenharmony_ci wake_up_var(&aux_dev->usecount); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci return res; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic int auxdev_release(struct inode *inode, struct file *file) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct drm_dp_aux_dev *aux_dev = file->private_data; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci kref_put(&aux_dev->refcount, release_drm_dp_aux_dev); 2378c2ecf20Sopenharmony_ci return 0; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic const struct file_operations auxdev_fops = { 2418c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2428c2ecf20Sopenharmony_ci .llseek = auxdev_llseek, 2438c2ecf20Sopenharmony_ci .read_iter = auxdev_read_iter, 2448c2ecf20Sopenharmony_ci .write_iter = auxdev_write_iter, 2458c2ecf20Sopenharmony_ci .open = auxdev_open, 2468c2ecf20Sopenharmony_ci .release = auxdev_release, 2478c2ecf20Sopenharmony_ci}; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci#define to_auxdev(d) container_of(d, struct drm_dp_aux_dev, aux) 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic struct drm_dp_aux_dev *drm_dp_aux_dev_get_by_aux(struct drm_dp_aux *aux) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct drm_dp_aux_dev *iter, *aux_dev = NULL; 2548c2ecf20Sopenharmony_ci int id; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* don't increase kref count here because this function should only be 2578c2ecf20Sopenharmony_ci * used by drm_dp_aux_unregister_devnode. Thus, it will always have at 2588c2ecf20Sopenharmony_ci * least one reference - the one that drm_dp_aux_register_devnode 2598c2ecf20Sopenharmony_ci * created 2608c2ecf20Sopenharmony_ci */ 2618c2ecf20Sopenharmony_ci mutex_lock(&aux_idr_mutex); 2628c2ecf20Sopenharmony_ci idr_for_each_entry(&aux_idr, iter, id) { 2638c2ecf20Sopenharmony_ci if (iter->aux == aux) { 2648c2ecf20Sopenharmony_ci aux_dev = iter; 2658c2ecf20Sopenharmony_ci break; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci mutex_unlock(&aux_idr_mutex); 2698c2ecf20Sopenharmony_ci return aux_dev; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_civoid drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct drm_dp_aux_dev *aux_dev; 2758c2ecf20Sopenharmony_ci unsigned int minor; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci aux_dev = drm_dp_aux_dev_get_by_aux(aux); 2788c2ecf20Sopenharmony_ci if (!aux_dev) /* attach must have failed */ 2798c2ecf20Sopenharmony_ci return; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci mutex_lock(&aux_idr_mutex); 2828c2ecf20Sopenharmony_ci idr_remove(&aux_idr, aux_dev->index); 2838c2ecf20Sopenharmony_ci mutex_unlock(&aux_idr_mutex); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci atomic_dec(&aux_dev->usecount); 2868c2ecf20Sopenharmony_ci wait_var_event(&aux_dev->usecount, !atomic_read(&aux_dev->usecount)); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci minor = aux_dev->index; 2898c2ecf20Sopenharmony_ci if (aux_dev->dev) 2908c2ecf20Sopenharmony_ci device_destroy(drm_dp_aux_dev_class, 2918c2ecf20Sopenharmony_ci MKDEV(drm_dev_major, minor)); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci DRM_DEBUG("drm_dp_aux_dev: aux [%s] unregistering\n", aux->name); 2948c2ecf20Sopenharmony_ci kref_put(&aux_dev->refcount, release_drm_dp_aux_dev); 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ciint drm_dp_aux_register_devnode(struct drm_dp_aux *aux) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct drm_dp_aux_dev *aux_dev; 3008c2ecf20Sopenharmony_ci int res; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci aux_dev = alloc_drm_dp_aux_dev(aux); 3038c2ecf20Sopenharmony_ci if (IS_ERR(aux_dev)) 3048c2ecf20Sopenharmony_ci return PTR_ERR(aux_dev); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci aux_dev->dev = device_create(drm_dp_aux_dev_class, aux->dev, 3078c2ecf20Sopenharmony_ci MKDEV(drm_dev_major, aux_dev->index), NULL, 3088c2ecf20Sopenharmony_ci "drm_dp_aux%d", aux_dev->index); 3098c2ecf20Sopenharmony_ci if (IS_ERR(aux_dev->dev)) { 3108c2ecf20Sopenharmony_ci res = PTR_ERR(aux_dev->dev); 3118c2ecf20Sopenharmony_ci aux_dev->dev = NULL; 3128c2ecf20Sopenharmony_ci goto error; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci DRM_DEBUG("drm_dp_aux_dev: aux [%s] registered as minor %d\n", 3168c2ecf20Sopenharmony_ci aux->name, aux_dev->index); 3178c2ecf20Sopenharmony_ci return 0; 3188c2ecf20Sopenharmony_cierror: 3198c2ecf20Sopenharmony_ci drm_dp_aux_unregister_devnode(aux); 3208c2ecf20Sopenharmony_ci return res; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ciint drm_dp_aux_dev_init(void) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci int res; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci drm_dp_aux_dev_class = class_create(THIS_MODULE, "drm_dp_aux_dev"); 3288c2ecf20Sopenharmony_ci if (IS_ERR(drm_dp_aux_dev_class)) { 3298c2ecf20Sopenharmony_ci return PTR_ERR(drm_dp_aux_dev_class); 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci drm_dp_aux_dev_class->dev_groups = drm_dp_aux_groups; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci res = register_chrdev(0, "aux", &auxdev_fops); 3348c2ecf20Sopenharmony_ci if (res < 0) 3358c2ecf20Sopenharmony_ci goto out; 3368c2ecf20Sopenharmony_ci drm_dev_major = res; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci return 0; 3398c2ecf20Sopenharmony_ciout: 3408c2ecf20Sopenharmony_ci class_destroy(drm_dp_aux_dev_class); 3418c2ecf20Sopenharmony_ci return res; 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_civoid drm_dp_aux_dev_exit(void) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci unregister_chrdev(drm_dev_major, "aux"); 3478c2ecf20Sopenharmony_ci class_destroy(drm_dp_aux_dev_class); 3488c2ecf20Sopenharmony_ci} 349