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__ */