162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Author: Dan Scally <djrscally@gmail.com> */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/acpi.h>
562306a36Sopenharmony_ci#include <linux/device.h>
662306a36Sopenharmony_ci#include <linux/i2c.h>
762306a36Sopenharmony_ci#include <linux/mei_cl_bus.h>
862306a36Sopenharmony_ci#include <linux/platform_device.h>
962306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1062306a36Sopenharmony_ci#include <linux/property.h>
1162306a36Sopenharmony_ci#include <linux/string.h>
1262306a36Sopenharmony_ci#include <linux/workqueue.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <media/ipu-bridge.h>
1562306a36Sopenharmony_ci#include <media/v4l2-fwnode.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/*
1862306a36Sopenharmony_ci * 92335fcf-3203-4472-af93-7b4453ac29da
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * Used to build MEI CSI device name to lookup MEI CSI device by
2162306a36Sopenharmony_ci * device_find_child_by_name().
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_ci#define MEI_CSI_UUID							\
2462306a36Sopenharmony_ci	UUID_LE(0x92335FCF, 0x3203, 0x4472,				\
2562306a36Sopenharmony_ci		0xAF, 0x93, 0x7B, 0x44, 0x53, 0xAC, 0x29, 0xDA)
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/*
2862306a36Sopenharmony_ci * IVSC device name
2962306a36Sopenharmony_ci *
3062306a36Sopenharmony_ci * Used to match IVSC device by ipu_bridge_match_ivsc_dev()
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_ci#define IVSC_DEV_NAME "intel_vsc"
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/*
3562306a36Sopenharmony_ci * Extend this array with ACPI Hardware IDs of devices known to be working
3662306a36Sopenharmony_ci * plus the number of link-frequencies expected by their drivers, along with
3762306a36Sopenharmony_ci * the frequency values in hertz. This is somewhat opportunistic way of adding
3862306a36Sopenharmony_ci * support for this for now in the hopes of a better source for the information
3962306a36Sopenharmony_ci * (possibly some encoded value in the SSDB buffer that we're unaware of)
4062306a36Sopenharmony_ci * becoming apparent in the future.
4162306a36Sopenharmony_ci *
4262306a36Sopenharmony_ci * Do not add an entry for a sensor that is not actually supported.
4362306a36Sopenharmony_ci */
4462306a36Sopenharmony_cistatic const struct ipu_sensor_config ipu_supported_sensors[] = {
4562306a36Sopenharmony_ci	/* Omnivision OV5693 */
4662306a36Sopenharmony_ci	IPU_SENSOR_CONFIG("INT33BE", 1, 419200000),
4762306a36Sopenharmony_ci	/* Omnivision OV8865 */
4862306a36Sopenharmony_ci	IPU_SENSOR_CONFIG("INT347A", 1, 360000000),
4962306a36Sopenharmony_ci	/* Omnivision OV7251 */
5062306a36Sopenharmony_ci	IPU_SENSOR_CONFIG("INT347E", 1, 319200000),
5162306a36Sopenharmony_ci	/* Omnivision OV2680 */
5262306a36Sopenharmony_ci	IPU_SENSOR_CONFIG("OVTI2680", 1, 331200000),
5362306a36Sopenharmony_ci	/* Omnivision ov8856 */
5462306a36Sopenharmony_ci	IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000),
5562306a36Sopenharmony_ci	/* Omnivision ov2740 */
5662306a36Sopenharmony_ci	IPU_SENSOR_CONFIG("INT3474", 1, 360000000),
5762306a36Sopenharmony_ci	/* Hynix hi556 */
5862306a36Sopenharmony_ci	IPU_SENSOR_CONFIG("INT3537", 1, 437000000),
5962306a36Sopenharmony_ci	/* Omnivision ov13b10 */
6062306a36Sopenharmony_ci	IPU_SENSOR_CONFIG("OVTIDB10", 1, 560000000),
6162306a36Sopenharmony_ci	/* GalaxyCore GC0310 */
6262306a36Sopenharmony_ci	IPU_SENSOR_CONFIG("INT0310", 0),
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic const struct ipu_property_names prop_names = {
6662306a36Sopenharmony_ci	.clock_frequency = "clock-frequency",
6762306a36Sopenharmony_ci	.rotation = "rotation",
6862306a36Sopenharmony_ci	.orientation = "orientation",
6962306a36Sopenharmony_ci	.bus_type = "bus-type",
7062306a36Sopenharmony_ci	.data_lanes = "data-lanes",
7162306a36Sopenharmony_ci	.remote_endpoint = "remote-endpoint",
7262306a36Sopenharmony_ci	.link_frequencies = "link-frequencies",
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic const char * const ipu_vcm_types[] = {
7662306a36Sopenharmony_ci	"ad5823",
7762306a36Sopenharmony_ci	"dw9714",
7862306a36Sopenharmony_ci	"ad5816",
7962306a36Sopenharmony_ci	"dw9719",
8062306a36Sopenharmony_ci	"dw9718",
8162306a36Sopenharmony_ci	"dw9806b",
8262306a36Sopenharmony_ci	"wv517s",
8362306a36Sopenharmony_ci	"lc898122xa",
8462306a36Sopenharmony_ci	"lc898212axb",
8562306a36Sopenharmony_ci};
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/*
8862306a36Sopenharmony_ci * Used to figure out IVSC acpi device by ipu_bridge_get_ivsc_acpi_dev()
8962306a36Sopenharmony_ci * instead of device and driver match to probe IVSC device.
9062306a36Sopenharmony_ci */
9162306a36Sopenharmony_cistatic const struct acpi_device_id ivsc_acpi_ids[] = {
9262306a36Sopenharmony_ci	{ "INTC1059" },
9362306a36Sopenharmony_ci	{ "INTC1095" },
9462306a36Sopenharmony_ci	{ "INTC100A" },
9562306a36Sopenharmony_ci	{ "INTC10CF" },
9662306a36Sopenharmony_ci};
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	acpi_handle handle = acpi_device_handle(adev);
10162306a36Sopenharmony_ci	struct acpi_device *consumer, *ivsc_adev;
10262306a36Sopenharmony_ci	unsigned int i;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ivsc_acpi_ids); i++) {
10562306a36Sopenharmony_ci		const struct acpi_device_id *acpi_id = &ivsc_acpi_ids[i];
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci		for_each_acpi_dev_match(ivsc_adev, acpi_id->id, NULL, -1)
10862306a36Sopenharmony_ci			/* camera sensor depends on IVSC in DSDT if exist */
10962306a36Sopenharmony_ci			for_each_acpi_consumer_dev(ivsc_adev, consumer)
11062306a36Sopenharmony_ci				if (consumer->handle == handle) {
11162306a36Sopenharmony_ci					acpi_dev_put(consumer);
11262306a36Sopenharmony_ci					return ivsc_adev;
11362306a36Sopenharmony_ci				}
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	return NULL;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic int ipu_bridge_match_ivsc_dev(struct device *dev, const void *adev)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	if (ACPI_COMPANION(dev) != adev)
12262306a36Sopenharmony_ci		return 0;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	if (!sysfs_streq(dev_name(dev), IVSC_DEV_NAME))
12562306a36Sopenharmony_ci		return 0;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	return 1;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic struct device *ipu_bridge_get_ivsc_csi_dev(struct acpi_device *adev)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	struct device *dev, *csi_dev;
13362306a36Sopenharmony_ci	uuid_le uuid = MEI_CSI_UUID;
13462306a36Sopenharmony_ci	char name[64];
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	/* IVSC device on platform bus */
13762306a36Sopenharmony_ci	dev = bus_find_device(&platform_bus_type, NULL, adev,
13862306a36Sopenharmony_ci			      ipu_bridge_match_ivsc_dev);
13962306a36Sopenharmony_ci	if (dev) {
14062306a36Sopenharmony_ci		snprintf(name, sizeof(name), "%s-%pUl", dev_name(dev), &uuid);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci		csi_dev = device_find_child_by_name(dev, name);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci		put_device(dev);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci		return csi_dev;
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	return NULL;
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic int ipu_bridge_check_ivsc_dev(struct ipu_sensor *sensor,
15362306a36Sopenharmony_ci				     struct acpi_device *sensor_adev)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct acpi_device *adev;
15662306a36Sopenharmony_ci	struct device *csi_dev;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	adev = ipu_bridge_get_ivsc_acpi_dev(sensor_adev);
15962306a36Sopenharmony_ci	if (adev) {
16062306a36Sopenharmony_ci		csi_dev = ipu_bridge_get_ivsc_csi_dev(adev);
16162306a36Sopenharmony_ci		if (!csi_dev) {
16262306a36Sopenharmony_ci			acpi_dev_put(adev);
16362306a36Sopenharmony_ci			dev_err(&adev->dev, "Failed to find MEI CSI dev\n");
16462306a36Sopenharmony_ci			return -ENODEV;
16562306a36Sopenharmony_ci		}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci		sensor->csi_dev = csi_dev;
16862306a36Sopenharmony_ci		sensor->ivsc_adev = adev;
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	return 0;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic int ipu_bridge_read_acpi_buffer(struct acpi_device *adev, char *id,
17562306a36Sopenharmony_ci				       void *data, u32 size)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
17862306a36Sopenharmony_ci	union acpi_object *obj;
17962306a36Sopenharmony_ci	acpi_status status;
18062306a36Sopenharmony_ci	int ret = 0;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	status = acpi_evaluate_object(adev->handle, id, NULL, &buffer);
18362306a36Sopenharmony_ci	if (ACPI_FAILURE(status))
18462306a36Sopenharmony_ci		return -ENODEV;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	obj = buffer.pointer;
18762306a36Sopenharmony_ci	if (!obj) {
18862306a36Sopenharmony_ci		dev_err(&adev->dev, "Couldn't locate ACPI buffer\n");
18962306a36Sopenharmony_ci		return -ENODEV;
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	if (obj->type != ACPI_TYPE_BUFFER) {
19362306a36Sopenharmony_ci		dev_err(&adev->dev, "Not an ACPI buffer\n");
19462306a36Sopenharmony_ci		ret = -ENODEV;
19562306a36Sopenharmony_ci		goto out_free_buff;
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	if (obj->buffer.length > size) {
19962306a36Sopenharmony_ci		dev_err(&adev->dev, "Given buffer is too small\n");
20062306a36Sopenharmony_ci		ret = -EINVAL;
20162306a36Sopenharmony_ci		goto out_free_buff;
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	memcpy(data, obj->buffer.pointer, obj->buffer.length);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ciout_free_buff:
20762306a36Sopenharmony_ci	kfree(buffer.pointer);
20862306a36Sopenharmony_ci	return ret;
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic u32 ipu_bridge_parse_rotation(struct acpi_device *adev,
21262306a36Sopenharmony_ci				     struct ipu_sensor_ssdb *ssdb)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	switch (ssdb->degree) {
21562306a36Sopenharmony_ci	case IPU_SENSOR_ROTATION_NORMAL:
21662306a36Sopenharmony_ci		return 0;
21762306a36Sopenharmony_ci	case IPU_SENSOR_ROTATION_INVERTED:
21862306a36Sopenharmony_ci		return 180;
21962306a36Sopenharmony_ci	default:
22062306a36Sopenharmony_ci		dev_warn(&adev->dev,
22162306a36Sopenharmony_ci			 "Unknown rotation %d. Assume 0 degree rotation\n",
22262306a36Sopenharmony_ci			 ssdb->degree);
22362306a36Sopenharmony_ci		return 0;
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic enum v4l2_fwnode_orientation ipu_bridge_parse_orientation(struct acpi_device *adev)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	enum v4l2_fwnode_orientation orientation;
23062306a36Sopenharmony_ci	struct acpi_pld_info *pld;
23162306a36Sopenharmony_ci	acpi_status status;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	status = acpi_get_physical_device_location(adev->handle, &pld);
23462306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
23562306a36Sopenharmony_ci		dev_warn(&adev->dev, "_PLD call failed, using default orientation\n");
23662306a36Sopenharmony_ci		return V4L2_FWNODE_ORIENTATION_EXTERNAL;
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	switch (pld->panel) {
24062306a36Sopenharmony_ci	case ACPI_PLD_PANEL_FRONT:
24162306a36Sopenharmony_ci		orientation = V4L2_FWNODE_ORIENTATION_FRONT;
24262306a36Sopenharmony_ci		break;
24362306a36Sopenharmony_ci	case ACPI_PLD_PANEL_BACK:
24462306a36Sopenharmony_ci		orientation = V4L2_FWNODE_ORIENTATION_BACK;
24562306a36Sopenharmony_ci		break;
24662306a36Sopenharmony_ci	case ACPI_PLD_PANEL_TOP:
24762306a36Sopenharmony_ci	case ACPI_PLD_PANEL_LEFT:
24862306a36Sopenharmony_ci	case ACPI_PLD_PANEL_RIGHT:
24962306a36Sopenharmony_ci	case ACPI_PLD_PANEL_UNKNOWN:
25062306a36Sopenharmony_ci		orientation = V4L2_FWNODE_ORIENTATION_EXTERNAL;
25162306a36Sopenharmony_ci		break;
25262306a36Sopenharmony_ci	default:
25362306a36Sopenharmony_ci		dev_warn(&adev->dev, "Unknown _PLD panel val %d\n", pld->panel);
25462306a36Sopenharmony_ci		orientation = V4L2_FWNODE_ORIENTATION_EXTERNAL;
25562306a36Sopenharmony_ci		break;
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	ACPI_FREE(pld);
25962306a36Sopenharmony_ci	return orientation;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ciint ipu_bridge_parse_ssdb(struct acpi_device *adev, struct ipu_sensor *sensor)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	struct ipu_sensor_ssdb ssdb = {};
26562306a36Sopenharmony_ci	int ret;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	ret = ipu_bridge_read_acpi_buffer(adev, "SSDB", &ssdb, sizeof(ssdb));
26862306a36Sopenharmony_ci	if (ret)
26962306a36Sopenharmony_ci		return ret;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	if (ssdb.vcmtype > ARRAY_SIZE(ipu_vcm_types)) {
27262306a36Sopenharmony_ci		dev_warn(&adev->dev, "Unknown VCM type %d\n", ssdb.vcmtype);
27362306a36Sopenharmony_ci		ssdb.vcmtype = 0;
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	if (ssdb.lanes > IPU_MAX_LANES) {
27762306a36Sopenharmony_ci		dev_err(&adev->dev, "Number of lanes in SSDB is invalid\n");
27862306a36Sopenharmony_ci		return -EINVAL;
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	sensor->link = ssdb.link;
28262306a36Sopenharmony_ci	sensor->lanes = ssdb.lanes;
28362306a36Sopenharmony_ci	sensor->mclkspeed = ssdb.mclkspeed;
28462306a36Sopenharmony_ci	sensor->rotation = ipu_bridge_parse_rotation(adev, &ssdb);
28562306a36Sopenharmony_ci	sensor->orientation = ipu_bridge_parse_orientation(adev);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	if (ssdb.vcmtype)
28862306a36Sopenharmony_ci		sensor->vcm_type = ipu_vcm_types[ssdb.vcmtype - 1];
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	return 0;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(ipu_bridge_parse_ssdb, INTEL_IPU_BRIDGE);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic void ipu_bridge_create_fwnode_properties(
29562306a36Sopenharmony_ci	struct ipu_sensor *sensor,
29662306a36Sopenharmony_ci	struct ipu_bridge *bridge,
29762306a36Sopenharmony_ci	const struct ipu_sensor_config *cfg)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	struct ipu_property_names *names = &sensor->prop_names;
30062306a36Sopenharmony_ci	struct software_node *nodes = sensor->swnodes;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	sensor->prop_names = prop_names;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	if (sensor->csi_dev) {
30562306a36Sopenharmony_ci		sensor->local_ref[0] =
30662306a36Sopenharmony_ci			SOFTWARE_NODE_REFERENCE(&nodes[SWNODE_IVSC_SENSOR_ENDPOINT]);
30762306a36Sopenharmony_ci		sensor->remote_ref[0] =
30862306a36Sopenharmony_ci			SOFTWARE_NODE_REFERENCE(&nodes[SWNODE_IVSC_IPU_ENDPOINT]);
30962306a36Sopenharmony_ci		sensor->ivsc_sensor_ref[0] =
31062306a36Sopenharmony_ci			SOFTWARE_NODE_REFERENCE(&nodes[SWNODE_SENSOR_ENDPOINT]);
31162306a36Sopenharmony_ci		sensor->ivsc_ipu_ref[0] =
31262306a36Sopenharmony_ci			SOFTWARE_NODE_REFERENCE(&nodes[SWNODE_IPU_ENDPOINT]);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci		sensor->ivsc_sensor_ep_properties[0] =
31562306a36Sopenharmony_ci			PROPERTY_ENTRY_U32(names->bus_type,
31662306a36Sopenharmony_ci					   V4L2_FWNODE_BUS_TYPE_CSI2_DPHY);
31762306a36Sopenharmony_ci		sensor->ivsc_sensor_ep_properties[1] =
31862306a36Sopenharmony_ci			PROPERTY_ENTRY_U32_ARRAY_LEN(names->data_lanes,
31962306a36Sopenharmony_ci						     bridge->data_lanes,
32062306a36Sopenharmony_ci						     sensor->lanes);
32162306a36Sopenharmony_ci		sensor->ivsc_sensor_ep_properties[2] =
32262306a36Sopenharmony_ci			PROPERTY_ENTRY_REF_ARRAY(names->remote_endpoint,
32362306a36Sopenharmony_ci						 sensor->ivsc_sensor_ref);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci		sensor->ivsc_ipu_ep_properties[0] =
32662306a36Sopenharmony_ci			PROPERTY_ENTRY_U32(names->bus_type,
32762306a36Sopenharmony_ci					   V4L2_FWNODE_BUS_TYPE_CSI2_DPHY);
32862306a36Sopenharmony_ci		sensor->ivsc_ipu_ep_properties[1] =
32962306a36Sopenharmony_ci			PROPERTY_ENTRY_U32_ARRAY_LEN(names->data_lanes,
33062306a36Sopenharmony_ci						     bridge->data_lanes,
33162306a36Sopenharmony_ci						     sensor->lanes);
33262306a36Sopenharmony_ci		sensor->ivsc_ipu_ep_properties[2] =
33362306a36Sopenharmony_ci			PROPERTY_ENTRY_REF_ARRAY(names->remote_endpoint,
33462306a36Sopenharmony_ci						 sensor->ivsc_ipu_ref);
33562306a36Sopenharmony_ci	} else {
33662306a36Sopenharmony_ci		sensor->local_ref[0] =
33762306a36Sopenharmony_ci			SOFTWARE_NODE_REFERENCE(&nodes[SWNODE_IPU_ENDPOINT]);
33862306a36Sopenharmony_ci		sensor->remote_ref[0] =
33962306a36Sopenharmony_ci			SOFTWARE_NODE_REFERENCE(&nodes[SWNODE_SENSOR_ENDPOINT]);
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	sensor->dev_properties[0] = PROPERTY_ENTRY_U32(
34362306a36Sopenharmony_ci					sensor->prop_names.clock_frequency,
34462306a36Sopenharmony_ci					sensor->mclkspeed);
34562306a36Sopenharmony_ci	sensor->dev_properties[1] = PROPERTY_ENTRY_U32(
34662306a36Sopenharmony_ci					sensor->prop_names.rotation,
34762306a36Sopenharmony_ci					sensor->rotation);
34862306a36Sopenharmony_ci	sensor->dev_properties[2] = PROPERTY_ENTRY_U32(
34962306a36Sopenharmony_ci					sensor->prop_names.orientation,
35062306a36Sopenharmony_ci					sensor->orientation);
35162306a36Sopenharmony_ci	if (sensor->vcm_type) {
35262306a36Sopenharmony_ci		sensor->vcm_ref[0] =
35362306a36Sopenharmony_ci			SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_VCM]);
35462306a36Sopenharmony_ci		sensor->dev_properties[3] =
35562306a36Sopenharmony_ci			PROPERTY_ENTRY_REF_ARRAY("lens-focus", sensor->vcm_ref);
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	sensor->ep_properties[0] = PROPERTY_ENTRY_U32(
35962306a36Sopenharmony_ci					sensor->prop_names.bus_type,
36062306a36Sopenharmony_ci					V4L2_FWNODE_BUS_TYPE_CSI2_DPHY);
36162306a36Sopenharmony_ci	sensor->ep_properties[1] = PROPERTY_ENTRY_U32_ARRAY_LEN(
36262306a36Sopenharmony_ci					sensor->prop_names.data_lanes,
36362306a36Sopenharmony_ci					bridge->data_lanes, sensor->lanes);
36462306a36Sopenharmony_ci	sensor->ep_properties[2] = PROPERTY_ENTRY_REF_ARRAY(
36562306a36Sopenharmony_ci					sensor->prop_names.remote_endpoint,
36662306a36Sopenharmony_ci					sensor->local_ref);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	if (cfg->nr_link_freqs > 0)
36962306a36Sopenharmony_ci		sensor->ep_properties[3] = PROPERTY_ENTRY_U64_ARRAY_LEN(
37062306a36Sopenharmony_ci			sensor->prop_names.link_frequencies,
37162306a36Sopenharmony_ci			cfg->link_freqs,
37262306a36Sopenharmony_ci			cfg->nr_link_freqs);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	sensor->ipu_properties[0] = PROPERTY_ENTRY_U32_ARRAY_LEN(
37562306a36Sopenharmony_ci					sensor->prop_names.data_lanes,
37662306a36Sopenharmony_ci					bridge->data_lanes, sensor->lanes);
37762306a36Sopenharmony_ci	sensor->ipu_properties[1] = PROPERTY_ENTRY_REF_ARRAY(
37862306a36Sopenharmony_ci					sensor->prop_names.remote_endpoint,
37962306a36Sopenharmony_ci					sensor->remote_ref);
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cistatic void ipu_bridge_init_swnode_names(struct ipu_sensor *sensor)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	snprintf(sensor->node_names.remote_port,
38562306a36Sopenharmony_ci		 sizeof(sensor->node_names.remote_port),
38662306a36Sopenharmony_ci		 SWNODE_GRAPH_PORT_NAME_FMT, sensor->link);
38762306a36Sopenharmony_ci	snprintf(sensor->node_names.port,
38862306a36Sopenharmony_ci		 sizeof(sensor->node_names.port),
38962306a36Sopenharmony_ci		 SWNODE_GRAPH_PORT_NAME_FMT, 0); /* Always port 0 */
39062306a36Sopenharmony_ci	snprintf(sensor->node_names.endpoint,
39162306a36Sopenharmony_ci		 sizeof(sensor->node_names.endpoint),
39262306a36Sopenharmony_ci		 SWNODE_GRAPH_ENDPOINT_NAME_FMT, 0); /* And endpoint 0 */
39362306a36Sopenharmony_ci	if (sensor->vcm_type) {
39462306a36Sopenharmony_ci		/* append link to distinguish nodes with same model VCM */
39562306a36Sopenharmony_ci		snprintf(sensor->node_names.vcm, sizeof(sensor->node_names.vcm),
39662306a36Sopenharmony_ci			 "%s-%u", sensor->vcm_type, sensor->link);
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	if (sensor->csi_dev) {
40062306a36Sopenharmony_ci		snprintf(sensor->node_names.ivsc_sensor_port,
40162306a36Sopenharmony_ci			 sizeof(sensor->node_names.ivsc_sensor_port),
40262306a36Sopenharmony_ci			 SWNODE_GRAPH_PORT_NAME_FMT, 0);
40362306a36Sopenharmony_ci		snprintf(sensor->node_names.ivsc_ipu_port,
40462306a36Sopenharmony_ci			 sizeof(sensor->node_names.ivsc_ipu_port),
40562306a36Sopenharmony_ci			 SWNODE_GRAPH_PORT_NAME_FMT, 1);
40662306a36Sopenharmony_ci	}
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic void ipu_bridge_init_swnode_group(struct ipu_sensor *sensor)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	struct software_node *nodes = sensor->swnodes;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	sensor->group[SWNODE_SENSOR_HID] = &nodes[SWNODE_SENSOR_HID];
41462306a36Sopenharmony_ci	sensor->group[SWNODE_SENSOR_PORT] = &nodes[SWNODE_SENSOR_PORT];
41562306a36Sopenharmony_ci	sensor->group[SWNODE_SENSOR_ENDPOINT] = &nodes[SWNODE_SENSOR_ENDPOINT];
41662306a36Sopenharmony_ci	sensor->group[SWNODE_IPU_PORT] = &nodes[SWNODE_IPU_PORT];
41762306a36Sopenharmony_ci	sensor->group[SWNODE_IPU_ENDPOINT] = &nodes[SWNODE_IPU_ENDPOINT];
41862306a36Sopenharmony_ci	if (sensor->vcm_type)
41962306a36Sopenharmony_ci		sensor->group[SWNODE_VCM] =  &nodes[SWNODE_VCM];
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	if (sensor->csi_dev) {
42262306a36Sopenharmony_ci		sensor->group[SWNODE_IVSC_HID] =
42362306a36Sopenharmony_ci					&nodes[SWNODE_IVSC_HID];
42462306a36Sopenharmony_ci		sensor->group[SWNODE_IVSC_SENSOR_PORT] =
42562306a36Sopenharmony_ci					&nodes[SWNODE_IVSC_SENSOR_PORT];
42662306a36Sopenharmony_ci		sensor->group[SWNODE_IVSC_SENSOR_ENDPOINT] =
42762306a36Sopenharmony_ci					&nodes[SWNODE_IVSC_SENSOR_ENDPOINT];
42862306a36Sopenharmony_ci		sensor->group[SWNODE_IVSC_IPU_PORT] =
42962306a36Sopenharmony_ci					&nodes[SWNODE_IVSC_IPU_PORT];
43062306a36Sopenharmony_ci		sensor->group[SWNODE_IVSC_IPU_ENDPOINT] =
43162306a36Sopenharmony_ci					&nodes[SWNODE_IVSC_IPU_ENDPOINT];
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci		if (sensor->vcm_type)
43462306a36Sopenharmony_ci			sensor->group[SWNODE_VCM] = &nodes[SWNODE_VCM];
43562306a36Sopenharmony_ci	} else {
43662306a36Sopenharmony_ci		if (sensor->vcm_type)
43762306a36Sopenharmony_ci			sensor->group[SWNODE_IVSC_HID] = &nodes[SWNODE_VCM];
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_ci}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_cistatic void ipu_bridge_create_connection_swnodes(struct ipu_bridge *bridge,
44262306a36Sopenharmony_ci						 struct ipu_sensor *sensor)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	struct ipu_node_names *names = &sensor->node_names;
44562306a36Sopenharmony_ci	struct software_node *nodes = sensor->swnodes;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	ipu_bridge_init_swnode_names(sensor);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	nodes[SWNODE_SENSOR_HID] = NODE_SENSOR(sensor->name,
45062306a36Sopenharmony_ci					       sensor->dev_properties);
45162306a36Sopenharmony_ci	nodes[SWNODE_SENSOR_PORT] = NODE_PORT(sensor->node_names.port,
45262306a36Sopenharmony_ci					      &nodes[SWNODE_SENSOR_HID]);
45362306a36Sopenharmony_ci	nodes[SWNODE_SENSOR_ENDPOINT] = NODE_ENDPOINT(
45462306a36Sopenharmony_ci						sensor->node_names.endpoint,
45562306a36Sopenharmony_ci						&nodes[SWNODE_SENSOR_PORT],
45662306a36Sopenharmony_ci						sensor->ep_properties);
45762306a36Sopenharmony_ci	nodes[SWNODE_IPU_PORT] = NODE_PORT(sensor->node_names.remote_port,
45862306a36Sopenharmony_ci					   &bridge->ipu_hid_node);
45962306a36Sopenharmony_ci	nodes[SWNODE_IPU_ENDPOINT] = NODE_ENDPOINT(
46062306a36Sopenharmony_ci						sensor->node_names.endpoint,
46162306a36Sopenharmony_ci						&nodes[SWNODE_IPU_PORT],
46262306a36Sopenharmony_ci						sensor->ipu_properties);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	if (sensor->csi_dev) {
46562306a36Sopenharmony_ci		snprintf(sensor->ivsc_name, sizeof(sensor->ivsc_name), "%s-%u",
46662306a36Sopenharmony_ci			 acpi_device_hid(sensor->ivsc_adev), sensor->link);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci		nodes[SWNODE_IVSC_HID] = NODE_SENSOR(sensor->ivsc_name,
46962306a36Sopenharmony_ci						     sensor->ivsc_properties);
47062306a36Sopenharmony_ci		nodes[SWNODE_IVSC_SENSOR_PORT] =
47162306a36Sopenharmony_ci				NODE_PORT(names->ivsc_sensor_port,
47262306a36Sopenharmony_ci					  &nodes[SWNODE_IVSC_HID]);
47362306a36Sopenharmony_ci		nodes[SWNODE_IVSC_SENSOR_ENDPOINT] =
47462306a36Sopenharmony_ci				NODE_ENDPOINT(names->endpoint,
47562306a36Sopenharmony_ci					      &nodes[SWNODE_IVSC_SENSOR_PORT],
47662306a36Sopenharmony_ci					      sensor->ivsc_sensor_ep_properties);
47762306a36Sopenharmony_ci		nodes[SWNODE_IVSC_IPU_PORT] =
47862306a36Sopenharmony_ci				NODE_PORT(names->ivsc_ipu_port,
47962306a36Sopenharmony_ci					  &nodes[SWNODE_IVSC_HID]);
48062306a36Sopenharmony_ci		nodes[SWNODE_IVSC_IPU_ENDPOINT] =
48162306a36Sopenharmony_ci				NODE_ENDPOINT(names->endpoint,
48262306a36Sopenharmony_ci					      &nodes[SWNODE_IVSC_IPU_PORT],
48362306a36Sopenharmony_ci					      sensor->ivsc_ipu_ep_properties);
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	nodes[SWNODE_VCM] = NODE_VCM(sensor->node_names.vcm);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	ipu_bridge_init_swnode_group(sensor);
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci/*
49262306a36Sopenharmony_ci * The actual instantiation must be done from a workqueue to avoid
49362306a36Sopenharmony_ci * a deadlock on taking list_lock from v4l2-async twice.
49462306a36Sopenharmony_ci */
49562306a36Sopenharmony_cistruct ipu_bridge_instantiate_vcm_work_data {
49662306a36Sopenharmony_ci	struct work_struct work;
49762306a36Sopenharmony_ci	struct device *sensor;
49862306a36Sopenharmony_ci	char name[16];
49962306a36Sopenharmony_ci	struct i2c_board_info board_info;
50062306a36Sopenharmony_ci};
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_cistatic void ipu_bridge_instantiate_vcm_work(struct work_struct *work)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	struct ipu_bridge_instantiate_vcm_work_data *data =
50562306a36Sopenharmony_ci		container_of(work, struct ipu_bridge_instantiate_vcm_work_data,
50662306a36Sopenharmony_ci			     work);
50762306a36Sopenharmony_ci	struct acpi_device *adev = ACPI_COMPANION(data->sensor);
50862306a36Sopenharmony_ci	struct i2c_client *vcm_client;
50962306a36Sopenharmony_ci	bool put_fwnode = true;
51062306a36Sopenharmony_ci	int ret;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	/*
51362306a36Sopenharmony_ci	 * The client may get probed before the device_link gets added below
51462306a36Sopenharmony_ci	 * make sure the sensor is powered-up during probe.
51562306a36Sopenharmony_ci	 */
51662306a36Sopenharmony_ci	ret = pm_runtime_get_sync(data->sensor);
51762306a36Sopenharmony_ci	if (ret < 0) {
51862306a36Sopenharmony_ci		dev_err(data->sensor, "Error %d runtime-resuming sensor, cannot instantiate VCM\n",
51962306a36Sopenharmony_ci			ret);
52062306a36Sopenharmony_ci		goto out_pm_put;
52162306a36Sopenharmony_ci	}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	/*
52462306a36Sopenharmony_ci	 * Note the client is created only once and then kept around
52562306a36Sopenharmony_ci	 * even after a rmmod, just like the software-nodes.
52662306a36Sopenharmony_ci	 */
52762306a36Sopenharmony_ci	vcm_client = i2c_acpi_new_device_by_fwnode(acpi_fwnode_handle(adev),
52862306a36Sopenharmony_ci						   1, &data->board_info);
52962306a36Sopenharmony_ci	if (IS_ERR(vcm_client)) {
53062306a36Sopenharmony_ci		dev_err(data->sensor, "Error instantiating VCM client: %ld\n",
53162306a36Sopenharmony_ci			PTR_ERR(vcm_client));
53262306a36Sopenharmony_ci		goto out_pm_put;
53362306a36Sopenharmony_ci	}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	device_link_add(&vcm_client->dev, data->sensor, DL_FLAG_PM_RUNTIME);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	dev_info(data->sensor, "Instantiated %s VCM\n", data->board_info.type);
53862306a36Sopenharmony_ci	put_fwnode = false; /* Ownership has passed to the i2c-client */
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ciout_pm_put:
54162306a36Sopenharmony_ci	pm_runtime_put(data->sensor);
54262306a36Sopenharmony_ci	put_device(data->sensor);
54362306a36Sopenharmony_ci	if (put_fwnode)
54462306a36Sopenharmony_ci		fwnode_handle_put(data->board_info.fwnode);
54562306a36Sopenharmony_ci	kfree(data);
54662306a36Sopenharmony_ci}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ciint ipu_bridge_instantiate_vcm(struct device *sensor)
54962306a36Sopenharmony_ci{
55062306a36Sopenharmony_ci	struct ipu_bridge_instantiate_vcm_work_data *data;
55162306a36Sopenharmony_ci	struct fwnode_handle *vcm_fwnode;
55262306a36Sopenharmony_ci	struct i2c_client *vcm_client;
55362306a36Sopenharmony_ci	struct acpi_device *adev;
55462306a36Sopenharmony_ci	char *sep;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	adev = ACPI_COMPANION(sensor);
55762306a36Sopenharmony_ci	if (!adev)
55862306a36Sopenharmony_ci		return 0;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	vcm_fwnode = fwnode_find_reference(dev_fwnode(sensor), "lens-focus", 0);
56162306a36Sopenharmony_ci	if (IS_ERR(vcm_fwnode))
56262306a36Sopenharmony_ci		return 0;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	/* When reloading modules the client will already exist */
56562306a36Sopenharmony_ci	vcm_client = i2c_find_device_by_fwnode(vcm_fwnode);
56662306a36Sopenharmony_ci	if (vcm_client) {
56762306a36Sopenharmony_ci		fwnode_handle_put(vcm_fwnode);
56862306a36Sopenharmony_ci		put_device(&vcm_client->dev);
56962306a36Sopenharmony_ci		return 0;
57062306a36Sopenharmony_ci	}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	data = kzalloc(sizeof(*data), GFP_KERNEL);
57362306a36Sopenharmony_ci	if (!data) {
57462306a36Sopenharmony_ci		fwnode_handle_put(vcm_fwnode);
57562306a36Sopenharmony_ci		return -ENOMEM;
57662306a36Sopenharmony_ci	}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	INIT_WORK(&data->work, ipu_bridge_instantiate_vcm_work);
57962306a36Sopenharmony_ci	data->sensor = get_device(sensor);
58062306a36Sopenharmony_ci	snprintf(data->name, sizeof(data->name), "%s-VCM",
58162306a36Sopenharmony_ci		 acpi_dev_name(adev));
58262306a36Sopenharmony_ci	data->board_info.dev_name = data->name;
58362306a36Sopenharmony_ci	data->board_info.fwnode = vcm_fwnode;
58462306a36Sopenharmony_ci	snprintf(data->board_info.type, sizeof(data->board_info.type),
58562306a36Sopenharmony_ci		 "%pfwP", vcm_fwnode);
58662306a36Sopenharmony_ci	/* Strip "-<link>" postfix */
58762306a36Sopenharmony_ci	sep = strchrnul(data->board_info.type, '-');
58862306a36Sopenharmony_ci	*sep = 0;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	queue_work(system_long_wq, &data->work);
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	return 0;
59362306a36Sopenharmony_ci}
59462306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(ipu_bridge_instantiate_vcm, INTEL_IPU_BRIDGE);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_cistatic int ipu_bridge_instantiate_ivsc(struct ipu_sensor *sensor)
59762306a36Sopenharmony_ci{
59862306a36Sopenharmony_ci	struct fwnode_handle *fwnode;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	if (!sensor->csi_dev)
60162306a36Sopenharmony_ci		return 0;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	fwnode = software_node_fwnode(&sensor->swnodes[SWNODE_IVSC_HID]);
60462306a36Sopenharmony_ci	if (!fwnode)
60562306a36Sopenharmony_ci		return -ENODEV;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	set_secondary_fwnode(sensor->csi_dev, fwnode);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	return 0;
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic void ipu_bridge_unregister_sensors(struct ipu_bridge *bridge)
61362306a36Sopenharmony_ci{
61462306a36Sopenharmony_ci	struct ipu_sensor *sensor;
61562306a36Sopenharmony_ci	unsigned int i;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	for (i = 0; i < bridge->n_sensors; i++) {
61862306a36Sopenharmony_ci		sensor = &bridge->sensors[i];
61962306a36Sopenharmony_ci		software_node_unregister_node_group(sensor->group);
62062306a36Sopenharmony_ci		acpi_dev_put(sensor->adev);
62162306a36Sopenharmony_ci		put_device(sensor->csi_dev);
62262306a36Sopenharmony_ci		acpi_dev_put(sensor->ivsc_adev);
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_cistatic int ipu_bridge_connect_sensor(const struct ipu_sensor_config *cfg,
62762306a36Sopenharmony_ci				     struct ipu_bridge *bridge)
62862306a36Sopenharmony_ci{
62962306a36Sopenharmony_ci	struct fwnode_handle *fwnode, *primary;
63062306a36Sopenharmony_ci	struct ipu_sensor *sensor;
63162306a36Sopenharmony_ci	struct acpi_device *adev;
63262306a36Sopenharmony_ci	int ret;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	for_each_acpi_dev_match(adev, cfg->hid, NULL, -1) {
63562306a36Sopenharmony_ci		if (!adev->status.enabled)
63662306a36Sopenharmony_ci			continue;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci		if (bridge->n_sensors >= IPU_MAX_PORTS) {
63962306a36Sopenharmony_ci			acpi_dev_put(adev);
64062306a36Sopenharmony_ci			dev_err(bridge->dev, "Exceeded available IPU ports\n");
64162306a36Sopenharmony_ci			return -EINVAL;
64262306a36Sopenharmony_ci		}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci		sensor = &bridge->sensors[bridge->n_sensors];
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci		ret = bridge->parse_sensor_fwnode(adev, sensor);
64762306a36Sopenharmony_ci		if (ret)
64862306a36Sopenharmony_ci			goto err_put_adev;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci		snprintf(sensor->name, sizeof(sensor->name), "%s-%u",
65162306a36Sopenharmony_ci			 cfg->hid, sensor->link);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci		ret = ipu_bridge_check_ivsc_dev(sensor, adev);
65462306a36Sopenharmony_ci		if (ret)
65562306a36Sopenharmony_ci			goto err_put_adev;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci		ipu_bridge_create_fwnode_properties(sensor, bridge, cfg);
65862306a36Sopenharmony_ci		ipu_bridge_create_connection_swnodes(bridge, sensor);
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci		ret = software_node_register_node_group(sensor->group);
66162306a36Sopenharmony_ci		if (ret)
66262306a36Sopenharmony_ci			goto err_put_ivsc;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci		fwnode = software_node_fwnode(&sensor->swnodes[
66562306a36Sopenharmony_ci						      SWNODE_SENSOR_HID]);
66662306a36Sopenharmony_ci		if (!fwnode) {
66762306a36Sopenharmony_ci			ret = -ENODEV;
66862306a36Sopenharmony_ci			goto err_free_swnodes;
66962306a36Sopenharmony_ci		}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci		sensor->adev = acpi_dev_get(adev);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci		primary = acpi_fwnode_handle(adev);
67462306a36Sopenharmony_ci		primary->secondary = fwnode;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci		ret = ipu_bridge_instantiate_ivsc(sensor);
67762306a36Sopenharmony_ci		if (ret)
67862306a36Sopenharmony_ci			goto err_free_swnodes;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci		dev_info(bridge->dev, "Found supported sensor %s\n",
68162306a36Sopenharmony_ci			 acpi_dev_name(adev));
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci		bridge->n_sensors++;
68462306a36Sopenharmony_ci	}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	return 0;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_cierr_free_swnodes:
68962306a36Sopenharmony_ci	software_node_unregister_node_group(sensor->group);
69062306a36Sopenharmony_cierr_put_ivsc:
69162306a36Sopenharmony_ci	put_device(sensor->csi_dev);
69262306a36Sopenharmony_ci	acpi_dev_put(sensor->ivsc_adev);
69362306a36Sopenharmony_cierr_put_adev:
69462306a36Sopenharmony_ci	acpi_dev_put(adev);
69562306a36Sopenharmony_ci	return ret;
69662306a36Sopenharmony_ci}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_cistatic int ipu_bridge_connect_sensors(struct ipu_bridge *bridge)
69962306a36Sopenharmony_ci{
70062306a36Sopenharmony_ci	unsigned int i;
70162306a36Sopenharmony_ci	int ret;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ipu_supported_sensors); i++) {
70462306a36Sopenharmony_ci		const struct ipu_sensor_config *cfg =
70562306a36Sopenharmony_ci			&ipu_supported_sensors[i];
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci		ret = ipu_bridge_connect_sensor(cfg, bridge);
70862306a36Sopenharmony_ci		if (ret)
70962306a36Sopenharmony_ci			goto err_unregister_sensors;
71062306a36Sopenharmony_ci	}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	return 0;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_cierr_unregister_sensors:
71562306a36Sopenharmony_ci	ipu_bridge_unregister_sensors(bridge);
71662306a36Sopenharmony_ci	return ret;
71762306a36Sopenharmony_ci}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_cistatic int ipu_bridge_ivsc_is_ready(void)
72062306a36Sopenharmony_ci{
72162306a36Sopenharmony_ci	struct acpi_device *sensor_adev, *adev;
72262306a36Sopenharmony_ci	struct device *csi_dev;
72362306a36Sopenharmony_ci	bool ready = true;
72462306a36Sopenharmony_ci	unsigned int i;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ipu_supported_sensors); i++) {
72762306a36Sopenharmony_ci		const struct ipu_sensor_config *cfg =
72862306a36Sopenharmony_ci			&ipu_supported_sensors[i];
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci		for_each_acpi_dev_match(sensor_adev, cfg->hid, NULL, -1) {
73162306a36Sopenharmony_ci			if (!sensor_adev->status.enabled)
73262306a36Sopenharmony_ci				continue;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci			adev = ipu_bridge_get_ivsc_acpi_dev(sensor_adev);
73562306a36Sopenharmony_ci			if (!adev)
73662306a36Sopenharmony_ci				continue;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci			csi_dev = ipu_bridge_get_ivsc_csi_dev(adev);
73962306a36Sopenharmony_ci			if (!csi_dev)
74062306a36Sopenharmony_ci				ready = false;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci			put_device(csi_dev);
74362306a36Sopenharmony_ci			acpi_dev_put(adev);
74462306a36Sopenharmony_ci		}
74562306a36Sopenharmony_ci	}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	return ready;
74862306a36Sopenharmony_ci}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ciint ipu_bridge_init(struct device *dev,
75162306a36Sopenharmony_ci		    ipu_parse_sensor_fwnode_t parse_sensor_fwnode)
75262306a36Sopenharmony_ci{
75362306a36Sopenharmony_ci	struct fwnode_handle *fwnode;
75462306a36Sopenharmony_ci	struct ipu_bridge *bridge;
75562306a36Sopenharmony_ci	unsigned int i;
75662306a36Sopenharmony_ci	int ret;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	if (!ipu_bridge_ivsc_is_ready())
75962306a36Sopenharmony_ci		return -EPROBE_DEFER;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
76262306a36Sopenharmony_ci	if (!bridge)
76362306a36Sopenharmony_ci		return -ENOMEM;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	strscpy(bridge->ipu_node_name, IPU_HID,
76662306a36Sopenharmony_ci		sizeof(bridge->ipu_node_name));
76762306a36Sopenharmony_ci	bridge->ipu_hid_node.name = bridge->ipu_node_name;
76862306a36Sopenharmony_ci	bridge->dev = dev;
76962306a36Sopenharmony_ci	bridge->parse_sensor_fwnode = parse_sensor_fwnode;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	ret = software_node_register(&bridge->ipu_hid_node);
77262306a36Sopenharmony_ci	if (ret < 0) {
77362306a36Sopenharmony_ci		dev_err(dev, "Failed to register the IPU HID node\n");
77462306a36Sopenharmony_ci		goto err_free_bridge;
77562306a36Sopenharmony_ci	}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	/*
77862306a36Sopenharmony_ci	 * Map the lane arrangement, which is fixed for the IPU3 (meaning we
77962306a36Sopenharmony_ci	 * only need one, rather than one per sensor). We include it as a
78062306a36Sopenharmony_ci	 * member of the struct ipu_bridge rather than a global variable so
78162306a36Sopenharmony_ci	 * that it survives if the module is unloaded along with the rest of
78262306a36Sopenharmony_ci	 * the struct.
78362306a36Sopenharmony_ci	 */
78462306a36Sopenharmony_ci	for (i = 0; i < IPU_MAX_LANES; i++)
78562306a36Sopenharmony_ci		bridge->data_lanes[i] = i + 1;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	ret = ipu_bridge_connect_sensors(bridge);
78862306a36Sopenharmony_ci	if (ret || bridge->n_sensors == 0)
78962306a36Sopenharmony_ci		goto err_unregister_ipu;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	dev_info(dev, "Connected %d cameras\n", bridge->n_sensors);
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	fwnode = software_node_fwnode(&bridge->ipu_hid_node);
79462306a36Sopenharmony_ci	if (!fwnode) {
79562306a36Sopenharmony_ci		dev_err(dev, "Error getting fwnode from ipu software_node\n");
79662306a36Sopenharmony_ci		ret = -ENODEV;
79762306a36Sopenharmony_ci		goto err_unregister_sensors;
79862306a36Sopenharmony_ci	}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	set_secondary_fwnode(dev, fwnode);
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	return 0;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_cierr_unregister_sensors:
80562306a36Sopenharmony_ci	ipu_bridge_unregister_sensors(bridge);
80662306a36Sopenharmony_cierr_unregister_ipu:
80762306a36Sopenharmony_ci	software_node_unregister(&bridge->ipu_hid_node);
80862306a36Sopenharmony_cierr_free_bridge:
80962306a36Sopenharmony_ci	kfree(bridge);
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	return ret;
81262306a36Sopenharmony_ci}
81362306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(ipu_bridge_init, INTEL_IPU_BRIDGE);
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
81662306a36Sopenharmony_ciMODULE_DESCRIPTION("Intel IPU Sensors Bridge driver");
817