18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Controls for M-5MOLS 8M Pixel camera sensor with ISP
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2011 Samsung Electronics Co., Ltd.
68c2ecf20Sopenharmony_ci * Author: HeungJun Kim <riverful.kim@samsung.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Copyright (C) 2009 Samsung Electronics Co., Ltd.
98c2ecf20Sopenharmony_ci * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/i2c.h>
138c2ecf20Sopenharmony_ci#include <linux/delay.h>
148c2ecf20Sopenharmony_ci#include <linux/videodev2.h>
158c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "m5mols.h"
188c2ecf20Sopenharmony_ci#include "m5mols_reg.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic struct m5mols_scenemode m5mols_default_scenemode[] = {
218c2ecf20Sopenharmony_ci	[REG_SCENE_NORMAL] = {
228c2ecf20Sopenharmony_ci		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
238c2ecf20Sopenharmony_ci		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
248c2ecf20Sopenharmony_ci		REG_AF_NORMAL, REG_FD_OFF,
258c2ecf20Sopenharmony_ci		REG_MCC_NORMAL, REG_LIGHT_OFF, REG_FLASH_OFF,
268c2ecf20Sopenharmony_ci		5, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
278c2ecf20Sopenharmony_ci	},
288c2ecf20Sopenharmony_ci	[REG_SCENE_PORTRAIT] = {
298c2ecf20Sopenharmony_ci		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
308c2ecf20Sopenharmony_ci		REG_CHROMA_ON, 3, REG_EDGE_ON, 4,
318c2ecf20Sopenharmony_ci		REG_AF_NORMAL, BIT_FD_EN | BIT_FD_DRAW_FACE_FRAME,
328c2ecf20Sopenharmony_ci		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
338c2ecf20Sopenharmony_ci		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
348c2ecf20Sopenharmony_ci	},
358c2ecf20Sopenharmony_ci	[REG_SCENE_LANDSCAPE] = {
368c2ecf20Sopenharmony_ci		REG_AE_ALL, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
378c2ecf20Sopenharmony_ci		REG_CHROMA_ON, 4, REG_EDGE_ON, 6,
388c2ecf20Sopenharmony_ci		REG_AF_NORMAL, REG_FD_OFF,
398c2ecf20Sopenharmony_ci		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
408c2ecf20Sopenharmony_ci		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
418c2ecf20Sopenharmony_ci	},
428c2ecf20Sopenharmony_ci	[REG_SCENE_SPORTS] = {
438c2ecf20Sopenharmony_ci		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
448c2ecf20Sopenharmony_ci		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
458c2ecf20Sopenharmony_ci		REG_AF_NORMAL, REG_FD_OFF,
468c2ecf20Sopenharmony_ci		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
478c2ecf20Sopenharmony_ci		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
488c2ecf20Sopenharmony_ci	},
498c2ecf20Sopenharmony_ci	[REG_SCENE_PARTY_INDOOR] = {
508c2ecf20Sopenharmony_ci		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
518c2ecf20Sopenharmony_ci		REG_CHROMA_ON, 4, REG_EDGE_ON, 5,
528c2ecf20Sopenharmony_ci		REG_AF_NORMAL, REG_FD_OFF,
538c2ecf20Sopenharmony_ci		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
548c2ecf20Sopenharmony_ci		6, REG_ISO_200, REG_CAP_NONE, REG_WDR_OFF,
558c2ecf20Sopenharmony_ci	},
568c2ecf20Sopenharmony_ci	[REG_SCENE_BEACH_SNOW] = {
578c2ecf20Sopenharmony_ci		REG_AE_CENTER, REG_AE_INDEX_10_POS, REG_AWB_AUTO, 0,
588c2ecf20Sopenharmony_ci		REG_CHROMA_ON, 4, REG_EDGE_ON, 5,
598c2ecf20Sopenharmony_ci		REG_AF_NORMAL, REG_FD_OFF,
608c2ecf20Sopenharmony_ci		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
618c2ecf20Sopenharmony_ci		6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF,
628c2ecf20Sopenharmony_ci	},
638c2ecf20Sopenharmony_ci	[REG_SCENE_SUNSET] = {
648c2ecf20Sopenharmony_ci		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET,
658c2ecf20Sopenharmony_ci		REG_AWB_DAYLIGHT,
668c2ecf20Sopenharmony_ci		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
678c2ecf20Sopenharmony_ci		REG_AF_NORMAL, REG_FD_OFF,
688c2ecf20Sopenharmony_ci		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
698c2ecf20Sopenharmony_ci		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
708c2ecf20Sopenharmony_ci	},
718c2ecf20Sopenharmony_ci	[REG_SCENE_DAWN_DUSK] = {
728c2ecf20Sopenharmony_ci		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET,
738c2ecf20Sopenharmony_ci		REG_AWB_FLUORESCENT_1,
748c2ecf20Sopenharmony_ci		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
758c2ecf20Sopenharmony_ci		REG_AF_NORMAL, REG_FD_OFF,
768c2ecf20Sopenharmony_ci		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
778c2ecf20Sopenharmony_ci		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
788c2ecf20Sopenharmony_ci	},
798c2ecf20Sopenharmony_ci	[REG_SCENE_FALL] = {
808c2ecf20Sopenharmony_ci		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
818c2ecf20Sopenharmony_ci		REG_CHROMA_ON, 5, REG_EDGE_ON, 5,
828c2ecf20Sopenharmony_ci		REG_AF_NORMAL, REG_FD_OFF,
838c2ecf20Sopenharmony_ci		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
848c2ecf20Sopenharmony_ci		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
858c2ecf20Sopenharmony_ci	},
868c2ecf20Sopenharmony_ci	[REG_SCENE_NIGHT] = {
878c2ecf20Sopenharmony_ci		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
888c2ecf20Sopenharmony_ci		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
898c2ecf20Sopenharmony_ci		REG_AF_NORMAL, REG_FD_OFF,
908c2ecf20Sopenharmony_ci		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
918c2ecf20Sopenharmony_ci		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
928c2ecf20Sopenharmony_ci	},
938c2ecf20Sopenharmony_ci	[REG_SCENE_AGAINST_LIGHT] = {
948c2ecf20Sopenharmony_ci		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
958c2ecf20Sopenharmony_ci		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
968c2ecf20Sopenharmony_ci		REG_AF_NORMAL, REG_FD_OFF,
978c2ecf20Sopenharmony_ci		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
988c2ecf20Sopenharmony_ci		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
998c2ecf20Sopenharmony_ci	},
1008c2ecf20Sopenharmony_ci	[REG_SCENE_FIRE] = {
1018c2ecf20Sopenharmony_ci		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
1028c2ecf20Sopenharmony_ci		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
1038c2ecf20Sopenharmony_ci		REG_AF_NORMAL, REG_FD_OFF,
1048c2ecf20Sopenharmony_ci		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
1058c2ecf20Sopenharmony_ci		6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF,
1068c2ecf20Sopenharmony_ci	},
1078c2ecf20Sopenharmony_ci	[REG_SCENE_TEXT] = {
1088c2ecf20Sopenharmony_ci		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
1098c2ecf20Sopenharmony_ci		REG_CHROMA_ON, 3, REG_EDGE_ON, 7,
1108c2ecf20Sopenharmony_ci		REG_AF_MACRO, REG_FD_OFF,
1118c2ecf20Sopenharmony_ci		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
1128c2ecf20Sopenharmony_ci		6, REG_ISO_AUTO, REG_CAP_ANTI_SHAKE, REG_WDR_ON,
1138c2ecf20Sopenharmony_ci	},
1148c2ecf20Sopenharmony_ci	[REG_SCENE_CANDLE] = {
1158c2ecf20Sopenharmony_ci		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
1168c2ecf20Sopenharmony_ci		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
1178c2ecf20Sopenharmony_ci		REG_AF_NORMAL, REG_FD_OFF,
1188c2ecf20Sopenharmony_ci		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
1198c2ecf20Sopenharmony_ci		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
1208c2ecf20Sopenharmony_ci	},
1218c2ecf20Sopenharmony_ci};
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci/**
1248c2ecf20Sopenharmony_ci * m5mols_do_scenemode() - Change current scenemode
1258c2ecf20Sopenharmony_ci * @info: M-5MOLS driver data structure
1268c2ecf20Sopenharmony_ci * @mode:	Desired mode of the scenemode
1278c2ecf20Sopenharmony_ci *
1288c2ecf20Sopenharmony_ci * WARNING: The execution order is important. Do not change the order.
1298c2ecf20Sopenharmony_ci */
1308c2ecf20Sopenharmony_ciint m5mols_do_scenemode(struct m5mols_info *info, u8 mode)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd = &info->sd;
1338c2ecf20Sopenharmony_ci	struct m5mols_scenemode scenemode = m5mols_default_scenemode[mode];
1348c2ecf20Sopenharmony_ci	int ret;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	if (mode > REG_SCENE_CANDLE)
1378c2ecf20Sopenharmony_ci		return -EINVAL;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	ret = v4l2_ctrl_s_ctrl(info->lock_3a, 0);
1408c2ecf20Sopenharmony_ci	if (!ret)
1418c2ecf20Sopenharmony_ci		ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, mode);
1428c2ecf20Sopenharmony_ci	if (!ret)
1438c2ecf20Sopenharmony_ci		ret = m5mols_write(sd, AE_EV_PRESET_CAPTURE, mode);
1448c2ecf20Sopenharmony_ci	if (!ret)
1458c2ecf20Sopenharmony_ci		ret = m5mols_write(sd, AE_MODE, scenemode.metering);
1468c2ecf20Sopenharmony_ci	if (!ret)
1478c2ecf20Sopenharmony_ci		ret = m5mols_write(sd, AE_INDEX, scenemode.ev_bias);
1488c2ecf20Sopenharmony_ci	if (!ret)
1498c2ecf20Sopenharmony_ci		ret = m5mols_write(sd, AWB_MODE, scenemode.wb_mode);
1508c2ecf20Sopenharmony_ci	if (!ret)
1518c2ecf20Sopenharmony_ci		ret = m5mols_write(sd, AWB_MANUAL, scenemode.wb_preset);
1528c2ecf20Sopenharmony_ci	if (!ret)
1538c2ecf20Sopenharmony_ci		ret = m5mols_write(sd, MON_CHROMA_EN, scenemode.chroma_en);
1548c2ecf20Sopenharmony_ci	if (!ret)
1558c2ecf20Sopenharmony_ci		ret = m5mols_write(sd, MON_CHROMA_LVL, scenemode.chroma_lvl);
1568c2ecf20Sopenharmony_ci	if (!ret)
1578c2ecf20Sopenharmony_ci		ret = m5mols_write(sd, MON_EDGE_EN, scenemode.edge_en);
1588c2ecf20Sopenharmony_ci	if (!ret)
1598c2ecf20Sopenharmony_ci		ret = m5mols_write(sd, MON_EDGE_LVL, scenemode.edge_lvl);
1608c2ecf20Sopenharmony_ci	if (!ret && is_available_af(info))
1618c2ecf20Sopenharmony_ci		ret = m5mols_write(sd, AF_MODE, scenemode.af_range);
1628c2ecf20Sopenharmony_ci	if (!ret && is_available_af(info))
1638c2ecf20Sopenharmony_ci		ret = m5mols_write(sd, FD_CTL, scenemode.fd_mode);
1648c2ecf20Sopenharmony_ci	if (!ret)
1658c2ecf20Sopenharmony_ci		ret = m5mols_write(sd, MON_TONE_CTL, scenemode.tone);
1668c2ecf20Sopenharmony_ci	if (!ret)
1678c2ecf20Sopenharmony_ci		ret = m5mols_write(sd, AE_ISO, scenemode.iso);
1688c2ecf20Sopenharmony_ci	if (!ret)
1698c2ecf20Sopenharmony_ci		ret = m5mols_set_mode(info, REG_CAPTURE);
1708c2ecf20Sopenharmony_ci	if (!ret)
1718c2ecf20Sopenharmony_ci		ret = m5mols_write(sd, CAPP_WDR_EN, scenemode.wdr);
1728c2ecf20Sopenharmony_ci	if (!ret)
1738c2ecf20Sopenharmony_ci		ret = m5mols_write(sd, CAPP_MCC_MODE, scenemode.mcc);
1748c2ecf20Sopenharmony_ci	if (!ret)
1758c2ecf20Sopenharmony_ci		ret = m5mols_write(sd, CAPP_LIGHT_CTRL, scenemode.light);
1768c2ecf20Sopenharmony_ci	if (!ret)
1778c2ecf20Sopenharmony_ci		ret = m5mols_write(sd, CAPP_FLASH_CTRL, scenemode.flash);
1788c2ecf20Sopenharmony_ci	if (!ret)
1798c2ecf20Sopenharmony_ci		ret = m5mols_write(sd, CAPC_MODE, scenemode.capt_mode);
1808c2ecf20Sopenharmony_ci	if (!ret)
1818c2ecf20Sopenharmony_ci		ret = m5mols_set_mode(info, REG_MONITOR);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	return ret;
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic int m5mols_3a_lock(struct m5mols_info *info, struct v4l2_ctrl *ctrl)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	bool af_lock = ctrl->val & V4L2_LOCK_FOCUS;
1898c2ecf20Sopenharmony_ci	int ret = 0;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_EXPOSURE) {
1928c2ecf20Sopenharmony_ci		bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci		ret = m5mols_write(&info->sd, AE_LOCK, ae_lock ?
1958c2ecf20Sopenharmony_ci				   REG_AE_LOCK : REG_AE_UNLOCK);
1968c2ecf20Sopenharmony_ci		if (ret)
1978c2ecf20Sopenharmony_ci			return ret;
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	if (((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_WHITE_BALANCE)
2018c2ecf20Sopenharmony_ci	    && info->auto_wb->val) {
2028c2ecf20Sopenharmony_ci		bool awb_lock = ctrl->val & V4L2_LOCK_WHITE_BALANCE;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci		ret = m5mols_write(&info->sd, AWB_LOCK, awb_lock ?
2058c2ecf20Sopenharmony_ci				   REG_AWB_LOCK : REG_AWB_UNLOCK);
2068c2ecf20Sopenharmony_ci		if (ret)
2078c2ecf20Sopenharmony_ci			return ret;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	if (!info->ver.af || !af_lock)
2118c2ecf20Sopenharmony_ci		return ret;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_FOCUS)
2148c2ecf20Sopenharmony_ci		ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	return ret;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic int m5mols_set_metering_mode(struct m5mols_info *info, int mode)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	unsigned int metering;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	switch (mode) {
2248c2ecf20Sopenharmony_ci	case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED:
2258c2ecf20Sopenharmony_ci		metering = REG_AE_CENTER;
2268c2ecf20Sopenharmony_ci		break;
2278c2ecf20Sopenharmony_ci	case V4L2_EXPOSURE_METERING_SPOT:
2288c2ecf20Sopenharmony_ci		metering = REG_AE_SPOT;
2298c2ecf20Sopenharmony_ci		break;
2308c2ecf20Sopenharmony_ci	default:
2318c2ecf20Sopenharmony_ci		metering = REG_AE_ALL;
2328c2ecf20Sopenharmony_ci		break;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	return m5mols_write(&info->sd, AE_MODE, metering);
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cistatic int m5mols_set_exposure(struct m5mols_info *info, int exposure)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd = &info->sd;
2418c2ecf20Sopenharmony_ci	int ret = 0;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	if (exposure == V4L2_EXPOSURE_AUTO) {
2448c2ecf20Sopenharmony_ci		/* Unlock auto exposure */
2458c2ecf20Sopenharmony_ci		info->lock_3a->val &= ~V4L2_LOCK_EXPOSURE;
2468c2ecf20Sopenharmony_ci		m5mols_3a_lock(info, info->lock_3a);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci		ret = m5mols_set_metering_mode(info, info->metering->val);
2498c2ecf20Sopenharmony_ci		if (ret < 0)
2508c2ecf20Sopenharmony_ci			return ret;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci		v4l2_dbg(1, m5mols_debug, sd,
2538c2ecf20Sopenharmony_ci			 "%s: exposure bias: %#x, metering: %#x\n",
2548c2ecf20Sopenharmony_ci			 __func__, info->exposure_bias->val,
2558c2ecf20Sopenharmony_ci			 info->metering->val);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci		return m5mols_write(sd, AE_INDEX, info->exposure_bias->val);
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	if (exposure == V4L2_EXPOSURE_MANUAL) {
2618c2ecf20Sopenharmony_ci		ret = m5mols_write(sd, AE_MODE, REG_AE_OFF);
2628c2ecf20Sopenharmony_ci		if (ret == 0)
2638c2ecf20Sopenharmony_ci			ret = m5mols_write(sd, AE_MAN_GAIN_MON,
2648c2ecf20Sopenharmony_ci					   info->exposure->val);
2658c2ecf20Sopenharmony_ci		if (ret == 0)
2668c2ecf20Sopenharmony_ci			ret = m5mols_write(sd, AE_MAN_GAIN_CAP,
2678c2ecf20Sopenharmony_ci					   info->exposure->val);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci		v4l2_dbg(1, m5mols_debug, sd, "%s: exposure: %#x\n",
2708c2ecf20Sopenharmony_ci			 __func__, info->exposure->val);
2718c2ecf20Sopenharmony_ci	}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	return ret;
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic int m5mols_set_white_balance(struct m5mols_info *info, int val)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	static const unsigned short wb[][2] = {
2798c2ecf20Sopenharmony_ci		{ V4L2_WHITE_BALANCE_INCANDESCENT,  REG_AWB_INCANDESCENT },
2808c2ecf20Sopenharmony_ci		{ V4L2_WHITE_BALANCE_FLUORESCENT,   REG_AWB_FLUORESCENT_1 },
2818c2ecf20Sopenharmony_ci		{ V4L2_WHITE_BALANCE_FLUORESCENT_H, REG_AWB_FLUORESCENT_2 },
2828c2ecf20Sopenharmony_ci		{ V4L2_WHITE_BALANCE_HORIZON,       REG_AWB_HORIZON },
2838c2ecf20Sopenharmony_ci		{ V4L2_WHITE_BALANCE_DAYLIGHT,      REG_AWB_DAYLIGHT },
2848c2ecf20Sopenharmony_ci		{ V4L2_WHITE_BALANCE_FLASH,         REG_AWB_LEDLIGHT },
2858c2ecf20Sopenharmony_ci		{ V4L2_WHITE_BALANCE_CLOUDY,        REG_AWB_CLOUDY },
2868c2ecf20Sopenharmony_ci		{ V4L2_WHITE_BALANCE_SHADE,         REG_AWB_SHADE },
2878c2ecf20Sopenharmony_ci		{ V4L2_WHITE_BALANCE_AUTO,          REG_AWB_AUTO },
2888c2ecf20Sopenharmony_ci	};
2898c2ecf20Sopenharmony_ci	int i;
2908c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd = &info->sd;
2918c2ecf20Sopenharmony_ci	int ret = -EINVAL;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(wb); i++) {
2948c2ecf20Sopenharmony_ci		int awb;
2958c2ecf20Sopenharmony_ci		if (wb[i][0] != val)
2968c2ecf20Sopenharmony_ci			continue;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci		v4l2_dbg(1, m5mols_debug, sd,
2998c2ecf20Sopenharmony_ci			 "Setting white balance to: %#x\n", wb[i][0]);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci		awb = wb[i][0] == V4L2_WHITE_BALANCE_AUTO;
3028c2ecf20Sopenharmony_ci		ret = m5mols_write(sd, AWB_MODE, awb ? REG_AWB_AUTO :
3038c2ecf20Sopenharmony_ci						 REG_AWB_PRESET);
3048c2ecf20Sopenharmony_ci		if (ret < 0)
3058c2ecf20Sopenharmony_ci			return ret;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci		if (!awb)
3088c2ecf20Sopenharmony_ci			ret = m5mols_write(sd, AWB_MANUAL, wb[i][1]);
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	return ret;
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic int m5mols_set_saturation(struct m5mols_info *info, int val)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	int ret = m5mols_write(&info->sd, MON_CHROMA_LVL, val);
3178c2ecf20Sopenharmony_ci	if (ret < 0)
3188c2ecf20Sopenharmony_ci		return ret;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	return m5mols_write(&info->sd, MON_CHROMA_EN, REG_CHROMA_ON);
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic int m5mols_set_color_effect(struct m5mols_info *info, int val)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	unsigned int m_effect = REG_COLOR_EFFECT_OFF;
3268c2ecf20Sopenharmony_ci	unsigned int p_effect = REG_EFFECT_OFF;
3278c2ecf20Sopenharmony_ci	unsigned int cfix_r = 0, cfix_b = 0;
3288c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd = &info->sd;
3298c2ecf20Sopenharmony_ci	int ret = 0;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	switch (val) {
3328c2ecf20Sopenharmony_ci	case V4L2_COLORFX_BW:
3338c2ecf20Sopenharmony_ci		m_effect = REG_COLOR_EFFECT_ON;
3348c2ecf20Sopenharmony_ci		break;
3358c2ecf20Sopenharmony_ci	case V4L2_COLORFX_NEGATIVE:
3368c2ecf20Sopenharmony_ci		p_effect = REG_EFFECT_NEGA;
3378c2ecf20Sopenharmony_ci		break;
3388c2ecf20Sopenharmony_ci	case V4L2_COLORFX_EMBOSS:
3398c2ecf20Sopenharmony_ci		p_effect = REG_EFFECT_EMBOSS;
3408c2ecf20Sopenharmony_ci		break;
3418c2ecf20Sopenharmony_ci	case V4L2_COLORFX_SEPIA:
3428c2ecf20Sopenharmony_ci		m_effect = REG_COLOR_EFFECT_ON;
3438c2ecf20Sopenharmony_ci		cfix_r = REG_CFIXR_SEPIA;
3448c2ecf20Sopenharmony_ci		cfix_b = REG_CFIXB_SEPIA;
3458c2ecf20Sopenharmony_ci		break;
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	ret = m5mols_write(sd, PARM_EFFECT, p_effect);
3498c2ecf20Sopenharmony_ci	if (!ret)
3508c2ecf20Sopenharmony_ci		ret = m5mols_write(sd, MON_EFFECT, m_effect);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	if (ret == 0 && m_effect == REG_COLOR_EFFECT_ON) {
3538c2ecf20Sopenharmony_ci		ret = m5mols_write(sd, MON_CFIXR, cfix_r);
3548c2ecf20Sopenharmony_ci		if (!ret)
3558c2ecf20Sopenharmony_ci			ret = m5mols_write(sd, MON_CFIXB, cfix_b);
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	v4l2_dbg(1, m5mols_debug, sd,
3598c2ecf20Sopenharmony_ci		 "p_effect: %#x, m_effect: %#x, r: %#x, b: %#x (%d)\n",
3608c2ecf20Sopenharmony_ci		 p_effect, m_effect, cfix_r, cfix_b, ret);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	return ret;
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_cistatic int m5mols_set_iso(struct m5mols_info *info, int auto_iso)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	u32 iso = auto_iso ? 0 : info->iso->val + 1;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	return m5mols_write(&info->sd, AE_ISO, iso);
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic int m5mols_set_wdr(struct m5mols_info *info, int wdr)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	int ret;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	ret = m5mols_write(&info->sd, MON_TONE_CTL, wdr ? 9 : 5);
3778c2ecf20Sopenharmony_ci	if (ret < 0)
3788c2ecf20Sopenharmony_ci		return ret;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	ret = m5mols_set_mode(info, REG_CAPTURE);
3818c2ecf20Sopenharmony_ci	if (ret < 0)
3828c2ecf20Sopenharmony_ci		return ret;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	return m5mols_write(&info->sd, CAPP_WDR_EN, wdr);
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistatic int m5mols_set_stabilization(struct m5mols_info *info, int val)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd = &info->sd;
3908c2ecf20Sopenharmony_ci	unsigned int evp = val ? 0xe : 0x0;
3918c2ecf20Sopenharmony_ci	int ret;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, evp);
3948c2ecf20Sopenharmony_ci	if (ret < 0)
3958c2ecf20Sopenharmony_ci		return ret;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	return m5mols_write(sd, AE_EV_PRESET_CAPTURE, evp);
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cistatic int m5mols_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
4018c2ecf20Sopenharmony_ci{
4028c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd = to_sd(ctrl);
4038c2ecf20Sopenharmony_ci	struct m5mols_info *info = to_m5mols(sd);
4048c2ecf20Sopenharmony_ci	int ret = 0;
4058c2ecf20Sopenharmony_ci	u8 status = REG_ISO_AUTO;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	v4l2_dbg(1, m5mols_debug, sd, "%s: ctrl: %s (%d)\n",
4088c2ecf20Sopenharmony_ci		 __func__, ctrl->name, info->isp_ready);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	if (!info->isp_ready)
4118c2ecf20Sopenharmony_ci		return -EBUSY;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	switch (ctrl->id) {
4148c2ecf20Sopenharmony_ci	case V4L2_CID_ISO_SENSITIVITY_AUTO:
4158c2ecf20Sopenharmony_ci		ret = m5mols_read_u8(sd, AE_ISO, &status);
4168c2ecf20Sopenharmony_ci		if (ret == 0)
4178c2ecf20Sopenharmony_ci			ctrl->val = !status;
4188c2ecf20Sopenharmony_ci		if (status != REG_ISO_AUTO)
4198c2ecf20Sopenharmony_ci			info->iso->val = status - 1;
4208c2ecf20Sopenharmony_ci		break;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	case V4L2_CID_3A_LOCK:
4238c2ecf20Sopenharmony_ci		ctrl->val &= ~0x7;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci		ret = m5mols_read_u8(sd, AE_LOCK, &status);
4268c2ecf20Sopenharmony_ci		if (ret)
4278c2ecf20Sopenharmony_ci			return ret;
4288c2ecf20Sopenharmony_ci		if (status)
4298c2ecf20Sopenharmony_ci			info->lock_3a->val |= V4L2_LOCK_EXPOSURE;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci		ret = m5mols_read_u8(sd, AWB_LOCK, &status);
4328c2ecf20Sopenharmony_ci		if (ret)
4338c2ecf20Sopenharmony_ci			return ret;
4348c2ecf20Sopenharmony_ci		if (status)
4358c2ecf20Sopenharmony_ci			info->lock_3a->val |= V4L2_LOCK_EXPOSURE;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci		ret = m5mols_read_u8(sd, AF_EXECUTE, &status);
4388c2ecf20Sopenharmony_ci		if (!status)
4398c2ecf20Sopenharmony_ci			info->lock_3a->val |= V4L2_LOCK_EXPOSURE;
4408c2ecf20Sopenharmony_ci		break;
4418c2ecf20Sopenharmony_ci	}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	return ret;
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_cistatic int m5mols_s_ctrl(struct v4l2_ctrl *ctrl)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	unsigned int ctrl_mode = m5mols_get_ctrl_mode(ctrl);
4498c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd = to_sd(ctrl);
4508c2ecf20Sopenharmony_ci	struct m5mols_info *info = to_m5mols(sd);
4518c2ecf20Sopenharmony_ci	int last_mode = info->mode;
4528c2ecf20Sopenharmony_ci	int ret = 0;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	/*
4558c2ecf20Sopenharmony_ci	 * If needed, defer restoring the controls until
4568c2ecf20Sopenharmony_ci	 * the device is fully initialized.
4578c2ecf20Sopenharmony_ci	 */
4588c2ecf20Sopenharmony_ci	if (!info->isp_ready) {
4598c2ecf20Sopenharmony_ci		info->ctrl_sync = 0;
4608c2ecf20Sopenharmony_ci		return 0;
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	v4l2_dbg(1, m5mols_debug, sd, "%s: %s, val: %d, priv: %p\n",
4648c2ecf20Sopenharmony_ci		 __func__, ctrl->name, ctrl->val, ctrl->priv);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	if (ctrl_mode && ctrl_mode != info->mode) {
4678c2ecf20Sopenharmony_ci		ret = m5mols_set_mode(info, ctrl_mode);
4688c2ecf20Sopenharmony_ci		if (ret < 0)
4698c2ecf20Sopenharmony_ci			return ret;
4708c2ecf20Sopenharmony_ci	}
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	switch (ctrl->id) {
4738c2ecf20Sopenharmony_ci	case V4L2_CID_3A_LOCK:
4748c2ecf20Sopenharmony_ci		ret = m5mols_3a_lock(info, ctrl);
4758c2ecf20Sopenharmony_ci		break;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	case V4L2_CID_ZOOM_ABSOLUTE:
4788c2ecf20Sopenharmony_ci		ret = m5mols_write(sd, MON_ZOOM, ctrl->val);
4798c2ecf20Sopenharmony_ci		break;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	case V4L2_CID_EXPOSURE_AUTO:
4828c2ecf20Sopenharmony_ci		ret = m5mols_set_exposure(info, ctrl->val);
4838c2ecf20Sopenharmony_ci		break;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	case V4L2_CID_ISO_SENSITIVITY:
4868c2ecf20Sopenharmony_ci		ret = m5mols_set_iso(info, ctrl->val);
4878c2ecf20Sopenharmony_ci		break;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:
4908c2ecf20Sopenharmony_ci		ret = m5mols_set_white_balance(info, ctrl->val);
4918c2ecf20Sopenharmony_ci		break;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	case V4L2_CID_SATURATION:
4948c2ecf20Sopenharmony_ci		ret = m5mols_set_saturation(info, ctrl->val);
4958c2ecf20Sopenharmony_ci		break;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	case V4L2_CID_COLORFX:
4988c2ecf20Sopenharmony_ci		ret = m5mols_set_color_effect(info, ctrl->val);
4998c2ecf20Sopenharmony_ci		break;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	case V4L2_CID_WIDE_DYNAMIC_RANGE:
5028c2ecf20Sopenharmony_ci		ret = m5mols_set_wdr(info, ctrl->val);
5038c2ecf20Sopenharmony_ci		break;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	case V4L2_CID_IMAGE_STABILIZATION:
5068c2ecf20Sopenharmony_ci		ret = m5mols_set_stabilization(info, ctrl->val);
5078c2ecf20Sopenharmony_ci		break;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	case V4L2_CID_JPEG_COMPRESSION_QUALITY:
5108c2ecf20Sopenharmony_ci		ret = m5mols_write(sd, CAPP_JPEG_RATIO, ctrl->val);
5118c2ecf20Sopenharmony_ci		break;
5128c2ecf20Sopenharmony_ci	}
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	if (ret == 0 && info->mode != last_mode)
5158c2ecf20Sopenharmony_ci		ret = m5mols_set_mode(info, last_mode);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	return ret;
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops m5mols_ctrl_ops = {
5218c2ecf20Sopenharmony_ci	.g_volatile_ctrl	= m5mols_g_volatile_ctrl,
5228c2ecf20Sopenharmony_ci	.s_ctrl			= m5mols_s_ctrl,
5238c2ecf20Sopenharmony_ci};
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci/* Supported manual ISO values */
5268c2ecf20Sopenharmony_cistatic const s64 iso_qmenu[] = {
5278c2ecf20Sopenharmony_ci	/* AE_ISO: 0x01...0x07 (ISO: 50...3200) */
5288c2ecf20Sopenharmony_ci	50000, 100000, 200000, 400000, 800000, 1600000, 3200000
5298c2ecf20Sopenharmony_ci};
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci/* Supported Exposure Bias values, -2.0EV...+2.0EV */
5328c2ecf20Sopenharmony_cistatic const s64 ev_bias_qmenu[] = {
5338c2ecf20Sopenharmony_ci	/* AE_INDEX: 0x00...0x08 */
5348c2ecf20Sopenharmony_ci	-2000, -1500, -1000, -500, 0, 500, 1000, 1500, 2000
5358c2ecf20Sopenharmony_ci};
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ciint m5mols_init_controls(struct v4l2_subdev *sd)
5388c2ecf20Sopenharmony_ci{
5398c2ecf20Sopenharmony_ci	struct m5mols_info *info = to_m5mols(sd);
5408c2ecf20Sopenharmony_ci	u16 exposure_max;
5418c2ecf20Sopenharmony_ci	u16 zoom_step;
5428c2ecf20Sopenharmony_ci	int ret;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	/* Determine the firmware dependent control range and step values */
5458c2ecf20Sopenharmony_ci	ret = m5mols_read_u16(sd, AE_MAX_GAIN_MON, &exposure_max);
5468c2ecf20Sopenharmony_ci	if (ret < 0)
5478c2ecf20Sopenharmony_ci		return ret;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	zoom_step = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1;
5508c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_init(&info->handle, 20);
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	info->auto_wb = v4l2_ctrl_new_std_menu(&info->handle,
5538c2ecf20Sopenharmony_ci			&m5mols_ctrl_ops, V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE,
5548c2ecf20Sopenharmony_ci			9, ~0x3fe, V4L2_WHITE_BALANCE_AUTO);
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	/* Exposure control cluster */
5578c2ecf20Sopenharmony_ci	info->auto_exposure = v4l2_ctrl_new_std_menu(&info->handle,
5588c2ecf20Sopenharmony_ci			&m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO,
5598c2ecf20Sopenharmony_ci			1, ~0x03, V4L2_EXPOSURE_AUTO);
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	info->exposure = v4l2_ctrl_new_std(&info->handle,
5628c2ecf20Sopenharmony_ci			&m5mols_ctrl_ops, V4L2_CID_EXPOSURE,
5638c2ecf20Sopenharmony_ci			0, exposure_max, 1, exposure_max / 2);
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	info->exposure_bias = v4l2_ctrl_new_int_menu(&info->handle,
5668c2ecf20Sopenharmony_ci			&m5mols_ctrl_ops, V4L2_CID_AUTO_EXPOSURE_BIAS,
5678c2ecf20Sopenharmony_ci			ARRAY_SIZE(ev_bias_qmenu) - 1,
5688c2ecf20Sopenharmony_ci			ARRAY_SIZE(ev_bias_qmenu)/2 - 1,
5698c2ecf20Sopenharmony_ci			ev_bias_qmenu);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	info->metering = v4l2_ctrl_new_std_menu(&info->handle,
5728c2ecf20Sopenharmony_ci			&m5mols_ctrl_ops, V4L2_CID_EXPOSURE_METERING,
5738c2ecf20Sopenharmony_ci			2, ~0x7, V4L2_EXPOSURE_METERING_AVERAGE);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	/* ISO control cluster */
5768c2ecf20Sopenharmony_ci	info->auto_iso = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops,
5778c2ecf20Sopenharmony_ci			V4L2_CID_ISO_SENSITIVITY_AUTO, 1, ~0x03, 1);
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	info->iso = v4l2_ctrl_new_int_menu(&info->handle, &m5mols_ctrl_ops,
5808c2ecf20Sopenharmony_ci			V4L2_CID_ISO_SENSITIVITY, ARRAY_SIZE(iso_qmenu) - 1,
5818c2ecf20Sopenharmony_ci			ARRAY_SIZE(iso_qmenu)/2 - 1, iso_qmenu);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	info->saturation = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops,
5848c2ecf20Sopenharmony_ci			V4L2_CID_SATURATION, 1, 5, 1, 3);
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	info->zoom = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops,
5878c2ecf20Sopenharmony_ci			V4L2_CID_ZOOM_ABSOLUTE, 1, 70, zoom_step, 1);
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	info->colorfx = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops,
5908c2ecf20Sopenharmony_ci			V4L2_CID_COLORFX, 4, 0, V4L2_COLORFX_NONE);
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	info->wdr = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops,
5938c2ecf20Sopenharmony_ci			V4L2_CID_WIDE_DYNAMIC_RANGE, 0, 1, 1, 0);
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	info->stabilization = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops,
5968c2ecf20Sopenharmony_ci			V4L2_CID_IMAGE_STABILIZATION, 0, 1, 1, 0);
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	info->jpeg_quality = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops,
5998c2ecf20Sopenharmony_ci			V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, 100, 1, 80);
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	info->lock_3a = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops,
6028c2ecf20Sopenharmony_ci			V4L2_CID_3A_LOCK, 0, 0x7, 0, 0);
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	if (info->handle.error) {
6058c2ecf20Sopenharmony_ci		int ret = info->handle.error;
6068c2ecf20Sopenharmony_ci		v4l2_err(sd, "Failed to initialize controls: %d\n", ret);
6078c2ecf20Sopenharmony_ci		v4l2_ctrl_handler_free(&info->handle);
6088c2ecf20Sopenharmony_ci		return ret;
6098c2ecf20Sopenharmony_ci	}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	v4l2_ctrl_auto_cluster(4, &info->auto_exposure, 1, false);
6128c2ecf20Sopenharmony_ci	info->auto_iso->flags |= V4L2_CTRL_FLAG_VOLATILE |
6138c2ecf20Sopenharmony_ci				V4L2_CTRL_FLAG_UPDATE;
6148c2ecf20Sopenharmony_ci	v4l2_ctrl_auto_cluster(2, &info->auto_iso, 0, false);
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	info->lock_3a->flags |= V4L2_CTRL_FLAG_VOLATILE;
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	m5mols_set_ctrl_mode(info->auto_exposure, REG_PARAMETER);
6198c2ecf20Sopenharmony_ci	m5mols_set_ctrl_mode(info->auto_wb, REG_PARAMETER);
6208c2ecf20Sopenharmony_ci	m5mols_set_ctrl_mode(info->colorfx, REG_MONITOR);
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	sd->ctrl_handler = &info->handle;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	return 0;
6258c2ecf20Sopenharmony_ci}
626