diff --git a/kernel/nvidia/include/media/mt9m021.h b/kernel/nvidia/include/media/mt9m021.h
new file mode 100644
index 0000000..1562716
--- /dev/null
+++ b/kernel/nvidia/include/media/mt9m021.h
@@ -0,0 +1,47 @@
+#ifndef MT9M021_H
+#define MT9M021_H
+
+struct v4l2_subdev;
+
+/*
+ * struct mt9m021_platform_data - MT9M021 platform data
+ * @reset: Chip reset GPIO (set to -1 if not used)
+ * @ext_freq: Input clock frequency
+ * @target_freq: Pixel clock frequency
+ */
+
+static uint16_t mt9m021_seq_data[] = {
+ 0x3227, 0x0101, 0x0F25, 0x0808, 0x0227, 0x0101, 0x0837, 0x2700,
+ 0x0138, 0x2701, 0x013A, 0x2700, 0x0125, 0x0020, 0x3C25, 0x0040,
+ 0x3427, 0x003F, 0x2500, 0x2037, 0x2540, 0x4036, 0x2500, 0x4031,
+ 0x2540, 0x403D, 0x6425, 0x2020, 0x3D64, 0x2510, 0x1037, 0x2520,
+ 0x2010, 0x2510, 0x100F, 0x2708, 0x0802, 0x2540, 0x402D, 0x2608,
+ 0x280D, 0x1709, 0x2600, 0x2805, 0x26A7, 0x2807, 0x2580, 0x8029,
+ 0x1705, 0x2500, 0x4027, 0x2222, 0x1616, 0x2726, 0x2617, 0x3626,
+ 0xA617, 0x0326, 0xA417, 0x1F28, 0x0526, 0x2028, 0x0425, 0x2020,
+ 0x2700, 0x2625, 0x0000, 0x171E, 0x2500, 0x0425, 0x0020, 0x2117,
+ 0x121B, 0x1703, 0x2726, 0x2617, 0x2828, 0x0517, 0x1A26, 0x6017,
+ 0xAE25, 0x0080, 0x2700, 0x2626, 0x1828, 0x002E, 0x2A28, 0x081E,
+ 0x4127, 0x1010, 0x0214, 0x6060, 0x0A14, 0x6060, 0x0B14, 0x6060,
+ 0x0C14, 0x6060, 0x0D14, 0x6060, 0x0217, 0x3C14, 0x0060, 0x0A14,
+ 0x0060, 0x0B14, 0x0060, 0x0C14, 0x0060, 0x0D14, 0x0060, 0x0811,
+ 0x2500, 0x1027, 0x0010, 0x2F6F, 0x0F3E, 0x2500, 0x0827, 0x0008,
+ 0x3066, 0x3225, 0x0008, 0x2700, 0x0830, 0x6631, 0x3D64, 0x2508,
+ 0x083D, 0xFF3D, 0x2A27, 0x083F, 0x2C00
+};
+
+static uint16_t mt9m021_analog_setting[] = {
+ 0x00FD, 0x0FFF, 0x0003, 0xF87A, 0xE075, 0x077C, 0xA4EB, 0xD208
+};
+
+/***************************************************
+ NVIDIA Camera Common Defines
+****************************************************/
+
+enum mt9m021_modes{
+ MT9M021_DEFAULT_MODE
+};
+
+static const int mt9m021_framerates[] = {10, 20, 30, 40, 50, 60,};
+
+#endif
diff --git a/nvidia/drivers/media/i2c/mt9m021.c b/nvidia/drivers/media/i2c/mt9m021.c
new file mode 100644
index 0000000..6fcd422
--- /dev/null
+++ b/nvidia/drivers/media/i2c/mt9m021.c
@@ -0,0 +1,1262 @@
+/*
+ * mt9m021.c - MT9M021 driver
+ *
+ * Copyright (c) 2019, RidgeRun. All rights reserved.
+ *
+ * Author: Enrique Ramírez (enrique.ramirez@ridgerun.com)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#define DEBUG 1
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "../platform/tegra/camera/camera_gpio.h"
+#include "mt9m021_mode_tbls.h"
+
+/***************************************************
+ MT9M021 Image Sensor Registers
+****************************************************/
+
+#define MT9M021_CHIP_ID_REG 0x3000
+#define MT9M021_RESET_REG 0x301A
+#define MT9M021_SEQ_CTRL_PORT 0x3088
+#define MT9M021_SEQ_DATA_PORT 0x3086
+#define MT9M021_ANALOG_REG 0x3ED6
+#define MT9M021_TEST_RAW_MODE 0x307A
+#define MT9M021_DARK_CTRL 0x3044
+#define MT9M021_DATA_PEDESTAL 0x301E
+#define MT9M021_COLUMN_CORRECTION 0x30D4
+#define MT9M021_FLASH 0x3046
+
+#define MT9M021_VT_SYS_CLK_DIV 0x302A
+#define MT9M021_VT_PIX_CLK_DIV 0x302C
+#define MT9M021_PRE_PLL_CLK_DIV 0x302E
+#define MT9M021_PLL_MULTIPLIER 0x3030
+#define MT9M021_DIGITAL_TEST 0x30B0
+
+#define MT9M021_Y_ADDR_START 0x3002
+#define MT9M021_X_ADDR_START 0x3004
+#define MT9M021_Y_ADDR_END 0x3006
+#define MT9M021_X_ADDR_END 0x3008
+#define MT9M021_FRAME_LENGTH_LINES 0x300A
+#define MT9M021_LINE_LENGTH_PCK 0x300C
+#define MT9M021_COARSE_INT_TIME 0x3012
+#define MT9M021_FINE_INT_TIME 0x3014
+#define MT9M021_COARSE_INT_TIME_CB 0x3016
+#define MT9M021_FINE_INT_TIME_CB 0x3018
+#define MT9M021_FRAME_LENGTH_LINES_CB 0x30AA
+#define MT9M021_X_ODD_INC 0x30A2
+#define MT9M021_Y_ODD_INC 0x30A6
+#define MT9M021_READ_MODE 0x3040
+#define MT9M021_TEST_PATTERN 0x3070
+#define MT9M021_DIGITAL_BINNING 0x3032
+
+#define MT9M021_AE_CTRL_REG 0x3100
+#define MT9M021_AE_LUMA_TARGET_REG 0x3102
+#define MT9M021_EMBEDDED_DATA_CTRL 0x3064
+#define MT9M021_DATAPATH_SELECT 0X306E
+
+#define MT9M021_GREEN1_GAIN 0x3056
+#define MT9M021_BLUE_GAIN 0x3058
+#define MT9M021_RED_GAIN 0x305A
+#define MT9M021_GREEN2_GAIN 0x305C
+#define MT9M021_GLOBAL_GAIN 0x305E
+#define MT9M021_GREEN1_GAIN_CB 0x30BC
+#define MT9M021_BLUE_GAIN_CB 0x30BE
+#define MT9M021_RED_GAIN_CB 0x30C0
+#define MT9M021_GREEN2_GAIN_CB 0x30C2
+#define MT9M021_GLOBAL_GAIN_CB 0x30C4
+
+/***************************************************
+ MT9M021 Image Sensor Defines
+****************************************************/
+
+#define BRIDGE_I2C_ADDR 0x0e
+#define MT9M021_I2C_ADDR 0x10
+#define MT9M021_CHIP_ID 0x2401
+
+#define MT9M021_PIXEL_ARRAY_WIDTH 1280
+#define MT9M021_PIXEL_ARRAY_HEIGHT 960
+
+#define MT9M021_EXT_FREQ 24000000
+#define MT9M021_TARGET_FREQ 74250000
+#define MT9M021_PLL_M 99
+#define MT9M021_PLL_N 4
+#define MT9M021_PLL_P1 1
+#define MT9M021_PLL_P2 8
+
+#define MT9M021_ROW_START_MIN 0
+#define MT9M021_ROW_START_MAX 960
+#define MT9M021_ROW_START_DEF 0x0078
+#define MT9M021_COLUMN_START_MIN 0
+#define MT9M021_COLUMN_START_MAX 1280
+#define MT9M021_COLUMN_START_DEF 0
+#define MT9M021_WINDOW_HEIGHT_MIN 2
+#define MT9M021_WINDOW_HEIGHT_MAX 720
+#define MT9M021_WINDOW_HEIGHT_DEF 720
+#define MT9M021_WINDOW_WIDTH_MIN 2
+#define MT9M021_WINDOW_WIDTH_MAX 1280
+#define MT9M021_WINDOW_WIDTH_DEF 1280
+#define MT9M021_HOR_AND_VER_BIN 0x0020
+#define MT9M021_HOR_BIN 0x0011
+#define MT9M021_DISABLE_BINNING 0x0000
+
+#define MT9M021_RESET 0x00D9
+#define MT9M021_STREAM_OFF 0x00D8
+#define MT9M021_STREAM_ON 0x00DC
+#define MT9M021_MASTER_MODE 0x10DC
+#define MT9M021_TRIGGER_MODE 0x19D8
+
+#define MT9M021_ANALOG_GAIN_MIN 0x0
+#define MT9M021_ANALOG_GAIN_MAX 0x3
+#define MT9M021_ANALOG_GAIN_DEF 0x0
+#define MT9M021_ANALOG_GAIN_SHIFT 4
+#define MT9M021_ANALOG_GAIN_MASK 0x0030
+
+#define MT9M021_GLOBAL_GAIN_MIN 4
+#define MT9M021_GLOBAL_GAIN_MAX 6476
+#define MT9M021_GLOBAL_GAIN_DEF 100
+
+#define MT9M021_COARSE_INT_TIME_MIN 0x0001
+#define MT9M021_COARSE_INT_TIME_MAX 0x02A0
+#define MT9M021_COARSE_INT_TIME_DEF 0x01C2
+
+#define MT9M021_LLP_RECOMMENDED 0x0672
+
+#define MT9M021_TEST_PATTERN_VAL 0x0
+
+#define MT9M021_GAIN_1X 0x0
+#define MT9M021_GAIN_2X 0x1
+#define MT9M021_GAIN_4X 0x2
+#define MT9M021_GAIN_8X 0x3
+
+#define MT9M021_GAIN_1X_FIXED 100
+#define MT9M021_GAIN_2X_FIXED 200
+#define MT9M021_GAIN_4X_FIXED 400
+#define MT9M021_GAIN_8X_FIXED 800
+
+#define MT9M021_HFLIP_MASK 0x4000
+#define MT9M021_VFLIP_MASK 0x8000
+
+/***************************************************
+ TC358746AXBG MIPI Converter Defines
+****************************************************/
+
+struct mipi_bridge_settings {
+ uint8_t len;
+ uint16_t addr;
+ uint32_t data;
+};
+
+struct mipi_bridge_settings bridge_config[] = {
+ {2, 0x0004, 0x0004}, // Parallel Data Format mode0 + PCLK Inverted
+ {2, 0x0002, 0x0001}, // reset 1
+ {2, 0x0002, 0x0000}, // reset 0
+ {2, 0x0016, 0x50F9}, // set the input and feedback frequency division ratio
+ {2, 0x0018, 0x0213}, // 50% maximum loop bandwidth + PLL clock enable + normal operation + PLL enable
+ {2, 0x0006, 0x0030}, // FIFO level 3
+
+#ifdef USE_RAW8
+ {2, 0x0008, 0x0000}, // data format RAW8
+#else
+ {2, 0x0008, 0x0020}, // data format RAW12
+#endif
+
+ {2, 0x0022, 0x0780}, // word count (bytes per line) 2560
+// {4, 0x0140,0x00000000},
+// {4, 0x0144,0x00000000},
+// {4, 0x0148,0x00000001},
+// {4, 0x014C,0x00000001},
+// {4, 0x0150,0x00000001},
+ {4, 0x0210, 0x00002C00},
+ {4, 0x0214, 0x00000005},
+ {4, 0x0218, 0x00001E06},
+ {4, 0x021C, 0x00000004},
+ {4, 0x0220, 0x00000406},
+ {4, 0x0224, 0x00004988},
+ {4, 0x0228, 0x0000000C},
+ {4, 0x022C, 0x00000006},
+ {4, 0x0234, 0x0000001F}, // Voltage regulator enable for data 0-3 and clock lanes.
+// {4, 0x0238, 0x00000001}, // Continuous clock mode. Maintains the clock lane output regardless of data lane operation
+ {4, 0x0238, 0x00000000}, // Discontinuous clock mode.
+ {4, 0x0518, 0x00000001}, // CSI start
+
+ {4, 0x0500, 0xA30080A1}, // 1 data lane
+// {4, 0x0500, 0xA30080A3}, // 2 data lanes
+// {4, 0x0500, 0xA30080A7}, // 4 data lanes
+
+ {4, 0x0204, 0x00000001}, // TX PPI starts
+ {2, 0x0004, 0x0044},
+};
+
+static int mt9m021_bridge_setup(struct i2c_client *client)
+{
+ struct i2c_msg msg[2];
+ uint8_t buf[6];
+ struct mipi_bridge_settings settings;
+ uint16_t __addr;
+ uint32_t __data;
+ int ret;
+ uint8_t i;
+
+ for (i = 0; i < ARRAY_SIZE(bridge_config); i++) {
+ settings = bridge_config[i];
+
+ __addr = cpu_to_be16(settings.addr);
+ __data = cpu_to_be32(settings.data);
+
+ buf[0] = (uint8_t) (__addr);
+ buf[1] = (uint8_t) (__addr >> 8);
+
+ buf[2] = (uint8_t) (__data >> 16);
+ buf[3] = (uint8_t) (__data >> 24);
+
+ buf[4] = (uint8_t) (__data);
+ buf[5] = (uint8_t) (__data >> 8);
+
+ msg[0].addr = BRIDGE_I2C_ADDR;
+ msg[0].flags = 0;
+ msg[0].len = settings.len + 2;
+ msg[0].buf = buf;
+
+ ret = i2c_transfer(client->adapter, msg, 1);
+
+ if (settings.len == 2)
+ dev_dbg(&client->dev, "BRIDGE %s: 0x%04x to 0x%04x\n",
+ __func__, settings.data, settings.addr);
+ else
+ dev_dbg(&client->dev, "BRIDGE %s: 0x%08x to 0x%04x\n",
+ __func__, settings.data, settings.addr);
+
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "BRIDGE %s failed at 0x%04x error %d\n",
+ __func__, settings.addr, ret);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static const struct of_device_id mt9m021_of_match[] = {
+ {.compatible = "nvidia,mt9m021",},
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, mt9m021_of_match);
+
+static const u32 ctrl_cid_list[] = {
+ TEGRA_CAMERA_CID_GAIN,
+ TEGRA_CAMERA_CID_EXPOSURE,
+ TEGRA_CAMERA_CID_FRAME_RATE,
+ TEGRA_CAMERA_CID_SENSOR_MODE_ID,
+ V4L2_CID_ANALOGUE_GAIN,
+ V4L2_CID_GAIN_RED,
+ V4L2_CID_GAIN_GREENR,
+ V4L2_CID_GAIN_GREENB,
+ V4L2_CID_GAIN_BLUE,
+ V4L2_CID_TEST_PATTERN,
+ V4L2_CID_FLASH_LED_MODE,
+ V4L2_CID_HFLIP,
+ V4L2_CID_VFLIP,
+};
+
+struct mt9m021 {
+ struct i2c_client *i2c_client;
+ struct v4l2_subdev *subdev;
+ const char *trigger_mode;
+ bool flash_en;
+ 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,
+};
+
+static inline int mt9m021_read_reg(struct camera_common_data *s_data,
+ u16 addr, u8 * val)
+{
+ int err = 0;
+ u32 reg_val = 0;
+
+ err = regmap_read(s_data->regmap, addr, ®_val);
+ *val = reg_val & 0xff;
+
+ return err;
+}
+
+static int mt9m021_read_reg16(struct camera_common_data *s_data,
+ u16 addr, u16 * val)
+{
+ u8 val_hi;
+ u8 val_lo;
+ int err = 0;
+
+ if (!s_data)
+ return -EFAULT;
+
+ err = mt9m021_read_reg(s_data, addr, &val_hi);
+ if (err)
+ return err;
+
+ err = mt9m021_read_reg(s_data, addr + 1, &val_lo);
+ *val = (val_hi << 8) | val_lo;
+
+ return err;
+}
+
+static inline int mt9m021_write_reg(struct camera_common_data *s_data,
+ u16 addr, u8 val)
+{
+ int err = 0;
+
+ err = regmap_write(s_data->regmap, addr, val);
+ if (err)
+ dev_err(s_data->dev, "%s: i2c write failed, 0x%x = %x",
+ __func__, addr, val);
+
+ return err;
+}
+
+static int
+mt9m021_write_reg16(struct camera_common_data *s_data, u16 addr, u16 val)
+{
+ int err;
+ u8 data[2];
+
+ dev_dbg(s_data->dev, "%s: 0x%04x to 0x%04x\n", __func__, val, addr);
+
+ data[0] = (val >> 8) & 0xff;;
+ data[1] = (val & 0xff);
+
+ err = regmap_raw_write(s_data->regmap, addr, data, 2);
+ if (err)
+ dev_err(s_data->dev, "%s: i2c write failed, 0x%x = %x",
+ __func__, addr, val);
+
+ return err;
+}
+
+static int
+mt9m021_update_bits(struct camera_common_data *s_data, u16 addr, u16 val,
+ u16 mask)
+{
+ u16 reg16;
+ int err;
+
+ err = mt9m021_read_reg16(s_data, mask, ®16);
+ if (err)
+ return err;
+
+ reg16 = (reg16 & ~mask) | (val & mask);
+ err = mt9m021_write_reg16(s_data, addr, reg16);
+
+ return err;
+}
+
+static int mt9m021_write_table(struct mt9m021 *priv, const mt9m021_reg table[])
+{
+ return regmap_util_write_table_16_as_8(priv->s_data->regmap, table,
+ NULL, 0, MT9M021_TABLE_WAIT_MS,
+ MT9M021_TABLE_END);
+}
+
+static int mt9m021_set_group_hold(struct tegracam_device *tc_dev, bool val)
+{
+ /* mt9m021 does not support group hold */
+ return 0;
+}
+
+static int mt9m021_set_gain(struct tegracam_device *tc_dev, s64 val)
+{
+ struct camera_common_data *s_data = tc_dev->s_data;
+ struct device *dev = tc_dev->dev;
+ int err = 0;
+ u16 gain_mul;
+ u16 reg16 = 0;
+ u8 integer, fraction;
+
+ dev_dbg(dev, "Setting Gain to: %lld", val);
+
+ if (val >= MT9M021_GAIN_8X_FIXED) {
+ integer = val / MT9M021_GAIN_8X_FIXED;
+ fraction = ((val / 8) % 100) * 32 / 100;
+ gain_mul = MT9M021_GAIN_8X;
+ } else if (val >= MT9M021_GAIN_4X_FIXED) {
+ integer = val / MT9M021_GAIN_4X_FIXED;
+ fraction = ((val / 4) % 100) * 32 / 100;
+ gain_mul = MT9M021_GAIN_4X;
+ } else if (val >= MT9M021_GAIN_2X_FIXED) {
+ integer = val / MT9M021_GAIN_2X_FIXED;
+ fraction = ((val / 2) % 100) * 32 / 100;
+ gain_mul = MT9M021_GAIN_2X;
+ } else {
+ integer = val / MT9M021_GAIN_1X_FIXED;
+ fraction = (val % 100) * 32 / 100;
+ gain_mul = MT9M021_GAIN_1X;
+ }
+
+ /* Update analog gain multiplier */
+ err = mt9m021_read_reg16(s_data, MT9M021_DIGITAL_TEST, ®16);
+ if (err)
+ goto exit;
+ reg16 =
+ (reg16 & ~MT9M021_ANALOG_GAIN_MASK) |
+ ((gain_mul << MT9M021_ANALOG_GAIN_SHIFT) &
+ MT9M021_ANALOG_GAIN_MASK);
+ err = mt9m021_write_reg16(s_data, MT9M021_DIGITAL_TEST, reg16);
+ msleep(10);
+ if (err)
+ goto exit;
+
+ /* Update global gain */
+ err =
+ mt9m021_write_reg16(s_data, MT9M021_GLOBAL_GAIN,
+ (integer << 5) | fraction);
+ msleep(10);
+ if (err)
+ goto exit;
+ err =
+ mt9m021_write_reg16(s_data, MT9M021_GLOBAL_GAIN_CB,
+ (integer << 5) | fraction);
+ if (err)
+ goto exit;
+
+ return 0;
+
+exit:
+ dev_err(dev, "Gain control error: %d", err);
+
+ return err;
+}
+
+static int mt9m021_set_frame_length(struct mt9m021 *priv, s64 val)
+{
+ struct device *dev = &priv->i2c_client->dev;
+ struct camera_common_data *s_data = priv->s_data;
+ int err = 0;
+
+ dev_dbg(dev, "Setting Frame Length to: %lld", val);
+
+ err = mt9m021_write_reg16(s_data, MT9M021_FRAME_LENGTH_LINES, val);
+ if (err)
+ return err;
+ msleep(30);
+
+ return 0;
+}
+
+static int mt9m021_set_frame_rate(struct tegracam_device *tc_dev, s64 val)
+{
+ struct camera_common_data *s_data = tc_dev->s_data;
+ struct mt9m021 *priv = (struct mt9m021 *)tc_dev->priv;
+ struct device *dev = tc_dev->dev;
+ const struct sensor_mode_properties *mode =
+ &s_data->sensor_props.sensor_modes[s_data->mode];
+ u64 frame_length;
+ int err;
+
+ dev_dbg(dev, "Setting Frame rate to: %lld", val);
+
+ /* Calculate frame-length */
+ frame_length = mode->signal_properties.pixel_clock.val *
+ mode->control_properties.framerate_factor /
+ mode->image_properties.line_length / val;
+
+ err = mt9m021_set_frame_length(priv, frame_length);
+ if (err)
+ goto exit;
+
+ return 0;
+
+exit:
+ dev_err(dev, "Frame rate control error: %d", err);
+ return err;
+}
+
+static int mt9m021_set_coarse_time(struct mt9m021 *priv, s64 val)
+{
+ struct device *dev = &priv->i2c_client->dev;
+ struct camera_common_data *s_data = priv->s_data;
+ int err = 0;
+
+ dev_dbg(dev, "Setting Coarse Time to: %lld", val);
+
+ err = mt9m021_write_reg16(s_data, MT9M021_COARSE_INT_TIME, val);
+ if (err)
+ return err;
+ err = mt9m021_write_reg16(s_data, MT9M021_COARSE_INT_TIME_CB, val);
+ if (err)
+ return err;
+ msleep(30);
+
+ return 0;
+}
+
+static int mt9m021_set_exposure(struct tegracam_device *tc_dev, s64 val)
+{
+ struct camera_common_data *s_data = tc_dev->s_data;
+ struct mt9m021 *priv = (struct mt9m021 *)tc_dev->priv;
+ struct device *dev = tc_dev->dev;
+ const struct sensor_mode_properties *mode =
+ &s_data->sensor_props.sensor_modes[s_data->mode];
+ u64 coarse_time;
+ int err = 0;
+
+ dev_dbg(dev, "Setting Exposure Time to: %lld", val);
+
+ /* Calculate coarse-time */
+ coarse_time = mode->signal_properties.pixel_clock.val *
+ val / mode->image_properties.line_length /
+ mode->control_properties.exposure_factor;
+
+ err = mt9m021_set_coarse_time(priv, coarse_time);
+ if (err)
+ goto exit;
+
+ return err;
+
+exit:
+ dev_err(dev, "Exposure control error: %d", err);
+ return err;
+}
+
+static int mt9m021_set_analog_gain(struct tegracam_device *tc_dev, s64 val)
+{
+ struct camera_common_data *s_data = tc_dev->s_data;
+ struct device *dev = tc_dev->dev;
+ int err = 0;
+ u16 reg16 = 0;
+
+ dev_dbg(dev, "Setting Analog Gain to: %lld", val);
+
+ /* Update analog gain multiplier */
+ err = mt9m021_read_reg16(s_data, MT9M021_DIGITAL_TEST, ®16);
+ if (err)
+ goto exit;
+ reg16 =
+ (reg16 & ~MT9M021_ANALOG_GAIN_MASK) |
+ ((val << MT9M021_ANALOG_GAIN_SHIFT) &
+ MT9M021_ANALOG_GAIN_MASK);
+ err = mt9m021_write_reg16(s_data, MT9M021_DIGITAL_TEST, reg16);
+ msleep(30);
+ if (err)
+ goto exit;
+
+ return 0;
+
+exit:
+ dev_err(dev, "Analog Gain control error: %d", err);
+
+ return err;
+}
+
+static int mt9m021_set_digital_gain(struct tegracam_device *tc_dev, s64 val,
+ int id)
+{
+ struct camera_common_data *s_data = tc_dev->s_data;
+ struct device *dev = tc_dev->dev;
+ u32 gain_reg;
+ u32 gain_cb_reg;
+ const char *gain_name;
+ int err = 0;
+
+ switch (id) {
+ case V4L2_CID_GAIN_RED:
+ gain_name = "Red Gain";
+ gain_reg = MT9M021_RED_GAIN;
+ gain_cb_reg = MT9M021_RED_GAIN_CB;
+ break;
+ case V4L2_CID_GAIN_GREENR:
+ gain_name = "GreenR Gain";
+ gain_reg = MT9M021_GREEN1_GAIN;
+ gain_cb_reg = MT9M021_GREEN1_GAIN_CB;
+ break;
+ case V4L2_CID_GAIN_GREENB:
+ gain_name = "GreenB Gain";
+ gain_reg = MT9M021_GREEN2_GAIN;
+ gain_cb_reg = MT9M021_GREEN2_GAIN_CB;
+ break;
+ case V4L2_CID_GAIN_BLUE:
+ gain_name = "Blue Gain";
+ gain_reg = MT9M021_BLUE_GAIN;
+ gain_cb_reg = MT9M021_BLUE_GAIN_CB;
+ break;
+ default:
+ dev_err(dev, "Unknown ctrl id %d", id);
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "Setting %s to: %lld", gain_name, val);
+
+ err = mt9m021_write_reg16(s_data, gain_reg, val);
+ if (err)
+ goto exit;
+ err = mt9m021_write_reg16(s_data, gain_cb_reg, val);
+ if (err)
+ goto exit;
+ msleep(30);
+
+ return 0;
+
+exit:
+ dev_err(dev, "Digital Gain control error: %d", err);
+ return err;
+}
+
+static int mt9m021_set_test_pattern(struct tegracam_device *tc_dev, s32 val)
+{
+ struct camera_common_data *s_data = tc_dev->s_data;
+ struct device *dev = tc_dev->dev;
+ int err = 0;
+
+ dev_dbg(dev, "Setting Test Pattern to: %d", val);
+
+ if (!val)
+ err = mt9m021_write_reg16(s_data, MT9M021_TEST_PATTERN,
+ MT9M021_TEST_PATTERN_VAL);
+ else
+ err = mt9m021_write_reg16(s_data, MT9M021_TEST_PATTERN,
+ val);
+ msleep(30);
+ if (err)
+ goto exit;
+
+ return 0;
+
+exit:
+ dev_err(dev, "Test Pattern control error: %d", err);
+
+ return err;
+}
+
+static int mt9m021_set_flash(struct tegracam_device *tc_dev, s32 val)
+{
+ struct camera_common_data *s_data = tc_dev->s_data;
+ struct mt9m021 *priv = (struct mt9m021 *)tc_dev->priv;
+ struct device *dev = tc_dev->dev;
+ int err = 0;
+
+ dev_dbg(dev, "Setting Flash to: %d", val);
+
+ switch(val) {
+ case V4L2_FLASH_LED_MODE_NONE:
+ err = mt9m021_write_reg16(s_data, MT9M021_FLASH, 0x0000);
+ priv->flash_en = false;
+ break;
+ case V4L2_FLASH_LED_MODE_FLASH:
+ err = mt9m021_write_reg16(s_data, MT9M021_FLASH, 0x0100);
+ priv->flash_en = true;
+ break;
+ default:
+ dev_err(dev, "Unsupported flash mode requested: %d\n", val);
+ err = -EINVAL;
+ break;
+ }
+
+ if (err)
+ goto exit;
+
+ return 0;
+exit:
+ dev_err(dev, "Flash control error: %d", err);
+
+ return err;
+}
+
+static int mt9m021_set_flip(struct tegracam_device *tc_dev, s32 val, int id)
+{
+ struct camera_common_data *s_data = tc_dev->s_data;
+ struct device *dev = tc_dev->dev;
+ int err = 0;
+
+ dev_dbg(dev, "Setting Flip to: %d", val);
+
+ switch(id) {
+ case V4L2_CID_HFLIP:
+ err = mt9m021_update_bits(s_data, MT9M021_READ_MODE,
+ val << 14, MT9M021_HFLIP_MASK);
+ break;
+ case V4L2_CID_VFLIP:
+ err = mt9m021_update_bits(s_data, MT9M021_READ_MODE,
+ val << 15, MT9M021_VFLIP_MASK);
+ break;
+ default:
+ dev_err(dev, "Unsupported flip mode requested: %d\n", val);
+ err = -EINVAL;
+ break;
+ }
+
+ if (err)
+ goto exit;
+
+ return 0;
+exit:
+ dev_err(dev, "Flip control error: %d", err);
+
+ return err;
+}
+
+static struct tegracam_ctrl_ops mt9m021_ctrl_ops = {
+ .numctrls = ARRAY_SIZE(ctrl_cid_list),
+ .ctrl_cid_list = ctrl_cid_list,
+ .set_gain = mt9m021_set_gain,
+ .set_exposure = mt9m021_set_exposure,
+ .set_frame_rate = mt9m021_set_frame_rate,
+ .set_group_hold = mt9m021_set_group_hold,
+ .set_analog_gain = mt9m021_set_analog_gain,
+ .set_digital_gain = mt9m021_set_digital_gain,
+ .set_test_pattern = mt9m021_set_test_pattern,
+ .set_flash = mt9m021_set_flash,
+ .set_flip = mt9m021_set_flip,
+};
+
+static int mt9m021_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, 1);
+ usleep_range(50000, 51000);
+ }
+
+ pw->state = SWITCH_ON;
+ return 0;
+
+}
+
+static int mt9m021_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)
+ goto power_off_done;
+ else
+ dev_err(dev, "%s failed.\n", __func__);
+ return err;
+ }
+ usleep_range(2000, 2010);
+
+ if (pw->reset_gpio) {
+ gpio_set_value(pw->reset_gpio, 0);
+ usleep_range(2000, 2010);
+ }
+
+power_off_done:
+ pw->state = SWITCH_OFF;
+
+ return 0;
+}
+
+static int mt9m021_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;
+
+ if (unlikely(!pw))
+ return -EFAULT;
+
+ if (likely(pw->dvdd))
+ regulator_disable(pw->dvdd);
+
+ if (likely(pw->avdd))
+ regulator_put(pw->avdd);
+
+ if (likely(pw->iovdd))
+ regulator_put(pw->iovdd);
+
+ pw->dvdd = NULL;
+ pw->avdd = NULL;
+ pw->iovdd = NULL;
+
+ if (likely(pw->reset_gpio))
+ gpio_free(pw->reset_gpio);
+
+ return 0;
+}
+
+static int mt9m021_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;
+ struct clk *parent;
+ int err = 0;
+ int ret = 0;
+
+ mclk_name = pdata->mclk_name ? pdata->mclk_name : "extperiph1";
+ 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);
+ }
+
+ parent = devm_clk_get(dev, "pllp_grtba");
+ if (IS_ERR(parent))
+ dev_err(dev, "devm_clk_get failed for pllp_grtba");
+ else
+ clk_set_parent(pw->mclk, parent);
+
+ pw->reset_gpio = pdata->reset_gpio;
+
+ if (pdata->use_cam_gpio) {
+ err = cam_gpio_register(dev, pw->pwdn_gpio);
+ if (err)
+ dev_err(dev, "%s ERR can't register cam gpio %u!\n",
+ __func__, pw->pwdn_gpio);
+ } else {
+ if (gpio_is_valid(pw->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);
+ }
+ gpio_direction_output(pw->reset_gpio, 1);
+ }
+ }
+
+ pw->state = SWITCH_OFF;
+ return err;
+}
+
+static struct camera_common_pdata *mt9m021_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;
+ struct camera_common_pdata *ret = NULL;
+ int err = 0;
+ int gpio;
+
+ if (!np)
+ return NULL;
+
+ match = of_match_device(mt9m021_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;
+
+ 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\n");
+ goto error;
+ }
+ board_priv_pdata->reset_gpio = (unsigned int)gpio;
+
+ err = of_property_read_string(np, "mclk", &board_priv_pdata->mclk_name);
+ if (err)
+ dev_dbg(dev, "mclk name not present, "
+ "assume sensor driven externally\n");
+
+ 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 mt9m021_sequencer_settings(struct mt9m021 *priv)
+{
+ struct device *dev = &priv->i2c_client->dev;
+ struct camera_common_data *s_data = priv->s_data;
+ int i, ret;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ ret = mt9m021_write_reg16(s_data, MT9M021_SEQ_CTRL_PORT, 0x8000);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(mt9m021_seq_data); i++) {
+ ret =
+ mt9m021_write_reg16(s_data, MT9M021_SEQ_DATA_PORT,
+ mt9m021_seq_data[i]);
+ if (ret < 0)
+ return ret;
+ }
+
+ return ret;
+}
+
+static int mt9m021_col_correction(struct mt9m021 *priv)
+{
+ struct device *dev = &priv->i2c_client->dev;
+ struct camera_common_data *s_data = priv->s_data;
+ int ret;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ /* Disable Streaming */
+ ret = mt9m021_write_table(priv, mode_table[MT9M021_MODE_STOP_STREAM]);
+ if (ret < 0)
+ return ret;
+
+ /* Disable column correction */
+ ret = mt9m021_write_reg16(s_data, MT9M021_COLUMN_CORRECTION, 0x0007);
+ if (ret < 0)
+ return ret;
+
+ msleep(200);
+
+ /* Enable Streaming */
+ ret = mt9m021_write_table(priv, mode_table[MT9M021_MODE_START_STREAM]);
+ if (ret < 0)
+ return ret;
+ msleep(200);
+
+ /* Disable Streaming */
+ ret = mt9m021_write_table(priv, mode_table[MT9M021_MODE_STOP_STREAM]);
+ if (ret < 0)
+ return ret;
+
+ /* Enable column correction */
+ ret = mt9m021_write_reg16(s_data, MT9M021_COLUMN_CORRECTION, 0xE007);
+ if (ret < 0)
+ return ret;
+ msleep(200);
+
+ return ret;
+}
+
+static int mt9m021_set_mode(struct tegracam_device *tc_dev)
+{
+ struct mt9m021 *priv = (struct mt9m021 *)tegracam_get_privdata(tc_dev);
+ struct camera_common_data *s_data = tc_dev->s_data;
+ struct device *dev = tc_dev->dev;
+ int ret;
+
+ dev_dbg(dev, "%s\n", __func__);
+ ret = mt9m021_bridge_setup(priv->i2c_client);
+ if (ret < 0) {
+ dev_err(dev, "%s: Failed to setup mipi bridge\n", __func__);
+ return ret;
+ }
+
+ ret = mt9m021_sequencer_settings(priv);
+ if (ret < 0) {
+ dev_err(dev, "%s: Failed to setup sequencer\n", __func__);
+ return ret;
+ }
+
+ ret = mt9m021_write_table(priv, mode_table[MT9M021_MODE_PLL_SETUP]);
+ if (ret < 0) {
+ dev_err(dev, "%s: Failed to set pll setup\n", __func__);
+ return ret;
+ }
+ msleep(100);
+
+ ret = mt9m021_col_correction(priv);
+ if (ret < 0) {
+ dev_err(dev, "%s: Failed to setup column correction\n",
+ __func__);
+ return ret;
+ }
+
+ ret = mt9m021_write_table(priv, mode_table[s_data->mode]);
+ if (ret < 0) {
+ dev_err(dev, "%s: Failed to setup sensor mode\n", __func__);
+ return ret;
+ }
+
+ ret = mt9m021_set_flash(tc_dev, priv->flash_en);
+ return ret;
+}
+
+static int mt9m021_start_streaming(struct tegracam_device *tc_dev)
+{
+ struct mt9m021 *priv = (struct mt9m021 *)tegracam_get_privdata(tc_dev);
+ struct camera_common_data *s_data = priv->s_data;
+
+ dev_info(tc_dev->dev, "Starting stream\n");
+
+ if (strstr(priv->trigger_mode, "slave") != NULL)
+ return mt9m021_write_reg16(s_data, MT9M021_RESET_REG,
+ MT9M021_TRIGGER_MODE);
+ else
+ return mt9m021_write_reg16(s_data, MT9M021_RESET_REG,
+ MT9M021_MASTER_MODE);
+}
+
+static int mt9m021_stop_streaming(struct tegracam_device *tc_dev)
+{
+ struct mt9m021 *priv = (struct mt9m021 *)tegracam_get_privdata(tc_dev);
+ struct device *dev = tc_dev->dev;
+ int err;
+
+ dev_info(dev, "Ending stream\n");
+
+ err = mt9m021_write_table(priv, mode_table[MT9M021_MODE_STOP_STREAM]);
+ if (err)
+ return err;
+
+ usleep_range(50000, 51000);
+
+ return err;
+}
+
+static struct camera_common_sensor_ops mt9m021_common_ops = {
+ .numfrmfmts = ARRAY_SIZE(mt9m021_frmfmt),
+ .frmfmt_table = mt9m021_frmfmt,
+ .power_on = mt9m021_power_on,
+ .power_off = mt9m021_power_off,
+ .write_reg = mt9m021_write_reg,
+ .read_reg = mt9m021_read_reg,
+ .parse_dt = mt9m021_parse_dt,
+ .power_get = mt9m021_power_get,
+ .power_put = mt9m021_power_put,
+ .set_mode = mt9m021_set_mode,
+ .start_streaming = mt9m021_start_streaming,
+ .stop_streaming = mt9m021_stop_streaming,
+};
+
+static int mt9m021_get_trigger_mode(struct mt9m021 *priv)
+{
+ int err = 0;
+ struct i2c_client *client = priv->i2c_client;
+ struct device_node *node = client->dev.of_node;
+ const struct of_device_id *match;
+
+ dev_dbg(&client->dev, "%s\n", __func__);
+
+ if (!node)
+ return -EFAULT;
+
+ match = of_match_device(mt9m021_of_match, &client->dev);
+ if (!match) {
+ dev_err(&client->dev, "Failed to find matching dt id\n");
+ return -EFAULT;
+ }
+
+ err =
+ of_property_read_string(node, "trigger_mode", &priv->trigger_mode);
+ if (err == -EINVAL) {
+ dev_warn(&client->dev, "trigger_mode not in device tree\n");
+ *(&priv->trigger_mode) = "master";
+ }
+
+ return 0;
+}
+
+static int mt9m021_verify_chip_id(struct mt9m021 *priv)
+{
+ struct i2c_client *client;
+ struct camera_common_data *s_data;
+ int max_retries = MT9M021_MAX_RETRIES;
+ u16 chip_id = 0;
+ int err;
+ int ret;
+
+ if (!priv)
+ return -EFAULT;
+
+ client = priv->i2c_client;
+ s_data = priv->s_data;
+
+ err = mt9m021_power_on(s_data);
+ if (err)
+ goto exit;
+
+ while (max_retries) {
+ err = mt9m021_read_reg16(s_data, MT9M021_CHIP_ID_REG, &chip_id);
+ if (!err)
+ break;
+ dev_info(&client->dev, "Failed to read Chip ID, trying again\n");
+ max_retries--;
+ msleep(30);
+ }
+ if (err) {
+ dev_err(&client->dev, "Failed to read Chip ID\n");
+ goto exit;
+ }
+ dev_info(&client->dev, "Read Chip ID 0x%04x\n", chip_id);
+
+ if (chip_id != MT9M021_CHIP_ID) {
+ dev_err(&client->dev, "Read unknown chip ID 0x%04x\n", chip_id);
+ err = -EINVAL;
+ goto exit;
+ }
+
+exit:
+ ret = mt9m021_power_off(s_data);
+ if (ret)
+ return ret;
+
+ return err;
+}
+
+static int mt9m021_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 mt9m021_subdev_internal_ops = {
+ .open = mt9m021_open,
+};
+
+static int mt9m021_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct tegracam_device *tc_dev;
+ struct mt9m021 *priv;
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ int err;
+
+ dev_dbg(dev, "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 mt9m021), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ tc_dev = devm_kzalloc(dev, sizeof(struct tegracam_device), GFP_KERNEL);
+ if (!tc_dev)
+ return -ENOMEM;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_err(&client->dev,
+ "i2c-adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
+ return -EIO;
+ }
+
+ priv->i2c_client = tc_dev->client = client;
+ tc_dev->dev = dev;
+ strncpy(tc_dev->name, "mt9m021", sizeof(tc_dev->name));
+ tc_dev->dev_regmap_config = &sensor_regmap_config;
+ tc_dev->sensor_ops = &mt9m021_common_ops;
+ tc_dev->v4l2sd_internal_ops = &mt9m021_subdev_internal_ops;
+ tc_dev->tcctrl_ops = &mt9m021_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);
+ priv->flash_en = true;
+
+ err = mt9m021_verify_chip_id(priv);
+ if (err)
+ return err;
+
+ err = mt9m021_get_trigger_mode(priv);
+ if (err)
+ return err;
+
+ if (strstr(priv->trigger_mode, "slave") != NULL)
+ dev_info(dev, "slave mode activated\n");
+
+ err = tegracam_v4l2subdev_register(tc_dev, true);
+ if (err) {
+ dev_err(dev, "tegra camera subdev registration failed\n");
+ return err;
+ }
+
+ dev_dbg(dev, "detected mt9m021 sensor\n");
+
+ return 0;
+}
+
+static int mt9m021_remove(struct i2c_client *client)
+{
+ struct camera_common_data *s_data = to_camera_common_data(&client->dev);
+ struct mt9m021 *priv = (struct mt9m021 *)s_data->priv;
+
+ tegracam_v4l2subdev_unregister(priv->tc_dev);
+ tegracam_device_unregister(priv->tc_dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id mt9m021_id[] = {
+ {"mt9m021", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, mt9m021_id);
+
+static struct i2c_driver mt9m021_i2c_driver = {
+ .driver = {
+ .name = "mt9m021",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(mt9m021_of_match),
+ },
+ .probe = mt9m021_probe,
+ .remove = mt9m021_remove,
+ .id_table = mt9m021_id,
+};
+
+module_i2c_driver(mt9m021_i2c_driver);
+
+MODULE_DESCRIPTION("Media Controller driver for Sony MT9M021");
+MODULE_AUTHOR("Enrique Ramirez ");
+MODULE_LICENSE("GPL v2");
diff --git a/nvidia/drivers/media/i2c/mt9m021_mode_tbls.h b/nvidia/drivers/media/i2c/mt9m021_mode_tbls.h
new file mode 100644
index 0000000..128173e
--- /dev/null
+++ b/nvidia/drivers/media/i2c/mt9m021_mode_tbls.h
@@ -0,0 +1,136 @@
+/*
+ * mt9m021_mode_tbls.h - MT9M021 sensor mode tables
+ *
+ * Copyright (c) 2019, RidgeRun. All rights reserved.
+ *
+ * Author: Enrique Ramírez (enrique.ramirez@ridgerun.com)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef __MT9M021_I2C_TABLES__
+#define __MT9M021_I2C_TABLES__
+
+#include
+
+#define MT9M021_TABLE_WAIT_MS 0
+#define MT9M021_TABLE_END 1
+#define MT9M021_MAX_RETRIES 6
+#define MT9M021_WAIT_MS 10
+
+#define mt9m021_reg struct reg_16
+
+static const mt9m021_reg mt9m021_start[] = {
+ {0x301A, 0x00DC}, /* Enable Streaming */
+ {MT9M021_TABLE_END, 0x00}
+};
+
+static const mt9m021_reg mt9m021_stop[] = {
+ {0x301A, 0x00D8}, /* Disable Streaming */
+ {MT9M021_TABLE_END, 0x00}
+};
+
+static uint16_t mt9m021_seq_data[] = {
+ 0x3227, 0x0101, 0x0F25, 0x0808, 0x0227, 0x0101, 0x0837, 0x2700,
+ 0x0138, 0x2701, 0x013A, 0x2700, 0x0125, 0x0020, 0x3C25, 0x0040,
+ 0x3427, 0x003F, 0x2500, 0x2037, 0x2540, 0x4036, 0x2500, 0x4031,
+ 0x2540, 0x403D, 0x6425, 0x2020, 0x3D64, 0x2510, 0x1037, 0x2520,
+ 0x2010, 0x2510, 0x100F, 0x2708, 0x0802, 0x2540, 0x402D, 0x2608,
+ 0x280D, 0x1709, 0x2600, 0x2805, 0x26A7, 0x2807, 0x2580, 0x8029,
+ 0x1705, 0x2500, 0x4027, 0x2222, 0x1616, 0x2726, 0x2617, 0x3626,
+ 0xA617, 0x0326, 0xA417, 0x1F28, 0x0526, 0x2028, 0x0425, 0x2020,
+ 0x2700, 0x2625, 0x0000, 0x171E, 0x2500, 0x0425, 0x0020, 0x2117,
+ 0x121B, 0x1703, 0x2726, 0x2617, 0x2828, 0x0517, 0x1A26, 0x6017,
+ 0xAE25, 0x0080, 0x2700, 0x2626, 0x1828, 0x002E, 0x2A28, 0x081E,
+ 0x4127, 0x1010, 0x0214, 0x6060, 0x0A14, 0x6060, 0x0B14, 0x6060,
+ 0x0C14, 0x6060, 0x0D14, 0x6060, 0x0217, 0x3C14, 0x0060, 0x0A14,
+ 0x0060, 0x0B14, 0x0060, 0x0C14, 0x0060, 0x0D14, 0x0060, 0x0811,
+ 0x2500, 0x1027, 0x0010, 0x2F6F, 0x0F3E, 0x2500, 0x0827, 0x0008,
+ 0x3066, 0x3225, 0x0008, 0x2700, 0x0830, 0x6631, 0x3D64, 0x2508,
+ 0x083D, 0xFF3D, 0x2A27, 0x083F, 0x2C00
+};
+
+static const mt9m021_reg mt9m021_pll_setup[] = {
+ {0x302C, 0x0001}, /* VT_PIX_CLK_DIV */
+ {0x302A, 0x0008}, /* VT_SYS_CLK_DIV */
+ {0x302E, 0x0004}, /* PRE_PLL_CLK_DIV */
+ {0x3030, 0x0063}, /* PLL_MULTIPLIER */
+ {0x30B0, 0x0000}, /* DIGITAL_TEST */
+ {MT9M021_TABLE_END, 0x00}
+};
+
+static const mt9m021_reg mt9m021_mode_1280x720_60fps[] = {
+ /* Rev2 Settings */
+ {0x307A, 0x0000},
+ {0x30EA, 0x0C00},
+ {0x3044, 0x0404},
+ {0x301E, 0x012C},
+ {0x3180, 0x8000},
+ {0x3014, 0x0000},
+
+ /* Analog Settings */
+ {0x3ED6, 0x00FD},
+ {0x3ED8, 0x0FFF},
+ {0x3EDA, 0x0003},
+ {0x3EDC, 0xF87A},
+ {0x3EDE, 0xE075},
+ {0x3EE0, 0x077C},
+ {0x3EE2, 0xA4EB},
+ {0x3EE4, 0xD208},
+
+ /* Size Settings */
+ {0x3064, 0x1802}, /* EMBEDDED_DATA_CTRL */
+ {0x3032, 0x0020}, /* DIGITAL_BINNING */
+ {0x3002, 0x0078}, /* Y ADDR START */
+ {0x3004, 0x0001}, /* X ADDR START */
+ {0x3006, 0x0347}, /* Y ADDR END */
+ {0x3008, 0x0500}, /* X ADDR END */
+ {0x300A, 0x02EE}, /* FRAME_LENGTH_LINES */
+ {0x300C, 0x0672}, /* LINE_LENGTH_PCK */
+ {0x30A2, 0x0001}, /* X_ODD_INC */
+ {0x30A6, 0x0001}, /* Y_ODD_INC */
+ {MT9M021_TABLE_END, 0x00}
+};
+
+enum {
+ MT9M021_MODE_1280x720_60FPS,
+
+ MT9M021_MODE_PLL_SETUP,
+
+ MT9M021_MODE_START_STREAM,
+ MT9M021_MODE_STOP_STREAM,
+};
+
+static const mt9m021_reg *mode_table[] = {
+ [MT9M021_MODE_1280x720_60FPS] = mt9m021_mode_1280x720_60fps,
+
+ [MT9M021_MODE_PLL_SETUP] = mt9m021_pll_setup,
+
+ [MT9M021_MODE_START_STREAM] = mt9m021_start,
+ [MT9M021_MODE_STOP_STREAM] = mt9m021_stop,
+};
+
+static const int mt9m021_framerates[] = {
+ 10,
+ 20,
+ 30,
+ 40,
+ 50,
+ 60,
+};
+
+static const struct camera_common_frmfmt mt9m021_frmfmt[] = {
+ {{1280, 720}, mt9m021_framerates, 1, 0, MT9M021_MODE_1280x720_60FPS},
+};
+
+#endif /* __MT9M021_I2C_TABLES__ */
diff --git a/nvidia/drivers/media/platform/tegra/camera/camera_common.c b/nvidia/drivers/media/platform/tegra/camera/camera_common.c
new file mode 100644
index 0000000..58e7ac2
--- /dev/null
+++ b/nvidia/drivers/media/platform/tegra/camera/camera_common.c
@@ -0,0 +1,1121 @@
+/*
+ * camera_common.c - utilities for tegra camera driver
+ *
+ * Copyright (c) 2015-2019, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#define has_s_op(master, op) \
+ (master->ops && master->ops->op)
+#define call_s_op(master, op) \
+ (has_s_op(master, op) ? \
+ master->ops->op(master) : 0)
+#define call_s_ops(master, op, ...) \
+ (has_s_op(master, op) ? \
+ master->ops->op(master, __VA_ARGS__) : 0)
+
+#define HDR_ENABLE 0x1
+
+static const struct camera_common_colorfmt camera_common_color_fmts[] = {
+ {
+ MEDIA_BUS_FMT_SRGGB12_1X12,
+ V4L2_COLORSPACE_SRGB,
+ V4L2_PIX_FMT_SRGGB12,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+ V4L2_COLORSPACE_SRGB,
+ V4L2_PIX_FMT_SGRBG12,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ V4L2_COLORSPACE_SRGB,
+ V4L2_PIX_FMT_SRGGB10,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ V4L2_COLORSPACE_SRGB,
+ V4L2_PIX_FMT_SGRBG10,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ V4L2_COLORSPACE_SRGB,
+ V4L2_PIX_FMT_SBGGR10,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB8_1X8,
+ V4L2_COLORSPACE_SRGB,
+ V4L2_PIX_FMT_SRGGB8,
+ },
+ {
+ MEDIA_BUS_FMT_YUYV8_1X16,
+ V4L2_COLORSPACE_SRGB,
+ V4L2_PIX_FMT_YUYV,
+ },
+ {
+ MEDIA_BUS_FMT_YVYU8_1X16,
+ V4L2_COLORSPACE_SRGB,
+ V4L2_PIX_FMT_YVYU,
+ },
+ {
+ MEDIA_BUS_FMT_UYVY8_1X16,
+ V4L2_COLORSPACE_SRGB,
+ V4L2_PIX_FMT_UYVY,
+ },
+ {
+ MEDIA_BUS_FMT_VYUY8_1X16,
+ V4L2_COLORSPACE_SRGB,
+ V4L2_PIX_FMT_VYUY,
+ },
+ {
+ MEDIA_BUS_FMT_YUYV8_2X8,
+ V4L2_COLORSPACE_SRGB,
+ V4L2_PIX_FMT_YUYV,
+ },
+ {
+ MEDIA_BUS_FMT_YVYU8_2X8,
+ V4L2_COLORSPACE_SRGB,
+ V4L2_PIX_FMT_YVYU,
+ },
+ {
+ MEDIA_BUS_FMT_UYVY8_2X8,
+ V4L2_COLORSPACE_SRGB,
+ V4L2_PIX_FMT_UYVY,
+ },
+ {
+ MEDIA_BUS_FMT_VYUY8_2X8,
+ V4L2_COLORSPACE_SRGB,
+ V4L2_PIX_FMT_VYUY,
+ },
+ /*
+ * The below two formats are not supported by VI4,
+ * keep them at the last to ensure they get discarded
+ */
+ {
+ MEDIA_BUS_FMT_XRGGB10P_3X10,
+ V4L2_COLORSPACE_SRGB,
+ V4L2_PIX_FMT_XRGGB10P,
+ },
+ {
+ MEDIA_BUS_FMT_XBGGR10P_3X10,
+ V4L2_COLORSPACE_SRGB,
+ V4L2_PIX_FMT_XRGGB10P,
+ },
+};
+
+struct camera_common_csi_io_pad_ctx {
+ const char *name;
+ atomic_t ref;
+};
+
+static struct camera_common_csi_io_pad_ctx camera_common_csi_io_pads[] = {
+ {"csia", ATOMIC_INIT(0)},
+ {"csib", ATOMIC_INIT(0)},
+ {"csic", ATOMIC_INIT(0)},
+ {"csid", ATOMIC_INIT(0)},
+ {"csie", ATOMIC_INIT(0)},
+ {"csif", ATOMIC_INIT(0)},
+ {"csig", ATOMIC_INIT(0)},
+ {"csih", ATOMIC_INIT(0)},
+};
+
+static bool camera_common_verify_code(
+ struct tegra_channel *chan, unsigned int code)
+{
+ int i;
+
+ for (i = 0; i < chan->num_video_formats; i++) {
+ if (chan->video_formats[i]->code == code)
+ return true;
+ }
+
+ return false;
+}
+
+int camera_common_g_ctrl(struct camera_common_data *s_data,
+ struct v4l2_control *control)
+{
+ int i;
+
+ for (i = 0; i < s_data->numctrls; i++) {
+ if (s_data->ctrls[i]->id == control->id) {
+ control->value = s_data->ctrls[i]->val;
+ dev_dbg(s_data->dev,
+ "%s: found control %s\n", __func__,
+ s_data->ctrls[i]->name);
+ return 0;
+ }
+ }
+
+ return -EFAULT;
+}
+EXPORT_SYMBOL_GPL(camera_common_g_ctrl);
+
+int camera_common_regulator_get(struct device *dev,
+ struct regulator **vreg, const char *vreg_name)
+{
+ struct regulator *reg = NULL;
+ int err = 0;
+
+ reg = devm_regulator_get(dev, vreg_name);
+ if (unlikely(IS_ERR(reg))) {
+ dev_err(dev, "%s %s ERR: %p\n",
+ __func__, vreg_name, reg);
+ err = PTR_ERR(reg);
+ reg = NULL;
+ } else
+ dev_dbg(dev, "%s: %s\n",
+ __func__, vreg_name);
+
+ *vreg = reg;
+ return err;
+}
+EXPORT_SYMBOL_GPL(camera_common_regulator_get);
+
+int camera_common_parse_clocks(struct device *dev,
+ struct camera_common_pdata *pdata)
+{
+ struct device_node *np = dev->of_node;
+ const char *prop;
+ int proplen = 0;
+ int i = 0;
+ int numclocks = 0;
+ int mclk_index = 0;
+ int parentclk_index = -1;
+ int err = 0;
+
+
+ pdata->mclk_name = NULL;
+ pdata->parentclk_name = NULL;
+ err = of_property_read_string(np, "mclk", &pdata->mclk_name);
+ if (!err) {
+ dev_dbg(dev, "mclk in DT %s\n", pdata->mclk_name);
+ of_property_read_string(np, "parent-clk",
+ &pdata->parentclk_name);
+ return 0;
+ }
+
+ prop = (const char *)of_get_property(np, "clock-names", &proplen);
+ if (!prop)
+ return -ENODATA;
+
+ /* find length of clock-names string array */
+ for (i = 0; i < proplen; i++) {
+ if (prop[i] == '\0')
+ numclocks++;
+ }
+
+ if (numclocks > 1) {
+ err = of_property_read_u32(np, "mclk-index", &mclk_index);
+ if (err) {
+ dev_err(dev, "Failed to find mclk index\n");
+ return err;
+ }
+ err = of_property_read_u32(np, "parent-clk-index",
+ &parentclk_index);
+ }
+
+ for (i = 0; i < numclocks; i++) {
+ if (i == mclk_index) {
+ pdata->mclk_name = prop;
+ dev_dbg(dev, "%s: mclk_name is %s\n",
+ __func__, pdata->mclk_name);
+ } else if (i == parentclk_index) {
+ pdata->parentclk_name = prop;
+ dev_dbg(dev, "%s: parentclk_name is %s\n",
+ __func__, pdata->parentclk_name);
+ } else
+ dev_dbg(dev, "%s: %s\n", __func__, prop);
+ prop += strlen(prop) + 1;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(camera_common_parse_clocks);
+
+int camera_common_parse_ports(struct device *dev,
+ struct camera_common_data *s_data)
+{
+ struct device_node *node = dev->of_node;
+ struct device_node *ep = NULL;
+ struct device_node *next;
+ int bus_width = 0;
+ int err = 0;
+ int port = 0;
+
+ /* Parse all the remote entities and put them into the list */
+ next = of_graph_get_next_endpoint(node, ep);
+ if (!next)
+ return -ENODATA;
+
+ of_node_put(ep);
+ ep = next;
+
+ err = of_property_read_u32(ep, "bus-width", &bus_width);
+ if (err) {
+ dev_err(dev,
+ "Failed to find num of lanes\n");
+ return err;
+ }
+ s_data->numlanes = bus_width;
+
+ err = of_property_read_u32(ep, "port-index", &port);
+ if (err) {
+ dev_err(dev,
+ "Failed to find port index\n");
+ return err;
+ }
+ s_data->csi_port = port;
+
+ dev_dbg(dev, "%s: port %d num of lanes %d\n",
+ __func__, s_data->csi_port, s_data->numlanes);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(camera_common_parse_ports);
+
+int camera_common_parse_general_properties(struct device *dev,
+ struct camera_common_data *s_data)
+{
+ struct device_node *np = dev->of_node;
+ int err = 0;
+ const char *str;
+
+ s_data->use_sensor_mode_id = false;
+ err = of_property_read_string(np, "use_sensor_mode_id", &str);
+ if (!err) {
+ if (!strcmp(str, "true"))
+ s_data->use_sensor_mode_id = true;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(camera_common_parse_general_properties);
+
+int camera_common_debugfs_show(struct seq_file *s, void *unused)
+{
+ struct camera_common_data *s_data = s->private;
+
+ dev_dbg(s_data->dev, "%s: ++\n", __func__);
+
+ return 0;
+}
+
+ssize_t camera_common_debugfs_write(
+ struct file *file,
+ char const __user *buf,
+ size_t count,
+ loff_t *offset)
+{
+ struct camera_common_data *s_data =
+ ((struct seq_file *)file->private_data)->private;
+ struct device *dev = s_data->dev;
+ int err = 0;
+ char buffer[MAX_BUFFER_SIZE];
+ u32 address;
+ u32 data;
+ u8 readback = 0;
+
+ dev_dbg(dev, "%s: ++\n", __func__);
+
+ if (copy_from_user(&buffer, buf, sizeof(buffer)))
+ goto debugfs_write_fail;
+
+ if (sscanf(buffer, "0x%x 0x%x", &address, &data) == 2)
+ goto set_attr;
+ if (sscanf(buffer, "0X%x 0X%x", &address, &data) == 2)
+ goto set_attr;
+ if (sscanf(buffer, "%d %d", &address, &data) == 2)
+ goto set_attr;
+
+ if (sscanf(buffer, "0x%x 0x%x", &address, &data) == 1)
+ goto read;
+ if (sscanf(buffer, "0X%x 0X%x", &address, &data) == 1)
+ goto read;
+ if (sscanf(buffer, "%d %d", &address, &data) == 1)
+ goto read;
+
+ dev_err(dev, "SYNTAX ERROR: %s\n", buf);
+ return -EFAULT;
+
+set_attr:
+ dev_dbg(dev,
+ "new address = %x, data = %x\n", address, data);
+ err |= call_s_ops(s_data, write_reg, address, data);
+read:
+ err |= call_s_ops(s_data, read_reg, address, &readback);
+ dev_dbg(dev,
+ "wrote to address 0x%x with value 0x%x\n",
+ address, readback);
+
+ if (err)
+ goto debugfs_write_fail;
+
+ return count;
+
+debugfs_write_fail:
+ dev_err(dev,
+ "%s: test pattern write failed\n", __func__);
+ return -EFAULT;
+}
+
+int camera_common_debugfs_open(struct inode *inode, struct file *file)
+{
+ struct camera_common_data *s_data = inode->i_private;
+ struct device *dev = s_data->dev;
+
+ dev_dbg(dev, "%s: ++\n", __func__);
+
+ return single_open(file, camera_common_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations camera_common_debugfs_fops = {
+ .open = camera_common_debugfs_open,
+ .read = seq_read,
+ .write = camera_common_debugfs_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+void camera_common_remove_debugfs(
+ struct camera_common_data *s_data)
+{
+ struct device *dev = s_data->dev;
+
+ dev_dbg(dev, "%s: ++\n", __func__);
+
+ debugfs_remove_recursive(s_data->debugdir);
+ s_data->debugdir = NULL;
+}
+EXPORT_SYMBOL_GPL(camera_common_remove_debugfs);
+
+void camera_common_create_debugfs(
+ struct camera_common_data *s_data,
+ const char *name)
+{
+ struct dentry *err;
+ struct device *dev = s_data->dev;
+
+ dev_dbg(dev, "%s %s\n", __func__, name);
+
+ s_data->debugdir =
+ debugfs_create_dir(name, NULL);
+ if (!s_data->debugdir)
+ goto remove_debugfs;
+
+ err = debugfs_create_file("d",
+ S_IWUSR | S_IRUGO,
+ s_data->debugdir, s_data,
+ &camera_common_debugfs_fops);
+ if (!err)
+ goto remove_debugfs;
+
+ return;
+remove_debugfs:
+ dev_err(dev, "couldn't create debugfs\n");
+ camera_common_remove_debugfs(s_data);
+}
+EXPORT_SYMBOL_GPL(camera_common_create_debugfs);
+
+/* Find a data format by a pixel code in an array */
+const struct camera_common_colorfmt *camera_common_find_datafmt(
+ unsigned int code)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(camera_common_color_fmts); i++)
+ if (camera_common_color_fmts[i].code == code)
+ return camera_common_color_fmts + i;
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(camera_common_find_datafmt);
+
+/* Find a data format by pixel format in an array*/
+const struct camera_common_colorfmt *camera_common_find_pixelfmt(
+ unsigned int pix_fmt)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(camera_common_color_fmts); i++)
+ if (camera_common_color_fmts[i].pix_fmt == pix_fmt)
+ return camera_common_color_fmts + i;
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(camera_common_find_pixelfmt);
+
+/* Filters for the sensor's supported colors */
+static const struct camera_common_colorfmt *find_matching_color_fmt(
+ const struct camera_common_data *s_data,
+ size_t index)
+{
+ const struct sensor_properties *sensor_props = &s_data->sensor_props;
+ const size_t num_modes = sensor_props->num_modes;
+ const size_t common_fmts_size = ARRAY_SIZE(camera_common_color_fmts);
+
+ struct sensor_image_properties *cur_props;
+ bool matched[ARRAY_SIZE(camera_common_color_fmts)];
+ int match_num = -1;
+ int match_index = -1;
+ size_t i, j;
+
+ // Clear matched array so no format has been matched
+ memset(matched, 0, sizeof(matched));
+
+ // Find and count matching color formats
+ for (i = 0; i < common_fmts_size; i++) {
+ for (j = 0; j < num_modes; j++) {
+ cur_props = &sensor_props->sensor_modes[j].
+ image_properties;
+ if (cur_props->pixel_format ==
+ camera_common_color_fmts[i].pix_fmt &&
+ !matched[i]) {
+ match_num++;
+ match_index = i;
+ // Found index
+ if (match_num == index)
+ goto break_loops;
+ }
+ }
+ }
+break_loops:
+ if (match_num < index)
+ return NULL;
+ return &camera_common_color_fmts[match_index];
+}
+
+int camera_common_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct camera_common_data *s_data = to_camera_common_data(sd->dev);
+ struct tegra_channel *chan = v4l2_get_subdev_hostdata(sd);
+ const struct camera_common_colorfmt *sensor_fmt;
+
+ sensor_fmt = find_matching_color_fmt(s_data, code->index);
+
+ if (sensor_fmt == NULL)
+ return -EINVAL;
+
+ if (!camera_common_verify_code(chan, sensor_fmt->code))
+ return -EINVAL;
+
+ code->code = sensor_fmt->code;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(camera_common_enum_mbus_code);
+
+int camera_common_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ unsigned int *code)
+{
+ struct camera_common_data *s_data = to_camera_common_data(sd->dev);
+ const struct camera_common_colorfmt *sensor_fmt;
+
+ sensor_fmt = find_matching_color_fmt(s_data, index);
+
+ if (sensor_fmt == NULL)
+ return -EINVAL;
+ *code = sensor_fmt->code;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(camera_common_enum_fmt);
+
+static void select_mode(struct camera_common_data *s_data,
+ struct v4l2_mbus_framefmt *mf,
+ unsigned int mode_type)
+{
+ int i;
+ const struct camera_common_frmfmt *frmfmt = s_data->frmfmt;
+ bool flag = 0;
+
+ for (i = 0; i < s_data->numfmts; i++) {
+ if (mode_type & HDR_ENABLE)
+ flag = !frmfmt[i].hdr_en;
+ /* Add more flags for different controls as needed */
+
+ if (flag)
+ continue;
+
+ if (mf->width == frmfmt[i].size.width &&
+ mf->height == frmfmt[i].size.height) {
+ s_data->mode = frmfmt[i].mode;
+ s_data->mode_prop_idx = i;
+ break;
+ }
+ }
+}
+
+int camera_common_try_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
+{
+ struct camera_common_data *s_data = to_camera_common_data(sd->dev);
+ struct tegra_channel *chan = v4l2_get_subdev_hostdata(sd);
+ struct v4l2_control hdr_control;
+ const struct camera_common_frmfmt *frmfmt = s_data->frmfmt;
+ unsigned int mode_type = 0;
+ int err = 0;
+ int i;
+
+ dev_dbg(sd->dev, "%s: size %i x %i\n", __func__,
+ mf->width, mf->height);
+
+ /* check hdr enable ctrl */
+ hdr_control.id = TEGRA_CAMERA_CID_HDR_EN;
+
+ /* mode_type can be filled in sensor driver */
+ if (!(v4l2_g_ctrl(s_data->ctrl_handler, &hdr_control)))
+ mode_type |=
+ switch_ctrl_qmenu[hdr_control.value] ? HDR_ENABLE : 0;
+
+ s_data->mode = s_data->def_mode;
+ s_data->mode_prop_idx = 0;
+ s_data->fmt_width = s_data->def_width;
+ s_data->fmt_height = s_data->def_height;
+
+ if (s_data->use_sensor_mode_id &&
+ s_data->sensor_mode_id >= 0 &&
+ s_data->sensor_mode_id < s_data->numfmts) {
+ dev_dbg(sd->dev, "%s: use_sensor_mode_id %d\n",
+ __func__, s_data->sensor_mode_id);
+ s_data->mode = frmfmt[s_data->sensor_mode_id].mode;
+ s_data->mode_prop_idx = s_data->sensor_mode_id;
+ s_data->fmt_width = mf->width;
+ s_data->fmt_height = mf->height;
+ } else {
+ /* select mode based on format match first */
+ for (i = 0; i < s_data->numfmts; i++) {
+ if (mf->width == frmfmt[i].size.width &&
+ mf->height == frmfmt[i].size.height) {
+ s_data->mode = frmfmt[i].mode;
+ s_data->mode_prop_idx = i;
+ s_data->fmt_width = mf->width;
+ s_data->fmt_height = mf->height;
+ break;
+ }
+ }
+
+ if (i == s_data->numfmts) {
+ mf->width = s_data->fmt_width;
+ mf->height = s_data->fmt_height;
+ dev_dbg(sd->dev,
+ "%s: invalid resolution supplied to set mode %d %d\n",
+ __func__, mf->width, mf->height);
+ goto verify_code;
+ }
+ /* update mode based on special mode types */
+ if (mode_type)
+ select_mode(s_data, mf, mode_type);
+ }
+
+ if (!camera_common_verify_code(chan, mf->code))
+ err = -EINVAL;
+
+verify_code:
+ mf->field = V4L2_FIELD_NONE;
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
+ mf->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+ mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ mf->quantization = V4L2_QUANTIZATION_DEFAULT;
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(camera_common_try_fmt);
+
+int camera_common_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
+{
+ struct camera_common_data *s_data = to_camera_common_data(sd->dev);
+ int ret;
+
+ dev_dbg(sd->dev, "%s(%u) size %i x %i\n", __func__,
+ mf->code, mf->width, mf->height);
+
+ /* MIPI CSI could have changed the format, double-check */
+ if (!camera_common_find_datafmt(mf->code))
+ return -EINVAL;
+
+ ret = camera_common_try_fmt(sd, mf);
+
+ s_data->colorfmt = camera_common_find_datafmt(mf->code);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(camera_common_s_fmt);
+
+int camera_common_g_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
+{
+ struct camera_common_data *s_data = to_camera_common_data(sd->dev);
+ const struct camera_common_colorfmt *fmt = s_data->colorfmt;
+
+ dev_dbg(sd->dev, "%s++\n", __func__);
+
+ mf->code = fmt->code;
+ mf->colorspace = fmt->colorspace;
+ mf->width = s_data->fmt_width;
+ mf->height = s_data->fmt_height;
+ mf->field = V4L2_FIELD_NONE;
+ mf->xfer_func = fmt->xfer_func;
+ mf->ycbcr_enc = fmt->ycbcr_enc;
+ mf->quantization = fmt->quantization;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(camera_common_g_fmt);
+
+static int camera_common_evaluate_color_format(struct v4l2_subdev *sd,
+ int code)
+{
+ struct camera_common_data *s_data = to_camera_common_data(sd->dev);
+ const size_t common_fmts_size = ARRAY_SIZE(camera_common_color_fmts);
+ struct sensor_image_properties *cur_props;
+ struct sensor_properties *sensor_props;
+ size_t sensor_num_modes;
+ int i, pixelformat;
+
+ if (!s_data)
+ return -EINVAL;
+
+ sensor_props = &s_data->sensor_props;
+ sensor_num_modes = sensor_props->num_modes;
+
+ for (i = 0; i < common_fmts_size; i++) {
+ if (camera_common_color_fmts[i].code == code)
+ break;
+ }
+
+ if (i == common_fmts_size) {
+ dev_dbg(s_data->dev,
+ "%s: unsupported color format(%08x) for vi\n"
+ , __func__, code);
+ return -EINVAL;
+ }
+
+ pixelformat = camera_common_color_fmts[i].pix_fmt;
+
+ for (i = 0; i < sensor_num_modes; i++) {
+ cur_props = &sensor_props->sensor_modes[i].image_properties;
+ if (cur_props->pixel_format == pixelformat)
+ return 0;
+ }
+
+ if (i == sensor_num_modes) {
+ dev_dbg(s_data->dev,
+ "%s: unsupported color format(%08x) for sensor\n"
+ , __func__, code);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int camera_common_enum_framesizes(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct camera_common_data *s_data = to_camera_common_data(sd->dev);
+ int ret;
+
+ if (!s_data || !s_data->frmfmt)
+ return -EINVAL;
+
+ if (fse->index >= s_data->numfmts)
+ return -EINVAL;
+ fse->index = array_index_nospec(fse->index, s_data->numfmts);
+
+ ret = camera_common_evaluate_color_format(sd, fse->code);
+ if (ret)
+ return ret;
+
+ fse->min_width = fse->max_width =
+ s_data->frmfmt[fse->index].size.width;
+ fse->min_height = fse->max_height =
+ s_data->frmfmt[fse->index].size.height;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(camera_common_enum_framesizes);
+
+int camera_common_enum_frameintervals(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_interval_enum *fie)
+{
+ struct camera_common_data *s_data = to_camera_common_data(sd->dev);
+ int i, ret;
+
+ if (!s_data || !s_data->frmfmt)
+ return -EINVAL;
+
+ /* Check color format */
+ ret = camera_common_evaluate_color_format(sd, fie->code);
+ if (ret)
+ return ret;
+
+ /* Check resolution sizes */
+ for (i = 0; i < s_data->numfmts; i++) {
+ if (s_data->frmfmt[i].size.width == fie->width &&
+ s_data->frmfmt[i].size.height == fie->height)
+ break;
+ }
+ if (i >= s_data->numfmts)
+ return -EINVAL;
+
+ /* Check index is in the rage of framerates array index */
+ if (fie->index >= s_data->frmfmt[i].num_framerates)
+ return -EINVAL;
+ fie->index = array_index_nospec(fie->index,
+ s_data->frmfmt[i].num_framerates);
+
+ fie->interval.numerator = 1;
+ fie->interval.denominator =
+ s_data->frmfmt[i].framerates[fie->index];
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(camera_common_enum_frameintervals);
+
+void camera_common_mclk_disable(struct camera_common_data *s_data)
+{
+ struct camera_common_power_rail *pw = s_data->power;
+
+ if (!pw) {
+ dev_err(s_data->dev, "%s: no device power rail\n",
+ __func__);
+ return;
+ }
+
+ dev_dbg(s_data->dev, "%s: disable MCLK\n", __func__);
+ clk_disable_unprepare(pw->mclk);
+}
+EXPORT_SYMBOL_GPL(camera_common_mclk_disable);
+
+int camera_common_mclk_enable(struct camera_common_data *s_data)
+{
+ int err;
+ struct camera_common_power_rail *pw = s_data->power;
+ unsigned long mclk_init_rate = s_data->def_clk_freq;
+
+ if (!pw) {
+ dev_err(s_data->dev, "%s: no device power rail\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ dev_dbg(s_data->dev, "%s: enable MCLK with %lu Hz\n",
+ __func__, mclk_init_rate);
+
+ err = clk_set_rate(pw->mclk, mclk_init_rate);
+ if (!err)
+ err = clk_prepare_enable(pw->mclk);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(camera_common_mclk_enable);
+
+void camera_common_dpd_disable(struct camera_common_data *s_data)
+{
+ int i;
+ int io_idx;
+ /* 2 lanes per port, divide by two to get numports */
+ int numports = (s_data->numlanes + 1) >> 1;
+
+ /* disable CSI IOs DPD mode to turn on camera */
+ for (i = 0; i < numports; i++) {
+ io_idx = s_data->csi_port + i;
+ if (atomic_inc_return(
+ &camera_common_csi_io_pads[io_idx].ref) == 1)
+ tegra_pmc_io_pad_low_power_disable(
+ camera_common_csi_io_pads[io_idx].name);
+ dev_dbg(s_data->dev,
+ "%s: csi %d\n", __func__, io_idx);
+ }
+}
+
+void camera_common_dpd_enable(struct camera_common_data *s_data)
+{
+ int i;
+ int io_idx;
+ /* 2 lanes per port, divide by two to get numports */
+ int numports = (s_data->numlanes + 1) >> 1;
+
+ /* disable CSI IOs DPD mode to turn on camera */
+ for (i = 0; i < numports; i++) {
+ io_idx = s_data->csi_port + i;
+ if (atomic_dec_return(
+ &camera_common_csi_io_pads[io_idx].ref) == 0)
+ tegra_pmc_io_pad_low_power_enable(
+ camera_common_csi_io_pads[io_idx].name);
+ dev_dbg(s_data->dev,
+ "%s: csi %d\n", __func__, io_idx);
+ }
+}
+
+int camera_common_s_power(struct v4l2_subdev *sd, int on)
+{
+ int err = 0;
+ struct camera_common_data *s_data = to_camera_common_data(sd->dev);
+
+ trace_camera_common_s_power("status", on);
+ if (on) {
+ if (tegra_platform_is_silicon()) {
+ err = camera_common_mclk_enable(s_data);
+ if (err)
+ return err;
+
+ camera_common_dpd_disable(s_data);
+ }
+ err = call_s_op(s_data, power_on);
+ if (err) {
+ dev_err(s_data->dev,
+ "%s: error power on\n", __func__);
+ if (tegra_platform_is_silicon()) {
+ camera_common_dpd_enable(s_data);
+ camera_common_mclk_disable(s_data);
+ }
+ }
+ } else {
+ call_s_op(s_data, power_off);
+ if (tegra_platform_is_silicon()) {
+ camera_common_dpd_enable(s_data);
+ camera_common_mclk_disable(s_data);
+ }
+ }
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(camera_common_s_power);
+
+int camera_common_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ cfg->type = V4L2_MBUS_CSI2;
+ cfg->flags = V4L2_MBUS_CSI2_4_LANE |
+ V4L2_MBUS_CSI2_CHANNEL_0 |
+ V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(camera_common_g_mbus_config);
+
+int camera_common_get_framesync(struct v4l2_subdev *sd,
+ struct camera_common_framesync *fs)
+{
+ struct camera_common_data *s_data = to_camera_common_data(sd->dev);
+ int err = -ENOTSUPP;
+
+ if (has_s_op(s_data, get_framesync))
+ err = call_s_ops(s_data, get_framesync, fs);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(camera_common_get_framesync);
+
+int camera_common_focuser_s_power(struct v4l2_subdev *sd, int on)
+{
+ int err = 0;
+ struct camera_common_focuser_data *s_data =
+ to_camera_common_focuser_data(sd->dev);
+
+ if (on) {
+ err = call_s_op(s_data, power_on);
+ if (err)
+ dev_err(s_data->dev,
+ "%s: error power on\n", __func__);
+ } else
+ err = call_s_op(s_data, power_off);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(camera_common_focuser_s_power);
+
+int camera_common_initialize(struct camera_common_data *s_data,
+ const char *dev_name)
+{
+ int err = 0;
+ char debugfs_name[50];
+
+ if (s_data->dev == NULL)
+ return -EINVAL;
+
+ err = camera_common_parse_ports(s_data->dev, s_data);
+ if (err) {
+ dev_err(s_data->dev, "Failed to find port info.\n");
+ return err;
+ }
+
+ err = camera_common_parse_general_properties(s_data->dev, s_data);
+ if (err) {
+ dev_err(s_data->dev, "Failed to find general properties.\n");
+ return err;
+ }
+
+ err = sensor_common_init_sensor_properties(s_data->dev,
+ s_data->dev->of_node,
+ &s_data->sensor_props);
+ if (err) {
+ dev_err(s_data->dev,
+ "Could not initialize sensor properties.\n");
+ return err;
+ }
+
+ sprintf(debugfs_name, "%s_%c", dev_name, s_data->csi_port + 'a');
+ dev_dbg(s_data->dev, "%s_probe: name %s\n", dev_name, debugfs_name);
+
+ camera_common_create_debugfs(s_data, debugfs_name);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(camera_common_initialize);
+
+void camera_common_cleanup(struct camera_common_data *s_data)
+{
+ camera_common_remove_debugfs(s_data);
+}
+EXPORT_SYMBOL_GPL(camera_common_cleanup);
+
+int camera_common_focuser_init(struct camera_common_focuser_data *s_data)
+{
+ int err = 0;
+
+ /* power on */
+ err = call_s_op(s_data, power_on);
+ if (err) {
+ dev_err(s_data->dev,
+ "%s: error power on\n", __func__);
+ return err;
+ }
+
+ /* load default configuration */
+ err = call_s_op(s_data, load_config);
+ if (err) {
+ dev_err(s_data->dev,
+ "%s: error loading config\n", __func__);
+ goto fail;
+ }
+
+ /* set controls */
+ err = call_s_op(s_data, ctrls_init);
+ if (err)
+ dev_err(s_data->dev,
+ "%s: error initializing controls\n", __func__);
+
+fail:
+ /* power off */
+ err |= call_s_op(s_data, power_off);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(camera_common_focuser_init);
+
+/*
+ * Regmap / RTCPU I2C driver interface
+ */
+
+int camera_common_i2c_init(
+ struct camera_common_i2c *sensor,
+ struct i2c_client *client,
+ struct regmap_config *regmap_config,
+ const struct tegra_i2c_rtcpu_config *rtcpu_config)
+{
+ sensor->regmap = devm_regmap_init_i2c(client, regmap_config);
+ if (IS_ERR(sensor->regmap)) {
+ dev_err(&client->dev,
+ "regmap init failed: %ld\n", PTR_ERR(sensor->regmap));
+ return -ENODEV;
+ }
+
+ sensor->rt_sensor = tegra_i2c_rtcpu_register_sensor(
+ client, rtcpu_config);
+
+ return 0;
+}
+EXPORT_SYMBOL(camera_common_i2c_init);
+
+int camera_common_i2c_aggregate(
+ struct camera_common_i2c *sensor,
+ bool start)
+{
+ if (sensor->rt_sensor)
+ return tegra_i2c_rtcpu_aggregate(sensor->rt_sensor, start);
+
+ return 0;
+}
+EXPORT_SYMBOL(camera_common_i2c_aggregate);
+
+int camera_common_i2c_set_frame_id(
+ struct camera_common_i2c *sensor,
+ int frame_id)
+{
+ if (sensor->rt_sensor)
+ return tegra_i2c_rtcpu_set_frame_id(
+ sensor->rt_sensor, frame_id);
+
+ return 0;
+}
+EXPORT_SYMBOL(camera_common_i2c_set_frame_id);
+
+int camera_common_i2c_read_reg8(
+ struct camera_common_i2c *sensor,
+ unsigned int addr,
+ u8 *data,
+ unsigned int count)
+{
+ if (sensor->rt_sensor)
+ return tegra_i2c_rtcpu_read_reg8(sensor->rt_sensor,
+ addr, data, count);
+ else
+ return regmap_bulk_read(sensor->regmap, addr, data, count);
+}
+EXPORT_SYMBOL(camera_common_i2c_read_reg8);
+
+int camera_common_i2c_write_reg8(
+ struct camera_common_i2c *sensor,
+ unsigned int addr,
+ const u8 *data,
+ unsigned int count)
+{
+ if (sensor->rt_sensor)
+ return tegra_i2c_rtcpu_write_reg8(sensor->rt_sensor,
+ addr, data, count);
+ else
+ return regmap_bulk_write(sensor->regmap, addr, data, count);
+}
+EXPORT_SYMBOL(camera_common_i2c_write_reg8);
+
+int camera_common_i2c_write_table_8(
+ struct camera_common_i2c *sensor,
+ const struct reg_8 table[],
+ const struct reg_8 override_list[],
+ int num_override_regs, u16 wait_ms_addr, u16 end_addr)
+{
+ if (sensor->rt_sensor)
+ return tegra_i2c_rtcpu_write_table_8(sensor->rt_sensor,
+ table, override_list, num_override_regs,
+ wait_ms_addr, end_addr);
+ else
+ return regmap_util_write_table_8(sensor->regmap,
+ table, override_list, num_override_regs,
+ wait_ms_addr, end_addr);
+}
+EXPORT_SYMBOL(camera_common_i2c_write_table_8);
diff --git a/nvidia/drivers/media/platform/tegra/camera/tegracam_ctrls.c b/nvidia/drivers/media/platform/tegra/camera/tegracam_ctrls.c
new file mode 100644
index 0000000..d1c7dce
--- /dev/null
+++ b/nvidia/drivers/media/platform/tegra/camera/tegracam_ctrls.c
@@ -0,0 +1,786 @@
+/*
+ * tegracam_ctrls - control framework for tegra camera drivers
+ *
+ * Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include
+#include
+#include
+
+#define CTRL_U32_MIN 0
+#define CTRL_U32_MAX 0x7FFFFFFF
+#define CTRL_U64_MIN 0
+#define CTRL_U64_MAX 0x7FFFFFFFFFFFFFFFLL
+#define CTRL_S32_MIN 0x80000000
+#define CTRL_S32_MAX 0x7FFFFFFF
+#define CTRL_S64_MIN 0x8000000000000000LL
+#define CTRL_S64_MAX 0x7FFFFFFFFFFFFFFFLL
+#define CTRL_MAX_STR_SIZE 4096
+
+#define TEGRACAM_DEF_CTRLS 1
+
+/* MT9M021 Controls Information */
+#define MT9M021_DEFAULT_ANALOG_GAIN (0x0)
+#define MT9M021_MAX_ANALOG_GAIN (0x3)
+
+static const char * const mt9m021_test_pattern_menu[] = {
+ "0:Disabled",
+ "1:Solid color test pattern",
+ "2:color bar test pattern",
+ "3:Fade to gray color bar test pattern",
+ "256:Walking 1s test pattern (12 bit)"
+};
+
+static int tegracam_s_ctrl(struct v4l2_ctrl *ctrl);
+static const struct v4l2_ctrl_ops tegracam_ctrl_ops = {
+ .s_ctrl = tegracam_s_ctrl,
+};
+
+static const u32 tegracam_def_cids[] = {
+ TEGRA_CAMERA_CID_GROUP_HOLD,
+};
+
+/*
+ * For auto control, the states of the previous controls must
+ * be applied to get optimal quality faster. List all the controls
+ * which must be overriden
+ */
+static const u32 tegracam_override_cids[] = {
+ TEGRA_CAMERA_CID_GAIN,
+ TEGRA_CAMERA_CID_EXPOSURE,
+ TEGRA_CAMERA_CID_FRAME_RATE,
+};
+#define NUM_OVERRIDE_CTRLS ARRAY_SIZE(tegracam_override_cids)
+
+static struct v4l2_ctrl_config ctrl_cfg_list[] = {
+/* Do not change the name field for the controls! */
+ {
+ .ops = &tegracam_ctrl_ops,
+ .id = TEGRA_CAMERA_CID_GAIN,
+ .name = "Gain",
+ .type = V4L2_CTRL_TYPE_INTEGER64,
+ .flags = V4L2_CTRL_FLAG_SLIDER,
+ .min = CTRL_U64_MIN,
+ .max = CTRL_U64_MAX,
+ .def = CTRL_U64_MIN,
+ .step = 1,
+ },
+ {
+ .ops = &tegracam_ctrl_ops,
+ .id = TEGRA_CAMERA_CID_EXPOSURE,
+ .name = "Exposure",
+ .type = V4L2_CTRL_TYPE_INTEGER64,
+ .flags = V4L2_CTRL_FLAG_SLIDER,
+ .min = CTRL_U64_MIN,
+ .max = CTRL_U64_MAX,
+ .def = CTRL_U64_MIN,
+ .step = 1,
+ },
+ {
+ .ops = &tegracam_ctrl_ops,
+ .id = TEGRA_CAMERA_CID_EXPOSURE_SHORT,
+ .name = "Exposure Short",
+ .type = V4L2_CTRL_TYPE_INTEGER64,
+ .flags = V4L2_CTRL_FLAG_SLIDER,
+ .min = CTRL_U64_MIN,
+ .max = CTRL_U64_MAX,
+ .def = CTRL_U64_MIN,
+ .step = 1,
+ },
+ {
+ .ops = &tegracam_ctrl_ops,
+ .id = TEGRA_CAMERA_CID_FRAME_RATE,
+ .name = "Frame Rate",
+ .type = V4L2_CTRL_TYPE_INTEGER64,
+ .flags = V4L2_CTRL_FLAG_SLIDER,
+ .min = CTRL_U64_MIN,
+ .max = CTRL_U64_MAX,
+ .def = CTRL_U64_MIN,
+ .step = 1,
+ },
+ {
+ .ops = &tegracam_ctrl_ops,
+ .id = TEGRA_CAMERA_CID_GROUP_HOLD,
+ .name = "Group Hold",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .flags = V4L2_CTRL_FLAG_EXECUTE_ON_WRITE,
+ .min = 0,
+ .max = 1,
+ .def = 0,
+ .step = 1,
+ },
+ {
+ .ops = &tegracam_ctrl_ops,
+ .id = TEGRA_CAMERA_CID_EEPROM_DATA,
+ .name = "EEPROM Data",
+ .type = V4L2_CTRL_TYPE_STRING,
+ .flags = V4L2_CTRL_FLAG_READ_ONLY,
+ .min = 0,
+ .max = CTRL_MAX_STR_SIZE,
+ .step = 2,
+ },
+ {
+ .ops = &tegracam_ctrl_ops,
+ .id = TEGRA_CAMERA_CID_FUSE_ID,
+ .name = "Fuse ID",
+ .type = V4L2_CTRL_TYPE_STRING,
+ .flags = V4L2_CTRL_FLAG_READ_ONLY,
+ .min = 0,
+ .max = CTRL_MAX_STR_SIZE,
+ .step = 2,
+ },
+ {
+ .ops = &tegracam_ctrl_ops,
+ .id = TEGRA_CAMERA_CID_SENSOR_MODE_ID,
+ .name = "Sensor Mode",
+ .type = V4L2_CTRL_TYPE_INTEGER64,
+ .flags = V4L2_CTRL_FLAG_SLIDER,
+ .min = CTRL_U32_MIN,
+ .max = CTRL_U32_MAX,
+ .def = CTRL_U32_MIN,
+ .step = 1,
+ },
+ {
+ .ops = &tegracam_ctrl_ops,
+ .id = TEGRA_CAMERA_CID_HDR_EN,
+ .name = "HDR enable",
+ .type = V4L2_CTRL_TYPE_INTEGER_MENU,
+ .min = 0,
+ .max = ARRAY_SIZE(switch_ctrl_qmenu) - 1,
+ .menu_skip_mask = 0,
+ .def = 0,
+ .qmenu_int = switch_ctrl_qmenu,
+ },
+ {
+ .ops = &tegracam_ctrl_ops,
+ .id = TEGRA_CAMERA_CID_OTP_DATA,
+ .name = "OTP Data",
+ .type = V4L2_CTRL_TYPE_STRING,
+ .flags = V4L2_CTRL_FLAG_READ_ONLY,
+ .min = 0,
+ .max = CTRL_MAX_STR_SIZE,
+ .step = 2,
+ },
+
+ /* Controls extension for MT9M021 */
+ {
+ .ops = &tegracam_ctrl_ops,
+ .id = V4L2_CID_ANALOGUE_GAIN,
+ .name = "Analog Gain",
+ .type = V4L2_CTRL_TYPE_INTEGER64,
+ .flags = V4L2_CTRL_FLAG_SLIDER,
+ .min = CTRL_U64_MIN,
+ .max = MT9M021_MAX_ANALOG_GAIN,
+ .def = MT9M021_DEFAULT_ANALOG_GAIN,
+ .step = 1,
+ },
+ {
+ .ops = &tegracam_ctrl_ops,
+ .id = V4L2_CID_GAIN_RED,
+ .name = "Red Gain",
+ .type = V4L2_CTRL_TYPE_INTEGER64,
+ .flags = V4L2_CTRL_FLAG_SLIDER,
+ .min = CTRL_U64_MIN,
+ .max = CTRL_U64_MAX,
+ .def = CTRL_U64_MIN,
+ .step = 1,
+ },
+ {
+ .ops = &tegracam_ctrl_ops,
+ .id = V4L2_CID_GAIN_GREENR,
+ .name = "GreenR Gain",
+ .type = V4L2_CTRL_TYPE_INTEGER64,
+ .flags = V4L2_CTRL_FLAG_SLIDER,
+ .min = CTRL_U64_MIN,
+ .max = CTRL_U64_MAX,
+ .def = CTRL_U64_MIN,
+ .step = 1,
+ },
+ {
+ .ops = &tegracam_ctrl_ops,
+ .id = V4L2_CID_GAIN_GREENB,
+ .name = "GreenB Gain",
+ .type = V4L2_CTRL_TYPE_INTEGER64,
+ .flags = V4L2_CTRL_FLAG_SLIDER,
+ .min = CTRL_U64_MIN,
+ .max = CTRL_U64_MAX,
+ .def = CTRL_U64_MIN,
+ .step = 1,
+ },
+ {
+ .ops = &tegracam_ctrl_ops,
+ .id = V4L2_CID_GAIN_BLUE,
+ .name = "Blue Gain",
+ .type = V4L2_CTRL_TYPE_INTEGER64,
+ .flags = V4L2_CTRL_FLAG_SLIDER,
+ .min = CTRL_U64_MIN,
+ .max = CTRL_U64_MAX,
+ .def = CTRL_U64_MIN,
+ .step = 1,
+ },
+ {
+ .ops = &tegracam_ctrl_ops,
+ .id = V4L2_CID_TEST_PATTERN,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Test Pattern",
+ .min = 0,
+ .max = ARRAY_SIZE(mt9m021_test_pattern_menu) - 1,
+ .step = 0,
+ .def = 0,
+ .flags = 0,
+ .menu_skip_mask = 0,
+ .qmenu = mt9m021_test_pattern_menu,
+ },
+ {
+ .ops = &tegracam_ctrl_ops,
+ .id = V4L2_CID_FLASH_LED_MODE,
+ .name = "Flash",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .flags = 0,
+ .min = V4L2_FLASH_LED_MODE_NONE,
+ .max = V4L2_FLASH_LED_MODE_FLASH,
+ .def = V4L2_FLASH_LED_MODE_FLASH,
+ .step = 1,
+ },
+ {
+ .ops = &tegracam_ctrl_ops,
+ .id = V4L2_CID_HFLIP,
+ .name = "Horizontal Flip",
+ .type = V4L2_CTRL_TYPE_INTEGER_MENU,
+ .min = 0,
+ .max = ARRAY_SIZE(switch_ctrl_qmenu) - 1,
+ .menu_skip_mask = 0,
+ .def = 0,
+ .qmenu_int = switch_ctrl_qmenu,
+ },
+ {
+ .ops = &tegracam_ctrl_ops,
+ .id = V4L2_CID_VFLIP,
+ .name = "Vertical Flip",
+ .type = V4L2_CTRL_TYPE_INTEGER_MENU,
+ .min = 0,
+ .max = ARRAY_SIZE(switch_ctrl_qmenu) - 1,
+ .menu_skip_mask = 0,
+ .def = 0,
+ .qmenu_int = switch_ctrl_qmenu,
+ },
+};
+
+static int tegracam_get_ctrl_index(u32 cid)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ctrl_cfg_list); i++) {
+ if (ctrl_cfg_list[i].id == cid)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int tegracam_get_string_ctrl_size(u32 cid,
+ const struct tegracam_ctrl_ops *ops)
+{
+ u32 index = 0;
+
+ switch (cid) {
+ case TEGRA_CAMERA_CID_EEPROM_DATA:
+ index = TEGRA_CAM_STRING_CTRL_EEPROM_INDEX;
+ break;
+ case TEGRA_CAMERA_CID_FUSE_ID:
+ index = TEGRA_CAM_STRING_CTRL_FUSEID_INDEX;
+ break;
+ case TEGRA_CAMERA_CID_OTP_DATA:
+ index = TEGRA_CAM_STRING_CTRL_OTP_INDEX;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ops->string_ctrl_size[index];
+}
+
+static int tegracam_setup_string_ctrls(struct tegracam_device *tc_dev,
+ struct tegracam_ctrl_handler *handler)
+{
+ const struct tegracam_ctrl_ops *ops = handler->ctrl_ops;
+ u32 numctrls = ops->numctrls;
+ int i;
+ int err = 0;
+
+ for (i = 0; i < numctrls; i++) {
+ struct v4l2_ctrl *ctrl = handler->ctrls[i];
+
+ if (ctrl->type == V4L2_CTRL_TYPE_STRING) {
+ err = ops->fill_string_ctrl(tc_dev, ctrl);
+ if (err)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int tegracam_set_ctrls(struct tegracam_ctrl_handler *handler,
+ struct v4l2_ctrl *ctrl)
+{
+ const struct tegracam_ctrl_ops *ops = handler->ctrl_ops;
+ struct tegracam_device *tc_dev = handler->tc_dev;
+ struct camera_common_data *s_data = tc_dev->s_data;
+ int err = 0;
+ u32 status = 0;
+
+ /* For controls that are independent of power state */
+ switch (ctrl->id) {
+ case TEGRA_CAMERA_CID_SENSOR_MODE_ID:
+ s_data->sensor_mode_id = (int) (*ctrl->p_new.p_s64);
+ return 0;
+ case TEGRA_CAMERA_CID_HDR_EN:
+ return 0;
+ }
+
+ if (v4l2_subdev_call(&s_data->subdev, video,
+ g_input_status, &status)) {
+ dev_err(s_data->dev, "power status query unsupported\n");
+ return -ENOTTY;
+ }
+
+ /* power state is turned off, do not program sensor now */
+ if (!status)
+ return 0;
+
+ /* For controls that require sensor to be on */
+ switch (ctrl->id) {
+ case TEGRA_CAMERA_CID_GAIN:
+ err = ops->set_gain(tc_dev, *ctrl->p_new.p_s64);
+ break;
+ case TEGRA_CAMERA_CID_FRAME_RATE:
+ err = ops->set_frame_rate(tc_dev, *ctrl->p_new.p_s64);
+ break;
+ case TEGRA_CAMERA_CID_EXPOSURE:
+ err = ops->set_exposure(tc_dev, *ctrl->p_new.p_s64);
+ break;
+ case TEGRA_CAMERA_CID_EXPOSURE_SHORT:
+ err = ops->set_exposure_short(tc_dev, *ctrl->p_new.p_s64);
+ break;
+ case TEGRA_CAMERA_CID_GROUP_HOLD:
+ err = ops->set_group_hold(tc_dev, ctrl->val);
+ break;
+ case V4L2_CID_ANALOGUE_GAIN:
+ err = ops->set_analog_gain(tc_dev, *ctrl->p_new.p_s64);
+ break;
+ case V4L2_CID_GAIN_RED:
+ case V4L2_CID_GAIN_GREENR:
+ case V4L2_CID_GAIN_GREENB:
+ case V4L2_CID_GAIN_BLUE:
+ err = ops->set_digital_gain(tc_dev, *ctrl->p_new.p_s64,
+ ctrl->id);
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ err = ops->set_test_pattern(tc_dev, ctrl->val);
+ break;
+ case V4L2_CID_FLASH_LED_MODE:
+ err = ops->set_flash(tc_dev, ctrl->val);
+ break;
+ case V4L2_CID_HFLIP:
+ case V4L2_CID_VFLIP:
+ err = ops->set_flip(tc_dev, ctrl->val, ctrl->id);
+ break;
+ default:
+ pr_err("%s: unknown ctrl id.\n", __func__);
+ return -EINVAL;
+ }
+
+ return err;
+}
+
+static int tegracam_set_grouphold_ex(struct tegracam_device *tc_dev,
+ struct sensor_blob *blob,
+ bool status)
+{
+ const struct tegracam_ctrl_ops *ops = tc_dev->tcctrl_ops;
+ struct camera_common_data *s_data = tc_dev->s_data;
+ int err = 0;
+
+ /*
+ * when grouphold is set, reset control blob
+ * set grouphold register using set API
+ * start packetize commands for delivering the blob
+ * when grouphold is unset, unset grouphold register
+ * and write the blob only if sensor is streaming.
+ */
+ if (status) {
+ memset(blob, 0, sizeof(struct sensor_blob));
+ err = ops->set_group_hold_ex(tc_dev, blob, status);
+ if (err)
+ return err;
+ } else {
+ err = ops->set_group_hold_ex(tc_dev, blob, status);
+ if (err)
+ return err;
+
+ /* TODO: block this write selectively from VI5 */
+ if (tc_dev->is_streaming) {
+ err = write_sensor_blob(s_data->regmap, blob);
+ if (err)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int tegracam_set_ctrls_ex(struct tegracam_ctrl_handler *handler,
+ struct v4l2_ctrl *ctrl)
+{
+ const struct tegracam_ctrl_ops *ops = handler->ctrl_ops;
+ struct tegracam_device *tc_dev = handler->tc_dev;
+ struct camera_common_data *s_data = tc_dev->s_data;
+ struct tegracam_sensor_data *sensor_data = &handler->sensor_data;
+ struct sensor_blob *blob = &sensor_data->ctrls_blob;
+ int err = 0;
+
+ switch (ctrl->id) {
+ case TEGRA_CAMERA_CID_GAIN:
+ err = ops->set_gain_ex(tc_dev, blob, *ctrl->p_new.p_s64);
+ break;
+ case TEGRA_CAMERA_CID_FRAME_RATE:
+ err = ops->set_frame_rate_ex(tc_dev, blob, *ctrl->p_new.p_s64);
+ break;
+ case TEGRA_CAMERA_CID_EXPOSURE:
+ err = ops->set_exposure_ex(tc_dev, blob, *ctrl->p_new.p_s64);
+ break;
+ case TEGRA_CAMERA_CID_GROUP_HOLD:
+ err = tegracam_set_grouphold_ex(tc_dev, blob, ctrl->val);
+ break;
+ case TEGRA_CAMERA_CID_SENSOR_MODE_ID:
+ s_data->sensor_mode_id = (int) (*ctrl->p_new.p_s64);
+ break;
+ case TEGRA_CAMERA_CID_HDR_EN:
+ break;
+ default:
+ pr_err("%s: unknown ctrl id.\n", __func__);
+ return -EINVAL;
+ }
+
+ return err;
+}
+
+
+static int tegracam_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct tegracam_ctrl_handler *handler =
+ container_of(ctrl->handler,
+ struct tegracam_ctrl_handler, ctrl_handler);
+ const struct tegracam_ctrl_ops *ops = handler->ctrl_ops;
+
+ if (ops->is_blob_supported)
+ return tegracam_set_ctrls_ex(handler, ctrl);
+ else
+ return tegracam_set_ctrls(handler, ctrl);
+
+ return 0;
+}
+
+int tegracam_ctrl_set_overrides(struct tegracam_ctrl_handler *hdl)
+{
+ struct v4l2_ext_controls ctrls;
+ struct v4l2_ext_control control;
+ struct tegracam_device *tc_dev = hdl->tc_dev;
+ struct device *dev = tc_dev->dev;
+ const struct tegracam_ctrl_ops *ops = hdl->ctrl_ops;
+ struct tegracam_sensor_data *sensor_data = &hdl->sensor_data;
+ struct sensor_blob *blob = &sensor_data->ctrls_blob;
+ bool is_blob_supported = ops->is_blob_supported;
+ int err, result = 0;
+ int i;
+
+ /*
+ * write list of override regs for the asking frame length,
+ * coarse integration time, and gain. Failures to write
+ * overrides are non-fatal
+ */
+ memset(&ctrls, 0, sizeof(ctrls));
+#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 9, 0)
+ ctrls.which = V4L2_CTRL_ID2WHICH(TEGRA_CAMERA_CID_BASE);
+#else
+ ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(TEGRA_CAMERA_CID_BASE);
+#endif
+ ctrls.count = 1;
+ ctrls.controls = &control;
+
+ for (i = 0; i < NUM_OVERRIDE_CTRLS; i++) {
+ s64 val = 0;
+
+ control.id = tegracam_override_cids[i];
+ result = v4l2_g_ext_ctrls(&hdl->ctrl_handler, &ctrls);
+ if (result == 0) {
+ val = control.value64;
+ switch (control.id) {
+ case TEGRA_CAMERA_CID_GAIN:
+ if (is_blob_supported)
+ err = ops->set_gain_ex(tc_dev,
+ blob, val);
+ else
+ err = ops->set_gain(tc_dev, val);
+ break;
+ case TEGRA_CAMERA_CID_EXPOSURE:
+ if (is_blob_supported)
+ err = ops->set_exposure_ex(tc_dev,
+ blob, val);
+ else
+ err = ops->set_exposure(tc_dev, val);
+ break;
+ case TEGRA_CAMERA_CID_FRAME_RATE:
+ if (is_blob_supported)
+ err = ops->set_frame_rate_ex(tc_dev,
+ blob, val);
+ else
+ err = ops->set_frame_rate(tc_dev, val);
+ break;
+ default:
+ dev_err(dev, "%s: unsupported override %x\n",
+ __func__, control.id);
+ return -EINVAL;
+ }
+
+ if (err) {
+ dev_err(dev, "%s: error to set %d override\n",
+ __func__, control.id);
+ return err;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int tegracam_init_ctrl_ranges_by_mode(
+ struct tegracam_ctrl_handler *handler,
+ u32 modeidx)
+{
+ struct tegracam_device *tc_dev = handler->tc_dev;
+ struct camera_common_data *s_data = tc_dev->s_data;
+ struct sensor_control_properties *ctrlprops = NULL;
+ s64 min_short_exp_time = 0;
+ s64 max_short_exp_time = 0;
+ s64 default_short_exp_time = 0;
+ int i;
+
+ if (modeidx >= s_data->sensor_props.num_modes)
+ return -EINVAL;
+
+ ctrlprops =
+ &s_data->sensor_props.sensor_modes[modeidx].control_properties;
+
+ for (i = 0; i < handler->numctrls; i++) {
+ struct v4l2_ctrl *ctrl = handler->ctrls[i];
+ int err = 0;
+
+ switch (ctrl->id) {
+ case TEGRA_CAMERA_CID_GAIN:
+ case V4L2_CID_GAIN_RED:
+ case V4L2_CID_GAIN_GREENR:
+ case V4L2_CID_GAIN_GREENB:
+ case V4L2_CID_GAIN_BLUE:
+ err = v4l2_ctrl_modify_range(ctrl,
+ ctrlprops->min_gain_val,
+ ctrlprops->max_gain_val,
+ ctrlprops->step_gain_val,
+ ctrlprops->default_gain);
+ break;
+ case TEGRA_CAMERA_CID_FRAME_RATE:
+ err = v4l2_ctrl_modify_range(ctrl,
+ ctrlprops->min_framerate,
+ ctrlprops->max_framerate,
+ ctrlprops->step_framerate,
+ ctrlprops->default_framerate);
+ break;
+ case TEGRA_CAMERA_CID_EXPOSURE:
+ err = v4l2_ctrl_modify_range(ctrl,
+ ctrlprops->min_exp_time.val,
+ ctrlprops->max_exp_time.val,
+ ctrlprops->step_exp_time.val,
+ ctrlprops->default_exp_time.val);
+ break;
+ case TEGRA_CAMERA_CID_EXPOSURE_SHORT:
+ /*
+ * min_hdr_ratio should be equal to max_hdr_ratio.
+ * This will ensure consistent short exposure
+ * limit calculations.
+ */
+ min_short_exp_time =
+ ctrlprops->min_exp_time.val /
+ ctrlprops->min_hdr_ratio;
+ max_short_exp_time =
+ ctrlprops->max_exp_time.val /
+ ctrlprops->min_hdr_ratio;
+ default_short_exp_time =
+ ctrlprops->default_exp_time.val /
+ ctrlprops->min_hdr_ratio;
+ err = v4l2_ctrl_modify_range(ctrl,
+ min_short_exp_time,
+ max_short_exp_time,
+ ctrlprops->step_exp_time.val,
+ default_short_exp_time);
+ dev_dbg(s_data->dev,
+ "%s:short_exp_limits[%lld,%lld], default_short_exp_time=%lld\n",
+ __func__,
+ min_short_exp_time,
+ max_short_exp_time,
+ default_short_exp_time);
+ break;
+ default:
+ /* Not required to modify these control ranges */
+ break;
+ }
+
+ if (err) {
+ dev_err(s_data->dev,
+ "ctrl %s range update failed\n", ctrl->name);
+ return err;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tegracam_init_ctrl_ranges_by_mode);
+
+int tegracam_init_ctrl_ranges(struct tegracam_ctrl_handler *handler)
+{
+ struct tegracam_device *tc_dev = handler->tc_dev;
+ struct camera_common_data *s_data = tc_dev->s_data;
+ struct device *dev = tc_dev->dev;
+ int i, err = 0;
+
+ /* Updating static control ranges */
+ for (i = 0; i < handler->numctrls; i++) {
+ struct v4l2_ctrl *ctrl = handler->ctrls[i];
+
+ switch (ctrl->id) {
+ case TEGRA_CAMERA_CID_SENSOR_MODE_ID:
+ err = v4l2_ctrl_modify_range(ctrl,
+ CTRL_U32_MIN,
+ (s64) s_data->sensor_props.num_modes,
+ 1,
+ CTRL_U32_MIN);
+ break;
+ default:
+ /* Not required to modify these control ranges */
+ break;
+ }
+
+ if (err) {
+ dev_err(s_data->dev,
+ "ctrl %s range update failed\n", ctrl->name);
+ return err;
+ }
+ }
+
+ /* Use mode 0 control ranges as default */
+ if (s_data->sensor_props.num_modes > 0) {
+ err = tegracam_init_ctrl_ranges_by_mode(handler, 0);
+ if (err) {
+ dev_err(dev,
+ "Error %d updating mode specific control ranges\n",
+ err);
+ return err;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tegracam_init_ctrl_ranges);
+
+int tegracam_ctrl_handler_init(struct tegracam_ctrl_handler *handler)
+{
+ struct tegracam_device *tc_dev = handler->tc_dev;
+ struct v4l2_ctrl *ctrl;
+ struct v4l2_ctrl_config *ctrl_cfg;
+ struct device *dev = tc_dev->dev;
+ const struct tegracam_ctrl_ops *ops = handler->ctrl_ops;
+ const u32 *cids = ops->ctrl_cid_list;
+ u32 numctrls = ops->numctrls + TEGRACAM_DEF_CTRLS;
+ int i, j;
+ int err = 0;
+
+ err = v4l2_ctrl_handler_init(&handler->ctrl_handler, numctrls);
+
+ for (i = 0, j = 0; i < numctrls; i++) {
+ u32 cid = i < ops->numctrls ? cids[i] : tegracam_def_cids[j++];
+ int index = tegracam_get_ctrl_index(cid);
+ int size = 0;
+
+ if (index >= ARRAY_SIZE(ctrl_cfg_list)) {
+ dev_err(dev, "unsupported control in the list\n");
+ return -ENOTTY;
+ }
+
+ ctrl_cfg = &ctrl_cfg_list[index];
+ if (ctrl_cfg->type == V4L2_CTRL_TYPE_STRING) {
+ size = tegracam_get_string_ctrl_size(ctrl_cfg->id, ops);
+ if (size < 0) {
+ dev_err(dev, "Invalid string ctrl size\n");
+ return -EINVAL;
+ }
+ ctrl_cfg->max = size;
+ }
+ ctrl = v4l2_ctrl_new_custom(&handler->ctrl_handler,
+ ctrl_cfg, NULL);
+ if (ctrl == NULL) {
+ dev_err(dev, "Failed to init %s ctrl\n",
+ ctrl_cfg->name);
+ return -EINVAL;
+ }
+
+ if (ctrl_cfg->type == V4L2_CTRL_TYPE_STRING &&
+ ctrl_cfg->flags & V4L2_CTRL_FLAG_READ_ONLY) {
+ ctrl->p_new.p_char = devm_kzalloc(tc_dev->dev,
+ size + 1, GFP_KERNEL);
+ }
+ handler->ctrls[i] = ctrl;
+ };
+
+ handler->numctrls = numctrls;
+ err = v4l2_ctrl_handler_setup(&handler->ctrl_handler);
+ if (err) {
+ dev_err(dev, "Error %d in control hdl setup\n", err);
+ goto error;
+ }
+
+ err = handler->ctrl_handler.error;
+ if (err) {
+ dev_err(dev, "Error %d adding controls\n", err);
+ goto error;
+ }
+
+ err = tegracam_setup_string_ctrls(tc_dev, handler);
+ if (err) {
+ dev_err(dev, "setup string controls failed\n");
+ goto error;
+ }
+
+ err = tegracam_init_ctrl_ranges(handler);
+ if (err) {
+ dev_err(dev, "Error %d updating control ranges\n", err);
+ goto error;
+ }
+ return 0;
+error:
+ v4l2_ctrl_handler_free(&handler->ctrl_handler);
+ return err;
+}
+EXPORT_SYMBOL_GPL(tegracam_ctrl_handler_init);
diff --git a/nvidia/include/media/camera_common.h b/nvidia/include/media/camera_common.h
new file mode 100644
index 0000000..3d8146e
--- /dev/null
+++ b/nvidia/include/media/camera_common.h
@@ -0,0 +1,413 @@
+/**
+ * camera_common.h - utilities for tegra camera driver
+ *
+ * Copyright (c) 2015-2019, NVIDIA Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef __camera_common__
+#define __camera_common__
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/*
+ * Scaling factor for converting a Q10.22 fixed point value
+ * back to its original floating point value
+ */
+#define FIXED_POINT_SCALING_FACTOR (1ULL << 22)
+
+struct reg_8 {
+ u16 addr;
+ u8 val;
+};
+
+struct reg_16 {
+ u16 addr;
+ u16 val;
+};
+
+struct camera_common_power_rail {
+ struct regulator *dvdd;
+ struct regulator *avdd;
+ struct regulator *iovdd;
+ struct regulator *vcmvdd;
+ struct clk *mclk;
+ unsigned int pwdn_gpio;
+ unsigned int reset_gpio;
+ unsigned int af_gpio;
+ bool state;
+};
+
+struct camera_common_regulators {
+ const char *avdd;
+ const char *dvdd;
+ const char *iovdd;
+ const char *vcmvdd;
+};
+
+struct camera_common_pdata {
+ const char *mclk_name; /* NULL for default default_mclk */
+ const char *parentclk_name; /* NULL for no parent clock*/
+ unsigned int pwdn_gpio;
+ unsigned int reset_gpio;
+ unsigned int af_gpio;
+ bool ext_reg;
+ int (*power_on)(struct camera_common_power_rail *pw);
+ int (*power_off)(struct camera_common_power_rail *pw);
+ struct camera_common_regulators regulators;
+ bool use_cam_gpio;
+ bool has_eeprom;
+ bool v_flip;
+ bool h_mirror;
+ unsigned int fuse_id_addr;
+};
+
+struct camera_common_eeprom_data {
+ struct i2c_client *i2c_client;
+ struct i2c_adapter *adap;
+ struct i2c_board_info brd;
+ struct regmap *regmap;
+};
+
+int
+regmap_util_write_table_8(struct regmap *regmap,
+ const struct reg_8 table[],
+ const struct reg_8 override_list[],
+ int num_override_regs,
+ u16 wait_ms_addr, u16 end_addr);
+
+int
+regmap_util_write_table_16_as_8(struct regmap *regmap,
+ const struct reg_16 table[],
+ const struct reg_16 override_list[],
+ int num_override_regs,
+ u16 wait_ms_addr, u16 end_addr);
+
+enum switch_state {
+ SWITCH_OFF,
+ SWITCH_ON,
+};
+
+static const s64 switch_ctrl_qmenu[] = {
+ SWITCH_OFF, SWITCH_ON
+};
+
+/*
+ * The memory buffers allocated from nvrm are aligned to
+ * fullfill the hardware requirements:
+ * - size in alignment with a multiple of 128K/64K bytes,
+ * see CL http://git-master/r/256468 and bug 1321091.
+ */
+static const s64 size_align_ctrl_qmenu[] = {
+ 1, (64 * 1024), (128 * 1024),
+};
+
+struct camera_common_frmfmt {
+ struct v4l2_frmsize_discrete size;
+ const int *framerates;
+ int num_framerates;
+ bool hdr_en;
+ int mode;
+};
+
+struct camera_common_colorfmt {
+ unsigned int code;
+ enum v4l2_colorspace colorspace;
+ int pix_fmt;
+ enum v4l2_xfer_func xfer_func;
+ enum v4l2_ycbcr_encoding ycbcr_enc;
+ enum v4l2_quantization quantization;
+};
+
+struct camera_common_framesync {
+ u32 inck; /* kHz */
+ u32 xhs; /* in inck */
+ u32 xvs; /* in xhs */
+ u32 fps; /* frames in 1000 second */
+};
+
+struct tegracam_device ;
+struct camera_common_data;
+
+struct camera_common_sensor_ops {
+ u32 numfrmfmts;
+ const struct camera_common_frmfmt *frmfmt_table;
+ int (*power_on)(struct camera_common_data *s_data);
+ int (*power_off)(struct camera_common_data *s_data);
+ int (*write_reg)(struct camera_common_data *s_data,
+ u16 addr, u8 val);
+ int (*read_reg)(struct camera_common_data *s_data,
+ u16 addr, u8 *val);
+ struct camera_common_pdata *(*parse_dt)(struct tegracam_device *tc_dev);
+ int (*power_get)(struct tegracam_device *tc_dev);
+ int (*power_put)(struct tegracam_device *tc_dev);
+ int (*get_framesync)(struct camera_common_data *s_data,
+ struct camera_common_framesync *vshs);
+ int (*set_mode)(struct tegracam_device *tc_dev);
+ int (*start_streaming)(struct tegracam_device *tc_dev);
+ int (*stop_streaming)(struct tegracam_device *tc_dev);
+};
+
+struct tegracam_sensor_data {
+ struct sensor_blob mode_blob;
+ struct sensor_blob ctrls_blob;
+};
+
+struct tegracam_ctrl_ops {
+ u32 numctrls;
+ u32 string_ctrl_size[TEGRA_CAM_MAX_STRING_CONTROLS];
+ const u32 *ctrl_cid_list;
+ bool is_blob_supported;
+ int (*set_gain)(struct tegracam_device *tc_dev, s64 val);
+ int (*set_exposure)(struct tegracam_device *tc_dev, s64 val);
+ int (*set_exposure_short)(struct tegracam_device *tc_dev, s64 val);
+ int (*set_frame_rate)(struct tegracam_device *tc_dev, s64 val);
+ int (*set_group_hold)(struct tegracam_device *tc_dev, bool val);
+ int (*fill_string_ctrl)(struct tegracam_device *tc_dev,
+ struct v4l2_ctrl *ctrl);
+ int (*set_gain_ex)(struct tegracam_device *tc_dev,
+ struct sensor_blob *blob, s64 val);
+ int (*set_exposure_ex)(struct tegracam_device *tc_dev,
+ struct sensor_blob *blob, s64 val);
+ int (*set_frame_rate_ex)(struct tegracam_device *tc_dev,
+ struct sensor_blob *blob, s64 val);
+ int (*set_group_hold_ex)(struct tegracam_device *tc_dev,
+ struct sensor_blob *blob, bool val);
+ int (*set_analog_gain)(struct tegracam_device *tc_dev, s64 val);
+ int (*set_digital_gain)(struct tegracam_device *tc_dev, s64 val,
+ int id);
+ int (*set_test_pattern)(struct tegracam_device *tc_dev, s32 val);
+ int (*set_flash)(struct tegracam_device *tc_dev, s32 val);
+ int (*set_flip)(struct tegracam_device *tc_dev, s32 val, int id);
+};
+
+struct tegracam_ctrl_handler {
+ struct v4l2_ctrl_handler ctrl_handler;
+ const struct tegracam_ctrl_ops *ctrl_ops;
+ struct tegracam_device *tc_dev;
+ struct tegracam_sensor_data sensor_data;
+
+ int numctrls;
+ struct v4l2_ctrl *ctrls[MAX_CID_CONTROLS];
+};
+
+struct camera_common_data {
+ struct camera_common_sensor_ops *ops;
+ struct v4l2_ctrl_handler *ctrl_handler;
+ struct device *dev;
+ const struct camera_common_frmfmt *frmfmt;
+ const struct camera_common_colorfmt *colorfmt;
+ struct dentry *debugdir;
+ struct camera_common_power_rail *power;
+
+ struct v4l2_subdev subdev;
+ struct v4l2_ctrl **ctrls;
+
+ struct sensor_properties sensor_props;
+ /* TODO: cleanup neeeded once all the sensors adapt new framework */
+ struct tegracam_ctrl_handler *tegracam_ctrl_hdl;
+ struct regmap *regmap;
+ struct camera_common_pdata *pdata;
+ /* TODO: cleanup needed for priv once all the sensors adapt new framework */
+ void *priv;
+ int numctrls;
+ int csi_port;
+ int numlanes;
+ int mode;
+ int mode_prop_idx;
+ int numfmts;
+ int def_mode, def_width, def_height;
+ int def_clk_freq;
+ int fmt_width, fmt_height;
+ int sensor_mode_id;
+ bool use_sensor_mode_id;
+ bool override_enable;
+ u32 version;
+};
+
+struct camera_common_focuser_data;
+
+struct camera_common_focuser_ops {
+ int (*power_on)(struct camera_common_focuser_data *s_data);
+ int (*power_off)(struct camera_common_focuser_data *s_data);
+ int (*load_config)(struct camera_common_focuser_data *s_data);
+ int (*ctrls_init)(struct camera_common_focuser_data *s_data);
+};
+
+struct camera_common_focuser_data {
+ struct camera_common_focuser_ops *ops;
+ struct v4l2_ctrl_handler *ctrl_handler;
+ struct v4l2_subdev subdev;
+ struct v4l2_ctrl **ctrls;
+ struct device *dev;
+
+ struct nv_focuser_config config;
+ void *priv;
+ int pwr_dev;
+ int def_position;
+};
+
+static inline void msleep_range(unsigned int delay_base)
+{
+ usleep_range(delay_base * 1000, delay_base * 1000 + 500);
+}
+
+static inline struct camera_common_data *to_camera_common_data(
+ const struct device *dev)
+{
+ if (sensor_common_parse_num_modes(dev))
+ return container_of(dev_get_drvdata(dev),
+ struct camera_common_data, subdev);
+ return NULL;
+}
+
+static inline struct camera_common_focuser_data *to_camera_common_focuser_data(
+ const struct device *dev)
+{
+ return container_of(dev_get_drvdata(dev),
+ struct camera_common_focuser_data, subdev);
+}
+
+int camera_common_g_ctrl(struct camera_common_data *s_data,
+ struct v4l2_control *control);
+
+int camera_common_regulator_get(struct device *dev,
+ struct regulator **vreg, const char *vreg_name);
+int camera_common_parse_clocks(struct device *dev,
+ struct camera_common_pdata *pdata);
+int camera_common_parse_ports(struct device *dev,
+ struct camera_common_data *s_data);
+int camera_common_mclk_enable(struct camera_common_data *s_data);
+void camera_common_mclk_disable(struct camera_common_data *s_data);
+
+
+int camera_common_debugfs_show(struct seq_file *s, void *unused);
+ssize_t camera_common_debugfs_write(
+ struct file *file,
+ char const __user *buf,
+ size_t count,
+ loff_t *offset);
+int camera_common_debugfs_open(struct inode *inode, struct file *file);
+void camera_common_remove_debugfs(struct camera_common_data *s_data);
+void camera_common_create_debugfs(struct camera_common_data *s_data,
+ const char *name);
+
+const struct camera_common_colorfmt *camera_common_find_datafmt(
+ unsigned int code);
+int camera_common_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code);
+int camera_common_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ unsigned int *code);
+int camera_common_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf);
+int camera_common_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf);
+int camera_common_g_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf);
+int camera_common_enum_framesizes(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse);
+int camera_common_enum_frameintervals(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_interval_enum *fie);
+int camera_common_set_power(struct camera_common_data *data, int on);
+int camera_common_s_power(struct v4l2_subdev *sd, int on);
+void camera_common_dpd_disable(struct camera_common_data *s_data);
+void camera_common_dpd_enable(struct camera_common_data *s_data);
+int camera_common_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg);
+int camera_common_get_framesync(struct v4l2_subdev *sd,
+ struct camera_common_framesync *vshs);
+
+/* Common initialize and cleanup for camera */
+int camera_common_initialize(struct camera_common_data *s_data,
+ const char *dev_name);
+void camera_common_cleanup(struct camera_common_data *s_data);
+
+/* Focuser */
+int camera_common_focuser_init(struct camera_common_focuser_data *s_data);
+int camera_common_focuser_s_power(struct v4l2_subdev *sd, int on);
+
+const struct camera_common_colorfmt *camera_common_find_pixelfmt(
+ unsigned int pix_fmt);
+
+/* common control layer init */
+int tegracam_ctrl_set_overrides(struct tegracam_ctrl_handler *handler);
+int tegracam_ctrl_handler_init(struct tegracam_ctrl_handler *handler);
+int tegracam_init_ctrl_ranges(struct tegracam_ctrl_handler *handler);
+int tegracam_init_ctrl_ranges_by_mode(
+ struct tegracam_ctrl_handler *handler,
+ u32 modeidx);
+
+/* Regmap / RTCPU I2C driver interface */
+struct tegra_i2c_rtcpu_sensor;
+struct tegra_i2c_rtcpu_config;
+
+struct camera_common_i2c {
+ struct regmap *regmap;
+ struct tegra_i2c_rtcpu_sensor *rt_sensor;
+};
+
+int camera_common_i2c_init(
+ struct camera_common_i2c *sensor,
+ struct i2c_client *client,
+ struct regmap_config *regmap_config,
+ const struct tegra_i2c_rtcpu_config *rtcpu_config);
+
+int camera_common_i2c_aggregate(
+ struct camera_common_i2c *sensor,
+ bool start);
+
+int camera_common_i2c_set_frame_id(
+ struct camera_common_i2c *sensor,
+ int frame_id);
+
+int camera_common_i2c_read_reg8(
+ struct camera_common_i2c *sensor,
+ unsigned int addr,
+ u8 *data,
+ unsigned int count);
+
+int camera_common_i2c_write_reg8(
+ struct camera_common_i2c *sensor,
+ unsigned int addr,
+ const u8 *data,
+ unsigned int count);
+
+int camera_common_i2c_write_table_8(
+ struct camera_common_i2c *sensor,
+ const struct reg_8 table[],
+ const struct reg_8 override_list[],
+ int num_override_regs, u16 wait_ms_addr, u16 end_addr);
+
+#endif /* __camera_common__ */
diff --git a/nvidia/include/media/tegra-v4l2-camera.h b/nvidia/include/media/tegra-v4l2-camera.h
new file mode 100644
index 0000000..54f0876
--- /dev/null
+++ b/nvidia/include/media/tegra-v4l2-camera.h
@@ -0,0 +1,181 @@
+/**
+ * TEGRA_V4L2_CAMERA.h - utilities for tegra camera driver
+ *
+ * Copyright (c) 2017-2019, NVIDIA Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef __TEGRA_V4L2_CAMERA__
+#define __TEGRA_V4L2_CAMERA__
+
+#include
+
+#define TEGRA_CAMERA_CID_BASE (V4L2_CTRL_CLASS_CAMERA | 0x2000)
+
+#define TEGRA_CAMERA_CID_FRAME_LENGTH (TEGRA_CAMERA_CID_BASE+0)
+#define TEGRA_CAMERA_CID_COARSE_TIME (TEGRA_CAMERA_CID_BASE+1)
+#define TEGRA_CAMERA_CID_COARSE_TIME_SHORT (TEGRA_CAMERA_CID_BASE+2)
+#define TEGRA_CAMERA_CID_GROUP_HOLD (TEGRA_CAMERA_CID_BASE+3)
+#define TEGRA_CAMERA_CID_HDR_EN (TEGRA_CAMERA_CID_BASE+4)
+#define TEGRA_CAMERA_CID_EEPROM_DATA (TEGRA_CAMERA_CID_BASE+5)
+#define TEGRA_CAMERA_CID_OTP_DATA (TEGRA_CAMERA_CID_BASE+6)
+#define TEGRA_CAMERA_CID_FUSE_ID (TEGRA_CAMERA_CID_BASE+7)
+#define TEGRA_CAMERA_CID_SENSOR_MODE_ID (TEGRA_CAMERA_CID_BASE+8)
+
+#define TEGRA_CAMERA_CID_GAIN (TEGRA_CAMERA_CID_BASE+9)
+#define TEGRA_CAMERA_CID_EXPOSURE (TEGRA_CAMERA_CID_BASE+10)
+#define TEGRA_CAMERA_CID_FRAME_RATE (TEGRA_CAMERA_CID_BASE+11)
+#define TEGRA_CAMERA_CID_EXPOSURE_SHORT (TEGRA_CAMERA_CID_BASE+12)
+
+#define TEGRA_CAMERA_CID_SENSOR_CONFIG (TEGRA_CAMERA_CID_BASE+50)
+#define TEGRA_CAMERA_CID_SENSOR_MODE_BLOB (TEGRA_CAMERA_CID_BASE+51)
+#define TEGRA_CAMERA_CID_SENSOR_CONTROL_BLOB (TEGRA_CAMERA_CID_BASE+52)
+
+#define TEGRA_CAMERA_CID_VI_BYPASS_MODE (TEGRA_CAMERA_CID_BASE+100)
+#define TEGRA_CAMERA_CID_OVERRIDE_ENABLE (TEGRA_CAMERA_CID_BASE+101)
+#define TEGRA_CAMERA_CID_VI_HEIGHT_ALIGN (TEGRA_CAMERA_CID_BASE+102)
+#define TEGRA_CAMERA_CID_VI_SIZE_ALIGN (TEGRA_CAMERA_CID_BASE+103)
+#define TEGRA_CAMERA_CID_WRITE_ISPFORMAT (TEGRA_CAMERA_CID_BASE+104)
+
+#define TEGRA_CAMERA_CID_SENSOR_SIGNAL_PROPERTIES (TEGRA_CAMERA_CID_BASE+105)
+#define TEGRA_CAMERA_CID_SENSOR_IMAGE_PROPERTIES (TEGRA_CAMERA_CID_BASE+106)
+#define TEGRA_CAMERA_CID_SENSOR_CONTROL_PROPERTIES (TEGRA_CAMERA_CID_BASE+107)
+#define TEGRA_CAMERA_CID_SENSOR_DV_TIMINGS (TEGRA_CAMERA_CID_BASE+108)
+#define TEGRA_CAMERA_CID_LOW_LATENCY (TEGRA_CAMERA_CID_BASE+109)
+
+/* Custom Controls */
+#define V4L2_CID_GAIN_RED V4L2_CID_USER_BASE
+#define V4L2_CID_GAIN_GREENR V4L2_CID_USER_BASE + 1
+#define V4L2_CID_GAIN_GREENB V4L2_CID_USER_BASE + 2
+#define V4L2_CID_GAIN_BLUE V4L2_CID_USER_BASE + 3
+
+/**
+ * This is temporary with the current v4l2 infrastructure
+ * currently discussing with upstream maintainers our proposals and
+ * better approaches to resolve this
+ */
+#define TEGRA_CAMERA_CID_SENSOR_MODES (TEGRA_CAMERA_CID_BASE + 130)
+
+#define MAX_BUFFER_SIZE 32
+#define MAX_CID_CONTROLS 32
+#define MAX_NUM_SENSOR_MODES 30
+#define OF_MAX_STR_LEN 256
+#define OF_SENSORMODE_PREFIX ("mode")
+
+/*
+ * Scaling factor for converting a Q10.22 fixed point value
+ * back to its original floating point value
+ */
+#define FIXED_POINT_SCALING_FACTOR (1ULL << 22)
+
+#define TEGRA_CAM_MAX_STRING_CONTROLS 8
+#define TEGRA_CAM_STRING_CTRL_EEPROM_INDEX 0
+#define TEGRA_CAM_STRING_CTRL_FUSEID_INDEX 1
+#define TEGRA_CAM_STRING_CTRL_OTP_INDEX 2
+
+#define CSI_PHY_MODE_DPHY 0
+#define CSI_PHY_MODE_CPHY 1
+#define SLVS_EC 2
+
+struct unpackedU64 {
+ __u32 high;
+ __u32 low;
+};
+
+union __u64val {
+ struct unpackedU64 unpacked;
+ __u64 val;
+};
+
+struct sensor_signal_properties {
+ __u32 readout_orientation;
+ __u32 num_lanes;
+ __u32 mclk_freq;
+ union __u64val pixel_clock;
+ __u32 cil_settletime;
+ __u32 discontinuous_clk;
+ __u32 dpcm_enable;
+ __u32 tegra_sinterface;
+ __u32 phy_mode;
+ __u32 deskew_initial_enable;
+ __u32 deskew_periodic_enable;
+ union __u64val serdes_pixel_clock;
+ __u32 reserved[2];
+};
+
+struct sensor_image_properties {
+ __u32 width;
+ __u32 height;
+ __u32 line_length;
+ __u32 pixel_format;
+ __u32 embedded_metadata_height;
+ __u32 reserved[11];
+};
+
+struct sensor_dv_timings {
+ __u32 hfrontporch;
+ __u32 hsync;
+ __u32 hbackporch;
+ __u32 vfrontporch;
+ __u32 vsync;
+ __u32 vbackporch;
+ __u32 reserved[10];
+};
+
+struct sensor_control_properties {
+ __u32 gain_factor;
+ __u32 framerate_factor;
+ __u32 inherent_gain;
+ __u32 min_gain_val;
+ __u32 max_gain_val;
+ __u32 min_hdr_ratio;
+ __u32 max_hdr_ratio;
+ __u32 min_framerate;
+ __u32 max_framerate;
+ union __u64val min_exp_time;
+ union __u64val max_exp_time;
+ __u32 step_gain_val;
+ __u32 step_framerate;
+ __u32 exposure_factor;
+ union __u64val step_exp_time;
+ __u32 default_gain;
+ __u32 default_framerate;
+ union __u64val default_exp_time;
+ __u32 reserved[10];
+};
+
+struct sensor_mode_properties {
+ struct sensor_signal_properties signal_properties;
+ struct sensor_image_properties image_properties;
+ struct sensor_control_properties control_properties;
+ struct sensor_dv_timings dv_timings;
+};
+
+#define SENSOR_SIGNAL_PROPERTIES_CID_SIZE \
+ (sizeof(struct sensor_signal_properties) / sizeof(__u32))
+#define SENSOR_IMAGE_PROPERTIES_CID_SIZE \
+ (sizeof(struct sensor_image_properties) / sizeof(__u32))
+#define SENSOR_CONTROL_PROPERTIES_CID_SIZE \
+ (sizeof(struct sensor_control_properties) / sizeof(__u32))
+#define SENSOR_DV_TIMINGS_CID_SIZE \
+ (sizeof(struct sensor_dv_timings) / sizeof(__u32))
+#define SENSOR_MODE_PROPERTIES_CID_SIZE \
+ (sizeof(struct sensor_mode_properties) / sizeof(__u32))
+#define SENSOR_CONFIG_SIZE \
+ (sizeof(struct sensor_cfg) / sizeof(__u32))
+#define SENSOR_MODE_BLOB_SIZE \
+ (sizeof(struct sensor_blob) / sizeof(__u32))
+#define SENSOR_CTRL_BLOB_SIZE \
+ (sizeof(struct sensor_blob) / sizeof(__u32))
+#endif /* __TEGRA_V4L2_CAMERA__ */