diff --git a/drivers/media/Makefile b/drivers/media/Makefile index 1349bbf9..3f292f7e 100644 --- a/drivers/media/Makefile +++ b/drivers/media/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# Copyright (c) 2022-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +obj-m += i2c/ obj-m += platform/ diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile new file mode 100644 index 00000000..08e53444 --- /dev/null +++ b/drivers/media/i2c/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +ubdir-ccflags-y += -Werror + +obj-m += nv_imx274.o diff --git a/drivers/media/i2c/imx274_mode_tbls.h b/drivers/media/i2c/imx274_mode_tbls.h new file mode 100644 index 00000000..e24d7c2f --- /dev/null +++ b/drivers/media/i2c/imx274_mode_tbls.h @@ -0,0 +1,700 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * imx274_mode_tbls.h - imx274 sensor driver + * + * Copyright (c) 2016-2023, NVIDIA CORPORATION. All rights reserved. + */ + +#ifndef __IMX274_I2C_TABLES__ +#define __IMX274_I2C_TABLES__ + +#include + +#define IMX274_TABLE_WAIT_MS 0 +#define IMX274_TABLE_END 1 +#define IMX274_WAIT_MS 1 +#define IMX274_WAIT_MS_START 15 + +#define ENABLE_EXTRA_MODES 0 + +#define imx274_reg struct reg_8 + +static const imx274_reg imx274_start[] = { + {0x3000, 0x00}, /* mode select streaming on */ + {0x303E, 0x02}, + {IMX274_TABLE_WAIT_MS, IMX274_WAIT_MS_START}, + {0x30F4, 0x00}, + {0x3018, 0xA2}, + {IMX274_TABLE_WAIT_MS, IMX274_WAIT_MS_START}, + {IMX274_TABLE_END, 0x00} +}; + +static const imx274_reg imx274_stop[] = { + {IMX274_TABLE_WAIT_MS, IMX274_WAIT_MS}, + {0x3000, 0x01}, /* mode select streaming off */ + {IMX274_TABLE_END, 0x00} +}; + +static const imx274_reg tp_colorbars[] = { + /* test pattern */ + {0x303C, 0x11}, + {0x303D, 0x0B}, + {0x370B, 0x11}, + {0x370E, 0x00}, + {0x377F, 0x01}, + {0x3781, 0x01}, + {IMX274_TABLE_END, 0x00} +}; + + +/* Mode 1 : 3840X2160 10 bits 30fps*/ +static const imx274_reg mode_3840X2160[] = { + {IMX274_TABLE_WAIT_MS, IMX274_WAIT_MS}, + {0x3000, 0x12}, /* mode select streaming on */ + /* input freq. 24M */ + {0x3120, 0xF0}, + {0x3122, 0x02}, + {0x3129, 0x9c}, + {0x312A, 0x02}, + {0x312D, 0x02}, + + {0x310B, 0x00}, + {0x304C, 0x00}, + {0x304D, 0x03}, + {0x331C, 0x1A}, + {0x3502, 0x02}, + {0x3529, 0x0E}, + {0x352A, 0x0E}, + {0x352B, 0x0E}, + {0x3538, 0x0E}, + {0x3539, 0x0E}, + {0x3553, 0x00}, + {0x357D, 0x05}, + {0x357F, 0x05}, + {0x3581, 0x04}, + {0x3583, 0x76}, + {0x3587, 0x01}, + {0x35BB, 0x0E}, + {0x35BC, 0x0E}, + {0x35BD, 0x0E}, + {0x35BE, 0x0E}, + {0x35BF, 0x0E}, + {0x366E, 0x00}, + {0x366F, 0x00}, + {0x3670, 0x00}, + {0x3671, 0x00}, + {0x30EE, 0x01}, + {0x3304, 0x32}, + {0x3306, 0x32}, + {0x3590, 0x32}, + {0x3686, 0x32}, + /* resolution */ + {0x30E2, 0x01}, + {0x30F6, 0x07}, + {0x30F7, 0x01}, + {0x30F8, 0xC6}, + {0x30F9, 0x11}, + {0x3130, 0x78}, /*WRITE_VSIZE*/ + {0x3131, 0x08}, + {0x3132, 0x70}, /*Y_OUT_SIZE*/ + {0x3133, 0x08}, + + /* crop */ + {0x30DD, 0x01}, /*VWIDCUTEN*/ + {0x30DE, 0x04}, /*VWIDCUT*/ + {0x30E0, 0x03}, /*VWINCUTPOS*/ + {0x3037, 0x01}, /*HTRIMMING_EN*/ + {0x3038, 0x0C}, /*HTRIMMING_START*/ + {0x3039, 0x00}, + {0x303A, 0x0C}, /*HTRIMMING_END*/ + {0x303B, 0x0F}, + + /* mode setting */ + {0x3004, 0x01}, + {0x3005, 0x01}, + {0x3006, 0x00}, + {0x3007, 0xA2}, + {0x300C, 0x0C}, /* SHR: Minimum 12 */ + {0x300D, 0x00}, + {0x300E, 0x01}, + {0x3019, 0x00}, + {0x3A41, 0x08}, + {0x3342, 0x0A}, + {0x3343, 0x00}, + {0x3344, 0x16}, + {0x3345, 0x00}, + {0x3528, 0x0E}, + {0x3554, 0x1F}, + {0x3555, 0x01}, + {0x3556, 0x01}, + {0x3557, 0x01}, + {0x3558, 0x01}, + {0x3559, 0x00}, + {0x355A, 0x00}, + {0x35BA, 0x0E}, + {0x366A, 0x1B}, + {0x366B, 0x1A}, + {0x366C, 0x19}, + {0x366D, 0x17}, + {0x33A6, 0x01}, + {0x306B, 0x05}, + + {IMX274_TABLE_WAIT_MS, IMX274_WAIT_MS}, + {IMX274_TABLE_END, 0x0000} +}; + +/* Mode 1 : 3840X2160 10 bits 60fps*/ +static const imx274_reg mode_3840X2160_60fps[] = { + {IMX274_TABLE_WAIT_MS, IMX274_WAIT_MS}, + {0x3000, 0x12}, /* mode select streaming on */ + /* input freq. 24M */ + {0x3120, 0xF0}, + {0x3122, 0x02}, + {0x3129, 0x9c}, + {0x312A, 0x02}, + {0x312D, 0x02}, + + {0x310B, 0x00}, + {0x304C, 0x00}, + {0x304D, 0x03}, + {0x331C, 0x1A}, + {0x3502, 0x02}, + {0x3529, 0x0E}, + {0x352A, 0x0E}, + {0x352B, 0x0E}, + {0x3538, 0x0E}, + {0x3539, 0x0E}, + {0x3553, 0x00}, + {0x357D, 0x05}, + {0x357F, 0x05}, + {0x3581, 0x04}, + {0x3583, 0x76}, + {0x3587, 0x01}, + {0x35BB, 0x0E}, + {0x35BC, 0x0E}, + {0x35BD, 0x0E}, + {0x35BE, 0x0E}, + {0x35BF, 0x0E}, + {0x366E, 0x00}, + {0x366F, 0x00}, + {0x3670, 0x00}, + {0x3671, 0x00}, + {0x30EE, 0x01}, + {0x3304, 0x32}, + {0x3306, 0x32}, + {0x3590, 0x32}, + {0x3686, 0x32}, + /* resolution */ + {0x30E2, 0x01}, + {0x30F6, 0x07}, + {0x30F7, 0x01}, + {0x30F8, 0xC6}, + {0x30F9, 0x11}, + {0x3130, 0x78}, /*WRITE_VSIZE*/ + {0x3131, 0x08}, + {0x3132, 0x70}, /*Y_OUT_SIZE*/ + {0x3133, 0x08}, + + /* crop */ + {0x30DD, 0x01}, /*VWIDCUTEN*/ + {0x30DE, 0x04}, /*VWIDCUT*/ + {0x30E0, 0x03}, /*VWINCUTPOS*/ + {0x3037, 0x01}, /*HTRIMMING_EN*/ + {0x3038, 0x0C}, /*HTRIMMING_START*/ + {0x3039, 0x00}, + {0x303A, 0x0C}, /*HTRIMMING_END*/ + {0x303B, 0x0F}, + + /* mode setting */ + {0x3004, 0x01}, + {0x3005, 0x01}, + {0x3006, 0x00}, + {0x3007, 0x02}, + {0x300C, 0x0C}, /* SHR: Minimum 12 */ + {0x300D, 0x00}, + {0x300E, 0x00}, + {0x3019, 0x00}, + {0x3A41, 0x08}, + {0x3342, 0x0A}, + {0x3343, 0x00}, + {0x3344, 0x16}, + {0x3345, 0x00}, + {0x3528, 0x0E}, + {0x3554, 0x1F}, + {0x3555, 0x01}, + {0x3556, 0x01}, + {0x3557, 0x01}, + {0x3558, 0x01}, + {0x3559, 0x00}, + {0x355A, 0x00}, + {0x35BA, 0x0E}, + {0x366A, 0x1B}, + {0x366B, 0x1A}, + {0x366C, 0x19}, + {0x366D, 0x17}, + {0x33A6, 0x01}, + {0x306B, 0x05}, + + {IMX274_TABLE_WAIT_MS, IMX274_WAIT_MS}, + {IMX274_TABLE_END, 0x0000} +}; + +/* Mode1(DOL): 3840x2160 10 bits 30fps DOL-HDR + * Active H: LI (4) + Left margin (12) + 3840 = 3856 + * Active V: [OB (8) + 2166 + VBP (50)] * 2 exposures = 4448 + */ +static imx274_reg mode_3840X2160_dol_30fps[] = { + {IMX274_TABLE_WAIT_MS, IMX274_WAIT_MS}, + {0x3000, 0x12}, + /*MCLK 24MHz */ + {0x3120, 0xF0}, + {0x3121, 0x00}, + {0x3122, 0x02}, + {0x3129, 0x9C}, + {0x312A, 0x02}, + {0x312D, 0x02}, + {0x310B, 0x00}, + {0x304C, 0x00}, + {0x304D, 0x03}, + {0x331C, 0x1A}, + {0x331D, 0x00}, + {0x3502, 0x02}, + {0x3529, 0x0E}, + {0x352A, 0x0E}, + {0x352B, 0x0E}, + {0x3538, 0x0E}, + {0x3539, 0x0E}, + {0x3553, 0x00}, + {0x357D, 0x05}, + {0x357F, 0x05}, + {0x3581, 0x04}, + {0x3583, 0x76}, + {0x3587, 0x01}, + {0x35BB, 0x0E}, + {0x35BC, 0x0E}, + {0x35BD, 0x0E}, + {0x35BE, 0x0E}, + {0x35BF, 0x0E}, + {0x366E, 0x00}, + {0x366F, 0x00}, + {0x3670, 0x00}, + {0x3671, 0x00}, + {0x30EE, 0x01}, + {0x3304, 0x32}, + {0x3305, 0x00}, + {0x3306, 0x32}, + {0x3307, 0x00}, + {0x3590, 0x32}, + {0x3391, 0x00}, + {0x3686, 0x32}, + {0x3687, 0x00}, + + /*Mode Setting*/ + {0x3004, 0x06}, + {0x3005, 0x01}, + {0x3006, 0x00}, + {0x3007, 0xA2}, /* [7:5] is set to 0x5 to enable VWINPOS cropping. */ + {0x300C, 0x06}, /* SHR: Minimum 6 */ + {0x300D, 0x00}, + {0x300E, 0x00}, + {0x3019, 0x31}, + {0x301A, 0x00}, + {0x302E, 0x06}, + {0x302F, 0x00}, + {0x3030, 0x80}, + {0x3031, 0x01}, + {0x3032, 0x32}, + {0x3033, 0x00}, + {0x3041, 0x31}, + {0x3042, 0x07}, + {0x3043, 0x01}, + {0x306B, 0x05}, + {0x30E2, 0x01}, + {0x30E9, 0x01}, + {0x30F6, 0x1C}, + {0x30F7, 0x04}, + {0x30F8, 0xEC}, + {0x30F9, 0x08}, + {0x30FA, 0x00}, + {0x3037, 0x01}, + {0x3038, 0x00}, /* Note that the 12 "margin" pixels are NOT cropped here. */ + /* They will be cropped by CSI along with LI pixels. */ + /* This is a WAR for CSI cropping alignment requirements. */ + {0x3039, 0x00}, + {0x303A, 0x0C}, + {0x303B, 0x0F}, + {0x30DD, 0x01}, + {0x30DE, 0x04}, /* VWIDCUT: Crop 4 margin rows from the top and bottom. */ + {0x30DF, 0x00}, + {0x30E0, 0x03}, /* VWINPOS: Crop after 6 ignored area rows (VWINPOS * 2) */ + {0x30E1, 0x00}, + {0x3130, 0x7E}, /* WRITE_VSIZE: 2174 = post-crop size (2166) + OB (8) */ + {0x3131, 0x08}, + {0x3132, 0xA8}, /* Y_OUT_SIZE: 2216 = post-crop (2166) + RHS (50) */ + {0x3133, 0x08}, + {0x3342, 0x0A}, + {0x3343, 0x00}, + {0x3344, 0x16}, + {0x3345, 0x00}, + {0x33A6, 0x01}, + {0x3528, 0x0E}, + {0x3554, 0x1F}, + {0x3555, 0x01}, + {0x3556, 0x01}, + {0x3557, 0x01}, + {0x3558, 0x01}, + {0x3559, 0x00}, + {0x355A, 0x00}, + {0x35BA, 0x0E}, + {0x366A, 0x1B}, + {0x366B, 0x1A}, + {0x366C, 0x19}, + {0x366D, 0x17}, + {0x3A41, 0x08}, + + {IMX274_TABLE_WAIT_MS, IMX274_WAIT_MS}, + {IMX274_TABLE_END, 0x0000} +}; + +/* Mode 3(DOL) : 1920x1080 10 bits 60fps DOL-HDR + * Active H: LI (4) + Left margin (6) + 1920 + Right margin (6) = 1936 + * Active V: [OB (8) + 1086 + VBP (38)] * 2 exposures = 2264 + */ +static imx274_reg mode_1920X1080_dol_60fps[] = { + {IMX274_TABLE_WAIT_MS, IMX274_WAIT_MS}, + {0x3000, 0x12}, + /*MCLK 24MHz */ + {0x3120, 0xF0}, + {0x3121, 0x00}, + {0x3122, 0x02}, + {0x3129, 0x9C}, + {0x312A, 0x02}, + {0x312D, 0x02}, + {0x310B, 0x00}, + {0x304C, 0x00}, + {0x304D, 0x03}, + {0x331C, 0x1A}, + {0x331D, 0x00}, + {0x3502, 0x02}, + {0x3529, 0x0E}, + {0x352A, 0x0E}, + {0x352B, 0x0E}, + {0x3538, 0x0E}, + {0x3539, 0x0E}, + {0x3553, 0x00}, + {0x357D, 0x05}, + {0x357F, 0x05}, + {0x3581, 0x04}, + {0x3583, 0x76}, + {0x3587, 0x01}, + {0x35BB, 0x0E}, + {0x35BC, 0x0E}, + {0x35BD, 0x0E}, + {0x35BE, 0x0E}, + {0x35BF, 0x0E}, + {0x366E, 0x00}, + {0x366F, 0x00}, + {0x3670, 0x00}, + {0x3671, 0x00}, + {0x30EE, 0x01}, + {0x3304, 0x32}, + {0x3305, 0x00}, + {0x3306, 0x32}, + {0x3307, 0x00}, + {0x3590, 0x32}, + {0x3391, 0x00}, + {0x3686, 0x32}, + {0x3687, 0x00}, + + /*Mode Setting*/ + {0x3004, 0x07}, + {0x3005, 0x21}, + {0x3006, 0x00}, + {0x3007, 0xB1}, + {0x300C, 0x04}, /* SHR: Minimum 4 */ + {0x300D, 0x00}, + {0x300E, 0x00}, + {0x3019, 0x31}, + {0x301A, 0x00}, + {0x302E, 0x06}, + {0x302F, 0x00}, + {0x3030, 0x10}, + {0x3031, 0x00}, + {0x3032, 0x26}, + {0x3033, 0x00}, + {0x3041, 0x31}, + {0x3042, 0x04}, + {0x3043, 0x01}, + {0x306B, 0x05}, + {0x30E2, 0x02}, + {0x30E9, 0x01}, + {0x30F6, 0x1C}, /* HMAX */ + {0x30F7, 0x04}, + {0x30F8, 0x83}, + {0x30F9, 0x04}, + {0x30FA, 0x00}, + {0x30EE, 0x01}, + {0x30DD, 0x01}, + {0x30DE, 0x04}, /* VWIDCUT */ + {0x30DF, 0x00}, + {0x30E0, 0x03}, /* VWINPOS */ + {0x30E1, 0x00}, + {0x3037, 0x01}, + {0x3038, 0x00}, /* HTRIM (6 left and 6 right margin pixels output) */ + {0x3039, 0x00}, + {0x303A, 0x18}, + {0x303B, 0x0F}, + {0x3130, 0x46}, /* WRITE_VSIZE: 1094 = post-crop size (1086) + OB (8)*/ + {0x3131, 0x04}, + {0x3132, 0x64}, /* Y_OUT_SIZE: 1124 = post-crop (1086) + RHS1 (38)*/ + {0x3133, 0x04}, + {0x3342, 0x0A}, + {0x3343, 0x00}, + {0x3344, 0x1A}, + {0x3345, 0x00}, + {0x33A6, 0x01}, + {0x3528, 0x0E}, + {0x3554, 0x00}, + {0x3555, 0x01}, + {0x3556, 0x01}, + {0x3557, 0x01}, + {0x3558, 0x01}, + {0x3559, 0x00}, + {0x355A, 0x00}, + {0x35BA, 0x0E}, + {0x366A, 0x1B}, + {0x366B, 0x1A}, + {0x366C, 0x19}, + {0x366D, 0x17}, + {0x3A41, 0x08}, + + {IMX274_TABLE_WAIT_MS, IMX274_WAIT_MS}, + {IMX274_TABLE_END, 0x0000} +}; + +/* Mode 3 : 1920X1080 10 bits 60fps*/ +static imx274_reg mode_1920X1080[] = { + {IMX274_TABLE_WAIT_MS, IMX274_WAIT_MS}, + {0x3000, 0x12}, /* mode select streaming on */ + /* input freq. 24M */ + {0x3120, 0xF0}, + {0x3122, 0x02}, + {0x3129, 0x9c}, + {0x312A, 0x02}, + {0x312D, 0x02}, + + {0x310B, 0x00}, + {0x304C, 0x00}, + {0x304D, 0x03}, + {0x331C, 0x1A}, + {0x3502, 0x02}, + {0x3529, 0x0E}, + {0x352A, 0x0E}, + {0x352B, 0x0E}, + {0x3538, 0x0E}, + {0x3539, 0x0E}, + {0x3553, 0x00}, + {0x357D, 0x05}, + {0x357F, 0x05}, + {0x3581, 0x04}, + {0x3583, 0x76}, + {0x3587, 0x01}, + {0x35BB, 0x0E}, + {0x35BC, 0x0E}, + {0x35BD, 0x0E}, + {0x35BE, 0x0E}, + {0x35BF, 0x0E}, + {0x366E, 0x00}, + {0x366F, 0x00}, + {0x3670, 0x00}, + {0x3671, 0x00}, + {0x30EE, 0x01}, + {0x3304, 0x32}, + {0x3306, 0x32}, + {0x3590, 0x32}, + {0x3686, 0x32}, + /* resolution */ + {0x30E2, 0x02}, + {0x30F6, 0x04}, + {0x30F7, 0x01}, + {0x30F8, 0x0C}, + {0x30F9, 0x12}, + {0x3130, 0x40}, + {0x3131, 0x04}, + {0x3132, 0x38}, + {0x3133, 0x04}, + + /* crop */ + {0x30DD, 0x01}, + {0x30DE, 0x07}, + {0x30DF, 0x00}, + {0x30E0, 0x04}, + {0x30E1, 0x00}, + {0x3037, 0x01}, + {0x3038, 0x0C}, + {0x3039, 0x00}, + {0x303A, 0x0C}, + {0x303B, 0x0F}, + + /* mode setting */ + {0x3004, 0x02}, + {0x3005, 0x21}, + {0x3006, 0x00}, + {0x3007, 0xB1}, + {0x300C, 0x08}, /* SHR: Minimum 8 */ + {0x300D, 0x00}, + {0x3019, 0x00}, + {0x3A41, 0x08}, + {0x3342, 0x0A}, + {0x3343, 0x00}, + {0x3344, 0x1A}, + {0x3345, 0x00}, + {0x3528, 0x0E}, + {0x3554, 0x00}, + {0x3555, 0x01}, + {0x3556, 0x01}, + {0x3557, 0x01}, + {0x3558, 0x01}, + {0x3559, 0x00}, + {0x355A, 0x00}, + {0x35BA, 0x0E}, + {0x366A, 0x1B}, + {0x366B, 0x1A}, + {0x366C, 0x19}, + {0x366D, 0x17}, + {0x33A6, 0x01}, + {0x306B, 0x05}, + + {IMX274_TABLE_WAIT_MS, IMX274_WAIT_MS}, + {IMX274_TABLE_END, 0x0000} +}; + +/* Mode 5 : 1288X546 10 bits 240fps*/ +static const imx274_reg mode_1288x546[] = { + {IMX274_TABLE_WAIT_MS, IMX274_WAIT_MS}, + {0x3000, 0x12}, /* mode select streaming on */ + /* input freq. 24M */ + {0x3120, 0xF0}, + {0x3122, 0x02}, + {0x3129, 0x9c}, + {0x312A, 0x02}, + {0x312D, 0x02}, + + {0x310B, 0x00}, + {0x304C, 0x00}, + {0x304D, 0x03}, + {0x331C, 0x1A}, + {0x3502, 0x02}, + {0x3529, 0x0E}, + {0x352A, 0x0E}, + {0x352B, 0x0E}, + {0x3538, 0x0E}, + {0x3539, 0x0E}, + {0x3553, 0x00}, + {0x357D, 0x05}, + {0x357F, 0x05}, + {0x3581, 0x04}, + {0x3583, 0x76}, + {0x3587, 0x01}, + {0x35BB, 0x0E}, + {0x35BC, 0x0E}, + {0x35BD, 0x0E}, + {0x35BE, 0x0E}, + {0x35BF, 0x0E}, + {0x366E, 0x00}, + {0x366F, 0x00}, + {0x3670, 0x00}, + {0x3671, 0x00}, + {0x30EE, 0x01}, + {0x3304, 0x32}, + {0x3306, 0x32}, + {0x3590, 0x32}, + {0x3686, 0x32}, + /* resolution */ + {0x30E2, 0x04}, + {0x30F6, 0x04}, /* HMAX 260 */ + {0x30F7, 0x01}, /* HMAX */ + {0x30F8, 0x83}, /* VMAX 1155 */ + {0x30F9, 0x04}, /* VMAX */ + {0x30FA, 0x00}, /* VMAX */ + {0x3130, 0x26}, + {0x3131, 0x02}, + {0x3132, 0x22}, + {0x3133, 0x02}, + /* mode setting */ + {0x3004, 0x04}, + {0x3005, 0x31}, + {0x3006, 0x00}, + {0x3007, 0x02}, + {0x300C, 0x04}, /* SHR: Minimum 4 */ + {0x300D, 0x00}, + {0x3019, 0x00}, + {0x3A41, 0x04}, + {0x3342, 0x0A}, + {0x3343, 0x00}, + {0x3344, 0x1A}, + {0x3345, 0x00}, + {0x3528, 0x0E}, + {0x3554, 0x00}, + {0x3555, 0x01}, + {0x3556, 0x01}, + {0x3557, 0x01}, + {0x3558, 0x01}, + {0x3559, 0x00}, + {0x355A, 0x00}, + {0x35BA, 0x0E}, + {0x366A, 0x1B}, + {0x366B, 0x19}, + {0x366C, 0x17}, + {0x366D, 0x17}, + {0x33A6, 0x01}, + {0x306B, 0x05}, + + {IMX274_TABLE_WAIT_MS, IMX274_WAIT_MS}, + {IMX274_TABLE_END, 0x0000} +}; + +enum { + IMX274_MODE_3840X2160, + IMX274_MODE_1920X1080, + IMX274_MODE_3840X2160_DOL_30FPS, + IMX274_MODE_1920X1080_DOL_60FPS, + IMX274_MODE_1288X546, + IMX274_MODE_START_STREAM, + IMX274_MODE_STOP_STREAM, + IMX274_MODE_TEST_PATTERN, +}; + +static const imx274_reg *mode_table[] = { + [IMX274_MODE_3840X2160] = mode_3840X2160_60fps, + [IMX274_MODE_1920X1080] = mode_1920X1080, + [IMX274_MODE_3840X2160_DOL_30FPS] = mode_3840X2160_dol_30fps, + [IMX274_MODE_1920X1080_DOL_60FPS] = mode_1920X1080_dol_60fps, + [IMX274_MODE_1288X546] = mode_1288x546, + [IMX274_MODE_START_STREAM] = imx274_start, + [IMX274_MODE_STOP_STREAM] = imx274_stop, + [IMX274_MODE_TEST_PATTERN] = tp_colorbars, +}; + +static const int imx274_30_fr[] = { + 30, +}; + +static const int imx274_60_fr[] = { + 60, +}; + +static const int imx274_240_fr[] = { + 240, +}; + +static const struct camera_common_frmfmt imx274_frmfmt[] = { + {{3840, 2160}, imx274_60_fr, 1, 0, IMX274_MODE_3840X2160}, + {{1920, 1080}, imx274_60_fr, 1, 0, IMX274_MODE_1920X1080}, + {{3856, 4448}, imx274_30_fr, 1, 1, IMX274_MODE_3840X2160_DOL_30FPS}, + {{1936, 2264}, imx274_60_fr, 1, 1, IMX274_MODE_1920X1080_DOL_60FPS}, +#if ENABLE_EXTRA_MODES + {{1288, 546}, imx274_240_fr, 1, 0, IMX274_MODE_1288X546}, +#endif +}; +#endif /* __IMX274_I2C_TABLES__ */ diff --git a/drivers/media/i2c/nv_imx274.c b/drivers/media/i2c/nv_imx274.c new file mode 100644 index 00000000..203edc08 --- /dev/null +++ b/drivers/media/i2c/nv_imx274.c @@ -0,0 +1,1370 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * nv_imx274.c - imx274 sensor driver + * + * Copyright (c) 2015-2023, NVIDIA CORPORATION. All rights reserved. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "imx274_mode_tbls.h" + +#define IMX274_GAIN_FACTOR 1000000 +#define IMX274_MIN_GAIN (1 * IMX274_GAIN_FACTOR) +#define IMX274_MAX_ANALOG_GAIN (IMX274_MIN_GAIN * 111 / 5) +#define IMX274_MAX_DIGITAL_GAIN 64 +#define IMX274_MAX_GAIN (IMX274_MAX_ANALOG_GAIN * IMX274_MAX_DIGITAL_GAIN) + +#define IMX274_SENSOR_INTERNAL_CLK_FREQ 72000000 +#define IMX274_4K_MODE_HMAX 263 +#define IMX274_4K_MODE_MIN_VMAX 4550 +#define IMX274_4K_MODE_OFFSET 112 +#define IMX274_DOL_MODE_CLOCKS_OFFSET 112 +#define IMX274_DOL_4K_MODE_HMAX 1052 +#define IMX274_DOL_4K_MODE_MIN_VMAX 2284 +#define IMX274_DOL_4K_MODE_DEFAULT_RHS1 50 +#define IMX274_DOL_4K_MIN_SHR_DOL1 6 +#define IMX274_DOL_1080P_MODE_HMAX 1040 +#define IMX274_DOL_1080P_MODE_MIN_VMAX 1155 +#define IMX274_DOL_1080P_MODE_DEFAULT_RHS1 38 +#define IMX274_DOL_1080P_MIN_SHR_DOL1 4 +#define IMX274_1080P_MODE_HMAX 260 +#define IMX274_1080P_MODE_MIN_VMAX 4620 +#define IMX274_1080P_MODE_OFFSET 112 + +#define IMX274_EEPROM_ADDRESS 0x57 +#define IMX274_EEPROM_SIZE 256 +#define IMX274_EEPROM_STR_SIZE (IMX274_EEPROM_SIZE * 2) +#define IMX274_EEPROM_BLOCK_SIZE (1 << 8) +#define IMX274_EEPROM_NUM_BLOCKS \ + (IMX274_EEPROM_SIZE / IMX274_EEPROM_BLOCK_SIZE) + +#define IMX274_FUSE_ID_START_ADDR 91 +#define IMX274_FUSE_ID_SIZE 8 +#define IMX274_FUSE_ID_STR_SIZE (IMX274_FUSE_ID_SIZE * 2) + +static const struct of_device_id imx274_of_match[] = { + { .compatible = "sony,imx274", }, + { }, +}; +MODULE_DEVICE_TABLE(of, imx274_of_match); + +static const u32 ctrl_cid_list[] = { + TEGRA_CAMERA_CID_GAIN, + TEGRA_CAMERA_CID_EXPOSURE, + TEGRA_CAMERA_CID_EXPOSURE_SHORT, + TEGRA_CAMERA_CID_FRAME_RATE, + TEGRA_CAMERA_CID_HDR_EN, + TEGRA_CAMERA_CID_FUSE_ID, +}; + +struct imx274 { + struct i2c_client *i2c_client; + struct v4l2_subdev *subdev; + + const char *devname; + struct dentry *debugfs_dir; + struct mutex streaming_lock; + bool streaming; + + struct camera_common_eeprom_data eeprom[IMX274_EEPROM_NUM_BLOCKS]; + u8 eeprom_buf[IMX274_EEPROM_STR_SIZE]; + u8 fuse_id[IMX274_FUSE_ID_SIZE]; + u32 frame_length; + u32 vmax; + s64 last_exposure_long; + s64 last_exposure_short; + s32 group_hold_prev; + bool group_hold_en; + struct regmap *regmap; + struct camera_common_data *s_data; + struct tegracam_device *tc_dev; +}; + +static const struct regmap_config sensor_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, +}; + +static inline void imx274_get_vmax_regs(imx274_reg *regs, + u32 vmax) +{ + regs->addr = IMX274_VMAX_ADDR_MSB; + regs->val = (vmax >> 16) & 0x0f; + (regs + 1)->addr = IMX274_VMAX_ADDR_MID; + (regs + 1)->val = (vmax >> 8) & 0xff; + (regs + 2)->addr = IMX274_VMAX_ADDR_LSB; + (regs + 2)->val = (vmax) & 0xff; +} + +static inline void imx274_get_shr_regs(imx274_reg *regs, + u16 shr) +{ + regs->addr = IMX274_SHR_ADDR_MSB; + regs->val = (shr >> 8) & 0xff; + (regs + 1)->addr = IMX274_SHR_ADDR_LSB; + (regs + 1)->val = (shr) & 0xff; +} + +static inline void imx274_get_shr_dol1_regs(imx274_reg *regs, + u16 shr) +{ + regs->addr = IMX274_SHR_DOL1_ADDR_MSB; + regs->val = (shr >> 8) & 0xff; + (regs + 1)->addr = IMX274_SHR_DOL1_ADDR_LSB; + (regs + 1)->val = (shr) & 0xff; +} + +static inline void imx274_get_shr_dol2_regs(imx274_reg *regs, + u16 shr) +{ + regs->addr = IMX274_SHR_DOL2_ADDR_MSB; + regs->val = (shr >> 8) & 0xff; + (regs + 1)->addr = IMX274_SHR_DOL2_ADDR_LSB; + (regs + 1)->val = (shr) & 0xff; +} + +static inline void imx274_get_gain_reg(imx274_reg *regs, + u16 gain) +{ + regs->addr = IMX274_ANALOG_GAIN_ADDR_MSB; + regs->val = (gain >> 8) & 0xff; + (regs + 1)->addr = IMX274_ANALOG_GAIN_ADDR_LSB; + (regs + 1)->val = (gain) & 0xff; +} + +static int test_mode; +module_param(test_mode, int, 0644); + +static inline int imx274_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 imx274_write_reg(struct camera_common_data *s_data, u16 addr, u8 val) +{ + int err; + struct device *dev = s_data->dev; + + err = regmap_write(s_data->regmap, addr, val); + if (err) + dev_err(dev, "%s: i2c write failed, %x = %x\n", + __func__, addr, val); + + return err; +} + +static int imx274_write_table(struct imx274 *priv, + const imx274_reg table[]) +{ + return regmap_util_write_table_8(priv->s_data->regmap, + table, + NULL, 0, + IMX274_TABLE_WAIT_MS, + IMX274_TABLE_END); +} + +static int imx274_set_gain(struct tegracam_device *tc_dev, s64 val); +static int imx274_set_frame_rate(struct tegracam_device *tc_dev, s64 val); +static int imx274_set_exposure(struct tegracam_device *tc_dev, s64 val); +static int imx274_set_exposure_shr(struct tegracam_device *tc_dev, s64 val); +static int imx274_set_exposure_shr_dol_short(struct tegracam_device *tc_dev, + s64 val); +static int imx274_set_exposure_shr_dol_long(struct tegracam_device *tc_dev, + s64 val); +static bool imx274_in_dol_mode(const struct imx274 *priv); + +static int imx274_set_group_hold(struct tegracam_device *tc_dev, bool val) +{ + struct camera_common_data *s_data = tc_dev->s_data; + struct imx274 *priv = (struct imx274 *)tc_dev->priv; + struct device *dev = tc_dev->dev; + int err; + + priv->group_hold_prev = val; + err = imx274_write_reg(s_data, + IMX274_GROUP_HOLD_ADDR, val); + if (err) + dev_err(dev, + "%s: Group hold control error\n", __func__); + + return err; +} + +static int imx274_set_gain(struct tegracam_device *tc_dev, s64 val) +{ + struct camera_common_data *s_data = tc_dev->s_data; + struct imx274 *priv = (struct imx274 *)tc_dev->priv; + struct device *dev = tc_dev->dev; + const struct sensor_mode_properties *mode = + &s_data->sensor_props.sensor_modes[s_data->mode]; + imx274_reg reg_list[2]; + int err; + int i = 0; + u32 again; + u8 dgain; + u16 reg_again; + u8 reg_dgain; + + dev_dbg(dev, "%s: val: %lld\n", __func__, val); + + if (val < IMX274_MIN_GAIN) + val = IMX274_MIN_GAIN; + else if (val > IMX274_MAX_GAIN) + val = IMX274_MAX_GAIN; + + if (val > (IMX274_MAX_ANALOG_GAIN * 32)) { + dgain = 64; + reg_dgain = 0x06; + } else if (val > (IMX274_MAX_ANALOG_GAIN * 16)) { + dgain = 32; + reg_dgain = 0x05; + } else if (val > (IMX274_MAX_ANALOG_GAIN * 8)) { + dgain = 16; + reg_dgain = 0x04; + } else if (val > (IMX274_MAX_ANALOG_GAIN * 4)) { + dgain = 8; + reg_dgain = 0x03; + } else if (val > (IMX274_MAX_ANALOG_GAIN * 2)) { + dgain = 4; + reg_dgain = 0x02; + } else if (val > (IMX274_MAX_ANALOG_GAIN)) { + dgain = 2; + reg_dgain = 0x01; + } else { + dgain = 1; + reg_dgain = 0x00; + } + + reg_again = 2048 - + (2048 * dgain * mode->control_properties.gain_factor / val); + if (reg_again > 1957) + reg_again = 1957; + again = val / (dgain * mode->control_properties.gain_factor); + + imx274_get_gain_reg(reg_list, reg_again); + + dev_dbg(dev, "%s: val:%lld, gain:%lld, again:(%d, %d), dgain:(%d, %d)\n", + __func__, + val, + val / IMX274_MIN_GAIN, + again, + reg_again, + dgain, + reg_dgain); + + /* writing analog gain */ + for (i = 0; i < 2; i++) { + err = imx274_write_reg(priv->s_data, reg_list[i].addr, + reg_list[i].val); + if (err) + goto fail; + } + + /* writing digital gain */ + err = imx274_write_reg(priv->s_data, IMX274_DIGITAL_GAIN_ADDR, + reg_dgain); + if (err) + goto fail; + + return 0; + +fail: + dev_err(dev, "%s: GAIN control error\n", __func__); + return err; +} + +static +int imx274_set_exposure_shr_dol_short(struct tegracam_device *tc_dev, s64 val) +{ + struct camera_common_data *s_data = tc_dev->s_data; + struct imx274 *priv = (struct imx274 *)tc_dev->priv; + struct device *dev = tc_dev->dev; + const struct sensor_mode_properties *mode = + &s_data->sensor_props.sensor_modes[s_data->mode]; + struct v4l2_control control; + int hdr_en; + imx274_reg reg_list[2]; + u16 rhs1, hmax; + s32 shr_dol1, min_shr; + int err; + int i = 0; + u64 freq = IMX274_SENSOR_INTERNAL_CLK_FREQ; + + control.id = TEGRA_CAMERA_CID_HDR_EN; + err = camera_common_g_ctrl(priv->s_data, &control); + if (err < 0) { + dev_err(dev, "could not find device ctrl.\n"); + return err; + } + + hdr_en = switch_ctrl_qmenu[control.value]; + if (hdr_en != SWITCH_ON) { + dev_dbg(dev, "%s: SHR DOL1 is ignored for non-HDR mode\n", + __func__); + return 0; + } + + if (s_data->mode == IMX274_MODE_3840X2160_DOL_30FPS) { + rhs1 = IMX274_DOL_4K_MODE_DEFAULT_RHS1; + min_shr = IMX274_DOL_4K_MIN_SHR_DOL1; + hmax = IMX274_DOL_4K_MODE_HMAX; + } else if (s_data->mode == IMX274_MODE_1920X1080_DOL_60FPS) { + rhs1 = IMX274_DOL_1080P_MODE_DEFAULT_RHS1; + min_shr = IMX274_DOL_1080P_MIN_SHR_DOL1; + hmax = IMX274_DOL_1080P_MODE_HMAX; + } else { + dev_err(dev, "%s: error, invalid dol mode\n", __func__); + err = -EINVAL; + goto fail; + } + + + priv->last_exposure_short = val; + shr_dol1 = rhs1 - + (val * freq / + mode->control_properties.exposure_factor - + IMX274_DOL_MODE_CLOCKS_OFFSET) / + hmax - 1 / 4; + + if (shr_dol1 <= min_shr) + shr_dol1 = min_shr; + + if (shr_dol1 > rhs1 - 2) + shr_dol1 = rhs1 - 2; + + dev_dbg(dev, + "%s: et:%d, shr_dol1:%d, rhs1:%d, Pclk:%lld, LL:%d, HMAX:%d\n", + __func__, + (int)(val * 1000000 / mode->control_properties.exposure_factor), + shr_dol1, + rhs1, + mode->signal_properties.pixel_clock.val, + mode->image_properties.line_length, + hmax); + + imx274_get_shr_dol1_regs(reg_list, (u16)shr_dol1); + + for (i = 0; i < 2; i++) { + err = imx274_write_reg(priv->s_data, reg_list[i].addr, + reg_list[i].val); + if (err) + goto fail; + } + return 0; + +fail: + dev_err(dev, "%s: EXPOSURE short control is not set\n", __func__); + return err; +} + +static +int imx274_set_exposure_shr_dol_long(struct tegracam_device *tc_dev, s64 val) +{ + struct camera_common_data *s_data = tc_dev->s_data; + struct imx274 *priv = (struct imx274 *)tc_dev->priv; + struct device *dev = tc_dev->dev; + const struct sensor_mode_properties *mode = + &s_data->sensor_props.sensor_modes[s_data->mode]; + imx274_reg reg_list[2]; + u16 hmax, rhs1; + s32 shr_dol2, min_shr; + int err; + int i = 0; + u64 freq = IMX274_SENSOR_INTERNAL_CLK_FREQ; + + if (s_data->mode == IMX274_MODE_3840X2160_DOL_30FPS) { + rhs1 = IMX274_DOL_4K_MODE_DEFAULT_RHS1; + min_shr = IMX274_DOL_4K_MIN_SHR_DOL1; + hmax = IMX274_DOL_4K_MODE_HMAX; + } else if (s_data->mode == IMX274_MODE_1920X1080_DOL_60FPS) { + rhs1 = IMX274_DOL_1080P_MODE_DEFAULT_RHS1; + min_shr = IMX274_DOL_1080P_MIN_SHR_DOL1; + hmax = IMX274_DOL_1080P_MODE_HMAX; + } else { + dev_err(dev, "%s: error, invalid dol mode\n", __func__); + err = -EINVAL; + goto fail; + } + + priv->last_exposure_long = val; + + shr_dol2 = priv->vmax - + (val * freq / + mode->control_properties.exposure_factor - + IMX274_DOL_MODE_CLOCKS_OFFSET) / + hmax - 1 / 4; + + if (shr_dol2 < rhs1 + min_shr) + shr_dol2 = rhs1 + min_shr; + + if (shr_dol2 > priv->vmax - 4) + shr_dol2 = priv->vmax - 4; + + dev_dbg(dev, + "%s: et:%d, shr_dol2:%d, vmax:%d, Pclk:%lld, LL:%d, HMAX:%d\n", + __func__, + (int)(val * 1000000 / mode->control_properties.exposure_factor), + shr_dol2, + priv->vmax, + mode->signal_properties.pixel_clock.val, + mode->image_properties.line_length, + hmax); + + imx274_get_shr_dol2_regs(reg_list, (u16)shr_dol2); + + for (i = 0; i < 2; i++) { + err = imx274_write_reg(priv->s_data, reg_list[i].addr, + reg_list[i].val); + if (err) + goto fail; + } + return 0; + +fail: + dev_err(dev, "%s: EXPOSURE_SHORT control error\n", __func__); + return err; +} + +static bool imx274_in_dol_mode(const struct imx274 *priv) +{ + const struct camera_common_data *s_data = priv->s_data; + + switch (s_data->mode) { + case IMX274_MODE_3840X2160_DOL_30FPS: + case IMX274_MODE_1920X1080_DOL_60FPS: + return true; + default: + return false; + } +} + +static int imx274_set_frame_rate(struct tegracam_device *tc_dev, s64 val) +{ + struct camera_common_data *s_data = tc_dev->s_data; + struct imx274 *priv = (struct imx274 *)tc_dev->priv; + struct device *dev = tc_dev->dev; + const struct sensor_mode_properties *mode = + &s_data->sensor_props.sensor_modes[s_data->mode]; + struct v4l2_control control; + int hdr_en; + imx274_reg reg_list[3]; + int err; + int i = 0; + u8 svr; + u64 freq = IMX274_SENSOR_INTERNAL_CLK_FREQ; + u32 vmax; + u32 min_vmax; + + dev_dbg(dev, "%s: val: %lld\n", __func__, val); + + err = imx274_read_reg(priv->s_data, IMX274_SVR_ADDR, &svr); + if (err) + goto fail; + + if (s_data->mode == IMX274_MODE_3840X2160_DOL_30FPS) { + vmax = IMX274_DOL_4K_MODE_HMAX; + min_vmax = IMX274_DOL_4K_MODE_MIN_VMAX; + } else if (s_data->mode == IMX274_MODE_1920X1080_DOL_60FPS) { + vmax = IMX274_DOL_1080P_MODE_HMAX; + min_vmax = IMX274_DOL_1080P_MODE_MIN_VMAX; + } else if (s_data->mode == IMX274_MODE_1920X1080) { + vmax = IMX274_1080P_MODE_HMAX; + min_vmax = IMX274_1080P_MODE_MIN_VMAX; + } else { + vmax = IMX274_4K_MODE_HMAX; + min_vmax = IMX274_4K_MODE_MIN_VMAX; + } + + priv->vmax = (u32)(freq * mode->control_properties.framerate_factor / + (val * vmax)); + + if (priv->vmax < min_vmax) + priv->vmax = min_vmax; + + imx274_get_vmax_regs(reg_list, priv->vmax); + + for (i = 0; i < 3; i++) { + err = imx274_write_reg(priv->s_data, reg_list[i].addr, + reg_list[i].val); + if (err) + goto fail; + } + + dev_dbg(dev, "%s: PCLK:%lld, LL:%d, fps:%lld, VMAX:%d\n", __func__, + mode->signal_properties.pixel_clock.val, + mode->image_properties.line_length, + val / mode->control_properties.framerate_factor, + priv->vmax); + + control.id = TEGRA_CAMERA_CID_HDR_EN; + err = camera_common_g_ctrl(priv->s_data, &control); + if (err < 0) { + dev_err(dev, "could not find device ctrl.\n"); + return err; + } + + hdr_en = switch_ctrl_qmenu[control.value]; + if ((hdr_en == SWITCH_ON && imx274_in_dol_mode(priv)) + && (priv->last_exposure_long != 0)) { + err = imx274_set_exposure_shr_dol_long(tc_dev, + priv->last_exposure_long); + if (err) + dev_err(dev, "%s: error exposure time dol long\n", + __func__); + + err = imx274_set_exposure_shr_dol_short(tc_dev, + priv->last_exposure_short); + if (err) + dev_err(dev, "%s: error exposure time dol short\n", + __func__); + } + + return err; + +fail: + dev_err(dev, "%s: FRAME_RATE control error\n", __func__); + return err; +} + +static +u16 imx274_calculate_exposure_shr(struct tegracam_device *tc_dev, s64 val) +{ + struct camera_common_data *s_data = tc_dev->s_data; + struct imx274 *priv = (struct imx274 *)tc_dev->priv; + struct device *dev = tc_dev->dev; + const struct sensor_mode_properties *mode = + &s_data->sensor_props.sensor_modes[s_data->mode]; + u8 svr; + u16 shr; + u64 freq = IMX274_SENSOR_INTERNAL_CLK_FREQ; + + dev_dbg(dev, "%s: val: %lld\n", __func__, val); + + imx274_read_reg(priv->s_data, IMX274_SVR_ADDR, &svr); + + if (s_data->mode == IMX274_MODE_1920X1080) { + shr = priv->vmax - + (u32) (val * freq / + mode->control_properties.exposure_factor - + IMX274_1080P_MODE_OFFSET) / + IMX274_1080P_MODE_HMAX; + + if (shr > priv->vmax - 4) + shr = priv->vmax - 4; + if (shr < 8) + shr = 8; + } else { + + shr = priv->vmax - + (u32) (val * freq / + mode->control_properties.exposure_factor - + IMX274_4K_MODE_OFFSET) / + IMX274_4K_MODE_HMAX; + + if (shr < 12) + shr = 12; + + if (shr > priv->vmax - 4) + shr = priv->vmax - 4; + } + + dev_dbg(dev, "%s: shr: %u vmax: %d\n", __func__, shr, priv->vmax); + return shr; +} + +static int imx274_set_exposure(struct tegracam_device *tc_dev, s64 val) +{ + struct imx274 *priv = (struct imx274 *)tc_dev->priv; + struct device *dev = tc_dev->dev; + struct v4l2_control control; + int hdr_en; + int err; + + dev_dbg(dev, "%s: val: %lld\n", __func__, val); + + control.id = TEGRA_CAMERA_CID_HDR_EN; + err = camera_common_g_ctrl(priv->s_data, &control); + if (err < 0) { + dev_err(dev, "could not find device ctrl.\n"); + return err; + } + + hdr_en = switch_ctrl_qmenu[control.value]; + + if (hdr_en == SWITCH_ON && imx274_in_dol_mode(priv)) { + err = imx274_set_exposure_shr_dol_long(tc_dev, val); + if (err) + dev_err(dev, + "%s: error exposure time dol long override\n", __func__); + } else { + err = imx274_set_exposure_shr(tc_dev, val); + if (err) + dev_err(dev, + "%s: error exposure time SHR override\n", __func__); + } + return err; +} + +static int imx274_set_exposure_shr(struct tegracam_device *tc_dev, s64 val) +{ + struct imx274 *priv = (struct imx274 *)tc_dev->priv; + struct device *dev = tc_dev->dev; + imx274_reg reg_list[2]; + int err; + u16 shr; + int i = 0; + + dev_dbg(dev, "%s: val: %lld\n", __func__, val); + + shr = imx274_calculate_exposure_shr(tc_dev, val); + + imx274_get_shr_regs(reg_list, shr); + + for (i = 0; i < 2; i++) { + err = imx274_write_reg(priv->s_data, reg_list[i].addr, + reg_list[i].val); + if (err) + goto fail; + } + + return 0; + +fail: + dev_err(dev, "%s: Exposure control error\n", __func__); + return err; +} + +static int imx274_fill_string_ctrl(struct tegracam_device *tc_dev, + struct v4l2_ctrl *ctrl) +{ + struct imx274 *priv = (struct imx274 *)tc_dev->priv; + int i, ret; + + switch (ctrl->id) { + case TEGRA_CAMERA_CID_FUSE_ID: + for (i = 0; i < IMX274_FUSE_ID_SIZE; i++) { + ret = sprintf(&ctrl->p_new.p_char[i*2], "%02x", + priv->fuse_id[i]); + if (ret < 0) + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + ctrl->p_cur.p_char = ctrl->p_new.p_char; + + return 0; +} + +static struct tegracam_ctrl_ops imx274_ctrl_ops = { + .numctrls = ARRAY_SIZE(ctrl_cid_list), + .ctrl_cid_list = ctrl_cid_list, + .string_ctrl_size = {0, IMX274_FUSE_ID_STR_SIZE}, + .set_gain = imx274_set_gain, + .set_exposure = imx274_set_exposure, + .set_exposure_short = imx274_set_exposure_shr_dol_short, + .set_frame_rate = imx274_set_frame_rate, + .set_group_hold = imx274_set_group_hold, + .fill_string_ctrl = imx274_fill_string_ctrl, +}; + +static int imx274_power_on(struct camera_common_data *s_data) +{ + int err = 0; + struct camera_common_power_rail *pw = s_data->power; + struct camera_common_pdata *pdata = s_data->pdata; + struct device *dev = s_data->dev; + + dev_dbg(dev, "%s: power on\n", __func__); + + if (pdata && pdata->power_on) { + err = pdata->power_on(pw); + if (err) + dev_err(dev, "%s failed.\n", __func__); + else + pw->state = SWITCH_ON; + return err; + } + + if (pw->reset_gpio) + gpio_set_value(pw->reset_gpio, 0); + if (pw->af_gpio) + gpio_set_value(pw->af_gpio, 1); + if (pw->pwdn_gpio) + gpio_set_value(pw->pwdn_gpio, 0); + usleep_range(10, 20); + + if (pw->dvdd) + err = regulator_enable(pw->dvdd); + if (err) + goto imx274_dvdd_fail; + + if (pw->iovdd) + err = regulator_enable(pw->iovdd); + if (err) + goto imx274_iovdd_fail; + + if (pw->avdd) + err = regulator_enable(pw->avdd); + if (err) + goto imx274_avdd_fail; + + usleep_range(1, 2); + /* Added latency due to AVDD setup time on McCoy */ + if (pdata && pdata->avdd_latency) + usleep_range(pdata->avdd_latency, pdata->avdd_latency + 10); + + if (pw->reset_gpio) + gpio_set_value(pw->reset_gpio, 1); + if (pw->pwdn_gpio) + gpio_set_value(pw->pwdn_gpio, 1); + + /* 1.2v input is generated on module board, adds more latency */ + usleep_range(10000, 10010); + + pw->state = SWITCH_ON; + return 0; + +imx274_avdd_fail: + if (pw->iovdd) + regulator_disable(pw->iovdd); + +imx274_iovdd_fail: + if (pw->dvdd) + regulator_disable(pw->dvdd); + +imx274_dvdd_fail: + if (pw->af_gpio) + gpio_set_value(pw->af_gpio, 0); + + dev_err(dev, "%s failed.\n", __func__); + return -ENODEV; +} + +static int imx274_power_off(struct camera_common_data *s_data) +{ + int err = 0; + struct camera_common_power_rail *pw = s_data->power; + struct camera_common_pdata *pdata = s_data->pdata; + struct device *dev = s_data->dev; + + dev_dbg(dev, "%s: power off\n", __func__); + + if (pdata && pdata->power_off) { + err = pdata->power_off(pw); + if (err) { + dev_err(dev, "%s failed.\n", __func__); + return err; + } + goto power_off_done; + } + + usleep_range(1, 2); + if (pw->reset_gpio) + gpio_set_value(pw->reset_gpio, 0); + if (pw->af_gpio) + gpio_set_value(pw->af_gpio, 0); + if (pw->pwdn_gpio) + gpio_set_value(pw->pwdn_gpio, 0); + usleep_range(1, 2); + + if (pw->avdd) + regulator_disable(pw->avdd); + if (pw->iovdd) + regulator_disable(pw->iovdd); + if (pw->dvdd) + regulator_disable(pw->dvdd); + +power_off_done: + pw->state = SWITCH_OFF; + return 0; +} + +static int imx274_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->avdd)) + devm_regulator_put(pw->avdd); + if (likely(pw->dvdd)) + devm_regulator_put(pw->dvdd); + if (likely(pw->iovdd)) + devm_regulator_put(pw->iovdd); + + pw->avdd = NULL; + pw->dvdd = NULL; + pw->iovdd = NULL; + + gpio_free(pw->pwdn_gpio); + gpio_free(pw->reset_gpio); + gpio_free(pw->af_gpio); + + return 0; +} + +static int imx274_power_get(struct tegracam_device *tc_dev) +{ + struct device *dev = tc_dev->dev; + struct camera_common_data *s_data = tc_dev->s_data; + struct camera_common_power_rail *pw = s_data->power; + struct camera_common_pdata *pdata = s_data->pdata; + const char *mclk_name; + const char *parentclk_name; + struct clk *parent; + int err = 0, ret = 0; + + mclk_name = pdata->mclk_name ? + pdata->mclk_name : "cam_mclk1"; + pw->mclk = devm_clk_get(dev, mclk_name); + if (IS_ERR(pw->mclk)) { + dev_err(dev, "unable to get clock %s\n", mclk_name); + return PTR_ERR(pw->mclk); + } + + parentclk_name = pdata->parentclk_name; + if (parentclk_name) { + parent = devm_clk_get(dev, parentclk_name); + if (IS_ERR(parent)) { + dev_err(dev, "unable to get parent clcok %s", + parentclk_name); + } else + clk_set_parent(pw->mclk, parent); + } + + /* ananlog 2.7v */ + err |= camera_common_regulator_get(dev, + &pw->avdd, pdata->regulators.avdd); + /* IO 1.8v */ + err |= camera_common_regulator_get(dev, + &pw->iovdd, pdata->regulators.iovdd); + /* digital 1.2v, not all imx274 modules draw this from CVB */ + if (pdata->regulators.dvdd != NULL) + err |= camera_common_regulator_get(dev, + &pw->dvdd, pdata->regulators.dvdd); + + if (!err) { + pw->reset_gpio = pdata->reset_gpio; + pw->af_gpio = pdata->af_gpio; + pw->pwdn_gpio = pdata->pwdn_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); + + ret = gpio_request(pw->af_gpio, "cam_af_gpio"); + if (ret < 0) + dev_dbg(dev, "%s can't request af_gpio %d\n", __func__, ret); + gpio_direction_output(pw->af_gpio, 1); + + ret = gpio_request(pw->pwdn_gpio, "cam_pwdn_gpio"); + if (ret < 0) + dev_dbg(dev, "%s can't request pwdn_gpio %d\n", __func__, ret); + gpio_direction_output(pw->pwdn_gpio, 1); + + pw->state = SWITCH_OFF; + return err; +} + +static +struct camera_common_pdata *imx274_parse_dt(struct tegracam_device *tc_dev) +{ + struct device *dev = tc_dev->dev; + struct device_node *node = dev->of_node; + struct camera_common_pdata *board_priv_pdata; + const struct of_device_id *match; + int err; + + if (!node) + return NULL; + + match = of_match_device(imx274_of_match, dev); + if (!match) { + dev_err(dev, "Failed to find matching dt id\n"); + return NULL; + } + + board_priv_pdata = devm_kzalloc(dev, + sizeof(*board_priv_pdata), GFP_KERNEL); + if (!board_priv_pdata) + return NULL; + + err = of_property_read_string(node, "mclk", + &board_priv_pdata->mclk_name); + if (err) + dev_err(dev, "mclk not in DT\n"); + + board_priv_pdata->reset_gpio = of_get_named_gpio(node, + "reset-gpios", 0); + + err = of_property_read_string(node, "avdd-reg", + &board_priv_pdata->regulators.avdd); + err |= of_property_read_string(node, "dvdd-reg", + &board_priv_pdata->regulators.dvdd); + err |= of_property_read_string(node, "iovdd-reg", + &board_priv_pdata->regulators.iovdd); + if (err) + dev_dbg(dev, "avdd, iovdd or dvdd reglrs. not present, ignoring."); + + + board_priv_pdata->has_eeprom = + of_property_read_bool(node, "has-eeprom"); + + err = of_property_read_u32(node, "fuse_id_start_addr", + &board_priv_pdata->fuse_id_addr); + if (err) + board_priv_pdata->fuse_id_addr = 0; + + err = of_property_read_u32(node, "avdd-setup-delay", + &board_priv_pdata->avdd_latency); + if (err) + board_priv_pdata->avdd_latency = 0; + + return board_priv_pdata; +} + + +static int imx274_set_mode(struct tegracam_device *tc_dev) +{ + struct imx274 *priv = (struct imx274 *)tegracam_get_privdata(tc_dev); + struct camera_common_data *s_data = tc_dev->s_data; + struct device *dev = s_data->dev; + int err; + + if (s_data->mode < 0) + return -EINVAL; + + err = imx274_write_table(priv, mode_table[s_data->mode]); + if (err) + return err; + + if (test_mode) { + err = imx274_write_table(priv, + mode_table[IMX274_MODE_TEST_PATTERN]); + if (err) + goto exit; + } + + return 0; + +exit: + dev_err(dev, "%s: error setting mode\n", __func__); + return err; +} + +static int imx274_start_streaming(struct tegracam_device *tc_dev) +{ + struct imx274 *priv = (struct imx274 *)tegracam_get_privdata(tc_dev); + struct camera_common_data *s_data = tc_dev->s_data; + struct device *dev = s_data->dev; + int err; + + mutex_lock(&priv->streaming_lock); + err = imx274_write_table(priv, mode_table[IMX274_MODE_START_STREAM]); + if (err) { + mutex_unlock(&priv->streaming_lock); + goto exit; + } else { + priv->streaming = true; + mutex_unlock(&priv->streaming_lock); + } + + return 0; + +exit: + dev_err(dev, "%s: error starting stream\n", __func__); + return err; +} + +static int imx274_stop_streaming(struct tegracam_device *tc_dev) +{ + struct imx274 *priv = (struct imx274 *)tegracam_get_privdata(tc_dev); + struct camera_common_data *s_data = tc_dev->s_data; + struct device *dev = s_data->dev; + int err; + + mutex_lock(&priv->streaming_lock); + err = imx274_write_table(priv, mode_table[IMX274_MODE_STOP_STREAM]); + if (err) { + mutex_unlock(&priv->streaming_lock); + goto exit; + } else { + priv->streaming = false; + mutex_unlock(&priv->streaming_lock); + } + + return 0; + +exit: + dev_err(dev, "%s: error stopping stream\n", __func__); + return err; +} + +static struct camera_common_sensor_ops imx274_common_ops = { + .numfrmfmts = ARRAY_SIZE(imx274_frmfmt), + .frmfmt_table = imx274_frmfmt, + .power_on = imx274_power_on, + .power_off = imx274_power_off, + .write_reg = imx274_write_reg, + .read_reg = imx274_read_reg, + .parse_dt = imx274_parse_dt, + .power_get = imx274_power_get, + .power_put = imx274_power_put, + .set_mode = imx274_set_mode, + .start_streaming = imx274_start_streaming, + .stop_streaming = imx274_stop_streaming, +}; + +static int imx274_eeprom_device_release(struct imx274 *priv) +{ + int i; + + for (i = 0; i < IMX274_EEPROM_NUM_BLOCKS; i++) { + if (priv->eeprom[i].i2c_client != NULL) { + i2c_unregister_device(priv->eeprom[i].i2c_client); + priv->eeprom[i].i2c_client = NULL; + } + } + + return 0; +} + +static int imx274_eeprom_device_init(struct imx274 *priv) +{ + char *dev_name = "eeprom_imx274"; + struct camera_common_pdata *pdata = priv->s_data->pdata; + static struct regmap_config eeprom_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + }; + int i; + int err; + + if (!pdata || !pdata->has_eeprom) + return -EINVAL; + + for (i = 0; i < IMX274_EEPROM_NUM_BLOCKS; i++) { + priv->eeprom[i].adap = i2c_get_adapter( + priv->i2c_client->adapter->nr); + memset(&priv->eeprom[i].brd, 0, sizeof(priv->eeprom[i].brd)); + strscpy(priv->eeprom[i].brd.type, dev_name, + sizeof(priv->eeprom[i].brd.type)); + priv->eeprom[i].brd.addr = IMX274_EEPROM_ADDRESS + i; + priv->eeprom[i].i2c_client = i2c_new_client_device( + priv->eeprom[i].adap, &priv->eeprom[i].brd); + + priv->eeprom[i].regmap = devm_regmap_init_i2c( + priv->eeprom[i].i2c_client, &eeprom_regmap_config); + if (IS_ERR(priv->eeprom[i].regmap)) { + err = PTR_ERR(priv->eeprom[i].regmap); + imx274_eeprom_device_release(priv); + return err; + } + } + + return 0; +} + +static int imx274_read_fuse_id(struct imx274 *priv) +{ + struct camera_common_pdata *pdata = priv->s_data->pdata; + int err, i; + + if (!pdata->has_eeprom) + return -EINVAL; + + for (i = 0; i < IMX274_EEPROM_NUM_BLOCKS; i++) { + err = regmap_bulk_read(priv->eeprom[i].regmap, 0, + &priv->eeprom_buf[i * IMX274_EEPROM_BLOCK_SIZE], + IMX274_EEPROM_BLOCK_SIZE); + if (err) + return err; + } + + for (i = 0; i < IMX274_FUSE_ID_SIZE; i++) + priv->fuse_id[i] = + priv->eeprom_buf[i + pdata->fuse_id_addr]; + + return 0; +} + +static int imx274_board_setup(struct imx274 *priv) +{ + struct camera_common_data *s_data = priv->s_data; + struct device *dev = s_data->dev; + int err = 0; + + if (!s_data->pdata->has_eeprom) + return 0; + + /* eeprom for fuse id*/ + err = imx274_eeprom_device_init(priv); + if (err && s_data->pdata->has_eeprom) { + dev_err(dev, + "Failed to allocate eeprom reg map: %d\n", err); + return err; + } + + err = camera_common_mclk_enable(s_data); + if (err) { + dev_err(dev, + "Error %d turning on mclk\n", err); + return err; + } + + err = imx274_power_on(s_data); + if (err) { + dev_err(dev, + "Error %d during power on sensor\n", err); + return err; + } + + err = imx274_read_fuse_id(priv); + if (err) + dev_err(dev, + "Error %d reading fuse id data\n", err); + + imx274_power_off(s_data); + camera_common_mclk_disable(s_data); + return err; +} + +static int imx274_debugfs_streaming_show(void *data, u64 *val) +{ + struct imx274 *priv = data; + + mutex_lock(&priv->streaming_lock); + *val = priv->streaming; + mutex_unlock(&priv->streaming_lock); + + return 0; +} + +static int imx274_debugfs_streaming_write(void *data, u64 val) +{ + int err = 0; + struct imx274 *priv = data; + struct i2c_client *client = priv->i2c_client; + bool enable = (val != 0); + int mode_index = enable ? + (IMX274_MODE_START_STREAM) : (IMX274_MODE_STOP_STREAM); + + dev_info(&client->dev, "%s: %s sensor\n", + __func__, (enable ? "enabling" : "disabling")); + + mutex_lock(&priv->streaming_lock); + + err = imx274_write_table(priv, mode_table[mode_index]); + if (err) { + dev_err(&client->dev, "%s: error setting sensor streaming\n", + __func__); + goto done; + } + + priv->streaming = enable; + +done: + mutex_unlock(&priv->streaming_lock); + + return err; +} + +DEFINE_SIMPLE_ATTRIBUTE(imx274_debugfs_streaming_fops, + imx274_debugfs_streaming_show, + imx274_debugfs_streaming_write, + "%lld\n"); + +static void imx274_debugfs_remove(struct imx274 *priv); + +static int imx274_debugfs_create(struct imx274 *priv) +{ + int err = 0; + struct i2c_client *client = priv->i2c_client; + const char *devnode; + char debugfs_dir[16]; + + err = of_property_read_string(client->dev.of_node, "devnode", &devnode); + if (err) { + dev_err(&client->dev, "devnode not in DT\n"); + return err; + } + err = snprintf(debugfs_dir, sizeof(debugfs_dir), "camera-%s", devnode); + if (err < 0) + return -EINVAL; + + priv->debugfs_dir = debugfs_create_dir(debugfs_dir, NULL); + if (priv->debugfs_dir == NULL) + return -ENOMEM; + + if (!debugfs_create_file("streaming", 0644, priv->debugfs_dir, priv, + &imx274_debugfs_streaming_fops)) + goto error; + + return 0; + +error: + imx274_debugfs_remove(priv); + + return -ENOMEM; +} + +static void imx274_debugfs_remove(struct imx274 *priv) +{ + debugfs_remove_recursive(priv->debugfs_dir); + priv->debugfs_dir = NULL; +} + +static int imx274_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 imx274_subdev_internal_ops = { + .open = imx274_open, +}; + +static int imx274_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = client->dev.of_node; + struct tegracam_device *tc_dev; + struct imx274 *priv; + int err; + + dev_info(dev, "probing v4l2 sensor.\n"); + + if (!IS_ENABLED(CONFIG_OF) || !node) + return -EINVAL; + + priv = devm_kzalloc(dev, + sizeof(struct imx274), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + tc_dev = devm_kzalloc(dev, + sizeof(struct tegracam_device), GFP_KERNEL); + if (!tc_dev) + return -ENOMEM; + + priv->i2c_client = tc_dev->client = client; + tc_dev->dev = dev; + strncpy(tc_dev->name, "imx274", sizeof(tc_dev->name)); + tc_dev->dev_regmap_config = &sensor_regmap_config; + tc_dev->sensor_ops = &imx274_common_ops; + tc_dev->v4l2sd_internal_ops = &imx274_subdev_internal_ops; + tc_dev->tcctrl_ops = &imx274_ctrl_ops; + + mutex_init(&priv->streaming_lock); + + err = tegracam_device_register(tc_dev); + if (err) { + dev_err(dev, "tegra camera driver registration failed\n"); + return err; + } + priv->tc_dev = tc_dev; + priv->s_data = tc_dev->s_data; + priv->subdev = &tc_dev->s_data->subdev; + tegracam_set_privdata(tc_dev, (void *)priv); + + err = imx274_board_setup(priv); + if (err) { + tegracam_device_unregister(tc_dev); + dev_err(dev, "board setup failed\n"); + return err; + } + + err = tegracam_v4l2subdev_register(tc_dev, true); + if (err) { + dev_err(dev, "tegra camera subdev registration failed\n"); + return err; + } + + err = imx274_debugfs_create(priv); + if (err) { + dev_err(&client->dev, "error creating debugfs interface"); + imx274_debugfs_remove(priv); + return err; + } + + dev_info(dev, "Detected IMX274 sensor\n"); + + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) +static void imx274_remove(struct i2c_client *client) +#else +static int imx274_remove(struct i2c_client *client) +#endif +{ + struct camera_common_data *s_data = to_camera_common_data(&client->dev); + struct imx274 *priv = (struct imx274 *)s_data->priv; + + imx274_debugfs_remove(priv); + + tegracam_v4l2subdev_unregister(priv->tc_dev); + tegracam_device_unregister(priv->tc_dev); + imx274_eeprom_device_release(priv); + + mutex_destroy(&priv->streaming_lock); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) + return 0; +#endif +} + +static const struct i2c_device_id imx274_id[] = { + { "imx274", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, imx274_id); + +static struct i2c_driver imx274_i2c_driver = { + .driver = { + .name = "imx274", + .owner = THIS_MODULE, + }, + .probe = imx274_probe, + .remove = imx274_remove, + .id_table = imx274_id, +}; + +module_i2c_driver(imx274_i2c_driver); + +MODULE_DESCRIPTION("Media Controller driver for Sony IMX274"); +MODULE_AUTHOR("Josh Kuo "); +MODULE_LICENSE("GPL v2"); diff --git a/include/media/imx274.h b/include/media/imx274.h new file mode 100644 index 00000000..a057421a --- /dev/null +++ b/include/media/imx274.h @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016-2023, NVIDIA CORPORATION. All rights reserved. + */ + +#ifndef __IMX274_H__ +#define __IMX274_H__ + +#include + +#define IMX274_SVR_ADDR 0x300E + +#define IMX274_SHR_ADDR_LSB 0x300C +#define IMX274_SHR_ADDR_MSB 0x300D + +#define IMX274_SHR_DOL1_ADDR_LSB 0x302E +#define IMX274_SHR_DOL1_ADDR_MSB 0x302F +#define IMX274_SHR_DOL2_ADDR_LSB 0x3030 +#define IMX274_SHR_DOL2_ADDR_MSB 0x3031 +#define IMX274_RHS1_ADDR_LSB 0x3032 +#define IMX274_RHS1_ADDR_MSB 0x3033 + +#define IMX274_VMAX_ADDR_LSB 0x30F8 +#define IMX274_VMAX_ADDR_MID 0x30F9 +#define IMX274_VMAX_ADDR_MSB 0x30FA + +#define IMX274_ANALOG_GAIN_ADDR_LSB 0x300A +#define IMX274_ANALOG_GAIN_ADDR_MSB 0x300B +#define IMX274_DIGITAL_GAIN_ADDR 0x3012 + +#define IMX274_GROUP_HOLD_ADDR 0x302D + +struct imx274_power_rail { + struct regulator *dvdd; + struct regulator *avdd; + struct regulator *iovdd; + struct regulator *ext_reg1; + struct regulator *ext_reg2; + struct clk *mclk; + unsigned int pwdn_gpio; + unsigned int cam1_gpio; + unsigned int reset_gpio; + unsigned int af_gpio; +}; + +struct imx274_platform_data { + const char *mclk_name; /* NULL for default default_mclk */ + unsigned int cam1_gpio; + unsigned int reset_gpio; + unsigned int af_gpio; + bool ext_reg; + int (*power_on)(struct imx274_power_rail *pw); + int (*power_off)(struct imx274_power_rail *pw); +}; + +#endif /* __IMX274_H__ */ diff --git a/include/uapi/media/imx274.h b/include/uapi/media/imx274.h new file mode 100644 index 00000000..7bf45e05 --- /dev/null +++ b/include/uapi/media/imx274.h @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016-2023, NVIDIA CORPORATION. All rights reserved. + */ + +#ifndef __UAPI_IMX274_H__ +#define __UAPI_IMX274_H__ + +#include +#include + +#define IMX274_IOCTL_SET_MODE _IOW('o', 1, struct imx274_mode) +#define IMX274_IOCTL_GET_STATUS _IOR('o', 2, __u8) +#define IMX274_IOCTL_SET_FRAME_LENGTH _IOW('o', 3, __u32) +#define IMX274_IOCTL_SET_COARSE_TIME _IOW('o', 4, __u32) +#define IMX274_IOCTL_SET_GAIN _IOW('o', 5, __u16) +#define IMX274_IOCTL_GET_SENSORDATA _IOR('o', 6, \ + struct imx274_sensordata) +#define IMX274_IOCTL_SET_GROUP_HOLD _IOW('o', 7, struct imx274_ae) +#define IMX274_IOCTL_SET_HDR_COARSE_TIME _IOW('o', 8, \ + struct imx274_hdr) +#define IMX274_IOCTL_SET_POWER _IOW('o', 20, __u32) + +struct imx274_mode { + __u32 xres; + __u32 yres; + __u32 frame_length; + __u32 coarse_time; + __u32 coarse_time_short; + __u16 gain; + __u8 hdr_en; +}; + +struct imx274_hdr { + __u32 coarse_time_long; + __u32 coarse_time_short; +}; + +struct imx274_ae { + __u32 frame_length; + __u8 frame_length_enable; + __u32 coarse_time; + __u32 coarse_time_short; + __u8 coarse_time_enable; + __s32 gain; + __u8 gain_enable; +}; + +#endif /* __UAPI_IMX274_H__ */