1548 lines
37 KiB
C
1548 lines
37 KiB
C
// 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 %u\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->flags = V4L2_MBUS_CSI2_1_LANE;
|
|
break;
|
|
case SENSOR_MIPI_2_LANE:
|
|
config->flags = V4L2_MBUS_CSI2_2_LANE;
|
|
break;
|
|
case SENSOR_MIPI_3_LANE:
|
|
config->flags = V4L2_MBUS_CSI2_3_LANE;
|
|
break;
|
|
case SENSOR_MIPI_4_LANE:
|
|
config->flags = V4L2_MBUS_CSI2_4_LANE;
|
|
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(void *data, int *temperature)
|
|
{
|
|
struct adaptor_ctx *ctx = data;
|
|
|
|
#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) {
|
|
if (ctx->aov_i2c_bus_scl_switch_en == 1 ||
|
|
ctx->aov_i2c_bus_sda_switch_en == 1)
|
|
*temperature = THERMAL_TEMP_INVALID;
|
|
else
|
|
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_of_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, ®);
|
|
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_zone_of_sensor_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 int 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);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#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");
|