LCOV - code coverage report
Current view: top level - ipu4 - ambu-ipu-bridge.c (source / functions) Coverage Total Hit
Test: ipu4.info Lines: 0.0 % 131 0
Test Date: 2026-05-12 04:57:36 Functions: 0.0 % 9 0

            Line data    Source code
       1              : // SPDX-License-Identifier: GPL-2.0
       2              : /* Author: Andreas Helbech Kleist <andreaskleist@gmail.com> */
       3              : 
       4              : #include <linux/device.h>
       5              : #include <linux/i2c.h>
       6              : #include <linux/mei_cl_bus.h>
       7              : #include <linux/platform_device.h>
       8              : #include <linux/pm_runtime.h>
       9              : #include <linux/property.h>
      10              : #include <linux/string.h>
      11              : #include <linux/workqueue.h>
      12              : #include <media/v4l2-fwnode.h>
      13              : 
      14              : #include "ambu-ipu-bridge.h"
      15              : 
      16              : #include "ipu4-compat.h"
      17              : 
      18              : static struct ipu_bridge *static_bridge;
      19              : 
      20              : static const struct mipi_bridge_config mipi_bridge_configs[] = {
      21              :         {
      22              :                 .compatible = "ambu,tc358748",
      23              :                 .i2c_addr = 0x0e,
      24              :                 .i2c_adapter = 0,
      25              :                 // ieib475_pll_configurations op_sys_clk from 4.19 driver
      26              :                 .link_freq = 317250000,
      27              :                 .mcsi_port = 0,
      28              :                 .csi2_device = 0,
      29              :         },
      30              :         {
      31              :                 .compatible = "ambu,tc358748",
      32              :                 .i2c_addr = 0x0e,
      33              :                 .i2c_adapter = 3,
      34              :                 // ieib475_pll_configurations op_sys_clk from 4.19 driver
      35              :                 .link_freq = 317250000,
      36              :                 .mcsi_port = 1,
      37              :                 .csi2_device = 4,
      38              :         },
      39              : 
      40              : };
      41              : 
      42              : static const struct ipu_property_names prop_names = {
      43              :         .clock_frequency = "clock-frequency",
      44              :         .bus_type = "bus-type",
      45              :         .data_lanes = "data-lanes",
      46              :         .remote_endpoint = "remote-endpoint",
      47              :         .link_frequencies = "link-frequencies",
      48              : };
      49              : 
      50            0 : static void ipu_bridge_create_fwnode_properties
      51              :         (struct ipu_sensor *sensor,
      52              :          struct ipu_bridge *bridge,
      53              :          const struct mipi_bridge_config *cfg)
      54              : {
      55            0 :         sensor->prop_names = prop_names;
      56              : 
      57            0 :         sensor->local_ref[0] =
      58            0 :         SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_IPU_ENDPOINT]);
      59              : 
      60            0 :         sensor->remote_ref[0] =
      61            0 :         SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_SENSOR_ENDPOINT]);
      62              : 
      63            0 :         sensor->dev_properties[0] =
      64            0 :         PROPERTY_ENTRY_U32(sensor->prop_names.clock_frequency,
      65              :                            cfg->link_freq);
      66              : 
      67            0 :         sensor->ep_properties[0] =
      68            0 :         PROPERTY_ENTRY_U32(sensor->prop_names.bus_type,
      69              :                            V4L2_FWNODE_BUS_TYPE_CSI2_DPHY);
      70              : 
      71            0 :         sensor->ep_properties[1] =
      72            0 :         PROPERTY_ENTRY_U32_ARRAY_LEN(sensor->prop_names.data_lanes,
      73              :                                      bridge->data_lanes,
      74              :                                      sensor->lanes);
      75            0 :         sensor->ep_properties[2] =
      76            0 :         PROPERTY_ENTRY_REF_ARRAY(sensor->prop_names.remote_endpoint,
      77              :                                  sensor->local_ref);
      78              : 
      79              :         // TODO - does this need to be an array when we always have 1?
      80            0 :         sensor->ep_properties[3] =
      81            0 :         PROPERTY_ENTRY_U64_ARRAY_LEN(sensor->prop_names.link_frequencies,
      82              :                                      &cfg->link_freq,
      83              :                                      1);
      84              : 
      85            0 :         sensor->ipu_properties[0] =
      86              :         PROPERTY_ENTRY_U32_ARRAY_LEN(sensor->prop_names.data_lanes,
      87              :                                      bridge->data_lanes,
      88              :                                      sensor->lanes);
      89            0 :         sensor->ipu_properties[1] =
      90            0 :         PROPERTY_ENTRY_REF_ARRAY(sensor->prop_names.remote_endpoint,
      91              :                                  sensor->remote_ref);
      92            0 : }
      93              : 
      94            0 : static void ipu_bridge_init_swnode_names(struct ipu_sensor *sensor)
      95              : {
      96            0 :         snprintf(sensor->node_names.remote_port,
      97              :                  sizeof(sensor->node_names.remote_port),
      98            0 :                  SWNODE_GRAPH_PORT_NAME_FMT, sensor->link);
      99            0 :         snprintf(sensor->node_names.port,
     100              :                  sizeof(sensor->node_names.port),
     101              :                  SWNODE_GRAPH_PORT_NAME_FMT, 0); /* Always port 0 */
     102            0 :         snprintf(sensor->node_names.endpoint,
     103              :                  sizeof(sensor->node_names.endpoint),
     104              :                  SWNODE_GRAPH_ENDPOINT_NAME_FMT, 0); /* And endpoint 0 */
     105            0 : }
     106              : 
     107              : static void ipu_bridge_init_swnode_group(struct ipu_sensor *sensor)
     108              : {
     109              :         struct software_node *nodes = sensor->swnodes;
     110              : 
     111            0 :         sensor->group[SWNODE_SENSOR_HID] = &nodes[SWNODE_SENSOR_HID];
     112            0 :         sensor->group[SWNODE_SENSOR_PORT] = &nodes[SWNODE_SENSOR_PORT];
     113            0 :         sensor->group[SWNODE_SENSOR_ENDPOINT] = &nodes[SWNODE_SENSOR_ENDPOINT];
     114            0 :         sensor->group[SWNODE_IPU_PORT] = &nodes[SWNODE_IPU_PORT];
     115            0 :         sensor->group[SWNODE_IPU_ENDPOINT] = &nodes[SWNODE_IPU_ENDPOINT];
     116              : }
     117              : 
     118            0 : static void ipu_bridge_create_connection_swnodes(struct ipu_bridge *bridge,
     119              :                                                  struct ipu_sensor *sensor)
     120              : {
     121            0 :         struct software_node *nodes = sensor->swnodes;
     122              : 
     123            0 :         ipu_bridge_init_swnode_names(sensor);
     124              : 
     125            0 :         nodes[SWNODE_SENSOR_HID] = NODE_SENSOR(sensor->name,
     126              :                                                sensor->dev_properties);
     127            0 :         nodes[SWNODE_SENSOR_PORT] = NODE_PORT(sensor->node_names.port,
     128              :                                               &nodes[SWNODE_SENSOR_HID]);
     129            0 :         nodes[SWNODE_SENSOR_ENDPOINT] =
     130            0 :                 NODE_ENDPOINT(sensor->node_names.endpoint,
     131              :                               &nodes[SWNODE_SENSOR_PORT],
     132              :                               sensor->ep_properties);
     133            0 :         nodes[SWNODE_IPU_PORT] = NODE_PORT(sensor->node_names.remote_port,
     134              :                                            &bridge->ipu_hid_node);
     135            0 :         nodes[SWNODE_IPU_ENDPOINT] = NODE_ENDPOINT(sensor->node_names.endpoint,
     136              :                                                    &nodes[SWNODE_IPU_PORT],
     137              :                                                    sensor->ipu_properties);
     138              : 
     139              :         ipu_bridge_init_swnode_group(sensor);
     140            0 : }
     141              : 
     142            0 : static void ipu_bridge_unregister_sensors(struct ipu_bridge *bridge)
     143              : {
     144              :         struct ipu_sensor *sensor;
     145              :         unsigned int i;
     146              : 
     147            0 :         for (i = 0; i < bridge->n_sensors; i++) {
     148              :                 sensor = &bridge->sensors[i];
     149            0 :                 i2c_unregister_device(sensor->i2c_dev);
     150            0 :                 software_node_unregister_node_group(sensor->group);
     151              :         }
     152            0 : }
     153              : 
     154              : static struct i2c_client *
     155            0 : ipu_bridge_init_i2c_dev(struct device *dev,
     156              :                         const struct mipi_bridge_config *cfg,
     157              :                         const struct software_node *swnode)
     158              : {
     159              :         struct i2c_adapter *adapter;
     160              :         struct i2c_client *client;
     161            0 :         struct i2c_board_info info = {
     162            0 :                 I2C_BOARD_INFO("ambu,tc358748", cfg->i2c_addr) };
     163              : 
     164              :         // TODO - when swnode is passed to i2c_new_device,
     165              :         // should it also be registered elsewhere?
     166            0 :         info.swnode = swnode;
     167              : 
     168            0 :         adapter = i2c_get_adapter(cfg->i2c_adapter);
     169            0 :         if (!adapter) {
     170            0 :                 dev_err(dev, "Invalid I2C adapter %d for port %d",
     171              :                         cfg->i2c_adapter, cfg->mcsi_port);
     172            0 :                 return ERR_PTR(-EINVAL);
     173              :         }
     174              : 
     175            0 :         client = i2c_new_client_device(adapter, &info);
     176            0 :         i2c_put_adapter(adapter);
     177            0 :         return client;
     178              : }
     179              : 
     180              : static int
     181            0 : ipu_bridge_setup_mipi_bridge(const struct mipi_bridge_config *cfg,
     182              :                              struct ipu_bridge *bridge,
     183              :                              struct device *dev)
     184              : {
     185              :         struct ipu_sensor *sensor;
     186              :         int ret;
     187              : 
     188            0 :         if (bridge->n_sensors >= IPU_MAX_PORTS) {
     189            0 :                 dev_err(dev, "Exceeded available IPU ports\n");
     190            0 :                 return -EINVAL;
     191              :         }
     192              : 
     193            0 :         sensor = &bridge->sensors[bridge->n_sensors];
     194              : 
     195            0 :         sensor->lanes = 1; // from 4.19.217 dev_dbg(&csi2->isys->adev->dev,
     196              :                            // "lane nr %d.\n", nlanes) => "lane nr 1."
     197            0 :         sensor->link = cfg->csi2_device;
     198              : 
     199            0 :         snprintf(sensor->name, sizeof(sensor->name), "%s-%u",
     200            0 :                  cfg->compatible, cfg->mcsi_port);
     201              : 
     202            0 :         if (sensor->lanes > IPU_MAX_LANES) {
     203            0 :                 dev_err(&sensor->i2c_dev->dev, "Number of lanes is invalid\n");
     204            0 :                 return -EINVAL;
     205              :         }
     206              : 
     207            0 :         ipu_bridge_create_fwnode_properties(sensor, bridge, cfg);
     208            0 :         ipu_bridge_create_connection_swnodes(bridge, sensor);
     209              : 
     210            0 :         ret = software_node_register_node_group(sensor->group);
     211            0 :         if (ret)
     212              :                 return ret;
     213              : 
     214              :         // HACK - This is probably not the right place to register this device
     215              : 
     216            0 :         sensor->i2c_dev =
     217            0 :                 ipu_bridge_init_i2c_dev(dev, cfg,
     218            0 :                                         &sensor->swnodes[SWNODE_SENSOR_HID]);
     219            0 :         if (IS_ERR(sensor->i2c_dev)) {
     220            0 :                 ret = PTR_ERR(sensor->i2c_dev);
     221            0 :                 sensor->i2c_dev = NULL;
     222            0 :                 goto err_free_swnodes;
     223              :         }
     224              : 
     225            0 :         dev_info(dev, "Found supported sensor %s\n",
     226              :                  dev_name(&sensor->i2c_dev->dev));
     227              : 
     228            0 :         bridge->n_sensors++;
     229              : 
     230            0 :         return 0;
     231              : 
     232              : err_free_swnodes:
     233            0 :         software_node_unregister_node_group(sensor->group);
     234            0 :         return ret;
     235              : }
     236              : 
     237            0 : static int ipu_bridge_setup_mipi_bridges(struct ipu_bridge *bridge,
     238              :                                          struct device *dev)
     239              : {
     240              :         unsigned int i;
     241              :         int ret;
     242              : 
     243            0 :         for (i = 0; i < ARRAY_SIZE(mipi_bridge_configs); i++) {
     244            0 :                 ret = ipu_bridge_setup_mipi_bridge(&mipi_bridge_configs[i],
     245              :                                                    bridge, dev);
     246            0 :                 if (ret)
     247            0 :                         goto err_unregister_sensors;
     248              :         }
     249              : 
     250              :         return 0;
     251              : 
     252              : err_unregister_sensors:
     253            0 :         ipu_bridge_unregister_sensors(bridge);
     254            0 :         return ret;
     255              : }
     256              : 
     257              : static int ipu_bridge_sensors_are_ready(void)
     258              : {
     259              :         //TODO - is there anything we should do here
     260              :         // to find out if toshiba bridge is ready?
     261              :         return true;
     262              : }
     263              : 
     264            0 : int ambu_ipu_bridge_init(struct device *dev)
     265              : {
     266              :         struct fwnode_handle *fwnode;
     267              :         struct ipu_bridge *bridge;
     268              :         unsigned int i;
     269              :         int ret;
     270              : 
     271              :         if (!ipu_bridge_sensors_are_ready())
     272              :                 return -EPROBE_DEFER;
     273              : 
     274            0 :         static_bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
     275            0 :         if (!static_bridge)
     276              :                 return -ENOMEM;
     277              :         // Use alias to avoid changing every line in this file
     278              :         bridge = static_bridge;
     279              : 
     280            0 :         strscpy(bridge->ipu_node_name, IPU_HID,
     281              :                 sizeof(bridge->ipu_node_name));
     282            0 :         bridge->ipu_hid_node.name = bridge->ipu_node_name;
     283              : 
     284            0 :         ret = software_node_register(&bridge->ipu_hid_node);
     285            0 :         if (ret < 0) {
     286            0 :                 dev_err(dev, "Failed to register the IPU HID node\n");
     287            0 :                 goto err_free_bridge;
     288              :         }
     289              : 
     290              :         /*
     291              :          * Map the lane arrangement, which is fixed for the IPU3 (meaning we
     292              :          * only need one, rather than one per sensor). We include it as a
     293              :          * member of the struct ipu_bridge rather than a global variable so
     294              :          * that it survives if the module is unloaded along with the rest of
     295              :          * the struct.
     296              :          */
     297            0 :         for (i = 0; i < IPU_MAX_LANES; i++)
     298            0 :                 bridge->data_lanes[i] = i + 1;
     299              : 
     300            0 :         ret = ipu_bridge_setup_mipi_bridges(bridge, dev);
     301            0 :         if (ret || bridge->n_sensors == 0)
     302            0 :                 goto err_unregister_ipu;
     303              : 
     304            0 :         dev_info(dev, "Connected %d cameras\n", bridge->n_sensors);
     305              : 
     306            0 :         fwnode = software_node_fwnode(&bridge->ipu_hid_node);
     307            0 :         if (!fwnode) {
     308            0 :                 dev_err(dev, "Error getting fwnode from ipu software_node\n");
     309              :                 ret = -ENODEV;
     310            0 :                 goto err_unregister_sensors;
     311              :         }
     312              : 
     313              :         // HACK - there appears to be no inverse of
     314              :         // set_secondary_fwnode, so we do this manually
     315            0 :         if (fwnode->secondary) {
     316            0 :                 dev_err(dev, "Expected no fwnode->secondary, cannot proceed");
     317              :                 ret = -ENOTUNIQ;
     318            0 :                 goto err_unregister_sensors;
     319              :         }
     320            0 :         dev->fwnode->secondary = fwnode;
     321            0 :         return 0;
     322              : 
     323            0 : err_unregister_sensors:
     324            0 :         ipu_bridge_unregister_sensors(bridge);
     325            0 : err_unregister_ipu:
     326            0 :         software_node_unregister(&bridge->ipu_hid_node);
     327            0 : err_free_bridge:
     328            0 :         kfree(bridge);
     329            0 :         static_bridge = NULL;
     330            0 :         return ret;
     331              : }
     332              : EXPORT_SYMBOL_NS_GPL(ambu_ipu_bridge_init, INTEL_IPU_BRIDGE);
     333              : 
     334            0 : void ambu_ipu_bridge_uninit(struct device *dev)
     335              : {
     336            0 :         dev->fwnode->secondary = NULL;
     337            0 :         ipu_bridge_unregister_sensors(static_bridge);
     338            0 :         software_node_unregister(&static_bridge->ipu_hid_node);
     339            0 :         kfree(static_bridge);
     340            0 :         static_bridge = NULL;
     341            0 : }
     342              : EXPORT_SYMBOL_NS_GPL(ambu_ipu_bridge_uninit, INTEL_IPU_BRIDGE);
     343              : MODULE_LICENSE("GPL v2");
        

Generated by: LCOV version 2.0-1