/**
* @brief Create the client vblank object of the given client output
+ * @details
+ * tdm client vblank basically uses the HW vblank resource. Therefore, if HW vblank
+ * is not available for some reasons, such as output disconnection and dpms off,
+ * #tdm_client_vblank_wait will return error. If you want it to work propery in spite
+ * of these reasons, you can use #tdm_client_vblank_set_enable_fake to get SW fake
+ * vblank events.
* @param[in] output The client output object
* @param[out] error #TDM_ERROR_NONE if success. Otherwise, error value.
* @return A client vblank object if success. Otherwise, NULL.
/**
* @brief Enable/Disable the fake vblank to the client vblank object
* @details
- * If enable_fake == 0, #tdm_client_vblank_wait will return TDM_ERROR_DPMS_OFF
- * when DPMS off. Otherwise, #tdm_client_vblank_wait will return TDM_ERROR_NONE
+ * If enable_fake == 0, #tdm_client_vblank_wait will return error when HW vblank is
+ * not available. Otherwise, #tdm_client_vblank_wait will return TDM_ERROR_NONE
* as success. Once #tdm_client_vblank_wait returns TDM_ERROR_NONE, the user client
* vblank handler(#tdm_client_vblank_handler) SHOULD be called after the given
* interval of #tdm_client_vblank_wait. Default is @b disable.
/**
* @brief Wait for a vblank
* @details
- * This function will return TDM_ERROR_DPMS_OFF when DPMS off. However,
+ * This function will return error when HW vblank resource is not available. However,
* #tdm_client_vblank_wait will return TDM_ERROR_NONE as success if
* #tdm_client_vblank_set_enable_fake sets true. Once #tdm_client_vblank_wait
* returns TDM_ERROR_NONE, the user client vblank handler(#tdm_client_vblank_handler)
/**
* @brief Wait for a vblank with the target sequence number
* @details
- * This function will return TDM_ERROR_DPMS_OFF when DPMS off. However,
+ * This function will return error when HW vblank resource is not available. However,
* #tdm_client_vblank_wait will return TDM_ERROR_NONE as success if
* #tdm_client_vblank_set_enable_fake sets true. Once #tdm_client_vblank_wait_seq
* returns TDM_ERROR_NONE, the user client vblank handler(#tdm_client_vblank_handler)
* - use a tdm_event_loop_source object only.
*/
+#define TDM_VBLANK_DEFAULT_VREFRESH 30
+
/* We expact "(unsigned int)(0.0016667 / 0.0016667) = 1". But it becomes 0. */
#define TDM_TIME_MARGIN 0.0000001
tdm_display *dpy;
tdm_output *output;
+ tdm_output_conn_status connection;
tdm_output_dpms dpms;
unsigned int vrefresh;
unsigned int check_HW_or_SW;
char name[TDM_NAME_LEN];
unsigned int fps;
+ unsigned int fps_changeable;
int offset;
unsigned int enable_fake;
unsigned int ignore_global_fps;
}
}
+static void
+_tdm_vblank_update_output_info(tdm_private_vblank *private_vblank)
+{
+ tdm_output *output = private_vblank->output;
+ tdm_output_conn_status connection = TDM_OUTPUT_CONN_STATUS_DISCONNECTED;
+ tdm_output_dpms dpms = TDM_OUTPUT_DPMS_OFF;
+ unsigned int vrefresh = 0;
+
+ tdm_output_get_conn_status(output, &connection);
+
+ if (connection != TDM_OUTPUT_CONN_STATUS_DISCONNECTED) {
+ const tdm_output_mode *mode = NULL;
+ tdm_output_get_mode(output, &mode);
+ vrefresh = (mode) ? mode->vrefresh : 0;
+ tdm_output_get_dpms(output, &dpms);
+ }
+
+ if (vrefresh == 0)
+ vrefresh = TDM_VBLANK_DEFAULT_VREFRESH;
+
+ private_vblank->dpms = dpms;
+ private_vblank->connection = connection;
+ private_vblank->vrefresh = vrefresh;
+ private_vblank->HW_vblank_gap = 1.0 / private_vblank->vrefresh;
+
+ if (private_vblank->fps_changeable)
+ private_vblank->fps = vrefresh;
+
+ VDB("dpms(%d) connection(%d) vrefresh(%d) fps(%d)",
+ private_vblank->dpms, private_vblank->connection,
+ private_vblank->vrefresh, private_vblank->fps);
+}
+
static void
_tdm_vblank_cb_output_change(tdm_output *output, tdm_output_change_type type,
tdm_value value, void *user_data)
if (private_vblank->dpms == value.u32)
break;
VDB("dpms %s", tdm_dpms_str(value.u32));
- private_vblank->dpms = value.u32;
+ _tdm_vblank_update_output_info(private_vblank);
private_vblank->check_HW_or_SW = 1;
if (private_vblank->dpms != TDM_OUTPUT_DPMS_ON) {
if (private_vblank->enable_fake)
}
break;
case TDM_OUTPUT_CHANGE_CONNECTION:
+ if (private_vblank->connection == value.u32)
+ break;
VDB("output %s", tdm_status_str(value.u32));
- if (value.u32 == TDM_OUTPUT_CONN_STATUS_DISCONNECTED)
- _tdm_vblank_free_HW_wait(private_vblank, 0, 0);
+ _tdm_vblank_update_output_info(private_vblank);
+ private_vblank->check_HW_or_SW = 1;
+ if (private_vblank->connection == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) {
+ if (private_vblank->enable_fake)
+ _tdm_vblank_change_to_SW(private_vblank);
+ else
+ _tdm_vblank_free_HW_wait(private_vblank, TDM_ERROR_OUTPUT_DISCONNECTED, 1);
+ }
break;
default:
break;
tdm_vblank_create(tdm_display *dpy, tdm_output *output, tdm_error *error)
{
tdm_private_vblank *private_vblank;
- const tdm_output_mode *mode = NULL;
- tdm_output_dpms dpms = TDM_OUTPUT_DPMS_ON;
tdm_error ret;
TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(dpy != NULL, TDM_ERROR_INVALID_PARAMETER, NULL);
vblank_list_inited = 1;
}
- tdm_output_get_mode(output, &mode);
- if (!mode) {
- if (error)
- *error = TDM_ERROR_OPERATION_FAILED;
- TDM_ERR("no mode");
- return NULL;
- }
-
- tdm_output_get_dpms(output, &dpms);
-
private_vblank = calloc(1, sizeof * private_vblank);
if (!private_vblank) {
if (error)
private_vblank->owner_tid = syscall(SYS_gettid);
private_vblank->dpy = dpy;
private_vblank->output = output;
- private_vblank->dpms = dpms;
- private_vblank->vrefresh = mode->vrefresh;
- private_vblank->HW_vblank_gap = 1.0 / private_vblank->vrefresh;
-
private_vblank->check_HW_or_SW = 1;
- private_vblank->fps = mode->vrefresh;
+ private_vblank->fps_changeable = 1;
+
+ _tdm_vblank_update_output_info(private_vblank);
strncpy(private_vblank->name, TDM_VBLANK_DEFAULT_NAME, TDM_NAME_LEN - 1);
private_vblank->name[TDM_NAME_LEN - 1] = '\0';
_tdm_vblank_valid_list_add(&private_vblank->valid_link, &valid_vblank_list);
if (tdm_debug_module & TDM_DEBUG_VBLANK)
- VIN("created. vrefresh(%d) dpms(%d)",
- private_vblank->vrefresh, private_vblank->dpms);
+ VIN("created. vrefresh(%d) dpms(%d) connection(%d)",
+ private_vblank->vrefresh, private_vblank->dpms, private_vblank->connection);
return (tdm_vblank *)private_vblank;
}
return TDM_ERROR_NONE;
private_vblank->fps = fps;
+ private_vblank->fps_changeable = 0;
private_vblank->check_HW_or_SW = 1;
if (tdm_debug_module & TDM_DEBUG_VBLANK)
return TDM_ERROR_BAD_REQUEST;
}
- if (private_vblank->dpms != TDM_OUTPUT_DPMS_ON && !private_vblank->enable_fake) {
- VIN("can't wait a vblank because of DPMS off");
- return TDM_ERROR_DPMS_OFF;
+ if (!private_vblank->enable_fake) {
+ if (private_vblank->connection == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) {
+ VIN("can't wait a vblank: output disconnected");
+ return TDM_ERROR_OUTPUT_DISCONNECTED;
+ }
+ if (private_vblank->dpms != TDM_OUTPUT_DPMS_ON) {
+ VIN("can't wait a vblank: DPMS off");
+ return TDM_ERROR_DPMS_OFF;
+ }
}
wait_info = calloc(1, sizeof * wait_info);
*/
if (private_vblank->vrefresh % fps)
wait_info->type = VBLANK_TYPE_SW;
- else if (private_vblank->dpms == TDM_OUTPUT_DPMS_OFF)
+ else if (private_vblank->dpms == TDM_OUTPUT_DPMS_OFF ||
+ private_vblank->connection == TDM_OUTPUT_CONN_STATUS_DISCONNECTED)
wait_info->type = VBLANK_TYPE_SW_FAKE;
else if (private_vblank->offset == 0)
wait_info->type = VBLANK_TYPE_HW;
ret = _tdm_vblank_wait_SW(wait_info);
else {
ret = _tdm_vblank_wait_HW(wait_info);
- if (ret == TDM_ERROR_DPMS_OFF)
+ if (ret == TDM_ERROR_OUTPUT_DISCONNECTED || ret == TDM_ERROR_DPMS_OFF)
ret = _tdm_vblank_wait_SW(wait_info);
}