diff options
Diffstat (limited to 'drivers/media/platform/vsp1/vsp1_dl.c')
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_dl.c | 84 |
1 files changed, 56 insertions, 28 deletions
diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index 26289adaf658..104b6f514536 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -178,7 +178,7 @@ struct vsp1_dl_cmd_pool { * @post_cmd: post command to be issued through extended dl header * @has_chain: if true, indicates that there's a partition chain * @chain: entry in the display list partition chain - * @internal: whether the display list is used for internal purpose + * @flags: display list flags, a combination of VSP1_DL_FRAME_END_* */ struct vsp1_dl_list { struct list_head list; @@ -197,7 +197,7 @@ struct vsp1_dl_list { bool has_chain; struct list_head chain; - bool internal; + unsigned int flags; }; /** @@ -699,8 +699,8 @@ struct vsp1_dl_body *vsp1_dl_list_get_body0(struct vsp1_dl_list *dl) * which bodies are added. * * Adding a body to a display list passes ownership of the body to the list. The - * caller retains its reference to the fragment when adding it to the display - * list, but is not allowed to add new entries to the body. + * caller retains its reference to the body when adding it to the display list, + * but is not allowed to add new entries to the body. * * The reference must be explicitly released by a call to vsp1_dl_body_put() * when the body isn't needed anymore. @@ -770,17 +770,35 @@ static void vsp1_dl_list_fill_header(struct vsp1_dl_list *dl, bool is_last) } dl->header->num_lists = num_lists; + dl->header->flags = 0; - if (!list_empty(&dl->chain) && !is_last) { + /* + * Enable the interrupt for the end of each frame. In continuous mode + * chained lists are used with one list per frame, so enable the + * interrupt for each list. In singleshot mode chained lists are used + * to partition a single frame, so enable the interrupt for the last + * list only. + */ + if (!dlm->singleshot || is_last) + dl->header->flags |= VSP1_DLH_INT_ENABLE; + + /* + * In continuous mode enable auto-start for all lists, as the VSP must + * loop on the same list until a new one is queued. In singleshot mode + * enable auto-start for all lists but the last to chain processing of + * partitions without software intervention. + */ + if (!dlm->singleshot || !is_last) + dl->header->flags |= VSP1_DLH_AUTO_START; + + if (!is_last) { /* - * If this display list's chain is not empty, we are on a list, - * and the next item is the display list that we must queue for - * automatic processing by the hardware. + * If this is not the last display list in the chain, queue the + * next item for automatic processing by the hardware. */ struct vsp1_dl_list *next = list_next_entry(dl, chain); dl->header->next_header = next->dma; - dl->header->flags = VSP1_DLH_AUTO_START; } else if (!dlm->singleshot) { /* * if the display list manager works in continuous mode, the VSP @@ -788,13 +806,6 @@ static void vsp1_dl_list_fill_header(struct vsp1_dl_list *dl, bool is_last) * instructed to do otherwise. */ dl->header->next_header = dl->dma; - dl->header->flags = VSP1_DLH_INT_ENABLE | VSP1_DLH_AUTO_START; - } else { - /* - * Otherwise, in mem-to-mem mode, we work in single-shot mode - * and the next display list must not be started automatically. - */ - dl->header->flags = VSP1_DLH_INT_ENABLE; } if (!dl->extension) @@ -861,13 +872,15 @@ static void vsp1_dl_list_commit_continuous(struct vsp1_dl_list *dl) * * If a display list is already pending we simply drop it as the new * display list is assumed to contain a more recent configuration. It is - * an error if the already pending list has the internal flag set, as - * there is then a process waiting for that list to complete. This - * shouldn't happen as the waiting process should perform proper - * locking, but warn just in case. + * an error if the already pending list has the + * VSP1_DL_FRAME_END_INTERNAL flag set, as there is then a process + * waiting for that list to complete. This shouldn't happen as the + * waiting process should perform proper locking, but warn just in + * case. */ if (vsp1_dl_list_hw_update_pending(dlm)) { - WARN_ON(dlm->pending && dlm->pending->internal); + WARN_ON(dlm->pending && + (dlm->pending->flags & VSP1_DL_FRAME_END_INTERNAL)); __vsp1_dl_list_put(dlm->pending); dlm->pending = dl; return; @@ -897,7 +910,7 @@ static void vsp1_dl_list_commit_singleshot(struct vsp1_dl_list *dl) dlm->active = dl; } -void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal) +void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags) { struct vsp1_dl_manager *dlm = dl->dlm; struct vsp1_dl_list *dl_next; @@ -912,7 +925,7 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal) vsp1_dl_list_fill_header(dl_next, last); } - dl->internal = internal; + dl->flags = dl_flags & ~VSP1_DL_FRAME_END_COMPLETED; spin_lock_irqsave(&dlm->lock, flags); @@ -941,9 +954,13 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal) * set in single-shot mode as display list processing is then not continuous and * races never occur. * - * The VSP1_DL_FRAME_END_INTERNAL flag indicates that the previous display list - * has completed and had been queued with the internal notification flag. - * Internal notification is only supported for continuous mode. + * The following flags are only supported for continuous mode. + * + * The VSP1_DL_FRAME_END_INTERNAL flag indicates that the display list that just + * became active had been queued with the internal notification flag. + * + * The VSP1_DL_FRAME_END_WRITEBACK flag indicates that the previously active + * display list had been queued with the writeback flag. */ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) { @@ -982,13 +999,24 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) goto done; /* + * If the active display list has the writeback flag set, the frame + * completion marks the end of the writeback capture. Return the + * VSP1_DL_FRAME_END_WRITEBACK flag and reset the display list's + * writeback flag. + */ + if (dlm->active && (dlm->active->flags & VSP1_DL_FRAME_END_WRITEBACK)) { + flags |= VSP1_DL_FRAME_END_WRITEBACK; + dlm->active->flags &= ~VSP1_DL_FRAME_END_WRITEBACK; + } + + /* * The device starts processing the queued display list right after the * frame end interrupt. The display list thus becomes active. */ if (dlm->queued) { - if (dlm->queued->internal) + if (dlm->queued->flags & VSP1_DL_FRAME_END_INTERNAL) flags |= VSP1_DL_FRAME_END_INTERNAL; - dlm->queued->internal = false; + dlm->queued->flags &= ~VSP1_DL_FRAME_END_INTERNAL; __vsp1_dl_list_put(dlm->active); dlm->active = dlm->queued; |