[media] omap3isp: Prevent pipelines that contain a crashed entity from starting
[deliverable/linux.git] / drivers / media / video / omap3isp / isp.c
index 12d5f923e1d0fbf01ff18c08ce904cea53ab3fc4..3db8583497ee4b95a4b1a18142e99a8978461421 100644 (file)
@@ -739,6 +739,17 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe,
        unsigned long flags;
        int ret;
 
+       /* If the preview engine crashed it might not respond to read/write
+        * operations on the L4 bus. This would result in a bus fault and a
+        * kernel oops. Refuse to start streaming in that case. This check must
+        * be performed before the loop below to avoid starting entities if the
+        * pipeline won't start anyway (those entities would then likely fail to
+        * stop, making the problem worse).
+        */
+       if ((pipe->entities & isp->crashed) &
+           (1U << isp->isp_prev.subdev.entity.id))
+               return -EIO;
+
        spin_lock_irqsave(&pipe->lock, flags);
        pipe->state &= ~(ISP_PIPELINE_IDLE_INPUT | ISP_PIPELINE_IDLE_OUTPUT);
        spin_unlock_irqrestore(&pipe->lock, flags);
@@ -879,13 +890,15 @@ static int isp_pipeline_disable(struct isp_pipeline *pipe)
 
                if (ret) {
                        dev_info(isp->dev, "Unable to stop %s\n", subdev->name);
+                       /* If the entity failed to stopped, assume it has
+                        * crashed. Mark it as such, the ISP will be reset when
+                        * applications will release it.
+                        */
+                       isp->crashed |= 1U << subdev->entity.id;
                        failure = -ETIMEDOUT;
                }
        }
 
-       if (failure < 0)
-               isp->needs_reset = true;
-
        return failure;
 }
 
@@ -1069,6 +1082,7 @@ static int isp_reset(struct isp_device *isp)
                udelay(1);
        }
 
+       isp->crashed = 0;
        return 0;
 }
 
@@ -1496,10 +1510,11 @@ void omap3isp_put(struct isp_device *isp)
        if (--isp->ref_count == 0) {
                isp_disable_interrupts(isp);
                isp_save_ctx(isp);
-               if (isp->needs_reset) {
+               /* Reset the ISP if an entity has failed to stop. This is the
+                * only way to recover from such conditions.
+                */
+               if (isp->crashed)
                        isp_reset(isp);
-                       isp->needs_reset = false;
-               }
                isp_disable_clocks(isp);
        }
        mutex_unlock(&isp->isp_mutex);
This page took 0.052247 seconds and 5 git commands to generate.