xref: /linux/drivers/acpi/mipi-disco-img.c (revision bd721b93)
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 }
293