HDMI 调试之输出RGB888

(仅作记录,rk3588平台)

        RGB888这种数据格式可以被csi或dsi接收,驱动代码里默认是被dsi接收,但我们经常会遇到hdmi输出rgb888到mipi csi,所以需要在驱动中增加对csi接口的支持。

        首先了解下回调函数 v4l2_subdev_call,参数介绍以及定义如下:

/**
 * v4l2_subdev_call - call an operation of a v4l2_subdev.
 *
 * @sd: pointer to the &struct v4l2_subdev
 * @o: name of the element at &struct v4l2_subdev_ops that contains @f.
 *     Each element there groups a set of callbacks functions.
 * @f: callback function to be called.
 *     The callback functions are defined in groups, according to
 *     each element at &struct v4l2_subdev_ops.
 * @args: arguments for @f.
 *
 * Example: err = v4l2_subdev_call(sd, video, s_std, norm);
 */
#define v4l2_subdev_call(sd, o, f, args...)			   \
	({								                   \
		struct v4l2_subdev *__sd = (sd);			   \
		int __result;						           \
		if (!__sd)						               \
			__result = -ENODEV;				           \
		else if (!(__sd->ops->o && __sd->ops->o->f))   \
			__result = -ENOIOCTLCMD;			       \
		else if (v4l2_subdev_call_wrappers.o &&		   \
			 v4l2_subdev_call_wrappers.o->f)		   \
			__result = v4l2_subdev_call_wrappers.o->f( \
							__sd, ##args);	           \
		else							               \
			__result = __sd->ops->o->f(__sd, ##args);  \
		__result;						               \
	})

        对于rgb888是要使用csi接口还是dsi接口呢? 这就是根据实际来确认,我们调试hdmi需要输出rgb到csi,那么在hdmi驱动中需要在ioctl里添加case 用来判断dsi与csi;假设这个case命名为RKMODULE_RGB888_GET_CSI_DSI_INFO,则在 kernel-5.10/include/uapi/linux/rk-camera-module.h 参考其它模块进行添加;

#define RKMODULE_GET_CSI_DPHY_PARAM       \
	_IOWR('V', BASE_VIDIOC_PRIVATE + 32, struct rkmodule_csi_dphy_param)

/* 定义一个新的case */
#define RKMODULE_RGB888_GET_CSI_DSI_INFO       \
	_IOWR('V', BASE_VIDIOC_PRIVATE + 33, __u32)

         那么在hdmi驱动中添加如下:

static long lt6911uxc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
	struct lt6911uxc *lt6911uxc = to_state(sd);
	long ret = 0;

	switch (cmd) {
	case RKMODULE_GET_MODULE_INFO:
		lt6911uxc_get_module_inf(lt6911uxc, (struct rkmodule_inf *)arg);
		break;
    // 添加一个case 
    case RKMODULE_RGB888_GET_CSI_DSI_INFO:
        *(int *)arg = RKMODULE_CSI_INPUT;  // 自己定义的
        break;

	default:
		ret = -ENOIOCTLCMD;
		break;
	}

	return ret;
}


static long lt6911uxc_compat_ioctl32(struct v4l2_subdev *sd,
				  unsigned int cmd, unsigned long arg)
{
    int *seq;
	...........
	switch (cmd) {
    ...........
	case RKMODULE_RGB888_GET_CSI_DSI_INFO:
		seq = kzalloc(sizeof(*seq), GFP_KERNEL);
		if (!seq) {
			ret = -ENOMEM;
			return ret;
		}

		ret = lt6911uxc_ioctl(sd, cmd, seq);
		if (!ret) {
			ret = copy_to_user(up, seq, sizeof(*seq));
			if (ret)
				ret = -EFAULT;
		}
		kfree(seq);
		break;
    ..........

	return ret;
}

        上面ioctl中的RKMODULE_CSI_INPUT也是自己在rk-camera-module.h文件中定义的,如下所示;

/*
 * CSI/DSI input select IOCTL
 */
enum rkmodule_csi_dsi_seq {
	RKMODULE_CSI_INPUT = 0,
	RKMODULE_DSI_INPUT,
};

        那么在cif驱动中如何调用新添加的这个case呢?首先要知道数据先经过mipi csi2然后再传入cif控制器的;因此在mipi csi2驱动里也需要增加对csi与dsi的判断,那首先就要定义个函数用来获得我们接入的sensor,如下

kernel-5.10\drivers\media\platform\rockchip\cif\mipi-csi2.c

static void get_remote_terminal_sensor(struct v4l2_subdev *sd,
				       struct v4l2_subdev **sensor_sd)
{
	struct media_graph graph;
	struct media_entity *entity = &sd->entity;
	struct media_device *mdev = entity->graph_obj.mdev;
	int ret;

	/* Walk the graph to locate sensor nodes. */
	mutex_lock(&mdev->graph_mutex);
	ret = media_graph_walk_init(&graph, mdev);
	if (ret) {
		mutex_unlock(&mdev->graph_mutex);
		*sensor_sd = NULL;
		return;
	}

	media_graph_walk_start(&graph, entity);
	while ((entity = media_graph_walk_next(&graph))) {
		if (entity->function == MEDIA_ENT_F_CAM_SENSOR)
			break;
	}
	mutex_unlock(&mdev->graph_mutex);
	media_graph_walk_cleanup(&graph);

	if (entity)
		*sensor_sd = media_entity_to_v4l2_subdev(entity);
	else
		*sensor_sd = NULL;
}

          看下v4l2_subdev结构体的定义,路径kernel/include/media/v4l2-subdev.h,上面那个函数主要是解析media拓扑结构,来获取sensor的entity。

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[V4L2_SUBDEV_NAME_SIZE];
	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 v4l2_async_subdev *asd;
	struct v4l2_async_notifier *notifier;
	struct v4l2_async_notifier *subdev_notifier;
	struct v4l2_subdev_platform_data *pdata;
};

        获取终端的sensor之后,那就要在csi2_update_sensor函数中获取sensor的最新状态,也就是获取rgb888数据是接入csi还是dsi;

kernel-5.10\drivers\media\platform\rockchip\cif\mipi-csi2.c

static void csi2_update_sensor_info(struct csi2_dev *csi2)
{
	.............
	struct v4l2_subdev *terminal_sensor_sd = NULL;
	.............

	get_remote_terminal_sensor(&csi2->sd, &terminal_sensor_sd);
	ret = v4l2_subdev_call(terminal_sensor_sd, core, ioctl,
				RKMODULE_GET_CSI_DSI_INFO, &csi2->dsi_input_en);
	if (ret) {
		v4l2_dbg(1, csi2_debug, &csi2->sd, "get CSI/DSI sel failed, default csi!\n");
		csi2->dsi_input_en = 0;
	}

	............

}

        v4l2_subdev_call中的参数是怎么选择的,像core,ioctl;其实上面刚才有介绍,可以看下hdmi驱动就知道了

//在 core 这个ops中,调用ioctl
static const struct v4l2_subdev_core_ops lt6911uxc_core_ops = {
	.interrupt_service_routine = lt6911uxc_isr,
	.subscribe_event = lt6911uxc_subscribe_event,
	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
	.ioctl = lt6911uxc_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl32 = lt6911uxc_compat_ioctl32,
#endif
};

        执行完回调函数后,就知道了rgb888是csi接收的,但在这还增加了判断;

kernel-5.10\drivers\media\platform\rockchip\cif\mipi-csi2.c

static int csi2_start(struct csi2_dev *csi2)
{
	............
	csi2_update_sensor_info(csi2);

	if (csi2->dsi_input_en == RKMODULE_DSI_INPUT)
		host_type = RK_DSI_RXHOST;
	else
		host_type = RK_CSI_RXHOST;

    .............
}


kernel-5.10\drivers\media\platform\rockchip\cif\mipi-csi2.h

struct csi2_dev {
	struct device		*dev;
	............
	const char		*dev_name;
	int			dsi_input_en; //增加这个变量
};

        mipi csi修改完后,接下来就是cif驱动了,和mipi csi的修改一样,加入对csi和dsi的判断

kernel-5.10\drivers\media\platform\rockchip\cif\capture.c


//回调函数,更新sensor的状态,确定接口
int rkcif_update_sensor_info(struct rkcif_stream *stream)
{
	..........
    terminal_sensor = &stream->cifdev->terminal_sensor;
	get_remote_terminal_sensor(stream, &terminal_sensor->sd);
    ..........

    if (v4l2_subdev_call(terminal_sensor->sd, core, ioctl, RKMODULE_GET_CSI_DSI_INFO,
					&terminal_sensor->dsi_input_en)) {
			v4l2_dbg(1, rkcif_debug, &stream->cifdev->v4l2_dev,
				"%s: get terminal %s CSI/DSI sel failed, default csi input!\n",
				__func__, terminal_sensor->sd->name);
			terminal_sensor->dsi_input_en = 0;
		}
    .........
}


static int rkcif_csi_channel_init(struct rkcif_stream *stream,
				  struct csi_channel_info *channel)
{
	struct rkcif_device *dev = stream->cifdev;
	..........

	channel->cmd_mode_en = 0; /* default use DSI Video Mode */
	channel->dsi_input = dev->terminal_sensor.dsi_input_en;

	..........
    channel->data_type = get_data_type(stream->cif_fmt_in->mbus_code,
						   channel->cmd_mode_en,
						   channel->dsi_input);
    ..........
}

static unsigned char get_data_type(u32 pixelformat, u8 cmd_mode_en, u8 dsi_input)
{
    .............
	
    case MEDIA_BUS_FMT_RGB888_1X24:
		if (dsi_input) {     //dsi_input 自己定义添加
			if (cmd_mode_en) /* dsi command mode*/
				return 0x39;
			else /* dsi video mode */
				return 0x3e;
		} else {
			return 0x24;
		}
    ............
}

kernel-5.10\drivers\media\platform\rockchip\cif\dev.h

struct rkcif_sensor_info {
	struct v4l2_subdev *sd;
	struct v4l2_mbus_config mbus;
	struct v4l2_subdev_frame_interval fi;
	int lanes;
	struct v4l2_rect raw_rect;
	struct v4l2_subdev_selection selection;
	int dsi_input_en;  //自己定义的
};

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值