From 2a2ce6901771f4aad14a3caca7ed8ced2a1b5a90 Mon Sep 17 00:00:00 2001 From: Ankur Pawar Date: Tue, 14 Mar 2023 08:35:56 +0000 Subject: [PATCH] media: add imx318 sensor driver Add imx318 camera sensor driver code, mode tables and makefile changes. Bug 3583587 Change-Id: I9854909823b67856edc50c2364e519e853134178 Signed-off-by: Ankur Pawar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2870729 Reviewed-by: Semi Malinen Reviewed-by: Praveen AC Reviewed-by: Shubham Chandra Reviewed-by: Laxman Dewangan GVS: Gerrit_Virtual_Submit --- drivers/media/i2c/Makefile | 1 + drivers/media/i2c/imx318_mode_tbls.h | 464 ++++++++++++++++ drivers/media/i2c/nv_imx318.c | 793 +++++++++++++++++++++++++++ include/media/imx318.h | 51 ++ include/uapi/media/imx318.h | 76 +++ 5 files changed, 1385 insertions(+) create mode 100644 drivers/media/i2c/imx318_mode_tbls.h create mode 100644 drivers/media/i2c/nv_imx318.c create mode 100644 include/media/imx318.h create mode 100644 include/uapi/media/imx318.h diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index a833d9c5..c89e79b6 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -5,4 +5,5 @@ subdir-ccflags-y += -Werror obj-m += nv_imx185.o obj-m += nv_imx274.o +obj-m += nv_imx318.o obj-m += nv_ov5693.o diff --git a/drivers/media/i2c/imx318_mode_tbls.h b/drivers/media/i2c/imx318_mode_tbls.h new file mode 100644 index 00000000..c12b5bdd --- /dev/null +++ b/drivers/media/i2c/imx318_mode_tbls.h @@ -0,0 +1,464 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2017-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ + +#ifndef IMX318_I2C_TABLES +#define IMX318_I2C_TABLES + +#include +#include + +#define IMX318_TABLE_WAIT_MS 0 +#define IMX318_TABLE_END 1 +#define IMX318_MAX_RETRIES 3 +#define imx318_reg struct reg_8 + +static imx318_reg imx318_start[] = { + {0x0100, 0x01}, + { IMX318_TABLE_END, 0x00 } +}; + +static imx318_reg imx318_stop[] = { + {0x0100, 0x00 }, + {IMX318_TABLE_END, 0x00 } +}; + +static imx318_reg mode_table_common[] = { +/*External Clock Setting*/ + {0x0136, 0x18}, + {0x0137, 0x00}, + +/*Global Setting*/ + {0x3067, 0x00}, + {0x30F0, 0x02}, + {0x31C2, 0x00}, + {0x41B6, 0x0A}, + {0x4600, 0x1B}, + {0x46C2, 0x00}, + {0x4877, 0x11}, + {0x487B, 0x4D}, + {0x487F, 0x27}, + {0x4883, 0xB4}, + {0x4C6F, 0x5E}, + {0x5113, 0xF4}, + {0x5115, 0xF6}, + {0x5125, 0xF4}, + {0x5127, 0xF8}, + {0x51CF, 0xF4}, + {0x51E9, 0xF4}, + {0x5483, 0x7A}, + {0x5485, 0x7C}, + {0x5495, 0x7A}, + {0x5497, 0x7F}, + {0x5515, 0xC3}, + {0x5517, 0xC7}, + {0x552B, 0x7A}, + {0x5535, 0x7A}, + {0x5A35, 0x1B}, + {0x5C13, 0x00}, + {0x5C5A, 0x01}, + {0x5C5B, 0x01}, + {0x5D89, 0x81}, + {0x5D8B, 0x2C}, + {0x5D8D, 0x61}, + {0x5D8F, 0xE1}, + {0x5D91, 0x4D}, + {0x5D93, 0xB4}, + {0x5D95, 0x41}, + {0x5D97, 0x96}, + {0x5D99, 0x37}, + {0x5D9B, 0x81}, + {0x5D9D, 0x31}, + {0x5D9F, 0x71}, + {0x5DA1, 0x2B}, + {0x5DA3, 0x64}, + {0x5DA5, 0x27}, + {0x5DA7, 0x5A}, + {0x6008, 0x0A}, + {0x6009, 0x03}, + {0x613A, 0x05}, + {0x613C, 0x23}, + {0x613D, 0x0F}, + {0x613E, 0xFF}, + {0x6142, 0x02}, + {0x6143, 0x63}, + {0x6144, 0x09}, + {0x6145, 0x0B}, + {0x6146, 0x24}, + {0x6147, 0x30}, + {0x6148, 0x90}, + {0x6149, 0xB2}, + {0x614A, 0x43}, + {0x614B, 0x0A}, + {0x614C, 0x01}, + {0x614D, 0x12}, + {0x614E, 0x30}, + {0x614F, 0x98}, + {0x6150, 0xA2}, + {0x6157, 0x1F}, + {0x615C, 0x40}, + {0x615D, 0x40}, + {0x615E, 0x66}, + {0x615F, 0xC0}, + {0x6160, 0x00}, + {0x616C, 0x42}, + {0x616D, 0x40}, + {0x616E, 0xAC}, + {0x616F, 0x4A}, + {0x6170, 0x49}, + {0x6171, 0x02}, + {0x6172, 0xB1}, + {0x6173, 0x29}, + {0x6176, 0x24}, + {0x6177, 0x93}, + {0x6178, 0x4B}, + {0x6179, 0x45}, + {0x617A, 0x48}, + {0x617B, 0x14}, + {0x617C, 0x26}, + {0x617D, 0x94}, + {0x617E, 0x5A}, + {0x617F, 0x35}, + {0x6182, 0x15}, + {0x6194, 0xC7}, + {0x6195, 0x7E}, + {0x6A5F, 0x03}, + {0x9002, 0x11}, + {0x9200, 0x5D}, + {0x9201, 0x49}, + {0x9202, 0x5D}, + {0x9203, 0x4A}, + {0x9204, 0x5D}, + {0x9205, 0x4B}, + {0x9206, 0x5D}, + {0x9207, 0x4D}, + {0x9208, 0x5D}, + {0x9209, 0x4F}, + {0x920A, 0x5D}, + {0x920B, 0x51}, + {0x920C, 0x5D}, + {0x920D, 0x53}, + {0x920E, 0x5D}, + {0x920F, 0x55}, + {0x9210, 0x5D}, + {0x9211, 0x57}, + {0x9212, 0x5D}, + {0x9213, 0x59}, + {0x9214, 0x5D}, + {0x9215, 0x5B}, + {0x9216, 0x5D}, + {0x9217, 0x5D}, + {0x9218, 0x5D}, + {0x9219, 0x5F}, + {0x921A, 0x5D}, + {0x921B, 0x61}, + {0x921C, 0x5D}, + {0x921D, 0x63}, + {0x921E, 0x5D}, + {0x921F, 0x65}, + {0x9220, 0x5D}, + {0x9221, 0x67}, + {0x9300, 0x80}, + {0x9301, 0xCD}, + {0x9302, 0xFF}, + {0x9306, 0x50}, + {0x9307, 0x50}, + {0x9309, 0x46}, + {0x930A, 0x46}, + {0x930C, 0x50}, + {0x930D, 0x50}, + {0x930F, 0x51}, + {0x9310, 0x3C}, + {0x9312, 0x30}, + {0x9313, 0x16}, + {0x9324, 0x05}, + {0x9327, 0x05}, + {0x932A, 0x05}, + {0x932D, 0x06}, + {0x9330, 0x10}, + {0x9331, 0x10}, + {0x9332, 0x15}, + {0x9333, 0x05}, + {0x9334, 0x0C}, + {0x9335, 0x10}, + {0x9336, 0x05}, + {0x9337, 0x0C}, + {0x9338, 0x10}, + {0x9339, 0x05}, + {0x933A, 0x0C}, + {0x933B, 0x10}, + {0x933C, 0x06}, + {0x933D, 0x0C}, + {0x933E, 0x10}, + {0x933F, 0x1B}, + {0x9340, 0x1B}, + {0x9341, 0x1B}, + {0x9342, 0x1B}, + {0x9343, 0x18}, + {0x9344, 0x18}, + {0x9345, 0x18}, + {0x9346, 0x18}, + {0xF800, 0xD9}, + {0xF801, 0xD9}, + {0xF802, 0x41}, + {0xF803, 0x00}, + {0xF804, 0x55}, + {0xF805, 0xE4}, + {0xF806, 0xDC}, + {0xF807, 0x01}, + {0xF808, 0x81}, + {0xF809, 0xD1}, + {0xF80A, 0x31}, + {0xF80B, 0x06}, + {0xF80C, 0x00}, + {0xF80D, 0xBA}, + {0xF80E, 0x70}, + {0xF80F, 0x47}, + {0xF810, 0xC0}, + {0xF811, 0xBA}, + {0xF812, 0x70}, + {0xF813, 0x47}, + +/*Load setting*/ + {0x3729, 0x01}, + +/*IMG Quality*/ + + {IMX318_TABLE_END, 0x00} +}; + +static imx318_reg imx318_cphy_4k_30fps[] = { +/*MIPI setting*/ + {0x0111, 0x03}, /*CPHY*/ + {0x0112, 0x0A}, /*RAW10*/ + {0x0113, 0x0A}, /*RAW10*/ + {0x0114, 0x02}, /*3 trios*/ + {0x0601, 0x00}, /*TPG mode*/ + +/*Frame Horizontal Clock Count*/ + {0x0342, 0x17}, + {0x0343, 0xB0}, + +/*Frame Vertical Clock Count*/ + {0x0340, 0x11}, + {0x0341, 0x28}, + +/*Visible Size*/ + {0x0344, 0x00}, + {0x0345, 0x00}, + {0x0346, 0x00}, + {0x0347, 0x00}, + {0x0348, 0x15}, + {0x0349, 0x6F}, + {0x034A, 0x10}, + {0x034B, 0x0F}, + {0x31A2, 0x00}, + +/*Mode Setting*/ + {0x0220, 0x00}, + {0x0221, 0x11}, + {0x0222, 0x01}, + {0x0381, 0x01}, + {0x0383, 0x01}, + {0x0385, 0x01}, + {0x0387, 0x01}, + {0x0900, 0x00}, + {0x0901, 0x11}, + {0x0902, 0x00}, + {0x3010, 0x65}, + {0x3011, 0x11}, + {0x301C, 0x00}, + {0x3045, 0x01}, + {0x3194, 0x01}, + {0x31A0, 0x00}, + {0x31A1, 0x00}, + {0xD5EC, 0x3A}, + {0xD5ED, 0x00}, + +/*Digital Crop & Scaling*/ + {0x0401, 0x00}, + {0x0404, 0x00}, + {0x0405, 0x10}, + {0x0408, 0x00}, + {0x0409, 0x00}, + {0x040A, 0x00}, + {0x040B, 0x00}, + {0x040C, 0x15}, + {0x040D, 0x70}, + {0x040E, 0x10}, + {0x040F, 0x10}, + +/*Output Size Settings*/ + {0x034C, 0x15}, /*Horizontal size*/ + {0x034D, 0x70}, + {0x034E, 0x10}, /*Vertical size*/ + {0x034F, 0x10}, + {0x4041, 0x00}, /*EMBD LINE SIZE*/ + + +/*Clock setting*/ + {0x0301, 0x05}, + {0x0303, 0x02}, + {0x0305, 0x04}, + {0x0306, 0x01}, + {0x0307, 0x4D}, + {0x0309, 0x08}, + {0x030B, 0x01}, + {0x030D, 0x04}, + {0x030E, 0x01}, + {0x030F, 0x3C}, + {0x0820, 0x32}, + {0x0821, 0xC9}, + {0x0822, 0x24}, + {0x0823, 0x92}, + {0x422F, 0x01}, + {0x4230, 0x00}, + +/*Output Data Select Setting*/ + {0x3031, 0x00}, + {0x3033, 0x00}, + {0x3039, 0x00}, + {0x303B, 0x00}, + +/*EIS setting*/ + {0x306C, 0x00}, + {0x306E, 0x0D}, + {0x306F, 0x56}, + {0x6636, 0x00}, + {0x6637, 0x14}, + {0xCA12, 0x2C}, + {0xCA13, 0x2C}, + {0xCA14, 0x1C}, + {0xCA15, 0x1C}, + {0xCA16, 0x06}, + {0xCA17, 0x06}, + {0xCA18, 0x20}, + {0xCA19, 0x20}, + {0xCA1A, 0x0C}, + {0xCA1B, 0x0C}, + {0xCA1C, 0x06}, + {0xCA1D, 0x06}, + {0xCA66, 0x39}, + {0xCA67, 0x39}, + {0xCA68, 0x39}, + {0xCA69, 0x39}, + {0xCA6A, 0x13}, + {0xCA6B, 0x13}, + {0xCA6C, 0x20}, + {0xCA6D, 0x20}, + {0xCA6E, 0x20}, + {0xCA6F, 0x20}, + {0xCA70, 0x10}, + {0xCA71, 0x10}, + +/*GYRO setting*/ + {0x30AC, 0x00}, + {0x3900, 0x00}, + {0x3901, 0x00}, + {0x31C3, 0x01}, + +/*DLC setting*/ + {0x3066, 0x00}, + +/*LSC setting*/ + {0x7B63, 0x00}, + +/*Analog setting*/ + {0x56FB, 0x50}, + {0x56FF, 0x50}, + {0x6174, 0x28}, + {0x6175, 0xC2}, + {0x9323, 0x0C}, + +/*Other setting*/ + {0x30F1, 0x00}, + {0x30F4, 0x01}, + {0x30F5, 0x54}, + {0x30F6, 0x00}, + {0x30F7, 0x14}, + {0x30FC, 0x01}, + {0x30FD, 0x01}, + {0x714E, 0x01}, + {0x714D, 0x06}, + {0x7152, 0x06}, + {0x7156, 0x01}, + {0x7155, 0x00}, + {0x7159, 0x00}, + {0x76A3, 0x06}, + {0x76A0, 0x01}, + {0x76A5, 0x06}, + {0x76A9, 0x00}, + {0x76AF, 0x00}, + {0x76AC, 0x01}, + {0x9303, 0x32}, + {0xD00C, 0x0C}, + {0x3600, 0x4F}, + {0x3601, 0x00}, + {0x3602, 0xFA}, + {0x3603, 0x3B}, + {0x3604, 0xBC}, + {0x3605, 0x2F}, + {0x3606, 0x96}, + {0x3607, 0x28}, + {0x3608, 0x7D}, + {0x3609, 0x22}, + {0x360A, 0x6C}, + {0x360B, 0x1E}, + {0x360C, 0x5E}, + {0x360D, 0x1B}, + {0x360E, 0x54}, + {0x360F, 0x18}, + {0x3610, 0x4B}, + +/*CIT setting*/ + {0x0202, 0x11}, + {0x0203, 0x14}, + {0x0224, 0x01}, + {0x0225, 0xF4}, + +/*Gain setting*/ + {0x0204, 0x00}, + {0x0205, 0x00}, + {0x020E, 0x01}, + {0x020F, 0x00}, + {0x0210, 0x01}, + {0x0211, 0x00}, + {0x0212, 0x01}, + {0x0213, 0x00}, + {0x0214, 0x01}, + {0x0215, 0x00}, + {0x0216, 0x00}, + {0x0217, 0x00}, + {0x0218, 0x01}, + {0x0219, 0x00}, + {IMX318_TABLE_END, 0x00} +}; + + +enum { + IMX318_MODE_CPHY_4k_30FPS, + + IMX318_MODE_COMMON, + IMX318_MODE_START_STREAM, + IMX318_MODE_STOP_STREAM +}; + +static imx318_reg *mode_table[] = { + [IMX318_MODE_CPHY_4k_30FPS] = imx318_cphy_4k_30fps, + [IMX318_MODE_COMMON] = mode_table_common, + [IMX318_MODE_START_STREAM] = imx318_start, + [IMX318_MODE_STOP_STREAM] = imx318_stop +}; + +static const int imx318_30fps[] = { + 30, +}; + +/* + * WARNING: frmfmt ordering need to match mode definition in + * device tree! + */ +static const struct camera_common_frmfmt imx318_frmfmt[] = { + {{5488, 4112}, imx318_30fps, 1, 0, IMX318_MODE_CPHY_4k_30FPS}, + /* Add modes with no device tree support after below */ +}; +#endif /* IMX318_I2C_TABLES */ diff --git a/drivers/media/i2c/nv_imx318.c b/drivers/media/i2c/nv_imx318.c new file mode 100644 index 00000000..35ccc626 --- /dev/null +++ b/drivers/media/i2c/nv_imx318.c @@ -0,0 +1,793 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2017-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../platform/tegra/camera/camera_gpio.h" +#include "imx318_mode_tbls.h" + +#define IMX318_MAX_COARSE_DIFF 10 + +/* IMX318 sensor register address */ +#define IMX318_GAIN_ADDR_MSB 0x0204 +#define IMX318_GAIN_ADDR_LSB 0x0205 +#define IMX318_FRAME_LEGNTH_CTRL_EN 0x0350 +#define IMX318_FRAME_LENGTH_ADDR_MSB 0x0340 +#define IMX318_FRAME_LENGTH_ADDR_LSB 0x0341 +#define IMX318_COARSE_INTEG_TIME_ADDR_MSB 0x0202 +#define IMX318_COARSE_INTEG_TIME_ADDR_LSB 0x0203 +#define IMX318_ST_COARSE_INTEG_TIME_ADDR_MSB 0x0224 +#define IMX318_ST_COARSE_INTEG_TIME_ADDR_LSB 0x0225 +#define IMX318_GROUP_HOLD_ADDR 0x0104 + +static const struct of_device_id imx318_of_match[] = { + { .compatible = "sony,imx318",}, + { }, +}; +MODULE_DEVICE_TABLE(of, imx318_of_match); + +static const u32 ctrl_cid_list[] = { + TEGRA_CAMERA_CID_GAIN, + TEGRA_CAMERA_CID_EXPOSURE, + TEGRA_CAMERA_CID_FRAME_RATE, + TEGRA_CAMERA_CID_EEPROM_DATA, + TEGRA_CAMERA_CID_FUSE_ID, +}; + +struct imx318 { + struct mutex imx318_camera_lock; + struct camera_common_eeprom_data eeprom[IMX318_EEPROM_NUM_BLOCKS]; + u8 eeprom_buf[IMX318_EEPROM_SIZE]; + u8 fuse_id[IMX318_FUSE_ID_SIZE]; + struct i2c_client *i2c_client; + struct v4l2_subdev *subdev; + u32 frame_length; + struct camera_common_data *s_data; + struct tegracam_device *tc_dev; +}; + +static const struct regmap_config sensor_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .use_single_read = true, + .use_single_write = true, +}; + +static int test_mode; +module_param(test_mode, int, 0644); + +static int imx318_write_table(struct imx318 *priv, + struct sensor_blob *blob, + const imx318_reg table[]) +{ + return convert_table_to_blob(blob, table, + IMX318_TABLE_WAIT_MS, IMX318_TABLE_END); +} + +static int imx318_set_group_hold_ex(struct tegracam_device *tc_dev, + struct sensor_blob *blob, bool val) +{ + u8 reg_val = val; + + return prepare_write_cmd(blob, 1, IMX318_GROUP_HOLD_ADDR, ®_val); +} + +static int imx318_set_gain_ex(struct tegracam_device *tc_dev, + struct sensor_blob *blob, s64 val) +{ + struct camera_common_data *s_data = tc_dev->s_data; + struct device *dev = s_data->dev; + const struct sensor_mode_properties *mode = + &s_data->sensor_props.sensor_modes[s_data->mode_prop_idx]; + s16 gain; + u8 gain_arr[2]; + + if (val < mode->control_properties.min_gain_val) + val = mode->control_properties.min_gain_val; + else if (val > mode->control_properties.max_gain_val) + val = mode->control_properties.max_gain_val; + + if (val == 0) + return -EINVAL; + + /* translate value */ + gain = (s16)((512 * mode->control_properties.gain_factor) / val); + gain = 512 - gain; + + if (gain < 0) + gain = 0; + + dev_dbg(dev, + "%s: gain reg: %d, times: %lld\n", __func__, gain, val); + + conv_u16_u8arr(gain, &gain_arr[0]); + return prepare_write_cmd(blob, 2, IMX318_GAIN_ADDR_MSB, gain_arr); +} + +static int imx318_set_frame_rate_ex(struct tegracam_device *tc_dev, + struct sensor_blob *blob, s64 val) +{ + struct camera_common_data *s_data = tc_dev->s_data; + struct imx318 *priv = (struct imx318 *)tc_dev->priv; + struct device *dev = tc_dev->dev; + const struct sensor_mode_properties *mode = + &s_data->sensor_props.sensor_modes[s_data->mode_prop_idx]; + u32 frame_length; + u8 fl_arr[2]; + int err = 0; + + if (mode->image_properties.line_length == 0 || + val == 0) { + return -EINVAL; + } + frame_length = (u32)(mode->signal_properties.pixel_clock.val * + (u64)mode->control_properties.framerate_factor / + mode->image_properties.line_length / val); + + dev_dbg(dev, + "%s: val:%d\n", __func__, frame_length); + + conv_u16_u8arr((u16)frame_length, &fl_arr[0]); + err = prepare_write_cmd(blob, 2, IMX318_FRAME_LENGTH_ADDR_MSB, fl_arr); + if (err) + return err; + + priv->frame_length = frame_length; + + return 0; +} + +static int imx318_set_exposure_ex(struct tegracam_device *tc_dev, + struct sensor_blob *blob, s64 val) +{ + struct camera_common_data *s_data = tc_dev->s_data; + struct imx318 *priv = (struct imx318 *)tc_dev->priv; + struct device *dev = tc_dev->dev; + const struct sensor_mode_properties *mode = + &s_data->sensor_props.sensor_modes[s_data->mode_prop_idx]; + u32 coarse_time; + u8 ct_arr[2]; + s32 max_coarse_time = priv->frame_length - IMX318_MAX_COARSE_DIFF; + + if (mode->image_properties.line_length == 0 || + mode->control_properties.exposure_factor == 0) { + return -EINVAL; + } + coarse_time = mode->signal_properties.pixel_clock.val * + val / mode->image_properties.line_length / + mode->control_properties.exposure_factor; + + dev_dbg(dev, "%s: val: %d\n", __func__, coarse_time); + if (coarse_time > max_coarse_time) { + coarse_time = max_coarse_time; + dev_dbg(dev, "%s:exposure limited by framelength val:%d\n", + __func__, max_coarse_time); + } + + conv_u16_u8arr((u16)coarse_time, &ct_arr[0]); + + return prepare_write_cmd(blob, 2, + IMX318_COARSE_INTEG_TIME_ADDR_MSB, ct_arr); +} + +static int imx318_fill_string_ctrl(struct tegracam_device *tc_dev, + struct v4l2_ctrl *ctrl) +{ + struct imx318 *priv = tc_dev->priv; + int i, ret; + + switch (ctrl->id) { + case TEGRA_CAMERA_CID_EEPROM_DATA: + for (i = 0; i < IMX318_EEPROM_SIZE; i++) { + ret = sprintf(&ctrl->p_new.p_char[i*2], "%02x", + priv->eeprom_buf[i]); + if (ret < 0) + return -EINVAL; + } + break; + case TEGRA_CAMERA_CID_FUSE_ID: + for (i = 0; i < IMX318_FUSE_ID_SIZE; i++) { + ret = sprintf(&ctrl->p_new.p_char[i*2], "%02x", + priv->fuse_id[i]); + if (ret < 0) + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + ctrl->p_cur.p_char = ctrl->p_new.p_char; + + return 0; +} + +static struct tegracam_ctrl_ops imx318_ctrl_ops = { + .numctrls = ARRAY_SIZE(ctrl_cid_list), + .is_blob_supported = true, + .ctrl_cid_list = ctrl_cid_list, + .string_ctrl_size = { IMX318_EEPROM_STR_SIZE, + IMX318_FUSE_ID_STR_SIZE}, + .set_gain_ex = imx318_set_gain_ex, + .set_exposure_ex = imx318_set_exposure_ex, + .set_frame_rate_ex = imx318_set_frame_rate_ex, + .set_group_hold_ex = imx318_set_group_hold_ex, + .fill_string_ctrl = imx318_fill_string_ctrl, +}; + +static int imx318_power_on(struct camera_common_data *s_data) +{ + int err = 0; + struct camera_common_power_rail *pw = s_data->power; + struct camera_common_pdata *pdata = s_data->pdata; + struct device *dev = s_data->dev; + + dev_dbg(dev, "%s: power on\n", __func__); + if (pdata && pdata->power_on) { + err = pdata->power_on(pw); + if (err) + dev_err(dev, "%s failed.\n", __func__); + else + pw->state = SWITCH_ON; + return err; + } + + if (pw->reset_gpio) + gpio_set_value(pw->reset_gpio, 0); + usleep_range(15, 20); + if (pw->avdd) + err = regulator_enable(pw->avdd); + if (err) + goto imx318_avdd_fail; + + if (pw->iovdd) + err = regulator_enable(pw->iovdd); + if (err) + goto imx318_iovdd_fail; + + if (pw->dvdd) + err = regulator_enable(pw->dvdd); + if (err) + goto imx318_dvdd_fail; + + if (pw->reset_gpio) + gpio_set_value(pw->reset_gpio, 1); + + usleep_range(19000, 19010); + pw->state = SWITCH_ON; + + return 0; + +imx318_dvdd_fail: + regulator_disable(pw->iovdd); + +imx318_iovdd_fail: + regulator_disable(pw->avdd); + +imx318_avdd_fail: + dev_err(dev, "%s failed.\n", __func__); + return -ENODEV; +} + +static int imx318_power_off(struct camera_common_data *s_data) +{ + int err = 0; + struct camera_common_power_rail *pw = s_data->power; + struct camera_common_pdata *pdata = s_data->pdata; + struct device *dev = s_data->dev; + + dev_dbg(dev, "%s: power off\n", __func__); + + if (pdata && pdata->power_off) { + err = pdata->power_off(pw); + if (err) { + dev_err(dev, "%s failed.\n", __func__); + return err; + } + } else { + if (pw->reset_gpio) + gpio_set_value(pw->reset_gpio, 0); + usleep_range(15, 20); + if (pw->avdd) + regulator_disable(pw->avdd); + if (pw->iovdd) + regulator_disable(pw->iovdd); + if (pw->dvdd) + regulator_disable(pw->dvdd); + /* + * Sleep for atleast 5ms after power off, discussions with + * sony revealed that this has to be investigated for the + * right delay and rootcause, for not add a WAR to pass testing + */ + usleep_range(5000, 6000); + } + + pw->state = SWITCH_OFF; + return 0; +} + +static int imx318_power_put(struct tegracam_device *tc_dev) +{ + struct camera_common_data *s_data = tc_dev->s_data; + struct camera_common_power_rail *pw = s_data->power; + struct camera_common_pdata *pdata = s_data->pdata; + + if (unlikely(!pw)) + return -EFAULT; + + if (likely(pw->avdd)) + devm_regulator_put(pw->avdd); + + if (likely(pw->iovdd)) + devm_regulator_put(pw->iovdd); + + if (likely(pw->iovdd)) + devm_regulator_put(pw->dvdd); + + pw->avdd = NULL; + pw->iovdd = NULL; + pw->dvdd = NULL; + + if (pdata && pdata->use_cam_gpio) + cam_gpio_deregister(s_data->dev, pw->pwdn_gpio); + else { + gpio_free(pw->pwdn_gpio); + gpio_free(pw->reset_gpio); + } + + return 0; +} + +static int imx318_power_get(struct tegracam_device *tc_dev) +{ + struct device *dev = tc_dev->dev; + struct camera_common_data *s_data = tc_dev->s_data; + struct camera_common_power_rail *pw = s_data->power; + struct camera_common_pdata *pdata = s_data->pdata; + const char *mclk_name; + const char *parentclk_name; + struct clk *parent; + int err = 0, ret = 0; + + if (!pdata) { + dev_err(dev, "pdata missing\n"); + return -EFAULT; + } + + mclk_name = pdata->mclk_name ? + pdata->mclk_name : "cam_mclk1"; + pw->mclk = devm_clk_get(dev, mclk_name); + if (IS_ERR(pw->mclk)) { + dev_err(dev, "unable to get clock %s\n", mclk_name); + return PTR_ERR(pw->mclk); + } + + parentclk_name = pdata->parentclk_name; + if (parentclk_name) { + parent = devm_clk_get(dev, parentclk_name); + if (IS_ERR(parent)) { + dev_err(dev, "unable to get parent clock %s", + parentclk_name); + } else + clk_set_parent(pw->mclk, parent); + } + + + /* analog 2.8v */ + err |= camera_common_regulator_get(dev, + &pw->avdd, pdata->regulators.avdd); + /* IO 1.8v */ + err |= camera_common_regulator_get(dev, + &pw->iovdd, pdata->regulators.iovdd); + /* dig 1.2v */ + err |= camera_common_regulator_get(dev, + &pw->dvdd, pdata->regulators.dvdd); + + if (!err) + pw->reset_gpio = pdata->reset_gpio; + + ret = gpio_request(pw->reset_gpio, "cam_reset_gpio"); + if (ret < 0) + dev_dbg(dev, "%s can't request reset_gpio %d\n", __func__, ret); + + pw->state = SWITCH_OFF; + return err; +} + +static struct camera_common_pdata *imx318_parse_dt(struct tegracam_device *tc_dev) +{ + struct device *dev = tc_dev->dev; + struct device_node *np = dev->of_node; + struct camera_common_pdata *board_priv_pdata; + const struct of_device_id *match; + int err; + struct camera_common_pdata *ret = NULL; + int gpio; + + if (!np) + return NULL; + + match = of_match_device(imx318_of_match, dev); + if (!match) { + dev_err(dev, "Failed to find matching dt id\n"); + return NULL; + } + + board_priv_pdata = devm_kzalloc(dev, + sizeof(*board_priv_pdata), GFP_KERNEL); + if (!board_priv_pdata) + return NULL; + + err = of_property_read_string(np, "mclk", + &board_priv_pdata->mclk_name); + if (err) + dev_err(dev, "mclk not in DT\n"); + + gpio = of_get_named_gpio(np, "reset-gpios", 0); + if (gpio < 0) { + if (gpio == -EPROBE_DEFER) + ret = ERR_PTR(-EPROBE_DEFER); + dev_err(dev, "reset-gpios not found %d\n", err); + goto error; + } + board_priv_pdata->reset_gpio = (unsigned int)gpio; + + err = of_property_read_string(np, "avdd-reg", + &board_priv_pdata->regulators.avdd); + if (err) { + dev_err(dev, "avdd-reg not in DT\n"); + goto error; + } + err = of_property_read_string(np, "iovdd-reg", + &board_priv_pdata->regulators.iovdd); + if (err) { + dev_err(dev, "iovdd-reg not in DT\n"); + goto error; + } + err = of_property_read_string(np, "dvdd-reg", + &board_priv_pdata->regulators.dvdd); + if (err) { + dev_err(dev, "dvdd-reg not in DT\n"); + goto error; + } + board_priv_pdata->has_eeprom = + of_property_read_bool(np, "has-eeprom"); + + return board_priv_pdata; + +error: + devm_kfree(dev, board_priv_pdata); + return ret; + +} + +static int imx318_set_mode(struct tegracam_device *tc_dev) +{ + struct imx318 *priv = (struct imx318 *)tegracam_get_privdata(tc_dev); + struct camera_common_data *s_data = tc_dev->s_data; + struct tegracam_sensor_data *sensor_data = + &s_data->tegracam_ctrl_hdl->sensor_data; + struct sensor_blob *mode_blob = &sensor_data->mode_blob; + int err; + + err = imx318_write_table(priv, mode_blob, + mode_table[IMX318_MODE_COMMON]); + if (err) + return err; + + if (s_data->mode < 0) + return -EINVAL; + + err = imx318_write_table(priv, mode_blob, + mode_table[s_data->mode]); + if (err) + return err; + + return 0; +} + +static int imx318_start_streaming(struct tegracam_device *tc_dev) +{ + struct imx318 *priv = (struct imx318 *)tegracam_get_privdata(tc_dev); + struct camera_common_data *s_data = tc_dev->s_data; + struct tegracam_sensor_data *sensor_data = + &s_data->tegracam_ctrl_hdl->sensor_data; + struct sensor_blob *ctrl_blob = &sensor_data->ctrls_blob; + int err; + + err = imx318_write_table(priv, ctrl_blob, + mode_table[IMX318_MODE_START_STREAM]); + if (err) + return err; + + return prepare_sleep_cmd(ctrl_blob, 10000); +} + +static int imx318_stop_streaming(struct tegracam_device *tc_dev) +{ + struct imx318 *priv = (struct imx318 *)tegracam_get_privdata(tc_dev); + struct camera_common_data *s_data = tc_dev->s_data; + struct tegracam_sensor_data *sensor_data = + &s_data->tegracam_ctrl_hdl->sensor_data; + struct sensor_blob *ctrl_blob = &sensor_data->ctrls_blob; + int err; + + err = imx318_write_table(priv, ctrl_blob, + mode_table[IMX318_MODE_STOP_STREAM]); + if (err) + return err; + /* + * Wait for one frame to make sure sensor is set to + * software standby in V-blank + * + * delay = frame length rows * Tline (10 us) / 1000 + */ + return prepare_sleep_cmd(ctrl_blob, (priv->frame_length * 10)); +} + +static struct camera_common_sensor_ops imx318_common_ops = { + .numfrmfmts = ARRAY_SIZE(imx318_frmfmt), + .frmfmt_table = imx318_frmfmt, + .power_on = imx318_power_on, + .power_off = imx318_power_off, + .parse_dt = imx318_parse_dt, + .power_get = imx318_power_get, + .power_put = imx318_power_put, + .set_mode = imx318_set_mode, + .start_streaming = imx318_start_streaming, + .stop_streaming = imx318_stop_streaming, +}; + +static int imx318_eeprom_device_release(struct imx318 *priv) +{ + int i; + + for (i = 0; i < IMX318_EEPROM_NUM_BLOCKS; i++) { + if (priv->eeprom[i].i2c_client != NULL) { + i2c_unregister_device(priv->eeprom[i].i2c_client); + priv->eeprom[i].i2c_client = NULL; + } + } + + return 0; +} + +static int imx318_eeprom_device_init(struct imx318 *priv) +{ + char *dev_name = "eeprom_imx318"; + struct camera_common_pdata *pdata = priv->s_data->pdata; + static struct regmap_config eeprom_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + }; + int i; + int err; + + if (!pdata || !pdata->has_eeprom) + return -EINVAL; + + for (i = 0; i < IMX318_EEPROM_NUM_BLOCKS; i++) { + priv->eeprom[i].adap = i2c_get_adapter( + priv->i2c_client->adapter->nr); + memset(&priv->eeprom[i].brd, 0, sizeof(priv->eeprom[i].brd)); + strscpy(priv->eeprom[i].brd.type, dev_name, + sizeof(priv->eeprom[i].brd.type)); + priv->eeprom[i].brd.addr = IMX318_EEPROM_ADDRESS + i; + priv->eeprom[i].i2c_client = i2c_new_client_device( + priv->eeprom[i].adap, &priv->eeprom[i].brd); + + priv->eeprom[i].regmap = devm_regmap_init_i2c( + priv->eeprom[i].i2c_client, &eeprom_regmap_config); + if (IS_ERR(priv->eeprom[i].regmap)) { + err = PTR_ERR(priv->eeprom[i].regmap); + imx318_eeprom_device_release(priv); + return err; + } + } + + return 0; +} + +static int imx318_read_eeprom(struct imx318 *priv) +{ + int err, i; + + for (i = 0; i < IMX318_EEPROM_NUM_BLOCKS; i++) { + err = regmap_bulk_read(priv->eeprom[i].regmap, 0, + &priv->eeprom_buf[i * IMX318_EEPROM_BLOCK_SIZE], + IMX318_EEPROM_BLOCK_SIZE); + if (err) + return err; + } + + return 0; +} + +static int imx318_read_fuse_id(struct imx318 *priv) +{ + /* fuse id is stored in eeprom */ + memcpy(priv->fuse_id, + &priv->eeprom_buf[IMX318_FUSE_ID_START_ADDR], + IMX318_FUSE_ID_SIZE); + + return 0; +} + +static int imx318_board_setup(struct imx318 *priv) +{ + struct camera_common_data *s_data = priv->s_data; + struct device *dev = s_data->dev; + bool eeprom_ctrl = 0; + int err = 0; + + dev_dbg(dev, "%s++\n", __func__); + + /* eeprom interface */ + err = imx318_eeprom_device_init(priv); + if (err && s_data->pdata->has_eeprom) + dev_err(dev, + "Failed to allocate eeprom reg map: %d\n", err); + eeprom_ctrl = !err; + + err = camera_common_mclk_enable(s_data); + if (err) { + dev_err(dev, + "Error %d turning on mclk\n", err); + return err; + } + + err = imx318_power_on(s_data); + if (err) { + dev_err(dev, + "Error %d during power on sensor\n", err); + return err; + } + + if (eeprom_ctrl) { + err = imx318_read_eeprom(priv); + if (err) { + dev_err(dev, + "Error %d reading eeprom data\n", err); + goto error; + } + err = imx318_read_fuse_id(priv); + if (err) { + dev_err(dev, + "Error %d reading fuse id data\n", err); + goto error; + } + } + +error: + (void)imx318_power_off(s_data); + camera_common_mclk_disable(s_data); + return err; +} + +static int imx318_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + dev_dbg(&client->dev, "%s:\n", __func__); + + return 0; +} + +static const struct v4l2_subdev_internal_ops imx318_subdev_internal_ops = { + .open = imx318_open, +}; + +static int imx318_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct tegracam_device *tc_dev; + struct imx318 *priv; + int err; + + dev_info(dev, "[IMX318]: probing v4l2 sensor at addr 0x%0x.\n", + client->addr); + + if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node) + return -EINVAL; + + priv = devm_kzalloc(dev, + sizeof(struct imx318), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + tc_dev = devm_kzalloc(dev, + sizeof(struct tegracam_device), GFP_KERNEL); + if (!tc_dev) + return -ENOMEM; + + priv->i2c_client = tc_dev->client = client; + tc_dev->dev = dev; + strscpy(tc_dev->name, "imx318", sizeof(tc_dev->name)); + tc_dev->dev_regmap_config = &sensor_regmap_config; + tc_dev->sensor_ops = &imx318_common_ops; + tc_dev->v4l2sd_internal_ops = &imx318_subdev_internal_ops; + tc_dev->tcctrl_ops = &imx318_ctrl_ops; + + err = tegracam_device_register(tc_dev); + if (err) { + dev_err(dev, "tegra camera driver registration failed\n"); + return err; + } + priv->tc_dev = tc_dev; + priv->s_data = tc_dev->s_data; + priv->subdev = &tc_dev->s_data->subdev; + tegracam_set_privdata(tc_dev, (void *)priv); + + err = imx318_board_setup(priv); + if (err) { + tegracam_device_unregister(tc_dev); + dev_err(dev, "board setup failed\n"); + return err; + } + + err = tegracam_v4l2subdev_register(tc_dev, true); + if (err) { + dev_err(dev, "tegra camera subdev registration failed\n"); + return err; + } + + dev_info(dev, "Detected IMX318 sensor\n"); + + return 0; +} + +#if (KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE) +static int imx318_remove(struct i2c_client *client) +#else +static void imx318_remove(struct i2c_client *client) +#endif +{ + struct camera_common_data *s_data = to_camera_common_data(&client->dev); + struct imx318 *priv = (struct imx318 *)s_data->priv; + + tegracam_v4l2subdev_unregister(priv->tc_dev); + tegracam_device_unregister(priv->tc_dev); + imx318_eeprom_device_release(priv); + +#if (KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE) + return 0; +#endif +} + +static const struct i2c_device_id imx318_id[] = { + { "imx318", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, imx318_id); + +static struct i2c_driver imx318_i2c_driver = { + .driver = { + .name = "imx318", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(imx318_of_match), + }, + .probe = imx318_probe, + .remove = imx318_remove, + .id_table = imx318_id, +}; + +module_i2c_driver(imx318_i2c_driver); + +MODULE_DESCRIPTION("Media Controller driver for Sony IMX318"); +MODULE_AUTHOR("NVIDIA Corporation"); +MODULE_LICENSE("GPL v2"); diff --git a/include/media/imx318.h b/include/media/imx318.h new file mode 100644 index 00000000..9277f531 --- /dev/null +++ b/include/media/imx318.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2017-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ + +#ifndef IMX318_H +#define IMX318_H + +#include +#include +#include + +#define IMX318_INVALID_COARSE_TIME -1 + +#define IMX318_EEPROM_ADDRESS 0x54 +#define IMX318_EEPROM_SIZE 256 +#define IMX318_EEPROM_STR_SIZE (IMX318_EEPROM_SIZE * 2) +#define IMX318_EEPROM_BLOCK_SIZE (1 << 8) +#define IMX318_EEPROM_NUM_BLOCKS \ + (IMX318_EEPROM_SIZE / IMX318_EEPROM_BLOCK_SIZE) + +/* Incorrect data, cannot find fuse ID in documentation */ +#define IMX318_FUSE_ID_START_ADDR 0x5b +#define IMX318_FUSE_ID_BANK 0 +#define IMX318_FUSE_ID_SIZE 8 +#define IMX318_FUSE_ID_STR_SIZE (IMX318_FUSE_ID_SIZE * 2) + +/* See notes in the nvc.h file on the GPIO usage */ +enum imx318_gpio_type { + IMX318_GPIO_TYPE_PWRDN = 0, + IMX318_GPIO_TYPE_RESET, +}; + +struct imx318_eeprom_data { + struct i2c_client *i2c_client; + struct i2c_adapter *adap; + struct i2c_board_info brd; + struct regmap *regmap; +}; + +struct imx318_power_rail { + struct regulator *dvdd; + struct regulator *avdd; + struct regulator *dovdd; +}; + +struct imx318_regulators { + const char *avdd; + const char *dvdd; + const char *dovdd; +}; + +#endif /* IMX318_H */ diff --git a/include/uapi/media/imx318.h b/include/uapi/media/imx318.h new file mode 100644 index 00000000..35a20b7d --- /dev/null +++ b/include/uapi/media/imx318.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2017-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ + +#ifndef UAPI_IMX318_H +#define UAPI_IMX318_H + +#include +#include "nvc_image.h" + +#define IMX318_IOCTL_SET_MODE _IOW('o', 1, struct imx318_mode) +#define IMX318_IOCTL_SET_FRAME_LENGTH _IOW('o', 2, uint32_t) +#define IMX318_IOCTL_SET_COARSE_TIME _IOW('o', 3, uint32_t) +#define IMX318_IOCTL_SET_GAIN _IOW('o', 4, uint16_t) +#define IMX318_IOCTL_GET_STATUS _IOR('o', 5, uint8_t) +#define IMX318_IOCTL_SET_BINNING _IOW('o', 6, uint8_t) +#define IMX318_IOCTL_TEST_PATTERN _IOW('o', 7, \ + enum imx318_test_pattern) +#define IMX318_IOCTL_SET_GROUP_HOLD _IOW('o', 8, struct imx318_ae) +/* operating mode can be either stereo , leftOnly or rightOnly */ +#define IMX318_IOCTL_SET_CAMERA_MODE _IOW('o', 10, uint32_t) +#define IMX318_IOCTL_SYNC_SENSORS _IOW('o', 11, uint32_t) +#define IMX318_IOCTL_GET_FUSEID _IOR('o', 12, struct nvc_fuseid) +#define IMX318_IOCTL_SET_CAL_DATA _IOW('o', 15, \ + struct imx318_cal_data) +#define IMX318_IOCTL_GET_EEPROM_DATA _IOR('o', 20, uint8_t *) +#define IMX318_IOCTL_SET_EEPROM_DATA _IOW('o', 21, uint8_t *) +#define IMX318_IOCTL_GET_CAPS _IOR('o', 22, \ + struct nvc_imager_cap) +#define IMX318_IOCTL_SET_POWER _IOW('o', 23, uint32_t) + +struct imx318_mode { + int res_x; + int res_y; + int fps; + uint32_t frame_length; + uint32_t coarse_time; + uint32_t coarse_time_short; + uint16_t gain; + uint8_t hdr_en; +}; + +struct imx318_ae { + uint32_t frame_length; + uint8_t frame_length_enable; + uint32_t coarse_time; + uint32_t coarse_time_short; + uint8_t coarse_time_enable; + int32_t gain; + uint8_t gain_enable; +}; + +struct imx318_fuseid { + uint32_t size; + uint8_t id[16]; +}; + +struct imx318_hdr { + uint32_t coarse_time_long; + uint32_t coarse_time_short; +}; + +struct imx318_otp_bank { + uint32_t id; + uint8_t buf[16]; +}; + +struct imx318_cal_data { + int loaded; + int rg_ratio; + int bg_ratio; + int rg_ratio_typical; + int bg_ratio_typical; + uint8_t lenc[62]; +}; + +#endif /* UAPI_IMX318_H */