C++程序  |  886行  |  33.61 KB

/*
 * Copyright (c) 2011 Intel Corporation. All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sub license, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial portions
 * of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
 * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * Authors:
 *    Jason Hu  <jason.hu@intel.com>
 *    Zhaohan Ren  <zhaohan.ren@intel.com>
 *
 */

#include <unistd.h>
#include "psb_xrandr.h"
#include "psb_x11.h"
#include "psb_drv_debug.h"

/* Global variable for xrandr */
psb_xrandr_info_p psb_xrandr_info;

#define INIT_DRIVER_DATA    psb_driver_data_p driver_data = (psb_driver_data_p) ctx->pDriverData
#define INIT_OUTPUT_PRIV    psb_x11_output_p output = (psb_x11_output_p)(((psb_driver_data_p)ctx->pDriverData)->ws_priv)

#define MWM_HINTS_DECORATIONS (1L << 1)
typedef struct {
    int flags;
    int functions;
    int decorations;
    int input_mode;
    int status;
} MWMHints;

char* location2string(psb_xrandr_location location)
{
    switch (location) {
    case ABOVE:
        return "ABOVE";
        break;
    case BELOW:
        return "BELOW";
        break;
    case LEFT_OF:
        return "LEFT_OF";
        break;
    case RIGHT_OF:
        return "RIGHT_OF";
        break;
    default:
        return "NORMAL";
        break;
    }
}

static int RRrotation2VArotation(Rotation rotation)
{
    switch (rotation) {
    case RR_Rotate_0:
        return VA_ROTATION_NONE;
    case RR_Rotate_90:
        return VA_ROTATION_270;
    case RR_Rotate_180:
        return VA_ROTATION_180;
    case RR_Rotate_270:
        return VA_ROTATION_90;
    }

    return 0;
}
static psb_xrandr_crtc_p get_crtc_by_id(RRCrtc crtc_id)
{
    psb_xrandr_crtc_p p_crtc;
    for (p_crtc = psb_xrandr_info->crtc_head; p_crtc; p_crtc = p_crtc->next)
        if (p_crtc->crtc_id == crtc_id)
            return p_crtc;
    return NULL;
}

static void psb_xrandr_hdmi_property(VADriverContextP ctx)
{
    INIT_DRIVER_DATA;
    Atom *props;
    Atom actual_type;
    XRRPropertyInfo *propinfo;
    int i, nprop, actual_format;
    unsigned long nitems, bytes_after;
    char* prop_name;
    unsigned char* prop;

    /* Check HDMI properties */
    props = XRRListOutputProperties(psb_xrandr_info->dpy, psb_xrandr_info->extend_output->output_id, &nprop);
    if (!props) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "Xrandr: XRRListOutputProperties failed\n", psb_xrandr_info->extend_output->output_id);
        return;
    }

    drv_debug_msg(VIDEO_DEBUG_GENERAL, "Xrandr: extend output %08x has %d properties\n", psb_xrandr_info->extend_output->output_id, nprop);

    for (i = 0; i < nprop; i++) {
        XRRGetOutputProperty(psb_xrandr_info->dpy, psb_xrandr_info->extend_output->output_id, props[i],
                             0, 100, False, False, AnyPropertyType, &actual_type, &actual_format,
                             &nitems, &bytes_after, &prop);

        propinfo = XRRQueryOutputProperty(psb_xrandr_info->dpy, psb_xrandr_info->extend_output->output_id, props[i]);
        if (!propinfo) {
            drv_debug_msg(VIDEO_DEBUG_ERROR, "Xrandr: get output %08x prop %08x failed\n", psb_xrandr_info->extend_output->output_id, props[i]);
            return;
        }

        prop_name = XGetAtomName(psb_xrandr_info->dpy, props[i]);

        /* Currently all properties are XA_INTEGER, 32 */
        if (!strcmp(prop_name, "ExtVideoMode")) {
            psb_xrandr_info->hdmi_extvideo_prop->ExtVideoMode = (int)((INT32*)prop)[0];
            drv_debug_msg(VIDEO_DEBUG_GENERAL, "Xrandr: ExtVideoMode (%08x)\n", psb_xrandr_info->hdmi_extvideo_prop->ExtVideoMode);
        } else if (!strcmp(prop_name, "ExtVideoMode_Xres")) {
            psb_xrandr_info->hdmi_extvideo_prop->ExtVideoMode_XRes = (int)((INT32*)prop)[0];
            drv_debug_msg(VIDEO_DEBUG_GENERAL, "Xrandr: ExtVideoMode_XRes (%08x)\n", psb_xrandr_info->hdmi_extvideo_prop->ExtVideoMode_XRes);
        } else if (!strcmp(prop_name, "ExtVideoMode_Yres")) {
            psb_xrandr_info->hdmi_extvideo_prop->ExtVideoMode_YRes = (int)((INT32*)prop)[0];
            drv_debug_msg(VIDEO_DEBUG_GENERAL, "Xrandr: ExtVideoMode_YRes (%08x)\n", psb_xrandr_info->hdmi_extvideo_prop->ExtVideoMode_YRes);
        } else if (!strcmp(prop_name, "ExtVideoMode_X_Offset")) {
            psb_xrandr_info->hdmi_extvideo_prop->ExtVideoMode_X_Offset = (int)((INT32*)prop)[0];
            drv_debug_msg(VIDEO_DEBUG_GENERAL, "Xrandr: ExtVideoMode_X_Offset (%08x)\n", psb_xrandr_info->hdmi_extvideo_prop->ExtVideoMode_X_Offset);
        } else if (!strcmp(prop_name, "ExtVideoMode_Y_Offset")) {
            psb_xrandr_info->hdmi_extvideo_prop->ExtVideoMode_Y_Offset = (int)((INT32*)prop)[0];
            drv_debug_msg(VIDEO_DEBUG_GENERAL, "Xrandr: ExtVideoMode_Y_Offset (%08x)\n", psb_xrandr_info->hdmi_extvideo_prop->ExtVideoMode_Y_Offset);
        } else if (!strcmp(prop_name, "ExtVideoMode_Center")) {
            psb_xrandr_info->hdmi_extvideo_prop->ExtVideoMode_Center = (int)((INT32*)prop)[0];
            drv_debug_msg(VIDEO_DEBUG_GENERAL, "Xrandr: ExtVideoMode_Center (%08x)\n", psb_xrandr_info->hdmi_extvideo_prop->ExtVideoMode_Center);
        } else if (!strcmp(prop_name, "ExtVideoMode_SubTitle")) {
            psb_xrandr_info->hdmi_extvideo_prop->ExtVideoMode_SubTitle = (int)((INT32*)prop)[0];
            drv_debug_msg(VIDEO_DEBUG_GENERAL, "Xrandr: ExtVideoMode_SubTitle (%08x)\n", psb_xrandr_info->hdmi_extvideo_prop->ExtVideoMode_SubTitle);
        } else if (!strcmp(prop_name, "ExtDesktopMode")) {
            if ((psb_xrandr_info->hdmi_extvideo_prop->ExtDesktopMode != EXTENDEDVIDEO) &&
                ((int)((INT32*)prop)[0] == EXTENDEDVIDEO)) {
                driver_data->xrandr_dirty |= PSB_NEW_EXTVIDEO;
            }
            psb_xrandr_info->hdmi_extvideo_prop->ExtDesktopMode = (int)((INT32*)prop)[0];
            drv_debug_msg(VIDEO_DEBUG_GENERAL, "Xrandr: ExtDesktopMode (%08x)\n", psb_xrandr_info->hdmi_extvideo_prop->ExtDesktopMode);
        } else if (!strcmp(prop_name, "OverscanMode")) {
            psb_xrandr_info->hdmi_extvideo_prop->OverscanMode = (int)((INT32*)prop)[0];
            drv_debug_msg(VIDEO_DEBUG_GENERAL, "Xrandr: OverscanMode (%08x)\n", psb_xrandr_info->hdmi_extvideo_prop->OverscanMode);
        } else if (!strcmp(prop_name, "PANELFITTING")) {
            psb_xrandr_info->hdmi_extvideo_prop->PANELFITTING = (int)((INT32*)prop)[0];
            drv_debug_msg(VIDEO_DEBUG_GENERAL, "Xrandr: PANELFITTING (%08x)\n", psb_xrandr_info->hdmi_extvideo_prop->PANELFITTING);
        }
    }
}

static void psb_xrandr_mipi_location_init(psb_output_device_mode output_device_mode)
{
    psb_xrandr_crtc_p local_crtc = NULL, extend_crtc = NULL;

    switch (output_device_mode) {
    case SINGLE_MIPI0:
        psb_xrandr_info->hdmi_extvideo_prop->ExtDesktopMode = SINGLE;
        psb_xrandr_info->local_crtc[0]->location = NORMAL;
        return;
    case SINGLE_MIPI1:
        psb_xrandr_info->hdmi_extvideo_prop->ExtDesktopMode = SINGLE;
        psb_xrandr_info->local_crtc[1]->location = NORMAL;
        return;
    case MIPI0_MIPI1:
        local_crtc = psb_xrandr_info->local_crtc[0];
        extend_crtc = psb_xrandr_info->local_crtc[1];
        break;
    default:
        break;
    }

    if (!local_crtc || !extend_crtc) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "Failed to get crtc info\n");
        return;
    }

    /* MIPI1 clone MIPI0 */
    if (local_crtc->x == 0 && local_crtc->y == 0 &&
        extend_crtc->x == 0 && extend_crtc->y == 0) {
        psb_xrandr_info->hdmi_extvideo_prop->ExtDesktopMode = CLONE;
        extend_crtc->location = NORMAL;
    } else {
        /* MIPI1 entend MIPI0 */
        psb_xrandr_info->hdmi_extvideo_prop->ExtDesktopMode = EXTENDED;
        if (local_crtc->y == extend_crtc->height)
            extend_crtc->location = ABOVE;
        else if (extend_crtc->y == local_crtc->height)
            extend_crtc->location = BELOW;
        else if (local_crtc->x == extend_crtc->width)
            extend_crtc->location = LEFT_OF;
        else if (extend_crtc->x == local_crtc->width)
            extend_crtc->location = RIGHT_OF;
    }
}

static void psb_xrandr_hdmi_location_init(psb_output_device_mode output_device_mode)
{
    psb_xrandr_crtc_p local_crtc = NULL, extend_crtc = NULL;

    switch (output_device_mode) {
    case SINGLE_HDMI:
        psb_xrandr_info->hdmi_extvideo_prop->ExtDesktopMode = SINGLE;
        psb_xrandr_info->extend_crtc->location = NORMAL;
        return;
    case MIPI0_HDMI:
    case MIPI0_MIPI1_HDMI:
        local_crtc = psb_xrandr_info->local_crtc[0];
        extend_crtc = psb_xrandr_info->extend_crtc;
        break;
    case MIPI1_HDMI:
        local_crtc = psb_xrandr_info->local_crtc[1];
        extend_crtc = psb_xrandr_info->extend_crtc;
        break;
    default:
        break;
    }

    if (!local_crtc || !extend_crtc) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "Failed to get crtc info\n");
        return;
    }

    if (psb_xrandr_info->hdmi_extvideo_prop->ExtDesktopMode == CLONE)
        psb_xrandr_info->extend_crtc->location = NORMAL;

    if (psb_xrandr_info->hdmi_extvideo_prop->ExtDesktopMode == EXTENDED
        || psb_xrandr_info->hdmi_extvideo_prop->ExtDesktopMode == EXTENDEDVIDEO) {
        if (local_crtc->y == extend_crtc->height)
            psb_xrandr_info->extend_crtc->location = ABOVE;
        else if (extend_crtc->y == local_crtc->height)
            psb_xrandr_info->extend_crtc->location = BELOW;
        else if (local_crtc->x == extend_crtc->width)
            psb_xrandr_info->extend_crtc->location = LEFT_OF;
        else if (extend_crtc->x == local_crtc->width)
            psb_xrandr_info->extend_crtc->location = RIGHT_OF;
    }
}

static void psb_xrandr_coordinate_init(VADriverContextP ctx)
{
    INIT_DRIVER_DATA;
    psb_xrandr_output_p p_output;

    psb_xrandr_info->output_changed = 1;

    for (p_output = psb_xrandr_info->output_head; p_output; p_output = p_output->next) {
        if (p_output->connection == RR_Connected) {
            if (!strcmp(p_output->name, "MIPI0")) {
                if (p_output->crtc) {
                    psb_xrandr_info->mipi0_enabled = 1;
                    psb_xrandr_info->local_output[0] = p_output;
                    psb_xrandr_info->local_crtc[0] = p_output->crtc;
                    if (psb_xrandr_info->mipi0_rotation != p_output->crtc->rotation) {
                        psb_xrandr_info->mipi0_rotation = p_output->crtc->rotation;
                        driver_data->mipi0_rotation = RRrotation2VArotation(psb_xrandr_info->mipi0_rotation);
                        driver_data->xrandr_dirty |= PSB_NEW_ROTATION;
                    }
                } else {
                    psb_xrandr_info->mipi0_enabled = 0;
                    psb_xrandr_info->local_output[0] = NULL;
                    psb_xrandr_info->local_crtc[0] = NULL;
                }
            } else if (!strcmp(p_output->name, "MIPI1")) {
                if (p_output->crtc) {
                    psb_xrandr_info->mipi1_enabled = 1;
                    psb_xrandr_info->local_output[1] = p_output;
                    psb_xrandr_info->local_crtc[1] = p_output->crtc;
                    if (psb_xrandr_info->mipi1_rotation != p_output->crtc->rotation) {
                        psb_xrandr_info->mipi1_rotation = p_output->crtc->rotation;
                        driver_data->mipi1_rotation = RRrotation2VArotation(psb_xrandr_info->mipi1_rotation);
                        driver_data->xrandr_dirty |= PSB_NEW_ROTATION;
                    }
                } else {
                    psb_xrandr_info->mipi1_enabled = 0;
                    psb_xrandr_info->local_output[1] = NULL;
                    psb_xrandr_info->local_crtc[1] = NULL;
                }
            } else if (!strcmp(p_output->name, "TMDS0-1")) {
                if (p_output->crtc) {
                    psb_xrandr_info->hdmi_enabled = 1;
                    psb_xrandr_info->extend_output = p_output;
                    psb_xrandr_info->extend_crtc = p_output->crtc;
                    if (psb_xrandr_info->hdmi_rotation != p_output->crtc->rotation) {
                        psb_xrandr_info->hdmi_rotation = p_output->crtc->rotation;
                        driver_data->hdmi_rotation = RRrotation2VArotation(psb_xrandr_info->hdmi_rotation);
                        driver_data->xrandr_dirty |= PSB_NEW_ROTATION;
                    }
                } else {
                    psb_xrandr_info->hdmi_enabled = 0;
                    psb_xrandr_info->extend_output = NULL;
                    psb_xrandr_info->extend_crtc = NULL;
                }
            } else if (!strcmp(p_output->name, "LVDS0") && IS_MRST(driver_data)) {
                if (p_output->crtc) {
                    psb_xrandr_info->lvds0_enabled = 1;
                    psb_xrandr_info->local_output[0] = p_output;
                    psb_xrandr_info->local_crtc[0] = p_output->crtc;
                } else {
                    psb_xrandr_info->lvds0_enabled = 0;
                    psb_xrandr_info->local_output[0] = NULL;
                    psb_xrandr_info->local_crtc[0] = NULL;
                }
            }
        }
    }

    /* for MRST */
    if (IS_MRST(driver_data) && psb_xrandr_info->lvds0_enabled) {
        psb_xrandr_info->hdmi_extvideo_prop->ExtDesktopMode = SINGLE;
        psb_xrandr_info->output_device_mode = SINGLE_LVDS0;
        psb_xrandr_info->local_crtc[0]->location = NORMAL;
        return;
    }

    /* HDMI + either MIPI0 or MIPI1 */
    if (psb_xrandr_info->hdmi_enabled) {

        /* Get HDMI properties if it is enabled*/
        psb_xrandr_hdmi_property(ctx);

        /* Only HDMI */
        if (!psb_xrandr_info->mipi0_enabled && !psb_xrandr_info->mipi1_enabled)
            psb_xrandr_info->output_device_mode = SINGLE_HDMI;

        /* HDMI + MIPI0 */
        if (psb_xrandr_info->mipi0_enabled && !psb_xrandr_info->mipi1_enabled)
            psb_xrandr_info->output_device_mode = MIPI0_HDMI;

        /* HDMI + MIPI1 */
        if (!psb_xrandr_info->mipi0_enabled && psb_xrandr_info->mipi1_enabled)
            psb_xrandr_info->output_device_mode = MIPI1_HDMI;

        /* HDMI + MIPI0 + MIPI1 */
        if (psb_xrandr_info->mipi0_enabled && psb_xrandr_info->mipi1_enabled)
            psb_xrandr_info->output_device_mode = MIPI0_MIPI1_HDMI;

        psb_xrandr_hdmi_location_init(psb_xrandr_info->output_device_mode);
    } else {
        /* MIPI0 + MIPI1 */
        if (psb_xrandr_info->mipi0_enabled && psb_xrandr_info->mipi1_enabled) {
            psb_xrandr_info->output_device_mode = MIPI0_MIPI1;
        } else {
            /* MIPI0/MIPI1 */
            if (psb_xrandr_info->mipi0_enabled)
                psb_xrandr_info->output_device_mode = SINGLE_MIPI0;
            else if (psb_xrandr_info->mipi1_enabled)
                psb_xrandr_info->output_device_mode = SINGLE_MIPI1;
        }

        psb_xrandr_mipi_location_init(psb_xrandr_info->output_device_mode);
    }
}

void psb_xrandr_refresh(VADriverContextP ctx)
{
    int i;

    XRROutputInfo *output_info;
    XRRCrtcInfo *crtc_info;

    psb_xrandr_crtc_p p_crtc;
    psb_xrandr_output_p p_output;

    pthread_mutex_lock(&psb_xrandr_info->psb_extvideo_mutex);

    //deinit crtc
    if (psb_xrandr_info->crtc_head) {
        while (psb_xrandr_info->crtc_head) {
            psb_xrandr_info->crtc_tail = psb_xrandr_info->crtc_head->next;

            free(psb_xrandr_info->crtc_head);

            psb_xrandr_info->crtc_head = psb_xrandr_info->crtc_tail;
        }
        psb_xrandr_info->crtc_head = psb_xrandr_info->crtc_tail = NULL;
    }

    for (i = 0; i < psb_xrandr_info->res->ncrtc; i++) {
        crtc_info = XRRGetCrtcInfo(psb_xrandr_info->dpy, psb_xrandr_info->res, psb_xrandr_info->res->crtcs[i]);
        if (crtc_info) {
            p_crtc = (psb_xrandr_crtc_p)calloc(1, sizeof(psb_xrandr_crtc_s));
            if (!p_crtc) {
                drv_debug_msg(VIDEO_DEBUG_ERROR, "output of memory\n");
                return;
            }

            if (i == 0)
                psb_xrandr_info->crtc_head = psb_xrandr_info->crtc_tail = p_crtc;

            p_crtc->crtc_id = psb_xrandr_info->res->crtcs[i];
            p_crtc->x = crtc_info->x;
            p_crtc->y = crtc_info->y;
            p_crtc->width = crtc_info->width;
            p_crtc->height = crtc_info->height;
            p_crtc->crtc_mode = crtc_info->mode;
            p_crtc->noutput = crtc_info->noutput;
            p_crtc->rotation = crtc_info->rotation;

            psb_xrandr_info->crtc_tail->next = p_crtc;
            p_crtc->next = NULL;
            psb_xrandr_info->crtc_tail = p_crtc;
        } else {
            drv_debug_msg(VIDEO_DEBUG_ERROR, "failed to get crtc_info\n");
            pthread_mutex_unlock(&psb_xrandr_info->psb_extvideo_mutex);
            return;
        }
    }

    //deinit output
    if (psb_xrandr_info->output_head) {
        while (psb_xrandr_info->output_head) {
            psb_xrandr_info->output_tail = psb_xrandr_info->output_head->next;

            free(psb_xrandr_info->output_head);

            psb_xrandr_info->output_head = psb_xrandr_info->output_tail;
        }
        psb_xrandr_info->output_head = psb_xrandr_info->output_tail = NULL;
    }
#if 0
    //destroy the full-screen window
    //FIXME: commited out for X Error message: BadDrawable, need more investigation
    if (va_output) {
        if (va_output->extend_drawable) {
            XDestroyWindow(ctx->native_dpy, va_output->extend_drawable);
            va_output->extend_drawable = 0;
            texture_priv->extend_dri_init_flag = 0;
        }
    }
#endif
    for (i = 0; i < psb_xrandr_info->res->noutput; i++) {
        output_info = XRRGetOutputInfo(psb_xrandr_info->dpy, psb_xrandr_info->res, psb_xrandr_info->res->outputs[i]);
        if (output_info) {
            p_output = (psb_xrandr_output_p)calloc(1, sizeof(psb_xrandr_output_s));
            if (!p_output) {
                drv_debug_msg(VIDEO_DEBUG_ERROR, "output of memory\n");
                return;
            }

            if (i == 0)
                psb_xrandr_info->output_head = psb_xrandr_info->output_tail = p_output;

            p_output->output_id = psb_xrandr_info->res->outputs[i];

            p_output->connection = output_info->connection;
            if (p_output->connection == RR_Connected)
                psb_xrandr_info->nconnected_output++;

            strcpy(p_output->name, output_info->name);

            if (output_info->crtc)
                p_output->crtc = get_crtc_by_id(output_info->crtc);
            else
                p_output->crtc = NULL;

            psb_xrandr_info->output_tail->next = p_output;
            p_output->next = NULL;
            psb_xrandr_info->output_tail = p_output;
        } else {
            drv_debug_msg(VIDEO_DEBUG_ERROR, "failed to get output_info\n");
            pthread_mutex_unlock(&psb_xrandr_info->psb_extvideo_mutex);
            return;
        }
    }

    psb_xrandr_coordinate_init(ctx);

    psb_RecalcRotate(ctx);
    pthread_mutex_unlock(&psb_xrandr_info->psb_extvideo_mutex);
}

static Bool
outputChangePredicate(Display *display, XEvent *event, char *args)
{
    int event_base, error_base;

    XRRQueryExtension(psb_xrandr_info->dpy, &event_base, &error_base);
    return ((event->type == event_base + RRNotify_OutputChange) ||
            ((event->type == ClientMessage) &&
             (((XClientMessageEvent*)event)->message_type == psb_xrandr_info->psb_exit_atom)));
}

void psb_xrandr_thread(void* arg)
{
    VADriverContextP ctx = (VADriverContextP)arg;
    INIT_DRIVER_DATA;
    int event_base, error_base;
    XEvent event;
    XRRQueryExtension(psb_xrandr_info->dpy, &event_base, &error_base);
    XRRSelectInput(psb_xrandr_info->dpy, psb_xrandr_info->root, RRScreenChangeNotifyMask | RRCrtcChangeNotifyMask | RROutputChangeNotifyMask | RROutputPropertyNotifyMask);
    drv_debug_msg(VIDEO_DEBUG_GENERAL, "Xrandr: psb xrandr thread start\n");

    while (1) {
        if (XCheckIfEvent(psb_xrandr_info->dpy, (XEvent *)&event, outputChangePredicate, NULL)) {
            if (event.type == ClientMessage) {
                drv_debug_msg(VIDEO_DEBUG_GENERAL, "Xrandr: receive ClientMessage event, thread should exit\n");
                XClientMessageEvent *evt;
                evt = (XClientMessageEvent*) & event;
                if (evt->message_type == psb_xrandr_info->psb_exit_atom) {
                    drv_debug_msg(VIDEO_DEBUG_GENERAL, "Xrandr: xrandr thread exit safely\n");
                    pthread_exit(NULL);
                }
            }
            switch (event.type - event_base) {
            case RRNotify_OutputChange:
                XRRUpdateConfiguration(&event);
                drv_debug_msg(VIDEO_DEBUG_GENERAL, "Xrandr: receive RRNotify_OutputChange event, refresh output/crtc info\n");
                driver_data->xrandr_update = 1;
                psb_xrandr_refresh(ctx);
                break;
            default:
                break;
            }
        }
        usleep(200000);
    }
}

Window psb_xrandr_create_full_screen_window(unsigned int destx, unsigned int desty, unsigned int destw, unsigned int desth)
{
    int x, y, width, height;
    Window win;

    x = psb_xrandr_info->extend_crtc->x;
    y = psb_xrandr_info->extend_crtc->y;
    width = psb_xrandr_info->extend_crtc->width;
    height = psb_xrandr_info->extend_crtc->height;

    if (destw == 0 || desth == 0) {
        destw = width;
        desth = height;
    }
    win = XCreateSimpleWindow(psb_xrandr_info->dpy, DefaultRootWindow(psb_xrandr_info->dpy), destx, desty, destw, desth, 0, 0, 0);

    MWMHints mwmhints;
    Atom MOTIF_WM_HINTS;

    mwmhints.flags = MWM_HINTS_DECORATIONS;
    mwmhints.decorations = 0; /* MWM_DECOR_BORDER */
    MOTIF_WM_HINTS = XInternAtom(psb_xrandr_info->dpy, "_MOTIF_WM_HINTS", False);
    XChangeProperty(psb_xrandr_info->dpy, win, MOTIF_WM_HINTS, MOTIF_WM_HINTS, sizeof(long) * 8,
                    PropModeReplace, (unsigned char*) &mwmhints, sizeof(mwmhints) / sizeof(long));

    XSetWindowAttributes attributes;
    attributes.override_redirect = 1;
    unsigned long valuemask;
    valuemask = CWOverrideRedirect ;
    XChangeWindowAttributes(psb_xrandr_info->dpy, win, valuemask, &attributes);

    XMapWindow(psb_xrandr_info->dpy, win);
    XFlush(psb_xrandr_info->dpy);
    return win;
}

int psb_xrandr_hdmi_enabled()
{
    int ret;
    pthread_mutex_lock(&psb_xrandr_info->psb_extvideo_mutex);
    ret = psb_xrandr_info->hdmi_enabled;
    pthread_mutex_unlock(&psb_xrandr_info->psb_extvideo_mutex);
    return ret;
}

int psb_xrandr_mipi0_enabled()
{
    int ret;
    pthread_mutex_lock(&psb_xrandr_info->psb_extvideo_mutex);
    ret = psb_xrandr_info->mipi0_enabled;
    pthread_mutex_unlock(&psb_xrandr_info->psb_extvideo_mutex);
    return ret;
}

int psb_xrandr_mipi1_enabled()
{
    int ret;
    pthread_mutex_lock(&psb_xrandr_info->psb_extvideo_mutex);
    ret = psb_xrandr_info->mipi1_enabled;
    pthread_mutex_unlock(&psb_xrandr_info->psb_extvideo_mutex);
    return ret;
}

int psb_xrandr_single_mode()
{
    int ret;
    pthread_mutex_lock(&psb_xrandr_info->psb_extvideo_mutex);
    ret = (psb_xrandr_info->hdmi_extvideo_prop->ExtDesktopMode == SINGLE) ? 1 : 0;
    pthread_mutex_unlock(&psb_xrandr_info->psb_extvideo_mutex);
    return ret;
}

int psb_xrandr_clone_mode()
{
    int ret;
    pthread_mutex_lock(&psb_xrandr_info->psb_extvideo_mutex);
    ret = (psb_xrandr_info->hdmi_extvideo_prop->ExtDesktopMode == CLONE) ? 1 : 0;
    pthread_mutex_unlock(&psb_xrandr_info->psb_extvideo_mutex);
    return ret;
}

int psb_xrandr_extend_mode()
{
    int ret;
    pthread_mutex_lock(&psb_xrandr_info->psb_extvideo_mutex);
    ret = (psb_xrandr_info->hdmi_extvideo_prop->ExtDesktopMode == EXTENDED) ? 1 : 0;
    pthread_mutex_unlock(&psb_xrandr_info->psb_extvideo_mutex);
    return ret;
}

int psb_xrandr_extvideo_mode()
{
    int ret;
    pthread_mutex_lock(&psb_xrandr_info->psb_extvideo_mutex);
    ret = (psb_xrandr_info->hdmi_extvideo_prop->ExtDesktopMode == EXTENDEDVIDEO) ? 1 : 0;
    pthread_mutex_unlock(&psb_xrandr_info->psb_extvideo_mutex);
    return ret;
}

int psb_xrandr_outputchanged()
{
    int ret;
    pthread_mutex_lock(&psb_xrandr_info->psb_extvideo_mutex);
    if (psb_xrandr_info->output_changed) {
        psb_xrandr_info->output_changed = 0;
        ret = 1;
    } else
        ret = 0;
    pthread_mutex_unlock(&psb_xrandr_info->psb_extvideo_mutex);
    return ret;
}

VAStatus psb_xrandr_extvideo_prop(unsigned int *xres, unsigned int *yres, unsigned int *xoffset,
                                  unsigned int *yoffset, psb_extvideo_center *center, psb_extvideo_subtitle *subtitle,
                                  unsigned int *overscanmode, unsigned int *pannelfitting)
{
    pthread_mutex_lock(&psb_xrandr_info->psb_extvideo_mutex);

    if (psb_xrandr_info->hdmi_extvideo_prop->ExtDesktopMode != EXTENDEDVIDEO) {
        pthread_mutex_unlock(&psb_xrandr_info->psb_extvideo_mutex);
        return VA_STATUS_ERROR_UNKNOWN;
    }

    *xres = psb_xrandr_info->hdmi_extvideo_prop->ExtVideoMode_XRes;
    *yres = psb_xrandr_info->hdmi_extvideo_prop->ExtVideoMode_YRes;
    *xoffset = psb_xrandr_info->hdmi_extvideo_prop->ExtVideoMode_X_Offset;
    *yoffset = psb_xrandr_info->hdmi_extvideo_prop->ExtVideoMode_Y_Offset;
    *center = psb_xrandr_info->hdmi_extvideo_prop->ExtVideoMode_Center;
    *subtitle = psb_xrandr_info->hdmi_extvideo_prop->ExtVideoMode_SubTitle;
    *pannelfitting = psb_xrandr_info->hdmi_extvideo_prop->PANELFITTING;
    *overscanmode = psb_xrandr_info->hdmi_extvideo_prop->OverscanMode;

    pthread_mutex_unlock(&psb_xrandr_info->psb_extvideo_mutex);
    return VA_STATUS_SUCCESS;
}

VAStatus psb_xrandr_local_crtc_coordinate(psb_output_device *local_device_enabled, int *x, int *y, int *width, int *height, Rotation *rotation)
{
    psb_xrandr_crtc_p p_crtc;
    pthread_mutex_lock(&psb_xrandr_info->psb_extvideo_mutex);

    switch (psb_xrandr_info->output_device_mode) {
    case SINGLE_LVDS0:
        *local_device_enabled = LVDS0;
        p_crtc = psb_xrandr_info->local_crtc[0];
        break;
    case SINGLE_MIPI0:
    case MIPI0_MIPI1:
    case MIPI0_HDMI:
    case MIPI0_MIPI1_HDMI:
        *local_device_enabled = MIPI0;
        p_crtc = psb_xrandr_info->local_crtc[0];
        break;
    case SINGLE_MIPI1:
    case MIPI1_HDMI:
        *local_device_enabled = MIPI1;
        p_crtc = psb_xrandr_info->local_crtc[1];
        break;
    case SINGLE_HDMI:
        *local_device_enabled = HDMI;
        p_crtc = psb_xrandr_info->extend_crtc;
        break;
    default:
        drv_debug_msg(VIDEO_DEBUG_ERROR, "Xrandr: Unknown statue\n");
        pthread_mutex_unlock(&psb_xrandr_info->psb_extvideo_mutex);
        return VA_STATUS_ERROR_UNKNOWN;
        break;
    }

    if (p_crtc) {
        *x = p_crtc->x;
        *y = p_crtc->y;
        *width = p_crtc->width;
        *height = p_crtc->height;
        *rotation = p_crtc->rotation;
        pthread_mutex_unlock(&psb_xrandr_info->psb_extvideo_mutex);
        drv_debug_msg(VIDEO_DEBUG_GENERAL, "Xrandr: device %08x enabled, crtc %08x coordinate: x = %d, y = %d, widht = %d, height = %d, rotate = %08x\n",
                                 *local_device_enabled, p_crtc->crtc_id, *x, *y, *width + 1, *height + 1, *rotation);
        return VA_STATUS_SUCCESS;
    } else {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "Xrandr: local device is not available\n");
        pthread_mutex_unlock(&psb_xrandr_info->psb_extvideo_mutex);
        return VA_STATUS_ERROR_UNKNOWN;
    }
}

VAStatus psb_xrandr_extend_crtc_coordinate(psb_output_device *extend_device_enabled, int *x, int *y, int *width, int *height, psb_xrandr_location *location, Rotation *rotation)
{
    psb_xrandr_crtc_p p_crtc;

    pthread_mutex_lock(&psb_xrandr_info->psb_extvideo_mutex);

    switch (psb_xrandr_info->output_device_mode) {
    case MIPI0_MIPI1:
        *extend_device_enabled = MIPI1;
        p_crtc = psb_xrandr_info->local_crtc[1];
        break;
    case MIPI0_HDMI:
    case MIPI0_MIPI1_HDMI:
    case MIPI1_HDMI:
        *extend_device_enabled = HDMI;
        p_crtc = psb_xrandr_info->extend_crtc;
        break;
    default:
        drv_debug_msg(VIDEO_DEBUG_ERROR, "Xrandr: Unknown status, may be extend device is not available\n");
        pthread_mutex_unlock(&psb_xrandr_info->psb_extvideo_mutex);
        return VA_STATUS_ERROR_UNKNOWN;
        break;
    }

    if (p_crtc) {
        *x = p_crtc->x;
        *y = p_crtc->y;
        *width = p_crtc->width;
        *height = p_crtc->height;
        *location = p_crtc->location;
        *rotation = p_crtc->rotation;
        pthread_mutex_unlock(&psb_xrandr_info->psb_extvideo_mutex);
        drv_debug_msg(VIDEO_DEBUG_GENERAL, "Xrandr: extend device %08x enabled, crtc %08x coordinate: x = %d, y = %d, widht = %d, height = %d, location = %s, rotation = %08x\n",
                                 *extend_device_enabled, p_crtc->crtc_id, *x, *y, *width + 1, *height + 1, location2string(p_crtc->location), *rotation);
    } else {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "Xrandr: extend device is not available\n");
        pthread_mutex_unlock(&psb_xrandr_info->psb_extvideo_mutex);
        return VA_STATUS_ERROR_UNKNOWN;
    }

    return VA_STATUS_SUCCESS;
}

VAStatus psb_xrandr_thread_exit()
{
    int ret;

    XSelectInput(psb_xrandr_info->dpy, psb_xrandr_info->root, StructureNotifyMask);
    XClientMessageEvent xevent;
    xevent.type = ClientMessage;
    xevent.message_type = psb_xrandr_info->psb_exit_atom;
    xevent.window = psb_xrandr_info->root;
    xevent.format = 32;
    ret = XSendEvent(psb_xrandr_info->dpy, psb_xrandr_info->root, 0, StructureNotifyMask, (XEvent*) & xevent);
    XFlush(psb_xrandr_info->dpy);
    if (!ret) {
        drv_debug_msg(VIDEO_DEBUG_GENERAL, "Xrandr: send thread exit event to drawable: failed\n");
        return VA_STATUS_ERROR_UNKNOWN;
    } else {
        drv_debug_msg(VIDEO_DEBUG_GENERAL, "Xrandr: send thread exit event to drawable: success\n");
        return VA_STATUS_SUCCESS;
    }
}

VAStatus psb_xrandr_thread_create(VADriverContextP ctx)
{
    pthread_t id;
    INIT_DRIVER_DATA;

    psb_xrandr_info->psb_exit_atom = XInternAtom(psb_xrandr_info->dpy, "psb_exit_atom", 0);
    pthread_create(&id, NULL, (void*)psb_xrandr_thread, ctx);
    driver_data->xrandr_thread_id = id;
    return VA_STATUS_SUCCESS;
}

VAStatus psb_xrandr_deinit()
{
#ifdef _FOR_FPGA_
    return VA_STATUS_SUCCESS;
#endif
    pthread_mutex_lock(&psb_xrandr_info->psb_extvideo_mutex);
    //free crtc
    if (psb_xrandr_info->crtc_head) {
        while (psb_xrandr_info->crtc_head) {
            psb_xrandr_info->crtc_tail = psb_xrandr_info->crtc_head->next;

            free(psb_xrandr_info->crtc_head);

            psb_xrandr_info->crtc_head = psb_xrandr_info->crtc_tail;
        }
        psb_xrandr_info->crtc_head = psb_xrandr_info->crtc_tail = NULL;
    }

    //free output
    if (psb_xrandr_info->output_head) {
        while (psb_xrandr_info->output_head) {
            psb_xrandr_info->output_tail = psb_xrandr_info->output_head->next;

            free(psb_xrandr_info->output_head);

            psb_xrandr_info->output_head = psb_xrandr_info->output_tail;
        }
        psb_xrandr_info->output_head = psb_xrandr_info->output_tail = NULL;
    }

    if (psb_xrandr_info->hdmi_extvideo_prop) {
        free(psb_xrandr_info->hdmi_extvideo_prop);
    }

    pthread_mutex_unlock(&psb_xrandr_info->psb_extvideo_mutex);
    pthread_mutex_destroy(&psb_xrandr_info->psb_extvideo_mutex);

    free(psb_xrandr_info);
    return VA_STATUS_SUCCESS;
}

VAStatus psb_xrandr_init(VADriverContextP ctx)
{
    int major, minor;
    int screen;

    psb_xrandr_info = (psb_xrandr_info_p)calloc(1, sizeof(psb_xrandr_info_s));

    if (!psb_xrandr_info) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "output of memory\n");
        return VA_STATUS_ERROR_UNKNOWN;
    }
    memset(psb_xrandr_info, 0, sizeof(psb_xrandr_info_s));
    psb_xrandr_info->mipi0_rotation = RR_Rotate_0;
    psb_xrandr_info->mipi1_rotation = RR_Rotate_0;
    psb_xrandr_info->hdmi_rotation = RR_Rotate_0;

    psb_xrandr_info->hdmi_extvideo_prop = (psb_extvideo_prop_p)calloc(1, sizeof(psb_extvideo_prop_s));
    if (!psb_xrandr_info->hdmi_extvideo_prop) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "output of memory\n");
        return VA_STATUS_ERROR_ALLOCATION_FAILED;
    }
    memset(psb_xrandr_info->hdmi_extvideo_prop, 0, sizeof(psb_extvideo_prop_s));

    psb_xrandr_info->dpy = (Display *)ctx->native_dpy;
    screen = DefaultScreen(psb_xrandr_info->dpy);

    if (screen >= ScreenCount(psb_xrandr_info->dpy)) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "Xrandr: Invalid screen number %d (display has %d)\n",
                           screen, ScreenCount(psb_xrandr_info->dpy));
        return VA_STATUS_ERROR_UNKNOWN;
    }

    psb_xrandr_info->root = RootWindow(psb_xrandr_info->dpy, screen);

    if (!XRRQueryVersion(psb_xrandr_info->dpy, &major, &minor)) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "Xrandr: RandR extension missing\n");
        return VA_STATUS_ERROR_UNKNOWN;
    }

    psb_xrandr_info->res = XRRGetScreenResources(psb_xrandr_info->dpy, psb_xrandr_info->root);
    if (!psb_xrandr_info->res)
        drv_debug_msg(VIDEO_DEBUG_ERROR, "Xrandr: failed to get screen resources\n");

    pthread_mutex_init(&psb_xrandr_info->psb_extvideo_mutex, NULL);

    psb_xrandr_refresh(ctx);

    return VA_STATUS_SUCCESS;
}