[COMMON] media: smfc: use s/w timer if HWFC is enabled
authorCho KyongHo <pullip.cho@samsung.com>
Wed, 22 Jul 2015 14:42:02 +0000 (23:42 +0900)
committerSeungchul Kim <sc377.kim@samsung.com>
Mon, 28 May 2018 05:31:10 +0000 (14:31 +0900)
If HWFC is enabled, the internal timer in the H/W is not available
because when the H/W finishes depends on the outside of SMFC itself.
Instead of the internal timer, S/W timer is used for the case of HWFC.
S/W timer is expired if the H/W is not completed in a second that is
quite huge amount of time.

Change-Id: I0a23448033b91bcb64523cebfeeee14d78c0f410
Signed-off-by: Cho KyongHo <pullip.cho@samsung.com>
drivers/media/platform/exynos/smfc/smfc.c
drivers/media/platform/exynos/smfc/smfc.h

index 2722162fb7e395b983b21a5cc9af20f5950be2f4..9c2c9992cbad5c02fbac12f6a18867dc8ca7e37b 100644 (file)
@@ -302,14 +302,25 @@ static irqreturn_t exynos_smfc_irq_handler(int irq, void *priv)
 
        ktime = ktime_get();
 
+       spin_lock(&smfc->flag_lock);
+
+       if (!!(smfc->flags & SMFC_DEV_TIMEDOUT)) {
+               /* The tieout handler does the rest */
+               dev_err(smfc->dev, "Interrupt occurred after timed-out.\n");
+               spin_unlock(&smfc->flag_lock);
+               return IRQ_HANDLED;
+       }
+
        if (!(smfc->flags & SMFC_DEV_RUNNING)) {
                smfc_dump_registers(smfc);
                BUG();
        }
 
-       spin_lock(&smfc->flag_lock);
        suspending = !!(smfc->flags & SMFC_DEV_SUSPENDING);
-       smfc->flags &= ~SMFC_DEV_RUNNING;
+       if (!!(smfc->flags & SMFC_DEV_OTF_EMUMODE))
+               del_timer(&smfc->timer);
+       smfc->flags &= ~(SMFC_DEV_RUNNING | SMFC_DEV_OTF_EMUMODE);
+
        spin_unlock(&smfc->flag_lock);
 
        if (!smfc_hwstatus_okay(smfc, ctx)) {
@@ -369,6 +380,60 @@ static irqreturn_t exynos_smfc_irq_handler(int irq, void *priv)
        return IRQ_HANDLED;
 }
 
+static void smfc_timedout_handler(unsigned long arg)
+{
+       struct smfc_dev *smfc = (struct smfc_dev *)arg;
+       struct smfc_ctx *ctx;
+       unsigned long flags;
+       bool suspending;
+
+       spin_lock_irqsave(&smfc->flag_lock, flags);
+       if (!(smfc->flags & SMFC_DEV_RUNNING)) {
+               /* Interrupt is occurred before timer handler is called */
+               spin_unlock_irqrestore(&smfc->flag_lock, flags);
+               return;
+       }
+
+       /* timer is enabled only when HWFC is enabled */
+       BUG_ON(!(smfc->flags & SMFC_DEV_OTF_EMUMODE));
+       suspending = !!(smfc->flags & SMFC_DEV_SUSPENDING);
+       smfc->flags |= SMFC_DEV_TIMEDOUT; /* indicate the timedout is handled */
+       smfc->flags &= ~(SMFC_DEV_RUNNING | SMFC_DEV_OTF_EMUMODE);
+       spin_unlock_irqrestore(&smfc->flag_lock, flags);
+
+       dev_err(smfc->dev, "=== TIMED-OUT! (1 sec.) =========================");
+       smfc_dump_registers(smfc);
+       smfc_hwconfigure_reset(smfc);
+
+       if (!IS_ERR(smfc->clk_gate)) {
+               clk_disable(smfc->clk_gate);
+               if (!IS_ERR(smfc->clk_gate2))
+                       clk_disable(smfc->clk_gate2);
+       }
+
+       pm_runtime_put(smfc->dev);
+
+       ctx = v4l2_m2m_get_curr_priv(smfc->m2mdev);
+       if (ctx) {
+               v4l2_m2m_buf_done(v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx),
+                                                       VB2_BUF_STATE_ERROR);
+               v4l2_m2m_buf_done(v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx),
+                                                       VB2_BUF_STATE_ERROR);
+               if (!suspending) {
+                       v4l2_m2m_job_finish(smfc->m2mdev, ctx->fh.m2m_ctx);
+               } else {
+                       spin_lock(&smfc->flag_lock);
+                       spin_unlock(&smfc->flag_lock);
+               }
+       }
+
+       spin_lock_irqsave(&smfc->flag_lock, flags);
+       /* finished timedout handling and suspend() can return */
+       smfc->flags &= ~(SMFC_DEV_TIMEDOUT | SMFC_DEV_SUSPENDING);
+       spin_unlock_irqrestore(&smfc->flag_lock, flags);
+}
+
+
 static int smfc_vb2_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
                                unsigned int *num_planes, unsigned int sizes[],
                                struct device *alloc_devs[])
@@ -1210,8 +1275,17 @@ static void smfc_m2m_device_run(void *priv)
 
        spin_lock_irqsave(&ctx->smfc->flag_lock, flags);
        ctx->smfc->flags |= SMFC_DEV_RUNNING;
+       if (!!enable_hwfc)
+               ctx->smfc->flags |= SMFC_DEV_OTF_EMUMODE;
        spin_unlock_irqrestore(&ctx->smfc->flag_lock, flags);
 
+       /*
+        * SMFC internal timer is unavailable if HWFC is enabled
+        * Therefore, S/W timer object is used to detect unexpected delay.
+        */
+       if (!!enable_hwfc)
+               mod_timer(&ctx->smfc->timer, jiffies + HZ); /* 1 sec. */
+
        ctx->ktime_beg = ktime_get();
 
        smfc_hwconfigure_start(ctx, restart_interval, !!enable_hwfc);
@@ -1446,6 +1520,8 @@ static int exynos_smfc_probe(struct platform_device *pdev)
        if (ret < 0)
                return ret;
 
+       setup_timer(&smfc->timer, smfc_timedout_handler, (unsigned long)smfc);
+
        platform_set_drvdata(pdev, smfc);
 
        spin_lock_init(&smfc->flag_lock);
index 1494b3d0a31f180a03d9e5fa4db27be888cbfb5e..d6778d73151ea44e310d793d14217d53383f44f7 100644 (file)
@@ -43,8 +43,14 @@ static inline bool is_jpeg(const struct smfc_image_format *fmt)
        return fmt->bpp_buf[0] == 0;
 }
 
+/* Set when H/W starts, cleared in irq/timeout handler */
 #define SMFC_DEV_RUNNING       (1 << 0)
+/* Set when suspend handler is called, cleared before irq handler returns. */
 #define SMFC_DEV_SUSPENDING    (1 << 1)
+/* Set when timeout handler is called, cleared before the handler returns. */
+#define SMFC_DEV_TIMEDOUT      (1 << 3)
+/* Set if HWFC is enabled in device_run, cleared in irq/timeout handler */
+#define SMFC_DEV_OTF_EMUMODE   (1 << 4)
 
 struct smfc_dev {
        struct v4l2_device v4l2_dev;
@@ -54,6 +60,7 @@ struct smfc_dev {
        void __iomem *reg;
        spinlock_t flag_lock;
        struct mutex video_device_mutex;
+       struct timer_list timer;
        int device_id;
        u32 hwver;
        u32 flags;