18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver for the ov7660 sensor
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2009 Erik Andrén
68c2ecf20Sopenharmony_ci * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
78c2ecf20Sopenharmony_ci * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Portions of code to USB interface and ALi driver software,
108c2ecf20Sopenharmony_ci * Copyright (c) 2006 Willem Duinker
118c2ecf20Sopenharmony_ci * v4l2 interface modeled after the V4L2 driver
128c2ecf20Sopenharmony_ci * for SN9C10x PC Camera Controllers
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "m5602_ov7660.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic int ov7660_s_ctrl(struct v4l2_ctrl *ctrl);
208c2ecf20Sopenharmony_cistatic void ov7660_dump_registers(struct sd *sd);
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic const unsigned char preinit_ov7660[][4] = {
238c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
248c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
258c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
268c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
278c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
288c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d},
298c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
308c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_DIR, 0x03},
318c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_DIR, 0x03},
328c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
338c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	{SENSOR, OV7660_OFON, 0x0c},
368c2ecf20Sopenharmony_ci	{SENSOR, OV7660_COM2, 0x11},
378c2ecf20Sopenharmony_ci	{SENSOR, OV7660_COM7, 0x05},
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_DIR, 0x01},
408c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
418c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
428c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
438c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
448c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08},
458c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
468c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
478c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
488c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
498c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
508c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
518c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
528c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
538c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00}
548c2ecf20Sopenharmony_ci};
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic const unsigned char init_ov7660[][4] = {
578c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
588c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
598c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
608c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
618c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
628c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d},
638c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
648c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_DIR, 0x01},
658c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_DIR, 0x01},
668c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
678c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
688c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
698c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
708c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
718c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
728c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
738c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
748c2ecf20Sopenharmony_ci	{SENSOR, OV7660_COM7, 0x80},
758c2ecf20Sopenharmony_ci	{SENSOR, OV7660_CLKRC, 0x80},
768c2ecf20Sopenharmony_ci	{SENSOR, OV7660_COM9, 0x4c},
778c2ecf20Sopenharmony_ci	{SENSOR, OV7660_OFON, 0x43},
788c2ecf20Sopenharmony_ci	{SENSOR, OV7660_COM12, 0x28},
798c2ecf20Sopenharmony_ci	{SENSOR, OV7660_COM8, 0x00},
808c2ecf20Sopenharmony_ci	{SENSOR, OV7660_COM10, 0x40},
818c2ecf20Sopenharmony_ci	{SENSOR, OV7660_HSTART, 0x0c},
828c2ecf20Sopenharmony_ci	{SENSOR, OV7660_HSTOP, 0x61},
838c2ecf20Sopenharmony_ci	{SENSOR, OV7660_HREF, 0xa4},
848c2ecf20Sopenharmony_ci	{SENSOR, OV7660_PSHFT, 0x0b},
858c2ecf20Sopenharmony_ci	{SENSOR, OV7660_VSTART, 0x01},
868c2ecf20Sopenharmony_ci	{SENSOR, OV7660_VSTOP, 0x7a},
878c2ecf20Sopenharmony_ci	{SENSOR, OV7660_VSTOP, 0x00},
888c2ecf20Sopenharmony_ci	{SENSOR, OV7660_COM7, 0x05},
898c2ecf20Sopenharmony_ci	{SENSOR, OV7660_COM6, 0x42},
908c2ecf20Sopenharmony_ci	{SENSOR, OV7660_BBIAS, 0x94},
918c2ecf20Sopenharmony_ci	{SENSOR, OV7660_GbBIAS, 0x94},
928c2ecf20Sopenharmony_ci	{SENSOR, OV7660_RSVD29, 0x94},
938c2ecf20Sopenharmony_ci	{SENSOR, OV7660_RBIAS, 0x94},
948c2ecf20Sopenharmony_ci	{SENSOR, OV7660_COM1, 0x00},
958c2ecf20Sopenharmony_ci	{SENSOR, OV7660_AECH, 0x00},
968c2ecf20Sopenharmony_ci	{SENSOR, OV7660_AECHH, 0x00},
978c2ecf20Sopenharmony_ci	{SENSOR, OV7660_ADC, 0x05},
988c2ecf20Sopenharmony_ci	{SENSOR, OV7660_COM13, 0x00},
998c2ecf20Sopenharmony_ci	{SENSOR, OV7660_RSVDA1, 0x23},
1008c2ecf20Sopenharmony_ci	{SENSOR, OV7660_TSLB, 0x0d},
1018c2ecf20Sopenharmony_ci	{SENSOR, OV7660_HV, 0x80},
1028c2ecf20Sopenharmony_ci	{SENSOR, OV7660_LCC1, 0x00},
1038c2ecf20Sopenharmony_ci	{SENSOR, OV7660_LCC2, 0x00},
1048c2ecf20Sopenharmony_ci	{SENSOR, OV7660_LCC3, 0x10},
1058c2ecf20Sopenharmony_ci	{SENSOR, OV7660_LCC4, 0x40},
1068c2ecf20Sopenharmony_ci	{SENSOR, OV7660_LCC5, 0x01},
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	{SENSOR, OV7660_AECH, 0x20},
1098c2ecf20Sopenharmony_ci	{SENSOR, OV7660_COM1, 0x00},
1108c2ecf20Sopenharmony_ci	{SENSOR, OV7660_OFON, 0x0c},
1118c2ecf20Sopenharmony_ci	{SENSOR, OV7660_COM2, 0x11},
1128c2ecf20Sopenharmony_ci	{SENSOR, OV7660_COM7, 0x05},
1138c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_DIR, 0x01},
1148c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
1158c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
1168c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
1178c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
1188c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08},
1198c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
1208c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
1218c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
1228c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
1238c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
1248c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
1258c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
1268c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
1278c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
1288c2ecf20Sopenharmony_ci	{SENSOR, OV7660_AECH, 0x5f},
1298c2ecf20Sopenharmony_ci	{SENSOR, OV7660_COM1, 0x03},
1308c2ecf20Sopenharmony_ci	{SENSOR, OV7660_OFON, 0x0c},
1318c2ecf20Sopenharmony_ci	{SENSOR, OV7660_COM2, 0x11},
1328c2ecf20Sopenharmony_ci	{SENSOR, OV7660_COM7, 0x05},
1338c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_DIR, 0x01},
1348c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
1358c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
1368c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
1378c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
1388c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08},
1398c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
1408c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
1418c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
1428c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
1438c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
1448c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
1458c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
1468c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
1478c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06},
1508c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
1518c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
1528c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
1538c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81},
1548c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},
1558c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SIG_INI, 0x01},
1568c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
1578c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_VSYNC_PARA, 0x08},
1588c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
1598c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
1608c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
1618c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_VSYNC_PARA, 0xec},
1628c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
1638c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
1648c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SIG_INI, 0x00},
1658c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SIG_INI, 0x02},
1668c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
1678c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_HSYNC_PARA, 0x27},
1688c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_HSYNC_PARA, 0x02},
1698c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_HSYNC_PARA, 0xa7},
1708c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SIG_INI, 0x00},
1718c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
1728c2ecf20Sopenharmony_ci	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
1738c2ecf20Sopenharmony_ci};
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic struct v4l2_pix_format ov7660_modes[] = {
1768c2ecf20Sopenharmony_ci	{
1778c2ecf20Sopenharmony_ci		640,
1788c2ecf20Sopenharmony_ci		480,
1798c2ecf20Sopenharmony_ci		V4L2_PIX_FMT_SBGGR8,
1808c2ecf20Sopenharmony_ci		V4L2_FIELD_NONE,
1818c2ecf20Sopenharmony_ci		.sizeimage =
1828c2ecf20Sopenharmony_ci			640 * 480,
1838c2ecf20Sopenharmony_ci		.bytesperline = 640,
1848c2ecf20Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
1858c2ecf20Sopenharmony_ci		.priv = 0
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci};
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops ov7660_ctrl_ops = {
1908c2ecf20Sopenharmony_ci	.s_ctrl = ov7660_s_ctrl,
1918c2ecf20Sopenharmony_ci};
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ciint ov7660_probe(struct sd *sd)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	int err = 0, i;
1968c2ecf20Sopenharmony_ci	u8 prod_id = 0, ver_id = 0;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (force_sensor) {
1998c2ecf20Sopenharmony_ci		if (force_sensor == OV7660_SENSOR) {
2008c2ecf20Sopenharmony_ci			pr_info("Forcing an %s sensor\n", ov7660.name);
2018c2ecf20Sopenharmony_ci			goto sensor_found;
2028c2ecf20Sopenharmony_ci		}
2038c2ecf20Sopenharmony_ci		/* If we want to force another sensor,
2048c2ecf20Sopenharmony_ci		don't try to probe this one */
2058c2ecf20Sopenharmony_ci		return -ENODEV;
2068c2ecf20Sopenharmony_ci	}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	/* Do the preinit */
2098c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(preinit_ov7660) && !err; i++) {
2108c2ecf20Sopenharmony_ci		u8 data[2];
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci		if (preinit_ov7660[i][0] == BRIDGE) {
2138c2ecf20Sopenharmony_ci			err = m5602_write_bridge(sd,
2148c2ecf20Sopenharmony_ci				preinit_ov7660[i][1],
2158c2ecf20Sopenharmony_ci				preinit_ov7660[i][2]);
2168c2ecf20Sopenharmony_ci		} else {
2178c2ecf20Sopenharmony_ci			data[0] = preinit_ov7660[i][2];
2188c2ecf20Sopenharmony_ci			err = m5602_write_sensor(sd,
2198c2ecf20Sopenharmony_ci				preinit_ov7660[i][1], data, 1);
2208c2ecf20Sopenharmony_ci		}
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci	if (err < 0)
2238c2ecf20Sopenharmony_ci		return err;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	if (m5602_read_sensor(sd, OV7660_PID, &prod_id, 1))
2268c2ecf20Sopenharmony_ci		return -ENODEV;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	if (m5602_read_sensor(sd, OV7660_VER, &ver_id, 1))
2298c2ecf20Sopenharmony_ci		return -ENODEV;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	pr_info("Sensor reported 0x%x%x\n", prod_id, ver_id);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	if ((prod_id == 0x76) && (ver_id == 0x60)) {
2348c2ecf20Sopenharmony_ci		pr_info("Detected a ov7660 sensor\n");
2358c2ecf20Sopenharmony_ci		goto sensor_found;
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci	return -ENODEV;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cisensor_found:
2408c2ecf20Sopenharmony_ci	sd->gspca_dev.cam.cam_mode = ov7660_modes;
2418c2ecf20Sopenharmony_ci	sd->gspca_dev.cam.nmodes = ARRAY_SIZE(ov7660_modes);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	return 0;
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ciint ov7660_init(struct sd *sd)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	int i, err;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	/* Init the sensor */
2518c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(init_ov7660); i++) {
2528c2ecf20Sopenharmony_ci		u8 data[2];
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci		if (init_ov7660[i][0] == BRIDGE) {
2558c2ecf20Sopenharmony_ci			err = m5602_write_bridge(sd,
2568c2ecf20Sopenharmony_ci				init_ov7660[i][1],
2578c2ecf20Sopenharmony_ci				init_ov7660[i][2]);
2588c2ecf20Sopenharmony_ci		} else {
2598c2ecf20Sopenharmony_ci			data[0] = init_ov7660[i][2];
2608c2ecf20Sopenharmony_ci			err = m5602_write_sensor(sd,
2618c2ecf20Sopenharmony_ci				init_ov7660[i][1], data, 1);
2628c2ecf20Sopenharmony_ci		}
2638c2ecf20Sopenharmony_ci		if (err < 0)
2648c2ecf20Sopenharmony_ci			return err;
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	if (dump_sensor)
2688c2ecf20Sopenharmony_ci		ov7660_dump_registers(sd);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	return 0;
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ciint ov7660_init_controls(struct sd *sd)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	sd->gspca_dev.vdev.ctrl_handler = hdl;
2788c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_init(hdl, 6);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &ov7660_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE,
2818c2ecf20Sopenharmony_ci			  0, 1, 1, 1);
2828c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std_menu(hdl, &ov7660_ctrl_ops,
2838c2ecf20Sopenharmony_ci			  V4L2_CID_EXPOSURE_AUTO, 1, 0, V4L2_EXPOSURE_AUTO);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	sd->autogain = v4l2_ctrl_new_std(hdl, &ov7660_ctrl_ops,
2868c2ecf20Sopenharmony_ci					 V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
2878c2ecf20Sopenharmony_ci	sd->gain = v4l2_ctrl_new_std(hdl, &ov7660_ctrl_ops, V4L2_CID_GAIN, 0,
2888c2ecf20Sopenharmony_ci				     255, 1, OV7660_DEFAULT_GAIN);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	sd->hflip = v4l2_ctrl_new_std(hdl, &ov7660_ctrl_ops, V4L2_CID_HFLIP,
2918c2ecf20Sopenharmony_ci				      0, 1, 1, 0);
2928c2ecf20Sopenharmony_ci	sd->vflip = v4l2_ctrl_new_std(hdl, &ov7660_ctrl_ops, V4L2_CID_VFLIP,
2938c2ecf20Sopenharmony_ci				      0, 1, 1, 0);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	if (hdl->error) {
2968c2ecf20Sopenharmony_ci		pr_err("Could not initialize controls\n");
2978c2ecf20Sopenharmony_ci		return hdl->error;
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, false);
3018c2ecf20Sopenharmony_ci	v4l2_ctrl_cluster(2, &sd->hflip);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	return 0;
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ciint ov7660_start(struct sd *sd)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	return 0;
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ciint ov7660_stop(struct sd *sd)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	return 0;
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_civoid ov7660_disconnect(struct sd *sd)
3178c2ecf20Sopenharmony_ci{
3188c2ecf20Sopenharmony_ci	ov7660_stop(sd);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	sd->sensor = NULL;
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic int ov7660_set_gain(struct gspca_dev *gspca_dev, __s32 val)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	int err;
3268c2ecf20Sopenharmony_ci	u8 i2c_data = val;
3278c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_CONF, "Setting gain to %d\n", val);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	err = m5602_write_sensor(sd, OV7660_GAIN, &i2c_data, 1);
3328c2ecf20Sopenharmony_ci	return err;
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic int ov7660_set_auto_white_balance(struct gspca_dev *gspca_dev,
3368c2ecf20Sopenharmony_ci					 __s32 val)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	int err;
3398c2ecf20Sopenharmony_ci	u8 i2c_data;
3408c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_CONF, "Set auto white balance to %d\n", val);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1);
3458c2ecf20Sopenharmony_ci	if (err < 0)
3468c2ecf20Sopenharmony_ci		return err;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1));
3498c2ecf20Sopenharmony_ci	err = m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	return err;
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic int ov7660_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	int err;
3578c2ecf20Sopenharmony_ci	u8 i2c_data;
3588c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_CONF, "Set auto gain control to %d\n", val);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1);
3638c2ecf20Sopenharmony_ci	if (err < 0)
3648c2ecf20Sopenharmony_ci		return err;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2));
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	return m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1);
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic int ov7660_set_auto_exposure(struct gspca_dev *gspca_dev,
3728c2ecf20Sopenharmony_ci				    __s32 val)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	int err;
3758c2ecf20Sopenharmony_ci	u8 i2c_data;
3768c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_CONF, "Set auto exposure control to %d\n", val);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1);
3818c2ecf20Sopenharmony_ci	if (err < 0)
3828c2ecf20Sopenharmony_ci		return err;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	val = (val == V4L2_EXPOSURE_AUTO);
3858c2ecf20Sopenharmony_ci	i2c_data = ((i2c_data & 0xfe) | ((val & 0x01) << 0));
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	return m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1);
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cistatic int ov7660_set_hvflip(struct gspca_dev *gspca_dev)
3918c2ecf20Sopenharmony_ci{
3928c2ecf20Sopenharmony_ci	int err;
3938c2ecf20Sopenharmony_ci	u8 i2c_data;
3948c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_CONF, "Set hvflip to %d, %d\n",
3978c2ecf20Sopenharmony_ci		  sd->hflip->val, sd->vflip->val);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	i2c_data = (sd->hflip->val << 5) | (sd->vflip->val << 4);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	err = m5602_write_sensor(sd, OV7660_MVFP, &i2c_data, 1);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	return err;
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_cistatic int ov7660_s_ctrl(struct v4l2_ctrl *ctrl)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	struct gspca_dev *gspca_dev =
4098c2ecf20Sopenharmony_ci		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
4108c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
4118c2ecf20Sopenharmony_ci	int err;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	if (!gspca_dev->streaming)
4148c2ecf20Sopenharmony_ci		return 0;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	switch (ctrl->id) {
4178c2ecf20Sopenharmony_ci	case V4L2_CID_AUTO_WHITE_BALANCE:
4188c2ecf20Sopenharmony_ci		err = ov7660_set_auto_white_balance(gspca_dev, ctrl->val);
4198c2ecf20Sopenharmony_ci		break;
4208c2ecf20Sopenharmony_ci	case V4L2_CID_EXPOSURE_AUTO:
4218c2ecf20Sopenharmony_ci		err = ov7660_set_auto_exposure(gspca_dev, ctrl->val);
4228c2ecf20Sopenharmony_ci		break;
4238c2ecf20Sopenharmony_ci	case V4L2_CID_AUTOGAIN:
4248c2ecf20Sopenharmony_ci		err = ov7660_set_auto_gain(gspca_dev, ctrl->val);
4258c2ecf20Sopenharmony_ci		if (err || ctrl->val)
4268c2ecf20Sopenharmony_ci			return err;
4278c2ecf20Sopenharmony_ci		err = ov7660_set_gain(gspca_dev, sd->gain->val);
4288c2ecf20Sopenharmony_ci		break;
4298c2ecf20Sopenharmony_ci	case V4L2_CID_HFLIP:
4308c2ecf20Sopenharmony_ci		err = ov7660_set_hvflip(gspca_dev);
4318c2ecf20Sopenharmony_ci		break;
4328c2ecf20Sopenharmony_ci	default:
4338c2ecf20Sopenharmony_ci		return -EINVAL;
4348c2ecf20Sopenharmony_ci	}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	return err;
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_cistatic void ov7660_dump_registers(struct sd *sd)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	int address;
4428c2ecf20Sopenharmony_ci	pr_info("Dumping the ov7660 register state\n");
4438c2ecf20Sopenharmony_ci	for (address = 0; address < 0xa9; address++) {
4448c2ecf20Sopenharmony_ci		u8 value;
4458c2ecf20Sopenharmony_ci		m5602_read_sensor(sd, address, &value, 1);
4468c2ecf20Sopenharmony_ci		pr_info("register 0x%x contains 0x%x\n", address, value);
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	pr_info("ov7660 register state dump complete\n");
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	pr_info("Probing for which registers that are read/write\n");
4528c2ecf20Sopenharmony_ci	for (address = 0; address < 0xff; address++) {
4538c2ecf20Sopenharmony_ci		u8 old_value, ctrl_value;
4548c2ecf20Sopenharmony_ci		u8 test_value[2] = {0xff, 0xff};
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci		m5602_read_sensor(sd, address, &old_value, 1);
4578c2ecf20Sopenharmony_ci		m5602_write_sensor(sd, address, test_value, 1);
4588c2ecf20Sopenharmony_ci		m5602_read_sensor(sd, address, &ctrl_value, 1);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci		if (ctrl_value == test_value[0])
4618c2ecf20Sopenharmony_ci			pr_info("register 0x%x is writeable\n", address);
4628c2ecf20Sopenharmony_ci		else
4638c2ecf20Sopenharmony_ci			pr_info("register 0x%x is read only\n", address);
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci		/* Restore original value */
4668c2ecf20Sopenharmony_ci		m5602_write_sensor(sd, address, &old_value, 1);
4678c2ecf20Sopenharmony_ci	}
4688c2ecf20Sopenharmony_ci}
469