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