Allwinner T507 プラットフォームは、I2C を直接操作するための V4L2 センサー API を追加します

参考

私は次の記事を非常に真剣に参照し、事前にリストしました。
とても良い記事
Linux V4L2 ドラフト
v4l2 学習の提案とプロセス分析
V4L2 フレームワーク分析
Linux V4L2 ドラフト
Linux V4L2 ドラフト

著者:
組み込み Max V4L2 フレームワークの完全な分析

00 - V4L2 フレームワークの概要
01 - V4L2 フレームワーク-v4l2 デバイス
02 - V4L2 フレームワーク-メディア-デバイス
03 - V4L2 フレームワーク-videobuf2
V4L2 フレームワーク-コントロール
V4L2 フレームワーク-コントロールのデータ構造

著者: Letcos
[カメラ]v4l2 フレームワークのカーネル空間分析

次の記事は一般的な参考用です。ここに記録してください。
V4L2 ソース コード ツアー 1: struct v4l2_subdev
V4L2 ソース コード ツアー 2: V4L2 サブデバイス ユーザー空間 API
V4L2 ソース コード ツアー 3: I2C サブデバイス ドライバー
V4L2 ソース コード ツアー 4: struct video_device
V4L2 ソース コード ジャーニー 5: V4L2 の開始点と終了点 V4L2
ソース コード ジャーニー 6: ソース コードの追跡
V4L2 ソース コード ジャーニー 7: 制御 queryctrl()
V4L2 ソース コード ジャーニー 8: ioctl
V4L2 ソース コード ジャーニー 9 :videobuf
V4L2 ソースコードツアー 10: videobuf の主な構造

ザイリンクス Linux V4L2 ビデオ パイプライン ドライバーの分析

目標

V4L2 でカプセル化された Sensor の場合、AP 層で I2C を直接操作する API を追加します。

背景:

ここではAllwinner T507のSDKを使用しており、Linuxのバージョンは4.9.191です。

Allwinner プラットフォームでは、センサー デバイスはデフォルトでは標準の I2C デバイスではないため、/dev/i2c-xデバイスは直接読み書きできますが、代わりに V4L2 デバイスとして登録されます (/dev/ビデオxこのように、AP 層は通常の I2C 操作を通じてセンサーのレジスタを操作できません。

Allwinner プラットフォームの I2C には独自の規格があります。TWI : Normal Two Wire Interface: 「Allwinner プラットフォームは、I2C 標準プロトコルのバス制御プロトコルと互換性があります。」

しかし、ソフトウェアレベルでは依然として I2C の記述が使用されているのはあまりにも不愉快であり、I2C 設立の本来の目的を裏切る新たな民間システムを発明する必要はないと思います。

これの目的は何ですか?

/dev/videox として登録される理由は、問題が発生する可能性があるセンサー上で AP 層が直接 I2C 操作を実行することを避けるためです。I2C 操作の場合、Allwinner は操作用の cci 層をカプセル化します

したがって、cci を使用するか twi (つまり、I2C) を使用するかは、選択できるカーネルの構成から知ることができます。デフォルトでは、現在の cci 方式を保持し、オプションでは、V4L2 を通常の I2C デバイスとして使用します。 。

.config - Linux/arm64 4.9.191 カーネル構成
デバイス ドライバー > マルチメディア サポート > V4L プラットフォーム デバイス > cci または cci to twi を選択 (内部 cci を使用) —>
(X) 内部 cci を使用
( ) cci を twi に変更

どうやってするの?

最初は V4L2 コマンドの仕組みがわからず、スズメを解剖することでしか勉強できませんでしたが、
サブデバイスについては、I2C、センサー、v4l2 のサブデバイスとして使用でき、相互に参照したり変換したりするのでサブデバイスと呼びます。新たな理解があれば追記します。

// 从v4l2_subdev获取i2c_client:
struct i2c_client *client = v4l2_get_subdevdata(sd);

// 从i2c_client获取v4l2_subdev:
 struct v4l2_subdev *sd = i2c_get_clientdata(client);
 
 
## 从sd成员来获取父成员的地址
struct v4l2_subdev *sd;
struct sensor_info *info = to_state(sd);

## 获取info的成员成员sd的地址
struct sensor_info *info;
struct v4l2_subdev *sd = &info->sd;
## 这2个是一个互为 相反的操作

1 V4L2サブデバイスの初期化を検討する

sunxi プラットフォームでは、/dev/video0 デバイスは次のプロセスを通じて大まかに初期化されます。

module_init(vin_init);
└──>vin_init(void);// drivers/media/platform/sunxi-vin/vin-video/vin_core.c
	├──	sunxi_csi_platform_register();
	└──>sunxi_vin_core_register_driver();
		└──>platform_driver_register(&vin_core_driver);
			└──>static struct platform_driver vin_core_driver = {
    
    
					.probe = vin_core_probe,
					...
					}
				vin_core_probe();//drivers/media/platform/sunxi-vin/vin-video/vin_video.c
				└──>vin_initialize_capture_subdev();// drivers/media/v4l2-core/v4l2-subdev.c				// 此时将device作为链表加入init序列,并开始初始化具体的device,下面是间接的调用				
					├──>v4l2_subdev_init();
					│ sd->internal_ops = &vin_capture_sd_internal_ops;struct v4l2_subdev_internal_ops vin_capture_sd_internal_ops = {
    
    .registered = vin_capture_subdev_registered,.unregistered = vin_capture_subdev_unregistered,};
					└──v4l2_set_subdevdata(sd)
						└──>vin_capture_subdev_registered()
							| // drivers/media/platform/sunxi-vin/vin-video/vin_video.c
							├──>vin_init_controls()
							│	│ //将3A之类的命令注册一下,并给初始值
							│	├──>v4l2_ctrl_new_std(V4L2_CID_XXX)
							│	├──>v4l2_ctrl_new_std_menu(V4L2_CID_XXX)
							│	└──>v4l2_ctrl_new_custom(V4L2_CID_XXX)
							└──>vin_init_video()
								├──>cap->vdev.name // vin_video0
								├──>cap->vdev.fops = &vin_fops; // 文件接口的ioctl
								├──>cap->vdev.ioctl_ops = &vin_ioctl_ops; // ioctl的命令列表
								├──>video_register_device(cap->vdev)
								├──>video_set_drvdata()
								└──>vb2_queue_init()

2 使用するセンサー mlx75027 に固有の、固有の初期化プロセス

// drivers/media/platform/sunxi-vin/modules/sensor/mlx75027_mipi.c
module_init(init_sensor);static struct i2c_driver sensor_driver = {
    
    .probe = sensor_probe,...};
└──>init_sensor(void);
	└──>cci_dev_init_helper(&sensor_driver);
		└──>sensor_driver->probe()
			└──>sensor_probe()
				├──>sensor_init_controls(sensor_ctrl_ops);
				│	│  // 定义sensor对3A命令处理的逻辑,可覆盖父级别
				│	└──>v4l2_ctrl_new_std(V4L2_CID_XXX);			
				└──>cci_dev_probe_helper(sensor_ops); // 设置sensor_ops// 定义sensor对3A命令处理的逻辑,可覆盖父级别
					└──>struct v4l2_subdev_ops sensor_ops = {
    
    
						.core = &sensor_core_ops,
						...
						};
						└──>struct v4l2_subdev_core_ops sensor_core_ops = {
    
    
							.ioctl = sensor_ioctl, // 上层通过ioctl,最终会调用到这里,我实现的自定义命令,也会到这个处理
							...
							};

3. V4L2 cmd の種類

  1. 1 つ目は、 /include/uapi/linux/videodev2.h で定義されているVIDIOC_XXX
    コマンドの数が #define BASE_VIDIOC_PRIVATE 192 を超えないことです。v4l2 用に予約されているコマンドとして、カスタム コマンドをこの範囲内に追加することは推奨されません。
    これらのコマンドはデバイスの開閉、ストリーミング データの要求など、v4l2 操作の基本機能。
/*
 *	I O C T L   C O D E S   F O R   V I D E O   D E V I C E S
 *
 */
#define VIDIOC_QUERYCAP		_IOR('V',  0, struct v4l2_capability)
#define VIDIOC_RESERVED		_IO('V',  1)
#define VIDIOC_ENUM_FMT     _IOWR('V',  2, struct v4l2_fmtdesc)
#define VIDIOC_G_FMT		_IOWR('V',  4, struct v4l2_format)
#define VIDIOC_S_FMT		_IOWR('V',  5, struct v4l2_format)
#define VIDIOC_REQBUFS		_IOWR('V',  8, struct v4l2_requestbuffers)
#define VIDIOC_QUERYBUF		_IOWR('V',  9, struct v4l2_buffer)
#define VIDIOC_G_FBUF		_IOR('V', 10, struct v4l2_framebuffer)
#define VIDIOC_S_FBUF		_IOW('V', 11, struct v4l2_framebuffer)
#define VIDIOC_OVERLAY		_IOW('V', 14, int)
#define VIDIOC_QBUF			_IOWR('V', 15, struct v4l2_buffer)
#define VIDIOC_EXPBUF		_IOWR('V', 16, struct v4l2_exportbuffer)
#define VIDIOC_DQBUF		_IOWR('V', 17, struct v4l2_buffer)
#define VIDIOC_STREAMON		_IOW('V', 18, int)
#define VIDIOC_STREAMOFF	_IOW('V', 19, int)
  1. 2 つ目は/include/uapi/linux/v4l2-controls.h に定義されているV4L2_CID_XXX
    で、アプリケーション層で使用されるため、ここでアドレスが定義され、一定のサイズによって異なるアプリケーションが使用できる範囲が区別されます。 。
#define V4L2_CTRL_CLASS_USER	0x00980000	/* Old-style 'user' controls */
#define V4L2_CID_BASE			(V4L2_CTRL_CLASS_USER | 0x900)
#define V4L2_CID_USER_BASE 		V4L2_CID_BASE
#define V4L2_CID_USER_CLASS 	(V4L2_CTRL_CLASS_USER | 1)
#define V4L2_CID_BRIGHTNESS		(V4L2_CID_BASE+0)
#define V4L2_CID_CONTRAST		(V4L2_CID_BASE+1)
  1. 3 番目のタイプは、カメラごとに /include/media/sunxi_camera_v2.h で定義された cmd で、プライベート 192 の後に、センサーのプライベート データVIDIOC_ISP_XXXおよびVIDIOC_VIN_SENSOR_XXX
    を追加できることがわかります
#define BASE_VIDIOC_PRIVATE	192
#define VIDIOC_ISP_AE_STAT_REQ  _IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct isp_stat_buf)
#define VIDIOC_ISP_HIST_STAT_REQ _IOWR('V', BASE_VIDIOC_PRIVATE + 2, struct isp_stat_buf)
...
#define VIDIOC_VIN_SENSOR_CFG_REQ _IOWR('V', BASE_VIDIOC_PRIVATE + 60, struct sensor_config)
#define VIDIOC_VIN_SENSOR_EXP_GAIN _IOWR('V', BASE_VIDIOC_PRIVATE + 61, struct sensor_exp_gain)
#define VIDIOC_VIN_SENSOR_SET_FPS  _IOWR('V', BASE_VIDIOC_PRIVATE + 62, struct sensor_fps)
...
// 我新增的命令放在了最后
struct msg_i2c {
    
    
	unsigned short addr;
	unsigned short value;
};
#define VIDIOC_VIN_SET_I2C_DATA  _IOWR('V', BASE_VIDIOC_PRIVATE + 76, struct msg_i2c)

4. 各種コマンドの処理手順

1. VIDIOC_XXXの処理

	int sel = 0;
	int mode = 5;
	enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
	if (-1 == camera_init(sel, mode))
		return -1;
	if (-1 == camera_fmt_set(mode))
		return -1;
	if (-1 == req_frame_buffers())
		return -1;

	pixformat = TVD_PL_YUV420;
	disp_init(input_size.width, input_size.height, pixformat);
	if (-1 == ioctl(fd, VIDIOC_STREAMON, &type))
	{
    
    
		printf("VIDIOC_STREAMON failed\n");
		return -1;
	}

ioctl以降、カーネルからドライバーまでのプロセスは次のようになります。

// 在 第1节已经知道了  vin_init_video() 会注册 cap->vdev.fops = &vin_fops; 
// 所以对vin设备的ioctl都会到这个函数
static struct v4l2_file_operations vin_fops = {
    
    
	.unlocked_ioctl = video_ioctl2,
}
static struct v4l2_ioctl_info v4l2_ioctls[] = {
    
    
	IOCTL_INFO_FNC(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
	IOCTL_INFO_FNC(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
}

EXPORT_SYMBOL(video_ioctl2);
video_ioctl2()
└──>__video_do_ioctl()
	├──>v4l2_is_known_ioctl() // 如果是在已知cmd列表里面,就执行注册的func
	├──>struct v4l2_ioctl_info *info = &v4l2_ioctls[_IOC_NR(cmd)];
	└──>info->func(ops, file, fh, arg);
		└──>v4l_streamon()
			└──>ops->vidioc_streamon(file, fh, *(unsigned int *)arg);
				└──>static const struct v4l2_ioctl_ops vin_ioctl_ops = {
    
    
					.vidioc_streamon = vidioc_streamon,
					.vidioc_streamoff = vidioc_streamoff,
					}
					// /drivers/media/platform/sunxi-vin/vin-video/vin_video.c
					vidioc_streamon()
					└──>vb2_streamon()// /drivers/media/v4l2-core/videobuf2-core.c
						└──>vb2_core_streamon()
							├──>v4l_vb2q_enable_media_source()
							└──>vb2_start_streaming()// /drivers/media/platform/sunxi-vin/modules/sensor/mlx75027_mipi.c
								└──>struct v4l2_subdev_video_ops sensor_video_ops = {
    
    
									.s_stream = sensor_s_stream,
									}
									sensor_s_stream()
									└──>sensor_s_streamon(sd,1);
										└──>sensor_write(sd, 0x1001, 0x01); // 实际操作寄存器

2. V4L2_CID_XXXの処理

// drivers/media/platform/sunxi-vin/modules/sensor/mlx75027_mipi.c
module_init(init_sensor);static struct i2c_driver sensor_driver = {
    
    .probe = sensor_probe,...};
└──>init_sensor(void);
	└──>cci_dev_init_helper(&sensor_driver);
		└──>sensor_driver->probe()
			└──>sensor_probe()
				├──>sensor_init_controls(sensor_video_ops);
				│	│  // 定义sensor对3A命令处理的逻辑,可覆盖父级别
				│	└──>v4l2_ctrl_new_std(V4L2_CID_XXX);			
				└──>cci_dev_probe_helper(sensor_ops); // 设置sensor_ops// 定义sensor对3A命令处理的逻辑,可覆盖父级别
					└──>v4l2_subdev_video_ops sensor_video_ops = {
    
    
							.s_parm = sensor_s_parm,
							.g_parm = sensor_g_parm,
						...
						};
						└──>struct v4l2_ioctl_ops soc_camera_ioctl_ops = {
    
    
							.vidioc_g_parm		 = soc_camera_g_parm,
							}
							└──>soc_camera_g_parm()
								└──>default_g_parm()
									└──>vidioc_g_parm()
										└──>v4l2_subdev_call(vinc->vid_cap.pipe.sd[VIN_IND_SENSOR], video, g_parm, parms);
											└──>in_ctrl_ops = {
    
    
												.g_volatile_ctrl = vin_g_volatile_ctrl,
												};
												└──>vin_g_volatile_ctrl()
													// /drivers/media/platform/sunxi-vin/modules/sensor/mlx75027_mipi.c
													v4l2_ctrl_ops sensor_ctrl_ops = {
    
    
														.g_volatile_ctrl = sensor_g_ctrl,
													};
													└──>int sensor_g_ctrl(struct v4l2_ctrl *ctrl)
															switch (ctrl->id)
															case V4L2_CID_EXPOSURE:
																sensor_g_exp(); // 实际的处理函数

3. カスタム cmd 処理

 // /include/media/sunxi_camera_v2.h   在头文件增加新的cmd和传入的参数定义
struct msg_i2c {
    
    
	unsigned short addr;
	unsigned short value;
};
#define VIDIOC_VIN_SET_I2C_DATA	_IOWR('V', BASE_VIDIOC_PRIVATE + 76, struct msg_i2c)

// /drivers/media/platform/sunxi-vin/vin-video/vin_video.c
struct v4l2_ioctl_ops vin_ioctl_ops = {
    
    
	.vidioc_default = vin_param_handler, // 走的是这个函数
└──>vin_param_handler()
	└──>switch (cmd) {
    
    
		case VIDIOC_VIN_SET_I2C_DATA:
		vidioc_set_i2c_zhang(struct file *file, struct v4l2_fh *fh, struct msg_i2c *i2c);
		└──>struct vin_core *vinc = video_drvdata(file);
			v4l2_subdev_call(vinc->vid_cap.pipe.sd[VIN_IND_SENSOR], core, ioctl, VIDIOC_VIN_SET_I2C_DATA, i2c);// /drivers/media/platform/sunxi-vin/modules/sensor/mlx75027_mipi.c
			└──>struct v4l2_subdev_ops sensor_ops = {
    
    
				.core = &sensor_core_ops,
				};
				struct v4l2_subdev_core_ops sensor_core_ops = {
    
    
				.ioctl = sensor_ioctl,
				}
				└──>sensor_ioctl()
					└──>switch (cmd) {
    
    
						case VIDIOC_VIN_SET_I2C_DATA:
				        sensor_s_i2c_data(sd, (struct msg_i2c *)arg);
				        └──> sensor_write(sd, i2c->addr, i2c->value);

4. v4l2_subdev_call() これはカスタム cmd の実装の焦点です。

/*
 * Call an ops of a v4l2_subdev, doing the right checks against
 * NULL pointers.
 *
 * Example: err = v4l2_subdev_call(sd, video, s_std, norm);
 */
#define v4l2_subdev_call(sd, o, f, args...)				\
	(!(sd) ? -ENODEV : (((sd)->ops->o && (sd)->ops->o->f) ?	\
		(sd)->ops->o->f((sd), ##args) : -ENOIOCTLCMD))
vidioc_set_i2c_zhang(struct file *file, struct v4l2_fh *fh, struct msg_i2c *i2c);		
└──>v4l2_subdev_call(vinc->vid_cap.pipe.sd[VIN_IND_SENSOR], core, ioctl, VIDIOC_VIN_SET_I2C_DATA, i2c);

struct v4l2_subdev_ops sensor_ops = {
    
    
	.core = &sensor_core_ops,
	};
struct v4l2_subdev_core_ops sensor_core_ops = {
    
    
	.ioctl = sensor_ioctl,
	}

v4l2_subdev_call(vinc->vid_cap.pipe.sd[VIN_IND_SENSOR]、コア、ioctl、VIDIOC_VIN_SET_I2C_DATA、i2c);
実際には以下と同等です
v4l2_subdev_call(サブデバイス, sensor_ops , ioctl, VIDIOC_VIN_SET_I2C_DATA, struct msg_i2c);
変換は、サブデバイスを介して sensor_ops の ioctl メソッドを呼び出すことです。渡されるパラメータは VIDIOC_VIN_SET_I2C_DATA で、メソッドの構造は struct msg_i2c です。

5. 発生した問題

1. is_isp_used と is_bayer_raw の値は、V4L2_CID_XXX の処理ロジックに影響します。

static int vin_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
    
    
	struct vin_vid_cap *cap = container_of(ctrl->handler, struct vin_vid_cap, ctrl_handler);
	struct sensor_instance *inst = get_valid_sensor(cap->vinc);
	if (inst->is_isp_used && inst->is_bayer_raw) {
    
    
	// 一般而言,V4L2_CID_XXX 会走这个分支
	} else {
    
    
	}
// 	/drivers/media/platform/sunxi-vin/vin.c
static int __vin_handle_sensor_info(struct sensor_instance *inst)
{
    
    
	if (inst->cam_type == SENSOR_RAW) {
    
    
		inst->is_bayer_raw = 1;
		inst->is_isp_used = 1;
	} else if (inst->cam_type == SENSOR_YUV) {
    
    
		inst->is_bayer_raw = 0;
		inst->is_isp_used = 0;
	} else {
    
    
		inst->is_bayer_raw = 0;
		inst->is_isp_used = 0;
	}
	return 0;
}
// 从这个函数可以看到,只有sensor的type是SENSOR_RAW的时候,这2个属性才都是1.
// 所以就需要在初始化列表里面将sensor的属性配置好,如果是新增的sensor,这2个值都是0.
// /drivers/media/platform/sunxi-vfe/utility/sensor_info.c
struct sensor_item sensor_list_t[] = {
    
    
	/* name       i2c_addr      sensor type  sensor size   sensor max pclk */
	{
    
    	"ov2640",	0x60,		SENSOR_YUV,	 PIXEL_NUM_2M, CORE_CLK_RATE_FOR_2M},
	{
    
    	"ov5647_mipi",	0x6c,	SENSOR_RAW,	 PIXEL_NUM_5M, CORE_CLK_RATE_FOR_5M},
	}
// 可以把新增的sensor添加在这个结构体的后面.	

2. Linuxカーネルマクロcontainer_ofの意味

Linuxカーネルマクロcontainer_ofの解析
Linuxcontainer_ofの使用法について Linuxドライバ
のcontainer_ofの使い方と実装Linuxカーネルのビット操作の詳細な解説
container_ofマクロ解析

struct struct *struct_p= container_of(struct_member_p, struct struct, struct_member);
//container_of 的作用是:已知 struct结构中某个成员struct_member的指针struct_member_p,就可以知道整个struct的指针struct_p

6. Allwinner は、V4L2 デバイスが I2C を直接読み書きする方法を提供します。

API をカプセル化した後、Quanzhi の公式 Web サイトでこの方法しか見たことがありませんでした...今から共有します
[FAQ440] Tina オンライン読み書きセンサー レジスタの例
公開日 2021-10-30 15:47:18
プラットフォーム V536 V533 V833 V831 T507 も対応しているかは分かりませんが、実際に見てみると、センサーディレクトリ内のファイルはすべて一致しています。

[コマンド例]
1) cd /sys/devices/gc2053_mipi (ターゲットセンサーノードのディレクトリを入力)
2) echo 16 > addr_width; echo 8 > data_width (ターゲットセンサーのレジスタアドレス/データビット幅を入力、データシートを参照してください)取得)
3) echo 0 > read_flag (read_flag: 読み取りおよび書き込み制御ノード、これを 1 に有効にすると後続の操作が読み取りアクションであることを意味し、0 に有効にすると後続の操作が書き込みアクションであることを意味します) 4) echo 30350021
> cci_client ("30350021": 0x3035 [ターゲット レジスタ アドレス] 、0x0021 [書き込まれるレジスタ値]、read_flag = 1 の場合、書き込まれた値は無効です) 5) cat read_value (前のステップの結果を出力します
:レジスタ値)

[注意事項]
1) IIC はシステムおよびハードウェア上で正常に動作します;
2) 上記のコマンドが有効になるには、/sys/devices/sensor ノードが存在するかどうか、カメラの通常の動作状態で実行する必要があります。カメラの電源が入っているなど;
3 ) 上記のコマンドは tina-linux システムでのみ有効であり、rtos システムではまだサポートされていません。

良い写真がいくつかあったので、それもシェアします

ここに画像の説明を挿入します
ここに画像の説明を挿入します
ここに画像の説明を挿入します

おすすめ

転載: blog.csdn.net/scarlettsp/article/details/121208936