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

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

soc平台

开发板品牌

sdk版本

开发平台

rv1126

firefly

20211022

ubuntu18.04 64位

学嵌入式开发也有一段时间了,本来也是想搞一款音视频编码器产品,出于这个目的,也验证一下自己的能力。

由于现在开发板是现成的了,引出了对应MIPI CSI的两组总线,需要自己做个HDMI转MIPI的模块,自己根据芯片手册,简单设计了一个LT6911UXC的转换模块(调试板有点脏乱)。

image-20230714165528631

整个硬件平台算是搭建完成了。

这里说是移植,而不是开发的原因是,正好手头上有已经比较完美的驱动了。只是硬件平台的改动而已。

一、根据电路在设备树中增加I2C设备

这里设计的是挂载在I2C2上的,所以

  1. &i2c2{
  2. status = "okay";
  3. clock-frequency = <100000>;
  4. lt6911uxc: lt6911uxc@2b {
  5. compatible = "LT6911UXC";
  6. reg = <0x2b>;
  7. };
  8. };

 

二、根据源码改设备树

都知道,设备树是描述硬件资源的。既然硬件平台改了,那么对于新的硬件平台当然是要在设备树中加入设备和相关信息了

1. 看源码

源码中有这样一个函数

  1. #ifdef CONFIG_OF
  2. static int lt6911uxc_parse_of(struct lt6911uxc *lt6911uxc)
  3. {
  4. struct device *dev = <6911uxc->i2c_client->dev;
  5. struct device_node *node = dev->of_node;
  6. struct v4l2_fwnode_endpoint *endpoint;
  7. struct device_node *ep;
  8. int ret;
  9. ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
  10. <6911uxc->module_index);
  11. ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
  12. <6911uxc->module_facing);
  13. ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
  14. <6911uxc->module_name);
  15. ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
  16. <6911uxc->len_name);
  17. if (ret) {
  18. dev_err(dev, "could not get module information!\n");
  19. return -EINVAL;
  20. }
  21. #if 0
  22. lt6911uxc->power_gpio = devm_gpiod_get_optional(dev, "power", //电源控制
  23. GPIOD_OUT_LOW);
  24. if (IS_ERR(lt6911uxc->power_gpio)) {
  25. dev_err(dev, "failed to get power gpio\n");
  26. ret = PTR_ERR(lt6911uxc->power_gpio);
  27. return ret;
  28. }
  29. #endif
  30. lt6911uxc->reset_gpio = devm_gpiod_get_optional(dev, "reset", //复位引脚
  31. GPIOD_OUT_LOW);
  32. if (IS_ERR(lt6911uxc->reset_gpio)) {
  33. dev_err(dev, "failed to get reset gpio\n");
  34. ret = PTR_ERR(lt6911uxc->reset_gpio);
  35. return ret;
  36. }
  37. lt6911uxc->plugin_det_gpio = devm_gpiod_get_optional(dev, "plugin-det", //插入检测
  38. GPIOD_IN);
  39. if (IS_ERR(lt6911uxc->plugin_det_gpio)) {
  40. dev_err(dev, "failed to get plugin det gpio\n");
  41. ret = PTR_ERR(lt6911uxc->plugin_det_gpio);
  42. return ret;
  43. }
  44. #if 0
  45. lt6911uxc->hpd_ctl_gpio = devm_gpiod_get_optional(dev, "hpd-ctl", //热插拔控制
  46. GPIOD_OUT_HIGH);
  47. if (IS_ERR(lt6911uxc->hpd_ctl_gpio)) {
  48. dev_err(dev, "failed to get hpd ctl gpio\n");
  49. ret = PTR_ERR(lt6911uxc->hpd_ctl_gpio);
  50. return ret;
  51. }
  52. #endif
  53. //获取和配置v4l2相关端点
  54. ep = of_graph_get_next_endpoint(dev->of_node, NULL);
  55. if (!ep) {
  56. dev_err(dev, "missing endpoint node\n");
  57. ret = -EINVAL;
  58. return ret;
  59. }
  60. endpoint = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(ep));
  61. if (IS_ERR(endpoint)) {
  62. dev_err(dev, "failed to parse endpoint\n");
  63. ret = PTR_ERR(endpoint);
  64. return ret;
  65. }
  66. if (endpoint->bus_type != V4L2_MBUS_CSI2 ||
  67. endpoint->bus.mipi_csi2.num_data_lanes == 0) {
  68. dev_err(dev, "missing CSI-2 properties in endpoint\n");
  69. ret = -EINVAL;
  70. goto free_endpoint;
  71. }
  72. #if 1
  73. lt6911uxc->xvclk = devm_clk_get(dev, "xvclk"); //如果lt6911的外部时钟是soc提供,则需要进行相关配置
  74. if (IS_ERR(lt6911uxc->xvclk)) {
  75. dev_err(dev, "failed to get xvclk\n");
  76. ret = -EINVAL;
  77. goto free_endpoint;
  78. }
  79. ret = clk_prepare_enable(lt6911uxc->xvclk);
  80. if (ret) {
  81. dev_err(dev, "Failed! to enable xvclk\n");
  82. goto free_endpoint;
  83. }
  84. #endif
  85. lt6911uxc->csi_lanes_in_use = endpoint->bus.mipi_csi2.num_data_lanes;
  86. lt6911uxc->bus = endpoint->bus.mipi_csi2;
  87. lt6911uxc->enable_hdcp = false;
  88. // gpiod_set_value(lt6911uxc->hpd_ctl_gpio, 0);
  89. // gpiod_set_value(lt6911uxc->power_gpio, 1);
  90. lt6911uxc_reset(lt6911uxc);
  91. ret = 0;
  92. free_endpoint:
  93. v4l2_fwnode_endpoint_free(endpoint);
  94. return ret;
  95. }

从整个处理过程中,此函数是在通过devm_gpiod_get_optional这个函数查找设备树中 "power-gpios" , "reset-gpios", "plugin-det-gpios", "hpd-ctl-gpios"相关节点,并进行输入输出模式配置。将返回的结果存储到定义的lt6911uxc私有数据中。

2. 改设备树

根据以上源码,对设备树进行如下配置(根据电路设计,power和HPD的控制这里没有用到,所以这里不做配置)

  1. reset-gpios = <&gpio4 RK_PA1 GPIO_ACTIVE_LOW>;
  2. plugin-det-gpios = <&gpio2 RK_PB0 IRQ_TYPE_LEVEL_LOW>;
  3. //hpd-ctl-gpios = <&gpio2 RK_PD4 GPIO_ACTIVE_LOW>;

3. 处理函数函数

复位函数

  1. static void lt6911uxc_reset(struct lt6911uxc *lt6911uxc)
  2. {
  3. gpiod_set_value(lt6911uxc->reset_gpio, 0);
  4. usleep_range(2000, 2100);
  5. gpiod_set_value(lt6911uxc->reset_gpio, 1);
  6. usleep_range(120*1000, 121*1000);
  7. gpiod_set_value(lt6911uxc->reset_gpio, 0);
  8. usleep_range(300*1000, 310*1000);
  9. }

插入检测函数

  1. static inline bool tx_5v_power_present(struct v4l2_subdev *sd)
  2. {
  3. int val;
  4. struct lt6911uxc *lt6911uxc = to_state(sd);
  5. val = gpiod_get_value(lt6911uxc->plugin_det_gpio);
  6. v4l2_dbg(1, debug, sd, "%s plug det: %s!\n", __func__,
  7. (val > 0) ? "int" : "out");
  8. return (val > 0);
  9. }
  10. static irqreturn_t plugin_detect_irq_handler(int irq, void *dev_id)
  11. {
  12. struct lt6911uxc *lt6911uxc = dev_id;
  13. struct v4l2_subdev *sd = <6911uxc->sd;
  14. /* control hpd output level after 25ms */
  15. // schedule_delayed_work(<6911uxc->delayed_work_enable_hotplug,
  16. // HZ / 40);
  17. tx_5v_power_present(sd);
  18. return IRQ_HANDLED;
  19. }

三、根据其他摄像头配置完善设备树

  1. &i2c2{
  2. status = "okay";
  3. clock-frequency = <100000>;
  4. lt6911uxc: lt6911uxc@2b {
  5. compatible = "LT6911UXC";
  6. reg = <0x2b>;
  7. clocks = <&cru CLK_MIPICSI_OUT>;
  8. clock-names = "xvclk";
  9. interrupt-parent=<&gpio3>;
  10. interrupts=;
  11. pinctrl-names = "default";
  12. pinctrl-0 = <<6911uxc_pin>;
  13. reset-gpios = <&gpio4 RK_PA1 GPIO_ACTIVE_LOW>;
  14. plugin-det-gpios = <&gpio2 RK_PB0 IRQ_TYPE_LEVEL_LOW>;
  15. // hpd-ctl-gpios = <&gpio2 RK_PD4 GPIO_ACTIVE_LOW>;
  16. rockchip,camera-module-index = <0>;
  17. rockchip,camera-module-facing = "back";
  18. rockchip,camera-module-name = "hdmi_in"; //摄像头名称
  19. rockchip,camera-module-lens-name = "lt6911uxc"; //摄像头镜头
  20. port { //v4l2端点配置
  21. ucam_out0: endpoint {
  22. remote-endpoint = <&mipi_in_ucam0>;
  23. data-lanes = <1 2 3 4>;
  24. };
  25. };
  26. };
  27. };

四、其他设备树配置

  1. &csi_dphy0 {
  2. status = "okay";
  3. ports {
  4. #address-cells = <1>;
  5. #size-cells = <0>;
  6. port@0 {
  7. reg = <0>;
  8. #address-cells = <1>;
  9. #size-cells = <0>;
  10. /*mipi_in_ucam0: endpoint@1 {
  11. reg = <1>;
  12. remote-endpoint = <&ucam_out0>;
  13. data-lanes = <1 2 3 4>;
  14. };*/
  15. mipi_in_ucam0: endpoint@1 {
  16. reg = <1>;
  17. remote-endpoint = <&ucam_out0>;
  18. data-lanes = <1 2 3 4>;
  19. };
  20. };
  21. port@1 {
  22. reg = <1>;
  23. #address-cells = <1>;
  24. #size-cells = <0>;
  25. csidphy0_out: endpoint@0 {
  26. reg = <0>;
  27. remote-endpoint = <&mipi_csi2_input>;
  28. data-lanes = <1 2 3 4>;
  29. };
  30. };
  31. };
  32. };
  33. &mipi_csi2 {
  34. status = "okay";
  35. ports {
  36. #address-cells = <1>;
  37. #size-cells = <0>;
  38. port@0 {
  39. reg = <0>;
  40. #address-cells = <1>;
  41. #size-cells = <0>;
  42. mipi_csi2_input: endpoint@1 {
  43. reg = <1>;
  44. remote-endpoint = <&csidphy0_out>;
  45. data-lanes = <1 2 3 4>;
  46. };
  47. };
  48. port@1 {
  49. reg = <1>;
  50. #address-cells = <1>;
  51. #size-cells = <0>;
  52. mipi_csi2_output: endpoint@0 {
  53. reg = <0>;
  54. remote-endpoint = <&cif_mipi_in>;
  55. data-lanes = <1 2 3 4>;
  56. };
  57. };
  58. };
  59. };

五、总结

以上也就配置完成了,具体调试过程可以参看我的另一篇文章

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条评论)

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