brintos

brintos / linux-shallow public Read only

0
0
Text · 30.1 KiB · ad3fa1c Raw
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