diff options
Diffstat (limited to 'drivers/media/video/s5ka3dfx.c')
-rwxr-xr-x | drivers/media/video/s5ka3dfx.c | 1229 |
1 files changed, 1229 insertions, 0 deletions
diff --git a/drivers/media/video/s5ka3dfx.c b/drivers/media/video/s5ka3dfx.c new file mode 100755 index 0000000..e4ca5f0 --- /dev/null +++ b/drivers/media/video/s5ka3dfx.c @@ -0,0 +1,1229 @@ +/* + * Driver for S5KA3DFX (UXGA camera) from Samsung Electronics + * + * 1/4" 2.0Mp CMOS Image Sensor SoC with an Embedded Image Processor + * + * Copyright (C) 2009, Jinsung Yang <jsgood.yang@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/version.h> +#include <media/v4l2-device.h> +#include <media/v4l2-subdev.h> +#include <media/s5ka3dfx_platform.h> + +#ifdef CONFIG_VIDEO_SAMSUNG_V4L2 +#include <linux/videodev2_samsung.h> +#endif + +#include "s5ka3dfx.h" + +#define S5KA3DFX_DRIVER_NAME "S5KA3DFX" + +/* #define VGA_CAM_DEBUG */ + +#ifdef VGA_CAM_DEBUG +#define dev_dbg dev_err +#endif + +/* Default resolution & pixelformat. plz ref s5ka3dfx_platform.h */ +#define DEFAULT_RESOLUTION WVGA /* Index of resoultion */ +#define DEFAUT_FPS_INDEX S5KA3DFX_15FPS +#define DEFUALT_MCLK 24000000 /* 24MHz default */ +#define DEFAULT_FMT V4L2_PIX_FMT_UYVY /* YUV422 */ +#define POLL_TIME_MS 10 + +/* + * Specification + * Parallel : ITU-R. 656/601 YUV422, RGB565, RGB888 (Up to VGA), RAW10 + * Serial : MIPI CSI2 (single lane) YUV422, RGB565, RGB888 (Up to VGA), RAW10 + * Resolution : 1280 (H) x 1024 (V) + * Image control : Brightness, Contrast, Saturation, Sharpness, Glamour + * Effect : Mono, Negative, Sepia, Aqua, Sketch + * FPS : 15fps @full resolution, 30fps @VGA, 24fps @720p + * Max. pixel clock frequency : 48MHz(upto) + * Internal PLL (6MHz to 27MHz input frequency) + */ + +static int s5ka3dfx_init(struct v4l2_subdev *sd, u32 val); + +/* Camera functional setting values configured by user concept */ +struct s5ka3dfx_userset { + int exposure_bias; /* V4L2_CID_EXPOSURE */ + unsigned int ae_lock; + unsigned int awb_lock; + unsigned int auto_wb; /* V4L2_CID_AUTO_WHITE_BALANCE */ + unsigned int manual_wb; /* V4L2_CID_WHITE_BALANCE_PRESET */ + unsigned int wb_temp; /* V4L2_CID_WHITE_BALANCE_TEMPERATURE */ + unsigned int effect; /* Color FX (AKA Color tone) */ + unsigned int contrast; /* V4L2_CID_CONTRAST */ + unsigned int saturation; /* V4L2_CID_SATURATION */ + unsigned int sharpness; /* V4L2_CID_SHARPNESS */ + unsigned int glamour; +}; + +struct s5ka3dfx_state { + struct s5ka3dfx_platform_data *pdata; + struct v4l2_subdev sd; + struct v4l2_pix_format pix; + struct v4l2_fract timeperframe; + struct s5ka3dfx_userset userset; + struct v4l2_streamparm strm; + int framesize_index; + int freq; /* MCLK in KHz */ + int is_mipi; + int isize; + int ver; + int fps; + int vt_mode; /*For VT camera */ + int check_dataline; + int check_previewdata; +}; + +enum { + S5KA3DFX_PREVIEW_QCIF, + S5KA3DFX_PREVIEW_QVGA, + S5KA3DFX_PREVIEW_VGA, +}; + +struct s5ka3dfx_enum_framesize { + unsigned int index; + unsigned int width; + unsigned int height; +}; + +struct s5ka3dfx_enum_framesize s5ka3dfx_framesize_list[] = { + { S5KA3DFX_PREVIEW_QCIF, 176, 144 }, + { S5KA3DFX_PREVIEW_QVGA, 320, 240 }, + { S5KA3DFX_PREVIEW_VGA, 640, 480 } +}; + +static inline struct s5ka3dfx_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct s5ka3dfx_state, sd); +} + +struct s5ka3dfx_regset_table { + struct s5ka3dfx_reg *regset; + int array_size; +}; + +#define S5KA3DFX_REGSET_TABLE_ELEMENT(x, y) \ + [(x)] = { \ + .regset = (y), \ + .array_size = ARRAY_SIZE((y)), \ + } + +static struct s5ka3dfx_regset_table brightness_vt[] = { + S5KA3DFX_REGSET_TABLE_ELEMENT(EV_MINUS_4, s5ka3dfx_ev_vt_m4), + S5KA3DFX_REGSET_TABLE_ELEMENT(EV_MINUS_3, s5ka3dfx_ev_vt_m3), + S5KA3DFX_REGSET_TABLE_ELEMENT(EV_MINUS_2, s5ka3dfx_ev_vt_m2), + S5KA3DFX_REGSET_TABLE_ELEMENT(EV_MINUS_1, s5ka3dfx_ev_vt_m1), + S5KA3DFX_REGSET_TABLE_ELEMENT(EV_DEFAULT, s5ka3dfx_ev_vt_default), + S5KA3DFX_REGSET_TABLE_ELEMENT(EV_PLUS_1, s5ka3dfx_ev_vt_p1), + S5KA3DFX_REGSET_TABLE_ELEMENT(EV_PLUS_2, s5ka3dfx_ev_vt_p2), + S5KA3DFX_REGSET_TABLE_ELEMENT(EV_PLUS_3, s5ka3dfx_ev_vt_p3), + S5KA3DFX_REGSET_TABLE_ELEMENT(EV_PLUS_4, s5ka3dfx_ev_vt_p4), +}; + +static struct s5ka3dfx_regset_table brightness[] = { + S5KA3DFX_REGSET_TABLE_ELEMENT(EV_MINUS_4, s5ka3dfx_ev_m4), + S5KA3DFX_REGSET_TABLE_ELEMENT(EV_MINUS_3, s5ka3dfx_ev_m3), + S5KA3DFX_REGSET_TABLE_ELEMENT(EV_MINUS_2, s5ka3dfx_ev_m2), + S5KA3DFX_REGSET_TABLE_ELEMENT(EV_MINUS_1, s5ka3dfx_ev_m1), + S5KA3DFX_REGSET_TABLE_ELEMENT(EV_DEFAULT, s5ka3dfx_ev_default), + S5KA3DFX_REGSET_TABLE_ELEMENT(EV_PLUS_1, s5ka3dfx_ev_p1), + S5KA3DFX_REGSET_TABLE_ELEMENT(EV_PLUS_2, s5ka3dfx_ev_p2), + S5KA3DFX_REGSET_TABLE_ELEMENT(EV_PLUS_3, s5ka3dfx_ev_p3), + S5KA3DFX_REGSET_TABLE_ELEMENT(EV_PLUS_4, s5ka3dfx_ev_p4), +}; + +static struct s5ka3dfx_regset_table white_balance[] = { + S5KA3DFX_REGSET_TABLE_ELEMENT(WHITE_BALANCE_AUTO, + s5ka3dfx_wb_auto), + S5KA3DFX_REGSET_TABLE_ELEMENT(WHITE_BALANCE_SUNNY, + s5ka3dfx_wb_sunny), + S5KA3DFX_REGSET_TABLE_ELEMENT(WHITE_BALANCE_CLOUDY, + s5ka3dfx_wb_cloudy), + S5KA3DFX_REGSET_TABLE_ELEMENT(WHITE_BALANCE_TUNGSTEN, + s5ka3dfx_wb_tungsten), + S5KA3DFX_REGSET_TABLE_ELEMENT(WHITE_BALANCE_FLUORESCENT, + s5ka3dfx_wb_fluorescent), +}; + +static struct s5ka3dfx_regset_table effects[] = { + S5KA3DFX_REGSET_TABLE_ELEMENT(IMAGE_EFFECT_NONE, + s5ka3dfx_effect_none), + S5KA3DFX_REGSET_TABLE_ELEMENT(IMAGE_EFFECT_BNW, + s5ka3dfx_effect_gray), + S5KA3DFX_REGSET_TABLE_ELEMENT(IMAGE_EFFECT_SEPIA, + s5ka3dfx_effect_sepia), + S5KA3DFX_REGSET_TABLE_ELEMENT(IMAGE_EFFECT_NEGATIVE, + s5ka3dfx_effect_negative), +}; + +static struct s5ka3dfx_regset_table fps_vt_table[] = { + S5KA3DFX_REGSET_TABLE_ELEMENT(0, s5ka3dfx_vt_fps_7), + S5KA3DFX_REGSET_TABLE_ELEMENT(1, s5ka3dfx_vt_fps_10), + S5KA3DFX_REGSET_TABLE_ELEMENT(2, s5ka3dfx_vt_fps_15), +}; + +static struct s5ka3dfx_regset_table fps_table[] = { + S5KA3DFX_REGSET_TABLE_ELEMENT(0, s5ka3dfx_fps_7), + S5KA3DFX_REGSET_TABLE_ELEMENT(1, s5ka3dfx_fps_10), + S5KA3DFX_REGSET_TABLE_ELEMENT(2, s5ka3dfx_fps_15), + S5KA3DFX_REGSET_TABLE_ELEMENT(3, s5ka3dfx_fps_auto), +}; + +static struct s5ka3dfx_regset_table blur_vt[] = { + S5KA3DFX_REGSET_TABLE_ELEMENT(BLUR_LEVEL_0, s5ka3dfx_blur_vt_none), + S5KA3DFX_REGSET_TABLE_ELEMENT(BLUR_LEVEL_1, s5ka3dfx_blur_vt_p1), + S5KA3DFX_REGSET_TABLE_ELEMENT(BLUR_LEVEL_2, s5ka3dfx_blur_vt_p2), + S5KA3DFX_REGSET_TABLE_ELEMENT(BLUR_LEVEL_3, s5ka3dfx_blur_vt_p3), +}; + +static struct s5ka3dfx_regset_table blur[] = { + S5KA3DFX_REGSET_TABLE_ELEMENT(BLUR_LEVEL_0, s5ka3dfx_blur_none), + S5KA3DFX_REGSET_TABLE_ELEMENT(BLUR_LEVEL_1, s5ka3dfx_blur_p1), + S5KA3DFX_REGSET_TABLE_ELEMENT(BLUR_LEVEL_2, s5ka3dfx_blur_p2), + S5KA3DFX_REGSET_TABLE_ELEMENT(BLUR_LEVEL_3, s5ka3dfx_blur_p3), +}; + +static struct s5ka3dfx_regset_table dataline[] = { + S5KA3DFX_REGSET_TABLE_ELEMENT(0, s5ka3dfx_dataline), +}; + +static struct s5ka3dfx_regset_table dataline_stop[] = { + S5KA3DFX_REGSET_TABLE_ELEMENT(0, s5ka3dfx_dataline_stop), +}; + +static struct s5ka3dfx_regset_table init_reg[] = { + S5KA3DFX_REGSET_TABLE_ELEMENT(0, s5ka3dfx_init_reg), +}; + +static struct s5ka3dfx_regset_table init_vt_reg[] = { + S5KA3DFX_REGSET_TABLE_ELEMENT(0, s5ka3dfx_init_vt_reg), +}; + +static struct s5ka3dfx_regset_table frame_size[] = { + S5KA3DFX_REGSET_TABLE_ELEMENT(0, s5ka3dfx_QCIF), + S5KA3DFX_REGSET_TABLE_ELEMENT(1, s5ka3dfx_QVGA), + S5KA3DFX_REGSET_TABLE_ELEMENT(2, s5ka3dfx_Return_VGA), +}; +static int s5ka3dfx_reset(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct s5ka3dfx_platform_data *pdata; + + pdata = client->dev.platform_data; + + if (pdata->cam_power) { + pdata->cam_power(0); + msleep(5); + pdata->cam_power(1); + msleep(5); + s5ka3dfx_init(sd, 0); + } + + return 0; +} + +static int s5ka3dfx_i2c_write_multi(struct i2c_client *client, + unsigned short addr, unsigned int w_data) +{ + int retry_count = 4; + unsigned char buf[2]; + struct i2c_msg msg = { client->addr, 0, 2, buf }; + int ret; + + buf[0] = addr; + buf[1] = w_data; + +#ifdef VGA_CAM_DEBUG + int i; + for (i = 0; i < 2; i++) { + dev_err(&client->dev, "buf[%d] = %x ", i, buf[i]); + if (i == 1) + dev_err(&client->dev, "\n"); + } +#endif + + do { + ret = i2c_transfer(client->adapter, &msg, 1); + if (likely(ret == 1)) + break; + msleep(POLL_TIME_MS); + } while (retry_count--); + + if (ret != 1) + dev_err(&client->dev, "I2C is not working.\n"); + + return (ret == 1) ? 0 : -EIO; +} + +static int s5ka3dfx_write_regs(struct v4l2_subdev *sd, + struct s5ka3dfx_reg regs[], int size) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int i, err; + + for (i = 0; i < size; i++) { + err = s5ka3dfx_i2c_write_multi(client, + regs[i].addr, regs[i].val); + if (err < 0) { + v4l_info(client, "%s: register set failed\n", __func__); + break; + } + } + if (err < 0) + return -EIO; + + return 0; +} + +static int s5ka3dfx_write_regset_table(struct v4l2_subdev *sd, + const struct s5ka3dfx_regset_table *regset_table) +{ + int err; + + if (regset_table->regset) + err = s5ka3dfx_write_regs(sd, regset_table->regset, + regset_table->array_size); + else + err = -EINVAL; + + return err; +} + +static int s5ka3dfx_set_from_table(struct v4l2_subdev *sd, + const char *setting_name, + const struct s5ka3dfx_regset_table *table, + int table_size, int index) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + dev_dbg(&client->dev, "%s: set %s index %d\n", + __func__, setting_name, index); + + if ((index < 0) || (index >= table_size)) { + dev_err(&client->dev, + "%s: index(%d) out of range[0:%d] for table for %s\n", + __func__, index, table_size, setting_name); + return -EINVAL; + } + table += index; + if (table == NULL) + return -EINVAL; + return s5ka3dfx_write_regset_table(sd, table); +} + +static int s5ka3dfx_set_parameter(struct v4l2_subdev *sd, + int *current_value_ptr, + int new_value, + const char *setting_name, + const struct s5ka3dfx_regset_table *table, + int table_size) +{ + int err; + + if (*current_value_ptr == new_value) + return 0; + + err = s5ka3dfx_set_from_table(sd, setting_name, table, + table_size, new_value); + + if (!err) + *current_value_ptr = new_value; + + return err; +} +static int s5ka3dfx_set_preview_start(struct v4l2_subdev *sd) +{ + int err; + struct s5ka3dfx_state *state = + container_of(sd, struct s5ka3dfx_state, sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!state->pix.width || !state->pix.height) + return -EINVAL; + + err = s5ka3dfx_set_from_table(sd, "frame_size", frame_size, + ARRAY_SIZE(frame_size), state->framesize_index); + if (err < 0) { + dev_err(&client->dev, + "%s: failed: Could not set preview size\n", + __func__); + return -EIO; + } + + return 0; +} +static struct v4l2_queryctrl s5ka3dfx_controls[] = { + /* Add here if needed */ +}; + +const char * const *s5ka3dfx_ctrl_get_menu(u32 id) +{ + pr_debug("%s is called... id : %d\n", __func__, id); + + switch (id) { + default: + return v4l2_ctrl_get_menu(id); + } +} + +static inline struct v4l2_queryctrl const *s5ka3dfx_find_qctrl(int id) +{ + int i; + + pr_debug("%s is called... id : %d\n", __func__, id); + + for (i = 0; i < ARRAY_SIZE(s5ka3dfx_controls); i++) + if (s5ka3dfx_controls[i].id == id) + return &s5ka3dfx_controls[i]; + + return NULL; +} + +static int s5ka3dfx_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + int i; + + pr_debug("%s is called...\n", __func__); + + for (i = 0; i < ARRAY_SIZE(s5ka3dfx_controls); i++) { + if (s5ka3dfx_controls[i].id == qc->id) { + memcpy(qc, &s5ka3dfx_controls[i], + sizeof(struct v4l2_queryctrl)); + return 0; + } + } + + return -EINVAL; +} + +static int s5ka3dfx_querymenu(struct v4l2_subdev *sd, struct v4l2_querymenu *qm) +{ + struct v4l2_queryctrl qctrl; + + pr_debug("%s is called...\n", __func__); + + qctrl.id = qm->id; + s5ka3dfx_queryctrl(sd, &qctrl); + + return v4l2_ctrl_query_menu(qm, &qctrl, s5ka3dfx_ctrl_get_menu(qm->id)); +} + +/* + * Clock configuration + * Configure expected MCLK from host and return EINVAL if not supported clock + * frequency is expected + * freq : in Hz + * flag : not supported for now + */ +static int s5ka3dfx_s_crystal_freq(struct v4l2_subdev *sd, u32 freq, u32 flags) +{ + int err = -EINVAL; + + pr_debug("%s is called...\n", __func__); + + return err; +} + +static int s5ka3dfx_g_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) +{ + int err = 0; + + pr_debug("%s is called...\n", __func__); + + return err; +} + +static void s5ka3dfx_set_framesize(struct v4l2_subdev *sd, + const struct s5ka3dfx_enum_framesize *frmsize, + int frmsize_count) +{ + struct s5ka3dfx_state *state = + container_of(sd, struct s5ka3dfx_state, sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + const struct s5ka3dfx_enum_framesize *last_frmsize = + &frmsize[frmsize_count - 1]; + + dev_dbg(&client->dev, "%s: Requested Res: %dx%d\n", __func__, + state->pix.width, state->pix.height); + + do { + if (frmsize->width == state->pix.width && + frmsize->height == state->pix.height) { + break; + } + + frmsize++; + } while (frmsize <= last_frmsize); + + if (frmsize > last_frmsize) + frmsize = last_frmsize; + + state->pix.width = frmsize->width; + state->pix.height = frmsize->height; + state->framesize_index = frmsize->index; + + dev_dbg(&client->dev, "%s: Preview Res Set: %dx%d, index %d\n", + __func__, state->pix.width, state->pix.height, + state->framesize_index); +} +static int s5ka3dfx_s_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct s5ka3dfx_state *state = + container_of(sd, struct s5ka3dfx_state, sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int err = 0; + + if (fmt->code == V4L2_MBUS_FMT_FIXED && + fmt->colorspace != V4L2_COLORSPACE_JPEG) { + dev_dbg(&client->dev, + "%s: mismatch in pixelformat and colorspace\n", + __func__); + return -EINVAL; + } + + state->pix.width = fmt->width; + state->pix.height = fmt->height; + if (fmt->colorspace == V4L2_COLORSPACE_JPEG) + state->pix.pixelformat = V4L2_PIX_FMT_JPEG; + else + state->pix.pixelformat = 0; /* is this used anywhere? */ + + s5ka3dfx_set_framesize(sd, s5ka3dfx_framesize_list, + ARRAY_SIZE(s5ka3dfx_framesize_list)); + + return err; +} + +static int s5ka3dfx_enum_framesizes(struct v4l2_subdev *sd, + struct v4l2_frmsizeenum *fsize) +{ + struct s5ka3dfx_state *state = + container_of(sd, struct s5ka3dfx_state, sd); + int num_entries = sizeof(s5ka3dfx_framesize_list) / + sizeof(struct s5ka3dfx_enum_framesize); + struct s5ka3dfx_enum_framesize *elem; + int index = 0; + int i = 0; + + pr_debug("%s is called...\n", __func__); + + /* 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 (WVGA) + */ + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + + index = state->framesize_index; + + for (i = 0; i < num_entries; i++) { + elem = &s5ka3dfx_framesize_list[i]; + if (elem->index == index) { + fsize->discrete.width = + s5ka3dfx_framesize_list[index].width; + fsize->discrete.height = + s5ka3dfx_framesize_list[index].height; + return 0; + } + } + + return -EINVAL; +} + +static int s5ka3dfx_enum_frameintervals(struct v4l2_subdev *sd, + struct v4l2_frmivalenum *fival) +{ + int err = 0; + + pr_debug("%s is called...\n", __func__); + + return err; +} + +static int s5ka3dfx_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index, + enum v4l2_mbus_pixelcode *code) +{ + int err = 0; + + pr_debug("%s is called...\n", __func__); + + return err; +} + +static int s5ka3dfx_try_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) +{ + int err = 0; + + pr_debug("%s is called...\n", __func__); + + return err; +} + +static int s5ka3dfx_g_parm(struct v4l2_subdev *sd, + struct v4l2_streamparm *param) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int err = 0; + + dev_dbg(&client->dev, "%s\n", __func__); + + return err; +} + +static int s5ka3dfx_s_parm(struct v4l2_subdev *sd, + struct v4l2_streamparm *param) +{ + int err = 0; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct s5ka3dfx_state *state = + container_of(sd, struct s5ka3dfx_state, sd); + struct sec_cam_parm *new_parms = + (struct sec_cam_parm *)¶m->parm.raw_data; + struct sec_cam_parm *parms = + (struct sec_cam_parm *)&state->strm.parm.raw_data; + + dev_dbg(&client->dev, "%s: start\n", __func__); + + /* we return an error if one happened but don't stop trying to + * set all parameters passed + */ + + err = s5ka3dfx_set_parameter(sd, &parms->brightness, + new_parms->brightness, "brightness", + brightness, ARRAY_SIZE(brightness)); + err |= s5ka3dfx_set_parameter(sd, &parms->effects, new_parms->effects, + "effects", effects, + ARRAY_SIZE(effects)); + err |= s5ka3dfx_set_parameter(sd, &parms->white_balance, + new_parms->white_balance, + "white_balance", white_balance, + ARRAY_SIZE(white_balance)); + + dev_dbg(&client->dev, "%s: returning %d\n", __func__, err); + + return err; +} + +/* set sensor register values for adjusting brightness */ +static int s5ka3dfx_set_brightness(struct v4l2_subdev *sd, + struct v4l2_control *ctrl) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct s5ka3dfx_state *state = + container_of(sd, struct s5ka3dfx_state, sd); + struct s5ka3dfx_regset_table *ev; + int err = -EINVAL; + int ev_index; + int array_size; + + dev_dbg(&client->dev, "%s: value : %d state->vt_mode %d\n", + __func__, ctrl->value, state->vt_mode); + + pr_debug("state->vt_mode : %d\n", state->vt_mode); + + if ((ctrl->value < 0) || (ctrl->value >= EV_MAX)) + ev_index = EV_DEFAULT; + else + ev_index = ctrl->value; + + if (state->vt_mode == 1) { + ev = brightness_vt; + array_size = ARRAY_SIZE(brightness_vt); + } else { + ev = brightness; + array_size = ARRAY_SIZE(brightness); + } + + if (ev_index >= array_size) + ev_index = EV_DEFAULT; + + ev += ev_index; + + err = s5ka3dfx_write_regset_table(sd, ev); + + if (err) + dev_dbg(&client->dev, "%s: register set failed\n", __func__); + + return err; +} + +/* + * set sensor register values for + * adjusting whitebalance, both auto and manual + */ +static int s5ka3dfx_set_wb(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct s5ka3dfx_regset_table *wb = white_balance; + int err = -EINVAL; + + dev_dbg(&client->dev, "%s: value : %d\n", __func__, ctrl->value); + + if ((ctrl->value < WHITE_BALANCE_BASE) || + (ctrl->value > WHITE_BALANCE_MAX) || + (ctrl->value >= ARRAY_SIZE(white_balance))) { + dev_dbg(&client->dev, "%s: Value(%d) out of range([%d:%d])\n", + __func__, ctrl->value, + WHITE_BALANCE_BASE, WHITE_BALANCE_MAX); + dev_dbg(&client->dev, "%s: Value out of range\n", __func__); + goto out; + } + + wb += ctrl->value; + + err = s5ka3dfx_write_regset_table(sd, wb); + + if (err) + dev_dbg(&client->dev, "%s: register set failed\n", __func__); +out: + return err; +} + +/* set sensor register values for adjusting color effect */ +static int s5ka3dfx_set_effect(struct v4l2_subdev *sd, + struct v4l2_control *ctrl) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct s5ka3dfx_regset_table *effect = effects; + int err = -EINVAL; + + dev_dbg(&client->dev, "%s: value : %d\n", __func__, ctrl->value); + + if ((ctrl->value < IMAGE_EFFECT_BASE) || + (ctrl->value > IMAGE_EFFECT_MAX) || + (ctrl->value >= ARRAY_SIZE(effects))) { + dev_dbg(&client->dev, "%s: Value(%d) out of range([%d:%d])\n", + __func__, ctrl->value, + IMAGE_EFFECT_BASE, IMAGE_EFFECT_MAX); + goto out; + } + + effect += ctrl->value; + + err = s5ka3dfx_write_regset_table(sd, effect); + + if (err) + dev_dbg(&client->dev, "%s: register set failed\n", __func__); +out: + return err; +} + +/* set sensor register values for frame rate(fps) setting */ +static int s5ka3dfx_set_frame_rate(struct v4l2_subdev *sd, + struct v4l2_control *ctrl) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct s5ka3dfx_state *state = + container_of(sd, struct s5ka3dfx_state, sd); + struct s5ka3dfx_regset_table *fps; + + int err = -EINVAL; + int fps_index; + + dev_dbg(&client->dev, "%s: value : %d\n", __func__, ctrl->value); + + pr_debug("state->vt_mode : %d\n", state->vt_mode); + + switch (ctrl->value) { + case 0: + fps_index = 3; + break; + + case 7: + fps_index = 0; + break; + + case 10: + fps_index = 1; + break; + + case 15: + fps_index = 2; + break; + + default: + dev_dbg(&client->dev, "%s: Value(%d) is not supported\n", + __func__, ctrl->value); + goto out; + } + + if (state->vt_mode == 1) + fps = fps_vt_table; + else + fps = fps_table; + + fps += fps_index; + state->fps = fps_index; + + err = s5ka3dfx_write_regset_table(sd, fps); +out: + return err; +} + +/* set sensor register values for adjusting blur effect */ +static int s5ka3dfx_set_blur(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct s5ka3dfx_state *state = + container_of(sd, struct s5ka3dfx_state, sd); + struct s5ka3dfx_regset_table *bl; + int err = -EINVAL; + int array_size; + + dev_dbg(&client->dev, "%s: value : %d\n", __func__, ctrl->value); + + pr_debug("state->vt_mode : %d\n", state->vt_mode); + + if ((ctrl->value < BLUR_LEVEL_0) || (ctrl->value > BLUR_LEVEL_MAX)) { + dev_dbg(&client->dev, "%s: Value(%d) out of range([%d:%d])\n", + __func__, ctrl->value, + BLUR_LEVEL_0, BLUR_LEVEL_MAX); + goto out; + } + + if (state->vt_mode == 1) { + bl = blur_vt; + array_size = ARRAY_SIZE(blur_vt); + } else { + bl = blur; + array_size = ARRAY_SIZE(blur); + } + + if (ctrl->value >= array_size) { + dev_dbg(&client->dev, "%s: Value(%d) out of range([%d:%d))\n", + __func__, ctrl->value, + BLUR_LEVEL_0, array_size); + goto out; + } + + bl += ctrl->value; + + err = s5ka3dfx_write_regset_table(sd, bl); +out: + return err; +} + +static int s5ka3dfx_check_dataline_stop(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct s5ka3dfx_state *state = + container_of(sd, struct s5ka3dfx_state, sd); + int err = -EINVAL; + + dev_dbg(&client->dev, "%s\n", __func__); + + err = s5ka3dfx_write_regset_table(sd, dataline_stop); + if (err < 0) { + v4l_info(client, "%s: register set failed\n", __func__); + return -EIO; + } + + state->check_dataline = 0; + err = s5ka3dfx_reset(sd); + if (err < 0) { + v4l_info(client, "%s: register set failed\n", __func__); + return -EIO; + } + return err; +} + +/* returns the real iso currently used by sensor due to lighting + * conditions, not the requested iso we sent using s_ctrl. + */ +static int s5ka3dfx_get_iso(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + int err; + struct i2c_client *client = v4l2_get_subdevdata(sd); + s32 read_value; + int gain; + + err = s5ka3dfx_i2c_write_multi(client, 0xEF, 0x02); + if (err < 0) + return err; + + read_value = i2c_smbus_read_byte_data(client, 0x1D); + if (read_value < 0) + return read_value; + + read_value &= 0x7F; + gain = (128 * 100) / (128 - read_value); + + if (gain > 280) + ctrl->value = ISO_400; + else if (gain > 230) + ctrl->value = ISO_200; + else if (gain > 190) + ctrl->value = ISO_100; + else if (gain > 100) + ctrl->value = ISO_50; + else + ctrl->value = gain; + + dev_dbg(&client->dev, "%s: get iso == %d (0x%x)\n", + __func__, ctrl->value, read_value); + + return err; +} + +static int s5ka3dfx_get_shutterspeed(struct v4l2_subdev *sd, + struct v4l2_control *ctrl) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct s5ka3dfx_state *state = to_state(sd); + s32 read_value; + int cintr; + int err; + + err = s5ka3dfx_i2c_write_multi(client, 0xEF, 0x02); + if (err < 0) + return err; + + read_value = i2c_smbus_read_byte_data(client, 0x0E); + if (read_value < 0) + return read_value; + cintr = (read_value & 0x1F) << 8; + + read_value = i2c_smbus_read_byte_data(client, 0x0F); + if (read_value < 0) + return read_value; + cintr |= read_value & 0xFF; + + /* A3D Shutter Speed (Micro Sec.) = + (2 * (cintr - 1) * 814) / MCLK * 1000 */ + ctrl->value = ((cintr - 1) * 1628) / (state->freq / 1000) * 1000; + + dev_dbg(&client->dev, + "%s: get shutterspeed == %d\n", __func__, ctrl->value); + + return err; +} + +static int s5ka3dfx_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct s5ka3dfx_state *state = + container_of(sd, struct s5ka3dfx_state, sd); + struct s5ka3dfx_userset userset = state->userset; + int err = 0; + + dev_dbg(&client->dev, "%s: id : 0x%08x\n", __func__, ctrl->id); + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + ctrl->value = userset.exposure_bias; + break; + + case V4L2_CID_AUTO_WHITE_BALANCE: + ctrl->value = userset.auto_wb; + break; + + case V4L2_CID_WHITE_BALANCE_PRESET: + ctrl->value = userset.manual_wb; + break; + + case V4L2_CID_COLORFX: + ctrl->value = userset.effect; + break; + + case V4L2_CID_CONTRAST: + ctrl->value = userset.contrast; + break; + + case V4L2_CID_SATURATION: + ctrl->value = userset.saturation; + break; + + case V4L2_CID_SHARPNESS: + ctrl->value = userset.saturation; + break; + + case V4L2_CID_CAMERA_GET_ISO: + err = s5ka3dfx_get_iso(sd, ctrl); + break; + + case V4L2_CID_CAMERA_GET_SHT_TIME: + err = s5ka3dfx_get_shutterspeed(sd, ctrl); + break; + + case V4L2_CID_ESD_INT: + ctrl->value = 0; + break; + + default: + dev_dbg(&client->dev, "%s: no such ctrl\n", __func__); + err = -EINVAL; + break; + } + + return err; +} + +static int s5ka3dfx_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct s5ka3dfx_state *state = + container_of(sd, struct s5ka3dfx_state, sd); + + int err = -EINVAL; + + pr_debug("%s : ctrl->id 0x%08x, ctrl->value %d\n", __func__, + ctrl->id, ctrl->value); + + switch (ctrl->id) { + + case V4L2_CID_CAMERA_BRIGHTNESS: + dev_dbg(&client->dev, "%s: " + "V4L2_CID_CAMERA_BRIGHTNESS\n", __func__); + err = s5ka3dfx_set_brightness(sd, ctrl); + break; + + case V4L2_CID_CAMERA_WHITE_BALANCE: + dev_dbg(&client->dev, "%s: " + "V4L2_CID_AUTO_WHITE_BALANCE\n", __func__); + err = s5ka3dfx_set_wb(sd, ctrl); + break; + + case V4L2_CID_CAMERA_EFFECT: + dev_dbg(&client->dev, "%s: " + "V4L2_CID_CAMERA_EFFECT\n", __func__); + err = s5ka3dfx_set_effect(sd, ctrl); + break; + + case V4L2_CID_CAMERA_FRAME_RATE: + dev_dbg(&client->dev, "%s: " + "V4L2_CID_CAMERA_FRAME_RATE\n", __func__); + err = s5ka3dfx_set_frame_rate(sd, ctrl); + break; + + case V4L2_CID_CAMERA_VGA_BLUR: + dev_dbg(&client->dev, "%s: " + "V4L2_CID_CAMERA_VGA_BLUR\n", __func__); + err = s5ka3dfx_set_blur(sd, ctrl); + break; + + case V4L2_CID_CAMERA_VT_MODE: + state->vt_mode = ctrl->value; + dev_dbg(&client->dev, "%s: V4L2_CID_CAMERA_VT_MODE : " + "state->vt_mode %d\n", + __func__, state->vt_mode); + err = 0; + break; + + case V4L2_CID_CAMERA_CHECK_DATALINE: + state->check_dataline = ctrl->value; + err = 0; + break; + + case V4L2_CID_CAMERA_CHECK_DATALINE_STOP: + err = s5ka3dfx_check_dataline_stop(sd); + break; + + case V4L2_CID_CAM_PREVIEW_ONOFF: + if (state->check_previewdata == 0) + err = s5ka3dfx_set_preview_start(sd); + else + err = -EIO; + break; + + case V4L2_CID_CAMERA_RESET: + dev_dbg(&client->dev, "%s: V4L2_CID_CAMERA_RESET\n", __func__); + err = s5ka3dfx_reset(sd); + break; + + default: + dev_dbg(&client->dev, "%s: no support control " + "in camera sensor, S5KA3DFX\n", __func__); + err = 0; + break; + } + + if (err < 0) + goto out; + else + return 0; + + out: + dev_dbg(&client->dev, "%s: vidioc_s_ctrl failed\n", __func__); + return err; +} + +static void s5ka3dfx_init_parameters(struct v4l2_subdev *sd) +{ + struct s5ka3dfx_state *state = + container_of(sd, struct s5ka3dfx_state, sd); + struct sec_cam_parm *parms = + (struct sec_cam_parm *)&state->strm.parm.raw_data; + + parms->effects = IMAGE_EFFECT_NONE; + parms->brightness = EV_DEFAULT; + parms->white_balance = WHITE_BALANCE_AUTO; + +} + +static int s5ka3dfx_init(struct v4l2_subdev *sd, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct s5ka3dfx_state *state = + container_of(sd, struct s5ka3dfx_state, sd); + int err = -EINVAL; + + pr_debug("camera initialization start, state->vt_mode : %d\n", + state->vt_mode); + pr_debug("state->check_dataline : %d\n", state->check_dataline); + + s5ka3dfx_init_parameters(sd); + if (state->vt_mode == 0) { + if (state->check_dataline) + err = s5ka3dfx_write_regset_table(sd, dataline); + else + err = s5ka3dfx_write_regset_table(sd, init_reg); + } else + err = s5ka3dfx_write_regset_table(sd, init_vt_reg); + + if (err < 0) { + /* This is preview fail */ + state->check_previewdata = 100; + v4l_err(client, + "%s: camera initialization failed. err(%d)\n", + __func__, state->check_previewdata); + return -EIO; + } + + /* This is preview success */ + state->check_previewdata = 0; + return 0; +} + +static const struct v4l2_subdev_core_ops s5ka3dfx_core_ops = { + .init = s5ka3dfx_init, /* initializing API */ + .queryctrl = s5ka3dfx_queryctrl, + .querymenu = s5ka3dfx_querymenu, + .g_ctrl = s5ka3dfx_g_ctrl, + .s_ctrl = s5ka3dfx_s_ctrl, +}; + +static const struct v4l2_subdev_video_ops s5ka3dfx_video_ops = { + .s_crystal_freq = s5ka3dfx_s_crystal_freq, + .g_mbus_fmt = s5ka3dfx_g_mbus_fmt, + .s_mbus_fmt = s5ka3dfx_s_mbus_fmt, + .enum_framesizes = s5ka3dfx_enum_framesizes, + .enum_frameintervals = s5ka3dfx_enum_frameintervals, + .enum_mbus_fmt = s5ka3dfx_enum_mbus_fmt, + .try_mbus_fmt = s5ka3dfx_try_mbus_fmt, + .g_parm = s5ka3dfx_g_parm, + .s_parm = s5ka3dfx_s_parm, +}; + +static const struct v4l2_subdev_ops s5ka3dfx_ops = { + .core = &s5ka3dfx_core_ops, + .video = &s5ka3dfx_video_ops, +}; + +/* + * s5ka3dfx_probe + * Fetching platform data is being done with s_config subdev call. + * In probe routine, we just register subdev device + */ +static int s5ka3dfx_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct s5ka3dfx_state *state; + struct v4l2_subdev *sd; + struct s5ka3dfx_platform_data *pdata = client->dev.platform_data; + + state = kzalloc(sizeof(struct s5ka3dfx_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + + sd = &state->sd; + strcpy(sd->name, S5KA3DFX_DRIVER_NAME); + + /* + * Assign default format and resolution + * Use configured default information in platform data + * or without them, use default information in driver + */ + if (!(pdata->default_width && pdata->default_height)) { + /* TODO: assign driver default resolution */ + } else { + state->pix.width = pdata->default_width; + state->pix.height = pdata->default_height; + } + + if (!pdata->pixelformat) + state->pix.pixelformat = DEFAULT_FMT; + else + state->pix.pixelformat = pdata->pixelformat; + + if (pdata->freq == 0) + state->freq = DEFUALT_MCLK; + else + state->freq = pdata->freq; + + if (!pdata->is_mipi) { + state->is_mipi = 0; + dev_dbg(&client->dev, "parallel mode\n"); + } else + state->is_mipi = pdata->is_mipi; + + /* Registering subdev */ + v4l2_i2c_subdev_init(sd, client, &s5ka3dfx_ops); + + dev_dbg(&client->dev, "s5ka3dfx has been probed\n"); + return 0; +} + +static int s5ka3dfx_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct s5ka3dfx_state *state = + container_of(sd, struct s5ka3dfx_state, sd); + + v4l2_device_unregister_subdev(sd); + kfree(state); + return 0; +} + +static const struct i2c_device_id s5ka3dfx_id[] = { + { S5KA3DFX_DRIVER_NAME, 0 }, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, s5ka3dfx_id); + +static struct i2c_driver v4l2_i2c_driver = { + .driver.name = S5KA3DFX_DRIVER_NAME, + .probe = s5ka3dfx_probe, + .remove = s5ka3dfx_remove, + .id_table = s5ka3dfx_id, +}; + +static int __init v4l2_i2c_drv_init(void) +{ + return i2c_add_driver(&v4l2_i2c_driver); +} + +static void __exit v4l2_i2c_drv_cleanup(void) +{ + i2c_del_driver(&v4l2_i2c_driver); +} + +module_init(v4l2_i2c_drv_init); +module_exit(v4l2_i2c_drv_cleanup); + +MODULE_DESCRIPTION("Samsung Electronics S5KA3DFX UXGA camera driver"); +MODULE_AUTHOR("Jinsung Yang <jsgood.yang@samsung.com>"); +MODULE_LICENSE("GPL"); |