drmModeModeInfoPtr IntelHWComposerDrm::selectDisplayDrmMode(int disp, intel_display_mode_t *displayMode) { if (disp != OUTPUT_HDMI) return NULL; drmModeModeInfoPtr mode = getOutputMode(OUTPUT_HDMI); // If mode no change, return current mode if (displayMode && !isModeChanged(mode, displayMode)) return mode; if (mHdmiConnector != NULL) { freeConnector(mHdmiConnector); mHdmiConnector = NULL; } drmModeConnectorPtr connector; connector = getHdmiConnector(); if (!connector) { ALOGW("%s: fail to get drm connector\n", __func__); return NULL; } mode = getSelectMode(displayMode, connector); if (!mode) { ALOGW("%s: fail to get selected mode or any other mode! \n", __func__); return NULL; } // update current mode to be selected setOutputMode(OUTPUT_HDMI, mode, 1); //freeConnector(connector); mode = getOutputMode(OUTPUT_HDMI); ALOGD("%s: mode is %dx%d@%dHz, 0x%x\n", __func__, mode->hdisplay, mode->vdisplay, mode->vrefresh, mode->flags); return mode; }
bool IntelHWComposerDrm::setDisplayDrmMode(int disp, uint32_t fb_handler, drmModeModeInfoPtr mode) { drmModeConnectorPtr connector = NULL; uint32_t crtc_id = 0; uint32_t fb_id = 0; if (mDrmFd < 0) { ALOGE("%s: invalid drm FD\n", __func__); return false; } if (!fb_handler) { ALOGE("%s: invalid fb handler\n", __func__); return false; } if (disp != OUTPUT_HDMI) return false; connector = getHdmiConnector(); if (!connector) { ALOGW("%s: fail to get drm connector\n", __func__); return false; } setOutputConnection(OUTPUT_HDMI, connector->connection); // re-check connection status if (connector->connection != DRM_MODE_CONNECTED) { freeConnector(connector); return false; } // get fb_id if (setupDrmFb(OUTPUT_HDMI, fb_handler, mode)) fb_id = getOutputFBId(OUTPUT_HDMI); if (!fb_id) { ALOGW("%s: fail to get drm fb id\n", __func__); freeConnector(connector); return false; } // get crtc_id crtc_id = getCrtcId(OUTPUT_HDMI); if (!crtc_id) { ALOGW("%s: fail to get drm crtc id\n", __func__); freeConnector(connector); return false; } // crtc mode setting int ret = drmModeSetCrtc(mDrmFd, crtc_id, fb_id, 0, 0, &connector->connector_id, 1, mode); if (ret) { ALOGW("drm Mode Set Crtc Error: 0x%x!\n", ret); freeConnector(connector); return false; } return true; }
// return number of unique modes int drm_hdmi_getModeInfo( int *pWidth, int *pHeight, int *pRefresh, int *pInterlace, int *pRatio) { if (!gDrmCxt.hdmiSupported || !gDrmCxt.connected) { LOGE("%s: HDMI is not supported or not connected.", __func__); return 0; } drmModeConnector *connector = getHdmiConnector(); if (connector == NULL) { LOGE("%s: Failed to get HDMI connector.", __func__); return 0; } if (connector->count_modes < 0 || connector->count_modes > HDMI_TIMING_MAX) { LOGW("%s: unexpected count of modes %d", __func__, connector->count_modes); drmModeFreeConnector(connector); gDrmCxt.hdmiConnector = NULL; return 0; } int i, valid_mode_count = 0; // get resolution of each mode for (i = 0; i < connector->count_modes; i++) { unsigned int temp_hdisplay = connector->modes[i].hdisplay; unsigned int temp_vdisplay = connector->modes[i].vdisplay; unsigned int temp_refresh = connector->modes[i].vrefresh; // Only extract the required flags for comparison unsigned int temp_flags = connector->modes[i].flags & (DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_PAR16_9 | DRM_MODE_FLAG_PAR4_3); // re-traverse the connector mode list to see if there is // same resolution and refresh. The same mode will not be // counted into valid mode. int j = i; unsigned int flags = 0; while ((--j) >= 0) { flags = connector->modes[j].flags & (DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_PAR16_9 | DRM_MODE_FLAG_PAR4_3); if (temp_hdisplay == connector->modes[j].hdisplay && temp_vdisplay == connector->modes[j].vdisplay && temp_refresh == connector->modes[j].vrefresh && temp_flags == flags) { LOGV("Found duplicated mode: %dx%d@%d with flags = 0x%x", temp_hdisplay, temp_vdisplay, temp_refresh, temp_flags); break; } } // if j<0, mode is not duplicated if (j < 0) { // pWidth, pHeight, etc can be NULL to get mode count. if (pWidth && pHeight && pRefresh && pInterlace && pRatio) { // record the valid mode info into mode array pWidth[valid_mode_count] = temp_hdisplay; pHeight[valid_mode_count] = temp_vdisplay; pRefresh[valid_mode_count] = temp_refresh; if (temp_flags & DRM_MODE_FLAG_INTERLACE) pInterlace[valid_mode_count] = 1; else pInterlace[valid_mode_count] = 0; if (temp_flags & DRM_MODE_FLAG_PAR16_9) pRatio[valid_mode_count] = 2; else if (temp_flags & DRM_MODE_FLAG_PAR4_3) pRatio[valid_mode_count] = 1; else pRatio[valid_mode_count] = 0; LOGV("Adding mode[%d]: %dx%d@%d with flags = 0x%x\n", valid_mode_count, temp_hdisplay, temp_vdisplay, temp_refresh, temp_flags); } valid_mode_count++; } } return valid_mode_count; }
bool drm_hdmi_getTiming(int mode, MDSHDMITiming* info) { if (info == NULL) return false; if (!gDrmCxt.hdmiSupported || !gDrmCxt.connected) { LOGE("%s: HDMI is not supported or not connected.", __func__); return false; } LOGV("Initial HDMI timing is %dx%d@%dHz", info->width, info->height, info->refresh); if (gDrmCxt.modeValid) { LOGI("use user-selected mode."); memcpy(info, &gDrmCxt.modeSelected, sizeof(MDSHDMITiming)); return true; } drmModeConnector *connector = getHdmiConnector(); if (connector == NULL) { LOGE("%s: get HDMI connector failed.", __func__); return false; } if (mode != DRM_HDMI_VIDEO_EXT) { drm_hdmi_setTiming(connector, gDrmCxt.preferredModeIndex, info); return true; } // Video extended mode, aspect ratio/interlace matching is not supported yet. // select 720P or 1080P as output if (info->width <= 1280 && info->height <= 720) { info->width = 1280; info->height = 720; } else { info->width = 1920; info->height = 1080; } // Find number of matched modes by resolution int i, num_matched = 0; int index_matched[HDMI_TIMING_MAX]; for (i = 0; i < connector->count_modes; i++) { if (info->width == connector->modes[i].hdisplay && info->height == connector->modes[i].vdisplay) { index_matched[num_matched++] = i; } if (num_matched >= HDMI_TIMING_MAX) { LOGW("number of matched modes exceeds the limit."); break; } } if (num_matched == 0) { LOGW("%s: Number of matched modes is 0.", __func__); // Use preferred mode drm_hdmi_setTiming(connector, gDrmCxt.preferredModeIndex, info); return true; } // select the best one from the matched modes int max_vrefresh_index = index_matched[0]; int best_vrefresh_index = -1; drmModeModeInfoPtr modeInfo; for (i = 0; i < num_matched; i++) { modeInfo = connector->modes + index_matched[i]; if (modeInfo->vrefresh > connector->modes[max_vrefresh_index].vrefresh) { max_vrefresh_index = index_matched[i]; } else if (modeInfo->vrefresh == connector->modes[max_vrefresh_index].vrefresh) { if (drm_is_preferred_flags(modeInfo->flags)) { max_vrefresh_index = index_matched[i]; } } if (info->refresh == 0) continue; if ((modeInfo->vrefresh % info->refresh) == 0) { if (modeInfo->vrefresh == info->refresh) { if (drm_is_preferred_flags(modeInfo->flags)) { best_vrefresh_index = index_matched[i]; break; } } else if (best_vrefresh_index == -1 || modeInfo->vrefresh > connector->modes[best_vrefresh_index].vrefresh) { best_vrefresh_index = index_matched[i]; } } } if (best_vrefresh_index != -1) { drm_hdmi_setTiming(connector, best_vrefresh_index, info); } else { drm_hdmi_setTiming(connector, max_vrefresh_index, info); } return true; }
// return 0 - not connected, 1 - HDMI connected, 2 - DVI connected int drm_hdmi_getConnectionStatus() { if (!gDrmCxt.hdmiSupported) return 0; if (gDrmCxt.hdmiConnector) drmModeFreeConnector(gDrmCxt.hdmiConnector); gDrmCxt.hdmiConnector = NULL; // reset connection status gDrmCxt.connected = false; drmModeConnector *connector = getHdmiConnector(); if (connector == NULL) return 0; // Read EDID, and check whether it's HDMI or DVI interface int ret = 0, i, j; for (i = 0; i < connector->count_props; i++) { drmModePropertyPtr props = drmModeGetProperty(gDrmCxt.drmFD, connector->props[i]); if (!props) continue; if (props->name == NULL || strncmp(props->name, "EDID", sizeof("EDID")) != 0) { drmModeFreeProperty(props); continue; } uint64_t* edid = &connector->prop_values[i]; drmModePropertyBlobPtr edidBlob = drmModeGetPropertyBlob(gDrmCxt.drmFD, *edid); if (edidBlob == NULL || edidBlob->data == NULL || edidBlob->length < HDMI_TIMING_MAX) { LOGE("%s: Invalid EDID Blob.", __func__); drmModeFreeProperty(props); ret = 0; break; } char* edid_binary = (char *)edidBlob->data; // offset of product_info char* product_info = edid_binary + 8; gDrmCxt.connected = true; if (memcmp(gDrmCxt.productInfo, product_info, EDID_PRODUCT_INFO_LEN)) { LOGI("A new HDMI sink is connected."); gDrmCxt.newDevice = true; gDrmCxt.modeValid = false; memcpy(gDrmCxt.productInfo, product_info, EDID_PRODUCT_INFO_LEN); } drm_select_preferredmode(connector); ret = 2; // DVI if (edid_binary[126] == 0) { drmModeFreeProperty(props); break; } // search VSDB in extend edid for (j = 0; j <= HDMI_TIMING_MAX - 3; j++) { int n = HDMI_TIMING_MAX + j; if (edid_binary[n] == 0x03 && edid_binary[n+1] == 0x0c && edid_binary[n+2] == 0x00) { ret = 1; //HDMI break; } } drmModeFreeProperty(props); break; } LOGV("%s: connect status is %d", __func__, ret); return ret; }