<commit_before>/*
 * Copyright © 2007 Red Hat, Inc.
 * Copyright © 2008 <NAME>
 * Copyright © 2011 Texas Instruments, 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:
 *    <NAME> <<EMAIL>>
 *    <NAME> <<EMAIL>>
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

/* TODO cleanup #includes, remove unnecessary ones */

#include "xorg-server.h"
#include "xorgVersion.h"

#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <string.h>
#include <math.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>

#include <linux/fb.h>


/* All drivers should typically include these */
#include "xf86.h"
#include "xf86_OSproc.h"
#define PPC_MMIO_IS_BE
#include "compiler.h"
#include "mipointer.h"

/* All drivers implementing backing store need this */
#include "mibstore.h"

#include "micmap.h"

#include "xf86DDC.h"

#include "xf86RandR12.h"
#include "dixstruct.h"
#include "scrnintstr.h"
#include "fb.h"
#include "xf86cmap.h"
#include "shadowfb.h"

#include "xf86xv.h"
#include <X11/extensions/Xv.h>

#include "xf86Cursor.h"
#include "xf86DDC.h"

#include "region.h"

#include <X11/extensions/randr.h>

#ifdef HAVE_XEXTPROTO_71
#include <X11/extensions/dpmsconst.h>
#else
#define DPMS_SERVER
#include <X11/extensions/dpms.h>
#endif

#include "omap_driver.h"

#include "xf86Crtc.h"

#include "xf86drmMode.h"
#include "drm_fourcc.h"
#include "X11/Xatom.h"

#include <sys/ioctl.h>
#include <libudev.h>

typedef struct {
	/* hardware cursor: */
	uint32_t plane_id;
	struct omap_bo *bo;
	uint32_t fb_id;
	uint32_t zpos_prop_id;
	int x, y;
} drmmode_cursor_rec, *drmmode_cursor_ptr;

typedef struct {
	int fd;
	drmModeResPtr mode_res;
	int cpp;
	struct udev_monitor *uevent_monitor;
	InputHandlerProc uevent_handler;
	drmmode_cursor_ptr cursor;
} drmmode_rec, *drmmode_ptr;

typedef struct {
	drmmode_ptr drmmode;
	drmModeCrtcPtr mode_crtc;
	int cursor_visible;
} drmmode_crtc_private_rec, *drmmode_crtc_private_ptr;

typedef struct {
	drmModePropertyPtr mode_prop;
	int index; /* Index within the kernel-side property arrays for
	 * this connector. */
	int num_atoms; /* if range prop, num_atoms == 1; if enum prop,
	 * num_atoms == num_enums + 1 */
	Atom *atoms;
} drmmode_prop_rec, *drmmode_prop_ptr;

typedef struct {
	drmmode_ptr drmmode;
	int output_id;
	drmModeConnectorPtr mode_output;
	drmModeEncoderPtr mode_encoder;
	drmModePropertyBlobPtr edid_blob;
	int num_props;
	drmmode_prop_ptr props;
} drmmode_output_private_rec, *drmmode_output_private_ptr;

static void drmmode_output_dpms(xf86OutputPtr output, int mode);

static OMAPScanoutPtr
drmmode_scanout_from_size(OMAPScanoutPtr scanouts, int x, int y, int width,
		int height)
{
	int i;
	for (i = 0; i < MAX_SCANOUTS; i++) {
		if (scanouts[i].x == x && scanouts[i].y == y &&
		    scanouts[i].width == width && scanouts[i].height == height)
			return &scanouts[i];
	}
	return NULL;
}

static OMAPScanoutPtr
drmmode_scanout_from_crtc(OMAPScanoutPtr scanouts, xf86CrtcPtr crtc)
{
	return drmmode_scanout_from_size(scanouts, crtc->x, crtc->y,
			crtc->mode.HDisplay, crtc->mode.VDisplay);
}

OMAPScanoutPtr
drmmode_scanout_from_drawable(OMAPScanoutPtr scanouts, DrawablePtr pDraw)
{
	return drmmode_scanout_from_size(scanouts, pDraw->x, pDraw->y,
			pDraw->width, pDraw->height);
}

static OMAPScanoutPtr
drmmode_scanout_add(OMAPScanoutPtr scanouts, xf86CrtcPtr crtc,
		struct omap_bo *bo)
{
	int i;
	for (i = 0; i < MAX_SCANOUTS; i++) {
		OMAPScanoutPtr s = &scanouts[i];

		if (s->bo)
			continue;

		omap_bo_reference(bo);
		s->x = crtc->x;
		s->y = crtc->y;
		s->width = crtc->mode.HDisplay;
		s->height = crtc->mode.VDisplay;
		s->bo = bo;
		return s;
	}

	return NULL;
}

void
drmmode_scanout_set(OMAPScanoutPtr scanouts, int x, int y, struct omap_bo *bo)
{
	OMAPScanoutPtr s;

	s = drmmode_scanout_from_size(scanouts, x, y, omap_bo_width(bo),
			omap_bo_height(bo));
	if (!s) {
		/* The scanout may not exist after flip, just ignore */
		return;
	}

	omap_bo_reference(bo);
	omap_bo_unreference(s->bo);
	s->bo = bo;
}

int drmmode_crtc_id(xf86CrtcPtr crtc)
{
	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
	return drmmode_crtc->mode_crtc->crtc_id;
}

int drmmode_crtc_index_from_drawable(ScrnInfoPtr pScrn, DrawablePtr pDraw)
{
	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
	xf86CrtcPtr crtc;
	DisplayModePtr mode;
	int i;

	for (i = 0; i < xf86_config->num_crtc; i++) {
		crtc = xf86_config->crtc[i];
		if (!crtc->enabled)
			continue;
		mode = &crtc->mode;
		if (crtc->x == pDraw->x && crtc->y == pDraw->y &&
		    mode->HDisplay == pDraw->width &&
		    mode->VDisplay == pDraw->height)
			return i;
	}
	return -1;
}

int drmmode_crtc_id_from_drawable(ScrnInfoPtr pScrn, DrawablePtr pDraw)
{
	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
	xf86CrtcPtr crtc;
	int index;

	index = drmmode_crtc_index_from_drawable(pScrn, pDraw);
	if (index == -1)
		return 0;
	crtc = xf86_config->crtc[index];
	return drmmode_crtc_id(crtc);
}

static drmmode_ptr
drmmode_from_scrn(ScrnInfoPtr pScrn)
{
	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
	drmmode_crtc_private_ptr drmmode_crtc;

	drmmode_crtc = xf86_config->crtc[0]->driver_private;
	return drmmode_crtc->drmmode;
}

static void
drmmode_ConvertFromKMode(ScrnInfoPtr pScrn, drmModeModeInfo *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);

	DEBUG_MSG("copy mode %s (%p %p)", kmode->name, mode->name, mode);

	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 void
drmmode_ConvertToKMode(ScrnInfoPtr pScrn, drmModeModeInfo *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 void
drmmode_crtc_dpms(xf86CrtcPtr drmmode_crtc, int mode)
{
	// FIXME - Implement this function
}

static struct omap_bo *
drmmode_new_fb(OMAPPtr pOMAP, int width, int height, int depth, int bpp)
{
	struct omap_bo *bo;

	bo = omap_bo_new_with_dim(pOMAP->dev, width, height, depth, bpp,
			OMAP_BO_SCANOUT | OMAP_BO_WC);
	if (!bo)
		return NULL;

	if (omap_bo_clear(bo))
		goto err;

	if (omap_bo_add_fb(bo))
		goto err;

	return bo;
err:
	omap_bo_unreference(bo);
	return NULL;
}

static int
drmmode_set_crtc(ScrnInfoPtr pScrn, xf86CrtcPtr crtc, struct omap_bo *bo, int x,
			int y)
{
	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
	xf86OutputPtr output;
	drmmode_crtc_private_ptr drmmode_crtc;
	drmmode_output_private_ptr drmmode_output;
	int ret, crtc_id, output_count, i;
	uint32_t *output_ids = NULL;
	drmModeModeInfo kmode;

	output_ids = calloc(sizeof(uint32_t), xf86_config->num_output);
	assert(output_ids);

	output_count = 0;
	for (i = 0; i < xf86_config->num_output; i++) {
		output = xf86_config->output[i];
		drmmode_output = output->driver_private;

		if (output->crtc != crtc)
			continue;

		output_ids[output_count]
			= drmmode_output->mode_output->connector_id;
		output_count++;
	}
	if (!output_count) {
		ERROR_MSG("No crtc outputs found");
		ret = -ENODEV;
		goto out;
	}

	drmmode_ConvertToKMode(pScrn, &kmode, &crtc->mode);

	drmmode_crtc = crtc->driver_private;
	crtc_id = drmmode_crtc->mode_crtc->crtc_id;
	ret = drmModeSetCrtc(drmmode_crtc->drmmode->fd, crtc_id,
			omap_bo_get_fb(bo), x, y, output_ids, output_count,
			&kmode);
	if (ret) {
		ERROR_MSG("failed to set mode: %s", strerror(-ret));
		goto out;
	}

out:
	free(output_ids);
	return ret;
}

/*
 * Copy region of @src starting at (@src_x, @src_y) to @dst at (@dst_x, dst_y).
 * This function does no conversions, so it assumes same src and dst bpp.
 *  @src         source buffer
 *  @src_x       x coordinate from which to start copying
 *  @src_y       y coordinate from which to start copying
 *  @src_width   max number of pixels per src row to copy
 *  @src_height  max number of src rows to copy
 *  @src_pitch   total length of each src row, in bytes
 *  @src_cpp     bytes (ie, chars) per pixel of source
 *  @dst         destination buffer
 *  @dst_x       x coordinate to which to start copying
 *  @dst_y       y coordinate to which to start copying
 *  @dst_width   max number of pixels per dst row to copy
 *  @dst_height  max number of dst rows to copy
 *  @dst_pitch   total length of each dst row, in bytes
 *  @dst_cpp     bytes (ie, chars) per pixel of dst, must be same as src_cpp
 */
static void
drmmode_copy_from_to(const uint8_t *src, int src_x, int src_y, int src_width,
		     int src_height, int src_pitch, int src_cpp,
		     uint8_t *dst, int dst_x, int dst_y, int dst_width,
		     int dst_height, int dst_pitch, int dst_cpp)
{
	int y;
	int src_x_start = max(dst_x - src_x, 0);
	int dst_x_start = max(src_x - dst_x, 0);
	int src_y_start = max(dst_y - src_y, 0);
	int dst_y_start = max(src_y - dst_y, 0);
	int width = min(src_width - src_x_start, dst_width - dst_x_start);
	int height = min(src_height - src_y_start, dst_height - dst_y_start);


	assert(src_cpp == dst_cpp);

	if (width <= 0 || height <= 0)
		return;

	src += src_y_start * src_pitch + src_x_start * src_cpp;
	dst += dst_y_start * dst_pitch + dst_x_start * src_cpp;

	for (y = 0; y < height; y++, src += src_pitch, dst += dst_pitch)
		memcpy(dst, src, width * dst_cpp);
}

/*
 * Copy region of src buffer located at (src_x, src_y) that overlaps the dst
 * buffer at dst_x, dst_y.
 * This function does no conversions, so it assumes same bpp and depth.
 * It also assumes the two regions are non-overlapping memory areas, even though
 * they may overlap in pixel space.
 */
static int
drmmode_copy_bo(ScrnInfoPtr pScrn, struct omap_bo *src_bo, int src_x, int src_y,
		struct omap_bo *dst_bo, int dst_x, int dst_y)
{
	void *dst;
	const void *src;

	if (!src_bo || !dst_bo) {
		ERROR_MSG("copy_bo received invalid arguments");
		return -EINVAL;
	}

	assert(omap_bo_bpp(src_bo) == omap_bo_bpp(dst_bo));

	src = omap_bo_map(src_bo);
	if (!src) {
		ERROR_MSG("Couldn't map src bo");
		return -EIO;
	}
	dst = omap_bo_map(dst_bo);
	if (!dst) {
		ERROR_MSG("Couldn't map dst bo");
		return -EIO;
	}

	omap_bo_cpu_prep(src_bo, OMAP_GEM_READ);
	omap_bo_cpu_prep(dst_bo, OMAP_GEM_WRITE);

	drmmode_copy_from_to(src, src_x, src_y,
			     omap_bo_width(src_bo), omap_bo_height(src_bo),
			     omap_bo_pitch(src_bo), omap_bo_Bpp(src_bo),
			     dst, dst_x, dst_y,
			     omap_bo_width(dst_bo), omap_bo_height(dst_bo),
			     omap_bo_pitch(dst_bo), omap_bo_Bpp(dst_bo));

	omap_bo_cpu_fini(dst_bo, 0);
	omap_bo_cpu_fini(src_bo, 0);

	return 0;
}

Bool drmmode_set_blit_mode(ScrnInfoPtr pScrn)
{
	OMAPPtr pOMAP = OMAPPTR(pScrn);
	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
	xf86CrtcPtr crtc;
	OMAPScanoutPtr scanout;
	int ret, i;

	if (pOMAP->flip_mode == OMAP_FLIP_DISABLED)
		return TRUE;

	/* Only copy if source is valid. */
	for (i = 0; i < MAX_SCANOUTS; i++) {
		scanout = &pOMAP->scanouts[i];

		if (!scanout->bo)
			continue;
		if (!scanout->valid)
			continue;

		ret = drmmode_copy_bo(pScrn, scanout->bo, scanout->x,
					  scanout->y, pOMAP->scanout, 0, 0);
		if (ret) {
			ERROR_MSG("Copy crtc to scanout failed");
			return FALSE;
		}
	}
	for (i = 0; i < xf86_config->num_crtc; i++) {
		crtc = xf86_config->crtc[i];

		if (!crtc->enabled)
			continue;

		ret = drmmode_set_crtc(pScrn, crtc, pOMAP->scanout, crtc->x,
				crtc->y);
		if (ret) {
			ERROR_MSG("Set crtc to scanout failed");
			return FALSE;
		}
	}
	pOMAP->flip_mode = OMAP_FLIP_DISABLED;
	return TRUE;
}

Bool drmmode_set_flip_mode(ScrnInfoPtr pScrn)
{
	OMAPPtr pOMAP = OMAPPTR(pScrn);
	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
	xf86CrtcPtr crtc;
	OMAPScanoutPtr scanout;
	int ret, i;

	if (pOMAP->flip_mode == OMAP_FLIP_ENABLED)
		return TRUE;

	/* Only copy if destination is invalid. */
	for (i = 0; i < MAX_SCANOUTS; i++) {
		scanout = &pOMAP->scanouts[i];

		if (!scanout->bo)
			continue;
		if (scanout->valid)
			continue;

		ret = drmmode_copy_bo(pScrn, pOMAP->scanout, 0, 0,
					  scanout->bo, scanout->x,
					  scanout->y);
		if (ret) {
			ERROR_MSG("Copy scanout to crtc failed");
			return FALSE;
		}
		scanout->valid = TRUE;
	}

	for (i = 0; i < xf86_config->num_crtc; i++) {
		crtc = xf86_config->crtc[i];

		if (!crtc->enabled)
			continue;

		scanout = drmmode_scanout_from_crtc(pOMAP->scanouts, crtc);
		if (!scanout)
			continue;

		ret = drmmode_set_crtc(pScrn, crtc, scanout->bo, 0, 0);
		if (ret) {
			ERROR_MSG("Set crtc to crtc scanout failed");
			return FALSE;
		}
	}
	pOMAP->flip_mode = OMAP_FLIP_ENABLED;
	return TRUE;
}

static Bool drmmode_update_scanouts(ScrnInfoPtr pScrn)
{
	OMAPPtr pOMAP = OMAPPTR(pScrn);
	OMAPScanoutPtr scanout;
	int i;
	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
	xf86CrtcPtr crtc;
	struct omap_bo *bo;
	Bool valid;

	OMAPScanout old_scanouts[MAX_SCANOUTS];
	memcpy(old_scanouts, pOMAP->scanouts, sizeof(old_scanouts));
	memset(pOMAP->scanouts, 0, sizeof(pOMAP->scanouts));

	for (i = 0; i < xf86_config->num_crtc; i++) {
		crtc = xf86_config->crtc[i];
		if (!crtc->enabled || !crtc->mode.HDisplay ||
				!crtc->mode.VDisplay)
			continue;

		scanout = drmmode_scanout_from_crtc(old_scanouts, crtc);
		if (scanout) {
			/* Use existing BO */
			bo = scanout->bo;
			valid = scanout->valid;
			memset(scanout, 0, sizeof(*scanout));
		} else {
			/* Allocate a new BO */
			bo = drmmode_new_fb(pOMAP, crtc->mode.HDisplay,
				crtc->mode.VDisplay, pScrn->depth,
				pScrn->bitsPerPixel);
			if (!bo) {
				ERROR_MSG("Scanout buffer allocation failed");
				return FALSE;
			}
			valid = FALSE;
		}
		scanout = drmmode_scanout_add(pOMAP->scanouts, crtc, bo);
		if (!scanout) {
			ERROR_MSG("Add scanout failed");
			omap_bo_unreference(bo);
			return FALSE;
		}
		scanout->valid = valid;

		/*
		 * drmmode_scanout_add() adds a reference, but we either:
		 * * already have a reference, from a recycled BO
		 * * was given a reference when a new BO was allocated
		 */
		omap_bo_unreference(bo);
	}

	/* Drop the remaining unused BOs. */
	for (i = 0; i < MAX_SCANOUTS; i++)
		if (old_scanouts[i].bo != NULL) {
			/*
			 * Set has_resized when discarding active scanouts. This
			 * ensures we trigger the blit/flip logic which will
			 * setcrtc to a valid fb if needed
			 */
			pOMAP->has_resized = TRUE;
			omap_bo_unreference(old_scanouts[i].bo);
		}

	return TRUE;
}

static Bool
drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
		Rotation rotation, int x, int y)
{
	ScrnInfoPtr pScrn = crtc->scrn;
	OMAPPtr pOMAP = OMAPPTR(pScrn);
	xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
	int saved_x, saved_y;
	Rotation saved_rotation;
	DisplayModeRec saved_mode;
	int ret = TRUE;
	int i;
	uint32_t fb_id;

	TRACE_ENTER();

	fb_id = omap_bo_get_fb(pOMAP->scanout);
	if (fb_id == 0) {

		DEBUG_MSG("create framebuffer: %dx%d",
				pScrn->virtualX, pScrn->virtualY);

		ret = omap_bo_add_fb(pOMAP->scanout);
		if (ret)
			return FALSE;
	}

	/* Save the current mode in case there's a problem: */
	saved_mode = crtc->mode;
	saved_x = crtc->x;
	saved_y = crtc->y;
	saved_rotation = crtc->rotation;

	/* Set the new mode: */
	crtc->mode = *mode;
	crtc->x = x;
	crtc->y = y;
	crtc->rotation = rotation;

	if (!xf86CrtcRotate(crtc))
		goto done;

	// Fixme - Intel puts this function here, and Nouveau puts it at the end
	// of this function -> determine what's best for TI'S OMAP4:
	if (crtc->funcs->gamma_set)
		crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green,
				       crtc->gamma_blue, crtc->gamma_size);

	ret = drmmode_update_scanouts(pScrn);
	if (!ret) {
		ERROR_MSG("Update scanouts failed, ret=%d", ret);
		goto done;
	}

	// FIXME - DO WE NEED TO CALL TO THE PVR EXA/DRI2 CODE TO UPDATE THEM???

	/* Turn on any outputs on this crtc that may have been disabled: */
	for (i = 0; i < xf86_config->num_output; i++) {
		xf86OutputPtr output = xf86_config->output[i];

		if (output->crtc != crtc)
			continue;

		drmmode_output_dpms(output, DPMSModeOn);
	}

	// TODO: only call this if we are not using sw cursor.. ie. bad to call this
	// if we haven't called xf86InitCursor()!!
	//	if (pScrn->pScreen)
	//		xf86_reload_cursors(pScrn->pScreen);

done:
	if (!ret) {
		/* If there was a problem, resture the old mode: */
		crtc->x = saved_x;
		crtc->y = saved_y;
		crtc->rotation = saved_rotation;
		crtc->mode = saved_mode;
	}

	TRACE_EXIT();
	return ret;
}

/* Two different workarounds at play here:
 * * Mali has trouble with the cursor overlay when the visible portion has
 *   width less than the minimum FIMD DMA burst, i.e. 32 bytes (8 pixels
 *   ARGB).  This is a problem when the cursor is at the left or right edge
 *   of the screen.  Work around this padding the cursor on the left and
 *   right sides.
 * * X has trouble with cursor dimensions that aren't a multiple of 32,
 *   because it expects bitmask cursors to have a bit pitch the size of the
 *   typical machine word, i.e. 32 bits.
 * We create a 96x64 pixel cursor overlay, with 16 pixels' (64 bytes')
 * worth of padding on each side, so we can export a 64x64 size to X as we'd
 * like to have HW accelerated cursors at least to this size.  We stay with
 * multiple-of-32-pixel sizes internally for efficiency since the longest FIMD
 * DMA burst length is 128 bytes (32 pixels ARGB).
 */

#define CURSORW  96
#define CURSORH  64
#define CURSORPAD 16

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;
	drmmode_cursor_ptr cursor = drmmode->cursor;
	int crtc_x, crtc_y, src_x, src_y, w, h;

	if (!cursor)
		return;

	cursor->x = x;
	cursor->y = y;

	if (!drmmode_crtc->cursor_visible)
		return;

	w = CURSORW;
	h = CURSORH;
	crtc_x = cursor->x - CURSORPAD;
	crtc_y = cursor->y;
	src_x = 0;
	src_y = 0;

	if (crtc_x < 0) {
		src_x += -crtc_x;
		w -= -crtc_x;
		crtc_x = 0;
	}

	if (crtc_y < 0) {
		src_y += -crtc_y;
		h -= -crtc_y;
		crtc_y = 0;
	}

	if ((crtc_x + w) > crtc->mode.HDisplay) {
		w = crtc->mode.HDisplay - crtc_x;
	}

	if ((crtc_y + h) > crtc->mode.VDisplay) {
		h = crtc->mode.VDisplay - crtc_y;
	}

	/* note src coords (last 4 args) are in Q16 format */
	drmModeSetPlane(drmmode->fd, cursor->plane_id,
			drmmode_crtc->mode_crtc->crtc_id, cursor->fb_id, 0,
			crtc_x, crtc_y, w, h, src_x<<16, src_y<<16, w<<16, h<<16);
}

static void
drmmode_hide_cursor(xf86CrtcPtr crtc)
{
	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
	drmmode_ptr drmmode = drmmode_crtc->drmmode;
	drmmode_cursor_ptr cursor = drmmode->cursor;

	if (!cursor)
		return;

	drmmode_crtc->cursor_visible = FALSE;

	/* set plane's fb_id to 0 to disable it */
	drmModeSetPlane(drmmode->fd, cursor->plane_id,
			drmmode_crtc->mode_crtc->crtc_id, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0);
}

static void
drmmode_show_cursor(xf86CrtcPtr crtc)
{
	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
	drmmode_ptr drmmode = drmmode_crtc->drmmode;
	drmmode_cursor_ptr cursor = drmmode->cursor;

	if (!cursor)
		return;

	drmmode_crtc->cursor_visible = TRUE;

	drmModeObjectSetProperty(drmmode->fd, cursor->plane_id,
				 DRM_MODE_OBJECT_PLANE,
				 cursor->zpos_prop_id, 1);

	drmmode_set_cursor_position(crtc, cursor->x, cursor->y);
}

static void
drmmode_load_cursor_argb(xf86CrtcPtr crtc, CARD32 *image)
{
	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
	drmmode_ptr drmmode = drmmode_crtc->drmmode;
	drmmode_cursor_ptr cursor = drmmode->cursor;
	int row, visible;
	void* dst;
	const char* src_row;
	char* dst_row;

	if (!cursor)
		return;

	visible = drmmode_crtc->cursor_visible;

	if (visible)
		drmmode_hide_cursor(crtc);

	dst = omap_bo_map(cursor->bo);
	omap_bo_cpu_prep(cursor->bo, OMAP_GEM_WRITE);
	for (row = 0; row < CURSORH; row += 1) {
		// we're operating with ARGB data (32bpp)
		src_row = (const char*)image + row * 4 * (CURSORW - 2 * CURSORPAD);
		dst_row = (char*)dst + row * 4 * CURSORW;

		// copy CURSORPAD pad bytes, then data, then CURSORPAD more pad bytes
		memset(dst_row, 0, (4 * CURSORPAD));
		memcpy(dst_row + (4 * CURSORPAD), src_row, 4 * (CURSORW - 2 * CURSORPAD));
		memset(dst_row + 4 * (CURSORW - CURSORPAD), 0, (4 * CURSORPAD));
	}
	omap_bo_cpu_fini(cursor->bo, 0);

	if (visible)
		drmmode_show_cursor(crtc);
}

Bool
drmmode_cursor_init(ScreenPtr pScreen)
{
	ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
	OMAPPtr pOMAP = OMAPPTR(pScrn);
	drmmode_ptr drmmode = drmmode_from_scrn(pScrn);
	drmmode_cursor_ptr cursor;
	drmModePlaneRes *plane_resources;
	drmModeObjectPropertiesPtr props;
	drmModePropertyPtr prop;
	int i;
	uint32_t plane_id;

	/* technically we probably don't have any size limit.. since we
	 * are just using an overlay... but xserver will always create
	 * cursor images in the max size, so don't use width/height values
	 * that are too big
	 */
	const int w = CURSORW, h = CURSORH;
	uint32_t handles[4], pitches[4], offsets[4]; /* we only use [0] */

	if (drmmode->cursor) {
		INFO_MSG("cursor already initialized");
		return TRUE;
	}

	cursor = calloc(1, sizeof(drmmode_cursor_rec));

	/* find an unused plane which can be used as a mouse cursor.  Note
	 * that we cheat a bit, in order to not burn one overlay per crtc,
	 * and only show the mouse cursor on one crtc at a time
	 */
	plane_resources = drmModeGetPlaneResources(drmmode->fd);
	if (!plane_resources) {
		ERROR_MSG("drmModeGetPlaneResources failed: %s", strerror(errno));
		return FALSE;
	}

	if (plane_resources->count_planes < 1) {
		ERROR_MSG("not enough planes for HW cursor");
		return FALSE;
	}

	/* HACK: HW Cursor is always using the first plane */
	plane_id = plane_resources->planes[0];
	INFO_MSG("HW Cursor using [PLANE:%u]", plane_id);

	props = drmModeObjectGetProperties(drmmode->fd, plane_id,
					   DRM_MODE_OBJECT_PLANE);
	if (props) {
		for (i = 0; i < props->count_props; i++) {
			prop = drmModeGetProperty(drmmode->fd, props->props[i]);

			if (!strcmp(prop->name, "zpos"))
				cursor->zpos_prop_id = prop->prop_id;
			drmModeFreeProperty(prop);
		}
		drmModeFreeObjectProperties(props);
	}

	cursor->plane_id = plane_id;
	cursor->bo  = omap_bo_new_with_dim(pOMAP->dev, w, h, 0, 32,
			OMAP_BO_SCANOUT | OMAP_BO_WC);

	handles[0] = omap_bo_handle(cursor->bo);
	pitches[0] = omap_bo_pitch(cursor->bo);
	offsets[0] = 0;

	if (drmModeAddFB2(drmmode->fd, w, h, DRM_FORMAT_ARGB8888,
			handles, pitches, offsets, &cursor->fb_id, 0)) {
		ERROR_MSG("drmModeAddFB2 failed: %s", strerror(errno));
		return FALSE;
	}

	// see definition of CURSORPAD
	if (xf86_cursors_init(pScreen, w - 2 * CURSORPAD, h,
		HARDWARE_CURSOR_ARGB | HARDWARE_CURSOR_UPDATE_UNHIDDEN)) {
		INFO_MSG("HW cursor initialized");
		drmmode->cursor = cursor;
		return TRUE;
	}

	// TODO cleanup when things fail..
	return FALSE;
}

void<commit_msg>Handle HW cursor init errors and fallback to SW cursor<commit_after>/*
 * Copyright © 2007 Red Hat, Inc.
 * Copyright © 2008 <NAME>
 * Copyright © 2011 Texas Instruments, 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:
 *    <NAME> <<EMAIL>>
 *    <NAME> <<EMAIL>>
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

/* TODO cleanup #includes, remove unnecessary ones */

#include "xorg-server.h"
#include "xorgVersion.h"

#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <string.h>
#include <math.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>

#include <linux/fb.h>


/* All drivers should typically include these */
#include "xf86.h"
#include "xf86_OSproc.h"
#define PPC_MMIO_IS_BE
#include "compiler.h"
#include "mipointer.h"

/* All drivers implementing backing store need this */
#include "mibstore.h"

#include "micmap.h"

#include "xf86DDC.h"

#include "xf86RandR12.h"
#include "dixstruct.h"
#include "scrnintstr.h"
#include "fb.h"
#include "xf86cmap.h"
#include "shadowfb.h"

#include "xf86xv.h"
#include <X11/extensions/Xv.h>

#include "xf86Cursor.h"
#include "xf86DDC.h"

#include "region.h"

#include <X11/extensions/randr.h>

#ifdef HAVE_XEXTPROTO_71
#include <X11/extensions/dpmsconst.h>
#else
#define DPMS_SERVER
#include <X11/extensions/dpms.h>
#endif

#include "omap_driver.h"

#include "xf86Crtc.h"

#include "xf86drmMode.h"
#include "drm_fourcc.h"
#include "X11/Xatom.h"

#include <sys/ioctl.h>
#include <libudev.h>

typedef struct {
	/* hardware cursor: */
	uint32_t plane_id;
	struct omap_bo *bo;
	uint32_t fb_id;
	uint32_t zpos_prop_id;
	int x, y;
} drmmode_cursor_rec, *drmmode_cursor_ptr;

typedef struct {
	int fd;
	drmModeResPtr mode_res;
	int cpp;
	struct udev_monitor *uevent_monitor;
	InputHandlerProc uevent_handler;
	drmmode_cursor_ptr cursor;
} drmmode_rec, *drmmode_ptr;

typedef struct {
	drmmode_ptr drmmode;
	drmModeCrtcPtr mode_crtc;
	int cursor_visible;
} drmmode_crtc_private_rec, *drmmode_crtc_private_ptr;

typedef struct {
	drmModePropertyPtr mode_prop;
	int index; /* Index within the kernel-side property arrays for
	 * this connector. */
	int num_atoms; /* if range prop, num_atoms == 1; if enum prop,
	 * num_atoms == num_enums + 1 */
	Atom *atoms;
} drmmode_prop_rec, *drmmode_prop_ptr;

typedef struct {
	drmmode_ptr drmmode;
	int output_id;
	drmModeConnectorPtr mode_output;
	drmModeEncoderPtr mode_encoder;
	drmModePropertyBlobPtr edid_blob;
	int num_props;
	drmmode_prop_ptr props;
} drmmode_output_private_rec, *drmmode_output_private_ptr;

static void drmmode_output_dpms(xf86OutputPtr output, int mode);

static OMAPScanoutPtr
drmmode_scanout_from_size(OMAPScanoutPtr scanouts, int x, int y, int width,
		int height)
{
	int i;
	for (i = 0; i < MAX_SCANOUTS; i++) {
		if (scanouts[i].x == x && scanouts[i].y == y &&
		    scanouts[i].width == width && scanouts[i].height == height)
			return &scanouts[i];
	}
	return NULL;
}

static OMAPScanoutPtr
drmmode_scanout_from_crtc(OMAPScanoutPtr scanouts, xf86CrtcPtr crtc)
{
	return drmmode_scanout_from_size(scanouts, crtc->x, crtc->y,
			crtc->mode.HDisplay, crtc->mode.VDisplay);
}

OMAPScanoutPtr
drmmode_scanout_from_drawable(OMAPScanoutPtr scanouts, DrawablePtr pDraw)
{
	return drmmode_scanout_from_size(scanouts, pDraw->x, pDraw->y,
			pDraw->width, pDraw->height);
}

static OMAPScanoutPtr
drmmode_scanout_add(OMAPScanoutPtr scanouts, xf86CrtcPtr crtc,
		struct omap_bo *bo)
{
	int i;
	for (i = 0; i < MAX_SCANOUTS; i++) {
		OMAPScanoutPtr s = &scanouts[i];

		if (s->bo)
			continue;

		omap_bo_reference(bo);
		s->x = crtc->x;
		s->y = crtc->y;
		s->width = crtc->mode.HDisplay;
		s->height = crtc->mode.VDisplay;
		s->bo = bo;
		return s;
	}

	return NULL;
}

void
drmmode_scanout_set(OMAPScanoutPtr scanouts, int x, int y, struct omap_bo *bo)
{
	OMAPScanoutPtr s;

	s = drmmode_scanout_from_size(scanouts, x, y, omap_bo_width(bo),
			omap_bo_height(bo));
	if (!s) {
		/* The scanout may not exist after flip, just ignore */
		return;
	}

	omap_bo_reference(bo);
	omap_bo_unreference(s->bo);
	s->bo = bo;
}

int drmmode_crtc_id(xf86CrtcPtr crtc)
{
	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
	return drmmode_crtc->mode_crtc->crtc_id;
}

int drmmode_crtc_index_from_drawable(ScrnInfoPtr pScrn, DrawablePtr pDraw)
{
	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
	xf86CrtcPtr crtc;
	DisplayModePtr mode;
	int i;

	for (i = 0; i < xf86_config->num_crtc; i++) {
		crtc = xf86_config->crtc[i];
		if (!crtc->enabled)
			continue;
		mode = &crtc->mode;
		if (crtc->x == pDraw->x && crtc->y == pDraw->y &&
		    mode->HDisplay == pDraw->width &&
		    mode->VDisplay == pDraw->height)
			return i;
	}
	return -1;
}

int drmmode_crtc_id_from_drawable(ScrnInfoPtr pScrn, DrawablePtr pDraw)
{
	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
	xf86CrtcPtr crtc;
	int index;

	index = drmmode_crtc_index_from_drawable(pScrn, pDraw);
	if (index == -1)
		return 0;
	crtc = xf86_config->crtc[index];
	return drmmode_crtc_id(crtc);
}

static drmmode_ptr
drmmode_from_scrn(ScrnInfoPtr pScrn)
{
	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
	drmmode_crtc_private_ptr drmmode_crtc;

	drmmode_crtc = xf86_config->crtc[0]->driver_private;
	return drmmode_crtc->drmmode;
}

static void
drmmode_ConvertFromKMode(ScrnInfoPtr pScrn, drmModeModeInfo *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);

	DEBUG_MSG("copy mode %s (%p %p)", kmode->name, mode->name, mode);

	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 void
drmmode_ConvertToKMode(ScrnInfoPtr pScrn, drmModeModeInfo *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 void
drmmode_crtc_dpms(xf86CrtcPtr drmmode_crtc, int mode)
{
	// FIXME - Implement this function
}

static struct omap_bo *
drmmode_new_fb(OMAPPtr pOMAP, int width, int height, int depth, int bpp)
{
	struct omap_bo *bo;

	bo = omap_bo_new_with_dim(pOMAP->dev, width, height, depth, bpp,
			OMAP_BO_SCANOUT | OMAP_BO_WC);
	if (!bo)
		return NULL;

	if (omap_bo_clear(bo))
		goto err;

	if (omap_bo_add_fb(bo))
		goto err;

	return bo;
err:
	omap_bo_unreference(bo);
	return NULL;
}

static int
drmmode_set_crtc(ScrnInfoPtr pScrn, xf86CrtcPtr crtc, struct omap_bo *bo, int x,
			int y)
{
	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
	xf86OutputPtr output;
	drmmode_crtc_private_ptr drmmode_crtc;
	drmmode_output_private_ptr drmmode_output;
	int ret, crtc_id, output_count, i;
	uint32_t *output_ids = NULL;
	drmModeModeInfo kmode;

	output_ids = calloc(sizeof(uint32_t), xf86_config->num_output);
	assert(output_ids);

	output_count = 0;
	for (i = 0; i < xf86_config->num_output; i++) {
		output = xf86_config->output[i];
		drmmode_output = output->driver_private;

		if (output->crtc != crtc)
			continue;

		output_ids[output_count]
			= drmmode_output->mode_output->connector_id;
		output_count++;
	}
	if (!output_count) {
		ERROR_MSG("No crtc outputs found");
		ret = -ENODEV;
		goto out;
	}

	drmmode_ConvertToKMode(pScrn, &kmode, &crtc->mode);

	drmmode_crtc = crtc->driver_private;
	crtc_id = drmmode_crtc->mode_crtc->crtc_id;
	ret = drmModeSetCrtc(drmmode_crtc->drmmode->fd, crtc_id,
			omap_bo_get_fb(bo), x, y, output_ids, output_count,
			&kmode);
	if (ret) {
		ERROR_MSG("failed to set mode: %s", strerror(-ret));
		goto out;
	}

out:
	free(output_ids);
	return ret;
}

/*
 * Copy region of @src starting at (@src_x, @src_y) to @dst at (@dst_x, dst_y).
 * This function does no conversions, so it assumes same src and dst bpp.
 *  @src         source buffer
 *  @src_x       x coordinate from which to start copying
 *  @src_y       y coordinate from which to start copying
 *  @src_width   max number of pixels per src row to copy
 *  @src_height  max number of src rows to copy
 *  @src_pitch   total length of each src row, in bytes
 *  @src_cpp     bytes (ie, chars) per pixel of source
 *  @dst         destination buffer
 *  @dst_x       x coordinate to which to start copying
 *  @dst_y       y coordinate to which to start copying
 *  @dst_width   max number of pixels per dst row to copy
 *  @dst_height  max number of dst rows to copy
 *  @dst_pitch   total length of each dst row, in bytes
 *  @dst_cpp     bytes (ie, chars) per pixel of dst, must be same as src_cpp
 */
static void
drmmode_copy_from_to(const uint8_t *src, int src_x, int src_y, int src_width,
		     int src_height, int src_pitch, int src_cpp,
		     uint8_t *dst, int dst_x, int dst_y, int dst_width,
		     int dst_height, int dst_pitch, int dst_cpp)
{
	int y;
	int src_x_start = max(dst_x - src_x, 0);
	int dst_x_start = max(src_x - dst_x, 0);
	int src_y_start = max(dst_y - src_y, 0);
	int dst_y_start = max(src_y - dst_y, 0);
	int width = min(src_width - src_x_start, dst_width - dst_x_start);
	int height = min(src_height - src_y_start, dst_height - dst_y_start);


	assert(src_cpp == dst_cpp);

	if (width <= 0 || height <= 0)
		return;

	src += src_y_start * src_pitch + src_x_start * src_cpp;
	dst += dst_y_start * dst_pitch + dst_x_start * src_cpp;

	for (y = 0; y < height; y++, src += src_pitch, dst += dst_pitch)
		memcpy(dst, src, width * dst_cpp);
}

/*
 * Copy region of src buffer located at (src_x, src_y) that overlaps the dst
 * buffer at dst_x, dst_y.
 * This function does no conversions, so it assumes same bpp and depth.
 * It also assumes the two regions are non-overlapping memory areas, even though
 * they may overlap in pixel space.
 */
static int
drmmode_copy_bo(ScrnInfoPtr pScrn, struct omap_bo *src_bo, int src_x, int src_y,
		struct omap_bo *dst_bo, int dst_x, int dst_y)
{
	void *dst;
	const void *src;

	if (!src_bo || !dst_bo) {
		ERROR_MSG("copy_bo received invalid arguments");
		return -EINVAL;
	}

	assert(omap_bo_bpp(src_bo) == omap_bo_bpp(dst_bo));

	src = omap_bo_map(src_bo);
	if (!src) {
		ERROR_MSG("Couldn't map src bo");
		return -EIO;
	}
	dst = omap_bo_map(dst_bo);
	if (!dst) {
		ERROR_MSG("Couldn't map dst bo");
		return -EIO;
	}

	omap_bo_cpu_prep(src_bo, OMAP_GEM_READ);
	omap_bo_cpu_prep(dst_bo, OMAP_GEM_WRITE);

	drmmode_copy_from_to(src, src_x, src_y,
			     omap_bo_width(src_bo), omap_bo_height(src_bo),
			     omap_bo_pitch(src_bo), omap_bo_Bpp(src_bo),
			     dst, dst_x, dst_y,
			     omap_bo_width(dst_bo), omap_bo_height(dst_bo),
			     omap_bo_pitch(dst_bo), omap_bo_Bpp(dst_bo));

	omap_bo_cpu_fini(dst_bo, 0);
	omap_bo_cpu_fini(src_bo, 0);

	return 0;
}

Bool drmmode_set_blit_mode(ScrnInfoPtr pScrn)
{
	OMAPPtr pOMAP = OMAPPTR(pScrn);
	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
	xf86CrtcPtr crtc;
	OMAPScanoutPtr scanout;
	int ret, i;

	if (pOMAP->flip_mode == OMAP_FLIP_DISABLED)
		return TRUE;

	/* Only copy if source is valid. */
	for (i = 0; i < MAX_SCANOUTS; i++) {
		scanout = &pOMAP->scanouts[i];

		if (!scanout->bo)
			continue;
		if (!scanout->valid)
			continue;

		ret = drmmode_copy_bo(pScrn, scanout->bo, scanout->x,
					  scanout->y, pOMAP->scanout, 0, 0);
		if (ret) {
			ERROR_MSG("Copy crtc to scanout failed");
			return FALSE;
		}
	}
	for (i = 0; i < xf86_config->num_crtc; i++) {
		crtc = xf86_config->crtc[i];

		if (!crtc->enabled)
			continue;

		ret = drmmode_set_crtc(pScrn, crtc, pOMAP->scanout, crtc->x,
				crtc->y);
		if (ret) {
			ERROR_MSG("Set crtc to scanout failed");
			return FALSE;
		}
	}
	pOMAP->flip_mode = OMAP_FLIP_DISABLED;
	return TRUE;
}

Bool drmmode_set_flip_mode(ScrnInfoPtr pScrn)
{
	OMAPPtr pOMAP = OMAPPTR(pScrn);
	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
	xf86CrtcPtr crtc;
	OMAPScanoutPtr scanout;
	int ret, i;

	if (pOMAP->flip_mode == OMAP_FLIP_ENABLED)
		return TRUE;

	/* Only copy if destination is invalid. */
	for (i = 0; i < MAX_SCANOUTS; i++) {
		scanout = &pOMAP->scanouts[i];

		if (!scanout->bo)
			continue;
		if (scanout->valid)
			continue;

		ret = drmmode_copy_bo(pScrn, pOMAP->scanout, 0, 0,
					  scanout->bo, scanout->x,
					  scanout->y);
		if (ret) {
			ERROR_MSG("Copy scanout to crtc failed");
			return FALSE;
		}
		scanout->valid = TRUE;
	}

	for (i = 0; i < xf86_config->num_crtc; i++) {
		crtc = xf86_config->crtc[i];

		if (!crtc->enabled)
			continue;

		scanout = drmmode_scanout_from_crtc(pOMAP->scanouts, crtc);
		if (!scanout)
			continue;

		ret = drmmode_set_crtc(pScrn, crtc, scanout->bo, 0, 0);
		if (ret) {
			ERROR_MSG("Set crtc to crtc scanout failed");
			return FALSE;
		}
	}
	pOMAP->flip_mode = OMAP_FLIP_ENABLED;
	return TRUE;
}

static Bool drmmode_update_scanouts(ScrnInfoPtr pScrn)
{
	OMAPPtr pOMAP = OMAPPTR(pScrn);
	OMAPScanoutPtr scanout;
	int i;
	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
	xf86CrtcPtr crtc;
	struct omap_bo *bo;
	Bool valid;

	OMAPScanout old_scanouts[MAX_SCANOUTS];
	memcpy(old_scanouts, pOMAP->scanouts, sizeof(old_scanouts));
	memset(pOMAP->scanouts, 0, sizeof(pOMAP->scanouts));

	for (i = 0; i < xf86_config->num_crtc; i++) {
		crtc = xf86_config->crtc[i];
		if (!crtc->enabled || !crtc->mode.HDisplay ||
				!crtc->mode.VDisplay)
			continue;

		scanout = drmmode_scanout_from_crtc(old_scanouts, crtc);
		if (scanout) {
			/* Use existing BO */
			bo = scanout->bo;
			valid = scanout->valid;
			memset(scanout, 0, sizeof(*scanout));
		} else {
			/* Allocate a new BO */
			bo = drmmode_new_fb(pOMAP, crtc->mode.HDisplay,
				crtc->mode.VDisplay, pScrn->depth,
				pScrn->bitsPerPixel);
			if (!bo) {
				ERROR_MSG("Scanout buffer allocation failed");
				return FALSE;
			}
			valid = FALSE;
		}
		scanout = drmmode_scanout_add(pOMAP->scanouts, crtc, bo);
		if (!scanout) {
			ERROR_MSG("Add scanout failed");
			omap_bo_unreference(bo);
			return FALSE;
		}
		scanout->valid = valid;

		/*
		 * drmmode_scanout_add() adds a reference, but we either:
		 * * already have a reference, from a recycled BO
		 * * was given a reference when a new BO was allocated
		 */
		omap_bo_unreference(bo);
	}

	/* Drop the remaining unused BOs. */
	for (i = 0; i < MAX_SCANOUTS; i++)
		if (old_scanouts[i].bo != NULL) {
			/*
			 * Set has_resized when discarding active scanouts. This
			 * ensures we trigger the blit/flip logic which will
			 * setcrtc to a valid fb if needed
			 */
			pOMAP->has_resized = TRUE;
			omap_bo_unreference(old_scanouts[i].bo);
		}

	return TRUE;
}

static Bool
drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
		Rotation rotation, int x, int y)
{
	ScrnInfoPtr pScrn = crtc->scrn;
	OMAPPtr pOMAP = OMAPPTR(pScrn);
	xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
	int saved_x, saved_y;
	Rotation saved_rotation;
	DisplayModeRec saved_mode;
	int ret = TRUE;
	int i;
	uint32_t fb_id;

	TRACE_ENTER();

	fb_id = omap_bo_get_fb(pOMAP->scanout);
	if (fb_id == 0) {

		DEBUG_MSG("create framebuffer: %dx%d",
				pScrn->virtualX, pScrn->virtualY);

		ret = omap_bo_add_fb(pOMAP->scanout);
		if (ret)
			return FALSE;
	}

	/* Save the current mode in case there's a problem: */
	saved_mode = crtc->mode;
	saved_x = crtc->x;
	saved_y = crtc->y;
	saved_rotation = crtc->rotation;

	/* Set the new mode: */
	crtc->mode = *mode;
	crtc->x = x;
	crtc->y = y;
	crtc->rotation = rotation;

	if (!xf86CrtcRotate(crtc))
		goto done;

	// Fixme - Intel puts this function here, and Nouveau puts it at the end
	// of this function -> determine what's best for TI'S OMAP4:
	if (crtc->funcs->gamma_set)
		crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green,
				       crtc->gamma_blue, crtc->gamma_size);

	ret = drmmode_update_scanouts(pScrn);
	if (!ret) {
		ERROR_MSG("Update scanouts failed, ret=%d", ret);
		goto done;
	}

	// FIXME - DO WE NEED TO CALL TO THE PVR EXA/DRI2 CODE TO UPDATE THEM???

	/* Turn on any outputs on this crtc that may have been disabled: */
	for (i = 0; i < xf86_config->num_output; i++) {
		xf86OutputPtr output = xf86_config->output[i];

		if (output->crtc != crtc)
			continue;

		drmmode_output_dpms(output, DPMSModeOn);
	}

	// TODO: only call this if we are not using sw cursor.. ie. bad to call this
	// if we haven't called xf86InitCursor()!!
	//	if (pScrn->pScreen)
	//		xf86_reload_cursors(pScrn->pScreen);

done:
	if (!ret) {
		/* If there was a problem, resture the old mode: */
		crtc->x = saved_x;
		crtc->y = saved_y;
		crtc->rotation = saved_rotation;
		crtc->mode = saved_mode;
	}

	TRACE_EXIT();
	return ret;
}

/* Two different workarounds at play here:
 * * Mali has trouble with the cursor overlay when the visible portion has
 *   width less than the minimum FIMD DMA burst, i.e. 32 bytes (8 pixels
 *   ARGB).  This is a problem when the cursor is at the left or right edge
 *   of the screen.  Work around this padding the cursor on the left and
 *   right sides.
 * * X has trouble with cursor dimensions that aren't a multiple of 32,
 *   because it expects bitmask cursors to have a bit pitch the size of the
 *   typical machine word, i.e. 32 bits.
 * We create a 96x64 pixel cursor overlay, with 16 pixels' (64 bytes')
 * worth of padding on each side, so we can export a 64x64 size to X as we'd
 * like to have HW accelerated cursors at least to this size.  We stay with
 * multiple-of-32-pixel sizes internally for efficiency since the longest FIMD
 * DMA burst length is 128 bytes (32 pixels ARGB).
 */

#define CURSORW  96
#define CURSORH  64
#define CURSORPAD 16

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;
	drmmode_cursor_ptr cursor = drmmode->cursor;
	int crtc_x, crtc_y, src_x, src_y, w, h;

	if (!cursor)
		return;

	cursor->x = x;
	cursor->y = y;

	if (!drmmode_crtc->cursor_visible)
		return;

	w = CURSORW;
	h = CURSORH;
	crtc_x = cursor->x - CURSORPAD;
	crtc_y = cursor->y;
	src_x = 0;
	src_y = 0;

	if (crtc_x < 0) {
		src_x += -crtc_x;
		w -= -crtc_x;
		crtc_x = 0;
	}

	if (crtc_y < 0) {
		src_y += -crtc_y;
		h -= -crtc_y;
		crtc_y = 0;
	}

	if ((crtc_x + w) > crtc->mode.HDisplay) {
		w = crtc->mode.HDisplay - crtc_x;
	}

	if ((crtc_y + h) > crtc->mode.VDisplay) {
		h = crtc->mode.VDisplay - crtc_y;
	}

	/* note src coords (last 4 args) are in Q16 format */
	drmModeSetPlane(drmmode->fd, cursor->plane_id,
			drmmode_crtc->mode_crtc->crtc_id, cursor->fb_id, 0,
			crtc_x, crtc_y, w, h, src_x<<16, src_y<<16, w<<16, h<<16);
}

static void
drmmode_hide_cursor(xf86CrtcPtr crtc)
{
	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
	drmmode_ptr drmmode = drmmode_crtc->drmmode;
	drmmode_cursor_ptr cursor = drmmode->cursor;

	if (!cursor)
		return;

	drmmode_crtc->cursor_visible = FALSE;

	/* set plane's fb_id to 0 to disable it */
	drmModeSetPlane(drmmode->fd, cursor->plane_id,
			drmmode_crtc->mode_crtc->crtc_id, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0);
}

static void
drmmode_show_cursor(xf86CrtcPtr crtc)
{
	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
	drmmode_ptr drmmode = drmmode_crtc->drmmode;
	drmmode_cursor_ptr cursor = drmmode->cursor;

	if (!cursor)
		return;

	drmmode_crtc->cursor_visible = TRUE;

	drmModeObjectSetProperty(drmmode->fd, cursor->plane_id,
				 DRM_MODE_OBJECT_PLANE,
				 cursor->zpos_prop_id, 1);

	drmmode_set_cursor_position(crtc, cursor->x, cursor->y);
}

static void
drmmode_load_cursor_argb(xf86CrtcPtr crtc, CARD32 *image)
{
	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
	drmmode_ptr drmmode = drmmode_crtc->drmmode;
	drmmode_cursor_ptr cursor = drmmode->cursor;
	int row, visible;
	void* dst;
	const char* src_row;
	char* dst_row;

	if (!cursor)
		return;

	visible = drmmode_crtc->cursor_visible;

	if (visible)
		drmmode_hide_cursor(crtc);

	dst = omap_bo_map(cursor->bo);
	omap_bo_cpu_prep(cursor->bo, OMAP_GEM_WRITE);
	for (row = 0; row < CURSORH; row += 1) {
		// we're operating with ARGB data (32bpp)
		src_row = (const char*)image + row * 4 * (CURSORW - 2 * CURSORPAD);
		dst_row = (char*)dst + row * 4 * CURSORW;

		// copy CURSORPAD pad bytes, then data, then CURSORPAD more pad bytes
		memset(dst_row, 0, (4 * CURSORPAD));
		memcpy(dst_row + (4 * CURSORPAD), src_row, 4 * (CURSORW - 2 * CURSORPAD));
		memset(dst_row + 4 * (CURSORW - CURSORPAD), 0, (4 * CURSORPAD));
	}
	omap_bo_cpu_fini(cursor->bo, 0);

	if (visible)
		drmmode_show_cursor(crtc);
}

Bool
drmmode_cursor_init(ScreenPtr pScreen)
{
	ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
	OMAPPtr pOMAP = OMAPPTR(pScrn);
	drmmode_ptr drmmode = drmmode_from_scrn(pScrn);
	drmmode_cursor_ptr cursor;
	drmModePlaneRes *plane_resources;
	drmModeObjectPropertiesPtr props;
	drmModePropertyPtr prop;
	int i;
	uint32_t plane_id;
	int zpos_prop_id;
	Bool ret = FALSE;

	/* technically we probably don't have any size limit.. since we
	 * are just using an overlay... but xserver will always create
	 * cursor images in the max size, so don't use width/height values
	 * that are too big
	 */
	const int w = CURSORW, h = CURSORH;
	uint32_t handles[4], pitches[4], offsets[4]; /* we only use [0] */

	TRACE_ENTER();

	if (drmmode->cursor) {
		INFO_MSG("HW Cursor already initialized");
		goto out;
	}

	/* find an unused plane which can be used as a mouse cursor.  Note
	 * that we cheat a bit, in order to not burn one overlay per crtc,
	 * and only show the mouse cursor on one crtc at a time
	 */
	plane_resources = drmModeGetPlaneResources(drmmode->fd);
	if (!plane_resources) {
		ERROR_MSG("drmModeGetPlaneResources failed: %s", strerror(errno));
		goto out;
	}

	if (plane_resources->count_planes < 1) {
		ERROR_MSG("HW Cursor: not enough planes");
		drmModeFreePlaneResources(plane_resources);
		goto out;
	}

	/* HACK: HW Cursor always uses the first plane */
	plane_id = plane_resources->planes[0];
	INFO_MSG("HW Cursor using [PLANE:%u]", plane_id);

	drmModeFreePlaneResources(plane_resources);

	props = drmModeObjectGetProperties(drmmode->fd, plane_id,
					   DRM_MODE_OBJECT_PLANE);
	if (!props) {
		ERROR_MSG("No properties found for DRM [PLANE:%u]", plane_id);
		goto out;
	}

	/* Find first "zpos" property for our HW Cursor plane */
	zpos_prop_id = -1;
	for (i = 0; i < props->count_props && zpos_prop_id == -1; i++) {
		prop = drmModeGetProperty(drmmode->fd, props->props[i]);

		if (!strcmp(prop->name, "zpos"))
			zpos_prop_id = prop->prop_id;
		drmModeFreeProperty(prop);
	}
	drmModeFreeObjectProperties(props);

	if (zpos_prop_id == -1) {
		ERROR_MSG("No 'zpos' property found for [PLANE:%u]", plane_id);
		goto out;
	}

	cursor = calloc(1, sizeof *cursor);
	if (!cursor) {
		ERROR_MSG("HW Cursor allocation failed");
		goto out;
	}
	cursor->zpos_prop_id = zpos_prop_id;
	cursor->plane_id = plane_id;
	cursor->bo = omap_bo_new_with_dim(pOMAP->dev, w, h, 0, 32,
			OMAP_BO_SCANOUT | OMAP_BO_WC);
	if (!cursor->bo) {
		ERROR_MSG("error allocating hw cursor buffer");
		goto err_free_cursor;
	}

	handles[0] = omap_bo_handle(cursor->bo);
	pitches[0] = omap_bo_pitch(cursor->bo);
	offsets[0] = 0;

	if (drmModeAddFB2(drmmode->fd, w, h, DRM_FORMAT_ARGB8888,
			handles, pitches, offsets, &cursor->fb_id, 0)) {
		ERROR_MSG("drmModeAddFB2 failed: %s", strerror(errno));
		goto err_unref_cursor;
	}

	INFO_MSG("HW Cursor using [FB:%u]", cursor->fb_id);

	// see definition of CURSORPAD
	if (!xf86_cursors_init(pScreen, w - 2 * CURSORPAD, h,
			HARDWARE_CURSOR_ARGB |
			HARDWARE_CURSOR_UPDATE_UNHIDDEN)) {
		ERROR_MSG("xf86_cursors_init() failed");
		goto err_rm_fb;
	}

	INFO_MSG("HW cursor initialized");
	drmmode->cursor = cursor;

	ret = TRUE;
	goto out;

err_rm_fb:
	if (drmModeRmFB(drmmode->fd, cursor->fb_id))
		ERROR_MSG("drmModeRmFB(%u) failed: %s", cursor->fb_id,
				strerror(errno));
err_unref_cursor:
	omap_bo_unreference(cursor->bo);
err_free_cursor:
	free(cursor);
out:
	TRACE_EXIT();
	return ret;
}

void
{'fraction_non_alphanumeric': 0.12119957411569857, 'fraction_numerical': 0.013348318151346662, 'mean_word_length': 4.169078679168365, 'pattern_counts': {'":': 0, '<': 103, '<?xml version=': 0, '>': 567, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1165333', 'n_tokens_mistral': 21618, 'n_tokens_neox': 19893, 'n_words': 4992}