On Wed, Mar 29, 2023 at 12:10 PM Sakari Ailus <sakari.ailus@xxxxxxxxxxxxxxx> wrote: > > Prepare generating software nodes for information parsed from ACPI _CRS for > CSI-2 as well as MIPI DisCo for Imaging spec. The software nodes are > compliant with existing ACPI or DT definitions and are parsed by relevant > drivers without changes. > > Signed-off-by: Sakari Ailus <sakari.ailus@xxxxxxxxxxxxxxx> > --- > drivers/acpi/internal.h | 3 + > drivers/acpi/mipi.c | 358 +++++++++++++++++++++++++++++++++++++++- > drivers/acpi/scan.c | 21 ++- > include/acpi/acpi_bus.h | 70 ++++++++ > 4 files changed, 449 insertions(+), 3 deletions(-) > > diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h > index aa5f9c69dbbe..1634a177c75e 100644 > --- a/drivers/acpi/internal.h > +++ b/drivers/acpi/internal.h > @@ -304,9 +304,12 @@ static inline void acpi_init_lpit(void) { } > ACPI _CRS CSI2 and MIPI DisCo for Imaging conversion > -------------------------------------------------------------------------- */ > > +#define MIPI_IMG_PORT_PREFIX "mipi-img-port-" > + > void acpi_crs_csi2_swnodes_del_free(void); > void acpi_bus_scan_check_crs_csi2(acpi_handle handle, struct acpi_scan_context *ctx); > void acpi_bus_scan_crs_csi2_release(struct list_head *crs_csi2_handles); > void acpi_bus_scan_crs_csi2(struct acpi_scan_context_csi2 *ctx); > +void acpi_init_swnodes(struct acpi_device *device); > > #endif /* _ACPI_INTERNAL_H_ */ > diff --git a/drivers/acpi/mipi.c b/drivers/acpi/mipi.c > index d719c879eb83..03079f78bd2c 100644 > --- a/drivers/acpi/mipi.c > +++ b/drivers/acpi/mipi.c > @@ -3,7 +3,8 @@ > * MIPI DisCo for Imaging support. > * > * Support MIPI DisCo for Imaging by parsing ACPI _CRS CSI2 records and DisCo > - * for Imaging data structures. > + * for Imaging data structures and generating nodes and properties using > + * software nodes compliant with DT definitions of the similar scope. > * > * Also see <URL:https://www.mipi.org/specifications/mipi-disco-imaging>. > * > @@ -20,6 +21,8 @@ > #include <linux/sort.h> > #include <linux/string.h> > > +#include <media/v4l2-fwnode.h> > + > #include "internal.h" > > /* Temporary ACPI device handle to software node data structure mapping */ > @@ -31,6 +34,18 @@ struct crs_csi2_swnodes { > > static LIST_HEAD(crs_csi2_swnodes); > > +/* Obtain pre-allocated software nodes for an ACPI device handle */ > +static struct acpi_device_software_nodes *crs_csi2_swnode_get(acpi_handle handle) > +{ > + struct crs_csi2_swnodes *swnodes; > + > + list_for_each_entry(swnodes, &crs_csi2_swnodes, list) > + if (swnodes->handle == handle) > + return swnodes->ads; > + > + return NULL; > +} > + > static void crs_csi2_swnode_del_free(struct crs_csi2_swnodes *swnodes) > { > list_del(&swnodes->list); > @@ -166,6 +181,35 @@ struct acpi_handle_ref { > > #define NO_CSI2_PORT (UINT_MAX - 1) > > +/* > + * Return next free entry in ports array of a software nodes related to an ACPI > + * device. > + */ > +static unsigned int next_csi2_port_index(struct acpi_device_software_nodes *ads, > + unsigned int port_nr) > +{ > + unsigned int i; > + > + for (i = 0; i < ads->num_ports; i++) { > + struct acpi_device_software_node_port *port = &ads->ports[i]; > + > + if (port->port_nr == port_nr) > + return i; > + > + if (port->port_nr == NO_CSI2_PORT) { > + port->port_nr = port_nr; > + return i; > + } > + } > + > + return NO_CSI2_PORT; > +} > + > +/* Print graph port name into a buffer, return non-zero if failed. */ > +#define GRAPH_PORT_NAME(var, num) \ > + (snprintf((var), sizeof(var), SWNODE_GRAPH_PORT_NAME_FMT, (num)) >= \ > + sizeof(var)) > + > static int crs_handle_cmp(const void *__a, const void *__b) > { > const struct acpi_handle_ref *a = __a, *b = __b; > @@ -258,6 +302,9 @@ static void acpi_crs_csi2_alloc_fill_swnodes(size_t ports_count, acpi_handle han > ports_count); > } > > +#define ACPI_CRS_CSI2_PHY_TYPE_C 0 > +#define ACPI_CRS_CSI2_PHY_TYPE_D 1 > + > /** > * acpi_bus_scan_crs_csi2 - Construct software nodes out of ACPI _CRS CSI2 > * resource descriptors > @@ -274,6 +321,8 @@ static void acpi_crs_csi2_alloc_fill_swnodes(size_t ports_count, acpi_handle han > * 3. Allocate memory for swnodes each ACPI device requires later on, and > * generate a list of such allocations. > * > + * 4. Set up properties for software nodes. > + * > * Note that struct acpi_device may not be available yet at this time. > * > * acpi_scan_lock in scan.c must be held when calling this function. > @@ -339,5 +388,312 @@ void acpi_bus_scan_crs_csi2(struct acpi_scan_context_csi2 *ctx) > this_count = this->count; > } > > + /* > + * Allocate and set up necessary software nodes for each device and set > + * up properties from _CRS CSI2 descriptor. > + */ > + list_for_each_entry(csi2, &ctx->crs_csi2_head, list) { > + struct acpi_device_software_nodes *local_swnodes; > + struct crs_csi2_instance *inst; > + > + local_swnodes = crs_csi2_swnode_get(csi2->handle); > + if (WARN_ON_ONCE(!local_swnodes)) > + continue; > + > + list_for_each_entry(inst, &csi2->buses, list) { > + struct acpi_device_software_nodes *remote_swnodes; > + struct acpi_device_software_node_port *local_port; > + struct acpi_device_software_node_port *remote_port; > + struct software_node *local_node, *remote_node; > + unsigned int local_index, remote_index; > + unsigned int bus_type; > + > + remote_swnodes = crs_csi2_swnode_get(inst->remote_handle); > + if (WARN_ON_ONCE(!remote_swnodes)) > + continue; > + > + local_index = next_csi2_port_index(local_swnodes, inst->csi2.local_port_instance); > + remote_index = next_csi2_port_index(remote_swnodes, inst->csi2.resource_source.index); > + > + if (WARN_ON_ONCE(local_index >= local_swnodes->num_ports) || > + WARN_ON_ONCE(remote_index >= remote_swnodes->num_ports)) > + goto out_free; > + > + switch (inst->csi2.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(csi2->handle, > + "ignoring CSI-2 PHY type %u\n", > + inst->csi2.phy_type); > + continue; > + } > + > + local_port = &local_swnodes->ports[local_index]; > + local_node = &local_swnodes->nodes[ACPI_DEVICE_SWNODE_EP(local_index)]; > + local_port->remote_ep_ref[0] = SOFTWARE_NODE_REFERENCE(local_node); This looks odd. Is local_port pointing to its own node as a remote endpont, or am I confused? > + local_port->crs_csi2_local = true; > + > + remote_port = &remote_swnodes->ports[remote_index]; > + remote_node = &remote_swnodes->nodes[ACPI_DEVICE_SWNODE_EP(remote_index)]; > + remote_port->remote_ep_ref[0] = SOFTWARE_NODE_REFERENCE(remote_node); Analogously here.