Re: [RFC PATCH] Add bridge driver to connect sensors to CIO2 device via software nodes on ACPI platforms

From: Greg KH
Date: Thu Sep 17 2020 - 03:54:59 EST


On Wed, Sep 16, 2020 at 10:36:18PM +0100, Daniel Scally wrote:
> MAINTAINERS | 6 +
> drivers/media/pci/intel/ipu3/ipu3-cio2.c | 67 +++-

staging drivers should be self-contained, and not modify stuff outside
of drivers/staging/

> drivers/staging/media/ipu3/Kconfig | 15 +
> drivers/staging/media/ipu3/Makefile | 1 +
> drivers/staging/media/ipu3/cio2-bridge.c | 448 +++++++++++++++++++++++

Why does this have to be in drivers/staging/ at all? Why not spend the
time to fix it up properly and get it merged correctly? It's a very
small driver, and should be smaller, so it should not be a lot of work
to do. And it would be faster to do that than to take it through
staging...

> 5 files changed, 534 insertions(+), 3 deletions(-)
> create mode 100644 drivers/staging/media/ipu3/cio2-bridge.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index b5cfab015bd6..55b0b9888bc0 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -9152,6 +9152,12 @@ S: Maintained
> W: http://www.adaptec.com/
> F: drivers/scsi/ips*
>
> +IPU3 CIO2 Bridge Driver
> +M: Daniel Scally <djrscally@xxxxxxxxx>
> +L: linux-media@xxxxxxxxxxxxxxx
> +S: Maintained
> +F: drivers/staging/media/ipu3/cio2-bridge.c
> +
> IPVS
> M: Wensong Zhang <wensong@xxxxxxxxxxxx>
> M: Simon Horman <horms@xxxxxxxxxxxx>
> diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
> index 92f5eadf2c99..fd941d2c7581 100644
> --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c
> +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
> @@ -1719,6 +1719,59 @@ static void cio2_queues_exit(struct cio2_device *cio2)
> cio2_queue_exit(cio2, &cio2->queue[i]);
> }
>
> +static int cio2_probe_can_progress(struct pci_dev *pci_dev)
> +{
> + void *sensor;

This is a huge flag that something is wrong, why void?

> +
> + /*
> + * On ACPI platforms, we need to probe _after_ sensors wishing to connect
> + * to cio2 have added a device link. If there are no consumers yet, then
> + * we need to defer. The .sync_state() callback will then be called after
> + * all linked sensors have probed
> + */
> +
> + if (IS_ENABLED(CONFIG_ACPI)) {
> + sensor = (struct device *)list_first_entry_or_null(

And you cast it??? Not right at all.

> + &pci_dev->dev.links.consumers,
> + struct dev_links_info,
> + consumers);

How do you "know" this is the first link? This feels really really
wrong and very fragile.

> +
> + if (!sensor)
> + return -EPROBE_DEFER;

So any random value will work? I doubt it :)

> + }
> +
> + return 0;
> +}
> +
> +void cio2_sync_state(struct device *dev)
> +{
> + struct cio2_device *cio2;
> + int ret = 0;
> +
> + if (IS_ENABLED(CONFIG_ACPI)) {
> + cio2 = dev_get_drvdata(dev);
> +
> + if (!cio2) {
> + dev_err(dev, "Failed to retrieve driver data\n");

How can this fail?

> + return;

No error value?

> + }
> +
> + /* insert the bridge driver to connect sensors via software nodes */
> + ret = request_module("cio2-bridge");
> +
> + if (ret)
> + dev_err(dev, "Failed to insert cio2-bridge\n");

Yet you keep on in the function???

> +
> + ret = cio2_parse_firmware(cio2);
> +
> + if (ret) {
> + v4l2_async_notifier_unregister(&cio2->notifier);
> + v4l2_async_notifier_cleanup(&cio2->notifier);
> + cio2_queues_exit(cio2);

But you clean up after this error?

> + }
> + }

And again, do not tell anyone?

Feels really wrong...

> +}
> +
> /**************** PCI interface ****************/
>
> static int cio2_pci_config_setup(struct pci_dev *dev)
> @@ -1746,6 +1799,11 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
> void __iomem *const *iomap;
> int r;
>
> + r = cio2_probe_can_progress(pci_dev);
> +
> + if (r)
> + return -EPROBE_DEFER;
> +
> cio2 = devm_kzalloc(&pci_dev->dev, sizeof(*cio2), GFP_KERNEL);
> if (!cio2)
> return -ENOMEM;
> @@ -1821,9 +1879,11 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
> v4l2_async_notifier_init(&cio2->notifier);
>
> /* Register notifier for subdevices we care */
> - r = cio2_parse_firmware(cio2);
> - if (r)
> - goto fail_clean_notifier;
> + if (!IS_ENABLED(CONFIG_ACPI)) {
> + r = cio2_parse_firmware(cio2);
> + if (r)
> + goto fail_clean_notifier;
> + }
>
> r = devm_request_irq(&pci_dev->dev, pci_dev->irq, cio2_irq,
> IRQF_SHARED, CIO2_NAME, cio2);
> @@ -2052,6 +2112,7 @@ static struct pci_driver cio2_pci_driver = {
> .remove = cio2_pci_remove,
> .driver = {
> .pm = &cio2_pm_ops,
> + .sync_state = cio2_sync_state
> },
> };
>
> diff --git a/drivers/staging/media/ipu3/Kconfig b/drivers/staging/media/ipu3/Kconfig
> index 3e9640523e50..08842fd8c0da 100644
> --- a/drivers/staging/media/ipu3/Kconfig
> +++ b/drivers/staging/media/ipu3/Kconfig
> @@ -14,3 +14,18 @@ config VIDEO_IPU3_IMGU
>
> Say Y or M here if you have a Skylake/Kaby Lake SoC with a MIPI
> camera. The module will be called ipu3-imgu.
> +
> +config VIDEO_CIO2_BRIDGE
> + tristate "IPU3 CIO2 Sensor Bridge Driver"
> + depends on PCI && VIDEO_V4L2
> + depends on ACPI
> + depends on X86

Why x86?

Why not CONFIG_TEST?



> + help
> + This module provides a bridge connecting sensors (I.E. cameras) to
> + the CIO2 device infrastructure via software nodes built from information
> + parsed from the SSDB buffer.
> +
> + Say Y or M here if your platform's cameras use IPU3 with connections
> + that should be defined in ACPI. The module will be called cio2-bridge.
> +
> + If in doubt, say N here.
> \ No newline at end of file
> diff --git a/drivers/staging/media/ipu3/Makefile b/drivers/staging/media/ipu3/Makefile
> index 9def80ef28f3..12dff56dbb9e 100644
> --- a/drivers/staging/media/ipu3/Makefile
> +++ b/drivers/staging/media/ipu3/Makefile
> @@ -10,3 +10,4 @@ ipu3-imgu-objs += \
> ipu3-css.o ipu3-v4l2.o ipu3.o
>
> obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3-imgu.o
> +obj-$(CONFIG_VIDEO_CIO2_BRIDGE) += cio2-bridge.o
> \ No newline at end of file
> diff --git a/drivers/staging/media/ipu3/cio2-bridge.c b/drivers/staging/media/ipu3/cio2-bridge.c
> new file mode 100644
> index 000000000000..5115aeeb35a1
> --- /dev/null
> +++ b/drivers/staging/media/ipu3/cio2-bridge.c
> @@ -0,0 +1,448 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <linux/acpi.h>
> +#include <acpi/acpi_bus.h>
> +#include <linux/device.h>
> +#include <linux/i2c.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <media/v4l2-subdev.h>
> +
> +#include <linux/fwnode.h>
> +#include <linux/kref.h>

Why kref.h?



> +
> +static void cio2_bridge_exit(void);
> +static int cio2_bridge_init(void);
> +
> +#define MAX_CONNECTED_DEVICES 4
> +#define SWNODE_SENSOR_HID 0
> +#define SWNODE_SENSOR_PORT 1
> +#define SWNODE_SENSOR_ENDPOINT 2
> +#define SWNODE_CIO2_PORT 3
> +#define SWNODE_CIO2_ENDPOINT 4
> +#define SWNODE_NULL_TERMINATOR 5
> +
> +#define CIO2_HID "INT343E"
> +#define CIO2_PCI_ID 0x9d32
> +
> +#define ENDPOINT_SENSOR 0
> +#define ENDPOINT_CIO2 1
> +
> +#define NODE_HID(_HID) \
> +((const struct software_node) { \
> + _HID, \
> +})
> +
> +#define NODE_PORT(_PORT, _HID_NODE) \
> +((const struct software_node) { \
> + _PORT, \
> + _HID_NODE, \
> +})
> +
> +#define NODE_ENDPOINT(_EP, _PORT, _PROPS) \
> +((const struct software_node) { \
> + _EP, \
> + _PORT, \
> + _PROPS, \
> +})
> +
> +#define PROPERTY_ENTRY_NULL \
> +((const struct property_entry) { })
> +#define SOFTWARE_NODE_NULL \
> +((const struct software_node) { })
> +
> +/*
> + * Extend this array with ACPI Hardware ID's of devices known to be
> + * working
> + */
> +
> +static char *supported_devices[] = {
> + "INT33BE",
> + "OVTI2680",
> + "OVTI5648",
> +};
> +
> +/*
> + * software_node needs const char * names. Can't snprintf a const char *,
> + * so instead we need an array of them and use the port num from SSDB as
> + * an index.
> + */
> +
> +const char *port_names[] = {
> + "port0", "port1", "port2", "port3", "port4",
> + "port5", "port6", "port7", "port8", "port9"
> +};
> +
> +struct software_node cio2_hid_node = { CIO2_HID, };
> +
> +struct sensor {
> + struct device *dev;
> + struct software_node swnodes[5];
> + struct property_entry sensor_props[6];
> + struct property_entry cio2_props[3];
> + struct fwnode_handle *fwnode;
> +};
> +
> +struct cio2_bridge {
> + int n_sensors;
> + struct sensor sensors[MAX_CONNECTED_DEVICES];
> + struct pci_dev *cio2;
> + struct fwnode_handle *cio2_fwnode;
> +};
> +
> +struct cio2_bridge bridge = { 0, };
> +
> +static const struct property_entry remote_endpoints[] = {
> + PROPERTY_ENTRY_REF("remote-endpoint", /* Sensor 0, Sensor Property */
> + &bridge.sensors[0].swnodes[SWNODE_CIO2_ENDPOINT]),
> + PROPERTY_ENTRY_REF("remote-endpoint", /* Sensor 0, CIO2 Property */
> + &bridge.sensors[0].swnodes[SWNODE_SENSOR_ENDPOINT]),
> + PROPERTY_ENTRY_REF("remote-endpoint",
> + &bridge.sensors[1].swnodes[SWNODE_CIO2_ENDPOINT]),
> + PROPERTY_ENTRY_REF("remote-endpoint",
> + &bridge.sensors[1].swnodes[SWNODE_SENSOR_ENDPOINT]),
> + PROPERTY_ENTRY_REF("remote-endpoint",
> + &bridge.sensors[2].swnodes[SWNODE_CIO2_ENDPOINT]),
> + PROPERTY_ENTRY_REF("remote-endpoint",
> + &bridge.sensors[2].swnodes[SWNODE_SENSOR_ENDPOINT]),
> + PROPERTY_ENTRY_REF("remote-endpoint",
> + &bridge.sensors[3].swnodes[SWNODE_CIO2_ENDPOINT]),
> + PROPERTY_ENTRY_REF("remote-endpoint",
> + &bridge.sensors[3].swnodes[SWNODE_SENSOR_ENDPOINT]),
> + { }
> +};
> +
> +/* Data representation as it is in ACPI SSDB buffer */
> +struct sensor_bios_data_packed {
> + u8 version;
> + u8 sku;
> + u8 guid_csi2[16];
> + u8 devfunction;
> + u8 bus;
> + u32 dphylinkenfuses;
> + u32 clockdiv;
> + u8 link;
> + u8 lanes;
> + u32 csiparams[10];
> + u32 maxlanespeed;
> + u8 sensorcalibfileidx;
> + u8 sensorcalibfileidxInMBZ[3];
> + u8 romtype;
> + u8 vcmtype;
> + u8 platforminfo;
> + u8 platformsubinfo;
> + u8 flash;
> + u8 privacyled;
> + u8 degree;
> + u8 mipilinkdefined;
> + u32 mclkspeed;
> + u8 controllogicid;
> + u8 reserved1[3];
> + u8 mclkport;
> + u8 reserved2[13];
> +} __attribute__((__packed__));

Endian issues???

This doesn't look "packed" to me, did you check it?

I've stopped here, sorry, ran out of time...

greg k-h