Message ID | 20230329100951.1522322-4-sakari.ailus@linux.intel.com |
---|---|
State | New |
Headers | show |
Series | ACPI _CRS CSI-2 and MIPI DisCo for Imaging support | expand |
On Wednesday, March 29, 2023 12:09:44 PM CEST Sakari Ailus wrote: > Parse newly added ACPI _CRS CSI-2 descriptor for CSI-2 and camera > configuration, associate it with appropriate devices and allocate memory for > software nodes needed to create a DT-like data structure for drivers. It occurred to me, that there would be so many things I would like to change in this patch, so it would be better to create my own version of it, which is appended. It is based on https://patchwork.kernel.org/project/linux-acpi/patch/2694293.mvXUDI8C0e@kreacher/ that has just been posted. IIUC, the idea is to extract the ACPI handle for each resource source in every _CRS CSI-2 resource descriptor and count how many times each handle appears in a CSI-2 context, either because it is referenced from a _CRS CSI-2 resource descriptor (as a "resource source"), or because its device object has CSI-2 resource descriptors in _CRS. This allows a set of software nodes to be allocated for each of these handles. If I got that totally wrong, please let me know. Otherwise, I will rework the remaining patches in the series to match this one. Thanks! --- From: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Subject: [PATCH] ACPI: scan: Parse _CRS CSI-2 descriptors for all devices Parse ACPI _CRS CSI-2 resource descriptors defined since ACPI 6.4 (for CSI-2 and camera configuration) for all device objects in the ACPI namespace, find the corresponding "remote" device objects for them and allocate memory for software nodes needed to create a DT-like data structure for drivers (that will be taken care of by subsequent patches). Co-developed-by: Sakari Ailus <sakari.ailus@linux.intel.com> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> --- drivers/acpi/Makefile | 2 drivers/acpi/internal.h | 7 drivers/acpi/mipi-disco-imaging.c | 313 ++++++++++++++++++++++++++++++++++++++ drivers/acpi/scan.c | 49 ++++- include/acpi/acpi_bus.h | 11 + 5 files changed, 372 insertions(+), 10 deletions(-) create mode 100644 drivers/acpi/mipi-disco-imaging.c Index: linux-pm/drivers/acpi/Makefile =================================================================== --- linux-pm.orig/drivers/acpi/Makefile +++ linux-pm/drivers/acpi/Makefile @@ -37,7 +37,7 @@ acpi-$(CONFIG_ACPI_SLEEP) += proc.o # ACPI Bus and Device Drivers # acpi-y += bus.o glue.o -acpi-y += scan.o +acpi-y += scan.o mipi-disco-imaging.o acpi-y += resource.o acpi-y += acpi_processor.o acpi-y += processor_core.o Index: linux-pm/drivers/acpi/internal.h =================================================================== --- linux-pm.orig/drivers/acpi/internal.h +++ linux-pm/drivers/acpi/internal.h @@ -282,4 +282,11 @@ void acpi_init_lpit(void); static inline void acpi_init_lpit(void) { } #endif +/*-------------------------------------------------------------------------- + ACPI _CRS CSI-2 and MIPI DisCo for Imaging + -------------------------------------------------------------------------- */ + +void acpi_mipi_check_crs_csi2(acpi_handle handle); +void acpi_mipi_scan_crs_csi2(void); + #endif /* _ACPI_INTERNAL_H_ */ Index: linux-pm/drivers/acpi/mipi-disco-imaging.c =================================================================== --- /dev/null +++ linux-pm/drivers/acpi/mipi-disco-imaging.c @@ -0,0 +1,313 @@ +// 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. + * + * The implementation looks for the information in the ACPI namespace (CSI-2 + * resource descriptors in _CRS) and constructs software nodes that are + * compatible with Documentation/firmware-guide/acpi/dsd/graph.rst. + */ + +#include <linux/acpi.h> +#include <linux/limits.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/overflow.h> +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/sort.h> +#include <linux/string.h> + +#include "internal.h" + +static LIST_HEAD(acpi_mipi_crs_csi2_list); + +/* + * List entry for the list of devices with software nodes allocated in + * accordance with the information from _CRS CSI-2 device desciptiors. + */ +struct crs_csi2_dev_swnodes { + struct list_head entry; + acpi_handle handle; + struct acpi_device_software_nodes *swnodes; +}; + +static LIST_HEAD(crs_csi2_swnodes); + +static void acpi_mipi_data_tag(acpi_handle handle, void *context) +{ +} + +/* Connection data extracted from one _CRS CSI-2 resource descriptor. */ +struct crs_csi2_connection { + struct list_head entry; + struct acpi_resource_csi2_serialbus csi2_data; + acpi_handle remote_handle; + char remote_name[]; +}; + +/* Data extracted from _CRS CSI-2 resource descriptors for one device. */ +struct crs_csi2 { + struct list_head entry; + acpi_handle handle; + struct list_head connections; + u32 port_count; +}; + +static acpi_status csi2_resource_extract(struct acpi_resource *res, void *context) +{ + struct crs_csi2 *csi2 = context; + struct acpi_resource_csi2_serialbus *csi2_res; + struct acpi_resource_source *csi2_res_src; + u16 csi2_res_src_length; + struct crs_csi2_connection *conn; + acpi_handle remote_handle; + + if (res->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) + return AE_OK; + + csi2_res = &res->data.csi2_serial_bus; + + if (csi2_res->type != ACPI_RESOURCE_SERIAL_TYPE_CSI2) + return AE_OK; + + csi2_res_src = &csi2_res->resource_source; + if (ACPI_FAILURE(acpi_get_handle(NULL, csi2_res_src->string_ptr, + &remote_handle))) { + acpi_handle_debug(csi2->handle, + "unable to find resource source\n"); + return AE_OK; + } + csi2_res_src_length = csi2_res_src->string_length; + if (!csi2_res_src_length) { + acpi_handle_debug(csi2->handle, + "invalid resource source string length\n"); + return AE_OK; + } + + conn = kmalloc(struct_size(conn, remote_name, csi2_res_src_length + 1), + GFP_KERNEL); + if (!conn) + return AE_OK; + + conn->csi2_data = *csi2_res; + strscpy(conn->remote_name, csi2_res_src->string_ptr, csi2_res_src_length); + conn->csi2_data.resource_source.string_ptr = conn->remote_name; + conn->remote_handle = remote_handle; + + list_add(&conn->entry, &csi2->connections); + + return AE_OK; +} + +static struct crs_csi2 *create_crs_csi2_entry(acpi_handle handle) +{ + struct crs_csi2 *csi2_entry; + + csi2_entry = kzalloc(sizeof(*csi2_entry), GFP_KERNEL); + if (!csi2_entry) + return NULL; + + csi2_entry->handle = handle; + INIT_LIST_HEAD(&csi2_entry->connections); + csi2_entry->port_count = 1; + + if (ACPI_FAILURE(acpi_attach_data(handle, acpi_mipi_data_tag, csi2_entry))) { + kfree(csi2_entry); + return NULL; + } + + return csi2_entry; +} + +/** + * acpi_mipi_check_crs_csi2 - Look for devices with _CRS CSI-2 resources + * @handle: ACPI namespace walk starting point. + * + * Find all devices with _CRS CSI-2 resource descriptors in the ACPI namespace + * branch starting at @handle and collect them into a list. + */ +void acpi_mipi_check_crs_csi2(acpi_handle handle) +{ + struct crs_csi2 csi2 = { + .handle = handle, + .connections = LIST_HEAD_INIT(csi2.connections), + }; + struct crs_csi2 *csi2_entry; + + /* + * Avoid creating _CRS CSI-2 list entries for devices without any CSI-2 + * resource descriptions in _CRS to reduce overhead. + */ + acpi_walk_resources(handle, METHOD_NAME__CRS, csi2_resource_extract, &csi2); + if (list_empty(&csi2.connections)) + return; + + /* + * Create a _CRS CSI-2 entry to store the extracted connection + * information and add it to the global list. + */ + csi2_entry = create_crs_csi2_entry(handle); + if (!csi2_entry) + return; /* There's nothing we really can do about this. */ + + list_replace(&csi2.connections, &csi2_entry->connections); + list_add(&csi2_entry->entry, &acpi_mipi_crs_csi2_list); +} + +#define NO_CSI2_PORT (UINT_MAX - 1) + +static void alloc_fill_crs_csi2_swnodes(acpi_handle handle, size_t port_count) +{ + struct acpi_device_software_nodes *swnodes; + struct crs_csi2_dev_swnodes *swnodes_entry; + size_t alloc_size; + unsigned int i; + bool overflow; + + /* + * Allocate memory for ports, node pointers (number of nodes + + * 1 (guardian), nodes (root + number of ports * 2 (because for + * every port there is an endpoint)). + */ + overflow = check_mul_overflow(sizeof(*swnodes->ports) + + sizeof(*swnodes->nodes) * 2 + + sizeof(*swnodes->nodeptrs) * 2, + port_count, &alloc_size); + overflow = overflow || + check_add_overflow(sizeof(*swnodes) + + sizeof(*swnodes->nodes) + + sizeof(*swnodes->nodeptrs) * 2, + alloc_size, &alloc_size); + if (overflow) { + acpi_handle_warn(handle, + "too many _CRS CSI-2 resource handles (%zu)", + port_count); + return; + } + + swnodes_entry = kzalloc(sizeof(*swnodes_entry), GFP_KERNEL); + swnodes = kmalloc(alloc_size, GFP_KERNEL); + if (!swnodes_entry || !swnodes) + goto out_free; + + swnodes->ports = (struct acpi_device_software_node_port *)(swnodes + 1); + swnodes->nodes = (struct software_node *)(swnodes->ports + port_count); + swnodes->nodeptrs = (const struct software_node **)(swnodes->nodes + 1 + + 2 * port_count); + swnodes->num_ports = port_count; + + for (i = 0; i < 2 * port_count + 1; i++) + swnodes->nodeptrs[i] = &swnodes->nodes[i]; + + swnodes->nodeptrs[i] = NULL; + + for (i = 0; i < port_count; i++) + swnodes->ports[i].port_nr = NO_CSI2_PORT; + + swnodes_entry->handle = handle; + swnodes_entry->swnodes = swnodes; + + list_add(&swnodes_entry->entry, &crs_csi2_swnodes); + + return; + +out_free: + kfree(swnodes_entry); + kfree(swnodes); + acpi_handle_debug(handle, "unable to allocate for %zu software nodes\n", + port_count); +} + +static void acpi_mipi_crs_csi2_release(void) +{ + struct crs_csi2 *csi2, *csi2_tmp; + + list_for_each_entry_safe(csi2, csi2_tmp, &acpi_mipi_crs_csi2_list, entry) { + struct crs_csi2_connection *conn, *conn_tmp; + + list_for_each_entry_safe(conn, conn_tmp, &csi2->connections, + entry) { + list_del(&conn->entry); + kfree(conn); + } + + list_del(&csi2->entry); + acpi_detach_data(csi2->handle, acpi_mipi_data_tag); + kfree(csi2); + } +} + +/** + * acpi_mipi_scan_crs_csi2 - Allocate 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; + + list_for_each_entry(conn, &csi2->connections, entry) { + struct crs_csi2 *remote_csi2; + acpi_status status; + + status = acpi_get_data_full(conn->remote_handle, + acpi_mipi_data_tag, + (void **)&remote_csi2, + NULL); + if (ACPI_SUCCESS(status) && remote_csi2) { + remote_csi2->port_count++; + continue; + } + + /* + * The "remote" device has no _CRS CSI-2 list entry yet, + * so create one for it and add it to the list. + */ + remote_csi2 = create_crs_csi2_entry(conn->remote_handle); + if (!remote_csi2) + continue; + + list_add(&remote_csi2->entry, &aux_list); + } + } + list_splice(&aux_list, &acpi_mipi_crs_csi2_list); + + /* Allocate softwware nodes for representing the CSI-2 information. */ + list_for_each_entry(csi2, &acpi_mipi_crs_csi2_list, entry) + alloc_fill_crs_csi2_swnodes(csi2->handle, csi2->port_count); + + acpi_mipi_crs_csi2_release(); +} + +static void crs_csi2_swnode_del_free(struct crs_csi2_dev_swnodes *swnodes) +{ + list_del(&swnodes->entry); + kfree(swnodes); +} + +/** + * acpi_mipi_swnodes_release - Free the list of devices with _CRS CSI-2 swnodes + */ +void acpi_mipi_swnodes_release(void) +{ + struct crs_csi2_dev_swnodes *sn, *sn_tmp; + + list_for_each_entry_safe(sn, sn_tmp, &crs_csi2_swnodes, entry) + crs_csi2_swnode_del_free(sn); +} Index: linux-pm/include/acpi/acpi_bus.h =================================================================== --- linux-pm.orig/include/acpi/acpi_bus.h +++ linux-pm/include/acpi/acpi_bus.h @@ -363,6 +363,17 @@ struct acpi_device_data { struct acpi_gpio_mapping; +struct acpi_device_software_node_port { + unsigned int port_nr; +}; + +struct acpi_device_software_nodes { + struct software_node *nodes; + const struct software_node **nodeptrs; + struct acpi_device_software_node_port *ports; + unsigned int num_ports; +}; + /* Device */ struct acpi_device { u32 pld_crc; Index: linux-pm/drivers/acpi/scan.c =================================================================== --- linux-pm.orig/drivers/acpi/scan.c +++ linux-pm/drivers/acpi/scan.c @@ -1970,7 +1970,7 @@ static void acpi_scan_init_hotplug(struc } } -static u32 acpi_scan_check_dep(acpi_handle handle, bool check_dep) +static u32 acpi_scan_check_dep(acpi_handle handle) { struct acpi_handle_list dep_devices; acpi_status status; @@ -1983,8 +1983,7 @@ static u32 acpi_scan_check_dep(acpi_hand * 2. ACPI nodes describing USB ports. * Still, checking for _HID catches more then just these cases ... */ - if (!check_dep || !acpi_has_method(handle, "_DEP") || - !acpi_has_method(handle, "_HID")) + if (!acpi_has_method(handle, "_DEP") || !acpi_has_method(handle, "_HID")) return 0; status = acpi_evaluate_reference(handle, "_DEP", NULL, &dep_devices); @@ -2029,7 +2028,16 @@ static u32 acpi_scan_check_dep(acpi_hand return count; } -static acpi_status acpi_bus_check_add(acpi_handle handle, bool check_dep, +static acpi_status acpi_scan_check_crs_csi2_cb(acpi_handle handle, + u32 lvl_not_used, + void *not_used, + void **ret_p_not_used) +{ + acpi_mipi_check_crs_csi2(handle); + return AE_OK; +} + +static acpi_status acpi_bus_check_add(acpi_handle handle, bool first_pass, struct acpi_device **adev_p) { struct acpi_device *device = acpi_fetch_acpi_dev(handle); @@ -2047,9 +2055,25 @@ static acpi_status acpi_bus_check_add(ac if (acpi_device_should_be_hidden(handle)) return AE_OK; - /* Bail out if there are dependencies. */ - if (acpi_scan_check_dep(handle, check_dep) > 0) - return AE_CTRL_DEPTH; + if (first_pass) { + acpi_mipi_check_crs_csi2(handle); + + /* Bail out if there are dependencies. */ + if (acpi_scan_check_dep(handle) > 0) { + /* + * The entire CSI-2 connection graph needs to be + * extracted before any drivers or scan handlers + * are bound to struct device objects, so scan + * _CRS CSI-2 resource descriptors for all + * devices below the current handle. + */ + acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, + ACPI_UINT32_MAX, + acpi_scan_check_crs_csi2_cb, + NULL, NULL, NULL); + return AE_CTRL_DEPTH; + } + } fallthrough; case ACPI_TYPE_ANY: /* for ACPI_ROOT_OBJECT */ @@ -2072,10 +2096,10 @@ static acpi_status acpi_bus_check_add(ac } /* - * If check_dep is true at this point, the device has no dependencies, + * If first_pass is true at this point, the device has no dependencies, * or the creation of the device object would have been postponed above. */ - acpi_add_single_object(&device, handle, type, !check_dep); + acpi_add_single_object(&device, handle, type, !first_pass); if (!device) return AE_CTRL_DEPTH; @@ -2487,6 +2511,13 @@ int acpi_bus_scan(acpi_handle handle) if (!device) return -ENODEV; + /* + * Allocate ACPI _CRS CSI-2 software nodes using information extracted + * from the _CRS CSI-2 resource descriptors during the ACPI namespace + * walk above. + */ + acpi_mipi_scan_crs_csi2(); + acpi_bus_attach(device, (void *)true); /* Pass 2: Enumerate all of the remaining devices. */
Hi Rafael, On Mon, May 15, 2023 at 06:45:10PM +0200, Rafael J. Wysocki wrote: > On Wednesday, March 29, 2023 12:09:44 PM CEST Sakari Ailus wrote: > > Parse newly added ACPI _CRS CSI-2 descriptor for CSI-2 and camera > > configuration, associate it with appropriate devices and allocate memory for > > software nodes needed to create a DT-like data structure for drivers. > > It occurred to me, that there would be so many things I would like to change > in this patch, so it would be better to create my own version of it, which > is appended. > > It is based on > > https://patchwork.kernel.org/project/linux-acpi/patch/2694293.mvXUDI8C0e@kreacher/ > > that has just been posted. > > IIUC, the idea is to extract the ACPI handle for each resource source in every > _CRS CSI-2 resource descriptor and count how many times each handle appears in > a CSI-2 context, either because it is referenced from a _CRS CSI-2 resource > descriptor (as a "resource source"), or because its device object has CSI-2 > resource descriptors in _CRS. Correct. > > This allows a set of software nodes to be allocated for each of these handles. > > If I got that totally wrong, please let me know. Otherwise, I will rework the > remaining patches in the series to match this one. It seems about right. I mostly see renames, moving the code around, using the existing dependency list and then parsing sub-tree for _CRS CSI-2 objects right from the bus scan callback. It seems you've also moved the structs from internal.h to what is now called mipi-disco-imaging.c. They'll be later needed in e.g. scan.c. At least I'd use names that indicate they're related to scanning the bus: they're not needed after this is done. I don't have objections to you reworking the rest, but given the number of non-trivial changes, will it work after this? :-) I can also do this, although I would un-do some of the changes in this patch in order to prepare for the rest (such as moving the structs from internal.h). See e.g. "ACPI: scan: Generate software nodes based on MIPI DisCo for Imaging", I think it's the 6th patch. ... > +/** > + * acpi_mipi_scan_crs_csi2 - Allocate 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; > + > + list_for_each_entry(conn, &csi2->connections, entry) { > + struct crs_csi2 *remote_csi2; > + acpi_status status; > + > + status = acpi_get_data_full(conn->remote_handle, > + acpi_mipi_data_tag, > + (void **)&remote_csi2, > + NULL); > + if (ACPI_SUCCESS(status) && remote_csi2) { > + remote_csi2->port_count++; > + continue; > + } > + > + /* > + * The "remote" device has no _CRS CSI-2 list entry yet, > + * so create one for it and add it to the list. > + */ > + remote_csi2 = create_crs_csi2_entry(conn->remote_handle); > + if (!remote_csi2) > + continue; > + > + list_add(&remote_csi2->entry, &aux_list); > + } > + } > + list_splice(&aux_list, &acpi_mipi_crs_csi2_list); > + > + /* Allocate softwware nodes for representing the CSI-2 information. */ "software" > + list_for_each_entry(csi2, &acpi_mipi_crs_csi2_list, entry) > + alloc_fill_crs_csi2_swnodes(csi2->handle, csi2->port_count); > + > + acpi_mipi_crs_csi2_release(); > +}
Hi Sakari, On Tue, May 16, 2023 at 10:57 AM Sakari Ailus <sakari.ailus@linux.intel.com> wrote: > > Hi Rafael, > > On Mon, May 15, 2023 at 06:45:10PM +0200, Rafael J. Wysocki wrote: > > On Wednesday, March 29, 2023 12:09:44 PM CEST Sakari Ailus wrote: > > > Parse newly added ACPI _CRS CSI-2 descriptor for CSI-2 and camera > > > configuration, associate it with appropriate devices and allocate memory for > > > software nodes needed to create a DT-like data structure for drivers. > > > > It occurred to me, that there would be so many things I would like to change > > in this patch, so it would be better to create my own version of it, which > > is appended. > > > > It is based on > > > > https://patchwork.kernel.org/project/linux-acpi/patch/2694293.mvXUDI8C0e@kreacher/ > > > > that has just been posted. > > > > IIUC, the idea is to extract the ACPI handle for each resource source in every > > _CRS CSI-2 resource descriptor and count how many times each handle appears in > > a CSI-2 context, either because it is referenced from a _CRS CSI-2 resource > > descriptor (as a "resource source"), or because its device object has CSI-2 > > resource descriptors in _CRS. > > Correct. > > > > > This allows a set of software nodes to be allocated for each of these handles. > > > > If I got that totally wrong, please let me know. Otherwise, I will rework the > > remaining patches in the series to match this one. > > It seems about right. I mostly see renames, moving the code around, > using the existing dependency list and then parsing sub-tree for _CRS CSI-2 > objects right from the bus scan callback. > > It seems you've also moved the structs from internal.h to what is now > called mipi-disco-imaging.c. No, I haven't moved anything in this direction, I've just dropped them. They can be added in the next patches if needed. > They'll be later needed in e.g. scan.c. At > least I'd use names that indicate they're related to scanning the bus: > they're not needed after this is done. > > I don't have objections to you reworking the rest, but given the number of > non-trivial changes, will it work after this? :-) Probably not right from the start, but after some minor adjustments it should work, unless I've missed something significant. > I can also do this, although I would un-do some of the changes in this patch in order to > prepare for the rest (such as moving the structs from internal.h). > > See e.g. "ACPI: scan: Generate software nodes based on MIPI DisCo for > Imaging", I think it's the 6th patch. I will. Thanks!
Hi Rafael, On Tue, May 16, 2023 at 12:12:13PM +0200, Rafael J. Wysocki wrote: > Hi Sakari, > > On Tue, May 16, 2023 at 10:57 AM Sakari Ailus > <sakari.ailus@linux.intel.com> wrote: > > > > Hi Rafael, > > > > On Mon, May 15, 2023 at 06:45:10PM +0200, Rafael J. Wysocki wrote: > > > On Wednesday, March 29, 2023 12:09:44 PM CEST Sakari Ailus wrote: > > > > Parse newly added ACPI _CRS CSI-2 descriptor for CSI-2 and camera > > > > configuration, associate it with appropriate devices and allocate memory for > > > > software nodes needed to create a DT-like data structure for drivers. > > > > > > It occurred to me, that there would be so many things I would like to change > > > in this patch, so it would be better to create my own version of it, which > > > is appended. > > > > > > It is based on > > > > > > https://patchwork.kernel.org/project/linux-acpi/patch/2694293.mvXUDI8C0e@kreacher/ > > > > > > that has just been posted. > > > > > > IIUC, the idea is to extract the ACPI handle for each resource source in every > > > _CRS CSI-2 resource descriptor and count how many times each handle appears in > > > a CSI-2 context, either because it is referenced from a _CRS CSI-2 resource > > > descriptor (as a "resource source"), or because its device object has CSI-2 > > > resource descriptors in _CRS. > > > > Correct. > > > > > > > > This allows a set of software nodes to be allocated for each of these handles. > > > > > > If I got that totally wrong, please let me know. Otherwise, I will rework the > > > remaining patches in the series to match this one. > > > > It seems about right. I mostly see renames, moving the code around, > > using the existing dependency list and then parsing sub-tree for _CRS CSI-2 > > objects right from the bus scan callback. > > > > It seems you've also moved the structs from internal.h to what is now > > called mipi-disco-imaging.c. > > No, I haven't moved anything in this direction, I've just dropped them. Ah, I think the struct was added to scan.c by the earlier patch. Indeed, by doing the _CRS CSI2 scanning right from the device scanning callback, it's probably possible to omit these.
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index a5b649e71ab1..a98fa1bc1554 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -37,7 +37,7 @@ acpi-$(CONFIG_ACPI_SLEEP) += proc.o # ACPI Bus and Device Drivers # acpi-y += bus.o glue.o -acpi-y += scan.o +acpi-y += scan.o mipi.o acpi-y += resource.o acpi-y += acpi_processor.o acpi-y += processor_core.o diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 06ad497067ac..aa5f9c69dbbe 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -102,6 +102,24 @@ struct acpi_device_bus_id { struct list_head node; }; +/* Context for scanning ACPI device nodes for _CRS CSI2 resource descriptors */ +struct acpi_scan_context_csi2 { + struct list_head crs_csi2_head; + size_t handle_count; +}; + +/** + * struct acpi_scan_context - Context for scanning ACPI devices + * @postponed_head: The list head of the postponed ACPI handles + * @device: The first encountered device, typically the root of the scanned tree + * @csi2: Context for scanning _CRS CSI2 resource descriptors + */ +struct acpi_scan_context { + struct list_head postponed_head; + struct acpi_device *device; + struct acpi_scan_context_csi2 csi2; +}; + void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, int type, void (*release)(struct device *)); int acpi_tie_acpi_dev(struct acpi_device *adev); @@ -282,4 +300,13 @@ void acpi_init_lpit(void); static inline void acpi_init_lpit(void) { } #endif +/*-------------------------------------------------------------------------- + ACPI _CRS CSI2 and MIPI DisCo for Imaging conversion + -------------------------------------------------------------------------- */ + +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); + #endif /* _ACPI_INTERNAL_H_ */ diff --git a/drivers/acpi/mipi.c b/drivers/acpi/mipi.c new file mode 100644 index 000000000000..d719c879eb83 --- /dev/null +++ b/drivers/acpi/mipi.c @@ -0,0 +1,343 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * MIPI DisCo for Imaging support. + * + * Support MIPI DisCo for Imaging by parsing ACPI _CRS CSI2 records and DisCo + * for Imaging data structures. + * + * Also see <URL:https://www.mipi.org/specifications/mipi-disco-imaging>. + * + * Copyright (C) 2023 Intel Corporation + */ + +#include <linux/acpi.h> +#include <linux/limits.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/overflow.h> +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/sort.h> +#include <linux/string.h> + +#include "internal.h" + +/* Temporary ACPI device handle to software node data structure mapping */ +struct crs_csi2_swnodes { + struct list_head list; + acpi_handle handle; + struct acpi_device_software_nodes *ads; +}; + +static LIST_HEAD(crs_csi2_swnodes); + +static void crs_csi2_swnode_del_free(struct crs_csi2_swnodes *swnodes) +{ + list_del(&swnodes->list); + kfree(swnodes); +} + +void acpi_crs_csi2_swnodes_del_free(void) +{ + struct crs_csi2_swnodes *swnodes, *swnodes_tmp; + + list_for_each_entry_safe(swnodes, swnodes_tmp, &crs_csi2_swnodes, list) + crs_csi2_swnode_del_free(swnodes); +} + +/* + * Description of a _CRS CSI2 resource descriptor before software node creation + */ +struct crs_csi2_instance { + struct list_head list; + struct acpi_resource_csi2_serialbus csi2; + acpi_handle remote_handle; + char remote_name[]; +}; + +/* Temporary list of ACPI device nodes with _CRS CSI2 resource descriptors */ +struct crs_csi2 { + struct list_head list; + acpi_handle handle; + struct list_head buses; +}; + +/* + * Context for collecting _CRS CSI2 resource descriptors during ACPI namespace + * walk + */ +struct scan_check_crs_csi2_context { + struct list_head res_head; + acpi_handle handle; + size_t handle_count; +}; + +/* Scan a single CSI2 resource descriptor in _CRS. */ +static acpi_status scan_check_crs_csi2_instance(struct acpi_resource *res, + void *context) +{ + struct scan_check_crs_csi2_context *inst_context = context; + struct acpi_resource_csi2_serialbus *csi2; + struct crs_csi2_instance *inst; + acpi_handle remote_handle; + + if (res->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) + return AE_OK; + + csi2 = &res->data.csi2_serial_bus; + + if (csi2->type != ACPI_RESOURCE_SERIAL_TYPE_CSI2) + return AE_OK; + + if (!csi2->resource_source.string_length) { + acpi_handle_debug(inst_context->handle, + "invalid resource source string length\n"); + return AE_OK; + } + + if (ACPI_FAILURE(acpi_get_handle(NULL, csi2->resource_source.string_ptr, + &remote_handle))) { + acpi_handle_warn(inst_context->handle, + "cannot get handle for %s\n", + csi2->resource_source.string_ptr); + return AE_OK; + } + + /* + * Allocate space to store the _CRS CSI2 entry and its data and add it + * to the list. + */ + inst = kmalloc(struct_size(inst, remote_name, csi2->resource_source.string_length), + GFP_KERNEL); + if (!inst) + return AE_OK; + + inst->csi2 = *csi2; + strscpy(inst->remote_name, csi2->resource_source.string_ptr, + csi2->resource_source.string_length); + inst->csi2.resource_source.string_ptr = inst->remote_name; + inst->remote_handle = remote_handle; + + list_add(&inst->list, &inst_context->res_head); + + inst_context->handle_count++; + + return AE_OK; +} + +/* + * Find all devices with _CRS CSI2 resource descriptors and collect them + * into a list for later use. + */ +void acpi_bus_scan_check_crs_csi2(acpi_handle handle, struct acpi_scan_context *ctx) +{ + struct scan_check_crs_csi2_context inst_context = { + .handle = handle, + .res_head = LIST_HEAD_INIT(inst_context.res_head), + }; + struct crs_csi2 *csi2; + + acpi_walk_resources(handle, METHOD_NAME__CRS, + scan_check_crs_csi2_instance, &inst_context); + + if (list_empty(&inst_context.res_head)) + return; + + /* + * Found entry, so allocate memory for it, fill it and add it to the + * list. + */ + csi2 = kmalloc(sizeof(*csi2), GFP_KERNEL); + if (!csi2) + return; /* There's nothing we really can do about this. */ + + csi2->handle = handle; + list_replace(&inst_context.res_head, &csi2->buses); + list_add(&csi2->list, &ctx->csi2.crs_csi2_head); + + /* This handle plus remote handles in _CRS CSI2 resource descriptors */ + ctx->csi2.handle_count += 1 + inst_context.handle_count; +} + +struct acpi_handle_ref { + acpi_handle handle; + unsigned int count; +}; + +#define NO_CSI2_PORT (UINT_MAX - 1) + +static int crs_handle_cmp(const void *__a, const void *__b) +{ + const struct acpi_handle_ref *a = __a, *b = __b; + + return a->handle < b->handle ? -1 : a->handle > b->handle; +} + +/* + * Release entries in temporary storage of ACPI device nodes with _CRS CSI2 + * resource descriptors. + */ +void acpi_bus_scan_crs_csi2_release(struct list_head *crs_csi2_handles) +{ + struct crs_csi2 *csi2, *csi2_tmp; + + list_for_each_entry_safe(csi2, csi2_tmp, crs_csi2_handles, list) { + struct crs_csi2_instance *inst, *inst_tmp; + + list_for_each_entry_safe(inst, inst_tmp, &csi2->buses, list) { + list_del(&inst->list); + kfree(inst); + } + + list_del(&csi2->list); + kfree(csi2); + } +} + +/* + * Allocate memory and set up software nodes for an ACPI device with given + * number of CSI-2 ports. + */ +static void acpi_crs_csi2_alloc_fill_swnodes(size_t ports_count, acpi_handle handle) +{ + struct acpi_device_software_nodes *ads; + struct crs_csi2_swnodes *swnodes; + size_t alloc_size; + unsigned int i; + bool overflow; + void *end; + + /* + * Allocate memory for ports, node pointers (number of nodes + + * 1 (guardian), nodes (root + number of ports * 2 (because for + * every port there is an endpoint)). + */ + overflow = check_mul_overflow(sizeof(*ads->ports) + + sizeof(*ads->nodes) * 2 + + sizeof(*ads->nodeptrs) * 2, + ports_count, &alloc_size); + overflow = overflow || + check_add_overflow(sizeof(*ads) + sizeof(*ads->nodes) + + sizeof(*ads->nodeptrs) * 2, + alloc_size, &alloc_size); + if (overflow) { + acpi_handle_warn(handle, + "too many _CRS CSI2 resource handles (%zu)", + ports_count); + return; + } + + swnodes = kzalloc(sizeof(*swnodes), GFP_KERNEL); + ads = kmalloc(alloc_size, GFP_KERNEL); + if (!swnodes || !ads) + goto out_free; + + ads->ports = (void *)(ads + 1); + ads->nodes = (void *)(ads->ports + ports_count); + ads->nodeptrs = (void *)(ads->nodes + ports_count * 2 + 1); + end = ads->nodeptrs + ports_count * 2 + 2; + if (WARN_ON((void *)ads + alloc_size != end)) + goto out_free; + + ads->num_ports = ports_count; + for (i = 0; i < ports_count * 2 + 1; i++) + ads->nodeptrs[i] = &ads->nodes[i]; + ads->nodeptrs[i] = NULL; + for (i = 0; i < ports_count; i++) + ads->ports[i].port_nr = NO_CSI2_PORT; + swnodes->handle = handle; + swnodes->ads = ads; + list_add(&swnodes->list, &crs_csi2_swnodes); + + return; + +out_free: + kfree(swnodes); + kfree(ads); + acpi_handle_debug(handle, "cannot allocate for %zu software nodes\n", + ports_count); +} + +/** + * acpi_bus_scan_crs_csi2 - Construct software nodes out of ACPI _CRS CSI2 + * resource descriptors + * @ctx: ACPI _CRS CSI2 context, gathered during tree walk earlier + * + * This function does a number of things: + * + * 1. Count how many references to other devices _CRS CSI-2 instances have in + * total. + * + * 2. Count the number of references to other devices for each _CRS CSI-2 + * instance. + * + * 3. Allocate memory for swnodes each ACPI device requires later on, and + * generate a list of such allocations. + * + * 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. + */ +void acpi_bus_scan_crs_csi2(struct acpi_scan_context_csi2 *ctx) +{ + struct acpi_handle_ref *handle_refs; + struct acpi_handle_ref *this = NULL; + size_t this_count; + unsigned int curr = 0; + struct crs_csi2 *csi2; + + /* No handles? Bail out here. */ + if (!ctx->handle_count) + return; + + handle_refs = kcalloc(ctx->handle_count + 1, sizeof(*handle_refs), + GFP_KERNEL); + if (!handle_refs) { + pr_debug("no memory for %zu handle refs\n", + ctx->handle_count + 1); + return; + } + + /* Associate handles to the number of references. */ + list_for_each_entry(csi2, &ctx->crs_csi2_head, list) { + struct crs_csi2_instance *inst; + struct acpi_handle_ref *handle_ref; + + handle_ref = &handle_refs[curr++]; + handle_ref->handle = csi2->handle; + + list_for_each_entry(inst, &csi2->buses, list) { + handle_refs[curr].handle = inst->remote_handle; + handle_refs[curr].count = 1; + handle_ref->count++; + curr++; + } + } + + handle_refs[curr].handle = NULL; + + /* Sort the handles by remote so duplicates canbe easily found. */ + sort(handle_refs, ctx->handle_count, sizeof(*handle_refs), crs_handle_cmp, NULL); + + /* + * Finally count references in each handle, allocate space for device + * specific ports, properties and fill the _CRS CSI2 descriptor + * originated data. + */ + this = handle_refs; + this_count = this->count; + for (curr = 1; curr < ctx->handle_count + 1; curr++) { + /* Weed out duplicate receiver devices. */ + if (this->handle == handle_refs[curr].handle) { + this_count += handle_refs[curr].count; + continue; + } + + acpi_crs_csi2_alloc_fill_swnodes(this_count, this->handle); + + this = &handle_refs[curr]; + this_count = this->count; + } + + kfree(handle_refs); +} diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 280d12e0aa2f..ddf7701a57d0 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2042,16 +2042,6 @@ struct acpi_postponed_handle { acpi_handle handle; }; -/** - * struct acpi_scan_context - Context for scanning ACPI devices - * @postponed_head: The list head of the postponed ACPI handles - * @device: The first encountered device, typically the root of the scanned tree - */ -struct acpi_scan_context { - struct list_head postponed_head; - struct acpi_device *device; -}; - /** * acpi_bus_handle_postpone - Add an ACPI handle to a given postponed list * @handle: The ACPI handle @@ -2073,7 +2063,7 @@ static void acpi_bus_handle_postpone(acpi_handle handle, list_add(&ph->list, head); } -static acpi_status acpi_bus_check_add(acpi_handle handle, bool check_dep, +static acpi_status acpi_bus_check_add(acpi_handle handle, bool first_pass, struct acpi_scan_context *ctx) { struct acpi_device *device = acpi_fetch_acpi_dev(handle); @@ -2091,8 +2081,11 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, bool check_dep, if (acpi_device_should_be_hidden(handle)) return AE_OK; + if (first_pass) + acpi_bus_scan_check_crs_csi2(handle, ctx); + /* Bail out if there are dependencies. */ - if (acpi_scan_check_dep(handle, check_dep) > 0) { + if (acpi_scan_check_dep(handle, first_pass) > 0) { acpi_bus_handle_postpone(handle, &ctx->postponed_head); return AE_CTRL_DEPTH; } @@ -2118,10 +2111,10 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, bool check_dep, } /* - * If check_dep is true at this point, the device has no dependencies, + * If first_pass is true at this point, the device has no dependencies, * or the creation of the device object would have been postponed above. */ - acpi_add_single_object(&device, handle, type, !check_dep); + acpi_add_single_object(&device, handle, type, !first_pass); if (!device) return AE_CTRL_DEPTH; @@ -2146,6 +2139,14 @@ static acpi_status acpi_bus_check_add_2(acpi_handle handle, u32 lvl_not_used, return acpi_bus_check_add(handle, false, (struct acpi_scan_context *)ctx); } +static acpi_status acpi_bus_check_csi2(acpi_handle handle, u32 lvl_not_used, + void *ctx, void **unused) +{ + acpi_bus_scan_check_crs_csi2(handle, ctx); + + return AE_OK; +} + static void acpi_default_enumeration(struct acpi_device *device) { /* @@ -2466,6 +2467,7 @@ int acpi_bus_scan(acpi_handle handle) { struct acpi_scan_context ctx = { .postponed_head = LIST_HEAD_INIT(ctx.postponed_head), + .csi2.crs_csi2_head = LIST_HEAD_INIT(ctx.csi2.crs_csi2_head), }; struct acpi_postponed_handle *ph, *tmp_ph; int ret = 0; @@ -2480,6 +2482,24 @@ int acpi_bus_scan(acpi_handle handle) goto out_release; } + /* + * Check _CRS CSI2 resource descriptors for devices that were not + * traversed during the previous pass. + */ + list_for_each_entry_safe(ph, tmp_ph, &ctx.postponed_head, list) { + acpi_bus_check_csi2(ph->handle, 0, &ctx, NULL); + acpi_walk_namespace(ACPI_TYPE_DEVICE, ph->handle, ACPI_UINT32_MAX, + acpi_bus_check_csi2, NULL, (void *)&ctx, + NULL); + } + + /* + * Construct software nodes out of _CRS CSI2 records and release the + * _CRS CSI2 records. + */ + acpi_bus_scan_crs_csi2(&ctx.csi2); + acpi_bus_scan_crs_csi2_release(&ctx.csi2.crs_csi2_head); + acpi_bus_attach(ctx.device, (void *)true); /* @@ -2511,6 +2531,8 @@ int acpi_bus_scan(acpi_handle handle) kfree(ph); } + acpi_crs_csi2_swnodes_del_free(); + return ret; } EXPORT_SYMBOL(acpi_bus_scan); diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 57acb895c038..e4e8faac9212 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -360,6 +360,17 @@ struct acpi_device_data { struct acpi_gpio_mapping; +struct acpi_device_software_node_port { + unsigned int port_nr; +}; + +struct acpi_device_software_nodes { + struct acpi_device_software_node_port *ports; + struct software_node *nodes; + const struct software_node **nodeptrs; + unsigned int num_ports; +}; + /* Device */ struct acpi_device { u32 pld_crc;
Parse newly added ACPI _CRS CSI-2 descriptor for CSI-2 and camera configuration, associate it with appropriate devices and allocate memory for software nodes needed to create a DT-like data structure for drivers. Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> --- drivers/acpi/Makefile | 2 +- drivers/acpi/internal.h | 27 ++++ drivers/acpi/mipi.c | 343 ++++++++++++++++++++++++++++++++++++++++ drivers/acpi/scan.c | 50 ++++-- include/acpi/acpi_bus.h | 11 ++ 5 files changed, 418 insertions(+), 15 deletions(-) create mode 100644 drivers/acpi/mipi.c