/* * 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 #include #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 != NULL); assert(out_table != NULL); 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, #ifndef ISP2401 const struct ia_css_binary *binary) #else unsigned int table_width, unsigned int table_height) #endif { /* initialize table with ones, shift becomes zero */ #ifndef ISP2401 unsigned int i, j, table_width, table_height; #else unsigned int i, j; #endif struct ia_css_shading_table *result; assert(target_table != NULL); #ifndef ISP2401 assert(binary != NULL); #endif #ifndef ISP2401 table_width = binary->sctbl_width_per_color; table_height = binary->sctbl_height; #endif result = ia_css_shading_table_alloc(table_width, table_height); if (result == NULL) { *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 != NULL); assert(binary != NULL); if (!in_table) { #ifndef ISP2401 sh_css_params_shading_id_table_generate(target_table, binary); #else sh_css_params_shading_id_table_generate(target_table, binary->sctbl_legacy_width_per_color, binary->sctbl_legacy_height); #endif 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); #ifndef ISP2401 table_width = binary->sctbl_width_per_color; table_height = binary->sctbl_height; #else /* 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; #endif result = ia_css_shading_table_alloc(table_width, table_height); if (result == NULL) { *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] = sh_css_malloc(width * height * sizeof(*me->data[0])); if (me->data[i] == NULL) { unsigned int j; for (j = 0; j < i; j++) { sh_css_free(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 == NULL) 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]) { sh_css_free(table->data[i]); table->data[i] = NULL; } } kfree(table); IA_CSS_LEAVE(""); }