All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/3] media: pci: intel: ivsc: Add driver of Intel Visual Sensing Controller(IVSC)
@ 2023-03-27  6:23 Wentong Wu
  2023-03-27  6:23 ` [PATCH v3 1/3] media: pci: intel: ivsc: Add CSI submodule Wentong Wu
                   ` (3 more replies)
  0 siblings, 4 replies; 26+ messages in thread
From: Wentong Wu @ 2023-03-27  6:23 UTC (permalink / raw)
  To: sakari.ailus, hdegoede, djrscally, laurent.pinchart, linux-media
  Cc: bingbu.cao, zhifeng.wang, xiang.ye, tian.shu.qiu, Wentong Wu

Intel Visual Sensing Controller (IVSC), codenamed "Clover Falls", is a
companion chip designed to provide secure and low power vision capability
to IA platforms. IVSC is available in existing commercial platforms from
multiple OEMs.

The primary use case of IVSC is to bring in context awareness. IVSC
interfaces directly with the platform main camera sensor via a CSI-2 link
and processes the image data with the embedded AI engine. The detected
events are sent over I2C to ISH (Intel Sensor Hub) for additional data
fusion from multiple sensors. The fusion results are used to implement
advanced use cases like:
 - Face detection to unlock screen
 - Detect user presence to manage backlight setting or waking up system

Since the Image Processing Unit(IPU) used on the host processor needs to
configure the CSI-2 link in normal camera usages, the CSI-2 link and
camera sensor can only be used in mutually-exclusive ways by host IPU and
IVSC. By default the IVSC owns the CSI-2 link and camera sensor. The IPU
driver can take ownership of the CSI-2 link and camera sensor using
interfaces exported via v4l2 sub-device.

Switching ownership requires an interface with two different hardware
modules inside IVSC. The software interface to these modules is via Intel
MEI (The Intel Management Engine) commands. These two hardware modules
have two different MEI UUIDs to enumerate. These hardware modules are:
 - ACE (Algorithm Context Engine): This module is for algorithm computing
when IVSC owns camera sensor. Also ACE module controls camera sensor's
ownership. This hardware module is used to set ownership of camera sensor.
 - CSI (Camera Serial Interface): This module is used to route camera
sensor data either to IVSC or to host for IPU driver and application.

IVSC also provides a privacy mode. When privacy mode is turned on,
camera sensor can't be used. This means that both ACE and host IPU can't
get image data. And when this mode is turned on, users are informed via
v4l2 control API.

In summary, to acquire ownership of camera by IPU driver, first ACE
module needs to be informed of ownership and then to setup MIPI CSI-2
link for the camera sensor and IPU.

Implementation:
There are two different drivers to handle ACE and CSI hardware modules
inside IVSC.
 - ivsc_csi: MEI client driver to send commands and receive notifications
from CSI module.
 - ivsc_ace: MEI client driver to send commands and get status from ACE
module.
Interface is exposed via v4l2 sub-devcie APIs to acquire and release
camera sensor and CSI-2 link.

Below diagram shows connections of IVSC/ISH/IPU/Camera sensor.
-----------------------------------------------------------------------------
| Host Processor                                                            |
|                                                                           |
|       -----------------       -----------------       ---------------     |
|       |               |       |               |       |             | I2C |
|       |      IPU      |       |      ISH      |       |camera driver|--|  |
|       |               |       |               |       |             |  |  |
|       -----------------       -----------------       ---------------  |  |
|               |                       |                      |         |  |
|               |                       |               ---------------  |  |
|               |                       |               |             |  |  |
|               |                       |               | IVSC driver |  |  |
|               |                       |               |             |  |  |
|               |                       |               ---------------  |  |
|               |                       |                      |         |  |
----------------|-----------------------|----------------------|---------|---
                | CSI                   | I2C                  |SPI      |
                |                       |                      |         |
----------------|-----------------------|----------------      |         |
| IVSC          |                                       |      |         |
|               |                                       |      |         |
|       -----------------       -----------------       |      |         |
|       |               |       |               |       |      |         |
|       |      CSI      |       |      ACE      |       |------|         |
|       |               |       |               |       |                |
|       -----------------       -----------------       |                |
|               |                       | I2C           |                |
----------------|-----------------------|----------------                |
                | CSI                   |                                |
                |                       |                                |
            --------------------------------                             |
            |                              | I2C                         |
            |         camera sensor        |-----------------------------|
            |                              |
            --------------------------------

Wentong Wu (3):
  media: pci: intel: ivsc: Add CSI submodule
  media: pci: intel: ivsc: Add ACE submodule
  ACPI: delay enumeration of devices with a _DEP pointing to INTC1059
    device

 drivers/acpi/scan.c                       |   1 +
 drivers/media/pci/Kconfig                 |   1 +
 drivers/media/pci/intel/Makefile          |   2 +
 drivers/media/pci/intel/ivsc/Kconfig      |  12 +
 drivers/media/pci/intel/ivsc/Makefile     |  10 +
 drivers/media/pci/intel/ivsc/csi_bridge.c | 332 +++++++++++++
 drivers/media/pci/intel/ivsc/csi_bridge.h | 122 +++++
 drivers/media/pci/intel/ivsc/mei_ace.c    | 534 ++++++++++++++++++++
 drivers/media/pci/intel/ivsc/mei_csi.c    | 775 ++++++++++++++++++++++++++++++
 9 files changed, 1789 insertions(+)
 create mode 100644 drivers/media/pci/intel/ivsc/Kconfig
 create mode 100644 drivers/media/pci/intel/ivsc/Makefile
 create mode 100644 drivers/media/pci/intel/ivsc/csi_bridge.c
 create mode 100644 drivers/media/pci/intel/ivsc/csi_bridge.h
 create mode 100644 drivers/media/pci/intel/ivsc/mei_ace.c
 create mode 100644 drivers/media/pci/intel/ivsc/mei_csi.c

-- 
2.7.4


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH v3 1/3] media: pci: intel: ivsc: Add CSI submodule
  2023-03-27  6:23 [PATCH v3 0/3] media: pci: intel: ivsc: Add driver of Intel Visual Sensing Controller(IVSC) Wentong Wu
@ 2023-03-27  6:23 ` Wentong Wu
  2023-03-27 10:01   ` kernel test robot
                     ` (3 more replies)
  2023-03-27  6:23 ` [PATCH v3 2/3] media: pci: intel: ivsc: Add ACE submodule Wentong Wu
                   ` (2 subsequent siblings)
  3 siblings, 4 replies; 26+ messages in thread
From: Wentong Wu @ 2023-03-27  6:23 UTC (permalink / raw)
  To: sakari.ailus, hdegoede, djrscally, laurent.pinchart, linux-media
  Cc: bingbu.cao, zhifeng.wang, xiang.ye, tian.shu.qiu, Wentong Wu

CSI is a submodule of IVSC which can route camera sensor data
to the outbound MIPI CSI-2 interface.

The interface communicating with firmware is via MEI. There is
a separate MEI UUID, which this driver uses to enumerate.

To route camera sensor data to host, the information of link
frequency and number of data lanes is sent to firmware by
sending MEI command when starting stream.

CSI also provides a privacy mode. When privacy mode is turned
on, camera sensor can't be used. This means that both IVSC and
host Image Processing Unit(IPU) can't get image data. And when
this mode is turned on, host Image Processing Unit(IPU) driver
is informed via v4l2 control callback, so that user can be
notified.

Signed-off-by: Wentong Wu <wentong.wu@intel.com>
---
 drivers/media/pci/Kconfig                 |   1 +
 drivers/media/pci/intel/Makefile          |   2 +
 drivers/media/pci/intel/ivsc/Kconfig      |  12 +
 drivers/media/pci/intel/ivsc/Makefile     |   7 +
 drivers/media/pci/intel/ivsc/csi_bridge.c | 332 +++++++++++++
 drivers/media/pci/intel/ivsc/csi_bridge.h | 122 +++++
 drivers/media/pci/intel/ivsc/mei_csi.c    | 775 ++++++++++++++++++++++++++++++
 7 files changed, 1251 insertions(+)
 create mode 100644 drivers/media/pci/intel/ivsc/Kconfig
 create mode 100644 drivers/media/pci/intel/ivsc/Makefile
 create mode 100644 drivers/media/pci/intel/ivsc/csi_bridge.c
 create mode 100644 drivers/media/pci/intel/ivsc/csi_bridge.h
 create mode 100644 drivers/media/pci/intel/ivsc/mei_csi.c

diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig
index dff0b45..c157f7c 100644
--- a/drivers/media/pci/Kconfig
+++ b/drivers/media/pci/Kconfig
@@ -72,6 +72,7 @@ config VIDEO_PCI_SKELETON
 	  when developing new drivers.
 
 source "drivers/media/pci/intel/ipu3/Kconfig"
+source "drivers/media/pci/intel/ivsc/Kconfig"
 
 endif #MEDIA_PCI_SUPPORT
 endif #PCI
diff --git a/drivers/media/pci/intel/Makefile b/drivers/media/pci/intel/Makefile
index 0b4236c..d27ca63 100644
--- a/drivers/media/pci/intel/Makefile
+++ b/drivers/media/pci/intel/Makefile
@@ -4,3 +4,5 @@
 #
 
 obj-y	+= ipu3/
+
+obj-$(CONFIG_INTEL_VSC) += ivsc/
diff --git a/drivers/media/pci/intel/ivsc/Kconfig b/drivers/media/pci/intel/ivsc/Kconfig
new file mode 100644
index 0000000..9535ac1
--- /dev/null
+++ b/drivers/media/pci/intel/ivsc/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# Copyright (C) 2023, Intel Corporation. All rights reserved.
+
+config INTEL_VSC
+	tristate "Intel Visual Sensing Controller"
+	depends on INTEL_MEI
+	help
+	  This adds support for Intel Visual Sensing Controller (IVSC).
+
+	  Enables the IVSC firmware services required for controlling
+	  camera sensor ownership and CSI-2 link through Image Processing
+	  Unit(IPU) driver of Intel.
diff --git a/drivers/media/pci/intel/ivsc/Makefile b/drivers/media/pci/intel/ivsc/Makefile
new file mode 100644
index 0000000..7e4c5f0
--- /dev/null
+++ b/drivers/media/pci/intel/ivsc/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2023, Intel Corporation. All rights reserved.
+
+obj-$(CONFIG_INTEL_VSC) += ivsc-csi.o
+ivsc-csi-y += mei_csi.o
+ivsc-csi-y += csi_bridge.o
diff --git a/drivers/media/pci/intel/ivsc/csi_bridge.c b/drivers/media/pci/intel/ivsc/csi_bridge.c
new file mode 100644
index 0000000..30279b5
--- /dev/null
+++ b/drivers/media/pci/intel/ivsc/csi_bridge.c
@@ -0,0 +1,332 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023 Intel Corporation.
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/property.h>
+#include <media/v4l2-fwnode.h>
+
+#include "csi_bridge.h"
+
+static const struct csi_property_names prop_names = {
+	.clock_frequency = "clock-frequency",
+	.rotation = "rotation",
+	.orientation = "orientation",
+	.bus_type = "bus-type",
+	.data_lanes = "data-lanes",
+	.remote_endpoint = "remote-endpoint",
+	.link_frequencies = "link-frequencies",
+};
+
+static int csi_bridge_read_acpi_buffer(struct acpi_device *adev, char *id,
+				       void *data, u32 size)
+{
+	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+	union acpi_object *obj;
+	acpi_status status;
+	int ret = 0;
+
+	status = acpi_evaluate_object(adev->handle, id, NULL, &buffer);
+	if (ACPI_FAILURE(status))
+		return -ENODEV;
+
+	obj = buffer.pointer;
+	if (!obj) {
+		dev_err(&adev->dev, "Couldn't locate ACPI buffer\n");
+		return -ENODEV;
+	}
+
+	if (obj->type != ACPI_TYPE_BUFFER) {
+		dev_err(&adev->dev, "Not an ACPI buffer\n");
+		ret = -ENODEV;
+		goto out_free_buff;
+	}
+
+	if (obj->buffer.length > size) {
+		dev_err(&adev->dev, "Given buffer is too small\n");
+		ret = -EINVAL;
+		goto out_free_buff;
+	}
+
+	memcpy(data, obj->buffer.pointer, obj->buffer.length);
+
+out_free_buff:
+	kfree(buffer.pointer);
+	return ret;
+}
+
+static u32 csi_bridge_parse_rotation(struct csi_sensor *sensor)
+{
+	switch (sensor->ssdb.degree) {
+	case CSI_SENSOR_ROTATION_NORMAL:
+		return 0;
+	case CSI_SENSOR_ROTATION_INVERTED:
+		return 180;
+	default:
+		dev_warn(&sensor->adev->dev,
+			 "Unknown rotation %d. Assume 0 degree rotation\n",
+			 sensor->ssdb.degree);
+		return 0;
+	}
+}
+
+static enum v4l2_fwnode_orientation csi_bridge_parse_orientation(struct csi_sensor *sensor)
+{
+	switch (sensor->pld->panel) {
+	case ACPI_PLD_PANEL_FRONT:
+		return V4L2_FWNODE_ORIENTATION_FRONT;
+	case ACPI_PLD_PANEL_BACK:
+		return V4L2_FWNODE_ORIENTATION_BACK;
+	case ACPI_PLD_PANEL_TOP:
+	case ACPI_PLD_PANEL_LEFT:
+	case ACPI_PLD_PANEL_RIGHT:
+	case ACPI_PLD_PANEL_UNKNOWN:
+		return V4L2_FWNODE_ORIENTATION_EXTERNAL;
+	default:
+		dev_warn(&sensor->adev->dev, "Unknown _PLD panel value %d\n",
+			 sensor->pld->panel);
+		return V4L2_FWNODE_ORIENTATION_EXTERNAL;
+	}
+}
+
+static void csi_bridge_create_fwnode_properties(struct csi_bridge *bridge)
+{
+	struct csi_sensor *sensor = &bridge->sensor;
+	enum v4l2_fwnode_orientation orientation;
+	u32 rotation;
+
+	rotation = csi_bridge_parse_rotation(sensor);
+	orientation = csi_bridge_parse_orientation(sensor);
+
+	sensor->prop_names = prop_names;
+
+	sensor->local_ref[0] = SOFTWARE_NODE_REFERENCE(
+					&sensor->swnodes[SWNODE_CSI_ENDPOINT]);
+	sensor->remote_ref[0] = SOFTWARE_NODE_REFERENCE(
+					&sensor->swnodes[SWNODE_SENSOR_ENDPOINT]);
+
+	sensor->dev_properties[0] = PROPERTY_ENTRY_U32(
+					sensor->prop_names.clock_frequency,
+					sensor->ssdb.mclkspeed);
+	sensor->dev_properties[1] = PROPERTY_ENTRY_U32(
+					sensor->prop_names.rotation,
+					rotation);
+	sensor->dev_properties[2] = PROPERTY_ENTRY_U32(
+					sensor->prop_names.orientation,
+					orientation);
+
+	sensor->ep_properties[0] = PROPERTY_ENTRY_U32(
+					sensor->prop_names.bus_type,
+					V4L2_FWNODE_BUS_TYPE_CSI2_DPHY);
+	sensor->ep_properties[1] = PROPERTY_ENTRY_U32_ARRAY_LEN(
+					sensor->prop_names.data_lanes,
+					sensor->data_lanes,
+					sensor->ssdb.lanes);
+	sensor->ep_properties[2] = PROPERTY_ENTRY_REF_ARRAY(
+					sensor->prop_names.remote_endpoint,
+					sensor->local_ref);
+
+	sensor->csi_properties[0] = PROPERTY_ENTRY_U32_ARRAY_LEN(
+					sensor->prop_names.data_lanes,
+					sensor->data_lanes,
+					sensor->ssdb.lanes);
+	sensor->csi_properties[1] = PROPERTY_ENTRY_REF_ARRAY(
+					sensor->prop_names.remote_endpoint,
+					sensor->remote_ref);
+}
+
+static void csi_bridge_init_swnode_names(struct csi_sensor *sensor)
+{
+	snprintf(sensor->node_names.remote_port,
+		 sizeof(sensor->node_names.remote_port),
+		 SWNODE_GRAPH_PORT_NAME_FMT, sensor->ssdb.link);
+	snprintf(sensor->node_names.port,
+		 sizeof(sensor->node_names.port),
+		 SWNODE_GRAPH_PORT_NAME_FMT, 0); /* Always port 0 */
+	snprintf(sensor->node_names.endpoint,
+		 sizeof(sensor->node_names.endpoint),
+		 SWNODE_GRAPH_ENDPOINT_NAME_FMT, 0); /* And endpoint 0 */
+}
+
+static void csi_bridge_create_connection_swnodes(struct csi_bridge *bridge)
+{
+	struct csi_sensor *sensor = &bridge->sensor;
+	struct software_node *nodes = sensor->swnodes;
+
+	csi_bridge_init_swnode_names(sensor);
+
+	nodes[SWNODE_SENSOR_HID] = NODE_SENSOR(sensor->name,
+					       sensor->dev_properties);
+	nodes[SWNODE_SENSOR_PORT] = NODE_PORT(sensor->node_names.port,
+					      &nodes[SWNODE_SENSOR_HID]);
+	nodes[SWNODE_SENSOR_ENDPOINT] = NODE_ENDPOINT(sensor->node_names.endpoint,
+						      &nodes[SWNODE_SENSOR_PORT],
+						      sensor->ep_properties);
+
+	nodes[SWNODE_CSI_PORT] = NODE_PORT(sensor->node_names.remote_port,
+					   &bridge->csi_hid_node);
+	nodes[SWNODE_CSI_ENDPOINT] = NODE_ENDPOINT(sensor->node_names.endpoint,
+						   &nodes[SWNODE_CSI_PORT],
+						   sensor->csi_properties);
+}
+
+static inline void csi_bridge_unregister_sensor(struct csi_bridge *bridge)
+{
+	struct csi_sensor *sensor = &bridge->sensor;
+
+	software_node_unregister_nodes(sensor->swnodes);
+	ACPI_FREE(sensor->pld);
+	acpi_dev_put(sensor->adev);
+}
+
+static int csi_bridge_connect_sensor(struct csi_bridge *bridge)
+{
+	struct csi_sensor *sensor = &bridge->sensor;
+	struct device *dev = &bridge->csi_dev->dev;
+	struct fwnode_handle *fwnode;
+	struct acpi_device *adev;
+	acpi_status status;
+	unsigned int i;
+	int ret;
+
+	/* sensor's ACPI _DEP is mei bus device */
+	adev = acpi_dev_get_next_consumer_dev(ACPI_COMPANION(dev->parent), NULL);
+	if (!adev)
+		return -ENODEV;
+
+	ret = csi_bridge_read_acpi_buffer(adev, "SSDB", &sensor->ssdb,
+					  sizeof(sensor->ssdb));
+	if (ret)
+		goto err_put_adev;
+
+	status = acpi_get_physical_device_location(adev->handle, &sensor->pld);
+	if (ACPI_FAILURE(status)) {
+		ret = -ENODEV;
+		goto err_put_adev;
+	}
+
+	if (sensor->ssdb.lanes > CSI_MAX_LANES) {
+		dev_err(&adev->dev, "Number of lanes in SSDB is invalid\n");
+		ret = -EINVAL;
+		goto err_free_pld;
+	}
+
+	/* read sensor name */
+	ret = csi_bridge_read_acpi_buffer(ACPI_COMPANION(dev->parent), "SID",
+					  sensor->name, sizeof(sensor->name));
+	if (ret)
+		goto err_free_pld;
+
+	for (i = 0; i < CSI_MAX_LANES; i++)
+		sensor->data_lanes[i] = i + 1;
+
+	/* create properties */
+	csi_bridge_create_fwnode_properties(bridge);
+
+	/* connect software nodes */
+	csi_bridge_create_connection_swnodes(bridge);
+
+	ret = software_node_register_nodes(sensor->swnodes);
+	if (ret)
+		goto err_free_pld;
+
+	fwnode = software_node_fwnode(&sensor->swnodes[SWNODE_SENSOR_HID]);
+	if (!fwnode) {
+		ret = -ENODEV;
+		goto err_free_swnodes;
+	}
+
+	sensor->adev = acpi_dev_get(adev);
+	adev->fwnode.secondary = fwnode;
+
+	dev_info(dev, "Found sensor %s\n", acpi_dev_name(adev));
+
+	return 0;
+
+err_free_swnodes:
+	software_node_unregister_nodes(sensor->swnodes);
+err_free_pld:
+	ACPI_FREE(sensor->pld);
+err_put_adev:
+	acpi_dev_put(adev);
+	return ret;
+}
+
+static int csi_bridge_sensor_is_ready(struct mei_cl_device *csi_dev)
+{
+	struct acpi_device *sensor;
+	struct acpi_device *adev;
+	bool ready = true;
+
+	/* sensor's ACPI _DEP is mei bus device */
+	adev = ACPI_COMPANION(csi_dev->dev.parent);
+
+	sensor = acpi_dev_get_next_consumer_dev(adev, NULL);
+	if (!sensor) {
+		ready = false;
+		goto err;
+	}
+
+	if (!sensor->status.enabled) {
+		ready = false;
+		goto err;
+	}
+
+	if (!acpi_dev_ready_for_enumeration(sensor))
+		ready = false;
+
+err:
+	return ready;
+}
+
+int csi_bridge_init(struct mei_cl_device *csi_dev)
+{
+	struct device *dev = &csi_dev->dev;
+	struct fwnode_handle *fwnode;
+	struct csi_bridge *bridge;
+	int ret;
+
+	if (!csi_bridge_sensor_is_ready(csi_dev))
+		return -EPROBE_DEFER;
+
+	bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
+	if (!bridge)
+		return -ENOMEM;
+	bridge->csi_dev = csi_dev;
+
+	strscpy(bridge->csi_node_name, CSI_HID,
+		sizeof(bridge->csi_node_name));
+	bridge->csi_hid_node.name = bridge->csi_node_name;
+
+	ret = software_node_register(&bridge->csi_hid_node);
+	if (ret < 0) {
+		dev_err(dev, "Failed to register the CSI HID node\n");
+		goto err_free_bridge;
+	}
+
+	ret = csi_bridge_connect_sensor(bridge);
+	if (ret)
+		goto err_unregister_csi;
+
+	fwnode = software_node_fwnode(&bridge->csi_hid_node);
+	if (!fwnode) {
+		dev_err(dev, "Error getting fwnode from csi software_node\n");
+		ret = -ENODEV;
+		goto err_unregister_sensor;
+	}
+
+	set_secondary_fwnode(dev, fwnode);
+
+	return 0;
+
+err_unregister_sensor:
+	csi_bridge_unregister_sensor(bridge);
+err_unregister_csi:
+	software_node_unregister(&bridge->csi_hid_node);
+err_free_bridge:
+	kfree(bridge);
+
+	return ret;
+}
diff --git a/drivers/media/pci/intel/ivsc/csi_bridge.h b/drivers/media/pci/intel/ivsc/csi_bridge.h
new file mode 100644
index 0000000..d6febd7
--- /dev/null
+++ b/drivers/media/pci/intel/ivsc/csi_bridge.h
@@ -0,0 +1,122 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Intel Corporation.
+ */
+
+#ifndef __CSI_BRIDGE_H
+#define __CSI_BRIDGE_H
+
+#include <linux/mei_cl_bus.h>
+#include <linux/property.h>
+#include <linux/types.h>
+
+#define CSI_HID				"INT343E"
+#define CSI_MAX_LANES				4
+
+/* Values are educated guesses as we don't have a spec */
+#define CSI_SENSOR_ROTATION_NORMAL		0
+#define CSI_SENSOR_ROTATION_INVERTED		1
+
+#define NODE_SENSOR(_HID, _PROPS)		\
+	(const struct software_node) {		\
+		.name = _HID,			\
+		.properties = _PROPS,		\
+	}
+
+#define NODE_PORT(_PORT, _SENSOR_NODE)		\
+	(const struct software_node) {		\
+		.name = _PORT,			\
+		.parent = _SENSOR_NODE,		\
+	}
+
+#define NODE_ENDPOINT(_EP, _PORT, _PROPS)	\
+	(const struct software_node) {		\
+		.name = _EP,			\
+		.parent = _PORT,		\
+		.properties = _PROPS,		\
+	}
+
+enum csi_sensor_swnodes {
+	SWNODE_SENSOR_HID,
+	SWNODE_SENSOR_PORT,
+	SWNODE_SENSOR_ENDPOINT,
+	SWNODE_CSI_PORT,
+	SWNODE_CSI_ENDPOINT,
+	SWNODE_COUNT
+};
+
+/* Data representation as it is in ACPI SSDB buffer */
+struct csi_sensor_ssdb {
+	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;
+
+struct csi_property_names {
+	char clock_frequency[16];
+	char rotation[9];
+	char orientation[12];
+	char bus_type[9];
+	char data_lanes[11];
+	char remote_endpoint[16];
+	char link_frequencies[17];
+};
+
+struct csi_node_names {
+	char port[7];
+	char endpoint[11];
+	char remote_port[7];
+};
+
+struct csi_sensor {
+	char name[ACPI_ID_LEN];
+	struct acpi_device *adev;
+
+	u32 data_lanes[CSI_MAX_LANES];
+
+	/* SWNODE_COUNT + 1 for terminating empty node */
+	struct software_node swnodes[SWNODE_COUNT + 1];
+	struct csi_node_names node_names;
+
+	struct csi_sensor_ssdb ssdb;
+	struct acpi_pld_info *pld;
+
+	struct csi_property_names prop_names;
+	struct property_entry ep_properties[5];
+	struct property_entry dev_properties[5];
+	struct property_entry csi_properties[3];
+	struct software_node_ref_args local_ref[1];
+	struct software_node_ref_args remote_ref[1];
+};
+
+struct csi_bridge {
+	char csi_node_name[ACPI_ID_LEN];
+	struct software_node csi_hid_node;
+	struct csi_sensor sensor;
+	struct mei_cl_device *csi_dev;
+};
+
+#endif
diff --git a/drivers/media/pci/intel/ivsc/mei_csi.c b/drivers/media/pci/intel/ivsc/mei_csi.c
new file mode 100644
index 0000000..6a01537
--- /dev/null
+++ b/drivers/media/pci/intel/ivsc/mei_csi.c
@@ -0,0 +1,775 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023 Intel Corporation. All rights reserved.
+ * Intel Visual Sensing Controller CSI Linux driver
+ */
+
+/*
+ * To set ownership of CSI-2 link and to configure CSI-2 link, there
+ * are specific commands, which are sent via MEI protocol. The send
+ * command function uses "completion" as a synchronization mechanism.
+ * The response for command is received via a mei callback which wakes
+ * up the caller. There can be only one outstanding command at a time.
+ */
+
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/mei_cl_bus.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/units.h>
+#include <linux/uuid.h>
+#include <linux/workqueue.h>
+
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#define MEI_CSI_DRIVER_NAME "ivsc_csi"
+
+/* the 5s used here is based on experiment */
+#define CSI_CMD_TIMEOUT (5 * HZ)
+/* to setup CSI-2 link an extra delay needed and determined experimentally */
+#define CSI_FW_READY_DELAY_MS 100
+/* link frequency unit is 100kHz */
+#define CSI_LINK_FREQ(x) ((u32)(div_u64(x, 100 * HZ_PER_KHZ)))
+
+/*
+ * identify the command id supported by firmware
+ * IPC, as well as the privacy notification id
+ * used when processing privacy event.
+ */
+enum csi_cmd_id {
+	/* used to set csi ownership */
+	CSI_SET_OWNER = 0,
+
+	/* used to configure CSI-2 link */
+	CSI_SET_CONF = 2,
+
+	/* privacy notification id used when privacy state changes */
+	CSI_PRIVACY_NOTIF = 6,
+};
+
+/* CSI-2 link ownership definition */
+enum csi_link_owner {
+	CSI_LINK_IVSC,
+	CSI_LINK_HOST,
+};
+
+/* privacy status definition */
+enum ivsc_privacy_status {
+	CSI_PRIVACY_OFF,
+	CSI_PRIVACY_ON,
+	CSI_PRIVACY_MAX,
+};
+
+enum csi_pads {
+	CSI_PAD_SOURCE,
+	CSI_PAD_SINK,
+	CSI_NUM_PADS
+};
+
+/* configuration of the CSI-2 link between host and IVSC */
+struct csi_link_cfg {
+	/* number of data lanes used on the CSI-2 link */
+	u32 nr_of_lanes;
+
+	/* frequency of the CSI-2 link */
+	u32 link_freq;
+
+	/* for future use */
+	u32 rsvd[2];
+} __packed;
+
+/* CSI command structure */
+struct csi_cmd {
+	u32 cmd_id;
+	union _cmd_param {
+		u32 param;
+		struct csi_link_cfg conf;
+	} param;
+} __packed;
+
+/* CSI notification structure */
+struct csi_notif {
+	u32 cmd_id;
+	int status;
+	union _resp_cont {
+		u32 cont;
+		struct csi_link_cfg conf;
+	} cont;
+} __packed;
+
+struct mei_csi {
+	struct mei_cl_device *cldev;
+
+	/* command response */
+	struct csi_notif cmd_response;
+	/* used to wait for command response from firmware */
+	struct completion cmd_completion;
+	/* protect command download */
+	struct mutex lock;
+
+	struct v4l2_subdev subdev;
+	struct v4l2_subdev *remote;
+	struct v4l2_async_notifier notifier;
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct v4l2_ctrl *privacy_ctrl;
+	unsigned int remote_pad;
+	/* start streaming or not */
+	int streaming;
+
+	struct media_pad pads[CSI_NUM_PADS];
+	struct v4l2_mbus_framefmt format_mbus[CSI_NUM_PADS];
+
+	/* number of data lanes used on the CSI-2 link */
+	u32 nr_of_lanes;
+	/* frequency of the CSI-2 link */
+	u64 link_freq;
+
+	/* privacy status */
+	enum ivsc_privacy_status status;
+};
+
+static const struct v4l2_mbus_framefmt mei_csi_format_mbus_default = {
+	.width = 1,
+	.height = 1,
+	.code = MEDIA_BUS_FMT_Y8_1X8,
+	.field = V4L2_FIELD_NONE,
+};
+
+int csi_bridge_init(struct mei_cl_device *csi_dev);
+
+static inline struct mei_csi *notifier_to_csi(struct v4l2_async_notifier *n)
+{
+	return container_of(n, struct mei_csi, notifier);
+}
+
+static inline struct mei_csi *sd_to_csi(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct mei_csi, subdev);
+}
+
+/* send a command to firmware and mutex must be held by caller */
+static int mei_csi_send(struct mei_csi *csi, u8 *buf, size_t len)
+{
+	struct csi_cmd *cmd = (struct csi_cmd *)buf;
+	int ret;
+
+	reinit_completion(&csi->cmd_completion);
+
+	ret = mei_cldev_send(csi->cldev, buf, len);
+	if (ret < 0)
+		goto out;
+
+	ret = wait_for_completion_killable_timeout(&csi->cmd_completion,
+						   CSI_CMD_TIMEOUT);
+	if (ret < 0) {
+		goto out;
+	} else if (!ret) {
+		ret = -ETIMEDOUT;
+		goto out;
+	}
+
+	/* command response status */
+	ret = csi->cmd_response.status;
+	if (ret) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (csi->cmd_response.cmd_id != cmd->cmd_id)
+		ret = -EINVAL;
+
+out:
+	return ret;
+}
+
+/* set CSI-2 link ownership */
+static int csi_set_link_owner(struct mei_csi *csi, enum csi_link_owner owner)
+{
+	struct csi_cmd cmd = { 0 };
+	size_t cmd_size;
+	int ret;
+
+	cmd.cmd_id = CSI_SET_OWNER;
+	cmd.param.param = owner;
+	cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.param);
+
+	mutex_lock(&csi->lock);
+
+	ret = mei_csi_send(csi, (u8 *)&cmd, cmd_size);
+
+	mutex_unlock(&csi->lock);
+
+	return ret;
+}
+
+/* configure CSI-2 link between host and IVSC */
+static int csi_set_link_cfg(struct mei_csi *csi)
+{
+	struct csi_cmd cmd = { 0 };
+	size_t cmd_size;
+	int ret;
+
+	cmd.cmd_id = CSI_SET_CONF;
+	cmd.param.conf.nr_of_lanes = csi->nr_of_lanes;
+	cmd.param.conf.link_freq = CSI_LINK_FREQ(csi->link_freq);
+	cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.conf);
+
+	mutex_lock(&csi->lock);
+
+	ret = mei_csi_send(csi, (u8 *)&cmd, cmd_size);
+	/*
+	 * wait configuration ready if download success. placing
+	 * delay under mutex is to make sure current command flow
+	 * completed before starting a possible new one.
+	 */
+	if (!ret)
+		msleep(CSI_FW_READY_DELAY_MS);
+
+	mutex_unlock(&csi->lock);
+
+	return ret;
+}
+
+/* callback for receive */
+static void mei_csi_rx(struct mei_cl_device *cldev)
+{
+	struct mei_csi *csi = mei_cldev_get_drvdata(cldev);
+	struct csi_notif notif = { 0 };
+	int ret;
+
+	ret = mei_cldev_recv(cldev, (u8 *)&notif, sizeof(notif));
+	if (ret < 0) {
+		dev_err(&cldev->dev, "recv error: %d\n", ret);
+		return;
+	}
+
+	switch (notif.cmd_id) {
+	case CSI_PRIVACY_NOTIF:
+		if (notif.cont.cont < CSI_PRIVACY_MAX) {
+			csi->status = notif.cont.cont;
+			v4l2_ctrl_s_ctrl(csi->privacy_ctrl, csi->status);
+		}
+		break;
+	case CSI_SET_OWNER:
+	case CSI_SET_CONF:
+		memcpy(&csi->cmd_response, &notif, ret);
+
+		complete(&csi->cmd_completion);
+		break;
+	default:
+		break;
+	}
+}
+
+static int mei_csi_pre_streamon(struct v4l2_subdev *sd, u32 flags)
+{
+	struct v4l2_querymenu qm = { .id = V4L2_CID_LINK_FREQ, };
+	struct v4l2_mbus_config mbus_config = { 0 };
+	struct mei_csi *csi = sd_to_csi(sd);
+	struct v4l2_ctrl *ctrl;
+	int ret = 0;
+
+	if (!csi->remote)
+		return -ENODEV;
+
+	ret = v4l2_subdev_call(csi->remote, pad, get_mbus_config,
+			       csi->remote_pad, &mbus_config);
+	if (ret)
+		return ret;
+
+	if (mbus_config.type != V4L2_MBUS_CSI2_DPHY)
+		return -EINVAL;
+
+	csi->nr_of_lanes = mbus_config.bus.mipi_csi2.num_data_lanes;
+
+	ctrl = v4l2_ctrl_find(csi->remote->ctrl_handler, V4L2_CID_LINK_FREQ);
+	if (!ctrl)
+		return -EINVAL;
+	qm.index = v4l2_ctrl_g_ctrl(ctrl);
+
+	ret = v4l2_querymenu(csi->remote->ctrl_handler, &qm);
+	if (ret)
+		return ret;
+
+	csi->link_freq = qm.value;
+
+	return ret;
+}
+
+static int mei_csi_set_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct mei_csi *csi = sd_to_csi(sd);
+	int ret = 0;
+
+	if (enable && csi->streaming == 0) {
+		/* switch CSI-2 link to host */
+		ret = csi_set_link_owner(csi, CSI_LINK_HOST);
+		if (ret < 0)
+			goto err;
+
+		/* configure CSI-2 link */
+		ret = csi_set_link_cfg(csi);
+		if (ret < 0)
+			goto err;
+
+		ret = v4l2_subdev_call(csi->remote, video, s_stream, 1);
+		if (ret)
+			goto err;
+	} else if (!enable && csi->streaming == 1) {
+		v4l2_subdev_call(csi->remote, video, s_stream, 0);
+
+		/* switch CSI-2 link to IVSC */
+		ret = csi_set_link_owner(csi, CSI_LINK_IVSC);
+		if (ret < 0)
+			goto err;
+	}
+
+	csi->streaming = enable;
+
+err:
+	return ret;
+}
+
+static struct v4l2_mbus_framefmt *
+mei_csi_get_pad_format(struct v4l2_subdev *sd,
+		       struct v4l2_subdev_state *sd_state,
+		       unsigned int pad, u32 which)
+{
+	struct mei_csi *csi = sd_to_csi(sd);
+
+	switch (which) {
+	case V4L2_SUBDEV_FORMAT_TRY:
+		return v4l2_subdev_get_try_format(sd, sd_state, pad);
+	case V4L2_SUBDEV_FORMAT_ACTIVE:
+		return &csi->format_mbus[pad];
+	default:
+		return NULL;
+	}
+}
+
+static int mei_csi_init_cfg(struct v4l2_subdev *sd,
+			    struct v4l2_subdev_state *sd_state)
+{
+	struct v4l2_mbus_framefmt *mbusformat;
+	struct mei_csi *csi = sd_to_csi(sd);
+	unsigned int i;
+
+	mutex_lock(&csi->lock);
+
+	for (i = 0; i < sd->entity.num_pads; i++) {
+		mbusformat = v4l2_subdev_get_try_format(sd, sd_state, i);
+		*mbusformat = mei_csi_format_mbus_default;
+	}
+
+	mutex_unlock(&csi->lock);
+
+	return 0;
+}
+
+static int mei_csi_get_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_state *sd_state,
+			   struct v4l2_subdev_format *format)
+{
+	struct v4l2_mbus_framefmt *mbusformat;
+	struct mei_csi *csi = sd_to_csi(sd);
+
+	mutex_lock(&csi->lock);
+
+	mbusformat = mei_csi_get_pad_format(sd, sd_state, format->pad,
+					    format->which);
+	if (mbusformat)
+		format->format = *mbusformat;
+
+	mutex_unlock(&csi->lock);
+
+	return 0;
+}
+
+static int mei_csi_set_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_state *sd_state,
+			   struct v4l2_subdev_format *format)
+{
+	struct v4l2_mbus_framefmt *source_mbusformat;
+	struct v4l2_mbus_framefmt *mbusformat;
+	struct mei_csi *csi = sd_to_csi(sd);
+	struct media_pad *pad;
+
+	mbusformat = mei_csi_get_pad_format(sd, sd_state, format->pad,
+					    format->which);
+	if (!mbusformat)
+		return -EINVAL;
+
+	source_mbusformat = mei_csi_get_pad_format(sd, sd_state,
+						   CSI_PAD_SOURCE,
+						   format->which);
+	if (!source_mbusformat)
+		return -EINVAL;
+
+	v4l_bound_align_image(&format->format.width, 1, 65536, 0,
+			      &format->format.height, 1, 65536, 0, 0);
+
+	switch (format->format.code) {
+	case MEDIA_BUS_FMT_RGB444_1X12:
+	case MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE:
+	case MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE:
+	case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE:
+	case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE:
+	case MEDIA_BUS_FMT_RGB565_1X16:
+	case MEDIA_BUS_FMT_BGR565_2X8_BE:
+	case MEDIA_BUS_FMT_BGR565_2X8_LE:
+	case MEDIA_BUS_FMT_RGB565_2X8_BE:
+	case MEDIA_BUS_FMT_RGB565_2X8_LE:
+	case MEDIA_BUS_FMT_RGB666_1X18:
+	case MEDIA_BUS_FMT_RBG888_1X24:
+	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
+	case MEDIA_BUS_FMT_BGR888_1X24:
+	case MEDIA_BUS_FMT_GBR888_1X24:
+	case MEDIA_BUS_FMT_RGB888_1X24:
+	case MEDIA_BUS_FMT_RGB888_2X12_BE:
+	case MEDIA_BUS_FMT_RGB888_2X12_LE:
+	case MEDIA_BUS_FMT_ARGB8888_1X32:
+	case MEDIA_BUS_FMT_RGB888_1X32_PADHI:
+	case MEDIA_BUS_FMT_RGB101010_1X30:
+	case MEDIA_BUS_FMT_RGB121212_1X36:
+	case MEDIA_BUS_FMT_RGB161616_1X48:
+	case MEDIA_BUS_FMT_Y8_1X8:
+	case MEDIA_BUS_FMT_UV8_1X8:
+	case MEDIA_BUS_FMT_UYVY8_1_5X8:
+	case MEDIA_BUS_FMT_VYUY8_1_5X8:
+	case MEDIA_BUS_FMT_YUYV8_1_5X8:
+	case MEDIA_BUS_FMT_YVYU8_1_5X8:
+	case MEDIA_BUS_FMT_UYVY8_2X8:
+	case MEDIA_BUS_FMT_VYUY8_2X8:
+	case MEDIA_BUS_FMT_YUYV8_2X8:
+	case MEDIA_BUS_FMT_YVYU8_2X8:
+	case MEDIA_BUS_FMT_Y10_1X10:
+	case MEDIA_BUS_FMT_UYVY10_2X10:
+	case MEDIA_BUS_FMT_VYUY10_2X10:
+	case MEDIA_BUS_FMT_YUYV10_2X10:
+	case MEDIA_BUS_FMT_YVYU10_2X10:
+	case MEDIA_BUS_FMT_Y12_1X12:
+	case MEDIA_BUS_FMT_UYVY12_2X12:
+	case MEDIA_BUS_FMT_VYUY12_2X12:
+	case MEDIA_BUS_FMT_YUYV12_2X12:
+	case MEDIA_BUS_FMT_YVYU12_2X12:
+	case MEDIA_BUS_FMT_UYVY8_1X16:
+	case MEDIA_BUS_FMT_VYUY8_1X16:
+	case MEDIA_BUS_FMT_YUYV8_1X16:
+	case MEDIA_BUS_FMT_YVYU8_1X16:
+	case MEDIA_BUS_FMT_YDYUYDYV8_1X16:
+	case MEDIA_BUS_FMT_UYVY10_1X20:
+	case MEDIA_BUS_FMT_VYUY10_1X20:
+	case MEDIA_BUS_FMT_YUYV10_1X20:
+	case MEDIA_BUS_FMT_YVYU10_1X20:
+	case MEDIA_BUS_FMT_VUY8_1X24:
+	case MEDIA_BUS_FMT_YUV8_1X24:
+	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+	case MEDIA_BUS_FMT_UYVY12_1X24:
+	case MEDIA_BUS_FMT_VYUY12_1X24:
+	case MEDIA_BUS_FMT_YUYV12_1X24:
+	case MEDIA_BUS_FMT_YVYU12_1X24:
+	case MEDIA_BUS_FMT_YUV10_1X30:
+	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+	case MEDIA_BUS_FMT_AYUV8_1X32:
+	case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
+	case MEDIA_BUS_FMT_YUV12_1X36:
+	case MEDIA_BUS_FMT_YUV16_1X48:
+	case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
+	case MEDIA_BUS_FMT_JPEG_1X8:
+	case MEDIA_BUS_FMT_AHSV8888_1X32:
+	case MEDIA_BUS_FMT_SBGGR8_1X8:
+	case MEDIA_BUS_FMT_SGBRG8_1X8:
+	case MEDIA_BUS_FMT_SGRBG8_1X8:
+	case MEDIA_BUS_FMT_SRGGB8_1X8:
+	case MEDIA_BUS_FMT_SBGGR10_1X10:
+	case MEDIA_BUS_FMT_SGBRG10_1X10:
+	case MEDIA_BUS_FMT_SGRBG10_1X10:
+	case MEDIA_BUS_FMT_SRGGB10_1X10:
+	case MEDIA_BUS_FMT_SBGGR12_1X12:
+	case MEDIA_BUS_FMT_SGBRG12_1X12:
+	case MEDIA_BUS_FMT_SGRBG12_1X12:
+	case MEDIA_BUS_FMT_SRGGB12_1X12:
+	case MEDIA_BUS_FMT_SBGGR14_1X14:
+	case MEDIA_BUS_FMT_SGBRG14_1X14:
+	case MEDIA_BUS_FMT_SGRBG14_1X14:
+	case MEDIA_BUS_FMT_SRGGB14_1X14:
+	case MEDIA_BUS_FMT_SBGGR16_1X16:
+	case MEDIA_BUS_FMT_SGBRG16_1X16:
+	case MEDIA_BUS_FMT_SGRBG16_1X16:
+	case MEDIA_BUS_FMT_SRGGB16_1X16:
+		break;
+	default:
+		format->format.code = MEDIA_BUS_FMT_Y8_1X8;
+		break;
+	}
+	if (format->format.field == V4L2_FIELD_ANY)
+		format->format.field = V4L2_FIELD_NONE;
+
+	mutex_lock(&csi->lock);
+
+	pad = &csi->pads[format->pad];
+	if (pad->flags & MEDIA_PAD_FL_SOURCE)
+		format->format = csi->format_mbus[CSI_PAD_SINK];
+
+	*mbusformat = format->format;
+
+	if (pad->flags & MEDIA_PAD_FL_SINK)
+		*source_mbusformat = format->format;
+
+	mutex_unlock(&csi->lock);
+
+	return 0;
+}
+
+static const struct v4l2_subdev_video_ops mei_csi_video_ops = {
+	.s_stream = mei_csi_set_stream,
+	.pre_streamon = mei_csi_pre_streamon,
+};
+
+static const struct v4l2_subdev_pad_ops mei_csi_pad_ops = {
+	.init_cfg = mei_csi_init_cfg,
+	.get_fmt = mei_csi_get_fmt,
+	.set_fmt = mei_csi_set_fmt,
+};
+
+static const struct v4l2_subdev_ops mei_csi_subdev_ops = {
+	.video = &mei_csi_video_ops,
+	.pad = &mei_csi_pad_ops,
+};
+
+static const struct media_entity_operations mei_csi_entity_ops = {
+	.link_validate = v4l2_subdev_link_validate,
+};
+
+static int mei_csi_notify_bound(struct v4l2_async_notifier *notifier,
+				struct v4l2_subdev *subdev,
+				struct v4l2_async_subdev *asd)
+{
+	struct mei_csi *csi = notifier_to_csi(notifier);
+	int pad;
+
+	pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode,
+					  MEDIA_PAD_FL_SOURCE);
+	if (pad < 0)
+		return pad;
+
+	csi->remote = subdev;
+	csi->remote_pad = pad;
+
+	return media_create_pad_link(&subdev->entity, pad,
+				     &csi->subdev.entity, 0,
+				     MEDIA_LNK_FL_ENABLED |
+				     MEDIA_LNK_FL_IMMUTABLE);
+}
+
+static void mei_csi_notify_unbind(struct v4l2_async_notifier *notifier,
+				  struct v4l2_subdev *subdev,
+				  struct v4l2_async_subdev *asd)
+{
+	struct mei_csi *csi = notifier_to_csi(notifier);
+
+	csi->remote = NULL;
+}
+
+static const struct v4l2_async_notifier_operations mei_csi_notify_ops = {
+	.bound = mei_csi_notify_bound,
+	.unbind = mei_csi_notify_unbind,
+};
+
+static int mei_csi_init_control(struct mei_csi *csi)
+{
+	v4l2_ctrl_handler_init(&csi->ctrl_handler, 1);
+	csi->ctrl_handler.lock = &csi->lock;
+
+	csi->privacy_ctrl = v4l2_ctrl_new_std(&csi->ctrl_handler, NULL,
+					      V4L2_CID_PRIVACY, 0, 1, 1, 0);
+	if (csi->ctrl_handler.error)
+		return csi->ctrl_handler.error;
+
+	csi->subdev.ctrl_handler = &csi->ctrl_handler;
+
+	return 0;
+}
+
+static int mei_csi_parse_firmware(struct mei_csi *csi)
+{
+	struct v4l2_fwnode_endpoint v4l2_ep = {
+		.bus_type = V4L2_MBUS_CSI2_DPHY,
+	};
+	struct device *dev = &csi->cldev->dev;
+	struct v4l2_async_subdev *asd;
+	struct fwnode_handle *fwnode;
+	struct fwnode_handle *ep;
+	int ret;
+
+	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0);
+	if (!ep) {
+		dev_err(dev, "not connected to subdevice\n");
+		return -EINVAL;
+	}
+
+	ret = v4l2_fwnode_endpoint_parse(ep, &v4l2_ep);
+	if (ret) {
+		dev_err(dev, "could not parse v4l2 endpoint\n");
+		fwnode_handle_put(ep);
+		return -EINVAL;
+	}
+
+	fwnode = fwnode_graph_get_remote_endpoint(ep);
+	fwnode_handle_put(ep);
+
+	v4l2_async_nf_init(&csi->notifier);
+	csi->notifier.ops = &mei_csi_notify_ops;
+
+	asd = v4l2_async_nf_add_fwnode(&csi->notifier, fwnode,
+				       struct v4l2_async_subdev);
+	fwnode_handle_put(fwnode);
+	if (IS_ERR(asd))
+		return PTR_ERR(asd);
+
+	ret = v4l2_async_subdev_nf_register(&csi->subdev, &csi->notifier);
+	if (ret)
+		v4l2_async_nf_cleanup(&csi->notifier);
+
+	return ret;
+}
+
+static int mei_csi_probe(struct mei_cl_device *cldev,
+			 const struct mei_cl_device_id *id)
+{
+	struct mei_csi *csi;
+	int ret;
+
+	/*
+	 * no connections to sensor are defined in firmware, try to
+	 * build connections as software_nodes parsed from SSDB.
+	 */
+	ret = csi_bridge_init(cldev);
+	if (ret)
+		return ret;
+
+	csi = devm_kzalloc(&cldev->dev, sizeof(struct mei_csi), GFP_KERNEL);
+	if (!csi)
+		return -ENOMEM;
+
+	csi->cldev = cldev;
+	mutex_init(&csi->lock);
+	init_completion(&csi->cmd_completion);
+
+	mei_cldev_set_drvdata(cldev, csi);
+
+	ret = mei_cldev_enable(cldev);
+	if (ret < 0) {
+		dev_err(&cldev->dev, "mei_cldev_enable failed: %d\n", ret);
+		goto destroy_mutex;
+	}
+
+	ret = mei_cldev_register_rx_cb(cldev, mei_csi_rx);
+	if (ret) {
+		dev_err(&cldev->dev, "event cb registration failed: %d\n", ret);
+		goto err_disable;
+	}
+
+	ret = mei_csi_parse_firmware(csi);
+	if (ret)
+		goto err_disable;
+
+	csi->subdev.dev = &cldev->dev;
+	v4l2_subdev_init(&csi->subdev, &mei_csi_subdev_ops);
+	v4l2_set_subdevdata(&csi->subdev, csi);
+	csi->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+	csi->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+	csi->subdev.entity.ops = &mei_csi_entity_ops;
+
+	ret = mei_csi_init_control(csi);
+	if (ret)
+		goto err_async;
+
+	csi->format_mbus[CSI_PAD_SOURCE] = mei_csi_format_mbus_default;
+	csi->format_mbus[CSI_PAD_SINK] = mei_csi_format_mbus_default;
+
+	csi->pads[CSI_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+	csi->pads[CSI_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+	ret = media_entity_pads_init(&csi->subdev.entity, CSI_NUM_PADS,
+				     csi->pads);
+	if (ret)
+		goto err_ctrl_handler;
+
+	ret = v4l2_subdev_init_finalize(&csi->subdev);
+	if (ret < 0)
+		goto err_entity;
+
+	ret = v4l2_async_register_subdev(&csi->subdev);
+	if (ret < 0)
+		goto err_subdev;
+
+	pm_runtime_enable(&cldev->dev);
+
+	return 0;
+
+err_subdev:
+	v4l2_subdev_cleanup(&csi->subdev);
+err_entity:
+	media_entity_cleanup(&csi->subdev.entity);
+err_ctrl_handler:
+	v4l2_ctrl_handler_free(&csi->ctrl_handler);
+err_async:
+	v4l2_async_nf_unregister(&csi->notifier);
+	v4l2_async_nf_cleanup(&csi->notifier);
+err_disable:
+	mei_cldev_disable(cldev);
+destroy_mutex:
+	mutex_destroy(&csi->lock);
+
+	return ret;
+}
+
+static void mei_csi_remove(struct mei_cl_device *cldev)
+{
+	struct mei_csi *csi = mei_cldev_get_drvdata(cldev);
+
+	v4l2_async_nf_unregister(&csi->notifier);
+	v4l2_async_nf_cleanup(&csi->notifier);
+	v4l2_ctrl_handler_free(&csi->ctrl_handler);
+	v4l2_async_unregister_subdev(&csi->subdev);
+	v4l2_subdev_cleanup(&csi->subdev);
+	media_entity_cleanup(&csi->subdev.entity);
+
+	pm_runtime_disable(&cldev->dev);
+
+	mutex_destroy(&csi->lock);
+}
+
+#define MEI_CSI_UUID UUID_LE(0x92335FCF, 0x3203, 0x4472, \
+			     0xAF, 0x93, 0x7b, 0x44, 0x53, 0xAC, 0x29, 0xDA)
+
+static const struct mei_cl_device_id mei_csi_tbl[] = {
+	{ MEI_CSI_DRIVER_NAME, MEI_CSI_UUID, MEI_CL_VERSION_ANY },
+
+	/* required last entry */
+	{ }
+};
+MODULE_DEVICE_TABLE(mei, mei_csi_tbl);
+
+static struct mei_cl_driver mei_csi_driver = {
+	.id_table = mei_csi_tbl,
+	.name = MEI_CSI_DRIVER_NAME,
+
+	.probe = mei_csi_probe,
+	.remove = mei_csi_remove,
+};
+
+module_mei_cl_driver(mei_csi_driver);
+
+MODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>");
+MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>");
+MODULE_DESCRIPTION("Device driver for IVSC CSI");
+MODULE_LICENSE("GPL");
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH v3 2/3] media: pci: intel: ivsc: Add ACE submodule
  2023-03-27  6:23 [PATCH v3 0/3] media: pci: intel: ivsc: Add driver of Intel Visual Sensing Controller(IVSC) Wentong Wu
  2023-03-27  6:23 ` [PATCH v3 1/3] media: pci: intel: ivsc: Add CSI submodule Wentong Wu
@ 2023-03-27  6:23 ` Wentong Wu
  2023-03-29  8:03   ` Sakari Ailus
  2023-03-27  6:23 ` [PATCH v3 3/3] ACPI: delay enumeration of devices with a _DEP pointing to INTC1059 device Wentong Wu
  2023-03-27  7:21 ` [PATCH v3 0/3] media: pci: intel: ivsc: Add driver of Intel Visual Sensing Controller(IVSC) Sakari Ailus
  3 siblings, 1 reply; 26+ messages in thread
From: Wentong Wu @ 2023-03-27  6:23 UTC (permalink / raw)
  To: sakari.ailus, hdegoede, djrscally, laurent.pinchart, linux-media
  Cc: bingbu.cao, zhifeng.wang, xiang.ye, tian.shu.qiu, Wentong Wu

ACE is a submodule of IVSC which controls camera sensor's
ownership, belonging to host or IVSC. When IVSC owns camera
sensor, it is for algorithm computing. When host wants to
control camera sensor, ACE module needs to be informed of
ownership with defined interface.

The interface is via MEI. There is a separate MEI UUID, which
this driver uses to enumerate.

To switch ownership of camera sensor between IVSC and host,
the caller specifies the defined ownership information which
will be sent to firmware by sending MEI command.

Device link(device_link_add) is used to set the right camera
sensor ownership before accessing the sensor via I²C. With
DL_FLAG_PM_RUNTIME and DL_FLAG_RPM_ACTIVE, the supplier device
will be PM runtime resumed before the consumer(camera sensor).
So use runtime PM callbacks to transfer the ownership between
host and IVSC.

Signed-off-by: Wentong Wu <wentong.wu@intel.com>
---
 drivers/media/pci/intel/ivsc/Makefile  |   3 +
 drivers/media/pci/intel/ivsc/mei_ace.c | 534 +++++++++++++++++++++++++++++++++
 2 files changed, 537 insertions(+)
 create mode 100644 drivers/media/pci/intel/ivsc/mei_ace.c

diff --git a/drivers/media/pci/intel/ivsc/Makefile b/drivers/media/pci/intel/ivsc/Makefile
index 7e4c5f0..a641f14 100644
--- a/drivers/media/pci/intel/ivsc/Makefile
+++ b/drivers/media/pci/intel/ivsc/Makefile
@@ -5,3 +5,6 @@
 obj-$(CONFIG_INTEL_VSC) += ivsc-csi.o
 ivsc-csi-y += mei_csi.o
 ivsc-csi-y += csi_bridge.o
+
+obj-$(CONFIG_INTEL_VSC) += ivsc-ace.o
+ivsc-ace-y += mei_ace.o
diff --git a/drivers/media/pci/intel/ivsc/mei_ace.c b/drivers/media/pci/intel/ivsc/mei_ace.c
new file mode 100644
index 0000000..434b072
--- /dev/null
+++ b/drivers/media/pci/intel/ivsc/mei_ace.c
@@ -0,0 +1,534 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023 Intel Corporation. All rights reserved.
+ * Intel Visual Sensing Controller ACE Linux driver
+ */
+
+/*
+ * To set ownership of camera sensor, there is specific command, which
+ * is sent via MEI protocol. That's a two-step scheme where the firmware
+ * first acks receipt of the command and later responses the command was
+ * executed. The command sending function uses "completion" as the
+ * synchronization mechanism. The notification for command is received
+ * via a mei callback which wakes up the caller. There can be only one
+ * outstanding command at a time.
+ */
+
+#include <linux/acpi.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/mei_cl_bus.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/uuid.h>
+
+#define	MEI_ACE_DRIVER_NAME	"ivsc_ace"
+
+/* indicating driver message */
+#define	ACE_DRV_MSG		1
+/* indicating set command */
+#define	ACE_CMD_SET		4
+/* command timeout determined experimentally */
+#define	ACE_CMD_TIMEOUT		(5 * HZ)
+/* indicating the first command block */
+#define	ACE_CMD_INIT_BLOCK	1
+/* indicating the last command block */
+#define	ACE_CMD_FINAL_BLOCK	1
+/* size of camera status notification content */
+#define	ACE_CAMERA_STATUS_SIZE	5
+
+/* UUID used to get firmware id */
+#define ACE_GET_FW_ID_UUID UUID_LE(0x6167DCFB, 0x72F1, 0x4584, 0xBF, \
+				   0xE3, 0x84, 0x17, 0x71, 0xAA, 0x79, 0x0B)
+
+/* UUID used to get csi device */
+#define MEI_CSI_UUID UUID_LE(0x92335FCF, 0x3203, 0x4472, \
+			     0xAF, 0x93, 0x7b, 0x44, 0x53, 0xAC, 0x29, 0xDA)
+
+/* identify firmware event type */
+enum ace_event_type {
+	/* firmware ready */
+	ACE_FW_READY = 0x8,
+
+	/* command response */
+	ACE_CMD_RESPONSE = 0x10,
+};
+
+/* identify camera sensor ownership */
+enum ace_camera_owner {
+	ACE_CAMERA_IVSC,
+	ACE_CAMERA_HOST,
+};
+
+/* identify the command id supported by firmware IPC */
+enum ace_cmd_id {
+	/* used to switch camera sensor to host */
+	ACE_SWITCH_CAMERA_TO_HOST = 0x13,
+
+	/* used to switch camera sensor to IVSC */
+	ACE_SWITCH_CAMERA_TO_IVSC = 0x14,
+
+	/* used to get firmware id */
+	ACE_GET_FW_ID = 0x1A,
+};
+
+/* ACE command header structure */
+struct ace_cmd_hdr {
+	u32 firmware_id : 16;
+	u32 instance_id : 8;
+	u32 type : 5;
+	u32 rsp : 1;
+	u32 msg_tgt : 1;
+	u32 _hw_rsvd_1 : 1;
+	u32 param_size : 20;
+	u32 cmd_id : 8;
+	u32 final_block : 1;
+	u32 init_block : 1;
+	u32 _hw_rsvd_2 : 2;
+} __packed;
+
+/* ACE command parameter structure */
+union ace_cmd_param {
+	uuid_le uuid;
+	u32 param;
+};
+
+/* ACE command structure */
+struct ace_cmd {
+	struct ace_cmd_hdr hdr;
+	union ace_cmd_param param;
+} __packed;
+
+/* ACE notification header */
+union ace_notif_hdr {
+	struct _confirm {
+		u32 status : 24;
+		u32 type : 5;
+		u32 rsp : 1;
+		u32 msg_tgt : 1;
+		u32 _hw_rsvd_1 : 1;
+		u32 param_size : 20;
+		u32 cmd_id : 8;
+		u32 final_block : 1;
+		u32 init_block : 1;
+		u32 _hw_rsvd_2 : 2;
+	} __packed ack;
+
+	struct _event {
+		u32 rsvd1 : 16;
+		u32 event_type : 8;
+		u32 type : 5;
+		u32 ack : 1;
+		u32 msg_tgt : 1;
+		u32 _hw_rsvd_1 : 1;
+		u32 rsvd2 : 30;
+		u32 _hw_rsvd_2 : 2;
+	} __packed event;
+
+	struct _response {
+		u32 event_id : 16;
+		u32 notif_type : 8;
+		u32 type : 5;
+		u32 rsp : 1;
+		u32 msg_tgt : 1;
+		u32 _hw_rsvd_1 : 1;
+		u32 event_data_size : 16;
+		u32 request_target : 1;
+		u32 request_type : 5;
+		u32 cmd_id : 8;
+		u32 _hw_rsvd_2 : 2;
+	} __packed response;
+};
+
+/* ACE notification content */
+union ace_notif_cont {
+	u16 firmware_id;
+	u8 state_notif;
+	u8 camera_status[ACE_CAMERA_STATUS_SIZE];
+};
+
+/* ACE notification structure */
+struct ace_notif {
+	union ace_notif_hdr hdr;
+	union ace_notif_cont cont;
+} __packed;
+
+struct mei_ace {
+	struct mei_cl_device *cldev;
+
+	/* command ack */
+	struct ace_notif cmd_ack;
+	/* command response */
+	struct ace_notif cmd_response;
+	/* used to wait for command ack and response */
+	struct completion cmd_completion;
+	/* lock used to prevent multiple call to ace */
+	struct mutex lock;
+
+	/* used to construct command */
+	u16 firmware_id;
+
+	/* runtime PM link from ace to csi */
+	struct device_link *csi_link;
+	/* runtime PM link from ace to sensor */
+	struct device_link *sensor_link;
+};
+
+static inline void init_cmd_hdr(struct ace_cmd_hdr *hdr)
+{
+	memset(hdr, 0, sizeof(struct ace_cmd_hdr));
+
+	hdr->type = ACE_CMD_SET;
+	hdr->msg_tgt = ACE_DRV_MSG;
+	hdr->init_block = ACE_CMD_INIT_BLOCK;
+	hdr->final_block = ACE_CMD_FINAL_BLOCK;
+}
+
+static int construct_command(struct mei_ace *ace, struct ace_cmd *cmd,
+			     enum ace_cmd_id cmd_id)
+{
+	union ace_cmd_param *param = &cmd->param;
+	struct ace_cmd_hdr *hdr = &cmd->hdr;
+
+	init_cmd_hdr(hdr);
+
+	hdr->cmd_id = cmd_id;
+	switch (cmd_id) {
+	case ACE_GET_FW_ID:
+		param->uuid = ACE_GET_FW_ID_UUID;
+		hdr->param_size = sizeof(param->uuid);
+		break;
+	case ACE_SWITCH_CAMERA_TO_IVSC:
+		param->param = 0;
+		hdr->firmware_id = ace->firmware_id;
+		hdr->param_size = sizeof(param->param);
+		break;
+	case ACE_SWITCH_CAMERA_TO_HOST:
+		hdr->firmware_id = ace->firmware_id;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return hdr->param_size + sizeof(cmd->hdr);
+}
+
+/* send a command to firmware and mutex must be held by caller */
+static int mei_ace_send(struct mei_ace *ace, struct ace_cmd *cmd,
+			size_t len, bool only_ack)
+{
+	union ace_notif_hdr *resp_hdr = &ace->cmd_response.hdr;
+	union ace_notif_hdr *ack_hdr = &ace->cmd_ack.hdr;
+	struct ace_cmd_hdr *cmd_hdr = &cmd->hdr;
+	int ret;
+
+	reinit_completion(&ace->cmd_completion);
+
+	ret = mei_cldev_send(ace->cldev, (u8 *)cmd, len);
+	if (ret < 0)
+		goto out;
+
+	ret = wait_for_completion_killable_timeout(&ace->cmd_completion,
+						   ACE_CMD_TIMEOUT);
+	if (ret < 0) {
+		goto out;
+	} else if (!ret) {
+		ret = -ETIMEDOUT;
+		goto out;
+	}
+
+	if (ack_hdr->ack.cmd_id != cmd_hdr->cmd_id) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* command ack status */
+	ret = ack_hdr->ack.status;
+	if (ret) {
+		ret = -EIO;
+		goto out;
+	}
+
+	if (only_ack)
+		goto out;
+
+	ret = wait_for_completion_killable_timeout(&ace->cmd_completion,
+						   ACE_CMD_TIMEOUT);
+	if (ret < 0) {
+		goto out;
+	} else if (!ret) {
+		ret = -ETIMEDOUT;
+		goto out;
+	}
+
+	if (resp_hdr->response.cmd_id != cmd_hdr->cmd_id)
+		ret = -EINVAL;
+
+out:
+	return ret;
+}
+
+static int ace_set_camera_owner(struct mei_ace *ace, enum ace_camera_owner owner)
+{
+	enum ace_cmd_id cmd_id;
+	struct ace_cmd cmd;
+	int cmd_size;
+	int ret;
+
+	if (owner == ACE_CAMERA_IVSC)
+		cmd_id = ACE_SWITCH_CAMERA_TO_IVSC;
+	else
+		cmd_id = ACE_SWITCH_CAMERA_TO_HOST;
+
+	mutex_lock(&ace->lock);
+
+	cmd_size = construct_command(ace, &cmd, cmd_id);
+	if (cmd_size >= 0)
+		ret = mei_ace_send(ace, &cmd, cmd_size, false);
+	else
+		ret = cmd_size;
+	mutex_unlock(&ace->lock);
+
+	return ret;
+}
+
+/* the first command downloaded to firmware */
+static inline int ace_get_firmware_id(struct mei_ace *ace)
+{
+	struct ace_cmd cmd;
+	int cmd_size;
+	int ret;
+
+	cmd_size = construct_command(ace, &cmd, ACE_GET_FW_ID);
+	if (cmd_size >= 0)
+		ret = mei_ace_send(ace, &cmd, cmd_size, true);
+	else
+		ret = cmd_size;
+
+	return ret;
+}
+
+static void handle_command_response(struct mei_ace *ace,
+				    struct ace_notif *resp, int len)
+{
+	union ace_notif_hdr *hdr = &resp->hdr;
+
+	switch (hdr->response.cmd_id) {
+	case ACE_SWITCH_CAMERA_TO_IVSC:
+	case ACE_SWITCH_CAMERA_TO_HOST:
+		memcpy(&ace->cmd_response, resp, len);
+		complete(&ace->cmd_completion);
+		break;
+	case ACE_GET_FW_ID:
+		break;
+	default:
+		break;
+	}
+}
+
+static void handle_command_ack(struct mei_ace *ace,
+			       struct ace_notif *ack, int len)
+{
+	union ace_notif_hdr *hdr = &ack->hdr;
+
+	switch (hdr->ack.cmd_id) {
+	case ACE_GET_FW_ID:
+		ace->firmware_id = ack->cont.firmware_id;
+		fallthrough;
+	case ACE_SWITCH_CAMERA_TO_IVSC:
+	case ACE_SWITCH_CAMERA_TO_HOST:
+		memcpy(&ace->cmd_ack, ack, len);
+		complete(&ace->cmd_completion);
+		break;
+	default:
+		break;
+	}
+}
+
+/* callback for receive */
+static void mei_ace_rx(struct mei_cl_device *cldev)
+{
+	struct mei_ace *ace = mei_cldev_get_drvdata(cldev);
+	struct ace_notif event;
+	union ace_notif_hdr *hdr = &event.hdr;
+	int ret;
+
+	ret = mei_cldev_recv(cldev, (u8 *)&event, sizeof(event));
+	if (ret < 0) {
+		dev_err(&cldev->dev, "recv error: %d\n", ret);
+		return;
+	}
+
+	if (hdr->event.ack) {
+		handle_command_ack(ace, &event, ret);
+		return;
+	}
+
+	switch (hdr->event.event_type) {
+	case ACE_CMD_RESPONSE:
+		handle_command_response(ace, &event, ret);
+		break;
+	case ACE_FW_READY:
+		/*
+		 * firmware ready notification sent to driver
+		 * after HECI client connected with firmware.
+		 */
+		dev_dbg(&cldev->dev, "firmware ready\n");
+		break;
+	default:
+		break;
+	}
+}
+
+static int mei_ace_setup_dev_link(struct mei_ace *ace)
+{
+	struct device *dev = &ace->cldev->dev;
+	uuid_le uuid = MEI_CSI_UUID;
+	struct acpi_device *adev;
+	struct device *csi_dev;
+	char name[64];
+
+	sprintf(name, "%s-%pUl", dev_name(dev->parent), &uuid);
+	csi_dev = device_find_child_by_name(dev->parent, name);
+	if (!csi_dev)
+		return -EPROBE_DEFER;
+
+	/* sensor's ACPI _DEP is mei bus device */
+	adev = acpi_dev_get_next_consumer_dev(ACPI_COMPANION(dev->parent), NULL);
+	if (!adev)
+		return -EPROBE_DEFER;
+
+	/* setup link between mei_ace and mei_csi */
+	ace->csi_link = device_link_add(csi_dev, dev,
+					DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE);
+
+	/* setup link between mei_ace and sensor */
+	ace->sensor_link = device_link_add(&adev->dev, dev,
+					   DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE);
+
+	return 0;
+}
+
+static int mei_ace_probe(struct mei_cl_device *cldev,
+			 const struct mei_cl_device_id *id)
+{
+	struct device *dev = &cldev->dev;
+	struct mei_ace *ace;
+	int ret;
+
+	ace = devm_kzalloc(dev, sizeof(struct mei_ace), GFP_KERNEL);
+	if (!ace)
+		return -ENOMEM;
+
+	ace->cldev = cldev;
+
+	ret = mei_ace_setup_dev_link(ace);
+	if (ret)
+		return ret;
+
+	mutex_init(&ace->lock);
+	init_completion(&ace->cmd_completion);
+
+	mei_cldev_set_drvdata(cldev, ace);
+
+	ret = mei_cldev_enable(cldev);
+	if (ret < 0) {
+		dev_err(dev, "mei_cldev_enable failed: %d\n", ret);
+		goto destroy_mutex;
+	}
+
+	ret = mei_cldev_register_rx_cb(cldev, mei_ace_rx);
+	if (ret) {
+		dev_err(dev, "event cb registration failed: %d\n", ret);
+		goto err_disable;
+	}
+
+	ret = ace_get_firmware_id(ace);
+	if (ret) {
+		dev_err(dev, "get firmware id failed: %d\n", ret);
+		goto err_disable;
+	}
+
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
+	acpi_dev_clear_dependencies(ACPI_COMPANION(dev->parent));
+
+	return 0;
+
+err_disable:
+	mei_cldev_disable(cldev);
+destroy_mutex:
+	mutex_destroy(&ace->lock);
+
+	device_link_del(ace->csi_link);
+	device_link_del(ace->sensor_link);
+
+	return ret;
+}
+
+static void mei_ace_remove(struct mei_cl_device *cldev)
+{
+	struct mei_ace *ace = mei_cldev_get_drvdata(cldev);
+
+	device_link_del(ace->csi_link);
+	device_link_del(ace->sensor_link);
+
+	pm_runtime_disable(&cldev->dev);
+	pm_runtime_set_suspended(&cldev->dev);
+
+	mutex_destroy(&ace->lock);
+}
+
+static int __maybe_unused mei_ace_runtime_suspend(struct device *dev)
+{
+	struct mei_ace *ace = dev_get_drvdata(dev);
+
+	return ace_set_camera_owner(ace, ACE_SWITCH_CAMERA_TO_IVSC);
+}
+
+static int __maybe_unused mei_ace_runtime_resume(struct device *dev)
+{
+	struct mei_ace *ace = dev_get_drvdata(dev);
+
+	return ace_set_camera_owner(ace, ACE_SWITCH_CAMERA_TO_HOST);
+}
+
+static const struct dev_pm_ops mei_ace_pm_ops = {
+	SET_RUNTIME_PM_OPS(mei_ace_runtime_suspend,
+			   mei_ace_runtime_resume,
+			   NULL)
+};
+
+#define MEI_ACE_UUID UUID_LE(0x5DB76CF6, 0x0A68, 0x4ED6, \
+			     0x9B, 0x78, 0x03, 0x61, 0x63, 0x5E, 0x24, 0x47)
+
+static const struct mei_cl_device_id mei_ace_tbl[] = {
+	{ MEI_ACE_DRIVER_NAME, MEI_ACE_UUID, MEI_CL_VERSION_ANY },
+
+	/* required last entry */
+	{ }
+};
+MODULE_DEVICE_TABLE(mei, mei_ace_tbl);
+
+static struct mei_cl_driver mei_ace_driver = {
+	.id_table = mei_ace_tbl,
+	.name = MEI_ACE_DRIVER_NAME,
+
+	.probe = mei_ace_probe,
+	.remove = mei_ace_remove,
+
+	.driver = {
+		.pm = &mei_ace_pm_ops,
+	},
+};
+
+module_mei_cl_driver(mei_ace_driver);
+
+MODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>");
+MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>");
+MODULE_DESCRIPTION("Device driver for IVSC ACE");
+MODULE_LICENSE("GPL");
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH v3 3/3] ACPI: delay enumeration of devices with a _DEP pointing to INTC1059 device
  2023-03-27  6:23 [PATCH v3 0/3] media: pci: intel: ivsc: Add driver of Intel Visual Sensing Controller(IVSC) Wentong Wu
  2023-03-27  6:23 ` [PATCH v3 1/3] media: pci: intel: ivsc: Add CSI submodule Wentong Wu
  2023-03-27  6:23 ` [PATCH v3 2/3] media: pci: intel: ivsc: Add ACE submodule Wentong Wu
@ 2023-03-27  6:23 ` Wentong Wu
  2023-03-27  7:06   ` Sakari Ailus
  2023-03-27  7:21 ` [PATCH v3 0/3] media: pci: intel: ivsc: Add driver of Intel Visual Sensing Controller(IVSC) Sakari Ailus
  3 siblings, 1 reply; 26+ messages in thread
From: Wentong Wu @ 2023-03-27  6:23 UTC (permalink / raw)
  To: sakari.ailus, hdegoede, djrscally, laurent.pinchart, linux-media
  Cc: bingbu.cao, zhifeng.wang, xiang.ye, tian.shu.qiu, Wentong Wu

Inside IVSC, switching ownership requires an interface with two different
hardware modules, ACE and CSI. The software interface to these modules is
based on Intel MEI framework. Usually mei client devices are dynamically
created, so the info of consumers depending on mei client devices is not
present in the firmware tables.

This causes problems with the probe ordering with respect to drivers for
consumers of these mei client devices. But on these camera sensor devices,
the ACPI nodes describing the sensors all have a _DEP dependency on the
matching INTC1059 ACPI device, so adding INTC1059 to acpi_honor_dep_ids
allows solving the probe-ordering problem by delaying the enumeration of
ACPI-devices which have a _DEP dependency on an INTC1059 device.

Signed-off-by: Wentong Wu <wentong.wu@intel.com>
---
 drivers/acpi/scan.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 2743444..5b48dcd 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -796,6 +796,7 @@ static const char * const acpi_ignore_dep_ids[] = {
 /* List of HIDs for which we honor deps of matching ACPI devs, when checking _DEP lists. */
 static const char * const acpi_honor_dep_ids[] = {
 	"INT3472", /* Camera sensor PMIC / clk and regulator info */
+	"INTC1059",
 	NULL
 };
 
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCH v3 3/3] ACPI: delay enumeration of devices with a _DEP pointing to INTC1059 device
  2023-03-27  6:23 ` [PATCH v3 3/3] ACPI: delay enumeration of devices with a _DEP pointing to INTC1059 device Wentong Wu
@ 2023-03-27  7:06   ` Sakari Ailus
  2023-03-27  7:17     ` Wu, Wentong
  0 siblings, 1 reply; 26+ messages in thread
From: Sakari Ailus @ 2023-03-27  7:06 UTC (permalink / raw)
  To: Wentong Wu
  Cc: hdegoede, djrscally, laurent.pinchart, linux-media, bingbu.cao,
	zhifeng.wang, xiang.ye, tian.shu.qiu

Hi Wentong,

On Mon, Mar 27, 2023 at 02:23:08PM +0800, Wentong Wu wrote:
> Inside IVSC, switching ownership requires an interface with two different
> hardware modules, ACE and CSI. The software interface to these modules is
> based on Intel MEI framework. Usually mei client devices are dynamically
> created, so the info of consumers depending on mei client devices is not
> present in the firmware tables.

Ouch.

> 
> This causes problems with the probe ordering with respect to drivers for
> consumers of these mei client devices. But on these camera sensor devices,
> the ACPI nodes describing the sensors all have a _DEP dependency on the
> matching INTC1059 ACPI device, so adding INTC1059 to acpi_honor_dep_ids
> allows solving the probe-ordering problem by delaying the enumeration of
> ACPI-devices which have a _DEP dependency on an INTC1059 device.

What does the INTC1059 device represent?

-- 
Sakari Ailus

^ permalink raw reply	[flat|nested] 26+ messages in thread

* RE: [PATCH v3 3/3] ACPI: delay enumeration of devices with a _DEP pointing to INTC1059 device
  2023-03-27  7:06   ` Sakari Ailus
@ 2023-03-27  7:17     ` Wu, Wentong
  2023-03-27  7:32       ` Sakari Ailus
  0 siblings, 1 reply; 26+ messages in thread
From: Wu, Wentong @ 2023-03-27  7:17 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: hdegoede, djrscally, laurent.pinchart, linux-media, bingbu.cao,
	Wang, Zhifeng, Ye, Xiang, Qiu, Tian Shu



> -----Original Message-----
> From: Sakari Ailus <sakari.ailus@linux.intel.com>
> 
> Hi Wentong,
> 
> On Mon, Mar 27, 2023 at 02:23:08PM +0800, Wentong Wu wrote:
> > Inside IVSC, switching ownership requires an interface with two
> > different hardware modules, ACE and CSI. The software interface to
> > these modules is based on Intel MEI framework. Usually mei client
> > devices are dynamically created, so the info of consumers depending on
> > mei client devices is not present in the firmware tables.
> 
> Ouch.

MEI client devices are not ACPI devices in our case.

> 
> >
> > This causes problems with the probe ordering with respect to drivers
> > for consumers of these mei client devices. But on these camera sensor
> > devices, the ACPI nodes describing the sensors all have a _DEP
> > dependency on the matching INTC1059 ACPI device, so adding INTC1059 to
> > acpi_honor_dep_ids allows solving the probe-ordering problem by
> > delaying the enumeration of ACPI-devices which have a _DEP dependency on
> an INTC1059 device.
> 
> What does the INTC1059 device represent?

This is the IVSC device on which camera sensor has _DEP 

            Device (CVFD)
            {
                Name (_HID, "INTC1059")  // _HID: Hardware ID
                Name (_DDN, "Intel CVF Device")  // _DDN: DOS Device Name
                Method (SID, 1, Serialized)
                {
                    Return (HCID (One))
                }
            }

        Device (LNK1)
        {
            Name (PNIO, Package (0x01)
            {
                "\\_SB.PC00.SPI1.SPFD.CVFD"
            })
            Name (PUSB, Package (0x02)
            {
                "\\_SB.PC00.SPI1.SPFD.CVFD", 
                "\\_SB.PC00.XHCI.RHUB.HS08.VIC0"
            })
            Method (_STA, 0, NotSerialized)  // _STA: Status
            {
                If (L1EN)
                {
                    Return (0x0F)
                }
                Else
                {
                    Return (Zero)
                }
            }

            Method (_DEP, 0, NotSerialized)  // _DEP: Dependencies
            {
                If (L1EN)
                {
                    If ((CVFS == 0x02))
                    {
                        Return (PUSB) /* \_SB_.PC00.LNK1.PUSB */
                    }

                    If ((CVFS == One))
                    {
                        Return (PNIO) /* \_SB_.PC00.LNK1.PNIO */
                    }
                    Else
                    {
                        Return (CDEP (L1CL, L1BS))
                    }
                }
                Else
                {
                    Return (Package (0x01)
                    {
                        PC00
                    })
                }
            }
> 
> --
> Sakari Ailus

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v3 0/3] media: pci: intel: ivsc: Add driver of Intel Visual Sensing Controller(IVSC)
  2023-03-27  6:23 [PATCH v3 0/3] media: pci: intel: ivsc: Add driver of Intel Visual Sensing Controller(IVSC) Wentong Wu
                   ` (2 preceding siblings ...)
  2023-03-27  6:23 ` [PATCH v3 3/3] ACPI: delay enumeration of devices with a _DEP pointing to INTC1059 device Wentong Wu
@ 2023-03-27  7:21 ` Sakari Ailus
  2023-03-27  7:33   ` Wu, Wentong
  3 siblings, 1 reply; 26+ messages in thread
From: Sakari Ailus @ 2023-03-27  7:21 UTC (permalink / raw)
  To: Wentong Wu
  Cc: hdegoede, djrscally, laurent.pinchart, linux-media, bingbu.cao,
	zhifeng.wang, xiang.ye, tian.shu.qiu

Hi Wentong,

On Mon, Mar 27, 2023 at 02:23:05PM +0800, Wentong Wu wrote:
> Intel Visual Sensing Controller (IVSC), codenamed "Clover Falls", is a
> companion chip designed to provide secure and low power vision capability
> to IA platforms. IVSC is available in existing commercial platforms from
> multiple OEMs.
> 
> The primary use case of IVSC is to bring in context awareness. IVSC
> interfaces directly with the platform main camera sensor via a CSI-2 link
> and processes the image data with the embedded AI engine. The detected
> events are sent over I2C to ISH (Intel Sensor Hub) for additional data
> fusion from multiple sensors. The fusion results are used to implement
> advanced use cases like:
>  - Face detection to unlock screen
>  - Detect user presence to manage backlight setting or waking up system
> 
> Since the Image Processing Unit(IPU) used on the host processor needs to
> configure the CSI-2 link in normal camera usages, the CSI-2 link and
> camera sensor can only be used in mutually-exclusive ways by host IPU and
> IVSC. By default the IVSC owns the CSI-2 link and camera sensor. The IPU
> driver can take ownership of the CSI-2 link and camera sensor using
> interfaces exported via v4l2 sub-device.
> 
> Switching ownership requires an interface with two different hardware
> modules inside IVSC. The software interface to these modules is via Intel
> MEI (The Intel Management Engine) commands. These two hardware modules
> have two different MEI UUIDs to enumerate. These hardware modules are:
>  - ACE (Algorithm Context Engine): This module is for algorithm computing
> when IVSC owns camera sensor. Also ACE module controls camera sensor's
> ownership. This hardware module is used to set ownership of camera sensor.
>  - CSI (Camera Serial Interface): This module is used to route camera
> sensor data either to IVSC or to host for IPU driver and application.
> 
> IVSC also provides a privacy mode. When privacy mode is turned on,
> camera sensor can't be used. This means that both ACE and host IPU can't
> get image data. And when this mode is turned on, users are informed via
> v4l2 control API.
> 
> In summary, to acquire ownership of camera by IPU driver, first ACE
> module needs to be informed of ownership and then to setup MIPI CSI-2
> link for the camera sensor and IPU.
> 
> Implementation:
> There are two different drivers to handle ACE and CSI hardware modules
> inside IVSC.
>  - ivsc_csi: MEI client driver to send commands and receive notifications
> from CSI module.
>  - ivsc_ace: MEI client driver to send commands and get status from ACE
> module.
> Interface is exposed via v4l2 sub-devcie APIs to acquire and release
> camera sensor and CSI-2 link.

Thanks for the update.

Could you elaborate the decision of keeping the csi_bridge entirely
separate from the cio2_bridge (to be turned to ipu_bridge first)? Both are
doing essentially the same and using the same data structures, aren't they?

-- 
Kind regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v3 3/3] ACPI: delay enumeration of devices with a _DEP pointing to INTC1059 device
  2023-03-27  7:17     ` Wu, Wentong
@ 2023-03-27  7:32       ` Sakari Ailus
  2023-03-27  7:36         ` Wu, Wentong
  0 siblings, 1 reply; 26+ messages in thread
From: Sakari Ailus @ 2023-03-27  7:32 UTC (permalink / raw)
  To: Wu, Wentong
  Cc: hdegoede, djrscally, laurent.pinchart, linux-media, bingbu.cao,
	Wang, Zhifeng, Ye, Xiang, Qiu, Tian Shu

Hi Wentong,

On Mon, Mar 27, 2023 at 07:17:13AM +0000, Wu, Wentong wrote:
> 
> 
> > -----Original Message-----
> > From: Sakari Ailus <sakari.ailus@linux.intel.com>
> > 
> > Hi Wentong,
> > 
> > On Mon, Mar 27, 2023 at 02:23:08PM +0800, Wentong Wu wrote:
> > > Inside IVSC, switching ownership requires an interface with two
> > > different hardware modules, ACE and CSI. The software interface to
> > > these modules is based on Intel MEI framework. Usually mei client
> > > devices are dynamically created, so the info of consumers depending on
> > > mei client devices is not present in the firmware tables.
> > 
> > Ouch.
> 
> MEI client devices are not ACPI devices in our case.
> 
> > 
> > >
> > > This causes problems with the probe ordering with respect to drivers
> > > for consumers of these mei client devices. But on these camera sensor
> > > devices, the ACPI nodes describing the sensors all have a _DEP
> > > dependency on the matching INTC1059 ACPI device, so adding INTC1059 to
> > > acpi_honor_dep_ids allows solving the probe-ordering problem by
> > > delaying the enumeration of ACPI-devices which have a _DEP dependency on
> > an INTC1059 device.
> > 
> > What does the INTC1059 device represent?
> 
> This is the IVSC device on which camera sensor has _DEP 
> 
>             Device (CVFD)
>             {
>                 Name (_HID, "INTC1059")  // _HID: Hardware ID
>                 Name (_DDN, "Intel CVF Device")  // _DDN: DOS Device Name
>                 Method (SID, 1, Serialized)
>                 {
>                     Return (HCID (One))
>                 }
>             }

Should this be interpreted that if you have a _DEP to this device, then the
IVSC is between the sensor and the IPU?

-- 
Kind regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 26+ messages in thread

* RE: [PATCH v3 0/3] media: pci: intel: ivsc: Add driver of Intel Visual Sensing Controller(IVSC)
  2023-03-27  7:21 ` [PATCH v3 0/3] media: pci: intel: ivsc: Add driver of Intel Visual Sensing Controller(IVSC) Sakari Ailus
@ 2023-03-27  7:33   ` Wu, Wentong
  2023-03-27  7:49     ` Sakari Ailus
  0 siblings, 1 reply; 26+ messages in thread
From: Wu, Wentong @ 2023-03-27  7:33 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: hdegoede, djrscally, laurent.pinchart, linux-media, bingbu.cao,
	Wang, Zhifeng, Ye, Xiang, Qiu, Tian Shu



> -----Original Message-----
> From: Sakari Ailus <sakari.ailus@linux.intel.com>
> Sent: Monday, March 27, 2023 3:21 PM
> 
> Hi Wentong,
> 
> On Mon, Mar 27, 2023 at 02:23:05PM +0800, Wentong Wu wrote:
> > Intel Visual Sensing Controller (IVSC), codenamed "Clover Falls", is a
> > companion chip designed to provide secure and low power vision
> > capability to IA platforms. IVSC is available in existing commercial
> > platforms from multiple OEMs.
> >
> > The primary use case of IVSC is to bring in context awareness. IVSC
> > interfaces directly with the platform main camera sensor via a CSI-2
> > link and processes the image data with the embedded AI engine. The
> > detected events are sent over I2C to ISH (Intel Sensor Hub) for
> > additional data fusion from multiple sensors. The fusion results are
> > used to implement advanced use cases like:
> >  - Face detection to unlock screen
> >  - Detect user presence to manage backlight setting or waking up
> > system
> >
> > Since the Image Processing Unit(IPU) used on the host processor needs
> > to configure the CSI-2 link in normal camera usages, the CSI-2 link
> > and camera sensor can only be used in mutually-exclusive ways by host
> > IPU and IVSC. By default the IVSC owns the CSI-2 link and camera
> > sensor. The IPU driver can take ownership of the CSI-2 link and camera
> > sensor using interfaces exported via v4l2 sub-device.
> >
> > Switching ownership requires an interface with two different hardware
> > modules inside IVSC. The software interface to these modules is via
> > Intel MEI (The Intel Management Engine) commands. These two hardware
> > modules have two different MEI UUIDs to enumerate. These hardware
> modules are:
> >  - ACE (Algorithm Context Engine): This module is for algorithm
> > computing when IVSC owns camera sensor. Also ACE module controls
> > camera sensor's ownership. This hardware module is used to set ownership of
> camera sensor.
> >  - CSI (Camera Serial Interface): This module is used to route camera
> > sensor data either to IVSC or to host for IPU driver and application.
> >
> > IVSC also provides a privacy mode. When privacy mode is turned on,
> > camera sensor can't be used. This means that both ACE and host IPU
> > can't get image data. And when this mode is turned on, users are
> > informed via
> > v4l2 control API.
> >
> > In summary, to acquire ownership of camera by IPU driver, first ACE
> > module needs to be informed of ownership and then to setup MIPI CSI-2
> > link for the camera sensor and IPU.
> >
> > Implementation:
> > There are two different drivers to handle ACE and CSI hardware modules
> > inside IVSC.
> >  - ivsc_csi: MEI client driver to send commands and receive
> > notifications from CSI module.
> >  - ivsc_ace: MEI client driver to send commands and get status from
> > ACE module.
> > Interface is exposed via v4l2 sub-devcie APIs to acquire and release
> > camera sensor and CSI-2 link.
> 
> Thanks for the update.
> 
> Could you elaborate the decision of keeping the csi_bridge entirely separate
> from the cio2_bridge (to be turned to ipu_bridge first)? Both are doing
> essentially the same and using the same data structures, aren't they?

yes, they're doing same thing to bridge the software nodes needed by v4l2, but
they have different type devices(pci and mei_client device) and dependency. And
they have same SSDB definition in DSDT, so the structures are almost same.

I have no idea what the ipu bridge would be like, but IVSC csi bridge can be configured
via kconfig to enable/disable.

> 
> --
> Kind regards,
> 
> Sakari Ailus

^ permalink raw reply	[flat|nested] 26+ messages in thread

* RE: [PATCH v3 3/3] ACPI: delay enumeration of devices with a _DEP pointing to INTC1059 device
  2023-03-27  7:32       ` Sakari Ailus
@ 2023-03-27  7:36         ` Wu, Wentong
  0 siblings, 0 replies; 26+ messages in thread
From: Wu, Wentong @ 2023-03-27  7:36 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: hdegoede, djrscally, laurent.pinchart, linux-media, bingbu.cao,
	Wang, Zhifeng, Ye, Xiang, Qiu, Tian Shu



> -----Original Message-----
> From: Sakari Ailus <sakari.ailus@linux.intel.com>
> Sent: Monday, March 27, 2023 3:33 PM
> 
> Hi Wentong,
> 
> On Mon, Mar 27, 2023 at 07:17:13AM +0000, Wu, Wentong wrote:
> >
> >
> > > -----Original Message-----
> > > From: Sakari Ailus <sakari.ailus@linux.intel.com>
> > >
> > > Hi Wentong,
> > >
> > > On Mon, Mar 27, 2023 at 02:23:08PM +0800, Wentong Wu wrote:
> > > > Inside IVSC, switching ownership requires an interface with two
> > > > different hardware modules, ACE and CSI. The software interface to
> > > > these modules is based on Intel MEI framework. Usually mei client
> > > > devices are dynamically created, so the info of consumers
> > > > depending on mei client devices is not present in the firmware tables.
> > >
> > > Ouch.
> >
> > MEI client devices are not ACPI devices in our case.
> >
> > >
> > > >
> > > > This causes problems with the probe ordering with respect to
> > > > drivers for consumers of these mei client devices. But on these
> > > > camera sensor devices, the ACPI nodes describing the sensors all
> > > > have a _DEP dependency on the matching INTC1059 ACPI device, so
> > > > adding INTC1059 to acpi_honor_dep_ids allows solving the
> > > > probe-ordering problem by delaying the enumeration of ACPI-devices
> > > > which have a _DEP dependency on
> > > an INTC1059 device.
> > >
> > > What does the INTC1059 device represent?
> >
> > This is the IVSC device on which camera sensor has _DEP
> >
> >             Device (CVFD)
> >             {
> >                 Name (_HID, "INTC1059")  // _HID: Hardware ID
> >                 Name (_DDN, "Intel CVF Device")  // _DDN: DOS Device Name
> >                 Method (SID, 1, Serialized)
> >                 {
> >                     Return (HCID (One))
> >                 }
> >             }
> 
> Should this be interpreted that if you have a _DEP to this device, then the IVSC is
> between the sensor and the IPU?

Ok, I can explain more in next version patch set. Thanks

BR,
Wentong

> 
> --
> Kind regards,
> 
> Sakari Ailus

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v3 0/3] media: pci: intel: ivsc: Add driver of Intel Visual Sensing Controller(IVSC)
  2023-03-27  7:33   ` Wu, Wentong
@ 2023-03-27  7:49     ` Sakari Ailus
  2023-03-27  8:13       ` Wu, Wentong
  0 siblings, 1 reply; 26+ messages in thread
From: Sakari Ailus @ 2023-03-27  7:49 UTC (permalink / raw)
  To: Wu, Wentong
  Cc: hdegoede, djrscally, laurent.pinchart, linux-media, bingbu.cao,
	Wang, Zhifeng, Ye, Xiang, Qiu, Tian Shu

Hi Wentong,

On Mon, Mar 27, 2023 at 07:33:48AM +0000, Wu, Wentong wrote:
> 
> 
> > -----Original Message-----
> > From: Sakari Ailus <sakari.ailus@linux.intel.com>
> > Sent: Monday, March 27, 2023 3:21 PM
> > 
> > Hi Wentong,
> > 
> > On Mon, Mar 27, 2023 at 02:23:05PM +0800, Wentong Wu wrote:
> > > Intel Visual Sensing Controller (IVSC), codenamed "Clover Falls", is a
> > > companion chip designed to provide secure and low power vision
> > > capability to IA platforms. IVSC is available in existing commercial
> > > platforms from multiple OEMs.
> > >
> > > The primary use case of IVSC is to bring in context awareness. IVSC
> > > interfaces directly with the platform main camera sensor via a CSI-2
> > > link and processes the image data with the embedded AI engine. The
> > > detected events are sent over I2C to ISH (Intel Sensor Hub) for
> > > additional data fusion from multiple sensors. The fusion results are
> > > used to implement advanced use cases like:
> > >  - Face detection to unlock screen
> > >  - Detect user presence to manage backlight setting or waking up
> > > system
> > >
> > > Since the Image Processing Unit(IPU) used on the host processor needs
> > > to configure the CSI-2 link in normal camera usages, the CSI-2 link
> > > and camera sensor can only be used in mutually-exclusive ways by host
> > > IPU and IVSC. By default the IVSC owns the CSI-2 link and camera
> > > sensor. The IPU driver can take ownership of the CSI-2 link and camera
> > > sensor using interfaces exported via v4l2 sub-device.
> > >
> > > Switching ownership requires an interface with two different hardware
> > > modules inside IVSC. The software interface to these modules is via
> > > Intel MEI (The Intel Management Engine) commands. These two hardware
> > > modules have two different MEI UUIDs to enumerate. These hardware
> > modules are:
> > >  - ACE (Algorithm Context Engine): This module is for algorithm
> > > computing when IVSC owns camera sensor. Also ACE module controls
> > > camera sensor's ownership. This hardware module is used to set ownership of
> > camera sensor.
> > >  - CSI (Camera Serial Interface): This module is used to route camera
> > > sensor data either to IVSC or to host for IPU driver and application.
> > >
> > > IVSC also provides a privacy mode. When privacy mode is turned on,
> > > camera sensor can't be used. This means that both ACE and host IPU
> > > can't get image data. And when this mode is turned on, users are
> > > informed via
> > > v4l2 control API.
> > >
> > > In summary, to acquire ownership of camera by IPU driver, first ACE
> > > module needs to be informed of ownership and then to setup MIPI CSI-2
> > > link for the camera sensor and IPU.
> > >
> > > Implementation:
> > > There are two different drivers to handle ACE and CSI hardware modules
> > > inside IVSC.
> > >  - ivsc_csi: MEI client driver to send commands and receive
> > > notifications from CSI module.
> > >  - ivsc_ace: MEI client driver to send commands and get status from
> > > ACE module.
> > > Interface is exposed via v4l2 sub-devcie APIs to acquire and release
> > > camera sensor and CSI-2 link.
> > 
> > Thanks for the update.
> > 
> > Could you elaborate the decision of keeping the csi_bridge entirely separate
> > from the cio2_bridge (to be turned to ipu_bridge first)? Both are doing
> > essentially the same and using the same data structures, aren't they?
> 
> yes, they're doing same thing to bridge the software nodes needed by
> v4l2, but they have different type devices(pci and mei_client device) and
> dependency. And they have same SSDB definition in DSDT, so the structures
> are almost same.

If there are differences, what are they?

What comes to cio2_bridge, the fact that it's related to a PCI device
doesn't seem to matter after initialisation so it could as well work with
struct device.

> 
> I have no idea what the ipu bridge would be like, but IVSC csi bridge can
> be configured via kconfig to enable/disable.

Please work out the details with Bingbu.

And please do wrap your lines at 74 or so.

-- 
Kind regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 26+ messages in thread

* RE: [PATCH v3 0/3] media: pci: intel: ivsc: Add driver of Intel Visual Sensing Controller(IVSC)
  2023-03-27  7:49     ` Sakari Ailus
@ 2023-03-27  8:13       ` Wu, Wentong
  2023-03-27  9:36         ` Sakari Ailus
  0 siblings, 1 reply; 26+ messages in thread
From: Wu, Wentong @ 2023-03-27  8:13 UTC (permalink / raw)
  To: Sakari Ailus, bingbu.cao
  Cc: hdegoede, djrscally, laurent.pinchart, linux-media, Wang,
	Zhifeng, Ye, Xiang, Qiu, Tian Shu



> -----Original Message-----
> From: Sakari Ailus <sakari.ailus@linux.intel.com>
> Sent: Monday, March 27, 2023 3:50 PM
> 
> Hi Wentong,
> 
> On Mon, Mar 27, 2023 at 07:33:48AM +0000, Wu, Wentong wrote:
> >
> >
> > > -----Original Message-----
> > > From: Sakari Ailus <sakari.ailus@linux.intel.com>
> > > Sent: Monday, March 27, 2023 3:21 PM
> > >
> > > Hi Wentong,
> > >
> > > On Mon, Mar 27, 2023 at 02:23:05PM +0800, Wentong Wu wrote:
> > > > Intel Visual Sensing Controller (IVSC), codenamed "Clover Falls",
> > > > is a companion chip designed to provide secure and low power
> > > > vision capability to IA platforms. IVSC is available in existing
> > > > commercial platforms from multiple OEMs.
> > > >
> > > > The primary use case of IVSC is to bring in context awareness.
> > > > IVSC interfaces directly with the platform main camera sensor via
> > > > a CSI-2 link and processes the image data with the embedded AI
> > > > engine. The detected events are sent over I2C to ISH (Intel Sensor
> > > > Hub) for additional data fusion from multiple sensors. The fusion
> > > > results are used to implement advanced use cases like:
> > > >  - Face detection to unlock screen
> > > >  - Detect user presence to manage backlight setting or waking up
> > > > system
> > > >
> > > > Since the Image Processing Unit(IPU) used on the host processor
> > > > needs to configure the CSI-2 link in normal camera usages, the
> > > > CSI-2 link and camera sensor can only be used in
> > > > mutually-exclusive ways by host IPU and IVSC. By default the IVSC
> > > > owns the CSI-2 link and camera sensor. The IPU driver can take
> > > > ownership of the CSI-2 link and camera sensor using interfaces exported
> via v4l2 sub-device.
> > > >
> > > > Switching ownership requires an interface with two different
> > > > hardware modules inside IVSC. The software interface to these
> > > > modules is via Intel MEI (The Intel Management Engine) commands.
> > > > These two hardware modules have two different MEI UUIDs to
> > > > enumerate. These hardware
> > > modules are:
> > > >  - ACE (Algorithm Context Engine): This module is for algorithm
> > > > computing when IVSC owns camera sensor. Also ACE module controls
> > > > camera sensor's ownership. This hardware module is used to set
> > > > ownership of
> > > camera sensor.
> > > >  - CSI (Camera Serial Interface): This module is used to route
> > > > camera sensor data either to IVSC or to host for IPU driver and application.
> > > >
> > > > IVSC also provides a privacy mode. When privacy mode is turned on,
> > > > camera sensor can't be used. This means that both ACE and host IPU
> > > > can't get image data. And when this mode is turned on, users are
> > > > informed via
> > > > v4l2 control API.
> > > >
> > > > In summary, to acquire ownership of camera by IPU driver, first
> > > > ACE module needs to be informed of ownership and then to setup
> > > > MIPI CSI-2 link for the camera sensor and IPU.
> > > >
> > > > Implementation:
> > > > There are two different drivers to handle ACE and CSI hardware
> > > > modules inside IVSC.
> > > >  - ivsc_csi: MEI client driver to send commands and receive
> > > > notifications from CSI module.
> > > >  - ivsc_ace: MEI client driver to send commands and get status
> > > > from ACE module.
> > > > Interface is exposed via v4l2 sub-devcie APIs to acquire and
> > > > release camera sensor and CSI-2 link.
> > >
> > > Thanks for the update.
> > >
> > > Could you elaborate the decision of keeping the csi_bridge entirely
> > > separate from the cio2_bridge (to be turned to ipu_bridge first)?
> > > Both are doing essentially the same and using the same data structures,
> aren't they?
> >
> > yes, they're doing same thing to bridge the software nodes needed by
> > v4l2, but they have different type devices(pci and mei_client device)
> > and dependency. And they have same SSDB definition in DSDT, so the
> > structures are almost same.
> 
> If there are differences, what are they?
> 
The properties of swnode are same, because we need same info to configure
CSI2. But csi bridge can get sensor connected to IVSC by dependency info instead
of just polling the only supported sensors for ipu. Also IVSC doesn't need vcm.
And the topology of dependency is different. 

> What comes to cio2_bridge, the fact that it's related to a PCI device doesn't
> seem to matter after initialisation so it could as well work with struct device.
> 
> >
> > I have no idea what the ipu bridge would be like, but IVSC csi bridge
> > can be configured via kconfig to enable/disable.
> 
> Please work out the details with Bingbu.

@bingbu.cao@linux.intel.com @Sakari Ailus Please share your design idea here.
What the ipu bridge would be like?  What's the responsibility of ipu bridge?

> 
> And please do wrap your lines at 74 or so.
> 
> --
> Kind regards,
> 
> Sakari Ailus

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v3 0/3] media: pci: intel: ivsc: Add driver of Intel Visual Sensing Controller(IVSC)
  2023-03-27  8:13       ` Wu, Wentong
@ 2023-03-27  9:36         ` Sakari Ailus
  2023-03-28  7:32           ` Wu, Wentong
  0 siblings, 1 reply; 26+ messages in thread
From: Sakari Ailus @ 2023-03-27  9:36 UTC (permalink / raw)
  To: Wu, Wentong
  Cc: bingbu.cao, hdegoede, djrscally, laurent.pinchart, linux-media,
	Wang, Zhifeng, Ye, Xiang, Qiu, Tian Shu

Hi Wentong,

On Mon, Mar 27, 2023 at 08:13:29AM +0000, Wu, Wentong wrote:
> 
> 
> > -----Original Message-----
> > From: Sakari Ailus <sakari.ailus@linux.intel.com>
> > Sent: Monday, March 27, 2023 3:50 PM
> > 
> > Hi Wentong,
> > 
> > On Mon, Mar 27, 2023 at 07:33:48AM +0000, Wu, Wentong wrote:
> > >
> > >
> > > > -----Original Message-----
> > > > From: Sakari Ailus <sakari.ailus@linux.intel.com>
> > > > Sent: Monday, March 27, 2023 3:21 PM
> > > >
> > > > Hi Wentong,
> > > >
> > > > On Mon, Mar 27, 2023 at 02:23:05PM +0800, Wentong Wu wrote:
> > > > > Intel Visual Sensing Controller (IVSC), codenamed "Clover Falls",
> > > > > is a companion chip designed to provide secure and low power
> > > > > vision capability to IA platforms. IVSC is available in existing
> > > > > commercial platforms from multiple OEMs.
> > > > >
> > > > > The primary use case of IVSC is to bring in context awareness.
> > > > > IVSC interfaces directly with the platform main camera sensor via
> > > > > a CSI-2 link and processes the image data with the embedded AI
> > > > > engine. The detected events are sent over I2C to ISH (Intel Sensor
> > > > > Hub) for additional data fusion from multiple sensors. The fusion
> > > > > results are used to implement advanced use cases like:
> > > > >  - Face detection to unlock screen
> > > > >  - Detect user presence to manage backlight setting or waking up
> > > > > system
> > > > >
> > > > > Since the Image Processing Unit(IPU) used on the host processor
> > > > > needs to configure the CSI-2 link in normal camera usages, the
> > > > > CSI-2 link and camera sensor can only be used in
> > > > > mutually-exclusive ways by host IPU and IVSC. By default the IVSC
> > > > > owns the CSI-2 link and camera sensor. The IPU driver can take
> > > > > ownership of the CSI-2 link and camera sensor using interfaces exported
> > via v4l2 sub-device.
> > > > >
> > > > > Switching ownership requires an interface with two different
> > > > > hardware modules inside IVSC. The software interface to these
> > > > > modules is via Intel MEI (The Intel Management Engine) commands.
> > > > > These two hardware modules have two different MEI UUIDs to
> > > > > enumerate. These hardware
> > > > modules are:
> > > > >  - ACE (Algorithm Context Engine): This module is for algorithm
> > > > > computing when IVSC owns camera sensor. Also ACE module controls
> > > > > camera sensor's ownership. This hardware module is used to set
> > > > > ownership of
> > > > camera sensor.
> > > > >  - CSI (Camera Serial Interface): This module is used to route
> > > > > camera sensor data either to IVSC or to host for IPU driver and application.
> > > > >
> > > > > IVSC also provides a privacy mode. When privacy mode is turned on,
> > > > > camera sensor can't be used. This means that both ACE and host IPU
> > > > > can't get image data. And when this mode is turned on, users are
> > > > > informed via
> > > > > v4l2 control API.
> > > > >
> > > > > In summary, to acquire ownership of camera by IPU driver, first
> > > > > ACE module needs to be informed of ownership and then to setup
> > > > > MIPI CSI-2 link for the camera sensor and IPU.
> > > > >
> > > > > Implementation:
> > > > > There are two different drivers to handle ACE and CSI hardware
> > > > > modules inside IVSC.
> > > > >  - ivsc_csi: MEI client driver to send commands and receive
> > > > > notifications from CSI module.
> > > > >  - ivsc_ace: MEI client driver to send commands and get status
> > > > > from ACE module.
> > > > > Interface is exposed via v4l2 sub-devcie APIs to acquire and
> > > > > release camera sensor and CSI-2 link.
> > > >
> > > > Thanks for the update.
> > > >
> > > > Could you elaborate the decision of keeping the csi_bridge entirely
> > > > separate from the cio2_bridge (to be turned to ipu_bridge first)?
> > > > Both are doing essentially the same and using the same data structures,
> > aren't they?
> > >
> > > yes, they're doing same thing to bridge the software nodes needed by
> > > v4l2, but they have different type devices(pci and mei_client device)
> > > and dependency. And they have same SSDB definition in DSDT, so the
> > > structures are almost same.
> > 
> > If there are differences, what are they?
> > 
> The properties of swnode are same, because we need same info to configure
> CSI2. But csi bridge can get sensor connected to IVSC by dependency info
> instead of just polling the only supported sensors for ipu. Also IVSC

It's totally fine the method for finding the sensor is different, this
should be an extra argument for the bridge init function and a small number
of lines of additional code.

> doesn't need vcm. And the topology of dependency is different.

The VCM is optional for current cio2_bridge, too.

It also seems that this currently creates swnodes on mei csi side only for
the connection towards the sensor, not the IPU. At the moment you can't
reliably add software nodes to a device that has may be already probing so
both of these should be created at the same time.

> 
> > What comes to cio2_bridge, the fact that it's related to a PCI device doesn't
> > seem to matter after initialisation so it could as well work with struct device.
> > 
> > >
> > > I have no idea what the ipu bridge would be like, but IVSC csi bridge
> > > can be configured via kconfig to enable/disable.
> > 
> > Please work out the details with Bingbu.
> 
> @bingbu.cao@linux.intel.com @Sakari Ailus Please share your design idea here.
> What the ipu bridge would be like?  What's the responsibility of ipu bridge?

I'd expect Bingbu to have patches to turn the current cio2_bridge to an
ipu_bridge at some point. These should come on top of those patches.

-- 
Kind regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v3 1/3] media: pci: intel: ivsc: Add CSI submodule
  2023-03-27  6:23 ` [PATCH v3 1/3] media: pci: intel: ivsc: Add CSI submodule Wentong Wu
@ 2023-03-27 10:01   ` kernel test robot
  2023-03-28 13:59   ` kernel test robot
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 26+ messages in thread
From: kernel test robot @ 2023-03-27 10:01 UTC (permalink / raw)
  To: Wentong Wu, sakari.ailus, hdegoede, djrscally, laurent.pinchart,
	linux-media
  Cc: oe-kbuild-all, bingbu.cao, zhifeng.wang, xiang.ye, tian.shu.qiu,
	Wentong Wu

Hi Wentong,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on media-tree/master]
[also build test WARNING on rafael-pm/linux-next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Wentong-Wu/media-pci-intel-ivsc-Add-CSI-submodule/20230327-142435
base:   git://linuxtv.org/media_tree.git master
patch link:    https://lore.kernel.org/r/1679898188-14426-2-git-send-email-wentong.wu%40intel.com
patch subject: [PATCH v3 1/3] media: pci: intel: ivsc: Add CSI submodule
config: i386-allyesconfig (https://download.01.org/0day-ci/archive/20230327/202303271753.XGeJ1Prl-lkp@intel.com/config)
compiler: gcc-11 (Debian 11.3.0-8) 11.3.0
reproduce (this is a W=1 build):
        # https://github.com/intel-lab-lkp/linux/commit/413726a06ca6f775b34425ed81e996b9b7237d31
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Wentong-Wu/media-pci-intel-ivsc-Add-CSI-submodule/20230327-142435
        git checkout 413726a06ca6f775b34425ed81e996b9b7237d31
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        make W=1 O=build_dir ARCH=i386 olddefconfig
        make W=1 O=build_dir ARCH=i386 SHELL=/bin/bash drivers/media/pci/intel/ivsc/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Link: https://lore.kernel.org/oe-kbuild-all/202303271753.XGeJ1Prl-lkp@intel.com/

All warnings (new ones prefixed by >>):

   drivers/media/pci/intel/ivsc/csi_bridge.c: In function 'csi_bridge_unregister_sensor':
   drivers/media/pci/intel/ivsc/csi_bridge.c:179:9: error: implicit declaration of function 'software_node_unregister_nodes'; did you mean 'software_node_unregister'? [-Werror=implicit-function-declaration]
     179 |         software_node_unregister_nodes(sensor->swnodes);
         |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         |         software_node_unregister
   drivers/media/pci/intel/ivsc/csi_bridge.c: In function 'csi_bridge_connect_sensor':
   drivers/media/pci/intel/ivsc/csi_bridge.c:231:15: error: implicit declaration of function 'software_node_register_nodes'; did you mean 'software_node_register'? [-Werror=implicit-function-declaration]
     231 |         ret = software_node_register_nodes(sensor->swnodes);
         |               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
         |               software_node_register
   drivers/media/pci/intel/ivsc/csi_bridge.c: At top level:
>> drivers/media/pci/intel/ivsc/csi_bridge.c:284:5: warning: no previous prototype for 'csi_bridge_init' [-Wmissing-prototypes]
     284 | int csi_bridge_init(struct mei_cl_device *csi_dev)
         |     ^~~~~~~~~~~~~~~
   cc1: some warnings being treated as errors


vim +/csi_bridge_init +284 drivers/media/pci/intel/ivsc/csi_bridge.c

   283	
 > 284	int csi_bridge_init(struct mei_cl_device *csi_dev)

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests

^ permalink raw reply	[flat|nested] 26+ messages in thread

* RE: [PATCH v3 0/3] media: pci: intel: ivsc: Add driver of Intel Visual Sensing Controller(IVSC)
  2023-03-27  9:36         ` Sakari Ailus
@ 2023-03-28  7:32           ` Wu, Wentong
  2023-03-28 19:48             ` Sakari Ailus
  0 siblings, 1 reply; 26+ messages in thread
From: Wu, Wentong @ 2023-03-28  7:32 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: bingbu.cao, hdegoede, djrscally, laurent.pinchart, linux-media,
	Wang, Zhifeng, Ye, Xiang, Qiu, Tian Shu



> -----Original Message-----
> From: Sakari Ailus <sakari.ailus@linux.intel.com>
> Sent: Monday, March 27, 2023 5:37 PM
> 
> Hi Wentong,
> 
> On Mon, Mar 27, 2023 at 08:13:29AM +0000, Wu, Wentong wrote:
> >
> >
> > > -----Original Message-----
> > > From: Sakari Ailus <sakari.ailus@linux.intel.com>
> > > Sent: Monday, March 27, 2023 3:50 PM
> > >
> > > Hi Wentong,
> > >
> > > On Mon, Mar 27, 2023 at 07:33:48AM +0000, Wu, Wentong wrote:
> > > >
> > > >
> > > > > -----Original Message-----
> > > > > From: Sakari Ailus <sakari.ailus@linux.intel.com>
> > > > > Sent: Monday, March 27, 2023 3:21 PM
> > > > >
> > > > > Hi Wentong,
> > > > >
> > > > > On Mon, Mar 27, 2023 at 02:23:05PM +0800, Wentong Wu wrote:
> > > > > > Intel Visual Sensing Controller (IVSC), codenamed "Clover
> > > > > > Falls", is a companion chip designed to provide secure and low
> > > > > > power vision capability to IA platforms. IVSC is available in
> > > > > > existing commercial platforms from multiple OEMs.
> > > > > >
> > > > > > The primary use case of IVSC is to bring in context awareness.
> > > > > > IVSC interfaces directly with the platform main camera sensor
> > > > > > via a CSI-2 link and processes the image data with the
> > > > > > embedded AI engine. The detected events are sent over I2C to
> > > > > > ISH (Intel Sensor
> > > > > > Hub) for additional data fusion from multiple sensors. The
> > > > > > fusion results are used to implement advanced use cases like:
> > > > > >  - Face detection to unlock screen
> > > > > >  - Detect user presence to manage backlight setting or waking
> > > > > > up system
> > > > > >
> > > > > > Since the Image Processing Unit(IPU) used on the host
> > > > > > processor needs to configure the CSI-2 link in normal camera
> > > > > > usages, the
> > > > > > CSI-2 link and camera sensor can only be used in
> > > > > > mutually-exclusive ways by host IPU and IVSC. By default the
> > > > > > IVSC owns the CSI-2 link and camera sensor. The IPU driver can
> > > > > > take ownership of the CSI-2 link and camera sensor using
> > > > > > interfaces exported
> > > via v4l2 sub-device.
> > > > > >
> > > > > > Switching ownership requires an interface with two different
> > > > > > hardware modules inside IVSC. The software interface to these
> > > > > > modules is via Intel MEI (The Intel Management Engine) commands.
> > > > > > These two hardware modules have two different MEI UUIDs to
> > > > > > enumerate. These hardware
> > > > > modules are:
> > > > > >  - ACE (Algorithm Context Engine): This module is for
> > > > > > algorithm computing when IVSC owns camera sensor. Also ACE
> > > > > > module controls camera sensor's ownership. This hardware
> > > > > > module is used to set ownership of
> > > > > camera sensor.
> > > > > >  - CSI (Camera Serial Interface): This module is used to route
> > > > > > camera sensor data either to IVSC or to host for IPU driver and
> application.
> > > > > >
> > > > > > IVSC also provides a privacy mode. When privacy mode is turned
> > > > > > on, camera sensor can't be used. This means that both ACE and
> > > > > > host IPU can't get image data. And when this mode is turned
> > > > > > on, users are informed via
> > > > > > v4l2 control API.
> > > > > >
> > > > > > In summary, to acquire ownership of camera by IPU driver,
> > > > > > first ACE module needs to be informed of ownership and then to
> > > > > > setup MIPI CSI-2 link for the camera sensor and IPU.
> > > > > >
> > > > > > Implementation:
> > > > > > There are two different drivers to handle ACE and CSI hardware
> > > > > > modules inside IVSC.
> > > > > >  - ivsc_csi: MEI client driver to send commands and receive
> > > > > > notifications from CSI module.
> > > > > >  - ivsc_ace: MEI client driver to send commands and get status
> > > > > > from ACE module.
> > > > > > Interface is exposed via v4l2 sub-devcie APIs to acquire and
> > > > > > release camera sensor and CSI-2 link.
> > > > >
> > > > > Thanks for the update.
> > > > >
> > > > > Could you elaborate the decision of keeping the csi_bridge
> > > > > entirely separate from the cio2_bridge (to be turned to ipu_bridge first)?
> > > > > Both are doing essentially the same and using the same data
> > > > > structures,
> > > aren't they?
> > > >
> > > > yes, they're doing same thing to bridge the software nodes needed
> > > > by v4l2, but they have different type devices(pci and mei_client
> > > > device) and dependency. And they have same SSDB definition in
> > > > DSDT, so the structures are almost same.
> > >
> > > If there are differences, what are they?
> > >
> > The properties of swnode are same, because we need same info to
> > configure CSI2. But csi bridge can get sensor connected to IVSC by
> > dependency info instead of just polling the only supported sensors for
> > ipu. Also IVSC
> 
> It's totally fine the method for finding the sensor is different, this should be an
> extra argument for the bridge init function and a small number of lines of
> additional code.
> 
> > doesn't need vcm. And the topology of dependency is different.
> 
> The VCM is optional for current cio2_bridge, too.
> 
> It also seems that this currently creates swnodes on mei csi side only for the
> connection towards the sensor, not the IPU.

IPU could find the MEI CSI software node by software_node_find_by_name,
and then setup the bridge between MEI CSI and IPU.

> At the moment you can't reliably
> add software nodes to a device that has may be already probing so both of
> these should be created at the same time.

If you're worried about sequence, IPU driver could make software node and
register it, MEI CSI bridge could find IPU software node by software_node_find_by_name,
if the return value is NULL, driver will defer the probe.

> 
> >
> > > What comes to cio2_bridge, the fact that it's related to a PCI
> > > device doesn't seem to matter after initialisation so it could as well work
> with struct device.
> > >
> > > >
> > > > I have no idea what the ipu bridge would be like, but IVSC csi
> > > > bridge can be configured via kconfig to enable/disable.
> > >
> > > Please work out the details with Bingbu.
> >
> > @bingbu.cao@linux.intel.com @Sakari Ailus Please share your design idea
> here.
> > What the ipu bridge would be like?  What's the responsibility of ipu bridge?
> 
> I'd expect Bingbu to have patches to turn the current cio2_bridge to an
> ipu_bridge at some point. These should come on top of those patches.

When will this be ready if you already make the plan? But could you please help
review my other patches except bridge?

Thanks
Wentong
> 
> --
> Kind regards,
> 
> Sakari Ailus

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v3 1/3] media: pci: intel: ivsc: Add CSI submodule
  2023-03-27  6:23 ` [PATCH v3 1/3] media: pci: intel: ivsc: Add CSI submodule Wentong Wu
  2023-03-27 10:01   ` kernel test robot
@ 2023-03-28 13:59   ` kernel test robot
  2023-03-29  8:48   ` Sakari Ailus
  2023-03-29  8:49   ` Sakari Ailus
  3 siblings, 0 replies; 26+ messages in thread
From: kernel test robot @ 2023-03-28 13:59 UTC (permalink / raw)
  To: Wentong Wu, sakari.ailus, hdegoede, djrscally, laurent.pinchart,
	linux-media
  Cc: llvm, oe-kbuild-all, bingbu.cao, zhifeng.wang, xiang.ye,
	tian.shu.qiu, Wentong Wu

Hi Wentong,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on media-tree/master]
[also build test WARNING on rafael-pm/linux-next sailus-media-tree/streams linus/master v6.3-rc4 next-20230328]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Wentong-Wu/media-pci-intel-ivsc-Add-CSI-submodule/20230327-142435
base:   git://linuxtv.org/media_tree.git master
patch link:    https://lore.kernel.org/r/1679898188-14426-2-git-send-email-wentong.wu%40intel.com
patch subject: [PATCH v3 1/3] media: pci: intel: ivsc: Add CSI submodule
config: i386-allyesconfig (https://download.01.org/0day-ci/archive/20230328/202303282158.PaVVxGIS-lkp@intel.com/config)
compiler: clang version 14.0.6 (https://github.com/llvm/llvm-project f28c006a5895fc0e329fe15fead81e37457cb1d1)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/413726a06ca6f775b34425ed81e996b9b7237d31
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Wentong-Wu/media-pci-intel-ivsc-Add-CSI-submodule/20230327-142435
        git checkout 413726a06ca6f775b34425ed81e996b9b7237d31
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 SHELL=/bin/bash drivers/media/i2c/ drivers/media/pci/intel/ivsc/ drivers/net/usb/ net/netfilter/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Link: https://lore.kernel.org/oe-kbuild-all/202303282158.PaVVxGIS-lkp@intel.com/

All warnings (new ones prefixed by >>):

   drivers/media/pci/intel/ivsc/csi_bridge.c:179:2: error: implicit declaration of function 'software_node_unregister_nodes' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
           software_node_unregister_nodes(sensor->swnodes);
           ^
   drivers/media/pci/intel/ivsc/csi_bridge.c:231:8: error: implicit declaration of function 'software_node_register_nodes' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
           ret = software_node_register_nodes(sensor->swnodes);
                 ^
   drivers/media/pci/intel/ivsc/csi_bridge.c:249:2: error: implicit declaration of function 'software_node_unregister_nodes' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
           software_node_unregister_nodes(sensor->swnodes);
           ^
>> drivers/media/pci/intel/ivsc/csi_bridge.c:284:5: warning: no previous prototype for function 'csi_bridge_init' [-Wmissing-prototypes]
   int csi_bridge_init(struct mei_cl_device *csi_dev)
       ^
   drivers/media/pci/intel/ivsc/csi_bridge.c:284:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
   int csi_bridge_init(struct mei_cl_device *csi_dev)
   ^
   static 
   1 warning and 3 errors generated.


vim +/csi_bridge_init +284 drivers/media/pci/intel/ivsc/csi_bridge.c

   283	
 > 284	int csi_bridge_init(struct mei_cl_device *csi_dev)

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v3 0/3] media: pci: intel: ivsc: Add driver of Intel Visual Sensing Controller(IVSC)
  2023-03-28  7:32           ` Wu, Wentong
@ 2023-03-28 19:48             ` Sakari Ailus
  2023-03-29  0:42               ` Wu, Wentong
  0 siblings, 1 reply; 26+ messages in thread
From: Sakari Ailus @ 2023-03-28 19:48 UTC (permalink / raw)
  To: Wu, Wentong
  Cc: bingbu.cao, hdegoede, djrscally, laurent.pinchart, linux-media,
	Wang, Zhifeng, Ye, Xiang, Qiu, Tian Shu

On Tue, Mar 28, 2023 at 07:32:27AM +0000, Wu, Wentong wrote:
> 
> 
> > -----Original Message-----
> > From: Sakari Ailus <sakari.ailus@linux.intel.com>
> > Sent: Monday, March 27, 2023 5:37 PM
> > 
> > Hi Wentong,
> > 
> > On Mon, Mar 27, 2023 at 08:13:29AM +0000, Wu, Wentong wrote:
> > >
> > >
> > > > -----Original Message-----
> > > > From: Sakari Ailus <sakari.ailus@linux.intel.com>
> > > > Sent: Monday, March 27, 2023 3:50 PM
> > > >
> > > > Hi Wentong,
> > > >
> > > > On Mon, Mar 27, 2023 at 07:33:48AM +0000, Wu, Wentong wrote:
> > > > >
> > > > >
> > > > > > -----Original Message-----
> > > > > > From: Sakari Ailus <sakari.ailus@linux.intel.com>
> > > > > > Sent: Monday, March 27, 2023 3:21 PM
> > > > > >
> > > > > > Hi Wentong,
> > > > > >
> > > > > > On Mon, Mar 27, 2023 at 02:23:05PM +0800, Wentong Wu wrote:
> > > > > > > Intel Visual Sensing Controller (IVSC), codenamed "Clover
> > > > > > > Falls", is a companion chip designed to provide secure and low
> > > > > > > power vision capability to IA platforms. IVSC is available in
> > > > > > > existing commercial platforms from multiple OEMs.
> > > > > > >
> > > > > > > The primary use case of IVSC is to bring in context awareness.
> > > > > > > IVSC interfaces directly with the platform main camera sensor
> > > > > > > via a CSI-2 link and processes the image data with the
> > > > > > > embedded AI engine. The detected events are sent over I2C to
> > > > > > > ISH (Intel Sensor
> > > > > > > Hub) for additional data fusion from multiple sensors. The
> > > > > > > fusion results are used to implement advanced use cases like:
> > > > > > >  - Face detection to unlock screen
> > > > > > >  - Detect user presence to manage backlight setting or waking
> > > > > > > up system
> > > > > > >
> > > > > > > Since the Image Processing Unit(IPU) used on the host
> > > > > > > processor needs to configure the CSI-2 link in normal camera
> > > > > > > usages, the
> > > > > > > CSI-2 link and camera sensor can only be used in
> > > > > > > mutually-exclusive ways by host IPU and IVSC. By default the
> > > > > > > IVSC owns the CSI-2 link and camera sensor. The IPU driver can
> > > > > > > take ownership of the CSI-2 link and camera sensor using
> > > > > > > interfaces exported
> > > > via v4l2 sub-device.
> > > > > > >
> > > > > > > Switching ownership requires an interface with two different
> > > > > > > hardware modules inside IVSC. The software interface to these
> > > > > > > modules is via Intel MEI (The Intel Management Engine) commands.
> > > > > > > These two hardware modules have two different MEI UUIDs to
> > > > > > > enumerate. These hardware
> > > > > > modules are:
> > > > > > >  - ACE (Algorithm Context Engine): This module is for
> > > > > > > algorithm computing when IVSC owns camera sensor. Also ACE
> > > > > > > module controls camera sensor's ownership. This hardware
> > > > > > > module is used to set ownership of
> > > > > > camera sensor.
> > > > > > >  - CSI (Camera Serial Interface): This module is used to route
> > > > > > > camera sensor data either to IVSC or to host for IPU driver and
> > application.
> > > > > > >
> > > > > > > IVSC also provides a privacy mode. When privacy mode is turned
> > > > > > > on, camera sensor can't be used. This means that both ACE and
> > > > > > > host IPU can't get image data. And when this mode is turned
> > > > > > > on, users are informed via
> > > > > > > v4l2 control API.
> > > > > > >
> > > > > > > In summary, to acquire ownership of camera by IPU driver,
> > > > > > > first ACE module needs to be informed of ownership and then to
> > > > > > > setup MIPI CSI-2 link for the camera sensor and IPU.
> > > > > > >
> > > > > > > Implementation:
> > > > > > > There are two different drivers to handle ACE and CSI hardware
> > > > > > > modules inside IVSC.
> > > > > > >  - ivsc_csi: MEI client driver to send commands and receive
> > > > > > > notifications from CSI module.
> > > > > > >  - ivsc_ace: MEI client driver to send commands and get status
> > > > > > > from ACE module.
> > > > > > > Interface is exposed via v4l2 sub-devcie APIs to acquire and
> > > > > > > release camera sensor and CSI-2 link.
> > > > > >
> > > > > > Thanks for the update.
> > > > > >
> > > > > > Could you elaborate the decision of keeping the csi_bridge
> > > > > > entirely separate from the cio2_bridge (to be turned to ipu_bridge first)?
> > > > > > Both are doing essentially the same and using the same data
> > > > > > structures,
> > > > aren't they?
> > > > >
> > > > > yes, they're doing same thing to bridge the software nodes needed
> > > > > by v4l2, but they have different type devices(pci and mei_client
> > > > > device) and dependency. And they have same SSDB definition in
> > > > > DSDT, so the structures are almost same.
> > > >
> > > > If there are differences, what are they?
> > > >
> > > The properties of swnode are same, because we need same info to
> > > configure CSI2. But csi bridge can get sensor connected to IVSC by
> > > dependency info instead of just polling the only supported sensors for
> > > ipu. Also IVSC
> > 
> > It's totally fine the method for finding the sensor is different, this should be an
> > extra argument for the bridge init function and a small number of lines of
> > additional code.
> > 
> > > doesn't need vcm. And the topology of dependency is different.
> > 
> > The VCM is optional for current cio2_bridge, too.
> > 
> > It also seems that this currently creates swnodes on mei csi side only for the
> > connection towards the sensor, not the IPU.
> 
> IPU could find the MEI CSI software node by software_node_find_by_name,
> and then setup the bridge between MEI CSI and IPU.
> 
> > At the moment you can't reliably
> > add software nodes to a device that has may be already probing so both of
> > these should be created at the same time.
> 
> If you're worried about sequence, IPU driver could make software node and
> register it, MEI CSI bridge could find IPU software node by software_node_find_by_name,
> if the return value is NULL, driver will defer the probe.

The IPU driver would also need to figure out whether a given sensor should
be behind the IVSC. I imagine it'll be hard to achieve this reasonably
neatly if you split the implementation into different parts --- both of
which need the same information from the same place (and in neither case
it's under the same device than to which the driver is bound).

> 
> > 
> > >
> > > > What comes to cio2_bridge, the fact that it's related to a PCI
> > > > device doesn't seem to matter after initialisation so it could as well work
> > with struct device.
> > > >
> > > > >
> > > > > I have no idea what the ipu bridge would be like, but IVSC csi
> > > > > bridge can be configured via kconfig to enable/disable.
> > > >
> > > > Please work out the details with Bingbu.
> > >
> > > @bingbu.cao@linux.intel.com @Sakari Ailus Please share your design idea
> > here.
> > > What the ipu bridge would be like?  What's the responsibility of ipu bridge?
> > 
> > I'd expect Bingbu to have patches to turn the current cio2_bridge to an
> > ipu_bridge at some point. These should come on top of those patches.
> 
> When will this be ready if you already make the plan? But could you please help
> review my other patches except bridge?

Please configure your e-mail client to wrap your lines at most at 74
characters or so.

I think it's binding this all together that is the concern, the individual
drivers much less so.

-- 
Regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 26+ messages in thread

* RE: [PATCH v3 0/3] media: pci: intel: ivsc: Add driver of Intel Visual Sensing Controller(IVSC)
  2023-03-28 19:48             ` Sakari Ailus
@ 2023-03-29  0:42               ` Wu, Wentong
  2023-03-29  7:39                 ` Sakari Ailus
  0 siblings, 1 reply; 26+ messages in thread
From: Wu, Wentong @ 2023-03-29  0:42 UTC (permalink / raw)
  To: Sakari Ailus, hdegoede, djrscally, laurent.pinchart
  Cc: bingbu.cao, linux-media, Wang, Zhifeng, Ye, Xiang, Qiu, Tian Shu

Hi Sakari,

Thanks

> -----Original Message-----
> From: Sakari Ailus <sakari.ailus@linux.intel.com>
> Sent: Wednesday, March 29, 2023 3:49 AM
> 
> On Tue, Mar 28, 2023 at 07:32:27AM +0000, Wu, Wentong wrote:
> >
> >
> > > -----Original Message-----
> > > From: Sakari Ailus <sakari.ailus@linux.intel.com>
> > > Sent: Monday, March 27, 2023 5:37 PM
> > >
> > > Hi Wentong,
> > >
> > > On Mon, Mar 27, 2023 at 08:13:29AM +0000, Wu, Wentong wrote:
> > > >
> > > >
> > > > > -----Original Message-----
> > > > > From: Sakari Ailus <sakari.ailus@linux.intel.com>
> > > > > Sent: Monday, March 27, 2023 3:50 PM
> > > > >
> > > > > Hi Wentong,
> > > > >
> > > > > On Mon, Mar 27, 2023 at 07:33:48AM +0000, Wu, Wentong wrote:
> > > > > >
> > > > > >
> > > > > > > -----Original Message-----
> > > > > > > From: Sakari Ailus <sakari.ailus@linux.intel.com>
> > > > > > > Sent: Monday, March 27, 2023 3:21 PM
> > > > > > >
> > > > > > > Hi Wentong,
> > > > > > >
> > > > > > > On Mon, Mar 27, 2023 at 02:23:05PM +0800, Wentong Wu wrote:
> > > > > > > > Intel Visual Sensing Controller (IVSC), codenamed "Clover
> > > > > > > > Falls", is a companion chip designed to provide secure and
> > > > > > > > low power vision capability to IA platforms. IVSC is
> > > > > > > > available in existing commercial platforms from multiple OEMs.
> > > > > > > >
> > > > > > > > The primary use case of IVSC is to bring in context awareness.
> > > > > > > > IVSC interfaces directly with the platform main camera
> > > > > > > > sensor via a CSI-2 link and processes the image data with
> > > > > > > > the embedded AI engine. The detected events are sent over
> > > > > > > > I2C to ISH (Intel Sensor
> > > > > > > > Hub) for additional data fusion from multiple sensors. The
> > > > > > > > fusion results are used to implement advanced use cases like:
> > > > > > > >  - Face detection to unlock screen
> > > > > > > >  - Detect user presence to manage backlight setting or
> > > > > > > > waking up system
> > > > > > > >
> > > > > > > > Since the Image Processing Unit(IPU) used on the host
> > > > > > > > processor needs to configure the CSI-2 link in normal
> > > > > > > > camera usages, the
> > > > > > > > CSI-2 link and camera sensor can only be used in
> > > > > > > > mutually-exclusive ways by host IPU and IVSC. By default
> > > > > > > > the IVSC owns the CSI-2 link and camera sensor. The IPU
> > > > > > > > driver can take ownership of the CSI-2 link and camera
> > > > > > > > sensor using interfaces exported
> > > > > via v4l2 sub-device.
> > > > > > > >
> > > > > > > > Switching ownership requires an interface with two
> > > > > > > > different hardware modules inside IVSC. The software
> > > > > > > > interface to these modules is via Intel MEI (The Intel Management
> Engine) commands.
> > > > > > > > These two hardware modules have two different MEI UUIDs to
> > > > > > > > enumerate. These hardware
> > > > > > > modules are:
> > > > > > > >  - ACE (Algorithm Context Engine): This module is for
> > > > > > > > algorithm computing when IVSC owns camera sensor. Also ACE
> > > > > > > > module controls camera sensor's ownership. This hardware
> > > > > > > > module is used to set ownership of
> > > > > > > camera sensor.
> > > > > > > >  - CSI (Camera Serial Interface): This module is used to
> > > > > > > > route camera sensor data either to IVSC or to host for IPU
> > > > > > > > driver and
> > > application.
> > > > > > > >
> > > > > > > > IVSC also provides a privacy mode. When privacy mode is
> > > > > > > > turned on, camera sensor can't be used. This means that
> > > > > > > > both ACE and host IPU can't get image data. And when this
> > > > > > > > mode is turned on, users are informed via
> > > > > > > > v4l2 control API.
> > > > > > > >
> > > > > > > > In summary, to acquire ownership of camera by IPU driver,
> > > > > > > > first ACE module needs to be informed of ownership and
> > > > > > > > then to setup MIPI CSI-2 link for the camera sensor and IPU.
> > > > > > > >
> > > > > > > > Implementation:
> > > > > > > > There are two different drivers to handle ACE and CSI
> > > > > > > > hardware modules inside IVSC.
> > > > > > > >  - ivsc_csi: MEI client driver to send commands and
> > > > > > > > receive notifications from CSI module.
> > > > > > > >  - ivsc_ace: MEI client driver to send commands and get
> > > > > > > > status from ACE module.
> > > > > > > > Interface is exposed via v4l2 sub-devcie APIs to acquire
> > > > > > > > and release camera sensor and CSI-2 link.
> > > > > > >
> > > > > > > Thanks for the update.
> > > > > > >
> > > > > > > Could you elaborate the decision of keeping the csi_bridge
> > > > > > > entirely separate from the cio2_bridge (to be turned to ipu_bridge
> first)?
> > > > > > > Both are doing essentially the same and using the same data
> > > > > > > structures,
> > > > > aren't they?
> > > > > >
> > > > > > yes, they're doing same thing to bridge the software nodes
> > > > > > needed by v4l2, but they have different type devices(pci and
> > > > > > mei_client
> > > > > > device) and dependency. And they have same SSDB definition in
> > > > > > DSDT, so the structures are almost same.
> > > > >
> > > > > If there are differences, what are they?
> > > > >
> > > > The properties of swnode are same, because we need same info to
> > > > configure CSI2. But csi bridge can get sensor connected to IVSC by
> > > > dependency info instead of just polling the only supported sensors
> > > > for ipu. Also IVSC
> > >
> > > It's totally fine the method for finding the sensor is different,
> > > this should be an extra argument for the bridge init function and a
> > > small number of lines of additional code.
> > >
> > > > doesn't need vcm. And the topology of dependency is different.
> > >
> > > The VCM is optional for current cio2_bridge, too.
> > >
> > > It also seems that this currently creates swnodes on mei csi side
> > > only for the connection towards the sensor, not the IPU.
> >
> > IPU could find the MEI CSI software node by
> > software_node_find_by_name, and then setup the bridge between MEI CSI
> and IPU.
> >
> > > At the moment you can't reliably
> > > add software nodes to a device that has may be already probing so
> > > both of these should be created at the same time.
> >
> > If you're worried about sequence, IPU driver could make software node
> > and register it, MEI CSI bridge could find IPU software node by
> > software_node_find_by_name, if the return value is NULL, driver will defer the
> probe.
> 
> The IPU driver would also need to figure out whether a given sensor should be
> behind the IVSC. I imagine it'll be hard to achieve this reasonably neatly if you
> split the implementation into different parts --- both of which need the same
> information from the same place (and in neither case it's under the same device
> than to which the driver is bound).
> 
> >
> > >
> > > >
> > > > > What comes to cio2_bridge, the fact that it's related to a PCI
> > > > > device doesn't seem to matter after initialisation so it could
> > > > > as well work
> > > with struct device.
> > > > >
> > > > > >
> > > > > > I have no idea what the ipu bridge would be like, but IVSC csi
> > > > > > bridge can be configured via kconfig to enable/disable.
> > > > >
> > > > > Please work out the details with Bingbu.
> > > >
> > > > @bingbu.cao@linux.intel.com @Sakari Ailus Please share your design
> > > > idea
> > > here.
> > > > What the ipu bridge would be like?  What's the responsibility of ipu bridge?
> > >
> > > I'd expect Bingbu to have patches to turn the current cio2_bridge to
> > > an ipu_bridge at some point. These should come on top of those patches.
> >
> > When will this be ready if you already make the plan? But could you
> > please help review my other patches except bridge?
> 
> Please configure your e-mail client to wrap your lines at most at 74 characters
> or so.
> 
> I think it's binding this all together that is the concern, the individual drivers
> much less so.

Thanks, I would remove the csi bridge in next version.

Ok, the IPU bridge will also cover the bridge between IVSC and sensor.

BTW, please hans and laurent help take a look the mei_csi and mei_ace patch.

BR,
Wentong
> 
> --
> Regards,
> 
> Sakari Ailus

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v3 0/3] media: pci: intel: ivsc: Add driver of Intel Visual Sensing Controller(IVSC)
  2023-03-29  0:42               ` Wu, Wentong
@ 2023-03-29  7:39                 ` Sakari Ailus
  0 siblings, 0 replies; 26+ messages in thread
From: Sakari Ailus @ 2023-03-29  7:39 UTC (permalink / raw)
  To: Wu, Wentong
  Cc: hdegoede, djrscally, laurent.pinchart, bingbu.cao, linux-media,
	Wang, Zhifeng, Ye, Xiang, Qiu, Tian Shu

Hi Wentong,

On Wed, Mar 29, 2023 at 12:42:06AM +0000, Wu, Wentong wrote:
> > > > > > > I have no idea what the ipu bridge would be like, but IVSC csi
> > > > > > > bridge can be configured via kconfig to enable/disable.
> > > > > >
> > > > > > Please work out the details with Bingbu.
> > > > >
> > > > > @bingbu.cao@linux.intel.com @Sakari Ailus Please share your design
> > > > > idea
> > > > here.
> > > > > What the ipu bridge would be like?  What's the responsibility of ipu bridge?
> > > >
> > > > I'd expect Bingbu to have patches to turn the current cio2_bridge to
> > > > an ipu_bridge at some point. These should come on top of those patches.
> > >
> > > When will this be ready if you already make the plan? But could you
> > > please help review my other patches except bridge?
> > 
> > Please configure your e-mail client to wrap your lines at most at 74 characters
> > or so.
> > 
> > I think it's binding this all together that is the concern, the individual drivers
> > much less so.
> 
> Thanks, I would remove the csi bridge in next version.
> 
> Ok, the IPU bridge will also cover the bridge between IVSC and sensor.
> 
> BTW, please hans and laurent help take a look the mei_csi and mei_ace
> patch.

I can review them. However they can't be tested without the bridge code in
place. I'm therefore leaning towards merging them all at the same time,
when we know how this looks like in the end and can actually test the
patches.

-- 
Regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v3 2/3] media: pci: intel: ivsc: Add ACE submodule
  2023-03-27  6:23 ` [PATCH v3 2/3] media: pci: intel: ivsc: Add ACE submodule Wentong Wu
@ 2023-03-29  8:03   ` Sakari Ailus
  2023-03-30  7:32     ` Wu, Wentong
  0 siblings, 1 reply; 26+ messages in thread
From: Sakari Ailus @ 2023-03-29  8:03 UTC (permalink / raw)
  To: Wentong Wu
  Cc: hdegoede, djrscally, laurent.pinchart, linux-media, bingbu.cao,
	zhifeng.wang, xiang.ye, tian.shu.qiu

Hi Wentong,

On Mon, Mar 27, 2023 at 02:23:07PM +0800, Wentong Wu wrote:
> ACE is a submodule of IVSC which controls camera sensor's
> ownership, belonging to host or IVSC. When IVSC owns camera
> sensor, it is for algorithm computing. When host wants to
> control camera sensor, ACE module needs to be informed of
> ownership with defined interface.
> 
> The interface is via MEI. There is a separate MEI UUID, which
> this driver uses to enumerate.
> 
> To switch ownership of camera sensor between IVSC and host,
> the caller specifies the defined ownership information which
> will be sent to firmware by sending MEI command.
> 
> Device link(device_link_add) is used to set the right camera
> sensor ownership before accessing the sensor via I²C. With
> DL_FLAG_PM_RUNTIME and DL_FLAG_RPM_ACTIVE, the supplier device
> will be PM runtime resumed before the consumer(camera sensor).
> So use runtime PM callbacks to transfer the ownership between
> host and IVSC.
> 
> Signed-off-by: Wentong Wu <wentong.wu@intel.com>
> ---
>  drivers/media/pci/intel/ivsc/Makefile  |   3 +
>  drivers/media/pci/intel/ivsc/mei_ace.c | 534 +++++++++++++++++++++++++++++++++
>  2 files changed, 537 insertions(+)
>  create mode 100644 drivers/media/pci/intel/ivsc/mei_ace.c
> 
> diff --git a/drivers/media/pci/intel/ivsc/Makefile b/drivers/media/pci/intel/ivsc/Makefile
> index 7e4c5f0..a641f14 100644
> --- a/drivers/media/pci/intel/ivsc/Makefile
> +++ b/drivers/media/pci/intel/ivsc/Makefile
> @@ -5,3 +5,6 @@
>  obj-$(CONFIG_INTEL_VSC) += ivsc-csi.o
>  ivsc-csi-y += mei_csi.o
>  ivsc-csi-y += csi_bridge.o
> +
> +obj-$(CONFIG_INTEL_VSC) += ivsc-ace.o
> +ivsc-ace-y += mei_ace.o
> diff --git a/drivers/media/pci/intel/ivsc/mei_ace.c b/drivers/media/pci/intel/ivsc/mei_ace.c
> new file mode 100644
> index 0000000..434b072
> --- /dev/null
> +++ b/drivers/media/pci/intel/ivsc/mei_ace.c
> @@ -0,0 +1,534 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2023 Intel Corporation. All rights reserved.
> + * Intel Visual Sensing Controller ACE Linux driver
> + */
> +
> +/*
> + * To set ownership of camera sensor, there is specific command, which
> + * is sent via MEI protocol. That's a two-step scheme where the firmware
> + * first acks receipt of the command and later responses the command was
> + * executed. The command sending function uses "completion" as the
> + * synchronization mechanism. The notification for command is received
> + * via a mei callback which wakes up the caller. There can be only one
> + * outstanding command at a time.

Could you document the dependencies in the sensor initialisation (ace +
csi) and how the sensor is powered (i.e. no direct control of the PMIC from
the host?)? This doesn't seem like a bad place to do it.

> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/completion.h>
> +#include <linux/delay.h>
> +#include <linux/kernel.h>
> +#include <linux/mei_cl_bus.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/slab.h>
> +#include <linux/uuid.h>
> +
> +#define	MEI_ACE_DRIVER_NAME	"ivsc_ace"
> +
> +/* indicating driver message */
> +#define	ACE_DRV_MSG		1
> +/* indicating set command */
> +#define	ACE_CMD_SET		4
> +/* command timeout determined experimentally */
> +#define	ACE_CMD_TIMEOUT		(5 * HZ)
> +/* indicating the first command block */
> +#define	ACE_CMD_INIT_BLOCK	1
> +/* indicating the last command block */
> +#define	ACE_CMD_FINAL_BLOCK	1
> +/* size of camera status notification content */
> +#define	ACE_CAMERA_STATUS_SIZE	5
> +
> +/* UUID used to get firmware id */
> +#define ACE_GET_FW_ID_UUID UUID_LE(0x6167DCFB, 0x72F1, 0x4584, 0xBF, \
> +				   0xE3, 0x84, 0x17, 0x71, 0xAA, 0x79, 0x0B)
> +
> +/* UUID used to get csi device */
> +#define MEI_CSI_UUID UUID_LE(0x92335FCF, 0x3203, 0x4472, \
> +			     0xAF, 0x93, 0x7b, 0x44, 0x53, 0xAC, 0x29, 0xDA)
> +
> +/* identify firmware event type */
> +enum ace_event_type {
> +	/* firmware ready */
> +	ACE_FW_READY = 0x8,
> +
> +	/* command response */
> +	ACE_CMD_RESPONSE = 0x10,
> +};
> +
> +/* identify camera sensor ownership */
> +enum ace_camera_owner {
> +	ACE_CAMERA_IVSC,
> +	ACE_CAMERA_HOST,
> +};
> +
> +/* identify the command id supported by firmware IPC */
> +enum ace_cmd_id {
> +	/* used to switch camera sensor to host */
> +	ACE_SWITCH_CAMERA_TO_HOST = 0x13,
> +
> +	/* used to switch camera sensor to IVSC */
> +	ACE_SWITCH_CAMERA_TO_IVSC = 0x14,
> +
> +	/* used to get firmware id */
> +	ACE_GET_FW_ID = 0x1A,
> +};
> +
> +/* ACE command header structure */
> +struct ace_cmd_hdr {
> +	u32 firmware_id : 16;
> +	u32 instance_id : 8;
> +	u32 type : 5;
> +	u32 rsp : 1;
> +	u32 msg_tgt : 1;
> +	u32 _hw_rsvd_1 : 1;
> +	u32 param_size : 20;
> +	u32 cmd_id : 8;
> +	u32 final_block : 1;
> +	u32 init_block : 1;
> +	u32 _hw_rsvd_2 : 2;
> +} __packed;
> +
> +/* ACE command parameter structure */
> +union ace_cmd_param {
> +	uuid_le uuid;
> +	u32 param;
> +};
> +
> +/* ACE command structure */
> +struct ace_cmd {
> +	struct ace_cmd_hdr hdr;
> +	union ace_cmd_param param;
> +} __packed;
> +
> +/* ACE notification header */
> +union ace_notif_hdr {
> +	struct _confirm {
> +		u32 status : 24;
> +		u32 type : 5;
> +		u32 rsp : 1;
> +		u32 msg_tgt : 1;
> +		u32 _hw_rsvd_1 : 1;
> +		u32 param_size : 20;
> +		u32 cmd_id : 8;
> +		u32 final_block : 1;
> +		u32 init_block : 1;
> +		u32 _hw_rsvd_2 : 2;
> +	} __packed ack;
> +
> +	struct _event {
> +		u32 rsvd1 : 16;
> +		u32 event_type : 8;
> +		u32 type : 5;
> +		u32 ack : 1;
> +		u32 msg_tgt : 1;
> +		u32 _hw_rsvd_1 : 1;
> +		u32 rsvd2 : 30;
> +		u32 _hw_rsvd_2 : 2;
> +	} __packed event;
> +
> +	struct _response {
> +		u32 event_id : 16;
> +		u32 notif_type : 8;
> +		u32 type : 5;
> +		u32 rsp : 1;
> +		u32 msg_tgt : 1;
> +		u32 _hw_rsvd_1 : 1;
> +		u32 event_data_size : 16;
> +		u32 request_target : 1;
> +		u32 request_type : 5;
> +		u32 cmd_id : 8;
> +		u32 _hw_rsvd_2 : 2;
> +	} __packed response;
> +};
> +
> +/* ACE notification content */
> +union ace_notif_cont {
> +	u16 firmware_id;
> +	u8 state_notif;
> +	u8 camera_status[ACE_CAMERA_STATUS_SIZE];
> +};
> +
> +/* ACE notification structure */
> +struct ace_notif {
> +	union ace_notif_hdr hdr;
> +	union ace_notif_cont cont;
> +} __packed;
> +
> +struct mei_ace {
> +	struct mei_cl_device *cldev;
> +
> +	/* command ack */
> +	struct ace_notif cmd_ack;
> +	/* command response */
> +	struct ace_notif cmd_response;
> +	/* used to wait for command ack and response */
> +	struct completion cmd_completion;
> +	/* lock used to prevent multiple call to ace */
> +	struct mutex lock;
> +
> +	/* used to construct command */
> +	u16 firmware_id;
> +
> +	/* runtime PM link from ace to csi */
> +	struct device_link *csi_link;
> +	/* runtime PM link from ace to sensor */
> +	struct device_link *sensor_link;
> +};
> +
> +static inline void init_cmd_hdr(struct ace_cmd_hdr *hdr)
> +{
> +	memset(hdr, 0, sizeof(struct ace_cmd_hdr));
> +
> +	hdr->type = ACE_CMD_SET;
> +	hdr->msg_tgt = ACE_DRV_MSG;
> +	hdr->init_block = ACE_CMD_INIT_BLOCK;
> +	hdr->final_block = ACE_CMD_FINAL_BLOCK;
> +}
> +
> +static int construct_command(struct mei_ace *ace, struct ace_cmd *cmd,
> +			     enum ace_cmd_id cmd_id)
> +{
> +	union ace_cmd_param *param = &cmd->param;
> +	struct ace_cmd_hdr *hdr = &cmd->hdr;
> +
> +	init_cmd_hdr(hdr);
> +
> +	hdr->cmd_id = cmd_id;
> +	switch (cmd_id) {
> +	case ACE_GET_FW_ID:
> +		param->uuid = ACE_GET_FW_ID_UUID;
> +		hdr->param_size = sizeof(param->uuid);
> +		break;
> +	case ACE_SWITCH_CAMERA_TO_IVSC:
> +		param->param = 0;
> +		hdr->firmware_id = ace->firmware_id;
> +		hdr->param_size = sizeof(param->param);
> +		break;
> +	case ACE_SWITCH_CAMERA_TO_HOST:
> +		hdr->firmware_id = ace->firmware_id;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return hdr->param_size + sizeof(cmd->hdr);
> +}
> +
> +/* send a command to firmware and mutex must be held by caller */
> +static int mei_ace_send(struct mei_ace *ace, struct ace_cmd *cmd,
> +			size_t len, bool only_ack)
> +{
> +	union ace_notif_hdr *resp_hdr = &ace->cmd_response.hdr;
> +	union ace_notif_hdr *ack_hdr = &ace->cmd_ack.hdr;
> +	struct ace_cmd_hdr *cmd_hdr = &cmd->hdr;
> +	int ret;
> +
> +	reinit_completion(&ace->cmd_completion);
> +
> +	ret = mei_cldev_send(ace->cldev, (u8 *)cmd, len);
> +	if (ret < 0)
> +		goto out;
> +
> +	ret = wait_for_completion_killable_timeout(&ace->cmd_completion,
> +						   ACE_CMD_TIMEOUT);
> +	if (ret < 0) {
> +		goto out;
> +	} else if (!ret) {
> +		ret = -ETIMEDOUT;
> +		goto out;
> +	}
> +
> +	if (ack_hdr->ack.cmd_id != cmd_hdr->cmd_id) {
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	/* command ack status */
> +	ret = ack_hdr->ack.status;
> +	if (ret) {
> +		ret = -EIO;
> +		goto out;
> +	}
> +
> +	if (only_ack)
> +		goto out;
> +
> +	ret = wait_for_completion_killable_timeout(&ace->cmd_completion,
> +						   ACE_CMD_TIMEOUT);
> +	if (ret < 0) {
> +		goto out;
> +	} else if (!ret) {
> +		ret = -ETIMEDOUT;
> +		goto out;
> +	}
> +
> +	if (resp_hdr->response.cmd_id != cmd_hdr->cmd_id)
> +		ret = -EINVAL;
> +
> +out:
> +	return ret;
> +}
> +
> +static int ace_set_camera_owner(struct mei_ace *ace, enum ace_camera_owner owner)
> +{
> +	enum ace_cmd_id cmd_id;
> +	struct ace_cmd cmd;
> +	int cmd_size;
> +	int ret;
> +
> +	if (owner == ACE_CAMERA_IVSC)
> +		cmd_id = ACE_SWITCH_CAMERA_TO_IVSC;
> +	else
> +		cmd_id = ACE_SWITCH_CAMERA_TO_HOST;
> +
> +	mutex_lock(&ace->lock);
> +
> +	cmd_size = construct_command(ace, &cmd, cmd_id);

There doesn't seem to be a need to take the lock during
construct_command(). Maybe just during mei_ace_send()? You could also
acquire and release the lock there I guess.

> +	if (cmd_size >= 0)
> +		ret = mei_ace_send(ace, &cmd, cmd_size, false);
> +	else
> +		ret = cmd_size;
> +	mutex_unlock(&ace->lock);
> +
> +	return ret;
> +}
> +
> +/* the first command downloaded to firmware */
> +static inline int ace_get_firmware_id(struct mei_ace *ace)
> +{
> +	struct ace_cmd cmd;
> +	int cmd_size;
> +	int ret;
> +
> +	cmd_size = construct_command(ace, &cmd, ACE_GET_FW_ID);
> +	if (cmd_size >= 0)
> +		ret = mei_ace_send(ace, &cmd, cmd_size, true);
> +	else
> +		ret = cmd_size;
> +
> +	return ret;
> +}
> +
> +static void handle_command_response(struct mei_ace *ace,
> +				    struct ace_notif *resp, int len)
> +{
> +	union ace_notif_hdr *hdr = &resp->hdr;
> +
> +	switch (hdr->response.cmd_id) {
> +	case ACE_SWITCH_CAMERA_TO_IVSC:
> +	case ACE_SWITCH_CAMERA_TO_HOST:
> +		memcpy(&ace->cmd_response, resp, len);
> +		complete(&ace->cmd_completion);
> +		break;
> +	case ACE_GET_FW_ID:
> +		break;
> +	default:
> +		break;
> +	}
> +}
> +
> +static void handle_command_ack(struct mei_ace *ace,
> +			       struct ace_notif *ack, int len)
> +{
> +	union ace_notif_hdr *hdr = &ack->hdr;
> +
> +	switch (hdr->ack.cmd_id) {
> +	case ACE_GET_FW_ID:
> +		ace->firmware_id = ack->cont.firmware_id;
> +		fallthrough;
> +	case ACE_SWITCH_CAMERA_TO_IVSC:
> +	case ACE_SWITCH_CAMERA_TO_HOST:
> +		memcpy(&ace->cmd_ack, ack, len);
> +		complete(&ace->cmd_completion);
> +		break;
> +	default:
> +		break;
> +	}
> +}
> +
> +/* callback for receive */
> +static void mei_ace_rx(struct mei_cl_device *cldev)
> +{
> +	struct mei_ace *ace = mei_cldev_get_drvdata(cldev);
> +	struct ace_notif event;
> +	union ace_notif_hdr *hdr = &event.hdr;
> +	int ret;
> +
> +	ret = mei_cldev_recv(cldev, (u8 *)&event, sizeof(event));
> +	if (ret < 0) {
> +		dev_err(&cldev->dev, "recv error: %d\n", ret);
> +		return;
> +	}
> +
> +	if (hdr->event.ack) {
> +		handle_command_ack(ace, &event, ret);
> +		return;
> +	}
> +
> +	switch (hdr->event.event_type) {
> +	case ACE_CMD_RESPONSE:
> +		handle_command_response(ace, &event, ret);
> +		break;
> +	case ACE_FW_READY:
> +		/*
> +		 * firmware ready notification sent to driver
> +		 * after HECI client connected with firmware.
> +		 */
> +		dev_dbg(&cldev->dev, "firmware ready\n");
> +		break;
> +	default:
> +		break;
> +	}
> +}
> +
> +static int mei_ace_setup_dev_link(struct mei_ace *ace)
> +{
> +	struct device *dev = &ace->cldev->dev;
> +	uuid_le uuid = MEI_CSI_UUID;
> +	struct acpi_device *adev;
> +	struct device *csi_dev;
> +	char name[64];
> +
> +	sprintf(name, "%s-%pUl", dev_name(dev->parent), &uuid);

snprintf(), please.

> +	csi_dev = device_find_child_by_name(dev->parent, name);
> +	if (!csi_dev)
> +		return -EPROBE_DEFER;
> +
> +	/* sensor's ACPI _DEP is mei bus device */
> +	adev = acpi_dev_get_next_consumer_dev(ACPI_COMPANION(dev->parent), NULL);

Nice.

Please also run this on the patchset:

	./scripts/checkpatch.pl --strict --max-line-length=80

> +	if (!adev)
> +		return -EPROBE_DEFER;
> +
> +	/* setup link between mei_ace and mei_csi */
> +	ace->csi_link = device_link_add(csi_dev, dev,
> +					DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE);
> +
> +	/* setup link between mei_ace and sensor */
> +	ace->sensor_link = device_link_add(&adev->dev, dev,
> +					   DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE);

device_link_add() may fail.

> +
> +	return 0;
> +}
> +
> +static int mei_ace_probe(struct mei_cl_device *cldev,
> +			 const struct mei_cl_device_id *id)
> +{
> +	struct device *dev = &cldev->dev;
> +	struct mei_ace *ace;
> +	int ret;
> +
> +	ace = devm_kzalloc(dev, sizeof(struct mei_ace), GFP_KERNEL);
> +	if (!ace)
> +		return -ENOMEM;
> +
> +	ace->cldev = cldev;
> +
> +	ret = mei_ace_setup_dev_link(ace);
> +	if (ret)
> +		return ret;
> +
> +	mutex_init(&ace->lock);
> +	init_completion(&ace->cmd_completion);
> +
> +	mei_cldev_set_drvdata(cldev, ace);
> +
> +	ret = mei_cldev_enable(cldev);
> +	if (ret < 0) {
> +		dev_err(dev, "mei_cldev_enable failed: %d\n", ret);
> +		goto destroy_mutex;
> +	}
> +
> +	ret = mei_cldev_register_rx_cb(cldev, mei_ace_rx);
> +	if (ret) {
> +		dev_err(dev, "event cb registration failed: %d\n", ret);
> +		goto err_disable;
> +	}
> +
> +	ret = ace_get_firmware_id(ace);
> +	if (ret) {
> +		dev_err(dev, "get firmware id failed: %d\n", ret);
> +		goto err_disable;
> +	}
> +
> +	pm_runtime_set_active(dev);
> +	pm_runtime_enable(dev);
> +
> +	acpi_dev_clear_dependencies(ACPI_COMPANION(dev->parent));
> +
> +	return 0;
> +
> +err_disable:
> +	mei_cldev_disable(cldev);
> +destroy_mutex:
> +	mutex_destroy(&ace->lock);
> +
> +	device_link_del(ace->csi_link);
> +	device_link_del(ace->sensor_link);
> +
> +	return ret;
> +}
> +
> +static void mei_ace_remove(struct mei_cl_device *cldev)
> +{
> +	struct mei_ace *ace = mei_cldev_get_drvdata(cldev);
> +
> +	device_link_del(ace->csi_link);
> +	device_link_del(ace->sensor_link);
> +
> +	pm_runtime_disable(&cldev->dev);
> +	pm_runtime_set_suspended(&cldev->dev);
> +
> +	mutex_destroy(&ace->lock);
> +}
> +
> +static int __maybe_unused mei_ace_runtime_suspend(struct device *dev)
> +{
> +	struct mei_ace *ace = dev_get_drvdata(dev);
> +
> +	return ace_set_camera_owner(ace, ACE_SWITCH_CAMERA_TO_IVSC);
> +}
> +
> +static int __maybe_unused mei_ace_runtime_resume(struct device *dev)
> +{
> +	struct mei_ace *ace = dev_get_drvdata(dev);
> +
> +	return ace_set_camera_owner(ace, ACE_SWITCH_CAMERA_TO_HOST);
> +}
> +
> +static const struct dev_pm_ops mei_ace_pm_ops = {
> +	SET_RUNTIME_PM_OPS(mei_ace_runtime_suspend,
> +			   mei_ace_runtime_resume,
> +			   NULL)
> +};
> +
> +#define MEI_ACE_UUID UUID_LE(0x5DB76CF6, 0x0A68, 0x4ED6, \
> +			     0x9B, 0x78, 0x03, 0x61, 0x63, 0x5E, 0x24, 0x47)
> +
> +static const struct mei_cl_device_id mei_ace_tbl[] = {
> +	{ MEI_ACE_DRIVER_NAME, MEI_ACE_UUID, MEI_CL_VERSION_ANY },
> +
> +	/* required last entry */
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(mei, mei_ace_tbl);
> +
> +static struct mei_cl_driver mei_ace_driver = {
> +	.id_table = mei_ace_tbl,
> +	.name = MEI_ACE_DRIVER_NAME,
> +
> +	.probe = mei_ace_probe,
> +	.remove = mei_ace_remove,
> +
> +	.driver = {
> +		.pm = &mei_ace_pm_ops,
> +	},
> +};
> +
> +module_mei_cl_driver(mei_ace_driver);
> +
> +MODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>");
> +MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>");
> +MODULE_DESCRIPTION("Device driver for IVSC ACE");
> +MODULE_LICENSE("GPL");

-- 
Kind regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v3 1/3] media: pci: intel: ivsc: Add CSI submodule
  2023-03-27  6:23 ` [PATCH v3 1/3] media: pci: intel: ivsc: Add CSI submodule Wentong Wu
  2023-03-27 10:01   ` kernel test robot
  2023-03-28 13:59   ` kernel test robot
@ 2023-03-29  8:48   ` Sakari Ailus
  2023-03-30  3:58     ` Wu, Wentong
  2023-03-29  8:49   ` Sakari Ailus
  3 siblings, 1 reply; 26+ messages in thread
From: Sakari Ailus @ 2023-03-29  8:48 UTC (permalink / raw)
  To: Wentong Wu
  Cc: hdegoede, djrscally, laurent.pinchart, linux-media, bingbu.cao,
	zhifeng.wang, xiang.ye, tian.shu.qiu

Hi Wentong,

On Mon, Mar 27, 2023 at 02:23:06PM +0800, Wentong Wu wrote:
> CSI is a submodule of IVSC which can route camera sensor data
> to the outbound MIPI CSI-2 interface.
> 
> The interface communicating with firmware is via MEI. There is
> a separate MEI UUID, which this driver uses to enumerate.
> 
> To route camera sensor data to host, the information of link
> frequency and number of data lanes is sent to firmware by
> sending MEI command when starting stream.
> 
> CSI also provides a privacy mode. When privacy mode is turned
> on, camera sensor can't be used. This means that both IVSC and
> host Image Processing Unit(IPU) can't get image data. And when
> this mode is turned on, host Image Processing Unit(IPU) driver
> is informed via v4l2 control callback, so that user can be
> notified.
> 
> Signed-off-by: Wentong Wu <wentong.wu@intel.com>
> ---
>  drivers/media/pci/Kconfig                 |   1 +
>  drivers/media/pci/intel/Makefile          |   2 +
>  drivers/media/pci/intel/ivsc/Kconfig      |  12 +
>  drivers/media/pci/intel/ivsc/Makefile     |   7 +
>  drivers/media/pci/intel/ivsc/csi_bridge.c | 332 +++++++++++++
>  drivers/media/pci/intel/ivsc/csi_bridge.h | 122 +++++
>  drivers/media/pci/intel/ivsc/mei_csi.c    | 775 ++++++++++++++++++++++++++++++
>  7 files changed, 1251 insertions(+)
>  create mode 100644 drivers/media/pci/intel/ivsc/Kconfig
>  create mode 100644 drivers/media/pci/intel/ivsc/Makefile
>  create mode 100644 drivers/media/pci/intel/ivsc/csi_bridge.c
>  create mode 100644 drivers/media/pci/intel/ivsc/csi_bridge.h
>  create mode 100644 drivers/media/pci/intel/ivsc/mei_csi.c

I'm commenting just mei_csi this time...

> diff --git a/drivers/media/pci/intel/ivsc/mei_csi.c b/drivers/media/pci/intel/ivsc/mei_csi.c
> new file mode 100644
> index 0000000..6a01537
> --- /dev/null
> +++ b/drivers/media/pci/intel/ivsc/mei_csi.c
> @@ -0,0 +1,775 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2023 Intel Corporation. All rights reserved.
> + * Intel Visual Sensing Controller CSI Linux driver
> + */
> +
> +/*
> + * To set ownership of CSI-2 link and to configure CSI-2 link, there
> + * are specific commands, which are sent via MEI protocol. The send
> + * command function uses "completion" as a synchronization mechanism.
> + * The response for command is received via a mei callback which wakes
> + * up the caller. There can be only one outstanding command at a time.
> + */
> +
> +#include <linux/completion.h>
> +#include <linux/delay.h>
> +#include <linux/kernel.h>
> +#include <linux/math64.h>
> +#include <linux/mei_cl_bus.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/slab.h>
> +#include <linux/units.h>
> +#include <linux/uuid.h>
> +#include <linux/workqueue.h>
> +
> +#include <media/v4l2-async.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-fwnode.h>
> +#include <media/v4l2-subdev.h>
> +
> +#define MEI_CSI_DRIVER_NAME "ivsc_csi"
> +
> +/* the 5s used here is based on experiment */
> +#define CSI_CMD_TIMEOUT (5 * HZ)
> +/* to setup CSI-2 link an extra delay needed and determined experimentally */
> +#define CSI_FW_READY_DELAY_MS 100
> +/* link frequency unit is 100kHz */
> +#define CSI_LINK_FREQ(x) ((u32)(div_u64(x, 100 * HZ_PER_KHZ)))
> +
> +/*
> + * identify the command id supported by firmware
> + * IPC, as well as the privacy notification id
> + * used when processing privacy event.
> + */
> +enum csi_cmd_id {
> +	/* used to set csi ownership */
> +	CSI_SET_OWNER = 0,
> +
> +	/* used to configure CSI-2 link */
> +	CSI_SET_CONF = 2,
> +
> +	/* privacy notification id used when privacy state changes */
> +	CSI_PRIVACY_NOTIF = 6,
> +};
> +
> +/* CSI-2 link ownership definition */
> +enum csi_link_owner {
> +	CSI_LINK_IVSC,
> +	CSI_LINK_HOST,
> +};
> +
> +/* privacy status definition */
> +enum ivsc_privacy_status {
> +	CSI_PRIVACY_OFF,
> +	CSI_PRIVACY_ON,
> +	CSI_PRIVACY_MAX,
> +};
> +
> +enum csi_pads {
> +	CSI_PAD_SOURCE,
> +	CSI_PAD_SINK,
> +	CSI_NUM_PADS
> +};
> +
> +/* configuration of the CSI-2 link between host and IVSC */
> +struct csi_link_cfg {
> +	/* number of data lanes used on the CSI-2 link */
> +	u32 nr_of_lanes;
> +
> +	/* frequency of the CSI-2 link */
> +	u32 link_freq;
> +
> +	/* for future use */
> +	u32 rsvd[2];
> +} __packed;
> +
> +/* CSI command structure */
> +struct csi_cmd {
> +	u32 cmd_id;
> +	union _cmd_param {
> +		u32 param;
> +		struct csi_link_cfg conf;
> +	} param;
> +} __packed;
> +
> +/* CSI notification structure */
> +struct csi_notif {
> +	u32 cmd_id;
> +	int status;
> +	union _resp_cont {
> +		u32 cont;
> +		struct csi_link_cfg conf;
> +	} cont;
> +} __packed;
> +
> +struct mei_csi {
> +	struct mei_cl_device *cldev;
> +
> +	/* command response */
> +	struct csi_notif cmd_response;
> +	/* used to wait for command response from firmware */
> +	struct completion cmd_completion;
> +	/* protect command download */
> +	struct mutex lock;
> +
> +	struct v4l2_subdev subdev;
> +	struct v4l2_subdev *remote;
> +	struct v4l2_async_notifier notifier;
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	struct v4l2_ctrl *privacy_ctrl;
> +	unsigned int remote_pad;
> +	/* start streaming or not */
> +	int streaming;
> +
> +	struct media_pad pads[CSI_NUM_PADS];
> +	struct v4l2_mbus_framefmt format_mbus[CSI_NUM_PADS];
> +
> +	/* number of data lanes used on the CSI-2 link */
> +	u32 nr_of_lanes;
> +	/* frequency of the CSI-2 link */
> +	u64 link_freq;
> +
> +	/* privacy status */
> +	enum ivsc_privacy_status status;
> +};
> +
> +static const struct v4l2_mbus_framefmt mei_csi_format_mbus_default = {
> +	.width = 1,
> +	.height = 1,
> +	.code = MEDIA_BUS_FMT_Y8_1X8,
> +	.field = V4L2_FIELD_NONE,
> +};
> +
> +int csi_bridge_init(struct mei_cl_device *csi_dev);
> +
> +static inline struct mei_csi *notifier_to_csi(struct v4l2_async_notifier *n)
> +{
> +	return container_of(n, struct mei_csi, notifier);
> +}
> +
> +static inline struct mei_csi *sd_to_csi(struct v4l2_subdev *sd)
> +{
> +	return container_of(sd, struct mei_csi, subdev);
> +}
> +
> +/* send a command to firmware and mutex must be held by caller */
> +static int mei_csi_send(struct mei_csi *csi, u8 *buf, size_t len)
> +{
> +	struct csi_cmd *cmd = (struct csi_cmd *)buf;
> +	int ret;
> +
> +	reinit_completion(&csi->cmd_completion);
> +
> +	ret = mei_cldev_send(csi->cldev, buf, len);
> +	if (ret < 0)
> +		goto out;
> +
> +	ret = wait_for_completion_killable_timeout(&csi->cmd_completion,
> +						   CSI_CMD_TIMEOUT);
> +	if (ret < 0) {
> +		goto out;
> +	} else if (!ret) {
> +		ret = -ETIMEDOUT;
> +		goto out;
> +	}
> +
> +	/* command response status */
> +	ret = csi->cmd_response.status;
> +	if (ret) {
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	if (csi->cmd_response.cmd_id != cmd->cmd_id)
> +		ret = -EINVAL;
> +
> +out:
> +	return ret;
> +}
> +
> +/* set CSI-2 link ownership */
> +static int csi_set_link_owner(struct mei_csi *csi, enum csi_link_owner owner)
> +{
> +	struct csi_cmd cmd = { 0 };
> +	size_t cmd_size;
> +	int ret;
> +
> +	cmd.cmd_id = CSI_SET_OWNER;
> +	cmd.param.param = owner;
> +	cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.param);

In some cases you're using memset and in others not. If you don't need
memset, I'd prefer assigning the fields in variable declaration instead.

> +
> +	mutex_lock(&csi->lock);
> +
> +	ret = mei_csi_send(csi, (u8 *)&cmd, cmd_size);
> +
> +	mutex_unlock(&csi->lock);
> +
> +	return ret;
> +}
> +
> +/* configure CSI-2 link between host and IVSC */
> +static int csi_set_link_cfg(struct mei_csi *csi)
> +{
> +	struct csi_cmd cmd = { 0 };
> +	size_t cmd_size;
> +	int ret;
> +
> +	cmd.cmd_id = CSI_SET_CONF;
> +	cmd.param.conf.nr_of_lanes = csi->nr_of_lanes;
> +	cmd.param.conf.link_freq = CSI_LINK_FREQ(csi->link_freq);
> +	cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.conf);

Ditto.

> +
> +	mutex_lock(&csi->lock);
> +
> +	ret = mei_csi_send(csi, (u8 *)&cmd, cmd_size);
> +	/*
> +	 * wait configuration ready if download success. placing
> +	 * delay under mutex is to make sure current command flow
> +	 * completed before starting a possible new one.
> +	 */
> +	if (!ret)
> +		msleep(CSI_FW_READY_DELAY_MS);
> +
> +	mutex_unlock(&csi->lock);
> +
> +	return ret;
> +}
> +
> +/* callback for receive */
> +static void mei_csi_rx(struct mei_cl_device *cldev)
> +{
> +	struct mei_csi *csi = mei_cldev_get_drvdata(cldev);
> +	struct csi_notif notif = { 0 };
> +	int ret;
> +
> +	ret = mei_cldev_recv(cldev, (u8 *)&notif, sizeof(notif));
> +	if (ret < 0) {
> +		dev_err(&cldev->dev, "recv error: %d\n", ret);
> +		return;
> +	}
> +
> +	switch (notif.cmd_id) {
> +	case CSI_PRIVACY_NOTIF:
> +		if (notif.cont.cont < CSI_PRIVACY_MAX) {
> +			csi->status = notif.cont.cont;
> +			v4l2_ctrl_s_ctrl(csi->privacy_ctrl, csi->status);
> +		}
> +		break;
> +	case CSI_SET_OWNER:
> +	case CSI_SET_CONF:
> +		memcpy(&csi->cmd_response, &notif, ret);
> +
> +		complete(&csi->cmd_completion);
> +		break;
> +	default:
> +		break;
> +	}
> +}
> +
> +static int mei_csi_pre_streamon(struct v4l2_subdev *sd, u32 flags)
> +{
> +	struct v4l2_querymenu qm = { .id = V4L2_CID_LINK_FREQ, };
> +	struct v4l2_mbus_config mbus_config = { 0 };
> +	struct mei_csi *csi = sd_to_csi(sd);
> +	struct v4l2_ctrl *ctrl;
> +	int ret = 0;
> +
> +	if (!csi->remote)
> +		return -ENODEV;
> +
> +	ret = v4l2_subdev_call(csi->remote, pad, get_mbus_config,
> +			       csi->remote_pad, &mbus_config);
> +	if (ret)
> +		return ret;

You're already parsing the endpoint in probe(). Do you need this, assuming
the sensor has a static lane configuration? Virtually all do.

Could this all be done via the s_stream() op instead?

> +
> +	if (mbus_config.type != V4L2_MBUS_CSI2_DPHY)
> +		return -EINVAL;
> +
> +	csi->nr_of_lanes = mbus_config.bus.mipi_csi2.num_data_lanes;
> +
> +	ctrl = v4l2_ctrl_find(csi->remote->ctrl_handler, V4L2_CID_LINK_FREQ);
> +	if (!ctrl)
> +		return -EINVAL;
> +	qm.index = v4l2_ctrl_g_ctrl(ctrl);
> +
> +	ret = v4l2_querymenu(csi->remote->ctrl_handler, &qm);
> +	if (ret)
> +		return ret;
> +
> +	csi->link_freq = qm.value;
> +
> +	return ret;
> +}
> +
> +static int mei_csi_set_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct mei_csi *csi = sd_to_csi(sd);
> +	int ret = 0;
> +
> +	if (enable && csi->streaming == 0) {
> +		/* switch CSI-2 link to host */
> +		ret = csi_set_link_owner(csi, CSI_LINK_HOST);
> +		if (ret < 0)
> +			goto err;
> +
> +		/* configure CSI-2 link */
> +		ret = csi_set_link_cfg(csi);
> +		if (ret < 0)
> +			goto err;
> +
> +		ret = v4l2_subdev_call(csi->remote, video, s_stream, 1);
> +		if (ret)
> +			goto err;
> +	} else if (!enable && csi->streaming == 1) {
> +		v4l2_subdev_call(csi->remote, video, s_stream, 0);
> +
> +		/* switch CSI-2 link to IVSC */
> +		ret = csi_set_link_owner(csi, CSI_LINK_IVSC);
> +		if (ret < 0)
> +			goto err;

I'd complain (e.g. dev_warn()) but return zero in this case. There's not
much if anything the caller can do with this.

> +	}
> +
> +	csi->streaming = enable;
> +
> +err:
> +	return ret;
> +}
> +
> +static struct v4l2_mbus_framefmt *
> +mei_csi_get_pad_format(struct v4l2_subdev *sd,
> +		       struct v4l2_subdev_state *sd_state,
> +		       unsigned int pad, u32 which)
> +{
> +	struct mei_csi *csi = sd_to_csi(sd);
> +
> +	switch (which) {
> +	case V4L2_SUBDEV_FORMAT_TRY:
> +		return v4l2_subdev_get_try_format(sd, sd_state, pad);
> +	case V4L2_SUBDEV_FORMAT_ACTIVE:
> +		return &csi->format_mbus[pad];
> +	default:
> +		return NULL;
> +	}
> +}
> +
> +static int mei_csi_init_cfg(struct v4l2_subdev *sd,
> +			    struct v4l2_subdev_state *sd_state)
> +{
> +	struct v4l2_mbus_framefmt *mbusformat;
> +	struct mei_csi *csi = sd_to_csi(sd);
> +	unsigned int i;
> +
> +	mutex_lock(&csi->lock);
> +
> +	for (i = 0; i < sd->entity.num_pads; i++) {
> +		mbusformat = v4l2_subdev_get_try_format(sd, sd_state, i);
> +		*mbusformat = mei_csi_format_mbus_default;
> +	}
> +
> +	mutex_unlock(&csi->lock);
> +
> +	return 0;
> +}
> +
> +static int mei_csi_get_fmt(struct v4l2_subdev *sd,
> +			   struct v4l2_subdev_state *sd_state,
> +			   struct v4l2_subdev_format *format)
> +{
> +	struct v4l2_mbus_framefmt *mbusformat;
> +	struct mei_csi *csi = sd_to_csi(sd);
> +
> +	mutex_lock(&csi->lock);
> +
> +	mbusformat = mei_csi_get_pad_format(sd, sd_state, format->pad,
> +					    format->which);
> +	if (mbusformat)
> +		format->format = *mbusformat;
> +
> +	mutex_unlock(&csi->lock);
> +
> +	return 0;
> +}

It'd be nice to have also enum_mbus_code support. Video mux of course
didn't have this either. Something that could be done in the framework,
including validating the mbus format.

> +
> +static int mei_csi_set_fmt(struct v4l2_subdev *sd,
> +			   struct v4l2_subdev_state *sd_state,
> +			   struct v4l2_subdev_format *format)
> +{
> +	struct v4l2_mbus_framefmt *source_mbusformat;
> +	struct v4l2_mbus_framefmt *mbusformat;
> +	struct mei_csi *csi = sd_to_csi(sd);
> +	struct media_pad *pad;
> +
> +	mbusformat = mei_csi_get_pad_format(sd, sd_state, format->pad,
> +					    format->which);
> +	if (!mbusformat)
> +		return -EINVAL;
> +
> +	source_mbusformat = mei_csi_get_pad_format(sd, sd_state,
> +						   CSI_PAD_SOURCE,
> +						   format->which);
> +	if (!source_mbusformat)
> +		return -EINVAL;
> +
> +	v4l_bound_align_image(&format->format.width, 1, 65536, 0,
> +			      &format->format.height, 1, 65536, 0, 0);
> +
> +	switch (format->format.code) {
> +	case MEDIA_BUS_FMT_RGB444_1X12:
> +	case MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE:
> +	case MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE:
> +	case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE:
> +	case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE:
> +	case MEDIA_BUS_FMT_RGB565_1X16:
> +	case MEDIA_BUS_FMT_BGR565_2X8_BE:
> +	case MEDIA_BUS_FMT_BGR565_2X8_LE:
> +	case MEDIA_BUS_FMT_RGB565_2X8_BE:
> +	case MEDIA_BUS_FMT_RGB565_2X8_LE:
> +	case MEDIA_BUS_FMT_RGB666_1X18:
> +	case MEDIA_BUS_FMT_RBG888_1X24:
> +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> +	case MEDIA_BUS_FMT_BGR888_1X24:
> +	case MEDIA_BUS_FMT_GBR888_1X24:
> +	case MEDIA_BUS_FMT_RGB888_1X24:
> +	case MEDIA_BUS_FMT_RGB888_2X12_BE:
> +	case MEDIA_BUS_FMT_RGB888_2X12_LE:
> +	case MEDIA_BUS_FMT_ARGB8888_1X32:
> +	case MEDIA_BUS_FMT_RGB888_1X32_PADHI:
> +	case MEDIA_BUS_FMT_RGB101010_1X30:
> +	case MEDIA_BUS_FMT_RGB121212_1X36:
> +	case MEDIA_BUS_FMT_RGB161616_1X48:
> +	case MEDIA_BUS_FMT_Y8_1X8:
> +	case MEDIA_BUS_FMT_UV8_1X8:
> +	case MEDIA_BUS_FMT_UYVY8_1_5X8:
> +	case MEDIA_BUS_FMT_VYUY8_1_5X8:
> +	case MEDIA_BUS_FMT_YUYV8_1_5X8:
> +	case MEDIA_BUS_FMT_YVYU8_1_5X8:
> +	case MEDIA_BUS_FMT_UYVY8_2X8:
> +	case MEDIA_BUS_FMT_VYUY8_2X8:
> +	case MEDIA_BUS_FMT_YUYV8_2X8:
> +	case MEDIA_BUS_FMT_YVYU8_2X8:
> +	case MEDIA_BUS_FMT_Y10_1X10:
> +	case MEDIA_BUS_FMT_UYVY10_2X10:
> +	case MEDIA_BUS_FMT_VYUY10_2X10:
> +	case MEDIA_BUS_FMT_YUYV10_2X10:
> +	case MEDIA_BUS_FMT_YVYU10_2X10:
> +	case MEDIA_BUS_FMT_Y12_1X12:
> +	case MEDIA_BUS_FMT_UYVY12_2X12:
> +	case MEDIA_BUS_FMT_VYUY12_2X12:
> +	case MEDIA_BUS_FMT_YUYV12_2X12:
> +	case MEDIA_BUS_FMT_YVYU12_2X12:
> +	case MEDIA_BUS_FMT_UYVY8_1X16:
> +	case MEDIA_BUS_FMT_VYUY8_1X16:
> +	case MEDIA_BUS_FMT_YUYV8_1X16:
> +	case MEDIA_BUS_FMT_YVYU8_1X16:
> +	case MEDIA_BUS_FMT_YDYUYDYV8_1X16:
> +	case MEDIA_BUS_FMT_UYVY10_1X20:
> +	case MEDIA_BUS_FMT_VYUY10_1X20:
> +	case MEDIA_BUS_FMT_YUYV10_1X20:
> +	case MEDIA_BUS_FMT_YVYU10_1X20:
> +	case MEDIA_BUS_FMT_VUY8_1X24:
> +	case MEDIA_BUS_FMT_YUV8_1X24:
> +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> +	case MEDIA_BUS_FMT_UYVY12_1X24:
> +	case MEDIA_BUS_FMT_VYUY12_1X24:
> +	case MEDIA_BUS_FMT_YUYV12_1X24:
> +	case MEDIA_BUS_FMT_YVYU12_1X24:
> +	case MEDIA_BUS_FMT_YUV10_1X30:
> +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> +	case MEDIA_BUS_FMT_AYUV8_1X32:
> +	case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
> +	case MEDIA_BUS_FMT_YUV12_1X36:
> +	case MEDIA_BUS_FMT_YUV16_1X48:
> +	case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
> +	case MEDIA_BUS_FMT_JPEG_1X8:
> +	case MEDIA_BUS_FMT_AHSV8888_1X32:
> +	case MEDIA_BUS_FMT_SBGGR8_1X8:
> +	case MEDIA_BUS_FMT_SGBRG8_1X8:
> +	case MEDIA_BUS_FMT_SGRBG8_1X8:
> +	case MEDIA_BUS_FMT_SRGGB8_1X8:
> +	case MEDIA_BUS_FMT_SBGGR10_1X10:
> +	case MEDIA_BUS_FMT_SGBRG10_1X10:
> +	case MEDIA_BUS_FMT_SGRBG10_1X10:
> +	case MEDIA_BUS_FMT_SRGGB10_1X10:
> +	case MEDIA_BUS_FMT_SBGGR12_1X12:
> +	case MEDIA_BUS_FMT_SGBRG12_1X12:
> +	case MEDIA_BUS_FMT_SGRBG12_1X12:
> +	case MEDIA_BUS_FMT_SRGGB12_1X12:
> +	case MEDIA_BUS_FMT_SBGGR14_1X14:
> +	case MEDIA_BUS_FMT_SGBRG14_1X14:
> +	case MEDIA_BUS_FMT_SGRBG14_1X14:
> +	case MEDIA_BUS_FMT_SRGGB14_1X14:
> +	case MEDIA_BUS_FMT_SBGGR16_1X16:
> +	case MEDIA_BUS_FMT_SGBRG16_1X16:
> +	case MEDIA_BUS_FMT_SGRBG16_1X16:
> +	case MEDIA_BUS_FMT_SRGGB16_1X16:
> +		break;
> +	default:
> +		format->format.code = MEDIA_BUS_FMT_Y8_1X8;
> +		break;
> +	}
> +	if (format->format.field == V4L2_FIELD_ANY)
> +		format->format.field = V4L2_FIELD_NONE;
> +
> +	mutex_lock(&csi->lock);
> +
> +	pad = &csi->pads[format->pad];
> +	if (pad->flags & MEDIA_PAD_FL_SOURCE)
> +		format->format = csi->format_mbus[CSI_PAD_SINK];
> +
> +	*mbusformat = format->format;
> +
> +	if (pad->flags & MEDIA_PAD_FL_SINK)
> +		*source_mbusformat = format->format;
> +
> +	mutex_unlock(&csi->lock);
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_subdev_video_ops mei_csi_video_ops = {
> +	.s_stream = mei_csi_set_stream,
> +	.pre_streamon = mei_csi_pre_streamon,
> +};
> +
> +static const struct v4l2_subdev_pad_ops mei_csi_pad_ops = {
> +	.init_cfg = mei_csi_init_cfg,
> +	.get_fmt = mei_csi_get_fmt,
> +	.set_fmt = mei_csi_set_fmt,
> +};
> +
> +static const struct v4l2_subdev_ops mei_csi_subdev_ops = {
> +	.video = &mei_csi_video_ops,
> +	.pad = &mei_csi_pad_ops,
> +};
> +
> +static const struct media_entity_operations mei_csi_entity_ops = {
> +	.link_validate = v4l2_subdev_link_validate,
> +};
> +
> +static int mei_csi_notify_bound(struct v4l2_async_notifier *notifier,
> +				struct v4l2_subdev *subdev,
> +				struct v4l2_async_subdev *asd)
> +{
> +	struct mei_csi *csi = notifier_to_csi(notifier);
> +	int pad;
> +
> +	pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode,
> +					  MEDIA_PAD_FL_SOURCE);
> +	if (pad < 0)
> +		return pad;
> +
> +	csi->remote = subdev;
> +	csi->remote_pad = pad;
> +
> +	return media_create_pad_link(&subdev->entity, pad,
> +				     &csi->subdev.entity, 0,
> +				     MEDIA_LNK_FL_ENABLED |
> +				     MEDIA_LNK_FL_IMMUTABLE);
> +}
> +
> +static void mei_csi_notify_unbind(struct v4l2_async_notifier *notifier,
> +				  struct v4l2_subdev *subdev,
> +				  struct v4l2_async_subdev *asd)
> +{
> +	struct mei_csi *csi = notifier_to_csi(notifier);
> +
> +	csi->remote = NULL;
> +}
> +
> +static const struct v4l2_async_notifier_operations mei_csi_notify_ops = {
> +	.bound = mei_csi_notify_bound,
> +	.unbind = mei_csi_notify_unbind,
> +};
> +
> +static int mei_csi_init_control(struct mei_csi *csi)
> +{
> +	v4l2_ctrl_handler_init(&csi->ctrl_handler, 1);
> +	csi->ctrl_handler.lock = &csi->lock;
> +
> +	csi->privacy_ctrl = v4l2_ctrl_new_std(&csi->ctrl_handler, NULL,
> +					      V4L2_CID_PRIVACY, 0, 1, 1, 0);
> +	if (csi->ctrl_handler.error)
> +		return csi->ctrl_handler.error;

The control should be made volatile here (flags |=
V4L2_CTRL_FLAG_READ_ONLY) as it can't be changed by the user.

> +
> +	csi->subdev.ctrl_handler = &csi->ctrl_handler;
> +
> +	return 0;
> +}
> +
> +static int mei_csi_parse_firmware(struct mei_csi *csi)
> +{
> +	struct v4l2_fwnode_endpoint v4l2_ep = {
> +		.bus_type = V4L2_MBUS_CSI2_DPHY,
> +	};
> +	struct device *dev = &csi->cldev->dev;
> +	struct v4l2_async_subdev *asd;
> +	struct fwnode_handle *fwnode;
> +	struct fwnode_handle *ep;
> +	int ret;
> +
> +	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0);
> +	if (!ep) {
> +		dev_err(dev, "not connected to subdevice\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = v4l2_fwnode_endpoint_parse(ep, &v4l2_ep);
> +	if (ret) {
> +		dev_err(dev, "could not parse v4l2 endpoint\n");
> +		fwnode_handle_put(ep);
> +		return -EINVAL;
> +	}
> +
> +	fwnode = fwnode_graph_get_remote_endpoint(ep);
> +	fwnode_handle_put(ep);
> +
> +	v4l2_async_nf_init(&csi->notifier);
> +	csi->notifier.ops = &mei_csi_notify_ops;
> +
> +	asd = v4l2_async_nf_add_fwnode(&csi->notifier, fwnode,
> +				       struct v4l2_async_subdev);
> +	fwnode_handle_put(fwnode);
> +	if (IS_ERR(asd))
> +		return PTR_ERR(asd);
> +
> +	ret = v4l2_async_subdev_nf_register(&csi->subdev, &csi->notifier);
> +	if (ret)
> +		v4l2_async_nf_cleanup(&csi->notifier);
> +
> +	return ret;
> +}
> +
> +static int mei_csi_probe(struct mei_cl_device *cldev,
> +			 const struct mei_cl_device_id *id)
> +{
> +	struct mei_csi *csi;
> +	int ret;
> +
> +	/*
> +	 * no connections to sensor are defined in firmware, try to
> +	 * build connections as software_nodes parsed from SSDB.
> +	 */
> +	ret = csi_bridge_init(cldev);
> +	if (ret)
> +		return ret;
> +
> +	csi = devm_kzalloc(&cldev->dev, sizeof(struct mei_csi), GFP_KERNEL);
> +	if (!csi)
> +		return -ENOMEM;
> +
> +	csi->cldev = cldev;
> +	mutex_init(&csi->lock);
> +	init_completion(&csi->cmd_completion);
> +
> +	mei_cldev_set_drvdata(cldev, csi);
> +
> +	ret = mei_cldev_enable(cldev);
> +	if (ret < 0) {
> +		dev_err(&cldev->dev, "mei_cldev_enable failed: %d\n", ret);
> +		goto destroy_mutex;
> +	}
> +
> +	ret = mei_cldev_register_rx_cb(cldev, mei_csi_rx);
> +	if (ret) {
> +		dev_err(&cldev->dev, "event cb registration failed: %d\n", ret);
> +		goto err_disable;
> +	}
> +
> +	ret = mei_csi_parse_firmware(csi);
> +	if (ret)
> +		goto err_disable;
> +
> +	csi->subdev.dev = &cldev->dev;
> +	v4l2_subdev_init(&csi->subdev, &mei_csi_subdev_ops);
> +	v4l2_set_subdevdata(&csi->subdev, csi);
> +	csi->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
> +	csi->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
> +	csi->subdev.entity.ops = &mei_csi_entity_ops;
> +
> +	ret = mei_csi_init_control(csi);
> +	if (ret)
> +		goto err_async;

You'll also need to call v4l2_ctrl_handler_free() if setting up the handler
failed. I.e. just change the label here and remove err_async label below.

> +
> +	csi->format_mbus[CSI_PAD_SOURCE] = mei_csi_format_mbus_default;
> +	csi->format_mbus[CSI_PAD_SINK] = mei_csi_format_mbus_default;
> +
> +	csi->pads[CSI_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
> +	csi->pads[CSI_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
> +	ret = media_entity_pads_init(&csi->subdev.entity, CSI_NUM_PADS,
> +				     csi->pads);
> +	if (ret)
> +		goto err_ctrl_handler;
> +
> +	ret = v4l2_subdev_init_finalize(&csi->subdev);
> +	if (ret < 0)
> +		goto err_entity;
> +
> +	ret = v4l2_async_register_subdev(&csi->subdev);
> +	if (ret < 0)
> +		goto err_subdev;
> +
> +	pm_runtime_enable(&cldev->dev);
> +
> +	return 0;
> +
> +err_subdev:
> +	v4l2_subdev_cleanup(&csi->subdev);
> +err_entity:
> +	media_entity_cleanup(&csi->subdev.entity);
> +err_ctrl_handler:
> +	v4l2_ctrl_handler_free(&csi->ctrl_handler);
> +err_async:
> +	v4l2_async_nf_unregister(&csi->notifier);
> +	v4l2_async_nf_cleanup(&csi->notifier);
> +err_disable:
> +	mei_cldev_disable(cldev);

It'd be nice to have an additional newline before the label. Elsewhere,
too.

> +destroy_mutex:
> +	mutex_destroy(&csi->lock);
> +
> +	return ret;
> +}
> +
> +static void mei_csi_remove(struct mei_cl_device *cldev)
> +{
> +	struct mei_csi *csi = mei_cldev_get_drvdata(cldev);
> +
> +	v4l2_async_nf_unregister(&csi->notifier);
> +	v4l2_async_nf_cleanup(&csi->notifier);
> +	v4l2_ctrl_handler_free(&csi->ctrl_handler);
> +	v4l2_async_unregister_subdev(&csi->subdev);
> +	v4l2_subdev_cleanup(&csi->subdev);
> +	media_entity_cleanup(&csi->subdev.entity);
> +
> +	pm_runtime_disable(&cldev->dev);
> +
> +	mutex_destroy(&csi->lock);
> +}
> +
> +#define MEI_CSI_UUID UUID_LE(0x92335FCF, 0x3203, 0x4472, \
> +			     0xAF, 0x93, 0x7b, 0x44, 0x53, 0xAC, 0x29, 0xDA)
> +
> +static const struct mei_cl_device_id mei_csi_tbl[] = {
> +	{ MEI_CSI_DRIVER_NAME, MEI_CSI_UUID, MEI_CL_VERSION_ANY },
> +

Extra newline.

> +	/* required last entry */

The comment could be on the same line below. E.g. "Sentinel" is
appropriate, as this isn't just any last entry.

> +	{ }
> +};
> +MODULE_DEVICE_TABLE(mei, mei_csi_tbl);
> +
> +static struct mei_cl_driver mei_csi_driver = {
> +	.id_table = mei_csi_tbl,
> +	.name = MEI_CSI_DRIVER_NAME,
> +
> +	.probe = mei_csi_probe,
> +	.remove = mei_csi_remove,
> +};
> +
> +module_mei_cl_driver(mei_csi_driver);
> +
> +MODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>");
> +MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>");
> +MODULE_DESCRIPTION("Device driver for IVSC CSI");
> +MODULE_LICENSE("GPL");

-- 
Kind regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v3 1/3] media: pci: intel: ivsc: Add CSI submodule
  2023-03-27  6:23 ` [PATCH v3 1/3] media: pci: intel: ivsc: Add CSI submodule Wentong Wu
                     ` (2 preceding siblings ...)
  2023-03-29  8:48   ` Sakari Ailus
@ 2023-03-29  8:49   ` Sakari Ailus
  3 siblings, 0 replies; 26+ messages in thread
From: Sakari Ailus @ 2023-03-29  8:49 UTC (permalink / raw)
  To: Wentong Wu
  Cc: hdegoede, djrscally, laurent.pinchart, linux-media, bingbu.cao,
	zhifeng.wang, xiang.ye, tian.shu.qiu

Hi Wentong,

On Mon, Mar 27, 2023 at 02:23:06PM +0800, Wentong Wu wrote:
> diff --git a/drivers/media/pci/intel/ivsc/Kconfig b/drivers/media/pci/intel/ivsc/Kconfig
> new file mode 100644
> index 0000000..9535ac1
> --- /dev/null
> +++ b/drivers/media/pci/intel/ivsc/Kconfig
> @@ -0,0 +1,12 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +# Copyright (C) 2023, Intel Corporation. All rights reserved.
> +
> +config INTEL_VSC
> +	tristate "Intel Visual Sensing Controller"
> +	depends on INTEL_MEI

I think you should depend on PM here.

Alternatively, request host ownership from ace + csi in probe() if runtime
PM isn't enabled.

> +	help
> +	  This adds support for Intel Visual Sensing Controller (IVSC).
> +
> +	  Enables the IVSC firmware services required for controlling
> +	  camera sensor ownership and CSI-2 link through Image Processing
> +	  Unit(IPU) driver of Intel.

-- 
Kind regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 26+ messages in thread

* RE: [PATCH v3 1/3] media: pci: intel: ivsc: Add CSI submodule
  2023-03-29  8:48   ` Sakari Ailus
@ 2023-03-30  3:58     ` Wu, Wentong
  2023-03-30  8:23       ` Sakari Ailus
  0 siblings, 1 reply; 26+ messages in thread
From: Wu, Wentong @ 2023-03-30  3:58 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: hdegoede, djrscally, laurent.pinchart, linux-media, bingbu.cao,
	Wang, Zhifeng, Ye, Xiang, Qiu, Tian Shu

Hi Sakari,

Thanks

> -----Original Message-----
> From: Sakari Ailus <sakari.ailus@linux.intel.com>
> Sent: Wednesday, March 29, 2023 4:49 PM
> 
> Hi Wentong,
> 
> On Mon, Mar 27, 2023 at 02:23:06PM +0800, Wentong Wu wrote:
> > CSI is a submodule of IVSC which can route camera sensor data to the
> > outbound MIPI CSI-2 interface.
> >
> > The interface communicating with firmware is via MEI. There is a
> > separate MEI UUID, which this driver uses to enumerate.
> >
> > To route camera sensor data to host, the information of link frequency
> > and number of data lanes is sent to firmware by sending MEI command
> > when starting stream.
> >
> > CSI also provides a privacy mode. When privacy mode is turned on,
> > camera sensor can't be used. This means that both IVSC and host Image
> > Processing Unit(IPU) can't get image data. And when this mode is
> > turned on, host Image Processing Unit(IPU) driver is informed via v4l2
> > control callback, so that user can be notified.
> >
> > Signed-off-by: Wentong Wu <wentong.wu@intel.com>
> > ---
> >  drivers/media/pci/Kconfig                 |   1 +
> >  drivers/media/pci/intel/Makefile          |   2 +
> >  drivers/media/pci/intel/ivsc/Kconfig      |  12 +
> >  drivers/media/pci/intel/ivsc/Makefile     |   7 +
> >  drivers/media/pci/intel/ivsc/csi_bridge.c | 332 +++++++++++++
> > drivers/media/pci/intel/ivsc/csi_bridge.h | 122 +++++
> >  drivers/media/pci/intel/ivsc/mei_csi.c    | 775
> ++++++++++++++++++++++++++++++
> >  7 files changed, 1251 insertions(+)
> >  create mode 100644 drivers/media/pci/intel/ivsc/Kconfig
> >  create mode 100644 drivers/media/pci/intel/ivsc/Makefile
> >  create mode 100644 drivers/media/pci/intel/ivsc/csi_bridge.c
> >  create mode 100644 drivers/media/pci/intel/ivsc/csi_bridge.h
> >  create mode 100644 drivers/media/pci/intel/ivsc/mei_csi.c
> 
> I'm commenting just mei_csi this time...
> 
> > diff --git a/drivers/media/pci/intel/ivsc/mei_csi.c
> > b/drivers/media/pci/intel/ivsc/mei_csi.c
> > new file mode 100644
> > index 0000000..6a01537
> > --- /dev/null
> > +++ b/drivers/media/pci/intel/ivsc/mei_csi.c
> > @@ -0,0 +1,775 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Copyright (C) 2023 Intel Corporation. All rights reserved.
> > + * Intel Visual Sensing Controller CSI Linux driver  */
> > +
> > +/*
> > + * To set ownership of CSI-2 link and to configure CSI-2 link, there
> > + * are specific commands, which are sent via MEI protocol. The send
> > + * command function uses "completion" as a synchronization mechanism.
> > + * The response for command is received via a mei callback which
> > +wakes
> > + * up the caller. There can be only one outstanding command at a time.
> > + */
> > +
> > +#include <linux/completion.h>
> > +#include <linux/delay.h>
> > +#include <linux/kernel.h>
> > +#include <linux/math64.h>
> > +#include <linux/mei_cl_bus.h>
> > +#include <linux/module.h>
> > +#include <linux/mutex.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/slab.h>
> > +#include <linux/units.h>
> > +#include <linux/uuid.h>
> > +#include <linux/workqueue.h>
> > +
> > +#include <media/v4l2-async.h>
> > +#include <media/v4l2-ctrls.h>
> > +#include <media/v4l2-fwnode.h>
> > +#include <media/v4l2-subdev.h>
> > +
> > +#define MEI_CSI_DRIVER_NAME "ivsc_csi"
> > +
> > +/* the 5s used here is based on experiment */ #define CSI_CMD_TIMEOUT
> > +(5 * HZ)
> > +/* to setup CSI-2 link an extra delay needed and determined
> > +experimentally */ #define CSI_FW_READY_DELAY_MS 100
> > +/* link frequency unit is 100kHz */
> > +#define CSI_LINK_FREQ(x) ((u32)(div_u64(x, 100 * HZ_PER_KHZ)))
> > +
> > +/*
> > + * identify the command id supported by firmware
> > + * IPC, as well as the privacy notification id
> > + * used when processing privacy event.
> > + */
> > +enum csi_cmd_id {
> > +	/* used to set csi ownership */
> > +	CSI_SET_OWNER = 0,
> > +
> > +	/* used to configure CSI-2 link */
> > +	CSI_SET_CONF = 2,
> > +
> > +	/* privacy notification id used when privacy state changes */
> > +	CSI_PRIVACY_NOTIF = 6,
> > +};
> > +
> > +/* CSI-2 link ownership definition */ enum csi_link_owner {
> > +	CSI_LINK_IVSC,
> > +	CSI_LINK_HOST,
> > +};
> > +
> > +/* privacy status definition */
> > +enum ivsc_privacy_status {
> > +	CSI_PRIVACY_OFF,
> > +	CSI_PRIVACY_ON,
> > +	CSI_PRIVACY_MAX,
> > +};
> > +
> > +enum csi_pads {
> > +	CSI_PAD_SOURCE,
> > +	CSI_PAD_SINK,
> > +	CSI_NUM_PADS
> > +};
> > +
> > +/* configuration of the CSI-2 link between host and IVSC */ struct
> > +csi_link_cfg {
> > +	/* number of data lanes used on the CSI-2 link */
> > +	u32 nr_of_lanes;
> > +
> > +	/* frequency of the CSI-2 link */
> > +	u32 link_freq;
> > +
> > +	/* for future use */
> > +	u32 rsvd[2];
> > +} __packed;
> > +
> > +/* CSI command structure */
> > +struct csi_cmd {
> > +	u32 cmd_id;
> > +	union _cmd_param {
> > +		u32 param;
> > +		struct csi_link_cfg conf;
> > +	} param;
> > +} __packed;
> > +
> > +/* CSI notification structure */
> > +struct csi_notif {
> > +	u32 cmd_id;
> > +	int status;
> > +	union _resp_cont {
> > +		u32 cont;
> > +		struct csi_link_cfg conf;
> > +	} cont;
> > +} __packed;
> > +
> > +struct mei_csi {
> > +	struct mei_cl_device *cldev;
> > +
> > +	/* command response */
> > +	struct csi_notif cmd_response;
> > +	/* used to wait for command response from firmware */
> > +	struct completion cmd_completion;
> > +	/* protect command download */
> > +	struct mutex lock;
> > +
> > +	struct v4l2_subdev subdev;
> > +	struct v4l2_subdev *remote;
> > +	struct v4l2_async_notifier notifier;
> > +	struct v4l2_ctrl_handler ctrl_handler;
> > +	struct v4l2_ctrl *privacy_ctrl;
> > +	unsigned int remote_pad;
> > +	/* start streaming or not */
> > +	int streaming;
> > +
> > +	struct media_pad pads[CSI_NUM_PADS];
> > +	struct v4l2_mbus_framefmt format_mbus[CSI_NUM_PADS];
> > +
> > +	/* number of data lanes used on the CSI-2 link */
> > +	u32 nr_of_lanes;
> > +	/* frequency of the CSI-2 link */
> > +	u64 link_freq;
> > +
> > +	/* privacy status */
> > +	enum ivsc_privacy_status status;
> > +};
> > +
> > +static const struct v4l2_mbus_framefmt mei_csi_format_mbus_default = {
> > +	.width = 1,
> > +	.height = 1,
> > +	.code = MEDIA_BUS_FMT_Y8_1X8,
> > +	.field = V4L2_FIELD_NONE,
> > +};
> > +
> > +int csi_bridge_init(struct mei_cl_device *csi_dev);
> > +
> > +static inline struct mei_csi *notifier_to_csi(struct
> > +v4l2_async_notifier *n) {
> > +	return container_of(n, struct mei_csi, notifier); }
> > +
> > +static inline struct mei_csi *sd_to_csi(struct v4l2_subdev *sd) {
> > +	return container_of(sd, struct mei_csi, subdev); }
> > +
> > +/* send a command to firmware and mutex must be held by caller */
> > +static int mei_csi_send(struct mei_csi *csi, u8 *buf, size_t len) {
> > +	struct csi_cmd *cmd = (struct csi_cmd *)buf;
> > +	int ret;
> > +
> > +	reinit_completion(&csi->cmd_completion);
> > +
> > +	ret = mei_cldev_send(csi->cldev, buf, len);
> > +	if (ret < 0)
> > +		goto out;
> > +
> > +	ret = wait_for_completion_killable_timeout(&csi->cmd_completion,
> > +						   CSI_CMD_TIMEOUT);
> > +	if (ret < 0) {
> > +		goto out;
> > +	} else if (!ret) {
> > +		ret = -ETIMEDOUT;
> > +		goto out;
> > +	}
> > +
> > +	/* command response status */
> > +	ret = csi->cmd_response.status;
> > +	if (ret) {
> > +		ret = -EINVAL;
> > +		goto out;
> > +	}
> > +
> > +	if (csi->cmd_response.cmd_id != cmd->cmd_id)
> > +		ret = -EINVAL;
> > +
> > +out:
> > +	return ret;
> > +}
> > +
> > +/* set CSI-2 link ownership */
> > +static int csi_set_link_owner(struct mei_csi *csi, enum
> > +csi_link_owner owner) {
> > +	struct csi_cmd cmd = { 0 };
> > +	size_t cmd_size;
> > +	int ret;
> > +
> > +	cmd.cmd_id = CSI_SET_OWNER;
> > +	cmd.param.param = owner;
> > +	cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.param);
> 
> In some cases you're using memset and in others not. If you don't need memset,
> I'd prefer assigning the fields in variable declaration instead.

The declaration will be like below, but it will break reverse x-mas tree style.

struct csi_cmd cmd = { 0 };
size_t cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.param);
int ret;


> 
> > +
> > +	mutex_lock(&csi->lock);
> > +
> > +	ret = mei_csi_send(csi, (u8 *)&cmd, cmd_size);
> > +
> > +	mutex_unlock(&csi->lock);
> > +
> > +	return ret;
> > +}
> > +
> > +/* configure CSI-2 link between host and IVSC */ static int
> > +csi_set_link_cfg(struct mei_csi *csi) {
> > +	struct csi_cmd cmd = { 0 };
> > +	size_t cmd_size;
> > +	int ret;
> > +
> > +	cmd.cmd_id = CSI_SET_CONF;
> > +	cmd.param.conf.nr_of_lanes = csi->nr_of_lanes;
> > +	cmd.param.conf.link_freq = CSI_LINK_FREQ(csi->link_freq);
> > +	cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.conf);
> 
> Ditto.
> 
> > +
> > +	mutex_lock(&csi->lock);
> > +
> > +	ret = mei_csi_send(csi, (u8 *)&cmd, cmd_size);
> > +	/*
> > +	 * wait configuration ready if download success. placing
> > +	 * delay under mutex is to make sure current command flow
> > +	 * completed before starting a possible new one.
> > +	 */
> > +	if (!ret)
> > +		msleep(CSI_FW_READY_DELAY_MS);
> > +
> > +	mutex_unlock(&csi->lock);
> > +
> > +	return ret;
> > +}
> > +
> > +/* callback for receive */
> > +static void mei_csi_rx(struct mei_cl_device *cldev) {
> > +	struct mei_csi *csi = mei_cldev_get_drvdata(cldev);
> > +	struct csi_notif notif = { 0 };
> > +	int ret;
> > +
> > +	ret = mei_cldev_recv(cldev, (u8 *)&notif, sizeof(notif));
> > +	if (ret < 0) {
> > +		dev_err(&cldev->dev, "recv error: %d\n", ret);
> > +		return;
> > +	}
> > +
> > +	switch (notif.cmd_id) {
> > +	case CSI_PRIVACY_NOTIF:
> > +		if (notif.cont.cont < CSI_PRIVACY_MAX) {
> > +			csi->status = notif.cont.cont;
> > +			v4l2_ctrl_s_ctrl(csi->privacy_ctrl, csi->status);
> > +		}
> > +		break;
> > +	case CSI_SET_OWNER:
> > +	case CSI_SET_CONF:
> > +		memcpy(&csi->cmd_response, &notif, ret);
> > +
> > +		complete(&csi->cmd_completion);
> > +		break;
> > +	default:
> > +		break;
> > +	}
> > +}
> > +
> > +static int mei_csi_pre_streamon(struct v4l2_subdev *sd, u32 flags) {
> > +	struct v4l2_querymenu qm = { .id = V4L2_CID_LINK_FREQ, };
> > +	struct v4l2_mbus_config mbus_config = { 0 };
> > +	struct mei_csi *csi = sd_to_csi(sd);
> > +	struct v4l2_ctrl *ctrl;
> > +	int ret = 0;
> > +
> > +	if (!csi->remote)
> > +		return -ENODEV;
> > +
> > +	ret = v4l2_subdev_call(csi->remote, pad, get_mbus_config,
> > +			       csi->remote_pad, &mbus_config);
> > +	if (ret)
> > +		return ret;
> 
> You're already parsing the endpoint in probe(). Do you need this, assuming the
> sensor has a static lane configuration? Virtually all do.

When parsing remote endpoint, just get num_data_lanes by
v4l2_fwnode_endpoint_alloc_parse?

> 
> Could this all be done via the s_stream() op instead?

We don't need csi2 link freq and lane number when s_steam(0)

> 
> > +
> > +	if (mbus_config.type != V4L2_MBUS_CSI2_DPHY)
> > +		return -EINVAL;
> > +
> > +	csi->nr_of_lanes = mbus_config.bus.mipi_csi2.num_data_lanes;
> > +
> > +	ctrl = v4l2_ctrl_find(csi->remote->ctrl_handler, V4L2_CID_LINK_FREQ);
> > +	if (!ctrl)
> > +		return -EINVAL;
> > +	qm.index = v4l2_ctrl_g_ctrl(ctrl);
> > +
> > +	ret = v4l2_querymenu(csi->remote->ctrl_handler, &qm);
> > +	if (ret)
> > +		return ret;
> > +
> > +	csi->link_freq = qm.value;
> > +
> > +	return ret;
> > +}
> > +
> > +static int mei_csi_set_stream(struct v4l2_subdev *sd, int enable) {
> > +	struct mei_csi *csi = sd_to_csi(sd);
> > +	int ret = 0;
> > +
> > +	if (enable && csi->streaming == 0) {
> > +		/* switch CSI-2 link to host */
> > +		ret = csi_set_link_owner(csi, CSI_LINK_HOST);
> > +		if (ret < 0)
> > +			goto err;
> > +
> > +		/* configure CSI-2 link */
> > +		ret = csi_set_link_cfg(csi);
> > +		if (ret < 0)
> > +			goto err;
> > +
> > +		ret = v4l2_subdev_call(csi->remote, video, s_stream, 1);
> > +		if (ret)
> > +			goto err;
> > +	} else if (!enable && csi->streaming == 1) {
> > +		v4l2_subdev_call(csi->remote, video, s_stream, 0);
> > +
> > +		/* switch CSI-2 link to IVSC */
> > +		ret = csi_set_link_owner(csi, CSI_LINK_IVSC);
> > +		if (ret < 0)
> > +			goto err;
> 
> I'd complain (e.g. dev_warn()) but return zero in this case. There's not much if
> anything the caller can do with this.

ack
> 
> > +	}
> > +
> > +	csi->streaming = enable;
> > +
> > +err:
> > +	return ret;
> > +}
> > +
> > +static struct v4l2_mbus_framefmt *
> > +mei_csi_get_pad_format(struct v4l2_subdev *sd,
> > +		       struct v4l2_subdev_state *sd_state,
> > +		       unsigned int pad, u32 which) {
> > +	struct mei_csi *csi = sd_to_csi(sd);
> > +
> > +	switch (which) {
> > +	case V4L2_SUBDEV_FORMAT_TRY:
> > +		return v4l2_subdev_get_try_format(sd, sd_state, pad);
> > +	case V4L2_SUBDEV_FORMAT_ACTIVE:
> > +		return &csi->format_mbus[pad];
> > +	default:
> > +		return NULL;
> > +	}
> > +}
> > +
> > +static int mei_csi_init_cfg(struct v4l2_subdev *sd,
> > +			    struct v4l2_subdev_state *sd_state) {
> > +	struct v4l2_mbus_framefmt *mbusformat;
> > +	struct mei_csi *csi = sd_to_csi(sd);
> > +	unsigned int i;
> > +
> > +	mutex_lock(&csi->lock);
> > +
> > +	for (i = 0; i < sd->entity.num_pads; i++) {
> > +		mbusformat = v4l2_subdev_get_try_format(sd, sd_state, i);
> > +		*mbusformat = mei_csi_format_mbus_default;
> > +	}
> > +
> > +	mutex_unlock(&csi->lock);
> > +
> > +	return 0;
> > +}
> > +
> > +static int mei_csi_get_fmt(struct v4l2_subdev *sd,
> > +			   struct v4l2_subdev_state *sd_state,
> > +			   struct v4l2_subdev_format *format) {
> > +	struct v4l2_mbus_framefmt *mbusformat;
> > +	struct mei_csi *csi = sd_to_csi(sd);
> > +
> > +	mutex_lock(&csi->lock);
> > +
> > +	mbusformat = mei_csi_get_pad_format(sd, sd_state, format->pad,
> > +					    format->which);
> > +	if (mbusformat)
> > +		format->format = *mbusformat;
> > +
> > +	mutex_unlock(&csi->lock);
> > +
> > +	return 0;
> > +}
> 
> It'd be nice to have also enum_mbus_code support. Video mux of course didn't
> have this either. Something that could be done in the framework, including
> validating the mbus format.

Sorry, you mean the default MEDIA_BUS_FMT_Y8_1X8?

> 
> > +
> > +static int mei_csi_set_fmt(struct v4l2_subdev *sd,
> > +			   struct v4l2_subdev_state *sd_state,
> > +			   struct v4l2_subdev_format *format) {
> > +	struct v4l2_mbus_framefmt *source_mbusformat;
> > +	struct v4l2_mbus_framefmt *mbusformat;
> > +	struct mei_csi *csi = sd_to_csi(sd);
> > +	struct media_pad *pad;
> > +
> > +	mbusformat = mei_csi_get_pad_format(sd, sd_state, format->pad,
> > +					    format->which);
> > +	if (!mbusformat)
> > +		return -EINVAL;
> > +
> > +	source_mbusformat = mei_csi_get_pad_format(sd, sd_state,
> > +						   CSI_PAD_SOURCE,
> > +						   format->which);
> > +	if (!source_mbusformat)
> > +		return -EINVAL;
> > +
> > +	v4l_bound_align_image(&format->format.width, 1, 65536, 0,
> > +			      &format->format.height, 1, 65536, 0, 0);
> > +
> > +	switch (format->format.code) {
> > +	case MEDIA_BUS_FMT_RGB444_1X12:
> > +	case MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE:
> > +	case MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE:
> > +	case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE:
> > +	case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE:
> > +	case MEDIA_BUS_FMT_RGB565_1X16:
> > +	case MEDIA_BUS_FMT_BGR565_2X8_BE:
> > +	case MEDIA_BUS_FMT_BGR565_2X8_LE:
> > +	case MEDIA_BUS_FMT_RGB565_2X8_BE:
> > +	case MEDIA_BUS_FMT_RGB565_2X8_LE:
> > +	case MEDIA_BUS_FMT_RGB666_1X18:
> > +	case MEDIA_BUS_FMT_RBG888_1X24:
> > +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> > +	case MEDIA_BUS_FMT_BGR888_1X24:
> > +	case MEDIA_BUS_FMT_GBR888_1X24:
> > +	case MEDIA_BUS_FMT_RGB888_1X24:
> > +	case MEDIA_BUS_FMT_RGB888_2X12_BE:
> > +	case MEDIA_BUS_FMT_RGB888_2X12_LE:
> > +	case MEDIA_BUS_FMT_ARGB8888_1X32:
> > +	case MEDIA_BUS_FMT_RGB888_1X32_PADHI:
> > +	case MEDIA_BUS_FMT_RGB101010_1X30:
> > +	case MEDIA_BUS_FMT_RGB121212_1X36:
> > +	case MEDIA_BUS_FMT_RGB161616_1X48:
> > +	case MEDIA_BUS_FMT_Y8_1X8:
> > +	case MEDIA_BUS_FMT_UV8_1X8:
> > +	case MEDIA_BUS_FMT_UYVY8_1_5X8:
> > +	case MEDIA_BUS_FMT_VYUY8_1_5X8:
> > +	case MEDIA_BUS_FMT_YUYV8_1_5X8:
> > +	case MEDIA_BUS_FMT_YVYU8_1_5X8:
> > +	case MEDIA_BUS_FMT_UYVY8_2X8:
> > +	case MEDIA_BUS_FMT_VYUY8_2X8:
> > +	case MEDIA_BUS_FMT_YUYV8_2X8:
> > +	case MEDIA_BUS_FMT_YVYU8_2X8:
> > +	case MEDIA_BUS_FMT_Y10_1X10:
> > +	case MEDIA_BUS_FMT_UYVY10_2X10:
> > +	case MEDIA_BUS_FMT_VYUY10_2X10:
> > +	case MEDIA_BUS_FMT_YUYV10_2X10:
> > +	case MEDIA_BUS_FMT_YVYU10_2X10:
> > +	case MEDIA_BUS_FMT_Y12_1X12:
> > +	case MEDIA_BUS_FMT_UYVY12_2X12:
> > +	case MEDIA_BUS_FMT_VYUY12_2X12:
> > +	case MEDIA_BUS_FMT_YUYV12_2X12:
> > +	case MEDIA_BUS_FMT_YVYU12_2X12:
> > +	case MEDIA_BUS_FMT_UYVY8_1X16:
> > +	case MEDIA_BUS_FMT_VYUY8_1X16:
> > +	case MEDIA_BUS_FMT_YUYV8_1X16:
> > +	case MEDIA_BUS_FMT_YVYU8_1X16:
> > +	case MEDIA_BUS_FMT_YDYUYDYV8_1X16:
> > +	case MEDIA_BUS_FMT_UYVY10_1X20:
> > +	case MEDIA_BUS_FMT_VYUY10_1X20:
> > +	case MEDIA_BUS_FMT_YUYV10_1X20:
> > +	case MEDIA_BUS_FMT_YVYU10_1X20:
> > +	case MEDIA_BUS_FMT_VUY8_1X24:
> > +	case MEDIA_BUS_FMT_YUV8_1X24:
> > +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> > +	case MEDIA_BUS_FMT_UYVY12_1X24:
> > +	case MEDIA_BUS_FMT_VYUY12_1X24:
> > +	case MEDIA_BUS_FMT_YUYV12_1X24:
> > +	case MEDIA_BUS_FMT_YVYU12_1X24:
> > +	case MEDIA_BUS_FMT_YUV10_1X30:
> > +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> > +	case MEDIA_BUS_FMT_AYUV8_1X32:
> > +	case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
> > +	case MEDIA_BUS_FMT_YUV12_1X36:
> > +	case MEDIA_BUS_FMT_YUV16_1X48:
> > +	case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
> > +	case MEDIA_BUS_FMT_JPEG_1X8:
> > +	case MEDIA_BUS_FMT_AHSV8888_1X32:
> > +	case MEDIA_BUS_FMT_SBGGR8_1X8:
> > +	case MEDIA_BUS_FMT_SGBRG8_1X8:
> > +	case MEDIA_BUS_FMT_SGRBG8_1X8:
> > +	case MEDIA_BUS_FMT_SRGGB8_1X8:
> > +	case MEDIA_BUS_FMT_SBGGR10_1X10:
> > +	case MEDIA_BUS_FMT_SGBRG10_1X10:
> > +	case MEDIA_BUS_FMT_SGRBG10_1X10:
> > +	case MEDIA_BUS_FMT_SRGGB10_1X10:
> > +	case MEDIA_BUS_FMT_SBGGR12_1X12:
> > +	case MEDIA_BUS_FMT_SGBRG12_1X12:
> > +	case MEDIA_BUS_FMT_SGRBG12_1X12:
> > +	case MEDIA_BUS_FMT_SRGGB12_1X12:
> > +	case MEDIA_BUS_FMT_SBGGR14_1X14:
> > +	case MEDIA_BUS_FMT_SGBRG14_1X14:
> > +	case MEDIA_BUS_FMT_SGRBG14_1X14:
> > +	case MEDIA_BUS_FMT_SRGGB14_1X14:
> > +	case MEDIA_BUS_FMT_SBGGR16_1X16:
> > +	case MEDIA_BUS_FMT_SGBRG16_1X16:
> > +	case MEDIA_BUS_FMT_SGRBG16_1X16:
> > +	case MEDIA_BUS_FMT_SRGGB16_1X16:
> > +		break;
> > +	default:
> > +		format->format.code = MEDIA_BUS_FMT_Y8_1X8;
> > +		break;
> > +	}
> > +	if (format->format.field == V4L2_FIELD_ANY)
> > +		format->format.field = V4L2_FIELD_NONE;
> > +
> > +	mutex_lock(&csi->lock);
> > +
> > +	pad = &csi->pads[format->pad];
> > +	if (pad->flags & MEDIA_PAD_FL_SOURCE)
> > +		format->format = csi->format_mbus[CSI_PAD_SINK];
> > +
> > +	*mbusformat = format->format;
> > +
> > +	if (pad->flags & MEDIA_PAD_FL_SINK)
> > +		*source_mbusformat = format->format;
> > +
> > +	mutex_unlock(&csi->lock);
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct v4l2_subdev_video_ops mei_csi_video_ops = {
> > +	.s_stream = mei_csi_set_stream,
> > +	.pre_streamon = mei_csi_pre_streamon, };
> > +
> > +static const struct v4l2_subdev_pad_ops mei_csi_pad_ops = {
> > +	.init_cfg = mei_csi_init_cfg,
> > +	.get_fmt = mei_csi_get_fmt,
> > +	.set_fmt = mei_csi_set_fmt,
> > +};
> > +
> > +static const struct v4l2_subdev_ops mei_csi_subdev_ops = {
> > +	.video = &mei_csi_video_ops,
> > +	.pad = &mei_csi_pad_ops,
> > +};
> > +
> > +static const struct media_entity_operations mei_csi_entity_ops = {
> > +	.link_validate = v4l2_subdev_link_validate, };
> > +
> > +static int mei_csi_notify_bound(struct v4l2_async_notifier *notifier,
> > +				struct v4l2_subdev *subdev,
> > +				struct v4l2_async_subdev *asd)
> > +{
> > +	struct mei_csi *csi = notifier_to_csi(notifier);
> > +	int pad;
> > +
> > +	pad = media_entity_get_fwnode_pad(&subdev->entity, asd-
> >match.fwnode,
> > +					  MEDIA_PAD_FL_SOURCE);
> > +	if (pad < 0)
> > +		return pad;
> > +
> > +	csi->remote = subdev;
> > +	csi->remote_pad = pad;
> > +
> > +	return media_create_pad_link(&subdev->entity, pad,
> > +				     &csi->subdev.entity, 0,
> > +				     MEDIA_LNK_FL_ENABLED |
> > +				     MEDIA_LNK_FL_IMMUTABLE);
> > +}
> > +
> > +static void mei_csi_notify_unbind(struct v4l2_async_notifier *notifier,
> > +				  struct v4l2_subdev *subdev,
> > +				  struct v4l2_async_subdev *asd)
> > +{
> > +	struct mei_csi *csi = notifier_to_csi(notifier);
> > +
> > +	csi->remote = NULL;
> > +}
> > +
> > +static const struct v4l2_async_notifier_operations mei_csi_notify_ops = {
> > +	.bound = mei_csi_notify_bound,
> > +	.unbind = mei_csi_notify_unbind,
> > +};
> > +
> > +static int mei_csi_init_control(struct mei_csi *csi) {
> > +	v4l2_ctrl_handler_init(&csi->ctrl_handler, 1);
> > +	csi->ctrl_handler.lock = &csi->lock;
> > +
> > +	csi->privacy_ctrl = v4l2_ctrl_new_std(&csi->ctrl_handler, NULL,
> > +					      V4L2_CID_PRIVACY, 0, 1, 1, 0);
> > +	if (csi->ctrl_handler.error)
> > +		return csi->ctrl_handler.error;
> 
> The control should be made volatile here (flags |=
> V4L2_CTRL_FLAG_READ_ONLY) as it can't be changed by the user.

ack
> 
> > +
> > +	csi->subdev.ctrl_handler = &csi->ctrl_handler;
> > +
> > +	return 0;
> > +}
> > +
> > +static int mei_csi_parse_firmware(struct mei_csi *csi) {
> > +	struct v4l2_fwnode_endpoint v4l2_ep = {
> > +		.bus_type = V4L2_MBUS_CSI2_DPHY,
> > +	};
> > +	struct device *dev = &csi->cldev->dev;
> > +	struct v4l2_async_subdev *asd;
> > +	struct fwnode_handle *fwnode;
> > +	struct fwnode_handle *ep;
> > +	int ret;
> > +
> > +	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0);
> > +	if (!ep) {
> > +		dev_err(dev, "not connected to subdevice\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	ret = v4l2_fwnode_endpoint_parse(ep, &v4l2_ep);
> > +	if (ret) {
> > +		dev_err(dev, "could not parse v4l2 endpoint\n");
> > +		fwnode_handle_put(ep);
> > +		return -EINVAL;
> > +	}
> > +
> > +	fwnode = fwnode_graph_get_remote_endpoint(ep);
> > +	fwnode_handle_put(ep);
> > +
> > +	v4l2_async_nf_init(&csi->notifier);
> > +	csi->notifier.ops = &mei_csi_notify_ops;
> > +
> > +	asd = v4l2_async_nf_add_fwnode(&csi->notifier, fwnode,
> > +				       struct v4l2_async_subdev);
> > +	fwnode_handle_put(fwnode);
> > +	if (IS_ERR(asd))
> > +		return PTR_ERR(asd);
> > +
> > +	ret = v4l2_async_subdev_nf_register(&csi->subdev, &csi->notifier);
> > +	if (ret)
> > +		v4l2_async_nf_cleanup(&csi->notifier);
> > +
> > +	return ret;
> > +}
> > +
> > +static int mei_csi_probe(struct mei_cl_device *cldev,
> > +			 const struct mei_cl_device_id *id) {
> > +	struct mei_csi *csi;
> > +	int ret;
> > +
> > +	/*
> > +	 * no connections to sensor are defined in firmware, try to
> > +	 * build connections as software_nodes parsed from SSDB.
> > +	 */
> > +	ret = csi_bridge_init(cldev);
> > +	if (ret)
> > +		return ret;
> > +
> > +	csi = devm_kzalloc(&cldev->dev, sizeof(struct mei_csi), GFP_KERNEL);
> > +	if (!csi)
> > +		return -ENOMEM;
> > +
> > +	csi->cldev = cldev;
> > +	mutex_init(&csi->lock);
> > +	init_completion(&csi->cmd_completion);
> > +
> > +	mei_cldev_set_drvdata(cldev, csi);
> > +
> > +	ret = mei_cldev_enable(cldev);
> > +	if (ret < 0) {
> > +		dev_err(&cldev->dev, "mei_cldev_enable failed: %d\n", ret);
> > +		goto destroy_mutex;
> > +	}
> > +
> > +	ret = mei_cldev_register_rx_cb(cldev, mei_csi_rx);
> > +	if (ret) {
> > +		dev_err(&cldev->dev, "event cb registration failed: %d\n", ret);
> > +		goto err_disable;
> > +	}
> > +
> > +	ret = mei_csi_parse_firmware(csi);
> > +	if (ret)
> > +		goto err_disable;
> > +
> > +	csi->subdev.dev = &cldev->dev;
> > +	v4l2_subdev_init(&csi->subdev, &mei_csi_subdev_ops);
> > +	v4l2_set_subdevdata(&csi->subdev, csi);
> > +	csi->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
> > +	csi->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
> > +	csi->subdev.entity.ops = &mei_csi_entity_ops;
> > +
> > +	ret = mei_csi_init_control(csi);
> > +	if (ret)
> > +		goto err_async;
> 
> You'll also need to call v4l2_ctrl_handler_free() if setting up the handler failed.
> I.e. just change the label here and remove err_async label below.
Thanks
> 
> > +
> > +	csi->format_mbus[CSI_PAD_SOURCE] = mei_csi_format_mbus_default;
> > +	csi->format_mbus[CSI_PAD_SINK] = mei_csi_format_mbus_default;
> > +
> > +	csi->pads[CSI_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
> > +	csi->pads[CSI_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
> > +	ret = media_entity_pads_init(&csi->subdev.entity, CSI_NUM_PADS,
> > +				     csi->pads);
> > +	if (ret)
> > +		goto err_ctrl_handler;
> > +
> > +	ret = v4l2_subdev_init_finalize(&csi->subdev);
> > +	if (ret < 0)
> > +		goto err_entity;
> > +
> > +	ret = v4l2_async_register_subdev(&csi->subdev);
> > +	if (ret < 0)
> > +		goto err_subdev;
> > +
> > +	pm_runtime_enable(&cldev->dev);
> > +
> > +	return 0;
> > +
> > +err_subdev:
> > +	v4l2_subdev_cleanup(&csi->subdev);
> > +err_entity:
> > +	media_entity_cleanup(&csi->subdev.entity);
> > +err_ctrl_handler:
> > +	v4l2_ctrl_handler_free(&csi->ctrl_handler);
> > +err_async:
> > +	v4l2_async_nf_unregister(&csi->notifier);
> > +	v4l2_async_nf_cleanup(&csi->notifier);
> > +err_disable:
> > +	mei_cldev_disable(cldev);
> 
> It'd be nice to have an additional newline before the label. Elsewhere, too.
ack
> 
> > +destroy_mutex:
> > +	mutex_destroy(&csi->lock);
> > +
> > +	return ret;
> > +}
> > +
> > +static void mei_csi_remove(struct mei_cl_device *cldev) {
> > +	struct mei_csi *csi = mei_cldev_get_drvdata(cldev);
> > +
> > +	v4l2_async_nf_unregister(&csi->notifier);
> > +	v4l2_async_nf_cleanup(&csi->notifier);
> > +	v4l2_ctrl_handler_free(&csi->ctrl_handler);
> > +	v4l2_async_unregister_subdev(&csi->subdev);
> > +	v4l2_subdev_cleanup(&csi->subdev);
> > +	media_entity_cleanup(&csi->subdev.entity);
> > +
> > +	pm_runtime_disable(&cldev->dev);
> > +
> > +	mutex_destroy(&csi->lock);
> > +}
> > +
> > +#define MEI_CSI_UUID UUID_LE(0x92335FCF, 0x3203, 0x4472, \
> > +			     0xAF, 0x93, 0x7b, 0x44, 0x53, 0xAC, 0x29, 0xDA)
> > +
> > +static const struct mei_cl_device_id mei_csi_tbl[] = {
> > +	{ MEI_CSI_DRIVER_NAME, MEI_CSI_UUID, MEI_CL_VERSION_ANY },
> > +
> 
> Extra newline.
> 
> > +	/* required last entry */
> 
> The comment could be on the same line below. E.g. "Sentinel" is appropriate, as
> this isn't just any last entry.
thanks
> 
> > +	{ }
> > +};
> > +MODULE_DEVICE_TABLE(mei, mei_csi_tbl);
> > +
> > +static struct mei_cl_driver mei_csi_driver = {
> > +	.id_table = mei_csi_tbl,
> > +	.name = MEI_CSI_DRIVER_NAME,
> > +
> > +	.probe = mei_csi_probe,
> > +	.remove = mei_csi_remove,
> > +};
> > +
> > +module_mei_cl_driver(mei_csi_driver);
> > +
> > +MODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>");
> > +MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>");
> > +MODULE_DESCRIPTION("Device driver for IVSC CSI");
> > +MODULE_LICENSE("GPL");
> 
> --
> Kind regards,
> 
> Sakari Ailus

^ permalink raw reply	[flat|nested] 26+ messages in thread

* RE: [PATCH v3 2/3] media: pci: intel: ivsc: Add ACE submodule
  2023-03-29  8:03   ` Sakari Ailus
@ 2023-03-30  7:32     ` Wu, Wentong
  2023-03-30  8:27       ` Sakari Ailus
  0 siblings, 1 reply; 26+ messages in thread
From: Wu, Wentong @ 2023-03-30  7:32 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: hdegoede, djrscally, laurent.pinchart, linux-media, bingbu.cao,
	Wang, Zhifeng, Ye, Xiang, Qiu, Tian Shu

Hi Sakari,

Thanks

> -----Original Message-----
> From: Sakari Ailus <sakari.ailus@linux.intel.com>
> Sent: Wednesday, March 29, 2023 4:04 PM
> 
> Hi Wentong,
> 
> On Mon, Mar 27, 2023 at 02:23:07PM +0800, Wentong Wu wrote:
> > ACE is a submodule of IVSC which controls camera sensor's ownership,
> > belonging to host or IVSC. When IVSC owns camera sensor, it is for
> > algorithm computing. When host wants to control camera sensor, ACE
> > module needs to be informed of ownership with defined interface.
> >
> > The interface is via MEI. There is a separate MEI UUID, which this
> > driver uses to enumerate.
> >
> > To switch ownership of camera sensor between IVSC and host, the caller
> > specifies the defined ownership information which will be sent to
> > firmware by sending MEI command.
> >
> > Device link(device_link_add) is used to set the right camera sensor
> > ownership before accessing the sensor via I²C. With DL_FLAG_PM_RUNTIME
> > and DL_FLAG_RPM_ACTIVE, the supplier device will be PM runtime resumed
> > before the consumer(camera sensor).
> > So use runtime PM callbacks to transfer the ownership between host and
> > IVSC.
> >
> > Signed-off-by: Wentong Wu <wentong.wu@intel.com>
> > ---
> >  drivers/media/pci/intel/ivsc/Makefile  |   3 +
> >  drivers/media/pci/intel/ivsc/mei_ace.c | 534
> > +++++++++++++++++++++++++++++++++
> >  2 files changed, 537 insertions(+)
> >  create mode 100644 drivers/media/pci/intel/ivsc/mei_ace.c
> >
> > diff --git a/drivers/media/pci/intel/ivsc/Makefile
> > b/drivers/media/pci/intel/ivsc/Makefile
> > index 7e4c5f0..a641f14 100644
> > --- a/drivers/media/pci/intel/ivsc/Makefile
> > +++ b/drivers/media/pci/intel/ivsc/Makefile
> > @@ -5,3 +5,6 @@
> >  obj-$(CONFIG_INTEL_VSC) += ivsc-csi.o  ivsc-csi-y += mei_csi.o
> > ivsc-csi-y += csi_bridge.o
> > +
> > +obj-$(CONFIG_INTEL_VSC) += ivsc-ace.o ivsc-ace-y += mei_ace.o
> > diff --git a/drivers/media/pci/intel/ivsc/mei_ace.c
> > b/drivers/media/pci/intel/ivsc/mei_ace.c
> > new file mode 100644
> > index 0000000..434b072
> > --- /dev/null
> > +++ b/drivers/media/pci/intel/ivsc/mei_ace.c
> > @@ -0,0 +1,534 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Copyright (C) 2023 Intel Corporation. All rights reserved.
> > + * Intel Visual Sensing Controller ACE Linux driver  */
> > +
> > +/*
> > + * To set ownership of camera sensor, there is specific command,
> > +which
> > + * is sent via MEI protocol. That's a two-step scheme where the
> > +firmware
> > + * first acks receipt of the command and later responses the command
> > +was
> > + * executed. The command sending function uses "completion" as the
> > + * synchronization mechanism. The notification for command is
> > +received
> > + * via a mei callback which wakes up the caller. There can be only
> > +one
> > + * outstanding command at a time.
> 
> Could you document the dependencies in the sensor initialisation (ace +
> csi) 

This is about ownership instead of dependency, if host sensor driver configure
sensor with ownership on IVSC, probably it will be changed by firmware somehow.

> and how the sensor is powered (i.e. no direct control of the PMIC from the
> host?)? 

The power line is directly connected to IVSC instead of host, when ownership
switched to host, sensor is already powered up by firmware. 

> This doesn't seem like a bad place to do it.
> 
> > + */
> > +
> > +#include <linux/acpi.h>
> > +#include <linux/completion.h>
> > +#include <linux/delay.h>
> > +#include <linux/kernel.h>
> > +#include <linux/mei_cl_bus.h>
> > +#include <linux/module.h>
> > +#include <linux/mutex.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/slab.h>
> > +#include <linux/uuid.h>
> > +
> > +#define	MEI_ACE_DRIVER_NAME	"ivsc_ace"
> > +
> > +/* indicating driver message */
> > +#define	ACE_DRV_MSG		1
> > +/* indicating set command */
> > +#define	ACE_CMD_SET		4
> > +/* command timeout determined experimentally */
> > +#define	ACE_CMD_TIMEOUT		(5 * HZ)
> > +/* indicating the first command block */
> > +#define	ACE_CMD_INIT_BLOCK	1
> > +/* indicating the last command block */
> > +#define	ACE_CMD_FINAL_BLOCK	1
> > +/* size of camera status notification content */
> > +#define	ACE_CAMERA_STATUS_SIZE	5
> > +
> > +/* UUID used to get firmware id */
> > +#define ACE_GET_FW_ID_UUID UUID_LE(0x6167DCFB, 0x72F1, 0x4584,
> 0xBF, \
> > +				   0xE3, 0x84, 0x17, 0x71, 0xAA, 0x79, 0x0B)
> > +
> > +/* UUID used to get csi device */
> > +#define MEI_CSI_UUID UUID_LE(0x92335FCF, 0x3203, 0x4472, \
> > +			     0xAF, 0x93, 0x7b, 0x44, 0x53, 0xAC, 0x29, 0xDA)
> > +
> > +/* identify firmware event type */
> > +enum ace_event_type {
> > +	/* firmware ready */
> > +	ACE_FW_READY = 0x8,
> > +
> > +	/* command response */
> > +	ACE_CMD_RESPONSE = 0x10,
> > +};
> > +
> > +/* identify camera sensor ownership */ enum ace_camera_owner {
> > +	ACE_CAMERA_IVSC,
> > +	ACE_CAMERA_HOST,
> > +};
> > +
> > +/* identify the command id supported by firmware IPC */ enum
> > +ace_cmd_id {
> > +	/* used to switch camera sensor to host */
> > +	ACE_SWITCH_CAMERA_TO_HOST = 0x13,
> > +
> > +	/* used to switch camera sensor to IVSC */
> > +	ACE_SWITCH_CAMERA_TO_IVSC = 0x14,
> > +
> > +	/* used to get firmware id */
> > +	ACE_GET_FW_ID = 0x1A,
> > +};
> > +
> > +/* ACE command header structure */
> > +struct ace_cmd_hdr {
> > +	u32 firmware_id : 16;
> > +	u32 instance_id : 8;
> > +	u32 type : 5;
> > +	u32 rsp : 1;
> > +	u32 msg_tgt : 1;
> > +	u32 _hw_rsvd_1 : 1;
> > +	u32 param_size : 20;
> > +	u32 cmd_id : 8;
> > +	u32 final_block : 1;
> > +	u32 init_block : 1;
> > +	u32 _hw_rsvd_2 : 2;
> > +} __packed;
> > +
> > +/* ACE command parameter structure */ union ace_cmd_param {
> > +	uuid_le uuid;
> > +	u32 param;
> > +};
> > +
> > +/* ACE command structure */
> > +struct ace_cmd {
> > +	struct ace_cmd_hdr hdr;
> > +	union ace_cmd_param param;
> > +} __packed;
> > +
> > +/* ACE notification header */
> > +union ace_notif_hdr {
> > +	struct _confirm {
> > +		u32 status : 24;
> > +		u32 type : 5;
> > +		u32 rsp : 1;
> > +		u32 msg_tgt : 1;
> > +		u32 _hw_rsvd_1 : 1;
> > +		u32 param_size : 20;
> > +		u32 cmd_id : 8;
> > +		u32 final_block : 1;
> > +		u32 init_block : 1;
> > +		u32 _hw_rsvd_2 : 2;
> > +	} __packed ack;
> > +
> > +	struct _event {
> > +		u32 rsvd1 : 16;
> > +		u32 event_type : 8;
> > +		u32 type : 5;
> > +		u32 ack : 1;
> > +		u32 msg_tgt : 1;
> > +		u32 _hw_rsvd_1 : 1;
> > +		u32 rsvd2 : 30;
> > +		u32 _hw_rsvd_2 : 2;
> > +	} __packed event;
> > +
> > +	struct _response {
> > +		u32 event_id : 16;
> > +		u32 notif_type : 8;
> > +		u32 type : 5;
> > +		u32 rsp : 1;
> > +		u32 msg_tgt : 1;
> > +		u32 _hw_rsvd_1 : 1;
> > +		u32 event_data_size : 16;
> > +		u32 request_target : 1;
> > +		u32 request_type : 5;
> > +		u32 cmd_id : 8;
> > +		u32 _hw_rsvd_2 : 2;
> > +	} __packed response;
> > +};
> > +
> > +/* ACE notification content */
> > +union ace_notif_cont {
> > +	u16 firmware_id;
> > +	u8 state_notif;
> > +	u8 camera_status[ACE_CAMERA_STATUS_SIZE];
> > +};
> > +
> > +/* ACE notification structure */
> > +struct ace_notif {
> > +	union ace_notif_hdr hdr;
> > +	union ace_notif_cont cont;
> > +} __packed;
> > +
> > +struct mei_ace {
> > +	struct mei_cl_device *cldev;
> > +
> > +	/* command ack */
> > +	struct ace_notif cmd_ack;
> > +	/* command response */
> > +	struct ace_notif cmd_response;
> > +	/* used to wait for command ack and response */
> > +	struct completion cmd_completion;
> > +	/* lock used to prevent multiple call to ace */
> > +	struct mutex lock;
> > +
> > +	/* used to construct command */
> > +	u16 firmware_id;
> > +
> > +	/* runtime PM link from ace to csi */
> > +	struct device_link *csi_link;
> > +	/* runtime PM link from ace to sensor */
> > +	struct device_link *sensor_link;
> > +};
> > +
> > +static inline void init_cmd_hdr(struct ace_cmd_hdr *hdr) {
> > +	memset(hdr, 0, sizeof(struct ace_cmd_hdr));
> > +
> > +	hdr->type = ACE_CMD_SET;
> > +	hdr->msg_tgt = ACE_DRV_MSG;
> > +	hdr->init_block = ACE_CMD_INIT_BLOCK;
> > +	hdr->final_block = ACE_CMD_FINAL_BLOCK; }
> > +
> > +static int construct_command(struct mei_ace *ace, struct ace_cmd *cmd,
> > +			     enum ace_cmd_id cmd_id)
> > +{
> > +	union ace_cmd_param *param = &cmd->param;
> > +	struct ace_cmd_hdr *hdr = &cmd->hdr;
> > +
> > +	init_cmd_hdr(hdr);
> > +
> > +	hdr->cmd_id = cmd_id;
> > +	switch (cmd_id) {
> > +	case ACE_GET_FW_ID:
> > +		param->uuid = ACE_GET_FW_ID_UUID;
> > +		hdr->param_size = sizeof(param->uuid);
> > +		break;
> > +	case ACE_SWITCH_CAMERA_TO_IVSC:
> > +		param->param = 0;
> > +		hdr->firmware_id = ace->firmware_id;
> > +		hdr->param_size = sizeof(param->param);
> > +		break;
> > +	case ACE_SWITCH_CAMERA_TO_HOST:
> > +		hdr->firmware_id = ace->firmware_id;
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +
> > +	return hdr->param_size + sizeof(cmd->hdr); }
> > +
> > +/* send a command to firmware and mutex must be held by caller */
> > +static int mei_ace_send(struct mei_ace *ace, struct ace_cmd *cmd,
> > +			size_t len, bool only_ack)
> > +{
> > +	union ace_notif_hdr *resp_hdr = &ace->cmd_response.hdr;
> > +	union ace_notif_hdr *ack_hdr = &ace->cmd_ack.hdr;
> > +	struct ace_cmd_hdr *cmd_hdr = &cmd->hdr;
> > +	int ret;
> > +
> > +	reinit_completion(&ace->cmd_completion);
> > +
> > +	ret = mei_cldev_send(ace->cldev, (u8 *)cmd, len);
> > +	if (ret < 0)
> > +		goto out;
> > +
> > +	ret = wait_for_completion_killable_timeout(&ace->cmd_completion,
> > +						   ACE_CMD_TIMEOUT);
> > +	if (ret < 0) {
> > +		goto out;
> > +	} else if (!ret) {
> > +		ret = -ETIMEDOUT;
> > +		goto out;
> > +	}
> > +
> > +	if (ack_hdr->ack.cmd_id != cmd_hdr->cmd_id) {
> > +		ret = -EINVAL;
> > +		goto out;
> > +	}
> > +
> > +	/* command ack status */
> > +	ret = ack_hdr->ack.status;
> > +	if (ret) {
> > +		ret = -EIO;
> > +		goto out;
> > +	}
> > +
> > +	if (only_ack)
> > +		goto out;
> > +
> > +	ret = wait_for_completion_killable_timeout(&ace->cmd_completion,
> > +						   ACE_CMD_TIMEOUT);
> > +	if (ret < 0) {
> > +		goto out;
> > +	} else if (!ret) {
> > +		ret = -ETIMEDOUT;
> > +		goto out;
> > +	}
> > +
> > +	if (resp_hdr->response.cmd_id != cmd_hdr->cmd_id)
> > +		ret = -EINVAL;
> > +
> > +out:
> > +	return ret;
> > +}
> > +
> > +static int ace_set_camera_owner(struct mei_ace *ace, enum
> > +ace_camera_owner owner) {
> > +	enum ace_cmd_id cmd_id;
> > +	struct ace_cmd cmd;
> > +	int cmd_size;
> > +	int ret;
> > +
> > +	if (owner == ACE_CAMERA_IVSC)
> > +		cmd_id = ACE_SWITCH_CAMERA_TO_IVSC;
> > +	else
> > +		cmd_id = ACE_SWITCH_CAMERA_TO_HOST;
> > +
> > +	mutex_lock(&ace->lock);
> > +
> > +	cmd_size = construct_command(ace, &cmd, cmd_id);
> 
> There doesn't seem to be a need to take the lock during construct_command().
> Maybe just during mei_ace_send()? You could also acquire and release the lock
> there I guess.

Thanks
> 
> > +	if (cmd_size >= 0)
> > +		ret = mei_ace_send(ace, &cmd, cmd_size, false);
> > +	else
> > +		ret = cmd_size;
> > +	mutex_unlock(&ace->lock);
> > +
> > +	return ret;
> > +}
> > +
> > +/* the first command downloaded to firmware */ static inline int
> > +ace_get_firmware_id(struct mei_ace *ace) {
> > +	struct ace_cmd cmd;
> > +	int cmd_size;
> > +	int ret;
> > +
> > +	cmd_size = construct_command(ace, &cmd, ACE_GET_FW_ID);
> > +	if (cmd_size >= 0)
> > +		ret = mei_ace_send(ace, &cmd, cmd_size, true);
> > +	else
> > +		ret = cmd_size;
> > +
> > +	return ret;
> > +}
> > +
> > +static void handle_command_response(struct mei_ace *ace,
> > +				    struct ace_notif *resp, int len) {
> > +	union ace_notif_hdr *hdr = &resp->hdr;
> > +
> > +	switch (hdr->response.cmd_id) {
> > +	case ACE_SWITCH_CAMERA_TO_IVSC:
> > +	case ACE_SWITCH_CAMERA_TO_HOST:
> > +		memcpy(&ace->cmd_response, resp, len);
> > +		complete(&ace->cmd_completion);
> > +		break;
> > +	case ACE_GET_FW_ID:
> > +		break;
> > +	default:
> > +		break;
> > +	}
> > +}
> > +
> > +static void handle_command_ack(struct mei_ace *ace,
> > +			       struct ace_notif *ack, int len) {
> > +	union ace_notif_hdr *hdr = &ack->hdr;
> > +
> > +	switch (hdr->ack.cmd_id) {
> > +	case ACE_GET_FW_ID:
> > +		ace->firmware_id = ack->cont.firmware_id;
> > +		fallthrough;
> > +	case ACE_SWITCH_CAMERA_TO_IVSC:
> > +	case ACE_SWITCH_CAMERA_TO_HOST:
> > +		memcpy(&ace->cmd_ack, ack, len);
> > +		complete(&ace->cmd_completion);
> > +		break;
> > +	default:
> > +		break;
> > +	}
> > +}
> > +
> > +/* callback for receive */
> > +static void mei_ace_rx(struct mei_cl_device *cldev) {
> > +	struct mei_ace *ace = mei_cldev_get_drvdata(cldev);
> > +	struct ace_notif event;
> > +	union ace_notif_hdr *hdr = &event.hdr;
> > +	int ret;
> > +
> > +	ret = mei_cldev_recv(cldev, (u8 *)&event, sizeof(event));
> > +	if (ret < 0) {
> > +		dev_err(&cldev->dev, "recv error: %d\n", ret);
> > +		return;
> > +	}
> > +
> > +	if (hdr->event.ack) {
> > +		handle_command_ack(ace, &event, ret);
> > +		return;
> > +	}
> > +
> > +	switch (hdr->event.event_type) {
> > +	case ACE_CMD_RESPONSE:
> > +		handle_command_response(ace, &event, ret);
> > +		break;
> > +	case ACE_FW_READY:
> > +		/*
> > +		 * firmware ready notification sent to driver
> > +		 * after HECI client connected with firmware.
> > +		 */
> > +		dev_dbg(&cldev->dev, "firmware ready\n");
> > +		break;
> > +	default:
> > +		break;
> > +	}
> > +}
> > +
> > +static int mei_ace_setup_dev_link(struct mei_ace *ace) {
> > +	struct device *dev = &ace->cldev->dev;
> > +	uuid_le uuid = MEI_CSI_UUID;
> > +	struct acpi_device *adev;
> > +	struct device *csi_dev;
> > +	char name[64];
> > +
> > +	sprintf(name, "%s-%pUl", dev_name(dev->parent), &uuid);
> 
> snprintf(), please.
Thanks
> 
> > +	csi_dev = device_find_child_by_name(dev->parent, name);
> > +	if (!csi_dev)
> > +		return -EPROBE_DEFER;
> > +
> > +	/* sensor's ACPI _DEP is mei bus device */
> > +	adev = acpi_dev_get_next_consumer_dev(ACPI_COMPANION(dev-
> >parent),
> > +NULL);
> 
> Nice.
> 
> Please also run this on the patchset:
> 
> 	./scripts/checkpatch.pl --strict --max-line-length=80
ack
> 
> > +	if (!adev)
> > +		return -EPROBE_DEFER;
> > +
> > +	/* setup link between mei_ace and mei_csi */
> > +	ace->csi_link = device_link_add(csi_dev, dev,
> > +					DL_FLAG_PM_RUNTIME |
> DL_FLAG_RPM_ACTIVE);
> > +
> > +	/* setup link between mei_ace and sensor */
> > +	ace->sensor_link = device_link_add(&adev->dev, dev,
> > +					   DL_FLAG_PM_RUNTIME |
> DL_FLAG_RPM_ACTIVE);
> 
> device_link_add() may fail.
Thanks
> 
> > +
> > +	return 0;
> > +}
> > +
> > +static int mei_ace_probe(struct mei_cl_device *cldev,
> > +			 const struct mei_cl_device_id *id) {
> > +	struct device *dev = &cldev->dev;
> > +	struct mei_ace *ace;
> > +	int ret;
> > +
> > +	ace = devm_kzalloc(dev, sizeof(struct mei_ace), GFP_KERNEL);
> > +	if (!ace)
> > +		return -ENOMEM;
> > +
> > +	ace->cldev = cldev;
> > +
> > +	ret = mei_ace_setup_dev_link(ace);
> > +	if (ret)
> > +		return ret;
> > +
> > +	mutex_init(&ace->lock);
> > +	init_completion(&ace->cmd_completion);
> > +
> > +	mei_cldev_set_drvdata(cldev, ace);
> > +
> > +	ret = mei_cldev_enable(cldev);
> > +	if (ret < 0) {
> > +		dev_err(dev, "mei_cldev_enable failed: %d\n", ret);
> > +		goto destroy_mutex;
> > +	}
> > +
> > +	ret = mei_cldev_register_rx_cb(cldev, mei_ace_rx);
> > +	if (ret) {
> > +		dev_err(dev, "event cb registration failed: %d\n", ret);
> > +		goto err_disable;
> > +	}
> > +
> > +	ret = ace_get_firmware_id(ace);
> > +	if (ret) {
> > +		dev_err(dev, "get firmware id failed: %d\n", ret);
> > +		goto err_disable;
> > +	}
> > +
> > +	pm_runtime_set_active(dev);
> > +	pm_runtime_enable(dev);
> > +
> > +	acpi_dev_clear_dependencies(ACPI_COMPANION(dev->parent));
> > +
> > +	return 0;
> > +
> > +err_disable:
> > +	mei_cldev_disable(cldev);
> > +destroy_mutex:
> > +	mutex_destroy(&ace->lock);
> > +
> > +	device_link_del(ace->csi_link);
> > +	device_link_del(ace->sensor_link);
> > +
> > +	return ret;
> > +}
> > +
> > +static void mei_ace_remove(struct mei_cl_device *cldev) {
> > +	struct mei_ace *ace = mei_cldev_get_drvdata(cldev);
> > +
> > +	device_link_del(ace->csi_link);
> > +	device_link_del(ace->sensor_link);
> > +
> > +	pm_runtime_disable(&cldev->dev);
> > +	pm_runtime_set_suspended(&cldev->dev);
> > +
> > +	mutex_destroy(&ace->lock);
> > +}
> > +
> > +static int __maybe_unused mei_ace_runtime_suspend(struct device *dev)
> > +{
> > +	struct mei_ace *ace = dev_get_drvdata(dev);
> > +
> > +	return ace_set_camera_owner(ace, ACE_SWITCH_CAMERA_TO_IVSC); }
> > +
> > +static int __maybe_unused mei_ace_runtime_resume(struct device *dev)
> > +{
> > +	struct mei_ace *ace = dev_get_drvdata(dev);
> > +
> > +	return ace_set_camera_owner(ace,
> ACE_SWITCH_CAMERA_TO_HOST); }
> > +
> > +static const struct dev_pm_ops mei_ace_pm_ops = {
> > +	SET_RUNTIME_PM_OPS(mei_ace_runtime_suspend,
> > +			   mei_ace_runtime_resume,
> > +			   NULL)
> > +};
> > +
> > +#define MEI_ACE_UUID UUID_LE(0x5DB76CF6, 0x0A68, 0x4ED6, \
> > +			     0x9B, 0x78, 0x03, 0x61, 0x63, 0x5E, 0x24, 0x47)
> > +
> > +static const struct mei_cl_device_id mei_ace_tbl[] = {
> > +	{ MEI_ACE_DRIVER_NAME, MEI_ACE_UUID, MEI_CL_VERSION_ANY },
> > +
> > +	/* required last entry */
> > +	{ }
> > +};
> > +MODULE_DEVICE_TABLE(mei, mei_ace_tbl);
> > +
> > +static struct mei_cl_driver mei_ace_driver = {
> > +	.id_table = mei_ace_tbl,
> > +	.name = MEI_ACE_DRIVER_NAME,
> > +
> > +	.probe = mei_ace_probe,
> > +	.remove = mei_ace_remove,
> > +
> > +	.driver = {
> > +		.pm = &mei_ace_pm_ops,
> > +	},
> > +};
> > +
> > +module_mei_cl_driver(mei_ace_driver);
> > +
> > +MODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>");
> > +MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>");
> > +MODULE_DESCRIPTION("Device driver for IVSC ACE");
> > +MODULE_LICENSE("GPL");
> 
> --
> Kind regards,
> 
> Sakari Ailus

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v3 1/3] media: pci: intel: ivsc: Add CSI submodule
  2023-03-30  3:58     ` Wu, Wentong
@ 2023-03-30  8:23       ` Sakari Ailus
  0 siblings, 0 replies; 26+ messages in thread
From: Sakari Ailus @ 2023-03-30  8:23 UTC (permalink / raw)
  To: Wu, Wentong
  Cc: hdegoede, djrscally, laurent.pinchart, linux-media, bingbu.cao,
	Wang, Zhifeng, Ye, Xiang, Qiu, Tian Shu

Hi Wentong,

On Thu, Mar 30, 2023 at 03:58:10AM +0000, Wu, Wentong wrote:
> Hi Sakari,
> 
> Thanks
> 
> > -----Original Message-----
> > From: Sakari Ailus <sakari.ailus@linux.intel.com>
> > Sent: Wednesday, March 29, 2023 4:49 PM
> > 
> > Hi Wentong,
> > 
> > On Mon, Mar 27, 2023 at 02:23:06PM +0800, Wentong Wu wrote:
> > > CSI is a submodule of IVSC which can route camera sensor data to the
> > > outbound MIPI CSI-2 interface.
> > >
> > > The interface communicating with firmware is via MEI. There is a
> > > separate MEI UUID, which this driver uses to enumerate.
> > >
> > > To route camera sensor data to host, the information of link frequency
> > > and number of data lanes is sent to firmware by sending MEI command
> > > when starting stream.
> > >
> > > CSI also provides a privacy mode. When privacy mode is turned on,
> > > camera sensor can't be used. This means that both IVSC and host Image
> > > Processing Unit(IPU) can't get image data. And when this mode is
> > > turned on, host Image Processing Unit(IPU) driver is informed via v4l2
> > > control callback, so that user can be notified.
> > >
> > > Signed-off-by: Wentong Wu <wentong.wu@intel.com>
> > > ---
> > >  drivers/media/pci/Kconfig                 |   1 +
> > >  drivers/media/pci/intel/Makefile          |   2 +
> > >  drivers/media/pci/intel/ivsc/Kconfig      |  12 +
> > >  drivers/media/pci/intel/ivsc/Makefile     |   7 +
> > >  drivers/media/pci/intel/ivsc/csi_bridge.c | 332 +++++++++++++
> > > drivers/media/pci/intel/ivsc/csi_bridge.h | 122 +++++
> > >  drivers/media/pci/intel/ivsc/mei_csi.c    | 775
> > ++++++++++++++++++++++++++++++
> > >  7 files changed, 1251 insertions(+)
> > >  create mode 100644 drivers/media/pci/intel/ivsc/Kconfig
> > >  create mode 100644 drivers/media/pci/intel/ivsc/Makefile
> > >  create mode 100644 drivers/media/pci/intel/ivsc/csi_bridge.c
> > >  create mode 100644 drivers/media/pci/intel/ivsc/csi_bridge.h
> > >  create mode 100644 drivers/media/pci/intel/ivsc/mei_csi.c
> > 
> > I'm commenting just mei_csi this time...
> > 
> > > diff --git a/drivers/media/pci/intel/ivsc/mei_csi.c
> > > b/drivers/media/pci/intel/ivsc/mei_csi.c
> > > new file mode 100644
> > > index 0000000..6a01537
> > > --- /dev/null
> > > +++ b/drivers/media/pci/intel/ivsc/mei_csi.c
> > > @@ -0,0 +1,775 @@
> > > +// SPDX-License-Identifier: GPL-2.0-only
> > > +/*
> > > + * Copyright (C) 2023 Intel Corporation. All rights reserved.
> > > + * Intel Visual Sensing Controller CSI Linux driver  */
> > > +
> > > +/*
> > > + * To set ownership of CSI-2 link and to configure CSI-2 link, there
> > > + * are specific commands, which are sent via MEI protocol. The send
> > > + * command function uses "completion" as a synchronization mechanism.
> > > + * The response for command is received via a mei callback which
> > > +wakes
> > > + * up the caller. There can be only one outstanding command at a time.
> > > + */
> > > +
> > > +#include <linux/completion.h>
> > > +#include <linux/delay.h>
> > > +#include <linux/kernel.h>
> > > +#include <linux/math64.h>
> > > +#include <linux/mei_cl_bus.h>
> > > +#include <linux/module.h>
> > > +#include <linux/mutex.h>
> > > +#include <linux/pm_runtime.h>
> > > +#include <linux/slab.h>
> > > +#include <linux/units.h>
> > > +#include <linux/uuid.h>
> > > +#include <linux/workqueue.h>
> > > +
> > > +#include <media/v4l2-async.h>
> > > +#include <media/v4l2-ctrls.h>
> > > +#include <media/v4l2-fwnode.h>
> > > +#include <media/v4l2-subdev.h>
> > > +
> > > +#define MEI_CSI_DRIVER_NAME "ivsc_csi"
> > > +
> > > +/* the 5s used here is based on experiment */ #define CSI_CMD_TIMEOUT
> > > +(5 * HZ)
> > > +/* to setup CSI-2 link an extra delay needed and determined
> > > +experimentally */ #define CSI_FW_READY_DELAY_MS 100
> > > +/* link frequency unit is 100kHz */
> > > +#define CSI_LINK_FREQ(x) ((u32)(div_u64(x, 100 * HZ_PER_KHZ)))
> > > +
> > > +/*
> > > + * identify the command id supported by firmware
> > > + * IPC, as well as the privacy notification id
> > > + * used when processing privacy event.
> > > + */
> > > +enum csi_cmd_id {
> > > +	/* used to set csi ownership */
> > > +	CSI_SET_OWNER = 0,
> > > +
> > > +	/* used to configure CSI-2 link */
> > > +	CSI_SET_CONF = 2,
> > > +
> > > +	/* privacy notification id used when privacy state changes */
> > > +	CSI_PRIVACY_NOTIF = 6,
> > > +};
> > > +
> > > +/* CSI-2 link ownership definition */ enum csi_link_owner {
> > > +	CSI_LINK_IVSC,
> > > +	CSI_LINK_HOST,
> > > +};
> > > +
> > > +/* privacy status definition */
> > > +enum ivsc_privacy_status {
> > > +	CSI_PRIVACY_OFF,
> > > +	CSI_PRIVACY_ON,
> > > +	CSI_PRIVACY_MAX,
> > > +};
> > > +
> > > +enum csi_pads {
> > > +	CSI_PAD_SOURCE,
> > > +	CSI_PAD_SINK,
> > > +	CSI_NUM_PADS
> > > +};
> > > +
> > > +/* configuration of the CSI-2 link between host and IVSC */ struct
> > > +csi_link_cfg {
> > > +	/* number of data lanes used on the CSI-2 link */
> > > +	u32 nr_of_lanes;
> > > +
> > > +	/* frequency of the CSI-2 link */
> > > +	u32 link_freq;
> > > +
> > > +	/* for future use */
> > > +	u32 rsvd[2];
> > > +} __packed;
> > > +
> > > +/* CSI command structure */
> > > +struct csi_cmd {
> > > +	u32 cmd_id;
> > > +	union _cmd_param {
> > > +		u32 param;
> > > +		struct csi_link_cfg conf;
> > > +	} param;
> > > +} __packed;
> > > +
> > > +/* CSI notification structure */
> > > +struct csi_notif {
> > > +	u32 cmd_id;
> > > +	int status;
> > > +	union _resp_cont {
> > > +		u32 cont;
> > > +		struct csi_link_cfg conf;
> > > +	} cont;
> > > +} __packed;
> > > +
> > > +struct mei_csi {
> > > +	struct mei_cl_device *cldev;
> > > +
> > > +	/* command response */
> > > +	struct csi_notif cmd_response;
> > > +	/* used to wait for command response from firmware */
> > > +	struct completion cmd_completion;
> > > +	/* protect command download */
> > > +	struct mutex lock;
> > > +
> > > +	struct v4l2_subdev subdev;
> > > +	struct v4l2_subdev *remote;
> > > +	struct v4l2_async_notifier notifier;
> > > +	struct v4l2_ctrl_handler ctrl_handler;
> > > +	struct v4l2_ctrl *privacy_ctrl;
> > > +	unsigned int remote_pad;
> > > +	/* start streaming or not */
> > > +	int streaming;
> > > +
> > > +	struct media_pad pads[CSI_NUM_PADS];
> > > +	struct v4l2_mbus_framefmt format_mbus[CSI_NUM_PADS];
> > > +
> > > +	/* number of data lanes used on the CSI-2 link */
> > > +	u32 nr_of_lanes;
> > > +	/* frequency of the CSI-2 link */
> > > +	u64 link_freq;
> > > +
> > > +	/* privacy status */
> > > +	enum ivsc_privacy_status status;
> > > +};
> > > +
> > > +static const struct v4l2_mbus_framefmt mei_csi_format_mbus_default = {
> > > +	.width = 1,
> > > +	.height = 1,
> > > +	.code = MEDIA_BUS_FMT_Y8_1X8,
> > > +	.field = V4L2_FIELD_NONE,
> > > +};
> > > +
> > > +int csi_bridge_init(struct mei_cl_device *csi_dev);
> > > +
> > > +static inline struct mei_csi *notifier_to_csi(struct
> > > +v4l2_async_notifier *n) {
> > > +	return container_of(n, struct mei_csi, notifier); }
> > > +
> > > +static inline struct mei_csi *sd_to_csi(struct v4l2_subdev *sd) {
> > > +	return container_of(sd, struct mei_csi, subdev); }
> > > +
> > > +/* send a command to firmware and mutex must be held by caller */
> > > +static int mei_csi_send(struct mei_csi *csi, u8 *buf, size_t len) {
> > > +	struct csi_cmd *cmd = (struct csi_cmd *)buf;
> > > +	int ret;
> > > +
> > > +	reinit_completion(&csi->cmd_completion);
> > > +
> > > +	ret = mei_cldev_send(csi->cldev, buf, len);
> > > +	if (ret < 0)
> > > +		goto out;
> > > +
> > > +	ret = wait_for_completion_killable_timeout(&csi->cmd_completion,
> > > +						   CSI_CMD_TIMEOUT);
> > > +	if (ret < 0) {
> > > +		goto out;
> > > +	} else if (!ret) {
> > > +		ret = -ETIMEDOUT;
> > > +		goto out;
> > > +	}
> > > +
> > > +	/* command response status */
> > > +	ret = csi->cmd_response.status;
> > > +	if (ret) {
> > > +		ret = -EINVAL;
> > > +		goto out;
> > > +	}
> > > +
> > > +	if (csi->cmd_response.cmd_id != cmd->cmd_id)
> > > +		ret = -EINVAL;
> > > +
> > > +out:
> > > +	return ret;
> > > +}
> > > +
> > > +/* set CSI-2 link ownership */
> > > +static int csi_set_link_owner(struct mei_csi *csi, enum
> > > +csi_link_owner owner) {
> > > +	struct csi_cmd cmd = { 0 };
> > > +	size_t cmd_size;
> > > +	int ret;
> > > +
> > > +	cmd.cmd_id = CSI_SET_OWNER;
> > > +	cmd.param.param = owner;
> > > +	cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.param);
> > 
> > In some cases you're using memset and in others not. If you don't need memset,
> > I'd prefer assigning the fields in variable declaration instead.
> 
> The declaration will be like below, but it will break reverse x-mas tree style.

Have I asked to change your e-mail client settings?

> 
> struct csi_cmd cmd = { 0 };
> size_t cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.param);
> int ret;

It's not a problem if you have a dependency.

> 
> 
> > 
> > > +
> > > +	mutex_lock(&csi->lock);
> > > +
> > > +	ret = mei_csi_send(csi, (u8 *)&cmd, cmd_size);
> > > +
> > > +	mutex_unlock(&csi->lock);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +/* configure CSI-2 link between host and IVSC */ static int
> > > +csi_set_link_cfg(struct mei_csi *csi) {
> > > +	struct csi_cmd cmd = { 0 };
> > > +	size_t cmd_size;
> > > +	int ret;
> > > +
> > > +	cmd.cmd_id = CSI_SET_CONF;
> > > +	cmd.param.conf.nr_of_lanes = csi->nr_of_lanes;
> > > +	cmd.param.conf.link_freq = CSI_LINK_FREQ(csi->link_freq);
> > > +	cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.conf);
> > 
> > Ditto.
> > 
> > > +
> > > +	mutex_lock(&csi->lock);
> > > +
> > > +	ret = mei_csi_send(csi, (u8 *)&cmd, cmd_size);
> > > +	/*
> > > +	 * wait configuration ready if download success. placing
> > > +	 * delay under mutex is to make sure current command flow
> > > +	 * completed before starting a possible new one.
> > > +	 */
> > > +	if (!ret)
> > > +		msleep(CSI_FW_READY_DELAY_MS);
> > > +
> > > +	mutex_unlock(&csi->lock);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +/* callback for receive */
> > > +static void mei_csi_rx(struct mei_cl_device *cldev) {
> > > +	struct mei_csi *csi = mei_cldev_get_drvdata(cldev);
> > > +	struct csi_notif notif = { 0 };
> > > +	int ret;
> > > +
> > > +	ret = mei_cldev_recv(cldev, (u8 *)&notif, sizeof(notif));
> > > +	if (ret < 0) {
> > > +		dev_err(&cldev->dev, "recv error: %d\n", ret);
> > > +		return;
> > > +	}
> > > +
> > > +	switch (notif.cmd_id) {
> > > +	case CSI_PRIVACY_NOTIF:
> > > +		if (notif.cont.cont < CSI_PRIVACY_MAX) {
> > > +			csi->status = notif.cont.cont;
> > > +			v4l2_ctrl_s_ctrl(csi->privacy_ctrl, csi->status);
> > > +		}
> > > +		break;
> > > +	case CSI_SET_OWNER:
> > > +	case CSI_SET_CONF:
> > > +		memcpy(&csi->cmd_response, &notif, ret);
> > > +
> > > +		complete(&csi->cmd_completion);
> > > +		break;
> > > +	default:
> > > +		break;
> > > +	}
> > > +}
> > > +
> > > +static int mei_csi_pre_streamon(struct v4l2_subdev *sd, u32 flags) {
> > > +	struct v4l2_querymenu qm = { .id = V4L2_CID_LINK_FREQ, };
> > > +	struct v4l2_mbus_config mbus_config = { 0 };
> > > +	struct mei_csi *csi = sd_to_csi(sd);
> > > +	struct v4l2_ctrl *ctrl;
> > > +	int ret = 0;
> > > +
> > > +	if (!csi->remote)
> > > +		return -ENODEV;
> > > +
> > > +	ret = v4l2_subdev_call(csi->remote, pad, get_mbus_config,
> > > +			       csi->remote_pad, &mbus_config);
> > > +	if (ret)
> > > +		return ret;
> > 
> > You're already parsing the endpoint in probe(). Do you need this, assuming the
> > sensor has a static lane configuration? Virtually all do.
> 
> When parsing remote endpoint, just get num_data_lanes by
> v4l2_fwnode_endpoint_alloc_parse?

Yes.

> 
> > 
> > Could this all be done via the s_stream() op instead?
> 
> We don't need csi2 link freq and lane number when s_steam(0)

You can set the VOLATILE flag on the control and then query the upstream
sub-device in the g_ctrl callback.

> 
> > 
> > > +
> > > +	if (mbus_config.type != V4L2_MBUS_CSI2_DPHY)
> > > +		return -EINVAL;
> > > +
> > > +	csi->nr_of_lanes = mbus_config.bus.mipi_csi2.num_data_lanes;
> > > +
> > > +	ctrl = v4l2_ctrl_find(csi->remote->ctrl_handler, V4L2_CID_LINK_FREQ);
> > > +	if (!ctrl)
> > > +		return -EINVAL;
> > > +	qm.index = v4l2_ctrl_g_ctrl(ctrl);
> > > +
> > > +	ret = v4l2_querymenu(csi->remote->ctrl_handler, &qm);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	csi->link_freq = qm.value;
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static int mei_csi_set_stream(struct v4l2_subdev *sd, int enable) {
> > > +	struct mei_csi *csi = sd_to_csi(sd);
> > > +	int ret = 0;
> > > +
> > > +	if (enable && csi->streaming == 0) {
> > > +		/* switch CSI-2 link to host */
> > > +		ret = csi_set_link_owner(csi, CSI_LINK_HOST);
> > > +		if (ret < 0)
> > > +			goto err;
> > > +
> > > +		/* configure CSI-2 link */
> > > +		ret = csi_set_link_cfg(csi);
> > > +		if (ret < 0)
> > > +			goto err;
> > > +
> > > +		ret = v4l2_subdev_call(csi->remote, video, s_stream, 1);
> > > +		if (ret)
> > > +			goto err;
> > > +	} else if (!enable && csi->streaming == 1) {
> > > +		v4l2_subdev_call(csi->remote, video, s_stream, 0);
> > > +
> > > +		/* switch CSI-2 link to IVSC */
> > > +		ret = csi_set_link_owner(csi, CSI_LINK_IVSC);
> > > +		if (ret < 0)
> > > +			goto err;
> > 
> > I'd complain (e.g. dev_warn()) but return zero in this case. There's not much if
> > anything the caller can do with this.
> 
> ack
> > 
> > > +	}
> > > +
> > > +	csi->streaming = enable;
> > > +
> > > +err:
> > > +	return ret;
> > > +}
> > > +
> > > +static struct v4l2_mbus_framefmt *
> > > +mei_csi_get_pad_format(struct v4l2_subdev *sd,
> > > +		       struct v4l2_subdev_state *sd_state,
> > > +		       unsigned int pad, u32 which) {
> > > +	struct mei_csi *csi = sd_to_csi(sd);
> > > +
> > > +	switch (which) {
> > > +	case V4L2_SUBDEV_FORMAT_TRY:
> > > +		return v4l2_subdev_get_try_format(sd, sd_state, pad);
> > > +	case V4L2_SUBDEV_FORMAT_ACTIVE:
> > > +		return &csi->format_mbus[pad];
> > > +	default:
> > > +		return NULL;
> > > +	}
> > > +}
> > > +
> > > +static int mei_csi_init_cfg(struct v4l2_subdev *sd,
> > > +			    struct v4l2_subdev_state *sd_state) {
> > > +	struct v4l2_mbus_framefmt *mbusformat;
> > > +	struct mei_csi *csi = sd_to_csi(sd);
> > > +	unsigned int i;
> > > +
> > > +	mutex_lock(&csi->lock);
> > > +
> > > +	for (i = 0; i < sd->entity.num_pads; i++) {
> > > +		mbusformat = v4l2_subdev_get_try_format(sd, sd_state, i);
> > > +		*mbusformat = mei_csi_format_mbus_default;
> > > +	}
> > > +
> > > +	mutex_unlock(&csi->lock);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int mei_csi_get_fmt(struct v4l2_subdev *sd,
> > > +			   struct v4l2_subdev_state *sd_state,
> > > +			   struct v4l2_subdev_format *format) {
> > > +	struct v4l2_mbus_framefmt *mbusformat;
> > > +	struct mei_csi *csi = sd_to_csi(sd);
> > > +
> > > +	mutex_lock(&csi->lock);
> > > +
> > > +	mbusformat = mei_csi_get_pad_format(sd, sd_state, format->pad,
> > > +					    format->which);
> > > +	if (mbusformat)
> > > +		format->format = *mbusformat;
> > > +
> > > +	mutex_unlock(&csi->lock);
> > > +
> > > +	return 0;
> > > +}
> > 
> > It'd be nice to have also enum_mbus_code support. Video mux of course didn't
> > have this either. Something that could be done in the framework, including
> > validating the mbus format.
> 
> Sorry, you mean the default MEDIA_BUS_FMT_Y8_1X8?

Any format that can be supported by the device.

The video mux should have that, too. I'll see if this would fit to the
framework neatly somehow.

> 
> > 
> > > +
> > > +static int mei_csi_set_fmt(struct v4l2_subdev *sd,
> > > +			   struct v4l2_subdev_state *sd_state,
> > > +			   struct v4l2_subdev_format *format) {
> > > +	struct v4l2_mbus_framefmt *source_mbusformat;
> > > +	struct v4l2_mbus_framefmt *mbusformat;
> > > +	struct mei_csi *csi = sd_to_csi(sd);
> > > +	struct media_pad *pad;
> > > +
> > > +	mbusformat = mei_csi_get_pad_format(sd, sd_state, format->pad,
> > > +					    format->which);
> > > +	if (!mbusformat)
> > > +		return -EINVAL;
> > > +
> > > +	source_mbusformat = mei_csi_get_pad_format(sd, sd_state,
> > > +						   CSI_PAD_SOURCE,
> > > +						   format->which);
> > > +	if (!source_mbusformat)
> > > +		return -EINVAL;
> > > +
> > > +	v4l_bound_align_image(&format->format.width, 1, 65536, 0,
> > > +			      &format->format.height, 1, 65536, 0, 0);
> > > +
> > > +	switch (format->format.code) {
> > > +	case MEDIA_BUS_FMT_RGB444_1X12:
> > > +	case MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE:
> > > +	case MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE:
> > > +	case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE:
> > > +	case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE:
> > > +	case MEDIA_BUS_FMT_RGB565_1X16:
> > > +	case MEDIA_BUS_FMT_BGR565_2X8_BE:
> > > +	case MEDIA_BUS_FMT_BGR565_2X8_LE:
> > > +	case MEDIA_BUS_FMT_RGB565_2X8_BE:
> > > +	case MEDIA_BUS_FMT_RGB565_2X8_LE:
> > > +	case MEDIA_BUS_FMT_RGB666_1X18:
> > > +	case MEDIA_BUS_FMT_RBG888_1X24:
> > > +	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> > > +	case MEDIA_BUS_FMT_BGR888_1X24:
> > > +	case MEDIA_BUS_FMT_GBR888_1X24:
> > > +	case MEDIA_BUS_FMT_RGB888_1X24:
> > > +	case MEDIA_BUS_FMT_RGB888_2X12_BE:
> > > +	case MEDIA_BUS_FMT_RGB888_2X12_LE:
> > > +	case MEDIA_BUS_FMT_ARGB8888_1X32:
> > > +	case MEDIA_BUS_FMT_RGB888_1X32_PADHI:
> > > +	case MEDIA_BUS_FMT_RGB101010_1X30:
> > > +	case MEDIA_BUS_FMT_RGB121212_1X36:
> > > +	case MEDIA_BUS_FMT_RGB161616_1X48:
> > > +	case MEDIA_BUS_FMT_Y8_1X8:
> > > +	case MEDIA_BUS_FMT_UV8_1X8:
> > > +	case MEDIA_BUS_FMT_UYVY8_1_5X8:
> > > +	case MEDIA_BUS_FMT_VYUY8_1_5X8:
> > > +	case MEDIA_BUS_FMT_YUYV8_1_5X8:
> > > +	case MEDIA_BUS_FMT_YVYU8_1_5X8:
> > > +	case MEDIA_BUS_FMT_UYVY8_2X8:
> > > +	case MEDIA_BUS_FMT_VYUY8_2X8:
> > > +	case MEDIA_BUS_FMT_YUYV8_2X8:
> > > +	case MEDIA_BUS_FMT_YVYU8_2X8:
> > > +	case MEDIA_BUS_FMT_Y10_1X10:
> > > +	case MEDIA_BUS_FMT_UYVY10_2X10:
> > > +	case MEDIA_BUS_FMT_VYUY10_2X10:
> > > +	case MEDIA_BUS_FMT_YUYV10_2X10:
> > > +	case MEDIA_BUS_FMT_YVYU10_2X10:
> > > +	case MEDIA_BUS_FMT_Y12_1X12:
> > > +	case MEDIA_BUS_FMT_UYVY12_2X12:
> > > +	case MEDIA_BUS_FMT_VYUY12_2X12:
> > > +	case MEDIA_BUS_FMT_YUYV12_2X12:
> > > +	case MEDIA_BUS_FMT_YVYU12_2X12:
> > > +	case MEDIA_BUS_FMT_UYVY8_1X16:
> > > +	case MEDIA_BUS_FMT_VYUY8_1X16:
> > > +	case MEDIA_BUS_FMT_YUYV8_1X16:
> > > +	case MEDIA_BUS_FMT_YVYU8_1X16:
> > > +	case MEDIA_BUS_FMT_YDYUYDYV8_1X16:
> > > +	case MEDIA_BUS_FMT_UYVY10_1X20:
> > > +	case MEDIA_BUS_FMT_VYUY10_1X20:
> > > +	case MEDIA_BUS_FMT_YUYV10_1X20:
> > > +	case MEDIA_BUS_FMT_YVYU10_1X20:
> > > +	case MEDIA_BUS_FMT_VUY8_1X24:
> > > +	case MEDIA_BUS_FMT_YUV8_1X24:
> > > +	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> > > +	case MEDIA_BUS_FMT_UYVY12_1X24:
> > > +	case MEDIA_BUS_FMT_VYUY12_1X24:
> > > +	case MEDIA_BUS_FMT_YUYV12_1X24:
> > > +	case MEDIA_BUS_FMT_YVYU12_1X24:
> > > +	case MEDIA_BUS_FMT_YUV10_1X30:
> > > +	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> > > +	case MEDIA_BUS_FMT_AYUV8_1X32:
> > > +	case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
> > > +	case MEDIA_BUS_FMT_YUV12_1X36:
> > > +	case MEDIA_BUS_FMT_YUV16_1X48:
> > > +	case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
> > > +	case MEDIA_BUS_FMT_JPEG_1X8:
> > > +	case MEDIA_BUS_FMT_AHSV8888_1X32:
> > > +	case MEDIA_BUS_FMT_SBGGR8_1X8:
> > > +	case MEDIA_BUS_FMT_SGBRG8_1X8:
> > > +	case MEDIA_BUS_FMT_SGRBG8_1X8:
> > > +	case MEDIA_BUS_FMT_SRGGB8_1X8:
> > > +	case MEDIA_BUS_FMT_SBGGR10_1X10:
> > > +	case MEDIA_BUS_FMT_SGBRG10_1X10:
> > > +	case MEDIA_BUS_FMT_SGRBG10_1X10:
> > > +	case MEDIA_BUS_FMT_SRGGB10_1X10:
> > > +	case MEDIA_BUS_FMT_SBGGR12_1X12:
> > > +	case MEDIA_BUS_FMT_SGBRG12_1X12:
> > > +	case MEDIA_BUS_FMT_SGRBG12_1X12:
> > > +	case MEDIA_BUS_FMT_SRGGB12_1X12:
> > > +	case MEDIA_BUS_FMT_SBGGR14_1X14:
> > > +	case MEDIA_BUS_FMT_SGBRG14_1X14:
> > > +	case MEDIA_BUS_FMT_SGRBG14_1X14:
> > > +	case MEDIA_BUS_FMT_SRGGB14_1X14:
> > > +	case MEDIA_BUS_FMT_SBGGR16_1X16:
> > > +	case MEDIA_BUS_FMT_SGBRG16_1X16:
> > > +	case MEDIA_BUS_FMT_SGRBG16_1X16:
> > > +	case MEDIA_BUS_FMT_SRGGB16_1X16:
> > > +		break;
> > > +	default:
> > > +		format->format.code = MEDIA_BUS_FMT_Y8_1X8;
> > > +		break;
> > > +	}
> > > +	if (format->format.field == V4L2_FIELD_ANY)
> > > +		format->format.field = V4L2_FIELD_NONE;
> > > +
> > > +	mutex_lock(&csi->lock);
> > > +
> > > +	pad = &csi->pads[format->pad];
> > > +	if (pad->flags & MEDIA_PAD_FL_SOURCE)
> > > +		format->format = csi->format_mbus[CSI_PAD_SINK];
> > > +
> > > +	*mbusformat = format->format;
> > > +
> > > +	if (pad->flags & MEDIA_PAD_FL_SINK)
> > > +		*source_mbusformat = format->format;
> > > +
> > > +	mutex_unlock(&csi->lock);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static const struct v4l2_subdev_video_ops mei_csi_video_ops = {
> > > +	.s_stream = mei_csi_set_stream,
> > > +	.pre_streamon = mei_csi_pre_streamon, };
> > > +
> > > +static const struct v4l2_subdev_pad_ops mei_csi_pad_ops = {
> > > +	.init_cfg = mei_csi_init_cfg,
> > > +	.get_fmt = mei_csi_get_fmt,
> > > +	.set_fmt = mei_csi_set_fmt,
> > > +};
> > > +
> > > +static const struct v4l2_subdev_ops mei_csi_subdev_ops = {
> > > +	.video = &mei_csi_video_ops,
> > > +	.pad = &mei_csi_pad_ops,
> > > +};
> > > +
> > > +static const struct media_entity_operations mei_csi_entity_ops = {
> > > +	.link_validate = v4l2_subdev_link_validate, };
> > > +
> > > +static int mei_csi_notify_bound(struct v4l2_async_notifier *notifier,
> > > +				struct v4l2_subdev *subdev,
> > > +				struct v4l2_async_subdev *asd)
> > > +{
> > > +	struct mei_csi *csi = notifier_to_csi(notifier);
> > > +	int pad;
> > > +
> > > +	pad = media_entity_get_fwnode_pad(&subdev->entity, asd-
> > >match.fwnode,
> > > +					  MEDIA_PAD_FL_SOURCE);
> > > +	if (pad < 0)
> > > +		return pad;
> > > +
> > > +	csi->remote = subdev;
> > > +	csi->remote_pad = pad;
> > > +
> > > +	return media_create_pad_link(&subdev->entity, pad,
> > > +				     &csi->subdev.entity, 0,
> > > +				     MEDIA_LNK_FL_ENABLED |
> > > +				     MEDIA_LNK_FL_IMMUTABLE);
> > > +}
> > > +
> > > +static void mei_csi_notify_unbind(struct v4l2_async_notifier *notifier,
> > > +				  struct v4l2_subdev *subdev,
> > > +				  struct v4l2_async_subdev *asd)
> > > +{
> > > +	struct mei_csi *csi = notifier_to_csi(notifier);
> > > +
> > > +	csi->remote = NULL;
> > > +}
> > > +
> > > +static const struct v4l2_async_notifier_operations mei_csi_notify_ops = {
> > > +	.bound = mei_csi_notify_bound,
> > > +	.unbind = mei_csi_notify_unbind,
> > > +};
> > > +
> > > +static int mei_csi_init_control(struct mei_csi *csi) {
> > > +	v4l2_ctrl_handler_init(&csi->ctrl_handler, 1);
> > > +	csi->ctrl_handler.lock = &csi->lock;
> > > +
> > > +	csi->privacy_ctrl = v4l2_ctrl_new_std(&csi->ctrl_handler, NULL,
> > > +					      V4L2_CID_PRIVACY, 0, 1, 1, 0);
> > > +	if (csi->ctrl_handler.error)
> > > +		return csi->ctrl_handler.error;
> > 
> > The control should be made volatile here (flags |=

Read-only, I meant. Volatile is for controls that are not driven by
hardware events somehow.

> > V4L2_CTRL_FLAG_READ_ONLY) as it can't be changed by the user.
> 
> ack
> > 
> > > +
> > > +	csi->subdev.ctrl_handler = &csi->ctrl_handler;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int mei_csi_parse_firmware(struct mei_csi *csi) {
> > > +	struct v4l2_fwnode_endpoint v4l2_ep = {
> > > +		.bus_type = V4L2_MBUS_CSI2_DPHY,
> > > +	};
> > > +	struct device *dev = &csi->cldev->dev;
> > > +	struct v4l2_async_subdev *asd;
> > > +	struct fwnode_handle *fwnode;
> > > +	struct fwnode_handle *ep;
> > > +	int ret;
> > > +
> > > +	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0);
> > > +	if (!ep) {
> > > +		dev_err(dev, "not connected to subdevice\n");
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	ret = v4l2_fwnode_endpoint_parse(ep, &v4l2_ep);
> > > +	if (ret) {
> > > +		dev_err(dev, "could not parse v4l2 endpoint\n");
> > > +		fwnode_handle_put(ep);
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	fwnode = fwnode_graph_get_remote_endpoint(ep);
> > > +	fwnode_handle_put(ep);
> > > +
> > > +	v4l2_async_nf_init(&csi->notifier);
> > > +	csi->notifier.ops = &mei_csi_notify_ops;
> > > +
> > > +	asd = v4l2_async_nf_add_fwnode(&csi->notifier, fwnode,
> > > +				       struct v4l2_async_subdev);
> > > +	fwnode_handle_put(fwnode);
> > > +	if (IS_ERR(asd))
> > > +		return PTR_ERR(asd);
> > > +
> > > +	ret = v4l2_async_subdev_nf_register(&csi->subdev, &csi->notifier);
> > > +	if (ret)
> > > +		v4l2_async_nf_cleanup(&csi->notifier);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static int mei_csi_probe(struct mei_cl_device *cldev,
> > > +			 const struct mei_cl_device_id *id) {
> > > +	struct mei_csi *csi;
> > > +	int ret;
> > > +
> > > +	/*
> > > +	 * no connections to sensor are defined in firmware, try to
> > > +	 * build connections as software_nodes parsed from SSDB.
> > > +	 */
> > > +	ret = csi_bridge_init(cldev);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	csi = devm_kzalloc(&cldev->dev, sizeof(struct mei_csi), GFP_KERNEL);
> > > +	if (!csi)
> > > +		return -ENOMEM;
> > > +
> > > +	csi->cldev = cldev;
> > > +	mutex_init(&csi->lock);
> > > +	init_completion(&csi->cmd_completion);
> > > +
> > > +	mei_cldev_set_drvdata(cldev, csi);
> > > +
> > > +	ret = mei_cldev_enable(cldev);
> > > +	if (ret < 0) {
> > > +		dev_err(&cldev->dev, "mei_cldev_enable failed: %d\n", ret);
> > > +		goto destroy_mutex;
> > > +	}
> > > +
> > > +	ret = mei_cldev_register_rx_cb(cldev, mei_csi_rx);
> > > +	if (ret) {
> > > +		dev_err(&cldev->dev, "event cb registration failed: %d\n", ret);
> > > +		goto err_disable;
> > > +	}
> > > +
> > > +	ret = mei_csi_parse_firmware(csi);
> > > +	if (ret)
> > > +		goto err_disable;
> > > +
> > > +	csi->subdev.dev = &cldev->dev;
> > > +	v4l2_subdev_init(&csi->subdev, &mei_csi_subdev_ops);
> > > +	v4l2_set_subdevdata(&csi->subdev, csi);
> > > +	csi->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
> > > +	csi->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
> > > +	csi->subdev.entity.ops = &mei_csi_entity_ops;
> > > +
> > > +	ret = mei_csi_init_control(csi);
> > > +	if (ret)
> > > +		goto err_async;
> > 
> > You'll also need to call v4l2_ctrl_handler_free() if setting up the handler failed.
> > I.e. just change the label here and remove err_async label below.
> Thanks
> > 
> > > +
> > > +	csi->format_mbus[CSI_PAD_SOURCE] = mei_csi_format_mbus_default;
> > > +	csi->format_mbus[CSI_PAD_SINK] = mei_csi_format_mbus_default;
> > > +
> > > +	csi->pads[CSI_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
> > > +	csi->pads[CSI_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
> > > +	ret = media_entity_pads_init(&csi->subdev.entity, CSI_NUM_PADS,
> > > +				     csi->pads);
> > > +	if (ret)
> > > +		goto err_ctrl_handler;
> > > +
> > > +	ret = v4l2_subdev_init_finalize(&csi->subdev);
> > > +	if (ret < 0)
> > > +		goto err_entity;
> > > +
> > > +	ret = v4l2_async_register_subdev(&csi->subdev);
> > > +	if (ret < 0)
> > > +		goto err_subdev;
> > > +
> > > +	pm_runtime_enable(&cldev->dev);
> > > +
> > > +	return 0;
> > > +
> > > +err_subdev:
> > > +	v4l2_subdev_cleanup(&csi->subdev);
> > > +err_entity:
> > > +	media_entity_cleanup(&csi->subdev.entity);
> > > +err_ctrl_handler:
> > > +	v4l2_ctrl_handler_free(&csi->ctrl_handler);
> > > +err_async:
> > > +	v4l2_async_nf_unregister(&csi->notifier);
> > > +	v4l2_async_nf_cleanup(&csi->notifier);
> > > +err_disable:
> > > +	mei_cldev_disable(cldev);
> > 
> > It'd be nice to have an additional newline before the label. Elsewhere, too.
> ack
> > 
> > > +destroy_mutex:
> > > +	mutex_destroy(&csi->lock);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static void mei_csi_remove(struct mei_cl_device *cldev) {
> > > +	struct mei_csi *csi = mei_cldev_get_drvdata(cldev);
> > > +
> > > +	v4l2_async_nf_unregister(&csi->notifier);
> > > +	v4l2_async_nf_cleanup(&csi->notifier);
> > > +	v4l2_ctrl_handler_free(&csi->ctrl_handler);
> > > +	v4l2_async_unregister_subdev(&csi->subdev);
> > > +	v4l2_subdev_cleanup(&csi->subdev);
> > > +	media_entity_cleanup(&csi->subdev.entity);
> > > +
> > > +	pm_runtime_disable(&cldev->dev);
> > > +
> > > +	mutex_destroy(&csi->lock);
> > > +}
> > > +
> > > +#define MEI_CSI_UUID UUID_LE(0x92335FCF, 0x3203, 0x4472, \
> > > +			     0xAF, 0x93, 0x7b, 0x44, 0x53, 0xAC, 0x29, 0xDA)
> > > +
> > > +static const struct mei_cl_device_id mei_csi_tbl[] = {
> > > +	{ MEI_CSI_DRIVER_NAME, MEI_CSI_UUID, MEI_CL_VERSION_ANY },
> > > +
> > 
> > Extra newline.
> > 
> > > +	/* required last entry */
> > 
> > The comment could be on the same line below. E.g. "Sentinel" is appropriate, as
> > this isn't just any last entry.
> thanks
> > 
> > > +	{ }
> > > +};
> > > +MODULE_DEVICE_TABLE(mei, mei_csi_tbl);
> > > +
> > > +static struct mei_cl_driver mei_csi_driver = {
> > > +	.id_table = mei_csi_tbl,
> > > +	.name = MEI_CSI_DRIVER_NAME,
> > > +
> > > +	.probe = mei_csi_probe,
> > > +	.remove = mei_csi_remove,
> > > +};
> > > +
> > > +module_mei_cl_driver(mei_csi_driver);
> > > +
> > > +MODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>");
> > > +MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>");
> > > +MODULE_DESCRIPTION("Device driver for IVSC CSI");
> > > +MODULE_LICENSE("GPL");
> > 

-- 
Kind regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v3 2/3] media: pci: intel: ivsc: Add ACE submodule
  2023-03-30  7:32     ` Wu, Wentong
@ 2023-03-30  8:27       ` Sakari Ailus
  0 siblings, 0 replies; 26+ messages in thread
From: Sakari Ailus @ 2023-03-30  8:27 UTC (permalink / raw)
  To: Wu, Wentong
  Cc: hdegoede, djrscally, laurent.pinchart, linux-media, bingbu.cao,
	Wang, Zhifeng, Ye, Xiang, Qiu, Tian Shu

Hi Wentong,

On Thu, Mar 30, 2023 at 07:32:27AM +0000, Wu, Wentong wrote:
> Hi Sakari,
> 
> Thanks
> 
> > -----Original Message-----
> > From: Sakari Ailus <sakari.ailus@linux.intel.com>
> > Sent: Wednesday, March 29, 2023 4:04 PM
> > 
> > Hi Wentong,
> > 
> > On Mon, Mar 27, 2023 at 02:23:07PM +0800, Wentong Wu wrote:
> > > ACE is a submodule of IVSC which controls camera sensor's ownership,
> > > belonging to host or IVSC. When IVSC owns camera sensor, it is for
> > > algorithm computing. When host wants to control camera sensor, ACE
> > > module needs to be informed of ownership with defined interface.
> > >
> > > The interface is via MEI. There is a separate MEI UUID, which this
> > > driver uses to enumerate.
> > >
> > > To switch ownership of camera sensor between IVSC and host, the caller
> > > specifies the defined ownership information which will be sent to
> > > firmware by sending MEI command.
> > >
> > > Device link(device_link_add) is used to set the right camera sensor
> > > ownership before accessing the sensor via I²C. With DL_FLAG_PM_RUNTIME
> > > and DL_FLAG_RPM_ACTIVE, the supplier device will be PM runtime resumed
> > > before the consumer(camera sensor).
> > > So use runtime PM callbacks to transfer the ownership between host and
> > > IVSC.
> > >
> > > Signed-off-by: Wentong Wu <wentong.wu@intel.com>
> > > ---
> > >  drivers/media/pci/intel/ivsc/Makefile  |   3 +
> > >  drivers/media/pci/intel/ivsc/mei_ace.c | 534
> > > +++++++++++++++++++++++++++++++++
> > >  2 files changed, 537 insertions(+)
> > >  create mode 100644 drivers/media/pci/intel/ivsc/mei_ace.c
> > >
> > > diff --git a/drivers/media/pci/intel/ivsc/Makefile
> > > b/drivers/media/pci/intel/ivsc/Makefile
> > > index 7e4c5f0..a641f14 100644
> > > --- a/drivers/media/pci/intel/ivsc/Makefile
> > > +++ b/drivers/media/pci/intel/ivsc/Makefile
> > > @@ -5,3 +5,6 @@
> > >  obj-$(CONFIG_INTEL_VSC) += ivsc-csi.o  ivsc-csi-y += mei_csi.o
> > > ivsc-csi-y += csi_bridge.o
> > > +
> > > +obj-$(CONFIG_INTEL_VSC) += ivsc-ace.o ivsc-ace-y += mei_ace.o
> > > diff --git a/drivers/media/pci/intel/ivsc/mei_ace.c
> > > b/drivers/media/pci/intel/ivsc/mei_ace.c
> > > new file mode 100644
> > > index 0000000..434b072
> > > --- /dev/null
> > > +++ b/drivers/media/pci/intel/ivsc/mei_ace.c
> > > @@ -0,0 +1,534 @@
> > > +// SPDX-License-Identifier: GPL-2.0-only
> > > +/*
> > > + * Copyright (C) 2023 Intel Corporation. All rights reserved.
> > > + * Intel Visual Sensing Controller ACE Linux driver  */
> > > +
> > > +/*
> > > + * To set ownership of camera sensor, there is specific command,
> > > +which
> > > + * is sent via MEI protocol. That's a two-step scheme where the
> > > +firmware
> > > + * first acks receipt of the command and later responses the command
> > > +was
> > > + * executed. The command sending function uses "completion" as the
> > > + * synchronization mechanism. The notification for command is
> > > +received
> > > + * via a mei callback which wakes up the caller. There can be only
> > > +one
> > > + * outstanding command at a time.
> > 
> > Could you document the dependencies in the sensor initialisation (ace +
> > csi) 
> 
> This is about ownership instead of dependency, if host sensor driver configure
> sensor with ownership on IVSC, probably it will be changed by firmware somehow.
> 
> > and how the sensor is powered (i.e. no direct control of the PMIC from the
> > host?)? 
> 
> The power line is directly connected to IVSC instead of host, when ownership
> switched to host, sensor is already powered up by firmware. 

Good. Please also document this in the comment.

-- 
Kind regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2023-03-30  8:27 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-03-27  6:23 [PATCH v3 0/3] media: pci: intel: ivsc: Add driver of Intel Visual Sensing Controller(IVSC) Wentong Wu
2023-03-27  6:23 ` [PATCH v3 1/3] media: pci: intel: ivsc: Add CSI submodule Wentong Wu
2023-03-27 10:01   ` kernel test robot
2023-03-28 13:59   ` kernel test robot
2023-03-29  8:48   ` Sakari Ailus
2023-03-30  3:58     ` Wu, Wentong
2023-03-30  8:23       ` Sakari Ailus
2023-03-29  8:49   ` Sakari Ailus
2023-03-27  6:23 ` [PATCH v3 2/3] media: pci: intel: ivsc: Add ACE submodule Wentong Wu
2023-03-29  8:03   ` Sakari Ailus
2023-03-30  7:32     ` Wu, Wentong
2023-03-30  8:27       ` Sakari Ailus
2023-03-27  6:23 ` [PATCH v3 3/3] ACPI: delay enumeration of devices with a _DEP pointing to INTC1059 device Wentong Wu
2023-03-27  7:06   ` Sakari Ailus
2023-03-27  7:17     ` Wu, Wentong
2023-03-27  7:32       ` Sakari Ailus
2023-03-27  7:36         ` Wu, Wentong
2023-03-27  7:21 ` [PATCH v3 0/3] media: pci: intel: ivsc: Add driver of Intel Visual Sensing Controller(IVSC) Sakari Ailus
2023-03-27  7:33   ` Wu, Wentong
2023-03-27  7:49     ` Sakari Ailus
2023-03-27  8:13       ` Wu, Wentong
2023-03-27  9:36         ` Sakari Ailus
2023-03-28  7:32           ` Wu, Wentong
2023-03-28 19:48             ` Sakari Ailus
2023-03-29  0:42               ` Wu, Wentong
2023-03-29  7:39                 ` Sakari Ailus

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.