/*
 *
 * Copyright (C) 2012-2019, Samsung Electronics Co., Ltd.
 *
 * Display driver, platform depended implementation
 */

#include <tees_ssapi.h>

#include "board.h"
#include "bsp_common.h"
#include "dbg.h"
#include "disp_core.h"
#include "protect.h"
#include "secmap.h"
#include "tuiHal.h"

/* Display device state */
static struct disp_dev_params disp_dev;

static const unsigned int dev_ids[] = {
    TZPC_DEV_DPP,
    TZPC_DEV_DECON0,
    TZPC_DEV_DPU_DMA,
    TZPC_DEV_SMMU_DPU0
};

/*
 *  PLATFORM DEPENDENT LOW LEVEL INTERFACE
 */

/* Get disp drv internal structure pointer */
struct disp_dev_params *get_disp_dev_params(void)
{
    return &disp_dev;
}

/* Protect display controller */
TEE_Result tuiHalDisplayProtectController(void)
{
    dbgPrintf(">> %s\n", __func__);
    TEE_Result ret = TEE_SUCCESS;
    unsigned int i;

    if (disp_dev.tzpc_done) {
        return ret;
    }

    for (i = 0; i < ARRAY_SIZE(dev_ids); i++) {
        ret = protect_by_tzpc(dev_ids[i], true);
        if (ret) {
            errPrintf("failed to protect %d by tzpc\n", dev_ids[i]);
            goto out_err;
        }
    }

    disp_dev.tzpc_done = true;
    dbgPrintf("<< %s\n", __func__);
    return TEE_SUCCESS;

out_err:
    for (unsigned int j = 0; j < i; j++) {
        ret = protect_by_tzpc(dev_ids[j], false);
        if (ret) {
            errPrintf("failed to unprotect %d by tzpc\n", dev_ids[j]);
        }
    }
    return TEE_ERROR_GENERIC;
}

/* Unprotect display controller */
TEE_Result tuiHalDisplayUnprotectController(void)
{
    dbgPrintf(">> %s\n", __func__);
    TEE_Result res = TEE_SUCCESS;
    int ret = 0;

    if (!disp_dev.tzpc_done) {
        return res;
    }

    for (unsigned int i = 0; i < ARRAY_SIZE(dev_ids); i++) {
        ret = protect_by_tzpc(dev_ids[i], false);
        if (ret) {
            errPrintf("failed to unprotect %d by tzpc\n", dev_ids[i]);
            res = TEE_ERROR_GENERIC;
        }
    }

    if (res == TEE_SUCCESS) {
        disp_dev.tzpc_done = false;
    }

    dbgPrintf("<< %s\n", __func__);
    return res;
}

/* Protect display framebuffer */
TEE_Result tuiHalDisplayProtectFramebuffer(void)
{
    dbgPrintf(">> %s\n", __func__);
    TEE_Result res = TEE_SUCCESS;

    if (!disp_dev.tzasc_done) {
        uint32_t total_size = disp_dev.physicalFbLen + disp_dev.physicalWbLen;

        res = TEES_TUI_Protect(disp_dev.physicalFb, total_size, disp_dev.display_if, NULL);
        if (res != TEE_SUCCESS) {
            errPrintf("TEES_TUI_Protect failed res %d\n", res);
            return TEE_ERROR_GENERIC;
        }

        res = TEES_TUI_SetInfo(TUI_SET_INFO, disp_dev.physicalFb,
                               total_size, NULL);
        if (res == TEE_SUCCESS) {
            disp_dev.tzasc_done = true;
        } else {
            errPrintf("TUI_SET_INFO failed res %d\n", res);
            res = TEES_TUI_Unprotect(disp_dev.physicalFb, total_size, disp_dev.display_if, NULL);
            if (res != TEE_SUCCESS) {
                errPrintf("TEES_TUI_Unprotect failed res %d\n", res);
            }
            return TEE_ERROR_GENERIC;
        }
    }

    dbgPrintf("<< %s\n", __func__);
    return res;
}

/* Unprotect display framebuffer */
TEE_Result tuiHalDisplayUnprotectFramebuffer(void)
{
    dbgPrintf(">> %s\n", __func__);
    TEE_Result res = TEE_SUCCESS;

    if (disp_dev.tzasc_done) {
        uint32_t total_size = disp_dev.physicalFbLen + disp_dev.physicalWbLen;

        res = TEES_TUI_Unprotect(disp_dev.physicalFb, total_size, disp_dev.display_if, NULL);
        if (res != TEE_SUCCESS) {
            errPrintf("TEES_TUI_Unprotect failed res %d\n", res);
            return TEE_ERROR_GENERIC;
        }

        res = TEES_TUI_SetInfo(TUI_CLEAR_INFO, disp_dev.physicalFb,
                               total_size, NULL);
        if (res == TEE_SUCCESS) {
            disp_dev.tzasc_done = false;
        } else {
            errPrintf("TUI_CLEAR_INFO failed res %d\n", res);
            return TEE_ERROR_GENERIC;
        }
    }

    dbgPrintf("<< %s\n", __func__);
    return res;
}

/* Map the video controller registers into the driver address space */
TEE_Result tuiHalDisplayMapController(void)
{
    dbgPrintf(">> %s\n", __func__);

    /* Map Display subsystem */
    if (sec_map(DEV_DPP_SEC, DISP_DPP_SEC_BASE, SIZE_8KB) != 0) {
        errPrintf("failed to map DEV_DPP_SEC\n");
        return E_TUI_HAL_MAP;
    }

    if (sec_map(DEV_DECON, DISP_DECON_BASE, SIZE_8KB) != 0) {
        errPrintf("failed to map DEV_DECON\n");
        return E_TUI_HAL_MAP;
    }

    if (sec_map(DEV_IDMA_COMMON, DISP_IDMA_COMMON_BASE, SIZE_4KB) != 0) {
        errPrintf("failed to map DEV_IDMA_COMMON\n");
        return E_TUI_HAL_MAP;
    }

    if (sec_map(DEV_IDMA_SEC, DISP_IDMA_SEC_BASE, SIZE_4KB) != 0) {
        errPrintf("failed to map DEV_IDMA_SEC\n");
        return E_TUI_HAL_MAP;
    }

    if (sec_map(DEV_SMMU1, SMMUDECON1_BASE, SIZE_4KB)) {
        errPrintf("failed to map DEV_SMMU\n");
        return E_TUI_HAL_MAP;
    }

    dbgPrintf("<< %s\n", __func__);
    return TEE_SUCCESS;
}

/* Unmap the video controller registers from the driver address space */
TEE_Result tuiHalDisplayUnMapController(void)
{
    dbgPrintf(">> %s\n", __func__);
    sec_release();
    dbgPrintf("<< %s\n", __func__);
    return TEE_SUCCESS;
}
