build/patch/kernel/archive/sunxi-6.2/patches.megous/media-ov5640-Improve-firmware-load-time.patch

178 lines
5.2 KiB
Diff
Raw Permalink Normal View History

From b96793244105aaa3522574740be6b78cbe598eb6 Mon Sep 17 00:00:00 2001
From: Benjamin Schaaf <ben.schaaf@gmail.com>
Date: Mon, 22 Nov 2021 23:38:26 +1100
Subject: [PATCH 048/391] 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 750361a39..d67298018 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -1221,6 +1221,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",
@@ -1231,6 +1233,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;
@@ -2510,6 +2548,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);
@@ -2517,10 +2556,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);
@@ -2542,31 +2595,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;
}
@@ -2607,10 +2646,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;
}
@@ -3390,9 +3428,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;
}
--
2.35.3