summaryrefslogtreecommitdiff
path: root/drivers/media/sprd_scale
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
committerYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
commit8e4bff4cab0ddac6060645b0715210484d02ff40 (patch)
tree3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/media/sprd_scale
Initial file dump from open source releaseHEADmaster
Diffstat (limited to 'drivers/media/sprd_scale')
-rw-r--r--drivers/media/sprd_scale/Makefile12
-rw-r--r--drivers/media/sprd_scale/common/compat_img_scale.c95
-rw-r--r--drivers/media/sprd_scale/common/compat_img_scale.h25
-rw-r--r--drivers/media/sprd_scale/common/gen_scale_coef.c644
-rw-r--r--drivers/media/sprd_scale/common/gen_scale_coef.h34
-rw-r--r--drivers/media/sprd_scale/common/img_scale.c388
-rw-r--r--drivers/media/sprd_scale/common/img_scale.h43
-rw-r--r--drivers/media/sprd_scale/common/sin_cos.c152
-rw-r--r--drivers/media/sprd_scale/common/sin_cos.h24
-rwxr-xr-xdrivers/media/sprd_scale/scale_drv.c770
-rw-r--r--drivers/media/sprd_scale/scale_drv.h69
-rwxr-xr-xdrivers/media/sprd_scale/scale_reg.h95
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, &reg_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