Commit | Line | Data |
---|---|---|
bd721b93 RW |
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 | |
a6cb0a61 RW |
9 | * Descriptor" of ACPI 6.5 and using device properties defined by the MIPI DisCo |
10 | * for Imaging specification. | |
bd721b93 RW |
11 | * |
12 | * The implementation looks for the information in the ACPI namespace (CSI-2 | |
13 | * resource descriptors in _CRS) and constructs software nodes compatible with | |
14 | * Documentation/firmware-guide/acpi/dsd/graph.rst to represent the CSI-2 | |
a6cb0a61 RW |
15 | * connection graph. The software nodes are then populated with the data |
16 | * extracted from the _CRS CSI-2 resource descriptors and the MIPI DisCo | |
17 | * for Imaging device properties present in _DSD for the ACPI device objects | |
18 | * with CSI-2 connections. | |
bd721b93 RW |
19 | */ |
20 | ||
21 | #include <linux/acpi.h> | |
22 | #include <linux/limits.h> | |
23 | #include <linux/list.h> | |
24 | #include <linux/module.h> | |
25 | #include <linux/overflow.h> | |
26 | #include <linux/types.h> | |
27 | #include <linux/slab.h> | |
28 | #include <linux/string.h> | |
29 | ||
693c667b RW |
30 | #include <media/v4l2-fwnode.h> |
31 | ||
bd721b93 RW |
32 | #include "internal.h" |
33 | ||
34 | static LIST_HEAD(acpi_mipi_crs_csi2_list); | |
35 | ||
36 | static void acpi_mipi_data_tag(acpi_handle handle, void *context) | |
37 | { | |
38 | } | |
39 | ||
40 | /* Connection data extracted from one _CRS CSI-2 resource descriptor. */ | |
41 | struct crs_csi2_connection { | |
42 | struct list_head entry; | |
43 | struct acpi_resource_csi2_serialbus csi2_data; | |
44 | acpi_handle remote_handle; | |
45 | char remote_name[]; | |
46 | }; | |
47 | ||
48 | /* Data extracted from _CRS CSI-2 resource descriptors for one device. */ | |
49 | struct crs_csi2 { | |
50 | struct list_head entry; | |
51 | acpi_handle handle; | |
52 | struct acpi_device_software_nodes *swnodes; | |
53 | struct list_head connections; | |
54 | u32 port_count; | |
55 | }; | |
56 | ||
57 | struct csi2_resources_walk_data { | |
58 | acpi_handle handle; | |
59 | struct list_head connections; | |
60 | }; | |
61 | ||
62 | static acpi_status parse_csi2_resource(struct acpi_resource *res, void *context) | |
63 | { | |
64 | struct csi2_resources_walk_data *crwd = context; | |
65 | struct acpi_resource_csi2_serialbus *csi2_res; | |
66 | struct acpi_resource_source *csi2_res_src; | |
67 | u16 csi2_res_src_length; | |
68 | struct crs_csi2_connection *conn; | |
69 | acpi_handle remote_handle; | |
70 | ||
71 | if (res->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) | |
72 | return AE_OK; | |
73 | ||
74 | csi2_res = &res->data.csi2_serial_bus; | |
75 | ||
76 | if (csi2_res->type != ACPI_RESOURCE_SERIAL_TYPE_CSI2) | |
77 | return AE_OK; | |
78 | ||
79 | csi2_res_src = &csi2_res->resource_source; | |
80 | if (ACPI_FAILURE(acpi_get_handle(NULL, csi2_res_src->string_ptr, | |
81 | &remote_handle))) { | |
82 | acpi_handle_debug(crwd->handle, | |
83 | "unable to find resource source\n"); | |
84 | return AE_OK; | |
85 | } | |
86 | csi2_res_src_length = csi2_res_src->string_length; | |
87 | if (!csi2_res_src_length) { | |
88 | acpi_handle_debug(crwd->handle, | |
89 | "invalid resource source string length\n"); | |
90 | return AE_OK; | |
91 | } | |
92 | ||
93 | conn = kmalloc(struct_size(conn, remote_name, csi2_res_src_length + 1), | |
94 | GFP_KERNEL); | |
95 | if (!conn) | |
96 | return AE_OK; | |
97 | ||
98 | conn->csi2_data = *csi2_res; | |
99 | strscpy(conn->remote_name, csi2_res_src->string_ptr, csi2_res_src_length); | |
100 | conn->csi2_data.resource_source.string_ptr = conn->remote_name; | |
101 | conn->remote_handle = remote_handle; | |
102 | ||
103 | list_add(&conn->entry, &crwd->connections); | |
104 | ||
105 | return AE_OK; | |
106 | } | |
107 | ||
108 | static struct crs_csi2 *acpi_mipi_add_crs_csi2(acpi_handle handle, | |
109 | struct list_head *list) | |
110 | { | |
111 | struct crs_csi2 *csi2; | |
112 | ||
113 | csi2 = kzalloc(sizeof(*csi2), GFP_KERNEL); | |
114 | if (!csi2) | |
115 | return NULL; | |
116 | ||
117 | csi2->handle = handle; | |
118 | INIT_LIST_HEAD(&csi2->connections); | |
119 | csi2->port_count = 1; | |
120 | ||
121 | if (ACPI_FAILURE(acpi_attach_data(handle, acpi_mipi_data_tag, csi2))) { | |
122 | kfree(csi2); | |
123 | return NULL; | |
124 | } | |
125 | ||
126 | list_add(&csi2->entry, list); | |
127 | ||
128 | return csi2; | |
129 | } | |
130 | ||
131 | static struct crs_csi2 *acpi_mipi_get_crs_csi2(acpi_handle handle) | |
132 | { | |
133 | struct crs_csi2 *csi2; | |
134 | ||
135 | if (ACPI_FAILURE(acpi_get_data_full(handle, acpi_mipi_data_tag, | |
136 | (void **)&csi2, NULL))) | |
137 | return NULL; | |
138 | ||
139 | return csi2; | |
140 | } | |
141 | ||
142 | static void csi_csr2_release_connections(struct list_head *list) | |
143 | { | |
144 | struct crs_csi2_connection *conn, *conn_tmp; | |
145 | ||
146 | list_for_each_entry_safe(conn, conn_tmp, list, entry) { | |
147 | list_del(&conn->entry); | |
148 | kfree(conn); | |
149 | } | |
150 | } | |
151 | ||
152 | static void acpi_mipi_del_crs_csi2(struct crs_csi2 *csi2) | |
153 | { | |
154 | list_del(&csi2->entry); | |
155 | acpi_detach_data(csi2->handle, acpi_mipi_data_tag); | |
156 | kfree(csi2->swnodes); | |
157 | csi_csr2_release_connections(&csi2->connections); | |
158 | kfree(csi2); | |
159 | } | |
160 | ||
161 | /** | |
162 | * acpi_mipi_check_crs_csi2 - Look for CSI-2 resources in _CRS | |
163 | * @handle: Device object handle to evaluate _CRS for. | |
164 | * | |
165 | * Find all CSI-2 resource descriptors in the given device's _CRS | |
166 | * and collect them into a list. | |
167 | */ | |
168 | void acpi_mipi_check_crs_csi2(acpi_handle handle) | |
169 | { | |
170 | struct csi2_resources_walk_data crwd = { | |
171 | .handle = handle, | |
172 | .connections = LIST_HEAD_INIT(crwd.connections), | |
173 | }; | |
174 | struct crs_csi2 *csi2; | |
175 | ||
176 | /* | |
177 | * Avoid allocating _CRS CSI-2 objects for devices without any CSI-2 | |
178 | * resource descriptions in _CRS to reduce overhead. | |
179 | */ | |
180 | acpi_walk_resources(handle, METHOD_NAME__CRS, parse_csi2_resource, &crwd); | |
181 | if (list_empty(&crwd.connections)) | |
182 | return; | |
183 | ||
184 | /* | |
185 | * Create a _CRS CSI-2 entry to store the extracted connection | |
186 | * information and add it to the global list. | |
187 | */ | |
188 | csi2 = acpi_mipi_add_crs_csi2(handle, &acpi_mipi_crs_csi2_list); | |
189 | if (!csi2) { | |
190 | csi_csr2_release_connections(&crwd.connections); | |
191 | return; /* Nothing really can be done about this. */ | |
192 | } | |
193 | ||
194 | list_replace(&crwd.connections, &csi2->connections); | |
195 | } | |
196 | ||
197 | #define NO_CSI2_PORT (UINT_MAX - 1) | |
198 | ||
199 | static void alloc_crs_csi2_swnodes(struct crs_csi2 *csi2) | |
200 | { | |
201 | size_t port_count = csi2->port_count; | |
202 | struct acpi_device_software_nodes *swnodes; | |
203 | size_t alloc_size; | |
204 | unsigned int i; | |
205 | ||
206 | /* | |
207 | * Allocate memory for ports, node pointers (number of nodes + | |
208 | * 1 (guardian), nodes (root + number of ports * 2 (because for | |
209 | * every port there is an endpoint)). | |
210 | */ | |
211 | if (check_mul_overflow(sizeof(*swnodes->ports) + | |
212 | sizeof(*swnodes->nodes) * 2 + | |
213 | sizeof(*swnodes->nodeptrs) * 2, | |
214 | port_count, &alloc_size) || | |
215 | check_add_overflow(sizeof(*swnodes) + | |
216 | sizeof(*swnodes->nodes) + | |
217 | sizeof(*swnodes->nodeptrs) * 2, | |
218 | alloc_size, &alloc_size)) { | |
219 | acpi_handle_info(csi2->handle, | |
220 | "too many _CRS CSI-2 resource handles (%zu)", | |
221 | port_count); | |
222 | return; | |
223 | } | |
224 | ||
225 | swnodes = kmalloc(alloc_size, GFP_KERNEL); | |
226 | if (!swnodes) | |
227 | return; | |
228 | ||
229 | swnodes->ports = (struct acpi_device_software_node_port *)(swnodes + 1); | |
230 | swnodes->nodes = (struct software_node *)(swnodes->ports + port_count); | |
231 | swnodes->nodeptrs = (const struct software_node **)(swnodes->nodes + 1 + | |
232 | 2 * port_count); | |
233 | swnodes->num_ports = port_count; | |
234 | ||
235 | for (i = 0; i < 2 * port_count + 1; i++) | |
236 | swnodes->nodeptrs[i] = &swnodes->nodes[i]; | |
237 | ||
238 | swnodes->nodeptrs[i] = NULL; | |
239 | ||
240 | for (i = 0; i < port_count; i++) | |
241 | swnodes->ports[i].port_nr = NO_CSI2_PORT; | |
242 | ||
243 | csi2->swnodes = swnodes; | |
244 | } | |
245 | ||
693c667b RW |
246 | #define ACPI_CRS_CSI2_PHY_TYPE_C 0 |
247 | #define ACPI_CRS_CSI2_PHY_TYPE_D 1 | |
248 | ||
249 | static unsigned int next_csi2_port_index(struct acpi_device_software_nodes *swnodes, | |
250 | unsigned int port_nr) | |
251 | { | |
252 | unsigned int i; | |
253 | ||
254 | for (i = 0; i < swnodes->num_ports; i++) { | |
255 | struct acpi_device_software_node_port *port = &swnodes->ports[i]; | |
256 | ||
257 | if (port->port_nr == port_nr) | |
258 | return i; | |
259 | ||
260 | if (port->port_nr == NO_CSI2_PORT) { | |
261 | port->port_nr = port_nr; | |
262 | return i; | |
263 | } | |
264 | } | |
265 | ||
266 | return NO_CSI2_PORT; | |
267 | } | |
268 | ||
269 | /* Print graph port name into a buffer, return non-zero on failure. */ | |
270 | #define GRAPH_PORT_NAME(var, num) \ | |
271 | (snprintf((var), sizeof(var), SWNODE_GRAPH_PORT_NAME_FMT, (num)) >= \ | |
272 | sizeof(var)) | |
273 | ||
274 | static void extract_crs_csi2_conn_info(acpi_handle local_handle, | |
275 | struct acpi_device_software_nodes *local_swnodes, | |
276 | struct crs_csi2_connection *conn) | |
277 | { | |
278 | struct crs_csi2 *remote_csi2 = acpi_mipi_get_crs_csi2(conn->remote_handle); | |
279 | struct acpi_device_software_nodes *remote_swnodes; | |
280 | struct acpi_device_software_node_port *local_port, *remote_port; | |
281 | struct software_node *local_node, *remote_node; | |
282 | unsigned int local_index, remote_index; | |
283 | unsigned int bus_type; | |
284 | ||
285 | /* | |
286 | * If the previous steps have failed to make room for a _CRS CSI-2 | |
287 | * representation for the remote end of the given connection, skip it. | |
288 | */ | |
289 | if (!remote_csi2) | |
290 | return; | |
291 | ||
292 | remote_swnodes = remote_csi2->swnodes; | |
293 | if (!remote_swnodes) | |
294 | return; | |
295 | ||
296 | switch (conn->csi2_data.phy_type) { | |
297 | case ACPI_CRS_CSI2_PHY_TYPE_C: | |
298 | bus_type = V4L2_FWNODE_BUS_TYPE_CSI2_CPHY; | |
299 | break; | |
300 | ||
301 | case ACPI_CRS_CSI2_PHY_TYPE_D: | |
302 | bus_type = V4L2_FWNODE_BUS_TYPE_CSI2_DPHY; | |
303 | break; | |
304 | ||
305 | default: | |
306 | acpi_handle_info(local_handle, "unknown CSI-2 PHY type %u\n", | |
307 | conn->csi2_data.phy_type); | |
308 | return; | |
309 | } | |
310 | ||
311 | local_index = next_csi2_port_index(local_swnodes, | |
312 | conn->csi2_data.local_port_instance); | |
313 | if (WARN_ON_ONCE(local_index >= local_swnodes->num_ports)) | |
314 | return; | |
315 | ||
316 | remote_index = next_csi2_port_index(remote_swnodes, | |
317 | conn->csi2_data.resource_source.index); | |
318 | if (WARN_ON_ONCE(remote_index >= remote_swnodes->num_ports)) | |
319 | return; | |
320 | ||
321 | local_port = &local_swnodes->ports[local_index]; | |
322 | local_node = &local_swnodes->nodes[ACPI_DEVICE_SWNODE_EP(local_index)]; | |
323 | local_port->crs_csi2_local = true; | |
324 | ||
325 | remote_port = &remote_swnodes->ports[remote_index]; | |
326 | remote_node = &remote_swnodes->nodes[ACPI_DEVICE_SWNODE_EP(remote_index)]; | |
327 | ||
328 | local_port->remote_ep[0] = SOFTWARE_NODE_REFERENCE(remote_node); | |
329 | remote_port->remote_ep[0] = SOFTWARE_NODE_REFERENCE(local_node); | |
330 | ||
331 | local_port->ep_props[ACPI_DEVICE_SWNODE_EP_REMOTE_EP] = | |
332 | PROPERTY_ENTRY_REF_ARRAY("remote-endpoint", | |
333 | local_port->remote_ep); | |
334 | ||
335 | local_port->ep_props[ACPI_DEVICE_SWNODE_EP_BUS_TYPE] = | |
336 | PROPERTY_ENTRY_U32("bus-type", bus_type); | |
337 | ||
338 | local_port->ep_props[ACPI_DEVICE_SWNODE_EP_REG] = | |
339 | PROPERTY_ENTRY_U32("reg", 0); | |
340 | ||
341 | local_port->port_props[ACPI_DEVICE_SWNODE_PORT_REG] = | |
342 | PROPERTY_ENTRY_U32("reg", conn->csi2_data.local_port_instance); | |
343 | ||
344 | if (GRAPH_PORT_NAME(local_port->port_name, | |
345 | conn->csi2_data.local_port_instance)) | |
346 | acpi_handle_info(local_handle, "local port %u name too long", | |
347 | conn->csi2_data.local_port_instance); | |
348 | ||
349 | remote_port->ep_props[ACPI_DEVICE_SWNODE_EP_REMOTE_EP] = | |
350 | PROPERTY_ENTRY_REF_ARRAY("remote-endpoint", | |
351 | remote_port->remote_ep); | |
352 | ||
353 | remote_port->ep_props[ACPI_DEVICE_SWNODE_EP_BUS_TYPE] = | |
354 | PROPERTY_ENTRY_U32("bus-type", bus_type); | |
355 | ||
356 | remote_port->ep_props[ACPI_DEVICE_SWNODE_EP_REG] = | |
357 | PROPERTY_ENTRY_U32("reg", 0); | |
358 | ||
359 | remote_port->port_props[ACPI_DEVICE_SWNODE_PORT_REG] = | |
360 | PROPERTY_ENTRY_U32("reg", conn->csi2_data.resource_source.index); | |
361 | ||
362 | if (GRAPH_PORT_NAME(remote_port->port_name, | |
363 | conn->csi2_data.resource_source.index)) | |
364 | acpi_handle_info(local_handle, "remote port %u name too long", | |
365 | conn->csi2_data.resource_source.index); | |
366 | } | |
367 | ||
368 | static void prepare_crs_csi2_swnodes(struct crs_csi2 *csi2) | |
369 | { | |
370 | struct acpi_device_software_nodes *local_swnodes = csi2->swnodes; | |
371 | acpi_handle local_handle = csi2->handle; | |
372 | struct crs_csi2_connection *conn; | |
373 | ||
374 | /* Bail out if the allocation of swnodes has failed. */ | |
375 | if (!local_swnodes) | |
376 | return; | |
377 | ||
378 | list_for_each_entry(conn, &csi2->connections, entry) | |
379 | extract_crs_csi2_conn_info(local_handle, local_swnodes, conn); | |
380 | } | |
381 | ||
bd721b93 RW |
382 | /** |
383 | * acpi_mipi_scan_crs_csi2 - Create ACPI _CRS CSI-2 software nodes | |
384 | * | |
385 | * Note that this function must be called before any struct acpi_device objects | |
386 | * are bound to any ACPI drivers or scan handlers, so it cannot assume the | |
387 | * existence of struct acpi_device objects for every device present in the ACPI | |
388 | * namespace. | |
389 | * | |
390 | * acpi_scan_lock in scan.c must be held when calling this function. | |
391 | */ | |
392 | void acpi_mipi_scan_crs_csi2(void) | |
393 | { | |
394 | struct crs_csi2 *csi2; | |
395 | LIST_HEAD(aux_list); | |
396 | ||
397 | /* Count references to each ACPI handle in the CSI-2 connection graph. */ | |
398 | list_for_each_entry(csi2, &acpi_mipi_crs_csi2_list, entry) { | |
399 | struct crs_csi2_connection *conn; | |
400 | ||
401 | list_for_each_entry(conn, &csi2->connections, entry) { | |
402 | struct crs_csi2 *remote_csi2; | |
403 | ||
404 | csi2->port_count++; | |
405 | ||
406 | remote_csi2 = acpi_mipi_get_crs_csi2(conn->remote_handle); | |
407 | if (remote_csi2) { | |
408 | remote_csi2->port_count++; | |
409 | continue; | |
410 | } | |
411 | /* | |
412 | * The remote endpoint has no _CRS CSI-2 list entry yet, | |
413 | * so create one for it and add it to the list. | |
414 | */ | |
415 | acpi_mipi_add_crs_csi2(conn->remote_handle, &aux_list); | |
416 | } | |
417 | } | |
418 | list_splice(&aux_list, &acpi_mipi_crs_csi2_list); | |
419 | ||
693c667b RW |
420 | /* |
421 | * Allocate software nodes for representing the CSI-2 information. | |
422 | * | |
423 | * This needs to be done for all of the list entries in one go, because | |
424 | * they may point to each other without restrictions and the next step | |
425 | * relies on the availability of swnodes memory for each list entry. | |
426 | */ | |
bd721b93 RW |
427 | list_for_each_entry(csi2, &acpi_mipi_crs_csi2_list, entry) |
428 | alloc_crs_csi2_swnodes(csi2); | |
693c667b RW |
429 | |
430 | /* | |
431 | * Set up software node properties using data from _CRS CSI-2 resource | |
432 | * descriptors. | |
433 | */ | |
434 | list_for_each_entry(csi2, &acpi_mipi_crs_csi2_list, entry) | |
435 | prepare_crs_csi2_swnodes(csi2); | |
bd721b93 RW |
436 | } |
437 | ||
a6cb0a61 RW |
438 | /* |
439 | * Get the index of the next property in the property array, with a given | |
440 | * maximum value. | |
441 | */ | |
442 | #define NEXT_PROPERTY(index, max) \ | |
443 | (WARN_ON((index) > ACPI_DEVICE_SWNODE_##max) ? \ | |
444 | ACPI_DEVICE_SWNODE_##max : (index)++) | |
445 | ||
446 | static void init_csi2_port_local(struct acpi_device *adev, | |
447 | struct acpi_device_software_node_port *port, | |
448 | struct fwnode_handle *port_fwnode, | |
449 | unsigned int index) | |
450 | { | |
451 | acpi_handle handle = acpi_device_handle(adev); | |
452 | unsigned int num_link_freqs; | |
453 | int ret; | |
454 | ||
455 | ret = fwnode_property_count_u64(port_fwnode, "mipi-img-link-frequencies"); | |
456 | if (ret <= 0) | |
457 | return; | |
458 | ||
459 | num_link_freqs = ret; | |
460 | if (num_link_freqs > ACPI_DEVICE_CSI2_DATA_LANES) { | |
461 | acpi_handle_info(handle, "Too many link frequencies: %u\n", | |
462 | num_link_freqs); | |
463 | num_link_freqs = ACPI_DEVICE_CSI2_DATA_LANES; | |
464 | } | |
465 | ||
466 | ret = fwnode_property_read_u64_array(port_fwnode, | |
467 | "mipi-img-link-frequencies", | |
468 | port->link_frequencies, | |
469 | num_link_freqs); | |
470 | if (ret) { | |
471 | acpi_handle_info(handle, "Unable to get link frequencies (%d)\n", | |
472 | ret); | |
473 | return; | |
474 | } | |
475 | ||
476 | port->ep_props[NEXT_PROPERTY(index, EP_LINK_FREQUENCIES)] = | |
477 | PROPERTY_ENTRY_U64_ARRAY_LEN("link-frequencies", | |
478 | port->link_frequencies, | |
479 | num_link_freqs); | |
480 | } | |
481 | ||
482 | static void init_csi2_port(struct acpi_device *adev, | |
483 | struct acpi_device_software_nodes *swnodes, | |
484 | struct acpi_device_software_node_port *port, | |
485 | struct fwnode_handle *port_fwnode, | |
486 | unsigned int port_index) | |
487 | { | |
488 | unsigned int ep_prop_index = ACPI_DEVICE_SWNODE_EP_CLOCK_LANES; | |
489 | acpi_handle handle = acpi_device_handle(adev); | |
490 | u8 val[ACPI_DEVICE_CSI2_DATA_LANES]; | |
491 | int num_lanes = 0; | |
492 | int ret; | |
493 | ||
494 | if (GRAPH_PORT_NAME(port->port_name, port->port_nr)) | |
495 | return; | |
496 | ||
497 | swnodes->nodes[ACPI_DEVICE_SWNODE_PORT(port_index)] = | |
498 | SOFTWARE_NODE(port->port_name, port->port_props, | |
499 | &swnodes->nodes[ACPI_DEVICE_SWNODE_ROOT]); | |
500 | ||
501 | ret = fwnode_property_read_u8(port_fwnode, "mipi-img-clock-lane", val); | |
502 | if (!ret) | |
503 | port->ep_props[NEXT_PROPERTY(ep_prop_index, EP_CLOCK_LANES)] = | |
504 | PROPERTY_ENTRY_U32("clock-lanes", val[0]); | |
505 | ||
506 | ret = fwnode_property_count_u8(port_fwnode, "mipi-img-data-lanes"); | |
507 | if (ret > 0) { | |
508 | num_lanes = ret; | |
509 | ||
510 | if (num_lanes > ACPI_DEVICE_CSI2_DATA_LANES) { | |
511 | acpi_handle_info(handle, "Too many data lanes: %u\n", | |
512 | num_lanes); | |
513 | num_lanes = ACPI_DEVICE_CSI2_DATA_LANES; | |
514 | } | |
515 | ||
516 | ret = fwnode_property_read_u8_array(port_fwnode, | |
517 | "mipi-img-data-lanes", | |
518 | val, num_lanes); | |
519 | if (!ret) { | |
520 | unsigned int i; | |
521 | ||
522 | for (i = 0; i < num_lanes; i++) | |
523 | port->data_lanes[i] = val[i]; | |
524 | ||
525 | port->ep_props[NEXT_PROPERTY(ep_prop_index, EP_DATA_LANES)] = | |
526 | PROPERTY_ENTRY_U32_ARRAY_LEN("data-lanes", | |
527 | port->data_lanes, | |
528 | num_lanes); | |
529 | } | |
530 | } | |
531 | ||
532 | ret = fwnode_property_count_u8(port_fwnode, "mipi-img-lane-polarities"); | |
533 | if (ret < 0) { | |
534 | acpi_handle_debug(handle, "Lane polarity bytes missing\n"); | |
535 | } else if (ret * BITS_PER_TYPE(u8) < num_lanes + 1) { | |
cb21746b | 536 | acpi_handle_info(handle, "Too few lane polarity bits (%zu vs. %d)\n", |
a6cb0a61 RW |
537 | ret * BITS_PER_TYPE(u8), num_lanes + 1); |
538 | } else { | |
539 | unsigned long mask = 0; | |
540 | int byte_count = ret; | |
541 | unsigned int i; | |
542 | ||
543 | /* | |
544 | * The total number of lanes is ACPI_DEVICE_CSI2_DATA_LANES + 1 | |
545 | * (data lanes + clock lane). It is not expected to ever be | |
546 | * greater than the number of bits in an unsigned long | |
547 | * variable, but ensure that this is the case. | |
548 | */ | |
549 | BUILD_BUG_ON(BITS_PER_TYPE(unsigned long) <= ACPI_DEVICE_CSI2_DATA_LANES); | |
550 | ||
551 | if (byte_count > sizeof(mask)) { | |
552 | acpi_handle_info(handle, "Too many lane polarities: %d\n", | |
553 | byte_count); | |
554 | byte_count = sizeof(mask); | |
555 | } | |
556 | fwnode_property_read_u8_array(port_fwnode, "mipi-img-lane-polarities", | |
557 | val, byte_count); | |
558 | ||
559 | for (i = 0; i < byte_count; i++) | |
560 | mask |= (unsigned long)val[i] << BITS_PER_TYPE(u8) * i; | |
561 | ||
562 | for (i = 0; i <= num_lanes; i++) | |
563 | port->lane_polarities[i] = test_bit(i, &mask); | |
564 | ||
565 | port->ep_props[NEXT_PROPERTY(ep_prop_index, EP_LANE_POLARITIES)] = | |
566 | PROPERTY_ENTRY_U32_ARRAY_LEN("lane-polarities", | |
567 | port->lane_polarities, | |
568 | num_lanes + 1); | |
569 | } | |
570 | ||
571 | swnodes->nodes[ACPI_DEVICE_SWNODE_EP(port_index)] = | |
572 | SOFTWARE_NODE("endpoint@0", swnodes->ports[port_index].ep_props, | |
573 | &swnodes->nodes[ACPI_DEVICE_SWNODE_PORT(port_index)]); | |
574 | ||
575 | if (port->crs_csi2_local) | |
576 | init_csi2_port_local(adev, port, port_fwnode, ep_prop_index); | |
577 | } | |
578 | ||
579 | #define MIPI_IMG_PORT_PREFIX "mipi-img-port-" | |
580 | ||
581 | static struct fwnode_handle *get_mipi_port_handle(struct fwnode_handle *adev_fwnode, | |
582 | unsigned int port_nr) | |
583 | { | |
584 | char port_name[sizeof(MIPI_IMG_PORT_PREFIX) + 2]; | |
585 | ||
586 | if (snprintf(port_name, sizeof(port_name), "%s%u", | |
587 | MIPI_IMG_PORT_PREFIX, port_nr) >= sizeof(port_name)) | |
588 | return NULL; | |
589 | ||
590 | return fwnode_get_named_child_node(adev_fwnode, port_name); | |
591 | } | |
592 | ||
593 | static void init_crs_csi2_swnodes(struct crs_csi2 *csi2) | |
594 | { | |
595 | struct acpi_buffer buffer = { .length = ACPI_ALLOCATE_BUFFER }; | |
596 | struct acpi_device_software_nodes *swnodes = csi2->swnodes; | |
597 | acpi_handle handle = csi2->handle; | |
f533e43a | 598 | unsigned int prop_index = 0; |
a6cb0a61 RW |
599 | struct fwnode_handle *adev_fwnode; |
600 | struct acpi_device *adev; | |
601 | acpi_status status; | |
602 | unsigned int i; | |
4cd57d6d | 603 | u32 val; |
a6cb0a61 RW |
604 | int ret; |
605 | ||
606 | /* | |
607 | * Bail out if the swnodes are not available (either they have not been | |
608 | * allocated or they have been assigned to the device already). | |
609 | */ | |
610 | if (!swnodes) | |
611 | return; | |
612 | ||
613 | adev = acpi_fetch_acpi_dev(handle); | |
614 | if (!adev) | |
615 | return; | |
616 | ||
617 | adev_fwnode = acpi_fwnode_handle(adev); | |
618 | ||
f533e43a SA |
619 | /* |
620 | * If the "rotation" property is not present, but _PLD is there, | |
621 | * evaluate it to get the "rotation" value. | |
622 | */ | |
623 | if (!fwnode_property_present(adev_fwnode, "rotation")) { | |
624 | struct acpi_pld_info *pld; | |
625 | ||
626 | status = acpi_get_physical_device_location(handle, &pld); | |
627 | if (ACPI_SUCCESS(status)) { | |
628 | swnodes->dev_props[NEXT_PROPERTY(prop_index, DEV_ROTATION)] = | |
629 | PROPERTY_ENTRY_U32("rotation", | |
630 | pld->rotation * 45U); | |
631 | kfree(pld); | |
632 | } | |
633 | } | |
634 | ||
4cd57d6d SA |
635 | if (!fwnode_property_read_u32(adev_fwnode, "mipi-img-clock-frequency", &val)) |
636 | swnodes->dev_props[NEXT_PROPERTY(prop_index, DEV_CLOCK_FREQUENCY)] = | |
637 | PROPERTY_ENTRY_U32("clock-frequency", val); | |
638 | ||
639 | if (!fwnode_property_read_u32(adev_fwnode, "mipi-img-led-max-current", &val)) | |
640 | swnodes->dev_props[NEXT_PROPERTY(prop_index, DEV_LED_MAX_MICROAMP)] = | |
641 | PROPERTY_ENTRY_U32("led-max-microamp", val); | |
642 | ||
643 | if (!fwnode_property_read_u32(adev_fwnode, "mipi-img-flash-max-current", &val)) | |
644 | swnodes->dev_props[NEXT_PROPERTY(prop_index, DEV_FLASH_MAX_MICROAMP)] = | |
645 | PROPERTY_ENTRY_U32("flash-max-microamp", val); | |
646 | ||
647 | if (!fwnode_property_read_u32(adev_fwnode, "mipi-img-flash-max-timeout-us", &val)) | |
648 | swnodes->dev_props[NEXT_PROPERTY(prop_index, DEV_FLASH_MAX_TIMEOUT_US)] = | |
649 | PROPERTY_ENTRY_U32("flash-max-timeout-us", val); | |
650 | ||
a6cb0a61 RW |
651 | status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); |
652 | if (ACPI_FAILURE(status)) { | |
653 | acpi_handle_info(handle, "Unable to get the path name\n"); | |
654 | return; | |
655 | } | |
656 | ||
657 | swnodes->nodes[ACPI_DEVICE_SWNODE_ROOT] = | |
658 | SOFTWARE_NODE(buffer.pointer, swnodes->dev_props, NULL); | |
659 | ||
660 | for (i = 0; i < swnodes->num_ports; i++) { | |
661 | struct acpi_device_software_node_port *port = &swnodes->ports[i]; | |
662 | struct fwnode_handle *port_fwnode; | |
663 | ||
664 | /* | |
665 | * The MIPI DisCo for Imaging specification defines _DSD device | |
666 | * properties for providing CSI-2 port parameters that can be | |
667 | * accessed through the generic device properties framework. To | |
668 | * access them, it is first necessary to find the data node | |
669 | * representing the port under the given ACPI device object. | |
670 | */ | |
671 | port_fwnode = get_mipi_port_handle(adev_fwnode, port->port_nr); | |
672 | if (!port_fwnode) { | |
673 | acpi_handle_info(handle, | |
674 | "MIPI port name too long for port %u\n", | |
675 | port->port_nr); | |
676 | continue; | |
677 | } | |
678 | ||
679 | init_csi2_port(adev, swnodes, port, port_fwnode, i); | |
680 | ||
681 | fwnode_handle_put(port_fwnode); | |
682 | } | |
683 | ||
684 | ret = software_node_register_node_group(swnodes->nodeptrs); | |
685 | if (ret < 0) { | |
686 | acpi_handle_info(handle, | |
687 | "Unable to register software nodes (%d)\n", ret); | |
688 | return; | |
689 | } | |
690 | ||
691 | adev->swnodes = swnodes; | |
692 | adev_fwnode->secondary = software_node_fwnode(swnodes->nodes); | |
693 | ||
694 | /* | |
695 | * Prevents the swnodes from this csi2 entry from being assigned again | |
696 | * or freed prematurely. | |
697 | */ | |
698 | csi2->swnodes = NULL; | |
699 | } | |
700 | ||
701 | /** | |
702 | * acpi_mipi_init_crs_csi2_swnodes - Initialize _CRS CSI-2 software nodes | |
703 | * | |
704 | * Use MIPI DisCo for Imaging device properties to finalize the initialization | |
705 | * of CSI-2 software nodes for all ACPI device objects that have been already | |
706 | * enumerated. | |
707 | */ | |
708 | void acpi_mipi_init_crs_csi2_swnodes(void) | |
709 | { | |
710 | struct crs_csi2 *csi2, *csi2_tmp; | |
711 | ||
712 | list_for_each_entry_safe(csi2, csi2_tmp, &acpi_mipi_crs_csi2_list, entry) | |
713 | init_crs_csi2_swnodes(csi2); | |
714 | } | |
715 | ||
bd721b93 RW |
716 | /** |
717 | * acpi_mipi_crs_csi2_cleanup - Free _CRS CSI-2 temporary data | |
718 | */ | |
719 | void acpi_mipi_crs_csi2_cleanup(void) | |
720 | { | |
721 | struct crs_csi2 *csi2, *csi2_tmp; | |
722 | ||
723 | list_for_each_entry_safe(csi2, csi2_tmp, &acpi_mipi_crs_csi2_list, entry) | |
724 | acpi_mipi_del_crs_csi2(csi2); | |
725 | } |