diff --git a/drivers/media/platform/rockchip/cif/capture.c b/drivers/media/platform/rockchip/cif/capture.c index 058966e0f8e5..4966e2387101 100644 --- a/drivers/media/platform/rockchip/cif/capture.c +++ b/drivers/media/platform/rockchip/cif/capture.c @@ -2930,6 +2930,47 @@ static void rkcif_release_rdbk_buf(struct rkcif_stream *stream) } +static void rkcif_detach_sync_mode(struct rkcif_device *cif_dev) +{ + int i = 0; + struct rkcif_hw *hw = cif_dev->hw_dev; + struct rkcif_device *tmp_dev; + + if ((!cif_dev->sync_type) || + (atomic_read(&cif_dev->pipe.stream_cnt) != 0)) + return; + + hw->sync_config.streaming_cnt--; + if (cif_dev->sync_type == EXTERNAL_MASTER_MODE) { + for (i = 0; i < hw->sync_config.ext_master.count; i++) { + tmp_dev = hw->sync_config.ext_master.cif_dev[i]; + if (tmp_dev == cif_dev) { + hw->sync_config.ext_master.is_streaming[i] = false; + break; + } + } + } + if (cif_dev->sync_type == INTERNAL_MASTER_MODE) + hw->sync_config.int_master.is_streaming[0] = false; + if (cif_dev->sync_type == SLAVE_MODE) { + for (i = 0; i < hw->sync_config.slave.count; i++) { + tmp_dev = hw->sync_config.slave.cif_dev[i]; + if (tmp_dev == cif_dev) { + hw->sync_config.slave.is_streaming[i] = false; + break; + } + } + } + + if (!hw->sync_config.streaming_cnt && hw->sync_config.is_attach) { + hw->sync_config.is_attach = false; + hw->sync_config.mode = RKCIF_NOSYNC_MODE; + hw->sync_config.dev_cnt = 0; + for (i = 0; i < hw->dev_num; i++) + hw->cif_dev[i]->sync_type = NO_SYNC_MODE; + } +} + void rkcif_do_stop_stream(struct rkcif_stream *stream, unsigned int mode) { @@ -3046,7 +3087,7 @@ void rkcif_do_stop_stream(struct rkcif_stream *stream, rkcif_destroy_dummy_buf(stream); v4l2_info(&dev->v4l2_dev, "stream[%d] stopping finished\n", stream->id); - + rkcif_detach_sync_mode(dev); mutex_unlock(&dev->stream_lock); } @@ -3799,6 +3840,68 @@ static int rkcif_stream_start(struct rkcif_stream *stream, unsigned int mode) return 0; } +static void rkcif_attach_sync_mode(struct rkcif_hw *hw) +{ + struct rkcif_device *dev; + int i = 0; + int ret = 0; + int sync_type = 0; + int count = 0; + + if (hw->sync_config.is_attach) + return; + + memset(&hw->sync_config, 0, sizeof(struct rkcif_multi_sync_config)); + for (i = 0; i < hw->dev_num; i++) { + dev = hw->cif_dev[i]; + ret = v4l2_subdev_call(dev->terminal_sensor.sd, + core, ioctl, + RKMODULE_GET_SYNC_MODE, + &sync_type); + if (!ret) { + if (sync_type == EXTERNAL_MASTER_MODE) { + count = hw->sync_config.ext_master.count; + hw->sync_config.ext_master.cif_dev[count] = dev; + hw->sync_config.ext_master.count++; + hw->sync_config.dev_cnt++; + dev->sync_type = EXTERNAL_MASTER_MODE; + } else if (sync_type == INTERNAL_MASTER_MODE) { + count = hw->sync_config.int_master.count; + hw->sync_config.int_master.cif_dev[count] = dev; + hw->sync_config.int_master.count++; + hw->sync_config.dev_cnt++; + dev->sync_type = INTERNAL_MASTER_MODE; + } else if (sync_type == SLAVE_MODE) { + count = hw->sync_config.slave.count; + hw->sync_config.slave.cif_dev[count] = dev; + hw->sync_config.slave.count++; + hw->sync_config.dev_cnt++; + dev->sync_type = SLAVE_MODE; + } + } + } + if (hw->sync_config.int_master.count == 1) { + if (hw->sync_config.ext_master.count) { + hw->sync_config.mode = RKCIF_MASTER_MASTER; + hw->sync_config.is_attach = true; + } else if (hw->sync_config.slave.count) { + hw->sync_config.mode = RKCIF_MASTER_SLAVE; + hw->sync_config.is_attach = true; + } else { + dev_info(hw->dev, + "Missing slave device, do not use sync mode\n"); + } + if (hw->sync_config.ext_master.count && + hw->sync_config.slave.count) + dev_info(hw->dev, + "There are two types of slave devices, it may cause problems\n"); + } else { + dev_info(hw->dev, + "Only support one master device, master device count %d\n", + hw->sync_config.int_master.count); + } +} + int rkcif_do_start_stream(struct rkcif_stream *stream, unsigned int mode) { struct rkcif_vdev_node *node = &stream->vnode; @@ -3813,6 +3916,7 @@ int rkcif_do_start_stream(struct rkcif_stream *stream, unsigned int mode) v4l2_info(&dev->v4l2_dev, "stream[%d] start streaming\n", stream->id); mutex_lock(&dev->stream_lock); + rkcif_attach_sync_mode(dev->hw_dev); if ((stream->cur_stream_mode & RKCIF_STREAM_MODE_CAPTURE) == mode) { ret = -EBUSY; v4l2_err(v4l2_dev, "stream in busy state\n"); diff --git a/drivers/media/platform/rockchip/cif/dev.c b/drivers/media/platform/rockchip/cif/dev.c index 9ec6005e7bc9..829ccd3a8e97 100644 --- a/drivers/media/platform/rockchip/cif/dev.c +++ b/drivers/media/platform/rockchip/cif/dev.c @@ -814,6 +814,71 @@ static int rkcif_pipeline_close(struct rkcif_pipeline *p) return 0; } +static void rkcif_set_sensor_streamon_in_sync_mode(struct rkcif_device *cif_dev) +{ + struct rkcif_hw *hw = cif_dev->hw_dev; + struct rkcif_device *dev = NULL; + int i = 0; + int on = 1; + int ret = 0; + bool is_streaming = false; + + if (cif_dev->sync_type) { + hw->sync_config.streaming_cnt++; + if (hw->sync_config.streaming_cnt < hw->sync_config.dev_cnt) + return; + } else { + return; + } + + if (hw->sync_config.mode == RKCIF_MASTER_MASTER || + hw->sync_config.mode == RKCIF_MASTER_SLAVE) { + for (i = 0; i < hw->sync_config.slave.count; i++) { + dev = hw->sync_config.slave.cif_dev[i]; + is_streaming = hw->sync_config.slave.is_streaming[i]; + if (!is_streaming) { + ret = v4l2_subdev_call(dev->terminal_sensor.sd, core, ioctl, + RKMODULE_SET_QUICK_STREAM, &on); + if (!ret) + dev_info(dev->dev, + "set RKMODULE_SET_QUICK_STREAM failed\n"); + hw->sync_config.slave.is_streaming[i] = true; + } + v4l2_dbg(3, rkcif_debug, &dev->v4l2_dev, + "quick stream in sync mode, slave_dev[%d]\n", i); + + } + for (i = 0; i < hw->sync_config.ext_master.count; i++) { + dev = hw->sync_config.ext_master.cif_dev[i]; + is_streaming = hw->sync_config.ext_master.is_streaming[i]; + if (!is_streaming) { + ret = v4l2_subdev_call(dev->terminal_sensor.sd, core, ioctl, + RKMODULE_SET_QUICK_STREAM, &on); + if (!ret) + dev_info(dev->dev, + "set RKMODULE_SET_QUICK_STREAM failed\n"); + hw->sync_config.ext_master.is_streaming[i] = true; + } + v4l2_dbg(3, rkcif_debug, &dev->v4l2_dev, + "quick stream in sync mode, ext_master_dev[%d]\n", i); + } + for (i = 0; i < hw->sync_config.int_master.count; i++) { + dev = hw->sync_config.int_master.cif_dev[i]; + is_streaming = hw->sync_config.int_master.is_streaming[i]; + if (!is_streaming) { + ret = v4l2_subdev_call(dev->terminal_sensor.sd, core, ioctl, + RKMODULE_SET_QUICK_STREAM, &on); + if (!ret) + dev_info(hw->dev, + "set RKMODULE_SET_QUICK_STREAM failed\n"); + hw->sync_config.int_master.is_streaming[i] = true; + } + v4l2_dbg(3, rkcif_debug, &dev->v4l2_dev, + "quick stream in sync mode, int_master_dev[%d]\n", i); + } + } +} + /* * stream-on order: isp_subdev, mipi dphy, sensor * stream-off order: mipi dphy, sensor, isp_subdev @@ -855,6 +920,8 @@ static int rkcif_pipeline_set_stream(struct rkcif_pipeline *p, bool on) if (on && ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) goto err_stream_off; } + if (on) + rkcif_set_sensor_streamon_in_sync_mode(cif_dev); } else { if (!on && atomic_dec_return(&p->stream_cnt) > 0) return 0; @@ -906,6 +973,9 @@ static int rkcif_pipeline_set_stream(struct rkcif_pipeline *p, bool on) if (on && ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) goto err_stream_off; } + + if (on) + rkcif_set_sensor_streamon_in_sync_mode(cif_dev); } } @@ -1553,6 +1623,7 @@ int rkcif_plat_init(struct rkcif_device *cif_dev, struct device_node *node, int cif_dev->pipe.set_stream = rkcif_pipeline_set_stream; cif_dev->isr_hdl = rkcif_irq_handler; cif_dev->id_use_cnt = 0; + cif_dev->sync_type = NO_SYNC_MODE; if (cif_dev->chip_id == CHIP_RV1126_CIF_LITE) cif_dev->isr_hdl = rkcif_irq_lite_handler; diff --git a/drivers/media/platform/rockchip/cif/dev.h b/drivers/media/platform/rockchip/cif/dev.h index 6073c0ab3e18..703b46be4488 100644 --- a/drivers/media/platform/rockchip/cif/dev.h +++ b/drivers/media/platform/rockchip/cif/dev.h @@ -709,6 +709,7 @@ struct rkcif_device { bool reset_work_cancel; bool iommu_en; bool is_use_dummybuf; + int sync_type; }; extern struct platform_driver rkcif_plat_drv; diff --git a/drivers/media/platform/rockchip/cif/hw.c b/drivers/media/platform/rockchip/cif/hw.c index d2e0e451371e..37e1d6c886c3 100644 --- a/drivers/media/platform/rockchip/cif/hw.c +++ b/drivers/media/platform/rockchip/cif/hw.c @@ -994,6 +994,8 @@ static int rkcif_plat_hw_probe(struct platform_device *pdev) cif_hw->irq = irq; cif_hw->match_data = data; cif_hw->chip_id = data->chip_id; + cif_hw->sync_config.is_attach = false; + cif_hw->sync_config.mode = RKCIF_NOSYNC_MODE; if (data->chip_id >= CHIP_RK1808_CIF) { res = platform_get_resource_byname(pdev, IORESOURCE_MEM, diff --git a/drivers/media/platform/rockchip/cif/hw.h b/drivers/media/platform/rockchip/cif/hw.h index 401af2bc33d5..b5698f7c35e6 100644 --- a/drivers/media/platform/rockchip/cif/hw.h +++ b/drivers/media/platform/rockchip/cif/hw.h @@ -33,6 +33,38 @@ #define write_cif_reg_and(base, addr, val) \ writel(readl((addr) + (base)) & (val), (addr) + (base)) +/* + * multi sensor sync mode + * RKCIF_NOSYNC_MODE: not used sync mode + * RKCIF_MASTER_MASTER: internal master->external master + * RKCIF_MASTER_SLAVE: internal master->slave + * RKCIF_MASTER_MASTER: pwm/gpio->external master + * RKCIF_MASTER_MASTER: pwm/gpio->slave + */ +enum rkcif_sync_mode { + RKCIF_NOSYNC_MODE, + RKCIF_MASTER_MASTER, + RKCIF_MASTER_SLAVE, + RKCIF_EXT_MASTER, + RKCIF_EXT_SLAVE, +}; + +struct rkcif_sync_dev { + struct rkcif_device *cif_dev[RKCIF_DEV_MAX]; + int count; + bool is_streaming[RKCIF_DEV_MAX]; +}; + +struct rkcif_multi_sync_config { + struct rkcif_sync_dev int_master; + struct rkcif_sync_dev ext_master; + struct rkcif_sync_dev slave; + enum rkcif_sync_mode mode; + int dev_cnt; + int streaming_cnt; + bool is_attach; +}; + /* * add new chip id in tail in time order * by increasing to distinguish cif version @@ -88,6 +120,7 @@ struct rkcif_hw { atomic_t power_cnt; const struct rkcif_hw_match_data *match_data; struct mutex dev_lock; + struct rkcif_multi_sync_config sync_config; }; void rkcif_hw_soft_reset(struct rkcif_hw *cif_hw, bool is_rst_iommu);