找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

4525

积分

0

好友

589

主题
发表于 昨天 01:23 | 查看: 12| 回复: 0

摘要

OTP(One-Time Programmable,一次性可编程存储器)是图像传感器内部用于保存芯片个体信息和出厂标定结果的非易失性存储区域。与普通寄存器不同,OTP 在断电后仍能保存内容;与外部 EEPROM 不同,OTP 位于 Sensor 芯片内部,并由 Sensor 自身的 OTP 控制器完成加载、校验和内部数据分发。

OS04H10 数据手册明确给出:芯片支持 2 Kbit、最大 256 字节 OTP,通过 SCCB 控制;地址 0x00~0x17 用于 KSCG、TPM 和 CP 相关信息,地址 0x18~0xFF 标注为 OTP DPC 信息。芯片同时提供独立的 OTP 控制寄存器页、OTP DPC 处理寄存器页,以及长帧和短帧的 OTP DPC 使能位。

本文从 Sensor OTP 的基础原理开始,结合 OS04H10 的存储布局和寄存器组织,说明 OTP 在芯片内部如何被加载、如何参与坏点校正、如何在 Linux V4L2 Sensor 驱动中安全接入,并给出可落地的代码框架、调试方法和生产烧录边界。

资料边界
OS04H10 数据手册给出了 OTP 容量、逻辑用途和控制寄存器定义,但没有完整公开原始 OTP 数据读出端口、逐字节读取协议、DPC 数据编码格式、编程电压及时序。因此,本文能够给出运行期状态检查、OTP DPC 启用、驱动架构和诊断实现;原始数据导出与工厂烧录必须以 OmniVision OTP Application Note、模组厂 OTP Map 和量产脚本为准,不能根据寄存器名称猜测。

1. Sensor OTP 到底是什么

1.1 一次性可编程的含义

OTP 是一种非易失性存储。写入动作会永久改变存储单元状态,正常情况下不能像 EEPROM 或 Flash 那样擦除后重写。

在图像传感器中,OTP 的典型生命周期如下:

晶圆测试 / 芯片测试
        │
        ├─ 测量芯片个体参数
        ├─ 扫描固定坏点
        ├─ 生成校准字节和坏点表
        ▼
受控的 OTP 编程工位
        │
        ├─ 按厂商时序写入
        ├─ 回读
        ├─ BIST / ECC / 一致性检查
        ▼
量产设备运行
        │
        ├─ 上电加载 OTP
        ├─ 校验状态
        └─ 将 OTP 数据送入内部校正模块

OTP 的核心目的不是保存每帧图像,也不是保存曝光、增益等实时参数,而是保存与这颗芯片个体相关、在生产阶段确定且运行期间基本不变化的数据。

1.2 OTP、普通寄存器、SRAM、EEPROM 与 SoC eFuse 的区别

存储对象 所在位置 断电保持 可重复写入 典型用途
Sensor 普通寄存器 Sensor 内部控制寄存器 曝光、增益、模式、MIPI、Stream On/Off
Sensor 内部 SRAM/Shadow Buffer Sensor 内部 OTP 加载后的暂存、内部查表
Sensor OTP Sensor 芯片内部 通常否 芯片信息、出厂参数、固定坏点信息
模组 EEPROM 摄像头模组 PCB 是,次数受限 模组序列号、AWB、LSC、AF、PDAF、双目标定
SoC eFuse/OTP 主控 SoC 内部 通常否 芯片唯一标识、安全密钥、SoC 校准数据

工程中常把模组 EEPROM 中的校准数据也口头称为“OTP 数据”,但在硬件实现上,两者不是同一块存储。

1.3 Sensor OTP 不等于完整的模组校准数据库

摄像头模组可能包含:

Lens + IR-CUT + Sensor + VCM + EEPROM

Sensor 内部 OTP 更接近芯片级信息;外部 EEPROM 更适合保存容量较大的模组级数据,例如:

  • AWB 单体白平衡数据;
  • LSC 镜头阴影网格;
  • AF 马达近焦和远焦位置;
  • PDAF 相位校准数据;
  • 双目相机内外参;
  • 模组序列号和生产追溯信息。

OS04H10 当前数据手册的 OTP Memory Usage 表没有定义 AWB、LSC 或 AF 字段。因此,不能仅凭这份手册把 OS04H10 内部 OTP 当作通用 AWB/LSC 存储区。模组确实需要这些数据时,应继续确认模组原理图中是否存在独立 EEPROM,并取得模组厂定义的 EEPROM Map。

2. OS04H10 OTP 的手册结论

2.1 容量与控制接口

OS04H10 的特性列表给出 2 Kbits OTP memory。第 4.7 节进一步说明最大容量为 256 bytes,用于保存芯片识别和制造信息,并可通过 SCCB 控制。

换算关系如下:

2 Kbit = 2048 bit = 256 byte
逻辑地址范围:0x00 ~ 0xFF

SCCB 在 Linux 平台上通常由 I2C 控制器承载。OS04H10 由 SID 引脚选择地址:

SID 电平 手册 8-bit 写地址 手册 8-bit 读地址 Linux 7-bit 地址
0x78 0x79 0x3C
0x7A 0x7B 0x3D

Linux 设备树中的 reg 应填写 0x3c0x3d,不能直接填写手册中的 8-bit 地址。SCCB 时钟上限为 400 kHz。

2.2 256 字节逻辑用途

根据 OS04H10 数据手册表 4-5,可整理为:

OTP 逻辑地址 手册字段 工程解释
0x00 KSCG 与 KSCG 相关的芯片标定字节;计算含义未在手册中展开
0x01 tpm_otp_reg1 TPM 相关字节 1
0x02 tpm_otp_reg2 TPM 相关字节 2
0x03 tpm_otp_reg3 TPM 相关字节 3
0x04 tpm_otp_reg4 TPM 相关字节 4
0x05~0x17 for CP CP 相关制造数据;具体编码未公开
0x18~0xFF OTP DPC info 固定坏点校正相关信息

这张表透露出两个关键事实:

  1. OS04H10 内部 OTP 的主要运行期用途是 OTP DPC
  2. 手册只给出用途边界,没有给出 0x18~0xFF 的坏点条目编码方式。

2.3 一个必须注意的手册不一致点

表 4-5 把 0x18~0xFF 标记为 OTP DPC 信息。寄存器表又给出:

  • P9:0xC0[7:6]:DPC 起始地址高 2 位;
  • P9:0xC1[5:0]:DPC 起始地址低 6 位,默认寄存器值为 0x18
  • P9:0xC2[7:6]:DPC 结束地址高 2 位,默认寄存器值为 0x00
  • P9:0xC3[5:0]:DPC 结束地址低 6 位,默认寄存器值为 0xFF

若严格按有效位拼接,P9:0xC2=0x00P9:0xC3[5:0]=0x3F 会得到 0x3F,与表 4-5 中的 0xFF 不一致。这可能是 Preliminary Specification 的默认值勘误、有效位说明不完整,或内部地址编码并非简单拼接。

因此:

DPC 起止地址应以经过验证的官方初始化表、OTP Application Note 和实际寄存器回读为准,不能只根据默认值推导。

3. OS04H10 OTP 在芯片内部的位置

OS04H10 的 OTP 不是直接挂在图像数据输出路径上的一片“可随意读取的内存”。从寄存器组织看,它至少包含三层:

┌───────────────────────────────────────────────────────────┐
│                    OS04H10 Sensor                         │
│                                                           │
│  ┌──────────────────┐                                     │
│  │ 256-byte OTP     │  芯片级非易失数据                   │
│  │ 0x00 ~ 0xFF      │                                     │
│  └────────┬─────────┘                                     │
│           │                                               │
│           ▼                                               │
│  ┌──────────────────┐  P9:0x80 ~ P9:0xC3                 │
│  │ OTP Controller   │  Load / Read / Program / BIST       │
│  │ + SRAM/Window    │  Address Range / ECC / DMA Control  │
│  └───────┬──────────┘                                     │
│          │                                                │
│          ├───────────────► KSCG 路径(P5:0xDB[2])         │
│          │                                                │
│          ▼                                                │
│  ┌──────────────────┐  P6:0x04 ~ P6:0x2B                 │
│  │ OTP DPC Engine   │  Mirror / Flip / Binning / Offset   │
│  └────────┬─────────┘                                     │
│           │                                               │
│           ▼                                               │
│  Pixel Array → ADC → Sensor ISP/DPC → MIPI CSI-2          │
│                            ▲                              │
│                            │ P3:0x9A 长/短帧使能           │
└───────────────────────────────────────────────────────────┘

这里最容易混淆的是两种地址:

  • OTP 逻辑地址 0x00~0xFF:表示 OTP 存储内容的位置;
  • SCCB 寄存器地址 P9:0x80~P9:0xC3:表示 OTP 控制器的控制寄存器。

不能把 OTP 逻辑地址 0x18 误认为 P9:0x18,也不能把 P9:0x80~P9:0xC3 当作 256 字节 OTP 的直接数据窗口。

4. 页式寄存器访问模型

4.1 Page Select

OS04H10 使用 0xFD 选择寄存器页:

0xFD[3:0] 寄存器页 与 OTP 的关系
0x0 System Control OTP 时钟、复位缓冲等辅助控制
0x1 Sensor Control Page 1 KSCG 寄存器等
0x3 ISP OTP DPC 长帧/短帧使能和测试图
0x5 BLC kscg_otp_en
0x6 OTP DPC 固定坏点校正几何与约束参数
0x9 OTP OTP 加载、读取、编程、BIST、地址控制

页寄存器是全局状态。并发访问时,曝光控制线程、Stream On 流程和 OTP 流程可能互相改写 0xFD。因此驱动必须在同一把互斥锁内完成:

选择 Page → 访问寄存器 → 必要时恢复/更新 Page Cache

4.2 P9:OTP Controller

P9 是 OTP 控制器寄存器页,主要寄存器可分为五组。

4.2.1 加载和状态

寄存器 含义
P9:0x81 bit7 otp_load_o,OTP 加载进行中
P9:0x81 bit5 otp_bist_err
P9:0x81 bit4 otp_bist_done
P9:0x81 bit2 bist_result_clr
P9:0x81 bit1 autoload_m
P9:0x81 bit0 otp_rd
P9:0x85 bit4 otp_bist_enable
P9:0x85 bit3 otp_pwup_load
P9:0x85 bit2 otp_wkup_load

otp_load_o 是运行期最有价值的状态位。加载动作之后应轮询该位,等待其清零后再启用依赖 OTP 的功能。

4.2.2 读写模式和 SRAM 切换

寄存器 含义
P9:0x80 bit7 otp_pgenb,只读状态
P9:0x80 bit0 otp_pgm,编程控制
P9:0x84 bit7 Disable program
P9:0x84 bit6 Manual/Auto 模式选择
P9:0x84 bit0 bank_sram_switch
P9:0x8C bit0 otp_disable
P9:0x8D - otp_rst

这些寄存器中包含不可逆编程相关控制。产品驱动应保持编程路径关闭,不应通过普通 sysfs、debugfs 或 ioctl 暴露写 OTP 功能。

4.2.3 加载地址范围

寄存器 含义
P9:0x88 adr_stt_pt[15:8]
P9:0x89 adr_stt_pt[7:0]
P9:0x8A adr_end_pt[15:8]
P9:0x8B adr_end_pt[7:0]
P9:0x90~0x91 otp_adr_base

手册默认给出的范围是 0x0000~0x00FF,与 256 字节容量一致。

4.2.4 数据路径与诊断

P9:0x9D 包含:

  • 固定内部地址;
  • Byte Read Enable;
  • 空窗口和无效窗口控制;
  • Memory Offset;
  • DPC 到 ISP 的数据路径使能。

P9:0xA8 包含 ECC Mode、DMA Mode 等控制。手册只描述位名,没有给出完整操作序列和数据返回端口,因此这些位不能单独拼成可靠的原始 OTP Dump 流程。

4.2.5 DPC 区间

P9:0xC0~0xC3 描述 DPC 数据的起止地址。它们属于“OTP 控制器如何解释 OTP 数据”的配置,而不是坏点坐标本身。

4.3 P6:OTP DPC Engine

P6 是固定坏点校正引擎的配置页。关键能力包括:

  • Mirror 和 Flip 适配;
  • Binning 适配;
  • X/Y Offset;
  • 奇偶行、奇偶列步进;
  • 曝光和增益约束;
  • 坏点替换策略;
  • 结束地址和阈值控制。

典型寄存器如下:

寄存器 关键字段 作用
P6:0x04 rgbc_bin_mir_swmirror_optdisable_bin Mirror/Binning 处理策略
P6:0x05 fixed_replacefixed_ptnflip_opt 固定坏点替换和 Flip 适配
P6:0x06~0x08 Exposure/Gain Constraint 长帧约束
P6:0x09 vsync_rst_en、Threshold 帧同步与阈值
P6:0x10~0x17 只读 Offset/Increment 当前几何映射状态
P6:0x20~0x23 Manual X/Y Offset 手动偏移
P6:0x25 end_addr_bin Binning 数据结束位置
P6:0x26 X/Y Binning Manual Binning 手动控制
P6:0x28~0x2B Short Exposure/Gain/Threshold 短帧约束

这些寄存器说明 OTP DPC 并不是“读取坐标后简单替换像素”这么单一。Sensor 还需要根据裁剪、镜像、翻转、Binning、长短曝光路径,对固定坏点位置进行坐标映射和约束处理。

4.4 P3:真正的 DPC 使能入口

P3:0x9A 是 OTP DPC 的直接使能寄存器:

P3:0x9A bit0 = otpdpc_en_l   长帧 / Linear 路径
P3:0x9A bit1 = otpdpc_en_s   短帧路径

模式对应关系可以按下表理解:

工作模式 bit0 长帧 bit1 短帧
Linear 1 0
HDR Long + Short 1 1
仅验证长帧 1 0
完全关闭 OTP DPC 0 0

最终值仍应以对应 Mode Register Table 为准,特别是 DCG、DCG Combine 和 HDR Dummy 模式。

P3:0xE2[1] 可以选择 OTP DPC Bad Pixel Pattern,用于验证固定坏点链路;它是测试功能,不应在正常出图时长期打开。

4.5 KSCG 的 OTP 入口

P5:0xDB[2] 定义为 kscg_otp_en。这说明 OTP 中的 0x00 KSCG 字节可被 Sensor 内部 BLC/SCG 相关路径消费。

但数据手册没有给出:

  • KSCG 字节的数值格式;
  • KSCG 与 P1:0xE8P1:0xE9 的精确换算关系;
  • 开启前后的完整寄存器依赖。

因此 KSCG 应沿用经过验证的官方模式表,不能单独打开一个使能位后假定校准立即正确。

5. OTP DPC 的原理

5.1 坏点从哪里来

CMOS Sensor 的像素阵列由数百万个光电转换单元组成。少量像素可能因为制造差异产生:

  • 暗场持续偏亮的 Hot Pixel;
  • 亮场响应过低的 Dead Pixel;
  • 对光照变化响应异常的 Stuck Pixel;
  • 相邻多个像素组成的 Cluster Defect。

其中一部分缺陷位置固定,适合在生产测试中扫描后写入 OTP。

5.2 OTP DPC 与 Dynamic DPC 的区别

对比项 OTP DPC Dynamic DPC
缺陷来源 出厂扫描得到的固定坏点 每帧根据局部像素关系动态判断
数据依据 OTP 中的坏点信息 图像内容、阈值、增益等
优点 坐标明确,误判少 可处理温度、增益变化后的新异常点
风险 坐标映射错误会修错位置 阈值过强会损伤纹理
OS04H10 寄存器页 P6 + P3:0x9A P4 长帧/短帧寄存器

产品中可以同时使用两者:OTP DPC 处理已知固定坏点,Dynamic DPC 处理剩余异常点。两套算法都过强时,细小纹理容易被误平滑,因此需要联合验证。

5.3 固定坏点修复的基本思想

假设坏点坐标为 (x, y),校正引擎会从同色 Bayer 邻域选取可信像素估算替换值:

B G B G B
G R G R G
B G X G B    X 为坏点
G R G R G
B G B G B

RAW Bayer 图像中不能随意使用紧邻像素平均,因为紧邻像素可能属于不同颜色通道。固定 DPC 通常需要考虑:

  • Bayer Pattern;
  • 水平和垂直同色邻居;
  • 边缘方向;
  • 白点/黑点类型;
  • 饱和像素;
  • 多点簇;
  • 镜像、翻转与裁剪后的坐标变化。

OS04H10 P6 中的 Pixel Geometry、Mirror、Flip、Binning、Offset 和 Increment 控制正是为这些场景服务。

6. 运行期正确使用流程

6.1 推荐时序

DOVDD / AVDD / DVDD 上电
          │
          ├─ EXTCLK 稳定
          ├─ XSHUTDN 拉高
          └─ 等待至少 8 ms 后开始 SCCB
          ▼
读取 Chip ID:53 04 48 10
          ▼
写入当前分辨率 / 帧率 / HDR 模式寄存器表
          ▼
确认 OTP 自动加载策略或执行已验证的 Load Sequence
          ▼
轮询 P9:0x81[7] otp_load_o,等待加载结束
          ▼
检查 P9:0x81[5:4] BIST 状态
          ▼
确认 DPC 地址范围、P6 几何参数和模式匹配
          ▼
设置 P3:0x9A 长帧 / 短帧 OTP DPC 使能
          ▼
按官方模式表决定是否开启 P5:0xDB[2] KSCG OTP
          ▼
Stream On

6.2 为什么要在 Stream On 前完成

OTP DPC 位于 Sensor 内部图像处理路径中。Stream On 后再修改以下内容,可能导致:

  • 前后帧坏点校正状态不一致;
  • HDR 长帧与短帧状态不同步;
  • Mirror/Flip 或 Binning 切换时坐标短暂错位;
  • 第一帧出现固定亮点或异常替换点。

因此,OTP 准备动作应与 Mode Table、Mirror/Flip、Crop/Binning 设置一起完成,并在 MIPI 输出前固定下来。

6.3 硬件掉电与软件待机的区别

OS04H10 手册说明:

  • 硬件 Standby/Power Down 会使寄存器恢复默认,并且不能进行 SCCB 通信;
  • 软件 Standby 保留寄存器内容,SCCB 仍可通信。

驱动中的 OTP 状态缓存必须与电源状态绑定:

软件 Standby:可保留 otp_loaded / otp_valid 状态
硬件掉电:必须清除状态,下次上电重新确认加载
Soft Reset:寄存器恢复默认,也应重新执行 OTP 准备流程

不能把“OTP 本体断电不丢失”误解成“OTP 控制器和 SRAM 状态断电后仍然有效”。非易失的是 OTP 阵列内容,不是运行期寄存器和加载缓冲。

7. Linux Sensor 驱动的实现原则

7.1 驱动职责分层

推荐把实现拆成三层:

┌──────────────────────────────────────────────────────┐
│ V4L2 Sensor Driver                                   │
│                                                      │
│  Mode / Exposure / Gain / Stream                     │
│       │                                              │
│       ├─ os04h10_otp_prepare()                       │
│       │     ├─ Load 状态等待                         │
│       │     ├─ BIST 检查                             │
│       │     ├─ DPC 模式配置                          │
│       │     └─ KSCG 策略                             │
│       │                                              │
│       ├─ Page-aware SCCB access                      │
│       │                                              │
│       └─ Optional vendor OTP operations              │
│             ├─ trigger_load()                        │
│             ├─ read_byte()                           │
│             └─ factory_program():不进入产品固件     │
└──────────────────────────────────────────────────────┘

其中:

  • 通用驱动负责上电、等待、状态检查和 DPC 使能;
  • 缺失的原始读取细节通过厂商验证后的回调补充;
  • 工厂编程逻辑与运行固件物理隔离。

7.2 数据结构

struct os04h10_otp_state {
    bool checked;
    bool load_done;
    bool bist_done;
    bool bist_error;
    bool dpc_long_enabled;
    bool dpc_short_enabled;

    u8 status_81;
    u8 dpc_start_hi_raw;
    u8 dpc_start_lo_raw;
    u8 dpc_end_hi_raw;
    u8 dpc_end_lo_raw;
};

struct os04h10_otp_ops {
    /* 由经过验证的厂商序列实现;NULL 表示依赖自动加载。 */
    int (*trigger_load)(struct os04h10 *sensor);

    /* 手册未给出数据返回端口,获得正式资料后再实现。 */
    int (*read_byte)(struct os04h10 *sensor, u16 addr, u8 *value);
};

struct os04h10 {
    struct i2c_client *client;
    struct v4l2_subdev subdev;
    struct mutex mutex;

    u8 current_page;
    bool page_valid;
    bool streaming;

    struct os04h10_otp_state otp;
    const struct os04h10_otp_ops *otp_ops;
};

otp.checked 只能表示当前硬件电源周期已经检查,不能跨真正掉电永久保留。

8. Page-aware SCCB 基础代码

8.1 单字节读写

OS04H10 使用 8-bit 寄存器地址和 8-bit 数据。下面代码假设 client->addr 已经是 Linux 7-bit 地址 0x3c0x3d

static int os04h10_write_reg8(struct os04h10 *sensor,
                              u8 reg, u8 value)
{
    struct i2c_client *client = sensor->client;
    u8 buf[2] = { reg, value };
    int ret;

    ret = i2c_master_send(client, buf, sizeof(buf));
    if (ret < 0)
        return ret;
    if (ret != sizeof(buf))
        return -EIO;

    return 0;
}

static int os04h10_read_reg8(struct os04h10 *sensor,
                             u8 reg, u8 *value)
{
    struct i2c_client *client = sensor->client;
    struct i2c_msg msgs[2] = {
        {
            .addr = client->addr,
            .flags = 0,
            .len = 1,
            .buf = ®,
        },
        {
            .addr = client->addr,
            .flags = I2C_M_RD,
            .len = 1,
            .buf = value,
        },
    };
    int ret;

    ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
    if (ret < 0)
        return ret;
    if (ret != ARRAY_SIZE(msgs))
        return -EIO;

    return 0;
}

8.2 页切换

#define OS04H10_REG_PAGE_SEL        0xFD

#define OS04H10_PAGE_SYSTEM         0x00
#define OS04H10_PAGE_SENSOR1        0x01
#define OS04H10_PAGE_ISP            0x03
#define OS04H10_PAGE_BLC            0x05
#define OS04H10_PAGE_OTP_DPC        0x06
#define OS04H10_PAGE_OTP            0x09

static int os04h10_select_page_locked(struct os04h10 *sensor, u8 page)
{
    int ret;

    page &= 0x0f;
    if (sensor->page_valid && sensor->current_page == page)
        return 0;

    ret = os04h10_write_reg8(sensor, OS04H10_REG_PAGE_SEL, page);
    if (ret)
        return ret;

    sensor->current_page = page;
    sensor->page_valid = true;
    return 0;
}

static void os04h10_invalidate_page_cache(struct os04h10 *sensor)
{
    sensor->page_valid = false;
}

硬件复位、软件复位、真正掉电、I2C 恢复失败后,都应调用 os04h10_invalidate_page_cache()

8.3 带页访问和位更新

static int os04h10_read_page_reg_locked(struct os04h10 *sensor,
                                        u8 page, u8 reg, u8 *value)
{
    int ret;

    ret = os04h10_select_page_locked(sensor, page);
    if (ret)
        return ret;

    return os04h10_read_reg8(sensor, reg, value);
}

static int os04h10_write_page_reg_locked(struct os04h10 *sensor,
                                         u8 page, u8 reg, u8 value)
{
    int ret;

    ret = os04h10_select_page_locked(sensor, page);
    if (ret)
        return ret;

    return os04h10_write_reg8(sensor, reg, value);
}

static int os04h10_update_bits_locked(struct os04h10 *sensor,
                                      u8 page, u8 reg,
                                      u8 mask, u8 value)
{
    u8 old_value;
    u8 new_value;
    int ret;

    ret = os04h10_read_page_reg_locked(sensor, page, reg, &old_value);
    if (ret)
        return ret;

    new_value = (old_value & ~mask) | (value & mask);
    if (new_value == old_value)
        return 0;

    return os04h10_write_page_reg_locked(sensor, page, reg, new_value);
}

所有函数名带 _locked,表示调用前必须持有 sensor->mutex。这样能够防止 V4L2 Control 回调和 Stream 流程在页切换期间互相打断。

9. Chip ID 与电源周期管理

9.1 Chip ID 检查

手册给出的 Chip ID 字节为 53 04 48 10

static int os04h10_check_chip_id_locked(struct os04h10 *sensor)
{
    static const u8 expected[4] = { 0x53, 0x04, 0x48, 0x10 };
    u8 id[4];
    unsigned int i;
    int ret;

    ret = os04h10_select_page_locked(sensor, OS04H10_PAGE_SYSTEM);
    if (ret)
        return ret;

    for (i = 0; i < ARRAY_SIZE(id); i++) {
        ret = os04h10_read_reg8(sensor, 0x02 + i, &id[i]);
        if (ret)
            return ret;
    }

    if (memcmp(id, expected, sizeof(id))) {
        dev_err(&sensor->client->dev,
                "unexpected chip id %02x %02x %02x %02x\n",
                id[0], id[1], id[2], id[3]);
        return -ENODEV;
    }

    return 0;
}

9.2 Power On 后清除 OTP 软件状态

static void os04h10_otp_reset_state(struct os04h10 *sensor)
{
    memset(&sensor->otp, 0, sizeof(sensor->otp));
    os04h10_invalidate_page_cache(sensor);
}

建议调用位置:

  • Runtime PM 的真实上电成功之后;
  • XSHUTDN 从低拉高并完成延时之后;
  • Soft Reset 之后;
  • I2C 总线恢复导致寄存器状态不可确认时。

10. OTP 加载状态轮询

10.1 状态位定义

#define OS04H10_OTP_REG_STATUS       0x81
#define OS04H10_OTP_LOAD_ONGOING     BIT(7)
#define OS04H10_OTP_BIST_ERROR       BIT(5)
#define OS04H10_OTP_BIST_DONE        BIT(4)
#define OS04H10_OTP_READ_REQ         BIT(0)

10.2 等待 Load 完成

static int os04h10_otp_wait_load_locked(struct os04h10 *sensor)
{
    unsigned int retry;
    u8 status;
    int ret;

    for (retry = 0; retry < 100; retry++) {
        ret = os04h10_read_page_reg_locked(sensor,
                                           OS04H10_PAGE_OTP,
                                           OS04H10_OTP_REG_STATUS,
                                           &status);
        if (ret)
            return ret;

        if (!(status & OS04H10_OTP_LOAD_ONGOING)) {
            sensor->otp.status_81 = status;
            sensor->otp.load_done = true;
            return 0;
        }

        usleep_range(1000, 1500);
    }

    dev_err(&sensor->client->dev, "OTP load timeout\n");
    return -ETIMEDOUT;
}

100 次、每次约 1 ms 是驱动保护上限,不代表手册规定的标准加载时间。正式量产值应根据厂商时序和实测裕量调整。

10.3 BIST 状态检查

static int os04h10_otp_check_bist_locked(struct os04h10 *sensor)
{
    u8 status;
    int ret;

    ret = os04h10_read_page_reg_locked(sensor,
                                       OS04H10_PAGE_OTP,
                                       OS04H10_OTP_REG_STATUS,
                                       &status);
    if (ret)
        return ret;

    sensor->otp.status_81 = status;
    sensor->otp.bist_done = !!(status & OS04H10_OTP_BIST_DONE);
    sensor->otp.bist_error = !!(status & OS04H10_OTP_BIST_ERROR);

    if (sensor->otp.bist_done && sensor->otp.bist_error) {
        dev_err(&sensor->client->dev,
                "OTP BIST failed, status=0x%02x\n", status);
        return -EILSEQ;
    }

    if (!sensor->otp.bist_done)
        dev_dbg(&sensor->client->dev,
                "OTP BIST has not reported done, status=0x%02x\n",
                status);

    return 0;
}

是否要求 bist_done=1 才能出图,需要结合官方初始化表确认。某些流程只在显式启动 BIST 后置位 Done,不能在没有启动 BIST 的情况下把 Done=0 直接判为硬件故障。

11. 自动加载与手动加载的实现边界

P9 中出现 autoload_motp_pwup_loadotp_wkup_loadotp_rd,说明芯片支持自动或手动加载机制。但数据手册没有给出以下必要信息:

  • 各控制位的脉冲极性;
  • 写入顺序;
  • 是否需要先切换 SRAM Bank;
  • 地址窗口如何配合;
  • Load 前后是否需要 OTP Reset;
  • 是否要求特定内部时钟;
  • 哪个寄存器是原始数据输出端口。

因此通用驱动应把 Load 触发封装成可选回调:

static int os04h10_otp_trigger_load_locked(struct os04h10 *sensor)
{
    if (!sensor->otp_ops || !sensor->otp_ops->trigger_load) {
        /* 当前模式表依赖上电自动加载。 */
        return 0;
    }

    return sensor->otp_ops->trigger_load(sensor);
}

只有拿到并验证官方序列后,才在 trigger_load() 中写入 P9 控制寄存器。不要仅看到 P9:0x81[0] = otp_rd 就直接写 1,因为缺失的前置和恢复步骤可能破坏 OTP SRAM 状态或影响后续图像链路。

12. DPC 地址寄存器诊断

由于手册默认值存在不一致,驱动应首先保留原始值,打印诊断信息,而不是静默假定一个解析结果。

static int os04h10_otp_read_dpc_range_locked(struct os04h10 *sensor)
{
    int ret;

    ret = os04h10_read_page_reg_locked(sensor, OS04H10_PAGE_OTP,
                                       0xC0, &sensor->otp.dpc_start_hi_raw);
    if (ret)
        return ret;

    ret = os04h10_read_page_reg_locked(sensor, OS04H10_PAGE_OTP,
                                       0xC1, &sensor->otp.dpc_start_lo_raw);
    if (ret)
        return ret;

    ret = os04h10_read_page_reg_locked(sensor, OS04H10_PAGE_OTP,
                                       0xC2, &sensor->otp.dpc_end_hi_raw);
    if (ret)
        return ret;

    ret = os04h10_read_page_reg_locked(sensor, OS04H10_PAGE_OTP,
                                       0xC3, &sensor->otp.dpc_end_lo_raw);
    if (ret)
        return ret;

    dev_info(&sensor->client->dev,
             "OTP DPC range raw: C0=%02x C1=%02x C2=%02x C3=%02x\n",
             sensor->otp.dpc_start_hi_raw,
             sensor->otp.dpc_start_lo_raw,
             sensor->otp.dpc_end_hi_raw,
             sensor->otp.dpc_end_lo_raw);

    return 0;
}

在实验室阶段,可以同时打印候选拼接值:

static u8 os04h10_otp_addr_candidate(u8 high_reg, u8 low_reg)
{
    return ((high_reg >> 6) << 6) | (low_reg & 0x3f);
}

但候选值只能用于对照,不应在没有官方确认时作为写寄存器或数组长度的唯一依据。

13. 开启长帧和短帧 OTP DPC

13.1 位定义

#define OS04H10_ISP_REG_OTP_DPC      0x9A
#define OS04H10_OTP_DPC_LONG_EN      BIT(0)
#define OS04H10_OTP_DPC_SHORT_EN     BIT(1)

13.2 模式相关配置

enum os04h10_image_mode {
    OS04H10_MODE_LINEAR,
    OS04H10_MODE_HDR_2X,
    OS04H10_MODE_DCG,
    OS04H10_MODE_DCG_COMBINE,
};

static int os04h10_otp_dpc_enable_locked(struct os04h10 *sensor,
                                         enum os04h10_image_mode mode)
{
    u8 enable = OS04H10_OTP_DPC_LONG_EN;
    int ret;

    if (mode == OS04H10_MODE_HDR_2X)
        enable |= OS04H10_OTP_DPC_SHORT_EN;

    /* DCG/DCG Combine 的最终位组合以对应官方模式表为准。 */
    ret = os04h10_update_bits_locked(sensor,
                                     OS04H10_PAGE_ISP,
                                     OS04H10_ISP_REG_OTP_DPC,
                                     OS04H10_OTP_DPC_LONG_EN |
                                     OS04H10_OTP_DPC_SHORT_EN,
                                     enable);
    if (ret)
        return ret;

    sensor->otp.dpc_long_enabled =
        !!(enable & OS04H10_OTP_DPC_LONG_EN);
    sensor->otp.dpc_short_enabled =
        !!(enable & OS04H10_OTP_DPC_SHORT_EN);

    return 0;
}

13.3 关闭接口

static int os04h10_otp_dpc_disable_locked(struct os04h10 *sensor)
{
    int ret;

    ret = os04h10_update_bits_locked(sensor,
                                     OS04H10_PAGE_ISP,
                                     OS04H10_ISP_REG_OTP_DPC,
                                     OS04H10_OTP_DPC_LONG_EN |
                                     OS04H10_OTP_DPC_SHORT_EN,
                                     0);
    if (!ret) {
        sensor->otp.dpc_long_enabled = false;
        sensor->otp.dpc_short_enabled = false;
    }

    return ret;
}

关闭接口适合 A/B 图像对比和故障隔离,不建议作为正常出图的频繁动态控制。

14. KSCG OTP 使能框架

#define OS04H10_BLC_REG_DB           0xDB
#define OS04H10_KSCG_OTP_EN          BIT(2)

static int os04h10_kscg_otp_enable_locked(struct os04h10 *sensor,
                                          bool enable)
{
    return os04h10_update_bits_locked(sensor,
                                      OS04H10_PAGE_BLC,
                                      OS04H10_BLC_REG_DB,
                                      OS04H10_KSCG_OTP_EN,
                                      enable ? OS04H10_KSCG_OTP_EN : 0);
}

该函数只提供位操作能力。是否调用、何时调用、是否还需要配置 P1:0xE8 KSCGP1:0xE9 KSCG_S,必须由经过验证的模式表决定。

15. 统一的 OTP Prepare 流程

static int os04h10_otp_prepare_locked(struct os04h10 *sensor,
                                      enum os04h10_image_mode mode)
{
    int ret;

    if (sensor->otp.checked)
        return sensor->otp.bist_error ? -EILSEQ : 0;

    ret = os04h10_otp_trigger_load_locked(sensor);
    if (ret)
        return ret;

    ret = os04h10_otp_wait_load_locked(sensor);
    if (ret)
        return ret;

    ret = os04h10_otp_check_bist_locked(sensor);
    if (ret)
        return ret;

    ret = os04h10_otp_read_dpc_range_locked(sensor);
    if (ret)
        return ret;

    ret = os04h10_otp_dpc_enable_locked(sensor, mode);
    if (ret)
        return ret;

    /* KSCG 是否开启由具体模式表或平台配置决定。 */

    sensor->otp.checked = true;
    return 0;
}

这个实现遵循一个重要原则:

加载触发可以因资料缺失而留给厂商回调,但加载完成检查、错误处理、DPC 使能和电源周期管理仍然可以在通用驱动中做得完整、可审计。

16. 与 s_stream() 的集成

static int os04h10_start_stream_locked(struct os04h10 *sensor)
{
    const struct os04h10_mode *mode = sensor->cur_mode;
    int ret;

    ret = os04h10_write_array(sensor, mode->reg_list);
    if (ret)
        return ret;

    ret = __v4l2_ctrl_handler_setup(&sensor->ctrl_handler);
    if (ret)
        return ret;

    ret = os04h10_otp_prepare_locked(sensor, mode->image_mode);
    if (ret) {
        dev_err(&sensor->client->dev,
                "OTP preparation failed: %d\n", ret);
        return ret;
    }

    /* 按已验证的模式表执行 MIPI/Stream On。 */
    ret = os04h10_write_page_reg_locked(sensor,
                                        OS04H10_PAGE_SYSTEM,
                                        OS04H10_REG_STREAM,
                                        OS04H10_STREAM_ON);
    if (ret)
        return ret;

    sensor->streaming = true;
    return 0;
}

实际驱动中,OS04H10_REG_STREAM 和 Stream On 值应使用现有驱动已验证的定义,不应从本文示例中另行推断。

Runtime PM 典型顺序如下:

pm_runtime_resume_and_get()
        │
        ▼
mutex_lock()
        │
        ├─ Mode Table
        ├─ V4L2 Controls
        ├─ OTP Prepare
        └─ Stream On
        ▼
mutex_unlock()

17. 原始 OTP Dump 为什么不能直接照手册完成

17.1 手册已经给出的信息

  • OTP 总容量和逻辑地址范围;
  • Load/Read/Program/BIST 控制位;
  • 地址起止控制;
  • Byte Read Enable、ECC、DMA 等功能位;
  • DPC 区间和内部消费路径。

17.2 手册没有给出的关键信息

  • OTP 数据输出寄存器或 FIFO 端口;
  • 一次读请求之后数据从哪里取出;
  • Byte Read 模式的地址递增规则;
  • bank_sram_switch 的准确切换时序;
  • otp_rd 的置位、等待、清零顺序;
  • ECC 状态与数据有效性的对应关系;
  • DPC 条目的字节编码;
  • 编程时序与电压条件。

因此,下面这种写法是错误的:

/* 错误示例:把 OTP 逻辑地址当成 P9 寄存器地址。 */
for (addr = 0; addr < 256; addr++)
    os04h10_read_page_reg_locked(sensor, 0x09, addr, &buf[addr]);

P9 只在 0x80~0xC3 定义了控制寄存器,逻辑 OTP 地址 0x00~0xFF 不等于 P9 寄存器地址。

17.3 正确的扩展接口

获得正式 OTP Read Sequence 后,可实现:

static int os04h10_otp_dump_locked(struct os04h10 *sensor,
                                   u8 *buf, size_t size)
{
    size_t i;
    int ret;

    if (!buf || size < 256)
        return -EINVAL;

    if (!sensor->otp_ops || !sensor->otp_ops->read_byte)
        return -EOPNOTSUPP;

    ret = os04h10_otp_prepare_locked(sensor,
                                     OS04H10_MODE_LINEAR);
    if (ret)
        return ret;

    for (i = 0; i < 256; i++) {
        ret = sensor->otp_ops->read_byte(sensor, i, &buf[i]);
        if (ret)
            return ret;
    }

    return 0;
}

read_byte() 只接受官方读序列的实现,不能根据相近型号的 OmniVision Sensor 驱动直接移植。不同 Sensor 的 OTP Controller、数据窗口和时序可能完全不同。

18. 接入 Linux NVMEM 的条件与框架

只有在原始 OTP 读取已经通过回读、BIST 和多次上电一致性验证后,才适合注册只读 NVMEM Provider。

static int os04h10_nvmem_read(void *priv, unsigned int offset,
                              void *val, size_t bytes)
{
    struct os04h10 *sensor = priv;
    u8 raw[256];
    int ret;

    if (offset + bytes > sizeof(raw))
        return -EINVAL;

    ret = pm_runtime_resume_and_get(&sensor->client->dev);
    if (ret < 0)
        return ret;

    mutex_lock(&sensor->mutex);
    ret = os04h10_otp_dump_locked(sensor, raw, sizeof(raw));
    if (!ret)
        memcpy(val, raw + offset, bytes);
    mutex_unlock(&sensor->mutex);

    pm_runtime_put(&sensor->client->dev);
    return ret;
}

static int os04h10_register_nvmem(struct os04h10 *sensor)
{
    struct nvmem_config config = {
        .name = "os04h10-otp",
        .dev = &sensor->client->dev,
        .owner = THIS_MODULE,
        .priv = sensor,
        .size = 256,
        .word_size = 1,
        .stride = 1,
        .read_only = true,
        .root_only = true,
        .reg_read = os04h10_nvmem_read,
    };

    return PTR_ERR_OR_ZERO(devm_nvmem_register(config.dev, &config));
}

NVMEM 接口应保持只读。原始 OTP 内容可能包含生产追溯信息,权限应限制为 root,并避免在普通日志中完整打印。

19. 工厂 OTP 编程的正确边界

19.1 为什么不能在产品驱动中开放

OTP 编程具有不可逆性。错误写入可能造成:

  • 坏点表永久损坏;
  • 芯片级校准参数错误;
  • 生产追溯信息丢失;
  • 不同批次的写入格式混用;
  • OTP 控制器进入异常状态;
  • 芯片永久报废。

P9 中虽然能看到 otp_pgm、Program Strobe、Program Timing 等寄存器,但寄存器名称不等于完整编程规范。

19.2 量产编程必须具备的资料

至少需要:

  1. OS04H10 OTP Programming Guide;
  2. OTP Read/Load Application Note;
  3. DPC Entry Encoding;
  4. 编程电压、电源纹波和温度要求;
  5. Program Pulse 和 Busy 轮询规则;
  6. ECC/BIST 判定规则;
  7. 可用区、保留区和锁定位定义;
  8. 模组厂量产数据格式与追溯规范。

19.3 推荐的工厂流程

暗场 / 亮场 / 多增益采集
          │
          ▼
坏点检测与分类
          │
          ▼
坐标去重、边界检查、数量限制
          │
          ▼
按照官方格式编码 OTP Blob
          │
          ▼
确认 OTP 目标区域为空且可编程
          │
          ▼
受控电源和时钟下执行 Program Sequence
          │
          ▼
断电重启后重新 Load
          │
          ├─ 原始数据回读
          ├─ BIST/ECC
          ├─ DPC 图像验证
          └─ 生产记录绑定芯片序列

烧录完成后的验证必须经过一次真正断电重启。只在同一电源周期内读回 SRAM,不能证明 OTP 阵列已经永久写入。

20. Rockchip 摄像头链路中的位置

在 RV1126B、RK3588 等 Rockchip 平台上,典型数据链路为:

OS04H10 Pixel Array
        │
        ├─ Sensor 内部 BLC / OTP DPC / Dynamic DPC
        ▼
MIPI CSI-2 / D-PHY
        ▼
RKCIF / VICAP
        ▼
RKISP
        ▼
RKAIQ 3A 与 ISP 算法
        ▼
NV12 / YUV / 编码器

OTP DPC 在 MIPI 发送之前完成,因此 RKCIF 和 RKISP 接收到的 RAW 数据已经可能被 Sensor 修复过。

20.1 与 RKISP DPC 的关系

Sensor OTP DPC 和 RKISP DPC 可以分工:

Sensor OTP DPC:处理芯片出厂已知固定坏点
RKISP DPC:处理残余坏点、温度和增益相关异常点

调试时应分别验证:

  1. Sensor OTP DPC 关、RKISP DPC 关;
  2. Sensor OTP DPC 开、RKISP DPC 关;
  3. Sensor OTP DPC 开、RKISP DPC 开。

这样才能判断坏点减少来自哪一级,并观察是否出现纹理误修复。

20.2 AWB/LSC 不应从这张 OTP Map 中凭空解析

当前 OS04H10 手册没有为内部 OTP 定义 AWB、LSC、AF 字段。Rockchip IQ 文件中的 AWB、LSC 等参数通常来自:

  • Sensor/镜头组合的实验室标定;
  • 模组外部 EEPROM;
  • RKAIQ Tuner 生成的 IQ 数据。

不能把 0x18~0xFF 的 DPC 数据误当作 LSC 网格,也不能把 CP 区域自行解释为 AWB 数据。

21. Device Tree 与 I2C 地址

OS04H10 本体仍然是普通 I2C/SCCB 从设备。示例:

&i2c3 {
    status = "okay";
    clock-frequency = <400000>;

    os04h10: camera@3c {
        compatible = "ovti,os04h10";
        reg = <0x3c>;

        clocks = <&cru CLK_MIPI_CAMARAOUT_M1>;
        clock-names = "xvclk";

        reset-gpios = <&gpio3 RK_PC5 GPIO_ACTIVE_LOW>;

        /* 电源、pinctrl、module-index、endpoint 等按板级设计补充。 */
    };
};

OTP 位于 Sensor 内部,不需要在设备树中额外创建 I2C EEPROM 节点。只有模组上确实存在外部 EEPROM 时,才增加独立地址节点。

22. 命令行调试方法

22.1 先确认 I2C 地址

i2cdetect -y 3

SID 低时通常看到 0x3c,SID 高时通常看到 0x3d

22.2 读取 Chip ID

由于 OS04H10 使用页寄存器,建议用一次组合事务明确选择 P0:

# 选择 P0
 i2ctransfer -f -y 3 w2@0x3c 0xfd 0x00

# 读取 P0:0x02~0x05,每次执行寄存器地址写 + 1 byte 读
 i2ctransfer -f -y 3 w1@0x3c 0x02 r1
 i2ctransfer -f -y 3 w1@0x3c 0x03 r1
 i2ctransfer -f -y 3 w1@0x3c 0x04 r1
 i2ctransfer -f -y 3 w1@0x3c 0x05 r1

预期为:

0x53 0x04 0x48 0x10

22.3 读取 OTP 状态

# 选择 P9
 i2ctransfer -f -y 3 w2@0x3c 0xfd 0x09

# 读取 P9:0x81
 i2ctransfer -f -y 3 w1@0x3c 0x81 r1

位解释:

bit7 = 1:Load 仍在进行
bit5 = 1:BIST Error
bit4 = 1:BIST Done

调试结束后,驱动可能仍在运行。外部命令改变 0xFD 会破坏驱动的 Page Cache,因此应在停流、解绑驱动或确认无并发访问的情况下操作。调试完成后重新上电或重新加载驱动,避免遗留页状态。

22.4 读取 OTP DPC 使能

# 选择 P3
 i2ctransfer -f -y 3 w2@0x3c 0xfd 0x03

# 读取 P3:0x9A
 i2ctransfer -f -y 3 w1@0x3c 0x9a r1

低两位分别表示长帧和短帧 OTP DPC 使能。

23. 图像级验证方案

23.1 暗场 Hot Pixel 测试

条件建议:

  • 镜头完全遮光;
  • 固定曝光;
  • 多档模拟增益;
  • 关闭时域降噪和空间降噪;
  • 抓取 RAW,不先转成压缩视频;
  • Sensor OTP DPC 开/关各抓多帧。

统计指标:

  • 超过黑电平目标一定阈值的像素数量;
  • 固定坐标重复出现次数;
  • 单点、双点和 Cluster 数量;
  • 长帧与短帧分别统计。

23.2 亮场 Dead Pixel 测试

使用均匀积分球或均匀白板,固定照度和曝光,统计显著低于局部邻域的像素。

23.3 Mirror/Flip/Binning 验证

至少覆盖:

模式 需要验证的项目
Full Resolution Normal 基准坏点位置
Horizontal Mirror X 坐标变换是否正确
Vertical Flip Y 坐标变换是否正确
Mirror + Flip 双向坐标变换
2x2 Binning 坏点合并与 end_addr_bin
HDR 长帧、短帧分别校正

异常表现通常不是“完全不出图”,而是在变换后出现新的固定亮点、错误替换点或边缘破坏。

24. 常见故障与定位

现象 可能原因 建议检查
P9:0x81[7] 长时间为 1 Load 时钟未开、序列错误、OTP 控制器异常 P0 时钟门控、官方 Load Sequence、电源与 EXTCLK
BIST Error OTP 数据损坏、编程失败、BIST 序列错误 错误地址寄存器、断电重试、生产记录
Linear 正常,HDR 短帧仍有坏点 只开启 otpdpc_en_l 检查 P3:0x9A[1] 和 P6 短帧约束
Mirror 后坏点位置错位 P6 Mirror 适配不匹配 P6:0x04、当前 Bayer/Crop 配置
Flip 后出现错误修复点 Flip 坐标映射未同步 P6:0x05[2]、Mode Table
Binning 模式坏点增多 Binning 地址范围或步进不匹配 P6:0x25~0x26、Offset/Increment
开启 DPC 后纹理变软 Dynamic DPC 阈值过强或重复修复 分级关闭 Sensor/RKISP DPC 做 A/B 对比
OTP 状态第一次正常,二次上电异常 驱动错误复用上个电源周期状态 上电后清除 otp.checked 并重新检查
命令行读寄存器后驱动异常 外部操作改变了 Page Select 停流后操作,重启驱动恢复状态
尝试 Dump 得到全 0/全 FF 未找到真实数据端口或 Load 未完成 不要把 P9 当数据窗口,取得正式读序列
AWB/LSC 解析结果异常 把 DPC/CP 区误当模组校准区 检查外部 EEPROM 和模组 OTP Map

25. 安全性与健壮性建议

25.1 产品固件只保留只读和使能路径

建议保留:

  • OTP Load 状态读取;
  • BIST 状态读取;
  • DPC 起止寄存器诊断;
  • OTP DPC Enable/Disable;
  • 只读 NVMEM,可选;
  • 受权限控制的状态日志。

建议删除或编译隔离:

  • otp_pgm 写入;
  • Program Timing 修改;
  • 可写 sysfs;
  • 任意地址写 OTP 的 ioctl;
  • 量产烧录脚本中的密钥和格式细节。

25.2 错误策略

OTP 错误是否阻止出图取决于产品定义:

  • 安防画质要求高:BIST 失败可直接拒绝 Stream On,并记录硬件故障;
  • 调试固件:可关闭 OTP DPC 后继续出图,同时输出高等级告警;
  • 量产测试:任何 OTP 校验异常都应判为不合格。

无论采用哪种策略,都不应静默忽略错误。

25.3 日志示例

正常:

os04h10 3-003c: chip id 53 04 48 10
os04h10 3-003c: OTP load complete, status=0x10
os04h10 3-003c: OTP DPC range raw C0=00 C1=18 C2=00 C3=ff
os04h10 3-003c: OTP DPC enabled: long=1 short=0

异常:

os04h10 3-003c: OTP load timeout
os04h10 3-003c: OTP BIST failed, status=0x30
os04h10 3-003c: refuse stream-on because OTP policy=strict

26. 驱动评审清单

  • [ ] Device Tree 使用 Linux 7-bit 地址 0x3c/0x3d
  • [ ] SCCB 频率不超过 400 kHz。
  • [ ] 上电、EXTCLK、XSHUTDN 时序满足手册要求。
  • [ ] Sensor 稳定后至少等待 8 ms 再开始 SCCB 初始化。
  • [ ] 所有页寄存器访问由同一互斥锁保护。
  • [ ] 硬件复位、软件复位和掉电后 Page Cache 失效。
  • [ ] 每个真实电源周期重新检查 OTP Load 状态。
  • [ ] P9:0x81[7] 有超时保护。
  • [ ] BIST Done 与 Error 的判定符合实际启动流程。
  • [ ] Linear/HDR/DCG 各模式分别确认 P3:0x9A
  • [ ] Mirror/Flip/Binning 后重新验证固定坏点位置。
  • [ ] 不把 P9:0x80~0xC3 当作 OTP 数据窗口。
  • [ ] 不自行解释 CP、TPM 和 DPC 字节格式。
  • [ ] 原始 Dump 只有在取得正式读序列后启用。
  • [ ] 产品固件不包含可写 OTP 编程入口。
  • [ ] Sensor DPC 与 RKISP DPC 完成分级 A/B 测试。
  • [ ] AWB/LSC 数据来源与外部 EEPROM/IQ 文件明确区分。

27. 结论

OS04H10 的 OTP 可以概括为一条完整的芯片内部校正链路,而不是单纯的 256 字节存储:

256-byte OTP Array
        │
        ▼
P9 OTP Controller:加载、状态、BIST、地址与数据路径
        │
        ├─ KSCG 相关内部校准
        └─ 固定坏点数据
                │
                ▼
P6 OTP DPC:坐标、镜像、翻转、Binning、约束
                │
                ▼
P3:0x9A:长帧与短帧使能
                │
                ▼
校正后的 RAW 经 MIPI 输出到 Rockchip CIF/ISP

运行期开发的重点是:

  1. 正确处理上电和 SCCB 时序;
  2. 以页安全方式访问寄存器;
  3. 等待 OTP Load 完成并检查错误状态;
  4. 根据 Linear/HDR 模式正确开启长、短帧 OTP DPC;
  5. 在每次真正掉电后重新建立 OTP 运行状态;
  6. 对 Mirror、Flip、Binning 和 HDR 做图像级验证;
  7. 不在缺少正式资料时猜测原始数据端口和编程流程。

从 OS04H10 当前数据手册能够可靠实现的是 OTP 运行状态管理和内部 OTP DPC 使用。要进一步完成 256 字节原始 Dump、坏点条目解析或量产烧录,还需要 OmniVision OTP Application Note、DPC Encoding 和模组量产规范。将资料边界写进驱动设计,本身就是避免不可逆硬件风险的关键工程措施。

如果你正在搭建 Rockchip 摄像头链路,建议同时关注 Rockchip IQ 标定说明 与 Sensor 侧 DPC 的配合,避免两级 DPC 叠加后纹理被误修复。

附录 A:OTP 相关寄存器速查

模块 地址 作用
Page Select 0xFD 选择 P0/P1/P3/P5/P6/P9
System P0:0x27[2] OTP DPC Clock Gating
System P0:0x57[4] OTP Buffer Soft Reset 相关
ISP P3:0x9A[0] OTP DPC Long Enable
ISP P3:0x9A[1] OTP DPC Short Enable
ISP Test P3:0xE2[1] OTP DPC Bad Pixel Pattern
BLC P5:0xDB[2] KSCG OTP Enable
OTP DPC P6:0x04~0x2B 固定坏点几何和约束控制
OTP P9:0x80 Program 状态与控制
OTP P9:0x81 Load/BIST/Read 状态与控制
OTP P9:0x84~0x85 模式、SRAM、自动加载、BIST
OTP P9:0x88~0x8B Start/End Point
OTP P9:0x8E~0x8F BIST Error Address
OTP P9:0x90~0x91 OTP Address Base
OTP P9:0x9D Byte Read/Window/DPC Path
OTP P9:0xA8 ECC/DMA/Test 控制
OTP P9:0xC0~0xC3 DPC Start/End Address

附录 B:继续落地所需资料

资料 用途
OS04H10 OTP Read/Load Application Note 完成手动加载与原始数据读取
OS04H10 OTP Programming Guide 建立安全量产烧录工位
OS04H10 OTP DPC Encoding 解析 0x18~0xFF 坏点条目
官方 Mode Register Tables 确认各模式的 P3/P5/P6/P9 组合
模组 EEPROM Map 确认 AWB/LSC/AF 等模组校准来源
Rockchip IQ 标定说明 协调 Sensor DPC 与 RKISP DPC

参考资料

  1. OmniVision OS04H10 Datasheet, Preliminary Specification, Version 1.0, 2024-03-01。
  2. OS04H10 数据手册第 2.11 节:Power Up/Off Sequence。
  3. OS04H10 数据手册第 2.12 节:SCCB Bus。
  4. OS04H10 数据手册第 4.7 节与表 4-5:OTP Memory Usage。
  5. OS04H10 数据手册第 5.8 节:OTP DPC Registers。
  6. OS04H10 数据手册第 5.10 节:OTP Registers。
  7. OS04H10 数据手册第 5.11 节:Page Selection。



上一篇:Agent-Reach 一句指令部署全网冲浪,GitHub 66k Star 懒人救星
下一篇:苹果发布2nm工艺M6与M5 Ultra芯片,Mac性能大幅跃升
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-8-27 03:20 , Processed in 0.805903 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表