/* * drivers/video/sunxi/disp2/disp/de/lowlevel_v2x/de_dcsc.c * * Copyright (c) 2007-2019 Allwinnertech Co., Ltd. * Author: zhengxiaobin * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * 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 "de_rtmx.h" #include "de_csc_type.h" #include "de_csc.h" #define DCSC_OFST 0xB0000 #define CSC_ENHANCE_MODE_NUM 3 /* must equal to ENHANCE_MODE_NUM */ static volatile struct __csc_reg_t *dcsc_dev[DE_NUM]; static volatile struct __csc2_reg_t *dcsc2_dev[DE_NUM]; static struct de_reg_blocks dcsc_coeff_block[DE_NUM]; static struct de_reg_blocks dcsc_enable_block[DE_NUM]; static struct disp_csc_config g_dcsc_config[DE_NUM]; static unsigned int is_in_smbl[DE_NUM]; /* device csc and smbl in the same module or not */ static int de_dcsc_set_reg_base(unsigned int sel, void *base) { __inf("sel=%d, base=0x%p\n", sel, base); if (is_in_smbl[sel]) dcsc2_dev[sel] = (struct __csc2_reg_t *) base; else dcsc_dev[sel] = (struct __csc_reg_t *) base; return 0; } int de_dcsc_apply(unsigned int sel, struct disp_csc_config *config) { int csc_coeff[12]; unsigned int enhance_mode; int csc_enhance_setting[CSC_ENHANCE_MODE_NUM][4] = { {50, 50, 50, 50}, /* normal */ {50, 50, 50, 50}, /* vivid */ {50, 40, 50, 50}, /* soft */ }; config->enhance_mode = (config->enhance_mode > CSC_ENHANCE_MODE_NUM - 1) ? g_dcsc_config[sel].enhance_mode : config->enhance_mode; enhance_mode = config->enhance_mode; config->brightness = csc_enhance_setting[enhance_mode][0]; config->contrast = csc_enhance_setting[enhance_mode][1]; config->saturation = csc_enhance_setting[enhance_mode][2]; config->hue = csc_enhance_setting[enhance_mode][3]; __inf("sel=%d, in_fmt=%d, mode=%d, out_fmt=%d, mode=%d, range=%d\n", sel, config->in_fmt, config->in_mode, config->out_fmt, config->out_mode, config->out_color_range); memcpy(&g_dcsc_config[sel], config, sizeof(struct disp_csc_config)); de_csc_coeff_calc(config->in_fmt, config->in_mode, config->out_fmt, config->out_mode, config->brightness, config->contrast, config->saturation, config->hue, config->out_color_range, csc_coeff); if (is_in_smbl[sel]) { dcsc2_dev[sel]->c00.dwval = *(csc_coeff); dcsc2_dev[sel]->c01.dwval = *(csc_coeff + 1); dcsc2_dev[sel]->c02.dwval = *(csc_coeff + 2); dcsc2_dev[sel]->c03.dwval = *(csc_coeff + 3) >> 6; dcsc2_dev[sel]->c10.dwval = *(csc_coeff + 4); dcsc2_dev[sel]->c11.dwval = *(csc_coeff + 5); dcsc2_dev[sel]->c12.dwval = *(csc_coeff + 6); dcsc2_dev[sel]->c13.dwval = *(csc_coeff + 7) >> 6; dcsc2_dev[sel]->c20.dwval = *(csc_coeff + 8); dcsc2_dev[sel]->c21.dwval = *(csc_coeff + 9); dcsc2_dev[sel]->c22.dwval = *(csc_coeff + 10); dcsc2_dev[sel]->c23.dwval = *(csc_coeff + 11) >> 6; dcsc2_dev[sel]->bypass.bits.enable = 1; /* always enable csc */ } else { dcsc_dev[sel]->c00.dwval = *(csc_coeff); dcsc_dev[sel]->c01.dwval = *(csc_coeff + 1); dcsc_dev[sel]->c02.dwval = *(csc_coeff + 2); dcsc_dev[sel]->c03.dwval = *(csc_coeff + 3) + 0x200; dcsc_dev[sel]->c10.dwval = *(csc_coeff + 4); dcsc_dev[sel]->c11.dwval = *(csc_coeff + 5); dcsc_dev[sel]->c12.dwval = *(csc_coeff + 6); dcsc_dev[sel]->c13.dwval = *(csc_coeff + 7) + 0x200; dcsc_dev[sel]->c20.dwval = *(csc_coeff + 8); dcsc_dev[sel]->c21.dwval = *(csc_coeff + 9); dcsc_dev[sel]->c22.dwval = *(csc_coeff + 10); dcsc_dev[sel]->c23.dwval = *(csc_coeff + 11) + 0x200; dcsc_dev[sel]->bypass.bits.enable = 1; /* always enable csc */ } dcsc_coeff_block[sel].dirty = 1; dcsc_enable_block[sel].dirty = 1; return 0; } int de_dcsc_get_config(unsigned int sel, struct disp_csc_config *config) { memcpy(config, &g_dcsc_config[sel], sizeof(struct disp_csc_config)); return 0; } int de_dcsc_update_regs(unsigned int sel) { unsigned int reg_val; if (dcsc_enable_block[sel].dirty == 0x1) { if (is_in_smbl[sel]) { reg_val = readl((void __iomem *)dcsc_enable_block[sel].off); reg_val &= 0xfffffffd; reg_val |= (*((unsigned int *)dcsc_enable_block[sel].val)); writel(reg_val, (void __iomem *)dcsc_enable_block[sel].off); } else { memcpy((void *)dcsc_enable_block[sel].off, dcsc_enable_block[sel].val, dcsc_enable_block[sel].size); } dcsc_enable_block[sel].dirty = 0x0; } if (dcsc_coeff_block[sel].dirty == 0x1) { memcpy((void *)dcsc_coeff_block[sel].off, dcsc_coeff_block[sel].val, dcsc_coeff_block[sel].size); dcsc_coeff_block[sel].dirty = 0x0; } return 0; } int de_dcsc_init(disp_bsp_init_para *para) { uintptr_t base; void *memory; int screen_id, device_num; device_num = de_feat_get_num_screens(); for (screen_id = 0; screen_id < device_num; screen_id++) { is_in_smbl[screen_id] = de_feat_is_support_smbl(screen_id); #if defined(CONFIG_INDEPENDENT_DE) base = para->reg_base[DISP_MOD_DE + screen_id] + (screen_id + 1) * 0x00100000 + DCSC_OFST; if (screen_id) base = base - 0x00100000; #else base = para->reg_base[DISP_MOD_DE] + (screen_id + 1) * 0x00100000 + DCSC_OFST; #endif __inf("sel %d, Dcsc_base=0x%p\n", screen_id, (void *)base); if (is_in_smbl[screen_id]) { memory = kmalloc(sizeof(struct __csc2_reg_t), GFP_KERNEL | __GFP_ZERO); if (NULL == memory) { __wrn("malloc Ccsc[%d] mm fail! size=0x%x\n", screen_id, (unsigned int)sizeof(struct __csc2_reg_t)); return -1; } dcsc_enable_block[screen_id].off = base; dcsc_enable_block[screen_id].val = memory; dcsc_enable_block[screen_id].size = 0x04; dcsc_enable_block[screen_id].dirty = 0; dcsc_coeff_block[screen_id].off = base + 0x80; dcsc_coeff_block[screen_id].val = memory + 0x80; dcsc_coeff_block[screen_id].size = 0x30; dcsc_coeff_block[screen_id].dirty = 0; } else { memory = kmalloc(sizeof(struct __csc_reg_t), GFP_KERNEL | __GFP_ZERO); if (NULL == memory) { __wrn("malloc Ccsc[%d] mm fail! size=0x%x\n", screen_id, (unsigned int)sizeof(struct __csc_reg_t)); return -1; } dcsc_enable_block[screen_id].off = base; dcsc_enable_block[screen_id].val = memory; dcsc_enable_block[screen_id].size = 0x04; dcsc_enable_block[screen_id].dirty = 0; dcsc_coeff_block[screen_id].off = base + 0x10; dcsc_coeff_block[screen_id].val = memory + 0x10; dcsc_coeff_block[screen_id].size = 0x30; dcsc_coeff_block[screen_id].dirty = 0; } g_dcsc_config[screen_id].enhance_mode = 0; de_dcsc_set_reg_base(screen_id, memory); } return 0; }