본문 바로가기
Linux/DRM

drm_connector_helper_funcs 의 get_modes 다중 호출 처리

by Jayden77 2020. 12. 8.

이 장에서는 DRM core에서 drm_connector_helper_funcs의 get_modes를 여러번 호출했을때 중복 저장되는 display mode가 어떻게 처리되는지 확인해 보기로 한다. 

 

DRM core는 drm_connector_helper_funcs의 get_modes api를 호출해서 현재 연결된 connector의 display mode정보를

가져온다. 

struct drm_connector_helper_funcs {
...
	int (*get_modes)(struct drm_connector *connector);
...
};

 

이 get_modes는 보통 다음과 같이 구현되어 있다.

static int imx_ldb_connector_get_modes(struct drm_connector *connector)
{
        struct imx_ldb_channel *imx_ldb_ch = con_to_imx_ldb_ch(connector);
		...

        if (imx_ldb_ch->edid) {
                drm_mode_connector_update_edid_property(connector,
                                                        imx_ldb_ch->edid);
                num_modes = drm_add_edid_modes(connector, imx_ldb_ch->edid);
        }

        if (imx_ldb_ch->mode_valid) {
                struct drm_display_mode *mode;

                mode = drm_mode_create(connector->dev);
                if (!mode)
                        return -EINVAL;
                drm_mode_copy(mode, &imx_ldb_ch->mode);
                mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
                drm_mode_probed_add(connector, mode);
                num_modes++;
        }

        return num_modes;
}

HDMI나 DP처럼 edid를 읽어온 경우 drm_add_edid_modes를 그렇지 않고 LVDS panel처럼 display mode를 제공하는 경우는 drm_mode_probed_add를 호출하고 이 두함수는 최종적으로 다음 함수를 호출해서 display mode정보fmf connector에 저장한다. 

void drm_mode_probed_add(struct drm_connector *connector,
                         struct drm_display_mode *mode)
{
        WARN_ON(!mutex_is_locked(&connector->dev->mode_config.mutex));

        list_add_tail(&mode->head, &connector->probed_modes);
}
EXPORT_SYMBOL(drm_mode_probed_add);

 

그런데 get_modes은 부팅시나 부팅후 상황에 따라 여러번 호출되는 것을 볼수 있다. 그래서 connecto의 probed_mods에 display mode를 매번 추가 하는것처럼 보이는데 여러번 호출되어도 문제 없는지 확인해 보기로 했다. 

 

확인 결과 drm_helper_probe_single_connector_modes가 호출될때마다 drm_mode_connector_list_update 함수가 호출되면서 아래와 같이 중복되는 display mode를 제거해주고 있는것을 알게되었다.

void drm_mode_connector_list_update(struct drm_connector *connector)
{
        struct drm_display_mode *pmode, *pt;

        WARN_ON(!mutex_is_locked(&connector->dev->mode_config.mutex));

        list_for_each_entry_safe(pmode, pt, &connector->probed_modes, head) {
                struct drm_display_mode *mode;
                bool found_it = false;

                /* go through current modes checking for the new probed mode */
                list_for_each_entry(mode, &connector->modes, head) {
                        if (!drm_mode_equal(pmode, mode))
                                continue;

                        found_it = true;

                        /*
                         * If the old matching mode is stale (ie. left over
                         * from a previous probe) just replace it outright.
                         * Otherwise just merge the type bits between all
                         * equal probed modes.
                         *
                         * If two probed modes are considered equal, pick the
                         * actual timings from the one that's marked as
                         * preferred (in case the match isn't 100%). If
                         * multiple or zero preferred modes are present, favor
                         * the mode added to the probed_modes list first.
                         */
                        if (mode->status == MODE_STALE) {
                                drm_mode_copy(mode, pmode);
                        } else if ((mode->type & DRM_MODE_TYPE_PREFERRED) == 0 &&
                                   (pmode->type & DRM_MODE_TYPE_PREFERRED) != 0) {
                                pmode->type |= mode->type;
                                drm_mode_copy(mode, pmode);
                        } else {
                                mode->type |= pmode->type;
                        }
                        pr_err("%s remove duplicated list", __func__);
                        list_del(&pmode->head);
                        drm_mode_destroy(connector->dev, pmode);
                        break;
                }

                if (!found_it) {
                        list_move_tail(&pmode->head, &connector->modes);
                }
        }
}
EXPORT_SYMBOL(drm_mode_connector_list_update);

 

 

'Linux > DRM' 카테고리의 다른 글

DRM flip-work  (0) 2022.01.24
drm_atomic_helper_commit_planes  (0) 2021.11.08