summaryrefslogtreecommitdiff
path: root/drivers/staging/media/tegra-video/vi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/media/tegra-video/vi.c')
-rw-r--r--drivers/staging/media/tegra-video/vi.c222
1 files changed, 64 insertions, 158 deletions
diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c
index 2f1aff7e8717..79284c3b6cae 100644
--- a/drivers/staging/media/tegra-video/vi.c
+++ b/drivers/staging/media/tegra-video/vi.c
@@ -30,15 +30,19 @@
#include "vi.h"
#include "video.h"
-#define MAX_CID_CONTROLS 1
-
-static const struct tegra_video_format tegra_default_format = {
- .img_dt = TEGRA_IMAGE_DT_RAW10,
- .bit_width = 10,
- .code = MEDIA_BUS_FMT_SRGGB10_1X10,
- .bpp = 2,
- .img_fmt = TEGRA_IMAGE_FORMAT_DEF,
- .fourcc = V4L2_PIX_FMT_SRGGB10,
+#define MAX_CID_CONTROLS 3
+
+/**
+ * struct tegra_vi_graph_entity - Entity in the video graph
+ *
+ * @asd: subdev asynchronous registration information
+ * @entity: media entity from the corresponding V4L2 subdev
+ * @subdev: V4L2 subdev
+ */
+struct tegra_vi_graph_entity {
+ struct v4l2_async_subdev asd;
+ struct media_entity *entity;
+ struct v4l2_subdev *subdev;
};
static inline struct tegra_vi *
@@ -98,6 +102,7 @@ tegra_get_format_by_fourcc(struct tegra_vi *vi, u32 fourcc)
/*
* videobuf2 queue operations
*/
+
static int tegra_channel_queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers,
unsigned int *nplanes,
@@ -113,6 +118,9 @@ static int tegra_channel_queue_setup(struct vb2_queue *vq,
sizes[0] = chan->format.sizeimage;
alloc_devs[0] = chan->vi->dev;
+ if (chan->vi->ops->channel_queue_setup)
+ chan->vi->ops->channel_queue_setup(chan);
+
return 0;
}
@@ -164,6 +172,9 @@ tegra_channel_get_remote_csi_subdev(struct tegra_vi_channel *chan)
return media_entity_to_v4l2_subdev(pad->entity);
}
+/*
+ * Walk up the chain until the initial source (e.g. image sensor)
+ */
struct v4l2_subdev *
tegra_channel_get_remote_source_subdev(struct tegra_vi_channel *chan)
{
@@ -190,49 +201,15 @@ tegra_channel_get_remote_source_subdev(struct tegra_vi_channel *chan)
static int tegra_channel_enable_stream(struct tegra_vi_channel *chan)
{
- struct v4l2_subdev *csi_subdev, *src_subdev;
- struct tegra_csi_channel *csi_chan;
- int ret, err;
+ struct v4l2_subdev *subdev;
+ int ret;
- /*
- * Tegra CSI receiver can detect the first LP to HS transition.
- * So, start the CSI stream-on prior to sensor stream-on and
- * vice-versa for stream-off.
- */
- csi_subdev = tegra_channel_get_remote_csi_subdev(chan);
- ret = v4l2_subdev_call(csi_subdev, video, s_stream, true);
+ subdev = tegra_channel_get_remote_csi_subdev(chan);
+ ret = v4l2_subdev_call(subdev, video, s_stream, true);
if (ret < 0 && ret != -ENOIOCTLCMD)
return ret;
- 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);
- 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)
@@ -240,18 +217,6 @@ static int tegra_channel_disable_stream(struct tegra_vi_channel *chan)
struct v4l2_subdev *subdev;
int ret;
- /*
- * Stream-off subdevices in reverse order to stream-on.
- * Remote source subdev in TPG mode is same as CSI subdev.
- */
- subdev = tegra_channel_get_remote_source_subdev(chan);
- ret = v4l2_subdev_call(subdev, video, s_stream, false);
- if (ret < 0 && ret != -ENOIOCTLCMD)
- return ret;
-
- if (IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG))
- return 0;
-
subdev = tegra_channel_get_remote_csi_subdev(chan);
ret = v4l2_subdev_call(subdev, video, s_stream, false);
if (ret < 0 && ret != -ENOIOCTLCMD)
@@ -457,36 +422,6 @@ static int tegra_channel_get_format(struct file *file, void *fh,
return 0;
}
-static void tegra_channel_fmt_align(struct tegra_vi_channel *chan,
- struct v4l2_pix_format *pix,
- unsigned int bpp)
-{
- unsigned int min_bpl;
- unsigned int max_bpl;
- unsigned int bpl;
-
- /*
- * The transfer alignment requirements are expressed in bytes.
- * Clamp the requested width and height to the limits.
- */
- pix->width = clamp(pix->width, TEGRA_MIN_WIDTH, TEGRA_MAX_WIDTH);
- pix->height = clamp(pix->height, TEGRA_MIN_HEIGHT, TEGRA_MAX_HEIGHT);
-
- /* Clamp the requested bytes per line value. If the maximum bytes per
- * line value is zero, the module doesn't support user configurable
- * line sizes. Override the requested value with the minimum in that
- * case.
- */
- min_bpl = pix->width * bpp;
- max_bpl = rounddown(TEGRA_MAX_WIDTH, SURFACE_ALIGN_BYTES);
- bpl = roundup(pix->bytesperline, SURFACE_ALIGN_BYTES);
-
- pix->bytesperline = clamp(bpl, min_bpl, max_bpl);
- pix->sizeimage = pix->bytesperline * pix->height;
- if (pix->pixelformat == V4L2_PIX_FMT_NV16)
- pix->sizeimage *= 2;
-}
-
static int __tegra_channel_try_format(struct tegra_vi_channel *chan,
struct v4l2_pix_format *pix)
{
@@ -563,7 +498,7 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan,
return ret;
v4l2_fill_pix_format(pix, &fmt.format);
- tegra_channel_fmt_align(chan, pix, fmtinfo->bpp);
+ chan->vi->ops->vi_fmt_align(pix, fmtinfo->bpp);
__v4l2_subdev_state_free(sd_state);
@@ -616,7 +551,7 @@ static int tegra_channel_set_format(struct file *file, void *fh,
return ret;
v4l2_fill_pix_format(pix, &fmt.format);
- tegra_channel_fmt_align(chan, pix, fmtinfo->bpp);
+ chan->vi->ops->vi_fmt_align(pix, fmtinfo->bpp);
chan->format = *pix;
chan->fmtinfo = fmtinfo;
@@ -652,7 +587,7 @@ static int tegra_channel_set_subdev_active_fmt(struct tegra_vi_channel *chan)
chan->format.bytesperline = chan->format.width * chan->fmtinfo->bpp;
chan->format.sizeimage = chan->format.bytesperline *
chan->format.height;
- tegra_channel_fmt_align(chan, &chan->format, chan->fmtinfo->bpp);
+ chan->vi->ops->vi_fmt_align(&chan->format, chan->fmtinfo->bpp);
tegra_channel_update_gangports(chan);
return 0;
@@ -821,7 +756,7 @@ static int tegra_channel_s_dv_timings(struct file *file, void *fh,
chan->format.height = bt->height;
chan->format.bytesperline = bt->width * chan->fmtinfo->bpp;
chan->format.sizeimage = chan->format.bytesperline * bt->height;
- tegra_channel_fmt_align(chan, &chan->format, chan->fmtinfo->bpp);
+ chan->vi->ops->vi_fmt_align(&chan->format, chan->fmtinfo->bpp);
tegra_channel_update_gangports(chan);
return 0;
@@ -977,6 +912,12 @@ static int vi_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_TEGRA_SYNCPT_TIMEOUT_RETRY:
chan->syncpt_timeout_retry = ctrl->val;
break;
+ case V4L2_CID_HFLIP:
+ chan->hflip = ctrl->val;
+ break;
+ case V4L2_CID_VFLIP:
+ chan->vflip = ctrl->val;
+ break;
default:
return -EINVAL;
}
@@ -1048,6 +989,12 @@ static int tegra_channel_setup_ctrl_handler(struct tegra_vi_channel *chan)
v4l2_ctrl_handler_free(&chan->ctrl_handler);
return ret;
}
+
+ if (chan->vi->soc->has_h_v_flip) {
+ v4l2_ctrl_new_std(&chan->ctrl_handler, &vi_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&chan->ctrl_handler, &vi_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
+ }
+
#endif
/* setup the controls */
@@ -1119,7 +1066,7 @@ static int vi_fmts_bitmap_init(struct tegra_vi_channel *chan)
* there are no matched formats.
*/
if (!match_code) {
- match_code = tegra_default_format.code;
+ match_code = chan->vi->soc->default_video_format->code;
index = tegra_get_format_idx_by_code(chan->vi, match_code, 0);
if (WARN_ON(index < 0))
return -EINVAL;
@@ -1133,21 +1080,11 @@ static int vi_fmts_bitmap_init(struct tegra_vi_channel *chan)
return 0;
}
-static void tegra_channel_host1x_syncpts_free(struct tegra_vi_channel *chan)
-{
- int i;
-
- for (i = 0; i < chan->numgangports; i++) {
- host1x_syncpt_put(chan->mw_ack_sp[i]);
- host1x_syncpt_put(chan->frame_start_sp[i]);
- }
-}
-
static void tegra_channel_cleanup(struct tegra_vi_channel *chan)
{
v4l2_ctrl_handler_free(&chan->ctrl_handler);
media_entity_cleanup(&chan->video.entity);
- tegra_channel_host1x_syncpts_free(chan);
+ chan->vi->ops->channel_host1x_syncpt_free(chan);
mutex_destroy(&chan->video_lock);
}
@@ -1165,42 +1102,6 @@ void tegra_channels_cleanup(struct tegra_vi *vi)
}
}
-static int tegra_channel_host1x_syncpt_init(struct tegra_vi_channel *chan)
-{
- struct tegra_vi *vi = chan->vi;
- unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED;
- struct host1x_syncpt *fs_sp;
- struct host1x_syncpt *mw_sp;
- int ret, i;
-
- for (i = 0; i < chan->numgangports; i++) {
- fs_sp = host1x_syncpt_request(&vi->client, flags);
- if (!fs_sp) {
- dev_err(vi->dev, "failed to request frame start syncpoint\n");
- ret = -ENOMEM;
- goto free_syncpts;
- }
-
- mw_sp = host1x_syncpt_request(&vi->client, flags);
- if (!mw_sp) {
- dev_err(vi->dev, "failed to request memory ack syncpoint\n");
- host1x_syncpt_put(fs_sp);
- ret = -ENOMEM;
- goto free_syncpts;
- }
-
- chan->frame_start_sp[i] = fs_sp;
- chan->mw_ack_sp[i] = mw_sp;
- spin_lock_init(&chan->sp_incr_lock[i]);
- }
-
- return 0;
-
-free_syncpts:
- tegra_channel_host1x_syncpts_free(chan);
- return ret;
-}
-
static int tegra_channel_init(struct tegra_vi_channel *chan)
{
struct tegra_vi *vi = chan->vi;
@@ -1216,7 +1117,7 @@ static int tegra_channel_init(struct tegra_vi_channel *chan)
init_waitqueue_head(&chan->done_wait);
/* initialize the video format */
- chan->fmtinfo = &tegra_default_format;
+ chan->fmtinfo = chan->vi->soc->default_video_format;
chan->format.pixelformat = chan->fmtinfo->fourcc;
chan->format.colorspace = V4L2_COLORSPACE_SRGB;
chan->format.field = V4L2_FIELD_NONE;
@@ -1224,9 +1125,9 @@ static int tegra_channel_init(struct tegra_vi_channel *chan)
chan->format.height = TEGRA_DEF_HEIGHT;
chan->format.bytesperline = TEGRA_DEF_WIDTH * chan->fmtinfo->bpp;
chan->format.sizeimage = chan->format.bytesperline * TEGRA_DEF_HEIGHT;
- tegra_channel_fmt_align(chan, &chan->format, chan->fmtinfo->bpp);
+ vi->ops->vi_fmt_align(&chan->format, chan->fmtinfo->bpp);
- ret = tegra_channel_host1x_syncpt_init(chan);
+ ret = vi->ops->channel_host1x_syncpt_init(chan);
if (ret)
return ret;
@@ -1289,7 +1190,7 @@ free_v4l2_ctrl_hdl:
cleanup_media:
media_entity_cleanup(&chan->video.entity);
free_syncpts:
- tegra_channel_host1x_syncpts_free(chan);
+ vi->ops->channel_host1x_syncpt_free(chan);
return ret;
}
@@ -1351,7 +1252,7 @@ static int tegra_vi_channels_alloc(struct tegra_vi *vi)
struct device_node *node = vi->dev->of_node;
struct device_node *ep = NULL;
struct device_node *ports;
- struct device_node *port;
+ struct device_node *port = NULL;
unsigned int port_num;
struct device_node *parent;
struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = 0 };
@@ -1360,7 +1261,7 @@ static int tegra_vi_channels_alloc(struct tegra_vi *vi)
ports = of_get_child_by_name(node, "ports");
if (!ports)
- return -ENODEV;
+ return dev_err_probe(vi->dev, -ENODEV, "%pOF: missing 'ports' node\n", node);
for_each_child_of_node(ports, port) {
if (!of_node_name_eq(port, "port"))
@@ -1374,7 +1275,6 @@ static int tegra_vi_channels_alloc(struct tegra_vi *vi)
dev_err(vi->dev, "invalid port num %d for %pOF\n",
port_num, port);
ret = -EINVAL;
- of_node_put(port);
goto cleanup;
}
@@ -1397,13 +1297,12 @@ static int tegra_vi_channels_alloc(struct tegra_vi *vi)
lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes;
ret = tegra_vi_channel_alloc(vi, port_num, port, lanes);
- if (ret < 0) {
- of_node_put(port);
+ if (ret < 0)
goto cleanup;
- }
}
cleanup:
+ of_node_put(port);
of_node_put(ports);
return ret;
}
@@ -1858,10 +1757,10 @@ static int tegra_vi_graph_init(struct tegra_vi *vi)
* Walk the links to parse the full graph. Each channel will have
* one endpoint of the composite node. Start by parsing the
* composite node and parse the remote entities in turn.
- * Each channel will register v4l2 async notifier to make the graph
- * independent between the channels so we can the current channel
+ * Each channel will register a v4l2 async notifier to make the graph
+ * independent between the channels so we can skip the current channel
* in case of something wrong during graph parsing and continue with
- * next channels.
+ * the next channels.
*/
list_for_each_entry(chan, &vi->vi_chans, list) {
struct fwnode_handle *ep, *remote;
@@ -1920,11 +1819,8 @@ static int tegra_vi_init(struct host1x_client *client)
ret = tegra_vi_tpg_channels_alloc(vi);
else
ret = tegra_vi_channels_alloc(vi);
- if (ret < 0) {
- dev_err(vi->dev,
- "failed to allocate vi channels: %d\n", ret);
+ if (ret < 0)
goto free_chans;
- }
ret = tegra_vi_channels_init(vi);
if (ret < 0)
@@ -2026,6 +1922,9 @@ static int tegra_vi_probe(struct platform_device *pdev)
vi->client.ops = &vi_client_ops;
vi->client.dev = &pdev->dev;
+ if (vi->ops->vi_enable)
+ vi->ops->vi_enable(vi, true);
+
ret = host1x_client_register(&vi->client);
if (ret < 0) {
dev_err(&pdev->dev,
@@ -2036,6 +1935,8 @@ static int tegra_vi_probe(struct platform_device *pdev)
return 0;
rpm_disable:
+ if (vi->ops->vi_enable)
+ vi->ops->vi_enable(vi, false);
pm_runtime_disable(&pdev->dev);
return ret;
}
@@ -2046,12 +1947,17 @@ static int tegra_vi_remove(struct platform_device *pdev)
host1x_client_unregister(&vi->client);
+ if (vi->ops->vi_enable)
+ vi->ops->vi_enable(vi, false);
pm_runtime_disable(&pdev->dev);
return 0;
}
static const struct of_device_id tegra_vi_of_id_table[] = {
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+ { .compatible = "nvidia,tegra20-vi", .data = &tegra20_vi_soc },
+#endif
#if defined(CONFIG_ARCH_TEGRA_210_SOC)
{ .compatible = "nvidia,tegra210-vi", .data = &tegra210_vi_soc },
#endif