#include <stdlib.h>
#include <stdio.h>
#include <poll.h>
+#include <math.h>
#include "gralloc_drm.h"
#include "gralloc_drm_priv.h"
#include <hardware_legacy/uevent.h>
#include <drm_fourcc.h>
+struct uevent {
+ const char *action;
+ const char *path;
+ const char *subsystem;
+ const char *device_name;
+ int major;
+ int minor;
+ int hotplug;
+ int switchstate;
+};
+
+static void parse_event(const char *msg, struct uevent *uevent)
+{
+ uevent->action = "";
+ uevent->path = "";
+ uevent->subsystem = "";
+ uevent->major = -1;
+ uevent->minor = -1;
+ uevent->device_name = "";
+ uevent->hotplug = 0;
+ uevent->switchstate = -1;
+
+ while (*msg) {
+ if (!strncmp(msg, "ACTION=", 7)) {
+ msg += 7;
+ uevent->action = msg;
+ } else if (!strncmp(msg, "DEVPATH=", 8)) {
+ msg += 8;
+ uevent->path = msg;
+ } else if (!strncmp(msg, "SUBSYSTEM=", 10)) {
+ msg += 10;
+ uevent->subsystem = msg;
+ } else if (!strncmp(msg, "MAJOR=", 6)) {
+ msg += 6;
+ uevent->major = atoi(msg);
+ } else if (!strncmp(msg, "MINOR=", 6)) {
+ msg += 6;
+ uevent->minor = atoi(msg);
+ } else if (!strncmp(msg, "DEVNAME=", 8)) {
+ msg += 8;
+ uevent->device_name = msg;
+ } else if (!strncmp(msg, "HOTPLUG=1", 9)) {
+ msg += 9;
+ uevent->hotplug = 1;
+ } else if (!strncmp(msg, "SWITCH_STATE=", 13)) {
+ msg += 13;
+ uevent->switchstate = atoi(msg);
+ }
+
+ /* advance to after the next \0 */
+ while (*msg++)
+ ;
+ }
+}
+
/*
* Return true if a bo needs fb.
*/
{
switch(hal_format) {
case HAL_PIXEL_FORMAT_RGB_888:
- case HAL_PIXEL_FORMAT_RGBX_8888:
case HAL_PIXEL_FORMAT_BGRA_8888:
return DRM_FORMAT_XRGB8888;
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ return DRM_FORMAT_XBGR8888;
case HAL_PIXEL_FORMAT_RGBA_8888:
- return DRM_FORMAT_RGBA8888;
+// return DRM_FORMAT_ABGR8888;
+ return DRM_FORMAT_XBGR8888;
case HAL_PIXEL_FORMAT_RGB_565:
return DRM_FORMAT_RGB565;
case HAL_PIXEL_FORMAT_YV12:
static int resolve_drm_format(struct gralloc_drm_bo_t *bo,
uint32_t *pitches, uint32_t *offsets, uint32_t *handles)
{
- memset(pitches, 0, 4 * sizeof(uint32_t));
- memset(offsets, 0, 4 * sizeof(uint32_t));
- memset(handles, 0, 4 * sizeof(uint32_t));
+ struct gralloc_drm_t *drm = bo->drm;
pitches[0] = bo->handle->stride;
handles[0] = bo->fb_handle;
- int format = drm_format_from_hal(bo->handle->format);
-
- // handle 'special formats'
- switch(bo->handle->format) {
- case HAL_PIXEL_FORMAT_YV12:
+ /* driver takes care of HW specific padding, alignment etc. */
+ if (drm->drv->resolve_format)
+ drm->drv->resolve_format(drm->drv, bo,
+ pitches, offsets, handles);
- // U and V stride are half of Y plane
- pitches[2] = pitches[0]/2;
- pitches[1] = pitches[0]/2;
+ return drm_format_from_hal(bo->handle->format);
+}
- // like I420 but U and V are in reverse order
- offsets[2] = offsets[0] +
- pitches[0] * bo->handle->height;
- offsets[1] = offsets[2] +
- pitches[2] * bo->handle->height/2;
+/*
+ * Returns planes that are supported for a particular format
+ */
+unsigned int planes_for_format(struct gralloc_drm_t *drm,
+ int hal_format)
+{
+ unsigned int i, j, mask = 0;
+ unsigned int drm_format = drm_format_from_hal(hal_format);
+ struct gralloc_drm_plane_t *plane = drm->planes;
- handles[1] = handles[2] = handles[0];
- break;
+ /* no planes available */
+ if (!plane)
+ return 0;
- case HAL_PIXEL_FORMAT_DRM_NV12:
+ /* iterate through planes, mark those that match format */
+ for (i=0; i<drm->plane_resources->count_planes; i++, plane++)
+ for (j=0; j<plane->drm_plane->count_formats; j++)
+ if (plane->drm_plane->formats[j] == drm_format)
+ mask |= (1U << plane->drm_plane->plane_id);
- // U and V are interleaved in 2nd plane
- pitches[1] = pitches[0];
- offsets[1] = offsets[0] +
- pitches[0] * bo->handle->height;
- handles[1] = handles[0];
- break;
- }
- return format;
+ return mask;
}
/*
*/
int gralloc_drm_bo_add_fb(struct gralloc_drm_bo_t *bo)
{
- uint32_t pitches[4];
- uint32_t offsets[4];
- uint32_t handles[4];
+ uint32_t pitches[4] = { 0, 0, 0, 0 };
+ uint32_t offsets[4] = { 0, 0, 0, 0 };
+ uint32_t handles[4] = { 0, 0, 0, 0 };
if (bo->fb_id)
return 0;
}
/*
+ * Set a plane.
+ */
+static int gralloc_drm_bo_setplane(struct gralloc_drm_t *drm,
+ struct gralloc_drm_plane_t *plane)
+{
+ struct gralloc_drm_bo_t *bo = NULL;
+ int err;
+
+ if (plane->handle)
+ bo = gralloc_drm_bo_from_handle(plane->handle);
+
+ // create a framebuffer if does not exist
+ if (bo && bo->fb_id == 0) {
+ err = gralloc_drm_bo_add_fb(bo);
+ if (err) {
+ ALOGE("%s: could not create drm fb, (%s)",
+ __func__, strerror(-err));
+ return err;
+ }
+ }
+
+ err = drmModeSetPlane(drm->fd,
+ plane->drm_plane->plane_id,
+ drm->primary->crtc_id,
+ bo ? bo->fb_id : 0,
+ 0, // flags
+ plane->dst_x,
+ plane->dst_y,
+ plane->dst_w,
+ plane->dst_h,
+ plane->src_x << 16,
+ plane->src_y << 16,
+ plane->src_w << 16,
+ plane->src_h << 16);
+
+ if (err) {
+ /* clear plane_mask so that this buffer won't be tried again */
+ struct gralloc_drm_handle_t *drm_handle =
+ (struct gralloc_drm_handle_t *) plane->handle;
+ drm_handle->plane_mask = 0;
+
+ ALOGE("drmModeSetPlane : error (%s) (plane %d crtc %d fb %d)",
+ strerror(-err),
+ plane->drm_plane->plane_id,
+ drm->primary->crtc_id,
+ bo ? bo->fb_id : 0);
+ }
+
+ if (plane->prev)
+ gralloc_drm_bo_decref(plane->prev);
+
+ if (bo)
+ bo->refcount++;
+
+ plane->prev = bo;
+
+ return err;
+}
+
+/*
+ * Returns if a particular plane is supported with the implementation
+ */
+static unsigned is_plane_supported(const struct gralloc_drm_t *drm,
+ const struct gralloc_drm_plane_t *plane)
+{
+ /* Planes are only supported on primary pipe for now */
+ return plane->drm_plane->possible_crtcs & (1 << drm->primary->pipe);
+}
+
+/*
+ * Sets all the active planes to be displayed.
+ */
+static void gralloc_drm_set_planes(struct gralloc_drm_t *drm)
+{
+ struct gralloc_drm_plane_t *plane = drm->planes;
+ unsigned int i;
+ for (i = 0; i < drm->plane_resources->count_planes;
+ i++, plane++) {
+ /* plane is not in use at all */
+ if (!plane->active && !plane->handle)
+ continue;
+
+ /* plane is active, safety check if it is supported */
+ if (!is_plane_supported(drm, plane))
+ ALOGE("%s: plane %d is not supported",
+ __func__, plane->drm_plane->plane_id);
+
+ /*
+ * Disable overlay if it is not active
+ * or if there is error during setplane
+ */
+ if (!plane->active)
+ plane->handle = 0;
+
+ if (gralloc_drm_bo_setplane(drm, plane))
+ plane->active = 0;
+ }
+}
+
+/*
+ * Interface for HWC, used to reserve a plane for a layer.
+ */
+int gralloc_drm_reserve_plane(struct gralloc_drm_t *drm,
+ buffer_handle_t handle,
+ uint32_t id,
+ uint32_t dst_x,
+ uint32_t dst_y,
+ uint32_t dst_w,
+ uint32_t dst_h,
+ uint32_t src_x,
+ uint32_t src_y,
+ uint32_t src_w,
+ uint32_t src_h)
+{
+ int j;
+ struct gralloc_drm_handle_t *drm_handle =
+ gralloc_drm_handle(handle);
+ int plane_count = drm->plane_resources->count_planes;
+ struct gralloc_drm_plane_t *plane = drm->planes;
+
+ /* no supported planes for this handle */
+ if (!drm_handle->plane_mask) {
+ ALOGE("%s: buffer %p cannot be shown on a plane\n",
+ __func__, drm_handle);
+ return -EINVAL;
+ }
+
+ for (j = 0; j < plane_count; j++, plane++) {
+
+ /*
+ * handle may be suitable to be shown on a plane, in
+ * addition we need to check that this particular plane
+ * is supported by the current implementation
+ */
+ if (!is_plane_supported(drm, plane))
+ continue;
+
+ /* if plane is available and can support this buffer */
+ if (!plane->active &&
+ drm_handle->plane_mask &
+ (1U << plane->drm_plane->plane_id)) {
+
+ plane->dst_x = dst_x;
+ plane->dst_y = dst_y;
+ plane->dst_w = dst_w;
+ plane->dst_h = dst_h;
+ plane->src_x = src_x;
+ plane->src_y = src_y;
+ plane->src_w = src_w;
+ plane->src_h = src_h;
+ plane->handle = handle;
+ plane->id = id;
+ plane->active = 1;
+
+ return 0;
+ }
+ }
+
+ /* no free planes available */
+ return -EBUSY;
+}
+
+/*
+ * Interface for HWC, used to disable all the overlays. Plane id
+ * is also set to 0 as it should be mappable to a particular layer only
+ * if it has been reserved with 'reserve_plane'.
+ */
+void gralloc_drm_disable_planes(struct gralloc_drm_t *drm)
+{
+ struct gralloc_drm_plane_t *plane = drm->planes;
+ unsigned int i;
+
+ for (i = 0; i < drm->plane_resources->count_planes; i++, plane++) {
+ plane->active = 0;
+ plane->id = 0;
+ }
+}
+
+/*
+ * Interface for HWC, used to change handle of a reserved plane.
+ */
+int gralloc_drm_set_plane_handle(struct gralloc_drm_t *drm,
+ uint32_t id, buffer_handle_t handle)
+{
+ struct gralloc_drm_plane_t *plane = drm->planes;
+ unsigned i;
+
+ for (i = 0; i < drm->plane_resources->count_planes; i++, plane++)
+ if (plane->active && plane->id == id) {
+ plane->handle = handle;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int drm_kms_blit_to_mirror_connectors(struct gralloc_drm_t *drm, struct gralloc_drm_bo_t *bo)
+{
+ int ret = 0;
+ for (int i = 1; i < drm->output_capacity; i++) {
+ struct gralloc_drm_output *output = &drm->outputs[i];
+
+ if (output->active && output->output_mode == DRM_OUTPUT_CLONED && output->bo) {
+
+ int dst_x1 = 0, dst_y1 = 0;
+
+ if (output->bo->handle->width > bo->handle->width)
+ dst_x1 = (output->bo->handle->width - bo->handle->width) / 2;
+ if (output->bo->handle->height > bo->handle->height)
+ dst_y1 = (output->bo->handle->height - bo->handle->height) / 2;
+
+ drm->drv->blit(drm->drv, output->bo, bo,
+ dst_x1, dst_y1,
+ dst_x1 + bo->handle->width,
+ dst_y1 + bo->handle->height,
+ 0, 0, bo->handle->width, bo->handle->height);
+
+ ret = drmModePageFlip(drm->fd, output->crtc_id, output->bo->fb_id, 0, NULL);
+ if (ret && errno != EBUSY)
+ ALOGE("failed to perform page flip for output (%s) (crtc %d fb %d))",
+ strerror(errno), output->crtc_id, output->bo->fb_id);
+ }
+ }
+
+ return ret;
+}
+
+/*
* Schedule a page flip.
*/
static int drm_kms_page_flip(struct gralloc_drm_t *drm,
if (!bo)
return 0;
- pthread_mutex_lock(&drm->hdmi_mutex);
- if (drm->hdmi.active && drm->hdmi_mode == HDMI_CLONED) {
- ret = drmModePageFlip(drm->fd, drm->hdmi.crtc_id, bo->fb_id, 0, NULL);
- if (ret && errno != EBUSY)
- ALOGE("failed to perform page flip for hdmi (%s) (crtc %d fb %d))",
- strerror(errno), drm->hdmi.crtc_id, bo->fb_id);
- }
- pthread_mutex_unlock(&drm->hdmi_mutex);
+ pthread_mutex_lock(&drm->outputs_mutex);
+ drm_kms_blit_to_mirror_connectors(drm, bo);
+ pthread_mutex_unlock(&drm->outputs_mutex);
- ret = drmModePageFlip(drm->fd, drm->primary.crtc_id, bo->fb_id,
+ /* set planes to be displayed */
+ gralloc_drm_set_planes(drm);
+
+ ret = drmModePageFlip(drm->fd, drm->primary->crtc_id, bo->fb_id,
DRM_MODE_PAGE_FLIP_EVENT, (void *) drm);
if (ret) {
ALOGE("failed to perform page flip for primary (%s) (crtc %d fb %d))",
- strerror(errno), drm->primary.crtc_id, bo->fb_id);
+ strerror(errno), drm->primary->crtc_id, bo->fb_id);
/* try to set mode for next frame */
if (errno != EBUSY)
drm->first_post = 1;
dst = (drm->next_front) ?
drm->next_front :
drm->current_front;
- drm->drv->copy(drm->drv, dst, bo, 0, 0,
+ drm->drv->blit(drm->drv, dst, bo, 0, 0,
+ bo->handle->width,
+ bo->handle->height,
+ 0, 0,
bo->handle->width,
bo->handle->height);
bo = dst;
}
- ret = drm_kms_set_crtc(drm, &drm->primary, bo->fb_id);
+ ret = drm_kms_set_crtc(drm, drm->primary, bo->fb_id);
if (!ret) {
drm->first_post = 0;
drm->current_front = bo;
drm->next_front = NULL;
}
- pthread_mutex_lock(&drm->hdmi_mutex);
- if (drm->hdmi.active && drm->hdmi_mode == HDMI_CLONED)
- drm_kms_set_crtc(drm, &drm->hdmi, bo->fb_id);
- pthread_mutex_unlock(&drm->hdmi_mutex);
+ pthread_mutex_lock(&drm->outputs_mutex);
+ for (int i = 1; i < drm->output_capacity; i++) {
+ struct gralloc_drm_output *output = &drm->outputs[i];
+ if (output->active && output->output_mode == DRM_OUTPUT_CLONED && output->bo)
+ drm_kms_set_crtc(drm, output, output->bo->fb_id);
+ }
+ pthread_mutex_unlock(&drm->outputs_mutex);
return ret;
}
* will be written by CPU
*/
if (drm->mode_sync_flip ||
- (drm->current_front->handle->usage &
- GRALLOC_USAGE_SW_WRITE_MASK))
+ (drm->current_front->handle->usage &
+ GRALLOC_USAGE_SW_WRITE_MASK))
drm_kms_page_flip(drm, NULL);
}
break;
case DRM_SWAP_COPY:
drm_kms_wait_for_post(drm, 0);
- drm->drv->copy(drm->drv, drm->current_front,
+ drm->drv->blit(drm->drv, drm->current_front,
bo, 0, 0,
bo->handle->width,
+ bo->handle->height,
+ 0, 0,
+ bo->handle->width,
bo->handle->height);
if (drm->mode_quirk_vmwgfx)
ret = drmModeDirtyFB(drm->fd, drm->current_front->fb_id, &drm->clip, 1);
break;
case DRM_SWAP_SETCRTC:
drm_kms_wait_for_post(drm, 0);
- ret = drm_kms_set_crtc(drm, &drm->primary, bo->fb_id);
+ ret = drm_kms_set_crtc(drm, drm->primary, bo->fb_id);
- pthread_mutex_lock(&drm->hdmi_mutex);
- if (drm->hdmi.active && drm->hdmi_mode == HDMI_CLONED)
- drm_kms_set_crtc(drm, &drm->hdmi, bo->fb_id);
- pthread_mutex_unlock(&drm->hdmi_mutex);
+ pthread_mutex_lock(&drm->outputs_mutex);
+ for (int i = 1; i < drm->output_capacity; i++) {
+ struct gralloc_drm_output *output = &drm->outputs[i];
+ if (output->active && output->output_mode == DRM_OUTPUT_CLONED && output->bo)
+ drm_kms_set_crtc(drm, output, output->bo->fb_id);
+ }
+ pthread_mutex_unlock(&drm->outputs_mutex);
drm->current_front = bo;
break;
/* create the real front buffer */
front = gralloc_drm_bo_create(drm,
- drm->primary.mode.hdisplay,
- drm->primary.mode.vdisplay,
- drm->primary.fb_format,
+ drm->primary->mode.hdisplay,
+ drm->primary->mode.vdisplay,
+ drm->primary->fb_format,
GRALLOC_USAGE_HW_FB);
if (front && gralloc_drm_bo_add_fb(front)) {
gralloc_drm_bo_decref(front);
ALOGD("will use %s for fb posting", swap_mode);
}
-static drmModeModeInfoPtr find_mode(drmModeConnectorPtr connector, int *bpp)
+#define MARGIN_PERCENT 1.8 /* % of active vertical image*/
+#define CELL_GRAN 8.0 /* assumed character cell granularity*/
+#define MIN_PORCH 1 /* minimum front porch */
+#define V_SYNC_RQD 3 /* width of vsync in lines */
+#define H_SYNC_PERCENT 8.0 /* width of hsync as % of total line */
+#define MIN_VSYNC_PLUS_BP 550.0 /* min time of vsync + back porch (microsec) */
+#define M 600.0 /* blanking formula gradient */
+#define C 40.0 /* blanking formula offset */
+#define K 128.0 /* blanking formula scaling factor */
+#define J 20.0 /* blanking formula scaling factor */
+/* C' and M' are part of the Blanking Duty Cycle computation */
+#define C_PRIME (((C - J) * K / 256.0) + J)
+#define M_PRIME (K / 256.0 * M)
+
+static drmModeModeInfoPtr generate_mode(int h_pixels, int v_lines, float freq)
+{
+ float h_pixels_rnd;
+ float v_lines_rnd;
+ float v_field_rate_rqd;
+ float top_margin;
+ float bottom_margin;
+ float interlace;
+ float h_period_est;
+ float vsync_plus_bp;
+ float v_back_porch;
+ float total_v_lines;
+ float v_field_rate_est;
+ float h_period;
+ float v_field_rate;
+ float v_frame_rate;
+ float left_margin;
+ float right_margin;
+ float total_active_pixels;
+ float ideal_duty_cycle;
+ float h_blank;
+ float total_pixels;
+ float pixel_freq;
+ float h_freq;
+
+ float h_sync;
+ float h_front_porch;
+ float v_odd_front_porch_lines;
+ int interlaced = 0;
+ int margins = 0;
+
+ drmModeModeInfoPtr m = malloc(sizeof(drmModeModeInfo));
+
+ h_pixels_rnd = rint((float) h_pixels / CELL_GRAN) * CELL_GRAN;
+ v_lines_rnd = interlaced ? rint((float) v_lines) / 2.0 : rint((float) v_lines);
+ v_field_rate_rqd = interlaced ? (freq * 2.0) : (freq);
+ top_margin = margins ? rint(MARGIN_PERCENT / 100.0 * v_lines_rnd) : (0.0);
+ bottom_margin = margins ? rint(MARGIN_PERCENT / 100.0 * v_lines_rnd) : (0.0);
+ interlace = interlaced ? 0.5 : 0.0;
+ h_period_est = (((1.0 / v_field_rate_rqd) - (MIN_VSYNC_PLUS_BP / 1000000.0)) / (v_lines_rnd + (2 * top_margin) + MIN_PORCH + interlace) * 1000000.0);
+ vsync_plus_bp = rint(MIN_VSYNC_PLUS_BP / h_period_est);
+ v_back_porch = vsync_plus_bp - V_SYNC_RQD;
+ total_v_lines = v_lines_rnd + top_margin + bottom_margin + vsync_plus_bp + interlace + MIN_PORCH;
+ v_field_rate_est = 1.0 / h_period_est / total_v_lines * 1000000.0;
+ h_period = h_period_est / (v_field_rate_rqd / v_field_rate_est);
+ v_field_rate = 1.0 / h_period / total_v_lines * 1000000.0;
+ v_frame_rate = interlaced ? v_field_rate / 2.0 : v_field_rate;
+ left_margin = margins ? rint(h_pixels_rnd * MARGIN_PERCENT / 100.0 / CELL_GRAN) * CELL_GRAN : 0.0;
+ right_margin = margins ? rint(h_pixels_rnd * MARGIN_PERCENT / 100.0 / CELL_GRAN) * CELL_GRAN : 0.0;
+ total_active_pixels = h_pixels_rnd + left_margin + right_margin;
+ ideal_duty_cycle = C_PRIME - (M_PRIME * h_period / 1000.0);
+ h_blank = rint(total_active_pixels * ideal_duty_cycle / (100.0 - ideal_duty_cycle) / (2.0 * CELL_GRAN)) * (2.0 * CELL_GRAN);
+ total_pixels = total_active_pixels + h_blank;
+ pixel_freq = total_pixels / h_period;
+ h_freq = 1000.0 / h_period;
+ h_sync = rint(H_SYNC_PERCENT / 100.0 * total_pixels / CELL_GRAN) * CELL_GRAN;
+ h_front_porch = (h_blank / 2.0) - h_sync;
+ v_odd_front_porch_lines = MIN_PORCH + interlace;
+
+ m->clock = ceil(pixel_freq) * 1000;
+ m->hdisplay = (int) (h_pixels_rnd);
+ m->hsync_start = (int) (h_pixels_rnd + h_front_porch);
+ m->hsync_end = (int) (h_pixels_rnd + h_front_porch + h_sync);
+ m->htotal = (int) (total_pixels);
+ m->hskew = 0;
+ m->vdisplay = (int) (v_lines_rnd);
+ m->vsync_start = (int) (v_lines_rnd + v_odd_front_porch_lines);
+ m->vsync_end = (int) (int) (v_lines_rnd + v_odd_front_porch_lines + V_SYNC_RQD);
+ m->vtotal = (int) (total_v_lines);
+ m->vscan = 0;
+ m->vrefresh = freq;
+ m->flags = 10;
+ m->type = 64;
+
+ return (m);
+}
+
+static int mode_distance_best_fit(
+ int xres_base,
+ int yres_base,
+ int xres,
+ int yres,
+ double prefered_aspect)
+{
+ const double eps = 0.3;
+
+ if (xres_base > xres || yres_base > yres) {
+ // if the res cannot cover base res, return max int
+ return INT_MAX;
+ } else if (fabs((double) xres / yres - prefered_aspect) > eps) {
+ // if the aspect is too different with prefered_aspect, return max int
+ return INT_MAX;
+ } else {
+ return (xres - xres_base) * (xres - xres_base) +
+ (yres - yres_base) * (yres - yres_base) +
+ ((xres / yres) - prefered_aspect) * ((xres / yres) - prefered_aspect);
+ }
+}
+
+static int mode_distance_closest(int xres_base, int yres_base, int xres, int yres) {
+ return (xres - xres_base) * (xres - xres_base) +
+ (yres - yres_base) * (yres - yres_base);
+}
+
+static drmModeModeInfoPtr find_mode(drmModeConnectorPtr connector, int *bpp, drmModeModeInfoPtr primary_mode)
{
char value[PROPERTY_VALUE_MAX];
drmModeModeInfoPtr mode;
int dist, i;
- int xres = 0, yres = 0;
+ int xres = 0, yres = 0, rate = 0;
+ int forcemode = 0;
+ int bestfit = 0;
if (property_get("debug.drm.mode", value, NULL)) {
char *p = value, *end;
ALOGI("will find the closest match for %dx%d@%d",
xres, yres, *bpp);
}
- }
- else {
+ } else if (property_get("debug.drm.mode.force", value, NULL)) {
+ char *p = value, *end;
+ *bpp = 0;
+
+ /* parse <xres>x<yres>[@<refreshrate>] */
+ if (sscanf(value, "%dx%d@%d", &xres, &yres, &rate) != 3) {
+ rate = 60;
+ if (sscanf(value, "%dx%d", &xres, &yres) != 2)
+ xres = yres = 0;
+ }
+
+ if (xres && yres && rate) {
+ ALOGI("will use %dx%d@%dHz", xres, yres, rate);
+ forcemode = 1;
+ }
+ } else if (primary_mode != NULL) {
+ xres = primary_mode->hdisplay;
+ yres = primary_mode->vdisplay;
+ *bpp = 0;
+ bestfit = 1;
+ ALOGI("will find the best fit for %dx%d", xres, yres);
+ } else {
*bpp = 0;
}
- mode = NULL;
dist = INT_MAX;
- for (i = 0; i < connector->count_modes; i++) {
- drmModeModeInfoPtr m = &connector->modes[i];
- int tmp;
- if (xres && yres) {
- tmp = (m->hdisplay - xres) * (m->hdisplay - xres) +
- (m->vdisplay - yres) * (m->vdisplay - yres);
- }
- else {
- /* use the first preferred mode */
- tmp = (m->type & DRM_MODE_TYPE_PREFERRED) ? 0 : dist;
- }
+ if (forcemode)
+ mode = generate_mode(xres, yres, rate);
+ else {
+ mode = NULL;
+ for (i = 0; i < connector->count_modes; i++) {
+ drmModeModeInfoPtr m = &connector->modes[i];
+ int tmp;
+
+ if (xres && yres) {
+ if (bestfit) {
+ tmp = mode_distance_best_fit(
+ xres,
+ yres,
+ m->hdisplay,
+ m->vdisplay,
+ (double) connector->modes[0].hdisplay / connector->modes[0].vdisplay);
+ } else {
+ tmp = mode_distance_closest(xres, yres, m->hdisplay, m->vdisplay);
+ }
+ }
+ else {
+ /* use the first preferred mode */
+ tmp = (m->type & DRM_MODE_TYPE_PREFERRED) ? 0 : dist;
+ }
- if (tmp < dist) {
- mode = m;
- dist = tmp;
- if (!dist)
- break;
+ if (tmp < dist) {
+ mode = m;
+ dist = tmp;
+ if (!dist)
+ break;
+ }
}
}
if (!mode)
mode = &connector->modes[0];
+ ALOGI("Established mode:");
+ ALOGI("clock: %d, hdisplay: %d, hsync_start: %d, hsync_end: %d, htotal: %d, hskew: %d", mode->clock, mode->hdisplay, mode->hsync_start, mode->hsync_end, mode->htotal, mode->hskew);
+ ALOGI("vdisplay: %d, vsync_start: %d, vsync_end: %d, vtotal: %d, vscan: %d, vrefresh: %d", mode->vdisplay, mode->vsync_start, mode->vsync_end, mode->vtotal, mode->vscan, mode->vrefresh);
+ ALOGI("flags: %d, type: %d, name %s", mode->flags, mode->type, mode->name);
+
*bpp /= 8;
return mode;
}
+static int used_crtcs = 0;
+
/*
* Initialize KMS with a connector.
*/
{
drmModeEncoderPtr encoder;
drmModeModeInfoPtr mode;
- static int used_crtcs = 0;
int bpp, i;
if (!connector->count_modes)
if (i == drm->resources->count_crtcs)
return -EINVAL;
+ output->bo = NULL;
output->crtc_id = drm->resources->crtcs[i];
output->connector_id = connector->connector_id;
+ output->pipe = i;
/* print connector info */
if (connector->count_modes > 1) {
- ALOGI("there are %d modes on connector 0x%x",
+ ALOGI("there are %d modes on connector 0x%x, type %d",
connector->count_modes,
- connector->connector_id);
+ connector->connector_id,
+ connector->connector_type);
for (i = 0; i < connector->count_modes; i++)
ALOGI(" %s", connector->modes[i].name);
}
connector->modes[0].name);
}
- mode = find_mode(connector, &bpp);
+ if (drm->primary && output != drm->primary && property_get_bool("persist.remixos.disp_best_fit", 1)) {
+ mode = find_mode(connector, &bpp, &drm->primary->mode);
+ } else {
+ mode = find_mode(connector, &bpp, NULL);
+ }
ALOGI("the best mode is %s", mode->name);
break;
case 4:
default:
- output->fb_format = HAL_PIXEL_FORMAT_BGRA_8888;
+ output->fb_format = HAL_PIXEL_FORMAT_RGBA_8888;
break;
}
return 0;
}
+static struct gralloc_drm_output*
+drm_kms_init_with_new_connector(struct gralloc_drm_t *drm, drmModeConnectorPtr connector)
+{
+ struct gralloc_drm_output *output = NULL;
+
+ for (int i=0;i<drm->output_capacity;i++) {
+ if (!drm->outputs[i].active) {
+ output = &drm->outputs[i];
+ break;
+ }
+ }
+
+ if (output && !drm_kms_init_with_connector(drm, output, connector)) {
+ drm->output_count++;
+ output->active = 1;
+ if (!drm->primary) {
+ drm->primary = output;
+ }
+ }
+
+ return output;
+}
+
/*
* Fetch a connector of particular type
return NULL;
for (i = 0; i < drm->resources->count_connectors; i++) {
- drmModeConnectorPtr connector =
- connector = drmModeGetConnector(drm->fd,
+ drmModeConnectorPtr connector = drmModeGetConnector(drm->fd,
drm->resources->connectors[i]);
if (connector) {
if (connector->connector_type == type &&
/*
+ * Initializes external output with a connector and allocates
+ * a private framebuffer for it. This is called on startup if
+ * external cable is connected and also on hotplug events.
+ */
+static int init_external_output(struct gralloc_drm_t *drm,
+ drmModeConnectorPtr connector)
+{
+ struct gralloc_drm_output *output;
+ output = drm_kms_init_with_new_connector(drm, connector);
+ if (output == NULL || !output->active) {
+ ALOGW("connector 0x%x output is null or not active", connector->connector_id);
+ return -EINVAL;
+ }
+
+ ALOGD("%s, allocate private buffer for hdmi [%dx%d]",
+ __func__, output->mode.hdisplay, output->mode.vdisplay);
+
+ output->bo = gralloc_drm_bo_create(drm,
+ output->mode.hdisplay, output->mode.vdisplay,
+ output->fb_format,
+ GRALLOC_USAGE_HW_RENDER);
+
+ int err = gralloc_drm_bo_add_fb(output->bo);
+ if (err) {
+ ALOGE("%s: could not create drm fb, (%s)",
+ __func__, strerror(-err));
+ return err;
+ }
+
+ output->output_mode = DRM_OUTPUT_CLONED;
+ output->active = 1;
+
+ /* This is a hack to workaround mirror mode render error */
+ drm_kms_set_crtc(drm, output, output->bo->fb_id);
+
+ return 0;
+}
+
+
+static int init_connectors(struct gralloc_drm_t *drm)
+{
+ if (!drm->resources)
+ return -1;
+
+ for (int i = 0; i<drm->resources->count_connectors; i++) {
+ drmModeConnectorPtr connector = drmModeGetConnector(drm->fd,
+ drm->resources->connectors[i]);
+ if (connector) {
+ if (connector->connection == DRM_MODE_CONNECTED &&
+ connector->connector_id != drm->primary->connector_id) {
+ init_external_output(drm, connector);
+ } else if (connector->connection == DRM_MODE_DISCONNECTED) {
+ for (int j = 1; j < drm->output_capacity; j++) {
+ if (drm->outputs[j].connector_id == connector->connector_id && drm->outputs[j].active) {
+ drm->outputs[j].active = 0;
+ used_crtcs &= ~(1 << drm->outputs[j].pipe);
+ gralloc_drm_bo_decref(drm->outputs[j].bo);
+ drm->outputs[j].bo = NULL;
+ break;
+ }
+ }
+ }
+ drmModeFreeConnector(connector);
+ }
+ }
+
+ return 0;
+}
+
+
+/*
* Thread that listens to uevents and checks if hdmi state changes
*/
-static void *hdmi_observer(void *data)
+static void *extcon_observer(void *data)
{
- static char uevent_desc[4096];
+ char uevent_desc[2048];
drmModeConnectorPtr hdmi;
struct gralloc_drm_t *drm =
(struct gralloc_drm_t *) data;
+ struct uevent event;
uevent_init();
/* this polls */
int len = uevent_next_event(uevent_desc, sizeof(uevent_desc) - 2);
+ if (!len) continue;
- if(len && strstr(uevent_desc, "devices/virtual/switch/hdmi")) {
-
- /* check what changed */
- const char *prop = uevent_desc + strlen(uevent_desc) + 1;
+ parse_event(uevent_desc, &event);
+ ALOGD_IF(0, "event { '%s', '%s', '%s', %d, %d, %d, %d }\n",
+ event.action, event.path, event.subsystem,
+ event.major, event.minor,
+ event.switchstate, event.hotplug);
- while (*prop) {
+ if (!strcmp(event.path, "devices/virtual/switch/hdmi")) {
+ if (event.switchstate != -1) {
+ pthread_mutex_lock(&drm->outputs_mutex);
- const char *state = strstr(prop, "SWITCH_STATE=");
- if (state) {
- unsigned int value = 0;
- state += strlen("SWITCH_STATE=");
- value = atoi(state);
+ init_connectors(drm);
- pthread_mutex_lock(&drm->hdmi_mutex);
+ /* will trigger modeset */
+ drm->first_post = 1;
- if (value) {
- hdmi = fetch_connector(drm, DRM_MODE_CONNECTOR_HDMIA);
- if (hdmi) {
- drm_kms_init_with_connector(drm, &drm->hdmi, hdmi);
- drmModeFreeConnector(hdmi);
-
- /* will trigger modeset */
- drm->first_post = 1;
-
- /* HACK, assume same mode for now */
- memcpy(&drm->hdmi.mode, &drm->primary.mode,
- sizeof(drmModeModeInfo));
+ pthread_mutex_unlock(&drm->outputs_mutex);
+ }
+ } else if (!strcmp(event.subsystem, "drm") &&
+ !strcmp(event.device_name, "dri/card0") && event.hotplug) {
+ pthread_mutex_lock(&drm->outputs_mutex);
- drm->hdmi_mode = HDMI_CLONED;
- drm->hdmi.active = 1;
- pthread_mutex_unlock(&drm->hdmi_mutex);
- }
- break;
- } else {
- drm->hdmi.active = 0;
- pthread_mutex_unlock(&drm->hdmi_mutex);
- break;
- }
+ init_connectors(drm);
- pthread_mutex_unlock(&drm->hdmi_mutex);
- }
+ /* will trigger modeset */
+ drm->first_post = 1;
- /* next property/value pair */
- prop += strlen(prop) + 1;
- if (prop - uevent_desc >= len)
- break;
- }
+ pthread_mutex_unlock(&drm->outputs_mutex);
}
}
*/
int gralloc_drm_init_kms(struct gralloc_drm_t *drm)
{
- drmModeConnectorPtr lvds, hdmi;
- int i, ret;
-
if (drm->resources)
return 0;
if (!drm->plane_resources) {
ALOGD("no planes found from drm resources");
} else {
+ unsigned int i, j;
+
ALOGD("supported drm planes and formats");
/* fill a helper structure for hwcomposer */
drm->planes = calloc(drm->plane_resources->count_planes,
sizeof(struct gralloc_drm_plane_t));
for (i = 0; i < drm->plane_resources->count_planes; i++) {
-
- unsigned int j;
-
drm->planes[i].drm_plane = drmModeGetPlane(drm->fd,
drm->plane_resources->planes[i]);
}
}
+ drm->output_capacity = drm->resources->count_connectors;
+ drm->output_count = 0;
+
+ drm->outputs = calloc(sizeof(*drm->outputs), drm->output_capacity);
+
/* find the crtc/connector/mode to use */
- lvds = fetch_connector(drm, DRM_MODE_CONNECTOR_LVDS);
- if (lvds) {
- drm_kms_init_with_connector(drm, &drm->primary, lvds);
- drmModeFreeConnector(lvds);
- drm->primary.active = 1;
+ uint32_t internal_connectors[] = {
+ DRM_MODE_CONNECTOR_LVDS,
+ DRM_MODE_CONNECTOR_DSI,
+ };
+ for (size_t i = 0; i < sizeof(internal_connectors) / sizeof(uint32_t); i++) {
+ drmModeConnectorPtr connector;
+ connector = fetch_connector(drm, internal_connectors[i]);
+ if (connector) {
+ drm_kms_init_with_new_connector(drm, connector);
+ drmModeFreeConnector(connector);
+ break;
+ }
}
/* if still no connector, find first connected connector and try it */
- if (!drm->primary.active) {
+ if (!drm->primary) {
+ int i;
+ ALOGI("Try to find the first connected connector");
for (i = 0; i < drm->resources->count_connectors; i++) {
drmModeConnectorPtr connector;
connector = drmModeGetConnector(drm->fd,
drm->resources->connectors[i]);
if (connector) {
- if (connector->connection == DRM_MODE_CONNECTED) {
- if (!drm_kms_init_with_connector(drm,
- &drm->primary, connector))
- break;
- }
-
+ bool found = (connector->connection == DRM_MODE_CONNECTED)
+ && !!drm_kms_init_with_new_connector(drm, connector);
drmModeFreeConnector(connector);
+ if (found)
+ break;
}
}
if (i == drm->resources->count_connectors) {
}
}
- /* check if hdmi is connected already */
- hdmi = fetch_connector(drm, DRM_MODE_CONNECTOR_HDMIA);
- if (hdmi) {
-
- if (hdmi->connector_id == drm->primary.connector_id) {
- /* special case: our primary connector is hdmi */
- goto skip_hdmi_modes;
- }
+ /* Mirror mode need driver support for blitting. which is not implemented for
+ drivers other than intel. skip external detection for them to avoid crash
+ */
+ if (drm->drv->blit) {
+ init_connectors(drm);
- drm_kms_init_with_connector(drm, &drm->hdmi, hdmi);
- drmModeFreeConnector(hdmi);
-
- /* HACK, assume same mode for now */
- memcpy(&drm->hdmi.mode, &drm->primary.mode,
- sizeof(drmModeModeInfo));
-
- drm->hdmi_mode = HDMI_CLONED;
- drm->hdmi.active = 1;
+ /* launch external display observer thread */
+ pthread_mutex_init(&drm->outputs_mutex, NULL);
+ pthread_create(&drm->hotplug_thread, NULL, extcon_observer, drm);
}
- /* launch hdmi observer thread */
- pthread_mutex_init(&drm->hdmi_mutex, NULL);
- pthread_create(&drm->hdmi_hotplug_thread, NULL, hdmi_observer, drm);
-
-skip_hdmi_modes:
-
drm_kms_init_features(drm);
drm->first_post = 1;
drm->plane_resources = NULL;
}
+ /* destroy private buffer of external output */
+ for (int i = 1; i < drm->output_capacity; i++)
+ if (drm->outputs[i].bo)
+ gralloc_drm_bo_decref(drm->outputs[i].bo);
+
+ free(drm->outputs);
+
drm_singleton = NULL;
}
struct framebuffer_device_t *fb)
{
*((uint32_t *) &fb->flags) = 0x0;
- *((uint32_t *) &fb->width) = drm->primary.mode.hdisplay;
- *((uint32_t *) &fb->height) = drm->primary.mode.vdisplay;
- *((int *) &fb->stride) = drm->primary.mode.hdisplay;
- *((float *) &fb->fps) = drm->primary.mode.vrefresh;
-
- *((int *) &fb->format) = drm->primary.fb_format;
- *((float *) &fb->xdpi) = drm->primary.xdpi;
- *((float *) &fb->ydpi) = drm->primary.ydpi;
+ *((uint32_t *) &fb->width) = drm->primary->mode.hdisplay;
+ *((uint32_t *) &fb->height) = drm->primary->mode.vdisplay;
+ *((int *) &fb->stride) = drm->primary->mode.hdisplay;
+ *((float *) &fb->fps) = drm->primary->mode.vrefresh;
+
+ *((int *) &fb->format) = drm->primary->fb_format;
+ *((float *) &fb->xdpi) = drm->primary->xdpi;
+ *((float *) &fb->ydpi) = drm->primary->ydpi;
*((int *) &fb->minSwapInterval) = drm->swap_interval;
*((int *) &fb->maxSwapInterval) = drm->swap_interval;
}