IMX415 里有两套“电源管理回调”:
Runtime PM 回调:摄像头平时不用时断电,开流时再上电。
System Sleep PM 回调:整个系统执行 suspend/resume 时触发。
它们都先放进struct dev_pm_ops,再绑定到i2c_driver.driver.pm。
一、回调在哪里绑定
1. 先定义dev_pm_ops
源码中:
static const struct dev_pm_ops imx415_pm_ops = { SET_RUNTIME_PM_OPS(imx415_runtime_suspend, imx415_runtime_resume, NULL) SET_LATE_SYSTEM_SLEEP_PM_OPS(imx415_suspend, imx415_resume) };这里包含两组回调。
Runtime PM
SET_RUNTIME_PM_OPS( imx415_runtime_suspend, imx415_runtime_resume, NULL )大致等价于:
static const struct dev_pm_ops imx415_pm_ops = { .runtime_suspend = imx415_runtime_suspend, .runtime_resume = imx415_runtime_resume, .runtime_idle = NULL, };系统休眠 PM
SET_LATE_SYSTEM_SLEEP_PM_OPS( imx415_suspend, imx415_resume )通常会把它们放到系统休眠的 late/early 阶段,例如:
.suspend_late = imx415_suspend; .resume_early = imx415_resume;同时也可能覆盖 freeze/thaw、poweroff/restore 等系统休眠阶段,具体宏展开与内核版本有关。IMX415 的 Runtime PM 和系统休眠回调都集中注册在imx415_pm_ops中。
2. 再绑定到 I2C 驱动
源码底部:
static struct i2c_driver imx415_i2c_driver = { .driver = { .name = IMX415_NAME, .pm = &imx415_pm_ops, .of_match_table = of_match_ptr(imx415_of_match), }, .probe = &imx415_probe, .remove = &imx415_remove, .id_table = imx415_match_id, };关键是:
.pm = &imx415_pm_ops,完整绑定关系:
imx415_i2c_driver │ └── driver │ └── pm │ └── imx415_pm_ops ├── runtime_suspend │ └── imx415_runtime_suspend ├── runtime_resume │ └── imx415_runtime_resume ├── suspend_late │ └── imx415_suspend └── resume_early └── imx415_resume驱动与 I2C 设备匹配后:
client->dev.driver ↓ &imx415_i2c_driver.driver ↓ dev->driver->pm ↓ &imx415_pm_ops以后 PM Core 需要挂起或恢复这个设备时,就通过:
dev->driver->pm->runtime_suspend(dev); dev->driver->pm->runtime_resume(dev);找到 IMX415 的回调。IMX415 的 PM 表最终就是通过i2c_driver.driver.pm绑定给 I2C 设备的。
二、Runtime PM 回调具体做什么
1. Runtime Resume
static int __maybe_unused imx415_runtime_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct v4l2_subdev *sd = i2c_get_clientdata(client); struct imx415 *imx415 = to_imx415(sd); return __imx415_power_on(imx415); }调用链:
PM Core ↓ imx415_runtime_resume(dev) ↓ device → i2c_client ↓ i2c_client → v4l2_subdev ↓ v4l2_subdev → struct imx415 ↓ __imx415_power_on(imx415)__imx415_power_on()才是真正操作硬件的函数,通常包括:
选择 default pinctrl 打开 power GPIO 拉 reset 设置并打开 xvclk 打开 dvdd/dovdd/avdd regulator 等待 Sensor 上电稳定 释放 reset2. Runtime Suspend
static int __maybe_unused imx415_runtime_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct v4l2_subdev *sd = i2c_get_clientdata(client); struct imx415 *imx415 = to_imx415(sd); __imx415_power_off(imx415); return 0; }调用链:
PM Core ↓ imx415_runtime_suspend(dev) ↓ __imx415_power_off(imx415)__imx415_power_off()通常执行:
reset GPIO 置为复位状态 关闭 xvclk 切换到 sleep pinctrl 关闭 power GPIO 关闭 regulator这两个 Runtime PM 回调最终分别调用__imx415_power_on()和__imx415_power_off()。
三、Runtime PM 是什么时候启用的
在imx415_probe()的前半段,驱动为了读取 Chip ID,会先直接上电:
ret = __imx415_power_on(imx415); if (ret) goto err_free_handler; ret = imx415_check_sensor_id(imx415, client); if (ret) goto err_power_off;注意:
这里第一次上电不是 Runtime PM 回调触发的,而是
probe()直接调用__imx415_power_on()。
因为这时 Runtime PM 还没启用。
完成 Sensor 注册后,probe()尾部执行:
pm_runtime_set_active(dev); pm_runtime_enable(dev); pm_runtime_idle(dev);含义分别是:
pm_runtime_set_active(dev)
告诉 PM Core:
设备当前物理状态是上电状态因为前面已经手动执行了:
__imx415_power_on()所以软件状态也要标记为 active。
pm_runtime_enable(dev)
正式启用该设备的 Runtime PM。
此后:
pm_runtime_get_xxx() pm_runtime_put_xxx()才会驱动 active/suspended 状态变化。
pm_runtime_idle(dev)
告诉 PM Core:
probe已经完成,当前没有用户使用设备, 可以检查是否进入Runtime Suspend如果当前:
usage_count == 0且没有其他阻止条件,PM Core 就可能调用:
imx415_runtime_suspend(dev)进而把 probe 阶段临时打开的 Sensor 关闭。
所以 probe 阶段的完整电源流程大致是:
imx415_probe() ↓ __imx415_power_on() // 直接上电 ↓ 读取 Chip ID ↓ 注册 V4L2 subdev ↓ pm_runtime_set_active() ↓ pm_runtime_enable() ↓ pm_runtime_idle() ↓ PM Core判断无人使用 ↓ imx415_runtime_suspend() ↓ __imx415_power_off()IMX415 在 probe 中先直接上电识别芯片,完成注册后才用pm_runtime_set_active()、pm_runtime_enable()和pm_runtime_idle()把控制权移交给 Runtime PM。
四、开流时什么时候触发 Runtime Resume
IMX415 的s_stream()中:
static int imx415_s_stream(struct v4l2_subdev *sd, int on) { ... if (on) { ret = pm_runtime_get_sync(&client->dev); if (ret < 0) { pm_runtime_put_noidle(&client->dev); goto unlock_and_return; } ret = __imx415_start_stream(imx415); ... } else { __imx415_stop_stream(imx415); pm_runtime_put(&client->dev); } }开流
应用执行:
v4l2-ctl -d /dev/video0 --stream-mmap大致链路:
VIDIOC_STREAMON ↓ RKISP/RKCIF启动pipeline ↓ 调用Sensor subdev的s_stream(1) ↓ imx415_s_stream(sd, 1) ↓ pm_runtime_get_sync(&client->dev)此时有两种情况。
Sensor 当前已 Runtime Suspend
dev->power.runtime_status == RPM_SUSPENDED那么:
pm_runtime_get_sync() ↓ usage_count加1 ↓ PM Core发现设备已挂起 ↓ dev->driver->pm->runtime_resume(dev) ↓ imx415_runtime_resume(dev) ↓ __imx415_power_on(imx415)Sensor 上电后再执行:
__imx415_start_stream(imx415);最终写寄存器并开启 MIPI 输出。
Sensor 本来就是 Active
这时pm_runtime_get_sync()主要增加引用计数,不会重复调用:
imx415_runtime_resume()所以:
调用了
pm_runtime_get_sync(),不代表每次都会进入runtime_resume();只有设备当前处于 Runtime Suspended 状态时才需要恢复。
回调函数触发入口:
IMX415 的主要 Runtime Resume 触发入口就是s_stream(1)中的pm_runtime_get_sync()。
五、停流时什么时候触发 Runtime Suspend
应用执行停止采集:
VIDIOC_STREAMOFF ↓ RKISP/RKCIF停止pipeline ↓ imx415_s_stream(sd, 0)驱动执行:
__imx415_stop_stream(imx415); pm_runtime_put(&client->dev);pm_runtime_put()会减少 Runtime PM 使用计数:
usage_count = usage_count - 1如果减少后:
usage_count == 0PM Core 会认为设备可能已经无人使用,随后根据 Runtime PM 策略进入 suspend:
pm_runtime_put() ↓ usage_count减到0 ↓ PM Core请求设备空闲 ↓ dev->driver->pm->runtime_suspend(dev) ↓ imx415_runtime_suspend(dev) ↓ __imx415_power_off(imx415)所以开关流对应关系是:
STREAMON ↓ pm_runtime_get_sync() ↓ runtime_resume ↓ Sensor上电 STREAMOFF ↓ pm_runtime_put() ↓ runtime_suspend ↓ Sensor断电不过严格来说,pm_runtime_put()是否立即执行 suspend,取决于内核 PM 策略、引用计数和是否启用了 autosuspend。这个驱动片段没有显示设置 autosuspend 延时,因此通常会较快进入 idle/suspend。
六、s_power()也可能触发 Runtime PM
IMX415 还实现了 V4L2 的:
static int imx415_s_power(struct v4l2_subdev *sd, int on)内部也是:
if (on) { ret = pm_runtime_get_sync(&client->dev); ... } else { pm_runtime_put(&client->dev); }因此:
V4L2调用s_power(1) ↓ pm_runtime_get_sync() ↓ 可能触发imx415_runtime_resume() V4L2调用s_power(0) ↓ pm_runtime_put() ↓ 可能触发imx415_runtime_suspend()注意:
imx415_s_power()本身不是 PM Core 回调,它是:
struct v4l2_subdev_core_ops中的 V4L2 回调:
static const struct v4l2_subdev_core_ops imx415_core_ops = { .s_power = imx415_s_power, ... };它只是通过调用 Runtime PM API,间接触发真正的 PM 回调。
关系是:
V4L2回调 imx415_s_power() ↓ pm_runtime_get_sync / put ↓ PM Core ↓ Runtime PM回调 imx415_runtime_resume / suspendIMX415 的s_stream()和s_power()都通过 Runtime PM API维护使用计数。
七、imx415_set_ctrl()会不会触发上电回调
代码中一般是:
if (!pm_runtime_get_if_in_use(&client->dev)) return 0;这里使用的是:
pm_runtime_get_if_in_use()它与:
pm_runtime_get_sync()不同。
pm_runtime_get_sync()
设备如果已经休眠,会主动唤醒:
可能触发 runtime_resumepm_runtime_get_if_in_use()
只在设备本来就在使用中时增加引用:
设备Active且正在使用 → 增加引用 → 可以写寄存器 设备已经Suspended → 返回0 → 不唤醒设备 → 不触发runtime_resume因此:
单独设置曝光时,如果 Sensor 已经断电,
imx415_set_ctrl()不会为了写曝光而把 Sensor 重新上电。
它只保存 Control 值,等开流时:
s_stream(1) ↓ pm_runtime_get_sync() ↓ runtime_resume ↓ __v4l2_ctrl_handler_setup() ↓ imx415_set_ctrl() ↓ 真正写曝光寄存器八、系统休眠回调什么时候触发
这一组:
SET_LATE_SYSTEM_SLEEP_PM_OPS(imx415_suspend, imx415_resume)不是开关摄像头流时触发,而是在整个 Linux 系统执行休眠/唤醒时触发。
例如:
echo mem > /sys/power/state或者:
systemctl suspend系统进入 suspend:
用户执行system suspend ↓ Linux PM Core遍历所有设备 ↓ 进入设备suspend late阶段 ↓ dev->driver->pm->suspend_late(dev) ↓ imx415_suspend(dev)系统唤醒:
系统开始resume ↓ 进入设备resume early阶段 ↓ dev->driver->pm->resume_early(dev) ↓ imx415_resume(dev)你的 IMX415 中,这两个函数只有在:
CONFIG_VIDEO_CAM_SLEEP_WAKEUP可用时才真正存在:
#if IS_REACHABLE(CONFIG_VIDEO_CAM_SLEEP_WAKEUP) static int imx415_suspend(struct device *dev) { ... cam_sw_prepare_sleep(...); return 0; } static int imx415_resume(struct device *dev) { ... cam_sw_prepare_wakeup(...); cam_sw_write_array(...); __v4l2_ctrl_handler_setup(...); return 0; } #else #define imx415_resume NULL #define imx415_suspend NULL #endif也就是说,如果没有开启该配置:
系统休眠回调实际是NULL但 Runtime PM 回调仍然存在。系统 resume 时还会重新写模式寄存器和 Control 值。
九、四种电源相关函数不要混淆
| 函数 | 类型 | 谁调用 |
|---|---|---|
imx415_s_power() | V4L2 subdev回调 | V4L2框架或上游驱动 |
imx415_runtime_resume() | Runtime PM回调 | Linux PM Core |
imx415_runtime_suspend() | Runtime PM回调 | Linux PM Core |
__imx415_power_on() | 内部硬件函数 | probe或runtime_resume |
__imx415_power_off() | 内部硬件函数 | remove或runtime_suspend |
imx415_suspend() | 系统休眠回调 | 系统PM Core |
imx415_resume() | 系统唤醒回调 | 系统PM Core |
最重要的分层是:
上层业务回调 s_stream / s_power ↓ Runtime PM接口 pm_runtime_get_sync / pm_runtime_put ↓ Linux PM Core ↓ PM回调 runtime_resume / runtime_suspend ↓ 真正硬件操作 __power_on / __power_off十、整条开流上电流程
应用层 v4l2-ctl --stream-mmap ↓ VIDIOC_STREAMON ↓ RKISP/RKCIF启动Media Pipeline ↓ 调用IMX415 subdev imx415_s_stream(sd, 1) ↓ pm_runtime_get_sync(&client->dev) ↓ Runtime PM检查设备状态 ├── 已Active:只增加usage_count └── 已Suspended: ↓ dev->driver->pm ↓ imx415_pm_ops.runtime_resume ↓ imx415_runtime_resume(dev) ↓ __imx415_power_on(imx415) ↓ regulator、GPIO、clock、reset上电 ↓ __imx415_start_stream() ↓ 写Sensor寄存器 ↓ Sensor输出MIPI数据停流则相反:
VIDIOC_STREAMOFF ↓ imx415_s_stream(sd, 0) ↓ __imx415_stop_stream() ↓ pm_runtime_put() ↓ usage_count降为0 ↓ imx415_runtime_suspend() ↓ __imx415_power_off()一句话概括:
电源管理回调通过
imx415_i2c_driver.driver.pm = &imx415_pm_ops绑定给 I2C 设备;Runtime PM 回调主要由s_stream()或s_power()中的pm_runtime_get_sync()、pm_runtime_put()间接触发,而系统suspend/resume回调由整个 Linux 系统进入休眠和唤醒过程触发。