--- /dev/null
+/*
+ * 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 <benjo.zhou@rock-chips.com>
+ *
+ * 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 <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf-core.h>
+#include <linux/slab.h>
+#include <linux/of_gpio.h>
+#include <linux/debugfs.h>
+#include <media/v4l2-controls_rockchip.h>
+#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[] = {
+ //<!-- Software Reset -->
+ {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},
+ //<!-- PLL Setting -->
+ {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},
+ //<!-- Misc Setting -->
+ {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},
+ //<!-- Interrupt Control -->
+ {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0014, 0x0000, 2},
+ {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x0016, 0x05FF, 2},
+ //<!-- change 749 mipi out drive capability,
+ //to avoid pic error -->
+ {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},
+ //<!-- CSI Lane Enable -->
+ {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},
+ //<!-- CSI Transition Timing -->
+ {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},
+ //<!-- Data ID Setting -->
+ //<!-- HDMI Interrupt Mask -->
+ //<!-- HDMI Audio REFCLK -->
+ {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},
+ //<!-- HDMI PHY -->
+ {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},
+ //<!-- HDMI SYSTEM -->
+ {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},
+ //<!-- EDID -->
+ {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},
+ //<!-- EDID Data -->
+ {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},
+ //<!-- HDCP Setting -->
+ //<!-- Video Setting -->
+ {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x8573, 0xC1, 1},
+ //<!-- HDMI Audio Setting -->
+ {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},
+ //<!-- Info Frame Extraction -->
+ {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},
+ //<!-- Let HDMI Source start access -->
+ {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x854A, 0x01, 1},
+ //<!-- VIP -->
+ //<!-- VIP Main Controls -->
+ //<!-- De-Interlacer IP Controls -->
+ //<!-- LCD Controller -->
+ //<!-- YCbCr to RGB -->
+ //<!-- VIP coeff -->
+ //<!-- Let HDMI Source start access -->
+ {TC_CAMERA_MODULE_REG_TYPE_DATA, 0x854A, 0x01, 1},
+ //<!-- Wait until HDMI sync is established -->
+ //<i2c_write addr="0x0F" count="2" radix="16">85 20</i2c_write>
+ //<i2c_read addr="0x0F" count="1" radix="16" />
+ //<!-- Sequence: Check bit7 of 8x8520 -->
+ {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");
--- /dev/null
+/*
+ * 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<benjo.zhou@rock-chips.com>
+ * 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 <linux/delay.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf-core.h>
+#include <linux/slab.h>
+#include <linux/gcd.h>
+#include <linux/i2c.h>
+#include <media/v4l2-controls_rockchip.h>
+#include <linux/platform_data/rk_isp10_platform.h>
+
+#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);
+}