|
@@ -251,7 +251,7 @@ static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd,
|
|
|
sd->grp_id = GRP_ID_SENSOR;
|
|
|
|
|
|
v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice %s\n",
|
|
|
- s_info->pdata.board_info->type);
|
|
|
+ sd->name);
|
|
|
return sd;
|
|
|
}
|
|
|
|
|
@@ -263,13 +263,191 @@ static void fimc_md_unregister_sensor(struct v4l2_subdev *sd)
|
|
|
if (!client)
|
|
|
return;
|
|
|
v4l2_device_unregister_subdev(sd);
|
|
|
- adapter = client->adapter;
|
|
|
- i2c_unregister_device(client);
|
|
|
- if (adapter)
|
|
|
- i2c_put_adapter(adapter);
|
|
|
+
|
|
|
+ if (!client->dev.of_node) {
|
|
|
+ adapter = client->adapter;
|
|
|
+ i2c_unregister_device(client);
|
|
|
+ if (adapter)
|
|
|
+ i2c_put_adapter(adapter);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_OF
|
|
|
+/* 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;
|
|
|
+}
|
|
|
+
|
|
|
static int __of_get_csis_id(struct device_node *np)
|
|
|
{
|
|
|
u32 reg = 0;
|
|
@@ -281,14 +459,17 @@ static int __of_get_csis_id(struct device_node *np)
|
|
|
return reg - FIMC_INPUT_MIPI_CSI2_0;
|
|
|
}
|
|
|
#else
|
|
|
+#define fimc_md_of_sensors_register(fmd, np) (-ENOSYS)
|
|
|
#define __of_get_csis_id(np) (-ENOSYS)
|
|
|
#endif
|
|
|
|
|
|
static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
|
|
|
{
|
|
|
struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data;
|
|
|
+ struct device_node *of_node = fmd->pdev->dev.of_node;
|
|
|
struct fimc_dev *fd = NULL;
|
|
|
- int num_clients, ret, i;
|
|
|
+ int num_clients = 0;
|
|
|
+ int ret, i;
|
|
|
|
|
|
/*
|
|
|
* Runtime resume one of the FIMC entities to make sure
|
|
@@ -299,34 +480,41 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
|
|
|
fd = fmd->fimc[i];
|
|
|
if (!fd)
|
|
|
return -ENXIO;
|
|
|
+
|
|
|
ret = pm_runtime_get_sync(&fd->pdev->dev);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
|
- WARN_ON(pdata->num_clients > ARRAY_SIZE(fmd->sensor));
|
|
|
- num_clients = min_t(u32, pdata->num_clients, ARRAY_SIZE(fmd->sensor));
|
|
|
+ if (of_node) {
|
|
|
+ fmd->num_sensors = 0;
|
|
|
+ ret = fimc_md_of_sensors_register(fmd, of_node);
|
|
|
+ } else if (pdata) {
|
|
|
+ WARN_ON(pdata->num_clients > ARRAY_SIZE(fmd->sensor));
|
|
|
+ num_clients = min_t(u32, pdata->num_clients,
|
|
|
+ ARRAY_SIZE(fmd->sensor));
|
|
|
+ fmd->num_sensors = num_clients;
|
|
|
|
|
|
- fmd->num_sensors = num_clients;
|
|
|
- for (i = 0; i < num_clients; i++) {
|
|
|
- struct v4l2_subdev *sd;
|
|
|
+ for (i = 0; i < num_clients; i++) {
|
|
|
+ struct v4l2_subdev *sd;
|
|
|
|
|
|
- fmd->sensor[i].pdata = pdata->source_info[i];
|
|
|
- ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true);
|
|
|
- if (ret)
|
|
|
- break;
|
|
|
- sd = fimc_md_register_sensor(fmd, &fmd->sensor[i]);
|
|
|
- ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], false);
|
|
|
-
|
|
|
- if (!IS_ERR(sd)) {
|
|
|
+ fmd->sensor[i].pdata = pdata->source_info[i];
|
|
|
+ ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true);
|
|
|
+ if (ret)
|
|
|
+ break;
|
|
|
+ sd = fimc_md_register_sensor(fmd, &fmd->sensor[i]);
|
|
|
+ ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], false);
|
|
|
+
|
|
|
+ if (IS_ERR(sd)) {
|
|
|
+ fmd->sensor[i].subdev = NULL;
|
|
|
+ ret = PTR_ERR(sd);
|
|
|
+ break;
|
|
|
+ }
|
|
|
fmd->sensor[i].subdev = sd;
|
|
|
- } else {
|
|
|
- fmd->sensor[i].subdev = NULL;
|
|
|
- ret = PTR_ERR(sd);
|
|
|
- break;
|
|
|
+ if (ret)
|
|
|
+ break;
|
|
|
}
|
|
|
- if (ret)
|
|
|
- break;
|
|
|
}
|
|
|
+
|
|
|
pm_runtime_put(&fd->pdev->dev);
|
|
|
return ret;
|
|
|
}
|
|
@@ -1037,7 +1225,7 @@ static int fimc_md_probe(struct platform_device *pdev)
|
|
|
if (ret)
|
|
|
goto err_unlock;
|
|
|
|
|
|
- if (dev->platform_data) {
|
|
|
+ if (dev->platform_data || dev->of_node) {
|
|
|
ret = fimc_md_register_sensor_entities(fmd);
|
|
|
if (ret)
|
|
|
goto err_unlock;
|