| #include <pthread.h> |
| |
| #include "modepolicyfunc.h" |
| #include "DisplayAdapter.h" |
| #include "modepolicy_aml.h" |
| |
| weston_ctx_list *gCtx_list = NULL; |
| static int g_activeLevel = 3; |
| |
| /* if prop set need change mode, add it to this */ |
| static char *prop_changed_and_mode[] = { |
| "color_space", |
| "color_depth", |
| "dv_mode", |
| "FRAC_RATE_POLICY" |
| }; |
| static int weston_set_property(int id, const char *name, int value); |
| |
| static weston_ctx_list *weston_get_ctx_list() |
| { |
| if (!gCtx_list) { |
| gCtx_list = (weston_ctx_list *)calloc(1, sizeof(*gCtx_list)); |
| wl_list_init(&gCtx_list->ctx_list); |
| } |
| return gCtx_list; |
| } |
| |
| static weston_ctx *weston_get_ctx_for_head(struct weston_head *head) |
| { |
| weston_ctx_list *ctx_list = weston_get_ctx_list(); |
| weston_ctx *ctx, *tmp; |
| |
| wl_list_for_each(ctx, &ctx_list->ctx_list, link) { |
| if (ctx->head == head) |
| return ctx; |
| } |
| return NULL; |
| } |
| |
| weston_ctx *weston_get_ctx() |
| { |
| weston_ctx_list *ctx_list = weston_get_ctx_list(); |
| |
| return weston_get_ctx_for_head(ctx_list->head); |
| } |
| |
| static void weston_prop_list_init(weston_ctx *ctx) |
| { |
| prop_info *info, *tmp; |
| if (!wl_list_empty(&ctx->prop_list)) { |
| wl_list_for_each_safe(info, tmp, &ctx->prop_list, link) { |
| wl_list_remove(&info->link); |
| free(info); |
| } |
| } |
| wl_list_init(&ctx->prop_list); |
| } |
| |
| static int weston_add_property_item(weston_ctx *ctx, |
| const char *name, int id, int prop_id) |
| { |
| prop_info *info; |
| |
| wl_list_for_each(info, &ctx->prop_list, link) { |
| if (!strcmp(name, info->name) && info->item_id == id) |
| return 0; |
| } |
| |
| info = (prop_info *)calloc(1, sizeof(*info)); |
| memcpy(info->name, name, sizeof(info->name)); |
| info->item_id = id; |
| info->prop_id = prop_id; |
| info->need_change = 0; |
| MESON_LOGD("ctx: %p, name: %s, id: %d, prop_id: %d\n", ctx, name, id, prop_id); |
| wl_list_insert(&ctx->prop_list, &info->link); |
| return 0; |
| } |
| |
| static int weston_add_property(weston_ctx *ctx, int id, int type) |
| { |
| int i, len; |
| int value = 0; |
| drmModePropertyRes *propRes; |
| drmModeObjectProperties *props; |
| |
| props = drmModeObjectGetProperties( ctx->drm_fd, id, type ); |
| if ( props ) { |
| for ( i= 0; i < props->count_props; ++i ) { |
| propRes = drmModeGetProperty( ctx->drm_fd, props->props[i] ); |
| if ( propRes ) { |
| weston_add_property_item(ctx, propRes->name, id, props->props[i]); |
| drmModeFreeProperty( propRes ); |
| propRes = 0; |
| } |
| } |
| drmModeFreeObjectProperties( props ); |
| props = 0; |
| } |
| return value; |
| } |
| |
| static int weston_get_property_value(weston_ctx *ctx, const char * key, int id, int type) |
| { |
| int i, len; |
| int value = 0; |
| drmModePropertyRes *propRes; |
| drmModeObjectProperties *props; |
| |
| props = drmModeObjectGetProperties( ctx->drm_fd, id, type ); |
| if ( props ) { |
| for ( i= 0; i < props->count_props; ++i ) { |
| propRes = drmModeGetProperty( ctx->drm_fd, props->props[i] ); |
| if ( propRes ) { |
| len = strlen(propRes->name); |
| if ( !strncmp( propRes->name, key, len) ) { |
| MESON_LOGD("property %d name (%s) value (%lld)", |
| props->props[i], propRes->name, props->prop_values[i] ); |
| value = props->prop_values[i]; |
| drmModeFreeProperty( propRes ); |
| propRes = 0; |
| break; |
| } |
| drmModeFreeProperty( propRes ); |
| propRes = 0; |
| } |
| } |
| drmModeFreeObjectProperties( props ); |
| props = 0; |
| } |
| return value; |
| } |
| |
| static int weston_get_connector_property(const char * key) |
| { |
| weston_ctx *ctx = weston_get_ctx(); |
| |
| if (!ctx) |
| return 0; |
| |
| return weston_get_property_value(ctx, key, ctx->conn->connector_id, DRM_MODE_OBJECT_CONNECTOR); |
| } |
| |
| static int weston_get_crtc_property(const char * key) |
| { |
| weston_ctx *ctx = weston_get_ctx(); |
| |
| if (!ctx) |
| return 0; |
| |
| return weston_get_property_value(ctx, key, ctx->crtc->crtc_id, DRM_MODE_OBJECT_CRTC); |
| } |
| |
| static bool weston_get_mode_for_env(weston_ctx *ctx, char *out) |
| { |
| const char *value = NULL; |
| |
| if (!ctx || !ctx->conn) |
| return false; |
| |
| switch (ctx->conn->connector_type) { |
| case DRM_MODE_CONNECTOR_HDMIA: |
| value = bootenv_get("hdmimode"); |
| break; |
| case DRM_MODE_CONNECTOR_LVDS: |
| value = bootenv_get("outputmode"); |
| break; |
| default: |
| break; |
| } |
| if (value) |
| strcpy(out, value); |
| else |
| strcpy(out, "none"); |
| return true; |
| } |
| |
| static bool get_mode_name_for_weston_mode( |
| weston_ctx *ctx, |
| struct weston_mode *mode, |
| char *out) |
| { |
| int i; |
| int current_interlaced; |
| const char *value; |
| |
| if (mode && getModeNameForPix(out, mode->width, mode->height, mode->refresh, mode->flags)) |
| return true; |
| |
| weston_get_mode_for_env(ctx, out); |
| MESON_LOGD("out: %s", out); |
| return false; |
| } |
| |
| static bool weston_get_mode(char *mode) |
| { |
| weston_ctx *ctx = weston_get_ctx(); |
| struct weston_mode *wmode = NULL; |
| |
| if (!wmode && ctx && ctx->current_mode.width != 0) |
| wmode = &ctx->current_mode; |
| |
| return get_mode_name_for_weston_mode(ctx, wmode, mode); |
| } |
| |
| bool WestonGetUbootIsBestmode() |
| { |
| const char *isbestmode = bootenv_get("is.bestmode"); |
| if ( isbestmode != NULL ) { |
| if (strcmp("false", isbestmode) == 0) |
| return false; |
| else |
| return true; |
| } else |
| return true; |
| } |
| |
| bool westonGetDrmModeInfoByName( weston_ctx *ctx, const char *mode, drmModeModeInfo *out_mode ) |
| { |
| bool result= false; |
| |
| if ( ctx && mode && out_mode ) { |
| int width = -1, height = -1, rate = -1; |
| bool interlaced = false; |
| bool haveTarget = false; |
| bool useBestRate = true; |
| |
| MESON_LOGD("%s: mode (%s)", __func__, mode); |
| |
| if ( sscanf( mode, "%dx%dp%d", &width, &height, &rate ) == 3 ) { |
| interlaced = false; |
| } else if ( sscanf( mode, "%dx%di%d", &width, &height, &rate ) == 3 ) { |
| interlaced = true; |
| } else if ( sscanf( mode, "%dx%dx%d", &width, &height, &rate ) == 3 ) { |
| interlaced = false; |
| } else if ( sscanf( mode, "%dx%d", &width, &height ) == 2 ) { |
| int len = strlen(mode); |
| interlaced = (mode[len - 1] == 'i'); |
| } else if (sscanf( mode, "%dp%dhz", &height,&rate ) == 2) { |
| interlaced = false; |
| width = -1; |
| } else if (sscanf( mode, "%di%dhz", &height,&rate ) == 2) { |
| interlaced = true; |
| width = -1; |
| } else if ( sscanf( mode, "%dp", &height ) == 1 ) { |
| int len = strlen(mode); |
| interlaced = (mode[len - 1] == 'i'); |
| width = -1; |
| } else if (sscanf( mode, "smpte%dhz", &rate ) == 1) { |
| interlaced = false; |
| height = 2160; |
| width = 4096; |
| } |
| |
| if ( height > 0 ) { |
| if ( width < 0 ) { |
| switch ( height ) { |
| case 480: |
| case 576: |
| width = 720; |
| break; |
| case 720: |
| width = 1280; |
| break; |
| case 1080: |
| width = 1920; |
| break; |
| case 1440: |
| width = 2560; |
| break; |
| case 2160: |
| width = 3840; |
| break; |
| case 2880: |
| width = 5120; |
| break; |
| case 4320: |
| width = 7680; |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| MESON_LOGD("%s w %d h %d rate %d", __func__, width, height, rate); |
| if ( rate >= 0 ) |
| useBestRate = false; |
| |
| if ( (width > 0) && (height > 0) ) { |
| if ( ctx->drm_fd >= 0 ) { |
| drmModeRes *res = 0; |
| drmModeConnector *conn = 0; |
| |
| res = drmModeGetResources( ctx->drm_fd ); |
| if ( res ) { |
| int i; |
| for ( i = 0; i < res->count_connectors; ++i ) { |
| conn = drmModeGetConnector( ctx->drm_fd, res->connectors[i] ); |
| if ( conn ) { |
| if ( conn->count_modes && (conn->connection == DRM_MODE_CONNECTED) ) |
| break; |
| drmModeFreeConnector(conn); |
| conn = 0; |
| } |
| } |
| if ( conn ) { |
| uint32_t rateBest = 0; |
| int miBest = -1; |
| |
| MESON_LOGD("%s: want %dx%dx%d interlaced %d use best rate %d", |
| __func__, width, height, rate, interlaced, useBestRate); |
| for ( i = 0; i < conn->count_modes; ++i ) { |
| MESON_LOGD("%s: consider mode %d: %dx%dx%d (%s) type 0x%x flags 0x%x", |
| __func__, i, conn->modes[i].hdisplay, conn->modes[i].vdisplay, |
| conn->modes[i].vrefresh, conn->modes[i].name, |
| conn->modes[i].type, conn->modes[i].flags ); |
| |
| if (!strcmp(conn->modes[i].name, mode)) |
| miBest = i; |
| |
| if ( (conn->modes[i].hdisplay == width) && |
| (conn->modes[i].vdisplay == height) ) { |
| bool modeIsInterlaced = (conn->modes[i].flags & DRM_MODE_FLAG_INTERLACE); |
| if ( modeIsInterlaced != interlaced ) |
| continue; |
| |
| if ( useBestRate ) { |
| if ( conn->modes[i].vrefresh > rateBest ) { |
| rateBest = conn->modes[i].vrefresh; |
| miBest = i; |
| } |
| } else if ( conn->modes[i].vrefresh == rate ) { |
| miBest = i; |
| break; |
| } |
| } |
| } |
| |
| if ( miBest >= 0 ) { |
| *out_mode = conn->modes[miBest]; |
| |
| MESON_LOGI("%s: choosing output mode: %dx%dx%d (%s) flags 0x%x", |
| __func__, |
| out_mode->hdisplay, |
| out_mode->vdisplay, |
| out_mode->vrefresh, |
| out_mode->name, |
| out_mode->flags ); |
| |
| result= true; |
| } else { |
| MESON_LOGE("%s: failed to find a mode matching (%s)", __func__, mode); |
| } |
| |
| drmModeFreeConnector( conn ); |
| } else { |
| MESON_LOGE("%s: unable to get connector for card", __func__); |
| } |
| drmModeFreeResources(res); |
| } else { |
| MESON_LOGE("%s: unable to get card resources", __func__); |
| } |
| } else { |
| MESON_LOGE("%s: no open device", __func__); |
| } |
| } else { |
| MESON_LOGE("%s: unable to parse mode (%s)", __func__, mode); |
| } |
| } |
| |
| return result; |
| } |
| |
| static bool weston_set_mode(const char *mode) |
| { |
| weston_ctx *ctx = weston_get_ctx(); |
| drmModeModeInfo info = {0}; |
| |
| if (!ctx) |
| return false; |
| |
| if (westonGetDrmModeInfoByName(ctx, mode, &info)) { |
| ctx->next_mode.width = info.hdisplay; |
| ctx->next_mode.height = info.vdisplay; |
| ctx->next_mode.refresh = info.vrefresh * 1000; |
| ctx->next_mode.flags = info.flags; |
| setBootConfig(info.name, WestonGetUbootIsBestmode()); |
| ctx->mode_update = true; |
| ctx->mode_changed = true; |
| } |
| return 0; |
| } |
| |
| static int weston_get_property(int id, const char *name, char *buf) |
| { |
| weston_ctx *ctx = weston_get_ctx(); |
| int value = 0; |
| |
| if (!ctx) |
| return 0; |
| |
| if (!ctx->crtc || !ctx->conn) |
| return 0; |
| |
| if (ctx->crtc->crtc_id == id) |
| value = weston_get_crtc_property(name); |
| |
| if (ctx->conn->connector_id == id) |
| value = weston_get_connector_property(name); |
| |
| snprintf(buf, MAX_BUF_LEN, "%d", value); |
| return 0; |
| } |
| |
| static int weston_set_property(int id, const char *name, int value) |
| { |
| prop_info *info; |
| weston_ctx *ctx = weston_get_ctx(); |
| |
| if (!ctx) |
| return 0; |
| |
| wl_list_for_each(info, &ctx->prop_list, link) { |
| if (!strcmp(name, info->name) && info->item_id == id) { |
| MESON_LOGD("name: %s, id: %d, prop: %d, value: %d\n", |
| name, id, info->prop_id, value); |
| info->value = value; |
| info->need_change = 1; |
| ctx->prop_changed = true; |
| break; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static bool weston_set_colorattribute(const char *attr) |
| { |
| weston_ctx *ctx = weston_get_ctx(); |
| int depth = -1, value = -1; // default none |
| |
| if ( attr && strlen(attr) > 3 ) { |
| if ( strstr (attr, "rgb") ) |
| value = 0; |
| else if ( strstr (attr, "422") ) |
| value = 1; |
| else if ( strstr (attr, "444") ) |
| value = 2; |
| else if ( strstr (attr, "420") ) |
| value = 3; |
| sscanf(attr + 4, "%dbit", &depth); |
| } |
| if (ctx && ctx->conn && ctx->conn->connector_type == DRM_MODE_CONNECTOR_HDMIA) { |
| weston_set_property(ctx->conn->connector_id, "color_space", value); |
| weston_set_property(ctx->conn->connector_id, "color_depth", depth); |
| } |
| return 0; |
| } |
| |
| void weston_set_scaling_position(int x, int y, int w, int h) |
| { |
| weston_ctx *ctx = weston_get_ctx(); |
| const char *value = bootenv_get("scaling"); |
| int scaling = -1; |
| |
| if (value) |
| scaling = atoi(value); |
| |
| if (ctx && ctx->scaling != scaling) |
| ctx->scaling = scaling; |
| } |
| |
| CompositorFunctionCallBack callback = { |
| .get_mode = weston_get_mode, |
| .set_mode = weston_set_mode, |
| .set_colorattribute = weston_set_colorattribute, |
| .get_property = weston_get_property, |
| .set_property = weston_set_property, |
| .set_scaling_position = weston_set_scaling_position, |
| }; |
| |
| static char *get_color_space_by_value(int value) |
| { |
| char *str; |
| |
| switch ( value ) { |
| case 0: |
| str = "rgb"; |
| break; |
| case 1: |
| str = "422"; |
| break; |
| case 2: |
| str = "444"; |
| break; |
| case 3: |
| default: |
| str = "420"; |
| break; |
| } |
| return str; |
| } |
| |
| static int get_property_value(char *name) |
| { |
| prop_info *info; |
| weston_ctx *ctx = weston_get_ctx(); |
| |
| if (!ctx) |
| return 0; |
| |
| wl_list_for_each(info, &ctx->prop_list, link) { |
| if (!strcmp(name, info->name)) { |
| if (info->need_change == 1) |
| return info->value; |
| } |
| } |
| return -1; |
| } |
| |
| static int get_id_by_name(const char *name) |
| { |
| prop_info *info; |
| weston_ctx *ctx = weston_get_ctx(); |
| |
| if (!ctx) |
| return -1; |
| |
| wl_list_for_each(info, &ctx->prop_list, link) { |
| if (!strcmp(name, info->name)) |
| return info->item_id; |
| } |
| return -1; |
| } |
| |
| static int mode_policy_parse_other(weston_ctx *ctx, const char *name, int value) |
| { |
| char attrvalue[32] = { 0 }; |
| |
| if (STRCMPS(name, "scaling") == 0) { |
| if (ctx && ctx->scaling != value) |
| ctx->scaling = value; |
| |
| sprintf(attrvalue, "%d", value); |
| bootenv_update("scaling", attrvalue); |
| return 0; |
| } |
| return -1; |
| } |
| |
| static void set_color_attribute(char *space, int depth) |
| { |
| char attrvalue[32] = { 0 }; |
| char mode[] = { 0 }; |
| |
| sprintf(attrvalue, "%s,%dbit", space, depth); |
| setColorSpace(attrvalue); |
| weston_get_mode(mode); |
| setBootConfig(mode, false); |
| } |
| |
| int mode_policy_set_property(const char *name, int value) |
| { |
| char attrvalue[32] = { 0 }; |
| int color_space = 0; |
| int color_depth = 0; |
| int id = get_id_by_name(name); |
| weston_ctx *ctx = weston_get_ctx(); |
| MESON_LOGI("id: %d, name: %s, value: %d", id, name, value); |
| |
| if (!ctx) |
| return 0; |
| |
| if (!ctx->conn || ctx->conn->connector_type != DRM_MODE_CONNECTOR_HDMIA) { |
| MESON_LOGW("isn't hdmi, don't set property"); |
| return 0; |
| } |
| |
| initModePolicyFun(ctx->crtc, ctx->conn, callback); |
| if (value < 0 || id < 0) |
| return mode_policy_parse_other(ctx, name, value); |
| |
| if (strncmp(name, "dv_mode", sizeof("dv_mode")) == 0) { |
| setDvMode(value == 0 ? 1 : (value == 1 ? 2 : 0)); |
| return 0; |
| } else if (strncmp(name, "color_depth", sizeof("color_depth")) == 0) { |
| color_space = get_property_value("color_space"); |
| if (color_space >= 0) { |
| set_color_attribute(get_color_space_by_value(color_space), value); |
| return 0; |
| } |
| } else if (strncmp(name, "color_space", sizeof("color_space")) == 0) { |
| color_depth = get_property_value("color_depth"); |
| if (color_depth >= 0) { |
| set_color_attribute(get_color_space_by_value(value), color_depth); |
| return 0; |
| } |
| } else if (strncmp(name, "meson.crtc.hdr_policy", sizeof("meson.crtc.hdr_policy")) == 0) { |
| sprintf(attrvalue, "%d", value); |
| bootenv_update("hdr_policy", attrvalue); |
| } |
| |
| return weston_set_property(id, name, value); |
| } |
| |
| int mode_policy_get_scaling() |
| { |
| const char *env = bootenv_get("scaling"); |
| int scaling = DRM_MAX_SCALING; |
| |
| if (env) |
| scaling = atoi(env); |
| |
| if (scaling < DRM_MIN_SCALING) |
| scaling = DRM_MIN_SCALING; |
| |
| if (scaling > DRM_MAX_SCALING) |
| scaling = DRM_MAX_SCALING; |
| |
| return scaling; |
| } |
| |
| int mode_policy_set_scaling(int scaling) |
| { |
| weston_ctx *ctx = weston_get_ctx(); |
| char attrvalue[32] = { 0 }; |
| |
| if (!ctx) |
| return -1; |
| |
| if (scaling != ctx->scaling) { |
| ctx->scaling = scaling; |
| sprintf(attrvalue, "%d", scaling); |
| bootenv_update("scaling", attrvalue); |
| } |
| return 0; |
| } |
| |
| static struct weston_mode |
| drm_mode_to_weston_mode(drmModeModeInfoPtr info) |
| { |
| struct weston_mode m; |
| |
| m.width = info->hdisplay; |
| m.height = info->vdisplay; |
| //mhz:refresh * 1000 |
| m.refresh = info->vrefresh * 1000; |
| //hack: use weston_mode's flags as drm_mode's flags. |
| m.flags = info->flags; |
| m.aspect_ratio = WESTON_MODE_PIC_AR_NONE; |
| |
| return m; |
| } |
| |
| drmModeCrtc *weston_get_crtc_for_conn(int drm_fd, drmModeConnector *conn) |
| { |
| drmModeEncoder *encoder; |
| drmModeCrtc *crtc = NULL; |
| drmModeRes *res = 0; |
| int i, j, k; |
| /* Get the current mode on the crtc that's currently driving |
| * this connector. */ |
| res = drmModeGetResources(drm_fd); |
| if ( res ) { |
| for (i= 0; i < res->count_encoders; ++i ) { |
| uint32_t crtcId= 0; |
| bool found= false; |
| encoder = drmModeGetEncoder(drm_fd, res->encoders[i]); |
| if (encoder && conn && (encoder->encoder_id == conn->encoder_id)) { |
| found = true; |
| break; |
| } |
| for (j = 0; j < res->count_crtcs; j++) { |
| if (encoder->possible_crtcs & (1 << j)) { |
| crtcId= res->crtcs[j]; |
| for (k = 0; k < res->count_crtcs; k++) { |
| if (res->crtcs[k] == crtcId) { |
| drmModeFreeEncoder(encoder); |
| encoder= drmModeGetEncoder(drm_fd, res->encoders[k]); |
| encoder->crtc_id= crtcId; |
| MESON_LOGD("got enc %p crtc id %d, conn id: %d", drm_fd, crtcId, conn->connector_id); |
| found = true; |
| break; |
| } |
| } |
| if (found) { |
| break; |
| } |
| } |
| } |
| if (!found) { |
| drmModeFreeEncoder(encoder); |
| encoder= 0; |
| } |
| if (found) { |
| break; |
| } |
| } |
| if (encoder != NULL) { |
| crtc = drmModeGetCrtc(drm_fd, encoder->crtc_id); |
| drmModeFreeEncoder(encoder); |
| } |
| drmModeFreeResources( res ); |
| res = 0; |
| } else { |
| MESON_LOGD("unable to get card resources"); |
| } |
| return crtc; |
| } |
| |
| void mode_policy_set_head(struct weston_head *head) |
| { |
| weston_ctx_list *ctx_list = weston_get_ctx_list(); |
| ctx_list->head = head; |
| } |
| |
| void mode_policy_set_output(struct weston_output *output) |
| { |
| weston_ctx_list *ctx_list = weston_get_ctx_list(); |
| weston_ctx *ctx, *tmp; |
| |
| wl_list_for_each_safe(ctx, tmp, &ctx_list->ctx_list, link) { |
| if (ctx->head && ctx->head->output == output) { |
| mode_policy_set_head(ctx->head); |
| break; |
| } |
| } |
| } |
| |
| static void mode_policy_update_ctx(weston_ctx *ctx, |
| struct weston_head *head, int fd, drmModeConnector *conn) |
| { |
| drmModeCrtc *crtc = NULL; |
| bool changed = false; |
| |
| if (!ctx) |
| return; |
| |
| crtc = weston_get_crtc_for_conn(fd, conn); |
| if (!crtc) |
| return; |
| |
| if ((ctx->crtc && ctx->crtc->crtc_id != crtc->crtc_id) || !ctx->crtc) |
| changed = true; |
| |
| if (ctx->crtc) |
| drmModeFreeCrtc(ctx->crtc); |
| ctx->drm_fd = fd; |
| ctx->crtc = crtc; |
| ctx->conn = conn; |
| ctx->head = head; |
| if (changed) { |
| ctx->current_mode = drm_mode_to_weston_mode(&ctx->crtc->mode); |
| weston_prop_list_init(ctx); |
| weston_add_property(ctx, ctx->crtc->crtc_id, DRM_MODE_OBJECT_CRTC); |
| weston_add_property(ctx, ctx->conn->connector_id, DRM_MODE_OBJECT_CONNECTOR); |
| } |
| } |
| |
| void init_mode_policy_without_mode(struct weston_head *head, int fd, drmModeConnector *conn) |
| { |
| weston_ctx_list *ctx_list = weston_get_ctx_list(); |
| weston_ctx *ctx; |
| |
| mode_policy_set_head(head); |
| ctx = weston_get_ctx(); |
| if (!ctx) { |
| ctx = (weston_ctx *)calloc(1, sizeof(*ctx)); |
| ctx->scaling = -1; |
| ctx->enableVrr = true; |
| ctx->state = ctx_list->state; |
| ctx->drm_fd = fd; |
| MESON_LOGD("\n init_mode_policy_without_mode :%d\n", fd); |
| wl_list_init(&ctx->prop_list); |
| wl_list_insert(&ctx_list->ctx_list, &ctx->link); |
| } |
| mode_policy_update_ctx(ctx, head, fd, conn); |
| } |
| |
| static bool need_mode_changed(char *name) |
| { |
| int size = sizeof(prop_changed_and_mode) / sizeof(prop_changed_and_mode[0]); |
| int i; |
| |
| for (i = 0; i < size; i++) { |
| if (STRCMPS(name, prop_changed_and_mode[i]) == 0) |
| return true; |
| } |
| return false; |
| } |
| |
| static void mode_policy_add_mode(weston_ctx_list * ctx_list, bool mode_changed) |
| { |
| weston_ctx *ctx, *tmp; |
| |
| wl_list_for_each_safe(ctx, tmp, &ctx_list->ctx_list, link) { |
| if (ctx->mode_changed && mode_changed) |
| ctx->need_update_hdmi_param = true; |
| } |
| } |
| |
| int mode_policy_add_prop(drmModeAtomicReq *req, bool mode_changed) |
| { |
| prop_info *info; |
| int err = 0, ret = 0; |
| int count = 0; |
| weston_ctx_list * ctx_list = weston_get_ctx_list(); |
| weston_ctx *ctx, *tmp; |
| |
| mode_policy_add_mode(ctx_list, mode_changed); |
| wl_list_for_each_safe(ctx, tmp, &ctx_list->ctx_list, link) { |
| wl_list_for_each(info, &ctx->prop_list, link) { |
| if (info->need_change) { |
| if (need_mode_changed(info->name)) |
| count++; |
| |
| err = drmModeAtomicAddProperty(req, info->item_id, info->prop_id, info->value); |
| if (!err) |
| MESON_LOGE("drmModeAtomicAddProperty %s fail: %d(%d)\n", |
| info->name, err, errno); |
| weston_log("mode_policy_add_prop name %s value %lld\n",info->name,info->value); |
| info->need_change = 0; |
| ret |= (err <= 0) ? -1 : 0; |
| ctx->need_update_hdmi_param = true; |
| } |
| } |
| } |
| return ret >= 0 ? count : ret; |
| } |
| |
| static void weston_set_state_for_ctx(weston_ctx *ctx, int state, bool force) |
| { |
| if (!ctx) |
| return; |
| |
| if (force) { |
| ctx->state = state; |
| } else { |
| if (ctx->state & state) |
| ctx->state = state; |
| } |
| } |
| |
| void mode_policy_set_hotplug(int plug, bool force) |
| { |
| weston_ctx_list *ctx_list = weston_get_ctx_list(); |
| weston_ctx *ctx, *tmp; |
| |
| ctx_list->state = plug; |
| wl_list_for_each_safe(ctx, tmp, &ctx_list->ctx_list, link) { |
| if (ctx->conn && ctx->conn->connector_type == DRM_MODE_CONNECTOR_HDMIA) |
| weston_set_state_for_ctx(ctx, plug, force); |
| } |
| } |
| |
| void mode_policy_set_state(struct weston_head *head, int state, bool force) |
| { |
| weston_ctx_list *ctx_list; |
| weston_ctx *ctx, *tmp; |
| |
| if (head) { |
| ctx = weston_get_ctx_for_head(head); |
| weston_set_state_for_ctx(ctx, state, force); |
| return; |
| } |
| |
| ctx_list = weston_get_ctx_list(); |
| ctx_list->state = state; |
| wl_list_for_each_safe(ctx, tmp, &ctx_list->ctx_list, link) |
| weston_set_state_for_ctx(ctx, state, force); |
| } |
| |
| void mode_policy_update_mode_state(struct weston_head *head, int state) |
| { |
| weston_ctx_list *ctx_list; |
| weston_ctx *ctx, *tmp; |
| |
| if (head) { |
| ctx = weston_get_ctx_for_head(head); |
| ctx->mode_state = state; |
| return; |
| } |
| |
| ctx_list = weston_get_ctx_list(); |
| wl_list_for_each_safe(ctx, tmp, &ctx_list->ctx_list, link) |
| ctx->mode_state = state; |
| } |
| |
| void mode_policy_update_mode(struct weston_mode *mode) |
| { |
| weston_ctx *ctx = weston_get_ctx(); |
| |
| if (!ctx) |
| return; |
| |
| if (mode) |
| ctx->current_mode = *mode; |
| else if (ctx->mode_changed) |
| ctx->current_mode = ctx->next_mode; |
| MESON_LOGD("curr: %dx%d@%d", |
| ctx->current_mode.width, |
| ctx->current_mode.height, |
| ctx->current_mode.refresh); |
| } |
| |
| struct weston_mode *mode_policy_choose_mode(struct weston_mode *mode) |
| { |
| weston_ctx_list * ctx_list = weston_get_ctx_list(); |
| weston_ctx *ctx = weston_get_ctx(); |
| char name[32] = { 0 }; |
| |
| if (!ctx) |
| return NULL; |
| |
| initModePolicyFun(ctx->crtc, ctx->conn, callback); |
| updateDrmfd(ctx->drm_fd); |
| seamlessSwitchEnabled(ctx->enableVrr); |
| if (mode) { |
| if (get_mode_name_for_weston_mode(ctx, mode, name)) |
| setActiveConfig(name); |
| } else { |
| if (ctx->state & (AML_WESTON_HOTPLUG_PLUG | AML_WESTON_HOTPLUG_UNPLUG)) |
| onHotplug(ctx->state & AML_WESTON_HOTPLUG_PLUG); |
| else if (ctx->state & (AML_WESTON_HOTPLUG_BOOT | AML_WESTON_HOTPLUG_SET)) |
| initModePolicy(NULL, NULL, callback); |
| ctx->state = AML_WESTON_HOTPLUG_INIT; |
| } |
| if (ctx->mode_update) |
| return &ctx->next_mode; |
| return &ctx->current_mode; |
| } |
| |
| void mode_policy_update_bestmode(bool bestmode) |
| { |
| bootenv_update("is.bestmode", bestmode ? "true" : "false"); |
| } |
| |
| static int get_possible_by_connector(weston_ctx *ctx) |
| { |
| drmModeEncoder *encoder; |
| int possible = 0; |
| |
| if (!ctx || !ctx->conn) |
| return 0; |
| |
| encoder = drmModeGetEncoder(ctx->drm_fd, ctx->conn->encoder_id); |
| if (encoder) { |
| possible = encoder->possible_crtcs; |
| drmModeFreeEncoder(encoder); |
| } |
| return possible; |
| } |
| |
| void mode_policy_update_modeset(int *possible) |
| { |
| weston_ctx_list * ctx_list = weston_get_ctx_list(); |
| weston_ctx *ctx, *tmp; |
| |
| wl_list_for_each_safe(ctx, tmp, &ctx_list->ctx_list, link) { |
| if (ctx->mode_changed) { |
| *possible |= get_possible_by_connector(ctx); |
| } |
| if (ctx->mode_state == AML_WESTON_EXIT_SET_MODE) |
| ctx->mode_state = AML_WESTON_NORMAL; |
| } |
| MESON_LOGV("*possible : %d", *possible); |
| } |
| |
| void mode_policy_resume() |
| { |
| weston_ctx *ctx = weston_get_ctx(); |
| int flag; |
| |
| if (!ctx) |
| return; |
| |
| flag = (ctx->conn && |
| ctx->conn->connector_type == DRM_MODE_CONNECTOR_HDMIA) ? |
| AML_WESTON_HOTPLUG_PLUG : AML_WESTON_HOTPLUG_SET; |
| |
| weston_set_state_for_ctx(ctx, flag, true); |
| } |
| |
| static int get_priority_strategy(int prio) |
| { |
| return (prio >> 28) & 0xf; |
| } |
| |
| static int switch_strategy1_to_strategy2(int prio) |
| { |
| int ret = prio; |
| |
| if (get_priority_strategy(prio) == 0) { |
| switch (prio) { |
| case MESON_DOLBY_VISION_PRIORITY: |
| ret = MESON_G_DV_HDR10_HLG; |
| break; |
| case MESON_HDR10_PRIORITY: |
| ret = MESON_G_HDR10_HLG; |
| break; |
| case MESON_SDR_PRIORITY: |
| ret = MESON_G_SDR; |
| break; |
| default: |
| ret = prio; |
| break; |
| } |
| } |
| return ret; |
| } |
| |
| static int switch_strategy2_to_strategy1(int prio) |
| { |
| int ret = prio; |
| |
| if (get_priority_strategy(prio) == 1) { |
| switch (prio) { |
| case MESON_G_DV_HDR10_HLG: |
| case MESON_G_DV_HDR10: |
| case MESON_G_DV_HLG: |
| case MESON_G_DV: |
| ret = MESON_DOLBY_VISION_PRIORITY; |
| break; |
| case MESON_G_HDR10_HLG: |
| case MESON_G_HDR10: |
| case MESON_G_HLG: |
| ret = MESON_HDR10_PRIORITY; |
| break; |
| case MESON_G_SDR: |
| ret = MESON_SDR_PRIORITY; |
| break; |
| default: |
| ret = prio; |
| break; |
| } |
| } |
| return ret; |
| } |
| |
| int mode_policy_set_priority(int priority) |
| { |
| weston_ctx_list * ctx_list = weston_get_ctx_list(); |
| weston_ctx *ctx = weston_get_ctx(); |
| |
| if (!ctx) |
| return NULL; |
| |
| initModePolicyFun(ctx->crtc, ctx->conn, callback); |
| updateDrmfd(ctx->drm_fd); |
| seamlessSwitchEnabled(ctx->enableVrr); |
| return setPriority(switch_strategy2_to_strategy1(priority)); |
| } |
| |
| int mode_policy_get_priority() |
| { |
| int priority = getPriority(); |
| return switch_strategy2_to_strategy1(priority); |
| } |
| |
| static void * wstUpdatenvThread(void *arg ) |
| { |
| long long delay = 16667LL; |
| weston_ctx_list *ctx_list = (weston_ctx_list *)arg; |
| weston_ctx *ctx, *tmp; |
| |
| while (!ctx_list->update_env_thread_stop_requested) |
| { |
| wl_list_for_each_safe(ctx, tmp, &ctx_list->ctx_list, link) { |
| if ((ctx->mode_changed && !ctx->prop_changed && ctx->need_update_hdmi_param) || |
| (ctx->prop_changed && ctx->need_update_hdmi_param)) |
| { |
| weston_log("%s[%d]: mode state: %d\n", __func__, __LINE__, ctx->mode_state); |
| if (ctx->mode_state) |
| continue; |
| mode_policy_set_head(ctx->head); |
| initModePolicyFun(ctx->crtc, ctx->conn, callback); |
| updateEnv(); |
| ctx->need_update_hdmi_param = false; |
| ctx->mode_changed = false; |
| ctx->prop_changed = false; |
| } |
| } |
| usleep( delay ); |
| } |
| MESON_LOGD("update env thread exit"); |
| return NULL; |
| } |
| |
| void weston_start_update_env_thread() |
| { |
| int rc; |
| weston_ctx_list *ctx_list = weston_get_ctx_list(); |
| |
| ctx_list->update_env_thread_stop_requested = false; |
| rc = pthread_create(&ctx_list->update_env_thread_id, NULL, wstUpdatenvThread, ctx_list); |
| if ( rc ) |
| MESON_LOGE("unable to start updatenv thread: rc %d errno %d", rc, errno); |
| } |
| bool set_policy_by_appName(const char *name, int state) { |
| return setPolicyByAppName( name, state); |
| } |
| |
| bool get_next_mode(struct weston_mode* mode) |
| { |
| bool ret = false; |
| weston_ctx *ctx = weston_get_ctx(); |
| if (!ctx) { |
| MESON_LOGE("invalid ctx,return\n"); |
| return ret; |
| } |
| if (ctx->mode_changed) { |
| *mode = ctx->next_mode; |
| ret = true; |
| } |
| return ret; |
| } |