summaryrefslogtreecommitdiffstats
path: root/src/armada_drm.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/armada_drm.c')
-rw-r--r--src/armada_drm.c1529
1 files changed, 1529 insertions, 0 deletions
diff --git a/src/armada_drm.c b/src/armada_drm.c
new file mode 100644
index 0000000..2ba77ac
--- /dev/null
+++ b/src/armada_drm.c
@@ -0,0 +1,1529 @@
+/*
+ * Marvell Armada DRM-based driver
+ *
+ * Written by Russell King, 2012, derived in part from the
+ * Intel xorg X server driver.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <armada_bufmgr.h>
+
+#include "armada_drm.h"
+#include "xf86_OSproc.h"
+#include "xf86Crtc.h"
+#include "xf86cmap.h"
+#include "fb.h"
+#include "mibstore.h"
+#include "micmap.h"
+#include <xf86DDC.h>
+#include <X11/extensions/dpmsconst.h>
+#include <X11/Xatom.h>
+
+#ifdef HAVE_UDEV
+#include <libudev.h>
+#endif
+
+#include "vivante.h"
+#include "vivante_dri2.h"
+
+#define CURSOR_MAX_WIDTH 64
+#define CURSOR_MAX_HEIGHT 32
+
+#define DRM_MODULE_NAME "armada-drm"
+#define DRM_DEFAULT_BUS_ID "platform:armada-drm:00"
+
+const OptionInfoRec armada_drm_options[] = {
+ { OPTION_HW_CURSOR, "HWcursor", OPTV_BOOLEAN, {0}, FALSE },
+ { OPTION_XV_ACCEL, "XvAccel", OPTV_BOOLEAN, {0}, FALSE },
+ { OPTION_USE_GPU, "UseGPU", OPTV_BOOLEAN, {0}, FALSE },
+ { OPTION_HOTPLUG, "HotPlug", OPTV_BOOLEAN, {0}, TRUE },
+ { -1, NULL, OPTV_NONE, {0}, FALSE }
+};
+
+
+struct armada_property {
+ drmModePropertyPtr mode_prop;
+ uint64_t value;
+ int natoms;
+ Atom *atoms;
+};
+
+struct armada_conn_info {
+ struct armada_drm_info *drm;
+ drmModeConnectorPtr mode_output;
+ drmModeEncoderPtr mode_encoder;
+ int id;
+ int dpms_mode;
+ int nprops;
+ struct armada_property *props;
+};
+
+static void armada_drm_ModifyScreenPixmap(ScreenPtr pScreen,
+ struct armada_drm_info *drm, int width, int height, int depth, int bpp,
+ struct drm_armada_bo *bo)
+{
+ PixmapPtr pixmap = pScreen->GetScreenPixmap(pScreen);
+
+ pScreen->ModifyPixmapHeader(pixmap, width, height, depth, bpp,
+ bo->pitch, bo->ptr);
+ if (drm->accel)
+ vivante_set_pixmap_bo(pixmap, bo);
+}
+
+static void drmmode_ConvertToKMode(drmModeModeInfoPtr 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;
+ if (mode->name)
+ strncpy(kmode->name, mode->name, DRM_DISPLAY_MODE_LEN);
+ kmode->name[DRM_DISPLAY_MODE_LEN-1] = 0;
+}
+
+static void drmmode_ConvertFromKMode(ScrnInfoPtr pScrn,
+ drmModeModeInfoPtr kmode, DisplayModePtr mode)
+{
+ memset(mode, 0, sizeof(*mode));
+
+ 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;
+ 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, pScrn->adjustFlags);
+}
+
+static struct drm_armada_bo *armada_bo_alloc_framebuffer(ScrnInfoPtr pScrn,
+ struct armada_drm_info *drm, int width, int height, int bpp)
+{
+ struct drm_armada_bo *bo;
+ int ret;
+
+ bo = drm_armada_bo_dumb_create(drm->bufmgr, width, height, bpp);
+ if (!bo) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] failed to allocate new bo: %s\n",
+ strerror(errno));
+ return NULL;
+ }
+
+ ret = drm_armada_bo_map(bo);
+ if (ret) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] failed to map fb bo: %s\n", strerror(errno));
+ drm_armada_bo_put(bo);
+ return NULL;
+ }
+
+// xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+// "Allocated new frame buffer %dx%d stride %d\n",
+// width, height, bo->pitch);
+
+ return bo;
+}
+
+static drmModePropertyPtr armada_drm_conn_find_property(
+ struct armada_conn_info *conn, const char *name, uint32_t *blob)
+{
+ drmModeConnectorPtr koutput = conn->mode_output;
+ struct armada_drm_info *drm = conn->drm;
+ int i;
+
+ for (i = 0; i < koutput->count_props; i++) {
+ drmModePropertyPtr p;
+
+ p = drmModeGetProperty(drm->fd, koutput->props[i]);
+ if (!p || (blob && !(p->flags & DRM_MODE_PROP_BLOB)))
+ continue;
+
+ if (!strcmp(p->name, name)) {
+ if (blob)
+ *blob = koutput->prop_values[i];
+ return p;
+ }
+
+ drmModeFreeProperty(p);
+ }
+ return NULL;
+}
+
+static void armada_drm_conn_create_resources(xf86OutputPtr output)
+{
+ struct armada_conn_info *conn = output->driver_private;
+ struct armada_drm_info *drm = conn->drm;
+ drmModeConnectorPtr mop = conn->mode_output;
+ int i, j, n, err;
+
+ conn->props = calloc(mop->count_props, sizeof *conn->props);
+ if (!conn->props)
+ return;
+
+ for (i = n = 0; i < mop->count_props; i++) {
+ struct armada_property *prop = &conn->props[n];
+ drmModePropertyPtr dprop;
+ Bool immutable;
+
+ dprop = drmModeGetProperty(drm->fd, mop->props[i]);
+ if (!dprop || dprop->flags & DRM_MODE_PROP_BLOB ||
+ !strcmp(dprop->name, "DPMS") ||
+ !strcmp(dprop->name, "EDID")) {
+ drmModeFreeProperty(dprop);
+ continue;
+ }
+
+ n++;
+ prop->mode_prop = dprop;
+ prop->value = mop->prop_values[i];
+
+ immutable = dprop->flags & DRM_MODE_PROP_IMMUTABLE ?
+ TRUE : FALSE;
+
+ if (dprop->flags & DRM_MODE_PROP_RANGE) {
+ INT32 range[2];
+ uint32_t value = prop->value;
+
+ prop->natoms = 1;
+ prop->atoms = calloc(prop->natoms, sizeof *prop->atoms);
+ if (!prop->atoms)
+ continue;
+
+ range[0] = dprop->values[0];
+ range[1] = dprop->values[1];
+
+ prop->atoms[0] = MakeAtom(dprop->name,
+ strlen(dprop->name), TRUE);
+ err = RRConfigureOutputProperty(output->randr_output,
+ prop->atoms[0], FALSE,
+ TRUE, immutable, 2,
+ range);
+ if (err)
+ xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
+ "RRConfigureOutputProperty error %d\n",
+ err);
+
+ err = RRChangeOutputProperty(output->randr_output,
+ prop->atoms[0],
+ XA_INTEGER, 32,
+ PropModeReplace, 1,
+ &value, FALSE, TRUE);
+ if (err)
+ xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
+ "RRChangeOutputProperty error %d\n",
+ err);
+ } else if (dprop->flags & DRM_MODE_PROP_ENUM) {
+ int current;
+
+ prop->natoms = dprop->count_enums + 1;
+ prop->atoms = calloc(prop->natoms, sizeof *prop->atoms);
+ if (!prop->atoms)
+ continue;
+
+ current = prop->natoms;
+ prop->atoms[0] = MakeAtom(dprop->name,
+ strlen(dprop->name), TRUE);
+ for (j = 1; j < prop->natoms; j++) {
+ struct drm_mode_property_enum *e;
+
+ e = &dprop->enums[j - 1];
+ prop->atoms[j] = MakeAtom(e->name,
+ strlen(e->name),
+ TRUE);
+ if (prop->value == e->value)
+ current = j;
+ }
+
+ err = RRConfigureOutputProperty(output->randr_output,
+ prop->atoms[0], FALSE, FALSE,
+ immutable, prop->natoms - 1,
+ (INT32 *)&prop->atoms[1]);
+ if (err)
+ xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
+ "RRConfigureOutputProperty error, %d\n",
+ err);
+
+ err = RRChangeOutputProperty(output->randr_output,
+ prop->atoms[0], XA_ATOM,
+ 32, PropModeReplace, 1,
+ &prop->atoms[current],
+ FALSE, TRUE);
+ if (err)
+ xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
+ "RRChangeOutputProperty error, %d\n",
+ err);
+ }
+ }
+ conn->nprops = n;
+}
+
+static void armada_drm_conn_dpms(xf86OutputPtr output, int mode)
+{
+ struct armada_conn_info *conn = output->driver_private;
+ struct armada_drm_info *drm = conn->drm;
+ drmModePropertyPtr p = armada_drm_conn_find_property(conn, "DPMS", NULL);
+
+ if (p) {
+ drmModeConnectorSetProperty(drm->fd, conn->id, p->prop_id,
+ mode);
+ conn->dpms_mode = mode;
+ drmModeFreeProperty(p);
+ }
+}
+
+static xf86OutputStatus armada_drm_conn_detect(xf86OutputPtr output)
+{
+ struct armada_conn_info *conn = output->driver_private;
+ struct armada_drm_info *drm = conn->drm;
+ xf86OutputStatus status = XF86OutputStatusUnknown;
+ drmModeConnectorPtr koutput;
+
+ koutput = drmModeGetConnector(drm->fd, conn->id);
+ if (!koutput)
+ return XF86OutputStatusUnknown;
+
+ drmModeFreeConnector(conn->mode_output);
+ conn->mode_output = koutput;
+
+ switch (koutput->connection) {
+ case DRM_MODE_CONNECTED:
+ status = XF86OutputStatusConnected;
+ break;
+ case DRM_MODE_DISCONNECTED:
+ status = XF86OutputStatusDisconnected;
+ break;
+ case DRM_MODE_UNKNOWNCONNECTION:
+ break;
+ }
+ return status;
+}
+
+static Bool
+armada_drm_conn_mode_valid(xf86OutputPtr output, DisplayModePtr pModes)
+{
+ return MODE_OK;
+}
+
+static DisplayModePtr armada_drm_conn_get_modes(xf86OutputPtr output)
+{
+ ScrnInfoPtr pScrn = output->scrn;
+ DisplayModePtr modes = NULL;
+ struct armada_conn_info *conn = output->driver_private;
+ struct armada_drm_info *drm = conn->drm;
+ uint32_t blob;
+ drmModePropertyPtr p;
+ drmModePropertyBlobPtr edid = NULL;
+ xf86MonPtr mon;
+ int i;
+
+ p = armada_drm_conn_find_property(conn, "EDID", &blob);
+ if (p) {
+ edid = drmModeGetPropertyBlob(drm->fd, blob);
+ drmModeFreeProperty(p);
+ }
+
+ mon = xf86InterpretEDID(pScrn->scrnIndex, edid ? edid->data : NULL);
+ if (mon && edid->length > 128)
+ mon->flags |= MONITOR_EDID_COMPLETE_RAWDATA;
+ xf86OutputSetEDID(output, mon);
+
+ /* modes should already be available */
+ for (i = 0; i < conn->mode_output->count_modes; i++) {
+ DisplayModePtr mode = xnfalloc(sizeof *mode);
+
+ drmmode_ConvertFromKMode(pScrn, &conn->mode_output->modes[i], mode);
+ modes = xf86ModesAdd(modes, mode);
+ }
+
+ return modes;
+}
+
+#ifdef RANDR_12_INTERFACE
+static Bool armada_drm_conn_set_property(xf86OutputPtr output, Atom property,
+ RRPropertyValuePtr value)
+{
+ struct armada_conn_info *conn = output->driver_private;
+ struct armada_drm_info *drm = conn->drm;
+ int i;
+
+ for (i = 0; i < conn->nprops; i++) {
+ struct armada_property *prop = &conn->props[i];
+ drmModePropertyPtr dprop;
+
+ if (prop->atoms[0] != property)
+ continue;
+
+ dprop = prop->mode_prop;
+ if (dprop->flags & DRM_MODE_PROP_RANGE) {
+ if (value->type != XA_INTEGER || value->format != 32 || value->size != 1)
+ return FALSE;
+
+ drmModeConnectorSetProperty(drm->fd, conn->id,
+ dprop->prop_id, (uint64_t)*(uint32_t *)value->data);
+
+ return TRUE;
+ } else if (dprop->flags & DRM_MODE_PROP_ENUM) {
+ Atom atom;
+ const char *name;
+ int j;
+
+ if (value->type != XA_ATOM || value->format != 32 || value->size != 1)
+ return FALSE;
+
+ memcpy(&atom, value->data, sizeof(atom));
+ name = NameForAtom(atom);
+ if (name == NULL)
+ return FALSE;
+
+ for (j = 0; j < dprop->count_enums; j++) {
+ if (!strcmp(dprop->enums[j].name, name)) {
+ drmModeConnectorSetProperty(drm->fd, conn->id,
+ dprop->prop_id, dprop->enums[j].value);
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+#endif
+#ifdef RANDR_13_INTERFACE
+static Bool armada_drm_conn_get_property(xf86OutputPtr output, Atom property)
+{
+ return FALSE;
+}
+#endif
+
+static void armada_drm_conn_destroy(xf86OutputPtr output)
+{
+ struct armada_conn_info *conn = output->driver_private;
+
+ drmModeFreeConnector(conn->mode_output);
+ drmModeFreeEncoder(conn->mode_encoder);
+ free(conn);
+
+ output->driver_private = NULL;
+}
+
+static const xf86OutputFuncsRec drm_output_funcs = {
+ .create_resources = armada_drm_conn_create_resources,
+ .dpms = armada_drm_conn_dpms,
+ .detect = armada_drm_conn_detect,
+ .mode_valid = armada_drm_conn_mode_valid,
+ .get_modes = armada_drm_conn_get_modes,
+#ifdef RANDR_12_INTERFACE
+ .set_property = armada_drm_conn_set_property,
+#endif
+#ifdef RANDR_13_INTERFACE
+ .get_property = armada_drm_conn_get_property,
+#endif
+ .destroy = armada_drm_conn_destroy,
+};
+
+static const char *const output_names[] = {
+ "None", "VGA", "DVI", "DVI", "DVI", "Composite", "TV",
+ "LVDS", "CTV", "DIN", "DP", "HDMI", "HDMI",
+};
+
+static const int subpixel_conv_table[] = {
+ 0, SubPixelUnknown, SubPixelHorizontalRGB, SubPixelHorizontalBGR,
+ SubPixelVerticalRGB, SubPixelVerticalBGR, SubPixelNone
+};
+
+static void
+armada_drm_conn_init(ScrnInfoPtr pScrn, struct armada_drm_info *drm,
+ uint32_t id)
+{
+ drmModeConnectorPtr koutput;
+ drmModeEncoderPtr kencoder;
+ xf86OutputPtr output;
+ struct armada_conn_info *conn;
+ char name[32];
+
+ koutput = drmModeGetConnector(drm->fd, id);
+ if (!koutput)
+ return;
+
+ kencoder = drmModeGetEncoder(drm->fd, koutput->encoders[0]);
+ if (!kencoder) {
+ drmModeFreeConnector(koutput);
+ return;
+ }
+
+ snprintf(name, sizeof(name), "%s%d",
+ output_names[koutput->connector_type],
+ koutput->connector_type_id);
+
+ output = xf86OutputCreate(pScrn, &drm_output_funcs, name);
+ if (!output) {
+ drmModeFreeEncoder(kencoder);
+ drmModeFreeConnector(koutput);
+ return;
+ }
+
+ conn = calloc(1, sizeof *conn);
+ if (!conn) {
+ xf86OutputDestroy(output);
+ drmModeFreeEncoder(kencoder);
+ drmModeFreeConnector(koutput);
+ return;
+ }
+
+ conn->drm = drm;
+ conn->id = id;
+ conn->mode_output = koutput;
+ conn->mode_encoder = kencoder;
+
+ output->driver_private = conn;
+ output->mm_width = koutput->mmWidth;
+ output->mm_height = koutput->mmHeight;
+ output->subpixel_order = subpixel_conv_table[koutput->subpixel];
+ output->possible_crtcs = kencoder->possible_crtcs;
+ output->possible_clones = kencoder->possible_clones;
+ output->interlaceAllowed = 1; /* wish there was a way to read that */
+ output->doubleScanAllowed = 0;
+}
+
+/*
+ * CRTC support
+ */
+static Bool armada_drm_crtc_apply(xf86CrtcPtr crtc, drmModeModeInfoPtr kmode)
+{
+ ScrnInfoPtr pScrn = crtc->scrn;
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
+ struct armada_crtc_info *drmc = crtc->driver_private;
+ struct armada_drm_info *drm = drmc->drm;
+ uint32_t fb_id, *output_ids;
+ int x, y, i, ret, output_num;
+
+ if (!kmode)
+ kmode = &drmc->kmode;
+
+ output_ids = calloc(xf86_config->num_output, sizeof *output_ids);
+ if (!output_ids)
+ return FALSE;
+
+ for (output_num = i = 0; i < xf86_config->num_output; i++) {
+ xf86OutputPtr output = xf86_config->output[i];
+ struct armada_conn_info *conn;
+
+ if (output->crtc == crtc) {
+ conn = output->driver_private;
+ output_ids[output_num++] = conn->mode_output->connector_id;
+ }
+ }
+
+ if (!xf86CrtcRotate(crtc)) {
+ ret = FALSE;
+ goto done;
+ }
+
+ crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green,
+ crtc->gamma_blue, crtc->gamma_size);
+
+ if (drmc->rotate_fb_id) {
+ fb_id = drmc->rotate_fb_id;
+ x = y = 0;
+ } else {
+ fb_id = drm->fb_id;
+ x = crtc->x;
+ y = crtc->y;
+ }
+
+ ret = drmModeSetCrtc(drm->fd, drmc->mode_crtc->crtc_id, fb_id, x, y,
+ output_ids, output_num, kmode);
+ if (ret) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] failed to set mode on crtc %u: %s\n",
+ drmc->mode_crtc->crtc_id, strerror(errno));
+ ret = FALSE;
+ } else {
+ ret = TRUE;
+
+ for (i = 0; i < xf86_config->num_output; i++) {
+ xf86OutputPtr output = xf86_config->output[i];
+
+ if (output->funcs != &drm_output_funcs ||
+ output->crtc != crtc)
+ continue;
+
+ armada_drm_conn_dpms(output, DPMSModeOn);
+ }
+ }
+
+ /* Work around stricter checks in X */
+ if (pScrn->pScreen && drm->hw_cursor)
+ xf86_reload_cursors(pScrn->pScreen);
+
+done:
+ free(output_ids);
+ return ret;
+}
+
+static void armada_drm_crtc_dpms(xf86CrtcPtr crtc, int mode)
+{
+}
+
+static Bool
+armada_drm_crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
+ Rotation rotation, int x, int y)
+{
+ ScrnInfoPtr pScrn = crtc->scrn;
+ struct armada_crtc_info *drmc = crtc->driver_private;
+ struct armada_drm_info *drm = drmc->drm;
+ drmModeModeInfo kmode;
+ DisplayModeRec saved_mode;
+ Rotation saved_rotation;
+ int ret, saved_x, saved_y;
+
+ if (drm->fb_id == 0) {
+ ret = drmModeAddFB(drm->fd, pScrn->virtualX, pScrn->virtualY,
+ pScrn->depth, pScrn->bitsPerPixel,
+ drm->front_bo->pitch, drm->front_bo->handle,
+ &drm->fb_id);
+ if (ret < 0) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] failed to add fb: %s\n",
+ strerror(errno));
+ return FALSE;
+ }
+ }
+
+ 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;
+
+ drmmode_ConvertToKMode(&kmode, mode);
+
+ ret = armada_drm_crtc_apply(crtc, &kmode);
+ if (!ret) {
+ crtc->mode = saved_mode;
+ crtc->x = saved_x;
+ crtc->y = saved_y;
+ crtc->rotation = saved_rotation;
+ } else
+ drmc->kmode = kmode;
+ return ret;
+}
+
+static void armada_drm_crtc_gamma_set(xf86CrtcPtr crtc, CARD16 *red,
+ CARD16 *green, CARD16 *blue, int size)
+{
+ struct armada_crtc_info *drmc = crtc->driver_private;
+ struct armada_drm_info *drm = drmc->drm;
+
+ drmModeCrtcSetGamma(drm->fd, drmc->mode_crtc->crtc_id,
+ size, red, green, blue);
+}
+
+static void armada_drm_crtc_set_cursor_position(xf86CrtcPtr crtc, int x, int y)
+{
+ struct armada_crtc_info *drmc = crtc->driver_private;
+ struct armada_drm_info *drm = drmc->drm;
+
+ drmModeMoveCursor(drm->fd, drmc->mode_crtc->crtc_id, x, y);
+}
+
+static void armada_drm_crtc_show_cursor(xf86CrtcPtr crtc)
+{
+ struct armada_crtc_info *drmc = crtc->driver_private;
+ struct armada_drm_info *drm = drmc->drm;
+
+ drmModeSetCursor(drm->fd, drmc->mode_crtc->crtc_id,
+ drmc->cursor_bo->handle,
+ CURSOR_MAX_WIDTH, CURSOR_MAX_HEIGHT);
+}
+
+static void armada_drm_crtc_hide_cursor(xf86CrtcPtr crtc)
+{
+ struct armada_crtc_info *drmc = crtc->driver_private;
+ struct armada_drm_info *drm = drmc->drm;
+
+ drmModeSetCursor(drm->fd, drmc->mode_crtc->crtc_id, 0, 0, 0);
+}
+
+static void armada_drm_crtc_load_cursor_argb(xf86CrtcPtr crtc, CARD32 *image)
+{
+ struct armada_crtc_info *drmc = crtc->driver_private;
+
+ drm_armada_bo_subdata(drmc->cursor_bo, 0,
+ CURSOR_MAX_WIDTH * CURSOR_MAX_HEIGHT * 4, image);
+}
+
+static void *
+armada_drm_crtc_shadow_allocate(xf86CrtcPtr crtc, int width, int height)
+{
+ struct armada_crtc_info *drmc = crtc->driver_private;
+ ScrnInfoPtr pScrn = crtc->scrn;
+ struct drm_armada_bo *bo;
+ int ret;
+
+ bo = armada_bo_alloc_framebuffer(pScrn, drmc->drm, width, height,
+ pScrn->bitsPerPixel);
+ if (!bo) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Failed to allocate shadow memory for rotated CRTC\n");
+ return NULL;
+ }
+
+ ret = drmModeAddFB(drmc->drm->fd, width, height, pScrn->depth,
+ pScrn->bitsPerPixel, bo->pitch, bo->handle,
+ &drmc->rotate_fb_id);
+ if (ret) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Failed to add rotate fb: %s\n",
+ strerror(errno));
+ drm_armada_bo_put(bo);
+ return NULL;
+ }
+
+ return bo;
+}
+
+static PixmapPtr
+armada_drm_crtc_shadow_create(xf86CrtcPtr crtc, void *data,
+ int width, int height)
+{
+ struct armada_crtc_info *drmc = crtc->driver_private;
+ ScrnInfoPtr scrn = crtc->scrn;
+ PixmapPtr rotate_pixmap;
+ struct drm_armada_bo *bo;
+
+ if (!data)
+ data = armada_drm_crtc_shadow_allocate(crtc, width, height);
+ if (!data) {
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+ "Failed to allocate shadow pixmap data for rotated CRTC\n");
+ return NULL;
+ }
+
+ bo = data;
+
+ rotate_pixmap = GetScratchPixmapHeader(scrn->pScreen, width, height,
+ scrn->depth, scrn->bitsPerPixel,
+ bo->pitch, bo->ptr);
+ if (!rotate_pixmap) {
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+ "Failed to allocate shadow pixmap for rotated CRTC\n");
+ return NULL;
+ }
+
+ if (drmc->drm->accel)
+ vivante_set_pixmap_bo(rotate_pixmap, bo);
+
+ return rotate_pixmap;
+}
+
+static void
+armada_drm_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rot_pixmap,
+ void *data)
+{
+ if (rot_pixmap) {
+ vivante_free_pixmap(rot_pixmap);
+ FreeScratchPixmapHeader(rot_pixmap);
+ }
+ if (data) {
+ struct armada_crtc_info *drmc = crtc->driver_private;
+ struct armada_drm_info *drm = drmc->drm;
+
+ drmModeRmFB(drm->fd, drmc->rotate_fb_id);
+ drmc->rotate_fb_id = 0;
+
+ drm_armada_bo_put(data);
+ }
+}
+
+static void armada_drm_crtc_destroy(xf86CrtcPtr crtc)
+{
+ struct armada_crtc_info *drmc = crtc->driver_private;
+ struct armada_drm_info *drm = drmc->drm;
+
+ if (drmc->cursor_bo) {
+ drmModeSetCursor(drm->fd, drmc->mode_crtc->crtc_id, 0, 0, 0);
+ drm_armada_bo_put(drmc->cursor_bo);
+ }
+ drmModeFreeCrtc(drmc->mode_crtc);
+
+ free(drmc);
+}
+
+static const xf86CrtcFuncsRec drm_crtc_funcs = {
+ .dpms = armada_drm_crtc_dpms,
+ .gamma_set = armada_drm_crtc_gamma_set,
+ .set_mode_major = armada_drm_crtc_set_mode_major,
+ .set_cursor_position = armada_drm_crtc_set_cursor_position,
+ .show_cursor = armada_drm_crtc_show_cursor,
+ .hide_cursor = armada_drm_crtc_hide_cursor,
+ .load_cursor_argb = armada_drm_crtc_load_cursor_argb,
+ .shadow_create = armada_drm_crtc_shadow_create,
+ .shadow_allocate = armada_drm_crtc_shadow_allocate,
+ .shadow_destroy = armada_drm_crtc_shadow_destroy,
+ .destroy = armada_drm_crtc_destroy,
+};
+
+static Bool
+armada_drm_crtc_init(ScrnInfoPtr pScrn, struct armada_drm_info *drm,
+ uint32_t id)
+{
+ xf86CrtcPtr crtc;
+ struct armada_crtc_info *drmc;
+
+ crtc = xf86CrtcCreate(pScrn, &drm_crtc_funcs);
+ if (!crtc)
+ return FALSE;
+
+ drmc = xnfcalloc(1, sizeof *drmc);
+ if (!drmc)
+ return FALSE;
+
+ drmc->drm = drm;
+ drmc->mode_crtc = drmModeGetCrtc(drm->fd, id);
+ crtc->driver_private = drmc;
+
+ drmc->cursor_bo = drm_armada_bo_dumb_create(drm->bufmgr,
+ CURSOR_MAX_WIDTH,
+ CURSOR_MAX_HEIGHT,
+ 32);
+
+ return TRUE;
+}
+
+static Bool armada_drm_xf86crtc_resize(ScrnInfoPtr pScrn, int width, int height)
+{
+ ScreenPtr screen = screenInfo.screens[pScrn->scrnIndex];
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
+ struct armada_drm_info *drm = GET_DRM_INFO(pScrn);
+ struct drm_armada_bo *bo, *old_bo;
+ uint32_t old_fb_id;
+ int ret, i;
+
+ if (pScrn->virtualX == width && pScrn->virtualY == height)
+ return TRUE;
+
+ bo = armada_bo_alloc_framebuffer(pScrn, drm, width, height,
+ pScrn->bitsPerPixel);
+ if (!bo)
+ return FALSE;
+
+ old_fb_id = drm->fb_id;
+ ret = drmModeAddFB(drm->fd, width, height,
+ pScrn->depth, pScrn->bitsPerPixel,
+ bo->pitch, bo->handle, &drm->fb_id);
+ if (ret) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] failed to add fb: %s\n", strerror(errno));
+ drm_armada_bo_put(bo);
+ return FALSE;
+ }
+
+ /* Okay, now switch everything */
+ pScrn->virtualX = width;
+ pScrn->virtualY = height;
+ pScrn->displayWidth = bo->pitch / drm->cpp;
+ old_bo = drm->front_bo;
+ drm->front_bo = bo;
+
+ armada_drm_ModifyScreenPixmap(screen, drm, width, height, -1, -1, bo);
+
+ for (i = 0; i < xf86_config->num_crtc; i++) {
+ xf86CrtcPtr crtc = xf86_config->crtc[i];
+
+ if (!crtc->enabled)
+ continue;
+
+ armada_drm_crtc_apply(crtc, NULL);
+ }
+
+ drmModeRmFB(drm->fd, old_fb_id);
+ drm_armada_bo_put(old_bo);
+ return TRUE;
+}
+
+static const xf86CrtcConfigFuncsRec armada_drm_config_funcs = {
+ armada_drm_xf86crtc_resize,
+};
+
+#ifdef HAVE_UDEV
+static void armada_drm_handle_uevent(int fd, pointer data)
+{
+ ScrnInfoPtr pScrn = data;
+ struct armada_drm_info *drm = GET_DRM_INFO(pScrn);
+ struct udev_device *ud;
+
+ ud = udev_monitor_receive_device(drm->udev_monitor);
+ if (ud) {
+ dev_t dev = udev_device_get_devnum(ud);
+ const char *hp = udev_device_get_property_value(ud, "HOTPLUG");
+
+ if (dev == drm->drm_dev && hp && strtol(hp, NULL, 10) == 1)
+ RRGetInfo(screenInfo.screens[pScrn->scrnIndex], TRUE);
+
+ udev_device_unref(ud);
+ }
+}
+
+static Bool armada_drm_udev_init(ScrnInfoPtr pScrn)
+{
+ struct armada_drm_info *drm = GET_DRM_INFO(pScrn);
+ struct udev_monitor *udev_mon;
+ struct udev *udev;
+ struct stat st;
+ MessageType from = X_CONFIG;
+ Bool hotplug;
+
+ if (!xf86GetOptValBool(drm->Options, OPTION_HOTPLUG, &hotplug)) {
+ from = X_DEFAULT;
+ hotplug = TRUE;
+ }
+
+ xf86DrvMsg(pScrn->scrnIndex, from, "hotplug detection %sabled\n",
+ hotplug ? "en" : "dis");
+ if (!hotplug)
+ return TRUE;
+
+ if (fstat(drm->fd, &st) || !S_ISCHR(st.st_mode))
+ return FALSE;
+
+ drm->drm_dev = st.st_rdev;
+
+ udev = udev_new();
+ if (!udev)
+ return FALSE;
+
+ udev_mon = udev_monitor_new_from_netlink(udev, "udev");
+ if (!udev_mon) {
+ udev_unref(udev);
+ return FALSE;
+ }
+
+ if (udev_monitor_filter_add_match_subsystem_devtype(udev_mon,
+ "drm", "drm_minor") ||
+ udev_monitor_enable_receiving(udev_mon)) {
+ udev_monitor_unref(udev_mon);
+ udev_unref(udev);
+ return FALSE;
+ }
+
+ drm->udev_monitor = udev_mon;
+ drm->udev_handler = xf86AddGeneralHandler(udev_monitor_get_fd(udev_mon),
+ armada_drm_handle_uevent,
+ pScrn);
+
+ return TRUE;
+}
+
+static void armada_drm_udev_fini(ScrnInfoPtr pScrn, struct armada_drm_info *drm)
+{
+ if (drm->udev_monitor) {
+ struct udev *udev = udev_monitor_get_udev(drm->udev_monitor);
+
+ xf86RemoveGeneralHandler(drm->udev_handler);
+ udev_monitor_unref(drm->udev_monitor);
+ udev_unref(udev);
+ }
+}
+#endif
+
+static void armada_drm_LoadPalette(ScrnInfoPtr pScrn, int num, int *indices,
+ LOCO *colors, VisualPtr pVisual)
+{
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
+ uint16_t lut_r[256], lut_g[256], lut_b[256];
+ int i, p;
+
+ for (i = 0; i < num; i++) {
+ int index = indices[i];
+ lut_r[index] = colors[index].red << 8;
+ lut_g[index] = colors[index].green << 8;
+ lut_b[index] = colors[index].blue << 8;
+ }
+
+ for (p = 0; p < xf86_config->num_crtc; p++) {
+ xf86CrtcPtr crtc = xf86_config->crtc[p];
+
+#ifdef RANDR_12_INTERFACE
+ RRCrtcGammaSet(crtc->randr_crtc, lut_r, lut_g, lut_b);
+#else
+ crtc->funcs->gamma_set(crtc, lut_r, lut_g, lut_b, 256);
+#endif
+ }
+}
+
+static void armada_drm_AdjustFrame(int scrnIndex, int x, int y, int flags)
+{
+ ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
+ xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
+ xf86OutputPtr output = config->output[config->compat_output];
+ xf86CrtcPtr crtc = output->crtc;
+
+ if (crtc && crtc->enabled) {
+ int saved_x = crtc->x;
+ int saved_y = crtc->y;
+ int ret;
+
+ crtc->x = x;
+ crtc->y = y;
+
+ ret = crtc->funcs->set_mode_major(crtc, &crtc->mode,
+ crtc->rotation, x, y);
+ if (!ret) {
+ crtc->x = saved_x;
+ crtc->y = saved_y;
+ }
+ }
+}
+
+static Bool armada_drm_EnterVT(int scrnIndex, int flags)
+{
+ ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
+ xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
+ struct armada_drm_info *drm = GET_DRM_INFO(pScrn);
+ int i;
+
+ if (drmSetMaster(drm->fd))
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "[drm] set master failed: %s\n", strerror(errno));
+
+ if (!xf86SetDesiredModes(pScrn))
+ return FALSE;
+
+ /* Disable unused CRTCs */
+ for (i = 0; i < config->num_crtc; i++) {
+ xf86CrtcPtr crtc = config->crtc[i];
+ struct armada_crtc_info *drmc = crtc->driver_private;
+ if (!crtc->enabled)
+ drmModeSetCrtc(drm->fd, drmc->mode_crtc->crtc_id,
+ 0, 0, 0, NULL, 0, NULL);
+ }
+
+ return TRUE;
+}
+
+static void armada_drm_LeaveVT(int scrnIndex, int flags)
+{
+ ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
+ struct armada_drm_info *drm = GET_DRM_INFO(pScrn);
+
+ xf86RotateFreeShadow(pScrn);
+
+ xf86_hide_cursors(pScrn);
+
+ drmDropMaster(drm->fd);
+}
+
+static ModeStatus
+armada_drm_ValidMode(int scrnIndex, DisplayModePtr mode, Bool verbose,
+ int flags)
+{
+ ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
+
+ if (mode->Flags & V_DBLSCAN) {
+ if (verbose)
+ xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
+ "Removing double-scanned mode \"%s\"\n",
+ mode->name);
+ return MODE_BAD;
+ }
+
+ return MODE_OK;
+}
+
+static Bool armada_drm_SwitchMode(int scrnIndex, DisplayModePtr mode, int flags)
+{
+ ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
+
+ return xf86SetSingleMode(pScrn, mode, RR_Rotate_0);
+}
+
+static Bool armada_drm_CloseScreen(int scrnIndex, ScreenPtr pScreen)
+{
+ ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
+ struct armada_drm_info *drm = GET_DRM_INFO(pScrn);
+ PixmapPtr pixmap = pScreen->GetScreenPixmap(pScreen);
+ Bool ret;
+
+#ifdef HAVE_UDEV
+ armada_drm_udev_fini(pScrn, drm);
+#endif
+
+ if (drm->fb_id) {
+ drmModeRmFB(drm->fd, drm->fb_id);
+ drm->fb_id = 0;
+ }
+ if (drm->front_bo) {
+ drm_armada_bo_put(drm->front_bo);
+ drm->front_bo = NULL;
+ }
+ if (drm->accel)
+ vivante_free_pixmap(pixmap);
+
+ if (drm->hw_cursor)
+ xf86_cursors_fini(pScreen);
+
+ pScreen->CloseScreen = drm->CloseScreen;
+ ret = (*pScreen->CloseScreen)(scrnIndex, pScreen);
+
+ if (pScrn->vtSema)
+ armada_drm_LeaveVT(pScreen->myNum, 0);
+
+ pScrn->vtSema = FALSE;
+
+ return ret;
+}
+
+static Bool armada_drm_CreateScreenResources(ScreenPtr pScreen)
+{
+ ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
+ struct armada_drm_info *drm = GET_DRM_INFO(pScrn);
+ Bool ret;
+
+ pScreen->CreateScreenResources = drm->CreateScreenResources;
+ ret = pScreen->CreateScreenResources(pScreen);
+ if (ret) {
+ struct drm_armada_bo *bo = drm->front_bo;
+
+ armada_drm_ModifyScreenPixmap(pScreen, drm, -1, -1, -1, -1, bo);
+ }
+ return ret;
+}
+
+static void armada_drm_wakeup_handler(pointer data, int err, pointer p)
+{
+ struct armada_drm_info *drm = data;
+ fd_set *read_mask = p;
+
+ if (data == NULL || err < 0)
+ return;
+
+ if (FD_ISSET(drm->fd, read_mask))
+ drmHandleEvent(drm->fd, &drm->event_context);
+}
+
+static Bool
+armada_drm_ScreenInit(int scrnIndex, ScreenPtr pScreen, int argc, char **argv)
+{
+ ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
+ struct armada_drm_info *drm = GET_DRM_INFO(pScrn);
+ struct drm_armada_bo *bo;
+ int visuals, preferredCVC;
+
+ if (drmSetMaster(drm->fd)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] set master failed: %s\n", strerror(errno));
+ return FALSE;
+ }
+
+ drm->accel = xf86ReturnOptValBool(drm->Options, OPTION_USE_GPU, TRUE);
+
+ bo = armada_bo_alloc_framebuffer(pScrn, drm, pScrn->virtualX,
+ pScrn->virtualY, pScrn->bitsPerPixel);
+ if (!bo)
+ return FALSE;
+
+ drm->front_bo = bo;
+ pScrn->displayWidth = bo->pitch / drm->cpp;
+
+ miClearVisualTypes();
+ if (pScrn->bitsPerPixel > 8) {
+ visuals = TrueColorMask;
+ preferredCVC = TrueColor;
+ } else {
+ visuals = miGetDefaultVisualMask(pScrn->depth);
+ preferredCVC = pScrn->defaultVisual;
+ }
+
+ if (!miSetVisualTypes(pScrn->depth, visuals, pScrn->rgbBits, preferredCVC)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] failed to set visual types for %d bpp depth %d\n",
+ pScrn->bitsPerPixel, pScrn->depth);
+ return FALSE;
+ }
+
+ if (!miSetPixmapDepths()) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] failed to set pixmap depths\n");
+ return FALSE;
+ }
+
+ if (!fbScreenInit(pScreen, NULL, pScrn->virtualX, pScrn->virtualY,
+ pScrn->xDpi, pScrn->yDpi, pScrn->displayWidth,
+ pScrn->bitsPerPixel)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] fbScreenInit failed\n");
+ return FALSE;
+ }
+
+ if (pScrn->bitsPerPixel > 8) {
+ /* Fixup RGB ordering */
+ VisualPtr visual = pScreen->visuals + pScreen->numVisuals;
+ while (--visual >= pScreen->visuals) {
+ if ((visual->class | DynamicClass) == DirectColor) {
+ visual->offsetRed = pScrn->offset.red;
+ visual->offsetGreen = pScrn->offset.green;
+ visual->offsetBlue = pScrn->offset.blue;
+ visual->redMask = pScrn->mask.red;
+ visual->greenMask = pScrn->mask.green;
+ visual->blueMask = pScrn->mask.blue;
+ }
+ }
+ }
+
+ if (!fbPictureInit(pScreen, NULL, 0)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] fbPictureInit failed\n");
+ return FALSE;
+ }
+
+ xf86SetBlackWhitePixels(pScreen);
+
+ if (drm->accel && !vivante_ScreenInit(pScreen, drm->bufmgr)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "[drm] Vivante initialization failed, running unaccelerated\n");
+ drm->accel = FALSE;
+ }
+
+ miInitializeBackingStore(pScreen);
+ xf86SetBackingStore(pScreen);
+ xf86SetSilkenMouse(pScreen);
+
+ /* software cursor */
+ miDCInitialize(pScreen, xf86GetPointerScreenFuncs());
+
+ drm->hw_cursor = xf86ReturnOptValBool(drm->Options, OPTION_HW_CURSOR, FALSE);
+ if (drm->hw_cursor &&
+ xf86_cursors_init(pScreen,
+ CURSOR_MAX_WIDTH, CURSOR_MAX_HEIGHT,
+ HARDWARE_CURSOR_TRUECOLOR_AT_8BPP |
+ HARDWARE_CURSOR_BIT_ORDER_MSBFIRST |
+ HARDWARE_CURSOR_INVERT_MASK |
+ HARDWARE_CURSOR_SWAP_SOURCE_AND_MASK |
+ HARDWARE_CURSOR_AND_SOURCE_WITH_MASK |
+ HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_64 |
+ HARDWARE_CURSOR_UPDATE_UNHIDDEN |
+ HARDWARE_CURSOR_ARGB)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Using hardware cursors\n");
+ } else {
+ drm->hw_cursor = FALSE;
+ }
+
+ pScreen->SaveScreen = xf86SaveScreen;
+ drm->CloseScreen = pScreen->CloseScreen;
+ pScreen->CloseScreen = armada_drm_CloseScreen;
+ drm->CreateScreenResources = pScreen->CreateScreenResources;
+ pScreen->CreateScreenResources = armada_drm_CreateScreenResources;
+
+ if (!xf86CrtcScreenInit(pScreen)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] failed to initialize screen\n");
+ return FALSE;
+ }
+
+ if (!miCreateDefColormap(pScreen)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] failed to initialize default colormap\n");
+ return FALSE;
+ }
+
+ if (!xf86HandleColormaps(pScreen, 256, 8, armada_drm_LoadPalette, NULL,
+ CMAP_RELOAD_ON_MODE_SWITCH |
+ CMAP_PALETTED_TRUECOLOR)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] failed to initialize colormap handler\n");
+ return FALSE;
+ }
+
+ xf86DPMSInit(pScreen, xf86DPMSSet, 0);
+
+ if (xf86ReturnOptValBool(drm->Options, OPTION_XV_ACCEL, TRUE))
+ armada_drm_XvInit(pScrn);
+
+ /* Setup the synchronisation feedback */
+ AddGeneralSocket(drm->fd);
+ RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA,
+ armada_drm_wakeup_handler, drm);
+
+#ifdef HAVE_UDEV
+ if (!armada_drm_udev_init(pScrn)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] failed to connect with udev: %s\n",
+ strerror(errno));
+ return FALSE;
+ }
+#endif
+
+ pScrn->vtSema = TRUE;
+
+ return armada_drm_EnterVT(pScreen->myNum, 0);
+}
+
+static Bool armada_drm_pre_init(ScrnInfoPtr pScrn)
+{
+ struct armada_drm_info *drm = GET_DRM_INFO(pScrn);
+ drmVersionPtr version;
+ Gamma zeros = { 0.0, 0.0, 0.0 };
+ int i;
+
+ xf86CollectOptions(pScrn, NULL);
+ drm->Options = malloc(sizeof(armada_drm_options));
+ if (!drm->Options)
+ return FALSE;
+ memcpy(drm->Options, armada_drm_options, sizeof(armada_drm_options));
+ xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, drm->Options);
+
+ version = drmGetVersion(drm->fd);
+ if (version) {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "hardware: %s\n",
+ version->name);
+ drmFreeVersion(version);
+ }
+
+ drm->cpp = (pScrn->bitsPerPixel + 7) / 8;
+
+ xf86CrtcConfigInit(pScrn, &armada_drm_config_funcs);
+
+ drm->mode_res = drmModeGetResources(drm->fd);
+ if (!drm->mode_res) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "failed to get resources: %s\n", strerror(errno));
+ return FALSE;
+ }
+
+ pScrn->virtualX = drm->mode_res->max_width;
+ pScrn->virtualY = drm->mode_res->max_height;
+
+ xf86CrtcSetSizeRange(pScrn, 320, 200, pScrn->virtualX, pScrn->virtualY);
+
+ for (i = 0; i < drm->mode_res->count_crtcs; i++)
+ if (!armada_drm_crtc_init(pScrn, drm, drm->mode_res->crtcs[i]))
+ return FALSE;
+
+ for (i = 0; i < drm->mode_res->count_connectors; i++)
+ armada_drm_conn_init(pScrn, drm, drm->mode_res->connectors[i]);
+
+ xf86InitialConfiguration(pScrn, TRUE);
+
+ /* Limit the maximum framebuffer size to 16MB */
+ pScrn->videoRam = 16 * 1048576;
+
+ drm->event_context.version = DRM_EVENT_CONTEXT_VERSION;
+#ifdef HAVE_DRI2
+ drm->event_context.vblank_handler = vivante_dri2_vblank;
+#else
+ drm->event_context.vblank_handler = NULL;
+#endif
+ drm->event_context.page_flip_handler = NULL;
+
+ if (!xf86SetGamma(pScrn, zeros))
+ return FALSE;
+
+ if (pScrn->modes == NULL) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No modes.\n");
+ return FALSE;
+ }
+
+ pScrn->currentMode = pScrn->modes;
+
+ /* Set display resolution */
+ xf86SetDpi(pScrn, 0, 0);
+
+ if (!xf86LoadSubModule(pScrn, "fb"))
+ return FALSE;
+
+ return TRUE;
+}
+
+static Bool armada_drm_open_master(ScrnInfoPtr pScrn)
+{
+ struct armada_drm_info *drm;
+ EntityInfoPtr pEnt;
+ drmSetVersion sv;
+ const char *busid = DRM_DEFAULT_BUS_ID;
+ int err;
+
+ pEnt = xf86GetEntityInfo(pScrn->entityList[0]);
+ if (pEnt) {
+ if (pEnt->device->busID)
+ busid = pEnt->device->busID;
+ free(pEnt);
+ }
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Using BusID \"%s\"\n", busid);
+
+ drm = calloc(1, sizeof *drm);
+ if (!drm)
+ return FALSE;
+
+ drm->fd = drmOpen(DRM_MODULE_NAME, busid);
+ if (drm->fd < 0) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] Failed to open DRM device for %s: %s\n",
+ busid, strerror(errno));
+ goto err_free;
+ }
+
+ /*
+ * Check that what we opened was a master or a master-capable FD
+ * by setting the version of the interface we'll use to talk to it.
+ */
+ sv.drm_di_major = 1;
+ sv.drm_di_minor = 1;
+ sv.drm_dd_major = -1;
+ sv.drm_dd_minor = -1;
+ err = drmSetInterfaceVersion(drm->fd, &sv);
+ if (err) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] failed to set DRM interface version: %s\n",
+ strerror(errno));
+ goto err_drm_close;
+ }
+
+ err = drm_armada_init(drm->fd, &drm->bufmgr);
+ if (err) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] failed to initialize Armada DRM manager.\n");
+ goto err_drm_close;
+ }
+
+ SET_DRM_INFO(pScrn, drm);
+
+ return TRUE;
+
+ err_drm_close:
+ drmClose(drm->fd);
+ err_free:
+ free(drm);
+ return FALSE;
+}
+
+static void armada_drm_close_master(ScrnInfoPtr pScrn)
+{
+ struct armada_drm_info *drm = GET_DRM_INFO(pScrn);
+
+ if (drm) {
+ drm_armada_fini(drm->bufmgr);
+ drmClose(drm->fd);
+ SET_DRM_INFO(pScrn, NULL);
+ free(drm);
+ }
+}
+
+static void armada_drm_FreeScreen(int scrnIndex, int flags)
+{
+ ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
+
+ armada_drm_close_master(pScrn);
+}
+
+static Bool armada_drm_PreInit(ScrnInfoPtr pScrn, int flags)
+{
+ rgb defaultWeight = { 0, 0, 0 };
+ int flags24;
+
+ if (pScrn->numEntities != 1)
+ return FALSE;
+
+ if (flags & PROBE_DETECT)
+ return FALSE;
+
+ if (!armada_drm_open_master(pScrn)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Failed to become DRM master.\n");
+ return FALSE;
+ }
+
+ pScrn->monitor = pScrn->confScreen->monitor;
+ pScrn->progClock = TRUE;
+ pScrn->rgbBits = 8;
+ pScrn->chipset = "fbdev";
+ pScrn->displayWidth = 640;
+
+ flags24 = Support24bppFb | Support32bppFb | SupportConvert24to32;
+ if (!xf86SetDepthBpp(pScrn, 0, 0, 0, flags24))
+ goto fail;
+
+ switch (pScrn->depth) {
+ case 8:
+ case 15:
+ case 16:
+ case 24:
+ break;
+ default:
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Given depth (%d) is not supported.\n",
+ pScrn->depth);
+ goto fail;
+ }
+
+ xf86PrintDepthBpp(pScrn);
+
+ if (!xf86SetWeight(pScrn, defaultWeight, defaultWeight))
+ goto fail;
+ if (!xf86SetDefaultVisual(pScrn, -1))
+ goto fail;
+
+ if (pScrn->depth > 8 && pScrn->defaultVisual != TrueColor) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Requested default visual (%s) is not supported at depth %d\n",
+ xf86GetVisualName(pScrn->defaultVisual),
+ pScrn->depth);
+ goto fail;
+ }
+
+ if (!armada_drm_pre_init(pScrn))
+ goto fail;
+
+ return TRUE;
+
+ fail:
+ armada_drm_FreeScreen(pScrn->scrnIndex, 0);
+ return FALSE;
+}
+
+Bool armada_drm_init_screen(ScrnInfoPtr pScrn)
+{
+ pScrn->PreInit = armada_drm_PreInit;
+ pScrn->ScreenInit = armada_drm_ScreenInit;
+ pScrn->SwitchMode = armada_drm_SwitchMode;
+ pScrn->AdjustFrame = armada_drm_AdjustFrame;
+ pScrn->EnterVT = armada_drm_EnterVT;
+ pScrn->LeaveVT = armada_drm_LeaveVT;
+ pScrn->FreeScreen = armada_drm_FreeScreen;
+ pScrn->ValidMode = armada_drm_ValidMode;
+
+ return TRUE;
+}