/**
 * Create Buffer.
 *
 * Note that 'format' is used from the client side to specify the DRI buffer
 * format, which could differ from the drawable format.  For example, the
 * drawable could be 32b RGB, but the DRI buffer some YUV format (video) or
 * perhaps lower bit depth RGB (GL).  The color conversion is handled when
 * blitting to front buffer, and page-flipping (overlay or flipchain) can
 * only be used if the display supports.
 */
static DRI2BufferPtr
ARMSOCDRI2CreateBuffer(DrawablePtr pDraw, unsigned int attachment,
		unsigned int format)
{
	ScreenPtr pScreen = pDraw->pScreen;
	ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
	struct ARMSOCDRI2BufferRec *buf;

	DEBUG_MSG("pDraw=%p, attachment=%d, format=%08x",
			pDraw, attachment, format);

	buf = calloc(1, sizeof *buf);
	if (!buf) {
		ERROR_MSG("Couldn't allocate internal buffer structure");
		return NULL;
	}

	DRIBUF(buf)->attachment = attachment;
	DRIBUF(buf)->format = format;

	if (!create_buffer(pDraw, buf)) {
		free(buf);
		return NULL;
	}

	return DRIBUF(buf);
}
/**
 * Create Buffer.
 */
static DRI2BufferPtr
MSMDRI2CreateBuffer(DrawablePtr pDraw, unsigned int attachment,
		unsigned int format)
{
	ScreenPtr pScreen = pDraw->pScreen;
	ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
	MSMDRI2BufferPtr buf = calloc(1, sizeof(*buf));
	PixmapPtr pPixmap;
	int ret;

	DEBUG_MSG("pDraw=%p, attachment=%d, format=%08x",
			pDraw, attachment, format);

	if (!buf) {
		return NULL;
	}

	if (attachment == DRI2BufferFrontLeft) {
		pPixmap = draw2pix(pDraw);
		pPixmap->refcnt++;
	} else {
		pPixmap = createpix(pDraw);
	}

	DRIBUF(buf)->attachment = attachment;
	DRIBUF(buf)->cpp = pPixmap->drawable.bitsPerPixel / 8;
	DRIBUF(buf)->format = format;
	buf->refcnt = 1;
	buf->pPixmap = pPixmap;

	ret = msm_get_pixmap_name(pPixmap, &DRIBUF(buf)->name,
			&DRIBUF(buf)->pitch);
	if (ret) {
		ERROR_MSG("could not get buffer name: %d", ret);
		MSMDRI2DestroyBuffer(pDraw, DRIBUF(buf));
		return NULL;
	}

	return DRIBUF(buf);
}
static Bool destroy_buffer(DrawablePtr pDraw, struct ARMSOCDRI2BufferRec *buf)
{
	ScreenPtr pScreen = buf->pPixmaps[0]->drawable.pScreen;
	ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
	struct ARMSOCRec *pARMSOC = ARMSOCPTR(pScrn);
	DRI2BufferPtr buffer = DRIBUF(buf);
	int numBuffers, i;

	if (--buf->refcnt > 0)
		return FALSE;

	if (buffer->attachment == DRI2BufferBackLeft) {
		assert(pARMSOC->driNumBufs > 1);
		numBuffers = pARMSOC->driNumBufs-1;
	} else
		numBuffers = 1;

	for (i = 0; i < numBuffers && buf->pPixmaps[i] != NULL; i++) {
		ARMSOCDeregisterExternalAccess(buf->pPixmaps[i]);
		pScreen->DestroyPixmap(buf->pPixmaps[i]);
	}

	return TRUE;
}
/**
 * Create Buffer.
 *
 * Note that 'format' is used from the client side to specify the DRI buffer
 * format, which could differ from the drawable format.  For example, the
 * drawable could be 32b RGB, but the DRI buffer some YUV format (video) or
 * perhaps lower bit depth RGB (GL).  The color conversion is handled when
 * blitting to front buffer, and page-flipping (overlay or flipchain) can
 * only be used if the display supports.
 */
static DRI2BufferPtr
ARMSOCDRI2CreateBuffer(DrawablePtr pDraw, unsigned int attachment,
		unsigned int format)
{
	ScreenPtr pScreen = pDraw->pScreen;
	ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
	struct ARMSOCDRI2BufferRec *buf = calloc(1, sizeof(*buf));
	struct ARMSOCRec *pARMSOC = ARMSOCPTR(pScrn);
	struct armsoc_bo *bo;

	DEBUG_MSG("pDraw=%p, attachment=%d, format=%08x",
			pDraw, attachment, format);

	if (!buf) {
		ERROR_MSG("Couldn't allocate internal buffer structure");
		return NULL;
	}

	buf->refcnt = 1;
	buf->previous_canflip = canflip(pDraw);
	DRIBUF(buf)->attachment = attachment;
	DRIBUF(buf)->cpp = pDraw->bitsPerPixel / 8;
	DRIBUF(buf)->format = format + 1; /* suppress DRI2 buffer reuse */
	DRIBUF(buf)->flags = 0;

	/* If it is a pixmap, just migrate to a GEM buffer */
	if (pDraw->type == DRAWABLE_PIXMAP)
	{
	    if (!(bo = MigratePixmapToGEM(pARMSOC, pDraw))) {
	        ErrorF("ARMSOCDRI2CreateBuffer: MigratePixmapToUMP failed\n");
	        free(buf);
	        return NULL;
	    }
	    DRIBUF(buf)->pitch = armsoc_bo_pitch(bo);
	    DRIBUF(buf)->name = armsoc_bo_name(bo);
            buf->bo = bo;
	    return DRIBUF(buf);
	}

	/* We are not interested in anything other than back buffer requests ... */
	if (attachment != DRI2BufferBackLeft || pDraw->type != DRAWABLE_WINDOW) {
		/* ... and just return some dummy UMP buffer */
		bo = pARMSOC->scanout;
		DRIBUF(buf)->pitch = armsoc_bo_pitch(bo);
		DRIBUF(buf)->name = armsoc_bo_name(bo);
		buf->bo = bo;
		armsoc_bo_reference(bo);
		return DRIBUF(buf);
	}

	bo = armsoc_bo_from_drawable(pDraw);
	if (bo && armsoc_bo_width(bo) == pDraw->width && armsoc_bo_height(bo) == pDraw->height && armsoc_bo_bpp(bo) == pDraw->bitsPerPixel) {
		// Reuse existing
		DRIBUF(buf)->pitch = armsoc_bo_pitch(bo);
		DRIBUF(buf)->name = armsoc_bo_name(bo);
		buf->bo = bo;
		armsoc_bo_reference(bo);
		return DRIBUF(buf);
	}

	bo = armsoc_bo_new_with_dim(pARMSOC->dev,
                                pDraw->width,
                                pDraw->height,
                                pDraw->depth,
                                pDraw->bitsPerPixel,
				canflip(pDraw) ? ARMSOC_BO_SCANOUT : ARMSOC_BO_NON_SCANOUT);
	if (!bo) {
	        ErrorF("ARMSOCDRI2CreateBuffer: BO alloc failed\n");
		free(buf);
		return NULL;
	}

	armsoc_bo_set_drawable(bo, pDraw);
	DRIBUF(buf)->name = armsoc_bo_name(bo);
	DRIBUF(buf)->pitch = armsoc_bo_pitch(bo);
	buf->bo = bo;

	if (canflip(pDraw) && attachment != DRI2BufferFrontLeft) {
		/* Create an fb around this buffer. This will fail and we will
		 * fall back to blitting if the display controller hardware
		 * cannot scan out this buffer (for example, if it doesn't
		 * support the format or there was insufficient scanout memory
		 * at buffer creation time). */
		int ret = armsoc_bo_add_fb(bo);
		if (ret) {
			WARNING_MSG(
					"Falling back to blitting a flippable window");
		}
	}

	/* Register Pixmap as having a buffer that can be accessed externally,
	 * so needs synchronised access */
	// FIXME ARMSOCRegisterExternalAccess(pPixmap);

	return DRIBUF(buf);
}
static Bool create_buffer(DrawablePtr pDraw, struct ARMSOCDRI2BufferRec *buf)
{
	ScreenPtr pScreen = pDraw->pScreen;
	ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
	struct ARMSOCRec *pARMSOC = ARMSOCPTR(pScrn);
	DRI2BufferPtr buffer = DRIBUF(buf);
	PixmapPtr pPixmap = NULL;
	struct armsoc_bo *bo;
	int ret;

	if (buffer->attachment == DRI2BufferFrontLeft) {
		pPixmap = draw2pix(pDraw);
		pPixmap->refcnt++;
	} else {
		pPixmap = createpix(pDraw);
	}

	if (!pPixmap) {
		assert(buffer->attachment != DRI2BufferFrontLeft);
		ERROR_MSG("Failed to create back buffer for window");
		goto fail;
	}

	if (buffer->attachment == DRI2BufferBackLeft && pARMSOC->driNumBufs > 2) {
		buf->pPixmaps = calloc(pARMSOC->driNumBufs-1,
				sizeof(PixmapPtr));
		buf->numPixmaps = pARMSOC->driNumBufs-1;
	} else {
		buf->pPixmaps = malloc(sizeof(PixmapPtr));
		buf->numPixmaps = 1;
	}

	if (!buf->pPixmaps) {
		ERROR_MSG("Failed to allocate PixmapPtr array for DRI2Buffer");
		goto fail;
	}

	buf->pPixmaps[0] = pPixmap;
	assert(buf->currentPixmap == 0);

	bo = ARMSOCPixmapBo(pPixmap);
	if (!bo) {
		ERROR_MSG(
				"Attempting to DRI2 wrap a pixmap with no DRM buffer object backing");
		goto fail;
	}

	DRIBUF(buf)->pitch = exaGetPixmapPitch(pPixmap);
	DRIBUF(buf)->cpp = pPixmap->drawable.bitsPerPixel / 8;
	DRIBUF(buf)->flags = 0;
	buf->refcnt = 1;
	buf->previous_canflip = canflip(pDraw);

	ret = armsoc_bo_get_name(bo, &DRIBUF(buf)->name);
	if (ret) {
		ERROR_MSG("could not get buffer name: %d", ret);
		goto fail;
	}

	if (canflip(pDraw) && buffer->attachment != DRI2BufferFrontLeft) {
		/* Create an fb around this buffer. This will fail and we will
		 * fall back to blitting if the display controller hardware
		 * cannot scan out this buffer (for example, if it doesn't
		 * support the format or there was insufficient scanout memory
		 * at buffer creation time). */
		int ret = armsoc_bo_add_fb(bo);
		if (ret) {
			WARNING_MSG(
					"Falling back to blitting a flippable window");
		}
#if DRI2INFOREC_VERSION >= 6
		else if (FALSE == DRI2SwapLimit(pDraw, pARMSOC->swap_chain_size)) {
			WARNING_MSG(
				"Failed to set DRI2SwapLimit(%x,%d)",
				(unsigned int)pDraw, pARMSOC->swap_chain_size);
		}
#endif /* DRI2INFOREC_VERSION >= 6 */
	}

	DRI2_BUFFER_SET_FB(DRIBUF(buf)->flags, armsoc_bo_get_fb(bo) > 0 ? 1 : 0);
	DRI2_BUFFER_SET_REUSED(DRIBUF(buf)->flags, 0);
	/* Register Pixmap as having a buffer that can be accessed externally,
	 * so needs synchronised access */
	ARMSOCRegisterExternalAccess(pPixmap);

	return TRUE;

fail:
	if (pPixmap != NULL) {
		if (buffer->attachment != DRI2BufferFrontLeft)
			pScreen->DestroyPixmap(pPixmap);
		else
			pPixmap->refcnt--;
	}

	return FALSE;
}