linux-media.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
To: Dafna Hirschfeld <dafna.hirschfeld@collabora.com>
Cc: linux-media@vger.kernel.org, helen.koike@collabora.com,
	ezequiel@collabora.com, hverkuil@xs4all.nl, kernel@collabora.com,
	dafna3@gmail.com, sakari.ailus@linux.intel.com,
	linux-rockchip@lists.infradead.org, mchehab@kernel.org
Subject: Re: [PATCH 5/5] media: staging: rkisp1: replace workqueue with threaded irq for reading statistics registers
Date: Thu, 21 May 2020 03:09:01 +0300	[thread overview]
Message-ID: <20200521000901.GE25474@pendragon.ideasonboard.com> (raw)
In-Reply-To: <20200512120522.25960-6-dafna.hirschfeld@collabora.com>

Hi Dafna,

Thank you for the patch.

On Tue, May 12, 2020 at 02:05:22PM +0200, Dafna Hirschfeld wrote:
> Reading the statistics registers might take too long
> to run inside the irq handler. Currently it is deferred
> to bottom half using workqueues. This patch replaces the
> workqueue with threaded interrupts for reading the
> statistics registers.

Could you please explain why this is needed/desired ? Removal of the
kzalloc(GFP_ATOMIC) is very nice, but I'm sure there would have been
ways to avoid it while keeping a workqueue. I'm not claiming the patch
is bad, but I'd like to understand the reason.

> A new struct type 'rkisp1_kstats_buffer' is used as the statistics
> buffers. The struct has a field 'ris' which is the flags of ready
> statistics. If new statistics are ready, the irq handler sets
> this variable and the frame sequence on the next available buffer
> and returns IRQ_WAKE_THREAD.
> Then the threaded interrupt reads the registers and calls
> vb2_buffer_done.
> 
> Signed-off-by: Dafna Hirschfeld <dafna.hirschfeld@collabora.com>
> ---
>  drivers/staging/media/rkisp1/TODO            |   1 -
>  drivers/staging/media/rkisp1/rkisp1-common.h |   5 +-
>  drivers/staging/media/rkisp1/rkisp1-dev.c    |   8 +-
>  drivers/staging/media/rkisp1/rkisp1-isp.c    |   5 +-
>  drivers/staging/media/rkisp1/rkisp1-stats.c  | 167 ++++++++-----------
>  5 files changed, 76 insertions(+), 110 deletions(-)
> 
> diff --git a/drivers/staging/media/rkisp1/TODO b/drivers/staging/media/rkisp1/TODO
> index c0cbec0a164d..bdb1b8f73556 100644
> --- a/drivers/staging/media/rkisp1/TODO
> +++ b/drivers/staging/media/rkisp1/TODO
> @@ -1,5 +1,4 @@
>  * Fix pad format size for statistics and parameters entities.
> -* Use threaded interrupt for rkisp1_stats_isr(), remove work queue.
>  * Fix checkpatch errors.
>  * Review and comment every lock
>  * Handle quantization
> diff --git a/drivers/staging/media/rkisp1/rkisp1-common.h b/drivers/staging/media/rkisp1/rkisp1-common.h
> index c635bb0a7727..c8adcdf661ab 100644
> --- a/drivers/staging/media/rkisp1/rkisp1-common.h
> +++ b/drivers/staging/media/rkisp1/rkisp1-common.h
> @@ -190,8 +190,6 @@ struct rkisp1_stats {
>  	struct list_head stat;
>  	struct v4l2_format vdev_fmt;
>  	bool is_streaming;
> -
> -	struct workqueue_struct *readout_wq;
>  };
>  
>  /*
> @@ -308,10 +306,11 @@ void rkisp1_isp_unregister(struct rkisp1_device *rkisp1);
>  
>  const struct rkisp1_isp_mbus_info *rkisp1_isp_mbus_info_get(u32 mbus_code);
>  
> +irqreturn_t rkisp1_read_stats_threaded_irq(int irq, void *ctx);
>  irqreturn_t rkisp1_isp_isr(struct rkisp1_device *rkisp1);
>  irqreturn_t rkisp1_mipi_isr(struct rkisp1_device *rkisp1);
>  irqreturn_t rkisp1_capture_isr(struct rkisp1_device *rkisp1);
> -void rkisp1_stats_isr(struct rkisp1_stats *stats, u32 isp_ris);
> +irqreturn_t rkisp1_stats_isr(struct rkisp1_stats *stats, u32 isp_ris);
>  void rkisp1_params_isr(struct rkisp1_device *rkisp1, u32 isp_mis);
>  
>  int rkisp1_capture_devs_register(struct rkisp1_device *rkisp1);
> diff --git a/drivers/staging/media/rkisp1/rkisp1-dev.c b/drivers/staging/media/rkisp1/rkisp1-dev.c
> index b7f43dab71c8..12e2e8559acd 100644
> --- a/drivers/staging/media/rkisp1/rkisp1-dev.c
> +++ b/drivers/staging/media/rkisp1/rkisp1-dev.c
> @@ -405,6 +405,8 @@ irqreturn_t rkisp1_isr(int irq, void *ctx)
>  	isp_ret = rkisp1_isp_isr(rkisp1);
>  	mipi_ret = rkisp1_mipi_isr(rkisp1);
>  
> +	if (isp_ret == IRQ_WAKE_THREAD)
> +		return IRQ_WAKE_THREAD;
>  	if (isp_ret == IRQ_NONE && cap_ret == IRQ_NONE && mipi_ret == IRQ_NONE)
>  		return IRQ_NONE;

Let me slightly modify my proposal from another patch in this series:

	irqreturn_t ret = IRQ_NONE;
	...

	ret |= rkisp1_capture_isr(rkisp1);
	ret |= rkisp1_isp_isr(rkisp1);
	ret |= rkisp1_mipi_isr(rkisp1);

	return ret & IRQ_WAKE_THREAD ? IRQ_WAKE_THREAD : ret;

Up to you.

>  
> @@ -490,8 +492,10 @@ static int rkisp1_probe(struct platform_device *pdev)
>  	if (irq < 0)
>  		return irq;
>  
> -	ret = devm_request_irq(dev, irq, rkisp1_isr, IRQF_SHARED,
> -			       dev_driver_string(dev), dev);
> +	ret = devm_request_threaded_irq(dev, irq, rkisp1_isr,
> +					rkisp1_read_stats_threaded_irq,
> +					IRQF_SHARED,
> +					dev_driver_string(dev), dev);
>  	if (ret) {
>  		dev_err(dev, "request irq failed: %d\n", ret);
>  		return ret;
> diff --git a/drivers/staging/media/rkisp1/rkisp1-isp.c b/drivers/staging/media/rkisp1/rkisp1-isp.c
> index 49b47e1734b0..09893073af00 100644
> --- a/drivers/staging/media/rkisp1/rkisp1-isp.c
> +++ b/drivers/staging/media/rkisp1/rkisp1-isp.c
> @@ -1111,6 +1111,7 @@ static void rkisp1_isp_queue_event_sof(struct rkisp1_isp *isp)
>  irqreturn_t rkisp1_isp_isr(struct rkisp1_device *rkisp1)
>  {
>  	u32 status, isp_err;
> +	irqreturn_t ret = IRQ_HANDLED;
>  
>  	status = rkisp1_read(rkisp1, RKISP1_CIF_ISP_MIS);
>  	if (!status)
> @@ -1138,7 +1139,7 @@ irqreturn_t rkisp1_isp_isr(struct rkisp1_device *rkisp1)
>  		/* New frame from the sensor received */
>  		isp_ris = rkisp1_read(rkisp1, RKISP1_CIF_ISP_RIS);
>  		if (isp_ris & RKISP1_STATS_MEAS_MASK)
> -			rkisp1_stats_isr(&rkisp1->stats, isp_ris);
> +			ret = rkisp1_stats_isr(&rkisp1->stats, isp_ris);

Overriding the ret variable isn't very nice, even if it should work in
practice here as ret is already equal to IRQ_HANDLED and
rkisp1_stats_isr() returns IRQ_HANDLED or IRQ_WAKE_THREAD, never
IRQ_NONE. You could however write

			ret |= rkisp1_stats_isr(&rkisp1->stats, isp_ris);

as IRQ_HANDLED and IRQ_WAKE_THREAD are independent bit flags to
accumulate them, provided you return IRQ_WAKE_THREAD from rkisp1_isr()
when both IRQ_WAKE_THREAD and IRQ_HANDLED are set (it seems the the core
IRQ code can't deal with both being set).

>  	}
>  
>  	/*
> @@ -1148,5 +1149,5 @@ irqreturn_t rkisp1_isp_isr(struct rkisp1_device *rkisp1)
>  	 */
>  	rkisp1_params_isr(rkisp1, status);
>  
> -	return IRQ_HANDLED;
> +	return ret;
>  }
> diff --git a/drivers/staging/media/rkisp1/rkisp1-stats.c b/drivers/staging/media/rkisp1/rkisp1-stats.c
> index e6fb2c5f3b3e..f5eaa81362ea 100644
> --- a/drivers/staging/media/rkisp1/rkisp1-stats.c
> +++ b/drivers/staging/media/rkisp1/rkisp1-stats.c
> @@ -18,19 +18,9 @@
>  #define RKISP1_ISP_STATS_REQ_BUFS_MIN 2
>  #define RKISP1_ISP_STATS_REQ_BUFS_MAX 8
>  
> -enum rkisp1_isp_readout_cmd {
> -	RKISP1_ISP_READOUT_MEAS,
> -	RKISP1_ISP_READOUT_META,
> -};
> -
> -struct rkisp1_isp_readout_work {
> -	struct work_struct work;
> -	struct rkisp1_stats *stats;
> -
> -	unsigned int frame_id;
> -	unsigned int isp_ris;
> -	enum rkisp1_isp_readout_cmd readout;
> -	struct vb2_buffer *vb;
> +struct rkisp1_kstats_buffer {
> +	struct rkisp1_buffer buff;
> +	u32 ris;
>  };
>  
>  static int rkisp1_stats_enum_fmt_meta_cap(struct file *file, void *priv,
> @@ -126,16 +116,17 @@ static int rkisp1_stats_vb2_queue_setup(struct vb2_queue *vq,
>  static void rkisp1_stats_vb2_buf_queue(struct vb2_buffer *vb)
>  {
>  	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> -	struct rkisp1_buffer *stats_buf =
> -		container_of(vbuf, struct rkisp1_buffer, vb);
> +	struct rkisp1_kstats_buffer *kstats_buf =
> +		container_of(vbuf, struct rkisp1_kstats_buffer, buff.vb);
>  	struct vb2_queue *vq = vb->vb2_queue;
>  	struct rkisp1_stats *stats_dev = vq->drv_priv;
>  	unsigned long flags;
>  
> -	stats_buf->vaddr[0] = vb2_plane_vaddr(vb, 0);
> +	kstats_buf->buff.vaddr[0] = vb2_plane_vaddr(vb, 0);
> +	kstats_buf->ris = 0;
>  
>  	spin_lock_irqsave(&stats_dev->stats_lock, flags);
> -	list_add_tail(&stats_buf->queue, &stats_dev->stat);
> +	list_add_tail(&kstats_buf->buff.queue, &stats_dev->stat);
>  	spin_unlock_irqrestore(&stats_dev->stats_lock, flags);
>  }
>  
> @@ -152,25 +143,19 @@ static int rkisp1_stats_vb2_buf_prepare(struct vb2_buffer *vb)
>  static void rkisp1_stats_vb2_stop_streaming(struct vb2_queue *vq)
>  {
>  	struct rkisp1_stats *stats = vq->drv_priv;
> -	struct rkisp1_buffer *buf;
> +	struct rkisp1_kstats_buffer *buf;
>  	unsigned long flags;
>  	unsigned int i;
>  
> -	/* Make sure no new work queued in isr before draining wq */
>  	spin_lock_irqsave(&stats->stats_lock, flags);
>  	stats->is_streaming = false;
> -	spin_unlock_irqrestore(&stats->stats_lock, flags);
> -
> -	drain_workqueue(stats->readout_wq);
> -
> -	spin_lock_irqsave(&stats->stats_lock, flags);
>  	for (i = 0; i < RKISP1_ISP_STATS_REQ_BUFS_MAX; i++) {
>  		if (list_empty(&stats->stat))
>  			break;
>  		buf = list_first_entry(&stats->stat,
> -				       struct rkisp1_buffer, queue);
> -		list_del(&buf->queue);
> -		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
> +				       struct rkisp1_kstats_buffer, buff.queue);
> +		list_del(&buf->buff.queue);
> +		vb2_buffer_done(&buf->buff.vb.vb2_buf, VB2_BUF_STATE_ERROR);
>  	}
>  	spin_unlock_irqrestore(&stats->stats_lock, flags);
>  }
> @@ -207,7 +192,7 @@ rkisp1_stats_init_vb2_queue(struct vb2_queue *q, struct rkisp1_stats *stats)
>  	q->drv_priv = stats;
>  	q->ops = &rkisp1_stats_vb2_ops;
>  	q->mem_ops = &vb2_vmalloc_memops;
> -	q->buf_struct_size = sizeof(struct rkisp1_buffer);
> +	q->buf_struct_size = sizeof(struct rkisp1_kstats_buffer);
>  	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
>  	q->lock = &node->vlock;
>  
> @@ -325,85 +310,81 @@ static void rkisp1_stats_get_bls_meas(struct rkisp1_stats *stats,
>  	}
>  }
>  
> -static void
> -rkisp1_stats_send_measurement(struct rkisp1_stats *stats,
> -			      struct rkisp1_isp_readout_work *meas_work)
> +irqreturn_t rkisp1_read_stats_threaded_irq(int irq, void *ctx)
>  {
> +	struct device *dev = ctx;
> +	struct rkisp1_device *rkisp1 = dev_get_drvdata(dev);
> +	struct rkisp1_stats *stats = &rkisp1->stats;
> +	struct rkisp1_kstats_buffer *kstats_buf = NULL;
>  	struct rkisp1_stat_buffer *cur_stat_buf;
> -	struct rkisp1_buffer *cur_buf = NULL;
> -	unsigned int frame_sequence =
> -		atomic_read(&stats->rkisp1->isp.frame_sequence);
> -	u64 timestamp = ktime_get_ns();
>  	unsigned long flags;
> -
> -	if (frame_sequence != meas_work->frame_id) {
> -		dev_warn(stats->rkisp1->dev,
> -			 "Measurement late(%d, %d)\n",
> -			 frame_sequence, meas_work->frame_id);
> -		frame_sequence = meas_work->frame_id;
> -	}
> +	u64 timestamp = ktime_get_ns();
>  
>  	spin_lock_irqsave(&stats->stats_lock, flags);
> -	/* get one empty buffer */
> -	if (!list_empty(&stats->stat)) {
> -		cur_buf = list_first_entry(&stats->stat,
> -					   struct rkisp1_buffer, queue);
> -		list_del(&cur_buf->queue);
> +	if (!stats->is_streaming) {
> +		spin_unlock_irqrestore(&stats->stats_lock, flags);
> +		return IRQ_HANDLED;
> +	}
> +	if (list_empty(&stats->stat)) {
> +		spin_unlock_irqrestore(&stats->stats_lock, flags);
> +		WARN("%s: threaded irq waked but there are no buffers",
> +		     __func__);
> +		return IRQ_HANDLED;
> +	}
> +	kstats_buf = list_first_entry(&stats->stat,
> +				      struct rkisp1_kstats_buffer, buff.queue);
> +
> +	/*
> +	 * each waked irq thread reads exactly one ready statistics
> +	 * so it is a bug  if no statistics are ready
> +	 */

What happens if this function takes too long to run ? With the
workqueue, if the work gets delayed once in a while, the work items will
accumulate, and all of them will be handled eventually. Here it seems
that an IRQ could get lost.

> +	if (!kstats_buf->ris) {
> +		spin_unlock_irqrestore(&stats->stats_lock, flags);
> +		WARN("%s: threaded irq waked but buffer holds no measures",
> +		     __func__);
> +		return IRQ_HANDLED;
>  	}
> +	list_del(&kstats_buf->buff.queue);
>  	spin_unlock_irqrestore(&stats->stats_lock, flags);
>  
> -	if (!cur_buf)
> -		return;
> -
>  	cur_stat_buf =
> -		(struct rkisp1_stat_buffer *)(cur_buf->vaddr[0]);
> +		(struct rkisp1_stat_buffer *)(kstats_buf->buff.vaddr[0]);
>  
> -	if (meas_work->isp_ris & RKISP1_CIF_ISP_AWB_DONE) {
> +	if (kstats_buf->ris & RKISP1_CIF_ISP_AWB_DONE) {
>  		rkisp1_stats_get_awb_meas(stats, cur_stat_buf);
>  		cur_stat_buf->meas_type |= RKISP1_CIF_ISP_STAT_AWB;
>  	}
>  
> -	if (meas_work->isp_ris & RKISP1_CIF_ISP_AFM_FIN) {
> +	if (kstats_buf->ris & RKISP1_CIF_ISP_AFM_FIN) {
>  		rkisp1_stats_get_afc_meas(stats, cur_stat_buf);
>  		cur_stat_buf->meas_type |= RKISP1_CIF_ISP_STAT_AFM_FIN;
>  	}
>  
> -	if (meas_work->isp_ris & RKISP1_CIF_ISP_EXP_END) {
> +	if (kstats_buf->ris & RKISP1_CIF_ISP_EXP_END) {
>  		rkisp1_stats_get_aec_meas(stats, cur_stat_buf);
>  		rkisp1_stats_get_bls_meas(stats, cur_stat_buf);
>  		cur_stat_buf->meas_type |= RKISP1_CIF_ISP_STAT_AUTOEXP;
>  	}
>  
> -	if (meas_work->isp_ris & RKISP1_CIF_ISP_HIST_MEASURE_RDY) {
> +	if (kstats_buf->ris & RKISP1_CIF_ISP_HIST_MEASURE_RDY) {
>  		rkisp1_stats_get_hst_meas(stats, cur_stat_buf);
>  		cur_stat_buf->meas_type |= RKISP1_CIF_ISP_STAT_HIST;
>  	}
>  
> -	vb2_set_plane_payload(&cur_buf->vb.vb2_buf, 0,
> +	vb2_set_plane_payload(&kstats_buf->buff.vb.vb2_buf, 0,
>  			      sizeof(struct rkisp1_stat_buffer));
> -	cur_buf->vb.sequence = frame_sequence;
> -	cur_buf->vb.vb2_buf.timestamp = timestamp;
> -	vb2_buffer_done(&cur_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
> +	kstats_buf->buff.vb.vb2_buf.timestamp = timestamp;
> +	vb2_buffer_done(&kstats_buf->buff.vb.vb2_buf, VB2_BUF_STATE_DONE);
> +	return IRQ_HANDLED;
>  }
>  
> -static void rkisp1_stats_readout_work(struct work_struct *work)
> -{
> -	struct rkisp1_isp_readout_work *readout_work =
> -		container_of(work, struct rkisp1_isp_readout_work, work);
> -	struct rkisp1_stats *stats = readout_work->stats;
> -
> -	if (readout_work->readout == RKISP1_ISP_READOUT_MEAS)
> -		rkisp1_stats_send_measurement(stats, readout_work);
>  

Is the second blank line intentional ?

> -	kfree(readout_work);
> -}
> -
> -void rkisp1_stats_isr(struct rkisp1_stats *stats, u32 isp_ris)
> +irqreturn_t rkisp1_stats_isr(struct rkisp1_stats *stats, u32 isp_ris)
>  {
> -	unsigned int frame_sequence =
> -		atomic_read(&stats->rkisp1->isp.frame_sequence);
>  	struct rkisp1_device *rkisp1 = stats->rkisp1;
> -	struct rkisp1_isp_readout_work *work;
> +	struct rkisp1_isp *isp = &rkisp1->isp;
> +	struct rkisp1_kstats_buffer *buf = NULL;
> +	irqreturn_t ret = IRQ_HANDLED;
>  	unsigned int isp_mis_tmp = 0;
>  	unsigned long flags;
>  	u32 val;
> @@ -417,28 +398,22 @@ void rkisp1_stats_isr(struct rkisp1_stats *stats, u32 isp_ris)
>  	if (isp_mis_tmp & RKISP1_STATS_MEAS_MASK)
>  		rkisp1->debug.stats_error++;
>  
> -	if (!stats->is_streaming)
> +	if (!stats->is_streaming || !(isp_ris & RKISP1_STATS_MEAS_MASK))
>  		goto unlock;
> -	if (isp_ris & RKISP1_STATS_MEAS_MASK) {
> -		work = kzalloc(sizeof(*work), GFP_ATOMIC);
> -		if (work) {
> -			INIT_WORK(&work->work,
> -				  rkisp1_stats_readout_work);
> -			work->readout = RKISP1_ISP_READOUT_MEAS;
> -			work->stats = stats;
> -			work->frame_id = frame_sequence;
> -			work->isp_ris = isp_ris;
> -			if (!queue_work(stats->readout_wq,
> -					&work->work))
> -				kfree(work);
> -		} else {
> -			dev_err(stats->rkisp1->dev,
> -				"Could not allocate work\n");
> +
> +	list_for_each_entry(buf, &stats->stat, buff.queue) {
> +		if (!buf->ris) {
> +			buf->buff.vb.sequence =
> +				atomic_read(&isp->frame_sequence);
> +			buf->ris = isp_ris;
> +			ret = IRQ_WAKE_THREAD;
> +			break;
>  		}
>  	}
>  
>  unlock:
>  	spin_unlock_irqrestore(&stats->stats_lock, flags);
> +	return ret;
>  }
>  
>  static void rkisp1_init_stats(struct rkisp1_stats *stats)
> @@ -489,19 +464,8 @@ int rkisp1_stats_register(struct rkisp1_stats *stats,
>  		goto err_cleanup_media_entity;
>  	}
>  
> -	stats->readout_wq = alloc_workqueue("measurement_queue",
> -					    WQ_UNBOUND | WQ_MEM_RECLAIM,
> -					    1);
> -
> -	if (!stats->readout_wq) {
> -		ret = -ENOMEM;
> -		goto err_unreg_vdev;
> -	}
> -
>  	return 0;
>  
> -err_unreg_vdev:
> -	video_unregister_device(vdev);
>  err_cleanup_media_entity:
>  	media_entity_cleanup(&vdev->entity);
>  err_release_queue:
> @@ -515,7 +479,6 @@ void rkisp1_stats_unregister(struct rkisp1_stats *stats)
>  	struct rkisp1_vdev_node *node = &stats->vnode;
>  	struct video_device *vdev = &node->vdev;
>  
> -	destroy_workqueue(stats->readout_wq);
>  	video_unregister_device(vdev);
>  	media_entity_cleanup(&vdev->entity);
>  	vb2_queue_release(vdev->queue);

-- 
Regards,

Laurent Pinchart

  parent reply	other threads:[~2020-05-21  0:09 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-05-12 12:05 [PATCH 0/5] media: staging: rkisp1: change workqueue to threaded irq in stats Dafna Hirschfeld
2020-05-12 12:05 ` [PATCH 1/5] media: staging: rkisp1: return IRQ_NONE in isr when irq isn't for ISP Dafna Hirschfeld
2020-05-12 15:42   ` kbuild test robot
2020-05-20 10:58   ` Helen Koike
2020-05-20 20:58     ` Laurent Pinchart
2020-05-12 12:05 ` [PATCH 2/5] media: staging: rkisp1: use a macro for the statistics flags mask Dafna Hirschfeld
2020-05-20 11:03   ` Helen Koike
2020-05-20 23:27   ` Laurent Pinchart
2020-05-12 12:05 ` [PATCH 3/5] media: staging: rkisp1: stats: use spin_lock_irqsave for irq_lock Dafna Hirschfeld
2020-05-20 11:11   ` Helen Koike
2020-05-20 19:22     ` Dafna Hirschfeld
2020-05-20 23:40       ` Laurent Pinchart
2020-05-12 12:05 ` [PATCH 4/5] media: staging: rkisp1: stats: replace locks wq_lock, irq_lock with one lock Dafna Hirschfeld
2020-05-20 23:48   ` Laurent Pinchart
2020-05-12 12:05 ` [PATCH 5/5] media: staging: rkisp1: replace workqueue with threaded irq for reading statistics registers Dafna Hirschfeld
2020-05-12 16:41   ` kbuild test robot
2020-05-21  0:09   ` Laurent Pinchart [this message]
2020-05-21 10:38     ` Tomasz Figa
2020-05-28 19:19       ` Dafna Hirschfeld
2020-05-28 19:35         ` Tomasz Figa

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200521000901.GE25474@pendragon.ideasonboard.com \
    --to=laurent.pinchart@ideasonboard.com \
    --cc=dafna.hirschfeld@collabora.com \
    --cc=dafna3@gmail.com \
    --cc=ezequiel@collabora.com \
    --cc=helen.koike@collabora.com \
    --cc=hverkuil@xs4all.nl \
    --cc=kernel@collabora.com \
    --cc=linux-media@vger.kernel.org \
    --cc=linux-rockchip@lists.infradead.org \
    --cc=mchehab@kernel.org \
    --cc=sakari.ailus@linux.intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).