// SPDX-License-Identifier: GPL-2.0-only /* * MIPI DisCo for Imaging support. * * Copyright (C) 2023 Intel Corporation * * Support MIPI DisCo for Imaging by parsing ACPI _CRS CSI-2 records defined in * Section 6.4.3.8.2.4 "Camera Serial Interface (CSI-2) Connection Resource * Descriptor" of ACPI 6.5 and using device properties defined by the MIPI DisCo * for Imaging specification. * * The implementation looks for the information in the ACPI namespace (CSI-2 * resource descriptors in _CRS) and constructs software nodes compatible with * Documentation/firmware-guide/acpi/dsd/graph.rst to represent the CSI-2 * connection graph. The software nodes are then populated with the data * extracted from the _CRS CSI-2 resource descriptors and the MIPI DisCo * for Imaging device properties present in _DSD for the ACPI device objects * with CSI-2 connections.
*/
/** * acpi_mipi_check_crs_csi2 - Look for CSI-2 resources in _CRS * @handle: Device object handle to evaluate _CRS for. * * Find all CSI-2 resource descriptors in the given device's _CRS * and collect them into a list.
*/ void acpi_mipi_check_crs_csi2(acpi_handle handle)
{ struct csi2_resources_walk_data crwd = {
.handle = handle,
.connections = LIST_HEAD_INIT(crwd.connections),
}; struct crs_csi2 *csi2;
/* * Avoid allocating _CRS CSI-2 objects for devices without any CSI-2 * resource descriptions in _CRS to reduce overhead.
*/
acpi_walk_resources(handle, METHOD_NAME__CRS, parse_csi2_resource, &crwd); if (list_empty(&crwd.connections)) return;
/* * Create a _CRS CSI-2 entry to store the extracted connection * information and add it to the global list.
*/
csi2 = acpi_mipi_add_crs_csi2(handle, &acpi_mipi_crs_csi2_list); if (!csi2) {
csi_csr2_release_connections(&crwd.connections); return; /* Nothing really can be done about this. */
}
/* Print graph port name into a buffer, return non-zero on failure. */ #define GRAPH_PORT_NAME(var, num) \
(snprintf((var), sizeof(var), SWNODE_GRAPH_PORT_NAME_FMT, (num)) >= \ sizeof(var))
/* * If the previous steps have failed to make room for a _CRS CSI-2 * representation for the remote end of the given connection, skip it.
*/ if (!remote_csi2) return;
remote_swnodes = remote_csi2->swnodes; if (!remote_swnodes) return;
switch (conn->csi2_data.phy_type) { case ACPI_CRS_CSI2_PHY_TYPE_C:
bus_type = V4L2_FWNODE_BUS_TYPE_CSI2_CPHY; break;
case ACPI_CRS_CSI2_PHY_TYPE_D:
bus_type = V4L2_FWNODE_BUS_TYPE_CSI2_DPHY; break;
default:
acpi_handle_info(local_handle, "unknown CSI-2 PHY type %u\n",
conn->csi2_data.phy_type); return;
}
local_index = next_csi2_port_index(local_swnodes,
conn->csi2_data.local_port_instance); if (WARN_ON_ONCE(local_index >= local_swnodes->num_ports)) return;
remote_index = next_csi2_port_index(remote_swnodes,
conn->csi2_data.resource_source.index); if (WARN_ON_ONCE(remote_index >= remote_swnodes->num_ports)) return;
if (GRAPH_PORT_NAME(local_port->port_name,
conn->csi2_data.local_port_instance))
acpi_handle_info(local_handle, "local port %u name too long",
conn->csi2_data.local_port_instance);
if (GRAPH_PORT_NAME(remote_port->port_name,
conn->csi2_data.resource_source.index))
acpi_handle_info(local_handle, "remote port %u name too long",
conn->csi2_data.resource_source.index);
}
/** * acpi_mipi_scan_crs_csi2 - Create ACPI _CRS CSI-2 software nodes * * Note that this function must be called before any struct acpi_device objects * are bound to any ACPI drivers or scan handlers, so it cannot assume the * existence of struct acpi_device objects for every device present in the ACPI * namespace. * * acpi_scan_lock in scan.c must be held when calling this function.
*/ void acpi_mipi_scan_crs_csi2(void)
{ struct crs_csi2 *csi2;
LIST_HEAD(aux_list);
/* Count references to each ACPI handle in the CSI-2 connection graph. */
list_for_each_entry(csi2, &acpi_mipi_crs_csi2_list, entry) { struct crs_csi2_connection *conn;
remote_csi2 = acpi_mipi_get_crs_csi2(conn->remote_handle); if (remote_csi2) {
remote_csi2->port_count++; continue;
} /* * The remote endpoint has no _CRS CSI-2 list entry yet, * so create one for it and add it to the list.
*/
acpi_mipi_add_crs_csi2(conn->remote_handle, &aux_list);
}
}
list_splice(&aux_list, &acpi_mipi_crs_csi2_list);
/* * Allocate software nodes for representing the CSI-2 information. * * This needs to be done for all of the list entries in one go, because * they may point to each other without restrictions and the next step * relies on the availability of swnodes memory for each list entry.
*/
list_for_each_entry(csi2, &acpi_mipi_crs_csi2_list, entry)
alloc_crs_csi2_swnodes(csi2);
/* * Set up software node properties using data from _CRS CSI-2 resource * descriptors.
*/
list_for_each_entry(csi2, &acpi_mipi_crs_csi2_list, entry)
prepare_crs_csi2_swnodes(csi2);
}
/* * Get the index of the next property in the property array, with a given * maximum value.
*/ #define NEXT_PROPERTY(index, max) \
(WARN_ON((index) > ACPI_DEVICE_SWNODE_##max) ? \
ACPI_DEVICE_SWNODE_##max : (index)++)
ret = fwnode_property_count_u64(port_fwnode, "mipi-img-link-frequencies"); if (ret <= 0) return;
num_link_freqs = ret; if (num_link_freqs > ACPI_DEVICE_CSI2_DATA_LANES) {
acpi_handle_info(handle, "Too many link frequencies: %u\n",
num_link_freqs);
num_link_freqs = ACPI_DEVICE_CSI2_DATA_LANES;
}
ret = fwnode_property_read_u64_array(port_fwnode, "mipi-img-link-frequencies",
port->link_frequencies,
num_link_freqs); if (ret) {
acpi_handle_info(handle, "Unable to get link frequencies (%d)\n",
ret); return;
}
ret = fwnode_property_count_u8(port_fwnode, "mipi-img-lane-polarities"); if (ret < 0) {
acpi_handle_debug(handle, "Lane polarity bytes missing\n");
} elseif (ret * BITS_PER_TYPE(u8) < num_lanes + 1) {
acpi_handle_info(handle, "Too few lane polarity bits (%zu vs. %d)\n",
ret * BITS_PER_TYPE(u8), num_lanes + 1);
} else { unsignedlong mask = 0; int byte_count = ret; unsignedint i;
/* * The total number of lanes is ACPI_DEVICE_CSI2_DATA_LANES + 1 * (data lanes + clock lane). It is not expected to ever be * greater than the number of bits in an unsigned long * variable, but ensure that this is the case.
*/
BUILD_BUG_ON(BITS_PER_TYPE(unsignedlong) <= ACPI_DEVICE_CSI2_DATA_LANES);
if (byte_count > sizeof(mask)) {
acpi_handle_info(handle, "Too many lane polarities: %d\n",
byte_count);
byte_count = sizeof(mask);
}
fwnode_property_read_u8_array(port_fwnode, "mipi-img-lane-polarities",
val, byte_count);
for (i = 0; i < byte_count; i++)
mask |= (unsignedlong)val[i] << BITS_PER_TYPE(u8) * i;
for (i = 0; i <= num_lanes; i++)
port->lane_polarities[i] = test_bit(i, &mask);
/* * Bail out if the swnodes are not available (either they have not been * allocated or they have been assigned to the device already).
*/ if (!swnodes) return;
adev = acpi_fetch_acpi_dev(handle); if (!adev) return;
adev_fwnode = acpi_fwnode_handle(adev);
/* * If the "rotation" property is not present, but _PLD is there, * evaluate it to get the "rotation" value.
*/ if (!fwnode_property_present(adev_fwnode, "rotation")) { struct acpi_pld_info *pld;
if (!fwnode_property_read_u32(adev_fwnode, "mipi-img-clock-frequency", &val))
swnodes->dev_props[NEXT_PROPERTY(prop_index, DEV_CLOCK_FREQUENCY)] =
PROPERTY_ENTRY_U32("clock-frequency", val);
if (!fwnode_property_read_u32(adev_fwnode, "mipi-img-led-max-current", &val))
swnodes->dev_props[NEXT_PROPERTY(prop_index, DEV_LED_MAX_MICROAMP)] =
PROPERTY_ENTRY_U32("led-max-microamp", val);
if (!fwnode_property_read_u32(adev_fwnode, "mipi-img-flash-max-current", &val))
swnodes->dev_props[NEXT_PROPERTY(prop_index, DEV_FLASH_MAX_MICROAMP)] =
PROPERTY_ENTRY_U32("flash-max-microamp", val);
if (!fwnode_property_read_u32(adev_fwnode, "mipi-img-flash-max-timeout-us", &val))
swnodes->dev_props[NEXT_PROPERTY(prop_index, DEV_FLASH_MAX_TIMEOUT_US)] =
PROPERTY_ENTRY_U32("flash-max-timeout-us", val);
status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); if (ACPI_FAILURE(status)) {
acpi_handle_info(handle, "Unable to get the path name\n"); return;
}
for (i = 0; i < swnodes->num_ports; i++) { struct acpi_device_software_node_port *port = &swnodes->ports[i]; struct fwnode_handle *port_fwnode;
/* * The MIPI DisCo for Imaging specification defines _DSD device * properties for providing CSI-2 port parameters that can be * accessed through the generic device properties framework. To * access them, it is first necessary to find the data node * representing the port under the given ACPI device object.
*/
port_fwnode = get_mipi_port_handle(adev_fwnode, port->port_nr); if (!port_fwnode) {
acpi_handle_info(handle, "MIPI port name too long for port %u\n",
port->port_nr); continue;
}
/* * Prevents the swnodes from this csi2 entry from being assigned again * or freed prematurely.
*/
csi2->swnodes = NULL;
}
/** * acpi_mipi_init_crs_csi2_swnodes - Initialize _CRS CSI-2 software nodes * * Use MIPI DisCo for Imaging device properties to finalize the initialization * of CSI-2 software nodes for all ACPI device objects that have been already * enumerated.
*/ void acpi_mipi_init_crs_csi2_swnodes(void)
{ struct crs_csi2 *csi2, *csi2_tmp;
/** * acpi_graph_ignore_port - Tell whether a port node should be ignored * @handle: The ACPI handle of the node (which may be a port node) * * Return: true if a port node should be ignored and the data to that should * come from other sources instead (Windows ACPI definitions and * ipu-bridge). This is currently used to ignore bad port nodes related to IPU6 * ("IPU?") and camera sensor devices ("LNK?") in certain Dell systems with * Intel VSC.
*/ bool acpi_graph_ignore_port(acpi_handle handle)
{ constchar *path = NULL, *orig_path; staticbool dmi_tested, ignore_port;
if (!dmi_tested) { if (dmi_name_in_vendors("Dell Inc.") &&
x86_match_cpu(dell_broken_mipi_disco_cpu_gens))
ignore_port = true;
dmi_tested = true;
}
if (!ignore_port) returnfalse;
/* Check if the device is either "IPU" or "LNK" (sensor). */
orig_path = acpi_handle_path(handle); if (!orig_path) returnfalse;
path = strnext(orig_path, "IPU"); if (!path)
path = strnext(orig_path, "LNK"); if (!path) goto out_free;
if (!(isdigit(path[0]) && path[1] == '.')) goto out_free;
/* Check if the node has a "PRT" prefix. */
path = strnext(path, "PRT"); if (path && isdigit(path[0]) && !path[1]) {
acpi_handle_debug(handle, "ignoring data node\n");
kfree(orig_path); returntrue;
}
out_free:
kfree(orig_path); returnfalse;
} #endif
Messung V0.5
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.