blob: 8258ea3ffd39a12cdb892e3418b8a7e3eb3744d8 [file] [log] [blame]
leng.fang91856072024-06-07 14:12:54 +08001#include <pthread.h>
2
3#include "modepolicyfunc.h"
4#include "DisplayAdapter.h"
5#include "modepolicy_aml.h"
6
7weston_ctx *gCtx = NULL;
8static int g_activeLevel = 3;
9
10weston_ctx *weston_get_ctx()
11{
12 if (!gCtx) {
13 gCtx = (weston_ctx *)calloc(1, sizeof(*gCtx));
14 gCtx->need_update_hdmi_param = false;
15 wl_list_init(&gCtx->prop_list);
16 }
17 return gCtx;
18}
19
20static void weston_prop_list_init(weston_ctx *ctx)
21{
22 prop_info *info, *tmp;
23 if (!wl_list_empty(&ctx->prop_list)) {
24 wl_list_for_each_safe(info, tmp, &ctx->prop_list, link) {
25 wl_list_remove(&info->link);
26 free(info);
27 }
28 }
29 wl_list_init(&ctx->prop_list);
30}
31
32static int weston_add_property_item(weston_ctx *ctx,
33 const char *name, int id, int prop_id)
34{
35 prop_info *info;
36
37 wl_list_for_each(info, &ctx->prop_list, link) {
38 if (!strcmp(name, info->name) && info->item_id == id)
39 return 0;
40 }
41
42 info = (prop_info *)calloc(1, sizeof(*info));
43 memcpy(info->name, name, sizeof(info->name));
44 info->item_id = id;
45 info->prop_id = prop_id;
46 info->need_change = 0;
47 MESON_LOGD("ctx: %p, name: %s, id: %d, prop_id: %d\n", ctx, name, id, prop_id);
48 wl_list_insert(&ctx->prop_list, &info->link);
49 return 0;
50}
51
52static int weston_add_property(weston_ctx *ctx, int id, int type)
53{
54 int i, len;
55 int value = 0;
56 drmModePropertyRes *propRes;
57 drmModeObjectProperties *props;
58
59 props = drmModeObjectGetProperties( ctx->drm_fd, id, type );
60 if ( props ) {
61 for ( i= 0; i < props->count_props; ++i ) {
62 propRes = drmModeGetProperty( ctx->drm_fd, props->props[i] );
63 if ( propRes ) {
64 weston_add_property_item(ctx, propRes->name, id, props->props[i]);
65 drmModeFreeProperty( propRes );
66 propRes = 0;
67 }
68 }
69 drmModeFreeObjectProperties( props );
70 props = 0;
71 }
72 return value;
73}
74
75static int weston_get_property_value(weston_ctx *ctx, const char * key, int id, int type)
76{
77 int i, len;
78 int value = 0;
79 drmModePropertyRes *propRes;
80 drmModeObjectProperties *props;
81
82 props = drmModeObjectGetProperties( ctx->drm_fd, id, type );
83 if ( props ) {
84 for ( i= 0; i < props->count_props; ++i ) {
85 propRes = drmModeGetProperty( ctx->drm_fd, props->props[i] );
86 if ( propRes ) {
87 len = strlen(propRes->name);
88 if ( !strncmp( propRes->name, key, len) ) {
89 MESON_LOGD("property %d name (%s) value (%lld)",
90 props->props[i], propRes->name, props->prop_values[i] );
91 value = props->prop_values[i];
92 drmModeFreeProperty( propRes );
93 propRes = 0;
94 break;
95 }
96 drmModeFreeProperty( propRes );
97 propRes = 0;
98 }
99 }
100 drmModeFreeObjectProperties( props );
101 props = 0;
102 }
103 return value;
104}
105
106static int weston_get_connector_property(const char * key)
107{
108 weston_ctx *ctx = weston_get_ctx();
109
110 return weston_get_property_value(ctx, key, ctx->conn->connector_id, DRM_MODE_OBJECT_CONNECTOR);
111}
112
113static int weston_get_crtc_property(const char * key)
114{
115 weston_ctx *ctx = weston_get_ctx();
116
117 return weston_get_property_value(ctx, key, ctx->crtc->crtc_id, DRM_MODE_OBJECT_CRTC);
118}
119
120static bool get_mode_name_for_weston_mode(
121 weston_ctx *ctx,
122 struct weston_mode *mode,
123 char *out)
124{
125 int i;
126 int current_interlaced;
127 const char *value;
128
129 if (mode && getModeNameForPix(out, mode->width, mode->height, mode->refresh, mode->flags))
130 return true;
131
132 value = bootenv_get("hdmimode");
133 if (value)
134 strcpy(out, value);
135 else
136 strcpy(out, "none");
137 MESON_LOGD("out: %s", out);
138 return false;
139}
140
141static bool weston_get_mode(char *mode)
142{
143 weston_ctx *ctx = weston_get_ctx();
144 struct weston_mode *wmode = NULL;
145
146 if (!wmode && ctx->current_mode.width != 0)
147 wmode = &ctx->current_mode;
148
149 return get_mode_name_for_weston_mode(ctx, wmode, mode);
150}
151
152static bool WestonGetUbootIsBestmode()
153{
154 const char *isbestmode = bootenv_get("is.bestmode");
155 if ( isbestmode != NULL ) {
156 if (strcmp("false", isbestmode) == 0)
157 return false;
158 else
159 return true;
160 } else
161 return true;
162}
163
164bool westonGetDrmModeInfoByName( weston_ctx *ctx, const char *mode, drmModeModeInfo *out_mode )
165{
166 bool result= false;
167
168 if ( ctx && mode && out_mode ) {
169 int width = -1, height = -1, rate = -1;
170 bool interlaced = false;
171 bool haveTarget = false;
172 bool useBestRate = true;
173
174 MESON_LOGD("%s: mode (%s)", __func__, mode);
175
176 if ( sscanf( mode, "%dx%dp%d", &width, &height, &rate ) == 3 ) {
177 interlaced = false;
178 } else if ( sscanf( mode, "%dx%di%d", &width, &height, &rate ) == 3 ) {
179 interlaced = true;
180 } else if ( sscanf( mode, "%dx%dx%d", &width, &height, &rate ) == 3 ) {
181 interlaced = false;
182 } else if ( sscanf( mode, "%dx%d", &width, &height ) == 2 ) {
183 int len = strlen(mode);
184 interlaced = (mode[len - 1] == 'i');
185 } else if (sscanf( mode, "%dp%dhz", &height,&rate ) == 2) {
186 interlaced = false;
187 width = -1;
188 } else if (sscanf( mode, "%di%dhz", &height,&rate ) == 2) {
189 interlaced = true;
190 width = -1;
191 } else if ( sscanf( mode, "%dp", &height ) == 1 ) {
192 int len = strlen(mode);
193 interlaced = (mode[len - 1] == 'i');
194 width = -1;
195 } else if (sscanf( mode, "smpte%dhz", &rate ) == 1) {
196 interlaced = false;
197 height = 2160;
198 width = 4096;
199 }
200
201 if ( height > 0 ) {
202 if ( width < 0 ) {
203 switch ( height ) {
204 case 480:
205 case 576:
206 width = 720;
207 break;
208 case 720:
209 width = 1280;
210 break;
211 case 1080:
212 width = 1920;
213 break;
214 case 1440:
215 width = 2560;
216 break;
217 case 2160:
218 width = 3840;
219 break;
220 case 2880:
221 width = 5120;
222 break;
223 case 4320:
224 width = 7680;
225 break;
226 default:
227 break;
228 }
229 }
230 }
231 MESON_LOGD("%s w %d h %d rate %d", __func__, width, height, rate);
232 if ( rate >= 0 )
233 useBestRate = false;
234
235 if ( (width > 0) && (height > 0) ) {
236 if ( ctx->drm_fd >= 0 ) {
237 drmModeRes *res = 0;
238 drmModeConnector *conn = 0;
239
240 res = drmModeGetResources( ctx->drm_fd );
241 if ( res ) {
242 int i;
243 for ( i = 0; i < res->count_connectors; ++i ) {
244 conn = drmModeGetConnector( ctx->drm_fd, res->connectors[i] );
245 if ( conn ) {
246 if ( conn->count_modes && (conn->connection == DRM_MODE_CONNECTED) )
247 break;
248 drmModeFreeConnector(conn);
249 conn = 0;
250 }
251 }
252 if ( conn ) {
253 uint32_t rateBest = 0;
254 int miBest = -1;
255
256 MESON_LOGD("%s: want %dx%dx%d interlaced %d use best rate %d",
257 __func__, width, height, rate, interlaced, useBestRate);
258 for ( i = 0; i < conn->count_modes; ++i ) {
259 MESON_LOGD("%s: consider mode %d: %dx%dx%d (%s) type 0x%x flags 0x%x",
260 __func__, i, conn->modes[i].hdisplay, conn->modes[i].vdisplay,
261 conn->modes[i].vrefresh, conn->modes[i].name,
262 conn->modes[i].type, conn->modes[i].flags );
263
264 if ( (conn->modes[i].hdisplay == width) &&
265 (conn->modes[i].vdisplay == height) ) {
266 bool modeIsInterlaced = (conn->modes[i].flags & DRM_MODE_FLAG_INTERLACE);
267 if ( modeIsInterlaced != interlaced )
268 continue;
269
270 if ( useBestRate ) {
271 if ( conn->modes[i].vrefresh > rateBest ) {
272 rateBest = conn->modes[i].vrefresh;
273 miBest = i;
274 }
275 } else if ( conn->modes[i].vrefresh == rate ) {
276 miBest = i;
277 break;
278 }
279 }
280 }
281
282 if ( miBest >= 0 ) {
283 *out_mode = conn->modes[miBest];
284
285 MESON_LOGI("%s: choosing output mode: %dx%dx%d (%s) flags 0x%x",
286 __func__,
287 out_mode->hdisplay,
288 out_mode->vdisplay,
289 out_mode->vrefresh,
290 out_mode->name,
291 out_mode->flags );
292
293 result= true;
294 } else {
295 MESON_LOGE("%s: failed to find a mode matching (%s)", __func__, mode);
296 }
297
298 drmModeFreeConnector( conn );
299 } else {
300 MESON_LOGE("%s: unable to get connector for card", __func__);
301 }
302 drmModeFreeResources(res);
303 } else {
304 MESON_LOGE("%s: unable to get card resources", __func__);
305 }
306 } else {
307 MESON_LOGE("%s: no open device", __func__);
308 }
309 } else {
310 MESON_LOGE("%s: unable to parse mode (%s)", __func__, mode);
311 }
312 }
313
314 return result;
315}
316
317static bool weston_set_mode(const char *mode)
318{
319 weston_ctx *ctx = weston_get_ctx();
320 drmModeModeInfo info = {0};
321
322 if (westonGetDrmModeInfoByName(ctx, mode, &info)) {
323 ctx->next_mode.width = info.hdisplay;
324 ctx->next_mode.height = info.vdisplay;
325 ctx->next_mode.refresh = info.vrefresh * 1000;
326 ctx->next_mode.flags = info.flags;
327 setBootConfig(info.name, WestonGetUbootIsBestmode());
328 ctx->mode_update = true;
329 ctx->mode_changed = true;
330 }
331 return 0;
332}
333
334static int weston_get_property(int id, const char *name, char *buf)
335{
336 weston_ctx *ctx = weston_get_ctx();
337 int value = 0;
338
339 if (!ctx->crtc || !ctx->conn)
340 return 0;
341
342 if (ctx->crtc->crtc_id == id)
343 value = weston_get_crtc_property(name);
344
345 if (ctx->conn->connector_id == id)
346 value = weston_get_connector_property(name);
347
348 snprintf(buf, MAX_BUF_LEN, "%d", value);
349 return 0;
350}
351
352static int weston_set_property(int id, const char *name, int value)
353{
354 prop_info *info;
355 weston_ctx *ctx = weston_get_ctx();
356
357 wl_list_for_each(info, &ctx->prop_list, link) {
358 if (!strcmp(name, info->name) && info->item_id == id) {
359 MESON_LOGD("name: %s, id: %d, prop: %d, value: %d\n",
360 name, id, info->prop_id, value);
361 info->value = value;
362 info->need_change = 1;
363 ctx->prop_changed = true;
364 break;
365 }
366 }
367
368 return 0;
369}
370
371static bool weston_set_colorattribute(const char *attr)
372{
373 weston_ctx *ctx = weston_get_ctx();
374 int depth = -1, value = -1; // default none
375
376 if ( attr && strlen(attr) > 3 ) {
377 if ( strstr (attr, "rgb") )
378 value = 0;
379 else if ( strstr (attr, "422") )
380 value = 1;
381 else if ( strstr (attr, "444") )
382 value = 2;
383 else if ( strstr (attr, "420") )
384 value = 3;
385 sscanf(attr + 4, "%dbit", &depth);
386 }
387 if (ctx && ctx->conn && ctx->conn->connector_type == DRM_MODE_CONNECTOR_HDMIA) {
388 weston_set_property(ctx->conn->connector_id, "color_space", value);
389 weston_set_property(ctx->conn->connector_id, "color_depth", depth);
390 }
391 return 0;
392}
393
394void weston_set_scaling_position(int x, int y, int w, int h)
395{
396 weston_ctx *ctx = weston_get_ctx();
397 const char *value = bootenv_get("scaling");
398 int scaling = 1;
399
400 if (value)
401 scaling = atoi(value);
402
403 if (ctx && ctx->head && ctx->head->output) {
404 if (ctx->head->output->current_scale != scaling)
405 ctx->head->output->current_scale = scaling;
406 }
407}
408
409CompositorFunctionCallBack callback = {
410 .get_mode = weston_get_mode,
411 .set_mode = weston_set_mode,
412 .set_colorattribute = weston_set_colorattribute,
413 .get_property = weston_get_property,
414 .set_property = weston_set_property,
415 .set_scaling_position = weston_set_scaling_position,
416};
417
418static drmModeCrtc *weston_get_crtc_for_conn(int drm_fd, drmModeConnector *conn)
419{
420 drmModeEncoder *encoder;
421 drmModeCrtc *crtc = NULL;
422
423 /* Get the current mode on the crtc that's currently driving
424 * this connector. */
425 encoder = drmModeGetEncoder(drm_fd, conn->encoder_id);
426 if (encoder != NULL) {
427 crtc = drmModeGetCrtc(drm_fd, encoder->crtc_id);
428 drmModeFreeEncoder(encoder);
429 }
430 return crtc;
431}
432
433void init_mode_policy_without_mode(struct weston_head *head, int fd, drmModeConnector *conn)
434{
435 weston_ctx *ctx = weston_get_ctx();
436 drmModeCrtc *crtc = NULL;
437
438 if (conn && conn->connector_type == DRM_MODE_CONNECTOR_HDMIA) {
439 crtc = weston_get_crtc_for_conn(fd, conn);
440 if (!crtc)
441 return;
442
443 initModePolicyFun(crtc, conn, callback);
444 if (ctx->crtc)
445 drmModeFreeCrtc(ctx->crtc);
446 ctx->drm_fd = fd;
447 ctx->crtc = crtc;
448 ctx->conn = conn;
449 ctx->head = head;
450 weston_prop_list_init(ctx);
451 weston_add_property(ctx, ctx->crtc->crtc_id, DRM_MODE_OBJECT_CRTC);
452 weston_add_property(ctx, ctx->conn->connector_id, DRM_MODE_OBJECT_CONNECTOR);
453 }
454}
455
456void init_mode_policy(int fd, drmModeConnector *conn)
457{
458 weston_ctx *ctx = weston_get_ctx();
459 drmModeCrtc *crtc = weston_get_crtc_for_conn(fd, conn);
460 if (ctx->crtc)
461 drmModeFreeCrtc(ctx->crtc);
462 ctx->drm_fd = fd;
463 ctx->crtc = crtc;
464 ctx->conn = conn;
465 initModePolicy(crtc, conn, callback);
466}
467
468int mode_policy_add_prop(drmModeAtomicReq *req)
469{
470 weston_ctx *ctx = weston_get_ctx();
471 prop_info *info;
472 int err = 0, ret = 0;
473
474 wl_list_for_each(info, &ctx->prop_list, link) {
475 if (info->need_change) {
476 err = drmModeAtomicAddProperty(req, info->item_id, info->prop_id, info->value);
477 if (!err)
478 MESON_LOGE("drmModeAtomicAddProperty %s fail: %d(%d)\n",
479 info->name, err, errno);
480 info->need_change = 0;
481 ret |= (err <= 0) ? -1 : 0;
482 ctx->need_update_hdmi_param = true;
483 }
484 }
485 return ret;
486}
487
488void mode_policy_set_hotplug(int plug)
489{
490 weston_ctx *ctx = weston_get_ctx();
491 if (ctx->hotplug == AML_WESTON_HOTPLUG_INIT)
492 ctx->hotplug = plug;
493 else if (ctx->hotplug & plug)
494 ctx->hotplug = plug;
495}
496
497void mode_policy_update_mode(struct weston_mode *mode)
498{
499 weston_ctx *ctx = weston_get_ctx();
500 if (mode)
501 ctx->current_mode = *mode;
502 else
503 ctx->current_mode = ctx->next_mode;
504 MESON_LOGD("curr: %dx%d@%d",
505 ctx->current_mode.width,
506 ctx->current_mode.height,
507 ctx->current_mode.refresh);
508}
509
510struct weston_mode *mode_policy_choose_mode(struct weston_mode *mode)
511{
512 weston_ctx *ctx = weston_get_ctx();
513 char name[32] = { 0 };
514 drmModeModeInfo info = { 0 };
515
516 if (mode) {
517 if (get_mode_name_for_weston_mode(ctx, mode, name))
518 setActiveConfig(name);
519 } else {
520 if (ctx->hotplug & (AML_WESTON_HOTPLUG_PLUG | AML_WESTON_HOTPLUG_UNPLUG))
521 onHotplug(ctx->hotplug & AML_WESTON_HOTPLUG_PLUG);
522 else if (ctx->hotplug & AML_WESTON_HOTPLUG_BOOT)
523 initModePolicy(NULL, NULL, callback);
524 ctx->hotplug = AML_WESTON_HOTPLUG_INIT;
525 }
526 if (ctx->mode_update)
527 return &ctx->next_mode;
528 return NULL;
529}
530
531static void * wstUpdatenvThread(void *arg )
532{
533 long long delay = 16667LL;
534 weston_ctx *ctx = (weston_ctx *)arg;
535 char * attr = NULL;
536 while (!ctx->update_env_thread_stop_requested)
537 {
538 if ((ctx->mode_changed && !ctx->prop_changed) ||
539 (ctx->prop_changed && ctx->need_update_hdmi_param))
540 {
541 weston_log("%s[%d]\n", __func__, __LINE__);
542 updateEnv();
543 ctx->need_update_hdmi_param = false;
544 ctx->mode_changed = false;
545 ctx->prop_changed = false;
546 }
547 usleep( delay );
548 }
549 MESON_LOGD("update env thread exit");
550 return NULL;
551}
552
553void weston_start_update_env_thread()
554{
555 int rc;
556 weston_ctx *ctx = weston_get_ctx();
557
558 ctx->update_env_thread_stop_requested = false;
559 rc = pthread_create(&ctx->update_env_thread_id, NULL, wstUpdatenvThread, ctx);
560 if ( rc )
561 MESON_LOGE("unable to start updatenv thread: rc %d errno %d", rc, errno);
562}