原创
移植lt6911uxc的驱动到rv1126的平台
soc平台
|
开发板品牌
|
sdk版本
|
开发平台
|
rv1126
|
firefly
|
20211022
|
ubuntu18.04 64位
|
学嵌入式开发也有一段时间了,本来也是想搞一款音视频编码器产品,出于这个目的,也验证一下自己的能力。
由于现在开发板是现成的了,引出了对应MIPI CSI的两组总线,需要自己做个HDMI转MIPI的模块,自己根据芯片手册,简单设计了一个LT6911UXC的转换模块(调试板有点脏乱)。
image-20230714165528631
整个硬件平台算是搭建完成了。
这里说是移植,而不是开发的原因是,正好手头上有已经比较完美的驱动了。只是硬件平台的改动而已。
一、根据电路在设备树中增加I2C设备
这里设计的是挂载在I2C2上的,所以
- &i2c2{
- status = "okay";
- clock-frequency = <100000>;
- lt6911uxc: lt6911uxc@2b {
- compatible = "LT6911UXC";
- reg = <0x2b>;
- };
- };
二、根据源码改设备树
都知道,设备树是描述硬件资源的。既然硬件平台改了,那么对于新的硬件平台当然是要在设备树中加入设备和相关信息了
1. 看源码
源码中有这样一个函数
- #ifdef CONFIG_OF
- static int lt6911uxc_parse_of(struct lt6911uxc *lt6911uxc)
- {
- struct device *dev = <6911uxc->i2c_client->dev;
- struct device_node *node = dev->of_node;
- struct v4l2_fwnode_endpoint *endpoint;
- struct device_node *ep;
- int ret;
- ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
- <6911uxc->module_index);
- ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
- <6911uxc->module_facing);
- ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
- <6911uxc->module_name);
- ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
- <6911uxc->len_name);
- if (ret) {
- dev_err(dev, "could not get module information!\n");
- return -EINVAL;
- }
- #if 0
- lt6911uxc->power_gpio = devm_gpiod_get_optional(dev, "power", //电源控制
- GPIOD_OUT_LOW);
- if (IS_ERR(lt6911uxc->power_gpio)) {
- dev_err(dev, "failed to get power gpio\n");
- ret = PTR_ERR(lt6911uxc->power_gpio);
- return ret;
- }
- #endif
- lt6911uxc->reset_gpio = devm_gpiod_get_optional(dev, "reset", //复位引脚
- GPIOD_OUT_LOW);
- if (IS_ERR(lt6911uxc->reset_gpio)) {
- dev_err(dev, "failed to get reset gpio\n");
- ret = PTR_ERR(lt6911uxc->reset_gpio);
- return ret;
- }
- lt6911uxc->plugin_det_gpio = devm_gpiod_get_optional(dev, "plugin-det", //插入检测
- GPIOD_IN);
- if (IS_ERR(lt6911uxc->plugin_det_gpio)) {
- dev_err(dev, "failed to get plugin det gpio\n");
- ret = PTR_ERR(lt6911uxc->plugin_det_gpio);
- return ret;
- }
- #if 0
- lt6911uxc->hpd_ctl_gpio = devm_gpiod_get_optional(dev, "hpd-ctl", //热插拔控制
- GPIOD_OUT_HIGH);
- if (IS_ERR(lt6911uxc->hpd_ctl_gpio)) {
- dev_err(dev, "failed to get hpd ctl gpio\n");
- ret = PTR_ERR(lt6911uxc->hpd_ctl_gpio);
- return ret;
- }
- #endif
- //获取和配置v4l2相关端点
- ep = of_graph_get_next_endpoint(dev->of_node, NULL);
- if (!ep) {
- dev_err(dev, "missing endpoint node\n");
- ret = -EINVAL;
- return ret;
- }
- endpoint = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(ep));
- if (IS_ERR(endpoint)) {
- dev_err(dev, "failed to parse endpoint\n");
- ret = PTR_ERR(endpoint);
- return ret;
- }
- if (endpoint->bus_type != V4L2_MBUS_CSI2 ||
- endpoint->bus.mipi_csi2.num_data_lanes == 0) {
- dev_err(dev, "missing CSI-2 properties in endpoint\n");
- ret = -EINVAL;
- goto free_endpoint;
- }
- #if 1
- lt6911uxc->xvclk = devm_clk_get(dev, "xvclk"); //如果lt6911的外部时钟是soc提供,则需要进行相关配置
- if (IS_ERR(lt6911uxc->xvclk)) {
- dev_err(dev, "failed to get xvclk\n");
- ret = -EINVAL;
- goto free_endpoint;
- }
- ret = clk_prepare_enable(lt6911uxc->xvclk);
- if (ret) {
- dev_err(dev, "Failed! to enable xvclk\n");
- goto free_endpoint;
- }
- #endif
- lt6911uxc->csi_lanes_in_use = endpoint->bus.mipi_csi2.num_data_lanes;
- lt6911uxc->bus = endpoint->bus.mipi_csi2;
- lt6911uxc->enable_hdcp = false;
- // gpiod_set_value(lt6911uxc->hpd_ctl_gpio, 0);
- // gpiod_set_value(lt6911uxc->power_gpio, 1);
- lt6911uxc_reset(lt6911uxc);
- ret = 0;
- free_endpoint:
- v4l2_fwnode_endpoint_free(endpoint);
- return ret;
- }
从整个处理过程中,此函数是在通过devm_gpiod_get_optional这个函数查找设备树中 "power-gpios" , "reset-gpios", "plugin-det-gpios", "hpd-ctl-gpios"相关节点,并进行输入输出模式配置。将返回的结果存储到定义的lt6911uxc私有数据中。
2. 改设备树
根据以上源码,对设备树进行如下配置(根据电路设计,power和HPD的控制这里没有用到,所以这里不做配置)
- reset-gpios = <&gpio4 RK_PA1 GPIO_ACTIVE_LOW>;
- plugin-det-gpios = <&gpio2 RK_PB0 IRQ_TYPE_LEVEL_LOW>;
- //hpd-ctl-gpios = <&gpio2 RK_PD4 GPIO_ACTIVE_LOW>;
3. 处理函数函数
复位函数
- static void lt6911uxc_reset(struct lt6911uxc *lt6911uxc)
- {
- gpiod_set_value(lt6911uxc->reset_gpio, 0);
- usleep_range(2000, 2100);
- gpiod_set_value(lt6911uxc->reset_gpio, 1);
- usleep_range(120*1000, 121*1000);
- gpiod_set_value(lt6911uxc->reset_gpio, 0);
- usleep_range(300*1000, 310*1000);
- }
插入检测函数
- static inline bool tx_5v_power_present(struct v4l2_subdev *sd)
- {
- int val;
- struct lt6911uxc *lt6911uxc = to_state(sd);
- val = gpiod_get_value(lt6911uxc->plugin_det_gpio);
- v4l2_dbg(1, debug, sd, "%s plug det: %s!\n", __func__,
- (val > 0) ? "int" : "out");
- return (val > 0);
- }
- static irqreturn_t plugin_detect_irq_handler(int irq, void *dev_id)
- {
- struct lt6911uxc *lt6911uxc = dev_id;
- struct v4l2_subdev *sd = <6911uxc->sd;
- /* control hpd output level after 25ms */
- // schedule_delayed_work(<6911uxc->delayed_work_enable_hotplug,
- // HZ / 40);
- tx_5v_power_present(sd);
- return IRQ_HANDLED;
- }
三、根据其他摄像头配置完善设备树
- &i2c2{
- status = "okay";
- clock-frequency = <100000>;
- lt6911uxc: lt6911uxc@2b {
- compatible = "LT6911UXC";
- reg = <0x2b>;
- clocks = <&cru CLK_MIPICSI_OUT>;
- clock-names = "xvclk";
- interrupt-parent=<&gpio3>;
- interrupts=;
- pinctrl-names = "default";
- pinctrl-0 = <<6911uxc_pin>;
- reset-gpios = <&gpio4 RK_PA1 GPIO_ACTIVE_LOW>;
- plugin-det-gpios = <&gpio2 RK_PB0 IRQ_TYPE_LEVEL_LOW>;
- // hpd-ctl-gpios = <&gpio2 RK_PD4 GPIO_ACTIVE_LOW>;
- rockchip,camera-module-index = <0>;
- rockchip,camera-module-facing = "back";
- rockchip,camera-module-name = "hdmi_in"; //摄像头名称
- rockchip,camera-module-lens-name = "lt6911uxc"; //摄像头镜头
- port { //v4l2端点配置
- ucam_out0: endpoint {
- remote-endpoint = <&mipi_in_ucam0>;
- data-lanes = <1 2 3 4>;
- };
- };
- };
- };
四、其他设备树配置
- &csi_dphy0 {
- status = "okay";
- ports {
- #address-cells = <1>;
- #size-cells = <0>;
- port@0 {
- reg = <0>;
- #address-cells = <1>;
- #size-cells = <0>;
- /*mipi_in_ucam0: endpoint@1 {
- reg = <1>;
- remote-endpoint = <&ucam_out0>;
- data-lanes = <1 2 3 4>;
- };*/
- mipi_in_ucam0: endpoint@1 {
- reg = <1>;
- remote-endpoint = <&ucam_out0>;
- data-lanes = <1 2 3 4>;
- };
- };
- port@1 {
- reg = <1>;
- #address-cells = <1>;
- #size-cells = <0>;
- csidphy0_out: endpoint@0 {
- reg = <0>;
- remote-endpoint = <&mipi_csi2_input>;
- data-lanes = <1 2 3 4>;
- };
- };
- };
- };
- &mipi_csi2 {
- status = "okay";
- ports {
- #address-cells = <1>;
- #size-cells = <0>;
- port@0 {
- reg = <0>;
- #address-cells = <1>;
- #size-cells = <0>;
- mipi_csi2_input: endpoint@1 {
- reg = <1>;
- remote-endpoint = <&csidphy0_out>;
- data-lanes = <1 2 3 4>;
- };
- };
- port@1 {
- reg = <1>;
- #address-cells = <1>;
- #size-cells = <0>;
- mipi_csi2_output: endpoint@0 {
- reg = <0>;
- remote-endpoint = <&cif_mipi_in>;
- data-lanes = <1 2 3 4>;
- };
- };
- };
- };
五、总结
以上也就配置完成了,具体调试过程可以参看我的另一篇文章
RK628底层调试,使用V4L2调试工具抓图-面包板社区 (eet-china.com)
rtsp获取demo可以使用瑞芯微SDK自带的
SDK/external/rkmedia/examples/rkmedia_vi_venc_rtsp_test.c
作者: 二月半, 来源:面包板社区
链接: https://mbb.eet-china.com/blog/uid-me-1862109.html
版权声明:本文为博主原创,未经本人允许,禁止转载!
文章评论(0条评论)
登录后参与讨论