/* * Copyright © 2007 Red Hat, Inc. * * 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, sublicense, * 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 NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS 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: * Dave Airlie * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef XF86DRM_MODE #include "i830.h" #include "sarea.h" static Bool drmmode_resize_fb(ScrnInfoPtr scrn, drmmode_ptr drmmode, int width, int height); static Bool drmmode_xf86crtc_resize (ScrnInfoPtr scrn, int width, int height) { xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn); drmmode_crtc_private_ptr drmmode_crtc = xf86_config->crtc[0]->driver_private; drmmode_ptr drmmode = drmmode_crtc->drmmode; Bool ret; ret = drmmode_resize_fb(scrn, drmmode, width, height); scrn->virtualX = width; scrn->virtualY = height; return ret; } static void drmmode_ConvertFromKMode(ScrnInfoPtr scrn, struct drm_mode_modeinfo *kmode, DisplayModePtr mode) { memset(mode, 0, sizeof(DisplayModeRec)); mode->status = MODE_OK; mode->Clock = kmode->clock; mode->HDisplay = kmode->hdisplay; mode->HSyncStart = kmode->hsync_start; mode->HSyncEnd = kmode->hsync_end; mode->HTotal = kmode->htotal; mode->HSkew = kmode->hskew; mode->VDisplay = kmode->vdisplay; mode->VSyncStart = kmode->vsync_start; mode->VSyncEnd = kmode->vsync_end; mode->VTotal = kmode->vtotal; mode->VScan = kmode->vscan; mode->Flags = kmode->flags; //& FLAG_BITS; mode->name = strdup(kmode->name); if (kmode->type & DRM_MODE_TYPE_DRIVER) mode->type = M_T_DRIVER; if (kmode->type & DRM_MODE_TYPE_PREFERRED) mode->type |= M_T_PREFERRED; xf86SetModeCrtc (mode, scrn->adjustFlags); } static void drmmode_ConvertToKMode(ScrnInfoPtr scrn, struct drm_mode_modeinfo *kmode, DisplayModePtr mode) { memset(kmode, 0, sizeof(*kmode)); kmode->clock = mode->Clock; kmode->hdisplay = mode->HDisplay; kmode->hsync_start = mode->HSyncStart; kmode->hsync_end = mode->HSyncEnd; kmode->htotal = mode->HTotal; kmode->hskew = mode->HSkew; kmode->vdisplay = mode->VDisplay; kmode->vsync_start = mode->VSyncStart; kmode->vsync_end = mode->VSyncEnd; kmode->vtotal = mode->VTotal; kmode->vscan = mode->VScan; kmode->flags = mode->Flags; //& FLAG_BITS; if (mode->name) strncpy(kmode->name, mode->name, DRM_DISPLAY_MODE_LEN); kmode->name[DRM_DISPLAY_MODE_LEN-1] = 0; } static const xf86CrtcConfigFuncsRec drmmode_xf86crtc_config_funcs = { drmmode_xf86crtc_resize }; static void drmmode_crtc_dpms(xf86CrtcPtr drmmode_crtc, int mode) { } static Bool drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, Rotation rotation, int x, int y) { xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn); drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; drmmode_ptr drmmode = drmmode_crtc->drmmode; int saved_x, saved_y; Rotation saved_rotation; DisplayModeRec saved_mode; uint32_t *output_ids; int output_count = 0; int ret = TRUE; int i; int fb_id; struct drm_mode_modeinfo kmode; saved_mode = crtc->mode; saved_x = crtc->x; saved_y = crtc->y; saved_rotation = crtc->rotation; crtc->mode = *mode; crtc->x = x; crtc->y = y; crtc->rotation = rotation; output_ids = xcalloc(sizeof(uint32_t), xf86_config->num_output); if (!output_ids) { ret = FALSE; goto done; } for (i = 0; i < xf86_config->num_output; i++) { xf86OutputPtr output = xf86_config->output[i]; drmmode_output_private_ptr drmmode_output; if (output->crtc != crtc) continue; drmmode_output = output->driver_private; output_ids[output_count] = drmmode_output->mode_output->connector_id; output_count++; } if (!xf86CrtcRotate(crtc, mode, rotation)) { goto done; } drmmode_ConvertToKMode(crtc->scrn, &kmode, mode); fb_id = drmmode->fb_id; if (drmmode_crtc->rotate_fb_id) fb_id = drmmode_crtc->rotate_fb_id; ErrorF("fb id is %d\n", fb_id); ret = drmModeSetCrtc(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, fb_id, x, y, output_ids, output_count, &kmode); if (ret) xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR, "failed to set mode: %s", strerror(-ret)); else ret = TRUE; done: if (!ret) { crtc->x = saved_x; crtc->y = saved_y; crtc->rotation = saved_rotation; crtc->mode = saved_mode; } return ret; } static void drmmode_set_cursor_colors (xf86CrtcPtr crtc, int bg, int fg) { } static void drmmode_set_cursor_position (xf86CrtcPtr crtc, int x, int y) { drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; drmmode_ptr drmmode = drmmode_crtc->drmmode; drmModeMoveCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, x, y); } static void drmmode_load_cursor_argb (xf86CrtcPtr crtc, CARD32 *image) { drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; ScrnInfoPtr pScrn = crtc->scrn; I830Ptr pI830 = I830PTR(pScrn); int ret; /* cursor should be mapped already */ ret = dri_bo_subdata(pI830->cursor_mem->bo, 0, 64*64*4, image); if (ret) xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR, "failed to set cursor: %s", strerror(-ret)); return; } static void drmmode_hide_cursor (xf86CrtcPtr crtc) { drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; drmmode_ptr drmmode = drmmode_crtc->drmmode; drmModeSetCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, 0, 64, 64); } static void drmmode_show_cursor (xf86CrtcPtr crtc) { drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; drmmode_ptr drmmode = drmmode_crtc->drmmode; ScrnInfoPtr pScrn = crtc->scrn; I830Ptr pI830 = I830PTR(pScrn); drmModeSetCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, pI830->cursor_mem->bo->handle, 64, 64); } static void * drmmode_crtc_shadow_allocate(xf86CrtcPtr crtc, int width, int height) { drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; drmmode_ptr drmmode = drmmode_crtc->drmmode; int size; unsigned long rotate_pitch; rotate_pitch = crtc->scrn->displayWidth * drmmode->cpp; size = rotate_pitch * height; #if 0 drmmode_crtc->rotate_bo = dri_bo_alloc(drmmode->bufmgr, "rotate", size, 4096); if (!drmmode_crtc->rotate_bo) { xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR, "Couldn't allocate shadow memory for rotated CRTC\n"); return NULL; } dri_bo_map(drmmode_crtc->rotate_bo, 1); ret = drmModeAddFB(drmmode->fd, width, height, crtc->scrn->depth, crtc->scrn->bitsPerPixel, rotate_pitch, drmmode_crtc->rotate_bo->handle, &drmmode_crtc->rotate_fb_id); if (ret) ErrorF("failed to add rotate fb\n"); return drmmode_crtc->rotate_bo->virtual; #endif return NULL; } static PixmapPtr drmmode_crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height) { ScrnInfoPtr pScrn = crtc->scrn; drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; drmmode_ptr drmmode = drmmode_crtc->drmmode; unsigned long rotate_pitch; PixmapPtr rotate_pixmap; if (!data) data = drmmode_crtc_shadow_allocate (crtc, width, height); rotate_pitch = pScrn->displayWidth * drmmode->cpp; rotate_pixmap = GetScratchPixmapHeader(pScrn->pScreen, width, height, pScrn->depth, pScrn->bitsPerPixel, rotate_pitch, data); if (rotate_pixmap == NULL) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Couldn't allocate shadow pixmap for rotated CRTC\n"); } return rotate_pixmap; } static void drmmode_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap, void *data) { drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; if (rotate_pixmap) FreeScratchPixmapHeader(rotate_pixmap); if (data) { #if 0 /* Be sure to sync acceleration before the memory gets unbound. */ drmModeRmFB(drmmode->fd, drmmode_crtc->rotate_fb_id); drmmode_crtc->rotate_fb_id = 0; dri_bo_unreference(drmmode_crtc->rotate_bo); drmmode_crtc->rotate_bo = NULL; #endif } } static const xf86CrtcFuncsRec drmmode_crtc_funcs = { .dpms = drmmode_crtc_dpms, .set_mode_major = drmmode_set_mode_major, .set_cursor_colors = drmmode_set_cursor_colors, .set_cursor_position = drmmode_set_cursor_position, .show_cursor = drmmode_show_cursor, .hide_cursor = drmmode_hide_cursor, .load_cursor_argb = drmmode_load_cursor_argb, .shadow_create = drmmode_crtc_shadow_create, .shadow_allocate = drmmode_crtc_shadow_allocate, .shadow_destroy = drmmode_crtc_shadow_destroy, #if 0 .gamma_set = i830_crtc_gamma_set, .shadow_create = i830_crtc_shadow_create, .shadow_allocate = i830_crtc_shadow_allocate, .shadow_destroy = i830_crtc_shadow_destroy, .set_cursor_colors = i830_crtc_set_cursor_colors, #endif .destroy = NULL, /* XXX */ }; static void drmmode_crtc_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int num) { xf86CrtcPtr crtc; drmmode_crtc_private_ptr drmmode_crtc; I830Ptr pI830 = I830PTR(pScrn); int ret; crtc = xf86CrtcCreate(pScrn, &drmmode_crtc_funcs); if (crtc == NULL) return; drmmode_crtc = xnfcalloc(sizeof(drmmode_crtc_private_rec), 1); drmmode_crtc->mode_crtc = drmModeGetCrtc(drmmode->fd, drmmode->mode_res->crtcs[num]); drmmode_crtc->drmmode = drmmode; crtc->driver_private = drmmode_crtc; return; } static xf86OutputStatus drmmode_output_detect(xf86OutputPtr output) { /* go to the hw and retrieve a new output struct */ drmmode_output_private_ptr drmmode_output = output->driver_private; drmmode_ptr drmmode = drmmode_output->drmmode; xf86OutputStatus status; drmModeFreeConnector(drmmode_output->mode_output); drmmode_output->mode_output = drmModeGetConnector(drmmode->fd, drmmode_output->output_id); switch (drmmode_output->mode_output->connection) { case DRM_MODE_CONNECTED: status = XF86OutputStatusConnected; break; case DRM_MODE_DISCONNECTED: status = XF86OutputStatusDisconnected; break; default: case DRM_MODE_UNKNOWNCONNECTION: status = XF86OutputStatusUnknown; break; } return status; } static Bool drmmode_output_mode_valid(xf86OutputPtr output, DisplayModePtr pModes) { return MODE_OK; } static DisplayModePtr drmmode_output_get_modes(xf86OutputPtr output) { drmmode_output_private_ptr drmmode_output = output->driver_private; drmModeConnectorPtr koutput = drmmode_output->mode_output; drmmode_ptr drmmode = drmmode_output->drmmode; int i; DisplayModePtr Modes = NULL, Mode; drmModePropertyPtr props; /* look for an EDID property */ for (i = 0; i < koutput->count_props; i++) { props = drmModeGetProperty(drmmode->fd, koutput->props[i]); if (!props || !(props->flags & DRM_MODE_PROP_BLOB)) continue; if (!strcmp(props->name, "EDID") && drmmode_output->edid_blob) { drmModeFreePropertyBlob(drmmode_output->edid_blob); drmmode_output->edid_blob = drmModeGetPropertyBlob(drmmode->fd, koutput->prop_values[i]); } drmModeFreeProperty(props); } if (drmmode_output->edid_blob) xf86OutputSetEDID(output, xf86InterpretEDID(output->scrn->scrnIndex, drmmode_output->edid_blob->data)); else xf86OutputSetEDID(output, xf86InterpretEDID(output->scrn->scrnIndex, NULL)); /* modes should already be available */ for (i = 0; i < koutput->count_modes; i++) { Mode = xnfalloc(sizeof(DisplayModeRec)); drmmode_ConvertFromKMode(output->scrn, &koutput->modes[i], Mode); Modes = xf86ModesAdd(Modes, Mode); } return Modes; } static void drmmode_output_destroy(xf86OutputPtr output) { drmmode_output_private_ptr drmmode_output = output->driver_private; if (drmmode_output->edid_blob) drmModeFreePropertyBlob(drmmode_output->edid_blob); drmModeFreeConnector(drmmode_output->mode_output); xfree(drmmode_output); output->driver_private = NULL; } static void drmmode_output_dpms(xf86OutputPtr output, int mode) { return; } static const xf86OutputFuncsRec drmmode_output_funcs = { .dpms = drmmode_output_dpms, #if 0 .save = drmmode_crt_save, .restore = drmmode_crt_restore, .mode_fixup = drmmode_crt_mode_fixup, .prepare = drmmode_output_prepare, .mode_set = drmmode_crt_mode_set, .commit = drmmode_output_commit, #endif .detect = drmmode_output_detect, .mode_valid = drmmode_output_mode_valid, .get_modes = drmmode_output_get_modes, .destroy = drmmode_output_destroy }; static int subpixel_conv_table[7] = { 0, SubPixelUnknown, SubPixelHorizontalRGB, SubPixelHorizontalBGR, SubPixelVerticalRGB, SubPixelVerticalBGR, SubPixelNone }; const char *output_names[] = { "None", "VGA", "DVI", "DVI", "DVI", "Composite" "TV", "LVDS", "CTV", "DIN", "DP", "HDMI", "HDMI", }; static void drmmode_output_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int num) { xf86OutputPtr output; drmModeConnectorPtr koutput; drmModeEncoderPtr kencoder; drmmode_output_private_ptr drmmode_output; char name[32]; koutput = drmModeGetConnector(drmmode->fd, drmmode->mode_res->connectors[num]); if (!koutput) return; kencoder = drmModeGetEncoder(drmmode->fd, koutput->encoders[0]); if (!kencoder) { drmModeFreeConnector(koutput); return; } snprintf(name, 32, "%s%d", output_names[koutput->connector_type], koutput->connector_type_id); output = xf86OutputCreate (pScrn, &drmmode_output_funcs, name); if (!output) { drmModeFreeEncoder(kencoder); drmModeFreeConnector(koutput); return; } drmmode_output = xcalloc(sizeof(drmmode_output_private_rec), 1); if (!drmmode_output) { xf86OutputDestroy(output); drmModeFreeConnector(koutput); drmModeFreeEncoder(kencoder); return; } drmmode_output->output_id = drmmode->mode_res->connectors[num]; drmmode_output->mode_output = koutput; drmmode_output->mode_encoder = kencoder; drmmode_output->drmmode = drmmode; output->mm_width = koutput->mmWidth; output->mm_height = koutput->mmHeight; output->subpixel_order = subpixel_conv_table[koutput->subpixel]; output->driver_private = drmmode_output; output->possible_crtcs = kencoder->possible_crtcs; output->possible_clones = kencoder->possible_clones; return; } Bool drmmode_pre_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, char *busId, char *driver_name, int cpp) { xf86CrtcConfigPtr xf86_config; int i; Bool ret; /* Create a bus Id */ /* Low level DRM open */ ret = DRIOpenDRMMaster(pScrn, SAREA_MAX, busId, driver_name); if (!ret) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "[dri] DRIGetVersion failed to open the DRM\n" "[dri] Disabling DRI.\n"); return FALSE; } drmmode->fd = DRIMasterFD(pScrn); xf86CrtcConfigInit(pScrn, &drmmode_xf86crtc_config_funcs); xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); drmmode->cpp = cpp; drmmode->mode_res = drmModeGetResources(drmmode->fd); if (!drmmode->mode_res) return FALSE; xf86CrtcSetSizeRange(pScrn, 320, 200, drmmode->mode_res->max_width, drmmode->mode_res->max_height); for (i = 0; i < drmmode->mode_res->count_crtcs; i++) drmmode_crtc_init(pScrn, drmmode, i); for (i = 0; i < drmmode->mode_res->count_connectors; i++) drmmode_output_init(pScrn, drmmode, i); xf86InitialConfiguration(pScrn, FALSE); return TRUE; } #if 0 Bool drmmode_set_bufmgr(ScrnInfoPtr pScrn, drmmode_ptr drmmode, dri_bufmgr *bufmgr) { drmmode->bufmgr = bufmgr; return TRUE; } #endif void drmmode_set_fb(ScrnInfoPtr scrn, drmmode_ptr drmmode, int width, int height, int pitch, dri_bo *bo) { int ret; ret = drmModeAddFB(drmmode->fd, width, height, scrn->depth, scrn->bitsPerPixel, pitch, bo->handle, &drmmode->fb_id); if (ret) { ErrorF("Failed to add fb: %s\n", strerror(-ret)); } drmmode->mode_fb = drmModeGetFB(drmmode->fd, drmmode->fb_id); if (!drmmode->mode_fb) return; ErrorF("Add fb id %d %d %d\n", drmmode->fb_id, width, height); } Bool drmmode_is_rotate_pixmap(ScrnInfoPtr pScrn, pointer pPixData, dri_bo **bo) { xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR (pScrn); int i; return FALSE; #if 0 for (i = 0; i < config->num_crtc; i++) { xf86CrtcPtr crtc = config->crtc[i]; drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; if (!drmmode_crtc->rotate_bo) continue; if (drmmode_crtc->rotate_bo->virtual == pPixData) { *bo = drmmode_crtc->rotate_bo; return TRUE; } } return FALSE; #endif } static Bool drmmode_resize_fb(ScrnInfoPtr scrn, drmmode_ptr drmmode, int width, int height) { uint32_t handle; int pitch; int ret; return FALSE; if (drmmode->mode_fb->width == width && drmmode->mode_fb->height == height) return TRUE; if (!drmmode->create_new_fb) return FALSE; handle = drmmode->create_new_fb(scrn, width, height, &pitch); if (handle == 0) return FALSE; ret = drmModeReplaceFB(drmmode->fd, drmmode->fb_id, width, height, scrn->depth, scrn->bitsPerPixel, pitch, handle); if (ret) return FALSE; drmModeFreeFB(drmmode->mode_fb); drmmode->mode_fb = drmModeGetFB(drmmode->fd, drmmode->fb_id); if (!drmmode->mode_fb) return FALSE; return TRUE; } #endif