// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2019 MediaTek Inc.

#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
#include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/thermal.h>

#include "kd_imgsensor_define_v4l2.h"

#include "adaptor.h"
#include "adaptor-hw.h"
#include "adaptor-i2c.h"
#include "adaptor-ctrls.h"
#include "adaptor-command.h"
#include "adaptor-fsync-ctrls.h"
#include "adaptor-ioctl.h"
#include "adaptor-trace.h"
#include "imgsensor-glue/imgsensor-glue.h"
#include "virt-sensor/virt-sensor-entry.h"

#undef E
#define E(__x__) (__x__##_entry)
#define EXTERN_IMGSENSOR_SUBDRVS extern struct subdrv_entry \
	IMGSENSOR_SUBDRVS
#ifdef IMGSENSOR_SUBDRVS
EXTERN_IMGSENSOR_SUBDRVS;
#endif

#undef E
#define E(__x__) (&__x__##_entry)
static struct subdrv_entry *imgsensor_subdrvs[] = {
#ifdef IMGSENSOR_SUBDRVS
	IMGSENSOR_SUBDRVS
#endif
};

module_param(sensor_debug, uint, 0644);
MODULE_PARM_DESC(sensor_debug, "imgsensor_debug");

static int get_outfmt_code(struct adaptor_ctx *ctx)
{
	int outfmt = ctx->sensor_info.SensorOutputDataFormat;

	switch (outfmt) {
	case SENSOR_OUTPUT_FORMAT_RAW_B:
		return MEDIA_BUS_FMT_SBGGR10_1X10;
	case SENSOR_OUTPUT_FORMAT_RAW_Gb:
		return MEDIA_BUS_FMT_SGBRG10_1X10;
	case SENSOR_OUTPUT_FORMAT_RAW_Gr:
		return MEDIA_BUS_FMT_SGRBG10_1X10;
	case SENSOR_OUTPUT_FORMAT_RAW_R:
		return MEDIA_BUS_FMT_SRGGB10_1X10;

	case SENSOR_OUTPUT_FORMAT_RAW_4CELL_B:
	case SENSOR_OUTPUT_FORMAT_RAW_4CELL_BAYER_B:
	case SENSOR_OUTPUT_FORMAT_RAW_4CELL_HW_BAYER_B:
		pr_warn("unsupported 4cell output_format %d\n", outfmt);
		return MEDIA_BUS_FMT_SBGGR10_1X10;

	case SENSOR_OUTPUT_FORMAT_RAW_4CELL_Gb:
	case SENSOR_OUTPUT_FORMAT_RAW_4CELL_BAYER_Gb:
	case SENSOR_OUTPUT_FORMAT_RAW_4CELL_HW_BAYER_Gb:
		pr_warn("unsupported 4cell output_format %d\n", outfmt);
		return MEDIA_BUS_FMT_SGBRG10_1X10;

	case SENSOR_OUTPUT_FORMAT_RAW_4CELL_Gr:
	case SENSOR_OUTPUT_FORMAT_RAW_4CELL_BAYER_Gr:
	case SENSOR_OUTPUT_FORMAT_RAW_4CELL_HW_BAYER_Gr:
		pr_warn("unsupported 4cell output_format %d\n", outfmt);
		return MEDIA_BUS_FMT_SGRBG10_1X10;

	case SENSOR_OUTPUT_FORMAT_RAW_4CELL_R:
	case SENSOR_OUTPUT_FORMAT_RAW_4CELL_BAYER_R:
	case SENSOR_OUTPUT_FORMAT_RAW_4CELL_HW_BAYER_R:
		pr_warn("unsupported 4cell output_format %d\n", outfmt);
		return MEDIA_BUS_FMT_SRGGB10_1X10;

	case SENSOR_OUTPUT_FORMAT_RAW8_MONO:
	case SENSOR_OUTPUT_FORMAT_RAW8_B:
		return MEDIA_BUS_FMT_SBGGR8_1X8;
	case SENSOR_OUTPUT_FORMAT_RAW8_Gb:
		return MEDIA_BUS_FMT_SGBRG8_1X8;
	case SENSOR_OUTPUT_FORMAT_RAW8_Gr:
		return MEDIA_BUS_FMT_SGRBG8_1X8;
	case SENSOR_OUTPUT_FORMAT_RAW8_R:
		return MEDIA_BUS_FMT_SRGGB8_1X8;

	case SENSOR_OUTPUT_FORMAT_RAW12_B:
		return MEDIA_BUS_FMT_SBGGR12_1X12;
	case SENSOR_OUTPUT_FORMAT_RAW12_Gb:
		return MEDIA_BUS_FMT_SGBRG12_1X12;
	case SENSOR_OUTPUT_FORMAT_RAW12_Gr:
		return MEDIA_BUS_FMT_SGRBG12_1X12;
	case SENSOR_OUTPUT_FORMAT_RAW12_R:
		return MEDIA_BUS_FMT_SRGGB12_1X12;

	case SENSOR_OUTPUT_FORMAT_NV12:
	case SENSOR_OUTPUT_FORMAT_NV21:
		return MEDIA_BUS_FMT_SBGGR8_1X8;
	case SENSOR_OUTPUT_FORMAT_YUV_P010:
	case SENSOR_OUTPUT_FORMAT_YVU_P010:
		return MEDIA_BUS_FMT_SBGGR10_1X10;
	}

	pr_warn("unknown output format %d\n", outfmt);

	return MEDIA_BUS_FMT_SBGGR10_1X10;
}

static void add_sensor_mode(struct adaptor_ctx *ctx,
		int id, int width, int height)
{
	union feature_para para;
	u32 idx, val, len, llp_readout;
	struct sensor_mode *mode;

	idx = ctx->mode_cnt;

	if (idx >= MODE_MAXCNT) {
		dev_warn(ctx->dev, "invalid mode idx %d\n", idx);
		return;
	}

	mode = &ctx->mode[idx];
	mode->id = id;
	mode->width = width;
	mode->height = height;

	para.u64[0] = id;
	para.u64[1] = (u64)&val;

	para.u64[2] = SENSOR_GET_LINELENGTH_FOR_READOUT;
	subdrv_call(ctx, feature_control,
		SENSOR_FEATURE_GET_PERIOD_BY_SCENARIO,
		para.u8, &len);
	llp_readout = val & 0xffff;
	para.u64[2] = 0;

	val = 0;
	subdrv_call(ctx, feature_control,
		SENSOR_FEATURE_GET_PERIOD_BY_SCENARIO,
		para.u8, &len);

	mode->llp = val & 0xffff;
	mode->fll = val >> 16;

	if (!mode->llp || !mode->fll)
		return;

	val = 0;
	subdrv_call(ctx, feature_control,
		SENSOR_FEATURE_GET_MIPI_PIXEL_RATE,
		para.u8, &len);

	mode->mipi_pixel_rate = val;

	val = 0;
	subdrv_call(ctx, feature_control,
		SENSOR_FEATURE_GET_CUST_PIXEL_RATE,
		para.u8, &len);

	mode->cust_pixel_rate = val;

	val = 0;
	subdrv_call(ctx, feature_control,
		SENSOR_FEATURE_GET_DEFAULT_FRAME_RATE_BY_SCENARIO,
		para.u8, &len);

	mode->max_framerate = val;

	val = 0;
	subdrv_call(ctx, feature_control,
		SENSOR_FEATURE_GET_PIXEL_CLOCK_FREQ_BY_SCENARIO,
		para.u8, &len);

	mode->pclk = val;

	val = 0;

	subdrv_call(ctx, feature_control,
		SENSOR_FEATURE_GET_FINE_INTEG_LINE_BY_SCENARIO,
		para.u8, &len);
	mode->fine_intg_line = val;

	val = 0;
	subdrv_call(ctx, feature_control,
		SENSOR_FEATURE_ESD_RESET_BY_USER,
		para.u8, &len);

	mode->esd_reset_by_user = val;


	if (!mode->mipi_pixel_rate || !mode->max_framerate || !mode->pclk)
		return;

	subdrv_call(ctx, get_csi_param, mode->id, &mode->csi_param);

	/* update linetime_in_ns */
	mode->linetime_in_ns = (u64)mode->llp * 1000000 +
		(mode->pclk / 1000 - 1);
	do_div(mode->linetime_in_ns, mode->pclk / 1000);


	mode->linetime_in_ns_readout = (u64)llp_readout * 1000000 +
		(mode->pclk / 1000 - 1);
	do_div(mode->linetime_in_ns_readout, mode->pclk / 1000);


	dev_dbg(ctx->dev, "%s [%d] id %d %dx%d %dx%d px %d fps %d tLine %lld|%lld fintl %llu\n",
		__func__,
		idx, id, width, height,
		mode->llp, mode->fll,
		mode->mipi_pixel_rate, mode->max_framerate,
		mode->linetime_in_ns,
		mode->linetime_in_ns_readout,
		mode->fine_intg_line);

	ctx->mode_cnt++;
}

static int init_sensor_mode(struct adaptor_ctx *ctx)
{
	MSDK_SENSOR_RESOLUTION_INFO_STRUCT res;
	unsigned int i = 0;

	memset(&res, 0, sizeof(res));
	subdrv_call(ctx, get_resolution, &res);
	for (i = SENSOR_SCENARIO_ID_MIN; i < SENSOR_SCENARIO_ID_MAX; i++) {
		ctx->seamless_scenarios[i] = SENSOR_SCENARIO_ID_NONE;

		if (res.SensorWidth[i] > 0 && res.SensorHeight[i] > 0)
			add_sensor_mode(ctx,
			i,
			res.SensorWidth[i],
			res.SensorHeight[i]);
	}
	return 0;
}

static void control_sensor(struct adaptor_ctx *ctx)
{
	MSDK_SENSOR_EXPOSURE_WINDOW_STRUCT image_window;
	MSDK_SENSOR_CONFIG_STRUCT sensor_config_data;
#if IMGSENSOR_LOG_MORE
	dev_info(ctx->dev,
		"[%s]+ is_sensor_scenario_inited(%u),is_streaming(%u)\n",
		__func__, ctx->is_sensor_scenario_inited, ctx->is_streaming);
#endif
	if (!ctx->is_sensor_scenario_inited && !ctx->is_streaming) {
		subdrv_call(ctx, control,
				ctx->cur_mode->id,
				&image_window,
				&sensor_config_data);
		ctx->is_sensor_scenario_inited = 1;
	}
	restore_ae_ctrl(ctx);
#if IMGSENSOR_LOG_MORE
	dev_info(ctx->dev, "[%s]-\n", __func__);
#endif
}

static int set_sensor_mode(struct adaptor_ctx *ctx,
		struct sensor_mode *mode, char update_ctrl_defs)
{
	s64 min, max, def;

	if (ctx->cur_mode == mode) {
		if (update_ctrl_defs)
			control_sensor(ctx);
		return 0;
	}

	ctx->cur_mode = mode;
	ctx->is_sensor_scenario_inited = 0;

	subdrv_call(ctx, get_info,
			mode->id,
			&ctx->sensor_info,
			&ctx->sensor_cfg);

	if (update_ctrl_defs) {
		/* pixel rate */
		min = max = def = mode->mipi_pixel_rate;
		__v4l2_ctrl_modify_range(ctx->pixel_rate, min, max, 1, def);

		/* hblank */
		min = max = def = mode->llp - mode->width;
		__v4l2_ctrl_modify_range(ctx->hblank, min, max, 1, def);

		/* vblank */
		min = def = get_mode_vb(ctx, mode);
		max = ctx->subctx.max_frame_length - mode->height;
		__v4l2_ctrl_modify_range(ctx->vblank, min, max, 1, def);

		/* max fps */
		max = def = mode->max_framerate;
		__v4l2_ctrl_modify_range(ctx->max_fps, 1, max, 1, def);

		/* init sensor scenario setting */
		control_sensor(ctx);
	}

	dev_dbg(ctx->dev, "select %dx%d@%d %dx%d px %d\n",
		mode->width, mode->height, mode->max_framerate,
		mode->llp, mode->fll, mode->mipi_pixel_rate);

	return 0;
}

static int init_sensor_info(struct adaptor_ctx *ctx)
{
	init_sensor_mode(ctx);
	set_sensor_mode(ctx, &ctx->mode[0], 0);
	ctx->try_format_mode = &ctx->mode[0];
	ctx->fmt_code = get_outfmt_code(ctx);
	return 0;
}

static int search_sensor(struct adaptor_ctx *ctx)
{
	int ret, i, j, of_sensor_names_cnt, subdrvs_cnt;
	struct subdrv_entry **subdrvs, *subdrv;
	struct subdrv_entry *of_subdrvs[OF_SENSOR_NAMES_MAXCNT];
	const char *of_sensor_names[OF_SENSOR_NAMES_MAXCNT];
	int subdrv_name_ret;
	const char *of_subdrv_name;

	of_sensor_names_cnt = of_property_read_string_array(ctx->dev->of_node,
		"sensor-names", of_sensor_names, ARRAY_SIZE(of_sensor_names));

	subdrv_name_ret = of_property_read_string(ctx->dev->of_node,
						  "subdrv-name", &of_subdrv_name);

	dev_info(ctx->dev, "subdrv_name_ret %d\n", subdrv_name_ret);

	if (!subdrv_name_ret)
		dev_info(ctx->dev, "subdrv name %s found\n", of_subdrv_name);

	/* try to load custom list from DT */
	if (of_sensor_names_cnt > 0) {
		subdrvs = of_subdrvs;
		subdrvs_cnt = 0;
		for (i = 0; i < of_sensor_names_cnt; i++) {
			for (j = 0; j < ARRAY_SIZE(imgsensor_subdrvs); j++) {
				subdrv = imgsensor_subdrvs[j];
				if (!strcmp(subdrv->name,
					of_sensor_names[i])) {
					of_subdrvs[subdrvs_cnt++] = subdrv;
					break;
				}
			}
			if (j == ARRAY_SIZE(imgsensor_subdrvs)) {
				dev_warn(ctx->dev, "%s not found\n",
					of_sensor_names[i]);
			}
		}
	} else if (!subdrv_name_ret) {
		subdrvs_cnt = 0;
		of_subdrvs[0] = vs_query_subdrv_entry(of_subdrv_name);

		if (of_subdrvs[0]) {
			subdrvs_cnt = 1;
			subdrvs = of_subdrvs;
			dev_info(ctx->dev, "subdrv name %s found\n", of_subdrv_name);
		}
	} else {
		subdrvs = imgsensor_subdrvs;
		subdrvs_cnt = ARRAY_SIZE(imgsensor_subdrvs);
	}

	for (i = 0; i < subdrvs_cnt; i++) {
		u32 sensor_id;

		ctx->subdrv = subdrvs[i];
		ctx->subctx.i2c_client = ctx->i2c_client;
		adaptor_hw_power_on(ctx);
		subdrv_call(ctx, init_ctx, ctx->i2c_client,
				ctx->subctx.i2c_write_id);
		ret = subdrv_call(ctx, get_id, &sensor_id);
		adaptor_hw_power_off(ctx);
		if (!ret) {
			dev_info(ctx->dev, "sensor %s found\n",
				ctx->subdrv->name);
			subdrv_call(ctx, init_ctx, ctx->i2c_client,
				ctx->subctx.i2c_write_id);
			ctx->ctx_pw_seq = kmalloc_array(ctx->subdrv->pw_seq_cnt,
					sizeof(struct subdrv_pw_seq_entry),
					GFP_KERNEL);
			if (ctx->ctx_pw_seq) {
				memcpy(ctx->ctx_pw_seq, ctx->subdrv->pw_seq,
				       ctx->subdrv->pw_seq_cnt *
				       sizeof(struct subdrv_pw_seq_entry));
			}
			return 0;
		}
	}

	return -EIO;
}

static int imgsensor_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
	struct adaptor_ctx *ctx = to_ctx(sd);
	struct v4l2_mbus_framefmt *try_fmt =
		v4l2_subdev_get_try_format(sd, fh->state, 0);
#if IMGSENSOR_LOG_MORE
	dev_info(ctx->dev, "[%s]+\n", __func__);
#endif
	mutex_lock(&ctx->mutex);

	ctx->open_refcnt++;

	/* Initialize try_fmt */
	try_fmt->width = ctx->cur_mode->width;
	try_fmt->height = ctx->cur_mode->height;
	try_fmt->code = ctx->fmt_code;
	try_fmt->field = V4L2_FIELD_NONE;

#ifdef POWERON_ONCE_OPENED
#ifdef IMGSENSOR_USE_PM_FRAMEWORK
	dev_info(ctx->dev, "%s use IMGSENSOR_USE_PM_FRAMEWORK\n", __func__);
	pm_runtime_get_sync(ctx->dev);
#else
	dev_info(ctx->dev, "%s use self ref cnt\n", __func__);
	adaptor_hw_power_on(ctx);
#endif
	adaptor_sensor_init(ctx);
#endif

	mutex_unlock(&ctx->mutex);
#if IMGSENSOR_LOG_MORE
	dev_info(ctx->dev, "[%s]-\n", __func__);
#endif
	return 0;
}

static int imgsensor_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
	struct adaptor_ctx *ctx = to_ctx(sd);
	int i;
#if IMGSENSOR_LOG_MORE
	dev_info(ctx->dev, "[%s]+\n", __func__);
#endif
	mutex_lock(&ctx->mutex);

#ifdef POWERON_ONCE_OPENED
#ifdef IMGSENSOR_USE_PM_FRAMEWORK
	dev_info(ctx->dev, "%s use IMGSENSOR_USE_PM_FRAMEWORK\n", __func__);
	pm_runtime_put(ctx->dev);
#else
	dev_info(ctx->dev, "%s use self ref cnt\n", __func__);
	adaptor_hw_power_off(ctx);
#endif
#endif

	ctx->open_refcnt--;
	if (ctx->open_refcnt <= 0) {
		for (i = 0; ctx->power_refcnt; i++)
			adaptor_hw_power_off(ctx);
		ctx->open_refcnt = 0;
	}

	mutex_unlock(&ctx->mutex);
#if IMGSENSOR_LOG_MORE
	dev_info(ctx->dev, "[%s]-\n", __func__);
#endif
	return 0;
}

static int imgsensor_enum_mbus_code(struct v4l2_subdev *sd,
				  struct v4l2_subdev_state *state,
				  struct v4l2_subdev_mbus_code_enum *code)
{
	struct adaptor_ctx *ctx = to_ctx(sd);

	if (code->index >= ctx->mode_cnt)
		return -EINVAL;

	code->code = to_mtk_ext_fmt_code(ctx->fmt_code, ctx->mode[code->index].id);
	// dev_info(ctx->dev, "enum mbus fmt code = 0x%x\n", code->code);

	return 0;
}

static int imgsensor_enum_frame_size(struct v4l2_subdev *sd,
				  struct v4l2_subdev_state *state,
				  struct v4l2_subdev_frame_size_enum *fse)
{
	struct adaptor_ctx *ctx = to_ctx(sd);

	if (fse->index >= ctx->mode_cnt)
		return -EINVAL;

	fse->min_width = ctx->mode[fse->index].width;
	fse->max_width = fse->min_width;
	fse->min_height = ctx->mode[fse->index].height;
	fse->max_height = fse->min_height;

	return 0;
}

static int imgsensor_enum_frame_interval(struct v4l2_subdev *sd,
		struct v4l2_subdev_state *state,
		struct v4l2_subdev_frame_interval_enum *fie)
{
	struct adaptor_ctx *ctx = to_ctx(sd);
	struct sensor_mode *mode;
	u32 i, index = fie->index;

	for (i = 0; i < ctx->mode_cnt; i++) {
		mode = &ctx->mode[i];
		if (fie->width != mode->width ||
			fie->height != mode->height)
			continue;
		if (index-- == 0) {
			fie->interval.numerator = 10;
			fie->interval.denominator = mode->max_framerate;
			return 0;
		}
	}

	return -EINVAL;
}

static int imgsensor_get_selection(struct v4l2_subdev *sd,
		struct v4l2_subdev_state *state,
		struct v4l2_subdev_selection *sel)
{
	struct adaptor_ctx *ctx = to_ctx(sd);

	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
		return -EINVAL;

	switch (sel->target) {
	case V4L2_SEL_TGT_CROP_BOUNDS:
	case V4L2_SEL_TGT_CROP_DEFAULT:
	case V4L2_SEL_TGT_CROP:
		sel->r.left = 0;
		sel->r.top = 0;
		sel->r.width = ctx->cur_mode->width;
		sel->r.height = ctx->cur_mode->height;
		return 0;
	default:
		return -EINVAL;
	}
}

static void update_pad_format(struct adaptor_ctx *ctx,
		const struct sensor_mode *mode,
		struct v4l2_subdev_format *fmt)
{
	fmt->format.width = mode->width;
	fmt->format.height = mode->height;
	fmt->format.code = to_mtk_ext_fmt_code(ctx->fmt_code, mode->id);
	fmt->format.field = V4L2_FIELD_NONE;
}

static int __imgsensor_get_pad_format(struct adaptor_ctx *ctx,
				   struct v4l2_subdev_state *state,
				   struct v4l2_subdev_format *fmt)
{
	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
		fmt->format = *v4l2_subdev_get_try_format(&ctx->sd, state,
							  fmt->pad);
	else
		update_pad_format(ctx, ctx->cur_mode, fmt);

	return 0;
}

static int imgsensor_get_pad_format(struct v4l2_subdev *sd,
				 struct v4l2_subdev_state *state,
				 struct v4l2_subdev_format *fmt)
{
	struct adaptor_ctx *ctx = to_ctx(sd);
	int ret;

	mutex_lock(&ctx->mutex);
	ret = __imgsensor_get_pad_format(ctx, state, fmt);
	mutex_unlock(&ctx->mutex);

	return ret;
}

static int imgsensor_set_pad_format(struct v4l2_subdev *sd,
				 struct v4l2_subdev_state *state,
				 struct v4l2_subdev_format *fmt)
{
	struct adaptor_ctx *ctx = to_ctx(sd);
	struct sensor_mode *mode;
	struct v4l2_mbus_framefmt *framefmt;
	int sensor_mode_id = 0;

	mutex_lock(&ctx->mutex);

	/* Only one raw bayer order is supported */
	set_std_parts_fmt_code(fmt->format.code, ctx->fmt_code);


	/* Returns the best match or NULL if the Length of the array is zero */
	mode = v4l2_find_nearest_size(ctx->mode,
		ctx->mode_cnt, width, height,
		fmt->format.width, fmt->format.height);

	/* Override mode when fmt code specified sensor mode */
	if (is_mtk_ext_fmt_code(fmt->format.code)) {
		sensor_mode_id = get_sensor_mode_from_fmt_code(fmt->format.code);
		if (sensor_mode_id >= 0 && sensor_mode_id < ctx->mode_cnt)
			mode = &ctx->mode[sensor_mode_id];
	}

	if (mode == NULL) {
		dev_info(ctx->dev,
			"set fmt code = 0x%x, which %d ctx->mode_cnt = %d\n",
			fmt->format.code, fmt->which, ctx->mode_cnt);

		mutex_unlock(&ctx->mutex);
		return -EINVAL;
	}

	update_pad_format(ctx, mode, fmt);
	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
		framefmt = v4l2_subdev_get_try_format(sd, state, fmt->pad);
		*framefmt = fmt->format;

		ctx->try_format_mode = mode;
	} else {
		dev_info(ctx->dev,
			"set fmt code = 0x%x, which %d sensor_mode_id = %u\n",
			fmt->format.code, fmt->which, mode->id);

#ifndef POWERON_ONCE_OPENED
		ADAPTOR_SYSTRACE_BEGIN("imgsensor::init_sensor");
		adaptor_sensor_init(ctx);
		ADAPTOR_SYSTRACE_END();
#endif
		ADAPTOR_SYSTRACE_BEGIN("imgsensor::set_mode_%u", mode->id);
		set_sensor_mode(ctx, mode, 1);
		ADAPTOR_SYSTRACE_END();
	}
	mutex_unlock(&ctx->mutex);

	return 0;
}

#ifdef IMGSENSOR_USE_PM_FRAMEWORK
static int imgsensor_runtime_resume(struct device *dev)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct v4l2_subdev *sd = i2c_get_clientdata(client);
	struct adaptor_ctx *ctx = to_ctx(sd);

	return adaptor_hw_power_on(ctx);
}

static int imgsensor_runtime_suspend(struct device *dev)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct v4l2_subdev *sd = i2c_get_clientdata(client);
	struct adaptor_ctx *ctx = to_ctx(sd);

	/* clear flags once power-off */
	ctx->is_sensor_inited = 0;
	ctx->is_sensor_scenario_inited = 0;

	return adaptor_hw_power_off(ctx);
}
#endif

static int imgsensor_set_power(struct v4l2_subdev *sd, int on)
{
	struct adaptor_ctx *ctx = to_ctx(sd);
	int ret;

	mutex_lock(&ctx->mutex);
	if (on)
#ifdef IMGSENSOR_USE_PM_FRAMEWORK
		ret = pm_runtime_get_sync(ctx->dev);
#else
	ret = adaptor_hw_power_on(ctx);
#endif
	else
#ifdef IMGSENSOR_USE_PM_FRAMEWORK
		ret = pm_runtime_put(ctx->dev);
#else
	ret = adaptor_hw_power_off(ctx);
#endif
	mutex_unlock(&ctx->mutex);

	return ret;
}

/* Start streaming */
static int imgsensor_start_streaming(struct adaptor_ctx *ctx)
{
//	int ret;
	u64 data[4];
	u32 len;
	union feature_para para;
#if IMGSENSOR_LOG_MORE
	dev_info(ctx->dev, "[%s]+\n", __func__);
#endif
	adaptor_sensor_init(ctx);

	control_sensor(ctx);

#ifdef APPLY_CUSTOMIZED_VALUES_FROM_USER
	/* Apply customized values from user */
	ret =  __v4l2_ctrl_handler_setup(ctx->sd.ctrl_handler);
	if (ret)
		dev_info(ctx->dev, "failed to apply customized values\n");
#endif

	data[0] = 0; // shutter
	para.u8[0] = 0;
#if IMGSENSOR_LOG_MORE
	dev_info(ctx->dev, "[%s] [SENSOR_FEATURE_SET_TEST_PATTERN]+\n", __func__);
#endif
	//Make sure close test pattern
	subdrv_call(ctx, feature_control,
		SENSOR_FEATURE_SET_TEST_PATTERN,
		para.u8, &len);
#if IMGSENSOR_LOG_MORE
	dev_info(ctx->dev, "[%s] [SENSOR_FEATURE_SET_TEST_PATTERN]-\n", __func__);
#endif
	subdrv_call(ctx, feature_control,
		SENSOR_FEATURE_SET_STREAMING_RESUME,
		(u8 *)data, &len);
#if IMGSENSOR_LOG_MORE
	dev_info(ctx->dev, "[%s] [SENSOR_FEATURE_SET_STREAMING_RESUME]-\n", __func__);
#endif

	/* update timeout value upon streaming on */
	if (ctx->ae_memento.exposure.le_exposure) {
		ctx->shutter_for_timeout = ctx->ae_memento.exposure.le_exposure;
		if (ctx->cur_mode->fine_intg_line)
			ctx->shutter_for_timeout /= 1000;
	}

	/* notify frame-sync streaming ON */
	notify_fsync_mgr_streaming(ctx, 1);
#if IMGSENSOR_LOG_MORE
	dev_info(ctx->dev, "[%s]-\n", __func__);
#endif
	return 0;
}

/* Stop streaming */
static int imgsensor_stop_streaming(struct adaptor_ctx *ctx)
{
	u64 data[4];
	u32 len;

	subdrv_call(ctx, feature_control,
		SENSOR_FEATURE_SET_STREAMING_SUSPEND,
		(u8 *)data, &len);

	/* notify frame-sync streaming OFF */
	notify_fsync_mgr_streaming(ctx, 0);

	memset(&ctx->ae_memento, 0, sizeof(ctx->ae_memento));

	return 0;
}

static int imgsensor_get_frame_interval(struct v4l2_subdev *sd,
		struct v4l2_subdev_frame_interval *fi)
{
	struct adaptor_ctx *ctx = to_ctx(sd);

	mutex_lock(&ctx->mutex);
	fi->interval.numerator = 10;

	if (fi->reserved[0] == V4L2_SUBDEV_FORMAT_TRY)
		fi->interval.denominator = ctx->try_format_mode->max_framerate;
	else
		fi->interval.denominator = ctx->cur_mode->max_framerate;

	mutex_unlock(&ctx->mutex);

	return 0;
}

static int imgsensor_set_frame_interval(struct v4l2_subdev *sd,
		struct v4l2_subdev_frame_interval *fi)
{
	struct adaptor_ctx *ctx = to_ctx(sd);

	dev_info(ctx->dev, "set_frame_interval = %u\n", fi->interval.denominator);

	return imgsensor_get_frame_interval(sd, fi);
}

static int imgsensor_set_stream(struct v4l2_subdev *sd, int enable)
{
	int ret;
	struct adaptor_ctx *ctx = to_ctx(sd);

	mutex_lock(&ctx->mutex);
	if (ctx->is_streaming == enable) {
#if IMGSENSOR_LOG_MORE
		dev_info(ctx->dev,
			"[%s]+ is_streaming(%d)\n",
			__func__, ctx->is_streaming);
#endif
		mutex_unlock(&ctx->mutex);
		return 0;
	}

	if (enable) {
#ifdef IMGSENSOR_USE_PM_FRAMEWORK
		ret = pm_runtime_get_sync(ctx->dev);
		if (ret < 0) {
			pm_runtime_put_noidle(ctx->dev);
			goto err_unlock;
		}
#endif

		/*
		 * Apply default & customized values
		 * and then start streaming.
		 */
		ret = imgsensor_start_streaming(ctx);
		if (ret)
			goto err_rpm_put;
	} else {
		imgsensor_stop_streaming(ctx);
#ifdef IMGSENSOR_USE_PM_FRAMEWORK
		pm_runtime_put(ctx->dev);
#endif
	}

	ctx->is_streaming = enable;
	mutex_unlock(&ctx->mutex);

	adaptor_logi(ctx, "en %d\n", enable);

	return 0;

err_rpm_put:
#ifdef IMGSENSOR_USE_PM_FRAMEWORK
	pm_runtime_put(ctx->dev);
err_unlock:
#endif
	mutex_unlock(&ctx->mutex);

	return ret;
}

static int imgsensor_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad,
		struct v4l2_mbus_config *config)
{
	struct adaptor_ctx *ctx = to_ctx(sd);

	config->type = ctx->sensor_info.MIPIsensorType == MIPI_CPHY ?
		V4L2_MBUS_CSI2_CPHY : V4L2_MBUS_CSI2_DPHY;

	switch (ctx->sensor_info.SensorMIPILaneNumber) {
	case SENSOR_MIPI_1_LANE:
		config->bus.mipi_csi2.num_data_lanes = 1;
		break;
	case SENSOR_MIPI_2_LANE:
		config->bus.mipi_csi2.num_data_lanes = 2;
		break;
	case SENSOR_MIPI_3_LANE:
		config->bus.mipi_csi2.num_data_lanes = 3;
		break;
	case SENSOR_MIPI_4_LANE:
		config->bus.mipi_csi2.num_data_lanes = 4;
		break;
	}

	return 0;
}

//def IMGSENSOR_VC_ROUTING
// static int imgsensor_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
		// struct mtk_mbus_frame_desc *fd)
// {
	// int ret;
	// struct adaptor_ctx *ctx = to_ctx(sd);
	// u64 desc_visited = 0x0;
	// int write_to = 0, i = -1, j = 0;

	// while (i < SENSOR_SCENARIO_ID_MAX) {
		// struct mtk_mbus_frame_desc fd_tmp;
		// u32 scenario_id = (-1 == i) ? ctx->cur_mode->id : ctx->seamless_scenarios[i];

		// if (scenario_id == SENSOR_SCENARIO_ID_NONE)
			// break;

		// ret = subdrv_call(ctx, get_frame_desc, scenario_id, &fd_tmp);

		// if (!ret) {
			// for (j = 0;
				// write_to < V4L2_FRAME_DESC_ENTRY_MAX && j < fd_tmp.num_entries;
				// ++j) {
				// if (desc_visited &
					// (0x1 << fd_tmp.entry[j].bus.csi2.user_data_desc))
					// continue;

				// dev_info(ctx->dev, "[%s] scenario %u desc %d\n", __func__,
						// scenario_id,
						// fd_tmp.entry[j].bus.csi2.user_data_desc);
				// memcpy(&fd->entry[write_to++], &fd_tmp.entry[j],
					//    sizeof(struct mtk_mbus_frame_desc_entry));

				// desc_visited |= (0x1 << fd_tmp.entry[j].bus.csi2.user_data_desc);
			// }
		// }

		// ++i;
	// }

	// fd->num_entries = write_to;
	// fd->type = MTK_MBUS_FRAME_DESC_TYPE_CSI2;

	// return ret;
// }

// #define IS_3HDR(udesc) (udesc == VC_3HDR_Y \
		// || udesc == VC_3HDR_AE \
		// || udesc == VC_3HDR_FLICKER)

// static int imgsensor_set_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
		// struct mtk_mbus_frame_desc *fd)
// {
	// return 0;

// }


#ifdef IMGSENSOR_USE_PM_FRAMEWORK
static int __maybe_unused imgsensor_suspend(struct device *dev)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct v4l2_subdev *sd = i2c_get_clientdata(client);
	struct adaptor_ctx *ctx = to_ctx(sd);

	if (pm_runtime_suspended(dev))
		return 0;

	if (ctx->is_streaming)
		imgsensor_stop_streaming(ctx);

	return imgsensor_runtime_suspend(dev);
}

static int __maybe_unused imgsensor_resume(struct device *dev)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct v4l2_subdev *sd = i2c_get_clientdata(client);
	struct adaptor_ctx *ctx = to_ctx(sd);
	int ret;

	if (pm_runtime_suspended(dev))
		return 0;

	ret = imgsensor_runtime_resume(dev);
	if (ret)
		return ret;

	if (ctx->is_streaming) {
		ret = imgsensor_start_streaming(ctx);
		if (ret)
			goto error;
	}

	return 0;

error:
	imgsensor_stop_streaming(ctx);
	ctx->is_streaming = 0;
	return ret;
}
#endif

static const struct v4l2_subdev_core_ops imgsensor_core_ops = {
	.s_power = imgsensor_set_power,
	.ioctl = adaptor_ioctl,
	.command = adaptor_command,
};

static const struct v4l2_subdev_video_ops imgsensor_video_ops = {
	.g_frame_interval = imgsensor_get_frame_interval,
	.s_frame_interval = imgsensor_set_frame_interval,
	.s_stream = imgsensor_set_stream,
};

static const struct v4l2_subdev_pad_ops imgsensor_pad_ops = {
	.enum_mbus_code = imgsensor_enum_mbus_code,
	.get_fmt = imgsensor_get_pad_format,
	.set_fmt = imgsensor_set_pad_format,
	.enum_frame_size = imgsensor_enum_frame_size,
	.enum_frame_interval = imgsensor_enum_frame_interval,
	.get_selection = imgsensor_get_selection,
	.get_mbus_config = imgsensor_g_mbus_config,
#ifdef IMGSENSOR_VC_ROUTING
	//.get_frame_desc = imgsensor_get_frame_desc,
	//.set_frame_desc = imgsensor_set_frame_desc,
#endif
};

static const struct v4l2_subdev_ops imgsensor_subdev_ops = {
	.core = &imgsensor_core_ops,
	.video = &imgsensor_video_ops,
	.pad = &imgsensor_pad_ops,
};

static const struct v4l2_subdev_internal_ops imgsensor_internal_ops = {
	.open = imgsensor_open,
	.close = imgsensor_close,
};

static int imgsensor_get_temp(struct thermal_zone_device *tz, int *temperature)
{
	struct adaptor_ctx *ctx = tz->devdata;

#ifdef IMGSENSOR_USE_PM_FRAMEWORK
	if (pm_runtime_get_if_in_use(ctx->dev) == 0) {
		*temperature = THERMAL_TEMP_INVALID;
		return 0;
	}
#else
	*temperature = 0;
#endif
	if (ctx->is_streaming)
		subdrv_call(ctx, get_temp, temperature);
	else
		*temperature = THERMAL_TEMP_INVALID;

#ifdef IMGSENSOR_USE_PM_FRAMEWORK
	pm_runtime_put(ctx->dev);
#endif

	return 0;
}

static const struct thermal_zone_device_ops imgsensor_tz_ops = {
	.get_temp = imgsensor_get_temp,
};

#define SHOW(buf, len, fmt, ...) { \
	len += snprintf(buf + len, PAGE_SIZE - len, fmt, ##__VA_ARGS__); \
}

static ssize_t debug_i2c_ops_show(struct device *dev,
			   struct device_attribute *attr, char *buf)
{
	int len = 0;
	struct adaptor_ctx *ctx = to_ctx(dev_get_drvdata(dev));


	SHOW(buf, len, "%s i2c read 0x%08x = 0x%08x\n",
			ctx->subdrv->name,
			ctx->sensorReg.RegAddr,
			ctx->sensorReg.RegData);
	return len;
}

enum DBG_ARG_IDX {
	DBG_ARG_IDX_I2C_ADDR,
	DBG_ARG_IDX_I2C_DATA,
	DBG_ARG_IDX_MAX_NUM,
};

static ssize_t debug_i2c_ops_store(struct device *dev,
			   struct device_attribute *attr,
			   const char *buf, size_t count)
{
	char delim[] = " ";
	char *token = NULL;
	char *sbuf = kzalloc(sizeof(char) * (count + 1), GFP_KERNEL);
	char *s = sbuf;
	int ret;
	unsigned int num_para = 0;
	char *arg[DBG_ARG_IDX_MAX_NUM];
	struct adaptor_ctx *ctx = to_ctx(dev_get_drvdata(dev));
	u32 val;
	u32 reg;

	ctx->sensorReg.RegAddr = 0;
	ctx->sensorReg.RegData = 0;

	if (!sbuf)
		goto ERR_DEBUG_OPS_STORE;

	memcpy(sbuf, buf, count);

	token = strsep(&s, delim);
	while (token != NULL && num_para < DBG_ARG_IDX_MAX_NUM) {
		if (strlen(token)) {
			arg[num_para] = token;
			num_para++;
		}

		token = strsep(&s, delim);
	}

	if (num_para > DBG_ARG_IDX_MAX_NUM) {
		dev_info(dev, "Wrong command parameter number %u\n", num_para);
		goto ERR_DEBUG_OPS_STORE;
	}
	ret = kstrtouint(arg[DBG_ARG_IDX_I2C_ADDR], 0, &reg);
	if (ret)
		goto ERR_DEBUG_OPS_STORE;
	ctx->sensorReg.RegAddr = reg;

	if (num_para == DBG_ARG_IDX_MAX_NUM) {
		ret = kstrtouint(arg[DBG_ARG_IDX_I2C_DATA], 0, &val);
		if (ret)
			goto ERR_DEBUG_OPS_STORE;
		ctx->sensorReg.RegData = val;

		ret = subdrv_call(ctx, feature_control, SENSOR_FEATURE_SET_REGISTER,
					(MUINT8 *) &ctx->sensorReg,
					(MUINT32 *) sizeof(MSDK_SENSOR_REG_INFO_STRUCT));
		dev_info(dev, "%s i2c write 0x%08x = 0x%08x ret = %d\n",
			__func__,
			ctx->sensorReg.RegAddr, ctx->sensorReg.RegData, ret);
	}

	ret = subdrv_call(ctx, feature_control, SENSOR_FEATURE_GET_REGISTER,
				(MUINT8 *) &ctx->sensorReg,
				(MUINT32 *) sizeof(MSDK_SENSOR_REG_INFO_STRUCT));
		dev_info(dev, "%s i2c read 0x%08x = 0x%08x  ret = %d\n",
			__func__,
			ctx->sensorReg.RegAddr, ctx->sensorReg.RegData, ret);


ERR_DEBUG_OPS_STORE:

	kfree(sbuf);
	dev_dbg(dev, "exit %s\n", __func__);

	return count;
}


static DEVICE_ATTR_RW(debug_i2c_ops);


static const char * const hw_id_names[] = {
	HW_ID_NAMES
};

static ssize_t debug_pwr_ops_show(struct device *dev,
			   struct device_attribute *attr, char *buf)
{
	int len = 0;
	struct adaptor_ctx *ctx = to_ctx(dev_get_drvdata(dev));
	int i;
	const struct subdrv_pw_seq_entry *ent;

	SHOW(buf, len, "sensor%d name %s\n", ctx->idx, ctx->subdrv->name);
	for (i = 0; i < ctx->subdrv->pw_seq_cnt; i++) {
		if (ctx->ctx_pw_seq)
			ent = &ctx->ctx_pw_seq[i]; // use ctx pw seq
		else
			ent = &ctx->subdrv->pw_seq[i];
		SHOW(buf, len, "\t%s power %d with delay %d\n",
		     hw_id_names[(unsigned int)ent->id], ent->val, ent->delay);
	}

	return len;
}

enum DBG_PWR_ARG_IDX {
	DBG_PWR_ARG_IDX_SEQ_IDX,
	DBG_PWR_ARG_IDX_SEQ_VAL,
	DBG_PWR_ARG_IDX_SEQ_DELAY,
	DBG_PWR_ARG_IDX_MAX_NUM,
};

static ssize_t debug_pwr_ops_store(struct device *dev,
			   struct device_attribute *attr,
			   const char *buf, size_t count)
{
	char delim[] = " ";
	char *token = NULL;
	char *sbuf = kzalloc(sizeof(char) * (count + 1), GFP_KERNEL);
	char *s = sbuf;
	unsigned int num_para = 0;
	char *arg[DBG_PWR_ARG_IDX_MAX_NUM];
	struct adaptor_ctx *ctx = to_ctx(dev_get_drvdata(dev));
	struct subdrv_pw_seq_entry *ent;
	unsigned int seq_idx, seq_val, seq_delay;
	int ret;

	if (!sbuf)
		goto ERR_DEBUG_PWR_OPS_STORE;

	if (!ctx->ctx_pw_seq) {
		dev_info(dev, "ctx pw seq is null\n");
		goto ERR_DEBUG_PWR_OPS_STORE;
	}

	memcpy(sbuf, buf, count);

	token = strsep(&s, delim);
	while (token != NULL && num_para < DBG_PWR_ARG_IDX_MAX_NUM) {
		if (strlen(token)) {
			arg[num_para] = token;
			num_para++;
		}

		token = strsep(&s, delim);
	}

	if (num_para > DBG_PWR_ARG_IDX_MAX_NUM) {
		dev_info(dev, "Wrong command parameter number %u\n", num_para);
		goto ERR_DEBUG_PWR_OPS_STORE;
	}
	ret = kstrtouint(arg[DBG_PWR_ARG_IDX_SEQ_IDX], 0, &seq_idx);
	if (ret)
		goto ERR_DEBUG_PWR_OPS_STORE;
	ret = kstrtouint(arg[DBG_PWR_ARG_IDX_SEQ_VAL], 0, &seq_val);
	if (ret)
		goto ERR_DEBUG_PWR_OPS_STORE;
	ret = kstrtouint(arg[DBG_PWR_ARG_IDX_SEQ_DELAY], 0, &seq_delay);
	if (ret)
		goto ERR_DEBUG_PWR_OPS_STORE;

	if (seq_idx < ctx->subdrv->pw_seq_cnt) {
		ent = &ctx->ctx_pw_seq[seq_idx];
		ent->val = seq_val;
		ent->delay = seq_delay;
	}

ERR_DEBUG_PWR_OPS_STORE:

	kfree(sbuf);

	return count;
}

static DEVICE_ATTR_RW(debug_pwr_ops);

static ssize_t debug_sensor_mode_ops_show(struct device *dev,
			   struct device_attribute *attr, char *buf)
{
	int len = 0;
	struct adaptor_ctx *ctx = to_ctx(dev_get_drvdata(dev));

	SHOW(buf, len, "(%s) sensor_mode_ops is (%d)\n",
			ctx->subdrv->name,
			ctx->subctx.sensor_mode_ops);
	return len;

}

static ssize_t debug_sensor_mode_ops_store(struct device *dev,
			   struct device_attribute *attr,
			   const char *buf, size_t count)
{
	char *sbuf = kzalloc(sizeof(char) * (count + 1), GFP_KERNEL);
	struct adaptor_ctx *ctx = to_ctx(dev_get_drvdata(dev));
	unsigned int val = 0;
	int ret;

	if (!sbuf)
		goto ERR_DEBUG_SENSOR_MODE_OPS_STORE;

	memcpy(sbuf, buf, count);

	ret = kstrtouint(sbuf, 0, &val);
	if (ret)
		goto ERR_DEBUG_SENSOR_MODE_OPS_STORE;

	switch (val) {
	case AOV_MODE_CTRL_OPS_SENSING_CTRL:
	case AOV_MODE_CTRL_OPS_MONTION_DETECTION_CTRL:
		ctx->subctx.sensor_mode_ops = val;
		break;
	case AOV_MODE_CTRL_OPS_SENSING_UT_ON_SCP:
		// true for default aov sensing mode streaming control on scp
		ctx->subctx.sensor_debug_sensing_ut_on_scp = true;
		break;
	case AOV_MODE_CTRL_OPS_SENSING_UT_ON_APMCU:
		// flase for default aov sensing mode streaming control on apmcu side
		ctx->subctx.sensor_debug_sensing_ut_on_scp = false;
		break;
	case AOV_MODE_CTRL_OPS_DPHY_GLOBAL_TIMING_CONTINUOUS_CLK:
		ctx->subctx.sensor_debug_dphy_global_timing_continuous_clk = true;
		break;
	case AOV_MODE_CTRL_OPS_DPHY_GLOBAL_TIMING_NON_CONTINUOUS_CLK:
		ctx->subctx.sensor_debug_dphy_global_timing_continuous_clk = false;
		break;
	default:
		dev_info(dev, "Wrong command parameter number (%u)\n", val);
		break;
	}

ERR_DEBUG_SENSOR_MODE_OPS_STORE:

	kfree(sbuf);

	return count;
}

static DEVICE_ATTR_RW(debug_sensor_mode_ops);

static int imgsensor_probe(struct i2c_client *client)
{
	struct device *dev = &client->dev;
	struct device_node *endpoint;
	struct adaptor_ctx *ctx;
	int i, ret;
	unsigned int reindex;
	const char *reindex_match[OF_SENSOR_NAMES_MAXCNT];
	int reindex_match_cnt;

	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
	if (!ctx)
		return -ENOMEM;

	mutex_init(&ctx->mutex);
	ctx->open_refcnt = 0;
	ctx->power_refcnt = 0;

	ctx->i2c_client = client;
	ctx->dev = dev;
	ctx->sensor_debug_flag = &sensor_debug;
	ctx->aov_pm_ops_flag = 0;
	ctx->aov_mclk_ulposc_flag = 0;

	endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
	if (!endpoint) {
		dev_err(dev, "endpoint node not found\n");
		return -EINVAL;
	}

	ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint),
					 &ctx->ep);

	of_node_put(endpoint);

	if (ret < 0) {
		dev_err(dev, "parsing endpoint node failed\n");
		return ret;
	}

	ret = adaptor_hw_init(ctx);
	if (ret) {
		dev_err(dev, "failed to init hw handles\n");
		return ret;
	}

	ret = search_sensor(ctx);
	if (ret) {
		dev_err(dev, "no sensor found\n");
		return ret;
	}

	/* read property */
	of_property_read_u32(dev->of_node, "location", &ctx->location);
	of_property_read_u32(dev->of_node, "rotation", &ctx->rotation);

	/* init sensor info */
	init_sensor_info(ctx);

	/* init subdev */
	v4l2_i2c_subdev_init(&ctx->sd, client, &imgsensor_subdev_ops);
	ctx->sd.internal_ops = &imgsensor_internal_ops;
	ctx->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
	ctx->pad.flags = MEDIA_PAD_FL_SOURCE;
	ctx->sd.dev = &client->dev;
	ctx->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;

	ret = sscanf(dev->of_node->name, OF_SENSOR_NAME_PREFIX"%d", &ctx->dts_idx);
	if (ret != 1)
		dev_warn(dev, "failed to parse %s\n", dev->of_node->name);
	ctx->idx = ctx->dts_idx;

	if (!of_property_read_u32(dev->of_node, "reindex-to", &reindex)) {
		reindex_match_cnt = of_property_read_string_array(dev->of_node,
			"reindex-match", reindex_match, ARRAY_SIZE(reindex_match));
		for (i = 0; i < reindex_match_cnt; i++) {
			if (!strncmp(reindex_match[i], ctx->subdrv->name,
				     strlen(reindex_match[i]))) {
				ctx->idx = reindex;
				dev_info(dev, "reindex to sensor%d\n", ctx->idx);
				break;
			}
		}
	}

	/* init subdev name */
	snprintf(ctx->sd.name, V4L2_SUBDEV_NAME_SIZE, "%s%d",
		OF_SENSOR_NAME_PREFIX, ctx->idx);


	/* init controls */
	ret = adaptor_init_ctrls(ctx);
	if (ret) {
		dev_err(dev, "failed to init controls\n");
		return ret;
	}

	ret = media_entity_pads_init(&ctx->sd.entity, 1, &ctx->pad);
	if (ret < 0) {
		dev_err(dev, "failed to init entity pads: %d", ret);
		goto free_ctrl;
	}

	ret = v4l2_async_register_subdev(&ctx->sd);
	if (ret < 0) {
		dev_err(dev, "could not register v4l2 device\n");
		goto free_entity;
	}

#ifdef IMGSENSOR_USE_PM_FRAMEWORK
	pm_runtime_enable(dev);
#else
	// TODO
#endif

	/* register thermal device */
	if (ctx->subdrv->ops->get_temp) {
		struct thermal_zone_device *tzdev;

		tzdev = devm_thermal_of_zone_register(
			       dev, 0, ctx, &imgsensor_tz_ops);
		if (IS_ERR(tzdev))
			dev_info(dev, "failed to register thermal zone\n");
	}

	notify_fsync_mgr(ctx, 1);

	ret = device_create_file(dev, &dev_attr_debug_i2c_ops);
	if (ret)
		dev_info(dev, "failed to create sysfs debug_i2c_ops\n");

	ret = device_create_file(dev, &dev_attr_debug_pwr_ops);
	if (ret)
		dev_info(dev, "failed to create sysfs debug_pwr_ops\n");

	ret = device_create_file(dev, &dev_attr_debug_sensor_mode_ops);
	if (ret)
		dev_info(dev, "failed to create sysfs debug_sensor_mode_ops\n");

	ctx->sensor_ws = wakeup_source_register(dev, ctx->sd.name);

	if (!ctx->sensor_ws)
		dev_info(dev, "failed to wakeup_source_register\n");

	return 0;

free_entity:
	media_entity_cleanup(&ctx->sd.entity);

free_ctrl:
	v4l2_ctrl_handler_free(&ctx->ctrls);
	mutex_destroy(&ctx->mutex);

	return ret;
}

static void imgsensor_remove(struct i2c_client *client)
{
	struct v4l2_subdev *sd = i2c_get_clientdata(client);
	struct adaptor_ctx *ctx = to_ctx(sd);

	v4l2_async_unregister_subdev(sd);
	media_entity_cleanup(&sd->entity);
	v4l2_ctrl_handler_free(sd->ctrl_handler);

	notify_fsync_mgr(ctx, 0);

	if (!ctx->sensor_ws)
		wakeup_source_unregister(ctx->sensor_ws);
	ctx->sensor_ws = NULL;

	kfree(ctx->ctx_pw_seq);

#ifdef IMGSENSOR_USE_PM_FRAMEWORK
	pm_runtime_disable(&client->dev);
#else
	// TODO
#endif
	device_remove_file(ctx->dev, &dev_attr_debug_i2c_ops);
	device_remove_file(ctx->dev, &dev_attr_debug_pwr_ops);
	device_remove_file(ctx->dev, &dev_attr_debug_sensor_mode_ops);

	mutex_destroy(&ctx->mutex);

}

#ifdef IMGSENSOR_USE_PM_FRAMEWORK
static const struct dev_pm_ops imgsensor_pm_ops = {
	SET_SYSTEM_SLEEP_PM_OPS(imgsensor_suspend, imgsensor_resume)
	SET_RUNTIME_PM_OPS(imgsensor_runtime_suspend,
			imgsensor_runtime_resume, NULL)
};
#endif

static const struct i2c_device_id imgsensor_id[] = {
	{"imgsensor", 0},
	{}
};
MODULE_DEVICE_TABLE(i2c, imgsensor_id);

static const struct of_device_id imgsensor_of_match[] = {
	{.compatible = "mediatek,imgsensor"},
	{}
};
MODULE_DEVICE_TABLE(of, imgsensor_of_match);

static struct i2c_driver imgsensor_i2c_driver = {
	.driver = {
		.of_match_table = of_match_ptr(imgsensor_of_match),
		.name = "imgsensor",
#ifdef IMGSENSOR_USE_PM_FRAMEWORK
		.pm = &imgsensor_pm_ops,
#endif
	},
	.probe_new = imgsensor_probe,
	.remove = imgsensor_remove,
	.id_table = imgsensor_id,
};

static int __init adaptor_drv_init(void)
{
	i2c_add_driver(&imgsensor_i2c_driver);

	return 0;
}

static void __exit adaptor_drv_exit(void)
{
	i2c_del_driver(&imgsensor_i2c_driver);
}

late_initcall(adaptor_drv_init);
module_exit(adaptor_drv_exit);

MODULE_LICENSE("GPL v2");
