* [PATCH 15/37] drm/display: bridge-connector: allocate the connector dynamically
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
Currently the drm_bridge_connector has an embedded drm_connector, so their
allocation lifetimes are tied to each other. This is insufficient to
support DRM bridge hotplugging, which requires the connector to be added
and removed dynamically at runtime multiple times based on hotplug/unplug
events while the drm_bridge_connector is persistent.
Moreover the drm_connector is exposed to user space and thus an ongoing
operation (e.g. an ioctl) might last for an arbitrarily long time even
after the hardware gets removed. This means a new connector might have to
be added when the previous one is still referenced by user space.
In preparation to handle hotplug, allocate the drm-connector dynamically,
to allow:
* creating and destroying a connector multiple times during a single
drm_bridge_connector lifetime
* creating a new connector even though the previous one is still in use
and thus still refcounted and not yet freed
This commit does not introduce the actions in the two bullets (it will
happen in a later commit), it only moves to dynamic APIs for connector
allocation and init.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 156 ++++++++++++++++++-------
1 file changed, 114 insertions(+), 42 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index 1fd104f1647b..b4e2c8f1d32d 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2019 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Copyright (C) 2026 GE HealthCare
+ * Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
*/
#include <linux/export.h>
@@ -53,13 +55,24 @@
*/
/**
- * struct drm_bridge_connector - A connector backed by a chain of bridges
+ * drm_bridge_connector_dynconn - wrapper struct for the dynamically
+ * created drm_connector
*/
-struct drm_bridge_connector {
+struct drm_bridge_connector_dynconn {
/**
- * @base: The base DRM connector
+ * @bridge_connector: the owning bridge connector
*/
- struct drm_connector base;
+ struct drm_bridge_connector *bridge_connector;
+ /**
+ * @connector: the drm_connector exposed to user space
+ */
+ struct drm_connector connector;
+};
+
+/**
+ * struct drm_bridge_connector - A connector backed by a chain of bridges
+ */
+struct drm_bridge_connector {
/**
* @drm: The DRM device we belong to
*/
@@ -70,6 +83,10 @@ struct drm_bridge_connector {
* The encoder at the start of the bridges chain.
*/
struct drm_encoder *encoder;
+ /**
+ * @dynconn: The DRM connector added by the drm_bridge_connector
+ */
+ struct drm_bridge_connector_dynconn *dynconn;
/**
* @bridge_edid:
*
@@ -137,8 +154,13 @@ struct drm_bridge_connector {
struct drm_connector_hdmi_funcs hdmi_funcs;
};
-#define to_drm_bridge_connector(x) \
- container_of(x, struct drm_bridge_connector, base)
+static struct drm_bridge_connector *to_drm_bridge_connector(const struct drm_connector *connector)
+{
+ const struct drm_bridge_connector_dynconn *dynconn =
+ container_of(connector, struct drm_bridge_connector_dynconn, connector);
+
+ return dynconn->bridge_connector;
+}
static void drm_bridge_connector_put_bridges(struct drm_bridge_connector *bridge_connector)
{
@@ -152,13 +174,6 @@ static void drm_bridge_connector_put_bridges(struct drm_bridge_connector *bridge
drm_bridge_put(bridge_connector->bridge_hdmi_cec);
}
-static void drmm_bridge_connector_put_bridges(struct drm_device *dev, void *data)
-{
- struct drm_bridge_connector *bridge_connector = (struct drm_bridge_connector *)data;
-
- drm_bridge_connector_put_bridges(bridge_connector);
-}
-
/* -----------------------------------------------------------------------------
* Bridge Connector Hot-Plug Handling
*/
@@ -179,7 +194,7 @@ static void drm_bridge_connector_hpd_notify(struct drm_connector *connector,
static void drm_bridge_connector_handle_hpd(struct drm_bridge_connector *bridge_connector,
enum drm_connector_status status)
{
- struct drm_connector *connector = &bridge_connector->base;
+ struct drm_connector *connector = &bridge_connector->dynconn->connector;
struct drm_device *dev = bridge_connector->drm;
mutex_lock(&dev->mode_config.mutex);
@@ -299,6 +314,16 @@ static void drm_bridge_connector_reset(struct drm_connector *connector)
connector->state);
}
+static void drm_bridge_connector_dynconn_destroy(struct drm_connector *connector)
+{
+ struct drm_bridge_connector_dynconn *dynconn =
+ container_of(connector, struct drm_bridge_connector_dynconn, connector);
+
+ drm_connector_cleanup(connector);
+ drm_bridge_connector_put_bridges(dynconn->bridge_connector);
+ kfree(dynconn);
+}
+
static const struct drm_connector_funcs drm_bridge_connector_funcs = {
.reset = drm_bridge_connector_reset,
.detect = drm_bridge_connector_detect,
@@ -308,6 +333,7 @@ static const struct drm_connector_funcs drm_bridge_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.debugfs_init = drm_bridge_connector_debugfs_init,
.oob_hotplug_event = drm_bridge_connector_oob_hotplug_event,
+ .destroy = drm_bridge_connector_dynconn_destroy,
};
/* -----------------------------------------------------------------------------
@@ -777,7 +803,17 @@ static const struct drm_connector_hdmi_cec_funcs drm_bridge_connector_hdmi_cec_f
* Bridge Connector Initialisation
*/
-static int drm_bridge_connector_get_bridges(struct drm_bridge_connector *bridge_connector,
+static void drm_bridge_connector_dynconn_release(struct drm_bridge_connector *bridge_connector)
+{
+ if (!bridge_connector->dynconn)
+ return;
+
+ drm_connector_unregister(&bridge_connector->dynconn->connector);
+ drm_connector_put(&bridge_connector->dynconn->connector);
+ bridge_connector->dynconn = NULL;
+}
+
+static int drm_bridge_connector_get_bridges(struct drm_bridge_connector_dynconn *dynconn,
int *connector_type,
unsigned int *supported_formats,
unsigned int *max_bpc,
@@ -785,7 +821,8 @@ static int drm_bridge_connector_get_bridges(struct drm_bridge_connector *bridge_
struct drm_bridge **panel_bridge,
bool *support_hdcp)
{
- struct drm_connector *connector = &bridge_connector->base;
+ struct drm_bridge_connector *bridge_connector = dynconn->bridge_connector;
+ struct drm_connector *connector = &dynconn->connector;
/*
* TODO: Handle doublescan_allowed and stereo_allowed.
@@ -937,6 +974,7 @@ static int drm_bridge_connector_get_bridges(struct drm_bridge_connector *bridge_
static int drm_bridge_connector_init_hdmi_audio_cec(struct drm_bridge_connector *bridge_connector)
{
+ struct drm_connector *connector = &bridge_connector->dynconn->connector;
int ret;
if (bridge_connector->bridge_hdmi_audio ||
@@ -951,7 +989,7 @@ static int drm_bridge_connector_init_hdmi_audio_cec(struct drm_bridge_connector
dev = bridge->hdmi_audio_dev;
- ret = drm_connector_hdmi_audio_init(&bridge_connector->base, dev,
+ ret = drm_connector_hdmi_audio_init(connector, dev,
&drm_bridge_connector_hdmi_audio_funcs,
bridge->hdmi_audio_max_i2s_playback_channels,
bridge->hdmi_audio_i2s_formats,
@@ -965,8 +1003,7 @@ static int drm_bridge_connector_init_hdmi_audio_cec(struct drm_bridge_connector
bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) {
struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec;
- ret = drmm_connector_hdmi_cec_notifier_register(&bridge_connector->base,
- NULL,
+ ret = drmm_connector_hdmi_cec_notifier_register(connector, NULL,
bridge->hdmi_cec_dev);
if (ret)
return ret;
@@ -976,7 +1013,7 @@ static int drm_bridge_connector_init_hdmi_audio_cec(struct drm_bridge_connector
bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) {
struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec;
- ret = drmm_connector_hdmi_cec_register(&bridge_connector->base,
+ ret = drmm_connector_hdmi_cec_register(connector,
&drm_bridge_connector_hdmi_cec_funcs,
bridge->hdmi_cec_adapter_name,
bridge->hdmi_cec_available_las,
@@ -1009,13 +1046,20 @@ static int drm_bridge_connector_add_connector(struct drm_bridge_connector *bridg
int connector_type;
int ret;
- connector = &bridge_connector->base;
+ struct drm_bridge_connector_dynconn *dynconn = kzalloc_obj(*dynconn);
+ if (!dynconn)
+ return -ENOMEM;
- ret = drm_bridge_connector_get_bridges(bridge_connector, &connector_type,
+ dynconn->bridge_connector = bridge_connector;
+ connector = &dynconn->connector;
+
+ ret = drm_bridge_connector_get_bridges(dynconn, &connector_type,
&supported_formats, &max_bpc, &ddc,
&panel_bridge, &support_hdcp);
- if (ret)
+ if (ret) {
+ kfree(dynconn);
return ret;
+ }
if (bridge_connector->bridge_hdmi) {
if (!connector->ycbcr_420_allowed)
@@ -1035,25 +1079,34 @@ static int drm_bridge_connector_add_connector(struct drm_bridge_connector *bridg
bridge_connector->hdmi_funcs.spd =
drm_bridge_connector_hdmi_spd_infoframe;
- ret = drmm_connector_hdmi_init(bridge_connector->drm, connector,
- bridge_connector->bridge_hdmi->vendor,
- bridge_connector->bridge_hdmi->product,
- &drm_bridge_connector_funcs,
- &bridge_connector->hdmi_funcs,
- connector_type, ddc,
- supported_formats,
- max_bpc);
+ ret = drm_connector_hdmi_dynamic_init(bridge_connector->drm, connector,
+ bridge_connector->bridge_hdmi->vendor,
+ bridge_connector->bridge_hdmi->product,
+ &drm_bridge_connector_funcs,
+ &bridge_connector->hdmi_funcs,
+ connector_type, ddc,
+ supported_formats,
+ max_bpc);
} else {
- ret = drmm_connector_init(bridge_connector->drm, connector,
- &drm_bridge_connector_funcs,
- connector_type, ddc);
+ ret = drm_connector_dynamic_init(bridge_connector->drm, connector,
+ &drm_bridge_connector_funcs,
+ connector_type, ddc);
}
- if (ret)
+ if (ret) {
+ drm_bridge_connector_put_bridges(dynconn->bridge_connector);
+ kfree(dynconn);
return ret;
+ }
+
+ /* From now on the connector is referenced and has to be put, not kfreed */
+
+ bridge_connector->dynconn = dynconn;
+
+ drm_bridge_connector_reset(connector);
ret = drm_bridge_connector_init_hdmi_audio_cec(bridge_connector);
if (ret)
- return ret;
+ goto err_put;
drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs);
@@ -1072,9 +1125,25 @@ static int drm_bridge_connector_add_connector(struct drm_bridge_connector *bridg
ret = drm_connector_attach_encoder(connector, bridge_connector->encoder);
if (ret)
- return ret;
+ goto err_put;
+
+ ret = drm_connector_dynamic_register(connector);
+ if (ret)
+ goto err_put;
return 0;
+
+err_put:
+ drm_connector_put(connector);
+ bridge_connector->dynconn = NULL;
+ return ret;
+}
+
+static void drm_bridge_connector_fini(struct drm_device *dev, void *res)
+{
+ struct drm_bridge_connector *bridge_connector = (struct drm_bridge_connector *)res;
+
+ drm_bridge_connector_dynconn_release(bridge_connector);
}
/**
@@ -1104,10 +1173,6 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
if (!bridge_connector)
return ERR_PTR(-ENOMEM);
- ret = drmm_add_action(drm, drmm_bridge_connector_put_bridges, bridge_connector);
- if (ret)
- return ERR_PTR(ret);
-
bridge_connector->drm = drm;
bridge_connector->encoder = encoder;
@@ -1115,6 +1180,13 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
if (ret)
return ERR_PTR(ret);
- return &bridge_connector->base;
+ ret = drmm_add_action_or_reset(drm, drm_bridge_connector_fini, bridge_connector);
+ if (ret)
+ return ERR_PTR(ret);
+
+ if (!bridge_connector->dynconn)
+ return ERR_PTR(-EINVAL);
+
+ return &bridge_connector->dynconn->connector;
}
EXPORT_SYMBOL_GPL(drm_bridge_connector_init);
--
2.54.0
^ permalink raw reply related
* [PATCH 14/37] drm/display: bridge-connector: add non-drmm variant of drm_bridge_connector_put_bridges()
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
drm_bridge_connector_put_bridges() is currently meant as a drmm cleanup
action. In preparation for bridge hotplug add a non-drmm action, so it can
be called more simply in non-drmm context.
To do this split drm_bridge_connector_put_bridges() in two variants, both
doing the same actions and only differing in the name and arguments:
* drm_bridge_connector_put_bridges() (same name as before) to be called
directly by the drm_bridge_connector code with a drm_bridge_connector
pointer as the only parameter
* drmm_bridge_connector_put_bridges() (same name + drmm prefix) providing
the same drmm calling convention as before
Update the only call site accordingly.
No functional changes.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index 2e96af8efb55..1fd104f1647b 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -140,10 +140,8 @@ struct drm_bridge_connector {
#define to_drm_bridge_connector(x) \
container_of(x, struct drm_bridge_connector, base)
-static void drm_bridge_connector_put_bridges(struct drm_device *dev, void *data)
+static void drm_bridge_connector_put_bridges(struct drm_bridge_connector *bridge_connector)
{
- struct drm_bridge_connector *bridge_connector = (struct drm_bridge_connector *)data;
-
drm_bridge_put(bridge_connector->bridge_edid);
drm_bridge_put(bridge_connector->bridge_hpd);
drm_bridge_put(bridge_connector->bridge_detect);
@@ -154,6 +152,13 @@ static void drm_bridge_connector_put_bridges(struct drm_device *dev, void *data)
drm_bridge_put(bridge_connector->bridge_hdmi_cec);
}
+static void drmm_bridge_connector_put_bridges(struct drm_device *dev, void *data)
+{
+ struct drm_bridge_connector *bridge_connector = (struct drm_bridge_connector *)data;
+
+ drm_bridge_connector_put_bridges(bridge_connector);
+}
+
/* -----------------------------------------------------------------------------
* Bridge Connector Hot-Plug Handling
*/
@@ -1099,7 +1104,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
if (!bridge_connector)
return ERR_PTR(-ENOMEM);
- ret = drmm_add_action(drm, drm_bridge_connector_put_bridges, bridge_connector);
+ ret = drmm_add_action(drm, drmm_bridge_connector_put_bridges, bridge_connector);
if (ret)
return ERR_PTR(ret);
--
2.54.0
^ permalink raw reply related
* [PATCH 13/37] drm/display: bridge-connector: move drm_bridge_connector_put_bridges() definition eariler
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
drm_bridge_connector_put_bridges() will have to be called in other places
to support bridge hot-plugging.
In preparation for bridge hotplug support, move it near the top of the
file.
No functional changes. Just moving code around.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 28 +++++++++++++-------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index defe29e339b0..2e96af8efb55 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -140,6 +140,20 @@ struct drm_bridge_connector {
#define to_drm_bridge_connector(x) \
container_of(x, struct drm_bridge_connector, base)
+static void drm_bridge_connector_put_bridges(struct drm_device *dev, void *data)
+{
+ struct drm_bridge_connector *bridge_connector = (struct drm_bridge_connector *)data;
+
+ drm_bridge_put(bridge_connector->bridge_edid);
+ drm_bridge_put(bridge_connector->bridge_hpd);
+ drm_bridge_put(bridge_connector->bridge_detect);
+ drm_bridge_put(bridge_connector->bridge_modes);
+ drm_bridge_put(bridge_connector->bridge_hdmi);
+ drm_bridge_put(bridge_connector->bridge_hdmi_audio);
+ drm_bridge_put(bridge_connector->bridge_dp_audio);
+ drm_bridge_put(bridge_connector->bridge_hdmi_cec);
+}
+
/* -----------------------------------------------------------------------------
* Bridge Connector Hot-Plug Handling
*/
@@ -758,20 +772,6 @@ static const struct drm_connector_hdmi_cec_funcs drm_bridge_connector_hdmi_cec_f
* Bridge Connector Initialisation
*/
-static void drm_bridge_connector_put_bridges(struct drm_device *dev, void *data)
-{
- struct drm_bridge_connector *bridge_connector = (struct drm_bridge_connector *)data;
-
- drm_bridge_put(bridge_connector->bridge_edid);
- drm_bridge_put(bridge_connector->bridge_hpd);
- drm_bridge_put(bridge_connector->bridge_detect);
- drm_bridge_put(bridge_connector->bridge_modes);
- drm_bridge_put(bridge_connector->bridge_hdmi);
- drm_bridge_put(bridge_connector->bridge_hdmi_audio);
- drm_bridge_put(bridge_connector->bridge_dp_audio);
- drm_bridge_put(bridge_connector->bridge_hdmi_cec);
-}
-
static int drm_bridge_connector_get_bridges(struct drm_bridge_connector *bridge_connector,
int *connector_type,
unsigned int *supported_formats,
--
2.54.0
^ permalink raw reply related
* [PATCH 12/37] drm/display: bridge-connector: hoist error management to common code
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
In prepataion to add more error management code common to the HDMI and
non-HDMI branches, move error management to be common to both cases.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index 982bc64d8361..defe29e339b0 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -1038,15 +1038,13 @@ static int drm_bridge_connector_add_connector(struct drm_bridge_connector *bridg
connector_type, ddc,
supported_formats,
max_bpc);
- if (ret)
- return ret;
} else {
ret = drmm_connector_init(bridge_connector->drm, connector,
&drm_bridge_connector_funcs,
connector_type, ddc);
- if (ret)
- return ret;
}
+ if (ret)
+ return ret;
ret = drm_bridge_connector_init_hdmi_audio_cec(bridge_connector);
if (ret)
--
2.54.0
^ permalink raw reply related
* [PATCH 10/37] drm/display: bridge-connector: return int from drm_bridge_connector_init_hdmi_audio_cec()
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
This function returns a struct drm_bridge_connector * because this was the
case in the origin of its code. In current code it does not make sense
anymore: it can only return either the same pointer it received as input or
a negative ERR_PTR.
Simplify up error management by just returning an int (0 or negative
error).
No functional changes.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index e6306e98a167..6066ca0c5624 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -930,8 +930,7 @@ static int drm_bridge_connector_get_bridges(struct drm_bridge_connector *bridge_
return 0;
}
-static struct drm_bridge_connector *
-drm_bridge_connector_init_hdmi_audio_cec(struct drm_bridge_connector *bridge_connector)
+static int drm_bridge_connector_init_hdmi_audio_cec(struct drm_bridge_connector *bridge_connector)
{
int ret;
@@ -954,7 +953,7 @@ drm_bridge_connector_init_hdmi_audio_cec(struct drm_bridge_connector *bridge_con
bridge->hdmi_audio_spdif_playback,
bridge->hdmi_audio_dai_port);
if (ret)
- return ERR_PTR(ret);
+ return ret;
}
if (bridge_connector->bridge_hdmi_cec &&
@@ -965,7 +964,7 @@ drm_bridge_connector_init_hdmi_audio_cec(struct drm_bridge_connector *bridge_con
NULL,
bridge->hdmi_cec_dev);
if (ret)
- return ERR_PTR(ret);
+ return ret;
}
if (bridge_connector->bridge_hdmi_cec &&
@@ -978,10 +977,10 @@ drm_bridge_connector_init_hdmi_audio_cec(struct drm_bridge_connector *bridge_con
bridge->hdmi_cec_available_las,
bridge->hdmi_cec_dev);
if (ret)
- return ERR_PTR(ret);
+ return ret;
}
- return bridge_connector;
+ return 0;
}
/**
@@ -1051,9 +1050,9 @@ drm_bridge_connector_add_connector(struct drm_bridge_connector *bridge_connector
return ERR_PTR(ret);
}
- bridge_connector = drm_bridge_connector_init_hdmi_audio_cec(bridge_connector);
- if (IS_ERR(bridge_connector))
- return bridge_connector;
+ ret = drm_bridge_connector_init_hdmi_audio_cec(bridge_connector);
+ if (ret)
+ return ERR_PTR(ret);
drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs);
--
2.54.0
^ permalink raw reply related
* [PATCH 08/37] drm/display: bridge-connector: return int from drm_bridge_connector_get_bridges()
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
This function returns a struct drm_bridge_connector * because this was the
case in the origin of its code. In current code it does not make sense
anymore: it can only return either the same pointer it received as input or
a negative ERR_PTR.
Simplify up error management by just returning an int (0 or negative
error).
No functional changes.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 61 +++++++++++++-------------
1 file changed, 30 insertions(+), 31 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index 97d9a4c6d166..d8a033ed8f41 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -772,14 +772,13 @@ static void drm_bridge_connector_put_bridges(struct drm_device *dev, void *data)
drm_bridge_put(bridge_connector->bridge_hdmi_cec);
}
-static struct drm_bridge_connector *
-drm_bridge_connector_get_bridges(struct drm_bridge_connector *bridge_connector,
- int *connector_type,
- unsigned int *supported_formats,
- unsigned int *max_bpc,
- struct i2c_adapter **ddc,
- struct drm_bridge **panel_bridge,
- bool *support_hdcp)
+static int drm_bridge_connector_get_bridges(struct drm_bridge_connector *bridge_connector,
+ int *connector_type,
+ unsigned int *supported_formats,
+ unsigned int *max_bpc,
+ struct i2c_adapter **ddc,
+ struct drm_bridge **panel_bridge,
+ bool *support_hdcp)
{
struct drm_connector *connector = &bridge_connector->base;
@@ -820,27 +819,27 @@ drm_bridge_connector_get_bridges(struct drm_bridge_connector *bridge_connector,
}
if (bridge->ops & DRM_BRIDGE_OP_HDMI) {
if (bridge_connector->bridge_hdmi)
- return ERR_PTR(-EBUSY);
+ return -EBUSY;
if (!bridge->funcs->hdmi_write_avi_infoframe ||
!bridge->funcs->hdmi_clear_avi_infoframe ||
!bridge->funcs->hdmi_write_hdmi_infoframe ||
!bridge->funcs->hdmi_clear_hdmi_infoframe)
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
if (bridge->ops & DRM_BRIDGE_OP_HDMI_AUDIO &&
(!bridge->funcs->hdmi_write_audio_infoframe ||
!bridge->funcs->hdmi_clear_audio_infoframe))
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
if (bridge->ops & DRM_BRIDGE_OP_HDMI_HDR_DRM_INFOFRAME &&
(!bridge->funcs->hdmi_write_hdr_drm_infoframe ||
!bridge->funcs->hdmi_clear_hdr_drm_infoframe))
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
if (bridge->ops & DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME &&
(!bridge->funcs->hdmi_write_spd_infoframe ||
!bridge->funcs->hdmi_clear_spd_infoframe))
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
bridge_connector->bridge_hdmi = drm_bridge_get(bridge);
@@ -852,57 +851,57 @@ drm_bridge_connector_get_bridges(struct drm_bridge_connector *bridge_connector,
if (bridge->ops & DRM_BRIDGE_OP_HDMI_AUDIO) {
if (bridge_connector->bridge_hdmi_audio)
- return ERR_PTR(-EBUSY);
+ return -EBUSY;
if (bridge_connector->bridge_dp_audio)
- return ERR_PTR(-EBUSY);
+ return -EBUSY;
if (!bridge->hdmi_audio_max_i2s_playback_channels &&
!bridge->hdmi_audio_spdif_playback)
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
if (!bridge->funcs->hdmi_audio_prepare ||
!bridge->funcs->hdmi_audio_shutdown)
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
bridge_connector->bridge_hdmi_audio = drm_bridge_get(bridge);
}
if (bridge->ops & DRM_BRIDGE_OP_DP_AUDIO) {
if (bridge_connector->bridge_dp_audio)
- return ERR_PTR(-EBUSY);
+ return -EBUSY;
if (bridge_connector->bridge_hdmi_audio)
- return ERR_PTR(-EBUSY);
+ return -EBUSY;
if (!bridge->hdmi_audio_max_i2s_playback_channels &&
!bridge->hdmi_audio_spdif_playback)
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
if (!bridge->funcs->dp_audio_prepare ||
!bridge->funcs->dp_audio_shutdown)
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
bridge_connector->bridge_dp_audio = drm_bridge_get(bridge);
}
if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) {
if (bridge_connector->bridge_hdmi_cec)
- return ERR_PTR(-EBUSY);
+ return -EBUSY;
bridge_connector->bridge_hdmi_cec = drm_bridge_get(bridge);
}
if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) {
if (bridge_connector->bridge_hdmi_cec)
- return ERR_PTR(-EBUSY);
+ return -EBUSY;
bridge_connector->bridge_hdmi_cec = drm_bridge_get(bridge);
if (!bridge->funcs->hdmi_cec_enable ||
!bridge->funcs->hdmi_cec_log_addr ||
!bridge->funcs->hdmi_cec_transmit)
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
}
if (drm_bridge_is_last(bridge))
@@ -926,9 +925,9 @@ drm_bridge_connector_get_bridges(struct drm_bridge_connector *bridge_connector,
}
if (connector_type == DRM_MODE_CONNECTOR_Unknown)
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
- return bridge_connector;
+ return 0;
}
/**
@@ -956,11 +955,11 @@ drm_bridge_connector_add_connector(struct drm_bridge_connector *bridge_connector
connector = &bridge_connector->base;
- bridge_connector = drm_bridge_connector_get_bridges(bridge_connector, &connector_type,
- &supported_formats, &max_bpc, &ddc,
- &panel_bridge, &support_hdcp);
- if (IS_ERR(bridge_connector))
- return bridge_connector;
+ ret = drm_bridge_connector_get_bridges(bridge_connector, &connector_type,
+ &supported_formats, &max_bpc, &ddc,
+ &panel_bridge, &support_hdcp);
+ if (ret)
+ return ERR_PTR(ret);
if (bridge_connector->bridge_hdmi) {
if (!connector->ycbcr_420_allowed)
--
2.54.0
^ permalink raw reply related
* [PATCH 11/37] drm/display: bridge-connector: return int from drm_bridge_connector_add_connector()
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
This function returns a struct drm_bridge_connector * because this was the
case in the origin of its code. In current code it does not make sense: it
can only return either the same pointer it received as input or a negative
ERR_PTR.
This forces users to an unnecessarily complex error management code.
Clean up error management by just returning an int (0 or negative error).
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 24 +++++++++++-------------
1 file changed, 11 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index 6066ca0c5624..982bc64d8361 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -991,11 +991,9 @@ static int drm_bridge_connector_init_hdmi_audio_cec(struct drm_bridge_connector
* @bridge_connector->drm and @bridge_connector->encoder fields are
* assigned at least)
*
- * Returns a pointer to @bridge_connector on success, or a negative error
- * pointer otherwise.
+ * Returns 0 on success or a negative error otherwise.
*/
-static struct drm_bridge_connector *
-drm_bridge_connector_add_connector(struct drm_bridge_connector *bridge_connector)
+static int drm_bridge_connector_add_connector(struct drm_bridge_connector *bridge_connector)
{
struct drm_connector *connector;
struct i2c_adapter *ddc = NULL;
@@ -1012,7 +1010,7 @@ drm_bridge_connector_add_connector(struct drm_bridge_connector *bridge_connector
&supported_formats, &max_bpc, &ddc,
&panel_bridge, &support_hdcp);
if (ret)
- return ERR_PTR(ret);
+ return ret;
if (bridge_connector->bridge_hdmi) {
if (!connector->ycbcr_420_allowed)
@@ -1041,18 +1039,18 @@ drm_bridge_connector_add_connector(struct drm_bridge_connector *bridge_connector
supported_formats,
max_bpc);
if (ret)
- return ERR_PTR(ret);
+ return ret;
} else {
ret = drmm_connector_init(bridge_connector->drm, connector,
&drm_bridge_connector_funcs,
connector_type, ddc);
if (ret)
- return ERR_PTR(ret);
+ return ret;
}
ret = drm_bridge_connector_init_hdmi_audio_cec(bridge_connector);
if (ret)
- return ERR_PTR(ret);
+ return ret;
drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs);
@@ -1071,9 +1069,9 @@ drm_bridge_connector_add_connector(struct drm_bridge_connector *bridge_connector
ret = drm_connector_attach_encoder(connector, bridge_connector->encoder);
if (ret)
- return ERR_PTR(ret);
+ return ret;
- return bridge_connector;
+ return 0;
}
/**
@@ -1110,9 +1108,9 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
bridge_connector->drm = drm;
bridge_connector->encoder = encoder;
- bridge_connector = drm_bridge_connector_add_connector(bridge_connector);
- if (IS_ERR(bridge_connector))
- return ERR_CAST(bridge_connector);
+ ret = drm_bridge_connector_add_connector(bridge_connector);
+ if (ret)
+ return ERR_PTR(ret);
return &bridge_connector->base;
}
--
2.54.0
^ permalink raw reply related
* [PATCH 09/37] drm/display: bridge-connector: extract drm_bridge_connector_init_hdmi_audio_cec()
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
To support bridge hotplug we will need better control over the steps
involved in the crm_connector creation, especially in terms of error
management. This will be complicated by the length and amount of return
points in this function. In preparation, split the code to initialize HDMI
audio and CEC fields out to a separate function.
No functional changes. Just moving code around.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 102 ++++++++++++++-----------
1 file changed, 57 insertions(+), 45 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index d8a033ed8f41..e6306e98a167 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -930,6 +930,60 @@ static int drm_bridge_connector_get_bridges(struct drm_bridge_connector *bridge_
return 0;
}
+static struct drm_bridge_connector *
+drm_bridge_connector_init_hdmi_audio_cec(struct drm_bridge_connector *bridge_connector)
+{
+ int ret;
+
+ if (bridge_connector->bridge_hdmi_audio ||
+ bridge_connector->bridge_dp_audio) {
+ struct device *dev;
+ struct drm_bridge *bridge;
+
+ if (bridge_connector->bridge_hdmi_audio)
+ bridge = bridge_connector->bridge_hdmi_audio;
+ else
+ bridge = bridge_connector->bridge_dp_audio;
+
+ dev = bridge->hdmi_audio_dev;
+
+ ret = drm_connector_hdmi_audio_init(&bridge_connector->base, dev,
+ &drm_bridge_connector_hdmi_audio_funcs,
+ bridge->hdmi_audio_max_i2s_playback_channels,
+ bridge->hdmi_audio_i2s_formats,
+ bridge->hdmi_audio_spdif_playback,
+ bridge->hdmi_audio_dai_port);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
+ if (bridge_connector->bridge_hdmi_cec &&
+ bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) {
+ struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec;
+
+ ret = drmm_connector_hdmi_cec_notifier_register(&bridge_connector->base,
+ NULL,
+ bridge->hdmi_cec_dev);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
+ if (bridge_connector->bridge_hdmi_cec &&
+ bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) {
+ struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec;
+
+ ret = drmm_connector_hdmi_cec_register(&bridge_connector->base,
+ &drm_bridge_connector_hdmi_cec_funcs,
+ bridge->hdmi_cec_adapter_name,
+ bridge->hdmi_cec_available_las,
+ bridge->hdmi_cec_dev);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
+ return bridge_connector;
+}
+
/**
* drm_bridge_connector_add_connector - add the drm_connector
* @bridge_connector: drm_bridge_connector to add the drm_connector to
@@ -997,51 +1051,9 @@ drm_bridge_connector_add_connector(struct drm_bridge_connector *bridge_connector
return ERR_PTR(ret);
}
- if (bridge_connector->bridge_hdmi_audio ||
- bridge_connector->bridge_dp_audio) {
- struct device *dev;
- struct drm_bridge *bridge;
-
- if (bridge_connector->bridge_hdmi_audio)
- bridge = bridge_connector->bridge_hdmi_audio;
- else
- bridge = bridge_connector->bridge_dp_audio;
-
- dev = bridge->hdmi_audio_dev;
-
- ret = drm_connector_hdmi_audio_init(connector, dev,
- &drm_bridge_connector_hdmi_audio_funcs,
- bridge->hdmi_audio_max_i2s_playback_channels,
- bridge->hdmi_audio_i2s_formats,
- bridge->hdmi_audio_spdif_playback,
- bridge->hdmi_audio_dai_port);
- if (ret)
- return ERR_PTR(ret);
- }
-
- if (bridge_connector->bridge_hdmi_cec &&
- bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) {
- struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec;
-
- ret = drmm_connector_hdmi_cec_notifier_register(connector,
- NULL,
- bridge->hdmi_cec_dev);
- if (ret)
- return ERR_PTR(ret);
- }
-
- if (bridge_connector->bridge_hdmi_cec &&
- bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) {
- struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec;
-
- ret = drmm_connector_hdmi_cec_register(connector,
- &drm_bridge_connector_hdmi_cec_funcs,
- bridge->hdmi_cec_adapter_name,
- bridge->hdmi_cec_available_las,
- bridge->hdmi_cec_dev);
- if (ret)
- return ERR_PTR(ret);
- }
+ bridge_connector = drm_bridge_connector_init_hdmi_audio_cec(bridge_connector);
+ if (IS_ERR(bridge_connector))
+ return bridge_connector;
drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs);
--
2.54.0
^ permalink raw reply related
* [PATCH v4 3/3] pinctrl: aspeed: Add AST2700 SoC1 support
From: Billy Tsai @ 2026-05-19 10:38 UTC (permalink / raw)
To: Linus Walleij, Tony Lindgren, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Joel Stanley, Andrew Jeffery, Bartosz Golaszewski,
Lee Jones, Ryan Chen
Cc: patrickw3, linux-gpio, devicetree, linux-kernel, linux-arm-kernel,
linux-aspeed, BMC-SW, openbmc, Andrew Jeffery, linux-clk,
Billy Tsai
In-Reply-To: <20260519-pinctrl-single-bit-v4-0-5fe568a8ffde@aspeedtech.com>
Implement pin multiplexing (and pin configuration where applicable)
for the AST2700 SoC1 SCU pinctrl block using static SoC data tables.
Unlike legacy ASPEED pin controllers, the SoC1 pin function control
fields are highly regular, which makes it practical to describe the
packed-field register layout directly in driver data rather than reuse
the existing Aspeed pinctrl macro infrastructure.
The driver uses the generic pinctrl, pinmux and pinconf frameworks.
The controller registers are accessed via regmap from the parent
syscon, allowing shared ownership of the SCU register block.
Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
---
drivers/pinctrl/aspeed/Kconfig | 14 +
drivers/pinctrl/aspeed/Makefile | 1 +
drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c | 1754 +++++++++++++++++++++++
3 files changed, 1769 insertions(+)
diff --git a/drivers/pinctrl/aspeed/Kconfig b/drivers/pinctrl/aspeed/Kconfig
index f9672cca891e..8e1d4da0891d 100644
--- a/drivers/pinctrl/aspeed/Kconfig
+++ b/drivers/pinctrl/aspeed/Kconfig
@@ -40,3 +40,17 @@ config PINCTRL_ASPEED_G7_SOC0
Say Y here to enable pin controller support for the SoC0 instance
of Aspeed's 7th generation SoCs. GPIO is provided by a separate
GPIO driver.
+
+config PINCTRL_ASPEED_G7_SOC1
+ bool "Aspeed G7 SoC1 pin control"
+ depends on (ARCH_ASPEED || COMPILE_TEST) && OF
+ select MFD_SYSCON
+ select PINMUX
+ select GENERIC_PINCTRL_GROUPS
+ select GENERIC_PINMUX_FUNCTIONS
+ select GENERIC_PINCONF
+ select REGMAP_MMIO
+ help
+ Say Y here to enable pin controller support for the SoC1 instance
+ of Aspeed's 7th generation SoCs. GPIO is provided by a separate
+ GPIO driver.
diff --git a/drivers/pinctrl/aspeed/Makefile b/drivers/pinctrl/aspeed/Makefile
index 0de524ca2c72..7a41ca45c6ba 100644
--- a/drivers/pinctrl/aspeed/Makefile
+++ b/drivers/pinctrl/aspeed/Makefile
@@ -7,3 +7,4 @@ obj-$(CONFIG_PINCTRL_ASPEED_G4) += pinctrl-aspeed-g4.o
obj-$(CONFIG_PINCTRL_ASPEED_G5) += pinctrl-aspeed-g5.o
obj-$(CONFIG_PINCTRL_ASPEED_G6) += pinctrl-aspeed-g6.o
obj-$(CONFIG_PINCTRL_ASPEED_G7_SOC0) += pinctrl-aspeed-g7-soc0.o
+obj-$(CONFIG_PINCTRL_ASPEED_G7_SOC1) += pinctrl-aspeed-g7-soc1.o
diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c
new file mode 100644
index 000000000000..400fdcf7b4d1
--- /dev/null
+++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c
@@ -0,0 +1,1754 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Pinctrl driver for Aspeed G7 SoC1
+ *
+ * Copyright (C) 2026 Aspeed Technology Inc.
+ */
+
+#include <linux/errno.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include "../core.h"
+#include "../pinconf.h"
+#include "../pinctrl-utils.h"
+#include "../pinmux.h"
+
+#define ASPEED_G7_SOC1_NR_PINS 220
+#define ASPEED_G7_SOC1_REG_WIDTH 32
+#define ASPEED_G7_SOC1_REG_STRIDE 4
+
+#define ASPEED_G7_SOC1_MUX_BASE 0x400
+#define ASPEED_G7_SOC1_BIAS_BASE 0x480
+#define ASPEED_G7_SOC1_DRV_BASE 0x4C0
+#define ASPEED_G7_SOC1_PCIE_REG 0x908
+#define ASPEED_G7_SOC1_USB_MODE_REG 0x3B0
+#define ASPEED_G7_SOC1_SGMII_REG 0x47C
+
+#define ASPEED_G7_SOC1_MUX_FUNC_MASK 0x7
+#define ASPEED_G7_SOC1_MUX_BITS_PER_PIN 4
+#define ASPEED_G7_SOC1_MUX_PINS_PER_REG \
+ (ASPEED_G7_SOC1_REG_WIDTH / ASPEED_G7_SOC1_MUX_BITS_PER_PIN)
+
+#define ASPEED_G7_SOC1_BIAS_FUNC_MASK 0x1
+#define ASPEED_G7_SOC1_BIAS_BITS_PER_PIN 1
+#define ASPEED_G7_SOC1_BIAS_PINS_PER_REG \
+ (ASPEED_G7_SOC1_REG_WIDTH / ASPEED_G7_SOC1_BIAS_BITS_PER_PIN)
+
+#define ASPEED_G7_SOC1_DRV_FUNC_MASK 0x3
+#define ASPEED_G7_SOC1_DRV_BITS_PER_PIN 2
+#define ASPEED_G7_SOC1_DRV_PINS_PER_REG \
+ (ASPEED_G7_SOC1_REG_WIDTH / ASPEED_G7_SOC1_DRV_BITS_PER_PIN)
+
+#define ASPEED_G7_SOC1_DRV_STRENGTH_STEP_MA 4
+#define ASPEED_G7_SOC1_DRV_STRENGTH_HW_BASE 1
+#define ASPEED_G7_SOC1_DRV_STRENGTH_MIN_MA \
+ (ASPEED_G7_SOC1_DRV_STRENGTH_HW_BASE * ASPEED_G7_SOC1_DRV_STRENGTH_STEP_MA)
+#define ASPEED_G7_SOC1_DRV_STRENGTH_MAX_MA \
+ ((ASPEED_G7_SOC1_DRV_FUNC_MASK + ASPEED_G7_SOC1_DRV_STRENGTH_HW_BASE) * \
+ ASPEED_G7_SOC1_DRV_STRENGTH_STEP_MA)
+
+/*
+ * NOTE: The numeric values of these enum entries are significant.
+ * They must match the SoC GPIO numbering / ball-to-GPIO ID mapping.
+ * Do not reorder alphabetically.
+ */
+enum {
+ C16,
+ C14,
+ C11,
+ D9,
+ F14,
+ D10,
+ C12,
+ C13,
+ AC26,
+ AA25,
+ AB23,
+ U22,
+ V21,
+ N26,
+ P25,
+ N25,
+ V23,
+ W22,
+ AB26,
+ AD26,
+ P26,
+ AE26,
+ AF26,
+ AF25,
+ AE25,
+ AD25,
+ AF23,
+ AF20,
+ AF21,
+ AE21,
+ AE23,
+ AD22,
+ AF17,
+ AA16,
+ Y16,
+ V17,
+ J13,
+ AB16,
+ AC16,
+ AF16,
+ AA15,
+ AB15,
+ AC15,
+ AD15,
+ Y15,
+ AA14,
+ W16,
+ V16,
+ AB18,
+ AC18,
+ K13,
+ AA17,
+ AB17,
+ AD16,
+ AC17,
+ AD17,
+ AE16,
+ AE17,
+ AB24,
+ W26,
+ HOLE0,
+ HOLE1,
+ HOLE2,
+ HOLE3,
+ W25,
+ Y23,
+ Y24,
+ W21,
+ AA23,
+ AC22,
+ AB22,
+ Y21,
+ AE20,
+ AF19,
+ Y22,
+ AA20,
+ AA22,
+ AB20,
+ AF18,
+ AE19,
+ AD20,
+ AC20,
+ AA21,
+ AB21,
+ AC19,
+ AE18,
+ AD19,
+ AD18,
+ U25,
+ U26,
+ Y26,
+ AA24,
+ R25,
+ AA26,
+ R26,
+ Y25,
+ B16,
+ D14,
+ B15,
+ B14,
+ C17,
+ B13,
+ E14,
+ C15,
+ D24,
+ B23,
+ B22,
+ C23,
+ B18,
+ B21,
+ M15,
+ B19,
+ B26,
+ A25,
+ A24,
+ B24,
+ E26,
+ A21,
+ A19,
+ A18,
+ D26,
+ C26,
+ A23,
+ A22,
+ B25,
+ F26,
+ A26,
+ A14,
+ E10,
+ E13,
+ D12,
+ F10,
+ E11,
+ F11,
+ F13,
+ N15,
+ C20,
+ C19,
+ A8,
+ R14,
+ A7,
+ P14,
+ D20,
+ A6,
+ B6,
+ N14,
+ B7,
+ B8,
+ B9,
+ M14,
+ J11,
+ E7,
+ D19,
+ B11,
+ D15,
+ B12,
+ B10,
+ P13,
+ C18,
+ C6,
+ C7,
+ D7,
+ N13,
+ C8,
+ C9,
+ C10,
+ M16,
+ A15,
+ G11,
+ H7,
+ H8,
+ H9,
+ H10,
+ H11,
+ J9,
+ J10,
+ E9,
+ F9,
+ F8,
+ M13,
+ F7,
+ D8,
+ E8,
+ L12,
+ F12,
+ E12,
+ J12,
+ G7,
+ G8,
+ G9,
+ G10,
+ K12,
+ W17,
+ V18,
+ W18,
+ Y17,
+ AA18,
+ AA13,
+ Y18,
+ AA12,
+ W20,
+ V20,
+ Y11,
+ V14,
+ V19,
+ W14,
+ Y20,
+ AB19,
+ U21,
+ T24,
+ V24,
+ V22,
+ T23,
+ AC25,
+ AB25,
+ AC24,
+ PCIERC2_PERST,
+ PORTC_MODE,
+ PORTD_MODE,
+ SGMII0,
+};
+
+struct aspeed_g7_soc1_pinctrl {
+ struct device *dev;
+ struct regmap *regmap;
+ struct pinctrl_dev *pctl;
+};
+
+struct aspeed_g7_field {
+ unsigned int reg;
+ unsigned int shift;
+ unsigned int mask;
+};
+
+static struct aspeed_g7_field
+aspeed_g7_soc1_pinmux_field_from_pin(unsigned int pin)
+{
+ return (struct aspeed_g7_field){
+ .reg = ASPEED_G7_SOC1_MUX_BASE +
+ (pin / ASPEED_G7_SOC1_MUX_PINS_PER_REG) *
+ ASPEED_G7_SOC1_REG_STRIDE,
+ .shift = (pin % ASPEED_G7_SOC1_MUX_PINS_PER_REG) *
+ ASPEED_G7_SOC1_MUX_BITS_PER_PIN,
+ .mask = ASPEED_G7_SOC1_MUX_FUNC_MASK,
+ };
+}
+
+static struct aspeed_g7_field
+aspeed_g7_soc1_bias_field_from_pin(unsigned int pin)
+{
+ return (struct aspeed_g7_field){
+ .reg = ASPEED_G7_SOC1_BIAS_BASE +
+ (pin / ASPEED_G7_SOC1_BIAS_PINS_PER_REG) *
+ ASPEED_G7_SOC1_REG_STRIDE,
+ .shift = pin % ASPEED_G7_SOC1_BIAS_PINS_PER_REG,
+ .mask = ASPEED_G7_SOC1_BIAS_FUNC_MASK,
+ };
+}
+
+static struct aspeed_g7_field
+aspeed_g7_soc1_drv_field_from_idx(unsigned int idx)
+{
+ return (struct aspeed_g7_field){
+ .reg = ASPEED_G7_SOC1_DRV_BASE +
+ (idx / ASPEED_G7_SOC1_DRV_PINS_PER_REG) *
+ ASPEED_G7_SOC1_REG_STRIDE,
+ .shift = (idx % ASPEED_G7_SOC1_DRV_PINS_PER_REG) *
+ ASPEED_G7_SOC1_DRV_BITS_PER_PIN,
+ .mask = ASPEED_G7_SOC1_DRV_FUNC_MASK,
+ };
+}
+
+#define PIN(n) PINCTRL_PIN(n, #n)
+
+static const struct pinctrl_pin_desc aspeed_g7_soc1_pins[] = {
+ PIN(C16),
+ PIN(C14),
+ PIN(C11),
+ PIN(D9),
+ PIN(F14),
+ PIN(D10),
+ PIN(C12),
+ PIN(C13),
+ PIN(AC26),
+ PIN(AA25),
+ PIN(AB23),
+ PIN(U22),
+ PIN(V21),
+ PIN(N26),
+ PIN(P25),
+ PIN(N25),
+ PIN(V23),
+ PIN(W22),
+ PIN(AB26),
+ PIN(AD26),
+ PIN(P26),
+ PIN(AE26),
+ PIN(AF26),
+ PIN(AF25),
+ PIN(AE25),
+ PIN(AD25),
+ PIN(AF23),
+ PIN(AF20),
+ PIN(AF21),
+ PIN(AE21),
+ PIN(AE23),
+ PIN(AD22),
+ PIN(AF17),
+ PIN(AA16),
+ PIN(Y16),
+ PIN(V17),
+ PIN(J13),
+ PIN(AB16),
+ PIN(AC16),
+ PIN(AF16),
+ PIN(AA15),
+ PIN(AB15),
+ PIN(AC15),
+ PIN(AD15),
+ PIN(Y15),
+ PIN(AA14),
+ PIN(W16),
+ PIN(V16),
+ PIN(AB18),
+ PIN(AC18),
+ PIN(K13),
+ PIN(AA17),
+ PIN(AB17),
+ PIN(AD16),
+ PIN(AC17),
+ PIN(AD17),
+ PIN(AE16),
+ PIN(AE17),
+ PIN(AB24),
+ PIN(W26),
+ PIN(HOLE0),
+ PIN(HOLE1),
+ PIN(HOLE2),
+ PIN(HOLE3),
+ PIN(W25),
+ PIN(Y23),
+ PIN(Y24),
+ PIN(W21),
+ PIN(AA23),
+ PIN(AC22),
+ PIN(AB22),
+ PIN(Y21),
+ PIN(AE20),
+ PIN(AF19),
+ PIN(Y22),
+ PIN(AA20),
+ PIN(AA22),
+ PIN(AB20),
+ PIN(AF18),
+ PIN(AE19),
+ PIN(AD20),
+ PIN(AC20),
+ PIN(AA21),
+ PIN(AB21),
+ PIN(AC19),
+ PIN(AE18),
+ PIN(AD19),
+ PIN(AD18),
+ PIN(U25),
+ PIN(U26),
+ PIN(Y26),
+ PIN(AA24),
+ PIN(R25),
+ PIN(AA26),
+ PIN(R26),
+ PIN(Y25),
+ PIN(B16),
+ PIN(D14),
+ PIN(B15),
+ PIN(B14),
+ PIN(C17),
+ PIN(B13),
+ PIN(E14),
+ PIN(C15),
+ PIN(D24),
+ PIN(B23),
+ PIN(B22),
+ PIN(C23),
+ PIN(B18),
+ PIN(B21),
+ PIN(M15),
+ PIN(B19),
+ PIN(B26),
+ PIN(A25),
+ PIN(A24),
+ PIN(B24),
+ PIN(E26),
+ PIN(A21),
+ PIN(A19),
+ PIN(A18),
+ PIN(D26),
+ PIN(C26),
+ PIN(A23),
+ PIN(A22),
+ PIN(B25),
+ PIN(F26),
+ PIN(A26),
+ PIN(A14),
+ PIN(E10),
+ PIN(E13),
+ PIN(D12),
+ PIN(F10),
+ PIN(E11),
+ PIN(F11),
+ PIN(F13),
+ PIN(N15),
+ PIN(C20),
+ PIN(C19),
+ PIN(A8),
+ PIN(R14),
+ PIN(A7),
+ PIN(P14),
+ PIN(D20),
+ PIN(A6),
+ PIN(B6),
+ PIN(N14),
+ PIN(B7),
+ PIN(B8),
+ PIN(B9),
+ PIN(M14),
+ PIN(J11),
+ PIN(E7),
+ PIN(D19),
+ PIN(B11),
+ PIN(D15),
+ PIN(B12),
+ PIN(B10),
+ PIN(P13),
+ PIN(C18),
+ PIN(C6),
+ PIN(C7),
+ PIN(D7),
+ PIN(N13),
+ PIN(C8),
+ PIN(C9),
+ PIN(C10),
+ PIN(M16),
+ PIN(A15),
+ PIN(G11),
+ PIN(H7),
+ PIN(H8),
+ PIN(H9),
+ PIN(H10),
+ PIN(H11),
+ PIN(J9),
+ PIN(J10),
+ PIN(E9),
+ PIN(F9),
+ PIN(F8),
+ PIN(M13),
+ PIN(F7),
+ PIN(D8),
+ PIN(E8),
+ PIN(L12),
+ PIN(F12),
+ PIN(E12),
+ PIN(J12),
+ PIN(G7),
+ PIN(G8),
+ PIN(G9),
+ PIN(G10),
+ PIN(K12),
+ PIN(W17),
+ PIN(V18),
+ PIN(W18),
+ PIN(Y17),
+ PIN(AA18),
+ PIN(AA13),
+ PIN(Y18),
+ PIN(AA12),
+ PIN(W20),
+ PIN(V20),
+ PIN(Y11),
+ PIN(V14),
+ PIN(V19),
+ PIN(W14),
+ PIN(Y20),
+ PIN(AB19),
+ PIN(U21),
+ PIN(T24),
+ PIN(V24),
+ PIN(V22),
+ PIN(T23),
+ PIN(AC25),
+ PIN(AB25),
+ PIN(AC24),
+ PIN(PCIERC2_PERST),
+ PIN(PORTC_MODE),
+ PIN(PORTD_MODE),
+ PIN(SGMII0),
+};
+
+static const struct pinctrl_ops aspeed_g7_soc1_pctl_ops = {
+ .get_groups_count = pinctrl_generic_get_group_count,
+ .get_group_name = pinctrl_generic_get_group_name,
+ .get_group_pins = pinctrl_generic_get_group_pins,
+ .dt_node_to_map = pinconf_generic_dt_node_to_map_all,
+ .dt_free_map = pinctrl_utils_free_map,
+};
+
+struct aspeed_g7_soc1_function {
+ struct pinfunction pinfunction;
+ const u8 *muxvals;
+};
+
+static int aspeed_g7_soc1_drive_strength_to_hw(u32 strength,
+ unsigned int *val)
+{
+ if (strength < ASPEED_G7_SOC1_DRV_STRENGTH_MIN_MA ||
+ strength > ASPEED_G7_SOC1_DRV_STRENGTH_MAX_MA ||
+ strength % ASPEED_G7_SOC1_DRV_STRENGTH_STEP_MA)
+ return -EINVAL;
+
+ *val = (strength / ASPEED_G7_SOC1_DRV_STRENGTH_STEP_MA) -
+ ASPEED_G7_SOC1_DRV_STRENGTH_HW_BASE;
+
+ return 0;
+}
+
+static int aspeed_g7_soc1_set_mux(struct pinctrl_dev *pctldev,
+ unsigned int fselector, unsigned int group)
+{
+ struct aspeed_g7_soc1_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+ const struct aspeed_g7_soc1_function *soc1_func;
+ const struct function_desc *fd;
+ const struct pinfunction *func;
+ const struct pingroup *grp;
+ struct group_desc *gd;
+ const char *gname;
+ int i, g_idx = -1, ret;
+
+ gd = pinctrl_generic_get_group(pctldev, group);
+ if (!gd)
+ return -EINVAL;
+
+ grp = &gd->grp;
+
+ fd = pinmux_generic_get_function(pctldev, fselector);
+ if (!fd)
+ return -EINVAL;
+
+ soc1_func = fd->data;
+ if (!soc1_func)
+ return -EINVAL;
+
+ func = &soc1_func->pinfunction;
+ gname = grp->name;
+
+ for (i = 0; i < func->ngroups; i++) {
+ if (!strcmp(gname, func->groups[i])) {
+ g_idx = i;
+ break;
+ }
+ }
+
+ if (g_idx < 0)
+ return -EINVAL;
+
+ for (i = 0; i < grp->npins; i++) {
+ unsigned int val = soc1_func->muxvals[g_idx];
+ unsigned int pin = grp->pins[i];
+ struct aspeed_g7_field field;
+
+ if (pin == PCIERC2_PERST) {
+ /*
+ * PCIERC2_PERST is a special case: it is managed by a
+ * dedicated control register (0x908) instead of the
+ * standard 4-bit multi-function field.
+ */
+ field.reg = ASPEED_G7_SOC1_PCIE_REG;
+ field.shift = 0;
+ field.mask = 0x1;
+ val = 1;
+ } else if (pin == PORTC_MODE || pin == PORTD_MODE) {
+ /*
+ * PORTC_MODE and PORTD_MODE are virtual "pins" that
+ * control the USB 2.0 controller mode settings.
+ * These reside in a specific control register (0x3B0)
+ * with non-standard bit widths.
+ */
+ field.reg = ASPEED_G7_SOC1_USB_MODE_REG;
+ field.mask = 0x3;
+ field.shift = pin == PORTC_MODE ? 0 : 2;
+ } else if (pin == SGMII0) {
+ /*
+ * SGMII0 is a virtual pin whose mux control resides at
+ * SCU47C bit 0, outside the contiguous pin-indexed MUX
+ * register range starting at MUX_BASE. The field is
+ * 1 bit wide; use a 1-bit mask to avoid clobbering
+ * adjacent bits in SCU47C.
+ */
+ field.reg = ASPEED_G7_SOC1_SGMII_REG;
+ field.shift = 0;
+ field.mask = 0x1;
+ } else {
+ /* Standard 4-bit-per-pin multi-function configuration */
+ field = aspeed_g7_soc1_pinmux_field_from_pin(pin);
+ }
+
+ dev_dbg(pctl->dev,
+ "Setting pin %u reg 0x%x shift %u to function %s (muxval=0x%x)\n",
+ pin, field.reg, field.shift, func->name, val);
+
+ ret = regmap_update_bits(pctl->regmap, field.reg,
+ field.mask << field.shift,
+ val << field.shift);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int aspeed_g7_soc1_gpio_request_enable(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned int pin)
+{
+ struct aspeed_g7_soc1_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+ struct aspeed_g7_field field;
+ int ret = -ENOTSUPP;
+
+ if (pin <= AC24) {
+ field = aspeed_g7_soc1_pinmux_field_from_pin(pin);
+ ret = regmap_update_bits(pctl->regmap, field.reg,
+ field.mask << field.shift, 0);
+ }
+
+ return ret;
+}
+
+static const struct pinmux_ops aspeed_g7_soc1_pmx_ops = {
+ .get_functions_count = pinmux_generic_get_function_count,
+ .get_function_name = pinmux_generic_get_function_name,
+ .get_function_groups = pinmux_generic_get_function_groups,
+ .set_mux = aspeed_g7_soc1_set_mux,
+ .gpio_request_enable = aspeed_g7_soc1_gpio_request_enable,
+ .strict = true,
+};
+
+/*
+ * aspeed_g7_soc1_drv_map - Mapping table for pin drive strength control.
+ *
+ * In AST2700 SOC1, drive strength configuration is architecturally decoupled
+ * from the main pin mux registers (0x400 range). It is managed by a separate
+ * set of registers starting at 0x4C0.
+ *
+ * This table is required because:
+ * 1. The mapping between physical pin IDs and drive strength control slots
+ * is non-linear and sparse.
+ * For example, W25 maps to field index 8 (stored as 9),
+ * meaning it occupies bits [17:16] of the first 0x4C0 register.
+ * 2. Only a subset of physical pins supports drive strength configuration.
+ *
+ * The table stores (drive strength field index + 1).
+ * The field index refers to the 2-bit drive strength field position within the
+ * 0x4C0 register range. A value of 0 indicates that the pin does not support
+ * drive strength configuration (returning -ENOTSUPP).
+ * This +1 offset allows us to rely on C's default zero-initialization for
+ * unsupported pins while avoiding compiler warnings regarding overridden
+ * initializers.
+ */
+static const int aspeed_g7_soc1_drv_map[ASPEED_G7_SOC1_NR_PINS] = {
+ [C16] = 1, [C14] = 2, [C11] = 3, [D9] = 4, [F14] = 5, [D10] = 6, [C12] = 7,
+ [C13] = 8, [W25] = 9, [Y23] = 10, [Y24] = 11, [W21] = 12, [AA23] = 13, [AC22] = 14,
+ [AB22] = 15, [Y21] = 16, [AE20] = 17, [AF19] = 18, [Y22] = 19, [AA20] = 20, [AA22] = 21,
+ [AB20] = 22, [AF18] = 23, [AE19] = 24, [AD20] = 25, [AC20] = 26, [AA21] = 27, [AB21] = 28,
+ [AC19] = 29, [AE18] = 30, [AD19] = 31, [AD18] = 32, [U25] = 33, [U26] = 34, [Y26] = 35,
+ [AA24] = 36, [R25] = 37, [AA26] = 38, [R26] = 39, [Y25] = 40, [B16] = 41, [D14] = 42,
+ [B15] = 43, [B14] = 44, [C17] = 45, [B13] = 46, [E14] = 47, [C15] = 48, [D24] = 49,
+ [B23] = 50, [B22] = 51, [C23] = 52, [B18] = 53, [B21] = 54, [M15] = 55, [B19] = 56,
+ [B26] = 57, [A25] = 58, [A24] = 59, [B24] = 60, [E26] = 61, [A21] = 62, [A19] = 63,
+ [A18] = 64, [D26] = 65, [C26] = 66, [A23] = 67, [A22] = 68, [B25] = 69, [F26] = 70,
+ [A26] = 71, [A14] = 72, [E10] = 73, [E13] = 74, [D12] = 75, [F10] = 76, [E11] = 77,
+ [F11] = 78, [F13] = 79, [N15] = 80, [C20] = 81, [C19] = 82, [A8] = 83, [R14] = 84,
+ [A7] = 85, [P14] = 86, [D20] = 87, [A6] = 88, [B6] = 89, [N14] = 90, [B7] = 91,
+ [B8] = 92, [B9] = 93, [M14] = 94, [J11] = 95, [E7] = 96, [D19] = 97, [B11] = 98,
+ [D15] = 99, [B12] = 100, [B10] = 101, [P13] = 102, [C18] = 103, [C6] = 104, [C7] = 105,
+ [D7] = 106, [N13] = 107, [C8] = 108, [C9] = 109, [C10] = 110, [M16] = 111, [A15] = 112,
+ [E9] = 113, [F9] = 114, [F8] = 115, [M13] = 116, [F7] = 117, [D8] = 118, [E8] = 119,
+ [L12] = 120,
+};
+
+static int aspeed_g7_soc1_pin_config_get(struct pinctrl_dev *pctldev,
+ unsigned int pin,
+ unsigned long *config)
+{
+ struct aspeed_g7_soc1_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+ enum pin_config_param param = pinconf_to_config_param(*config);
+ struct aspeed_g7_field field;
+ unsigned int val, val_raw;
+ int ret, ds_idx;
+
+ if (pin > AC24)
+ return -EINVAL;
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ field = aspeed_g7_soc1_bias_field_from_pin(pin);
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ case PIN_CONFIG_BIAS_PULL_UP:
+ /*
+ * The hardware has a single 1-bit enable/disable field per
+ * pin; pull direction is fixed in silicon and cannot be read
+ * back from the register. Reject readback requests for a
+ * specific pull direction.
+ */
+ return -ENOTSUPP;
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ ds_idx = aspeed_g7_soc1_drv_map[pin];
+ if (!ds_idx)
+ return -ENOTSUPP;
+ ds_idx--; /* Adjust back to 0-based hardware index */
+ field = aspeed_g7_soc1_drv_field_from_idx(ds_idx);
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ ret = regmap_read(pctl->regmap, field.reg, &val_raw);
+ if (ret)
+ return ret;
+
+ val = (val_raw & (field.mask << field.shift)) >> field.shift;
+ if (param == PIN_CONFIG_DRIVE_STRENGTH)
+ val = (val + ASPEED_G7_SOC1_DRV_STRENGTH_HW_BASE) *
+ ASPEED_G7_SOC1_DRV_STRENGTH_STEP_MA;
+
+ if (!val)
+ return -EINVAL;
+
+ *config = pinconf_to_config_packed(param, val);
+
+ return 0;
+}
+
+static int aspeed_g7_soc1_pin_config_set(struct pinctrl_dev *pctldev,
+ unsigned int pin,
+ unsigned long *configs,
+ unsigned int num_configs)
+{
+ struct aspeed_g7_soc1_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+ struct aspeed_g7_field field;
+ enum pin_config_param param;
+ int i, ret, ds_idx;
+ unsigned int val;
+ u32 arg;
+
+ if (pin > AC24)
+ return -EINVAL;
+
+ for (i = 0; i < num_configs; i++) {
+ param = pinconf_to_config_param(configs[i]);
+ arg = pinconf_to_config_argument(configs[i]);
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ case PIN_CONFIG_BIAS_PULL_UP:
+ case PIN_CONFIG_BIAS_DISABLE:
+ field = aspeed_g7_soc1_bias_field_from_pin(pin);
+ val = (param == PIN_CONFIG_BIAS_DISABLE) ? 1 : 0;
+ break;
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ ds_idx = aspeed_g7_soc1_drv_map[pin];
+ if (!ds_idx)
+ return -ENOTSUPP;
+ ds_idx--; /* Adjust back to 0-based hardware index */
+ field = aspeed_g7_soc1_drv_field_from_idx(ds_idx);
+ ret = aspeed_g7_soc1_drive_strength_to_hw(arg, &val);
+ if (ret)
+ return ret;
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ dev_dbg(pctl->dev,
+ "Configuring pin %u reg 0x%x shift %u param %d arg %u val 0x%x\n",
+ pin, field.reg, field.shift, param, arg, val);
+
+ ret = regmap_update_bits(pctl->regmap, field.reg,
+ field.mask << field.shift,
+ val << field.shift);
+
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int aspeed_g7_soc1_get_group_pins(struct pinctrl_dev *pctldev,
+ unsigned int selector,
+ const unsigned int **pins,
+ unsigned int *npins)
+{
+ struct group_desc *group;
+
+ group = pinctrl_generic_get_group(pctldev, selector);
+ if (!group)
+ return -EINVAL;
+
+ if (!group->grp.npins)
+ return -ENODEV;
+
+ *pins = group->grp.pins;
+ *npins = group->grp.npins;
+
+ return 0;
+}
+
+static int aspeed_g7_soc1_pin_config_group_get(struct pinctrl_dev *pctldev,
+ unsigned int selector,
+ unsigned long *config)
+{
+ const unsigned int *pins;
+ unsigned int npins;
+ int ret;
+
+ ret = aspeed_g7_soc1_get_group_pins(pctldev, selector, &pins, &npins);
+ if (ret)
+ return ret;
+
+ return aspeed_g7_soc1_pin_config_get(pctldev, pins[0], config);
+}
+
+static int aspeed_g7_soc1_pin_config_group_set(struct pinctrl_dev *pctldev,
+ unsigned int selector,
+ unsigned long *configs,
+ unsigned int num_configs)
+{
+ const unsigned int *pins;
+ unsigned int npins;
+ int ret;
+ int i;
+
+ ret = aspeed_g7_soc1_get_group_pins(pctldev, selector, &pins, &npins);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < npins; i++) {
+ ret = aspeed_g7_soc1_pin_config_set(pctldev, pins[i], configs,
+ num_configs);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct pinconf_ops aspeed_g7_soc1_conf_ops = {
+ .is_generic = true,
+ .pin_config_get = aspeed_g7_soc1_pin_config_get,
+ .pin_config_set = aspeed_g7_soc1_pin_config_set,
+ .pin_config_group_get = aspeed_g7_soc1_pin_config_group_get,
+ .pin_config_group_set = aspeed_g7_soc1_pin_config_group_set,
+ .pin_config_config_dbg_show = pinconf_generic_dump_config,
+};
+
+static const struct pinctrl_desc aspeed_g7_soc1_desc = {
+ .name = "aspeed-g7-soc1-pinctrl",
+ .pins = aspeed_g7_soc1_pins,
+ .npins = ARRAY_SIZE(aspeed_g7_soc1_pins),
+ .pctlops = &aspeed_g7_soc1_pctl_ops,
+ .pmxops = &aspeed_g7_soc1_pmx_ops,
+ .confops = &aspeed_g7_soc1_conf_ops,
+ .owner = THIS_MODULE,
+};
+
+ #define PIN_GROUP(name, ...) static const unsigned int name ## _pins[] = { __VA_ARGS__ }
+
+/* Pin Groups and Functions */
+PIN_GROUP(ADC0, W17);
+PIN_GROUP(ADC1, V18);
+PIN_GROUP(ADC10, Y11);
+PIN_GROUP(ADC11, V14);
+PIN_GROUP(ADC12, V19);
+PIN_GROUP(ADC13, W14);
+PIN_GROUP(ADC14, Y20);
+PIN_GROUP(ADC15, AB19);
+PIN_GROUP(ADC2, W18);
+PIN_GROUP(ADC3, Y17);
+PIN_GROUP(ADC4, AA18);
+PIN_GROUP(ADC5, AA13);
+PIN_GROUP(ADC6, Y18);
+PIN_GROUP(ADC7, AA12);
+PIN_GROUP(ADC8, W20);
+PIN_GROUP(ADC9, V20);
+PIN_GROUP(AUXPWRGOOD0, W14);
+PIN_GROUP(AUXPWRGOOD1, Y20);
+PIN_GROUP(CANBUS, G7, G8, G9);
+PIN_GROUP(DI2C0, C16, D9);
+PIN_GROUP(DI2C1, C14, F14);
+PIN_GROUP(DI2C10, R25, AA26);
+PIN_GROUP(DI2C11, R26, Y25);
+PIN_GROUP(DI2C12, W25, Y23);
+PIN_GROUP(DI2C13, Y24, W21);
+PIN_GROUP(DI2C14, AA23, AC22);
+PIN_GROUP(DI2C15, AB22, Y21);
+PIN_GROUP(DI2C2, D10, C12);
+PIN_GROUP(DI2C3, C11, C13);
+PIN_GROUP(DI2C8, U25, U26);
+PIN_GROUP(DI2C9, Y26, AA24);
+PIN_GROUP(DSGPM0, D19, B10, C7, D7);
+PIN_GROUP(ESPI0, B16, D14, B15, B14, C17, B13, E14, C15);
+PIN_GROUP(ESPI1, C16, C14, C11, D9, F14, D10, C12, C13);
+PIN_GROUP(FSI0, AD20, AC20);
+PIN_GROUP(FSI1, AA21, AB21);
+PIN_GROUP(FSI2, AC19, AE18);
+PIN_GROUP(FSI3, AD19, AD18);
+PIN_GROUP(FWQSPI, M16, A15);
+PIN_GROUP(FWSPIABR, A14);
+PIN_GROUP(FWWPN, N15);
+PIN_GROUP(HBLED, V24);
+PIN_GROUP(HVI3C0, U25, U26);
+PIN_GROUP(HVI3C1, Y26, AA24);
+PIN_GROUP(HVI3C12, W25, Y23);
+PIN_GROUP(HVI3C13, Y24, W21);
+PIN_GROUP(HVI3C14, AA23, AC22);
+PIN_GROUP(HVI3C15, AB22, Y21);
+PIN_GROUP(HVI3C2, R25, AA26);
+PIN_GROUP(HVI3C3, R26, Y25);
+PIN_GROUP(I2C0, G11, H7);
+PIN_GROUP(I2C1, H8, H9);
+PIN_GROUP(I2C10, G8, G9);
+PIN_GROUP(I2C11, G10, K12);
+PIN_GROUP(I2C12, AC18, AA17);
+PIN_GROUP(I2C13, AB17, AD16);
+PIN_GROUP(I2C14, AC17, AD17);
+PIN_GROUP(I2C15, AE16, AE17);
+PIN_GROUP(I2C2, H10, H11);
+PIN_GROUP(I2C3, J9, J10);
+PIN_GROUP(I2C4, E9, F9);
+PIN_GROUP(I2C5, F8, M13);
+PIN_GROUP(I2C6, F7, D8);
+PIN_GROUP(I2C7, E8, L12);
+PIN_GROUP(I2C8, F12, E12);
+PIN_GROUP(I2C9, J12, G7);
+PIN_GROUP(I2CF0, F12, E12, J12, G7);
+PIN_GROUP(I2CF1, E9, F9, F8, M13);
+PIN_GROUP(I2CF2, F7, D8, E8, L12);
+PIN_GROUP(I3C10, AC19, AE18);
+PIN_GROUP(I3C11, AD19, AD18);
+PIN_GROUP(I3C4, AE20, AF19);
+PIN_GROUP(I3C5, Y22, AA20);
+PIN_GROUP(I3C6, AA22, AB20);
+PIN_GROUP(I3C7, AF18, AE19);
+PIN_GROUP(I3C8, AD20, AC20);
+PIN_GROUP(I3C9, AA21, AB21);
+PIN_GROUP(JTAGM1, D12, F10, E11, F11, F13);
+PIN_GROUP(LPC0, AF26, AF25, B16, D14, B15, B14, C17, B13, E14, C15);
+PIN_GROUP(LPC1, C16, C14, C11, D9, F14, D10, C12, C13, AE16, AE17);
+PIN_GROUP(LTPI, U25, U26, Y26, AA24);
+PIN_GROUP(LTPI_PS_I2C0, G11, H7);
+PIN_GROUP(LTPI_PS_I2C1, H8, H9);
+PIN_GROUP(LTPI_PS_I2C2, H10, H11);
+PIN_GROUP(LTPI_PS_I2C3, J9, J10);
+PIN_GROUP(MACLINK0, U21);
+PIN_GROUP(MACLINK1, AC24);
+PIN_GROUP(MACLINK2, T24);
+PIN_GROUP(MDIO0, B9, M14);
+PIN_GROUP(MDIO1, C9, C10);
+PIN_GROUP(MDIO2, E10, E13);
+PIN_GROUP(NCTS0, AF17);
+PIN_GROUP(NCTS1, AA15);
+PIN_GROUP(NCTS5, V21);
+PIN_GROUP(NCTS6, AB26);
+PIN_GROUP(NDCD0, AA16);
+PIN_GROUP(NDCD1, AB15);
+PIN_GROUP(NDCD5, N26);
+PIN_GROUP(NDCD6, AD26);
+PIN_GROUP(NDSR0, Y16);
+PIN_GROUP(NDSR1, AC15);
+PIN_GROUP(NDSR5, P25);
+PIN_GROUP(NDSR6, P26);
+PIN_GROUP(NDTR0, J13);
+PIN_GROUP(NDTR1, Y15);
+PIN_GROUP(NDTR5, V23);
+PIN_GROUP(NDTR6, AF26);
+PIN_GROUP(NRI0, V17);
+PIN_GROUP(NRI1, AD15);
+PIN_GROUP(NRI5, N25);
+PIN_GROUP(NRI6, AE26);
+PIN_GROUP(NRTS0, AB16);
+PIN_GROUP(NRTS1, AA14);
+PIN_GROUP(NRTS5, W22);
+PIN_GROUP(NRTS6, AF25);
+PIN_GROUP(OSCCLK, C17);
+PIN_GROUP(PE2SGRSTN, E10, PCIERC2_PERST);
+PIN_GROUP(PWM0, AE25);
+PIN_GROUP(PWM1, AD25);
+PIN_GROUP(PWM10, AB17);
+PIN_GROUP(PWM11, AD16);
+PIN_GROUP(PWM12, AC17);
+PIN_GROUP(PWM13, AD17);
+PIN_GROUP(PWM14, AE16);
+PIN_GROUP(PWM15, AE17);
+PIN_GROUP(PWM2, AF23);
+PIN_GROUP(PWM3, AF20);
+PIN_GROUP(PWM4, AF21);
+PIN_GROUP(PWM5, AE21);
+PIN_GROUP(PWM6, AE23);
+PIN_GROUP(PWM7, AD22);
+PIN_GROUP(PWM8, K13);
+PIN_GROUP(PWM9, AA17);
+PIN_GROUP(QSPI0, C23, B18);
+PIN_GROUP(QSPI1, B24, E26);
+PIN_GROUP(QSPI2, B25, F26);
+PIN_GROUP(RGMII0, C20, C19, A8, R14, A7, P14, D20, A6, B6, N14, B7, B8);
+PIN_GROUP(RGMII1, D19, B11, D15, B12, B10, P13, C18, C6, C7, D7, N13, C8);
+PIN_GROUP(RMII0, C20, A8, R14, A7, P14, A6, B6, N14);
+PIN_GROUP(RMII0RCLKO, D20);
+PIN_GROUP(RMII1, D19, D15, B12, B10, P13, C6, C7, D7);
+PIN_GROUP(RMII1RCLKO, C18);
+PIN_GROUP(SALT0, AC17);
+PIN_GROUP(SALT1, AD17);
+PIN_GROUP(SALT10, Y18);
+PIN_GROUP(SALT11, AA12);
+PIN_GROUP(SALT12, AB26);
+PIN_GROUP(SALT13, AD26);
+PIN_GROUP(SALT14, P26);
+PIN_GROUP(SALT15, AE26);
+PIN_GROUP(SALT2, AC15);
+PIN_GROUP(SALT3, AD15);
+PIN_GROUP(SALT4, W17);
+PIN_GROUP(SALT5, V18);
+PIN_GROUP(SALT6, W18);
+PIN_GROUP(SALT7, Y17);
+PIN_GROUP(SALT8, AA18);
+PIN_GROUP(SALT9, AA13);
+PIN_GROUP(SD, C16, C14, C11, D9, F14, D10, C12, C13);
+PIN_GROUP(SGMII, SGMII0);
+PIN_GROUP(SGPM0, U21, T24, V22, T23);
+PIN_GROUP(SGPM1, AC25, AB25, AB24, W26);
+PIN_GROUP(SGPS, B11, C18, N13, C8);
+PIN_GROUP(SIOONCTRLN0, AE23);
+PIN_GROUP(SIOONCTRLN1, AA15);
+PIN_GROUP(SIOPBIN0, AD25);
+PIN_GROUP(SIOPBIN1, AA16);
+PIN_GROUP(SIOPBON0, AE25);
+PIN_GROUP(SIOPBON1, AF17);
+PIN_GROUP(SIOPWREQN0, AE21);
+PIN_GROUP(SIOPWREQN1, AB16);
+PIN_GROUP(SIOPWRGD1, AB15);
+PIN_GROUP(SIOS3N0, AF20);
+PIN_GROUP(SIOS3N1, V17);
+PIN_GROUP(SIOS5N0, AF21);
+PIN_GROUP(SIOS5N1, J13);
+PIN_GROUP(SIOSCIN0, AF23);
+PIN_GROUP(SIOSCIN1, Y16);
+PIN_GROUP(SMON0, U21, T24, V22, T23);
+PIN_GROUP(SMON1, W26, AC25, AB25);
+PIN_GROUP(SPI0, D24, B23, B22);
+PIN_GROUP(SPI0ABR, M15);
+PIN_GROUP(SPI0CS1, B21);
+PIN_GROUP(SPI0WPN, B19);
+PIN_GROUP(SPI1, B26, A25, A24);
+PIN_GROUP(SPI1ABR, A19);
+PIN_GROUP(SPI1CS1, A21);
+PIN_GROUP(SPI1WPN, A18);
+PIN_GROUP(SPI2, D26, C26, A23, A22);
+PIN_GROUP(SPI2CS1, A26);
+PIN_GROUP(TACH0, AC26);
+PIN_GROUP(TACH1, AA25);
+PIN_GROUP(TACH10, AB26);
+PIN_GROUP(TACH11, AD26);
+PIN_GROUP(TACH12, P26);
+PIN_GROUP(TACH13, AE26);
+PIN_GROUP(TACH14, AF26);
+PIN_GROUP(TACH15, AF25);
+PIN_GROUP(TACH2, AB23);
+PIN_GROUP(TACH3, U22);
+PIN_GROUP(TACH4, V21);
+PIN_GROUP(TACH5, N26);
+PIN_GROUP(TACH6, P25);
+PIN_GROUP(TACH7, N25);
+PIN_GROUP(TACH8, V23);
+PIN_GROUP(TACH9, W22);
+PIN_GROUP(THRU0, AC26, AA25);
+PIN_GROUP(THRU1, AB23, U22);
+PIN_GROUP(THRU2, A19, A18);
+PIN_GROUP(THRU3, B25, F26);
+PIN_GROUP(UART0, AC16, AF16);
+PIN_GROUP(UART1, W16, V16);
+PIN_GROUP(UART2, AB18, AC18);
+PIN_GROUP(UART3, K13, AA17);
+PIN_GROUP(UART5, AB17, AD16);
+PIN_GROUP(UART6, AC17, AD17);
+PIN_GROUP(UART7, AE16, AE17);
+PIN_GROUP(UART8, M15, B19);
+PIN_GROUP(UART9, B26, A25);
+PIN_GROUP(UART10, A24, B24);
+PIN_GROUP(UART11, E26, A21);
+PIN_GROUP(USB2CD, PORTC_MODE);
+PIN_GROUP(USB2CH, PORTC_MODE);
+PIN_GROUP(USB2CU, PORTC_MODE);
+PIN_GROUP(USB2CUD, PORTC_MODE);
+PIN_GROUP(USB2DD, PORTD_MODE);
+PIN_GROUP(USB2DH, PORTD_MODE);
+PIN_GROUP(USBUART, G10, K12);
+PIN_GROUP(VGA, J11, E7);
+PIN_GROUP(VPI, C16, C14, C11, D9, F14, D10, AC26, AA25, AB23, U22, V21, N26,
+ P25, N25, V23, W22, AB26, AD26, P26, AE26, AF26, AF25, AE25, AD25,
+ AF23, AF20, AF21, AE21);
+PIN_GROUP(WDTRST0N, K13);
+PIN_GROUP(WDTRST1N, AA17);
+PIN_GROUP(WDTRST2N, AB17);
+PIN_GROUP(WDTRST3N, AD16);
+PIN_GROUP(WDTRST4N, AC25);
+PIN_GROUP(WDTRST5N, AB25);
+PIN_GROUP(WDTRST6N, AC24);
+PIN_GROUP(WDTRST7N, AB24);
+
+#define GROUP(n) PINCTRL_PINGROUP(#n, n##_pins, ARRAY_SIZE(n##_pins))
+
+static const struct pingroup aspeed_g7_soc1_groups[] = {
+ GROUP(ADC0),
+ GROUP(ADC1),
+ GROUP(ADC10),
+ GROUP(ADC11),
+ GROUP(ADC12),
+ GROUP(ADC13),
+ GROUP(ADC14),
+ GROUP(ADC15),
+ GROUP(ADC2),
+ GROUP(ADC3),
+ GROUP(ADC4),
+ GROUP(ADC5),
+ GROUP(ADC6),
+ GROUP(ADC7),
+ GROUP(ADC8),
+ GROUP(ADC9),
+ GROUP(AUXPWRGOOD0),
+ GROUP(AUXPWRGOOD1),
+ GROUP(CANBUS),
+ GROUP(DI2C0),
+ GROUP(DI2C1),
+ GROUP(DI2C10),
+ GROUP(DI2C11),
+ GROUP(DI2C12),
+ GROUP(DI2C13),
+ GROUP(DI2C14),
+ GROUP(DI2C15),
+ GROUP(DI2C2),
+ GROUP(DI2C3),
+ GROUP(DI2C8),
+ GROUP(DI2C9),
+ GROUP(DSGPM0),
+ GROUP(ESPI0),
+ GROUP(ESPI1),
+ GROUP(FSI0),
+ GROUP(FSI1),
+ GROUP(FSI2),
+ GROUP(FSI3),
+ GROUP(FWQSPI),
+ GROUP(FWSPIABR),
+ GROUP(FWWPN),
+ GROUP(HBLED),
+ GROUP(HVI3C0),
+ GROUP(HVI3C1),
+ GROUP(HVI3C12),
+ GROUP(HVI3C13),
+ GROUP(HVI3C14),
+ GROUP(HVI3C15),
+ GROUP(HVI3C2),
+ GROUP(HVI3C3),
+ GROUP(I2C0),
+ GROUP(I2C1),
+ GROUP(I2C10),
+ GROUP(I2C11),
+ GROUP(I2C12),
+ GROUP(I2C13),
+ GROUP(I2C14),
+ GROUP(I2C15),
+ GROUP(I2C2),
+ GROUP(I2C3),
+ GROUP(I2C4),
+ GROUP(I2C5),
+ GROUP(I2C6),
+ GROUP(I2C7),
+ GROUP(I2C8),
+ GROUP(I2C9),
+ GROUP(I2CF0),
+ GROUP(I2CF1),
+ GROUP(I2CF2),
+ GROUP(I3C10),
+ GROUP(I3C11),
+ GROUP(I3C4),
+ GROUP(I3C5),
+ GROUP(I3C6),
+ GROUP(I3C7),
+ GROUP(I3C8),
+ GROUP(I3C9),
+ GROUP(JTAGM1),
+ GROUP(LPC0),
+ GROUP(LPC1),
+ GROUP(LTPI),
+ GROUP(LTPI_PS_I2C0),
+ GROUP(LTPI_PS_I2C1),
+ GROUP(LTPI_PS_I2C2),
+ GROUP(LTPI_PS_I2C3),
+ GROUP(MACLINK0),
+ GROUP(MACLINK1),
+ GROUP(MACLINK2),
+ GROUP(MDIO0),
+ GROUP(MDIO1),
+ GROUP(MDIO2),
+ GROUP(NCTS0),
+ GROUP(NCTS1),
+ GROUP(NCTS5),
+ GROUP(NCTS6),
+ GROUP(NDCD0),
+ GROUP(NDCD1),
+ GROUP(NDCD5),
+ GROUP(NDCD6),
+ GROUP(NDSR0),
+ GROUP(NDSR1),
+ GROUP(NDSR5),
+ GROUP(NDSR6),
+ GROUP(NDTR0),
+ GROUP(NDTR1),
+ GROUP(NDTR5),
+ GROUP(NDTR6),
+ GROUP(NRI0),
+ GROUP(NRI1),
+ GROUP(NRI5),
+ GROUP(NRI6),
+ GROUP(NRTS0),
+ GROUP(NRTS1),
+ GROUP(NRTS5),
+ GROUP(NRTS6),
+ GROUP(OSCCLK),
+ GROUP(PE2SGRSTN),
+ GROUP(PWM0),
+ GROUP(PWM1),
+ GROUP(PWM10),
+ GROUP(PWM11),
+ GROUP(PWM12),
+ GROUP(PWM13),
+ GROUP(PWM14),
+ GROUP(PWM15),
+ GROUP(PWM2),
+ GROUP(PWM3),
+ GROUP(PWM4),
+ GROUP(PWM5),
+ GROUP(PWM6),
+ GROUP(PWM7),
+ GROUP(PWM8),
+ GROUP(PWM9),
+ GROUP(QSPI0),
+ GROUP(QSPI1),
+ GROUP(QSPI2),
+ GROUP(RGMII0),
+ GROUP(RGMII1),
+ GROUP(RMII0),
+ GROUP(RMII0RCLKO),
+ GROUP(RMII1),
+ GROUP(RMII1RCLKO),
+ GROUP(SALT0),
+ GROUP(SALT1),
+ GROUP(SALT10),
+ GROUP(SALT11),
+ GROUP(SALT12),
+ GROUP(SALT13),
+ GROUP(SALT14),
+ GROUP(SALT15),
+ GROUP(SALT2),
+ GROUP(SALT3),
+ GROUP(SALT4),
+ GROUP(SALT5),
+ GROUP(SALT6),
+ GROUP(SALT7),
+ GROUP(SALT8),
+ GROUP(SALT9),
+ GROUP(SD),
+ GROUP(SGMII),
+ GROUP(SGPM0),
+ GROUP(SGPM1),
+ GROUP(SGPS),
+ GROUP(SIOONCTRLN0),
+ GROUP(SIOONCTRLN1),
+ GROUP(SIOPBIN0),
+ GROUP(SIOPBIN1),
+ GROUP(SIOPBON0),
+ GROUP(SIOPBON1),
+ GROUP(SIOPWREQN0),
+ GROUP(SIOPWREQN1),
+ GROUP(SIOPWRGD1),
+ GROUP(SIOS3N0),
+ GROUP(SIOS3N1),
+ GROUP(SIOS5N0),
+ GROUP(SIOS5N1),
+ GROUP(SIOSCIN0),
+ GROUP(SIOSCIN1),
+ GROUP(SMON0),
+ GROUP(SMON1),
+ GROUP(SPI0),
+ GROUP(SPI0ABR),
+ GROUP(SPI0CS1),
+ GROUP(SPI0WPN),
+ GROUP(SPI1),
+ GROUP(SPI1ABR),
+ GROUP(SPI1CS1),
+ GROUP(SPI1WPN),
+ GROUP(SPI2),
+ GROUP(SPI2CS1),
+ GROUP(TACH0),
+ GROUP(TACH1),
+ GROUP(TACH10),
+ GROUP(TACH11),
+ GROUP(TACH12),
+ GROUP(TACH13),
+ GROUP(TACH14),
+ GROUP(TACH15),
+ GROUP(TACH2),
+ GROUP(TACH3),
+ GROUP(TACH4),
+ GROUP(TACH5),
+ GROUP(TACH6),
+ GROUP(TACH7),
+ GROUP(TACH8),
+ GROUP(TACH9),
+ GROUP(THRU0),
+ GROUP(THRU1),
+ GROUP(THRU2),
+ GROUP(THRU3),
+ GROUP(UART0),
+ GROUP(UART1),
+ GROUP(UART10),
+ GROUP(UART11),
+ GROUP(UART2),
+ GROUP(UART3),
+ GROUP(UART5),
+ GROUP(UART6),
+ GROUP(UART7),
+ GROUP(UART8),
+ GROUP(UART9),
+ GROUP(USB2CD),
+ GROUP(USB2CH),
+ GROUP(USB2CU),
+ GROUP(USB2CUD),
+ GROUP(USB2DD),
+ GROUP(USB2DH),
+ GROUP(USBUART),
+ GROUP(VGA),
+ GROUP(VPI),
+ GROUP(WDTRST0N),
+ GROUP(WDTRST1N),
+ GROUP(WDTRST2N),
+ GROUP(WDTRST3N),
+ GROUP(WDTRST4N),
+ GROUP(WDTRST5N),
+ GROUP(WDTRST6N),
+ GROUP(WDTRST7N),
+};
+
+/**
+ * VM() - Helper macro to unwrap a parenthesized list of arguments.
+ * @...: The parenthesized list to be unwrapped.
+ *
+ * Since the C preprocessor treats commas inside braces {} as argument
+ * separators for macros, we wrap lists (like mux values) in parentheses ()
+ * to protect them during macro expansion. This macro strips those
+ * parentheses when the values are needed for array initialization.
+ */
+#define VM(...) __VA_ARGS__
+
+/**
+ * FUNC() - Macro to initialize an aspeed_g7_soc1_function entry.
+ * @n: Name of the pin function.
+ * @m: Parenthesized list of mux values, mapped 1:1 to the groups list.
+ * @...: Variable list of pin group names associated with this function.
+ *
+ * This macro solves complex static initialization by:
+ * 1. Creating anonymous arrays for both group names and mux values
+ * using C99 Compound Literals.
+ * 2. Using VM(m) to unwrap mux values into the array initializer.
+ * 3. Calculating the number of groups via sizeof() division, which
+ * bypasses the __must_be_array() check performed by ARRAY_SIZE()
+ * that often fails on compound literals in the kernel environment.
+ *
+ * Example: FUNC(i2c0, (1, 4), "i2c0", "di2c0")
+ * Maps "i2c0" group to mux value 1 and "di2c0" group to mux value 4.
+ */
+#define FUNC(n, m, ...) \
+ { \
+ .pinfunction = { \
+ .name = #n, \
+ .groups = (const char *const[]){ __VA_ARGS__ }, \
+ .ngroups = sizeof((const char *const[]){ __VA_ARGS__ }) / sizeof(char *), \
+ }, \
+ .muxvals = (const u8[]){ VM m } \
+ }
+
+static const struct aspeed_g7_soc1_function aspeed_g7_soc1_functions[] = {
+ FUNC(ADC0, (0), "ADC0"),
+ FUNC(ADC1, (0), "ADC1"),
+ FUNC(ADC10, (0), "ADC10"),
+ FUNC(ADC11, (0), "ADC11"),
+ FUNC(ADC12, (0), "ADC12"),
+ FUNC(ADC13, (0), "ADC13"),
+ FUNC(ADC14, (0), "ADC14"),
+ FUNC(ADC15, (0), "ADC15"),
+ FUNC(ADC2, (0), "ADC2"),
+ FUNC(ADC3, (0), "ADC3"),
+ FUNC(ADC4, (0), "ADC4"),
+ FUNC(ADC5, (0), "ADC5"),
+ FUNC(ADC6, (0), "ADC6"),
+ FUNC(ADC7, (0), "ADC7"),
+ FUNC(ADC8, (0), "ADC8"),
+ FUNC(ADC9, (0), "ADC9"),
+ FUNC(AUXPWRGOOD0, (2), "AUXPWRGOOD0"),
+ FUNC(AUXPWRGOOD1, (2), "AUXPWRGOOD1"),
+ FUNC(CANBUS, (2), "CANBUS"),
+ FUNC(ESPI0, (1), "ESPI0"),
+ FUNC(ESPI1, (1), "ESPI1"),
+ FUNC(FSI0, (2), "FSI0"),
+ FUNC(FSI1, (2), "FSI1"),
+ FUNC(FSI2, (2), "FSI2"),
+ FUNC(FSI3, (2), "FSI3"),
+ FUNC(FWQSPI, (1), "FWQSPI"),
+ FUNC(FWSPIABR, (1), "FWSPIABR"),
+ FUNC(FWWPN, (1), "FWWPN"),
+ FUNC(HBLED, (2), "HBLED"),
+ FUNC(I2C0, (1, 2, 4), "I2C0", "LTPI_PS_I2C0", "DI2C0"),
+ FUNC(I2C1, (1, 2, 4), "I2C1", "LTPI_PS_I2C1", "DI2C1"),
+ FUNC(I2C10, (1, 2), "I2C10", "DI2C10"),
+ FUNC(I2C11, (1, 2), "I2C11", "DI2C11"),
+ FUNC(I2C12, (4, 2), "I2C12", "DI2C12"),
+ FUNC(I2C13, (4, 2), "I2C13", "DI2C13"),
+ FUNC(I2C14, (4, 2), "I2C14", "DI2C14"),
+ FUNC(I2C15, (2, 2), "I2C15", "DI2C15"),
+ FUNC(I2C2, (1, 2, 4), "I2C2", "LTPI_PS_I2C2", "DI2C2"),
+ FUNC(I2C3, (1, 2, 4), "I2C3", "LTPI_PS_I2C3", "DI2C3"),
+ FUNC(I2C4, (1), "I2C4"),
+ FUNC(I2C5, (1), "I2C5"),
+ FUNC(I2C6, (1), "I2C6"),
+ FUNC(I2C7, (1), "I2C7"),
+ FUNC(I2C8, (1, 2), "I2C8", "DI2C8"),
+ FUNC(I2C9, (1, 2), "I2C9", "DI2C9"),
+ FUNC(I2CF0, (5), "I2CF0"),
+ FUNC(I2CF1, (5), "I2CF1"),
+ FUNC(I2CF2, (5), "I2CF2"),
+ FUNC(I3C0, (1), "HVI3C0"),
+ FUNC(I3C1, (1), "HVI3C1"),
+ FUNC(I3C10, (1), "I3C10"),
+ FUNC(I3C11, (1), "I3C11"),
+ FUNC(I3C12, (1), "HVI3C12"),
+ FUNC(I3C13, (1), "HVI3C13"),
+ FUNC(I3C14, (1), "HVI3C14"),
+ FUNC(I3C15, (1), "HVI3C15"),
+ FUNC(I3C2, (1), "HVI3C2"),
+ FUNC(I3C3, (1), "HVI3C3"),
+ FUNC(I3C4, (1), "I3C4"),
+ FUNC(I3C5, (1), "I3C5"),
+ FUNC(I3C6, (1), "I3C6"),
+ FUNC(I3C7, (1), "I3C7"),
+ FUNC(I3C8, (1), "I3C8"),
+ FUNC(I3C9, (1), "I3C9"),
+ FUNC(JTAGM1, (1), "JTAGM1"),
+ FUNC(LPC0, (2), "LPC0"),
+ FUNC(LPC1, (2), "LPC1"),
+ FUNC(LTPI, (2), "LTPI"),
+ FUNC(MACLINK0, (4), "MACLINK0"),
+ FUNC(MACLINK1, (3), "MACLINK1"),
+ FUNC(MACLINK2, (4), "MACLINK2"),
+ FUNC(MDIO0, (1), "MDIO0"),
+ FUNC(MDIO1, (1), "MDIO1"),
+ FUNC(MDIO2, (1), "MDIO2"),
+ FUNC(NCTS0, (1), "NCTS0"),
+ FUNC(NCTS1, (1), "NCTS1"),
+ FUNC(NCTS5, (4), "NCTS5"),
+ FUNC(NCTS6, (4), "NCTS6"),
+ FUNC(NDCD0, (1), "NDCD0"),
+ FUNC(NDCD1, (1), "NDCD1"),
+ FUNC(NDCD5, (4), "NDCD5"),
+ FUNC(NDCD6, (4), "NDCD6"),
+ FUNC(NDSR0, (1), "NDSR0"),
+ FUNC(NDSR1, (1), "NDSR1"),
+ FUNC(NDSR5, (4), "NDSR5"),
+ FUNC(NDSR6, (4), "NDSR6"),
+ FUNC(NDTR0, (1), "NDTR0"),
+ FUNC(NDTR1, (1), "NDTR1"),
+ FUNC(NDTR5, (4), "NDTR5"),
+ FUNC(NDTR6, (4), "NDTR6"),
+ FUNC(NRI0, (1), "NRI0"),
+ FUNC(NRI1, (1), "NRI1"),
+ FUNC(NRI5, (4), "NRI5"),
+ FUNC(NRI6, (4), "NRI6"),
+ FUNC(NRTS0, (1), "NRTS0"),
+ FUNC(NRTS1, (1), "NRTS1"),
+ FUNC(NRTS5, (4), "NRTS5"),
+ FUNC(NRTS6, (4), "NRTS6"),
+ FUNC(OSCCLK, (3), "OSCCLK"),
+ FUNC(PCIERC, (2), "PE2SGRSTN"),
+ FUNC(PWM0, (1), "PWM0"),
+ FUNC(PWM1, (1), "PWM1"),
+ FUNC(PWM10, (3), "PWM10"),
+ FUNC(PWM11, (3), "PWM11"),
+ FUNC(PWM12, (3), "PWM12"),
+ FUNC(PWM13, (3), "PWM13"),
+ FUNC(PWM14, (3), "PWM14"),
+ FUNC(PWM15, (3), "PWM15"),
+ FUNC(PWM2, (1), "PWM2"),
+ FUNC(PWM3, (1), "PWM3"),
+ FUNC(PWM4, (1), "PWM4"),
+ FUNC(PWM5, (1), "PWM5"),
+ FUNC(PWM6, (1), "PWM6"),
+ FUNC(PWM7, (1), "PWM7"),
+ FUNC(PWM8, (3), "PWM8"),
+ FUNC(PWM9, (3), "PWM9"),
+ FUNC(QSPI0, (1), "QSPI0"),
+ FUNC(QSPI1, (1), "QSPI1"),
+ FUNC(QSPI2, (1), "QSPI2"),
+ FUNC(RGMII0, (1), "RGMII0"),
+ FUNC(RGMII1, (1), "RGMII1"),
+ FUNC(RMII0, (2), "RMII0"),
+ FUNC(RMII0RCLKO, (2), "RMII0RCLKO"),
+ FUNC(RMII1, (2), "RMII1"),
+ FUNC(RMII1RCLKO, (2), "RMII1RCLKO"),
+ FUNC(SALT0, (2), "SALT0"),
+ FUNC(SALT1, (2), "SALT1"),
+ FUNC(SALT10, (2), "SALT10"),
+ FUNC(SALT11, (2), "SALT11"),
+ FUNC(SALT12, (2), "SALT12"),
+ FUNC(SALT13, (2), "SALT13"),
+ FUNC(SALT14, (2), "SALT14"),
+ FUNC(SALT15, (2), "SALT15"),
+ FUNC(SALT2, (2), "SALT2"),
+ FUNC(SALT3, (2), "SALT3"),
+ FUNC(SALT4, (2), "SALT4"),
+ FUNC(SALT5, (2), "SALT5"),
+ FUNC(SALT6, (2), "SALT6"),
+ FUNC(SALT7, (2), "SALT7"),
+ FUNC(SALT8, (2), "SALT8"),
+ FUNC(SALT9, (2), "SALT9"),
+ FUNC(SD, (3), "SD"),
+ FUNC(SGMII, (1), "SGMII"),
+ FUNC(SGPM0, (1, 4), "SGPM0", "DSGPM0"),
+ FUNC(SGPM1, (1), "SGPM1"),
+ FUNC(SGPS, (5), "SGPS"),
+ FUNC(SIOONCTRLN0, (2), "SIOONCTRLN0"),
+ FUNC(SIOONCTRLN1, (2), "SIOONCTRLN1"),
+ FUNC(SIOPBIN0, (2), "SIOPBIN0"),
+ FUNC(SIOPBIN1, (2), "SIOPBIN1"),
+ FUNC(SIOPBON0, (2), "SIOPBON0"),
+ FUNC(SIOPBON1, (2), "SIOPBON1"),
+ FUNC(SIOPWREQN0, (2), "SIOPWREQN0"),
+ FUNC(SIOPWREQN1, (2), "SIOPWREQN1"),
+ FUNC(SIOPWRGD1, (2), "SIOPWRGD1"),
+ FUNC(SIOS3N0, (2), "SIOS3N0"),
+ FUNC(SIOS3N1, (2), "SIOS3N1"),
+ FUNC(SIOS5N0, (2), "SIOS5N0"),
+ FUNC(SIOS5N1, (2), "SIOS5N1"),
+ FUNC(SIOSCIN0, (2), "SIOSCIN0"),
+ FUNC(SIOSCIN1, (2), "SIOSCIN1"),
+ FUNC(SMON0, (2), "SMON0"),
+ FUNC(SMON1, (4), "SMON1"),
+ FUNC(SPI0, (1), "SPI0"),
+ FUNC(SPI0ABR, (1), "SPI0ABR"),
+ FUNC(SPI0CS1, (1), "SPI0CS1"),
+ FUNC(SPI0WPN, (1), "SPI0WPN"),
+ FUNC(SPI1, (1), "SPI1"),
+ FUNC(SPI1ABR, (1), "SPI1ABR"),
+ FUNC(SPI1CS1, (1), "SPI1CS1"),
+ FUNC(SPI1WPN, (1), "SPI1WPN"),
+ FUNC(SPI2, (1), "SPI2"),
+ FUNC(SPI2CS1, (1), "SPI2CS1"),
+ FUNC(TACH0, (1), "TACH0"),
+ FUNC(TACH1, (1), "TACH1"),
+ FUNC(TACH10, (1), "TACH10"),
+ FUNC(TACH11, (1), "TACH11"),
+ FUNC(TACH12, (1), "TACH12"),
+ FUNC(TACH13, (1), "TACH13"),
+ FUNC(TACH14, (1), "TACH14"),
+ FUNC(TACH15, (1), "TACH15"),
+ FUNC(TACH2, (1), "TACH2"),
+ FUNC(TACH3, (1), "TACH3"),
+ FUNC(TACH4, (1), "TACH4"),
+ FUNC(TACH5, (1), "TACH5"),
+ FUNC(TACH6, (1), "TACH6"),
+ FUNC(TACH7, (1), "TACH7"),
+ FUNC(TACH8, (1), "TACH8"),
+ FUNC(TACH9, (1), "TACH9"),
+ FUNC(THRU0, (2), "THRU0"),
+ FUNC(THRU1, (2), "THRU1"),
+ FUNC(THRU2, (4), "THRU2"),
+ FUNC(THRU3, (4), "THRU3"),
+ FUNC(UART0, (1), "UART0"),
+ FUNC(UART1, (1), "UART1"),
+ FUNC(UART10, (3), "UART10"),
+ FUNC(UART11, (3), "UART11"),
+ FUNC(UART2, (1), "UART2"),
+ FUNC(UART3, (1), "UART3"),
+ FUNC(UART5, (4), "UART5"),
+ FUNC(UART6, (4), "UART6"),
+ FUNC(UART7, (1), "UART7"),
+ FUNC(UART8, (3), "UART8"),
+ FUNC(UART9, (3), "UART9"),
+ FUNC(USB2C, (0, 1, 2, 3), "USB2CUD", "USB2CD", "USB2CH", "USB2CU"),
+ FUNC(USB2D, (1, 2), "USB2DD", "USB2DH"),
+ FUNC(USBUART, (2), "USBUART"),
+ FUNC(VGA, (1), "VGA"),
+ FUNC(VPI, (5), "VPI"),
+ FUNC(WDTRST0N, (2), "WDTRST0N"),
+ FUNC(WDTRST1N, (2), "WDTRST1N"),
+ FUNC(WDTRST2N, (2), "WDTRST2N"),
+ FUNC(WDTRST3N, (2), "WDTRST3N"),
+ FUNC(WDTRST4N, (2), "WDTRST4N"),
+ FUNC(WDTRST5N, (2), "WDTRST5N"),
+ FUNC(WDTRST6N, (2), "WDTRST6N"),
+ FUNC(WDTRST7N, (2), "WDTRST7N"),
+};
+
+static int aspeed_g7_soc1_pinctrl_probe(struct platform_device *pdev)
+{
+ struct aspeed_g7_soc1_pinctrl *pctl;
+ struct device *dev = &pdev->dev;
+ int i, ret;
+
+ pctl = devm_kzalloc(dev, sizeof(*pctl), GFP_KERNEL);
+ if (!pctl)
+ return -ENOMEM;
+
+ pctl->dev = dev;
+ pctl->regmap = syscon_node_to_regmap(dev->parent->of_node);
+ if (IS_ERR(pctl->regmap)) {
+ dev_err(dev, "Failed to get regmap from parent\n");
+ return PTR_ERR(pctl->regmap);
+ }
+
+ ret = devm_pinctrl_register_and_init(dev, &aspeed_g7_soc1_desc, pctl,
+ &pctl->pctl);
+ if (ret) {
+ dev_err(dev, "Failed to register pinctrl\n");
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(aspeed_g7_soc1_groups); i++) {
+ const struct pingroup *grp = &aspeed_g7_soc1_groups[i];
+
+ ret = pinctrl_generic_add_group(pctl->pctl, grp->name,
+ (const unsigned int *)grp->pins,
+ grp->npins, pctl);
+ if (ret < 0) {
+ dev_err(dev, "Failed to add group %s\n", grp->name);
+ return ret;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(aspeed_g7_soc1_functions); i++) {
+ const struct aspeed_g7_soc1_function *func = &aspeed_g7_soc1_functions[i];
+
+ ret = pinmux_generic_add_function(pctl->pctl, func->pinfunction.name,
+ func->pinfunction.groups,
+ func->pinfunction.ngroups, (void *)func);
+ if (ret < 0) {
+ dev_err(dev, "Failed to add function %s\n", func->pinfunction.name);
+ return ret;
+ }
+ }
+
+ return pinctrl_enable(pctl->pctl);
+}
+
+static const struct of_device_id aspeed_g7_soc1_pinctrl_match[] = {
+ { .compatible = "aspeed,ast2700-soc1-pinctrl" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, aspeed_g7_soc1_pinctrl_match);
+
+static struct platform_driver aspeed_g7_soc1_pinctrl_driver = {
+ .probe = aspeed_g7_soc1_pinctrl_probe,
+ .driver = {
+ .name = "aspeed-g7-soc1-pinctrl",
+ .of_match_table = aspeed_g7_soc1_pinctrl_match,
+ .suppress_bind_attrs = true,
+ },
+};
+
+static int __init aspeed_g7_soc1_pinctrl_init(void)
+{
+ return platform_driver_register(&aspeed_g7_soc1_pinctrl_driver);
+}
+arch_initcall(aspeed_g7_soc1_pinctrl_init);
--
2.34.1
^ permalink raw reply related
* [PATCH v4 2/3] dt-bindings: pinctrl: Add aspeed,ast2700-soc1-pinctrl
From: Billy Tsai @ 2026-05-19 10:38 UTC (permalink / raw)
To: Linus Walleij, Tony Lindgren, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Joel Stanley, Andrew Jeffery, Bartosz Golaszewski,
Lee Jones, Ryan Chen
Cc: patrickw3, linux-gpio, devicetree, linux-kernel, linux-arm-kernel,
linux-aspeed, BMC-SW, openbmc, Andrew Jeffery, linux-clk,
Billy Tsai, Conor Dooley
In-Reply-To: <20260519-pinctrl-single-bit-v4-0-5fe568a8ffde@aspeedtech.com>
SoC1 in the AST2700 integrates its own pin controller responsible for
pin multiplexing and pin configuration.
The controller manages various peripheral functions such as eSPI, LPC,
VPI, SD, UART, I2C, I3C, PWM and others through SCU registers.
The binding reuses the standard pinmux and generic pin configuration
schemas and does not introduce custom Devicetree properties.
Acked-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
---
.../pinctrl/aspeed,ast2700-soc1-pinctrl.yaml | 760 +++++++++++++++++++++
1 file changed, 760 insertions(+)
diff --git a/Documentation/devicetree/bindings/pinctrl/aspeed,ast2700-soc1-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2700-soc1-pinctrl.yaml
new file mode 100644
index 000000000000..76944fd14e2c
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2700-soc1-pinctrl.yaml
@@ -0,0 +1,760 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pinctrl/aspeed,ast2700-soc1-pinctrl.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ASPEED AST2700 SoC1 Pin Controller
+
+maintainers:
+ - Billy Tsai <billy_tsai@aspeedtech.com>
+
+description:
+ The AST2700 features a dual-SoC architecture with two interconnected SoCs,
+ each having its own System Control Unit (SCU) for independent pin control.
+ This pin controller manages the pin multiplexing for SoC1.
+
+ The SoC1 pin controller manages pin functions including eSPI, LPC and I2C,
+ among others.
+
+properties:
+ compatible:
+ const: aspeed,ast2700-soc1-pinctrl
+ reg:
+ maxItems: 1
+
+patternProperties:
+ '-state$':
+ description: |
+ Pin control state.
+
+ If `function` is present, the node describes a pinmux state and must
+ specify `groups`.
+
+ For pin configuration, exactly one of `groups` or `pins` must be
+ specified in each state node. Group-level configuration applies to all
+ pins in the group. Pin-level configuration may be supplied in a
+ separate state node for individual pins; when both group-level and
+ pin-level configuration apply to the same pin, the pin-level
+ configuration takes precedence.
+
+ type: object
+ allOf:
+ - $ref: pinmux-node.yaml#
+ - $ref: pincfg-node.yaml#
+ - if:
+ required:
+ - function
+ then:
+ required:
+ - groups
+ - oneOf:
+ - required:
+ - groups
+ - required:
+ - pins
+ additionalProperties: false
+
+ properties:
+ function:
+ enum:
+ - ADC0
+ - ADC1
+ - ADC10
+ - ADC11
+ - ADC12
+ - ADC13
+ - ADC14
+ - ADC15
+ - ADC2
+ - ADC3
+ - ADC4
+ - ADC5
+ - ADC6
+ - ADC7
+ - ADC8
+ - ADC9
+ - AUXPWRGOOD0
+ - AUXPWRGOOD1
+ - CANBUS
+ - ESPI0
+ - ESPI1
+ - FSI0
+ - FSI1
+ - FSI2
+ - FSI3
+ - FWQSPI
+ - FWSPIABR
+ - FWWPN
+ - HBLED
+ - I2C0
+ - I2C1
+ - I2C10
+ - I2C11
+ - I2C12
+ - I2C13
+ - I2C14
+ - I2C15
+ - I2C2
+ - I2C3
+ - I2C4
+ - I2C5
+ - I2C6
+ - I2C7
+ - I2C8
+ - I2C9
+ - I2CF0
+ - I2CF1
+ - I2CF2
+ - I3C0
+ - I3C1
+ - I3C10
+ - I3C11
+ - I3C12
+ - I3C13
+ - I3C14
+ - I3C15
+ - I3C2
+ - I3C3
+ - I3C4
+ - I3C5
+ - I3C6
+ - I3C7
+ - I3C8
+ - I3C9
+ - JTAGM1
+ - LPC0
+ - LPC1
+ - LTPI
+ - MACLINK0
+ - MACLINK1
+ - MACLINK2
+ - MDIO0
+ - MDIO1
+ - MDIO2
+ - NCTS0
+ - NCTS1
+ - NCTS5
+ - NCTS6
+ - NDCD0
+ - NDCD1
+ - NDCD5
+ - NDCD6
+ - NDSR0
+ - NDSR1
+ - NDSR5
+ - NDSR6
+ - NDTR0
+ - NDTR1
+ - NDTR5
+ - NDTR6
+ - NRI0
+ - NRI1
+ - NRI5
+ - NRI6
+ - NRTS0
+ - NRTS1
+ - NRTS5
+ - NRTS6
+ - OSCCLK
+ - PCIERC
+ - PWM0
+ - PWM1
+ - PWM10
+ - PWM11
+ - PWM12
+ - PWM13
+ - PWM14
+ - PWM15
+ - PWM2
+ - PWM3
+ - PWM4
+ - PWM5
+ - PWM6
+ - PWM7
+ - PWM8
+ - PWM9
+ - QSPI0
+ - QSPI1
+ - QSPI2
+ - RGMII0
+ - RGMII1
+ - RMII0
+ - RMII0RCLKO
+ - RMII1
+ - RMII1RCLKO
+ - SALT0
+ - SALT1
+ - SALT10
+ - SALT11
+ - SALT12
+ - SALT13
+ - SALT14
+ - SALT15
+ - SALT2
+ - SALT3
+ - SALT4
+ - SALT5
+ - SALT6
+ - SALT7
+ - SALT8
+ - SALT9
+ - SD
+ - SGMII
+ - SGPM0
+ - SGPM1
+ - SGPS
+ - SIOONCTRLN0
+ - SIOONCTRLN1
+ - SIOPBIN0
+ - SIOPBIN1
+ - SIOPBON0
+ - SIOPBON1
+ - SIOPWREQN0
+ - SIOPWREQN1
+ - SIOPWRGD1
+ - SIOS3N0
+ - SIOS3N1
+ - SIOS5N0
+ - SIOS5N1
+ - SIOSCIN0
+ - SIOSCIN1
+ - SMON0
+ - SMON1
+ - SPI0
+ - SPI0ABR
+ - SPI0CS1
+ - SPI0WPN
+ - SPI1
+ - SPI1ABR
+ - SPI1CS1
+ - SPI1WPN
+ - SPI2
+ - SPI2CS1
+ - TACH0
+ - TACH1
+ - TACH10
+ - TACH11
+ - TACH12
+ - TACH13
+ - TACH14
+ - TACH15
+ - TACH2
+ - TACH3
+ - TACH4
+ - TACH5
+ - TACH6
+ - TACH7
+ - TACH8
+ - TACH9
+ - THRU0
+ - THRU1
+ - THRU2
+ - THRU3
+ - UART0
+ - UART1
+ - UART10
+ - UART11
+ - UART2
+ - UART3
+ - UART5
+ - UART6
+ - UART7
+ - UART8
+ - UART9
+ - USB2C
+ - USB2D
+ - USBUART
+ - VGA
+ - VPI
+ - WDTRST0N
+ - WDTRST1N
+ - WDTRST2N
+ - WDTRST3N
+ - WDTRST4N
+ - WDTRST5N
+ - WDTRST6N
+ - WDTRST7N
+
+ groups:
+ enum:
+ - ADC0
+ - ADC1
+ - ADC10
+ - ADC11
+ - ADC12
+ - ADC13
+ - ADC14
+ - ADC15
+ - ADC2
+ - ADC3
+ - ADC4
+ - ADC5
+ - ADC6
+ - ADC7
+ - ADC8
+ - ADC9
+ - AUXPWRGOOD0
+ - AUXPWRGOOD1
+ - CANBUS
+ - DI2C0
+ - DI2C1
+ - DI2C10
+ - DI2C11
+ - DI2C12
+ - DI2C13
+ - DI2C14
+ - DI2C15
+ - DI2C2
+ - DI2C3
+ - DI2C8
+ - DI2C9
+ - DSGPM0
+ - ESPI0
+ - ESPI1
+ - FSI0
+ - FSI1
+ - FSI2
+ - FSI3
+ - FWQSPI
+ - FWSPIABR
+ - FWWPN
+ - HBLED
+ - HVI3C0
+ - HVI3C1
+ - HVI3C12
+ - HVI3C13
+ - HVI3C14
+ - HVI3C15
+ - HVI3C2
+ - HVI3C3
+ - I2C0
+ - I2C1
+ - I2C10
+ - I2C11
+ - I2C12
+ - I2C13
+ - I2C14
+ - I2C15
+ - I2C2
+ - I2C3
+ - I2C4
+ - I2C5
+ - I2C6
+ - I2C7
+ - I2C8
+ - I2C9
+ - I2CF0
+ - I2CF1
+ - I2CF2
+ - I3C10
+ - I3C11
+ - I3C4
+ - I3C5
+ - I3C6
+ - I3C7
+ - I3C8
+ - I3C9
+ - JTAGM1
+ - LPC0
+ - LPC1
+ - LTPI
+ - LTPI_PS_I2C0
+ - LTPI_PS_I2C1
+ - LTPI_PS_I2C2
+ - LTPI_PS_I2C3
+ - MACLINK0
+ - MACLINK1
+ - MACLINK2
+ - MDIO0
+ - MDIO1
+ - MDIO2
+ - NCTS0
+ - NCTS1
+ - NCTS5
+ - NCTS6
+ - NDCD0
+ - NDCD1
+ - NDCD5
+ - NDCD6
+ - NDSR0
+ - NDSR1
+ - NDSR5
+ - NDSR6
+ - NDTR0
+ - NDTR1
+ - NDTR5
+ - NDTR6
+ - NRI0
+ - NRI1
+ - NRI5
+ - NRI6
+ - NRTS0
+ - NRTS1
+ - NRTS5
+ - NRTS6
+ - OSCCLK
+ - PE2SGRSTN
+ - PWM0
+ - PWM1
+ - PWM10
+ - PWM11
+ - PWM12
+ - PWM13
+ - PWM14
+ - PWM15
+ - PWM2
+ - PWM3
+ - PWM4
+ - PWM5
+ - PWM6
+ - PWM7
+ - PWM8
+ - PWM9
+ - QSPI0
+ - QSPI1
+ - QSPI2
+ - RGMII0
+ - RGMII1
+ - RMII0
+ - RMII0RCLKO
+ - RMII1
+ - RMII1RCLKO
+ - SALT0
+ - SALT1
+ - SALT10
+ - SALT11
+ - SALT12
+ - SALT13
+ - SALT14
+ - SALT15
+ - SALT2
+ - SALT3
+ - SALT4
+ - SALT5
+ - SALT6
+ - SALT7
+ - SALT8
+ - SALT9
+ - SD
+ - SGMII
+ - SGPM0
+ - SGPM1
+ - SGPS
+ - SIOONCTRLN0
+ - SIOONCTRLN1
+ - SIOPBIN0
+ - SIOPBIN1
+ - SIOPBON0
+ - SIOPBON1
+ - SIOPWREQN0
+ - SIOPWREQN1
+ - SIOPWRGD1
+ - SIOS3N0
+ - SIOS3N1
+ - SIOS5N0
+ - SIOS5N1
+ - SIOSCIN0
+ - SIOSCIN1
+ - SMON0
+ - SMON1
+ - SPI0
+ - SPI0ABR
+ - SPI0CS1
+ - SPI0WPN
+ - SPI1
+ - SPI1ABR
+ - SPI1CS1
+ - SPI1WPN
+ - SPI2
+ - SPI2CS1
+ - TACH0
+ - TACH1
+ - TACH10
+ - TACH11
+ - TACH12
+ - TACH13
+ - TACH14
+ - TACH15
+ - TACH2
+ - TACH3
+ - TACH4
+ - TACH5
+ - TACH6
+ - TACH7
+ - TACH8
+ - TACH9
+ - THRU0
+ - THRU1
+ - THRU2
+ - THRU3
+ - UART0
+ - UART1
+ - UART10
+ - UART11
+ - UART2
+ - UART3
+ - UART5
+ - UART6
+ - UART7
+ - UART8
+ - UART9
+ - USB2CD
+ - USB2CH
+ - USB2CU
+ - USB2CUD
+ - USB2DD
+ - USB2DH
+ - USBUART
+ - VGA
+ - VPI
+ - WDTRST0N
+ - WDTRST1N
+ - WDTRST2N
+ - WDTRST3N
+ - WDTRST4N
+ - WDTRST5N
+ - WDTRST6N
+ - WDTRST7N
+
+ pins:
+ enum:
+ - A14
+ - A15
+ - A18
+ - A19
+ - A21
+ - A22
+ - A23
+ - A24
+ - A25
+ - A26
+ - A6
+ - A7
+ - A8
+ - AA12
+ - AA13
+ - AA14
+ - AA15
+ - AA16
+ - AA17
+ - AA18
+ - AA20
+ - AA21
+ - AA22
+ - AA23
+ - AA24
+ - AA25
+ - AA26
+ - AB15
+ - AB16
+ - AB17
+ - AB18
+ - AB19
+ - AB20
+ - AB21
+ - AB22
+ - AB23
+ - AB24
+ - AB25
+ - AB26
+ - AC15
+ - AC16
+ - AC17
+ - AC18
+ - AC19
+ - AC20
+ - AC22
+ - AC24
+ - AC25
+ - AC26
+ - AD15
+ - AD16
+ - AD17
+ - AD18
+ - AD19
+ - AD20
+ - AD22
+ - AD25
+ - AD26
+ - AE16
+ - AE17
+ - AE18
+ - AE19
+ - AE20
+ - AE21
+ - AE23
+ - AE25
+ - AE26
+ - AF16
+ - AF17
+ - AF18
+ - AF19
+ - AF20
+ - AF21
+ - AF23
+ - AF25
+ - AF26
+ - B10
+ - B11
+ - B12
+ - B13
+ - B14
+ - B15
+ - B16
+ - B18
+ - B19
+ - B21
+ - B22
+ - B23
+ - B24
+ - B25
+ - B26
+ - B6
+ - B7
+ - B8
+ - B9
+ - C10
+ - C11
+ - C12
+ - C13
+ - C14
+ - C15
+ - C16
+ - C17
+ - C18
+ - C19
+ - C20
+ - C23
+ - C26
+ - C6
+ - C7
+ - C8
+ - C9
+ - D10
+ - D12
+ - D14
+ - D15
+ - D19
+ - D20
+ - D24
+ - D26
+ - D7
+ - D8
+ - D9
+ - E10
+ - E11
+ - E12
+ - E13
+ - E14
+ - E26
+ - E7
+ - E8
+ - E9
+ - F10
+ - F11
+ - F12
+ - F13
+ - F14
+ - F26
+ - F7
+ - F8
+ - F9
+ - G10
+ - G11
+ - G7
+ - G8
+ - G9
+ - H10
+ - H11
+ - H7
+ - H8
+ - H9
+ - J10
+ - J11
+ - J12
+ - J13
+ - J9
+ - K12
+ - K13
+ - L12
+ - M13
+ - M14
+ - M15
+ - M16
+ - N13
+ - N14
+ - N15
+ - N25
+ - N26
+ - P13
+ - P14
+ - P25
+ - P26
+ - R14
+ - R25
+ - R26
+ - T23
+ - T24
+ - U21
+ - U22
+ - U25
+ - U26
+ - V14
+ - V16
+ - V17
+ - V18
+ - V19
+ - V20
+ - V21
+ - V22
+ - V23
+ - V24
+ - W14
+ - W16
+ - W17
+ - W18
+ - W20
+ - W21
+ - W22
+ - W25
+ - W26
+ - Y11
+ - Y15
+ - Y16
+ - Y17
+ - Y18
+ - Y20
+ - Y21
+ - Y22
+ - Y23
+ - Y24
+ - Y25
+ - Y26
+
+ drive-strength:
+ enum: [4, 8, 12, 16]
+
+ bias-disable: true
+ bias-pull-up: true
+ bias-pull-down: true
+
+required:
+ - compatible
+ - reg
+
+allOf:
+ - $ref: pinctrl.yaml#
+
+additionalProperties: false
+
+examples:
+ - |
+ pinctrl@400 {
+ compatible = "aspeed,ast2700-soc1-pinctrl";
+ reg = <0x400 0x2A0>;
+ sgpm0-state {
+ function = "SGPM0";
+ groups = "SGPM0";
+ };
+ };
--
2.34.1
^ permalink raw reply related
* [PATCH 07/37] drm/display: bridge-connector: extract drm_bridge_connector_get_bridges()
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
To support bridge hotplug we will need better control over the steps
involved in the crm_connector creation, especially in terms of error
management. This will be complicated by the length and amount of return
points in this function. In preparation, split the code to grab the various
bridge pointers out to a separate function.
No functional changes. Just moving code around.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 86 +++++++++++++++-----------
1 file changed, 50 insertions(+), 36 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index eb1912611cd6..97d9a4c6d166 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -772,44 +772,24 @@ static void drm_bridge_connector_put_bridges(struct drm_device *dev, void *data)
drm_bridge_put(bridge_connector->bridge_hdmi_cec);
}
-/**
- * drm_bridge_connector_add_connector - add the drm_connector
- * @bridge_connector: drm_bridge_connector to add the drm_connector to
- *
- * Preconditions: the drm_bridge_connector is initialized (the
- * @bridge_connector->drm and @bridge_connector->encoder fields are
- * assigned at least)
- *
- * Returns a pointer to @bridge_connector on success, or a negative error
- * pointer otherwise.
- */
static struct drm_bridge_connector *
-drm_bridge_connector_add_connector(struct drm_bridge_connector *bridge_connector)
+drm_bridge_connector_get_bridges(struct drm_bridge_connector *bridge_connector,
+ int *connector_type,
+ unsigned int *supported_formats,
+ unsigned int *max_bpc,
+ struct i2c_adapter **ddc,
+ struct drm_bridge **panel_bridge,
+ bool *support_hdcp)
{
- struct drm_connector *connector;
- struct i2c_adapter *ddc = NULL;
- struct drm_bridge *panel_bridge __free(drm_bridge_put) = NULL;
- unsigned int supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444);
- unsigned int max_bpc = 8;
- bool support_hdcp = false;
- int connector_type;
- int ret;
+ struct drm_connector *connector = &bridge_connector->base;
/*
* TODO: Handle doublescan_allowed and stereo_allowed.
*/
- connector = &bridge_connector->base;
connector->interlace_allowed = true;
connector->ycbcr_420_allowed = true;
+ *connector_type = DRM_MODE_CONNECTOR_Unknown;
- /*
- * Initialise connector status handling. First locate the furthest
- * bridges in the pipeline that support HPD and output detection. Then
- * initialise the connector polling mode, using HPD if available and
- * falling back to polling if supported. If neither HPD nor output
- * detection are available, we don't support hotplug detection at all.
- */
- connector_type = DRM_MODE_CONNECTOR_Unknown;
drm_for_each_bridge_in_chain_scoped(bridge_connector->encoder, bridge) {
if (!bridge->interlace_allowed)
connector->interlace_allowed = false;
@@ -865,9 +845,9 @@ drm_bridge_connector_add_connector(struct drm_bridge_connector *bridge_connector
bridge_connector->bridge_hdmi = drm_bridge_get(bridge);
if (bridge->supported_formats)
- supported_formats = bridge->supported_formats;
+ *supported_formats = bridge->supported_formats;
if (bridge->max_bpc)
- max_bpc = bridge->max_bpc;
+ *max_bpc = bridge->max_bpc;
}
if (bridge->ops & DRM_BRIDGE_OP_HDMI_AUDIO) {
@@ -926,7 +906,7 @@ drm_bridge_connector_add_connector(struct drm_bridge_connector *bridge_connector
}
if (drm_bridge_is_last(bridge))
- connector_type = bridge->type;
+ *connector_type = bridge->type;
#ifdef CONFIG_OF
if (drm_bridge_is_last(bridge) && bridge->of_node)
@@ -934,20 +914,54 @@ drm_bridge_connector_add_connector(struct drm_bridge_connector *bridge_connector
#endif
if (bridge->ddc)
- ddc = bridge->ddc;
+ *ddc = bridge->ddc;
if (drm_bridge_is_panel(bridge)) {
- drm_bridge_put(panel_bridge);
- panel_bridge = drm_bridge_get(bridge);
+ drm_bridge_put(*panel_bridge);
+ *panel_bridge = drm_bridge_get(bridge);
}
if (bridge->support_hdcp)
- support_hdcp = true;
+ *support_hdcp = true;
}
if (connector_type == DRM_MODE_CONNECTOR_Unknown)
return ERR_PTR(-EINVAL);
+ return bridge_connector;
+}
+
+/**
+ * drm_bridge_connector_add_connector - add the drm_connector
+ * @bridge_connector: drm_bridge_connector to add the drm_connector to
+ *
+ * Preconditions: the drm_bridge_connector is initialized (the
+ * @bridge_connector->drm and @bridge_connector->encoder fields are
+ * assigned at least)
+ *
+ * Returns a pointer to @bridge_connector on success, or a negative error
+ * pointer otherwise.
+ */
+static struct drm_bridge_connector *
+drm_bridge_connector_add_connector(struct drm_bridge_connector *bridge_connector)
+{
+ struct drm_connector *connector;
+ struct i2c_adapter *ddc = NULL;
+ struct drm_bridge *panel_bridge __free(drm_bridge_put) = NULL;
+ unsigned int supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444);
+ unsigned int max_bpc = 8;
+ bool support_hdcp = false;
+ int connector_type;
+ int ret;
+
+ connector = &bridge_connector->base;
+
+ bridge_connector = drm_bridge_connector_get_bridges(bridge_connector, &connector_type,
+ &supported_formats, &max_bpc, &ddc,
+ &panel_bridge, &support_hdcp);
+ if (IS_ERR(bridge_connector))
+ return bridge_connector;
+
if (bridge_connector->bridge_hdmi) {
if (!connector->ycbcr_420_allowed)
supported_formats &= ~BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420);
--
2.54.0
^ permalink raw reply related
* [PATCH 06/37] drm/display: bridge-connector: use a drm_bridge_connector internally, not a drm_connector
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
Currently drm_bridge_connector_init() always returns the added connector or
errors out. When adding bridge hotplug the bridge-connector can be
successfully initialized without creating a connector, which can be added
later when the pipeline will be complete.
For this the internal function drm_bridge_connector_add_connector() must be
able to return a valid drm_bridge_connector even without any drm_connector.
In preparation to support bridge hotplug, change its return value to be the
same drm_bridge_connector pointer it gets as input, or a PTR_ERR.
No functional changes, just changing an internal API.
Note the return value could now become an int (0 or negative error) because
returning the same value received as input does not carry any added
value. However this would be change a lot of lines, so leave such change as
a future cleanup.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index 6813c6008985..eb1912611cd6 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -780,10 +780,10 @@ static void drm_bridge_connector_put_bridges(struct drm_device *dev, void *data)
* @bridge_connector->drm and @bridge_connector->encoder fields are
* assigned at least)
*
- * Returns a pointer to the new drm_connector on success, or a negative
- * error pointer otherwise.
+ * Returns a pointer to @bridge_connector on success, or a negative error
+ * pointer otherwise.
*/
-static struct drm_connector *
+static struct drm_bridge_connector *
drm_bridge_connector_add_connector(struct drm_bridge_connector *bridge_connector)
{
struct drm_connector *connector;
@@ -1049,7 +1049,7 @@ drm_bridge_connector_add_connector(struct drm_bridge_connector *bridge_connector
if (ret)
return ERR_PTR(ret);
- return connector;
+ return bridge_connector;
}
/**
@@ -1086,6 +1086,10 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
bridge_connector->drm = drm;
bridge_connector->encoder = encoder;
- return drm_bridge_connector_add_connector(bridge_connector);
+ bridge_connector = drm_bridge_connector_add_connector(bridge_connector);
+ if (IS_ERR(bridge_connector))
+ return ERR_CAST(bridge_connector);
+
+ return &bridge_connector->base;
}
EXPORT_SYMBOL_GPL(drm_bridge_connector_init);
--
2.54.0
^ permalink raw reply related
* [PATCH v4 1/3] dt-bindings: mfd: aspeed,ast2x00-scu: Support AST2700 SoC1 pinctrl
From: Billy Tsai @ 2026-05-19 10:38 UTC (permalink / raw)
To: Linus Walleij, Tony Lindgren, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Joel Stanley, Andrew Jeffery, Bartosz Golaszewski,
Lee Jones, Ryan Chen
Cc: patrickw3, linux-gpio, devicetree, linux-kernel, linux-arm-kernel,
linux-aspeed, BMC-SW, openbmc, Andrew Jeffery, linux-clk,
Billy Tsai, Conor Dooley
In-Reply-To: <20260519-pinctrl-single-bit-v4-0-5fe568a8ffde@aspeedtech.com>
The AST2700 SoC integrates two interconnected SoC instances, each
managed by its own System Control Unit (SCU).
Allow the AST2700 SoC1 pin controller to be described as a child
node of the SCU by extending the compatible strings accepted by
the SCU binding.
There is no functional change to the SCU binding beyond permitting
the aspeed,ast2700-soc1-pinctrl compatible string.
Acked-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
---
Note: The companion AST2700 SoC0 pinctrl series modifies the same
binding and has been applied to pinctrl/for-next. Linus Walleij noted
for the equivalent SoC0 MFD patch that it should be applied through the
MFD tree by Lee Jones, providing an Acked-by in the process [1]. The
same routing applies to this patch.
[1] https://lore.kernel.org/all/CAD++jL=3p9BvDgaot3=emM4Zn5jU-ZAUKtB4UwT1HzDiyzKq4Q@mail.gmail.com/
---
Documentation/devicetree/bindings/mfd/aspeed,ast2x00-scu.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/mfd/aspeed,ast2x00-scu.yaml b/Documentation/devicetree/bindings/mfd/aspeed,ast2x00-scu.yaml
index a87f31fce019..b81c561d2a8c 100644
--- a/Documentation/devicetree/bindings/mfd/aspeed,ast2x00-scu.yaml
+++ b/Documentation/devicetree/bindings/mfd/aspeed,ast2x00-scu.yaml
@@ -87,6 +87,7 @@ patternProperties:
- aspeed,ast2400-pinctrl
- aspeed,ast2500-pinctrl
- aspeed,ast2600-pinctrl
+ - aspeed,ast2700-soc1-pinctrl
required:
- compatible
--
2.34.1
^ permalink raw reply related
* [PATCH v4 0/3] pinctrl: aspeed: Add AST2700 SoC1 support
From: Billy Tsai @ 2026-05-19 10:38 UTC (permalink / raw)
To: Linus Walleij, Tony Lindgren, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Joel Stanley, Andrew Jeffery, Bartosz Golaszewski,
Lee Jones, Ryan Chen
Cc: patrickw3, linux-gpio, devicetree, linux-kernel, linux-arm-kernel,
linux-aspeed, BMC-SW, openbmc, Andrew Jeffery, linux-clk,
Billy Tsai, Conor Dooley
Legacy ASPEED pin controllers have historically not had a coherent
register interface. Control fields often had no consistent mapping to
individual pins, and configuring a function frequently required
coordinating multiple control bits across several registers. As a
result, the existing ASPEED pinctrl drivers rely on complex macro
infrastructure to describe the dependencies between pins, functions,
and register fields.
The pin controller for SoC1 in the AST2700 breaks from this legacy
design.
For SoC1, each pin maps directly to a dedicated function field in the
SCU register space that determines the active mux function for that
pin. This results in a much more regular register layout compared to
previous generations.
While the behaviour is conceptually similar to pinctrl-single, the
register layout and configuration model differ enough that reusing
pinctrl-single directly is not practical. Therefore this driver is
implemented as a SoC-specific pinctrl driver using static data tables
to describe the register layout.
The binding reuses the standard pinmux and generic pin configuration
schemas and does not introduce any custom Devicetree properties.
Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
---
Changes in v4:
- Fix SGMII0 mux register: SGMII0's control bit is at SCU47C[0],
outside the contiguous pin-indexed MUX register range. Handle it as
a special case in set_mux (analogous to PCIERC2_PERST) using
field.mask = 0x1 to avoid clobbering adjacent bits in SCU47C.
- Use devm_pinctrl_register_and_init() and pinctrl_enable(): populate
all groups and functions before enabling the controller, closing the
race window where a consumer could observe zero groups/functions.
- pin_config_get: return -EINVAL when BIAS_DISABLE is not active; return
-ENOTSUPP for BIAS_PULL_UP and BIAS_PULL_DOWN because the hardware
has a single 1-bit enable/disable field per pin and pull direction
cannot be read back from the register.
- Probe: preserve const qualification in the pinctrl_generic_add_group()
pin array cast.
- Binding: retain additionalProperties: false for the state-node schema.
The state-node properties block re-declares the four pincfg properties
the hardware supports (bias-disable, bias-pull-up, bias-pull-down,
drive-strength) and intentionally rejects all others.
unevaluatedProperties: false was suggested as an alternative, but that
would permit the full set of pincfg-node.yaml properties
(input-schmitt-enable, slew-rate, etc.) even though the hardware does
not support them. additionalProperties: false is the correct mechanism
when the goal is to restrict the accepted properties to a known subset.
- Link to v3: https://lore.kernel.org/r/20260515-pinctrl-single-bit-v3-0-e97da4312104@aspeedtech.com
Changes in v3:
- Added pin configuration group support for AST2700 SoC1 by
implementing `pin_config_group_get()` and `pin_config_group_set()`.
- Restricted AST2700 SoC1 `drive-strength` settings to the supported
4/8/12/16 mA values in the pinctrl driver.
- Convert kernel-doc comment for aspeed_g7_soc1_drv_map to a regular
comment to avoid kernel-doc warning reported by kernel test robot.
- Update the AST2700 SoC1 pinctrl binding to describe the `reg`
property and require it.
- Allow standard pinconf properties in pin state nodes.
- Add a binding example for the AST2700 SoC1 pinctrl node.
- Add state-node description, function+groups dependency constraint,
and oneOf groups/pins constraint to the binding, matching the SoC0
binding style.
- Add pins enum (212 entries) to the binding to cover all physical pins
that support per-pin configuration.
- Add UART modem-line signals as independent functions/groups:
NCTS0/1/5/6, NDCD0/1/5/6, NDSR0/1/5/6, NDTR0/1/5/6,
NRI0/1/5/6, NRTS0/1/5/6; remove those pins from UART0/1/5/6 groups.
- Add LTPI_PS_I2C0/1/2/3 functions/groups for I2C-over-LTPI;
extend I2C0/1/2/3 functions with the new LTPI groups.
- Fix typo: rename RMII0RCKO/RMII1RCKO to RMII0RCLKO/RMII1RCLKO.
- Fix wrong index: rename DSGPM1 to DSGPM0.
- Kconfig: use "Aspeed G7 SoC1 pin control" to match neighbouring entries.
- pin_config_get: fix BIAS_DISABLE readback (val=!val must be skipped
for BIAS_DISABLE since hardware bit=1 means pull disabled).
- set_mux: remove dead null check on grp; propagate regmap_update_bits()
return value.
- gpio_request_enable: propagate regmap_update_bits() return value.
- Link to v2: https://lore.kernel.org/r/20260306-pinctrl-single-bit-v2-0-79918cfab641@aspeedtech.com
Changes in v2:
- Updated the series title to focus on AST2700 SoC1 support.
- Reworked implementation to use static SoC-specific layout tables
instead of a generic packed-field model.
- Dropped the generic "pinctrl-packed" driver approach.
- Removed custom Devicetree properties.
- Updated binding to reuse standard pinmux and generic pin
configuration schemas.
- Link to v1: https://lore.kernel.org/r/20260213-pinctrl-single-bit-v1-0-c60f2fb80efb@aspeedtech.com
---
Billy Tsai (3):
dt-bindings: mfd: aspeed,ast2x00-scu: Support AST2700 SoC1 pinctrl
dt-bindings: pinctrl: Add aspeed,ast2700-soc1-pinctrl
pinctrl: aspeed: Add AST2700 SoC1 support
.../bindings/mfd/aspeed,ast2x00-scu.yaml | 1 +
.../pinctrl/aspeed,ast2700-soc1-pinctrl.yaml | 760 +++++++++
drivers/pinctrl/aspeed/Kconfig | 14 +
drivers/pinctrl/aspeed/Makefile | 1 +
drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c | 1754 ++++++++++++++++++++
5 files changed, 2530 insertions(+)
---
base-commit: e532a5a81d0db872acd2c0a92d2639580ca3da44
change-id: 20260211-pinctrl-single-bit-da213f282c95
Best regards,
--
Billy Tsai <billy_tsai@aspeedtech.com>
^ permalink raw reply
* [PATCH 05/37] drm/display: bridge-connector: split code creating the connector to a subfunction
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
In preparation to introduce bridge hotplug, split out from
drm_bridge_connector_init() the code adding the drm_connector into a
dedicated function. This will be needed to be able to add (and re-add) the
connector from different code paths.
No functional changes. Just moving code around.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 81 ++++++++++++++++----------
1 file changed, 50 insertions(+), 31 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index 62c4cb1e241f..6813c6008985 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -773,26 +773,19 @@ static void drm_bridge_connector_put_bridges(struct drm_device *dev, void *data)
}
/**
- * drm_bridge_connector_init - Initialise a connector for a chain of bridges
- * @drm: the DRM device
- * @encoder: the encoder where the bridge chain starts
- *
- * Create a new &drm_bridge_connector for the @drm device. The connector is
- * allocated, initialised, registered with the @drm device and attached to
- * @encoder.
+ * drm_bridge_connector_add_connector - add the drm_connector
+ * @bridge_connector: drm_bridge_connector to add the drm_connector to
*
- * The connector is associated with a chain of bridges that starts at
- * the @encoder. All bridges in the chain shall report bridge operation flags
- * (&drm_bridge->ops) and bridge output type (&drm_bridge->type), and none of
- * them may create a DRM connector directly.
+ * Preconditions: the drm_bridge_connector is initialized (the
+ * @bridge_connector->drm and @bridge_connector->encoder fields are
+ * assigned at least)
*
- * Returns a pointer to the new connector on success, or a negative error
- * pointer otherwise.
+ * Returns a pointer to the new drm_connector on success, or a negative
+ * error pointer otherwise.
*/
-struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
- struct drm_encoder *encoder)
+static struct drm_connector *
+drm_bridge_connector_add_connector(struct drm_bridge_connector *bridge_connector)
{
- struct drm_bridge_connector *bridge_connector;
struct drm_connector *connector;
struct i2c_adapter *ddc = NULL;
struct drm_bridge *panel_bridge __free(drm_bridge_put) = NULL;
@@ -802,17 +795,6 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
int connector_type;
int ret;
- bridge_connector = drmm_kzalloc(drm, sizeof(*bridge_connector), GFP_KERNEL);
- if (!bridge_connector)
- return ERR_PTR(-ENOMEM);
-
- ret = drmm_add_action(drm, drm_bridge_connector_put_bridges, bridge_connector);
- if (ret)
- return ERR_PTR(ret);
-
- bridge_connector->drm = drm;
- bridge_connector->encoder = encoder;
-
/*
* TODO: Handle doublescan_allowed and stereo_allowed.
*/
@@ -828,7 +810,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
* detection are available, we don't support hotplug detection at all.
*/
connector_type = DRM_MODE_CONNECTOR_Unknown;
- drm_for_each_bridge_in_chain_scoped(encoder, bridge) {
+ drm_for_each_bridge_in_chain_scoped(bridge_connector->encoder, bridge) {
if (!bridge->interlace_allowed)
connector->interlace_allowed = false;
if (!bridge->ycbcr_420_allowed)
@@ -984,7 +966,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
bridge_connector->hdmi_funcs.spd =
drm_bridge_connector_hdmi_spd_infoframe;
- ret = drmm_connector_hdmi_init(drm, connector,
+ ret = drmm_connector_hdmi_init(bridge_connector->drm, connector,
bridge_connector->bridge_hdmi->vendor,
bridge_connector->bridge_hdmi->product,
&drm_bridge_connector_funcs,
@@ -995,7 +977,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
if (ret)
return ERR_PTR(ret);
} else {
- ret = drmm_connector_init(drm, connector,
+ ret = drmm_connector_init(bridge_connector->drm, connector,
&drm_bridge_connector_funcs,
connector_type, ddc);
if (ret)
@@ -1063,10 +1045,47 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
IS_ENABLED(CONFIG_DRM_DISPLAY_HDCP_HELPER))
drm_connector_attach_content_protection_property(connector, true);
- ret = drm_connector_attach_encoder(connector, encoder);
+ ret = drm_connector_attach_encoder(connector, bridge_connector->encoder);
if (ret)
return ERR_PTR(ret);
return connector;
}
+
+/**
+ * drm_bridge_connector_init - Initialise a connector for a chain of bridges
+ * @drm: the DRM device
+ * @encoder: the encoder where the bridge chain starts
+ *
+ * Create a new &drm_bridge_connector for the @drm device. The connector is
+ * allocated, initialised, registered with the @drm device and attached to
+ * @encoder.
+ *
+ * The connector is associated with a chain of bridges that starts at
+ * the @encoder. All bridges in the chain shall report bridge operation flags
+ * (&drm_bridge->ops) and bridge output type (&drm_bridge->type), and none of
+ * them may create a DRM connector directly.
+ *
+ * Returns a pointer to the new connector on success, or a negative error
+ * pointer otherwise.
+ */
+struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
+ struct drm_encoder *encoder)
+{
+ struct drm_bridge_connector *bridge_connector;
+ int ret;
+
+ bridge_connector = drmm_kzalloc(drm, sizeof(*bridge_connector), GFP_KERNEL);
+ if (!bridge_connector)
+ return ERR_PTR(-ENOMEM);
+
+ ret = drmm_add_action(drm, drm_bridge_connector_put_bridges, bridge_connector);
+ if (ret)
+ return ERR_PTR(ret);
+
+ bridge_connector->drm = drm;
+ bridge_connector->encoder = encoder;
+
+ return drm_bridge_connector_add_connector(bridge_connector);
+}
EXPORT_SYMBOL_GPL(drm_bridge_connector_init);
--
2.54.0
^ permalink raw reply related
* [PATCH 04/37] drm/display: bridge-connector: store the drm_device pointer
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
Currently the struct drm_device pointer is only needed during the initial
drm_bridge_connector_init() and in drm_bridge_connector_handle_hpd() which
gets it from the struct drm_connector.
This will be insufficient when introducing bridge hotplugging, because:
* some of the actions in drm_bridge_connector_init() will have to be
performed later on, when a bridge is hot(un)plugged
* the connector will be removed and re-added based on hotplug events,
so the drm_connector might just not exist or its content be cleared
Store the drm_device pointer in struct drm_bridge_connector for any later
needs. Also convert drm_bridge_connector_handle_hpd() to use the newly
stored value.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index bf9b8a6bca78..62c4cb1e241f 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -60,6 +60,10 @@ struct drm_bridge_connector {
* @base: The base DRM connector
*/
struct drm_connector base;
+ /**
+ * @drm: The DRM device we belong to
+ */
+ struct drm_device *drm;
/**
* @encoder:
*
@@ -157,7 +161,7 @@ static void drm_bridge_connector_handle_hpd(struct drm_bridge_connector *bridge_
enum drm_connector_status status)
{
struct drm_connector *connector = &bridge_connector->base;
- struct drm_device *dev = connector->dev;
+ struct drm_device *dev = bridge_connector->drm;
mutex_lock(&dev->mode_config.mutex);
connector->status = status;
@@ -806,6 +810,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
if (ret)
return ERR_PTR(ret);
+ bridge_connector->drm = drm;
bridge_connector->encoder = encoder;
/*
--
2.54.0
^ permalink raw reply related
* [PATCH 02/37] drm/connector: add drm_connector_hdmi_dynamic_init()
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
In preparation for adding hotpluggable bridges into the
drm_bridge_connector, we need connectors to be created dynamically, both
regular connectors and HDMI ones. drm_connector_init() already has a
dynamic variant, add one for HDMI connectors too.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/drm_connector.c | 50 +++++++++++++++++++++++++++++++++++++++++
include/drm/drm_connector.h | 9 ++++++++
2 files changed, 59 insertions(+)
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 37ed73300a18..5e7a3a438e5d 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -658,6 +658,56 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
}
EXPORT_SYMBOL(drmm_connector_hdmi_init);
+/**
+ * drm_connector_hdmi_dynamic_init - Init a preallocated dynamic HDMI connector
+ * @dev: DRM device
+ * @connector: A pointer to the HDMI connector to init
+ * @vendor: HDMI Controller Vendor name
+ * @product: HDMI Controller Product name
+ * @funcs: callbacks for this connector
+ * @hdmi_funcs: HDMI-related callbacks for this connector
+ * @connector_type: user visible type of the connector
+ * @ddc: optional pointer to the associated ddc adapter
+ * @supported_formats: Bitmask of @hdmi_colorspace listing supported output formats
+ * @max_bpc: Maximum bits per char the HDMI connector supports
+ *
+ * Initialises a preallocated dynamic HDMI connector. Connectors can be
+ * subclassed as part of driver connector objects.
+ *
+ * See drm_connector_dynamic_init(), the same constraints apply here. This
+ * is just the HDMI version.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_connector_hdmi_dynamic_init(struct drm_device *dev,
+ struct drm_connector *connector,
+ const char *vendor, const char *product,
+ const struct drm_connector_funcs *funcs,
+ const struct drm_connector_hdmi_funcs *hdmi_funcs,
+ int connector_type,
+ struct i2c_adapter *ddc,
+ unsigned long supported_formats,
+ unsigned int max_bpc)
+{
+ int ret;
+
+ ret = drm_connector_hdmi_sanity_checks(connector, vendor, product, hdmi_funcs,
+ connector_type, supported_formats, max_bpc);
+ if (ret)
+ return ret;
+
+ ret = drm_connector_dynamic_init(dev, connector, funcs, connector_type, ddc);
+ if (ret)
+ return ret;
+
+ drm_connector_hdmi_init(connector, vendor, product,
+ hdmi_funcs, supported_formats, max_bpc);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_connector_hdmi_dynamic_init);
+
/**
* drm_connector_attach_edid_property - attach edid property.
* @connector: the connector
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 5ad62c207d00..1f5dba947748 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -2442,6 +2442,15 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
struct i2c_adapter *ddc,
unsigned long supported_formats,
unsigned int max_bpc);
+int drm_connector_hdmi_dynamic_init(struct drm_device *dev,
+ struct drm_connector *connector,
+ const char *vendor, const char *product,
+ const struct drm_connector_funcs *funcs,
+ const struct drm_connector_hdmi_funcs *hdmi_funcs,
+ int connector_type,
+ struct i2c_adapter *ddc,
+ unsigned long supported_formats,
+ unsigned int max_bpc);
void drm_connector_attach_edid_property(struct drm_connector *connector);
int drm_connector_register(struct drm_connector *connector);
int drm_connector_dynamic_register(struct drm_connector *connector);
--
2.54.0
^ permalink raw reply related
* [PATCH 03/37] drm/display: bridge-connector: rename variable for consistency
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
All struct drm_bridge_connector pointers are called bridge_connector except
this one. Rename it for consistency.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index 649969fca141..bf9b8a6bca78 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -153,10 +153,10 @@ static void drm_bridge_connector_hpd_notify(struct drm_connector *connector,
}
}
-static void drm_bridge_connector_handle_hpd(struct drm_bridge_connector *drm_bridge_connector,
+static void drm_bridge_connector_handle_hpd(struct drm_bridge_connector *bridge_connector,
enum drm_connector_status status)
{
- struct drm_connector *connector = &drm_bridge_connector->base;
+ struct drm_connector *connector = &bridge_connector->base;
struct drm_device *dev = connector->dev;
mutex_lock(&dev->mode_config.mutex);
--
2.54.0
^ permalink raw reply related
* [PATCH 01/37] drm/connector: split drmm_connector_hdmi_init() in 3 parts
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
In preparation for adding a dynamic variant of drmm_connector_hdmi_init(),
split reusable parts of the code to subfunctions.
drmm_connector_hdmi_init() currently has 3 sections:
1. sanity checks
2. call drmm_connector_init()
3. initialize HDMI-specific fields not initialized at step 2
Split 1 and 3 to new functions, reusable independently.
No functional changes. Just moving code around.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/drm_connector.c | 105 +++++++++++++++++++++++++---------------
1 file changed, 65 insertions(+), 40 deletions(-)
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 3fa4d2082cd7..37ed73300a18 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -542,44 +542,13 @@ int drmm_connector_init(struct drm_device *dev,
}
EXPORT_SYMBOL(drmm_connector_init);
-/**
- * drmm_connector_hdmi_init - Init a preallocated HDMI connector
- * @dev: DRM device
- * @connector: A pointer to the HDMI connector to init
- * @vendor: HDMI Controller Vendor name
- * @product: HDMI Controller Product name
- * @funcs: callbacks for this connector
- * @hdmi_funcs: HDMI-related callbacks for this connector
- * @connector_type: user visible type of the connector
- * @ddc: optional pointer to the associated ddc adapter
- * @supported_formats: Bitmask of @drm_output_color_format listing supported output formats
- * @max_bpc: Maximum bits per char the HDMI connector supports
- *
- * Initialises a preallocated HDMI connector. Connectors can be
- * subclassed as part of driver connector objects.
- *
- * Cleanup is automatically handled with a call to
- * drm_connector_cleanup() in a DRM-managed action.
- *
- * The connector structure should be allocated with drmm_kzalloc().
- *
- * The @drm_connector_funcs.destroy hook must be NULL.
- *
- * Returns:
- * Zero on success, error code on failure.
- */
-int drmm_connector_hdmi_init(struct drm_device *dev,
- struct drm_connector *connector,
- const char *vendor, const char *product,
- const struct drm_connector_funcs *funcs,
- const struct drm_connector_hdmi_funcs *hdmi_funcs,
- int connector_type,
- struct i2c_adapter *ddc,
- unsigned long supported_formats,
- unsigned int max_bpc)
+static int drm_connector_hdmi_sanity_checks(struct drm_connector *connector,
+ const char *vendor, const char *product,
+ const struct drm_connector_hdmi_funcs *hdmi_funcs,
+ int connector_type,
+ unsigned long supported_formats,
+ unsigned int max_bpc)
{
- int ret;
-
if (!vendor || !product)
return -EINVAL;
@@ -606,10 +575,15 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
!hdmi_funcs->hdmi.write_infoframe)
return -EINVAL;
- ret = drmm_connector_init(dev, connector, funcs, connector_type, ddc);
- if (ret)
- return ret;
+ return 0;
+}
+static void drm_connector_hdmi_init(struct drm_connector *connector,
+ const char *vendor, const char *product,
+ const struct drm_connector_hdmi_funcs *hdmi_funcs,
+ unsigned long supported_formats,
+ unsigned int max_bpc)
+{
connector->hdmi.supported_formats = supported_formats;
strtomem_pad(connector->hdmi.vendor, vendor, 0);
strtomem_pad(connector->hdmi.product, product, 0);
@@ -628,6 +602,57 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
drm_connector_attach_hdr_output_metadata_property(connector);
connector->hdmi.funcs = hdmi_funcs;
+}
+
+/**
+ * drmm_connector_hdmi_init - Init a preallocated HDMI connector
+ * @dev: DRM device
+ * @connector: A pointer to the HDMI connector to init
+ * @vendor: HDMI Controller Vendor name
+ * @product: HDMI Controller Product name
+ * @funcs: callbacks for this connector
+ * @hdmi_funcs: HDMI-related callbacks for this connector
+ * @connector_type: user visible type of the connector
+ * @ddc: optional pointer to the associated ddc adapter
+ * @supported_formats: Bitmask of @drm_output_color_format listing supported output formats
+ * @max_bpc: Maximum bits per char the HDMI connector supports
+ *
+ * Initialises a preallocated HDMI connector. Connectors can be
+ * subclassed as part of driver connector objects.
+ *
+ * Cleanup is automatically handled with a call to
+ * drm_connector_cleanup() in a DRM-managed action.
+ *
+ * The connector structure should be allocated with drmm_kzalloc().
+ *
+ * The @drm_connector_funcs.destroy hook must be NULL.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drmm_connector_hdmi_init(struct drm_device *dev,
+ struct drm_connector *connector,
+ const char *vendor, const char *product,
+ const struct drm_connector_funcs *funcs,
+ const struct drm_connector_hdmi_funcs *hdmi_funcs,
+ int connector_type,
+ struct i2c_adapter *ddc,
+ unsigned long supported_formats,
+ unsigned int max_bpc)
+{
+ int ret;
+
+ ret = drm_connector_hdmi_sanity_checks(connector, vendor, product, hdmi_funcs,
+ connector_type, supported_formats, max_bpc);
+ if (ret)
+ return ret;
+
+ ret = drmm_connector_init(dev, connector, funcs, connector_type, ddc);
+ if (ret)
+ return ret;
+
+ drm_connector_hdmi_init(connector, vendor, product,
+ hdmi_funcs, supported_formats, max_bpc);
return 0;
}
--
2.54.0
^ permalink raw reply related
* [PATCH 00/37] drm bridge hotplug
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
Hello,
this series adds support for Linux-based devices with a DRM pipeline whose
final components, including one or more bridges, can be hot-plugged and
hot-unplugged.
TL;DR:
* This new approach is totally different from the one proposed in the
past [0], and the code is an almost completely rewrite
* If you already know the use case, feel free to skip to the "Design"
section below
Use case
========
This series targets as its first use case a professional product (GE SUNH)
that is composed of a "main" part, with the main SoC and able to work
autonomously with limited features, and an optional "add-on" that enables
more features by adding more hardware peripherals.
The hotplug connector has a MIPI DSI bus. The addon has a DSI-to-LVDS
bridge and an LVDS panel attached to it. Different addon models can have
different components. As a consequence, a DRM bridge must be added and
removed at runtime without tearing down the whole card. This is currently
not possible, and this series enables it.
DRM already supports pipelines whose display can be removed, but all the
components preceding it (all the display controller and any bridges) are
assumed to be fixed and cannot be plugged, removed or modified at
runtime. Additionally, dynamic drm_connectors are supported for DP MST.
This picture summarizes the DRM structure implemented by this series:
.------------------------.
| DISPLAY CONTROLLER |
| .---------. .------. |
| | ENCODER |<--| CRTC | |
| '---------' '------' |
'------|-----------------'
| MACHANICAL
|DSI HOTPLUG
V CONNECTOR
.---------. .--. .-. .---------. .-------.
| 0 to N | | _| _| | | 1 to N | | |
| BRIDGES |--DSI-->||_ |_ |--DSI-->| BRIDGES |--LVDS-->| PANEL |
| | | | | | | | | |
'---------' '--' '-' '---------' '-------'
[--- fixed components --] [----------- removable add-on -----------]
The video bus is MIPI DSI in the example and in the implementation provided
by this series, but the implementation is meant to allow generalization to
other video busses without native hotplug support, such as parallel video
and LVDS. Addons which terminate with a HDMI/DP connector are possible too.
== Design
The old approach [0] was based on a hotplug-bridge driver, connected
between the last fixed bridge and the first removable bridge.
That approach has been rejected, and this new approach is based on a
central place to handle DRM hotplug, not a specific driver.
The drm_bridge_connector is nowadays the recommended way to implement DRM
connectors when a chain of bridges is used. It takes care of adding the
drm_connector when the pipeline is composed by an arbitrarily long chain of
bridges, which it scans to properly implement the drm_connector
operations.
As such the drm_bridge_connector looked like the ideal component to
implement DRM bridge hotplug.
This series augments the drm_bridge_connector code to be able to create and
destroy the drm_connector reacting on hot(un)plug events.
== Series description
This series has many preparatory patches: to make the drm_bridge_connector
code allocate the drm_connector dynamically and to prepare various other
parts of the involved DRM code.
In current code the last bridge in the encoder chain is always assumed to
complete the pipeline. There is no way for a bridge to tell "I'm currently
the last bridge in the encoder chain, but I need an next bridge to
work". This series has some patches to allow such a behaviour:
* bridges can now report whether they conclude the pipeline or not (I
propose the "tail bridge" naming to tell this)
* returning -EPROBE_DEFER on bridge attach is not considered a hard error
anymore, it's normal when the next bridge is needed but not yet present
Compared to the hotplug-bridge approach, now bridge drivers need a bit of
adaptation in order to support that the following bridge is
hotpluggable. So there are a few changes to the samsung-dsim bridge driver,
which in the hardware I'm working on is the last fixed bridge.
Finally, as the current drm_bridge_connector API is unable to support
hotplug, a new API is added to enable the new features. This makes existing
drivers not affected by the changes. The last patch shows to changes needed
to an encoder driver to switch to the new API enabling hotplug support.
== Series layout
1. Add a dynamic variant of drm_connector_hdmi_init()
(needed for the bridge-connector to allocate the connector dynamically)
* drm/connector: split drmm_connector_hdmi_init() in 3 parts
* drm/connector: add drm_connector_hdmi_dynamic_init()
2. bridge-connector: split the long drm_bridge_connector_init() in various
parts in preparation to be called in different ways
* drm/display: bridge-connector: rename variable for consistency
* drm/display: bridge-connector: store the drm_device pointer
* drm/display: bridge-connector: split code creating the connector to a subfunction
* drm/display: bridge-connector: use a drm_bridge_connector internally, not a drm_connector
* drm/display: bridge-connector: extract drm_bridge_connector_get_bridges()
* drm/display: bridge-connector: return int from drm_bridge_connector_get_bridges()
* drm/display: bridge-connector: extract drm_bridge_connector_init_hdmi_audio_cec()
* drm/display: bridge-connector: return int from drm_bridge_connector_init_hdmi_audio_cec()
* drm/display: bridge-connector: return int from drm_bridge_connector_add_connector()
3. bridge-connector: create the drm_connector dynamically
* drm/display: bridge-connector: hoist error management to common code
* drm/display: bridge-connector: move drm_bridge_connector_put_bridges() definition eariler
* drm/display: bridge-connector: add non-drmm variant of drm_bridge_connector_put_bridges()
* drm/display: bridge-connector: allocate the connector dynamically
* drm/display: bridge-connector: move per-connector fields to the dynamic connector
* drm/display: bridge-connector: protect dynconn creation and destruction with a mutex
4. samsung-dsim: prepare driver to work when the following bridge is hotpluggable
* drm/bridge: samsung-dsim: remove the panel_bridge on host_detach
* drm/bridge: samsung-dsim: move drm_bridge_add() call to probe
* drm/bridge: samsung-dsim: attach: return -EPROBE_DEFER is next bridge not yet available
5. Misc preparation work
* drm/bridge: initialize chain_node list head on allocation
* drm/bridge: initialize chain_node list head on detach and attach errors
6. drm_bridge: stop pipeline when a bridge is removed
* drm/encoder: add drm_encoder_cleanup_from()
* drm/atomic: move drm_atomic_helper_disable_all() and drm_atomic_helper_shutdown() from drm_atomic_helper to drm_atomic
* drm/bridge: shutdown and cleanup on bridge unplug
7. Add notifier mechanism to let common code (the bridge-connector)
take actions on hotplug events
* drm: event-notifier: add mechanism to notify about hotplug events
* drm/bridge: notify about detached bridges
* drm/mipi-dsi: turn DRM_MIPI_DSI into a tristate
* drm/mipi-dsi: notify about DSI attach
8. Allow common code to know when the pipeline is complete
* drm/bridge: add drm_bridge_is_tail() to know whether a bridge completes the pipeline
* drm/bridge: panel: implement .is_tail
* drm/bridge: display-connector: implement .is_tail
* drm/bridge: samsung-dsim: implement .is_tail
* drm/bridge: ti-sn65dsi83: implement .is_tail
9. Allow probing incomplete pipelines
* drm/bridge: drm_bridge_attach(): don't fail on -EPROBE_DEFER
10. Implement bridge hotplug in bridge-connector, enable it in a driver
* drm/display: bridge-connector: handle bridge hotplug
* drm/mxsfb/lcdif: enable bridge hotplug
== Grand plan
This is part of the work to support hotplug of DRM bridges. The grand plan
was discussed in [0].
Here's the work breakdown (➜ marks the current series):
1. … add refcounting to DRM bridges struct drm_bridge,
based on devm_drm_bridge_alloc()
A. ✔ add new alloc API and refcounting (v6.16)
B. ✔ convert all bridge drivers to new API (v6.17)
C. ✔ kunit tests (v6.17)
D. ✔ add get/put to drm_bridge_add/remove() + attach/detach()
and warn on old allocation pattern (v6.17)
E. … add get/put on drm_bridge accessors
1. ✔ drm_bridge_chain_get_first_bridge(), add cleanup action (v6.18)
2. ✔ drm_bridge_get_prev_bridge() (v6.18)
3. ✔ drm_bridge_get_next_bridge() (v6.19)
4. ✔ drm_for_each_bridge_in_chain() (v6.19)
5. ✔ drm_bridge_connector_init (v6.19)
6. ✔ protect encoder bridge chain with a mutex (v7.2)
7. ✔ of_drm_find_bridge
a. ✔ add of_drm_get_bridge() (v7.0),
convert basic direct users (v7.0-v7.1)
b. ✔ convert direct of_drm_get_bridge() users, part 2 (v7.0)
c. ✔ convert direct of_drm_get_bridge() users, part 3 (v7.0)
d. ✔ convert direct of_drm_get_bridge() users, part 4 (v7.1-v7.2)
e. ✔ bridge-only drm_of_find_panel_or_bridge() users (v7.2)
8. drm_of_find_panel_or_bridge, *_of_get_bridge
9. ✔ enforce drm_bridge_add before drm_bridge_attach (v6.19)
F. ✔ debugfs improvements
1. ✔ add top-level 'bridges' file (v6.16)
2. ✔ show refcount and list lingering bridges (v6.19)
2. … handle gracefully atomic updates during bridge removal
A. ✔ Add drm_bridge_enter/exit() to protect device resources (v7.0)
B. … protect private_obj removal from list
C. ✔ Add drm_bridge_clear_and_put() (v7.1)
3. … DSI host-device driver interaction
4. ✔ removing the need for the "always-disconnected" connector
5. ✔ Migrate i.MX LCDIF driver to bridge-connector (v7.2)
6. ➜ DRM bridge hotplug
A. ➜ Bridge hotplug management in the DRM core
1. ✔ bridge-connector: attach encoder to the connector (v7.2)
2. ➜ drm bridge hotplug
B. Device tree description
[0] https://lore.kernel.org/lkml/20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com/#t
== Dependencies
This series depends on:
* "drm/atomic: drm_atomic_private_obj_fini: protect private_obj removal
from list"
- https://lore.kernel.org/lkml/20260324-drm-bridge-atomic-vs-remove-private_obj-v3-1-64deefe84044@bootlin.com/
- Reason: avoid sporadic deadlock
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
Luca Ceresoli (37):
drm/connector: split drmm_connector_hdmi_init() in 3 parts
drm/connector: add drm_connector_hdmi_dynamic_init()
drm/display: bridge-connector: rename variable for consistency
drm/display: bridge-connector: store the drm_device pointer
drm/display: bridge-connector: split code creating the connector to a subfunction
drm/display: bridge-connector: use a drm_bridge_connector internally, not a drm_connector
drm/display: bridge-connector: extract drm_bridge_connector_get_bridges()
drm/display: bridge-connector: return int from drm_bridge_connector_get_bridges()
drm/display: bridge-connector: extract drm_bridge_connector_init_hdmi_audio_cec()
drm/display: bridge-connector: return int from drm_bridge_connector_init_hdmi_audio_cec()
drm/display: bridge-connector: return int from drm_bridge_connector_add_connector()
drm/display: bridge-connector: hoist error management to common code
drm/display: bridge-connector: move drm_bridge_connector_put_bridges() definition eariler
drm/display: bridge-connector: add non-drmm variant of drm_bridge_connector_put_bridges()
drm/display: bridge-connector: allocate the connector dynamically
drm/display: bridge-connector: move per-connector fields to the dynamic connector
drm/display: bridge-connector: protect dynconn creation and destruction with a mutex
drm/bridge: samsung-dsim: remove the panel_bridge on host_detach
drm/bridge: samsung-dsim: move drm_bridge_add() call to probe
drm/bridge: samsung-dsim: attach: return -EPROBE_DEFER is next bridge not yet available
drm/bridge: initialize chain_node list head on allocation
drm/bridge: initialize chain_node list head on detach and attach errors
drm/encoder: add drm_encoder_cleanup_from()
drm/atomic: move drm_atomic_helper_disable_all() and drm_atomic_helper_shutdown() from drm_atomic_helper to drm_atomic
drm/bridge: shutdown and cleanup on bridge unplug
drm: event-notifier: add mechanism to notify about hotplug events
drm/bridge: notify about detached bridges
drm/mipi-dsi: turn DRM_MIPI_DSI into a tristate
drm/mipi-dsi: notify about DSI attach
drm/bridge: add drm_bridge_is_tail() to know whether a bridge completes the pipeline
drm/bridge: panel: implement .is_tail
drm/bridge: display-connector: implement .is_tail
drm/bridge: samsung-dsim: implement .is_tail
drm/bridge: ti-sn65dsi83: implement .is_tail
drm/bridge: drm_bridge_attach(): don't fail on -EPROBE_DEFER
drm/display: bridge-connector: handle bridge hotplug
drm/mxsfb/lcdif: enable bridge hotplug
drivers/gpu/drm/Kconfig | 7 +-
drivers/gpu/drm/Makefile | 2 +
drivers/gpu/drm/bridge/display-connector.c | 7 +
drivers/gpu/drm/bridge/panel.c | 8 +-
drivers/gpu/drm/bridge/samsung-dsim.c | 36 +-
drivers/gpu/drm/bridge/ti-sn65dsi83.c | 7 +
drivers/gpu/drm/display/drm_bridge_connector.c | 835 +++++++++++++++++--------
drivers/gpu/drm/drm_atomic.c | 115 ++++
drivers/gpu/drm/drm_atomic_helper.c | 76 +--
drivers/gpu/drm/drm_bridge.c | 47 +-
drivers/gpu/drm/drm_connector.c | 155 +++--
drivers/gpu/drm/drm_encoder.c | 38 ++
drivers/gpu/drm/drm_event_notifier.c | 58 ++
drivers/gpu/drm/drm_mipi_dsi.c | 3 +
drivers/gpu/drm/mxsfb/lcdif_drv.c | 8 +-
include/drm/bridge/samsung-dsim.h | 2 +
include/drm/drm_atomic.h | 3 +
include/drm/drm_bridge.h | 19 +
include/drm/drm_bridge_connector.h | 2 +
include/drm/drm_connector.h | 9 +
include/drm/drm_encoder.h | 1 +
include/drm/drm_event_notifier.h | 36 ++
22 files changed, 1078 insertions(+), 396 deletions(-)
---
base-commit: 289b9a790d52a6865db3006f07922a9191faa54e
change-id: 20260515-drm-bridge-hotplug-46265d3a2f85
Best regards,
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply
* [PATCH] i2c: imx: mark I2C adapter when hardware is powered down
From: Carlos Song (OSS) @ 2026-05-19 10:38 UTC (permalink / raw)
To: o.rempel, kernel, andi.shyti, Frank.Li, s.hauer, festevam,
carlos.song, haibo.chen
Cc: linux-i2c, imx, linux-arm-kernel, linux-kernel, stable
From: Carlos Song <carlos.song@nxp.com>
Mark the I2C adapter as suspended during system suspend to block further
transfers, and resume it on system resume. This prevents potential hangs
when the hardware is powered down but clients still attempt I2C transfers.
Fixes: 358025ac091e ("i2c: imx: make controller available until system suspend_noirq() and from resume_noirq()")
Cc: stable@vger.kernel.org
Signed-off-by: Carlos Song <carlos.song@nxp.com>
---
drivers/i2c/busses/i2c-imx.c | 32 ++++++++++++++++++++++++++++++--
1 file changed, 30 insertions(+), 2 deletions(-)
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index a208fefd3c3b..3b19d4a424ca 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -1913,6 +1913,34 @@ static int i2c_imx_runtime_resume(struct device *dev)
return ret;
}
+static int __maybe_unused i2c_imx_suspend_noirq(struct device *dev)
+{
+ struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret)
+ return ret;
+
+ i2c_mark_adapter_suspended(&i2c_imx->adapter);
+
+ return 0;
+}
+
+static int __maybe_unused i2c_imx_resume_noirq(struct device *dev)
+{
+ struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret)
+ return ret;
+
+ i2c_mark_adapter_resumed(&i2c_imx->adapter);
+
+ return 0;
+}
+
static int i2c_imx_suspend(struct device *dev)
{
/*
@@ -1946,8 +1974,8 @@ static int i2c_imx_resume(struct device *dev)
}
static const struct dev_pm_ops i2c_imx_pm_ops = {
- NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(i2c_imx_suspend_noirq,
+ i2c_imx_resume_noirq)
SYSTEM_SLEEP_PM_OPS(i2c_imx_suspend, i2c_imx_resume)
RUNTIME_PM_OPS(i2c_imx_runtime_suspend, i2c_imx_runtime_resume, NULL)
};
--
2.43.0
^ permalink raw reply related
* [GIT PULL 2/2] Renesas DTS updates for v7.2
From: Geert Uytterhoeven @ 2026-05-19 10:35 UTC (permalink / raw)
To: soc, soc
Cc: Magnus Damm, linux-arm-kernel, linux-renesas-soc,
Geert Uytterhoeven
In-Reply-To: <cover.1779185457.git.geert+renesas@glider.be>
The following changes since commit 5d6919055dec134de3c40167a490f33c74c12581:
Linux 7.1-rc3 (2026-05-10 14:08:09 -0700)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-devel.git tags/renesas-dts-for-v7.2-tag1
for you to fetch changes up to 44f1ef06ceec55b7704c7d773d6136ca8b90f8b7:
ARM: dts: renesas: r8a73a4: Describe coresight on R-Mobile APE6 (2026-05-15 11:35:25 +0200)
----------------------------------------------------------------
Renesas DTS updates for v7.2
- Add GPU support for R-Car M3-W(+)-based ULCB and Salvator-X(S)
development boards,
- Add Ethernet, OPP table, interrupt, pin control, and watchdog
support for the RZ/G3L SoC and the RZ/G3L SMARC SoM board,
- Add Coresight support for the R-Mobile A1 and APE6 SoCs, and the
Armadillo-800 EVA and APE6EVM development boards,
- Miscellaneous fixes and improvements.
Note that this (1) is based on v7.1-rc3, as PowerVR GPU enablement has a
dependency on commit 26735dfdd8930d9e ("pmdomain: core: Fix detach
procedure for virtual devices in genpd"), and (2) includes:
- Renesas SoC fixes for v7.1, which are already present in
soc/for-next, but not yet in v7.1-rc4,
- DT binding definition updates for the R-Mobile A1 and APE6 SoCs,
which are shared by the clock subsystem and DT source files,
- DT binding definitions for the RZ/G3L SoC, which are shared by the
pin control subsystem and DT source files.
Thanks for pulling!
----------------------------------------------------------------
Biju Das (13):
dt-bindings: pinctrl: renesas,rzg2l-pinctrl: Document reset-names
dt-bindings: pinctrl: renesas: Document RZ/G3L SoC
arm64: dts: renesas: r9a08g046: Add GBETH nodes
arm64: dts: renesas: rzg3l-smarc-som: Enable eth0 (GBETH0) interface
arm64: dts: renesas: Add pinctrl reset-names for RZ/G2L and RZ/V2H family SoCs
arm64: dts: renesas: r9a08g046: Add OPP table
arm64: dts: renesas: r9a08g046: Add ICU node
arm64: dts: renesas: r9a08g046: Add pincontrol node
arm64: dts: renesas: r9a08g046l48-smarc: Add SCIF0 pincontrol
arm64: dts: renesas: rzg3l-smarc-som: Add pinctrl configuration for ETH0
arm64: dts: renesas: rzg3l-smarc-som: Enable eth1 (GBETH1) interface
arm64: dts: renesas: r9a08g046: Add wdt device node
arm64: dts: renesas: rzg3l-smarc-som: Enable watchdog
Geert Uytterhoeven (5):
arm64: dts: renesas: r8a78000: Fix SCIF brg_int clocks
Merge tag 'renesas-fixes-for-v7.1-tag1' into renesas-dts-for-v7.2
Merge tag 'renesas-r8a7740-dt-binding-defs-tag1' into renesas-dts-for-v7.2
Merge tag 'renesas-r9a08g046-dt-binding-defs-tag2' into renesas-dts-for-v7.2
Merge tag 'renesas-r8a73a4-dt-binding-defs-tag1' into renesas-dts-for-v7.2
Marek Vasut (24):
arm64: dts: renesas: draak/ebisu-panel: Fix missing cells and reg in DTO
arm64: dts: renesas: salvator-panel: Fix missing cells and reg in DTO
arm64: dts: renesas: rz-smarc-cru-csi-ov5645: Fix missing cells and reg in CSI2 subnode
arm64: dts: renesas: rz-smarc-du-adv7513-smarc: Fix missing cells and reg in DU subnode
ARM: dts: renesas: r8a7778: Add missing unit address to bus node
ARM: dts: renesas: r8a7779: Add missing unit address to bus node
ARM: dts: renesas: r8a7792: Add missing unit address to bus node
ARM: dts: renesas: r7s72100: Add missing unit address to bus node
ARM: dts: renesas: genmai: Drop superfluous cells
ARM: dts: renesas: rskrza1: Drop superfluous cells
dt-bindings: clock: renesas,cpg-clocks: Document ZT/ZTR trace clock on R-Mobile A1
arm64: dts: renesas: r8a77960-ulcb: Enable GPU support
arm64: dts: renesas: r8a77960-salvator-x: Enable GPU support
arm64: dts: renesas: r8a77960-salvator-xs: Enable GPU support
arm64: dts: renesas: r8a77961-ulcb: Enable GPU support
arm64: dts: renesas: r8a77961-salvator-xs: Enable GPU support
ARM: dts: renesas: r8a7740: Add ZT/ZTR trace clocks
ARM: dts: renesas: r8a7740: Describe coresight
arm64: dts: renesas: gray-hawk-single: Fix AVB0 PHY node alignment
arm64: dts: renesas: ebisu: Sort sound node
arm64: dts: renesas: salvator-common: Sort sound node
dt-bindings: clock: renesas,cpg-clocks: Document ZT/ZTR trace clock on R-Mobile APE6
ARM: dts: renesas: r8a73a4: Add ZT/ZTR trace clock on R-Mobile APE6
ARM: dts: renesas: r8a73a4: Describe coresight on R-Mobile APE6
Tommaso Merciai (2):
arm64: dts: renesas: r9a09g057: Add #mux-state-cells to usb2{0,1}phyrst
arm64: dts: renesas: r9a09g056: Add #mux-state-cells to usb20phyrst
.../bindings/clock/renesas,cpg-clocks.yaml | 8 +-
.../bindings/pinctrl/renesas,rzg2l-pinctrl.yaml | 35 ++
arch/arm/boot/dts/renesas/r7s72100-genmai.dts | 3 -
arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts | 2 -
arch/arm/boot/dts/renesas/r7s72100.dtsi | 2 +-
arch/arm/boot/dts/renesas/r8a73a4.dtsi | 114 +++++-
arch/arm/boot/dts/renesas/r8a7740.dtsi | 116 +++++-
arch/arm/boot/dts/renesas/r8a7778.dtsi | 2 +-
arch/arm/boot/dts/renesas/r8a7779.dtsi | 2 +-
arch/arm/boot/dts/renesas/r8a7792.dtsi | 2 +-
.../dts/renesas/draak-ebisu-panel-aa104xd12.dtso | 5 +
arch/arm64/boot/dts/renesas/ebisu.dtsi | 34 +-
arch/arm64/boot/dts/renesas/gray-hawk-single.dtsi | 10 +-
.../arm64/boot/dts/renesas/r8a77960-salvator-x.dts | 4 +
.../boot/dts/renesas/r8a77960-salvator-xs.dts | 4 +
arch/arm64/boot/dts/renesas/r8a77960-ulcb.dts | 4 +
.../boot/dts/renesas/r8a77961-salvator-xs.dts | 4 +
arch/arm64/boot/dts/renesas/r8a77961-ulcb.dts | 4 +
arch/arm64/boot/dts/renesas/r8a78000.dtsi | 8 +-
arch/arm64/boot/dts/renesas/r9a07g043.dtsi | 1 +
arch/arm64/boot/dts/renesas/r9a07g044.dtsi | 1 +
arch/arm64/boot/dts/renesas/r9a07g054.dtsi | 1 +
arch/arm64/boot/dts/renesas/r9a08g045.dtsi | 1 +
arch/arm64/boot/dts/renesas/r9a08g046.dtsi | 402 ++++++++++++++++++++-
arch/arm64/boot/dts/renesas/r9a08g046l48-smarc.dts | 13 +
arch/arm64/boot/dts/renesas/r9a09g047.dtsi | 1 +
arch/arm64/boot/dts/renesas/r9a09g056.dtsi | 2 +
arch/arm64/boot/dts/renesas/r9a09g057.dtsi | 3 +
.../boot/dts/renesas/rz-smarc-cru-csi-ov5645.dtsi | 5 +
.../boot/dts/renesas/rz-smarc-du-adv7513.dtsi | 5 +
arch/arm64/boot/dts/renesas/rzg3l-smarc-som.dtsi | 132 +++++++
arch/arm64/boot/dts/renesas/salvator-common.dtsi | 26 +-
.../boot/dts/renesas/salvator-panel-aa104xd12.dtso | 5 +
include/dt-bindings/clock/r8a73a4-clock.h | 2 +
include/dt-bindings/clock/r8a7740-clock.h | 2 +
.../pinctrl/renesas,r9a08g046-pinctrl.h | 38 ++
36 files changed, 945 insertions(+), 58 deletions(-)
create mode 100644 include/dt-bindings/pinctrl/renesas,r9a08g046-pinctrl.h
^ permalink raw reply
* [GIT PULL 1/2] Renesas driver updates for v7.2
From: Geert Uytterhoeven @ 2026-05-19 10:35 UTC (permalink / raw)
To: soc, soc
Cc: Magnus Damm, linux-arm-kernel, linux-renesas-soc,
Geert Uytterhoeven
In-Reply-To: <cover.1779185457.git.geert+renesas@glider.be>
The following changes since commit 254f49634ee16a731174d2ae34bc50bd5f45e731:
Linux 7.1-rc1 (2026-04-26 14:19:00 -0700)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-devel.git tags/renesas-drivers-for-v7.2-tag1
for you to fetch changes up to 17e48e7e5f18b45fd4a9411090148aae3b74f7f3:
soc: renesas: Convert to of_machine_get_match() (2026-05-11 09:56:17 +0200)
----------------------------------------------------------------
Renesas driver updates for v7.2
- Add Multifunctional Interface (MFIS) mailbox and product register
support for R-Car X5H,
- Miscellaneous fixes and improvements.
Note that this includes DT binding definitions for R-Car X5H, which are
shared by driver and DT source files.
Thanks for pulling!
----------------------------------------------------------------
Claudiu Beznea (5):
soc: renesas: r9a08g045-sysc: Move common code to a helper
soc: renesas: r9a08g046-sysc: Move common code to a helper
soc: renesas: r9a09g047-sys: Move common code to a helper
soc: renesas: r9a09g056-sys: Move common code to a helper
soc: renesas: r9a09g057-sys: Move common code to a helper
Geert Uytterhoeven (2):
Merge tag 'renesas-r8a78000-dt-binding-defs-tag1' into renesas-drivers-for-v7.2
soc: renesas: Convert to of_machine_get_match()
Wolfram Sang (3):
dt-bindings: soc: renesas: Document MFIS IP core
soc: renesas: Add Renesas R-Car MFIS driver
soc: renesas: Add R-Car X5H PRR support
.../soc/renesas/renesas,r8a78000-mfis.yaml | 187 +++++++++++
drivers/soc/renesas/Kconfig | 9 +
drivers/soc/renesas/Makefile | 1 +
drivers/soc/renesas/r9a08g045-sysc.c | 33 +-
drivers/soc/renesas/r9a08g046-sysc.c | 31 +-
drivers/soc/renesas/r9a09g047-sys.c | 34 +-
drivers/soc/renesas/r9a09g056-sys.c | 33 +-
drivers/soc/renesas/r9a09g057-sys.c | 44 +--
drivers/soc/renesas/rcar-mfis.c | 344 +++++++++++++++++++++
drivers/soc/renesas/renesas-soc.c | 10 +-
include/dt-bindings/soc/renesas,r8a78000-mfis.h | 28 ++
11 files changed, 644 insertions(+), 110 deletions(-)
create mode 100644 Documentation/devicetree/bindings/soc/renesas/renesas,r8a78000-mfis.yaml
create mode 100644 drivers/soc/renesas/rcar-mfis.c
create mode 100644 include/dt-bindings/soc/renesas,r8a78000-mfis.h
^ permalink raw reply
* [GIT PULL 0/2] Renesas SoC updates for v7.2
From: Geert Uytterhoeven @ 2026-05-19 10:35 UTC (permalink / raw)
To: soc, soc
Cc: Magnus Damm, linux-arm-kernel, linux-renesas-soc,
Geert Uytterhoeven
Hi SoC folks,
This is my first pull request for the inclusion of Renesas SoC updates
for v7.2.
It consists of 2 parts:
[GIT PULL 1/2] Renesas driver updates for v7.2
- Add Multifunctional Interface (MFIS) mailbox and product register
support for R-Car X5H,
- Miscellaneous fixes and improvements.
[GIT PULL 2/2] Renesas DTS updates for v7.2
- Add GPU support for R-Car M3-W(+)-based ULCB and Salvator-X(S)
development boards,
- Add Ethernet, OPP table, interrupt, pin control, and watchdog
support for the RZ/G3L SoC and the RZ/G3L SMARC SoM board,
- Add Coresight support for the R-Mobile A1 and APE6 SoCs, and the
Armadillo-800 EVA and APE6EVM development boards,
- Miscellaneous fixes and improvements.
Note that the first PR includes DT binding definitions for R-Car X5H,
which are shared by driver and DT source files.
Note that the second PR (1) is based on v7.1-rc3, as PowerVR GPU
enablement has a dependency on commit 26735dfdd8930d9e ("pmdomain: core:
Fix detach procedure for virtual devices in genpd"), and (2) includes:
- Renesas SoC fixes for v7.1, which are already present in
soc/for-next, but not yet in v7.1-rc4,
- DT binding definition updates for the R-Mobile A1 and APE6 SoCs,
which are shared by the clock subsystem and DT source files,
- DT binding definitions for the RZ/G3L SoC, which are shared by the
pin control subsystem and DT source files.
Thanks for pulling!
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* RE: [PATCH v2 1/2] i2c: imx: Don't recover bus when arbitration lost
From: Carlos Song (OSS) @ 2026-05-19 10:29 UTC (permalink / raw)
To: Dan Scally
Cc: linux-i2c@vger.kernel.org, imx@lists.linux.dev,
linux-arm-kernel@lists.infradead.org, Andi Shyti, Frank Li,
Sascha Hauer, Fabio Estevam, Gao Pan, Fugang Duan, Wolfram Sang,
Oleksij Rempel, Pengutronix Kernel Team
In-Reply-To: <aa1a6931-1a98-4f72-b8fc-ae05df28f931@ideasonboard.com>
> -----Original Message-----
> From: Dan Scally <dan.scally@ideasonboard.com>
> Sent: Tuesday, May 19, 2026 4:42 PM
> To: Oleksij Rempel <o.rempel@pengutronix.de>; Pengutronix Kernel Team
> <kernel@pengutronix.de>
> Cc: linux-i2c@vger.kernel.org; imx@lists.linux.dev;
> linux-arm-kernel@lists.infradead.org; Andi Shyti <andi.shyti@kernel.org>; Frank
> Li <frank.li@nxp.com>; Sascha Hauer <s.hauer@pengutronix.de>; Fabio
> Estevam <festevam@gmail.com>; Gao Pan <b54642@freescale.com>; Fugang
> Duan <B38611@freescale.com>; Wolfram Sang <wsa@kernel.org>
> Subject: Re: [PATCH v2 1/2] i2c: imx: Don't recover bus when arbitration lost
>
> [You don't often get email from dan.scally@ideasonboard.com. Learn why this is
> important at https://aka.ms/LearnAboutSenderIdentification ]
>
> Hello Oleksij / all
>
> On 24/04/2026 13:36, Daniel Scally wrote:
> > In i2c_imx_xfer_common(), the driver attempts bus recovery whenever
> > i2c_imx_start() fails. One of the failure modes for i2c_imx_start() is
> > an arbitration-lost signal which results when a second I2C master on
> > the bus tries to control the bus simultaneously, which is a normal and
> > expected behaviour.
> >
> > Bus recovery is not the right response for this case. Add a check for
> > the -EAGAIN return code to avoid running the bus recovery.
> >
> > Fixes: 1c4b6c3bcf30d ("i2c: imx: implement bus recovery")
> > Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com>
> > ---
>
> I raised this patch after we had issues with one of the i2c controllers on imx8mp.
> In that case, the bus had multiple masters that were causing the SoC's i2c
> controller to lose arbitration. The result was that the framework attempted to
> run i2c_generic_scl_recovery() and regularly hit the "SCL is stuck low, exit
> recovery" message [1] because the bus was busy rather than stuck.
>
> I'm now experiencing a different issue with the imx8mp in which a different
> controller - which isn't on a multiple-masters bus - starts transacting fine early in
> boot, but then seems to get stuck - any attempt to start a transaction by either
> a driver or i2ctransfer results in the IAL bit in I2C_I2SR being set and so the
> driver reports that it's lost arbitration [2]. In this case, the bus recovery is
> needed to fix the problem, and so this commit hurts things rather than helps
> them. This problem isn't consistent - I get it on maybe 10% of boots.
>
Hi Dan,
This is the RM shows:
Arbitration lost. Set by hardware in the following circumstances (IAL must be cleared by software by
writing a "0" to it at the start of the interrupt service routine):
* I2Cn_SDA input samples low when the master drives high during an address or data-transmit cycle.
* I2Cn_SDA input samples low when the master drives high during the acknowledge bit of a datareceive
cycle.
For the above two cases, the bit is set at the falling edge of the ninth I2Cn_SCL clock during the ACK
cycle.
* A Start cycle is attempted when the bus is busy.
* A Repeated Start cycle is requested in Slave mode.
* A Stop condition is detected when the master did not request it.
NOTE: Software cannot set the bit.
0 No arbitration lost.
1 Arbitration is lost.
From my understanding:
The IAL (Arbitration Lost) bit is set not only when true arbitration is lost, but also in several other conditions:
- SDA is sampled low when the master drives it high (during address/data or ACK phase)
- A START is attempted while the bus is busy
- A STOP condition is detected unexpectedly
- A repeated START occurs in slave mode
So in practice, IAL can be asserted not only by real arbitration loss, but also when the controller detects abnormal bus conditions.
Since your system is single-master, this is unlikely to be a true arbitration scenario. Instead, it is more likely caused by signal integrity or timing-related issues, such as:
- weak pull-up / slow rising edges
- noise or glitches on SDA
- timing violations from the slave device
- others
As a workaround, you can enable the 'single-master' property to disable arbitration checks in single-master systems, for example:
&i2c1 {
clock-frequency = <400000>;
pinctrl-names = "default", "gpio";
pinctrl-0 = <&pinctrl_i2c1>;
pinctrl-1 = <&pinctrl_i2c1_gpio>;
scl-gpios = <&gpio5 14 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
sda-gpios = <&gpio5 15 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
single-master;
status = "okay";
};
Hope it will help some.
Carlos
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox