| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Support for Intel Camera Imaging ISP subsystem. |
| * Copyright (c) 2015, 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 <linux/slab.h> |
| |
| #include <math_support.h> |
| #include "sh_css_param_shading.h" |
| #include "ia_css_shading.h" |
| #include "assert_support.h" |
| #include "sh_css_defs.h" |
| #include "sh_css_internal.h" |
| #include "ia_css_debug.h" |
| #include "ia_css_pipe_binarydesc.h" |
| |
| #include "sh_css_hrt.h" |
| |
| #include "platform_support.h" |
| |
| /* Bilinear interpolation on shading tables: |
| * For each target point T, we calculate the 4 surrounding source points: |
| * ul (upper left), ur (upper right), ll (lower left) and lr (lower right). |
| * We then calculate the distances from the T to the source points: x0, x1, |
| * y0 and y1. |
| * We then calculate the value of T: |
| * dx0*dy0*Slr + dx0*dy1*Sur + dx1*dy0*Sll + dx1*dy1*Sul. |
| * We choose a grid size of 1x1 which means: |
| * dx1 = 1-dx0 |
| * dy1 = 1-dy0 |
| * |
| * Sul dx0 dx1 Sur |
| * .<----->|<------------->. |
| * ^ |
| * dy0| |
| * v T |
| * - . |
| * ^ |
| * | |
| * dy1| |
| * v |
| * . . |
| * Sll Slr |
| * |
| * Padding: |
| * The area that the ISP operates on can include padding both on the left |
| * and the right. We need to padd the shading table such that the shading |
| * values end up on the correct pixel values. This means we must padd the |
| * shading table to match the ISP padding. |
| * We can have 5 cases: |
| * 1. All 4 points fall in the left padding. |
| * 2. The left 2 points fall in the left padding. |
| * 3. All 4 points fall in the cropped (target) region. |
| * 4. The right 2 points fall in the right padding. |
| * 5. All 4 points fall in the right padding. |
| * Cases 1 and 5 are easy to handle: we simply use the |
| * value 1 in the shading table. |
| * Cases 2 and 4 require interpolation that takes into |
| * account how far into the padding area the pixels |
| * fall. We extrapolate the shading table into the |
| * padded area and then interpolate. |
| */ |
| static void |
| crop_and_interpolate(unsigned int cropped_width, |
| unsigned int cropped_height, |
| unsigned int left_padding, |
| int right_padding, |
| int top_padding, |
| const struct ia_css_shading_table *in_table, |
| struct ia_css_shading_table *out_table, |
| enum ia_css_sc_color color) |
| { |
| unsigned int i, j, |
| sensor_width, |
| sensor_height, |
| table_width, |
| table_height, |
| table_cell_h, |
| out_cell_size, |
| in_cell_size, |
| out_start_row, |
| padded_width; |
| int out_start_col, /* can be negative to indicate padded space */ |
| table_cell_w; |
| unsigned short *in_ptr, |
| *out_ptr; |
| |
| assert(in_table); |
| assert(out_table); |
| |
| sensor_width = in_table->sensor_width; |
| sensor_height = in_table->sensor_height; |
| table_width = in_table->width; |
| table_height = in_table->height; |
| in_ptr = in_table->data[color]; |
| out_ptr = out_table->data[color]; |
| |
| padded_width = cropped_width + left_padding + right_padding; |
| out_cell_size = CEIL_DIV(padded_width, out_table->width - 1); |
| in_cell_size = CEIL_DIV(sensor_width, table_width - 1); |
| |
| out_start_col = ((int)sensor_width - (int)cropped_width) / 2 - left_padding; |
| out_start_row = ((int)sensor_height - (int)cropped_height) / 2 - top_padding; |
| table_cell_w = (int)((table_width - 1) * in_cell_size); |
| table_cell_h = (table_height - 1) * in_cell_size; |
| |
| for (i = 0; i < out_table->height; i++) { |
| int ty, src_y0, src_y1; |
| unsigned int sy0, sy1, dy0, dy1, divy; |
| |
| /* calculate target point and make sure it falls within |
| the table */ |
| ty = out_start_row + i * out_cell_size; |
| |
| /* calculate closest source points in shading table and |
| make sure they fall within the table */ |
| src_y0 = ty / (int)in_cell_size; |
| if (in_cell_size < out_cell_size) |
| src_y1 = (ty + out_cell_size) / in_cell_size; |
| else |
| src_y1 = src_y0 + 1; |
| src_y0 = clamp(src_y0, 0, (int)table_height - 1); |
| src_y1 = clamp(src_y1, 0, (int)table_height - 1); |
| ty = min(clamp(ty, 0, (int)sensor_height - 1), |
| (int)table_cell_h); |
| |
| /* calculate closest source points for distance computation */ |
| sy0 = min(src_y0 * in_cell_size, sensor_height - 1); |
| sy1 = min(src_y1 * in_cell_size, sensor_height - 1); |
| /* calculate distance between source and target pixels */ |
| dy0 = ty - sy0; |
| dy1 = sy1 - ty; |
| divy = sy1 - sy0; |
| if (divy == 0) { |
| dy0 = 1; |
| divy = 1; |
| } |
| |
| for (j = 0; j < out_table->width; j++, out_ptr++) { |
| int tx, src_x0, src_x1; |
| unsigned int sx0, sx1, dx0, dx1, divx; |
| unsigned short s_ul, s_ur, s_ll, s_lr; |
| |
| /* calculate target point */ |
| tx = out_start_col + j * out_cell_size; |
| /* calculate closest source points. */ |
| src_x0 = tx / (int)in_cell_size; |
| if (in_cell_size < out_cell_size) { |
| src_x1 = (tx + out_cell_size) / |
| (int)in_cell_size; |
| } else { |
| src_x1 = src_x0 + 1; |
| } |
| /* if src points fall in padding, select closest ones.*/ |
| src_x0 = clamp(src_x0, 0, (int)table_width - 1); |
| src_x1 = clamp(src_x1, 0, (int)table_width - 1); |
| tx = min(clamp(tx, 0, (int)sensor_width - 1), |
| (int)table_cell_w); |
| /* calculate closest source points for distance |
| computation */ |
| sx0 = min(src_x0 * in_cell_size, sensor_width - 1); |
| sx1 = min(src_x1 * in_cell_size, sensor_width - 1); |
| /* calculate distances between source and target |
| pixels */ |
| dx0 = tx - sx0; |
| dx1 = sx1 - tx; |
| divx = sx1 - sx0; |
| /* if we're at the edge, we just use the closest |
| point still in the grid. We make up for the divider |
| in this case by setting the distance to |
| out_cell_size, since it's actually 0. */ |
| if (divx == 0) { |
| dx0 = 1; |
| divx = 1; |
| } |
| |
| /* get source pixel values */ |
| s_ul = in_ptr[(table_width * src_y0) + src_x0]; |
| s_ur = in_ptr[(table_width * src_y0) + src_x1]; |
| s_ll = in_ptr[(table_width * src_y1) + src_x0]; |
| s_lr = in_ptr[(table_width * src_y1) + src_x1]; |
| |
| *out_ptr = (unsigned short)((dx0 * dy0 * s_lr + dx0 * dy1 * s_ur + dx1 * dy0 * |
| s_ll + dx1 * dy1 * s_ul) / |
| (divx * divy)); |
| } |
| } |
| } |
| |
| void |
| sh_css_params_shading_id_table_generate( |
| struct ia_css_shading_table **target_table, |
| unsigned int table_width, |
| unsigned int table_height) |
| { |
| /* initialize table with ones, shift becomes zero */ |
| unsigned int i, j; |
| struct ia_css_shading_table *result; |
| |
| assert(target_table); |
| |
| result = ia_css_shading_table_alloc(table_width, table_height); |
| if (!result) { |
| *target_table = NULL; |
| return; |
| } |
| |
| for (i = 0; i < IA_CSS_SC_NUM_COLORS; i++) { |
| for (j = 0; j < table_height * table_width; j++) |
| result->data[i][j] = 1; |
| } |
| result->fraction_bits = 0; |
| *target_table = result; |
| } |
| |
| void |
| prepare_shading_table(const struct ia_css_shading_table *in_table, |
| unsigned int sensor_binning, |
| struct ia_css_shading_table **target_table, |
| const struct ia_css_binary *binary, |
| unsigned int bds_factor) |
| { |
| unsigned int input_width, |
| input_height, |
| table_width, |
| table_height, |
| left_padding, |
| top_padding, |
| padded_width, |
| left_cropping, |
| i; |
| unsigned int bds_numerator, bds_denominator; |
| int right_padding; |
| |
| struct ia_css_shading_table *result; |
| |
| assert(target_table); |
| assert(binary); |
| |
| if (!in_table) { |
| sh_css_params_shading_id_table_generate(target_table, |
| binary->sctbl_legacy_width_per_color, |
| binary->sctbl_legacy_height); |
| return; |
| } |
| |
| padded_width = binary->in_frame_info.padded_width; |
| /* We use the ISP input resolution for the shading table because |
| shading correction is performed in the bayer domain (before bayer |
| down scaling). */ |
| #if defined(USE_INPUT_SYSTEM_VERSION_2401) |
| padded_width = CEIL_MUL(binary->effective_in_frame_res.width + 2 * |
| ISP_VEC_NELEMS, |
| 2 * ISP_VEC_NELEMS); |
| #endif |
| input_height = binary->in_frame_info.res.height; |
| input_width = binary->in_frame_info.res.width; |
| left_padding = binary->left_padding; |
| left_cropping = (binary->info->sp.pipeline.left_cropping == 0) ? |
| binary->dvs_envelope.width : 2 * ISP_VEC_NELEMS; |
| |
| sh_css_bds_factor_get_numerator_denominator |
| (bds_factor, &bds_numerator, &bds_denominator); |
| |
| left_padding = (left_padding + binary->info->sp.pipeline.left_cropping) * |
| bds_numerator / bds_denominator - |
| binary->info->sp.pipeline.left_cropping; |
| right_padding = (binary->internal_frame_info.res.width - |
| binary->effective_in_frame_res.width * bds_denominator / |
| bds_numerator - left_cropping) * bds_numerator / bds_denominator; |
| top_padding = binary->info->sp.pipeline.top_cropping * bds_numerator / |
| bds_denominator - |
| binary->info->sp.pipeline.top_cropping; |
| |
| #if !defined(USE_WINDOWS_BINNING_FACTOR) |
| /* @deprecated{This part of the code will be replaced by the code |
| * in the #else section below to make the calculation same across |
| * all platforms. |
| * Android and Windows platforms interpret the binning_factor parameter |
| * differently. In Android, the binning factor is expressed in the form |
| * 2^N * 2^N, whereas in Windows platform, the binning factor is N*N} |
| */ |
| |
| /* We take into account the binning done by the sensor. We do this |
| by cropping the non-binned part of the shading table and then |
| increasing the size of a grid cell with this same binning factor. */ |
| input_width <<= sensor_binning; |
| input_height <<= sensor_binning; |
| /* We also scale the padding by the same binning factor. This will |
| make it much easier later on to calculate the padding of the |
| shading table. */ |
| left_padding <<= sensor_binning; |
| right_padding <<= sensor_binning; |
| top_padding <<= sensor_binning; |
| #else |
| input_width *= sensor_binning; |
| input_height *= sensor_binning; |
| left_padding *= sensor_binning; |
| right_padding *= sensor_binning; |
| top_padding *= sensor_binning; |
| #endif /*USE_WINDOWS_BINNING_FACTOR*/ |
| |
| /* during simulation, the used resolution can exceed the sensor |
| resolution, so we clip it. */ |
| input_width = min(input_width, in_table->sensor_width); |
| input_height = min(input_height, in_table->sensor_height); |
| |
| /* This prepare_shading_table() function is called only in legacy API (not in new API). |
| Then, the legacy shading table width and height should be used. */ |
| table_width = binary->sctbl_legacy_width_per_color; |
| table_height = binary->sctbl_legacy_height; |
| |
| result = ia_css_shading_table_alloc(table_width, table_height); |
| if (!result) { |
| *target_table = NULL; |
| return; |
| } |
| result->sensor_width = in_table->sensor_width; |
| result->sensor_height = in_table->sensor_height; |
| result->fraction_bits = in_table->fraction_bits; |
| |
| /* now we crop the original shading table and then interpolate to the |
| requested resolution and decimation factor. */ |
| for (i = 0; i < IA_CSS_SC_NUM_COLORS; i++) { |
| crop_and_interpolate(input_width, input_height, |
| left_padding, right_padding, top_padding, |
| in_table, |
| result, i); |
| } |
| *target_table = result; |
| } |
| |
| struct ia_css_shading_table * |
| ia_css_shading_table_alloc( |
| unsigned int width, |
| unsigned int height) |
| { |
| unsigned int i; |
| struct ia_css_shading_table *me; |
| |
| IA_CSS_ENTER(""); |
| |
| me = kmalloc(sizeof(*me), GFP_KERNEL); |
| if (!me) |
| return me; |
| |
| me->width = width; |
| me->height = height; |
| me->sensor_width = 0; |
| me->sensor_height = 0; |
| me->fraction_bits = 0; |
| for (i = 0; i < IA_CSS_SC_NUM_COLORS; i++) { |
| me->data[i] = |
| kvmalloc(width * height * sizeof(*me->data[0]), |
| GFP_KERNEL); |
| if (!me->data[i]) { |
| unsigned int j; |
| |
| for (j = 0; j < i; j++) { |
| kvfree(me->data[j]); |
| me->data[j] = NULL; |
| } |
| kfree(me); |
| return NULL; |
| } |
| } |
| |
| IA_CSS_LEAVE(""); |
| return me; |
| } |
| |
| void |
| ia_css_shading_table_free(struct ia_css_shading_table *table) |
| { |
| unsigned int i; |
| |
| if (!table) |
| return; |
| |
| /* We only output logging when the table is not NULL, otherwise |
| * logs will give the impression that a table was freed. |
| * */ |
| IA_CSS_ENTER(""); |
| |
| for (i = 0; i < IA_CSS_SC_NUM_COLORS; i++) { |
| if (table->data[i]) { |
| kvfree(table->data[i]); |
| table->data[i] = NULL; |
| } |
| } |
| kfree(table); |
| |
| IA_CSS_LEAVE(""); |
| } |