diff options
| author | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
| commit | 8e4bff4cab0ddac6060645b0715210484d02ff40 (patch) | |
| tree | 3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/media/sprd_scale | |
Diffstat (limited to 'drivers/media/sprd_scale')
| -rw-r--r-- | drivers/media/sprd_scale/Makefile | 12 | ||||
| -rw-r--r-- | drivers/media/sprd_scale/common/compat_img_scale.c | 95 | ||||
| -rw-r--r-- | drivers/media/sprd_scale/common/compat_img_scale.h | 25 | ||||
| -rw-r--r-- | drivers/media/sprd_scale/common/gen_scale_coef.c | 644 | ||||
| -rw-r--r-- | drivers/media/sprd_scale/common/gen_scale_coef.h | 34 | ||||
| -rw-r--r-- | drivers/media/sprd_scale/common/img_scale.c | 388 | ||||
| -rw-r--r-- | drivers/media/sprd_scale/common/img_scale.h | 43 | ||||
| -rw-r--r-- | drivers/media/sprd_scale/common/sin_cos.c | 152 | ||||
| -rw-r--r-- | drivers/media/sprd_scale/common/sin_cos.h | 24 | ||||
| -rwxr-xr-x | drivers/media/sprd_scale/scale_drv.c | 770 | ||||
| -rw-r--r-- | drivers/media/sprd_scale/scale_drv.h | 69 | ||||
| -rwxr-xr-x | drivers/media/sprd_scale/scale_reg.h | 95 |
12 files changed, 2351 insertions, 0 deletions
diff --git a/drivers/media/sprd_scale/Makefile b/drivers/media/sprd_scale/Makefile new file mode 100644 index 00000000..e043b1ae --- /dev/null +++ b/drivers/media/sprd_scale/Makefile @@ -0,0 +1,12 @@ +ccflags-y += -Idrivers/media/sprd_scale/common -Idrivers/media/sprd_dcam/common + +ifeq ($(CONFIG_ARCH_SCX35),y) +ccflags-y += -Idrivers/media/sprd_scale +sprd_scale-objs := common/sin_cos.o common/gen_scale_coef.o common/img_scale.o scale_drv.o +ifeq ($(CONFIG_64BIT),y) +sprd_scale-objs += common/compat_img_scale.o +endif +obj-y += sprd_scale.o +endif + +include drivers/media/sprd_dcam/common/Makefile.inc diff --git a/drivers/media/sprd_scale/common/compat_img_scale.c b/drivers/media/sprd_scale/common/compat_img_scale.c new file mode 100644 index 00000000..f7b72a81 --- /dev/null +++ b/drivers/media/sprd_scale/common/compat_img_scale.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2012 Spreadtrum Communications Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#include <linux/compat.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <video/sprd_scale_k.h> +#include "img_scale.h" +#include "compat_img_scale.h" + +struct compat_scale_frame_param_t { + struct scale_size_t input_size; + struct scale_rect_t input_rect; + enum scale_fmt_e input_format; + struct scale_addr_t input_addr; + struct scale_endian_sel_t input_endian; + struct scale_size_t output_size; + enum scale_fmt_e output_format; + struct scale_addr_t output_addr; + struct scale_endian_sel_t output_endian; + enum scale_mode_e scale_mode; + uint32_t slice_height; + compat_caddr_t coeff_addr; +}; + +#define COMPAT_SCALE_IO_START _IOW(SCALE_IO_MAGIC, 0, struct compat_scale_frame_param_t) + +static long compat_get_scale_frame_param( + struct scale_frame_param_t *kp, + struct compat_scale_frame_param_t __user *up) +{ + compat_caddr_t tmp; + + if (!access_ok(VERIFY_READ, up, sizeof(struct compat_scale_frame_param_t)) || + copy_from_user(&kp->input_size, &up->input_size, sizeof(struct scale_size_t)) || + copy_from_user(&kp->input_rect, &up->input_rect, sizeof(struct scale_rect_t)) || + get_user(kp->input_format, &up->input_format) || + copy_from_user(&kp->input_addr, &up->input_addr, sizeof(struct scale_addr_t)) || + copy_from_user(&kp->input_endian, &up->input_endian, sizeof(struct scale_endian_sel_t)) || + copy_from_user(&kp->output_size, &up->output_size, sizeof(struct scale_size_t)) || + get_user(kp->output_format, &up->output_format) || + copy_from_user(&kp->output_addr, &up->output_addr, sizeof(struct scale_addr_t)) || + copy_from_user(&kp->output_endian, &up->output_endian, sizeof(struct scale_endian_sel_t)) || + get_user(kp->scale_mode, &up->scale_mode) || + get_user(kp->slice_height, &up->slice_height) || + get_user(tmp, &up->coeff_addr)) + return -EFAULT; + kp->coeff_addr = compat_ptr(tmp); + return 0; +} + +long compat_scale_k_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct scale_frame_param_t karg; + void __user *up = compat_ptr(arg); + int compatible_arg = 1; + long err = 0; + + if (!filp->f_op || !filp->f_op->unlocked_ioctl) + return -ENOTTY; + + switch (cmd) { + case COMPAT_SCALE_IO_START: + err = compat_get_scale_frame_param(&karg, up); + cmd = SCALE_IO_START; + compatible_arg = 0; + break; + + default: + break; + } + if (err) + return err; + + if (compatible_arg) + err = filp->f_op->unlocked_ioctl(filp, cmd, (unsigned long)up); + else { + mm_segment_t old_fs = get_fs(); + + set_fs(KERNEL_DS); + err = filp->f_op->unlocked_ioctl(filp, cmd, (unsigned long)&karg); + set_fs(old_fs); + } + return err; +} diff --git a/drivers/media/sprd_scale/common/compat_img_scale.h b/drivers/media/sprd_scale/common/compat_img_scale.h new file mode 100644 index 00000000..5e61b88d --- /dev/null +++ b/drivers/media/sprd_scale/common/compat_img_scale.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2012 Spreadtrum Communications Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ +#ifndef _COMPAT_IMG_SCALE_H_ +#define _COMPAT_IMG_SCALE_H_ + +#if IS_ENABLED(CONFIG_COMPAT) + +long compat_scale_k_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); + +#else + +#define compat_scale_k_ioctl NULL + +#endif /* CONFIG_COMPAT */ +#endif diff --git a/drivers/media/sprd_scale/common/gen_scale_coef.c b/drivers/media/sprd_scale/common/gen_scale_coef.c new file mode 100644 index 00000000..e2287766 --- /dev/null +++ b/drivers/media/sprd_scale/common/gen_scale_coef.c @@ -0,0 +1,644 @@ +/* + * Copyright (C) 2012 Spreadtrum Communications Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ +#include "sin_cos.h" +#include "gen_scale_coef.h" +#include <linux/mm.h> +#include <linux/math64.h> + +#define GSC_FIX 24 +#define GSC_COUNT 256 + +#define GSC_ABS(_a) ((_a) < 0 ? -(_a) : (_a)) +#define GSC_SIGN2(input, p) {if (p>=0) input = 1; if (p < 0) input = -1;} +#define COEF_ARR_ROWS 9 +#define COEF_ARR_COLUMNS 8 +#define COEF_ARR_COL_MAX 16 +#define MIN_POOL_SIZE (6 * 1024) +#define TRUE 1 +#define FALSE 0 +#define SCI_MEMSET memset +#define MAX( _x, _y ) ( ((_x) > (_y)) ? (_x) : (_y) ) + +typedef struct { + unsigned long begin_addr; + uint32_t total_size; + uint32_t used_size; +} GSC_MEM_POOL; + + +static uint8_t _InitPool(void *buffer_ptr, + uint32_t buffer_size, GSC_MEM_POOL * pool_ptr) +{ + if (NULL == buffer_ptr || 0 == buffer_size || NULL == pool_ptr) + return FALSE; + + if (buffer_size < MIN_POOL_SIZE) + return FALSE; + + pool_ptr->begin_addr = (unsigned long) buffer_ptr; + pool_ptr->total_size = buffer_size; + pool_ptr->used_size = 0; + return TRUE; +} + +static void *_Allocate(uint32_t size, + uint32_t align_shift, GSC_MEM_POOL * pool_ptr) +{ + unsigned long begin_addr = 0; + unsigned long temp_addr = 0; + + if (NULL == pool_ptr) + return NULL; + + begin_addr = pool_ptr->begin_addr; + temp_addr = begin_addr + pool_ptr->used_size; + temp_addr =((temp_addr + (1 << align_shift) -1) >> align_shift) << align_shift; + if (temp_addr + size > begin_addr + pool_ptr->total_size) + return NULL; + + pool_ptr->used_size = (temp_addr + size) - begin_addr; + SCI_MEMSET((void *)temp_addr, 0, size); + return (void *)temp_addr; +} + +s64 dcam_div64_s64(s64 dividend, s64 divisor, s64 *ret) +{ + s64 quot, t; + + quot = div64_u64(abs64(dividend), abs64(divisor)); + t = (dividend ^ divisor) >> 63; + + *ret = (quot ^ t) - t; + return div64_s64(dividend, divisor);; +} +static int64_t div64_s64_s64(int64_t dividend, int64_t divisor) +{ + int8_t sign = 1; + int64_t dividend_tmp = dividend; + int64_t divisor_tmp = divisor; + int64_t ret = 0; + + if (0 == divisor) + return 0; + + if ((dividend >> 63) & 0x1) { + sign *= -1; + dividend_tmp = dividend * (-1); + } + if ((divisor >> 63) & 0x1) { + sign *= -1; + divisor_tmp = divisor * (-1); + } + ret = div64_s64(dividend_tmp, divisor_tmp); + + ret *= sign; + return ret; +} + +static void normalize_inter(int64_t * data, int16_t * int_data, uint8_t ilen) +{ + uint8_t it; + int64_t tmp_d = 0; + int64_t *tmp_data = NULL; + int64_t tmp_sum_val = 0; + tmp_data = data; + tmp_sum_val = 0; + for (it = 0; it < ilen; it++) { + tmp_sum_val += tmp_data[it]; + } + if (0 == tmp_sum_val) { + uint8_t value = 256 / ilen; + for (it = 0; it < ilen; it++) { + tmp_d = value; + int_data[it] = (int16_t) tmp_d; + } + } else { + for (it = 0; it < ilen; it++) { + tmp_d = + div64_s64_s64(tmp_data[it] * (int64_t) 256,tmp_sum_val); + int_data[it] = (uint16_t) tmp_d; + } + } +} + +static int16_t sum_fun(int16_t * data, int8_t ilen) +{ + int8_t i; + int16_t tmp_sum; + tmp_sum = 0; + + for (i = 0; i < ilen; i++) + tmp_sum += *data++; + return tmp_sum; +} + +static void adjust_filter_inter(int16_t * filter, uint8_t ilen) +{ + int32_t i, midi; + int32_t tmpi, tmp_S; + int32_t tmp_val = 0; + + tmpi = sum_fun(filter, ilen) - 256; + midi = ilen >> 1; + GSC_SIGN2(tmp_val, tmpi); + + if ((tmpi & 1) == 1) { + filter[midi] = filter[midi] - tmp_val; + tmpi -= tmp_val; + } + tmp_S = GSC_ABS(tmpi / 2); + if ((ilen & 1) == 1) { + for (i = 0; i < tmp_S; i++) { + filter[midi - (i + 1)] = + filter[midi - (i + 1)] - tmp_val; + filter[midi + (i + 1)] = + filter[midi + (i + 1)] - tmp_val; + } + } else { + for (i = 0; i < tmp_S; i++) { + filter[midi - (i + 1)] = + filter[midi - (i + 1)] - tmp_val; + filter[midi + i] = filter[midi + i] - tmp_val; + } + } + if (filter[midi] > 255) { + tmp_val = filter[midi]; + filter[midi] = 255; + filter[midi - 1] = filter[midi - 1] + tmp_val - 255; + } +} + +static int16_t CalYmodelCoef(int16_t coef_lenght, + int16_t * coef_data_ptr, + int16_t N, int16_t M, GSC_MEM_POOL * pool_ptr) +{ + int8_t mount; + int16_t i, mid_i, kk, j, sum_val; + int64_t *filter = _Allocate(GSC_COUNT * sizeof(int64_t), 3, pool_ptr); + int64_t *tmp_filter = + _Allocate(GSC_COUNT * sizeof(int64_t), 3, pool_ptr); + int16_t *normal_filter = + _Allocate(GSC_COUNT * sizeof(int16_t), 2, pool_ptr); + + if (NULL == filter || NULL == tmp_filter || NULL == normal_filter) + return 1; + + mid_i = coef_lenght >> 1; + filter[mid_i] = + div64_s64_s64((int64_t) ((int64_t) N << GSC_FIX), (int64_t) MAX(M, N)); + for (i = 0; i < mid_i; i++) { + int64_t angle_x = + div64_s64_s64(ARC_32_COEF * (int64_t) (i + 1) * + (int64_t) N, (int64_t) MAX(M,N) * (int64_t) 8); + int64_t angle_y = + div64_s64_s64(ARC_32_COEF * (int64_t) (i + 1) * + (int64_t) N, (int64_t) (M * N) * (int64_t) 8); + int32_t value_x = sin_32((int32_t) angle_x); + int32_t value_y = sin_32((int32_t) angle_y); + filter[mid_i + i + 1] = + div64_s64_s64((int64_t) + ((int64_t) value_x * + (int64_t) (1 << GSC_FIX)), + (int64_t) ((int64_t) M * (int64_t) value_y)); + filter[mid_i - (i + 1)] = filter[mid_i + i + 1]; + } + for (i = -1; i < mid_i; i++) { + int32_t angle_32 = + (int32_t)div64_s64_s64((int64_t) + ((int64_t) 2 * (int64_t) (mid_i - i - 1) * + ARC_32_COEF), + (int64_t) coef_lenght); + int64_t a = (int64_t) 9059697; + int64_t b = (int64_t) 7717519; + int64_t t = a - ((b * cos_32(angle_32)) >> 30); + filter[mid_i + i + 1] = (t * filter[mid_i + i + 1]) >> GSC_FIX; + filter[mid_i - (i + 1)] = filter[mid_i + i + 1]; + } + for (i = 0; i < 8; i++) { + mount = 0; + for (j = i; j < coef_lenght; j += 8) { + tmp_filter[mount] = filter[j]; + mount++; + } + normalize_inter(tmp_filter, normal_filter, (int8_t) mount); + sum_val = sum_fun(normal_filter, mount); + if (256 != sum_val) { + adjust_filter_inter(normal_filter, mount); + } + mount = 0; + for (kk = i; kk < coef_lenght; kk += 8) { + coef_data_ptr[kk] = normal_filter[mount]; + mount++; + } + } + return 0; +} + +static int16_t CalY_ScalingCoef(int16_t tap, + int16_t D, + int16_t I, + int16_t * y_coef_data_ptr, + int16_t dir, GSC_MEM_POOL * pool_ptr) +{ + uint16_t coef_lenght; + + coef_lenght = (uint16_t) (tap * 8); + SCI_MEMSET(y_coef_data_ptr, 0, coef_lenght * sizeof(int16_t)); + CalYmodelCoef(coef_lenght, y_coef_data_ptr, I, D, pool_ptr); + return coef_lenght; +} + +static int16_t CalUV_ScalingCoef(int16_t tap, + int16_t D, + int16_t I, + int16_t * uv_coef_data_ptr, + int16_t dir, GSC_MEM_POOL * pool_ptr) +{ + int16_t uv_coef_lenght; + + if ((dir == 1)) { + uv_coef_lenght = (int16_t) (tap * 8); + CalYmodelCoef(uv_coef_lenght, uv_coef_data_ptr, I, D, pool_ptr); + } else { + if (D > I) { + uv_coef_lenght = (int16_t) (tap * 8); + } else { + uv_coef_lenght = (int16_t) (2 * 8); + } + CalYmodelCoef(uv_coef_lenght, uv_coef_data_ptr, I, D, pool_ptr); + } + return uv_coef_lenght; +} + +static void GetFilter(int16_t * coef_data_ptr, + int16_t * out_filter, + int16_t iI_hor, int16_t coef_len, int16_t * filter_len) +{ + int16_t i, pos_start; + + pos_start = coef_len / 2; + while (pos_start >= iI_hor) { + pos_start -= iI_hor; + } + for (i = 0; i < iI_hor; i++) { + int16_t len = 0; + int16_t j; + int16_t pos = pos_start + i; + while (pos >= iI_hor) { + pos -= iI_hor; + } + for (j = 0; j < coef_len; j += iI_hor) { + *out_filter++ = coef_data_ptr[j + pos]; + len++; + } + *filter_len++ = len; + } +} + +static void WriteScalarCoef(int16_t * dst_coef_ptr, + int16_t * coef_ptr, int16_t dst_pitch, int16_t src_pitch) +{ + int i, j; + + for (i = 0; i < 8; i++) { + for (j = 0; j < src_pitch; j++) { + *(dst_coef_ptr + j) = + *(coef_ptr + i * src_pitch + src_pitch - 1 - j); + } + dst_coef_ptr += dst_pitch; + } +} + +static void SetHorRegisterCoef(uint32_t * reg_coef_ptr, int16_t * y_coef_ptr, + int16_t * uv_coef_ptr) +{ + int32_t i = 0; + int16_t *y_coef_arr[COEF_ARR_ROWS] = { NULL }; + int16_t *uv_coef_arr[COEF_ARR_ROWS] = { NULL }; + + for (i = 0; i < COEF_ARR_ROWS; i++) { + y_coef_arr[i] = y_coef_ptr; + uv_coef_arr[i] = uv_coef_ptr; + y_coef_ptr += COEF_ARR_COLUMNS; + uv_coef_ptr += COEF_ARR_COLUMNS; + } + + /*horizontal Y Scaling Coef Config register*/ + for (i = 0; i < 8; i++) { + uint16_t p0, p1; + uint32_t reg; + + p0 = (uint16_t) y_coef_arr[i][7]; + p1 = (uint16_t) y_coef_arr[i][6]; + reg = ((p0 & 0x1ff)) | ((p1 & 0x1ff) << 9); + *reg_coef_ptr++ = reg; + p0 = (uint16_t) y_coef_arr[i][5]; + p1 = (uint16_t) y_coef_arr[i][4]; + reg = ((p0 & 0x1ff)) | ((p1 & 0x1ff) << 9); + *reg_coef_ptr++ = reg; + p0 = (uint16_t) y_coef_arr[i][3]; + p1 = (uint16_t) y_coef_arr[i][2]; + reg = ((p0 & 0x1ff)) | ((p1 & 0x1ff) << 9); + *reg_coef_ptr++ = reg; + p0 = (uint16_t) y_coef_arr[i][1]; + p1 = (uint16_t) y_coef_arr[i][0]; + reg = ((p0 & 0x1ff)) | ((p1 & 0x1ff) << 9); + *reg_coef_ptr++ = reg; + } + + /*horizontal UV Scaling Coef Config register*/ + for (i = 0; i < 8; i++) { + uint16_t p0, p1; + uint32_t reg; + + p0 = (uint16_t) uv_coef_arr[i][3]; + p1 = (uint16_t) uv_coef_arr[i][2]; + reg = ((p0 & 0x1ff)) | ((p1 & 0x1ff) << 9); + *reg_coef_ptr++ = reg; + p0 = (uint16_t) uv_coef_arr[i][1]; + p1 = (uint16_t) uv_coef_arr[i][0]; + reg = ((p0 & 0x1ff)) | ((p1 & 0x1ff) << 9); + *reg_coef_ptr++ = reg; + } +} + +static void SetVerRegisterCoef(uint32_t * reg_coef_lum_ptr, + uint32_t *reg_coef_ch_ptr, + int16_t * y_coef_ptr, + int16_t * uv_coef_ptr, + int16_t i_h, + int16_t o_h, + uint8_t is_scaling2yuv420) +{ + uint32_t cnts = 0; + uint32_t i = 0, j = 0; + + if(2 * o_h <= i_h) { + for(i = 0; i < 9; i++) { + for(j = 0; j < 16; j++) { + reg_coef_lum_ptr[cnts++] = *(y_coef_ptr +i * 16 + j); + if(SCALER_COEF_TAB_LEN_VER == cnts) + break; + } + } + + cnts = 0; + for(i = 0; i < 9; i++) { + for(j = 0; j < 16; j++) { + reg_coef_ch_ptr[cnts++] = *(uv_coef_ptr + i * 16 +j); + if(SCALER_COEF_TAB_LEN_VER == cnts) + break; + } + } + } else { + for(i = 0; i < 8; i++) + for(j = 0; j < 4; j++) + reg_coef_lum_ptr[cnts++] = *(y_coef_ptr +i * 16 + j); + + cnts = 0; + if((o_h <= i_h) && is_scaling2yuv420) { + for(i = 0; i < 9; i++) { + for(j = 0; j < 16; j++) { + reg_coef_ch_ptr[cnts++] = *(uv_coef_ptr +i * 16 + j); + if(SCALER_COEF_TAB_LEN_VER == cnts) + break; + } + } + } else { + for(i = 0; i < 8; i++) + for(j = 0; j < 4; j++) + reg_coef_ch_ptr[cnts++] = *(uv_coef_ptr +i * 16 + j); + } + } +} + +static void CheckCoefRange(int16_t * coef_ptr, int16_t rows, int16_t columns, int16_t pitch) +{ + int16_t i, j; + int16_t value, diff, sign; + int16_t *coef_arr[COEF_ARR_ROWS] = { NULL }; + + for (i = 0; i < COEF_ARR_ROWS; i++) { + coef_arr[i] = coef_ptr; + coef_ptr += pitch; + } + for (i = 0; i < rows; i++) { + for (j = 0; j < columns; j++) { + value = coef_arr[i][j]; + if (value > 255) { + diff = value - 255; + coef_arr[i][j] = 255; + sign = GSC_ABS(diff); + if ((sign & 1) == 1) { + coef_arr[i][j + 1] = + coef_arr[i][j + 1] + (diff + 1) / 2; + coef_arr[i][j - 1] = + coef_arr[i][j - 1] + (diff - 1) / 2; + } else { + coef_arr[i][j + 1] = + coef_arr[i][j + 1] + (diff) / 2; + coef_arr[i][j - 1] = + coef_arr[i][j - 1] + (diff) / 2; + } + } + } + } +} + +static void CalcVerEdgeCoef(int16_t * coeff_ptr, int16_t D, int16_t I, + int16_t tap, int16_t pitch) +{ + int32_t phase_temp[9]; + int32_t acc = 0; + int32_t i_sample_cnt = 0; + uint8_t phase = 0; + uint8_t spec_tap = 0; + int32_t l; + int16_t i, j; + int16_t *coeff_arr[COEF_ARR_ROWS]; + + for (i = 0; i < COEF_ARR_ROWS; i++) { + coeff_arr[i] = coeff_ptr; + coeff_ptr += pitch; + } + for (j = 0; j <= 8; j++) + phase_temp[j] = j * I / 8; + + for (i = 0; i < I; i++) { + spec_tap = i & 1; + while (acc >= I) { + acc -= I; + i_sample_cnt++; + } + + for (j = 0; j < 8; j++) { + if (acc >= phase_temp[j] && acc < phase_temp[j + 1]) { + phase = (uint8_t) j; + break; + } + } + + { + int32_t j; + for (j = (1 - tap / 2); j <= (tap / 2); j++) { + l = i_sample_cnt + j; + if (l <= 0) { + coeff_arr[8][spec_tap] += + coeff_arr[phase][j + tap / 2 - 1]; + } else { + if (l >= D - 1) { + coeff_arr[8][spec_tap + 2] += + coeff_arr[phase][j +tap / 2 -1]; + } + } + } + } + acc += D; + } +} + +uint8_t GenScaleCoeff(int16_t i_w, + int16_t i_h, + int16_t o_w, + int16_t o_h, + uint32_t *coeff_h_ptr, + uint32_t *coeff_v_lum_ptr, + uint32_t *coeff_v_ch_ptr, + uint8_t scaling2yuv420, + uint8_t *scaler_tap, + uint8_t *chrome_tap, + void *temp_buf_ptr, + uint32_t temp_buf_size + ) +{ + int16_t D_hor = i_w; + int16_t D_ver = i_h; + int16_t I_hor = o_w; + int16_t I_ver = o_h; + int16_t I_ver_bak_uv = o_h; + int16_t I_ver_bak_y = o_h; + int16_t *cong_Ycom_hor = 0; + int16_t *cong_UVcom_hor = 0; + int16_t *cong_Ycom_ver = 0; + int16_t *cong_UVcom_ver = 0; + + uint16_t luma_ver_tap, chrome_ver_tap; + uint16_t luma_ver_maxtap = 16, chrome_ver_maxtap = 16; + + uint32_t coef_buf_size = 0; + int16_t *temp_filter_ptr = NULL; + int16_t *filter_ptr = NULL; + uint32_t filter_buf_size = GSC_COUNT * sizeof(int16_t); + int16_t filter_len[COEF_ARR_ROWS] = { 0 }; + int16_t coef_len = 0; + GSC_MEM_POOL pool = { 0 }; + + if (0 == i_w || 0 == i_h || 0 == o_w || (0 == o_h) \ + || (NULL == coeff_h_ptr) || (NULL == coeff_v_lum_ptr) \ + || (NULL == coeff_v_ch_ptr) || (NULL == scaler_tap) \ + || (NULL == chrome_tap) || (NULL == temp_buf_ptr)) { + + printk("GenScaleCoeff: i_w: %d, i_h: %d, o_w:%d, o_h: %d, coef_h:%p, coef_v_lum:%p, \ + coef_v_chr: %p, y_tap: %p, ch_tap: %p, tmp_buf:%p", + i_w, i_h, o_w, o_h, coeff_h_ptr, coeff_v_lum_ptr, \ + coeff_v_ch_ptr, scaler_tap, chrome_tap, temp_buf_ptr); + + return FALSE; + } + + /* init pool and allocate static array */ + if (!_InitPool(temp_buf_ptr, temp_buf_size, &pool)) + return FALSE; + + coef_buf_size = COEF_ARR_ROWS * COEF_ARR_COL_MAX * sizeof(int16_t); + cong_Ycom_hor = (int16_t*)_Allocate(coef_buf_size, 2, &pool); + cong_UVcom_hor = (int16_t*)_Allocate(coef_buf_size, 2, &pool); + cong_Ycom_ver = (int16_t*)_Allocate(coef_buf_size, 2, &pool); + cong_UVcom_ver = (int16_t*)_Allocate(coef_buf_size, 2, &pool); + + if (NULL == cong_Ycom_hor + || NULL == cong_UVcom_hor + ||(NULL == cong_Ycom_ver) + ||(NULL == cong_UVcom_ver)) + return FALSE; + + temp_filter_ptr = _Allocate(filter_buf_size, 2, &pool); + filter_ptr = _Allocate(filter_buf_size, 2, &pool); + if (NULL == temp_filter_ptr || NULL == filter_ptr) + return FALSE; + + + /* calculate coefficients of Y component in horizontal direction */ + coef_len = CalY_ScalingCoef(8, D_hor, I_hor, temp_filter_ptr, 1, &pool); + GetFilter(temp_filter_ptr, filter_ptr, 8, coef_len, filter_len); + WriteScalarCoef(cong_Ycom_hor, filter_ptr, 8, 8); + CheckCoefRange(cong_Ycom_hor, 8, 8, 8); + + /* calculate coefficients of UV component in horizontal direction */ + coef_len = CalUV_ScalingCoef(4, D_hor, I_hor, temp_filter_ptr, 1, &pool); + GetFilter(temp_filter_ptr, filter_ptr, 8, coef_len, filter_len); + WriteScalarCoef(cong_UVcom_hor, filter_ptr, 8, 4); + CheckCoefRange(cong_UVcom_hor, 8, 4, 8); + /* write the coefficient to register format */ + SetHorRegisterCoef(coeff_h_ptr, cong_Ycom_hor, cong_UVcom_hor); + + luma_ver_tap = ((uint8_t)(D_ver / I_ver)) * 2; + chrome_ver_tap = luma_ver_tap; + + if ( luma_ver_tap > luma_ver_maxtap) + luma_ver_tap = luma_ver_maxtap; + if(luma_ver_tap <= 2) + luma_ver_tap = 4; + + *scaler_tap = (uint8_t)luma_ver_tap; + + /* calculate coefficients of Y component in vertical direction*/ + coef_len = CalY_ScalingCoef(luma_ver_tap, D_ver, I_ver, temp_filter_ptr, 0, &pool); + GetFilter(temp_filter_ptr, filter_ptr, 8, coef_len, filter_len); + WriteScalarCoef(cong_Ycom_ver, filter_ptr, 16, filter_len[0]); + CheckCoefRange(cong_Ycom_ver, 8, luma_ver_tap, 16); + /* calculate coefficients of UV component in vertical direction */ + if (scaling2yuv420) { + I_ver_bak_uv /= 2; + chrome_ver_tap *= 2; + chrome_ver_maxtap = 16; + } + + if ( chrome_ver_tap > chrome_ver_maxtap) + chrome_ver_tap = chrome_ver_maxtap; + if(chrome_ver_tap <= 2) + chrome_ver_tap = 4; + *chrome_tap = (uint8_t)chrome_ver_tap; + + coef_len = CalUV_ScalingCoef((int16_t) (chrome_ver_tap), + D_ver, I_ver_bak_uv, temp_filter_ptr, + 0, &pool); + GetFilter(temp_filter_ptr, filter_ptr, 8, coef_len, filter_len); + WriteScalarCoef(cong_UVcom_ver, filter_ptr, 16, filter_len[0]); + CheckCoefRange(cong_UVcom_ver, 8, chrome_ver_tap, 16); + + /* calculate edge coefficients of Y component in vertical direction */ + if(2 * I_ver_bak_y <= D_ver) { //only scale down + CalcVerEdgeCoef(cong_Ycom_ver, D_ver, I_ver_bak_y, luma_ver_tap, 16); + } + /* calculate edge coefficients of UV component in vertical direction */ + if(2 * I_ver_bak_uv <= D_ver) { //only scale down + CalcVerEdgeCoef(cong_UVcom_ver, D_ver, I_ver_bak_uv, chrome_ver_tap, 16); + } + /* write the coefficient to register format */ + SetVerRegisterCoef(coeff_v_lum_ptr, coeff_v_ch_ptr, + cong_Ycom_ver, cong_UVcom_ver, + D_ver, I_ver, scaling2yuv420); + + return TRUE; +} diff --git a/drivers/media/sprd_scale/common/gen_scale_coef.h b/drivers/media/sprd_scale/common/gen_scale_coef.h new file mode 100644 index 00000000..f8518d68 --- /dev/null +++ b/drivers/media/sprd_scale/common/gen_scale_coef.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2012 Spreadtrum Communications Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ +#ifndef _GEN_SCALE_COEF_H_ +#define _GEN_SCALE_COEF_H_ + +#include <linux/types.h> + +#define SCALER_COEF_TAB_LEN_HOR 48 +#define SCALER_COEF_TAB_LEN_VER 132 + +uint8_t GenScaleCoeff(int16_t i_w, + int16_t i_h, + int16_t o_w, + int16_t o_h, + uint32_t *coeff_h_ptr, + uint32_t *coeff_v_lum_ptr, + uint32_t *coeff_v_ch_ptr, + uint8_t scaling2yuv420, + uint8_t *scaler_tap, + uint8_t *chrome_tap, + void *temp_buf_ptr, + uint32_t temp_buf_size); + +#endif diff --git a/drivers/media/sprd_scale/common/img_scale.c b/drivers/media/sprd_scale/common/img_scale.c new file mode 100644 index 00000000..0fa976a2 --- /dev/null +++ b/drivers/media/sprd_scale/common/img_scale.c @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2012 Spreadtrum Communications Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/miscdevice.h> +#include <linux/platform_device.h> +#include <linux/proc_fs.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <asm/uaccess.h> +#include <video/sprd_scale_k.h> +#include <linux/kthread.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/vmalloc.h> + +#include "img_scale.h" +#include "compat_img_scale.h" +#include "dcam_drv.h" + +#ifndef CONFIG_64BIT +#include <soc/sprd/hardware.h> +#endif + +#define SCALE_DEVICE_NAME "sprd_scale" +#define SCALE_TIMEOUT 5000/*ms*/ +#define SCALE_MINOR MISC_DYNAMIC_MINOR +#define SC_COEFF_BUF_SIZE (24 << 10) + +static void scale_k_irq(void *fd) +{ + struct scale_k_file *scale_file = (struct scale_k_file *)fd; + + if (!scale_file) { + printk("scale_k_irq error: hand is null"); + return; + } + + printk("sc done.\n"); + + up(&scale_file->scale_done_sem); +} + +static void scale_k_file_init(struct scale_k_file *fd, struct scale_k_private *scale_private) +{ + fd->scale_private = scale_private; + + sema_init(&fd->scale_done_sem, 0); + + fd->drv_private.scale_fd = (void*)fd; + fd->drv_private.path_info.coeff_addr = scale_private->coeff_addr; + + spin_lock_init(&fd->drv_private.scale_drv_lock); + sema_init(&fd->drv_private.path_info.done_sem, 0); +} + +static int scale_k_open(struct inode *node, struct file *file) +{ + int ret = 0; + struct scale_k_file *fd = NULL; + struct scale_k_private *scale_private = NULL; + struct miscdevice *md = file->private_data; + + if (!md) { + ret = -EFAULT; + printk("scale_k_open error: miscdevice is null \n"); + goto exit; + } + scale_private = (struct scale_k_private *)md->this_device->platform_data; + if (!scale_private) { + ret = -EFAULT; + printk("scale_k_open error: scale_private is null \n"); + goto exit; + } + + fd = vzalloc(sizeof(*fd)); + if (!fd) { + ret = -ENOMEM; + printk("scale_k_open error: alloc \n"); + goto exit; + } + fd->dn = md->this_device->of_node; + scale_k_file_init(fd, scale_private); + + file->private_data = fd; + + SCALE_TRACE("scale_k_open fd=%p ret=%d\n", fd, ret); + +exit: + return ret; +} + +ssize_t scale_k_read(struct file *file, char __user *u_data, size_t cnt, loff_t *cnt_ret) +{ + uint32_t rt_word[2]; + + (void)file; (void)cnt; (void)cnt_ret; + + if (cnt < sizeof(uint32_t)) { + printk("scale_k_read error: wrong size of u_data: %ld \n", cnt); + return -1; + } + + rt_word[0] = SCALE_LINE_BUF_LENGTH; + rt_word[1] = SCALE_SC_COEFF_MAX; + + return copy_to_user(u_data, (void*)rt_word, (uint32_t)(2 * sizeof(uint32_t))); +} + +static int scale_k_release(struct inode *node, struct file *file) +{ + struct scale_k_file *fd = NULL; + struct scale_k_private *scale_private = NULL; + + fd = file->private_data; + if (!fd) { + goto exit; + } + + scale_private = fd->scale_private; + if (!scale_private) { + goto fd_free; + } + + down(&scale_private->start_sem); + up(&scale_private->start_sem); + +fd_free: + vfree(fd); + fd = NULL; + file->private_data = NULL; + +exit: + SCALE_TRACE("scale_k_release\n"); + + return 0; +} + +static void sprd_img_print_reg(void) +{ + uint32_t* reg_buf = NULL; + uint32_t reg_buf_len = 0x400; + int ret; + uint32_t print_len = 0, print_cnt = 0; + reg_buf = (uint32_t*)vzalloc(reg_buf_len); + if (NULL == reg_buf) + return; + ret = dcam_read_registers(reg_buf, ®_buf_len); + if (ret) { + vfree(reg_buf); + return; + } + printk("dcam registers \n"); + while (print_len < reg_buf_len) { + printk("offset 0x%03x : 0x%08x, 0x%08x, 0x%08x, 0x%08x \n", + print_len, + reg_buf[print_cnt], + reg_buf[print_cnt+1], + reg_buf[print_cnt+2], + reg_buf[print_cnt+3]); + print_cnt += 4; + print_len += 16; + } + udelay(1); + vfree(reg_buf); + return; +} +static long scale_k_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct scale_k_private *scale_private; + struct scale_k_file *fd; + struct scale_frame_param_t frame_params; + struct scale_slice_param_t slice_params; + + fd = file->private_data; + if (!fd) { + ret = - EFAULT; + printk("scale_k_ioctl error: fd null \n"); + goto ioctl_exit; + } + + scale_private = fd->scale_private; + if (!scale_private) { + ret = -EFAULT; + printk("scale_k_ioctl erro: scale private null \n"); + goto ioctl_exit; + } + + switch (cmd) { + case SCALE_IO_START: + + down(&scale_private->start_sem); + + ret = scale_k_module_en(fd->dn); + if (unlikely(ret)) { + printk("rot_k_ioctl erro: scale_module_en\n"); + up(&scale_private->start_sem); + goto ioctl_exit; + } + + ret = scale_k_isr_reg(scale_k_irq, &fd->drv_private); + if (unlikely(ret)) { + printk("rot_k_ioctl error: scale_k_isr_reg\n"); + scale_k_module_dis(fd->dn); + up(&scale_private->start_sem); + goto ioctl_exit; + + } + + ret = copy_from_user(&frame_params, (struct scale_frame_param_t *)arg, sizeof(frame_params)); + if (ret) { + printk("rot_k_ioctl error: get frame param info \n"); + scale_k_module_dis(fd->dn); + up(&scale_private->start_sem); + goto ioctl_exit; + } + + ret = scale_k_start(&frame_params, &fd->drv_private.path_info); + if (ret) { + printk("rot_k_ioctl error: frame start \n"); + scale_k_module_dis(fd->dn); + up(&scale_private->start_sem); + goto ioctl_exit; + } + + break; + case SCALE_IO_DONE: + ret = down_timeout(&fd->scale_done_sem, msecs_to_jiffies(SCALE_TIMEOUT)); + if (ret) { + printk("scale_k_ioctl error: interruptible time out\n"); + sprd_img_print_reg(); + goto ioctl_out; + } + + scale_k_stop(); + + scale_k_module_dis(fd->dn); + + up(&scale_private->start_sem); + break; + case SCALE_IO_CONTINUE: + /*Caution: slice scale is not supported by current driver.Please do not use it*/ + ret = copy_from_user(&slice_params, (struct scale_slice_param_t *)arg, sizeof(slice_params)); + if (ret) { + printk("rot_k_ioctl error: get slice param info \n"); + goto ioctl_exit; + } + + ret = scale_k_continue(&slice_params, &fd->drv_private.path_info); + if (ret) { + printk("rot_k_ioctl error: continue \n"); + } + break; + + default: + break; + } + +ioctl_exit: + return ret; + +ioctl_out: + dcam_resize_end(); + scale_k_stop(); + scale_k_module_dis(fd->dn); + up(&scale_private->start_sem); + return ret; +} + +static struct file_operations scale_fops = { + .owner = THIS_MODULE, + .open = scale_k_open, + .read = scale_k_read, + .unlocked_ioctl = scale_k_ioctl, + .compat_ioctl = compat_scale_k_ioctl, + .release = scale_k_release, +}; + +static struct miscdevice scale_dev = { + .minor = SCALE_MINOR, + .name = SCALE_DEVICE_NAME, + .fops = &scale_fops, +}; + +int scale_k_probe(struct platform_device *pdev) +{ + int ret; + struct scale_k_private *scale_private; + + scale_private = devm_kzalloc(&pdev->dev, sizeof(*scale_private), GFP_KERNEL); + if (!scale_private) { + return -ENOMEM; + } + scale_private->coeff_addr = (void *)vzalloc(SC_COEFF_BUF_SIZE); + if (!scale_private->coeff_addr) { + devm_kfree(&pdev->dev, scale_private); + return -ENOMEM; + } + sema_init(&scale_private->start_sem, 1); + + platform_set_drvdata(pdev, scale_private); + + ret = misc_register(&scale_dev); + if (ret) { + printk("scale_k_probe error: ret=%d\n", ret); + ret = -EACCES; + goto probe_out; + } + + scale_dev.this_device->of_node = pdev->dev.of_node; + scale_dev.this_device->platform_data = (void *)scale_private; + + goto exit; + +probe_out: + vfree(scale_private->coeff_addr); + devm_kfree(&pdev->dev, scale_private); + platform_set_drvdata(pdev, NULL); +exit: + return 0; +} + +static int scale_k_remove(struct platform_device *pdev) +{ + struct scale_k_private *scale_private; + + scale_private = platform_get_drvdata(pdev); + + if (!scale_private) + goto remove_exit; + + misc_deregister(&scale_dev); + if (scale_private->coeff_addr) { + vfree(scale_private->coeff_addr); + } + devm_kfree(&pdev->dev, scale_private); + platform_set_drvdata(pdev, NULL); + +remove_exit: + return 0; +} + +static const struct of_device_id of_match_table_scale[] = { + { .compatible = "sprd,sprd_scale", }, + { }, +}; + +static struct platform_driver scale_driver = +{ + .probe = scale_k_probe, + .remove = scale_k_remove, + .driver = { + .owner = THIS_MODULE, + .name = SCALE_DEVICE_NAME, + .of_match_table = of_match_ptr(of_match_table_scale), + } +}; + +int __init scale_k_init(void) +{ + if (platform_driver_register(&scale_driver) != 0) { + printk("platform scale device register Failed \n"); + return -1; + } + + return 0; +} + +void scale_k_exit(void) +{ + platform_driver_unregister(&scale_driver); +} + +module_init(scale_k_init); +module_exit(scale_k_exit); +MODULE_DESCRIPTION("Sprd Scale Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/sprd_scale/common/img_scale.h b/drivers/media/sprd_scale/common/img_scale.h new file mode 100644 index 00000000..6df49a0a --- /dev/null +++ b/drivers/media/sprd_scale/common/img_scale.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2012 Spreadtrum Communications Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ +#ifndef _IMG_SCALE_H_ +#define _IMG_SCALE_H_ + +#include "scale_drv.h" + +#include <asm/ioctl.h> +#include <linux/types.h> + +struct scale_k_private{ + struct semaphore start_sem; + void *coeff_addr; +}; + +enum scale_k_status{ + SCALE_K_IDLE, + SCALE_K_RUNNING, + SCALE_K_DONE, + SCALE_K_MAX +}; + +struct scale_k_file{ + struct scale_k_private *scale_private; + + struct semaphore scale_done_sem; + + struct scale_drv_private drv_private; + + struct device_node *dn; +}; + +#endif diff --git a/drivers/media/sprd_scale/common/sin_cos.c b/drivers/media/sprd_scale/common/sin_cos.c new file mode 100644 index 00000000..57828254 --- /dev/null +++ b/drivers/media/sprd_scale/common/sin_cos.c @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2012 Spreadtrum Communications Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ +#include "sin_cos.h" +#define SINCOS_BIT_31 (1 << 31) +#define SINCOS_BIT_30 (1 << 30) +#define SMULL(x, y) ((int64_t)(x) * (int64_t)(y)) + +static const uint32_t cossin_tbl[64] = { + 0x3ffec42d, 0x00c90e90, 0x3ff4e5e0, 0x025b0caf, + 0x3fe12acb, 0x03ecadcf, 0x3fc395f9, 0x057db403, + 0x3f9c2bfb, 0x070de172, 0x3f6af2e3, 0x089cf867, + 0x3f2ff24a, 0x0a2abb59, 0x3eeb3347, 0x0bb6ecef, + 0x3e9cc076, 0x0d415013, 0x3e44a5ef, 0x0ec9a7f3, + 0x3de2f148, 0x104fb80e, 0x3d77b192, 0x11d3443f, + 0x3d02f757, 0x135410c3, 0x3c84d496, 0x14d1e242, + 0x3bfd5cc4, 0x164c7ddd, 0x3b6ca4c4, 0x17c3a931, + 0x3ad2c2e8, 0x19372a64, 0x3a2fcee8, 0x1aa6c82b, + 0x3983e1e8, 0x1c1249d8, 0x38cf1669, 0x1d79775c, + 0x3811884d, 0x1edc1953, 0x374b54ce, 0x2039f90f, + 0x367c9a7e, 0x2192e09b, 0x35a5793c, 0x22e69ac8, + 0x34c61236, 0x2434f332, 0x33de87de, 0x257db64c, + 0x32eefdea, 0x26c0b162, 0x31f79948, 0x27fdb2a7, + 0x30f8801f, 0x29348937, 0x2ff1d9c7, 0x2a650525, + 0x2ee3cebe, 0x2b8ef77d, 0x2dce88aa, 0x2cb2324c +}; + +static int32_t sin_core(int32_t arc_q33, int32_t sign) +{ + int32_t a, b, B; + int32_t sin_a, cos_a; + int32_t S1, S2, S3, S4, T1; + int32_t R = 0; + int32_t C1 = 0x6487ed51; + int32_t C2 = -715827882; + + a = arc_q33 >> 25; + cos_a = cossin_tbl[2 * a]; + sin_a = cossin_tbl[2 * a + 1]; + cos_a ^= (sign >> 31); + sin_a ^= (sign >> 31); + a = a << 25; + b = arc_q33 - a; + b -= (1 << 24); + B = SMULL((b << 3), C1) >> 32; + T1 = SMULL(B, B) >> 32; + S1 = sin_a; + S2 = SMULL(sin_a, T1 >> 1) >> 32; + S3 = SMULL(cos_a, B) >> 32; + S4 = SMULL(T1, C2) >> 32; + S4 = SMULL(S3, S4) >> 32; + R = S1 - S2 + S3 + S4; + return R; +} + +static int32_t cos_core(int32_t arc_q33, int32_t sign) +{ + int32_t a, b, B; + int32_t sin_a, cos_a; + int32_t S1, S2, S3, S4, T1; + int32_t R = 0; + int32_t C1 = 0x6487ed51; + int32_t C2 = 0x2aaaaaab; + + a = arc_q33 >> 25; + cos_a = cossin_tbl[a * 2]; + sin_a = cossin_tbl[a * 2 + 1]; + cos_a ^= (sign >> 31); + sin_a ^= (sign >> 31); + a = a << 25; + b = arc_q33 - a; + b -= (1 << 24); + B = SMULL((b << 3), C1) >> 32; + T1 = SMULL(B, B) >> 32; + S1 = cos_a; + S2 = SMULL(cos_a, T1 >> 1) >> 32; + S3 = SMULL(sin_a, B) >> 32; + S4 = SMULL(T1, C2) >> 32; + S4 = SMULL(S3, S4) >> 32; + R = S1 - S2 - S3 + S4; + return R; +} + +int32_t sin_32(int32_t n) +{ + int32_t s = n & SINCOS_BIT_31; + n = n << 1; + if (0 == n) { + return 0; + } + if (SINCOS_BIT_31 == (n & SINCOS_BIT_31)) { + n &= ~SINCOS_BIT_31; + if (n < SINCOS_BIT_30) { + return cos_core(n, s); + } else if (n == SINCOS_BIT_30) { + n -= 1; + } else if (n > SINCOS_BIT_30) { + n = SINCOS_BIT_31 - n; + } + return sin_core(n, s); + } else { + if (n < SINCOS_BIT_30) { + return sin_core(n, s); + } + if (n == SINCOS_BIT_30) { + n -= 1; + } else if (n > SINCOS_BIT_30) { + n = SINCOS_BIT_31 - n; + } + return cos_core(n, s); + } +} + +int32_t cos_32(int32_t n) +{ + int32_t s = n & SINCOS_BIT_31; + + n = n << 1; + if (n == SINCOS_BIT_31) { + return 0; + } + if (SINCOS_BIT_31 == (n & SINCOS_BIT_31)) { + n &= ~SINCOS_BIT_31; + if (n < SINCOS_BIT_30) { + return -sin_core(n, s); + } else if (n == SINCOS_BIT_30) { + n -= 1; + } else if (n > SINCOS_BIT_30) { + n = SINCOS_BIT_31 - n; + } + return -cos_core(n, s); + } else { + if (n < SINCOS_BIT_30) { + return cos_core(n, s); + } + if (n == SINCOS_BIT_30) { + n -= 1; + } else if (n > SINCOS_BIT_30) { + n = SINCOS_BIT_31 - n; + } + return sin_core(n, s); + } +} diff --git a/drivers/media/sprd_scale/common/sin_cos.h b/drivers/media/sprd_scale/common/sin_cos.h new file mode 100644 index 00000000..64012926 --- /dev/null +++ b/drivers/media/sprd_scale/common/sin_cos.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2012 Spreadtrum Communications Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ +#ifndef _SIN_COS_H_ +#define _SIN_COS_H_ +#include <linux/types.h> + +#define COSSIN_Q 30 +#define pi 3.14159265359 +#define PI_32 0x3243F6A88 +#define ARC_32_COEF 0x80000000 +#define ARC_32(arc) (int32_t)((arc) / (pi) * 0x80000000); +int32_t sin_32(int32_t n); +int32_t cos_32(int32_t n); +#endif diff --git a/drivers/media/sprd_scale/scale_drv.c b/drivers/media/sprd_scale/scale_drv.c new file mode 100755 index 00000000..c1a4002a --- /dev/null +++ b/drivers/media/sprd_scale/scale_drv.c @@ -0,0 +1,770 @@ +/* + * Copyright (C) 2012 Spreadtrum Communications Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/bitops.h> +#include <linux/semaphore.h> +#include <linux/delay.h> +#include <asm/io.h> +#ifndef CONFIG_64BIT +#include <soc/sprd/hardware.h> +#endif +#include "scale_drv.h" +#include "gen_scale_coef.h" +#include "dcam_drv.h" + +#define SCALE_LOWEST_ADDR 0x800 +#define SCALE_ADDR_INVALIDE(addr) ((addr) < SCALE_LOWEST_ADDR) +#define SCALE_YUV_ADDR_INVALIDE(y,u,v) \ + (SCALE_ADDR_INVALIDE(y) && \ + SCALE_ADDR_INVALIDE(u) && \ + SCALE_ADDR_INVALIDE(v)) +#define SC_COEFF_H_TAB_OFFSET 0x1400 +#define SC_COEFF_V_TAB_OFFSET 0x14F0 +#define SC_COEFF_V_CHROMA_TAB_OFFSET 0x18F0 +#define SC_COEFF_COEF_SIZE (1 << 10) +#define SC_COEFF_TMP_SIZE (21 << 10) +#define SC_H_COEF_SIZE (0xC0) +#define SC_V_COEF_SIZE (0x210) +#define SC_V_CHROM_COEF_SIZE (0x210) +#define SC_COEFF_H_NUM (SC_H_COEF_SIZE / 4) +#define SC_COEFF_V_NUM (SC_V_COEF_SIZE / 4) +#define SC_COEFF_V_CHROMA_NUM (SC_V_CHROM_COEF_SIZE / 4) +#define SCALE_PIXEL_ALIGNED 4 +#define SCALE_SLICE_HEIGHT_ALIGNED 4 + +int scale_k_module_en(struct device_node *dn) +{ + int ret = 0; + + ret = dcam_get_resizer(DCAM_WAIT_FOREVER); + if (ret) { + printk("scale_module_en, failed to get review path %d \n", ret); + goto get_resizer_fail; + } + + ret = dcam_module_en(dn); + if (ret) { + printk("scale_module_en, failed to enable scale module %d \n", ret); + goto dcam_eb_fail; + } + + return ret; + +dcam_eb_fail: + dcam_rel_resizer(); +get_resizer_fail: + return ret; +} + +int scale_k_module_dis(struct device_node *dn) +{ + int ret = 0; + + ret = dcam_module_dis(dn); + if (ret) { + printk("scale_module_dis, failed to disable scale module %d \n", ret); + } + + ret = dcam_rel_resizer(); + if (ret) { + printk("scale_module_dis, failed to free review path %d \n", ret); + } + + return ret; +} + +static int scale_k_check_deci_slice_mode(uint32_t deci_val, uint32_t slice_h) +{ + int rtn = 0; + + if (deci_val > 0) { + if ((slice_h >= deci_val) && (0 == (slice_h % deci_val))) { + rtn = 0; + } else { + rtn = -1; + } + } + return rtn; +} + +static int scale_k_set_sc_coeff(struct scale_path_info *path_info_ptr) +{ + uint32_t i = 0; + unsigned long h_coeff_addr = SCALE_BASE; + unsigned long v_coeff_addr = SCALE_BASE; + unsigned long v_chroma_coeff_addr = SCALE_BASE; + uint32_t *tmp_buf = NULL; + uint32_t *h_coeff = NULL; + uint32_t *v_coeff = NULL; + uint32_t *v_chroma_coeff = NULL; + uint32_t scale2yuv420 = 0; + uint8_t y_tap = 0; + uint8_t uv_tap = 0; + + if (!path_info_ptr) { + printk("scale_k_set_sc_coeff error: path_info_ptr null \n"); + return -1; + } + + tmp_buf = (uint32_t *)path_info_ptr->coeff_addr; + if (NULL == tmp_buf) { + printk("scale_k_set_sc_coeff error: coeff mem null \n"); + return -1;; + } + + h_coeff_addr += SC_COEFF_H_TAB_OFFSET; + v_coeff_addr += SC_COEFF_V_TAB_OFFSET; + v_chroma_coeff_addr += SC_COEFF_V_CHROMA_TAB_OFFSET; + + if (SCALE_YUV420 == path_info_ptr->output_format) + scale2yuv420 = 1; + + h_coeff = tmp_buf; + v_coeff = tmp_buf + (SC_COEFF_COEF_SIZE/4); + v_chroma_coeff = v_coeff + (SC_COEFF_COEF_SIZE/4); + + if (!(GenScaleCoeff((int16_t)path_info_ptr->sc_input_size.w, + (int16_t)path_info_ptr->sc_input_size.h, + (int16_t)path_info_ptr->output_size.w, + (int16_t)path_info_ptr->output_size.h, + h_coeff, + v_coeff, + v_chroma_coeff, + scale2yuv420, + &y_tap, + &uv_tap, + tmp_buf + (SC_COEFF_COEF_SIZE*3/4), + SC_COEFF_TMP_SIZE))) { + printk("scale_k_set_sc_coeff error: gen scale coeff \n"); + return -1; + } + + for (i = 0; i < SC_COEFF_H_NUM; i++) { + REG_WR(h_coeff_addr, *h_coeff); + h_coeff_addr += 4; + h_coeff++; + } + + for (i = 0; i < SC_COEFF_V_NUM; i++) { + REG_WR(v_coeff_addr, *v_coeff); + v_coeff_addr += 4; + v_coeff++; + } + + for (i = 0; i < SC_COEFF_V_CHROMA_NUM; i++) { + REG_WR(v_chroma_coeff_addr, *v_chroma_coeff); + v_chroma_coeff_addr += 4; + v_chroma_coeff++; + } + + REG_MWR(SCALE_CFG, (BIT_19 | BIT_18 | BIT_17 | BIT_16), ((y_tap & 0x0F) << 16)); + REG_MWR(SCALE_CFG, (BIT_15 | BIT_14 | BIT_13 | BIT_12 | BIT_11), ((uv_tap & 0x1F) << 11)); + + return 0; +} + +static int scale_k_calc_sc_size(struct scale_path_info *path_info_ptr) +{ + int rtn = 0; + uint32_t reg_val = 0; + uint32_t div_factor = 1; + uint32_t i = 0, pixel_aligned_num = 0; + + if (!path_info_ptr) { + printk("scale_k_calc_sc_size error: path_info_ptr null \n"); + return -1; + } + + if (path_info_ptr->input_rect.w > (path_info_ptr->output_size.w * SCALE_SC_COEFF_MAX * (1 << SCALE_DECI_FAC_MAX)) || + path_info_ptr->input_rect.h > (path_info_ptr->output_size.h * SCALE_SC_COEFF_MAX * (1 << SCALE_DECI_FAC_MAX)) || + path_info_ptr->input_rect.w * SCALE_SC_COEFF_MAX < path_info_ptr->output_size.w || + path_info_ptr->input_rect.h * SCALE_SC_COEFF_MAX < path_info_ptr->output_size.h) { + printk("scale_k_calc_sc_size error: input{%d %d}, output{%d %d}\n", + path_info_ptr->input_rect.w, path_info_ptr->input_rect.h, + path_info_ptr->output_size.w, path_info_ptr->output_size.h); + rtn = -1; + } else { + path_info_ptr->sc_input_size.w = path_info_ptr->input_rect.w; + path_info_ptr->sc_input_size.h = path_info_ptr->input_rect.h; + if (path_info_ptr->input_rect.w > path_info_ptr->output_size.w * SCALE_SC_COEFF_MAX || + path_info_ptr->input_rect.h > path_info_ptr->output_size.h * SCALE_SC_COEFF_MAX) { + for (i = 0; i < SCALE_DECI_FAC_MAX; i++) { + div_factor = (uint32_t)(SCALE_SC_COEFF_MAX * (1 << (1 + i))); + if (path_info_ptr->input_rect.w <= (path_info_ptr->output_size.w * div_factor) && + path_info_ptr->input_rect.h <= (path_info_ptr->output_size.h * div_factor)) { + break; + } + } + path_info_ptr->sc_deci_val = (1 << (1 + i)); + pixel_aligned_num = (path_info_ptr->sc_deci_val >= SCALE_PIXEL_ALIGNED) ? path_info_ptr->sc_deci_val : SCALE_PIXEL_ALIGNED; + path_info_ptr->sc_input_size.w = path_info_ptr->input_rect.w >> (1 + i); + path_info_ptr->sc_input_size.h = path_info_ptr->input_rect.h >> (1 + i); + if ((path_info_ptr->sc_input_size.w % pixel_aligned_num) || + (path_info_ptr->sc_input_size.h % pixel_aligned_num)) { + path_info_ptr->sc_input_size.w = path_info_ptr->sc_input_size.w / pixel_aligned_num * pixel_aligned_num; + path_info_ptr->sc_input_size.h = path_info_ptr->sc_input_size.h / pixel_aligned_num * pixel_aligned_num; + path_info_ptr->input_rect.w = path_info_ptr->sc_input_size.w << (1 + i); + path_info_ptr->input_rect.h = path_info_ptr->sc_input_size.h << (1 + i); + } + + REG_WR(SCALE_TRIM_START, 0); + reg_val = path_info_ptr->sc_input_size.w | (path_info_ptr->sc_input_size.h << 16); + REG_WR(SCALE_TRIM_SIZE, reg_val); + REG_WR(SCALE_SRC_SIZE, reg_val); + + REG_OWR(SCALE_REV_BURST_IN_CFG, SCALE_BURST_SUB_EB_BIT); + REG_MWR(SCALE_REV_BURST_IN_CFG, SCALE_BURST_SUB_SAMPLE_MASK, (i << 13)); + REG_MWR(SCALE_REV_BURST_IN_CFG, SCALE_BURST_SRC_WIDTH_MASK, path_info_ptr->input_size.w); + reg_val = path_info_ptr->input_rect.x | (path_info_ptr->input_rect.y << 16); + REG_WR(SCALE_REV_BURST_IN_TRIM_START, reg_val); + reg_val = path_info_ptr->input_rect.w | (path_info_ptr->input_rect.h << 16); + REG_WR(SCALE_REV_BURST_IN_TRIM_SIZE, reg_val); + } + + } + + return rtn; +} + +static int scale_k_cfg_scaler(struct scale_path_info *path_info_ptr) +{ + int rtn = 0; + + if (!path_info_ptr) { + printk("scale_k_cfg_scaler error: path_info_ptr null \n"); + return -1; + } + + rtn = scale_k_calc_sc_size(path_info_ptr); + if (rtn) { + printk("scale_k_cfg_scaler error: calc \n"); + return rtn; + } + + if (path_info_ptr->sc_input_size.w != path_info_ptr->output_size.w || + path_info_ptr->sc_input_size.h != path_info_ptr->output_size.h || + SCALE_YUV420 == path_info_ptr->input_format) { + REG_MWR(SCALE_CFG, SCALE_BYPASS_BIT, 0); + rtn = scale_k_set_sc_coeff(path_info_ptr); + if (rtn) { + printk("scale_k_cfg_scaler error: coeff \n"); + } + } else { + REG_OWR(SCALE_CFG, SCALE_BYPASS_BIT); + } + + return rtn; +} + +int scale_k_slice_cfg(struct scale_slice_param_t *cfg_ptr, struct scale_path_info *path_info_ptr) +{ + int rtn = 0; + uint32_t reg_val = 0; + + if (!cfg_ptr || !path_info_ptr) { + printk("scale_k_io_cfg error: cfg_ptr=%p path_info_ptr=%p", + cfg_ptr, path_info_ptr); + return -1; + } + + /*set slice scale height*/ + if (cfg_ptr->slice_height > SCALE_FRAME_HEIGHT_MAX + || (cfg_ptr->slice_height % SCALE_SLICE_HEIGHT_ALIGNED)) { + printk("scale_k_io_cfg error: slice_height:%d \n", + cfg_ptr->slice_height); + rtn = -1; + goto cfg_exit; + } else { + REG_MWR(SCALE_REV_SLICE_CFG, SCALE_INPUT_SLICE_HEIGHT_MASK, cfg_ptr->slice_height); + path_info_ptr->slice_height = cfg_ptr->slice_height; + } + + /*set input rect*/ + if (cfg_ptr->input_rect.x > SCALE_FRAME_WIDTH_MAX + || cfg_ptr->input_rect.y > SCALE_FRAME_HEIGHT_MAX + || cfg_ptr->input_rect.w > SCALE_FRAME_WIDTH_MAX + || cfg_ptr->input_rect.h > SCALE_FRAME_HEIGHT_MAX) { + printk("scale_k_io_cfg error: input_rect {%d %d %d %d} \n", + cfg_ptr->input_rect.x, cfg_ptr->input_rect.y, + cfg_ptr->input_rect.w, cfg_ptr->input_rect.h); + rtn = -1; + goto cfg_exit; + } else { + reg_val = cfg_ptr->input_rect.x | (cfg_ptr->input_rect.y << 16); + REG_WR(SCALE_REV_BURST_IN_TRIM_START, reg_val); + REG_WR(SCALE_TRIM_START, 0); + reg_val = cfg_ptr->input_rect.w | (cfg_ptr->input_rect.h << 16); + REG_WR(SCALE_REV_BURST_IN_TRIM_SIZE, reg_val); + REG_WR(SCALE_TRIM_SIZE, reg_val); + REG_WR(SCALE_SRC_SIZE, reg_val); + memcpy((void*)&path_info_ptr->input_rect, (void*)&cfg_ptr->input_rect, + sizeof(struct scale_rect_t )); + } + + /*set input address*/ + if (SCALE_YUV_ADDR_INVALIDE(cfg_ptr->input_addr.yaddr, + cfg_ptr->input_addr.uaddr, cfg_ptr->input_addr.vaddr)) { + printk("scale_k_io_cfg error: input_addr {%x %x %x} \n", + cfg_ptr->input_addr.yaddr, cfg_ptr->input_addr.uaddr, + cfg_ptr->input_addr.vaddr); + rtn = -1; + goto cfg_exit; + } else { + REG_WR(SCALE_FRM_IN_Y, cfg_ptr->input_addr.yaddr); + REG_WR(SCALE_FRM_IN_U, cfg_ptr->input_addr.uaddr); + REG_WR(SCALE_FRM_IN_V, cfg_ptr->input_addr.vaddr); + path_info_ptr->input_addr.yaddr = cfg_ptr->input_addr.yaddr; + path_info_ptr->input_addr.uaddr = cfg_ptr->input_addr.uaddr; + path_info_ptr->input_addr.vaddr = cfg_ptr->input_addr.vaddr; + } + + /*set output address*/ + if (SCALE_YUV_ADDR_INVALIDE(cfg_ptr->output_addr.yaddr, + cfg_ptr->output_addr.uaddr, cfg_ptr->output_addr.vaddr)) { + printk("scale_k_io_cfg error: output_addr {%x %x %x} \n", + cfg_ptr->output_addr.yaddr, cfg_ptr->output_addr.uaddr, + cfg_ptr->output_addr.vaddr); + rtn = -1; + goto cfg_exit; + } else { + REG_WR(SCALE_FRM_OUT_Y, cfg_ptr->output_addr.yaddr); + REG_WR(SCALE_FRM_OUT_U, cfg_ptr->output_addr.uaddr); + REG_WR(SCALE_FRM_OUT_V, cfg_ptr->output_addr.vaddr); + path_info_ptr->output_addr.yaddr = cfg_ptr->output_addr.yaddr; + path_info_ptr->output_addr.uaddr = cfg_ptr->output_addr.uaddr; + path_info_ptr->output_addr.vaddr = cfg_ptr->output_addr.vaddr; + } + +cfg_exit: + return rtn; +} + +int scale_k_frame_cfg(struct scale_frame_param_t *cfg_ptr, struct scale_path_info *path_info_ptr) +{ + int rtn = 0; + uint32_t reg_val = 0; + + if (!cfg_ptr || !path_info_ptr) { + printk("scale_k_io_cfg error: cfg_ptr=%p path_info_ptr=%p", + cfg_ptr, path_info_ptr); + return -1; + } + + SCALE_TRACE("scale_k_io_cfg : input_size {%d %d} \n", + cfg_ptr->input_size.w, cfg_ptr->input_size.h); + /*set input size*/ + if (cfg_ptr->input_size.w > SCALE_FRAME_WIDTH_MAX + || cfg_ptr->input_size.h > SCALE_FRAME_HEIGHT_MAX) { + printk("scale_k_io_cfg error: input_size {%d %d} \n", + cfg_ptr->input_size.w, cfg_ptr->input_size.h); + rtn = -1; + goto cfg_exit; + } else { + REG_MWR(SCALE_REV_BURST_IN_CFG, SCALE_BURST_SUB_EB_BIT, 0); + REG_MWR(SCALE_REV_BURST_IN_CFG, SCALE_BURST_SUB_SAMPLE_MASK, 0); + REG_MWR(SCALE_REV_BURST_IN_CFG, SCALE_BURST_SRC_WIDTH_MASK, cfg_ptr->input_size.w); + path_info_ptr->input_size.w = cfg_ptr->input_size.w; + path_info_ptr->input_size.h= cfg_ptr->input_size.h; + } + + SCALE_TRACE("scale_k_io_cfg : input_rect {%d %d %d %d} \n", + cfg_ptr->input_rect.x, cfg_ptr->input_rect.y, + cfg_ptr->input_rect.w, cfg_ptr->input_rect.h); + /*set input rect*/ + if (cfg_ptr->input_rect.x > SCALE_FRAME_WIDTH_MAX + || cfg_ptr->input_rect.x > SCALE_FRAME_HEIGHT_MAX + || cfg_ptr->input_rect.w > SCALE_FRAME_WIDTH_MAX + || cfg_ptr->input_rect.h > SCALE_FRAME_HEIGHT_MAX) { + printk("scale_k_io_cfg error: input_rect {%d %d %d %d} \n", + cfg_ptr->input_rect.x, cfg_ptr->input_rect.y, + cfg_ptr->input_rect.w, cfg_ptr->input_rect.h); + rtn = -1; + goto cfg_exit; + } else { + reg_val = cfg_ptr->input_rect.x | (cfg_ptr->input_rect.y << 16); + REG_WR(SCALE_REV_BURST_IN_TRIM_START, reg_val); + REG_WR(SCALE_TRIM_START, 0); + reg_val = cfg_ptr->input_rect.w | (cfg_ptr->input_rect.h << 16); + REG_WR(SCALE_REV_BURST_IN_TRIM_SIZE, reg_val); + REG_WR(SCALE_TRIM_SIZE, reg_val); + REG_WR(SCALE_SRC_SIZE, reg_val); + memcpy((void*)&path_info_ptr->input_rect, (void*)&cfg_ptr->input_rect, + sizeof(struct scale_rect_t )); + } + + SCALE_TRACE("scale_k_io_cfg : input_format:%d \n", + cfg_ptr->input_format); + /*set input foramt*/ + path_info_ptr->input_format = cfg_ptr->input_format; + if (SCALE_YUV422 == cfg_ptr->input_format + || SCALE_YUV420 == cfg_ptr->input_format || + SCALE_YUV420_3FRAME == cfg_ptr->input_format) { + REG_MWR(SCALE_REV_BURST_IN_CFG, SCALE_BURST_INPUT_MODE_MASK, (cfg_ptr->input_format << 16)); + } else { + printk("scale_k_io_cfg error: input_format:%d \n", + cfg_ptr->input_format); + rtn = -1; + goto cfg_exit; + } + + SCALE_TRACE("scale_k_io_cfg : input_addr {%x %x %x} \n", + cfg_ptr->input_addr.yaddr, cfg_ptr->input_addr.uaddr, + cfg_ptr->input_addr.vaddr); + /*set input address*/ + if (SCALE_YUV_ADDR_INVALIDE(cfg_ptr->input_addr.yaddr, + cfg_ptr->input_addr.uaddr, cfg_ptr->input_addr.vaddr)) { + printk("scale_k_io_cfg error: input_addr {%x %x %x} \n", + cfg_ptr->input_addr.yaddr, cfg_ptr->input_addr.uaddr, + cfg_ptr->input_addr.vaddr); + rtn = -1; + goto cfg_exit; + } else { + REG_WR(SCALE_FRM_IN_Y, cfg_ptr->input_addr.yaddr); + REG_WR(SCALE_FRM_IN_U, cfg_ptr->input_addr.uaddr); + REG_WR(SCALE_FRM_IN_V, cfg_ptr->input_addr.vaddr); + path_info_ptr->input_addr.yaddr = cfg_ptr->input_addr.yaddr; + path_info_ptr->input_addr.uaddr = cfg_ptr->input_addr.uaddr; + path_info_ptr->input_addr.vaddr = cfg_ptr->input_addr.vaddr; + } + + SCALE_TRACE("scale_k_io_cfg : input_endian {%d %d} \n", + cfg_ptr->input_endian.y_endian, cfg_ptr->input_endian.uv_endian); + /*set input endian*/ + if (cfg_ptr->input_endian.y_endian >= SCALE_ENDIAN_MAX + ||cfg_ptr->input_endian.uv_endian >= SCALE_ENDIAN_MAX) { + printk("scale_k_io_cfg error: input_endian {%d %d} \n", + cfg_ptr->input_endian.y_endian, cfg_ptr->input_endian.uv_endian); + rtn = -1; + goto cfg_exit; + } else { + cfg_ptr->input_endian.uv_endian = cfg_ptr->input_endian.y_endian; + dcam_glb_reg_owr(SCALE_ENDIAN_SEL,(SCALE_AXI_RD_ENDIAN_BIT | SCALE_AXI_WR_ENDIAN_BIT), DCAM_ENDIAN_REG); + dcam_glb_reg_mwr(SCALE_ENDIAN_SEL, SCALE_INPUT_Y_ENDIAN_MASK, cfg_ptr->input_endian.y_endian, DCAM_ENDIAN_REG); + dcam_glb_reg_mwr(SCALE_ENDIAN_SEL, SCALE_INPUT_UV_ENDIAN_MASK, (cfg_ptr->input_endian.uv_endian << 2), DCAM_ENDIAN_REG); + SCALE_TRACE("scale_k_io_cfg: output_endian {%d %d} 0x%x \n", + cfg_ptr->input_endian.y_endian, cfg_ptr->input_endian.uv_endian, REG_RD(SCALE_ENDIAN_SEL)); + } + + SCALE_TRACE("scale_k_io_cfg: output_size {%d %d} \n", + cfg_ptr->output_size.w, cfg_ptr->output_size.h); + + /*set output size*/ + if (cfg_ptr->output_size.w > SCALE_FRAME_WIDTH_MAX || + cfg_ptr->output_size.h > SCALE_FRAME_HEIGHT_MAX) { + printk("scale_k_io_cfg error: output_size {%d %d} \n", + cfg_ptr->output_size.w, cfg_ptr->output_size.h); + rtn = -1; + goto cfg_exit; + } else { + reg_val = cfg_ptr->output_size.w | (cfg_ptr->output_size.h << 16); + REG_WR(SCALE_DST_SIZE, reg_val); + path_info_ptr->output_size.w = cfg_ptr->output_size.w; + path_info_ptr->output_size.h = cfg_ptr->output_size.h; + } + + SCALE_TRACE("scale_k_io_cfg: output_format:%d \n", + cfg_ptr->output_format); + + /*set output format*/ + path_info_ptr->output_format = cfg_ptr->output_format; + if (SCALE_YUV422 == cfg_ptr->output_format) { + REG_MWR(SCALE_CFG, SCALE_OUTPUT_MODE_MASK, (0 << 6)); + } else if (SCALE_YUV420 == cfg_ptr->output_format) { + REG_MWR(SCALE_CFG, SCALE_OUTPUT_MODE_MASK, (1 << 6)); + } else if (SCALE_YUV420_3FRAME == cfg_ptr->output_format) { + REG_MWR(SCALE_CFG, SCALE_OUTPUT_MODE_MASK, (3 << 6)); + } else { + printk("scale_k_io_cfg error: output_format:%d \n", + cfg_ptr->output_format); + rtn = -1; + goto cfg_exit; + } + + SCALE_TRACE("scale_k_io_cfg : output_addr {%x %x %x} \n", + cfg_ptr->output_addr.yaddr, cfg_ptr->output_addr.uaddr, + cfg_ptr->output_addr.vaddr); + + /*set output address*/ + if (SCALE_YUV_ADDR_INVALIDE(cfg_ptr->output_addr.yaddr, + cfg_ptr->output_addr.uaddr, cfg_ptr->output_addr.vaddr)) { + printk("scale_k_io_cfg error: output_addr {%x %x %x} \n", + cfg_ptr->output_addr.yaddr, cfg_ptr->output_addr.uaddr, + cfg_ptr->output_addr.vaddr); + rtn = -1; + goto cfg_exit; + } else { + REG_WR(SCALE_FRM_OUT_Y, cfg_ptr->output_addr.yaddr); + REG_WR(SCALE_FRM_OUT_U, cfg_ptr->output_addr.uaddr); + REG_WR(SCALE_FRM_OUT_V, cfg_ptr->output_addr.vaddr); + path_info_ptr->output_addr.yaddr = cfg_ptr->output_addr.yaddr; + path_info_ptr->output_addr.uaddr = cfg_ptr->output_addr.uaddr; + path_info_ptr->output_addr.vaddr = cfg_ptr->output_addr.vaddr; + } + + SCALE_TRACE("scale_k_io_cfg: output_endian {%d %d} \n", + cfg_ptr->output_endian.y_endian, cfg_ptr->output_endian.uv_endian); + + /*set output endian*/ + if (cfg_ptr->output_endian.y_endian >= SCALE_ENDIAN_MAX || + cfg_ptr->output_endian.uv_endian >= SCALE_ENDIAN_MAX) { + printk("scale_k_io_cfg error: output_endian {%d %d} \n", + cfg_ptr->output_endian.y_endian, cfg_ptr->output_endian.uv_endian); + rtn = -1; + goto cfg_exit; + } else { + cfg_ptr->output_endian.uv_endian = cfg_ptr->output_endian.y_endian; + dcam_glb_reg_owr(SCALE_ENDIAN_SEL,(SCALE_AXI_RD_ENDIAN_BIT|SCALE_AXI_WR_ENDIAN_BIT), DCAM_ENDIAN_REG); + dcam_glb_reg_mwr(SCALE_ENDIAN_SEL, SCALE_OUTPUT_Y_ENDIAN_MASK, (cfg_ptr->output_endian.y_endian << 10), DCAM_ENDIAN_REG); + dcam_glb_reg_mwr(SCALE_ENDIAN_SEL, SCALE_OUTPUT_UV_ENDIAN_MASK, (cfg_ptr->output_endian.uv_endian<< 12), DCAM_ENDIAN_REG); + SCALE_TRACE("scale_k_io_cfg: output_endian {%d %d} 0x%x \n", + cfg_ptr->output_endian.y_endian, cfg_ptr->output_endian.uv_endian, REG_RD(SCALE_ENDIAN_SEL)); + } + + SCALE_TRACE("scale_k_io_cfg: scale_mode:%d \n", + cfg_ptr->scale_mode); + + /*set scale mode*/ + if (cfg_ptr->scale_mode >= SCALE_MODE_MAX) { + printk("scale_k_io_cfg error: scale_mode:%d \n", + cfg_ptr->scale_mode); + rtn = -1; + goto cfg_exit; + } else { + if (SCALE_MODE_NORMAL == cfg_ptr->scale_mode) { + REG_MWR(SCALE_CFG, SCALE_MODE_MASK, SCALE_MODE_NORMAL_TYPE); + } else if(SCALE_MODE_SLICE == cfg_ptr->scale_mode) { + REG_MWR(SCALE_CFG, SCALE_MODE_MASK, SCALE_MODE_SLICE_TYPE); + REG_MWR(SCALE_REV_SLICE_CFG, SCALE_SLICE_TYPE_BIT, 0); + }else{ + REG_MWR(SCALE_CFG, SCALE_MODE_MASK, SCALE_MODE_SLICE_TYPE); + REG_OWR(SCALE_REV_SLICE_CFG, SCALE_SLICE_TYPE_BIT); + } + path_info_ptr->scale_mode = cfg_ptr->scale_mode; + } + + /*set slice scale height*/ + if (SCALE_MODE_SLICE == cfg_ptr->scale_mode + || SCALE_MODE_SLICE_READDR ==cfg_ptr->scale_mode) { + if (cfg_ptr->slice_height > SCALE_FRAME_HEIGHT_MAX + || (cfg_ptr->slice_height % SCALE_SLICE_HEIGHT_ALIGNED)) { + printk("scale_k_io_cfg error: slice_height:%d \n", + cfg_ptr->slice_height); + rtn = -1; + goto cfg_exit; + } else { + REG_MWR(SCALE_REV_SLICE_CFG, SCALE_INPUT_SLICE_HEIGHT_MASK, cfg_ptr->slice_height); + path_info_ptr->slice_height = cfg_ptr->slice_height; + } + } + +cfg_exit: + return rtn; +} + +int scale_k_start(struct scale_frame_param_t *cfg_ptr, struct scale_path_info *path_info_ptr) +{ + int rtn = 0; + + if (!path_info_ptr) { + printk("scale_k_start error: path_info_ptr null \n"); + return -1; + } + + dcam_resize_start(); + + rtn = scale_k_frame_cfg(cfg_ptr, path_info_ptr); + if (rtn) { + printk("scale_k_start error: frame cfg \n"); + goto start_exit; + } + + if (SCALE_MODE_NORMAL == path_info_ptr->scale_mode) { + if (path_info_ptr->output_size.w > SCALE_FRAME_WIDTH_MAX) { + rtn = -1; + printk("scale_k_start error: frame output_size_w=%d \n", + path_info_ptr->output_size.w); + goto start_exit; + } + } else { + if (path_info_ptr->output_size.w > SCALE_LINE_BUF_LENGTH) { + rtn = -1; + printk("scale_k_start error: frame output_size_w=%d \n", + path_info_ptr->output_size.w); + goto start_exit; + } + } + + path_info_ptr->slice_in_height = 0; + path_info_ptr->slice_out_height = 0; + path_info_ptr->is_last_slice = 0; + path_info_ptr->sc_deci_val = 0; + + REG_MWR(SCALE_CFG, (SCALE_DEC_X_EB_BIT|SCALE_DEC_Y_EB_BIT), 0); + + rtn = scale_k_cfg_scaler(path_info_ptr); + if (rtn) { + printk("scale_k_start error: cfg \n"); + goto start_exit; + } + + if (SCALE_MODE_NORMAL != path_info_ptr->scale_mode) { + path_info_ptr->slice_in_height += path_info_ptr->slice_height; + rtn = scale_k_check_deci_slice_mode(path_info_ptr->sc_deci_val, path_info_ptr->slice_height); + if (rtn) goto start_exit; + } + dcam_glb_reg_mwr(SCALE_BASE, SCALE_PATH_MASK, SCALE_PATH_SELECT, DCAM_CFG_REG); + dcam_glb_reg_owr(SCALE_BASE, SCALE_PATH_EB_BIT, DCAM_CFG_REG); + + dcam_glb_reg_owr(SCALE_CTRL, (SCALE_FRC_COPY_BIT|SCALE_COEFF_FRC_COPY_BIT), DCAM_CONTROL_REG); + REG_OWR(SCALE_REV_BURST_IN_CFG, SCALE_START_BIT); + REG_WR(SCALE_BURST_GAP, 0x100000); + printk("scale_k_start\n"); + return rtn; + +start_exit: + dcam_resize_end(); + SCALE_TRACE("scale_k_start error: ret=%d \n", rtn); + return rtn; +} + +int scale_k_continue(struct scale_slice_param_t *cfg_ptr, struct scale_path_info *path_info_ptr) +{ + int rtn = 0; + uint32_t slice_h = 0; + + if (!path_info_ptr) { + printk("scale_k_continue error: path_info_ptr null \n"); + return -1; + } + + rtn = scale_k_slice_cfg(cfg_ptr, path_info_ptr); + if (rtn) { + printk("rot_k_ioctl error: slice cfg \n"); + return -1; + } + + + SCALE_TRACE("scale_k_continue: { %d, %d, %d} \n", + path_info_ptr->slice_height, path_info_ptr->slice_in_height, path_info_ptr->scale_mode); + + if (SCALE_MODE_NORMAL != path_info_ptr->scale_mode) { + if (path_info_ptr->slice_in_height + path_info_ptr->slice_height >= path_info_ptr->input_rect.h) { + slice_h = path_info_ptr->input_rect.h - path_info_ptr->slice_in_height; + if (scale_k_check_deci_slice_mode(path_info_ptr->sc_deci_val, slice_h)) { + printk("scale_k_continue error: check deci \n"); + return -1; + } + path_info_ptr->is_last_slice = 1; + REG_MWR(SCALE_REV_SLICE_CFG, SCALE_INPUT_SLICE_HEIGHT_MASK, slice_h); + REG_OWR(SCALE_REV_SLICE_CFG, SCALE_IS_LAST_SLICE_BIT); + } else { + path_info_ptr->is_last_slice = 0; + REG_MWR(SCALE_REV_SLICE_CFG, SCALE_IS_LAST_SLICE_BIT, 0); + } + path_info_ptr->slice_in_height += path_info_ptr->slice_height; + } + + dcam_glb_reg_owr(SCALE_CTRL, SCALE_START_BIT, DCAM_CONTROL_REG); + + return rtn; +} + +int scale_k_stop(void) +{ + int rtn = 0; + + dcam_glb_reg_mwr(SCALE_BASE, SCALE_PATH_EB_BIT, 0, DCAM_CFG_REG); + + return rtn; +} + +int scale_k_isr(struct dcam_frame* dcam_frm, void* u_data) +{ + unsigned long flag; + scale_isr_func user_isr_func; + struct scale_drv_private *private = (struct scale_drv_private *)u_data; + struct scale_path_info *path_info_ptr; + struct scale_frame_info_t *frm_info_ptr; + + if (!private) { + printk("scale_k_isr error: private null \n"); + goto isr_exit; + } + + user_isr_func = private->user_isr_func; + if (!user_isr_func) { + printk("scale_k_isr error: user_isr_func null \n"); + goto isr_exit; + } + path_info_ptr = &private->path_info; + frm_info_ptr = &private->frm_info; + + spin_lock_irqsave(&private->scale_drv_lock, flag); + + memset(frm_info_ptr, 0, sizeof(struct scale_frame_info_t)); + frm_info_ptr->yaddr = path_info_ptr->output_addr.yaddr; + frm_info_ptr->uaddr = path_info_ptr->output_addr.uaddr; + frm_info_ptr->vaddr = path_info_ptr->output_addr.vaddr; + frm_info_ptr->width = path_info_ptr->output_size.w; + if (SCALE_MODE_NORMAL != path_info_ptr->scale_mode) { + frm_info_ptr->height_uv = REG_RD(SCALE_REV_SLICE_O_VCNT); + frm_info_ptr->height_uv = (frm_info_ptr->height_uv >> 16) & SCALE_OUTPUT_SLICE_HEIGHT_MASK; + frm_info_ptr->height = REG_RD(SCALE_REV_SLICE_O_VCNT); + frm_info_ptr->height = frm_info_ptr->height & SCALE_OUTPUT_SLICE_HEIGHT_MASK; + path_info_ptr->slice_out_height += frm_info_ptr->height; + } else { + frm_info_ptr->height = path_info_ptr->output_size.h; + } + + if (path_info_ptr->is_wait_stop) { + up(&path_info_ptr->done_sem); + path_info_ptr->is_wait_stop = 0; + } + if (SCALE_MODE_NORMAL == path_info_ptr->scale_mode || + (SCALE_MODE_NORMAL != path_info_ptr->scale_mode && path_info_ptr->output_size.h == path_info_ptr->slice_out_height)) { + printk("begin to dcam_resize_end\n"); + dcam_resize_end(); + } + user_isr_func(private->scale_fd); + spin_unlock_irqrestore(&private->scale_drv_lock, flag); + +isr_exit: + return 0; +} + +int scale_k_isr_reg(scale_isr_func user_func, struct scale_drv_private *drv_private) +{ + int rtn = 0; + unsigned long flag; + + if (user_func) { + if (!drv_private) { + rtn = -1; + printk("scale_reg_isr Failed \n"); + goto reg_exit; + } + + spin_lock_irqsave(&drv_private->scale_drv_lock, flag); + drv_private->user_isr_func = user_func; + spin_unlock_irqrestore(&drv_private->scale_drv_lock, flag); + + dcam_reg_isr(DCAM_PATH2_DONE, scale_k_isr, (void *)drv_private); + } else { + dcam_reg_isr(DCAM_PATH2_DONE, NULL, NULL); + } + +reg_exit: + return rtn; +} diff --git a/drivers/media/sprd_scale/scale_drv.h b/drivers/media/sprd_scale/scale_drv.h new file mode 100644 index 00000000..d989717c --- /dev/null +++ b/drivers/media/sprd_scale/scale_drv.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2012 Spreadtrum Communications Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ +#ifndef _SCALE_DRV_H_ +#define _SCALE_DRV_H_ + +#include <linux/of.h> +#include <linux/of_device.h> + +#include <linux/types.h> +#include <video/sprd_scale_k.h> +#include "scale_reg.h" + +//#define SCALE_DEBUG + +#ifdef SCALE_DEBUG + #define SCALE_TRACE printk +#else + #define SCALE_TRACE pr_debug +#endif + +typedef void (*scale_isr_func)(void *scale_k_private); + +struct scale_path_info{ + struct scale_size_t input_size; + struct scale_rect_t input_rect; + struct scale_size_t sc_input_size; + struct scale_addr_t input_addr; + uint32_t input_format; + struct scale_size_t output_size; + struct scale_addr_t output_addr; + uint32_t output_format; + uint32_t scale_mode; + uint32_t slice_height; + uint32_t slice_out_height; + uint32_t slice_in_height; + uint32_t is_last_slice; + void *user_data; + uint32_t sc_deci_val; + void * coeff_addr; + uint32_t is_wait_stop; + struct semaphore done_sem; +}; + +struct scale_drv_private{ + struct scale_path_info path_info; + struct scale_frame_info_t frm_info; + spinlock_t scale_drv_lock; + scale_isr_func user_isr_func; + void *scale_fd;/*scale file*/ +}; + +int32_t scale_k_module_en(struct device_node *dn); +int32_t scale_k_module_dis(struct device_node *dn); +int scale_k_start(struct scale_frame_param_t *cfg_ptr, struct scale_path_info *path_info_ptr); +int scale_k_stop(void); +int scale_k_continue(struct scale_slice_param_t *cfg_ptr, struct scale_path_info *path_info_ptr); +int scale_k_isr_reg(scale_isr_func user_func, struct scale_drv_private *drv_private); + +#endif diff --git a/drivers/media/sprd_scale/scale_reg.h b/drivers/media/sprd_scale/scale_reg.h new file mode 100755 index 00000000..851e38d9 --- /dev/null +++ b/drivers/media/sprd_scale/scale_reg.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2012 Spreadtrum Communications Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ +#ifndef _REG_SCALE_TIGER_H_ +#define _REG_SCALE_TIGER_H_ + +#ifndef CONFIG_64BIT +#include <soc/sprd/globalregs.h> +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + + +#define SCALE_BASE DCAM_BASE +#define SCALE_CTRL (SCALE_BASE + 0x0004UL) +#define SCALE_CFG (SCALE_BASE + 0x0024UL) +#define SCALE_SRC_SIZE (SCALE_BASE + 0x0028UL) +#define SCALE_DST_SIZE (SCALE_BASE + 0x002CUL) +#define SCALE_TRIM_START (SCALE_BASE + 0x0030UL) +#define SCALE_TRIM_SIZE (SCALE_BASE + 0x0034UL) +#define SCALE_INT_STS (SCALE_BASE + 0x003CUL) +#define SCALE_INT_MASK (SCALE_BASE + 0x0040UL) +#define SCALE_INT_CLR (SCALE_BASE + 0x0044UL) +#define SCALE_INT_RAW (SCALE_BASE + 0x0048UL) +#define SCALE_FRM_OUT_Y (SCALE_BASE + 0x005CUL) +#define SCALE_FRM_OUT_U (SCALE_BASE + 0x0060UL) +#define SCALE_FRM_OUT_V (SCALE_BASE + 0x0064UL) +#define SCALE_BURST_GAP (SCALE_BASE + 0x0068UL) +#define SCALE_ENDIAN_SEL (SCALE_BASE + 0x006CUL) +#define SCALE_FRM_IN_Y (SCALE_BASE + 0x0074UL) +#define SCALE_FRM_IN_U (SCALE_BASE + 0x0078UL) +#define SCALE_FRM_IN_V (SCALE_BASE + 0x007CUL) +#define SCALE_REV_BURST_IN_CFG (SCALE_BASE + 0x0098UL) +#define SCALE_REV_BURST_IN_TRIM_START (SCALE_BASE + 0x009CUL) +#define SCALE_REV_BURST_IN_TRIM_SIZE (SCALE_BASE + 0x00A0UL) +#define SCALE_REV_SLICE_CFG (SCALE_BASE + 0x00A4UL) +#define SCALE_REV_SLICE_O_VCNT (SCALE_BASE + 0x00ACUL) +#define SCALE_REG_START (SCALE_BASE + 0x0000UL) +#define SCALE_REG_END (SCALE_BASE + 0x0100UL) + +#define SCALE_FRAME_WIDTH_MAX 8192 +#define SCALE_FRAME_HEIGHT_MAX 8192 +#define SCALE_SC_COEFF_MAX 4 +#define SCALE_DECI_FAC_MAX 4 +#define SCALE_LINE_BUF_LENGTH 4096 + +#define SCALE_IRQ_SLICE_BIT (1 << 17) +#define SCALE_IRQ_BIT (1 << 8) +#define SCALE_PATH_MASK (3 << 13) +#define SCALE_PATH_SELECT (2 << 13) +#define SCALE_PATH_EB_BIT (1 << 2) +#define SCALE_FRC_COPY_BIT (1 << 12) +#define SCALE_COEFF_FRC_COPY_BIT (1 << 16) +#define SCALE_START_BIT (1 << 18) +#define SCALE_IS_LAST_SLICE_BIT (1 << 14) +#define SCALE_INPUT_SLICE_HEIGHT_MASK (0x1FFF) +#define SCALE_OUTPUT_SLICE_HEIGHT_MASK (0x1FFF) +#define SCALE_BURST_SUB_EB_BIT (1 << 15) +#define SCALE_BURST_SUB_SAMPLE_MASK (3 << 13) +#define SCALE_BURST_SRC_WIDTH_MASK (0x1FFF) +#define SCALE_BURST_INPUT_MODE_MASK (3 << 16) +#define SCALE_AXI_RD_ENDIAN_BIT (1 << 19) +#define SCALE_AXI_WR_ENDIAN_BIT (1 << 18) +#define SCALE_INPUT_Y_ENDIAN_MASK (3 << 0) +#define SCALE_INPUT_UV_ENDIAN_MASK (3 << 2) +#define SCALE_OUTPUT_Y_ENDIAN_MASK (3 << 10) +#define SCALE_OUTPUT_UV_ENDIAN_MASK (3 << 12) +#define SCALE_OUTPUT_MODE_MASK (3 << 6) +#define SCALE_MODE_MASK (3 << 21) +#define SCALE_MODE_NORMAL_TYPE (0 << 21) +#define SCALE_MODE_SLICE_TYPE (1 << 21) +#define SCALE_SLICE_TYPE_BIT (1 << 13) +#define SCALE_BYPASS_BIT (1 << 20) +#define SCALE_DEC_X_MASK (3 << 0) +#define SCALE_DEC_X_EB_BIT (1 << 2) +#define SCALE_DEC_Y_MASK (3 << 3) +#define SCALE_DEC_Y_EB_BIT (1 << 5) + +#ifdef __cplusplus +} +#endif + +#endif |
