On Wed, 2 Aug 2023 at 11:15, Neil Armstrong <neil.armstrong@xxxxxxxxxx> wrote: > > Hi, > > On 02/08/2023 03:18, Dmitry Baryshkov 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 | 127 ++++++++++++++++++++ > > I wonder why drm/display/ and not drm/bridge ? > > It's an helper, but it's mainly a bridge. Why not? I'm open to any suggestions. > > > include/drm/display/drm_simple_bridge.h | 19 +++ > > 4 files changed, 157 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..9e80efe67b93 > > --- /dev/null > > +++ b/drivers/gpu/drm/display/drm_simple_bridge.c > > @@ -0,0 +1,127 @@ > > +// 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); > > +} > > +EXPORT_SYMBOL_GPL(drm_simple_bridge_register); > > + > > +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; > > +#ifdef CONFIG_OF > > + data->bridge.of_node = data->dev->of_node; > > +#endif > > I think the whole stuff should depend on OF since devm_drm_of_get_bridge() is a no-op when !OF Hmm, true. Probably we should rework bridges to use fwnode at some point. > > > + > > + 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); > > + > > +MODULE_AUTHOR("Dmitry Baryshkov <dmitry.baryshkov@xxxxxxxxxx>"); > > +MODULE_DESCRIPTION("DRM simple bridge helper"); > > +MODULE_LICENSE("GPL"); > > 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 > > The design looks fine, but I'll need another review. > > Thanks, > Neil > -- With best wishes Dmitry