+/* Register I2C client subdev associated with @node. */
+static int fimc_md_of_add_sensor(struct fimc_md *fmd,
+ struct device_node *node, int index)
+{
+ struct fimc_sensor_info *si;
+ struct i2c_client *client;
+ struct v4l2_subdev *sd;
+ int ret;
+
+ if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor)))
+ return -EINVAL;
+ si = &fmd->sensor[index];
+
+ client = of_find_i2c_device_by_node(node);
+ if (!client)
+ return -EPROBE_DEFER;
+
+ device_lock(&client->dev);
+
+ if (!client->driver ||
+ !try_module_get(client->driver->driver.owner)) {
+ ret = -EPROBE_DEFER;
+ v4l2_info(&fmd->v4l2_dev, "No driver found for %s\n",
+ node->full_name);
+ goto dev_put;
+ }
+
+ /* Enable sensor's master clock */
+ ret = __fimc_md_set_camclk(fmd, si, true);
+ if (ret < 0)
+ goto mod_put;
+ sd = i2c_get_clientdata(client);
+
+ ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
+ __fimc_md_set_camclk(fmd, si, false);
+ if (ret < 0)
+ goto mod_put;
+
+ v4l2_set_subdev_hostdata(sd, si);
+ sd->grp_id = GRP_ID_SENSOR;
+ si->subdev = sd;
+ v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice: %s (%d)\n",
+ sd->name, fmd->num_sensors);
+ fmd->num_sensors++;
+
+mod_put:
+ module_put(client->driver->driver.owner);
+dev_put:
+ device_unlock(&client->dev);
+ put_device(&client->dev);
+ return ret;
+}
+
+/* Parse port node and register as a sub-device any sensor specified there. */
+static int fimc_md_parse_port_node(struct fimc_md *fmd,
+ struct device_node *port,
+ unsigned int index)
+{
+ struct device_node *rem, *ep, *np;
+ struct fimc_source_info *pd;
+ struct v4l2_of_endpoint endpoint;
+ int ret;
+ u32 val;
+
+ pd = &fmd->sensor[index].pdata;
+
+ /* Assume here a port node can have only one endpoint node. */
+ ep = of_get_next_child(port, NULL);
+ if (!ep)
+ return 0;
+
+ v4l2_of_parse_endpoint(ep, &endpoint);
+ if (WARN_ON(endpoint.port == 0) || index >= FIMC_MAX_SENSORS)
+ return -EINVAL;
+
+ pd->mux_id = (endpoint.port - 1) & 0x1;
+
+ rem = v4l2_of_get_remote_port_parent(ep);
+ of_node_put(ep);
+ if (rem == NULL) {
+ v4l2_info(&fmd->v4l2_dev, "Remote device at %s not found\n",
+ ep->full_name);
+ return 0;
+ }
+ if (!of_property_read_u32(rem, "samsung,camclk-out", &val))
+ pd->clk_id = val;
+
+ if (!of_property_read_u32(rem, "clock-frequency", &val))
+ pd->clk_frequency = val;
+
+ if (pd->clk_frequency == 0) {
+ v4l2_err(&fmd->v4l2_dev, "Wrong clock frequency at node %s\n",
+ rem->full_name);
+ of_node_put(rem);
+ return -EINVAL;
+ }
+
+ if (fimc_input_is_parallel(endpoint.port)) {
+ if (endpoint.bus_type == V4L2_MBUS_PARALLEL)
+ pd->sensor_bus_type = FIMC_BUS_TYPE_ITU_601;
+ else
+ pd->sensor_bus_type = FIMC_BUS_TYPE_ITU_656;
+ pd->flags = endpoint.bus.parallel.flags;
+ } else if (fimc_input_is_mipi_csi(endpoint.port)) {
+ /*
+ * MIPI CSI-2: only input mux selection and
+ * the sensor's clock frequency is needed.
+ */
+ pd->sensor_bus_type = FIMC_BUS_TYPE_MIPI_CSI2;
+ } else {
+ v4l2_err(&fmd->v4l2_dev, "Wrong port id (%u) at node %s\n",
+ endpoint.port, rem->full_name);
+ }
+ /*
+ * For FIMC-IS handled sensors, that are placed under i2c-isp device
+ * node, FIMC is connected to the FIMC-IS through its ISP Writeback
+ * input. Sensors are attached to the FIMC-LITE hostdata interface
+ * directly or through MIPI-CSIS, depending on the external media bus
+ * used. This needs to be handled in a more reliable way, not by just
+ * checking parent's node name.
+ */
+ np = of_get_parent(rem);
+
+ if (np && !of_node_cmp(np->name, "i2c-isp"))
+ pd->fimc_bus_type = FIMC_BUS_TYPE_ISP_WRITEBACK;
+ else
+ pd->fimc_bus_type = pd->sensor_bus_type;
+
+ ret = fimc_md_of_add_sensor(fmd, rem, index);
+ of_node_put(rem);
+
+ return ret;
+}
+
+/* Register all SoC external sub-devices */
+static int fimc_md_of_sensors_register(struct fimc_md *fmd,
+ struct device_node *np)
+{
+ struct device_node *parent = fmd->pdev->dev.of_node;
+ struct device_node *node, *ports;
+ int index = 0;
+ int ret;
+
+ /* Attach sensors linked to MIPI CSI-2 receivers */
+ for_each_available_child_of_node(parent, node) {
+ struct device_node *port;
+
+ if (of_node_cmp(node->name, "csis"))
+ continue;
+ /* The csis node can have only port subnode. */
+ port = of_get_next_child(node, NULL);
+ if (!port)
+ continue;
+
+ ret = fimc_md_parse_port_node(fmd, port, index);
+ if (ret < 0)
+ return ret;
+ index++;
+ }
+
+ /* Attach sensors listed in the parallel-ports node */
+ ports = of_get_child_by_name(parent, "parallel-ports");
+ if (!ports)
+ return 0;
+
+ for_each_child_of_node(ports, node) {
+ ret = fimc_md_parse_port_node(fmd, node, index);
+ if (ret < 0)
+ break;
+ index++;
+ }
+
+ return 0;
+}
+