blob: 3a43991486368f434b695cd856683cae251b25c5 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* drivers/amlogic/media/di_multi/dolby_sys.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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 the GNU General Public License for
* more details.
*
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/semaphore.h>
#include <linux/workqueue.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/major.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/cdev.h>
#include <linux/proc_fs.h>
#include <linux/list.h>
#include <linux/of_reserved_mem.h>
#include <linux/of_irq.h>
#include <linux/uaccess.h>
#include <linux/of_fdt.h>
#include <linux/cma.h>
#include <linux/dma-contiguous.h>
#include <linux/ctype.h>
#include <linux/string.h>
#include "dolby_sys.h"
#include "register.h"
#include <linux/amlogic/media/vfm/vframe.h>
#include <linux/amlogic/media/amdolbyvision/dolby_vision.h>
#define OP_BYPASS_CVM 0x01
#define OP_BYPASS_CSC 0x02
#define OP_CLKGATE_WHEN_LOAD_LUT 0x04
#define DI_FLAG_CHANGE_TC 0x000010
#define DIFLAG_FORCE_CVM 0x01
#define DIFLAG_BYPASS_CVM 0x02
#define DIFLAG_BYPASS_VPP 0x04
#define DIFLAG_USE_SINK_MIN_MAX 0x08
#define DIFLAG_CLKGATE_WHEN_LOAD_LUT 0x10
#define DIFLAG_BYPASS_CSC 0x200
struct di_dolby_hw_s {
/* SWAP_CTRL2 */
u32 vsize;
u32 hsize;
/* SWAP_CTRL1 */
u32 vtotal;
u32 htotal;
/* SWAP_CTRL3 */
u32 vwidth;
u32 hwidth;
/* SWAP_CTRL4 */
u32 vpotch;
u32 hpotch;
/* SWAP_CTRL0 */
bool bl_enable;
bool el_enable;
bool el_41_mode;
u8 bl_lay_line_delay;
u8 el_lay_line_delay;
bool roundup_output; //add 0.5 for 8bit mode
/* SWAP_CTRL5 */
u8 bl_uv_mode;
u8 el_uv_mode;
u32 dm_count;
u32 comp_count;
u32 lut_count;
struct composer_reg_ipcore *comp_reg;
struct dm_reg_ipcore1 *dm_reg;
struct dm_lut_ipcore *dm_lut;
struct composer_reg_ipcore *last_comp_reg;
struct dm_reg_ipcore1 *last_dm_reg;
struct dm_lut_ipcore *last_dm_lut;
};
enum disignal_format_e {
FORMAT_INVALID = -1,
FORMAT_DOVI = 0,
FORMAT_HDR10 = 1,
FORMAT_SDR = 2,
FORMAT_DOVI_LL = 3,
FORMAT_HLG = 4,
FORMAT_HDR10PLUS = 5,
FORMAT_SDR_2020 = 6,
FORMAT_MVC = 7,
FORMAT_CUVA = 8
};
struct di_dolby_dev_s {
enum disignal_format_e src_fmt;
bool bypass_csc;
bool bypass_cvm;
bool lut_endian;
u8 next_tbl_id;
u8 cur_tbl_id;
//struct mutex di_mutex;
u32 update_flag[2];
struct composer_reg_ipcore comp_reg[2];
struct dm_reg_ipcore1 dm_reg[2];
struct dm_lut_ipcore dm_lut[2];
struct di_dolby_hw_s hw;
};
static struct di_dolby_dev_s *di_dolby;
void di_dolby_enable(bool enable)
{
if (enable) {
DIM_DI_WR_REG_BITS(DI_SC_TOP_CTRL, 1, 24, 1);//dolby enable
DIM_DI_WR_REG_BITS(DI_SC_TOP_CTRL, 1, 19, 1);//dither enable
} else {
DIM_DI_WR_REG_BITS(DI_SC_TOP_CTRL, 0, 24, 1);//dolby enable
DIM_DI_WR_REG_BITS(DI_SC_TOP_CTRL, 0, 19, 1);//dither enable
}
}
static int di_dolby_core_set(struct di_dolby_hw_s *hw,
bool lut_endian,
u32 op_flag,
u32 update_flag,
bool reset)
{
u32 count;
u32 bypass_flag = 0;
int i;
bool set_lut = false;
u32 dm_count;
u32 comp_count;
u32 lut_count;
u32 *dm_regs;
u32 *comp_regs;
u32 *lut_regs;
u32 *last_dm;
u32 *last_comp;
if (!hw)
return -1;
dm_count = hw->dm_count;
comp_count = hw->comp_count;
lut_count = hw->lut_count;
dm_regs = (uint32_t *)hw->dm_reg;
comp_regs = (uint32_t *)hw->comp_reg;
lut_regs = (uint32_t *)hw->dm_lut;
last_dm = (uint32_t *)hw->last_dm_reg;
last_comp = (uint32_t *)hw->last_comp_reg;
//reset = true;
if (update_flag & DI_FLAG_CHANGE_TC)
set_lut = true;
DIM_DI_WR(DOLBY_CORE1C_CLKGATE_CTRL, 0);
DIM_DI_WR(DOLBY_CORE1C_SWAP_CTRL1,
((hw->hsize + hw->htotal) << 16) | (hw->vsize + hw->vtotal));
DIM_DI_WR(DOLBY_CORE1C_SWAP_CTRL3,
(hw->hwidth << 16) | hw->vwidth);
DIM_DI_WR(DOLBY_CORE1C_SWAP_CTRL4,
(hw->hpotch << 16) | hw->vpotch);
DIM_DI_WR(DOLBY_CORE1C_SWAP_CTRL2,
(hw->hsize << 16) | hw->vsize);
DIM_DI_WR(DOLBY_CORE1C_SWAP_CTRL5,
((hw->el_uv_mode & 3) << 2) | ((hw->bl_uv_mode & 3) << 0));
DIM_DI_WR(DOLBY_CORE1C_DMA_CTRL, 0x0);
DIM_DI_WR(DOLBY_CORE1C_REG_START + 4, 4);
DIM_DI_WR(DOLBY_CORE1C_REG_START + 2, 1);
if (!hw->el_enable)
bypass_flag |= 1 << 3;
if (op_flag & DIFLAG_BYPASS_CSC)
bypass_flag |= 1 << 1;
if (op_flag & DIFLAG_BYPASS_CVM)
bypass_flag |= 1 << 2;
if (hw->el_41_mode)
bypass_flag |= 1 << 3;
DIM_DI_WR(DOLBY_CORE1C_REG_START + 1,
0x70 | bypass_flag); // bypass CVM and/or CSC
DIM_DI_WR(DOLBY_CORE1C_REG_START + 1,
0x70 | bypass_flag); // for delay
if (dm_count == 0)
count = 24;
else
count = dm_count;
for (i = 0; i < count; i++)
if (reset ||
dm_regs[i] != last_dm[i]) {
DIM_DI_WR(DOLBY_CORE1C_REG_START + 6 + i,
dm_regs[i]);
}
if (comp_count == 0)
count = 173;
else
count = comp_count;
for (i = 0; i < count; i++)
if (reset ||
comp_regs[i] != last_comp[i]) {
DIM_DI_WR(DOLBY_CORE1C_REG_START + 6 + 44 + i,
comp_regs[i]);
}
/* metadata program done */
DIM_DI_WR(DOLBY_CORE1C_REG_START + 3, 1);
if (lut_count == 0)
count = 256 * 5;
else
count = lut_count;
if (count && (set_lut || reset)) {
if (op_flag & DIFLAG_CLKGATE_WHEN_LOAD_LUT) {
DIM_DI_WR_REG_BITS(DOLBY_CORE1C_CLKGATE_CTRL,
2, 2, 2);
}
DIM_DI_WR(DOLBY_CORE1C_DMA_CTRL, 0x1401);
if (lut_endian) {
for (i = 0; i < count; i += 4) {
DIM_DI_WR(DOLBY_CORE1C_DMA_PORT,
lut_regs[i + 3]);
DIM_DI_WR(DOLBY_CORE1C_DMA_PORT,
lut_regs[i + 2]);
DIM_DI_WR(DOLBY_CORE1C_DMA_PORT,
lut_regs[i + 1]);
DIM_DI_WR(DOLBY_CORE1C_DMA_PORT,
lut_regs[i]);
}
} else {
for (i = 0; i < count; i++)
DIM_DI_WR(DOLBY_CORE1C_DMA_PORT,
lut_regs[i]);
}
if (op_flag & DIFLAG_CLKGATE_WHEN_LOAD_LUT) {
DIM_DI_WR_REG_BITS(DOLBY_CORE1C_CLKGATE_CTRL,
0, 2, 2);
}
}
DIM_DI_WR(DI_HDR_IN_HSIZE, hw->hsize);
DIM_DI_WR(DI_HDR_IN_VSIZE, hw->vsize);
DIM_DI_WR(DOLBY_CORE1C_SWAP_CTRL0,
((hw->roundup_output ? 1 : 0) << 12) |
((hw->bl_lay_line_delay & 0xf) << 8) |
((hw->el_lay_line_delay & 0xf) << 4) |
((hw->el_41_mode ? 1 : 0) << 2) |
((hw->el_enable ? 1 : 0) << 1) |
((hw->bl_enable ? 1 : 0) << 0));
return 0;
}
int di_dolby_do_setting(void /*struct di_dolby_dev_s *dev*/)
{
u32 op_flag = 0;
u32 update_flag = 0;
int tmp;
struct di_dolby_dev_s *dev = di_dolby;
if (!dev)
return -1;
//mutex_lock(&dev->di_mutex);
if (dev->cur_tbl_id == 0xff) {
//mutex_unlock(&dev->di_mutex);
return -2;
}
if (dev->bypass_csc)
op_flag |= DIFLAG_BYPASS_CSC;
if (dev->bypass_cvm)
op_flag |= DIFLAG_BYPASS_CVM;
dev->hw.dm_reg = &dev->dm_reg[dev->cur_tbl_id];
dev->hw.comp_reg = &dev->comp_reg[dev->cur_tbl_id];
dev->hw.dm_lut = &dev->dm_lut[dev->cur_tbl_id];
update_flag = dev->update_flag[dev->cur_tbl_id];
//mutex_unlock(&dev->di_mutex);
tmp = di_dolby_core_set(&dev->hw, dev->lut_endian,
op_flag, update_flag, true);
if (tmp)
return -3;
dev->hw.last_dm_reg = dev->hw.dm_reg;
dev->hw.last_comp_reg = dev->hw.comp_reg;
dev->hw.last_dm_lut = dev->hw.dm_lut;
return 0;
}
int di_dolby_update_setting(struct dm_reg_ipcore1 *dm_reg,
struct composer_reg_ipcore *comp_reg,
struct dm_lut_ipcore *dm_lut,
u32 dm_count,
u32 comp_count,
u32 lut_count,
u32 update_flag,
u32 hsize,
u32 vsize)
{
struct dm_reg_ipcore1 *new_dm_reg;
struct composer_reg_ipcore *new_comp_reg;
struct dm_lut_ipcore *new_dm_lut;
if (!dm_reg || !comp_reg || !dm_lut || !dm_count || !comp_count ||
!lut_count)
return -1;
di_dolby->hw.hsize = hsize;
di_dolby->hw.vsize = vsize;
//mutex_lock(&di_dolby->di_mutex);
if (di_dolby->next_tbl_id == 0xff)
di_dolby->next_tbl_id = di_dolby->cur_tbl_id ^ 1;
new_dm_reg = &di_dolby->dm_reg[di_dolby->next_tbl_id];
new_comp_reg = &di_dolby->comp_reg[di_dolby->next_tbl_id];
new_dm_lut = &di_dolby->dm_lut[di_dolby->next_tbl_id];
di_dolby->update_flag[di_dolby->next_tbl_id] = update_flag;
di_dolby->cur_tbl_id = di_dolby->next_tbl_id;
di_dolby->next_tbl_id = di_dolby->next_tbl_id ^ 1;
if (dm_count > di_dolby->hw.dm_count)
dm_count = di_dolby->hw.dm_count;
if (comp_count > di_dolby->hw.comp_count)
comp_count = di_dolby->hw.comp_count;
if (lut_count > di_dolby->hw.lut_count)
lut_count = di_dolby->hw.lut_count;
memcpy(new_dm_reg, dm_reg, sizeof(uint32_t) * dm_count);
memcpy(new_comp_reg, comp_reg, sizeof(uint32_t) * comp_count);
memcpy(new_dm_lut, dm_lut, sizeof(uint32_t) * lut_count);
//mutex_unlock(&di_dolby->di_mutex);
return 0;
}
void di_dolby_sw_init(void)
{
di_dolby = vmalloc(sizeof(*di_dolby));
if (!di_dolby) {
//pr_info("di_dolby_sw_init fail\n");
return;
}
memset(di_dolby, 0, sizeof(struct di_dolby_dev_s));
//mutex_init(&di_dolby->di_mutex);
di_dolby->hw.hsize = 1920;
di_dolby->hw.vsize = 1080;
di_dolby->hw.vtotal = 0x60; //0x40
di_dolby->hw.htotal = 0x160; //0x80
di_dolby->hw.vwidth = 0x8;
di_dolby->hw.hwidth = 0x8;
di_dolby->hw.vpotch = 0x4; //0x10
di_dolby->hw.hpotch = 0x8;
di_dolby->hw.bl_enable = true;
di_dolby->hw.el_enable = false;
di_dolby->hw.el_41_mode = true;
di_dolby->hw.bl_lay_line_delay = 0;
di_dolby->hw.el_lay_line_delay = 0;
di_dolby->hw.roundup_output = true;
di_dolby->hw.bl_uv_mode = 2;
di_dolby->hw.el_uv_mode = 1;
di_dolby->hw.dm_count = 27;
di_dolby->hw.comp_count = 173;
di_dolby->hw.lut_count = 256 * 5;
di_dolby->bypass_cvm = false;
di_dolby->bypass_csc = false;
di_dolby->src_fmt = FORMAT_INVALID;
di_dolby->lut_endian = true;
di_dolby->cur_tbl_id = 1;
di_dolby->next_tbl_id = 0xff;
}