Commit 13d69674 authored by Rodrigo Vivi's avatar Rodrigo Vivi
Browse files

Merge tag 'drm-misc-intel-oob-hotplug-v1' of...

Merge tag 'drm-misc-intel-oob-hotplug-v1' of git://git.kernel.org/pub/scm/linux/kernel/git/hansg/linux

 into drm-intel-next

Topic branch for drm-misc / drm-intel for OOB hotplug support for Type-C connectors

Signed-off-by: default avatarRodrigo Vivi <rodrigo.vivi@intel.com>

# -----BEGIN PGP SIGNATURE-----
#
# iQFIBAABCAAyFiEEuvA7XScYQRpenhd+kuxHeUQDJ9wFAmEf5msUHGhkZWdvZWRl
# QHJlZGhhdC5jb20ACgkQkuxHeUQDJ9xUFQgAtBkpLk/oBn6uRcIXCc8KIGs5NqWG
# orVB7c1Ilsd8jdsap6uD2fVVlS7fn5I6hkiyjdRz5A96aLxkF7oCGLATmxGtYd1k
# lZTUOLXGdl2ye798vuwaO8UzpN7s2yiIaI44GCGLT5Qwrpq9D1tykY3ggXH/03t2
# Z8xOB+XLKbcibnWVQL/4Fz+cQ3KzJ8lo10oiZ131b1ytwf/zdKx0fA3yrPA9C0Kv
# V3dbWxYMWfkO+IUieZgMzl2LrbChp8fC6zmPJYdvlpB7AZXrOKD+4K8JdlKPMZNE
# hi5xF4X1tPeTiOo1jju5JN2hnjsc30JuBLemDcV7CLeL01Y4VLdAwHPwyw==
# =Zar+
# -----END PGP SIGNATURE-----
# gpg: Signature made Fri 20 Aug 2021 01:29:15 PM EDT
# gpg:                using RSA key BAF03B5D2718411A5E9E177E92EC4779440327DC
# gpg:                issuer "hdegoede@redhat.com"
# gpg: Can't check signature: No public key
From: Hans de Goede <hdegoede@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/34f13e21-9b1a-5f54-7e03-9705a6b51428@redhat.com
parents 3a8e7fd6 7f811394
Loading
Loading
Loading
Loading
+79 −0
Original line number Diff line number Diff line
@@ -65,6 +65,14 @@
 * support can instead use e.g. drm_helper_hpd_irq_event().
 */

/*
 * Global connector list for drm_connector_find_by_fwnode().
 * Note drm_connector_[un]register() first take connector->lock and then
 * take the connector_list_lock.
 */
static DEFINE_MUTEX(connector_list_lock);
static LIST_HEAD(connector_list);

struct drm_conn_prop_enum_list {
	int type;
	const char *name;
@@ -267,6 +275,7 @@ int drm_connector_init(struct drm_device *dev,
		goto out_put_type_id;
	}

	INIT_LIST_HEAD(&connector->global_connector_list_entry);
	INIT_LIST_HEAD(&connector->probed_modes);
	INIT_LIST_HEAD(&connector->modes);
	mutex_init(&connector->mutex);
@@ -474,6 +483,8 @@ void drm_connector_cleanup(struct drm_connector *connector)
	drm_mode_object_unregister(dev, &connector->base);
	kfree(connector->name);
	connector->name = NULL;
	fwnode_handle_put(connector->fwnode);
	connector->fwnode = NULL;
	spin_lock_irq(&dev->mode_config.connector_list_lock);
	list_del(&connector->head);
	dev->mode_config.num_connector--;
@@ -532,6 +543,9 @@ int drm_connector_register(struct drm_connector *connector)
	/* Let userspace know we have a new connector */
	drm_sysfs_hotplug_event(connector->dev);

	mutex_lock(&connector_list_lock);
	list_add_tail(&connector->global_connector_list_entry, &connector_list);
	mutex_unlock(&connector_list_lock);
	goto unlock;

err_debugfs:
@@ -560,6 +574,10 @@ void drm_connector_unregister(struct drm_connector *connector)
		return;
	}

	mutex_lock(&connector_list_lock);
	list_del_init(&connector->global_connector_list_entry);
	mutex_unlock(&connector_list_lock);

	if (connector->funcs->early_unregister)
		connector->funcs->early_unregister(connector);

@@ -2543,6 +2561,67 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
	return ret;
}

/**
 * drm_connector_find_by_fwnode - Find a connector based on the associated fwnode
 * @fwnode: fwnode for which to find the matching drm_connector
 *
 * This functions looks up a drm_connector based on its associated fwnode. When
 * a connector is found a reference to the connector is returned. The caller must
 * call drm_connector_put() to release this reference when it is done with the
 * connector.
 *
 * Returns: A reference to the found connector or an ERR_PTR().
 */
struct drm_connector *drm_connector_find_by_fwnode(struct fwnode_handle *fwnode)
{
	struct drm_connector *connector, *found = ERR_PTR(-ENODEV);

	if (!fwnode)
		return ERR_PTR(-ENODEV);

	mutex_lock(&connector_list_lock);

	list_for_each_entry(connector, &connector_list, global_connector_list_entry) {
		if (connector->fwnode == fwnode ||
		    (connector->fwnode && connector->fwnode->secondary == fwnode)) {
			drm_connector_get(connector);
			found = connector;
			break;
		}
	}

	mutex_unlock(&connector_list_lock);

	return found;
}

/**
 * drm_connector_oob_hotplug_event - Report out-of-band hotplug event to connector
 * @connector: connector to report the event on
 *
 * On some hardware a hotplug event notification may come from outside the display
 * driver / device. An example of this is some USB Type-C setups where the hardware
 * muxes the DisplayPort data and aux-lines but does not pass the altmode HPD
 * status bit to the GPU's DP HPD pin.
 *
 * This function can be used to report these out-of-band events after obtaining
 * a drm_connector reference through calling drm_connector_find_by_fwnode().
 */
void drm_connector_oob_hotplug_event(struct fwnode_handle *connector_fwnode)
{
	struct drm_connector *connector;

	connector = drm_connector_find_by_fwnode(connector_fwnode);
	if (IS_ERR(connector))
		return;

	if (connector->funcs->oob_hotplug_event)
		connector->funcs->oob_hotplug_event(connector);

	drm_connector_put(connector);
}
EXPORT_SYMBOL(drm_connector_oob_hotplug_event);


/**
 * DOC: Tile group
+2 −0
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ struct drm_property;
struct edid;
struct kref;
struct work_struct;
struct fwnode_handle;

/* drm_crtc.c */
int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj,
@@ -186,6 +187,7 @@ int drm_connector_set_obj_prop(struct drm_mode_object *obj,
int drm_connector_create_standard_properties(struct drm_device *dev);
const char *drm_get_connector_force_name(enum drm_connector_force force);
void drm_connector_free_work_fn(struct work_struct *work);
struct drm_connector *drm_connector_find_by_fwnode(struct fwnode_handle *fwnode);

/* IOCTL */
int drm_connector_property_set_ioctl(struct drm_device *dev,
+74 −13
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@
 * Copyright (c) 2003-2004 IBM Corp.
 */

#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/export.h>
@@ -50,8 +51,45 @@ static struct device_type drm_sysfs_device_minor = {
	.name = "drm_minor"
};

static struct device_type drm_sysfs_device_connector = {
	.name = "drm_connector",
};

struct class *drm_class;

#ifdef CONFIG_ACPI
static bool drm_connector_acpi_bus_match(struct device *dev)
{
	return dev->type == &drm_sysfs_device_connector;
}

static struct acpi_device *drm_connector_acpi_find_companion(struct device *dev)
{
	struct drm_connector *connector = to_drm_connector(dev);

	return to_acpi_device_node(connector->fwnode);
}

static struct acpi_bus_type drm_connector_acpi_bus = {
	.name = "drm_connector",
	.match = drm_connector_acpi_bus_match,
	.find_companion = drm_connector_acpi_find_companion,
};

static void drm_sysfs_acpi_register(void)
{
	register_acpi_bus_type(&drm_connector_acpi_bus);
}

static void drm_sysfs_acpi_unregister(void)
{
	unregister_acpi_bus_type(&drm_connector_acpi_bus);
}
#else
static void drm_sysfs_acpi_register(void) { }
static void drm_sysfs_acpi_unregister(void) { }
#endif

static char *drm_devnode(struct device *dev, umode_t *mode)
{
	return kasprintf(GFP_KERNEL, "dri/%s", dev_name(dev));
@@ -85,6 +123,8 @@ int drm_sysfs_init(void)
	}

	drm_class->devnode = drm_devnode;

	drm_sysfs_acpi_register();
	return 0;
}

@@ -97,11 +137,17 @@ void drm_sysfs_destroy(void)
{
	if (IS_ERR_OR_NULL(drm_class))
		return;
	drm_sysfs_acpi_unregister();
	class_remove_file(drm_class, &class_attr_version.attr);
	class_destroy(drm_class);
	drm_class = NULL;
}

static void drm_sysfs_release(struct device *dev)
{
	kfree(dev);
}

/*
 * Connector properties
 */
@@ -273,27 +319,47 @@ static const struct attribute_group *connector_dev_groups[] = {
int drm_sysfs_connector_add(struct drm_connector *connector)
{
	struct drm_device *dev = connector->dev;
	struct device *kdev;
	int r;

	if (connector->kdev)
		return 0;

	connector->kdev =
		device_create_with_groups(drm_class, dev->primary->kdev, 0,
					  connector, connector_dev_groups,
					  "card%d-%s", dev->primary->index,
					  connector->name);
	kdev = kzalloc(sizeof(*kdev), GFP_KERNEL);
	if (!kdev)
		return -ENOMEM;

	device_initialize(kdev);
	kdev->class = drm_class;
	kdev->type = &drm_sysfs_device_connector;
	kdev->parent = dev->primary->kdev;
	kdev->groups = connector_dev_groups;
	kdev->release = drm_sysfs_release;
	dev_set_drvdata(kdev, connector);

	r = dev_set_name(kdev, "card%d-%s", dev->primary->index, connector->name);
	if (r)
		goto err_free;

	DRM_DEBUG("adding \"%s\" to sysfs\n",
		  connector->name);

	if (IS_ERR(connector->kdev)) {
		DRM_ERROR("failed to register connector device: %ld\n", PTR_ERR(connector->kdev));
		return PTR_ERR(connector->kdev);
	r = device_add(kdev);
	if (r) {
		drm_err(dev, "failed to register connector device: %d\n", r);
		goto err_free;
	}

	connector->kdev = kdev;

	if (connector->ddc)
		return sysfs_create_link(&connector->kdev->kobj,
				 &connector->ddc->dev.kobj, "ddc");
	return 0;

err_free:
	put_device(kdev);
	return r;
}

void drm_sysfs_connector_remove(struct drm_connector *connector)
@@ -374,11 +440,6 @@ void drm_sysfs_connector_status_event(struct drm_connector *connector,
}
EXPORT_SYMBOL(drm_sysfs_connector_status_event);

static void drm_sysfs_release(struct device *dev)
{
	kfree(dev);
}

struct device *drm_sysfs_minor_alloc(struct drm_minor *minor)
{
	const char *minor_str;
+1 −0
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@ menu "USB Type-C Alternate Mode drivers"

config TYPEC_DP_ALTMODE
	tristate "DisplayPort Alternate Mode driver"
	depends on DRM
	help
	  DisplayPort USB Type-C Alternate Mode allows DisplayPort
	  displays and adapters to be attached to the USB Type-C
+36 −22
Original line number Diff line number Diff line
@@ -11,8 +11,10 @@
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/usb/pd_vdo.h>
#include <linux/usb/typec_dp.h>
#include <drm/drm_connector.h>
#include "displayport.h"

#define DP_HEADER(_dp, ver, cmd)	(VDO((_dp)->alt->svid, 1, ver, cmd)	\
@@ -57,19 +59,28 @@ struct dp_altmode {
	struct typec_displayport_data data;

	enum dp_state state;
	bool hpd;

	struct mutex lock; /* device lock */
	struct work_struct work;
	struct typec_altmode *alt;
	const struct typec_altmode *port;
	struct fwnode_handle *connector_fwnode;
};

static int dp_altmode_notify(struct dp_altmode *dp)
{
	u8 state = get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf));
	unsigned long conf;
	u8 state;

	return typec_altmode_notify(dp->alt, TYPEC_MODAL_STATE(state),
				   &dp->data);
	if (dp->data.conf) {
		state = get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf));
		conf = TYPEC_MODAL_STATE(state);
	} else {
		conf = TYPEC_STATE_USB;
	}

	return typec_altmode_notify(dp->alt, conf, &dp->data);
}

static int dp_altmode_configure(struct dp_altmode *dp, u8 con)
@@ -118,6 +129,7 @@ static int dp_altmode_configure(struct dp_altmode *dp, u8 con)
static int dp_altmode_status_update(struct dp_altmode *dp)
{
	bool configured = !!DP_CONF_GET_PIN_ASSIGN(dp->data.conf);
	bool hpd = !!(dp->data.status & DP_STATUS_HPD_STATE);
	u8 con = DP_STATUS_CONNECTION(dp->data.status);
	int ret = 0;

@@ -130,6 +142,11 @@ static int dp_altmode_status_update(struct dp_altmode *dp)
		ret = dp_altmode_configure(dp, con);
		if (!ret)
			dp->state = DP_STATE_CONFIGURE;
	} else {
		if (dp->hpd != hpd) {
			drm_connector_oob_hotplug_event(dp->connector_fwnode);
			dp->hpd = hpd;
		}
	}

	return ret;
@@ -137,21 +154,10 @@ static int dp_altmode_status_update(struct dp_altmode *dp)

static int dp_altmode_configured(struct dp_altmode *dp)
{
	int ret;

	sysfs_notify(&dp->alt->dev.kobj, "displayport", "configuration");

	if (!dp->data.conf)
		return typec_altmode_notify(dp->alt, TYPEC_STATE_USB,
					    &dp->data);

	ret = dp_altmode_notify(dp);
	if (ret)
		return ret;

	sysfs_notify(&dp->alt->dev.kobj, "displayport", "pin_assignment");

	return 0;
	return dp_altmode_notify(dp);
}

static int dp_altmode_configure_vdm(struct dp_altmode *dp, u32 conf)
@@ -172,13 +178,8 @@ static int dp_altmode_configure_vdm(struct dp_altmode *dp, u32 conf)
	}

	ret = typec_altmode_vdm(dp->alt, header, &conf, 2);
	if (ret) {
		if (DP_CONF_GET_PIN_ASSIGN(dp->data.conf))
	if (ret)
		dp_altmode_notify(dp);
		else
			typec_altmode_notify(dp->alt, TYPEC_STATE_USB,
					     &dp->data);
	}

	return ret;
}
@@ -521,6 +522,7 @@ static const struct attribute_group dp_altmode_group = {
int dp_altmode_probe(struct typec_altmode *alt)
{
	const struct typec_altmode *port = typec_altmode_get_partner(alt);
	struct fwnode_handle *fwnode;
	struct dp_altmode *dp;
	int ret;

@@ -549,6 +551,11 @@ int dp_altmode_probe(struct typec_altmode *alt)
	alt->desc = "DisplayPort";
	alt->ops = &dp_altmode_ops;

	fwnode = dev_fwnode(alt->dev.parent->parent); /* typec_port fwnode */
	dp->connector_fwnode = fwnode_find_reference(fwnode, "displayport", 0);
	if (IS_ERR(dp->connector_fwnode))
		dp->connector_fwnode = NULL;

	typec_altmode_set_drvdata(alt, dp);

	dp->state = DP_STATE_ENTER;
@@ -564,6 +571,13 @@ void dp_altmode_remove(struct typec_altmode *alt)

	sysfs_remove_group(&alt->dev.kobj, &dp_altmode_group);
	cancel_work_sync(&dp->work);

	if (dp->connector_fwnode) {
		if (dp->hpd)
			drm_connector_oob_hotplug_event(dp->connector_fwnode);

		fwnode_handle_put(dp->connector_fwnode);
	}
}
EXPORT_SYMBOL_GPL(dp_altmode_remove);

Loading