2.7 V4L2 sub-devices

许多 drivers 需要和 sub-devices 交流,这些子设备可以处理 audio, video muxing, encoding, decoding 等等。 对于 webcams, 常见的 sub-devices 有 sensors, camera controllers.

通过 v4l2_subdev_init(sd, &ops) 来初始化 v4l2_subdev. 接着需要设置 sd->name.

如果需要和 media framework 聚合,那么需要初始化 media_entity 成员,如果 entity 有 pads 还需要调用media_entity_pads_init.

struct v4l2_subdev {
#if defined(CONFIG_MEDIA_CONTROLLER)
	struct media_entity entity;
#endif
	struct list_head list;
	struct module *owner;
	bool owner_v4l2_dev;
	u32 flags;
	struct v4l2_device *v4l2_dev;
	const struct v4l2_subdev_ops *ops;
	const struct v4l2_subdev_internal_ops *internal_ops;
	struct v4l2_ctrl_handler *ctrl_handler;
	char name[52];
	u32 grp_id;
	void *dev_priv;
	void *host_priv;
	struct video_device *devnode;
	struct device *dev;
	struct fwnode_handle *fwnode;
	struct list_head async_list;
	struct list_head async_subdev_endpoint_list;
	struct v4l2_async_notifier *subdev_notifier;
	struct list_head asc_list;
	struct v4l2_subdev_platform_data *pdata;
	struct mutex *state_lock;

	struct led_classdev *privacy_led;
	struct v4l2_subdev_state *active_state;
	u64 enabled_streams;
};

owner_v4l2_dev: 如果和 v4l2_dev->dev 的 owner 一致,则为 true.
flags: V4L2_SUBDEV_FL_IS_I2C 表示是 I2C 设备,V4L2_SUBDEV_FL_IS_SPI 表示是 SPI 设备, V4L2_SUBDEV_FL_HAS_DEVNODE 表示需要 device node, V4L2_SUBDEV_FL_HAS_EVENTS 表示会生成 events. V4L2_SUBDEV_FL_STREAMS 表示支持 multiplexed streams. v4l2_dev: 指向 v4l2_device.
ops: subdev 的操作函数。
internal_ops: subdev 的内部操作函数。
ctrl_handler: v4l2 控制句柄。
grp_id: 该 subdev 属于哪个 subdev group, 由 driver 自定义。

e.g.

static const struct v4l2_subdev_pad_ops rkisp1_isp_pad_ops = {
	...
};

static const struct v4l2_subdev_video_ops rkisp1_isp_video_ops = {
	...
};

static const struct v4l2_subdev_core_ops rkisp1_isp_core_ops = {
	...
};

static const struct v4l2_subdev_ops rkisp1_isp_ops = {
	.core = &rkisp1_isp_core_ops,
	.video = &rkisp1_isp_video_ops,
	.pad = &rkisp1_isp_pad_ops,
	...
};

v4l2_subdev_init(sd, &rkisp1_isp_ops);

下面的回调中只选取了一些和 video input 相关的重要的回调函数。

struct v4l2_subdev_internal_ops {
	int (*init_state)(struct v4l2_subdev *sd,
			  struct v4l2_subdev_state *state);
	int (*registered)(struct v4l2_subdev *sd);
	void (*unregistered)(struct v4l2_subdev *sd);
	int (*open)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh);
	int (*close)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh);
	void (*release)(struct v4l2_subdev *sd);
};

init_state: 初始化 subdev state.
registered: 在 subdev 注册完成后调用的回调函数。
unregistered: 与 registered 相反。
open: 在 open subdev device node 后的回调函数。
close: 与 open 相反。
release: 当 reference 都被释放调用的回调函数。

struct v4l2_subdev_core_ops {
	int (*log_status)(struct v4l2_subdev *sd);
	long (*ioctl)(struct v4l2_subdev *sd, unsigned int cmd, void *arg);
	int (*subscribe_event)(struct v4l2_subdev *sd, struct v4l2_fh *fh,
			       struct v4l2_event_subscription *sub);
	int (*unsubscribe_event)(struct v4l2_subdev *sd, struct v4l2_fh *fh,
				 struct v4l2_event_subscription *sub);
}

log_status: ioctl VIDIOC_LOG_STATUS 的回调函数。
ioctl: subdev 的自定义扩展 ioctl。
subscribe_event: 订阅 event 回调。
unsubscribe_event: 和 subscribe_event 相反。

struct v4l2_subdev_video_ops {
	int (*s_stream)(struct v4l2_subdev *sd, int enable);
	int (*g_frame_interval)(struct v4l2_subdev *sd,
				struct v4l2_subdev_frame_interval *interval);
	int (*s_frame_interval)(struct v4l2_subdev *sd,
				struct v4l2_subdev_frame_interval *interval);
	int (*s_rx_buffer)(struct v4l2_subdev *sd, void *buf,
			   unsigned int *size);
	int (*pre_streamon)(struct v4l2_subdev *sd, u32 flags);
	int (*post_streamoff)(struct v4l2_subdev *sd);
}

s_stream: subdev start stream callback.
g_frame_interval, s_frame_interval: sensor subdev 实现用来获取/调节帧率。 s_rx_buffer: pre_streamon: stream on 前的操作,目前用来 set CSI-2 transmitter 到 LP-11 或者 LP-111 这样的状态。 post_streamoff: stream off 之后的操作。

struct v4l2_subdev_pad_ops {
	int (*init_cfg)(struct v4l2_subdev *sd,
			struct v4l2_subdev_state *state);
	int (*enum_mbus_code)(struct v4l2_subdev *sd,
			      struct v4l2_subdev_state *state,
			      struct v4l2_subdev_mbus_code_enum *code);
	int (*enum_frame_size)(struct v4l2_subdev *sd,
			       struct v4l2_subdev_state *state,
			       struct v4l2_subdev_frame_size_enum *fse);
	int (*enum_frame_interval)(struct v4l2_subdev *sd,
				   struct v4l2_subdev_state *state,
				   struct v4l2_subdev_frame_interval_enum *fie);
	int (*get_fmt)(struct v4l2_subdev *sd,
		       struct v4l2_subdev_state *state,
		       struct v4l2_subdev_format *format);
	int (*set_fmt)(struct v4l2_subdev *sd,
		       struct v4l2_subdev_state *state,
		       struct v4l2_subdev_format *format);
	int (*get_selection)(struct v4l2_subdev *sd,
			     struct v4l2_subdev_state *state,
			     struct v4l2_subdev_selection *sel);
	int (*set_selection)(struct v4l2_subdev *sd,
			     struct v4l2_subdev_state *state,
			     struct v4l2_subdev_selection *sel);
	int (*get_edid)(struct v4l2_subdev *sd, struct v4l2_edid *edid);
	int (*set_edid)(struct v4l2_subdev *sd, struct v4l2_edid *edid);
#ifdef CONFIG_MEDIA_CONTROLLER
	int (*link_validate)(struct v4l2_subdev *sd, struct media_link *link,
			     struct v4l2_subdev_format *source_fmt,
			     struct v4l2_subdev_format *sink_fmt);
#endif /* CONFIG_MEDIA_CONTROLLER */
	int (*get_frame_desc)(struct v4l2_subdev *sd, unsigned int pad,
			      struct v4l2_mbus_frame_desc *fd);
	int (*set_frame_desc)(struct v4l2_subdev *sd, unsigned int pad,
			      struct v4l2_mbus_frame_desc *fd);
	int (*get_mbus_config)(struct v4l2_subdev *sd, unsigned int pad,
			       struct v4l2_mbus_config *config);
	int (*enable_streams)(struct v4l2_subdev *sd,
			      struct v4l2_subdev_state *state, u32 pad,
			      u64 streams_mask);
	int (*disable_streams)(struct v4l2_subdev *sd,
			       struct v4l2_subdev_state *state, u32 pad,
			       u64 streams_mask);
}

enum_mbus_code: subdev VIDIOC_SUBDEV_ENUM_MBUS_CODE ioctl 的回调函数。

2.7.1 Subdev registration

V4L2 子设备注册的方式有两种。

第一种是由 bridge driver 来注册 subdevices. 适用于 internal subdevices, 比如 video data processing units, camera sensor.

第二种是 bridge driver 和 subdevice 异步注册。适用于 subdevices 的信息不是在 bridge driver 中, 而是比如在设备树中定义的 i2c 设备。

2.7.1.1 Registering synchronous sub-devices

和 bridge driver 同步注册。

注册:v4l2_device_register_subdev(v4l2_dev, sd)

注销:v4l2_device_unregister_subdev(sd)

2.7.1.2 Registering asynchronous sub-devices

和 bridge driver 异步注册。

注册:v4l2_async_register_subdev(v4l2_dev, sd)

注销:v4l2_async_unregister_subdev(sd)

通过异步注册的 subdev, 会被挂入全局的 global list, 等待 bridge driver 统一注册。

2.7.1.3 Asynchronous sub-device notifiers

Bridge driver 需要注册一个 notifier object.

注册:v4l2_async_nf_register()

注销:v4l2_async_nf_unregister(), 在释放 unregistered notifier 前,还需要调用v4l2_async_nf_cleanup().

注册 notifier 前,bridge driver 首先需要调用v4l2_async_nf_init(), 接着可以通过v4l2_async_nf_add_fwnode(), v4l2_async_nf_add_fwnode_remote()v4l2_async_nf_add_i2c() 来获取 bridge device 需要的 connection descriptor.

2.7.1.4 Asynchronous sub-device notifier for sub-devices

async sub-device 也可以注册一个 asyncchronous notifier, 比如用于注册 sensor 依赖的一些设备,flash LED, lens focus.

通过 v4l2_async_subdev_nf_init() 初始化。

2.7.1.5 Asynchronous sub-device registration helper for camera sensor drivers

对于 camera sensor, 有一个 helper function 用来注册 subdev, 同时还会注册 sensor 的异步 notifier, 用来 异步注册 lens, flash devices 等。

v4l2_async_register_subdev_sensor()

2.7.1.7 Asynchronous sub-device notifier callbacks

V4L2 core 会利用 connection descriptors 来匹配异步注册的 subdevices.

connection match 之后调用.bound() 回调,所有 connections 都 bound 之后调用.complete() 回调。

connection remove 之后调用.unbind() 回调。

2.7.2 Calling subdev operations

调用某个 subdev 的 ops 回调,使用如下宏:

v4l2_subdev_call()

调用所有或某组 subdev 的 ops:

v4l2_device_call_all()

v4l2_device_call_until_err()

如果该 subdev 要通知上层的 v4l2_device, 可以调用v4l2_device_notify()

2.8 V4L2 sub-device userspace API

/dev 下的 v4l-subdevX 节点,对应于 v4l2_subdev. 如果需要生成 subdev 设备节点,subdev 需要在注册前 置起 V4L2_SUBDEV_FL_HAS_DEVNODE flag. v4l2 driver 通过 v4l2_device_register_subdev_nodes() 来注册所有的 subdev 节点。

这些 subdev device nodes 支持下列 ioctl:

VIDIOC_QUERYCTRL, VIDIOC_QUERYMENU, VIDIOC_G_CTRL, VIDIOC_S_CTRL, VIDIOC_G_EXT_CTRLS, VIDIOC_S_EXT_CTRLS, VIDIOC_TRY_EXT_CTRLS.

subdev 这些 ioctl 和 v4l2 device 是一致的。

VIDIOC_DQEVENT, VIDIOC_SUBSCRIBE_EVENT, VIDIOC_UNSUBSCRIBE_EVENT

subdev event 相关的 ioctl 和 v4l2 device 也是一致的。需要在注册 subdev 前置起 V4L2_SUBDEV_FL_HAS_EVENTS.

2.9 Read-only sub-device userspace API

如果不希望通过 subdev device node 修改参数,可以设置为只读的。

v4l2 driver 用 v4l2_device_register_ro_subdev_nodes() 代替 v4l2_device_register_subdev_nodes() 注册 subdev 节点。

2.10 I2C sub-device drivers

i2c subdev 的一些 helper function 在v4l2-common.h中。

void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
		const struct v4l2_subdev_ops *ops);
struct v4l2_subdev *v4l2_i2c_new_subdev(struct v4l2_device *v4l2_dev,
		struct i2c_adapter *adapter, const char *client_type,
		u8 addr, const unsigned short *probe_addrs);
struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev,
		struct i2c_adapter *adapter, struct i2c_board_info *info,
		const unsigned short *probe_addrs);

v4l2_i2c_subdev_init(): 初始化 v4l2_subdev.

v4l2_i2c_new_subdev(): 注册 v4l2_subdev 和 i2c_client.

v4l2_i2c_new_subdev_board(): v4l2_i2c_new_subdev 更底层的实现。

2.11 Centrally managed subdev active state

// TODO:

2.13 data structures and functions

APIs:

v4l2-async.h

struct v4l2_async_notifier {
	const struct v4l2_async_notifier_operations *ops;
	struct v4l2_device *v4l2_dev;
	struct v4l2_subdev *sd;
	struct v4l2_async_notifier *parent;
	struct list_head waiting_list;
	struct list_head done_list;
	struct list_head notifier_entry;
}

ops: 包括.bound 在绑定一个 subdev 后调用,.complete 在绑定完所有 subdev 后调用,.unbind, .destroy。
v4l2_dev: 如果是顶层的 async_notifier, 那么在 v4l2_async_nf_init() 中绑定 v4l2_dev.
sd: 如果是 subdev 的 async_notifier, 那么在 v4l2_async_subdev_nf_init() 中绑定 subdev.
parent: 上一层 async_notifier.
waiting_list: 从设备树中 parse 到的 async connection.
done_list: 匹配完成的 subdev。
notifier_entry:

void v4l2_async_nf_init(struct v4l2_async_notifier *notifier,
			struct v4l2_device *v4l2_dev);
void v4l2_async_subdev_nf_init(struct v4l2_async_notifier *notifier,
			       struct v4l2_subdev *sd);

int v4l2_async_nf_register(struct v4l2_async_notifier *notifier);
void v4l2_async_nf_unregister(struct v4l2_async_notifier *notifier);
void v4l2_async_nf_cleanup(struct v4l2_async_notifier *notifier);

#define v4l2_async_nf_add_fwnode(notifier, fwnode, type);
#define v4l2_async_nf_add_fwnode_remote(notifier, ep, type);
#define v4l2_async_nf_add_i2c(notifier, adapter, address, type);

int v4l2_async_subdev_endpoint_add(struct v4l2_subdev *sd,
				   struct fwnode_handle *fwnode);
struct v4l2_async_connection *
v4l2_async_connection_unique(struct v4l2_subdev *sd);


#define v4l2_async_register_subdev(sd);
void v4l2_async_unregister_subdev(struct v4l2_subdev *sd);
int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd);

v4l2-subdev.h