rk3568制冷项目驱动开发流程汇总(只适用于部分模块CIF DVP等,自用)

采用fpga输入,3568采集并显示至hdmi
RKVICAP 驱动框架说明
RKVICAP驱动主要是基于 v4l2 / media 框架实现硬件的配置、中断处理、控制 buffer 轮转,以及控制 subdevice(如 mipi dphy 及 sensor) 的上下电等功能。
对于RK356X 芯片而言, VICAP 只有单核,同时拥有 dvp/mipi 两种接口, dvp 接口对应一个 rkvicap_dvp 节点,mipi 接口对应一个 rkvicap_mipi_lvds 节点(与 RV1126/RV1109 的 VICAP FULL 同名),各节点可独立采集。
为了将VICAP 采集数据信息同步给 isp 驱动,需要将 VICAP 驱动生成的逻辑 sditf 节点链接到 isp 所生成的虚拟节点设备。DVP 接口对应 rkvicap_dvp_sditf 节点, VICAP FULL 的 mipi/lvds 接口对应 rkvicap_mipi_lvds_sditf节点, VICAP LITE 对应 rkvicap_lite_sditf 。

1.DVP camera

RK3568有一个DVP接口,支持BT601/BT656/BT1120等,同样的,如果是RAW的sensor,需要配置到ISP,如果是YUV的,则不需经过ISP,关键配置如下

(1)在sensor驱动的g_mbus_config接口中,通过flag指明当前sensor的hsync-acitve/vsyncactive/pclk-ative的有效极性,否则会导致无法收到数据;

python 复制代码
static int avafpga_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id,
				struct v4l2_mbus_config *config)
{
	config->type = V4L2_MBUS_BT656;
	config->flags = V4L2_MBUS_HSYNC_ACTIVE_HIGH |
			        V4L2_MBUS_VSYNC_ACTIVE_HIGH |
			        V4L2_MBUS_PCLK_SAMPLE_RISING;
	return 0;
}

(2)设备树的节点中hsync-active/vsync-active 不要配置,否则 v4l2 框架异步注册时会识别为 BT601;
(3)pclk-sample/bus-width 可选;

python 复制代码
bus-width = <16>;
pclk-sample = <1>;

(4)必须实现v4l2_subdev_video_ops中的querystd接口,指明当前接口为ATSC接口,否则会导致无法收到数据;

python 复制代码
static int avafpga_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
{
	*std = V4L2_STD_ATSC;
	return 0;
}

(5)必须实现RKMODULE_GET_BT656_MBUS_INFO,BT656/BT1120都是调用这个ioctl,接口兼容,实现参考drivers/media/i2c/nvp6158_drv/nvp6158_v4l2.c

cpp 复制代码
static __maybe_unused void
avafpga_get_bt656_module_inf(struct avafpga *avafpga,
			       struct rkmodule_bt656_mbus_info *inf)
{
	memset(inf, 0, sizeof(*inf));
	inf->flags = RKMODULE_CAMERA_BT656_PARSE_ID_LSB;
	switch (avafpga->ch_nums) {
	case 1:
		inf->flags |= RKMODULE_CAMERA_BT656_CHANNEL_0;
		break;
	case 2:
		inf->flags |= RKMODULE_CAMERA_BT656_CHANNEL_0 |
			      RKMODULE_CAMERA_BT656_CHANNEL_1;
		break;
	case 4:
		inf->flags |= RKMODULE_CAMERA_BT656_CHANNELS;
		break;
	default:
		inf->flags |= RKMODULE_CAMERA_BT656_CHANNELS;
	}
}

static long avafpga_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
	long ret = 0;
	switch (cmd) {

	default:
		ret = -ENOTTY;
		break;
	}

	return ret;
}

#ifdef CONFIG_COMPAT
static long avafpga_compat_ioctl32(struct v4l2_subdev *sd,
				   unsigned int cmd, unsigned long arg)
{
	long ret = 0;
	//struct rkmodule_bt656_mbus_info *bt565_inf;
	switch (cmd) {
		
	default:
		ret = -ENOIOCTLCMD;
		break;
	}

	return ret;
}
#endif

#define DST_XPOS 0
#define DST_YPOS 0
#define DST_WIDTH 1536 //1920
#define DST_HEIGHT 576 //1080

static int avafpga_get_selection(struct v4l2_subdev *sd,
				struct v4l2_subdev_pad_config *cfg,
				struct v4l2_subdev_selection *sel)
{
	if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) {
		sel->r.left = DST_XPOS;
		sel->r.top = DST_YPOS;
		sel->r.width = DST_WIDTH;
		sel->r.height = DST_HEIGHT;
		return 0;
	}
	return -EINVAL;
}

static int avafpga_initialize_controls(struct avafpga *avafpga)
{
	int ret;
	struct v4l2_ctrl_handler *handler;
	const struct avafpga_mode *mode;
	u32 h_blank, v_blank;

	handler = &avafpga->ctrl_handler;
	mode = avafpga->cur_mode;

	ret = v4l2_ctrl_handler_init(handler, 2);
	if (ret)
		return ret;
	handler->lock = &avafpga->mutex;

	h_blank = mode->hts_def - mode->width;
	avafpga->hblank_ctrl = v4l2_ctrl_new_std(handler, NULL,
				V4L2_CID_HBLANK, h_blank, h_blank, 1, h_blank);

	v_blank = mode->vts_def - mode->height;
	avafpga->vblank_ctrl = v4l2_ctrl_new_std(handler, NULL,
				V4L2_CID_VBLANK, v_blank, v_blank, 1, v_blank);

	if (handler->error) {
		ret = handler->error;
		dev_err(&avafpga->client->dev,
			"Failed to init controls(%d)\n", ret);
		goto err_free_handler;
	}

	avafpga->subdev.ctrl_handler = handler;

	return 0;

err_free_handler:
	v4l2_ctrl_handler_free(handler);

	return ret;
}

static const struct v4l2_subdev_core_ops avafpga_core_ops = {
	.s_power = avafpga_s_power,
	.ioctl = avafpga_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl32 = avafpga_compat_ioctl32,
#endif
};

(6)pinctrl需要引用对,以对bt656/bt1120相关gpio做相应iomux,否则会导致无法收到数据。
dts 配置如下:

python 复制代码
&i2c0 {
    status =  "okay";

    sensor@3c{
        status =  "okay";
        compatible = "firefly,pc9202";
        reg = <0x3c>;
        wd-en-gpio = <&gpio3 23 GPIO_ACTIVE_HIGH>;
    };

	
	avafpga: avafpga@32 {
		status = "okay";
		compatible = "ava,fpga";
		reg = <0x32>;
		clocks = <&cru CLK_CIF_OUT>;
		clock-names = "xvclk";

		power-domains = <&power RK3568_PD_VI>;
		pwdn-gpios = <&gpio4 RK_PB4 GPIO_ACTIVE_HIGH>;
		reset-gpios = <&gpio3 RK_PB6 GPIO_ACTIVE_HIGH>;
		rockchip,grf = <&grf>;
		pinctrl-names = "default";
		pinctrl-0 = <&cif_clk&cif_dvp_clk&cif_dvp_bus16&cif_dvp_bus8>;
		rockchip,camera-module-index = <0>;
		rockchip,camera-module-facing = "back";
		rockchip,camera-module-name = "default";
		rockchip,camera-module-lens-name = "default";
		rockchip,dvp_mode = "BT1120"; //BT656 or BT1120 or BT656_TEST
		port {
			cam_para_out2: endpoint {
				remote-endpoint = <&dvp_in_bcam>;
				bus-width = <16>;
				pclk-sample = <1>;
			};
		};
	};
};

&rkcif {
        status = "okay";
};

&rkcif_mmu {
        status = "okay";
};

&rkcif_dvp {
        status = "okay";
		port {
		dvp_in_bcam: endpoint {
		remote-endpoint = <&cam_para_out2>;
		bus-width = <16>;
		};
		};
};

(7)/rk356x_sdk-linux5.10/kernel/drivers/media/i2c目录下增加fpga.c,并在kconfig和makefile中增加相关模块的设置

python 复制代码
//kconfig
config VIDEO_FPGA
	tristate "AVA FPGA sensor support"
	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
	depends on MEDIA_CAMERA_SUPPORT
	help
	  This is a Video4Linux2 sensor driver for the AVA
	  FPGA camera.

	  To compile this driver as a module, choose M here: the
	  module will be called fpga.
python 复制代码
//makefile
obj-y += fpga.o

完整的fpga的c代码如下,亦可见附件(开始未设置ioctl,上电v4l2抓图时,打印_avafpga_start_stream enter然后崩掉,后经查阅资料以及参考rv1126的内容,加入ioctl和vblank的ctrl对应部分,可以成功获取数据,且不会崩掉)

python 复制代码
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/sysfs.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <linux/rk-camera-module.h>
#include <media/media-entity.h>
#include <media/v4l2-async.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-subdev.h>
#include <linux/pinctrl/consumer.h>
#include <linux/rk-preisp.h>

#include <media/v4l2-fwnode.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_graph.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/mfd/syscon.h>
#include <linux/version.h>


#define DRIVER_VERSION			KERNEL_VERSION(0, 0x0, 0x01)
//#define PIX_FORMAT			MEDIA_BUS_FMT_UYVY8_2X8
#define PIX_FORMAT                    MEDIA_BUS_FMT_YUYV8_2X8
#define AVAFPGA_NAME			"avafpga"

struct regval {
	u16 addr;
	u8 val;
};

struct avafpga_mode {
	u32 width;
	u32 height;
	struct v4l2_fract max_fps;
	u32 hts_def;
	u32 vts_def;
	u32 exp_def;
	const struct regval *reg_list;
};

struct avafpga {
	struct i2c_client	*client;
	struct clk		*xvclk;
	struct gpio_desc	*reset_gpio;
	struct gpio_desc	*pwdn_gpio;

	struct v4l2_subdev	subdev;
	struct media_pad	pad;
	struct v4l2_ctrl_handler ctrl_handler;
	struct v4l2_ctrl	*exposure;
	struct v4l2_ctrl	*anal_gain;
	struct v4l2_ctrl	*digi_gain;
	struct v4l2_ctrl	*hblank;
	struct v4l2_ctrl	*vblank;
	struct v4l2_ctrl	*test_pattern;
	struct mutex		mutex;
	bool			streaming;
	bool			power_on;
	const struct avafpga_mode *cur_mode;
	u32			module_index;
	const char		*module_facing;
	const char		*module_name;
	const char		*len_name;
	u32			ch_nums;
	//hjk
	struct v4l2_ctrl	*vblank_ctrl;
	struct v4l2_ctrl	*hblank_ctrl;

	u8 is_reset;
};

#define to_avafpga(sd) container_of(sd, struct avafpga, subdev)

static const struct avafpga_mode supported_modes[] = {
	{
		//.width = 1920,
		//.height = 1080,
		.width = 1536,
		.height = 576,		
		.max_fps = {
			.numerator = 10000,
			.denominator = 600000,
		},
		.exp_def = 0x0100,
		.hts_def = 0x044c * 2,
		.vts_def = 0x0465,
	}
};

static int __avafpga_start_stream(struct avafpga *avafpga)
{
	printk(KERN_ERR "%s enter\n", __func__);
	avafpga->is_reset = 1;//hjk1126
	printk(KERN_ERR "%s exit\n", __func__);//hjk1126

	return 0;
}

static int __avafpga_stop_stream(struct avafpga *avafpga)
{
	printk(KERN_ERR "%s enter\n", __func__);
	return 0;
}

static int avafpga_s_stream(struct v4l2_subdev *sd, int on)
{
	struct avafpga *avafpga = to_avafpga(sd);
	struct i2c_client *client = avafpga->client;
	int ret = 0;

	mutex_lock(&avafpga->mutex);
	on = !!on;
	if (on == avafpga->streaming)
		goto unlock_and_return;

	if (on) {
		ret = pm_runtime_get_sync(&client->dev);
		if (ret < 0) {
			pm_runtime_put_noidle(&client->dev);
			goto unlock_and_return;
		}

		ret = __avafpga_start_stream(avafpga);
		if (ret) {
			v4l2_err(sd, "start stream failed while write regs\n");
			pm_runtime_put(&client->dev);
			goto unlock_and_return;
		}
	} else {
		__avafpga_stop_stream(avafpga);
		pm_runtime_put(&client->dev);
	}

	avafpga->streaming = on;
unlock_and_return:
	mutex_unlock(&avafpga->mutex);

	return ret;
}


#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
static int avafpga_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
	struct avafpga *avafpga = to_avafpga(sd);
	struct v4l2_mbus_framefmt *try_fmt =
				v4l2_subdev_get_try_format(sd, fh->pad, 0);
	const struct avafpga_mode *def_mode = &supported_modes[0];

	mutex_lock(&avafpga->mutex);
	/* Initialize try_fmt */
	try_fmt->width = def_mode->width;
	try_fmt->height = def_mode->height;
	try_fmt->code = PIX_FORMAT;
	try_fmt->field = V4L2_FIELD_NONE;
	mutex_unlock(&avafpga->mutex);
	/* No crop or compose */

	return 0;
}
#endif

static int avafpga_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id,
				struct v4l2_mbus_config *config)
{
	//config->type = V4L2_MBUS_BT656;
	//config->flags = V4L2_MBUS_PCLK_SAMPLE_RISING;

	config->type = V4L2_MBUS_BT656;
	config->flags = V4L2_MBUS_PCLK_SAMPLE_RISING;
	//config->flags = V4L2_MBUS_HSYNC_ACTIVE_LOW |
 			//V4L2_MBUS_VSYNC_ACTIVE_LOW |
			//V4L2_MBUS_PCLK_SAMPLE_FALLING;
	//config->type = V4L2_MBUS_BT656;
	//config->flags = RKMODULE_CAMERA_BT656_CHANNELS |
				//V4L2_MBUS_PCLK_SAMPLE_RISING;

	//config->type = V4L2_MBUS_BT656;
	config->flags = V4L2_MBUS_HSYNC_ACTIVE_HIGH |
			V4L2_MBUS_VSYNC_ACTIVE_HIGH |
			V4L2_MBUS_PCLK_SAMPLE_RISING;
	return 0;

}

static int avafpga_g_frame_interval(struct v4l2_subdev *sd,
				   struct v4l2_subdev_frame_interval *fi)
{
	struct avafpga *avafpga = to_avafpga(sd);
	const struct avafpga_mode *mode = avafpga->cur_mode;

	mutex_lock(&avafpga->mutex);
	fi->interval = mode->max_fps;
	mutex_unlock(&avafpga->mutex);

	return 0;
}

static int avafpga_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
{
	*std = V4L2_STD_ATSC;
	return 0;
}


static int avafpga_enum_frame_interval(struct v4l2_subdev *sd,
				       struct v4l2_subdev_pad_config *cfg,
				       struct v4l2_subdev_frame_interval_enum *fie)
{
	if (fie->index >= ARRAY_SIZE(supported_modes))
		return -EINVAL;

	//if (fie->code != PIX_FORMAT)
		//return -EINVAL;

	fie->width = supported_modes[fie->index].width;
	fie->height = supported_modes[fie->index].height;
	fie->interval = supported_modes[fie->index].max_fps;
	return 0;
}

static int avafpga_enum_mbus_code(struct v4l2_subdev *sd,
				 struct v4l2_subdev_pad_config *cfg,
				 struct v4l2_subdev_mbus_code_enum *code)
{
	if (code->index != 0)
		return -EINVAL;
	code->code = PIX_FORMAT;

	return 0;
}

static int avafpga_enum_frame_sizes(struct v4l2_subdev *sd,
				   struct v4l2_subdev_pad_config *cfg,
				   struct v4l2_subdev_frame_size_enum *fse)
{
	if (fse->index >= ARRAY_SIZE(supported_modes))
		return -EINVAL;

	//if (fse->code != PIX_FORMAT)//hjk1126中没有这个
		//return -EINVAL;//hjk1126中没有这个

	fse->min_width  = supported_modes[fse->index].width;
	fse->max_width  = supported_modes[fse->index].width;
	fse->max_height = supported_modes[fse->index].height;
	fse->min_height = supported_modes[fse->index].height;

	return 0;
}

static int avafpga_set_fmt(struct v4l2_subdev *sd,
			  struct v4l2_subdev_pad_config *cfg,
			  struct v4l2_subdev_format *fmt)
{
	struct avafpga *avafpga = to_avafpga(sd);
	const struct avafpga_mode *mode;

	mutex_lock(&avafpga->mutex);

	mode = avafpga->cur_mode;
	fmt->format.code = PIX_FORMAT;
	fmt->format.width = mode->width;
	fmt->format.height = mode->height;
	fmt->format.field = V4L2_FIELD_NONE;
	//fmt->format.field = V4L2_FIELD_INTERLACED;
	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
		*v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format;
#else
		mutex_unlock(&avafpga->mutex);
		return -ENOTTY;
#endif
	} else {
		avafpga->cur_mode = mode;
	}

	mutex_unlock(&avafpga->mutex);

	return 0;
}

static int avafpga_get_fmt(struct v4l2_subdev *sd,
			  struct v4l2_subdev_pad_config *cfg,
			  struct v4l2_subdev_format *fmt)
{
	struct avafpga *avafpga = to_avafpga(sd);
	const struct avafpga_mode *mode = avafpga->cur_mode;

	mutex_lock(&avafpga->mutex);
	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
		fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
#else
		mutex_unlock(&avafpga->mutex);
		return -ENOTTY;
#endif
	} else {
		fmt->format.width = mode->width;
		fmt->format.height = mode->height;
		fmt->format.code = PIX_FORMAT;
		fmt->format.field = V4L2_FIELD_NONE;
	}
	mutex_unlock(&avafpga->mutex);

	return 0;
}

static int avafpga_s_power(struct v4l2_subdev *sd, int on)
{
	struct avafpga *avafpga = to_avafpga(sd);
	struct i2c_client *client = avafpga->client;
	int ret = 0;

	mutex_lock(&avafpga->mutex);

	/* If the power state is not modified - no work to do. */
	if (avafpga->power_on == !!on)
		goto unlock_and_return;

	if (on) {
		ret = pm_runtime_get_sync(&client->dev);
		if (ret < 0) {
			pm_runtime_put_noidle(&client->dev);
			goto unlock_and_return;
		}

		avafpga->power_on = true;
	} else {
		pm_runtime_put(&client->dev);
		avafpga->power_on = false;
	}

unlock_and_return:
	mutex_unlock(&avafpga->mutex);

	return ret;
}

static int __avafpga_power_on(struct avafpga *avafpga)
{
	return 0;
}

static void __avafpga_power_off(struct avafpga *avafpga)
{
	return;
}

static int avafpga_runtime_resume(struct device *dev)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct v4l2_subdev *sd = i2c_get_clientdata(client);
	struct avafpga *avafpga = to_avafpga(sd);

	return __avafpga_power_on(avafpga);
}

static int avafpga_runtime_suspend(struct device *dev)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct v4l2_subdev *sd = i2c_get_clientdata(client);
	struct avafpga *avafpga = to_avafpga(sd);

	__avafpga_power_off(avafpga);

	return 0;
}

hjk///
static __maybe_unused void
avafpga_get_bt656_module_inf(struct avafpga *avafpga,
			       struct rkmodule_bt656_mbus_info *inf)
{
	memset(inf, 0, sizeof(*inf));
	inf->flags = RKMODULE_CAMERA_BT656_PARSE_ID_LSB;
	switch (avafpga->ch_nums) {
	case 1:
		inf->flags |= RKMODULE_CAMERA_BT656_CHANNEL_0;
		break;
	case 2:
		inf->flags |= RKMODULE_CAMERA_BT656_CHANNEL_0 |
			      RKMODULE_CAMERA_BT656_CHANNEL_1;
		break;
	case 4:
		inf->flags |= RKMODULE_CAMERA_BT656_CHANNELS;
		break;
	default:
		inf->flags |= RKMODULE_CAMERA_BT656_CHANNELS;
	}
}

static long avafpga_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
	long ret = 0;
	//struct avafpga *avafpga = to_avafpga(sd);
	switch (cmd) {
		/*case RKMODULE_GET_BT656_MBUS_INFO:
		avafpga_get_bt656_module_inf(avafpga,
					       (struct rkmodule_bt656_mbus_info
						*)arg);
		break;*/

	default:
		ret = -ENOTTY;
		break;
	}

	return ret;
}

#ifdef CONFIG_COMPAT
static long avafpga_compat_ioctl32(struct v4l2_subdev *sd,
				   unsigned int cmd, unsigned long arg)
{
	//void __user *up = compat_ptr(arg);
	//struct rkmodule_inf *inf;
	//struct rkmodule_awb_cfg *cfg;
	//int *seq;
	long ret = 0;
	//struct rkmodule_bt656_mbus_info *bt565_inf;
	switch (cmd) {
		/*case RKMODULE_GET_BT656_MBUS_INFO:
		bt565_inf = kzalloc(sizeof(*bt565_inf), GFP_KERNEL);
		if (!bt565_inf) {
			ret = -ENOMEM;
			return ret;
		}

		ret = avafpga_ioctl(sd, cmd, bt565_inf);
		if (!ret) {
			ret = copy_to_user(up, bt565_inf, sizeof(*bt565_inf));
			if (ret)
				ret = -EFAULT;
		}
		kfree(bt565_inf);
		break;*/
	default:
		ret = -ENOIOCTLCMD;
		break;
	}

	return ret;
}
#endif

#define DST_XPOS 0
#define DST_YPOS 0
#define DST_WIDTH 1536 //1920
#define DST_HEIGHT 576 //1080

static int avafpga_get_selection(struct v4l2_subdev *sd,
				struct v4l2_subdev_pad_config *cfg,
				struct v4l2_subdev_selection *sel)
{
	if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) {
		sel->r.left = DST_XPOS;
		sel->r.top = DST_YPOS;
		sel->r.width = DST_WIDTH;
		sel->r.height = DST_HEIGHT;
		return 0;
	}
	return -EINVAL;
}

static int avafpga_initialize_controls(struct avafpga *avafpga)
{
	int ret;
	struct v4l2_ctrl_handler *handler;
	const struct avafpga_mode *mode;
	u32 h_blank, v_blank;

	handler = &avafpga->ctrl_handler;
	mode = avafpga->cur_mode;

	ret = v4l2_ctrl_handler_init(handler, 2);
	if (ret)
		return ret;
	handler->lock = &avafpga->mutex;

	h_blank = mode->hts_def - mode->width;
	avafpga->hblank_ctrl = v4l2_ctrl_new_std(handler, NULL,
				V4L2_CID_HBLANK, h_blank, h_blank, 1, h_blank);

	v_blank = mode->vts_def - mode->height;
	avafpga->vblank_ctrl = v4l2_ctrl_new_std(handler, NULL,
				V4L2_CID_VBLANK, v_blank, v_blank, 1, v_blank);

	if (handler->error) {
		ret = handler->error;
		dev_err(&avafpga->client->dev,
			"Failed to init controls(%d)\n", ret);
		goto err_free_handler;
	}

	avafpga->subdev.ctrl_handler = handler;

	return 0;

err_free_handler:
	v4l2_ctrl_handler_free(handler);

	return ret;
}
hjk//

static const struct dev_pm_ops avafpga_pm_ops = {
	SET_RUNTIME_PM_OPS(avafpga_runtime_suspend,
			   avafpga_runtime_resume, NULL)
};

#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
static const struct v4l2_subdev_internal_ops avafpga_internal_ops = {
	.open = avafpga_open,
};
#endif

static const struct v4l2_subdev_core_ops avafpga_core_ops = {
	.s_power = avafpga_s_power,
	//hjk
	.ioctl = avafpga_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl32 = avafpga_compat_ioctl32,
#endif
    //hjk
};

static const struct v4l2_subdev_video_ops avafpga_video_ops = {
	.s_stream = avafpga_s_stream,
	.g_frame_interval = avafpga_g_frame_interval,
	.querystd = avafpga_querystd,
};

static const struct v4l2_subdev_pad_ops avafpga_pad_ops = {
	.enum_mbus_code = avafpga_enum_mbus_code,
	.enum_frame_size = avafpga_enum_frame_sizes,
	.enum_frame_interval = avafpga_enum_frame_interval,
	.get_fmt = avafpga_get_fmt,
	.set_fmt = avafpga_set_fmt,
	.get_selection = avafpga_get_selection,
	.get_mbus_config = avafpga_g_mbus_config,
};

static const struct v4l2_subdev_ops avafpga_subdev_ops = {
	.core	= &avafpga_core_ops,
	.video	= &avafpga_video_ops,
	.pad	= &avafpga_pad_ops,
};


static int avafpga_probe(struct i2c_client *client,
			const struct i2c_device_id *id)
{
	struct device *dev = &client->dev;
	struct avafpga *avafpga;
	struct v4l2_subdev *sd;
	char facing[2];
	int ret;

	dev_info(dev, "driver version: %02x.%02x.%02x",
		DRIVER_VERSION >> 16,
		(DRIVER_VERSION & 0xff00) >> 8,
		DRIVER_VERSION & 0x00ff);

	avafpga = devm_kzalloc(dev, sizeof(*avafpga), GFP_KERNEL);
	if (!avafpga)
		return -ENOMEM;

	avafpga->client = client;
	avafpga->cur_mode = &supported_modes[0];

	avafpga->xvclk = devm_clk_get(dev, "xvclk");
	if (IS_ERR(avafpga->xvclk)) {
		dev_err(dev, "Failed to get xvclk\n");
		return -EINVAL;
	}
	//avafpga->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
	//if (IS_ERR(avafpga->reset_gpio))
		//dev_warn(dev, "Failed to get reset-gpios\n");
	//avafpga->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW);
	//if (IS_ERR(avafpga->pwdn_gpio))
		//dev_warn(dev, "Failed to get pwdn-gpios\n");

	mutex_init(&avafpga->mutex);

	sd = &avafpga->subdev;
	v4l2_i2c_subdev_init(sd, client, &avafpga_subdev_ops);

	//hjk
	ret = avafpga_initialize_controls(avafpga);
	if (ret) {
		dev_err(dev, "Failed to initialize controls fpga\n");
		goto err_free_handler;
	}
	//hjk

	ret = __avafpga_power_on(avafpga);
	if (ret)
		goto err_free_handler;

#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
	sd->internal_ops = &avafpga_internal_ops;
	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
		     V4L2_SUBDEV_FL_HAS_EVENTS;
#endif

#if defined(CONFIG_MEDIA_CONTROLLER)
	avafpga->pad.flags = MEDIA_PAD_FL_SOURCE;
	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
	ret = media_entity_pads_init(&sd->entity, 1, &avafpga->pad);
	if (ret < 0)
		goto err_power_off;
#endif

	memset(facing, 0, sizeof(facing));

	if (!sd->dev)
		dev_err(dev, "sd dev is null\n");

	snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
		 avafpga->module_index, facing,
		 AVAFPGA_NAME, dev_name(sd->dev));

	ret = v4l2_async_register_subdev_sensor_common(sd);
	if (ret) {
		dev_err(dev, "v4l2 async register subdev failed\n");
		goto err_clean_entity;
	}

	pm_runtime_set_active(dev);
	pm_runtime_enable(dev);
	pm_runtime_idle(dev);

	dev_err(dev, "v4l2 async register subdev sucessfully\n");

	return 0;

err_clean_entity:
#if defined(CONFIG_MEDIA_CONTROLLER)
	media_entity_cleanup(&sd->entity);
#endif
err_power_off:
	__avafpga_power_off(avafpga);
err_free_handler:
	v4l2_ctrl_handler_free(&avafpga->ctrl_handler);

	mutex_destroy(&avafpga->mutex);

	return ret;
}

static int avafpga_remove(struct i2c_client *client)
{
	struct v4l2_subdev *sd = i2c_get_clientdata(client);
	struct avafpga *avafpga = to_avafpga(sd);

	v4l2_async_unregister_subdev(sd);
#if defined(CONFIG_MEDIA_CONTROLLER)
	media_entity_cleanup(&sd->entity);
#endif
	v4l2_ctrl_handler_free(&avafpga->ctrl_handler);
	mutex_destroy(&avafpga->mutex);

	pm_runtime_disable(&client->dev);
	if (!pm_runtime_status_suspended(&client->dev))
		__avafpga_power_off(avafpga);
	pm_runtime_set_suspended(&client->dev);

	return 0;
}

#if IS_ENABLED(CONFIG_OF)
static const struct of_device_id avafpga_of_match[] = {
	{ .compatible = "ava,fpga" },
	{},
};
MODULE_DEVICE_TABLE(of, avafpga_of_match);
#endif

static const struct i2c_device_id avafpga_match_id[] = {
	{"ava,fpga", 0 },
	{},
};

static struct i2c_driver avafpga_i2c_driver = {
	.driver = {
		.name = AVAFPGA_NAME,
		.of_match_table = of_match_ptr(avafpga_of_match),
	},
	.probe		= &avafpga_probe,
	.remove		= &avafpga_remove,
	.id_table	= avafpga_match_id,
};

static int __init sensor_mod_init(void)
{
	return i2c_add_driver(&avafpga_i2c_driver);
}

static void __exit sensor_mod_exit(void)
{
	i2c_del_driver(&avafpga_i2c_driver);
}

device_initcall_sync(sensor_mod_init);
module_exit(sensor_mod_exit);

MODULE_DESCRIPTION("AVA FPGA sensor driver");
MODULE_LICENSE("GPL v2");

编译后会在路径下生成fpga.o,加载进内核模块

具体的调试结果如下

相关推荐
cxr8283 天前
SPARC方法论在Claude Code基于规则驱动开发中的应用
人工智能·驱动开发·claude·智能体
sukalot3 天前
window显示驱动开发—显示适配器的子设备
驱动开发
Evan_ZGYF丶4 天前
【RK3576】【Android14】如何在Android14下单独编译kernel-6.1?
linux·驱动开发·android14·rk3576
sukalot5 天前
window显示驱动开发—视频呈现网络简介
驱动开发
sukalot5 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(二)
驱动开发
zwhSunday5 天前
Linux驱动开发(1)概念、环境与代码框架
linux·运维·驱动开发
sukalot6 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(三)
驱动开发
sukalot6 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(一)
驱动开发
cxr8287 天前
基于Claude Code的 规范驱动开发(SDD)指南
人工智能·hive·驱动开发·敏捷流程·智能体
zwhSunday7 天前
Linux驱动开发(2)进一步理解驱动
linux·驱动开发