| /* |
| * |
| * (C) COPYRIGHT 2013-2015,2017-2018 ARM Limited. All rights reserved. |
| * |
| * This program is free software and is provided to you under the terms of the |
| * GNU General Public License version 2 as published by the Free Software |
| * Foundation, and any use by you of this program is subject to the terms |
| * of such GNU licence. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, you can access it online at |
| * http://www.gnu.org/licenses/gpl-2.0.html. |
| * |
| * SPDX-License-Identifier: GPL-2.0 |
| * |
| */ |
| #include <linux/dma-mapping.h> |
| #include <mali_kbase.h> |
| #include <mali_kbase_10969_workaround.h> |
| |
| /* Mask of X and Y coordinates for the coordinates words in the descriptors*/ |
| #define X_COORDINATE_MASK 0x00000FFF |
| #define Y_COORDINATE_MASK 0x0FFF0000 |
| /* Max number of words needed from the fragment shader job descriptor */ |
| #define JOB_HEADER_SIZE_IN_WORDS 10 |
| #define JOB_HEADER_SIZE (JOB_HEADER_SIZE_IN_WORDS*sizeof(u32)) |
| |
| /* Word 0: Status Word */ |
| #define JOB_DESC_STATUS_WORD 0 |
| /* Word 1: Restart Index */ |
| #define JOB_DESC_RESTART_INDEX_WORD 1 |
| /* Word 2: Fault address low word */ |
| #define JOB_DESC_FAULT_ADDR_LOW_WORD 2 |
| /* Word 8: Minimum Tile Coordinates */ |
| #define FRAG_JOB_DESC_MIN_TILE_COORD_WORD 8 |
| /* Word 9: Maximum Tile Coordinates */ |
| #define FRAG_JOB_DESC_MAX_TILE_COORD_WORD 9 |
| |
| int kbasep_10969_workaround_clamp_coordinates(struct kbase_jd_atom *katom) |
| { |
| struct device *dev = katom->kctx->kbdev->dev; |
| u32 clamped = 0; |
| struct kbase_va_region *region; |
| struct tagged_addr *page_array; |
| u64 page_index; |
| u32 offset = katom->jc & (~PAGE_MASK); |
| u32 *page_1 = NULL; |
| u32 *page_2 = NULL; |
| u32 job_header[JOB_HEADER_SIZE_IN_WORDS]; |
| void *dst = job_header; |
| u32 minX, minY, maxX, maxY; |
| u32 restartX, restartY; |
| struct page *p; |
| u32 copy_size; |
| |
| dev_warn(dev, "Called TILE_RANGE_FAULT workaround clamping function.\n"); |
| if (!(katom->core_req & BASE_JD_REQ_FS)) |
| return 0; |
| |
| kbase_gpu_vm_lock(katom->kctx); |
| region = kbase_region_tracker_find_region_enclosing_address(katom->kctx, |
| katom->jc); |
| if (kbase_is_region_invalid_or_free(region)) |
| goto out_unlock; |
| |
| page_array = kbase_get_cpu_phy_pages(region); |
| if (!page_array) |
| goto out_unlock; |
| |
| page_index = (katom->jc >> PAGE_SHIFT) - region->start_pfn; |
| |
| p = as_page(page_array[page_index]); |
| |
| /* we need the first 10 words of the fragment shader job descriptor. |
| * We need to check that the offset + 10 words is less that the page |
| * size otherwise we need to load the next page. |
| * page_size_overflow will be equal to 0 in case the whole descriptor |
| * is within the page > 0 otherwise. |
| */ |
| copy_size = MIN(PAGE_SIZE - offset, JOB_HEADER_SIZE); |
| |
| page_1 = kmap_atomic(p); |
| |
| /* page_1 is a u32 pointer, offset is expressed in bytes */ |
| page_1 += offset>>2; |
| |
| kbase_sync_single_for_cpu(katom->kctx->kbdev, |
| kbase_dma_addr(p) + offset, |
| copy_size, DMA_BIDIRECTIONAL); |
| |
| memcpy(dst, page_1, copy_size); |
| |
| /* The data needed overflows page the dimension, |
| * need to map the subsequent page */ |
| if (copy_size < JOB_HEADER_SIZE) { |
| p = as_page(page_array[page_index + 1]); |
| page_2 = kmap_atomic(p); |
| |
| kbase_sync_single_for_cpu(katom->kctx->kbdev, |
| kbase_dma_addr(p), |
| JOB_HEADER_SIZE - copy_size, DMA_BIDIRECTIONAL); |
| |
| memcpy(dst + copy_size, page_2, JOB_HEADER_SIZE - copy_size); |
| } |
| |
| /* We managed to correctly map one or two pages (in case of overflow) */ |
| /* Get Bounding Box data and restart index from fault address low word */ |
| minX = job_header[FRAG_JOB_DESC_MIN_TILE_COORD_WORD] & X_COORDINATE_MASK; |
| minY = job_header[FRAG_JOB_DESC_MIN_TILE_COORD_WORD] & Y_COORDINATE_MASK; |
| maxX = job_header[FRAG_JOB_DESC_MAX_TILE_COORD_WORD] & X_COORDINATE_MASK; |
| maxY = job_header[FRAG_JOB_DESC_MAX_TILE_COORD_WORD] & Y_COORDINATE_MASK; |
| restartX = job_header[JOB_DESC_FAULT_ADDR_LOW_WORD] & X_COORDINATE_MASK; |
| restartY = job_header[JOB_DESC_FAULT_ADDR_LOW_WORD] & Y_COORDINATE_MASK; |
| |
| dev_warn(dev, "Before Clamping:\n" |
| "Jobstatus: %08x\n" |
| "restartIdx: %08x\n" |
| "Fault_addr_low: %08x\n" |
| "minCoordsX: %08x minCoordsY: %08x\n" |
| "maxCoordsX: %08x maxCoordsY: %08x\n", |
| job_header[JOB_DESC_STATUS_WORD], |
| job_header[JOB_DESC_RESTART_INDEX_WORD], |
| job_header[JOB_DESC_FAULT_ADDR_LOW_WORD], |
| minX, minY, |
| maxX, maxY); |
| |
| /* Set the restart index to the one which generated the fault*/ |
| job_header[JOB_DESC_RESTART_INDEX_WORD] = |
| job_header[JOB_DESC_FAULT_ADDR_LOW_WORD]; |
| |
| if (restartX < minX) { |
| job_header[JOB_DESC_RESTART_INDEX_WORD] = (minX) | restartY; |
| dev_warn(dev, |
| "Clamping restart X index to minimum. %08x clamped to %08x\n", |
| restartX, minX); |
| clamped = 1; |
| } |
| if (restartY < minY) { |
| job_header[JOB_DESC_RESTART_INDEX_WORD] = (minY) | restartX; |
| dev_warn(dev, |
| "Clamping restart Y index to minimum. %08x clamped to %08x\n", |
| restartY, minY); |
| clamped = 1; |
| } |
| if (restartX > maxX) { |
| job_header[JOB_DESC_RESTART_INDEX_WORD] = (maxX) | restartY; |
| dev_warn(dev, |
| "Clamping restart X index to maximum. %08x clamped to %08x\n", |
| restartX, maxX); |
| clamped = 1; |
| } |
| if (restartY > maxY) { |
| job_header[JOB_DESC_RESTART_INDEX_WORD] = (maxY) | restartX; |
| dev_warn(dev, |
| "Clamping restart Y index to maximum. %08x clamped to %08x\n", |
| restartY, maxY); |
| clamped = 1; |
| } |
| |
| if (clamped) { |
| /* Reset the fault address low word |
| * and set the job status to STOPPED */ |
| job_header[JOB_DESC_FAULT_ADDR_LOW_WORD] = 0x0; |
| job_header[JOB_DESC_STATUS_WORD] = BASE_JD_EVENT_STOPPED; |
| dev_warn(dev, "After Clamping:\n" |
| "Jobstatus: %08x\n" |
| "restartIdx: %08x\n" |
| "Fault_addr_low: %08x\n" |
| "minCoordsX: %08x minCoordsY: %08x\n" |
| "maxCoordsX: %08x maxCoordsY: %08x\n", |
| job_header[JOB_DESC_STATUS_WORD], |
| job_header[JOB_DESC_RESTART_INDEX_WORD], |
| job_header[JOB_DESC_FAULT_ADDR_LOW_WORD], |
| minX, minY, |
| maxX, maxY); |
| |
| /* Flush CPU cache to update memory for future GPU reads*/ |
| memcpy(page_1, dst, copy_size); |
| p = as_page(page_array[page_index]); |
| |
| kbase_sync_single_for_device(katom->kctx->kbdev, |
| kbase_dma_addr(p) + offset, |
| copy_size, DMA_TO_DEVICE); |
| |
| if (copy_size < JOB_HEADER_SIZE) { |
| memcpy(page_2, dst + copy_size, |
| JOB_HEADER_SIZE - copy_size); |
| p = as_page(page_array[page_index + 1]); |
| |
| kbase_sync_single_for_device(katom->kctx->kbdev, |
| kbase_dma_addr(p), |
| JOB_HEADER_SIZE - copy_size, |
| DMA_TO_DEVICE); |
| } |
| } |
| if (copy_size < JOB_HEADER_SIZE) |
| kunmap_atomic(page_2); |
| |
| kunmap_atomic(page_1); |
| |
| out_unlock: |
| kbase_gpu_vm_unlock(katom->kctx); |
| return clamped; |
| } |