原创 移植lt6911uxc的驱动到rv1126的平台

2023-7-27 17:21 1849 6 6 分类: MCU/ 嵌入式 文集: Rockchip

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

    版权声明:本文为博主原创,未经本人允许,禁止转载!

    PARTNER CONTENT

    文章评论0条评论)

    登录后参与讨论
    我要评论
    0
    6
    关闭 站长推荐上一条 /3 下一条