meson-display: fix set displaymode sequence issue for on weston. [1/2]
PD#SH-4403
Problem:
weston will stop repaint, after set displaymode with
meson-display-helper same times, It happend when setmode between
drm_output_repaint && drm_pending_state_test. it cause
atomic_commit_test fail, and next atomic-commit fail, then weston
stop repaint.
Solution:
Post the swith_mode task at repaint cycle end, and effect at next
repaint.
Verify:
local w400. with test script:
https://jira.amlogic.com/secure/attachment/181744/test_meson_display.sh
Change-Id: I06ea353ce22027a872d1b95bb33761801dac0deb
Signed-off-by: lingjie li <lingjie.li@amlogic.com>
diff --git a/display_framework/src/extension/weston-6.0/compositor-drm-help.c b/display_framework/src/extension/weston-6.0/compositor-drm-help.c
index b76ab73..597e367 100644
--- a/display_framework/src/extension/weston-6.0/compositor-drm-help.c
+++ b/display_framework/src/extension/weston-6.0/compositor-drm-help.c
@@ -24,6 +24,7 @@
//Must leave a NULL to list end.
+//The last one element will not be handle.
#define for_each_list(pos, list_p) for (pos = list_p; pos->next != NULL; pos = pos->next)
/**
@@ -86,16 +87,45 @@
} connector_list;
+typedef struct _compositor_interface {
+ switch_mode switch_mode;
+ force_refresh force_refresh;
+} compositor_interface;
+
+typedef struct _compositor_output_list {
+ struct _compositor_output_list* prev;
+ struct _compositor_output_list* next;
+ struct compositor_output* data;
+ connector_list conn;
+ bool enable;
+} compositor_output_list;
+
+#define MAX_TASK_NUM 10
+typedef struct _helper_task_queue {
+ json_object* task[MAX_TASK_NUM];
+ int put_index;
+ int get_index;
+} helper_task_queue;
+
/* The server only one instance in one process so use global value
- * g_output : hold the weston output
- * g_switch_mode_fun : hold the drm output's switch mode function
+ * g_output_list: hold the weston output
+ * g_interface: hold the api of compositor
* mutex : for the globle value.
* g_server_ctx : use to save a only one instance ipc server context
* global_connector_list : the connector list current.
* g_ui_viewport : the ui viewport.
*/
+
+/* For compatible with old version */
output_ctx g_output = NULL;
-switch_mode g_switch_mode_fun = NULL;
+
+compositor_output_list g_output_list = { 0 };
+compositor_interface g_interface = { 0 };
+
+
+helper_task_queue g_task_after_repaint_cycle = { 0 };
+
+
pthread_mutex_t mutex;
server_ctx* g_server_ctx = NULL;
int g_drm_fd = -1;
@@ -109,6 +139,8 @@
+void update_connector_props(connector_list* connector);
+
/* TODO: reduce the codesize
* NOTE :The format need same as the client's json resovle format.
*/
@@ -298,6 +330,76 @@
return true;
}
+bool schedule_task(helper_task_queue* queue, json_object* task) {
+ json_object* cmd = NULL;
+ int next = (queue->put_index + 1) % MAX_TASK_NUM;
+
+ if (next == queue->get_index) {
+ DEBUG_INFO("task queue full");
+ return false;
+ }
+ if (0 != json_object_deep_copy(task, &cmd, NULL)) {
+ return false;
+ }
+ queue->task[queue->put_index] = cmd;
+ queue->put_index = next;
+ return true;
+}
+
+
+bool have_task(helper_task_queue* queue) {
+ return (queue->get_index != queue->put_index);
+}
+
+bool get_task(helper_task_queue* queue, json_object** task) {
+ int next = (queue->get_index + 1) % MAX_TASK_NUM;
+
+ if (have_task(queue)) {
+ *task = queue->task[queue->get_index];
+ queue->task[queue->get_index] = NULL;
+ queue->get_index = next;
+ return true;
+ }
+ return false;
+}
+
+void process_task(json_object* data_in, json_object** data_out) {
+ int ret = 0;
+ json_object* tmp = NULL;
+ json_object* opt = NULL;
+ *data_out = NULL;
+ assert(0 != json_object_object_get_ex(data_in, "cmd", &tmp));
+ //cmd's buffer under the json object "data_in"
+ const char* cmd = json_object_get_string(tmp);
+ json_object_object_get_ex(data_in, "value", &opt);
+ DEBUG_INFO("process task:%s", cmd);
+ if (0 == strcmp("set mode", cmd)) {
+ drm_helper_mode mode;
+ assert(opt);
+ const char* value = json_object_get_string(opt);
+ if (true == parse_modestring(value, &mode)) {
+ pthread_mutex_lock(&mutex);
+ if (g_interface.switch_mode) {
+ if (g_output_list.data) {
+ compositor_output_list* current;
+ for_each_list(current, &g_output_list) {
+ if (current->enable) {
+ assert(current->data);
+ g_interface.switch_mode(current->data, &mode);
+ }
+ }
+ } else if (g_output) {
+ g_interface.switch_mode(g_output, &mode);
+ }
+ }
+ pthread_mutex_unlock(&mutex);
+ } else {
+ ret = -1;
+ }
+ }
+ json_object_put(data_in);
+}
+
/*
json formate:
@@ -328,22 +430,28 @@
ret = -1;
} else {
const char* value = json_object_get_string(opt);
+ drm_helper_mode mode;
DEBUG_INFO("CMD set mode :%s", value);
- drm_helper_mode m;
- if (false == parse_modestring(value, &m)) {
+ if (false == parse_modestring(value, &mode)) {
ret = -1;
} else {
pthread_mutex_lock(&mutex);
- if (g_output && g_switch_mode_fun) {
- g_switch_mode_fun(g_output, &m);
- } else {
- if (g_switch_mode_fun == NULL) {
- DEBUG_INFO("Output not enabled");
- } else {
- DEBUG_INFO("Output not ready");
- }
+ if (false == schedule_task(&g_task_after_repaint_cycle, data_in)) {
+ DEBUG_INFO("schedule task failed");
ret = -1;
}
+ //trigger a refresh, for the next repaint
+ if (g_interface.force_refresh && g_output_list.data) {
+ //TODO: refresh for need output olny
+ compositor_output_list* current;
+ for_each_list(current, &g_output_list) {
+ if (current->enable) {
+ assert(current->data);
+ g_interface.force_refresh(current->data);
+ }
+ }
+ }
+
pthread_mutex_unlock(&mutex);
}
}
@@ -625,10 +733,16 @@
END_EVENT;
}
-void help_set_switch_mode_function(output_ctx ctx, switch_mode fun) {
+void help_set_switch_mode_function(struct compositor_output* ctx, switch_mode fun) {
BEGING_EVENT;
+ g_interface.switch_mode = fun;
g_output = ctx;
- g_switch_mode_fun = fun;
+ END_EVENT;
+}
+
+void help_set_force_refresh_function(force_refresh fun) {
+ BEGING_EVENT;
+ g_interface.force_refresh = fun;
END_EVENT;
}
@@ -660,36 +774,165 @@
}
void help_do_repaint_cycle_completed(void) {
- int ret;
- if (g_atomic_modeset_enable == 0) {
- //Update the props
- BEGING_EVENT;
- connector_list* current;
- for_each_list(current, &global_connector_list) {
- int i;
- if (current->data == NULL) {
- continue;
- }
- drmModeConnector* conn = current->data;
- drm_property_info* props = current->props;
- for (i = 0; i < DRM_CONNECTOR_PROPERTY__COUNT; i++) {
- if (props[i].need_change) {
- if (props[i].prop_id == 0) {
- DEBUG_INFO("%s prop_id is 0", props[i].name);
- continue;
- }
- if (props[i].prop_id) {
- DEBUG_INFO("Set %s to %lld", props[i].name, props[i].new_value);
- //ret = drmModeConnectorSetProperty(g_drm_fd, conn->connector_id, props[i].prop_id, props[i].new_value);
- if (ret) {
- DEBUG_INFO("Update property error");
- }
- }
- props[i].need_change = 0;
- }
- }
+ json_object* task = NULL;
+ json_object* result = NULL;
+ bool got_task = false;
+ DEBUG_INFO("cycle_completed");
+ do {
+ if (!have_task(&g_task_after_repaint_cycle)) {
+ break;
}
+
+ BEGING_EVENT;
+ got_task = get_task(&g_task_after_repaint_cycle, &task);
END_EVENT;
+ if (got_task && task) {
+ process_task(task, &result);
+ }
+ } while (got_task);
+}
+
+void dump_output_status(const char* fname) {
+#if DEBUG
+ compositor_output_list* current;
+ fprintf(stderr, "Dump output info at [%s]:", fname);
+ int id = 0;
+ for_each_list(current, &g_output_list) {
+ fprintf(stderr, "==>[output:%d(%p):%p enable=%d conn=%p ", id, current, current->data, current->enable, current->conn.data);
+ if (current->conn.data)
+ fprintf(stderr, "type=%d", current->conn.data->connector_type);
+ fprintf(stderr, "]");
+ id++;
+ };
+ fprintf(stderr, "\n");
+#endif
+}
+
+void help_updata_compositor_output(struct compositor_output* older, struct compositor_output* newer) {
+ if (older == newer) {
+ return;
}
+ BEGING_EVENT;
+ compositor_output_list* current;
+ bool updated = false;
+ int update_index = 0;
+
+ int index = 0;
+ DEBUG_INFO("update old:%p, newer:%p", older, newer);
+ for_each_list(current, &g_output_list) {
+ //remove old output or replace same one
+ if (older == NULL) {
+ if (current->data == newer) {
+ updated = true;
+ update_index = index;
+ break;
+ }
+ } else if (current->data == older) {
+ updated = true;
+ update_index = index;
+ break;
+ }
+ index++;
+ }
+
+ index = 0;
+ //remove the output same as newer
+ for_each_list(current, &g_output_list) {
+ if (current->data == newer) {
+ if (updated && update_index != index) {
+ //hold a empty output
+ current->data = NULL;
+ //current->conn = NULL;
+ current->enable = false;
+ } else if (!updated) {
+ updated = true;
+ update_index = index;
+ }
+ }
+ index++;
+ }
+ //Seek to newer position
+ index = 0;
+ for_each_list(current, &g_output_list) {
+ if (updated && index == update_index) {
+ //current is the newer position
+ break;
+ }
+ if (!updated && current->data == NULL) {
+ //put into empty pos
+ updated = true;
+ update_index = index;
+ break;
+ }
+ index++;
+ }
+
+ if (!updated && newer != NULL) {
+ //Need append a empty element at end
+ //And Use current element
+ assert(current->next == NULL);
+ current->next = calloc(sizeof(compositor_output_list), 1);
+ if (current->next == NULL) {
+ fprintf(stderr, "out of memory, can't append output list [%s]\n", __func__);
+ goto error_out;
+ }
+ current->next->prev = current;
+ current->next->next = NULL;
+ updated = true;
+ update_index = index;
+ }
+ {
+ // do real update newer output
+ current->data = newer;
+ //current->conn = NULL;
+ current->enable = false;
+ }
+
+ //shrink empty output
+ index = 0;
+ bool need_remove_prev = false;
+ compositor_output_list* need_remove = NULL;
+ compositor_output_list* last_element = &g_output_list;
+ for (current = &g_output_list; current != NULL; current = current->next) {
+ //remain one empty element at the end
+ if (current->data == NULL && current != &g_output_list && current->next != NULL) {
+ current->prev->next = current->next;
+ current->next->prev = current->prev;
+ free(current);
+ } else if (current->next != NULL) {
+ //last not empty element or first element
+ last_element = current;
+ }
+ }
+
+ if (g_output_list.data == NULL && last_element != &g_output_list) {
+ //swap end with first, then remove the last
+ last_element->prev->next = last_element->next;
+ last_element->next = g_output_list.next;
+ last_element->prev = NULL;
+ memcpy(&g_output_list, last_element, sizeof(connector_list));
+ free(last_element);
+ }
+
+error_out:
+ dump_output_status(__func__);
+ END_EVENT;
+}
+
+void help_switch_compositor_output(struct compositor_output* output, bool enable) {
+ compositor_output_list* current;
+ if (output == NULL)
+ return;
+
+ BEGING_EVENT;
+ for_each_list(current, &g_output_list) {
+ if (output == current->data) {
+ current->enable = enable;
+ break;
+ }
+ }
+
+ dump_output_status(__func__);
+ END_EVENT;
}
diff --git a/display_framework/src/extension/weston-6.0/compositor-drm-help.h b/display_framework/src/extension/weston-6.0/compositor-drm-help.h
index 0d70665..453c966 100644
--- a/display_framework/src/extension/weston-6.0/compositor-drm-help.h
+++ b/display_framework/src/extension/weston-6.0/compositor-drm-help.h
@@ -38,10 +38,16 @@
uint32_t w, h;
} drm_helper_size;
-typedef void* output_ctx;
+/* A dummy define, we will not use compositor_output's implement (the way same as wl_surface)
+ * it save weston_output for weston
+ */
+struct compositor_output;
-/* the switch_mode function which used by message handle*/
-typedef int (*switch_mode)(output_ctx ctx, drm_helper_mode* mode);
+/* For compatible with old version */
+typedef struct compositor_output* output_ctx;
+
+typedef int (*switch_mode)(struct compositor_output* output, drm_helper_mode* mode);
+typedef int (*force_refresh)(struct compositor_output* output);
/* create a ipc thread to handle(m_message_handle)
* the message from client
@@ -72,7 +78,9 @@
void help_delete_connector(drmModeConnector* connector);
/*Call it when update weston compositor's switch mode function*/
-void help_set_switch_mode_function(output_ctx ctx, switch_mode fun);
+void help_set_switch_mode_function(struct compositor_output* output, switch_mode fun);
+
+void help_set_force_refresh_function(force_refresh fun);
/*Call it when need update you prop befor atomic commit*/
int help_atomic_req_add_prop(drmModeAtomicReq *req);
@@ -80,6 +88,12 @@
/*Call it when repaint cycle completed*/
void help_do_repaint_cycle_completed(void);
+/*Call it when compositor output changed (for multi-screen) */
+void help_updata_compositor_output(struct compositor_output* old_output,
+ struct compositor_output* new_output);
+
+void help_switch_compositor_output(struct compositor_output* output, bool enable);
+
#ifdef __cplusplus
}
#endif
diff --git a/display_framework/src/ipc/ipc.c b/display_framework/src/ipc/ipc.c
index 07a798e..06071c2 100644
--- a/display_framework/src/ipc/ipc.c
+++ b/display_framework/src/ipc/ipc.c
@@ -351,6 +351,7 @@
if (ret != 0) {
DEBUG_INFO("client connect failed!");
close(client_socket);
+ free(ctx->name);
free(ctx);
return NULL;
}