diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index dffa31e274d6..5ad604d0d16b 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -573,12 +573,22 @@ static int iss_pipeline_link_notify(struct media_link *link, u32 flags, static int iss_pipeline_enable(struct iss_pipeline *pipe, enum iss_pipeline_stream_state mode) { + struct iss_device *iss = pipe->output->iss; struct media_entity *entity; struct media_pad *pad; struct v4l2_subdev *subdev; unsigned long flags; int ret; + /* If one of the entities in the pipeline has crashed it will not work + * properly. 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 & iss->crashed) + return -EIO; + spin_lock_irqsave(&pipe->lock, flags); pipe->state &= ~(ISS_PIPELINE_IDLE_INPUT | ISS_PIPELINE_IDLE_OUTPUT); spin_unlock_irqrestore(&pipe->lock, flags); @@ -617,6 +627,7 @@ static int iss_pipeline_enable(struct iss_pipeline *pipe, */ static int iss_pipeline_disable(struct iss_pipeline *pipe) { + struct iss_device *iss = pipe->output->iss; struct media_entity *entity; struct media_pad *pad; struct v4l2_subdev *subdev; @@ -641,6 +652,11 @@ static int iss_pipeline_disable(struct iss_pipeline *pipe) if (ret < 0) { dev_dbg(iss->dev, "%s: module stop timeout.\n", subdev->name); + /* If the entity failed to stopped, assume it has + * crashed. Mark it as such, the ISS will be reset when + * applications will release it. + */ + iss->crashed |= 1U << subdev->entity.id; failure = -ETIMEDOUT; } } @@ -715,6 +731,7 @@ static int iss_reset(struct iss_device *iss) usleep_range(10, 10); } + iss->crashed = 0; return 0; } @@ -1058,6 +1075,13 @@ void omap4iss_put(struct iss_device *iss) BUG_ON(iss->ref_count == 0); if (--iss->ref_count == 0) { iss_disable_interrupts(iss); + /* Reset the ISS if an entity has failed to stop. This is the + * only way to recover from such conditions, although it would + * be worth investigating whether resetting the ISP only can't + * fix the problem in some cases. + */ + if (iss->crashed) + iss_reset(iss); iss_disable_clocks(iss); } mutex_unlock(&iss->iss_mutex); diff --git a/drivers/staging/media/omap4iss/iss.h b/drivers/staging/media/omap4iss/iss.h index f63caaf90f33..3c1e92024513 100644 --- a/drivers/staging/media/omap4iss/iss.h +++ b/drivers/staging/media/omap4iss/iss.h @@ -77,6 +77,10 @@ struct iss_reg { u32 val; }; +/* + * struct iss_device - ISS device structure. + * @crashed: Bitmask of crashed entities (indexed by entity ID) + */ struct iss_device { struct v4l2_device v4l2_dev; struct media_device media_dev; @@ -93,6 +97,7 @@ struct iss_device { u64 raw_dmamask; struct mutex iss_mutex; /* For handling ref_count field */ + bool crashed; int has_context; int ref_count; diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 68eab6e6dc36..910648766e6c 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -772,6 +772,8 @@ iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) { struct iss_video_fh *vfh = to_iss_video_fh(fh); struct iss_video *video = video_drvdata(file); + struct media_entity_graph graph; + struct media_entity *entity; enum iss_pipeline_state state; struct iss_pipeline *pipe; struct iss_video *far_end; @@ -791,6 +793,7 @@ iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) pipe->external = NULL; pipe->external_rate = 0; pipe->external_bpp = 0; + pipe->entities = 0; if (video->iss->pdata->set_constraints) video->iss->pdata->set_constraints(video->iss, true); @@ -799,6 +802,11 @@ iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) if (ret < 0) goto err_media_entity_pipeline_start; + entity = &video->video.entity; + media_entity_graph_walk_start(&graph, entity); + while ((entity = media_entity_graph_walk_next(&graph))) + pipe->entities |= 1 << entity->id; + /* Verify that the currently configured format matches the output of * the connected subdev. */ diff --git a/drivers/staging/media/omap4iss/iss_video.h b/drivers/staging/media/omap4iss/iss_video.h index 2c8d256a87c5..73e1a3419b01 100644 --- a/drivers/staging/media/omap4iss/iss_video.h +++ b/drivers/staging/media/omap4iss/iss_video.h @@ -77,6 +77,7 @@ enum iss_pipeline_state { /* * struct iss_pipeline - An OMAP4 ISS hardware pipeline + * @entities: Bitmask of entities in the pipeline (indexed by entity ID) * @error: A hardware error occurred during capture */ struct iss_pipeline { @@ -86,6 +87,7 @@ struct iss_pipeline { enum iss_pipeline_stream_state stream_state; struct iss_video *input; struct iss_video *output; + unsigned int entities; atomic_t frame_number; bool do_propagation; /* of frame number */ bool error;