diff mbox series

[RFC,v3,9/9] ipu3-cio2: Add functionality allowing software_node connections to sensors on platforms designed for Windows

Message ID 20201019225903.14276-10-djrscally@gmail.com (mailing list archive)
State New, archived
Headers show
Series Add functionality to ipu3-cio2 driver allowing software_node connections to sensors on platforms designed for Windows | expand

Commit Message

Daniel Scally Oct. 19, 2020, 10:59 p.m. UTC
Currently on platforms designed for Windows, connections between CIO2 and
sensors are not properly defined in DSDT. This patch extends the ipu3-cio2
driver to compensate by building software_node connections, parsing the
connection properties from the sensor's SSDB buffer.

Suggested-by: Jordan Hand <jorhand@linux.microsoft.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
---
Changes in v3:
	- Rather than overwriting the device's primary fwnode, we now
	simply assign a secondary. Some of the preceding patches alter the
	existing driver code and v4l2 framework to allow for that.
	- Rather than reprobe() the sensor after connecting the devices in
	cio2-bridge we create the software_nodes right away. In this case,
	sensor drivers will have to defer probing until they detect that a
	fwnode graph is connecting them to the CIO2 device.
	- Error paths in connect_supported_devices() moved outside the
	loop
	- Replaced pr_*() with dev_*() throughout
	- Moved creation of software_node / property_entry arrays to stack
	- A lot of formatting changes.

 MAINTAINERS                                   |   1 +
 drivers/media/pci/intel/ipu3/Kconfig          |  18 +
 drivers/media/pci/intel/ipu3/Makefile         |   3 +-
 drivers/media/pci/intel/ipu3/cio2-bridge.c    | 327 ++++++++++++++++++
 drivers/media/pci/intel/ipu3/cio2-bridge.h    |  94 +++++
 drivers/media/pci/intel/ipu3/ipu3-cio2-main.c |  21 ++
 drivers/media/pci/intel/ipu3/ipu3-cio2.h      |   9 +
 7 files changed, 472 insertions(+), 1 deletion(-)
 create mode 100644 drivers/media/pci/intel/ipu3/cio2-bridge.c
 create mode 100644 drivers/media/pci/intel/ipu3/cio2-bridge.h

Comments

Andy Shevchenko Oct. 20, 2020, 9:41 a.m. UTC | #1
On Mon, Oct 19, 2020 at 11:59:03PM +0100, Daniel Scally wrote:
> Currently on platforms designed for Windows, connections between CIO2 and
> sensors are not properly defined in DSDT. This patch extends the ipu3-cio2
> driver to compensate by building software_node connections, parsing the
> connection properties from the sensor's SSDB buffer.

...

> +	  	- Some Microsoft Surface models

Perhaps an example? Like '(e.g. Surface Book)'

> +		- The Lenovo Miix line

Ditto.

> +		- Dell 7285

...

> +#include <linux/acpi.h>
> +#include <linux/device.h>

> +#include <linux/fwnode.h>

This is implied by property.h, no need to have an explicit inclusion.

> +#include <linux/i2c.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/property.h>
> +#include <media/v4l2-subdev.h>

...

> +static const char * const port_names[] = {
> +	"port0", "port1", "port2", "port3"

+ comma.

> +};

...

> +static int read_acpi_block(struct device *dev, char *id, void *data, u32 size)
> +{
> +	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
> +	struct acpi_handle *handle;
> +	union acpi_object *obj;
> +	acpi_status status;
> +	int ret;
> +
> +	handle = ACPI_HANDLE(dev);
> +
> +	status = acpi_evaluate_object(handle, id, NULL, &buffer);
> +	if (ACPI_FAILURE(status))
> +		return -ENODEV;
> +
> +	obj = buffer.pointer;
> +	if (!obj) {
> +		dev_err(dev, "Couldn't locate ACPI buffer\n");
> +		return -ENODEV;
> +	}
> +
> +	if (obj->type != ACPI_TYPE_BUFFER) {
> +		dev_err(dev, "Couldn't read ACPI buffer\n");

"not an ACPI buffer"

> +		ret = -ENODEV;

> +		goto out_free_buff;
> +	}
> +
> +	if (obj->buffer.length > size) {
> +		dev_err(dev, "Given buffer is too small\n");

> +		ret = -ENODEV;

-EINVAL?

> +		goto out_free_buff;
> +	}
> +
> +	memcpy(data, obj->buffer.pointer, obj->buffer.length);
> +	ret = obj->buffer.length;
> +
> +out_free_buff:
> +	kfree(buffer.pointer);
> +	return ret;
> +}

...

> +	/* device fwnode properties */
> +	memset(dev_properties, 0, sizeof(struct property_entry) * 3);

3 -> sizeof(...) ?
Same Q to other similar cases.

...

> +	sensor->data_lanes = kmalloc_array(ssdb->lanes, sizeof(u32),
> +					   GFP_KERNEL);

Perhaps one line?

> +

Redundant blank line.

> +	if (!sensor->data_lanes)
> +		return -ENOMEM;

...

> +static int connect_supported_devices(struct pci_dev *cio2)
> +{
> +	struct sensor_bios_data ssdb;
> +	struct fwnode_handle *fwnode;
> +	struct acpi_device *adev;
> +	struct sensor *sensor;
> +	struct device *dev;
> +	int i, ret;
> +
> +	ret = 0;
> +	for (i = 0; i < ARRAY_SIZE(supported_devices); i++) {
> +		adev = acpi_dev_get_first_match_dev(supported_devices[i], NULL, -1);
> +		if (!adev)
> +			continue;
> +
> +		dev = bus_find_device_by_acpi_dev(&i2c_bus_type, adev);
> +		if (!dev) {
> +			ret = -EPROBE_DEFER;
> +			goto err_rollback;
> +		}
> +
> +		sensor = &bridge.sensors[bridge.n_sensors];
> +		sensor->dev = dev;
> +		sensor->adev = adev;

> +		snprintf(sensor->name, ACPI_ID_LEN, "%s",
> +			 supported_devices[i]);

One line?

> +		ret = get_acpi_ssdb_sensor_data(dev, &ssdb);
> +		if (ret)
> +			goto err_free_dev;
> +
> +		ret = create_fwnode_properties(sensor, &ssdb);
> +		if (ret)
> +			goto err_free_dev;
> +
> +		ret = create_connection_swnodes(sensor, &ssdb);
> +		if (ret)
> +			goto err_free_dev;
> +
> +		ret = software_node_register_nodes(sensor->swnodes);
> +		if (ret)
> +			goto err_free_dev;
> +
> +		fwnode = software_node_fwnode(&sensor->swnodes[SWNODE_SENSOR_HID]);
> +		if (!fwnode) {
> +			ret = -ENODEV;
> +			goto err_free_swnodes;
> +		}
> +
> +		set_secondary_fwnode(dev, fwnode);

> +		dev_info(&cio2->dev, "Found supported device %s\n",
> +			 supported_devices[i]);

One line?

(In both cases you can use temporary variable to keep supported_devices[i])

> +		bridge.n_sensors++;
> +		continue;
> +	}
> +
> +	return ret;
> +
> +err_free_swnodes:
> +	software_node_unregister_nodes_reverse(sensor->swnodes);
> +err_free_dev:
> +	put_device(dev);
> +err_rollback:
> +	acpi_dev_put(adev);
> +
> +	/*
> +	 * If an iteration of this loop results in -EPROBE_DEFER then
> +	 * we need to roll back any sensors that were successfully
> +	 * registered. Any other error and we'll skip that step, as
> +	 * it seems better to have one successfully connected sensor.
> +	 */
> +
> +	if (ret == -EPROBE_DEFER)
> +		cio2_bridge_unregister_sensors();
> +
> +	return ret;
> +}
> +
> +int cio2_bridge_build(struct pci_dev *cio2)
> +{

	struct device *dev = &cio2->dev;

will help to clean the code below.

> +	struct fwnode_handle *fwnode;
> +	int ret;
> +
> +	pci_dev_get(cio2);
> +
> +	ret = software_node_register(&cio2_hid_node);
> +	if (ret < 0) {
> +		dev_err(&cio2->dev, "Failed to register the CIO2 HID node\n");
> +		goto err_put_cio2;
> +	}
> +
> +	ret = connect_supported_devices(cio2);
> +	if (ret == -EPROBE_DEFER)
> +		goto err_unregister_cio2;
> +
> +	if (bridge.n_sensors == 0) {
> +		ret = -EPROBE_DEFER;
> +		goto err_unregister_cio2;
> +	}
> +
> +	dev_info(&cio2->dev, "Connected %d cameras\n", bridge.n_sensors);
> +
> +	fwnode = software_node_fwnode(&cio2_hid_node);
> +	if (!fwnode) {

> +		dev_err(&cio2->dev,
> +			"Error getting fwnode from cio2 software_node\n");

One line (after above change)

> +		ret = -ENODEV;
> +		goto err_unregister_sensors;
> +	}
> +
> +	set_secondary_fwnode(&cio2->dev, fwnode);
> +
> +	return 0;
> +
> +err_unregister_sensors:
> +	cio2_bridge_unregister_sensors();
> +err_unregister_cio2:
> +	software_node_unregister(&cio2_hid_node);
> +err_put_cio2:
> +	pci_dev_put(cio2);
> +
> +	return ret;
> +}
> +
> +void cio2_bridge_burn(struct pci_dev *cio2)
> +{
> +	pci_dev_put(cio2);
> +
> +	cio2_bridge_unregister_sensors();
> +
> +	software_node_unregister(&cio2_hid_node);
> +}

I would rather name them like
build -> init / register / ...
burn -> clean / unregister / ...

...

> +#ifndef __CIO2_BRIDGE_H
> +#define __CIO2_BRIDGE_H

Missed inclusion that defines struct software_nodes type.

And so on. This file is consumer of some types and you need either to include
corresponding headers, or provide a forward declarations (for example, no need
to include device.h or acpi.h AFAICS).

> +#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_SENSOR(_HID, _PROPS)		\
> +	((const struct software_node) {		\
> +		.name = _HID,			\
> +		.properties = _PROPS,		\
> +	})
> +
> +#define NODE_PORT(_PORT, _SENSOR_NODE)		\
> +	((const struct software_node) {		\
> +		_PORT,				\
> +		_SENSOR_NODE,			\
> +	})
> +
> +#define NODE_ENDPOINT(_EP, _PORT, _PROPS)	\
> +	((const struct software_node) {		\
> +		_EP,				\
> +		_PORT,				\
> +		_PROPS,				\
> +	})
> +
> +struct sensor {
> +	char name[ACPI_ID_LEN];
> +	struct device *dev;
> +	struct acpi_device *adev;
> +	struct software_node swnodes[6];
> +	struct property_entry dev_properties[3];
> +	struct property_entry ep_properties[4];
> +	struct property_entry cio2_properties[3];
> +	u32 *data_lanes;
> +};
> +
> +struct cio2_bridge {
> +	int n_sensors;
> +	struct sensor sensors[MAX_CONNECTED_DEVICES];
> +};
> +
> +/* 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];
> +} __packed__;
> +
> +/* Fields needed by bridge driver */
> +struct sensor_bios_data {
> +	struct device *dev;
> +	u8 link;
> +	u8 lanes;
> +	u8 degree;
> +	u32 mclkspeed;
> +};

...

> +	/*
> +	 * On some platforms no connections to sensors are defined in firmware,
> +	 * if the device has no endpoints then we can try to build those as
> +	 * software_nodes parsed from SSDB.
> +	 *
> +	 * This may EPROBE_DEFER if supported devices are found defined in ACPI
> +	 * but not yet ready for use (either not attached to the i2c bus yet,
> +	 * or not done probing themselves).
> +	 */
> +
> +	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&pci_dev->dev), NULL);
> +	if (!endpoint) {
> +		r = cio2_bridge_build(pci_dev);
> +		if (r)
> +			return r;
> +	}
> +
>  	cio2 = devm_kzalloc(&pci_dev->dev, sizeof(*cio2), GFP_KERNEL);
>  	if (!cio2)
>  		return -ENOMEM;
> @@ -1825,6 +1843,9 @@ static void cio2_pci_remove(struct pci_dev *pci_dev)
>  {
>  	struct cio2_device *cio2 = pci_get_drvdata(pci_dev);

> +	if (is_software_node(dev_fwnode(&pci_dev->dev)))

Can we use the same check as for _build call above?

> +		cio2_bridge_burn(pci_dev);
Daniel Scally Oct. 21, 2020, 10:05 p.m. UTC | #2
Hi Andy

On 20/10/2020 10:41, Andy Shevchenko wrote:
>> +	  	- Some Microsoft Surface models
> Perhaps an example? Like '(e.g. Surface Book)'
>
>> +		- The Lenovo Miix line
> Ditto.
Sure - I'll list them.
>> +static const char * const port_names[] = {
>> +	"port0", "port1", "port2", "port3"
> + comma.
I think 4 ports is the maximum for CIO2 device, so this shouldn't ever
get extended?
>> +	/* device fwnode properties */
>> +	memset(dev_properties, 0, sizeof(struct property_entry) * 3);
> 3 -> sizeof(...) ?
> Same Q to other similar cases.
Whoops, of course, that was stupid in hindsight!
>> +	if (is_software_node(dev_fwnode(&pci_dev->dev)))
> Can we use the same check as for _build call above?

And just set a flag in struct cio2? sure.


For all the other comments; ack - I'll make those changes for the next
version
Andy Shevchenko Oct. 22, 2020, 1:40 p.m. UTC | #3
On Thu, Oct 22, 2020 at 3:59 PM Daniel Scally <djrscally@gmail.com> wrote:
> On 20/10/2020 10:41, Andy Shevchenko wrote:

...

> >> +static const char * const port_names[] = {
> >> +    "port0", "port1", "port2", "port3"
> > + comma.
> I think 4 ports is the maximum for CIO2 device, so this shouldn't ever
> get extended?

It's better for at least teaching purposes (if anybody takes this
driver as an example for anything) if you have really believe that no
new generation will have more than that.

...

> >> +    if (is_software_node(dev_fwnode(&pci_dev->dev)))
> > Can we use the same check as for _build call above?
>
> And just set a flag in struct cio2? sure.

I meant can we use exact conditional w/o any additional flags added?
Daniel Scally Oct. 23, 2020, 10:06 a.m. UTC | #4
On 22/10/2020 14:40, Andy Shevchenko wrote:
> On Thu, Oct 22, 2020 at 3:59 PM Daniel Scally <djrscally@gmail.com> wrote:
>> On 20/10/2020 10:41, Andy Shevchenko wrote:
> ...
>
>>>> +static const char * const port_names[] = {
>>>> +    "port0", "port1", "port2", "port3"
>>> + comma.
>> I think 4 ports is the maximum for CIO2 device, so this shouldn't ever
>> get extended?
> It's better for at least teaching purposes (if anybody takes this
> driver as an example for anything) if you have really believe that no
> new generation will have more than that.

Yeah fair point - it's added


>>>> +    if (is_software_node(dev_fwnode(&pci_dev->dev)))
>>> Can we use the same check as for _build call above?
>> And just set a flag in struct cio2? sure.
> I meant can we use exact conditional w/o any additional flags added?
Oh I see. Erm...I can't think of a way to do that immediately; checking
for the fwnode graph's presence will of course pass if it's defined in
ACPI too. Let me think about it
Laurent Pinchart Oct. 24, 2020, 1:24 a.m. UTC | #5
Hi Daniel,

Thank you for the patch.

On Mon, Oct 19, 2020 at 11:59:03PM +0100, Daniel Scally wrote:
> Currently on platforms designed for Windows, connections between CIO2 and
> sensors are not properly defined in DSDT. This patch extends the ipu3-cio2
> driver to compensate by building software_node connections, parsing the
> connection properties from the sensor's SSDB buffer.
> 
> Suggested-by: Jordan Hand <jorhand@linux.microsoft.com>
> Signed-off-by: Daniel Scally <djrscally@gmail.com>
> ---
> Changes in v3:
> 	- Rather than overwriting the device's primary fwnode, we now
> 	simply assign a secondary. Some of the preceding patches alter the
> 	existing driver code and v4l2 framework to allow for that.
> 	- Rather than reprobe() the sensor after connecting the devices in
> 	cio2-bridge we create the software_nodes right away. In this case,
> 	sensor drivers will have to defer probing until they detect that a
> 	fwnode graph is connecting them to the CIO2 device.
> 	- Error paths in connect_supported_devices() moved outside the
> 	loop
> 	- Replaced pr_*() with dev_*() throughout
> 	- Moved creation of software_node / property_entry arrays to stack
> 	- A lot of formatting changes.
> 
>  MAINTAINERS                                   |   1 +
>  drivers/media/pci/intel/ipu3/Kconfig          |  18 +
>  drivers/media/pci/intel/ipu3/Makefile         |   3 +-
>  drivers/media/pci/intel/ipu3/cio2-bridge.c    | 327 ++++++++++++++++++
>  drivers/media/pci/intel/ipu3/cio2-bridge.h    |  94 +++++
>  drivers/media/pci/intel/ipu3/ipu3-cio2-main.c |  21 ++
>  drivers/media/pci/intel/ipu3/ipu3-cio2.h      |   9 +
>  7 files changed, 472 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/media/pci/intel/ipu3/cio2-bridge.c
>  create mode 100644 drivers/media/pci/intel/ipu3/cio2-bridge.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 5d768d5ad..4c9c646c7 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -8848,6 +8848,7 @@ INTEL IPU3 CSI-2 CIO2 DRIVER
>  M:	Yong Zhi <yong.zhi@intel.com>
>  M:	Sakari Ailus <sakari.ailus@linux.intel.com>
>  M:	Bingbu Cao <bingbu.cao@intel.com>
> +M:	Dan Scally <djrscally@gmail.com>
>  R:	Tianshu Qiu <tian.shu.qiu@intel.com>
>  L:	linux-media@vger.kernel.org
>  S:	Maintained
> diff --git a/drivers/media/pci/intel/ipu3/Kconfig b/drivers/media/pci/intel/ipu3/Kconfig
> index 82d7f17e6..d14cbceae 100644
> --- a/drivers/media/pci/intel/ipu3/Kconfig
> +++ b/drivers/media/pci/intel/ipu3/Kconfig
> @@ -16,3 +16,21 @@ config VIDEO_IPU3_CIO2
>  	  Say Y or M here if you have a Skylake/Kaby Lake SoC with MIPI CSI-2
>  	  connected camera.
>  	  The module will be called ipu3-cio2.
> +
> +config CIO2_BRIDGE
> +	bool "IPU3 CIO2 Sensors Bridge"
> +	depends on VIDEO_IPU3_CIO2
> +	help
> +	  This extension provides an API for the ipu3-cio2 driver to create
> +	  connections to cameras that are hidden in SSDB buffer in ACPI. It
> +	  can be used to enable support for cameras in detachable / hybrid
> +	  devices that ship with Windows.
> +
> +	  Say Y here if your device is a detachable / hybrid laptop that comes
> +	  with Windows installed by the OEM, for example:
> +
> +	  	- Some Microsoft Surface models
> +		- The Lenovo Miix line
> +		- Dell 7285
> +
> +	  If in doubt, say N here.
> diff --git a/drivers/media/pci/intel/ipu3/Makefile b/drivers/media/pci/intel/ipu3/Makefile
> index b4e3266d9..933777e6e 100644
> --- a/drivers/media/pci/intel/ipu3/Makefile
> +++ b/drivers/media/pci/intel/ipu3/Makefile
> @@ -1,4 +1,5 @@
>  # SPDX-License-Identifier: GPL-2.0-only
>  obj-$(CONFIG_VIDEO_IPU3_CIO2) += ipu3-cio2.o
>  
> -ipu3-cio2-y += ipu3-cio2-main.o
> \ No newline at end of file
> +ipu3-cio2-y += ipu3-cio2-main.o
> +ipu3-cio2-$(CONFIG_CIO2_BRIDGE) += cio2-bridge.o
> diff --git a/drivers/media/pci/intel/ipu3/cio2-bridge.c b/drivers/media/pci/intel/ipu3/cio2-bridge.c
> new file mode 100644
> index 000000000..bbe072f04
> --- /dev/null
> +++ b/drivers/media/pci/intel/ipu3/cio2-bridge.c
> @@ -0,0 +1,327 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Author: Dan Scally <djrscally@gmail.com>
> +#include <linux/acpi.h>
> +#include <linux/device.h>
> +#include <linux/fwnode.h>
> +#include <linux/i2c.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/property.h>
> +#include <media/v4l2-subdev.h>
> +
> +#include "cio2-bridge.h"
> +
> +/*
> + * Extend this array with ACPI Hardware ID's of devices known to be
> + * working
> + */
> +static const char * const supported_devices[] = {
> +	"INT33BE",
> +	"OVTI2680",
> +};
> +
> +static struct software_node cio2_hid_node = { CIO2_HID };
> +
> +static struct cio2_bridge bridge;

While there shouldn't be more than one CIO2 instance, we try to develop
drivers in a way that avoids global per-device variables. Could all this
be allocated dynamically, with the pointer returned by
cio2_bridge_build() and stored in the cio2_device structure ?

> +
> +static const char * const port_names[] = {
> +	"port0", "port1", "port2", "port3"
> +};
> +
> +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]),
> +};

For the same reason, I would move this to the sensor structure (with two
property_entry per sensor). That will simplify the code below, avoiding
indexing this array with bridge.n_sensors * 2.

> +
> +static int read_acpi_block(struct device *dev, char *id, void *data, u32 size)

To avoid potential future namespace classes, I'd advise naming the
functions with a cio2_bridge_ prefix, even the static ones.

And maybe cio2_bridge_read_acpi_buffer(), as this function reads a
buffer ?

> +{
> +	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
> +	struct acpi_handle *handle;
> +	union acpi_object *obj;
> +	acpi_status status;
> +	int ret;
> +
> +	handle = ACPI_HANDLE(dev);
> +
> +	status = acpi_evaluate_object(handle, id, NULL, &buffer);
> +	if (ACPI_FAILURE(status))
> +		return -ENODEV;
> +
> +	obj = buffer.pointer;
> +	if (!obj) {
> +		dev_err(dev, "Couldn't locate ACPI buffer\n");
> +		return -ENODEV;
> +	}
> +
> +	if (obj->type != ACPI_TYPE_BUFFER) {
> +		dev_err(dev, "Couldn't read ACPI buffer\n");
> +		ret = -ENODEV;
> +		goto out_free_buff;
> +	}
> +
> +	if (obj->buffer.length > size) {
> +		dev_err(dev, "Given buffer is too small\n");
> +		ret = -ENODEV;
> +		goto out_free_buff;
> +	}
> +
> +	memcpy(data, obj->buffer.pointer, obj->buffer.length);
> +	ret = obj->buffer.length;
> +
> +out_free_buff:
> +	kfree(buffer.pointer);
> +	return ret;
> +}
> +
> +static int get_acpi_ssdb_sensor_data(struct device *dev,
> +				     struct sensor_bios_data *sensor)
> +{
> +	struct sensor_bios_data_packed sensor_data;
> +	int ret;
> +
> +	ret = read_acpi_block(dev, "SSDB", &sensor_data, sizeof(sensor_data));
> +	if (ret < 0)
> +		return ret;
> +
> +	sensor->link = sensor_data.link;
> +	sensor->lanes = sensor_data.lanes;
> +	sensor->mclkspeed = sensor_data.mclkspeed;
> +	sensor->degree = sensor_data.degree;

How about storing a sensor_bios_data_packed inside sensor_bios_data ?
That will avoid copying fields individually, with manual addition of
extra fields as they become useful. Usage of the sensor_bios_data
structure would turn from sensor->degree to sensor->ssdb.degree, which
is slightly longer, but I think more maintainable.

> +
> +	return 0;
> +}
> +
> +static int create_fwnode_properties(struct sensor *sensor,
> +				    struct sensor_bios_data *ssdb)
> +{
> +	struct property_entry *cio2_properties = sensor->cio2_properties;
> +	struct property_entry *dev_properties = sensor->dev_properties;
> +	struct property_entry *ep_properties = sensor->ep_properties;
> +	int i;

i never takes negative values, you can make it an unsigned int. Same for
other occurrences below.

> +
> +	/* device fwnode properties */
> +	memset(dev_properties, 0, sizeof(struct property_entry) * 3);

I would memset() bridge to 0 in one go and avoid individual memsets. And
if you allocate it with kzalloc() it will be initialized to 0.

> +
> +	dev_properties[0] = PROPERTY_ENTRY_U32("clock-frequency",
> +					       ssdb->mclkspeed);
> +	dev_properties[1] = PROPERTY_ENTRY_U8("rotation", ssdb->degree);
> +
> +	/* endpoint fwnode properties */
> +	memset(ep_properties, 0, sizeof(struct property_entry) * 4);
> +
> +	sensor->data_lanes = kmalloc_array(ssdb->lanes, sizeof(u32),
> +					   GFP_KERNEL);

Given that there can't be more than 4 data lanes, how about turning
data_lanes into an array with 4 entries, to avoid the dynamic allocation
? You will have to validate ssdb->lanes in connect_supported_devices(),
to make sure not to overflow the array. This and the next function can
then be turned into void functions.

> +
> +	if (!sensor->data_lanes)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < ssdb->lanes; i++)
> +		sensor->data_lanes[i] = i + 1;
> +
> +	ep_properties[0] = PROPERTY_ENTRY_U32("bus-type", 5);
> +	ep_properties[1] = PROPERTY_ENTRY_U32_ARRAY_LEN("data-lanes",
> +							sensor->data_lanes,
> +							ssdb->lanes);
> +	ep_properties[2] = remote_endpoints[(bridge.n_sensors * 2) + ENDPOINT_SENSOR];
> +
> +	/* cio2 endpoint props */
> +	memset(cio2_properties, 0, sizeof(struct property_entry) * 3);
> +
> +	cio2_properties[0] = PROPERTY_ENTRY_U32_ARRAY_LEN("data-lanes",
> +							  sensor->data_lanes,
> +							  ssdb->lanes);
> +	cio2_properties[1] = remote_endpoints[(bridge.n_sensors * 2) + ENDPOINT_CIO2];
> +
> +	return 0;
> +}
> +
> +static int create_connection_swnodes(struct sensor *sensor,
> +				     struct sensor_bios_data *ssdb)
> +{
> +	struct software_node *nodes = sensor->swnodes;
> +
> +	memset(nodes, 0, sizeof(struct software_node) * 6);
> +
> +	nodes[SWNODE_SENSOR_HID] = NODE_SENSOR(sensor->name,
> +					       sensor->dev_properties);
> +	nodes[SWNODE_SENSOR_PORT] = NODE_PORT("port0",
> +					      &nodes[SWNODE_SENSOR_HID]);
> +	nodes[SWNODE_SENSOR_ENDPOINT] = NODE_ENDPOINT("endpoint0",
> +						      &nodes[SWNODE_SENSOR_PORT],
> +						      sensor->ep_properties);
> +	nodes[SWNODE_CIO2_PORT] = NODE_PORT(port_names[ssdb->link],
> +					    &cio2_hid_node);
> +	nodes[SWNODE_CIO2_ENDPOINT] = NODE_ENDPOINT("endpoint0",
> +						    &nodes[SWNODE_CIO2_PORT],
> +						    sensor->cio2_properties);
> +
> +	return 0;
> +}
> +
> +static void cio2_bridge_unregister_sensors(void)
> +{
> +	struct sensor *sensor;
> +	int i;
> +
> +	for (i = 0; i < bridge.n_sensors; i++) {
> +		sensor = &bridge.sensors[i];
> +
> +		software_node_unregister_nodes_reverse(sensor->swnodes);
> +
> +		kfree(sensor->data_lanes);
> +
> +		put_device(sensor->dev);
> +		acpi_dev_put(sensor->adev);
> +	}
> +}
> +
> +static int connect_supported_devices(struct pci_dev *cio2)
> +{
> +	struct sensor_bios_data ssdb;
> +	struct fwnode_handle *fwnode;
> +	struct acpi_device *adev;
> +	struct sensor *sensor;
> +	struct device *dev;
> +	int i, ret;
> +
> +	ret = 0;

You can initialize ret to 0 when declaring the variable.

> +	for (i = 0; i < ARRAY_SIZE(supported_devices); i++) {
> +		adev = acpi_dev_get_first_match_dev(supported_devices[i], NULL, -1);

What if there are multiple sensor of the same model ?

> +		if (!adev)
> +			continue;
> +

Does acpi_dev_get_first_match_dev() skip disabled devices (as reported
by _STA) ?

> +		dev = bus_find_device_by_acpi_dev(&i2c_bus_type, adev);
> +		if (!dev) {
> +			ret = -EPROBE_DEFER;
> +			goto err_rollback;
> +		}
> +
> +		sensor = &bridge.sensors[bridge.n_sensors];
> +		sensor->dev = dev;
> +		sensor->adev = adev;
> +
> +		snprintf(sensor->name, ACPI_ID_LEN, "%s",
> +			 supported_devices[i]);

How about strlcpy() ?

> +
> +		ret = get_acpi_ssdb_sensor_data(dev, &ssdb);
> +		if (ret)
> +			goto err_free_dev;
> +
> +		ret = create_fwnode_properties(sensor, &ssdb);
> +		if (ret)
> +			goto err_free_dev;
> +
> +		ret = create_connection_swnodes(sensor, &ssdb);
> +		if (ret)
> +			goto err_free_dev;
> +
> +		ret = software_node_register_nodes(sensor->swnodes);
> +		if (ret)
> +			goto err_free_dev;
> +
> +		fwnode = software_node_fwnode(&sensor->swnodes[SWNODE_SENSOR_HID]);
> +		if (!fwnode) {
> +			ret = -ENODEV;
> +			goto err_free_swnodes;
> +		}
> +
> +		set_secondary_fwnode(dev, fwnode);

I wonder if we could avoid depending on the I2C device being created by
getting the fwnode from adev, and setting ->secondary manually. adev
would need to be passed to get_acpi_ssdb_sensor_data() instead of dev.

> +
> +		dev_info(&cio2->dev, "Found supported device %s\n",
> +			 supported_devices[i]);
> +
> +		bridge.n_sensors++;
> +		continue;
> +	}
> +
> +	return ret;
> +
> +err_free_swnodes:
> +	software_node_unregister_nodes_reverse(sensor->swnodes);
> +err_free_dev:
> +	put_device(dev);
> +err_rollback:
> +	acpi_dev_put(adev);

I think you'll leak sensor->data_lanes here. It won't be a problem if
you don't allocate it manually, as proposed above. I however wonder if
error handling couldn't be simplified by increasing bridge.n_sensors
earlier, and skipping cleanup in cio2_bridge_unregister_sensors() for
the fields that haven't been initialized (for instance kfree() is a
no-op on NULL pointers, so that's already handled).

> +
> +	/*
> +	 * If an iteration of this loop results in -EPROBE_DEFER then
> +	 * we need to roll back any sensors that were successfully
> +	 * registered. Any other error and we'll skip that step, as
> +	 * it seems better to have one successfully connected sensor.
> +	 */
> +
> +	if (ret == -EPROBE_DEFER)
> +		cio2_bridge_unregister_sensors();
> +
> +	return ret;
> +}
> +
> +int cio2_bridge_build(struct pci_dev *cio2)
> +{
> +	struct fwnode_handle *fwnode;
> +	int ret;
> +
> +	pci_dev_get(cio2);
> +
> +	ret = software_node_register(&cio2_hid_node);
> +	if (ret < 0) {
> +		dev_err(&cio2->dev, "Failed to register the CIO2 HID node\n");
> +		goto err_put_cio2;
> +	}
> +
> +	ret = connect_supported_devices(cio2);
> +	if (ret == -EPROBE_DEFER)
> +		goto err_unregister_cio2;
> +
> +	if (bridge.n_sensors == 0) {
> +		ret = -EPROBE_DEFER;
> +		goto err_unregister_cio2;
> +	}
> +
> +	dev_info(&cio2->dev, "Connected %d cameras\n", bridge.n_sensors);
> +
> +	fwnode = software_node_fwnode(&cio2_hid_node);
> +	if (!fwnode) {
> +		dev_err(&cio2->dev,
> +			"Error getting fwnode from cio2 software_node\n");
> +		ret = -ENODEV;
> +		goto err_unregister_sensors;
> +	}
> +
> +	set_secondary_fwnode(&cio2->dev, fwnode);
> +
> +	return 0;
> +
> +err_unregister_sensors:
> +	cio2_bridge_unregister_sensors();
> +err_unregister_cio2:
> +	software_node_unregister(&cio2_hid_node);
> +err_put_cio2:
> +	pci_dev_put(cio2);
> +
> +	return ret;
> +}
> +
> +void cio2_bridge_burn(struct pci_dev *cio2)

Interesting function name :-) I like the creativity, but I think
consistency with the rest of the kernel code should unfortunately be
favoured.

> +{
> +	pci_dev_put(cio2);
> +
> +	cio2_bridge_unregister_sensors();
> +
> +	software_node_unregister(&cio2_hid_node);
> +}
> diff --git a/drivers/media/pci/intel/ipu3/cio2-bridge.h b/drivers/media/pci/intel/ipu3/cio2-bridge.h
> new file mode 100644
> index 000000000..077354ca8
> --- /dev/null
> +++ b/drivers/media/pci/intel/ipu3/cio2-bridge.h
> @@ -0,0 +1,94 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Author: Dan Scally <djrscally@gmail.com> */
> +#ifndef __CIO2_BRIDGE_H
> +#define __CIO2_BRIDGE_H
> +
> +#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_SENSOR(_HID, _PROPS)		\
> +	((const struct software_node) {		\
> +		.name = _HID,			\
> +		.properties = _PROPS,		\
> +	})
> +
> +#define NODE_PORT(_PORT, _SENSOR_NODE)		\
> +	((const struct software_node) {		\
> +		_PORT,				\
> +		_SENSOR_NODE,			\
> +	})
> +
> +#define NODE_ENDPOINT(_EP, _PORT, _PROPS)	\
> +	((const struct software_node) {		\
> +		_EP,				\
> +		_PORT,				\
> +		_PROPS,				\
> +	})
> +
> +struct sensor {

That's a very common name, prone to namespace clashes. How about naming
it cio2_sensor ?

> +	char name[ACPI_ID_LEN];
> +	struct device *dev;
> +	struct acpi_device *adev;
> +	struct software_node swnodes[6];
> +	struct property_entry dev_properties[3];
> +	struct property_entry ep_properties[4];
> +	struct property_entry cio2_properties[3];
> +	u32 *data_lanes;
> +};
> +
> +struct cio2_bridge {
> +	int n_sensors;

This can never be negative, I would make it an unsigned int.

> +	struct sensor sensors[MAX_CONNECTED_DEVICES];
> +};
> +
> +/* Data representation as it is in ACPI SSDB buffer */
> +struct sensor_bios_data_packed {

Similarly as above, I'd use a cio2_ prefix, and I think you can drop the
_packed suffix. How about naming it cio2_sensor_ssdb_data (or even just
cio2_sensor_ssdb) to make it clearer that it contains the SSDB data ?

> +	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];
> +} __packed__;
> +
> +/* Fields needed by bridge driver */
> +struct sensor_bios_data {

And cio2_sensor_data ?

> +	struct device *dev;
> +	u8 link;
> +	u8 lanes;
> +	u8 degree;
> +	u32 mclkspeed;
> +};
> +
> +#endif
> diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
> index f68ef0f6b..827457110 100644
> --- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
> +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
> @@ -1715,9 +1715,27 @@ static void cio2_queues_exit(struct cio2_device *cio2)
>  static int cio2_pci_probe(struct pci_dev *pci_dev,
>  			  const struct pci_device_id *id)
>  {
> +	struct fwnode_handle *endpoint;
>  	struct cio2_device *cio2;
>  	int r;
>  
> +	/*
> +	 * On some platforms no connections to sensors are defined in firmware,
> +	 * if the device has no endpoints then we can try to build those as
> +	 * software_nodes parsed from SSDB.
> +	 *
> +	 * This may EPROBE_DEFER if supported devices are found defined in ACPI
> +	 * but not yet ready for use (either not attached to the i2c bus yet,
> +	 * or not done probing themselves).

Why do we need for the I2C devices to be probed, isn't it enough to have
them created ?

> +	 */
> +
> +	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&pci_dev->dev), NULL);
> +	if (!endpoint) {
> +		r = cio2_bridge_build(pci_dev);
> +		if (r)
> +			return r;
> +	}
> +
>  	cio2 = devm_kzalloc(&pci_dev->dev, sizeof(*cio2), GFP_KERNEL);
>  	if (!cio2)
>  		return -ENOMEM;
> @@ -1825,6 +1843,9 @@ static void cio2_pci_remove(struct pci_dev *pci_dev)
>  {
>  	struct cio2_device *cio2 = pci_get_drvdata(pci_dev);
>  
> +	if (is_software_node(dev_fwnode(&pci_dev->dev)))
> +		cio2_bridge_burn(pci_dev);
> +
>  	media_device_unregister(&cio2->media_dev);
>  	v4l2_async_notifier_unregister(&cio2->notifier);
>  	v4l2_async_notifier_cleanup(&cio2->notifier);
> diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.h b/drivers/media/pci/intel/ipu3/ipu3-cio2.h
> index 549b08f88..80a081d7e 100644
> --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.h
> +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.h
> @@ -436,4 +436,13 @@ static inline struct cio2_queue *vb2q_to_cio2_queue(struct vb2_queue *vq)
>  	return container_of(vq, struct cio2_queue, vbq);
>  }
>  
> +#if IS_ENABLED(CONFIG_CIO2_BRIDGE)
> +	int cio2_bridge_build(struct pci_dev *cio2);
> +	void cio2_bridge_burn(struct pci_dev *cio2);

No need for an extra indentation level, neither here, not below.

> +#else
> +

NO need for this blank line.

> +	int cio2_bridge_build(struct pci_dev *cio2) { return 0; }
> +	void cio2_bridge_burn(struct pci_dev *cio2) { }
> +#endif
> +
>  #endif
Daniel Scally Oct. 24, 2020, 8:50 a.m. UTC | #6
On 24/10/2020 02:24, Laurent Pinchart wrote:
> Hi Daniel,
>
> Thank you for the patch.
Thank you for reviewing it - very helpful comments
>
> On Mon, Oct 19, 2020 at 11:59:03PM +0100, Daniel Scally wrote:
>> Currently on platforms designed for Windows, connections between CIO2 and
>> sensors are not properly defined in DSDT. This patch extends the ipu3-cio2
>> driver to compensate by building software_node connections, parsing the
>> connection properties from the sensor's SSDB buffer.
>>
>> Suggested-by: Jordan Hand <jorhand@linux.microsoft.com>
>> Signed-off-by: Daniel Scally <djrscally@gmail.com>
>> ---
>> Changes in v3:
>> 	- Rather than overwriting the device's primary fwnode, we now
>> 	simply assign a secondary. Some of the preceding patches alter the
>> 	existing driver code and v4l2 framework to allow for that.
>> 	- Rather than reprobe() the sensor after connecting the devices in
>> 	cio2-bridge we create the software_nodes right away. In this case,
>> 	sensor drivers will have to defer probing until they detect that a
>> 	fwnode graph is connecting them to the CIO2 device.
>> 	- Error paths in connect_supported_devices() moved outside the
>> 	loop
>> 	- Replaced pr_*() with dev_*() throughout
>> 	- Moved creation of software_node / property_entry arrays to stack
>> 	- A lot of formatting changes.
>>
>>  MAINTAINERS                                   |   1 +
>>  drivers/media/pci/intel/ipu3/Kconfig          |  18 +
>>  drivers/media/pci/intel/ipu3/Makefile         |   3 +-
>>  drivers/media/pci/intel/ipu3/cio2-bridge.c    | 327 ++++++++++++++++++
>>  drivers/media/pci/intel/ipu3/cio2-bridge.h    |  94 +++++
>>  drivers/media/pci/intel/ipu3/ipu3-cio2-main.c |  21 ++
>>  drivers/media/pci/intel/ipu3/ipu3-cio2.h      |   9 +
>>  7 files changed, 472 insertions(+), 1 deletion(-)
>>  create mode 100644 drivers/media/pci/intel/ipu3/cio2-bridge.c
>>  create mode 100644 drivers/media/pci/intel/ipu3/cio2-bridge.h
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 5d768d5ad..4c9c646c7 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -8848,6 +8848,7 @@ INTEL IPU3 CSI-2 CIO2 DRIVER
>>  M:	Yong Zhi <yong.zhi@intel.com>
>>  M:	Sakari Ailus <sakari.ailus@linux.intel.com>
>>  M:	Bingbu Cao <bingbu.cao@intel.com>
>> +M:	Dan Scally <djrscally@gmail.com>
>>  R:	Tianshu Qiu <tian.shu.qiu@intel.com>
>>  L:	linux-media@vger.kernel.org
>>  S:	Maintained
>> diff --git a/drivers/media/pci/intel/ipu3/Kconfig b/drivers/media/pci/intel/ipu3/Kconfig
>> index 82d7f17e6..d14cbceae 100644
>> --- a/drivers/media/pci/intel/ipu3/Kconfig
>> +++ b/drivers/media/pci/intel/ipu3/Kconfig
>> @@ -16,3 +16,21 @@ config VIDEO_IPU3_CIO2
>>  	  Say Y or M here if you have a Skylake/Kaby Lake SoC with MIPI CSI-2
>>  	  connected camera.
>>  	  The module will be called ipu3-cio2.
>> +
>> +config CIO2_BRIDGE
>> +	bool "IPU3 CIO2 Sensors Bridge"
>> +	depends on VIDEO_IPU3_CIO2
>> +	help
>> +	  This extension provides an API for the ipu3-cio2 driver to create
>> +	  connections to cameras that are hidden in SSDB buffer in ACPI. It
>> +	  can be used to enable support for cameras in detachable / hybrid
>> +	  devices that ship with Windows.
>> +
>> +	  Say Y here if your device is a detachable / hybrid laptop that comes
>> +	  with Windows installed by the OEM, for example:
>> +
>> +	  	- Some Microsoft Surface models
>> +		- The Lenovo Miix line
>> +		- Dell 7285
>> +
>> +	  If in doubt, say N here.
>> diff --git a/drivers/media/pci/intel/ipu3/Makefile b/drivers/media/pci/intel/ipu3/Makefile
>> index b4e3266d9..933777e6e 100644
>> --- a/drivers/media/pci/intel/ipu3/Makefile
>> +++ b/drivers/media/pci/intel/ipu3/Makefile
>> @@ -1,4 +1,5 @@
>>  # SPDX-License-Identifier: GPL-2.0-only
>>  obj-$(CONFIG_VIDEO_IPU3_CIO2) += ipu3-cio2.o
>>  
>> -ipu3-cio2-y += ipu3-cio2-main.o
>> \ No newline at end of file
>> +ipu3-cio2-y += ipu3-cio2-main.o
>> +ipu3-cio2-$(CONFIG_CIO2_BRIDGE) += cio2-bridge.o
>> diff --git a/drivers/media/pci/intel/ipu3/cio2-bridge.c b/drivers/media/pci/intel/ipu3/cio2-bridge.c
>> new file mode 100644
>> index 000000000..bbe072f04
>> --- /dev/null
>> +++ b/drivers/media/pci/intel/ipu3/cio2-bridge.c
>> @@ -0,0 +1,327 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +// Author: Dan Scally <djrscally@gmail.com>
>> +#include <linux/acpi.h>
>> +#include <linux/device.h>
>> +#include <linux/fwnode.h>
>> +#include <linux/i2c.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/pci.h>
>> +#include <linux/property.h>
>> +#include <media/v4l2-subdev.h>
>> +
>> +#include "cio2-bridge.h"
>> +
>> +/*
>> + * Extend this array with ACPI Hardware ID's of devices known to be
>> + * working
>> + */
>> +static const char * const supported_devices[] = {
>> +	"INT33BE",
>> +	"OVTI2680",
>> +};
>> +
>> +static struct software_node cio2_hid_node = { CIO2_HID };
>> +
>> +static struct cio2_bridge bridge;
> While there shouldn't be more than one CIO2 instance, we try to develop
> drivers in a way that avoids global per-device variables. Could all this
> be allocated dynamically, with the pointer returned by
> cio2_bridge_build() and stored in the cio2_device structure ?
Yes, ok, I'll make that change.
>> +
>> +static const char * const port_names[] = {
>> +	"port0", "port1", "port2", "port3"
>> +};
>> +
>> +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]),
>> +};
> For the same reason, I would move this to the sensor structure (with two
> property_entry per sensor). That will simplify the code below, avoiding
> indexing this array with bridge.n_sensors * 2.
I had some trouble with that which is why I ended up doing things this
way; I'll revisit it and see if I can resolve that.
>> +
>> +static int read_acpi_block(struct device *dev, char *id, void *data, u32 size)
> To avoid potential future namespace classes, I'd advise naming the
> functions with a cio2_bridge_ prefix, even the static ones.
>
> And maybe cio2_bridge_read_acpi_buffer(), as this function reads a
> buffer ?
Ack to both; and to the similar comments re: variable naming below.
>> +{
>> +	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
>> +	struct acpi_handle *handle;
>> +	union acpi_object *obj;
>> +	acpi_status status;
>> +	int ret;
>> +
>> +	handle = ACPI_HANDLE(dev);
>> +
>> +	status = acpi_evaluate_object(handle, id, NULL, &buffer);
>> +	if (ACPI_FAILURE(status))
>> +		return -ENODEV;
>> +
>> +	obj = buffer.pointer;
>> +	if (!obj) {
>> +		dev_err(dev, "Couldn't locate ACPI buffer\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	if (obj->type != ACPI_TYPE_BUFFER) {
>> +		dev_err(dev, "Couldn't read ACPI buffer\n");
>> +		ret = -ENODEV;
>> +		goto out_free_buff;
>> +	}
>> +
>> +	if (obj->buffer.length > size) {
>> +		dev_err(dev, "Given buffer is too small\n");
>> +		ret = -ENODEV;
>> +		goto out_free_buff;
>> +	}
>> +
>> +	memcpy(data, obj->buffer.pointer, obj->buffer.length);
>> +	ret = obj->buffer.length;
>> +
>> +out_free_buff:
>> +	kfree(buffer.pointer);
>> +	return ret;
>> +}
>> +
>> +static int get_acpi_ssdb_sensor_data(struct device *dev,
>> +				     struct sensor_bios_data *sensor)
>> +{
>> +	struct sensor_bios_data_packed sensor_data;
>> +	int ret;
>> +
>> +	ret = read_acpi_block(dev, "SSDB", &sensor_data, sizeof(sensor_data));
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	sensor->link = sensor_data.link;
>> +	sensor->lanes = sensor_data.lanes;
>> +	sensor->mclkspeed = sensor_data.mclkspeed;
>> +	sensor->degree = sensor_data.degree;
> How about storing a sensor_bios_data_packed inside sensor_bios_data ?
> That will avoid copying fields individually, with manual addition of
> extra fields as they become useful. Usage of the sensor_bios_data
> structure would turn from sensor->degree to sensor->ssdb.degree, which
> is slightly longer, but I think more maintainable.
Inside the struct sensor you mean (confusingly, the variable named
sensor here is _not_ of type struct sensor, which I acknowledge is plain
silly)? If so, agreed, I'll change it to that. That's also consistent
with what I'm doing with the equivalent struct for the PMIC's CLDB in
the regulator work so that makes sense anyway.
>> +
>> +	return 0;
>> +}
>> +
>> +static int create_fwnode_properties(struct sensor *sensor,
>> +				    struct sensor_bios_data *ssdb)
>> +{
>> +	struct property_entry *cio2_properties = sensor->cio2_properties;
>> +	struct property_entry *dev_properties = sensor->dev_properties;
>> +	struct property_entry *ep_properties = sensor->ep_properties;
>> +	int i;
> i never takes negative values, you can make it an unsigned int. Same for
> other occurrences below.
>
>> +
>> +	/* device fwnode properties */
>> +	memset(dev_properties, 0, sizeof(struct property_entry) * 3);
> I would memset() bridge to 0 in one go and avoid individual memsets. And
> if you allocate it with kzalloc() it will be initialized to 0.
Yep ok, I'll initialize the whole thing with kzalloc at the start then
>
>> +
>> +	dev_properties[0] = PROPERTY_ENTRY_U32("clock-frequency",
>> +					       ssdb->mclkspeed);
>> +	dev_properties[1] = PROPERTY_ENTRY_U8("rotation", ssdb->degree);
>> +
>> +	/* endpoint fwnode properties */
>> +	memset(ep_properties, 0, sizeof(struct property_entry) * 4);
>> +
>> +	sensor->data_lanes = kmalloc_array(ssdb->lanes, sizeof(u32),
>> +					   GFP_KERNEL);
> Given that there can't be more than 4 data lanes, how about turning
> data_lanes into an array with 4 entries, to avoid the dynamic allocation
> ? You will have to validate ssdb->lanes in connect_supported_devices(),
> to make sure not to overflow the array. This and the next function can
> then be turned into void functions.
OK - is that generally better then (I.E. avoiding dynamic allocation),
or just when the "wasted" memory is so small?
>
>> +
>> +	if (!sensor->data_lanes)
>> +		return -ENOMEM;
>> +
>> +	for (i = 0; i < ssdb->lanes; i++)
>> +		sensor->data_lanes[i] = i + 1;
>> +
>> +	ep_properties[0] = PROPERTY_ENTRY_U32("bus-type", 5);
>> +	ep_properties[1] = PROPERTY_ENTRY_U32_ARRAY_LEN("data-lanes",
>> +							sensor->data_lanes,
>> +							ssdb->lanes);
>> +	ep_properties[2] = remote_endpoints[(bridge.n_sensors * 2) + ENDPOINT_SENSOR];
>> +
>> +	/* cio2 endpoint props */
>> +	memset(cio2_properties, 0, sizeof(struct property_entry) * 3);
>> +
>> +	cio2_properties[0] = PROPERTY_ENTRY_U32_ARRAY_LEN("data-lanes",
>> +							  sensor->data_lanes,
>> +							  ssdb->lanes);
>> +	cio2_properties[1] = remote_endpoints[(bridge.n_sensors * 2) + ENDPOINT_CIO2];
>> +
>> +	return 0;
>> +}
>> +
>> +static int create_connection_swnodes(struct sensor *sensor,
>> +				     struct sensor_bios_data *ssdb)
>> +{
>> +	struct software_node *nodes = sensor->swnodes;
>> +
>> +	memset(nodes, 0, sizeof(struct software_node) * 6);
>> +
>> +	nodes[SWNODE_SENSOR_HID] = NODE_SENSOR(sensor->name,
>> +					       sensor->dev_properties);
>> +	nodes[SWNODE_SENSOR_PORT] = NODE_PORT("port0",
>> +					      &nodes[SWNODE_SENSOR_HID]);
>> +	nodes[SWNODE_SENSOR_ENDPOINT] = NODE_ENDPOINT("endpoint0",
>> +						      &nodes[SWNODE_SENSOR_PORT],
>> +						      sensor->ep_properties);
>> +	nodes[SWNODE_CIO2_PORT] = NODE_PORT(port_names[ssdb->link],
>> +					    &cio2_hid_node);
>> +	nodes[SWNODE_CIO2_ENDPOINT] = NODE_ENDPOINT("endpoint0",
>> +						    &nodes[SWNODE_CIO2_PORT],
>> +						    sensor->cio2_properties);
>> +
>> +	return 0;
>> +}
>> +
>> +static void cio2_bridge_unregister_sensors(void)
>> +{
>> +	struct sensor *sensor;
>> +	int i;
>> +
>> +	for (i = 0; i < bridge.n_sensors; i++) {
>> +		sensor = &bridge.sensors[i];
>> +
>> +		software_node_unregister_nodes_reverse(sensor->swnodes);
>> +
>> +		kfree(sensor->data_lanes);
>> +
>> +		put_device(sensor->dev);
>> +		acpi_dev_put(sensor->adev);
>> +	}
>> +}
>> +
>> +static int connect_supported_devices(struct pci_dev *cio2)
>> +{
>> +	struct sensor_bios_data ssdb;
>> +	struct fwnode_handle *fwnode;
>> +	struct acpi_device *adev;
>> +	struct sensor *sensor;
>> +	struct device *dev;
>> +	int i, ret;
>> +
>> +	ret = 0;
> You can initialize ret to 0 when declaring the variable.
Is that ok on the same like as i's declaration, or should I split them?
>
>> +	for (i = 0; i < ARRAY_SIZE(supported_devices); i++) {
>> +		adev = acpi_dev_get_first_match_dev(supported_devices[i], NULL, -1);
> What if there are multiple sensor of the same model ?

Hmm, yeah, that would be a bit of a pickle. I guess the newer
smartphones have multiple sensors on the back, which I presume are the
same model. So that will probably crop up at some point. How about
instead I use bus_for_each_dev() and in the applied function check if
the _HID is in the supported list?

>
>> +		if (!adev)
>> +			continue;
>> +
> Does acpi_dev_get_first_match_dev() skip disabled devices (as reported
> by _STA) ?
Yes.
>> +		dev = bus_find_device_by_acpi_dev(&i2c_bus_type, adev);
>> +		if (!dev) {
>> +			ret = -EPROBE_DEFER;
>> +			goto err_rollback;
>> +		}
>> +
>> +		sensor = &bridge.sensors[bridge.n_sensors];
>> +		sensor->dev = dev;
>> +		sensor->adev = adev;
>> +
>> +		snprintf(sensor->name, ACPI_ID_LEN, "%s",
>> +			 supported_devices[i]);
> How about strlcpy() ?
Sure
>> +
>> +		ret = get_acpi_ssdb_sensor_data(dev, &ssdb);
>> +		if (ret)
>> +			goto err_free_dev;
>> +
>> +		ret = create_fwnode_properties(sensor, &ssdb);
>> +		if (ret)
>> +			goto err_free_dev;
>> +
>> +		ret = create_connection_swnodes(sensor, &ssdb);
>> +		if (ret)
>> +			goto err_free_dev;
>> +
>> +		ret = software_node_register_nodes(sensor->swnodes);
>> +		if (ret)
>> +			goto err_free_dev;
>> +
>> +		fwnode = software_node_fwnode(&sensor->swnodes[SWNODE_SENSOR_HID]);
>> +		if (!fwnode) {
>> +			ret = -ENODEV;
>> +			goto err_free_swnodes;
>> +		}
>> +
>> +		set_secondary_fwnode(dev, fwnode);
> I wonder if we could avoid depending on the I2C device being created by
> getting the fwnode from adev, and setting ->secondary manually. adev
> would need to be passed to get_acpi_ssdb_sensor_data() instead of dev.
Let me try that; I initially wanted to do
set_secondary_fwnode(&adev->dev, fwnode) to avoid depending on the I2C
dev being created but it turns out &adev->dev isn't the same pointer. I
shall try it and see.
>
>> +
>> +		dev_info(&cio2->dev, "Found supported device %s\n",
>> +			 supported_devices[i]);
>> +
>> +		bridge.n_sensors++;
>> +		continue;
>> +	}
>> +
>> +	return ret;
>> +
>> +err_free_swnodes:
>> +	software_node_unregister_nodes_reverse(sensor->swnodes);
>> +err_free_dev:
>> +	put_device(dev);
>> +err_rollback:
>> +	acpi_dev_put(adev);
> I think you'll leak sensor->data_lanes here. It won't be a problem if
> you don't allocate it manually, as proposed above. I however wonder if
> error handling couldn't be simplified by increasing bridge.n_sensors
> earlier, and skipping cleanup in cio2_bridge_unregister_sensors() for
> the fields that haven't been initialized (for instance kfree() is a
> no-op on NULL pointers, so that's already handled).
Errrr the only sticky bit there is the desire _not_ to unwind all the
sensors if it managed to successfully connect one of them; if I'm just
calling cio2_bridge_unregister_sensors() on any error then a failure for
one sensor will result in no working cameras, where this way at least
one of them might be available.
>
>> +
>> +	/*
>> +	 * If an iteration of this loop results in -EPROBE_DEFER then
>> +	 * we need to roll back any sensors that were successfully
>> +	 * registered. Any other error and we'll skip that step, as
>> +	 * it seems better to have one successfully connected sensor.
>> +	 */
>> +
>> +	if (ret == -EPROBE_DEFER)
>> +		cio2_bridge_unregister_sensors();
>> +
>> +	return ret;
>> +}
>> +
>> +int cio2_bridge_build(struct pci_dev *cio2)
>> +{
>> +	struct fwnode_handle *fwnode;
>> +	int ret;
>> +
>> +	pci_dev_get(cio2);
>> +
>> +	ret = software_node_register(&cio2_hid_node);
>> +	if (ret < 0) {
>> +		dev_err(&cio2->dev, "Failed to register the CIO2 HID node\n");
>> +		goto err_put_cio2;
>> +	}
>> +
>> +	ret = connect_supported_devices(cio2);
>> +	if (ret == -EPROBE_DEFER)
>> +		goto err_unregister_cio2;
>> +
>> +	if (bridge.n_sensors == 0) {
>> +		ret = -EPROBE_DEFER;
>> +		goto err_unregister_cio2;
>> +	}
>> +
>> +	dev_info(&cio2->dev, "Connected %d cameras\n", bridge.n_sensors);
>> +
>> +	fwnode = software_node_fwnode(&cio2_hid_node);
>> +	if (!fwnode) {
>> +		dev_err(&cio2->dev,
>> +			"Error getting fwnode from cio2 software_node\n");
>> +		ret = -ENODEV;
>> +		goto err_unregister_sensors;
>> +	}
>> +
>> +	set_secondary_fwnode(&cio2->dev, fwnode);
>> +
>> +	return 0;
>> +
>> +err_unregister_sensors:
>> +	cio2_bridge_unregister_sensors();
>> +err_unregister_cio2:
>> +	software_node_unregister(&cio2_hid_node);
>> +err_put_cio2:
>> +	pci_dev_put(cio2);
>> +
>> +	return ret;
>> +}
>> +
>> +void cio2_bridge_burn(struct pci_dev *cio2)
> Interesting function name :-) I like the creativity, but I think
> consistency with the rest of the kernel code should unfortunately be
> favoured.

Heh yep - already changed to _init/_clean per Andy's comments. Couldn't
resist the pun!

>> +	struct sensor sensors[MAX_CONNECTED_DEVICES];
>> +};
>> +
>> +/* Data representation as it is in ACPI SSDB buffer */
>> +struct sensor_bios_data_packed {
> Similarly as above, I'd use a cio2_ prefix, and I think you can drop the
> _packed suffix. How about naming it cio2_sensor_ssdb_data (or even just
> cio2_sensor_ssdb) to make it clearer that it contains the SSDB data ?
Already done (well, sensor_ssdb currently) to keep consistent with
struct pmic_cldb that was introduced. I'll add the cio2 prefix.
>
>> +	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];
>> +} __packed__;
>> +
>> +/* Fields needed by bridge driver */
>> +struct sensor_bios_data {
> And cio2_sensor_data ?
Ack
>
>> +	struct device *dev;
>> +	u8 link;
>> +	u8 lanes;
>> +	u8 degree;
>> +	u32 mclkspeed;
>> +};
>> +
>> +#endif
>> diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
>> index f68ef0f6b..827457110 100644
>> --- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
>> +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
>> @@ -1715,9 +1715,27 @@ static void cio2_queues_exit(struct cio2_device *cio2)
>>  static int cio2_pci_probe(struct pci_dev *pci_dev,
>>  			  const struct pci_device_id *id)
>>  {
>> +	struct fwnode_handle *endpoint;
>>  	struct cio2_device *cio2;
>>  	int r;
>>  
>> +	/*
>> +	 * On some platforms no connections to sensors are defined in firmware,
>> +	 * if the device has no endpoints then we can try to build those as
>> +	 * software_nodes parsed from SSDB.
>> +	 *
>> +	 * This may EPROBE_DEFER if supported devices are found defined in ACPI
>> +	 * but not yet ready for use (either not attached to the i2c bus yet,
>> +	 * or not done probing themselves).
> Why do we need for the I2C devices to be probed, isn't it enough to have
> them created ?
Ooops  - a relic of the prior version that I missed out when cleaning up
- I'll fix that
> No need for an extra indentation level, neither here, not below.
> NO need for this blank line.
Both fixed - thanks very much
Laurent Pinchart Oct. 24, 2020, 9:37 a.m. UTC | #7
Hi Daniel,

On Sat, Oct 24, 2020 at 09:50:07AM +0100, Dan Scally wrote:
> On 24/10/2020 02:24, Laurent Pinchart wrote:
>
> > On Mon, Oct 19, 2020 at 11:59:03PM +0100, Daniel Scally wrote:
> >> Currently on platforms designed for Windows, connections between CIO2 and
> >> sensors are not properly defined in DSDT. This patch extends the ipu3-cio2
> >> driver to compensate by building software_node connections, parsing the
> >> connection properties from the sensor's SSDB buffer.
> >>
> >> Suggested-by: Jordan Hand <jorhand@linux.microsoft.com>
> >> Signed-off-by: Daniel Scally <djrscally@gmail.com>
> >> ---
> >> Changes in v3:
> >> 	- Rather than overwriting the device's primary fwnode, we now
> >> 	simply assign a secondary. Some of the preceding patches alter the
> >> 	existing driver code and v4l2 framework to allow for that.
> >> 	- Rather than reprobe() the sensor after connecting the devices in
> >> 	cio2-bridge we create the software_nodes right away. In this case,
> >> 	sensor drivers will have to defer probing until they detect that a
> >> 	fwnode graph is connecting them to the CIO2 device.
> >> 	- Error paths in connect_supported_devices() moved outside the
> >> 	loop
> >> 	- Replaced pr_*() with dev_*() throughout
> >> 	- Moved creation of software_node / property_entry arrays to stack
> >> 	- A lot of formatting changes.
> >>
> >>  MAINTAINERS                                   |   1 +
> >>  drivers/media/pci/intel/ipu3/Kconfig          |  18 +
> >>  drivers/media/pci/intel/ipu3/Makefile         |   3 +-
> >>  drivers/media/pci/intel/ipu3/cio2-bridge.c    | 327 ++++++++++++++++++
> >>  drivers/media/pci/intel/ipu3/cio2-bridge.h    |  94 +++++
> >>  drivers/media/pci/intel/ipu3/ipu3-cio2-main.c |  21 ++
> >>  drivers/media/pci/intel/ipu3/ipu3-cio2.h      |   9 +
> >>  7 files changed, 472 insertions(+), 1 deletion(-)
> >>  create mode 100644 drivers/media/pci/intel/ipu3/cio2-bridge.c
> >>  create mode 100644 drivers/media/pci/intel/ipu3/cio2-bridge.h
> >>
> >> diff --git a/MAINTAINERS b/MAINTAINERS
> >> index 5d768d5ad..4c9c646c7 100644
> >> --- a/MAINTAINERS
> >> +++ b/MAINTAINERS
> >> @@ -8848,6 +8848,7 @@ INTEL IPU3 CSI-2 CIO2 DRIVER
> >>  M:	Yong Zhi <yong.zhi@intel.com>
> >>  M:	Sakari Ailus <sakari.ailus@linux.intel.com>
> >>  M:	Bingbu Cao <bingbu.cao@intel.com>
> >> +M:	Dan Scally <djrscally@gmail.com>
> >>  R:	Tianshu Qiu <tian.shu.qiu@intel.com>
> >>  L:	linux-media@vger.kernel.org
> >>  S:	Maintained
> >> diff --git a/drivers/media/pci/intel/ipu3/Kconfig b/drivers/media/pci/intel/ipu3/Kconfig
> >> index 82d7f17e6..d14cbceae 100644
> >> --- a/drivers/media/pci/intel/ipu3/Kconfig
> >> +++ b/drivers/media/pci/intel/ipu3/Kconfig
> >> @@ -16,3 +16,21 @@ config VIDEO_IPU3_CIO2
> >>  	  Say Y or M here if you have a Skylake/Kaby Lake SoC with MIPI CSI-2
> >>  	  connected camera.
> >>  	  The module will be called ipu3-cio2.
> >> +
> >> +config CIO2_BRIDGE
> >> +	bool "IPU3 CIO2 Sensors Bridge"
> >> +	depends on VIDEO_IPU3_CIO2
> >> +	help
> >> +	  This extension provides an API for the ipu3-cio2 driver to create
> >> +	  connections to cameras that are hidden in SSDB buffer in ACPI. It
> >> +	  can be used to enable support for cameras in detachable / hybrid
> >> +	  devices that ship with Windows.
> >> +
> >> +	  Say Y here if your device is a detachable / hybrid laptop that comes
> >> +	  with Windows installed by the OEM, for example:
> >> +
> >> +	  	- Some Microsoft Surface models
> >> +		- The Lenovo Miix line
> >> +		- Dell 7285
> >> +
> >> +	  If in doubt, say N here.
> >> diff --git a/drivers/media/pci/intel/ipu3/Makefile b/drivers/media/pci/intel/ipu3/Makefile
> >> index b4e3266d9..933777e6e 100644
> >> --- a/drivers/media/pci/intel/ipu3/Makefile
> >> +++ b/drivers/media/pci/intel/ipu3/Makefile
> >> @@ -1,4 +1,5 @@
> >>  # SPDX-License-Identifier: GPL-2.0-only
> >>  obj-$(CONFIG_VIDEO_IPU3_CIO2) += ipu3-cio2.o
> >>  
> >> -ipu3-cio2-y += ipu3-cio2-main.o
> >> \ No newline at end of file
> >> +ipu3-cio2-y += ipu3-cio2-main.o
> >> +ipu3-cio2-$(CONFIG_CIO2_BRIDGE) += cio2-bridge.o
> >> diff --git a/drivers/media/pci/intel/ipu3/cio2-bridge.c b/drivers/media/pci/intel/ipu3/cio2-bridge.c
> >> new file mode 100644
> >> index 000000000..bbe072f04
> >> --- /dev/null
> >> +++ b/drivers/media/pci/intel/ipu3/cio2-bridge.c
> >> @@ -0,0 +1,327 @@
> >> +// SPDX-License-Identifier: GPL-2.0
> >> +// Author: Dan Scally <djrscally@gmail.com>
> >> +#include <linux/acpi.h>
> >> +#include <linux/device.h>
> >> +#include <linux/fwnode.h>
> >> +#include <linux/i2c.h>
> >> +#include <linux/kernel.h>
> >> +#include <linux/module.h>
> >> +#include <linux/pci.h>
> >> +#include <linux/property.h>
> >> +#include <media/v4l2-subdev.h>
> >> +
> >> +#include "cio2-bridge.h"
> >> +
> >> +/*
> >> + * Extend this array with ACPI Hardware ID's of devices known to be
> >> + * working
> >> + */
> >> +static const char * const supported_devices[] = {
> >> +	"INT33BE",
> >> +	"OVTI2680",
> >> +};
> >> +
> >> +static struct software_node cio2_hid_node = { CIO2_HID };
> >> +
> >> +static struct cio2_bridge bridge;
> >
> > While there shouldn't be more than one CIO2 instance, we try to develop
> > drivers in a way that avoids global per-device variables. Could all this
> > be allocated dynamically, with the pointer returned by
> > cio2_bridge_build() and stored in the cio2_device structure ?
>
> Yes, ok, I'll make that change.
>
> >> +
> >> +static const char * const port_names[] = {
> >> +	"port0", "port1", "port2", "port3"
> >> +};
> >> +
> >> +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]),
> >> +};
> >
> > For the same reason, I would move this to the sensor structure (with two
> > property_entry per sensor). That will simplify the code below, avoiding
> > indexing this array with bridge.n_sensors * 2.
>
> I had some trouble with that which is why I ended up doing things this
> way; I'll revisit it and see if I can resolve that.
>
> >> +
> >> +static int read_acpi_block(struct device *dev, char *id, void *data, u32 size)
> >
> > To avoid potential future namespace classes, I'd advise naming the
> > functions with a cio2_bridge_ prefix, even the static ones.
> >
> > And maybe cio2_bridge_read_acpi_buffer(), as this function reads a
> > buffer ?
>
> Ack to both; and to the similar comments re: variable naming below.
>
> >> +{
> >> +	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
> >> +	struct acpi_handle *handle;
> >> +	union acpi_object *obj;
> >> +	acpi_status status;
> >> +	int ret;
> >> +
> >> +	handle = ACPI_HANDLE(dev);
> >> +
> >> +	status = acpi_evaluate_object(handle, id, NULL, &buffer);
> >> +	if (ACPI_FAILURE(status))
> >> +		return -ENODEV;
> >> +
> >> +	obj = buffer.pointer;
> >> +	if (!obj) {
> >> +		dev_err(dev, "Couldn't locate ACPI buffer\n");
> >> +		return -ENODEV;
> >> +	}
> >> +
> >> +	if (obj->type != ACPI_TYPE_BUFFER) {
> >> +		dev_err(dev, "Couldn't read ACPI buffer\n");
> >> +		ret = -ENODEV;
> >> +		goto out_free_buff;
> >> +	}
> >> +
> >> +	if (obj->buffer.length > size) {
> >> +		dev_err(dev, "Given buffer is too small\n");
> >> +		ret = -ENODEV;
> >> +		goto out_free_buff;
> >> +	}
> >> +
> >> +	memcpy(data, obj->buffer.pointer, obj->buffer.length);
> >> +	ret = obj->buffer.length;
> >> +
> >> +out_free_buff:
> >> +	kfree(buffer.pointer);
> >> +	return ret;
> >> +}
> >> +
> >> +static int get_acpi_ssdb_sensor_data(struct device *dev,
> >> +				     struct sensor_bios_data *sensor)
> >> +{
> >> +	struct sensor_bios_data_packed sensor_data;
> >> +	int ret;
> >> +
> >> +	ret = read_acpi_block(dev, "SSDB", &sensor_data, sizeof(sensor_data));
> >> +	if (ret < 0)
> >> +		return ret;
> >> +
> >> +	sensor->link = sensor_data.link;
> >> +	sensor->lanes = sensor_data.lanes;
> >> +	sensor->mclkspeed = sensor_data.mclkspeed;
> >> +	sensor->degree = sensor_data.degree;
> >
> > How about storing a sensor_bios_data_packed inside sensor_bios_data ?
> > That will avoid copying fields individually, with manual addition of
> > extra fields as they become useful. Usage of the sensor_bios_data
> > structure would turn from sensor->degree to sensor->ssdb.degree, which
> > is slightly longer, but I think more maintainable.
>
> Inside the struct sensor you mean (confusingly, the variable named
> sensor here is _not_ of type struct sensor, which I acknowledge is plain
> silly)? If so, agreed, I'll change it to that. That's also consistent
> with what I'm doing with the equivalent struct for the PMIC's CLDB in
> the regulator work so that makes sense anyway.

Yes. Looking at it again, the sensor_bios_data structure can likely be
dropped, as it bundles a struct device pointer, which is unused, with
fields copied from sensor_bios_data_packed, which should be stored in
the sensor struct.

> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int create_fwnode_properties(struct sensor *sensor,
> >> +				    struct sensor_bios_data *ssdb)
> >> +{
> >> +	struct property_entry *cio2_properties = sensor->cio2_properties;
> >> +	struct property_entry *dev_properties = sensor->dev_properties;
> >> +	struct property_entry *ep_properties = sensor->ep_properties;
> >> +	int i;
> >
> > i never takes negative values, you can make it an unsigned int. Same for
> > other occurrences below.
> >
> >> +
> >> +	/* device fwnode properties */
> >> +	memset(dev_properties, 0, sizeof(struct property_entry) * 3);
> >
> > I would memset() bridge to 0 in one go and avoid individual memsets. And
> > if you allocate it with kzalloc() it will be initialized to 0.
>
> Yep ok, I'll initialize the whole thing with kzalloc at the start then
>
> >> +
> >> +	dev_properties[0] = PROPERTY_ENTRY_U32("clock-frequency",
> >> +					       ssdb->mclkspeed);
> >> +	dev_properties[1] = PROPERTY_ENTRY_U8("rotation", ssdb->degree);
> >> +
> >> +	/* endpoint fwnode properties */
> >> +	memset(ep_properties, 0, sizeof(struct property_entry) * 4);
> >> +
> >> +	sensor->data_lanes = kmalloc_array(ssdb->lanes, sizeof(u32),
> >> +					   GFP_KERNEL);
> >
> > Given that there can't be more than 4 data lanes, how about turning
> > data_lanes into an array with 4 entries, to avoid the dynamic allocation
> > ? You will have to validate ssdb->lanes in connect_supported_devices(),
> > to make sure not to overflow the array. This and the next function can
> > then be turned into void functions.
>
> OK - is that generally better then (I.E. avoiding dynamic allocation),
> or just when the "wasted" memory is so small?

Just when the amount of wasted memory is small.

> >> +
> >> +	if (!sensor->data_lanes)
> >> +		return -ENOMEM;
> >> +
> >> +	for (i = 0; i < ssdb->lanes; i++)
> >> +		sensor->data_lanes[i] = i + 1;
> >> +
> >> +	ep_properties[0] = PROPERTY_ENTRY_U32("bus-type", 5);
> >> +	ep_properties[1] = PROPERTY_ENTRY_U32_ARRAY_LEN("data-lanes",
> >> +							sensor->data_lanes,
> >> +							ssdb->lanes);
> >> +	ep_properties[2] = remote_endpoints[(bridge.n_sensors * 2) + ENDPOINT_SENSOR];
> >> +
> >> +	/* cio2 endpoint props */
> >> +	memset(cio2_properties, 0, sizeof(struct property_entry) * 3);
> >> +
> >> +	cio2_properties[0] = PROPERTY_ENTRY_U32_ARRAY_LEN("data-lanes",
> >> +							  sensor->data_lanes,
> >> +							  ssdb->lanes);
> >> +	cio2_properties[1] = remote_endpoints[(bridge.n_sensors * 2) + ENDPOINT_CIO2];
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int create_connection_swnodes(struct sensor *sensor,
> >> +				     struct sensor_bios_data *ssdb)
> >> +{
> >> +	struct software_node *nodes = sensor->swnodes;
> >> +
> >> +	memset(nodes, 0, sizeof(struct software_node) * 6);
> >> +
> >> +	nodes[SWNODE_SENSOR_HID] = NODE_SENSOR(sensor->name,
> >> +					       sensor->dev_properties);
> >> +	nodes[SWNODE_SENSOR_PORT] = NODE_PORT("port0",
> >> +					      &nodes[SWNODE_SENSOR_HID]);
> >> +	nodes[SWNODE_SENSOR_ENDPOINT] = NODE_ENDPOINT("endpoint0",
> >> +						      &nodes[SWNODE_SENSOR_PORT],
> >> +						      sensor->ep_properties);
> >> +	nodes[SWNODE_CIO2_PORT] = NODE_PORT(port_names[ssdb->link],
> >> +					    &cio2_hid_node);
> >> +	nodes[SWNODE_CIO2_ENDPOINT] = NODE_ENDPOINT("endpoint0",
> >> +						    &nodes[SWNODE_CIO2_PORT],
> >> +						    sensor->cio2_properties);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static void cio2_bridge_unregister_sensors(void)
> >> +{
> >> +	struct sensor *sensor;
> >> +	int i;
> >> +
> >> +	for (i = 0; i < bridge.n_sensors; i++) {
> >> +		sensor = &bridge.sensors[i];
> >> +
> >> +		software_node_unregister_nodes_reverse(sensor->swnodes);
> >> +
> >> +		kfree(sensor->data_lanes);
> >> +
> >> +		put_device(sensor->dev);
> >> +		acpi_dev_put(sensor->adev);
> >> +	}
> >> +}
> >> +
> >> +static int connect_supported_devices(struct pci_dev *cio2)
> >> +{
> >> +	struct sensor_bios_data ssdb;
> >> +	struct fwnode_handle *fwnode;
> >> +	struct acpi_device *adev;
> >> +	struct sensor *sensor;
> >> +	struct device *dev;
> >> +	int i, ret;
> >> +
> >> +	ret = 0;
> >
> > You can initialize ret to 0 when declaring the variable.
>
> Is that ok on the same like as i's declaration, or should I split them?

I usually prefer splitting them, but that's a matter of personal taste I
suppose. This being said, as i should be an unsigned int, they will have
to be split anyway.

> >> +	for (i = 0; i < ARRAY_SIZE(supported_devices); i++) {
> >> +		adev = acpi_dev_get_first_match_dev(supported_devices[i], NULL, -1);
> >
> > What if there are multiple sensor of the same model ?
> 
> Hmm, yeah, that would be a bit of a pickle. I guess the newer
> smartphones have multiple sensors on the back, which I presume are the
> same model. So that will probably crop up at some point. How about
> instead I use bus_for_each_dev() and in the applied function check if
> the _HID is in the supported list?

Sounds good to me.

> >> +		if (!adev)
> >> +			continue;
> >> +
> >
> > Does acpi_dev_get_first_match_dev() skip disabled devices (as reported
> > by _STA) ?
>
> Yes.
>
> >> +		dev = bus_find_device_by_acpi_dev(&i2c_bus_type, adev);
> >> +		if (!dev) {
> >> +			ret = -EPROBE_DEFER;
> >> +			goto err_rollback;
> >> +		}
> >> +
> >> +		sensor = &bridge.sensors[bridge.n_sensors];
> >> +		sensor->dev = dev;
> >> +		sensor->adev = adev;
> >> +
> >> +		snprintf(sensor->name, ACPI_ID_LEN, "%s",
> >> +			 supported_devices[i]);
> >
> > How about strlcpy() ?
>
> Sure
>
> >> +
> >> +		ret = get_acpi_ssdb_sensor_data(dev, &ssdb);
> >> +		if (ret)
> >> +			goto err_free_dev;
> >> +
> >> +		ret = create_fwnode_properties(sensor, &ssdb);
> >> +		if (ret)
> >> +			goto err_free_dev;
> >> +
> >> +		ret = create_connection_swnodes(sensor, &ssdb);
> >> +		if (ret)
> >> +			goto err_free_dev;
> >> +
> >> +		ret = software_node_register_nodes(sensor->swnodes);
> >> +		if (ret)
> >> +			goto err_free_dev;
> >> +
> >> +		fwnode = software_node_fwnode(&sensor->swnodes[SWNODE_SENSOR_HID]);
> >> +		if (!fwnode) {
> >> +			ret = -ENODEV;
> >> +			goto err_free_swnodes;
> >> +		}
> >> +
> >> +		set_secondary_fwnode(dev, fwnode);
> >
> > I wonder if we could avoid depending on the I2C device being created by
> > getting the fwnode from adev, and setting ->secondary manually. adev
> > would need to be passed to get_acpi_ssdb_sensor_data() instead of dev.
>
> Let me try that; I initially wanted to do
> set_secondary_fwnode(&adev->dev, fwnode) to avoid depending on the I2C
> dev being created but it turns out &adev->dev isn't the same pointer. I
> shall try it and see.
>
> >> +
> >> +		dev_info(&cio2->dev, "Found supported device %s\n",
> >> +			 supported_devices[i]);
> >> +
> >> +		bridge.n_sensors++;
> >> +		continue;
> >> +	}
> >> +
> >> +	return ret;
> >> +
> >> +err_free_swnodes:
> >> +	software_node_unregister_nodes_reverse(sensor->swnodes);
> >> +err_free_dev:
> >> +	put_device(dev);
> >> +err_rollback:
> >> +	acpi_dev_put(adev);
> >
> > I think you'll leak sensor->data_lanes here. It won't be a problem if
> > you don't allocate it manually, as proposed above. I however wonder if
> > error handling couldn't be simplified by increasing bridge.n_sensors
> > earlier, and skipping cleanup in cio2_bridge_unregister_sensors() for
> > the fields that haven't been initialized (for instance kfree() is a
> > no-op on NULL pointers, so that's already handled).
>
> Errrr the only sticky bit there is the desire _not_ to unwind all the
> sensors if it managed to successfully connect one of them; if I'm just
> calling cio2_bridge_unregister_sensors() on any error then a failure for
> one sensor will result in no working cameras, where this way at least
> one of them might be available.

Good point. I expect this to be reworked anyway if we can stop depending
on the I2C device being created, there should be no need to defer
probing in that case.

> >> +
> >> +	/*
> >> +	 * If an iteration of this loop results in -EPROBE_DEFER then
> >> +	 * we need to roll back any sensors that were successfully
> >> +	 * registered. Any other error and we'll skip that step, as
> >> +	 * it seems better to have one successfully connected sensor.
> >> +	 */
> >> +
> >> +	if (ret == -EPROBE_DEFER)
> >> +		cio2_bridge_unregister_sensors();
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +int cio2_bridge_build(struct pci_dev *cio2)
> >> +{
> >> +	struct fwnode_handle *fwnode;
> >> +	int ret;
> >> +
> >> +	pci_dev_get(cio2);
> >> +
> >> +	ret = software_node_register(&cio2_hid_node);
> >> +	if (ret < 0) {
> >> +		dev_err(&cio2->dev, "Failed to register the CIO2 HID node\n");
> >> +		goto err_put_cio2;
> >> +	}
> >> +
> >> +	ret = connect_supported_devices(cio2);
> >> +	if (ret == -EPROBE_DEFER)
> >> +		goto err_unregister_cio2;
> >> +
> >> +	if (bridge.n_sensors == 0) {
> >> +		ret = -EPROBE_DEFER;
> >> +		goto err_unregister_cio2;
> >> +	}
> >> +
> >> +	dev_info(&cio2->dev, "Connected %d cameras\n", bridge.n_sensors);
> >> +
> >> +	fwnode = software_node_fwnode(&cio2_hid_node);
> >> +	if (!fwnode) {
> >> +		dev_err(&cio2->dev,
> >> +			"Error getting fwnode from cio2 software_node\n");
> >> +		ret = -ENODEV;
> >> +		goto err_unregister_sensors;
> >> +	}
> >> +
> >> +	set_secondary_fwnode(&cio2->dev, fwnode);
> >> +
> >> +	return 0;
> >> +
> >> +err_unregister_sensors:
> >> +	cio2_bridge_unregister_sensors();
> >> +err_unregister_cio2:
> >> +	software_node_unregister(&cio2_hid_node);
> >> +err_put_cio2:
> >> +	pci_dev_put(cio2);
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +void cio2_bridge_burn(struct pci_dev *cio2)
> >
> > Interesting function name :-) I like the creativity, but I think
> > consistency with the rest of the kernel code should unfortunately be
> > favoured.
> 
> Heh yep - already changed to _init/_clean per Andy's comments. Couldn't
> resist the pun!
> 
> >> +	struct sensor sensors[MAX_CONNECTED_DEVICES];
> >> +};
> >> +
> >> +/* Data representation as it is in ACPI SSDB buffer */
> >> +struct sensor_bios_data_packed {
> >
> > Similarly as above, I'd use a cio2_ prefix, and I think you can drop the
> > _packed suffix. How about naming it cio2_sensor_ssdb_data (or even just
> > cio2_sensor_ssdb) to make it clearer that it contains the SSDB data ?
>
> Already done (well, sensor_ssdb currently) to keep consistent with
> struct pmic_cldb that was introduced. I'll add the cio2 prefix.
>
> >> +	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];
> >> +} __packed__;
> >> +
> >> +/* Fields needed by bridge driver */
> >> +struct sensor_bios_data {
> >
> > And cio2_sensor_data ?
>
> Ack
>
> >> +	struct device *dev;
> >> +	u8 link;
> >> +	u8 lanes;
> >> +	u8 degree;
> >> +	u32 mclkspeed;
> >> +};
> >> +
> >> +#endif
> >> diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
> >> index f68ef0f6b..827457110 100644
> >> --- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
> >> +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
> >> @@ -1715,9 +1715,27 @@ static void cio2_queues_exit(struct cio2_device *cio2)
> >>  static int cio2_pci_probe(struct pci_dev *pci_dev,
> >>  			  const struct pci_device_id *id)
> >>  {
> >> +	struct fwnode_handle *endpoint;
> >>  	struct cio2_device *cio2;
> >>  	int r;
> >>  
> >> +	/*
> >> +	 * On some platforms no connections to sensors are defined in firmware,
> >> +	 * if the device has no endpoints then we can try to build those as
> >> +	 * software_nodes parsed from SSDB.
> >> +	 *
> >> +	 * This may EPROBE_DEFER if supported devices are found defined in ACPI
> >> +	 * but not yet ready for use (either not attached to the i2c bus yet,
> >> +	 * or not done probing themselves).
> >
> > Why do we need for the I2C devices to be probed, isn't it enough to have
> > them created ?
>
> Ooops  - a relic of the prior version that I missed out when cleaning up
> - I'll fix that
>
> > No need for an extra indentation level, neither here, not below.
> > NO need for this blank line.
>
> Both fixed - thanks very much
Sakari Ailus Oct. 24, 2020, 3:11 p.m. UTC | #8
Hi Laurent, Daniel,

On Sat, Oct 24, 2020 at 04:24:11AM +0300, Laurent Pinchart wrote:
> Hi Daniel,
> 
> Thank you for the patch.
> 
> On Mon, Oct 19, 2020 at 11:59:03PM +0100, Daniel Scally wrote:
> > Currently on platforms designed for Windows, connections between CIO2 and
> > sensors are not properly defined in DSDT. This patch extends the ipu3-cio2
> > driver to compensate by building software_node connections, parsing the
> > connection properties from the sensor's SSDB buffer.
> > 
> > Suggested-by: Jordan Hand <jorhand@linux.microsoft.com>
> > Signed-off-by: Daniel Scally <djrscally@gmail.com>
> > ---
> > Changes in v3:
> > 	- Rather than overwriting the device's primary fwnode, we now
> > 	simply assign a secondary. Some of the preceding patches alter the
> > 	existing driver code and v4l2 framework to allow for that.
> > 	- Rather than reprobe() the sensor after connecting the devices in
> > 	cio2-bridge we create the software_nodes right away. In this case,
> > 	sensor drivers will have to defer probing until they detect that a
> > 	fwnode graph is connecting them to the CIO2 device.
> > 	- Error paths in connect_supported_devices() moved outside the
> > 	loop
> > 	- Replaced pr_*() with dev_*() throughout
> > 	- Moved creation of software_node / property_entry arrays to stack
> > 	- A lot of formatting changes.
> > 
> >  MAINTAINERS                                   |   1 +
> >  drivers/media/pci/intel/ipu3/Kconfig          |  18 +
> >  drivers/media/pci/intel/ipu3/Makefile         |   3 +-
> >  drivers/media/pci/intel/ipu3/cio2-bridge.c    | 327 ++++++++++++++++++
> >  drivers/media/pci/intel/ipu3/cio2-bridge.h    |  94 +++++
> >  drivers/media/pci/intel/ipu3/ipu3-cio2-main.c |  21 ++
> >  drivers/media/pci/intel/ipu3/ipu3-cio2.h      |   9 +
> >  7 files changed, 472 insertions(+), 1 deletion(-)
> >  create mode 100644 drivers/media/pci/intel/ipu3/cio2-bridge.c
> >  create mode 100644 drivers/media/pci/intel/ipu3/cio2-bridge.h
> > 
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 5d768d5ad..4c9c646c7 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -8848,6 +8848,7 @@ INTEL IPU3 CSI-2 CIO2 DRIVER
> >  M:	Yong Zhi <yong.zhi@intel.com>
> >  M:	Sakari Ailus <sakari.ailus@linux.intel.com>
> >  M:	Bingbu Cao <bingbu.cao@intel.com>
> > +M:	Dan Scally <djrscally@gmail.com>
> >  R:	Tianshu Qiu <tian.shu.qiu@intel.com>
> >  L:	linux-media@vger.kernel.org
> >  S:	Maintained
> > diff --git a/drivers/media/pci/intel/ipu3/Kconfig b/drivers/media/pci/intel/ipu3/Kconfig
> > index 82d7f17e6..d14cbceae 100644
> > --- a/drivers/media/pci/intel/ipu3/Kconfig
> > +++ b/drivers/media/pci/intel/ipu3/Kconfig
> > @@ -16,3 +16,21 @@ config VIDEO_IPU3_CIO2
> >  	  Say Y or M here if you have a Skylake/Kaby Lake SoC with MIPI CSI-2
> >  	  connected camera.
> >  	  The module will be called ipu3-cio2.
> > +
> > +config CIO2_BRIDGE
> > +	bool "IPU3 CIO2 Sensors Bridge"
> > +	depends on VIDEO_IPU3_CIO2
> > +	help
> > +	  This extension provides an API for the ipu3-cio2 driver to create
> > +	  connections to cameras that are hidden in SSDB buffer in ACPI. It
> > +	  can be used to enable support for cameras in detachable / hybrid
> > +	  devices that ship with Windows.
> > +
> > +	  Say Y here if your device is a detachable / hybrid laptop that comes
> > +	  with Windows installed by the OEM, for example:
> > +
> > +	  	- Some Microsoft Surface models
> > +		- The Lenovo Miix line
> > +		- Dell 7285
> > +
> > +	  If in doubt, say N here.
> > diff --git a/drivers/media/pci/intel/ipu3/Makefile b/drivers/media/pci/intel/ipu3/Makefile
> > index b4e3266d9..933777e6e 100644
> > --- a/drivers/media/pci/intel/ipu3/Makefile
> > +++ b/drivers/media/pci/intel/ipu3/Makefile
> > @@ -1,4 +1,5 @@
> >  # SPDX-License-Identifier: GPL-2.0-only
> >  obj-$(CONFIG_VIDEO_IPU3_CIO2) += ipu3-cio2.o
> >  
> > -ipu3-cio2-y += ipu3-cio2-main.o
> > \ No newline at end of file
> > +ipu3-cio2-y += ipu3-cio2-main.o
> > +ipu3-cio2-$(CONFIG_CIO2_BRIDGE) += cio2-bridge.o
> > diff --git a/drivers/media/pci/intel/ipu3/cio2-bridge.c b/drivers/media/pci/intel/ipu3/cio2-bridge.c
> > new file mode 100644
> > index 000000000..bbe072f04
> > --- /dev/null
> > +++ b/drivers/media/pci/intel/ipu3/cio2-bridge.c
> > @@ -0,0 +1,327 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +// Author: Dan Scally <djrscally@gmail.com>
> > +#include <linux/acpi.h>
> > +#include <linux/device.h>
> > +#include <linux/fwnode.h>
> > +#include <linux/i2c.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/pci.h>
> > +#include <linux/property.h>
> > +#include <media/v4l2-subdev.h>
> > +
> > +#include "cio2-bridge.h"
> > +
> > +/*
> > + * Extend this array with ACPI Hardware ID's of devices known to be
> > + * working
> > + */
> > +static const char * const supported_devices[] = {
> > +	"INT33BE",
> > +	"OVTI2680",
> > +};
> > +
> > +static struct software_node cio2_hid_node = { CIO2_HID };
> > +
> > +static struct cio2_bridge bridge;
> 
> While there shouldn't be more than one CIO2 instance, we try to develop
> drivers in a way that avoids global per-device variables. Could all this
> be allocated dynamically, with the pointer returned by
> cio2_bridge_build() and stored in the cio2_device structure ?

I don't mind but the Windows ACPI table design assumes there's a single
CIO2. Thus the same assumption can be made here, too. Admittedly, I think
it could be cleaner that way.

...

> > +		dev = bus_find_device_by_acpi_dev(&i2c_bus_type, adev);
> > +		if (!dev) {
> > +			ret = -EPROBE_DEFER;
> > +			goto err_rollback;
> > +		}
> > +
> > +		sensor = &bridge.sensors[bridge.n_sensors];
> > +		sensor->dev = dev;
> > +		sensor->adev = adev;
> > +
> > +		snprintf(sensor->name, ACPI_ID_LEN, "%s",
> > +			 supported_devices[i]);
> 
> How about strlcpy() ?

Or even strscpy()?

> > +void cio2_bridge_burn(struct pci_dev *cio2)
> 
> Interesting function name :-) I like the creativity, but I think
> consistency with the rest of the kernel code should unfortunately be
> favoured.

I guess we can use any pairs that make sense. Create and destroy? Build and
plunder?
Sakari Ailus Oct. 24, 2020, 3:14 p.m. UTC | #9
Hi Daniel,

Thanks for the update.

On Mon, Oct 19, 2020 at 11:59:03PM +0100, Daniel Scally wrote:
> Currently on platforms designed for Windows, connections between CIO2 and
> sensors are not properly defined in DSDT. This patch extends the ipu3-cio2
> driver to compensate by building software_node connections, parsing the
> connection properties from the sensor's SSDB buffer.
> 
> Suggested-by: Jordan Hand <jorhand@linux.microsoft.com>
> Signed-off-by: Daniel Scally <djrscally@gmail.com>
> ---
> Changes in v3:
> 	- Rather than overwriting the device's primary fwnode, we now
> 	simply assign a secondary. Some of the preceding patches alter the
> 	existing driver code and v4l2 framework to allow for that.
> 	- Rather than reprobe() the sensor after connecting the devices in
> 	cio2-bridge we create the software_nodes right away. In this case,
> 	sensor drivers will have to defer probing until they detect that a
> 	fwnode graph is connecting them to the CIO2 device.
> 	- Error paths in connect_supported_devices() moved outside the
> 	loop
> 	- Replaced pr_*() with dev_*() throughout
> 	- Moved creation of software_node / property_entry arrays to stack
> 	- A lot of formatting changes.
> 
>  MAINTAINERS                                   |   1 +
>  drivers/media/pci/intel/ipu3/Kconfig          |  18 +
>  drivers/media/pci/intel/ipu3/Makefile         |   3 +-
>  drivers/media/pci/intel/ipu3/cio2-bridge.c    | 327 ++++++++++++++++++
>  drivers/media/pci/intel/ipu3/cio2-bridge.h    |  94 +++++
>  drivers/media/pci/intel/ipu3/ipu3-cio2-main.c |  21 ++
>  drivers/media/pci/intel/ipu3/ipu3-cio2.h      |   9 +
>  7 files changed, 472 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/media/pci/intel/ipu3/cio2-bridge.c
>  create mode 100644 drivers/media/pci/intel/ipu3/cio2-bridge.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 5d768d5ad..4c9c646c7 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -8848,6 +8848,7 @@ INTEL IPU3 CSI-2 CIO2 DRIVER
>  M:	Yong Zhi <yong.zhi@intel.com>
>  M:	Sakari Ailus <sakari.ailus@linux.intel.com>
>  M:	Bingbu Cao <bingbu.cao@intel.com>
> +M:	Dan Scally <djrscally@gmail.com>
>  R:	Tianshu Qiu <tian.shu.qiu@intel.com>
>  L:	linux-media@vger.kernel.org
>  S:	Maintained
> diff --git a/drivers/media/pci/intel/ipu3/Kconfig b/drivers/media/pci/intel/ipu3/Kconfig
> index 82d7f17e6..d14cbceae 100644
> --- a/drivers/media/pci/intel/ipu3/Kconfig
> +++ b/drivers/media/pci/intel/ipu3/Kconfig
> @@ -16,3 +16,21 @@ config VIDEO_IPU3_CIO2
>  	  Say Y or M here if you have a Skylake/Kaby Lake SoC with MIPI CSI-2
>  	  connected camera.
>  	  The module will be called ipu3-cio2.
> +
> +config CIO2_BRIDGE
> +	bool "IPU3 CIO2 Sensors Bridge"
> +	depends on VIDEO_IPU3_CIO2
> +	help
> +	  This extension provides an API for the ipu3-cio2 driver to create
> +	  connections to cameras that are hidden in SSDB buffer in ACPI. It
> +	  can be used to enable support for cameras in detachable / hybrid
> +	  devices that ship with Windows.
> +
> +	  Say Y here if your device is a detachable / hybrid laptop that comes
> +	  with Windows installed by the OEM, for example:
> +
> +	  	- Some Microsoft Surface models
> +		- The Lenovo Miix line
> +		- Dell 7285
> +
> +	  If in doubt, say N here.
> diff --git a/drivers/media/pci/intel/ipu3/Makefile b/drivers/media/pci/intel/ipu3/Makefile
> index b4e3266d9..933777e6e 100644
> --- a/drivers/media/pci/intel/ipu3/Makefile
> +++ b/drivers/media/pci/intel/ipu3/Makefile
> @@ -1,4 +1,5 @@
>  # SPDX-License-Identifier: GPL-2.0-only
>  obj-$(CONFIG_VIDEO_IPU3_CIO2) += ipu3-cio2.o
>  
> -ipu3-cio2-y += ipu3-cio2-main.o
> \ No newline at end of file
> +ipu3-cio2-y += ipu3-cio2-main.o
> +ipu3-cio2-$(CONFIG_CIO2_BRIDGE) += cio2-bridge.o
> diff --git a/drivers/media/pci/intel/ipu3/cio2-bridge.c b/drivers/media/pci/intel/ipu3/cio2-bridge.c
> new file mode 100644
> index 000000000..bbe072f04
> --- /dev/null
> +++ b/drivers/media/pci/intel/ipu3/cio2-bridge.c
> @@ -0,0 +1,327 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Author: Dan Scally <djrscally@gmail.com>

/* Author: ... */

But not the SPDX tag.

> +#include <linux/acpi.h>
> +#include <linux/device.h>
> +#include <linux/fwnode.h>
> +#include <linux/i2c.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/property.h>
> +#include <media/v4l2-subdev.h>
> +
> +#include "cio2-bridge.h"
> +
> +/*
> + * Extend this array with ACPI Hardware ID's of devices known to be
> + * working
> + */
> +static const char * const supported_devices[] = {
> +	"INT33BE",
> +	"OVTI2680",
> +};
> +
> +static struct software_node cio2_hid_node = { CIO2_HID };
> +
> +static struct cio2_bridge bridge;
> +
> +static const char * const port_names[] = {
> +	"port0", "port1", "port2", "port3"
> +};
> +
> +static const struct property_entry remote_endpoints[] = {

How about another dimension, for local and remote? Or make it a struct with
local and remote fields. Perhaps a struct would be better?

This could also be nicer to initialise in a function.

> +	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]),
> +};
> +
> +static int read_acpi_block(struct device *dev, char *id, void *data, u32 size)
> +{
> +	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
> +	struct acpi_handle *handle;
> +	union acpi_object *obj;
> +	acpi_status status;
> +	int ret;
> +
> +	handle = ACPI_HANDLE(dev);
> +
> +	status = acpi_evaluate_object(handle, id, NULL, &buffer);
> +	if (ACPI_FAILURE(status))
> +		return -ENODEV;
> +
> +	obj = buffer.pointer;
> +	if (!obj) {
> +		dev_err(dev, "Couldn't locate ACPI buffer\n");
> +		return -ENODEV;
> +	}
> +
> +	if (obj->type != ACPI_TYPE_BUFFER) {
> +		dev_err(dev, "Couldn't read ACPI buffer\n");
> +		ret = -ENODEV;
> +		goto out_free_buff;
> +	}
> +
> +	if (obj->buffer.length > size) {
> +		dev_err(dev, "Given buffer is too small\n");
> +		ret = -ENODEV;
> +		goto out_free_buff;
> +	}
> +
> +	memcpy(data, obj->buffer.pointer, obj->buffer.length);
> +	ret = obj->buffer.length;
> +
> +out_free_buff:
> +	kfree(buffer.pointer);
> +	return ret;
> +}
> +
> +static int get_acpi_ssdb_sensor_data(struct device *dev,
> +				     struct sensor_bios_data *sensor)
> +{
> +	struct sensor_bios_data_packed sensor_data;
> +	int ret;
> +
> +	ret = read_acpi_block(dev, "SSDB", &sensor_data, sizeof(sensor_data));
> +	if (ret < 0)
> +		return ret;
> +
> +	sensor->link = sensor_data.link;
> +	sensor->lanes = sensor_data.lanes;
> +	sensor->mclkspeed = sensor_data.mclkspeed;
> +	sensor->degree = sensor_data.degree;
> +
> +	return 0;
> +}
> +
> +static int create_fwnode_properties(struct sensor *sensor,
> +				    struct sensor_bios_data *ssdb)
> +{
> +	struct property_entry *cio2_properties = sensor->cio2_properties;
> +	struct property_entry *dev_properties = sensor->dev_properties;
> +	struct property_entry *ep_properties = sensor->ep_properties;
> +	int i;
> +
> +	/* device fwnode properties */
> +	memset(dev_properties, 0, sizeof(struct property_entry) * 3);

I'd put them all to the struct itself. Then the compiler will be able to
check array indices.

> +
> +	dev_properties[0] = PROPERTY_ENTRY_U32("clock-frequency",
> +					       ssdb->mclkspeed);
> +	dev_properties[1] = PROPERTY_ENTRY_U8("rotation", ssdb->degree);
> +
> +	/* endpoint fwnode properties */
> +	memset(ep_properties, 0, sizeof(struct property_entry) * 4);
> +
> +	sensor->data_lanes = kmalloc_array(ssdb->lanes, sizeof(u32),
> +					   GFP_KERNEL);
> +
> +	if (!sensor->data_lanes)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < ssdb->lanes; i++)
> +		sensor->data_lanes[i] = i + 1;
> +
> +	ep_properties[0] = PROPERTY_ENTRY_U32("bus-type", 5);
> +	ep_properties[1] = PROPERTY_ENTRY_U32_ARRAY_LEN("data-lanes",
> +							sensor->data_lanes,
> +							ssdb->lanes);
> +	ep_properties[2] = remote_endpoints[(bridge.n_sensors * 2) + ENDPOINT_SENSOR];
> +
> +	/* cio2 endpoint props */
> +	memset(cio2_properties, 0, sizeof(struct property_entry) * 3);
> +
> +	cio2_properties[0] = PROPERTY_ENTRY_U32_ARRAY_LEN("data-lanes",
> +							  sensor->data_lanes,
> +							  ssdb->lanes);
> +	cio2_properties[1] = remote_endpoints[(bridge.n_sensors * 2) + ENDPOINT_CIO2];
> +
> +	return 0;
> +}
> +
> +static int create_connection_swnodes(struct sensor *sensor,
> +				     struct sensor_bios_data *ssdb)
> +{
> +	struct software_node *nodes = sensor->swnodes;
> +
> +	memset(nodes, 0, sizeof(struct software_node) * 6);

Could you make the index an enum, and add an item to the end used to tell
the number of entries. It could be called e.g. NR_OF_SENSOR_SWNODES.

> +
> +	nodes[SWNODE_SENSOR_HID] = NODE_SENSOR(sensor->name,
> +					       sensor->dev_properties);
> +	nodes[SWNODE_SENSOR_PORT] = NODE_PORT("port0",
> +					      &nodes[SWNODE_SENSOR_HID]);
> +	nodes[SWNODE_SENSOR_ENDPOINT] = NODE_ENDPOINT("endpoint0",
> +						      &nodes[SWNODE_SENSOR_PORT],
> +						      sensor->ep_properties);
> +	nodes[SWNODE_CIO2_PORT] = NODE_PORT(port_names[ssdb->link],
> +					    &cio2_hid_node);
> +	nodes[SWNODE_CIO2_ENDPOINT] = NODE_ENDPOINT("endpoint0",
> +						    &nodes[SWNODE_CIO2_PORT],
> +						    sensor->cio2_properties);
> +
> +	return 0;
> +}
> +
> +static void cio2_bridge_unregister_sensors(void)
> +{
> +	struct sensor *sensor;
> +	int i;
> +
> +	for (i = 0; i < bridge.n_sensors; i++) {
> +		sensor = &bridge.sensors[i];
> +
> +		software_node_unregister_nodes_reverse(sensor->swnodes);
> +
> +		kfree(sensor->data_lanes);
> +
> +		put_device(sensor->dev);
> +		acpi_dev_put(sensor->adev);
> +	}
> +}
> +
> +static int connect_supported_devices(struct pci_dev *cio2)
> +{
> +	struct sensor_bios_data ssdb;
> +	struct fwnode_handle *fwnode;
> +	struct acpi_device *adev;
> +	struct sensor *sensor;
> +	struct device *dev;
> +	int i, ret;
> +
> +	ret = 0;

In declaration.

> +	for (i = 0; i < ARRAY_SIZE(supported_devices); i++) {
> +		adev = acpi_dev_get_first_match_dev(supported_devices[i], NULL, -1);

Please wrap no later than at 80.

> +		if (!adev)
> +			continue;
> +
> +		dev = bus_find_device_by_acpi_dev(&i2c_bus_type, adev);
> +		if (!dev) {
> +			ret = -EPROBE_DEFER;
> +			goto err_rollback;
> +		}
> +
> +		sensor = &bridge.sensors[bridge.n_sensors];
> +		sensor->dev = dev;
> +		sensor->adev = adev;
> +
> +		snprintf(sensor->name, ACPI_ID_LEN, "%s",

sizeof(sensor->name)

> +			 supported_devices[i]);
> +
> +		ret = get_acpi_ssdb_sensor_data(dev, &ssdb);
> +		if (ret)
> +			goto err_free_dev;
> +
> +		ret = create_fwnode_properties(sensor, &ssdb);
> +		if (ret)
> +			goto err_free_dev;
> +
> +		ret = create_connection_swnodes(sensor, &ssdb);
> +		if (ret)
> +			goto err_free_dev;
> +
> +		ret = software_node_register_nodes(sensor->swnodes);
> +		if (ret)
> +			goto err_free_dev;
> +
> +		fwnode = software_node_fwnode(&sensor->swnodes[SWNODE_SENSOR_HID]);
> +		if (!fwnode) {
> +			ret = -ENODEV;
> +			goto err_free_swnodes;
> +		}
> +
> +		set_secondary_fwnode(dev, fwnode);
> +
> +		dev_info(&cio2->dev, "Found supported device %s\n",
> +			 supported_devices[i]);
> +
> +		bridge.n_sensors++;
> +		continue;
> +	}
> +
> +	return ret;
> +
> +err_free_swnodes:
> +	software_node_unregister_nodes_reverse(sensor->swnodes);
> +err_free_dev:
> +	put_device(dev);
> +err_rollback:
> +	acpi_dev_put(adev);
> +
> +	/*
> +	 * If an iteration of this loop results in -EPROBE_DEFER then
> +	 * we need to roll back any sensors that were successfully
> +	 * registered. Any other error and we'll skip that step, as
> +	 * it seems better to have one successfully connected sensor.
> +	 */
> +
> +	if (ret == -EPROBE_DEFER)
> +		cio2_bridge_unregister_sensors();
> +
> +	return ret;
> +}
> +
> +int cio2_bridge_build(struct pci_dev *cio2)
> +{
> +	struct fwnode_handle *fwnode;
> +	int ret;
> +
> +	pci_dev_get(cio2);

Could you check that this isn't used by more than one user? The current
implementation assumes that. I'm not sure if there could be more instances
of CIO2 but if there were, that'd be an issue currently.

> +
> +	ret = software_node_register(&cio2_hid_node);
> +	if (ret < 0) {
> +		dev_err(&cio2->dev, "Failed to register the CIO2 HID node\n");
> +		goto err_put_cio2;
> +	}
> +
> +	ret = connect_supported_devices(cio2);
> +	if (ret == -EPROBE_DEFER)
> +		goto err_unregister_cio2;
> +
> +	if (bridge.n_sensors == 0) {
> +		ret = -EPROBE_DEFER;
> +		goto err_unregister_cio2;
> +	}
> +
> +	dev_info(&cio2->dev, "Connected %d cameras\n", bridge.n_sensors);
> +
> +	fwnode = software_node_fwnode(&cio2_hid_node);
> +	if (!fwnode) {
> +		dev_err(&cio2->dev,
> +			"Error getting fwnode from cio2 software_node\n");
> +		ret = -ENODEV;
> +		goto err_unregister_sensors;
> +	}
> +
> +	set_secondary_fwnode(&cio2->dev, fwnode);
> +
> +	return 0;
> +
> +err_unregister_sensors:
> +	cio2_bridge_unregister_sensors();
> +err_unregister_cio2:
> +	software_node_unregister(&cio2_hid_node);
> +err_put_cio2:
> +	pci_dev_put(cio2);
> +
> +	return ret;
> +}
> +
> +void cio2_bridge_burn(struct pci_dev *cio2)
> +{
> +	pci_dev_put(cio2);
> +
> +	cio2_bridge_unregister_sensors();
> +
> +	software_node_unregister(&cio2_hid_node);
> +}
> diff --git a/drivers/media/pci/intel/ipu3/cio2-bridge.h b/drivers/media/pci/intel/ipu3/cio2-bridge.h
> new file mode 100644
> index 000000000..077354ca8
> --- /dev/null
> +++ b/drivers/media/pci/intel/ipu3/cio2-bridge.h
> @@ -0,0 +1,94 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Author: Dan Scally <djrscally@gmail.com> */
> +#ifndef __CIO2_BRIDGE_H
> +#define __CIO2_BRIDGE_H
> +
> +#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_SENSOR(_HID, _PROPS)		\
> +	((const struct software_node) {		\
> +		.name = _HID,			\
> +		.properties = _PROPS,		\
> +	})
> +
> +#define NODE_PORT(_PORT, _SENSOR_NODE)		\
> +	((const struct software_node) {		\
> +		_PORT,				\
> +		_SENSOR_NODE,			\
> +	})
> +
> +#define NODE_ENDPOINT(_EP, _PORT, _PROPS)	\
> +	((const struct software_node) {		\
> +		_EP,				\
> +		_PORT,				\
> +		_PROPS,				\
> +	})
> +
> +struct sensor {

How about calling this e.g. cio2_sensor? sensor is rather generic.

> +	char name[ACPI_ID_LEN];
> +	struct device *dev;
> +	struct acpi_device *adev;
> +	struct software_node swnodes[6];
> +	struct property_entry dev_properties[3];
> +	struct property_entry ep_properties[4];
> +	struct property_entry cio2_properties[3];
> +	u32 *data_lanes;

The maximum is four so you could as well make this static.

> +};
> +
> +struct cio2_bridge {
> +	int n_sensors;

Do you need negative values? %u, too, if not.

> +	struct sensor sensors[MAX_CONNECTED_DEVICES];
> +};
> +
> +/* 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];
> +} __packed__;
> +
> +/* Fields needed by bridge driver */
> +struct sensor_bios_data {
> +	struct device *dev;
> +	u8 link;
> +	u8 lanes;
> +	u8 degree;
> +	u32 mclkspeed;
> +};
> +
> +#endif
> diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
> index f68ef0f6b..827457110 100644
> --- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
> +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
> @@ -1715,9 +1715,27 @@ static void cio2_queues_exit(struct cio2_device *cio2)
>  static int cio2_pci_probe(struct pci_dev *pci_dev,
>  			  const struct pci_device_id *id)
>  {
> +	struct fwnode_handle *endpoint;
>  	struct cio2_device *cio2;
>  	int r;
>  
> +	/*
> +	 * On some platforms no connections to sensors are defined in firmware,
> +	 * if the device has no endpoints then we can try to build those as
> +	 * software_nodes parsed from SSDB.
> +	 *
> +	 * This may EPROBE_DEFER if supported devices are found defined in ACPI
> +	 * but not yet ready for use (either not attached to the i2c bus yet,
> +	 * or not done probing themselves).
> +	 */
> +
> +	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&pci_dev->dev), NULL);
> +	if (!endpoint) {
> +		r = cio2_bridge_build(pci_dev);
> +		if (r)
> +			return r;
> +	}

} else {
	fwnode_handle_put(endpoint);
}

> +
>  	cio2 = devm_kzalloc(&pci_dev->dev, sizeof(*cio2), GFP_KERNEL);
>  	if (!cio2)
>  		return -ENOMEM;
> @@ -1825,6 +1843,9 @@ static void cio2_pci_remove(struct pci_dev *pci_dev)
>  {
>  	struct cio2_device *cio2 = pci_get_drvdata(pci_dev);
>  
> +	if (is_software_node(dev_fwnode(&pci_dev->dev)))
> +		cio2_bridge_burn(pci_dev);
> +
>  	media_device_unregister(&cio2->media_dev);
>  	v4l2_async_notifier_unregister(&cio2->notifier);
>  	v4l2_async_notifier_cleanup(&cio2->notifier);
> diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.h b/drivers/media/pci/intel/ipu3/ipu3-cio2.h
> index 549b08f88..80a081d7e 100644
> --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.h
> +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.h
> @@ -436,4 +436,13 @@ static inline struct cio2_queue *vb2q_to_cio2_queue(struct vb2_queue *vq)
>  	return container_of(vq, struct cio2_queue, vbq);
>  }
>  
> +#if IS_ENABLED(CONFIG_CIO2_BRIDGE)
> +	int cio2_bridge_build(struct pci_dev *cio2);
> +	void cio2_bridge_burn(struct pci_dev *cio2);
> +#else
> +
> +	int cio2_bridge_build(struct pci_dev *cio2) { return 0; }
> +	void cio2_bridge_burn(struct pci_dev *cio2) { }
> +#endif
> +
>  #endif
Daniel Scally Oct. 24, 2020, 8:28 p.m. UTC | #10
On 24/10/2020 16:14, Sakari Ailus wrote:
> Hi Daniel,
>
> Thanks for the update.
Thanks for the comments as always
>> +// SPDX-License-Identifier: GPL-2.0
>> +// Author: Dan Scally <djrscally@gmail.com>
> /* Author: ... */
>
> But not the SPDX tag.
Weird - okedokey
>> +#include <linux/acpi.h>
>> +#include <linux/device.h>
>> +#include <linux/fwnode.h>
>> +#include <linux/i2c.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/pci.h>
>> +#include <linux/property.h>
>> +#include <media/v4l2-subdev.h>
>> +
>> +#include "cio2-bridge.h"
>> +
>> +/*
>> + * Extend this array with ACPI Hardware ID's of devices known to be
>> + * working
>> + */
>> +static const char * const supported_devices[] = {
>> +	"INT33BE",
>> +	"OVTI2680",
>> +};
>> +
>> +static struct software_node cio2_hid_node = { CIO2_HID };
>> +
>> +static struct cio2_bridge bridge;
>> +
>> +static const char * const port_names[] = {
>> +	"port0", "port1", "port2", "port3"
>> +};
>> +
>> +static const struct property_entry remote_endpoints[] = {
> How about another dimension, for local and remote? Or make it a struct with
> local and remote fields. Perhaps a struct would be better?
>
> This could also be nicer to initialise in a function.
Sure; a struct probably would be cleaner I agree. I shall make that change
>> +static int create_fwnode_properties(struct sensor *sensor,
>> +				    struct sensor_bios_data *ssdb)
>> +{
>> +	struct property_entry *cio2_properties = sensor->cio2_properties;
>> +	struct property_entry *dev_properties = sensor->dev_properties;
>> +	struct property_entry *ep_properties = sensor->ep_properties;
>> +	int i;
>> +
>> +	/* device fwnode properties */
>> +	memset(dev_properties, 0, sizeof(struct property_entry) * 3);
> I'd put them all to the struct itself. Then the compiler will be able to
> check array indices.
Makes sense; I think I was just trying to save line length in the rest
of that function by that.
>> +
>> +	dev_properties[0] = PROPERTY_ENTRY_U32("clock-frequency",
>> +					       ssdb->mclkspeed);
>> +	dev_properties[1] = PROPERTY_ENTRY_U8("rotation", ssdb->degree);
>> +
>> +	/* endpoint fwnode properties */
>> +	memset(ep_properties, 0, sizeof(struct property_entry) * 4);
>> +
>> +	sensor->data_lanes = kmalloc_array(ssdb->lanes, sizeof(u32),
>> +					   GFP_KERNEL);
>> +
>> +	if (!sensor->data_lanes)
>> +		return -ENOMEM;
>> +
>> +	for (i = 0; i < ssdb->lanes; i++)
>> +		sensor->data_lanes[i] = i + 1;
>> +
>> +	ep_properties[0] = PROPERTY_ENTRY_U32("bus-type", 5);
>> +	ep_properties[1] = PROPERTY_ENTRY_U32_ARRAY_LEN("data-lanes",
>> +							sensor->data_lanes,
>> +							ssdb->lanes);
>> +	ep_properties[2] = remote_endpoints[(bridge.n_sensors * 2) + ENDPOINT_SENSOR];
>> +
>> +	/* cio2 endpoint props */
>> +	memset(cio2_properties, 0, sizeof(struct property_entry) * 3);
>> +
>> +	cio2_properties[0] = PROPERTY_ENTRY_U32_ARRAY_LEN("data-lanes",
>> +							  sensor->data_lanes,
>> +							  ssdb->lanes);
>> +	cio2_properties[1] = remote_endpoints[(bridge.n_sensors * 2) + ENDPOINT_CIO2];
>> +
>> +	return 0;
>> +}
>> +
>> +static int create_connection_swnodes(struct sensor *sensor,
>> +				     struct sensor_bios_data *ssdb)
>> +{
>> +	struct software_node *nodes = sensor->swnodes;
>> +
>> +	memset(nodes, 0, sizeof(struct software_node) * 6);
> Could you make the index an enum, and add an item to the end used to tell
> the number of entries. It could be called e.g. NR_OF_SENSOR_SWNODES.
Ooh I like that, it's neat; thanks - will do.
>> +int cio2_bridge_build(struct pci_dev *cio2)
>> +{
>> +	struct fwnode_handle *fwnode;
>> +	int ret;
>> +
>> +	pci_dev_get(cio2);
> Could you check that this isn't used by more than one user? The current
> implementation assumes that. I'm not sure if there could be more instances
> of CIO2 but if there were, that'd be an issue currently.

I can check; can't think of anything better than just failing out if it
turns out to be in use already though - any ideas or is that appropriate?

>> +struct sensor {
> How about calling this e.g. cio2_sensor? sensor is rather generic.
Yup, will probably prefix all such generically named vars with cio2_*
and functions with cio2_bridge_*().
>> +	char name[ACPI_ID_LEN];
>> +	struct device *dev;
>> +	struct acpi_device *adev;
>> +	struct software_node swnodes[6];
>> +	struct property_entry dev_properties[3];
>> +	struct property_entry ep_properties[4];
>> +	struct property_entry cio2_properties[3];
>> +	u32 *data_lanes;
> The maximum is four so you could as well make this static.
Ack
>> +};
>> +
>> +struct cio2_bridge {
>> +	int n_sensors;
> Do you need negative values? %u, too, if not.
I do not, I will switch to using unsigned.
>> diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
>> index f68ef0f6b..827457110 100644
>> --- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
>> +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
>> @@ -1715,9 +1715,27 @@ static void cio2_queues_exit(struct cio2_device *cio2)
>>  static int cio2_pci_probe(struct pci_dev *pci_dev,
>>  			  const struct pci_device_id *id)
>>  {
>> +	struct fwnode_handle *endpoint;
>>  	struct cio2_device *cio2;
>>  	int r;
>>  
>> +	/*
>> +	 * On some platforms no connections to sensors are defined in firmware,
>> +	 * if the device has no endpoints then we can try to build those as
>> +	 * software_nodes parsed from SSDB.
>> +	 *
>> +	 * This may EPROBE_DEFER if supported devices are found defined in ACPI
>> +	 * but not yet ready for use (either not attached to the i2c bus yet,
>> +	 * or not done probing themselves).
>> +	 */
>> +
>> +	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&pci_dev->dev), NULL);
>> +	if (!endpoint) {
>> +		r = cio2_bridge_build(pci_dev);
>> +		if (r)
>> +			return r;
>> +	}
> } else {
> 	fwnode_handle_put(endpoint);
> }
Ah, of course, thank you.
Daniel Scally Oct. 24, 2020, 10:28 p.m. UTC | #11
Hi Laurent

On 24/10/2020 10:37, Laurent Pinchart wrote:
>
>>> I wonder if we could avoid depending on the I2C device being created by
>>> getting the fwnode from adev, and setting ->secondary manually. adev
>>> would need to be passed to get_acpi_ssdb_sensor_data() instead of dev.
>> Let me try that; I initially wanted to do
>> set_secondary_fwnode(&adev->dev, fwnode) to avoid depending on the I2C
>> dev being created but it turns out &adev->dev isn't the same pointer. I
>> shall try it and see.


Actually, thinking on this further I think maybe we can't avoid that -
it's not actually in this patch series but during assigning GPIO
resources parsed from PMIC's ACPI node to the sensor, I'm using
dev_name() on the i2c dev to pass to .dev_id member of gpiod_lookup_table
Laurent Pinchart Oct. 24, 2020, 10:36 p.m. UTC | #12
Hi Dan,

On Sat, Oct 24, 2020 at 11:28:06PM +0100, Daniel Scally wrote:
> On 24/10/2020 10:37, Laurent Pinchart wrote:
> >
> >>> I wonder if we could avoid depending on the I2C device being created by
> >>> getting the fwnode from adev, and setting ->secondary manually. adev
> >>> would need to be passed to get_acpi_ssdb_sensor_data() instead of dev.
> >> Let me try that; I initially wanted to do
> >> set_secondary_fwnode(&adev->dev, fwnode) to avoid depending on the I2C
> >> dev being created but it turns out &adev->dev isn't the same pointer. I
> >> shall try it and see.
> 
> Actually, thinking on this further I think maybe we can't avoid that -
> it's not actually in this patch series but during assigning GPIO
> resources parsed from PMIC's ACPI node to the sensor, I'm using
> dev_name() on the i2c dev to pass to .dev_id member of gpiod_lookup_table

Any chance we can construct the I2C device name from the ACPI device,
the same way that the ACPI/I2C core does ? It may be a dead end, but if
we could avoid depending on the I2C device, I think it will make
initialization easier. I have a feeling that will be difficult though,
as we'll need the I2C bus number, which won't be readily available.

Maybe this calls for extending the gpiod lookup API, but that would then
likely be something we would build on top. I'm thinking about a way to
specify the GPIO mapping in the software node, and retrieving it from
there, the same way this is done for GPIOs in OF-based systems.
Daniel Scally Oct. 24, 2020, 10:50 p.m. UTC | #13
Hi Laurent

On 24/10/2020 23:36, Laurent Pinchart wrote:
> Hi Dan,
>
> On Sat, Oct 24, 2020 at 11:28:06PM +0100, Daniel Scally wrote:
>> On 24/10/2020 10:37, Laurent Pinchart wrote:
>>
>>>>> I wonder if we could avoid depending on the I2C device being created by
>>>>> getting the fwnode from adev, and setting ->secondary manually. adev
>>>>> would need to be passed to get_acpi_ssdb_sensor_data() instead of dev.
>>>> Let me try that; I initially wanted to do
>>>> set_secondary_fwnode(&adev->dev, fwnode) to avoid depending on the I2C
>>>> dev being created but it turns out &adev->dev isn't the same pointer. I
>>>> shall try it and see.
>> Actually, thinking on this further I think maybe we can't avoid that -
>> it's not actually in this patch series but during assigning GPIO
>> resources parsed from PMIC's ACPI node to the sensor, I'm using
>> dev_name() on the i2c dev to pass to .dev_id member of gpiod_lookup_table
> Any chance we can construct the I2C device name from the ACPI device,
> the same way that the ACPI/I2C core does ? It may be a dead end, but if
> we could avoid depending on the I2C device, I think it will make
> initialization easier. I have a feeling that will be difficult though,
> as we'll need the I2C bus number, which won't be readily available.

I'd have to look into how the ACPI/I2C core does it to be confident, but
I think it would be ok. Doesn't look to me like the bus number is
involved; my sensor's device names come through as "i2c-OVTI2680:00";
that's just a prefix tacked on to dev_name(adev->dev). I'm sure there's
something that will stop it being quite so easy, but hopefully not
insurmountable.

> Maybe this calls for extending the gpiod lookup API, but that would then
> likely be something we would build on top. I'm thinking about a way to
> specify the GPIO mapping in the software node, and retrieving it from
> there, the same way this is done for GPIOs in OF-based systems.
Yeah that's an option too; I had had the same thought actually. Probably
an extensive piece of work in its own right though
Sakari Ailus Oct. 25, 2020, 11:18 a.m. UTC | #14
Hi Daniel,

On Sat, Oct 24, 2020 at 09:28:07PM +0100, Dan Scally wrote:
...
> >> +int cio2_bridge_build(struct pci_dev *cio2)
> >> +{
> >> +	struct fwnode_handle *fwnode;
> >> +	int ret;
> >> +
> >> +	pci_dev_get(cio2);
> > Could you check that this isn't used by more than one user? The current
> > implementation assumes that. I'm not sure if there could be more instances
> > of CIO2 but if there were, that'd be an issue currently.
> 
> I can check; can't think of anything better than just failing out if it
> turns out to be in use already though - any ideas or is that appropriate?

A negative error code would be appropriate, e.g. -EBUSY.
Daniel Scally Oct. 26, 2020, 8:20 a.m. UTC | #15
On 24/10/2020 23:36, Laurent Pinchart wrote:
> Hi Dan,
>
> On Sat, Oct 24, 2020 at 11:28:06PM +0100, Daniel Scally wrote:
>> On 24/10/2020 10:37, Laurent Pinchart wrote:
>>>>> I wonder if we could avoid depending on the I2C device being created by
>>>>> getting the fwnode from adev, and setting ->secondary manually. adev
>>>>> would need to be passed to get_acpi_ssdb_sensor_data() instead of dev.
>>>> Let me try that; I initially wanted to do
>>>> set_secondary_fwnode(&adev->dev, fwnode) to avoid depending on the I2C
>>>> dev being created but it turns out &adev->dev isn't the same pointer. I
>>>> shall try it and see.
>> Actually, thinking on this further I think maybe we can't avoid that -
>> it's not actually in this patch series but during assigning GPIO
>> resources parsed from PMIC's ACPI node to the sensor, I'm using
>> dev_name() on the i2c dev to pass to .dev_id member of gpiod_lookup_table
> Any chance we can construct the I2C device name from the ACPI device,
> the same way that the ACPI/I2C core does ? It may be a dead end, but if
> we could avoid depending on the I2C device, I think it will make
> initialization easier. I have a feeling that will be difficult though,
> as we'll need the I2C bus number, which won't be readily available.
OK yeah; the i2c core does indeed just prefix "i2c-" onto the acpi
device name, so I will make this change too.
Andy Shevchenko Oct. 26, 2020, 4:05 p.m. UTC | #16
On Mon, Oct 26, 2020 at 08:20:14AM +0000, Dan Scally wrote:
> On 24/10/2020 23:36, Laurent Pinchart wrote:
> > On Sat, Oct 24, 2020 at 11:28:06PM +0100, Daniel Scally wrote:
> >> On 24/10/2020 10:37, Laurent Pinchart wrote:
> >>>>> I wonder if we could avoid depending on the I2C device being created by
> >>>>> getting the fwnode from adev, and setting ->secondary manually. adev
> >>>>> would need to be passed to get_acpi_ssdb_sensor_data() instead of dev.
> >>>> Let me try that; I initially wanted to do
> >>>> set_secondary_fwnode(&adev->dev, fwnode) to avoid depending on the I2C
> >>>> dev being created but it turns out &adev->dev isn't the same pointer. I
> >>>> shall try it and see.
> >> Actually, thinking on this further I think maybe we can't avoid that -
> >> it's not actually in this patch series but during assigning GPIO
> >> resources parsed from PMIC's ACPI node to the sensor, I'm using
> >> dev_name() on the i2c dev to pass to .dev_id member of gpiod_lookup_table
> > Any chance we can construct the I2C device name from the ACPI device,
> > the same way that the ACPI/I2C core does ? It may be a dead end, but if
> > we could avoid depending on the I2C device, I think it will make
> > initialization easier. I have a feeling that will be difficult though,
> > as we'll need the I2C bus number, which won't be readily available.
> OK yeah; the i2c core does indeed just prefix "i2c-" onto the acpi
> device name, so I will make this change too.

This is part of the I²C core and if you go this way, do not home grow the
functionality that doesn't belong here.

Instead, export a helper function that will do it for you and for I²C core with
explanation why it's needed.
Andy Shevchenko Oct. 26, 2020, 4:10 p.m. UTC | #17
On Sat, Oct 24, 2020 at 12:37:02PM +0300, Laurent Pinchart wrote:
> On Sat, Oct 24, 2020 at 09:50:07AM +0100, Dan Scally wrote:
> > On 24/10/2020 02:24, Laurent Pinchart wrote:
> > > On Mon, Oct 19, 2020 at 11:59:03PM +0100, Daniel Scally wrote:

> > >> +		adev = acpi_dev_get_first_match_dev(supported_devices[i], NULL, -1);
> > >
> > > What if there are multiple sensor of the same model ?
> > 
> > Hmm, yeah, that would be a bit of a pickle. I guess the newer
> > smartphones have multiple sensors on the back, which I presume are the
> > same model. So that will probably crop up at some point. How about
> > instead I use bus_for_each_dev() and in the applied function check if
> > the _HID is in the supported list?
> 
> Sounds good to me.
> 
> > >> +		if (!adev)
> > >> +			continue;

Please, don't.

If we have so weird ACPI tables it must be w/a differently. The all, even badly
formed, ACPI tables I have seen so far are using _UID to distinguish instance
of the device (see second parameter to the above function).

If we meet the very broken table I would like rather to know about, then
silently think ahead what could be best.

I.o.w. don't change this until we will have a real example of the problematic
firmware.
Laurent Pinchart Oct. 29, 2020, 8:17 p.m. UTC | #18
Hi Andy,

On Mon, Oct 26, 2020 at 06:05:49PM +0200, Andy Shevchenko wrote:
> On Mon, Oct 26, 2020 at 08:20:14AM +0000, Dan Scally wrote:
> > On 24/10/2020 23:36, Laurent Pinchart wrote:
> > > On Sat, Oct 24, 2020 at 11:28:06PM +0100, Daniel Scally wrote:
> > >> On 24/10/2020 10:37, Laurent Pinchart wrote:
> > >>>>> I wonder if we could avoid depending on the I2C device being created by
> > >>>>> getting the fwnode from adev, and setting ->secondary manually. adev
> > >>>>> would need to be passed to get_acpi_ssdb_sensor_data() instead of dev.
> > >>>>
> > >>>> Let me try that; I initially wanted to do
> > >>>> set_secondary_fwnode(&adev->dev, fwnode) to avoid depending on the I2C
> > >>>> dev being created but it turns out &adev->dev isn't the same pointer. I
> > >>>> shall try it and see.
> > >>
> > >> Actually, thinking on this further I think maybe we can't avoid that -
> > >> it's not actually in this patch series but during assigning GPIO
> > >> resources parsed from PMIC's ACPI node to the sensor, I'm using
> > >> dev_name() on the i2c dev to pass to .dev_id member of gpiod_lookup_table
> > >
> > > Any chance we can construct the I2C device name from the ACPI device,
> > > the same way that the ACPI/I2C core does ? It may be a dead end, but if
> > > we could avoid depending on the I2C device, I think it will make
> > > initialization easier. I have a feeling that will be difficult though,
> > > as we'll need the I2C bus number, which won't be readily available.
> >
> > OK yeah; the i2c core does indeed just prefix "i2c-" onto the acpi
> > device name, so I will make this change too.
> 
> This is part of the I²C core and if you go this way, do not home grow the
> functionality that doesn't belong here.
> 
> Instead, export a helper function that will do it for you and for I²C core with
> explanation why it's needed.

Agreed, I was actually considering suggesting that. Hardcoding the same
naming scheme in two places is asking for trouble, especially if we
don't let the I2C maintainers know. It should be easy to do, not
necessarily the highest priority task, but something that should be
handled to get this merged.
Laurent Pinchart Oct. 29, 2020, 8:19 p.m. UTC | #19
Hi Andy,

On Mon, Oct 26, 2020 at 06:10:50PM +0200, Andy Shevchenko wrote:
> On Sat, Oct 24, 2020 at 12:37:02PM +0300, Laurent Pinchart wrote:
> > On Sat, Oct 24, 2020 at 09:50:07AM +0100, Dan Scally wrote:
> > > On 24/10/2020 02:24, Laurent Pinchart wrote:
> > > > On Mon, Oct 19, 2020 at 11:59:03PM +0100, Daniel Scally wrote:
> 
> > > >> +		adev = acpi_dev_get_first_match_dev(supported_devices[i], NULL, -1);
> > > >
> > > > What if there are multiple sensor of the same model ?
> > > 
> > > Hmm, yeah, that would be a bit of a pickle. I guess the newer
> > > smartphones have multiple sensors on the back, which I presume are the
> > > same model. So that will probably crop up at some point. How about
> > > instead I use bus_for_each_dev() and in the applied function check if
> > > the _HID is in the supported list?
> > 
> > Sounds good to me.
> > 
> > > >> +		if (!adev)
> > > >> +			continue;
> 
> Please, don't.
> 
> If we have so weird ACPI tables it must be w/a differently. The all, even badly
> formed, ACPI tables I have seen so far are using _UID to distinguish instance
> of the device (see second parameter to the above function).
> 
> If we meet the very broken table I would like rather to know about, then
> silently think ahead what could be best.
> 
> I.o.w. don't change this until we will have a real example of the problematic
> firmware.

I'm not sure to follow you. Daniel's current code loops over all the
supported HID (as stored in the supported_devices table), and then gets
the first ACPI device for each of them. If multiple ACPI devices exist
with the same HID, we need to handle them all, so enumerating all ACPI
devices and checking whether their HID is one we handle seems to be the
right option to me.
Andy Shevchenko Oct. 29, 2020, 8:26 p.m. UTC | #20
On Thu, Oct 29, 2020 at 10:21 PM Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
> On Mon, Oct 26, 2020 at 06:10:50PM +0200, Andy Shevchenko wrote:
> > On Sat, Oct 24, 2020 at 12:37:02PM +0300, Laurent Pinchart wrote:
> > > On Sat, Oct 24, 2020 at 09:50:07AM +0100, Dan Scally wrote:
> > > > On 24/10/2020 02:24, Laurent Pinchart wrote:
> > > > > On Mon, Oct 19, 2020 at 11:59:03PM +0100, Daniel Scally wrote:
> >
> > > > >> +              adev = acpi_dev_get_first_match_dev(supported_devices[i], NULL, -1);
> > > > >
> > > > > What if there are multiple sensor of the same model ?
> > > >
> > > > Hmm, yeah, that would be a bit of a pickle. I guess the newer
> > > > smartphones have multiple sensors on the back, which I presume are the
> > > > same model. So that will probably crop up at some point. How about
> > > > instead I use bus_for_each_dev() and in the applied function check if
> > > > the _HID is in the supported list?
> > >
> > > Sounds good to me.
> > >
> > > > >> +              if (!adev)
> > > > >> +                      continue;
> >
> > Please, don't.
> >
> > If we have so weird ACPI tables it must be w/a differently. The all, even badly
> > formed, ACPI tables I have seen so far are using _UID to distinguish instance
> > of the device (see second parameter to the above function).
> >
> > If we meet the very broken table I would like rather to know about, then
> > silently think ahead what could be best.
> >
> > I.o.w. don't change this until we will have a real example of the problematic
> > firmware.
>
> I'm not sure to follow you. Daniel's current code loops over all the
> supported HID (as stored in the supported_devices table), and then gets
> the first ACPI device for each of them. If multiple ACPI devices exist
> with the same HID, we need to handle them all, so enumerating all ACPI
> devices and checking whether their HID is one we handle seems to be the
> right option to me.

Devices with the same HID should be still different by another
parameter in ACPI. The above mentioned call just uses the rough
estimation for relaxed conditions. If you expect more than one device
with the same HID how do you expect to distinguish them? The correct
way is to use _UID. It may be absent, or set to a value. And this
value should be unique (as per U letter in UID abbreviation). That
said, the above is good enough till we find the firmware with the
above true (several devices with the same HID). Until then the code is
fine.
Laurent Pinchart Oct. 29, 2020, 9:29 p.m. UTC | #21
On Thu, Oct 29, 2020 at 10:26:56PM +0200, Andy Shevchenko wrote:
> On Thu, Oct 29, 2020 at 10:21 PM Laurent Pinchart wrote:
> > On Mon, Oct 26, 2020 at 06:10:50PM +0200, Andy Shevchenko wrote:
> > > On Sat, Oct 24, 2020 at 12:37:02PM +0300, Laurent Pinchart wrote:
> > > > On Sat, Oct 24, 2020 at 09:50:07AM +0100, Dan Scally wrote:
> > > > > On 24/10/2020 02:24, Laurent Pinchart wrote:
> > > > > > On Mon, Oct 19, 2020 at 11:59:03PM +0100, Daniel Scally wrote:
> > >
> > > > > >> +              adev = acpi_dev_get_first_match_dev(supported_devices[i], NULL, -1);
> > > > > >
> > > > > > What if there are multiple sensor of the same model ?
> > > > >
> > > > > Hmm, yeah, that would be a bit of a pickle. I guess the newer
> > > > > smartphones have multiple sensors on the back, which I presume are the
> > > > > same model. So that will probably crop up at some point. How about
> > > > > instead I use bus_for_each_dev() and in the applied function check if
> > > > > the _HID is in the supported list?
> > > >
> > > > Sounds good to me.
> > > >
> > > > > >> +              if (!adev)
> > > > > >> +                      continue;
> > >
> > > Please, don't.
> > >
> > > If we have so weird ACPI tables it must be w/a differently. The all, even badly
> > > formed, ACPI tables I have seen so far are using _UID to distinguish instance
> > > of the device (see second parameter to the above function).
> > >
> > > If we meet the very broken table I would like rather to know about, then
> > > silently think ahead what could be best.
> > >
> > > I.o.w. don't change this until we will have a real example of the problematic
> > > firmware.
> >
> > I'm not sure to follow you. Daniel's current code loops over all the
> > supported HID (as stored in the supported_devices table), and then gets
> > the first ACPI device for each of them. If multiple ACPI devices exist
> > with the same HID, we need to handle them all, so enumerating all ACPI
> > devices and checking whether their HID is one we handle seems to be the
> > right option to me.
> 
> Devices with the same HID should be still different by another
> parameter in ACPI. The above mentioned call just uses the rough
> estimation for relaxed conditions. If you expect more than one device
> with the same HID how do you expect to distinguish them? The correct
> way is to use _UID. It may be absent, or set to a value. And this
> value should be unique (as per U letter in UID abbreviation). That
> said, the above is good enough till we find the firmware with the
> above true (several devices with the same HID). Until then the code is
> fine.

I expect those devices with the same _HID to have different _UID values,
yes. On the systems I've seen so far, that assumption is not violated,
and I don't think we need to already plan how we will support systems
where multiple devices would have the same _HID and _UID (within the
same scope). There's no disagreement there.

My point is that supported_devices stores HID values, and doesn't care
about UID. The code loops over supported_devices, and for each entry,
calls acpi_dev_get_first_match_dev() and process the ACPI devices
returned by that call. We thus process at most one ACPI device per HID,
which isn't right.
Andy Shevchenko Oct. 29, 2020, 10:22 p.m. UTC | #22
On Thu, Oct 29, 2020 at 11:29:30PM +0200, Laurent Pinchart wrote:
> On Thu, Oct 29, 2020 at 10:26:56PM +0200, Andy Shevchenko wrote:
> > On Thu, Oct 29, 2020 at 10:21 PM Laurent Pinchart wrote:
> > > On Mon, Oct 26, 2020 at 06:10:50PM +0200, Andy Shevchenko wrote:
> > > > On Sat, Oct 24, 2020 at 12:37:02PM +0300, Laurent Pinchart wrote:
> > > > > On Sat, Oct 24, 2020 at 09:50:07AM +0100, Dan Scally wrote:
> > > > > > On 24/10/2020 02:24, Laurent Pinchart wrote:
> > > > > > > On Mon, Oct 19, 2020 at 11:59:03PM +0100, Daniel Scally wrote:
> > > >
> > > > > > >> +              adev = acpi_dev_get_first_match_dev(supported_devices[i], NULL, -1);
> > > > > > >
> > > > > > > What if there are multiple sensor of the same model ?
> > > > > >
> > > > > > Hmm, yeah, that would be a bit of a pickle. I guess the newer
> > > > > > smartphones have multiple sensors on the back, which I presume are the
> > > > > > same model. So that will probably crop up at some point. How about
> > > > > > instead I use bus_for_each_dev() and in the applied function check if
> > > > > > the _HID is in the supported list?
> > > > >
> > > > > Sounds good to me.
> > > > >
> > > > > > >> +              if (!adev)
> > > > > > >> +                      continue;
> > > >
> > > > Please, don't.
> > > >
> > > > If we have so weird ACPI tables it must be w/a differently. The all, even badly
> > > > formed, ACPI tables I have seen so far are using _UID to distinguish instance
> > > > of the device (see second parameter to the above function).
> > > >
> > > > If we meet the very broken table I would like rather to know about, then
> > > > silently think ahead what could be best.
> > > >
> > > > I.o.w. don't change this until we will have a real example of the problematic
> > > > firmware.
> > >
> > > I'm not sure to follow you. Daniel's current code loops over all the
> > > supported HID (as stored in the supported_devices table), and then gets
> > > the first ACPI device for each of them. If multiple ACPI devices exist
> > > with the same HID, we need to handle them all, so enumerating all ACPI
> > > devices and checking whether their HID is one we handle seems to be the
> > > right option to me.
> > 
> > Devices with the same HID should be still different by another
> > parameter in ACPI. The above mentioned call just uses the rough
> > estimation for relaxed conditions. If you expect more than one device
> > with the same HID how do you expect to distinguish them? The correct
> > way is to use _UID. It may be absent, or set to a value. And this
> > value should be unique (as per U letter in UID abbreviation). That
> > said, the above is good enough till we find the firmware with the
> > above true (several devices with the same HID). Until then the code is
> > fine.
> 
> I expect those devices with the same _HID to have different _UID values,
> yes. On the systems I've seen so far, that assumption is not violated,
> and I don't think we need to already plan how we will support systems
> where multiple devices would have the same _HID and _UID (within the
> same scope). There's no disagreement there.
> 
> My point is that supported_devices stores HID values, and doesn't care
> about UID. The code loops over supported_devices, and for each entry,
> calls acpi_dev_get_first_match_dev() and process the ACPI devices
> returned by that call. We thus process at most one ACPI device per HID,
> which isn't right.

In this case we probably need something like

struct acpi_device *
acpi_dev_get_next_match_dev(struct acpi_device *adev,
			    const char *hid, const char *uid, s64 hrv)
{
	struct device *start = adev ? &adev->dev : NULL;
	...
	dev = bus_find_device(&acpi_bus_type, start, &match, acpi_dev_match_cb);
	...
}

in drivers/acpi/utils.c and

static inline struct acpi_device *
acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv)
{
	return acpi_dev_get_next_match_dev(NULL, hid, uid, hrv);
}

in include/linux/acpi.h.

Then we may add

#define for_each_acpi_dev_match(adev, hid, uid, hrv)			\
	for (adev = acpi_dev_get_first_match_dev(hid, uid, hrv);	\
	     adev;							\
	     adev = acpi_dev_get_next_match_dev(adev, hid, uid, hrv))
Daniel Scally Oct. 29, 2020, 10:36 p.m. UTC | #23
Hi both

On 29/10/2020 20:17, Laurent Pinchart wrote:
> Hi Andy,
>
> On Mon, Oct 26, 2020 at 06:05:49PM +0200, Andy Shevchenko wrote:
>> On Mon, Oct 26, 2020 at 08:20:14AM +0000, Dan Scally wrote:
>>> On 24/10/2020 23:36, Laurent Pinchart wrote:
>>>> On Sat, Oct 24, 2020 at 11:28:06PM +0100, Daniel Scally wrote:
>>>>> On 24/10/2020 10:37, Laurent Pinchart wrote:
>>>>>>>> I wonder if we could avoid depending on the I2C device being created by
>>>>>>>> getting the fwnode from adev, and setting ->secondary manually. adev
>>>>>>>> would need to be passed to get_acpi_ssdb_sensor_data() instead of dev.
>>>>>>> Let me try that; I initially wanted to do
>>>>>>> set_secondary_fwnode(&adev->dev, fwnode) to avoid depending on the I2C
>>>>>>> dev being created but it turns out &adev->dev isn't the same pointer. I
>>>>>>> shall try it and see.
>>>>> Actually, thinking on this further I think maybe we can't avoid that -
>>>>> it's not actually in this patch series but during assigning GPIO
>>>>> resources parsed from PMIC's ACPI node to the sensor, I'm using
>>>>> dev_name() on the i2c dev to pass to .dev_id member of gpiod_lookup_table
>>>> Any chance we can construct the I2C device name from the ACPI device,
>>>> the same way that the ACPI/I2C core does ? It may be a dead end, but if
>>>> we could avoid depending on the I2C device, I think it will make
>>>> initialization easier. I have a feeling that will be difficult though,
>>>> as we'll need the I2C bus number, which won't be readily available.
>>> OK yeah; the i2c core does indeed just prefix "i2c-" onto the acpi
>>> device name, so I will make this change too.
>> This is part of the I²C core and if you go this way, do not home grow the
>> functionality that doesn't belong here.
>>
>> Instead, export a helper function that will do it for you and for I²C core with
>> explanation why it's needed.
> Agreed, I was actually considering suggesting that. Hardcoding the same
> naming scheme in two places is asking for trouble, especially if we
> don't let the I2C maintainers know. It should be easy to do, not
> necessarily the highest priority task, but something that should be
> handled to get this merged.
Understood - I'll have a look at the best way to do this once I'm
through the current to do list
Laurent Pinchart Oct. 29, 2020, 10:51 p.m. UTC | #24
Hi Andy,

On Fri, Oct 30, 2020 at 12:22:15AM +0200, Andy Shevchenko wrote:
> On Thu, Oct 29, 2020 at 11:29:30PM +0200, Laurent Pinchart wrote:
> > On Thu, Oct 29, 2020 at 10:26:56PM +0200, Andy Shevchenko wrote:
> > > On Thu, Oct 29, 2020 at 10:21 PM Laurent Pinchart wrote:
> > > > On Mon, Oct 26, 2020 at 06:10:50PM +0200, Andy Shevchenko wrote:
> > > > > On Sat, Oct 24, 2020 at 12:37:02PM +0300, Laurent Pinchart wrote:
> > > > > > On Sat, Oct 24, 2020 at 09:50:07AM +0100, Dan Scally wrote:
> > > > > > > On 24/10/2020 02:24, Laurent Pinchart wrote:
> > > > > > > > On Mon, Oct 19, 2020 at 11:59:03PM +0100, Daniel Scally wrote:
> > > > >
> > > > > > > >> +              adev = acpi_dev_get_first_match_dev(supported_devices[i], NULL, -1);
> > > > > > > >
> > > > > > > > What if there are multiple sensor of the same model ?
> > > > > > >
> > > > > > > Hmm, yeah, that would be a bit of a pickle. I guess the newer
> > > > > > > smartphones have multiple sensors on the back, which I presume are the
> > > > > > > same model. So that will probably crop up at some point. How about
> > > > > > > instead I use bus_for_each_dev() and in the applied function check if
> > > > > > > the _HID is in the supported list?
> > > > > >
> > > > > > Sounds good to me.
> > > > > >
> > > > > > > >> +              if (!adev)
> > > > > > > >> +                      continue;
> > > > >
> > > > > Please, don't.
> > > > >
> > > > > If we have so weird ACPI tables it must be w/a differently. The all, even badly
> > > > > formed, ACPI tables I have seen so far are using _UID to distinguish instance
> > > > > of the device (see second parameter to the above function).
> > > > >
> > > > > If we meet the very broken table I would like rather to know about, then
> > > > > silently think ahead what could be best.
> > > > >
> > > > > I.o.w. don't change this until we will have a real example of the problematic
> > > > > firmware.
> > > >
> > > > I'm not sure to follow you. Daniel's current code loops over all the
> > > > supported HID (as stored in the supported_devices table), and then gets
> > > > the first ACPI device for each of them. If multiple ACPI devices exist
> > > > with the same HID, we need to handle them all, so enumerating all ACPI
> > > > devices and checking whether their HID is one we handle seems to be the
> > > > right option to me.
> > > 
> > > Devices with the same HID should be still different by another
> > > parameter in ACPI. The above mentioned call just uses the rough
> > > estimation for relaxed conditions. If you expect more than one device
> > > with the same HID how do you expect to distinguish them? The correct
> > > way is to use _UID. It may be absent, or set to a value. And this
> > > value should be unique (as per U letter in UID abbreviation). That
> > > said, the above is good enough till we find the firmware with the
> > > above true (several devices with the same HID). Until then the code is
> > > fine.
> > 
> > I expect those devices with the same _HID to have different _UID values,
> > yes. On the systems I've seen so far, that assumption is not violated,
> > and I don't think we need to already plan how we will support systems
> > where multiple devices would have the same _HID and _UID (within the
> > same scope). There's no disagreement there.
> > 
> > My point is that supported_devices stores HID values, and doesn't care
> > about UID. The code loops over supported_devices, and for each entry,
> > calls acpi_dev_get_first_match_dev() and process the ACPI devices
> > returned by that call. We thus process at most one ACPI device per HID,
> > which isn't right.
> 
> In this case we probably need something like
> 
> struct acpi_device *
> acpi_dev_get_next_match_dev(struct acpi_device *adev,
> 			    const char *hid, const char *uid, s64 hrv)
> {
> 	struct device *start = adev ? &adev->dev : NULL;
> 	...
> 	dev = bus_find_device(&acpi_bus_type, start, &match, acpi_dev_match_cb);
> 	...
> }
> 
> in drivers/acpi/utils.c and
> 
> static inline struct acpi_device *
> acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv)
> {
> 	return acpi_dev_get_next_match_dev(NULL, hid, uid, hrv);
> }
> 
> in include/linux/acpi.h.
> 
> Then we may add
> 
> #define for_each_acpi_dev_match(adev, hid, uid, hrv)			\
> 	for (adev = acpi_dev_get_first_match_dev(hid, uid, hrv);	\
> 	     adev;							\
> 	     adev = acpi_dev_get_next_match_dev(adev, hid, uid, hrv))

What the cio2-bridge code needs is indeed

	for each hid in supported hids:
		for each acpi device that is compatible with hid:
			...

which could also be expressed as

	for each acpi device:
		if acpi device hid is in supported hids:
			...

I don't mind either option, I'll happily follow the preference of the
ACPI maintainers.
Daniel Scally Nov. 13, 2020, 10:02 a.m. UTC | #25
On 29/10/2020 22:51, Laurent Pinchart wrote:
> Hi Andy,
>
> On Fri, Oct 30, 2020 at 12:22:15AM +0200, Andy Shevchenko wrote:
>> On Thu, Oct 29, 2020 at 11:29:30PM +0200, Laurent Pinchart wrote:
>>> On Thu, Oct 29, 2020 at 10:26:56PM +0200, Andy Shevchenko wrote:
>>>> On Thu, Oct 29, 2020 at 10:21 PM Laurent Pinchart wrote:
>>>>> On Mon, Oct 26, 2020 at 06:10:50PM +0200, Andy Shevchenko wrote:
>>>>>> On Sat, Oct 24, 2020 at 12:37:02PM +0300, Laurent Pinchart wrote:
>>>>>>> On Sat, Oct 24, 2020 at 09:50:07AM +0100, Dan Scally wrote:
>>>>>>>> On 24/10/2020 02:24, Laurent Pinchart wrote:
>>>>>>>>> On Mon, Oct 19, 2020 at 11:59:03PM +0100, Daniel Scally wrote:
>>>>>>>>>> +              adev = acpi_dev_get_first_match_dev(supported_devices[i], NULL, -1);
>>>>>>>>> What if there are multiple sensor of the same model ?
>>>>>>>> Hmm, yeah, that would be a bit of a pickle. I guess the newer
>>>>>>>> smartphones have multiple sensors on the back, which I presume are the
>>>>>>>> same model. So that will probably crop up at some point. How about
>>>>>>>> instead I use bus_for_each_dev() and in the applied function check if
>>>>>>>> the _HID is in the supported list?
>>>>>>> Sounds good to me.
>>>>>>>
>>>>>>>>>> +              if (!adev)
>>>>>>>>>> +                      continue;
>>>>>> Please, don't.
>>>>>>
>>>>>> If we have so weird ACPI tables it must be w/a differently. The all, even badly
>>>>>> formed, ACPI tables I have seen so far are using _UID to distinguish instance
>>>>>> of the device (see second parameter to the above function).
>>>>>>
>>>>>> If we meet the very broken table I would like rather to know about, then
>>>>>> silently think ahead what could be best.
>>>>>>
>>>>>> I.o.w. don't change this until we will have a real example of the problematic
>>>>>> firmware.
>>>>> I'm not sure to follow you. Daniel's current code loops over all the
>>>>> supported HID (as stored in the supported_devices table), and then gets
>>>>> the first ACPI device for each of them. If multiple ACPI devices exist
>>>>> with the same HID, we need to handle them all, so enumerating all ACPI
>>>>> devices and checking whether their HID is one we handle seems to be the
>>>>> right option to me.
>>>> Devices with the same HID should be still different by another
>>>> parameter in ACPI. The above mentioned call just uses the rough
>>>> estimation for relaxed conditions. If you expect more than one device
>>>> with the same HID how do you expect to distinguish them? The correct
>>>> way is to use _UID. It may be absent, or set to a value. And this
>>>> value should be unique (as per U letter in UID abbreviation). That
>>>> said, the above is good enough till we find the firmware with the
>>>> above true (several devices with the same HID). Until then the code is
>>>> fine.
>>> I expect those devices with the same _HID to have different _UID values,
>>> yes. On the systems I've seen so far, that assumption is not violated,
>>> and I don't think we need to already plan how we will support systems
>>> where multiple devices would have the same _HID and _UID (within the
>>> same scope). There's no disagreement there.
>>>
>>> My point is that supported_devices stores HID values, and doesn't care
>>> about UID. The code loops over supported_devices, and for each entry,
>>> calls acpi_dev_get_first_match_dev() and process the ACPI devices
>>> returned by that call. We thus process at most one ACPI device per HID,
>>> which isn't right.
>> In this case we probably need something like
>>
>> struct acpi_device *
>> acpi_dev_get_next_match_dev(struct acpi_device *adev,
>> 			    const char *hid, const char *uid, s64 hrv)
>> {
>> 	struct device *start = adev ? &adev->dev : NULL;
>> 	...
>> 	dev = bus_find_device(&acpi_bus_type, start, &match, acpi_dev_match_cb);
>> 	...
>> }
>>
>> in drivers/acpi/utils.c and
>>
>> static inline struct acpi_device *
>> acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv)
>> {
>> 	return acpi_dev_get_next_match_dev(NULL, hid, uid, hrv);
>> }
>>
>> in include/linux/acpi.h.
>>
>> Then we may add
>>
>> #define for_each_acpi_dev_match(adev, hid, uid, hrv)			\
>> 	for (adev = acpi_dev_get_first_match_dev(hid, uid, hrv);	\
>> 	     adev;							\
>> 	     adev = acpi_dev_get_next_match_dev(adev, hid, uid, hrv))
> What the cio2-bridge code needs is indeed
>
> 	for each hid in supported hids:
> 		for each acpi device that is compatible with hid:
> 			...
>
> which could also be expressed as
>
> 	for each acpi device:
> 		if acpi device hid is in supported hids:
> 			...
>
> I don't mind either option, I'll happily follow the preference of the
> ACPI maintainers.
>
Does this need raising elsewhere then? The original idea of just
bus_for_each_dev(&acpi_bus_type...) I have now tested and it works fine,
but it does mean that I need to export acpi_bus_type (currently that
symbol's not available)...that seems much simpler to me but I'm not sure
whether that's something to avoid, and if so whether Andy's approach is
better.


Thoughts?
Laurent Pinchart Nov. 13, 2020, 4:22 p.m. UTC | #26
Hi Dan,

On Fri, Nov 13, 2020 at 10:02:30AM +0000, Dan Scally wrote:
> On 29/10/2020 22:51, Laurent Pinchart wrote:
> > On Fri, Oct 30, 2020 at 12:22:15AM +0200, Andy Shevchenko wrote:
> >> On Thu, Oct 29, 2020 at 11:29:30PM +0200, Laurent Pinchart wrote:
> >>> On Thu, Oct 29, 2020 at 10:26:56PM +0200, Andy Shevchenko wrote:
> >>>> On Thu, Oct 29, 2020 at 10:21 PM Laurent Pinchart wrote:
> >>>>> On Mon, Oct 26, 2020 at 06:10:50PM +0200, Andy Shevchenko wrote:
> >>>>>> On Sat, Oct 24, 2020 at 12:37:02PM +0300, Laurent Pinchart wrote:
> >>>>>>> On Sat, Oct 24, 2020 at 09:50:07AM +0100, Dan Scally wrote:
> >>>>>>>> On 24/10/2020 02:24, Laurent Pinchart wrote:
> >>>>>>>>> On Mon, Oct 19, 2020 at 11:59:03PM +0100, Daniel Scally wrote:
> >>>>>>>>>> +              adev = acpi_dev_get_first_match_dev(supported_devices[i], NULL, -1);
> >>>>>>>>> What if there are multiple sensor of the same model ?
> >>>>>>>> Hmm, yeah, that would be a bit of a pickle. I guess the newer
> >>>>>>>> smartphones have multiple sensors on the back, which I presume are the
> >>>>>>>> same model. So that will probably crop up at some point. How about
> >>>>>>>> instead I use bus_for_each_dev() and in the applied function check if
> >>>>>>>> the _HID is in the supported list?
> >>>>>>> Sounds good to me.
> >>>>>>>
> >>>>>>>>>> +              if (!adev)
> >>>>>>>>>> +                      continue;
> >>>>>> Please, don't.
> >>>>>>
> >>>>>> If we have so weird ACPI tables it must be w/a differently. The all, even badly
> >>>>>> formed, ACPI tables I have seen so far are using _UID to distinguish instance
> >>>>>> of the device (see second parameter to the above function).
> >>>>>>
> >>>>>> If we meet the very broken table I would like rather to know about, then
> >>>>>> silently think ahead what could be best.
> >>>>>>
> >>>>>> I.o.w. don't change this until we will have a real example of the problematic
> >>>>>> firmware.
> >>>>> I'm not sure to follow you. Daniel's current code loops over all the
> >>>>> supported HID (as stored in the supported_devices table), and then gets
> >>>>> the first ACPI device for each of them. If multiple ACPI devices exist
> >>>>> with the same HID, we need to handle them all, so enumerating all ACPI
> >>>>> devices and checking whether their HID is one we handle seems to be the
> >>>>> right option to me.
> >>>> Devices with the same HID should be still different by another
> >>>> parameter in ACPI. The above mentioned call just uses the rough
> >>>> estimation for relaxed conditions. If you expect more than one device
> >>>> with the same HID how do you expect to distinguish them? The correct
> >>>> way is to use _UID. It may be absent, or set to a value. And this
> >>>> value should be unique (as per U letter in UID abbreviation). That
> >>>> said, the above is good enough till we find the firmware with the
> >>>> above true (several devices with the same HID). Until then the code is
> >>>> fine.
> >>> I expect those devices with the same _HID to have different _UID values,
> >>> yes. On the systems I've seen so far, that assumption is not violated,
> >>> and I don't think we need to already plan how we will support systems
> >>> where multiple devices would have the same _HID and _UID (within the
> >>> same scope). There's no disagreement there.
> >>>
> >>> My point is that supported_devices stores HID values, and doesn't care
> >>> about UID. The code loops over supported_devices, and for each entry,
> >>> calls acpi_dev_get_first_match_dev() and process the ACPI devices
> >>> returned by that call. We thus process at most one ACPI device per HID,
> >>> which isn't right.
> >>
> >> In this case we probably need something like
> >>
> >> struct acpi_device *
> >> acpi_dev_get_next_match_dev(struct acpi_device *adev,
> >> 			    const char *hid, const char *uid, s64 hrv)
> >> {
> >> 	struct device *start = adev ? &adev->dev : NULL;
> >> 	...
> >> 	dev = bus_find_device(&acpi_bus_type, start, &match, acpi_dev_match_cb);
> >> 	...
> >> }
> >>
> >> in drivers/acpi/utils.c and
> >>
> >> static inline struct acpi_device *
> >> acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv)
> >> {
> >> 	return acpi_dev_get_next_match_dev(NULL, hid, uid, hrv);
> >> }
> >>
> >> in include/linux/acpi.h.
> >>
> >> Then we may add
> >>
> >> #define for_each_acpi_dev_match(adev, hid, uid, hrv)			\
> >> 	for (adev = acpi_dev_get_first_match_dev(hid, uid, hrv);	\
> >> 	     adev;							\
> >> 	     adev = acpi_dev_get_next_match_dev(adev, hid, uid, hrv))
> >
> > What the cio2-bridge code needs is indeed
> >
> > 	for each hid in supported hids:
> > 		for each acpi device that is compatible with hid:
> > 			...
> >
> > which could also be expressed as
> >
> > 	for each acpi device:
> > 		if acpi device hid is in supported hids:
> > 			...
> >
> > I don't mind either option, I'll happily follow the preference of the
> > ACPI maintainers.
>
> Does this need raising elsewhere then? The original idea of just
> bus_for_each_dev(&acpi_bus_type...) I have now tested and it works fine,
> but it does mean that I need to export acpi_bus_type (currently that
> symbol's not available)...that seems much simpler to me but I'm not sure
> whether that's something to avoid, and if so whether Andy's approach is
> better.
> 
> Thoughts?

I like simple options :-) A patch to export acpi_bus_type, with enough
context in the commit message (and in the cover latter of the series),
should be enough to provide all the information the ACPI maintainers
need to decide which option is best. With a bit of luck that patch will
be considered the best option and no extra work will be needed.
Andy Shevchenko Nov. 13, 2020, 7:45 p.m. UTC | #27
On Fri, Nov 13, 2020 at 6:22 PM Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
> On Fri, Nov 13, 2020 at 10:02:30AM +0000, Dan Scally wrote:
> > On 29/10/2020 22:51, Laurent Pinchart wrote:
> > > On Fri, Oct 30, 2020 at 12:22:15AM +0200, Andy Shevchenko wrote:
> > >> On Thu, Oct 29, 2020 at 11:29:30PM +0200, Laurent Pinchart wrote:

...

> > >> In this case we probably need something like
> > >>
> > >> struct acpi_device *
> > >> acpi_dev_get_next_match_dev(struct acpi_device *adev,
> > >>                        const char *hid, const char *uid, s64 hrv)
> > >> {
> > >>    struct device *start = adev ? &adev->dev : NULL;
> > >>    ...
> > >>    dev = bus_find_device(&acpi_bus_type, start, &match, acpi_dev_match_cb);
> > >>    ...
> > >> }
> > >>
> > >> in drivers/acpi/utils.c and
> > >>
> > >> static inline struct acpi_device *
> > >> acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv)
> > >> {
> > >>    return acpi_dev_get_next_match_dev(NULL, hid, uid, hrv);
> > >> }
> > >>
> > >> in include/linux/acpi.h.
> > >>
> > >> Then we may add
> > >>
> > >> #define for_each_acpi_dev_match(adev, hid, uid, hrv)                       \
> > >>    for (adev = acpi_dev_get_first_match_dev(hid, uid, hrv);        \
> > >>         adev;                                                      \
> > >>         adev = acpi_dev_get_next_match_dev(adev, hid, uid, hrv))
> > >
> > > What the cio2-bridge code needs is indeed
> > >
> > >     for each hid in supported hids:
> > >             for each acpi device that is compatible with hid:
> > >                     ...
> > >
> > > which could also be expressed as
> > >
> > >     for each acpi device:
> > >             if acpi device hid is in supported hids:
> > >                     ...
> > >
> > > I don't mind either option, I'll happily follow the preference of the
> > > ACPI maintainers.
> >
> > Does this need raising elsewhere then? The original idea of just
> > bus_for_each_dev(&acpi_bus_type...) I have now tested and it works fine,
> > but it does mean that I need to export acpi_bus_type (currently that
> > symbol's not available)...that seems much simpler to me but I'm not sure
> > whether that's something to avoid, and if so whether Andy's approach is
> > better.
> >
> > Thoughts?
>
> I like simple options :-) A patch to export acpi_bus_type, with enough
> context in the commit message (and in the cover latter of the series),
> should be enough to provide all the information the ACPI maintainers
> need to decide which option is best. With a bit of luck that patch will
> be considered the best option and no extra work will be needed.

The problem with ACPI bus is that it is not as simple as other buses,
i.e. it may have purely ACPI devices along with *companion* devices,
which are usually represented by platform bus. On top of that for
several ACPI devices there can be one physical node and it will be not
so clear what you are exactly looking for by traversing acpi_bus_type.
I believe it's hidden on purpose.
Daniel Scally Nov. 15, 2020, 8:45 a.m. UTC | #28
On 13/11/2020 19:45, Andy Shevchenko wrote:
> On Fri, Nov 13, 2020 at 6:22 PM Laurent Pinchart
> <laurent.pinchart@ideasonboard.com> wrote:
>> On Fri, Nov 13, 2020 at 10:02:30AM +0000, Dan Scally wrote:
>>> On 29/10/2020 22:51, Laurent Pinchart wrote:
>>>> On Fri, Oct 30, 2020 at 12:22:15AM +0200, Andy Shevchenko wrote:
>>>>> On Thu, Oct 29, 2020 at 11:29:30PM +0200, Laurent Pinchart wrote:
> ...
>
>>>>> In this case we probably need something like
>>>>>
>>>>> struct acpi_device *
>>>>> acpi_dev_get_next_match_dev(struct acpi_device *adev,
>>>>>                        const char *hid, const char *uid, s64 hrv)
>>>>> {
>>>>>    struct device *start = adev ? &adev->dev : NULL;
>>>>>    ...
>>>>>    dev = bus_find_device(&acpi_bus_type, start, &match, acpi_dev_match_cb);
>>>>>    ...
>>>>> }
>>>>>
>>>>> in drivers/acpi/utils.c and
>>>>>
>>>>> static inline struct acpi_device *
>>>>> acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv)
>>>>> {
>>>>>    return acpi_dev_get_next_match_dev(NULL, hid, uid, hrv);
>>>>> }
>>>>>
>>>>> in include/linux/acpi.h.
>>>>>
>>>>> Then we may add
>>>>>
>>>>> #define for_each_acpi_dev_match(adev, hid, uid, hrv)                       \
>>>>>    for (adev = acpi_dev_get_first_match_dev(hid, uid, hrv);        \
>>>>>         adev;                                                      \
>>>>>         adev = acpi_dev_get_next_match_dev(adev, hid, uid, hrv))
>>>> What the cio2-bridge code needs is indeed
>>>>
>>>>     for each hid in supported hids:
>>>>             for each acpi device that is compatible with hid:
>>>>                     ...
>>>>
>>>> which could also be expressed as
>>>>
>>>>     for each acpi device:
>>>>             if acpi device hid is in supported hids:
>>>>                     ...
>>>>
>>>> I don't mind either option, I'll happily follow the preference of the
>>>> ACPI maintainers.
>>> Does this need raising elsewhere then? The original idea of just
>>> bus_for_each_dev(&acpi_bus_type...) I have now tested and it works fine,
>>> but it does mean that I need to export acpi_bus_type (currently that
>>> symbol's not available)...that seems much simpler to me but I'm not sure
>>> whether that's something to avoid, and if so whether Andy's approach is
>>> better.
>>>
>>> Thoughts?
>> I like simple options :-) A patch to export acpi_bus_type, with enough
>> context in the commit message (and in the cover latter of the series),
>> should be enough to provide all the information the ACPI maintainers
>> need to decide which option is best. With a bit of luck that patch will
>> be considered the best option and no extra work will be needed.
> The problem with ACPI bus is that it is not as simple as other buses,
> i.e. it may have purely ACPI devices along with *companion* devices,
> which are usually represented by platform bus. On top of that for
> several ACPI devices there can be one physical node and it will be not
> so clear what you are exactly looking for by traversing acpi_bus_type.
> I believe it's hidden on purpose.
Alright - I followed your suggestion to implement the iterator instead
then and left acpi_bus_type hidden, thanks.
Laurent Pinchart Nov. 16, 2020, 8:53 a.m. UTC | #29
Hi Andy,

On Fri, Nov 13, 2020 at 09:45:00PM +0200, Andy Shevchenko wrote:
> On Fri, Nov 13, 2020 at 6:22 PM Laurent Pinchart wrote:
> > On Fri, Nov 13, 2020 at 10:02:30AM +0000, Dan Scally wrote:
> > > On 29/10/2020 22:51, Laurent Pinchart wrote:
> > > > On Fri, Oct 30, 2020 at 12:22:15AM +0200, Andy Shevchenko wrote:
> > > >> On Thu, Oct 29, 2020 at 11:29:30PM +0200, Laurent Pinchart wrote:
> 
> ...
> 
> > > >> In this case we probably need something like
> > > >>
> > > >> struct acpi_device *
> > > >> acpi_dev_get_next_match_dev(struct acpi_device *adev,
> > > >>                        const char *hid, const char *uid, s64 hrv)
> > > >> {
> > > >>    struct device *start = adev ? &adev->dev : NULL;
> > > >>    ...
> > > >>    dev = bus_find_device(&acpi_bus_type, start, &match, acpi_dev_match_cb);
> > > >>    ...
> > > >> }
> > > >>
> > > >> in drivers/acpi/utils.c and
> > > >>
> > > >> static inline struct acpi_device *
> > > >> acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv)
> > > >> {
> > > >>    return acpi_dev_get_next_match_dev(NULL, hid, uid, hrv);
> > > >> }
> > > >>
> > > >> in include/linux/acpi.h.
> > > >>
> > > >> Then we may add
> > > >>
> > > >> #define for_each_acpi_dev_match(adev, hid, uid, hrv)                       \
> > > >>    for (adev = acpi_dev_get_first_match_dev(hid, uid, hrv);        \
> > > >>         adev;                                                      \
> > > >>         adev = acpi_dev_get_next_match_dev(adev, hid, uid, hrv))
> > > >
> > > > What the cio2-bridge code needs is indeed
> > > >
> > > >     for each hid in supported hids:
> > > >             for each acpi device that is compatible with hid:
> > > >                     ...
> > > >
> > > > which could also be expressed as
> > > >
> > > >     for each acpi device:
> > > >             if acpi device hid is in supported hids:
> > > >                     ...
> > > >
> > > > I don't mind either option, I'll happily follow the preference of the
> > > > ACPI maintainers.
> > >
> > > Does this need raising elsewhere then? The original idea of just
> > > bus_for_each_dev(&acpi_bus_type...) I have now tested and it works fine,
> > > but it does mean that I need to export acpi_bus_type (currently that
> > > symbol's not available)...that seems much simpler to me but I'm not sure
> > > whether that's something to avoid, and if so whether Andy's approach is
> > > better.
> > >
> > > Thoughts?
> >
> > I like simple options :-) A patch to export acpi_bus_type, with enough
> > context in the commit message (and in the cover latter of the series),
> > should be enough to provide all the information the ACPI maintainers
> > need to decide which option is best. With a bit of luck that patch will
> > be considered the best option and no extra work will be needed.
> 
> The problem with ACPI bus is that it is not as simple as other buses,
> i.e. it may have purely ACPI devices along with *companion* devices,
> which are usually represented by platform bus. On top of that for
> several ACPI devices there can be one physical node and it will be not
> so clear what you are exactly looking for by traversing acpi_bus_type.
> I believe it's hidden on purpose.

Maybe there's something I don't get, as I'm not very familiar with the
ACPI implementation in the kernel, but the code in the cio2-bridge,
unless I'm mistaken, is really interested in ACPI devices on the ACPI
bus, not companions or other devices related to the ACPI devices. The
iterators you have proposed above use bus_find_device() on
acpi_bus_type, so I don't really see how they make a difference from a
cio2-bridge point of view. Is your point that acpi_bus_type shouldn't be
exported because it could then be misused by *other* drivers ? Couldn't
those drivers then equally misuse the iterators ?
Andy Shevchenko Nov. 16, 2020, 1:57 p.m. UTC | #30
On Mon, Nov 16, 2020 at 10:53 AM Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
> On Fri, Nov 13, 2020 at 09:45:00PM +0200, Andy Shevchenko wrote:
> > On Fri, Nov 13, 2020 at 6:22 PM Laurent Pinchart wrote:
> > > On Fri, Nov 13, 2020 at 10:02:30AM +0000, Dan Scally wrote:
> > > > On 29/10/2020 22:51, Laurent Pinchart wrote:
> > > > > On Fri, Oct 30, 2020 at 12:22:15AM +0200, Andy Shevchenko wrote:
> > > > >> On Thu, Oct 29, 2020 at 11:29:30PM +0200, Laurent Pinchart wrote:
> >
> > ...
> >
> > > > >> In this case we probably need something like
> > > > >>
> > > > >> struct acpi_device *
> > > > >> acpi_dev_get_next_match_dev(struct acpi_device *adev,
> > > > >>                        const char *hid, const char *uid, s64 hrv)
> > > > >> {
> > > > >>    struct device *start = adev ? &adev->dev : NULL;
> > > > >>    ...
> > > > >>    dev = bus_find_device(&acpi_bus_type, start, &match, acpi_dev_match_cb);
> > > > >>    ...
> > > > >> }
> > > > >>
> > > > >> in drivers/acpi/utils.c and
> > > > >>
> > > > >> static inline struct acpi_device *
> > > > >> acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv)
> > > > >> {
> > > > >>    return acpi_dev_get_next_match_dev(NULL, hid, uid, hrv);
> > > > >> }
> > > > >>
> > > > >> in include/linux/acpi.h.
> > > > >>
> > > > >> Then we may add
> > > > >>
> > > > >> #define for_each_acpi_dev_match(adev, hid, uid, hrv)                       \
> > > > >>    for (adev = acpi_dev_get_first_match_dev(hid, uid, hrv);        \
> > > > >>         adev;                                                      \
> > > > >>         adev = acpi_dev_get_next_match_dev(adev, hid, uid, hrv))
> > > > >
> > > > > What the cio2-bridge code needs is indeed
> > > > >
> > > > >     for each hid in supported hids:
> > > > >             for each acpi device that is compatible with hid:
> > > > >                     ...
> > > > >
> > > > > which could also be expressed as
> > > > >
> > > > >     for each acpi device:
> > > > >             if acpi device hid is in supported hids:
> > > > >                     ...
> > > > >
> > > > > I don't mind either option, I'll happily follow the preference of the
> > > > > ACPI maintainers.
> > > >
> > > > Does this need raising elsewhere then? The original idea of just
> > > > bus_for_each_dev(&acpi_bus_type...) I have now tested and it works fine,
> > > > but it does mean that I need to export acpi_bus_type (currently that
> > > > symbol's not available)...that seems much simpler to me but I'm not sure
> > > > whether that's something to avoid, and if so whether Andy's approach is
> > > > better.
> > > >
> > > > Thoughts?
> > >
> > > I like simple options :-) A patch to export acpi_bus_type, with enough
> > > context in the commit message (and in the cover latter of the series),
> > > should be enough to provide all the information the ACPI maintainers
> > > need to decide which option is best. With a bit of luck that patch will
> > > be considered the best option and no extra work will be needed.
> >
> > The problem with ACPI bus is that it is not as simple as other buses,
> > i.e. it may have purely ACPI devices along with *companion* devices,
> > which are usually represented by platform bus. On top of that for
> > several ACPI devices there can be one physical node and it will be not
> > so clear what you are exactly looking for by traversing acpi_bus_type.
> > I believe it's hidden on purpose.
>
> Maybe there's something I don't get, as I'm not very familiar with the
> ACPI implementation in the kernel, but the code in the cio2-bridge,
> unless I'm mistaken, is really interested in ACPI devices on the ACPI
> bus, not companions or other devices related to the ACPI devices.

AFAICS cio2-bridge wants to find ACPI companion devices which are
enumerated as platform ones (or I²C or SPI).

> The
> iterators you have proposed above use bus_find_device() on
> acpi_bus_type, so I don't really see how they make a difference from a
> cio2-bridge point of view.

This seems to be true. The iterators have no means to distinguish
between companion devices and pure ACPI, for example.
For this one needs to call something like 'get first physical node'
followed by 'let's check that it has a companion and that the one we
have already got from ACPI bus iterator'.

> Is your point that acpi_bus_type shouldn't be
> exported because it could then be misused by *other* drivers ? Couldn't
> those drivers then equally misuse the iterators ?

My point is that the ACPI bus type here is not homogenous.
And thus I think it was the reason behind hiding it. I might be
mistaken and you may ask ACPI maintainers for the clarification.

In summary I think we are trying to solve a problem that has not yet
existed (devices with several same sensors). Do we have a DSDT of such
to look into?
Laurent Pinchart Nov. 16, 2020, 2:10 p.m. UTC | #31
Hi Andy,

On Mon, Nov 16, 2020 at 03:57:17PM +0200, Andy Shevchenko wrote:
> On Mon, Nov 16, 2020 at 10:53 AM Laurent Pinchart wrote:
> > On Fri, Nov 13, 2020 at 09:45:00PM +0200, Andy Shevchenko wrote:
> > > On Fri, Nov 13, 2020 at 6:22 PM Laurent Pinchart wrote:
> > > > On Fri, Nov 13, 2020 at 10:02:30AM +0000, Dan Scally wrote:
> > > > > On 29/10/2020 22:51, Laurent Pinchart wrote:
> > > > > > On Fri, Oct 30, 2020 at 12:22:15AM +0200, Andy Shevchenko wrote:
> > > > > >> On Thu, Oct 29, 2020 at 11:29:30PM +0200, Laurent Pinchart wrote:
> > >
> > > ...
> > >
> > > > > >> In this case we probably need something like
> > > > > >>
> > > > > >> struct acpi_device *
> > > > > >> acpi_dev_get_next_match_dev(struct acpi_device *adev,
> > > > > >>                        const char *hid, const char *uid, s64 hrv)
> > > > > >> {
> > > > > >>    struct device *start = adev ? &adev->dev : NULL;
> > > > > >>    ...
> > > > > >>    dev = bus_find_device(&acpi_bus_type, start, &match, acpi_dev_match_cb);
> > > > > >>    ...
> > > > > >> }
> > > > > >>
> > > > > >> in drivers/acpi/utils.c and
> > > > > >>
> > > > > >> static inline struct acpi_device *
> > > > > >> acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv)
> > > > > >> {
> > > > > >>    return acpi_dev_get_next_match_dev(NULL, hid, uid, hrv);
> > > > > >> }
> > > > > >>
> > > > > >> in include/linux/acpi.h.
> > > > > >>
> > > > > >> Then we may add
> > > > > >>
> > > > > >> #define for_each_acpi_dev_match(adev, hid, uid, hrv)                       \
> > > > > >>    for (adev = acpi_dev_get_first_match_dev(hid, uid, hrv);        \
> > > > > >>         adev;                                                      \
> > > > > >>         adev = acpi_dev_get_next_match_dev(adev, hid, uid, hrv))
> > > > > >
> > > > > > What the cio2-bridge code needs is indeed
> > > > > >
> > > > > >     for each hid in supported hids:
> > > > > >             for each acpi device that is compatible with hid:
> > > > > >                     ...
> > > > > >
> > > > > > which could also be expressed as
> > > > > >
> > > > > >     for each acpi device:
> > > > > >             if acpi device hid is in supported hids:
> > > > > >                     ...
> > > > > >
> > > > > > I don't mind either option, I'll happily follow the preference of the
> > > > > > ACPI maintainers.
> > > > >
> > > > > Does this need raising elsewhere then? The original idea of just
> > > > > bus_for_each_dev(&acpi_bus_type...) I have now tested and it works fine,
> > > > > but it does mean that I need to export acpi_bus_type (currently that
> > > > > symbol's not available)...that seems much simpler to me but I'm not sure
> > > > > whether that's something to avoid, and if so whether Andy's approach is
> > > > > better.
> > > > >
> > > > > Thoughts?
> > > >
> > > > I like simple options :-) A patch to export acpi_bus_type, with enough
> > > > context in the commit message (and in the cover latter of the series),
> > > > should be enough to provide all the information the ACPI maintainers
> > > > need to decide which option is best. With a bit of luck that patch will
> > > > be considered the best option and no extra work will be needed.
> > >
> > > The problem with ACPI bus is that it is not as simple as other buses,
> > > i.e. it may have purely ACPI devices along with *companion* devices,
> > > which are usually represented by platform bus. On top of that for
> > > several ACPI devices there can be one physical node and it will be not
> > > so clear what you are exactly looking for by traversing acpi_bus_type.
> > > I believe it's hidden on purpose.
> >
> > Maybe there's something I don't get, as I'm not very familiar with the
> > ACPI implementation in the kernel, but the code in the cio2-bridge,
> > unless I'm mistaken, is really interested in ACPI devices on the ACPI
> > bus, not companions or other devices related to the ACPI devices.
> 
> AFAICS cio2-bridge wants to find ACPI companion devices which are
> enumerated as platform ones (or I²C or SPI).

I thought we were looking for ACPI devices, not companion devices, in
order to extract information from the DSDT and store it in a software
node. I could very well be wrong though.

> > The
> > iterators you have proposed above use bus_find_device() on
> > acpi_bus_type, so I don't really see how they make a difference from a
> > cio2-bridge point of view.
> 
> This seems to be true. The iterators have no means to distinguish
> between companion devices and pure ACPI, for example.
> For this one needs to call something like 'get first physical node'
> followed by 'let's check that it has a companion and that the one we
> have already got from ACPI bus iterator'.
> 
> > Is your point that acpi_bus_type shouldn't be
> > exported because it could then be misused by *other* drivers ? Couldn't
> > those drivers then equally misuse the iterators ?
> 
> My point is that the ACPI bus type here is not homogenous.
> And thus I think it was the reason behind hiding it. I might be
> mistaken and you may ask ACPI maintainers for the clarification.
> 
> In summary I think we are trying to solve a problem that has not yet
> existed (devices with several same sensors). Do we have a DSDT of such
> to look into?

Not to my knowledge.
Daniel Scally Nov. 16, 2020, 2:15 p.m. UTC | #32
On 16/11/2020 14:10, Laurent Pinchart wrote:
> Hi Andy,
>
> On Mon, Nov 16, 2020 at 03:57:17PM +0200, Andy Shevchenko wrote:
>> On Mon, Nov 16, 2020 at 10:53 AM Laurent Pinchart wrote:
>>> On Fri, Nov 13, 2020 at 09:45:00PM +0200, Andy Shevchenko wrote:
>>>> On Fri, Nov 13, 2020 at 6:22 PM Laurent Pinchart wrote:
>>>>> On Fri, Nov 13, 2020 at 10:02:30AM +0000, Dan Scally wrote:
>>>>>> On 29/10/2020 22:51, Laurent Pinchart wrote:
>>>>>>> On Fri, Oct 30, 2020 at 12:22:15AM +0200, Andy Shevchenko wrote:
>>>>>>>> On Thu, Oct 29, 2020 at 11:29:30PM +0200, Laurent Pinchart wrote:
>>>> ...
>>>>
>>>>>>>> In this case we probably need something like
>>>>>>>>
>>>>>>>> struct acpi_device *
>>>>>>>> acpi_dev_get_next_match_dev(struct acpi_device *adev,
>>>>>>>>                        const char *hid, const char *uid, s64 hrv)
>>>>>>>> {
>>>>>>>>    struct device *start = adev ? &adev->dev : NULL;
>>>>>>>>    ...
>>>>>>>>    dev = bus_find_device(&acpi_bus_type, start, &match, acpi_dev_match_cb);
>>>>>>>>    ...
>>>>>>>> }
>>>>>>>>
>>>>>>>> in drivers/acpi/utils.c and
>>>>>>>>
>>>>>>>> static inline struct acpi_device *
>>>>>>>> acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv)
>>>>>>>> {
>>>>>>>>    return acpi_dev_get_next_match_dev(NULL, hid, uid, hrv);
>>>>>>>> }
>>>>>>>>
>>>>>>>> in include/linux/acpi.h.
>>>>>>>>
>>>>>>>> Then we may add
>>>>>>>>
>>>>>>>> #define for_each_acpi_dev_match(adev, hid, uid, hrv)                       \
>>>>>>>>    for (adev = acpi_dev_get_first_match_dev(hid, uid, hrv);        \
>>>>>>>>         adev;                                                      \
>>>>>>>>         adev = acpi_dev_get_next_match_dev(adev, hid, uid, hrv))
>>>>>>> What the cio2-bridge code needs is indeed
>>>>>>>
>>>>>>>     for each hid in supported hids:
>>>>>>>             for each acpi device that is compatible with hid:
>>>>>>>                     ...
>>>>>>>
>>>>>>> which could also be expressed as
>>>>>>>
>>>>>>>     for each acpi device:
>>>>>>>             if acpi device hid is in supported hids:
>>>>>>>                     ...
>>>>>>>
>>>>>>> I don't mind either option, I'll happily follow the preference of the
>>>>>>> ACPI maintainers.
>>>>>> Does this need raising elsewhere then? The original idea of just
>>>>>> bus_for_each_dev(&acpi_bus_type...) I have now tested and it works fine,
>>>>>> but it does mean that I need to export acpi_bus_type (currently that
>>>>>> symbol's not available)...that seems much simpler to me but I'm not sure
>>>>>> whether that's something to avoid, and if so whether Andy's approach is
>>>>>> better.
>>>>>>
>>>>>> Thoughts?
>>>>> I like simple options :-) A patch to export acpi_bus_type, with enough
>>>>> context in the commit message (and in the cover latter of the series),
>>>>> should be enough to provide all the information the ACPI maintainers
>>>>> need to decide which option is best. With a bit of luck that patch will
>>>>> be considered the best option and no extra work will be needed.
>>>> The problem with ACPI bus is that it is not as simple as other buses,
>>>> i.e. it may have purely ACPI devices along with *companion* devices,
>>>> which are usually represented by platform bus. On top of that for
>>>> several ACPI devices there can be one physical node and it will be not
>>>> so clear what you are exactly looking for by traversing acpi_bus_type.
>>>> I believe it's hidden on purpose.
>>> Maybe there's something I don't get, as I'm not very familiar with the
>>> ACPI implementation in the kernel, but the code in the cio2-bridge,
>>> unless I'm mistaken, is really interested in ACPI devices on the ACPI
>>> bus, not companions or other devices related to the ACPI devices.
>> AFAICS cio2-bridge wants to find ACPI companion devices which are
>> enumerated as platform ones (or I²C or SPI).
> I thought we were looking for ACPI devices, not companion devices, in
> order to extract information from the DSDT and store it in a software
> node. I could very well be wrong though.
This is correct - the code to fetch the various resources we're looking
at all uses acpi_device. Whether using Andy's iterator suggestions or
previous bus_for_each_dev(&acpi_bus_type...) I'm just getting the
acpi_device via to_acpi_dev() and using that.
>>> The
>>> iterators you have proposed above use bus_find_device() on
>>> acpi_bus_type, so I don't really see how they make a difference from a
>>> cio2-bridge point of view.
>> This seems to be true. The iterators have no means to distinguish
>> between companion devices and pure ACPI, for example.
>> For this one needs to call something like 'get first physical node'
>> followed by 'let's check that it has a companion and that the one we
>> have already got from ACPI bus iterator'.
>>
>>> Is your point that acpi_bus_type shouldn't be
>>> exported because it could then be misused by *other* drivers ? Couldn't
>>> those drivers then equally misuse the iterators ?
>> My point is that the ACPI bus type here is not homogenous.
>> And thus I think it was the reason behind hiding it. I might be
>> mistaken and you may ask ACPI maintainers for the clarification.
>>
>> In summary I think we are trying to solve a problem that has not yet
>> existed (devices with several same sensors). Do we have a DSDT of such
>> to look into?
> Not to my knowledge.
>
Andy Shevchenko Nov. 16, 2020, 4:16 p.m. UTC | #33
On Mon, Nov 16, 2020 at 02:15:01PM +0000, Dan Scally wrote:
> On 16/11/2020 14:10, Laurent Pinchart wrote:
> > On Mon, Nov 16, 2020 at 03:57:17PM +0200, Andy Shevchenko wrote:
> >> On Mon, Nov 16, 2020 at 10:53 AM Laurent Pinchart wrote:
> >>> On Fri, Nov 13, 2020 at 09:45:00PM +0200, Andy Shevchenko wrote:
> >>>> On Fri, Nov 13, 2020 at 6:22 PM Laurent Pinchart wrote:
> >>>>> On Fri, Nov 13, 2020 at 10:02:30AM +0000, Dan Scally wrote:
> >>>>>> On 29/10/2020 22:51, Laurent Pinchart wrote:
> >>>>>>> On Fri, Oct 30, 2020 at 12:22:15AM +0200, Andy Shevchenko wrote:
> >>>>>>>> On Thu, Oct 29, 2020 at 11:29:30PM +0200, Laurent Pinchart wrote:
> >>>> ...
> >>>>
> >>>>>>>> In this case we probably need something like
> >>>>>>>>
> >>>>>>>> struct acpi_device *
> >>>>>>>> acpi_dev_get_next_match_dev(struct acpi_device *adev,
> >>>>>>>>                        const char *hid, const char *uid, s64 hrv)
> >>>>>>>> {
> >>>>>>>>    struct device *start = adev ? &adev->dev : NULL;
> >>>>>>>>    ...
> >>>>>>>>    dev = bus_find_device(&acpi_bus_type, start, &match, acpi_dev_match_cb);
> >>>>>>>>    ...
> >>>>>>>> }
> >>>>>>>>
> >>>>>>>> in drivers/acpi/utils.c and
> >>>>>>>>
> >>>>>>>> static inline struct acpi_device *
> >>>>>>>> acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv)
> >>>>>>>> {
> >>>>>>>>    return acpi_dev_get_next_match_dev(NULL, hid, uid, hrv);
> >>>>>>>> }
> >>>>>>>>
> >>>>>>>> in include/linux/acpi.h.
> >>>>>>>>
> >>>>>>>> Then we may add
> >>>>>>>>
> >>>>>>>> #define for_each_acpi_dev_match(adev, hid, uid, hrv)                       \
> >>>>>>>>    for (adev = acpi_dev_get_first_match_dev(hid, uid, hrv);        \
> >>>>>>>>         adev;                                                      \
> >>>>>>>>         adev = acpi_dev_get_next_match_dev(adev, hid, uid, hrv))
> >>>>>>> What the cio2-bridge code needs is indeed
> >>>>>>>
> >>>>>>>     for each hid in supported hids:
> >>>>>>>             for each acpi device that is compatible with hid:
> >>>>>>>                     ...
> >>>>>>>
> >>>>>>> which could also be expressed as
> >>>>>>>
> >>>>>>>     for each acpi device:
> >>>>>>>             if acpi device hid is in supported hids:
> >>>>>>>                     ...
> >>>>>>>
> >>>>>>> I don't mind either option, I'll happily follow the preference of the
> >>>>>>> ACPI maintainers.
> >>>>>> Does this need raising elsewhere then? The original idea of just
> >>>>>> bus_for_each_dev(&acpi_bus_type...) I have now tested and it works fine,
> >>>>>> but it does mean that I need to export acpi_bus_type (currently that
> >>>>>> symbol's not available)...that seems much simpler to me but I'm not sure
> >>>>>> whether that's something to avoid, and if so whether Andy's approach is
> >>>>>> better.
> >>>>>>
> >>>>>> Thoughts?
> >>>>> I like simple options :-) A patch to export acpi_bus_type, with enough
> >>>>> context in the commit message (and in the cover latter of the series),
> >>>>> should be enough to provide all the information the ACPI maintainers
> >>>>> need to decide which option is best. With a bit of luck that patch will
> >>>>> be considered the best option and no extra work will be needed.
> >>>> The problem with ACPI bus is that it is not as simple as other buses,
> >>>> i.e. it may have purely ACPI devices along with *companion* devices,
> >>>> which are usually represented by platform bus. On top of that for
> >>>> several ACPI devices there can be one physical node and it will be not
> >>>> so clear what you are exactly looking for by traversing acpi_bus_type.
> >>>> I believe it's hidden on purpose.
> >>> Maybe there's something I don't get, as I'm not very familiar with the
> >>> ACPI implementation in the kernel, but the code in the cio2-bridge,
> >>> unless I'm mistaken, is really interested in ACPI devices on the ACPI
> >>> bus, not companions or other devices related to the ACPI devices.
> >> AFAICS cio2-bridge wants to find ACPI companion devices which are
> >> enumerated as platform ones (or I²C or SPI).
> > I thought we were looking for ACPI devices, not companion devices, in
> > order to extract information from the DSDT and store it in a software
> > node. I could very well be wrong though.
> This is correct - the code to fetch the various resources we're looking
> at all uses acpi_device. Whether using Andy's iterator suggestions or
> previous bus_for_each_dev(&acpi_bus_type...) I'm just getting the
> acpi_device via to_acpi_dev() and using that.

If you try to get an I²C ore SPI device out of pure ACPI device (with given
APCI _HID) you will fail. So, it's not correct. You are retrieving companion
devices, while they are still in the struct acpi_device.

And don't ask me, why it's so. I wasn't designed that and didn't affect any
decision made there.

> >>> The
> >>> iterators you have proposed above use bus_find_device() on
> >>> acpi_bus_type, so I don't really see how they make a difference from a
> >>> cio2-bridge point of view.
> >> This seems to be true. The iterators have no means to distinguish
> >> between companion devices and pure ACPI, for example.
> >> For this one needs to call something like 'get first physical node'
> >> followed by 'let's check that it has a companion and that the one we
> >> have already got from ACPI bus iterator'.
> >>
> >>> Is your point that acpi_bus_type shouldn't be
> >>> exported because it could then be misused by *other* drivers ? Couldn't
> >>> those drivers then equally misuse the iterators ?
> >> My point is that the ACPI bus type here is not homogenous.
> >> And thus I think it was the reason behind hiding it. I might be
> >> mistaken and you may ask ACPI maintainers for the clarification.
> >>
> >> In summary I think we are trying to solve a problem that has not yet
> >> existed (devices with several same sensors). Do we have a DSDT of such
> >> to look into?
> > Not to my knowledge.
Daniel Scally Nov. 17, 2020, 12:01 p.m. UTC | #34
On 16/11/2020 16:16, Andy Shevchenko wrote:
> On Mon, Nov 16, 2020 at 02:15:01PM +0000, Dan Scally wrote:
>> On 16/11/2020 14:10, Laurent Pinchart wrote:
>>> I thought we were looking for ACPI devices, not companion devices, in
>>> order to extract information from the DSDT and store it in a software
>>> node. I could very well be wrong though.
>> This is correct - the code to fetch the various resources we're looking
>> at all uses acpi_device. Whether using Andy's iterator suggestions or
>> previous bus_for_each_dev(&acpi_bus_type...) I'm just getting the
>> acpi_device via to_acpi_dev() and using that.
> If you try to get an I²C ore SPI device out of pure ACPI device (with given
> APCI _HID) you will fail. So, it's not correct. You are retrieving companion
> devices, while they are still in the struct acpi_device.
>
> And don't ask me, why it's so. I wasn't designed that and didn't affect any
> decision made there.

Well, in terms of the actual device we're getting, I don't think we're
fundamentally doing anything different between the methods...unless I'm
really mistaken.


Originally implementation was like:


const char *supported_devices[] = {

        "OVTI2680",

};


static int cio2_bridge_connect_supported_devices(void)

{

        struct acpi_device *adev;

        int i;

        for (i = 0; i < ARRAY_SIZE(supported_devices); i++) {

                adev =
acpi_dev_get_first_match_dev(supported_devices[i], NULL, -1);

...

}


and acpi_dev_get_first_match_dev() likewise just returns adev via
to_acpi_device(dev).


So, maybe we don't need to do the iterating over all devices with
matching _HID at all, in which case it can be dropped, but if we're
doing it then I can't see that it's different to the original
implementation in terms of the struct acpi_device we're working with or
the route taken to get it.


Either way; ACPI maintainers asked to be CC'd on the next patchset
anyway, so they'll see what we're doing and be able to weigh in.
Andy Shevchenko Nov. 17, 2020, 4:42 p.m. UTC | #35
On Tue, Nov 17, 2020 at 2:02 PM Dan Scally <djrscally@gmail.com> wrote:
>
> On 16/11/2020 16:16, Andy Shevchenko wrote:
> > On Mon, Nov 16, 2020 at 02:15:01PM +0000, Dan Scally wrote:
> >> On 16/11/2020 14:10, Laurent Pinchart wrote:
> >>> I thought we were looking for ACPI devices, not companion devices, in
> >>> order to extract information from the DSDT and store it in a software
> >>> node. I could very well be wrong though.
> >> This is correct - the code to fetch the various resources we're looking
> >> at all uses acpi_device. Whether using Andy's iterator suggestions or
> >> previous bus_for_each_dev(&acpi_bus_type...) I'm just getting the
> >> acpi_device via to_acpi_dev() and using that.
> > If you try to get an I²C ore SPI device out of pure ACPI device (with given
> > APCI _HID) you will fail. So, it's not correct. You are retrieving companion
> > devices, while they are still in the struct acpi_device.
> >
> > And don't ask me, why it's so. I wasn't designed that and didn't affect any
> > decision made there.
>
> Well, in terms of the actual device we're getting, I don't think we're
> fundamentally doing anything different between the methods...unless I'm
> really mistaken.
>
>
> Originally implementation was like:
>
>
> const char *supported_devices[] = {
>
>         "OVTI2680",
>
> };
>
>
> static int cio2_bridge_connect_supported_devices(void)
>
> {
>
>         struct acpi_device *adev;
>
>         int i;
>
>         for (i = 0; i < ARRAY_SIZE(supported_devices); i++) {
>
>                 adev =
> acpi_dev_get_first_match_dev(supported_devices[i], NULL, -1);
>
> ...
>
> }
>
>
> and acpi_dev_get_first_match_dev() likewise just returns adev via
> to_acpi_device(dev).
>
>
> So, maybe we don't need to do the iterating over all devices with
> matching _HID at all, in which case it can be dropped, but if we're
> doing it then I can't see that it's different to the original
> implementation in terms of the struct acpi_device we're working with or
> the route taken to get it.
>
>
> Either way; ACPI maintainers asked to be CC'd on the next patchset
> anyway, so they'll see what we're doing and be able to weigh in.

Implementation wise the two approaches are quite similar for now, indeed.
I would rather go with an iterator approach for a simple reason, EFI
code already has something which may utilize iterators rather than
using their home grown solution.
Daniel Scally Nov. 17, 2020, 10:59 p.m. UTC | #36
On 17/11/2020 16:42, Andy Shevchenko wrote:
> On Tue, Nov 17, 2020 at 2:02 PM Dan Scally <djrscally@gmail.com> wrote:
>> On 16/11/2020 16:16, Andy Shevchenko wrote:
>>> On Mon, Nov 16, 2020 at 02:15:01PM +0000, Dan Scally wrote:
>>>> On 16/11/2020 14:10, Laurent Pinchart wrote:
>>>>> I thought we were looking for ACPI devices, not companion devices, in
>>>>> order to extract information from the DSDT and store it in a software
>>>>> node. I could very well be wrong though.
>>>> This is correct - the code to fetch the various resources we're looking
>>>> at all uses acpi_device. Whether using Andy's iterator suggestions or
>>>> previous bus_for_each_dev(&acpi_bus_type...) I'm just getting the
>>>> acpi_device via to_acpi_dev() and using that.
>>> If you try to get an I²C ore SPI device out of pure ACPI device (with given
>>> APCI _HID) you will fail. So, it's not correct. You are retrieving companion
>>> devices, while they are still in the struct acpi_device.
>>>
>>> And don't ask me, why it's so. I wasn't designed that and didn't affect any
>>> decision made there.
>> Well, in terms of the actual device we're getting, I don't think we're
>> fundamentally doing anything different between the methods...unless I'm
>> really mistaken.
>>
>>
>> Originally implementation was like:
>>
>>
>> const char *supported_devices[] = {
>>
>>         "OVTI2680",
>>
>> };
>>
>>
>> static int cio2_bridge_connect_supported_devices(void)
>>
>> {
>>
>>         struct acpi_device *adev;
>>
>>         int i;
>>
>>         for (i = 0; i < ARRAY_SIZE(supported_devices); i++) {
>>
>>                 adev =
>> acpi_dev_get_first_match_dev(supported_devices[i], NULL, -1);
>>
>> ...
>>
>> }
>>
>>
>> and acpi_dev_get_first_match_dev() likewise just returns adev via
>> to_acpi_device(dev).
>>
>>
>> So, maybe we don't need to do the iterating over all devices with
>> matching _HID at all, in which case it can be dropped, but if we're
>> doing it then I can't see that it's different to the original
>> implementation in terms of the struct acpi_device we're working with or
>> the route taken to get it.
>>
>>
>> Either way; ACPI maintainers asked to be CC'd on the next patchset
>> anyway, so they'll see what we're doing and be able to weigh in.
> Implementation wise the two approaches are quite similar for now, indeed.
> I would rather go with an iterator approach for a simple reason, EFI
> code already has something which may utilize iterators rather than
> using their home grown solution.
Alright - let's stick with that approach and leave the handling multiple
sensors of same model in then. That's the current state of the code
anyway, and it means it can be used elsewhere too.
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 5d768d5ad..4c9c646c7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8848,6 +8848,7 @@  INTEL IPU3 CSI-2 CIO2 DRIVER
 M:	Yong Zhi <yong.zhi@intel.com>
 M:	Sakari Ailus <sakari.ailus@linux.intel.com>
 M:	Bingbu Cao <bingbu.cao@intel.com>
+M:	Dan Scally <djrscally@gmail.com>
 R:	Tianshu Qiu <tian.shu.qiu@intel.com>
 L:	linux-media@vger.kernel.org
 S:	Maintained
diff --git a/drivers/media/pci/intel/ipu3/Kconfig b/drivers/media/pci/intel/ipu3/Kconfig
index 82d7f17e6..d14cbceae 100644
--- a/drivers/media/pci/intel/ipu3/Kconfig
+++ b/drivers/media/pci/intel/ipu3/Kconfig
@@ -16,3 +16,21 @@  config VIDEO_IPU3_CIO2
 	  Say Y or M here if you have a Skylake/Kaby Lake SoC with MIPI CSI-2
 	  connected camera.
 	  The module will be called ipu3-cio2.
+
+config CIO2_BRIDGE
+	bool "IPU3 CIO2 Sensors Bridge"
+	depends on VIDEO_IPU3_CIO2
+	help
+	  This extension provides an API for the ipu3-cio2 driver to create
+	  connections to cameras that are hidden in SSDB buffer in ACPI. It
+	  can be used to enable support for cameras in detachable / hybrid
+	  devices that ship with Windows.
+
+	  Say Y here if your device is a detachable / hybrid laptop that comes
+	  with Windows installed by the OEM, for example:
+
+	  	- Some Microsoft Surface models
+		- The Lenovo Miix line
+		- Dell 7285
+
+	  If in doubt, say N here.
diff --git a/drivers/media/pci/intel/ipu3/Makefile b/drivers/media/pci/intel/ipu3/Makefile
index b4e3266d9..933777e6e 100644
--- a/drivers/media/pci/intel/ipu3/Makefile
+++ b/drivers/media/pci/intel/ipu3/Makefile
@@ -1,4 +1,5 @@ 
 # SPDX-License-Identifier: GPL-2.0-only
 obj-$(CONFIG_VIDEO_IPU3_CIO2) += ipu3-cio2.o
 
-ipu3-cio2-y += ipu3-cio2-main.o
\ No newline at end of file
+ipu3-cio2-y += ipu3-cio2-main.o
+ipu3-cio2-$(CONFIG_CIO2_BRIDGE) += cio2-bridge.o
diff --git a/drivers/media/pci/intel/ipu3/cio2-bridge.c b/drivers/media/pci/intel/ipu3/cio2-bridge.c
new file mode 100644
index 000000000..bbe072f04
--- /dev/null
+++ b/drivers/media/pci/intel/ipu3/cio2-bridge.c
@@ -0,0 +1,327 @@ 
+// SPDX-License-Identifier: GPL-2.0
+// Author: Dan Scally <djrscally@gmail.com>
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/fwnode.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/property.h>
+#include <media/v4l2-subdev.h>
+
+#include "cio2-bridge.h"
+
+/*
+ * Extend this array with ACPI Hardware ID's of devices known to be
+ * working
+ */
+static const char * const supported_devices[] = {
+	"INT33BE",
+	"OVTI2680",
+};
+
+static struct software_node cio2_hid_node = { CIO2_HID };
+
+static struct cio2_bridge bridge;
+
+static const char * const port_names[] = {
+	"port0", "port1", "port2", "port3"
+};
+
+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]),
+};
+
+static int read_acpi_block(struct device *dev, char *id, void *data, u32 size)
+{
+	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+	struct acpi_handle *handle;
+	union acpi_object *obj;
+	acpi_status status;
+	int ret;
+
+	handle = ACPI_HANDLE(dev);
+
+	status = acpi_evaluate_object(handle, id, NULL, &buffer);
+	if (ACPI_FAILURE(status))
+		return -ENODEV;
+
+	obj = buffer.pointer;
+	if (!obj) {
+		dev_err(dev, "Couldn't locate ACPI buffer\n");
+		return -ENODEV;
+	}
+
+	if (obj->type != ACPI_TYPE_BUFFER) {
+		dev_err(dev, "Couldn't read ACPI buffer\n");
+		ret = -ENODEV;
+		goto out_free_buff;
+	}
+
+	if (obj->buffer.length > size) {
+		dev_err(dev, "Given buffer is too small\n");
+		ret = -ENODEV;
+		goto out_free_buff;
+	}
+
+	memcpy(data, obj->buffer.pointer, obj->buffer.length);
+	ret = obj->buffer.length;
+
+out_free_buff:
+	kfree(buffer.pointer);
+	return ret;
+}
+
+static int get_acpi_ssdb_sensor_data(struct device *dev,
+				     struct sensor_bios_data *sensor)
+{
+	struct sensor_bios_data_packed sensor_data;
+	int ret;
+
+	ret = read_acpi_block(dev, "SSDB", &sensor_data, sizeof(sensor_data));
+	if (ret < 0)
+		return ret;
+
+	sensor->link = sensor_data.link;
+	sensor->lanes = sensor_data.lanes;
+	sensor->mclkspeed = sensor_data.mclkspeed;
+	sensor->degree = sensor_data.degree;
+
+	return 0;
+}
+
+static int create_fwnode_properties(struct sensor *sensor,
+				    struct sensor_bios_data *ssdb)
+{
+	struct property_entry *cio2_properties = sensor->cio2_properties;
+	struct property_entry *dev_properties = sensor->dev_properties;
+	struct property_entry *ep_properties = sensor->ep_properties;
+	int i;
+
+	/* device fwnode properties */
+	memset(dev_properties, 0, sizeof(struct property_entry) * 3);
+
+	dev_properties[0] = PROPERTY_ENTRY_U32("clock-frequency",
+					       ssdb->mclkspeed);
+	dev_properties[1] = PROPERTY_ENTRY_U8("rotation", ssdb->degree);
+
+	/* endpoint fwnode properties */
+	memset(ep_properties, 0, sizeof(struct property_entry) * 4);
+
+	sensor->data_lanes = kmalloc_array(ssdb->lanes, sizeof(u32),
+					   GFP_KERNEL);
+
+	if (!sensor->data_lanes)
+		return -ENOMEM;
+
+	for (i = 0; i < ssdb->lanes; i++)
+		sensor->data_lanes[i] = i + 1;
+
+	ep_properties[0] = PROPERTY_ENTRY_U32("bus-type", 5);
+	ep_properties[1] = PROPERTY_ENTRY_U32_ARRAY_LEN("data-lanes",
+							sensor->data_lanes,
+							ssdb->lanes);
+	ep_properties[2] = remote_endpoints[(bridge.n_sensors * 2) + ENDPOINT_SENSOR];
+
+	/* cio2 endpoint props */
+	memset(cio2_properties, 0, sizeof(struct property_entry) * 3);
+
+	cio2_properties[0] = PROPERTY_ENTRY_U32_ARRAY_LEN("data-lanes",
+							  sensor->data_lanes,
+							  ssdb->lanes);
+	cio2_properties[1] = remote_endpoints[(bridge.n_sensors * 2) + ENDPOINT_CIO2];
+
+	return 0;
+}
+
+static int create_connection_swnodes(struct sensor *sensor,
+				     struct sensor_bios_data *ssdb)
+{
+	struct software_node *nodes = sensor->swnodes;
+
+	memset(nodes, 0, sizeof(struct software_node) * 6);
+
+	nodes[SWNODE_SENSOR_HID] = NODE_SENSOR(sensor->name,
+					       sensor->dev_properties);
+	nodes[SWNODE_SENSOR_PORT] = NODE_PORT("port0",
+					      &nodes[SWNODE_SENSOR_HID]);
+	nodes[SWNODE_SENSOR_ENDPOINT] = NODE_ENDPOINT("endpoint0",
+						      &nodes[SWNODE_SENSOR_PORT],
+						      sensor->ep_properties);
+	nodes[SWNODE_CIO2_PORT] = NODE_PORT(port_names[ssdb->link],
+					    &cio2_hid_node);
+	nodes[SWNODE_CIO2_ENDPOINT] = NODE_ENDPOINT("endpoint0",
+						    &nodes[SWNODE_CIO2_PORT],
+						    sensor->cio2_properties);
+
+	return 0;
+}
+
+static void cio2_bridge_unregister_sensors(void)
+{
+	struct sensor *sensor;
+	int i;
+
+	for (i = 0; i < bridge.n_sensors; i++) {
+		sensor = &bridge.sensors[i];
+
+		software_node_unregister_nodes_reverse(sensor->swnodes);
+
+		kfree(sensor->data_lanes);
+
+		put_device(sensor->dev);
+		acpi_dev_put(sensor->adev);
+	}
+}
+
+static int connect_supported_devices(struct pci_dev *cio2)
+{
+	struct sensor_bios_data ssdb;
+	struct fwnode_handle *fwnode;
+	struct acpi_device *adev;
+	struct sensor *sensor;
+	struct device *dev;
+	int i, ret;
+
+	ret = 0;
+	for (i = 0; i < ARRAY_SIZE(supported_devices); i++) {
+		adev = acpi_dev_get_first_match_dev(supported_devices[i], NULL, -1);
+		if (!adev)
+			continue;
+
+		dev = bus_find_device_by_acpi_dev(&i2c_bus_type, adev);
+		if (!dev) {
+			ret = -EPROBE_DEFER;
+			goto err_rollback;
+		}
+
+		sensor = &bridge.sensors[bridge.n_sensors];
+		sensor->dev = dev;
+		sensor->adev = adev;
+
+		snprintf(sensor->name, ACPI_ID_LEN, "%s",
+			 supported_devices[i]);
+
+		ret = get_acpi_ssdb_sensor_data(dev, &ssdb);
+		if (ret)
+			goto err_free_dev;
+
+		ret = create_fwnode_properties(sensor, &ssdb);
+		if (ret)
+			goto err_free_dev;
+
+		ret = create_connection_swnodes(sensor, &ssdb);
+		if (ret)
+			goto err_free_dev;
+
+		ret = software_node_register_nodes(sensor->swnodes);
+		if (ret)
+			goto err_free_dev;
+
+		fwnode = software_node_fwnode(&sensor->swnodes[SWNODE_SENSOR_HID]);
+		if (!fwnode) {
+			ret = -ENODEV;
+			goto err_free_swnodes;
+		}
+
+		set_secondary_fwnode(dev, fwnode);
+
+		dev_info(&cio2->dev, "Found supported device %s\n",
+			 supported_devices[i]);
+
+		bridge.n_sensors++;
+		continue;
+	}
+
+	return ret;
+
+err_free_swnodes:
+	software_node_unregister_nodes_reverse(sensor->swnodes);
+err_free_dev:
+	put_device(dev);
+err_rollback:
+	acpi_dev_put(adev);
+
+	/*
+	 * If an iteration of this loop results in -EPROBE_DEFER then
+	 * we need to roll back any sensors that were successfully
+	 * registered. Any other error and we'll skip that step, as
+	 * it seems better to have one successfully connected sensor.
+	 */
+
+	if (ret == -EPROBE_DEFER)
+		cio2_bridge_unregister_sensors();
+
+	return ret;
+}
+
+int cio2_bridge_build(struct pci_dev *cio2)
+{
+	struct fwnode_handle *fwnode;
+	int ret;
+
+	pci_dev_get(cio2);
+
+	ret = software_node_register(&cio2_hid_node);
+	if (ret < 0) {
+		dev_err(&cio2->dev, "Failed to register the CIO2 HID node\n");
+		goto err_put_cio2;
+	}
+
+	ret = connect_supported_devices(cio2);
+	if (ret == -EPROBE_DEFER)
+		goto err_unregister_cio2;
+
+	if (bridge.n_sensors == 0) {
+		ret = -EPROBE_DEFER;
+		goto err_unregister_cio2;
+	}
+
+	dev_info(&cio2->dev, "Connected %d cameras\n", bridge.n_sensors);
+
+	fwnode = software_node_fwnode(&cio2_hid_node);
+	if (!fwnode) {
+		dev_err(&cio2->dev,
+			"Error getting fwnode from cio2 software_node\n");
+		ret = -ENODEV;
+		goto err_unregister_sensors;
+	}
+
+	set_secondary_fwnode(&cio2->dev, fwnode);
+
+	return 0;
+
+err_unregister_sensors:
+	cio2_bridge_unregister_sensors();
+err_unregister_cio2:
+	software_node_unregister(&cio2_hid_node);
+err_put_cio2:
+	pci_dev_put(cio2);
+
+	return ret;
+}
+
+void cio2_bridge_burn(struct pci_dev *cio2)
+{
+	pci_dev_put(cio2);
+
+	cio2_bridge_unregister_sensors();
+
+	software_node_unregister(&cio2_hid_node);
+}
diff --git a/drivers/media/pci/intel/ipu3/cio2-bridge.h b/drivers/media/pci/intel/ipu3/cio2-bridge.h
new file mode 100644
index 000000000..077354ca8
--- /dev/null
+++ b/drivers/media/pci/intel/ipu3/cio2-bridge.h
@@ -0,0 +1,94 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Author: Dan Scally <djrscally@gmail.com> */
+#ifndef __CIO2_BRIDGE_H
+#define __CIO2_BRIDGE_H
+
+#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_SENSOR(_HID, _PROPS)		\
+	((const struct software_node) {		\
+		.name = _HID,			\
+		.properties = _PROPS,		\
+	})
+
+#define NODE_PORT(_PORT, _SENSOR_NODE)		\
+	((const struct software_node) {		\
+		_PORT,				\
+		_SENSOR_NODE,			\
+	})
+
+#define NODE_ENDPOINT(_EP, _PORT, _PROPS)	\
+	((const struct software_node) {		\
+		_EP,				\
+		_PORT,				\
+		_PROPS,				\
+	})
+
+struct sensor {
+	char name[ACPI_ID_LEN];
+	struct device *dev;
+	struct acpi_device *adev;
+	struct software_node swnodes[6];
+	struct property_entry dev_properties[3];
+	struct property_entry ep_properties[4];
+	struct property_entry cio2_properties[3];
+	u32 *data_lanes;
+};
+
+struct cio2_bridge {
+	int n_sensors;
+	struct sensor sensors[MAX_CONNECTED_DEVICES];
+};
+
+/* 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];
+} __packed__;
+
+/* Fields needed by bridge driver */
+struct sensor_bios_data {
+	struct device *dev;
+	u8 link;
+	u8 lanes;
+	u8 degree;
+	u32 mclkspeed;
+};
+
+#endif
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
index f68ef0f6b..827457110 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
@@ -1715,9 +1715,27 @@  static void cio2_queues_exit(struct cio2_device *cio2)
 static int cio2_pci_probe(struct pci_dev *pci_dev,
 			  const struct pci_device_id *id)
 {
+	struct fwnode_handle *endpoint;
 	struct cio2_device *cio2;
 	int r;
 
+	/*
+	 * On some platforms no connections to sensors are defined in firmware,
+	 * if the device has no endpoints then we can try to build those as
+	 * software_nodes parsed from SSDB.
+	 *
+	 * This may EPROBE_DEFER if supported devices are found defined in ACPI
+	 * but not yet ready for use (either not attached to the i2c bus yet,
+	 * or not done probing themselves).
+	 */
+
+	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&pci_dev->dev), NULL);
+	if (!endpoint) {
+		r = cio2_bridge_build(pci_dev);
+		if (r)
+			return r;
+	}
+
 	cio2 = devm_kzalloc(&pci_dev->dev, sizeof(*cio2), GFP_KERNEL);
 	if (!cio2)
 		return -ENOMEM;
@@ -1825,6 +1843,9 @@  static void cio2_pci_remove(struct pci_dev *pci_dev)
 {
 	struct cio2_device *cio2 = pci_get_drvdata(pci_dev);
 
+	if (is_software_node(dev_fwnode(&pci_dev->dev)))
+		cio2_bridge_burn(pci_dev);
+
 	media_device_unregister(&cio2->media_dev);
 	v4l2_async_notifier_unregister(&cio2->notifier);
 	v4l2_async_notifier_cleanup(&cio2->notifier);
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.h b/drivers/media/pci/intel/ipu3/ipu3-cio2.h
index 549b08f88..80a081d7e 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2.h
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.h
@@ -436,4 +436,13 @@  static inline struct cio2_queue *vb2q_to_cio2_queue(struct vb2_queue *vq)
 	return container_of(vq, struct cio2_queue, vbq);
 }
 
+#if IS_ENABLED(CONFIG_CIO2_BRIDGE)
+	int cio2_bridge_build(struct pci_dev *cio2);
+	void cio2_bridge_burn(struct pci_dev *cio2);
+#else
+
+	int cio2_bridge_build(struct pci_dev *cio2) { return 0; }
+	void cio2_bridge_burn(struct pci_dev *cio2) { }
+#endif
+
 #endif