From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Schaaf <ben.schaaf@gmail.com>
Date: Mon, 22 Nov 2021 23:38:26 +1100
Subject: media: ov5640: Improve firmware load time

Downloading the firmware can be done in groups to minimize i2c packets.
The firmware status is set practically immediately upon successfully
loading it, so there's no need for a long timeout or retry. The
firmware can also not be loaded when the sensor isn't powered on, so
don't bother trying.
---
 drivers/media/i2c/ov5640.c | 97 +++++++---
 1 file changed, 71 insertions(+), 26 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 30018f02ba7b..15eb59d6eab6 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -1232,6 +1232,8 @@ static int ov5640_write_reg(struct ov5640_dev *sensor, u16 reg, u8 val)
 	msg.buf = buf;
 	msg.len = sizeof(buf);
 
+	dev_dbg(&client->dev, "[wr %04x] <= %d\n", reg, val);
+
 	ret = i2c_transfer(client->adapter, &msg, 1);
 	if (ret < 0) {
 		dev_err(&client->dev, "%s: error: reg=%x, val=%x\n",
@@ -1242,6 +1244,42 @@ static int ov5640_write_reg(struct ov5640_dev *sensor, u16 reg, u8 val)
 	return 0;
 }
 
+static int ov5640_write_regs(struct ov5640_dev *sensor, u16 reg,
+			     const u8 *data, int data_size)
+{
+	struct i2c_client *client = sensor->i2c_client;
+	struct i2c_msg msg;
+	u8 buf[254 + 2];
+	int ret;
+
+	if (data_size > sizeof(buf) - 2) {
+		v4l2_err(&sensor->sd, "%s: oversized transfer (size=%d)\n",
+			 __func__, data_size);
+		return -EINVAL;
+	}
+
+	buf[0] = reg >> 8;
+	buf[1] = reg & 0xff;
+	memcpy(buf + 2, data, data_size);
+
+	msg.addr = client->addr;
+	msg.flags = client->flags;
+	msg.buf = buf;
+	msg.len = data_size + 2;
+
+	dev_dbg(&client->dev, "[wr %04x] <= %*ph\n", (u32)reg, data_size, data);
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret < 0) {
+		v4l2_err(&sensor->sd,
+			 "%s: error %d: reg=%x, data=%*ph\n",
+			 __func__, ret, (u32)reg, data_size, data);
+		return ret;
+	}
+
+	return 0;
+}
+
 static int ov5640_read_reg(struct ov5640_dev *sensor, u16 reg, u8 *val)
 {
 	struct i2c_client *client = sensor->i2c_client;
@@ -2542,6 +2580,7 @@ static int ov5640_copy_fw_to_device(struct ov5640_dev *sensor,
 	u8 fw_status;
 	int i;
 	int ret;
+	int num_groups, group_size = 254;
 
 	// Putting MCU in reset state
 	ret = ov5640_write_reg(sensor, OV5640_REG_SYS_RESET00, 0x20);
@@ -2549,10 +2588,24 @@ static int ov5640_copy_fw_to_device(struct ov5640_dev *sensor,
 		return ret;
 
 	// Write firmware
-	for (i = 0; i < fw->size / sizeof(u8); i++)
-		ov5640_write_reg(sensor,
-				OV5640_REG_FIRMWARE_BASE + i, 
-				data[i]);
+	num_groups = fw->size / group_size;
+	for (i = 0; i < num_groups; i++) {
+		ret = ov5640_write_regs(sensor,
+					OV5640_REG_FIRMWARE_BASE + i * group_size,
+					data + i * group_size,
+					group_size);
+		if (ret)
+			return ret;
+	}
+
+	if (i * group_size < fw->size) {
+		ret = ov5640_write_regs(sensor,
+					OV5640_REG_FIRMWARE_BASE + i * group_size,
+					data + i * group_size,
+					fw->size - i * group_size);
+		if (ret)
+			return ret;
+	}
 
 	// Reset MCU state
 	ov5640_write_reg(sensor, OV5640_REG_FW_CMD_MAIN, 0x00);
@@ -2574,31 +2627,17 @@ static int ov5640_copy_fw_to_device(struct ov5640_dev *sensor,
 	// Wait for firmware to be ready
 	for (i = 0; i < 5; i++) {
 		ret = ov5640_read_reg(sensor, OV5640_REG_FW_STATUS, &fw_status);
-		if (fw_status == OV5640_FW_STATUS_S_IDLE) {
-			dev_info(&client->dev, "fw started after %d ms\n", i * 50);
+		if (ret) {
 			return ret;
 		}
-		msleep(50);
-	}
-	dev_err(&client->dev, "uploaded firmware didn't start, got to 0x%x, retrying...\n", fw_status);
 
-	// Putting MCU in reset state
-	ret = ov5640_write_reg(sensor, OV5640_REG_SYS_RESET00, 0x20);
-	if (ret)
-		return ret;
-	// Start AF MCU
-	ret = ov5640_write_reg(sensor, OV5640_REG_SYS_RESET00, 0x00);
-	if (ret)
-		return ret;
-	// Wait for firmware to be ready
-	for (i = 0; i < 5; i++) {
-		ret = ov5640_read_reg(sensor, OV5640_REG_FW_STATUS, &fw_status);
 		if (fw_status == OV5640_FW_STATUS_S_IDLE) {
-			dev_info(&client->dev, "fw started after %d ms\n", i * 50);
-			return ret;
+			dev_info(&client->dev, "fw started after %d ms\n", i * 5);
+			return 0;
 		}
-		msleep(50);
+		msleep(5);
 	}
+
 	dev_err(&client->dev, "uploaded firmware didn't start, got to 0x%x\n", fw_status);
 	return -ETIMEDOUT;
 }
@@ -2639,10 +2678,9 @@ static int ov5640_af_init(struct ov5640_dev *sensor)
 		return ret;
 
 	// Set lens focus driver on
-	ov5640_write_reg(sensor, OV5640_REG_VCM_CONTROL4, 0x3f);
+	ret = ov5640_write_reg(sensor, OV5640_REG_VCM_CONTROL4, 0x3f);
 	if (ret)
 		return ret;
-
 	return ret;
 }
 
@@ -3416,9 +3454,16 @@ static int ov5640_set_ctrl_focus(struct ov5640_dev *sensor, int command)
 	struct i2c_client *client = sensor->i2c_client;
 	int ret;
 
+	// Don't attempt to do focus if the embedded controller is powered down
+	if (!sensor->streaming) {
+		dev_err(&client->dev, "%s: can't set focus when not powered\n",
+			__func__);
+		return 0;
+	}
+
 	ret = ov5640_af_init(sensor);
 	if (ret) {
-		dev_err(&client->dev, "%s: no autofocus firmware loaded\n",
+		dev_err(&client->dev, "%s: autofocus firmware load failed\n",
 			__func__);
 		return 0;
 	}
-- 
Armbian

