struct tegra_vi_channel *chan = v4l2_get_subdev_hostdata(subdev);
struct tegra_csi_channel *csi_chan = to_csi_chan(subdev);
struct tegra_csi *csi = csi_chan->csi;
- int ret;
+ int ret, err;
ret = pm_runtime_get_sync(csi->dev);
if (ret < 0) {
return ret;
}
+ if (csi_chan->mipi) {
+ ret = tegra_mipi_enable(csi_chan->mipi);
+ if (ret < 0) {
+ dev_err(csi->dev,
+ "failed to enable MIPI pads: %d\n", ret);
+ goto rpm_put;
+ }
+
+ /*
+ * CSI MIPI pads PULLUP, PULLDN and TERM impedances need to
+ * be calibrated after power on.
+ * So, trigger the calibration start here and results will
+ * be latched and applied to the pads when link is in LP11
+ * state during start of sensor streaming.
+ */
+ ret = tegra_mipi_start_calibration(csi_chan->mipi);
+ if (ret < 0) {
+ dev_err(csi->dev,
+ "failed to start MIPI calibration: %d\n", ret);
+ goto disable_mipi;
+ }
+ }
+
csi_chan->pg_mode = chan->pg_mode;
ret = csi->ops->csi_start_streaming(csi_chan);
if (ret < 0)
- goto rpm_put;
+ goto finish_calibration;
return 0;
+finish_calibration:
+ if (csi_chan->mipi)
+ tegra_mipi_finish_calibration(csi_chan->mipi);
+disable_mipi:
+ if (csi_chan->mipi) {
+ err = tegra_mipi_disable(csi_chan->mipi);
+ if (err < 0)
+ dev_err(csi->dev,
+ "failed to disable MIPI pads: %d\n", err);
+ }
+
rpm_put:
pm_runtime_put(csi->dev);
return ret;
{
struct tegra_csi_channel *csi_chan = to_csi_chan(subdev);
struct tegra_csi *csi = csi_chan->csi;
+ int err;
csi->ops->csi_stop_streaming(csi_chan);
+ if (csi_chan->mipi) {
+ err = tegra_mipi_disable(csi_chan->mipi);
+ if (err < 0)
+ dev_err(csi->dev,
+ "failed to disable MIPI pads: %d\n", err);
+ }
+
pm_runtime_put(csi->dev);
return 0;
unsigned int num_pads)
{
struct tegra_csi_channel *chan;
+ int ret = 0;
chan = kzalloc(sizeof(*chan), GFP_KERNEL);
if (!chan)
chan->pads[0].flags = MEDIA_PAD_FL_SOURCE;
}
- return 0;
+ if (IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG))
+ return 0;
+
+ chan->mipi = tegra_mipi_request(csi->dev, node);
+ if (IS_ERR(chan->mipi)) {
+ ret = PTR_ERR(chan->mipi);
+ dev_err(csi->dev, "failed to get mipi device: %d\n", ret);
+ }
+
+ return ret;
}
static int tegra_csi_tpg_channels_alloc(struct tegra_csi *csi)
struct tegra_csi_channel *chan, *tmp;
list_for_each_entry_safe(chan, tmp, &csi->csi_chans, list) {
+ if (chan->mipi)
+ tegra_mipi_free(chan->mipi);
+
subdev = &chan->subdev;
if (subdev->dev) {
if (!IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG))
static int tegra_channel_enable_stream(struct tegra_vi_channel *chan)
{
struct v4l2_subdev *csi_subdev, *src_subdev;
- int ret;
+ struct tegra_csi_channel *csi_chan;
+ int ret, err;
/*
* Tegra CSI receiver can detect the first LP to HS transition.
if (IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG))
return 0;
+ csi_chan = v4l2_get_subdevdata(csi_subdev);
+ /*
+ * TRM has incorrectly documented to wait for done status from
+ * calibration logic after CSI interface power on.
+ * As per the design, calibration results are latched and applied
+ * to the pads only when the link is in LP11 state which will happen
+ * during the sensor stream-on.
+ * CSI subdev stream-on triggers start of MIPI pads calibration.
+ * Wait for calibration to finish here after sensor subdev stream-on.
+ */
src_subdev = tegra_channel_get_remote_source_subdev(chan);
ret = v4l2_subdev_call(src_subdev, video, s_stream, true);
- if (ret < 0 && ret != -ENOIOCTLCMD) {
- v4l2_subdev_call(csi_subdev, video, s_stream, false);
- return ret;
- }
+ err = tegra_mipi_finish_calibration(csi_chan->mipi);
+
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ goto err_disable_csi_stream;
+
+ if (err < 0)
+ dev_warn(csi_chan->csi->dev,
+ "MIPI calibration failed: %d\n", err);
return 0;
+
+err_disable_csi_stream:
+ v4l2_subdev_call(csi_subdev, video, s_stream, false);
+ return ret;
}
static int tegra_channel_disable_stream(struct tegra_vi_channel *chan)