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> 311e12ee3bSFranç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> 35*3f2dd94aSFrançois Tigeot #include <linux/uio.h> 368621f407SFrançois Tigeot #include <drm/drm_dp_helper.h> 378621f407SFrançois Tigeot #include <drm/drm_crtc.h> 381e12ee3bSFrançois Tigeot #include <drm/drmP.h> 398621f407SFrançois Tigeot 401dedbd3bSFrançois Tigeot #include "drm_crtc_helper_internal.h" 411dedbd3bSFrançois Tigeot 428621f407SFrançois Tigeot struct drm_dp_aux_dev { 438621f407SFrançois Tigeot unsigned index; 448621f407SFrançois Tigeot struct drm_dp_aux *aux; 458621f407SFrançois Tigeot struct device *dev; 468621f407SFrançois Tigeot struct kref refcount; 478621f407SFrançois Tigeot atomic_t usecount; 488621f407SFrançois Tigeot }; 498621f407SFrançois Tigeot 508621f407SFrançois Tigeot #define DRM_AUX_MINORS 256 518621f407SFrançois Tigeot #define AUX_MAX_OFFSET (1 << 20) 528621f407SFrançois Tigeot #if 0 538621f407SFrançois Tigeot static DEFINE_IDR(aux_idr); 548621f407SFrançois Tigeot static DEFINE_MUTEX(aux_idr_mutex); 558621f407SFrançois Tigeot static struct class *drm_dp_aux_dev_class; 568621f407SFrançois Tigeot static int drm_dev_major = -1; 578621f407SFrançois Tigeot 588621f407SFrançois Tigeot static struct drm_dp_aux_dev *drm_dp_aux_dev_get_by_minor(unsigned index) 598621f407SFrançois Tigeot { 608621f407SFrançois Tigeot struct drm_dp_aux_dev *aux_dev = NULL; 618621f407SFrançois Tigeot 628621f407SFrançois Tigeot mutex_lock(&aux_idr_mutex); 638621f407SFrançois Tigeot aux_dev = idr_find(&aux_idr, index); 648621f407SFrançois Tigeot if (!kref_get_unless_zero(&aux_dev->refcount)) 658621f407SFrançois Tigeot aux_dev = NULL; 668621f407SFrançois Tigeot mutex_unlock(&aux_idr_mutex); 678621f407SFrançois Tigeot 688621f407SFrançois Tigeot return aux_dev; 698621f407SFrançois Tigeot } 708621f407SFrançois Tigeot 718621f407SFrançois Tigeot static struct drm_dp_aux_dev *alloc_drm_dp_aux_dev(struct drm_dp_aux *aux) 728621f407SFrançois Tigeot { 738621f407SFrançois Tigeot struct drm_dp_aux_dev *aux_dev; 748621f407SFrançois Tigeot int index; 758621f407SFrançois Tigeot 768621f407SFrançois Tigeot aux_dev = kzalloc(sizeof(*aux_dev), GFP_KERNEL); 778621f407SFrançois Tigeot if (!aux_dev) 788621f407SFrançois Tigeot return ERR_PTR(-ENOMEM); 798621f407SFrançois Tigeot aux_dev->aux = aux; 808621f407SFrançois Tigeot atomic_set(&aux_dev->usecount, 1); 818621f407SFrançois Tigeot kref_init(&aux_dev->refcount); 828621f407SFrançois Tigeot 838621f407SFrançois Tigeot mutex_lock(&aux_idr_mutex); 848621f407SFrançois Tigeot index = idr_alloc_cyclic(&aux_idr, aux_dev, 0, DRM_AUX_MINORS, 858621f407SFrançois Tigeot GFP_KERNEL); 868621f407SFrançois Tigeot mutex_unlock(&aux_idr_mutex); 878621f407SFrançois Tigeot if (index < 0) { 888621f407SFrançois Tigeot kfree(aux_dev); 898621f407SFrançois Tigeot return ERR_PTR(index); 908621f407SFrançois Tigeot } 918621f407SFrançois Tigeot aux_dev->index = index; 928621f407SFrançois Tigeot 938621f407SFrançois Tigeot return aux_dev; 948621f407SFrançois Tigeot } 958621f407SFrançois Tigeot 968621f407SFrançois Tigeot static void release_drm_dp_aux_dev(struct kref *ref) 978621f407SFrançois Tigeot { 988621f407SFrançois Tigeot struct drm_dp_aux_dev *aux_dev = 998621f407SFrançois Tigeot container_of(ref, struct drm_dp_aux_dev, refcount); 1008621f407SFrançois Tigeot 1018621f407SFrançois Tigeot kfree(aux_dev); 1028621f407SFrançois Tigeot } 1038621f407SFrançois Tigeot 1048621f407SFrançois Tigeot static ssize_t name_show(struct device *dev, 1058621f407SFrançois Tigeot struct device_attribute *attr, char *buf) 1068621f407SFrançois Tigeot { 1078621f407SFrançois Tigeot ssize_t res; 1088621f407SFrançois Tigeot struct drm_dp_aux_dev *aux_dev = 1098621f407SFrançois Tigeot drm_dp_aux_dev_get_by_minor(MINOR(dev->devt)); 1108621f407SFrançois Tigeot 1118621f407SFrançois Tigeot if (!aux_dev) 1128621f407SFrançois Tigeot return -ENODEV; 1138621f407SFrançois Tigeot 1148621f407SFrançois Tigeot res = sprintf(buf, "%s\n", aux_dev->aux->name); 1158621f407SFrançois Tigeot kref_put(&aux_dev->refcount, release_drm_dp_aux_dev); 1168621f407SFrançois Tigeot 1178621f407SFrançois Tigeot return res; 1188621f407SFrançois Tigeot } 1198621f407SFrançois Tigeot static DEVICE_ATTR_RO(name); 1208621f407SFrançois Tigeot 1218621f407SFrançois Tigeot static struct attribute *drm_dp_aux_attrs[] = { 1228621f407SFrançois Tigeot &dev_attr_name.attr, 1238621f407SFrançois Tigeot NULL, 1248621f407SFrançois Tigeot }; 1258621f407SFrançois Tigeot ATTRIBUTE_GROUPS(drm_dp_aux); 1268621f407SFrançois Tigeot 1278621f407SFrançois Tigeot static int auxdev_open(struct inode *inode, struct file *file) 1288621f407SFrançois Tigeot { 1298621f407SFrançois Tigeot unsigned int minor = iminor(inode); 1308621f407SFrançois Tigeot struct drm_dp_aux_dev *aux_dev; 1318621f407SFrançois Tigeot 1328621f407SFrançois Tigeot aux_dev = drm_dp_aux_dev_get_by_minor(minor); 1338621f407SFrançois Tigeot if (!aux_dev) 1348621f407SFrançois Tigeot return -ENODEV; 1358621f407SFrançois Tigeot 1368621f407SFrançois Tigeot file->private_data = aux_dev; 1378621f407SFrançois Tigeot return 0; 1388621f407SFrançois Tigeot } 1398621f407SFrançois Tigeot 1408621f407SFrançois Tigeot static loff_t auxdev_llseek(struct file *file, loff_t offset, int whence) 1418621f407SFrançois Tigeot { 1428621f407SFrançois Tigeot return fixed_size_llseek(file, offset, whence, AUX_MAX_OFFSET); 1438621f407SFrançois Tigeot } 1448621f407SFrançois Tigeot 145*3f2dd94aSFrançois Tigeot static ssize_t auxdev_read_iter(struct kiocb *iocb, struct iov_iter *to) 1468621f407SFrançois Tigeot { 147*3f2dd94aSFrançois Tigeot struct drm_dp_aux_dev *aux_dev = iocb->ki_filp->private_data; 148*3f2dd94aSFrançois Tigeot loff_t pos = iocb->ki_pos; 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 154*3f2dd94aSFrançois Tigeot iov_iter_truncate(to, AUX_MAX_OFFSET - pos); 1558621f407SFrançois Tigeot 156*3f2dd94aSFrançois Tigeot while (iov_iter_count(to)) { 157*3f2dd94aSFrançois Tigeot uint8_t buf[DP_AUX_MAX_PAYLOAD_BYTES]; 158*3f2dd94aSFrançois Tigeot ssize_t todo = min(iov_iter_count(to), sizeof(buf)); 1598621f407SFrançois Tigeot 1608621f407SFrançois Tigeot if (signal_pending(current)) { 161*3f2dd94aSFrançois Tigeot res = -ERESTARTSYS; 162*3f2dd94aSFrançois Tigeot break; 1638621f407SFrançois Tigeot } 1648621f407SFrançois Tigeot 165*3f2dd94aSFrançois Tigeot res = drm_dp_dpcd_read(aux_dev->aux, pos, buf, todo); 166*3f2dd94aSFrançois Tigeot if (res <= 0) 167*3f2dd94aSFrançois Tigeot break; 168*3f2dd94aSFrançois Tigeot 169*3f2dd94aSFrançois Tigeot if (copy_to_iter(buf, res, to) != res) { 170*3f2dd94aSFrançois Tigeot res = -EFAULT; 171*3f2dd94aSFrançois Tigeot break; 1728621f407SFrançois Tigeot } 1738621f407SFrançois Tigeot 174*3f2dd94aSFrançois Tigeot pos += res; 175*3f2dd94aSFrançois Tigeot } 176*3f2dd94aSFrançois Tigeot 177*3f2dd94aSFrançois Tigeot if (pos != iocb->ki_pos) 178*3f2dd94aSFrançois Tigeot res = pos - iocb->ki_pos; 179*3f2dd94aSFrançois Tigeot iocb->ki_pos = pos; 180*3f2dd94aSFrançois Tigeot 1818621f407SFrançois Tigeot atomic_dec(&aux_dev->usecount); 1828621f407SFrançois Tigeot wake_up_atomic_t(&aux_dev->usecount); 1838621f407SFrançois Tigeot return res; 1848621f407SFrançois Tigeot } 1858621f407SFrançois Tigeot 186*3f2dd94aSFrançois Tigeot static ssize_t auxdev_write_iter(struct kiocb *iocb, struct iov_iter *from) 1878621f407SFrançois Tigeot { 188*3f2dd94aSFrançois Tigeot struct drm_dp_aux_dev *aux_dev = iocb->ki_filp->private_data; 189*3f2dd94aSFrançois Tigeot loff_t pos = iocb->ki_pos; 1908621f407SFrançois Tigeot ssize_t res = 0; 1918621f407SFrançois Tigeot 1928621f407SFrançois Tigeot if (!atomic_inc_not_zero(&aux_dev->usecount)) 1938621f407SFrançois Tigeot return -ENODEV; 1948621f407SFrançois Tigeot 195*3f2dd94aSFrançois Tigeot iov_iter_truncate(from, AUX_MAX_OFFSET - pos); 1968621f407SFrançois Tigeot 197*3f2dd94aSFrançois Tigeot while (iov_iter_count(from)) { 198*3f2dd94aSFrançois Tigeot uint8_t buf[DP_AUX_MAX_PAYLOAD_BYTES]; 199*3f2dd94aSFrançois Tigeot ssize_t todo = min(iov_iter_count(from), sizeof(buf)); 2008621f407SFrançois Tigeot 2018621f407SFrançois Tigeot if (signal_pending(current)) { 202*3f2dd94aSFrançois Tigeot res = -ERESTARTSYS; 203*3f2dd94aSFrançois Tigeot break; 2048621f407SFrançois Tigeot } 2058621f407SFrançois Tigeot 206*3f2dd94aSFrançois Tigeot if (!copy_from_iter_full(buf, todo, from)) { 207*3f2dd94aSFrançois Tigeot res = -EFAULT; 208*3f2dd94aSFrançois Tigeot break; 2098621f407SFrançois Tigeot } 2108621f407SFrançois Tigeot 211*3f2dd94aSFrançois Tigeot res = drm_dp_dpcd_write(aux_dev->aux, pos, buf, todo); 212*3f2dd94aSFrançois Tigeot if (res <= 0) 213*3f2dd94aSFrançois Tigeot break; 214*3f2dd94aSFrançois Tigeot 215*3f2dd94aSFrançois Tigeot pos += res; 2168621f407SFrançois Tigeot } 2178621f407SFrançois Tigeot 218*3f2dd94aSFrançois Tigeot if (pos != iocb->ki_pos) 219*3f2dd94aSFrançois Tigeot res = pos - iocb->ki_pos; 220*3f2dd94aSFrançois Tigeot iocb->ki_pos = pos; 221*3f2dd94aSFrançois Tigeot 2228621f407SFrançois Tigeot atomic_dec(&aux_dev->usecount); 2238621f407SFrançois Tigeot wake_up_atomic_t(&aux_dev->usecount); 2248621f407SFrançois Tigeot return res; 2258621f407SFrançois Tigeot } 2268621f407SFrançois Tigeot 2278621f407SFrançois Tigeot static int auxdev_release(struct inode *inode, struct file *file) 2288621f407SFrançois Tigeot { 2298621f407SFrançois Tigeot struct drm_dp_aux_dev *aux_dev = file->private_data; 2308621f407SFrançois Tigeot 2318621f407SFrançois Tigeot kref_put(&aux_dev->refcount, release_drm_dp_aux_dev); 2328621f407SFrançois Tigeot return 0; 2338621f407SFrançois Tigeot } 2348621f407SFrançois Tigeot 2358621f407SFrançois Tigeot static const struct file_operations auxdev_fops = { 2368621f407SFrançois Tigeot .owner = THIS_MODULE, 2378621f407SFrançois Tigeot .llseek = auxdev_llseek, 238*3f2dd94aSFrançois Tigeot .read_iter = auxdev_read_iter, 239*3f2dd94aSFrançois Tigeot .write_iter = auxdev_write_iter, 2408621f407SFrançois Tigeot .open = auxdev_open, 2418621f407SFrançois Tigeot .release = auxdev_release, 2428621f407SFrançois Tigeot }; 2438621f407SFrançois Tigeot 2448621f407SFrançois Tigeot #define to_auxdev(d) container_of(d, struct drm_dp_aux_dev, aux) 2458621f407SFrançois Tigeot 2468621f407SFrançois Tigeot static struct drm_dp_aux_dev *drm_dp_aux_dev_get_by_aux(struct drm_dp_aux *aux) 2478621f407SFrançois Tigeot { 2488621f407SFrançois Tigeot struct drm_dp_aux_dev *iter, *aux_dev = NULL; 2498621f407SFrançois Tigeot int id; 2508621f407SFrançois Tigeot 2518621f407SFrançois Tigeot /* don't increase kref count here because this function should only be 2528621f407SFrançois Tigeot * used by drm_dp_aux_unregister_devnode. Thus, it will always have at 2538621f407SFrançois Tigeot * least one reference - the one that drm_dp_aux_register_devnode 2548621f407SFrançois Tigeot * created 2558621f407SFrançois Tigeot */ 2568621f407SFrançois Tigeot mutex_lock(&aux_idr_mutex); 2578621f407SFrançois Tigeot idr_for_each_entry(&aux_idr, iter, id) { 2588621f407SFrançois Tigeot if (iter->aux == aux) { 2598621f407SFrançois Tigeot aux_dev = iter; 2608621f407SFrançois Tigeot break; 2618621f407SFrançois Tigeot } 2628621f407SFrançois Tigeot } 2638621f407SFrançois Tigeot mutex_unlock(&aux_idr_mutex); 2648621f407SFrançois Tigeot return aux_dev; 2658621f407SFrançois Tigeot } 2668621f407SFrançois Tigeot 2678621f407SFrançois Tigeot void drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux) 2688621f407SFrançois Tigeot { 2698621f407SFrançois Tigeot struct drm_dp_aux_dev *aux_dev; 2708621f407SFrançois Tigeot unsigned int minor; 2718621f407SFrançois Tigeot 2728621f407SFrançois Tigeot aux_dev = drm_dp_aux_dev_get_by_aux(aux); 2738621f407SFrançois Tigeot if (!aux_dev) /* attach must have failed */ 2748621f407SFrançois Tigeot return; 2758621f407SFrançois Tigeot 2768621f407SFrançois Tigeot mutex_lock(&aux_idr_mutex); 2778621f407SFrançois Tigeot idr_remove(&aux_idr, aux_dev->index); 2788621f407SFrançois Tigeot mutex_unlock(&aux_idr_mutex); 2798621f407SFrançois Tigeot 2808621f407SFrançois Tigeot atomic_dec(&aux_dev->usecount); 281*3f2dd94aSFrançois Tigeot wait_on_atomic_t(&aux_dev->usecount, atomic_t_wait, 2828621f407SFrançois Tigeot TASK_UNINTERRUPTIBLE); 2838621f407SFrançois Tigeot 2848621f407SFrançois Tigeot minor = aux_dev->index; 2858621f407SFrançois Tigeot if (aux_dev->dev) 2868621f407SFrançois Tigeot device_destroy(drm_dp_aux_dev_class, 2878621f407SFrançois Tigeot MKDEV(drm_dev_major, minor)); 2888621f407SFrançois Tigeot 2898621f407SFrançois Tigeot DRM_DEBUG("drm_dp_aux_dev: aux [%s] unregistering\n", aux->name); 2908621f407SFrançois Tigeot kref_put(&aux_dev->refcount, release_drm_dp_aux_dev); 2918621f407SFrançois Tigeot } 2928621f407SFrançois Tigeot 2938621f407SFrançois Tigeot int drm_dp_aux_register_devnode(struct drm_dp_aux *aux) 2948621f407SFrançois Tigeot { 2958621f407SFrançois Tigeot struct drm_dp_aux_dev *aux_dev; 2968621f407SFrançois Tigeot int res; 2978621f407SFrançois Tigeot 2988621f407SFrançois Tigeot aux_dev = alloc_drm_dp_aux_dev(aux); 2998621f407SFrançois Tigeot if (IS_ERR(aux_dev)) 3008621f407SFrançois Tigeot return PTR_ERR(aux_dev); 3018621f407SFrançois Tigeot 3028621f407SFrançois Tigeot aux_dev->dev = device_create(drm_dp_aux_dev_class, aux->dev, 3038621f407SFrançois Tigeot MKDEV(drm_dev_major, aux_dev->index), NULL, 3048621f407SFrançois Tigeot "drm_dp_aux%d", aux_dev->index); 3058621f407SFrançois Tigeot if (IS_ERR(aux_dev->dev)) { 3068621f407SFrançois Tigeot res = PTR_ERR(aux_dev->dev); 3078621f407SFrançois Tigeot aux_dev->dev = NULL; 3088621f407SFrançois Tigeot goto error; 3098621f407SFrançois Tigeot } 3108621f407SFrançois Tigeot 3118621f407SFrançois Tigeot DRM_DEBUG("drm_dp_aux_dev: aux [%s] registered as minor %d\n", 3128621f407SFrançois Tigeot aux->name, aux_dev->index); 3138621f407SFrançois Tigeot return 0; 3148621f407SFrançois Tigeot error: 3158621f407SFrançois Tigeot drm_dp_aux_unregister_devnode(aux); 3168621f407SFrançois Tigeot return res; 3178621f407SFrançois Tigeot } 3188621f407SFrançois Tigeot 3198621f407SFrançois Tigeot int drm_dp_aux_dev_init(void) 3208621f407SFrançois Tigeot { 3218621f407SFrançois Tigeot int res; 3228621f407SFrançois Tigeot 3238621f407SFrançois Tigeot drm_dp_aux_dev_class = class_create(THIS_MODULE, "drm_dp_aux_dev"); 3248621f407SFrançois Tigeot if (IS_ERR(drm_dp_aux_dev_class)) { 3251dedbd3bSFrançois Tigeot return PTR_ERR(drm_dp_aux_dev_class); 3268621f407SFrançois Tigeot } 3278621f407SFrançois Tigeot drm_dp_aux_dev_class->dev_groups = drm_dp_aux_groups; 3288621f407SFrançois Tigeot 3298621f407SFrançois Tigeot res = register_chrdev(0, "aux", &auxdev_fops); 3308621f407SFrançois Tigeot if (res < 0) 3318621f407SFrançois Tigeot goto out; 3328621f407SFrançois Tigeot drm_dev_major = res; 3338621f407SFrançois Tigeot 3348621f407SFrançois Tigeot return 0; 3358621f407SFrançois Tigeot out: 3368621f407SFrançois Tigeot class_destroy(drm_dp_aux_dev_class); 3378621f407SFrançois Tigeot return res; 3388621f407SFrançois Tigeot } 3398621f407SFrançois Tigeot 3408621f407SFrançois Tigeot void drm_dp_aux_dev_exit(void) 3418621f407SFrançois Tigeot { 3428621f407SFrançois Tigeot unregister_chrdev(drm_dev_major, "aux"); 3438621f407SFrançois Tigeot class_destroy(drm_dp_aux_dev_class); 3448621f407SFrançois Tigeot } 3451e12ee3bSFrançois Tigeot #endif /* 0 */ 346