Skip to content

Commit bd721b9

Browse files
committed
ACPI: scan: Extract CSI-2 connection graph from _CRS
Find ACPI CSI-2 resource descriptors defined since ACPI 6.4 (for CSI-2 and camera configuration) in _CRS for all device objects in the given scope of the ACPI namespace that have them, identify the corresponding "remote endpoint" device objects for them and allocate memory for software nodes needed to create a DT-like data structure representing the CSI-2 connection graph for drivers. The code needed to populate these software nodes will be added by subsequent change sets. Link: https://uefi.org/specs/ACPI/6.5/06_Device_Configuration.html#camera-serial-interface-csi-2-connection-resource-descriptor 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> Tested-by: Sakari Ailus <sakari.ailus@linux.intel.com>
1 parent 9880702 commit bd721b9

File tree

5 files changed

+358
-10
lines changed

5 files changed

+358
-10
lines changed

drivers/acpi/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ acpi-$(CONFIG_ACPI_SLEEP) += proc.o
3737
# ACPI Bus and Device Drivers
3838
#
3939
acpi-y += bus.o glue.o
40-
acpi-y += scan.o
40+
acpi-y += scan.o mipi-disco-img.o
4141
acpi-y += resource.o
4242
acpi-y += acpi_processor.o
4343
acpi-y += processor_core.o

drivers/acpi/internal.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,4 +276,12 @@ void acpi_init_lpit(void);
276276
static inline void acpi_init_lpit(void) { }
277277
#endif
278278

279+
/*--------------------------------------------------------------------------
280+
ACPI _CRS CSI-2 and MIPI DisCo for Imaging
281+
-------------------------------------------------------------------------- */
282+
283+
void acpi_mipi_check_crs_csi2(acpi_handle handle);
284+
void acpi_mipi_scan_crs_csi2(void);
285+
void acpi_mipi_crs_csi2_cleanup(void);
286+
279287
#endif /* _ACPI_INTERNAL_H_ */

drivers/acpi/mipi-disco-img.c

Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* MIPI DisCo for Imaging support.
4+
*
5+
* Copyright (C) 2023 Intel Corporation
6+
*
7+
* Support MIPI DisCo for Imaging by parsing ACPI _CRS CSI-2 records defined in
8+
* Section 6.4.3.8.2.4 "Camera Serial Interface (CSI-2) Connection Resource
9+
* Descriptor" of ACPI 6.5.
10+
*
11+
* The implementation looks for the information in the ACPI namespace (CSI-2
12+
* resource descriptors in _CRS) and constructs software nodes compatible with
13+
* Documentation/firmware-guide/acpi/dsd/graph.rst to represent the CSI-2
14+
* connection graph.
15+
*/
16+
17+
#include <linux/acpi.h>
18+
#include <linux/limits.h>
19+
#include <linux/list.h>
20+
#include <linux/module.h>
21+
#include <linux/overflow.h>
22+
#include <linux/types.h>
23+
#include <linux/slab.h>
24+
#include <linux/string.h>
25+
26+
#include "internal.h"
27+
28+
static LIST_HEAD(acpi_mipi_crs_csi2_list);
29+
30+
static void acpi_mipi_data_tag(acpi_handle handle, void *context)
31+
{
32+
}
33+
34+
/* Connection data extracted from one _CRS CSI-2 resource descriptor. */
35+
struct crs_csi2_connection {
36+
struct list_head entry;
37+
struct acpi_resource_csi2_serialbus csi2_data;
38+
acpi_handle remote_handle;
39+
char remote_name[];
40+
};
41+
42+
/* Data extracted from _CRS CSI-2 resource descriptors for one device. */
43+
struct crs_csi2 {
44+
struct list_head entry;
45+
acpi_handle handle;
46+
struct acpi_device_software_nodes *swnodes;
47+
struct list_head connections;
48+
u32 port_count;
49+
};
50+
51+
struct csi2_resources_walk_data {
52+
acpi_handle handle;
53+
struct list_head connections;
54+
};
55+
56+
static acpi_status parse_csi2_resource(struct acpi_resource *res, void *context)
57+
{
58+
struct csi2_resources_walk_data *crwd = context;
59+
struct acpi_resource_csi2_serialbus *csi2_res;
60+
struct acpi_resource_source *csi2_res_src;
61+
u16 csi2_res_src_length;
62+
struct crs_csi2_connection *conn;
63+
acpi_handle remote_handle;
64+
65+
if (res->type != ACPI_RESOURCE_TYPE_SERIAL_BUS)
66+
return AE_OK;
67+
68+
csi2_res = &res->data.csi2_serial_bus;
69+
70+
if (csi2_res->type != ACPI_RESOURCE_SERIAL_TYPE_CSI2)
71+
return AE_OK;
72+
73+
csi2_res_src = &csi2_res->resource_source;
74+
if (ACPI_FAILURE(acpi_get_handle(NULL, csi2_res_src->string_ptr,
75+
&remote_handle))) {
76+
acpi_handle_debug(crwd->handle,
77+
"unable to find resource source\n");
78+
return AE_OK;
79+
}
80+
csi2_res_src_length = csi2_res_src->string_length;
81+
if (!csi2_res_src_length) {
82+
acpi_handle_debug(crwd->handle,
83+
"invalid resource source string length\n");
84+
return AE_OK;
85+
}
86+
87+
conn = kmalloc(struct_size(conn, remote_name, csi2_res_src_length + 1),
88+
GFP_KERNEL);
89+
if (!conn)
90+
return AE_OK;
91+
92+
conn->csi2_data = *csi2_res;
93+
strscpy(conn->remote_name, csi2_res_src->string_ptr, csi2_res_src_length);
94+
conn->csi2_data.resource_source.string_ptr = conn->remote_name;
95+
conn->remote_handle = remote_handle;
96+
97+
list_add(&conn->entry, &crwd->connections);
98+
99+
return AE_OK;
100+
}
101+
102+
static struct crs_csi2 *acpi_mipi_add_crs_csi2(acpi_handle handle,
103+
struct list_head *list)
104+
{
105+
struct crs_csi2 *csi2;
106+
107+
csi2 = kzalloc(sizeof(*csi2), GFP_KERNEL);
108+
if (!csi2)
109+
return NULL;
110+
111+
csi2->handle = handle;
112+
INIT_LIST_HEAD(&csi2->connections);
113+
csi2->port_count = 1;
114+
115+
if (ACPI_FAILURE(acpi_attach_data(handle, acpi_mipi_data_tag, csi2))) {
116+
kfree(csi2);
117+
return NULL;
118+
}
119+
120+
list_add(&csi2->entry, list);
121+
122+
return csi2;
123+
}
124+
125+
static struct crs_csi2 *acpi_mipi_get_crs_csi2(acpi_handle handle)
126+
{
127+
struct crs_csi2 *csi2;
128+
129+
if (ACPI_FAILURE(acpi_get_data_full(handle, acpi_mipi_data_tag,
130+
(void **)&csi2, NULL)))
131+
return NULL;
132+
133+
return csi2;
134+
}
135+
136+
static void csi_csr2_release_connections(struct list_head *list)
137+
{
138+
struct crs_csi2_connection *conn, *conn_tmp;
139+
140+
list_for_each_entry_safe(conn, conn_tmp, list, entry) {
141+
list_del(&conn->entry);
142+
kfree(conn);
143+
}
144+
}
145+
146+
static void acpi_mipi_del_crs_csi2(struct crs_csi2 *csi2)
147+
{
148+
list_del(&csi2->entry);
149+
acpi_detach_data(csi2->handle, acpi_mipi_data_tag);
150+
kfree(csi2->swnodes);
151+
csi_csr2_release_connections(&csi2->connections);
152+
kfree(csi2);
153+
}
154+
155+
/**
156+
* acpi_mipi_check_crs_csi2 - Look for CSI-2 resources in _CRS
157+
* @handle: Device object handle to evaluate _CRS for.
158+
*
159+
* Find all CSI-2 resource descriptors in the given device's _CRS
160+
* and collect them into a list.
161+
*/
162+
void acpi_mipi_check_crs_csi2(acpi_handle handle)
163+
{
164+
struct csi2_resources_walk_data crwd = {
165+
.handle = handle,
166+
.connections = LIST_HEAD_INIT(crwd.connections),
167+
};
168+
struct crs_csi2 *csi2;
169+
170+
/*
171+
* Avoid allocating _CRS CSI-2 objects for devices without any CSI-2
172+
* resource descriptions in _CRS to reduce overhead.
173+
*/
174+
acpi_walk_resources(handle, METHOD_NAME__CRS, parse_csi2_resource, &crwd);
175+
if (list_empty(&crwd.connections))
176+
return;
177+
178+
/*
179+
* Create a _CRS CSI-2 entry to store the extracted connection
180+
* information and add it to the global list.
181+
*/
182+
csi2 = acpi_mipi_add_crs_csi2(handle, &acpi_mipi_crs_csi2_list);
183+
if (!csi2) {
184+
csi_csr2_release_connections(&crwd.connections);
185+
return; /* Nothing really can be done about this. */
186+
}
187+
188+
list_replace(&crwd.connections, &csi2->connections);
189+
}
190+
191+
#define NO_CSI2_PORT (UINT_MAX - 1)
192+
193+
static void alloc_crs_csi2_swnodes(struct crs_csi2 *csi2)
194+
{
195+
size_t port_count = csi2->port_count;
196+
struct acpi_device_software_nodes *swnodes;
197+
size_t alloc_size;
198+
unsigned int i;
199+
200+
/*
201+
* Allocate memory for ports, node pointers (number of nodes +
202+
* 1 (guardian), nodes (root + number of ports * 2 (because for
203+
* every port there is an endpoint)).
204+
*/
205+
if (check_mul_overflow(sizeof(*swnodes->ports) +
206+
sizeof(*swnodes->nodes) * 2 +
207+
sizeof(*swnodes->nodeptrs) * 2,
208+
port_count, &alloc_size) ||
209+
check_add_overflow(sizeof(*swnodes) +
210+
sizeof(*swnodes->nodes) +
211+
sizeof(*swnodes->nodeptrs) * 2,
212+
alloc_size, &alloc_size)) {
213+
acpi_handle_info(csi2->handle,
214+
"too many _CRS CSI-2 resource handles (%zu)",
215+
port_count);
216+
return;
217+
}
218+
219+
swnodes = kmalloc(alloc_size, GFP_KERNEL);
220+
if (!swnodes)
221+
return;
222+
223+
swnodes->ports = (struct acpi_device_software_node_port *)(swnodes + 1);
224+
swnodes->nodes = (struct software_node *)(swnodes->ports + port_count);
225+
swnodes->nodeptrs = (const struct software_node **)(swnodes->nodes + 1 +
226+
2 * port_count);
227+
swnodes->num_ports = port_count;
228+
229+
for (i = 0; i < 2 * port_count + 1; i++)
230+
swnodes->nodeptrs[i] = &swnodes->nodes[i];
231+
232+
swnodes->nodeptrs[i] = NULL;
233+
234+
for (i = 0; i < port_count; i++)
235+
swnodes->ports[i].port_nr = NO_CSI2_PORT;
236+
237+
csi2->swnodes = swnodes;
238+
}
239+
240+
/**
241+
* acpi_mipi_scan_crs_csi2 - Create ACPI _CRS CSI-2 software nodes
242+
*
243+
* Note that this function must be called before any struct acpi_device objects
244+
* are bound to any ACPI drivers or scan handlers, so it cannot assume the
245+
* existence of struct acpi_device objects for every device present in the ACPI
246+
* namespace.
247+
*
248+
* acpi_scan_lock in scan.c must be held when calling this function.
249+
*/
250+
void acpi_mipi_scan_crs_csi2(void)
251+
{
252+
struct crs_csi2 *csi2;
253+
LIST_HEAD(aux_list);
254+
255+
/* Count references to each ACPI handle in the CSI-2 connection graph. */
256+
list_for_each_entry(csi2, &acpi_mipi_crs_csi2_list, entry) {
257+
struct crs_csi2_connection *conn;
258+
259+
list_for_each_entry(conn, &csi2->connections, entry) {
260+
struct crs_csi2 *remote_csi2;
261+
262+
csi2->port_count++;
263+
264+
remote_csi2 = acpi_mipi_get_crs_csi2(conn->remote_handle);
265+
if (remote_csi2) {
266+
remote_csi2->port_count++;
267+
continue;
268+
}
269+
/*
270+
* The remote endpoint has no _CRS CSI-2 list entry yet,
271+
* so create one for it and add it to the list.
272+
*/
273+
acpi_mipi_add_crs_csi2(conn->remote_handle, &aux_list);
274+
}
275+
}
276+
list_splice(&aux_list, &acpi_mipi_crs_csi2_list);
277+
278+
/* Allocate software nodes for representing the CSI-2 information. */
279+
list_for_each_entry(csi2, &acpi_mipi_crs_csi2_list, entry)
280+
alloc_crs_csi2_swnodes(csi2);
281+
}
282+
283+
/**
284+
* acpi_mipi_crs_csi2_cleanup - Free _CRS CSI-2 temporary data
285+
*/
286+
void acpi_mipi_crs_csi2_cleanup(void)
287+
{
288+
struct crs_csi2 *csi2, *csi2_tmp;
289+
290+
list_for_each_entry_safe(csi2, csi2_tmp, &acpi_mipi_crs_csi2_list, entry)
291+
acpi_mipi_del_crs_csi2(csi2);
292+
}

0 commit comments

Comments
 (0)