Hi,
If the system firmware supplies device nodes representing the display connectors on integrated video hardware, those nodes should probable always be associated with the display connector entries (struct drm_connector).
With the USB Type-C DisplayPort alt mode, we will need a way to inform the correct drm connector entry about HPD IRQ, lane counts and other details. In ACPI (and most likely in DT too) the device node representing a DisplayPort behind USB Type-C connector should have a reference to the device node representing the USB Type-C connector (or vise versa). Once we have associated the DP connector device nodes with the drm connector entries, we can use those references to find the correct drm connector that the information the USB Type-C drivers are sending is meant for.
Because I think the connector firmware nodes should be associated with the connector entries in any case (those nodes do seem to be supplying the connectors all kinds of resources, not only references to other components), I'm proposing this now instead of waiting for the USB Type-C patches.
thanks,
Heikki Krogerus (2): drm: Add fwnode member to the struct drm_connector drm/i915: Associate ACPI connector nodes with connector entries
drivers/gpu/drm/drm_sysfs.c | 49 +++++++++++++++++++--------- drivers/gpu/drm/i915/intel_display.c | 40 +++++++++++++++++++++++ include/drm/drm_connector.h | 2 ++ 3 files changed, 76 insertions(+), 15 deletions(-)
If the system firmware supplies nodes that represent the connectors, they need to be associated with the connector entries.
ACPI tables at least always supply a device node for every connector on integrated video hardware - this is explained in ACPI specification's ch. "Appendix B Video Extension". Many drivers appear to already deal with those connector firmware nodes "indirectly" in order to support custom Operation Regions, _DSM (device specific method) and access other resources the nodes supply for the connectors.
This commit will only add the fwnode member. It's first up to the drivers to assign and take advantage of it. For convenience, the nodes are assigned to the device entries that are used for exposing the connectors to the user space via sysfs. That makes it possible to see the link between the connector and its node also from user space. In case of ACPI, the connector's sysfs directory will have a symlink named "firmware_node" pointing to the node object directory in sysfs if a node is associated with the connector.
Signed-off-by: Heikki Krogerus heikki.krogerus@linux.intel.com --- drivers/gpu/drm/drm_sysfs.c | 49 +++++++++++++++++++++++++------------ include/drm/drm_connector.h | 2 ++ 2 files changed, 36 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index ecb7b33002bb..7667939ef1c0 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -264,26 +264,50 @@ static const struct attribute_group *connector_dev_groups[] = { NULL };
+static void drm_sysfs_release(struct device *dev) +{ + kfree(dev); +} + int drm_sysfs_connector_add(struct drm_connector *connector) { struct drm_device *dev = connector->dev; + struct device *kdev; + int ret;
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); - DRM_DEBUG("adding "%s" to sysfs\n", - connector->name); + kdev = kzalloc(sizeof(*kdev), GFP_KERNEL); + if (!kdev) + return -ENOMEM;
- if (IS_ERR(connector->kdev)) { - DRM_ERROR("failed to register connector device: %ld\n", PTR_ERR(connector->kdev)); - return PTR_ERR(connector->kdev); + device_initialize(kdev); + kdev->class = drm_class; + kdev->parent = dev->primary->kdev; + kdev->fwnode = connector->fwnode; + kdev->groups = connector_dev_groups; + kdev->release = drm_sysfs_release; + dev_set_drvdata(kdev, connector); + + ret = dev_set_name(kdev, "card%d-%s", dev->primary->index, + connector->name); + if (ret) { + kfree(kdev); + return ret; + } + + DRM_DEBUG("adding "%s" to sysfs\n", connector->name); + + ret = device_add(kdev); + if (ret) { + DRM_ERROR("failed to register connector device: %d\n", ret); + put_device(kdev); + return ret; }
+ connector->kdev = kdev; + /* Let userspace know we have a new connector */ drm_sysfs_hotplug_event(dev);
@@ -330,11 +354,6 @@ void drm_sysfs_hotplug_event(struct drm_device *dev) } EXPORT_SYMBOL(drm_sysfs_hotplug_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; diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index c8061992d6cb..b8977c4eab14 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -886,6 +886,8 @@ struct drm_connector { struct device *kdev; /** @attr: sysfs attributes */ struct device_attribute *attr; + /** @fwnode: associated device node supplied by platform firmware */ + struct fwnode_handle *fwnode;
/** * @head:
On Intel platforms we know that the ACPI connector device node order will follow the order the driver (i915) decides. The decision is made using the custom Intel ACPI OpRegion (intel_opregion.c), though the driver does not actually know that the values it sends to ACPI there are used for associating a device node for the connectors, and assigning address for them.
In reality that custom Intel ACPI OpRegion actually violates ACPI specification (we supply dynamic information to objects that are defined static, for example _ADR), however, it makes assigning correct connector node for a connector entry straightforward (it's one-on-one mapping).
Signed-off-by: Heikki Krogerus heikki.krogerus@linux.intel.com --- drivers/gpu/drm/i915/intel_display.c | 40 ++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 008560ef4db0..27aea2ef80ac 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -15526,6 +15526,45 @@ static int intel_initial_commit(struct drm_device *dev) return ret; }
+/* NOTE: The connector order must be final before this is called. */ +static void intel_assign_connector_fwnodes(struct drm_device *dev) +{ + struct drm_connector_list_iter conn_iter; + struct device *kdev = &dev->pdev->dev; + struct fwnode_handle *fwnode = NULL; + struct drm_connector *connector; + struct acpi_device *adev; + + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { + /* Always getting the next, even when the last was not used. */ + fwnode = device_get_next_child_node(kdev, fwnode); + if (!fwnode) + break; + + switch (connector->connector_type) { + case DRM_MODE_CONNECTOR_LVDS: + case DRM_MODE_CONNECTOR_eDP: + case DRM_MODE_CONNECTOR_DSI: + /* + * Integrated displays have a specific address 0x1f on + * most Intel platforms, but not on all of them. + */ + adev = acpi_find_child_device(ACPI_COMPANION(kdev), + 0x1f, 0); + if (adev) { + connector->fwnode = acpi_fwnode_handle(adev); + break; + } + /* fallthrough */ + default: + connector->fwnode = fwnode; + break; + } + } + drm_connector_list_iter_end(&conn_iter); +} + int intel_modeset_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); @@ -15630,6 +15669,7 @@ int intel_modeset_init(struct drm_device *dev)
drm_modeset_lock_all(dev); intel_modeset_setup_hw_state(dev, dev->mode_config.acquire_ctx); + intel_assign_connector_fwnodes(dev); drm_modeset_unlock_all(dev);
for_each_intel_crtc(dev, crtc) {
dri-devel@lists.freedesktop.org