aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/s5c73m3.c
diff options
context:
space:
mode:
authorcodeworkx <daniel.hillenbrand@codeworkx.de>2012-06-02 13:09:29 +0200
committercodeworkx <daniel.hillenbrand@codeworkx.de>2012-06-02 13:09:29 +0200
commitc6da2cfeb05178a11c6d062a06f8078150ee492f (patch)
treef3b4021d252c52d6463a9b3c1bb7245e399b009c /drivers/media/video/s5c73m3.c
parentc6d7c4dbff353eac7919342ae6b3299a378160a6 (diff)
downloadkernel_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/s5c73m3.c')
-rw-r--r--drivers/media/video/s5c73m3.c3413
1 files changed, 3413 insertions, 0 deletions
diff --git a/drivers/media/video/s5c73m3.c b/drivers/media/video/s5c73m3.c
new file mode 100644
index 0000000..07fab10
--- /dev/null
+++ b/drivers/media/video/s5c73m3.c
@@ -0,0 +1,3413 @@
+/*
+ * driver for LSI S5C73M3 (ISP for 8MP Camera)
+ *
+ * Copyright (c) 2011, Samsung Electronics. All rights reserved
+ *
+ * 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 <linux/i2c.h>
+#include <linux/init.h>
+#include <media/v4l2-device.h>
+#include <linux/delay.h>
+#include <linux/vmalloc.h>
+#include <linux/firmware.h>
+#include <linux/videodev2.h>
+#include <linux/unistd.h>
+
+#include <plat/gpio-cfg.h>
+#include <linux/gpio.h>
+
+#define S5C73M3_BUSFREQ_OPP
+
+#ifdef S5C73M3_BUSFREQ_OPP
+#include <mach/dev.h>
+#endif
+
+#ifdef CONFIG_VIDEO_SAMSUNG_V4L2
+#include <linux/videodev2_exynos_media.h>
+#include <linux/videodev2_exynos_camera.h>
+#endif
+
+#include <linux/regulator/machine.h>
+
+#include <media/s5c73m3_platform.h>
+#include "s5c73m3.h"
+
+#define S5C73M3_DRIVER_NAME "S5C73M3"
+
+extern struct class *camera_class; /*sys/class/camera*/
+struct device *s5c73m3_dev; /*sys/class/camera/rear*/
+struct v4l2_subdev *sd_internal;
+
+#ifdef S5C73M3_BUSFREQ_OPP
+struct device *bus_dev;
+#endif
+
+/*#define S5C73M3_FROM_BOOTING*/
+#define S5C73M3_CORE_VDD "/data/ISP_CV"
+#define S5C73M3_FW_PATH "/sdcard/SlimISP.bin"
+#define S5C73M3_FW_VER_LEN 6
+#define S5C73M3_FW_VER_FILE_CUR 0x60
+
+#define S5C73M3_I2C_RETRY 5
+
+#define CHECK_ERR(x) if ((x) < 0) { \
+ cam_err("i2c failed, err %d\n", x); \
+ return x; \
+ }
+struct s5c73m3_fw_version camfw_info[S5C73M3_PATH_MAX];
+
+static const struct s5c73m3_frmsizeenum preview_frmsizes[] = {
+ { S5C73M3_PREVIEW_QVGA, 320, 240, 0x01 },
+ { S5C73M3_PREVIEW_CIF, 352, 288, 0x0E },
+ { S5C73M3_PREVIEW_VGA, 640, 480, 0x02 },
+ { S5C73M3_PREVIEW_704X576, 704, 576, 0x09 },
+ { S5C73M3_PREVIEW_880X720, 880, 720, 0x03 },
+ { S5C73M3_PREVIEW_960X640, 960, 640, 0x0B },
+ { S5C73M3_PREVIEW_960X720, 960, 720, 0x04 },
+ { S5C73M3_PREVIEW_1008X672, 1008, 672, 0x0F },
+ { S5C73M3_PREVIEW_1184X666, 1184, 666, 0x05 },
+ { S5C73M3_PREVIEW_720P, 1280, 720, 0x06 },
+ { S5C73M3_VDIS_720P, 1536, 864, 0x07 },
+ { S5C73M3_PREVIEW_1080P, 1920, 1080, 0x0A},
+ { S5C73M3_VDIS_1080P, 2304, 1296, 0x0C},
+};
+
+static const struct s5c73m3_frmsizeenum capture_frmsizes[] = {
+ { S5C73M3_CAPTURE_VGA, 640, 480, 0x10 },
+ { S5C73M3_CAPTURE_1024X768, 1024, 768, 0xD0 },
+ { S5C73M3_CAPTURE_HD, 1280, 720, 0x40 },
+ { S5C73M3_CAPTURE_2MP, 1600, 1200, 0x70 },
+ { S5C73M3_CAPTURE_W2MP, 2048, 1152, 0x80 },
+ { S5C73M3_CAPTURE_3MP, 2048, 1536, 0x90 },
+ { S5C73M3_CAPTURE_5MP, 2560, 1920, 0xB0 },
+ { S5C73M3_CAPTURE_W6MP, 3264, 1836, 0xE0 },
+ { S5C73M3_CAPTURE_3264X2176, 3264, 2176, 0xC0 },
+ { S5C73M3_CAPTURE_8MP, 3264, 2448, 0xF0 },
+};
+
+static const struct s5c73m3_effectenum s5c73m3_effects[] = {
+ {IMAGE_EFFECT_NONE, S5C73M3_IMAGE_EFFECT_NONE},
+ {IMAGE_EFFECT_NEGATIVE, S5C73M3_IMAGE_EFFECT_NEGATIVE},
+ {IMAGE_EFFECT_AQUA, S5C73M3_IMAGE_EFFECT_AQUA},
+ {IMAGE_EFFECT_SEPIA, S5C73M3_IMAGE_EFFECT_SEPIA},
+ {IMAGE_EFFECT_BNW, S5C73M3_IMAGE_EFFECT_MONO},
+ {IMAGE_EFFECT_SKETCH, S5C73M3_IMAGE_EFFECT_SKETCH},
+ {IMAGE_EFFECT_WASHED, S5C73M3_IMAGE_EFFECT_WASHED},
+ {IMAGE_EFFECT_VINTAGE_WARM, S5C73M3_IMAGE_EFFECT_VINTAGE_WARM},
+ {IMAGE_EFFECT_VINTAGE_COLD, S5C73M3_IMAGE_EFFECT_VINTAGE_COLD},
+ {IMAGE_EFFECT_SOLARIZE, S5C73M3_IMAGE_EFFECT_SOLARIZE},
+ {IMAGE_EFFECT_POSTERIZE, S5C73M3_IMAGE_EFFECT_POSTERIZE},
+ {IMAGE_EFFECT_POINT_BLUE, S5C73M3_IMAGE_EFFECT_POINT_BLUE},
+ {IMAGE_EFFECT_POINT_RED_YELLOW, S5C73M3_IMAGE_EFFECT_POINT_RED_YELLOW},
+ {IMAGE_EFFECT_POINT_COLOR_3, S5C73M3_IMAGE_EFFECT_POINT_COLOR_3},
+ {IMAGE_EFFECT_POINT_GREEN, S5C73M3_IMAGE_EFFECT_POINT_GREEN},
+};
+
+static struct s5c73m3_control s5c73m3_ctrls[] = {
+ {
+ .id = V4L2_CID_CAMERA_ISO,
+ .minimum = ISO_AUTO,
+ .maximum = ISO_800,
+ .step = 1,
+ .value = ISO_AUTO,
+ .default_value = ISO_AUTO,
+ }, {
+ .id = V4L2_CID_CAMERA_BRIGHTNESS,
+ .minimum = EV_MINUS_4,
+ .maximum = EV_MAX - 1,
+ .step = 1,
+ .value = EV_DEFAULT,
+ .default_value = EV_DEFAULT,
+ }, {
+ .id = V4L2_CID_CAMERA_SATURATION,
+ .minimum = SATURATION_MINUS_2,
+ .maximum = SATURATION_MAX - 1,
+ .step = 1,
+ .value = SATURATION_DEFAULT,
+ .default_value = SATURATION_DEFAULT,
+ }, {
+ .id = V4L2_CID_CAMERA_SHARPNESS,
+ .minimum = SHARPNESS_MINUS_2,
+ .maximum = SHARPNESS_MAX - 1,
+ .step = 1,
+ .value = SHARPNESS_DEFAULT,
+ .default_value = SHARPNESS_DEFAULT,
+ }, {
+ .id = V4L2_CID_CAMERA_ZOOM,
+ .minimum = ZOOM_LEVEL_0,
+ .maximum = ZOOM_LEVEL_MAX - 1,
+ .step = 1,
+ .value = ZOOM_LEVEL_0,
+ .default_value = ZOOM_LEVEL_0,
+ }, {
+ .id = V4L2_CID_CAM_JPEG_QUALITY,
+ .minimum = 1,
+ .maximum = 100,
+ .step = 1,
+ .value = 100,
+ .default_value = 100,
+ },
+};
+
+static u8 sysfs_sensor_fw[10] = {0,};
+static u8 sysfs_phone_fw[10] = {0,};
+static u8 sysfs_sensor_type[15] = {0,};
+static u8 sysfs_isp_core[10] = {0,};
+static u8 data_memory[500000] = {0,};
+
+static u16 isp_chip_info1;
+static u16 isp_chip_info2;
+static u16 isp_chip_info3;
+
+static int s5c73m3_s_stream_sensor(struct v4l2_subdev *sd, int onoff);
+static int s5c73m3_set_touch_auto_focus(struct v4l2_subdev *sd);
+static int s5c73m3_SPI_booting(struct v4l2_subdev *sd);
+static int s5c73m3_get_af_cal_version(struct v4l2_subdev *sd);
+static int s5c73m3_set_timing_register_for_vdd(struct v4l2_subdev *sd);
+
+static inline struct s5c73m3_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct s5c73m3_state, sd);
+}
+
+static int s5c73m3_i2c_write(struct v4l2_subdev *sd,
+ unsigned short addr, unsigned short data)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct i2c_msg msg;
+ unsigned char buf[4];
+ int i, err;
+
+ if (!client->adapter)
+ return -ENODEV;
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = sizeof(buf);
+ msg.buf = buf;
+
+ buf[0] = addr >> 8;
+ buf[1] = addr & 0xff;
+ buf[2] = data >> 8;
+ buf[3] = data & 0xff;
+
+ cam_i2c_dbg("addr %#x, data %#x\n", addr, data);
+
+ for (i = S5C73M3_I2C_RETRY; i; i--) {
+ err = i2c_transfer(client->adapter, &msg, 1);
+ if (err == 1)
+ break;
+ msleep(20);
+ }
+
+ return err;
+}
+
+static int s5c73m3_i2c_write_block(struct v4l2_subdev *sd,
+ const u32 regs[], int size)
+{
+ int i, err = 0;
+
+ for (i = 0; i < size; i++) {
+ err = s5c73m3_i2c_write(sd, (regs[i]>>16), regs[i]);
+ CHECK_ERR(err);
+ }
+
+ return err;
+}
+
+static int s5c73m3_i2c_read(struct v4l2_subdev *sd,
+ unsigned short addr, unsigned short *data)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct i2c_msg msg;
+ unsigned char buf[2];
+ int i, err;
+
+ if (!client->adapter)
+ return -ENODEV;
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = sizeof(buf);
+ msg.buf = buf;
+
+ buf[0] = addr >> 8;
+ buf[1] = addr & 0xff;
+
+ for (i = S5C73M3_I2C_RETRY; i; i--) {
+ err = i2c_transfer(client->adapter, &msg, 1);
+ if (err == 1)
+ break;
+ msleep(20);
+ }
+
+ if (err != 1) {
+ cam_err("addr %#x\n", addr);
+ return err;
+ }
+
+ msg.flags = I2C_M_RD;
+
+ for (i = S5C73M3_I2C_RETRY; i; i--) {
+ err = i2c_transfer(client->adapter, &msg, 1);
+ if (err == 1)
+ break;
+ msleep(20);
+ }
+
+ if (err != 1) {
+ cam_err("addr %#x\n", addr);
+ return err;
+ }
+
+ *data = ((buf[0] << 8) | buf[1]);
+
+ return err;
+}
+
+static int s5c73m3_write(struct v4l2_subdev *sd,
+ unsigned short addr1, unsigned short addr2, unsigned short data)
+{
+ int err;
+
+ err = s5c73m3_i2c_write(sd, 0x0050, addr1);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0054, addr2);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0F14, data);
+ CHECK_ERR(err);
+
+ return err;
+}
+
+static int s5c73m3_read(struct v4l2_subdev *sd,
+ unsigned short addr1, unsigned short addr2, unsigned short *data)
+{
+ int err;
+
+ err = s5c73m3_i2c_write(sd, 0xfcfc, 0x3310);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0058, addr1);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x005C, addr2);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_read(sd, 0x0F14, data);
+ CHECK_ERR(err);
+
+ return err;
+}
+
+static int s5c73m3_i2c_check_status(struct v4l2_subdev *sd)
+{
+ int err = 0;
+ int index = 0;
+ u16 status = 0;
+ u16 i2c_status = 0;
+ u16 i2c_seq_status = 0;
+
+ do {
+ err = s5c73m3_read(sd, 0x0009, S5C73M3_STATUS, &status);
+ if (status == 0xffff)
+ break;
+
+ index++;
+ udelay(500);
+ } while (index < 2000); /* 1 sec */
+
+ if (index >= 2000) {
+ err = s5c73m3_read(sd, 0x0009,
+ S5C73M3_I2C_ERR_STATUS, &i2c_status);
+ err = s5c73m3_read(sd, 0x0009,
+ S5C73M3_I2C_SEQ_STATUS, &i2c_seq_status);
+ cam_dbg("TimeOut!! index:%d,status:%#x,i2c_stauts:%#x,i2c_seq_status:%#x\n",
+ index,
+ status,
+ i2c_status,
+ i2c_seq_status);
+
+ err = -1;
+ }
+
+ return err;
+}
+
+static int s5c73m3_writeb(struct v4l2_subdev *sd,
+ unsigned short addr, unsigned short data)
+{
+ int err;
+ err = s5c73m3_i2c_check_status(sd);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0050, 0x0009);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0054, 0x5000);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0F14, addr);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0F14, data);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0054, 0x5080);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0F14, 0x0001);
+ CHECK_ERR(err);
+
+ return err;
+}
+
+static int s5c73m3_set_mode(struct v4l2_subdev *sd)
+{
+ struct s5c73m3_state *state = to_state(sd);
+ int err;
+ cam_trace("E\n");
+
+ if (state->format_mode != V4L2_PIX_FMT_MODE_CAPTURE) {
+ if (state->hdr_mode) {
+ err = s5c73m3_writeb(sd, S5C73M3_IMG_OUTPUT,
+ S5C73M3_HDR_OUTPUT);
+ CHECK_ERR(err);
+ cam_dbg("hdr ouput mode\n");
+ } else {
+ err = s5c73m3_writeb(sd, S5C73M3_IMG_OUTPUT,
+ S5C73M3_YUV_OUTPUT);
+ CHECK_ERR(err);
+ cam_dbg("yuv ouput mode\n");
+ }
+ } else {
+ if (state->hybrid_mode) {
+ err = s5c73m3_writeb(sd, S5C73M3_IMG_OUTPUT,
+ S5C73M3_HYBRID_OUTPUT);
+ CHECK_ERR(err);
+ cam_dbg("hybrid ouput mode\n");
+ } else {
+ err = s5c73m3_writeb(sd, S5C73M3_IMG_OUTPUT,
+ S5C73M3_INTERLEAVED_OUTPUT);
+ CHECK_ERR(err);
+ cam_dbg("interleaved ouput mode\n");
+ }
+ }
+
+ cam_trace("X\n");
+ return 0;
+}
+
+/*
+ * v4l2_subdev_core_ops
+ */
+static int s5c73m3_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(s5c73m3_ctrls); i++) {
+ if (qc->id == s5c73m3_ctrls[i].id) {
+ qc->maximum = s5c73m3_ctrls[i].maximum;
+ qc->minimum = s5c73m3_ctrls[i].minimum;
+ qc->step = s5c73m3_ctrls[i].step;
+ qc->default_value = s5c73m3_ctrls[i].default_value;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int s5c73m3_set_antibanding(struct v4l2_subdev *sd, int val)
+{
+ int err = 0;
+ int antibanding_mode = 0;
+
+ switch (val) {
+ case ANTI_BANDING_OFF:
+ antibanding_mode = S5C73M3_FLICKER_NONE;
+ break;
+ case ANTI_BANDING_50HZ:
+ antibanding_mode = S5C73M3_FLICKER_AUTO_50HZ;
+ break;
+ case ANTI_BANDING_60HZ:
+ antibanding_mode = S5C73M3_FLICKER_AUTO_60HZ;
+ break;
+ case ANTI_BANDING_AUTO:
+ default:
+ antibanding_mode = S5C73M3_FLICKER_AUTO;
+ break;
+
+ }
+
+ err = s5c73m3_writeb(sd, S5C73M3_FLICKER_MODE, antibanding_mode);
+ CHECK_ERR(err);
+
+ return err;
+}
+
+static int s5c73m3_set_af_softlanding(struct v4l2_subdev *sd)
+{
+ int err = 0;
+
+ cam_trace("E\n");
+
+ err = s5c73m3_writeb(sd, S5C73M3_AF_SOFTLANDING,
+ S5C73M3_AF_SOFTLANDING_ON);
+ CHECK_ERR(err);
+ cam_trace("X\n");
+
+ return 0;
+}
+
+static int s5c73m3_dump_fw(struct v4l2_subdev *sd)
+{
+ return 0;
+}
+
+static int s5c73m3_get_sensor_fw_binary(struct v4l2_subdev *sd)
+{
+ struct s5c73m3_state *state = to_state(sd);
+ u16 read_val;
+ int i, rxSize;
+ int err = 0;
+ struct file *fp = NULL;
+ mm_segment_t old_fs;
+ long ret = 0;
+ char fw_path[25] = {0,};
+
+ /*ARM go*/
+ err = s5c73m3_write(sd, 0x3000, 0x0004, 0xFFFF);
+ CHECK_ERR(err);
+
+ udelay(400);
+
+ /*Check boot done*/
+ for (i = 0; i < 3; i++) {
+ err = s5c73m3_read(sd, 0x3010, 0x0010, &read_val);
+ CHECK_ERR(err);
+
+ if (read_val == 0x0C)
+ break;
+
+ udelay(100);
+ }
+
+ if (read_val != 0x0C) {
+ cam_err("boot fail, read_val %#x\n", read_val);
+ return -1;
+ }
+
+ /* Change I/O Driver Current in order to read from F-ROM */
+ err = s5c73m3_write(sd, 0x3010, 0x0120, 0x0820);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x3010, 0x0124, 0x0820);
+ CHECK_ERR(err);
+
+ /*P,M,S and Boot Mode*/
+ err = s5c73m3_write(sd, 0x3010, 0x0014, 0x2146);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x3010, 0x0010, 0x230C);
+ CHECK_ERR(err);
+
+ udelay(200);
+
+ /*Check SPI ready*/
+ for (i = 0; i < 300; i++) {
+ err = s5c73m3_read(sd, 0x3010, 0x0010, &read_val);
+ CHECK_ERR(err);
+
+ if (read_val == 0x230E)
+ break;
+
+ udelay(100);
+ }
+
+ if (read_val != 0x230E) {
+ cam_err("SPI not ready, read_val %#x\n", read_val);
+ return -1;
+ }
+
+ /*ARM reset*/
+ err = s5c73m3_write(sd, 0x3000, 0x0004, 0xFFFD);
+ CHECK_ERR(err);
+
+ /*remap*/
+ err = s5c73m3_write(sd, 0x3010, 0x00A4, 0x0183);
+ CHECK_ERR(err);
+
+ /*ARM go again*/
+ err = s5c73m3_write(sd, 0x3000, 0x0004, 0xFFFF);
+ CHECK_ERR(err);
+
+ mdelay(200);
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ if (state->sensor_fw[0] == 'O') {
+ sprintf(fw_path, "/data/cfw/SlimISP_G%c.bin",
+ state->sensor_fw[1]);
+ } else if (state->sensor_fw[0] == 'S') {
+ sprintf(fw_path, "/data/cfw/SlimISP_Z%c.bin",
+ state->sensor_fw[1]);
+ } else {
+ sprintf(fw_path, "/data/cfw/SlimISP_%c%c.bin",
+ state->sensor_fw[0],
+ state->sensor_fw[1]);
+ }
+
+ fp = filp_open(fw_path, O_WRONLY|O_CREAT, 0644);
+ if (IS_ERR(fp) || fp == NULL) {
+ cam_err("failed to open %s, err %ld\n",
+ fw_path, PTR_ERR(fp));
+ err = -EINVAL;
+ goto out;
+ }
+
+ /* SPI Copy mode ready I2C CMD */
+ err = s5c73m3_writeb(sd, 0x0924, 0x0000);
+ CHECK_ERR(err);
+
+ rxSize = 64*1024;
+ mdelay(10);
+ s5c73m3_i2c_check_status(sd);
+
+ err = s5c73m3_spi_read((char *)&data_memory,
+ state->sensor_size, rxSize);
+ CHECK_ERR(err);
+
+ ret = vfs_write(fp, (char __user *)data_memory,
+ state->sensor_size, &fp->f_pos);
+
+ if (fp != NULL)
+ filp_close(fp, current->files);
+
+out:
+ set_fs(old_fs);
+
+ return err;
+}
+
+
+static int s5c73m3_get_sensor_fw_version(struct v4l2_subdev *sd)
+{
+ struct s5c73m3_state *state = to_state(sd);
+ u16 read_val;
+ u16 sensor_fw;
+ u16 sensor_type;
+ u16 temp_buf;
+ int i;
+ int err = 0;
+
+ /*ARM go*/
+ err = s5c73m3_write(sd, 0x3000, 0x0004, 0xFFFF);
+ CHECK_ERR(err);
+
+ udelay(400);
+
+ /*Check boot done*/
+ for (i = 0; i < 3; i++) {
+ err = s5c73m3_read(sd, 0x3010, 0x0010, &read_val);
+ CHECK_ERR(err);
+
+ if (read_val == 0x0C)
+ break;
+
+ udelay(100);
+ }
+
+ if (read_val != 0x0C) {
+ cam_err("boot fail, read_val %#x\n", read_val);
+ return -1;
+ }
+
+ /* Change I/O Driver Current in order to read from F-ROM */
+ err = s5c73m3_write(sd, 0x3010, 0x0120, 0x0820);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x3010, 0x0124, 0x0820);
+ CHECK_ERR(err);
+
+ /* Offset Setting */
+ err = s5c73m3_write(sd, 0x0001, 0x0418, 0x0008);
+ CHECK_ERR(err);
+
+ /*P,M,S and Boot Mode*/
+ err = s5c73m3_write(sd, 0x3010, 0x0014, 0x2146);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x3010, 0x0010, 0x230C);
+ CHECK_ERR(err);
+
+ udelay(200);
+
+ /*Check SPI ready*/
+ for (i = 0; i < 300; i++) {
+ err = s5c73m3_read(sd, 0x3010, 0x0010, &read_val);
+ CHECK_ERR(err);
+
+ if (read_val == 0x230E)
+ break;
+
+ udelay(100);
+ }
+
+ if (read_val != 0x230E) {
+ cam_err("SPI not ready, read_val %#x\n", read_val);
+ return -1;
+ }
+
+ /*ARM reset*/
+ err = s5c73m3_write(sd, 0x3000, 0x0004, 0xFFFD);
+ CHECK_ERR(err);
+
+ /*remap*/
+ err = s5c73m3_write(sd, 0x3010, 0x00A4, 0x0183);
+ CHECK_ERR(err);
+
+ for (i = 0; i < 6; i++) {
+ err = s5c73m3_read(sd, 0x0000, 0x06+i*2, &sensor_type);
+ CHECK_ERR(err);
+ state->sensor_type[i*2] = sensor_type&0x00ff;
+ state->sensor_type[i*2+1] = (sensor_type&0xff00)>>8;
+#ifdef FEATURE_DEBUG_DUMP
+ cam_err("0x%x\n", sensor_type);
+#endif
+ }
+ state->sensor_type[i*2+2] = ' ';
+
+ for (i = 0; i < 3; i++) {
+ err = s5c73m3_read(sd, 0x0000, i*2, &sensor_fw);
+ CHECK_ERR(err);
+ state->sensor_fw[i*2] = sensor_fw&0x00ff;
+ state->sensor_fw[i*2+1] = (sensor_fw&0xff00)>>8;
+#ifdef FEATURE_DEBUG_DUMP
+ cam_err("0x%x\n", sensor_fw);
+#endif
+ }
+ state->sensor_fw[i*2+2] = ' ';
+
+ state->sensor_size = 0;
+ for (i = 0; i < 2; i++) {
+ err = s5c73m3_read(sd, 0x0000, 0x0014+i*2, &temp_buf);
+ state->sensor_size += temp_buf<<(i*16);
+ CHECK_ERR(err);
+ }
+
+ memcpy(sysfs_sensor_fw, state->sensor_fw,
+ sizeof(state->sensor_fw));
+ memcpy(sysfs_sensor_type, state->sensor_type,
+ sizeof(state->sensor_type));
+
+ cam_dbg("Sensor_version = %s, Sensor_Type = %s\n",
+ state->sensor_fw, state->sensor_type);
+
+ if ((state->sensor_fw[0] < 'A') || state->sensor_fw[0] > 'Z') {
+ cam_dbg("Sensor Version is invalid data\n");
+#ifdef FEATURE_DEBUG_DUMP
+ cam_err("0000h : ");
+ for (i = 0; i < 0x20; i++) {
+ err = s5c73m3_read(sd, 0x0000, i*2, &sensor_fw);
+ cam_err("%x", sensor_fw);
+
+ if (i == 0x10)
+ cam_err("\n 0010h : ");
+ }
+ mdelay(50);
+ memcpy(state->sensor_type,
+ state->sensor_fw,
+ 0x100000); /* for kernel panic */
+#endif
+ err = -1;
+ }
+
+ return err;
+}
+
+static int s5c73m3_open_firmware_file(struct v4l2_subdev *sd,
+ const char *filename, u8 *buf, u16 offset, u16 size) {
+ struct file *fp;
+ int err = 0;
+ mm_segment_t old_fs;
+ long nread;
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ fp = filp_open(filename, O_RDONLY, 0);
+ if (IS_ERR(fp)) {
+ err = -ENOENT;
+ goto out;
+ } else {
+ cam_dbg("%s is opened\n", filename);
+ }
+
+ err = vfs_llseek(fp, offset, SEEK_SET);
+ if (err < 0) {
+ cam_warn("failed to fseek, %d\n", err);
+ goto out;
+ }
+
+ nread = vfs_read(fp, (char __user *)buf, size, &fp->f_pos);
+
+ if (nread != size) {
+ cam_err("failed to read firmware file, %ld Bytes\n", nread);
+ err = -EIO;
+ goto out;
+ }
+out:
+ if (!IS_ERR(fp))
+ filp_close(fp, current->files);
+
+ set_fs(old_fs);
+
+ return err;
+}
+
+static int s5c73m3_compare_date(struct v4l2_subdev *sd,
+ int index1, int index2)
+{
+ u8 date1[5] = {0,};
+ u8 date2[5] = {0,};
+
+ strncpy((char *)&date1, &camfw_info[index1].ver[2], 4);
+ strncpy((char *)&date2, &camfw_info[index2].ver[2], 4);
+ cam_dbg("date1 = %s, date2 = %s\n, compare result = %d",
+ date1,
+ date2,
+ strcmp((char *)&date1, (char *)&date2));
+
+ return strcmp((char *)&date1, (char *)&date2);
+}
+
+static int s5c73m3_get_phone_fw_version(struct v4l2_subdev *sd)
+{
+ struct device *dev = sd->v4l2_dev->dev;
+ struct s5c73m3_state *state = to_state(sd);
+ const struct firmware *fw = {0, };
+ char fw_path[20] = {0,};
+ char fw_path_in_data[25] = {0,};
+ u8 *buf = NULL;
+ int err = 0;
+ int retVal = 0;
+ int fw_requested = 1;
+
+ if (state->sensor_fw[0] == 'O') {
+ sprintf(fw_path, "SlimISP_G%c.bin",
+ state->sensor_fw[1]);
+ } else if (state->sensor_fw[0] == 'S') {
+ sprintf(fw_path, "SlimISP_Z%c.bin",
+ state->sensor_fw[1]);
+ } else {
+ sprintf(fw_path, "SlimISP_%c%c.bin",
+ state->sensor_fw[0],
+ state->sensor_fw[1]);
+ }
+ sprintf(fw_path_in_data, "/data/cfw/%s",
+ fw_path);
+
+ buf = vmalloc(S5C73M3_FW_VER_LEN+1);
+ if (!buf) {
+ cam_err("failed to allocate memory\n");
+ err = -ENOMEM;
+ goto out;
+ }
+
+ retVal = s5c73m3_open_firmware_file(sd,
+ S5C73M3_FW_PATH,
+ buf,
+ S5C73M3_FW_VER_FILE_CUR,
+ S5C73M3_FW_VER_LEN);
+ if (retVal >= 0) {
+ camfw_info[S5C73M3_SD_CARD].opened = 1;
+ memcpy(camfw_info[S5C73M3_SD_CARD].ver,
+ buf,
+ S5C73M3_FW_VER_LEN);
+ camfw_info[S5C73M3_SD_CARD]
+ .ver[S5C73M3_FW_VER_LEN+1] = ' ';
+ state->fw_index = S5C73M3_SD_CARD;
+ fw_requested = 0;
+ }
+request_fw:
+ if (fw_requested) {
+ /* check fw in data folder */
+ retVal = s5c73m3_open_firmware_file(sd,
+ fw_path_in_data,
+ buf,
+ S5C73M3_FW_VER_FILE_CUR,
+ S5C73M3_FW_VER_LEN);
+ if (retVal >= 0) {
+ camfw_info[S5C73M3_IN_DATA].opened = 1;
+ memcpy(camfw_info[S5C73M3_IN_DATA].ver,
+ buf,
+ S5C73M3_FW_VER_LEN);
+ camfw_info[S5C73M3_IN_DATA]
+ .ver[S5C73M3_FW_VER_LEN+1] = ' ';
+ }
+
+ /* check fw in system folder */
+ retVal = request_firmware(&fw, fw_path, dev);
+ if (retVal == 0) {
+ camfw_info[S5C73M3_IN_SYSTEM].opened = 1;
+ memcpy(camfw_info[S5C73M3_IN_SYSTEM].ver,
+ (u8 *)&fw->data[S5C73M3_FW_VER_FILE_CUR],
+ S5C73M3_FW_VER_LEN);
+ }
+
+ /* compare */
+ if (camfw_info[S5C73M3_IN_DATA].opened == 0 &&
+ camfw_info[S5C73M3_IN_SYSTEM].opened == 1) {
+ state->fw_index = S5C73M3_IN_SYSTEM;
+ } else if (camfw_info[S5C73M3_IN_DATA].opened == 1 &&
+ camfw_info[S5C73M3_IN_SYSTEM].opened == 0) {
+ state->fw_index = S5C73M3_IN_DATA;
+ } else if (camfw_info[S5C73M3_IN_DATA].opened == 1 &&
+ camfw_info[S5C73M3_IN_SYSTEM].opened == 1) {
+ retVal = s5c73m3_compare_date(sd,
+ S5C73M3_IN_DATA,
+ S5C73M3_IN_SYSTEM);
+ if (retVal <= 0) {
+ /*unlink(&fw_path_in_data);*/
+ state->fw_index = S5C73M3_IN_SYSTEM;
+ } else {
+ state->fw_index = S5C73M3_IN_DATA;
+ }
+ } else {
+ cam_dbg("get new fw to F-ROM : %s Version\n",
+ state->sensor_fw);
+ if (fw != NULL)
+ release_firmware(fw);
+
+ retVal = state->pdata->is_isp_reset();
+ CHECK_ERR(retVal);
+ retVal = s5c73m3_set_timing_register_for_vdd(sd);
+ CHECK_ERR(retVal);
+ retVal = s5c73m3_get_sensor_fw_binary(sd);
+ CHECK_ERR(retVal);
+ goto request_fw;
+ }
+
+ memcpy(state->phone_fw,
+ camfw_info[state->fw_index].ver,
+ S5C73M3_FW_VER_LEN);
+ }
+ memcpy(state->phone_fw,
+ camfw_info[state->fw_index].ver,
+ S5C73M3_FW_VER_LEN);
+ state->phone_fw[S5C73M3_FW_VER_LEN+1] = ' ';
+
+ memcpy(sysfs_phone_fw, state->phone_fw, sizeof(state->phone_fw));
+ cam_dbg("Phone_version = %s(index=%d)\n",
+ state->phone_fw, state->fw_index);
+
+out:
+ if (buf != NULL)
+ vfree(buf);
+
+ if (fw_requested)
+ release_firmware(fw);
+
+ return err;
+}
+
+static int s5c73m3_update_camerafw_to_FROM(struct v4l2_subdev *sd)
+{
+ int err;
+ int index = 0;
+ u16 status = 0;
+
+ do {
+ /* stauts 0 : not ready ISP */
+ if (status == 0) {
+ err = s5c73m3_writeb(sd, 0x0906, 0x0000);
+ CHECK_ERR(err);
+ }
+
+ err = s5c73m3_read(sd, 0x0009, 0x5906, &status);
+ /* Success : 0x05, Fail : 0x07 , Progressing : 0xFFFF*/
+ if (status == 0x0005 ||
+ status == 0x0007)
+ break;
+
+ index++;
+ msleep(20);
+ } while (index < 500); /* 10 sec */
+
+
+ if (status == 0x0007)
+ return -1;
+ else
+ return 0;
+}
+
+static int s5c73m3_SPI_booting_by_ISP(struct v4l2_subdev *sd)
+{
+ u16 read_val;
+ int i;
+ int err = 0;
+
+ /*ARM go*/
+ err = s5c73m3_write(sd, 0x3000, 0x0004, 0xFFFF);
+ CHECK_ERR(err);
+
+ udelay(400);
+
+ /*Check boot done*/
+ for (i = 0; i < 3; i++) {
+ err = s5c73m3_read(sd, 0x3010, 0x0010, &read_val);
+ CHECK_ERR(err);
+
+ if (read_val == 0x0C)
+ break;
+
+ udelay(100);
+ }
+
+ if (read_val != 0x0C) {
+ cam_err("boot fail, read_val %#x\n", read_val);
+ return -1;
+ }
+
+ /* Change I/O Driver Current in order to read from F-ROM */
+ err = s5c73m3_write(sd, 0x3010, 0x0120, 0x0820);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x3010, 0x0124, 0x0820);
+ CHECK_ERR(err);
+
+ /*P,M,S and Boot Mode*/
+ err = s5c73m3_write(sd, 0x3010, 0x0014, 0x2146);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x3010, 0x0010, 0x230C);
+ CHECK_ERR(err);
+
+ udelay(200);
+
+ /*Check SPI ready*/
+ for (i = 0; i < 300; i++) {
+ err = s5c73m3_read(sd, 0x3010, 0x0010, &read_val);
+ CHECK_ERR(err);
+
+ if (read_val == 0x230E)
+ break;
+
+ udelay(100);
+ }
+
+ if (read_val != 0x230E) {
+ cam_err("SPI not ready, read_val %#x\n", read_val);
+ return -1;
+ }
+
+ /*ARM reset*/
+ err = s5c73m3_write(sd, 0x3000, 0x0004, 0xFFFD);
+ CHECK_ERR(err);
+
+ /*remap*/
+ err = s5c73m3_write(sd, 0x3010, 0x00A4, 0x0183);
+ CHECK_ERR(err);
+
+ /*ARM go*/
+ err = s5c73m3_write(sd, 0x3000, 0x0004, 0xFFFF);
+ CHECK_ERR(err);
+
+ return err;
+}
+
+static int s5c73m3_check_fw_date(struct v4l2_subdev *sd)
+{
+ struct s5c73m3_state *state = to_state(sd);
+ u8 sensor_date[5] = {0,};
+ u8 phone_date[5] = {0,};
+
+ strncpy((char *)&sensor_date, &state->sensor_fw[2], 4);
+ strncpy((char *)&phone_date, (const char *)&state->phone_fw[2], 4);
+ cam_dbg("Sensor_date = %s, Phone_date = %s\n, compare result = %d",
+ sensor_date,
+ phone_date,
+ strcmp((char *)&sensor_date, (char *)&phone_date));
+
+ return strcmp((char *)&sensor_date, (char *)&phone_date);
+}
+
+static int s5c73m3_check_fw(struct v4l2_subdev *sd, int download)
+{
+ struct s5c73m3_state *state = to_state(sd);
+ int err, i;
+ int retVal;
+
+ if (!download) {
+ for (i = 0; i < S5C73M3_PATH_MAX; i++)
+ camfw_info[i].opened = 0;
+
+ err = s5c73m3_get_sensor_fw_version(sd);
+ CHECK_ERR(err);
+ s5c73m3_get_af_cal_version(sd);
+ err = s5c73m3_get_phone_fw_version(sd);
+ CHECK_ERR(err);
+ }
+
+ retVal = s5c73m3_check_fw_date(sd);
+ err = state->pdata->is_isp_reset();
+ CHECK_ERR(err);
+ err = s5c73m3_set_timing_register_for_vdd(sd);
+ CHECK_ERR(err);
+
+ /* retVal = 0 : Same Version
+ retVal < 0 : Phone Version is latest Version than sensorFW.
+ retVal > 0 : Sensor Version is latest version than phoenFW. */
+ if (retVal <= 0 || download) {
+ cam_dbg("Loading From PhoneFW......\n");
+ err = s5c73m3_SPI_booting(sd);
+ CHECK_ERR(err);
+
+ if (download) {
+ err = s5c73m3_update_camerafw_to_FROM(sd);
+ CHECK_ERR(err);
+ }
+ } else {
+ cam_dbg("Loading From SensorFW......\n");
+#if 0
+ err = s5c73m3_SPI_booting_by_ISP(sd);
+ CHECK_ERR(err);
+#else
+ err = s5c73m3_get_sensor_fw_binary(sd);
+ CHECK_ERR(err);
+#endif
+ }
+
+ return 0;
+}
+
+static int s5c73m3_set_sensor_mode(struct v4l2_subdev *sd, int val)
+{
+ struct s5c73m3_state *state = to_state(sd);
+ int err;
+ cam_dbg("E, value %d\n", val);
+
+retry:
+ switch (val) {
+ case SENSOR_CAMERA:
+ err = s5c73m3_writeb(sd, S5C73M3_AE_MODE,
+ S5C73M3_AUTO_MODE_AE_SET);
+ CHECK_ERR(err);
+ break;
+
+ case SENSOR_MOVIE:
+ err = s5c73m3_writeb(sd, S5C73M3_AE_MODE,
+ S5C73M3_FIXED_30FPS);
+ CHECK_ERR(err);
+ break;
+
+ default:
+ cam_warn("invalid value, %d\n", val);
+ val = SENSOR_CAMERA;
+ goto retry;
+ }
+ state->sensor_mode = val;
+
+ cam_trace("X\n");
+ return 0;
+}
+
+static int s5c73m3_set_flash(struct v4l2_subdev *sd, int val, int recording)
+{
+ struct s5c73m3_state *state = to_state(sd);
+ int err;
+ cam_dbg("E, value %d\n", val);
+
+retry:
+ switch (val) {
+ case FLASH_MODE_OFF:
+ err = s5c73m3_writeb(sd, S5C73M3_FLASH_MODE,
+ S5C73M3_FLASH_MODE_OFF);
+ CHECK_ERR(err);
+ err = s5c73m3_writeb(sd, S5C73M3_FLASH_TORCH,
+ S5C73M3_FLASH_TORCH_OFF);
+ CHECK_ERR(err);
+ break;
+
+ case FLASH_MODE_AUTO:
+ err = s5c73m3_writeb(sd, S5C73M3_FLASH_TORCH,
+ S5C73M3_FLASH_TORCH_OFF);
+ CHECK_ERR(err);
+ err = s5c73m3_writeb(sd, S5C73M3_FLASH_MODE,
+ S5C73M3_FLASH_MODE_AUTO);
+ CHECK_ERR(err);
+ break;
+
+ case FLASH_MODE_ON:
+ err = s5c73m3_writeb(sd, S5C73M3_FLASH_TORCH,
+ S5C73M3_FLASH_TORCH_OFF);
+ CHECK_ERR(err);
+ err = s5c73m3_writeb(sd, S5C73M3_FLASH_MODE,
+ S5C73M3_FLASH_MODE_ON);
+ CHECK_ERR(err);
+ break;
+
+ case FLASH_MODE_TORCH:
+ err = s5c73m3_writeb(sd, S5C73M3_FLASH_MODE,
+ S5C73M3_FLASH_MODE_OFF);
+ CHECK_ERR(err);
+ err = s5c73m3_writeb(sd, S5C73M3_FLASH_TORCH,
+ S5C73M3_FLASH_TORCH_ON);
+ CHECK_ERR(err);
+ break;
+
+ default:
+ cam_warn("invalid value, %d\n", val);
+ val = FLASH_MODE_OFF;
+ goto retry;
+ }
+ state->flash_mode = val;
+
+ cam_trace("X\n");
+ return 0;
+}
+
+static int s5c73m3_set_iso(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ int err;
+ cam_dbg("E, value %d\n", ctrl->value);
+
+retry:
+ switch (ctrl->value) {
+ case ISO_AUTO:
+ err = s5c73m3_writeb(sd, S5C73M3_ISO,
+ S5C73M3_ISO_AUTO);
+ CHECK_ERR(err);
+ break;
+
+ case ISO_100:
+ err = s5c73m3_writeb(sd, S5C73M3_ISO,
+ S5C73M3_ISO_100);
+ CHECK_ERR(err);
+ break;
+
+ case ISO_200:
+ err = s5c73m3_writeb(sd, S5C73M3_ISO,
+ S5C73M3_ISO_200);
+ CHECK_ERR(err);
+ break;
+
+ case ISO_400:
+ err = s5c73m3_writeb(sd, S5C73M3_ISO,
+ S5C73M3_ISO_400);
+ CHECK_ERR(err);
+ break;
+
+ case ISO_800:
+ err = s5c73m3_writeb(sd, S5C73M3_ISO,
+ S5C73M3_ISO_800);
+ CHECK_ERR(err);
+ break;
+
+ default:
+ cam_warn("invalid value, %d\n", ctrl->value);
+ ctrl->value = ISO_AUTO;
+ goto retry;
+ }
+
+ cam_trace("X\n");
+ return 0;
+}
+
+static int s5c73m3_set_metering(struct v4l2_subdev *sd, int val)
+{
+ int err;
+ cam_dbg("E, value %d\n", val);
+
+retry:
+ switch (val) {
+ case METERING_CENTER:
+ err = s5c73m3_writeb(sd, S5C73M3_METER,
+ S5C73M3_METER_CENTER);
+ CHECK_ERR(err);
+ break;
+
+ case METERING_SPOT:
+ err = s5c73m3_writeb(sd, S5C73M3_METER,
+ S5C73M3_METER_SPOT);
+ CHECK_ERR(err);
+ break;
+
+ case METERING_MATRIX:
+ err = s5c73m3_writeb(sd, S5C73M3_METER,
+ S5C73M3_METER_AVERAGE);
+ CHECK_ERR(err);
+ break;
+
+ default:
+ cam_warn("invalid value, %d\n", val);
+ val = METERING_CENTER;
+ goto retry;
+ }
+
+ cam_trace("X\n");
+ return 0;
+}
+
+static int s5c73m3_set_exposure(struct v4l2_subdev *sd,
+ struct v4l2_control *ctrl)
+{
+ int err;
+ cam_dbg("E, value %d\n", ctrl->value);
+
+ if (ctrl->value < -4 || ctrl->value > 4) {
+ cam_warn("invalid value, %d\n", ctrl->value);
+ ctrl->value = 0;
+ }
+ err = s5c73m3_writeb(sd, S5C73M3_EV,
+ ctrl->value + 4);
+ CHECK_ERR(err);
+
+ cam_trace("X\n");
+ return 0;
+}
+
+static int s5c73m3_set_contrast(struct v4l2_subdev *sd,
+ struct v4l2_control *ctrl)
+{
+ int err;
+ int contrast = 0;
+ int temp_contrast = 0;
+ cam_dbg("E, value %d\n", ctrl->value);
+
+ if (ctrl->value < 0 || ctrl->value > 4) {
+ cam_warn("invalid value, %d\n", ctrl->value);
+ ctrl->value = 2;
+ }
+ temp_contrast = ctrl->value - 2;
+ if (temp_contrast < 0)
+ contrast = (temp_contrast * (-1)) + 2;
+ else
+ contrast = temp_contrast;
+ err = s5c73m3_writeb(sd, S5C73M3_CONTRAST,
+ contrast);
+ CHECK_ERR(err);
+
+ cam_trace("X\n");
+ return 0;
+}
+
+static int s5c73m3_set_whitebalance(struct v4l2_subdev *sd, int val)
+{
+ struct s5c73m3_state *state = to_state(sd);
+ int err;
+ cam_dbg("E, value %d\n", val);
+
+retry:
+ switch (val) {
+ case WHITE_BALANCE_AUTO:
+ err = s5c73m3_writeb(sd, S5C73M3_AWB_MODE,
+ S5C73M3_AWB_MODE_AUTO);
+ CHECK_ERR(err);
+ break;
+
+ case WHITE_BALANCE_SUNNY:
+ err = s5c73m3_writeb(sd, S5C73M3_AWB_MODE,
+ S5C73M3_AWB_MODE_DAYLIGHT);
+ CHECK_ERR(err);
+ break;
+
+ case WHITE_BALANCE_CLOUDY:
+ err = s5c73m3_writeb(sd, S5C73M3_AWB_MODE,
+ S5C73M3_AWB_MODE_CLOUDY);
+ CHECK_ERR(err);
+ break;
+
+ case WHITE_BALANCE_TUNGSTEN:
+ err = s5c73m3_writeb(sd, S5C73M3_AWB_MODE,
+ S5C73M3_AWB_MODE_INCANDESCENT);
+ CHECK_ERR(err);
+ break;
+
+ case WHITE_BALANCE_FLUORESCENT:
+ err = s5c73m3_writeb(sd, S5C73M3_AWB_MODE,
+ S5C73M3_AWB_MODE_FLUORESCENT1);
+ CHECK_ERR(err);
+ break;
+
+ default:
+ cam_warn("invalid value, %d\n", val);
+ val = WHITE_BALANCE_AUTO;
+ goto retry;
+ }
+
+ state->wb_mode = val;
+
+ cam_trace("X\n");
+ return 0;
+}
+
+static int s5c73m3_set_scene_mode(struct v4l2_subdev *sd, int val)
+{
+ struct s5c73m3_state *state = to_state(sd);
+ int err;
+ cam_dbg("E, value %d\n", val);
+
+retry:
+ switch (val) {
+ case SCENE_MODE_NONE:
+ err = s5c73m3_writeb(sd, S5C73M3_SCENE_MODE,
+ S5C73M3_SCENE_MODE_NONE);
+ CHECK_ERR(err);
+ break;
+
+ case SCENE_MODE_PORTRAIT:
+ err = s5c73m3_writeb(sd, S5C73M3_SCENE_MODE,
+ S5C73M3_SCENE_MODE_PORTRAIT);
+ CHECK_ERR(err);
+ break;
+
+ case SCENE_MODE_LANDSCAPE:
+ err = s5c73m3_writeb(sd, S5C73M3_SCENE_MODE,
+ S5C73M3_SCENE_MODE_LANDSCAPE);
+ CHECK_ERR(err);
+ break;
+
+ case SCENE_MODE_SPORTS:
+ err = s5c73m3_writeb(sd, S5C73M3_SCENE_MODE,
+ S5C73M3_SCENE_MODE_SPORTS);
+ CHECK_ERR(err);
+ break;
+
+ case SCENE_MODE_PARTY_INDOOR:
+ err = s5c73m3_writeb(sd, S5C73M3_SCENE_MODE,
+ S5C73M3_SCENE_MODE_INDOOR);
+ CHECK_ERR(err);
+ break;
+
+ case SCENE_MODE_BEACH_SNOW:
+ err = s5c73m3_writeb(sd, S5C73M3_SCENE_MODE,
+ S5C73M3_SCENE_MODE_BEACH);
+ CHECK_ERR(err);
+ break;
+
+ case SCENE_MODE_SUNSET:
+ err = s5c73m3_writeb(sd, S5C73M3_SCENE_MODE,
+ S5C73M3_SCENE_MODE_SUNSET);
+ CHECK_ERR(err);
+ break;
+
+ case SCENE_MODE_DUSK_DAWN:
+ err = s5c73m3_writeb(sd, S5C73M3_SCENE_MODE,
+ S5C73M3_SCENE_MODE_DAWN);
+ CHECK_ERR(err);
+ break;
+
+ case SCENE_MODE_FALL_COLOR:
+ err = s5c73m3_writeb(sd, S5C73M3_SCENE_MODE,
+ S5C73M3_SCENE_MODE_FALL);
+ CHECK_ERR(err);
+ break;
+
+ case SCENE_MODE_NIGHTSHOT:
+ err = s5c73m3_writeb(sd, S5C73M3_SCENE_MODE,
+ S5C73M3_SCENE_MODE_NIGHT);
+ CHECK_ERR(err);
+ break;
+
+ case SCENE_MODE_BACK_LIGHT:
+ err = s5c73m3_writeb(sd, S5C73M3_SCENE_MODE,
+ S5C73M3_SCENE_MODE_AGAINSTLIGHT);
+ CHECK_ERR(err);
+ break;
+
+ case SCENE_MODE_FIREWORKS:
+ err = s5c73m3_writeb(sd, S5C73M3_SCENE_MODE,
+ S5C73M3_SCENE_MODE_FIRE);
+ CHECK_ERR(err);
+ break;
+
+ case SCENE_MODE_TEXT:
+ err = s5c73m3_writeb(sd, S5C73M3_SCENE_MODE,
+ S5C73M3_SCENE_MODE_TEXT);
+ CHECK_ERR(err);
+ break;
+
+ case SCENE_MODE_CANDLE_LIGHT:
+ err = s5c73m3_writeb(sd, S5C73M3_SCENE_MODE,
+ S5C73M3_SCENE_MODE_CANDLE);
+ CHECK_ERR(err);
+ break;
+
+ default:
+ cam_warn("invalid value, %d\n", val);
+ val = SCENE_MODE_NONE;
+ goto retry;
+ }
+
+ state->scene_mode = val;
+ cam_trace("X\n");
+ return 0;
+}
+
+static int s5c73m3_capture_firework(struct v4l2_subdev *sd)
+{
+ int err = 0;
+
+ cam_dbg("E, capture_firework\n");
+
+ err = s5c73m3_writeb(sd, S5C73M3_FIREWORK_CAPTURE, 0x0001);
+ CHECK_ERR(err);
+
+ return err;
+}
+
+static int s5c73m3_capture_nightshot(struct v4l2_subdev *sd)
+{
+ int err = 0;
+
+ cam_dbg("E, capture_nightshot\n");
+
+ err = s5c73m3_writeb(sd, S5C73M3_NIGHTSHOT_CAPTURE, 0x0001);
+ CHECK_ERR(err);
+
+ return err;
+}
+
+static int s5c73m3_set_effect(struct v4l2_subdev *sd, int val)
+{
+ int err = 0;
+ int num_entries = 0;
+ int i = 0;
+ cam_dbg("E, value %d\n", val);
+
+ if (val < IMAGE_EFFECT_BASE || val > IMAGE_EFFECT_MAX)
+ val = IMAGE_EFFECT_NONE;
+
+ num_entries = ARRAY_SIZE(s5c73m3_effects);
+ for (i = 0; i < num_entries; i++) {
+ if (val == s5c73m3_effects[i].index) {
+ err = s5c73m3_writeb(sd, S5C73M3_IMAGE_EFFECT,
+ s5c73m3_effects[i].reg_val);
+ CHECK_ERR(err);
+ break;
+ }
+ }
+
+ cam_trace("X\n");
+ return 0;
+}
+
+static int s5c73m3_set_wdr(struct v4l2_subdev *sd, int val)
+{
+ int err;
+ cam_dbg("E, value %d\n", val);
+
+retry:
+ switch (val) {
+ case WDR_OFF:
+ err = s5c73m3_writeb(sd, S5C73M3_WDR,
+ S5C73M3_WDR_OFF);
+ CHECK_ERR(err);
+ break;
+
+ case WDR_ON:
+ err = s5c73m3_writeb(sd, S5C73M3_WDR,
+ S5C73M3_WDR_ON);
+ CHECK_ERR(err);
+ break;
+
+ default:
+ cam_warn("invalid value, %d\n", val);
+ val = WDR_OFF;
+ goto retry;
+ }
+
+ cam_trace("X\n");
+ return 0;
+}
+
+static int s5c73m3_set_antishake(struct v4l2_subdev *sd, int val)
+{
+ int err = 0;
+ cam_dbg("E, value %d\n", val);
+
+ if (val) {
+ err = s5c73m3_writeb(sd, S5C73M3_AE_MODE,
+ S5C73M3_ANTI_SHAKE);
+ CHECK_ERR(err);
+ } else {
+ err = s5c73m3_writeb(sd, S5C73M3_AE_MODE,
+ S5C73M3_AUTO_MODE_AE_SET);
+ CHECK_ERR(err);
+ }
+ return err;
+}
+
+static int s5c73m3_get_af_cal_version(struct v4l2_subdev *sd)
+{
+ struct s5c73m3_state *state = to_state(sd);
+ u32 data = 0;
+ u16 status = 0;
+ int err = 0;
+
+ /* Calibration Device */
+ err = s5c73m3_read(sd, 0x0009, 0x300C, &status);
+ CHECK_ERR(err);
+ data = status;
+
+ status = 0;
+ err = s5c73m3_read(sd, 0x0009, 0x300E, &status);
+ CHECK_ERR(err);
+ data += status<<16;
+ state->cal_device = data;
+
+ /* Calibration DLL Version */
+ status = 0;
+ data = 0;
+ err = s5c73m3_read(sd, 0x0009, 0x4FF8, &status);
+ CHECK_ERR(err);
+ data = status;
+
+ status = 0;
+ err = s5c73m3_read(sd, 0x0009, 0x4FFA, &status);
+ CHECK_ERR(err);
+ data += status<<16;
+ state->cal_dll = data;
+
+ cam_dbg("Cal_Device = 0x%x, Cal_DLL = 0x%x\n",
+ state->cal_device, state->cal_dll);
+ return 0;
+}
+
+static int s5c73m3_stop_af_lens(struct v4l2_subdev *sd, int val)
+{
+ struct s5c73m3_state *state = to_state(sd);
+ int err;
+ cam_dbg("E, value %d\n", val);
+
+ if (val == CAF_START) {
+ if (state->focus.mode == FOCUS_MODE_CONTINOUS_VIDEO) {
+ err = s5c73m3_writeb(sd, S5C73M3_AF_MODE,
+ S5C73M3_AF_MODE_MOVIE_CAF_START);
+
+ } else {
+ err = s5c73m3_writeb(sd, S5C73M3_AF_MODE,
+ S5C73M3_AF_MODE_PREVIEW_CAF_START);
+ }
+ } else {
+ err = s5c73m3_writeb(sd, S5C73M3_AF_CON,
+ S5C73M3_AF_CON_STOP);
+ }
+ CHECK_ERR(err);
+
+ cam_trace("X\n");
+
+ return err;
+}
+
+static int s5c73m3_set_af(struct v4l2_subdev *sd, int val)
+{
+ struct s5c73m3_state *state = to_state(sd);
+ int err = 0;
+
+ cam_info("%s, mode %#x\n", val ? "start" : "stop", state->focus.mode);
+
+ state->focus.status = 0;
+
+ if (val) {
+ state->isflash = S5C73M3_ISNEED_FLASH_ON;
+
+ if (state->focus.mode == FOCUS_MODE_TOUCH)
+ err = s5c73m3_set_touch_auto_focus(sd);
+ else
+ err = s5c73m3_writeb(sd, S5C73M3_AF_CON,
+ S5C73M3_AF_CON_START);
+ } else {
+ err = s5c73m3_writeb(sd, S5C73M3_STILL_MAIN_FLASH
+ , S5C73M3_STILL_MAIN_FLASH_CANCEL);
+ err = s5c73m3_writeb(sd, S5C73M3_AF_CON,
+ S5C73M3_AF_CON_STOP);
+ state->isflash = S5C73M3_ISNEED_FLASH_UNDEFINED;
+ }
+
+ CHECK_ERR(err);
+
+ cam_trace("X\n");
+ return err;
+}
+
+static int s5c73m3_get_pre_flash(struct v4l2_subdev *sd,
+ struct v4l2_control *ctrl)
+{
+ int err = 0;
+ u16 pre_flash = false;
+
+ s5c73m3_read(sd, 0x0009, S5C73M3_STILL_PRE_FLASH | 0x5000, &pre_flash);
+ ctrl->value = pre_flash;
+ return err;
+}
+
+static int s5c73m3_get_af_result(struct v4l2_subdev *sd,
+ struct v4l2_control *ctrl)
+{
+ struct s5c73m3_state *state = to_state(sd);
+ int err = 0;
+ u16 af_status = S5C73M3_AF_STATUS_UNFOCUSED;
+ /*u16 temp_status = 0;*/
+
+ err = s5c73m3_read(sd, 0x0009, S5C73M3_AF_STATUS, &af_status);
+
+ /*err = s5c73m3_read(sd, 0x0009, 0x5840, &temp_status);*/
+
+ switch (af_status) {
+ case S5C73M3_AF_STATUS_FOCUSING:
+ case S5C73M3_CAF_STATUS_FOCUSING:
+ case S5C73M3_CAF_STATUS_FIND_SEARCHING_DIR:
+ case S5C73M3_AF_STATUS_INVALID:
+ ctrl->value = CAMERA_AF_STATUS_IN_PROGRESS;
+ break;
+
+ case S5C73M3_AF_STATUS_FOCUSED:
+ case S5C73M3_CAF_STATUS_FOCUSED:
+ ctrl->value = CAMERA_AF_STATUS_SUCCESS;
+ break;
+
+ case S5C73M3_CAF_STATUS_UNFOCUSED:
+ case S5C73M3_AF_STATUS_UNFOCUSED:
+ default:
+ ctrl->value = CAMERA_AF_STATUS_FAIL;
+ break;
+ }
+
+ state->focus.status = af_status;
+
+ /*cam_dbg("af_status = %d, frame_cnt = %d\n",
+ state->focus.status, temp_status);*/
+ return err;
+}
+
+static int s5c73m3_set_af_mode(struct v4l2_subdev *sd, int val)
+{
+ struct s5c73m3_state *state = to_state(sd);
+ int err;
+ cam_dbg("E, value %d\n", val);
+
+retry:
+ switch (val) {
+ case FOCUS_MODE_AUTO:
+ case FOCUS_MODE_INFINITY:
+ if (state->focus.mode != FOCUS_MODE_CONTINOUS_PICTURE) {
+ err = s5c73m3_writeb(sd, S5C73M3_AF_MODE,
+ S5C73M3_AF_MODE_NORMAL);
+ CHECK_ERR(err);
+ } else {
+ err = s5c73m3_writeb(sd, S5C73M3_AF_CON,
+ S5C73M3_AF_CON_STOP);
+ CHECK_ERR(err);
+ }
+
+ state->focus.mode = val;
+ state->caf_mode = S5C73M3_AF_MODE_NORMAL;
+ break;
+
+ case FOCUS_MODE_MACRO:
+ if (state->focus.mode != FOCUS_MODE_CONTINOUS_PICTURE_MACRO) {
+ err = s5c73m3_writeb(sd, S5C73M3_AF_MODE,
+ S5C73M3_AF_MODE_MACRO);
+ CHECK_ERR(err);
+ } else {
+ err = s5c73m3_writeb(sd, S5C73M3_AF_CON,
+ S5C73M3_AF_CON_STOP);
+ CHECK_ERR(err);
+ }
+
+ state->focus.mode = val;
+ state->caf_mode = S5C73M3_AF_MODE_MACRO;
+ break;
+
+ case FOCUS_MODE_CONTINOUS_PICTURE:
+ state->isflash = S5C73M3_ISNEED_FLASH_UNDEFINED;
+
+ if (val != state->focus.mode &&
+ state->caf_mode != S5C73M3_AF_MODE_NORMAL) {
+ state->focus.mode = val;
+
+ err = s5c73m3_writeb(sd, S5C73M3_AF_MODE,
+ S5C73M3_AF_MODE_NORMAL);
+ CHECK_ERR(err);
+ state->caf_mode = S5C73M3_AF_MODE_NORMAL;
+ }
+
+ err = s5c73m3_writeb(sd, S5C73M3_AF_MODE,
+ S5C73M3_AF_MODE_PREVIEW_CAF_START);
+ CHECK_ERR(err);
+ break;
+
+ case FOCUS_MODE_CONTINOUS_PICTURE_MACRO:
+ state->isflash = S5C73M3_ISNEED_FLASH_UNDEFINED;
+ if (val != state->focus.mode &&
+ state->caf_mode != S5C73M3_AF_MODE_MACRO) {
+ state->focus.mode = val;
+
+ err = s5c73m3_writeb(sd, S5C73M3_AF_MODE,
+ S5C73M3_AF_MODE_MACRO);
+ state->caf_mode = S5C73M3_AF_MODE_MACRO;
+ CHECK_ERR(err);
+ }
+
+ err = s5c73m3_writeb(sd, S5C73M3_AF_MODE,
+ S5C73M3_AF_MODE_PREVIEW_CAF_START);
+ CHECK_ERR(err);
+ break;
+
+ case FOCUS_MODE_CONTINOUS_VIDEO:
+ state->focus.mode = val;
+
+ err = s5c73m3_writeb(sd, S5C73M3_AF_MODE,
+ S5C73M3_AF_MODE_MOVIE_CAF_START);
+ CHECK_ERR(err);
+ break;
+
+ case FOCUS_MODE_FACEDETECT:
+ state->focus.mode = val;
+ break;
+
+ case FOCUS_MODE_TOUCH:
+ state->focus.mode = val;
+ break;
+
+ default:
+ cam_warn("invalid value, %d\n", val);
+ val = FOCUS_MODE_AUTO;
+ goto retry;
+ }
+
+ state->focus.mode = val;
+
+ cam_trace("X\n");
+ return 0;
+}
+
+static int s5c73m3_set_touch_auto_focus(struct v4l2_subdev *sd)
+{
+ struct s5c73m3_state *state = to_state(sd);
+ int err;
+
+ err = s5c73m3_i2c_write(sd, 0xfcfc, 0x3310);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0050, 0x0009);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0054, S5C73M3_AF_TOUCH_POSITION);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0F14, state->focus.pos_x);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0F14, state->focus.pos_y);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0F14, state->real_preview_width);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0F14, state->real_preview_height);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0050, 0x0009);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0054, 0x5000);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0F14, 0x0E0A);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0F14, 0x0000);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0054, 0x5080);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0F14, 0x0001);
+ CHECK_ERR(err);
+
+ return 0;
+}
+
+static int s5c73m3_set_zoom(struct v4l2_subdev *sd, int value)
+{
+ int err;
+ cam_dbg("E, value %d\n", value);
+
+retry:
+ if (value < 0 || value > 30) {
+ cam_warn("invalid value, %d\n", value);
+ value = 0;
+ goto retry;
+ }
+ err = s5c73m3_writeb(sd, S5C73M3_ZOOM_STEP, value);
+ CHECK_ERR(err);
+
+ cam_trace("X\n");
+ return 0;
+}
+
+static int s5c73m3_set_jpeg_quality(struct v4l2_subdev *sd,
+ struct v4l2_control *ctrl)
+{
+ int val = ctrl->value, err;
+ cam_dbg("E, value %d\n", val);
+
+ if (val <= 65) /* Normal */
+ err = s5c73m3_writeb(sd, S5C73M3_IMAGE_QUALITY,
+ S5C73M3_IMAGE_QUALITY_NORMAL);
+ else if (val <= 75) /* Fine */
+ err = s5c73m3_writeb(sd, S5C73M3_IMAGE_QUALITY,
+ S5C73M3_IMAGE_QUALITY_FINE);
+ else /* Superfine */
+ err = s5c73m3_writeb(sd, S5C73M3_IMAGE_QUALITY,
+ S5C73M3_IMAGE_QUALITY_SUPERFINE);
+
+ CHECK_ERR(err);
+
+ cam_trace("X\n");
+ return 0;
+}
+
+static int s5c73m3_aeawb_lock_unlock(struct v4l2_subdev *sd, int val)
+{
+ struct s5c73m3_state *state = to_state(sd);
+ int err = 0;
+ int ae_lock = val & 0x1;
+ int awb_lock = (val & 0x2) >> 1;
+ int ae_lock_changed =
+ ~(ae_lock & state->ae_lock) & (ae_lock | state->ae_lock);
+ int awb_lock_changed =
+ ~(awb_lock & state->awb_lock) & (awb_lock | state->awb_lock);
+
+ if (ae_lock_changed) {
+ cam_dbg("ae lock - %s\n", ae_lock ? "true" : "false");
+ err = s5c73m3_writeb(sd, S5C73M3_AE_CON,
+ ae_lock ? S5C73M3_AE_STOP : S5C73M3_AE_START);
+ CHECK_ERR(err);
+ state->ae_lock = ae_lock;
+ }
+ if (awb_lock_changed &&
+ state->wb_mode == WHITE_BALANCE_AUTO) {
+ cam_dbg("awb lock - %s\n", awb_lock ? "true" : "false");
+ err = s5c73m3_writeb(sd, S5C73M3_AWB_CON,
+ awb_lock ? S5C73M3_AWB_STOP : S5C73M3_AWB_START);
+ CHECK_ERR(err);
+ state->awb_lock = awb_lock;
+ }
+
+ return 0;
+}
+
+static int s5c73m3_start_capture(struct v4l2_subdev *sd, int val)
+{
+ struct s5c73m3_state *state = to_state(sd);
+ int err = 0;
+ u16 isneed_flash = false;
+ u16 pre_flash = false;
+
+ s5c73m3_read(sd, 0x0009, S5C73M3_STILL_PRE_FLASH | 0x5000, &pre_flash);
+
+ if (state->flash_mode == FLASH_MODE_ON) {
+ if (!pre_flash) {
+ err = s5c73m3_writeb(sd, S5C73M3_STILL_PRE_FLASH
+ , S5C73M3_STILL_PRE_FLASH_FIRE);
+ msleep(100);
+ }
+ err = s5c73m3_writeb(sd, S5C73M3_STILL_MAIN_FLASH
+ , S5C73M3_STILL_MAIN_FLASH_FIRE);
+ } else if (state->flash_mode == FLASH_MODE_AUTO) {
+ if (pre_flash) {
+ err = s5c73m3_writeb(sd, S5C73M3_STILL_MAIN_FLASH
+ , S5C73M3_STILL_MAIN_FLASH_FIRE);
+ } else if (state->isflash != S5C73M3_ISNEED_FLASH_ON) {
+ err = s5c73m3_read(sd, 0x0009,
+ S5C73M3_AE_ISNEEDFLASH | 0x5000, &isneed_flash);
+ if (isneed_flash) {
+ err = s5c73m3_writeb(sd, S5C73M3_STILL_PRE_FLASH
+ , S5C73M3_STILL_PRE_FLASH_FIRE);
+ msleep(100);
+ err = s5c73m3_writeb(sd,
+ S5C73M3_STILL_MAIN_FLASH,
+ S5C73M3_STILL_MAIN_FLASH_FIRE);
+ }
+ }
+ }
+
+ state->isflash = S5C73M3_ISNEED_FLASH_UNDEFINED;
+
+ return 0;
+}
+
+static int s5c73m3_set_auto_bracket_mode(struct v4l2_subdev *sd)
+{
+ int err = 0;
+
+ err = s5c73m3_writeb(sd, S5C73M3_AE_AUTO_BRAKET,
+ S5C73M3_AE_AUTO_BRAKET_EV20);
+ CHECK_ERR(err);
+
+ return err;
+}
+
+static int s5c73m3_set_frame_rate(struct v4l2_subdev *sd, int fps)
+{
+ int err = 0;
+ struct s5c73m3_state *state = to_state(sd);
+
+ if (!state->stream_enable) {
+ state->fps = fps;
+ return 0;
+ }
+
+ switch (fps) {
+ case 30:
+ err = s5c73m3_writeb(sd, S5C73M3_AE_MODE,
+ S5C73M3_FIXED_30FPS); /* 30fps */
+ break;
+ case 20:
+ err = s5c73m3_writeb(sd, S5C73M3_AE_MODE,
+ S5C73M3_FIXED_20FPS); /* 20fps */
+ break;
+ case 15:
+ err = s5c73m3_writeb(sd, S5C73M3_AE_MODE,
+ S5C73M3_FIXED_15FPS); /* 15fps */
+ break;
+ case 10:
+ err = s5c73m3_writeb(sd, S5C73M3_AE_MODE,
+ S5C73M3_FIXED_10FPS); /* 10fps */
+ break;
+ default:
+ err = s5c73m3_writeb(sd, S5C73M3_AE_MODE,
+ S5C73M3_AUTO_MODE_AE_SET); /* auto */
+ break;
+ }
+ return err;
+}
+
+static int s5c73m3_set_face_zoom(struct v4l2_subdev *sd, int val)
+{
+ struct s5c73m3_state *state = to_state(sd);
+ int err;
+
+ cam_dbg("s5c73m3_set_face_zoom\n");
+
+ err = s5c73m3_writeb(sd, S5C73M3_AF_CON,
+ S5C73M3_AF_CON_STOP);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0xfcfc, 0x3310);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0050, 0x0009);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0054, S5C73M3_AF_TOUCH_POSITION);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0F14, state->focus.pos_x);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0F14, state->focus.pos_y);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0F14, state->preview->width);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0F14, state->preview->height);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0050, 0x0009);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0054, 0x5000);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0F14, S5C73M3_AF_FACE_ZOOM);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0F14, val); /*0:reset, 1:Start*/
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0054, 0x5080);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0F14, 0x0001);
+ CHECK_ERR(err);
+
+ udelay(400);
+ err = s5c73m3_writeb(sd, S5C73M3_AF_MODE,
+ S5C73M3_AF_MODE_PREVIEW_CAF_START);
+ CHECK_ERR(err);
+
+ return 0;
+}
+
+
+static int s5c73m3_set_face_detection(struct v4l2_subdev *sd, int val)
+{
+ int err;
+ cam_dbg("E, value %d\n", val);
+
+retry:
+ switch (val) {
+ case FACE_DETECTION_ON:
+ err = s5c73m3_writeb(sd, S5C73M3_FACE_DET,
+ S5C73M3_FACE_DET_ON);
+ CHECK_ERR(err);
+
+ err = s5c73m3_writeb(sd, S5C73M3_AF_MODE,
+ S5C73M3_AF_MODE_PREVIEW_CAF_START);
+ CHECK_ERR(err);
+
+ break;
+
+ case FACE_DETECTION_OFF:
+ err = s5c73m3_writeb(sd, S5C73M3_FACE_DET,
+ S5C73M3_FACE_DET_OFF);
+ CHECK_ERR(err);
+
+ err = s5c73m3_writeb(sd, S5C73M3_AF_MODE,
+ S5C73M3_AF_MODE_PREVIEW_CAF_START);
+ CHECK_ERR(err);
+
+ break;
+
+ default:
+ cam_warn("invalid value, %d\n", val);
+ val = FACE_DETECTION_OFF;
+ goto retry;
+ }
+
+ cam_trace("X\n");
+ return 0;
+
+}
+
+static int s5c73m3_set_hybrid_capture(struct v4l2_subdev *sd)
+{
+ int err;
+ cam_trace("E\n");
+
+ err = s5c73m3_writeb(sd, S5C73M3_HYBRID_CAPTURE, 1);
+
+ CHECK_ERR(err);
+
+ cam_trace("X\n");
+ return 0;
+}
+
+static int s5c73m3_get_lux(struct v4l2_subdev *sd,
+ struct v4l2_control *ctrl)
+{
+ int err = 0;
+ u16 lux_val = 0;
+
+ err = s5c73m3_read(sd, 0x0009, 0x5C88, &lux_val);
+ ctrl->value = lux_val;
+
+ return err;
+}
+
+static int s5c73m3_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct s5c73m3_state *state = to_state(sd);
+ int err = 0;
+
+ printk(KERN_INFO "id %d, value %d\n",
+ ctrl->id - V4L2_CID_PRIVATE_BASE, ctrl->value);
+
+ if (unlikely(state->isp.bad_fw && ctrl->id != V4L2_CID_CAM_UPDATE_FW)) {
+ cam_err("\"Unknown\" state, please update F/W");
+ return -ENOSYS;
+ }
+
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_FRAME_RATE:
+ err = s5c73m3_set_frame_rate(sd, ctrl->value);
+ break;
+
+ case V4L2_CID_CAMERA_FACE_DETECTION:
+ err = s5c73m3_set_face_detection(sd, ctrl->value);
+ break;
+
+ case V4L2_CID_CAMERA_FACE_ZOOM:
+ err = s5c73m3_set_face_zoom(sd, ctrl->value);
+ break;
+
+ case V4L2_CID_CAM_UPDATE_FW:
+ if (ctrl->value == FW_MODE_DUMP)
+ err = s5c73m3_dump_fw(sd);
+ else if (ctrl->value == FW_MODE_UPDATE)
+ err = s5c73m3_check_fw(sd, 1);
+ else
+ err = 0;
+
+ break;
+
+ case V4L2_CID_CAMERA_SENSOR_MODE:
+ err = s5c73m3_set_sensor_mode(sd, ctrl->value);
+ break;
+
+ case V4L2_CID_CAMERA_FLASH_MODE:
+ err = s5c73m3_set_flash(sd, ctrl->value, 0);
+ break;
+
+ case V4L2_CID_CAMERA_ANTI_BANDING:
+ err = s5c73m3_set_antibanding(sd, ctrl->value);
+ break;
+
+ case V4L2_CID_CAMERA_ISO:
+ err = s5c73m3_set_iso(sd, ctrl);
+ break;
+
+ case V4L2_CID_CAMERA_METERING:
+ err = s5c73m3_set_metering(sd, ctrl->value);
+ break;
+
+ case V4L2_CID_CAMERA_BRIGHTNESS:
+ err = s5c73m3_set_exposure(sd, ctrl);
+ break;
+
+ case V4L2_CID_CAMERA_CONTRAST:
+ err = s5c73m3_set_contrast(sd, ctrl);
+ break;
+
+ case V4L2_CID_CAMERA_WHITE_BALANCE:
+ err = s5c73m3_set_whitebalance(sd, ctrl->value);
+ break;
+
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ err = s5c73m3_set_scene_mode(sd, ctrl->value);
+ break;
+
+ case V4L2_CID_CAMERA_EFFECT:
+ err = s5c73m3_set_effect(sd, ctrl->value);
+ break;
+
+ case V4L2_CID_CAMERA_WDR:
+ err = s5c73m3_set_wdr(sd, ctrl->value);
+ break;
+
+ case V4L2_CID_CAMERA_ANTI_SHAKE:
+ if (state->sensor_mode == SENSOR_CAMERA)
+ err = s5c73m3_set_antishake(sd, ctrl->value);
+ break;
+
+ case V4L2_CID_CAMERA_DEFAULT_FOCUS_POSITION:
+ /*err = s5c73m3_set_af_mode(sd, state->focus.mode);*/
+ err = 0;
+ break;
+
+ case V4L2_CID_CAMERA_FOCUS_MODE:
+ /*state->focus.mode = ctrl->value;*/
+ err = s5c73m3_set_af_mode(sd, ctrl->value);
+ break;
+
+ case V4L2_CID_CAMERA_SET_AUTO_FOCUS:
+ state->real_preview_width = ((u32)ctrl->value >> 20) & 0xFFF;
+ state->real_preview_height = ((u32)ctrl->value >> 8) & 0xFFF;
+
+ err = s5c73m3_set_af(sd, (u32)ctrl->value & 0x000F);
+ break;
+
+ case V4L2_CID_CAMERA_OBJECT_POSITION_X:
+ state->focus.pos_x = ctrl->value;
+ break;
+
+ case V4L2_CID_CAMERA_OBJECT_POSITION_Y:
+ state->focus.pos_y = ctrl->value;
+ break;
+
+ case V4L2_CID_CAMERA_ZOOM:
+ err = s5c73m3_set_zoom(sd, ctrl->value);
+ break;
+
+ case V4L2_CID_CAM_JPEG_QUALITY:
+ err = s5c73m3_set_jpeg_quality(sd, ctrl);
+ break;
+
+ case V4L2_CID_CAMERA_CAPTURE:
+ err = s5c73m3_start_capture(sd, ctrl->value);
+
+ if (state->scene_mode == SCENE_MODE_FIREWORKS)
+ err = s5c73m3_capture_firework(sd);
+ else if (state->scene_mode == SCENE_MODE_NIGHTSHOT)
+ err = s5c73m3_capture_nightshot(sd);
+ break;
+
+ case V4L2_CID_CAMERA_HDR:
+ state->hdr_mode = ctrl->value;
+ err = 0;
+ break;
+
+ case V4L2_CID_CAMERA_HYBRID:
+ state->hybrid_mode = ctrl->value;
+ err = 0;
+ break;
+
+ case V4L2_CID_CAMERA_HYBRID_CAPTURE:
+ err = s5c73m3_set_hybrid_capture(sd);
+ break;
+
+ case V4L2_CID_CAMERA_VT_MODE:
+ state->vt_mode = ctrl->value;
+ break;
+
+ case V4L2_CID_CAMERA_JPEG_RESOLUTION:
+ state->jpeg_width = (u32)ctrl->value >> 16;
+ state->jpeg_height = (u32)ctrl->value & 0x0FFFF;
+ break;
+
+ case V4L2_CID_CAMERA_AEAWB_LOCK_UNLOCK:
+ err = s5c73m3_aeawb_lock_unlock(sd, ctrl->value);
+ break;
+
+ case V4L2_CID_CAMERA_CAF_START_STOP:
+ err = s5c73m3_stop_af_lens(sd, ctrl->value);
+ break;
+
+ default:
+ cam_err("no such control id %d, value %d\n",
+ ctrl->id - V4L2_CID_PRIVATE_BASE, ctrl->value);
+ /*err = -ENOIOCTLCMD;*/
+ err = 0;
+ break;
+ }
+
+ if (err < 0 && err != -ENOIOCTLCMD)
+ cam_err("failed, id %d, value %d\n",
+ ctrl->id - V4L2_CID_PRIVATE_BASE, ctrl->value);
+ return err;
+}
+
+static int s5c73m3_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_CAPTURE:
+ err = s5c73m3_get_pre_flash(sd, ctrl);
+ break;
+
+ case V4L2_CID_CAMERA_AUTO_FOCUS_RESULT:
+ err = s5c73m3_get_af_result(sd, ctrl);
+ break;
+
+ case V4L2_CID_CAM_JPEG_MEMSIZE:
+ ctrl->value = 0xA00000; /*interleaved data size*/
+ break;
+
+ case V4L2_CID_CAMERA_GET_LUX:
+ err = s5c73m3_get_lux(sd, ctrl);
+ break;
+
+ default:
+ cam_err("no such control id %d\n",
+ ctrl->id - V4L2_CID_PRIVATE_BASE);
+ /*err = -ENOIOCTLCMD*/
+ err = 0;
+ break;
+ }
+
+ if (err < 0 && err != -ENOIOCTLCMD)
+ cam_err("failed, id %d\n", ctrl->id - V4L2_CID_PRIVATE_BASE);
+
+ return err;
+}
+
+
+static int s5c73m3_g_ext_ctrl(struct v4l2_subdev *sd,
+ struct v4l2_ext_control *ctrl)
+{
+ struct s5c73m3_state *state = to_state(sd);
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_CAM_SENSOR_FW_VER:
+ strcpy(ctrl->string, state->phone_fw);
+ break;
+
+ default:
+ cam_err("no such control id %d\n",
+ ctrl->id - V4L2_CID_CAMERA_CLASS_BASE);
+ /*err = -ENOIOCTLCMD*/
+ break;
+ }
+
+ return err;
+}
+
+static int s5c73m3_g_ext_ctrls(struct v4l2_subdev *sd,
+ struct v4l2_ext_controls *ctrls)
+{
+ struct v4l2_ext_control *ctrl = ctrls->controls;
+ int i, err = 0;
+
+ for (i = 0; i < ctrls->count; i++, ctrl++) {
+ err = s5c73m3_g_ext_ctrl(sd, ctrl);
+ if (err) {
+ ctrls->error_idx = i;
+ break;
+ }
+ }
+ return err;
+}
+
+#ifndef CONFIG_VIDEO_S5C73M3_SPI
+int s5c73m3_spi_write(const u8 *addr, const int len, const int txSize)
+{ return 0; }
+#endif
+
+static int s5c73m3_load_fw(struct v4l2_subdev *sd)
+{
+ struct device *dev = sd->v4l2_dev->dev;
+ struct s5c73m3_state *state = to_state(sd);
+ const struct firmware *fw;
+ char fw_path[20] = {0,};
+ char fw_path_in_data[25] = {0,};
+ u8 *buf = NULL;
+ int err = 0;
+ int txSize = 0;
+
+ struct file *fp = NULL;
+ mm_segment_t old_fs;
+ long fsize = 0, nread;
+
+ if (state->sensor_fw[0] == 'O') {
+ sprintf(fw_path, "SlimISP_G%c.bin",
+ state->sensor_fw[1]);
+ } else if (state->sensor_fw[0] == 'S') {
+ sprintf(fw_path, "SlimISP_Z%c.bin",
+ state->sensor_fw[1]);
+ } else {
+ sprintf(fw_path, "SlimISP_%c%c.bin",
+ state->sensor_fw[0],
+ state->sensor_fw[1]);
+ }
+ sprintf(fw_path_in_data, "/data/cfw/%s",
+ fw_path);
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ if (state->fw_index == S5C73M3_SD_CARD ||
+ state->fw_index == S5C73M3_IN_DATA) {
+
+ if (state->fw_index == S5C73M3_SD_CARD)
+ fp = filp_open(S5C73M3_FW_PATH, O_RDONLY, 0);
+ else
+ fp = filp_open(fw_path_in_data, O_RDONLY, 0);
+ if (IS_ERR(fp))
+ goto out;
+ else
+ cam_dbg("%s is opened\n",
+ state->fw_index == S5C73M3_SD_CARD ?
+ S5C73M3_FW_PATH : fw_path_in_data);
+
+ fsize = fp->f_path.dentry->d_inode->i_size;
+
+ nread = vfs_read(fp, (char __user *)data_memory,
+ fsize, &fp->f_pos);
+ if (nread != fsize) {
+ cam_err("failed to read firmware file_2\n");
+ err = -EIO;
+ goto out;
+ }
+ set_fs(old_fs);
+ } else {
+ set_fs(old_fs);
+ err = request_firmware(&fw, fw_path, dev);
+ if (err != 0) {
+ /*cam_err("request_firmware falied\n");*/
+ err = -EINVAL;
+ goto out;
+ }
+
+ /*cam_dbg("start, size %d Bytes\n", fw->size);*/
+ buf = (u8 *)fw->data;
+ fsize = fw->size;
+ }
+
+ txSize = 60*1024; /*60KB*/
+
+ if (state->fw_index != S5C73M3_IN_SYSTEM) {
+ err = s5c73m3_spi_write((char *)&data_memory,
+ fsize, txSize);
+ if (err < 0) {
+ cam_err("s5c73m3_spi_write falied\n");
+ goto out;
+ }
+ } else {
+ err = s5c73m3_spi_write((char *)buf, fsize, txSize);
+ }
+out:
+ if (state->fw_index == S5C73M3_SD_CARD ||
+ state->fw_index == S5C73M3_IN_DATA) {
+ if (!IS_ERR(fp) && fp != NULL)
+ filp_close(fp, current->files);
+
+ vfree(buf);
+
+ set_fs(old_fs);
+ } else {
+ release_firmware(fw);
+ }
+
+ return err;
+}
+
+/*
+ * v4l2_subdev_video_ops
+ */
+static const struct s5c73m3_frmsizeenum *s5c73m3_get_frmsize
+ (const struct s5c73m3_frmsizeenum *frmsizes, int num_entries, int index)
+{
+ int i;
+
+ for (i = 0; i < num_entries; i++) {
+ if (frmsizes[i].index == index)
+ return &frmsizes[i];
+ }
+
+ return NULL;
+}
+
+static int s5c73m3_set_frmsize(struct v4l2_subdev *sd)
+{
+ struct s5c73m3_state *state = to_state(sd);
+ int err ;
+ cam_trace("E\n");
+
+ if (state->format_mode != V4L2_PIX_FMT_MODE_CAPTURE) {
+ err = s5c73m3_writeb(sd, S5C73M3_CHG_MODE,
+ S5C73M3_YUV_MODE | state->preview->reg_val |
+ (state->sensor_mode<<8));
+ CHECK_ERR(err);
+ } else {
+ err = s5c73m3_writeb(sd, S5C73M3_CHG_MODE,
+ S5C73M3_INTERLEAVED_MODE
+ | state->capture->reg_val | state->preview->reg_val
+ |(state->sensor_mode<<8));
+ CHECK_ERR(err);
+ }
+
+ cam_trace("X\n");
+ return 0;
+}
+
+static int s5c73m3_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *ffmt)
+{
+ struct s5c73m3_state *state = to_state(sd);
+ const struct s5c73m3_frmsizeenum **frmsize;
+ const struct s5c73m3_frmsizeenum **capfrmsize;
+
+ u32 width = ffmt->width;
+ u32 height = ffmt->height;
+ u32 tmp_width;
+ u32 old_index, old_index_cap;
+ int i, num_entries;
+ cam_trace("E\n");
+
+ if (unlikely(state->isp.bad_fw)) {
+ cam_err("\"Unknown\" state, please update F/W");
+ return -ENOSYS;
+ }
+ if (ffmt->width < ffmt->height) {
+ tmp_width = ffmt->height;
+ height = ffmt->width;
+ width = tmp_width;
+ }
+
+ if (ffmt->colorspace == V4L2_COLORSPACE_JPEG)
+ state->format_mode = V4L2_PIX_FMT_MODE_CAPTURE;
+ else
+ state->format_mode = V4L2_PIX_FMT_MODE_PREVIEW;
+
+ s5c73m3_set_mode(sd);
+
+ /*set frame size for preview(yuv)*/
+ frmsize = &state->preview;
+ old_index = *frmsize ? (*frmsize)->index : -1;
+ *frmsize = NULL;
+
+ num_entries = ARRAY_SIZE(preview_frmsizes);
+ for (i = 0; i < num_entries; i++) {
+ if (width == preview_frmsizes[i].width &&
+ height == preview_frmsizes[i].height) {
+ *frmsize = &preview_frmsizes[i];
+ break;
+ }
+ }
+
+ if (*frmsize == NULL) {
+ cam_warn("invalid yuv frame size %dx%d\n", width, height);
+ *frmsize = s5c73m3_get_frmsize(preview_frmsizes,
+ num_entries,
+ S5C73M3_PREVIEW_960X720);
+ }
+
+ /*set frame size for capture(jpeg)*/
+ /*it's meaningful for interleaved mode*/
+ capfrmsize = &state->capture;
+ old_index_cap = *capfrmsize ? (*capfrmsize)->index : -1;
+ *capfrmsize = NULL;
+
+ width = state->jpeg_width;
+ height = state->jpeg_height;
+
+ num_entries = ARRAY_SIZE(capture_frmsizes);
+ for (i = 0; i < num_entries; i++) {
+ if (width == capture_frmsizes[i].width &&
+ height == capture_frmsizes[i].height) {
+ *capfrmsize = &capture_frmsizes[i];
+ break;
+ }
+ }
+
+ if (*capfrmsize == NULL) {
+ cam_warn("invalid jpeg frame size %dx%d\n", width, height);
+ *capfrmsize = s5c73m3_get_frmsize(capture_frmsizes, num_entries,
+ S5C73M3_CAPTURE_VGA);
+ }
+
+ cam_dbg("yuv %dx%d\n", (*frmsize)->width, (*frmsize)->height);
+ cam_dbg("jpeg %dx%d\n", (*capfrmsize)->width, (*capfrmsize)->height);
+ if (state->stream_enable) {
+ if (ffmt->colorspace == V4L2_COLORSPACE_JPEG) {
+ if ((old_index != (*frmsize)->index)
+ || (old_index_cap != (*capfrmsize)->index))
+ s5c73m3_set_frmsize(sd);
+ } else {
+ if (old_index != (*frmsize)->index)
+ s5c73m3_set_frmsize(sd);
+ }
+ } else
+ s5c73m3_set_frmsize(sd);
+
+ cam_trace("X\n");
+ return 0;
+}
+
+static int s5c73m3_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+ struct s5c73m3_state *state = to_state(sd);
+
+ a->parm.capture.timeperframe.numerator = 1;
+ a->parm.capture.timeperframe.denominator = state->fps;
+
+ return 0;
+}
+
+static int s5c73m3_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+ struct s5c73m3_state *state = to_state(sd);
+ int err = 0;
+
+ u32 fps = a->parm.capture.timeperframe.denominator /
+ a->parm.capture.timeperframe.numerator;
+
+ if (unlikely(state->isp.bad_fw)) {
+ cam_err("\"Unknown\" state, please update F/W");
+ return -ENOSYS;
+ }
+
+ if (fps != state->fps) {
+ if (fps <= 0 || fps > 30) {
+ cam_err("invalid frame rate %d\n", fps);
+ fps = 30;
+ }
+ state->fps = fps;
+ }
+ cam_err("Frame rate = %d(%d)\n", fps, state->fps);
+
+ err = s5c73m3_set_frame_rate(sd, state->fps);
+ CHECK_ERR(err);
+
+ return 0;
+}
+
+static int s5c73m3_enum_framesizes(struct v4l2_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct s5c73m3_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->preview == NULL)
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ if (state->hdr_mode) {
+ fsize->discrete.width = state->capture->width;
+ fsize->discrete.height = state->capture->height;
+ } else {
+ fsize->discrete.width = state->preview->width;
+ fsize->discrete.height = state->preview->height;
+ }
+ return 0;
+}
+
+static int s5c73m3_s_stream_sensor(struct v4l2_subdev *sd, int onoff)
+{
+ int err = 0;
+ int index = 0;
+ u16 status = 0;
+ u16 i2c_status = 0;
+ u16 i2c_seq_status = 0;
+
+ cam_info("onoff=%d\n", onoff);
+ err = s5c73m3_writeb(sd, S5C73M3_SENSOR_STREAMING,
+ onoff ? S5C73M3_SENSOR_STREAMING_ON :
+ S5C73M3_SENSOR_STREAMING_OFF);
+ CHECK_ERR(err);
+
+ do {
+ err = s5c73m3_read(sd, 0x0009, S5C73M3_STATUS, &status);
+ if (status == 0xffff)
+ break;
+
+ index++;
+ msleep(20);
+ } while (index < 30);
+
+ if (index >= 30) {
+ err = s5c73m3_read(sd, 0x0009,
+ S5C73M3_I2C_ERR_STATUS, &i2c_status);
+ err = s5c73m3_read(sd, 0x0009,
+ S5C73M3_I2C_SEQ_STATUS, &i2c_seq_status);
+ cam_dbg("TimeOut!! index:%d,status:%#x,i2c_stauts:%#x,i2c_seq_status:%#x\n",
+ index,
+ status,
+ i2c_status,
+ i2c_seq_status);
+
+ err = -1;
+ }
+
+ return err;
+}
+
+static int s5c73m3_s_stream_hdr(struct v4l2_subdev *sd, int enable)
+{
+ struct s5c73m3_state *state = to_state(sd);
+ int err = 0;
+ cam_trace("E\n");
+
+ if (enable) {
+ err = s5c73m3_i2c_write(sd, 0x0050, 0x0009);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0054, 0x5000);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0F14, 0x0902);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0F14, 0x0008);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0F14, 0x091A);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0F14, 0x0002);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0F14, 0x0B10);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0F14, 0x8000 |
+ state->capture->reg_val |
+ state->preview->reg_val);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0054, 0x5080);
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_write(sd, 0x0F14, 0x0003);
+ CHECK_ERR(err);
+
+ err = s5c73m3_s_stream_sensor(sd, enable);
+ err = s5c73m3_set_auto_bracket_mode(sd);
+ } else {
+ err = s5c73m3_s_stream_sensor(sd, enable);
+ }
+
+ cam_trace("X\n");
+ return 0;
+}
+
+static int s5c73m3_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct s5c73m3_state *state = to_state(sd);
+ int err;
+
+ cam_trace("E\n");
+
+ if (unlikely(state->isp.bad_fw)) {
+ cam_err("\"Unknown\" state, please update F/W");
+ return -ENOSYS;
+ }
+
+ switch (enable) {
+ case STREAM_MODE_CAM_ON:
+ case STREAM_MODE_CAM_OFF:
+ switch (state->format_mode) {
+ case V4L2_PIX_FMT_MODE_CAPTURE:
+ cam_info("capture %s",
+ enable == STREAM_MODE_CAM_ON ? "on" : "off");
+
+ s5c73m3_s_stream_sensor(sd, enable);
+ if (enable == STREAM_MODE_CAM_ON &&
+ (state->focus.mode >=
+ FOCUS_MODE_CONTINOUS &&
+ state->focus.mode <=
+ FOCUS_MODE_CONTINOUS_VIDEO)) {
+ s5c73m3_set_af_mode(sd,
+ state->focus.mode);
+ }
+ break;
+
+ default:
+ cam_info("preview %s",
+ enable == STREAM_MODE_CAM_ON ? "on" : "off");
+
+ if (state->hdr_mode) {
+ err = s5c73m3_set_flash(sd, FLASH_MODE_OFF, 0);
+ err = s5c73m3_s_stream_hdr(sd, enable);
+ } else {
+ err = s5c73m3_s_stream_sensor(sd, enable);
+ if (enable == STREAM_MODE_CAM_ON &&
+ (state->focus.mode >=
+ FOCUS_MODE_CONTINOUS &&
+ state->focus.mode <=
+ FOCUS_MODE_CONTINOUS_VIDEO)) {
+ s5c73m3_set_af_mode(sd,
+ state->focus.mode);
+ }
+ }
+ break;
+ }
+ break;
+
+ case STREAM_MODE_MOVIE_ON:
+ if (state->flash_mode != FLASH_MODE_OFF)
+ err = s5c73m3_set_flash(sd, state->flash_mode, 1);
+
+ if (state->preview->index == S5C73M3_PREVIEW_720P ||
+ state->preview->index == S5C73M3_PREVIEW_1080P)
+ err = s5c73m3_set_af(sd, 1);
+ break;
+
+ case STREAM_MODE_MOVIE_OFF:
+ if (state->preview->index == S5C73M3_PREVIEW_720P ||
+ state->preview->index == S5C73M3_PREVIEW_1080P)
+ err = s5c73m3_set_af(sd, 0);
+
+ s5c73m3_set_flash(sd, FLASH_MODE_OFF, 1);
+ break;
+
+ default:
+ cam_err("invalid stream option, %d\n", enable);
+ break;
+ }
+
+#if 0
+ err = s5c73m3_writeb(sd, S5C73M3_AF_CAL, 0);
+ CHECK_ERR(err);
+#endif
+ state->stream_enable = enable;
+ if (state->stream_enable && state->hdr_mode == 0) {
+ if (state->fps)
+ s5c73m3_set_frame_rate(sd, state->fps);
+ }
+
+ cam_trace("X\n");
+ return 0;
+}
+
+static int s5c73m3_init_param(struct v4l2_subdev *sd)
+{
+ s5c73m3_set_flash(sd, FLASH_MODE_OFF, 0);
+ return 0;
+}
+
+static int s5c73m3_FROM_booting(struct v4l2_subdev *sd)
+{
+ u16 read_val;
+ int i, err;
+
+ cam_trace("E\n");
+
+ /*ARM go*/
+ err = s5c73m3_write(sd, 0x3000, 0x0004, 0xFFFF);
+ CHECK_ERR(err);
+
+ udelay(400);
+
+ /*Check boot done*/
+ for (i = 0; i < 4; i++) {
+ err = s5c73m3_read(sd, 0x3010, 0x0010, &read_val);
+ CHECK_ERR(err);
+
+ if (read_val == 0x0C)
+ break;
+
+ udelay(100);
+ }
+
+ if (read_val != 0x0C) {
+ cam_err("boot fail, read_val %#x\n", read_val);
+ return -1;
+ }
+
+ /*P,M,S and Boot Mode*/
+ err = s5c73m3_write(sd, 0x3100, 0x010C, 0x0044);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x3100, 0x0108, 0x000D);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x3100, 0x0304, 0x0001);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x0001, 0x0000, 0x5800);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x0001, 0x0002, 0x0002);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x3100, 0x0000, 0x0001);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x3010, 0x0014, 0x1B85);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x3010, 0x0010, 0x230C);
+ CHECK_ERR(err);
+
+ mdelay(300);
+
+ /*Check binary read done*/
+ for (i = 0; i < 3; i++) {
+ err = s5c73m3_read(sd, 0x3010, 0x0010, &read_val);
+ CHECK_ERR(err);
+
+ if (read_val == 0x230E)
+ break;
+
+ udelay(100);
+ }
+
+ if (read_val != 0x230E) {
+ cam_err("binary read fail, read_val %#x\n", read_val);
+ return -1;
+ }
+
+ /*ARM reset*/
+ err = s5c73m3_write(sd, 0x3000, 0x0004, 0xFFFD);
+ CHECK_ERR(err);
+
+ /*remap*/
+ err = s5c73m3_write(sd, 0x3010, 0x00A4, 0x0183);
+ CHECK_ERR(err);
+
+ /*ARM go again*/
+ err = s5c73m3_write(sd, 0x3000, 0x0004, 0xFFFF);
+ CHECK_ERR(err);
+
+ cam_trace("X\n");
+
+ return 0;
+}
+
+static int s5c73m3_SPI_booting(struct v4l2_subdev *sd)
+{
+ u16 read_val;
+ int i, err;
+
+ /*ARM go*/
+ err = s5c73m3_write(sd, 0x3000, 0x0004, 0xFFFF);
+ CHECK_ERR(err);
+
+ udelay(400);
+
+ /*Check boot done*/
+ for (i = 0; i < 3; i++) {
+ err = s5c73m3_read(sd, 0x3010, 0x0010, &read_val);
+ CHECK_ERR(err);
+
+ if (read_val == 0x0C)
+ break;
+
+ udelay(100);
+ }
+
+ if (read_val != 0x0C) {
+ cam_err("boot fail, read_val %#x\n", read_val);
+ return -1;
+ }
+
+ /*P,M,S and Boot Mode*/
+ err = s5c73m3_write(sd, 0x3010, 0x0014, 0x2146);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x3010, 0x0010, 0x210C);
+ CHECK_ERR(err);
+
+ udelay(200);
+
+ /*Check SPI ready*/
+ for (i = 0; i < 3; i++) {
+ err = s5c73m3_read(sd, 0x3010, 0x0010, &read_val);
+ CHECK_ERR(err);
+
+ if (read_val == 0x210D)
+ break;
+
+ udelay(100);
+ }
+
+ if (read_val != 0x210D) {
+ cam_err("SPI not ready, read_val %#x\n", read_val);
+ return -1;
+ }
+
+ /*download fw by SPI*/
+ s5c73m3_load_fw(sd);
+
+ /*ARM reset*/
+ err = s5c73m3_write(sd, 0x3000, 0x0004, 0xFFFD);
+ CHECK_ERR(err);
+
+ /*remap*/
+ err = s5c73m3_write(sd, 0x3010, 0x00A4, 0x0183);
+ CHECK_ERR(err);
+
+ /*ARM go again*/
+ err = s5c73m3_write(sd, 0x3000, 0x0004, 0xFFFF);
+ CHECK_ERR(err);
+
+ return 0;
+}
+
+static int s5c73m3_read_vdd_core(struct v4l2_subdev *sd)
+{
+ struct s5c73m3_state *state = to_state(sd);
+ u8 *buf = NULL;
+ u16 read_val;
+ u32 vdd_core_val = 0;
+ int err;
+ struct file *fp;
+ mm_segment_t old_fs;
+
+ cam_trace("E\n");
+
+ /*Initialize OTP Controller*/
+ err = s5c73m3_write(sd, 0x3800, 0xA004, 0x0000);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x3800, 0xA000, 0x0004);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x3800, 0xA0D8, 0x0000);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x3800, 0xA0DC, 0x0004);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x3800, 0xA0C4, 0x4000);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x3800, 0xA0D4, 0x0015);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x3800, 0xA000, 0x0001);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x3800, 0xA0B4, 0x9F90);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x3800, 0xA09C, 0x9A95);
+ CHECK_ERR(err);
+
+ /*Page Select*/
+ err = s5c73m3_write(sd, 0x3800, 0xA0C4, 0x4800);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x3800, 0xA0C4, 0x4400);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x3800, 0xA0C4, 0x4200);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x3800, 0xA004, 0x00C0);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x3800, 0xA000, 0x0001);
+ CHECK_ERR(err);
+
+#if 0 /*read_val should be 0x7383*/
+ err = s5c73m3_read(sd, 0x0000, 0x131C, &read_val);
+ CHECK_ERR(err);
+
+ cam_dbg("read_val %#x\n", read_val);
+#endif
+
+ /*Read Data*/
+ err = s5c73m3_read(sd, 0x3800, 0xA034, &read_val);
+ CHECK_ERR(err);
+
+ cam_dbg("read_val %#x\n", read_val);
+
+ err = s5c73m3_read(sd, 0x3800, 0xA040, &isp_chip_info1);
+ CHECK_ERR(err);
+ err = s5c73m3_read(sd, 0x3800, 0xA044, &isp_chip_info2);
+ CHECK_ERR(err);
+ err = s5c73m3_read(sd, 0x3800, 0xA048, &isp_chip_info3);
+ CHECK_ERR(err);
+
+ /*Read Data End*/
+ err = s5c73m3_write(sd, 0x3800, 0xA000, 0x0000);
+ CHECK_ERR(err);
+
+ if (read_val & 0x200) {
+ state->pdata->set_vdd_core(1150000);
+ strcpy(sysfs_isp_core, "1.15V");
+ vdd_core_val = 1150000;
+ } else if (read_val & 0x800) {
+ state->pdata->set_vdd_core(1100000);
+ strcpy(sysfs_isp_core, "1.10V");
+ vdd_core_val = 1100000;
+ } else if (read_val & 0x2000) {
+ state->pdata->set_vdd_core(1100000);
+ strcpy(sysfs_isp_core, "1.05V");
+ vdd_core_val = 1100000;
+ } else if (read_val & 0x8000) {
+ state->pdata->set_vdd_core(1000000);
+ strcpy(sysfs_isp_core, "1.00V");
+ vdd_core_val = 1000000;
+ } else {
+ state->pdata->set_vdd_core(1150000);
+ strcpy(sysfs_isp_core, "1.15V");
+ vdd_core_val = 1150000;
+ }
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ fp = filp_open(S5C73M3_CORE_VDD,
+ O_WRONLY|O_CREAT, 0666);
+ if (IS_ERR(fp))
+ goto out;
+
+ buf = vmalloc(10);
+ if (!buf) {
+ cam_err("failed to allocate memory\n");
+ err = -ENOMEM;
+ goto out;
+ }
+
+ sprintf(buf, "%d\n", vdd_core_val);
+
+ err = vfs_write(fp, (char __user *)buf, 10, &fp->f_pos);
+ /*cam_dbg("return value of vfs_write = %d\n", err);*/
+out:
+ if (buf != NULL)
+ vfree(buf);
+
+ if (fp != NULL)
+ filp_close(fp, current->files);
+
+ set_fs(old_fs);
+ cam_trace("X\n");
+
+ return 0;
+}
+
+static int s5c73m3_set_timing_register_for_vdd(struct v4l2_subdev *sd)
+{
+ int err = 0;
+
+ err = s5c73m3_write(sd, 0x3010, 0x0018, 0x0618);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x3010, 0x001C, 0x10C1);
+ CHECK_ERR(err);
+ err = s5c73m3_write(sd, 0x3010, 0x0020, 0x249E);
+ CHECK_ERR(err);
+
+ return err;
+}
+
+static int s5c73m3_init(struct v4l2_subdev *sd, u32 val)
+{
+ struct s5c73m3_state *state = to_state(sd);
+ int err = 0;
+ int retVal = 0;
+ sd_internal = sd;
+
+ /* Default state values */
+ state->isp.bad_fw = 1;
+
+ state->preview = NULL;
+ state->capture = NULL;
+ state->fw_index = S5C73M3_PATH_MAX;
+
+ state->format_mode = V4L2_PIX_FMT_MODE_PREVIEW;
+ state->sensor_mode = SENSOR_CAMERA;
+ state->flash_mode = FLASH_MODE_OFF;
+ state->wb_mode = WHITE_BALANCE_AUTO;
+ state->focus.mode = FOCUS_MODE_CONTINOUS_PICTURE;
+ state->focus.touch = 0;
+
+ state->fps = 0; /* auto */
+
+ memset(&state->focus, 0, sizeof(state->focus));
+
+ if (!state->pdata->is_vdd_core_set())
+ s5c73m3_read_vdd_core(sd);
+
+ cam_dbg("vdd core value from OTP : %s", sysfs_isp_core);
+ cam_dbg("chip info from OTP : %#x, %#x, %#x\n",
+ isp_chip_info1, isp_chip_info2, isp_chip_info3);
+
+#ifdef S5C73M3_FROM_BOOTING
+ err = s5c73m3_FROM_booting(sd);
+#else
+ err = s5c73m3_set_timing_register_for_vdd(sd);
+ CHECK_ERR(err);
+
+ err = s5c73m3_check_fw(sd, 0);
+ if (err < 0) {
+ cam_dbg("isp.bad_fw is true\n");
+ state->isp.bad_fw = 1;
+ }
+#endif
+ CHECK_ERR(err);
+
+ err = s5c73m3_i2c_check_status(sd);
+ if (err < 0) {
+ cam_err("ISP is not ready. retry loading fw!!\n");
+ /* retry */
+ retVal = s5c73m3_check_fw_date(sd);
+
+ err = state->pdata->is_isp_reset();
+ CHECK_ERR(err);
+ err = s5c73m3_set_timing_register_for_vdd(sd);
+ CHECK_ERR(err);
+
+ /* retVal = 0 : Same Version
+ retVal < 0 : Phone Version is latest Version than sensorFW.
+ retVal > 0 : Sensor Version is latest version than phoenFW. */
+ if (retVal <= 0) {
+ cam_dbg("Loading From PhoneFW......\n");
+ err = s5c73m3_SPI_booting(sd);
+ CHECK_ERR(err);
+ } else {
+ cam_dbg("Loading From SensorFW......\n");
+ err = s5c73m3_get_sensor_fw_binary(sd);
+ CHECK_ERR(err);
+ }
+
+ }
+
+ state->isp.bad_fw = 0;
+
+ s5c73m3_init_param(sd);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_core_ops s5c73m3_core_ops = {
+ .init = s5c73m3_init, /* initializing API */
+ .load_fw = s5c73m3_load_fw,
+ .queryctrl = s5c73m3_queryctrl,
+ .g_ctrl = s5c73m3_g_ctrl,
+ .s_ctrl = s5c73m3_s_ctrl,
+ .g_ext_ctrls = s5c73m3_g_ext_ctrls,
+};
+
+static const struct v4l2_subdev_video_ops s5c73m3_video_ops = {
+ .s_mbus_fmt = s5c73m3_s_fmt,
+ .g_parm = s5c73m3_g_parm,
+ .s_parm = s5c73m3_s_parm,
+ .enum_framesizes = s5c73m3_enum_framesizes,
+ .s_stream = s5c73m3_s_stream,
+};
+
+static const struct v4l2_subdev_ops s5c73m3_ops = {
+ .core = &s5c73m3_core_ops,
+ .video = &s5c73m3_video_ops,
+};
+
+static ssize_t s5c73m3_camera_rear_camtype_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ char type[25];
+
+ strcpy(type, sysfs_sensor_type);
+ return sprintf(buf, "%s\n", type);
+}
+
+static ssize_t s5c73m3_camera_rear_camfw_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s %s\n", sysfs_sensor_fw, sysfs_phone_fw);
+}
+
+static ssize_t s5c73m3_camera_rear_flash(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ int err;
+
+ if (buf[0] == '0')
+ err = s5c73m3_writeb(sd_internal, S5C73M3_FLASH_TORCH,
+ S5C73M3_FLASH_TORCH_OFF);
+ else
+ err = s5c73m3_writeb(sd_internal, S5C73M3_FLASH_TORCH,
+ S5C73M3_FLASH_TORCH_ON);
+
+ CHECK_ERR(err);
+
+ return count;
+}
+
+static ssize_t s5c73m3_camera_isp_core_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ char core[10];
+
+ strcpy(core, sysfs_isp_core);
+ return sprintf(buf, "%s\n", core);
+}
+
+static DEVICE_ATTR(rear_camtype, S_IRUGO,
+ s5c73m3_camera_rear_camtype_show, NULL);
+static DEVICE_ATTR(rear_camfw, S_IRUGO, s5c73m3_camera_rear_camfw_show, NULL);
+static DEVICE_ATTR(rear_flash, S_IWUSR|S_IWGRP|S_IROTH,
+ NULL, s5c73m3_camera_rear_flash);
+static DEVICE_ATTR(isp_core, S_IRUGO, s5c73m3_camera_isp_core_show, NULL);
+
+/*
+ * s5c73m3_probe
+ * Fetching platform data is being done with s_config subdev call.
+ * In probe routine, we just register subdev device
+ */
+static int __devinit s5c73m3_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct s5c73m3_state *state;
+ struct v4l2_subdev *sd;
+
+ state = kzalloc(sizeof(struct s5c73m3_state), GFP_KERNEL);
+ if (state == NULL)
+ return -ENOMEM;
+
+ sd = &state->sd;
+ strcpy(sd->name, S5C73M3_DRIVER_NAME);
+
+ state->pdata = client->dev.platform_data;
+
+ /* Registering subdev */
+ v4l2_i2c_subdev_init(sd, client, &s5c73m3_ops);
+
+#ifdef CAM_DEBUG
+ state->dbg_level = CAM_DEBUG;
+#endif
+
+#ifdef S5C73M3_BUSFREQ_OPP
+ /* lock bus frequency */
+ dev_lock(bus_dev, s5c73m3_dev, 400200);
+#endif
+
+ if (s5c73m3_dev)
+ dev_set_drvdata(s5c73m3_dev, state);
+
+ printk(KERN_DEBUG "%s\n", __func__);
+
+ return 0;
+}
+
+static int __devexit s5c73m3_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct s5c73m3_state *state = to_state(sd);
+
+ if (unlikely(state->isp.bad_fw)) {
+ cam_err("camera is not ready!!\n");
+ } else {
+ if (s5c73m3_set_af_softlanding(sd) < 0)
+ cam_err("failed to set soft landing\n");
+ }
+ v4l2_device_unregister_subdev(sd);
+
+#ifdef S5C73M3_BUSFREQ_OPP
+ /* Unlock bus frequency */
+ dev_unlock(bus_dev, s5c73m3_dev);
+#endif
+
+ kfree(state);
+
+ return 0;
+}
+
+static const struct i2c_device_id s5c73m3_id[] = {
+ { S5C73M3_DRIVER_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, s5c73m3_id);
+
+static struct i2c_driver s5c73m3_i2c_driver = {
+ .driver = {
+ .name = S5C73M3_DRIVER_NAME,
+ },
+ .probe = s5c73m3_probe,
+ .remove = __devexit_p(s5c73m3_remove),
+ .id_table = s5c73m3_id,
+};
+
+static int __init s5c73m3_mod_init(void)
+{
+#ifdef S5C73M3_BUSFREQ_OPP
+ /* To lock bus frequency in OPP mode */
+ bus_dev = dev_get("exynos-busfreq");
+#endif
+
+ if (!s5c73m3_dev) {
+ s5c73m3_dev = device_create(camera_class,
+ NULL, 0, NULL, "rear");
+ if (IS_ERR(s5c73m3_dev)) {
+ cam_warn("failed to create device!\n");
+ return 0;
+ }
+
+ if (device_create_file(s5c73m3_dev, &dev_attr_rear_camtype)
+ < 0) {
+ cam_warn("failed to create device file, %s\n",
+ dev_attr_rear_camtype.attr.name);
+ }
+
+ if (device_create_file(s5c73m3_dev, &dev_attr_rear_camfw) < 0) {
+ cam_warn("failed to create device file, %s\n",
+ dev_attr_rear_camfw.attr.name);
+ }
+
+ if (device_create_file(s5c73m3_dev, &dev_attr_rear_flash) < 0) {
+ cam_warn("failed to create device file, %s\n",
+ dev_attr_rear_flash.attr.name);
+ }
+
+ if (device_create_file(s5c73m3_dev, &dev_attr_isp_core) < 0) {
+ cam_warn("failed to create device file, %s\n",
+ dev_attr_isp_core.attr.name);
+ }
+ }
+
+ return i2c_add_driver(&s5c73m3_i2c_driver);
+}
+
+static void __exit s5c73m3_mod_exit(void)
+{
+ i2c_del_driver(&s5c73m3_i2c_driver);
+}
+module_init(s5c73m3_mod_init);
+module_exit(s5c73m3_mod_exit);
+
+
+MODULE_DESCRIPTION("driver for LSI S5C73M3");
+MODULE_LICENSE("GPL");