// SPDX-License-Identifier: GPL-2.0
/******************************************************************************
 *
 * Copyright(C) 2024 MediaTek Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See http://www.gnu.org/licenses/gpl-2.0.html for more details.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *****************************************************************************/


#include <mali_kbase.h>
#include "mali_kbase_platform_mtk_dtv.h"
#include "mali_kbase_platform_mtk_dtv_mt5867_reg.h"
#include <mtk_dtv_gpu_bridge.h>
#include <linux/delay.h>

#ifndef _KBASE_PLATFORM_MTK_DTV_MT5867_MFDEC_H_
#define _KBASE_PLATFORM_MTK_DTV_MT5867_MFDEC_H_

#define MAX_MFDEC_SLOT_NUM 16
#define INVALID_MFDEC_SLOT 0xFF

#define FB_TYPE_L_BASE   0
#define FB_TYPE_C_BASE   1
#define FB_TYPE_PITCH    2
#define FB_TYPE_PROP     3

bridge_device mfdec_bridge;
const u8 max_mfdec_slot_num = MAX_MFDEC_SLOT_NUM;

#if defined(CONFIG_MALI_DEBUG)
#define mfdec_printk printk
#else
#define mfdec_printk(x...)
#endif

void init_mfdec_reg(void)
{
    mfdec_printk("[MFDec] %s()\n", __func__);

    SET_REG(DISP_MFDEC0, mfdec_detile_rst, 1);
    udelay(1);
    SET_REG(DISP_MFDEC0, mfdec_detile_rst, 0);

    /* set mfdec read request length to 64 */
    SET_REG(GPU, mfdec_rd_len, 0x0);
    /* enable divider, no need to set pitch as power of 2 */
    SET_REG(GPU, mfdec_divider_en, 1);
    udelay(1);

    /* enable mfdec */
    SET_REG(GPU, mfdec_off, 0);
    udelay(1);
}

void init_mfdec(struct kbase_device* kbdev)
{
    init_bridge_device(kbdev, &mfdec_bridge);
    init_mfdec_reg();
}

int get_mfdec_slot(struct kbase_device* kbdev, u8 *id)
{
    *id = acquire_slot(kbdev, &mfdec_bridge, MAX_MFDEC_SLOT_NUM);
    if (*id == INVALID_MFDEC_SLOT)
        return -1;
    else
        return 0;
}

int set_mfdec_bridge(struct kbase_device* kbdev, u8 id, u64 base, u32 height, u32 pitch)
{
    u32 size_y, size_uv;
    u64 base_uv;
    u16 valid_id = 0x0;

    mfdec_printk("[MFDec] %s() id=%d, base=0x%llx, height=%d, pitch=%d\n", __func__, id, base, height, pitch);

    /* Make sure soft_reset is pulled down when setting bridge registers.
     * The internal register cannot be updated if soft_reset is pulled up.
     */
    if (GET_REG(GPU, soft_reset) == 0x1) {
        power_on(kbdev);
#if defined(CONFIG_MALI_DEBUG)
        printk("[MFDec] soft_reset=1, pull down GPU reset.");
#endif
    }

    SET_REG(GPU, mfdec_adr_idx, (id & 0xF));

    /* [pitch] ex: 4K, 3840 or 4096(2^n)
      * divider mode : 16 bytes alignment  (Bank: 0x2ee9 Reg: 42, bit[14] 1: use divider)
      * non-divider mode : 2 ^ n bytes alignment
      */
    SET_REG(GPU, video_pitch, pitch);

#define BRIDGE_SIZE_MASK       0xFFF
#define BRIDGE_SIZE_SHIFT      12
#define BRIDGE_ADDR_MASK       0xFFFF
#define BRIDGE_ADDR_LOW_SHIFT  12
#define BRIDGE_ADDR_HIGH_SHIFT 28

#define Y_SIZE(pitch, height)  (pitch * height)
#define UV_SIZE(pitch, height) (pitch * height / 2)

    /* Bifrost MMU table is bus address */
    size_y = ((Y_SIZE(pitch, height) + BRIDGE_SIZE_MASK) >> BRIDGE_SIZE_SHIFT) << BRIDGE_SIZE_SHIFT;
    base_uv = base + size_y;
    /* size of YUV_420SP(NV12) format */
    size_uv = ((UV_SIZE(pitch, height) + BRIDGE_SIZE_MASK) >> BRIDGE_SIZE_SHIFT) << BRIDGE_SIZE_SHIFT;

    /* Set start & end addr of luma & chroma (4K byte unit, bus address) */
    SET_REG(GPU, mfdec_y_start_low,   ((base >> BRIDGE_ADDR_LOW_SHIFT) & BRIDGE_ADDR_MASK));
    SET_REG(GPU, mfdec_y_start_high,  ((base >> BRIDGE_ADDR_HIGH_SHIFT) & BRIDGE_ADDR_MASK));
    SET_REG(GPU, mfdec_y_end_low,     (((base + size_y) >> BRIDGE_ADDR_LOW_SHIFT) & BRIDGE_ADDR_MASK));
    SET_REG(GPU, mfdec_y_end_high,    (((base + size_y) >> BRIDGE_ADDR_HIGH_SHIFT) & BRIDGE_ADDR_MASK));
    SET_REG(GPU, mfdec_uv_start_low,  ((base_uv >> BRIDGE_ADDR_LOW_SHIFT) & BRIDGE_ADDR_MASK));
    SET_REG(GPU, mfdec_uv_start_high, ((base_uv >> BRIDGE_ADDR_HIGH_SHIFT) & BRIDGE_ADDR_MASK));
    SET_REG(GPU, mfdec_uv_end_low,    (((base_uv + size_uv) >> BRIDGE_ADDR_LOW_SHIFT) & BRIDGE_ADDR_MASK));
    SET_REG(GPU, mfdec_uv_end_high,   (((base_uv + size_uv) >> BRIDGE_ADDR_HIGH_SHIFT) & BRIDGE_ADDR_MASK));

    /* Update & clean */
    SET_REG(GPU, mfdec_adr_update,    0x1);
    SET_REG(GPU, mfdec_adr_update,    0x0);

    /* Set valid vector, do not clear any valid bit while GPU is working */
    valid_id = GET_REG(GPU, mfdec_valid);
    valid_id |= (0x1 << id);
    SET_REG_NO_CLEAR(GPU, mfdec_valid,         (valid_id & BRIDGE_ADDR_MASK));

#if defined(CONFIG_MALI_DEBUG)
    dump_slot_status(kbdev);
#endif

    return 0;
}

int set_mfdec_detile(struct kbase_device* kbdev, u8 id, u64 video_ybase, u64 video_uvbase, u32 pitch, u32 tile_mode, u32 flags)
{
    mfdec_printk("[MFDec] %s() id=%d, video_ybase=0x%llx, video_uvbase=0x%llx, pitch=%d, tile=%d, flags=0x%x\n",
                    __func__, id, video_ybase, video_uvbase, pitch, tile_mode, flags);

#define MFDEC_ADDR_HIGH_SHIFT    16
#define MFDEC_PROP_IOMMU_TRIM_4G 32
#define MFDEC_PROP_IOMMU_SHIFT   2
#define MFDEC_PROP_IOMMU_MASK    0xF
#define MFDEC_PROP_IOMMU_UV_OFFSET_SHIFT 8
#define MFDEC_PROP_IOMMU_Y_OFFSET_SHIFT  4
#define MFDEC_PROP_FLAG_OFFSET_SHIFT     16

    /* Set frame buffer index */
    SET_REG(DISP_MFDEC0, prg_fb_idx, id);

    /* Set frame buffer luma base */
    SET_REG(DISP_MFDEC0, prg_fb_type, FB_TYPE_L_BASE);
    SET_REG(DISP_MFDEC0, fb_para_data_low, video_ybase);
    SET_REG(DISP_MFDEC0, fb_para_data_high, (video_ybase >> MFDEC_ADDR_HIGH_SHIFT));
    udelay(1);

    /* Set frame buffer chroma base */
    SET_REG(DISP_MFDEC0, prg_fb_type, FB_TYPE_C_BASE);
    SET_REG(DISP_MFDEC0, fb_para_data_low, video_uvbase);
    SET_REG(DISP_MFDEC0, fb_para_data_high, (video_uvbase >> MFDEC_ADDR_HIGH_SHIFT));
    udelay(1);

    /* Set frame buffer pitch */
    SET_REG(DISP_MFDEC0, prg_fb_type, FB_TYPE_PITCH);
    SET_REG(DISP_MFDEC0, fb_para_data_low, pitch);
    SET_REG(DISP_MFDEC0, fb_para_data_high, 0x0);
    udelay(1);

    /* Set frame buffer property */
    SET_REG(DISP_MFDEC0, prg_fb_type, FB_TYPE_PROP);
    {
        u8 offset = 0x0;
        u32 property = 0x0;

        /* For IOMMU */
        offset = (((video_uvbase) >> MFDEC_PROP_IOMMU_TRIM_4G) << MFDEC_PROP_IOMMU_SHIFT) & MFDEC_PROP_IOMMU_MASK;
        property += offset << MFDEC_PROP_IOMMU_UV_OFFSET_SHIFT;
        offset = (((video_ybase) >> MFDEC_PROP_IOMMU_TRIM_4G) << MFDEC_PROP_IOMMU_SHIFT) & MFDEC_PROP_IOMMU_MASK;
        property += offset << MFDEC_PROP_IOMMU_Y_OFFSET_SHIFT;
        property += tile_mode;
        SET_REG(DISP_MFDEC0, fb_para_data_low, property);

        property = 0x0;
        if (flags != 0)
        {
            property = property | (flags >> MFDEC_PROP_FLAG_OFFSET_SHIFT);
        }
        SET_REG(DISP_MFDEC0, fb_para_data_high, property);
    }

    return 0;
}

int set_mfdec_decode(struct kbase_device* kbdev, u8 id, u64 video_ybase, u64 video_uvbase, u64 BLDbase, u64 BLDuvbase, u32 pitch, u32 codec_type, u32 flags)
{
    pr_alert("Does not support mfdec decode! Detile only.\n");
    return -1;
}

void reset_mfdec_slot_reg(struct kbase_device* kbdev, u8 id)
{
    mfdec_printk("[MFDec] %s() id=%d\n", __func__ , id);

    if (id >= MAX_MFDEC_SLOT_NUM)
        return;

    /* Set invalid vector */
    CLEAR_REG_VAL(GPU, mfdec_valid, (0x1 << id));
}

void release_mfdec_slot(struct kbase_device* kbdev, u8 id)
{
    release_slot(kbdev, &mfdec_bridge, id);
}

void print_debug_status(struct kbase_device* kbdev)
{
    printk(KERN_DEBUG " [Mali-DEBUG] mfdec_off      = 0x%x\n", GET_REG(GPU, mfdec_off));
    printk(KERN_DEBUG " [Mali-DEBUG] mfdec_used_id  = 0x%x\n", mfdec_used_id);

    /* Check the MFDEC bank status */
    printk(KERN_DEBUG " [Mali-DEBUG][MFDEC] INDEX           = 0x%x\n", GET_REG(DISP_MFDEC0, prg_fb_idx));

    SET_REG(DISP_MFDEC0, prg_fb_type, FB_TYPE_L_BASE);
    printk(KERN_DEBUG " [Mali-DEBUG][MFDEC] Y_BASE  = 0x%04x %04x\n", GET_REG(DISP_MFDEC0, fb_para_data_high), GET_REG(DISP_MFDEC0, fb_para_data_low));

    SET_REG(DISP_MFDEC0, prg_fb_type, FB_TYPE_C_BASE);
    printk(KERN_DEBUG " [Mali-DEBUG][MFDEC] C_BASE  = 0x%04x %04x\n", GET_REG(DISP_MFDEC0, fb_para_data_high), GET_REG(DISP_MFDEC0, fb_para_data_low));

    SET_REG(DISP_MFDEC0, prg_fb_type, FB_TYPE_PITCH);
    printk(KERN_DEBUG " [Mali-DEBUG][MFDEC] PITCH   = 0x%x %x\n", GET_REG(DISP_MFDEC0, fb_para_data_high), GET_REG(DISP_MFDEC0, fb_para_data_low));

    SET_REG(DISP_MFDEC0, prg_fb_type, FB_TYPE_PROP);
    printk(KERN_DEBUG " [Mali-DEBUG][MFDEC] PROP    = 0x%x %x\n", GET_REG(DISP_MFDEC0, fb_para_data_high), GET_REG(DISP_MFDEC0, fb_para_data_low));


    printk(KERN_DEBUG " [Mali-DEBUG][BRIDGE] ID       = 0x%x\n", GET_REG(GPU, mfdec_adr_idx));

    printk(KERN_DEBUG " [Mali-DEBUG][BRIDGE] PITCH    = 0x%x\n", GET_REG(GPU, video_pitch));

    printk(KERN_DEBUG " [Mali-DEBUG][BRIDGE] Y_START  = 0x%04x %04x\n", GET_REG(GPU, mfdec_y_start_high), GET_REG(GPU, mfdec_y_start_low));
    printk(KERN_DEBUG " [Mali-DEBUG][BRIDGE] Y_END    = 0x%04x %04x\n", GET_REG(GPU, mfdec_y_end_high), GET_REG(GPU, mfdec_y_end_low));
    printk(KERN_DEBUG " [Mali-DEBUG][BRIDGE] UV_START = 0x%04x %04x\n", GET_REG(GPU, mfdec_uv_start_high), GET_REG(GPU, mfdec_uv_start_low));
    printk(KERN_DEBUG " [Mali-DEBUG][BRIDGE] UV_END   = 0x%04x %04x\n", GET_REG(GPU, mfdec_uv_end_high), GET_REG(GPU, mfdec_uv_end_low));

    printk(KERN_DEBUG " [Mali-DEBUG][BRIDGE] VALID    = 0x%x\n", GET_REG(GPU, mfdec_valid));
}

#endif /* _KBASE_PLATFORM_MTK_DTV_MT5867_MFDEC_H_ */
