On Sat, 29 Jul 2023 at 23:43, Dmitry Baryshkov <dmitry.baryshkov@xxxxxxxxxx> wrote: > > Define a helper for creating simple transparent bridges which serve the > only purpose of linking devices into the bridge chain up to the last > bridge representing the connector. This is especially useful for > DP/USB-C bridge chains, which can span across several devices, but do > not require any additional functionality from the intermediate bridges. > > Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@xxxxxxxxxx> > --- > drivers/gpu/drm/display/Kconfig | 9 ++ > drivers/gpu/drm/display/Makefile | 2 + > drivers/gpu/drm/display/drm_simple_bridge.c | 120 ++++++++++++++++++++ > include/drm/display/drm_simple_bridge.h | 19 ++++ > 4 files changed, 150 insertions(+) > create mode 100644 drivers/gpu/drm/display/drm_simple_bridge.c > create mode 100644 include/drm/display/drm_simple_bridge.h > > diff --git a/drivers/gpu/drm/display/Kconfig b/drivers/gpu/drm/display/Kconfig > index 09712b88a5b8..a6132984b9e3 100644 > --- a/drivers/gpu/drm/display/Kconfig > +++ b/drivers/gpu/drm/display/Kconfig > @@ -49,3 +49,12 @@ config DRM_DP_CEC > > Note: not all adapters support this feature, and even for those > that do support this they often do not hook up the CEC pin. > + > +config DRM_SIMPLE_BRIDGE > + tristate > + depends on DRM > + select AUXILIARY_BUS > + select DRM_PANEL_BRIDGE > + help > + Simple transparent bridge that is used by several drivers to build > + bridges chain. > diff --git a/drivers/gpu/drm/display/Makefile b/drivers/gpu/drm/display/Makefile > index 17ac4a1006a8..6e2b0d7f24b3 100644 > --- a/drivers/gpu/drm/display/Makefile > +++ b/drivers/gpu/drm/display/Makefile > @@ -16,3 +16,5 @@ drm_display_helper-$(CONFIG_DRM_DP_AUX_CHARDEV) += drm_dp_aux_dev.o > drm_display_helper-$(CONFIG_DRM_DP_CEC) += drm_dp_cec.o > > obj-$(CONFIG_DRM_DISPLAY_HELPER) += drm_display_helper.o > + > +obj-$(CONFIG_DRM_SIMPLE_BRIDGE) += drm_simple_bridge.o > diff --git a/drivers/gpu/drm/display/drm_simple_bridge.c b/drivers/gpu/drm/display/drm_simple_bridge.c > new file mode 100644 > index 000000000000..c9966691efb1 > --- /dev/null > +++ b/drivers/gpu/drm/display/drm_simple_bridge.c > @@ -0,0 +1,120 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Copyright (C) 2023 Linaro Ltd. > + * > + * Author: Dmitry Baryshkov <dmitry.baryshkov@xxxxxxxxxx> > + */ > +#include <linux/auxiliary_bus.h> > +#include <linux/module.h> > + > +#include <drm/drm_bridge.h> > +#include <drm/display/drm_simple_bridge.h> > + > +static DEFINE_IDA(simple_bridge_ida); > + > +static void drm_simple_bridge_release(struct device *dev) > +{ > + struct auxiliary_device *adev = to_auxiliary_dev(dev); > + > + kfree(adev); > +} > + > +static void drm_simple_bridge_unregister_adev(void *_adev) > +{ > + struct auxiliary_device *adev = _adev; > + > + auxiliary_device_delete(adev); > + auxiliary_device_uninit(adev); > +} > + > +int drm_simple_bridge_register(struct device *parent) > +{ > + struct auxiliary_device *adev; > + int ret; > + > + adev = kzalloc(sizeof(*adev), GFP_KERNEL); > + if (!adev) > + return -ENOMEM; > + > + ret = ida_alloc(&simple_bridge_ida, GFP_KERNEL); > + if (ret < 0) > + return ret; > + > + adev->id = ret; > + adev->name = "simple_bridge"; > + adev->dev.parent = parent; > + adev->dev.of_node = parent->of_node; > + adev->dev.release = drm_simple_bridge_release; > + > + ret = auxiliary_device_init(adev); > + if (ret) { > + kfree(adev); > + return ret; > + } > + > + ret = auxiliary_device_add(adev); > + if (ret) { > + auxiliary_device_uninit(adev); > + return ret; > + } > + > + return devm_add_action_or_reset(parent, drm_simple_bridge_unregister_adev, adev); > +} Forgot EXPORT_SYMBOL_GPL, MODULE_LICENSE, etc. I'll fix that for v2 if the overall idea is found to be acceptable. > + > +struct drm_simple_bridge_data { > + struct drm_bridge bridge; > + struct drm_bridge *next_bridge; > + struct device *dev; > +}; > + > +static int drm_simple_bridge_attach(struct drm_bridge *bridge, > + enum drm_bridge_attach_flags flags) > +{ > + struct drm_simple_bridge_data *data; > + > + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) > + return -EINVAL; > + > + data = container_of(bridge, struct drm_simple_bridge_data, bridge); > + > + return drm_bridge_attach(bridge->encoder, data->next_bridge, bridge, > + DRM_BRIDGE_ATTACH_NO_CONNECTOR); > +} > + > +static const struct drm_bridge_funcs drm_simple_bridge_funcs = { > + .attach = drm_simple_bridge_attach, > +}; > + > +static int drm_simple_bridge_probe(struct auxiliary_device *auxdev, > + const struct auxiliary_device_id *id) > +{ > + struct drm_simple_bridge_data *data; > + > + data = devm_kzalloc(&auxdev->dev, sizeof(*data), GFP_KERNEL); > + if (!data) > + return -ENOMEM; > + > + data->dev = &auxdev->dev; > + data->next_bridge = devm_drm_of_get_bridge(&auxdev->dev, auxdev->dev.of_node, 0, 0); > + if (IS_ERR(data->next_bridge)) > + return dev_err_probe(&auxdev->dev, PTR_ERR(data->next_bridge), > + "failed to acquire drm_bridge\n"); > + > + data->bridge.funcs = &drm_simple_bridge_funcs; > + data->bridge.of_node = data->dev->of_node; > + > + return devm_drm_bridge_add(data->dev, &data->bridge); > +} > + > +static const struct auxiliary_device_id drm_simple_bridge_table[] = { > + { .name = KBUILD_MODNAME ".simple_bridge" }, > + {}, > +}; > +MODULE_DEVICE_TABLE(auxiliary, drm_simple_bridge_table); > + > +struct auxiliary_driver drm_simple_bridge_drv = { > + .name = "simple_bridge", > + .id_table = drm_simple_bridge_table, > + .probe = drm_simple_bridge_probe, > +}; > +module_auxiliary_driver(drm_simple_bridge_drv); > diff --git a/include/drm/display/drm_simple_bridge.h b/include/drm/display/drm_simple_bridge.h > new file mode 100644 > index 000000000000..3da8e1fb1137 > --- /dev/null > +++ b/include/drm/display/drm_simple_bridge.h > @@ -0,0 +1,19 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Copyright (C) 2023 Linaro Ltd. > + * > + * Author: Dmitry Baryshkov <dmitry.baryshkov@xxxxxxxxxx> > + */ > +#ifndef DRM_SIMPLE_BRIDGE_H > +#define DRM_SIMPLE_BRIDGE_H > + > +#if IS_ENABLED(CONFIG_DRM_SIMPLE_BRIDGE) > +int drm_simple_bridge_register(struct device *parent); > +#else > +static inline int drm_simple_bridge_register(struct device *parent) > +{ > + return 0; > +} > +#endif > + > +#endif > -- > 2.39.2 > -- With best wishes Dmitry