18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * vsp1_dl.c -- R-Car VSP1 Display List 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015 Renesas Corporation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/device.h> 118c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 128c2ecf20Sopenharmony_ci#include <linux/gfp.h> 138c2ecf20Sopenharmony_ci#include <linux/refcount.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "vsp1.h" 188c2ecf20Sopenharmony_ci#include "vsp1_dl.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define VSP1_DL_NUM_ENTRIES 256 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define VSP1_DLH_INT_ENABLE (1 << 1) 238c2ecf20Sopenharmony_ci#define VSP1_DLH_AUTO_START (1 << 0) 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define VSP1_DLH_EXT_PRE_CMD_EXEC (1 << 9) 268c2ecf20Sopenharmony_ci#define VSP1_DLH_EXT_POST_CMD_EXEC (1 << 8) 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistruct vsp1_dl_header_list { 298c2ecf20Sopenharmony_ci u32 num_bytes; 308c2ecf20Sopenharmony_ci u32 addr; 318c2ecf20Sopenharmony_ci} __packed; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistruct vsp1_dl_header { 348c2ecf20Sopenharmony_ci u32 num_lists; 358c2ecf20Sopenharmony_ci struct vsp1_dl_header_list lists[8]; 368c2ecf20Sopenharmony_ci u32 next_header; 378c2ecf20Sopenharmony_ci u32 flags; 388c2ecf20Sopenharmony_ci} __packed; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/** 418c2ecf20Sopenharmony_ci * struct vsp1_dl_ext_header - Extended display list header 428c2ecf20Sopenharmony_ci * @padding: padding zero bytes for alignment 438c2ecf20Sopenharmony_ci * @pre_ext_dl_num_cmd: number of pre-extended command bodies to parse 448c2ecf20Sopenharmony_ci * @flags: enables or disables execution of the pre and post command 458c2ecf20Sopenharmony_ci * @pre_ext_dl_plist: start address of pre-extended display list bodies 468c2ecf20Sopenharmony_ci * @post_ext_dl_num_cmd: number of post-extended command bodies to parse 478c2ecf20Sopenharmony_ci * @post_ext_dl_plist: start address of post-extended display list bodies 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_cistruct vsp1_dl_ext_header { 508c2ecf20Sopenharmony_ci u32 padding; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci /* 538c2ecf20Sopenharmony_ci * The datasheet represents flags as stored before pre_ext_dl_num_cmd, 548c2ecf20Sopenharmony_ci * expecting 32-bit accesses. The flags are appropriate to the whole 558c2ecf20Sopenharmony_ci * header, not just the pre_ext command, and thus warrant being 568c2ecf20Sopenharmony_ci * separated out. Due to byte ordering, and representing as 16 bit 578c2ecf20Sopenharmony_ci * values here, the flags must be positioned after the 588c2ecf20Sopenharmony_ci * pre_ext_dl_num_cmd. 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci u16 pre_ext_dl_num_cmd; 618c2ecf20Sopenharmony_ci u16 flags; 628c2ecf20Sopenharmony_ci u32 pre_ext_dl_plist; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci u32 post_ext_dl_num_cmd; 658c2ecf20Sopenharmony_ci u32 post_ext_dl_plist; 668c2ecf20Sopenharmony_ci} __packed; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistruct vsp1_dl_header_extended { 698c2ecf20Sopenharmony_ci struct vsp1_dl_header header; 708c2ecf20Sopenharmony_ci struct vsp1_dl_ext_header ext; 718c2ecf20Sopenharmony_ci} __packed; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistruct vsp1_dl_entry { 748c2ecf20Sopenharmony_ci u32 addr; 758c2ecf20Sopenharmony_ci u32 data; 768c2ecf20Sopenharmony_ci} __packed; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/** 798c2ecf20Sopenharmony_ci * struct vsp1_pre_ext_dl_body - Pre Extended Display List Body 808c2ecf20Sopenharmony_ci * @opcode: Extended display list command operation code 818c2ecf20Sopenharmony_ci * @flags: Pre-extended command flags. These are specific to each command 828c2ecf20Sopenharmony_ci * @address_set: Source address set pointer. Must have 16-byte alignment 838c2ecf20Sopenharmony_ci * @reserved: Zero bits for alignment. 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_cistruct vsp1_pre_ext_dl_body { 868c2ecf20Sopenharmony_ci u32 opcode; 878c2ecf20Sopenharmony_ci u32 flags; 888c2ecf20Sopenharmony_ci u32 address_set; 898c2ecf20Sopenharmony_ci u32 reserved; 908c2ecf20Sopenharmony_ci} __packed; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/** 938c2ecf20Sopenharmony_ci * struct vsp1_dl_body - Display list body 948c2ecf20Sopenharmony_ci * @list: entry in the display list list of bodies 958c2ecf20Sopenharmony_ci * @free: entry in the pool free body list 968c2ecf20Sopenharmony_ci * @refcnt: reference tracking for the body 978c2ecf20Sopenharmony_ci * @pool: pool to which this body belongs 988c2ecf20Sopenharmony_ci * @entries: array of entries 998c2ecf20Sopenharmony_ci * @dma: DMA address of the entries 1008c2ecf20Sopenharmony_ci * @size: size of the DMA memory in bytes 1018c2ecf20Sopenharmony_ci * @num_entries: number of stored entries 1028c2ecf20Sopenharmony_ci * @max_entries: number of entries available 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_cistruct vsp1_dl_body { 1058c2ecf20Sopenharmony_ci struct list_head list; 1068c2ecf20Sopenharmony_ci struct list_head free; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci refcount_t refcnt; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci struct vsp1_dl_body_pool *pool; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci struct vsp1_dl_entry *entries; 1138c2ecf20Sopenharmony_ci dma_addr_t dma; 1148c2ecf20Sopenharmony_ci size_t size; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci unsigned int num_entries; 1178c2ecf20Sopenharmony_ci unsigned int max_entries; 1188c2ecf20Sopenharmony_ci}; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/** 1218c2ecf20Sopenharmony_ci * struct vsp1_dl_body_pool - display list body pool 1228c2ecf20Sopenharmony_ci * @dma: DMA address of the entries 1238c2ecf20Sopenharmony_ci * @size: size of the full DMA memory pool in bytes 1248c2ecf20Sopenharmony_ci * @mem: CPU memory pointer for the pool 1258c2ecf20Sopenharmony_ci * @bodies: Array of DLB structures for the pool 1268c2ecf20Sopenharmony_ci * @free: List of free DLB entries 1278c2ecf20Sopenharmony_ci * @lock: Protects the free list 1288c2ecf20Sopenharmony_ci * @vsp1: the VSP1 device 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_cistruct vsp1_dl_body_pool { 1318c2ecf20Sopenharmony_ci /* DMA allocation */ 1328c2ecf20Sopenharmony_ci dma_addr_t dma; 1338c2ecf20Sopenharmony_ci size_t size; 1348c2ecf20Sopenharmony_ci void *mem; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* Body management */ 1378c2ecf20Sopenharmony_ci struct vsp1_dl_body *bodies; 1388c2ecf20Sopenharmony_ci struct list_head free; 1398c2ecf20Sopenharmony_ci spinlock_t lock; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci struct vsp1_device *vsp1; 1428c2ecf20Sopenharmony_ci}; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/** 1458c2ecf20Sopenharmony_ci * struct vsp1_cmd_pool - Display List commands pool 1468c2ecf20Sopenharmony_ci * @dma: DMA address of the entries 1478c2ecf20Sopenharmony_ci * @size: size of the full DMA memory pool in bytes 1488c2ecf20Sopenharmony_ci * @mem: CPU memory pointer for the pool 1498c2ecf20Sopenharmony_ci * @cmds: Array of command structures for the pool 1508c2ecf20Sopenharmony_ci * @free: Free pool entries 1518c2ecf20Sopenharmony_ci * @lock: Protects the free list 1528c2ecf20Sopenharmony_ci * @vsp1: the VSP1 device 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_cistruct vsp1_dl_cmd_pool { 1558c2ecf20Sopenharmony_ci /* DMA allocation */ 1568c2ecf20Sopenharmony_ci dma_addr_t dma; 1578c2ecf20Sopenharmony_ci size_t size; 1588c2ecf20Sopenharmony_ci void *mem; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci struct vsp1_dl_ext_cmd *cmds; 1618c2ecf20Sopenharmony_ci struct list_head free; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci spinlock_t lock; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci struct vsp1_device *vsp1; 1668c2ecf20Sopenharmony_ci}; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci/** 1698c2ecf20Sopenharmony_ci * struct vsp1_dl_list - Display list 1708c2ecf20Sopenharmony_ci * @list: entry in the display list manager lists 1718c2ecf20Sopenharmony_ci * @dlm: the display list manager 1728c2ecf20Sopenharmony_ci * @header: display list header 1738c2ecf20Sopenharmony_ci * @extension: extended display list header. NULL for normal lists 1748c2ecf20Sopenharmony_ci * @dma: DMA address for the header 1758c2ecf20Sopenharmony_ci * @body0: first display list body 1768c2ecf20Sopenharmony_ci * @bodies: list of extra display list bodies 1778c2ecf20Sopenharmony_ci * @pre_cmd: pre command to be issued through extended dl header 1788c2ecf20Sopenharmony_ci * @post_cmd: post command to be issued through extended dl header 1798c2ecf20Sopenharmony_ci * @has_chain: if true, indicates that there's a partition chain 1808c2ecf20Sopenharmony_ci * @chain: entry in the display list partition chain 1818c2ecf20Sopenharmony_ci * @flags: display list flags, a combination of VSP1_DL_FRAME_END_* 1828c2ecf20Sopenharmony_ci */ 1838c2ecf20Sopenharmony_cistruct vsp1_dl_list { 1848c2ecf20Sopenharmony_ci struct list_head list; 1858c2ecf20Sopenharmony_ci struct vsp1_dl_manager *dlm; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci struct vsp1_dl_header *header; 1888c2ecf20Sopenharmony_ci struct vsp1_dl_ext_header *extension; 1898c2ecf20Sopenharmony_ci dma_addr_t dma; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci struct vsp1_dl_body *body0; 1928c2ecf20Sopenharmony_ci struct list_head bodies; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci struct vsp1_dl_ext_cmd *pre_cmd; 1958c2ecf20Sopenharmony_ci struct vsp1_dl_ext_cmd *post_cmd; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci bool has_chain; 1988c2ecf20Sopenharmony_ci struct list_head chain; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci unsigned int flags; 2018c2ecf20Sopenharmony_ci}; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci/** 2048c2ecf20Sopenharmony_ci * struct vsp1_dl_manager - Display List manager 2058c2ecf20Sopenharmony_ci * @index: index of the related WPF 2068c2ecf20Sopenharmony_ci * @singleshot: execute the display list in single-shot mode 2078c2ecf20Sopenharmony_ci * @vsp1: the VSP1 device 2088c2ecf20Sopenharmony_ci * @lock: protects the free, active, queued, and pending lists 2098c2ecf20Sopenharmony_ci * @free: array of all free display lists 2108c2ecf20Sopenharmony_ci * @active: list currently being processed (loaded) by hardware 2118c2ecf20Sopenharmony_ci * @queued: list queued to the hardware (written to the DL registers) 2128c2ecf20Sopenharmony_ci * @pending: list waiting to be queued to the hardware 2138c2ecf20Sopenharmony_ci * @pool: body pool for the display list bodies 2148c2ecf20Sopenharmony_ci * @cmdpool: commands pool for extended display list 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_cistruct vsp1_dl_manager { 2178c2ecf20Sopenharmony_ci unsigned int index; 2188c2ecf20Sopenharmony_ci bool singleshot; 2198c2ecf20Sopenharmony_ci struct vsp1_device *vsp1; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci spinlock_t lock; 2228c2ecf20Sopenharmony_ci struct list_head free; 2238c2ecf20Sopenharmony_ci struct vsp1_dl_list *active; 2248c2ecf20Sopenharmony_ci struct vsp1_dl_list *queued; 2258c2ecf20Sopenharmony_ci struct vsp1_dl_list *pending; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci struct vsp1_dl_body_pool *pool; 2288c2ecf20Sopenharmony_ci struct vsp1_dl_cmd_pool *cmdpool; 2298c2ecf20Sopenharmony_ci}; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 2328c2ecf20Sopenharmony_ci * Display List Body Management 2338c2ecf20Sopenharmony_ci */ 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci/** 2368c2ecf20Sopenharmony_ci * vsp1_dl_body_pool_create - Create a pool of bodies from a single allocation 2378c2ecf20Sopenharmony_ci * @vsp1: The VSP1 device 2388c2ecf20Sopenharmony_ci * @num_bodies: The number of bodies to allocate 2398c2ecf20Sopenharmony_ci * @num_entries: The maximum number of entries that a body can contain 2408c2ecf20Sopenharmony_ci * @extra_size: Extra allocation provided for the bodies 2418c2ecf20Sopenharmony_ci * 2428c2ecf20Sopenharmony_ci * Allocate a pool of display list bodies each with enough memory to contain the 2438c2ecf20Sopenharmony_ci * requested number of entries plus the @extra_size. 2448c2ecf20Sopenharmony_ci * 2458c2ecf20Sopenharmony_ci * Return a pointer to a pool on success or NULL if memory can't be allocated. 2468c2ecf20Sopenharmony_ci */ 2478c2ecf20Sopenharmony_cistruct vsp1_dl_body_pool * 2488c2ecf20Sopenharmony_civsp1_dl_body_pool_create(struct vsp1_device *vsp1, unsigned int num_bodies, 2498c2ecf20Sopenharmony_ci unsigned int num_entries, size_t extra_size) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct vsp1_dl_body_pool *pool; 2528c2ecf20Sopenharmony_ci size_t dlb_size; 2538c2ecf20Sopenharmony_ci unsigned int i; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci pool = kzalloc(sizeof(*pool), GFP_KERNEL); 2568c2ecf20Sopenharmony_ci if (!pool) 2578c2ecf20Sopenharmony_ci return NULL; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci pool->vsp1 = vsp1; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* 2628c2ecf20Sopenharmony_ci * TODO: 'extra_size' is only used by vsp1_dlm_create(), to allocate 2638c2ecf20Sopenharmony_ci * extra memory for the display list header. We need only one header per 2648c2ecf20Sopenharmony_ci * display list, not per display list body, thus this allocation is 2658c2ecf20Sopenharmony_ci * extraneous and should be reworked in the future. 2668c2ecf20Sopenharmony_ci */ 2678c2ecf20Sopenharmony_ci dlb_size = num_entries * sizeof(struct vsp1_dl_entry) + extra_size; 2688c2ecf20Sopenharmony_ci pool->size = dlb_size * num_bodies; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci pool->bodies = kcalloc(num_bodies, sizeof(*pool->bodies), GFP_KERNEL); 2718c2ecf20Sopenharmony_ci if (!pool->bodies) { 2728c2ecf20Sopenharmony_ci kfree(pool); 2738c2ecf20Sopenharmony_ci return NULL; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci pool->mem = dma_alloc_wc(vsp1->bus_master, pool->size, &pool->dma, 2778c2ecf20Sopenharmony_ci GFP_KERNEL); 2788c2ecf20Sopenharmony_ci if (!pool->mem) { 2798c2ecf20Sopenharmony_ci kfree(pool->bodies); 2808c2ecf20Sopenharmony_ci kfree(pool); 2818c2ecf20Sopenharmony_ci return NULL; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci spin_lock_init(&pool->lock); 2858c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&pool->free); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci for (i = 0; i < num_bodies; ++i) { 2888c2ecf20Sopenharmony_ci struct vsp1_dl_body *dlb = &pool->bodies[i]; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci dlb->pool = pool; 2918c2ecf20Sopenharmony_ci dlb->max_entries = num_entries; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci dlb->dma = pool->dma + i * dlb_size; 2948c2ecf20Sopenharmony_ci dlb->entries = pool->mem + i * dlb_size; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci list_add_tail(&dlb->free, &pool->free); 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci return pool; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci/** 3038c2ecf20Sopenharmony_ci * vsp1_dl_body_pool_destroy - Release a body pool 3048c2ecf20Sopenharmony_ci * @pool: The body pool 3058c2ecf20Sopenharmony_ci * 3068c2ecf20Sopenharmony_ci * Release all components of a pool allocation. 3078c2ecf20Sopenharmony_ci */ 3088c2ecf20Sopenharmony_civoid vsp1_dl_body_pool_destroy(struct vsp1_dl_body_pool *pool) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci if (!pool) 3118c2ecf20Sopenharmony_ci return; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (pool->mem) 3148c2ecf20Sopenharmony_ci dma_free_wc(pool->vsp1->bus_master, pool->size, pool->mem, 3158c2ecf20Sopenharmony_ci pool->dma); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci kfree(pool->bodies); 3188c2ecf20Sopenharmony_ci kfree(pool); 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci/** 3228c2ecf20Sopenharmony_ci * vsp1_dl_body_get - Obtain a body from a pool 3238c2ecf20Sopenharmony_ci * @pool: The body pool 3248c2ecf20Sopenharmony_ci * 3258c2ecf20Sopenharmony_ci * Obtain a body from the pool without blocking. 3268c2ecf20Sopenharmony_ci * 3278c2ecf20Sopenharmony_ci * Returns a display list body or NULL if there are none available. 3288c2ecf20Sopenharmony_ci */ 3298c2ecf20Sopenharmony_cistruct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci struct vsp1_dl_body *dlb = NULL; 3328c2ecf20Sopenharmony_ci unsigned long flags; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci spin_lock_irqsave(&pool->lock, flags); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (!list_empty(&pool->free)) { 3378c2ecf20Sopenharmony_ci dlb = list_first_entry(&pool->free, struct vsp1_dl_body, free); 3388c2ecf20Sopenharmony_ci list_del(&dlb->free); 3398c2ecf20Sopenharmony_ci refcount_set(&dlb->refcnt, 1); 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pool->lock, flags); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci return dlb; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci/** 3488c2ecf20Sopenharmony_ci * vsp1_dl_body_put - Return a body back to its pool 3498c2ecf20Sopenharmony_ci * @dlb: The display list body 3508c2ecf20Sopenharmony_ci * 3518c2ecf20Sopenharmony_ci * Return a body back to the pool, and reset the num_entries to clear the list. 3528c2ecf20Sopenharmony_ci */ 3538c2ecf20Sopenharmony_civoid vsp1_dl_body_put(struct vsp1_dl_body *dlb) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci unsigned long flags; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (!dlb) 3588c2ecf20Sopenharmony_ci return; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (!refcount_dec_and_test(&dlb->refcnt)) 3618c2ecf20Sopenharmony_ci return; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci dlb->num_entries = 0; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci spin_lock_irqsave(&dlb->pool->lock, flags); 3668c2ecf20Sopenharmony_ci list_add_tail(&dlb->free, &dlb->pool->free); 3678c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dlb->pool->lock, flags); 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci/** 3718c2ecf20Sopenharmony_ci * vsp1_dl_body_write - Write a register to a display list body 3728c2ecf20Sopenharmony_ci * @dlb: The body 3738c2ecf20Sopenharmony_ci * @reg: The register address 3748c2ecf20Sopenharmony_ci * @data: The register value 3758c2ecf20Sopenharmony_ci * 3768c2ecf20Sopenharmony_ci * Write the given register and value to the display list body. The maximum 3778c2ecf20Sopenharmony_ci * number of entries that can be written in a body is specified when the body is 3788c2ecf20Sopenharmony_ci * allocated by vsp1_dl_body_alloc(). 3798c2ecf20Sopenharmony_ci */ 3808c2ecf20Sopenharmony_civoid vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci if (WARN_ONCE(dlb->num_entries >= dlb->max_entries, 3838c2ecf20Sopenharmony_ci "DLB size exceeded (max %u)", dlb->max_entries)) 3848c2ecf20Sopenharmony_ci return; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci dlb->entries[dlb->num_entries].addr = reg; 3878c2ecf20Sopenharmony_ci dlb->entries[dlb->num_entries].data = data; 3888c2ecf20Sopenharmony_ci dlb->num_entries++; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 3928c2ecf20Sopenharmony_ci * Display List Extended Command Management 3938c2ecf20Sopenharmony_ci */ 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cienum vsp1_extcmd_type { 3968c2ecf20Sopenharmony_ci VSP1_EXTCMD_AUTODISP, 3978c2ecf20Sopenharmony_ci VSP1_EXTCMD_AUTOFLD, 3988c2ecf20Sopenharmony_ci}; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistruct vsp1_extended_command_info { 4018c2ecf20Sopenharmony_ci u16 opcode; 4028c2ecf20Sopenharmony_ci size_t body_size; 4038c2ecf20Sopenharmony_ci}; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic const struct vsp1_extended_command_info vsp1_extended_commands[] = { 4068c2ecf20Sopenharmony_ci [VSP1_EXTCMD_AUTODISP] = { 0x02, 96 }, 4078c2ecf20Sopenharmony_ci [VSP1_EXTCMD_AUTOFLD] = { 0x03, 160 }, 4088c2ecf20Sopenharmony_ci}; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci/** 4118c2ecf20Sopenharmony_ci * vsp1_dl_cmd_pool_create - Create a pool of commands from a single allocation 4128c2ecf20Sopenharmony_ci * @vsp1: The VSP1 device 4138c2ecf20Sopenharmony_ci * @type: The command pool type 4148c2ecf20Sopenharmony_ci * @num_cmds: The number of commands to allocate 4158c2ecf20Sopenharmony_ci * 4168c2ecf20Sopenharmony_ci * Allocate a pool of commands each with enough memory to contain the private 4178c2ecf20Sopenharmony_ci * data of each command. The allocation sizes are dependent upon the command 4188c2ecf20Sopenharmony_ci * type. 4198c2ecf20Sopenharmony_ci * 4208c2ecf20Sopenharmony_ci * Return a pointer to the pool on success or NULL if memory can't be allocated. 4218c2ecf20Sopenharmony_ci */ 4228c2ecf20Sopenharmony_cistatic struct vsp1_dl_cmd_pool * 4238c2ecf20Sopenharmony_civsp1_dl_cmd_pool_create(struct vsp1_device *vsp1, enum vsp1_extcmd_type type, 4248c2ecf20Sopenharmony_ci unsigned int num_cmds) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci struct vsp1_dl_cmd_pool *pool; 4278c2ecf20Sopenharmony_ci unsigned int i; 4288c2ecf20Sopenharmony_ci size_t cmd_size; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci pool = kzalloc(sizeof(*pool), GFP_KERNEL); 4318c2ecf20Sopenharmony_ci if (!pool) 4328c2ecf20Sopenharmony_ci return NULL; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci pool->vsp1 = vsp1; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci spin_lock_init(&pool->lock); 4378c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&pool->free); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci pool->cmds = kcalloc(num_cmds, sizeof(*pool->cmds), GFP_KERNEL); 4408c2ecf20Sopenharmony_ci if (!pool->cmds) { 4418c2ecf20Sopenharmony_ci kfree(pool); 4428c2ecf20Sopenharmony_ci return NULL; 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci cmd_size = sizeof(struct vsp1_pre_ext_dl_body) + 4468c2ecf20Sopenharmony_ci vsp1_extended_commands[type].body_size; 4478c2ecf20Sopenharmony_ci cmd_size = ALIGN(cmd_size, 16); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci pool->size = cmd_size * num_cmds; 4508c2ecf20Sopenharmony_ci pool->mem = dma_alloc_wc(vsp1->bus_master, pool->size, &pool->dma, 4518c2ecf20Sopenharmony_ci GFP_KERNEL); 4528c2ecf20Sopenharmony_ci if (!pool->mem) { 4538c2ecf20Sopenharmony_ci kfree(pool->cmds); 4548c2ecf20Sopenharmony_ci kfree(pool); 4558c2ecf20Sopenharmony_ci return NULL; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci for (i = 0; i < num_cmds; ++i) { 4598c2ecf20Sopenharmony_ci struct vsp1_dl_ext_cmd *cmd = &pool->cmds[i]; 4608c2ecf20Sopenharmony_ci size_t cmd_offset = i * cmd_size; 4618c2ecf20Sopenharmony_ci /* data_offset must be 16 byte aligned for DMA. */ 4628c2ecf20Sopenharmony_ci size_t data_offset = sizeof(struct vsp1_pre_ext_dl_body) + 4638c2ecf20Sopenharmony_ci cmd_offset; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci cmd->pool = pool; 4668c2ecf20Sopenharmony_ci cmd->opcode = vsp1_extended_commands[type].opcode; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* 4698c2ecf20Sopenharmony_ci * TODO: Auto-disp can utilise more than one extended body 4708c2ecf20Sopenharmony_ci * command per cmd. 4718c2ecf20Sopenharmony_ci */ 4728c2ecf20Sopenharmony_ci cmd->num_cmds = 1; 4738c2ecf20Sopenharmony_ci cmd->cmds = pool->mem + cmd_offset; 4748c2ecf20Sopenharmony_ci cmd->cmd_dma = pool->dma + cmd_offset; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci cmd->data = pool->mem + data_offset; 4778c2ecf20Sopenharmony_ci cmd->data_dma = pool->dma + data_offset; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci list_add_tail(&cmd->free, &pool->free); 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci return pool; 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic 4868c2ecf20Sopenharmony_cistruct vsp1_dl_ext_cmd *vsp1_dl_ext_cmd_get(struct vsp1_dl_cmd_pool *pool) 4878c2ecf20Sopenharmony_ci{ 4888c2ecf20Sopenharmony_ci struct vsp1_dl_ext_cmd *cmd = NULL; 4898c2ecf20Sopenharmony_ci unsigned long flags; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci spin_lock_irqsave(&pool->lock, flags); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci if (!list_empty(&pool->free)) { 4948c2ecf20Sopenharmony_ci cmd = list_first_entry(&pool->free, struct vsp1_dl_ext_cmd, 4958c2ecf20Sopenharmony_ci free); 4968c2ecf20Sopenharmony_ci list_del(&cmd->free); 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pool->lock, flags); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci return cmd; 5028c2ecf20Sopenharmony_ci} 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cistatic void vsp1_dl_ext_cmd_put(struct vsp1_dl_ext_cmd *cmd) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci unsigned long flags; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (!cmd) 5098c2ecf20Sopenharmony_ci return; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci /* Reset flags, these mark data usage. */ 5128c2ecf20Sopenharmony_ci cmd->flags = 0; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci spin_lock_irqsave(&cmd->pool->lock, flags); 5158c2ecf20Sopenharmony_ci list_add_tail(&cmd->free, &cmd->pool->free); 5168c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cmd->pool->lock, flags); 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic void vsp1_dl_ext_cmd_pool_destroy(struct vsp1_dl_cmd_pool *pool) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci if (!pool) 5228c2ecf20Sopenharmony_ci return; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci if (pool->mem) 5258c2ecf20Sopenharmony_ci dma_free_wc(pool->vsp1->bus_master, pool->size, pool->mem, 5268c2ecf20Sopenharmony_ci pool->dma); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci kfree(pool->cmds); 5298c2ecf20Sopenharmony_ci kfree(pool); 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistruct vsp1_dl_ext_cmd *vsp1_dl_get_pre_cmd(struct vsp1_dl_list *dl) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci struct vsp1_dl_manager *dlm = dl->dlm; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (dl->pre_cmd) 5378c2ecf20Sopenharmony_ci return dl->pre_cmd; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci dl->pre_cmd = vsp1_dl_ext_cmd_get(dlm->cmdpool); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci return dl->pre_cmd; 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------------- 5458c2ecf20Sopenharmony_ci * Display List Transaction Management 5468c2ecf20Sopenharmony_ci */ 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_cistatic struct vsp1_dl_list *vsp1_dl_list_alloc(struct vsp1_dl_manager *dlm) 5498c2ecf20Sopenharmony_ci{ 5508c2ecf20Sopenharmony_ci struct vsp1_dl_list *dl; 5518c2ecf20Sopenharmony_ci size_t header_offset; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci dl = kzalloc(sizeof(*dl), GFP_KERNEL); 5548c2ecf20Sopenharmony_ci if (!dl) 5558c2ecf20Sopenharmony_ci return NULL; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dl->bodies); 5588c2ecf20Sopenharmony_ci dl->dlm = dlm; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci /* Get a default body for our list. */ 5618c2ecf20Sopenharmony_ci dl->body0 = vsp1_dl_body_get(dlm->pool); 5628c2ecf20Sopenharmony_ci if (!dl->body0) { 5638c2ecf20Sopenharmony_ci kfree(dl); 5648c2ecf20Sopenharmony_ci return NULL; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci header_offset = dl->body0->max_entries * sizeof(*dl->body0->entries); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci dl->header = ((void *)dl->body0->entries) + header_offset; 5708c2ecf20Sopenharmony_ci dl->dma = dl->body0->dma + header_offset; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci memset(dl->header, 0, sizeof(*dl->header)); 5738c2ecf20Sopenharmony_ci dl->header->lists[0].addr = dl->body0->dma; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci return dl; 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_cistatic void vsp1_dl_list_bodies_put(struct vsp1_dl_list *dl) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci struct vsp1_dl_body *dlb, *tmp; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci list_for_each_entry_safe(dlb, tmp, &dl->bodies, list) { 5838c2ecf20Sopenharmony_ci list_del(&dlb->list); 5848c2ecf20Sopenharmony_ci vsp1_dl_body_put(dlb); 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_cistatic void vsp1_dl_list_free(struct vsp1_dl_list *dl) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci vsp1_dl_body_put(dl->body0); 5918c2ecf20Sopenharmony_ci vsp1_dl_list_bodies_put(dl); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci kfree(dl); 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci/** 5978c2ecf20Sopenharmony_ci * vsp1_dl_list_get - Get a free display list 5988c2ecf20Sopenharmony_ci * @dlm: The display list manager 5998c2ecf20Sopenharmony_ci * 6008c2ecf20Sopenharmony_ci * Get a display list from the pool of free lists and return it. 6018c2ecf20Sopenharmony_ci * 6028c2ecf20Sopenharmony_ci * This function must be called without the display list manager lock held. 6038c2ecf20Sopenharmony_ci */ 6048c2ecf20Sopenharmony_cistruct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci struct vsp1_dl_list *dl = NULL; 6078c2ecf20Sopenharmony_ci unsigned long flags; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci spin_lock_irqsave(&dlm->lock, flags); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci if (!list_empty(&dlm->free)) { 6128c2ecf20Sopenharmony_ci dl = list_first_entry(&dlm->free, struct vsp1_dl_list, list); 6138c2ecf20Sopenharmony_ci list_del(&dl->list); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci /* 6168c2ecf20Sopenharmony_ci * The display list chain must be initialised to ensure every 6178c2ecf20Sopenharmony_ci * display list can assert list_empty() if it is not in a chain. 6188c2ecf20Sopenharmony_ci */ 6198c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dl->chain); 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dlm->lock, flags); 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci return dl; 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci/* This function must be called with the display list manager lock held.*/ 6288c2ecf20Sopenharmony_cistatic void __vsp1_dl_list_put(struct vsp1_dl_list *dl) 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci struct vsp1_dl_list *dl_next; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci if (!dl) 6338c2ecf20Sopenharmony_ci return; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci /* 6368c2ecf20Sopenharmony_ci * Release any linked display-lists which were chained for a single 6378c2ecf20Sopenharmony_ci * hardware operation. 6388c2ecf20Sopenharmony_ci */ 6398c2ecf20Sopenharmony_ci if (dl->has_chain) { 6408c2ecf20Sopenharmony_ci list_for_each_entry(dl_next, &dl->chain, chain) 6418c2ecf20Sopenharmony_ci __vsp1_dl_list_put(dl_next); 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci dl->has_chain = false; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci vsp1_dl_list_bodies_put(dl); 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci vsp1_dl_ext_cmd_put(dl->pre_cmd); 6498c2ecf20Sopenharmony_ci vsp1_dl_ext_cmd_put(dl->post_cmd); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci dl->pre_cmd = NULL; 6528c2ecf20Sopenharmony_ci dl->post_cmd = NULL; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci /* 6558c2ecf20Sopenharmony_ci * body0 is reused as as an optimisation as presently every display list 6568c2ecf20Sopenharmony_ci * has at least one body, thus we reinitialise the entries list. 6578c2ecf20Sopenharmony_ci */ 6588c2ecf20Sopenharmony_ci dl->body0->num_entries = 0; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci list_add_tail(&dl->list, &dl->dlm->free); 6618c2ecf20Sopenharmony_ci} 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci/** 6648c2ecf20Sopenharmony_ci * vsp1_dl_list_put - Release a display list 6658c2ecf20Sopenharmony_ci * @dl: The display list 6668c2ecf20Sopenharmony_ci * 6678c2ecf20Sopenharmony_ci * Release the display list and return it to the pool of free lists. 6688c2ecf20Sopenharmony_ci * 6698c2ecf20Sopenharmony_ci * Passing a NULL pointer to this function is safe, in that case no operation 6708c2ecf20Sopenharmony_ci * will be performed. 6718c2ecf20Sopenharmony_ci */ 6728c2ecf20Sopenharmony_civoid vsp1_dl_list_put(struct vsp1_dl_list *dl) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci unsigned long flags; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci if (!dl) 6778c2ecf20Sopenharmony_ci return; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci spin_lock_irqsave(&dl->dlm->lock, flags); 6808c2ecf20Sopenharmony_ci __vsp1_dl_list_put(dl); 6818c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dl->dlm->lock, flags); 6828c2ecf20Sopenharmony_ci} 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci/** 6858c2ecf20Sopenharmony_ci * vsp1_dl_list_get_body0 - Obtain the default body for the display list 6868c2ecf20Sopenharmony_ci * @dl: The display list 6878c2ecf20Sopenharmony_ci * 6888c2ecf20Sopenharmony_ci * Obtain a pointer to the internal display list body allowing this to be passed 6898c2ecf20Sopenharmony_ci * directly to configure operations. 6908c2ecf20Sopenharmony_ci */ 6918c2ecf20Sopenharmony_cistruct vsp1_dl_body *vsp1_dl_list_get_body0(struct vsp1_dl_list *dl) 6928c2ecf20Sopenharmony_ci{ 6938c2ecf20Sopenharmony_ci return dl->body0; 6948c2ecf20Sopenharmony_ci} 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci/** 6978c2ecf20Sopenharmony_ci * vsp1_dl_list_add_body - Add a body to the display list 6988c2ecf20Sopenharmony_ci * @dl: The display list 6998c2ecf20Sopenharmony_ci * @dlb: The body 7008c2ecf20Sopenharmony_ci * 7018c2ecf20Sopenharmony_ci * Add a display list body to a display list. Registers contained in bodies are 7028c2ecf20Sopenharmony_ci * processed after registers contained in the main display list, in the order in 7038c2ecf20Sopenharmony_ci * which bodies are added. 7048c2ecf20Sopenharmony_ci * 7058c2ecf20Sopenharmony_ci * Adding a body to a display list passes ownership of the body to the list. The 7068c2ecf20Sopenharmony_ci * caller retains its reference to the body when adding it to the display list, 7078c2ecf20Sopenharmony_ci * but is not allowed to add new entries to the body. 7088c2ecf20Sopenharmony_ci * 7098c2ecf20Sopenharmony_ci * The reference must be explicitly released by a call to vsp1_dl_body_put() 7108c2ecf20Sopenharmony_ci * when the body isn't needed anymore. 7118c2ecf20Sopenharmony_ci */ 7128c2ecf20Sopenharmony_ciint vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) 7138c2ecf20Sopenharmony_ci{ 7148c2ecf20Sopenharmony_ci refcount_inc(&dlb->refcnt); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci list_add_tail(&dlb->list, &dl->bodies); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci return 0; 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci/** 7228c2ecf20Sopenharmony_ci * vsp1_dl_list_add_chain - Add a display list to a chain 7238c2ecf20Sopenharmony_ci * @head: The head display list 7248c2ecf20Sopenharmony_ci * @dl: The new display list 7258c2ecf20Sopenharmony_ci * 7268c2ecf20Sopenharmony_ci * Add a display list to an existing display list chain. The chained lists 7278c2ecf20Sopenharmony_ci * will be automatically processed by the hardware without intervention from 7288c2ecf20Sopenharmony_ci * the CPU. A display list end interrupt will only complete after the last 7298c2ecf20Sopenharmony_ci * display list in the chain has completed processing. 7308c2ecf20Sopenharmony_ci * 7318c2ecf20Sopenharmony_ci * Adding a display list to a chain passes ownership of the display list to 7328c2ecf20Sopenharmony_ci * the head display list item. The chain is released when the head dl item is 7338c2ecf20Sopenharmony_ci * put back with __vsp1_dl_list_put(). 7348c2ecf20Sopenharmony_ci */ 7358c2ecf20Sopenharmony_ciint vsp1_dl_list_add_chain(struct vsp1_dl_list *head, 7368c2ecf20Sopenharmony_ci struct vsp1_dl_list *dl) 7378c2ecf20Sopenharmony_ci{ 7388c2ecf20Sopenharmony_ci head->has_chain = true; 7398c2ecf20Sopenharmony_ci list_add_tail(&dl->chain, &head->chain); 7408c2ecf20Sopenharmony_ci return 0; 7418c2ecf20Sopenharmony_ci} 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_cistatic void vsp1_dl_ext_cmd_fill_header(struct vsp1_dl_ext_cmd *cmd) 7448c2ecf20Sopenharmony_ci{ 7458c2ecf20Sopenharmony_ci cmd->cmds[0].opcode = cmd->opcode; 7468c2ecf20Sopenharmony_ci cmd->cmds[0].flags = cmd->flags; 7478c2ecf20Sopenharmony_ci cmd->cmds[0].address_set = cmd->data_dma; 7488c2ecf20Sopenharmony_ci cmd->cmds[0].reserved = 0; 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_cistatic void vsp1_dl_list_fill_header(struct vsp1_dl_list *dl, bool is_last) 7528c2ecf20Sopenharmony_ci{ 7538c2ecf20Sopenharmony_ci struct vsp1_dl_manager *dlm = dl->dlm; 7548c2ecf20Sopenharmony_ci struct vsp1_dl_header_list *hdr = dl->header->lists; 7558c2ecf20Sopenharmony_ci struct vsp1_dl_body *dlb; 7568c2ecf20Sopenharmony_ci unsigned int num_lists = 0; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci /* 7598c2ecf20Sopenharmony_ci * Fill the header with the display list bodies addresses and sizes. The 7608c2ecf20Sopenharmony_ci * address of the first body has already been filled when the display 7618c2ecf20Sopenharmony_ci * list was allocated. 7628c2ecf20Sopenharmony_ci */ 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci hdr->num_bytes = dl->body0->num_entries 7658c2ecf20Sopenharmony_ci * sizeof(*dl->header->lists); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci list_for_each_entry(dlb, &dl->bodies, list) { 7688c2ecf20Sopenharmony_ci num_lists++; 7698c2ecf20Sopenharmony_ci hdr++; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci hdr->addr = dlb->dma; 7728c2ecf20Sopenharmony_ci hdr->num_bytes = dlb->num_entries 7738c2ecf20Sopenharmony_ci * sizeof(*dl->header->lists); 7748c2ecf20Sopenharmony_ci } 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci dl->header->num_lists = num_lists; 7778c2ecf20Sopenharmony_ci dl->header->flags = 0; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci /* 7808c2ecf20Sopenharmony_ci * Enable the interrupt for the end of each frame. In continuous mode 7818c2ecf20Sopenharmony_ci * chained lists are used with one list per frame, so enable the 7828c2ecf20Sopenharmony_ci * interrupt for each list. In singleshot mode chained lists are used 7838c2ecf20Sopenharmony_ci * to partition a single frame, so enable the interrupt for the last 7848c2ecf20Sopenharmony_ci * list only. 7858c2ecf20Sopenharmony_ci */ 7868c2ecf20Sopenharmony_ci if (!dlm->singleshot || is_last) 7878c2ecf20Sopenharmony_ci dl->header->flags |= VSP1_DLH_INT_ENABLE; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci /* 7908c2ecf20Sopenharmony_ci * In continuous mode enable auto-start for all lists, as the VSP must 7918c2ecf20Sopenharmony_ci * loop on the same list until a new one is queued. In singleshot mode 7928c2ecf20Sopenharmony_ci * enable auto-start for all lists but the last to chain processing of 7938c2ecf20Sopenharmony_ci * partitions without software intervention. 7948c2ecf20Sopenharmony_ci */ 7958c2ecf20Sopenharmony_ci if (!dlm->singleshot || !is_last) 7968c2ecf20Sopenharmony_ci dl->header->flags |= VSP1_DLH_AUTO_START; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci if (!is_last) { 7998c2ecf20Sopenharmony_ci /* 8008c2ecf20Sopenharmony_ci * If this is not the last display list in the chain, queue the 8018c2ecf20Sopenharmony_ci * next item for automatic processing by the hardware. 8028c2ecf20Sopenharmony_ci */ 8038c2ecf20Sopenharmony_ci struct vsp1_dl_list *next = list_next_entry(dl, chain); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci dl->header->next_header = next->dma; 8068c2ecf20Sopenharmony_ci } else if (!dlm->singleshot) { 8078c2ecf20Sopenharmony_ci /* 8088c2ecf20Sopenharmony_ci * if the display list manager works in continuous mode, the VSP 8098c2ecf20Sopenharmony_ci * should loop over the display list continuously until 8108c2ecf20Sopenharmony_ci * instructed to do otherwise. 8118c2ecf20Sopenharmony_ci */ 8128c2ecf20Sopenharmony_ci dl->header->next_header = dl->dma; 8138c2ecf20Sopenharmony_ci } 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci if (!dl->extension) 8168c2ecf20Sopenharmony_ci return; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci dl->extension->flags = 0; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci if (dl->pre_cmd) { 8218c2ecf20Sopenharmony_ci dl->extension->pre_ext_dl_plist = dl->pre_cmd->cmd_dma; 8228c2ecf20Sopenharmony_ci dl->extension->pre_ext_dl_num_cmd = dl->pre_cmd->num_cmds; 8238c2ecf20Sopenharmony_ci dl->extension->flags |= VSP1_DLH_EXT_PRE_CMD_EXEC; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci vsp1_dl_ext_cmd_fill_header(dl->pre_cmd); 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci if (dl->post_cmd) { 8298c2ecf20Sopenharmony_ci dl->extension->post_ext_dl_plist = dl->post_cmd->cmd_dma; 8308c2ecf20Sopenharmony_ci dl->extension->post_ext_dl_num_cmd = dl->post_cmd->num_cmds; 8318c2ecf20Sopenharmony_ci dl->extension->flags |= VSP1_DLH_EXT_POST_CMD_EXEC; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci vsp1_dl_ext_cmd_fill_header(dl->post_cmd); 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci} 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_cistatic bool vsp1_dl_list_hw_update_pending(struct vsp1_dl_manager *dlm) 8388c2ecf20Sopenharmony_ci{ 8398c2ecf20Sopenharmony_ci struct vsp1_device *vsp1 = dlm->vsp1; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci if (!dlm->queued) 8428c2ecf20Sopenharmony_ci return false; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci /* 8458c2ecf20Sopenharmony_ci * Check whether the VSP1 has taken the update. The hardware indicates 8468c2ecf20Sopenharmony_ci * this by clearing the UPDHDR bit in the CMD register. 8478c2ecf20Sopenharmony_ci */ 8488c2ecf20Sopenharmony_ci return !!(vsp1_read(vsp1, VI6_CMD(dlm->index)) & VI6_CMD_UPDHDR); 8498c2ecf20Sopenharmony_ci} 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_cistatic void vsp1_dl_list_hw_enqueue(struct vsp1_dl_list *dl) 8528c2ecf20Sopenharmony_ci{ 8538c2ecf20Sopenharmony_ci struct vsp1_dl_manager *dlm = dl->dlm; 8548c2ecf20Sopenharmony_ci struct vsp1_device *vsp1 = dlm->vsp1; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci /* 8578c2ecf20Sopenharmony_ci * Program the display list header address. If the hardware is idle 8588c2ecf20Sopenharmony_ci * (single-shot mode or first frame in continuous mode) it will then be 8598c2ecf20Sopenharmony_ci * started independently. If the hardware is operating, the 8608c2ecf20Sopenharmony_ci * VI6_DL_HDR_REF_ADDR register will be updated with the display list 8618c2ecf20Sopenharmony_ci * address. 8628c2ecf20Sopenharmony_ci */ 8638c2ecf20Sopenharmony_ci vsp1_write(vsp1, VI6_DL_HDR_ADDR(dlm->index), dl->dma); 8648c2ecf20Sopenharmony_ci} 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_cistatic void vsp1_dl_list_commit_continuous(struct vsp1_dl_list *dl) 8678c2ecf20Sopenharmony_ci{ 8688c2ecf20Sopenharmony_ci struct vsp1_dl_manager *dlm = dl->dlm; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci /* 8718c2ecf20Sopenharmony_ci * If a previous display list has been queued to the hardware but not 8728c2ecf20Sopenharmony_ci * processed yet, the VSP can start processing it at any time. In that 8738c2ecf20Sopenharmony_ci * case we can't replace the queued list by the new one, as we could 8748c2ecf20Sopenharmony_ci * race with the hardware. We thus mark the update as pending, it will 8758c2ecf20Sopenharmony_ci * be queued up to the hardware by the frame end interrupt handler. 8768c2ecf20Sopenharmony_ci * 8778c2ecf20Sopenharmony_ci * If a display list is already pending we simply drop it as the new 8788c2ecf20Sopenharmony_ci * display list is assumed to contain a more recent configuration. It is 8798c2ecf20Sopenharmony_ci * an error if the already pending list has the 8808c2ecf20Sopenharmony_ci * VSP1_DL_FRAME_END_INTERNAL flag set, as there is then a process 8818c2ecf20Sopenharmony_ci * waiting for that list to complete. This shouldn't happen as the 8828c2ecf20Sopenharmony_ci * waiting process should perform proper locking, but warn just in 8838c2ecf20Sopenharmony_ci * case. 8848c2ecf20Sopenharmony_ci */ 8858c2ecf20Sopenharmony_ci if (vsp1_dl_list_hw_update_pending(dlm)) { 8868c2ecf20Sopenharmony_ci WARN_ON(dlm->pending && 8878c2ecf20Sopenharmony_ci (dlm->pending->flags & VSP1_DL_FRAME_END_INTERNAL)); 8888c2ecf20Sopenharmony_ci __vsp1_dl_list_put(dlm->pending); 8898c2ecf20Sopenharmony_ci dlm->pending = dl; 8908c2ecf20Sopenharmony_ci return; 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci /* 8948c2ecf20Sopenharmony_ci * Pass the new display list to the hardware and mark it as queued. It 8958c2ecf20Sopenharmony_ci * will become active when the hardware starts processing it. 8968c2ecf20Sopenharmony_ci */ 8978c2ecf20Sopenharmony_ci vsp1_dl_list_hw_enqueue(dl); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci __vsp1_dl_list_put(dlm->queued); 9008c2ecf20Sopenharmony_ci dlm->queued = dl; 9018c2ecf20Sopenharmony_ci} 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_cistatic void vsp1_dl_list_commit_singleshot(struct vsp1_dl_list *dl) 9048c2ecf20Sopenharmony_ci{ 9058c2ecf20Sopenharmony_ci struct vsp1_dl_manager *dlm = dl->dlm; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci /* 9088c2ecf20Sopenharmony_ci * When working in single-shot mode, the caller guarantees that the 9098c2ecf20Sopenharmony_ci * hardware is idle at this point. Just commit the head display list 9108c2ecf20Sopenharmony_ci * to hardware. Chained lists will be started automatically. 9118c2ecf20Sopenharmony_ci */ 9128c2ecf20Sopenharmony_ci vsp1_dl_list_hw_enqueue(dl); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci dlm->active = dl; 9158c2ecf20Sopenharmony_ci} 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_civoid vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags) 9188c2ecf20Sopenharmony_ci{ 9198c2ecf20Sopenharmony_ci struct vsp1_dl_manager *dlm = dl->dlm; 9208c2ecf20Sopenharmony_ci struct vsp1_dl_list *dl_next; 9218c2ecf20Sopenharmony_ci unsigned long flags; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci /* Fill the header for the head and chained display lists. */ 9248c2ecf20Sopenharmony_ci vsp1_dl_list_fill_header(dl, list_empty(&dl->chain)); 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci list_for_each_entry(dl_next, &dl->chain, chain) { 9278c2ecf20Sopenharmony_ci bool last = list_is_last(&dl_next->chain, &dl->chain); 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci vsp1_dl_list_fill_header(dl_next, last); 9308c2ecf20Sopenharmony_ci } 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci dl->flags = dl_flags & ~VSP1_DL_FRAME_END_COMPLETED; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci spin_lock_irqsave(&dlm->lock, flags); 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci if (dlm->singleshot) 9378c2ecf20Sopenharmony_ci vsp1_dl_list_commit_singleshot(dl); 9388c2ecf20Sopenharmony_ci else 9398c2ecf20Sopenharmony_ci vsp1_dl_list_commit_continuous(dl); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dlm->lock, flags); 9428c2ecf20Sopenharmony_ci} 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 9458c2ecf20Sopenharmony_ci * Display List Manager 9468c2ecf20Sopenharmony_ci */ 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci/** 9498c2ecf20Sopenharmony_ci * vsp1_dlm_irq_frame_end - Display list handler for the frame end interrupt 9508c2ecf20Sopenharmony_ci * @dlm: the display list manager 9518c2ecf20Sopenharmony_ci * 9528c2ecf20Sopenharmony_ci * Return a set of flags that indicates display list completion status. 9538c2ecf20Sopenharmony_ci * 9548c2ecf20Sopenharmony_ci * The VSP1_DL_FRAME_END_COMPLETED flag indicates that the previous display list 9558c2ecf20Sopenharmony_ci * has completed at frame end. If the flag is not returned display list 9568c2ecf20Sopenharmony_ci * completion has been delayed by one frame because the display list commit 9578c2ecf20Sopenharmony_ci * raced with the frame end interrupt. The function always returns with the flag 9588c2ecf20Sopenharmony_ci * set in single-shot mode as display list processing is then not continuous and 9598c2ecf20Sopenharmony_ci * races never occur. 9608c2ecf20Sopenharmony_ci * 9618c2ecf20Sopenharmony_ci * The following flags are only supported for continuous mode. 9628c2ecf20Sopenharmony_ci * 9638c2ecf20Sopenharmony_ci * The VSP1_DL_FRAME_END_INTERNAL flag indicates that the display list that just 9648c2ecf20Sopenharmony_ci * became active had been queued with the internal notification flag. 9658c2ecf20Sopenharmony_ci * 9668c2ecf20Sopenharmony_ci * The VSP1_DL_FRAME_END_WRITEBACK flag indicates that the previously active 9678c2ecf20Sopenharmony_ci * display list had been queued with the writeback flag. 9688c2ecf20Sopenharmony_ci */ 9698c2ecf20Sopenharmony_ciunsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) 9708c2ecf20Sopenharmony_ci{ 9718c2ecf20Sopenharmony_ci struct vsp1_device *vsp1 = dlm->vsp1; 9728c2ecf20Sopenharmony_ci u32 status = vsp1_read(vsp1, VI6_STATUS); 9738c2ecf20Sopenharmony_ci unsigned int flags = 0; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci spin_lock(&dlm->lock); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci /* 9788c2ecf20Sopenharmony_ci * The mem-to-mem pipelines work in single-shot mode. No new display 9798c2ecf20Sopenharmony_ci * list can be queued, we don't have to do anything. 9808c2ecf20Sopenharmony_ci */ 9818c2ecf20Sopenharmony_ci if (dlm->singleshot) { 9828c2ecf20Sopenharmony_ci __vsp1_dl_list_put(dlm->active); 9838c2ecf20Sopenharmony_ci dlm->active = NULL; 9848c2ecf20Sopenharmony_ci flags |= VSP1_DL_FRAME_END_COMPLETED; 9858c2ecf20Sopenharmony_ci goto done; 9868c2ecf20Sopenharmony_ci } 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci /* 9898c2ecf20Sopenharmony_ci * If the commit operation raced with the interrupt and occurred after 9908c2ecf20Sopenharmony_ci * the frame end event but before interrupt processing, the hardware 9918c2ecf20Sopenharmony_ci * hasn't taken the update into account yet. We have to skip one frame 9928c2ecf20Sopenharmony_ci * and retry. 9938c2ecf20Sopenharmony_ci */ 9948c2ecf20Sopenharmony_ci if (vsp1_dl_list_hw_update_pending(dlm)) 9958c2ecf20Sopenharmony_ci goto done; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci /* 9988c2ecf20Sopenharmony_ci * Progressive streams report only TOP fields. If we have a BOTTOM 9998c2ecf20Sopenharmony_ci * field, we are interlaced, and expect the frame to complete on the 10008c2ecf20Sopenharmony_ci * next frame end interrupt. 10018c2ecf20Sopenharmony_ci */ 10028c2ecf20Sopenharmony_ci if (status & VI6_STATUS_FLD_STD(dlm->index)) 10038c2ecf20Sopenharmony_ci goto done; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci /* 10068c2ecf20Sopenharmony_ci * If the active display list has the writeback flag set, the frame 10078c2ecf20Sopenharmony_ci * completion marks the end of the writeback capture. Return the 10088c2ecf20Sopenharmony_ci * VSP1_DL_FRAME_END_WRITEBACK flag and reset the display list's 10098c2ecf20Sopenharmony_ci * writeback flag. 10108c2ecf20Sopenharmony_ci */ 10118c2ecf20Sopenharmony_ci if (dlm->active && (dlm->active->flags & VSP1_DL_FRAME_END_WRITEBACK)) { 10128c2ecf20Sopenharmony_ci flags |= VSP1_DL_FRAME_END_WRITEBACK; 10138c2ecf20Sopenharmony_ci dlm->active->flags &= ~VSP1_DL_FRAME_END_WRITEBACK; 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci /* 10178c2ecf20Sopenharmony_ci * The device starts processing the queued display list right after the 10188c2ecf20Sopenharmony_ci * frame end interrupt. The display list thus becomes active. 10198c2ecf20Sopenharmony_ci */ 10208c2ecf20Sopenharmony_ci if (dlm->queued) { 10218c2ecf20Sopenharmony_ci if (dlm->queued->flags & VSP1_DL_FRAME_END_INTERNAL) 10228c2ecf20Sopenharmony_ci flags |= VSP1_DL_FRAME_END_INTERNAL; 10238c2ecf20Sopenharmony_ci dlm->queued->flags &= ~VSP1_DL_FRAME_END_INTERNAL; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci __vsp1_dl_list_put(dlm->active); 10268c2ecf20Sopenharmony_ci dlm->active = dlm->queued; 10278c2ecf20Sopenharmony_ci dlm->queued = NULL; 10288c2ecf20Sopenharmony_ci flags |= VSP1_DL_FRAME_END_COMPLETED; 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci /* 10328c2ecf20Sopenharmony_ci * Now that the VSP has started processing the queued display list, we 10338c2ecf20Sopenharmony_ci * can queue the pending display list to the hardware if one has been 10348c2ecf20Sopenharmony_ci * prepared. 10358c2ecf20Sopenharmony_ci */ 10368c2ecf20Sopenharmony_ci if (dlm->pending) { 10378c2ecf20Sopenharmony_ci vsp1_dl_list_hw_enqueue(dlm->pending); 10388c2ecf20Sopenharmony_ci dlm->queued = dlm->pending; 10398c2ecf20Sopenharmony_ci dlm->pending = NULL; 10408c2ecf20Sopenharmony_ci } 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_cidone: 10438c2ecf20Sopenharmony_ci spin_unlock(&dlm->lock); 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci return flags; 10468c2ecf20Sopenharmony_ci} 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci/* Hardware Setup */ 10498c2ecf20Sopenharmony_civoid vsp1_dlm_setup(struct vsp1_device *vsp1) 10508c2ecf20Sopenharmony_ci{ 10518c2ecf20Sopenharmony_ci unsigned int i; 10528c2ecf20Sopenharmony_ci u32 ctrl = (256 << VI6_DL_CTRL_AR_WAIT_SHIFT) 10538c2ecf20Sopenharmony_ci | VI6_DL_CTRL_DC2 | VI6_DL_CTRL_DC1 | VI6_DL_CTRL_DC0 10548c2ecf20Sopenharmony_ci | VI6_DL_CTRL_DLE; 10558c2ecf20Sopenharmony_ci u32 ext_dl = (0x02 << VI6_DL_EXT_CTRL_POLINT_SHIFT) 10568c2ecf20Sopenharmony_ci | VI6_DL_EXT_CTRL_DLPRI | VI6_DL_EXT_CTRL_EXT; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci if (vsp1_feature(vsp1, VSP1_HAS_EXT_DL)) { 10598c2ecf20Sopenharmony_ci for (i = 0; i < vsp1->info->wpf_count; ++i) 10608c2ecf20Sopenharmony_ci vsp1_write(vsp1, VI6_DL_EXT_CTRL(i), ext_dl); 10618c2ecf20Sopenharmony_ci } 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci vsp1_write(vsp1, VI6_DL_CTRL, ctrl); 10648c2ecf20Sopenharmony_ci vsp1_write(vsp1, VI6_DL_SWAP, VI6_DL_SWAP_LWS); 10658c2ecf20Sopenharmony_ci} 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_civoid vsp1_dlm_reset(struct vsp1_dl_manager *dlm) 10688c2ecf20Sopenharmony_ci{ 10698c2ecf20Sopenharmony_ci unsigned long flags; 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci spin_lock_irqsave(&dlm->lock, flags); 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci __vsp1_dl_list_put(dlm->active); 10748c2ecf20Sopenharmony_ci __vsp1_dl_list_put(dlm->queued); 10758c2ecf20Sopenharmony_ci __vsp1_dl_list_put(dlm->pending); 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dlm->lock, flags); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci dlm->active = NULL; 10808c2ecf20Sopenharmony_ci dlm->queued = NULL; 10818c2ecf20Sopenharmony_ci dlm->pending = NULL; 10828c2ecf20Sopenharmony_ci} 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_cistruct vsp1_dl_body *vsp1_dlm_dl_body_get(struct vsp1_dl_manager *dlm) 10858c2ecf20Sopenharmony_ci{ 10868c2ecf20Sopenharmony_ci return vsp1_dl_body_get(dlm->pool); 10878c2ecf20Sopenharmony_ci} 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_cistruct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, 10908c2ecf20Sopenharmony_ci unsigned int index, 10918c2ecf20Sopenharmony_ci unsigned int prealloc) 10928c2ecf20Sopenharmony_ci{ 10938c2ecf20Sopenharmony_ci struct vsp1_dl_manager *dlm; 10948c2ecf20Sopenharmony_ci size_t header_size; 10958c2ecf20Sopenharmony_ci unsigned int i; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci dlm = devm_kzalloc(vsp1->dev, sizeof(*dlm), GFP_KERNEL); 10988c2ecf20Sopenharmony_ci if (!dlm) 10998c2ecf20Sopenharmony_ci return NULL; 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci dlm->index = index; 11028c2ecf20Sopenharmony_ci dlm->singleshot = vsp1->info->uapi; 11038c2ecf20Sopenharmony_ci dlm->vsp1 = vsp1; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci spin_lock_init(&dlm->lock); 11068c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dlm->free); 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci /* 11098c2ecf20Sopenharmony_ci * Initialize the display list body and allocate DMA memory for the body 11108c2ecf20Sopenharmony_ci * and the header. Both are allocated together to avoid memory 11118c2ecf20Sopenharmony_ci * fragmentation, with the header located right after the body in 11128c2ecf20Sopenharmony_ci * memory. An extra body is allocated on top of the prealloc to account 11138c2ecf20Sopenharmony_ci * for the cached body used by the vsp1_pipeline object. 11148c2ecf20Sopenharmony_ci */ 11158c2ecf20Sopenharmony_ci header_size = vsp1_feature(vsp1, VSP1_HAS_EXT_DL) ? 11168c2ecf20Sopenharmony_ci sizeof(struct vsp1_dl_header_extended) : 11178c2ecf20Sopenharmony_ci sizeof(struct vsp1_dl_header); 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci header_size = ALIGN(header_size, 8); 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci dlm->pool = vsp1_dl_body_pool_create(vsp1, prealloc + 1, 11228c2ecf20Sopenharmony_ci VSP1_DL_NUM_ENTRIES, header_size); 11238c2ecf20Sopenharmony_ci if (!dlm->pool) 11248c2ecf20Sopenharmony_ci return NULL; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci for (i = 0; i < prealloc; ++i) { 11278c2ecf20Sopenharmony_ci struct vsp1_dl_list *dl; 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci dl = vsp1_dl_list_alloc(dlm); 11308c2ecf20Sopenharmony_ci if (!dl) { 11318c2ecf20Sopenharmony_ci vsp1_dlm_destroy(dlm); 11328c2ecf20Sopenharmony_ci return NULL; 11338c2ecf20Sopenharmony_ci } 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci /* The extended header immediately follows the header. */ 11368c2ecf20Sopenharmony_ci if (vsp1_feature(vsp1, VSP1_HAS_EXT_DL)) 11378c2ecf20Sopenharmony_ci dl->extension = (void *)dl->header 11388c2ecf20Sopenharmony_ci + sizeof(*dl->header); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci list_add_tail(&dl->list, &dlm->free); 11418c2ecf20Sopenharmony_ci } 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci if (vsp1_feature(vsp1, VSP1_HAS_EXT_DL)) { 11448c2ecf20Sopenharmony_ci dlm->cmdpool = vsp1_dl_cmd_pool_create(vsp1, 11458c2ecf20Sopenharmony_ci VSP1_EXTCMD_AUTOFLD, prealloc); 11468c2ecf20Sopenharmony_ci if (!dlm->cmdpool) { 11478c2ecf20Sopenharmony_ci vsp1_dlm_destroy(dlm); 11488c2ecf20Sopenharmony_ci return NULL; 11498c2ecf20Sopenharmony_ci } 11508c2ecf20Sopenharmony_ci } 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci return dlm; 11538c2ecf20Sopenharmony_ci} 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_civoid vsp1_dlm_destroy(struct vsp1_dl_manager *dlm) 11568c2ecf20Sopenharmony_ci{ 11578c2ecf20Sopenharmony_ci struct vsp1_dl_list *dl, *next; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci if (!dlm) 11608c2ecf20Sopenharmony_ci return; 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci list_for_each_entry_safe(dl, next, &dlm->free, list) { 11638c2ecf20Sopenharmony_ci list_del(&dl->list); 11648c2ecf20Sopenharmony_ci vsp1_dl_list_free(dl); 11658c2ecf20Sopenharmony_ci } 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci vsp1_dl_body_pool_destroy(dlm->pool); 11688c2ecf20Sopenharmony_ci vsp1_dl_ext_cmd_pool_destroy(dlm->cmdpool); 11698c2ecf20Sopenharmony_ci} 1170