stm32mp157_drm驱动源码分析

### stm32mp157_drm驱动源码分析

stm32mp1系列出厂SDK中自带LTDC接口驱动,用于连接RGB LCD,对其驱动源码分析如下。

  1. 从其设备树文件中找出LTDC对应的节点:<arch/arm/boot/dts/stm32mp151.dtsi>,stm32mp1系列共有的LTDC节点:

    1
    2
    3
    4
    5
    6
    7
    8
    ltdc: display-controller@5a001000 { 
    compatible = "st,stm32-ltdc";
    reg = <0x5a001000 0x400>;
    interrupts = <GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>,
    <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&rcc LTDC_PX>;
    clock-names = "lcd";
    resets = <&rcc LTDC_R>; 1660 status = "disabled"; 1661 };
    • compatible属性:”st,stm32-ltdc”,在源码中检索此字符串,找到驱动源码
  2. DRM驱动源码:<drivers/gpu/drm/stm/drv.c>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    static const struct of_device_id drv_dt_ids[] = {
    { .compatible = "st,stm32-ltdc"},
    { /* end node */ },
    };
    MODULE_DEVICE_TABLE(of, drv_dt_ids);

    static struct platform_driver stm_drm_platform_driver = {
    .probe = stm_drm_platform_probe,
    .remove = stm_drm_platform_remove,
    .driver = {
    .name = "stm32-display",
    .of_match_table = drv_dt_ids,
    .pm = &drv_pm_ops, //power manage
    },
    };
    • 标准platform总线驱动,与设备树中LTDC接口匹配
    • 匹配成功后执行probe函数
  3. stm_drm_platform_probe函数源码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    static int stm_drm_platform_probe(struct platform_device *pdev)
    {
    struct device *dev = &pdev->dev;
    struct drm_device *ddev; //drm设备
    int ret;

    DRM_DEBUG("%s\n", __func__);

    dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); //DMA内存映射相关

    ddev = drm_dev_alloc(&drv_driver, dev); //为drm设备分配内存,将drm设备与驱动绑定并完成初始化工作

    if (IS_ERR(ddev))
    return PTR_ERR(ddev);

    ret = drv_load(ddev); //完成KMS的相关初始化工作 这是一个st自己封装的函数,下详解
    if (ret)
    goto err_put;

    ret = drm_dev_register(ddev, 0); //将drm dev注册进drm core
    if (ret)
    goto err_put;

    drm_fbdev_generic_setup(ddev, 16); //设置prefer bpp

    return 0;

    err_put:
    drm_dev_put(ddev);

    return ret;
    }
    • drm_dev_alloc函数:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      struct drm_device *drm_dev_alloc(struct drm_driver *driver, struct device *parent)
      {
      struct drm_device *dev;
      int ret;

      dev = kzalloc(sizeof(*dev), GFP_KERNEL); //分配内存
      if (!dev)
      return ERR_PTR(-ENOMEM);

      ret = drm_dev_init(dev, driver, parent); //初始化drm_dev设备,此函数会使drm_device结构体下的driver成员变量指向drm_driver
      if (ret) {
      kfree(dev);
      return ERR_PTR(ret);
      }

      return dev; //返回drm_dev结构体指针
      }
    • drv_load函数解析:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
          static int drv_load(struct drm_device *ddev)
      {
      struct platform_device *pdev = to_platform_device(ddev->dev);
      struct ltdc_device *ldev;
      int ret;

      DRM_DEBUG("%s\n", __func__);

      ldev = devm_kzalloc(ddev->dev, sizeof(*ldev), GFP_KERNEL); //为ltdc设备申请内存
      if (!ldev)
      return -ENOMEM;

      ddev->dev_private = (void *)ldev;

      drm_mode_config_init(ddev); //创建一些全局数据结构 包括standard properties

      //设置宽度和高度,在调用drm_mode_addbf()函数时限制framebuffer的size,
      ddev->mode_config.min_width = 0;
      ddev->mode_config.min_height = 0;
      ddev->mode_config.max_width = STM_MAX_FB_WIDTH;
      ddev->mode_config.max_height = STM_MAX_FB_HEIGHT;

      ddev->mode_config.funcs = &drv_mode_config_funcs; //设置mode config回调函数结构体 struct drm_mode_config_funcs{...};

      ret = ltdc_load(ddev);
      if (ret)
      goto err;

      drm_mode_config_reset(ddev);
      drm_kms_helper_poll_init(ddev);

      platform_set_drvdata(pdev, ddev); //将ddev设置为platform_dev->dev的私有数据

      return 0;
      err:
      drm_mode_config_cleanup(ddev);
      return ret;
      }
      • ltdc_load函数解析
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
        57
        58
        59
        60
        61
        62
        63
        64
        65
        66
        67
        68
        69
        70
        71
        72
        73
        74
        75
        76
        77
        78
        79
        80
        81
        82
        83
        84
        85
        86
        87
        88
        89
        90
        91
        92
        93
        94
        95
        96
        97
        98
        99
        100
        101
        102
        103
        104
        105
        106
        107
        108
        109
        110
        111
        112
        113
        114
        115
        116
        117
        118
        119
        120
        121
        122
        123
        124
        125
        126
        127
        128
        129
        130
        131
        132
        133
        134
        135
        136
        137
        138
        139
        140
        141
        int ltdc_load(struct drm_device *ddev)
        {
        struct platform_device *pdev = to_platform_device(ddev->dev);
        struct ltdc_device *ldev = ddev->dev_private;
        struct device *dev = ddev->dev;
        struct device_node *np = dev->of_node;
        struct drm_bridge *bridge;
        struct drm_panel *panel;
        struct drm_crtc *crtc;
        struct reset_control *rstc;
        struct resource *res;
        int irq, i, nb_endpoints;
        int ret = -ENODEV;

        DRM_DEBUG_DRIVER("\n");

        nb_endpoints = of_graph_get_endpoint_count(np); //实际上在property.c中定义的,获取节点的num
        if (!nb_endpoints)
        return -ENODEV;

        ldev->pixel_clk = devm_clk_get(dev, "lcd"); //通过设备树中的clock-names获取ltdc-px clock,"lcd"为设备树中该属性的名字
        if (IS_ERR(ldev->pixel_clk)) {
        if (PTR_ERR(ldev->pixel_clk) != -EPROBE_DEFER)
        DRM_ERROR("Unable to get lcd clock\n");
        return PTR_ERR(ldev->pixel_clk);
        }

        if (clk_prepare_enable(ldev->pixel_clk)) { //enable clk
        DRM_ERROR("Unable to prepare pixel clock\n");
        return -ENODEV;
        }

        /* Get endpoints if any */
        for (i = 0; i < nb_endpoints; i++) {
        ret = drm_of_find_panel_or_bridge(np, 0, i, &panel, &bridge);

        if (ret == -ENODEV)
        continue;
        else if (ret)
        goto err;

        if (panel) { //panel在panel-simple.c中完成定义
        bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_DPI); //Creates a &drm_bridge and &drm_connector that just calls the appropriate functions from &drm_panel.
        if (IS_ERR(bridge)) {
        DRM_ERROR("panel-bridge endpoint %d\n", i);
        ret = PTR_ERR(bridge);
        goto err;
        }
        }

        if (bridge) {
        ret = ltdc_encoder_init(ddev, bridge); //encoder的创建和初始化 并且attach encoder and bridge
        if (ret) {
        DRM_ERROR("init encoder endpoint %d\n", i);
        goto err;
        }
        }
        }

        rstc = devm_reset_control_get_exclusive(dev, NULL);

        mutex_init(&ldev->err_lock);

        if (!IS_ERR(rstc)) {
        reset_control_assert(rstc);
        usleep_range(10, 20);
        reset_control_deassert(rstc);
        }

        res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //获取ltdc的mem资源
        ldev->regs = devm_ioremap_resource(dev, res); //remap 得到base
        if (IS_ERR(ldev->regs)) {
        DRM_ERROR("Unable to get ltdc registers\n");
        ret = PTR_ERR(ldev->regs);
        goto err;
        }

        reg_clear(ldev->regs, LTDC_IER, IER_LIE | IER_RRIE | IER_FUIE | IER_TERRIE); //关闭ltdc中断

        ret = ltdc_get_caps(ddev);
        if (ret) {
        DRM_ERROR("hardware identifier (0x%08x) not supported!\n", ldev->caps.hw_version);
        goto err;
        }

        DRM_DEBUG_DRIVER("ltdc hw version 0x%08x\n", ldev->caps.hw_version);

        for (i = 0; i < ldev->caps.nb_irq; i++) {
        irq = platform_get_irq(pdev, i); //获取中断号
        if (irq < 0) {
        ret = irq;
        goto err;
        }

        ret = devm_request_threaded_irq(dev, irq, ltdc_irq, ltdc_irq_thread, IRQF_ONESHOT, dev_name(dev), ddev); //申请中断
        if (ret) {
        DRM_ERROR("Failed to register LTDC interrupt\n");
        goto err;
        }

        }

        crtc = devm_kzalloc(dev, sizeof(*crtc), GFP_KERNEL);
        if (!crtc) {
        DRM_ERROR("Failed to allocate crtc\n");
        ret = -ENOMEM;
        goto err;
        }

        ddev->mode_config.allow_fb_modifiers = true;

        ret = ltdc_crtc_init(ddev, crtc); //plane 和 crtc的创建和初始化
        if (ret) {
        DRM_ERROR("Failed to init crtc\n");
        goto err;
        }

        ret = drm_vblank_init(ddev, NB_CRTC); //initialize vblank support
        if (ret) {
        DRM_ERROR("Failed calling drm_vblank_init()\n");
        goto err;
        }

        /* Allow usage of vblank without having to call drm_irq_install */
        ddev->irq_enabled = 1;

        clk_disable_unprepare(ldev->pixel_clk);

        pinctrl_pm_select_sleep_state(ddev->dev);

        pm_runtime_enable(ddev->dev);

        return 0;
        err:
        for (i = 0; i < nb_endpoints; i++)
        drm_of_panel_bridge_remove(ddev->dev->of_node, 0, i);

        clk_disable_unprepare(ldev->pixel_clk);

        return ret;
        }
  4. panel-simple.c文件解析

    • 数据结构
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      struct panel_simple {
      struct drm_panel base; //继承的基础数据结构
      bool prepared;
      bool enabled;
      bool no_hpd;

      const struct panel_desc *desc; //panel describe

      struct backlight_device *backlight; //背光
      struct regulator *supply;
      struct i2c_adapter *ddc;

      struct gpio_desc *enable_gpio;

      struct drm_display_mode override_mode;
      };
      • struct drm_panel base
        1
        2
        3
        4
        5
        6
        7
        struct drm_panel {
        struct drm_device *drm; //拥有此panel的drm_dev
        struct drm_connector *connector; //connector attached to the panel
        struct device *dev; //parent device
        const struct drm_panel_funcs *funcs; //panel的操作函数集
        struct list_head list;
        };
      • struct panel_desc *desc
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        struct panel_desc {
        const struct drm_display_mode *modes; //指向一个适合此panel的mode array,设置屏幕参数
        unsigned int num_modes; //size of mode array
        const struct display_timing *timings; //指向一个timing array
        unsigned int num_timings; //size of timing array
        unsigned int bpc; // bits/color
        /*屏幕大小 单位为毫米*/
        struct {
        unsigned int width;
        unsigned int height;
        } size;

        struct {
        unsigned int prepare;
        unsigned int hpd_absent_delay;
        unsigned int enable;
        unsigned int disable;
        unsigned int unprepare;
        } delay;

        u32 bus_format; //See MEDIA_BUS_FMT_... defines.
        u32 bus_flags; //See DRM_BUS_FLAG_... defines.
    • 代码分析

    panel-simple也是一个标准的platform总线驱动

    -

    1
    2
    3
    4
    5
    6
    7
    8
    9
    static struct platform_driver panel_simple_platform_driver = {
    .driver = {
    .name = "panel-simple",
    .of_match_table = platform_of_match,
    },
    .probe = panel_simple_platform_probe,
    .remove = panel_simple_platform_remove,
    .shutdown = panel_simple_platform_shutdown,
    };

    - platform_of_match中包含了大量的匹配项用于匹配不同的屏幕设备
    1
    2
    3
    4
    5
    6
    7
    static const struct of_device_id platform_of_match[] = {
    {
    .compatible = "ampire,am-480272h3tmqw-t01h", //用于与设备树匹配
    .data = &ampire_am_480272h3tmqw_t01h, //指向一个panel_desc结构体
    },
    ...
    }

    - panel_simple_platform_probe中核心是panel_simple_probe函数,主要完成屏幕设备的参数设置、初始化、注册
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
    {
    struct device_node *backlight, *ddc;
    struct panel_simple *panel;
    struct display_timing dt;
    int err;

    panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL); //为panel开辟内存
    if (!panel)
    return -ENOMEM;

    panel->enabled = false;
    panel->prepared = false;
    panel->desc = desc;

    panel->no_hpd = of_property_read_bool(dev->of_node, "no-hpd");

    panel->supply = devm_regulator_get(dev, "power");
    if (IS_ERR(panel->supply))
    return PTR_ERR(panel->supply);

    panel->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
    if (IS_ERR(panel->enable_gpio)) {
    err = PTR_ERR(panel->enable_gpio);
    if (err != -EPROBE_DEFER)
    dev_err(dev, "failed to request GPIO: %d\n", err);
    return err;
    }

    backlight = of_parse_phandle(dev->of_node, "backlight", 0);
    if (backlight) {
    panel->backlight = of_find_backlight_by_node(backlight);
    of_node_put(backlight);

    if (!panel->backlight)
    return -EPROBE_DEFER;
    }

    ddc = of_parse_phandle(dev->of_node, "ddc-i2c-bus", 0);
    if (ddc) {
    panel->ddc = of_find_i2c_adapter_by_node(ddc);
    of_node_put(ddc);

    if (!panel->ddc) {
    err = -EPROBE_DEFER;
    goto free_backlight;
    }
    }

    if (!of_get_display_timing(dev->of_node, "panel-timing", &dt))
    panel_simple_parse_panel_timing_node(dev, panel, &dt);

    drm_panel_init(&panel->base);
    panel->base.dev = dev;
    panel->base.funcs = &panel_simple_funcs;

    err = drm_panel_add(&panel->base);
    if (err < 0)
    goto free_ddc;

    dev_set_drvdata(dev, panel);

    return 0;

    free_ddc:
    if (panel->ddc)
    put_device(&panel->ddc->dev);
    free_backlight:
    if (panel->backlight)
    put_device(&panel->backlight->dev);

    return err;
    }


stm32mp157_drm驱动源码分析
http://example.com/2022/10/24/stm32mp157_drm驱动分析/
作者
Hector
发布于
2022年10月24日
许可协议