diff options
author | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
---|---|---|
committer | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
commit | c6da2cfeb05178a11c6d062a06f8078150ee492f (patch) | |
tree | f3b4021d252c52d6463a9b3c1bb7245e399b009c /drivers/media/video/s5k5ccgx.c | |
parent | c6d7c4dbff353eac7919342ae6b3299a378160a6 (diff) | |
download | kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.zip kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.gz kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.bz2 |
samsung update 1
Diffstat (limited to 'drivers/media/video/s5k5ccgx.c')
-rw-r--r-- | drivers/media/video/s5k5ccgx.c | 3199 |
1 files changed, 3199 insertions, 0 deletions
diff --git a/drivers/media/video/s5k5ccgx.c b/drivers/media/video/s5k5ccgx.c new file mode 100644 index 0000000..2123304 --- /dev/null +++ b/drivers/media/video/s5k5ccgx.c @@ -0,0 +1,3199 @@ +/* drivers/media/video/s5k5ccgx.c + * + * Copyright (c) 2010, Samsung Electronics. All rights reserved + * Author: dongseong.lim + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 "s5k5ccgx.h" + +static int s5k5ccgx_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl); + +static const struct s5k5ccgx_fps s5k5ccgx_framerates[] = { + { I_FPS_0, FRAME_RATE_AUTO }, + { I_FPS_15, FRAME_RATE_15 }, + { I_FPS_25, FRAME_RATE_25 }, + { I_FPS_30, FRAME_RATE_30 }, +}; + +static const struct s5k5ccgx_framesize s5k5ccgx_preview_frmsizes[] = { + { S5K5CCGX_PREVIEW_QCIF, 176, 144 }, + { S5K5CCGX_PREVIEW_320x240, 320, 240 }, + { S5K5CCGX_PREVIEW_CIF, 352, 288 }, + { S5K5CCGX_PREVIEW_528x432, 528, 432 }, + { S5K5CCGX_PREVIEW_VGA, 640, 480 }, + { S5K5CCGX_PREVIEW_D1, 720, 480 }, + { S5K5CCGX_PREVIEW_SVGA, 800, 600 }, +#ifdef CONFIG_VIDEO_S5K5CCGX_P2 + { S5K5CCGX_PREVIEW_1024x552, 1024, 552 }, +#else + { S5K5CCGX_PREVIEW_1024x576, 1024, 576 }, +#endif + /*{ S5K5CCGX_PREVIEW_1024x616, 1024, 616 },*/ + { S5K5CCGX_PREVIEW_XGA, 1024, 768 }, + { S5K5CCGX_PREVIEW_PVGA, 1280, 720 }, +}; + +static const struct s5k5ccgx_framesize s5k5ccgx_capture_frmsizes[] = { + { S5K5CCGX_CAPTURE_VGA, 640, 480 }, +#ifdef CONFIG_VIDEO_S5K5CCGX_P2 + { S5K5CCGX_CAPTURE_W2MP, 2048, 1104 }, +#else + { S5K5CCGX_CAPTURE_W2MP, 2048, 1152 }, +#endif + { S5K5CCGX_CAPTURE_3MP, 2048, 1536 }, +}; + +static struct s5k5ccgx_control s5k5ccgx_ctrls[] = { + S5K5CCGX_INIT_CONTROL(V4L2_CID_CAMERA_FLASH_MODE, \ + FLASH_MODE_OFF), + + S5K5CCGX_INIT_CONTROL(V4L2_CID_CAMERA_BRIGHTNESS, \ + EV_DEFAULT), + + S5K5CCGX_INIT_CONTROL(V4L2_CID_CAMERA_METERING, \ + METERING_MATRIX), + + S5K5CCGX_INIT_CONTROL(V4L2_CID_CAMERA_WHITE_BALANCE, \ + WHITE_BALANCE_AUTO), + + S5K5CCGX_INIT_CONTROL(V4L2_CID_CAMERA_EFFECT, \ + IMAGE_EFFECT_NONE), +}; + +static const struct s5k5ccgx_regs reg_datas = { + .ev = { + S5K5CCGX_REGSET(GET_EV_INDEX(EV_MINUS_4), + s5k5ccgx_brightness_m_4), + S5K5CCGX_REGSET(GET_EV_INDEX(EV_MINUS_3), + s5k5ccgx_brightness_m_3), + S5K5CCGX_REGSET(GET_EV_INDEX(EV_MINUS_2), + s5k5ccgx_brightness_m_2), + S5K5CCGX_REGSET(GET_EV_INDEX(EV_MINUS_1), + s5k5ccgx_brightness_m_1), + S5K5CCGX_REGSET(GET_EV_INDEX(EV_DEFAULT), + s5k5ccgx_brightness_0), + S5K5CCGX_REGSET(GET_EV_INDEX(EV_PLUS_1), + s5k5ccgx_brightness_p_1), + S5K5CCGX_REGSET(GET_EV_INDEX(EV_PLUS_2), + s5k5ccgx_brightness_p_2), + S5K5CCGX_REGSET(GET_EV_INDEX(EV_PLUS_3), + s5k5ccgx_brightness_p_3), + S5K5CCGX_REGSET(GET_EV_INDEX(EV_PLUS_4), + s5k5ccgx_brightness_p_4), + }, + .metering = { + S5K5CCGX_REGSET(METERING_MATRIX, s5k5ccgx_metering_normal), + S5K5CCGX_REGSET(METERING_CENTER, s5k5ccgx_metering_center), + S5K5CCGX_REGSET(METERING_SPOT, s5k5ccgx_metering_spot), + }, + .iso = { + S5K5CCGX_REGSET(ISO_AUTO, s5k5ccgx_iso_auto), + S5K5CCGX_REGSET(ISO_100, s5k5ccgx_iso_100), + S5K5CCGX_REGSET(ISO_200, s5k5ccgx_iso_200), + S5K5CCGX_REGSET(ISO_400, s5k5ccgx_iso_400), + }, + .effect = { + S5K5CCGX_REGSET(IMAGE_EFFECT_NONE, s5k5ccgx_effect_off), + S5K5CCGX_REGSET(IMAGE_EFFECT_BNW, s5k5ccgx_effect_mono), + S5K5CCGX_REGSET(IMAGE_EFFECT_SEPIA, s5k5ccgx_effect_sepia), + S5K5CCGX_REGSET(IMAGE_EFFECT_NEGATIVE, + s5k5ccgx_effect_negative), + }, + .white_balance = { + S5K5CCGX_REGSET(WHITE_BALANCE_AUTO, s5k5ccgx_wb_auto), + S5K5CCGX_REGSET(WHITE_BALANCE_SUNNY, s5k5ccgx_wb_daylight), + S5K5CCGX_REGSET(WHITE_BALANCE_CLOUDY, s5k5ccgx_wb_cloudy), + S5K5CCGX_REGSET(WHITE_BALANCE_TUNGSTEN, + s5k5ccgx_wb_incandescent), + S5K5CCGX_REGSET(WHITE_BALANCE_FLUORESCENT, + s5k5ccgx_wb_fluorescent), + }, + .preview_size = { + S5K5CCGX_REGSET(S5K5CCGX_PREVIEW_QCIF, + s5k5ccgx_176_144_Preview), + S5K5CCGX_REGSET(S5K5CCGX_PREVIEW_320x240, + s5k5ccgx_320_240_Preview), + S5K5CCGX_REGSET(S5K5CCGX_PREVIEW_CIF, s5k5ccgx_352_288_Preview), + S5K5CCGX_REGSET(S5K5CCGX_PREVIEW_528x432, + s5k5ccgx_528_432_Preview), + S5K5CCGX_REGSET(S5K5CCGX_PREVIEW_VGA, s5k5ccgx_640_480_Preview), + S5K5CCGX_REGSET(S5K5CCGX_PREVIEW_D1, s5k5ccgx_720_480_Preview), + S5K5CCGX_REGSET(S5K5CCGX_PREVIEW_SVGA, + s5k5ccgx_800_600_Preview), + S5K5CCGX_REGSET(PREVIEW_WIDE_SIZE, + S5K5CCGX_WIDE_PREVIEW_REG), + S5K5CCGX_REGSET(S5K5CCGX_PREVIEW_XGA, + s5k5ccgx_1024_768_Preview), + }, + .scene_mode = { + S5K5CCGX_REGSET(SCENE_MODE_NONE, s5k5ccgx_scene_off), + S5K5CCGX_REGSET(SCENE_MODE_PORTRAIT, s5k5ccgx_scene_portrait), + S5K5CCGX_REGSET(SCENE_MODE_NIGHTSHOT, s5k5ccgx_scene_nightshot), + S5K5CCGX_REGSET(SCENE_MODE_LANDSCAPE, s5k5ccgx_scene_landscape), + S5K5CCGX_REGSET(SCENE_MODE_SPORTS, s5k5ccgx_scene_sports), + S5K5CCGX_REGSET(SCENE_MODE_PARTY_INDOOR, s5k5ccgx_scene_party), + S5K5CCGX_REGSET(SCENE_MODE_BEACH_SNOW, s5k5ccgx_scene_beach), + S5K5CCGX_REGSET(SCENE_MODE_SUNSET, s5k5ccgx_scene_sunset), + S5K5CCGX_REGSET(SCENE_MODE_DUSK_DAWN, s5k5ccgx_scene_dawn), + S5K5CCGX_REGSET(SCENE_MODE_TEXT, s5k5ccgx_scene_text), + S5K5CCGX_REGSET(SCENE_MODE_CANDLE_LIGHT, s5k5ccgx_scene_candle), + }, + .saturation = { + S5K5CCGX_REGSET(SATURATION_MINUS_2, s5k5ccgx_saturation_m_2), + S5K5CCGX_REGSET(SATURATION_MINUS_1, s5k5ccgx_saturation_m_1), + S5K5CCGX_REGSET(SATURATION_DEFAULT, s5k5ccgx_saturation_0), + S5K5CCGX_REGSET(SATURATION_PLUS_1, s5k5ccgx_saturation_p_1), + S5K5CCGX_REGSET(SATURATION_PLUS_2, s5k5ccgx_saturation_p_2), + }, + .contrast = { + S5K5CCGX_REGSET(CONTRAST_MINUS_2, s5k5ccgx_contrast_m_2), + S5K5CCGX_REGSET(CONTRAST_MINUS_1, s5k5ccgx_contrast_m_1), + S5K5CCGX_REGSET(CONTRAST_DEFAULT, s5k5ccgx_contrast_0), + S5K5CCGX_REGSET(CONTRAST_PLUS_1, s5k5ccgx_contrast_p_1), + S5K5CCGX_REGSET(CONTRAST_PLUS_2, s5k5ccgx_contrast_p_2), + + }, + .sharpness = { + S5K5CCGX_REGSET(SHARPNESS_MINUS_2, s5k5ccgx_sharpness_m_2), + S5K5CCGX_REGSET(SHARPNESS_MINUS_1, s5k5ccgx_sharpness_m_1), + S5K5CCGX_REGSET(SHARPNESS_DEFAULT, s5k5ccgx_sharpness_0), + S5K5CCGX_REGSET(SHARPNESS_PLUS_1, s5k5ccgx_sharpness_p_1), + S5K5CCGX_REGSET(SHARPNESS_PLUS_2, s5k5ccgx_sharpness_p_2), + }, + .fps = { + S5K5CCGX_REGSET(I_FPS_0, s5k5ccgx_fps_auto), + S5K5CCGX_REGSET(I_FPS_15, s5k5ccgx_fps_15fix), + S5K5CCGX_REGSET(I_FPS_25, s5k5ccgx_fps_25fix), + S5K5CCGX_REGSET(I_FPS_30, s5k5ccgx_fps_30fix), + }, + .preview_return = S5K5CCGX_REGSET_TABLE(s5k5ccgx_preview_return), + + .flash_start = S5K5CCGX_REGSET_TABLE(s5k5ccgx_mainflash_start), + .flash_end = S5K5CCGX_REGSET_TABLE(s5k5ccgx_mainflash_end), + .af_pre_flash_start = + S5K5CCGX_REGSET_TABLE(s5k5ccgx_preflash_start), + .af_pre_flash_end = + S5K5CCGX_REGSET_TABLE(s5k5ccgx_preflash_end), + .flash_ae_set = + S5K5CCGX_REGSET_TABLE(s5k5ccgx_flash_ae_set), + .flash_ae_clear = + S5K5CCGX_REGSET_TABLE(s5k5ccgx_flash_ae_clear), + .ae_lock_on = + S5K5CCGX_REGSET_TABLE(s5k5ccgx_ae_lock), + .ae_lock_off = + S5K5CCGX_REGSET_TABLE(s5k5ccgx_ae_unlock), + .awb_lock_on = + S5K5CCGX_REGSET_TABLE(s5k5ccgx_awb_lock), + .awb_lock_off = + S5K5CCGX_REGSET_TABLE(s5k5ccgx_awb_unlock), + + .restore_cap = S5K5CCGX_REGSET_TABLE(s5k5ccgx_restore_capture_reg), + .change_wide_cap = S5K5CCGX_REGSET_TABLE(s5k5ccgx_change_wide_cap), +#ifdef CONFIG_VIDEO_S5K5CCGX_P8 + .set_lowlight_cap = S5K5CCGX_REGSET_TABLE(s5k5ccgx_set_lowlight_reg), +#endif + + .af_macro_mode = S5K5CCGX_REGSET_TABLE(s5k5ccgx_af_macro_on), + .af_normal_mode = S5K5CCGX_REGSET_TABLE(s5k5ccgx_af_normal_on), +#if !defined(CONFIG_VIDEO_S5K5CCGX_P2) + .af_night_normal_mode = + S5K5CCGX_REGSET_TABLE(s5k5ccgx_af_night_normal_on), +#endif +#ifdef CONFIG_VIDEO_S5K5CCGX_P4W + .af_off = S5K5CCGX_REGSET_TABLE(s5k5ccgx_af_off_reg), +#endif + .hd_af_start = S5K5CCGX_REGSET_TABLE(s5k5ccgx_720P_af_do), + .hd_first_af_start = S5K5CCGX_REGSET_TABLE(s5k5ccgx_1st_720P_af_do), + .single_af_start = S5K5CCGX_REGSET_TABLE(s5k5ccgx_af_do), + .capture_start = { + S5K5CCGX_REGSET(S5K5CCGX_CAPTURE_VGA, s5k5ccgx_snapshot_vga), + S5K5CCGX_REGSET(S5K5CCGX_CAPTURE_W2MP, s5k5ccgx_snapshot), + S5K5CCGX_REGSET(S5K5CCGX_CAPTURE_3MP, s5k5ccgx_snapshot), + }, + .init_reg = S5K5CCGX_REGSET_TABLE(s5k5ccgx_init_reg), + .get_esd_status = S5K5CCGX_REGSET_TABLE(s5k5ccgx_get_esd_reg), + .stream_stop = S5K5CCGX_REGSET_TABLE(s5k5ccgx_stream_stop_reg), + .get_light_level = S5K5CCGX_REGSET_TABLE(s5k5ccgx_get_light_status), + .get_iso = S5K5CCGX_REGSET_TABLE(s5k5ccgx_get_iso_reg), + .get_ae_stable = S5K5CCGX_REGSET_TABLE(s5k5ccgx_get_ae_stable_reg), + .get_shutterspeed = + S5K5CCGX_REGSET_TABLE(s5k5ccgx_get_shutterspeed_reg), + .update_preview = S5K5CCGX_REGSET_TABLE(s5k5ccgx_update_preview_reg), + .update_hd_preview = + S5K5CCGX_REGSET_TABLE(s5k5ccgx_update_hd_preview_reg), +#ifdef CONFIG_VIDEO_S5K5CCGX_P8 + .antibanding = S5K5CCGX_REGSET_TABLE(S5K5CCGX_ANTIBANDING_REG), +#endif +}; + +static const struct v4l2_mbus_framefmt capture_fmts[] = { + { + .code = V4L2_MBUS_FMT_FIXED, + .colorspace = V4L2_COLORSPACE_JPEG, + }, +}; + +#ifdef CONFIG_LOAD_FILE +static int loadFile(void) +{ + struct file *fp = NULL; + struct test *nextBuf = NULL; + + u8 *nBuf = NULL; + size_t file_size = 0, max_size = 0, testBuf_size = 0; + ssize_t nread = 0; + s32 check = 0, starCheck = 0; + s32 tmp_large_file = 0; + s32 i = 0; + int ret = 0; + loff_t pos; + + mm_segment_t fs = get_fs(); + set_fs(get_ds()); + + cam_info("%s: E\n", __func__); + + BUG_ON(testBuf); + + fp = filp_open(TUNING_FILE_PATH, O_RDONLY, 0); + if (IS_ERR(fp)) { + cam_err("file open error\n"); + return PTR_ERR(fp); + } + + file_size = (size_t) fp->f_path.dentry->d_inode->i_size; + max_size = file_size; + + cam_dbg("file_size = %d\n", file_size); + + nBuf = kmalloc(file_size, GFP_ATOMIC); + if (nBuf == NULL) { + cam_dbg("Fail to 1st get memory\n"); + nBuf = vmalloc(file_size); + if (nBuf == NULL) { + cam_err("ERR: nBuf Out of Memory\n"); + ret = -ENOMEM; + goto error_out; + } + tmp_large_file = 1; + } + + testBuf_size = sizeof(struct test) * file_size; + if (tmp_large_file) { + testBuf = (struct test *)vmalloc(testBuf_size); + large_file = 1; + } else { + testBuf = kmalloc(testBuf_size, GFP_ATOMIC); + if (testBuf == NULL) { + cam_dbg("Fail to get mem(%d bytes)\n", testBuf_size); + testBuf = (struct test *)vmalloc(testBuf_size); + large_file = 1; + } + } + if (testBuf == NULL) { + cam_err("ERR: Out of Memory\n"); + ret = -ENOMEM; + goto error_out; + } + + pos = 0; + memset(nBuf, 0, file_size); + memset(testBuf, 0, file_size * sizeof(struct test)); + + nread = vfs_read(fp, (char __user *)nBuf, file_size, &pos); + if (nread != file_size) { + cam_err("failed to read file ret = %d\n", nread); + ret = -1; + goto error_out; + } + + set_fs(fs); + + i = max_size; + + printk(KERN_DEBUG "i = %d\n", i); + + while (i) { + testBuf[max_size - i].data = *nBuf; + if (i != 1) { + testBuf[max_size - i].nextBuf = + &testBuf[max_size - i + 1]; + } else { + testBuf[max_size - i].nextBuf = NULL; + break; + } + i--; + nBuf++; + } + + i = max_size; + nextBuf = &testBuf[0]; + +#if 1 + while (i - 1) { + if (!check && !starCheck) { + if (testBuf[max_size - i].data == '/') { + if (testBuf[max_size-i].nextBuf != NULL) { + if (testBuf[max_size-i].nextBuf->data + == '/') { + check = 1;/* when find '//' */ + i--; + } else if ( + testBuf[max_size-i].nextBuf->data + == '*') { + /* when find '/ *' */ + starCheck = 1; + i--; + } + } else + break; + } + if (!check && !starCheck) { + /* ignore '\t' */ + if (testBuf[max_size - i].data != '\t') { + nextBuf->nextBuf = &testBuf[max_size-i]; + nextBuf = &testBuf[max_size - i]; + } + } + } else if (check && !starCheck) { + if (testBuf[max_size - i].data == '/') { + if (testBuf[max_size-i].nextBuf != NULL) { + if (testBuf[max_size-i].nextBuf->data + == '*') { + /* when find '/ *' */ + starCheck = 1; + check = 0; + i--; + } + } else + break; + } + + /* when find '\n' */ + if (testBuf[max_size - i].data == '\n' && check) { + check = 0; + nextBuf->nextBuf = &testBuf[max_size - i]; + nextBuf = &testBuf[max_size - i]; + } + + } else if (!check && starCheck) { + if (testBuf[max_size - i].data == '*') { + if (testBuf[max_size-i].nextBuf != NULL) { + if (testBuf[max_size-i].nextBuf->data + == '/') { + /* when find '* /' */ + starCheck = 0; + i--; + } + } else + break; + } + } + + i--; + + if (i < 2) { + nextBuf = NULL; + break; + } + + if (testBuf[max_size - i].nextBuf == NULL) { + nextBuf = NULL; + break; + } + } +#endif + +#if 0 /* for print */ + cam_dbg("i = %d\n", i); + nextBuf = &testBuf[0]; + while (1) { + if (nextBuf->nextBuf == NULL) + break; + cam_dbg("%c", nextBuf->data); + nextBuf = nextBuf->nextBuf; + } +#endif + +error_out: + tmp_large_file ? vfree(nBuf) : kfree(nBuf); + + if (fp) + filp_close(fp, current->files); + return ret; +} + + +#endif + +/** + * s5k5ccgx_i2c_read_twobyte: Read 2 bytes from sensor + */ +static int s5k5ccgx_i2c_read_twobyte(struct i2c_client *client, + u16 subaddr, u16 *data) +{ + int err; + u8 buf[2]; + struct i2c_msg msg[2]; + + cpu_to_be16s(&subaddr); + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 2; + msg[0].buf = (u8 *)&subaddr; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 2; + msg[1].buf = buf; + + err = i2c_transfer(client->adapter, msg, 2); + CHECK_ERR_COND_MSG(err != 2, -EIO, "fail to read register\n"); + + *data = ((buf[0] << 8) | buf[1]); + + return 0; +} + +/** + * s5k5ccgx_i2c_write_twobyte: Write (I2C) multiple bytes to the camera sensor + * @client: pointer to i2c_client + * @cmd: command register + * @w_data: data to be written + * @w_len: length of data to be written + * + * Returns 0 on success, <0 on error + */ +static int s5k5ccgx_i2c_write_twobyte(struct i2c_client *client, + u16 addr, u16 w_data) +{ + int retry_count = 5; + int ret = 0; + u8 buf[4] = {0,}; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = 4, + .buf = buf, + }; + + buf[0] = addr >> 8; + buf[1] = addr; + buf[2] = w_data >> 8; + buf[3] = w_data & 0xff; + +#if (0) + s5k5ccgx_debug(S5K5CCGX_DEBUG_I2C, "%s : W(0x%02X%02X%02X%02X)\n", + __func__, buf[0], buf[1], buf[2], buf[3]); +#else + /* cam_dbg("I2C writing: 0x%02X%02X%02X%02X\n", + buf[0], buf[1], buf[2], buf[3]); */ +#endif + + do { + ret = i2c_transfer(client->adapter, &msg, 1); + if (likely(ret == 1)) + break; + msleep(POLL_TIME_MS); + cam_err("%s: ERROR(%d), write (%04X, %04X), retry %d.\n", + __func__, ret, addr, w_data, retry_count); + } while (retry_count-- > 0); + + CHECK_ERR_COND_MSG(ret != 1, -EIO, "I2C does not working.\n\n"); + + return 0; +} + +/* PX: */ +#ifdef CONFIG_LOAD_FILE +static int s5k5ccgx_write_regs_from_sd(struct v4l2_subdev *sd, + const u8 s_name[]) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct test *tempData = NULL; + + int ret = -EAGAIN; + u32 temp; + u32 delay = 0; + u8 data[11]; + s32 searched = 0; + size_t size = strlen(s_name); + s32 i; +#ifdef DEBUG_WRITE_REGS + u8 regs_name[128] = {0,}; + + BUG_ON(size > sizeof(regs_name)); +#endif + + cam_dbg("%s: E size = %d, string = %s\n", __func__, size, s_name); + tempData = &testBuf[0]; + while (!searched) { + searched = 1; + for (i = 0; i < size; i++) { + if (tempData->data != s_name[i]) { + searched = 0; + break; + } +#ifdef DEBUG_WRITE_REGS + regs_name[i] = tempData->data; +#endif + tempData = tempData->nextBuf; + } +#ifdef DEBUG_WRITE_REGS + if (i > 9) { + regs_name[i] = '\0'; + cam_dbg("Searching: regs_name = %s\n", regs_name); + } +#endif + tempData = tempData->nextBuf; + } + /* structure is get..*/ +#ifdef DEBUG_WRITE_REGS + regs_name[i] = '\0'; + cam_dbg("Searched regs_name = %s\n\n", regs_name); +#endif + + while (1) { + if (tempData->data == '{') + break; + else + tempData = tempData->nextBuf; + } + + while (1) { + searched = 0; + while (1) { + if (tempData->data == 'x') { + /* get 10 strings.*/ + data[0] = '0'; + for (i = 1; i < 11; i++) { + data[i] = tempData->data; + tempData = tempData->nextBuf; + } + /*cam_dbg("%s\n", data);*/ + temp = simple_strtoul(data, NULL, 16); + break; + } else if (tempData->data == '}') { + searched = 1; + break; + } else + tempData = tempData->nextBuf; + + if (tempData->nextBuf == NULL) + return -1; + } + + if (searched) + break; + + if ((temp & S5K5CCGX_DELAY) == S5K5CCGX_DELAY) { + delay = temp & 0x0FFFF; + debug_msleep(sd, delay); + continue; + } + + /* cam_dbg("I2C writing: 0x%08X,\n",temp);*/ + ret = s5k5ccgx_i2c_write_twobyte(client, + (temp >> 16), (u16)temp); + + /* In error circumstances */ + /* Give second shot */ + if (unlikely(ret)) { + dev_info(&client->dev, + "s5k5ccgx i2c retry one more time\n"); + ret = s5k5ccgx_i2c_write_twobyte(client, + (temp >> 16), (u16)temp); + + /* Give it one more shot */ + if (unlikely(ret)) { + dev_info(&client->dev, + "s5k5ccgx i2c retry twice\n"); + ret = s5k5ccgx_i2c_write_twobyte(client, + (temp >> 16), (u16)temp); + } + } + } + + return ret; +} +#endif + +/* Write register + * If success, return value: 0 + * If fail, return value: -EIO + */ +static int s5k5ccgx_write_regs(struct v4l2_subdev *sd, const u32 regs[], + int size) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u16 delay = 0; + int i, err = 0; + + for (i = 0; i < size; i++) { + if ((regs[i] & S5K5CCGX_DELAY) == S5K5CCGX_DELAY) { + delay = regs[i] & 0xFFFF; + debug_msleep(sd, delay); + continue; + } + + err = s5k5ccgx_i2c_write_twobyte(client, + (regs[i] >> 16), regs[i]); + CHECK_ERR_MSG(err, "write registers\n") + } + + return 0; +} + +#if 0 +static int s5k5ccgx_i2c_write_block(struct v4l2_subdev *sd, u8 *buf, u32 size) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int retry_count = 5; + int ret = 0; + struct i2c_msg msg = {client->addr, 0, size, buf}; + +#ifdef CONFIG_VIDEO_S5K5CCGX_DEBUG + if (s5k5ccgx_debug_mask & S5K5CCGX_DEBUG_I2C_BURSTS) { + if ((buf[0] == 0x0F) && (buf[1] == 0x12)) + pr_info("%s : data[0,1] = 0x%02X%02X," + " total data size = %d\n", + __func__, buf[2], buf[3], size-2); + else + pr_info("%s : 0x%02X%02X%02X%02X\n", + __func__, buf[0], buf[1], buf[2], buf[3]); + } +#endif + + do { + ret = i2c_transfer(client->adapter, &msg, 1); + if (likely(ret == 1)) + break; + msleep(POLL_TIME_MS); + } while (retry_count-- > 0); + if (ret != 1) { + dev_err(&client->dev, "%s: I2C is not working.\n", __func__); + return -EIO; + } + + return 0; +} +#endif + +#define BURST_MODE_BUFFER_MAX_SIZE 2700 +u8 s5k5ccgx_burstmode_buf[BURST_MODE_BUFFER_MAX_SIZE]; + +/* PX: */ +static int s5k5ccgx_burst_write_regs(struct v4l2_subdev *sd, + const u32 list[], u32 size, char *name) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int err = -EINVAL; + int i = 0, idx = 0; + u16 subaddr = 0, next_subaddr = 0, value = 0; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = 0, + .buf = s5k5ccgx_burstmode_buf, + }; + + cam_trace("E\n"); + + for (i = 0; i < size; i++) { + CHECK_ERR_COND_MSG((idx > (BURST_MODE_BUFFER_MAX_SIZE - 10)), + err, "BURST MOD buffer overflow!\n") + + subaddr = (list[i] & 0xFFFF0000) >> 16; + if (subaddr == 0x0F12) + next_subaddr = (list[i+1] & 0xFFFF0000) >> 16; + + value = list[i] & 0x0000FFFF; + + switch (subaddr) { + case 0x0F12: + /* make and fill buffer for burst mode write. */ + if (idx == 0) { + s5k5ccgx_burstmode_buf[idx++] = 0x0F; + s5k5ccgx_burstmode_buf[idx++] = 0x12; + } + s5k5ccgx_burstmode_buf[idx++] = value >> 8; + s5k5ccgx_burstmode_buf[idx++] = value & 0xFF; + + /* write in burstmode*/ + if (next_subaddr != 0x0F12) { + msg.len = idx; + err = i2c_transfer(client->adapter, + &msg, 1) == 1 ? 0 : -EIO; + CHECK_ERR_MSG(err, "i2c_transfer\n"); + /* cam_dbg("s5k5ccgx_sensor_burst_write, + idx = %d\n", idx); */ + idx = 0; + } + break; + + case 0xFFFF: + debug_msleep(sd, value); + break; + + default: + idx = 0; + err = s5k5ccgx_i2c_write_twobyte(client, + subaddr, value); + CHECK_ERR_MSG(err, "i2c_write_twobytes\n"); + break; + } + } + + return 0; +} + +/* PX: */ +static int s5k5ccgx_set_from_table(struct v4l2_subdev *sd, + const char *setting_name, + const struct s5k5ccgx_regset_table *table, + u32 table_size, s32 index) +{ + int err = 0; + + /* cam_dbg("%s: set %s index %d\n", + __func__, setting_name, index); */ + CHECK_ERR_COND_MSG(((index < 0) || (index >= table_size)), + -EINVAL, "index(%d) out of range[0:%d] for table for %s\n", + index, table_size, setting_name); + + table += index; + CHECK_ERR_COND_MSG(!table->reg, -EFAULT, \ + "table=%s, index=%d, reg = NULL\n", setting_name, index); + +#ifdef CONFIG_LOAD_FILE + cam_dbg("%s: \"%s\", reg_name=%s\n", __func__, + setting_name, table->name); + return s5k5ccgx_write_regs_from_sd(sd, table->name); + +#else /* CONFIG_LOAD_FILE */ + +# ifdef DEBUG_WRITE_REGS + cam_dbg("%s: \"%s\", reg_name=%s\n", __func__, + setting_name, table->name); +# endif /* DEBUG_WRITE_REGS */ + + err = s5k5ccgx_write_regs(sd, table->reg, table->array_size); + CHECK_ERR_MSG(err, "write regs(%s), err=%d\n", setting_name, err); + + return 0; +#endif /* CONFIG_LOAD_FILE */ +} + +/* PX: */ +static inline int s5k5ccgx_save_ctrl(struct v4l2_subdev *sd, + struct v4l2_control *ctrl) +{ + int ctrl_cnt = ARRAY_SIZE(s5k5ccgx_ctrls); + int i; + + /* cam_trace("E, Ctrl-ID = 0x%X", ctrl->id);*/ + + for (i = 0; i < ctrl_cnt; i++) { + if (ctrl->id == s5k5ccgx_ctrls[i].id) { + s5k5ccgx_ctrls[i].value = ctrl->value; + break; + } + } + + if (unlikely(i >= ctrl_cnt)) + cam_trace("WARNING, not saved ctrl-ID=0x%X\n", ctrl->id); + + return 0; +} + +/** + * s5k5ccgx_is_hwflash_on - check whether flash device is on + * + * Refer to state->flash_on to check whether flash is in use in driver. + */ +static inline int s5k5ccgx_is_hwflash_on(struct v4l2_subdev *sd) +{ + struct s5k5ccgx_state *state = to_state(sd); + +#ifdef S5K5CCGX_SUPPORT_FLASH + return state->pdata->is_flash_on(); +#else + return 0; +#endif +} + +/** + * s5k5ccgx_flash_en - contro Flash LED + * @mode: S5K5CCGX_FLASH_MODE_NORMAL or S5K5CCGX_FLASH_MODE_MOVIE + * @onoff: S5K5CCGX_FLASH_ON or S5K5CCGX_FLASH_OFF + */ +static int s5k5ccgx_flash_en(struct v4l2_subdev *sd, s32 mode, s32 onoff) +{ + struct s5k5ccgx_state *state = to_state(sd); + + if (unlikely(state->ignore_flash)) { + cam_warn("WARNING, we ignore flash command.\n"); + return 0; + } + +#ifdef S5K5CCGX_SUPPORT_FLASH + return state->pdata->flash_en(mode, onoff); +#endif + return 0; +} + +/** + * s5k5ccgx_flash_torch - turn flash on/off as torch for preflash, recording + * @onoff: S5K5CCGX_FLASH_ON or S5K5CCGX_FLASH_OFF + * + * This func set state->flash_on properly. + */ +static inline int s5k5ccgx_flash_torch(struct v4l2_subdev *sd, s32 onoff) +{ + struct s5k5ccgx_state *state = to_state(sd); + int err = 0; + + err = s5k5ccgx_flash_en(sd, S5K5CCGX_FLASH_MODE_MOVIE, onoff); + state->flash_on = (onoff == S5K5CCGX_FLASH_ON) ? 1 : 0; + + return err; +} + +/** + * s5k5ccgx_flash_oneshot - turn main flash on for capture + * @onoff: S5K5CCGX_FLASH_ON or S5K5CCGX_FLASH_OFF + * + * Main flash is turn off automatically in some milliseconds. + */ +static inline int s5k5ccgx_flash_oneshot(struct v4l2_subdev *sd, s32 onoff) +{ + struct s5k5ccgx_state *state = to_state(sd); + int err = 0; + + err = s5k5ccgx_flash_en(sd, S5K5CCGX_FLASH_MODE_NORMAL, onoff); + + /* The flash_on here is only used for EXIF */ + state->flash_on = (onoff == S5K5CCGX_FLASH_ON) ? 1 : 0; + + return err; +} + +/* PX: Set scene mode */ +static int s5k5ccgx_set_scene_mode(struct v4l2_subdev *sd, s32 val) +{ + struct s5k5ccgx_state *state = to_state(sd); + int err = -ENODEV; + + cam_trace("E, value %d\n", val); + + if (state->scene_mode == val) + return 0; + + /* when scene mode is switched, + * we frist have to write scene_off. + */ + if (state->scene_mode != SCENE_MODE_NONE) + err = s5k5ccgx_set_from_table(sd, "scene_mode", + state->regs->scene_mode, + ARRAY_SIZE(state->regs->scene_mode), SCENE_MODE_NONE); + + if (val != SCENE_MODE_NONE) + err = s5k5ccgx_set_from_table(sd, "scene_mode", + state->regs->scene_mode, + ARRAY_SIZE(state->regs->scene_mode), val); + + state->scene_mode = val; + + cam_trace("X\n"); + return 0; +} + +/* PX: Set brightness */ +static int s5k5ccgx_set_exposure(struct v4l2_subdev *sd, s32 val) +{ + struct s5k5ccgx_state *state = to_state(sd); + int err = -EINVAL; + + if ((val < EV_MINUS_4) || (val > EV_PLUS_4)) { + cam_err("%s: ERROR, invalid value(%d)\n", __func__, val); + return -EINVAL; + } + + err = s5k5ccgx_set_from_table(sd, "brightness", state->regs->ev, + ARRAY_SIZE(state->regs->ev), GET_EV_INDEX(val)); + + return err; +} + +/* PX: Check light level */ +static u32 s5k5ccgx_get_light_level(struct v4l2_subdev *sd, u32 *light_level) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct s5k5ccgx_state *state = to_state(sd); + u16 val_lsb = 0; + u16 val_msb = 0; + int err = -ENODEV; + + err = s5k5ccgx_set_from_table(sd, "get_light_level", + &state->regs->get_light_level, 1, 0); + CHECK_ERR_MSG(err, "fail to get light level\n"); + + err = s5k5ccgx_i2c_read_twobyte(client, 0x0F12, &val_lsb); + err = s5k5ccgx_i2c_read_twobyte(client, 0x0F12, &val_msb); + CHECK_ERR_MSG(err, "fail to read light level\n"); + + *light_level = val_lsb | (val_msb<<16); + + /* cam_trace("X, light level = 0x%X", *light_level); */ + + return 0; +} + +static int s5k5ccgx_set_capture_size(struct v4l2_subdev *sd) +{ + struct s5k5ccgx_state *state = to_state(sd); + int err = -EINVAL; + + if (likely(!state->wide_cmd)) + return 0; + + cam_err("%s: WARNING, reconfiguring sensor register.\n\n", __func__); + + switch (state->wide_cmd) { + case WIDE_REQ_CHANGE: + cam_info("%s: Wide Capture setting\n", __func__); + err = s5k5ccgx_set_from_table(sd, "change_wide_cap", + &state->regs->change_wide_cap, 1, 0); + break; + + case WIDE_REQ_RESTORE: + cam_info("%s: Restore capture setting\n", __func__); + err = s5k5ccgx_set_from_table(sd, "restore_capture", + &state->regs->restore_cap, 1, 0); + break; + + default: + cam_err("%s: WARNING, invalid argument(%d)\n", + __func__, state->wide_cmd); + break; + } + + /* Don't forget the below codes. + * We set here state->preview to NULL after reconfiguring + * capure config if capture ratio does't match with preview ratio. + */ + state->preview = NULL; + CHECK_ERR(err); + + return 0; +} + +/* PX: Set sensor mode */ +static int s5k5ccgx_set_sensor_mode(struct v4l2_subdev *sd, s32 val) +{ + struct s5k5ccgx_state *state = to_state(sd); + + state->hd_videomode = 0; + + switch (val) { + case SENSOR_MOVIE: + /* We does not support movie mode when in VT. */ + if (state->vt_mode) { + state->sensor_mode = SENSOR_CAMERA; + cam_err("%s: ERROR, Not support movie\n", __func__); + break; + } + /* We do not break. */ + + case SENSOR_CAMERA: + state->sensor_mode = val; + break; + + case 2: /* 720p HD video mode */ + state->sensor_mode = SENSOR_MOVIE; + state->hd_videomode = 1; + break; + + default: + cam_err("%s: ERROR, Not support.(%d)\n", __func__, val); + state->sensor_mode = SENSOR_CAMERA; + WARN_ON(1); + break; + } + + return 0; +} + +/* PX: Set framerate */ +static int s5k5ccgx_set_frame_rate(struct v4l2_subdev *sd, s32 fps) +{ + struct s5k5ccgx_state *state = to_state(sd); + int err = -EIO; + int i = 0, fps_index = -1; + + cam_info("set frame rate %d\n", fps); + + for (i = 0; i < ARRAY_SIZE(s5k5ccgx_framerates); i++) { + if (fps == s5k5ccgx_framerates[i].fps) { + fps_index = s5k5ccgx_framerates[i].index; + state->fps = fps; + state->req_fps = -1; + break; + } + } + + if (unlikely(fps_index < 0)) { + cam_err("%s: WARNING, Not supported FPS(%d)\n", __func__, fps); + return 0; + } + + if (!state->hd_videomode) { + err = s5k5ccgx_set_from_table(sd, "fps", state->regs->fps, + ARRAY_SIZE(state->regs->fps), fps_index); + CHECK_ERR_MSG(err, "fail to set framerate\n") + } + + return 0; +} + +static int s5k5ccgx_set_ae_lock(struct v4l2_subdev *sd, s32 val, bool force) +{ + struct s5k5ccgx_state *state = to_state(sd); + int err = 0; + + switch (val) { + case AE_LOCK: + if (state->focus.touch) + return 0; + + err = s5k5ccgx_set_from_table(sd, "ae_lock_on", + &state->regs->ae_lock_on, 1, 0); + WARN_ON(state->focus.ae_lock); + state->focus.ae_lock = 1; + break; + + case AE_UNLOCK: + if (unlikely(!force && !state->focus.ae_lock)) + return 0; + + err = s5k5ccgx_set_from_table(sd, "ae_lock_off", + &state->regs->ae_lock_off, 1, 0); + state->focus.ae_lock = 0; + break; + + default: + cam_err("%s: WARNING, invalid argument(%d)\n", __func__, val); + } + + CHECK_ERR_MSG(err, "fail to lock AE(%d), err=%d\n", val, err); + + return 0; +} + +static int s5k5ccgx_set_awb_lock(struct v4l2_subdev *sd, s32 val, bool force) +{ + struct s5k5ccgx_state *state = to_state(sd); + int err = 0; + + switch (val) { + case AWB_LOCK: + if (state->flash_on || + (state->wb_mode != WHITE_BALANCE_AUTO)) + return 0; + + err = s5k5ccgx_set_from_table(sd, "awb_lock_on", + &state->regs->awb_lock_on, 1, 0); + WARN_ON(state->focus.awb_lock); + state->focus.awb_lock = 1; + break; + + case AWB_UNLOCK: + if (unlikely(!force && !state->focus.awb_lock)) + return 0; + + err = s5k5ccgx_set_from_table(sd, "awb_lock_off", + &state->regs->awb_lock_off, 1, 0); + state->focus.awb_lock = 0; + break; + + default: + cam_err("%s: WARNING, invalid argument(%d)\n", __func__, val); + } + + CHECK_ERR_MSG(err, "fail to lock AWB(%d), err=%d\n", val, err); + + return 0; +} + +/* PX: Set AE, AWB Lock */ +static int s5k5ccgx_set_lock(struct v4l2_subdev *sd, s32 lock, bool force) +{ + int err = -EIO; + + cam_trace("%s\n", lock ? "on" : "off"); + if (unlikely((u32)lock >= AEAWB_LOCK_MAX)) { + cam_err("%s: ERROR, invalid argument\n", __func__); + return -EINVAL; + } + + err = s5k5ccgx_set_ae_lock(sd, (lock == AEAWB_LOCK) ? + AE_LOCK : AE_UNLOCK, force); + if (unlikely(err)) + goto out_err; + + err = s5k5ccgx_set_awb_lock(sd, (lock == AEAWB_LOCK) ? + AWB_LOCK : AWB_UNLOCK, force); + if (unlikely(err)) + goto out_err; + + cam_trace("X\n"); + return 0; + +out_err: + cam_err("%s: ERROR, failed to set lock\n", __func__); + return err; +} + +#ifdef CONFIG_VIDEO_S5K5CCGX_P4W +static int s5k5ccgx_set_af_softlanding(struct v4l2_subdev *sd) +{ + struct s5k5ccgx_state *state = to_state(sd); + int err = -EINVAL; + + cam_trace("E\n"); + + err = s5k5ccgx_set_from_table(sd, "af_off", + &state->regs->af_off, 1, 0); + CHECK_ERR_MSG(err, "fail to set softlanding\n"); + + cam_trace("X\n"); + return 0; +} +#endif + +/* PX: */ +static int s5k5ccgx_return_focus(struct v4l2_subdev *sd) +{ + struct s5k5ccgx_state *state = to_state(sd); + int err = -EINVAL; + + cam_trace("E\n"); + + switch (state->focus.mode) { + case FOCUS_MODE_MACRO: + err = s5k5ccgx_set_from_table(sd, "af_macro_mode", + &state->regs->af_macro_mode, 1, 0); + break; + + default: +#if !defined(CONFIG_VIDEO_S5K5CCGX_P2) + if (state->scene_mode == SCENE_MODE_NIGHTSHOT) + err = s5k5ccgx_set_from_table(sd, + "af_night_normal_mode", + &state->regs->af_night_normal_mode, 1, 0); + else +#endif + err = s5k5ccgx_set_from_table(sd, + "af_norma_mode", + &state->regs->af_normal_mode, 1, 0); + break; + } + + CHECK_ERR(err); + return 0; +} + +#ifdef DEBUG_FILTER_DATA +static void __used s5k5ccgx_display_AF_win_info(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct s5k5ccgx_rect first_win = {0, 0, 0, 0}; + struct s5k5ccgx_rect second_win = {0, 0, 0, 0}; + + s5k5ccgx_i2c_write_twobyte(client, 0x002C, 0x7000); + s5k5ccgx_i2c_write_twobyte(client, 0x002E, 0x022C); + s5k5ccgx_i2c_read_twobyte(client, 0x0F12, (u16 *)&first_win.x); + s5k5ccgx_i2c_write_twobyte(client, 0x002C, 0x7000); + s5k5ccgx_i2c_write_twobyte(client, 0x002E, 0x022E); + s5k5ccgx_i2c_read_twobyte(client, 0x0F12, (u16 *)&first_win.y); + s5k5ccgx_i2c_write_twobyte(client, 0x002C, 0x7000); + s5k5ccgx_i2c_write_twobyte(client, 0x002E, 0x0230); + s5k5ccgx_i2c_read_twobyte(client, 0x0F12, (u16 *)&first_win.width); + s5k5ccgx_i2c_write_twobyte(client, 0x002C, 0x7000); + s5k5ccgx_i2c_write_twobyte(client, 0x002E, 0x0232); + s5k5ccgx_i2c_read_twobyte(client, 0x0F12, (u16 *)&first_win.height); + + s5k5ccgx_i2c_write_twobyte(client, 0x002C, 0x7000); + s5k5ccgx_i2c_write_twobyte(client, 0x002E, 0x0234); + s5k5ccgx_i2c_read_twobyte(client, 0x0F12, (u16 *)&second_win.x); + s5k5ccgx_i2c_write_twobyte(client, 0x002C, 0x7000); + s5k5ccgx_i2c_write_twobyte(client, 0x002E, 0x0236); + s5k5ccgx_i2c_read_twobyte(client, 0x0F12, (u16 *)&second_win.y); + s5k5ccgx_i2c_write_twobyte(client, 0x002C, 0x7000); + s5k5ccgx_i2c_write_twobyte(client, 0x002E, 0x0238); + s5k5ccgx_i2c_read_twobyte(client, 0x0F12, (u16 *)&second_win.width); + s5k5ccgx_i2c_write_twobyte(client, 0x002C, 0x7000); + s5k5ccgx_i2c_write_twobyte(client, 0x002E, 0x023A); + s5k5ccgx_i2c_read_twobyte(client, 0x0F12, (u16 *)&second_win.height); + + cam_info("------- AF Window info -------\n"); + cam_info("Firtst Window: (%4d %4d %4d %4d)\n", + first_win.x, first_win.y, first_win.width, first_win.height); + cam_info("Second Window: (%4d %4d %4d %4d)\n", + second_win.x, second_win.y, + second_win.width, second_win.height); + cam_info("------- AF Window info -------\n\n"); +} +#endif + +/* PX: Prepare AF Flash */ +static int s5k5ccgx_af_start_preflash(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct s5k5ccgx_state *state = to_state(sd); + u16 read_value = 0; + int count = 0; + int err = 0; + + cam_trace("E\n"); + + if (state->sensor_mode == SENSOR_MOVIE) + return 0; + + cam_dbg("Start SINGLE AF, flash mode %d\n", state->flash_mode); + + /* in case user calls auto_focus repeatedly without a cancel + * or a capture, we need to cancel here to allow ae_awb + * to work again, or else we could be locked forever while + * that app is running, which is not the expected behavior. + */ + err = s5k5ccgx_set_lock(sd, AEAWB_UNLOCK, true); + CHECK_ERR_MSG(err, "fail to set lock\n"); + + state->focus.preflash = PREFLASH_OFF; + state->light_level = 0xFFFFFFFF; + + s5k5ccgx_get_light_level(sd, &state->light_level); + + switch (state->flash_mode) { + case FLASH_MODE_AUTO: + if (state->light_level >= FLASH_LOW_LIGHT_LEVEL) { + /* flash not needed */ + break; + } + + case FLASH_MODE_ON: + s5k5ccgx_set_from_table(sd, "af_pre_flash_start", + &state->regs->af_pre_flash_start, 1, 0); + s5k5ccgx_set_from_table(sd, "flash_ae_set", + &state->regs->flash_ae_set, 1, 0); + s5k5ccgx_flash_torch(sd, S5K5CCGX_FLASH_ON); + state->focus.preflash = PREFLASH_ON; + break; + + case FLASH_MODE_OFF: + if (state->light_level < FLASH_LOW_LIGHT_LEVEL) + state->one_frame_delay_ms = ONE_FRAME_DELAY_MS_LOW; + break; + + default: + break; + } + + /* We wait for 200ms after pre flash on. + * check whether AE is stable.*/ + msleep(200); + + /* Check AE-stable */ + if (state->focus.preflash == PREFLASH_ON) { + /* Do checking AE-stable */ + for (count = 0; count < AE_STABLE_SEARCH_COUNT; count++) { + if (state->focus.start == AUTO_FOCUS_OFF) { + cam_info("af_start_preflash: \ + AF is cancelled!\n"); + state->focus.status = AF_RESULT_CANCELLED; + break; + } + + s5k5ccgx_set_from_table(sd, "get_ae_stable", + &state->regs->get_ae_stable, 1, 0); + s5k5ccgx_i2c_read_twobyte(client, 0x0F12, &read_value); + + /* af_dbg("Check AE-Stable: 0x%04X\n", read_value); */ + if (read_value == 0x0001) { + af_dbg("AE-stable success," + " count=%d, delay=%dms\n", count, + state->one_frame_delay_ms); + break; + } + + msleep(state->one_frame_delay_ms); + } + + /* restore write mode */ + s5k5ccgx_i2c_write_twobyte(client, 0x0028, 0x7000); + + if (unlikely(count >= AE_STABLE_SEARCH_COUNT)) { + cam_err("%s: ERROR, AE unstable." + " count=%d, delay=%dms\n", + __func__, count, state->one_frame_delay_ms); + /* return -ENODEV; */ + } + } else if (state->focus.start == AUTO_FOCUS_OFF) { + cam_info("af_start_preflash: AF is cancelled!\n"); + state->focus.status = AF_RESULT_CANCELLED; + } + + /* If AF cancel, finish pre-flash process. */ + if (state->focus.status == AF_RESULT_CANCELLED) { + if (state->focus.preflash == PREFLASH_ON) { + s5k5ccgx_set_from_table(sd, "af_pre_flash_end", + &state->regs->af_pre_flash_end, 1, 0); + s5k5ccgx_set_from_table(sd, "flash_ae_clear", + &state->regs->flash_ae_clear, 1, 0); + s5k5ccgx_flash_torch(sd, S5K5CCGX_FLASH_OFF); + state->focus.preflash = PREFLASH_NONE; + } + + if (state->focus.touch) + state->focus.touch = 0; + } + + cam_trace("X\n"); + + return 0; +} + +/* PX: Do AF */ +static int s5k5ccgx_do_af(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct s5k5ccgx_state *state = to_state(sd); + u16 read_value = 0; + u32 count = 0; + + cam_trace("E\n"); + + /* AE, AWB Lock */ + s5k5ccgx_set_lock(sd, AEAWB_LOCK, false); + + if (state->sensor_mode == SENSOR_MOVIE) { + s5k5ccgx_set_from_table(sd, "hd_af_start", + &state->regs->hd_af_start, 1, 0); + + cam_info("%s : 720P Auto Focus Operation\n\n", __func__); + } else + s5k5ccgx_set_from_table(sd, "single_af_start", + &state->regs->single_af_start, 1, 0); + + /* Sleep while 2frame */ + if (state->hd_videomode) + msleep(100); /* 100ms */ + else if (state->scene_mode == SCENE_MODE_NIGHTSHOT) + msleep(ONE_FRAME_DELAY_MS_NIGHTMODE * 2); /* 330ms */ + else + msleep(ONE_FRAME_DELAY_MS_LOW * 2); /* 200ms */ + + /* AF Searching */ + cam_dbg("AF 1st search\n"); + + /*1st search*/ + for (count = 0; count < FIRST_AF_SEARCH_COUNT; count++) { + if (state->focus.start == AUTO_FOCUS_OFF) { + cam_dbg("do_af: AF is cancelled while doing(1st)\n"); + state->focus.status = AF_RESULT_CANCELLED; + goto check_done; + } + + read_value = 0x0; + s5k5ccgx_i2c_write_twobyte(client, 0x002C, 0x7000); + s5k5ccgx_i2c_write_twobyte(client, 0x002E, 0x2D12); + s5k5ccgx_i2c_read_twobyte(client, 0x0F12, &read_value); + af_dbg("1st AF status(%02d) = 0x%04X\n", + count, read_value); + if (read_value != 0x01) + break; + + msleep(state->one_frame_delay_ms); + } + + if (read_value != 0x02) { + cam_err("%s: ERROR, 1st AF failed. count=%d, read_val=0x%X\n\n", + __func__, count, read_value); + state->focus.status = AF_RESULT_FAILED; + goto check_done; + } + + /*2nd search*/ + cam_dbg("AF 2nd search\n"); + for (count = 0; count < SECOND_AF_SEARCH_COUNT; count++) { + msleep(state->one_frame_delay_ms); + + if (state->focus.start == AUTO_FOCUS_OFF) { + cam_dbg("do_af: AF is cancelled while doing(2nd)\n"); + state->focus.status = AF_RESULT_CANCELLED; + goto check_done; + } + + read_value = 0x0FFFF; + s5k5ccgx_i2c_write_twobyte(client, 0x002C, 0x7000); + s5k5ccgx_i2c_write_twobyte(client, 0x002E, 0x1F2F); + s5k5ccgx_i2c_read_twobyte(client, 0x0F12, &read_value); + af_dbg("2nd AF status(%02d) = 0x%04X\n", + count, read_value); + if ((read_value & 0x0ff00) == 0x0) + break; + } + + if (count >= SECOND_AF_SEARCH_COUNT) { + /* 0x01XX means "Not Finish". */ + cam_err("%s: ERROR, 2nd AF failed. read_val=0x%X\n\n", + __func__, read_value & 0x0ff00); + state->focus.status = AF_RESULT_FAILED; + goto check_done; + } + + cam_info("AF Success!\n"); + state->focus.status = AF_RESULT_SUCCESS; + +check_done: + /* restore write mode */ + + /* We only unlocked AE,AWB in case of being cancelled. + * But we now unlock it unconditionally if AF is started, + */ + if (state->focus.status == AF_RESULT_CANCELLED) { + cam_dbg("%s: Single AF cancelled.\n", __func__); + s5k5ccgx_set_lock(sd, AEAWB_UNLOCK, false); + } else { + state->focus.start = AUTO_FOCUS_OFF; + cam_dbg("%s: Single AF finished\n", __func__); + } + + if ((state->focus.preflash == PREFLASH_ON) && + (state->sensor_mode == SENSOR_CAMERA)) { + s5k5ccgx_set_from_table(sd, "af_pre_flash_end", + &state->regs->af_pre_flash_end, 1, 0); + s5k5ccgx_set_from_table(sd, "flash_ae_clear", + &state->regs->flash_ae_clear, 1, 0); + s5k5ccgx_flash_torch(sd, S5K5CCGX_FLASH_OFF); + if (state->focus.status == AF_RESULT_CANCELLED) { + state->focus.preflash = PREFLASH_NONE; + } + } + + /* Notice: we here turn touch flag off set previously + * when doing Touch AF. */ + if (state->focus.touch) + state->focus.touch = 0; + + return 0; +} + +/* PX: Set AF */ +static int s5k5ccgx_set_af(struct v4l2_subdev *sd, s32 val) +{ + struct s5k5ccgx_state *state = to_state(sd); + int err = 0; + + cam_info("%s: %s, focus mode %d\n", __func__, + val ? "start" : "stop", state->focus.mode); + + if (unlikely((u32)val >= AUTO_FOCUS_MAX)) { + cam_err("%s: ERROR, invalid value(%d)\n", __func__, val); + return -EINVAL; + } + + if (state->focus.start == val) + return 0; + + state->focus.start = val; + + if (val == AUTO_FOCUS_ON) { + err = queue_work(state->workqueue, &state->af_work); + if (likely(err)) + state->focus.status = AF_RESULT_DOING; + else + cam_warn("WARNING, AF is still processing. So new AF cannot start\n"); + } else { + /* Cancel AF */ + cam_info("set_af: AF cancel requested!\n"); + } + + cam_trace("X\n"); + return 0; +} + +/* PX: Stop AF */ +static int s5k5ccgx_stop_af(struct v4l2_subdev *sd) +{ + struct s5k5ccgx_state *state = to_state(sd); + int err = 0; + + cam_trace("E\n"); + mutex_lock(&state->af_lock); + + switch (state->focus.status) { + case AF_RESULT_FAILED: + case AF_RESULT_SUCCESS: + cam_dbg("Stop AF, focus mode %d, AF result %d\n", + state->focus.mode, state->focus.status); + + err = s5k5ccgx_set_lock(sd, AEAWB_UNLOCK, false); + if (unlikely(err)) { + cam_err("%s: ERROR, fail to set lock\n", __func__); + goto err_out; + } + state->focus.status = AF_RESULT_CANCELLED; + state->focus.preflash = PREFLASH_NONE; + break; + + case AF_RESULT_CANCELLED: + break; + + default: + cam_warn("%s: WARNING, unnecessary calling. AF status=%d\n", + __func__, state->focus.status); + /* Return 0. */ + goto err_out; + break; + } + + if (!state->focus.touch) { + /* We move lens to default position if af is cancelled.*/ + err = s5k5ccgx_return_focus(sd); + if (unlikely(err)) { + cam_err("%s: ERROR, fail to af_norma_mode (%d)\n", + __func__, err); + goto err_out; + } + } else + state->focus.touch = 0; + + mutex_unlock(&state->af_lock); + cam_trace("X\n"); + return 0; + +err_out: + mutex_unlock(&state->af_lock); + return err; +} + +static void s5k5ccgx_af_worker(struct work_struct *work) +{ + struct s5k5ccgx_state *state = container_of(work, \ + struct s5k5ccgx_state, af_work); + struct v4l2_subdev *sd = &state->sd; + int err = -EINVAL; + + cam_trace("E\n"); + + mutex_lock(&state->af_lock); + + if (state->sensor_mode == SENSOR_CAMERA) { + state->one_frame_delay_ms = ONE_FRAME_DELAY_MS_NORMAL; + err = s5k5ccgx_af_start_preflash(sd); + if (unlikely(err)) + goto out; + + if (state->focus.status == AF_RESULT_CANCELLED) + goto out; + } else { + state->one_frame_delay_ms = 50; + } + + s5k5ccgx_do_af(sd); + +out: + mutex_unlock(&state->af_lock); + cam_trace("X\n"); + return; +} + +/* PX: Set focus mode */ +static int s5k5ccgx_set_focus_mode(struct v4l2_subdev *sd, s32 val) +{ + struct s5k5ccgx_state *state = to_state(sd); + u32 af_cancel = 0; + int err = -EINVAL; + + /* cam_trace("E\n");*/ + cam_dbg("%s val =%d(0x%X)\n", __func__, val, val); + + if (state->focus.mode == val) + return 0; + + af_cancel = (u32)val & FOCUS_MODE_DEFAULT; + mutex_lock(&state->af_lock); + + switch (val) { + case FOCUS_MODE_MACRO: + err = s5k5ccgx_set_from_table(sd, "af_macro_mode", + &state->regs->af_macro_mode, 1, 0); + if (unlikely(err)) { + cam_err("%s: ERROR, fail to af_macro_mode (%d)\n", + __func__, err); + goto err_out; + } + + state->focus.mode = FOCUS_MODE_MACRO; + break; + + case FOCUS_MODE_INFINITY: + case FOCUS_MODE_AUTO: + case FOCUS_MODE_FIXED: + err = s5k5ccgx_set_from_table(sd, "af_norma_mode", + &state->regs->af_normal_mode, 1, 0); + if (unlikely(err)) { + cam_err("%s: ERROR, fail to af_norma_mode (%d)\n", + __func__, err); + goto err_out; + } + + state->focus.mode = val; + break; + + case FOCUS_MODE_FACEDETECT: + case FOCUS_MODE_CONTINOUS: + case FOCUS_MODE_TOUCH: + break; + + default: + if (!af_cancel) { + cam_err("%s: ERROR, invalid val(0x%X)\n:", + __func__, val); + goto err_out; + } + break; + } + mutex_unlock(&state->af_lock); + + if (af_cancel) + s5k5ccgx_stop_af(sd); + + return 0; + +err_out: + mutex_unlock(&state->af_lock); + return err; +} + +/* PX: */ +static int s5k5ccgx_set_af_window(struct v4l2_subdev *sd) +{ + int err = -EIO; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct s5k5ccgx_state *state = to_state(sd); + struct s5k5ccgx_rect inner_window = {0, 0, 0, 0}; + struct s5k5ccgx_rect outter_window = {0, 0, 0, 0}; + struct s5k5ccgx_rect first_window = {0, 0, 0, 0}; + struct s5k5ccgx_rect second_window = {0, 0, 0, 0}; + const s32 mapped_x = state->focus.pos_x; + const s32 mapped_y = state->focus.pos_y; + const u32 preview_width = state->preview->width; + const u32 preview_height = state->preview->height; + u32 inner_half_width = 0, inner_half_height = 0; + u32 outter_half_width = 0, outter_half_height = 0; + + cam_trace("E\n"); + + mutex_lock(&state->af_lock); + + inner_window.width = SCND_WINSIZE_X * preview_width / 1024; + inner_window.height = SCND_WINSIZE_Y * preview_height / 1024; + outter_window.width = FIRST_WINSIZE_X * preview_width / 1024; + outter_window.height = FIRST_WINSIZE_Y * preview_height / 1024; + + inner_half_width = inner_window.width / 2; + inner_half_height = inner_window.height / 2; + outter_half_width = outter_window.width / 2; + outter_half_height = outter_window.height / 2; + + af_dbg("Preview width=%d, height=%d\n", preview_width, preview_height); + af_dbg("inner_window_width=%d, inner_window_height=%d, " \ + "outter_window_width=%d, outter_window_height=%d\n ", + inner_window.width, inner_window.height, + outter_window.width, outter_window.height); + + /* Get X */ + if (mapped_x <= inner_half_width) { + inner_window.x = outter_window.x = 0; + af_dbg("inner & outter window over sensor left." + "in_x=%d, out_x=%d\n", inner_window.x, outter_window.x); + } else if (mapped_x <= outter_half_width) { + inner_window.x = mapped_x - inner_half_width; + outter_window.x = 0; + af_dbg("outter window over sensor left. in_x=%d, out_x=%d\n", + inner_window.x, outter_window.x); + } else if (mapped_x >= ((preview_width - 1) - inner_half_width)) { + inner_window.x = (preview_width - 1) - inner_window.width; + outter_window.x = (preview_width - 1) - outter_window.width; + af_dbg("inner & outter window over sensor right." \ + "in_x=%d, out_x=%d\n", inner_window.x, outter_window.x); + } else if (mapped_x >= ((preview_width - 1) - outter_half_width)) { + inner_window.x = mapped_x - inner_half_width; + outter_window.x = (preview_width - 1) - outter_window.width; + af_dbg("outter window over sensor right. in_x=%d, out_x=%d\n", + inner_window.x, outter_window.x); + } else { + inner_window.x = mapped_x - inner_half_width; + outter_window.x = mapped_x - outter_half_width; + af_dbg("inner & outter window within sensor area." \ + "in_x=%d, out_x=%d\n", inner_window.x, outter_window.x); + } + + /* Get Y */ + if (mapped_y <= inner_half_height) { + inner_window.y = outter_window.y = 0; + af_dbg("inner & outter window over sensor top." \ + "in_y=%d, out_y=%d\n", inner_window.y, outter_window.y); + } else if (mapped_y <= outter_half_height) { + inner_window.y = mapped_y - inner_half_height; + outter_window.y = 0; + af_dbg("outter window over sensor top. in_y=%d, out_y=%d\n", + inner_window.y, outter_window.y); + } else if (mapped_y >= ((preview_height - 1) - inner_half_height)) { + inner_window.y = (preview_height - 1) - inner_window.height; + outter_window.y = (preview_height - 1) - outter_window.height; + af_dbg("inner & outter window over sensor bottom." \ + "in_y=%d, out_y=%d\n", inner_window.y, outter_window.y); + } else if (mapped_y >= ((preview_height - 1) - outter_half_height)) { + inner_window.y = mapped_y - inner_half_height; + outter_window.y = (preview_height - 1) - outter_window.height; + af_dbg("outter window over sensor bottom. in_y=%d, out_y=%d\n", + inner_window.y, outter_window.y); + } else { + inner_window.y = mapped_y - inner_half_height; + outter_window.y = mapped_y - outter_half_height; + af_dbg("inner & outter window within sensor area." \ + "in_y=%d, out_y=%d\n", inner_window.y, outter_window.y); + } + + af_dbg("==> inner_window top=(%d,%d), bottom=(%d, %d)\n", + inner_window.x, inner_window.y, + inner_window.x + inner_window.width, + inner_window.y + inner_window.height); + af_dbg("==> outter_window top=(%d,%d), bottom=(%d, %d)\n", + outter_window.x, outter_window.y, + outter_window.x + outter_window.width , + outter_window.y + outter_window.height); + + second_window.x = inner_window.x * 1024 / preview_width; + second_window.y = inner_window.y * 1024 / preview_height; + first_window.x = outter_window.x * 1024 / preview_width; + first_window.y = outter_window.y * 1024 / preview_height; + + af_dbg("=> second_window top=(%d, %d)\n", + second_window.x, second_window.y); + af_dbg("=> first_window top=(%d, %d)\n", + first_window.x, first_window.y); + + /* restore write mode */ + err = s5k5ccgx_i2c_write_twobyte(client, 0x0028, 0x7000); + + /* Set first window x, y */ + err |= s5k5ccgx_i2c_write_twobyte(client, 0x002A, 0x022C); + err |= s5k5ccgx_i2c_write_twobyte(client, 0x0F12, + (u16)(first_window.x)); + err |= s5k5ccgx_i2c_write_twobyte(client, 0x002A, 0x022E); + err |= s5k5ccgx_i2c_write_twobyte(client, 0x0F12, + (u16)(first_window.y)); + + /* Set second widnow x, y */ + err |= s5k5ccgx_i2c_write_twobyte(client, 0x002A, 0x0234); + err |= s5k5ccgx_i2c_write_twobyte(client, 0x0F12, + (u16)(second_window.x)); + err |= s5k5ccgx_i2c_write_twobyte(client, 0x002A, 0x0236); + err |= s5k5ccgx_i2c_write_twobyte(client, 0x0F12, + (u16)(second_window.y)); + + /* Update AF window */ + err |= s5k5ccgx_i2c_write_twobyte(client, 0x002A, 0x023C); + err |= s5k5ccgx_i2c_write_twobyte(client, 0x0F12, 0x0001); + + debug_msleep(sd, 60); + mutex_unlock(&state->af_lock); + + CHECK_ERR(err); + cam_dbg("%s: AF window position completed.\n", __func__); + + cam_trace("X\n"); + return 0; +} + +static int s5k5ccgx_set_touch_af(struct v4l2_subdev *sd, s32 val) +{ + struct s5k5ccgx_state *state = to_state(sd); + int err = -EIO; + + cam_trace("%s, x=%d y=%d\n", val ? "start" : "stop", + state->focus.pos_x, state->focus.pos_y); + + state->focus.touch = val; + + if (val) { + if (mutex_is_locked(&state->af_lock)) { + cam_warn("%s: WARNING, AF is busy\n", __func__); + return 0; + } + + err = queue_work(state->workqueue, &state->af_win_work); + if (likely(!err)) + cam_warn("WARNING, AF window is still processing\n"); + } else { + err = s5k5ccgx_stop_af(sd); + CHECK_ERR_MSG(err, "val=%d\n", 0) + } + + cam_trace("X\n"); + return 0; +} + +static void s5k5ccgx_af_win_worker(struct work_struct *work) +{ + struct s5k5ccgx_state *state = container_of(work, \ + struct s5k5ccgx_state, af_win_work); + struct v4l2_subdev *sd = &state->sd; + + cam_trace("E\n"); + s5k5ccgx_set_af_window(sd); + cam_trace("X\n"); +} + +static int s5k5ccgx_init_param(struct v4l2_subdev *sd) +{ + struct v4l2_control ctrl; + int i; + + for (i = 0; i < ARRAY_SIZE(s5k5ccgx_ctrls); i++) { + if (s5k5ccgx_ctrls[i].value != + s5k5ccgx_ctrls[i].default_value) { + ctrl.id = s5k5ccgx_ctrls[i].id; + ctrl.value = s5k5ccgx_ctrls[i].value; + s5k5ccgx_s_ctrl(sd, &ctrl); + } + } + + return 0; +} + +static int s5k5ccgx_init_regs(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct s5k5ccgx_state *state = to_state(sd); + u16 read_value = 0; + int err = -ENODEV; + + /* we'd prefer to do this in probe, but the framework hasn't + * turned on the camera yet so our i2c operations would fail + * if we tried to do it in probe, so we have to do it here + * and keep track if we succeeded or not. + */ + + /* enter read mode */ + err = s5k5ccgx_i2c_write_twobyte(client, 0x002C, 0x7000); + if (unlikely(err < 0)) + return -ENODEV; + + s5k5ccgx_i2c_write_twobyte(client, 0x002E, 0x0150); + s5k5ccgx_i2c_read_twobyte(client, 0x0F12, &read_value); + if (likely(read_value == S5K5CCGX_CHIP_ID)) + cam_info("Sensor ChipID: 0x%04X\n", S5K5CCGX_CHIP_ID); + else + cam_info("Sensor ChipID: 0x%04X, unknown ChipID\n", read_value); + + s5k5ccgx_i2c_write_twobyte(client, 0x002C, 0x7000); + s5k5ccgx_i2c_write_twobyte(client, 0x002E, 0x0152); + s5k5ccgx_i2c_read_twobyte(client, 0x0F12, &read_value); + if (likely(read_value == S5K5CCGX_CHIP_REV)) + cam_info("Sensor revision: 0x%04X\n", S5K5CCGX_CHIP_REV); + else + cam_info("Sensor revision: 0x%04X, unknown revision\n", + read_value); + + /* restore write mode */ + err = s5k5ccgx_i2c_write_twobyte(client, 0x0028, 0x7000); + CHECK_ERR_COND(err < 0, -ENODEV); + + state->regs = ®_datas; + + return 0; +} + +static const struct s5k5ccgx_framesize *s5k5ccgx_get_framesize + (const struct s5k5ccgx_framesize *frmsizes, + u32 frmsize_count, u32 index) +{ + int i = 0; + + for (i = 0; i < frmsize_count; i++) { + if (frmsizes[i].index == index) + return &frmsizes[i]; + } + + return NULL; +} + +/* This function is called from the g_ctrl api + * + * This function should be called only after the s_fmt call, + * which sets the required width/height value. + * + * It checks a list of available frame sizes and sets the + * most appropriate frame size. + * + * The list is stored in an increasing order (as far as possible). + * Hence the first entry (searching from the beginning) where both the + * width and height is more than the required value is returned. + * In case of no perfect match, we set the last entry (which is supposed + * to be the largest resolution supported.) + */ +static void s5k5ccgx_set_framesize(struct v4l2_subdev *sd, + const struct s5k5ccgx_framesize *frmsizes, + u32 num_frmsize, bool preview) +{ + struct s5k5ccgx_state *state = to_state(sd); + const struct s5k5ccgx_framesize **found_frmsize = NULL; + u32 width = state->req_fmt.width; + u32 height = state->req_fmt.height; + int i = 0; + + cam_dbg("%s: Requested Res %dx%d\n", __func__, + width, height); + + found_frmsize = (const struct s5k5ccgx_framesize **) + (preview ? &state->preview : &state->capture); + + for (i = 0; i < num_frmsize; i++) { + if ((frmsizes[i].width == width) && + (frmsizes[i].height == height)) { + *found_frmsize = &frmsizes[i]; + break; + } + } + + if (*found_frmsize == NULL) { + cam_err("%s: ERROR, invalid frame size %dx%d\n", __func__, + width, height); + *found_frmsize = preview ? + s5k5ccgx_get_framesize(frmsizes, num_frmsize, + S5K5CCGX_PREVIEW_XGA) : + s5k5ccgx_get_framesize(frmsizes, num_frmsize, + S5K5CCGX_CAPTURE_3MP); + BUG_ON(!(*found_frmsize)); + } + + if (preview) + cam_info("Preview Res Set: %dx%d, index %d\n", + (*found_frmsize)->width, (*found_frmsize)->height, + (*found_frmsize)->index); + else + cam_info("Capture Res Set: %dx%d, index %d\n", + (*found_frmsize)->width, (*found_frmsize)->height, + (*found_frmsize)->index); +} + +static int s5k5ccgx_wait_steamoff(struct v4l2_subdev *sd) +{ + struct s5k5ccgx_state *state = to_state(sd); + struct s5k5ccgx_stream_time *stream_time = &state->stream_time; + s32 elapsed_msec = 0; + + cam_trace("E\n"); + + if (unlikely(!(state->pdata->is_mipi & state->need_wait_streamoff))) + return 0; + + do_gettimeofday(&stream_time->curr_time); + + elapsed_msec = GET_ELAPSED_TIME(stream_time->curr_time, \ + stream_time->before_time) / 1000; + + if (state->pdata->streamoff_delay > elapsed_msec) { + cam_info("stream-off: %dms + %dms\n", elapsed_msec, + state->pdata->streamoff_delay - elapsed_msec); + debug_msleep(sd, state->pdata->streamoff_delay - elapsed_msec); + } else + cam_info("stream-off: %dms\n", elapsed_msec); + + state->need_wait_streamoff = 0; + + return 0; +} + +static int s5k5ccgx_control_stream(struct v4l2_subdev *sd, u32 cmd) +{ + struct s5k5ccgx_state *state = to_state(sd); + int err = -EINVAL; + + if (unlikely(cmd != STREAM_STOP)) + return 0; + + cam_info("STREAM STOP!!\n"); + err = s5k5ccgx_set_from_table(sd, "stream_stop", + &state->regs->stream_stop, 1, 0); + +#ifdef CONFIG_VIDEO_IMPROVE_STREAMOFF + do_gettimeofday(&state->stream_time.before_time); + state->need_wait_streamoff = 1; +#else + debug_msleep(sd, state->pdata->streamoff_delay); +#endif + + if (state->runmode == S5K5CCGX_RUNMODE_CAPTURING) { + state->runmode = S5K5CCGX_RUNMODE_CAPTURE_STOP; + cam_dbg("Capture Stop!\n"); + } + + CHECK_ERR_MSG(err, "failed to stop stream\n"); + return 0; +} + +/* PX: Set flash mode */ +static int s5k5ccgx_set_flash_mode(struct v4l2_subdev *sd, s32 val) +{ + struct s5k5ccgx_state *state = to_state(sd); + + /* movie flash mode should be set when recording is started */ +/* if (state->sensor_mode == SENSOR_MOVIE && !state->recording) + return 0;*/ + + if (state->flash_mode == val) { + cam_dbg("the same flash mode=%d\n", val); + return 0; + } + + if (val == FLASH_MODE_TORCH) + s5k5ccgx_flash_torch(sd, S5K5CCGX_FLASH_ON); + + if ((state->flash_mode == FLASH_MODE_TORCH) + && (val == FLASH_MODE_OFF)) + s5k5ccgx_flash_torch(sd, S5K5CCGX_FLASH_OFF); + + state->flash_mode = val; + cam_dbg("Flash mode = %d\n", val); + return 0; +} + +static int s5k5ccgx_check_esd(struct v4l2_subdev *sd) +{ + struct s5k5ccgx_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int err = -EINVAL; + u16 read_value = 0; + + err = s5k5ccgx_set_from_table(sd, "get_esd_status", + &state->regs->get_esd_status, 1, 0); + CHECK_ERR(err); + err = s5k5ccgx_i2c_read_twobyte(client, 0x0F12, &read_value); + CHECK_ERR(err); + + if (read_value != 0xAAAA) + goto esd_out; + + cam_info("Check ESD: not detected\n\n"); + return 0; + +esd_out: + cam_err("Check ESD: ERROR, ESD Shock detected! (val=0x%X)\n\n", + read_value); + return -ERESTART; +} + +/* returns the real iso currently used by sensor due to lighting + * conditions, not the requested iso we sent using s_ctrl. + */ +/* PX: */ +static inline int s5k5ccgx_get_exif_iso(struct v4l2_subdev *sd, u16 *iso) +{ + struct s5k5ccgx_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u16 iso_gain_table[] = {10, 15, 25, 35}; + u16 iso_table[] = {0, 50, 100, 200, 400}; + int err = -EIO; + u16 val = 0, gain = 0; + int i = 0; + + err = s5k5ccgx_set_from_table(sd, "get_iso", + &state->regs->get_iso, 1, 0); + err |= s5k5ccgx_i2c_read_twobyte(client, 0x0F12, &val); + CHECK_ERR(err); + + gain = val * 10 / 256; + for (i = 0; i < ARRAY_SIZE(iso_gain_table); i++) { + if (gain < iso_gain_table[i]) + break; + } + + *iso = iso_table[i]; + + cam_dbg("gain=%d, ISO=%d\n", gain, *iso); + + /* We do not restore write mode */ + + return 0; +} + +/* PX: Set ISO */ +static int __used s5k5ccgx_set_iso(struct v4l2_subdev *sd, s32 val) +{ + struct s5k5ccgx_state *state = to_state(sd); + int err = -EINVAL; + +retry: + switch (val) { + case ISO_AUTO: + case ISO_50: + case ISO_100: + case ISO_200: + case ISO_400: + err = s5k5ccgx_set_from_table(sd, "iso", + state->regs->iso, ARRAY_SIZE(state->regs->iso), + val); + break; + + default: + cam_err("%s: ERROR, invalid arguement(%d)\n", __func__, val); + val = ISO_AUTO; + goto retry; + break; + } + + cam_trace("X\n"); + return 0; +} + +/* PX: Return exposure time (ms) */ +static inline int s5k5ccgx_get_exif_exptime(struct v4l2_subdev *sd, + u32 *exp_time) +{ + struct s5k5ccgx_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int err; + u16 read_value_lsb = 0; + u16 read_value_msb = 0; + + err = s5k5ccgx_set_from_table(sd, "get_shutterspeed", + &state->regs->get_shutterspeed, 1, 0); + CHECK_ERR(err); + + err = s5k5ccgx_i2c_read_twobyte(client, 0x0F12, &read_value_lsb); + err |= s5k5ccgx_i2c_read_twobyte(client, 0x0F12, &read_value_msb); + CHECK_ERR(err); + + *exp_time = (((read_value_msb << 16) | (read_value_lsb & 0xFFFF)) + * 1000) / 400; + + /* We do not restore write mode */ + + return 0; + +} + +static inline void s5k5ccgx_get_exif_flash(struct v4l2_subdev *sd, + u16 *flash) +{ + struct s5k5ccgx_state *state = to_state(sd); + + switch (state->flash_mode) { + case FLASH_MODE_OFF: + *flash |= EXIF_FLASH_MODE_SUPPRESSION; + break; + + case FLASH_MODE_AUTO: + *flash |= EXIF_FLASH_MODE_AUTO; + break; + + case FLASH_MODE_ON: + case FLASH_MODE_TORCH: + *flash |= EXIF_FLASH_MODE_FIRING; + break; + + default: + break; + } + + if (state->flash_on) { + *flash |= EXIF_FLASH_FIRED; + if (state->sensor_mode == SENSOR_CAMERA) + state->flash_on = 0; + } + +} + +/* PX: */ +static int s5k5ccgx_get_exif(struct v4l2_subdev *sd) +{ + struct s5k5ccgx_state *state = to_state(sd); + u32 exposure_time = 0; + + /* exposure time */ + state->exif.exp_time_den = 0; + s5k5ccgx_get_exif_exptime(sd, &exposure_time); + /*WARN(!exposure_time, "WARNING: exposure time is 0\n");*/ + state->exif.exp_time_den = 1000 * 1000 / exposure_time; + + /* iso */ + state->exif.iso = 0; + s5k5ccgx_get_exif_iso(sd, &state->exif.iso); + + /* flash */ + state->exif.flash = 0; + s5k5ccgx_get_exif_flash(sd, &state->exif.flash); + + cam_dbg("EXIF: ex_time_den=%d, iso=%d, flash=0x%02X\n", + state->exif.exp_time_den, state->exif.iso, state->exif.flash); + + return 0; +} + +static int s5k5ccgx_set_preview_size(struct v4l2_subdev *sd) +{ + struct s5k5ccgx_state *state = to_state(sd); + int err = -EINVAL; + + cam_trace("E, wide_cmd=%d\n", state->wide_cmd); + + switch (state->wide_cmd) { + case WIDE_REQ_CHANGE: + cam_info("%s: Wide Capture setting\n", __func__); + err = s5k5ccgx_set_from_table(sd, "change_wide_cap", + &state->regs->change_wide_cap, 1, 0); + break; + + case WIDE_REQ_RESTORE: + cam_info("%s:Restore capture setting\n", __func__); + err = s5k5ccgx_set_from_table(sd, "restore_capture", + &state->regs->restore_cap, 1, 0); + /* We do not break */ + + default: + cam_dbg("set_preview_size\n"); + err = s5k5ccgx_set_from_table(sd, "preview_size", + state->regs->preview_size, + ARRAY_SIZE(state->regs->preview_size), + state->preview->index); + BUG_ON(state->preview->index == S5K5CCGX_PREVIEW_PVGA); + break; + } + CHECK_ERR(err); + + return 0; +} + +static int s5k5ccgx_set_preview_start(struct v4l2_subdev *sd) +{ + struct s5k5ccgx_state *state = to_state(sd); + int err = -EINVAL; + /* bool set_size = true; */ + + cam_dbg("Camera Preview start, runmode = %d\n", state->runmode); + + if ((state->runmode == S5K5CCGX_RUNMODE_NOTREADY) || + (state->runmode == S5K5CCGX_RUNMODE_CAPTURING)) { + cam_err("%s: ERROR - Invalid runmode\n", __func__); + return -EPERM; + } + + state->focus.status = AF_RESULT_NONE; + + if (state->need_update_frmsize) { + err = s5k5ccgx_set_preview_size(sd); + state->need_update_frmsize = 0; + CHECK_ERR_MSG(err, "failed to set preview size(%d)\n", err); + } + + if (state->runmode == S5K5CCGX_RUNMODE_CAPTURE_STOP) { + /* We turn flash off if one shot flash is still on. */ + if (s5k5ccgx_is_hwflash_on(sd)) + s5k5ccgx_flash_oneshot(sd, S5K5CCGX_FLASH_OFF); + + err = s5k5ccgx_set_lock(sd, AEAWB_UNLOCK, true); + CHECK_ERR_MSG(err, "fail to set lock\n"); + + cam_info("Sending Preview_Return cmd\n"); + err = s5k5ccgx_set_from_table(sd, "preview_return", + &state->regs->preview_return, 1, 0); + CHECK_ERR_MSG(err, "fail to set Preview_Return (%d)\n", err) + } else { + err = s5k5ccgx_set_from_table(sd, "update_preview", + &state->regs->update_preview, 1, 0); + CHECK_ERR_MSG(err, "failed to update preview(%d)\n", err); + } + + state->runmode = S5K5CCGX_RUNMODE_RUNNING; + + return 0; +} + +static int s5k5ccgx_set_video_preview(struct v4l2_subdev *sd) +{ + struct s5k5ccgx_state *state = to_state(sd); + int err = -EINVAL; + + cam_dbg("Video Preview start, runmode = %d\n", state->runmode); + + if ((state->runmode == S5K5CCGX_RUNMODE_NOTREADY) || + (state->runmode == S5K5CCGX_RUNMODE_CAPTURING)) { + cam_err("%s: ERROR - Invalid runmode\n", __func__); + return -EPERM; + } + + state->focus.status = AF_RESULT_NONE; + + if (state->hd_videomode) { + s5k5ccgx_init_param(sd); + err = s5k5ccgx_set_from_table(sd, "update_hd_preview", + &state->regs->update_hd_preview, 1, 0); + CHECK_ERR_MSG(err, "failed to update HD preview\n"); + + s5k5ccgx_set_from_table(sd, "hd_first_af_start", + &state->regs->hd_first_af_start, 1, 0); + } else { + err = s5k5ccgx_set_from_table(sd, "preview_size", + state->regs->preview_size, + ARRAY_SIZE(state->regs->preview_size), + state->preview->index); + CHECK_ERR_MSG(err, "failed to set preview size\n"); + + err = s5k5ccgx_set_from_table(sd, "update_preview", + &state->regs->update_preview, 1, 0); + CHECK_ERR_MSG(err, "failed to update preview\n"); + } + + cam_dbg("runmode now RUNNING\n"); + state->runmode = S5K5CCGX_RUNMODE_RUNNING; + + return 0; +} + +/* PX: Start capture */ +static int s5k5ccgx_set_capture_start(struct v4l2_subdev *sd) +{ + struct s5k5ccgx_state *state = to_state(sd); + int err = -ENODEV; + u32 light_level = 0xFFFFFFFF; + + /* Set capture size */ + err = s5k5ccgx_set_capture_size(sd); + CHECK_ERR_MSG(err, "fail to set capture size (%d)\n", err); + + /* Set flash */ + switch (state->flash_mode) { + case FLASH_MODE_AUTO: + /* 3rd party App may do capturing without AF. So we check + * whether AF is executed before capture and turn on flash + * if needed. But we do not consider low-light capture of Market + * App. */ + if (state->focus.preflash == PREFLASH_NONE) { + s5k5ccgx_get_light_level(sd, &state->light_level); + if (light_level >= FLASH_LOW_LIGHT_LEVEL) + break; + } else if (state->focus.preflash == PREFLASH_OFF) + break; + /* We do not break. */ + + case FLASH_MODE_ON: + s5k5ccgx_flash_oneshot(sd, S5K5CCGX_FLASH_ON); + /* We here don't need to set state->flash_on to 1 */ + + err = s5k5ccgx_set_lock(sd, AEAWB_UNLOCK, true); + CHECK_ERR_MSG(err, "fail to set lock\n"); + + /* Full flash start */ + err = s5k5ccgx_set_from_table(sd, "flash_start", + &state->regs->flash_start, 1, 0); + break; + + case FLASH_MODE_OFF: +#ifdef CONFIG_VIDEO_S5K5CCGX_P8 + if (state->light_level < CAPTURE_LOW_LIGHT_LEVEL) + err = s5k5ccgx_set_from_table(sd, "set_lowlight_cap", + &state->regs->set_lowlight_cap, 1, 0); + break; +#endif + default: + break; + } + + /* Send capture start command. */ + cam_dbg("Send Capture_Start cmd\n"); + err = s5k5ccgx_set_from_table(sd, "capture_start", + state->regs->capture_start, + ARRAY_SIZE(state->regs->capture_start), + state->capture->index); + if (state->scene_mode == SCENE_MODE_NIGHTSHOT) + debug_msleep(sd, 140); + + state->runmode = S5K5CCGX_RUNMODE_CAPTURING; + state->focus.preflash = PREFLASH_NONE; + + CHECK_ERR_MSG(err, "fail to capture_start (%d)\n", err); + + s5k5ccgx_get_exif(sd); + + return 0; +} + +static int s5k5ccgx_s_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct s5k5ccgx_state *state = to_state(sd); + s32 previous_index = 0; + + cam_dbg("%s: pixelformat = 0x%x, colorspace = 0x%x, width = %d, height = %d\n", + __func__, fmt->code, fmt->colorspace, fmt->width, fmt->height); + + v4l2_fill_pix_format(&state->req_fmt, fmt); + state->format_mode = fmt->field; + state->wide_cmd = WIDE_REQ_NONE; + + if (state->format_mode != V4L2_PIX_FMT_MODE_CAPTURE) { + previous_index = state->preview ? state->preview->index : -1; + + s5k5ccgx_set_framesize(sd, s5k5ccgx_preview_frmsizes, + ARRAY_SIZE(s5k5ccgx_preview_frmsizes), + true); + + if (unlikely((state->sensor_mode == SENSOR_CAMERA) && + (state->preview->index == S5K5CCGX_PREVIEW_PVGA))) { + cam_err("%s: ERROR, invalid preview size\n", __func__); + return -EINVAL; + } + + if (previous_index != state->preview->index) { + if ((state->preview->index == PREVIEW_WIDE_SIZE) + && (previous_index != PREVIEW_WIDE_SIZE)) { + cam_dbg("preview, need to change to WIDE\n"); + state->wide_cmd = WIDE_REQ_CHANGE; + } else if ((state->preview->index != PREVIEW_WIDE_SIZE) + && (previous_index == PREVIEW_WIDE_SIZE)) { + cam_dbg("preview, need to restore form WIDE\n"); + state->wide_cmd = WIDE_REQ_RESTORE; + } + + state->need_update_frmsize = 1; + } + } else { + /* + * In case of image capture mode, + * if the given image resolution is not supported, + * use the next higher image resolution. */ + s5k5ccgx_set_framesize(sd, s5k5ccgx_capture_frmsizes, + ARRAY_SIZE(s5k5ccgx_capture_frmsizes), + false); + + /* for maket app. + * Samsung camera app does not use unmatched ratio.*/ + if (unlikely(FRM_RATIO(state->preview) + != FRM_RATIO(state->capture))) { + cam_warn("%s: WARNING, capture ratio " \ + "is different with preview ratio\n\n", + __func__); + if (state->capture->index == CAPTURE_WIDE_SIZE) { + cam_dbg("captre: need to change to WIDE\n"); + state->wide_cmd = WIDE_REQ_CHANGE; + } else { + cam_dbg("capture, need to restore form WIDE\n"); + state->wide_cmd = WIDE_REQ_RESTORE; + } + } + } + + return 0; +} + +static int s5k5ccgx_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index, + enum v4l2_mbus_pixelcode *code) +{ + cam_dbg("%s: index = %d\n", __func__, index); + + if (index >= ARRAY_SIZE(capture_fmts)) + return -EINVAL; + + *code = capture_fmts[index].code; + + return 0; +} + +static int s5k5ccgx_try_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + int num_entries; + int i; + + num_entries = ARRAY_SIZE(capture_fmts); + + cam_dbg("%s: code = 0x%x , colorspace = 0x%x, num_entries = %d\n", + __func__, fmt->code, fmt->colorspace, num_entries); + + for (i = 0; i < num_entries; i++) { + if (capture_fmts[i].code == fmt->code && + capture_fmts[i].colorspace == fmt->colorspace) { + cam_dbg("%s: match found, returning 0\n", __func__); + return 0; + } + } + + cam_err("%s: no match found, returning -EINVAL\n", __func__); + return -EINVAL; +} + + +static int s5k5ccgx_enum_framesizes(struct v4l2_subdev *sd, + struct v4l2_frmsizeenum *fsize) +{ + struct s5k5ccgx_state *state = to_state(sd); + + /* + * The camera interface should read this value, this is the resolution + * at which the sensor would provide framedata to the camera i/f + * In case of image capture, + * this returns the default camera resolution (VGA) + */ + if (state->format_mode != V4L2_PIX_FMT_MODE_CAPTURE) { + if (unlikely(state->preview == NULL)) { + cam_err("%s: ERROR\n", __func__); + return -EFAULT; + } + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = state->preview->width; + fsize->discrete.height = state->preview->height; + } else { + if (unlikely(state->capture == NULL)) { + cam_err("%s: ERROR\n", __func__); + return -EFAULT; + } + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = state->capture->width; + fsize->discrete.height = state->capture->height; + } + + return 0; +} + +static int s5k5ccgx_g_parm(struct v4l2_subdev *sd, + struct v4l2_streamparm *param) +{ + return 0; +} + +static int s5k5ccgx_s_parm(struct v4l2_subdev *sd, + struct v4l2_streamparm *param) +{ + int err = 0; + struct s5k5ccgx_state *state = to_state(sd); + + state->req_fps = param->parm.capture.timeperframe.denominator / + param->parm.capture.timeperframe.numerator; + + cam_dbg("s_parm state->fps=%d, state->req_fps=%d\n", + state->fps, state->req_fps); + + if ((state->req_fps < 0) || (state->req_fps > 30)) { + cam_err("%s: ERROR, invalid frame rate %d. we'll set to 30\n", + __func__, state->req_fps); + state->req_fps = 30; + } + + if (state->initialized && (state->scene_mode == SCENE_MODE_NONE)) { + err = s5k5ccgx_set_frame_rate(sd, state->req_fps); + CHECK_ERR(err); + } + + return 0; +} + +static int s5k5ccgx_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct s5k5ccgx_state *state = to_state(sd); + int err = 0; + + if (!state->initialized) { + cam_err("%s: WARNING, camera not initialized\n", __func__); + return 0; + } + + mutex_lock(&state->ctrl_lock); + + switch (ctrl->id) { + case V4L2_CID_CAMERA_EXIF_EXPTIME: + if (state->sensor_mode == SENSOR_CAMERA) + ctrl->value = state->exif.exp_time_den; + else + ctrl->value = 24; + break; + + case V4L2_CID_CAMERA_EXIF_ISO: + if (state->sensor_mode == SENSOR_CAMERA) + ctrl->value = state->exif.iso; + else + ctrl->value = 100; + break; + + case V4L2_CID_CAMERA_EXIF_FLASH: + if (state->sensor_mode == SENSOR_CAMERA) + ctrl->value = state->exif.flash; + else + s5k5ccgx_get_exif_flash(sd, (u16 *)ctrl->value); + break; + +#if !defined(FEATURE_YUV_CAPTURE) + case V4L2_CID_CAM_JPEG_MAIN_SIZE: + ctrl->value = state->jpeg.main_size; + break; + + case V4L2_CID_CAM_JPEG_MAIN_OFFSET: + ctrl->value = state->jpeg.main_offset; + break; + + case V4L2_CID_CAM_JPEG_THUMB_SIZE: + ctrl->value = state->jpeg.thumb_size; + break; + + case V4L2_CID_CAM_JPEG_THUMB_OFFSET: + ctrl->value = state->jpeg.thumb_offset; + break; + + case V4L2_CID_CAM_JPEG_QUALITY: + ctrl->value = state->jpeg.quality; + break; + + case V4L2_CID_CAM_JPEG_MEMSIZE: + ctrl->value = SENSOR_JPEG_SNAPSHOT_MEMSIZE; + break; +#endif + + case V4L2_CID_CAMERA_AUTO_FOCUS_RESULT: + ctrl->value = state->focus.status; + break; + + case V4L2_CID_CAMERA_WHITE_BALANCE: + case V4L2_CID_CAMERA_EFFECT: + case V4L2_CID_CAMERA_CONTRAST: + case V4L2_CID_CAMERA_SATURATION: + case V4L2_CID_CAMERA_SHARPNESS: + case V4L2_CID_CAMERA_OBJ_TRACKING_STATUS: + case V4L2_CID_CAMERA_SMART_AUTO_STATUS: + default: + cam_err("%s: WARNING, unknown Ctrl-ID 0x%x\n", + __func__, ctrl->id); + err = 0; /* we return no error. */ + break; + } + + mutex_unlock(&state->ctrl_lock); + + return err; +} + +static int s5k5ccgx_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct s5k5ccgx_state *state = to_state(sd); + int err = -ENOIOCTLCMD; + + if (unlikely(state->sensor_mode == SENSOR_MOVIE)) + s5k5ccgx_save_ctrl(sd, ctrl); + + if (!state->initialized && ctrl->id != V4L2_CID_CAMERA_SENSOR_MODE) { + if (state->sensor_mode == SENSOR_MOVIE) + return 0; + + cam_warn("%s: WARNING, camera not initialized. ID = %d(0x%X)\n", + __func__, ctrl->id - V4L2_CID_PRIVATE_BASE, + ctrl->id - V4L2_CID_PRIVATE_BASE); + return 0; + } + + cam_dbg("%s: ID =%d, val = %d\n", + __func__, ctrl->id - V4L2_CID_PRIVATE_BASE, ctrl->value); + + if (ctrl->id != V4L2_CID_CAMERA_SET_AUTO_FOCUS) + mutex_lock(&state->ctrl_lock); + + switch (ctrl->id) { + case V4L2_CID_CAMERA_SENSOR_MODE: + err = s5k5ccgx_set_sensor_mode(sd, ctrl->value); + break; + + case V4L2_CID_CAMERA_OBJECT_POSITION_X: + state->focus.pos_x = ctrl->value; + err = 0; + break; + + case V4L2_CID_CAMERA_OBJECT_POSITION_Y: + state->focus.pos_y = ctrl->value; + err = 0; + break; + + case V4L2_CID_CAMERA_TOUCH_AF_START_STOP: + err = s5k5ccgx_set_touch_af(sd, ctrl->value); + break; + + case V4L2_CID_CAMERA_FOCUS_MODE: + err = s5k5ccgx_set_focus_mode(sd, ctrl->value); + break; + + case V4L2_CID_CAMERA_SET_AUTO_FOCUS: + err = s5k5ccgx_set_af(sd, ctrl->value); + break; + + case V4L2_CID_CAMERA_FLASH_MODE: + err = s5k5ccgx_set_flash_mode(sd, ctrl->value); + break; + + case V4L2_CID_CAMERA_BRIGHTNESS: + err = s5k5ccgx_set_exposure(sd, ctrl->value); + break; + + case V4L2_CID_CAMERA_WHITE_BALANCE: + err = s5k5ccgx_set_from_table(sd, "white balance", + state->regs->white_balance, + ARRAY_SIZE(state->regs->white_balance), ctrl->value); + state->wb_mode = ctrl->value; + break; + + case V4L2_CID_CAMERA_EFFECT: + err = s5k5ccgx_set_from_table(sd, "effects", + state->regs->effect, + ARRAY_SIZE(state->regs->effect), ctrl->value); + break; + + case V4L2_CID_CAMERA_METERING: + err = s5k5ccgx_set_from_table(sd, "metering", + state->regs->metering, + ARRAY_SIZE(state->regs->metering), ctrl->value); + break; + + case V4L2_CID_CAMERA_CONTRAST: + err = s5k5ccgx_set_from_table(sd, "contrast", + state->regs->contrast, + ARRAY_SIZE(state->regs->contrast), ctrl->value); + break; + + case V4L2_CID_CAMERA_SATURATION: + err = s5k5ccgx_set_from_table(sd, "saturation", + state->regs->saturation, + ARRAY_SIZE(state->regs->saturation), ctrl->value); + break; + + case V4L2_CID_CAMERA_SHARPNESS: + err = s5k5ccgx_set_from_table(sd, "sharpness", + state->regs->sharpness, + ARRAY_SIZE(state->regs->sharpness), ctrl->value); + break; + + case V4L2_CID_CAMERA_SCENE_MODE: + err = s5k5ccgx_set_scene_mode(sd, ctrl->value); + break; + + case V4L2_CID_CAMERA_AE_LOCK_UNLOCK: + err = s5k5ccgx_set_ae_lock(sd, ctrl->value, false); + break; + + case V4L2_CID_CAMERA_AWB_LOCK_UNLOCK: + err = s5k5ccgx_set_awb_lock(sd, ctrl->value, false); + break; + + case V4L2_CID_CAMERA_CHECK_ESD: + err = s5k5ccgx_check_esd(sd); + break; + + case V4L2_CID_CAMERA_ISO: + /* we do not break. */ + case V4L2_CID_CAMERA_FRAME_RATE: + default: + cam_err("%s: WARNING, unknown Ctrl-ID 0x%x\n", + __func__, ctrl->id); + err = 0; /* we return no error. */ + break; + } + + if (ctrl->id != V4L2_CID_CAMERA_SET_AUTO_FOCUS) + mutex_unlock(&state->ctrl_lock); + + CHECK_ERR_MSG(err, "s_ctrl failed %d\n", err) + + return 0; +} + +static int s5k5ccgx_s_ext_ctrl(struct v4l2_subdev *sd, + struct v4l2_ext_control *ctrl) +{ + return 0; +} + +static int s5k5ccgx_s_ext_ctrls(struct v4l2_subdev *sd, + struct v4l2_ext_controls *ctrls) +{ + struct v4l2_ext_control *ctrl = ctrls->controls; + int ret; + int i; + + for (i = 0; i < ctrls->count; i++, ctrl++) { + ret = s5k5ccgx_s_ext_ctrl(sd, ctrl); + + if (ret) { + ctrls->error_idx = i; + break; + } + } + + return ret; +} + +static int s5k5ccgx_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct s5k5ccgx_state *state = to_state(sd); + int err = 0; + + cam_info("stream mode = %d\n", enable); + + BUG_ON(!state->initialized); + + switch (enable) { + case STREAM_MODE_CAM_OFF: + if (state->pdata->is_mipi) + err = s5k5ccgx_control_stream(sd, STREAM_STOP); + break; + + case STREAM_MODE_CAM_ON: + switch (state->sensor_mode) { + case SENSOR_CAMERA: + if (state->format_mode == V4L2_PIX_FMT_MODE_CAPTURE) + err = s5k5ccgx_set_capture_start(sd); + else + err = s5k5ccgx_set_preview_start(sd); + break; + + case SENSOR_MOVIE: + err = s5k5ccgx_set_video_preview(sd); + break; + + default: + break; + } + break; + + case STREAM_MODE_MOVIE_ON: + cam_info("movie on"); + state->recording = 1; + if (state->flash_mode != FLASH_MODE_OFF) + s5k5ccgx_flash_torch(sd, S5K5CCGX_FLASH_ON); + break; + + case STREAM_MODE_MOVIE_OFF: + cam_info("movie off"); + state->recording = 0; + if (state->flash_on) + s5k5ccgx_flash_torch(sd, S5K5CCGX_FLASH_OFF); + break; + +#ifdef CONFIG_VIDEO_IMPROVE_STREAMOFF + case STREAM_MODE_WAIT_OFF: + s5k5ccgx_wait_steamoff(sd); + break; +#endif + default: + cam_err("%s: ERROR - Invalid stream mode\n", __func__); + break; + } + + CHECK_ERR_MSG(err, "failed\n"); + + return 0; +} + +static int s5k5ccgx_reset(struct v4l2_subdev *sd, u32 val) +{ + struct s5k5ccgx_state *state = to_state(sd); + + cam_trace("EX\n"); + + s5k5ccgx_return_focus(sd); + state->initialized = 0; + + return 0; +} + +static int s5k5ccgx_init(struct v4l2_subdev *sd, u32 val) +{ + struct s5k5ccgx_state *state = to_state(sd); + int err = -EINVAL; + + cam_dbg("%s: start\n", __func__); + + err = s5k5ccgx_init_regs(sd); + CHECK_ERR_MSG(err, "failed to indentify sensor chip\n"); + + if (state->hd_videomode) { + cam_info("init: HD mode\n"); + err = S5K5CCGX_BURST_WRITE_REGS(sd, s5k5ccgx_hd_init_reg); + } else { + cam_info("init: Cam, Non-HD mode\n"); + err = S5K5CCGX_BURST_WRITE_REGS(sd, s5k5ccgx_init_reg); + } + CHECK_ERR_MSG(err, "failed to initialize camera device\n"); + +#ifdef CONFIG_VIDEO_S5K5CCGX_P8 + s5k5ccgx_set_from_table(sd, "antibanding", + &state->regs->antibanding, 1, 0); +#endif + + state->runmode = S5K5CCGX_RUNMODE_INIT; + + /* Default state values */ + state->flash_mode = FLASH_MODE_OFF; + state->scene_mode = SCENE_MODE_NONE; + state->flash_on = 0; + state->light_level = 0xFFFFFFFF; + memset(&state->focus, 0, sizeof(state->focus)); + + state->initialized = 1; + + if (state->sensor_mode == SENSOR_MOVIE) + s5k5ccgx_init_param(sd); + + if (state->req_fps >= 0) { + err = s5k5ccgx_set_frame_rate(sd, state->req_fps); + CHECK_ERR(err); + } + + return 0; +} + +/* + * s_config subdev ops + * With camera device, we need to re-initialize + * every single opening time therefor, + * it is not necessary to be initialized on probe time. + * except for version checking + * NOTE: version checking is optional + */ +static int s5k5ccgx_s_config(struct v4l2_subdev *sd, + int irq, void *platform_data) +{ + struct s5k5ccgx_state *state = to_state(sd); + int i; +#ifdef CONFIG_LOAD_FILE + int err = 0; +#endif + + if (!platform_data) { + cam_err("%s: ERROR, no platform data\n", __func__); + return -ENODEV; + } + state->pdata = platform_data; + state->dbg_level = &state->pdata->dbg_level; + + /* + * Assign default format and resolution + * Use configured default information in platform data + * or without them, use default information in driver + */ + state->req_fmt.width = state->pdata->default_width; + state->req_fmt.height = state->pdata->default_height; + + if (!state->pdata->pixelformat) + state->req_fmt.pixelformat = DEFAULT_PIX_FMT; + else + state->req_fmt.pixelformat = state->pdata->pixelformat; + + if (!state->pdata->freq) + state->freq = DEFAULT_MCLK; /* 24MHz default */ + else + state->freq = state->pdata->freq; + + state->preview = state->capture = NULL; + state->sensor_mode = SENSOR_CAMERA; + state->hd_videomode = 0; + state->format_mode = V4L2_PIX_FMT_MODE_PREVIEW; + state->fps = 0; + state->req_fps = -1; + + for (i = 0; i < ARRAY_SIZE(s5k5ccgx_ctrls); i++) + s5k5ccgx_ctrls[i].value = s5k5ccgx_ctrls[i].default_value; + +#ifdef S5K5CCGX_SUPPORT_FLASH + if (s5k5ccgx_is_hwflash_on(sd)) + state->ignore_flash = 1; +#endif + +#if !defined(FEATURE_YUV_CAPTURE) + state->jpeg.enable = 0; + state->jpeg.quality = 100; + state->jpeg.main_offset = 1280; /* 0x500 */ + + /* Maximum size 2048 * 1536 * 2 = 6291456 */ + state->jpeg.main_size = SENSOR_JPEG_SNAPSHOT_MEMSIZE; + + state->jpeg.thumb_offset = 636; /* 0x27C */ + state->jpeg.thumb_size = 320 * 240 * 2; /* 320 * 240 * 2 = 153600 */ +#endif + +#ifdef CONFIG_LOAD_FILE + err = loadFile(); + if (unlikely(err < 0)) { + cam_err("failed to load file ERR=%d\n", err); + return err; + } +#endif + + return 0; +} + +static const struct v4l2_subdev_core_ops s5k5ccgx_core_ops = { + .init = s5k5ccgx_init, /* initializing API */ + .g_ctrl = s5k5ccgx_g_ctrl, + .s_ctrl = s5k5ccgx_s_ctrl, + .s_ext_ctrls = s5k5ccgx_s_ext_ctrls, + .reset = s5k5ccgx_reset, +}; + +static const struct v4l2_subdev_video_ops s5k5ccgx_video_ops = { + .s_mbus_fmt = s5k5ccgx_s_mbus_fmt, + .enum_framesizes = s5k5ccgx_enum_framesizes, + .enum_mbus_fmt = s5k5ccgx_enum_mbus_fmt, + .try_mbus_fmt = s5k5ccgx_try_mbus_fmt, + .g_parm = s5k5ccgx_g_parm, + .s_parm = s5k5ccgx_s_parm, + .s_stream = s5k5ccgx_s_stream, +}; + +static const struct v4l2_subdev_ops s5k5ccgx_ops = { + .core = &s5k5ccgx_core_ops, + .video = &s5k5ccgx_video_ops, +}; + + +/* + * s5k5ccgx_probe + * Fetching platform data is being done with s_config subdev call. + * In probe routine, we just register subdev device + */ +static int s5k5ccgx_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct v4l2_subdev *sd; + struct s5k5ccgx_state *state; + int err = -EINVAL; + + state = kzalloc(sizeof(struct s5k5ccgx_state), GFP_KERNEL); + if (unlikely(!state)) { + dev_err(&client->dev, "probe, fail to get memory\n"); + return -ENOMEM; + } + + mutex_init(&state->ctrl_lock); + mutex_init(&state->af_lock); + + state->runmode = S5K5CCGX_RUNMODE_NOTREADY; + sd = &state->sd; + strcpy(sd->name, S5K5CCGX_DRIVER_NAME); + + /* Registering subdev */ + v4l2_i2c_subdev_init(sd, client, &s5k5ccgx_ops); + + state->workqueue = create_workqueue("cam_workqueue"); + if (unlikely(!state->workqueue)) { + dev_err(&client->dev, "probe, fail to create workqueue\n"); + goto err_out; + } + INIT_WORK(&state->af_work, s5k5ccgx_af_worker); + INIT_WORK(&state->af_win_work, s5k5ccgx_af_win_worker); + + err = s5k5ccgx_s_config(sd, 0, client->dev.platform_data); + CHECK_ERR_MSG(err, "fail to s_config\n"); + + printk(KERN_DEBUG "%s %s: driver probed!!\n", + dev_driver_string(&client->dev), dev_name(&client->dev)); + + return 0; + +err_out: + kfree(state); + return -ENOMEM; +} + +static int s5k5ccgx_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct s5k5ccgx_state *state = to_state(sd); + + destroy_workqueue(state->workqueue); + + /* for softlanding */ + if (state->initialized) { +#ifdef CONFIG_VIDEO_S5K5CCGX_P4W + s5k5ccgx_set_af_softlanding(sd); +#else + s5k5ccgx_return_focus(sd); +#endif + } + + /* Check whether flash is on when unlolading driver, + * to preventing Market App from controlling improperly flash. + * It isn't necessary in case that you power flash down + * in power routine to turn camera off.*/ + if (unlikely(state->flash_on && !state->ignore_flash)) + s5k5ccgx_flash_torch(sd, S5K5CCGX_FLASH_OFF); + + v4l2_device_unregister_subdev(sd); + mutex_destroy(&state->ctrl_lock); + mutex_destroy(&state->af_lock); + kfree(state); + +#ifdef CONFIG_LOAD_FILE + large_file ? vfree(testBuf) : kfree(testBuf); + large_file = 0; + testBuf = NULL; +#endif + + printk(KERN_DEBUG "%s %s: driver removed!!\n", + dev_driver_string(&client->dev), dev_name(&client->dev)); + return 0; +} + +static const struct i2c_device_id s5k5ccgx_id[] = { + { S5K5CCGX_DRIVER_NAME, 0 }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, s5k5ccgx_id); + +static struct i2c_driver v4l2_i2c_driver = { + .driver.name = S5K5CCGX_DRIVER_NAME, + .probe = s5k5ccgx_probe, + .remove = s5k5ccgx_remove, + .id_table = s5k5ccgx_id, +}; + +static int __init v4l2_i2c_drv_init(void) +{ + pr_info("%s: %s called\n", __func__, S5K5CCGX_DRIVER_NAME); /* dslim*/ + return i2c_add_driver(&v4l2_i2c_driver); +} + +static void __exit v4l2_i2c_drv_cleanup(void) +{ + pr_info("%s: %s called\n", __func__, S5K5CCGX_DRIVER_NAME); /* dslim*/ + i2c_del_driver(&v4l2_i2c_driver); +} + +module_init(v4l2_i2c_drv_init); +module_exit(v4l2_i2c_drv_cleanup); + +MODULE_DESCRIPTION("LSI S5K5CCGX 3MP SOC camera driver"); +MODULE_AUTHOR("Dong-Seong Lim <dongseong.lim@samsung.com>"); +MODULE_LICENSE("GPL"); |