[PATCH v6 6/6] staging: vc04_services: vchiq: Register devices with a custom bus_type

Stefan Wahren stefan.wahren at i2se.com
Sun Jan 22 15:50:46 PST 2023


Hi Umang,

Am 20.01.23 um 21:11 schrieb Umang Jain:
> The devices that the vchiq interface registers (bcm2835-audio,
> bcm2835-camera) are implemented and exposed by the VC04 firmware.
> The device tree describes the VC04 itself with the resources required
> to communicate with it through a mailbox interface. However, the
> vchiq interface registers these devices as platform devices. This
> also means the specific drivers for these devices are getting
> registered as platform drivers. This is not correct and a blatant
> abuse of platform device/driver.
>
> Replace the platform device/driver model with a standard device driver
> model. A custom bus_type, vchiq_bus_type, is created in the vchiq
> interface which matches the devices to their specific device drivers
> thereby, establishing driver binding. A struct vchiq_device wraps the
> struct device for each device being registered on the bus by the vchiq
> interface. On the other hand, struct vchiq_driver wraps the struct
> device_driver and the module_vchiq_driver() macro is provided for the
> driver registration.
>
> Each device registered will expose a 'name' read-only device attribute
> in sysfs (/sys/bus/vchiq-bus/devices). New devices and drivers can be
> added by registering on vchiq_bus_type and adding a corresponding
> device name entry in the static list of devices, vchiq_devices. There
> is currently no way to enumerate the VCHIQ devices that are available
> from the firmware.
>
> Signed-off-by: Umang Jain <umang.jain at ideasonboard.com>
> ---
>   drivers/staging/vc04_services/Makefile        |   1 +
>   .../vc04_services/bcm2835-audio/bcm2835.c     |  27 +++--
>   .../bcm2835-camera/bcm2835-camera.c           |  25 ++---
>   .../interface/vchiq_arm/vchiq_arm.c           |  52 +++++----
>   .../interface/vchiq_arm/vchiq_device.c        | 102 ++++++++++++++++++
>   .../interface/vchiq_arm/vchiq_device.h        |  39 +++++++
>   6 files changed, 192 insertions(+), 54 deletions(-)
>   create mode 100644 drivers/staging/vc04_services/interface/vchiq_arm/vchiq_device.c
>   create mode 100644 drivers/staging/vc04_services/interface/vchiq_arm/vchiq_device.h
>
> diff --git a/drivers/staging/vc04_services/Makefile b/drivers/staging/vc04_services/Makefile
> index 44794bdf6173..2d071e55e175 100644
> --- a/drivers/staging/vc04_services/Makefile
> +++ b/drivers/staging/vc04_services/Makefile
> @@ -5,6 +5,7 @@ vchiq-objs := \
>      interface/vchiq_arm/vchiq_core.o  \
>      interface/vchiq_arm/vchiq_arm.o \
>      interface/vchiq_arm/vchiq_debugfs.o \
> +   interface/vchiq_arm/vchiq_device.o \
>      interface/vchiq_arm/vchiq_connected.o \
>   
>   ifdef CONFIG_VCHIQ_CDEV
> diff --git a/drivers/staging/vc04_services/bcm2835-audio/bcm2835.c b/drivers/staging/vc04_services/bcm2835-audio/bcm2835.c
> index 00bc898b0189..05118dafe62d 100644
> --- a/drivers/staging/vc04_services/bcm2835-audio/bcm2835.c
> +++ b/drivers/staging/vc04_services/bcm2835-audio/bcm2835.c
> @@ -1,12 +1,12 @@
>   // SPDX-License-Identifier: GPL-2.0
>   /* Copyright 2011 Broadcom Corporation.  All rights reserved. */
>   
> -#include <linux/platform_device.h>
> -
>   #include <linux/init.h>
>   #include <linux/slab.h>
>   #include <linux/module.h>
>   
> +#include "../interface/vchiq_arm/vchiq_arm.h"
> +#include "../interface/vchiq_arm/vchiq_device.h"
>   #include "bcm2835.h"
>   
>   static bool enable_hdmi;
> @@ -268,9 +268,8 @@ static int snd_add_child_devices(struct device *device, u32 numchans)
>   	return 0;
>   }
>   
> -static int snd_bcm2835_alsa_probe(struct platform_device *pdev)
> +static int snd_bcm2835_alsa_probe(struct device *dev)
>   {
> -	struct device *dev = &pdev->dev;
>   	int err;
>   
>   	if (num_channels <= 0 || num_channels > MAX_SUBSTREAMS) {
> @@ -292,32 +291,32 @@ static int snd_bcm2835_alsa_probe(struct platform_device *pdev)
>   
>   #ifdef CONFIG_PM
>   
> -static int snd_bcm2835_alsa_suspend(struct platform_device *pdev,
> +static int snd_bcm2835_alsa_suspend(struct device *pdev,
>   				    pm_message_t state)
>   {
>   	return 0;
>   }
>   
> -static int snd_bcm2835_alsa_resume(struct platform_device *pdev)
> +static int snd_bcm2835_alsa_resume(struct device *pdev)
>   {
>   	return 0;
>   }
>   
>   #endif
>   
> -static struct platform_driver bcm2835_alsa_driver = {
> -	.probe = snd_bcm2835_alsa_probe,
> +static struct vchiq_driver bcm2835_alsa_driver = {
> +	.driver	= {
> +		.probe = snd_bcm2835_alsa_probe,
>   #ifdef CONFIG_PM
> -	.suspend = snd_bcm2835_alsa_suspend,
> -	.resume = snd_bcm2835_alsa_resume,
> +		.suspend = snd_bcm2835_alsa_suspend,
> +		.resume = snd_bcm2835_alsa_resume,
>   #endif
> -	.driver = {
>   		.name = "bcm2835_audio",
> -	},
> +	}
>   };
> -module_platform_driver(bcm2835_alsa_driver);
> +module_vchiq_driver(bcm2835_alsa_driver);
>   
>   MODULE_AUTHOR("Dom Cobley");
>   MODULE_DESCRIPTION("Alsa driver for BCM2835 chip");
>   MODULE_LICENSE("GPL");
> -MODULE_ALIAS("platform:bcm2835_audio");
> +MODULE_ALIAS("bcm2835_audio");
> diff --git a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c
> index 4f81765912ea..57f053de53b9 100644
> --- a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c
> +++ b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c
> @@ -24,8 +24,9 @@
>   #include <media/v4l2-event.h>
>   #include <media/v4l2-common.h>
>   #include <linux/delay.h>
> -#include <linux/platform_device.h>
>   
> +#include "../interface/vchiq_arm/vchiq_arm.h"
> +#include "../interface/vchiq_arm/vchiq_device.h"
>   #include "../vchiq-mmal/mmal-common.h"
>   #include "../vchiq-mmal/mmal-encodings.h"
>   #include "../vchiq-mmal/mmal-vchiq.h"
> @@ -1841,7 +1842,7 @@ static struct v4l2_format default_v4l2_format = {
>   	.fmt.pix.sizeimage = 1024 * 768,
>   };
>   
> -static int bcm2835_mmal_probe(struct platform_device *pdev)
> +static int bcm2835_mmal_probe(struct device *device)
>   {
>   	int ret;
>   	struct bcm2835_mmal_dev *dev;
> @@ -1896,7 +1897,7 @@ static int bcm2835_mmal_probe(struct platform_device *pdev)
>   						       &camera_instance);
>   		ret = v4l2_device_register(NULL, &dev->v4l2_dev);
>   		if (ret) {
> -			dev_err(&pdev->dev, "%s: could not register V4L2 device: %d\n",
> +			dev_err(device, "%s: could not register V4L2 device: %d\n",
>   				__func__, ret);
>   			goto free_dev;
>   		}
> @@ -1976,7 +1977,7 @@ static int bcm2835_mmal_probe(struct platform_device *pdev)
>   	return ret;
>   }
>   
> -static int bcm2835_mmal_remove(struct platform_device *pdev)
> +static int bcm2835_mmal_remove(struct device *device)
>   {
>   	int camera;
>   	struct vchiq_mmal_instance *instance = gdev[0]->instance;
> @@ -1990,17 +1991,17 @@ static int bcm2835_mmal_remove(struct platform_device *pdev)
>   	return 0;
>   }
>   
> -static struct platform_driver bcm2835_camera_driver = {
> -	.probe		= bcm2835_mmal_probe,
> -	.remove		= bcm2835_mmal_remove,
> -	.driver		= {
> -		.name	= "bcm2835-camera",
> -	},
> +static struct vchiq_driver bcm2835_camera_driver = {
> +	.driver = {
> +		.name		= "bcm2835-camera",
> +		.probe          = bcm2835_mmal_probe,
> +		.remove         = bcm2835_mmal_remove,
> +	}
>   };
>   
> -module_platform_driver(bcm2835_camera_driver)
> +module_vchiq_driver(bcm2835_camera_driver)
>   
>   MODULE_DESCRIPTION("Broadcom 2835 MMAL video capture");
>   MODULE_AUTHOR("Vincent Sanders");
>   MODULE_LICENSE("GPL");
> -MODULE_ALIAS("platform:bcm2835-camera");
> +MODULE_ALIAS("bcm2835-camera");
> diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
> index 22de23f3af02..4a57ff760106 100644
> --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
> +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
> @@ -12,6 +12,8 @@
>   #include <linux/cdev.h>
>   #include <linux/fs.h>
>   #include <linux/device.h>
> +#include <linux/device/bus.h>
> +#include <linux/string.h>
>   #include <linux/mm.h>
>   #include <linux/highmem.h>
>   #include <linux/pagemap.h>
> @@ -34,6 +36,7 @@
>   #include "vchiq_ioctl.h"
>   #include "vchiq_arm.h"
>   #include "vchiq_debugfs.h"
> +#include "vchiq_device.h"
>   #include "vchiq_connected.h"
>   #include "vchiq_pagelist.h"
>   
> @@ -65,9 +68,6 @@ int vchiq_susp_log_level = VCHIQ_LOG_ERROR;
>   DEFINE_SPINLOCK(msg_queue_spinlock);
>   struct vchiq_state g_state;
>   
> -static struct platform_device *bcm2835_camera;
> -static struct platform_device *bcm2835_audio;
> -
>   struct vchiq_drvdata {
>   	const unsigned int cache_line_size;
>   	struct rpi_firmware *fw;
> @@ -132,6 +132,11 @@ struct vchiq_pagelist_info {
>   	unsigned int scatterlist_mapped;
>   };
>   
> +static const char *const vchiq_devices[] = {
> +	"bcm2835_audio",
> +	"bcm2835-camera",
> +};
> +
>   static void __iomem *g_regs;
>   /* This value is the size of the L2 cache lines as understood by the
>    * VPU firmware, which determines the required alignment of the
> @@ -1763,33 +1768,13 @@ static const struct of_device_id vchiq_of_match[] = {
>   };
>   MODULE_DEVICE_TABLE(of, vchiq_of_match);
>   
> -static struct platform_device *
> -vchiq_register_child(struct platform_device *pdev, const char *name)
> -{
> -	struct platform_device_info pdevinfo;
> -	struct platform_device *child;
> -
> -	memset(&pdevinfo, 0, sizeof(pdevinfo));
> -
> -	pdevinfo.parent = &pdev->dev;
> -	pdevinfo.name = name;
> -	pdevinfo.id = PLATFORM_DEVID_NONE;
> -	pdevinfo.dma_mask = DMA_BIT_MASK(32);
> -
> -	child = platform_device_register_full(&pdevinfo);
> -	if (IS_ERR(child)) {
> -		dev_warn(&pdev->dev, "%s not registered\n", name);
> -		child = NULL;
> -	}
> -
> -	return child;
> -}
>   
>   static int vchiq_probe(struct platform_device *pdev)
>   {
>   	struct device_node *fw_node;
>   	const struct of_device_id *of_id;
>   	struct vchiq_drvdata *drvdata;
> +	unsigned int i;
>   	int err;
>   
>   	of_id = of_match_node(vchiq_of_match, pdev->dev.of_node);
> @@ -1832,8 +1817,12 @@ static int vchiq_probe(struct platform_device *pdev)
>   		goto error_exit;
>   	}
>   
> -	bcm2835_camera = vchiq_register_child(pdev, "bcm2835-camera");
> -	bcm2835_audio = vchiq_register_child(pdev, "bcm2835_audio");
> +	for (i = 0; i < ARRAY_SIZE(vchiq_devices); i++) {
> +		err = vchiq_device_register(&pdev->dev, vchiq_devices[i]);
> +		if (!err)
> +			dev_err(&pdev->dev, "Failed to register %s vchiq device\n",
> +				vchiq_devices[i]);
I think it's helpful to log "err" here.
> +	}
>   
>   	return 0;
>   
> @@ -1845,8 +1834,8 @@ static int vchiq_probe(struct platform_device *pdev)
>   
>   static int vchiq_remove(struct platform_device *pdev)
>   {
> -	platform_device_unregister(bcm2835_audio);
> -	platform_device_unregister(bcm2835_camera);
> +	bus_for_each_dev(&vchiq_bus_type, NULL, NULL, vchiq_device_unregister);
> +
>   	vchiq_debugfs_deinit();
>   	vchiq_deregister_chrdev();
>   
> @@ -1866,6 +1855,12 @@ static int __init vchiq_driver_init(void)
>   {
>   	int ret;
>   
> +	ret = bus_register(&vchiq_bus_type);
> +	if (ret) {
> +		pr_err("Failed to register %s\n", vchiq_bus_type.name);
> +		return ret;
> +	}
> +
>   	ret = platform_driver_register(&vchiq_driver);
>   	if (ret)
>   		pr_err("Failed to register vchiq driver\n");
Shouldn't we unregister the bus in this error case?
> @@ -1876,6 +1871,7 @@ module_init(vchiq_driver_init);
>   
>   static void __exit vchiq_driver_exit(void)
>   {
> +	bus_unregister(&vchiq_bus_type);
>   	platform_driver_unregister(&vchiq_driver);
>   }
>   module_exit(vchiq_driver_exit);
> diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_device.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_device.c
> new file mode 100644
> index 000000000000..ec542d6bc68a
> --- /dev/null
> +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_device.c
> @@ -0,0 +1,102 @@
> +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
> +/*
> + * vchiq_device.c - VCHIQ generic device and bus-type
> + *
> + * Copyright (c) 2023 Ideas On Board Oy
> + */
> +
> +#include <linux/device/bus.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +
> +#include "vchiq_device.h"
> +
> +static int vchiq_bus_type_match(struct device *dev, struct device_driver *drv);
> +
> +static ssize_t vchiq_dev_show(struct device *dev,
> +			      struct device_attribute *attr, char *buf)
> +{
> +	struct vchiq_device *device = container_of(dev, struct vchiq_device, dev);
> +
> +	return sprintf(buf, "%s", device->name);
> +}
> +
> +static DEVICE_ATTR_RO(vchiq_dev);
> +
> +static struct attribute *vchiq_dev_attrs[] = {
> +	&dev_attr_vchiq_dev.attr,
> +	NULL
> +};
> +
> +ATTRIBUTE_GROUPS(vchiq_dev);
> +
> +static const struct device_type vchiq_device_type = {
> +	.groups         = vchiq_dev_groups
> +};
> +
> +struct bus_type vchiq_bus_type = {
> +	.name   = "vchiq-bus",
> +	.match  = vchiq_bus_type_match,
> +};
> +EXPORT_SYMBOL_GPL(vchiq_bus_type);
> +
> +static int vchiq_bus_type_match(struct device *dev, struct device_driver *drv)
> +{
> +	if (dev->bus == &vchiq_bus_type &&
> +	    strcmp(dev_name(dev), drv->name) == 0)
> +		return 1;
Please add a empty line here.
> +	return 0;
> +}
> +
> +static void vchiq_device_release(struct device *dev)
> +{
> +	struct vchiq_device *device;
> +
> +	device = container_of(dev, struct vchiq_device, dev);
> +	kfree(device);
> +}
> +
> +int vchiq_device_register(struct device *parent, const char *name)
> +{
> +	struct vchiq_device *device = NULL;
> +	int ret;
> +
> +	device = kzalloc(sizeof(*device), GFP_KERNEL);
> +	if (!device)
> +		return -ENOMEM;
> +
> +	device->name = name;
> +	device->dev.init_name = name;
> +	device->dev.parent = parent;
> +	device->dev.bus = &vchiq_bus_type;
> +	device->dev.type = &vchiq_device_type;
> +	device->dev.release = vchiq_device_release;
> +
> +	ret = device_register(&device->dev);
> +	if (ret) {
> +		put_device(&device->dev);
> +		return -EINVAL;
Why not returning "ret" here?
> +	}
> +
> +	return 0;
> +}
> +
> +int vchiq_device_unregister(struct device *dev, void *data)
> +{
> +	device_unregister(dev);
> +	return 0;
> +}
> +
> +int vchiq_driver_register(struct vchiq_driver *vchiq_drv)
> +{
> +	vchiq_drv->driver.bus = &vchiq_bus_type;
> +
> +	return driver_register(&vchiq_drv->driver);
> +}
> +EXPORT_SYMBOL_GPL(vchiq_driver_register);
> +
> +void vchiq_driver_unregister(struct vchiq_driver *vchiq_drv)
> +{
> +	driver_unregister(&vchiq_drv->driver);
> +}
> +EXPORT_SYMBOL_GPL(vchiq_driver_unregister);
> diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_device.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_device.h
> new file mode 100644
> index 000000000000..0848c1b353f8
> --- /dev/null
> +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_device.h
> @@ -0,0 +1,39 @@
> +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
> +/*
> + * Copyright (c) 2023 Ideas On Board Oy
> + */
> +
> +#ifndef _VCHIQ_DEVICE_H
> +#define _VCHIQ_DEVICE_H
> +
> +#include <linux/device.h>
> +
> +struct vchiq_device {
> +	struct device dev;
> +	const char *name;
> +};
> +
> +struct vchiq_driver {
> +	struct device_driver driver;
> +};
> +
> +extern struct bus_type vchiq_bus_type;
> +
> +int vchiq_device_register(struct device *parent, const char *name);
> +int vchiq_device_unregister(struct device *dev, void *data);
> +
> +int vchiq_driver_register(struct vchiq_driver *vchiq_drv);
> +void vchiq_driver_unregister(struct vchiq_driver *vchiq_drv);
> +
> +/**
> + * module_vchiq_driver() - Helper macro for registering a vchiq driver
> + * @__vchiq_driver: vchiq driver struct
> + *
> + * Helper macro for vchiq drivers which do not do anything special in
> + * module init/exit. This eliminates a lot of boilerplate. Each module may only
> + * use this macro once, and calling it replaces module_init() and module_exit()
> + */
> +#define module_vchiq_driver(__vchiq_driver) \
> +	module_driver(__vchiq_driver, vchiq_driver_register, vchiq_driver_unregister)
> +
> +#endif /* _VCHIQ_DEVICE_H */



More information about the linux-arm-kernel mailing list