178 lines
5.2 KiB
Diff
178 lines
5.2 KiB
Diff
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
|
|
|