18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * V4L2 fwnode binding parsing library 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * The origins of the V4L2 fwnode library are in V4L2 OF library that 68c2ecf20Sopenharmony_ci * formerly was located in v4l2-of.c. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (c) 2016 Intel Corporation. 98c2ecf20Sopenharmony_ci * Author: Sakari Ailus <sakari.ailus@linux.intel.com> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd. 128c2ecf20Sopenharmony_ci * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Copyright (C) 2012 Renesas Electronics Corp. 158c2ecf20Sopenharmony_ci * Author: Guennadi Liakhovetski <g.liakhovetski@gmx.de> 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci#include <linux/acpi.h> 188c2ecf20Sopenharmony_ci#include <linux/kernel.h> 198c2ecf20Sopenharmony_ci#include <linux/mm.h> 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/of.h> 228c2ecf20Sopenharmony_ci#include <linux/property.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci#include <linux/string.h> 258c2ecf20Sopenharmony_ci#include <linux/types.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <media/v4l2-async.h> 288c2ecf20Sopenharmony_ci#include <media/v4l2-fwnode.h> 298c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cienum v4l2_fwnode_bus_type { 328c2ecf20Sopenharmony_ci V4L2_FWNODE_BUS_TYPE_GUESS = 0, 338c2ecf20Sopenharmony_ci V4L2_FWNODE_BUS_TYPE_CSI2_CPHY, 348c2ecf20Sopenharmony_ci V4L2_FWNODE_BUS_TYPE_CSI1, 358c2ecf20Sopenharmony_ci V4L2_FWNODE_BUS_TYPE_CCP2, 368c2ecf20Sopenharmony_ci V4L2_FWNODE_BUS_TYPE_CSI2_DPHY, 378c2ecf20Sopenharmony_ci V4L2_FWNODE_BUS_TYPE_PARALLEL, 388c2ecf20Sopenharmony_ci V4L2_FWNODE_BUS_TYPE_BT656, 398c2ecf20Sopenharmony_ci NR_OF_V4L2_FWNODE_BUS_TYPE, 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic const struct v4l2_fwnode_bus_conv { 438c2ecf20Sopenharmony_ci enum v4l2_fwnode_bus_type fwnode_bus_type; 448c2ecf20Sopenharmony_ci enum v4l2_mbus_type mbus_type; 458c2ecf20Sopenharmony_ci const char *name; 468c2ecf20Sopenharmony_ci} buses[] = { 478c2ecf20Sopenharmony_ci { 488c2ecf20Sopenharmony_ci V4L2_FWNODE_BUS_TYPE_GUESS, 498c2ecf20Sopenharmony_ci V4L2_MBUS_UNKNOWN, 508c2ecf20Sopenharmony_ci "not specified", 518c2ecf20Sopenharmony_ci }, { 528c2ecf20Sopenharmony_ci V4L2_FWNODE_BUS_TYPE_CSI2_CPHY, 538c2ecf20Sopenharmony_ci V4L2_MBUS_CSI2_CPHY, 548c2ecf20Sopenharmony_ci "MIPI CSI-2 C-PHY", 558c2ecf20Sopenharmony_ci }, { 568c2ecf20Sopenharmony_ci V4L2_FWNODE_BUS_TYPE_CSI1, 578c2ecf20Sopenharmony_ci V4L2_MBUS_CSI1, 588c2ecf20Sopenharmony_ci "MIPI CSI-1", 598c2ecf20Sopenharmony_ci }, { 608c2ecf20Sopenharmony_ci V4L2_FWNODE_BUS_TYPE_CCP2, 618c2ecf20Sopenharmony_ci V4L2_MBUS_CCP2, 628c2ecf20Sopenharmony_ci "compact camera port 2", 638c2ecf20Sopenharmony_ci }, { 648c2ecf20Sopenharmony_ci V4L2_FWNODE_BUS_TYPE_CSI2_DPHY, 658c2ecf20Sopenharmony_ci V4L2_MBUS_CSI2_DPHY, 668c2ecf20Sopenharmony_ci "MIPI CSI-2 D-PHY", 678c2ecf20Sopenharmony_ci }, { 688c2ecf20Sopenharmony_ci V4L2_FWNODE_BUS_TYPE_PARALLEL, 698c2ecf20Sopenharmony_ci V4L2_MBUS_PARALLEL, 708c2ecf20Sopenharmony_ci "parallel", 718c2ecf20Sopenharmony_ci }, { 728c2ecf20Sopenharmony_ci V4L2_FWNODE_BUS_TYPE_BT656, 738c2ecf20Sopenharmony_ci V4L2_MBUS_BT656, 748c2ecf20Sopenharmony_ci "Bt.656", 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci}; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic const struct v4l2_fwnode_bus_conv * 798c2ecf20Sopenharmony_ciget_v4l2_fwnode_bus_conv_by_fwnode_bus(enum v4l2_fwnode_bus_type type) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci unsigned int i; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(buses); i++) 848c2ecf20Sopenharmony_ci if (buses[i].fwnode_bus_type == type) 858c2ecf20Sopenharmony_ci return &buses[i]; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci return NULL; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic enum v4l2_mbus_type 918c2ecf20Sopenharmony_civ4l2_fwnode_bus_type_to_mbus(enum v4l2_fwnode_bus_type type) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci const struct v4l2_fwnode_bus_conv *conv = 948c2ecf20Sopenharmony_ci get_v4l2_fwnode_bus_conv_by_fwnode_bus(type); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return conv ? conv->mbus_type : V4L2_MBUS_INVALID; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic const char * 1008c2ecf20Sopenharmony_civ4l2_fwnode_bus_type_to_string(enum v4l2_fwnode_bus_type type) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci const struct v4l2_fwnode_bus_conv *conv = 1038c2ecf20Sopenharmony_ci get_v4l2_fwnode_bus_conv_by_fwnode_bus(type); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci return conv ? conv->name : "not found"; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic const struct v4l2_fwnode_bus_conv * 1098c2ecf20Sopenharmony_ciget_v4l2_fwnode_bus_conv_by_mbus(enum v4l2_mbus_type type) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci unsigned int i; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(buses); i++) 1148c2ecf20Sopenharmony_ci if (buses[i].mbus_type == type) 1158c2ecf20Sopenharmony_ci return &buses[i]; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return NULL; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic const char * 1218c2ecf20Sopenharmony_civ4l2_fwnode_mbus_type_to_string(enum v4l2_mbus_type type) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci const struct v4l2_fwnode_bus_conv *conv = 1248c2ecf20Sopenharmony_ci get_v4l2_fwnode_bus_conv_by_mbus(type); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return conv ? conv->name : "not found"; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int v4l2_fwnode_endpoint_parse_csi2_bus(struct fwnode_handle *fwnode, 1308c2ecf20Sopenharmony_ci struct v4l2_fwnode_endpoint *vep, 1318c2ecf20Sopenharmony_ci enum v4l2_mbus_type bus_type) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct v4l2_fwnode_bus_mipi_csi2 *bus = &vep->bus.mipi_csi2; 1348c2ecf20Sopenharmony_ci bool have_clk_lane = false, have_data_lanes = false, 1358c2ecf20Sopenharmony_ci have_lane_polarities = false; 1368c2ecf20Sopenharmony_ci unsigned int flags = 0, lanes_used = 0; 1378c2ecf20Sopenharmony_ci u32 array[1 + V4L2_FWNODE_CSI2_MAX_DATA_LANES]; 1388c2ecf20Sopenharmony_ci u32 clock_lane = 0; 1398c2ecf20Sopenharmony_ci unsigned int num_data_lanes = 0; 1408c2ecf20Sopenharmony_ci bool use_default_lane_mapping = false; 1418c2ecf20Sopenharmony_ci unsigned int i; 1428c2ecf20Sopenharmony_ci u32 v; 1438c2ecf20Sopenharmony_ci int rval; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (bus_type == V4L2_MBUS_CSI2_DPHY || 1468c2ecf20Sopenharmony_ci bus_type == V4L2_MBUS_CSI2_CPHY) { 1478c2ecf20Sopenharmony_ci use_default_lane_mapping = true; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci num_data_lanes = min_t(u32, bus->num_data_lanes, 1508c2ecf20Sopenharmony_ci V4L2_FWNODE_CSI2_MAX_DATA_LANES); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci clock_lane = bus->clock_lane; 1538c2ecf20Sopenharmony_ci if (clock_lane) 1548c2ecf20Sopenharmony_ci use_default_lane_mapping = false; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci for (i = 0; i < num_data_lanes; i++) { 1578c2ecf20Sopenharmony_ci array[i] = bus->data_lanes[i]; 1588c2ecf20Sopenharmony_ci if (array[i]) 1598c2ecf20Sopenharmony_ci use_default_lane_mapping = false; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (use_default_lane_mapping) 1638c2ecf20Sopenharmony_ci pr_debug("no lane mapping given, using defaults\n"); 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci rval = fwnode_property_count_u32(fwnode, "data-lanes"); 1678c2ecf20Sopenharmony_ci if (rval > 0) { 1688c2ecf20Sopenharmony_ci num_data_lanes = 1698c2ecf20Sopenharmony_ci min_t(int, V4L2_FWNODE_CSI2_MAX_DATA_LANES, rval); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci fwnode_property_read_u32_array(fwnode, "data-lanes", array, 1728c2ecf20Sopenharmony_ci num_data_lanes); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci have_data_lanes = true; 1758c2ecf20Sopenharmony_ci if (use_default_lane_mapping) { 1768c2ecf20Sopenharmony_ci pr_debug("data-lanes property exists; disabling default mapping\n"); 1778c2ecf20Sopenharmony_ci use_default_lane_mapping = false; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci for (i = 0; i < num_data_lanes; i++) { 1828c2ecf20Sopenharmony_ci if (lanes_used & BIT(array[i])) { 1838c2ecf20Sopenharmony_ci if (have_data_lanes || !use_default_lane_mapping) 1848c2ecf20Sopenharmony_ci pr_warn("duplicated lane %u in data-lanes, using defaults\n", 1858c2ecf20Sopenharmony_ci array[i]); 1868c2ecf20Sopenharmony_ci use_default_lane_mapping = true; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci lanes_used |= BIT(array[i]); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (have_data_lanes) 1918c2ecf20Sopenharmony_ci pr_debug("lane %u position %u\n", i, array[i]); 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci rval = fwnode_property_count_u32(fwnode, "lane-polarities"); 1958c2ecf20Sopenharmony_ci if (rval > 0) { 1968c2ecf20Sopenharmony_ci if (rval != 1 + num_data_lanes /* clock+data */) { 1978c2ecf20Sopenharmony_ci pr_warn("invalid number of lane-polarities entries (need %u, got %u)\n", 1988c2ecf20Sopenharmony_ci 1 + num_data_lanes, rval); 1998c2ecf20Sopenharmony_ci return -EINVAL; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci have_lane_polarities = true; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (!fwnode_property_read_u32(fwnode, "clock-lanes", &v)) { 2068c2ecf20Sopenharmony_ci clock_lane = v; 2078c2ecf20Sopenharmony_ci pr_debug("clock lane position %u\n", v); 2088c2ecf20Sopenharmony_ci have_clk_lane = true; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (have_clk_lane && lanes_used & BIT(clock_lane) && 2128c2ecf20Sopenharmony_ci !use_default_lane_mapping) { 2138c2ecf20Sopenharmony_ci pr_warn("duplicated lane %u in clock-lanes, using defaults\n", 2148c2ecf20Sopenharmony_ci v); 2158c2ecf20Sopenharmony_ci use_default_lane_mapping = true; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (fwnode_property_present(fwnode, "clock-noncontinuous")) { 2198c2ecf20Sopenharmony_ci flags |= V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK; 2208c2ecf20Sopenharmony_ci pr_debug("non-continuous clock\n"); 2218c2ecf20Sopenharmony_ci } else { 2228c2ecf20Sopenharmony_ci flags |= V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (bus_type == V4L2_MBUS_CSI2_DPHY || 2268c2ecf20Sopenharmony_ci bus_type == V4L2_MBUS_CSI2_CPHY || lanes_used || 2278c2ecf20Sopenharmony_ci have_clk_lane || (flags & ~V4L2_MBUS_CSI2_CONTINUOUS_CLOCK)) { 2288c2ecf20Sopenharmony_ci /* Only D-PHY has a clock lane. */ 2298c2ecf20Sopenharmony_ci unsigned int dfl_data_lane_index = 2308c2ecf20Sopenharmony_ci bus_type == V4L2_MBUS_CSI2_DPHY; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci bus->flags = flags; 2338c2ecf20Sopenharmony_ci if (bus_type == V4L2_MBUS_UNKNOWN) 2348c2ecf20Sopenharmony_ci vep->bus_type = V4L2_MBUS_CSI2_DPHY; 2358c2ecf20Sopenharmony_ci bus->num_data_lanes = num_data_lanes; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (use_default_lane_mapping) { 2388c2ecf20Sopenharmony_ci bus->clock_lane = 0; 2398c2ecf20Sopenharmony_ci for (i = 0; i < num_data_lanes; i++) 2408c2ecf20Sopenharmony_ci bus->data_lanes[i] = dfl_data_lane_index + i; 2418c2ecf20Sopenharmony_ci } else { 2428c2ecf20Sopenharmony_ci bus->clock_lane = clock_lane; 2438c2ecf20Sopenharmony_ci for (i = 0; i < num_data_lanes; i++) 2448c2ecf20Sopenharmony_ci bus->data_lanes[i] = array[i]; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (have_lane_polarities) { 2488c2ecf20Sopenharmony_ci fwnode_property_read_u32_array(fwnode, 2498c2ecf20Sopenharmony_ci "lane-polarities", array, 2508c2ecf20Sopenharmony_ci 1 + num_data_lanes); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci for (i = 0; i < 1 + num_data_lanes; i++) { 2538c2ecf20Sopenharmony_ci bus->lane_polarities[i] = array[i]; 2548c2ecf20Sopenharmony_ci pr_debug("lane %u polarity %sinverted", 2558c2ecf20Sopenharmony_ci i, array[i] ? "" : "not "); 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci } else { 2588c2ecf20Sopenharmony_ci pr_debug("no lane polarities defined, assuming not inverted\n"); 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci return 0; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci#define PARALLEL_MBUS_FLAGS (V4L2_MBUS_HSYNC_ACTIVE_HIGH | \ 2668c2ecf20Sopenharmony_ci V4L2_MBUS_HSYNC_ACTIVE_LOW | \ 2678c2ecf20Sopenharmony_ci V4L2_MBUS_VSYNC_ACTIVE_HIGH | \ 2688c2ecf20Sopenharmony_ci V4L2_MBUS_VSYNC_ACTIVE_LOW | \ 2698c2ecf20Sopenharmony_ci V4L2_MBUS_FIELD_EVEN_HIGH | \ 2708c2ecf20Sopenharmony_ci V4L2_MBUS_FIELD_EVEN_LOW) 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic void 2738c2ecf20Sopenharmony_civ4l2_fwnode_endpoint_parse_parallel_bus(struct fwnode_handle *fwnode, 2748c2ecf20Sopenharmony_ci struct v4l2_fwnode_endpoint *vep, 2758c2ecf20Sopenharmony_ci enum v4l2_mbus_type bus_type) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct v4l2_fwnode_bus_parallel *bus = &vep->bus.parallel; 2788c2ecf20Sopenharmony_ci unsigned int flags = 0; 2798c2ecf20Sopenharmony_ci u32 v; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (bus_type == V4L2_MBUS_PARALLEL || bus_type == V4L2_MBUS_BT656) 2828c2ecf20Sopenharmony_ci flags = bus->flags; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (!fwnode_property_read_u32(fwnode, "hsync-active", &v)) { 2858c2ecf20Sopenharmony_ci flags &= ~(V4L2_MBUS_HSYNC_ACTIVE_HIGH | 2868c2ecf20Sopenharmony_ci V4L2_MBUS_HSYNC_ACTIVE_LOW); 2878c2ecf20Sopenharmony_ci flags |= v ? V4L2_MBUS_HSYNC_ACTIVE_HIGH : 2888c2ecf20Sopenharmony_ci V4L2_MBUS_HSYNC_ACTIVE_LOW; 2898c2ecf20Sopenharmony_ci pr_debug("hsync-active %s\n", v ? "high" : "low"); 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (!fwnode_property_read_u32(fwnode, "vsync-active", &v)) { 2938c2ecf20Sopenharmony_ci flags &= ~(V4L2_MBUS_VSYNC_ACTIVE_HIGH | 2948c2ecf20Sopenharmony_ci V4L2_MBUS_VSYNC_ACTIVE_LOW); 2958c2ecf20Sopenharmony_ci flags |= v ? V4L2_MBUS_VSYNC_ACTIVE_HIGH : 2968c2ecf20Sopenharmony_ci V4L2_MBUS_VSYNC_ACTIVE_LOW; 2978c2ecf20Sopenharmony_ci pr_debug("vsync-active %s\n", v ? "high" : "low"); 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (!fwnode_property_read_u32(fwnode, "field-even-active", &v)) { 3018c2ecf20Sopenharmony_ci flags &= ~(V4L2_MBUS_FIELD_EVEN_HIGH | 3028c2ecf20Sopenharmony_ci V4L2_MBUS_FIELD_EVEN_LOW); 3038c2ecf20Sopenharmony_ci flags |= v ? V4L2_MBUS_FIELD_EVEN_HIGH : 3048c2ecf20Sopenharmony_ci V4L2_MBUS_FIELD_EVEN_LOW; 3058c2ecf20Sopenharmony_ci pr_debug("field-even-active %s\n", v ? "high" : "low"); 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (!fwnode_property_read_u32(fwnode, "pclk-sample", &v)) { 3098c2ecf20Sopenharmony_ci flags &= ~(V4L2_MBUS_PCLK_SAMPLE_RISING | 3108c2ecf20Sopenharmony_ci V4L2_MBUS_PCLK_SAMPLE_FALLING); 3118c2ecf20Sopenharmony_ci flags |= v ? V4L2_MBUS_PCLK_SAMPLE_RISING : 3128c2ecf20Sopenharmony_ci V4L2_MBUS_PCLK_SAMPLE_FALLING; 3138c2ecf20Sopenharmony_ci pr_debug("pclk-sample %s\n", v ? "high" : "low"); 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (!fwnode_property_read_u32(fwnode, "data-active", &v)) { 3178c2ecf20Sopenharmony_ci flags &= ~(V4L2_MBUS_DATA_ACTIVE_HIGH | 3188c2ecf20Sopenharmony_ci V4L2_MBUS_DATA_ACTIVE_LOW); 3198c2ecf20Sopenharmony_ci flags |= v ? V4L2_MBUS_DATA_ACTIVE_HIGH : 3208c2ecf20Sopenharmony_ci V4L2_MBUS_DATA_ACTIVE_LOW; 3218c2ecf20Sopenharmony_ci pr_debug("data-active %s\n", v ? "high" : "low"); 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (fwnode_property_present(fwnode, "slave-mode")) { 3258c2ecf20Sopenharmony_ci pr_debug("slave mode\n"); 3268c2ecf20Sopenharmony_ci flags &= ~V4L2_MBUS_MASTER; 3278c2ecf20Sopenharmony_ci flags |= V4L2_MBUS_SLAVE; 3288c2ecf20Sopenharmony_ci } else { 3298c2ecf20Sopenharmony_ci flags &= ~V4L2_MBUS_SLAVE; 3308c2ecf20Sopenharmony_ci flags |= V4L2_MBUS_MASTER; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (!fwnode_property_read_u32(fwnode, "bus-width", &v)) { 3348c2ecf20Sopenharmony_ci bus->bus_width = v; 3358c2ecf20Sopenharmony_ci pr_debug("bus-width %u\n", v); 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (!fwnode_property_read_u32(fwnode, "data-shift", &v)) { 3398c2ecf20Sopenharmony_ci bus->data_shift = v; 3408c2ecf20Sopenharmony_ci pr_debug("data-shift %u\n", v); 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (!fwnode_property_read_u32(fwnode, "sync-on-green-active", &v)) { 3448c2ecf20Sopenharmony_ci flags &= ~(V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH | 3458c2ecf20Sopenharmony_ci V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW); 3468c2ecf20Sopenharmony_ci flags |= v ? V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH : 3478c2ecf20Sopenharmony_ci V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW; 3488c2ecf20Sopenharmony_ci pr_debug("sync-on-green-active %s\n", v ? "high" : "low"); 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci if (!fwnode_property_read_u32(fwnode, "data-enable-active", &v)) { 3528c2ecf20Sopenharmony_ci flags &= ~(V4L2_MBUS_DATA_ENABLE_HIGH | 3538c2ecf20Sopenharmony_ci V4L2_MBUS_DATA_ENABLE_LOW); 3548c2ecf20Sopenharmony_ci flags |= v ? V4L2_MBUS_DATA_ENABLE_HIGH : 3558c2ecf20Sopenharmony_ci V4L2_MBUS_DATA_ENABLE_LOW; 3568c2ecf20Sopenharmony_ci pr_debug("data-enable-active %s\n", v ? "high" : "low"); 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci switch (bus_type) { 3608c2ecf20Sopenharmony_ci default: 3618c2ecf20Sopenharmony_ci bus->flags = flags; 3628c2ecf20Sopenharmony_ci if (flags & PARALLEL_MBUS_FLAGS) 3638c2ecf20Sopenharmony_ci vep->bus_type = V4L2_MBUS_PARALLEL; 3648c2ecf20Sopenharmony_ci else 3658c2ecf20Sopenharmony_ci vep->bus_type = V4L2_MBUS_BT656; 3668c2ecf20Sopenharmony_ci break; 3678c2ecf20Sopenharmony_ci case V4L2_MBUS_PARALLEL: 3688c2ecf20Sopenharmony_ci vep->bus_type = V4L2_MBUS_PARALLEL; 3698c2ecf20Sopenharmony_ci bus->flags = flags; 3708c2ecf20Sopenharmony_ci break; 3718c2ecf20Sopenharmony_ci case V4L2_MBUS_BT656: 3728c2ecf20Sopenharmony_ci vep->bus_type = V4L2_MBUS_BT656; 3738c2ecf20Sopenharmony_ci bus->flags = flags & ~PARALLEL_MBUS_FLAGS; 3748c2ecf20Sopenharmony_ci break; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic void 3798c2ecf20Sopenharmony_civ4l2_fwnode_endpoint_parse_csi1_bus(struct fwnode_handle *fwnode, 3808c2ecf20Sopenharmony_ci struct v4l2_fwnode_endpoint *vep, 3818c2ecf20Sopenharmony_ci enum v4l2_mbus_type bus_type) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci struct v4l2_fwnode_bus_mipi_csi1 *bus = &vep->bus.mipi_csi1; 3848c2ecf20Sopenharmony_ci u32 v; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (!fwnode_property_read_u32(fwnode, "clock-inv", &v)) { 3878c2ecf20Sopenharmony_ci bus->clock_inv = v; 3888c2ecf20Sopenharmony_ci pr_debug("clock-inv %u\n", v); 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (!fwnode_property_read_u32(fwnode, "strobe", &v)) { 3928c2ecf20Sopenharmony_ci bus->strobe = v; 3938c2ecf20Sopenharmony_ci pr_debug("strobe %u\n", v); 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci if (!fwnode_property_read_u32(fwnode, "data-lanes", &v)) { 3978c2ecf20Sopenharmony_ci bus->data_lane = v; 3988c2ecf20Sopenharmony_ci pr_debug("data-lanes %u\n", v); 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci if (!fwnode_property_read_u32(fwnode, "clock-lanes", &v)) { 4028c2ecf20Sopenharmony_ci bus->clock_lane = v; 4038c2ecf20Sopenharmony_ci pr_debug("clock-lanes %u\n", v); 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (bus_type == V4L2_MBUS_CCP2) 4078c2ecf20Sopenharmony_ci vep->bus_type = V4L2_MBUS_CCP2; 4088c2ecf20Sopenharmony_ci else 4098c2ecf20Sopenharmony_ci vep->bus_type = V4L2_MBUS_CSI1; 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic int __v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode, 4138c2ecf20Sopenharmony_ci struct v4l2_fwnode_endpoint *vep) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci u32 bus_type = V4L2_FWNODE_BUS_TYPE_GUESS; 4168c2ecf20Sopenharmony_ci enum v4l2_mbus_type mbus_type; 4178c2ecf20Sopenharmony_ci int rval; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (vep->bus_type == V4L2_MBUS_UNKNOWN) { 4208c2ecf20Sopenharmony_ci /* Zero fields from bus union to until the end */ 4218c2ecf20Sopenharmony_ci memset(&vep->bus, 0, 4228c2ecf20Sopenharmony_ci sizeof(*vep) - offsetof(typeof(*vep), bus)); 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci pr_debug("===== begin parsing endpoint %pfw\n", fwnode); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* 4288c2ecf20Sopenharmony_ci * Zero the fwnode graph endpoint memory in case we don't end up parsing 4298c2ecf20Sopenharmony_ci * the endpoint. 4308c2ecf20Sopenharmony_ci */ 4318c2ecf20Sopenharmony_ci memset(&vep->base, 0, sizeof(vep->base)); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci fwnode_property_read_u32(fwnode, "bus-type", &bus_type); 4348c2ecf20Sopenharmony_ci pr_debug("fwnode video bus type %s (%u), mbus type %s (%u)\n", 4358c2ecf20Sopenharmony_ci v4l2_fwnode_bus_type_to_string(bus_type), bus_type, 4368c2ecf20Sopenharmony_ci v4l2_fwnode_mbus_type_to_string(vep->bus_type), 4378c2ecf20Sopenharmony_ci vep->bus_type); 4388c2ecf20Sopenharmony_ci mbus_type = v4l2_fwnode_bus_type_to_mbus(bus_type); 4398c2ecf20Sopenharmony_ci if (mbus_type == V4L2_MBUS_INVALID) { 4408c2ecf20Sopenharmony_ci pr_debug("unsupported bus type %u\n", bus_type); 4418c2ecf20Sopenharmony_ci return -EINVAL; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (vep->bus_type != V4L2_MBUS_UNKNOWN) { 4458c2ecf20Sopenharmony_ci if (mbus_type != V4L2_MBUS_UNKNOWN && 4468c2ecf20Sopenharmony_ci vep->bus_type != mbus_type) { 4478c2ecf20Sopenharmony_ci pr_debug("expecting bus type %s\n", 4488c2ecf20Sopenharmony_ci v4l2_fwnode_mbus_type_to_string(vep->bus_type)); 4498c2ecf20Sopenharmony_ci return -ENXIO; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci } else { 4528c2ecf20Sopenharmony_ci vep->bus_type = mbus_type; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci switch (vep->bus_type) { 4568c2ecf20Sopenharmony_ci case V4L2_MBUS_UNKNOWN: 4578c2ecf20Sopenharmony_ci rval = v4l2_fwnode_endpoint_parse_csi2_bus(fwnode, vep, 4588c2ecf20Sopenharmony_ci V4L2_MBUS_UNKNOWN); 4598c2ecf20Sopenharmony_ci if (rval) 4608c2ecf20Sopenharmony_ci return rval; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci if (vep->bus_type == V4L2_MBUS_UNKNOWN) 4638c2ecf20Sopenharmony_ci v4l2_fwnode_endpoint_parse_parallel_bus(fwnode, vep, 4648c2ecf20Sopenharmony_ci V4L2_MBUS_UNKNOWN); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci pr_debug("assuming media bus type %s (%u)\n", 4678c2ecf20Sopenharmony_ci v4l2_fwnode_mbus_type_to_string(vep->bus_type), 4688c2ecf20Sopenharmony_ci vep->bus_type); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci break; 4718c2ecf20Sopenharmony_ci case V4L2_MBUS_CCP2: 4728c2ecf20Sopenharmony_ci case V4L2_MBUS_CSI1: 4738c2ecf20Sopenharmony_ci v4l2_fwnode_endpoint_parse_csi1_bus(fwnode, vep, vep->bus_type); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci break; 4768c2ecf20Sopenharmony_ci case V4L2_MBUS_CSI2_DPHY: 4778c2ecf20Sopenharmony_ci case V4L2_MBUS_CSI2_CPHY: 4788c2ecf20Sopenharmony_ci rval = v4l2_fwnode_endpoint_parse_csi2_bus(fwnode, vep, 4798c2ecf20Sopenharmony_ci vep->bus_type); 4808c2ecf20Sopenharmony_ci if (rval) 4818c2ecf20Sopenharmony_ci return rval; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci break; 4848c2ecf20Sopenharmony_ci case V4L2_MBUS_PARALLEL: 4858c2ecf20Sopenharmony_ci case V4L2_MBUS_BT656: 4868c2ecf20Sopenharmony_ci v4l2_fwnode_endpoint_parse_parallel_bus(fwnode, vep, 4878c2ecf20Sopenharmony_ci vep->bus_type); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci break; 4908c2ecf20Sopenharmony_ci default: 4918c2ecf20Sopenharmony_ci pr_warn("unsupported bus type %u\n", mbus_type); 4928c2ecf20Sopenharmony_ci return -EINVAL; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci fwnode_graph_parse_endpoint(fwnode, &vep->base); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci return 0; 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ciint v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode, 5018c2ecf20Sopenharmony_ci struct v4l2_fwnode_endpoint *vep) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci int ret; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci ret = __v4l2_fwnode_endpoint_parse(fwnode, vep); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci pr_debug("===== end parsing endpoint %pfw\n", fwnode); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci return ret; 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_parse); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_civoid v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(vep)) 5168c2ecf20Sopenharmony_ci return; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci kfree(vep->link_frequencies); 5198c2ecf20Sopenharmony_ci vep->link_frequencies = NULL; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_free); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ciint v4l2_fwnode_endpoint_alloc_parse(struct fwnode_handle *fwnode, 5248c2ecf20Sopenharmony_ci struct v4l2_fwnode_endpoint *vep) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci int rval; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci rval = __v4l2_fwnode_endpoint_parse(fwnode, vep); 5298c2ecf20Sopenharmony_ci if (rval < 0) 5308c2ecf20Sopenharmony_ci return rval; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci rval = fwnode_property_count_u64(fwnode, "link-frequencies"); 5338c2ecf20Sopenharmony_ci if (rval > 0) { 5348c2ecf20Sopenharmony_ci unsigned int i; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci vep->link_frequencies = 5378c2ecf20Sopenharmony_ci kmalloc_array(rval, sizeof(*vep->link_frequencies), 5388c2ecf20Sopenharmony_ci GFP_KERNEL); 5398c2ecf20Sopenharmony_ci if (!vep->link_frequencies) 5408c2ecf20Sopenharmony_ci return -ENOMEM; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci vep->nr_of_link_frequencies = rval; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci rval = fwnode_property_read_u64_array(fwnode, 5458c2ecf20Sopenharmony_ci "link-frequencies", 5468c2ecf20Sopenharmony_ci vep->link_frequencies, 5478c2ecf20Sopenharmony_ci vep->nr_of_link_frequencies); 5488c2ecf20Sopenharmony_ci if (rval < 0) { 5498c2ecf20Sopenharmony_ci v4l2_fwnode_endpoint_free(vep); 5508c2ecf20Sopenharmony_ci return rval; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci for (i = 0; i < vep->nr_of_link_frequencies; i++) 5548c2ecf20Sopenharmony_ci pr_debug("link-frequencies %u value %llu\n", i, 5558c2ecf20Sopenharmony_ci vep->link_frequencies[i]); 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci pr_debug("===== end parsing endpoint %pfw\n", fwnode); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci return 0; 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_alloc_parse); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ciint v4l2_fwnode_parse_link(struct fwnode_handle *fwnode, 5658c2ecf20Sopenharmony_ci struct v4l2_fwnode_link *link) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci struct fwnode_endpoint fwep; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci memset(link, 0, sizeof(*link)); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci fwnode_graph_parse_endpoint(fwnode, &fwep); 5728c2ecf20Sopenharmony_ci link->local_id = fwep.id; 5738c2ecf20Sopenharmony_ci link->local_port = fwep.port; 5748c2ecf20Sopenharmony_ci link->local_node = fwnode_graph_get_port_parent(fwnode); 5758c2ecf20Sopenharmony_ci if (!link->local_node) 5768c2ecf20Sopenharmony_ci return -ENOLINK; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci fwnode = fwnode_graph_get_remote_endpoint(fwnode); 5798c2ecf20Sopenharmony_ci if (!fwnode) 5808c2ecf20Sopenharmony_ci goto err_put_local_node; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci fwnode_graph_parse_endpoint(fwnode, &fwep); 5838c2ecf20Sopenharmony_ci link->remote_id = fwep.id; 5848c2ecf20Sopenharmony_ci link->remote_port = fwep.port; 5858c2ecf20Sopenharmony_ci link->remote_node = fwnode_graph_get_port_parent(fwnode); 5868c2ecf20Sopenharmony_ci if (!link->remote_node) 5878c2ecf20Sopenharmony_ci goto err_put_remote_endpoint; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci return 0; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_cierr_put_remote_endpoint: 5928c2ecf20Sopenharmony_ci fwnode_handle_put(fwnode); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cierr_put_local_node: 5958c2ecf20Sopenharmony_ci fwnode_handle_put(link->local_node); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci return -ENOLINK; 5988c2ecf20Sopenharmony_ci} 5998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_fwnode_parse_link); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_civoid v4l2_fwnode_put_link(struct v4l2_fwnode_link *link) 6028c2ecf20Sopenharmony_ci{ 6038c2ecf20Sopenharmony_ci fwnode_handle_put(link->local_node); 6048c2ecf20Sopenharmony_ci fwnode_handle_put(link->remote_node); 6058c2ecf20Sopenharmony_ci} 6068c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_fwnode_put_link); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic const struct v4l2_fwnode_connector_conv { 6098c2ecf20Sopenharmony_ci enum v4l2_connector_type type; 6108c2ecf20Sopenharmony_ci const char *compatible; 6118c2ecf20Sopenharmony_ci} connectors[] = { 6128c2ecf20Sopenharmony_ci { 6138c2ecf20Sopenharmony_ci .type = V4L2_CONN_COMPOSITE, 6148c2ecf20Sopenharmony_ci .compatible = "composite-video-connector", 6158c2ecf20Sopenharmony_ci }, { 6168c2ecf20Sopenharmony_ci .type = V4L2_CONN_SVIDEO, 6178c2ecf20Sopenharmony_ci .compatible = "svideo-connector", 6188c2ecf20Sopenharmony_ci }, 6198c2ecf20Sopenharmony_ci}; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_cistatic enum v4l2_connector_type 6228c2ecf20Sopenharmony_civ4l2_fwnode_string_to_connector_type(const char *con_str) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci unsigned int i; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(connectors); i++) 6278c2ecf20Sopenharmony_ci if (!strcmp(con_str, connectors[i].compatible)) 6288c2ecf20Sopenharmony_ci return connectors[i].type; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci return V4L2_CONN_UNKNOWN; 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cistatic void 6348c2ecf20Sopenharmony_civ4l2_fwnode_connector_parse_analog(struct fwnode_handle *fwnode, 6358c2ecf20Sopenharmony_ci struct v4l2_fwnode_connector *vc) 6368c2ecf20Sopenharmony_ci{ 6378c2ecf20Sopenharmony_ci u32 stds; 6388c2ecf20Sopenharmony_ci int ret; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci ret = fwnode_property_read_u32(fwnode, "sdtv-standards", &stds); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci /* The property is optional. */ 6438c2ecf20Sopenharmony_ci vc->connector.analog.sdtv_stds = ret ? V4L2_STD_ALL : stds; 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_civoid v4l2_fwnode_connector_free(struct v4l2_fwnode_connector *connector) 6478c2ecf20Sopenharmony_ci{ 6488c2ecf20Sopenharmony_ci struct v4l2_connector_link *link, *tmp; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(connector) || connector->type == V4L2_CONN_UNKNOWN) 6518c2ecf20Sopenharmony_ci return; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci list_for_each_entry_safe(link, tmp, &connector->links, head) { 6548c2ecf20Sopenharmony_ci v4l2_fwnode_put_link(&link->fwnode_link); 6558c2ecf20Sopenharmony_ci list_del(&link->head); 6568c2ecf20Sopenharmony_ci kfree(link); 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci kfree(connector->label); 6608c2ecf20Sopenharmony_ci connector->label = NULL; 6618c2ecf20Sopenharmony_ci connector->type = V4L2_CONN_UNKNOWN; 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_fwnode_connector_free); 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_cistatic enum v4l2_connector_type 6668c2ecf20Sopenharmony_civ4l2_fwnode_get_connector_type(struct fwnode_handle *fwnode) 6678c2ecf20Sopenharmony_ci{ 6688c2ecf20Sopenharmony_ci const char *type_name; 6698c2ecf20Sopenharmony_ci int err; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci if (!fwnode) 6728c2ecf20Sopenharmony_ci return V4L2_CONN_UNKNOWN; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci /* The connector-type is stored within the compatible string. */ 6758c2ecf20Sopenharmony_ci err = fwnode_property_read_string(fwnode, "compatible", &type_name); 6768c2ecf20Sopenharmony_ci if (err) 6778c2ecf20Sopenharmony_ci return V4L2_CONN_UNKNOWN; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci return v4l2_fwnode_string_to_connector_type(type_name); 6808c2ecf20Sopenharmony_ci} 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ciint v4l2_fwnode_connector_parse(struct fwnode_handle *fwnode, 6838c2ecf20Sopenharmony_ci struct v4l2_fwnode_connector *connector) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci struct fwnode_handle *connector_node; 6868c2ecf20Sopenharmony_ci enum v4l2_connector_type connector_type; 6878c2ecf20Sopenharmony_ci const char *label; 6888c2ecf20Sopenharmony_ci int err; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci if (!fwnode) 6918c2ecf20Sopenharmony_ci return -EINVAL; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci memset(connector, 0, sizeof(*connector)); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&connector->links); 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci connector_node = fwnode_graph_get_port_parent(fwnode); 6988c2ecf20Sopenharmony_ci connector_type = v4l2_fwnode_get_connector_type(connector_node); 6998c2ecf20Sopenharmony_ci if (connector_type == V4L2_CONN_UNKNOWN) { 7008c2ecf20Sopenharmony_ci fwnode_handle_put(connector_node); 7018c2ecf20Sopenharmony_ci connector_node = fwnode_graph_get_remote_port_parent(fwnode); 7028c2ecf20Sopenharmony_ci connector_type = v4l2_fwnode_get_connector_type(connector_node); 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci if (connector_type == V4L2_CONN_UNKNOWN) { 7068c2ecf20Sopenharmony_ci pr_err("Unknown connector type\n"); 7078c2ecf20Sopenharmony_ci err = -ENOTCONN; 7088c2ecf20Sopenharmony_ci goto out; 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci connector->type = connector_type; 7128c2ecf20Sopenharmony_ci connector->name = fwnode_get_name(connector_node); 7138c2ecf20Sopenharmony_ci err = fwnode_property_read_string(connector_node, "label", &label); 7148c2ecf20Sopenharmony_ci connector->label = err ? NULL : kstrdup_const(label, GFP_KERNEL); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci /* Parse the connector specific properties. */ 7178c2ecf20Sopenharmony_ci switch (connector->type) { 7188c2ecf20Sopenharmony_ci case V4L2_CONN_COMPOSITE: 7198c2ecf20Sopenharmony_ci case V4L2_CONN_SVIDEO: 7208c2ecf20Sopenharmony_ci v4l2_fwnode_connector_parse_analog(connector_node, connector); 7218c2ecf20Sopenharmony_ci break; 7228c2ecf20Sopenharmony_ci /* Avoid compiler warnings */ 7238c2ecf20Sopenharmony_ci case V4L2_CONN_UNKNOWN: 7248c2ecf20Sopenharmony_ci break; 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ciout: 7288c2ecf20Sopenharmony_ci fwnode_handle_put(connector_node); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci return err; 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_fwnode_connector_parse); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ciint v4l2_fwnode_connector_add_link(struct fwnode_handle *fwnode, 7358c2ecf20Sopenharmony_ci struct v4l2_fwnode_connector *connector) 7368c2ecf20Sopenharmony_ci{ 7378c2ecf20Sopenharmony_ci struct fwnode_handle *connector_ep; 7388c2ecf20Sopenharmony_ci struct v4l2_connector_link *link; 7398c2ecf20Sopenharmony_ci int err; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci if (!fwnode || !connector || connector->type == V4L2_CONN_UNKNOWN) 7428c2ecf20Sopenharmony_ci return -EINVAL; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci connector_ep = fwnode_graph_get_remote_endpoint(fwnode); 7458c2ecf20Sopenharmony_ci if (!connector_ep) 7468c2ecf20Sopenharmony_ci return -ENOTCONN; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci link = kzalloc(sizeof(*link), GFP_KERNEL); 7498c2ecf20Sopenharmony_ci if (!link) { 7508c2ecf20Sopenharmony_ci err = -ENOMEM; 7518c2ecf20Sopenharmony_ci goto err; 7528c2ecf20Sopenharmony_ci } 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci err = v4l2_fwnode_parse_link(connector_ep, &link->fwnode_link); 7558c2ecf20Sopenharmony_ci if (err) 7568c2ecf20Sopenharmony_ci goto err; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci fwnode_handle_put(connector_ep); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci list_add(&link->head, &connector->links); 7618c2ecf20Sopenharmony_ci connector->nr_of_links++; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci return 0; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_cierr: 7668c2ecf20Sopenharmony_ci kfree(link); 7678c2ecf20Sopenharmony_ci fwnode_handle_put(connector_ep); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci return err; 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_fwnode_connector_add_link); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ciint v4l2_fwnode_device_parse(struct device *dev, 7748c2ecf20Sopenharmony_ci struct v4l2_fwnode_device_properties *props) 7758c2ecf20Sopenharmony_ci{ 7768c2ecf20Sopenharmony_ci struct fwnode_handle *fwnode = dev_fwnode(dev); 7778c2ecf20Sopenharmony_ci u32 val; 7788c2ecf20Sopenharmony_ci int ret; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci memset(props, 0, sizeof(*props)); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci props->orientation = V4L2_FWNODE_PROPERTY_UNSET; 7838c2ecf20Sopenharmony_ci ret = fwnode_property_read_u32(fwnode, "orientation", &val); 7848c2ecf20Sopenharmony_ci if (!ret) { 7858c2ecf20Sopenharmony_ci switch (val) { 7868c2ecf20Sopenharmony_ci case V4L2_FWNODE_ORIENTATION_FRONT: 7878c2ecf20Sopenharmony_ci case V4L2_FWNODE_ORIENTATION_BACK: 7888c2ecf20Sopenharmony_ci case V4L2_FWNODE_ORIENTATION_EXTERNAL: 7898c2ecf20Sopenharmony_ci break; 7908c2ecf20Sopenharmony_ci default: 7918c2ecf20Sopenharmony_ci dev_warn(dev, "Unsupported device orientation: %u\n", val); 7928c2ecf20Sopenharmony_ci return -EINVAL; 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci props->orientation = val; 7968c2ecf20Sopenharmony_ci dev_dbg(dev, "device orientation: %u\n", val); 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci props->rotation = V4L2_FWNODE_PROPERTY_UNSET; 8008c2ecf20Sopenharmony_ci ret = fwnode_property_read_u32(fwnode, "rotation", &val); 8018c2ecf20Sopenharmony_ci if (!ret) { 8028c2ecf20Sopenharmony_ci if (val >= 360) { 8038c2ecf20Sopenharmony_ci dev_warn(dev, "Unsupported device rotation: %u\n", val); 8048c2ecf20Sopenharmony_ci return -EINVAL; 8058c2ecf20Sopenharmony_ci } 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci props->rotation = val; 8088c2ecf20Sopenharmony_ci dev_dbg(dev, "device rotation: %u\n", val); 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci return 0; 8128c2ecf20Sopenharmony_ci} 8138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_fwnode_device_parse); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_cistatic int 8168c2ecf20Sopenharmony_civ4l2_async_notifier_fwnode_parse_endpoint(struct device *dev, 8178c2ecf20Sopenharmony_ci struct v4l2_async_notifier *notifier, 8188c2ecf20Sopenharmony_ci struct fwnode_handle *endpoint, 8198c2ecf20Sopenharmony_ci unsigned int asd_struct_size, 8208c2ecf20Sopenharmony_ci parse_endpoint_func parse_endpoint) 8218c2ecf20Sopenharmony_ci{ 8228c2ecf20Sopenharmony_ci struct v4l2_fwnode_endpoint vep = { .bus_type = 0 }; 8238c2ecf20Sopenharmony_ci struct v4l2_async_subdev *asd; 8248c2ecf20Sopenharmony_ci int ret; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci asd = kzalloc(asd_struct_size, GFP_KERNEL); 8278c2ecf20Sopenharmony_ci if (!asd) 8288c2ecf20Sopenharmony_ci return -ENOMEM; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci asd->match_type = V4L2_ASYNC_MATCH_FWNODE; 8318c2ecf20Sopenharmony_ci asd->match.fwnode = 8328c2ecf20Sopenharmony_ci fwnode_graph_get_remote_port_parent(endpoint); 8338c2ecf20Sopenharmony_ci if (!asd->match.fwnode) { 8348c2ecf20Sopenharmony_ci dev_dbg(dev, "no remote endpoint found\n"); 8358c2ecf20Sopenharmony_ci ret = -ENOTCONN; 8368c2ecf20Sopenharmony_ci goto out_err; 8378c2ecf20Sopenharmony_ci } 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &vep); 8408c2ecf20Sopenharmony_ci if (ret) { 8418c2ecf20Sopenharmony_ci dev_warn(dev, "unable to parse V4L2 fwnode endpoint (%d)\n", 8428c2ecf20Sopenharmony_ci ret); 8438c2ecf20Sopenharmony_ci goto out_err; 8448c2ecf20Sopenharmony_ci } 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci ret = parse_endpoint ? parse_endpoint(dev, &vep, asd) : 0; 8478c2ecf20Sopenharmony_ci if (ret == -ENOTCONN) 8488c2ecf20Sopenharmony_ci dev_dbg(dev, "ignoring port@%u/endpoint@%u\n", vep.base.port, 8498c2ecf20Sopenharmony_ci vep.base.id); 8508c2ecf20Sopenharmony_ci else if (ret < 0) 8518c2ecf20Sopenharmony_ci dev_warn(dev, 8528c2ecf20Sopenharmony_ci "driver could not parse port@%u/endpoint@%u (%d)\n", 8538c2ecf20Sopenharmony_ci vep.base.port, vep.base.id, ret); 8548c2ecf20Sopenharmony_ci v4l2_fwnode_endpoint_free(&vep); 8558c2ecf20Sopenharmony_ci if (ret < 0) 8568c2ecf20Sopenharmony_ci goto out_err; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci ret = v4l2_async_notifier_add_subdev(notifier, asd); 8598c2ecf20Sopenharmony_ci if (ret < 0) { 8608c2ecf20Sopenharmony_ci /* not an error if asd already exists */ 8618c2ecf20Sopenharmony_ci if (ret == -EEXIST) 8628c2ecf20Sopenharmony_ci ret = 0; 8638c2ecf20Sopenharmony_ci goto out_err; 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci return 0; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ciout_err: 8698c2ecf20Sopenharmony_ci fwnode_handle_put(asd->match.fwnode); 8708c2ecf20Sopenharmony_ci kfree(asd); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci return ret == -ENOTCONN ? 0 : ret; 8738c2ecf20Sopenharmony_ci} 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_cistatic int 8768c2ecf20Sopenharmony_ci__v4l2_async_notifier_parse_fwnode_ep(struct device *dev, 8778c2ecf20Sopenharmony_ci struct v4l2_async_notifier *notifier, 8788c2ecf20Sopenharmony_ci size_t asd_struct_size, 8798c2ecf20Sopenharmony_ci unsigned int port, 8808c2ecf20Sopenharmony_ci bool has_port, 8818c2ecf20Sopenharmony_ci parse_endpoint_func parse_endpoint) 8828c2ecf20Sopenharmony_ci{ 8838c2ecf20Sopenharmony_ci struct fwnode_handle *fwnode; 8848c2ecf20Sopenharmony_ci int ret = 0; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci if (WARN_ON(asd_struct_size < sizeof(struct v4l2_async_subdev))) 8878c2ecf20Sopenharmony_ci return -EINVAL; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci fwnode_graph_for_each_endpoint(dev_fwnode(dev), fwnode) { 8908c2ecf20Sopenharmony_ci struct fwnode_handle *dev_fwnode; 8918c2ecf20Sopenharmony_ci bool is_available; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci dev_fwnode = fwnode_graph_get_port_parent(fwnode); 8948c2ecf20Sopenharmony_ci is_available = fwnode_device_is_available(dev_fwnode); 8958c2ecf20Sopenharmony_ci fwnode_handle_put(dev_fwnode); 8968c2ecf20Sopenharmony_ci if (!is_available) 8978c2ecf20Sopenharmony_ci continue; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci if (has_port) { 9008c2ecf20Sopenharmony_ci struct fwnode_endpoint ep; 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci ret = fwnode_graph_parse_endpoint(fwnode, &ep); 9038c2ecf20Sopenharmony_ci if (ret) 9048c2ecf20Sopenharmony_ci break; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci if (ep.port != port) 9078c2ecf20Sopenharmony_ci continue; 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci ret = v4l2_async_notifier_fwnode_parse_endpoint(dev, 9118c2ecf20Sopenharmony_ci notifier, 9128c2ecf20Sopenharmony_ci fwnode, 9138c2ecf20Sopenharmony_ci asd_struct_size, 9148c2ecf20Sopenharmony_ci parse_endpoint); 9158c2ecf20Sopenharmony_ci if (ret < 0) 9168c2ecf20Sopenharmony_ci break; 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci fwnode_handle_put(fwnode); 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci return ret; 9228c2ecf20Sopenharmony_ci} 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ciint 9258c2ecf20Sopenharmony_civ4l2_async_notifier_parse_fwnode_endpoints(struct device *dev, 9268c2ecf20Sopenharmony_ci struct v4l2_async_notifier *notifier, 9278c2ecf20Sopenharmony_ci size_t asd_struct_size, 9288c2ecf20Sopenharmony_ci parse_endpoint_func parse_endpoint) 9298c2ecf20Sopenharmony_ci{ 9308c2ecf20Sopenharmony_ci return __v4l2_async_notifier_parse_fwnode_ep(dev, notifier, 9318c2ecf20Sopenharmony_ci asd_struct_size, 0, 9328c2ecf20Sopenharmony_ci false, parse_endpoint); 9338c2ecf20Sopenharmony_ci} 9348c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_endpoints); 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ciint 9378c2ecf20Sopenharmony_civ4l2_async_notifier_parse_fwnode_endpoints_by_port(struct device *dev, 9388c2ecf20Sopenharmony_ci struct v4l2_async_notifier *notifier, 9398c2ecf20Sopenharmony_ci size_t asd_struct_size, 9408c2ecf20Sopenharmony_ci unsigned int port, 9418c2ecf20Sopenharmony_ci parse_endpoint_func parse_endpoint) 9428c2ecf20Sopenharmony_ci{ 9438c2ecf20Sopenharmony_ci return __v4l2_async_notifier_parse_fwnode_ep(dev, notifier, 9448c2ecf20Sopenharmony_ci asd_struct_size, 9458c2ecf20Sopenharmony_ci port, true, 9468c2ecf20Sopenharmony_ci parse_endpoint); 9478c2ecf20Sopenharmony_ci} 9488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_endpoints_by_port); 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci/* 9518c2ecf20Sopenharmony_ci * v4l2_fwnode_reference_parse - parse references for async sub-devices 9528c2ecf20Sopenharmony_ci * @dev: the device node the properties of which are parsed for references 9538c2ecf20Sopenharmony_ci * @notifier: the async notifier where the async subdevs will be added 9548c2ecf20Sopenharmony_ci * @prop: the name of the property 9558c2ecf20Sopenharmony_ci * 9568c2ecf20Sopenharmony_ci * Return: 0 on success 9578c2ecf20Sopenharmony_ci * -ENOENT if no entries were found 9588c2ecf20Sopenharmony_ci * -ENOMEM if memory allocation failed 9598c2ecf20Sopenharmony_ci * -EINVAL if property parsing failed 9608c2ecf20Sopenharmony_ci */ 9618c2ecf20Sopenharmony_cistatic int v4l2_fwnode_reference_parse(struct device *dev, 9628c2ecf20Sopenharmony_ci struct v4l2_async_notifier *notifier, 9638c2ecf20Sopenharmony_ci const char *prop) 9648c2ecf20Sopenharmony_ci{ 9658c2ecf20Sopenharmony_ci struct fwnode_reference_args args; 9668c2ecf20Sopenharmony_ci unsigned int index; 9678c2ecf20Sopenharmony_ci int ret; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci for (index = 0; 9708c2ecf20Sopenharmony_ci !(ret = fwnode_property_get_reference_args(dev_fwnode(dev), 9718c2ecf20Sopenharmony_ci prop, NULL, 0, 9728c2ecf20Sopenharmony_ci index, &args)); 9738c2ecf20Sopenharmony_ci index++) 9748c2ecf20Sopenharmony_ci fwnode_handle_put(args.fwnode); 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci if (!index) 9778c2ecf20Sopenharmony_ci return -ENOENT; 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci /* 9808c2ecf20Sopenharmony_ci * Note that right now both -ENODATA and -ENOENT may signal 9818c2ecf20Sopenharmony_ci * out-of-bounds access. Return the error in cases other than that. 9828c2ecf20Sopenharmony_ci */ 9838c2ecf20Sopenharmony_ci if (ret != -ENOENT && ret != -ENODATA) 9848c2ecf20Sopenharmony_ci return ret; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci for (index = 0; 9878c2ecf20Sopenharmony_ci !fwnode_property_get_reference_args(dev_fwnode(dev), prop, NULL, 9888c2ecf20Sopenharmony_ci 0, index, &args); 9898c2ecf20Sopenharmony_ci index++) { 9908c2ecf20Sopenharmony_ci struct v4l2_async_subdev *asd; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci asd = v4l2_async_notifier_add_fwnode_subdev(notifier, 9938c2ecf20Sopenharmony_ci args.fwnode, 9948c2ecf20Sopenharmony_ci sizeof(*asd)); 9958c2ecf20Sopenharmony_ci fwnode_handle_put(args.fwnode); 9968c2ecf20Sopenharmony_ci if (IS_ERR(asd)) { 9978c2ecf20Sopenharmony_ci /* not an error if asd already exists */ 9988c2ecf20Sopenharmony_ci if (PTR_ERR(asd) == -EEXIST) 9998c2ecf20Sopenharmony_ci continue; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci return PTR_ERR(asd); 10028c2ecf20Sopenharmony_ci } 10038c2ecf20Sopenharmony_ci } 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci return 0; 10068c2ecf20Sopenharmony_ci} 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci/* 10098c2ecf20Sopenharmony_ci * v4l2_fwnode_reference_get_int_prop - parse a reference with integer 10108c2ecf20Sopenharmony_ci * arguments 10118c2ecf20Sopenharmony_ci * @fwnode: fwnode to read @prop from 10128c2ecf20Sopenharmony_ci * @notifier: notifier for @dev 10138c2ecf20Sopenharmony_ci * @prop: the name of the property 10148c2ecf20Sopenharmony_ci * @index: the index of the reference to get 10158c2ecf20Sopenharmony_ci * @props: the array of integer property names 10168c2ecf20Sopenharmony_ci * @nprops: the number of integer property names in @nprops 10178c2ecf20Sopenharmony_ci * 10188c2ecf20Sopenharmony_ci * First find an fwnode referred to by the reference at @index in @prop. 10198c2ecf20Sopenharmony_ci * 10208c2ecf20Sopenharmony_ci * Then under that fwnode, @nprops times, for each property in @props, 10218c2ecf20Sopenharmony_ci * iteratively follow child nodes starting from fwnode such that they have the 10228c2ecf20Sopenharmony_ci * property in @props array at the index of the child node distance from the 10238c2ecf20Sopenharmony_ci * root node and the value of that property matching with the integer argument 10248c2ecf20Sopenharmony_ci * of the reference, at the same index. 10258c2ecf20Sopenharmony_ci * 10268c2ecf20Sopenharmony_ci * The child fwnode reached at the end of the iteration is then returned to the 10278c2ecf20Sopenharmony_ci * caller. 10288c2ecf20Sopenharmony_ci * 10298c2ecf20Sopenharmony_ci * The core reason for this is that you cannot refer to just any node in ACPI. 10308c2ecf20Sopenharmony_ci * So to refer to an endpoint (easy in DT) you need to refer to a device, then 10318c2ecf20Sopenharmony_ci * provide a list of (property name, property value) tuples where each tuple 10328c2ecf20Sopenharmony_ci * uniquely identifies a child node. The first tuple identifies a child directly 10338c2ecf20Sopenharmony_ci * underneath the device fwnode, the next tuple identifies a child node 10348c2ecf20Sopenharmony_ci * underneath the fwnode identified by the previous tuple, etc. until you 10358c2ecf20Sopenharmony_ci * reached the fwnode you need. 10368c2ecf20Sopenharmony_ci * 10378c2ecf20Sopenharmony_ci * THIS EXAMPLE EXISTS MERELY TO DOCUMENT THIS FUNCTION. DO NOT USE IT AS A 10388c2ecf20Sopenharmony_ci * REFERENCE IN HOW ACPI TABLES SHOULD BE WRITTEN!! See documentation under 10398c2ecf20Sopenharmony_ci * Documentation/firmware-guide/acpi/dsd/ instead and especially graph.txt, 10408c2ecf20Sopenharmony_ci * data-node-references.txt and leds.txt . 10418c2ecf20Sopenharmony_ci * 10428c2ecf20Sopenharmony_ci * Scope (\_SB.PCI0.I2C2) 10438c2ecf20Sopenharmony_ci * { 10448c2ecf20Sopenharmony_ci * Device (CAM0) 10458c2ecf20Sopenharmony_ci * { 10468c2ecf20Sopenharmony_ci * Name (_DSD, Package () { 10478c2ecf20Sopenharmony_ci * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), 10488c2ecf20Sopenharmony_ci * Package () { 10498c2ecf20Sopenharmony_ci * Package () { 10508c2ecf20Sopenharmony_ci * "compatible", 10518c2ecf20Sopenharmony_ci * Package () { "nokia,smia" } 10528c2ecf20Sopenharmony_ci * }, 10538c2ecf20Sopenharmony_ci * }, 10548c2ecf20Sopenharmony_ci * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"), 10558c2ecf20Sopenharmony_ci * Package () { 10568c2ecf20Sopenharmony_ci * Package () { "port0", "PRT0" }, 10578c2ecf20Sopenharmony_ci * } 10588c2ecf20Sopenharmony_ci * }) 10598c2ecf20Sopenharmony_ci * Name (PRT0, Package() { 10608c2ecf20Sopenharmony_ci * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), 10618c2ecf20Sopenharmony_ci * Package () { 10628c2ecf20Sopenharmony_ci * Package () { "port", 0 }, 10638c2ecf20Sopenharmony_ci * }, 10648c2ecf20Sopenharmony_ci * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"), 10658c2ecf20Sopenharmony_ci * Package () { 10668c2ecf20Sopenharmony_ci * Package () { "endpoint0", "EP00" }, 10678c2ecf20Sopenharmony_ci * } 10688c2ecf20Sopenharmony_ci * }) 10698c2ecf20Sopenharmony_ci * Name (EP00, Package() { 10708c2ecf20Sopenharmony_ci * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), 10718c2ecf20Sopenharmony_ci * Package () { 10728c2ecf20Sopenharmony_ci * Package () { "endpoint", 0 }, 10738c2ecf20Sopenharmony_ci * Package () { 10748c2ecf20Sopenharmony_ci * "remote-endpoint", 10758c2ecf20Sopenharmony_ci * Package() { 10768c2ecf20Sopenharmony_ci * \_SB.PCI0.ISP, 4, 0 10778c2ecf20Sopenharmony_ci * } 10788c2ecf20Sopenharmony_ci * }, 10798c2ecf20Sopenharmony_ci * } 10808c2ecf20Sopenharmony_ci * }) 10818c2ecf20Sopenharmony_ci * } 10828c2ecf20Sopenharmony_ci * } 10838c2ecf20Sopenharmony_ci * 10848c2ecf20Sopenharmony_ci * Scope (\_SB.PCI0) 10858c2ecf20Sopenharmony_ci * { 10868c2ecf20Sopenharmony_ci * Device (ISP) 10878c2ecf20Sopenharmony_ci * { 10888c2ecf20Sopenharmony_ci * Name (_DSD, Package () { 10898c2ecf20Sopenharmony_ci * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"), 10908c2ecf20Sopenharmony_ci * Package () { 10918c2ecf20Sopenharmony_ci * Package () { "port4", "PRT4" }, 10928c2ecf20Sopenharmony_ci * } 10938c2ecf20Sopenharmony_ci * }) 10948c2ecf20Sopenharmony_ci * 10958c2ecf20Sopenharmony_ci * Name (PRT4, Package() { 10968c2ecf20Sopenharmony_ci * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), 10978c2ecf20Sopenharmony_ci * Package () { 10988c2ecf20Sopenharmony_ci * Package () { "port", 4 }, 10998c2ecf20Sopenharmony_ci * }, 11008c2ecf20Sopenharmony_ci * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"), 11018c2ecf20Sopenharmony_ci * Package () { 11028c2ecf20Sopenharmony_ci * Package () { "endpoint0", "EP40" }, 11038c2ecf20Sopenharmony_ci * } 11048c2ecf20Sopenharmony_ci * }) 11058c2ecf20Sopenharmony_ci * 11068c2ecf20Sopenharmony_ci * Name (EP40, Package() { 11078c2ecf20Sopenharmony_ci * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), 11088c2ecf20Sopenharmony_ci * Package () { 11098c2ecf20Sopenharmony_ci * Package () { "endpoint", 0 }, 11108c2ecf20Sopenharmony_ci * Package () { 11118c2ecf20Sopenharmony_ci * "remote-endpoint", 11128c2ecf20Sopenharmony_ci * Package () { 11138c2ecf20Sopenharmony_ci * \_SB.PCI0.I2C2.CAM0, 11148c2ecf20Sopenharmony_ci * 0, 0 11158c2ecf20Sopenharmony_ci * } 11168c2ecf20Sopenharmony_ci * }, 11178c2ecf20Sopenharmony_ci * } 11188c2ecf20Sopenharmony_ci * }) 11198c2ecf20Sopenharmony_ci * } 11208c2ecf20Sopenharmony_ci * } 11218c2ecf20Sopenharmony_ci * 11228c2ecf20Sopenharmony_ci * From the EP40 node under ISP device, you could parse the graph remote 11238c2ecf20Sopenharmony_ci * endpoint using v4l2_fwnode_reference_get_int_prop with these arguments: 11248c2ecf20Sopenharmony_ci * 11258c2ecf20Sopenharmony_ci * @fwnode: fwnode referring to EP40 under ISP. 11268c2ecf20Sopenharmony_ci * @prop: "remote-endpoint" 11278c2ecf20Sopenharmony_ci * @index: 0 11288c2ecf20Sopenharmony_ci * @props: "port", "endpoint" 11298c2ecf20Sopenharmony_ci * @nprops: 2 11308c2ecf20Sopenharmony_ci * 11318c2ecf20Sopenharmony_ci * And you'd get back fwnode referring to EP00 under CAM0. 11328c2ecf20Sopenharmony_ci * 11338c2ecf20Sopenharmony_ci * The same works the other way around: if you use EP00 under CAM0 as the 11348c2ecf20Sopenharmony_ci * fwnode, you'll get fwnode referring to EP40 under ISP. 11358c2ecf20Sopenharmony_ci * 11368c2ecf20Sopenharmony_ci * The same example in DT syntax would look like this: 11378c2ecf20Sopenharmony_ci * 11388c2ecf20Sopenharmony_ci * cam: cam0 { 11398c2ecf20Sopenharmony_ci * compatible = "nokia,smia"; 11408c2ecf20Sopenharmony_ci * 11418c2ecf20Sopenharmony_ci * port { 11428c2ecf20Sopenharmony_ci * port = <0>; 11438c2ecf20Sopenharmony_ci * endpoint { 11448c2ecf20Sopenharmony_ci * endpoint = <0>; 11458c2ecf20Sopenharmony_ci * remote-endpoint = <&isp 4 0>; 11468c2ecf20Sopenharmony_ci * }; 11478c2ecf20Sopenharmony_ci * }; 11488c2ecf20Sopenharmony_ci * }; 11498c2ecf20Sopenharmony_ci * 11508c2ecf20Sopenharmony_ci * isp: isp { 11518c2ecf20Sopenharmony_ci * ports { 11528c2ecf20Sopenharmony_ci * port@4 { 11538c2ecf20Sopenharmony_ci * port = <4>; 11548c2ecf20Sopenharmony_ci * endpoint { 11558c2ecf20Sopenharmony_ci * endpoint = <0>; 11568c2ecf20Sopenharmony_ci * remote-endpoint = <&cam 0 0>; 11578c2ecf20Sopenharmony_ci * }; 11588c2ecf20Sopenharmony_ci * }; 11598c2ecf20Sopenharmony_ci * }; 11608c2ecf20Sopenharmony_ci * }; 11618c2ecf20Sopenharmony_ci * 11628c2ecf20Sopenharmony_ci * Return: 0 on success 11638c2ecf20Sopenharmony_ci * -ENOENT if no entries (or the property itself) were found 11648c2ecf20Sopenharmony_ci * -EINVAL if property parsing otherwise failed 11658c2ecf20Sopenharmony_ci * -ENOMEM if memory allocation failed 11668c2ecf20Sopenharmony_ci */ 11678c2ecf20Sopenharmony_cistatic struct fwnode_handle * 11688c2ecf20Sopenharmony_civ4l2_fwnode_reference_get_int_prop(struct fwnode_handle *fwnode, 11698c2ecf20Sopenharmony_ci const char *prop, 11708c2ecf20Sopenharmony_ci unsigned int index, 11718c2ecf20Sopenharmony_ci const char * const *props, 11728c2ecf20Sopenharmony_ci unsigned int nprops) 11738c2ecf20Sopenharmony_ci{ 11748c2ecf20Sopenharmony_ci struct fwnode_reference_args fwnode_args; 11758c2ecf20Sopenharmony_ci u64 *args = fwnode_args.args; 11768c2ecf20Sopenharmony_ci struct fwnode_handle *child; 11778c2ecf20Sopenharmony_ci int ret; 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci /* 11808c2ecf20Sopenharmony_ci * Obtain remote fwnode as well as the integer arguments. 11818c2ecf20Sopenharmony_ci * 11828c2ecf20Sopenharmony_ci * Note that right now both -ENODATA and -ENOENT may signal 11838c2ecf20Sopenharmony_ci * out-of-bounds access. Return -ENOENT in that case. 11848c2ecf20Sopenharmony_ci */ 11858c2ecf20Sopenharmony_ci ret = fwnode_property_get_reference_args(fwnode, prop, NULL, nprops, 11868c2ecf20Sopenharmony_ci index, &fwnode_args); 11878c2ecf20Sopenharmony_ci if (ret) 11888c2ecf20Sopenharmony_ci return ERR_PTR(ret == -ENODATA ? -ENOENT : ret); 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci /* 11918c2ecf20Sopenharmony_ci * Find a node in the tree under the referred fwnode corresponding to 11928c2ecf20Sopenharmony_ci * the integer arguments. 11938c2ecf20Sopenharmony_ci */ 11948c2ecf20Sopenharmony_ci fwnode = fwnode_args.fwnode; 11958c2ecf20Sopenharmony_ci while (nprops--) { 11968c2ecf20Sopenharmony_ci u32 val; 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci /* Loop over all child nodes under fwnode. */ 11998c2ecf20Sopenharmony_ci fwnode_for_each_child_node(fwnode, child) { 12008c2ecf20Sopenharmony_ci if (fwnode_property_read_u32(child, *props, &val)) 12018c2ecf20Sopenharmony_ci continue; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci /* Found property, see if its value matches. */ 12048c2ecf20Sopenharmony_ci if (val == *args) 12058c2ecf20Sopenharmony_ci break; 12068c2ecf20Sopenharmony_ci } 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci fwnode_handle_put(fwnode); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci /* No property found; return an error here. */ 12118c2ecf20Sopenharmony_ci if (!child) { 12128c2ecf20Sopenharmony_ci fwnode = ERR_PTR(-ENOENT); 12138c2ecf20Sopenharmony_ci break; 12148c2ecf20Sopenharmony_ci } 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci props++; 12178c2ecf20Sopenharmony_ci args++; 12188c2ecf20Sopenharmony_ci fwnode = child; 12198c2ecf20Sopenharmony_ci } 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci return fwnode; 12228c2ecf20Sopenharmony_ci} 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_cistruct v4l2_fwnode_int_props { 12258c2ecf20Sopenharmony_ci const char *name; 12268c2ecf20Sopenharmony_ci const char * const *props; 12278c2ecf20Sopenharmony_ci unsigned int nprops; 12288c2ecf20Sopenharmony_ci}; 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci/* 12318c2ecf20Sopenharmony_ci * v4l2_fwnode_reference_parse_int_props - parse references for async 12328c2ecf20Sopenharmony_ci * sub-devices 12338c2ecf20Sopenharmony_ci * @dev: struct device pointer 12348c2ecf20Sopenharmony_ci * @notifier: notifier for @dev 12358c2ecf20Sopenharmony_ci * @prop: the name of the property 12368c2ecf20Sopenharmony_ci * @props: the array of integer property names 12378c2ecf20Sopenharmony_ci * @nprops: the number of integer properties 12388c2ecf20Sopenharmony_ci * 12398c2ecf20Sopenharmony_ci * Use v4l2_fwnode_reference_get_int_prop to find fwnodes through reference in 12408c2ecf20Sopenharmony_ci * property @prop with integer arguments with child nodes matching in properties 12418c2ecf20Sopenharmony_ci * @props. Then, set up V4L2 async sub-devices for those fwnodes in the notifier 12428c2ecf20Sopenharmony_ci * accordingly. 12438c2ecf20Sopenharmony_ci * 12448c2ecf20Sopenharmony_ci * While it is technically possible to use this function on DT, it is only 12458c2ecf20Sopenharmony_ci * meaningful on ACPI. On Device tree you can refer to any node in the tree but 12468c2ecf20Sopenharmony_ci * on ACPI the references are limited to devices. 12478c2ecf20Sopenharmony_ci * 12488c2ecf20Sopenharmony_ci * Return: 0 on success 12498c2ecf20Sopenharmony_ci * -ENOENT if no entries (or the property itself) were found 12508c2ecf20Sopenharmony_ci * -EINVAL if property parsing otherwisefailed 12518c2ecf20Sopenharmony_ci * -ENOMEM if memory allocation failed 12528c2ecf20Sopenharmony_ci */ 12538c2ecf20Sopenharmony_cistatic int 12548c2ecf20Sopenharmony_civ4l2_fwnode_reference_parse_int_props(struct device *dev, 12558c2ecf20Sopenharmony_ci struct v4l2_async_notifier *notifier, 12568c2ecf20Sopenharmony_ci const struct v4l2_fwnode_int_props *p) 12578c2ecf20Sopenharmony_ci{ 12588c2ecf20Sopenharmony_ci struct fwnode_handle *fwnode; 12598c2ecf20Sopenharmony_ci unsigned int index; 12608c2ecf20Sopenharmony_ci int ret; 12618c2ecf20Sopenharmony_ci const char *prop = p->name; 12628c2ecf20Sopenharmony_ci const char * const *props = p->props; 12638c2ecf20Sopenharmony_ci unsigned int nprops = p->nprops; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci index = 0; 12668c2ecf20Sopenharmony_ci do { 12678c2ecf20Sopenharmony_ci fwnode = v4l2_fwnode_reference_get_int_prop(dev_fwnode(dev), 12688c2ecf20Sopenharmony_ci prop, index, 12698c2ecf20Sopenharmony_ci props, nprops); 12708c2ecf20Sopenharmony_ci if (IS_ERR(fwnode)) { 12718c2ecf20Sopenharmony_ci /* 12728c2ecf20Sopenharmony_ci * Note that right now both -ENODATA and -ENOENT may 12738c2ecf20Sopenharmony_ci * signal out-of-bounds access. Return the error in 12748c2ecf20Sopenharmony_ci * cases other than that. 12758c2ecf20Sopenharmony_ci */ 12768c2ecf20Sopenharmony_ci if (PTR_ERR(fwnode) != -ENOENT && 12778c2ecf20Sopenharmony_ci PTR_ERR(fwnode) != -ENODATA) 12788c2ecf20Sopenharmony_ci return PTR_ERR(fwnode); 12798c2ecf20Sopenharmony_ci break; 12808c2ecf20Sopenharmony_ci } 12818c2ecf20Sopenharmony_ci fwnode_handle_put(fwnode); 12828c2ecf20Sopenharmony_ci index++; 12838c2ecf20Sopenharmony_ci } while (1); 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci for (index = 0; 12868c2ecf20Sopenharmony_ci !IS_ERR((fwnode = v4l2_fwnode_reference_get_int_prop(dev_fwnode(dev), 12878c2ecf20Sopenharmony_ci prop, index, 12888c2ecf20Sopenharmony_ci props, 12898c2ecf20Sopenharmony_ci nprops))); 12908c2ecf20Sopenharmony_ci index++) { 12918c2ecf20Sopenharmony_ci struct v4l2_async_subdev *asd; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci asd = v4l2_async_notifier_add_fwnode_subdev(notifier, fwnode, 12948c2ecf20Sopenharmony_ci sizeof(*asd)); 12958c2ecf20Sopenharmony_ci fwnode_handle_put(fwnode); 12968c2ecf20Sopenharmony_ci if (IS_ERR(asd)) { 12978c2ecf20Sopenharmony_ci ret = PTR_ERR(asd); 12988c2ecf20Sopenharmony_ci /* not an error if asd already exists */ 12998c2ecf20Sopenharmony_ci if (ret == -EEXIST) 13008c2ecf20Sopenharmony_ci continue; 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci return PTR_ERR(asd); 13038c2ecf20Sopenharmony_ci } 13048c2ecf20Sopenharmony_ci } 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci return !fwnode || PTR_ERR(fwnode) == -ENOENT ? 0 : PTR_ERR(fwnode); 13078c2ecf20Sopenharmony_ci} 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ciint v4l2_async_notifier_parse_fwnode_sensor_common(struct device *dev, 13108c2ecf20Sopenharmony_ci struct v4l2_async_notifier *notifier) 13118c2ecf20Sopenharmony_ci{ 13128c2ecf20Sopenharmony_ci static const char * const led_props[] = { "led" }; 13138c2ecf20Sopenharmony_ci static const struct v4l2_fwnode_int_props props[] = { 13148c2ecf20Sopenharmony_ci { "flash-leds", led_props, ARRAY_SIZE(led_props) }, 13158c2ecf20Sopenharmony_ci { "lens-focus", NULL, 0 }, 13168c2ecf20Sopenharmony_ci }; 13178c2ecf20Sopenharmony_ci unsigned int i; 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(props); i++) { 13208c2ecf20Sopenharmony_ci int ret; 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci if (props[i].props && is_acpi_node(dev_fwnode(dev))) 13238c2ecf20Sopenharmony_ci ret = v4l2_fwnode_reference_parse_int_props(dev, 13248c2ecf20Sopenharmony_ci notifier, 13258c2ecf20Sopenharmony_ci &props[i]); 13268c2ecf20Sopenharmony_ci else 13278c2ecf20Sopenharmony_ci ret = v4l2_fwnode_reference_parse(dev, notifier, 13288c2ecf20Sopenharmony_ci props[i].name); 13298c2ecf20Sopenharmony_ci if (ret && ret != -ENOENT) { 13308c2ecf20Sopenharmony_ci dev_warn(dev, "parsing property \"%s\" failed (%d)\n", 13318c2ecf20Sopenharmony_ci props[i].name, ret); 13328c2ecf20Sopenharmony_ci return ret; 13338c2ecf20Sopenharmony_ci } 13348c2ecf20Sopenharmony_ci } 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci return 0; 13378c2ecf20Sopenharmony_ci} 13388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_sensor_common); 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ciint v4l2_async_register_subdev_sensor_common(struct v4l2_subdev *sd) 13418c2ecf20Sopenharmony_ci{ 13428c2ecf20Sopenharmony_ci struct v4l2_async_notifier *notifier; 13438c2ecf20Sopenharmony_ci int ret; 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci if (WARN_ON(!sd->dev)) 13468c2ecf20Sopenharmony_ci return -ENODEV; 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci notifier = kzalloc(sizeof(*notifier), GFP_KERNEL); 13498c2ecf20Sopenharmony_ci if (!notifier) 13508c2ecf20Sopenharmony_ci return -ENOMEM; 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci v4l2_async_notifier_init(notifier); 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci ret = v4l2_async_notifier_parse_fwnode_sensor_common(sd->dev, 13558c2ecf20Sopenharmony_ci notifier); 13568c2ecf20Sopenharmony_ci if (ret < 0) 13578c2ecf20Sopenharmony_ci goto out_cleanup; 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci ret = v4l2_async_subdev_notifier_register(sd, notifier); 13608c2ecf20Sopenharmony_ci if (ret < 0) 13618c2ecf20Sopenharmony_ci goto out_cleanup; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci ret = v4l2_async_register_subdev(sd); 13648c2ecf20Sopenharmony_ci if (ret < 0) 13658c2ecf20Sopenharmony_ci goto out_unregister; 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci sd->subdev_notifier = notifier; 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci return 0; 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ciout_unregister: 13728c2ecf20Sopenharmony_ci v4l2_async_notifier_unregister(notifier); 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ciout_cleanup: 13758c2ecf20Sopenharmony_ci v4l2_async_notifier_cleanup(notifier); 13768c2ecf20Sopenharmony_ci kfree(notifier); 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci return ret; 13798c2ecf20Sopenharmony_ci} 13808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_async_register_subdev_sensor_common); 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 13838c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>"); 13848c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); 13858c2ecf20Sopenharmony_ciMODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); 1386