18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Samsung LSI S5C73M3 8M pixel camera driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012, Samsung Electronics, Co., Ltd. 68c2ecf20Sopenharmony_ci * Sylwester Nawrocki <s.nawrocki@samsung.com> 78c2ecf20Sopenharmony_ci * Andrzej Hajda <a.hajda@samsung.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/sizes.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/firmware.h> 138c2ecf20Sopenharmony_ci#include <linux/gpio.h> 148c2ecf20Sopenharmony_ci#include <linux/i2c.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/media.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 218c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 228c2ecf20Sopenharmony_ci#include <media/media-entity.h> 238c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 248c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 258c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h> 268c2ecf20Sopenharmony_ci#include <media/v4l2-mediabus.h> 278c2ecf20Sopenharmony_ci#include <media/i2c/s5c73m3.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include "s5c73m3.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic int s5c73m3_get_af_status(struct s5c73m3 *state, struct v4l2_ctrl *ctrl) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci u16 reg = REG_AF_STATUS_UNFOCUSED; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci int ret = s5c73m3_read(state, REG_AF_STATUS, ®); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci switch (reg) { 388c2ecf20Sopenharmony_ci case REG_CAF_STATUS_FIND_SEARCH_DIR: 398c2ecf20Sopenharmony_ci case REG_AF_STATUS_FOCUSING: 408c2ecf20Sopenharmony_ci case REG_CAF_STATUS_FOCUSING: 418c2ecf20Sopenharmony_ci ctrl->val = V4L2_AUTO_FOCUS_STATUS_BUSY; 428c2ecf20Sopenharmony_ci break; 438c2ecf20Sopenharmony_ci case REG_CAF_STATUS_FOCUSED: 448c2ecf20Sopenharmony_ci case REG_AF_STATUS_FOCUSED: 458c2ecf20Sopenharmony_ci ctrl->val = V4L2_AUTO_FOCUS_STATUS_REACHED; 468c2ecf20Sopenharmony_ci break; 478c2ecf20Sopenharmony_ci default: 488c2ecf20Sopenharmony_ci v4l2_info(&state->sensor_sd, "Unknown AF status %#x\n", reg); 498c2ecf20Sopenharmony_ci fallthrough; 508c2ecf20Sopenharmony_ci case REG_CAF_STATUS_UNFOCUSED: 518c2ecf20Sopenharmony_ci case REG_AF_STATUS_UNFOCUSED: 528c2ecf20Sopenharmony_ci case REG_AF_STATUS_INVALID: 538c2ecf20Sopenharmony_ci ctrl->val = V4L2_AUTO_FOCUS_STATUS_FAILED; 548c2ecf20Sopenharmony_ci break; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci return ret; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic int s5c73m3_g_volatile_ctrl(struct v4l2_ctrl *ctrl) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = ctrl_to_sensor_sd(ctrl); 638c2ecf20Sopenharmony_ci struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd); 648c2ecf20Sopenharmony_ci int ret; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (state->power == 0) 678c2ecf20Sopenharmony_ci return -EBUSY; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci switch (ctrl->id) { 708c2ecf20Sopenharmony_ci case V4L2_CID_FOCUS_AUTO: 718c2ecf20Sopenharmony_ci ret = s5c73m3_get_af_status(state, state->ctrls.af_status); 728c2ecf20Sopenharmony_ci if (ret) 738c2ecf20Sopenharmony_ci return ret; 748c2ecf20Sopenharmony_ci break; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci return 0; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic int s5c73m3_set_colorfx(struct s5c73m3 *state, int val) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci static const unsigned short colorfx[][2] = { 838c2ecf20Sopenharmony_ci { V4L2_COLORFX_NONE, COMM_IMAGE_EFFECT_NONE }, 848c2ecf20Sopenharmony_ci { V4L2_COLORFX_BW, COMM_IMAGE_EFFECT_MONO }, 858c2ecf20Sopenharmony_ci { V4L2_COLORFX_SEPIA, COMM_IMAGE_EFFECT_SEPIA }, 868c2ecf20Sopenharmony_ci { V4L2_COLORFX_NEGATIVE, COMM_IMAGE_EFFECT_NEGATIVE }, 878c2ecf20Sopenharmony_ci { V4L2_COLORFX_AQUA, COMM_IMAGE_EFFECT_AQUA }, 888c2ecf20Sopenharmony_ci }; 898c2ecf20Sopenharmony_ci int i; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(colorfx); i++) { 928c2ecf20Sopenharmony_ci if (colorfx[i][0] != val) 938c2ecf20Sopenharmony_ci continue; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd, 968c2ecf20Sopenharmony_ci "Setting %s color effect\n", 978c2ecf20Sopenharmony_ci v4l2_ctrl_get_menu(state->ctrls.colorfx->id)[i]); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci return s5c73m3_isp_command(state, COMM_IMAGE_EFFECT, 1008c2ecf20Sopenharmony_ci colorfx[i][1]); 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci return -EINVAL; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/* Set exposure metering/exposure bias */ 1068c2ecf20Sopenharmony_cistatic int s5c73m3_set_exposure(struct s5c73m3 *state, int auto_exp) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sensor_sd; 1098c2ecf20Sopenharmony_ci struct s5c73m3_ctrls *ctrls = &state->ctrls; 1108c2ecf20Sopenharmony_ci int ret = 0; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (ctrls->exposure_metering->is_new) { 1138c2ecf20Sopenharmony_ci u16 metering; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci switch (ctrls->exposure_metering->val) { 1168c2ecf20Sopenharmony_ci case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: 1178c2ecf20Sopenharmony_ci metering = COMM_METERING_CENTER; 1188c2ecf20Sopenharmony_ci break; 1198c2ecf20Sopenharmony_ci case V4L2_EXPOSURE_METERING_SPOT: 1208c2ecf20Sopenharmony_ci metering = COMM_METERING_SPOT; 1218c2ecf20Sopenharmony_ci break; 1228c2ecf20Sopenharmony_ci default: 1238c2ecf20Sopenharmony_ci metering = COMM_METERING_AVERAGE; 1248c2ecf20Sopenharmony_ci break; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci ret = s5c73m3_isp_command(state, COMM_METERING, metering); 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (!ret && ctrls->exposure_bias->is_new) { 1318c2ecf20Sopenharmony_ci u16 exp_bias = ctrls->exposure_bias->val; 1328c2ecf20Sopenharmony_ci ret = s5c73m3_isp_command(state, COMM_EV, exp_bias); 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci v4l2_dbg(1, s5c73m3_dbg, sd, 1368c2ecf20Sopenharmony_ci "%s: exposure bias: %#x, metering: %#x (%d)\n", __func__, 1378c2ecf20Sopenharmony_ci ctrls->exposure_bias->val, ctrls->exposure_metering->val, ret); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return ret; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int s5c73m3_set_white_balance(struct s5c73m3 *state, int val) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci static const unsigned short wb[][2] = { 1458c2ecf20Sopenharmony_ci { V4L2_WHITE_BALANCE_INCANDESCENT, COMM_AWB_MODE_INCANDESCENT}, 1468c2ecf20Sopenharmony_ci { V4L2_WHITE_BALANCE_FLUORESCENT, COMM_AWB_MODE_FLUORESCENT1}, 1478c2ecf20Sopenharmony_ci { V4L2_WHITE_BALANCE_FLUORESCENT_H, COMM_AWB_MODE_FLUORESCENT2}, 1488c2ecf20Sopenharmony_ci { V4L2_WHITE_BALANCE_CLOUDY, COMM_AWB_MODE_CLOUDY}, 1498c2ecf20Sopenharmony_ci { V4L2_WHITE_BALANCE_DAYLIGHT, COMM_AWB_MODE_DAYLIGHT}, 1508c2ecf20Sopenharmony_ci { V4L2_WHITE_BALANCE_AUTO, COMM_AWB_MODE_AUTO}, 1518c2ecf20Sopenharmony_ci }; 1528c2ecf20Sopenharmony_ci int i; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(wb); i++) { 1558c2ecf20Sopenharmony_ci if (wb[i][0] != val) 1568c2ecf20Sopenharmony_ci continue; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd, 1598c2ecf20Sopenharmony_ci "Setting white balance to: %s\n", 1608c2ecf20Sopenharmony_ci v4l2_ctrl_get_menu(state->ctrls.auto_wb->id)[i]); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return s5c73m3_isp_command(state, COMM_AWB_MODE, wb[i][1]); 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci return -EINVAL; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic int s5c73m3_af_run(struct s5c73m3 *state, bool on) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct s5c73m3_ctrls *c = &state->ctrls; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (!on) 1738c2ecf20Sopenharmony_ci return s5c73m3_isp_command(state, COMM_AF_CON, 1748c2ecf20Sopenharmony_ci COMM_AF_CON_STOP); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (c->focus_auto->val) 1778c2ecf20Sopenharmony_ci return s5c73m3_isp_command(state, COMM_AF_MODE, 1788c2ecf20Sopenharmony_ci COMM_AF_MODE_PREVIEW_CAF_START); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci return s5c73m3_isp_command(state, COMM_AF_CON, COMM_AF_CON_START); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic int s5c73m3_3a_lock(struct s5c73m3 *state, struct v4l2_ctrl *ctrl) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci bool awb_lock = ctrl->val & V4L2_LOCK_WHITE_BALANCE; 1868c2ecf20Sopenharmony_ci bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE; 1878c2ecf20Sopenharmony_ci bool af_lock = ctrl->val & V4L2_LOCK_FOCUS; 1888c2ecf20Sopenharmony_ci int ret = 0; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_EXPOSURE) { 1918c2ecf20Sopenharmony_ci ret = s5c73m3_isp_command(state, COMM_AE_CON, 1928c2ecf20Sopenharmony_ci ae_lock ? COMM_AE_STOP : COMM_AE_START); 1938c2ecf20Sopenharmony_ci if (ret) 1948c2ecf20Sopenharmony_ci return ret; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_WHITE_BALANCE) 1988c2ecf20Sopenharmony_ci && state->ctrls.auto_wb->val) { 1998c2ecf20Sopenharmony_ci ret = s5c73m3_isp_command(state, COMM_AWB_CON, 2008c2ecf20Sopenharmony_ci awb_lock ? COMM_AWB_STOP : COMM_AWB_START); 2018c2ecf20Sopenharmony_ci if (ret) 2028c2ecf20Sopenharmony_ci return ret; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_FOCUS) 2068c2ecf20Sopenharmony_ci ret = s5c73m3_af_run(state, !af_lock); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci return ret; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic int s5c73m3_set_auto_focus(struct s5c73m3 *state, int caf) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci struct s5c73m3_ctrls *c = &state->ctrls; 2148c2ecf20Sopenharmony_ci int ret = 1; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (c->af_distance->is_new) { 2178c2ecf20Sopenharmony_ci u16 mode = (c->af_distance->val == V4L2_AUTO_FOCUS_RANGE_MACRO) 2188c2ecf20Sopenharmony_ci ? COMM_AF_MODE_MACRO : COMM_AF_MODE_NORMAL; 2198c2ecf20Sopenharmony_ci ret = s5c73m3_isp_command(state, COMM_AF_MODE, mode); 2208c2ecf20Sopenharmony_ci if (ret != 0) 2218c2ecf20Sopenharmony_ci return ret; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (!ret || (c->focus_auto->is_new && c->focus_auto->val) || 2258c2ecf20Sopenharmony_ci c->af_start->is_new) 2268c2ecf20Sopenharmony_ci ret = s5c73m3_af_run(state, 1); 2278c2ecf20Sopenharmony_ci else if ((c->focus_auto->is_new && !c->focus_auto->val) || 2288c2ecf20Sopenharmony_ci c->af_stop->is_new) 2298c2ecf20Sopenharmony_ci ret = s5c73m3_af_run(state, 0); 2308c2ecf20Sopenharmony_ci else 2318c2ecf20Sopenharmony_ci ret = 0; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return ret; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic int s5c73m3_set_contrast(struct s5c73m3 *state, int val) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci u16 reg = (val < 0) ? -val + 2 : val; 2398c2ecf20Sopenharmony_ci return s5c73m3_isp_command(state, COMM_CONTRAST, reg); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic int s5c73m3_set_saturation(struct s5c73m3 *state, int val) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci u16 reg = (val < 0) ? -val + 2 : val; 2458c2ecf20Sopenharmony_ci return s5c73m3_isp_command(state, COMM_SATURATION, reg); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic int s5c73m3_set_sharpness(struct s5c73m3 *state, int val) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci u16 reg = (val < 0) ? -val + 2 : val; 2518c2ecf20Sopenharmony_ci return s5c73m3_isp_command(state, COMM_SHARPNESS, reg); 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic int s5c73m3_set_iso(struct s5c73m3 *state, int val) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci u32 iso; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (val == V4L2_ISO_SENSITIVITY_MANUAL) 2598c2ecf20Sopenharmony_ci iso = state->ctrls.iso->val + 1; 2608c2ecf20Sopenharmony_ci else 2618c2ecf20Sopenharmony_ci iso = 0; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci return s5c73m3_isp_command(state, COMM_ISO, iso); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic int s5c73m3_set_stabilization(struct s5c73m3 *state, int val) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &state->sensor_sd; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci v4l2_dbg(1, s5c73m3_dbg, sd, "Image stabilization: %d\n", val); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci return s5c73m3_isp_command(state, COMM_FRAME_RATE, val ? 2738c2ecf20Sopenharmony_ci COMM_FRAME_RATE_ANTI_SHAKE : COMM_FRAME_RATE_AUTO_SET); 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic int s5c73m3_set_jpeg_quality(struct s5c73m3 *state, int quality) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci int reg; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (quality <= 65) 2818c2ecf20Sopenharmony_ci reg = COMM_IMAGE_QUALITY_NORMAL; 2828c2ecf20Sopenharmony_ci else if (quality <= 75) 2838c2ecf20Sopenharmony_ci reg = COMM_IMAGE_QUALITY_FINE; 2848c2ecf20Sopenharmony_ci else 2858c2ecf20Sopenharmony_ci reg = COMM_IMAGE_QUALITY_SUPERFINE; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return s5c73m3_isp_command(state, COMM_IMAGE_QUALITY, reg); 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic int s5c73m3_set_scene_program(struct s5c73m3 *state, int val) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci static const unsigned short scene_lookup[] = { 2938c2ecf20Sopenharmony_ci COMM_SCENE_MODE_NONE, /* V4L2_SCENE_MODE_NONE */ 2948c2ecf20Sopenharmony_ci COMM_SCENE_MODE_AGAINST_LIGHT,/* V4L2_SCENE_MODE_BACKLIGHT */ 2958c2ecf20Sopenharmony_ci COMM_SCENE_MODE_BEACH, /* V4L2_SCENE_MODE_BEACH_SNOW */ 2968c2ecf20Sopenharmony_ci COMM_SCENE_MODE_CANDLE, /* V4L2_SCENE_MODE_CANDLE_LIGHT */ 2978c2ecf20Sopenharmony_ci COMM_SCENE_MODE_DAWN, /* V4L2_SCENE_MODE_DAWN_DUSK */ 2988c2ecf20Sopenharmony_ci COMM_SCENE_MODE_FALL, /* V4L2_SCENE_MODE_FALL_COLORS */ 2998c2ecf20Sopenharmony_ci COMM_SCENE_MODE_FIRE, /* V4L2_SCENE_MODE_FIREWORKS */ 3008c2ecf20Sopenharmony_ci COMM_SCENE_MODE_LANDSCAPE, /* V4L2_SCENE_MODE_LANDSCAPE */ 3018c2ecf20Sopenharmony_ci COMM_SCENE_MODE_NIGHT, /* V4L2_SCENE_MODE_NIGHT */ 3028c2ecf20Sopenharmony_ci COMM_SCENE_MODE_INDOOR, /* V4L2_SCENE_MODE_PARTY_INDOOR */ 3038c2ecf20Sopenharmony_ci COMM_SCENE_MODE_PORTRAIT, /* V4L2_SCENE_MODE_PORTRAIT */ 3048c2ecf20Sopenharmony_ci COMM_SCENE_MODE_SPORTS, /* V4L2_SCENE_MODE_SPORTS */ 3058c2ecf20Sopenharmony_ci COMM_SCENE_MODE_SUNSET, /* V4L2_SCENE_MODE_SUNSET */ 3068c2ecf20Sopenharmony_ci COMM_SCENE_MODE_TEXT, /* V4L2_SCENE_MODE_TEXT */ 3078c2ecf20Sopenharmony_ci }; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd, "Setting %s scene mode\n", 3108c2ecf20Sopenharmony_ci v4l2_ctrl_get_menu(state->ctrls.scene_mode->id)[val]); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci return s5c73m3_isp_command(state, COMM_SCENE_MODE, scene_lookup[val]); 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic int s5c73m3_set_power_line_freq(struct s5c73m3 *state, int val) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci unsigned int pwr_line_freq = COMM_FLICKER_NONE; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci switch (val) { 3208c2ecf20Sopenharmony_ci case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED: 3218c2ecf20Sopenharmony_ci pwr_line_freq = COMM_FLICKER_NONE; 3228c2ecf20Sopenharmony_ci break; 3238c2ecf20Sopenharmony_ci case V4L2_CID_POWER_LINE_FREQUENCY_50HZ: 3248c2ecf20Sopenharmony_ci pwr_line_freq = COMM_FLICKER_AUTO_50HZ; 3258c2ecf20Sopenharmony_ci break; 3268c2ecf20Sopenharmony_ci case V4L2_CID_POWER_LINE_FREQUENCY_60HZ: 3278c2ecf20Sopenharmony_ci pwr_line_freq = COMM_FLICKER_AUTO_60HZ; 3288c2ecf20Sopenharmony_ci break; 3298c2ecf20Sopenharmony_ci default: 3308c2ecf20Sopenharmony_ci case V4L2_CID_POWER_LINE_FREQUENCY_AUTO: 3318c2ecf20Sopenharmony_ci pwr_line_freq = COMM_FLICKER_NONE; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci return s5c73m3_isp_command(state, COMM_FLICKER_MODE, pwr_line_freq); 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic int s5c73m3_s_ctrl(struct v4l2_ctrl *ctrl) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = ctrl_to_sensor_sd(ctrl); 3408c2ecf20Sopenharmony_ci struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd); 3418c2ecf20Sopenharmony_ci int ret = 0; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci v4l2_dbg(1, s5c73m3_dbg, sd, "set_ctrl: %s, value: %d\n", 3448c2ecf20Sopenharmony_ci ctrl->name, ctrl->val); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci mutex_lock(&state->lock); 3478c2ecf20Sopenharmony_ci /* 3488c2ecf20Sopenharmony_ci * If the device is not powered up by the host driver do 3498c2ecf20Sopenharmony_ci * not apply any controls to H/W at this time. Instead 3508c2ecf20Sopenharmony_ci * the controls will be restored right after power-up. 3518c2ecf20Sopenharmony_ci */ 3528c2ecf20Sopenharmony_ci if (state->power == 0) 3538c2ecf20Sopenharmony_ci goto unlock; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) { 3568c2ecf20Sopenharmony_ci ret = -EINVAL; 3578c2ecf20Sopenharmony_ci goto unlock; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci switch (ctrl->id) { 3618c2ecf20Sopenharmony_ci case V4L2_CID_3A_LOCK: 3628c2ecf20Sopenharmony_ci ret = s5c73m3_3a_lock(state, ctrl); 3638c2ecf20Sopenharmony_ci break; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: 3668c2ecf20Sopenharmony_ci ret = s5c73m3_set_white_balance(state, ctrl->val); 3678c2ecf20Sopenharmony_ci break; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci case V4L2_CID_CONTRAST: 3708c2ecf20Sopenharmony_ci ret = s5c73m3_set_contrast(state, ctrl->val); 3718c2ecf20Sopenharmony_ci break; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci case V4L2_CID_COLORFX: 3748c2ecf20Sopenharmony_ci ret = s5c73m3_set_colorfx(state, ctrl->val); 3758c2ecf20Sopenharmony_ci break; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci case V4L2_CID_EXPOSURE_AUTO: 3788c2ecf20Sopenharmony_ci ret = s5c73m3_set_exposure(state, ctrl->val); 3798c2ecf20Sopenharmony_ci break; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci case V4L2_CID_FOCUS_AUTO: 3828c2ecf20Sopenharmony_ci ret = s5c73m3_set_auto_focus(state, ctrl->val); 3838c2ecf20Sopenharmony_ci break; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci case V4L2_CID_IMAGE_STABILIZATION: 3868c2ecf20Sopenharmony_ci ret = s5c73m3_set_stabilization(state, ctrl->val); 3878c2ecf20Sopenharmony_ci break; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci case V4L2_CID_ISO_SENSITIVITY: 3908c2ecf20Sopenharmony_ci ret = s5c73m3_set_iso(state, ctrl->val); 3918c2ecf20Sopenharmony_ci break; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci case V4L2_CID_JPEG_COMPRESSION_QUALITY: 3948c2ecf20Sopenharmony_ci ret = s5c73m3_set_jpeg_quality(state, ctrl->val); 3958c2ecf20Sopenharmony_ci break; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci case V4L2_CID_POWER_LINE_FREQUENCY: 3988c2ecf20Sopenharmony_ci ret = s5c73m3_set_power_line_freq(state, ctrl->val); 3998c2ecf20Sopenharmony_ci break; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci case V4L2_CID_SATURATION: 4028c2ecf20Sopenharmony_ci ret = s5c73m3_set_saturation(state, ctrl->val); 4038c2ecf20Sopenharmony_ci break; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci case V4L2_CID_SCENE_MODE: 4068c2ecf20Sopenharmony_ci ret = s5c73m3_set_scene_program(state, ctrl->val); 4078c2ecf20Sopenharmony_ci break; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci case V4L2_CID_SHARPNESS: 4108c2ecf20Sopenharmony_ci ret = s5c73m3_set_sharpness(state, ctrl->val); 4118c2ecf20Sopenharmony_ci break; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci case V4L2_CID_WIDE_DYNAMIC_RANGE: 4148c2ecf20Sopenharmony_ci ret = s5c73m3_isp_command(state, COMM_WDR, !!ctrl->val); 4158c2ecf20Sopenharmony_ci break; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci case V4L2_CID_ZOOM_ABSOLUTE: 4188c2ecf20Sopenharmony_ci ret = s5c73m3_isp_command(state, COMM_ZOOM_STEP, ctrl->val); 4198c2ecf20Sopenharmony_ci break; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ciunlock: 4228c2ecf20Sopenharmony_ci mutex_unlock(&state->lock); 4238c2ecf20Sopenharmony_ci return ret; 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops s5c73m3_ctrl_ops = { 4278c2ecf20Sopenharmony_ci .g_volatile_ctrl = s5c73m3_g_volatile_ctrl, 4288c2ecf20Sopenharmony_ci .s_ctrl = s5c73m3_s_ctrl, 4298c2ecf20Sopenharmony_ci}; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci/* Supported manual ISO values */ 4328c2ecf20Sopenharmony_cistatic const s64 iso_qmenu[] = { 4338c2ecf20Sopenharmony_ci /* COMM_ISO: 0x0001...0x0004 */ 4348c2ecf20Sopenharmony_ci 100, 200, 400, 800, 4358c2ecf20Sopenharmony_ci}; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci/* Supported exposure bias values (-2.0EV...+2.0EV) */ 4388c2ecf20Sopenharmony_cistatic const s64 ev_bias_qmenu[] = { 4398c2ecf20Sopenharmony_ci /* COMM_EV: 0x0000...0x0008 */ 4408c2ecf20Sopenharmony_ci -2000, -1500, -1000, -500, 0, 500, 1000, 1500, 2000 4418c2ecf20Sopenharmony_ci}; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ciint s5c73m3_init_controls(struct s5c73m3 *state) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci const struct v4l2_ctrl_ops *ops = &s5c73m3_ctrl_ops; 4468c2ecf20Sopenharmony_ci struct s5c73m3_ctrls *ctrls = &state->ctrls; 4478c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &ctrls->handler; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci int ret = v4l2_ctrl_handler_init(hdl, 22); 4508c2ecf20Sopenharmony_ci if (ret) 4518c2ecf20Sopenharmony_ci return ret; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci /* White balance */ 4548c2ecf20Sopenharmony_ci ctrls->auto_wb = v4l2_ctrl_new_std_menu(hdl, ops, 4558c2ecf20Sopenharmony_ci V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, 4568c2ecf20Sopenharmony_ci 9, ~0x15e, V4L2_WHITE_BALANCE_AUTO); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci /* Exposure (only automatic exposure) */ 4598c2ecf20Sopenharmony_ci ctrls->auto_exposure = v4l2_ctrl_new_std_menu(hdl, ops, 4608c2ecf20Sopenharmony_ci V4L2_CID_EXPOSURE_AUTO, 0, ~0x01, V4L2_EXPOSURE_AUTO); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci ctrls->exposure_bias = v4l2_ctrl_new_int_menu(hdl, ops, 4638c2ecf20Sopenharmony_ci V4L2_CID_AUTO_EXPOSURE_BIAS, 4648c2ecf20Sopenharmony_ci ARRAY_SIZE(ev_bias_qmenu) - 1, 4658c2ecf20Sopenharmony_ci ARRAY_SIZE(ev_bias_qmenu)/2 - 1, 4668c2ecf20Sopenharmony_ci ev_bias_qmenu); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci ctrls->exposure_metering = v4l2_ctrl_new_std_menu(hdl, ops, 4698c2ecf20Sopenharmony_ci V4L2_CID_EXPOSURE_METERING, 4708c2ecf20Sopenharmony_ci 2, ~0x7, V4L2_EXPOSURE_METERING_AVERAGE); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci /* Auto focus */ 4738c2ecf20Sopenharmony_ci ctrls->focus_auto = v4l2_ctrl_new_std(hdl, ops, 4748c2ecf20Sopenharmony_ci V4L2_CID_FOCUS_AUTO, 0, 1, 1, 0); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci ctrls->af_start = v4l2_ctrl_new_std(hdl, ops, 4778c2ecf20Sopenharmony_ci V4L2_CID_AUTO_FOCUS_START, 0, 1, 1, 0); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci ctrls->af_stop = v4l2_ctrl_new_std(hdl, ops, 4808c2ecf20Sopenharmony_ci V4L2_CID_AUTO_FOCUS_STOP, 0, 1, 1, 0); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci ctrls->af_status = v4l2_ctrl_new_std(hdl, ops, 4838c2ecf20Sopenharmony_ci V4L2_CID_AUTO_FOCUS_STATUS, 0, 4848c2ecf20Sopenharmony_ci (V4L2_AUTO_FOCUS_STATUS_BUSY | 4858c2ecf20Sopenharmony_ci V4L2_AUTO_FOCUS_STATUS_REACHED | 4868c2ecf20Sopenharmony_ci V4L2_AUTO_FOCUS_STATUS_FAILED), 4878c2ecf20Sopenharmony_ci 0, V4L2_AUTO_FOCUS_STATUS_IDLE); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci ctrls->af_distance = v4l2_ctrl_new_std_menu(hdl, ops, 4908c2ecf20Sopenharmony_ci V4L2_CID_AUTO_FOCUS_RANGE, 4918c2ecf20Sopenharmony_ci V4L2_AUTO_FOCUS_RANGE_MACRO, 4928c2ecf20Sopenharmony_ci ~(1 << V4L2_AUTO_FOCUS_RANGE_NORMAL | 4938c2ecf20Sopenharmony_ci 1 << V4L2_AUTO_FOCUS_RANGE_MACRO), 4948c2ecf20Sopenharmony_ci V4L2_AUTO_FOCUS_RANGE_NORMAL); 4958c2ecf20Sopenharmony_ci /* ISO sensitivity */ 4968c2ecf20Sopenharmony_ci ctrls->auto_iso = v4l2_ctrl_new_std_menu(hdl, ops, 4978c2ecf20Sopenharmony_ci V4L2_CID_ISO_SENSITIVITY_AUTO, 1, 0, 4988c2ecf20Sopenharmony_ci V4L2_ISO_SENSITIVITY_AUTO); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci ctrls->iso = v4l2_ctrl_new_int_menu(hdl, ops, 5018c2ecf20Sopenharmony_ci V4L2_CID_ISO_SENSITIVITY, ARRAY_SIZE(iso_qmenu) - 1, 5028c2ecf20Sopenharmony_ci ARRAY_SIZE(iso_qmenu)/2 - 1, iso_qmenu); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci ctrls->contrast = v4l2_ctrl_new_std(hdl, ops, 5058c2ecf20Sopenharmony_ci V4L2_CID_CONTRAST, -2, 2, 1, 0); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, 5088c2ecf20Sopenharmony_ci V4L2_CID_SATURATION, -2, 2, 1, 0); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci ctrls->sharpness = v4l2_ctrl_new_std(hdl, ops, 5118c2ecf20Sopenharmony_ci V4L2_CID_SHARPNESS, -2, 2, 1, 0); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci ctrls->zoom = v4l2_ctrl_new_std(hdl, ops, 5148c2ecf20Sopenharmony_ci V4L2_CID_ZOOM_ABSOLUTE, 0, 30, 1, 0); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci ctrls->colorfx = v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX, 5178c2ecf20Sopenharmony_ci V4L2_COLORFX_AQUA, ~0x40f, V4L2_COLORFX_NONE); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci ctrls->wdr = v4l2_ctrl_new_std(hdl, ops, 5208c2ecf20Sopenharmony_ci V4L2_CID_WIDE_DYNAMIC_RANGE, 0, 1, 1, 0); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci ctrls->stabilization = v4l2_ctrl_new_std(hdl, ops, 5238c2ecf20Sopenharmony_ci V4L2_CID_IMAGE_STABILIZATION, 0, 1, 1, 0); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY, 5268c2ecf20Sopenharmony_ci V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, 5278c2ecf20Sopenharmony_ci V4L2_CID_POWER_LINE_FREQUENCY_AUTO); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci ctrls->jpeg_quality = v4l2_ctrl_new_std(hdl, ops, 5308c2ecf20Sopenharmony_ci V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, 100, 1, 80); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci ctrls->scene_mode = v4l2_ctrl_new_std_menu(hdl, ops, 5338c2ecf20Sopenharmony_ci V4L2_CID_SCENE_MODE, V4L2_SCENE_MODE_TEXT, ~0x3fff, 5348c2ecf20Sopenharmony_ci V4L2_SCENE_MODE_NONE); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci ctrls->aaa_lock = v4l2_ctrl_new_std(hdl, ops, 5378c2ecf20Sopenharmony_ci V4L2_CID_3A_LOCK, 0, 0x7, 0, 0); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci if (hdl->error) { 5408c2ecf20Sopenharmony_ci ret = hdl->error; 5418c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(hdl); 5428c2ecf20Sopenharmony_ci return ret; 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci v4l2_ctrl_auto_cluster(3, &ctrls->auto_exposure, 0, false); 5468c2ecf20Sopenharmony_ci ctrls->auto_iso->flags |= V4L2_CTRL_FLAG_VOLATILE | 5478c2ecf20Sopenharmony_ci V4L2_CTRL_FLAG_UPDATE; 5488c2ecf20Sopenharmony_ci v4l2_ctrl_auto_cluster(2, &ctrls->auto_iso, 0, false); 5498c2ecf20Sopenharmony_ci ctrls->af_status->flags |= V4L2_CTRL_FLAG_VOLATILE; 5508c2ecf20Sopenharmony_ci v4l2_ctrl_cluster(5, &ctrls->focus_auto); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci state->sensor_sd.ctrl_handler = hdl; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci return 0; 5558c2ecf20Sopenharmony_ci} 556