From: zhoupeng Date: Thu, 20 Jul 2017 07:49:59 +0000 (+0800) Subject: media: soc_camera: add tc358749xgb sensor driver X-Git-Tag: release-20171130_firefly~4^2~124 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=5e8201e3717bf94625090901f8bac654cfd6634e;p=firefly-linux-kernel-4.4.55.git media: soc_camera: add tc358749xgb sensor driver Change-Id: I66ccb856cb2689bf26a8a12069b29c7ec6dae6f0 Signed-off-by: zhoupeng --- diff --git a/drivers/media/i2c/soc_camera/rockchip/Kconfig b/drivers/media/i2c/soc_camera/rockchip/Kconfig index b6c73e183e58..d277e180fc79 100644 --- a/drivers/media/i2c/soc_camera/rockchip/Kconfig +++ b/drivers/media/i2c/soc_camera/rockchip/Kconfig @@ -32,3 +32,10 @@ config VIDEO_OV7750 default n ---help--- This is ov7750 camera driver adapt to rockchip cif isp platform. + +config VIDEO_TC358749XBG + tristate "tc358749xbg driver adapt to rockchip cif isp platform" + depends on VIDEO_V4L2 && VIDEO_RK_CIF_ISP10 && I2C + default n + ---help--- + This is tc358749xbg hdmi video driver adapt to rockchip cif isp platform. diff --git a/drivers/media/i2c/soc_camera/rockchip/Makefile b/drivers/media/i2c/soc_camera/rockchip/Makefile index 5bb9c4e4bc48..ba29f02768e9 100644 --- a/drivers/media/i2c/soc_camera/rockchip/Makefile +++ b/drivers/media/i2c/soc_camera/rockchip/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_VIDEO_OV2710) += ov_camera_module.o rk_camera_module.o ov2710_v4l2- obj-$(CONFIG_VIDEO_OV4689) += ov_camera_module.o rk_camera_module.o ov4689_v4l2-i2c-subdev.o obj-$(CONFIG_VIDEO_IMX323) += imx_camera_module.o rk_camera_module.o imx323_v4l2-i2c-subdev.o obj-$(CONFIG_VIDEO_OV7750) += ov_camera_module.o rk_camera_module.o ov7750_v4l2-i2c-subdev.o +obj-$(CONFIG_VIDEO_TC358749XBG) += tc_camera_module.o rk_camera_module.o tc358749xbg_v4l2-i2c-subdev.o diff --git a/drivers/media/i2c/soc_camera/rockchip/tc358749xbg_v4l2-i2c-subdev.c b/drivers/media/i2c/soc_camera/rockchip/tc358749xbg_v4l2-i2c-subdev.c new file mode 100644 index 000000000000..f6408b8c753f --- /dev/null +++ b/drivers/media/i2c/soc_camera/rockchip/tc358749xbg_v4l2-i2c-subdev.c @@ -0,0 +1,1114 @@ +/* + * drivers/media/i2c/soc_camera/xgold/tc358749xbg.c + * + * tc358749xbg sensor driver + * + * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd. + * Copyright (C) 2012-2014 Intel Mobile Communications GmbH + * Copyright (C) 2008 Texas Instruments. + * Author: zhoupeng + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + * Note: + * + * v0.1.0: + * 1. Initialize version; + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tc_camera_module.h" + +#define TC358749xbg_DRIVER_NAME "tc358749xbg" + +/* product ID */ +#define TC358749xbg_PID_MAGIC 0x4701 +#define TC358749xbg_PID_ADDR 0x0000 + +#define INIT_END 0x854A +#define SYS_STATUS 0x8520 +#define CONFCTL 0x0004 +#define VI_REP 0x8576 +#define MASK_VOUT_COLOR_SEL 0xe0 +#define MASK_VOUT_COLOR_RGB_FULL 0x00 +#define MASK_VOUT_COLOR_RGB_LIMITED 0x20 +#define MASK_VOUT_COLOR_601_YCBCR_FULL 0x40 +#define MASK_VOUT_COLOR_601_YCBCR_LIMITED 0x60 +#define MASK_VOUT_COLOR_709_YCBCR_FULL 0x80 +#define MASK_VOUT_COLOR_709_YCBCR_LIMITED 0xa0 +#define MASK_VOUT_COLOR_FULL_TO_LIMITED 0xc0 +#define MASK_VOUT_COLOR_LIMITED_TO_FULL 0xe0 +#define MASK_IN_REP_HEN 0x10 +#define MASK_IN_REP 0x0f + +/* ======================================================================== */ +/* Base sensor configs */ +/* ======================================================================== */ +/* MCLK:24MHz 1920x1080 30fps mipi 4lane 800Mbps/lane */ +static struct tc_camera_module_reg tc358749xbg_init_tab_1920_1080_60fps[] = { + // + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0004, 0x0004, 2}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0002, 0x7F80, 2}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0002, 0x0000, 2}, + // + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0020, 0x508A, 2}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0022, 0x0203, 2}, + {TC_CAMERA_MODULE_REG_TYPE_TIMEOUT, 0x0000, 0x0001, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0022, 0x0213, 2}, + // + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0006, 0x012C, 2}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0060, 0x0001, 2}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x7080, 0x0000, 2}, + // + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0014, 0x0000, 2}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0016, 0x05FF, 2}, + // + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0100, 0x00000003, 4}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0104, 0x00000003, 4}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0108, 0x00000003, 4}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x010C, 0x00000003, 4}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0110, 0x00000003, 4}, + // + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0140, 0x00000000, 4}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0144, 0x00000000, 4}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0148, 0x00000000, 4}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x014C, 0x00000000, 4}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0150, 0x00000000, 4}, + // + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0210, 0x00001770, 4}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0214, 0x00000005, 4}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0218, 0x00001505, 4}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x021C, 0x00000001, 4}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0220, 0x00000105, 4}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0224, 0x0000332C, 4}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0228, 0x00000008, 4}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x022C, 0x00000002, 4}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0230, 0x00000005, 4}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0234, 0x0000001F, 4}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0238, 0x00000000, 4}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x023C, 0x00040005, 4}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0204, 0x00000001, 4}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0518, 0x00000001, 4}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0500, 0xA3008086, 4}, + // + // + // + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8531, 0x01, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8532, 0x80, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8540, 0x8C, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8541, 0x0A, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8630, 0xB0, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8631, 0x1E, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8632, 0x04, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8670, 0x01, 1}, + // + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8532, 0x80, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8536, 0x40, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x853F, 0x0A, 1}, + // + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8543, 0x32, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8544, 0x10, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8545, 0x31, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8546, 0x2D, 1}, + // + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x85C7, 0x01, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x85CA, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x85CB, 0x01, 1}, + // + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C00, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C01, 0xFF, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C02, 0xFF, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C03, 0xFF, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C04, 0xFF, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C05, 0xFF, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C06, 0xFF, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C07, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C08, 0x52, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C09, 0x62, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C0A, 0x88, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C0B, 0x88, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C0C, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C0D, 0x88, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C0E, 0x88, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C0F, 0x88, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C10, 0x1C, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C11, 0x15, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C12, 0x01, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C13, 0x03, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C14, 0x80, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C15, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C16, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C17, 0x78, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C18, 0x0A, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C19, 0x0D, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C1A, 0xC9, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C1B, 0xA0, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C1C, 0x57, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C1D, 0x47, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C1E, 0x98, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C1F, 0x27, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C20, 0x12, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C21, 0x48, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C22, 0x4C, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C23, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C24, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C25, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C26, 0x01, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C27, 0x01, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C28, 0x01, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C29, 0x01, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C2A, 0x01, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C2B, 0x01, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C2C, 0x01, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C2D, 0x01, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C2E, 0x01, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C2F, 0x01, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C30, 0x01, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C31, 0x01, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C32, 0x01, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C33, 0x01, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C34, 0x01, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C35, 0x01, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C36, 0x02, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C37, 0x3A, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C38, 0x80, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C39, 0x18, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C3A, 0x71, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C3B, 0x38, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C3C, 0x2D, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C3D, 0x40, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C3E, 0x58, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C3F, 0x2C, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C40, 0x45, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C41, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C42, 0xC4, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C43, 0x8E, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C44, 0x21, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C45, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C46, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C47, 0x1E, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C48, 0x01, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C49, 0x1D, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C4A, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C4B, 0x72, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C4C, 0x51, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C4D, 0xD0, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C4E, 0x1E, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C4F, 0x20, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C50, 0x6E, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C51, 0x28, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C52, 0x55, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C53, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C54, 0xC4, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C55, 0x8E, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C56, 0x21, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C57, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C58, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C59, 0x1E, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C5A, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C5B, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C5C, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C5D, 0xFC, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C5E, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C5F, 0x54, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C60, 0x6F, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C61, 0x73, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C62, 0x68, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C63, 0x69, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C64, 0x62, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C65, 0x61, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C66, 0x2D, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C67, 0x48, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C68, 0x32, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C69, 0x44, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C6A, 0x0A, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C6B, 0x20, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C6C, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C6D, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C6E, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C6F, 0xFD, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C70, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C71, 0x17, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C72, 0x3D, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C73, 0x0F, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C74, 0x8C, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C75, 0x17, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C76, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C77, 0x0A, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C78, 0x20, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C79, 0x20, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C7A, 0x20, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C7B, 0x20, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C7C, 0x20, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C7D, 0x20, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C7E, 0x01, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C7F, 0x92, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C80, 0x02, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C81, 0x03, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C82, 0x1A, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C83, 0x74, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C84, 0x47, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C85, 0x10, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C86, 0x04, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C87, 0x02, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C88, 0x01, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C89, 0x0A, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C8A, 0x04, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C8B, 0x04, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C8C, 0x23, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C8D, 0x09, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C8E, 0x07, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C8F, 0x01, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C90, 0x83, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C91, 0x01, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C92, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C93, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C94, 0x65, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C95, 0x03, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C96, 0x0C, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C97, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C98, 0x10, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C99, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C9A, 0x8C, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C9B, 0x0A, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C9C, 0xD0, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C9D, 0x8A, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C9E, 0x20, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8C9F, 0xE0, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CA0, 0x2D, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CA1, 0x10, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CA2, 0x10, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CA3, 0x3E, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CA4, 0x96, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CA5, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CA6, 0x13, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CA7, 0x8E, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CA8, 0x21, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CA9, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CAA, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CAB, 0x1E, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CAC, 0xD8, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CAD, 0x09, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CAE, 0x80, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CAF, 0xA0, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CB0, 0x20, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CB1, 0xE0, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CB2, 0x2D, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CB3, 0x10, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CB4, 0x10, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CB5, 0x60, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CB6, 0xA2, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CB7, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CB8, 0xC4, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CB9, 0x8E, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CBA, 0x21, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CBB, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CBC, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CBD, 0x18, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CBE, 0x8C, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CBF, 0x0A, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CC0, 0xD0, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CC1, 0x90, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CC2, 0x20, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CC3, 0x40, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CC4, 0x31, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CC5, 0x20, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CC6, 0x0C, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CC7, 0x40, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CC8, 0x55, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CC9, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CCA, 0x80, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CCB, 0xB0, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CCC, 0x74, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CCD, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CCE, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CCF, 0x18, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CD0, 0x01, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CD1, 0x1D, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CD2, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CD3, 0x72, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CD4, 0x51, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CD5, 0xD0, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CD6, 0x1E, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CD7, 0x20, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CD8, 0x6E, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CD9, 0x28, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CDA, 0x55, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CDB, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CDC, 0xC4, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CDD, 0x8E, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CDE, 0x21, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CDF, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CE0, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CE1, 0x1E, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CE2, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CE3, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CE4, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CE5, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CE6, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CE7, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CE8, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CE9, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CEA, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CEB, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CEC, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CED, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CEE, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CEF, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CF0, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CF1, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CF2, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CF3, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CF4, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CF5, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CF6, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CF7, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CF8, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CF9, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CFA, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CFB, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CFC, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CFD, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CFE, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8CFF, 0x86, 1}, + // + // + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8573, 0xC1, 1}, + // + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8600, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8602, 0xF3, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8603, 0x02, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8604, 0x0C, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8606, 0x05, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8607, 0x00, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8620, 0x22, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8640, 0x01, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8641, 0x65, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8642, 0x07, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8652, 0x02, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8665, 0x10, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x85AA, 0x50, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x85AF, 0xC6, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x85AB, 0x00, 1}, + // + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x870B, 0x2C, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x870C, 0x53, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x870D, 0x01, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x870E, 0x30, 1}, + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x9007, 0x10, 1}, + // + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x854A, 0x01, 1}, + // + // + // + // + // + // + // + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x854A, 0x01, 1}, + // + //85 20 + // + // + {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0004, 0x0CD7, 2}, +}; + +/* ======================================================================== */ +static struct tc_camera_module_config tc358749xbg_configs[] = { + { + .name = "1920x1080_60fps", + .frm_fmt = { + .width = 1920, + .height = 1080, + .code = MEDIA_BUS_FMT_UYVY8_2X8 + }, + .frm_intrvl = { + .interval = { + .numerator = 1, + .denominator = 60 + } + }, + .auto_exp_enabled = false, + .auto_gain_enabled = false, + .auto_wb_enabled = false, + .reg_table = (void *)tc358749xbg_init_tab_1920_1080_60fps, + .reg_table_num_entries = + sizeof(tc358749xbg_init_tab_1920_1080_60fps) / + sizeof(tc358749xbg_init_tab_1920_1080_60fps[0]), + .v_blanking_time_us = 3078, + .ignore_measurement_check = 1, + PLTFRM_CAM_ITF_MIPI_CFG(0, 4, 800, 24000000) + } +}; + +static struct tc35x_priv tc358749xbg_priv; + +static struct tc_camera_module *to_tc_camera_module(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tc_camera_module, sd); +} + +/*--------------------------------------------------------------------------*/ +static int tc358749xbg_set_flip( + struct tc_camera_module *cam_mod, + struct tc_camera_module_reg reglist[], + int len) +{ + int mode = 0; + + mode = tc_camera_module_get_flip_mirror(cam_mod); + + if (mode == -1) { + tc_camera_module_pr_debug( + cam_mod, + "dts don't set flip, return!\n"); + return 0; + } + + return 0; +} + +static int tc358749xbg_write_aec(struct tc_camera_module *cam_mod) +{ + int ret = 0; + + tc_camera_module_pr_debug(cam_mod, + "exp_time=%d lines, gain=%d, flash_mode=%d\n", + cam_mod->exp_config.exp_time, + cam_mod->exp_config.gain, + cam_mod->exp_config.flash_mode); + + if (IS_ERR_VALUE(ret)) + tc_camera_module_pr_err(cam_mod, + "failed with error (%d)\n", ret); + return ret; +} + +int tc358749xbg_enable_stream(struct tc_camera_module *cam_mod, bool enable) +{ + u8 init_end, sys_status; + u16 confctl; + + if (enable) { + init_end = tc_camera_module_read8_reg(cam_mod, INIT_END); + tc_camera_module_pr_debug(cam_mod, + "INIT_END(0x854A) = 0x%02x\n", + init_end); + sys_status = tc_camera_module_read8_reg(cam_mod, SYS_STATUS); + tc_camera_module_pr_debug(cam_mod, + "INIT_END(0x8520) = 0x%02x\n", + sys_status); + } + + confctl = tc_camera_module_read16_reg(cam_mod, CONFCTL); + tc_camera_module_pr_debug(cam_mod, + "CONFCTL(0x0004) = 0x%04x\n", + confctl); + + return 0; +} + +int tc358749xbg_s_power(struct tc_camera_module *cam_mod, bool enable) +{ + tc_camera_module_pr_debug(cam_mod, + "power %d\n", + enable); + + if (enable) { + gpiod_direction_output(tc358749xbg_priv.gpio_reset, 0); + gpiod_direction_output(tc358749xbg_priv.gpio_int, 0); + gpiod_direction_output(tc358749xbg_priv.gpio_stanby, 0); + gpiod_direction_output(tc358749xbg_priv.gpio_power18, 1); + gpiod_direction_output(tc358749xbg_priv.gpio_power33, 1); + gpiod_direction_output(tc358749xbg_priv.gpio_power, 1); + gpiod_direction_output(tc358749xbg_priv.gpio_stanby, 1); + gpiod_direction_output(tc358749xbg_priv.gpio_reset, 1); + } else { + gpiod_direction_output(tc358749xbg_priv.gpio_power, 0); + gpiod_direction_output(tc358749xbg_priv.gpio_reset, 1); + gpiod_direction_output(tc358749xbg_priv.gpio_reset, 0); + } + + return 0; +} + +static int tc358749xbg_g_fmt( + struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct tc_camera_module *cam_mod = to_tc_camera_module(sd); + struct v4l2_mbus_framefmt *fmt = &format->format; + u8 vi_rep = tc_camera_module_read8_reg(cam_mod, VI_REP); + + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "read VI_REP return 0x%x\n", vi_rep); + + if (format->pad != 0) + return -EINVAL; + if (cam_mod->active_config) { + fmt->code = cam_mod->active_config->frm_fmt.code; + fmt->width = cam_mod->active_config->frm_fmt.width; + fmt->height = cam_mod->active_config->frm_fmt.height; + fmt->field = V4L2_FIELD_NONE; + + switch (vi_rep & MASK_VOUT_COLOR_SEL) { + case MASK_VOUT_COLOR_RGB_FULL: + case MASK_VOUT_COLOR_RGB_LIMITED: + fmt->colorspace = V4L2_COLORSPACE_SRGB; + break; + case MASK_VOUT_COLOR_601_YCBCR_LIMITED: + case MASK_VOUT_COLOR_601_YCBCR_FULL: + fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + break; + case MASK_VOUT_COLOR_709_YCBCR_FULL: + case MASK_VOUT_COLOR_709_YCBCR_LIMITED: + fmt->colorspace = V4L2_COLORSPACE_REC709; + break; + default: + fmt->colorspace = 0; + break; + } + + return 0; + } + + pltfrm_camera_module_pr_err(&cam_mod->sd, "no active config\n"); + + return -1; +} + +static int tc358749xbg_g_ctrl(struct tc_camera_module *cam_mod, u32 ctrl_id) +{ + int ret = 0; + + tc_camera_module_pr_debug(cam_mod, "\n"); + + switch (ctrl_id) { + case V4L2_CID_GAIN: + case V4L2_CID_EXPOSURE: + case V4L2_CID_FLASH_LED_MODE: + /* nothing to be done here */ + break; + default: + ret = -EINVAL; + break; + } + + if (IS_ERR_VALUE(ret)) + tc_camera_module_pr_debug(cam_mod, + "failed with error (%d)\n", + ret); + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int tc358749xbg_filltimings( + struct tc_camera_module_custom_config *custom) +{ + return 0; +} + +/*--------------------------------------------------------------------------*/ + +static int tc358749xbg_g_timings( + struct tc_camera_module *cam_mod, + struct tc_camera_module_timings *timings) +{ + int ret = 0; + unsigned int vts; + + if (IS_ERR_OR_NULL(cam_mod->active_config)) + goto err; + + *timings = cam_mod->active_config->timings; + + vts = (!cam_mod->vts_cur) ? + timings->frame_length_lines : + cam_mod->vts_cur; + if (cam_mod->frm_intrvl_valid) + timings->vt_pix_clk_freq_hz = + cam_mod->frm_intrvl.interval.denominator + * vts + * timings->line_length_pck; + else + timings->vt_pix_clk_freq_hz = + cam_mod->active_config->frm_intrvl.interval.denominator * + vts * timings->line_length_pck; + + return ret; +err: + tc_camera_module_pr_err(cam_mod, + "failed with error (%d)\n", + ret); + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int tc358749xbg_s_ctrl(struct tc_camera_module *cam_mod, u32 ctrl_id) +{ + int ret = 0; + + tc_camera_module_pr_debug(cam_mod, "\n"); + + switch (ctrl_id) { + case V4L2_CID_GAIN: + case V4L2_CID_EXPOSURE: + ret = tc358749xbg_write_aec(cam_mod); + break; + case V4L2_CID_FLASH_LED_MODE: + /* nothing to be done here */ + break; + case V4L2_CID_FOCUS_ABSOLUTE: + /* todo*/ + break; + /* + * case RK_V4L2_CID_FPS_CTRL: + * if (cam_mod->auto_adjust_fps) + * ret = TC358749xbg_auto_adjust_fps( + * cam_mod, + * cam_mod->exp_config.exp_time); + * break; + */ + default: + ret = -EINVAL; + break; + } + + if (IS_ERR_VALUE(ret)) + tc_camera_module_pr_err(cam_mod, + "failed with error (%d)\n", + ret); + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int tc358749xbg_s_ext_ctrls( + struct tc_camera_module *cam_mod, + struct tc_camera_module_ext_ctrls *ctrls) +{ + int ret = 0; + + /* Handles only exposure and gain together special case. */ + if (ctrls->count == 1) + ret = tc358749xbg_s_ctrl(cam_mod, ctrls->ctrls[0].id); + else if ((ctrls->count == 3) && + (ctrls->ctrls[0].id == V4L2_CID_GAIN || + ctrls->ctrls[0].id == V4L2_CID_EXPOSURE || + ctrls->ctrls[1].id == V4L2_CID_GAIN || + ctrls->ctrls[1].id == V4L2_CID_EXPOSURE)) + ret = tc358749xbg_write_aec(cam_mod); + else + ret = -EINVAL; + + if (IS_ERR_VALUE(ret)) + tc_camera_module_pr_debug(cam_mod, + "failed with error (%d)\n", + ret); + + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int tc358749xbg_check_camera_id(struct tc_camera_module *cam_mod) +{ + u16 pid; + int ret = 0; + + tc_camera_module_pr_debug(cam_mod, "in\n"); + + pid = tc_camera_module_read16_reg(cam_mod, TC358749xbg_PID_ADDR); + tc_camera_module_pr_err(cam_mod, + "read pid return 0x%x\n", + pid); + + if (pid == TC358749xbg_PID_MAGIC) { + tc_camera_module_pr_debug(cam_mod, + "successfully detected camera ID 0x%04x\n", + pid); + } else { + tc_camera_module_pr_err(cam_mod, + "wrong camera ID, expected 0x%04x, detected 0x%04x\n", + TC358749xbg_PID_MAGIC, + pid); + ret = -EINVAL; + goto err; + } + + return 0; +err: + tc_camera_module_pr_err(cam_mod, + "failed with error (%d)\n", + ret); + return ret; +} + +/* ======================================================================== */ +int tc_camera_358749xbg_module_s_ctrl( + struct v4l2_subdev *sd, + struct v4l2_control *ctrl) +{ + return 0; +} + +/* ======================================================================== */ + +int tc_camera_358749xbg_module_s_ext_ctrls( + struct v4l2_subdev *sd, + struct v4l2_ext_controls *ctrls) +{ + return 0; +} + +long tc_camera_358749xbg_module_ioctl( + struct v4l2_subdev *sd, + unsigned int cmd, + void *arg) +{ + return 0; +} + +/* ======================================================================== */ +/* This part is platform dependent */ +/* ======================================================================== */ + +static struct v4l2_subdev_core_ops tc358749xbg_camera_module_core_ops = { + .g_ctrl = tc_camera_module_g_ctrl, + .s_ctrl = tc_camera_module_s_ctrl, + .s_ext_ctrls = tc_camera_module_s_ext_ctrls, + .s_power = tc_camera_module_s_power, + .ioctl = tc_camera_module_ioctl +}; + +static struct v4l2_subdev_video_ops tc358749xbg_camera_module_video_ops = { + .s_frame_interval = tc_camera_module_s_frame_interval, + .s_stream = tc_camera_module_s_stream +}; + +static struct v4l2_subdev_pad_ops tc358749xbg_camera_module_pad_ops = { + .enum_frame_interval = tc_camera_module_enum_frameintervals, + .get_fmt = tc358749xbg_g_fmt, + .set_fmt = tc_camera_module_s_fmt, +}; + +static struct v4l2_subdev_ops tc358749xbg_camera_module_ops = { + .core = &tc358749xbg_camera_module_core_ops, + .video = &tc358749xbg_camera_module_video_ops, + .pad = &tc358749xbg_camera_module_pad_ops +}; + +static struct tc_camera_module tc358749xbg; + +static struct tc_camera_module_custom_config tc358749xbg_custom_config = { + .s_ctrl = tc358749xbg_s_ctrl, + .g_ctrl = tc358749xbg_g_ctrl, + .s_ext_ctrls = tc358749xbg_s_ext_ctrls, + .g_timings = tc358749xbg_g_timings, + .set_flip = tc358749xbg_set_flip, + .check_camera_id = tc358749xbg_check_camera_id, + .enable_stream = tc358749xbg_enable_stream, + .s_power = tc358749xbg_s_power, + .configs = tc358749xbg_configs, + .num_configs = ARRAY_SIZE(tc358749xbg_configs), + .power_up_delays_ms = {5, 30, 30} +}; + +static int test_parse_dts( + struct tc_camera_module *cam_mod, + struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct device_node *np = of_node_get(client->dev.of_node); + + tc_camera_module_pr_debug(cam_mod, "enter!\n"); + + if (!dev) + tc_camera_module_pr_debug(cam_mod, "dev is NULL!"); + + if (!np) + tc_camera_module_pr_debug(cam_mod, "np is NULL"); + + tc358749xbg_priv.gpio_int = devm_gpiod_get_optional(dev, "int", + GPIOD_OUT_HIGH); + tc358749xbg_priv.gpio_power = devm_gpiod_get_optional(dev, "power", + GPIOD_OUT_HIGH); + tc358749xbg_priv.gpio_power18 = devm_gpiod_get_optional(dev, "power18", + GPIOD_OUT_HIGH); + tc358749xbg_priv.gpio_power33 = devm_gpiod_get_optional(dev, "power33", + GPIOD_OUT_HIGH); + tc358749xbg_priv.gpio_csi_ctl = devm_gpiod_get_optional(dev, "csi-ctl", + GPIOD_OUT_LOW); + tc358749xbg_priv.gpio_reset = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + tc358749xbg_priv.gpio_stanby = devm_gpiod_get_optional(dev, "stanby", + GPIOD_OUT_LOW); + return 0; +} + +static int test_deparse_dts(struct i2c_client *client) +{ + if (tc358749xbg_priv.gpio_int) { + gpiod_direction_input(tc358749xbg_priv.gpio_int); + gpiod_put(tc358749xbg_priv.gpio_int); + } + + if (tc358749xbg_priv.gpio_reset) { + gpiod_direction_input(tc358749xbg_priv.gpio_reset); + gpiod_put(tc358749xbg_priv.gpio_reset); + } + + if (tc358749xbg_priv.gpio_stanby) { + gpiod_direction_input(tc358749xbg_priv.gpio_stanby); + gpiod_put(tc358749xbg_priv.gpio_stanby); + } + + if (tc358749xbg_priv.gpio_csi_ctl) { + gpiod_direction_input(tc358749xbg_priv.gpio_csi_ctl); + gpiod_put(tc358749xbg_priv.gpio_csi_ctl); + } + + if (tc358749xbg_priv.gpio_power) { + gpiod_direction_input(tc358749xbg_priv.gpio_power); + gpiod_put(tc358749xbg_priv.gpio_power); + } + + if (tc358749xbg_priv.gpio_power18) { + gpiod_direction_input(tc358749xbg_priv.gpio_power18); + gpiod_put(tc358749xbg_priv.gpio_power18); + } + + if (tc358749xbg_priv.gpio_power33) { + gpiod_direction_input(tc358749xbg_priv.gpio_power33); + gpiod_put(tc358749xbg_priv.gpio_power33); + } + + return 0; +} + +static ssize_t tc358749xbg_debugfs_reg_write( + struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + struct tc_camera_module *cam_mod = + ((struct seq_file *)file->private_data)->private; + + char kbuf[30]; + char rw; + u32 reg; + u32 reg_value; + int len; + int ret; + int nbytes = min(count, sizeof(kbuf) - 1); + + if (copy_from_user(kbuf, buf, nbytes)) + return -EFAULT; + + kbuf[nbytes] = '\0'; + tc_camera_module_pr_debug(cam_mod, "kbuf is %s\n", kbuf); + ret = sscanf(kbuf, " %c %x %x %d", &rw, ®, ®_value, &len); + tc_camera_module_pr_err(cam_mod, "ret = %d!\n", ret); + if (ret != 4) { + tc_camera_module_pr_err(cam_mod, "sscanf failed!\n"); + return 0; + } + + if (rw == 'w') { + if (len == 1) { + tc_camera_module_write8_reg(cam_mod, reg, reg_value); + tc_camera_module_pr_err(cam_mod, + "%s(%d): write reg 0x%02x ---> 0x%x!\n", + __func__, __LINE__, + reg, reg_value); + } else if (len == 2) { + tc_camera_module_write16_reg(cam_mod, reg, reg_value); + tc_camera_module_pr_err(cam_mod, + "%s(%d): write reg 0x%02x ---> 0x%x!\n", + __func__, __LINE__, + reg, reg_value); + } else if (len == 4) { + tc_camera_module_write32_reg(cam_mod, reg, reg_value); + tc_camera_module_pr_err(cam_mod, + "%s(%d): write reg 0x%02x ---> 0x%x!\n", + __func__, __LINE__, + reg, reg_value); + } else { + tc_camera_module_pr_err(cam_mod, + "len %d is err!\n", + len); + } + } else if (rw == 'r') { + if (len == 1) { + reg_value = tc_camera_module_read8_reg(cam_mod, reg); + tc_camera_module_pr_err(cam_mod, + "%s(%d): read reg 0x%02x ---> 0x%x!\n", + __func__, __LINE__, + reg, reg_value); + } else if (len == 2) { + reg_value = tc_camera_module_read16_reg(cam_mod, reg); + tc_camera_module_pr_err(cam_mod, + "%s(%d): read reg 0x%02x ---> 0x%x!\n", + __func__, __LINE__, + reg, reg_value); + } else if (len == 4) { + reg_value = tc_camera_module_read32_reg(cam_mod, reg); + tc_camera_module_pr_err(cam_mod, + "%s(%d): read reg 0x%02x ---> 0x%x!\n", + __func__, __LINE__, + reg, reg_value); + } else { + tc_camera_module_pr_err(cam_mod, + "len %d is err!\n", + len); + } + } else { + tc_camera_module_pr_err(cam_mod, + "%c command is not support\n", + rw); + } + + return count; +} + +static int tc358749xbg_debugfs_reg_show(struct seq_file *s, void *v) +{ + int i; + u32 value; + int reg_table_num_entries = + sizeof(tc358749xbg_init_tab_1920_1080_60fps) / + sizeof(tc358749xbg_init_tab_1920_1080_60fps[0]); + struct tc_camera_module *cam_mod = s->private; + + for (i = 0; i < reg_table_num_entries; i++) { + switch (tc358749xbg_init_tab_1920_1080_60fps[i].len) { + case 1: + value = tc_camera_module_read8_reg(cam_mod, + tc358749xbg_init_tab_1920_1080_60fps[i].reg); + break; + case 2: + value = tc_camera_module_read16_reg(cam_mod, + tc358749xbg_init_tab_1920_1080_60fps[i].reg); + break; + case 4: + value = tc_camera_module_read32_reg(cam_mod, + tc358749xbg_init_tab_1920_1080_60fps[i].reg); + break; + default: + printk(KERN_ERR "%s(%d): command no support!\n", __func__, __LINE__); + break; + } + printk(KERN_ERR "%s(%d): reg 0x%04x ---> 0x%x\n", __func__, __LINE__, + tc358749xbg_init_tab_1920_1080_60fps[i].reg, value); + } + return 0; +} + +static int tc358749xbg_debugfs_open(struct inode *inode, struct file *file) +{ + struct specific_sensor *spsensor = inode->i_private; + + return single_open(file, tc358749xbg_debugfs_reg_show, spsensor); +} + +static const struct file_operations tc358749xbg_debugfs_fops = { + .owner = THIS_MODULE, + .open = tc358749xbg_debugfs_open, + .read = seq_read, + .write = tc358749xbg_debugfs_reg_write, + .llseek = seq_lseek, + .release = single_release +}; + +static struct dentry *debugfs_dir; + +static int tc358749xbg_probe( + struct i2c_client *client, + const struct i2c_device_id *id) +{ + dev_info(&client->dev, "probing...\n"); + + tc358749xbg_filltimings(&tc358749xbg_custom_config); + v4l2_i2c_subdev_init(&tc358749xbg.sd, client, + &tc358749xbg_camera_module_ops); + + tc358749xbg.custom = tc358749xbg_custom_config; + + test_parse_dts(&tc358749xbg, client); + debugfs_dir = debugfs_create_dir("hdmiin", NULL); + if (IS_ERR(debugfs_dir)) + printk(KERN_ERR "%s(%d): create debugfs dir failed!\n", + __func__, __LINE__); + else + debugfs_create_file("register", S_IRUSR, + debugfs_dir, &tc358749xbg, + &tc358749xbg_debugfs_fops); + + dev_info(&client->dev, "probing successful\n"); + return 0; +} + +/* ======================================================================== */ + +static int tc358749xbg_remove( + struct i2c_client *client) +{ + struct tc_camera_module *cam_mod = i2c_get_clientdata(client); + + dev_info(&client->dev, "remtcing device...\n"); + + if (!client->adapter) + return -ENODEV; /* our client isn't attached */ + + debugfs_remove_recursive(debugfs_dir); + test_deparse_dts(client); + + tc_camera_module_release(cam_mod); + + dev_info(&client->dev, "remtced\n"); + return 0; +} + +static const struct i2c_device_id tc358749xbg_id[] = { + { TC358749xbg_DRIVER_NAME, 0 }, + { } +}; + +static const struct of_device_id tc358749xbg_of_match[] = { + {.compatible = "toshiba,tc358749xbg-v4l2-i2c-subdev"}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, tc358749xbg_id); + +static struct i2c_driver tc358749xbg_i2c_driver = { + .driver = { + .name = TC358749xbg_DRIVER_NAME, + .of_match_table = tc358749xbg_of_match + }, + .probe = tc358749xbg_probe, + .remove = tc358749xbg_remove, + .id_table = tc358749xbg_id, +}; + +module_i2c_driver(tc358749xbg_i2c_driver); + +MODULE_DESCRIPTION("SoC Camera driver for tc358749xbg"); +MODULE_AUTHOR("Benjo"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/soc_camera/rockchip/tc_camera_module.c b/drivers/media/i2c/soc_camera/rockchip/tc_camera_module.c new file mode 100644 index 000000000000..50b02c10a4c1 --- /dev/null +++ b/drivers/media/i2c/soc_camera/rockchip/tc_camera_module.c @@ -0,0 +1,1309 @@ +/* + * tc_camera_module.c + * + * Generic toshiba sensor driver + * + * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd. + * + * Copyright (C) 2012-2014 Intel Mobile Communications GmbH + * + * Copyright (C) 2008 Texas Instruments. + * + * Author:zhoupeng + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tc_camera_module.h" + +#define I2C_M_WR 0 +#define I2C_MSG_MAX 300 + +static struct tc_camera_module *to_tc_camera_module(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tc_camera_module, sd); +} + +/* ======================================================================== */ + +static void tc_camera_module_reset( + struct tc_camera_module *cam_mod) +{ + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + cam_mod->inited = false; + cam_mod->active_config = NULL; + cam_mod->update_config = true; + cam_mod->frm_fmt_valid = false; + cam_mod->frm_intrvl_valid = false; + cam_mod->exp_config.auto_exp = false; + cam_mod->exp_config.auto_gain = false; + cam_mod->wb_config.auto_wb = false; + cam_mod->hflip = false; + cam_mod->vflip = false; + cam_mod->auto_adjust_fps = true; + cam_mod->rotation = 0; + cam_mod->ctrl_updt = 0; + cam_mod->state = TC_CAMERA_MODULE_POWER_OFF; + cam_mod->state_before_suspend = TC_CAMERA_MODULE_POWER_OFF; + + cam_mod->exp_config.exp_time = 0; + cam_mod->exp_config.gain = 0; + cam_mod->vts_cur = 0; +} + +/* ======================================================================== */ + +static void tc_camera_module_set_active_config( + struct tc_camera_module *cam_mod, + struct tc_camera_module_config *new_config) +{ + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + if (IS_ERR_OR_NULL(new_config)) { + cam_mod->active_config = new_config; + pltfrm_camera_module_pr_err(&cam_mod->sd, + "no active config\n"); + } else { + cam_mod->ctrl_updt &= TC_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP | + TC_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN | + TC_CAMERA_MODULE_CTRL_UPDT_AUTO_WB; + if (new_config->auto_exp_enabled != + cam_mod->exp_config.auto_exp) { + cam_mod->ctrl_updt |= + TC_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP; + cam_mod->exp_config.auto_exp = + new_config->auto_exp_enabled; + } + if (new_config->auto_gain_enabled != + cam_mod->exp_config.auto_gain) { + cam_mod->ctrl_updt |= + TC_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN; + cam_mod->exp_config.auto_gain = + new_config->auto_gain_enabled; + } + if (new_config->auto_wb_enabled != + cam_mod->wb_config.auto_wb) { + cam_mod->ctrl_updt |= + TC_CAMERA_MODULE_CTRL_UPDT_AUTO_WB; + cam_mod->wb_config.auto_wb = + new_config->auto_wb_enabled; + } + if (new_config != cam_mod->active_config) { + cam_mod->update_config = true; + cam_mod->active_config = new_config; + pltfrm_camera_module_pr_err(&cam_mod->sd, + "activating config '%s'\n", + cam_mod->active_config->name); + } + } +} + +/* ======================================================================== */ + +static struct tc_camera_module_config *tc_camera_module_find_config( + struct tc_camera_module *cam_mod, + struct v4l2_mbus_framefmt *fmt, + struct v4l2_subdev_frame_interval *frm_intrvl) +{ + u32 i; + unsigned long gcdiv; + struct v4l2_subdev_frame_interval norm_interval; + + if (!IS_ERR_OR_NULL(fmt)) + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "%dx%d, fmt code 0x%04x\n", + fmt->width, fmt->height, fmt->code); + + if (!IS_ERR_OR_NULL(frm_intrvl)) + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "frame interval %d/%d\n", + frm_intrvl->interval.numerator, + frm_intrvl->interval.denominator); + + for (i = 0; i < cam_mod->custom.num_configs; i++) { + if (!IS_ERR_OR_NULL(frm_intrvl)) { + gcdiv = gcd(cam_mod->custom.configs[i].frm_intrvl.interval.numerator, + cam_mod->custom.configs[i].frm_intrvl.interval.denominator); + norm_interval.interval.numerator = + cam_mod->custom.configs[i].frm_intrvl.interval.numerator / gcdiv; + norm_interval.interval.denominator = + cam_mod->custom.configs[i].frm_intrvl.interval.denominator / gcdiv; + if ((frm_intrvl->interval.numerator != + norm_interval.interval.numerator) || + (frm_intrvl->interval.denominator != + norm_interval.interval.denominator)) + continue; + } + if (!IS_ERR_OR_NULL(fmt)) { + if ((cam_mod->custom.configs[i].frm_fmt.width != + fmt->width) || + (cam_mod->custom.configs[i].frm_fmt.height != + fmt->height) || + (cam_mod->custom.configs[i].frm_fmt.code != + fmt->code)) { + continue; + } + } + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "found matching config %s\n", + cam_mod->custom.configs[i].name); + return &cam_mod->custom.configs[i]; + } + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "no matching config found\n"); + + return ERR_PTR(-EINVAL); +} + +/* ======================================================================== */ + +static int tc_camera_module_write_config( + struct tc_camera_module *cam_mod) +{ + int ret = 0; + struct tc_camera_module_reg *reg_table; + u32 reg_table_num_entries; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + if (IS_ERR_OR_NULL(cam_mod->active_config)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "no active sensor configuration"); + ret = -EFAULT; + goto err; + } + + if (!cam_mod->inited) { + cam_mod->active_config->soft_reset = true; + reg_table = cam_mod->active_config->reg_table; + reg_table_num_entries = + cam_mod->active_config->reg_table_num_entries; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "write config %s\n", + cam_mod->active_config->name); + } else { + if (cam_mod->active_config->reg_diff_table && + cam_mod->active_config->reg_diff_table_num_entries) { + cam_mod->active_config->soft_reset = false; + reg_table = cam_mod->active_config->reg_diff_table; + reg_table_num_entries = + cam_mod->active_config->reg_diff_table_num_entries; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "write config %s%s\n", + cam_mod->active_config->name, "_diff"); + } else { + cam_mod->active_config->soft_reset = true; + reg_table = cam_mod->active_config->reg_table; + reg_table_num_entries = + cam_mod->active_config->reg_table_num_entries; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "write config %s\n", + cam_mod->active_config->name); + } + } + + if (!IS_ERR_OR_NULL(cam_mod->custom.set_flip)) + cam_mod->custom.set_flip(cam_mod, + reg_table, reg_table_num_entries); + + ret = tc_camera_module_write_reglist(cam_mod, + reg_table, reg_table_num_entries); + if (IS_ERR_VALUE(ret)) + goto err; + ret = pltfrm_camera_module_patch_config(&cam_mod->sd, + &cam_mod->frm_fmt, + &cam_mod->frm_intrvl); + if (IS_ERR_VALUE(ret)) + goto err; + + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +static int tc_camera_module_attach( + struct tc_camera_module *cam_mod) +{ + int ret = 0; + struct tc_camera_module_custom_config *custom; + + pltfrm_camera_module_pr_err(&cam_mod->sd, "enter\n"); + + custom = &cam_mod->custom; + + if (custom->check_camera_id) { + tc_camera_module_s_power(&cam_mod->sd, 1); + ret = custom->check_camera_id(cam_mod); + tc_camera_module_s_power(&cam_mod->sd, 0); + if (ret != 0) + goto err; + } + + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + tc_camera_module_release(cam_mod); + return ret; +} + +/* ======================================================================== */ + +int tc_camera_module_try_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct tc_camera_module *cam_mod = to_tc_camera_module(sd); + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%dx%d, fmt code 0x%04x\n", + fmt->width, fmt->height, fmt->code); + + if (IS_ERR_OR_NULL(tc_camera_module_find_config(cam_mod, fmt, NULL))) { + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "format not supported\n"); + return -EINVAL; + } + pltfrm_camera_module_pr_debug(&cam_mod->sd, "format supported\n"); + + return 0; +} + +/* ======================================================================== */ + +int tc_camera_module_s_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct tc_camera_module *cam_mod = to_tc_camera_module(sd); + struct v4l2_mbus_framefmt *fmt = &format->format; + int ret = 0; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%dx%d, fmt code 0x%04x\n", + fmt->width, fmt->height, fmt->code); + + if (IS_ERR_OR_NULL(tc_camera_module_find_config(cam_mod, fmt, NULL))) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "format %dx%d, code 0x%04x, not supported\n", + fmt->width, fmt->height, fmt->code); + ret = -EINVAL; + goto err; + } + cam_mod->frm_fmt_valid = true; + cam_mod->frm_fmt = *fmt; + if (cam_mod->frm_intrvl_valid) { + tc_camera_module_set_active_config(cam_mod, + tc_camera_module_find_config(cam_mod, + fmt, &cam_mod->frm_intrvl)); + } + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +/* ======================================================================== */ + +int tc_camera_module_g_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct tc_camera_module *cam_mod = to_tc_camera_module(sd); + struct v4l2_mbus_framefmt *fmt = &format->format; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + if (cam_mod->active_config) { + fmt->code = cam_mod->active_config->frm_fmt.code; + fmt->width = cam_mod->active_config->frm_fmt.width; + fmt->height = cam_mod->active_config->frm_fmt.height; + return 0; + } + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "no active config\n"); + + return -1; +} + +/* ======================================================================== */ + +int tc_camera_module_s_frame_interval( + struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *interval) +{ + struct tc_camera_module *cam_mod = to_tc_camera_module(sd); + unsigned long gcdiv; + struct v4l2_subdev_frame_interval norm_interval; + int ret = 0; + + if ((interval->interval.denominator == 0) || + (interval->interval.numerator == 0)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "invalid frame interval %d/%d\n", + interval->interval.numerator, + interval->interval.denominator); + ret = -EINVAL; + goto err; + } + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d/%d (%dfps)\n", + interval->interval.numerator, interval->interval.denominator, + (interval->interval.denominator + + (interval->interval.numerator >> 1)) / + interval->interval.numerator); + + /* normalize interval */ + gcdiv = gcd(interval->interval.numerator, + interval->interval.denominator); + norm_interval.interval.numerator = + interval->interval.numerator / gcdiv; + norm_interval.interval.denominator = + interval->interval.denominator / gcdiv; + + if (IS_ERR_OR_NULL(tc_camera_module_find_config(cam_mod, + NULL, &norm_interval))) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "frame interval %d/%d not supported\n", + interval->interval.numerator, + interval->interval.denominator); + ret = -EINVAL; + goto err; + } + cam_mod->frm_intrvl_valid = true; + cam_mod->frm_intrvl = norm_interval; + if (cam_mod->frm_fmt_valid) { + tc_camera_module_set_active_config(cam_mod, + tc_camera_module_find_config(cam_mod, + &cam_mod->frm_fmt, interval)); + } + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +/* ======================================================================== */ + +int tc_camera_module_s_stream(struct v4l2_subdev *sd, int enable) +{ + int ret = 0; + struct tc_camera_module *cam_mod = to_tc_camera_module(sd); + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d\n", enable); + + if (enable) { + if (cam_mod->state == TC_CAMERA_MODULE_STREAMING) + return 0; + if (IS_ERR_OR_NULL(cam_mod->active_config)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "no active sensor configuration, cannot start streaming\n"); + ret = -EFAULT; + goto err; + } + if (cam_mod->state != TC_CAMERA_MODULE_SW_STANDBY) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "sensor is not powered on (in state %d), cannot start streaming\n", + cam_mod->state); + ret = -EINVAL; + goto err; + } + if (cam_mod->update_config) { + ret = tc_camera_module_write_config(cam_mod); + if (IS_ERR_VALUE(ret)) + goto err; + } + + ret = cam_mod->custom.enable_stream(cam_mod, true); + if (IS_ERR_VALUE(ret)) + goto err; + if (!cam_mod->inited && cam_mod->update_config) + cam_mod->inited = true; + cam_mod->update_config = false; + cam_mod->ctrl_updt = 0; + mdelay(cam_mod->custom.power_up_delays_ms[2]); + cam_mod->state = TC_CAMERA_MODULE_STREAMING; + } else { + int pclk; + int wait_ms; + struct isp_supplemental_sensor_mode_data timings; + + if (cam_mod->state != TC_CAMERA_MODULE_STREAMING) + return 0; + ret = cam_mod->custom.enable_stream(cam_mod, false); + if (IS_ERR_VALUE(ret)) + goto err; + + ret = tc_camera_module_ioctl(sd, + RK_VIDIOC_SENSOR_MODE_DATA, + &timings); + + cam_mod->state = TC_CAMERA_MODULE_SW_STANDBY; + + if (IS_ERR_VALUE(ret)) + goto err; + + pclk = timings.vt_pix_clk_freq_hz / 1000; + + if (!pclk) + goto err; + + wait_ms = + (timings.line_length_pck * + timings.frame_length_lines) / + pclk; + + /* + * wait for a frame period to make sure that there is + * no pending frame left. + */ + + msleep(wait_ms + 1); + } + + cam_mod->state_before_suspend = cam_mod->state; + + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +/* ======================================================================== */ + +int tc_camera_module_s_power(struct v4l2_subdev *sd, int on) +{ + int ret = 0; + struct tc_camera_module *cam_mod = to_tc_camera_module(sd); + struct v4l2_subdev *af_ctrl; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d\n", on); + + if (on) { + if (cam_mod->state == TC_CAMERA_MODULE_POWER_OFF) { + cam_mod->custom.s_power(cam_mod, 1); + ret = pltfrm_camera_module_s_power(&cam_mod->sd, 1); + if (!IS_ERR_VALUE(ret)) { + mdelay(cam_mod->custom.power_up_delays_ms[0]); + cam_mod->state = TC_CAMERA_MODULE_HW_STANDBY; + } + } + if (cam_mod->state == TC_CAMERA_MODULE_HW_STANDBY) { + ret = pltfrm_camera_module_set_pin_state(&cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_PD, + PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE); + if (!IS_ERR_VALUE(ret)) { + mdelay(cam_mod->custom.power_up_delays_ms[1]); + cam_mod->state = TC_CAMERA_MODULE_SW_STANDBY; + if (!IS_ERR_OR_NULL( + cam_mod->custom.init_common) && + cam_mod->custom.init_common( + cam_mod)) + usleep_range(1000, 1500); + + af_ctrl = pltfrm_camera_module_get_af_ctrl(sd); + if (!IS_ERR_OR_NULL(af_ctrl)) { + v4l2_subdev_call(af_ctrl, + core, init, 0); + } + } + } + if (cam_mod->update_config) { + tc_camera_module_write_config(cam_mod); + cam_mod->update_config = false; + } + } else { + if (cam_mod->state == TC_CAMERA_MODULE_STREAMING) { + ret = tc_camera_module_s_stream(sd, 0); + if (!IS_ERR_VALUE(ret)) + cam_mod->state = TC_CAMERA_MODULE_SW_STANDBY; + } + if (cam_mod->state == TC_CAMERA_MODULE_SW_STANDBY) { + ret = pltfrm_camera_module_set_pin_state( + &cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_PD, + PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE); + + if (!IS_ERR_VALUE(ret)) + cam_mod->state = TC_CAMERA_MODULE_HW_STANDBY; + } + if (cam_mod->state == TC_CAMERA_MODULE_HW_STANDBY) { + cam_mod->custom.s_power(cam_mod, 0); + ret = pltfrm_camera_module_s_power(&cam_mod->sd, 0); + if (!IS_ERR_VALUE(ret)) { + cam_mod->state = TC_CAMERA_MODULE_POWER_OFF; + tc_camera_module_reset(cam_mod); + } + } + } + + cam_mod->state_before_suspend = cam_mod->state; + + if (IS_ERR_VALUE(ret)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "%s failed, camera left in state %d\n", + on ? "on" : "off", cam_mod->state); + goto err; + } else { + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "camera powered %s\n", on ? "on" : "off"); + } + + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +/* ======================================================================== */ + +int tc_camera_module_g_ctrl(struct v4l2_subdev *sd, + struct v4l2_control *ctrl) +{ + struct tc_camera_module *cam_mod = to_tc_camera_module(sd); + int ret; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, " id 0x%x\n", ctrl->id); + + if (ctrl->id == V4L2_CID_MIN_BUFFERS_FOR_CAPTURE) { + ctrl->value = 2; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_MIN_BUFFERS_FOR_CAPTURE %d\n", + ctrl->value); + return 0; + } + + if (ctrl->id == V4L2_CID_FLASH_LED_MODE) { + ctrl->value = cam_mod->exp_config.flash_mode; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_FLASH_LED_MODE %d\n", + ctrl->value); + return 0; + } + + if (IS_ERR_OR_NULL(cam_mod->active_config)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "no active configuration\n"); + return -EFAULT; + } + + if (ctrl->id == RK_V4L2_CID_VBLANKING) { + ctrl->value = cam_mod->active_config->v_blanking_time_us; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "RK_V4L2_CID_VBLANKING %d\n", + ctrl->value); + return 0; + } + + if ((cam_mod->state != TC_CAMERA_MODULE_SW_STANDBY) && + (cam_mod->state != TC_CAMERA_MODULE_STREAMING)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "cannot get controls when camera is off\n"); + return -EFAULT; + } + + if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) { + struct v4l2_subdev *af_ctrl; + + af_ctrl = pltfrm_camera_module_get_af_ctrl(sd); + if (!IS_ERR_OR_NULL(af_ctrl)) { + ret = v4l2_subdev_call(af_ctrl, core, g_ctrl, ctrl); + return ret; + } + } + + if (!IS_ERR_OR_NULL(cam_mod->custom.g_ctrl)) { + ret = cam_mod->custom.g_ctrl(cam_mod, ctrl->id); + if (IS_ERR_VALUE(ret)) + return ret; + } + + switch (ctrl->id) { + case V4L2_CID_GAIN: + ctrl->value = cam_mod->exp_config.gain; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_GAIN %d\n", + ctrl->value); + break; + case V4L2_CID_EXPOSURE: + ctrl->value = cam_mod->exp_config.exp_time; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_EXPOSURE %d\n", + ctrl->value); + break; + case V4L2_CID_WHITE_BALANCE_TEMPERATURE: + ctrl->value = cam_mod->wb_config.temperature; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_WHITE_BALANCE_TEMPERATURE %d\n", + ctrl->value); + break; + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: + ctrl->value = cam_mod->wb_config.preset_id; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE %d\n", + ctrl->value); + break; + case V4L2_CID_AUTOGAIN: + ctrl->value = cam_mod->exp_config.auto_gain; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_AUTOGAIN %d\n", + ctrl->value); + break; + case V4L2_CID_EXPOSURE_AUTO: + ctrl->value = cam_mod->exp_config.auto_exp; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_EXPOSURE_AUTO %d\n", + ctrl->value); + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + ctrl->value = cam_mod->wb_config.auto_wb; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_AUTO_WHITE_BALANCE %d\n", + ctrl->value); + break; + case V4L2_CID_FOCUS_ABSOLUTE: + ctrl->value = cam_mod->af_config.abs_pos; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_FOCUS_ABSOLUTE %d\n", + ctrl->value); + break; + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + /* TBD */ + /* fallthrough */ + default: + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "failed, unknown ctrl %d\n", ctrl->id); + return -EINVAL; + } + + return 0; +} + +static int flash_light_ctrl( + struct v4l2_subdev *sd, + struct tc_camera_module *cam_mod, + int value) +{ + return 0; +} + +/* ======================================================================== */ + +int tc_camera_module_s_ext_ctrls( + struct v4l2_subdev *sd, + struct v4l2_ext_controls *ctrls) +{ + int i; + int ctrl_cnt = 0; + struct tc_camera_module *cam_mod = to_tc_camera_module(sd); + int ret = 0; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + if (ctrls->count == 0) + return -EINVAL; + + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl; + u32 ctrl_updt = 0; + + ctrl = &ctrls->controls[i]; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + ctrl_updt = TC_CAMERA_MODULE_CTRL_UPDT_GAIN; + cam_mod->exp_config.gain = ctrl->value; + pltfrm_camera_module_pr_debug( + &cam_mod->sd, + "V4L2_CID_GAIN %d\n", + ctrl->value); + break; + case RK_V4L2_CID_GAIN_PERCENT: + ctrl_updt = TC_CAMERA_MODULE_CTRL_UPDT_GAIN; + cam_mod->exp_config.gain_percent = ctrl->value; + break; + case V4L2_CID_FLASH_LED_MODE: + ret = flash_light_ctrl(sd, cam_mod, ctrl->value); + if (ret == 0) { + cam_mod->exp_config.flash_mode = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_FLASH_LED_MODE %d\n", + ctrl->value); + } + break; + case V4L2_CID_EXPOSURE: + ctrl_updt = TC_CAMERA_MODULE_CTRL_UPDT_EXP_TIME; + cam_mod->exp_config.exp_time = ctrl->value; + pltfrm_camera_module_pr_debug + (&cam_mod->sd, + "V4L2_CID_EXPOSURE %d\n", + ctrl->value); + break; + case V4L2_CID_WHITE_BALANCE_TEMPERATURE: + ctrl_updt = TC_CAMERA_MODULE_CTRL_UPDT_WB_TEMPERATURE; + cam_mod->wb_config.temperature = ctrl->value; + pltfrm_camera_module_pr_debug( + &cam_mod->sd, + "V4L2_CID_WHITE_BALANCE_TEMPERATURE %d\n", + ctrl->value); + break; + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: + ctrl_updt = TC_CAMERA_MODULE_CTRL_UPDT_PRESET_WB; + cam_mod->wb_config.preset_id = ctrl->value; + pltfrm_camera_module_pr_debug( + &cam_mod->sd, + "V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE %d\n", + ctrl->value); + break; + case V4L2_CID_AUTOGAIN: + ctrl_updt = TC_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN; + cam_mod->exp_config.auto_gain = ctrl->value; + pltfrm_camera_module_pr_debug( + &cam_mod->sd, + "V4L2_CID_AUTOGAIN %d\n", + ctrl->value); + break; + case V4L2_CID_EXPOSURE_AUTO: + ctrl_updt = TC_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP; + cam_mod->exp_config.auto_exp = ctrl->value; + pltfrm_camera_module_pr_debug( + &cam_mod->sd, + "V4L2_CID_EXPOSURE_AUTO %d\n", + ctrl->value); + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + ctrl_updt = TC_CAMERA_MODULE_CTRL_UPDT_AUTO_WB; + cam_mod->wb_config.auto_wb = ctrl->value; + pltfrm_camera_module_pr_debug( + &cam_mod->sd, + "V4L2_CID_AUTO_WHITE_BALANCE %d\n", + ctrl->value); + break; + case RK_V4L2_CID_AUTO_FPS: + cam_mod->auto_adjust_fps = ctrl->value; + pltfrm_camera_module_pr_debug( + &cam_mod->sd, + "RK_V4L2_CID_AUTO_FPS %d\n", + ctrl->value); + break; + case V4L2_CID_FOCUS_ABSOLUTE: + { + struct v4l2_subdev *af_ctrl; + + af_ctrl = pltfrm_camera_module_get_af_ctrl(sd); + if (!IS_ERR_OR_NULL(af_ctrl)) { + struct v4l2_control single_ctrl; + + single_ctrl.id = + V4L2_CID_FOCUS_ABSOLUTE; + single_ctrl.value = ctrl->value; + ret = v4l2_subdev_call(af_ctrl, + core, s_ctrl, &single_ctrl); + return ret; + } + } + ctrl_updt = + TC_CAMERA_MODULE_CTRL_UPDT_FOCUS_ABSOLUTE; + cam_mod->af_config.abs_pos = ctrl->value; + pltfrm_camera_module_pr_debug( + &cam_mod->sd, + "V4L2_CID_FOCUS_ABSOLUTE %d\n", + ctrl->value); + break; + case V4L2_CID_HFLIP: + if (ctrl->value) + cam_mod->hflip = true; + else + cam_mod->hflip = false; + break; + case V4L2_CID_VFLIP: + if (ctrl->value) + cam_mod->vflip = true; + else + cam_mod->vflip = false; + break; + default: + pltfrm_camera_module_pr_warn( + &cam_mod->sd, + "ignoring unknown ctrl 0x%x\n", + ctrl->id); + break; + } + + if (cam_mod->state != TC_CAMERA_MODULE_SW_STANDBY && + cam_mod->state != TC_CAMERA_MODULE_STREAMING) + cam_mod->ctrl_updt |= ctrl_updt; + else if (ctrl_updt) + ctrl_cnt++; + } + + /* if camera module is already streaming, write through */ + if (ctrl_cnt && + (cam_mod->state == TC_CAMERA_MODULE_STREAMING || + cam_mod->state == TC_CAMERA_MODULE_SW_STANDBY)) { + struct tc_camera_module_ext_ctrls tc_ctrls; + + tc_ctrls.ctrls = + (struct tc_camera_module_ext_ctrl *) + kmalloc(ctrl_cnt * sizeof(struct tc_camera_module_ext_ctrl), + GFP_KERNEL); + + if (tc_ctrls.ctrls) { + for (i = 0; i < ctrl_cnt; i++) { + tc_ctrls.ctrls[i].id = ctrls->controls[i].id; + tc_ctrls.ctrls[i].value = + ctrls->controls[i].value; + } + + tc_ctrls.count = ctrl_cnt; + + ret = cam_mod->custom.s_ext_ctrls(cam_mod, &tc_ctrls); + + kfree(tc_ctrls.ctrls); + } else { + ret = -ENOMEM; + } + if (IS_ERR_VALUE(ret)) + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "failed with error %d\n", ret); + } + + return ret; +} + +/* ======================================================================== */ + +int tc_camera_module_s_ctrl( + struct v4l2_subdev *sd, + struct v4l2_control *ctrl) +{ + struct tc_camera_module *cam_mod = to_tc_camera_module(sd); + struct v4l2_ext_control ext_ctrl[1]; + struct v4l2_ext_controls ext_ctrls; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "0x%x 0x%x\n", ctrl->id, ctrl->value); + + ext_ctrl[0].id = ctrl->id; + ext_ctrl[0].value = ctrl->value; + + ext_ctrls.count = 1; + ext_ctrls.controls = ext_ctrl; + + return tc_camera_module_s_ext_ctrls(sd, &ext_ctrls); +} + +/* ======================================================================== */ + +long tc_camera_module_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, + void *arg) +{ + struct tc_camera_module *cam_mod = to_tc_camera_module(sd); + int ret; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "cmd: 0x%x\n", cmd); + + if (cmd == RK_VIDIOC_SENSOR_MODE_DATA) { + struct tc_camera_module_timings tc_timings; + struct isp_supplemental_sensor_mode_data *timings = + (struct isp_supplemental_sensor_mode_data *)arg; + + if (cam_mod->custom.g_timings) + ret = cam_mod->custom.g_timings(cam_mod, &tc_timings); + else + ret = -EPERM; + + if (IS_ERR_VALUE(ret)) { + pltfrm_camera_module_pr_err( + &cam_mod->sd, + "failed with error %d\n", + ret); + return ret; + } + + timings->sensor_output_width = tc_timings.sensor_output_width; + timings->sensor_output_height = tc_timings.sensor_output_height; + timings->crop_horizontal_start = + tc_timings.crop_horizontal_start; + timings->crop_vertical_start = tc_timings.crop_vertical_start; + timings->crop_horizontal_end = tc_timings.crop_horizontal_end; + timings->crop_vertical_end = tc_timings.crop_vertical_end; + timings->line_length_pck = tc_timings.line_length_pck; + timings->frame_length_lines = tc_timings.frame_length_lines; + timings->vt_pix_clk_freq_hz = tc_timings.vt_pix_clk_freq_hz; + timings->binning_factor_x = tc_timings.binning_factor_x; + timings->binning_factor_y = tc_timings.binning_factor_y; + timings->coarse_integration_time_max_margin = + tc_timings.coarse_integration_time_max_margin; + timings->coarse_integration_time_min = + tc_timings.coarse_integration_time_min; + timings->fine_integration_time_max_margin = + tc_timings.fine_integration_time_max_margin; + timings->fine_integration_time_min = + tc_timings.fine_integration_time_min; + + if (cam_mod->custom.g_exposure_valid_frame) + timings->exposure_valid_frame = + cam_mod->custom.g_exposure_valid_frame(cam_mod); + if (cam_mod->exp_config.exp_time) + timings->exp_time = cam_mod->exp_config.exp_time; + else + timings->exp_time = tc_timings.exp_time; + if (cam_mod->exp_config.gain) + timings->gain = cam_mod->exp_config.gain; + else + timings->gain = tc_timings.gain; + return ret; + } else if (cmd == PLTFRM_CIFCAM_G_ITF_CFG) { + struct pltfrm_cam_itf *itf_cfg = (struct pltfrm_cam_itf *)arg; + struct tc_camera_module_config *config; + + if (cam_mod->custom.num_configs <= 0) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "cam_mod->custom.num_configs is NULL, Get interface config failed!\n"); + return -EINVAL; + } + + if (IS_ERR_OR_NULL(cam_mod->active_config)) + config = &cam_mod->custom.configs[0]; + else + config = cam_mod->active_config; + + *itf_cfg = config->itf_cfg; + + pltfrm_camera_module_ioctl(sd, PLTFRM_CIFCAM_G_ITF_CFG, arg); + return 0; + } else if (cmd == PLTFRM_CIFCAM_ATTACH) { + tc_camera_module_init(cam_mod, &cam_mod->custom); + pltfrm_camera_module_ioctl(sd, cmd, arg); + return tc_camera_module_attach(cam_mod); + } + + ret = pltfrm_camera_module_ioctl(sd, cmd, arg); + return ret; +} + +/* ======================================================================== */ + +int tc_camera_module_get_flip_mirror( + struct tc_camera_module *cam_mod) +{ + return pltfrm_camera_module_get_flip_mirror(&cam_mod->sd); +} + +/* ======================================================================== */ + +int tc_camera_module_enum_frameintervals( + struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct tc_camera_module *cam_mod = to_tc_camera_module(sd); + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d\n", fie->index); + + if (fie->index >= cam_mod->custom.num_configs) + return -EINVAL; + fie->code = + cam_mod->custom.configs[fie->index].frm_fmt.code; + fie->width = + cam_mod->custom.configs[fie->index].frm_fmt.width; + fie->height = + cam_mod->custom.configs[fie->index].frm_fmt.height; + fie->interval.numerator = cam_mod->custom.configs[fie->index].frm_intrvl.interval.numerator; + fie->interval.denominator = cam_mod->custom.configs[fie->index].frm_intrvl.interval.denominator; + return 0; +} + +/* ======================================================================== */ +static int tc_camera_module_i2c_write(struct v4l2_subdev *sd, u16 reg, + u8 *values, u32 len) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret, i; + struct i2c_msg msg[1]; + unsigned char data[32]; + int retries; + + if (!client->adapter) { + pltfrm_camera_module_pr_err(sd, "client->adapter NULL\n"); + return -ENODEV; + } + + for (retries = 0; retries < 5; retries++) { + msg->addr = client->addr; + msg->flags = I2C_M_WR; + msg->len = 2 + len; + msg->buf = data; + + /* high byte goes out first */ + data[0] = (u8)(reg >> 8); + data[1] = (u8)(reg & 0xff); + for (i = 0; i < len; i++) + data[2 + i] = values[i]; + + ret = i2c_transfer(client->adapter, msg, 1); + if (ret == 1) { + pltfrm_camera_module_pr_debug(sd, + "i2c write to offset 0x%08x success\n", reg); + return 0; + } + + pltfrm_camera_module_pr_err(sd, + "retrying I2C... %d\n", retries); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(20)); + } + pltfrm_camera_module_pr_err(sd, + "i2c write to offset 0x%08x failed with error %d\n", reg, ret); + return ret; +} + +/* ======================================================================== */ +static int tc_camera_module_i2c_read(struct v4l2_subdev *sd, u16 reg, + u32 *value, u32 len) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + unsigned char data[4] = {0, 0, 0, 0}; + struct i2c_msg msg[1]; + + if (!client->adapter) { + pltfrm_camera_module_pr_err(sd, "client->adapter NULL\n"); + return -ENODEV; + } + + msg->addr = client->addr; + msg->flags = I2C_M_WR; + msg->len = 2; + msg->buf = data; + + /* High byte goes out first */ + data[0] = (u8)(reg >> 8); + data[1] = (u8)(reg & 0xff); + + ret = i2c_transfer(client->adapter, msg, 1); + if (ret >= 0) { + mdelay(3); + msg->flags = I2C_M_RD; + msg->len = len; + msg->buf = data; + i2c_transfer(client->adapter, msg, 1); + } + if (ret >= 0) { + if (len == 1) + *value = data[0]; + else if (len == 2) + *value = data[0] + (data[1] << 8); + else + *value = data[0] + (data[1] << 8) + + (data[2] << 16) + (data[3] << 24); + return 0; + } + + return ret; +} + +/* ===============================I2c==================================== */ + +int tc_camera_module_write8_reg( + struct tc_camera_module *cam_mod, + u16 reg, + u8 val) +{ + return tc_camera_module_i2c_write(&cam_mod->sd, reg, &val, 1); +} + +int tc_camera_module_write16_reg( + struct tc_camera_module *cam_mod, + u16 reg, + u16 val) +{ + return tc_camera_module_i2c_write(&cam_mod->sd, reg, (u8 *)&val, 2); +} + +int tc_camera_module_write32_reg( + struct tc_camera_module *cam_mod, + u16 reg, + u32 val) +{ + return tc_camera_module_i2c_write(&cam_mod->sd, reg, (u8 *)&val, 4); +} + +u8 tc_camera_module_read8_reg( + struct tc_camera_module *cam_mod, + u16 reg) +{ + u32 val; + u8 ret; + + tc_camera_module_i2c_read(&cam_mod->sd, reg, &val, 1); + ret = val & 0xff; + + return ret; +} + +u16 tc_camera_module_read16_reg( + struct tc_camera_module *cam_mod, + u16 reg) +{ + u32 val; + u16 ret; + + tc_camera_module_i2c_read(&cam_mod->sd, reg, &val, 2); + ret = val & 0xffff; + + return ret; +} + +u32 tc_camera_module_read32_reg( + struct tc_camera_module *cam_mod, + u16 reg) +{ + u32 val; + + tc_camera_module_i2c_read(&cam_mod->sd, reg, &val, 4); + + return val; +} + +int tc_camera_module_read_reg_table( + struct tc_camera_module *cam_mod, + u16 reg, + u32 *val) +{ + int i; + + if (cam_mod->state == TC_CAMERA_MODULE_STREAMING) + return tc_camera_module_i2c_read(&cam_mod->sd, + reg, val, 1); + + if (!IS_ERR_OR_NULL(cam_mod->active_config)) { + for ( + i = cam_mod->active_config->reg_table_num_entries - 1; + i > 0; + i--) { + if (cam_mod->active_config->reg_table[i].reg == reg) { + *val = cam_mod->active_config->reg_table[i].val; + return 0; + } + } + } + + if (cam_mod->state == TC_CAMERA_MODULE_SW_STANDBY) + return tc_camera_module_i2c_read(&cam_mod->sd, + reg, val, 1); + + return -EFAULT; +} + +int tc_camera_module_write_reglist( + struct tc_camera_module *cam_mod, + const struct tc_camera_module_reg reglist[], + int len) +{ + int i = 0; + + for (i = 0; i < len; i++) { + switch (reglist[i].flag) { + case TC_CAMERA_MODULE_REG_TYPE_DATA: + switch (reglist[i].len) { + case TC_CAMERA_MOUDLE_REG_VALUE_LEN_8BIT: + tc_camera_module_write8_reg(cam_mod, reglist[i].reg, (u8)reglist[i].val); + break; + case TC_CAMERA_MOUDLE_REG_VALUE_LEN_16BIT: + tc_camera_module_write16_reg(cam_mod, reglist[i].reg, (u16)reglist[i].val); + break; + case TC_CAMERA_MOUDLE_REG_VALUE_LEN_32BIT: + tc_camera_module_write32_reg(cam_mod, reglist[i].reg, (u32)reglist[i].val); + break; + default: + pltfrm_camera_module_pr_err(&cam_mod->sd, "unknown value len, default 8bit\n"); + tc_camera_module_write8_reg(cam_mod, reglist[i].reg, (u8)reglist[i].val); + break; + } + break; + case PLTFRM_CAMERA_MODULE_REG_TYPE_TIMEOUT: + mdelay(reglist[i].val); + break; + default: + pltfrm_camera_module_pr_err(&cam_mod->sd, "unknown command\n"); + return -1; + } + } + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "write reg list len %d\n", len); + return 0; +} + +int tc_camera_module_init(struct tc_camera_module *cam_mod, + struct tc_camera_module_custom_config *custom) +{ + int ret = 0; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + tc_camera_module_reset(cam_mod); + + if (IS_ERR_OR_NULL(custom->s_ctrl) || + IS_ERR_OR_NULL(custom->g_ctrl)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "mandatory callback function is missing\n"); + ret = -EINVAL; + goto err; + } + + ret = pltfrm_camera_module_init(&cam_mod->sd, &cam_mod->pltfm_data); + if (IS_ERR_VALUE(ret)) + goto err; + + ret = pltfrm_camera_module_set_pin_state(&cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_PD, + PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE); + ret = pltfrm_camera_module_set_pin_state(&cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_RESET, + PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE); + if (IS_ERR_VALUE(ret)) { + tc_camera_module_release(cam_mod); + goto err; + } + + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +void tc_camera_module_release(struct tc_camera_module *cam_mod) +{ + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + if (cam_mod->otp_work.wq) { + flush_workqueue(cam_mod->otp_work.wq); + destroy_workqueue(cam_mod->otp_work.wq); + cam_mod->otp_work.wq = NULL; + } + + cam_mod->custom.configs = NULL; + + pltfrm_camera_module_release(&cam_mod->sd); + v4l2_device_unregister_subdev(&cam_mod->sd); +} diff --git a/drivers/media/i2c/soc_camera/rockchip/tc_camera_module.h b/drivers/media/i2c/soc_camera/rockchip/tc_camera_module.h new file mode 100644 index 000000000000..c4c1d25df250 --- /dev/null +++ b/drivers/media/i2c/soc_camera/rockchip/tc_camera_module.h @@ -0,0 +1,325 @@ +/* + * tc_camera_module.h + * + * Generic toshiba sensor driver + * + * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd. + * + * Copyright (C) 2012-2014 Intel Mobile Communications GmbH + * + * Copyright (C) 2008 Texas Instruments. + * + * Author:zhoupeng + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ + +#ifndef TC_CAMERA_MODULE_H +#define TC_CAMERA_MODULE_H +#include +#include +#include +#include + +/* + * TODO: references to v4l2 should be reomved from here and go into a + * platform dependent wrapper + */ + +#define TC_CAMERA_MODULE_REG_TYPE_DATA PLTFRM_CAMERA_MODULE_REG_TYPE_DATA +#define TC_CAMERA_MODULE_REG_TYPE_TIMEOUT PLTFRM_CAMERA_MODULE_REG_TYPE_TIMEOUT +#define TC_CAMERA_MOUDLE_REG_VALUE_LEN_8BIT 1 +#define TC_CAMERA_MOUDLE_REG_VALUE_LEN_16BIT 2 +#define TC_CAMERA_MOUDLE_REG_VALUE_LEN_32BIT 4 +#define tc_camera_module_csi_config +#define TC_FLIP_BIT_MASK 0x2 +#define TC_MIRROR_BIT_MASK 0x1 + +#define TC_CAMERA_MODULE_CTRL_UPDT_GAIN 0x01 +#define TC_CAMERA_MODULE_CTRL_UPDT_EXP_TIME 0x02 +#define TC_CAMERA_MODULE_CTRL_UPDT_WB_TEMPERATURE 0x04 +#define TC_CAMERA_MODULE_CTRL_UPDT_AUTO_WB 0x08 +#define TC_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN 0x10 +#define TC_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP 0x20 +#define TC_CAMERA_MODULE_CTRL_UPDT_FOCUS_ABSOLUTE 0x40 +#define TC_CAMERA_MODULE_CTRL_UPDT_PRESET_WB 0x80 + +enum tc_camera_module_state { + TC_CAMERA_MODULE_POWER_OFF = 0, + TC_CAMERA_MODULE_HW_STANDBY = 1, + TC_CAMERA_MODULE_SW_STANDBY = 2, + TC_CAMERA_MODULE_STREAMING = 3 +}; + +struct tc_camera_module; + +struct tc_camera_module_reg { + u32 flag; + u16 reg; + u32 val; + u8 len; +}; + +struct tc_camera_module_timings { + /* public */ + u32 coarse_integration_time_min; + u32 coarse_integration_time_max_margin; + u32 fine_integration_time_min; + u32 fine_integration_time_max_margin; + u32 frame_length_lines; + u32 line_length_pck; + u32 vt_pix_clk_freq_hz; + u32 sensor_output_width; + u32 sensor_output_height; + u32 crop_horizontal_start; /* Sensor crop start cord. (x0,y0) */ + u32 crop_vertical_start; + u32 crop_horizontal_end; /* Sensor crop end cord. (x1,y1) */ + u32 crop_vertical_end; + u8 binning_factor_x; + u8 binning_factor_y; + u32 exp_time; + u32 gain; +}; + +struct tc_camera_module_config { + const char *name; + struct v4l2_mbus_framefmt frm_fmt; + struct v4l2_subdev_frame_interval frm_intrvl; + bool auto_exp_enabled; + bool auto_gain_enabled; + bool auto_wb_enabled; + struct tc_camera_module_reg *reg_table; + u32 reg_table_num_entries; + struct tc_camera_module_reg *reg_diff_table; + u32 reg_diff_table_num_entries; + u32 v_blanking_time_us; + u32 line_length_pck; + u32 frame_length_lines; + struct tc_camera_module_timings timings; + bool soft_reset; + bool ignore_measurement_check; + + struct pltfrm_cam_itf itf_cfg; +}; + +struct tc_camera_module_exp_config { + s32 exp_time; + bool auto_exp; + u16 gain; + u16 gain_percent; + bool auto_gain; + enum v4l2_flash_led_mode flash_mode; +}; + +struct tc_camera_module_wb_config { + u32 temperature; + u32 preset_id; + bool auto_wb; +}; + +struct tc_camera_module_af_config { + u32 abs_pos; + u32 rel_pos; +}; + +struct tc_camera_module_ext_ctrl { + /* public */ + u32 id; + u32 value; + __u32 reserved2[1]; +}; + +struct tc_camera_module_ext_ctrls { + /* public */ + u32 count; + struct tc_camera_module_ext_ctrl *ctrls; +}; + +/* + * start_streaming: (mandatory) will be called when sensor should be + * put into streaming mode right after the base config has been + * written to the sensor. After a successful call of this function + * the sensor should start delivering frame data. + * + * stop_streaming: (mandatory) will be called when sensor should stop + * delivering data. After a successful call of this function the + * sensor should not deliver any more frame data. + * + * check_camera_id: (optional) will be called when the sensor is + * powered on. If prtcided should check the sensor ID/version + * required by the custom driver. Register access should be + * possible when this function is invoked. + * + * s_ctrl: (mandatory) will be called at the successful end of + * tc_camera_module_s_ctrl with the ctrl_id as argument. + * + * priv: (optional) for private data used by the custom driver. + */ +struct tc_camera_module_custom_config { + int (*check_camera_id)(struct tc_camera_module *cam_mod); + int (*s_ctrl)(struct tc_camera_module *cam_mod, u32 ctrl_id); + int (*g_ctrl)(struct tc_camera_module *cam_mod, u32 ctrl_id); + int (*g_timings)(struct tc_camera_module *cam_mod, + struct tc_camera_module_timings *timings); + int (*g_exposure_valid_frame)(struct tc_camera_module *cam_mod); + int (*s_ext_ctrls)(struct tc_camera_module *cam_mod, + struct tc_camera_module_ext_ctrls *ctrls); + int (*set_flip)( + struct tc_camera_module *cam_mod, + struct tc_camera_module_reg reglist[], + int len); + int (*init_common)(struct tc_camera_module *cam_mod); + int (*read_otp)(struct tc_camera_module *cam_mod); + int (*enable_stream)(struct tc_camera_module *cam_mod, bool enable); + int (*s_power)(struct tc_camera_module *cam_mod, bool enable); + struct tc_camera_module_config *configs; + u32 num_configs; + u32 power_up_delays_ms[3]; + void *priv; +}; + +struct tc_camera_module_otp_work { + struct work_struct work; + struct workqueue_struct *wq; + void *cam_mod; +}; + +struct tc_camera_module { + /* public */ + struct v4l2_subdev sd; + struct v4l2_mbus_framefmt frm_fmt; + struct v4l2_subdev_frame_interval frm_intrvl; + struct tc_camera_module_exp_config exp_config; + struct tc_camera_module_wb_config wb_config; + struct tc_camera_module_af_config af_config; + struct tc_camera_module_custom_config custom; + enum tc_camera_module_state state; + enum tc_camera_module_state state_before_suspend; + struct tc_camera_module_config *active_config; + struct tc_camera_module_otp_work otp_work; + u32 ctrl_updt; + u32 vts_cur; + u32 vts_min; + bool auto_adjust_fps; + bool update_config; + bool frm_fmt_valid; + bool frm_intrvl_valid; + bool hflip; + bool vflip; + u32 rotation; + void *pltfm_data; + bool inited; +}; + +struct tc35x_priv { + struct regmap *regmap; + struct i2c_client *client; + struct device *dev; + struct gpio_desc *gpio_power; + struct gpio_desc *gpio_power18; + struct gpio_desc *gpio_power33; + struct gpio_desc *gpio_csi_ctl; + struct gpio_desc *gpio_reset; + struct gpio_desc *gpio_stanby; + struct gpio_desc *gpio_int; +}; + +#define tc_camera_module_pr_info(cam_mod, fmt, arg...) \ + pltfrm_camera_module_pr_info(&(cam_mod)->sd, fmt, ## arg) +#define tc_camera_module_pr_debug(cam_mod, fmt, arg...) \ + pltfrm_camera_module_pr_debug(&(cam_mod)->sd, fmt, ## arg) +#define tc_camera_module_pr_warn(cam_mod, fmt, arg...) \ + pltfrm_camera_module_pr_warn(&(cam_mod)->sd, fmt, ## arg) +#define tc_camera_module_pr_err(cam_mod, fmt, arg...) \ + pltfrm_camera_module_pr_err(&(cam_mod)->sd, fmt, ## arg) + +int tc_camera_module_write8_reg( + struct tc_camera_module *cam_mod, + u16 reg, + u8 val); + +int tc_camera_module_write16_reg( + struct tc_camera_module *cam_mod, + u16 reg, + u16 val); + +int tc_camera_module_write32_reg( + struct tc_camera_module *cam_mod, + u16 reg, + u32 val); + +u8 tc_camera_module_read8_reg( + struct tc_camera_module *cam_mod, + u16 reg); + +u16 tc_camera_module_read16_reg( + struct tc_camera_module *cam_mod, + u16 reg); + +u32 tc_camera_module_read32_reg( + struct tc_camera_module *cam_mod, + u16 reg); + +int tc_camera_module_read_reg_table( + struct tc_camera_module *cam_mod, + u16 reg, + u32 *val); + +int tc_camera_module_write_reglist( + struct tc_camera_module *cam_mod, + const struct tc_camera_module_reg reglist[], + int len); + +int tc_camera_module_s_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format); + +int tc_camera_module_g_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format); + +int tc_camera_module_s_frame_interval( + struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *interval); + +int tc_camera_module_s_stream( + struct v4l2_subdev *sd, + int enable); + +int tc_camera_module_s_power( + struct v4l2_subdev *sd, + int on); + +int tc_camera_module_g_ctrl( + struct v4l2_subdev *sd, + struct v4l2_control *ctrl); + +int tc_camera_module_s_ctrl( + struct v4l2_subdev *sd, + struct v4l2_control *ctrl); + +int tc_camera_module_s_ext_ctrls( + struct v4l2_subdev *sd, + struct v4l2_ext_controls *ctrls); + +int tc_camera_module_enum_frameintervals( + struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie); + +int tc_camera_module_init( + struct tc_camera_module *cam_mod, + struct tc_camera_module_custom_config *custom); + +void tc_camera_module_release( + struct tc_camera_module *cam_mod); + +long tc_camera_module_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, + void *arg); + +int tc_camera_module_get_flip_mirror( + struct tc_camera_module *cam_mod); +#endif