// SPDX-License-Identifier: GPL-2.0 /* * Support for Intel Camera Imaging ISP subsystem. * Copyright (c) 2010 - 2016, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #include "isp.h" #include "vmem.h" #include "vmem_local.h" #if !defined(HRT_MEMORY_ACCESS) #include "ia_css_device_access.h" #endif #include "assert_support.h" typedef unsigned long long hive_uedge; typedef hive_uedge *hive_wide; /* Copied from SDK: sim_semantics.c */ /* subword bits move like this: MSB[____xxxx____]LSB -> MSB[00000000xxxx]LSB */ #define SUBWORD(w, start, end) (((w) & (((1ULL << ((end) - 1)) - 1) << 1 | 1)) >> (start)) /* inverse subword bits move like this: MSB[xxxx____xxxx]LSB -> MSB[xxxx0000xxxx]LSB */ #define INV_SUBWORD(w, start, end) ((w) & (~(((1ULL << ((end) - 1)) - 1) << 1 | 1) | ((1ULL << (start)) - 1))) #define uedge_bits (8 * sizeof(hive_uedge)) #define move_lower_bits(target, target_bit, src, src_bit) move_subword(target, target_bit, src, 0, src_bit) #define move_upper_bits(target, target_bit, src, src_bit) move_subword(target, target_bit, src, src_bit, uedge_bits) #define move_word(target, target_bit, src) move_subword(target, target_bit, src, 0, uedge_bits) static void move_subword( hive_uedge *target, unsigned int target_bit, hive_uedge src, unsigned int src_start, unsigned int src_end) { unsigned int start_elem = target_bit / uedge_bits; unsigned int start_bit = target_bit % uedge_bits; unsigned int subword_width = src_end - src_start; hive_uedge src_subword = SUBWORD(src, src_start, src_end); if (subword_width + start_bit > uedge_bits) { /* overlap */ hive_uedge old_val1; hive_uedge old_val0 = INV_SUBWORD(target[start_elem], start_bit, uedge_bits); target[start_elem] = old_val0 | (src_subword << start_bit); old_val1 = INV_SUBWORD(target[start_elem + 1], 0, subword_width + start_bit - uedge_bits); target[start_elem + 1] = old_val1 | (src_subword >> (uedge_bits - start_bit)); } else { hive_uedge old_val = INV_SUBWORD(target[start_elem], start_bit, start_bit + subword_width); target[start_elem] = old_val | (src_subword << start_bit); } } static void hive_sim_wide_unpack( hive_wide vector, hive_wide elem, hive_uint elem_bits, hive_uint index) { /* pointers into wide_type: */ unsigned int start_elem = (elem_bits * index) / uedge_bits; unsigned int start_bit = (elem_bits * index) % uedge_bits; unsigned int end_elem = (elem_bits * (index + 1) - 1) / uedge_bits; unsigned int end_bit = ((elem_bits * (index + 1) - 1) % uedge_bits) + 1; if (elem_bits == uedge_bits) { /* easy case for speedup: */ elem[0] = vector[index]; } else if (start_elem == end_elem) { /* only one (<=64 bits) element needs to be (partly) copied: */ move_subword(elem, 0, vector[start_elem], start_bit, end_bit); } else { /* general case: handles edge spanning cases (includes >64bit elements) */ unsigned int bits_written = 0; unsigned int i; move_upper_bits(elem, bits_written, vector[start_elem], start_bit); bits_written += (64 - start_bit); for (i = start_elem + 1; i < end_elem; i++) { move_word(elem, bits_written, vector[i]); bits_written += uedge_bits; } move_lower_bits(elem, bits_written, vector[end_elem], end_bit); } } static void hive_sim_wide_pack( hive_wide vector, hive_wide elem, hive_uint elem_bits, hive_uint index) { /* pointers into wide_type: */ unsigned int start_elem = (elem_bits * index) / uedge_bits; /* easy case for speedup: */ if (elem_bits == uedge_bits) { vector[start_elem] = elem[0]; } else if (elem_bits > uedge_bits) { unsigned int bits_to_write = elem_bits; unsigned int start_bit = elem_bits * index; unsigned int i = 0; for (; bits_to_write > uedge_bits; bits_to_write -= uedge_bits, i++, start_bit += uedge_bits) { move_word(vector, start_bit, elem[i]); } move_lower_bits(vector, start_bit, elem[i], bits_to_write); } else { /* only one element needs to be (partly) copied: */ move_lower_bits(vector, elem_bits * index, elem[0], elem_bits); } } static void load_vector( const isp_ID_t ID, t_vmem_elem *to, const t_vmem_elem *from) { unsigned int i; hive_uedge *data; unsigned int size = sizeof(short) * ISP_NWAY; VMEM_ARRAY(v, 2 * ISP_NWAY); /* Need 2 vectors to work around vmem hss bug */ assert(ISP_BAMEM_BASE[ID] != (hrt_address) - 1); #if !defined(HRT_MEMORY_ACCESS) ia_css_device_load(ISP_BAMEM_BASE[ID] + (unsigned long)from, &v[0][0], size); #else hrt_master_port_load(ISP_BAMEM_BASE[ID] + (unsigned long)from, &v[0][0], size); #endif data = (hive_uedge *)v; for (i = 0; i < ISP_NWAY; i++) { hive_uedge elem = 0; hive_sim_wide_unpack(data, &elem, ISP_VEC_ELEMBITS, i); to[i] = elem; } udelay(1); /* Spend at least 1 cycles per vector */ } static void store_vector( const isp_ID_t ID, t_vmem_elem *to, const t_vmem_elem *from) { unsigned int i; unsigned int size = sizeof(short) * ISP_NWAY; VMEM_ARRAY(v, 2 * ISP_NWAY); /* Need 2 vectors to work around vmem hss bug */ //load_vector (&v[1][0], &to[ISP_NWAY]); /* Fetch the next vector, since it will be overwritten. */ hive_uedge *data = (hive_uedge *)v; for (i = 0; i < ISP_NWAY; i++) { hive_sim_wide_pack(data, (hive_wide)&from[i], ISP_VEC_ELEMBITS, i); } assert(ISP_BAMEM_BASE[ID] != (hrt_address) - 1); #if !defined(HRT_MEMORY_ACCESS) ia_css_device_store(ISP_BAMEM_BASE[ID] + (unsigned long)to, &v, size); #else //hrt_mem_store (ISP, VMEM, (unsigned)to, &v, siz); /* This will overwrite the next vector as well */ hrt_master_port_store(ISP_BAMEM_BASE[ID] + (unsigned long)to, &v, size); #endif udelay(1); /* Spend at least 1 cycles per vector */ } void isp_vmem_load( const isp_ID_t ID, const t_vmem_elem *from, t_vmem_elem *to, unsigned int elems) /* In t_vmem_elem */ { unsigned int c; const t_vmem_elem *vp = from; assert(ID < N_ISP_ID); assert((unsigned long)from % ISP_VEC_ALIGN == 0); assert(elems % ISP_NWAY == 0); for (c = 0; c < elems; c += ISP_NWAY) { load_vector(ID, &to[c], vp); vp = (t_vmem_elem *)((char *)vp + ISP_VEC_ALIGN); } } void isp_vmem_store( const isp_ID_t ID, t_vmem_elem *to, const t_vmem_elem *from, unsigned int elems) /* In t_vmem_elem */ { unsigned int c; t_vmem_elem *vp = to; assert(ID < N_ISP_ID); assert((unsigned long)to % ISP_VEC_ALIGN == 0); assert(elems % ISP_NWAY == 0); for (c = 0; c < elems; c += ISP_NWAY) { store_vector(ID, vp, &from[c]); vp = (t_vmem_elem *)((char *)vp + ISP_VEC_ALIGN); } } void isp_vmem_2d_load( const isp_ID_t ID, const t_vmem_elem *from, t_vmem_elem *to, unsigned int height, unsigned int width, unsigned int stride_to, /* In t_vmem_elem */ unsigned stride_from /* In t_vmem_elem */) { unsigned int h; assert(ID < N_ISP_ID); assert((unsigned long)from % ISP_VEC_ALIGN == 0); assert(width % ISP_NWAY == 0); assert(stride_from % ISP_NWAY == 0); for (h = 0; h < height; h++) { unsigned int c; const t_vmem_elem *vp = from; for (c = 0; c < width; c += ISP_NWAY) { load_vector(ID, &to[stride_to * h + c], vp); vp = (t_vmem_elem *)((char *)vp + ISP_VEC_ALIGN); } from = (const t_vmem_elem *)((const char *)from + stride_from / ISP_NWAY * ISP_VEC_ALIGN); } } void isp_vmem_2d_store( const isp_ID_t ID, t_vmem_elem *to, const t_vmem_elem *from, unsigned int height, unsigned int width, unsigned int stride_to, /* In t_vmem_elem */ unsigned stride_from /* In t_vmem_elem */) { unsigned int h; assert(ID < N_ISP_ID); assert((unsigned long)to % ISP_VEC_ALIGN == 0); assert(width % ISP_NWAY == 0); assert(stride_to % ISP_NWAY == 0); for (h = 0; h < height; h++) { unsigned int c; t_vmem_elem *vp = to; for (c = 0; c < width; c += ISP_NWAY) { store_vector(ID, vp, &from[stride_from * h + c]); vp = (t_vmem_elem *)((char *)vp + ISP_VEC_ALIGN); } to = (t_vmem_elem *)((char *)to + stride_to / ISP_NWAY * ISP_VEC_ALIGN); } }