124169a5aSJacopo Mondi // SPDX-License-Identifier: GPL-2.0 23c2472a3SRamiro Oliveira /* 33c2472a3SRamiro Oliveira * A V4L2 driver for OmniVision OV5647 cameras. 43c2472a3SRamiro Oliveira * 53c2472a3SRamiro Oliveira * Based on Samsung S5K6AAFX SXGA 1/6" 1.3M CMOS Image Sensor driver 63c2472a3SRamiro Oliveira * Copyright (C) 2011 Sylwester Nawrocki <s.nawrocki@samsung.com> 73c2472a3SRamiro Oliveira * 83c2472a3SRamiro Oliveira * Based on Omnivision OV7670 Camera Driver 93c2472a3SRamiro Oliveira * Copyright (C) 2006-7 Jonathan Corbet <corbet@lwn.net> 103c2472a3SRamiro Oliveira * 113c2472a3SRamiro Oliveira * Copyright (C) 2016, Synopsys, Inc. 123c2472a3SRamiro Oliveira */ 133c2472a3SRamiro Oliveira 143c2472a3SRamiro Oliveira #include <linux/clk.h> 153c2472a3SRamiro Oliveira #include <linux/delay.h> 16b050791dSDave Stevenson #include <linux/gpio/consumer.h> 173c2472a3SRamiro Oliveira #include <linux/i2c.h> 183c2472a3SRamiro Oliveira #include <linux/init.h> 193c2472a3SRamiro Oliveira #include <linux/io.h> 203c2472a3SRamiro Oliveira #include <linux/module.h> 21859969b3SSakari Ailus #include <linux/of_graph.h> 22089b7c70SJacopo Mondi #include <linux/pm_runtime.h> 233c2472a3SRamiro Oliveira #include <linux/slab.h> 243c2472a3SRamiro Oliveira #include <linux/videodev2.h> 254974c2f1SDavid Plowman #include <media/v4l2-ctrls.h> 263c2472a3SRamiro Oliveira #include <media/v4l2-device.h> 27859969b3SSakari Ailus #include <media/v4l2-fwnode.h> 283c2472a3SRamiro Oliveira #include <media/v4l2-image-sizes.h> 293c2472a3SRamiro Oliveira #include <media/v4l2-mediabus.h> 303c2472a3SRamiro Oliveira 31b050791dSDave Stevenson /* 32b050791dSDave Stevenson * From the datasheet, "20ms after PWDN goes low or 20ms after RESETB goes 33b050791dSDave Stevenson * high if reset is inserted after PWDN goes high, host can access sensor's 34b050791dSDave Stevenson * SCCB to initialize sensor." 35b050791dSDave Stevenson */ 36b050791dSDave Stevenson #define PWDN_ACTIVE_DELAY_MS 20 37b050791dSDave Stevenson 38cef66734SJacob Chen #define MIPI_CTRL00_CLOCK_LANE_GATE BIT(5) 39dea4fcfeSDave Stevenson #define MIPI_CTRL00_LINE_SYNC_ENABLE BIT(4) 40cef66734SJacob Chen #define MIPI_CTRL00_BUS_IDLE BIT(2) 41cef66734SJacob Chen #define MIPI_CTRL00_CLOCK_LANE_DISABLE BIT(0) 42cef66734SJacob Chen 43cef66734SJacob Chen #define OV5647_SW_STANDBY 0x0100 443c2472a3SRamiro Oliveira #define OV5647_SW_RESET 0x0103 45c9a05cecSJacopo Mondi #define OV5647_REG_CHIPID_H 0x300a 46c9a05cecSJacopo Mondi #define OV5647_REG_CHIPID_L 0x300b 47c9a05cecSJacopo Mondi #define OV5640_REG_PAD_OUT 0x300d 484974c2f1SDavid Plowman #define OV5647_REG_EXP_HI 0x3500 494974c2f1SDavid Plowman #define OV5647_REG_EXP_MID 0x3501 504974c2f1SDavid Plowman #define OV5647_REG_EXP_LO 0x3502 514974c2f1SDavid Plowman #define OV5647_REG_AEC_AGC 0x3503 524974c2f1SDavid Plowman #define OV5647_REG_GAIN_HI 0x350a 534974c2f1SDavid Plowman #define OV5647_REG_GAIN_LO 0x350b 542512c064SDave Stevenson #define OV5647_REG_VTS_HI 0x380e 552512c064SDave Stevenson #define OV5647_REG_VTS_LO 0x380f 56cef66734SJacob Chen #define OV5647_REG_FRAME_OFF_NUMBER 0x4202 57cef66734SJacob Chen #define OV5647_REG_MIPI_CTRL00 0x4800 58cef66734SJacob Chen #define OV5647_REG_MIPI_CTRL14 0x4814 594974c2f1SDavid Plowman #define OV5647_REG_AWB 0x5001 603c2472a3SRamiro Oliveira 613c2472a3SRamiro Oliveira #define REG_TERM 0xfffe 623c2472a3SRamiro Oliveira #define VAL_TERM 0xfe 633c2472a3SRamiro Oliveira #define REG_DLY 0xffff 643c2472a3SRamiro Oliveira 6514f70a32SDave Stevenson /* OV5647 native and active pixel array size */ 6614f70a32SDave Stevenson #define OV5647_NATIVE_WIDTH 2624U 6714f70a32SDave Stevenson #define OV5647_NATIVE_HEIGHT 1956U 683c2472a3SRamiro Oliveira 6914f70a32SDave Stevenson #define OV5647_PIXEL_ARRAY_LEFT 16U 7014f70a32SDave Stevenson #define OV5647_PIXEL_ARRAY_TOP 16U 7114f70a32SDave Stevenson #define OV5647_PIXEL_ARRAY_WIDTH 2592U 7214f70a32SDave Stevenson #define OV5647_PIXEL_ARRAY_HEIGHT 1944U 733c2472a3SRamiro Oliveira 742512c064SDave Stevenson #define OV5647_VBLANK_MIN 4 752512c064SDave Stevenson #define OV5647_VTS_MAX 32767 762512c064SDave Stevenson 77646a0249SDave Stevenson #define OV5647_EXPOSURE_MIN 4 78646a0249SDave Stevenson #define OV5647_EXPOSURE_STEP 1 79646a0249SDave Stevenson #define OV5647_EXPOSURE_DEFAULT 1000 80646a0249SDave Stevenson #define OV5647_EXPOSURE_MAX 65535 81646a0249SDave Stevenson 823c2472a3SRamiro Oliveira struct regval_list { 833c2472a3SRamiro Oliveira u16 addr; 843c2472a3SRamiro Oliveira u8 data; 853c2472a3SRamiro Oliveira }; 863c2472a3SRamiro Oliveira 87d7d6074eSJacopo Mondi struct ov5647_mode { 88d7d6074eSJacopo Mondi struct v4l2_mbus_framefmt format; 8914f70a32SDave Stevenson struct v4l2_rect crop; 90911f4516SDave Stevenson u64 pixel_rate; 91c6da1ae4SJacopo Mondi int hts; 922512c064SDave Stevenson int vts; 93d7d6074eSJacopo Mondi const struct regval_list *reg_list; 94d7d6074eSJacopo Mondi unsigned int num_regs; 95d7d6074eSJacopo Mondi }; 96d7d6074eSJacopo Mondi 97d7d6074eSJacopo Mondi struct ov5647_format_list { 98d7d6074eSJacopo Mondi unsigned int mbus_code; 99d7d6074eSJacopo Mondi const struct ov5647_mode *modes; 100d7d6074eSJacopo Mondi unsigned int num_modes; 101d7d6074eSJacopo Mondi }; 102d7d6074eSJacopo Mondi 1033c2472a3SRamiro Oliveira struct ov5647 { 1043c2472a3SRamiro Oliveira struct v4l2_subdev sd; 1053c2472a3SRamiro Oliveira struct media_pad pad; 1063c2472a3SRamiro Oliveira struct mutex lock; 1073c2472a3SRamiro Oliveira int power_count; 1083c2472a3SRamiro Oliveira struct clk *xclk; 109b050791dSDave Stevenson struct gpio_desc *pwdn; 110dea4fcfeSDave Stevenson bool clock_ncont; 1114974c2f1SDavid Plowman struct v4l2_ctrl_handler ctrls; 112d7d6074eSJacopo Mondi const struct ov5647_mode *mode; 113911f4516SDave Stevenson struct v4l2_ctrl *pixel_rate; 114c6da1ae4SJacopo Mondi struct v4l2_ctrl *hblank; 1152512c064SDave Stevenson struct v4l2_ctrl *vblank; 116646a0249SDave Stevenson struct v4l2_ctrl *exposure; 117*2f038c97SJacopo Mondi bool streaming; 1183c2472a3SRamiro Oliveira }; 1193c2472a3SRamiro Oliveira 1205bc5ca71SJacopo Mondi static inline struct ov5647 *to_sensor(struct v4l2_subdev *sd) 1213c2472a3SRamiro Oliveira { 1223c2472a3SRamiro Oliveira return container_of(sd, struct ov5647, sd); 1233c2472a3SRamiro Oliveira } 1243c2472a3SRamiro Oliveira 1253c2472a3SRamiro Oliveira static struct regval_list sensor_oe_disable_regs[] = { 1263c2472a3SRamiro Oliveira {0x3000, 0x00}, 1273c2472a3SRamiro Oliveira {0x3001, 0x00}, 1283c2472a3SRamiro Oliveira {0x3002, 0x00}, 1293c2472a3SRamiro Oliveira }; 1303c2472a3SRamiro Oliveira 1313c2472a3SRamiro Oliveira static struct regval_list sensor_oe_enable_regs[] = { 1323c2472a3SRamiro Oliveira {0x3000, 0x0f}, 1333c2472a3SRamiro Oliveira {0x3001, 0xff}, 1343c2472a3SRamiro Oliveira {0x3002, 0xe4}, 1353c2472a3SRamiro Oliveira }; 1363c2472a3SRamiro Oliveira 137e907bd66SJacopo Mondi static const struct regval_list ov5647_640x480_8bpp[] = { 1383c2472a3SRamiro Oliveira {0x0100, 0x00}, 1393c2472a3SRamiro Oliveira {0x0103, 0x01}, 1403c2472a3SRamiro Oliveira {0x3034, 0x08}, 1413c2472a3SRamiro Oliveira {0x3035, 0x21}, 1423c2472a3SRamiro Oliveira {0x3036, 0x46}, 1433c2472a3SRamiro Oliveira {0x303c, 0x11}, 1443c2472a3SRamiro Oliveira {0x3106, 0xf5}, 1453c2472a3SRamiro Oliveira {0x3821, 0x07}, 1463c2472a3SRamiro Oliveira {0x3820, 0x41}, 1473c2472a3SRamiro Oliveira {0x3827, 0xec}, 1483c2472a3SRamiro Oliveira {0x370c, 0x0f}, 1493c2472a3SRamiro Oliveira {0x3612, 0x59}, 1503c2472a3SRamiro Oliveira {0x3618, 0x00}, 1513c2472a3SRamiro Oliveira {0x5000, 0x06}, 1523c2472a3SRamiro Oliveira {0x5002, 0x41}, 1533c2472a3SRamiro Oliveira {0x5003, 0x08}, 1543c2472a3SRamiro Oliveira {0x5a00, 0x08}, 1553c2472a3SRamiro Oliveira {0x3000, 0x00}, 1563c2472a3SRamiro Oliveira {0x3001, 0x00}, 1573c2472a3SRamiro Oliveira {0x3002, 0x00}, 1583c2472a3SRamiro Oliveira {0x3016, 0x08}, 1593c2472a3SRamiro Oliveira {0x3017, 0xe0}, 1603c2472a3SRamiro Oliveira {0x3018, 0x44}, 1613c2472a3SRamiro Oliveira {0x301c, 0xf8}, 1623c2472a3SRamiro Oliveira {0x301d, 0xf0}, 1633c2472a3SRamiro Oliveira {0x3a18, 0x00}, 1643c2472a3SRamiro Oliveira {0x3a19, 0xf8}, 1653c2472a3SRamiro Oliveira {0x3c01, 0x80}, 1663c2472a3SRamiro Oliveira {0x3b07, 0x0c}, 1673c2472a3SRamiro Oliveira {0x380c, 0x07}, 1683c2472a3SRamiro Oliveira {0x380d, 0x68}, 1693c2472a3SRamiro Oliveira {0x3814, 0x31}, 1703c2472a3SRamiro Oliveira {0x3815, 0x31}, 1713c2472a3SRamiro Oliveira {0x3708, 0x64}, 1723c2472a3SRamiro Oliveira {0x3709, 0x52}, 1733c2472a3SRamiro Oliveira {0x3808, 0x02}, 1743c2472a3SRamiro Oliveira {0x3809, 0x80}, 1753c2472a3SRamiro Oliveira {0x380a, 0x01}, 176c9a05cecSJacopo Mondi {0x380b, 0xe0}, 1773c2472a3SRamiro Oliveira {0x3801, 0x00}, 1783c2472a3SRamiro Oliveira {0x3802, 0x00}, 1793c2472a3SRamiro Oliveira {0x3803, 0x00}, 1803c2472a3SRamiro Oliveira {0x3804, 0x0a}, 1813c2472a3SRamiro Oliveira {0x3805, 0x3f}, 1823c2472a3SRamiro Oliveira {0x3806, 0x07}, 1833c2472a3SRamiro Oliveira {0x3807, 0xa1}, 1843c2472a3SRamiro Oliveira {0x3811, 0x08}, 1853c2472a3SRamiro Oliveira {0x3813, 0x02}, 1863c2472a3SRamiro Oliveira {0x3630, 0x2e}, 1873c2472a3SRamiro Oliveira {0x3632, 0xe2}, 1883c2472a3SRamiro Oliveira {0x3633, 0x23}, 1893c2472a3SRamiro Oliveira {0x3634, 0x44}, 1903c2472a3SRamiro Oliveira {0x3636, 0x06}, 1913c2472a3SRamiro Oliveira {0x3620, 0x64}, 1923c2472a3SRamiro Oliveira {0x3621, 0xe0}, 1933c2472a3SRamiro Oliveira {0x3600, 0x37}, 1943c2472a3SRamiro Oliveira {0x3704, 0xa0}, 1953c2472a3SRamiro Oliveira {0x3703, 0x5a}, 1963c2472a3SRamiro Oliveira {0x3715, 0x78}, 1973c2472a3SRamiro Oliveira {0x3717, 0x01}, 1983c2472a3SRamiro Oliveira {0x3731, 0x02}, 1993c2472a3SRamiro Oliveira {0x370b, 0x60}, 2003c2472a3SRamiro Oliveira {0x3705, 0x1a}, 2013c2472a3SRamiro Oliveira {0x3f05, 0x02}, 2023c2472a3SRamiro Oliveira {0x3f06, 0x10}, 2033c2472a3SRamiro Oliveira {0x3f01, 0x0a}, 2043c2472a3SRamiro Oliveira {0x3a08, 0x01}, 2053c2472a3SRamiro Oliveira {0x3a09, 0x27}, 2063c2472a3SRamiro Oliveira {0x3a0a, 0x00}, 2073c2472a3SRamiro Oliveira {0x3a0b, 0xf6}, 2083c2472a3SRamiro Oliveira {0x3a0d, 0x04}, 2093c2472a3SRamiro Oliveira {0x3a0e, 0x03}, 2103c2472a3SRamiro Oliveira {0x3a0f, 0x58}, 2113c2472a3SRamiro Oliveira {0x3a10, 0x50}, 2123c2472a3SRamiro Oliveira {0x3a1b, 0x58}, 2133c2472a3SRamiro Oliveira {0x3a1e, 0x50}, 2143c2472a3SRamiro Oliveira {0x3a11, 0x60}, 2153c2472a3SRamiro Oliveira {0x3a1f, 0x28}, 2163c2472a3SRamiro Oliveira {0x4001, 0x02}, 2173c2472a3SRamiro Oliveira {0x4004, 0x02}, 2183c2472a3SRamiro Oliveira {0x4000, 0x09}, 2193c2472a3SRamiro Oliveira {0x4837, 0x24}, 2203c2472a3SRamiro Oliveira {0x4050, 0x6e}, 2213c2472a3SRamiro Oliveira {0x4051, 0x8f}, 2223c2472a3SRamiro Oliveira {0x0100, 0x01}, 2233c2472a3SRamiro Oliveira }; 2243c2472a3SRamiro Oliveira 225a8df5af6SJacopo Mondi static struct regval_list ov5647_2592x1944_10bpp[] = { 226a8df5af6SJacopo Mondi {0x0100, 0x00}, 227a8df5af6SJacopo Mondi {0x0103, 0x01}, 228a8df5af6SJacopo Mondi {0x3034, 0x1a}, 229a8df5af6SJacopo Mondi {0x3035, 0x21}, 230a8df5af6SJacopo Mondi {0x3036, 0x69}, 231a8df5af6SJacopo Mondi {0x303c, 0x11}, 232a8df5af6SJacopo Mondi {0x3106, 0xf5}, 233a8df5af6SJacopo Mondi {0x3821, 0x06}, 234a8df5af6SJacopo Mondi {0x3820, 0x00}, 235a8df5af6SJacopo Mondi {0x3827, 0xec}, 236a8df5af6SJacopo Mondi {0x370c, 0x03}, 237a8df5af6SJacopo Mondi {0x3612, 0x5b}, 238a8df5af6SJacopo Mondi {0x3618, 0x04}, 239a8df5af6SJacopo Mondi {0x5000, 0x06}, 240a8df5af6SJacopo Mondi {0x5002, 0x41}, 241a8df5af6SJacopo Mondi {0x5003, 0x08}, 242a8df5af6SJacopo Mondi {0x5a00, 0x08}, 243a8df5af6SJacopo Mondi {0x3000, 0x00}, 244a8df5af6SJacopo Mondi {0x3001, 0x00}, 245a8df5af6SJacopo Mondi {0x3002, 0x00}, 246a8df5af6SJacopo Mondi {0x3016, 0x08}, 247a8df5af6SJacopo Mondi {0x3017, 0xe0}, 248a8df5af6SJacopo Mondi {0x3018, 0x44}, 249a8df5af6SJacopo Mondi {0x301c, 0xf8}, 250a8df5af6SJacopo Mondi {0x301d, 0xf0}, 251a8df5af6SJacopo Mondi {0x3a18, 0x00}, 252a8df5af6SJacopo Mondi {0x3a19, 0xf8}, 253a8df5af6SJacopo Mondi {0x3c01, 0x80}, 254a8df5af6SJacopo Mondi {0x3b07, 0x0c}, 255a8df5af6SJacopo Mondi {0x380c, 0x0b}, 256a8df5af6SJacopo Mondi {0x380d, 0x1c}, 257a8df5af6SJacopo Mondi {0x3814, 0x11}, 258a8df5af6SJacopo Mondi {0x3815, 0x11}, 259a8df5af6SJacopo Mondi {0x3708, 0x64}, 260a8df5af6SJacopo Mondi {0x3709, 0x12}, 261a8df5af6SJacopo Mondi {0x3808, 0x0a}, 262a8df5af6SJacopo Mondi {0x3809, 0x20}, 263a8df5af6SJacopo Mondi {0x380a, 0x07}, 264a8df5af6SJacopo Mondi {0x380b, 0x98}, 265a8df5af6SJacopo Mondi {0x3800, 0x00}, 266a8df5af6SJacopo Mondi {0x3801, 0x00}, 267a8df5af6SJacopo Mondi {0x3802, 0x00}, 268a8df5af6SJacopo Mondi {0x3803, 0x00}, 269a8df5af6SJacopo Mondi {0x3804, 0x0a}, 270a8df5af6SJacopo Mondi {0x3805, 0x3f}, 271a8df5af6SJacopo Mondi {0x3806, 0x07}, 272a8df5af6SJacopo Mondi {0x3807, 0xa3}, 273a8df5af6SJacopo Mondi {0x3811, 0x10}, 274a8df5af6SJacopo Mondi {0x3813, 0x06}, 275a8df5af6SJacopo Mondi {0x3630, 0x2e}, 276a8df5af6SJacopo Mondi {0x3632, 0xe2}, 277a8df5af6SJacopo Mondi {0x3633, 0x23}, 278a8df5af6SJacopo Mondi {0x3634, 0x44}, 279a8df5af6SJacopo Mondi {0x3636, 0x06}, 280a8df5af6SJacopo Mondi {0x3620, 0x64}, 281a8df5af6SJacopo Mondi {0x3621, 0xe0}, 282a8df5af6SJacopo Mondi {0x3600, 0x37}, 283a8df5af6SJacopo Mondi {0x3704, 0xa0}, 284a8df5af6SJacopo Mondi {0x3703, 0x5a}, 285a8df5af6SJacopo Mondi {0x3715, 0x78}, 286a8df5af6SJacopo Mondi {0x3717, 0x01}, 287a8df5af6SJacopo Mondi {0x3731, 0x02}, 288a8df5af6SJacopo Mondi {0x370b, 0x60}, 289a8df5af6SJacopo Mondi {0x3705, 0x1a}, 290a8df5af6SJacopo Mondi {0x3f05, 0x02}, 291a8df5af6SJacopo Mondi {0x3f06, 0x10}, 292a8df5af6SJacopo Mondi {0x3f01, 0x0a}, 293a8df5af6SJacopo Mondi {0x3a08, 0x01}, 294a8df5af6SJacopo Mondi {0x3a09, 0x28}, 295a8df5af6SJacopo Mondi {0x3a0a, 0x00}, 296a8df5af6SJacopo Mondi {0x3a0b, 0xf6}, 297a8df5af6SJacopo Mondi {0x3a0d, 0x08}, 298a8df5af6SJacopo Mondi {0x3a0e, 0x06}, 299a8df5af6SJacopo Mondi {0x3a0f, 0x58}, 300a8df5af6SJacopo Mondi {0x3a10, 0x50}, 301a8df5af6SJacopo Mondi {0x3a1b, 0x58}, 302a8df5af6SJacopo Mondi {0x3a1e, 0x50}, 303a8df5af6SJacopo Mondi {0x3a11, 0x60}, 304a8df5af6SJacopo Mondi {0x3a1f, 0x28}, 305a8df5af6SJacopo Mondi {0x4001, 0x02}, 306a8df5af6SJacopo Mondi {0x4004, 0x04}, 307a8df5af6SJacopo Mondi {0x4000, 0x09}, 308a8df5af6SJacopo Mondi {0x4837, 0x19}, 309a8df5af6SJacopo Mondi {0x4800, 0x24}, 310a8df5af6SJacopo Mondi {0x3503, 0x03}, 311a8df5af6SJacopo Mondi {0x0100, 0x01}, 312a8df5af6SJacopo Mondi }; 313a8df5af6SJacopo Mondi 314a8df5af6SJacopo Mondi static struct regval_list ov5647_1080p30_10bpp[] = { 315a8df5af6SJacopo Mondi {0x0100, 0x00}, 316a8df5af6SJacopo Mondi {0x0103, 0x01}, 317a8df5af6SJacopo Mondi {0x3034, 0x1a}, 318a8df5af6SJacopo Mondi {0x3035, 0x21}, 319a8df5af6SJacopo Mondi {0x3036, 0x62}, 320a8df5af6SJacopo Mondi {0x303c, 0x11}, 321a8df5af6SJacopo Mondi {0x3106, 0xf5}, 322a8df5af6SJacopo Mondi {0x3821, 0x06}, 323a8df5af6SJacopo Mondi {0x3820, 0x00}, 324a8df5af6SJacopo Mondi {0x3827, 0xec}, 325a8df5af6SJacopo Mondi {0x370c, 0x03}, 326a8df5af6SJacopo Mondi {0x3612, 0x5b}, 327a8df5af6SJacopo Mondi {0x3618, 0x04}, 328a8df5af6SJacopo Mondi {0x5000, 0x06}, 329a8df5af6SJacopo Mondi {0x5002, 0x41}, 330a8df5af6SJacopo Mondi {0x5003, 0x08}, 331a8df5af6SJacopo Mondi {0x5a00, 0x08}, 332a8df5af6SJacopo Mondi {0x3000, 0x00}, 333a8df5af6SJacopo Mondi {0x3001, 0x00}, 334a8df5af6SJacopo Mondi {0x3002, 0x00}, 335a8df5af6SJacopo Mondi {0x3016, 0x08}, 336a8df5af6SJacopo Mondi {0x3017, 0xe0}, 337a8df5af6SJacopo Mondi {0x3018, 0x44}, 338a8df5af6SJacopo Mondi {0x301c, 0xf8}, 339a8df5af6SJacopo Mondi {0x301d, 0xf0}, 340a8df5af6SJacopo Mondi {0x3a18, 0x00}, 341a8df5af6SJacopo Mondi {0x3a19, 0xf8}, 342a8df5af6SJacopo Mondi {0x3c01, 0x80}, 343a8df5af6SJacopo Mondi {0x3b07, 0x0c}, 344a8df5af6SJacopo Mondi {0x380c, 0x09}, 345a8df5af6SJacopo Mondi {0x380d, 0x70}, 346a8df5af6SJacopo Mondi {0x3814, 0x11}, 347a8df5af6SJacopo Mondi {0x3815, 0x11}, 348a8df5af6SJacopo Mondi {0x3708, 0x64}, 349a8df5af6SJacopo Mondi {0x3709, 0x12}, 350a8df5af6SJacopo Mondi {0x3808, 0x07}, 351a8df5af6SJacopo Mondi {0x3809, 0x80}, 352a8df5af6SJacopo Mondi {0x380a, 0x04}, 353a8df5af6SJacopo Mondi {0x380b, 0x38}, 354a8df5af6SJacopo Mondi {0x3800, 0x01}, 355a8df5af6SJacopo Mondi {0x3801, 0x5c}, 356a8df5af6SJacopo Mondi {0x3802, 0x01}, 357a8df5af6SJacopo Mondi {0x3803, 0xb2}, 358a8df5af6SJacopo Mondi {0x3804, 0x08}, 359a8df5af6SJacopo Mondi {0x3805, 0xe3}, 360a8df5af6SJacopo Mondi {0x3806, 0x05}, 361a8df5af6SJacopo Mondi {0x3807, 0xf1}, 362a8df5af6SJacopo Mondi {0x3811, 0x04}, 363a8df5af6SJacopo Mondi {0x3813, 0x02}, 364a8df5af6SJacopo Mondi {0x3630, 0x2e}, 365a8df5af6SJacopo Mondi {0x3632, 0xe2}, 366a8df5af6SJacopo Mondi {0x3633, 0x23}, 367a8df5af6SJacopo Mondi {0x3634, 0x44}, 368a8df5af6SJacopo Mondi {0x3636, 0x06}, 369a8df5af6SJacopo Mondi {0x3620, 0x64}, 370a8df5af6SJacopo Mondi {0x3621, 0xe0}, 371a8df5af6SJacopo Mondi {0x3600, 0x37}, 372a8df5af6SJacopo Mondi {0x3704, 0xa0}, 373a8df5af6SJacopo Mondi {0x3703, 0x5a}, 374a8df5af6SJacopo Mondi {0x3715, 0x78}, 375a8df5af6SJacopo Mondi {0x3717, 0x01}, 376a8df5af6SJacopo Mondi {0x3731, 0x02}, 377a8df5af6SJacopo Mondi {0x370b, 0x60}, 378a8df5af6SJacopo Mondi {0x3705, 0x1a}, 379a8df5af6SJacopo Mondi {0x3f05, 0x02}, 380a8df5af6SJacopo Mondi {0x3f06, 0x10}, 381a8df5af6SJacopo Mondi {0x3f01, 0x0a}, 382a8df5af6SJacopo Mondi {0x3a08, 0x01}, 383a8df5af6SJacopo Mondi {0x3a09, 0x4b}, 384a8df5af6SJacopo Mondi {0x3a0a, 0x01}, 385a8df5af6SJacopo Mondi {0x3a0b, 0x13}, 386a8df5af6SJacopo Mondi {0x3a0d, 0x04}, 387a8df5af6SJacopo Mondi {0x3a0e, 0x03}, 388a8df5af6SJacopo Mondi {0x3a0f, 0x58}, 389a8df5af6SJacopo Mondi {0x3a10, 0x50}, 390a8df5af6SJacopo Mondi {0x3a1b, 0x58}, 391a8df5af6SJacopo Mondi {0x3a1e, 0x50}, 392a8df5af6SJacopo Mondi {0x3a11, 0x60}, 393a8df5af6SJacopo Mondi {0x3a1f, 0x28}, 394a8df5af6SJacopo Mondi {0x4001, 0x02}, 395a8df5af6SJacopo Mondi {0x4004, 0x04}, 396a8df5af6SJacopo Mondi {0x4000, 0x09}, 397a8df5af6SJacopo Mondi {0x4837, 0x19}, 398a8df5af6SJacopo Mondi {0x4800, 0x34}, 399a8df5af6SJacopo Mondi {0x3503, 0x03}, 400a8df5af6SJacopo Mondi {0x0100, 0x01}, 401a8df5af6SJacopo Mondi }; 402a8df5af6SJacopo Mondi 403a8df5af6SJacopo Mondi static struct regval_list ov5647_2x2binned_10bpp[] = { 404a8df5af6SJacopo Mondi {0x0100, 0x00}, 405a8df5af6SJacopo Mondi {0x0103, 0x01}, 406a8df5af6SJacopo Mondi {0x3034, 0x1a}, 407a8df5af6SJacopo Mondi {0x3035, 0x21}, 408a8df5af6SJacopo Mondi {0x3036, 0x62}, 409a8df5af6SJacopo Mondi {0x303c, 0x11}, 410a8df5af6SJacopo Mondi {0x3106, 0xf5}, 411a8df5af6SJacopo Mondi {0x3827, 0xec}, 412a8df5af6SJacopo Mondi {0x370c, 0x03}, 413a8df5af6SJacopo Mondi {0x3612, 0x59}, 414a8df5af6SJacopo Mondi {0x3618, 0x00}, 415a8df5af6SJacopo Mondi {0x5000, 0x06}, 416a8df5af6SJacopo Mondi {0x5002, 0x41}, 417a8df5af6SJacopo Mondi {0x5003, 0x08}, 418a8df5af6SJacopo Mondi {0x5a00, 0x08}, 419a8df5af6SJacopo Mondi {0x3000, 0x00}, 420a8df5af6SJacopo Mondi {0x3001, 0x00}, 421a8df5af6SJacopo Mondi {0x3002, 0x00}, 422a8df5af6SJacopo Mondi {0x3016, 0x08}, 423a8df5af6SJacopo Mondi {0x3017, 0xe0}, 424a8df5af6SJacopo Mondi {0x3018, 0x44}, 425a8df5af6SJacopo Mondi {0x301c, 0xf8}, 426a8df5af6SJacopo Mondi {0x301d, 0xf0}, 427a8df5af6SJacopo Mondi {0x3a18, 0x00}, 428a8df5af6SJacopo Mondi {0x3a19, 0xf8}, 429a8df5af6SJacopo Mondi {0x3c01, 0x80}, 430a8df5af6SJacopo Mondi {0x3b07, 0x0c}, 431a8df5af6SJacopo Mondi {0x3800, 0x00}, 432a8df5af6SJacopo Mondi {0x3801, 0x00}, 433a8df5af6SJacopo Mondi {0x3802, 0x00}, 434a8df5af6SJacopo Mondi {0x3803, 0x00}, 435a8df5af6SJacopo Mondi {0x3804, 0x0a}, 436a8df5af6SJacopo Mondi {0x3805, 0x3f}, 437a8df5af6SJacopo Mondi {0x3806, 0x07}, 438a8df5af6SJacopo Mondi {0x3807, 0xa3}, 439a8df5af6SJacopo Mondi {0x3808, 0x05}, 440a8df5af6SJacopo Mondi {0x3809, 0x10}, 441a8df5af6SJacopo Mondi {0x380a, 0x03}, 442a8df5af6SJacopo Mondi {0x380b, 0xcc}, 443a8df5af6SJacopo Mondi {0x380c, 0x07}, 444a8df5af6SJacopo Mondi {0x380d, 0x68}, 445a8df5af6SJacopo Mondi {0x3811, 0x0c}, 446a8df5af6SJacopo Mondi {0x3813, 0x06}, 447a8df5af6SJacopo Mondi {0x3814, 0x31}, 448a8df5af6SJacopo Mondi {0x3815, 0x31}, 449a8df5af6SJacopo Mondi {0x3630, 0x2e}, 450a8df5af6SJacopo Mondi {0x3632, 0xe2}, 451a8df5af6SJacopo Mondi {0x3633, 0x23}, 452a8df5af6SJacopo Mondi {0x3634, 0x44}, 453a8df5af6SJacopo Mondi {0x3636, 0x06}, 454a8df5af6SJacopo Mondi {0x3620, 0x64}, 455a8df5af6SJacopo Mondi {0x3621, 0xe0}, 456a8df5af6SJacopo Mondi {0x3600, 0x37}, 457a8df5af6SJacopo Mondi {0x3704, 0xa0}, 458a8df5af6SJacopo Mondi {0x3703, 0x5a}, 459a8df5af6SJacopo Mondi {0x3715, 0x78}, 460a8df5af6SJacopo Mondi {0x3717, 0x01}, 461a8df5af6SJacopo Mondi {0x3731, 0x02}, 462a8df5af6SJacopo Mondi {0x370b, 0x60}, 463a8df5af6SJacopo Mondi {0x3705, 0x1a}, 464a8df5af6SJacopo Mondi {0x3f05, 0x02}, 465a8df5af6SJacopo Mondi {0x3f06, 0x10}, 466a8df5af6SJacopo Mondi {0x3f01, 0x0a}, 467a8df5af6SJacopo Mondi {0x3a08, 0x01}, 468a8df5af6SJacopo Mondi {0x3a09, 0x28}, 469a8df5af6SJacopo Mondi {0x3a0a, 0x00}, 470a8df5af6SJacopo Mondi {0x3a0b, 0xf6}, 471a8df5af6SJacopo Mondi {0x3a0d, 0x08}, 472a8df5af6SJacopo Mondi {0x3a0e, 0x06}, 473a8df5af6SJacopo Mondi {0x3a0f, 0x58}, 474a8df5af6SJacopo Mondi {0x3a10, 0x50}, 475a8df5af6SJacopo Mondi {0x3a1b, 0x58}, 476a8df5af6SJacopo Mondi {0x3a1e, 0x50}, 477a8df5af6SJacopo Mondi {0x3a11, 0x60}, 478a8df5af6SJacopo Mondi {0x3a1f, 0x28}, 479a8df5af6SJacopo Mondi {0x4001, 0x02}, 480a8df5af6SJacopo Mondi {0x4004, 0x04}, 481a8df5af6SJacopo Mondi {0x4000, 0x09}, 482a8df5af6SJacopo Mondi {0x4837, 0x16}, 483a8df5af6SJacopo Mondi {0x4800, 0x24}, 484a8df5af6SJacopo Mondi {0x3503, 0x03}, 485a8df5af6SJacopo Mondi {0x3820, 0x41}, 486a8df5af6SJacopo Mondi {0x3821, 0x07}, 487a8df5af6SJacopo Mondi {0x350a, 0x00}, 488a8df5af6SJacopo Mondi {0x350b, 0x10}, 489a8df5af6SJacopo Mondi {0x3500, 0x00}, 490a8df5af6SJacopo Mondi {0x3501, 0x1a}, 491a8df5af6SJacopo Mondi {0x3502, 0xf0}, 492a8df5af6SJacopo Mondi {0x3212, 0xa0}, 493a8df5af6SJacopo Mondi {0x0100, 0x01}, 494a8df5af6SJacopo Mondi }; 495a8df5af6SJacopo Mondi 496a8df5af6SJacopo Mondi static struct regval_list ov5647_640x480_10bpp[] = { 497a8df5af6SJacopo Mondi {0x0100, 0x00}, 498a8df5af6SJacopo Mondi {0x0103, 0x01}, 499a8df5af6SJacopo Mondi {0x3035, 0x11}, 500a8df5af6SJacopo Mondi {0x3036, 0x46}, 501a8df5af6SJacopo Mondi {0x303c, 0x11}, 502a8df5af6SJacopo Mondi {0x3821, 0x07}, 503a8df5af6SJacopo Mondi {0x3820, 0x41}, 504a8df5af6SJacopo Mondi {0x370c, 0x03}, 505a8df5af6SJacopo Mondi {0x3612, 0x59}, 506a8df5af6SJacopo Mondi {0x3618, 0x00}, 507a8df5af6SJacopo Mondi {0x5000, 0x06}, 508a8df5af6SJacopo Mondi {0x5003, 0x08}, 509a8df5af6SJacopo Mondi {0x5a00, 0x08}, 510a8df5af6SJacopo Mondi {0x3000, 0xff}, 511a8df5af6SJacopo Mondi {0x3001, 0xff}, 512a8df5af6SJacopo Mondi {0x3002, 0xff}, 513a8df5af6SJacopo Mondi {0x301d, 0xf0}, 514a8df5af6SJacopo Mondi {0x3a18, 0x00}, 515a8df5af6SJacopo Mondi {0x3a19, 0xf8}, 516a8df5af6SJacopo Mondi {0x3c01, 0x80}, 517a8df5af6SJacopo Mondi {0x3b07, 0x0c}, 518a8df5af6SJacopo Mondi {0x380c, 0x07}, 519a8df5af6SJacopo Mondi {0x380d, 0x3c}, 520a8df5af6SJacopo Mondi {0x3814, 0x35}, 521a8df5af6SJacopo Mondi {0x3815, 0x35}, 522a8df5af6SJacopo Mondi {0x3708, 0x64}, 523a8df5af6SJacopo Mondi {0x3709, 0x52}, 524a8df5af6SJacopo Mondi {0x3808, 0x02}, 525a8df5af6SJacopo Mondi {0x3809, 0x80}, 526a8df5af6SJacopo Mondi {0x380a, 0x01}, 527a8df5af6SJacopo Mondi {0x380b, 0xe0}, 528a8df5af6SJacopo Mondi {0x3800, 0x00}, 529a8df5af6SJacopo Mondi {0x3801, 0x10}, 530a8df5af6SJacopo Mondi {0x3802, 0x00}, 531a8df5af6SJacopo Mondi {0x3803, 0x00}, 532a8df5af6SJacopo Mondi {0x3804, 0x0a}, 533a8df5af6SJacopo Mondi {0x3805, 0x2f}, 534a8df5af6SJacopo Mondi {0x3806, 0x07}, 535a8df5af6SJacopo Mondi {0x3807, 0x9f}, 536a8df5af6SJacopo Mondi {0x3630, 0x2e}, 537a8df5af6SJacopo Mondi {0x3632, 0xe2}, 538a8df5af6SJacopo Mondi {0x3633, 0x23}, 539a8df5af6SJacopo Mondi {0x3634, 0x44}, 540a8df5af6SJacopo Mondi {0x3620, 0x64}, 541a8df5af6SJacopo Mondi {0x3621, 0xe0}, 542a8df5af6SJacopo Mondi {0x3600, 0x37}, 543a8df5af6SJacopo Mondi {0x3704, 0xa0}, 544a8df5af6SJacopo Mondi {0x3703, 0x5a}, 545a8df5af6SJacopo Mondi {0x3715, 0x78}, 546a8df5af6SJacopo Mondi {0x3717, 0x01}, 547a8df5af6SJacopo Mondi {0x3731, 0x02}, 548a8df5af6SJacopo Mondi {0x370b, 0x60}, 549a8df5af6SJacopo Mondi {0x3705, 0x1a}, 550a8df5af6SJacopo Mondi {0x3f05, 0x02}, 551a8df5af6SJacopo Mondi {0x3f06, 0x10}, 552a8df5af6SJacopo Mondi {0x3f01, 0x0a}, 553a8df5af6SJacopo Mondi {0x3a08, 0x01}, 554a8df5af6SJacopo Mondi {0x3a09, 0x2e}, 555a8df5af6SJacopo Mondi {0x3a0a, 0x00}, 556a8df5af6SJacopo Mondi {0x3a0b, 0xfb}, 557a8df5af6SJacopo Mondi {0x3a0d, 0x02}, 558a8df5af6SJacopo Mondi {0x3a0e, 0x01}, 559a8df5af6SJacopo Mondi {0x3a0f, 0x58}, 560a8df5af6SJacopo Mondi {0x3a10, 0x50}, 561a8df5af6SJacopo Mondi {0x3a1b, 0x58}, 562a8df5af6SJacopo Mondi {0x3a1e, 0x50}, 563a8df5af6SJacopo Mondi {0x3a11, 0x60}, 564a8df5af6SJacopo Mondi {0x3a1f, 0x28}, 565a8df5af6SJacopo Mondi {0x4001, 0x02}, 566a8df5af6SJacopo Mondi {0x4004, 0x02}, 567a8df5af6SJacopo Mondi {0x4000, 0x09}, 568a8df5af6SJacopo Mondi {0x3000, 0x00}, 569a8df5af6SJacopo Mondi {0x3001, 0x00}, 570a8df5af6SJacopo Mondi {0x3002, 0x00}, 571a8df5af6SJacopo Mondi {0x3017, 0xe0}, 572a8df5af6SJacopo Mondi {0x301c, 0xfc}, 573a8df5af6SJacopo Mondi {0x3636, 0x06}, 574a8df5af6SJacopo Mondi {0x3016, 0x08}, 575a8df5af6SJacopo Mondi {0x3827, 0xec}, 576a8df5af6SJacopo Mondi {0x3018, 0x44}, 577a8df5af6SJacopo Mondi {0x3035, 0x21}, 578a8df5af6SJacopo Mondi {0x3106, 0xf5}, 579a8df5af6SJacopo Mondi {0x3034, 0x1a}, 580a8df5af6SJacopo Mondi {0x301c, 0xf8}, 581a8df5af6SJacopo Mondi {0x4800, 0x34}, 582a8df5af6SJacopo Mondi {0x3503, 0x03}, 583a8df5af6SJacopo Mondi {0x0100, 0x01}, 584a8df5af6SJacopo Mondi }; 585a8df5af6SJacopo Mondi 586e907bd66SJacopo Mondi static const struct ov5647_mode ov5647_8bpp_modes[] = { 587e907bd66SJacopo Mondi /* 8-bit VGA mode: Uncentred crop 2x2 binned 1296x972 image. */ 588d7d6074eSJacopo Mondi { 589d7d6074eSJacopo Mondi .format = { 590d7d6074eSJacopo Mondi .code = MEDIA_BUS_FMT_SBGGR8_1X8, 591d7d6074eSJacopo Mondi .colorspace = V4L2_COLORSPACE_SRGB, 592d7d6074eSJacopo Mondi .field = V4L2_FIELD_NONE, 593d7d6074eSJacopo Mondi .width = 640, 594d7d6074eSJacopo Mondi .height = 480 595d7d6074eSJacopo Mondi }, 59614f70a32SDave Stevenson .crop = { 59714f70a32SDave Stevenson .left = OV5647_PIXEL_ARRAY_LEFT, 59814f70a32SDave Stevenson .top = OV5647_PIXEL_ARRAY_TOP, 59914f70a32SDave Stevenson .width = 1280, 60014f70a32SDave Stevenson .height = 960, 60114f70a32SDave Stevenson }, 602911f4516SDave Stevenson .pixel_rate = 77291670, 603c6da1ae4SJacopo Mondi .hts = 1896, 6042512c064SDave Stevenson .vts = 0x3d8, 605e907bd66SJacopo Mondi .reg_list = ov5647_640x480_8bpp, 606e907bd66SJacopo Mondi .num_regs = ARRAY_SIZE(ov5647_640x480_8bpp) 607d7d6074eSJacopo Mondi }, 608d7d6074eSJacopo Mondi }; 609d7d6074eSJacopo Mondi 610a8df5af6SJacopo Mondi static const struct ov5647_mode ov5647_10bpp_modes[] = { 611a8df5af6SJacopo Mondi /* 2592x1944 full resolution full FOV 10-bit mode. */ 612a8df5af6SJacopo Mondi { 613a8df5af6SJacopo Mondi .format = { 614a8df5af6SJacopo Mondi .code = MEDIA_BUS_FMT_SBGGR10_1X10, 615a8df5af6SJacopo Mondi .colorspace = V4L2_COLORSPACE_SRGB, 616a8df5af6SJacopo Mondi .field = V4L2_FIELD_NONE, 617a8df5af6SJacopo Mondi .width = 2592, 618a8df5af6SJacopo Mondi .height = 1944 619a8df5af6SJacopo Mondi }, 620a8df5af6SJacopo Mondi .crop = { 621a8df5af6SJacopo Mondi .left = OV5647_PIXEL_ARRAY_LEFT, 622a8df5af6SJacopo Mondi .top = OV5647_PIXEL_ARRAY_TOP, 623a8df5af6SJacopo Mondi .width = 2592, 624a8df5af6SJacopo Mondi .height = 1944 625a8df5af6SJacopo Mondi }, 626911f4516SDave Stevenson .pixel_rate = 87500000, 627c6da1ae4SJacopo Mondi .hts = 2844, 6282512c064SDave Stevenson .vts = 0x7b0, 629a8df5af6SJacopo Mondi .reg_list = ov5647_2592x1944_10bpp, 630a8df5af6SJacopo Mondi .num_regs = ARRAY_SIZE(ov5647_2592x1944_10bpp) 631a8df5af6SJacopo Mondi }, 632a8df5af6SJacopo Mondi /* 1080p30 10-bit mode. Full resolution centre-cropped down to 1080p. */ 633a8df5af6SJacopo Mondi { 634a8df5af6SJacopo Mondi .format = { 635a8df5af6SJacopo Mondi .code = MEDIA_BUS_FMT_SBGGR10_1X10, 636a8df5af6SJacopo Mondi .colorspace = V4L2_COLORSPACE_SRGB, 637a8df5af6SJacopo Mondi .field = V4L2_FIELD_NONE, 638a8df5af6SJacopo Mondi .width = 1920, 639a8df5af6SJacopo Mondi .height = 1080 640a8df5af6SJacopo Mondi }, 641a8df5af6SJacopo Mondi .crop = { 642a8df5af6SJacopo Mondi .left = 348 + OV5647_PIXEL_ARRAY_LEFT, 643a8df5af6SJacopo Mondi .top = 434 + OV5647_PIXEL_ARRAY_TOP, 644a8df5af6SJacopo Mondi .width = 1928, 645a8df5af6SJacopo Mondi .height = 1080, 646a8df5af6SJacopo Mondi }, 647911f4516SDave Stevenson .pixel_rate = 81666700, 648c6da1ae4SJacopo Mondi .hts = 2416, 6492512c064SDave Stevenson .vts = 0x450, 650a8df5af6SJacopo Mondi .reg_list = ov5647_1080p30_10bpp, 651a8df5af6SJacopo Mondi .num_regs = ARRAY_SIZE(ov5647_1080p30_10bpp) 652a8df5af6SJacopo Mondi }, 653a8df5af6SJacopo Mondi /* 2x2 binned full FOV 10-bit mode. */ 654a8df5af6SJacopo Mondi { 655a8df5af6SJacopo Mondi .format = { 656a8df5af6SJacopo Mondi .code = MEDIA_BUS_FMT_SBGGR10_1X10, 657a8df5af6SJacopo Mondi .colorspace = V4L2_COLORSPACE_SRGB, 658a8df5af6SJacopo Mondi .field = V4L2_FIELD_NONE, 659a8df5af6SJacopo Mondi .width = 1296, 660a8df5af6SJacopo Mondi .height = 972 661a8df5af6SJacopo Mondi }, 662a8df5af6SJacopo Mondi .crop = { 663a8df5af6SJacopo Mondi .left = OV5647_PIXEL_ARRAY_LEFT, 664a8df5af6SJacopo Mondi .top = OV5647_PIXEL_ARRAY_TOP, 665a8df5af6SJacopo Mondi .width = 2592, 666a8df5af6SJacopo Mondi .height = 1944, 667a8df5af6SJacopo Mondi }, 668911f4516SDave Stevenson .pixel_rate = 81666700, 669c6da1ae4SJacopo Mondi .hts = 1896, 6702512c064SDave Stevenson .vts = 0x59b, 671a8df5af6SJacopo Mondi .reg_list = ov5647_2x2binned_10bpp, 672a8df5af6SJacopo Mondi .num_regs = ARRAY_SIZE(ov5647_2x2binned_10bpp) 673a8df5af6SJacopo Mondi }, 674a8df5af6SJacopo Mondi /* 10-bit VGA full FOV 60fps. 2x2 binned and subsampled down to VGA. */ 675a8df5af6SJacopo Mondi { 676a8df5af6SJacopo Mondi .format = { 677a8df5af6SJacopo Mondi .code = MEDIA_BUS_FMT_SBGGR10_1X10, 678a8df5af6SJacopo Mondi .colorspace = V4L2_COLORSPACE_SRGB, 679a8df5af6SJacopo Mondi .field = V4L2_FIELD_NONE, 680a8df5af6SJacopo Mondi .width = 640, 681a8df5af6SJacopo Mondi .height = 480 682a8df5af6SJacopo Mondi }, 683a8df5af6SJacopo Mondi .crop = { 684a8df5af6SJacopo Mondi .left = 16 + OV5647_PIXEL_ARRAY_LEFT, 685a8df5af6SJacopo Mondi .top = OV5647_PIXEL_ARRAY_TOP, 686a8df5af6SJacopo Mondi .width = 2560, 687a8df5af6SJacopo Mondi .height = 1920, 688a8df5af6SJacopo Mondi }, 689911f4516SDave Stevenson .pixel_rate = 55000000, 690c6da1ae4SJacopo Mondi .hts = 1852, 6912512c064SDave Stevenson .vts = 0x1f8, 692a8df5af6SJacopo Mondi .reg_list = ov5647_640x480_10bpp, 693a8df5af6SJacopo Mondi .num_regs = ARRAY_SIZE(ov5647_640x480_10bpp) 694a8df5af6SJacopo Mondi }, 695a8df5af6SJacopo Mondi }; 696a8df5af6SJacopo Mondi 697d7d6074eSJacopo Mondi static const struct ov5647_format_list ov5647_formats[] = { 698d7d6074eSJacopo Mondi { 699d7d6074eSJacopo Mondi .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, 700e907bd66SJacopo Mondi .modes = ov5647_8bpp_modes, 701e907bd66SJacopo Mondi .num_modes = ARRAY_SIZE(ov5647_8bpp_modes), 702d7d6074eSJacopo Mondi }, 703a8df5af6SJacopo Mondi { 704a8df5af6SJacopo Mondi .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, 705a8df5af6SJacopo Mondi .modes = ov5647_10bpp_modes, 706a8df5af6SJacopo Mondi .num_modes = ARRAY_SIZE(ov5647_10bpp_modes), 707a8df5af6SJacopo Mondi }, 708d7d6074eSJacopo Mondi }; 709d7d6074eSJacopo Mondi 710d7d6074eSJacopo Mondi #define OV5647_NUM_FORMATS (ARRAY_SIZE(ov5647_formats)) 711d7d6074eSJacopo Mondi 71287576ac6SJacopo Mondi /* Default sensor mode is 2x2 binned 640x480 SBGGR10_1X10. */ 71387576ac6SJacopo Mondi #define OV5647_DEFAULT_MODE (&ov5647_formats[1].modes[3]) 71487576ac6SJacopo Mondi #define OV5647_DEFAULT_FORMAT (ov5647_formats[1].modes[3].format) 715d7d6074eSJacopo Mondi 7162512c064SDave Stevenson static int ov5647_write16(struct v4l2_subdev *sd, u16 reg, u16 val) 7172512c064SDave Stevenson { 7182512c064SDave Stevenson unsigned char data[4] = { reg >> 8, reg & 0xff, val >> 8, val & 0xff}; 7192512c064SDave Stevenson struct i2c_client *client = v4l2_get_subdevdata(sd); 7202512c064SDave Stevenson int ret; 7212512c064SDave Stevenson 7222512c064SDave Stevenson ret = i2c_master_send(client, data, 4); 7232512c064SDave Stevenson if (ret < 0) { 7242512c064SDave Stevenson dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", 7252512c064SDave Stevenson __func__, reg); 7262512c064SDave Stevenson return ret; 7272512c064SDave Stevenson } 7282512c064SDave Stevenson 7292512c064SDave Stevenson return 0; 7302512c064SDave Stevenson } 7312512c064SDave Stevenson 7323c2472a3SRamiro Oliveira static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val) 7333c2472a3SRamiro Oliveira { 7343c2472a3SRamiro Oliveira unsigned char data[3] = { reg >> 8, reg & 0xff, val}; 7353c2472a3SRamiro Oliveira struct i2c_client *client = v4l2_get_subdevdata(sd); 736c9a05cecSJacopo Mondi int ret; 7373c2472a3SRamiro Oliveira 7383c2472a3SRamiro Oliveira ret = i2c_master_send(client, data, 3); 7392b18cbcfSJacopo Mondi if (ret < 0) { 7403c2472a3SRamiro Oliveira dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", 7413c2472a3SRamiro Oliveira __func__, reg); 7423c2472a3SRamiro Oliveira return ret; 7433c2472a3SRamiro Oliveira } 7443c2472a3SRamiro Oliveira 7452b18cbcfSJacopo Mondi return 0; 7462b18cbcfSJacopo Mondi } 7472b18cbcfSJacopo Mondi 7483c2472a3SRamiro Oliveira static int ov5647_read(struct v4l2_subdev *sd, u16 reg, u8 *val) 7493c2472a3SRamiro Oliveira { 7503c2472a3SRamiro Oliveira unsigned char data_w[2] = { reg >> 8, reg & 0xff }; 7513c2472a3SRamiro Oliveira struct i2c_client *client = v4l2_get_subdevdata(sd); 752c9a05cecSJacopo Mondi int ret; 7533c2472a3SRamiro Oliveira 7543c2472a3SRamiro Oliveira ret = i2c_master_send(client, data_w, 2); 7553c2472a3SRamiro Oliveira if (ret < 0) { 7563c2472a3SRamiro Oliveira dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", 7573c2472a3SRamiro Oliveira __func__, reg); 7583c2472a3SRamiro Oliveira return ret; 7593c2472a3SRamiro Oliveira } 7603c2472a3SRamiro Oliveira 7613c2472a3SRamiro Oliveira ret = i2c_master_recv(client, val, 1); 7622b18cbcfSJacopo Mondi if (ret < 0) { 7633c2472a3SRamiro Oliveira dev_dbg(&client->dev, "%s: i2c read error, reg: %x\n", 7643c2472a3SRamiro Oliveira __func__, reg); 7653c2472a3SRamiro Oliveira return ret; 7663c2472a3SRamiro Oliveira } 7673c2472a3SRamiro Oliveira 7682b18cbcfSJacopo Mondi return 0; 7692b18cbcfSJacopo Mondi } 7702b18cbcfSJacopo Mondi 7713c2472a3SRamiro Oliveira static int ov5647_write_array(struct v4l2_subdev *sd, 772d7d6074eSJacopo Mondi const struct regval_list *regs, int array_size) 7733c2472a3SRamiro Oliveira { 7743c2472a3SRamiro Oliveira int i, ret; 7753c2472a3SRamiro Oliveira 7763c2472a3SRamiro Oliveira for (i = 0; i < array_size; i++) { 7773c2472a3SRamiro Oliveira ret = ov5647_write(sd, regs[i].addr, regs[i].data); 7783c2472a3SRamiro Oliveira if (ret < 0) 7793c2472a3SRamiro Oliveira return ret; 7803c2472a3SRamiro Oliveira } 7813c2472a3SRamiro Oliveira 7823c2472a3SRamiro Oliveira return 0; 7833c2472a3SRamiro Oliveira } 7843c2472a3SRamiro Oliveira 7853c2472a3SRamiro Oliveira static int ov5647_set_virtual_channel(struct v4l2_subdev *sd, int channel) 7863c2472a3SRamiro Oliveira { 7873c2472a3SRamiro Oliveira u8 channel_id; 7883c2472a3SRamiro Oliveira int ret; 7893c2472a3SRamiro Oliveira 790cef66734SJacob Chen ret = ov5647_read(sd, OV5647_REG_MIPI_CTRL14, &channel_id); 7913c2472a3SRamiro Oliveira if (ret < 0) 7923c2472a3SRamiro Oliveira return ret; 7933c2472a3SRamiro Oliveira 7943c2472a3SRamiro Oliveira channel_id &= ~(3 << 6); 795c9a05cecSJacopo Mondi 796c9a05cecSJacopo Mondi return ov5647_write(sd, OV5647_REG_MIPI_CTRL14, 797c9a05cecSJacopo Mondi channel_id | (channel << 6)); 7983c2472a3SRamiro Oliveira } 7993c2472a3SRamiro Oliveira 800f7a70f9aSJacopo Mondi static int ov5647_set_mode(struct v4l2_subdev *sd) 801f7a70f9aSJacopo Mondi { 802f7a70f9aSJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 803d7d6074eSJacopo Mondi struct ov5647 *sensor = to_sensor(sd); 804f7a70f9aSJacopo Mondi u8 resetval, rdval; 805f7a70f9aSJacopo Mondi int ret; 806f7a70f9aSJacopo Mondi 807f7a70f9aSJacopo Mondi ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval); 808f7a70f9aSJacopo Mondi if (ret < 0) 809f7a70f9aSJacopo Mondi return ret; 810f7a70f9aSJacopo Mondi 811d7d6074eSJacopo Mondi ret = ov5647_write_array(sd, sensor->mode->reg_list, 812d7d6074eSJacopo Mondi sensor->mode->num_regs); 813f7a70f9aSJacopo Mondi if (ret < 0) { 814f7a70f9aSJacopo Mondi dev_err(&client->dev, "write sensor default regs error\n"); 815f7a70f9aSJacopo Mondi return ret; 816f7a70f9aSJacopo Mondi } 817f7a70f9aSJacopo Mondi 818f7a70f9aSJacopo Mondi ret = ov5647_set_virtual_channel(sd, 0); 819f7a70f9aSJacopo Mondi if (ret < 0) 820f7a70f9aSJacopo Mondi return ret; 821f7a70f9aSJacopo Mondi 822f7a70f9aSJacopo Mondi ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval); 823f7a70f9aSJacopo Mondi if (ret < 0) 824f7a70f9aSJacopo Mondi return ret; 825f7a70f9aSJacopo Mondi 826f7a70f9aSJacopo Mondi if (!(resetval & 0x01)) { 827f7a70f9aSJacopo Mondi dev_err(&client->dev, "Device was in SW standby"); 828f7a70f9aSJacopo Mondi ret = ov5647_write(sd, OV5647_SW_STANDBY, 0x01); 829f7a70f9aSJacopo Mondi if (ret < 0) 830f7a70f9aSJacopo Mondi return ret; 831f7a70f9aSJacopo Mondi } 832f7a70f9aSJacopo Mondi 833f7a70f9aSJacopo Mondi return 0; 834f7a70f9aSJacopo Mondi } 835f7a70f9aSJacopo Mondi 8363c2472a3SRamiro Oliveira static int ov5647_stream_on(struct v4l2_subdev *sd) 8373c2472a3SRamiro Oliveira { 838f7a70f9aSJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 8395bc5ca71SJacopo Mondi struct ov5647 *sensor = to_sensor(sd); 840dea4fcfeSDave Stevenson u8 val = MIPI_CTRL00_BUS_IDLE; 8413c2472a3SRamiro Oliveira int ret; 8423c2472a3SRamiro Oliveira 843f7a70f9aSJacopo Mondi ret = ov5647_set_mode(sd); 844f7a70f9aSJacopo Mondi if (ret) { 845f7a70f9aSJacopo Mondi dev_err(&client->dev, "Failed to program sensor mode: %d\n", ret); 846f7a70f9aSJacopo Mondi return ret; 847f7a70f9aSJacopo Mondi } 848f7a70f9aSJacopo Mondi 8494974c2f1SDavid Plowman /* Apply customized values from user when stream starts. */ 8504974c2f1SDavid Plowman ret = __v4l2_ctrl_handler_setup(sd->ctrl_handler); 8514974c2f1SDavid Plowman if (ret) 8524974c2f1SDavid Plowman return ret; 8534974c2f1SDavid Plowman 8545bc5ca71SJacopo Mondi if (sensor->clock_ncont) 855dea4fcfeSDave Stevenson val |= MIPI_CTRL00_CLOCK_LANE_GATE | 856dea4fcfeSDave Stevenson MIPI_CTRL00_LINE_SYNC_ENABLE; 857dea4fcfeSDave Stevenson 858dea4fcfeSDave Stevenson ret = ov5647_write(sd, OV5647_REG_MIPI_CTRL00, val); 8597bc9f038SJacob Chen if (ret < 0) 8607bc9f038SJacob Chen return ret; 8617bc9f038SJacob Chen 862cef66734SJacob Chen ret = ov5647_write(sd, OV5647_REG_FRAME_OFF_NUMBER, 0x00); 8633c2472a3SRamiro Oliveira if (ret < 0) 8643c2472a3SRamiro Oliveira return ret; 8653c2472a3SRamiro Oliveira 866cef66734SJacob Chen return ov5647_write(sd, OV5640_REG_PAD_OUT, 0x00); 8673c2472a3SRamiro Oliveira } 8683c2472a3SRamiro Oliveira 8693c2472a3SRamiro Oliveira static int ov5647_stream_off(struct v4l2_subdev *sd) 8703c2472a3SRamiro Oliveira { 8713c2472a3SRamiro Oliveira int ret; 8723c2472a3SRamiro Oliveira 873c9a05cecSJacopo Mondi ret = ov5647_write(sd, OV5647_REG_MIPI_CTRL00, 874c9a05cecSJacopo Mondi MIPI_CTRL00_CLOCK_LANE_GATE | MIPI_CTRL00_BUS_IDLE | 875c9a05cecSJacopo Mondi MIPI_CTRL00_CLOCK_LANE_DISABLE); 8767bc9f038SJacob Chen if (ret < 0) 8777bc9f038SJacob Chen return ret; 8787bc9f038SJacob Chen 879cef66734SJacob Chen ret = ov5647_write(sd, OV5647_REG_FRAME_OFF_NUMBER, 0x0f); 8803c2472a3SRamiro Oliveira if (ret < 0) 8813c2472a3SRamiro Oliveira return ret; 8823c2472a3SRamiro Oliveira 883cef66734SJacob Chen return ov5647_write(sd, OV5640_REG_PAD_OUT, 0x01); 8843c2472a3SRamiro Oliveira } 8853c2472a3SRamiro Oliveira 886089b7c70SJacopo Mondi static int ov5647_power_on(struct device *dev) 8873c2472a3SRamiro Oliveira { 888089b7c70SJacopo Mondi struct ov5647 *sensor = dev_get_drvdata(dev); 8893c2472a3SRamiro Oliveira int ret; 8903c2472a3SRamiro Oliveira 891089b7c70SJacopo Mondi dev_dbg(dev, "OV5647 power on\n"); 8923c2472a3SRamiro Oliveira 8935bc5ca71SJacopo Mondi if (sensor->pwdn) { 8945bc5ca71SJacopo Mondi gpiod_set_value_cansleep(sensor->pwdn, 0); 895b050791dSDave Stevenson msleep(PWDN_ACTIVE_DELAY_MS); 896b050791dSDave Stevenson } 897b050791dSDave Stevenson 8985bc5ca71SJacopo Mondi ret = clk_prepare_enable(sensor->xclk); 8993c2472a3SRamiro Oliveira if (ret < 0) { 900089b7c70SJacopo Mondi dev_err(dev, "clk prepare enable failed\n"); 901089b7c70SJacopo Mondi goto error_pwdn; 9023c2472a3SRamiro Oliveira } 9033c2472a3SRamiro Oliveira 904089b7c70SJacopo Mondi ret = ov5647_write_array(&sensor->sd, sensor_oe_enable_regs, 9053c2472a3SRamiro Oliveira ARRAY_SIZE(sensor_oe_enable_regs)); 9063c2472a3SRamiro Oliveira if (ret < 0) { 907089b7c70SJacopo Mondi dev_err(dev, "write sensor_oe_enable_regs error\n"); 908089b7c70SJacopo Mondi goto error_clk_disable; 9093c2472a3SRamiro Oliveira } 9103c2472a3SRamiro Oliveira 911f7a70f9aSJacopo Mondi /* Stream off to coax lanes into LP-11 state. */ 912089b7c70SJacopo Mondi ret = ov5647_stream_off(&sensor->sd); 9133c2472a3SRamiro Oliveira if (ret < 0) { 914089b7c70SJacopo Mondi dev_err(dev, "camera not available, check power\n"); 915089b7c70SJacopo Mondi goto error_clk_disable; 9163c2472a3SRamiro Oliveira } 9173c2472a3SRamiro Oliveira 918089b7c70SJacopo Mondi return 0; 919089b7c70SJacopo Mondi 920089b7c70SJacopo Mondi error_clk_disable: 921089b7c70SJacopo Mondi clk_disable_unprepare(sensor->xclk); 922089b7c70SJacopo Mondi error_pwdn: 923089b7c70SJacopo Mondi gpiod_set_value_cansleep(sensor->pwdn, 1); 924089b7c70SJacopo Mondi 925089b7c70SJacopo Mondi return ret; 926089b7c70SJacopo Mondi } 927089b7c70SJacopo Mondi 928089b7c70SJacopo Mondi static int ov5647_power_off(struct device *dev) 929089b7c70SJacopo Mondi { 930089b7c70SJacopo Mondi struct ov5647 *sensor = dev_get_drvdata(dev); 931089b7c70SJacopo Mondi u8 rdval; 932089b7c70SJacopo Mondi int ret; 933089b7c70SJacopo Mondi 934089b7c70SJacopo Mondi dev_dbg(dev, "OV5647 power off\n"); 935089b7c70SJacopo Mondi 936089b7c70SJacopo Mondi ret = ov5647_write_array(&sensor->sd, sensor_oe_disable_regs, 9373c2472a3SRamiro Oliveira ARRAY_SIZE(sensor_oe_disable_regs)); 9383c2472a3SRamiro Oliveira if (ret < 0) 939089b7c70SJacopo Mondi dev_dbg(dev, "disable oe failed\n"); 9403c2472a3SRamiro Oliveira 941089b7c70SJacopo Mondi /* Enter software standby */ 942089b7c70SJacopo Mondi ret = ov5647_read(&sensor->sd, OV5647_SW_STANDBY, &rdval); 9433c2472a3SRamiro Oliveira if (ret < 0) 944089b7c70SJacopo Mondi dev_dbg(dev, "software standby failed\n"); 945089b7c70SJacopo Mondi 946089b7c70SJacopo Mondi rdval &= ~0x01; 947089b7c70SJacopo Mondi ret = ov5647_write(&sensor->sd, OV5647_SW_STANDBY, rdval); 948089b7c70SJacopo Mondi if (ret < 0) 949089b7c70SJacopo Mondi dev_dbg(dev, "software standby failed\n"); 9503c2472a3SRamiro Oliveira 9515bc5ca71SJacopo Mondi clk_disable_unprepare(sensor->xclk); 9525bc5ca71SJacopo Mondi gpiod_set_value_cansleep(sensor->pwdn, 1); 9533c2472a3SRamiro Oliveira 954089b7c70SJacopo Mondi return 0; 9553c2472a3SRamiro Oliveira } 9563c2472a3SRamiro Oliveira 9573c2472a3SRamiro Oliveira #ifdef CONFIG_VIDEO_ADV_DEBUG 9583c2472a3SRamiro Oliveira static int ov5647_sensor_get_register(struct v4l2_subdev *sd, 9593c2472a3SRamiro Oliveira struct v4l2_dbg_register *reg) 9603c2472a3SRamiro Oliveira { 9613c2472a3SRamiro Oliveira int ret; 962c9a05cecSJacopo Mondi u8 val; 9633c2472a3SRamiro Oliveira 9643c2472a3SRamiro Oliveira ret = ov5647_read(sd, reg->reg & 0xff, &val); 9653c2472a3SRamiro Oliveira if (ret < 0) 9663c2472a3SRamiro Oliveira return ret; 9673c2472a3SRamiro Oliveira 9683c2472a3SRamiro Oliveira reg->val = val; 9693c2472a3SRamiro Oliveira reg->size = 1; 9703c2472a3SRamiro Oliveira 9713c2472a3SRamiro Oliveira return 0; 9723c2472a3SRamiro Oliveira } 9733c2472a3SRamiro Oliveira 9743c2472a3SRamiro Oliveira static int ov5647_sensor_set_register(struct v4l2_subdev *sd, 9753c2472a3SRamiro Oliveira const struct v4l2_dbg_register *reg) 9763c2472a3SRamiro Oliveira { 9773c2472a3SRamiro Oliveira return ov5647_write(sd, reg->reg & 0xff, reg->val & 0xff); 9783c2472a3SRamiro Oliveira } 9793c2472a3SRamiro Oliveira #endif 9803c2472a3SRamiro Oliveira 981c9a05cecSJacopo Mondi /* Subdev core operations registration */ 9823c2472a3SRamiro Oliveira static const struct v4l2_subdev_core_ops ov5647_subdev_core_ops = { 9833c2472a3SRamiro Oliveira #ifdef CONFIG_VIDEO_ADV_DEBUG 9843c2472a3SRamiro Oliveira .g_register = ov5647_sensor_get_register, 9853c2472a3SRamiro Oliveira .s_register = ov5647_sensor_set_register, 9863c2472a3SRamiro Oliveira #endif 9873c2472a3SRamiro Oliveira }; 9883c2472a3SRamiro Oliveira 98914f70a32SDave Stevenson static const struct v4l2_rect * 99014f70a32SDave Stevenson __ov5647_get_pad_crop(struct ov5647 *ov5647, struct v4l2_subdev_pad_config *cfg, 99114f70a32SDave Stevenson unsigned int pad, enum v4l2_subdev_format_whence which) 99214f70a32SDave Stevenson { 99314f70a32SDave Stevenson switch (which) { 99414f70a32SDave Stevenson case V4L2_SUBDEV_FORMAT_TRY: 99514f70a32SDave Stevenson return v4l2_subdev_get_try_crop(&ov5647->sd, cfg, pad); 99614f70a32SDave Stevenson case V4L2_SUBDEV_FORMAT_ACTIVE: 99714f70a32SDave Stevenson return &ov5647->mode->crop; 99814f70a32SDave Stevenson } 99914f70a32SDave Stevenson 100014f70a32SDave Stevenson return NULL; 100114f70a32SDave Stevenson } 100214f70a32SDave Stevenson 10033c2472a3SRamiro Oliveira static int ov5647_s_stream(struct v4l2_subdev *sd, int enable) 10043c2472a3SRamiro Oliveira { 1005*2f038c97SJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 10065bc5ca71SJacopo Mondi struct ov5647 *sensor = to_sensor(sd); 1007ab614f27SJacopo Mondi int ret; 1008ab614f27SJacopo Mondi 1009ab614f27SJacopo Mondi mutex_lock(&sensor->lock); 1010*2f038c97SJacopo Mondi if (sensor->streaming == enable) { 1011*2f038c97SJacopo Mondi mutex_unlock(&sensor->lock); 1012*2f038c97SJacopo Mondi return 0; 1013*2f038c97SJacopo Mondi } 1014*2f038c97SJacopo Mondi 1015*2f038c97SJacopo Mondi if (enable) { 1016*2f038c97SJacopo Mondi ret = pm_runtime_get_sync(&client->dev); 1017*2f038c97SJacopo Mondi if (ret < 0) 1018*2f038c97SJacopo Mondi goto error_unlock; 1019*2f038c97SJacopo Mondi 1020ab614f27SJacopo Mondi ret = ov5647_stream_on(sd); 1021*2f038c97SJacopo Mondi if (ret < 0) { 1022*2f038c97SJacopo Mondi dev_err(&client->dev, "stream start failed: %d\n", ret); 1023*2f038c97SJacopo Mondi goto error_unlock; 1024*2f038c97SJacopo Mondi } 1025*2f038c97SJacopo Mondi } else { 1026ab614f27SJacopo Mondi ret = ov5647_stream_off(sd); 1027*2f038c97SJacopo Mondi if (ret < 0) { 1028*2f038c97SJacopo Mondi dev_err(&client->dev, "stream stop failed: %d\n", ret); 1029*2f038c97SJacopo Mondi goto error_unlock; 1030*2f038c97SJacopo Mondi } 1031*2f038c97SJacopo Mondi pm_runtime_put(&client->dev); 1032*2f038c97SJacopo Mondi } 1033*2f038c97SJacopo Mondi 1034*2f038c97SJacopo Mondi sensor->streaming = enable; 1035*2f038c97SJacopo Mondi mutex_unlock(&sensor->lock); 1036*2f038c97SJacopo Mondi 1037*2f038c97SJacopo Mondi return 0; 1038*2f038c97SJacopo Mondi 1039*2f038c97SJacopo Mondi error_unlock: 1040*2f038c97SJacopo Mondi pm_runtime_put(&client->dev); 1041ab614f27SJacopo Mondi mutex_unlock(&sensor->lock); 1042ab614f27SJacopo Mondi 1043ab614f27SJacopo Mondi return ret; 10443c2472a3SRamiro Oliveira } 10453c2472a3SRamiro Oliveira 10463c2472a3SRamiro Oliveira static const struct v4l2_subdev_video_ops ov5647_subdev_video_ops = { 10473c2472a3SRamiro Oliveira .s_stream = ov5647_s_stream, 10483c2472a3SRamiro Oliveira }; 10493c2472a3SRamiro Oliveira 10503c2472a3SRamiro Oliveira static int ov5647_enum_mbus_code(struct v4l2_subdev *sd, 10513c2472a3SRamiro Oliveira struct v4l2_subdev_pad_config *cfg, 10523c2472a3SRamiro Oliveira struct v4l2_subdev_mbus_code_enum *code) 10533c2472a3SRamiro Oliveira { 1054d7d6074eSJacopo Mondi if (code->index >= OV5647_NUM_FORMATS) 10553c2472a3SRamiro Oliveira return -EINVAL; 10563c2472a3SRamiro Oliveira 1057d7d6074eSJacopo Mondi code->code = ov5647_formats[code->index].mbus_code; 10583c2472a3SRamiro Oliveira 10593c2472a3SRamiro Oliveira return 0; 10603c2472a3SRamiro Oliveira } 10613c2472a3SRamiro Oliveira 1062464090c0SJacopo Mondi static int ov5647_enum_frame_size(struct v4l2_subdev *sd, 1063464090c0SJacopo Mondi struct v4l2_subdev_pad_config *cfg, 1064464090c0SJacopo Mondi struct v4l2_subdev_frame_size_enum *fse) 1065464090c0SJacopo Mondi { 1066d7d6074eSJacopo Mondi const struct v4l2_mbus_framefmt *fmt; 1067d7d6074eSJacopo Mondi unsigned int i = 0; 1068d7d6074eSJacopo Mondi 1069d7d6074eSJacopo Mondi for (; i < OV5647_NUM_FORMATS; ++i) { 1070d7d6074eSJacopo Mondi if (ov5647_formats[i].mbus_code == fse->code) 1071d7d6074eSJacopo Mondi break; 1072d7d6074eSJacopo Mondi } 1073d7d6074eSJacopo Mondi if (i == OV5647_NUM_FORMATS) 1074464090c0SJacopo Mondi return -EINVAL; 1075464090c0SJacopo Mondi 1076d7d6074eSJacopo Mondi if (fse->index >= ov5647_formats[i].num_modes) 1077464090c0SJacopo Mondi return -EINVAL; 1078464090c0SJacopo Mondi 1079d7d6074eSJacopo Mondi fmt = &ov5647_formats[i].modes[fse->index].format; 1080d7d6074eSJacopo Mondi fse->min_width = fmt->width; 1081d7d6074eSJacopo Mondi fse->max_width = fmt->width; 1082d7d6074eSJacopo Mondi fse->min_height = fmt->height; 1083d7d6074eSJacopo Mondi fse->max_height = fmt->height; 1084464090c0SJacopo Mondi 1085464090c0SJacopo Mondi return 0; 1086464090c0SJacopo Mondi } 1087464090c0SJacopo Mondi 10886869e971SJacopo Mondi static int ov5647_get_pad_fmt(struct v4l2_subdev *sd, 10890f87233aSDave Stevenson struct v4l2_subdev_pad_config *cfg, 10900f87233aSDave Stevenson struct v4l2_subdev_format *format) 10910f87233aSDave Stevenson { 10920f87233aSDave Stevenson struct v4l2_mbus_framefmt *fmt = &format->format; 10936869e971SJacopo Mondi const struct v4l2_mbus_framefmt *sensor_format; 10946869e971SJacopo Mondi struct ov5647 *sensor = to_sensor(sd); 10950f87233aSDave Stevenson 10966869e971SJacopo Mondi mutex_lock(&sensor->lock); 10976869e971SJacopo Mondi switch (format->which) { 10986869e971SJacopo Mondi case V4L2_SUBDEV_FORMAT_TRY: 10996869e971SJacopo Mondi sensor_format = v4l2_subdev_get_try_format(sd, cfg, format->pad); 11006869e971SJacopo Mondi break; 11016869e971SJacopo Mondi default: 11026869e971SJacopo Mondi sensor_format = &sensor->mode->format; 11036869e971SJacopo Mondi break; 11046869e971SJacopo Mondi } 11056869e971SJacopo Mondi 11066869e971SJacopo Mondi *fmt = *sensor_format; 11076869e971SJacopo Mondi mutex_unlock(&sensor->lock); 11086869e971SJacopo Mondi 11096869e971SJacopo Mondi return 0; 11106869e971SJacopo Mondi } 11116869e971SJacopo Mondi 11126869e971SJacopo Mondi static int ov5647_set_pad_fmt(struct v4l2_subdev *sd, 11136869e971SJacopo Mondi struct v4l2_subdev_pad_config *cfg, 11146869e971SJacopo Mondi struct v4l2_subdev_format *format) 11156869e971SJacopo Mondi { 11166869e971SJacopo Mondi struct v4l2_mbus_framefmt *fmt = &format->format; 11176869e971SJacopo Mondi const struct ov5647_mode *ov5647_mode_list; 11186869e971SJacopo Mondi struct ov5647 *sensor = to_sensor(sd); 1119646a0249SDave Stevenson 11206869e971SJacopo Mondi const struct ov5647_mode *mode; 11216869e971SJacopo Mondi unsigned int num_modes; 11226869e971SJacopo Mondi unsigned int i; 11236869e971SJacopo Mondi 11246869e971SJacopo Mondi for (i = 0; i < OV5647_NUM_FORMATS; ++i) { 11256869e971SJacopo Mondi if (ov5647_formats[i].mbus_code != fmt->code) 11266869e971SJacopo Mondi continue; 11276869e971SJacopo Mondi 11286869e971SJacopo Mondi ov5647_mode_list = ov5647_formats[i].modes; 11296869e971SJacopo Mondi num_modes = ov5647_formats[i].num_modes; 11306869e971SJacopo Mondi break; 11316869e971SJacopo Mondi } 11326869e971SJacopo Mondi 11336869e971SJacopo Mondi /* 11346869e971SJacopo Mondi * Default mbus code MEDIA_BUS_FMT_SBGGR10_1X10 if the requested one is 11356869e971SJacopo Mondi * not supported. 11366869e971SJacopo Mondi */ 11376869e971SJacopo Mondi if (i == OV5647_NUM_FORMATS) { 11386869e971SJacopo Mondi ov5647_mode_list = ov5647_10bpp_modes; 11396869e971SJacopo Mondi num_modes = ARRAY_SIZE(ov5647_10bpp_modes); 11406869e971SJacopo Mondi } 11416869e971SJacopo Mondi 11426869e971SJacopo Mondi mode = v4l2_find_nearest_size(ov5647_mode_list, num_modes, 11436869e971SJacopo Mondi format.width, format.height, 11446869e971SJacopo Mondi fmt->width, fmt->height); 11456869e971SJacopo Mondi 11466869e971SJacopo Mondi /* Update the sensor mode and apply at it at streamon time. */ 11476869e971SJacopo Mondi mutex_lock(&sensor->lock); 1148911f4516SDave Stevenson if (format->which == V4L2_SUBDEV_FORMAT_TRY) { 11496869e971SJacopo Mondi *v4l2_subdev_get_try_format(sd, cfg, format->pad) = mode->format; 1150911f4516SDave Stevenson } else { 1151646a0249SDave Stevenson int exposure_max, exposure_def; 11522512c064SDave Stevenson int hblank, vblank; 1153c6da1ae4SJacopo Mondi 11546869e971SJacopo Mondi sensor->mode = mode; 1155911f4516SDave Stevenson __v4l2_ctrl_modify_range(sensor->pixel_rate, mode->pixel_rate, 1156911f4516SDave Stevenson mode->pixel_rate, 1, mode->pixel_rate); 1157c6da1ae4SJacopo Mondi 1158c6da1ae4SJacopo Mondi hblank = mode->hts - mode->format.width; 1159c6da1ae4SJacopo Mondi __v4l2_ctrl_modify_range(sensor->hblank, hblank, hblank, 1, 1160c6da1ae4SJacopo Mondi hblank); 11612512c064SDave Stevenson 11622512c064SDave Stevenson vblank = mode->vts - mode->format.height; 11632512c064SDave Stevenson __v4l2_ctrl_modify_range(sensor->vblank, OV5647_VBLANK_MIN, 11642512c064SDave Stevenson OV5647_VTS_MAX - mode->format.height, 11652512c064SDave Stevenson 1, vblank); 11662512c064SDave Stevenson __v4l2_ctrl_s_ctrl(sensor->vblank, vblank); 1167646a0249SDave Stevenson 1168646a0249SDave Stevenson exposure_max = mode->vts - 4; 1169646a0249SDave Stevenson exposure_def = min(exposure_max, OV5647_EXPOSURE_DEFAULT); 1170646a0249SDave Stevenson __v4l2_ctrl_modify_range(sensor->exposure, 1171646a0249SDave Stevenson sensor->exposure->minimum, 1172646a0249SDave Stevenson exposure_max, sensor->exposure->step, 1173646a0249SDave Stevenson exposure_def); 1174911f4516SDave Stevenson } 11756869e971SJacopo Mondi *fmt = mode->format; 11766869e971SJacopo Mondi mutex_unlock(&sensor->lock); 11770f87233aSDave Stevenson 11780f87233aSDave Stevenson return 0; 11790f87233aSDave Stevenson } 11800f87233aSDave Stevenson 118114f70a32SDave Stevenson static int ov5647_get_selection(struct v4l2_subdev *sd, 118214f70a32SDave Stevenson struct v4l2_subdev_pad_config *cfg, 118314f70a32SDave Stevenson struct v4l2_subdev_selection *sel) 118414f70a32SDave Stevenson { 118514f70a32SDave Stevenson switch (sel->target) { 118614f70a32SDave Stevenson case V4L2_SEL_TGT_CROP: { 118714f70a32SDave Stevenson struct ov5647 *sensor = to_sensor(sd); 118814f70a32SDave Stevenson 118914f70a32SDave Stevenson mutex_lock(&sensor->lock); 119014f70a32SDave Stevenson sel->r = *__ov5647_get_pad_crop(sensor, cfg, sel->pad, 119114f70a32SDave Stevenson sel->which); 119214f70a32SDave Stevenson mutex_unlock(&sensor->lock); 119314f70a32SDave Stevenson 119414f70a32SDave Stevenson return 0; 119514f70a32SDave Stevenson } 119614f70a32SDave Stevenson 119714f70a32SDave Stevenson case V4L2_SEL_TGT_NATIVE_SIZE: 119814f70a32SDave Stevenson sel->r.top = 0; 119914f70a32SDave Stevenson sel->r.left = 0; 120014f70a32SDave Stevenson sel->r.width = OV5647_NATIVE_WIDTH; 120114f70a32SDave Stevenson sel->r.height = OV5647_NATIVE_HEIGHT; 120214f70a32SDave Stevenson 120314f70a32SDave Stevenson return 0; 120414f70a32SDave Stevenson 120514f70a32SDave Stevenson case V4L2_SEL_TGT_CROP_DEFAULT: 120614f70a32SDave Stevenson case V4L2_SEL_TGT_CROP_BOUNDS: 120714f70a32SDave Stevenson sel->r.top = OV5647_PIXEL_ARRAY_TOP; 120814f70a32SDave Stevenson sel->r.left = OV5647_PIXEL_ARRAY_LEFT; 120914f70a32SDave Stevenson sel->r.width = OV5647_PIXEL_ARRAY_WIDTH; 121014f70a32SDave Stevenson sel->r.height = OV5647_PIXEL_ARRAY_HEIGHT; 121114f70a32SDave Stevenson 121214f70a32SDave Stevenson return 0; 121314f70a32SDave Stevenson } 121414f70a32SDave Stevenson 121514f70a32SDave Stevenson return -EINVAL; 121614f70a32SDave Stevenson } 121714f70a32SDave Stevenson 12183c2472a3SRamiro Oliveira static const struct v4l2_subdev_pad_ops ov5647_subdev_pad_ops = { 12193c2472a3SRamiro Oliveira .enum_mbus_code = ov5647_enum_mbus_code, 1220464090c0SJacopo Mondi .enum_frame_size = ov5647_enum_frame_size, 12216869e971SJacopo Mondi .set_fmt = ov5647_set_pad_fmt, 12226869e971SJacopo Mondi .get_fmt = ov5647_get_pad_fmt, 122314f70a32SDave Stevenson .get_selection = ov5647_get_selection, 12243c2472a3SRamiro Oliveira }; 12253c2472a3SRamiro Oliveira 12263c2472a3SRamiro Oliveira static const struct v4l2_subdev_ops ov5647_subdev_ops = { 12273c2472a3SRamiro Oliveira .core = &ov5647_subdev_core_ops, 12283c2472a3SRamiro Oliveira .video = &ov5647_subdev_video_ops, 12293c2472a3SRamiro Oliveira .pad = &ov5647_subdev_pad_ops, 12303c2472a3SRamiro Oliveira }; 12313c2472a3SRamiro Oliveira 12323c2472a3SRamiro Oliveira static int ov5647_detect(struct v4l2_subdev *sd) 12333c2472a3SRamiro Oliveira { 1234c9a05cecSJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 12353c2472a3SRamiro Oliveira u8 read; 12363c2472a3SRamiro Oliveira int ret; 12373c2472a3SRamiro Oliveira 12383c2472a3SRamiro Oliveira ret = ov5647_write(sd, OV5647_SW_RESET, 0x01); 12393c2472a3SRamiro Oliveira if (ret < 0) 12403c2472a3SRamiro Oliveira return ret; 12413c2472a3SRamiro Oliveira 12423c2472a3SRamiro Oliveira ret = ov5647_read(sd, OV5647_REG_CHIPID_H, &read); 12433c2472a3SRamiro Oliveira if (ret < 0) 12443c2472a3SRamiro Oliveira return ret; 12453c2472a3SRamiro Oliveira 12463c2472a3SRamiro Oliveira if (read != 0x56) { 12473c2472a3SRamiro Oliveira dev_err(&client->dev, "ID High expected 0x56 got %x", read); 12483c2472a3SRamiro Oliveira return -ENODEV; 12493c2472a3SRamiro Oliveira } 12503c2472a3SRamiro Oliveira 12513c2472a3SRamiro Oliveira ret = ov5647_read(sd, OV5647_REG_CHIPID_L, &read); 12523c2472a3SRamiro Oliveira if (ret < 0) 12533c2472a3SRamiro Oliveira return ret; 12543c2472a3SRamiro Oliveira 12553c2472a3SRamiro Oliveira if (read != 0x47) { 12563c2472a3SRamiro Oliveira dev_err(&client->dev, "ID Low expected 0x47 got %x", read); 12573c2472a3SRamiro Oliveira return -ENODEV; 12583c2472a3SRamiro Oliveira } 12593c2472a3SRamiro Oliveira 12603c2472a3SRamiro Oliveira return ov5647_write(sd, OV5647_SW_RESET, 0x00); 12613c2472a3SRamiro Oliveira } 12623c2472a3SRamiro Oliveira 12633c2472a3SRamiro Oliveira static int ov5647_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 12643c2472a3SRamiro Oliveira { 12653c2472a3SRamiro Oliveira struct v4l2_mbus_framefmt *format = 12663c2472a3SRamiro Oliveira v4l2_subdev_get_try_format(sd, fh->pad, 0); 1267c9a05cecSJacopo Mondi struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, fh->pad, 0); 12683c2472a3SRamiro Oliveira 126914f70a32SDave Stevenson crop->left = OV5647_PIXEL_ARRAY_LEFT; 127014f70a32SDave Stevenson crop->top = OV5647_PIXEL_ARRAY_TOP; 127114f70a32SDave Stevenson crop->width = OV5647_PIXEL_ARRAY_WIDTH; 127214f70a32SDave Stevenson crop->height = OV5647_PIXEL_ARRAY_HEIGHT; 12733c2472a3SRamiro Oliveira 1274d7d6074eSJacopo Mondi *format = OV5647_DEFAULT_FORMAT; 12753c2472a3SRamiro Oliveira 12763c2472a3SRamiro Oliveira return 0; 12773c2472a3SRamiro Oliveira } 12783c2472a3SRamiro Oliveira 12793c2472a3SRamiro Oliveira static const struct v4l2_subdev_internal_ops ov5647_subdev_internal_ops = { 12803c2472a3SRamiro Oliveira .open = ov5647_open, 12813c2472a3SRamiro Oliveira }; 12823c2472a3SRamiro Oliveira 12834974c2f1SDavid Plowman static int ov5647_s_auto_white_balance(struct v4l2_subdev *sd, u32 val) 12844974c2f1SDavid Plowman { 12854974c2f1SDavid Plowman return ov5647_write(sd, OV5647_REG_AWB, val ? 1 : 0); 12864974c2f1SDavid Plowman } 12874974c2f1SDavid Plowman 12884974c2f1SDavid Plowman static int ov5647_s_autogain(struct v4l2_subdev *sd, u32 val) 12894974c2f1SDavid Plowman { 12904974c2f1SDavid Plowman int ret; 12914974c2f1SDavid Plowman u8 reg; 12924974c2f1SDavid Plowman 12934974c2f1SDavid Plowman /* Non-zero turns on AGC by clearing bit 1.*/ 12944974c2f1SDavid Plowman ret = ov5647_read(sd, OV5647_REG_AEC_AGC, ®); 12954974c2f1SDavid Plowman if (ret) 12964974c2f1SDavid Plowman return ret; 12974974c2f1SDavid Plowman 12984974c2f1SDavid Plowman return ov5647_write(sd, OV5647_REG_AEC_AGC, val ? reg & ~BIT(1) 12994974c2f1SDavid Plowman : reg | BIT(1)); 13004974c2f1SDavid Plowman } 13014974c2f1SDavid Plowman 13024974c2f1SDavid Plowman static int ov5647_s_exposure_auto(struct v4l2_subdev *sd, u32 val) 13034974c2f1SDavid Plowman { 13044974c2f1SDavid Plowman int ret; 13054974c2f1SDavid Plowman u8 reg; 13064974c2f1SDavid Plowman 13074974c2f1SDavid Plowman /* 13084974c2f1SDavid Plowman * Everything except V4L2_EXPOSURE_MANUAL turns on AEC by 13094974c2f1SDavid Plowman * clearing bit 0. 13104974c2f1SDavid Plowman */ 13114974c2f1SDavid Plowman ret = ov5647_read(sd, OV5647_REG_AEC_AGC, ®); 13124974c2f1SDavid Plowman if (ret) 13134974c2f1SDavid Plowman return ret; 13144974c2f1SDavid Plowman 13154974c2f1SDavid Plowman return ov5647_write(sd, OV5647_REG_AEC_AGC, 13164974c2f1SDavid Plowman val == V4L2_EXPOSURE_MANUAL ? reg | BIT(0) 13174974c2f1SDavid Plowman : reg & ~BIT(0)); 13184974c2f1SDavid Plowman } 13194974c2f1SDavid Plowman 13204974c2f1SDavid Plowman static int ov5647_s_analogue_gain(struct v4l2_subdev *sd, u32 val) 13214974c2f1SDavid Plowman { 13224974c2f1SDavid Plowman int ret; 13234974c2f1SDavid Plowman 13244974c2f1SDavid Plowman /* 10 bits of gain, 2 in the high register. */ 13254974c2f1SDavid Plowman ret = ov5647_write(sd, OV5647_REG_GAIN_HI, (val >> 8) & 3); 13264974c2f1SDavid Plowman if (ret) 13274974c2f1SDavid Plowman return ret; 13284974c2f1SDavid Plowman 13294974c2f1SDavid Plowman return ov5647_write(sd, OV5647_REG_GAIN_LO, val & 0xff); 13304974c2f1SDavid Plowman } 13314974c2f1SDavid Plowman 13324974c2f1SDavid Plowman static int ov5647_s_exposure(struct v4l2_subdev *sd, u32 val) 13334974c2f1SDavid Plowman { 13344974c2f1SDavid Plowman int ret; 13354974c2f1SDavid Plowman 13364974c2f1SDavid Plowman /* 13374974c2f1SDavid Plowman * Sensor has 20 bits, but the bottom 4 bits are fractions of a line 13384974c2f1SDavid Plowman * which we leave as zero (and don't receive in "val"). 13394974c2f1SDavid Plowman */ 13404974c2f1SDavid Plowman ret = ov5647_write(sd, OV5647_REG_EXP_HI, (val >> 12) & 0xf); 13414974c2f1SDavid Plowman if (ret) 13424974c2f1SDavid Plowman return ret; 13434974c2f1SDavid Plowman 13444974c2f1SDavid Plowman ret = ov5647_write(sd, OV5647_REG_EXP_MID, (val >> 4) & 0xff); 13454974c2f1SDavid Plowman if (ret) 13464974c2f1SDavid Plowman return ret; 13474974c2f1SDavid Plowman 13484974c2f1SDavid Plowman return ov5647_write(sd, OV5647_REG_EXP_LO, (val & 0xf) << 4); 13494974c2f1SDavid Plowman } 13504974c2f1SDavid Plowman 13514974c2f1SDavid Plowman static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) 13524974c2f1SDavid Plowman { 13534974c2f1SDavid Plowman struct ov5647 *sensor = container_of(ctrl->handler, 13544974c2f1SDavid Plowman struct ov5647, ctrls); 13554974c2f1SDavid Plowman struct v4l2_subdev *sd = &sensor->sd; 13564974c2f1SDavid Plowman struct i2c_client *client = v4l2_get_subdevdata(sd); 13574974c2f1SDavid Plowman 13584974c2f1SDavid Plowman /* v4l2_ctrl_lock() locks our own mutex */ 13594974c2f1SDavid Plowman 1360646a0249SDave Stevenson if (ctrl->id == V4L2_CID_VBLANK) { 1361646a0249SDave Stevenson int exposure_max, exposure_def; 1362646a0249SDave Stevenson 1363646a0249SDave Stevenson /* Update max exposure while meeting expected vblanking */ 1364646a0249SDave Stevenson exposure_max = sensor->mode->format.height + ctrl->val - 4; 1365646a0249SDave Stevenson exposure_def = min(exposure_max, OV5647_EXPOSURE_DEFAULT); 1366646a0249SDave Stevenson __v4l2_ctrl_modify_range(sensor->exposure, 1367646a0249SDave Stevenson sensor->exposure->minimum, 1368646a0249SDave Stevenson exposure_max, sensor->exposure->step, 1369646a0249SDave Stevenson exposure_def); 1370646a0249SDave Stevenson } 1371646a0249SDave Stevenson 13724974c2f1SDavid Plowman /* 13734974c2f1SDavid Plowman * If the device is not powered up by the host driver do 13744974c2f1SDavid Plowman * not apply any controls to H/W at this time. Instead 13754974c2f1SDavid Plowman * the controls will be restored at s_stream(1) time. 13764974c2f1SDavid Plowman */ 13774974c2f1SDavid Plowman if (!sensor->power_count) 13784974c2f1SDavid Plowman return 0; 13794974c2f1SDavid Plowman 13804974c2f1SDavid Plowman switch (ctrl->id) { 13814974c2f1SDavid Plowman case V4L2_CID_AUTO_WHITE_BALANCE: 13824974c2f1SDavid Plowman return ov5647_s_auto_white_balance(sd, ctrl->val); 13834974c2f1SDavid Plowman case V4L2_CID_AUTOGAIN: 13844974c2f1SDavid Plowman return ov5647_s_autogain(sd, ctrl->val); 13854974c2f1SDavid Plowman case V4L2_CID_EXPOSURE_AUTO: 13864974c2f1SDavid Plowman return ov5647_s_exposure_auto(sd, ctrl->val); 13874974c2f1SDavid Plowman case V4L2_CID_ANALOGUE_GAIN: 13884974c2f1SDavid Plowman return ov5647_s_analogue_gain(sd, ctrl->val); 13894974c2f1SDavid Plowman case V4L2_CID_EXPOSURE: 13904974c2f1SDavid Plowman return ov5647_s_exposure(sd, ctrl->val); 1391911f4516SDave Stevenson case V4L2_CID_PIXEL_RATE: 1392911f4516SDave Stevenson /* Read-only, but we adjust it based on mode. */ 1393911f4516SDave Stevenson return 0; 1394c6da1ae4SJacopo Mondi case V4L2_CID_HBLANK: 1395c6da1ae4SJacopo Mondi /* Read-only, but we adjust it based on mode. */ 1396c6da1ae4SJacopo Mondi return 0; 13972512c064SDave Stevenson case V4L2_CID_VBLANK: 13982512c064SDave Stevenson return ov5647_write16(sd, OV5647_REG_VTS_HI, 13992512c064SDave Stevenson sensor->mode->format.height + ctrl->val); 14004974c2f1SDavid Plowman default: 14014974c2f1SDavid Plowman dev_info(&client->dev, 14024974c2f1SDavid Plowman "Control (id:0x%x, val:0x%x) not supported\n", 14034974c2f1SDavid Plowman ctrl->id, ctrl->val); 14044974c2f1SDavid Plowman return -EINVAL; 14054974c2f1SDavid Plowman } 14064974c2f1SDavid Plowman 14074974c2f1SDavid Plowman return 0; 14084974c2f1SDavid Plowman } 14094974c2f1SDavid Plowman 14104974c2f1SDavid Plowman static const struct v4l2_ctrl_ops ov5647_ctrl_ops = { 14114974c2f1SDavid Plowman .s_ctrl = ov5647_s_ctrl, 14124974c2f1SDavid Plowman }; 14134974c2f1SDavid Plowman 14144974c2f1SDavid Plowman static int ov5647_init_controls(struct ov5647 *sensor) 14154974c2f1SDavid Plowman { 14164974c2f1SDavid Plowman struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); 1417646a0249SDave Stevenson int hblank, exposure_max, exposure_def; 14184974c2f1SDavid Plowman 14192512c064SDave Stevenson v4l2_ctrl_handler_init(&sensor->ctrls, 8); 14204974c2f1SDavid Plowman 14214974c2f1SDavid Plowman v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, 14224974c2f1SDavid Plowman V4L2_CID_AUTOGAIN, 0, 1, 1, 0); 14234974c2f1SDavid Plowman 14244974c2f1SDavid Plowman v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, 14254974c2f1SDavid Plowman V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 0); 14264974c2f1SDavid Plowman 14274974c2f1SDavid Plowman v4l2_ctrl_new_std_menu(&sensor->ctrls, &ov5647_ctrl_ops, 14284974c2f1SDavid Plowman V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 14294974c2f1SDavid Plowman 0, V4L2_EXPOSURE_MANUAL); 14304974c2f1SDavid Plowman 1431646a0249SDave Stevenson exposure_max = sensor->mode->vts - 4; 1432646a0249SDave Stevenson exposure_def = min(exposure_max, OV5647_EXPOSURE_DEFAULT); 1433646a0249SDave Stevenson sensor->exposure = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, 1434646a0249SDave Stevenson V4L2_CID_EXPOSURE, 1435646a0249SDave Stevenson OV5647_EXPOSURE_MIN, 1436646a0249SDave Stevenson exposure_max, OV5647_EXPOSURE_STEP, 1437646a0249SDave Stevenson exposure_def); 14384974c2f1SDavid Plowman 14394974c2f1SDavid Plowman /* min: 16 = 1.0x; max (10 bits); default: 32 = 2.0x. */ 14404974c2f1SDavid Plowman v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, 14414974c2f1SDavid Plowman V4L2_CID_ANALOGUE_GAIN, 16, 1023, 1, 32); 14424974c2f1SDavid Plowman 1443911f4516SDave Stevenson /* By default, PIXEL_RATE is read only, but it does change per mode */ 1444911f4516SDave Stevenson sensor->pixel_rate = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, 1445911f4516SDave Stevenson V4L2_CID_PIXEL_RATE, 1446911f4516SDave Stevenson sensor->mode->pixel_rate, 1447911f4516SDave Stevenson sensor->mode->pixel_rate, 1, 1448911f4516SDave Stevenson sensor->mode->pixel_rate); 1449c6da1ae4SJacopo Mondi 1450c6da1ae4SJacopo Mondi /* By default, HBLANK is read only, but it does change per mode. */ 1451c6da1ae4SJacopo Mondi hblank = sensor->mode->hts - sensor->mode->format.width; 1452c6da1ae4SJacopo Mondi sensor->hblank = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, 1453c6da1ae4SJacopo Mondi V4L2_CID_HBLANK, hblank, hblank, 1, 1454c6da1ae4SJacopo Mondi hblank); 1455c6da1ae4SJacopo Mondi 14562512c064SDave Stevenson sensor->vblank = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, 14572512c064SDave Stevenson V4L2_CID_VBLANK, OV5647_VBLANK_MIN, 14582512c064SDave Stevenson OV5647_VTS_MAX - 14592512c064SDave Stevenson sensor->mode->format.height, 1, 14602512c064SDave Stevenson sensor->mode->vts - 14612512c064SDave Stevenson sensor->mode->format.height); 14622512c064SDave Stevenson 1463911f4516SDave Stevenson if (sensor->ctrls.error) 1464911f4516SDave Stevenson goto handler_free; 1465911f4516SDave Stevenson 1466911f4516SDave Stevenson sensor->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; 1467c6da1ae4SJacopo Mondi sensor->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; 1468911f4516SDave Stevenson sensor->sd.ctrl_handler = &sensor->ctrls; 1469911f4516SDave Stevenson 1470911f4516SDave Stevenson return 0; 1471911f4516SDave Stevenson 1472911f4516SDave Stevenson handler_free: 1473911f4516SDave Stevenson dev_err(&client->dev, "%s Controls initialization failed (%d)\n", 14744974c2f1SDavid Plowman __func__, sensor->ctrls.error); 14754974c2f1SDavid Plowman v4l2_ctrl_handler_free(&sensor->ctrls); 14764974c2f1SDavid Plowman 14774974c2f1SDavid Plowman return sensor->ctrls.error; 14784974c2f1SDavid Plowman } 14794974c2f1SDavid Plowman 1480dea4fcfeSDave Stevenson static int ov5647_parse_dt(struct ov5647 *sensor, struct device_node *np) 14813c2472a3SRamiro Oliveira { 1482dea4fcfeSDave Stevenson struct v4l2_fwnode_endpoint bus_cfg = { 1483dea4fcfeSDave Stevenson .bus_type = V4L2_MBUS_CSI2_DPHY, 1484dea4fcfeSDave Stevenson }; 14853c2472a3SRamiro Oliveira struct device_node *ep; 14863c2472a3SRamiro Oliveira int ret; 14873c2472a3SRamiro Oliveira 14883c2472a3SRamiro Oliveira ep = of_graph_get_next_endpoint(np, NULL); 14893c2472a3SRamiro Oliveira if (!ep) 14903c2472a3SRamiro Oliveira return -EINVAL; 14913c2472a3SRamiro Oliveira 1492859969b3SSakari Ailus ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg); 1493dea4fcfeSDave Stevenson if (ret) 1494dea4fcfeSDave Stevenson goto out; 14953c2472a3SRamiro Oliveira 1496dea4fcfeSDave Stevenson sensor->clock_ncont = bus_cfg.bus.mipi_csi2.flags & 1497dea4fcfeSDave Stevenson V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK; 1498dea4fcfeSDave Stevenson 1499dea4fcfeSDave Stevenson out: 15003c2472a3SRamiro Oliveira of_node_put(ep); 1501c9a05cecSJacopo Mondi 15023c2472a3SRamiro Oliveira return ret; 15033c2472a3SRamiro Oliveira } 15043c2472a3SRamiro Oliveira 1505e6714993SKieran Bingham static int ov5647_probe(struct i2c_client *client) 15063c2472a3SRamiro Oliveira { 1507c9a05cecSJacopo Mondi struct device_node *np = client->dev.of_node; 15083c2472a3SRamiro Oliveira struct device *dev = &client->dev; 15093c2472a3SRamiro Oliveira struct ov5647 *sensor; 15103c2472a3SRamiro Oliveira struct v4l2_subdev *sd; 15113c2472a3SRamiro Oliveira u32 xclk_freq; 1512c9a05cecSJacopo Mondi int ret; 15133c2472a3SRamiro Oliveira 15143c2472a3SRamiro Oliveira sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); 15153c2472a3SRamiro Oliveira if (!sensor) 15163c2472a3SRamiro Oliveira return -ENOMEM; 15173c2472a3SRamiro Oliveira 15183c2472a3SRamiro Oliveira if (IS_ENABLED(CONFIG_OF) && np) { 1519dea4fcfeSDave Stevenson ret = ov5647_parse_dt(sensor, np); 15203c2472a3SRamiro Oliveira if (ret) { 15213c2472a3SRamiro Oliveira dev_err(dev, "DT parsing error: %d\n", ret); 15223c2472a3SRamiro Oliveira return ret; 15233c2472a3SRamiro Oliveira } 15243c2472a3SRamiro Oliveira } 15253c2472a3SRamiro Oliveira 15263c2472a3SRamiro Oliveira sensor->xclk = devm_clk_get(dev, NULL); 15273c2472a3SRamiro Oliveira if (IS_ERR(sensor->xclk)) { 15283c2472a3SRamiro Oliveira dev_err(dev, "could not get xclk"); 15293c2472a3SRamiro Oliveira return PTR_ERR(sensor->xclk); 15303c2472a3SRamiro Oliveira } 15313c2472a3SRamiro Oliveira 15323c2472a3SRamiro Oliveira xclk_freq = clk_get_rate(sensor->xclk); 15333c2472a3SRamiro Oliveira if (xclk_freq != 25000000) { 15343c2472a3SRamiro Oliveira dev_err(dev, "Unsupported clock frequency: %u\n", xclk_freq); 15353c2472a3SRamiro Oliveira return -EINVAL; 15363c2472a3SRamiro Oliveira } 15373c2472a3SRamiro Oliveira 1538c9a05cecSJacopo Mondi /* Request the power down GPIO asserted. */ 1539c9a05cecSJacopo Mondi sensor->pwdn = devm_gpiod_get_optional(dev, "pwdn", GPIOD_OUT_HIGH); 1540b050791dSDave Stevenson if (IS_ERR(sensor->pwdn)) { 1541b050791dSDave Stevenson dev_err(dev, "Failed to get 'pwdn' gpio\n"); 1542b050791dSDave Stevenson return -EINVAL; 1543b050791dSDave Stevenson } 1544b050791dSDave Stevenson 15453c2472a3SRamiro Oliveira mutex_init(&sensor->lock); 15463c2472a3SRamiro Oliveira 1547d7d6074eSJacopo Mondi sensor->mode = OV5647_DEFAULT_MODE; 1548d7d6074eSJacopo Mondi 15494974c2f1SDavid Plowman ret = ov5647_init_controls(sensor); 15504974c2f1SDavid Plowman if (ret) 15514974c2f1SDavid Plowman goto mutex_destroy; 15524974c2f1SDavid Plowman 15533c2472a3SRamiro Oliveira sd = &sensor->sd; 15543c2472a3SRamiro Oliveira v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops); 1555c9a05cecSJacopo Mondi sd->internal_ops = &ov5647_subdev_internal_ops; 15567ef761a0SDave Stevenson sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; 15573c2472a3SRamiro Oliveira 15583c2472a3SRamiro Oliveira sensor->pad.flags = MEDIA_PAD_FL_SOURCE; 15593c2472a3SRamiro Oliveira sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; 15603c2472a3SRamiro Oliveira ret = media_entity_pads_init(&sd->entity, 1, &sensor->pad); 15613c2472a3SRamiro Oliveira if (ret < 0) 15624974c2f1SDavid Plowman goto ctrl_handler_free; 15633c2472a3SRamiro Oliveira 1564089b7c70SJacopo Mondi ret = ov5647_power_on(dev); 1565089b7c70SJacopo Mondi if (ret) 1566089b7c70SJacopo Mondi goto entity_cleanup; 1567b050791dSDave Stevenson 15683c2472a3SRamiro Oliveira ret = ov5647_detect(sd); 15693c2472a3SRamiro Oliveira if (ret < 0) 1570089b7c70SJacopo Mondi goto power_off; 15713c2472a3SRamiro Oliveira 15723c2472a3SRamiro Oliveira ret = v4l2_async_register_subdev(sd); 15733c2472a3SRamiro Oliveira if (ret < 0) 1574089b7c70SJacopo Mondi goto power_off; 1575089b7c70SJacopo Mondi 1576089b7c70SJacopo Mondi /* Enable runtime PM and turn off the device */ 1577089b7c70SJacopo Mondi pm_runtime_set_active(dev); 1578089b7c70SJacopo Mondi pm_runtime_enable(dev); 1579089b7c70SJacopo Mondi pm_runtime_idle(dev); 15803c2472a3SRamiro Oliveira 15813c2472a3SRamiro Oliveira dev_dbg(dev, "OmniVision OV5647 camera driver probed\n"); 1582c9a05cecSJacopo Mondi 15833c2472a3SRamiro Oliveira return 0; 1584c9a05cecSJacopo Mondi 1585089b7c70SJacopo Mondi power_off: 1586089b7c70SJacopo Mondi ov5647_power_off(dev); 1587c9a05cecSJacopo Mondi entity_cleanup: 15883c2472a3SRamiro Oliveira media_entity_cleanup(&sd->entity); 15894974c2f1SDavid Plowman ctrl_handler_free: 15904974c2f1SDavid Plowman v4l2_ctrl_handler_free(&sensor->ctrls); 1591c9a05cecSJacopo Mondi mutex_destroy: 15923c2472a3SRamiro Oliveira mutex_destroy(&sensor->lock); 1593c9a05cecSJacopo Mondi 15943c2472a3SRamiro Oliveira return ret; 15953c2472a3SRamiro Oliveira } 15963c2472a3SRamiro Oliveira 15973c2472a3SRamiro Oliveira static int ov5647_remove(struct i2c_client *client) 15983c2472a3SRamiro Oliveira { 15993c2472a3SRamiro Oliveira struct v4l2_subdev *sd = i2c_get_clientdata(client); 16005bc5ca71SJacopo Mondi struct ov5647 *sensor = to_sensor(sd); 16013c2472a3SRamiro Oliveira 16025bc5ca71SJacopo Mondi v4l2_async_unregister_subdev(&sensor->sd); 16035bc5ca71SJacopo Mondi media_entity_cleanup(&sensor->sd.entity); 16045bc5ca71SJacopo Mondi v4l2_ctrl_handler_free(&sensor->ctrls); 16053c2472a3SRamiro Oliveira v4l2_device_unregister_subdev(sd); 1606089b7c70SJacopo Mondi pm_runtime_disable(&client->dev); 16075bc5ca71SJacopo Mondi mutex_destroy(&sensor->lock); 16083c2472a3SRamiro Oliveira 16093c2472a3SRamiro Oliveira return 0; 16103c2472a3SRamiro Oliveira } 16113c2472a3SRamiro Oliveira 1612089b7c70SJacopo Mondi static const struct dev_pm_ops ov5647_pm_ops = { 1613089b7c70SJacopo Mondi SET_RUNTIME_PM_OPS(ov5647_power_off, ov5647_power_on, NULL) 1614089b7c70SJacopo Mondi }; 1615089b7c70SJacopo Mondi 16163c2472a3SRamiro Oliveira static const struct i2c_device_id ov5647_id[] = { 16173c2472a3SRamiro Oliveira { "ov5647", 0 }, 1618c9a05cecSJacopo Mondi { /* sentinel */ } 16193c2472a3SRamiro Oliveira }; 16203c2472a3SRamiro Oliveira MODULE_DEVICE_TABLE(i2c, ov5647_id); 16213c2472a3SRamiro Oliveira 16223c2472a3SRamiro Oliveira #if IS_ENABLED(CONFIG_OF) 16233c2472a3SRamiro Oliveira static const struct of_device_id ov5647_of_match[] = { 16243c2472a3SRamiro Oliveira { .compatible = "ovti,ov5647" }, 16253c2472a3SRamiro Oliveira { /* sentinel */ }, 16263c2472a3SRamiro Oliveira }; 16273c2472a3SRamiro Oliveira MODULE_DEVICE_TABLE(of, ov5647_of_match); 16283c2472a3SRamiro Oliveira #endif 16293c2472a3SRamiro Oliveira 16303c2472a3SRamiro Oliveira static struct i2c_driver ov5647_driver = { 16313c2472a3SRamiro Oliveira .driver = { 16323c2472a3SRamiro Oliveira .of_match_table = of_match_ptr(ov5647_of_match), 1633c9a05cecSJacopo Mondi .name = "ov5647", 1634089b7c70SJacopo Mondi .pm = &ov5647_pm_ops, 16353c2472a3SRamiro Oliveira }, 1636e6714993SKieran Bingham .probe_new = ov5647_probe, 16373c2472a3SRamiro Oliveira .remove = ov5647_remove, 16383c2472a3SRamiro Oliveira .id_table = ov5647_id, 16393c2472a3SRamiro Oliveira }; 16403c2472a3SRamiro Oliveira 16413c2472a3SRamiro Oliveira module_i2c_driver(ov5647_driver); 16423c2472a3SRamiro Oliveira 16433c2472a3SRamiro Oliveira MODULE_AUTHOR("Ramiro Oliveira <roliveir@synopsys.com>"); 16443c2472a3SRamiro Oliveira MODULE_DESCRIPTION("A low-level driver for OmniVision ov5647 sensors"); 16453c2472a3SRamiro Oliveira MODULE_LICENSE("GPL v2"); 1646