/**
 * @file    dpp_reg.c
 * @brief  CAL file for Samsung DPP
 *
 * Copyright (c) 2012-2020, Samsung Electronics Co., Ltd.
 *
 * This software is proprietary of Samsung Electronics.
 * No part of this software, either material or conceptual may be copied
 * or distributed, transmitted, transcribed, stored in a retrieval system
 * or translated into any human or computer language in any form by any means,
 * electronic, mechanical, manual or otherwise, or disclosed to third parties
 * without the express written permission of Samsung Electronics.
 */

#include "board.h"
#include "bsp_common.h"
#include "dbg.h"
#include "decon.h"
#include "device.h"
#include "dpp_coef.h"
#include "dpp_common.h"
#include "regs-dpp.h"
#include "secmap.h"

/*************************************************/
/*************** INTERNAL FUNCTION ***************/
/*************************************************/

#define TIMEOUT           (20 * 1000) /* 20ms */
#define SECURE_IDMA_DPP   0

static uint32_t log_level = 0;
/*************************************************/
/***************** IDMA FUNCTION *****************/
/*************************************************/

/* 1. INTERNAL FUNCTION */

static uint32_t dma_read(uint32_t id, uint32_t reg_id)
{
    (void)id;

    return IDMA_SEC_REG(reg_id);
}

static void dma_write(uint32_t id, uint32_t reg_id, uint32_t val)
{
    (void)id;

    IDMA_SEC_REG(reg_id) = val;

    if (0 < log_level) {
        dbgPrintf("[%s] off(0x%x)= 0x%x\n", __func__, reg_id, val);
    }
}

static void dma_write_mask(uint32_t id, uint32_t reg_id, uint32_t val, uint32_t mask)
{
    uint32_t old = dma_read(id, reg_id);

    val = (val & mask) | (old & ~mask);
    dma_write(id, reg_id, val);
}

/* 2. H/W access API */

uint32_t dma_reg_get_op_status(uint32_t id)
{
    uint32_t val;

    val = dma_read(id, IDMA_ENABLE);
    if (val & IDMA_OP_STATUS)
		return OP_STATUS_BUSY;

	return OP_STATUS_IDLE;
}

static int dpp_reg_wait_status_run(uint32_t id)
{
    uint32_t dma_status = 0;
    unsigned long delay_time = 10;
    unsigned long cnt = TIMEOUT / delay_time;

    while (cnt) {
        dma_status = dma_reg_get_op_status(id);

        if (dma_status != OP_STATUS_IDLE) {
            break;
        }

        cnt--;
        udelay(delay_time);
    }

    if (!cnt) {
        if (dma_status == OP_STATUS_IDLE) {
            dbgPrintf("[dma%d] timeout op_status to run\n", id);
        }
        return -1;
    }

    return 0;
}

static int dpp_reg_wait_status_idle(uint32_t id)
{
    uint32_t dma_status = 0;
    unsigned long delay_time = 10;
    unsigned long cnt = TIMEOUT / delay_time;

    while (cnt) {
        dma_status = dma_reg_get_op_status(id);

        if (dma_status != OP_STATUS_BUSY) {
            break;
        }

        cnt--;
        udelay(delay_time);
    }

    if (!cnt) {
        if (dma_status == OP_STATUS_BUSY) {
            dbgPrintf("[dma%d] timeout op_status to idle\n", id);
        }
        return -1;
    }

    return 0;
}

/*---------------------------[ DMA CAL APIs ]---------------------------*/

static void idma_reg_set_irq_mask_all(uint32_t id, uint32_t en)
{
    uint32_t val = en ? ~0 : 0;

    dma_write_mask(id, IDMA_IRQ, val, IDMA_ALL_IRQ_MASK);
}

static void idma_reg_clear_irq(uint32_t id, uint32_t irq)
{
    dma_write_mask(id, IDMA_IRQ, ~0, irq);
}

static void idma_reg_set_coordinates(uint32_t id, uint32_t w, uint32_t h)
{
    dma_write(id, IDMA_SRC_OFFSET,
            IDMA_SRC_OFFSET_Y(0) | IDMA_SRC_OFFSET_X(0));
    dma_write(id, IDMA_SRC_SIZE,
            IDMA_SRC_HEIGHT(h) | IDMA_SRC_WIDTH(w));
    dma_write(id, IDMA_IMG_SIZE,
            IDMA_IMG_HEIGHT(h) | IDMA_IMG_WIDTH(w));
}

static void idma_reg_set_format(uint32_t id, uint32_t fmt)
{
    dma_write_mask(id, IDMA_IN_CON, IDMA_IMG_FORMAT(fmt),
            IDMA_IMG_FORMAT_MASK);
}

static void dma_reg_set_base_addr(uint32_t id, uint32_t addr_y, uint32_t addr_c)
{
    dma_write(id, IDMA_IN_BASE_ADDR_Y8, addr_y);
    dma_write(id, IDMA_IN_BASE_ADDR_C8, addr_c);
}

static int dma_dpp_reg_set_format(uint32_t id, enum decon_pixel_format format)
{
    uint32_t fmt;
    uint32_t alpha_type = 0; /* 0: per-frame, 1: per-pixel */
    uint32_t fmt_type = 0;

    switch (format) {
    case DECON_PIXEL_FORMAT_ARGB_8888:
        fmt = IDMA_IMG_FORMAT_ARGB8888;
        fmt_type = DPP_IMG_FORMAT_ARGB8888;
        alpha_type = 1;
        break;
    case DECON_PIXEL_FORMAT_ABGR_8888:
        fmt = IDMA_IMG_FORMAT_ABGR8888;
        fmt_type = DPP_IMG_FORMAT_ARGB8888;
        alpha_type = 1;
        break;
    case DECON_PIXEL_FORMAT_RGBA_8888:
        fmt = IDMA_IMG_FORMAT_RGBA8888;
        fmt_type = DPP_IMG_FORMAT_ARGB8888;
        alpha_type = 1;
        break;
    case DECON_PIXEL_FORMAT_BGRA_8888:
        fmt = IDMA_IMG_FORMAT_BGRA8888;
        fmt_type = DPP_IMG_FORMAT_ARGB8888;
        alpha_type = 1;
        break;
    case DECON_PIXEL_FORMAT_XRGB_8888:
        fmt = IDMA_IMG_FORMAT_XRGB8888;
        fmt_type = DPP_IMG_FORMAT_ARGB8888;
        break;
    case DECON_PIXEL_FORMAT_XBGR_8888:
        fmt = IDMA_IMG_FORMAT_XBGR8888;
        fmt_type = DPP_IMG_FORMAT_ARGB8888;
        break;
    case DECON_PIXEL_FORMAT_RGBX_8888:
        fmt = IDMA_IMG_FORMAT_RGBX8888;
        fmt_type = DPP_IMG_FORMAT_ARGB8888;
        break;
    case DECON_PIXEL_FORMAT_BGRX_8888:
        fmt = IDMA_IMG_FORMAT_BGRX8888;
        fmt_type = DPP_IMG_FORMAT_ARGB8888;
        break;
    case DECON_PIXEL_FORMAT_RGB_565:
        fmt = IDMA_IMG_FORMAT_RGB565;
        fmt_type = DPP_IMG_FORMAT_ARGB8888;
        break;
    /* TODO: add ARGB1555 & ARGB4444 */
    case DECON_PIXEL_FORMAT_ARGB_2101010:
        fmt = IDMA_IMG_FORMAT_ARGB2101010;
        fmt_type = DPP_IMG_FORMAT_ARGB8101010;
        alpha_type = 1;
        break;
    case DECON_PIXEL_FORMAT_ABGR_2101010:
        fmt = IDMA_IMG_FORMAT_ABGR2101010;
        fmt_type = DPP_IMG_FORMAT_ARGB8101010;
        alpha_type = 1;
        break;
    case DECON_PIXEL_FORMAT_RGBA_1010102:
        fmt = IDMA_IMG_FORMAT_RGBA1010102;
        fmt_type = DPP_IMG_FORMAT_ARGB8101010;
        alpha_type = 1;
        break;
    case DECON_PIXEL_FORMAT_BGRA_1010102:
        fmt = IDMA_IMG_FORMAT_BGRA1010102;
        fmt_type = DPP_IMG_FORMAT_ARGB8101010;
        alpha_type = 1;
        break;

    case DECON_PIXEL_FORMAT_NV12:
    case DECON_PIXEL_FORMAT_NV12M:
        fmt = IDMA_IMG_FORMAT_YUV420_2P;
        fmt_type = DPP_IMG_FORMAT_YUV420_8P;
        break;
    case DECON_PIXEL_FORMAT_NV21:
    case DECON_PIXEL_FORMAT_NV21M:
    case DECON_PIXEL_FORMAT_NV12N:
        fmt = IDMA_IMG_FORMAT_YVU420_2P;
        fmt_type = DPP_IMG_FORMAT_YUV420_8P;
        break;

    case DECON_PIXEL_FORMAT_NV12N_10B:
        fmt = IDMA_IMG_FORMAT_YVU420_8P2;
        fmt_type = DPP_IMG_FORMAT_YUV420_8P2;
        break;
    case DECON_PIXEL_FORMAT_NV12M_P010:
        fmt = IDMA_IMG_FORMAT_YUV420_P010;
        fmt_type = DPP_IMG_FORMAT_YUV420_P010;
        break;
    case DECON_PIXEL_FORMAT_NV21M_P010:
        fmt = IDMA_IMG_FORMAT_YVU420_P010;
        fmt_type = DPP_IMG_FORMAT_YUV420_P010;
        break;
    case DECON_PIXEL_FORMAT_NV12M_S10B:
        fmt = IDMA_IMG_FORMAT_YVU420_8P2;
        fmt_type = DPP_IMG_FORMAT_YUV420_8P2;
        break;
    case DECON_PIXEL_FORMAT_NV21M_S10B:
        fmt = IDMA_IMG_FORMAT_YUV420_8P2;
        fmt_type = DPP_IMG_FORMAT_YUV420_8P2;
        break;
    case DECON_PIXEL_FORMAT_NV16:
        fmt = IDMA_IMG_FORMAT_YVU422_2P;
        fmt_type = DPP_IMG_FORMAT_YUV422_8P;
        break;
    case DECON_PIXEL_FORMAT_NV61:
        fmt = IDMA_IMG_FORMAT_YUV422_2P;
        fmt_type = DPP_IMG_FORMAT_YUV422_8P;
        break;
    default:
        dbgPrintf("Unsupported Format\n");
        return -1;
    }

    idma_reg_set_format(id, fmt);
	return fmt_type + alpha_type;
}


/* 0: secure, 1: non-secure */
void dma_reg_set_secure_en(uint32_t id, uint32_t en)
{
    uint32_t val = en ? ~0 : 0;
    dma_write_mask(id, IDMA_ENABLE, val, IDMA_SECURE_MODE);
}

uint32_t g_width;
uint32_t g_height;

/*************************************************/
/******* EXTERNAL FUNCTION: H/W access API *******/
/*************************************************/
void dpp_reg_init(uint32_t width, uint32_t height)
{
    uint32_t id = SECURE_IDMA_DPP;

    dbgPrintf("%s:%d +\n", __func__, __LINE__);
    g_width = width;
    g_height = height;

    /* IDMA setting */
    /* Set SWD irq disable */
    idma_reg_clear_irq(id, IDMA_ALL_IRQ_CLEAR);
    idma_reg_set_irq_mask_all(id, 1);
    idma_reg_set_coordinates(id, g_width, g_height);
    dma_dpp_reg_set_format(id, PIXEL_FORMAT_ABGR_8888);
    dbgPrintf("%s:%d IDMA setting\n", __func__, __LINE__);

    /* part of the dpp/dma configuration */
    dma_dpp_reg_set_format(id, DECON_PIXEL_FORMAT_ABGR_8888);
    dbgPrintf("%s:%d DMA/DPP config\n", __func__, __LINE__);
    dma_reg_set_secure_en(id, 1);
    dbgPrintf("%s:%d -\n", __func__, __LINE__);
}

void dpp_reg_ready(uint32_t fb_addr)
{
    uint32_t id = SECURE_IDMA_DPP;
    dbgPrintf("%s:%x +\n", __func__, fb_addr);

    /* TODO : resetted? if it was soft reset, then re-init */
#if 0
    if (dpp_read(id, DPP_IMG_SIZE) == 0) {
        dbgPrintf("%s:%d * %d +\n", __func__, g_width, g_height);
        dpp_reg_init(g_width, g_height);
    }
#endif
    dma_reg_set_base_addr(id, fb_addr, 0);
}

int dpp_reg_start(void)
{
    uint32_t id = SECURE_IDMA_DPP;
    int ret;
    dbgPrintf("%s:%d +\n", __func__, __LINE__);

    ret = dpp_reg_wait_status_run(id);

    return ret;
}

int dpp_reg_wait_done(void)
{
    uint32_t id = SECURE_IDMA_DPP;
    dbgPrintf("%s:%d +\n", __func__, __LINE__);

    return dpp_reg_wait_status_idle(id);
}

void dpp_reg_deinit(void)
{
    uint32_t id = SECURE_IDMA_DPP;
    dbgPrintf("%s:%d +\n", __func__, __LINE__);

    dpp_reg_wait_status_idle(id);

    /* set non-secure mode for DMA/DPP */
    dma_reg_set_secure_en(id, 0);
    /* IDMA Setting */
    idma_reg_clear_irq(id, IDMA_ALL_IRQ_CLEAR);
    idma_reg_set_irq_mask_all(id, 1);
}
