18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * A V4L2 driver for OmniVision OV5647 cameras.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Based on Samsung S5K6AAFX SXGA 1/6" 1.3M CMOS Image Sensor driver
58c2ecf20Sopenharmony_ci * Copyright (C) 2011 Sylwester Nawrocki <s.nawrocki@samsung.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Based on Omnivision OV7670 Camera Driver
88c2ecf20Sopenharmony_ci * Copyright (C) 2006-7 Jonathan Corbet <corbet@lwn.net>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Copyright (C) 2016, Synopsys, Inc.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or
138c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License as
148c2ecf20Sopenharmony_ci * published by the Free Software Foundation version 2.
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * This program is distributed .as is. WITHOUT ANY WARRANTY of any
178c2ecf20Sopenharmony_ci * kind, whether express or implied; without even the implied warranty
188c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
198c2ecf20Sopenharmony_ci * GNU General Public License for more details.
208c2ecf20Sopenharmony_ci */
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <linux/clk.h>
238c2ecf20Sopenharmony_ci#include <linux/delay.h>
248c2ecf20Sopenharmony_ci#include <linux/i2c.h>
258c2ecf20Sopenharmony_ci#include <linux/init.h>
268c2ecf20Sopenharmony_ci#include <linux/io.h>
278c2ecf20Sopenharmony_ci#include <linux/module.h>
288c2ecf20Sopenharmony_ci#include <linux/of_graph.h>
298c2ecf20Sopenharmony_ci#include <linux/slab.h>
308c2ecf20Sopenharmony_ci#include <linux/videodev2.h>
318c2ecf20Sopenharmony_ci#include <media/v4l2-device.h>
328c2ecf20Sopenharmony_ci#include <media/v4l2-fwnode.h>
338c2ecf20Sopenharmony_ci#include <media/v4l2-image-sizes.h>
348c2ecf20Sopenharmony_ci#include <media/v4l2-mediabus.h>
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define SENSOR_NAME "ov5647"
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define MIPI_CTRL00_CLOCK_LANE_GATE		BIT(5)
398c2ecf20Sopenharmony_ci#define MIPI_CTRL00_BUS_IDLE			BIT(2)
408c2ecf20Sopenharmony_ci#define MIPI_CTRL00_CLOCK_LANE_DISABLE		BIT(0)
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define OV5647_SW_STANDBY		0x0100
438c2ecf20Sopenharmony_ci#define OV5647_SW_RESET			0x0103
448c2ecf20Sopenharmony_ci#define OV5647_REG_CHIPID_H		0x300A
458c2ecf20Sopenharmony_ci#define OV5647_REG_CHIPID_L		0x300B
468c2ecf20Sopenharmony_ci#define OV5640_REG_PAD_OUT		0x300D
478c2ecf20Sopenharmony_ci#define OV5647_REG_FRAME_OFF_NUMBER	0x4202
488c2ecf20Sopenharmony_ci#define OV5647_REG_MIPI_CTRL00		0x4800
498c2ecf20Sopenharmony_ci#define OV5647_REG_MIPI_CTRL14		0x4814
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#define REG_TERM 0xfffe
528c2ecf20Sopenharmony_ci#define VAL_TERM 0xfe
538c2ecf20Sopenharmony_ci#define REG_DLY  0xffff
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#define OV5647_ROW_START		0x01
568c2ecf20Sopenharmony_ci#define OV5647_ROW_START_MIN		0
578c2ecf20Sopenharmony_ci#define OV5647_ROW_START_MAX		2004
588c2ecf20Sopenharmony_ci#define OV5647_ROW_START_DEF		54
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci#define OV5647_COLUMN_START		0x02
618c2ecf20Sopenharmony_ci#define OV5647_COLUMN_START_MIN		0
628c2ecf20Sopenharmony_ci#define OV5647_COLUMN_START_MAX		2750
638c2ecf20Sopenharmony_ci#define OV5647_COLUMN_START_DEF		16
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci#define OV5647_WINDOW_HEIGHT		0x03
668c2ecf20Sopenharmony_ci#define OV5647_WINDOW_HEIGHT_MIN	2
678c2ecf20Sopenharmony_ci#define OV5647_WINDOW_HEIGHT_MAX	2006
688c2ecf20Sopenharmony_ci#define OV5647_WINDOW_HEIGHT_DEF	1944
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci#define OV5647_WINDOW_WIDTH		0x04
718c2ecf20Sopenharmony_ci#define OV5647_WINDOW_WIDTH_MIN		2
728c2ecf20Sopenharmony_ci#define OV5647_WINDOW_WIDTH_MAX		2752
738c2ecf20Sopenharmony_ci#define OV5647_WINDOW_WIDTH_DEF		2592
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistruct regval_list {
768c2ecf20Sopenharmony_ci	u16 addr;
778c2ecf20Sopenharmony_ci	u8 data;
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistruct ov5647 {
818c2ecf20Sopenharmony_ci	struct v4l2_subdev		sd;
828c2ecf20Sopenharmony_ci	struct media_pad		pad;
838c2ecf20Sopenharmony_ci	struct mutex			lock;
848c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt	format;
858c2ecf20Sopenharmony_ci	unsigned int			width;
868c2ecf20Sopenharmony_ci	unsigned int			height;
878c2ecf20Sopenharmony_ci	int				power_count;
888c2ecf20Sopenharmony_ci	struct clk			*xclk;
898c2ecf20Sopenharmony_ci};
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic inline struct ov5647 *to_state(struct v4l2_subdev *sd)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	return container_of(sd, struct ov5647, sd);
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic struct regval_list sensor_oe_disable_regs[] = {
978c2ecf20Sopenharmony_ci	{0x3000, 0x00},
988c2ecf20Sopenharmony_ci	{0x3001, 0x00},
998c2ecf20Sopenharmony_ci	{0x3002, 0x00},
1008c2ecf20Sopenharmony_ci};
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic struct regval_list sensor_oe_enable_regs[] = {
1038c2ecf20Sopenharmony_ci	{0x3000, 0x0f},
1048c2ecf20Sopenharmony_ci	{0x3001, 0xff},
1058c2ecf20Sopenharmony_ci	{0x3002, 0xe4},
1068c2ecf20Sopenharmony_ci};
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic struct regval_list ov5647_640x480[] = {
1098c2ecf20Sopenharmony_ci	{0x0100, 0x00},
1108c2ecf20Sopenharmony_ci	{0x0103, 0x01},
1118c2ecf20Sopenharmony_ci	{0x3034, 0x08},
1128c2ecf20Sopenharmony_ci	{0x3035, 0x21},
1138c2ecf20Sopenharmony_ci	{0x3036, 0x46},
1148c2ecf20Sopenharmony_ci	{0x303c, 0x11},
1158c2ecf20Sopenharmony_ci	{0x3106, 0xf5},
1168c2ecf20Sopenharmony_ci	{0x3821, 0x07},
1178c2ecf20Sopenharmony_ci	{0x3820, 0x41},
1188c2ecf20Sopenharmony_ci	{0x3827, 0xec},
1198c2ecf20Sopenharmony_ci	{0x370c, 0x0f},
1208c2ecf20Sopenharmony_ci	{0x3612, 0x59},
1218c2ecf20Sopenharmony_ci	{0x3618, 0x00},
1228c2ecf20Sopenharmony_ci	{0x5000, 0x06},
1238c2ecf20Sopenharmony_ci	{0x5001, 0x01},
1248c2ecf20Sopenharmony_ci	{0x5002, 0x41},
1258c2ecf20Sopenharmony_ci	{0x5003, 0x08},
1268c2ecf20Sopenharmony_ci	{0x5a00, 0x08},
1278c2ecf20Sopenharmony_ci	{0x3000, 0x00},
1288c2ecf20Sopenharmony_ci	{0x3001, 0x00},
1298c2ecf20Sopenharmony_ci	{0x3002, 0x00},
1308c2ecf20Sopenharmony_ci	{0x3016, 0x08},
1318c2ecf20Sopenharmony_ci	{0x3017, 0xe0},
1328c2ecf20Sopenharmony_ci	{0x3018, 0x44},
1338c2ecf20Sopenharmony_ci	{0x301c, 0xf8},
1348c2ecf20Sopenharmony_ci	{0x301d, 0xf0},
1358c2ecf20Sopenharmony_ci	{0x3a18, 0x00},
1368c2ecf20Sopenharmony_ci	{0x3a19, 0xf8},
1378c2ecf20Sopenharmony_ci	{0x3c01, 0x80},
1388c2ecf20Sopenharmony_ci	{0x3b07, 0x0c},
1398c2ecf20Sopenharmony_ci	{0x380c, 0x07},
1408c2ecf20Sopenharmony_ci	{0x380d, 0x68},
1418c2ecf20Sopenharmony_ci	{0x380e, 0x03},
1428c2ecf20Sopenharmony_ci	{0x380f, 0xd8},
1438c2ecf20Sopenharmony_ci	{0x3814, 0x31},
1448c2ecf20Sopenharmony_ci	{0x3815, 0x31},
1458c2ecf20Sopenharmony_ci	{0x3708, 0x64},
1468c2ecf20Sopenharmony_ci	{0x3709, 0x52},
1478c2ecf20Sopenharmony_ci	{0x3808, 0x02},
1488c2ecf20Sopenharmony_ci	{0x3809, 0x80},
1498c2ecf20Sopenharmony_ci	{0x380a, 0x01},
1508c2ecf20Sopenharmony_ci	{0x380b, 0xE0},
1518c2ecf20Sopenharmony_ci	{0x3801, 0x00},
1528c2ecf20Sopenharmony_ci	{0x3802, 0x00},
1538c2ecf20Sopenharmony_ci	{0x3803, 0x00},
1548c2ecf20Sopenharmony_ci	{0x3804, 0x0a},
1558c2ecf20Sopenharmony_ci	{0x3805, 0x3f},
1568c2ecf20Sopenharmony_ci	{0x3806, 0x07},
1578c2ecf20Sopenharmony_ci	{0x3807, 0xa1},
1588c2ecf20Sopenharmony_ci	{0x3811, 0x08},
1598c2ecf20Sopenharmony_ci	{0x3813, 0x02},
1608c2ecf20Sopenharmony_ci	{0x3630, 0x2e},
1618c2ecf20Sopenharmony_ci	{0x3632, 0xe2},
1628c2ecf20Sopenharmony_ci	{0x3633, 0x23},
1638c2ecf20Sopenharmony_ci	{0x3634, 0x44},
1648c2ecf20Sopenharmony_ci	{0x3636, 0x06},
1658c2ecf20Sopenharmony_ci	{0x3620, 0x64},
1668c2ecf20Sopenharmony_ci	{0x3621, 0xe0},
1678c2ecf20Sopenharmony_ci	{0x3600, 0x37},
1688c2ecf20Sopenharmony_ci	{0x3704, 0xa0},
1698c2ecf20Sopenharmony_ci	{0x3703, 0x5a},
1708c2ecf20Sopenharmony_ci	{0x3715, 0x78},
1718c2ecf20Sopenharmony_ci	{0x3717, 0x01},
1728c2ecf20Sopenharmony_ci	{0x3731, 0x02},
1738c2ecf20Sopenharmony_ci	{0x370b, 0x60},
1748c2ecf20Sopenharmony_ci	{0x3705, 0x1a},
1758c2ecf20Sopenharmony_ci	{0x3f05, 0x02},
1768c2ecf20Sopenharmony_ci	{0x3f06, 0x10},
1778c2ecf20Sopenharmony_ci	{0x3f01, 0x0a},
1788c2ecf20Sopenharmony_ci	{0x3a08, 0x01},
1798c2ecf20Sopenharmony_ci	{0x3a09, 0x27},
1808c2ecf20Sopenharmony_ci	{0x3a0a, 0x00},
1818c2ecf20Sopenharmony_ci	{0x3a0b, 0xf6},
1828c2ecf20Sopenharmony_ci	{0x3a0d, 0x04},
1838c2ecf20Sopenharmony_ci	{0x3a0e, 0x03},
1848c2ecf20Sopenharmony_ci	{0x3a0f, 0x58},
1858c2ecf20Sopenharmony_ci	{0x3a10, 0x50},
1868c2ecf20Sopenharmony_ci	{0x3a1b, 0x58},
1878c2ecf20Sopenharmony_ci	{0x3a1e, 0x50},
1888c2ecf20Sopenharmony_ci	{0x3a11, 0x60},
1898c2ecf20Sopenharmony_ci	{0x3a1f, 0x28},
1908c2ecf20Sopenharmony_ci	{0x4001, 0x02},
1918c2ecf20Sopenharmony_ci	{0x4004, 0x02},
1928c2ecf20Sopenharmony_ci	{0x4000, 0x09},
1938c2ecf20Sopenharmony_ci	{0x4837, 0x24},
1948c2ecf20Sopenharmony_ci	{0x4050, 0x6e},
1958c2ecf20Sopenharmony_ci	{0x4051, 0x8f},
1968c2ecf20Sopenharmony_ci	{0x0100, 0x01},
1978c2ecf20Sopenharmony_ci};
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	int ret;
2028c2ecf20Sopenharmony_ci	unsigned char data[3] = { reg >> 8, reg & 0xff, val};
2038c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	ret = i2c_master_send(client, data, 3);
2068c2ecf20Sopenharmony_ci	if (ret < 0)
2078c2ecf20Sopenharmony_ci		dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n",
2088c2ecf20Sopenharmony_ci				__func__, reg);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	return ret;
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic int ov5647_read(struct v4l2_subdev *sd, u16 reg, u8 *val)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	int ret;
2168c2ecf20Sopenharmony_ci	unsigned char data_w[2] = { reg >> 8, reg & 0xff };
2178c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	ret = i2c_master_send(client, data_w, 2);
2208c2ecf20Sopenharmony_ci	if (ret < 0) {
2218c2ecf20Sopenharmony_ci		dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n",
2228c2ecf20Sopenharmony_ci			__func__, reg);
2238c2ecf20Sopenharmony_ci		return ret;
2248c2ecf20Sopenharmony_ci	}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	ret = i2c_master_recv(client, val, 1);
2278c2ecf20Sopenharmony_ci	if (ret < 0)
2288c2ecf20Sopenharmony_ci		dev_dbg(&client->dev, "%s: i2c read error, reg: %x\n",
2298c2ecf20Sopenharmony_ci				__func__, reg);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	return ret;
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic int ov5647_write_array(struct v4l2_subdev *sd,
2358c2ecf20Sopenharmony_ci				struct regval_list *regs, int array_size)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	int i, ret;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	for (i = 0; i < array_size; i++) {
2408c2ecf20Sopenharmony_ci		ret = ov5647_write(sd, regs[i].addr, regs[i].data);
2418c2ecf20Sopenharmony_ci		if (ret < 0)
2428c2ecf20Sopenharmony_ci			return ret;
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	return 0;
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic int ov5647_set_virtual_channel(struct v4l2_subdev *sd, int channel)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	u8 channel_id;
2518c2ecf20Sopenharmony_ci	int ret;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	ret = ov5647_read(sd, OV5647_REG_MIPI_CTRL14, &channel_id);
2548c2ecf20Sopenharmony_ci	if (ret < 0)
2558c2ecf20Sopenharmony_ci		return ret;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	channel_id &= ~(3 << 6);
2588c2ecf20Sopenharmony_ci	return ov5647_write(sd, OV5647_REG_MIPI_CTRL14, channel_id | (channel << 6));
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cistatic int ov5647_stream_on(struct v4l2_subdev *sd)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	int ret;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	ret = ov5647_write(sd, OV5647_REG_MIPI_CTRL00, MIPI_CTRL00_BUS_IDLE);
2668c2ecf20Sopenharmony_ci	if (ret < 0)
2678c2ecf20Sopenharmony_ci		return ret;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	ret = ov5647_write(sd, OV5647_REG_FRAME_OFF_NUMBER, 0x00);
2708c2ecf20Sopenharmony_ci	if (ret < 0)
2718c2ecf20Sopenharmony_ci		return ret;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	return ov5647_write(sd, OV5640_REG_PAD_OUT, 0x00);
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic int ov5647_stream_off(struct v4l2_subdev *sd)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	int ret;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	ret = ov5647_write(sd, OV5647_REG_MIPI_CTRL00, MIPI_CTRL00_CLOCK_LANE_GATE
2818c2ecf20Sopenharmony_ci			   | MIPI_CTRL00_BUS_IDLE | MIPI_CTRL00_CLOCK_LANE_DISABLE);
2828c2ecf20Sopenharmony_ci	if (ret < 0)
2838c2ecf20Sopenharmony_ci		return ret;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	ret = ov5647_write(sd, OV5647_REG_FRAME_OFF_NUMBER, 0x0f);
2868c2ecf20Sopenharmony_ci	if (ret < 0)
2878c2ecf20Sopenharmony_ci		return ret;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	return ov5647_write(sd, OV5640_REG_PAD_OUT, 0x01);
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_cistatic int set_sw_standby(struct v4l2_subdev *sd, bool standby)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	int ret;
2958c2ecf20Sopenharmony_ci	u8 rdval;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval);
2988c2ecf20Sopenharmony_ci	if (ret < 0)
2998c2ecf20Sopenharmony_ci		return ret;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	if (standby)
3028c2ecf20Sopenharmony_ci		rdval &= ~0x01;
3038c2ecf20Sopenharmony_ci	else
3048c2ecf20Sopenharmony_ci		rdval |= 0x01;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	return ov5647_write(sd, OV5647_SW_STANDBY, rdval);
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_cistatic int __sensor_init(struct v4l2_subdev *sd)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	int ret;
3128c2ecf20Sopenharmony_ci	u8 resetval, rdval;
3138c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval);
3168c2ecf20Sopenharmony_ci	if (ret < 0)
3178c2ecf20Sopenharmony_ci		return ret;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	ret = ov5647_write_array(sd, ov5647_640x480,
3208c2ecf20Sopenharmony_ci					ARRAY_SIZE(ov5647_640x480));
3218c2ecf20Sopenharmony_ci	if (ret < 0) {
3228c2ecf20Sopenharmony_ci		dev_err(&client->dev, "write sensor default regs error\n");
3238c2ecf20Sopenharmony_ci		return ret;
3248c2ecf20Sopenharmony_ci	}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	ret = ov5647_set_virtual_channel(sd, 0);
3278c2ecf20Sopenharmony_ci	if (ret < 0)
3288c2ecf20Sopenharmony_ci		return ret;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval);
3318c2ecf20Sopenharmony_ci	if (ret < 0)
3328c2ecf20Sopenharmony_ci		return ret;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	if (!(resetval & 0x01)) {
3358c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Device was in SW standby");
3368c2ecf20Sopenharmony_ci		ret = ov5647_write(sd, OV5647_SW_STANDBY, 0x01);
3378c2ecf20Sopenharmony_ci		if (ret < 0)
3388c2ecf20Sopenharmony_ci			return ret;
3398c2ecf20Sopenharmony_ci	}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	/*
3428c2ecf20Sopenharmony_ci	 * stream off to make the clock lane into LP-11 state.
3438c2ecf20Sopenharmony_ci	 */
3448c2ecf20Sopenharmony_ci	return ov5647_stream_off(sd);
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic int ov5647_sensor_power(struct v4l2_subdev *sd, int on)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	int ret = 0;
3508c2ecf20Sopenharmony_ci	struct ov5647 *ov5647 = to_state(sd);
3518c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	mutex_lock(&ov5647->lock);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	if (on && !ov5647->power_count)	{
3568c2ecf20Sopenharmony_ci		dev_dbg(&client->dev, "OV5647 power on\n");
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci		ret = clk_prepare_enable(ov5647->xclk);
3598c2ecf20Sopenharmony_ci		if (ret < 0) {
3608c2ecf20Sopenharmony_ci			dev_err(&client->dev, "clk prepare enable failed\n");
3618c2ecf20Sopenharmony_ci			goto out;
3628c2ecf20Sopenharmony_ci		}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci		ret = ov5647_write_array(sd, sensor_oe_enable_regs,
3658c2ecf20Sopenharmony_ci				ARRAY_SIZE(sensor_oe_enable_regs));
3668c2ecf20Sopenharmony_ci		if (ret < 0) {
3678c2ecf20Sopenharmony_ci			clk_disable_unprepare(ov5647->xclk);
3688c2ecf20Sopenharmony_ci			dev_err(&client->dev,
3698c2ecf20Sopenharmony_ci				"write sensor_oe_enable_regs error\n");
3708c2ecf20Sopenharmony_ci			goto out;
3718c2ecf20Sopenharmony_ci		}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci		ret = __sensor_init(sd);
3748c2ecf20Sopenharmony_ci		if (ret < 0) {
3758c2ecf20Sopenharmony_ci			clk_disable_unprepare(ov5647->xclk);
3768c2ecf20Sopenharmony_ci			dev_err(&client->dev,
3778c2ecf20Sopenharmony_ci				"Camera not available, check Power\n");
3788c2ecf20Sopenharmony_ci			goto out;
3798c2ecf20Sopenharmony_ci		}
3808c2ecf20Sopenharmony_ci	} else if (!on && ov5647->power_count == 1) {
3818c2ecf20Sopenharmony_ci		dev_dbg(&client->dev, "OV5647 power off\n");
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci		ret = ov5647_write_array(sd, sensor_oe_disable_regs,
3848c2ecf20Sopenharmony_ci				ARRAY_SIZE(sensor_oe_disable_regs));
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci		if (ret < 0)
3878c2ecf20Sopenharmony_ci			dev_dbg(&client->dev, "disable oe failed\n");
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci		ret = set_sw_standby(sd, true);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci		if (ret < 0)
3928c2ecf20Sopenharmony_ci			dev_dbg(&client->dev, "soft stby failed\n");
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci		clk_disable_unprepare(ov5647->xclk);
3958c2ecf20Sopenharmony_ci	}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	/* Update the power count. */
3988c2ecf20Sopenharmony_ci	ov5647->power_count += on ? 1 : -1;
3998c2ecf20Sopenharmony_ci	WARN_ON(ov5647->power_count < 0);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ciout:
4028c2ecf20Sopenharmony_ci	mutex_unlock(&ov5647->lock);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	return ret;
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG
4088c2ecf20Sopenharmony_cistatic int ov5647_sensor_get_register(struct v4l2_subdev *sd,
4098c2ecf20Sopenharmony_ci				struct v4l2_dbg_register *reg)
4108c2ecf20Sopenharmony_ci{
4118c2ecf20Sopenharmony_ci	u8 val;
4128c2ecf20Sopenharmony_ci	int ret;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	ret = ov5647_read(sd, reg->reg & 0xff, &val);
4158c2ecf20Sopenharmony_ci	if (ret < 0)
4168c2ecf20Sopenharmony_ci		return ret;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	reg->val = val;
4198c2ecf20Sopenharmony_ci	reg->size = 1;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	return 0;
4228c2ecf20Sopenharmony_ci}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_cistatic int ov5647_sensor_set_register(struct v4l2_subdev *sd,
4258c2ecf20Sopenharmony_ci				const struct v4l2_dbg_register *reg)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	return ov5647_write(sd, reg->reg & 0xff, reg->val & 0xff);
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci#endif
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci/*
4328c2ecf20Sopenharmony_ci * Subdev core operations registration
4338c2ecf20Sopenharmony_ci */
4348c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops ov5647_subdev_core_ops = {
4358c2ecf20Sopenharmony_ci	.s_power		= ov5647_sensor_power,
4368c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG
4378c2ecf20Sopenharmony_ci	.g_register		= ov5647_sensor_get_register,
4388c2ecf20Sopenharmony_ci	.s_register		= ov5647_sensor_set_register,
4398c2ecf20Sopenharmony_ci#endif
4408c2ecf20Sopenharmony_ci};
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cistatic int ov5647_s_stream(struct v4l2_subdev *sd, int enable)
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	if (enable)
4458c2ecf20Sopenharmony_ci		return ov5647_stream_on(sd);
4468c2ecf20Sopenharmony_ci	else
4478c2ecf20Sopenharmony_ci		return ov5647_stream_off(sd);
4488c2ecf20Sopenharmony_ci}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops ov5647_subdev_video_ops = {
4518c2ecf20Sopenharmony_ci	.s_stream =		ov5647_s_stream,
4528c2ecf20Sopenharmony_ci};
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_cistatic int ov5647_enum_mbus_code(struct v4l2_subdev *sd,
4558c2ecf20Sopenharmony_ci				struct v4l2_subdev_pad_config *cfg,
4568c2ecf20Sopenharmony_ci				struct v4l2_subdev_mbus_code_enum *code)
4578c2ecf20Sopenharmony_ci{
4588c2ecf20Sopenharmony_ci	if (code->index > 0)
4598c2ecf20Sopenharmony_ci		return -EINVAL;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	code->code = MEDIA_BUS_FMT_SBGGR8_1X8;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	return 0;
4648c2ecf20Sopenharmony_ci}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops ov5647_subdev_pad_ops = {
4678c2ecf20Sopenharmony_ci	.enum_mbus_code = ov5647_enum_mbus_code,
4688c2ecf20Sopenharmony_ci};
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops ov5647_subdev_ops = {
4718c2ecf20Sopenharmony_ci	.core		= &ov5647_subdev_core_ops,
4728c2ecf20Sopenharmony_ci	.video		= &ov5647_subdev_video_ops,
4738c2ecf20Sopenharmony_ci	.pad		= &ov5647_subdev_pad_ops,
4748c2ecf20Sopenharmony_ci};
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_cistatic int ov5647_detect(struct v4l2_subdev *sd)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	u8 read;
4798c2ecf20Sopenharmony_ci	int ret;
4808c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	ret = ov5647_write(sd, OV5647_SW_RESET, 0x01);
4838c2ecf20Sopenharmony_ci	if (ret < 0)
4848c2ecf20Sopenharmony_ci		return ret;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	ret = ov5647_read(sd, OV5647_REG_CHIPID_H, &read);
4878c2ecf20Sopenharmony_ci	if (ret < 0)
4888c2ecf20Sopenharmony_ci		return ret;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	if (read != 0x56) {
4918c2ecf20Sopenharmony_ci		dev_err(&client->dev, "ID High expected 0x56 got %x", read);
4928c2ecf20Sopenharmony_ci		return -ENODEV;
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	ret = ov5647_read(sd, OV5647_REG_CHIPID_L, &read);
4968c2ecf20Sopenharmony_ci	if (ret < 0)
4978c2ecf20Sopenharmony_ci		return ret;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	if (read != 0x47) {
5008c2ecf20Sopenharmony_ci		dev_err(&client->dev, "ID Low expected 0x47 got %x", read);
5018c2ecf20Sopenharmony_ci		return -ENODEV;
5028c2ecf20Sopenharmony_ci	}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	return ov5647_write(sd, OV5647_SW_RESET, 0x00);
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cistatic int ov5647_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *format =
5108c2ecf20Sopenharmony_ci				v4l2_subdev_get_try_format(sd, fh->pad, 0);
5118c2ecf20Sopenharmony_ci	struct v4l2_rect *crop =
5128c2ecf20Sopenharmony_ci				v4l2_subdev_get_try_crop(sd, fh->pad, 0);
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	crop->left = OV5647_COLUMN_START_DEF;
5158c2ecf20Sopenharmony_ci	crop->top = OV5647_ROW_START_DEF;
5168c2ecf20Sopenharmony_ci	crop->width = OV5647_WINDOW_WIDTH_DEF;
5178c2ecf20Sopenharmony_ci	crop->height = OV5647_WINDOW_HEIGHT_DEF;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	format->code = MEDIA_BUS_FMT_SBGGR8_1X8;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	format->width = OV5647_WINDOW_WIDTH_DEF;
5228c2ecf20Sopenharmony_ci	format->height = OV5647_WINDOW_HEIGHT_DEF;
5238c2ecf20Sopenharmony_ci	format->field = V4L2_FIELD_NONE;
5248c2ecf20Sopenharmony_ci	format->colorspace = V4L2_COLORSPACE_SRGB;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	return 0;
5278c2ecf20Sopenharmony_ci}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_internal_ops ov5647_subdev_internal_ops = {
5308c2ecf20Sopenharmony_ci	.open = ov5647_open,
5318c2ecf20Sopenharmony_ci};
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_cistatic int ov5647_parse_dt(struct device_node *np)
5348c2ecf20Sopenharmony_ci{
5358c2ecf20Sopenharmony_ci	struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 };
5368c2ecf20Sopenharmony_ci	struct device_node *ep;
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	int ret;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	ep = of_graph_get_next_endpoint(np, NULL);
5418c2ecf20Sopenharmony_ci	if (!ep)
5428c2ecf20Sopenharmony_ci		return -EINVAL;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	of_node_put(ep);
5478c2ecf20Sopenharmony_ci	return ret;
5488c2ecf20Sopenharmony_ci}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic int ov5647_probe(struct i2c_client *client)
5518c2ecf20Sopenharmony_ci{
5528c2ecf20Sopenharmony_ci	struct device *dev = &client->dev;
5538c2ecf20Sopenharmony_ci	struct ov5647 *sensor;
5548c2ecf20Sopenharmony_ci	int ret;
5558c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd;
5568c2ecf20Sopenharmony_ci	struct device_node *np = client->dev.of_node;
5578c2ecf20Sopenharmony_ci	u32 xclk_freq;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
5608c2ecf20Sopenharmony_ci	if (!sensor)
5618c2ecf20Sopenharmony_ci		return -ENOMEM;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_OF) && np) {
5648c2ecf20Sopenharmony_ci		ret = ov5647_parse_dt(np);
5658c2ecf20Sopenharmony_ci		if (ret) {
5668c2ecf20Sopenharmony_ci			dev_err(dev, "DT parsing error: %d\n", ret);
5678c2ecf20Sopenharmony_ci			return ret;
5688c2ecf20Sopenharmony_ci		}
5698c2ecf20Sopenharmony_ci	}
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	/* get system clock (xclk) */
5728c2ecf20Sopenharmony_ci	sensor->xclk = devm_clk_get(dev, NULL);
5738c2ecf20Sopenharmony_ci	if (IS_ERR(sensor->xclk)) {
5748c2ecf20Sopenharmony_ci		dev_err(dev, "could not get xclk");
5758c2ecf20Sopenharmony_ci		return PTR_ERR(sensor->xclk);
5768c2ecf20Sopenharmony_ci	}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	xclk_freq = clk_get_rate(sensor->xclk);
5798c2ecf20Sopenharmony_ci	if (xclk_freq != 25000000) {
5808c2ecf20Sopenharmony_ci		dev_err(dev, "Unsupported clock frequency: %u\n", xclk_freq);
5818c2ecf20Sopenharmony_ci		return -EINVAL;
5828c2ecf20Sopenharmony_ci	}
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	mutex_init(&sensor->lock);
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	sd = &sensor->sd;
5878c2ecf20Sopenharmony_ci	v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops);
5888c2ecf20Sopenharmony_ci	sensor->sd.internal_ops = &ov5647_subdev_internal_ops;
5898c2ecf20Sopenharmony_ci	sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
5928c2ecf20Sopenharmony_ci	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
5938c2ecf20Sopenharmony_ci	ret = media_entity_pads_init(&sd->entity, 1, &sensor->pad);
5948c2ecf20Sopenharmony_ci	if (ret < 0)
5958c2ecf20Sopenharmony_ci		goto mutex_remove;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	ret = ov5647_detect(sd);
5988c2ecf20Sopenharmony_ci	if (ret < 0)
5998c2ecf20Sopenharmony_ci		goto error;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	ret = v4l2_async_register_subdev(sd);
6028c2ecf20Sopenharmony_ci	if (ret < 0)
6038c2ecf20Sopenharmony_ci		goto error;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	dev_dbg(dev, "OmniVision OV5647 camera driver probed\n");
6068c2ecf20Sopenharmony_ci	return 0;
6078c2ecf20Sopenharmony_cierror:
6088c2ecf20Sopenharmony_ci	media_entity_cleanup(&sd->entity);
6098c2ecf20Sopenharmony_cimutex_remove:
6108c2ecf20Sopenharmony_ci	mutex_destroy(&sensor->lock);
6118c2ecf20Sopenharmony_ci	return ret;
6128c2ecf20Sopenharmony_ci}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_cistatic int ov5647_remove(struct i2c_client *client)
6158c2ecf20Sopenharmony_ci{
6168c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd = i2c_get_clientdata(client);
6178c2ecf20Sopenharmony_ci	struct ov5647 *ov5647 = to_state(sd);
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	v4l2_async_unregister_subdev(&ov5647->sd);
6208c2ecf20Sopenharmony_ci	media_entity_cleanup(&ov5647->sd.entity);
6218c2ecf20Sopenharmony_ci	v4l2_device_unregister_subdev(sd);
6228c2ecf20Sopenharmony_ci	mutex_destroy(&ov5647->lock);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	return 0;
6258c2ecf20Sopenharmony_ci}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_cistatic const struct i2c_device_id ov5647_id[] = {
6288c2ecf20Sopenharmony_ci	{ "ov5647", 0 },
6298c2ecf20Sopenharmony_ci	{ }
6308c2ecf20Sopenharmony_ci};
6318c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ov5647_id);
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_OF)
6348c2ecf20Sopenharmony_cistatic const struct of_device_id ov5647_of_match[] = {
6358c2ecf20Sopenharmony_ci	{ .compatible = "ovti,ov5647" },
6368c2ecf20Sopenharmony_ci	{ /* sentinel */ },
6378c2ecf20Sopenharmony_ci};
6388c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ov5647_of_match);
6398c2ecf20Sopenharmony_ci#endif
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_cistatic struct i2c_driver ov5647_driver = {
6428c2ecf20Sopenharmony_ci	.driver = {
6438c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(ov5647_of_match),
6448c2ecf20Sopenharmony_ci		.name	= SENSOR_NAME,
6458c2ecf20Sopenharmony_ci	},
6468c2ecf20Sopenharmony_ci	.probe_new	= ov5647_probe,
6478c2ecf20Sopenharmony_ci	.remove		= ov5647_remove,
6488c2ecf20Sopenharmony_ci	.id_table	= ov5647_id,
6498c2ecf20Sopenharmony_ci};
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_cimodule_i2c_driver(ov5647_driver);
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ramiro Oliveira <roliveir@synopsys.com>");
6548c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("A low-level driver for OmniVision ov5647 sensors");
6558c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
656