diff options
author | Álvaro Fernández Rojas <noltari@gmail.com> | 2022-05-16 23:40:32 +0200 |
---|---|---|
committer | Álvaro Fernández Rojas <noltari@gmail.com> | 2022-05-17 15:11:22 +0200 |
commit | 20ea6adbf199097c4f5f591ffee088340630dae4 (patch) | |
tree | d6719d95e136611a1c25bbf7789652d6d402779d /target/linux/bcm27xx/patches-5.15/950-0214-media-bcm2835-unicam-Add-embedded-data-node.patch | |
parent | bca05bd072180dc38ef740b37ded9572a6db1981 (diff) | |
download | upstream-20ea6adbf199097c4f5f591ffee088340630dae4.tar.gz upstream-20ea6adbf199097c4f5f591ffee088340630dae4.tar.bz2 upstream-20ea6adbf199097c4f5f591ffee088340630dae4.zip |
bcm27xx: add support for linux v5.15
Build system: x86_64
Build-tested: bcm2708, bcm2709, bcm2710, bcm2711
Run-tested: bcm2708/RPiB+, bcm2709/RPi3B, bcm2710/RPi3B, bcm2711/RPi4B
Signed-off-by: Marty Jones <mj8263788@gmail.com>
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
Diffstat (limited to 'target/linux/bcm27xx/patches-5.15/950-0214-media-bcm2835-unicam-Add-embedded-data-node.patch')
-rw-r--r-- | target/linux/bcm27xx/patches-5.15/950-0214-media-bcm2835-unicam-Add-embedded-data-node.patch | 1170 |
1 files changed, 1170 insertions, 0 deletions
diff --git a/target/linux/bcm27xx/patches-5.15/950-0214-media-bcm2835-unicam-Add-embedded-data-node.patch b/target/linux/bcm27xx/patches-5.15/950-0214-media-bcm2835-unicam-Add-embedded-data-node.patch new file mode 100644 index 0000000000..84f80b5559 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.15/950-0214-media-bcm2835-unicam-Add-embedded-data-node.patch @@ -0,0 +1,1170 @@ +From 85fc685aabde594162e398c048b901d31332c2e6 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck <naush@raspberrypi.com> +Date: Thu, 16 Apr 2020 11:35:41 +0100 +Subject: [PATCH] media: bcm2835-unicam: Add embedded data node. + +This patch adds a new node in the bcm2835-unicam driver to support +CSI-2 embedded data streams. The subdevice is queried to see if +embedded data is available from the sensor. + +Signed-off-by: Naushir Patuck <naush@raspberrypi.com> +--- + .../media/platform/bcm2835/bcm2835-unicam.c | 667 +++++++++++++----- + 1 file changed, 474 insertions(+), 193 deletions(-) + +--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c ++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c +@@ -109,8 +109,15 @@ MODULE_PARM_DESC(debug, "Debug level 0-3 + /* Define a nominal minimum image size */ + #define MIN_WIDTH 16 + #define MIN_HEIGHT 16 +-/* Maximum number of simulataneous streams Uncaim can handle. */ +-#define MAX_NODES 2 ++/* Default size of the embedded buffer */ ++#define UNICAM_EMBEDDED_SIZE 8192 ++ ++enum pad_types { ++ IMAGE_PAD, ++ METADATA_PAD, ++ MAX_NODES ++}; ++ + /* + * struct unicam_fmt - Unicam media bus format information + * @pixelformat: V4L2 pixel format FCC identifier. 0 if n/a. +@@ -327,6 +334,12 @@ static const struct unicam_fmt formats[] + .depth = 12, + .csi_dt = 0x2c, + }, ++ /* Embedded data format */ ++ { ++ .fourcc = V4L2_META_FMT_SENSOR_DATA, ++ .code = MEDIA_BUS_FMT_SENSOR_DATA, ++ .depth = 8, ++ } + }; + + struct unicam_dmaqueue { +@@ -348,7 +361,9 @@ struct unicam_cfg { + #define MAX_POSSIBLE_PIX_FMTS (ARRAY_SIZE(formats)) + + struct unicam_node { +- bool registered; ++ int registered; ++ int open; ++ int streaming; + unsigned int pad_id; + /* Pointer pointing to current v4l2_buffer */ + struct unicam_buffer *cur_frm; +@@ -374,6 +389,7 @@ struct unicam_node { + struct unicam_device *dev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; ++ unsigned int embedded_lines; + }; + + struct unicam_device { +@@ -401,8 +417,6 @@ struct unicam_device { + struct v4l2_subdev *sensor; + /* Pad config for the sensor */ + struct v4l2_subdev_pad_config *sensor_config; +- /* current input at the sub device */ +- int current_input; + + unsigned int virtual_channel; + enum v4l2_mbus_type bus_type; +@@ -413,10 +427,7 @@ struct unicam_device { + unsigned int bus_flags; + unsigned int max_data_lanes; + unsigned int active_data_lanes; +- +- struct v4l2_rect crop; +- /* Flag to denote that we are processing buffers */ +- int streaming; ++ bool sensor_embedded_data; + + struct unicam_node node[MAX_NODES]; + }; +@@ -488,6 +499,7 @@ static int check_mbus_format(struct unic + for (i = 0; !ret && i < MAX_ENUM_MBUS_CODE; i++) { + memset(&mbus_code, 0, sizeof(mbus_code)); + mbus_code.index = i; ++ mbus_code.pad = IMAGE_PAD; + mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; + + ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, +@@ -552,10 +564,11 @@ static int __subdev_get_format(struct un + } + + static int __subdev_set_format(struct unicam_device *dev, +- struct v4l2_mbus_framefmt *fmt) ++ struct v4l2_mbus_framefmt *fmt, int pad_id) + { + struct v4l2_subdev_format sd_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, ++ .pad = pad_id + }; + int ret; + +@@ -566,8 +579,12 @@ static int __subdev_set_format(struct un + if (ret < 0) + return ret; + +- unicam_dbg(1, dev, "%s %dx%d code:%04x\n", __func__, +- fmt->width, fmt->height, fmt->code); ++ if (pad_id == IMAGE_PAD) ++ unicam_dbg(1, dev, "%s %dx%d code:%04x\n", __func__, fmt->width, ++ fmt->height, fmt->code); ++ else ++ unicam_dbg(1, dev, "%s Embedded data code:%04x\n", __func__, ++ sd_fmt.format.code); + + return 0; + } +@@ -609,46 +626,70 @@ static int unicam_reset_format(struct un + struct v4l2_mbus_framefmt mbus_fmt; + int ret; + +- ret = __subdev_get_format(dev, &mbus_fmt, node->pad_id); +- if (ret) { +- unicam_err(dev, "Failed to get_format - ret %d\n", ret); +- return ret; +- } ++ if (dev->sensor_embedded_data || node->pad_id != METADATA_PAD) { ++ ret = __subdev_get_format(dev, &mbus_fmt, node->pad_id); ++ if (ret) { ++ unicam_err(dev, "Failed to get_format - ret %d\n", ret); ++ return ret; ++ } + +- if (mbus_fmt.code != dev->node[0].fmt->code) { +- unicam_err(dev, "code mismatch - fmt->code %08x, mbus_fmt.code %08x\n", +- dev->node[0].fmt->code, mbus_fmt.code); +- return ret; ++ if (mbus_fmt.code != node->fmt->code) { ++ unicam_err(dev, "code mismatch - fmt->code %08x, mbus_fmt.code %08x\n", ++ node->fmt->code, mbus_fmt.code); ++ return ret; ++ } + } + +- v4l2_fill_pix_format(&dev->node[0].v_fmt.fmt.pix, &mbus_fmt); +- dev->node[0].v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; +- +- unicam_calc_format_size_bpl(dev, dev->node[0].fmt, &dev->node[0].v_fmt); +- +- dev->node[0].m_fmt = mbus_fmt; ++ if (node->pad_id == IMAGE_PAD) { ++ v4l2_fill_pix_format(&node->v_fmt.fmt.pix, &mbus_fmt); ++ node->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ unicam_calc_format_size_bpl(dev, node->fmt, &node->v_fmt); ++ } else { ++ node->v_fmt.type = V4L2_BUF_TYPE_META_CAPTURE; ++ node->v_fmt.fmt.meta.dataformat = V4L2_META_FMT_SENSOR_DATA; ++ if (dev->sensor_embedded_data) { ++ node->v_fmt.fmt.meta.buffersize = ++ mbus_fmt.width * mbus_fmt.height; ++ node->embedded_lines = mbus_fmt.height; ++ } else { ++ node->v_fmt.fmt.meta.buffersize = UNICAM_EMBEDDED_SIZE; ++ node->embedded_lines = 1; ++ } ++ } + ++ node->m_fmt = mbus_fmt; + return 0; + } + +-static void unicam_wr_dma_addr(struct unicam_device *dev, dma_addr_t dmaaddr) ++static void unicam_wr_dma_addr(struct unicam_device *dev, dma_addr_t dmaaddr, ++ int pad_id) + { ++ dma_addr_t endaddr; ++ + /* + * dmaaddr should be a 32-bit address with the top two bits set to 0x3 + * to signify uncached access through the Videocore memory controller. + */ + BUG_ON((dmaaddr >> 30) != 0x3); + +- reg_write(&dev->cfg, UNICAM_IBSA0, dmaaddr); +- reg_write(&dev->cfg, UNICAM_IBEA0, +- dmaaddr + dev->node[0].v_fmt.fmt.pix.sizeimage); ++ if (pad_id == IMAGE_PAD) { ++ endaddr = dmaaddr + ++ dev->node[IMAGE_PAD].v_fmt.fmt.pix.sizeimage; ++ reg_write(&dev->cfg, UNICAM_IBSA0, dmaaddr); ++ reg_write(&dev->cfg, UNICAM_IBEA0, endaddr); ++ } else { ++ endaddr = dmaaddr + ++ dev->node[METADATA_PAD].v_fmt.fmt.meta.buffersize; ++ reg_write(&dev->cfg, UNICAM_DBSA0, dmaaddr); ++ reg_write(&dev->cfg, UNICAM_DBEA0, endaddr); ++ } + } + + static inline unsigned int unicam_get_lines_done(struct unicam_device *dev) + { + dma_addr_t start_addr, cur_addr; +- unsigned int stride = dev->node[0].v_fmt.fmt.pix.bytesperline; +- struct unicam_buffer *frm = dev->node[0].cur_frm; ++ unsigned int stride = dev->node[IMAGE_PAD].v_fmt.fmt.pix.bytesperline; ++ struct unicam_buffer *frm = dev->node[IMAGE_PAD].cur_frm; + + if (!frm) + return 0; +@@ -658,27 +699,51 @@ static inline unsigned int unicam_get_li + return (unsigned int)(cur_addr - start_addr) / stride; + } + +-static inline void unicam_schedule_next_buffer(struct unicam_device *dev) ++static inline void unicam_schedule_next_buffer(struct unicam_node *node) + { +- struct unicam_dmaqueue *dma_q = &dev->node[0].dma_queue; ++ struct unicam_device *dev = node->dev; ++ struct unicam_dmaqueue *dma_q = &node->dma_queue; + struct unicam_buffer *buf; + dma_addr_t addr; + + buf = list_entry(dma_q->active.next, struct unicam_buffer, list); +- dev->node[0].next_frm = buf; ++ node->next_frm = buf; + list_del(&buf->list); + + addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); +- unicam_wr_dma_addr(dev, addr); ++ unicam_wr_dma_addr(dev, addr, node->pad_id); + } + +-static inline void unicam_process_buffer_complete(struct unicam_device *dev) ++static inline void unicam_process_buffer_complete(struct unicam_node *node, ++ unsigned int sequence) + { +- dev->node[0].cur_frm->vb.field = dev->node[0].m_fmt.field; +- dev->node[0].cur_frm->vb.sequence = dev->sequence++; ++ node->cur_frm->vb.field = node->m_fmt.field; ++ node->cur_frm->vb.sequence = sequence; ++ ++ vb2_buffer_done(&node->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE); ++ node->cur_frm = node->next_frm; ++} + +- vb2_buffer_done(&dev->node[0].cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE); +- dev->node[0].cur_frm = dev->node[0].next_frm; ++static int unicam_num_nodes_streaming(struct unicam_device *dev) ++{ ++ return dev->node[IMAGE_PAD].streaming + ++ dev->node[METADATA_PAD].streaming; ++} ++ ++static int unicam_all_nodes_streaming(struct unicam_device *dev) ++{ ++ int ret; ++ ++ ret = dev->node[IMAGE_PAD].open && dev->node[IMAGE_PAD].streaming; ++ ret &= !dev->node[METADATA_PAD].open || ++ dev->node[METADATA_PAD].streaming; ++ return ret; ++} ++ ++static int unicam_all_nodes_disabled(struct unicam_device *dev) ++{ ++ return !dev->node[IMAGE_PAD].streaming && ++ !dev->node[METADATA_PAD].streaming; + } + + /* +@@ -693,10 +758,12 @@ static irqreturn_t unicam_isr(int irq, v + { + struct unicam_device *unicam = (struct unicam_device *)dev; + struct unicam_cfg *cfg = &unicam->cfg; +- struct unicam_dmaqueue *dma_q = &unicam->node[0].dma_queue; + unsigned int lines_done = unicam_get_lines_done(dev); + unsigned int sequence = unicam->sequence; ++ int num_nodes_streaming = unicam_num_nodes_streaming(dev); + int ista, sta; ++ u64 ts; ++ int i; + + /* + * Don't service interrupts if not streaming. +@@ -704,7 +771,7 @@ static irqreturn_t unicam_isr(int irq, v + * peripheral without the kernel knowing (that + * shouldn't happen, but causes issues if it does). + */ +- if (!unicam->streaming) ++ if (unicam_all_nodes_disabled(unicam)) + return IRQ_HANDLED; + + sta = reg_read(cfg, UNICAM_STA); +@@ -726,9 +793,12 @@ static irqreturn_t unicam_isr(int irq, v + * Timestamp is to be when the first data byte was captured, + * aka frame start. + */ +- if (unicam->node[0].cur_frm) +- unicam->node[0].cur_frm->vb.vb2_buf.timestamp = +- ktime_get_ns(); ++ ts = ktime_get_ns(); ++ for (i = 0; i < num_nodes_streaming; i++) { ++ if (unicam->node[i].cur_frm) ++ unicam->node[i].cur_frm->vb.vb2_buf.timestamp = ++ ts; ++ } + } + if (ista & UNICAM_FEI || sta & UNICAM_PI0) { + /* +@@ -736,9 +806,13 @@ static irqreturn_t unicam_isr(int irq, v + * stop the peripheral. Overwrite the frame we've just + * captured instead. + */ +- if (unicam->node[0].cur_frm && +- unicam->node[0].cur_frm != unicam->node[0].next_frm) +- unicam_process_buffer_complete(unicam); ++ for (i = 0; i < num_nodes_streaming; i++) { ++ if (unicam->node[i].cur_frm && ++ unicam->node[i].cur_frm != unicam->node[i].next_frm) ++ unicam_process_buffer_complete(&unicam->node[i], ++ sequence); ++ } ++ unicam->sequence++; + } + + /* Cannot swap buffer at frame end, there may be a race condition +@@ -746,11 +820,13 @@ static irqreturn_t unicam_isr(int irq, v + * already started. + */ + if (ista & (UNICAM_FSI | UNICAM_LCI) && !(ista & UNICAM_FEI)) { +- spin_lock(&unicam->node[0].dma_queue_lock); +- if (!list_empty(&dma_q->active) && +- unicam->node[0].cur_frm == unicam->node[0].next_frm) +- unicam_schedule_next_buffer(unicam); +- spin_unlock(&unicam->node[0].dma_queue_lock); ++ for (i = 0; i < num_nodes_streaming; i++) { ++ spin_lock(&unicam->node[i].dma_queue_lock); ++ if (!list_empty(&unicam->node[i].dma_queue.active) && ++ unicam->node[i].cur_frm == unicam->node[i].next_frm) ++ unicam_schedule_next_buffer(&unicam->node[i]); ++ spin_unlock(&unicam->node[i].dma_queue_lock); ++ } + } + + if (reg_read(&unicam->cfg, UNICAM_ICTL) & UNICAM_FCM) { +@@ -773,6 +849,15 @@ static int unicam_querycap(struct file * + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", dev->v4l2_dev.name); + ++ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | ++ V4L2_CAP_READWRITE | V4L2_CAP_DEVICE_CAPS | ++ V4L2_CAP_META_CAPTURE; ++ ++ if (node->pad_id == IMAGE_PAD) ++ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; ++ else ++ cap->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING; ++ + return 0; + } + +@@ -787,9 +872,14 @@ static int unicam_enum_fmt_vid_cap(struc + int ret = 0; + int i; + ++ if (node->pad_id == METADATA_PAD) ++ return -EINVAL; ++ + for (i = 0; !ret && i < MAX_ENUM_MBUS_CODE; i++) { + memset(&mbus_code, 0, sizeof(mbus_code)); + mbus_code.index = i; ++ mbus_code.pad = IMAGE_PAD; ++ mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; + + ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, + NULL, &mbus_code); +@@ -827,6 +917,9 @@ static int unicam_g_fmt_vid_cap(struct f + { + struct unicam_node *node = video_drvdata(file); + ++ if (node->pad_id == METADATA_PAD) ++ return -EINVAL; ++ + *f = node->v_fmt; + + return 0; +@@ -843,6 +936,9 @@ const struct unicam_fmt *get_first_suppo + for (j = 0; ret != -EINVAL && ret != -ENOIOCTLCMD; ++j) { + memset(&mbus_code, 0, sizeof(mbus_code)); + mbus_code.index = j; ++ mbus_code.pad = IMAGE_PAD; ++ mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; ++ + ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, NULL, + &mbus_code); + if (ret < 0) { +@@ -873,12 +969,15 @@ static int unicam_try_fmt_vid_cap(struct + struct unicam_device *dev = node->dev; + struct v4l2_subdev_format sd_fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, +- .pad = 0 ++ .pad = IMAGE_PAD + }; + struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; + const struct unicam_fmt *fmt; + int ret; + ++ if (node->pad_id == METADATA_PAD) ++ return -EINVAL; ++ + fmt = find_format_by_pix(dev, f->fmt.pix.pixelformat); + if (!fmt) { + /* Pixel format not supported by unicam. Choose the first +@@ -983,7 +1082,7 @@ static int unicam_s_fmt_vid_cap(struct f + + v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code); + +- ret = __subdev_set_format(dev, &mbus_fmt); ++ ret = __subdev_set_format(dev, &mbus_fmt, node->pad_id); + if (ret) { + unicam_dbg(3, dev, "%s __subdev_set_format failed %d\n", + __func__, ret); +@@ -1014,6 +1113,106 @@ static int unicam_s_fmt_vid_cap(struct f + return 0; + } + ++static int unicam_enum_fmt_meta_cap(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; ++ struct v4l2_subdev_mbus_code_enum mbus_code; ++ const struct unicam_fmt *fmt = NULL; ++ int ret = 0; ++ ++ if (node->pad_id != METADATA_PAD || f->index != 0) ++ return -EINVAL; ++ ++ if (dev->sensor_embedded_data) { ++ memset(&mbus_code, 0, sizeof(mbus_code)); ++ mbus_code.index = f->index; ++ mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; ++ mbus_code.pad = METADATA_PAD; ++ ++ ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, NULL, ++ &mbus_code); ++ if (ret < 0) { ++ unicam_dbg(2, dev, ++ "subdev->enum_mbus_code idx 0 returned %d - index invalid\n", ++ ret); ++ return -EINVAL; ++ } ++ } else { ++ mbus_code.code = MEDIA_BUS_FMT_SENSOR_DATA; ++ } ++ ++ fmt = find_format_by_code(mbus_code.code); ++ if (fmt) ++ f->pixelformat = fmt->fourcc; ++ ++ return 0; ++} ++ ++static int unicam_g_fmt_meta_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct unicam_node *node = video_drvdata(file); ++ ++ if (node->pad_id != METADATA_PAD) ++ return -EINVAL; ++ ++ *f = node->v_fmt; ++ ++ return 0; ++} ++ ++static int unicam_try_fmt_meta_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct unicam_node *node = video_drvdata(file); ++ ++ if (node->pad_id != METADATA_PAD) ++ return -EINVAL; ++ ++ *f = node->v_fmt; ++ ++ return 0; ++} ++ ++static int unicam_s_fmt_meta_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; ++ struct v4l2_mbus_framefmt mbus_fmt = { 0 }; ++ const struct unicam_fmt *fmt; ++ int ret; ++ ++ if (node->pad_id == IMAGE_PAD) ++ return -EINVAL; ++ ++ if (dev->sensor_embedded_data) { ++ fmt = find_format_by_pix(dev, f->fmt.meta.dataformat); ++ if (!fmt) { ++ unicam_err(dev, "unknown format: V4L2 pix 0x%08x\n", ++ f->fmt.meta.dataformat); ++ return -EINVAL; ++ } ++ mbus_fmt.code = fmt->code; ++ ret = __subdev_set_format(dev, &mbus_fmt, node->pad_id); ++ if (ret) { ++ unicam_dbg(3, dev, "%s __subdev_set_format failed %d\n", ++ __func__, ret); ++ return ret; ++ } ++ } ++ ++ *f = node->v_fmt; ++ ++ unicam_dbg(3, dev, "%s size %d, V4L2 pix 0x%08x\n", ++ __func__, node->v_fmt.fmt.meta.buffersize, ++ node->v_fmt.fmt.meta.dataformat); ++ ++ return 0; ++} ++ + static int unicam_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, + unsigned int *nplanes, +@@ -1022,7 +1221,9 @@ static int unicam_queue_setup(struct vb2 + { + struct unicam_node *node = vb2_get_drv_priv(vq); + struct unicam_device *dev = node->dev; +- unsigned int size = node->v_fmt.fmt.pix.sizeimage; ++ unsigned int size = node->pad_id == IMAGE_PAD ? ++ node->v_fmt.fmt.pix.sizeimage : ++ node->v_fmt.fmt.meta.buffersize; + + if (vq->num_buffers + *nbuffers < 3) + *nbuffers = 3 - vq->num_buffers; +@@ -1053,7 +1254,8 @@ static int unicam_buffer_prepare(struct + if (WARN_ON(!node->fmt)) + return -EINVAL; + +- size = node->v_fmt.fmt.pix.sizeimage; ++ size = node->pad_id == IMAGE_PAD ? node->v_fmt.fmt.pix.sizeimage : ++ node->v_fmt.fmt.meta.buffersize; + if (vb2_plane_size(vb, 0) < size) { + unicam_err(dev, "data will not fit into plane (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); +@@ -1082,12 +1284,12 @@ static void unicam_set_packing_config(st + int pack, unpack; + u32 val; + +- if (dev->node[0].v_fmt.fmt.pix.pixelformat == +- dev->node[0].fmt->fourcc) { ++ if (dev->node[IMAGE_PAD].v_fmt.fmt.pix.pixelformat == ++ dev->node[IMAGE_PAD].fmt->fourcc) { + unpack = UNICAM_PUM_NONE; + pack = UNICAM_PPM_NONE; + } else { +- switch (dev->node[0].fmt->depth) { ++ switch (dev->node[IMAGE_PAD].fmt->depth) { + case 8: + unpack = UNICAM_PUM_UNPACK8; + break; +@@ -1125,17 +1327,31 @@ static void unicam_cfg_image_id(struct u + if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) { + /* CSI2 mode */ + reg_write(cfg, UNICAM_IDI0, +- (dev->virtual_channel << 6) | dev->node[0].fmt->csi_dt); ++ (dev->virtual_channel << 6) | ++ dev->node[IMAGE_PAD].fmt->csi_dt); + } else { + /* CCP2 mode */ +- reg_write(cfg, UNICAM_IDI0, (0x80 | dev->node[0].fmt->csi_dt)); ++ reg_write(cfg, UNICAM_IDI0, ++ 0x80 | dev->node[IMAGE_PAD].fmt->csi_dt); + } + } + +-static void unicam_start_rx(struct unicam_device *dev, unsigned long addr) ++static void unicam_enable_ed(struct unicam_device *dev) ++{ ++ struct unicam_cfg *cfg = &dev->cfg; ++ u32 val = reg_read(cfg, UNICAM_DCS); ++ ++ set_field(&val, 2, UNICAM_EDL_MASK); ++ /* Do not wrap at the end of the embedded data buffer */ ++ set_field(&val, 0, UNICAM_DBOB); ++ ++ reg_write(cfg, UNICAM_DCS, val); ++} ++ ++static void unicam_start_rx(struct unicam_device *dev, dma_addr_t *addr) + { + struct unicam_cfg *cfg = &dev->cfg; +- int line_int_freq = dev->node[0].v_fmt.fmt.pix.height >> 2; ++ int line_int_freq = dev->node[IMAGE_PAD].v_fmt.fmt.pix.height >> 2; + unsigned int i; + u32 val; + +@@ -1284,27 +1500,31 @@ static void unicam_start_rx(struct unica + } + + reg_write(&dev->cfg, UNICAM_IBLS, +- dev->node[0].v_fmt.fmt.pix.bytesperline); +- unicam_wr_dma_addr(dev, addr); ++ dev->node[IMAGE_PAD].v_fmt.fmt.pix.bytesperline); ++ unicam_wr_dma_addr(dev, addr[IMAGE_PAD], IMAGE_PAD); + unicam_set_packing_config(dev); + unicam_cfg_image_id(dev); + +- /* Disabled embedded data */ +- val = 0; +- set_field(&val, 0, UNICAM_EDL_MASK); +- reg_write(cfg, UNICAM_DCS, val); +- + val = reg_read(cfg, UNICAM_MISC); + set_field(&val, 1, UNICAM_FL0); + set_field(&val, 1, UNICAM_FL1); + reg_write(cfg, UNICAM_MISC, val); + ++ if (dev->node[METADATA_PAD].streaming && dev->sensor_embedded_data) { ++ unicam_enable_ed(dev); ++ unicam_wr_dma_addr(dev, addr[METADATA_PAD], METADATA_PAD); ++ } ++ + /* Enable peripheral */ + reg_write_field(cfg, UNICAM_CTRL, 1, UNICAM_CPE); + + /* Load image pointers */ + reg_write_field(cfg, UNICAM_ICTL, 1, UNICAM_LIP_MASK); + ++ /* Load embedded data buffer pointers if needed */ ++ if (dev->node[METADATA_PAD].streaming && dev->sensor_embedded_data) ++ reg_write_field(cfg, UNICAM_DCS, 1, UNICAM_LDP); ++ + /* + * Enable trigger only for the first frame to + * sync correctly to the FS from the source. +@@ -1339,6 +1559,9 @@ static void unicam_disable(struct unicam + /* Disable peripheral */ + reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_CPE); + ++ /* Clear ED setup */ ++ reg_write(cfg, UNICAM_DCS, 0); ++ + /* Disable all lane clocks */ + clk_write(cfg, 0); + } +@@ -1347,26 +1570,23 @@ static int unicam_start_streaming(struct + { + struct unicam_node *node = vb2_get_drv_priv(vq); + struct unicam_device *dev = node->dev; +- struct unicam_dmaqueue *dma_q = &node->dma_queue; +- struct unicam_buffer *buf, *tmp; +- unsigned long addr = 0; ++ struct unicam_buffer *buf; ++ dma_addr_t buffer_addr[MAX_NODES] = { 0 }; ++ int num_nodes_streaming; + unsigned long flags; +- int ret; ++ int ret, i; + +- spin_lock_irqsave(&node->dma_queue_lock, flags); +- buf = list_entry(dma_q->active.next, struct unicam_buffer, list); +- node->cur_frm = buf; +- node->next_frm = buf; +- list_del(&buf->list); +- spin_unlock_irqrestore(&node->dma_queue_lock, flags); ++ node->streaming = 1; ++ if (!unicam_all_nodes_streaming(dev)) { ++ unicam_dbg(3, dev, "Not all nodes are streaming yet."); ++ return 0; ++ } + +- addr = vb2_dma_contig_plane_dma_addr(&node->cur_frm->vb.vb2_buf, 0); + dev->sequence = 0; +- + ret = unicam_runtime_get(dev); + if (ret < 0) { + unicam_dbg(3, dev, "unicam_runtime_get failed\n"); +- goto err_release_buffers; ++ return ret; + } + + dev->active_data_lanes = dev->max_data_lanes; +@@ -1388,7 +1608,7 @@ static int unicam_start_streaming(struct + dev->active_data_lanes = dev->max_data_lanes; + } + if (dev->active_data_lanes > dev->max_data_lanes) { +- unicam_err(dev, "Device has requested %u data lanes, which is >%u configured in DT\n", ++ unicam_err(dev, "Device has requested %u data lanes, which is >%u configured in DT\n", + dev->active_data_lanes, dev->max_data_lanes); + ret = -EINVAL; + goto err_pm_put; +@@ -1408,9 +1628,22 @@ static int unicam_start_streaming(struct + unicam_err(dev, "Failed to enable CSI clock: %d\n", ret); + goto err_pm_put; + } +- dev->streaming = 1; + +- unicam_start_rx(dev, addr); ++ num_nodes_streaming = unicam_num_nodes_streaming(dev); ++ for (i = 0; i < num_nodes_streaming; i++) { ++ spin_lock_irqsave(&dev->node[i].dma_queue_lock, flags); ++ buf = list_entry(dev->node[i].dma_queue.active.next, ++ struct unicam_buffer, list); ++ dev->node[i].cur_frm = buf; ++ dev->node[i].next_frm = buf; ++ list_del(&buf->list); ++ spin_unlock_irqrestore(&dev->node[i].dma_queue_lock, flags); ++ buffer_addr[i] = ++ vb2_dma_contig_plane_dma_addr(&dev->node[i].cur_frm->vb.vb2_buf, ++ 0); ++ } ++ ++ unicam_start_rx(dev, buffer_addr); + + ret = v4l2_subdev_call(dev->sensor, video, s_stream, 1); + if (ret < 0) { +@@ -1421,21 +1654,11 @@ static int unicam_start_streaming(struct + return 0; + + err_disable_unicam: ++ node->streaming = 0; + unicam_disable(dev); + clk_disable_unprepare(dev->clock); + err_pm_put: + unicam_runtime_put(dev); +-err_release_buffers: +- list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { +- list_del(&buf->list); +- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); +- } +- if (node->cur_frm != node->next_frm) +- vb2_buffer_done(&node->next_frm->vb.vb2_buf, +- VB2_BUF_STATE_QUEUED); +- vb2_buffer_done(&node->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED); +- node->next_frm = NULL; +- node->cur_frm = NULL; + + return ret; + } +@@ -1448,33 +1671,47 @@ static void unicam_stop_streaming(struct + struct unicam_buffer *buf, *tmp; + unsigned long flags; + +- if (v4l2_subdev_call(dev->sensor, video, s_stream, 0) < 0) +- unicam_err(dev, "stream off failed in subdev\n"); ++ node->streaming = 0; + +- unicam_disable(dev); ++ if (node->pad_id == IMAGE_PAD) { ++ /* Stop streaming the sensor and disable the peripheral. ++ * We cannot continue streaming embedded data with the ++ * image pad disabled. ++ */ ++ if (v4l2_subdev_call(dev->sensor, video, s_stream, 0) < 0) ++ unicam_err(dev, "stream off failed in subdev\n"); + +- /* Release all active buffers */ ++ unicam_disable(dev); ++ clk_disable_unprepare(dev->clock); ++ unicam_runtime_put(dev); ++ ++ } else if (node->pad_id == METADATA_PAD) { ++ /* Null out the embedded data buffer address so the HW does ++ * not use it. This is only really needed if the embedded data ++ * pad is disabled before the image pad. The 0x3 in the top two ++ * bits signifies uncached accesses through the Videocore ++ * memory controller. ++ */ ++ unicam_wr_dma_addr(dev, 0xc0000000, METADATA_PAD); ++ } ++ ++ /* Clear all queued buffers for the node */ + spin_lock_irqsave(&node->dma_queue_lock, flags); + list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } + +- if (node->cur_frm == node->next_frm) { +- vb2_buffer_done(&node->cur_frm->vb.vb2_buf, +- VB2_BUF_STATE_ERROR); +- } else { ++ if (node->cur_frm) + vb2_buffer_done(&node->cur_frm->vb.vb2_buf, + VB2_BUF_STATE_ERROR); ++ if (node->next_frm && node->cur_frm != node->next_frm) + vb2_buffer_done(&node->next_frm->vb.vb2_buf, + VB2_BUF_STATE_ERROR); +- } ++ + node->cur_frm = NULL; + node->next_frm = NULL; + spin_unlock_irqrestore(&node->dma_queue_lock, flags); +- +- clk_disable_unprepare(dev->clock); +- unicam_runtime_put(dev); + } + + static int unicam_enum_input(struct file *file, void *priv, +@@ -1595,17 +1832,23 @@ static int unicam_enum_framesizes(struct + struct v4l2_subdev_frame_size_enum fse; + int ret; + +- /* check for valid format */ +- fmt = find_format_by_pix(dev, fsize->pixel_format); +- if (!fmt) { +- unicam_dbg(3, dev, "Invalid pixel code: %x\n", +- fsize->pixel_format); +- return -EINVAL; ++ if (node->pad_id == IMAGE_PAD) { ++ /* check for valid format */ ++ fmt = find_format_by_pix(dev, fsize->pixel_format); ++ if (!fmt) { ++ unicam_dbg(3, dev, "Invalid pixel code: %x\n", ++ fsize->pixel_format); ++ return -EINVAL; ++ } ++ fse.code = fmt->code; ++ } else { ++ /* This pad is for embedded data, so just set the format */ ++ fse.code = MEDIA_BUS_FMT_SENSOR_DATA; + } + ++ fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fse.index = fsize->index; +- fse.pad = 0; +- fse.code = fmt->code; ++ fse.pad = node->pad_id; + + ret = v4l2_subdev_call(dev->sensor, pad, enum_frame_size, NULL, &fse); + if (ret) +@@ -1782,7 +2025,7 @@ static void unicam_notify(struct v4l2_su + + switch (notification) { + case V4L2_DEVICE_NOTIFY_EVENT: +- v4l2_event_queue(&dev->node[0].video_dev, arg); ++ v4l2_event_queue(&dev->node[IMAGE_PAD].video_dev, arg); + break; + default: + break; +@@ -1826,6 +2069,7 @@ static int unicam_open(struct file *file + goto unlock; + } + ++ node->open++; + ret = 0; + + unlock: +@@ -1850,6 +2094,10 @@ static int unicam_release(struct file *f + if (fh_singular) + v4l2_subdev_call(sd, core, s_power, 0); + ++ if (node->streaming) ++ unicam_stop_streaming(&node->buffer_queue); ++ ++ node->open--; + mutex_unlock(&node->lock); + + return ret; +@@ -1874,6 +2122,11 @@ static const struct v4l2_ioctl_ops unica + .vidioc_s_fmt_vid_cap = unicam_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = unicam_try_fmt_vid_cap, + ++ .vidioc_enum_fmt_meta_cap = unicam_enum_fmt_meta_cap, ++ .vidioc_g_fmt_meta_cap = unicam_g_fmt_meta_cap, ++ .vidioc_s_fmt_meta_cap = unicam_s_fmt_meta_cap, ++ .vidioc_try_fmt_meta_cap = unicam_try_fmt_meta_cap, ++ + .vidioc_enum_input = unicam_enum_input, + .vidioc_g_input = unicam_g_input, + .vidioc_s_input = unicam_s_input, +@@ -1941,42 +2194,53 @@ static int register_node(struct unicam_d + const struct unicam_fmt *fmt; + int ret; + +- ret = __subdev_get_format(unicam, &mbus_fmt, pad_id); +- if (ret) { +- unicam_err(unicam, "Failed to get_format - ret %d\n", ret); +- return ret; +- } +- +- fmt = find_format_by_code(mbus_fmt.code); +- if (!fmt) { +- /* Find the first format that the sensor and unicam both +- * support +- */ +- fmt = get_first_supported_format(unicam); ++ if (unicam->sensor_embedded_data || pad_id != METADATA_PAD) { ++ ret = __subdev_get_format(unicam, &mbus_fmt, pad_id); ++ if (ret) { ++ unicam_err(unicam, "Failed to get_format - ret %d\n", ++ ret); ++ return ret; ++ } + +- if (!fmt) +- /* No compatible formats */ +- return -EINVAL; ++ fmt = find_format_by_code(mbus_fmt.code); ++ if (!fmt) { ++ /* Find the first format that the sensor and unicam both ++ * support ++ */ ++ fmt = get_first_supported_format(unicam); + +- mbus_fmt.code = fmt->code; +- ret = __subdev_set_format(unicam, &mbus_fmt); +- if (ret) +- return -EINVAL; +- } +- if (mbus_fmt.field != V4L2_FIELD_NONE) { +- /* Interlaced not supported - disable it now. */ +- mbus_fmt.field = V4L2_FIELD_NONE; +- ret = __subdev_set_format(unicam, &mbus_fmt); +- if (ret) +- return -EINVAL; ++ if (!fmt) ++ /* No compatible formats */ ++ return -EINVAL; ++ ++ mbus_fmt.code = fmt->code; ++ ret = __subdev_set_format(unicam, &mbus_fmt, pad_id); ++ if (ret) ++ return -EINVAL; ++ } ++ if (mbus_fmt.field != V4L2_FIELD_NONE) { ++ /* Interlaced not supported - disable it now. */ ++ mbus_fmt.field = V4L2_FIELD_NONE; ++ ret = __subdev_set_format(unicam, &mbus_fmt, pad_id); ++ if (ret) ++ return -EINVAL; ++ } ++ } else { ++ /* Fix this node format as embedded data. */ ++ fmt = find_format_by_code(MEDIA_BUS_FMT_SENSOR_DATA); + } + ++ node->dev = unicam; + node->pad_id = pad_id; + node->fmt = fmt; +- if (fmt->fourcc) +- node->v_fmt.fmt.pix.pixelformat = fmt->fourcc; +- else ++ if (fmt->fourcc) { ++ if (fmt->fourcc != V4L2_META_FMT_SENSOR_DATA) ++ node->v_fmt.fmt.pix.pixelformat = fmt->fourcc; ++ else ++ node->v_fmt.fmt.meta.dataformat = fmt->fourcc; ++ } else { + node->v_fmt.fmt.pix.pixelformat = fmt->repacked_fourcc; ++ } + + /* Read current subdev format */ + unicam_reset_format(node); +@@ -2002,13 +2266,21 @@ static int register_node(struct unicam_d + spin_lock_init(&node->dma_queue_lock); + mutex_init(&node->lock); + +- if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { ++ vdev = &node->video_dev; ++ if (pad_id == IMAGE_PAD) { + /* Add controls from the subdevice */ + ret = v4l2_ctrl_add_handler(&node->ctrl_handler, + unicam->sensor->ctrl_handler, NULL, + true); + if (ret < 0) + return ret; ++ ++ /* ++ * If the sensor subdevice has any controls, associate the node ++ * with the ctrl handler to allow access from userland. ++ */ ++ if (!list_empty(&node->ctrl_handler.ctrls)) ++ vdev->ctrl_handler = &node->ctrl_handler; + } + + q = &node->buffer_queue; +@@ -2031,8 +2303,6 @@ static int register_node(struct unicam_d + + INIT_LIST_HEAD(&node->dma_queue.active); + +- vdev = &node->video_dev; +- strlcpy(vdev->name, UNICAM_MODULE_NAME, sizeof(vdev->name)); + vdev->release = video_device_release_empty; + vdev->fops = &unicam_fops; + vdev->ioctl_ops = &unicam_ioctl_ops; +@@ -2040,24 +2310,28 @@ static int register_node(struct unicam_d + vdev->vfl_dir = VFL_DIR_RX; + vdev->queue = q; + vdev->lock = &node->lock; +- vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | +- V4L2_CAP_READWRITE; +- /* If the source has no controls then remove our ctrl handler. */ +- if (list_empty(&node->ctrl_handler.ctrls)) +- unicam->v4l2_dev.ctrl_handler = NULL; ++ vdev->device_caps = (pad_id == IMAGE_PAD) ? ++ (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING) : ++ (V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING); ++ ++ /* Define the device names */ ++ snprintf(vdev->name, sizeof(vdev->name), "%s-%s", UNICAM_MODULE_NAME, ++ node->pad_id == IMAGE_PAD ? "image" : "embedded"); + +- node->dev = unicam; + video_set_drvdata(vdev, node); + vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT; + +- if (!v4l2_subdev_has_op(unicam->sensor, video, s_std)) { ++ if (node->pad_id == METADATA_PAD || ++ !v4l2_subdev_has_op(unicam->sensor, video, s_std)) { + v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_STD); + v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_STD); + v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUMSTD); + } +- if (!v4l2_subdev_has_op(unicam->sensor, video, querystd)) ++ if (node->pad_id == METADATA_PAD || ++ !v4l2_subdev_has_op(unicam->sensor, video, querystd)) + v4l2_disable_ioctl(&node->video_dev, VIDIOC_QUERYSTD); +- if (!v4l2_subdev_has_op(unicam->sensor, video, s_dv_timings)) { ++ if (node->pad_id == METADATA_PAD || ++ !v4l2_subdev_has_op(unicam->sensor, video, s_dv_timings)) { + v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_EDID); + v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_EDID); + v4l2_disable_ioctl(&node->video_dev, VIDIOC_DV_TIMINGS_CAP); +@@ -2066,15 +2340,19 @@ static int register_node(struct unicam_d + v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUM_DV_TIMINGS); + v4l2_disable_ioctl(&node->video_dev, VIDIOC_QUERY_DV_TIMINGS); + } +- if (!v4l2_subdev_has_op(unicam->sensor, pad, enum_frame_interval)) ++ if (node->pad_id == METADATA_PAD || ++ !v4l2_subdev_has_op(unicam->sensor, pad, enum_frame_interval)) + v4l2_disable_ioctl(&node->video_dev, + VIDIOC_ENUM_FRAMEINTERVALS); +- if (!v4l2_subdev_has_op(unicam->sensor, video, g_frame_interval)) ++ if (node->pad_id == METADATA_PAD || ++ !v4l2_subdev_has_op(unicam->sensor, video, g_frame_interval)) + v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_PARM); +- if (!v4l2_subdev_has_op(unicam->sensor, video, s_frame_interval)) ++ if (node->pad_id == METADATA_PAD || ++ !v4l2_subdev_has_op(unicam->sensor, video, s_frame_interval)) + v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_PARM); + +- if (!v4l2_subdev_has_op(unicam->sensor, pad, enum_frame_size)) ++ if (node->pad_id == METADATA_PAD || ++ !v4l2_subdev_has_op(unicam->sensor, pad, enum_frame_size)) + v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUM_FRAMESIZES); + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); +@@ -2082,27 +2360,29 @@ static int register_node(struct unicam_d + unicam_err(unicam, "Unable to register video device.\n"); + return ret; + } +- node->registered = true; ++ node->registered = 1; + +- ret = media_create_pad_link(&unicam->sensor->entity, +- 0, &node->video_dev.entity, 0, +- MEDIA_LNK_FL_ENABLED | +- MEDIA_LNK_FL_IMMUTABLE); +- if (ret) +- unicam_err(unicam, "Unable to create pad links.\n"); ++ if (unicam->sensor_embedded_data) { ++ ret = media_create_pad_link(&unicam->sensor->entity, pad_id, ++ &node->video_dev.entity, 0, ++ MEDIA_LNK_FL_ENABLED | ++ MEDIA_LNK_FL_IMMUTABLE); ++ if (ret) ++ unicam_err(unicam, "Unable to create pad links.\n"); ++ } + + return ret; + } + + static void unregister_nodes(struct unicam_device *unicam) + { +- if (unicam->node[0].registered) { +- video_unregister_device(&unicam->node[0].video_dev); +- unicam->node[0].registered = false; +- } +- if (unicam->node[1].registered) { +- video_unregister_device(&unicam->node[1].video_dev); +- unicam->node[1].registered = false; ++ if (unicam->node[IMAGE_PAD].registered) { ++ video_unregister_device(&unicam->node[IMAGE_PAD].video_dev); ++ unicam->node[IMAGE_PAD].registered = 0; ++ } ++ if (unicam->node[METADATA_PAD].registered) { ++ video_unregister_device(&unicam->node[METADATA_PAD].video_dev); ++ unicam->node[METADATA_PAD].registered = 0; + } + } + +@@ -2118,20 +2398,20 @@ static int unicam_probe_complete(struct + if (!unicam->sensor_config) + return -ENOMEM; + +- ret = register_node(unicam, &unicam->node[0], +- V4L2_BUF_TYPE_VIDEO_CAPTURE, 0); ++ unicam->sensor_embedded_data = (unicam->sensor->entity.num_pads >= 2); ++ ++ ret = register_node(unicam, &unicam->node[IMAGE_PAD], ++ V4L2_BUF_TYPE_VIDEO_CAPTURE, IMAGE_PAD); + if (ret) { + unicam_err(unicam, "Unable to register subdev node 0.\n"); + goto unregister; + } +- if (unicam->sensor->entity.num_pads >= 2) { +- ret = register_node(unicam, &unicam->node[1], +- V4L2_BUF_TYPE_META_CAPTURE, 1); +- if (ret) { +- unicam_err(unicam, +- "Unable to register subdev node 1.\n"); +- goto unregister; +- } ++ ++ ret = register_node(unicam, &unicam->node[METADATA_PAD], ++ V4L2_BUF_TYPE_META_CAPTURE, METADATA_PAD); ++ if (ret) { ++ unicam_err(unicam, "Unable to register subdev node 1.\n"); ++ goto unregister; + } + + ret = v4l2_device_register_ro_subdev_nodes(&unicam->v4l2_dev); +@@ -2355,8 +2635,10 @@ static int unicam_probe(struct platform_ + pdev->dev.driver->name, dev_name(&pdev->dev)); + unicam->mdev.hw_revision = 1; + +- media_entity_pads_init(&unicam->node[0].video_dev.entity, 1, +- &unicam->node[0].pad); ++ media_entity_pads_init(&unicam->node[IMAGE_PAD].video_dev.entity, 1, ++ &unicam->node[IMAGE_PAD].pad); ++ media_entity_pads_init(&unicam->node[METADATA_PAD].video_dev.entity, 1, ++ &unicam->node[METADATA_PAD].pad); + media_device_init(&unicam->mdev); + + unicam->v4l2_dev.mdev = &unicam->mdev; +@@ -2376,11 +2658,10 @@ static int unicam_probe(struct platform_ + } + + /* Reserve space for the controls */ +- hdl = &unicam->node[0].ctrl_handler; ++ hdl = &unicam->node[IMAGE_PAD].ctrl_handler; + ret = v4l2_ctrl_handler_init(hdl, 16); + if (ret < 0) + goto media_unregister; +- unicam->v4l2_dev.ctrl_handler = hdl; + + /* set the driver data in platform device */ + platform_set_drvdata(pdev, unicam); +@@ -2417,7 +2698,7 @@ static int unicam_remove(struct platform + pm_runtime_disable(&pdev->dev); + + v4l2_async_notifier_unregister(&unicam->notifier); +- v4l2_ctrl_handler_free(&unicam->node[0].ctrl_handler); ++ v4l2_ctrl_handler_free(&unicam->node[IMAGE_PAD].ctrl_handler); + v4l2_device_unregister(&unicam->v4l2_dev); + unregister_nodes(unicam); + if (unicam->sensor_config) |