|
6 | 6 | *
|
7 | 7 | * Support MIPI DisCo for Imaging by parsing ACPI _CRS CSI-2 records defined in
|
8 | 8 | * Section 6.4.3.8.2.4 "Camera Serial Interface (CSI-2) Connection Resource
|
9 |
| - * Descriptor" of ACPI 6.5. |
| 9 | + * Descriptor" of ACPI 6.5 and using device properties defined by the MIPI DisCo |
| 10 | + * for Imaging specification. |
10 | 11 | *
|
11 | 12 | * The implementation looks for the information in the ACPI namespace (CSI-2
|
12 | 13 | * resource descriptors in _CRS) and constructs software nodes compatible with
|
13 | 14 | * Documentation/firmware-guide/acpi/dsd/graph.rst to represent the CSI-2
|
14 |
| - * connection graph. |
| 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. |
15 | 19 | */
|
16 | 20 |
|
17 | 21 | #include <linux/acpi.h>
|
@@ -431,6 +435,250 @@ void acpi_mipi_scan_crs_csi2(void)
|
431 | 435 | prepare_crs_csi2_swnodes(csi2);
|
432 | 436 | }
|
433 | 437 |
|
| 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) { |
| 536 | + acpi_handle_info(handle, "Too few lane polarity bytes (%lu vs. %d)\n", |
| 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; |
| 598 | + struct fwnode_handle *adev_fwnode; |
| 599 | + struct acpi_device *adev; |
| 600 | + acpi_status status; |
| 601 | + unsigned int i; |
| 602 | + int ret; |
| 603 | + |
| 604 | + /* |
| 605 | + * Bail out if the swnodes are not available (either they have not been |
| 606 | + * allocated or they have been assigned to the device already). |
| 607 | + */ |
| 608 | + if (!swnodes) |
| 609 | + return; |
| 610 | + |
| 611 | + adev = acpi_fetch_acpi_dev(handle); |
| 612 | + if (!adev) |
| 613 | + return; |
| 614 | + |
| 615 | + adev_fwnode = acpi_fwnode_handle(adev); |
| 616 | + |
| 617 | + status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); |
| 618 | + if (ACPI_FAILURE(status)) { |
| 619 | + acpi_handle_info(handle, "Unable to get the path name\n"); |
| 620 | + return; |
| 621 | + } |
| 622 | + |
| 623 | + swnodes->nodes[ACPI_DEVICE_SWNODE_ROOT] = |
| 624 | + SOFTWARE_NODE(buffer.pointer, swnodes->dev_props, NULL); |
| 625 | + |
| 626 | + for (i = 0; i < swnodes->num_ports; i++) { |
| 627 | + struct acpi_device_software_node_port *port = &swnodes->ports[i]; |
| 628 | + struct fwnode_handle *port_fwnode; |
| 629 | + |
| 630 | + /* |
| 631 | + * The MIPI DisCo for Imaging specification defines _DSD device |
| 632 | + * properties for providing CSI-2 port parameters that can be |
| 633 | + * accessed through the generic device properties framework. To |
| 634 | + * access them, it is first necessary to find the data node |
| 635 | + * representing the port under the given ACPI device object. |
| 636 | + */ |
| 637 | + port_fwnode = get_mipi_port_handle(adev_fwnode, port->port_nr); |
| 638 | + if (!port_fwnode) { |
| 639 | + acpi_handle_info(handle, |
| 640 | + "MIPI port name too long for port %u\n", |
| 641 | + port->port_nr); |
| 642 | + continue; |
| 643 | + } |
| 644 | + |
| 645 | + init_csi2_port(adev, swnodes, port, port_fwnode, i); |
| 646 | + |
| 647 | + fwnode_handle_put(port_fwnode); |
| 648 | + } |
| 649 | + |
| 650 | + ret = software_node_register_node_group(swnodes->nodeptrs); |
| 651 | + if (ret < 0) { |
| 652 | + acpi_handle_info(handle, |
| 653 | + "Unable to register software nodes (%d)\n", ret); |
| 654 | + return; |
| 655 | + } |
| 656 | + |
| 657 | + adev->swnodes = swnodes; |
| 658 | + adev_fwnode->secondary = software_node_fwnode(swnodes->nodes); |
| 659 | + |
| 660 | + /* |
| 661 | + * Prevents the swnodes from this csi2 entry from being assigned again |
| 662 | + * or freed prematurely. |
| 663 | + */ |
| 664 | + csi2->swnodes = NULL; |
| 665 | +} |
| 666 | + |
| 667 | +/** |
| 668 | + * acpi_mipi_init_crs_csi2_swnodes - Initialize _CRS CSI-2 software nodes |
| 669 | + * |
| 670 | + * Use MIPI DisCo for Imaging device properties to finalize the initialization |
| 671 | + * of CSI-2 software nodes for all ACPI device objects that have been already |
| 672 | + * enumerated. |
| 673 | + */ |
| 674 | +void acpi_mipi_init_crs_csi2_swnodes(void) |
| 675 | +{ |
| 676 | + struct crs_csi2 *csi2, *csi2_tmp; |
| 677 | + |
| 678 | + list_for_each_entry_safe(csi2, csi2_tmp, &acpi_mipi_crs_csi2_list, entry) |
| 679 | + init_crs_csi2_swnodes(csi2); |
| 680 | +} |
| 681 | + |
434 | 682 | /**
|
435 | 683 | * acpi_mipi_crs_csi2_cleanup - Free _CRS CSI-2 temporary data
|
436 | 684 | */
|
|
0 commit comments