(仅作记录,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.cstatic 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;
#endifstruct 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.cstatic 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.cstatic 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;elsehost_type = RK_CSI_RXHOST;.............
}kernel-5.10\drivers\media\platform\rockchip\cif\mipi-csi2.hstruct 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.hstruct 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; //自己定义的
};