554 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Samsung LSI S5C73M3 8M pixel camera driver4 *5 * Copyright (C) 2012, Samsung Electronics, Co., Ltd.6 * Sylwester Nawrocki <s.nawrocki@samsung.com>7 * Andrzej Hajda <a.hajda@samsung.com>8 */9 10#include <linux/sizes.h>11#include <linux/delay.h>12#include <linux/firmware.h>13#include <linux/i2c.h>14#include <linux/init.h>15#include <linux/media.h>16#include <linux/module.h>17#include <linux/regulator/consumer.h>18#include <linux/slab.h>19#include <linux/spi/spi.h>20#include <linux/videodev2.h>21#include <media/media-entity.h>22#include <media/v4l2-ctrls.h>23#include <media/v4l2-device.h>24#include <media/v4l2-subdev.h>25#include <media/v4l2-mediabus.h>26 27#include "s5c73m3.h"28 29static int s5c73m3_get_af_status(struct s5c73m3 *state, struct v4l2_ctrl *ctrl)30{31 u16 reg = REG_AF_STATUS_UNFOCUSED;32 33 int ret = s5c73m3_read(state, REG_AF_STATUS, ®);34 35 switch (reg) {36 case REG_CAF_STATUS_FIND_SEARCH_DIR:37 case REG_AF_STATUS_FOCUSING:38 case REG_CAF_STATUS_FOCUSING:39 ctrl->val = V4L2_AUTO_FOCUS_STATUS_BUSY;40 break;41 case REG_CAF_STATUS_FOCUSED:42 case REG_AF_STATUS_FOCUSED:43 ctrl->val = V4L2_AUTO_FOCUS_STATUS_REACHED;44 break;45 default:46 v4l2_info(&state->sensor_sd, "Unknown AF status %#x\n", reg);47 fallthrough;48 case REG_CAF_STATUS_UNFOCUSED:49 case REG_AF_STATUS_UNFOCUSED:50 case REG_AF_STATUS_INVALID:51 ctrl->val = V4L2_AUTO_FOCUS_STATUS_FAILED;52 break;53 }54 55 return ret;56}57 58static int s5c73m3_g_volatile_ctrl(struct v4l2_ctrl *ctrl)59{60 struct v4l2_subdev *sd = ctrl_to_sensor_sd(ctrl);61 struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd);62 int ret;63 64 if (state->power == 0)65 return -EBUSY;66 67 switch (ctrl->id) {68 case V4L2_CID_FOCUS_AUTO:69 ret = s5c73m3_get_af_status(state, state->ctrls.af_status);70 if (ret)71 return ret;72 break;73 }74 75 return 0;76}77 78static int s5c73m3_set_colorfx(struct s5c73m3 *state, int val)79{80 static const unsigned short colorfx[][2] = {81 { V4L2_COLORFX_NONE, COMM_IMAGE_EFFECT_NONE },82 { V4L2_COLORFX_BW, COMM_IMAGE_EFFECT_MONO },83 { V4L2_COLORFX_SEPIA, COMM_IMAGE_EFFECT_SEPIA },84 { V4L2_COLORFX_NEGATIVE, COMM_IMAGE_EFFECT_NEGATIVE },85 { V4L2_COLORFX_AQUA, COMM_IMAGE_EFFECT_AQUA },86 };87 int i;88 89 for (i = 0; i < ARRAY_SIZE(colorfx); i++) {90 if (colorfx[i][0] != val)91 continue;92 93 v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd,94 "Setting %s color effect\n",95 v4l2_ctrl_get_menu(state->ctrls.colorfx->id)[i]);96 97 return s5c73m3_isp_command(state, COMM_IMAGE_EFFECT,98 colorfx[i][1]);99 }100 return -EINVAL;101}102 103/* Set exposure metering/exposure bias */104static int s5c73m3_set_exposure(struct s5c73m3 *state, int auto_exp)105{106 struct v4l2_subdev *sd = &state->sensor_sd;107 struct s5c73m3_ctrls *ctrls = &state->ctrls;108 int ret = 0;109 110 if (ctrls->exposure_metering->is_new) {111 u16 metering;112 113 switch (ctrls->exposure_metering->val) {114 case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED:115 metering = COMM_METERING_CENTER;116 break;117 case V4L2_EXPOSURE_METERING_SPOT:118 metering = COMM_METERING_SPOT;119 break;120 default:121 metering = COMM_METERING_AVERAGE;122 break;123 }124 125 ret = s5c73m3_isp_command(state, COMM_METERING, metering);126 }127 128 if (!ret && ctrls->exposure_bias->is_new) {129 u16 exp_bias = ctrls->exposure_bias->val;130 ret = s5c73m3_isp_command(state, COMM_EV, exp_bias);131 }132 133 v4l2_dbg(1, s5c73m3_dbg, sd,134 "%s: exposure bias: %#x, metering: %#x (%d)\n", __func__,135 ctrls->exposure_bias->val, ctrls->exposure_metering->val, ret);136 137 return ret;138}139 140static int s5c73m3_set_white_balance(struct s5c73m3 *state, int val)141{142 static const unsigned short wb[][2] = {143 { V4L2_WHITE_BALANCE_INCANDESCENT, COMM_AWB_MODE_INCANDESCENT},144 { V4L2_WHITE_BALANCE_FLUORESCENT, COMM_AWB_MODE_FLUORESCENT1},145 { V4L2_WHITE_BALANCE_FLUORESCENT_H, COMM_AWB_MODE_FLUORESCENT2},146 { V4L2_WHITE_BALANCE_CLOUDY, COMM_AWB_MODE_CLOUDY},147 { V4L2_WHITE_BALANCE_DAYLIGHT, COMM_AWB_MODE_DAYLIGHT},148 { V4L2_WHITE_BALANCE_AUTO, COMM_AWB_MODE_AUTO},149 };150 int i;151 152 for (i = 0; i < ARRAY_SIZE(wb); i++) {153 if (wb[i][0] != val)154 continue;155 156 v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd,157 "Setting white balance to: %s\n",158 v4l2_ctrl_get_menu(state->ctrls.auto_wb->id)[i]);159 160 return s5c73m3_isp_command(state, COMM_AWB_MODE, wb[i][1]);161 }162 163 return -EINVAL;164}165 166static int s5c73m3_af_run(struct s5c73m3 *state, bool on)167{168 struct s5c73m3_ctrls *c = &state->ctrls;169 170 if (!on)171 return s5c73m3_isp_command(state, COMM_AF_CON,172 COMM_AF_CON_STOP);173 174 if (c->focus_auto->val)175 return s5c73m3_isp_command(state, COMM_AF_MODE,176 COMM_AF_MODE_PREVIEW_CAF_START);177 178 return s5c73m3_isp_command(state, COMM_AF_CON, COMM_AF_CON_START);179}180 181static int s5c73m3_3a_lock(struct s5c73m3 *state, struct v4l2_ctrl *ctrl)182{183 bool awb_lock = ctrl->val & V4L2_LOCK_WHITE_BALANCE;184 bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE;185 bool af_lock = ctrl->val & V4L2_LOCK_FOCUS;186 int ret = 0;187 188 if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_EXPOSURE) {189 ret = s5c73m3_isp_command(state, COMM_AE_CON,190 ae_lock ? COMM_AE_STOP : COMM_AE_START);191 if (ret)192 return ret;193 }194 195 if (((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_WHITE_BALANCE)196 && state->ctrls.auto_wb->val) {197 ret = s5c73m3_isp_command(state, COMM_AWB_CON,198 awb_lock ? COMM_AWB_STOP : COMM_AWB_START);199 if (ret)200 return ret;201 }202 203 if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_FOCUS)204 ret = s5c73m3_af_run(state, !af_lock);205 206 return ret;207}208 209static int s5c73m3_set_auto_focus(struct s5c73m3 *state, int caf)210{211 struct s5c73m3_ctrls *c = &state->ctrls;212 int ret = 1;213 214 if (c->af_distance->is_new) {215 u16 mode = (c->af_distance->val == V4L2_AUTO_FOCUS_RANGE_MACRO)216 ? COMM_AF_MODE_MACRO : COMM_AF_MODE_NORMAL;217 ret = s5c73m3_isp_command(state, COMM_AF_MODE, mode);218 if (ret != 0)219 return ret;220 }221 222 if (!ret || (c->focus_auto->is_new && c->focus_auto->val) ||223 c->af_start->is_new)224 ret = s5c73m3_af_run(state, 1);225 else if ((c->focus_auto->is_new && !c->focus_auto->val) ||226 c->af_stop->is_new)227 ret = s5c73m3_af_run(state, 0);228 else229 ret = 0;230 231 return ret;232}233 234static int s5c73m3_set_contrast(struct s5c73m3 *state, int val)235{236 u16 reg = (val < 0) ? -val + 2 : val;237 return s5c73m3_isp_command(state, COMM_CONTRAST, reg);238}239 240static int s5c73m3_set_saturation(struct s5c73m3 *state, int val)241{242 u16 reg = (val < 0) ? -val + 2 : val;243 return s5c73m3_isp_command(state, COMM_SATURATION, reg);244}245 246static int s5c73m3_set_sharpness(struct s5c73m3 *state, int val)247{248 u16 reg = (val < 0) ? -val + 2 : val;249 return s5c73m3_isp_command(state, COMM_SHARPNESS, reg);250}251 252static int s5c73m3_set_iso(struct s5c73m3 *state, int val)253{254 u32 iso;255 256 if (val == V4L2_ISO_SENSITIVITY_MANUAL)257 iso = state->ctrls.iso->val + 1;258 else259 iso = 0;260 261 return s5c73m3_isp_command(state, COMM_ISO, iso);262}263 264static int s5c73m3_set_stabilization(struct s5c73m3 *state, int val)265{266 struct v4l2_subdev *sd = &state->sensor_sd;267 268 v4l2_dbg(1, s5c73m3_dbg, sd, "Image stabilization: %d\n", val);269 270 return s5c73m3_isp_command(state, COMM_FRAME_RATE, val ?271 COMM_FRAME_RATE_ANTI_SHAKE : COMM_FRAME_RATE_AUTO_SET);272}273 274static int s5c73m3_set_jpeg_quality(struct s5c73m3 *state, int quality)275{276 int reg;277 278 if (quality <= 65)279 reg = COMM_IMAGE_QUALITY_NORMAL;280 else if (quality <= 75)281 reg = COMM_IMAGE_QUALITY_FINE;282 else283 reg = COMM_IMAGE_QUALITY_SUPERFINE;284 285 return s5c73m3_isp_command(state, COMM_IMAGE_QUALITY, reg);286}287 288static int s5c73m3_set_scene_program(struct s5c73m3 *state, int val)289{290 static const unsigned short scene_lookup[] = {291 COMM_SCENE_MODE_NONE, /* V4L2_SCENE_MODE_NONE */292 COMM_SCENE_MODE_AGAINST_LIGHT,/* V4L2_SCENE_MODE_BACKLIGHT */293 COMM_SCENE_MODE_BEACH, /* V4L2_SCENE_MODE_BEACH_SNOW */294 COMM_SCENE_MODE_CANDLE, /* V4L2_SCENE_MODE_CANDLE_LIGHT */295 COMM_SCENE_MODE_DAWN, /* V4L2_SCENE_MODE_DAWN_DUSK */296 COMM_SCENE_MODE_FALL, /* V4L2_SCENE_MODE_FALL_COLORS */297 COMM_SCENE_MODE_FIRE, /* V4L2_SCENE_MODE_FIREWORKS */298 COMM_SCENE_MODE_LANDSCAPE, /* V4L2_SCENE_MODE_LANDSCAPE */299 COMM_SCENE_MODE_NIGHT, /* V4L2_SCENE_MODE_NIGHT */300 COMM_SCENE_MODE_INDOOR, /* V4L2_SCENE_MODE_PARTY_INDOOR */301 COMM_SCENE_MODE_PORTRAIT, /* V4L2_SCENE_MODE_PORTRAIT */302 COMM_SCENE_MODE_SPORTS, /* V4L2_SCENE_MODE_SPORTS */303 COMM_SCENE_MODE_SUNSET, /* V4L2_SCENE_MODE_SUNSET */304 COMM_SCENE_MODE_TEXT, /* V4L2_SCENE_MODE_TEXT */305 };306 307 v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd, "Setting %s scene mode\n",308 v4l2_ctrl_get_menu(state->ctrls.scene_mode->id)[val]);309 310 return s5c73m3_isp_command(state, COMM_SCENE_MODE, scene_lookup[val]);311}312 313static int s5c73m3_set_power_line_freq(struct s5c73m3 *state, int val)314{315 unsigned int pwr_line_freq = COMM_FLICKER_NONE;316 317 switch (val) {318 case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED:319 pwr_line_freq = COMM_FLICKER_NONE;320 break;321 case V4L2_CID_POWER_LINE_FREQUENCY_50HZ:322 pwr_line_freq = COMM_FLICKER_AUTO_50HZ;323 break;324 case V4L2_CID_POWER_LINE_FREQUENCY_60HZ:325 pwr_line_freq = COMM_FLICKER_AUTO_60HZ;326 break;327 default:328 case V4L2_CID_POWER_LINE_FREQUENCY_AUTO:329 pwr_line_freq = COMM_FLICKER_NONE;330 }331 332 return s5c73m3_isp_command(state, COMM_FLICKER_MODE, pwr_line_freq);333}334 335static int s5c73m3_s_ctrl(struct v4l2_ctrl *ctrl)336{337 struct v4l2_subdev *sd = ctrl_to_sensor_sd(ctrl);338 struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd);339 int ret = 0;340 341 v4l2_dbg(1, s5c73m3_dbg, sd, "set_ctrl: %s, value: %d\n",342 ctrl->name, ctrl->val);343 344 mutex_lock(&state->lock);345 /*346 * If the device is not powered up by the host driver do347 * not apply any controls to H/W at this time. Instead348 * the controls will be restored right after power-up.349 */350 if (state->power == 0)351 goto unlock;352 353 if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) {354 ret = -EINVAL;355 goto unlock;356 }357 358 switch (ctrl->id) {359 case V4L2_CID_3A_LOCK:360 ret = s5c73m3_3a_lock(state, ctrl);361 break;362 363 case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:364 ret = s5c73m3_set_white_balance(state, ctrl->val);365 break;366 367 case V4L2_CID_CONTRAST:368 ret = s5c73m3_set_contrast(state, ctrl->val);369 break;370 371 case V4L2_CID_COLORFX:372 ret = s5c73m3_set_colorfx(state, ctrl->val);373 break;374 375 case V4L2_CID_EXPOSURE_AUTO:376 ret = s5c73m3_set_exposure(state, ctrl->val);377 break;378 379 case V4L2_CID_FOCUS_AUTO:380 ret = s5c73m3_set_auto_focus(state, ctrl->val);381 break;382 383 case V4L2_CID_IMAGE_STABILIZATION:384 ret = s5c73m3_set_stabilization(state, ctrl->val);385 break;386 387 case V4L2_CID_ISO_SENSITIVITY:388 ret = s5c73m3_set_iso(state, ctrl->val);389 break;390 391 case V4L2_CID_JPEG_COMPRESSION_QUALITY:392 ret = s5c73m3_set_jpeg_quality(state, ctrl->val);393 break;394 395 case V4L2_CID_POWER_LINE_FREQUENCY:396 ret = s5c73m3_set_power_line_freq(state, ctrl->val);397 break;398 399 case V4L2_CID_SATURATION:400 ret = s5c73m3_set_saturation(state, ctrl->val);401 break;402 403 case V4L2_CID_SCENE_MODE:404 ret = s5c73m3_set_scene_program(state, ctrl->val);405 break;406 407 case V4L2_CID_SHARPNESS:408 ret = s5c73m3_set_sharpness(state, ctrl->val);409 break;410 411 case V4L2_CID_WIDE_DYNAMIC_RANGE:412 ret = s5c73m3_isp_command(state, COMM_WDR, !!ctrl->val);413 break;414 415 case V4L2_CID_ZOOM_ABSOLUTE:416 ret = s5c73m3_isp_command(state, COMM_ZOOM_STEP, ctrl->val);417 break;418 }419unlock:420 mutex_unlock(&state->lock);421 return ret;422}423 424static const struct v4l2_ctrl_ops s5c73m3_ctrl_ops = {425 .g_volatile_ctrl = s5c73m3_g_volatile_ctrl,426 .s_ctrl = s5c73m3_s_ctrl,427};428 429/* Supported manual ISO values */430static const s64 iso_qmenu[] = {431 /* COMM_ISO: 0x0001...0x0004 */432 100, 200, 400, 800,433};434 435/* Supported exposure bias values (-2.0EV...+2.0EV) */436static const s64 ev_bias_qmenu[] = {437 /* COMM_EV: 0x0000...0x0008 */438 -2000, -1500, -1000, -500, 0, 500, 1000, 1500, 2000439};440 441int s5c73m3_init_controls(struct s5c73m3 *state)442{443 const struct v4l2_ctrl_ops *ops = &s5c73m3_ctrl_ops;444 struct s5c73m3_ctrls *ctrls = &state->ctrls;445 struct v4l2_ctrl_handler *hdl = &ctrls->handler;446 447 int ret = v4l2_ctrl_handler_init(hdl, 22);448 if (ret)449 return ret;450 451 /* White balance */452 ctrls->auto_wb = v4l2_ctrl_new_std_menu(hdl, ops,453 V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE,454 9, ~0x15e, V4L2_WHITE_BALANCE_AUTO);455 456 /* Exposure (only automatic exposure) */457 ctrls->auto_exposure = v4l2_ctrl_new_std_menu(hdl, ops,458 V4L2_CID_EXPOSURE_AUTO, 0, ~0x01, V4L2_EXPOSURE_AUTO);459 460 ctrls->exposure_bias = v4l2_ctrl_new_int_menu(hdl, ops,461 V4L2_CID_AUTO_EXPOSURE_BIAS,462 ARRAY_SIZE(ev_bias_qmenu) - 1,463 ARRAY_SIZE(ev_bias_qmenu)/2 - 1,464 ev_bias_qmenu);465 466 ctrls->exposure_metering = v4l2_ctrl_new_std_menu(hdl, ops,467 V4L2_CID_EXPOSURE_METERING,468 2, ~0x7, V4L2_EXPOSURE_METERING_AVERAGE);469 470 /* Auto focus */471 ctrls->focus_auto = v4l2_ctrl_new_std(hdl, ops,472 V4L2_CID_FOCUS_AUTO, 0, 1, 1, 0);473 474 ctrls->af_start = v4l2_ctrl_new_std(hdl, ops,475 V4L2_CID_AUTO_FOCUS_START, 0, 1, 1, 0);476 477 ctrls->af_stop = v4l2_ctrl_new_std(hdl, ops,478 V4L2_CID_AUTO_FOCUS_STOP, 0, 1, 1, 0);479 480 ctrls->af_status = v4l2_ctrl_new_std(hdl, ops,481 V4L2_CID_AUTO_FOCUS_STATUS, 0,482 (V4L2_AUTO_FOCUS_STATUS_BUSY |483 V4L2_AUTO_FOCUS_STATUS_REACHED |484 V4L2_AUTO_FOCUS_STATUS_FAILED),485 0, V4L2_AUTO_FOCUS_STATUS_IDLE);486 487 ctrls->af_distance = v4l2_ctrl_new_std_menu(hdl, ops,488 V4L2_CID_AUTO_FOCUS_RANGE,489 V4L2_AUTO_FOCUS_RANGE_MACRO,490 ~(1 << V4L2_AUTO_FOCUS_RANGE_NORMAL |491 1 << V4L2_AUTO_FOCUS_RANGE_MACRO),492 V4L2_AUTO_FOCUS_RANGE_NORMAL);493 /* ISO sensitivity */494 ctrls->auto_iso = v4l2_ctrl_new_std_menu(hdl, ops,495 V4L2_CID_ISO_SENSITIVITY_AUTO, 1, 0,496 V4L2_ISO_SENSITIVITY_AUTO);497 498 ctrls->iso = v4l2_ctrl_new_int_menu(hdl, ops,499 V4L2_CID_ISO_SENSITIVITY, ARRAY_SIZE(iso_qmenu) - 1,500 ARRAY_SIZE(iso_qmenu)/2 - 1, iso_qmenu);501 502 ctrls->contrast = v4l2_ctrl_new_std(hdl, ops,503 V4L2_CID_CONTRAST, -2, 2, 1, 0);504 505 ctrls->saturation = v4l2_ctrl_new_std(hdl, ops,506 V4L2_CID_SATURATION, -2, 2, 1, 0);507 508 ctrls->sharpness = v4l2_ctrl_new_std(hdl, ops,509 V4L2_CID_SHARPNESS, -2, 2, 1, 0);510 511 ctrls->zoom = v4l2_ctrl_new_std(hdl, ops,512 V4L2_CID_ZOOM_ABSOLUTE, 0, 30, 1, 0);513 514 ctrls->colorfx = v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX,515 V4L2_COLORFX_AQUA, ~0x40f, V4L2_COLORFX_NONE);516 517 ctrls->wdr = v4l2_ctrl_new_std(hdl, ops,518 V4L2_CID_WIDE_DYNAMIC_RANGE, 0, 1, 1, 0);519 520 ctrls->stabilization = v4l2_ctrl_new_std(hdl, ops,521 V4L2_CID_IMAGE_STABILIZATION, 0, 1, 1, 0);522 523 v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY,524 V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,525 V4L2_CID_POWER_LINE_FREQUENCY_AUTO);526 527 ctrls->jpeg_quality = v4l2_ctrl_new_std(hdl, ops,528 V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, 100, 1, 80);529 530 ctrls->scene_mode = v4l2_ctrl_new_std_menu(hdl, ops,531 V4L2_CID_SCENE_MODE, V4L2_SCENE_MODE_TEXT, ~0x3fff,532 V4L2_SCENE_MODE_NONE);533 534 ctrls->aaa_lock = v4l2_ctrl_new_std(hdl, ops,535 V4L2_CID_3A_LOCK, 0, 0x7, 0, 0);536 537 if (hdl->error) {538 ret = hdl->error;539 v4l2_ctrl_handler_free(hdl);540 return ret;541 }542 543 v4l2_ctrl_auto_cluster(3, &ctrls->auto_exposure, 0, false);544 ctrls->auto_iso->flags |= V4L2_CTRL_FLAG_VOLATILE |545 V4L2_CTRL_FLAG_UPDATE;546 v4l2_ctrl_auto_cluster(2, &ctrls->auto_iso, 0, false);547 ctrls->af_status->flags |= V4L2_CTRL_FLAG_VOLATILE;548 v4l2_ctrl_cluster(5, &ctrls->focus_auto);549 550 state->sensor_sd.ctrl_handler = hdl;551 552 return 0;553}554