1170 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * vsp1_dl.c -- R-Car VSP1 Display List4 *5 * Copyright (C) 2015 Renesas Corporation6 *7 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)8 */9 10#include <linux/device.h>11#include <linux/dma-mapping.h>12#include <linux/gfp.h>13#include <linux/refcount.h>14#include <linux/slab.h>15#include <linux/workqueue.h>16 17#include "vsp1.h"18#include "vsp1_dl.h"19 20#define VSP1_DL_NUM_ENTRIES 25621 22#define VSP1_DLH_INT_ENABLE (1 << 1)23#define VSP1_DLH_AUTO_START (1 << 0)24 25#define VSP1_DLH_EXT_PRE_CMD_EXEC (1 << 9)26#define VSP1_DLH_EXT_POST_CMD_EXEC (1 << 8)27 28struct vsp1_dl_header_list {29 u32 num_bytes;30 u32 addr;31} __packed;32 33struct vsp1_dl_header {34 u32 num_lists;35 struct vsp1_dl_header_list lists[8];36 u32 next_header;37 u32 flags;38} __packed;39 40/**41 * struct vsp1_dl_ext_header - Extended display list header42 * @padding: padding zero bytes for alignment43 * @pre_ext_dl_num_cmd: number of pre-extended command bodies to parse44 * @flags: enables or disables execution of the pre and post command45 * @pre_ext_dl_plist: start address of pre-extended display list bodies46 * @post_ext_dl_num_cmd: number of post-extended command bodies to parse47 * @post_ext_dl_plist: start address of post-extended display list bodies48 */49struct vsp1_dl_ext_header {50 u32 padding;51 52 /*53 * The datasheet represents flags as stored before pre_ext_dl_num_cmd,54 * expecting 32-bit accesses. The flags are appropriate to the whole55 * header, not just the pre_ext command, and thus warrant being56 * separated out. Due to byte ordering, and representing as 16 bit57 * values here, the flags must be positioned after the58 * pre_ext_dl_num_cmd.59 */60 u16 pre_ext_dl_num_cmd;61 u16 flags;62 u32 pre_ext_dl_plist;63 64 u32 post_ext_dl_num_cmd;65 u32 post_ext_dl_plist;66} __packed;67 68struct vsp1_dl_header_extended {69 struct vsp1_dl_header header;70 struct vsp1_dl_ext_header ext;71} __packed;72 73struct vsp1_dl_entry {74 u32 addr;75 u32 data;76} __packed;77 78/**79 * struct vsp1_pre_ext_dl_body - Pre Extended Display List Body80 * @opcode: Extended display list command operation code81 * @flags: Pre-extended command flags. These are specific to each command82 * @address_set: Source address set pointer. Must have 16-byte alignment83 * @reserved: Zero bits for alignment.84 */85struct vsp1_pre_ext_dl_body {86 u32 opcode;87 u32 flags;88 u32 address_set;89 u32 reserved;90} __packed;91 92/**93 * struct vsp1_dl_body - Display list body94 * @list: entry in the display list list of bodies95 * @free: entry in the pool free body list96 * @refcnt: reference tracking for the body97 * @pool: pool to which this body belongs98 * @entries: array of entries99 * @dma: DMA address of the entries100 * @size: size of the DMA memory in bytes101 * @num_entries: number of stored entries102 * @max_entries: number of entries available103 */104struct vsp1_dl_body {105 struct list_head list;106 struct list_head free;107 108 refcount_t refcnt;109 110 struct vsp1_dl_body_pool *pool;111 112 struct vsp1_dl_entry *entries;113 dma_addr_t dma;114 size_t size;115 116 unsigned int num_entries;117 unsigned int max_entries;118};119 120/**121 * struct vsp1_dl_body_pool - display list body pool122 * @dma: DMA address of the entries123 * @size: size of the full DMA memory pool in bytes124 * @mem: CPU memory pointer for the pool125 * @bodies: Array of DLB structures for the pool126 * @free: List of free DLB entries127 * @lock: Protects the free list128 * @vsp1: the VSP1 device129 */130struct vsp1_dl_body_pool {131 /* DMA allocation */132 dma_addr_t dma;133 size_t size;134 void *mem;135 136 /* Body management */137 struct vsp1_dl_body *bodies;138 struct list_head free;139 spinlock_t lock;140 141 struct vsp1_device *vsp1;142};143 144/**145 * struct vsp1_dl_cmd_pool - Display List commands pool146 * @dma: DMA address of the entries147 * @size: size of the full DMA memory pool in bytes148 * @mem: CPU memory pointer for the pool149 * @cmds: Array of command structures for the pool150 * @free: Free pool entries151 * @lock: Protects the free list152 * @vsp1: the VSP1 device153 */154struct vsp1_dl_cmd_pool {155 /* DMA allocation */156 dma_addr_t dma;157 size_t size;158 void *mem;159 160 struct vsp1_dl_ext_cmd *cmds;161 struct list_head free;162 163 spinlock_t lock;164 165 struct vsp1_device *vsp1;166};167 168/**169 * struct vsp1_dl_list - Display list170 * @list: entry in the display list manager lists171 * @dlm: the display list manager172 * @header: display list header173 * @extension: extended display list header. NULL for normal lists174 * @dma: DMA address for the header175 * @body0: first display list body176 * @bodies: list of extra display list bodies177 * @pre_cmd: pre command to be issued through extended dl header178 * @post_cmd: post command to be issued through extended dl header179 * @has_chain: if true, indicates that there's a partition chain180 * @chain: entry in the display list partition chain181 * @flags: display list flags, a combination of VSP1_DL_FRAME_END_*182 */183struct vsp1_dl_list {184 struct list_head list;185 struct vsp1_dl_manager *dlm;186 187 struct vsp1_dl_header *header;188 struct vsp1_dl_ext_header *extension;189 dma_addr_t dma;190 191 struct vsp1_dl_body *body0;192 struct list_head bodies;193 194 struct vsp1_dl_ext_cmd *pre_cmd;195 struct vsp1_dl_ext_cmd *post_cmd;196 197 bool has_chain;198 struct list_head chain;199 200 unsigned int flags;201};202 203/**204 * struct vsp1_dl_manager - Display List manager205 * @index: index of the related WPF206 * @singleshot: execute the display list in single-shot mode207 * @vsp1: the VSP1 device208 * @lock: protects the free, active, queued, and pending lists209 * @free: array of all free display lists210 * @active: list currently being processed (loaded) by hardware211 * @queued: list queued to the hardware (written to the DL registers)212 * @pending: list waiting to be queued to the hardware213 * @pool: body pool for the display list bodies214 * @cmdpool: commands pool for extended display list215 */216struct vsp1_dl_manager {217 unsigned int index;218 bool singleshot;219 struct vsp1_device *vsp1;220 221 spinlock_t lock;222 struct list_head free;223 struct vsp1_dl_list *active;224 struct vsp1_dl_list *queued;225 struct vsp1_dl_list *pending;226 227 struct vsp1_dl_body_pool *pool;228 struct vsp1_dl_cmd_pool *cmdpool;229};230 231/* -----------------------------------------------------------------------------232 * Display List Body Management233 */234 235/**236 * vsp1_dl_body_pool_create - Create a pool of bodies from a single allocation237 * @vsp1: The VSP1 device238 * @num_bodies: The number of bodies to allocate239 * @num_entries: The maximum number of entries that a body can contain240 * @extra_size: Extra allocation provided for the bodies241 *242 * Allocate a pool of display list bodies each with enough memory to contain the243 * requested number of entries plus the @extra_size.244 *245 * Return a pointer to a pool on success or NULL if memory can't be allocated.246 */247struct vsp1_dl_body_pool *248vsp1_dl_body_pool_create(struct vsp1_device *vsp1, unsigned int num_bodies,249 unsigned int num_entries, size_t extra_size)250{251 struct vsp1_dl_body_pool *pool;252 size_t dlb_size;253 unsigned int i;254 255 pool = kzalloc(sizeof(*pool), GFP_KERNEL);256 if (!pool)257 return NULL;258 259 pool->vsp1 = vsp1;260 261 /*262 * TODO: 'extra_size' is only used by vsp1_dlm_create(), to allocate263 * extra memory for the display list header. We need only one header per264 * display list, not per display list body, thus this allocation is265 * extraneous and should be reworked in the future.266 */267 dlb_size = num_entries * sizeof(struct vsp1_dl_entry) + extra_size;268 pool->size = dlb_size * num_bodies;269 270 pool->bodies = kcalloc(num_bodies, sizeof(*pool->bodies), GFP_KERNEL);271 if (!pool->bodies) {272 kfree(pool);273 return NULL;274 }275 276 pool->mem = dma_alloc_wc(vsp1->bus_master, pool->size, &pool->dma,277 GFP_KERNEL);278 if (!pool->mem) {279 kfree(pool->bodies);280 kfree(pool);281 return NULL;282 }283 284 spin_lock_init(&pool->lock);285 INIT_LIST_HEAD(&pool->free);286 287 for (i = 0; i < num_bodies; ++i) {288 struct vsp1_dl_body *dlb = &pool->bodies[i];289 290 dlb->pool = pool;291 dlb->max_entries = num_entries;292 293 dlb->dma = pool->dma + i * dlb_size;294 dlb->entries = pool->mem + i * dlb_size;295 296 list_add_tail(&dlb->free, &pool->free);297 }298 299 return pool;300}301 302/**303 * vsp1_dl_body_pool_destroy - Release a body pool304 * @pool: The body pool305 *306 * Release all components of a pool allocation.307 */308void vsp1_dl_body_pool_destroy(struct vsp1_dl_body_pool *pool)309{310 if (!pool)311 return;312 313 if (pool->mem)314 dma_free_wc(pool->vsp1->bus_master, pool->size, pool->mem,315 pool->dma);316 317 kfree(pool->bodies);318 kfree(pool);319}320 321/**322 * vsp1_dl_body_get - Obtain a body from a pool323 * @pool: The body pool324 *325 * Obtain a body from the pool without blocking.326 *327 * Returns a display list body or NULL if there are none available.328 */329struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool)330{331 struct vsp1_dl_body *dlb = NULL;332 unsigned long flags;333 334 spin_lock_irqsave(&pool->lock, flags);335 336 if (!list_empty(&pool->free)) {337 dlb = list_first_entry(&pool->free, struct vsp1_dl_body, free);338 list_del(&dlb->free);339 refcount_set(&dlb->refcnt, 1);340 }341 342 spin_unlock_irqrestore(&pool->lock, flags);343 344 return dlb;345}346 347/**348 * vsp1_dl_body_put - Return a body back to its pool349 * @dlb: The display list body350 *351 * Return a body back to the pool, and reset the num_entries to clear the list.352 */353void vsp1_dl_body_put(struct vsp1_dl_body *dlb)354{355 unsigned long flags;356 357 if (!dlb)358 return;359 360 if (!refcount_dec_and_test(&dlb->refcnt))361 return;362 363 dlb->num_entries = 0;364 365 spin_lock_irqsave(&dlb->pool->lock, flags);366 list_add_tail(&dlb->free, &dlb->pool->free);367 spin_unlock_irqrestore(&dlb->pool->lock, flags);368}369 370/**371 * vsp1_dl_body_write - Write a register to a display list body372 * @dlb: The body373 * @reg: The register address374 * @data: The register value375 *376 * Write the given register and value to the display list body. The maximum377 * number of entries that can be written in a body is specified when the body is378 * allocated by vsp1_dl_body_alloc().379 */380void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data)381{382 if (WARN_ONCE(dlb->num_entries >= dlb->max_entries,383 "DLB size exceeded (max %u)", dlb->max_entries))384 return;385 386 dlb->entries[dlb->num_entries].addr = reg;387 dlb->entries[dlb->num_entries].data = data;388 dlb->num_entries++;389}390 391/* -----------------------------------------------------------------------------392 * Display List Extended Command Management393 */394 395enum vsp1_extcmd_type {396 VSP1_EXTCMD_AUTODISP,397 VSP1_EXTCMD_AUTOFLD,398};399 400struct vsp1_extended_command_info {401 u16 opcode;402 size_t body_size;403};404 405static const struct vsp1_extended_command_info vsp1_extended_commands[] = {406 [VSP1_EXTCMD_AUTODISP] = { 0x02, 96 },407 [VSP1_EXTCMD_AUTOFLD] = { 0x03, 160 },408};409 410/**411 * vsp1_dl_cmd_pool_create - Create a pool of commands from a single allocation412 * @vsp1: The VSP1 device413 * @type: The command pool type414 * @num_cmds: The number of commands to allocate415 *416 * Allocate a pool of commands each with enough memory to contain the private417 * data of each command. The allocation sizes are dependent upon the command418 * type.419 *420 * Return a pointer to the pool on success or NULL if memory can't be allocated.421 */422static struct vsp1_dl_cmd_pool *423vsp1_dl_cmd_pool_create(struct vsp1_device *vsp1, enum vsp1_extcmd_type type,424 unsigned int num_cmds)425{426 struct vsp1_dl_cmd_pool *pool;427 unsigned int i;428 size_t cmd_size;429 430 pool = kzalloc(sizeof(*pool), GFP_KERNEL);431 if (!pool)432 return NULL;433 434 pool->vsp1 = vsp1;435 436 spin_lock_init(&pool->lock);437 INIT_LIST_HEAD(&pool->free);438 439 pool->cmds = kcalloc(num_cmds, sizeof(*pool->cmds), GFP_KERNEL);440 if (!pool->cmds) {441 kfree(pool);442 return NULL;443 }444 445 cmd_size = sizeof(struct vsp1_pre_ext_dl_body) +446 vsp1_extended_commands[type].body_size;447 cmd_size = ALIGN(cmd_size, 16);448 449 pool->size = cmd_size * num_cmds;450 pool->mem = dma_alloc_wc(vsp1->bus_master, pool->size, &pool->dma,451 GFP_KERNEL);452 if (!pool->mem) {453 kfree(pool->cmds);454 kfree(pool);455 return NULL;456 }457 458 for (i = 0; i < num_cmds; ++i) {459 struct vsp1_dl_ext_cmd *cmd = &pool->cmds[i];460 size_t cmd_offset = i * cmd_size;461 /* data_offset must be 16 byte aligned for DMA. */462 size_t data_offset = sizeof(struct vsp1_pre_ext_dl_body) +463 cmd_offset;464 465 cmd->pool = pool;466 cmd->opcode = vsp1_extended_commands[type].opcode;467 468 /*469 * TODO: Auto-disp can utilise more than one extended body470 * command per cmd.471 */472 cmd->num_cmds = 1;473 cmd->cmds = pool->mem + cmd_offset;474 cmd->cmd_dma = pool->dma + cmd_offset;475 476 cmd->data = pool->mem + data_offset;477 cmd->data_dma = pool->dma + data_offset;478 479 list_add_tail(&cmd->free, &pool->free);480 }481 482 return pool;483}484 485static486struct vsp1_dl_ext_cmd *vsp1_dl_ext_cmd_get(struct vsp1_dl_cmd_pool *pool)487{488 struct vsp1_dl_ext_cmd *cmd = NULL;489 unsigned long flags;490 491 spin_lock_irqsave(&pool->lock, flags);492 493 if (!list_empty(&pool->free)) {494 cmd = list_first_entry(&pool->free, struct vsp1_dl_ext_cmd,495 free);496 list_del(&cmd->free);497 }498 499 spin_unlock_irqrestore(&pool->lock, flags);500 501 return cmd;502}503 504static void vsp1_dl_ext_cmd_put(struct vsp1_dl_ext_cmd *cmd)505{506 unsigned long flags;507 508 if (!cmd)509 return;510 511 /* Reset flags, these mark data usage. */512 cmd->flags = 0;513 514 spin_lock_irqsave(&cmd->pool->lock, flags);515 list_add_tail(&cmd->free, &cmd->pool->free);516 spin_unlock_irqrestore(&cmd->pool->lock, flags);517}518 519static void vsp1_dl_ext_cmd_pool_destroy(struct vsp1_dl_cmd_pool *pool)520{521 if (!pool)522 return;523 524 if (pool->mem)525 dma_free_wc(pool->vsp1->bus_master, pool->size, pool->mem,526 pool->dma);527 528 kfree(pool->cmds);529 kfree(pool);530}531 532struct vsp1_dl_ext_cmd *vsp1_dl_get_pre_cmd(struct vsp1_dl_list *dl)533{534 struct vsp1_dl_manager *dlm = dl->dlm;535 536 if (dl->pre_cmd)537 return dl->pre_cmd;538 539 dl->pre_cmd = vsp1_dl_ext_cmd_get(dlm->cmdpool);540 541 return dl->pre_cmd;542}543 544/* ----------------------------------------------------------------------------545 * Display List Transaction Management546 */547 548static struct vsp1_dl_list *vsp1_dl_list_alloc(struct vsp1_dl_manager *dlm)549{550 struct vsp1_dl_list *dl;551 size_t header_offset;552 553 dl = kzalloc(sizeof(*dl), GFP_KERNEL);554 if (!dl)555 return NULL;556 557 INIT_LIST_HEAD(&dl->bodies);558 dl->dlm = dlm;559 560 /* Get a default body for our list. */561 dl->body0 = vsp1_dl_body_get(dlm->pool);562 if (!dl->body0) {563 kfree(dl);564 return NULL;565 }566 567 header_offset = dl->body0->max_entries * sizeof(*dl->body0->entries);568 569 dl->header = ((void *)dl->body0->entries) + header_offset;570 dl->dma = dl->body0->dma + header_offset;571 572 memset(dl->header, 0, sizeof(*dl->header));573 dl->header->lists[0].addr = dl->body0->dma;574 575 return dl;576}577 578static void vsp1_dl_list_bodies_put(struct vsp1_dl_list *dl)579{580 struct vsp1_dl_body *dlb, *tmp;581 582 list_for_each_entry_safe(dlb, tmp, &dl->bodies, list) {583 list_del(&dlb->list);584 vsp1_dl_body_put(dlb);585 }586}587 588static void vsp1_dl_list_free(struct vsp1_dl_list *dl)589{590 vsp1_dl_body_put(dl->body0);591 vsp1_dl_list_bodies_put(dl);592 593 kfree(dl);594}595 596/**597 * vsp1_dl_list_get - Get a free display list598 * @dlm: The display list manager599 *600 * Get a display list from the pool of free lists and return it.601 *602 * This function must be called without the display list manager lock held.603 */604struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm)605{606 struct vsp1_dl_list *dl = NULL;607 unsigned long flags;608 609 spin_lock_irqsave(&dlm->lock, flags);610 611 if (!list_empty(&dlm->free)) {612 dl = list_first_entry(&dlm->free, struct vsp1_dl_list, list);613 list_del(&dl->list);614 615 /*616 * The display list chain must be initialised to ensure every617 * display list can assert list_empty() if it is not in a chain.618 */619 INIT_LIST_HEAD(&dl->chain);620 }621 622 spin_unlock_irqrestore(&dlm->lock, flags);623 624 return dl;625}626 627/* This function must be called with the display list manager lock held.*/628static void __vsp1_dl_list_put(struct vsp1_dl_list *dl)629{630 struct vsp1_dl_list *dl_next;631 632 if (!dl)633 return;634 635 /*636 * Release any linked display-lists which were chained for a single637 * hardware operation.638 */639 if (dl->has_chain) {640 list_for_each_entry(dl_next, &dl->chain, chain)641 __vsp1_dl_list_put(dl_next);642 }643 644 dl->has_chain = false;645 646 vsp1_dl_list_bodies_put(dl);647 648 vsp1_dl_ext_cmd_put(dl->pre_cmd);649 vsp1_dl_ext_cmd_put(dl->post_cmd);650 651 dl->pre_cmd = NULL;652 dl->post_cmd = NULL;653 654 /*655 * body0 is reused as as an optimisation as presently every display list656 * has at least one body, thus we reinitialise the entries list.657 */658 dl->body0->num_entries = 0;659 660 list_add_tail(&dl->list, &dl->dlm->free);661}662 663/**664 * vsp1_dl_list_put - Release a display list665 * @dl: The display list666 *667 * Release the display list and return it to the pool of free lists.668 *669 * Passing a NULL pointer to this function is safe, in that case no operation670 * will be performed.671 */672void vsp1_dl_list_put(struct vsp1_dl_list *dl)673{674 unsigned long flags;675 676 if (!dl)677 return;678 679 spin_lock_irqsave(&dl->dlm->lock, flags);680 __vsp1_dl_list_put(dl);681 spin_unlock_irqrestore(&dl->dlm->lock, flags);682}683 684/**685 * vsp1_dl_list_get_body0 - Obtain the default body for the display list686 * @dl: The display list687 *688 * Obtain a pointer to the internal display list body allowing this to be passed689 * directly to configure operations.690 */691struct vsp1_dl_body *vsp1_dl_list_get_body0(struct vsp1_dl_list *dl)692{693 return dl->body0;694}695 696/**697 * vsp1_dl_list_add_body - Add a body to the display list698 * @dl: The display list699 * @dlb: The body700 *701 * Add a display list body to a display list. Registers contained in bodies are702 * processed after registers contained in the main display list, in the order in703 * which bodies are added.704 *705 * Adding a body to a display list passes ownership of the body to the list. The706 * caller retains its reference to the body when adding it to the display list,707 * but is not allowed to add new entries to the body.708 *709 * The reference must be explicitly released by a call to vsp1_dl_body_put()710 * when the body isn't needed anymore.711 */712int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb)713{714 refcount_inc(&dlb->refcnt);715 716 list_add_tail(&dlb->list, &dl->bodies);717 718 return 0;719}720 721/**722 * vsp1_dl_list_add_chain - Add a display list to a chain723 * @head: The head display list724 * @dl: The new display list725 *726 * Add a display list to an existing display list chain. The chained lists727 * will be automatically processed by the hardware without intervention from728 * the CPU. A display list end interrupt will only complete after the last729 * display list in the chain has completed processing.730 *731 * Adding a display list to a chain passes ownership of the display list to732 * the head display list item. The chain is released when the head dl item is733 * put back with __vsp1_dl_list_put().734 */735int vsp1_dl_list_add_chain(struct vsp1_dl_list *head,736 struct vsp1_dl_list *dl)737{738 head->has_chain = true;739 list_add_tail(&dl->chain, &head->chain);740 return 0;741}742 743static void vsp1_dl_ext_cmd_fill_header(struct vsp1_dl_ext_cmd *cmd)744{745 cmd->cmds[0].opcode = cmd->opcode;746 cmd->cmds[0].flags = cmd->flags;747 cmd->cmds[0].address_set = cmd->data_dma;748 cmd->cmds[0].reserved = 0;749}750 751static void vsp1_dl_list_fill_header(struct vsp1_dl_list *dl, bool is_last)752{753 struct vsp1_dl_manager *dlm = dl->dlm;754 struct vsp1_dl_header_list *hdr = dl->header->lists;755 struct vsp1_dl_body *dlb;756 unsigned int num_lists = 0;757 758 /*759 * Fill the header with the display list bodies addresses and sizes. The760 * address of the first body has already been filled when the display761 * list was allocated.762 */763 764 hdr->num_bytes = dl->body0->num_entries765 * sizeof(*dl->header->lists);766 767 list_for_each_entry(dlb, &dl->bodies, list) {768 num_lists++;769 hdr++;770 771 hdr->addr = dlb->dma;772 hdr->num_bytes = dlb->num_entries773 * sizeof(*dl->header->lists);774 }775 776 dl->header->num_lists = num_lists;777 dl->header->flags = 0;778 779 /*780 * Enable the interrupt for the end of each frame. In continuous mode781 * chained lists are used with one list per frame, so enable the782 * interrupt for each list. In singleshot mode chained lists are used783 * to partition a single frame, so enable the interrupt for the last784 * list only.785 */786 if (!dlm->singleshot || is_last)787 dl->header->flags |= VSP1_DLH_INT_ENABLE;788 789 /*790 * In continuous mode enable auto-start for all lists, as the VSP must791 * loop on the same list until a new one is queued. In singleshot mode792 * enable auto-start for all lists but the last to chain processing of793 * partitions without software intervention.794 */795 if (!dlm->singleshot || !is_last)796 dl->header->flags |= VSP1_DLH_AUTO_START;797 798 if (!is_last) {799 /*800 * If this is not the last display list in the chain, queue the801 * next item for automatic processing by the hardware.802 */803 struct vsp1_dl_list *next = list_next_entry(dl, chain);804 805 dl->header->next_header = next->dma;806 } else if (!dlm->singleshot) {807 /*808 * if the display list manager works in continuous mode, the VSP809 * should loop over the display list continuously until810 * instructed to do otherwise.811 */812 dl->header->next_header = dl->dma;813 }814 815 if (!dl->extension)816 return;817 818 dl->extension->flags = 0;819 820 if (dl->pre_cmd) {821 dl->extension->pre_ext_dl_plist = dl->pre_cmd->cmd_dma;822 dl->extension->pre_ext_dl_num_cmd = dl->pre_cmd->num_cmds;823 dl->extension->flags |= VSP1_DLH_EXT_PRE_CMD_EXEC;824 825 vsp1_dl_ext_cmd_fill_header(dl->pre_cmd);826 }827 828 if (dl->post_cmd) {829 dl->extension->post_ext_dl_plist = dl->post_cmd->cmd_dma;830 dl->extension->post_ext_dl_num_cmd = dl->post_cmd->num_cmds;831 dl->extension->flags |= VSP1_DLH_EXT_POST_CMD_EXEC;832 833 vsp1_dl_ext_cmd_fill_header(dl->post_cmd);834 }835}836 837static bool vsp1_dl_list_hw_update_pending(struct vsp1_dl_manager *dlm)838{839 struct vsp1_device *vsp1 = dlm->vsp1;840 841 if (!dlm->queued)842 return false;843 844 /*845 * Check whether the VSP1 has taken the update. The hardware indicates846 * this by clearing the UPDHDR bit in the CMD register.847 */848 return !!(vsp1_read(vsp1, VI6_CMD(dlm->index)) & VI6_CMD_UPDHDR);849}850 851static void vsp1_dl_list_hw_enqueue(struct vsp1_dl_list *dl)852{853 struct vsp1_dl_manager *dlm = dl->dlm;854 struct vsp1_device *vsp1 = dlm->vsp1;855 856 /*857 * Program the display list header address. If the hardware is idle858 * (single-shot mode or first frame in continuous mode) it will then be859 * started independently. If the hardware is operating, the860 * VI6_DL_HDR_REF_ADDR register will be updated with the display list861 * address.862 */863 vsp1_write(vsp1, VI6_DL_HDR_ADDR(dlm->index), dl->dma);864}865 866static void vsp1_dl_list_commit_continuous(struct vsp1_dl_list *dl)867{868 struct vsp1_dl_manager *dlm = dl->dlm;869 870 /*871 * If a previous display list has been queued to the hardware but not872 * processed yet, the VSP can start processing it at any time. In that873 * case we can't replace the queued list by the new one, as we could874 * race with the hardware. We thus mark the update as pending, it will875 * be queued up to the hardware by the frame end interrupt handler.876 *877 * If a display list is already pending we simply drop it as the new878 * display list is assumed to contain a more recent configuration. It is879 * an error if the already pending list has the880 * VSP1_DL_FRAME_END_INTERNAL flag set, as there is then a process881 * waiting for that list to complete. This shouldn't happen as the882 * waiting process should perform proper locking, but warn just in883 * case.884 */885 if (vsp1_dl_list_hw_update_pending(dlm)) {886 WARN_ON(dlm->pending &&887 (dlm->pending->flags & VSP1_DL_FRAME_END_INTERNAL));888 __vsp1_dl_list_put(dlm->pending);889 dlm->pending = dl;890 return;891 }892 893 /*894 * Pass the new display list to the hardware and mark it as queued. It895 * will become active when the hardware starts processing it.896 */897 vsp1_dl_list_hw_enqueue(dl);898 899 __vsp1_dl_list_put(dlm->queued);900 dlm->queued = dl;901}902 903static void vsp1_dl_list_commit_singleshot(struct vsp1_dl_list *dl)904{905 struct vsp1_dl_manager *dlm = dl->dlm;906 907 /*908 * When working in single-shot mode, the caller guarantees that the909 * hardware is idle at this point. Just commit the head display list910 * to hardware. Chained lists will be started automatically.911 */912 vsp1_dl_list_hw_enqueue(dl);913 914 dlm->active = dl;915}916 917void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags)918{919 struct vsp1_dl_manager *dlm = dl->dlm;920 struct vsp1_dl_list *dl_next;921 unsigned long flags;922 923 /* Fill the header for the head and chained display lists. */924 vsp1_dl_list_fill_header(dl, list_empty(&dl->chain));925 926 list_for_each_entry(dl_next, &dl->chain, chain) {927 bool last = list_is_last(&dl_next->chain, &dl->chain);928 929 vsp1_dl_list_fill_header(dl_next, last);930 }931 932 dl->flags = dl_flags & ~VSP1_DL_FRAME_END_COMPLETED;933 934 spin_lock_irqsave(&dlm->lock, flags);935 936 if (dlm->singleshot)937 vsp1_dl_list_commit_singleshot(dl);938 else939 vsp1_dl_list_commit_continuous(dl);940 941 spin_unlock_irqrestore(&dlm->lock, flags);942}943 944/* -----------------------------------------------------------------------------945 * Display List Manager946 */947 948/**949 * vsp1_dlm_irq_frame_end - Display list handler for the frame end interrupt950 * @dlm: the display list manager951 *952 * Return a set of flags that indicates display list completion status.953 *954 * The VSP1_DL_FRAME_END_COMPLETED flag indicates that the previous display list955 * has completed at frame end. If the flag is not returned display list956 * completion has been delayed by one frame because the display list commit957 * raced with the frame end interrupt. The function always returns with the flag958 * set in single-shot mode as display list processing is then not continuous and959 * races never occur.960 *961 * The following flags are only supported for continuous mode.962 *963 * The VSP1_DL_FRAME_END_INTERNAL flag indicates that the display list that just964 * became active had been queued with the internal notification flag.965 *966 * The VSP1_DL_FRAME_END_WRITEBACK flag indicates that the previously active967 * display list had been queued with the writeback flag.968 */969unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)970{971 struct vsp1_device *vsp1 = dlm->vsp1;972 u32 status = vsp1_read(vsp1, VI6_STATUS);973 unsigned int flags = 0;974 975 spin_lock(&dlm->lock);976 977 /*978 * The mem-to-mem pipelines work in single-shot mode. No new display979 * list can be queued, we don't have to do anything.980 */981 if (dlm->singleshot) {982 __vsp1_dl_list_put(dlm->active);983 dlm->active = NULL;984 flags |= VSP1_DL_FRAME_END_COMPLETED;985 goto done;986 }987 988 /*989 * If the commit operation raced with the interrupt and occurred after990 * the frame end event but before interrupt processing, the hardware991 * hasn't taken the update into account yet. We have to skip one frame992 * and retry.993 */994 if (vsp1_dl_list_hw_update_pending(dlm))995 goto done;996 997 /*998 * Progressive streams report only TOP fields. If we have a BOTTOM999 * field, we are interlaced, and expect the frame to complete on the1000 * next frame end interrupt.1001 */1002 if (status & VI6_STATUS_FLD_STD(dlm->index))1003 goto done;1004 1005 /*1006 * If the active display list has the writeback flag set, the frame1007 * completion marks the end of the writeback capture. Return the1008 * VSP1_DL_FRAME_END_WRITEBACK flag and reset the display list's1009 * writeback flag.1010 */1011 if (dlm->active && (dlm->active->flags & VSP1_DL_FRAME_END_WRITEBACK)) {1012 flags |= VSP1_DL_FRAME_END_WRITEBACK;1013 dlm->active->flags &= ~VSP1_DL_FRAME_END_WRITEBACK;1014 }1015 1016 /*1017 * The device starts processing the queued display list right after the1018 * frame end interrupt. The display list thus becomes active.1019 */1020 if (dlm->queued) {1021 if (dlm->queued->flags & VSP1_DL_FRAME_END_INTERNAL)1022 flags |= VSP1_DL_FRAME_END_INTERNAL;1023 dlm->queued->flags &= ~VSP1_DL_FRAME_END_INTERNAL;1024 1025 __vsp1_dl_list_put(dlm->active);1026 dlm->active = dlm->queued;1027 dlm->queued = NULL;1028 flags |= VSP1_DL_FRAME_END_COMPLETED;1029 }1030 1031 /*1032 * Now that the VSP has started processing the queued display list, we1033 * can queue the pending display list to the hardware if one has been1034 * prepared.1035 */1036 if (dlm->pending) {1037 vsp1_dl_list_hw_enqueue(dlm->pending);1038 dlm->queued = dlm->pending;1039 dlm->pending = NULL;1040 }1041 1042done:1043 spin_unlock(&dlm->lock);1044 1045 return flags;1046}1047 1048/* Hardware Setup */1049void vsp1_dlm_setup(struct vsp1_device *vsp1)1050{1051 unsigned int i;1052 u32 ctrl = (256 << VI6_DL_CTRL_AR_WAIT_SHIFT)1053 | VI6_DL_CTRL_DC2 | VI6_DL_CTRL_DC1 | VI6_DL_CTRL_DC01054 | VI6_DL_CTRL_DLE;1055 u32 ext_dl = (0x02 << VI6_DL_EXT_CTRL_POLINT_SHIFT)1056 | VI6_DL_EXT_CTRL_DLPRI | VI6_DL_EXT_CTRL_EXT;1057 1058 if (vsp1_feature(vsp1, VSP1_HAS_EXT_DL)) {1059 for (i = 0; i < vsp1->info->wpf_count; ++i)1060 vsp1_write(vsp1, VI6_DL_EXT_CTRL(i), ext_dl);1061 }1062 1063 vsp1_write(vsp1, VI6_DL_CTRL, ctrl);1064 vsp1_write(vsp1, VI6_DL_SWAP, VI6_DL_SWAP_LWS);1065}1066 1067void vsp1_dlm_reset(struct vsp1_dl_manager *dlm)1068{1069 unsigned long flags;1070 1071 spin_lock_irqsave(&dlm->lock, flags);1072 1073 __vsp1_dl_list_put(dlm->active);1074 __vsp1_dl_list_put(dlm->queued);1075 __vsp1_dl_list_put(dlm->pending);1076 1077 spin_unlock_irqrestore(&dlm->lock, flags);1078 1079 dlm->active = NULL;1080 dlm->queued = NULL;1081 dlm->pending = NULL;1082}1083 1084struct vsp1_dl_body *vsp1_dlm_dl_body_get(struct vsp1_dl_manager *dlm)1085{1086 return vsp1_dl_body_get(dlm->pool);1087}1088 1089struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,1090 unsigned int index,1091 unsigned int prealloc)1092{1093 struct vsp1_dl_manager *dlm;1094 size_t header_size;1095 unsigned int i;1096 1097 dlm = devm_kzalloc(vsp1->dev, sizeof(*dlm), GFP_KERNEL);1098 if (!dlm)1099 return NULL;1100 1101 dlm->index = index;1102 dlm->singleshot = vsp1->info->uapi;1103 dlm->vsp1 = vsp1;1104 1105 spin_lock_init(&dlm->lock);1106 INIT_LIST_HEAD(&dlm->free);1107 1108 /*1109 * Initialize the display list body and allocate DMA memory for the body1110 * and the header. Both are allocated together to avoid memory1111 * fragmentation, with the header located right after the body in1112 * memory. An extra body is allocated on top of the prealloc to account1113 * for the cached body used by the vsp1_pipeline object.1114 */1115 header_size = vsp1_feature(vsp1, VSP1_HAS_EXT_DL) ?1116 sizeof(struct vsp1_dl_header_extended) :1117 sizeof(struct vsp1_dl_header);1118 1119 header_size = ALIGN(header_size, 8);1120 1121 dlm->pool = vsp1_dl_body_pool_create(vsp1, prealloc + 1,1122 VSP1_DL_NUM_ENTRIES, header_size);1123 if (!dlm->pool)1124 return NULL;1125 1126 for (i = 0; i < prealloc; ++i) {1127 struct vsp1_dl_list *dl;1128 1129 dl = vsp1_dl_list_alloc(dlm);1130 if (!dl) {1131 vsp1_dlm_destroy(dlm);1132 return NULL;1133 }1134 1135 /* The extended header immediately follows the header. */1136 if (vsp1_feature(vsp1, VSP1_HAS_EXT_DL))1137 dl->extension = (void *)dl->header1138 + sizeof(*dl->header);1139 1140 list_add_tail(&dl->list, &dlm->free);1141 }1142 1143 if (vsp1_feature(vsp1, VSP1_HAS_EXT_DL)) {1144 dlm->cmdpool = vsp1_dl_cmd_pool_create(vsp1,1145 VSP1_EXTCMD_AUTOFLD, prealloc);1146 if (!dlm->cmdpool) {1147 vsp1_dlm_destroy(dlm);1148 return NULL;1149 }1150 }1151 1152 return dlm;1153}1154 1155void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm)1156{1157 struct vsp1_dl_list *dl, *next;1158 1159 if (!dlm)1160 return;1161 1162 list_for_each_entry_safe(dl, next, &dlm->free, list) {1163 list_del(&dl->list);1164 vsp1_dl_list_free(dl);1165 }1166 1167 vsp1_dl_body_pool_destroy(dlm->pool);1168 vsp1_dl_ext_cmd_pool_destroy(dlm->cmdpool);1169}1170