18d28ec7eSHans de Goede // SPDX-License-Identifier: GPL-2.0
28d28ec7eSHans de Goede /*
38d28ec7eSHans de Goede  * Code to build software firmware node graph for atomisp2 connected sensors
48d28ec7eSHans de Goede  * from ACPI tables.
58d28ec7eSHans de Goede  *
68d28ec7eSHans de Goede  * Copyright (C) 2023 Hans de Goede <hdegoede@redhat.com>
78d28ec7eSHans de Goede  *
88d28ec7eSHans de Goede  * Based on drivers/media/pci/intel/ipu3/cio2-bridge.c written by:
98d28ec7eSHans de Goede  * Dan Scally <djrscally@gmail.com>
108d28ec7eSHans de Goede  */
118d28ec7eSHans de Goede 
128d28ec7eSHans de Goede #include <linux/acpi.h>
130af90783SHans de Goede #include <linux/clk.h>
148d28ec7eSHans de Goede #include <linux/device.h>
158d28ec7eSHans de Goede #include <linux/dmi.h>
168d28ec7eSHans de Goede #include <linux/property.h>
17f04eedb9SHans de Goede 
18f04eedb9SHans de Goede #include <media/ipu-bridge.h>
198d28ec7eSHans de Goede #include <media/v4l2-fwnode.h>
208d28ec7eSHans de Goede 
218d28ec7eSHans de Goede #include "atomisp_cmd.h"
228d28ec7eSHans de Goede #include "atomisp_csi2.h"
238d28ec7eSHans de Goede #include "atomisp_internal.h"
248d28ec7eSHans de Goede 
250af90783SHans de Goede #define PMC_CLK_RATE_19_2MHZ			19200000
260af90783SHans de Goede 
278d28ec7eSHans de Goede /*
288d28ec7eSHans de Goede  * 79234640-9e10-4fea-a5c1-b5aa8b19756f
298d28ec7eSHans de Goede  * This _DSM GUID returns information about the GPIO lines mapped to a sensor.
308d28ec7eSHans de Goede  * Function number 1 returns a count of the GPIO lines that are mapped.
318d28ec7eSHans de Goede  * Subsequent functions return 32 bit ints encoding information about the GPIO.
328d28ec7eSHans de Goede  */
338d28ec7eSHans de Goede static const guid_t intel_sensor_gpio_info_guid =
348d28ec7eSHans de Goede 	GUID_INIT(0x79234640, 0x9e10, 0x4fea,
358d28ec7eSHans de Goede 		  0xa5, 0xc1, 0xb5, 0xaa, 0x8b, 0x19, 0x75, 0x6f);
368d28ec7eSHans de Goede 
378d28ec7eSHans de Goede #define INTEL_GPIO_DSM_TYPE_SHIFT			0
388d28ec7eSHans de Goede #define INTEL_GPIO_DSM_TYPE_MASK			GENMASK(7, 0)
398d28ec7eSHans de Goede #define INTEL_GPIO_DSM_PIN_SHIFT			8
408d28ec7eSHans de Goede #define INTEL_GPIO_DSM_PIN_MASK				GENMASK(15, 8)
418d28ec7eSHans de Goede #define INTEL_GPIO_DSM_SENSOR_ON_VAL_SHIFT		24
428d28ec7eSHans de Goede #define INTEL_GPIO_DSM_SENSOR_ON_VAL_MASK		GENMASK(31, 24)
438d28ec7eSHans de Goede 
448d28ec7eSHans de Goede #define INTEL_GPIO_DSM_TYPE(x) \
458d28ec7eSHans de Goede 	(((x) & INTEL_GPIO_DSM_TYPE_MASK) >> INTEL_GPIO_DSM_TYPE_SHIFT)
468d28ec7eSHans de Goede #define INTEL_GPIO_DSM_PIN(x) \
478d28ec7eSHans de Goede 	(((x) & INTEL_GPIO_DSM_PIN_MASK) >> INTEL_GPIO_DSM_PIN_SHIFT)
488d28ec7eSHans de Goede #define INTEL_GPIO_DSM_SENSOR_ON_VAL(x) \
498d28ec7eSHans de Goede 	(((x) & INTEL_GPIO_DSM_SENSOR_ON_VAL_MASK) >> INTEL_GPIO_DSM_SENSOR_ON_VAL_SHIFT)
508d28ec7eSHans de Goede 
518d28ec7eSHans de Goede /*
528d28ec7eSHans de Goede  * 822ace8f-2814-4174-a56b-5f029fe079ee
538d28ec7eSHans de Goede  * This _DSM GUID returns a string from the sensor device, which acts as a
548d28ec7eSHans de Goede  * module identifier.
558d28ec7eSHans de Goede  */
568d28ec7eSHans de Goede static const guid_t intel_sensor_module_guid =
578d28ec7eSHans de Goede 	GUID_INIT(0x822ace8f, 0x2814, 0x4174,
588d28ec7eSHans de Goede 		  0xa5, 0x6b, 0x5f, 0x02, 0x9f, 0xe0, 0x79, 0xee);
598d28ec7eSHans de Goede 
608d28ec7eSHans de Goede /*
618d28ec7eSHans de Goede  * dc2f6c4f-045b-4f1d-97b9-882a6860a4be
628d28ec7eSHans de Goede  * This _DSM GUID returns a package with n*2 strings, with each set of 2 strings
638d28ec7eSHans de Goede  * forming a key, value pair for settings like e.g. "CsiLanes" = "1".
648d28ec7eSHans de Goede  */
658d28ec7eSHans de Goede static const guid_t atomisp_dsm_guid =
668d28ec7eSHans de Goede 	GUID_INIT(0xdc2f6c4f, 0x045b, 0x4f1d,
678d28ec7eSHans de Goede 		  0x97, 0xb9, 0x88, 0x2a, 0x68, 0x60, 0xa4, 0xbe);
688d28ec7eSHans de Goede 
69*f663fb49SHans de Goede /*
70*f663fb49SHans de Goede  * 75c9a639-5c8a-4a00-9f48-a9c3b5da789f
71*f663fb49SHans de Goede  * This _DSM GUID returns a string giving the VCM type e.g. "AD5823".
72*f663fb49SHans de Goede  */
73*f663fb49SHans de Goede static const guid_t vcm_dsm_guid =
74*f663fb49SHans de Goede 	GUID_INIT(0x75c9a639, 0x5c8a, 0x4a00,
75*f663fb49SHans de Goede 		  0x9f, 0x48, 0xa9, 0xc3, 0xb5, 0xda, 0x78, 0x9f);
76*f663fb49SHans de Goede 
77f04eedb9SHans de Goede struct atomisp_sensor_config {
78f04eedb9SHans de Goede 	int lanes;
79*f663fb49SHans de Goede 	bool vcm;
808d28ec7eSHans de Goede };
818d28ec7eSHans de Goede 
82*f663fb49SHans de Goede #define ATOMISP_SENSOR_CONFIG(_HID, _LANES, _VCM)			\
83f04eedb9SHans de Goede {									\
84f04eedb9SHans de Goede 	.id = _HID,							\
85f04eedb9SHans de Goede 	.driver_data = (long)&((const struct atomisp_sensor_config) {	\
86f04eedb9SHans de Goede 		.lanes = _LANES,					\
87*f663fb49SHans de Goede 		.vcm = _VCM,						\
88f04eedb9SHans de Goede 	})								\
89f04eedb9SHans de Goede }
90f04eedb9SHans de Goede 
918d28ec7eSHans de Goede /*
928d28ec7eSHans de Goede  * gmin_cfg parsing code. This is a cleaned up version of the gmin_cfg parsing
938d28ec7eSHans de Goede  * code from atomisp_gmin_platform.c.
948d28ec7eSHans de Goede  * Once all sensors are moved to v4l2-async probing atomisp_gmin_platform.c can
958d28ec7eSHans de Goede  * be removed and the duplication of this code goes away.
968d28ec7eSHans de Goede  */
978d28ec7eSHans de Goede struct gmin_cfg_var {
988d28ec7eSHans de Goede 	const char *acpi_dev_name;
998d28ec7eSHans de Goede 	const char *key;
1008d28ec7eSHans de Goede 	const char *val;
1018d28ec7eSHans de Goede };
1028d28ec7eSHans de Goede 
1038d28ec7eSHans de Goede static struct gmin_cfg_var lenovo_ideapad_miix_310_vars[] = {
1048d28ec7eSHans de Goede 	/* _DSM contains the wrong CsiPort! */
1058d28ec7eSHans de Goede 	{ "OVTI2680:01", "CsiPort", "0" },
1068d28ec7eSHans de Goede 	{}
1078d28ec7eSHans de Goede };
1088d28ec7eSHans de Goede 
1098d28ec7eSHans de Goede static const struct dmi_system_id gmin_cfg_dmi_overrides[] = {
1108d28ec7eSHans de Goede 	{
1118d28ec7eSHans de Goede 		/* Lenovo Ideapad Miix 310 */
1128d28ec7eSHans de Goede 		.matches = {
1138d28ec7eSHans de Goede 			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1148d28ec7eSHans de Goede 			DMI_MATCH(DMI_PRODUCT_VERSION, "MIIX 310-10"),
1158d28ec7eSHans de Goede 		},
1168d28ec7eSHans de Goede 		.driver_data = lenovo_ideapad_miix_310_vars,
1178d28ec7eSHans de Goede 	},
1188d28ec7eSHans de Goede 	{}
1198d28ec7eSHans de Goede };
1208d28ec7eSHans de Goede 
gmin_cfg_get_dsm(struct acpi_device * adev,const char * key)1218d28ec7eSHans de Goede static char *gmin_cfg_get_dsm(struct acpi_device *adev, const char *key)
1228d28ec7eSHans de Goede {
1238d28ec7eSHans de Goede 	union acpi_object *obj, *key_el, *val_el;
1248d28ec7eSHans de Goede 	char *val = NULL;
1258d28ec7eSHans de Goede 	int i;
1268d28ec7eSHans de Goede 
1278d28ec7eSHans de Goede 	obj = acpi_evaluate_dsm_typed(adev->handle, &atomisp_dsm_guid, 0, 0,
1288d28ec7eSHans de Goede 				      NULL, ACPI_TYPE_PACKAGE);
1298d28ec7eSHans de Goede 	if (!obj)
1308d28ec7eSHans de Goede 		return NULL;
1318d28ec7eSHans de Goede 
1328d28ec7eSHans de Goede 	for (i = 0; i < obj->package.count - 1; i += 2) {
1338d28ec7eSHans de Goede 		key_el = &obj->package.elements[i + 0];
1348d28ec7eSHans de Goede 		val_el = &obj->package.elements[i + 1];
1358d28ec7eSHans de Goede 
1368d28ec7eSHans de Goede 		if (key_el->type != ACPI_TYPE_STRING || val_el->type != ACPI_TYPE_STRING)
1378d28ec7eSHans de Goede 			break;
1388d28ec7eSHans de Goede 
1398d28ec7eSHans de Goede 		if (!strcmp(key_el->string.pointer, key)) {
1408d28ec7eSHans de Goede 			val = kstrdup(val_el->string.pointer, GFP_KERNEL);
1418d28ec7eSHans de Goede 			if (!val)
1428d28ec7eSHans de Goede 				break;
1438d28ec7eSHans de Goede 
144fc0f5b59SHans de Goede 			acpi_handle_info(adev->handle, "%s: Using DSM entry %s=%s\n",
145fc0f5b59SHans de Goede 					 dev_name(&adev->dev), key, val);
1468d28ec7eSHans de Goede 			break;
1478d28ec7eSHans de Goede 		}
1488d28ec7eSHans de Goede 	}
1498d28ec7eSHans de Goede 
1508d28ec7eSHans de Goede 	ACPI_FREE(obj);
1518d28ec7eSHans de Goede 	return val;
1528d28ec7eSHans de Goede }
1538d28ec7eSHans de Goede 
gmin_cfg_get_dmi_override(struct acpi_device * adev,const char * key)1548d28ec7eSHans de Goede static char *gmin_cfg_get_dmi_override(struct acpi_device *adev, const char *key)
1558d28ec7eSHans de Goede {
1568d28ec7eSHans de Goede 	const struct dmi_system_id *id;
1578d28ec7eSHans de Goede 	struct gmin_cfg_var *gv;
1588d28ec7eSHans de Goede 
1598d28ec7eSHans de Goede 	id = dmi_first_match(gmin_cfg_dmi_overrides);
1608d28ec7eSHans de Goede 	if (!id)
1618d28ec7eSHans de Goede 		return NULL;
1628d28ec7eSHans de Goede 
1638d28ec7eSHans de Goede 	for (gv = id->driver_data; gv->acpi_dev_name; gv++) {
1648d28ec7eSHans de Goede 		if (strcmp(gv->acpi_dev_name, acpi_dev_name(adev)))
1658d28ec7eSHans de Goede 			continue;
1668d28ec7eSHans de Goede 
1678d28ec7eSHans de Goede 		if (strcmp(key, gv->key))
1688d28ec7eSHans de Goede 			continue;
1698d28ec7eSHans de Goede 
170fc0f5b59SHans de Goede 		acpi_handle_info(adev->handle, "%s: Using DMI entry %s=%s\n",
171fc0f5b59SHans de Goede 				 dev_name(&adev->dev), key, gv->val);
1728d28ec7eSHans de Goede 		return kstrdup(gv->val, GFP_KERNEL);
1738d28ec7eSHans de Goede 	}
1748d28ec7eSHans de Goede 
1758d28ec7eSHans de Goede 	return NULL;
1768d28ec7eSHans de Goede }
1778d28ec7eSHans de Goede 
gmin_cfg_get(struct acpi_device * adev,const char * key)1788d28ec7eSHans de Goede static char *gmin_cfg_get(struct acpi_device *adev, const char *key)
1798d28ec7eSHans de Goede {
1808d28ec7eSHans de Goede 	char *val;
1818d28ec7eSHans de Goede 
1828d28ec7eSHans de Goede 	val = gmin_cfg_get_dmi_override(adev, key);
1838d28ec7eSHans de Goede 	if (val)
1848d28ec7eSHans de Goede 		return val;
1858d28ec7eSHans de Goede 
1868d28ec7eSHans de Goede 	return gmin_cfg_get_dsm(adev, key);
1878d28ec7eSHans de Goede }
1888d28ec7eSHans de Goede 
gmin_cfg_get_int(struct acpi_device * adev,const char * key,int default_val)1898d28ec7eSHans de Goede static int gmin_cfg_get_int(struct acpi_device *adev, const char *key, int default_val)
1908d28ec7eSHans de Goede {
1918d28ec7eSHans de Goede 	char *str_val;
1928d28ec7eSHans de Goede 	long int_val;
1938d28ec7eSHans de Goede 	int ret;
1948d28ec7eSHans de Goede 
1958d28ec7eSHans de Goede 	str_val = gmin_cfg_get(adev, key);
1968d28ec7eSHans de Goede 	if (!str_val)
1978d28ec7eSHans de Goede 		goto out_use_default;
1988d28ec7eSHans de Goede 
1998d28ec7eSHans de Goede 	ret = kstrtoul(str_val, 0, &int_val);
2008d28ec7eSHans de Goede 	kfree(str_val);
2018d28ec7eSHans de Goede 	if (ret)
2028d28ec7eSHans de Goede 		goto out_use_default;
2038d28ec7eSHans de Goede 
2048d28ec7eSHans de Goede 	return int_val;
2058d28ec7eSHans de Goede 
2068d28ec7eSHans de Goede out_use_default:
207fc0f5b59SHans de Goede 	acpi_handle_info(adev->handle, "%s: Using default %s=%d\n",
208fc0f5b59SHans de Goede 			 dev_name(&adev->dev), key, default_val);
2098d28ec7eSHans de Goede 	return default_val;
2108d28ec7eSHans de Goede }
2118d28ec7eSHans de Goede 
atomisp_csi2_get_pmc_clk_nr_from_acpi_pr0(struct acpi_device * adev)2128d28ec7eSHans de Goede static int atomisp_csi2_get_pmc_clk_nr_from_acpi_pr0(struct acpi_device *adev)
2138d28ec7eSHans de Goede {
2148d28ec7eSHans de Goede 	/* ACPI_PATH_SEGMENT_LENGTH is guaranteed to be big enough for name + 0 term. */
2158d28ec7eSHans de Goede 	char name[ACPI_PATH_SEGMENT_LENGTH];
2168d28ec7eSHans de Goede 	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
2178d28ec7eSHans de Goede 	struct acpi_buffer b_name = { sizeof(name), name };
2188d28ec7eSHans de Goede 	union acpi_object *package, *element;
2198d28ec7eSHans de Goede 	int i, ret = -ENOENT;
2208d28ec7eSHans de Goede 	acpi_handle rhandle;
2218d28ec7eSHans de Goede 	acpi_status status;
2228d28ec7eSHans de Goede 	u8 clock_num;
2238d28ec7eSHans de Goede 
2248d28ec7eSHans de Goede 	status = acpi_evaluate_object_typed(adev->handle, "_PR0", NULL, &buffer, ACPI_TYPE_PACKAGE);
2258d28ec7eSHans de Goede 	if (ACPI_FAILURE(status))
2268d28ec7eSHans de Goede 		return -ENOENT;
2278d28ec7eSHans de Goede 
2288d28ec7eSHans de Goede 	package = buffer.pointer;
2298d28ec7eSHans de Goede 	for (i = 0; i < package->package.count; i++) {
2308d28ec7eSHans de Goede 		element = &package->package.elements[i];
2318d28ec7eSHans de Goede 
2328d28ec7eSHans de Goede 		if (element->type != ACPI_TYPE_LOCAL_REFERENCE)
2338d28ec7eSHans de Goede 			continue;
2348d28ec7eSHans de Goede 
2358d28ec7eSHans de Goede 		rhandle = element->reference.handle;
2368d28ec7eSHans de Goede 		if (!rhandle)
2378d28ec7eSHans de Goede 			continue;
2388d28ec7eSHans de Goede 
2398d28ec7eSHans de Goede 		acpi_get_name(rhandle, ACPI_SINGLE_NAME, &b_name);
2408d28ec7eSHans de Goede 
2418d28ec7eSHans de Goede 		if (str_has_prefix(name, "CLK") && !kstrtou8(&name[3], 10, &clock_num) &&
2428d28ec7eSHans de Goede 		    clock_num <= 4) {
2438d28ec7eSHans de Goede 			ret = clock_num;
2448d28ec7eSHans de Goede 			break;
2458d28ec7eSHans de Goede 		}
2468d28ec7eSHans de Goede 	}
2478d28ec7eSHans de Goede 
2488d28ec7eSHans de Goede 	ACPI_FREE(buffer.pointer);
2490af90783SHans de Goede 
2500af90783SHans de Goede 	if (ret < 0)
251fc0f5b59SHans de Goede 		acpi_handle_warn(adev->handle, "%s: Could not find PMC clk in _PR0\n",
252fc0f5b59SHans de Goede 				 dev_name(&adev->dev));
2530af90783SHans de Goede 
2548d28ec7eSHans de Goede 	return ret;
2558d28ec7eSHans de Goede }
2568d28ec7eSHans de Goede 
atomisp_csi2_set_pmc_clk_freq(struct acpi_device * adev,int clock_num)2570af90783SHans de Goede static int atomisp_csi2_set_pmc_clk_freq(struct acpi_device *adev, int clock_num)
2588d28ec7eSHans de Goede {
2590af90783SHans de Goede 	struct clk *clk;
2600af90783SHans de Goede 	char name[14];
2610af90783SHans de Goede 	int ret;
2620af90783SHans de Goede 
2630af90783SHans de Goede 	if (clock_num < 0)
2640af90783SHans de Goede 		return 0;
2650af90783SHans de Goede 
2660af90783SHans de Goede 	snprintf(name, sizeof(name), "pmc_plt_clk_%d", clock_num);
2670af90783SHans de Goede 
2680af90783SHans de Goede 	clk = clk_get(NULL, name);
2690af90783SHans de Goede 	if (IS_ERR(clk)) {
2700af90783SHans de Goede 		ret = PTR_ERR(clk);
271fc0f5b59SHans de Goede 		acpi_handle_err(adev->handle, "%s: Error getting clk %s: %d\n",
272fc0f5b59SHans de Goede 				dev_name(&adev->dev), name, ret);
2730af90783SHans de Goede 		return ret;
2740af90783SHans de Goede 	}
2758d28ec7eSHans de Goede 
2768d28ec7eSHans de Goede 	/*
2770af90783SHans de Goede 	 * The firmware might enable the clock at boot, to change
2780af90783SHans de Goede 	 * the rate we must ensure the clock is disabled.
2798d28ec7eSHans de Goede 	 */
2800af90783SHans de Goede 	ret = clk_prepare_enable(clk);
2810af90783SHans de Goede 	if (!ret)
2820af90783SHans de Goede 		clk_disable_unprepare(clk);
2830af90783SHans de Goede 	if (!ret)
2840af90783SHans de Goede 		ret = clk_set_rate(clk, PMC_CLK_RATE_19_2MHZ);
2850af90783SHans de Goede 	if (ret)
286fc0f5b59SHans de Goede 		acpi_handle_err(adev->handle, "%s: Error setting clk-rate for %s: %d\n",
287fc0f5b59SHans de Goede 				dev_name(&adev->dev), name, ret);
2880af90783SHans de Goede 
2890af90783SHans de Goede 	clk_put(clk);
2900af90783SHans de Goede 	return ret;
2910af90783SHans de Goede }
2920af90783SHans de Goede 
atomisp_csi2_get_port(struct acpi_device * adev,int clock_num)2930af90783SHans de Goede static int atomisp_csi2_get_port(struct acpi_device *adev, int clock_num)
2940af90783SHans de Goede {
2950af90783SHans de Goede 	int port;
2960af90783SHans de Goede 
2970af90783SHans de Goede 	/*
2980af90783SHans de Goede 	 * Compare clock-number to the PMC-clock used for CsiPort 1
2990af90783SHans de Goede 	 * in the CHT/BYT reference designs.
3000af90783SHans de Goede 	 */
3018d28ec7eSHans de Goede 	if (IS_ISP2401)
3028d28ec7eSHans de Goede 		port = clock_num == 4 ? 1 : 0;
3038d28ec7eSHans de Goede 	else
3048d28ec7eSHans de Goede 		port = clock_num == 0 ? 1 : 0;
3058d28ec7eSHans de Goede 
3060af90783SHans de Goede 	/* Intel DSM or DMI quirk overrides _PR0 CLK derived default */
3078d28ec7eSHans de Goede 	return gmin_cfg_get_int(adev, "CsiPort", port);
3088d28ec7eSHans de Goede }
3098d28ec7eSHans de Goede 
3108d28ec7eSHans de Goede /* Note this always returns 1 to continue looping so that res_count is accurate */
atomisp_csi2_handle_acpi_gpio_res(struct acpi_resource * ares,void * _data)3118d28ec7eSHans de Goede static int atomisp_csi2_handle_acpi_gpio_res(struct acpi_resource *ares, void *_data)
3128d28ec7eSHans de Goede {
3138d28ec7eSHans de Goede 	struct atomisp_csi2_acpi_gpio_parsing_data *data = _data;
3148d28ec7eSHans de Goede 	struct acpi_resource_gpio *agpio;
3158d28ec7eSHans de Goede 	const char *name;
3168d28ec7eSHans de Goede 	bool active_low;
3178d28ec7eSHans de Goede 	unsigned int i;
3188d28ec7eSHans de Goede 	u32 settings = 0;
3198d28ec7eSHans de Goede 	u16 pin;
3208d28ec7eSHans de Goede 
3218d28ec7eSHans de Goede 	if (!acpi_gpio_get_io_resource(ares, &agpio))
3228d28ec7eSHans de Goede 		return 1; /* Not a GPIO, continue the loop */
3238d28ec7eSHans de Goede 
3248d28ec7eSHans de Goede 	data->res_count++;
3258d28ec7eSHans de Goede 
3268d28ec7eSHans de Goede 	pin = agpio->pin_table[0];
3278d28ec7eSHans de Goede 	for (i = 0; i < data->settings_count; i++) {
3288d28ec7eSHans de Goede 		if (INTEL_GPIO_DSM_PIN(data->settings[i]) == pin) {
3298d28ec7eSHans de Goede 			settings = data->settings[i];
3308d28ec7eSHans de Goede 			break;
3318d28ec7eSHans de Goede 		}
3328d28ec7eSHans de Goede 	}
3338d28ec7eSHans de Goede 
3348d28ec7eSHans de Goede 	if (i == data->settings_count) {
3358d28ec7eSHans de Goede 		acpi_handle_warn(data->adev->handle,
336fc0f5b59SHans de Goede 				 "%s: Could not find DSM GPIO settings for pin %u\n",
337fc0f5b59SHans de Goede 				 dev_name(&data->adev->dev), pin);
3388d28ec7eSHans de Goede 		return 1;
3398d28ec7eSHans de Goede 	}
3408d28ec7eSHans de Goede 
3418d28ec7eSHans de Goede 	switch (INTEL_GPIO_DSM_TYPE(settings)) {
3428d28ec7eSHans de Goede 	case 0:
3438d28ec7eSHans de Goede 		name = "reset-gpios";
3448d28ec7eSHans de Goede 		break;
3458d28ec7eSHans de Goede 	case 1:
3468d28ec7eSHans de Goede 		name = "powerdown-gpios";
3478d28ec7eSHans de Goede 		break;
3488d28ec7eSHans de Goede 	default:
349fc0f5b59SHans de Goede 		acpi_handle_warn(data->adev->handle, "%s: Unknown GPIO type 0x%02lx for pin %u\n",
350fc0f5b59SHans de Goede 				 dev_name(&data->adev->dev),
3518d28ec7eSHans de Goede 				 INTEL_GPIO_DSM_TYPE(settings), pin);
3528d28ec7eSHans de Goede 		return 1;
3538d28ec7eSHans de Goede 	}
3548d28ec7eSHans de Goede 
3558d28ec7eSHans de Goede 	/*
3568d28ec7eSHans de Goede 	 * Both reset and power-down need to be logical false when the sensor
3578d28ec7eSHans de Goede 	 * is on (sensor should not be in reset and not be powered-down). So
3588d28ec7eSHans de Goede 	 * when the sensor-on-value (which is the physical pin value) is high,
3598d28ec7eSHans de Goede 	 * then the signal is active-low.
3608d28ec7eSHans de Goede 	 */
3618d28ec7eSHans de Goede 	active_low = INTEL_GPIO_DSM_SENSOR_ON_VAL(settings);
3628d28ec7eSHans de Goede 
3638d28ec7eSHans de Goede 	i = data->map_count;
3648d28ec7eSHans de Goede 	if (i == CSI2_MAX_ACPI_GPIOS)
3658d28ec7eSHans de Goede 		return 1;
3668d28ec7eSHans de Goede 
3678d28ec7eSHans de Goede 	/* res_count is already incremented */
3688d28ec7eSHans de Goede 	data->map->params[i].crs_entry_index = data->res_count - 1;
3698d28ec7eSHans de Goede 	data->map->params[i].active_low = active_low;
3708d28ec7eSHans de Goede 	data->map->mapping[i].name = name;
3718d28ec7eSHans de Goede 	data->map->mapping[i].data = &data->map->params[i];
3728d28ec7eSHans de Goede 	data->map->mapping[i].size = 1;
3738d28ec7eSHans de Goede 	data->map_count++;
3748d28ec7eSHans de Goede 
375fc0f5b59SHans de Goede 	acpi_handle_info(data->adev->handle, "%s: %s crs %d %s pin %u active-%s\n",
376fc0f5b59SHans de Goede 			 dev_name(&data->adev->dev), name,
3778d28ec7eSHans de Goede 			 data->res_count - 1, agpio->resource_source.string_ptr,
3788d28ec7eSHans de Goede 			 pin, active_low ? "low" : "high");
3798d28ec7eSHans de Goede 
3808d28ec7eSHans de Goede 	return 1;
3818d28ec7eSHans de Goede }
3828d28ec7eSHans de Goede 
3838d28ec7eSHans de Goede /*
3848d28ec7eSHans de Goede  * Helper function to create an ACPI GPIO lookup table for sensor reset and
3858d28ec7eSHans de Goede  * powerdown signals on Intel Bay Trail (BYT) and Cherry Trail (CHT) devices,
3868d28ec7eSHans de Goede  * including setting the correct polarity for the GPIO.
3878d28ec7eSHans de Goede  *
3888d28ec7eSHans de Goede  * This uses the "79234640-9e10-4fea-a5c1-b5aa8b19756f" DSM method directly
3898d28ec7eSHans de Goede  * on the sensor device's ACPI node. This is different from later Intel
3908d28ec7eSHans de Goede  * hardware which has a separate INT3472 acpi_device with this info.
3918d28ec7eSHans de Goede  *
3928d28ec7eSHans de Goede  * This function must be called before creating the sw-noded describing
3938d28ec7eSHans de Goede  * the fwnode graph endpoint. And sensor drivers used on these devices
3948d28ec7eSHans de Goede  * must return -EPROBE_DEFER when there is no endpoint description yet.
3958d28ec7eSHans de Goede  * Together this guarantees that the GPIO lookups are in place before
3968d28ec7eSHans de Goede  * the sensor driver tries to get GPIOs with gpiod_get().
3978d28ec7eSHans de Goede  *
3988d28ec7eSHans de Goede  * Note this code uses the same DSM GUID as the int3472_gpio_guid in
3998d28ec7eSHans de Goede  * the INT3472 discrete.c code and there is some overlap, but there are
4008d28ec7eSHans de Goede  * enough differences that it is difficult to share the code.
4018d28ec7eSHans de Goede  */
atomisp_csi2_add_gpio_mappings(struct acpi_device * adev)402f04eedb9SHans de Goede static int atomisp_csi2_add_gpio_mappings(struct acpi_device *adev)
4038d28ec7eSHans de Goede {
4048d28ec7eSHans de Goede 	struct atomisp_csi2_acpi_gpio_parsing_data data = { };
4058d28ec7eSHans de Goede 	LIST_HEAD(resource_list);
4068d28ec7eSHans de Goede 	union acpi_object *obj;
4078d28ec7eSHans de Goede 	unsigned int i, j;
4088d28ec7eSHans de Goede 	int ret;
4098d28ec7eSHans de Goede 
4108d28ec7eSHans de Goede 	obj = acpi_evaluate_dsm_typed(adev->handle, &intel_sensor_module_guid,
4118d28ec7eSHans de Goede 				      0x00, 1, NULL, ACPI_TYPE_STRING);
4128d28ec7eSHans de Goede 	if (obj) {
413fc0f5b59SHans de Goede 		acpi_handle_info(adev->handle, "%s: Sensor module id: '%s'\n",
414fc0f5b59SHans de Goede 				 dev_name(&adev->dev), obj->string.pointer);
4158d28ec7eSHans de Goede 		ACPI_FREE(obj);
4168d28ec7eSHans de Goede 	}
4178d28ec7eSHans de Goede 
4188d28ec7eSHans de Goede 	/*
4198d28ec7eSHans de Goede 	 * First get the GPIO-settings count and then get count GPIO-settings
4208d28ec7eSHans de Goede 	 * values. Note the order of these may differ from the order in which
4218d28ec7eSHans de Goede 	 * the GPIOs are listed on the ACPI resources! So we first store them all
4228d28ec7eSHans de Goede 	 * and then enumerate the ACPI resources and match them up by pin number.
4238d28ec7eSHans de Goede 	 */
4248d28ec7eSHans de Goede 	obj = acpi_evaluate_dsm_typed(adev->handle,
4258d28ec7eSHans de Goede 				      &intel_sensor_gpio_info_guid, 0x00, 1,
4268d28ec7eSHans de Goede 				      NULL, ACPI_TYPE_INTEGER);
4278d28ec7eSHans de Goede 	if (!obj) {
428fc0f5b59SHans de Goede 		acpi_handle_err(adev->handle, "%s: No _DSM entry for GPIO pin count\n",
429fc0f5b59SHans de Goede 				dev_name(&adev->dev));
4308d28ec7eSHans de Goede 		return -EIO;
4318d28ec7eSHans de Goede 	}
4328d28ec7eSHans de Goede 
4338d28ec7eSHans de Goede 	data.settings_count = obj->integer.value;
4348d28ec7eSHans de Goede 	ACPI_FREE(obj);
4358d28ec7eSHans de Goede 
4368d28ec7eSHans de Goede 	if (data.settings_count > CSI2_MAX_ACPI_GPIOS) {
437fc0f5b59SHans de Goede 		acpi_handle_err(adev->handle, "%s: Too many GPIOs %u > %u\n",
438fc0f5b59SHans de Goede 				dev_name(&adev->dev), data.settings_count,
439fc0f5b59SHans de Goede 				CSI2_MAX_ACPI_GPIOS);
4408d28ec7eSHans de Goede 		return -EOVERFLOW;
4418d28ec7eSHans de Goede 	}
4428d28ec7eSHans de Goede 
4438d28ec7eSHans de Goede 	for (i = 0; i < data.settings_count; i++) {
4448d28ec7eSHans de Goede 		/*
4458d28ec7eSHans de Goede 		 * i + 2 because the index of this _DSM function is 1-based
4468d28ec7eSHans de Goede 		 * and the first function is just a count.
4478d28ec7eSHans de Goede 		 */
4488d28ec7eSHans de Goede 		obj = acpi_evaluate_dsm_typed(adev->handle,
4498d28ec7eSHans de Goede 					      &intel_sensor_gpio_info_guid,
4508d28ec7eSHans de Goede 					      0x00, i + 2,
4518d28ec7eSHans de Goede 					      NULL, ACPI_TYPE_INTEGER);
4528d28ec7eSHans de Goede 		if (!obj) {
453fc0f5b59SHans de Goede 			acpi_handle_err(adev->handle, "%s: No _DSM entry for pin %u\n",
454fc0f5b59SHans de Goede 					dev_name(&adev->dev), i);
4558d28ec7eSHans de Goede 			return -EIO;
4568d28ec7eSHans de Goede 		}
4578d28ec7eSHans de Goede 
4588d28ec7eSHans de Goede 		data.settings[i] = obj->integer.value;
4598d28ec7eSHans de Goede 		ACPI_FREE(obj);
4608d28ec7eSHans de Goede 	}
4618d28ec7eSHans de Goede 
4628d28ec7eSHans de Goede 	/* Since we match up by pin-number the pin-numbers must be unique */
4638d28ec7eSHans de Goede 	for (i = 0; i < data.settings_count; i++) {
4648d28ec7eSHans de Goede 		for (j = i + 1; j < data.settings_count; j++) {
4658d28ec7eSHans de Goede 			if (INTEL_GPIO_DSM_PIN(data.settings[i]) !=
4668d28ec7eSHans de Goede 			    INTEL_GPIO_DSM_PIN(data.settings[j]))
4678d28ec7eSHans de Goede 				continue;
4688d28ec7eSHans de Goede 
469fc0f5b59SHans de Goede 			acpi_handle_err(adev->handle, "%s: Duplicate pin number %lu\n",
470fc0f5b59SHans de Goede 					dev_name(&adev->dev),
4718d28ec7eSHans de Goede 					INTEL_GPIO_DSM_PIN(data.settings[i]));
4728d28ec7eSHans de Goede 			return -EIO;
4738d28ec7eSHans de Goede 		}
4748d28ec7eSHans de Goede 	}
4758d28ec7eSHans de Goede 
476f04eedb9SHans de Goede 	data.map = kzalloc(sizeof(*data.map), GFP_KERNEL);
477f04eedb9SHans de Goede 	if (!data.map)
478f04eedb9SHans de Goede 		return -ENOMEM;
479f04eedb9SHans de Goede 
4808d28ec7eSHans de Goede 	/* Now parse the ACPI resources and build the lookup table */
4818d28ec7eSHans de Goede 	data.adev = adev;
4828d28ec7eSHans de Goede 	ret = acpi_dev_get_resources(adev, &resource_list,
4838d28ec7eSHans de Goede 				     atomisp_csi2_handle_acpi_gpio_res, &data);
4848d28ec7eSHans de Goede 	if (ret < 0)
4858d28ec7eSHans de Goede 		return ret;
4868d28ec7eSHans de Goede 
4878d28ec7eSHans de Goede 	acpi_dev_free_resource_list(&resource_list);
4888d28ec7eSHans de Goede 
4898d28ec7eSHans de Goede 	if (data.map_count != data.settings_count ||
4908d28ec7eSHans de Goede 	    data.res_count != data.settings_count)
491fc0f5b59SHans de Goede 		acpi_handle_warn(adev->handle, "%s: ACPI GPIO resources vs DSM GPIO-info count mismatch (dsm: %d res: %d map %d\n",
492fc0f5b59SHans de Goede 				 dev_name(&adev->dev), data.settings_count,
493fc0f5b59SHans de Goede 				 data.res_count, data.map_count);
4948d28ec7eSHans de Goede 
4958d28ec7eSHans de Goede 	ret = acpi_dev_add_driver_gpios(adev, data.map->mapping);
4968d28ec7eSHans de Goede 	if (ret)
497fc0f5b59SHans de Goede 		acpi_handle_err(adev->handle, "%s: Error adding driver GPIOs: %d\n",
498fc0f5b59SHans de Goede 				dev_name(&adev->dev), ret);
4998d28ec7eSHans de Goede 
5008d28ec7eSHans de Goede 	return ret;
5018d28ec7eSHans de Goede }
5028d28ec7eSHans de Goede 
atomisp_csi2_get_vcm_type(struct acpi_device * adev)503*f663fb49SHans de Goede static char *atomisp_csi2_get_vcm_type(struct acpi_device *adev)
504*f663fb49SHans de Goede {
505*f663fb49SHans de Goede 	union acpi_object *obj;
506*f663fb49SHans de Goede 	char *vcm_type;
507*f663fb49SHans de Goede 
508*f663fb49SHans de Goede 	obj = acpi_evaluate_dsm_typed(adev->handle, &vcm_dsm_guid, 0, 0,
509*f663fb49SHans de Goede 				      NULL, ACPI_TYPE_STRING);
510*f663fb49SHans de Goede 	if (!obj)
511*f663fb49SHans de Goede 		return NULL;
512*f663fb49SHans de Goede 
513*f663fb49SHans de Goede 	vcm_type = kstrdup(obj->string.pointer, GFP_KERNEL);
514*f663fb49SHans de Goede 	ACPI_FREE(obj);
515*f663fb49SHans de Goede 
516*f663fb49SHans de Goede 	if (!vcm_type)
517*f663fb49SHans de Goede 		return NULL;
518*f663fb49SHans de Goede 
519*f663fb49SHans de Goede 	string_lower(vcm_type, vcm_type);
520*f663fb49SHans de Goede 	return vcm_type;
521*f663fb49SHans de Goede }
522*f663fb49SHans de Goede 
523f04eedb9SHans de Goede static const struct acpi_device_id atomisp_sensor_configs[] = {
524*f663fb49SHans de Goede 	ATOMISP_SENSOR_CONFIG("INT33BE", 2, true),	/* OV5693 */
525f04eedb9SHans de Goede 	{}
5268d28ec7eSHans de Goede };
5278d28ec7eSHans de Goede 
atomisp_csi2_parse_sensor_fwnode(struct acpi_device * adev,struct ipu_sensor * sensor)528f04eedb9SHans de Goede static int atomisp_csi2_parse_sensor_fwnode(struct acpi_device *adev,
529f04eedb9SHans de Goede 					    struct ipu_sensor *sensor)
5308d28ec7eSHans de Goede {
531f04eedb9SHans de Goede 	const struct acpi_device_id *id;
5320af90783SHans de Goede 	int ret, clock_num;
533*f663fb49SHans de Goede 	bool vcm = false;
534f04eedb9SHans de Goede 	int lanes = 1;
5358d28ec7eSHans de Goede 
536f04eedb9SHans de Goede 	id = acpi_match_acpi_device(atomisp_sensor_configs, adev);
537f04eedb9SHans de Goede 	if (id) {
538f04eedb9SHans de Goede 		struct atomisp_sensor_config *cfg =
539f04eedb9SHans de Goede 			(struct atomisp_sensor_config *)id->driver_data;
5408d28ec7eSHans de Goede 
541f04eedb9SHans de Goede 		lanes = cfg->lanes;
542*f663fb49SHans de Goede 		vcm = cfg->vcm;
5438d28ec7eSHans de Goede 	}
5448d28ec7eSHans de Goede 
5450af90783SHans de Goede 	/*
5460af90783SHans de Goede 	 * ACPI takes care of turning the PMC clock on and off, but on BYT
5470af90783SHans de Goede 	 * the clock defaults to 25 MHz instead of the expected 19.2 MHz.
548f04eedb9SHans de Goede 	 * Get the PMC-clock number from ACPI PR0 method and set it to 19.2 MHz.
5490af90783SHans de Goede 	 * The PMC-clock number is also used to determine the default CSI port.
5500af90783SHans de Goede 	 */
5510af90783SHans de Goede 	clock_num = atomisp_csi2_get_pmc_clk_nr_from_acpi_pr0(adev);
5520af90783SHans de Goede 
5530af90783SHans de Goede 	ret = atomisp_csi2_set_pmc_clk_freq(adev, clock_num);
5540af90783SHans de Goede 	if (ret)
555f04eedb9SHans de Goede 		return ret;
5560af90783SHans de Goede 
557f04eedb9SHans de Goede 	sensor->link = atomisp_csi2_get_port(adev, clock_num);
558f04eedb9SHans de Goede 	if (sensor->link >= ATOMISP_CAMERA_NR_PORTS) {
559f04eedb9SHans de Goede 		acpi_handle_err(adev->handle, "%s: Invalid port: %u\n",
560f04eedb9SHans de Goede 				dev_name(&adev->dev), sensor->link);
561f04eedb9SHans de Goede 		return -EINVAL;
5628d28ec7eSHans de Goede 	}
5638d28ec7eSHans de Goede 
564f04eedb9SHans de Goede 	sensor->lanes = gmin_cfg_get_int(adev, "CsiLanes", lanes);
565f04eedb9SHans de Goede 	if (sensor->lanes > IPU_MAX_LANES) {
566f04eedb9SHans de Goede 		acpi_handle_err(adev->handle, "%s: Invalid lane-count: %d\n",
567f04eedb9SHans de Goede 				dev_name(&adev->dev), sensor->lanes);
568f04eedb9SHans de Goede 		return -EINVAL;
5698d28ec7eSHans de Goede 	}
5708d28ec7eSHans de Goede 
571f04eedb9SHans de Goede 	ret = atomisp_csi2_add_gpio_mappings(adev);
5728d28ec7eSHans de Goede 	if (ret)
573f04eedb9SHans de Goede 		return ret;
5748d28ec7eSHans de Goede 
575f04eedb9SHans de Goede 	sensor->mclkspeed = PMC_CLK_RATE_19_2MHZ;
576f04eedb9SHans de Goede 	sensor->rotation = 0;
577f04eedb9SHans de Goede 	sensor->orientation = (sensor->link == 1) ?
578f04eedb9SHans de Goede 		V4L2_FWNODE_ORIENTATION_BACK : V4L2_FWNODE_ORIENTATION_FRONT;
5798d28ec7eSHans de Goede 
580*f663fb49SHans de Goede 	if (vcm)
581*f663fb49SHans de Goede 		sensor->vcm_type = atomisp_csi2_get_vcm_type(adev);
582*f663fb49SHans de Goede 
5838d28ec7eSHans de Goede 	return 0;
5848d28ec7eSHans de Goede }
5858d28ec7eSHans de Goede 
atomisp_csi2_bridge_init(struct atomisp_device * isp)5868d28ec7eSHans de Goede int atomisp_csi2_bridge_init(struct atomisp_device *isp)
5878d28ec7eSHans de Goede {
5888d28ec7eSHans de Goede 	struct device *dev = isp->dev;
5898d28ec7eSHans de Goede 	struct fwnode_handle *fwnode;
5908d28ec7eSHans de Goede 
5918d28ec7eSHans de Goede 	/*
5928d28ec7eSHans de Goede 	 * This function is intended to run only once and then leave
5938d28ec7eSHans de Goede 	 * the created nodes attached even after a rmmod, therefore:
5948d28ec7eSHans de Goede 	 * 1. The bridge memory is leaked deliberately on success
5958d28ec7eSHans de Goede 	 * 2. If a secondary fwnode is already set exit early.
5968d28ec7eSHans de Goede 	 */
5978d28ec7eSHans de Goede 	fwnode = dev_fwnode(dev);
5988d28ec7eSHans de Goede 	if (fwnode && fwnode->secondary)
5998d28ec7eSHans de Goede 		return 0;
6008d28ec7eSHans de Goede 
601f04eedb9SHans de Goede 	return ipu_bridge_init(dev, atomisp_csi2_parse_sensor_fwnode);
6028d28ec7eSHans de Goede }
6038d28ec7eSHans de Goede 
6048d28ec7eSHans de Goede /******* V4L2 sub-device asynchronous registration callbacks***********/
6058d28ec7eSHans de Goede 
6068d28ec7eSHans de Goede struct sensor_async_subdev {
607adb2dcd5SSakari Ailus 	struct v4l2_async_connection asd;
6088d28ec7eSHans de Goede 	int port;
6098d28ec7eSHans de Goede };
6108d28ec7eSHans de Goede 
6118d28ec7eSHans de Goede #define to_sensor_asd(a)	container_of(a, struct sensor_async_subdev, asd)
6128d28ec7eSHans de Goede #define notifier_to_atomisp(n)	container_of(n, struct atomisp_device, notifier)
6138d28ec7eSHans de Goede 
6148d28ec7eSHans de Goede /* .bound() notifier callback when a match is found */
atomisp_notifier_bound(struct v4l2_async_notifier * notifier,struct v4l2_subdev * sd,struct v4l2_async_connection * asd)6158d28ec7eSHans de Goede static int atomisp_notifier_bound(struct v4l2_async_notifier *notifier,
6168d28ec7eSHans de Goede 				  struct v4l2_subdev *sd,
617adb2dcd5SSakari Ailus 				  struct v4l2_async_connection *asd)
6188d28ec7eSHans de Goede {
6198d28ec7eSHans de Goede 	struct atomisp_device *isp = notifier_to_atomisp(notifier);
6208d28ec7eSHans de Goede 	struct sensor_async_subdev *s_asd = to_sensor_asd(asd);
621*f663fb49SHans de Goede 	int ret;
6228d28ec7eSHans de Goede 
6238d28ec7eSHans de Goede 	if (s_asd->port >= ATOMISP_CAMERA_NR_PORTS) {
6248d28ec7eSHans de Goede 		dev_err(isp->dev, "port %d not supported\n", s_asd->port);
6258d28ec7eSHans de Goede 		return -EINVAL;
6268d28ec7eSHans de Goede 	}
6278d28ec7eSHans de Goede 
6288d28ec7eSHans de Goede 	if (isp->sensor_subdevs[s_asd->port]) {
6298d28ec7eSHans de Goede 		dev_err(isp->dev, "port %d already has a sensor attached\n", s_asd->port);
6308d28ec7eSHans de Goede 		return -EBUSY;
6318d28ec7eSHans de Goede 	}
6328d28ec7eSHans de Goede 
633*f663fb49SHans de Goede 	ret = ipu_bridge_instantiate_vcm(sd->dev);
634*f663fb49SHans de Goede 	if (ret)
635*f663fb49SHans de Goede 		return ret;
636*f663fb49SHans de Goede 
6378d28ec7eSHans de Goede 	isp->sensor_subdevs[s_asd->port] = sd;
6388d28ec7eSHans de Goede 	return 0;
6398d28ec7eSHans de Goede }
6408d28ec7eSHans de Goede 
6418d28ec7eSHans de Goede /* The .unbind callback */
atomisp_notifier_unbind(struct v4l2_async_notifier * notifier,struct v4l2_subdev * sd,struct v4l2_async_connection * asd)6428d28ec7eSHans de Goede static void atomisp_notifier_unbind(struct v4l2_async_notifier *notifier,
6438d28ec7eSHans de Goede 				    struct v4l2_subdev *sd,
644adb2dcd5SSakari Ailus 				    struct v4l2_async_connection *asd)
6458d28ec7eSHans de Goede {
6468d28ec7eSHans de Goede 	struct atomisp_device *isp = notifier_to_atomisp(notifier);
6478d28ec7eSHans de Goede 	struct sensor_async_subdev *s_asd = to_sensor_asd(asd);
6488d28ec7eSHans de Goede 
6498d28ec7eSHans de Goede 	isp->sensor_subdevs[s_asd->port] = NULL;
6508d28ec7eSHans de Goede }
6518d28ec7eSHans de Goede 
6528d28ec7eSHans de Goede /* .complete() is called after all subdevices have been located */
atomisp_notifier_complete(struct v4l2_async_notifier * notifier)6538d28ec7eSHans de Goede static int atomisp_notifier_complete(struct v4l2_async_notifier *notifier)
6548d28ec7eSHans de Goede {
6558d28ec7eSHans de Goede 	struct atomisp_device *isp = notifier_to_atomisp(notifier);
6568d28ec7eSHans de Goede 
6578d28ec7eSHans de Goede 	return atomisp_register_device_nodes(isp);
6588d28ec7eSHans de Goede }
6598d28ec7eSHans de Goede 
6608d28ec7eSHans de Goede static const struct v4l2_async_notifier_operations atomisp_async_ops = {
6618d28ec7eSHans de Goede 	.bound = atomisp_notifier_bound,
6628d28ec7eSHans de Goede 	.unbind = atomisp_notifier_unbind,
6638d28ec7eSHans de Goede 	.complete = atomisp_notifier_complete,
6648d28ec7eSHans de Goede };
6658d28ec7eSHans de Goede 
atomisp_csi2_bridge_parse_firmware(struct atomisp_device * isp)6668d28ec7eSHans de Goede int atomisp_csi2_bridge_parse_firmware(struct atomisp_device *isp)
6678d28ec7eSHans de Goede {
6688d28ec7eSHans de Goede 	int i, mipi_port, ret;
6698d28ec7eSHans de Goede 
670b8ec754aSSakari Ailus 	v4l2_async_nf_init(&isp->notifier, &isp->v4l2_dev);
6718d28ec7eSHans de Goede 	isp->notifier.ops = &atomisp_async_ops;
6728d28ec7eSHans de Goede 
6738d28ec7eSHans de Goede 	for (i = 0; i < ATOMISP_CAMERA_NR_PORTS; i++) {
6748d28ec7eSHans de Goede 		struct v4l2_fwnode_endpoint vep = {
6758d28ec7eSHans de Goede 			.bus_type = V4L2_MBUS_CSI2_DPHY,
6768d28ec7eSHans de Goede 		};
6778d28ec7eSHans de Goede 		struct sensor_async_subdev *s_asd;
6788d28ec7eSHans de Goede 		struct fwnode_handle *ep;
6798d28ec7eSHans de Goede 
6808d28ec7eSHans de Goede 		ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(isp->dev), i, 0,
6818d28ec7eSHans de Goede 						     FWNODE_GRAPH_ENDPOINT_NEXT);
6828d28ec7eSHans de Goede 		if (!ep)
6838d28ec7eSHans de Goede 			continue;
6848d28ec7eSHans de Goede 
6858d28ec7eSHans de Goede 		ret = v4l2_fwnode_endpoint_parse(ep, &vep);
6868d28ec7eSHans de Goede 		if (ret)
6878d28ec7eSHans de Goede 			goto err_parse;
6888d28ec7eSHans de Goede 
6898d28ec7eSHans de Goede 		if (vep.base.port >= ATOMISP_CAMERA_NR_PORTS) {
6908d28ec7eSHans de Goede 			dev_err(isp->dev, "port %d not supported\n", vep.base.port);
6918d28ec7eSHans de Goede 			ret = -EINVAL;
6928d28ec7eSHans de Goede 			goto err_parse;
6938d28ec7eSHans de Goede 		}
6948d28ec7eSHans de Goede 
6958d28ec7eSHans de Goede 		mipi_port = atomisp_port_to_mipi_port(isp, vep.base.port);
6968d28ec7eSHans de Goede 		isp->sensor_lanes[mipi_port] = vep.bus.mipi_csi2.num_data_lanes;
6978d28ec7eSHans de Goede 
6988d28ec7eSHans de Goede 		s_asd = v4l2_async_nf_add_fwnode_remote(&isp->notifier, ep,
6998d28ec7eSHans de Goede 							struct sensor_async_subdev);
7008d28ec7eSHans de Goede 		if (IS_ERR(s_asd)) {
7018d28ec7eSHans de Goede 			ret = PTR_ERR(s_asd);
7028d28ec7eSHans de Goede 			goto err_parse;
7038d28ec7eSHans de Goede 		}
7048d28ec7eSHans de Goede 
7058d28ec7eSHans de Goede 		s_asd->port = vep.base.port;
7068d28ec7eSHans de Goede 
7078d28ec7eSHans de Goede 		fwnode_handle_put(ep);
7088d28ec7eSHans de Goede 		continue;
7098d28ec7eSHans de Goede 
7108d28ec7eSHans de Goede err_parse:
7118d28ec7eSHans de Goede 		fwnode_handle_put(ep);
7128d28ec7eSHans de Goede 		return ret;
7138d28ec7eSHans de Goede 	}
7148d28ec7eSHans de Goede 
7158d28ec7eSHans de Goede 	return 0;
7168d28ec7eSHans de Goede }
717