Android Qcom lcd display 学习

Posted bobuddy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Qcom lcd display 学习相关的知识,希望对你有一定的参考价值。

DRM
Linux DRM(二)基本概念和特性
DRM(Direct Rendering Manager)学习简介
DRM 的诞生就是用来处理多个程序对 Video Card 资源的协同使用问题,通过单单一个device node来控制所有硬件设备、通用访问硬件接口、通用内存管理机制。Framebuffer的架构它不能用于处理 基于视频卡的 GPU 的 3D 加速。

DRM从模块上划分,可以简单分为3部分:libdrm、KMS、GEM
libdrm:对底层接口进行封装,向上层提供通用的API接口,主要是对各种IOCTL接口进行封装。
KMS: 更新画面和设置显示参数。更新画面:显示buffer的切换,多图层的合成方式,以及每个图层的显示位置。设置显示参数:包括分辨率、刷新率、电源状态(休眠唤醒)等。
GEM:Graphic Execution Manager,主要负责显示buffer的分配和释放,也是GPU唯一用到DRM的地方。

KMS
CRTC 对显示buffer进行扫描,并产生时序信号的硬件模块,通常指Display Controller
ENCODER 负责将CRTC输出的timing时序转换成外部设备所需要的信号的模块,如HDMI转换器或DSI Controller
CONNECTOR 连接物理显示设备的连接器,如HDMI、DisplayPort、DSI总线,通常和Encoder驱动绑定在一起
FrameBuffer,单个图层的显示内容,唯一一个和硬件无关的基本元素


Qcom Code
DRM top level control
kernel/msm-4.19/techpack/display/msm/msm_drv.c
Controls all the blocks – crtc/encoder/planes/interface Adreno DPU 显示图像处理器相关
kernel/msm-4.19/techpack/display/msm/sde/sde_kms.c

 

msm_pdev_probe
    add_display_components(&pdev->dev, &match);
        component_match_add(dev, matchptr, compare_of, node);/*qcom,sde-kms  connectors = <&sde_dsi>;*/
    component_master_add_with_match(&pdev->dev, &msm_drm_ops, match);
    /* master在component框架中component_list链表找到适配于自己的component组件,注册自己为master到component框架 
     dsi_display调用component_add 把自己添加到component_list, crtc+plane+encoder+connector 都是在DSI Controller驱动中 */

static const struct component_master_ops msm_drm_ops =
        .bind = msm_drm_bind,
        .unbind = msm_drm_unbind,
;

msm_drm_bind    /* 当该master下的所有设备(dsi wb dp等)都初始化完成后,调用该回调的bind */
    msm_drm_init(dev, &msm_driver);
        msm_mdss_init(ddev);
        msm_component_bind_all(dev, ddev);/* Bind all our sub-components: */
            component_bind_all(dev, drm_dev);/* 调用所有从设备的bind 调用dsi_display_bind*/
        _msm_drm_init_helper(priv, ddev, dev, pdev);
            sde_kms_init(ddev);/* .compatible = "qcom,sde-kms" */
                msm_kms_init(&sde_kms->base, &kms_funcs);/*.hw_init  .prepare_commit kms_func函数注册
            (kms)->funcs->hw_init(kms); /* 上述init中注册*/
                _sde_kms_hw_init_blocks(sde_kms, dev, priv);
                    _sde_kms_drm_obj_init(sde_kms); /* create the DRM related objects */
                        _sde_kms_get_displays(sde_kms) /* 这里对应到Interface了 */
                            dsi_display_get_active_displays(sde_kms->dsi_displays,sde_kms->dsi_display_count);/* dsi */
                            wb_display_get_displays(sde_kms->wb_displays,sde_kms->wb_display_count);/* wb */
                            dp_display_get_displays(sde_kms->dp_displays,sde_kms->dp_display_count);/* dp */
                        _sde_kms_setup_displays(dev, priv, sde_kms)
                             sde_encoder_init(dev, &info);
                                 drm_encoder_init(dev, drm_enc, &sde_encoder_funcs, drm_enc_mode, NULL);
                             sde_connector_init(dev,encoder,dsi_display_get_drm_panel(display),display,
                                        &dsi_ops,DRM_CONNECTOR_POLL_HPD, DRM_MODE_CONNECTOR_DSI);
                                 drm_connector_init(dev,&c_conn->base, &sde_connector_ops, connector_type);
                        sde_plane_init(dev, catalog->sspp[i].id, primary,(1UL << max_crtc_count) - 1, 0);
                            drm_universal_plane_init(dev, plane, 0xff, &sde_plane_funcs, psde->formats,/* plane初始化 */
                                                    psde->nformats,NULL, type, NULL);
                        sde_crtc_init(dev, primary_planes[i]); /* Create one CRTC per encoder */
                            drm_crtc_init_with_planes(dev, crtc, plane, NULL, &sde_crtc_funcs, NULL);
                            drm_crtc_helper_add(crtc, &sde_crtc_helper_funcs);
                        priv->planes[i]->possible_crtcs =(1 << priv->num_crtcs) - 1; //plane to crtcs
                        priv->encoders[i]->possible_crtcs = (1 << priv->num_crtcs) - 1;//crtcs ti encoder 
                        /* 一个CRTC 可以连接多个 Encoder 用于实现多屏显示 */

PLANE
Linux DRM PLANE完全解析
PLANE 代表显示图层,每个 CRTC 需要定义一个 primary plane 以及可选的 overlay plane、cursor plane
PLANE 存在的意义主要有两点:增强系统灵活性、提高系统性能
(1)对于背景和光标等变化不频繁的基本图显输出,可用通用 plane 来实现。而那些频繁变化则可由专用的 plane 来实现。
(2)plane 具备图像缩放、剪裁、多图层叠加等基本的图像处理功能,因此,可以让 GPU 来将更多的精力放在图形渲染上。

drm_universal_plane_init
    type = drm_plane_type    
enum drm_plane_type
   DRM_PLANE_TYPE_OVERLAY,
   DRM_PLANE_TYPE_PRIMARY,
   DRM_PLANE_TYPE_CURSOR,
;
static const struct drm_plane_funcs sde_plane_funcs =
    .update_plane = drm_atomic_helper_update_plane,
    .disable_plane = drm_atomic_helper_disable_plane,
    .destroy = sde_plane_destroy,
    ...

CTRC
Linux DRM CRTC完全解析
DRM 下的 CRTC 代表 RGB 数据管道,从&drm_plane 接收像素数据并将其混合到一起,传输给下级显示设备&drm_encoder。由&drm_display_mode 控制时序。
CRTC 模块存在的意义主要有以下三点:
(1)统一协调 FB、DRM、用户空间代码之间对图显输出的控制
(2)图显模式的配置权回收到 kernel 空间,避免用户空间代码直接控制图显控制器引起 kernel panic
(3)kernel 空间实现的图显输出暂停与恢复相关代码,可以方便的调用图显控制器的配置函数

drm_crtc_init_with_planes  初始化 CRTC 模块
drm_crtc_helper_add        注册 CRTC 功能
static const struct drm_crtc_helper_funcs sde_crtc_helper_funcs =
    .mode_fixup = sde_crtc_mode_fixup,
    .disable = sde_crtc_disable,
    .atomic_enable = sde_crtc_enable,
    .atomic_check = sde_crtc_atomic_check,
    .atomic_begin = sde_crtc_atomic_begin,
    .atomic_flush = sde_crtc_atomic_flush,
;


ENCODER/CONNECTOR
kernel图显系统的DRM ENCODER和CONNECTOR模块
DRM ENCODER 和 CONNECTOR 模块由图显外设抽象而来,从传统意义上来讲,ENCODER 属于控制器部分,将内存的像素编码转换为显示器所需要的信号例如VGA、MIPI;而 CONNECTOR 包含外设 PHY 或者显示器参数,代表连接的显示设备连接状态,支持的视频模式等。这两部分紧密关联,因此,软件 DRM 架构下的两者需要按照一个整体来理解和实现。

drm_encoder_init
drm_encoder_helper_add
static const struct drm_encoder_helper_funcs sde_encoder_helper_funcs =
        .mode_set = sde_encoder_virt_mode_set,
        .disable = sde_encoder_virt_disable,
        .enable = sde_encoder_virt_enable,
        .atomic_check = sde_encoder_virt_atomic_check,
;

static const struct sde_connector_ops dsi_ops =
                .detect =     dsi_conn_detect,
                .get_modes =  dsi_connector_get_modes,
                .get_info =   dsi_display_get_info,
                .set_backlight = dsi_display_set_backlight,
                .clk_ctrl = dsi_display_clk_ctrl,
                .set_power = dsi_display_set_power,

drm_connector_init
static const struct drm_connector_funcs sde_connector_ops =
        .reset =                  sde_connector_atomic_reset,
        .detect =                 sde_connector_detect,
        .destroy =                sde_connector_destroy,
        .fill_modes =             sde_connector_fill_modes,

DSI/PANEL
kernel/msm-4.19/techpack/display/msm/dsi/dsi_display.c
kernel/msm-4.19/techpack/display/msm/dsi/dsi_ctrl.c
kernel/msm-4.19/techpack/display/msm/dsi/dsi_phy.c
kernel/msm-4.19/techpack/display/msm/dsi/dsi_panel.c

module_param_string(name, string, len, perm);    内核把字符串直接复制到程序中的字符数组
name是外部的参数名,string是内部的变量名,len是以string命名的buffer大小,perm表示sysfs的访问权限

static char dsi_display_primary[MAX_CMDLINE_PARAM_LEN];
module_param_string(dsi_display0, dsi_display_primary, MAX_CMDLINE_PARAM_LEN, 0600);

MODULE_PARM_DESC(dsi_display0,      参数格式的说明
   "msm_drm.dsi_display0=<display node>:<configX> where <display node> is 'primary dsi display node name' and
   <configX> where x represents index in the topology list");
cat /proc/cmdline:   msm_drm.dsi_display0=qcom,mdss_dsi_hx8399_1080_video

dsi_phy_drv_register();
    platform_driver_register(&dsi_phy_platform_driver);
dsi_ctrl_drv_register();
    platform_driver_register(&dsi_ctrl_driver);
dsi_display_parse_boot_display_selection();  /* 解析得到 boot_display.name boot_disp_en = true */
    platform_driver_register(&dsi_display_driver);

Linux Command Line 详细解析
saved_command_line 用于保存没有处理过的命令行参数

dsi_display_dev_probe
    boot_disp_en = true  /* 当前dsi控制器支持的panel列表中查找uefi传递的panel */
        of_find_node_by_name(mdp_node, boot_disp->name);
    else
        display_panel = getDisplayPanel();
            display_panel_name = strstr ( saved_command_line, "hx8399");
            panel_node = of_parse_phandle(node,"qcom,dsi-default-panel1", 0);
        dtsi:
         qcom,dsi-default-panel1 = <&dsi_hx8399_1080_video>;
    dsi_display_init(display);
        dsi_display_dev_init(display); 
            dsi_display_parse_dt(display);/* 解析dtsi包括TE,初始化一些handel 包括dsi_ctrl dsi_phy */
            dsi_display_res_init(display);
                dsi_panel_get(&display->pdev->dev, display->panel_node, display->parser_node,
                              display->display_type, display->cmdline_topology); ↓↓↓
                    dsi_panel_parse_power_cfg(panel);
                    dsi_panel_parse_bl_config(panel); /* parse dtsi */
                    drm_panel_add(&panel->drm_panel); /* Let DRM can get panel name */ 
        component_add(&pdev->dev, &dsi_display_comp_ops); 
        /* component框架中通过component_add添加管理多个组件的加载,保证都正常工作
         其中注册了两个函数 .bind = dsi_display_bind, .unbind = dsi_display_unbind */

Linux component子系统
kernel图显系统里DRM模块的注册与绑定
内核加载每个模块时间不定,component框架能保证在最后初始化的设备加载前,所需设备全部加载完毕。注册主要分为两类设备:
(1)master即超级设备,执行probe使用component_master_add_with_match函数注册自己到component框架中。
(2)component即普通设备,执行probe使用component_add函数注册自己到component框架中。

Linux notifier chain子系统
Linux内核中各个子系统相互依赖,当其中某个子系统状态发生改变时,就必须使用一定的机制告知使用其服务的其他子系统,以便其他子系统采取相应的措施。为满足这样的需求,内核实现了事件通知链机制(notification chain),而对应Display来说紧密相关就是TP,通过内核通知链通知TP进行Resume和Suspend等操作。
在msm_kms_init初始化的时候注册的函数,通过内核通知链去通知TP resume和suspend流程

.prepare_commit  = sde_kms_prepare_commit,
     _sde_kms_drm_check_dpms(state, DRM_PANEL_EARLY_EVENT_BLANK);
.complete_commit = sde_kms_complete_commit,
    _sde_kms_drm_check_dpms(old_state, DRM_PANEL_EVENT_BLANK);

    _sde_kms_drm_check_dpms
        sde_kms_get_blank(crtc->state, connector->state);
            case SDE_MODE_DPMS_ON:
                blank = DRM_PANEL_BLANK_UNBLANK; //new mode:亮屏操作
                break;
            case SDE_MODE_DPMS_LP1:
               case SDE_MODE_DPMS_LP2:
                blank = DRM_PANEL_BLANK_LP;
                break;
            case SDE_MODE_DPMS_OFF:
            default:
                blank = DRM_PANEL_BLANK_POWERDOWN;//new mode:灭屏操作
                break;
        notifier_data.data = &new_mode;
        drm_panel_notifier_call_chain(connector->panel,event, &notifier_data); 

tp driver:
    data->fb_notif.notifier_call = fb_notifier_callback;
    drm_panel_notifier_register(data->active_panel,&data->fb_notif);
fb_notifier_callback
    if (action == DRM_PANEL_EARLY_EVENT_BLANK && *transition == DRM_PANEL_BLANK_POWERDOWN)
        mxt_secure_touch_stop(mxt_dev_data, 0); 
    else if (action == DRM_PANEL_EVENT_BLANK)
        if (*transition == DRM_PANEL_BLANK_UNBLANK)
            mxt_resume(&mxt_dev_data->client->dev); /* resume */
       
        else if (*transition == DRM_PANEL_BLANK_POWERDOWN)
            mxt_suspend(&mxt_dev_data->client->dev);/* suspend */
       
   
————————————————
 

以上是关于Android Qcom lcd display 学习的主要内容,如果未能解决你的问题,请参考以下文章

Android Qcom LCD display 学习

Android Qcom lcd display 学习

Qcom android L ro.sf.lcd_density属性修改

QCOM LCD调试

[RK3288][Android6.0] Display驱动初始化流程小结

qcom Android Camera