11.3Sriastrad/*	$NetBSD: drm_dp_aux_dev.c,v 1.3 2021/12/19 11:06:54 riastradh Exp $	*/
21.1Sriastrad
31.1Sriastrad/*
41.1Sriastrad * Copyright © 2015 Intel Corporation
51.1Sriastrad *
61.1Sriastrad * Permission is hereby granted, free of charge, to any person obtaining a
71.1Sriastrad * copy of this software and associated documentation files (the "Software"),
81.1Sriastrad * to deal in the Software without restriction, including without limitation
91.1Sriastrad * the rights to use, copy, modify, merge, publish, distribute, sublicense,
101.1Sriastrad * and/or sell copies of the Software, and to permit persons to whom the
111.1Sriastrad * Software is furnished to do so, subject to the following conditions:
121.1Sriastrad *
131.1Sriastrad * The above copyright notice and this permission notice (including the next
141.1Sriastrad * paragraph) shall be included in all copies or substantial portions of the
151.1Sriastrad * Software.
161.1Sriastrad *
171.1Sriastrad * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
181.1Sriastrad * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
191.1Sriastrad * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
201.1Sriastrad * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
211.1Sriastrad * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
221.1Sriastrad * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
231.1Sriastrad * IN THE SOFTWARE.
241.1Sriastrad *
251.1Sriastrad * Authors:
261.1Sriastrad *    Rafael Antognolli <rafael.antognolli@intel.com>
271.1Sriastrad *
281.1Sriastrad */
291.1Sriastrad
301.1Sriastrad#include <sys/cdefs.h>
311.3Sriastrad__KERNEL_RCSID(0, "$NetBSD: drm_dp_aux_dev.c,v 1.3 2021/12/19 11:06:54 riastradh Exp $");
321.1Sriastrad
331.1Sriastrad#include <linux/device.h>
341.1Sriastrad#include <linux/fs.h>
351.1Sriastrad#include <linux/init.h>
361.1Sriastrad#include <linux/kernel.h>
371.1Sriastrad#include <linux/module.h>
381.1Sriastrad#include <linux/sched/signal.h>
391.1Sriastrad#include <linux/slab.h>
401.1Sriastrad#include <linux/uaccess.h>
411.1Sriastrad#include <linux/uio.h>
421.1Sriastrad
431.1Sriastrad#include <drm/drm_crtc.h>
441.1Sriastrad#include <drm/drm_dp_helper.h>
451.1Sriastrad#include <drm/drm_dp_mst_helper.h>
461.1Sriastrad#include <drm/drm_print.h>
471.1Sriastrad
481.1Sriastrad#include "drm_crtc_helper_internal.h"
491.1Sriastrad
501.1Sriastradstruct drm_dp_aux_dev {
511.1Sriastrad	unsigned index;
521.1Sriastrad	struct drm_dp_aux *aux;
531.1Sriastrad	struct device *dev;
541.1Sriastrad	struct kref refcount;
551.1Sriastrad	atomic_t usecount;
561.1Sriastrad};
571.1Sriastrad
581.1Sriastrad#define DRM_AUX_MINORS	256
591.1Sriastrad#define AUX_MAX_OFFSET	(1 << 20)
601.1Sriastradstatic DEFINE_IDR(aux_idr);
611.1Sriastradstatic DEFINE_MUTEX(aux_idr_mutex);
621.1Sriastradstatic struct class *drm_dp_aux_dev_class;
631.1Sriastradstatic int drm_dev_major = -1;
641.1Sriastrad
651.1Sriastradstatic struct drm_dp_aux_dev *drm_dp_aux_dev_get_by_minor(unsigned index)
661.1Sriastrad{
671.1Sriastrad	struct drm_dp_aux_dev *aux_dev = NULL;
681.1Sriastrad
691.1Sriastrad	mutex_lock(&aux_idr_mutex);
701.1Sriastrad	aux_dev = idr_find(&aux_idr, index);
711.1Sriastrad	if (!kref_get_unless_zero(&aux_dev->refcount))
721.1Sriastrad		aux_dev = NULL;
731.1Sriastrad	mutex_unlock(&aux_idr_mutex);
741.1Sriastrad
751.1Sriastrad	return aux_dev;
761.1Sriastrad}
771.1Sriastrad
781.1Sriastradstatic struct drm_dp_aux_dev *alloc_drm_dp_aux_dev(struct drm_dp_aux *aux)
791.1Sriastrad{
801.1Sriastrad	struct drm_dp_aux_dev *aux_dev;
811.1Sriastrad	int index;
821.1Sriastrad
831.1Sriastrad	aux_dev = kzalloc(sizeof(*aux_dev), GFP_KERNEL);
841.1Sriastrad	if (!aux_dev)
851.1Sriastrad		return ERR_PTR(-ENOMEM);
861.1Sriastrad	aux_dev->aux = aux;
871.1Sriastrad	atomic_set(&aux_dev->usecount, 1);
881.1Sriastrad	kref_init(&aux_dev->refcount);
891.1Sriastrad
901.3Sriastrad	idr_preload(GFP_KERNEL);
911.1Sriastrad	mutex_lock(&aux_idr_mutex);
921.1Sriastrad	index = idr_alloc(&aux_idr, aux_dev, 0, DRM_AUX_MINORS, GFP_KERNEL);
931.1Sriastrad	mutex_unlock(&aux_idr_mutex);
941.3Sriastrad	idr_preload_end();
951.1Sriastrad	if (index < 0) {
961.1Sriastrad		kfree(aux_dev);
971.1Sriastrad		return ERR_PTR(index);
981.1Sriastrad	}
991.1Sriastrad	aux_dev->index = index;
1001.1Sriastrad
1011.1Sriastrad	return aux_dev;
1021.1Sriastrad}
1031.1Sriastrad
1041.1Sriastradstatic void release_drm_dp_aux_dev(struct kref *ref)
1051.1Sriastrad{
1061.1Sriastrad	struct drm_dp_aux_dev *aux_dev =
1071.1Sriastrad		container_of(ref, struct drm_dp_aux_dev, refcount);
1081.1Sriastrad
1091.1Sriastrad	kfree(aux_dev);
1101.1Sriastrad}
1111.1Sriastrad
1121.1Sriastradstatic ssize_t name_show(struct device *dev,
1131.1Sriastrad			 struct device_attribute *attr, char *buf)
1141.1Sriastrad{
1151.1Sriastrad	ssize_t res;
1161.1Sriastrad	struct drm_dp_aux_dev *aux_dev =
1171.1Sriastrad		drm_dp_aux_dev_get_by_minor(MINOR(dev->devt));
1181.1Sriastrad
1191.1Sriastrad	if (!aux_dev)
1201.1Sriastrad		return -ENODEV;
1211.1Sriastrad
1221.1Sriastrad	res = sprintf(buf, "%s\n", aux_dev->aux->name);
1231.1Sriastrad	kref_put(&aux_dev->refcount, release_drm_dp_aux_dev);
1241.1Sriastrad
1251.1Sriastrad	return res;
1261.1Sriastrad}
1271.1Sriastradstatic DEVICE_ATTR_RO(name);
1281.1Sriastrad
1291.1Sriastradstatic struct attribute *drm_dp_aux_attrs[] = {
1301.1Sriastrad	&dev_attr_name.attr,
1311.1Sriastrad	NULL,
1321.1Sriastrad};
1331.1SriastradATTRIBUTE_GROUPS(drm_dp_aux);
1341.1Sriastrad
1351.1Sriastradstatic int auxdev_open(struct inode *inode, struct file *file)
1361.1Sriastrad{
1371.1Sriastrad	unsigned int minor = iminor(inode);
1381.1Sriastrad	struct drm_dp_aux_dev *aux_dev;
1391.1Sriastrad
1401.1Sriastrad	aux_dev = drm_dp_aux_dev_get_by_minor(minor);
1411.1Sriastrad	if (!aux_dev)
1421.1Sriastrad		return -ENODEV;
1431.1Sriastrad
1441.1Sriastrad	file->private_data = aux_dev;
1451.1Sriastrad	return 0;
1461.1Sriastrad}
1471.1Sriastrad
1481.1Sriastradstatic loff_t auxdev_llseek(struct file *file, loff_t offset, int whence)
1491.1Sriastrad{
1501.1Sriastrad	return fixed_size_llseek(file, offset, whence, AUX_MAX_OFFSET);
1511.1Sriastrad}
1521.1Sriastrad
1531.1Sriastradstatic ssize_t auxdev_read_iter(struct kiocb *iocb, struct iov_iter *to)
1541.1Sriastrad{
1551.1Sriastrad	struct drm_dp_aux_dev *aux_dev = iocb->ki_filp->private_data;
1561.1Sriastrad	loff_t pos = iocb->ki_pos;
1571.1Sriastrad	ssize_t res = 0;
1581.1Sriastrad
1591.1Sriastrad	if (!atomic_inc_not_zero(&aux_dev->usecount))
1601.1Sriastrad		return -ENODEV;
1611.1Sriastrad
1621.1Sriastrad	iov_iter_truncate(to, AUX_MAX_OFFSET - pos);
1631.1Sriastrad
1641.1Sriastrad	while (iov_iter_count(to)) {
1651.1Sriastrad		uint8_t buf[DP_AUX_MAX_PAYLOAD_BYTES];
1661.1Sriastrad		ssize_t todo = min(iov_iter_count(to), sizeof(buf));
1671.1Sriastrad
1681.1Sriastrad		if (signal_pending(current)) {
1691.1Sriastrad			res = -ERESTARTSYS;
1701.1Sriastrad			break;
1711.1Sriastrad		}
1721.1Sriastrad
1731.1Sriastrad		res = drm_dp_dpcd_read(aux_dev->aux, pos, buf, todo);
1741.1Sriastrad
1751.1Sriastrad		if (res <= 0)
1761.1Sriastrad			break;
1771.1Sriastrad
1781.1Sriastrad		if (copy_to_iter(buf, res, to) != res) {
1791.1Sriastrad			res = -EFAULT;
1801.1Sriastrad			break;
1811.1Sriastrad		}
1821.1Sriastrad
1831.1Sriastrad		pos += res;
1841.1Sriastrad	}
1851.1Sriastrad
1861.1Sriastrad	if (pos != iocb->ki_pos)
1871.1Sriastrad		res = pos - iocb->ki_pos;
1881.1Sriastrad	iocb->ki_pos = pos;
1891.1Sriastrad
1901.1Sriastrad	if (atomic_dec_and_test(&aux_dev->usecount))
1911.1Sriastrad		wake_up_var(&aux_dev->usecount);
1921.1Sriastrad
1931.1Sriastrad	return res;
1941.1Sriastrad}
1951.1Sriastrad
1961.1Sriastradstatic ssize_t auxdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
1971.1Sriastrad{
1981.1Sriastrad	struct drm_dp_aux_dev *aux_dev = iocb->ki_filp->private_data;
1991.1Sriastrad	loff_t pos = iocb->ki_pos;
2001.1Sriastrad	ssize_t res = 0;
2011.1Sriastrad
2021.1Sriastrad	if (!atomic_inc_not_zero(&aux_dev->usecount))
2031.1Sriastrad		return -ENODEV;
2041.1Sriastrad
2051.1Sriastrad	iov_iter_truncate(from, AUX_MAX_OFFSET - pos);
2061.1Sriastrad
2071.1Sriastrad	while (iov_iter_count(from)) {
2081.1Sriastrad		uint8_t buf[DP_AUX_MAX_PAYLOAD_BYTES];
2091.1Sriastrad		ssize_t todo = min(iov_iter_count(from), sizeof(buf));
2101.1Sriastrad
2111.1Sriastrad		if (signal_pending(current)) {
2121.1Sriastrad			res = -ERESTARTSYS;
2131.1Sriastrad			break;
2141.1Sriastrad		}
2151.1Sriastrad
2161.1Sriastrad		if (!copy_from_iter_full(buf, todo, from)) {
2171.1Sriastrad			res = -EFAULT;
2181.1Sriastrad			break;
2191.1Sriastrad		}
2201.1Sriastrad
2211.1Sriastrad		res = drm_dp_dpcd_write(aux_dev->aux, pos, buf, todo);
2221.1Sriastrad
2231.1Sriastrad		if (res <= 0)
2241.1Sriastrad			break;
2251.1Sriastrad
2261.1Sriastrad		pos += res;
2271.1Sriastrad	}
2281.1Sriastrad
2291.1Sriastrad	if (pos != iocb->ki_pos)
2301.1Sriastrad		res = pos - iocb->ki_pos;
2311.1Sriastrad	iocb->ki_pos = pos;
2321.1Sriastrad
2331.1Sriastrad	if (atomic_dec_and_test(&aux_dev->usecount))
2341.1Sriastrad		wake_up_var(&aux_dev->usecount);
2351.1Sriastrad
2361.1Sriastrad	return res;
2371.1Sriastrad}
2381.1Sriastrad
2391.1Sriastradstatic int auxdev_release(struct inode *inode, struct file *file)
2401.1Sriastrad{
2411.1Sriastrad	struct drm_dp_aux_dev *aux_dev = file->private_data;
2421.1Sriastrad
2431.1Sriastrad	kref_put(&aux_dev->refcount, release_drm_dp_aux_dev);
2441.1Sriastrad	return 0;
2451.1Sriastrad}
2461.1Sriastrad
2471.1Sriastradstatic const struct file_operations auxdev_fops = {
2481.1Sriastrad	.owner		= THIS_MODULE,
2491.1Sriastrad	.llseek		= auxdev_llseek,
2501.1Sriastrad	.read_iter	= auxdev_read_iter,
2511.1Sriastrad	.write_iter	= auxdev_write_iter,
2521.1Sriastrad	.open		= auxdev_open,
2531.1Sriastrad	.release	= auxdev_release,
2541.1Sriastrad};
2551.1Sriastrad
2561.1Sriastrad#define to_auxdev(d) container_of(d, struct drm_dp_aux_dev, aux)
2571.1Sriastrad
2581.1Sriastradstatic struct drm_dp_aux_dev *drm_dp_aux_dev_get_by_aux(struct drm_dp_aux *aux)
2591.1Sriastrad{
2601.1Sriastrad	struct drm_dp_aux_dev *iter, *aux_dev = NULL;
2611.1Sriastrad	int id;
2621.1Sriastrad
2631.1Sriastrad	/* don't increase kref count here because this function should only be
2641.1Sriastrad	 * used by drm_dp_aux_unregister_devnode. Thus, it will always have at
2651.1Sriastrad	 * least one reference - the one that drm_dp_aux_register_devnode
2661.1Sriastrad	 * created
2671.1Sriastrad	 */
2681.1Sriastrad	mutex_lock(&aux_idr_mutex);
2691.1Sriastrad	idr_for_each_entry(&aux_idr, iter, id) {
2701.1Sriastrad		if (iter->aux == aux) {
2711.1Sriastrad			aux_dev = iter;
2721.1Sriastrad			break;
2731.1Sriastrad		}
2741.1Sriastrad	}
2751.1Sriastrad	mutex_unlock(&aux_idr_mutex);
2761.1Sriastrad	return aux_dev;
2771.1Sriastrad}
2781.1Sriastrad
2791.1Sriastradvoid drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux)
2801.1Sriastrad{
2811.1Sriastrad	struct drm_dp_aux_dev *aux_dev;
2821.1Sriastrad	unsigned int minor;
2831.1Sriastrad
2841.1Sriastrad	aux_dev = drm_dp_aux_dev_get_by_aux(aux);
2851.1Sriastrad	if (!aux_dev) /* attach must have failed */
2861.1Sriastrad		return;
2871.1Sriastrad
2881.1Sriastrad	mutex_lock(&aux_idr_mutex);
2891.1Sriastrad	idr_remove(&aux_idr, aux_dev->index);
2901.1Sriastrad	mutex_unlock(&aux_idr_mutex);
2911.1Sriastrad
2921.1Sriastrad	atomic_dec(&aux_dev->usecount);
2931.1Sriastrad	wait_var_event(&aux_dev->usecount, !atomic_read(&aux_dev->usecount));
2941.1Sriastrad
2951.1Sriastrad	minor = aux_dev->index;
2961.1Sriastrad	if (aux_dev->dev)
2971.1Sriastrad		device_destroy(drm_dp_aux_dev_class,
2981.1Sriastrad			       MKDEV(drm_dev_major, minor));
2991.1Sriastrad
3001.1Sriastrad	DRM_DEBUG("drm_dp_aux_dev: aux [%s] unregistering\n", aux->name);
3011.1Sriastrad	kref_put(&aux_dev->refcount, release_drm_dp_aux_dev);
3021.1Sriastrad}
3031.1Sriastrad
3041.1Sriastradint drm_dp_aux_register_devnode(struct drm_dp_aux *aux)
3051.1Sriastrad{
3061.1Sriastrad	struct drm_dp_aux_dev *aux_dev;
3071.1Sriastrad	int res;
3081.1Sriastrad
3091.1Sriastrad	aux_dev = alloc_drm_dp_aux_dev(aux);
3101.1Sriastrad	if (IS_ERR(aux_dev))
3111.1Sriastrad		return PTR_ERR(aux_dev);
3121.1Sriastrad
3131.1Sriastrad	aux_dev->dev = device_create(drm_dp_aux_dev_class, aux->dev,
3141.1Sriastrad				     MKDEV(drm_dev_major, aux_dev->index), NULL,
3151.1Sriastrad				     "drm_dp_aux%d", aux_dev->index);
3161.1Sriastrad	if (IS_ERR(aux_dev->dev)) {
3171.1Sriastrad		res = PTR_ERR(aux_dev->dev);
3181.1Sriastrad		aux_dev->dev = NULL;
3191.1Sriastrad		goto error;
3201.1Sriastrad	}
3211.1Sriastrad
3221.1Sriastrad	DRM_DEBUG("drm_dp_aux_dev: aux [%s] registered as minor %d\n",
3231.1Sriastrad		  aux->name, aux_dev->index);
3241.1Sriastrad	return 0;
3251.1Sriastraderror:
3261.1Sriastrad	drm_dp_aux_unregister_devnode(aux);
3271.1Sriastrad	return res;
3281.1Sriastrad}
3291.1Sriastrad
3301.1Sriastradint drm_dp_aux_dev_init(void)
3311.1Sriastrad{
3321.1Sriastrad	int res;
3331.1Sriastrad
3341.1Sriastrad	drm_dp_aux_dev_class = class_create(THIS_MODULE, "drm_dp_aux_dev");
3351.1Sriastrad	if (IS_ERR(drm_dp_aux_dev_class)) {
3361.1Sriastrad		return PTR_ERR(drm_dp_aux_dev_class);
3371.1Sriastrad	}
3381.1Sriastrad	drm_dp_aux_dev_class->dev_groups = drm_dp_aux_groups;
3391.1Sriastrad
3401.1Sriastrad	res = register_chrdev(0, "aux", &auxdev_fops);
3411.1Sriastrad	if (res < 0)
3421.1Sriastrad		goto out;
3431.1Sriastrad	drm_dev_major = res;
3441.1Sriastrad
3451.1Sriastrad	return 0;
3461.1Sriastradout:
3471.1Sriastrad	class_destroy(drm_dp_aux_dev_class);
3481.1Sriastrad	return res;
3491.1Sriastrad}
3501.1Sriastrad
3511.1Sriastradvoid drm_dp_aux_dev_exit(void)
3521.1Sriastrad{
3531.1Sriastrad	unregister_chrdev(drm_dev_major, "aux");
3541.1Sriastrad	class_destroy(drm_dp_aux_dev_class);
3551.1Sriastrad}
356