diff options
Diffstat (limited to 'drivers/staging/media/atomisp/pci/sh_css_param_shading.c')
-rw-r--r-- | drivers/staging/media/atomisp/pci/sh_css_param_shading.c | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/drivers/staging/media/atomisp/pci/sh_css_param_shading.c b/drivers/staging/media/atomisp/pci/sh_css_param_shading.c new file mode 100644 index 0000000000..5b43cc6562 --- /dev/null +++ b/drivers/staging/media/atomisp/pci/sh_css_param_shading.c @@ -0,0 +1,392 @@ +// 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/math.h> +#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, i; + unsigned int left_padding, top_padding, left_cropping; + struct ia_css_shading_table *result; + struct u32_fract bds; + int right_padding; + + assert(target_table); + assert(binary); + + if (!in_table) { + sh_css_params_shading_id_table_generate(target_table, + binary->sctbl_width_per_color, + binary->sctbl_height); + return; + } + + /* + * We use the ISP input resolution for the shading table because + * shading correction is performed in the bayer domain (before bayer + * down scaling). + */ + 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_fract(bds_factor, &bds); + + 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; + + /* + * 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; + + /* + * 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_width_per_color; + table_height = binary->sctbl_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(""); +} |