media: tegra: add completion sync
Bryan Wu [Fri, 8 Apr 2016 06:52:26 +0000 (23:52 -0700)]
When stop streaming, 2 kernel threads need synchronization. Using
completion framework in kernel to sync between kthreads.

Also remove lock and flags for HDMI unplug operation, which can be
achieved by completion sync.

Bug 1736471

Change-Id: I10df7499426a6d7b5489d836590f095f2d1baf12
Reviewed-on: http://git-master/r/1122379
Signed-off-by: Bryan Wu <pengw@nvidia.com>
Signed-off-by: Ahung Cheng <ahcheng@nvidia.com>
(cherry picked from commit 2caab0bb07458efecd8934493677477a129d5d5e)
Reviewed-on: http://git-master/r/1124256
Signed-off-by: Bryan Wu <pengw@nvidia.com>
Reviewed-on: http://git-master/r/1141898
Reviewed-by: Automatic_Commit_Validation_User
GVS: Gerrit_Virtual_Submit
Reviewed-by: Jihoon Bang <jbang@nvidia.com>

drivers/media/platform/tegra/camera/channel.c
drivers/media/platform/tegra/camera/mc_common.h
drivers/media/platform/tegra/camera/registers.h
drivers/media/platform/tegra/csi/csi.c

index 38478b4..effeb21 100644 (file)
@@ -271,14 +271,6 @@ static void tegra_channel_capture_frame(struct tegra_channel *chan,
        int index = 0;
        u32 thresh[TEGRA_CSI_BLOCKS] = { 0 };
        int valid_ports = chan->valid_ports;
-       bool is_hdmiin_unplug;
-
-       spin_lock(&chan->hdmiin_lock);
-       is_hdmiin_unplug = chan->is_hdmiin_unplug;
-       spin_unlock(&chan->hdmiin_lock);
-
-       if (is_hdmiin_unplug)
-               return;
 
        for (index = 0; index < valid_ports; index++) {
                /* Program buffer address by using surface 0 */
@@ -314,14 +306,18 @@ static void tegra_channel_capture_frame(struct tegra_channel *chan,
                }
        }
 
-       /* Move buffer to capture done queue */
-       spin_lock(&chan->done_lock);
-       list_add_tail(&buf->queue, &chan->done);
-       spin_unlock(&chan->done_lock);
-
-       /* Wait up kthread for capture done */
-       wake_up_interruptible(&chan->done_wait);
+       if (atomic_read(&chan->is_hdmiin_unplug)) {
+               vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR);
+               complete(&chan->capture_comp);
+       } else {
+               /* Move buffer to capture done queue */
+               spin_lock(&chan->done_lock);
+               list_add_tail(&buf->queue, &chan->done);
+               spin_unlock(&chan->done_lock);
 
+               /* Wait up kthread for capture done */
+               wake_up_interruptible(&chan->done_wait);
+       }
 }
 
 static void tegra_channel_capture_done(struct tegra_channel *chan,
@@ -333,14 +329,6 @@ static void tegra_channel_capture_done(struct tegra_channel *chan,
        int index = 0;
        u32 thresh[TEGRA_CSI_BLOCKS] = { 0 };
        int valid_ports = chan->valid_ports;
-       bool is_hdmiin_unplug;
-
-       spin_lock(&chan->hdmiin_lock);
-       is_hdmiin_unplug = chan->is_hdmiin_unplug;
-       spin_unlock(&chan->hdmiin_lock);
-
-       if (is_hdmiin_unplug)
-               return;
 
        for (index = 0; index < valid_ports; index++) {
                /* Program syncpoint */
@@ -364,12 +352,19 @@ static void tegra_channel_capture_done(struct tegra_channel *chan,
                                "MW_ACK_DONE syncpoint time out!%d\n", index);
        }
 
-       /* Captured one frame */
-       vb->v4l2_buf.sequence = chan->sequence++;
-       vb->v4l2_buf.field = V4L2_FIELD_NONE;
-       v4l2_get_timestamp(&vb->v4l2_buf.timestamp);
-       vb2_set_plane_payload(vb, 0, chan->format.sizeimage);
-       vb2_buffer_done(vb, err < 0 ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+       if (atomic_read(&chan->is_hdmiin_unplug)) {
+               vb2_buffer_done(vb, err < 0 ? VB2_BUF_STATE_ERROR :
+                               VB2_BUF_STATE_DONE);
+               complete(&chan->done_comp);
+       } else {
+               /* Captured one frame */
+               vb->v4l2_buf.sequence = chan->sequence++;
+               vb->v4l2_buf.field = V4L2_FIELD_NONE;
+               v4l2_get_timestamp(&vb->v4l2_buf.timestamp);
+               vb2_set_plane_payload(vb, 0, chan->format.sizeimage);
+               vb2_buffer_done(vb, err < 0 ? VB2_BUF_STATE_ERROR :
+                               VB2_BUF_STATE_DONE);
+       }
 }
 
 static int tegra_channel_kthread_capture_start(void *data)
@@ -383,9 +378,13 @@ static int tegra_channel_kthread_capture_start(void *data)
                try_to_freeze();
                wait_event_interruptible(chan->start_wait,
                                         !list_empty(&chan->capture) ||
-                                        kthread_should_stop());
-               if (kthread_should_stop())
+                                        kthread_should_stop() ||
+                                        atomic_read(&chan->is_hdmiin_unplug));
+               if (kthread_should_stop() ||
+                   atomic_read(&chan->is_hdmiin_unplug)) {
+                       complete(&chan->capture_comp);
                        break;
+               }
 
                spin_lock(&chan->start_lock);
                if (list_empty(&chan->capture)) {
@@ -415,9 +414,19 @@ static int tegra_channel_kthread_capture_done(void *data)
                try_to_freeze();
                wait_event_interruptible(chan->done_wait,
                                         !list_empty(&chan->done) ||
-                                        kthread_should_stop());
-               if (kthread_should_stop() && list_empty(&chan->done))
+                                        kthread_should_stop() ||
+                                        atomic_read(&chan->is_hdmiin_unplug));
+
+               if ((chan->vi->pg_mode &&
+                       kthread_should_stop()) ||
+                       atomic_read(&chan->is_hdmiin_unplug)) {
+                       complete(&chan->done_comp);
+                       break;
+               } else if (kthread_should_stop() &&
+                       list_empty(&chan->done)) {
+                       complete(&chan->done_comp);
                        break;
+               }
 
                spin_lock(&chan->done_lock);
                if (list_empty(&chan->done)) {
@@ -436,18 +445,27 @@ static int tegra_channel_kthread_capture_done(void *data)
        return 0;
 }
 
+static void tegra_channel_queued_buf_done(struct tegra_channel *chan,
+                                         enum vb2_buffer_state state,
+                                         bool is_capture);
+
 static void tegra_channel_stop_kthreads(struct tegra_channel *chan)
 {
+
        mutex_lock(&chan->stop_kthread_lock);
        /* Stop the kthread for capture */
        if (chan->kthread_capture_start) {
                kthread_stop(chan->kthread_capture_start);
+               wait_for_completion(&chan->capture_comp);
                chan->kthread_capture_start = NULL;
        }
        if (chan->kthread_capture_done) {
                kthread_stop(chan->kthread_capture_done);
+               wait_for_completion(&chan->done_comp);
                chan->kthread_capture_done = NULL;
        }
+       tegra_channel_queued_buf_done(chan, VB2_BUF_STATE_ERROR, 1);
+       tegra_channel_queued_buf_done(chan, VB2_BUF_STATE_ERROR, 0);
        mutex_unlock(&chan->stop_kthread_lock);
 }
 
@@ -483,12 +501,9 @@ void tegra_channel_query_hdmiin_unplug(struct tegra_channel *chan,
                        bt = &timings.bt;
                        if (bt->width == 0 && bt->height == 0) {
                                dev_info(&chan->video.dev,
-                                               "Got unplug event during capture!\n");
-
-                               spin_lock(&chan->hdmiin_lock);
-                               chan->is_hdmiin_unplug = true;
-                               spin_unlock(&chan->hdmiin_lock);
+                                        "Got unplug event during capture!\n");
 
+                               atomic_set(&chan->is_hdmiin_unplug, 1);
                                for (index = 0; index < valid_ports; index++) {
                                        nvhost_syncpt_cpu_incr_ext(
                                                        chan->vi->ndev,
@@ -564,16 +579,19 @@ static void tegra_channel_buffer_queue(struct vb2_buffer *vb)
 
 /* Return all queued buffers back to videobuf2 */
 static void tegra_channel_queued_buf_done(struct tegra_channel *chan,
-                                         enum vb2_buffer_state state)
+                                         enum vb2_buffer_state state,
+                                         bool is_capture)
 {
        struct tegra_channel_buffer *buf, *nbuf;
+       spinlock_t *lock = is_capture ? &chan->start_lock : &chan->done_lock;
+       struct list_head *q = is_capture ? &chan->capture : &chan->done;
 
-       spin_lock(&chan->start_lock);
-       list_for_each_entry_safe(buf, nbuf, &chan->capture, queue) {
+       spin_lock(lock);
+       list_for_each_entry_safe(buf, nbuf, q, queue) {
                vb2_buffer_done(&buf->buf, state);
                list_del(&buf->queue);
        }
-       spin_unlock(&chan->start_lock);
+       spin_unlock(lock);
 }
 
 /*
@@ -658,10 +676,6 @@ static int tegra_channel_start_streaming(struct vb2_queue *vq, u32 count)
        struct media_pipeline *pipe = chan->video.entity.pipe;
        int ret = 0, i;
 
-       spin_lock(&chan->hdmiin_lock);
-       chan->is_hdmiin_unplug = false;
-       spin_unlock(&chan->hdmiin_lock);
-
        if (!chan->vi->pg_mode) {
                ret = media_entity_pipeline_start(&chan->video.entity, pipe);
                if (ret < 0)
@@ -684,6 +698,8 @@ static int tegra_channel_start_streaming(struct vb2_queue *vq, u32 count)
                goto error_capture_setup;
 
        chan->sequence = 0;
+       atomic_set(&chan->is_hdmiin_unplug, 0);
+
        /* Start kthread to capture data to buffer */
        chan->kthread_capture_start = kthread_run(
                                        tegra_channel_kthread_capture_start,
@@ -716,7 +732,7 @@ error_set_stream:
                media_entity_pipeline_stop(&chan->video.entity);
 error_pipeline_start:
        vq->start_streaming_called = 0;
-       tegra_channel_queued_buf_done(chan, VB2_BUF_STATE_QUEUED);
+       tegra_channel_queued_buf_done(chan, VB2_BUF_STATE_QUEUED, 1);
 
        return ret;
 }
@@ -730,8 +746,7 @@ static int tegra_channel_stop_streaming(struct vb2_queue *vq)
                tegra_channel_stop_kthreads(chan);
                for (index = 0; index < chan->valid_ports; index++)
                        tegra_csi_stop_streaming(chan->vi->csi,
-                                                       chan->port[index]);
-               tegra_channel_queued_buf_done(chan, VB2_BUF_STATE_ERROR);
+                                                chan->port[index]);
                tegra_channel_update_clknbw(chan, 0);
        }
 
@@ -1548,8 +1563,10 @@ static int tegra_channel_init(struct tegra_mc_vi *vi, unsigned int index)
        init_waitqueue_head(&chan->done_wait);
        spin_lock_init(&chan->start_lock);
        spin_lock_init(&chan->done_lock);
-       spin_lock_init(&chan->hdmiin_lock);
        mutex_init(&chan->stop_kthread_lock);
+       init_completion(&chan->capture_comp);
+       init_completion(&chan->done_comp);
+       atomic_set(&chan->is_hdmiin_unplug, 0);
 
        /* Init video format */
        chan->fmtinfo = tegra_core_get_format_by_code(TEGRA_VF_DEF);
index 2b35758..544582b 100644 (file)
@@ -130,7 +130,8 @@ struct tegra_channel {
        spinlock_t start_lock;
        struct list_head done;
        spinlock_t done_lock;
-       spinlock_t hdmiin_lock;
+       struct completion capture_comp;
+       struct completion done_comp;
 
        void __iomem *csibase[TEGRA_CSI_BLOCKS];
        unsigned int align;
@@ -152,7 +153,7 @@ struct tegra_channel {
        atomic_t power_on_refcnt;
        struct v4l2_fh *fh;
        bool bypass;
-       bool is_hdmiin_unplug;
+       atomic_t is_hdmiin_unplug;
        int requested_kbyteps;
        unsigned long requested_hz;
 };
index becf286..004a8f7 100644 (file)
 #define TEGRA_CSI_PATTERN_GENERATOR_CTRL               0x000
 #define PG_MODE_OFFSET                                 2
 #define PG_ENABLE                                      0x1
+#define PG_DISABLE                                     0x0
 
 #define TEGRA_CSI_PG_BLANK                             0x004
 #define TEGRA_CSI_PG_PHASE                             0x008
index af9e421..cef829b 100644 (file)
@@ -418,6 +418,9 @@ void tegra_csi_stop_streaming(struct tegra_csi_device *csi,
 {
        struct tegra_csi_port *port = &csi->ports[port_num];
 
+       if (csi->pg_mode)
+               tpg_write(port, TEGRA_CSI_PATTERN_GENERATOR_CTRL, PG_DISABLE);
+
        pp_write(port, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND,
                 (0xF << CSI_PP_START_MARKER_FRAME_MAX_OFFSET) |
                 CSI_PP_DISABLE);