blob: 5247bdc41609ee929ae15028415a883a02bb84dc [file] [log] [blame]
/*
* drivers/amlogic/media/enhancement/amvecm/amcm.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 <mach/am_regs.h> */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/amlogic/media/amvecm/cm.h>
/* #include <linux/amlogic/aml_common.h> */
#include <linux/amlogic/media/vfm/vframe.h>
#include <linux/amlogic/media/amvecm/amvecm.h>
#include <linux/uaccess.h>
#include "arch/vpp_regs.h"
#include "arch/cm_regs.h"
#include "amcm.h"
#include "amcm_regmap.h"
#include <linux/amlogic/media/amdolbyvision/dolby_vision.h>
#include "amcsc.h"
#define pr_amcm_dbg(fmt, args...)\
do {\
if (debug_amcm)\
pr_info("AMCM: " fmt, ## args);\
} while (0)\
static bool debug_amcm;
module_param(debug_amcm, bool, 0664);
MODULE_PARM_DESC(debug_amcm, "\n debug_amcm\n");
static bool debug_regload;
module_param(debug_regload, bool, 0664);
MODULE_PARM_DESC(debug_regload, "\n debug_regload\n");
static int cm_level = 1;/* 0:optimize;1:enhancement */
module_param(cm_level, int, 0664);
MODULE_PARM_DESC(cm_level, "\n selcet cm lever\n");
int cm_en;/* 0:disabel;1:enable */
module_param(cm_en, int, 0664);
MODULE_PARM_DESC(cm_en, "\n enable or disable cm\n");
static unsigned int cm_width_limit = 50;/* vlsi adjust */
module_param(cm_width_limit, uint, 0664);
MODULE_PARM_DESC(cm_width_limit, "\n cm_width_limit\n");
int pq_reg_wr_rdma = 1;/* 0:disabel;1:enable */
module_param(pq_reg_wr_rdma, int, 0664);
MODULE_PARM_DESC(pq_reg_wr_rdma, "\n pq_reg_wr_rdma\n");
#if 0
struct cm_region_s cm_region;
struct cm_top_s cm_top;
struct cm_demo_s cm_demo;
#endif
static int cm_level_last = 0xff;/* 0:optimize;1:enhancement */
unsigned int cm2_patch_flag;
unsigned int cm_size;
/* cm enable flag internal for cm size issue--0:disable;1:enable */
static bool cm_en_flag;
/* cm disable flag sync with pq-db--1:disable;0:enable */
static bool cm_dis_flag;
static struct am_regs_s amregs0;
static struct am_regs_s amregs1;
static struct am_regs_s amregs2;
static struct am_regs_s amregs3;
static struct am_regs_s amregs4;
static struct am_regs_s amregs5;
static struct sr1_regs_s sr1_regs[101];
/* extern unsigned int vecm_latch_flag; */
void am_set_regmap(struct am_regs_s *p)
{
unsigned short i;
unsigned int temp = 0;
unsigned short sr1_temp = 0;
for (i = 0; i < p->length; i++) {
switch (p->am_reg[i].type) {
case REG_TYPE_PHY:
break;
case REG_TYPE_CBUS:
if (p->am_reg[i].mask == 0xffffffff)
/* WRITE_CBUS_REG(p->am_reg[i].addr,*/
/* p->am_reg[i].val); */
aml_write_cbus(p->am_reg[i].addr,
p->am_reg[i].val);
else
/* WRITE_CBUS_REG(p->am_reg[i].addr, */
/* (READ_CBUS_REG(p->am_reg[i].addr) & */
/* (~(p->am_reg[i].mask))) | */
/* (p->am_reg[i].val & p->am_reg[i].mask)); */
aml_write_cbus(p->am_reg[i].addr,
(aml_read_cbus(p->am_reg[i].addr) &
(~(p->am_reg[i].mask))) |
(p->am_reg[i].val & p->am_reg[i].mask));
break;
case REG_TYPE_APB:
/* if (p->am_reg[i].mask == 0xffffffff) */
/* WRITE_APB_REG(p->am_reg[i].addr,*/
/* p->am_reg[i].val); */
/* else */
/* WRITE_APB_REG(p->am_reg[i].addr, */
/* (READ_APB_REG(p->am_reg[i].addr) & */
/* (~(p->am_reg[i].mask))) | */
/* (p->am_reg[i].val & p->am_reg[i].mask)); */
break;
case REG_TYPE_MPEG:
/* if (p->am_reg[i].mask == 0xffffffff) */
/* WRITE_MPEG_REG(p->am_reg[i].addr,*/
/* p->am_reg[i].val); */
/* else */
/* WRITE_MPEG_REG(p->am_reg[i].addr, */
/* (READ_MPEG_REG(p->am_reg[i].addr) & */
/* (~(p->am_reg[i].mask))) | */
/* (p->am_reg[i].val & p->am_reg[i].mask)); */
break;
case REG_TYPE_AXI:
/* if (p->am_reg[i].mask == 0xffffffff) */
/* WRITE_AXI_REG(p->am_reg[i].addr,*/
/* p->am_reg[i].val); */
/* else */
/* WRITE_AXI_REG(p->am_reg[i].addr, */
/* (READ_AXI_REG(p->am_reg[i].addr) & */
/* (~(p->am_reg[i].mask))) | */
/* (p->am_reg[i].val & p->am_reg[i].mask)); */
break;
case REG_TYPE_INDEX_VPPCHROMA:
/* add for vm2 demo frame size setting */
if (p->am_reg[i].addr == 0x20f) {
if ((p->am_reg[i].val & 0xff) != 0) {
cm2_patch_flag = p->am_reg[i].val;
p->am_reg[i].val =
p->am_reg[i].val & 0xffffff00;
} else
cm2_patch_flag = 0;
}
/* add for cm patch size config */
if ((p->am_reg[i].addr == 0x205) ||
(p->am_reg[i].addr == 0x209) ||
(p->am_reg[i].addr == 0x20a)) {
pr_amcm_dbg("[amcm]:%s REG_TYPE_INDEX_VPPCHROMA addr:0x%x",
__func__, p->am_reg[i].addr);
break;
}
if (!cm_en) {
if (p->am_reg[i].addr == 0x208) {
if (get_cpu_type() ==
MESON_CPU_MAJOR_ID_TL1)
p->am_reg[i].val =
p->am_reg[i].val & 0xfffffffe;
else
p->am_reg[i].val =
p->am_reg[i].val & 0xfffffffd;
}
pr_amcm_dbg("[amcm]:%s REG_TYPE_INDEX_VPPCHROMA addr:0x%x",
__func__, p->am_reg[i].addr);
} else if (p->am_reg[i].addr == 0x208) {
if (p->am_reg[i].val & 0x2)
cm_dis_flag = false;
else
cm_dis_flag = true;
}
WRITE_VPP_REG(VPP_CHROMA_ADDR_PORT,
p->am_reg[i].addr);
if (p->am_reg[i].mask == 0xffffffff)
WRITE_VPP_REG(VPP_CHROMA_DATA_PORT,
p->am_reg[i].val);
else {
temp = READ_VPP_REG(VPP_CHROMA_DATA_PORT);
WRITE_VPP_REG(VPP_CHROMA_ADDR_PORT,
p->am_reg[i].addr);
WRITE_VPP_REG(VPP_CHROMA_DATA_PORT,
(temp & (~(p->am_reg[i].mask))) |
(p->am_reg[i].val & p->am_reg[i].mask));
}
break;
case REG_TYPE_INDEX_GAMMA:
break;
case VALUE_TYPE_CONTRAST_BRIGHTNESS:
break;
case REG_TYPE_INDEX_VPP_COEF:
if (((p->am_reg[i].addr&0xf) == 0) ||
((p->am_reg[i].addr&0xf) == 0x8)) {
WRITE_VPP_REG(VPP_CHROMA_ADDR_PORT,
p->am_reg[i].addr);
WRITE_VPP_REG(VPP_CHROMA_DATA_PORT,
p->am_reg[i].val);
} else
WRITE_VPP_REG(VPP_CHROMA_DATA_PORT,
p->am_reg[i].val);
break;
/* #if (MESON_CPU_TYPE >= MESON_CPU_TYPE_MESONG9TV) */
case REG_TYPE_VCBUS:
if (p->am_reg[i].mask == 0xffffffff) {
/* WRITE_VCBUS_REG(p->am_reg[i].addr,*/
/* p->am_reg[i].val); */
if (pq_reg_wr_rdma)
VSYNC_WR_MPEG_REG(p->am_reg[i].addr,
p->am_reg[i].val);
else
aml_write_vcbus(p->am_reg[i].addr,
p->am_reg[i].val);
} else
/* WRITE_VCBUS_REG(p->am_reg[i].addr, */
/* (READ_VCBUS_REG(p->am_reg[i].addr) & */
/* (~(p->am_reg[i].mask))) | */
/* (p->am_reg[i].val & p->am_reg[i].mask)); */
if ((is_meson_gxtvbb_cpu()) &&
(p->am_reg[i].addr >= 0x3280)
&& (p->am_reg[i].addr <= 0x32e4)) {
if (p->am_reg[i].addr == 0x32d7)
break;
sr1_temp = p->am_reg[i].addr - 0x3280;
sr1_regs[sr1_temp].addr =
p->am_reg[i].addr;
sr1_regs[sr1_temp].mask =
p->am_reg[i].mask;
sr1_regs[sr1_temp].val =
(sr1_regs[sr1_temp].val &
(~(p->am_reg[i].mask))) |
(p->am_reg[i].val & p->am_reg[i].mask);
sr1_reg_val[sr1_temp] =
sr1_regs[sr1_temp].val;
aml_write_vcbus(p->am_reg[i].addr,
sr1_regs[sr1_temp].val);
} else {
if (p->am_reg[i].addr == 0x1d26)
break;
if (pq_reg_wr_rdma)
VSYNC_WR_MPEG_REG(p->am_reg[i].addr,
(aml_read_vcbus(p->am_reg[i].addr) &
(~(p->am_reg[i].mask))) |
(p->am_reg[i].val & p->am_reg[i].mask));
else
aml_write_vcbus(p->am_reg[i].addr,
(aml_read_vcbus(p->am_reg[i].addr) &
(~(p->am_reg[i].mask))) |
(p->am_reg[i].val & p->am_reg[i].mask));
}
break;
/* #endif */
default:
break;
}
}
return;
}
void amcm_disable(void)
{
int temp;
WRITE_VPP_REG(VPP_CHROMA_ADDR_PORT, 0x208);
temp = READ_VPP_REG(VPP_CHROMA_DATA_PORT);
if (get_cpu_type() == MESON_CPU_MAJOR_ID_TL1) {
if (temp & 0x1) {
WRITE_VPP_REG(VPP_CHROMA_ADDR_PORT,
0x208);
WRITE_VPP_REG(VPP_CHROMA_DATA_PORT,
temp & 0xfffffffe);
}
} else {
if (temp & 0x2) {
WRITE_VPP_REG(VPP_CHROMA_ADDR_PORT,
0x208);
WRITE_VPP_REG(VPP_CHROMA_DATA_PORT,
temp & 0xfffffffd);
}
}
cm_en_flag = false;
}
void amcm_enable(void)
{
int temp;
if (!(READ_VPP_REG(VPP_MISC) & (0x1 << 28)))
WRITE_VPP_REG_BITS(VPP_MISC, 1, 28, 1);
WRITE_VPP_REG(VPP_CHROMA_ADDR_PORT, 0x208);
temp = READ_VPP_REG(VPP_CHROMA_DATA_PORT);
if (get_cpu_type() == MESON_CPU_MAJOR_ID_TL1) {
if (!(temp & 0x1)) {
WRITE_VPP_REG(VPP_CHROMA_ADDR_PORT,
0x208);
WRITE_VPP_REG(VPP_CHROMA_DATA_PORT,
temp | 0x1);
}
} else {
if (!(temp & 0x2)) {
WRITE_VPP_REG(VPP_CHROMA_ADDR_PORT,
0x208);
WRITE_VPP_REG(VPP_CHROMA_DATA_PORT,
temp | 0x2);
}
}
cm_en_flag = true;
}
void cm_regmap_latch(struct am_regs_s *am_regs, unsigned int reg_map)
{
am_set_regmap(am_regs);
vecm_latch_flag &= ~reg_map;
pr_amcm_dbg("\n[amcm..] load reg %d table OK!!!\n", reg_map);
}
void amcm_level_sel(unsigned int cm_level)
{
int temp;
if (cm_level == 1)
am_set_regmap(&cmreg_lever1);
else if (cm_level == 2)
am_set_regmap(&cmreg_lever2);
else if (cm_level == 3)
am_set_regmap(&cmreg_lever3);
else if (cm_level == 4)
am_set_regmap(&cmreg_enhancement);
else
am_set_regmap(&cmreg_optimize);
#ifdef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_DOLBYVISION
if (!is_dolby_vision_enable())
#endif
{
if (!(READ_VPP_REG(VPP_MISC) & (0x1 << 28)))
WRITE_VPP_REG_BITS(VPP_MISC, 1, 28, 1);
WRITE_VPP_REG(VPP_CHROMA_ADDR_PORT, 0x208);
temp = READ_VPP_REG(VPP_CHROMA_DATA_PORT);
if (!(temp & 0x2)) {
WRITE_VPP_REG(VPP_CHROMA_ADDR_PORT, 0x208);
WRITE_VPP_REG(VPP_CHROMA_DATA_PORT, temp | 0x2);
}
}
}
void cm2_frame_size_patch(unsigned int width, unsigned int height)
{
unsigned int vpp_size;
if (!cm_en)
return;
else if (width < cm_width_limit)
amcm_disable();
else if ((cm_en_flag != true) && (cm_dis_flag == false))
amcm_enable();
vpp_size = width|(height << 16);
if (cm_size != vpp_size) {
WRITE_VPP_REG(VPP_CHROMA_ADDR_PORT, 0x205);
WRITE_VPP_REG(VPP_CHROMA_DATA_PORT, vpp_size);
WRITE_VPP_REG(VPP_CHROMA_ADDR_PORT, 0x209);
WRITE_VPP_REG(VPP_CHROMA_DATA_PORT, width<<15);
WRITE_VPP_REG(VPP_CHROMA_ADDR_PORT, 0x20a);
WRITE_VPP_REG(VPP_CHROMA_DATA_PORT, height<<16);
cm_size = vpp_size;
pr_amcm_dbg("\n[amcm..]cm2_frame_patch: set cm2 framesize %x, ",
vpp_size);
pr_amcm_dbg("set demo mode %x\n", cm2_patch_flag);
}
}
/* set the frame size for cm2 demo*/
void cm2_frame_switch_patch(void)
{
WRITE_VPP_REG(VPP_CHROMA_ADDR_PORT, 0x20f);
WRITE_VPP_REG(VPP_CHROMA_DATA_PORT, cm2_patch_flag);
}
void cm_latch_process(void)
{
/*if ((vecm_latch_flag & FLAG_REG_MAP0) ||*/
/*(vecm_latch_flag & FLAG_REG_MAP1) ||*/
/*(vecm_latch_flag & FLAG_REG_MAP2) ||*/
/*(vecm_latch_flag & FLAG_REG_MAP3) ||*/
/*(vecm_latch_flag & FLAG_REG_MAP4) ||*/
/* (vecm_latch_flag & FLAG_REG_MAP5)){*/
do {
if (vecm_latch_flag & FLAG_REG_MAP0) {
cm_regmap_latch(&amregs0, FLAG_REG_MAP0);
break;
}
if (vecm_latch_flag & FLAG_REG_MAP1) {
cm_regmap_latch(&amregs1, FLAG_REG_MAP1);
break;
}
if (vecm_latch_flag & FLAG_REG_MAP2) {
cm_regmap_latch(&amregs2, FLAG_REG_MAP2);
break;
}
if (vecm_latch_flag & FLAG_REG_MAP3) {
cm_regmap_latch(&amregs3, FLAG_REG_MAP3);
break;
}
if (vecm_latch_flag & FLAG_REG_MAP4) {
cm_regmap_latch(&amregs4, FLAG_REG_MAP4);
break;
}
if (vecm_latch_flag & FLAG_REG_MAP5) {
cm_regmap_latch(&amregs5, FLAG_REG_MAP5);
break;
}
if ((cm2_patch_flag & 0xff) > 0)
cm2_frame_switch_patch();
} while (0);
if (cm_en && (cm_level_last != cm_level)) {
cm_level_last = cm_level;
if ((!is_meson_gxtvbb_cpu()) &&
(!is_meson_txl_cpu()) &&
(!is_meson_txlx_cpu()) &&
(!is_meson_tl1_cpu()))
amcm_level_sel(cm_level);
amcm_enable();
pr_amcm_dbg("\n[amcm..] set cm2 load OK!!!\n");
} else if ((cm_en == 0) && (cm_level_last != 0xff)) {
cm_level_last = 0xff;
amcm_disable();/* CM manage disable */
pr_amcm_dbg("\n[amcm..] set cm disable!!!\n");
}
}
static int amvecm_regmap_info(struct am_regs_s *p)
{
unsigned short i;
for (i = 0; i < p->length; i++) {
switch (p->am_reg[i].type) {
case REG_TYPE_PHY:
pr_info("%s:%d bus type: phy...\n", __func__, i);
break;
case REG_TYPE_CBUS:
pr_info("%s:%-3d cbus: 0x%-4x=0x%-8x (%-5u)=(%-10u)",
__func__, i, p->am_reg[i].addr,
(p->am_reg[i].val & p->am_reg[i].mask),
p->am_reg[i].addr,
(p->am_reg[i].val & p->am_reg[i].mask));
pr_info(" mask=%-8x(%u)\n",
p->am_reg[i].mask,
p->am_reg[i].mask);
break;
case REG_TYPE_APB:
pr_info("%s:%-3d apb: 0x%-4x=0x%-8x (%-5u)=(%-10u)",
__func__, i, p->am_reg[i].addr,
(p->am_reg[i].val & p->am_reg[i].mask),
p->am_reg[i].addr,
(p->am_reg[i].val & p->am_reg[i].mask));
pr_info(" mask=%-8x(%u)\n",
p->am_reg[i].mask,
p->am_reg[i].mask);
break;
case REG_TYPE_MPEG:
pr_info("%s:%-3d mpeg: 0x%-4x=0x%-8x (%-5u)=(%-10u)",
__func__, i, p->am_reg[i].addr,
(p->am_reg[i].val & p->am_reg[i].mask),
p->am_reg[i].addr,
(p->am_reg[i].val & p->am_reg[i].mask));
pr_info(" mask=%-8x(%u)\n",
p->am_reg[i].mask,
p->am_reg[i].mask);
break;
case REG_TYPE_AXI:
pr_info("%s:%-3d axi: 0x%-4x=0x%-8x (%-5u)=(%-10u)",
__func__, i, p->am_reg[i].addr,
(p->am_reg[i].val & p->am_reg[i].mask),
p->am_reg[i].addr,
(p->am_reg[i].val & p->am_reg[i].mask));
pr_info(" mask=%-8x(%u)\n",
p->am_reg[i].mask,
p->am_reg[i].mask);
break;
case REG_TYPE_INDEX_VPPCHROMA:
pr_info("%s:%-3d chroma: 0x%-4x=0x%-8x (%-5u)=(%-10u)",
__func__, i, p->am_reg[i].addr,
(p->am_reg[i].val & p->am_reg[i].mask),
p->am_reg[i].addr,
(p->am_reg[i].val & p->am_reg[i].mask));
pr_info(" mask=%-8x(%u)\n",
p->am_reg[i].mask,
p->am_reg[i].mask);
break;
case REG_TYPE_INDEX_GAMMA:
pr_info("%s:%-3d bus type: REG_TYPE_INDEX_GAMMA...\n",
__func__, i);
break;
case VALUE_TYPE_CONTRAST_BRIGHTNESS:
pr_info("%s:%-3d bus type: VALUE_TYPE_CONTRAST_BRIGHTNESS...\n",
__func__, i);
break;
case REG_TYPE_INDEX_VPP_COEF:
pr_info("%s:%-3d vpp coef: 0x%-4x=0x%-8x (%-5u)=(%-10u)",
__func__, i, p->am_reg[i].addr,
(p->am_reg[i].val & p->am_reg[i].mask),
p->am_reg[i].addr,
(p->am_reg[i].val & p->am_reg[i].mask));
pr_info(" mask=%-8x(%u)\n",
p->am_reg[i].mask,
p->am_reg[i].mask);
break;
case REG_TYPE_VCBUS:
pr_info("%s:%-3d vcbus: 0x%-4x=0x%-8x (%-5u)=(%-10u)",
__func__, i, p->am_reg[i].addr,
(p->am_reg[i].val & p->am_reg[i].mask),
p->am_reg[i].addr,
(p->am_reg[i].val & p->am_reg[i].mask));
pr_info(" mask=%-8x(%u)\n", p->am_reg[i].mask,
p->am_reg[i].mask);
break;
default:
pr_info("%s:%3d bus type error!!!bustype = 0x%x...\n",
__func__, i, p->am_reg[i].type);
break;
}
}
return 0;
}
static long amvecm_regmap_set(struct am_regs_s *regs,
struct am_regs_s *arg, unsigned int reg_map)
{
int ret = 0;
if (!(memcpy(regs, arg, sizeof(struct am_regs_s)))) {
pr_amcm_dbg(
"[amcm..]0x%x load reg errors: can't get buffer length\n",
reg_map);
return -EFAULT;
}
if (!regs->length || (regs->length > am_reg_size)) {
pr_amcm_dbg(
"[amcm..]0x%x load regs error: buf length error!!!, length=0x%x\n",
reg_map, regs->length);
return -EINVAL;
}
pr_amcm_dbg("\n[amcm..]0x%x reg length=0x%x ......\n",
reg_map, regs->length);
if (debug_regload)
amvecm_regmap_info(regs);
vecm_latch_flag |= reg_map;
return ret;
}
int cm_load_reg(struct am_regs_s *arg)
{
int ret = 0;
/*force set cm size to 0,enable check vpp size*/
cm_size = 0;
if (!(vecm_latch_flag & FLAG_REG_MAP0))
ret = amvecm_regmap_set(&amregs0, arg, FLAG_REG_MAP0);
else if (!(vecm_latch_flag & FLAG_REG_MAP1))
ret = amvecm_regmap_set(&amregs1, arg, FLAG_REG_MAP1);
else if (!(vecm_latch_flag & FLAG_REG_MAP2))
ret = amvecm_regmap_set(&amregs2, arg, FLAG_REG_MAP2);
else if (!(vecm_latch_flag & FLAG_REG_MAP3))
ret = amvecm_regmap_set(&amregs3, arg, FLAG_REG_MAP3);
else if (!(vecm_latch_flag & FLAG_REG_MAP4))
ret = amvecm_regmap_set(&amregs4, arg, FLAG_REG_MAP4);
else if (!(vecm_latch_flag & FLAG_REG_MAP5))
ret = amvecm_regmap_set(&amregs5, arg, FLAG_REG_MAP5);
return ret;
}