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

#include "saa_priv.h"
#include "saa.h"

PixmapPtr
saa_create_pixmap(ScreenPtr pScreen, int w, int h, int depth,
		  unsigned usage_hint)
{
    PixmapPtr pPixmap;
    struct saa_pixmap *spix;
    int bpp;
    size_t paddedWidth;
    struct saa_screen_priv *sscreen = saa_screen(pScreen);
    int new_pitch = 0;
    struct saa_driver *driver = sscreen->driver;

    if (w > 32767 || h > 32767)
	return NullPixmap;

    /*
     * Create a scratch pixmap without backing storage (w and h are zero)
     */

    saa_swap(sscreen, pScreen, CreatePixmap);
    pPixmap = pScreen->CreatePixmap(pScreen, 0, 0, depth, usage_hint);
    saa_swap(sscreen, pScreen, CreatePixmap);

    if (!pPixmap)
	goto out_no_pix;

    spix = saa_pixmap(pPixmap);
    memset(spix, 0, driver->pixmap_size);
    REGION_NULL(pScreen, &spix->dirty_shadow);
    REGION_NULL(pScreen, &spix->dirty_hw);
    REGION_NULL(pScreen, &spix->shadow_damage);
    spix->read_access = 0;
    spix->write_access = 0;
    spix->mapped_access = 0;
    spix->addr = NULL;
    spix->auth_loc = saa_loc_override;
    spix->override = SAA_INVALID_ADDRESS;
    spix->pixmap = pPixmap;
    bpp = pPixmap->drawable.bitsPerPixel;

    if (!driver->create_pixmap(driver, spix, w, h, depth,
			       usage_hint, bpp, &new_pitch))
	goto out_no_driver_priv;

    paddedWidth = new_pitch;
    spix->damage = NULL;

    /*
     * Now set w and h to the correct value. This might allocate
     * backing store if w and h are NON-NULL.
     */

    if (!(*pScreen->ModifyPixmapHeader) (pPixmap, w, h, 0, 0,
					 paddedWidth, NULL))
	goto out_no_pixmap_header;

    /*
     * During a fallback we must prepare access. This hack is initially used
     * for pixmaps created during ValidateGC.
     */

    spix->fallback_created = FALSE;
    if (sscreen->fallback_count) {
	if (!saa_prepare_access_pixmap(pPixmap, SAA_ACCESS_W, NULL))
	    goto out_no_access;

	spix->fallback_created = TRUE;
    }

    return pPixmap;
 out_no_access:
 out_no_pixmap_header:
    driver->destroy_pixmap(driver, pPixmap);
 out_no_driver_priv:
    saa_swap(sscreen, pScreen, DestroyPixmap);
    if (pScreen->DestroyPixmap)
        pScreen->DestroyPixmap(pPixmap);
    saa_swap(sscreen, pScreen, DestroyPixmap);
 out_no_pix:
    LogMessage(X_ERROR, "Failing pixmap creation.\n");
    return NullPixmap;
}

Bool
saa_destroy_pixmap(PixmapPtr pPixmap)
{
    ScreenPtr pScreen = pPixmap->drawable.pScreen;
    struct saa_screen_priv *sscreen = saa_screen(pScreen);
    Bool ret = TRUE;
    struct saa_driver *driver = sscreen->driver;

    if (pPixmap->refcnt == 1) {
	struct saa_pixmap *spix = saa_pixmap(pPixmap);

	if (spix->fallback_created) {
	    if (!sscreen->fallback_count)
		LogMessage(X_ERROR, "Fallback pixmap destroyed outside "
			   "fallback.\n");

	    saa_finish_access_pixmap(pPixmap, SAA_ACCESS_W);
	}

	driver->destroy_pixmap(driver, pPixmap);

	REGION_UNINIT(pScreen, &spix->dirty_hw);
	REGION_UNINIT(pScreen, &spix->dirty_shadow);
	if (spix->damage)
	    DamageDestroy(spix->damage);
    }

    saa_swap(sscreen, pScreen, DestroyPixmap);
    if (pScreen->DestroyPixmap)
        ret = pScreen->DestroyPixmap(pPixmap);
    saa_swap(sscreen, pScreen, DestroyPixmap);

    return ret;
}

Bool
saa_modify_pixmap_header(PixmapPtr pPixmap, int width, int height, int depth,
			 int bitsPerPixel, int devKind, void *pPixData)
{
    ScreenPtr pScreen;
    struct saa_screen_priv *sscreen;
    struct saa_pixmap *spix;
    struct saa_driver *driver;
    Bool ret = TRUE;

    if (!pPixmap)
	return FALSE;

    pScreen = pPixmap->drawable.pScreen;
    sscreen = saa_screen(pScreen);
    spix = saa_pixmap(pPixmap);
    driver = sscreen->driver;

    if (spix && driver->modify_pixmap_header &&
	driver->modify_pixmap_header(pPixmap, width, height, depth,
				     bitsPerPixel, devKind, pPixData)) {
	spix->auth_loc = saa_loc_driver;
	spix->override = SAA_INVALID_ADDRESS;
	goto out;
    }

    saa_swap(sscreen, pScreen, ModifyPixmapHeader);
    ret = pScreen->ModifyPixmapHeader(pPixmap, width, height, depth,
				      bitsPerPixel, devKind, pPixData);
    saa_swap(sscreen, pScreen, ModifyPixmapHeader);
    spix->override = pPixmap->devPrivate.ptr;
    spix->auth_loc = saa_loc_override;

 out:
    pPixmap->devPrivate.ptr = NULL;
    return ret;
}

struct saa_pixmap *
saa_get_saa_pixmap(PixmapPtr pPixmap)
{
    return saa_pixmap(pPixmap);
}

void
saa_pixmap_dirty(PixmapPtr pixmap, Bool hw, RegionPtr reg)
{
    struct saa_pixmap *spix = saa_pixmap(pixmap);
    struct saa_screen_priv *sscreen = saa_screen(pixmap->drawable.pScreen);

    if (hw) {
	REGION_UNION(pixmap->drawable.pScreen, &spix->dirty_hw,
		     &spix->dirty_hw, reg);
	REGION_SUBTRACT(pixmap->drawable.pScreen, &spix->dirty_shadow,
			&spix->dirty_shadow, reg);
    } else {
	REGION_UNION(pixmap->drawable.pScreen, &spix->dirty_shadow,
		     &spix->dirty_shadow, reg);
	REGION_SUBTRACT(pixmap->drawable.pScreen, &spix->dirty_hw,
			&spix->dirty_hw, reg);
    }

    sscreen->driver->damage(sscreen->driver, pixmap, hw, reg);
}

void
saa_drawable_dirty(DrawablePtr draw, Bool hw, RegionPtr reg)
{
    PixmapPtr pixmap;
    int x_offset, y_offset;

    pixmap = saa_get_pixmap(draw, &x_offset, &y_offset);
    REGION_TRANSLATE(draw->pScreen, reg, x_offset, y_offset);
    saa_pixmap_dirty(pixmap, hw, reg);
    REGION_TRANSLATE(draw->pScreen, reg, -x_offset, -y_offset);
}
