1e4802cb0SJason Chen // SPDX-License-Identifier: GPL-2.0 2e4802cb0SJason Chen // Copyright (C) 2018 Intel Corporation 3e4802cb0SJason Chen 4e4802cb0SJason Chen #include <linux/acpi.h> 5e4802cb0SJason Chen #include <linux/delay.h> 6e4802cb0SJason Chen #include <linux/i2c.h> 7e4802cb0SJason Chen #include <linux/module.h> 8e4802cb0SJason Chen #include <linux/pm_runtime.h> 9e4802cb0SJason Chen #include <media/v4l2-ctrls.h> 10e4802cb0SJason Chen #include <media/v4l2-device.h> 11e4802cb0SJason Chen #include <asm/unaligned.h> 12e4802cb0SJason Chen 13e4802cb0SJason Chen #define IMX258_REG_VALUE_08BIT 1 14e4802cb0SJason Chen #define IMX258_REG_VALUE_16BIT 2 15e4802cb0SJason Chen 16e4802cb0SJason Chen #define IMX258_REG_MODE_SELECT 0x0100 17e4802cb0SJason Chen #define IMX258_MODE_STANDBY 0x00 18e4802cb0SJason Chen #define IMX258_MODE_STREAMING 0x01 19e4802cb0SJason Chen 20e4802cb0SJason Chen /* Chip ID */ 21e4802cb0SJason Chen #define IMX258_REG_CHIP_ID 0x0016 22e4802cb0SJason Chen #define IMX258_CHIP_ID 0x0258 23e4802cb0SJason Chen 24e4802cb0SJason Chen /* V_TIMING internal */ 25e4802cb0SJason Chen #define IMX258_VTS_30FPS 0x0c98 26e4802cb0SJason Chen #define IMX258_VTS_30FPS_2K 0x0638 27e4802cb0SJason Chen #define IMX258_VTS_30FPS_VGA 0x034c 28e4802cb0SJason Chen #define IMX258_VTS_MAX 0xffff 29e4802cb0SJason Chen 30e4802cb0SJason Chen /*Frame Length Line*/ 31e4802cb0SJason Chen #define IMX258_FLL_MIN 0x08a6 32e4802cb0SJason Chen #define IMX258_FLL_MAX 0xffff 33e4802cb0SJason Chen #define IMX258_FLL_STEP 1 34e4802cb0SJason Chen #define IMX258_FLL_DEFAULT 0x0c98 35e4802cb0SJason Chen 36e4802cb0SJason Chen /* HBLANK control - read only */ 37e4802cb0SJason Chen #define IMX258_PPL_DEFAULT 5352 38e4802cb0SJason Chen 39e4802cb0SJason Chen /* Exposure control */ 40e4802cb0SJason Chen #define IMX258_REG_EXPOSURE 0x0202 41e4802cb0SJason Chen #define IMX258_EXPOSURE_MIN 4 42e4802cb0SJason Chen #define IMX258_EXPOSURE_STEP 1 43e4802cb0SJason Chen #define IMX258_EXPOSURE_DEFAULT 0x640 44e4802cb0SJason Chen #define IMX258_EXPOSURE_MAX 65535 45e4802cb0SJason Chen 46e4802cb0SJason Chen /* Analog gain control */ 47e4802cb0SJason Chen #define IMX258_REG_ANALOG_GAIN 0x0204 48e4802cb0SJason Chen #define IMX258_ANA_GAIN_MIN 0 49e4802cb0SJason Chen #define IMX258_ANA_GAIN_MAX 0x1fff 50e4802cb0SJason Chen #define IMX258_ANA_GAIN_STEP 1 51e4802cb0SJason Chen #define IMX258_ANA_GAIN_DEFAULT 0x0 52e4802cb0SJason Chen 53e4802cb0SJason Chen /* Digital gain control */ 54e4802cb0SJason Chen #define IMX258_REG_GR_DIGITAL_GAIN 0x020e 55e4802cb0SJason Chen #define IMX258_REG_R_DIGITAL_GAIN 0x0210 56e4802cb0SJason Chen #define IMX258_REG_B_DIGITAL_GAIN 0x0212 57e4802cb0SJason Chen #define IMX258_REG_GB_DIGITAL_GAIN 0x0214 58e4802cb0SJason Chen #define IMX258_DGTL_GAIN_MIN 0 59e4802cb0SJason Chen #define IMX258_DGTL_GAIN_MAX 4096 /* Max = 0xFFF */ 60e4802cb0SJason Chen #define IMX258_DGTL_GAIN_DEFAULT 1024 61e4802cb0SJason Chen #define IMX258_DGTL_GAIN_STEP 1 62e4802cb0SJason Chen 63e4802cb0SJason Chen /* Test Pattern Control */ 64e4802cb0SJason Chen #define IMX258_REG_TEST_PATTERN 0x0600 65e4802cb0SJason Chen 66e4802cb0SJason Chen /* Orientation */ 67e4802cb0SJason Chen #define REG_MIRROR_FLIP_CONTROL 0x0101 68e4802cb0SJason Chen #define REG_CONFIG_MIRROR_FLIP 0x03 69e4802cb0SJason Chen #define REG_CONFIG_FLIP_TEST_PATTERN 0x02 70e4802cb0SJason Chen 71e4802cb0SJason Chen struct imx258_reg { 72e4802cb0SJason Chen u16 address; 73e4802cb0SJason Chen u8 val; 74e4802cb0SJason Chen }; 75e4802cb0SJason Chen 76e4802cb0SJason Chen struct imx258_reg_list { 77e4802cb0SJason Chen u32 num_of_regs; 78e4802cb0SJason Chen const struct imx258_reg *regs; 79e4802cb0SJason Chen }; 80e4802cb0SJason Chen 81e4802cb0SJason Chen /* Link frequency config */ 82e4802cb0SJason Chen struct imx258_link_freq_config { 83e4802cb0SJason Chen u32 pixels_per_line; 84e4802cb0SJason Chen 85e4802cb0SJason Chen /* PLL registers for this link frequency */ 86e4802cb0SJason Chen struct imx258_reg_list reg_list; 87e4802cb0SJason Chen }; 88e4802cb0SJason Chen 89e4802cb0SJason Chen /* Mode : resolution and related config&values */ 90e4802cb0SJason Chen struct imx258_mode { 91e4802cb0SJason Chen /* Frame width */ 92e4802cb0SJason Chen u32 width; 93e4802cb0SJason Chen /* Frame height */ 94e4802cb0SJason Chen u32 height; 95e4802cb0SJason Chen 96e4802cb0SJason Chen /* V-timing */ 97e4802cb0SJason Chen u32 vts_def; 98e4802cb0SJason Chen u32 vts_min; 99e4802cb0SJason Chen 100e4802cb0SJason Chen /* Index of Link frequency config to be used */ 101e4802cb0SJason Chen u32 link_freq_index; 102e4802cb0SJason Chen /* Default register values */ 103e4802cb0SJason Chen struct imx258_reg_list reg_list; 104e4802cb0SJason Chen }; 105e4802cb0SJason Chen 106e4802cb0SJason Chen /* 4208x3118 needs 1267Mbps/lane, 4 lanes */ 107e4802cb0SJason Chen static const struct imx258_reg mipi_data_rate_1267mbps[] = { 108e4802cb0SJason Chen { 0x0301, 0x05 }, 109e4802cb0SJason Chen { 0x0303, 0x02 }, 110e4802cb0SJason Chen { 0x0305, 0x03 }, 111e4802cb0SJason Chen { 0x0306, 0x00 }, 112e4802cb0SJason Chen { 0x0307, 0xC6 }, 113e4802cb0SJason Chen { 0x0309, 0x0A }, 114e4802cb0SJason Chen { 0x030B, 0x01 }, 115e4802cb0SJason Chen { 0x030D, 0x02 }, 116e4802cb0SJason Chen { 0x030E, 0x00 }, 117e4802cb0SJason Chen { 0x030F, 0xD8 }, 118e4802cb0SJason Chen { 0x0310, 0x00 }, 119e4802cb0SJason Chen { 0x0820, 0x13 }, 120e4802cb0SJason Chen { 0x0821, 0x4C }, 121e4802cb0SJason Chen { 0x0822, 0xCC }, 122e4802cb0SJason Chen { 0x0823, 0xCC }, 123e4802cb0SJason Chen }; 124e4802cb0SJason Chen 125e4802cb0SJason Chen static const struct imx258_reg mipi_data_rate_640mbps[] = { 126e4802cb0SJason Chen { 0x0301, 0x05 }, 127e4802cb0SJason Chen { 0x0303, 0x02 }, 128e4802cb0SJason Chen { 0x0305, 0x03 }, 129e4802cb0SJason Chen { 0x0306, 0x00 }, 130e4802cb0SJason Chen { 0x0307, 0x64 }, 131e4802cb0SJason Chen { 0x0309, 0x0A }, 132e4802cb0SJason Chen { 0x030B, 0x01 }, 133e4802cb0SJason Chen { 0x030D, 0x02 }, 134e4802cb0SJason Chen { 0x030E, 0x00 }, 135e4802cb0SJason Chen { 0x030F, 0xD8 }, 136e4802cb0SJason Chen { 0x0310, 0x00 }, 137e4802cb0SJason Chen { 0x0820, 0x0A }, 138e4802cb0SJason Chen { 0x0821, 0x00 }, 139e4802cb0SJason Chen { 0x0822, 0x00 }, 140e4802cb0SJason Chen { 0x0823, 0x00 }, 141e4802cb0SJason Chen }; 142e4802cb0SJason Chen 143e4802cb0SJason Chen static const struct imx258_reg mode_4208x3118_regs[] = { 144e4802cb0SJason Chen { 0x0136, 0x13 }, 145e4802cb0SJason Chen { 0x0137, 0x33 }, 146e4802cb0SJason Chen { 0x3051, 0x00 }, 147e4802cb0SJason Chen { 0x3052, 0x00 }, 148e4802cb0SJason Chen { 0x4E21, 0x14 }, 149e4802cb0SJason Chen { 0x6B11, 0xCF }, 150e4802cb0SJason Chen { 0x7FF0, 0x08 }, 151e4802cb0SJason Chen { 0x7FF1, 0x0F }, 152e4802cb0SJason Chen { 0x7FF2, 0x08 }, 153e4802cb0SJason Chen { 0x7FF3, 0x1B }, 154e4802cb0SJason Chen { 0x7FF4, 0x23 }, 155e4802cb0SJason Chen { 0x7FF5, 0x60 }, 156e4802cb0SJason Chen { 0x7FF6, 0x00 }, 157e4802cb0SJason Chen { 0x7FF7, 0x01 }, 158e4802cb0SJason Chen { 0x7FF8, 0x00 }, 159e4802cb0SJason Chen { 0x7FF9, 0x78 }, 160e4802cb0SJason Chen { 0x7FFA, 0x00 }, 161e4802cb0SJason Chen { 0x7FFB, 0x00 }, 162e4802cb0SJason Chen { 0x7FFC, 0x00 }, 163e4802cb0SJason Chen { 0x7FFD, 0x00 }, 164e4802cb0SJason Chen { 0x7FFE, 0x00 }, 165e4802cb0SJason Chen { 0x7FFF, 0x03 }, 166e4802cb0SJason Chen { 0x7F76, 0x03 }, 167e4802cb0SJason Chen { 0x7F77, 0xFE }, 168e4802cb0SJason Chen { 0x7FA8, 0x03 }, 169e4802cb0SJason Chen { 0x7FA9, 0xFE }, 170e4802cb0SJason Chen { 0x7B24, 0x81 }, 171e4802cb0SJason Chen { 0x7B25, 0x00 }, 172e4802cb0SJason Chen { 0x6564, 0x07 }, 173e4802cb0SJason Chen { 0x6B0D, 0x41 }, 174e4802cb0SJason Chen { 0x653D, 0x04 }, 175e4802cb0SJason Chen { 0x6B05, 0x8C }, 176e4802cb0SJason Chen { 0x6B06, 0xF9 }, 177e4802cb0SJason Chen { 0x6B08, 0x65 }, 178e4802cb0SJason Chen { 0x6B09, 0xFC }, 179e4802cb0SJason Chen { 0x6B0A, 0xCF }, 180e4802cb0SJason Chen { 0x6B0B, 0xD2 }, 181e4802cb0SJason Chen { 0x6700, 0x0E }, 182e4802cb0SJason Chen { 0x6707, 0x0E }, 183e4802cb0SJason Chen { 0x9104, 0x00 }, 184e4802cb0SJason Chen { 0x4648, 0x7F }, 185e4802cb0SJason Chen { 0x7420, 0x00 }, 186e4802cb0SJason Chen { 0x7421, 0x1C }, 187e4802cb0SJason Chen { 0x7422, 0x00 }, 188e4802cb0SJason Chen { 0x7423, 0xD7 }, 189e4802cb0SJason Chen { 0x5F04, 0x00 }, 190e4802cb0SJason Chen { 0x5F05, 0xED }, 191e4802cb0SJason Chen { 0x0112, 0x0A }, 192e4802cb0SJason Chen { 0x0113, 0x0A }, 193e4802cb0SJason Chen { 0x0114, 0x03 }, 194e4802cb0SJason Chen { 0x0342, 0x14 }, 195e4802cb0SJason Chen { 0x0343, 0xE8 }, 196e4802cb0SJason Chen { 0x0340, 0x0C }, 197e4802cb0SJason Chen { 0x0341, 0x50 }, 198e4802cb0SJason Chen { 0x0344, 0x00 }, 199e4802cb0SJason Chen { 0x0345, 0x00 }, 200e4802cb0SJason Chen { 0x0346, 0x00 }, 201e4802cb0SJason Chen { 0x0347, 0x00 }, 202e4802cb0SJason Chen { 0x0348, 0x10 }, 203e4802cb0SJason Chen { 0x0349, 0x6F }, 204e4802cb0SJason Chen { 0x034A, 0x0C }, 205e4802cb0SJason Chen { 0x034B, 0x2E }, 206e4802cb0SJason Chen { 0x0381, 0x01 }, 207e4802cb0SJason Chen { 0x0383, 0x01 }, 208e4802cb0SJason Chen { 0x0385, 0x01 }, 209e4802cb0SJason Chen { 0x0387, 0x01 }, 210e4802cb0SJason Chen { 0x0900, 0x00 }, 211e4802cb0SJason Chen { 0x0901, 0x11 }, 212e4802cb0SJason Chen { 0x0401, 0x00 }, 213e4802cb0SJason Chen { 0x0404, 0x00 }, 214e4802cb0SJason Chen { 0x0405, 0x10 }, 215e4802cb0SJason Chen { 0x0408, 0x00 }, 216e4802cb0SJason Chen { 0x0409, 0x00 }, 217e4802cb0SJason Chen { 0x040A, 0x00 }, 218e4802cb0SJason Chen { 0x040B, 0x00 }, 219e4802cb0SJason Chen { 0x040C, 0x10 }, 220e4802cb0SJason Chen { 0x040D, 0x70 }, 221e4802cb0SJason Chen { 0x040E, 0x0C }, 222e4802cb0SJason Chen { 0x040F, 0x30 }, 223e4802cb0SJason Chen { 0x3038, 0x00 }, 224e4802cb0SJason Chen { 0x303A, 0x00 }, 225e4802cb0SJason Chen { 0x303B, 0x10 }, 226e4802cb0SJason Chen { 0x300D, 0x00 }, 227e4802cb0SJason Chen { 0x034C, 0x10 }, 228e4802cb0SJason Chen { 0x034D, 0x70 }, 229e4802cb0SJason Chen { 0x034E, 0x0C }, 230e4802cb0SJason Chen { 0x034F, 0x30 }, 231e4802cb0SJason Chen { 0x0350, 0x01 }, 232e4802cb0SJason Chen { 0x0202, 0x0C }, 233e4802cb0SJason Chen { 0x0203, 0x46 }, 234e4802cb0SJason Chen { 0x0204, 0x00 }, 235e4802cb0SJason Chen { 0x0205, 0x00 }, 236e4802cb0SJason Chen { 0x020E, 0x01 }, 237e4802cb0SJason Chen { 0x020F, 0x00 }, 238e4802cb0SJason Chen { 0x0210, 0x01 }, 239e4802cb0SJason Chen { 0x0211, 0x00 }, 240e4802cb0SJason Chen { 0x0212, 0x01 }, 241e4802cb0SJason Chen { 0x0213, 0x00 }, 242e4802cb0SJason Chen { 0x0214, 0x01 }, 243e4802cb0SJason Chen { 0x0215, 0x00 }, 244e4802cb0SJason Chen { 0x7BCD, 0x00 }, 245e4802cb0SJason Chen { 0x94DC, 0x20 }, 246e4802cb0SJason Chen { 0x94DD, 0x20 }, 247e4802cb0SJason Chen { 0x94DE, 0x20 }, 248e4802cb0SJason Chen { 0x95DC, 0x20 }, 249e4802cb0SJason Chen { 0x95DD, 0x20 }, 250e4802cb0SJason Chen { 0x95DE, 0x20 }, 251e4802cb0SJason Chen { 0x7FB0, 0x00 }, 252e4802cb0SJason Chen { 0x9010, 0x3E }, 253e4802cb0SJason Chen { 0x9419, 0x50 }, 254e4802cb0SJason Chen { 0x941B, 0x50 }, 255e4802cb0SJason Chen { 0x9519, 0x50 }, 256e4802cb0SJason Chen { 0x951B, 0x50 }, 257e4802cb0SJason Chen { 0x3030, 0x00 }, 258e4802cb0SJason Chen { 0x3032, 0x00 }, 259e4802cb0SJason Chen { 0x0220, 0x00 }, 260e4802cb0SJason Chen }; 261e4802cb0SJason Chen 262e4802cb0SJason Chen static const struct imx258_reg mode_2104_1560_regs[] = { 263e4802cb0SJason Chen { 0x0136, 0x13 }, 264e4802cb0SJason Chen { 0x0137, 0x33 }, 265e4802cb0SJason Chen { 0x3051, 0x00 }, 266e4802cb0SJason Chen { 0x3052, 0x00 }, 267e4802cb0SJason Chen { 0x4E21, 0x14 }, 268e4802cb0SJason Chen { 0x6B11, 0xCF }, 269e4802cb0SJason Chen { 0x7FF0, 0x08 }, 270e4802cb0SJason Chen { 0x7FF1, 0x0F }, 271e4802cb0SJason Chen { 0x7FF2, 0x08 }, 272e4802cb0SJason Chen { 0x7FF3, 0x1B }, 273e4802cb0SJason Chen { 0x7FF4, 0x23 }, 274e4802cb0SJason Chen { 0x7FF5, 0x60 }, 275e4802cb0SJason Chen { 0x7FF6, 0x00 }, 276e4802cb0SJason Chen { 0x7FF7, 0x01 }, 277e4802cb0SJason Chen { 0x7FF8, 0x00 }, 278e4802cb0SJason Chen { 0x7FF9, 0x78 }, 279e4802cb0SJason Chen { 0x7FFA, 0x00 }, 280e4802cb0SJason Chen { 0x7FFB, 0x00 }, 281e4802cb0SJason Chen { 0x7FFC, 0x00 }, 282e4802cb0SJason Chen { 0x7FFD, 0x00 }, 283e4802cb0SJason Chen { 0x7FFE, 0x00 }, 284e4802cb0SJason Chen { 0x7FFF, 0x03 }, 285e4802cb0SJason Chen { 0x7F76, 0x03 }, 286e4802cb0SJason Chen { 0x7F77, 0xFE }, 287e4802cb0SJason Chen { 0x7FA8, 0x03 }, 288e4802cb0SJason Chen { 0x7FA9, 0xFE }, 289e4802cb0SJason Chen { 0x7B24, 0x81 }, 290e4802cb0SJason Chen { 0x7B25, 0x00 }, 291e4802cb0SJason Chen { 0x6564, 0x07 }, 292e4802cb0SJason Chen { 0x6B0D, 0x41 }, 293e4802cb0SJason Chen { 0x653D, 0x04 }, 294e4802cb0SJason Chen { 0x6B05, 0x8C }, 295e4802cb0SJason Chen { 0x6B06, 0xF9 }, 296e4802cb0SJason Chen { 0x6B08, 0x65 }, 297e4802cb0SJason Chen { 0x6B09, 0xFC }, 298e4802cb0SJason Chen { 0x6B0A, 0xCF }, 299e4802cb0SJason Chen { 0x6B0B, 0xD2 }, 300e4802cb0SJason Chen { 0x6700, 0x0E }, 301e4802cb0SJason Chen { 0x6707, 0x0E }, 302e4802cb0SJason Chen { 0x9104, 0x00 }, 303e4802cb0SJason Chen { 0x4648, 0x7F }, 304e4802cb0SJason Chen { 0x7420, 0x00 }, 305e4802cb0SJason Chen { 0x7421, 0x1C }, 306e4802cb0SJason Chen { 0x7422, 0x00 }, 307e4802cb0SJason Chen { 0x7423, 0xD7 }, 308e4802cb0SJason Chen { 0x5F04, 0x00 }, 309e4802cb0SJason Chen { 0x5F05, 0xED }, 310e4802cb0SJason Chen { 0x0112, 0x0A }, 311e4802cb0SJason Chen { 0x0113, 0x0A }, 312e4802cb0SJason Chen { 0x0114, 0x03 }, 313e4802cb0SJason Chen { 0x0342, 0x14 }, 314e4802cb0SJason Chen { 0x0343, 0xE8 }, 315e4802cb0SJason Chen { 0x0340, 0x06 }, 316e4802cb0SJason Chen { 0x0341, 0x38 }, 317e4802cb0SJason Chen { 0x0344, 0x00 }, 318e4802cb0SJason Chen { 0x0345, 0x00 }, 319e4802cb0SJason Chen { 0x0346, 0x00 }, 320e4802cb0SJason Chen { 0x0347, 0x00 }, 321e4802cb0SJason Chen { 0x0348, 0x10 }, 322e4802cb0SJason Chen { 0x0349, 0x6F }, 323e4802cb0SJason Chen { 0x034A, 0x0C }, 324e4802cb0SJason Chen { 0x034B, 0x2E }, 325e4802cb0SJason Chen { 0x0381, 0x01 }, 326e4802cb0SJason Chen { 0x0383, 0x01 }, 327e4802cb0SJason Chen { 0x0385, 0x01 }, 328e4802cb0SJason Chen { 0x0387, 0x01 }, 329e4802cb0SJason Chen { 0x0900, 0x01 }, 330e4802cb0SJason Chen { 0x0901, 0x12 }, 331e4802cb0SJason Chen { 0x0401, 0x01 }, 332e4802cb0SJason Chen { 0x0404, 0x00 }, 333e4802cb0SJason Chen { 0x0405, 0x20 }, 334e4802cb0SJason Chen { 0x0408, 0x00 }, 335e4802cb0SJason Chen { 0x0409, 0x02 }, 336e4802cb0SJason Chen { 0x040A, 0x00 }, 337e4802cb0SJason Chen { 0x040B, 0x00 }, 338e4802cb0SJason Chen { 0x040C, 0x10 }, 339e4802cb0SJason Chen { 0x040D, 0x6A }, 340e4802cb0SJason Chen { 0x040E, 0x06 }, 341e4802cb0SJason Chen { 0x040F, 0x18 }, 342e4802cb0SJason Chen { 0x3038, 0x00 }, 343e4802cb0SJason Chen { 0x303A, 0x00 }, 344e4802cb0SJason Chen { 0x303B, 0x10 }, 345e4802cb0SJason Chen { 0x300D, 0x00 }, 346e4802cb0SJason Chen { 0x034C, 0x08 }, 347e4802cb0SJason Chen { 0x034D, 0x38 }, 348e4802cb0SJason Chen { 0x034E, 0x06 }, 349e4802cb0SJason Chen { 0x034F, 0x18 }, 350e4802cb0SJason Chen { 0x0350, 0x01 }, 351e4802cb0SJason Chen { 0x0202, 0x06 }, 352e4802cb0SJason Chen { 0x0203, 0x2E }, 353e4802cb0SJason Chen { 0x0204, 0x00 }, 354e4802cb0SJason Chen { 0x0205, 0x00 }, 355e4802cb0SJason Chen { 0x020E, 0x01 }, 356e4802cb0SJason Chen { 0x020F, 0x00 }, 357e4802cb0SJason Chen { 0x0210, 0x01 }, 358e4802cb0SJason Chen { 0x0211, 0x00 }, 359e4802cb0SJason Chen { 0x0212, 0x01 }, 360e4802cb0SJason Chen { 0x0213, 0x00 }, 361e4802cb0SJason Chen { 0x0214, 0x01 }, 362e4802cb0SJason Chen { 0x0215, 0x00 }, 363e4802cb0SJason Chen { 0x7BCD, 0x01 }, 364e4802cb0SJason Chen { 0x94DC, 0x20 }, 365e4802cb0SJason Chen { 0x94DD, 0x20 }, 366e4802cb0SJason Chen { 0x94DE, 0x20 }, 367e4802cb0SJason Chen { 0x95DC, 0x20 }, 368e4802cb0SJason Chen { 0x95DD, 0x20 }, 369e4802cb0SJason Chen { 0x95DE, 0x20 }, 370e4802cb0SJason Chen { 0x7FB0, 0x00 }, 371e4802cb0SJason Chen { 0x9010, 0x3E }, 372e4802cb0SJason Chen { 0x9419, 0x50 }, 373e4802cb0SJason Chen { 0x941B, 0x50 }, 374e4802cb0SJason Chen { 0x9519, 0x50 }, 375e4802cb0SJason Chen { 0x951B, 0x50 }, 376e4802cb0SJason Chen { 0x3030, 0x00 }, 377e4802cb0SJason Chen { 0x3032, 0x00 }, 378e4802cb0SJason Chen { 0x0220, 0x00 }, 379e4802cb0SJason Chen }; 380e4802cb0SJason Chen 381e4802cb0SJason Chen static const struct imx258_reg mode_1048_780_regs[] = { 382e4802cb0SJason Chen { 0x0136, 0x13 }, 383e4802cb0SJason Chen { 0x0137, 0x33 }, 384e4802cb0SJason Chen { 0x3051, 0x00 }, 385e4802cb0SJason Chen { 0x3052, 0x00 }, 386e4802cb0SJason Chen { 0x4E21, 0x14 }, 387e4802cb0SJason Chen { 0x6B11, 0xCF }, 388e4802cb0SJason Chen { 0x7FF0, 0x08 }, 389e4802cb0SJason Chen { 0x7FF1, 0x0F }, 390e4802cb0SJason Chen { 0x7FF2, 0x08 }, 391e4802cb0SJason Chen { 0x7FF3, 0x1B }, 392e4802cb0SJason Chen { 0x7FF4, 0x23 }, 393e4802cb0SJason Chen { 0x7FF5, 0x60 }, 394e4802cb0SJason Chen { 0x7FF6, 0x00 }, 395e4802cb0SJason Chen { 0x7FF7, 0x01 }, 396e4802cb0SJason Chen { 0x7FF8, 0x00 }, 397e4802cb0SJason Chen { 0x7FF9, 0x78 }, 398e4802cb0SJason Chen { 0x7FFA, 0x00 }, 399e4802cb0SJason Chen { 0x7FFB, 0x00 }, 400e4802cb0SJason Chen { 0x7FFC, 0x00 }, 401e4802cb0SJason Chen { 0x7FFD, 0x00 }, 402e4802cb0SJason Chen { 0x7FFE, 0x00 }, 403e4802cb0SJason Chen { 0x7FFF, 0x03 }, 404e4802cb0SJason Chen { 0x7F76, 0x03 }, 405e4802cb0SJason Chen { 0x7F77, 0xFE }, 406e4802cb0SJason Chen { 0x7FA8, 0x03 }, 407e4802cb0SJason Chen { 0x7FA9, 0xFE }, 408e4802cb0SJason Chen { 0x7B24, 0x81 }, 409e4802cb0SJason Chen { 0x7B25, 0x00 }, 410e4802cb0SJason Chen { 0x6564, 0x07 }, 411e4802cb0SJason Chen { 0x6B0D, 0x41 }, 412e4802cb0SJason Chen { 0x653D, 0x04 }, 413e4802cb0SJason Chen { 0x6B05, 0x8C }, 414e4802cb0SJason Chen { 0x6B06, 0xF9 }, 415e4802cb0SJason Chen { 0x6B08, 0x65 }, 416e4802cb0SJason Chen { 0x6B09, 0xFC }, 417e4802cb0SJason Chen { 0x6B0A, 0xCF }, 418e4802cb0SJason Chen { 0x6B0B, 0xD2 }, 419e4802cb0SJason Chen { 0x6700, 0x0E }, 420e4802cb0SJason Chen { 0x6707, 0x0E }, 421e4802cb0SJason Chen { 0x9104, 0x00 }, 422e4802cb0SJason Chen { 0x4648, 0x7F }, 423e4802cb0SJason Chen { 0x7420, 0x00 }, 424e4802cb0SJason Chen { 0x7421, 0x1C }, 425e4802cb0SJason Chen { 0x7422, 0x00 }, 426e4802cb0SJason Chen { 0x7423, 0xD7 }, 427e4802cb0SJason Chen { 0x5F04, 0x00 }, 428e4802cb0SJason Chen { 0x5F05, 0xED }, 429e4802cb0SJason Chen { 0x0112, 0x0A }, 430e4802cb0SJason Chen { 0x0113, 0x0A }, 431e4802cb0SJason Chen { 0x0114, 0x03 }, 432e4802cb0SJason Chen { 0x0342, 0x14 }, 433e4802cb0SJason Chen { 0x0343, 0xE8 }, 434e4802cb0SJason Chen { 0x0340, 0x03 }, 435e4802cb0SJason Chen { 0x0341, 0x4C }, 436e4802cb0SJason Chen { 0x0344, 0x00 }, 437e4802cb0SJason Chen { 0x0345, 0x00 }, 438e4802cb0SJason Chen { 0x0346, 0x00 }, 439e4802cb0SJason Chen { 0x0347, 0x00 }, 440e4802cb0SJason Chen { 0x0348, 0x10 }, 441e4802cb0SJason Chen { 0x0349, 0x6F }, 442e4802cb0SJason Chen { 0x034A, 0x0C }, 443e4802cb0SJason Chen { 0x034B, 0x2E }, 444e4802cb0SJason Chen { 0x0381, 0x01 }, 445e4802cb0SJason Chen { 0x0383, 0x01 }, 446e4802cb0SJason Chen { 0x0385, 0x01 }, 447e4802cb0SJason Chen { 0x0387, 0x01 }, 448e4802cb0SJason Chen { 0x0900, 0x01 }, 449e4802cb0SJason Chen { 0x0901, 0x14 }, 450e4802cb0SJason Chen { 0x0401, 0x01 }, 451e4802cb0SJason Chen { 0x0404, 0x00 }, 452e4802cb0SJason Chen { 0x0405, 0x40 }, 453e4802cb0SJason Chen { 0x0408, 0x00 }, 454e4802cb0SJason Chen { 0x0409, 0x06 }, 455e4802cb0SJason Chen { 0x040A, 0x00 }, 456e4802cb0SJason Chen { 0x040B, 0x00 }, 457e4802cb0SJason Chen { 0x040C, 0x10 }, 458e4802cb0SJason Chen { 0x040D, 0x64 }, 459e4802cb0SJason Chen { 0x040E, 0x03 }, 460e4802cb0SJason Chen { 0x040F, 0x0C }, 461e4802cb0SJason Chen { 0x3038, 0x00 }, 462e4802cb0SJason Chen { 0x303A, 0x00 }, 463e4802cb0SJason Chen { 0x303B, 0x10 }, 464e4802cb0SJason Chen { 0x300D, 0x00 }, 465e4802cb0SJason Chen { 0x034C, 0x04 }, 466e4802cb0SJason Chen { 0x034D, 0x18 }, 467e4802cb0SJason Chen { 0x034E, 0x03 }, 468e4802cb0SJason Chen { 0x034F, 0x0C }, 469e4802cb0SJason Chen { 0x0350, 0x01 }, 470e4802cb0SJason Chen { 0x0202, 0x03 }, 471e4802cb0SJason Chen { 0x0203, 0x42 }, 472e4802cb0SJason Chen { 0x0204, 0x00 }, 473e4802cb0SJason Chen { 0x0205, 0x00 }, 474e4802cb0SJason Chen { 0x020E, 0x01 }, 475e4802cb0SJason Chen { 0x020F, 0x00 }, 476e4802cb0SJason Chen { 0x0210, 0x01 }, 477e4802cb0SJason Chen { 0x0211, 0x00 }, 478e4802cb0SJason Chen { 0x0212, 0x01 }, 479e4802cb0SJason Chen { 0x0213, 0x00 }, 480e4802cb0SJason Chen { 0x0214, 0x01 }, 481e4802cb0SJason Chen { 0x0215, 0x00 }, 482e4802cb0SJason Chen { 0x7BCD, 0x00 }, 483e4802cb0SJason Chen { 0x94DC, 0x20 }, 484e4802cb0SJason Chen { 0x94DD, 0x20 }, 485e4802cb0SJason Chen { 0x94DE, 0x20 }, 486e4802cb0SJason Chen { 0x95DC, 0x20 }, 487e4802cb0SJason Chen { 0x95DD, 0x20 }, 488e4802cb0SJason Chen { 0x95DE, 0x20 }, 489e4802cb0SJason Chen { 0x7FB0, 0x00 }, 490e4802cb0SJason Chen { 0x9010, 0x3E }, 491e4802cb0SJason Chen { 0x9419, 0x50 }, 492e4802cb0SJason Chen { 0x941B, 0x50 }, 493e4802cb0SJason Chen { 0x9519, 0x50 }, 494e4802cb0SJason Chen { 0x951B, 0x50 }, 495e4802cb0SJason Chen { 0x3030, 0x00 }, 496e4802cb0SJason Chen { 0x3032, 0x00 }, 497e4802cb0SJason Chen { 0x0220, 0x00 }, 498e4802cb0SJason Chen }; 499e4802cb0SJason Chen 500e4802cb0SJason Chen static const char * const imx258_test_pattern_menu[] = { 501e4802cb0SJason Chen "Disabled", 502ce6ebeacSBingbu Cao "Solid Colour", 503ce6ebeacSBingbu Cao "Eight Vertical Colour Bars", 504ce6ebeacSBingbu Cao "Colour Bars With Fade to Grey", 505ce6ebeacSBingbu Cao "Pseudorandom Sequence (PN9)", 506e4802cb0SJason Chen }; 507e4802cb0SJason Chen 508e4802cb0SJason Chen /* Configurations for supported link frequencies */ 509e4802cb0SJason Chen #define IMX258_LINK_FREQ_634MHZ 633600000ULL 510e4802cb0SJason Chen #define IMX258_LINK_FREQ_320MHZ 320000000ULL 511e4802cb0SJason Chen 512e4802cb0SJason Chen enum { 513e4802cb0SJason Chen IMX258_LINK_FREQ_1267MBPS, 514e4802cb0SJason Chen IMX258_LINK_FREQ_640MBPS, 515e4802cb0SJason Chen }; 516e4802cb0SJason Chen 517e4802cb0SJason Chen /* 518e4802cb0SJason Chen * pixel_rate = link_freq * data-rate * nr_of_lanes / bits_per_sample 519e4802cb0SJason Chen * data rate => double data rate; number of lanes => 4; bits per pixel => 10 520e4802cb0SJason Chen */ 521e4802cb0SJason Chen static u64 link_freq_to_pixel_rate(u64 f) 522e4802cb0SJason Chen { 523e4802cb0SJason Chen f *= 2 * 4; 524e4802cb0SJason Chen do_div(f, 10); 525e4802cb0SJason Chen 526e4802cb0SJason Chen return f; 527e4802cb0SJason Chen } 528e4802cb0SJason Chen 529e4802cb0SJason Chen /* Menu items for LINK_FREQ V4L2 control */ 530e4802cb0SJason Chen static const s64 link_freq_menu_items[] = { 531e4802cb0SJason Chen IMX258_LINK_FREQ_634MHZ, 532e4802cb0SJason Chen IMX258_LINK_FREQ_320MHZ, 533e4802cb0SJason Chen }; 534e4802cb0SJason Chen 535e4802cb0SJason Chen /* Link frequency configs */ 536e4802cb0SJason Chen static const struct imx258_link_freq_config link_freq_configs[] = { 537e4802cb0SJason Chen [IMX258_LINK_FREQ_1267MBPS] = { 538e4802cb0SJason Chen .pixels_per_line = IMX258_PPL_DEFAULT, 539e4802cb0SJason Chen .reg_list = { 540e4802cb0SJason Chen .num_of_regs = ARRAY_SIZE(mipi_data_rate_1267mbps), 541e4802cb0SJason Chen .regs = mipi_data_rate_1267mbps, 542e4802cb0SJason Chen } 543e4802cb0SJason Chen }, 544e4802cb0SJason Chen [IMX258_LINK_FREQ_640MBPS] = { 545e4802cb0SJason Chen .pixels_per_line = IMX258_PPL_DEFAULT, 546e4802cb0SJason Chen .reg_list = { 547e4802cb0SJason Chen .num_of_regs = ARRAY_SIZE(mipi_data_rate_640mbps), 548e4802cb0SJason Chen .regs = mipi_data_rate_640mbps, 549e4802cb0SJason Chen } 550e4802cb0SJason Chen }, 551e4802cb0SJason Chen }; 552e4802cb0SJason Chen 553e4802cb0SJason Chen /* Mode configs */ 554e4802cb0SJason Chen static const struct imx258_mode supported_modes[] = { 555e4802cb0SJason Chen { 556e4802cb0SJason Chen .width = 4208, 557e4802cb0SJason Chen .height = 3118, 558e4802cb0SJason Chen .vts_def = IMX258_VTS_30FPS, 559e4802cb0SJason Chen .vts_min = IMX258_VTS_30FPS, 560e4802cb0SJason Chen .reg_list = { 561e4802cb0SJason Chen .num_of_regs = ARRAY_SIZE(mode_4208x3118_regs), 562e4802cb0SJason Chen .regs = mode_4208x3118_regs, 563e4802cb0SJason Chen }, 564e4802cb0SJason Chen .link_freq_index = IMX258_LINK_FREQ_1267MBPS, 565e4802cb0SJason Chen }, 566e4802cb0SJason Chen { 567e4802cb0SJason Chen .width = 2104, 568e4802cb0SJason Chen .height = 1560, 569e4802cb0SJason Chen .vts_def = IMX258_VTS_30FPS_2K, 570e4802cb0SJason Chen .vts_min = IMX258_VTS_30FPS_2K, 571e4802cb0SJason Chen .reg_list = { 572e4802cb0SJason Chen .num_of_regs = ARRAY_SIZE(mode_2104_1560_regs), 573e4802cb0SJason Chen .regs = mode_2104_1560_regs, 574e4802cb0SJason Chen }, 575e4802cb0SJason Chen .link_freq_index = IMX258_LINK_FREQ_640MBPS, 576e4802cb0SJason Chen }, 577e4802cb0SJason Chen { 578e4802cb0SJason Chen .width = 1048, 579e4802cb0SJason Chen .height = 780, 580e4802cb0SJason Chen .vts_def = IMX258_VTS_30FPS_VGA, 581e4802cb0SJason Chen .vts_min = IMX258_VTS_30FPS_VGA, 582e4802cb0SJason Chen .reg_list = { 583e4802cb0SJason Chen .num_of_regs = ARRAY_SIZE(mode_1048_780_regs), 584e4802cb0SJason Chen .regs = mode_1048_780_regs, 585e4802cb0SJason Chen }, 586e4802cb0SJason Chen .link_freq_index = IMX258_LINK_FREQ_640MBPS, 587e4802cb0SJason Chen }, 588e4802cb0SJason Chen }; 589e4802cb0SJason Chen 590e4802cb0SJason Chen struct imx258 { 591e4802cb0SJason Chen struct v4l2_subdev sd; 592e4802cb0SJason Chen struct media_pad pad; 593e4802cb0SJason Chen 594e4802cb0SJason Chen struct v4l2_ctrl_handler ctrl_handler; 595e4802cb0SJason Chen /* V4L2 Controls */ 596e4802cb0SJason Chen struct v4l2_ctrl *link_freq; 597e4802cb0SJason Chen struct v4l2_ctrl *pixel_rate; 598e4802cb0SJason Chen struct v4l2_ctrl *vblank; 599e4802cb0SJason Chen struct v4l2_ctrl *hblank; 600e4802cb0SJason Chen struct v4l2_ctrl *exposure; 601e4802cb0SJason Chen 602e4802cb0SJason Chen /* Current mode */ 603e4802cb0SJason Chen const struct imx258_mode *cur_mode; 604e4802cb0SJason Chen 605e4802cb0SJason Chen /* 606e4802cb0SJason Chen * Mutex for serialized access: 607e4802cb0SJason Chen * Protect sensor module set pad format and start/stop streaming safely. 608e4802cb0SJason Chen */ 609e4802cb0SJason Chen struct mutex mutex; 610e4802cb0SJason Chen 611e4802cb0SJason Chen /* Streaming on/off */ 612e4802cb0SJason Chen bool streaming; 613e4802cb0SJason Chen }; 614e4802cb0SJason Chen 615e4802cb0SJason Chen static inline struct imx258 *to_imx258(struct v4l2_subdev *_sd) 616e4802cb0SJason Chen { 617e4802cb0SJason Chen return container_of(_sd, struct imx258, sd); 618e4802cb0SJason Chen } 619e4802cb0SJason Chen 620e4802cb0SJason Chen /* Read registers up to 2 at a time */ 621e4802cb0SJason Chen static int imx258_read_reg(struct imx258 *imx258, u16 reg, u32 len, u32 *val) 622e4802cb0SJason Chen { 623e4802cb0SJason Chen struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); 624e4802cb0SJason Chen struct i2c_msg msgs[2]; 625e4802cb0SJason Chen u8 addr_buf[2] = { reg >> 8, reg & 0xff }; 626e4802cb0SJason Chen u8 data_buf[4] = { 0, }; 627e4802cb0SJason Chen int ret; 628e4802cb0SJason Chen 629e4802cb0SJason Chen if (len > 4) 630e4802cb0SJason Chen return -EINVAL; 631e4802cb0SJason Chen 632e4802cb0SJason Chen /* Write register address */ 633e4802cb0SJason Chen msgs[0].addr = client->addr; 634e4802cb0SJason Chen msgs[0].flags = 0; 635e4802cb0SJason Chen msgs[0].len = ARRAY_SIZE(addr_buf); 636e4802cb0SJason Chen msgs[0].buf = addr_buf; 637e4802cb0SJason Chen 638e4802cb0SJason Chen /* Read data from register */ 639e4802cb0SJason Chen msgs[1].addr = client->addr; 640e4802cb0SJason Chen msgs[1].flags = I2C_M_RD; 641e4802cb0SJason Chen msgs[1].len = len; 642e4802cb0SJason Chen msgs[1].buf = &data_buf[4 - len]; 643e4802cb0SJason Chen 644e4802cb0SJason Chen ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); 645e4802cb0SJason Chen if (ret != ARRAY_SIZE(msgs)) 646e4802cb0SJason Chen return -EIO; 647e4802cb0SJason Chen 648e4802cb0SJason Chen *val = get_unaligned_be32(data_buf); 649e4802cb0SJason Chen 650e4802cb0SJason Chen return 0; 651e4802cb0SJason Chen } 652e4802cb0SJason Chen 653e4802cb0SJason Chen /* Write registers up to 2 at a time */ 654e4802cb0SJason Chen static int imx258_write_reg(struct imx258 *imx258, u16 reg, u32 len, u32 val) 655e4802cb0SJason Chen { 656e4802cb0SJason Chen struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); 657e4802cb0SJason Chen u8 buf[6]; 658e4802cb0SJason Chen 659e4802cb0SJason Chen if (len > 4) 660e4802cb0SJason Chen return -EINVAL; 661e4802cb0SJason Chen 662e4802cb0SJason Chen put_unaligned_be16(reg, buf); 663e4802cb0SJason Chen put_unaligned_be32(val << (8 * (4 - len)), buf + 2); 664e4802cb0SJason Chen if (i2c_master_send(client, buf, len + 2) != len + 2) 665e4802cb0SJason Chen return -EIO; 666e4802cb0SJason Chen 667e4802cb0SJason Chen return 0; 668e4802cb0SJason Chen } 669e4802cb0SJason Chen 670e4802cb0SJason Chen /* Write a list of registers */ 671e4802cb0SJason Chen static int imx258_write_regs(struct imx258 *imx258, 672e4802cb0SJason Chen const struct imx258_reg *regs, u32 len) 673e4802cb0SJason Chen { 674e4802cb0SJason Chen struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); 675e4802cb0SJason Chen unsigned int i; 676e4802cb0SJason Chen int ret; 677e4802cb0SJason Chen 678e4802cb0SJason Chen for (i = 0; i < len; i++) { 679e4802cb0SJason Chen ret = imx258_write_reg(imx258, regs[i].address, 1, 680e4802cb0SJason Chen regs[i].val); 681e4802cb0SJason Chen if (ret) { 682e4802cb0SJason Chen dev_err_ratelimited( 683e4802cb0SJason Chen &client->dev, 684e4802cb0SJason Chen "Failed to write reg 0x%4.4x. error = %d\n", 685e4802cb0SJason Chen regs[i].address, ret); 686e4802cb0SJason Chen 687e4802cb0SJason Chen return ret; 688e4802cb0SJason Chen } 689e4802cb0SJason Chen } 690e4802cb0SJason Chen 691e4802cb0SJason Chen return 0; 692e4802cb0SJason Chen } 693e4802cb0SJason Chen 694e4802cb0SJason Chen /* Open sub-device */ 695e4802cb0SJason Chen static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 696e4802cb0SJason Chen { 697e4802cb0SJason Chen struct v4l2_mbus_framefmt *try_fmt = 698e4802cb0SJason Chen v4l2_subdev_get_try_format(sd, fh->pad, 0); 699e4802cb0SJason Chen 700e4802cb0SJason Chen /* Initialize try_fmt */ 701e4802cb0SJason Chen try_fmt->width = supported_modes[0].width; 702e4802cb0SJason Chen try_fmt->height = supported_modes[0].height; 703e4802cb0SJason Chen try_fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; 704e4802cb0SJason Chen try_fmt->field = V4L2_FIELD_NONE; 705e4802cb0SJason Chen 706e4802cb0SJason Chen return 0; 707e4802cb0SJason Chen } 708e4802cb0SJason Chen 709e4802cb0SJason Chen static int imx258_update_digital_gain(struct imx258 *imx258, u32 len, u32 val) 710e4802cb0SJason Chen { 711e4802cb0SJason Chen int ret; 712e4802cb0SJason Chen 713e4802cb0SJason Chen ret = imx258_write_reg(imx258, IMX258_REG_GR_DIGITAL_GAIN, 714e4802cb0SJason Chen IMX258_REG_VALUE_16BIT, 715e4802cb0SJason Chen val); 716e4802cb0SJason Chen if (ret) 717e4802cb0SJason Chen return ret; 718e4802cb0SJason Chen ret = imx258_write_reg(imx258, IMX258_REG_GB_DIGITAL_GAIN, 719e4802cb0SJason Chen IMX258_REG_VALUE_16BIT, 720e4802cb0SJason Chen val); 721e4802cb0SJason Chen if (ret) 722e4802cb0SJason Chen return ret; 723e4802cb0SJason Chen ret = imx258_write_reg(imx258, IMX258_REG_R_DIGITAL_GAIN, 724e4802cb0SJason Chen IMX258_REG_VALUE_16BIT, 725e4802cb0SJason Chen val); 726e4802cb0SJason Chen if (ret) 727e4802cb0SJason Chen return ret; 728e4802cb0SJason Chen ret = imx258_write_reg(imx258, IMX258_REG_B_DIGITAL_GAIN, 729e4802cb0SJason Chen IMX258_REG_VALUE_16BIT, 730e4802cb0SJason Chen val); 731e4802cb0SJason Chen if (ret) 732e4802cb0SJason Chen return ret; 733e4802cb0SJason Chen return 0; 734e4802cb0SJason Chen } 735e4802cb0SJason Chen 736e4802cb0SJason Chen static int imx258_set_ctrl(struct v4l2_ctrl *ctrl) 737e4802cb0SJason Chen { 738e4802cb0SJason Chen struct imx258 *imx258 = 739e4802cb0SJason Chen container_of(ctrl->handler, struct imx258, ctrl_handler); 740e4802cb0SJason Chen struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); 741e4802cb0SJason Chen int ret = 0; 742e4802cb0SJason Chen 743e4802cb0SJason Chen /* 744e4802cb0SJason Chen * Applying V4L2 control value only happens 745e4802cb0SJason Chen * when power is up for streaming 746e4802cb0SJason Chen */ 747e4802cb0SJason Chen if (pm_runtime_get_if_in_use(&client->dev) == 0) 748e4802cb0SJason Chen return 0; 749e4802cb0SJason Chen 750e4802cb0SJason Chen switch (ctrl->id) { 751e4802cb0SJason Chen case V4L2_CID_ANALOGUE_GAIN: 752e4802cb0SJason Chen ret = imx258_write_reg(imx258, IMX258_REG_ANALOG_GAIN, 753e4802cb0SJason Chen IMX258_REG_VALUE_16BIT, 754e4802cb0SJason Chen ctrl->val); 755e4802cb0SJason Chen break; 756e4802cb0SJason Chen case V4L2_CID_EXPOSURE: 757e4802cb0SJason Chen ret = imx258_write_reg(imx258, IMX258_REG_EXPOSURE, 758e4802cb0SJason Chen IMX258_REG_VALUE_16BIT, 759e4802cb0SJason Chen ctrl->val); 760e4802cb0SJason Chen break; 761e4802cb0SJason Chen case V4L2_CID_DIGITAL_GAIN: 762e4802cb0SJason Chen ret = imx258_update_digital_gain(imx258, IMX258_REG_VALUE_16BIT, 763e4802cb0SJason Chen ctrl->val); 764e4802cb0SJason Chen break; 765e4802cb0SJason Chen case V4L2_CID_TEST_PATTERN: 766e4802cb0SJason Chen ret = imx258_write_reg(imx258, IMX258_REG_TEST_PATTERN, 767e4802cb0SJason Chen IMX258_REG_VALUE_16BIT, 76853f6f81dSChen, JasonX Z ctrl->val); 769e4802cb0SJason Chen ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL, 770e4802cb0SJason Chen IMX258_REG_VALUE_08BIT, 77153f6f81dSChen, JasonX Z !ctrl->val ? REG_CONFIG_MIRROR_FLIP : 772e4802cb0SJason Chen REG_CONFIG_FLIP_TEST_PATTERN); 773e4802cb0SJason Chen break; 774e4802cb0SJason Chen default: 775e4802cb0SJason Chen dev_info(&client->dev, 776e4802cb0SJason Chen "ctrl(id:0x%x,val:0x%x) is not handled\n", 777e4802cb0SJason Chen ctrl->id, ctrl->val); 778e4802cb0SJason Chen ret = -EINVAL; 779e4802cb0SJason Chen break; 780e4802cb0SJason Chen } 781e4802cb0SJason Chen 782e4802cb0SJason Chen pm_runtime_put(&client->dev); 783e4802cb0SJason Chen 784e4802cb0SJason Chen return ret; 785e4802cb0SJason Chen } 786e4802cb0SJason Chen 787e4802cb0SJason Chen static const struct v4l2_ctrl_ops imx258_ctrl_ops = { 788e4802cb0SJason Chen .s_ctrl = imx258_set_ctrl, 789e4802cb0SJason Chen }; 790e4802cb0SJason Chen 791e4802cb0SJason Chen static int imx258_enum_mbus_code(struct v4l2_subdev *sd, 792e4802cb0SJason Chen struct v4l2_subdev_pad_config *cfg, 793e4802cb0SJason Chen struct v4l2_subdev_mbus_code_enum *code) 794e4802cb0SJason Chen { 795e4802cb0SJason Chen /* Only one bayer order(GRBG) is supported */ 796e4802cb0SJason Chen if (code->index > 0) 797e4802cb0SJason Chen return -EINVAL; 798e4802cb0SJason Chen 799e4802cb0SJason Chen code->code = MEDIA_BUS_FMT_SGRBG10_1X10; 800e4802cb0SJason Chen 801e4802cb0SJason Chen return 0; 802e4802cb0SJason Chen } 803e4802cb0SJason Chen 804e4802cb0SJason Chen static int imx258_enum_frame_size(struct v4l2_subdev *sd, 805e4802cb0SJason Chen struct v4l2_subdev_pad_config *cfg, 806e4802cb0SJason Chen struct v4l2_subdev_frame_size_enum *fse) 807e4802cb0SJason Chen { 808e4802cb0SJason Chen if (fse->index >= ARRAY_SIZE(supported_modes)) 809e4802cb0SJason Chen return -EINVAL; 810e4802cb0SJason Chen 811e4802cb0SJason Chen if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) 812e4802cb0SJason Chen return -EINVAL; 813e4802cb0SJason Chen 814e4802cb0SJason Chen fse->min_width = supported_modes[fse->index].width; 815e4802cb0SJason Chen fse->max_width = fse->min_width; 816e4802cb0SJason Chen fse->min_height = supported_modes[fse->index].height; 817e4802cb0SJason Chen fse->max_height = fse->min_height; 818e4802cb0SJason Chen 819e4802cb0SJason Chen return 0; 820e4802cb0SJason Chen } 821e4802cb0SJason Chen 822e4802cb0SJason Chen static void imx258_update_pad_format(const struct imx258_mode *mode, 823e4802cb0SJason Chen struct v4l2_subdev_format *fmt) 824e4802cb0SJason Chen { 825e4802cb0SJason Chen fmt->format.width = mode->width; 826e4802cb0SJason Chen fmt->format.height = mode->height; 827e4802cb0SJason Chen fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; 828e4802cb0SJason Chen fmt->format.field = V4L2_FIELD_NONE; 829e4802cb0SJason Chen } 830e4802cb0SJason Chen 831e4802cb0SJason Chen static int __imx258_get_pad_format(struct imx258 *imx258, 832e4802cb0SJason Chen struct v4l2_subdev_pad_config *cfg, 833e4802cb0SJason Chen struct v4l2_subdev_format *fmt) 834e4802cb0SJason Chen { 835e4802cb0SJason Chen if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) 836e4802cb0SJason Chen fmt->format = *v4l2_subdev_get_try_format(&imx258->sd, cfg, 837e4802cb0SJason Chen fmt->pad); 838e4802cb0SJason Chen else 839e4802cb0SJason Chen imx258_update_pad_format(imx258->cur_mode, fmt); 840e4802cb0SJason Chen 841e4802cb0SJason Chen return 0; 842e4802cb0SJason Chen } 843e4802cb0SJason Chen 844e4802cb0SJason Chen static int imx258_get_pad_format(struct v4l2_subdev *sd, 845e4802cb0SJason Chen struct v4l2_subdev_pad_config *cfg, 846e4802cb0SJason Chen struct v4l2_subdev_format *fmt) 847e4802cb0SJason Chen { 848e4802cb0SJason Chen struct imx258 *imx258 = to_imx258(sd); 849e4802cb0SJason Chen int ret; 850e4802cb0SJason Chen 851e4802cb0SJason Chen mutex_lock(&imx258->mutex); 852e4802cb0SJason Chen ret = __imx258_get_pad_format(imx258, cfg, fmt); 853e4802cb0SJason Chen mutex_unlock(&imx258->mutex); 854e4802cb0SJason Chen 855e4802cb0SJason Chen return ret; 856e4802cb0SJason Chen } 857e4802cb0SJason Chen 858e4802cb0SJason Chen static int imx258_set_pad_format(struct v4l2_subdev *sd, 859e4802cb0SJason Chen struct v4l2_subdev_pad_config *cfg, 860e4802cb0SJason Chen struct v4l2_subdev_format *fmt) 861e4802cb0SJason Chen { 862e4802cb0SJason Chen struct imx258 *imx258 = to_imx258(sd); 863e4802cb0SJason Chen const struct imx258_mode *mode; 864e4802cb0SJason Chen struct v4l2_mbus_framefmt *framefmt; 865e4802cb0SJason Chen s32 vblank_def; 866e4802cb0SJason Chen s32 vblank_min; 867e4802cb0SJason Chen s64 h_blank; 868e4802cb0SJason Chen s64 pixel_rate; 869e4802cb0SJason Chen s64 link_freq; 870e4802cb0SJason Chen 871e4802cb0SJason Chen mutex_lock(&imx258->mutex); 872e4802cb0SJason Chen 873e4802cb0SJason Chen /* Only one raw bayer(GBRG) order is supported */ 874e4802cb0SJason Chen fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; 875e4802cb0SJason Chen 876e4802cb0SJason Chen mode = v4l2_find_nearest_size(supported_modes, 877e4802cb0SJason Chen ARRAY_SIZE(supported_modes), width, height, 878e4802cb0SJason Chen fmt->format.width, fmt->format.height); 879e4802cb0SJason Chen imx258_update_pad_format(mode, fmt); 880e4802cb0SJason Chen if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { 881e4802cb0SJason Chen framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); 882e4802cb0SJason Chen *framefmt = fmt->format; 883e4802cb0SJason Chen } else { 884e4802cb0SJason Chen imx258->cur_mode = mode; 885e4802cb0SJason Chen __v4l2_ctrl_s_ctrl(imx258->link_freq, mode->link_freq_index); 886e4802cb0SJason Chen 887e4802cb0SJason Chen link_freq = link_freq_menu_items[mode->link_freq_index]; 888e4802cb0SJason Chen pixel_rate = link_freq_to_pixel_rate(link_freq); 889e4802cb0SJason Chen __v4l2_ctrl_s_ctrl_int64(imx258->pixel_rate, pixel_rate); 890e4802cb0SJason Chen /* Update limits and set FPS to default */ 891e4802cb0SJason Chen vblank_def = imx258->cur_mode->vts_def - 892e4802cb0SJason Chen imx258->cur_mode->height; 893e4802cb0SJason Chen vblank_min = imx258->cur_mode->vts_min - 894e4802cb0SJason Chen imx258->cur_mode->height; 895e4802cb0SJason Chen __v4l2_ctrl_modify_range( 896e4802cb0SJason Chen imx258->vblank, vblank_min, 897e4802cb0SJason Chen IMX258_VTS_MAX - imx258->cur_mode->height, 1, 898e4802cb0SJason Chen vblank_def); 899e4802cb0SJason Chen __v4l2_ctrl_s_ctrl(imx258->vblank, vblank_def); 900e4802cb0SJason Chen h_blank = 901e4802cb0SJason Chen link_freq_configs[mode->link_freq_index].pixels_per_line 902e4802cb0SJason Chen - imx258->cur_mode->width; 903e4802cb0SJason Chen __v4l2_ctrl_modify_range(imx258->hblank, h_blank, 904e4802cb0SJason Chen h_blank, 1, h_blank); 905e4802cb0SJason Chen } 906e4802cb0SJason Chen 907e4802cb0SJason Chen mutex_unlock(&imx258->mutex); 908e4802cb0SJason Chen 909e4802cb0SJason Chen return 0; 910e4802cb0SJason Chen } 911e4802cb0SJason Chen 912e4802cb0SJason Chen /* Start streaming */ 913e4802cb0SJason Chen static int imx258_start_streaming(struct imx258 *imx258) 914e4802cb0SJason Chen { 915e4802cb0SJason Chen struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); 916e4802cb0SJason Chen const struct imx258_reg_list *reg_list; 917e4802cb0SJason Chen int ret, link_freq_index; 918e4802cb0SJason Chen 919e4802cb0SJason Chen /* Setup PLL */ 920e4802cb0SJason Chen link_freq_index = imx258->cur_mode->link_freq_index; 921e4802cb0SJason Chen reg_list = &link_freq_configs[link_freq_index].reg_list; 922e4802cb0SJason Chen ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs); 923e4802cb0SJason Chen if (ret) { 924e4802cb0SJason Chen dev_err(&client->dev, "%s failed to set plls\n", __func__); 925e4802cb0SJason Chen return ret; 926e4802cb0SJason Chen } 927e4802cb0SJason Chen 928e4802cb0SJason Chen /* Apply default values of current mode */ 929e4802cb0SJason Chen reg_list = &imx258->cur_mode->reg_list; 930e4802cb0SJason Chen ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs); 931e4802cb0SJason Chen if (ret) { 932e4802cb0SJason Chen dev_err(&client->dev, "%s failed to set mode\n", __func__); 933e4802cb0SJason Chen return ret; 934e4802cb0SJason Chen } 935e4802cb0SJason Chen 936e4802cb0SJason Chen /* Set Orientation be 180 degree */ 937e4802cb0SJason Chen ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL, 938e4802cb0SJason Chen IMX258_REG_VALUE_08BIT, REG_CONFIG_MIRROR_FLIP); 939e4802cb0SJason Chen if (ret) { 940e4802cb0SJason Chen dev_err(&client->dev, "%s failed to set orientation\n", 941e4802cb0SJason Chen __func__); 942e4802cb0SJason Chen return ret; 943e4802cb0SJason Chen } 944e4802cb0SJason Chen 945e4802cb0SJason Chen /* Apply customized values from user */ 946e4802cb0SJason Chen ret = __v4l2_ctrl_handler_setup(imx258->sd.ctrl_handler); 947e4802cb0SJason Chen if (ret) 948e4802cb0SJason Chen return ret; 949e4802cb0SJason Chen 950e4802cb0SJason Chen /* set stream on register */ 951e4802cb0SJason Chen return imx258_write_reg(imx258, IMX258_REG_MODE_SELECT, 952e4802cb0SJason Chen IMX258_REG_VALUE_08BIT, 953e4802cb0SJason Chen IMX258_MODE_STREAMING); 954e4802cb0SJason Chen } 955e4802cb0SJason Chen 956e4802cb0SJason Chen /* Stop streaming */ 957e4802cb0SJason Chen static int imx258_stop_streaming(struct imx258 *imx258) 958e4802cb0SJason Chen { 959e4802cb0SJason Chen struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); 960e4802cb0SJason Chen int ret; 961e4802cb0SJason Chen 962e4802cb0SJason Chen /* set stream off register */ 963e4802cb0SJason Chen ret = imx258_write_reg(imx258, IMX258_REG_MODE_SELECT, 964e4802cb0SJason Chen IMX258_REG_VALUE_08BIT, IMX258_MODE_STANDBY); 965e4802cb0SJason Chen if (ret) 966e4802cb0SJason Chen dev_err(&client->dev, "%s failed to set stream\n", __func__); 967e4802cb0SJason Chen 968e4802cb0SJason Chen /* 969e4802cb0SJason Chen * Return success even if it was an error, as there is nothing the 970e4802cb0SJason Chen * caller can do about it. 971e4802cb0SJason Chen */ 972e4802cb0SJason Chen return 0; 973e4802cb0SJason Chen } 974e4802cb0SJason Chen 975e4802cb0SJason Chen static int imx258_set_stream(struct v4l2_subdev *sd, int enable) 976e4802cb0SJason Chen { 977e4802cb0SJason Chen struct imx258 *imx258 = to_imx258(sd); 978e4802cb0SJason Chen struct i2c_client *client = v4l2_get_subdevdata(sd); 979e4802cb0SJason Chen int ret = 0; 980e4802cb0SJason Chen 981e4802cb0SJason Chen mutex_lock(&imx258->mutex); 982e4802cb0SJason Chen if (imx258->streaming == enable) { 983e4802cb0SJason Chen mutex_unlock(&imx258->mutex); 984e4802cb0SJason Chen return 0; 985e4802cb0SJason Chen } 986e4802cb0SJason Chen 987e4802cb0SJason Chen if (enable) { 988e4802cb0SJason Chen ret = pm_runtime_get_sync(&client->dev); 989e4802cb0SJason Chen if (ret < 0) { 990e4802cb0SJason Chen pm_runtime_put_noidle(&client->dev); 991e4802cb0SJason Chen goto err_unlock; 992e4802cb0SJason Chen } 993e4802cb0SJason Chen 994e4802cb0SJason Chen /* 995e4802cb0SJason Chen * Apply default & customized values 996e4802cb0SJason Chen * and then start streaming. 997e4802cb0SJason Chen */ 998e4802cb0SJason Chen ret = imx258_start_streaming(imx258); 999e4802cb0SJason Chen if (ret) 1000e4802cb0SJason Chen goto err_rpm_put; 1001e4802cb0SJason Chen } else { 1002e4802cb0SJason Chen imx258_stop_streaming(imx258); 1003e4802cb0SJason Chen pm_runtime_put(&client->dev); 1004e4802cb0SJason Chen } 1005e4802cb0SJason Chen 1006e4802cb0SJason Chen imx258->streaming = enable; 1007e4802cb0SJason Chen mutex_unlock(&imx258->mutex); 1008e4802cb0SJason Chen 1009e4802cb0SJason Chen return ret; 1010e4802cb0SJason Chen 1011e4802cb0SJason Chen err_rpm_put: 1012e4802cb0SJason Chen pm_runtime_put(&client->dev); 1013e4802cb0SJason Chen err_unlock: 1014e4802cb0SJason Chen mutex_unlock(&imx258->mutex); 1015e4802cb0SJason Chen 1016e4802cb0SJason Chen return ret; 1017e4802cb0SJason Chen } 1018e4802cb0SJason Chen 1019e4802cb0SJason Chen static int __maybe_unused imx258_suspend(struct device *dev) 1020e4802cb0SJason Chen { 1021e4802cb0SJason Chen struct i2c_client *client = to_i2c_client(dev); 1022e4802cb0SJason Chen struct v4l2_subdev *sd = i2c_get_clientdata(client); 1023e4802cb0SJason Chen struct imx258 *imx258 = to_imx258(sd); 1024e4802cb0SJason Chen 1025e4802cb0SJason Chen if (imx258->streaming) 1026e4802cb0SJason Chen imx258_stop_streaming(imx258); 1027e4802cb0SJason Chen 1028e4802cb0SJason Chen return 0; 1029e4802cb0SJason Chen } 1030e4802cb0SJason Chen 1031e4802cb0SJason Chen static int __maybe_unused imx258_resume(struct device *dev) 1032e4802cb0SJason Chen { 1033e4802cb0SJason Chen struct i2c_client *client = to_i2c_client(dev); 1034e4802cb0SJason Chen struct v4l2_subdev *sd = i2c_get_clientdata(client); 1035e4802cb0SJason Chen struct imx258 *imx258 = to_imx258(sd); 1036e4802cb0SJason Chen int ret; 1037e4802cb0SJason Chen 1038e4802cb0SJason Chen if (imx258->streaming) { 1039e4802cb0SJason Chen ret = imx258_start_streaming(imx258); 1040e4802cb0SJason Chen if (ret) 1041e4802cb0SJason Chen goto error; 1042e4802cb0SJason Chen } 1043e4802cb0SJason Chen 1044e4802cb0SJason Chen return 0; 1045e4802cb0SJason Chen 1046e4802cb0SJason Chen error: 1047e4802cb0SJason Chen imx258_stop_streaming(imx258); 1048e4802cb0SJason Chen imx258->streaming = 0; 1049e4802cb0SJason Chen return ret; 1050e4802cb0SJason Chen } 1051e4802cb0SJason Chen 1052e4802cb0SJason Chen /* Verify chip ID */ 1053e4802cb0SJason Chen static int imx258_identify_module(struct imx258 *imx258) 1054e4802cb0SJason Chen { 1055e4802cb0SJason Chen struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); 1056e4802cb0SJason Chen int ret; 1057e4802cb0SJason Chen u32 val; 1058e4802cb0SJason Chen 1059e4802cb0SJason Chen ret = imx258_read_reg(imx258, IMX258_REG_CHIP_ID, 1060e4802cb0SJason Chen IMX258_REG_VALUE_16BIT, &val); 1061e4802cb0SJason Chen if (ret) { 1062e4802cb0SJason Chen dev_err(&client->dev, "failed to read chip id %x\n", 1063e4802cb0SJason Chen IMX258_CHIP_ID); 1064e4802cb0SJason Chen return ret; 1065e4802cb0SJason Chen } 1066e4802cb0SJason Chen 1067e4802cb0SJason Chen if (val != IMX258_CHIP_ID) { 1068e4802cb0SJason Chen dev_err(&client->dev, "chip id mismatch: %x!=%x\n", 1069e4802cb0SJason Chen IMX258_CHIP_ID, val); 1070e4802cb0SJason Chen return -EIO; 1071e4802cb0SJason Chen } 1072e4802cb0SJason Chen 1073e4802cb0SJason Chen return 0; 1074e4802cb0SJason Chen } 1075e4802cb0SJason Chen 1076e4802cb0SJason Chen static const struct v4l2_subdev_video_ops imx258_video_ops = { 1077e4802cb0SJason Chen .s_stream = imx258_set_stream, 1078e4802cb0SJason Chen }; 1079e4802cb0SJason Chen 1080e4802cb0SJason Chen static const struct v4l2_subdev_pad_ops imx258_pad_ops = { 1081e4802cb0SJason Chen .enum_mbus_code = imx258_enum_mbus_code, 1082e4802cb0SJason Chen .get_fmt = imx258_get_pad_format, 1083e4802cb0SJason Chen .set_fmt = imx258_set_pad_format, 1084e4802cb0SJason Chen .enum_frame_size = imx258_enum_frame_size, 1085e4802cb0SJason Chen }; 1086e4802cb0SJason Chen 1087e4802cb0SJason Chen static const struct v4l2_subdev_ops imx258_subdev_ops = { 1088e4802cb0SJason Chen .video = &imx258_video_ops, 1089e4802cb0SJason Chen .pad = &imx258_pad_ops, 1090e4802cb0SJason Chen }; 1091e4802cb0SJason Chen 1092e4802cb0SJason Chen static const struct v4l2_subdev_internal_ops imx258_internal_ops = { 1093e4802cb0SJason Chen .open = imx258_open, 1094e4802cb0SJason Chen }; 1095e4802cb0SJason Chen 1096e4802cb0SJason Chen /* Initialize control handlers */ 1097e4802cb0SJason Chen static int imx258_init_controls(struct imx258 *imx258) 1098e4802cb0SJason Chen { 1099e4802cb0SJason Chen struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); 1100e4802cb0SJason Chen struct v4l2_ctrl_handler *ctrl_hdlr; 1101e4802cb0SJason Chen s64 vblank_def; 1102e4802cb0SJason Chen s64 vblank_min; 1103e4802cb0SJason Chen s64 pixel_rate_min; 1104e4802cb0SJason Chen s64 pixel_rate_max; 1105e4802cb0SJason Chen int ret; 1106e4802cb0SJason Chen 1107e4802cb0SJason Chen ctrl_hdlr = &imx258->ctrl_handler; 1108e4802cb0SJason Chen ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); 1109e4802cb0SJason Chen if (ret) 1110e4802cb0SJason Chen return ret; 1111e4802cb0SJason Chen 1112e4802cb0SJason Chen mutex_init(&imx258->mutex); 1113e4802cb0SJason Chen ctrl_hdlr->lock = &imx258->mutex; 1114e4802cb0SJason Chen imx258->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, 1115e4802cb0SJason Chen &imx258_ctrl_ops, 1116e4802cb0SJason Chen V4L2_CID_LINK_FREQ, 1117e4802cb0SJason Chen ARRAY_SIZE(link_freq_menu_items) - 1, 1118e4802cb0SJason Chen 0, 1119e4802cb0SJason Chen link_freq_menu_items); 1120e4802cb0SJason Chen 1121e4802cb0SJason Chen if (imx258->link_freq) 1122e4802cb0SJason Chen imx258->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; 1123e4802cb0SJason Chen 1124e4802cb0SJason Chen pixel_rate_max = link_freq_to_pixel_rate(link_freq_menu_items[0]); 1125e4802cb0SJason Chen pixel_rate_min = link_freq_to_pixel_rate(link_freq_menu_items[1]); 1126e4802cb0SJason Chen /* By default, PIXEL_RATE is read only */ 1127e4802cb0SJason Chen imx258->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, 1128e4802cb0SJason Chen V4L2_CID_PIXEL_RATE, 1129e4802cb0SJason Chen pixel_rate_min, pixel_rate_max, 1130e4802cb0SJason Chen 1, pixel_rate_max); 1131e4802cb0SJason Chen 1132e4802cb0SJason Chen 1133e4802cb0SJason Chen vblank_def = imx258->cur_mode->vts_def - imx258->cur_mode->height; 1134e4802cb0SJason Chen vblank_min = imx258->cur_mode->vts_min - imx258->cur_mode->height; 1135e4802cb0SJason Chen imx258->vblank = v4l2_ctrl_new_std( 1136e4802cb0SJason Chen ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_VBLANK, 1137e4802cb0SJason Chen vblank_min, 1138e4802cb0SJason Chen IMX258_VTS_MAX - imx258->cur_mode->height, 1, 1139e4802cb0SJason Chen vblank_def); 1140e4802cb0SJason Chen 1141e4802cb0SJason Chen if (imx258->vblank) 1142e4802cb0SJason Chen imx258->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; 1143e4802cb0SJason Chen 1144e4802cb0SJason Chen imx258->hblank = v4l2_ctrl_new_std( 1145e4802cb0SJason Chen ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_HBLANK, 1146e4802cb0SJason Chen IMX258_PPL_DEFAULT - imx258->cur_mode->width, 1147e4802cb0SJason Chen IMX258_PPL_DEFAULT - imx258->cur_mode->width, 1148e4802cb0SJason Chen 1, 1149e4802cb0SJason Chen IMX258_PPL_DEFAULT - imx258->cur_mode->width); 1150e4802cb0SJason Chen 1151e4802cb0SJason Chen if (imx258->hblank) 1152e4802cb0SJason Chen imx258->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; 1153e4802cb0SJason Chen 1154e4802cb0SJason Chen imx258->exposure = v4l2_ctrl_new_std( 1155e4802cb0SJason Chen ctrl_hdlr, &imx258_ctrl_ops, 1156e4802cb0SJason Chen V4L2_CID_EXPOSURE, IMX258_EXPOSURE_MIN, 1157e4802cb0SJason Chen IMX258_EXPOSURE_MAX, IMX258_EXPOSURE_STEP, 1158e4802cb0SJason Chen IMX258_EXPOSURE_DEFAULT); 1159e4802cb0SJason Chen 1160e4802cb0SJason Chen v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, 1161e4802cb0SJason Chen IMX258_ANA_GAIN_MIN, IMX258_ANA_GAIN_MAX, 1162e4802cb0SJason Chen IMX258_ANA_GAIN_STEP, IMX258_ANA_GAIN_DEFAULT); 1163e4802cb0SJason Chen 1164e4802cb0SJason Chen v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_DIGITAL_GAIN, 1165e4802cb0SJason Chen IMX258_DGTL_GAIN_MIN, IMX258_DGTL_GAIN_MAX, 1166e4802cb0SJason Chen IMX258_DGTL_GAIN_STEP, 1167e4802cb0SJason Chen IMX258_DGTL_GAIN_DEFAULT); 1168e4802cb0SJason Chen 1169e4802cb0SJason Chen v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx258_ctrl_ops, 1170e4802cb0SJason Chen V4L2_CID_TEST_PATTERN, 1171e4802cb0SJason Chen ARRAY_SIZE(imx258_test_pattern_menu) - 1, 1172e4802cb0SJason Chen 0, 0, imx258_test_pattern_menu); 1173e4802cb0SJason Chen 1174e4802cb0SJason Chen if (ctrl_hdlr->error) { 1175e4802cb0SJason Chen ret = ctrl_hdlr->error; 1176e4802cb0SJason Chen dev_err(&client->dev, "%s control init failed (%d)\n", 1177e4802cb0SJason Chen __func__, ret); 1178e4802cb0SJason Chen goto error; 1179e4802cb0SJason Chen } 1180e4802cb0SJason Chen 1181e4802cb0SJason Chen imx258->sd.ctrl_handler = ctrl_hdlr; 1182e4802cb0SJason Chen 1183e4802cb0SJason Chen return 0; 1184e4802cb0SJason Chen 1185e4802cb0SJason Chen error: 1186e4802cb0SJason Chen v4l2_ctrl_handler_free(ctrl_hdlr); 1187e4802cb0SJason Chen mutex_destroy(&imx258->mutex); 1188e4802cb0SJason Chen 1189e4802cb0SJason Chen return ret; 1190e4802cb0SJason Chen } 1191e4802cb0SJason Chen 1192e4802cb0SJason Chen static void imx258_free_controls(struct imx258 *imx258) 1193e4802cb0SJason Chen { 1194e4802cb0SJason Chen v4l2_ctrl_handler_free(imx258->sd.ctrl_handler); 1195e4802cb0SJason Chen mutex_destroy(&imx258->mutex); 1196e4802cb0SJason Chen } 1197e4802cb0SJason Chen 1198e4802cb0SJason Chen static int imx258_probe(struct i2c_client *client) 1199e4802cb0SJason Chen { 1200e4802cb0SJason Chen struct imx258 *imx258; 1201e4802cb0SJason Chen int ret; 1202e4802cb0SJason Chen u32 val = 0; 1203e4802cb0SJason Chen 1204e4802cb0SJason Chen device_property_read_u32(&client->dev, "clock-frequency", &val); 1205e4802cb0SJason Chen if (val != 19200000) 1206e4802cb0SJason Chen return -EINVAL; 1207e4802cb0SJason Chen 120817121d12SSakari Ailus /* 120917121d12SSakari Ailus * Check that the device is mounted upside down. The driver only 121017121d12SSakari Ailus * supports a single pixel order right now. 121117121d12SSakari Ailus */ 121217121d12SSakari Ailus ret = device_property_read_u32(&client->dev, "rotation", &val); 121317121d12SSakari Ailus if (ret || val != 180) 121417121d12SSakari Ailus return -EINVAL; 121517121d12SSakari Ailus 1216e4802cb0SJason Chen imx258 = devm_kzalloc(&client->dev, sizeof(*imx258), GFP_KERNEL); 1217e4802cb0SJason Chen if (!imx258) 1218e4802cb0SJason Chen return -ENOMEM; 1219e4802cb0SJason Chen 1220e4802cb0SJason Chen /* Initialize subdev */ 1221e4802cb0SJason Chen v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops); 1222e4802cb0SJason Chen 1223e4802cb0SJason Chen /* Check module identity */ 1224e4802cb0SJason Chen ret = imx258_identify_module(imx258); 1225e4802cb0SJason Chen if (ret) 1226e4802cb0SJason Chen return ret; 1227e4802cb0SJason Chen 1228e4802cb0SJason Chen /* Set default mode to max resolution */ 1229e4802cb0SJason Chen imx258->cur_mode = &supported_modes[0]; 1230e4802cb0SJason Chen 1231e4802cb0SJason Chen ret = imx258_init_controls(imx258); 1232e4802cb0SJason Chen if (ret) 1233e4802cb0SJason Chen return ret; 1234e4802cb0SJason Chen 1235e4802cb0SJason Chen /* Initialize subdev */ 1236e4802cb0SJason Chen imx258->sd.internal_ops = &imx258_internal_ops; 1237e4802cb0SJason Chen imx258->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 1238e4802cb0SJason Chen imx258->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; 1239e4802cb0SJason Chen 1240e4802cb0SJason Chen /* Initialize source pad */ 1241e4802cb0SJason Chen imx258->pad.flags = MEDIA_PAD_FL_SOURCE; 1242e4802cb0SJason Chen 1243e4802cb0SJason Chen ret = media_entity_pads_init(&imx258->sd.entity, 1, &imx258->pad); 1244e4802cb0SJason Chen if (ret) 1245e4802cb0SJason Chen goto error_handler_free; 1246e4802cb0SJason Chen 1247e4802cb0SJason Chen ret = v4l2_async_register_subdev_sensor_common(&imx258->sd); 1248e4802cb0SJason Chen if (ret < 0) 1249e4802cb0SJason Chen goto error_media_entity; 1250e4802cb0SJason Chen 1251e4802cb0SJason Chen pm_runtime_set_active(&client->dev); 1252e4802cb0SJason Chen pm_runtime_enable(&client->dev); 1253e4802cb0SJason Chen pm_runtime_idle(&client->dev); 1254e4802cb0SJason Chen 1255e4802cb0SJason Chen return 0; 1256e4802cb0SJason Chen 1257e4802cb0SJason Chen error_media_entity: 1258e4802cb0SJason Chen media_entity_cleanup(&imx258->sd.entity); 1259e4802cb0SJason Chen 1260e4802cb0SJason Chen error_handler_free: 1261e4802cb0SJason Chen imx258_free_controls(imx258); 1262e4802cb0SJason Chen 1263e4802cb0SJason Chen return ret; 1264e4802cb0SJason Chen } 1265e4802cb0SJason Chen 1266e4802cb0SJason Chen static int imx258_remove(struct i2c_client *client) 1267e4802cb0SJason Chen { 1268e4802cb0SJason Chen struct v4l2_subdev *sd = i2c_get_clientdata(client); 1269e4802cb0SJason Chen struct imx258 *imx258 = to_imx258(sd); 1270e4802cb0SJason Chen 1271e4802cb0SJason Chen v4l2_async_unregister_subdev(sd); 1272e4802cb0SJason Chen media_entity_cleanup(&sd->entity); 1273e4802cb0SJason Chen imx258_free_controls(imx258); 1274e4802cb0SJason Chen 1275e4802cb0SJason Chen pm_runtime_disable(&client->dev); 1276e4802cb0SJason Chen pm_runtime_set_suspended(&client->dev); 1277e4802cb0SJason Chen 1278e4802cb0SJason Chen return 0; 1279e4802cb0SJason Chen } 1280e4802cb0SJason Chen 1281e4802cb0SJason Chen static const struct dev_pm_ops imx258_pm_ops = { 1282e4802cb0SJason Chen SET_SYSTEM_SLEEP_PM_OPS(imx258_suspend, imx258_resume) 1283e4802cb0SJason Chen }; 1284e4802cb0SJason Chen 1285e4802cb0SJason Chen #ifdef CONFIG_ACPI 1286e4802cb0SJason Chen static const struct acpi_device_id imx258_acpi_ids[] = { 1287e4802cb0SJason Chen { "SONY258A" }, 1288e4802cb0SJason Chen { /* sentinel */ } 1289e4802cb0SJason Chen }; 1290e4802cb0SJason Chen 1291e4802cb0SJason Chen MODULE_DEVICE_TABLE(acpi, imx258_acpi_ids); 1292e4802cb0SJason Chen #endif 1293e4802cb0SJason Chen 1294e4802cb0SJason Chen static struct i2c_driver imx258_i2c_driver = { 1295e4802cb0SJason Chen .driver = { 1296e4802cb0SJason Chen .name = "imx258", 1297e4802cb0SJason Chen .pm = &imx258_pm_ops, 1298e4802cb0SJason Chen .acpi_match_table = ACPI_PTR(imx258_acpi_ids), 1299e4802cb0SJason Chen }, 1300e4802cb0SJason Chen .probe_new = imx258_probe, 1301e4802cb0SJason Chen .remove = imx258_remove, 1302e4802cb0SJason Chen }; 1303e4802cb0SJason Chen 1304e4802cb0SJason Chen module_i2c_driver(imx258_i2c_driver); 1305e4802cb0SJason Chen 1306e4802cb0SJason Chen MODULE_AUTHOR("Yeh, Andy <andy.yeh@intel.com>"); 13072f248f7fSSakari Ailus MODULE_AUTHOR("Chiang, Alan"); 1308*d30ac9d8SSakari Ailus MODULE_AUTHOR("Chen, Jason"); 1309e4802cb0SJason Chen MODULE_DESCRIPTION("Sony IMX258 sensor driver"); 1310e4802cb0SJason Chen MODULE_LICENSE("GPL v2"); 1311