// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2025 Amlogic, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_HAS_WAKELOCK #include #endif #include #include #include #include #include #include "common/plat_ctrl.h" #include #include #include #include "common/vm.h" #define GC2145_CAMERA_MODULE_NAME "gc2145" /* Wake up at about 30 fps */ #define WAKE_NUMERATOR 30 #define WAKE_DENOMINATOR 1001 #define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */ #define MAGIC_RE_MEM 0x123039dc #define CAMERA_USER_CANVAS_INDEX 0x4e #define GC2145_CAMERA_MAJOR_VERSION 0 #define GC2145_CAMERA_MINOR_VERSION 7 #define GC2145_CAMERA_RELEASE 0 #define GC2145_CAMERA_VERSION \ KERNEL_VERSION(GC2145_CAMERA_MAJOR_VERSION, \ GC2145_CAMERA_MINOR_VERSION, \ GC2145_CAMERA_RELEASE) #define GC2145_DRIVER_VERSION "GC2145-COMMON-01-140722" /*unsigned short DGain_shutter,AGain_shutter,*/ /*DGain_shutterH,DGain_shutterL,AGain_shutterH,*/ /*AGain_shutterL,shutterH,shutterL,shutter;*/ /*unsigned short UXGA_Cap = 0;*/ static struct i2c_client *g_i2c_client; static u32 cur_reg; static u8 cur_val; static u8 is_first_time_open; enum DCAMERA_FLICKER { DCAMERA_FLICKER_50HZ = 0, DCAMERA_FLICKER_60HZ, FLICKER_MAX }; /*static unsigned short Antiflicker = DCAMERA_FLICKER_50HZ;*/ #define GC2145_NORMAL_Y0ffset 0x08 #define GC2145_LOWLIGHT_Y0ffset 0x20 MODULE_DESCRIPTION("gc2145 On Board"); MODULE_AUTHOR("amlogic-sh"); MODULE_LICENSE("GPL v2"); static unsigned int video_nr = -1; /* videoX start number, -1 is autodetect. */ static unsigned int debug; /* __module_param(debug, uint, 0644); */ /* MODULE_PARM_DESC(debug, "activates debug info"); */ static unsigned int vid_limit = 16; /* __module_param(vid_limit, uint, 0644); */ /* MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); */ //static int vidio_set_fmt_ticks; static int GC2145_h_active = 640; /* 800; */ static int GC2145_v_active = 480; /* 600; */ static int gc2145_have_open; static struct v4l2_fract gc2145_frmintervals_active = { .numerator = 1, .denominator = 15, }; static int gc2145_night_or_normal; /* add by sp_yjp,20120905 */ static struct vdin_v4l2_ops_s *vops; /* supported controls */ static struct v4l2_queryctrl gc2145_qctrl[] = { { .id = V4L2_CID_DO_WHITE_BALANCE, .type = V4L2_CTRL_TYPE_MENU, .name = "white balance", .minimum = CAM_WB_AUTO, .maximum = CAM_WB_FLUORESCENT, .step = 0x1, .default_value = CAM_WB_AUTO, .flags = V4L2_CTRL_FLAG_SLIDER, }, { .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, .name = "exposure", .minimum = 0, .maximum = 8, .step = 0x1, .default_value = 4, .flags = V4L2_CTRL_FLAG_SLIDER, }, { .id = V4L2_CID_COLORFX, .type = V4L2_CTRL_TYPE_INTEGER, .name = "effect", .minimum = 0, .maximum = 6, .step = 0x1, .default_value = 0, .flags = V4L2_CTRL_FLAG_SLIDER, }, { .id = V4L2_CID_POWER_LINE_FREQUENCY, .type = V4L2_CTRL_TYPE_MENU, .name = "banding", .minimum = CAM_BANDING_50HZ, .maximum = CAM_BANDING_60HZ, .step = 0x1, .default_value = CAM_BANDING_50HZ, .flags = V4L2_CTRL_FLAG_SLIDER, }, { .id = V4L2_CID_BLUE_BALANCE, .type = V4L2_CTRL_TYPE_INTEGER, .name = "scene mode", .minimum = 0, .maximum = 1, .step = 0x1, .default_value = 0, .flags = V4L2_CTRL_FLAG_SLIDER, }, { .id = V4L2_CID_HFLIP, .type = V4L2_CTRL_TYPE_INTEGER, .name = "flip on horizontal", .minimum = 0, .maximum = 1, .step = 0x1, .default_value = 0, .flags = V4L2_CTRL_FLAG_SLIDER, }, { .id = V4L2_CID_VFLIP, .type = V4L2_CTRL_TYPE_INTEGER, .name = "flip on vertical", .minimum = 0, .maximum = 1, .step = 0x1, .default_value = 0, .flags = V4L2_CTRL_FLAG_SLIDER, }, { .id = V4L2_CID_ZOOM_ABSOLUTE, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Zoom, Absolute", .minimum = 100, .maximum = 300, .step = 20, .default_value = 100, .flags = V4L2_CTRL_FLAG_SLIDER, }, { .id = V4L2_CID_ROTATE, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Rotate", .minimum = 0, .maximum = 270, .step = 90, .default_value = 0, .flags = V4L2_CTRL_FLAG_SLIDER, } }; struct v4l2_querymenu gc2145_qmenu_wbmode[] = { { .id = V4L2_CID_DO_WHITE_BALANCE, .index = CAM_WB_AUTO, .name = "auto", .reserved = 0, }, { .id = V4L2_CID_DO_WHITE_BALANCE, .index = CAM_WB_CLOUD, .name = "cloudy-daylight", .reserved = 0, }, { .id = V4L2_CID_DO_WHITE_BALANCE, .index = CAM_WB_INCANDESCENCE, .name = "incandescent", .reserved = 0, }, { .id = V4L2_CID_DO_WHITE_BALANCE, .index = CAM_WB_DAYLIGHT, .name = "daylight", .reserved = 0, }, { .id = V4L2_CID_DO_WHITE_BALANCE, .index = CAM_WB_FLUORESCENT, .name = "fluorescent", .reserved = 0, }, { .id = V4L2_CID_DO_WHITE_BALANCE, .index = CAM_WB_WARM_FLUORESCENT, .name = "warm-fluorescent", .reserved = 0, }, }; struct v4l2_querymenu gc2145_qmenu_anti_banding_mode[] = { { .id = V4L2_CID_POWER_LINE_FREQUENCY, .index = CAM_BANDING_50HZ, .name = "50hz", .reserved = 0, }, { .id = V4L2_CID_POWER_LINE_FREQUENCY, .index = CAM_BANDING_60HZ, .name = "60hz", .reserved = 0, }, }; static struct v4l2_frmivalenum gc2145_frmivalenum[] = { { .index = 0, .pixel_format = V4L2_PIX_FMT_NV21, .width = 352, .height = 288, .type = V4L2_FRMIVAL_TYPE_DISCRETE, { .discrete = { .numerator = 1, .denominator = 30,/* 15 */ } } }, { .index = 0, .pixel_format = V4L2_PIX_FMT_NV21, .width = 640, .height = 480, .type = V4L2_FRMIVAL_TYPE_DISCRETE, { .discrete = { .numerator = 1, .denominator = 30,/* 15 */ } } }, { .index = 1, .pixel_format = V4L2_PIX_FMT_NV21, .width = 1600, .height = 1200, .type = V4L2_FRMIVAL_TYPE_DISCRETE, { .discrete = { .numerator = 1, .denominator = 5, } } }, }; struct gc2145_qmenu_set_s { __u32 id; int num; struct v4l2_querymenu *gc2145_qmenu; }; struct gc2145_qmenu_set_s gc2145_qmenu_set[] = { { .id = V4L2_CID_DO_WHITE_BALANCE, .num = ARRAY_SIZE(gc2145_qmenu_wbmode), .gc2145_qmenu = gc2145_qmenu_wbmode, }, { .id = V4L2_CID_POWER_LINE_FREQUENCY, .num = ARRAY_SIZE(gc2145_qmenu_anti_banding_mode), .gc2145_qmenu = gc2145_qmenu_anti_banding_mode, }, }; static int vidioc_querymenu(struct file *file, void *priv, struct v4l2_querymenu *a) { int i, j; for (i = 0; i < ARRAY_SIZE(gc2145_qmenu_set); i++) { if (a->id && a->id == gc2145_qmenu_set[i].id) { for (j = 0; j < gc2145_qmenu_set[i].num; j++) { if (a->index == gc2145_qmenu_set[i].gc2145_qmenu[j].index) { memcpy(a, &gc2145_qmenu_set[i].gc2145_qmenu[j], sizeof(*a)); return 0; } } } } return -EINVAL; } #define dprintk(dev, level, fmt, arg...) \ v4l2_dbg(level, debug, &(dev)->v4l2_dev, fmt, ## arg) /* * ------------------------------------------------------------------ * Basic structures * ------------------------------------------------------------------ */ struct gc2145_fmt { char *name; u32 fourcc; /* v4l2 format id */ int depth; }; static struct gc2145_fmt formats[] = { { .name = "RGB565 (BE)", .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ .depth = 16, }, { .name = "RGB888 (24)", .fourcc = V4L2_PIX_FMT_RGB24, /* 24 RGB-8-8-8 */ .depth = 24, }, { .name = "BGR888 (24)", .fourcc = V4L2_PIX_FMT_BGR24, /* 24 BGR-8-8-8 */ .depth = 24, }, { .name = "12 Y/CbCr 4:2:0", .fourcc = V4L2_PIX_FMT_NV12, .depth = 12, }, { .name = "12 Y/CbCr 4:2:0", .fourcc = V4L2_PIX_FMT_NV21, .depth = 12, }, { .name = "YUV420P", .fourcc = V4L2_PIX_FMT_YUV420, .depth = 12, }, { .name = "YVU420P", .fourcc = V4L2_PIX_FMT_YVU420, .depth = 12, } }; static struct gc2145_fmt *get_format(struct v4l2_format *f) { struct gc2145_fmt *fmt; unsigned int k; for (k = 0; k < ARRAY_SIZE(formats); k++) { fmt = &formats[k]; if (fmt->fourcc == f->fmt.pix.pixelformat) break; } if (k == ARRAY_SIZE(formats)) return NULL; return &formats[k]; } struct sg_to_addr { int pos; struct scatterlist *sg; }; /* buffer for one video frame */ struct gc2145_buffer { /* common v4l buffer stuff -- must be first */ struct videobuf_buffer vb; struct gc2145_fmt *fmt; unsigned int canvas_id; }; struct gc2145_dmaqueue { struct list_head active; /* thread for generating video stream*/ struct task_struct *kthread; wait_queue_head_t wq; /* Counters to control fps rate */ int frame; int ini_jiffies; }; static LIST_HEAD(gc2145_devicelist); struct gc2145_device { struct list_head gc2145_devicelist; struct v4l2_subdev sd; struct v4l2_device v4l2_dev; /*spinlock of gc2145*/ spinlock_t slock; /*mutex of gc2145*/ struct mutex mutex; int users; /* various device info */ struct video_device *vdev; struct gc2145_dmaqueue vidq; /* Several counters */ unsigned long jiffies; /* Input Number */ int input; /* platform device data from board init. */ struct aml_cam_info_s cam_info; #ifdef CONFIG_HAS_WAKELOCK /* wake lock */ struct wake_lock wake_lock; #endif /* Control 'registers' */ int qctl_regs[ARRAY_SIZE(gc2145_qctrl)]; struct vm_init_s vminfo; }; static inline struct gc2145_device *to_dev(struct v4l2_subdev *sd) { return container_of(sd, struct gc2145_device, sd); } struct gc2145_fh { struct gc2145_device *dev; /* video capture */ struct gc2145_fmt *fmt; unsigned int width, height; struct videobuf_queue vb_vidq; struct videobuf_res_privdata res; enum v4l2_buf_type type; int input; /* Input Number on bars */ int stream_on; unsigned int f_flags; }; /* *static inline struct gc2145_fh *to_fh(struct gc2145_device *dev) *{ * return container_of(dev, struct gc2145_fh, dev); *} */ static struct v4l2_frmsize_discrete gc2145_prev_resolution[] = { /* should include 352x288 and 640x480,*/ /*those two size are used for recording*/ {352, 288}, {640, 480}, }; static struct v4l2_frmsize_discrete gc2145_pic_resolution[] = { {1600, 1200}, {800, 600}, {640, 480} }; #ifndef GC2145_MIRROR #define GC2145_MIRROR 0 #endif #ifndef GC2145_FLIP #define GC2145_FLIP 0 #endif /* *------------------------------------------------------------------ * reg spec of GC2145 * ------------------------------------------------------------------ */ struct aml_camera_i2c_fig_s GC2145_script[] = { /*SENSORDB("GC2145_Sensor_Init"}*/ {0xfe, 0xf0}, {0xfe, 0xf0}, {0xfe, 0xf0}, {0xfc, 0x06}, {0xf6, 0x00}, {0xf7, 0x1d}, {0xf8, 0x84}, {0xfa, 0x00}, {0xf9, 0xfe}, {0xf2, 0x00}, /*ISP reg*/ {0xfe, 0x00}, {0x03, 0x04}, {0x04, 0xe2}, {0x09, 0x00}, {0x0a, 0x00}, {0x0b, 0x00}, {0x0c, 0x00}, {0x0d, 0x04}, {0x0e, 0xc0}, {0x0f, 0x06}, {0x10, 0x52}, {0x12, 0x2e}, {0x17, 0x14}, /*mirror*/ {0x18, 0x22}, {0x19, 0x0e}, {0x1a, 0x01}, {0x1b, 0x4b}, {0x1c, 0x07}, {0x1d, 0x10}, {0x1e, 0x88}, {0x1f, 0x78}, {0x20, 0x03}, {0x21, 0x40}, {0x22, 0xa0}, {0x24, 0x16}, {0x25, 0x01}, {0x26, 0x10}, {0x2d, 0x60}, {0x30, 0x01}, {0x31, 0x90}, {0x33, 0x06}, {0x34, 0x01}, /*ISP reg*/ {0xfe, 0x00}, {0x80, 0x7f}, {0x81, 0x26}, {0x82, 0xfa}, {0x83, 0x00}, {0x84, 0x02}, {0x86, 0x01},/*0x03*/ {0x88, 0x03}, {0x89, 0x03}, {0x85, 0x08}, {0x8a, 0x00}, {0x8b, 0x00}, {0xb0, 0x55}, {0xc3, 0x00}, {0xc4, 0x80}, {0xc5, 0x90}, {0xc6, 0x3b}, {0xc7, 0x46}, {0xec, 0x06}, {0xed, 0x04}, {0xee, 0x60}, {0xef, 0x90}, {0xb6, 0x01}, {0x90, 0x01}, {0x91, 0x00}, {0x92, 0x00}, {0x93, 0x00}, {0x94, 0x00}, {0x95, 0x04}, {0x96, 0xb0}, {0x97, 0x06}, {0x98, 0x40}, /*BLK*/ {0xfe, 0x00}, {0x40, 0x42}, {0x41, 0x00}, {0x43, 0x5b}, {0x5e, 0x00}, {0x5f, 0x00}, {0x60, 0x00}, {0x61, 0x00}, {0x62, 0x00}, {0x63, 0x00}, {0x64, 0x00}, {0x65, 0x00}, {0x66, 0x20}, {0x67, 0x20}, {0x68, 0x20}, {0x69, 0x20}, {0x76, 0x00}, {0x6a, 0x08}, {0x6b, 0x08}, {0x6c, 0x08}, {0x6d, 0x08}, {0x6e, 0x08}, {0x6f, 0x08}, {0x70, 0x08}, {0x71, 0x08}, {0x76, 0x00}, {0x72, 0xf0}, {0x7e, 0x3c}, {0x7f, 0x00}, {0xfe, 0x02}, {0x48, 0x15}, {0x49, 0x00}, {0x4b, 0x0b}, {0xfe, 0x00}, /*AEC*/ {0xfe, 0x01}, {0x01, 0x04}, {0x02, 0xc0}, {0x03, 0x04}, {0x04, 0x90}, {0x05, 0x30}, {0x06, 0x90}, {0x07, 0x30}, {0x08, 0x80}, {0x09, 0x00}, {0x0a, 0x82}, {0x0b, 0x11}, {0x0c, 0x10}, {0x11, 0x10}, {0x13, 0x7b}, {0x17, 0x00}, {0x1c, 0x11}, {0x1e, 0x61}, {0x1f, 0x35}, {0x20, 0x40}, {0x22, 0x40}, {0x23, 0x20}, {0xfe, 0x02}, {0x0f, 0x04}, {0xfe, 0x01}, {0x12, 0x35}, {0x15, 0xb0}, {0x10, 0x31}, {0x3e, 0x28}, {0x3f, 0xb0}, {0x40, 0x90}, {0x41, 0x0f}, /*INTPEE*/ {0xfe, 0x02}, {0x90, 0x6c}, {0x91, 0x03}, {0x92, 0xcb}, {0x94, 0x33}, {0x95, 0x84}, {0x97, 0x65}, {0xa2, 0x11}, {0xfe, 0x00}, /*DNDD*/ {0xfe, 0x02}, {0x80, 0xc1}, {0x81, 0x08}, {0x82, 0x05}, {0x83, 0x08}, {0x84, 0x0a}, {0x86, 0xf0}, {0x87, 0x50}, {0x88, 0x15}, {0x89, 0xb0}, {0x8a, 0x30}, {0x8b, 0x10}, /*ASDE*/ {0xfe, 0x01}, {0x21, 0x04}, {0xfe, 0x02}, {0xa3, 0x50}, {0xa4, 0x20}, {0xa5, 0x40}, {0xa6, 0x80}, {0xab, 0x40}, {0xae, 0x0c}, {0xb3, 0x46}, {0xb4, 0x64}, {0xb6, 0x38}, {0xb7, 0x01}, {0xb9, 0x2b}, {0x3c, 0x04}, {0x3d, 0x15}, {0x4b, 0x06}, {0x4c, 0x20}, {0xfe, 0x00}, /*gamma1*/ {0xfe, 0x02}, {0x10, 0x09}, {0x11, 0x0d}, {0x12, 0x13}, {0x13, 0x19}, {0x14, 0x27}, {0x15, 0x37}, {0x16, 0x45}, {0x17, 0x53}, {0x18, 0x69}, {0x19, 0x7d}, {0x1a, 0x8f}, {0x1b, 0x9d}, {0x1c, 0xa9}, {0x1d, 0xbd}, {0x1e, 0xcd}, {0x1f, 0xd9}, {0x20, 0xe3}, {0x21, 0xea}, {0x22, 0xef}, {0x23, 0xf5}, {0x24, 0xf9}, {0x25, 0xff}, {0xfe, 0x00}, {0xc6, 0x20}, {0xc7, 0x2b}, /*gamma2*/ {0xfe, 0x02}, {0x26, 0x0f}, {0x27, 0x14}, {0x28, 0x19}, {0x29, 0x1e}, {0x2a, 0x27}, {0x2b, 0x33}, {0x2c, 0x3b}, {0x2d, 0x45}, {0x2e, 0x59}, {0x2f, 0x69}, {0x30, 0x7c}, {0x31, 0x89}, {0x32, 0x98}, {0x33, 0xae}, {0x34, 0xc0}, {0x35, 0xcf}, {0x36, 0xda}, {0x37, 0xe2}, {0x38, 0xe9}, {0x39, 0xf3}, {0x3a, 0xf9}, {0x3b, 0xff}, /*YCP*/ {0xfe, 0x02}, {0xd1, 0x32}, {0xd2, 0x32}, {0xd3, 0x40}, {0xd6, 0xf0}, {0xd7, 0x10}, {0xd8, 0xda}, {0xdd, 0x14}, {0xde, 0x86}, {0xed, 0x80}, {0xee, 0x00}, {0xef, 0x3f}, {0xd8, 0xd8}, /*abs*/ {0xfe, 0x01}, {0x9f, 0x40}, /*LSC*/ {0xfe, 0x01}, {0xc2, 0x14}, {0xc3, 0x0d}, {0xc4, 0x0c}, {0xc8, 0x15}, {0xc9, 0x0d}, {0xca, 0x0a}, {0xbc, 0x24}, {0xbd, 0x10}, {0xbe, 0x0b}, {0xb6, 0x25}, {0xb7, 0x16}, {0xb8, 0x15}, {0xc5, 0x00}, {0xc6, 0x00}, {0xc7, 0x00}, {0xcb, 0x00}, {0xcc, 0x00}, {0xcd, 0x00}, {0xbf, 0x07}, {0xc0, 0x00}, {0xc1, 0x00}, {0xb9, 0x00}, {0xba, 0x00}, {0xbb, 0x00}, {0xaa, 0x01}, {0xab, 0x01}, {0xac, 0x00}, {0xad, 0x05}, {0xae, 0x06}, {0xaf, 0x0e}, {0xb0, 0x0b}, {0xb1, 0x07}, {0xb2, 0x06}, {0xb3, 0x17}, {0xb4, 0x0e}, {0xb5, 0x0e}, {0xd0, 0x09}, {0xd1, 0x00}, {0xd2, 0x00}, {0xd6, 0x08}, {0xd7, 0x00}, {0xd8, 0x00}, {0xd9, 0x00}, {0xda, 0x00}, {0xdb, 0x00}, {0xd3, 0x0a}, {0xd4, 0x00}, {0xd5, 0x00}, {0xa4, 0x00}, {0xa5, 0x00}, {0xa6, 0x77}, {0xa7, 0x77}, {0xa8, 0x77}, {0xa9, 0x77}, {0xa1, 0x80}, {0xa2, 0x80}, {0xfe, 0x01}, {0xdf, 0x0d}, {0xdc, 0x25}, {0xdd, 0x30}, {0xe0, 0x77}, {0xe1, 0x80}, {0xe2, 0x77}, {0xe3, 0x90}, {0xe6, 0x90}, {0xe7, 0xa0}, {0xe8, 0x90}, {0xe9, 0xa0}, {0xfe, 0x00}, /*AWB*/ {0xfe, 0x01}, {0x4f, 0x00}, {0x4f, 0x00}, {0x4b, 0x01}, {0x4f, 0x00}, {0x4c, 0x01}, /*D75*/ {0x4d, 0x71}, {0x4e, 0x01}, {0x4c, 0x01}, {0x4d, 0x91}, {0x4e, 0x01}, {0x4c, 0x01}, {0x4d, 0x70}, {0x4e, 0x01}, {0x4c, 0x01}, /*D65*/ {0x4d, 0x90}, {0x4e, 0x02}, {0x4c, 0x01}, {0x4d, 0xb0}, {0x4e, 0x02}, {0x4c, 0x01}, {0x4d, 0x8f}, {0x4e, 0x02}, {0x4c, 0x01}, {0x4d, 0x6f}, {0x4e, 0x02}, {0x4c, 0x01}, {0x4d, 0xaf}, {0x4e, 0x02}, {0x4c, 0x01}, {0x4d, 0xd0}, {0x4e, 0x02}, {0x4c, 0x01}, {0x4d, 0xf0}, {0x4e, 0x02}, {0x4c, 0x01}, {0x4d, 0xcf}, {0x4e, 0x02}, {0x4c, 0x01}, {0x4d, 0xef}, {0x4e, 0x02}, {0x4c, 0x01},/*D50*/ {0x4d, 0x6e}, {0x4e, 0x03}, {0x4c, 0x01}, {0x4d, 0x8e}, {0x4e, 0x03}, {0x4c, 0x01}, {0x4d, 0xae}, {0x4e, 0x03}, {0x4c, 0x01}, {0x4d, 0xce}, {0x4e, 0x03}, {0x4c, 0x01}, {0x4d, 0x4d}, {0x4e, 0x03}, {0x4c, 0x01}, {0x4d, 0x6d}, {0x4e, 0x03}, {0x4c, 0x01}, {0x4d, 0x8d}, {0x4e, 0x03}, {0x4c, 0x01}, {0x4d, 0xad}, {0x4e, 0x03}, {0x4c, 0x01}, {0x4d, 0xcd}, {0x4e, 0x03}, {0x4c, 0x01}, {0x4d, 0x4c}, {0x4e, 0x03}, {0x4c, 0x01}, {0x4d, 0x6c}, {0x4e, 0x03}, {0x4c, 0x01}, {0x4d, 0x8c}, {0x4e, 0x03}, {0x4c, 0x01}, {0x4d, 0xac}, {0x4e, 0x03}, {0x4c, 0x01}, {0x4d, 0xcc}, {0x4e, 0x03}, {0x4c, 0x01}, {0x4d, 0xcb}, {0x4e, 0x03}, {0x4c, 0x01}, {0x4d, 0x4b}, {0x4e, 0x03}, {0x4c, 0x01}, {0x4d, 0x6b}, {0x4e, 0x03}, {0x4c, 0x01}, {0x4d, 0x8b}, {0x4e, 0x03}, {0x4c, 0x01}, {0x4d, 0xab}, {0x4e, 0x03}, {0x4c, 0x01},/*CWF*/ {0x4d, 0x8a}, {0x4e, 0x04}, {0x4c, 0x01}, {0x4d, 0xaa}, {0x4e, 0x04}, {0x4c, 0x01}, {0x4d, 0xca}, {0x4e, 0x04}, {0x4c, 0x01}, {0x4d, 0xca}, {0x4e, 0x04}, {0x4c, 0x01}, {0x4d, 0xc9}, {0x4e, 0x04}, {0x4c, 0x01}, {0x4d, 0x8a}, {0x4e, 0x04}, {0x4c, 0x01}, {0x4d, 0x89}, {0x4e, 0x04}, {0x4c, 0x01}, {0x4d, 0xa9}, {0x4e, 0x04}, {0x4c, 0x02},/*tl84*/ {0x4d, 0x0b}, {0x4e, 0x05}, {0x4c, 0x02}, {0x4d, 0x0a}, {0x4e, 0x05}, {0x4c, 0x01}, {0x4d, 0xeb}, {0x4e, 0x05}, {0x4c, 0x01}, {0x4d, 0xea}, {0x4e, 0x05}, {0x4c, 0x02}, {0x4d, 0x09}, {0x4e, 0x05}, {0x4c, 0x02}, {0x4d, 0x29}, {0x4e, 0x05}, {0x4c, 0x02}, {0x4d, 0x2a}, {0x4e, 0x05}, {0x4c, 0x02}, {0x4d, 0x4a}, {0x4e, 0x05}, /*{0x4c , 0x02},*/ /*{0x4d , 0x6a},*/ /*{0x4e , 0x06},*/ {0x4c, 0x02}, {0x4d, 0x8a}, {0x4e, 0x06}, {0x4c, 0x02}, {0x4d, 0x49}, {0x4e, 0x06}, {0x4c, 0x02}, {0x4d, 0x69}, {0x4e, 0x06}, {0x4c, 0x02}, {0x4d, 0x89}, {0x4e, 0x06}, {0x4c, 0x02}, {0x4d, 0xa9}, {0x4e, 0x06}, {0x4c, 0x02}, {0x4d, 0x48}, {0x4e, 0x06}, {0x4c, 0x02}, {0x4d, 0x68}, {0x4e, 0x06}, {0x4c, 0x02}, {0x4d, 0x69}, {0x4e, 0x06}, {0x4c, 0x02},/*H*/ {0x4d, 0xca}, {0x4e, 0x07}, {0x4c, 0x02}, {0x4d, 0xc9}, {0x4e, 0x07}, {0x4c, 0x02}, {0x4d, 0xe9}, {0x4e, 0x07}, {0x4c, 0x03}, {0x4d, 0x09}, {0x4e, 0x07}, {0x4c, 0x02}, {0x4d, 0xc8}, {0x4e, 0x07}, {0x4c, 0x02}, {0x4d, 0xe8}, {0x4e, 0x07}, {0x4c, 0x02}, {0x4d, 0xa7}, {0x4e, 0x07}, {0x4c, 0x02}, {0x4d, 0xc7}, {0x4e, 0x07}, {0x4c, 0x02}, {0x4d, 0xe7}, {0x4e, 0x07}, {0x4c, 0x03}, {0x4d, 0x07}, {0x4e, 0x07}, {0x4f, 0x01}, {0x50, 0x80}, {0x51, 0xa8}, {0x52, 0x47}, {0x53, 0x38}, {0x54, 0xc7}, {0x56, 0x0e}, {0x58, 0x08}, {0x5b, 0x00}, {0x5c, 0x74}, {0x5d, 0x8b}, {0x61, 0xdb}, {0x62, 0xb8}, {0x63, 0x86}, {0x64, 0xc0}, {0x65, 0x04}, {0x67, 0xa8}, {0x68, 0xb0}, {0x69, 0x00}, {0x6a, 0xa8}, {0x6b, 0xb0}, {0x6c, 0xaf}, {0x6d, 0x8b}, {0x6e, 0x50}, {0x6f, 0x18}, {0x73, 0xf0}, {0x70, 0x0d}, {0x71, 0x60}, {0x72, 0x80}, {0x74, 0x01}, {0x75, 0x01}, {0x7f, 0x0c}, {0x76, 0x70}, {0x77, 0x58}, {0x78, 0xa0}, {0x79, 0x5e}, {0x7a, 0x54}, {0x7b, 0x58}, {0xfe, 0x00}, /*CC*/ {0xfe, 0x02}, {0xc0, 0x01}, {0xc1, 0x44}, {0xc2, 0xfd}, {0xc3, 0x04}, {0xc4, 0xF0}, {0xc5, 0x48}, {0xc6, 0xfd}, {0xc7, 0x46}, {0xc8, 0xfd}, {0xc9, 0x02}, {0xca, 0xe0}, {0xcb, 0x45}, {0xcc, 0xec}, {0xcd, 0x48}, {0xce, 0xf0}, {0xcf, 0xf0}, {0xe3, 0x0c}, {0xe4, 0x4b}, {0xe5, 0xe0}, /*ABS*/ {0xfe, 0x01}, {0x9f, 0x40}, {0xfe, 0x00}, /*OUTPUT*/ {0xfe, 0x00}, {0xf2, 0x0f}, /*dark sun*/ {0xfe, 0x02}, {0x40, 0xbf}, {0x46, 0xcf}, {0xfe, 0x00}, /*frame rate 50Hz*/ {0xfe, 0x00}, {0x05, 0x01}, {0x06, 0x56}, {0x07, 0x00}, {0x08, 0x32}, {0xfe, 0x01}, {0x25, 0x00}, {0x26, 0xfa}, {0x27, 0x04}, {0x28, 0xe2}, /*20fps*/ {0x29, 0x06}, {0x2a, 0xd6}, /*14fps*/ {0x2b, 0x07}, {0x2c, 0xd0}, /*12fps*/ {0x2d, 0x0b}, {0x2e, 0xb8}, /*8fps*/ {0xfe, 0x00}, /*SENSORDB("GC2145_Sensor_SVGA"},*/ {0xfe, 0x00}, {0xfd, 0x01}, {0xfa, 0x00}, /*crop window*/ {0xfe, 0x00}, {0x90, 0x01}, {0x91, 0x00}, {0x92, 0x00}, {0x93, 0x00}, {0x94, 0x00}, {0x95, 0x02}, {0x96, 0x58}, {0x97, 0x03}, {0x98, 0x20}, {0x99, 0x11}, {0x9a, 0x06}, /*AWB*/ {0xfe, 0x00}, {0xec, 0x02}, {0xed, 0x02}, {0xee, 0x30}, {0xef, 0x48}, {0xfe, 0x02}, {0x9d, 0x08}, {0xfe, 0x01}, {0x74, 0x00}, /*AEC*/ {0xfe, 0x01}, {0x01, 0x04}, {0x02, 0x60}, {0x03, 0x02}, {0x04, 0x48}, {0x05, 0x18}, {0x06, 0x50}, {0x07, 0x10}, {0x08, 0x38}, {0x0a, 0x80}, {0x21, 0x04}, {0xfe, 0x00}, {0x20, 0x03}, {0xfe, 0x00}, {0xff, 0xff}, }; struct aml_camera_i2c_fig_s gc2145_svga[] = { {0xfe, 0x00}, {0xb6, 0x01}, {0xfd, 0x01}, {0xfa, 0x11}, /*crop window*/ {0xfe, 0x00}, {0x90, 0x01}, {0x91, 0x00}, {0x92, 0x00}, {0x93, 0x00}, {0x94, 0x00}, {0x95, 0x02}, {0x96, 0x5a}, {0x97, 0x03}, {0x98, 0x22}, {0x99, 0x11}, {0x9a, 0x06}, /*AWB*/ {0xfe, 0x00}, {0xec, 0x02}, {0xed, 0x02}, {0xee, 0x30}, {0xef, 0x48}, {0xfe, 0x02}, {0x9d, 0x08}, {0xfe, 0x01}, {0x74, 0x00}, /*AEC*/ {0xfe, 0x01}, {0x01, 0x04}, {0x02, 0x60}, {0x03, 0x02}, {0x04, 0x48}, {0x05, 0x18}, {0x06, 0x50}, {0x07, 0x10}, {0x08, 0x38}, {0x0a, 0x80}, {0x21, 0x04}, {0xfe, 0x00}, {0x20, 0x03}, {0xfe, 0x00}, {0xff, 0xff}, }; struct aml_camera_i2c_fig_s gc2145_uxga[] = { {0xfe, 0x00}, {0xfd, 0x00}, {0xfa, 0x11}, /*crop window*/ {0xfe, 0x00}, {0x90, 0x01}, {0x91, 0x00}, {0x92, 0x00}, {0x93, 0x00}, {0x94, 0x00}, {0x95, 0x04}, {0x96, 0xb2}, {0x97, 0x06}, {0x98, 0x42}, {0x99, 0x11}, {0x9a, 0x06}, /*AWB*/ {0xfe, 0x00}, {0xec, 0x06}, {0xed, 0x04}, {0xee, 0x60}, {0xef, 0x90}, {0xfe, 0x01}, {0x74, 0x01}, /*AEC*/ {0xfe, 0x01}, {0x01, 0x04}, {0x02, 0xc0}, {0x03, 0x04}, {0x04, 0x90}, {0x05, 0x30}, {0x06, 0x90}, {0x07, 0x30}, {0x08, 0x80}, {0x0a, 0x82}, {0xfe, 0x01}, {0x21, 0x15}, {0xfe, 0x00}, {0x20, 0x15}, {0xfe, 0x00}, {0xff, 0xff}, }; struct aml_camera_i2c_fig_s gc2145_1280x960[] = { /*1280X960*/ {0xfe, 0x00}, {0xfa, 0x11}, {0xfd, 0x00}, {0x1c, 0x05}, /*crop window*/ {0xfe, 0x00}, {0x99, 0x55}, {0x9a, 0x06}, {0x9b, 0x00}, {0x9c, 0x00}, {0x9d, 0x01}, {0x9e, 0x23}, {0x9f, 0x00}, {0xa0, 0x00}, {0xa1, 0x01}, {0xa2, 0x23}, {0x90, 0x01}, {0x91, 0x00}, {0x92, 0x00}, {0x93, 0x00}, {0x94, 0x00}, {0x95, 0x03}, {0x96, 0xc0}, {0x97, 0x05}, {0x98, 0x00}, /*AWB*/ {0xfe, 0x00}, {0xec, 0x06}, {0xed, 0x04}, {0xee, 0x60}, {0xef, 0x90}, {0xfe, 0x01}, {0x74, 0x01}, /*AEC*/ {0xfe, 0x01}, {0x01, 0x04}, {0x02, 0xc0}, {0x03, 0x04}, {0x04, 0x90}, {0x05, 0x30}, {0x06, 0x90}, {0x07, 0x30}, {0x08, 0x80}, {0x0a, 0x82}, {0x21, 0x15}, {0xfe, 0x00}, {0x20, 0x15}, {0xfe, 0x00}, }; struct aml_camera_i2c_fig_s gc2145_FLICKER_50HZ[] = { {0xfe, 0x00}, {0x05, 0x01}, {0x06, 0x56}, {0x07, 0x00}, {0x08, 0x32}, {0xfe, 0x01}, {0x25, 0x00}, {0x26, 0xfa}, {0x27, 0x04}, {0x28, 0xe2}, /*20fps*/ {0x29, 0x06}, {0x2a, 0xd6}, /*14fps*/ {0x2b, 0x07}, {0x2c, 0xd0}, /*12fps*/ {0x2d, 0x0b}, {0x2e, 0xb8}, /*8fps*/ {0xfe, 0x00}, {0xff, 0xff}, }; struct aml_camera_i2c_fig_s gc2145_FLICKER_60HZ[] = { {0x05, 0x01},/*hb*/ {0x06, 0x58}, {0x07, 0x00},/*vb*/ {0x08, 0x32}, {0xfe, 0x01}, {0x25, 0x00},/*step*/ {0x26, 0xd0}, {0x27, 0x04},/*level1*/ {0x28, 0xe0}, {0x29, 0x06},/*level2*/ {0x2a, 0x80}, {0x2b, 0x08},/*level3*/ {0x2c, 0x20}, {0x2d, 0x0b},/*level4*/ {0x2e, 0x60}, {0xfe, 0x00}, {0xff, 0xff}, }; static inline uint64_t getTimestamp(void) { struct timespec64 ts; ktime_get_real_ts64(&ts); return ts.tv_sec * 1000000000ULL + ts.tv_nsec; } /* load GC2145 parameters */ void GC2145_init_regs(struct gc2145_device *dev) { struct i2c_client *client = v4l2_get_subdevdata(&dev->sd); int i = 0; if (!dev->vminfo.isused) return; while (1) { if (GC2145_script[i].val == 0xff && GC2145_script[i].addr == 0xff) { pr_info("GC2145_write_regs success in initial.\n"); break; } if ((i2c_put_byte_add8_new(client, GC2145_script[i].addr, GC2145_script[i].val)) < 0) { pr_err("fail in initial GC2145.\n"); return; } i++; } } /* ************************************************************************ * FUNCTION * GC2145_set_param_wb * * DESCRIPTION * wb setting. * * PARAMETERS * none * * RETURNS * None * * GLOBALS AFFECTED * ************************************************************************ */ void GC2145_set_param_wb(struct gc2145_device *dev, enum camera_wb_flip_e para) /* white balance */ { struct i2c_client *client = v4l2_get_subdevdata(&dev->sd); unsigned int temp = 0; if (!dev->vminfo.isused) return; i2c_put_byte_add8_new(client, 0xfe, 0x00); temp = i2c_get_byte_add8(client, 0x82); switch (para) { case CAM_WB_AUTO:/* auto */ i2c_put_byte_add8_new(client, 0xfe, 0x00); i2c_put_byte_add8_new(client, 0xb3, 0x61); i2c_put_byte_add8_new(client, 0xb4, 0x40); i2c_put_byte_add8_new(client, 0xb5, 0x61); i2c_put_byte_add8_new(client, 0x82, temp | 0x2); break; case CAM_WB_CLOUD: /* cloud */ i2c_put_byte_add8_new(client, 0xfe, 0x00); i2c_put_byte_add8_new(client, 0x82, temp & (~0x02)); i2c_put_byte_add8_new(client, 0xb3, 0x58); i2c_put_byte_add8_new(client, 0xb4, 0x40); i2c_put_byte_add8_new(client, 0xb5, 0x50); break; case CAM_WB_DAYLIGHT: /* */ i2c_put_byte_add8_new(client, 0xfe, 0x00); i2c_put_byte_add8_new(client, 0x82, temp & (~0x02)); i2c_put_byte_add8_new(client, 0xb3, 0x70); i2c_put_byte_add8_new(client, 0xb4, 0x40); i2c_put_byte_add8_new(client, 0xb5, 0x50); break; case CAM_WB_INCANDESCENCE: i2c_put_byte_add8_new(client, 0xfe, 0x00); i2c_put_byte_add8_new(client, 0x82, temp & (~0x02)); i2c_put_byte_add8_new(client, 0xb3, 0x50); i2c_put_byte_add8_new(client, 0xb4, 0x40); i2c_put_byte_add8_new(client, 0xb5, 0xa8); break; case CAM_WB_TUNGSTEN: i2c_put_byte_add8_new(client, 0xfe, 0x00); i2c_put_byte_add8_new(client, 0x82, temp & (~0x02)); i2c_put_byte_add8_new(client, 0xb3, 0xa0); i2c_put_byte_add8_new(client, 0xb4, 0x45); i2c_put_byte_add8_new(client, 0xb5, 0x40); break; case CAM_WB_FLUORESCENT: i2c_put_byte_add8_new(client, 0xfe, 0x00); i2c_put_byte_add8_new(client, 0x82, temp & (~0x02)); i2c_put_byte_add8_new(client, 0xb3, 0x72); i2c_put_byte_add8_new(client, 0xb4, 0x40); i2c_put_byte_add8_new(client, 0xb5, 0x5b); break; case CAM_WB_MANUAL: /* TODO */ break; default: break; } } /* GC2145_set_param_wb */ /* ************************************************************************ * FUNCTION * GC2145_set_param_exposure * * DESCRIPTION * exposure setting. * * PARAMETERS * none * * RETURNS * None * * GLOBALS AFFECTED * ************************************************************************ */ void GC2145_set_param_exposure(struct gc2145_device *dev, enum camera_exposure_e para) { struct i2c_client *client = v4l2_get_subdevdata(&dev->sd); if (!dev->vminfo.isused) return; switch (para) { case EXPOSURE_N4_STEP: i2c_put_byte_add8_new(client, 0xfe, 0x01); i2c_put_byte_add8_new(client, 0x13, 0x55); /*target_y */ i2c_put_byte_add8_new(client, 0xfe, 0x00); break; case EXPOSURE_N3_STEP: i2c_put_byte_add8_new(client, 0xfe, 0x01); i2c_put_byte_add8_new(client, 0x13, 0x60); /*target_y */ i2c_put_byte_add8_new(client, 0xfe, 0x00); break; case EXPOSURE_N2_STEP: i2c_put_byte_add8_new(client, 0xfe, 0x01); i2c_put_byte_add8_new(client, 0x13, 0x65); /* target_y */ i2c_put_byte_add8_new(client, 0xfe, 0x00); break; case EXPOSURE_N1_STEP: i2c_put_byte_add8_new(client, 0xfe, 0x01); i2c_put_byte_add8_new(client, 0x13, 0x70); /* target_y */ i2c_put_byte_add8_new(client, 0xfe, 0x00); break; case EXPOSURE_0_STEP: i2c_put_byte_add8_new(client, 0xfe, 0x01); i2c_put_byte_add8_new(client, 0x13, 0x7b); /* target_y */ i2c_put_byte_add8_new(client, 0xfe, 0x00); break; case EXPOSURE_P1_STEP: i2c_put_byte_add8_new(client, 0xfe, 0x01); i2c_put_byte_add8_new(client, 0x13, 0x85); /* target_y */ i2c_put_byte_add8_new(client, 0xfe, 0x00); break; case EXPOSURE_P2_STEP: i2c_put_byte_add8_new(client, 0xfe, 0x01); i2c_put_byte_add8_new(client, 0x13, 0x90); /* target_y */ i2c_put_byte_add8_new(client, 0xfe, 0x00); break; case EXPOSURE_P3_STEP: i2c_put_byte_add8_new(client, 0xfe, 0x01); i2c_put_byte_add8_new(client, 0x13, 0x95); /* target_y */ i2c_put_byte_add8_new(client, 0xfe, 0x00); break; case EXPOSURE_P4_STEP: i2c_put_byte_add8_new(client, 0xfe, 0x01); i2c_put_byte_add8_new(client, 0x13, 0xa0); /* target_y */ i2c_put_byte_add8_new(client, 0xfe, 0x00); break; default: i2c_put_byte_add8_new(client, 0xfe, 0x01); i2c_put_byte_add8_new(client, 0x13, 0x7b); /* target_y */ i2c_put_byte_add8_new(client, 0xfe, 0x00); break; /* break; */ } } /* GC2145_set_param_exposure */ /* ************************************************************************ * FUNCTION * GC2145_set_param_effect * * DESCRIPTION * effect setting. * * PARAMETERS * none * * RETURNS * None * * GLOBALS AFFECTED * ************************************************************************ */ void GC2145_set_param_effect(struct gc2145_device *dev, enum camera_effect_flip_e para) { struct i2c_client *client = v4l2_get_subdevdata(&dev->sd); if (!dev->vminfo.isused) return; switch (para) { case CAM_EFFECT_ENC_NORMAL: i2c_put_byte_add8_new(client, 0xfe, 0x00); i2c_put_byte_add8_new(client, 0x83, 0xe0); break; case CAM_EFFECT_ENC_GRAYSCALE: i2c_put_byte_add8_new(client, 0xfe, 0x00); i2c_put_byte_add8_new(client, 0x83, 0x12); break; case CAM_EFFECT_ENC_SEPIA: i2c_put_byte_add8_new(client, 0xfe, 0x00); i2c_put_byte_add8_new(client, 0x83, 0x82); break; case CAM_EFFECT_ENC_SEPIAGREEN: i2c_put_byte_add8_new(client, 0xfe, 0x00); i2c_put_byte_add8_new(client, 0x43, 0x52); break; case CAM_EFFECT_ENC_SEPIABLUE: i2c_put_byte_add8_new(client, 0xfe, 0x00); i2c_put_byte_add8_new(client, 0x43, 0x62); break; case CAM_EFFECT_ENC_COLORINV: i2c_put_byte_add8_new(client, 0xfe, 0x00); i2c_put_byte_add8_new(client, 0x83, 0x01); break; default: i2c_put_byte_add8_new(client, 0xfe, 0x00); i2c_put_byte_add8_new(client, 0x83, 0xe0); break; } } /* GC2145_set_param_effect */ /* ************************************************************************ * FUNCTION * GC2145_NightMode * * DESCRIPTION * This function night mode of GC2145. * * PARAMETERS * none * * RETURNS * None * * GLOBALS AFFECTED * ************************************************************************ */ void GC2145_set_night_mode(struct gc2145_device *dev, enum camera_night_mode_flip_e enable) { } void GC2145_set_param_banding(struct gc2145_device *dev, enum camera_banding_flip_e banding) { struct i2c_client *client = v4l2_get_subdevdata(&dev->sd); unsigned char buf[4]; int i = 0; switch (banding) { case CAM_BANDING_50HZ: while (1) { buf[0] = gc2145_FLICKER_50HZ[i].addr; buf[1] = gc2145_FLICKER_50HZ[i].val; if (gc2145_FLICKER_50HZ[i].val == 0xff && gc2145_FLICKER_50HZ[i].addr == 0xff) { pr_err("success in gc2145_FLICKER_50HZ gc2145\n"); break; } if ((i2c_put_byte_add8(client, buf, 2)) < 0) { pr_err("fail in gc2145_FLICKER_50HZ gc2145\n"); return; } i++; } break; case CAM_BANDING_60HZ: while (1) { buf[0] = gc2145_FLICKER_60HZ[i].addr; buf[1] = gc2145_FLICKER_60HZ[i].val; if (gc2145_FLICKER_60HZ[i].val == 0xff && gc2145_FLICKER_60HZ[i].addr == 0xff) { pr_err("success in gc2145_FLICKER_60HZ gc2145\n"); break; } if ((i2c_put_byte_add8(client, buf, 2)) < 0) { pr_err("fail in gc2145_FLICKER_60HZ gc2145\n"); return; } i++; } break; default: break; } } static int set_flip(struct gc2145_device *dev) { struct i2c_client *client = v4l2_get_subdevdata(&dev->sd); unsigned char temp; unsigned char buf[2]; if (!dev->vminfo.isused) return 0; i2c_put_byte_add8_new(client, 0xfe, 0x00); temp = i2c_get_byte_add8(client, 0x17); temp &= 0xfc; temp |= dev->cam_info.m_flip << 0; temp |= dev->cam_info.v_flip << 1; buf[0] = 0x17; buf[1] = temp; if ((i2c_put_byte_add8(client, buf, 2)) < 0) { pr_err("fail in setting sensor orientation\n"); return -1; } return 0; } void GC2145_set_resolution(struct gc2145_device *dev, int height, int width) { unsigned char buf[4]; unsigned int value; unsigned int pid = 0, shutter; unsigned int i = 0; static unsigned int shutter_l; static unsigned int shutter_h; struct i2c_client *client = v4l2_get_subdevdata(&dev->sd); pr_info("%s :wxh = %d x %d\n", __func__, width, height); if (!dev->vminfo.isused) return; if ((width * height < 1600 * 1200)) { while (1) { buf[0] = gc2145_uxga[i].addr; buf[1] = gc2145_uxga[i].val; if (gc2145_uxga[i].val == 0xff && gc2145_uxga[i].addr == 0xff) { pr_info("success in gc2145_uxga.\n"); break; } if ((i2c_put_byte_add8(client, buf, 2)) < 0) { pr_err("fail in gc2145_uxga.\n"); return; } i++; } gc2145_frmintervals_active.numerator = 1; gc2145_frmintervals_active.denominator = 15; GC2145_h_active = 1600; GC2145_v_active = 1200; mdelay(80); } else if (width * height >= 1200 * 1600) { buf[0] = 0xfe; buf[1] = 0x00; i2c_put_byte_add8(client, buf, 2); buf[0] = 0xb6; buf[1] = 0x00; i2c_put_byte_add8(client, buf, 2); buf[0] = 0x03; value = i2c_get_byte_add8(client, 0x03); shutter_l = value; /*printk(KERN_INFO"set camera 0x03=0x%x\n", value);*/ pid |= (value << 8); buf[0] = 0x04; value = i2c_get_byte_add8(client, 0x04); shutter_h = value; /*printk(KERN_INFO"set camera 0x04=0x%x\n", value);*/ pid |= (value & 0x1f); shutter = pid; /*printk(KERN_INFO "set camera shutter=0x%x\n", shutter);*/ while (1) { buf[0] = gc2145_uxga[i].addr; buf[1] = gc2145_uxga[i].val; if (gc2145_uxga[i].val == 0xff && gc2145_uxga[i].addr == 0xff) { pr_info("gc2145_write_regs success in gc2145_uxga.\n"); break; } if ((i2c_put_byte_add8(client, buf, 2)) < 0) { pr_err("fail in gc2145_uxga.\n"); return; } i++; } shutter = shutter / 2; if (shutter < 1) shutter = 1; buf[0] = 0x03; buf[1] = ((shutter >> 8) & 0xff); i2c_put_byte_add8(client, buf, 2); buf[0] = 0x04; buf[1] = (shutter & 0x1f); i2c_put_byte_add8(client, buf, 2); gc2145_frmintervals_active.denominator = 5; gc2145_frmintervals_active.numerator = 1; GC2145_h_active = 1600; GC2145_v_active = 1200; mdelay(130); } set_flip(dev); } /* GC2145_set_resolution */ unsigned char v4l_2_gc2145(int val) { int ret = val / 0x20; if (ret < 4) return ret * 0x20 + 0x80; else if (ret < 8) return ret * 0x20 + 0x20; else return 0; } static int convert_canvas_index(unsigned int v4l2_format, unsigned int start_canvas) { int canvas = start_canvas; switch (v4l2_format) { case V4L2_PIX_FMT_RGB565X: case V4L2_PIX_FMT_VYUY: canvas = start_canvas; break; case V4L2_PIX_FMT_YUV444: case V4L2_PIX_FMT_BGR24: case V4L2_PIX_FMT_RGB24: canvas = start_canvas; break; case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV21: canvas = start_canvas | ((start_canvas + 1) << 8); break; case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_YUV420: if (v4l2_format == V4L2_PIX_FMT_YUV420) canvas = start_canvas | ((start_canvas + 1) << 8) | ((start_canvas + 2) << 16); else canvas = start_canvas | ((start_canvas + 2) << 8) | ((start_canvas + 1) << 16); break; default: break; } return canvas; } static int gc2145_setting(struct gc2145_device *dev, int PROP_ID, int value) { int ret = 0; /* unsigned char cur_val; */ struct i2c_client *client = v4l2_get_subdevdata(&dev->sd); if (!dev->vminfo.isused) return 0; /*here register not finished */ switch (PROP_ID) { case V4L2_CID_BRIGHTNESS: dprintk(dev, 1, "setting brighten:%d\n", v4l_2_gc2145(value)); ret = i2c_put_byte_add8_new(client, 0xdc, v4l_2_gc2145(value)); break; case V4L2_CID_CONTRAST: ret = i2c_put_byte_add8_new(client, 0xde, value); break; case V4L2_CID_SATURATION: ret = i2c_put_byte_add8_new(client, 0xd9, value); break; case V4L2_CID_DO_WHITE_BALANCE: if (gc2145_qctrl[0].default_value != value) { gc2145_qctrl[0].default_value = value; GC2145_set_param_wb(dev, value); pr_info("set camera white_balance=%d.\n ", value); } break; case V4L2_CID_EXPOSURE: if (gc2145_qctrl[1].default_value != value) { gc2145_qctrl[1].default_value = value; GC2145_set_param_exposure(dev, value); pr_info("set camera exposure=%d.\n", value); } break; case V4L2_CID_COLORFX: if (gc2145_qctrl[2].default_value != value) { gc2145_qctrl[2].default_value = value; /* GC2145_set_param_effect(dev,value); */ pr_info("set camera effect=%d.\n", value); } break; case V4L2_CID_WHITENESS: if (gc2145_qctrl[3].default_value != value) { gc2145_qctrl[3].default_value = value; pr_info("@@@SP_000:GC2145_set_param_banding,value=%d\n", value); GC2145_set_param_banding(dev, value); pr_info("@@@SP_111:gc2145_night_or_normal = %d", gc2145_night_or_normal); GC2145_set_night_mode(dev, gc2145_night_or_normal); pr_info("set camera banding=%d.\n", value); } break; case V4L2_CID_BLUE_BALANCE: if (gc2145_qctrl[4].default_value != value) { gc2145_qctrl[4].default_value = value; pr_info("@@@SP_222:GC2145_set_night_mode,night mode=%d\n", value); pr_info("@@@SP_333:gc2145_night_or_normal = %d", gc2145_night_or_normal); GC2145_set_night_mode(dev, value); pr_info("set camera scene mode=%d.\n", value); } break; case V4L2_CID_HFLIP: /* set flip on H. */ value = value & 0x3; if (gc2145_qctrl[5].default_value != value) { gc2145_qctrl[5].default_value = value; pr_info(" set camera h filp =%d.\n ", value); } break; case V4L2_CID_VFLIP: /* set flip on V. */ break; case V4L2_CID_ZOOM_ABSOLUTE: if (gc2145_qctrl[7].default_value != value) gc2145_qctrl[7].default_value = value; break; case V4L2_CID_ROTATE: if (gc2145_qctrl[8].default_value != value) { gc2145_qctrl[8].default_value = value; pr_info(" set camera rotate =%d.\n ", value); } break; default: ret = -1; break; } return ret; } static void power_down_gc2145(struct gc2145_device *dev) { } /* *------------------------------------------------------------------ * DMA and thread functions * ------------------------------------------------------------------ */ #define TSTAMP_MIN_Y 24 #define TSTAMP_MAX_Y (TSTAMP_MIN_Y + 15) #define TSTAMP_INPUT_X 10 #define TSTAMP_MIN_X (54 + TSTAMP_INPUT_X) static void gc2145_fillbuff(struct gc2145_fh *fh, struct gc2145_buffer *buf) { int ret; void *vbuf; struct vm_output_para para = {0}; struct gc2145_device *dev = fh->dev; if (dev->vminfo.mem_alloc_succeed) vbuf = (void *)videobuf_to_res(&buf->vb); else vbuf = videobuf_to_vmalloc(&buf->vb); dprintk(dev, 1, "%s\n", __func__); if (!vbuf) return; /* 0x18221223 indicate the memory type is MAGIC_VMAL_MEM*/ if (dev->vminfo.mem_alloc_succeed) { if (buf->canvas_id == 0) buf->canvas_id = convert_canvas_index(fh->fmt->fourcc, CAMERA_USER_CANVAS_INDEX + buf->vb.i * 3); para.v4l2_memory = MAGIC_RE_MEM; } else { para.v4l2_memory = MAGIC_VMAL_MEM; } para.mirror = gc2145_qctrl[5].default_value & 3;/* not set */ para.v4l2_format = fh->fmt->fourcc; para.zoom = gc2145_qctrl[7].default_value; para.angle = gc2145_qctrl[8].default_value; para.vaddr = (uintptr_t)vbuf; /* para.ext_canvas = buf->canvas_id; */ para.ext_canvas = 0; para.width = buf->vb.width; para.height = buf->vb.height; dev->vminfo.vdin_id = dev->cam_info.vdin_path; ret = vm_fill_this_buffer(&buf->vb, ¶, &dev->vminfo); /*if the vm is not used by sensor ,*/ /*we let vm_fill_this_buffer() return -2*/ if (ret == -2) msleep(40); buf->vb.state = VIDEOBUF_DONE; } static void gc2145_thread_tick(struct gc2145_fh *fh) { struct gc2145_buffer *buf; struct gc2145_device *dev = fh->dev; struct gc2145_dmaqueue *dma_q = &dev->vidq; unsigned long flags = 0; dprintk(dev, 1, "Thread tick\n"); if (!fh->stream_on) { dprintk(dev, 1, "sensor doesn't stream on\n"); return; } spin_lock_irqsave(&dev->slock, flags); if (list_empty(&dma_q->active)) { dprintk(dev, 1, "No active queue to serve\n"); goto unlock; } buf = list_entry(dma_q->active.next, struct gc2145_buffer, vb.queue); dprintk(dev, 1, "%s\n", __func__); dprintk(dev, 1, "list entry get buf is %p\n", buf); if (!(fh->f_flags & O_NONBLOCK)) { /* Nobody is waiting on this buffer, return */ if (!waitqueue_active(&buf->vb.done)) goto unlock; } buf->vb.state = VIDEOBUF_ACTIVE; list_del(&buf->vb.queue); buf->vb.ts = getTimestamp(); /* Fill buffer */ spin_unlock_irqrestore(&dev->slock, flags); gc2145_fillbuff(fh, buf); dprintk(dev, 1, "filled buffer %p\n", buf); wake_up(&buf->vb.done); dprintk(dev, 2, "[%p/%d] wakeup\n", buf, buf->vb. i); return; unlock: spin_unlock_irqrestore(&dev->slock, flags); } #define frames_to_ms(frames) \ (((frames) * WAKE_NUMERATOR * 1000) / WAKE_DENOMINATOR) static void gc2145_sleep(struct gc2145_fh *fh) { struct gc2145_device *dev = fh->dev; struct gc2145_dmaqueue *dma_q = &dev->vidq; DECLARE_WAITQUEUE(wait, current); dprintk(dev, 1, "%s dma_q=0x%08lx\n", __func__, (unsigned long)dma_q); add_wait_queue(&dma_q->wq, &wait); if (kthread_should_stop()) goto stop_task; /* Calculate time to wake up */ /* timeout = msecs_to_jiffies(frames_to_ms(1)); */ gc2145_thread_tick(fh); schedule_timeout_interruptible(2); stop_task: remove_wait_queue(&dma_q->wq, &wait); try_to_freeze(); } static int gc2145_thread(void *data) { struct gc2145_fh *fh = data; struct gc2145_device *dev = fh->dev; dprintk(dev, 1, "thread started\n"); set_freezable(); for (; ;) { gc2145_sleep(fh); if (kthread_should_stop()) break; } dprintk(dev, 1, "thread: exit\n"); return 0; } static int gc2145_start_thread(struct gc2145_fh *fh) { struct gc2145_device *dev = fh->dev; struct gc2145_dmaqueue *dma_q = &dev->vidq; dma_q->frame = 0; dma_q->ini_jiffies = jiffies; dprintk(dev, 1, "%s\n", __func__); dma_q->kthread = kthread_run(gc2145_thread, fh, "gc2145"); if (IS_ERR(dma_q->kthread)) { v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n"); return PTR_ERR(dma_q->kthread); } /* Wakes thread */ wake_up_interruptible(&dma_q->wq); dprintk(dev, 1, "returning from %s\n", __func__); return 0; } static void gc2145_stop_thread(struct gc2145_dmaqueue *dma_q) { struct gc2145_device *dev = container_of(dma_q, struct gc2145_device, vidq); dprintk(dev, 1, "%s\n", __func__); /* shutdown control thread */ if (dma_q->kthread) { kthread_stop(dma_q->kthread); dma_q->kthread = NULL; } } /* *------------------------------------------------------------------ * Videobuf operations * ------------------------------------------------------------------ */ static int vmall_buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) { struct gc2145_fh *fh = vq->priv_data; struct gc2145_device *dev = fh->dev; /* int bytes = fh->fmt->depth >> 3 ; */ *size = (fh->width * fh->height * fh->fmt->depth) >> 3; if (*count == 0) *count = 32; while (*size * *count > vid_limit * 1024 * 1024) (*count)--; dprintk(dev, 1, "%s, count=%d, size=%d\n", __func__, *count, *size); return 0; } static int res_buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) { struct videobuf_res_privdata *res = vq->priv_data; struct gc2145_fh *fh = container_of(res, struct gc2145_fh, res); struct gc2145_device *dev = fh->dev; /* int bytes = fh->fmt->depth >> 3 ; */ int height = fh->height; if (height == 1080) height = 1088; *size = (fh->width * height * fh->fmt->depth) >> 3; if (*count == 0) *count = 32; while (*size * *count > vid_limit * 1024 * 1024) (*count)--; dprintk(dev, 1, "%s, count=%d, size=%d\n", __func__, *count, *size); return 0; } static void free_vmall_buffer(struct videobuf_queue *vq, struct gc2145_buffer *buf) { struct gc2145_fh *fh; struct gc2145_device *dev; fh = vq->priv_data; dev = fh->dev; dprintk(dev, 1, "%s, state: %i\n", __func__, buf->vb.state); videobuf_waiton(vq, &buf->vb, 0, 0); if (in_interrupt()) WARN_ON(1); videobuf_vmalloc_free(&buf->vb); dprintk(dev, 1, "free vmall buffer: freed\n"); buf->vb.state = VIDEOBUF_NEEDS_INIT; } static void free_res_buffer(struct videobuf_queue *vq, struct gc2145_buffer *buf) { struct gc2145_fh *fh; struct gc2145_device *dev; struct videobuf_res_privdata *res = vq->priv_data; fh = container_of(res, struct gc2145_fh, res); dev = fh->dev; dprintk(dev, 1, "%s, state: %i\n", __func__, buf->vb.state); videobuf_waiton(vq, &buf->vb, 0, 0); if (in_interrupt()) WARN_ON(1); videobuf_res_free(vq, &buf->vb); dprintk(dev, 1, "free res buffer: freed\n"); buf->vb.state = VIDEOBUF_NEEDS_INIT; } #define norm_maxw() 1920 #define norm_maxh() 1600 static int vmall_buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, enum v4l2_field field) { struct gc2145_fh *fh; struct gc2145_device *dev; struct gc2145_buffer *buf; int rc; fh = vq->priv_data; dev = fh->dev; buf = container_of(vb, struct gc2145_buffer, vb); /* int bytes = fh->fmt->depth >> 3 ; */ dprintk(dev, 1, "%s, field=%d\n", __func__, field); WARN_ON(!fh->fmt); if (fh->width < 48 || fh->width > norm_maxw() || fh->height < 32 || fh->height > norm_maxh()) return -EINVAL; buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3; if (buf->vb.baddr != 0 && buf->vb.bsize < buf->vb.size) return -EINVAL; /* These properties only change when queue is idle, see s_fmt */ buf->fmt = fh->fmt; buf->vb.width = fh->width; buf->vb.height = fh->height; buf->vb.field = field; /* precalculate_bars(fh); */ if (buf->vb.state == VIDEOBUF_NEEDS_INIT) { rc = videobuf_iolock(vq, &buf->vb, NULL); if (rc < 0) goto fail; } buf->vb.state = VIDEOBUF_PREPARED; return 0; fail: free_vmall_buffer(vq, buf); return rc; } static int res_buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, enum v4l2_field field) { struct gc2145_fh *fh; struct gc2145_device *dev; struct gc2145_buffer *buf; int rc; struct videobuf_res_privdata *res = vq->priv_data; fh = container_of(res, struct gc2145_fh, res); dev = fh->dev; buf = container_of(vb, struct gc2145_buffer, vb); dprintk(dev, 1, "%s, field=%d\n", __func__, field); WARN_ON(!fh->fmt); if (fh->width < 48 || fh->width > norm_maxw() || fh->height < 32 || fh->height > norm_maxh()) return -EINVAL; buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3; if (buf->vb.baddr != 0 && buf->vb.bsize < buf->vb.size) return -EINVAL; /* These properties only change when queue is idle, see s_fmt */ buf->fmt = fh->fmt; buf->vb.width = fh->width; buf->vb.height = fh->height; buf->vb.field = field; /* precalculate_bars(fh); */ if (buf->vb.state == VIDEOBUF_NEEDS_INIT) { rc = videobuf_iolock(vq, &buf->vb, NULL); if (rc < 0) goto fail; } buf->vb.state = VIDEOBUF_PREPARED; return 0; fail: free_res_buffer(vq, buf); return rc; } static void res_buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) { struct gc2145_fh *fh; struct gc2145_device *dev; struct gc2145_dmaqueue *vidq; struct gc2145_buffer *buf = container_of(vb, struct gc2145_buffer, vb); struct videobuf_res_privdata *res = vq->priv_data; fh = container_of(res, struct gc2145_fh, res); dev = fh->dev; vidq = &dev->vidq; dprintk(dev, 1, "%s\n", __func__); buf->vb.state = VIDEOBUF_QUEUED; list_add_tail(&buf->vb.queue, &vidq->active); } static void vmall_buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) { struct gc2145_fh *fh; struct gc2145_device *dev; struct gc2145_dmaqueue *vidq; struct gc2145_buffer *buf = container_of(vb, struct gc2145_buffer, vb); fh = vq->priv_data; dev = fh->dev; vidq = &dev->vidq; dprintk(dev, 1, "%s\n", __func__); buf->vb.state = VIDEOBUF_QUEUED; list_add_tail(&buf->vb.queue, &vidq->active); } static void res_buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb) { struct gc2145_fh *fh; struct gc2145_device *dev; struct gc2145_buffer *buf = container_of(vb, struct gc2145_buffer, vb); struct videobuf_res_privdata *res = vq->priv_data; fh = container_of(res, struct gc2145_fh, res); dev = (struct gc2145_device *)fh->dev; dprintk(dev, 1, "%s\n", __func__); free_res_buffer(vq, buf); } static void vmall_buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb) { struct gc2145_fh *fh; struct gc2145_device *dev; struct gc2145_buffer *buf = container_of(vb, struct gc2145_buffer, vb); fh = vq->priv_data; dev = (struct gc2145_device *)fh->dev; dprintk(dev, 1, "%s\n", __func__); free_vmall_buffer(vq, buf); } static struct videobuf_queue_ops gc2145_video_vmall_qops = { .buf_setup = vmall_buffer_setup, .buf_prepare = vmall_buffer_prepare, .buf_queue = vmall_buffer_queue, .buf_release = vmall_buffer_release, }; static struct videobuf_queue_ops gc2145_video_res_qops = { .buf_setup = res_buffer_setup, .buf_prepare = res_buffer_prepare, .buf_queue = res_buffer_queue, .buf_release = res_buffer_release, }; /* *------------------------------------------------------------------ * IOCTL vidioc handling * ------------------------------------------------------------------ */ static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct gc2145_fh *fh = priv; struct gc2145_device *dev = fh->dev; strcpy(cap->driver, "gc2145"); strcpy(cap->card, "gc2145.canvas"); if (dev->cam_info.front_back == 0) strcat(cap->card, "back"); else strcat(cap->card, "front"); strscpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info)); cap->version = GC2145_CAMERA_VERSION; cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { struct gc2145_fmt *fmt; if (f->index >= ARRAY_SIZE(formats)) return -EINVAL; fmt = &formats[f->index]; strscpy(f->description, fmt->name, sizeof(f->description)); f->pixelformat = fmt->fourcc; return 0; } static int vidioc_enum_frameintervals(struct file *file, void *priv, struct v4l2_frmivalenum *fival) { unsigned int k; if (fival->index > ARRAY_SIZE(gc2145_frmivalenum)) return -EINVAL; for (k = 0; k < ARRAY_SIZE(gc2145_frmivalenum); k++) { if (fival->index == gc2145_frmivalenum[k].index && fival->pixel_format == gc2145_frmivalenum[k].pixel_format && fival->width == gc2145_frmivalenum[k].width && fival->height == gc2145_frmivalenum[k].height) { memcpy(fival, &gc2145_frmivalenum[k], sizeof(struct v4l2_frmivalenum)); return 0; } } return -EINVAL; } static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct gc2145_fh *fh = priv; f->fmt.pix.width = fh->width; f->fmt.pix.height = fh->height; f->fmt.pix.field = fh->vb_vidq.field; f->fmt.pix.pixelformat = fh->fmt->fourcc; f->fmt.pix.bytesperline = (f->fmt.pix.width * fh->fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; return 0; } static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct gc2145_fh *fh = priv; struct gc2145_device *dev = fh->dev; struct gc2145_fmt *fmt; enum v4l2_field field; unsigned int maxw, maxh; fmt = get_format(f); if (!fmt) { dprintk(dev, 1, "Fourcc format (0x%08x) invalid.\n", f->fmt.pix.pixelformat); return -EINVAL; } field = f->fmt.pix.field; if (field == V4L2_FIELD_ANY) { field = V4L2_FIELD_INTERLACED; } else if (field != V4L2_FIELD_INTERLACED) { dprintk(dev, 1, "Field type invalid.\n"); return -EINVAL; } maxw = norm_maxw(); maxh = norm_maxh(); f->fmt.pix.field = field; v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2, &f->fmt.pix.height, 32, maxh, 0, 0); f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; return 0; } /*FIXME: This seems to be generic enough to be at videodev2 */ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct gc2145_fh *fh = priv; struct videobuf_queue *q = &fh->vb_vidq; struct gc2145_device *dev = fh->dev; int ret; f->fmt.pix.width = (f->fmt.pix.width + (CANVAS_WIDTH_ALIGN - 1)) & (~(CANVAS_WIDTH_ALIGN - 1)); if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YVU420 || f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) f->fmt.pix.width = (f->fmt.pix.width + (CANVAS_WIDTH_ALIGN * 2 - 1)) & (~(CANVAS_WIDTH_ALIGN * 2 - 1)); ret = vidioc_try_fmt_vid_cap(file, fh, f); if (ret < 0) return ret; mutex_lock(&q->vb_lock); if (videobuf_queue_is_busy(&fh->vb_vidq)) { dprintk(fh->dev, 1, "%s queue busy\n", __func__); ret = -EBUSY; goto out; } fh->fmt = get_format(f); fh->width = f->fmt.pix.width; fh->height = f->fmt.pix.height; fh->vb_vidq.field = f->fmt.pix.field; fh->type = f->type; // set_flip(dev); // if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) { // vidio_set_fmt_ticks = 1; // GC2145_set_resolution(dev, fh->height, fh->width); // } else if (vidio_set_fmt_ticks == 1) // GC2145_set_resolution(dev, fh->height, fh->width); GC2145_set_resolution(dev, fh->height, fh->width); ret = 0; out: mutex_unlock(&q->vb_lock); return ret; } static int vidioc_g_parm(struct file *file, void *priv, struct v4l2_streamparm *parms) { struct gc2145_fh *fh = priv; struct gc2145_device *dev = fh->dev; struct v4l2_captureparm *cp = &parms->parm.capture; dprintk(dev, 3, "vidioc g parm\n"); if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; memset(cp, 0, sizeof(struct v4l2_captureparm)); cp->capability = V4L2_CAP_TIMEPERFRAME; cp->timeperframe = gc2145_frmintervals_active; pr_debug("g_parm,deno=%d, numerator=%d\n", cp->timeperframe.denominator, cp->timeperframe.numerator); return 0; } static int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p) { struct gc2145_fh *fh = priv; return videobuf_reqbufs(&fh->vb_vidq, p); } static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) { struct gc2145_fh *fh = priv; int ret = videobuf_querybuf(&fh->vb_vidq, p); if (get_cpu_type() == MESON_CPU_MAJOR_ID_M8) { if (ret == 0) p->reserved = convert_canvas_index(fh->fmt->fourcc, CAMERA_USER_CANVAS_INDEX + p->index * 3); else p->reserved = 0; } return ret; } static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) { struct gc2145_fh *fh = priv; return videobuf_qbuf(&fh->vb_vidq, p); } static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) { struct gc2145_fh *fh = priv; return videobuf_dqbuf(&fh->vb_vidq, p, file->f_flags & O_NONBLOCK); } #ifdef CONFIG_VIDEO_V4L1_COMPAT static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) { struct gc2145_fh *fh = priv; return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8); } #endif static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) { struct gc2145_fh *fh = priv; struct vdin_parm_s para; unsigned int vdin_path; int ret = 0; pr_info("vidioc streamon+++\n "); if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; if (i != fh->type) return -EINVAL; memset(¶, 0, sizeof(para)); para.port = TVIN_PORT_CAMERA; para.fmt = TVIN_SIG_FMT_MAX; para.frame_rate = gc2145_frmintervals_active.denominator; para.h_active = GC2145_h_active; para.v_active = GC2145_v_active; para.hsync_phase = 1; para.vsync_phase = 1; para.hs_bp = 0; para.vs_bp = 3; para.bt_path = fh->dev->cam_info.bt_path; vdin_path = fh->dev->cam_info.vdin_path; para.cfmt = TVIN_YUV422; para.dfmt = TVIN_NV21; para.scan_mode = TVIN_SCAN_MODE_PROGRESSIVE; if (is_first_time_open == 0) { para.skip_count = 4; is_first_time_open = 1; } else { para.skip_count = 0; } ret = videobuf_streamon(&fh->vb_vidq); if (ret == 0) { if (fh->dev->vminfo.isused) { vops->start_tvin_service(vdin_path, ¶); fh->stream_on = 1; } } return ret; } static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) { struct gc2145_fh *fh = priv; unsigned int vdin_path; int ret = 0; pr_info("vidioc streamoff+++\n "); vdin_path = fh->dev->cam_info.vdin_path; if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; if (i != fh->type) return -EINVAL; ret = videobuf_streamoff(&fh->vb_vidq); if (ret == 0) { if (fh->dev->vminfo.isused) vops->stop_tvin_service(vdin_path); fh->stream_on = 0; } return ret; } static int vidioc_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize) { int ret = 0, i = 0; struct gc2145_fmt *fmt = NULL; struct v4l2_frmsize_discrete *frmsize = NULL; for (i = 0; i < ARRAY_SIZE(formats); i++) { if (formats[i].fourcc == fsize->pixel_format) { fmt = &formats[i]; break; } } if (!fmt) return -EINVAL; if (fmt->fourcc == V4L2_PIX_FMT_NV21 || fmt->fourcc == V4L2_PIX_FMT_NV12 || fmt->fourcc == V4L2_PIX_FMT_YVU420 || fmt->fourcc == V4L2_PIX_FMT_YUV420) { if (fsize->index >= ARRAY_SIZE(gc2145_prev_resolution)) return -EINVAL; frmsize = &gc2145_prev_resolution[fsize->index]; fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; fsize->discrete.width = frmsize->width; fsize->discrete.height = frmsize->height; } else if (fmt->fourcc == V4L2_PIX_FMT_RGB24) { if (fsize->index >= ARRAY_SIZE(gc2145_pic_resolution)) return -EINVAL; frmsize = &gc2145_pic_resolution[fsize->index]; fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; fsize->discrete.width = frmsize->width; fsize->discrete.height = frmsize->height; } return ret; } static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id i) { return 0; } /* only one input in this sample driver */ static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *inp) { /* if (inp->index >= NUM_INPUTS) */ /* return -EINVAL; */ inp->type = V4L2_INPUT_TYPE_CAMERA; inp->std = V4L2_STD_525_60; sprintf(inp->name, "Camera %u", inp->index); return 0; } static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) { struct gc2145_fh *fh = priv; struct gc2145_device *dev = fh->dev; *i = dev->input; return 0; } static int vidioc_s_input(struct file *file, void *priv, unsigned int i) { struct gc2145_fh *fh = priv; struct gc2145_device *dev = fh->dev; /* if (i >= NUM_INPUTS) */ /* return -EINVAL; */ dev->input = i; /* precalculate_bars(fh); */ return 0; } /* --- controls ---------------------------------------------- */ static int vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *qc) { int i; for (i = 0; i < ARRAY_SIZE(gc2145_qctrl); i++) if (qc->id && qc->id == gc2145_qctrl[i].id) { memcpy(qc, &gc2145_qctrl[i], sizeof(*qc)); return 0; } return -EINVAL; } static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { struct gc2145_fh *fh = priv; struct gc2145_device *dev = fh->dev; int i; for (i = 0; i < ARRAY_SIZE(gc2145_qctrl); i++) if (ctrl->id == gc2145_qctrl[i].id) { ctrl->value = dev->qctl_regs[i]; return 0; } return -EINVAL; } static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { struct gc2145_fh *fh = priv; struct gc2145_device *dev = fh->dev; int i; for (i = 0; i < ARRAY_SIZE(gc2145_qctrl); i++) if (ctrl->id == gc2145_qctrl[i].id) { if (ctrl->value < gc2145_qctrl[i].minimum || ctrl->value > gc2145_qctrl[i].maximum || gc2145_setting(dev, ctrl->id, ctrl->value) < 0) return -ERANGE; dev->qctl_regs[i] = ctrl->value; return 0; } return -EINVAL; } /* *------------------------------------------------------------------ * File operations for the device * ----------------------------------------------------------------- */ static int gc2145_open(struct file *file) { struct gc2145_device *dev = video_drvdata(file); struct gc2145_fh *fh = NULL; int retval = 0; dev->vminfo.vdin_id = dev->cam_info.vdin_path; dev->vminfo.bt_path_count = dev->cam_info.bt_path_count; #if CONFIG_CMA vm_init_resource(16 * SZ_1M, &dev->vminfo); #endif if (dev->vminfo.isused) { pr_info("%s, front_back = %d\n", __func__, dev->cam_info.front_back); aml_cam_init(&dev->cam_info); } GC2145_init_regs(dev); msleep(40); mutex_lock(&dev->mutex); dev->users++; if (dev->users > 1) { dev->users--; pr_err("gc2145 device is opened, device is busy\n"); mutex_unlock(&dev->mutex); return -EBUSY; } dprintk(dev, 1, "open %s type=%s users=%d\n", video_device_node_name(dev->vdev), v4l2_type_names[V4L2_BUF_TYPE_VIDEO_CAPTURE], dev->users); /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); init_waitqueue_head(&dev->vidq.wq); spin_lock_init(&dev->slock); /* allocate + initialize per filehandle data */ fh = kzalloc(sizeof(*fh), GFP_KERNEL); if (!fh) { dev->users--; retval = -ENOMEM; } mutex_unlock(&dev->mutex); if (retval) return retval; #ifdef CONFIG_HAS_WAKELOCK wake_lock(&dev->wake_lock); #endif file->private_data = fh; fh->dev = dev; fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fh->fmt = &formats[0]; fh->width = 1600;/* 640//zyy */ fh->height = 1198;/* 1200;//480 */ fh->stream_on = 0; fh->f_flags = file->f_flags; /* Resets frame counters */ /*dev->jiffies = jiffies;*/ /* TVIN_SIG_FMT_CAMERA_640X480P_30Hz, */ /* TVIN_SIG_FMT_CAMERA_800X600P_30Hz, */ /* TVIN_SIG_FMT_CAMERA_1024X768P_30Hz, // 190 */ /* TVIN_SIG_FMT_CAMERA_1920X1080P_30Hz, */ /* TVIN_SIG_FMT_CAMERA_1280X720P_30Hz, */ if (dev->vminfo.mem_alloc_succeed) { fh->res.start = dev->vminfo.buffer_start; fh->res.end = dev->vminfo.buffer_start + dev->vminfo.vm_buf_size - 1; fh->res.magic = MAGIC_RE_MEM; fh->res.priv = NULL; videobuf_queue_res_init(&fh->vb_vidq, &gc2145_video_res_qops, NULL, &dev->slock, fh->type, V4L2_FIELD_INTERLACED, sizeof(struct gc2145_buffer), (void *)&fh->res, NULL); } else { videobuf_queue_vmalloc_init(&fh->vb_vidq, &gc2145_video_vmall_qops, NULL, &dev->slock, fh->type, V4L2_FIELD_INTERLACED, sizeof(struct gc2145_buffer), fh, NULL); } gc2145_start_thread(fh); gc2145_have_open = 1; return 0; } static ssize_t gc2145_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { struct gc2145_fh *fh = file->private_data; if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { return videobuf_read_stream(&fh->vb_vidq, data, count, ppos, 0, file->f_flags & O_NONBLOCK); } return 0; } static unsigned int gc2145_poll(struct file *file, struct poll_table_struct *wait) { struct gc2145_fh *fh = file->private_data; struct gc2145_device *dev = fh->dev; struct videobuf_queue *q = &fh->vb_vidq; dprintk(dev, 1, "%s\n", __func__); if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return POLLERR; return videobuf_poll_stream(file, q, wait); } static int gc2145_close(struct file *file) { struct gc2145_fh *fh = file->private_data; struct gc2145_device *dev = fh->dev; struct gc2145_dmaqueue *vidq = &dev->vidq; struct video_device *vdev = video_devdata(file); unsigned int vdin_path; vdin_path = fh->dev->cam_info.vdin_path; gc2145_have_open = 0; is_first_time_open = 0; gc2145_stop_thread(vidq); videobuf_stop(&fh->vb_vidq); if (fh->stream_on) { if (dev->vminfo.isused) { pr_info("%s, vdin_path = %d\n", __func__, vdin_path); vops->stop_tvin_service(vdin_path); } } videobuf_mmap_free(&fh->vb_vidq); kfree(fh); mutex_lock(&dev->mutex); dev->users--; mutex_unlock(&dev->mutex); dprintk(dev, 1, "close called (dev=%s, users=%d)\n", video_device_node_name(vdev), dev->users); GC2145_h_active = 640; /* 800//zyy */ GC2145_v_active = 480; /* 1200;//600 */ gc2145_qctrl[0].default_value = 0; gc2145_qctrl[1].default_value = 4; gc2145_qctrl[2].default_value = 0; gc2145_qctrl[3].default_value = 0; gc2145_qctrl[4].default_value = 0; gc2145_qctrl[5].default_value = 0; gc2145_qctrl[7].default_value = 100; gc2145_qctrl[8].default_value = 0; gc2145_frmintervals_active.numerator = 1; gc2145_frmintervals_active.denominator = 15; power_down_gc2145(dev); aml_cam_uninit(&dev->cam_info); #ifdef CONFIG_HAS_WAKELOCK wake_unlock(&dev->wake_lock); #endif #ifdef CONFIG_CMA vm_deinit_resource(&dev->vminfo); #endif return 0; } static int gc2145_mmap(struct file *file, struct vm_area_struct *vma) { struct gc2145_fh *fh = file->private_data; struct gc2145_device *dev = fh->dev; int ret; dprintk(dev, 1, "mmap called, vma=0x%08lx\n", (unsigned long)vma); ret = videobuf_mmap_mapper(&fh->vb_vidq, vma); dprintk(dev, 1, "vma start=0x%08lx, size=%ld, ret=%d\n", (unsigned long)vma->vm_start, (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret); return ret; } static const struct v4l2_file_operations gc2145_fops = { .owner = THIS_MODULE, .open = gc2145_open, .release = gc2145_close, .read = gc2145_read, .poll = gc2145_poll, .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ .mmap = gc2145_mmap, }; static const struct v4l2_ioctl_ops gc2145_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, .vidioc_reqbufs = vidioc_reqbufs, .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, .vidioc_s_std = vidioc_s_std, .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, .vidioc_queryctrl = vidioc_queryctrl, .vidioc_querymenu = vidioc_querymenu, .vidioc_g_ctrl = vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_enum_framesizes = vidioc_enum_framesizes, .vidioc_g_parm = vidioc_g_parm, .vidioc_enum_frameintervals = vidioc_enum_frameintervals, #ifdef CONFIG_VIDEO_V4L1_COMPAT .vidiocgmbuf = vidiocgmbuf, #endif }; static struct video_device gc2145_template = { .name = "gc2145_v4l", .fops = &gc2145_fops, .ioctl_ops = &gc2145_ioctl_ops, .release = video_device_release, .tvnorms = V4L2_STD_525_60, }; static const struct v4l2_subdev_core_ops gc2145_core_ops = { /*.g_chip_ident = gc2145_g_chip_ident,*/ }; static const struct v4l2_subdev_ops gc2145_ops = { .core = &gc2145_core_ops, }; static ssize_t gc2145_show(struct device *dev, struct device_attribute *attr, char *_buf) { return sprintf(_buf, "0x%02x=0x%02x\n", cur_reg, cur_val); } static u32 strtol(const char *nptr, int base) { u32 ret; if (!nptr || (base != 16 && base != 10 && base != 8)) { pr_err("%s(): NULL pointer input\n", __func__); return -1; } for (ret = 0; *nptr; nptr++) { if ((base == 16 && *nptr >= 'A' && *nptr <= 'F') || (base == 16 && *nptr >= 'a' && *nptr <= 'f') || (base >= 10 && *nptr >= '0' && *nptr <= '9') || (base >= 8 && *nptr >= '0' && *nptr <= '7')) { ret *= base; if (base == 16 && *nptr >= 'A' && *nptr <= 'F') ret += *nptr - 'A' + 10; else if (base == 16 && *nptr >= 'a' && *nptr <= 'f') ret += *nptr - 'a' + 10; else if (base >= 10 && *nptr >= '0' && *nptr <= '9') ret += *nptr - '0'; else if (base >= 8 && *nptr >= '0' && *nptr <= '7') ret += *nptr - '0'; } else { return ret; } } return ret; } static ssize_t gc2145_store(struct device *dev, struct device_attribute *attr, const char *_buf, size_t _count) { const char *p = _buf; u16 reg; u8 val; if (!strncmp(_buf, "get", strlen("get"))) { p += strlen("get"); cur_reg = (u32)strtol(p, 16); val = i2c_get_byte_add8(g_i2c_client, cur_reg); pr_info("%s(): get 0x%04x=0x%02x\n", __func__, cur_reg, val); cur_val = val; } else if (!strncmp(_buf, "put", strlen("put"))) { p += strlen("put"); reg = strtol(p, 16); p = strchr(_buf, '='); if (p) { ++p; val = strtol(p, 16); i2c_put_byte_add8_new(g_i2c_client, reg, val); pr_info("%s(): set 0x%04x=0x%02x\n", __func__, reg, val); } else { pr_info("%s(): Bad string format input!\n", __func__); } } else { pr_info("%s(): Bad string format input!\n", __func__); } return _count; } static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *_buf) { strcpy(_buf, "GC2145"); return 4; } static struct device *gc2145_dev; static struct class *gc2145_class; static DEVICE_ATTR_RW(gc2145); static DEVICE_ATTR_RO(name); #define EMDOOR_DEBUG_GC2145 1 #ifdef EMDOOR_DEBUG_GC2145 unsigned int gc2145_reg_addr; static struct i2c_client *gc2145_client; static ssize_t gc2145_show_mine(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { unsigned char dat; dat = i2c_get_byte_add8(gc2145_client, gc2145_reg_addr); return sprintf(buf, "REG[0x%x]=0x%x\n", gc2145_reg_addr, dat); } static ssize_t gc2145_store_mine(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { int tmp; unsigned short reg; unsigned char val; tmp = kstrtoul(buf, 16, NULL); /* sscanf(buf, "%du", &tmp); */ if (tmp < 0xff) { gc2145_reg_addr = tmp; } else { reg = (tmp >> 8) & 0xFFFF; /* reg */ gc2145_reg_addr = reg; val = tmp & 0xFF; /* val */ i2c_put_byte_add8_new(gc2145_client, reg, val); } return count; } static struct kobj_attribute gc2145_attribute = __ATTR(gc2145, 0444, gc2145_show_mine, gc2145_store_mine); static struct attribute *gc2145_attrs[] = { &gc2145_attribute.attr, NULL, }; static const struct attribute_group gc2145_group = { .attrs = gc2145_attrs, }; #endif static int gc2145_probe(struct i2c_client *client) { int err; struct gc2145_device *t; struct v4l2_subdev *sd; struct aml_cam_info_s *plat_dat; int ret; vops = get_vdin_v4l2_ops(); v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); t = kzalloc(sizeof(*t), GFP_KERNEL); /* modify correct i2c addr--- 0x30 */ client->addr = 0x3c; if (!t) { pr_err("%s, no memory", __func__); return -ENOMEM; } snprintf(t->v4l2_dev.name, sizeof(t->v4l2_dev.name), "%s-%03d", "gc2145", 0); ret = v4l2_device_register(NULL, &t->v4l2_dev); if (ret) { pr_info("%s, v4l2 device register failed", __func__); kfree(t); kfree(client); return ret; } sd = &t->sd; v4l2_i2c_subdev_init(sd, client, &gc2145_ops); plat_dat = (struct aml_cam_info_s *)client->dev.platform_data; /* test if devices exist. */ memset(&t->vminfo, 0, sizeof(struct vm_init_s)); /* Now create a video4linux device */ mutex_init(&t->mutex); /* Now create a video4linux device */ t->vdev = video_device_alloc(); if (!t->vdev) { kfree(t); kfree(client); return -ENOMEM; } memcpy(t->vdev, &gc2145_template, sizeof(*t->vdev)); t->vdev->v4l2_dev = &t->v4l2_dev; video_set_drvdata(t->vdev, t); #ifdef CONFIG_HAS_WAKELOCK wake_lock_init(&t->wake_lock, WAKE_LOCK_SUSPEND, "gc2145"); #endif /* Register it */ if (plat_dat) { memcpy(&t->cam_info, plat_dat, sizeof(struct aml_cam_info_s)); pr_info("%s, front_back = %d\n", __func__, plat_dat->front_back); video_nr = plat_dat->front_back; } else { pr_err("camera gc2145: have no platform data\n"); kfree(t); return -1; } t->cam_info.version = GC2145_DRIVER_VERSION; if (aml_cam_info_reg(&t->cam_info) < 0) pr_err("reg caminfo error\n"); err = video_register_device(t->vdev, VFL_TYPE_GRABBER, video_nr); if (err < 0) { video_device_release(t->vdev); kfree(t); return err; } gc2145_class = class_create("gc2145"); if (IS_ERR(gc2145_class)) { pr_err("Create class gc2145 fail.\n"); return -ENOMEM; } gc2145_dev = device_create(gc2145_class, NULL, MKDEV(0, 1), NULL, "dev"); device_create_file(gc2145_dev, &dev_attr_gc2145); device_create_file(gc2145_dev, &dev_attr_name); g_i2c_client = client; #ifdef EMDOOR_DEBUG_GC2145 gc2145_client = client; err = sysfs_create_group(&client->dev.kobj, &gc2145_group); #endif return 0; } static void gc2145_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); struct gc2145_device *t = to_dev(sd); video_unregister_device(t->vdev); v4l2_device_unregister_subdev(sd); #ifdef CONFIG_HAS_WAKELOCK wake_lock_destroy(&t->wake_lock); #endif aml_cam_info_unreg(&t->cam_info); kfree(t); } static const struct i2c_device_id gc2145_id[] = { { "gc2145", 0 }, { } }; static struct i2c_driver gc2145_i2c_driver = { .driver = { .name = "gc2145", }, .probe = gc2145_probe, .remove = gc2145_remove, .id_table = gc2145_id, }; //module_i2c_driver(gc2145_i2c_driver); int __init gc2145_i2c_driver_init(void) { return i2c_add_driver(&gc2145_i2c_driver); }