mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-10-31 05:54:26 -04:00 
			
		
		
		
	Also removes random module and switches to new bcm2711 thermal driver. Boot tested on RPi 4B v1.1 4G. Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
		
			
				
	
	
		
			1171 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			1171 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From 272ee62d6410319ab4d73997de32776cc3e274cb 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_GRABBER, -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)
 |