162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT
262306a36Sopenharmony_ci/**************************************************************************
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright 2017 VMware, Inc., Palo Alto, CA., USA
562306a36Sopenharmony_ci * All Rights Reserved.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
862306a36Sopenharmony_ci * copy of this software and associated documentation files (the
962306a36Sopenharmony_ci * "Software"), to deal in the Software without restriction, including
1062306a36Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish,
1162306a36Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to
1262306a36Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to
1362306a36Sopenharmony_ci * the following conditions:
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the
1662306a36Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions
1762306a36Sopenharmony_ci * of the Software.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2062306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2162306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
2262306a36Sopenharmony_ci * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
2362306a36Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
2462306a36Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
2562306a36Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE.
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci **************************************************************************/
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include "vmwgfx_drv.h"
3062306a36Sopenharmony_ci#include <linux/highmem.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/*
3362306a36Sopenharmony_ci * Template that implements find_first_diff() for a generic
3462306a36Sopenharmony_ci * unsigned integer type. @size and return value are in bytes.
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_ci#define VMW_FIND_FIRST_DIFF(_type)			 \
3762306a36Sopenharmony_cistatic size_t vmw_find_first_diff_ ## _type		 \
3862306a36Sopenharmony_ci	(const _type * dst, const _type * src, size_t size)\
3962306a36Sopenharmony_ci{							 \
4062306a36Sopenharmony_ci	size_t i;					 \
4162306a36Sopenharmony_ci							 \
4262306a36Sopenharmony_ci	for (i = 0; i < size; i += sizeof(_type)) {	 \
4362306a36Sopenharmony_ci		if (*dst++ != *src++)			 \
4462306a36Sopenharmony_ci			break;				 \
4562306a36Sopenharmony_ci	}						 \
4662306a36Sopenharmony_ci							 \
4762306a36Sopenharmony_ci	return i;					 \
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/*
5262306a36Sopenharmony_ci * Template that implements find_last_diff() for a generic
5362306a36Sopenharmony_ci * unsigned integer type. Pointers point to the item following the
5462306a36Sopenharmony_ci * *end* of the area to be examined. @size and return value are in
5562306a36Sopenharmony_ci * bytes.
5662306a36Sopenharmony_ci */
5762306a36Sopenharmony_ci#define VMW_FIND_LAST_DIFF(_type)					\
5862306a36Sopenharmony_cistatic ssize_t vmw_find_last_diff_ ## _type(				\
5962306a36Sopenharmony_ci	const _type * dst, const _type * src, size_t size)		\
6062306a36Sopenharmony_ci{									\
6162306a36Sopenharmony_ci	while (size) {							\
6262306a36Sopenharmony_ci		if (*--dst != *--src)					\
6362306a36Sopenharmony_ci			break;						\
6462306a36Sopenharmony_ci									\
6562306a36Sopenharmony_ci		size -= sizeof(_type);					\
6662306a36Sopenharmony_ci	}								\
6762306a36Sopenharmony_ci	return size;							\
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/*
7262306a36Sopenharmony_ci * Instantiate find diff functions for relevant unsigned integer sizes,
7362306a36Sopenharmony_ci * assuming that wider integers are faster (including aligning) up to the
7462306a36Sopenharmony_ci * architecture native width, which is assumed to be 32 bit unless
7562306a36Sopenharmony_ci * CONFIG_64BIT is defined.
7662306a36Sopenharmony_ci */
7762306a36Sopenharmony_ciVMW_FIND_FIRST_DIFF(u8);
7862306a36Sopenharmony_ciVMW_FIND_LAST_DIFF(u8);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ciVMW_FIND_FIRST_DIFF(u16);
8162306a36Sopenharmony_ciVMW_FIND_LAST_DIFF(u16);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ciVMW_FIND_FIRST_DIFF(u32);
8462306a36Sopenharmony_ciVMW_FIND_LAST_DIFF(u32);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci#ifdef CONFIG_64BIT
8762306a36Sopenharmony_ciVMW_FIND_FIRST_DIFF(u64);
8862306a36Sopenharmony_ciVMW_FIND_LAST_DIFF(u64);
8962306a36Sopenharmony_ci#endif
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci/* We use size aligned copies. This computes (addr - align(addr)) */
9362306a36Sopenharmony_ci#define SPILL(_var, _type) ((unsigned long) _var & (sizeof(_type) - 1))
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci/*
9762306a36Sopenharmony_ci * Template to compute find_first_diff() for a certain integer type
9862306a36Sopenharmony_ci * including a head copy for alignment, and adjustment of parameters
9962306a36Sopenharmony_ci * for tail find or increased resolution find using an unsigned integer find
10062306a36Sopenharmony_ci * of smaller width. If finding is complete, and resolution is sufficient,
10162306a36Sopenharmony_ci * the macro executes a return statement. Otherwise it falls through.
10262306a36Sopenharmony_ci */
10362306a36Sopenharmony_ci#define VMW_TRY_FIND_FIRST_DIFF(_type)					\
10462306a36Sopenharmony_cido {									\
10562306a36Sopenharmony_ci	unsigned int spill = SPILL(dst, _type);				\
10662306a36Sopenharmony_ci	size_t diff_offs;						\
10762306a36Sopenharmony_ci									\
10862306a36Sopenharmony_ci	if (spill && spill == SPILL(src, _type) &&			\
10962306a36Sopenharmony_ci	    sizeof(_type) - spill <= size) {				\
11062306a36Sopenharmony_ci		spill = sizeof(_type) - spill;				\
11162306a36Sopenharmony_ci		diff_offs = vmw_find_first_diff_u8(dst, src, spill);	\
11262306a36Sopenharmony_ci		if (diff_offs < spill)					\
11362306a36Sopenharmony_ci			return round_down(offset + diff_offs, granularity); \
11462306a36Sopenharmony_ci									\
11562306a36Sopenharmony_ci		dst += spill;						\
11662306a36Sopenharmony_ci		src += spill;						\
11762306a36Sopenharmony_ci		size -= spill;						\
11862306a36Sopenharmony_ci		offset += spill;					\
11962306a36Sopenharmony_ci		spill = 0;						\
12062306a36Sopenharmony_ci	}								\
12162306a36Sopenharmony_ci	if (!spill && !SPILL(src, _type)) {				\
12262306a36Sopenharmony_ci		size_t to_copy = size &	 ~(sizeof(_type) - 1);		\
12362306a36Sopenharmony_ci									\
12462306a36Sopenharmony_ci		diff_offs = vmw_find_first_diff_ ## _type		\
12562306a36Sopenharmony_ci			((_type *) dst, (_type *) src, to_copy);	\
12662306a36Sopenharmony_ci		if (diff_offs >= size || granularity == sizeof(_type))	\
12762306a36Sopenharmony_ci			return (offset + diff_offs);			\
12862306a36Sopenharmony_ci									\
12962306a36Sopenharmony_ci		dst += diff_offs;					\
13062306a36Sopenharmony_ci		src += diff_offs;					\
13162306a36Sopenharmony_ci		size -= diff_offs;					\
13262306a36Sopenharmony_ci		offset += diff_offs;					\
13362306a36Sopenharmony_ci	}								\
13462306a36Sopenharmony_ci} while (0)								\
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci/**
13862306a36Sopenharmony_ci * vmw_find_first_diff - find the first difference between dst and src
13962306a36Sopenharmony_ci *
14062306a36Sopenharmony_ci * @dst: The destination address
14162306a36Sopenharmony_ci * @src: The source address
14262306a36Sopenharmony_ci * @size: Number of bytes to compare
14362306a36Sopenharmony_ci * @granularity: The granularity needed for the return value in bytes.
14462306a36Sopenharmony_ci * return: The offset from find start where the first difference was
14562306a36Sopenharmony_ci * encountered in bytes. If no difference was found, the function returns
14662306a36Sopenharmony_ci * a value >= @size.
14762306a36Sopenharmony_ci */
14862306a36Sopenharmony_cistatic size_t vmw_find_first_diff(const u8 *dst, const u8 *src, size_t size,
14962306a36Sopenharmony_ci				  size_t granularity)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	size_t offset = 0;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	/*
15462306a36Sopenharmony_ci	 * Try finding with large integers if alignment allows, or we can
15562306a36Sopenharmony_ci	 * fix it. Fall through if we need better resolution or alignment
15662306a36Sopenharmony_ci	 * was bad.
15762306a36Sopenharmony_ci	 */
15862306a36Sopenharmony_ci#ifdef CONFIG_64BIT
15962306a36Sopenharmony_ci	VMW_TRY_FIND_FIRST_DIFF(u64);
16062306a36Sopenharmony_ci#endif
16162306a36Sopenharmony_ci	VMW_TRY_FIND_FIRST_DIFF(u32);
16262306a36Sopenharmony_ci	VMW_TRY_FIND_FIRST_DIFF(u16);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	return round_down(offset + vmw_find_first_diff_u8(dst, src, size),
16562306a36Sopenharmony_ci			  granularity);
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci/*
17062306a36Sopenharmony_ci * Template to compute find_last_diff() for a certain integer type
17162306a36Sopenharmony_ci * including a tail copy for alignment, and adjustment of parameters
17262306a36Sopenharmony_ci * for head find or increased resolution find using an unsigned integer find
17362306a36Sopenharmony_ci * of smaller width. If finding is complete, and resolution is sufficient,
17462306a36Sopenharmony_ci * the macro executes a return statement. Otherwise it falls through.
17562306a36Sopenharmony_ci */
17662306a36Sopenharmony_ci#define VMW_TRY_FIND_LAST_DIFF(_type)					\
17762306a36Sopenharmony_cido {									\
17862306a36Sopenharmony_ci	unsigned int spill = SPILL(dst, _type);				\
17962306a36Sopenharmony_ci	ssize_t location;						\
18062306a36Sopenharmony_ci	ssize_t diff_offs;						\
18162306a36Sopenharmony_ci									\
18262306a36Sopenharmony_ci	if (spill && spill <= size && spill == SPILL(src, _type)) {	\
18362306a36Sopenharmony_ci		diff_offs = vmw_find_last_diff_u8(dst, src, spill);	\
18462306a36Sopenharmony_ci		if (diff_offs) {					\
18562306a36Sopenharmony_ci			location = size - spill + diff_offs - 1;	\
18662306a36Sopenharmony_ci			return round_down(location, granularity);	\
18762306a36Sopenharmony_ci		}							\
18862306a36Sopenharmony_ci									\
18962306a36Sopenharmony_ci		dst -= spill;						\
19062306a36Sopenharmony_ci		src -= spill;						\
19162306a36Sopenharmony_ci		size -= spill;						\
19262306a36Sopenharmony_ci		spill = 0;						\
19362306a36Sopenharmony_ci	}								\
19462306a36Sopenharmony_ci	if (!spill && !SPILL(src, _type)) {				\
19562306a36Sopenharmony_ci		size_t to_copy = round_down(size, sizeof(_type));	\
19662306a36Sopenharmony_ci									\
19762306a36Sopenharmony_ci		diff_offs = vmw_find_last_diff_ ## _type		\
19862306a36Sopenharmony_ci			((_type *) dst, (_type *) src, to_copy);	\
19962306a36Sopenharmony_ci		location = size - to_copy + diff_offs - sizeof(_type);	\
20062306a36Sopenharmony_ci		if (location < 0 || granularity == sizeof(_type))	\
20162306a36Sopenharmony_ci			return location;				\
20262306a36Sopenharmony_ci									\
20362306a36Sopenharmony_ci		dst -= to_copy - diff_offs;				\
20462306a36Sopenharmony_ci		src -= to_copy - diff_offs;				\
20562306a36Sopenharmony_ci		size -= to_copy - diff_offs;				\
20662306a36Sopenharmony_ci	}								\
20762306a36Sopenharmony_ci} while (0)
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci/**
21162306a36Sopenharmony_ci * vmw_find_last_diff - find the last difference between dst and src
21262306a36Sopenharmony_ci *
21362306a36Sopenharmony_ci * @dst: The destination address
21462306a36Sopenharmony_ci * @src: The source address
21562306a36Sopenharmony_ci * @size: Number of bytes to compare
21662306a36Sopenharmony_ci * @granularity: The granularity needed for the return value in bytes.
21762306a36Sopenharmony_ci * return: The offset from find start where the last difference was
21862306a36Sopenharmony_ci * encountered in bytes, or a negative value if no difference was found.
21962306a36Sopenharmony_ci */
22062306a36Sopenharmony_cistatic ssize_t vmw_find_last_diff(const u8 *dst, const u8 *src, size_t size,
22162306a36Sopenharmony_ci				  size_t granularity)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	dst += size;
22462306a36Sopenharmony_ci	src += size;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci#ifdef CONFIG_64BIT
22762306a36Sopenharmony_ci	VMW_TRY_FIND_LAST_DIFF(u64);
22862306a36Sopenharmony_ci#endif
22962306a36Sopenharmony_ci	VMW_TRY_FIND_LAST_DIFF(u32);
23062306a36Sopenharmony_ci	VMW_TRY_FIND_LAST_DIFF(u16);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	return round_down(vmw_find_last_diff_u8(dst, src, size) - 1,
23362306a36Sopenharmony_ci			  granularity);
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci/**
23862306a36Sopenharmony_ci * vmw_memcpy - A wrapper around kernel memcpy with allowing to plug it into a
23962306a36Sopenharmony_ci * struct vmw_diff_cpy.
24062306a36Sopenharmony_ci *
24162306a36Sopenharmony_ci * @diff: The struct vmw_diff_cpy closure argument (unused).
24262306a36Sopenharmony_ci * @dest: The copy destination.
24362306a36Sopenharmony_ci * @src: The copy source.
24462306a36Sopenharmony_ci * @n: Number of bytes to copy.
24562306a36Sopenharmony_ci */
24662306a36Sopenharmony_civoid vmw_memcpy(struct vmw_diff_cpy *diff, u8 *dest, const u8 *src, size_t n)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	memcpy(dest, src, n);
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci/**
25362306a36Sopenharmony_ci * vmw_adjust_rect - Adjust rectangle coordinates for newly found difference
25462306a36Sopenharmony_ci *
25562306a36Sopenharmony_ci * @diff: The struct vmw_diff_cpy used to track the modified bounding box.
25662306a36Sopenharmony_ci * @diff_offs: The offset from @diff->line_offset where the difference was
25762306a36Sopenharmony_ci * found.
25862306a36Sopenharmony_ci */
25962306a36Sopenharmony_cistatic void vmw_adjust_rect(struct vmw_diff_cpy *diff, size_t diff_offs)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	size_t offs = (diff_offs + diff->line_offset) / diff->cpp;
26262306a36Sopenharmony_ci	struct drm_rect *rect = &diff->rect;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	rect->x1 = min_t(int, rect->x1, offs);
26562306a36Sopenharmony_ci	rect->x2 = max_t(int, rect->x2, offs + 1);
26662306a36Sopenharmony_ci	rect->y1 = min_t(int, rect->y1, diff->line);
26762306a36Sopenharmony_ci	rect->y2 = max_t(int, rect->y2, diff->line + 1);
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci/**
27162306a36Sopenharmony_ci * vmw_diff_memcpy - memcpy that creates a bounding box of modified content.
27262306a36Sopenharmony_ci *
27362306a36Sopenharmony_ci * @diff: The struct vmw_diff_cpy used to track the modified bounding box.
27462306a36Sopenharmony_ci * @dest: The copy destination.
27562306a36Sopenharmony_ci * @src: The copy source.
27662306a36Sopenharmony_ci * @n: Number of bytes to copy.
27762306a36Sopenharmony_ci *
27862306a36Sopenharmony_ci * In order to correctly track the modified content, the field @diff->line must
27962306a36Sopenharmony_ci * be pre-loaded with the current line number, the field @diff->line_offset must
28062306a36Sopenharmony_ci * be pre-loaded with the line offset in bytes where the copy starts, and
28162306a36Sopenharmony_ci * finally the field @diff->cpp need to be preloaded with the number of bytes
28262306a36Sopenharmony_ci * per unit in the horizontal direction of the area we're examining.
28362306a36Sopenharmony_ci * Typically bytes per pixel.
28462306a36Sopenharmony_ci * This is needed to know the needed granularity of the difference computing
28562306a36Sopenharmony_ci * operations. A higher cpp generally leads to faster execution at the cost of
28662306a36Sopenharmony_ci * bounding box width precision.
28762306a36Sopenharmony_ci */
28862306a36Sopenharmony_civoid vmw_diff_memcpy(struct vmw_diff_cpy *diff, u8 *dest, const u8 *src,
28962306a36Sopenharmony_ci		     size_t n)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	ssize_t csize, byte_len;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	if (WARN_ON_ONCE(round_down(n, diff->cpp) != n))
29462306a36Sopenharmony_ci		return;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	/* TODO: Possibly use a single vmw_find_first_diff per line? */
29762306a36Sopenharmony_ci	csize = vmw_find_first_diff(dest, src, n, diff->cpp);
29862306a36Sopenharmony_ci	if (csize < n) {
29962306a36Sopenharmony_ci		vmw_adjust_rect(diff, csize);
30062306a36Sopenharmony_ci		byte_len = diff->cpp;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci		/*
30362306a36Sopenharmony_ci		 * Starting from where first difference was found, find
30462306a36Sopenharmony_ci		 * location of last difference, and then copy.
30562306a36Sopenharmony_ci		 */
30662306a36Sopenharmony_ci		diff->line_offset += csize;
30762306a36Sopenharmony_ci		dest += csize;
30862306a36Sopenharmony_ci		src += csize;
30962306a36Sopenharmony_ci		n -= csize;
31062306a36Sopenharmony_ci		csize = vmw_find_last_diff(dest, src, n, diff->cpp);
31162306a36Sopenharmony_ci		if (csize >= 0) {
31262306a36Sopenharmony_ci			byte_len += csize;
31362306a36Sopenharmony_ci			vmw_adjust_rect(diff, csize);
31462306a36Sopenharmony_ci		}
31562306a36Sopenharmony_ci		memcpy(dest, src, byte_len);
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci	diff->line_offset += n;
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci/**
32162306a36Sopenharmony_ci * struct vmw_bo_blit_line_data - Convenience argument to vmw_bo_cpu_blit_line
32262306a36Sopenharmony_ci *
32362306a36Sopenharmony_ci * @mapped_dst: Already mapped destination page index in @dst_pages.
32462306a36Sopenharmony_ci * @dst_addr: Kernel virtual address of mapped destination page.
32562306a36Sopenharmony_ci * @dst_pages: Array of destination bo pages.
32662306a36Sopenharmony_ci * @dst_num_pages: Number of destination bo pages.
32762306a36Sopenharmony_ci * @dst_prot: Destination bo page protection.
32862306a36Sopenharmony_ci * @mapped_src: Already mapped source page index in @dst_pages.
32962306a36Sopenharmony_ci * @src_addr: Kernel virtual address of mapped source page.
33062306a36Sopenharmony_ci * @src_pages: Array of source bo pages.
33162306a36Sopenharmony_ci * @src_num_pages: Number of source bo pages.
33262306a36Sopenharmony_ci * @src_prot: Source bo page protection.
33362306a36Sopenharmony_ci * @diff: Struct vmw_diff_cpy, in the end forwarded to the memcpy routine.
33462306a36Sopenharmony_ci */
33562306a36Sopenharmony_cistruct vmw_bo_blit_line_data {
33662306a36Sopenharmony_ci	u32 mapped_dst;
33762306a36Sopenharmony_ci	u8 *dst_addr;
33862306a36Sopenharmony_ci	struct page **dst_pages;
33962306a36Sopenharmony_ci	u32 dst_num_pages;
34062306a36Sopenharmony_ci	pgprot_t dst_prot;
34162306a36Sopenharmony_ci	u32 mapped_src;
34262306a36Sopenharmony_ci	u8 *src_addr;
34362306a36Sopenharmony_ci	struct page **src_pages;
34462306a36Sopenharmony_ci	u32 src_num_pages;
34562306a36Sopenharmony_ci	pgprot_t src_prot;
34662306a36Sopenharmony_ci	struct vmw_diff_cpy *diff;
34762306a36Sopenharmony_ci};
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci/**
35062306a36Sopenharmony_ci * vmw_bo_cpu_blit_line - Blit part of a line from one bo to another.
35162306a36Sopenharmony_ci *
35262306a36Sopenharmony_ci * @d: Blit data as described above.
35362306a36Sopenharmony_ci * @dst_offset: Destination copy start offset from start of bo.
35462306a36Sopenharmony_ci * @src_offset: Source copy start offset from start of bo.
35562306a36Sopenharmony_ci * @bytes_to_copy: Number of bytes to copy in this line.
35662306a36Sopenharmony_ci */
35762306a36Sopenharmony_cistatic int vmw_bo_cpu_blit_line(struct vmw_bo_blit_line_data *d,
35862306a36Sopenharmony_ci				u32 dst_offset,
35962306a36Sopenharmony_ci				u32 src_offset,
36062306a36Sopenharmony_ci				u32 bytes_to_copy)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	struct vmw_diff_cpy *diff = d->diff;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	while (bytes_to_copy) {
36562306a36Sopenharmony_ci		u32 copy_size = bytes_to_copy;
36662306a36Sopenharmony_ci		u32 dst_page = dst_offset >> PAGE_SHIFT;
36762306a36Sopenharmony_ci		u32 src_page = src_offset >> PAGE_SHIFT;
36862306a36Sopenharmony_ci		u32 dst_page_offset = dst_offset & ~PAGE_MASK;
36962306a36Sopenharmony_ci		u32 src_page_offset = src_offset & ~PAGE_MASK;
37062306a36Sopenharmony_ci		bool unmap_dst = d->dst_addr && dst_page != d->mapped_dst;
37162306a36Sopenharmony_ci		bool unmap_src = d->src_addr && (src_page != d->mapped_src ||
37262306a36Sopenharmony_ci						 unmap_dst);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci		copy_size = min_t(u32, copy_size, PAGE_SIZE - dst_page_offset);
37562306a36Sopenharmony_ci		copy_size = min_t(u32, copy_size, PAGE_SIZE - src_page_offset);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci		if (unmap_src) {
37862306a36Sopenharmony_ci			kunmap_atomic(d->src_addr);
37962306a36Sopenharmony_ci			d->src_addr = NULL;
38062306a36Sopenharmony_ci		}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci		if (unmap_dst) {
38362306a36Sopenharmony_ci			kunmap_atomic(d->dst_addr);
38462306a36Sopenharmony_ci			d->dst_addr = NULL;
38562306a36Sopenharmony_ci		}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci		if (!d->dst_addr) {
38862306a36Sopenharmony_ci			if (WARN_ON_ONCE(dst_page >= d->dst_num_pages))
38962306a36Sopenharmony_ci				return -EINVAL;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci			d->dst_addr =
39262306a36Sopenharmony_ci				kmap_atomic_prot(d->dst_pages[dst_page],
39362306a36Sopenharmony_ci						 d->dst_prot);
39462306a36Sopenharmony_ci			if (!d->dst_addr)
39562306a36Sopenharmony_ci				return -ENOMEM;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci			d->mapped_dst = dst_page;
39862306a36Sopenharmony_ci		}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci		if (!d->src_addr) {
40162306a36Sopenharmony_ci			if (WARN_ON_ONCE(src_page >= d->src_num_pages))
40262306a36Sopenharmony_ci				return -EINVAL;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci			d->src_addr =
40562306a36Sopenharmony_ci				kmap_atomic_prot(d->src_pages[src_page],
40662306a36Sopenharmony_ci						 d->src_prot);
40762306a36Sopenharmony_ci			if (!d->src_addr)
40862306a36Sopenharmony_ci				return -ENOMEM;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci			d->mapped_src = src_page;
41162306a36Sopenharmony_ci		}
41262306a36Sopenharmony_ci		diff->do_cpy(diff, d->dst_addr + dst_page_offset,
41362306a36Sopenharmony_ci			     d->src_addr + src_page_offset, copy_size);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		bytes_to_copy -= copy_size;
41662306a36Sopenharmony_ci		dst_offset += copy_size;
41762306a36Sopenharmony_ci		src_offset += copy_size;
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	return 0;
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci/**
42462306a36Sopenharmony_ci * vmw_bo_cpu_blit - in-kernel cpu blit.
42562306a36Sopenharmony_ci *
42662306a36Sopenharmony_ci * @dst: Destination buffer object.
42762306a36Sopenharmony_ci * @dst_offset: Destination offset of blit start in bytes.
42862306a36Sopenharmony_ci * @dst_stride: Destination stride in bytes.
42962306a36Sopenharmony_ci * @src: Source buffer object.
43062306a36Sopenharmony_ci * @src_offset: Source offset of blit start in bytes.
43162306a36Sopenharmony_ci * @src_stride: Source stride in bytes.
43262306a36Sopenharmony_ci * @w: Width of blit.
43362306a36Sopenharmony_ci * @h: Height of blit.
43462306a36Sopenharmony_ci * @diff: The struct vmw_diff_cpy used to track the modified bounding box.
43562306a36Sopenharmony_ci * return: Zero on success. Negative error value on failure. Will print out
43662306a36Sopenharmony_ci * kernel warnings on caller bugs.
43762306a36Sopenharmony_ci *
43862306a36Sopenharmony_ci * Performs a CPU blit from one buffer object to another avoiding a full
43962306a36Sopenharmony_ci * bo vmap which may exhaust- or fragment vmalloc space.
44062306a36Sopenharmony_ci * On supported architectures (x86), we're using kmap_atomic which avoids
44162306a36Sopenharmony_ci * cross-processor TLB- and cache flushes and may, on non-HIGHMEM systems
44262306a36Sopenharmony_ci * reference already set-up mappings.
44362306a36Sopenharmony_ci *
44462306a36Sopenharmony_ci * Neither of the buffer objects may be placed in PCI memory
44562306a36Sopenharmony_ci * (Fixed memory in TTM terminology) when using this function.
44662306a36Sopenharmony_ci */
44762306a36Sopenharmony_ciint vmw_bo_cpu_blit(struct ttm_buffer_object *dst,
44862306a36Sopenharmony_ci		    u32 dst_offset, u32 dst_stride,
44962306a36Sopenharmony_ci		    struct ttm_buffer_object *src,
45062306a36Sopenharmony_ci		    u32 src_offset, u32 src_stride,
45162306a36Sopenharmony_ci		    u32 w, u32 h,
45262306a36Sopenharmony_ci		    struct vmw_diff_cpy *diff)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	struct ttm_operation_ctx ctx = {
45562306a36Sopenharmony_ci		.interruptible = false,
45662306a36Sopenharmony_ci		.no_wait_gpu = false
45762306a36Sopenharmony_ci	};
45862306a36Sopenharmony_ci	u32 j, initial_line = dst_offset / dst_stride;
45962306a36Sopenharmony_ci	struct vmw_bo_blit_line_data d;
46062306a36Sopenharmony_ci	int ret = 0;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	/* Buffer objects need to be either pinned or reserved: */
46362306a36Sopenharmony_ci	if (!(dst->pin_count))
46462306a36Sopenharmony_ci		dma_resv_assert_held(dst->base.resv);
46562306a36Sopenharmony_ci	if (!(src->pin_count))
46662306a36Sopenharmony_ci		dma_resv_assert_held(src->base.resv);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	if (!ttm_tt_is_populated(dst->ttm)) {
46962306a36Sopenharmony_ci		ret = dst->bdev->funcs->ttm_tt_populate(dst->bdev, dst->ttm, &ctx);
47062306a36Sopenharmony_ci		if (ret)
47162306a36Sopenharmony_ci			return ret;
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	if (!ttm_tt_is_populated(src->ttm)) {
47562306a36Sopenharmony_ci		ret = src->bdev->funcs->ttm_tt_populate(src->bdev, src->ttm, &ctx);
47662306a36Sopenharmony_ci		if (ret)
47762306a36Sopenharmony_ci			return ret;
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	d.mapped_dst = 0;
48162306a36Sopenharmony_ci	d.mapped_src = 0;
48262306a36Sopenharmony_ci	d.dst_addr = NULL;
48362306a36Sopenharmony_ci	d.src_addr = NULL;
48462306a36Sopenharmony_ci	d.dst_pages = dst->ttm->pages;
48562306a36Sopenharmony_ci	d.src_pages = src->ttm->pages;
48662306a36Sopenharmony_ci	d.dst_num_pages = PFN_UP(dst->resource->size);
48762306a36Sopenharmony_ci	d.src_num_pages = PFN_UP(src->resource->size);
48862306a36Sopenharmony_ci	d.dst_prot = ttm_io_prot(dst, dst->resource, PAGE_KERNEL);
48962306a36Sopenharmony_ci	d.src_prot = ttm_io_prot(src, src->resource, PAGE_KERNEL);
49062306a36Sopenharmony_ci	d.diff = diff;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	for (j = 0; j < h; ++j) {
49362306a36Sopenharmony_ci		diff->line = j + initial_line;
49462306a36Sopenharmony_ci		diff->line_offset = dst_offset % dst_stride;
49562306a36Sopenharmony_ci		ret = vmw_bo_cpu_blit_line(&d, dst_offset, src_offset, w);
49662306a36Sopenharmony_ci		if (ret)
49762306a36Sopenharmony_ci			goto out;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci		dst_offset += dst_stride;
50062306a36Sopenharmony_ci		src_offset += src_stride;
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ciout:
50362306a36Sopenharmony_ci	if (d.src_addr)
50462306a36Sopenharmony_ci		kunmap_atomic(d.src_addr);
50562306a36Sopenharmony_ci	if (d.dst_addr)
50662306a36Sopenharmony_ci		kunmap_atomic(d.dst_addr);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	return ret;
50962306a36Sopenharmony_ci}
510