From: hwangjae lee Date: Tue, 15 May 2018 07:29:50 +0000 (+0900) Subject: [9610] fbdev: dpu: DPU2.0 driver is added X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=a7f1745edd6e907600e2c0d50627fe8c4dd720f9;p=GitHub%2FLineageOS%2Fandroid_kernel_motorola_exynos9610.git [9610] fbdev: dpu: DPU2.0 driver is added Change-Id: I4e1dbb842bc57f704ef90885af7b4d89307e1062 Signed-off-by: hwangjae lee --- diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 5e58f5ec0a28..d2783031e507 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -2453,6 +2453,7 @@ config FB_SIMPLE source "drivers/video/fbdev/omap/Kconfig" source "drivers/video/fbdev/omap2/Kconfig" +source "drivers/video/fbdev/exynos/Kconfig" source "drivers/video/fbdev/mmp/Kconfig" config FB_SH_MOBILE_MERAM diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile index 8895536a20d6..c066712905e0 100644 --- a/drivers/video/fbdev/Makefile +++ b/drivers/video/fbdev/Makefile @@ -7,6 +7,8 @@ obj-y += core/ +obj-y += exynos/ + obj-$(CONFIG_FB_MACMODES) += macmodes.o obj-$(CONFIG_FB_WMT_GE_ROPS) += wmt_ge_rops.o diff --git a/drivers/video/fbdev/exynos/dpu20/Kconfig b/drivers/video/fbdev/exynos/dpu20/Kconfig new file mode 100644 index 000000000000..1d046d1c48b3 --- /dev/null +++ b/drivers/video/fbdev/exynos/dpu20/Kconfig @@ -0,0 +1,110 @@ +config EXYNOS_DPU20 + bool "Samsung Exynos Display Sub-system (DPP, DECON, MIPI, DISPLAYPORT)" + default n + depends on FB + +if EXYNOS_DPU20 +config EXYNOS_DPP + bool "Samsung Exynos Display Post Processor driver" + depends on EXYNOS_DPU20 + default y + +config EXYNOS_MIPI_DSIM + bool "Samsung Exynos MIPI-DSI driver" + depends on EXYNOS_DPU20 + default y + help + Enable MIPI-DSI driver. + +config EXYNOS_DISPLAYPORT + bool "Samsung Exynos DISPLAYPORT driver" + depends on EXYNOS_DPU20 + default n + help + Enable DISPLAYPORT driver. + +config EXYNOS_WINDOW_UPDATE + bool "Support Window Update Mode" + depends on EXYNOS_DPU20 + default n + +config EXYNOS_BLOCK_MODE + bool "Support Blocking Mode" + depends on EXYNOS_DPU20 + default y + +config EXYNOS_HIBERNATION + bool "Support Display Hibernation Mode" + depends on EXYNOS_DPU20 + default n + +config EXYNOS_DOZE + bool "Support DOZE mode" + depends on EXYNOS_DPU20 + default n + +config EXYNOS_AFBC_DEBUG + bool "Support AFBC Decoder Debug" + depends on EXYNOS_DPU20 + default n + +config EXYNOS_MULTIRESOLUTION + bool "Support Multi Resolution LCD" + depends on EXYNOS_DPU20 && EXYNOS_WINDOW_UPDATE + default y + +config EXYNOS_VIRTUAL_DISPLAY + bool "Support Virtual Display Mode" + depends on EXYNOS_DPU20 + default n + help + If this menu is enabled, you can boot up board without LCD. + +config EXYNOS_LOW_PERSISTENCE + bool "Support Low Persistence Mode" + depends on EXYNOS_DPU20 + default n + +config EXYNOS_CURSOR + bool "Support Cursor Async Mode" + depends on EXYNOS_DPU20 + default n + +config EXYNOS_DSIM_DITHER + bool "DSIM SSCG feature" + depends on EXYNOS_MIPI_DSIM + default y + +config DECON_BTS_LEGACY + bool "Legacy BTS scheme of display" + depends on EXYNOS_DPU20 + default n + +config FB_TEST + bool "Framebuffer test only" + depends on EXYNOS_DPU20 + default n + +config DSIM_CMD_TEST + bool "DSIM command test only" + depends on EXYNOS_DPU20 + default n + +config SUPPORT_LEGACY_ION + bool "Support legacy ION API" + depends on EXYNOS_DPU20 + default n + +config SUPPORT_LEGACY_FENCE + bool "Support legacy fence API" + depends on EXYNOS_DPU20 + default n + +config SUPPORT_KERNEL_4_9 + bool "Support kernel version 4.9" + depends on EXYNOS_DPU20 + default n + +endif + +source "drivers/video/fbdev/exynos/dpu20/panels/Kconfig" diff --git a/drivers/video/fbdev/exynos/dpu20/Makefile b/drivers/video/fbdev/exynos/dpu20/Makefile new file mode 100644 index 000000000000..cbbc7ae06d4b --- /dev/null +++ b/drivers/video/fbdev/exynos/dpu20/Makefile @@ -0,0 +1,18 @@ +# +# Copyright (c) 2013 Samsung Electronics Co., Ltd. +# http://www.samsung.com +# +# Licensed under GPLv2 +# + +obj-$(CONFIG_EXYNOS_DPP) += dpp.o +dpp-y := dpp_drv.o +obj-$(CONFIG_EXYNOS_MIPI_DSIM) += dsim.o +dsim-y += dsim_drv.o +obj-$(CONFIG_EXYNOS_DISPLAYPORT) += displayport.o +displayport-y += displayport_drv.o displayport_edid.o displayport_hdcp13.o ./cal_9610/displayport_reg.o +obj-$(CONFIG_SOC_EXYNOS9610) += ./cal_9610/dsim_reg.o ./cal_9610/dpp_reg.o ./cal_9610/decon_reg.o +obj-$(CONFIG_EXYNOS_DPU20) += decon.o +obj-$(CONFIG_EXYNOS9610_BTS) += bts.o +decon-y += decon_core.o decon_dsi.o decon_wb.o helper.o win_update.o cursor.o fence.o event_log.o +obj-y += panels/ diff --git a/drivers/video/fbdev/exynos/dpu20/bts.c b/drivers/video/fbdev/exynos/dpu20/bts.c new file mode 100644 index 000000000000..e7fdeb6b8fed --- /dev/null +++ b/drivers/video/fbdev/exynos/dpu20/bts.c @@ -0,0 +1,550 @@ + /* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * BTS file for Samsung EXYNOS DPU driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "decon.h" + +#include +#include +#if defined(CONFIG_CAL_IF) +#include +#endif +#if defined(CONFIG_SOC_EXYNOS9610) +#include +#endif + +#define DISP_FACTOR 100UL +#define PPC 2UL +#define LCD_REFRESH_RATE 63UL +#define MULTI_FACTOR (1UL << 10) + +u64 dpu_bts_calc_aclk_disp(struct decon_device *decon, + struct decon_win_config *config, u64 resol_clock) +{ + u64 s_ratio_h, s_ratio_v; + u64 aclk_disp; + u64 ppc; + struct decon_frame *src = &config->src; + struct decon_frame *dst = &config->dst; + + s_ratio_h = (src->w <= dst->w) ? MULTI_FACTOR : MULTI_FACTOR * (u64)src->w / (u64)dst->w; + s_ratio_v = (src->h <= dst->h) ? MULTI_FACTOR : MULTI_FACTOR * (u64)src->h / (u64)dst->h; + + /* case for using dsc encoder 1ea at decon0 or decon1 */ + if ((decon->id != 2) && (decon->lcd_info->dsc_cnt == 1)) + ppc = PPC / 2UL; + else + ppc = PPC; + + aclk_disp = resol_clock * s_ratio_h * s_ratio_v * DISP_FACTOR / 100UL + / ppc * (MULTI_FACTOR * (u64)dst->w / (u64)decon->lcd_info->xres) + / (MULTI_FACTOR * MULTI_FACTOR * MULTI_FACTOR); + + if (aclk_disp < (resol_clock / ppc)) + aclk_disp = resol_clock / ppc; + + return aclk_disp; +} + +static void dpu_bts_sum_all_decon_bw(struct decon_device *decon, u32 ch_bw[]) +{ + int id = decon->id; + + /* store current bw for each channel */ + decon->bts.ch_bw[id][BTS_DPU0] = ch_bw[BTS_DPU0]; + decon->bts.ch_bw[id][BTS_DPU1] = ch_bw[BTS_DPU1]; + decon->bts.ch_bw[id][BTS_DPU2] = ch_bw[BTS_DPU2]; + + switch (id) { + case 0: + /* sum with bw of other decons */ + ch_bw[BTS_DPU0] += decon->bts.ch_bw[1][BTS_DPU0] + + decon->bts.ch_bw[2][BTS_DPU0]; + ch_bw[BTS_DPU1] += decon->bts.ch_bw[1][BTS_DPU1] + + decon->bts.ch_bw[2][BTS_DPU1]; + ch_bw[BTS_DPU2] += decon->bts.ch_bw[1][BTS_DPU2] + + decon->bts.ch_bw[2][BTS_DPU2]; + break; + case 1: + /* sum with bw of other decons */ + ch_bw[BTS_DPU0] += decon->bts.ch_bw[0][BTS_DPU0] + + decon->bts.ch_bw[2][BTS_DPU0]; + ch_bw[BTS_DPU1] += decon->bts.ch_bw[0][BTS_DPU1] + + decon->bts.ch_bw[2][BTS_DPU1]; + ch_bw[BTS_DPU2] += decon->bts.ch_bw[0][BTS_DPU2] + + decon->bts.ch_bw[2][BTS_DPU2]; + break; + case 2: + /* sum with bw of other decons */ + ch_bw[BTS_DPU0] += decon->bts.ch_bw[0][BTS_DPU0] + + decon->bts.ch_bw[1][BTS_DPU0]; + ch_bw[BTS_DPU1] += decon->bts.ch_bw[0][BTS_DPU1] + + decon->bts.ch_bw[1][BTS_DPU1]; + ch_bw[BTS_DPU2] += decon->bts.ch_bw[0][BTS_DPU2] + + decon->bts.ch_bw[1][BTS_DPU2]; + break; + default: + decon_warn("[%s] undefined decon id(%d)!\n", __func__, id); + break; + } +} + +/* bus utilization 75% */ +#define BUS_UTIL 75 + +static void dpu_bts_find_max_disp_freq(struct decon_device *decon, + struct decon_reg_data *regs) +{ + int i, idx; + u32 disp_ch_bw[BTS_DPU_MAX]; + u32 max_disp_ch_bw; + u32 disp_op_freq = 0, freq = 0; + u64 resol_clock; + u64 op_fps = LCD_REFRESH_RATE; + struct decon_win_config *config = regs->dpp_config; + + memset(disp_ch_bw, 0, sizeof(disp_ch_bw)); + + disp_ch_bw[BTS_DPU0] = decon->bts.bw[BTS_DPP4] + decon->bts.bw[BTS_DPP5]; + disp_ch_bw[BTS_DPU1] = decon->bts.bw[BTS_DPP0] + decon->bts.bw[BTS_DPP2]; + disp_ch_bw[BTS_DPU2] = decon->bts.bw[BTS_DPP1] + decon->bts.bw[BTS_DPP3]; + + /* must be considered other decon's bw */ + dpu_bts_sum_all_decon_bw(decon, disp_ch_bw); + + for (i = 0; i < BTS_DPU_MAX; ++i) + if (disp_ch_bw[i]) + DPU_DEBUG_BTS("\tCH%d = %d\n", i, disp_ch_bw[i]); + + max_disp_ch_bw = disp_ch_bw[0]; + for (i = 1; i < BTS_DPU_MAX; ++i) + if (max_disp_ch_bw < disp_ch_bw[i]) + max_disp_ch_bw = disp_ch_bw[i]; + + decon->bts.peak = max_disp_ch_bw; + decon->bts.max_disp_freq = max_disp_ch_bw * 100 / (16 * BUS_UTIL) + 1; + + if (decon->dt.out_type == DECON_OUT_DP) + op_fps = decon->lcd_info->fps; + + /* 1.1: 10% margin, 1000: for KHZ, 1: for raising to a unit */ + resol_clock = decon->lcd_info->xres * decon->lcd_info->yres * + op_fps * 11 / 10 / 1000 + 1; + decon->bts.resol_clk = resol_clock; + + DPU_DEBUG_BTS("\tDECON%d : resol clock = %d Khz\n", + decon->id, decon->bts.resol_clk); + + for (i = 0; i < MAX_DECON_WIN; ++i) { + idx = config[i].idma_type; + if ((config[i].state != DECON_WIN_STATE_BUFFER) && + (config[i].state != DECON_WIN_STATE_COLOR)) + continue; + + freq = dpu_bts_calc_aclk_disp(decon, &config[i], resol_clock); + if (disp_op_freq < freq) + disp_op_freq = freq; + } + + DPU_DEBUG_BTS("\tDISP bus freq(%d), operating freq(%d)\n", + decon->bts.max_disp_freq, disp_op_freq); + + if (decon->bts.max_disp_freq < disp_op_freq) + decon->bts.max_disp_freq = disp_op_freq; + + DPU_DEBUG_BTS("\tMAX DISP CH FREQ = %d\n", decon->bts.max_disp_freq); +} + +static void dpu_bts_share_bw_info(int id) +{ + int i; + struct decon_device *decon[3]; + + for (i = 0; i < 3; i++) + decon[i] = get_decon_drvdata(i); + + switch (id) { + case 0: + if (decon[1] != NULL) { + decon[1]->bts.ch_bw[id][BTS_DPU0] = + decon[id]->bts.ch_bw[id][BTS_DPU0]; + decon[1]->bts.ch_bw[id][BTS_DPU1] = + decon[id]->bts.ch_bw[id][BTS_DPU1]; + decon[1]->bts.ch_bw[id][BTS_DPU2] = + decon[id]->bts.ch_bw[id][BTS_DPU2]; + } + if (decon[2] != NULL) { + decon[2]->bts.ch_bw[id][BTS_DPU0] = + decon[id]->bts.ch_bw[id][BTS_DPU0]; + decon[2]->bts.ch_bw[id][BTS_DPU1] = + decon[id]->bts.ch_bw[id][BTS_DPU1]; + decon[2]->bts.ch_bw[id][BTS_DPU2] = + decon[id]->bts.ch_bw[id][BTS_DPU2]; + } + break; + case 1: + if (decon[0] != NULL) { + decon[0]->bts.ch_bw[id][BTS_DPU0] = + decon[id]->bts.ch_bw[id][BTS_DPU0]; + decon[0]->bts.ch_bw[id][BTS_DPU1] = + decon[id]->bts.ch_bw[id][BTS_DPU1]; + decon[0]->bts.ch_bw[id][BTS_DPU2] = + decon[id]->bts.ch_bw[id][BTS_DPU2]; + } + if (decon[2] != NULL) { + decon[2]->bts.ch_bw[id][BTS_DPU0] = + decon[id]->bts.ch_bw[id][BTS_DPU0]; + decon[2]->bts.ch_bw[id][BTS_DPU1] = + decon[id]->bts.ch_bw[id][BTS_DPU1]; + decon[2]->bts.ch_bw[id][BTS_DPU2] = + decon[id]->bts.ch_bw[id][BTS_DPU2]; + } + break; + case 2: + if (decon[0] != NULL) { + decon[0]->bts.ch_bw[id][BTS_DPU0] = + decon[id]->bts.ch_bw[id][BTS_DPU0]; + decon[0]->bts.ch_bw[id][BTS_DPU1] = + decon[id]->bts.ch_bw[id][BTS_DPU1]; + decon[0]->bts.ch_bw[id][BTS_DPU2] = + decon[id]->bts.ch_bw[id][BTS_DPU2]; + } + if (decon[1] != NULL) { + decon[1]->bts.ch_bw[id][BTS_DPU0] = + decon[id]->bts.ch_bw[id][BTS_DPU0]; + decon[1]->bts.ch_bw[id][BTS_DPU1] = + decon[id]->bts.ch_bw[id][BTS_DPU1]; + decon[1]->bts.ch_bw[id][BTS_DPU2] = + decon[id]->bts.ch_bw[id][BTS_DPU2]; + } + break; + default: + break; + } +} + +void dpu_bts_calc_bw(struct decon_device *decon, struct decon_reg_data *regs) +{ + struct decon_win_config *config = regs->dpp_config; + struct bts_decon_info bts_info; + enum dpp_rotate rot; + int idx, i; + + if (!decon->bts.enabled) + return; + + DPU_DEBUG_BTS("\n"); + DPU_DEBUG_BTS("%s + : DECON%d\n", __func__, decon->id); + + memset(&bts_info, 0, sizeof(struct bts_decon_info)); + for (i = 0; i < MAX_DECON_WIN; ++i) { + if (config[i].state == DECON_WIN_STATE_BUFFER) { + idx = config[i].idma_type; + bts_info.dpp[idx].used = true; + } else { + continue; + } + + bts_info.dpp[idx].bpp = dpu_get_bpp(config[i].format); + bts_info.dpp[idx].src_w = config[i].src.w; + bts_info.dpp[idx].src_h = config[i].src.h; + bts_info.dpp[idx].dst.x1 = config[i].dst.x; + bts_info.dpp[idx].dst.x2 = config[i].dst.x + config[i].dst.w; + bts_info.dpp[idx].dst.y1 = config[i].dst.y; + bts_info.dpp[idx].dst.y2 = config[i].dst.y + config[i].dst.h; + rot = config[i].dpp_parm.rot; + bts_info.dpp[idx].rotation = (rot > DPP_ROT_180) ? true : false; + + DPU_DEBUG_BTS("\tDPP%d : bpp(%d) src w(%d) h(%d) rot(%d)\n", + idx, bts_info.dpp[idx].bpp, + bts_info.dpp[idx].src_w, bts_info.dpp[idx].src_h, + bts_info.dpp[idx].rotation); + DPU_DEBUG_BTS("\t\t\t\tdst x(%d) right(%d) y(%d) bottom(%d)\n", + bts_info.dpp[idx].dst.x1, + bts_info.dpp[idx].dst.x2, + bts_info.dpp[idx].dst.y1, + bts_info.dpp[idx].dst.y2); + } + + bts_info.vclk = decon->bts.resol_clk; + bts_info.lcd_w = decon->lcd_info->xres; + bts_info.lcd_h = decon->lcd_info->yres; + decon->bts.total_bw = bts_calc_bw(decon->bts.type, &bts_info); + memcpy(&decon->bts.bts_info, &bts_info, sizeof(struct bts_decon_info)); + + for (i = 0; i < BTS_DPP_MAX; ++i) { + decon->bts.bw[i] = bts_info.dpp[i].bw; + if (decon->bts.bw[i]) + DPU_DEBUG_BTS("\tDPP%d bandwidth = %d\n", + i, decon->bts.bw[i]); + } + + DPU_DEBUG_BTS("\tDECON%d total bandwidth = %d\n", decon->id, + decon->bts.total_bw); + + dpu_bts_find_max_disp_freq(decon, regs); + + /* update bw for other decons */ + dpu_bts_share_bw_info(decon->id); + + DPU_DEBUG_BTS("%s -\n", __func__); +} + +void dpu_bts_update_bw(struct decon_device *decon, struct decon_reg_data *regs, + u32 is_after) +{ + struct bts_bw bw = { 0, }; +#if defined(CONFIG_EXYNOS_DISPLAYPORT) + struct displayport_device *displayport = get_displayport_drvdata(); + videoformat cur = displayport->cur_video; + __u64 pixelclock = supported_videos[cur].dv_timings.bt.pixelclock; +#endif + + DPU_DEBUG_BTS("%s +\n", __func__); + + if (!decon->bts.enabled) + return; + + /* update peak & read bandwidth per DPU port */ + bw.peak = decon->bts.peak; + bw.read = decon->bts.total_bw; + DPU_DEBUG_BTS("\tpeak = %d, read = %d\n", bw.peak, bw.read); + + if (bw.read == 0) + bw.peak = 0; + + if (is_after) { /* after DECON h/w configuration */ + if (decon->bts.total_bw <= decon->bts.prev_total_bw) + bts_update_bw(decon->bts.type, bw); + +#if defined(CONFIG_EXYNOS_DISPLAYPORT) + if ((displayport->state == DISPLAYPORT_STATE_ON) + && (pixelclock >= 533000000)) /* 4K DP case */ + return; +#endif + + if (decon->bts.max_disp_freq <= decon->bts.prev_max_disp_freq) + pm_qos_update_request(&decon->bts.disp_qos, + decon->bts.max_disp_freq); + + decon->bts.prev_total_bw = decon->bts.total_bw; + decon->bts.prev_max_disp_freq = decon->bts.max_disp_freq; + } else { + if (decon->bts.total_bw > decon->bts.prev_total_bw) + bts_update_bw(decon->bts.type, bw); + +#if defined(CONFIG_EXYNOS_DISPLAYPORT) + if ((displayport->state == DISPLAYPORT_STATE_ON) + && (pixelclock >= 533000000)) /* 4K DP case */ + return; +#endif + + if (decon->bts.max_disp_freq > decon->bts.prev_max_disp_freq) + pm_qos_update_request(&decon->bts.disp_qos, + decon->bts.max_disp_freq); + } + + DPU_DEBUG_BTS("%s -\n", __func__); +} + +void dpu_bts_acquire_bw(struct decon_device *decon) +{ +#if defined(CONFIG_DECON_BTS_LEGACY) && defined(CONFIG_EXYNOS_DISPLAYPORT) + struct displayport_device *displayport = get_displayport_drvdata(); + videoformat cur = displayport->cur_video; + __u64 pixelclock = supported_videos[cur].dv_timings.bt.pixelclock; +#endif + struct decon_win_config config; + u64 resol_clock; + u32 aclk_freq = 0; + + DPU_DEBUG_BTS("%s +\n", __func__); + + if (!decon->bts.enabled) + return; + + if (decon->dt.out_type == DECON_OUT_DSI) { + memset(&config, 0, sizeof(struct decon_win_config)); + config.src.w = config.dst.w = decon->lcd_info->xres; + config.src.h = config.dst.h = decon->lcd_info->yres; + resol_clock = decon->lcd_info->xres * decon->lcd_info->yres * + LCD_REFRESH_RATE * 11 / 10 / 1000 + 1; + aclk_freq = dpu_bts_calc_aclk_disp(decon, &config, resol_clock); + DPU_DEBUG_BTS("Initial calculated disp freq(%lu)\n", aclk_freq); + /* + * If current disp freq is higher than calculated freq, + * it must not be set. if not, underrun can occur. + */ + if (cal_dfs_get_rate(ACPM_DVFS_DISP) < aclk_freq) + pm_qos_update_request(&decon->bts.disp_qos, aclk_freq); + + DPU_DEBUG_BTS("Get initial disp freq(%lu)\n", + cal_dfs_get_rate(ACPM_DVFS_DISP)); + + return; + } + +#if defined(CONFIG_DECON_BTS_LEGACY) && defined(CONFIG_EXYNOS_DISPLAYPORT) + if (decon->dt.out_type != DECON_OUT_DP) + return; + + if (pixelclock >= 533000000) { + if (pm_qos_request_active(&decon->bts.mif_qos)) + pm_qos_update_request(&decon->bts.mif_qos, 1794 * 1000); + else + DPU_ERR_BTS("%s mif qos setting error\n", __func__); + + if (pm_qos_request_active(&decon->bts.int_qos)) + pm_qos_update_request(&decon->bts.int_qos, 534 * 1000); + else + DPU_ERR_BTS("%s int qos setting error\n", __func__); + + if (pm_qos_request_active(&decon->bts.disp_qos)) + pm_qos_update_request(&decon->bts.disp_qos, 400 * 1000); + else + DPU_ERR_BTS("%s int qos setting error\n", __func__); + + if (!decon->bts.scen_updated) { + decon->bts.scen_updated = 1; + bts_update_scen(BS_DP_DEFAULT, 1); + } + } else if (pixelclock > 148500000) { /* pixelclock < 533000000 ? */ + if (pm_qos_request_active(&decon->bts.mif_qos)) + pm_qos_update_request(&decon->bts.mif_qos, 1352 * 1000); + else + DPU_ERR_BTS("%s mif qos setting error\n", __func__); + } /* pixelclock <= 148500000 ? */ + + DPU_DEBUG_BTS("%s: decon%d, pixelclock(%u)\n", __func__, decon->id, + pixelclock); +#endif +} + +void dpu_bts_release_bw(struct decon_device *decon) +{ + struct bts_bw bw = { 0, }; + DPU_DEBUG_BTS("%s +\n", __func__); + + if (!decon->bts.enabled) + return; + + if (decon->dt.out_type == DECON_OUT_DSI) { + bts_update_bw(decon->bts.type, bw); + decon->bts.prev_total_bw = 0; + pm_qos_update_request(&decon->bts.disp_qos, 0); + decon->bts.prev_max_disp_freq = 0; + } else if (decon->dt.out_type == DECON_OUT_DP) { +#if defined(CONFIG_DECON_BTS_LEGACY) && defined(CONFIG_EXYNOS_DISPLAYPORT) + if (pm_qos_request_active(&decon->bts.mif_qos)) + pm_qos_update_request(&decon->bts.mif_qos, 0); + else + DPU_ERR_BTS("%s mif qos setting error\n", __func__); + + if (pm_qos_request_active(&decon->bts.int_qos)) + pm_qos_update_request(&decon->bts.int_qos, 0); + else + DPU_ERR_BTS("%s int qos setting error\n", __func__); + + if (pm_qos_request_active(&decon->bts.disp_qos)) + pm_qos_update_request(&decon->bts.disp_qos, 0); + else + DPU_ERR_BTS("%s int qos setting error\n", __func__); + + if (decon->bts.scen_updated) { + decon->bts.scen_updated = 0; + bts_update_scen(BS_DP_DEFAULT, 0); + } +#endif + } + + DPU_DEBUG_BTS("%s -\n", __func__); +} + +void dpu_bts_init(struct decon_device *decon) +{ + int comp_ratio; + int i; + + DPU_DEBUG_BTS("%s +\n", __func__); + + decon->bts.enabled = false; + + if (!IS_ENABLED(CONFIG_EXYNOS9820_BTS)) { + DPU_ERR_BTS("decon%d bts feature is disabled\n", decon->id); + return; + } + + if (decon->id == 1) + decon->bts.type = BTS_BW_DECON1; + else if (decon->id == 2) + decon->bts.type = BTS_BW_DECON2; + else + decon->bts.type = BTS_BW_DECON0; + + for (i = 0; i < BTS_DPU_MAX; i++) + decon->bts.ch_bw[decon->id][i] = 0; + + DPU_DEBUG_BTS("BTS_BW_TYPE(%d) -\n", decon->bts.type); + + if (decon->lcd_info->dsc_enabled) + comp_ratio = 3; + else + comp_ratio = 1; + + if (decon->dt.out_type == DECON_OUT_DP) { + /* + * Decon2-DP : various resolutions are available + * therefore, set max resolution clock at init phase to avoid underrun + */ + decon->bts.resol_clk = (u32)((u64)4096 * 2160 * 60 * 11 + / 10 / 1000 + 1); + } else { + /* + * Resol clock(KHZ) = lcd width x lcd height x 63(refresh rate) x + * 1.1(10% margin) x comp_ratio(1/3 DSC) / 2(2PPC) / + * 1000(for KHZ) + 1(for raising to a unit) + */ + decon->bts.resol_clk = (u32)((u64)decon->lcd_info->xres * + (u64)decon->lcd_info->yres * + LCD_REFRESH_RATE * 11 / 10 / 1000 + 1); + } + DPU_DEBUG_BTS("[Init: D%d] resol clock = %d Khz\n", + decon->id, decon->bts.resol_clk); + + pm_qos_add_request(&decon->bts.mif_qos, PM_QOS_BUS_THROUGHPUT, 0); + pm_qos_add_request(&decon->bts.int_qos, PM_QOS_DEVICE_THROUGHPUT, 0); + pm_qos_add_request(&decon->bts.disp_qos, PM_QOS_DISPLAY_THROUGHPUT, 0); + decon->bts.scen_updated = 0; + + decon->bts.enabled = true; + + DPU_INFO_BTS("decon%d bts feature is enabled\n", decon->id); +} + +void dpu_bts_deinit(struct decon_device *decon) +{ + if (!decon->bts.enabled) + return; + + DPU_DEBUG_BTS("%s +\n", __func__); + pm_qos_remove_request(&decon->bts.disp_qos); + pm_qos_remove_request(&decon->bts.int_qos); + pm_qos_remove_request(&decon->bts.mif_qos); + DPU_DEBUG_BTS("%s -\n", __func__); +} + +struct decon_bts_ops decon_bts_control = { + .bts_init = dpu_bts_init, + .bts_calc_bw = dpu_bts_calc_bw, + .bts_update_bw = dpu_bts_update_bw, + .bts_acquire_bw = dpu_bts_acquire_bw, + .bts_release_bw = dpu_bts_release_bw, + .bts_deinit = dpu_bts_deinit, +}; diff --git a/drivers/video/fbdev/exynos/dpu20/cal_9610/decon_cal.h b/drivers/video/fbdev/exynos/dpu20/cal_9610/decon_cal.h new file mode 100644 index 000000000000..630e445ea1ce --- /dev/null +++ b/drivers/video/fbdev/exynos/dpu20/cal_9610/decon_cal.h @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Header file for Exynos9820 DECON CAL + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __SAMSUNG_DECON_CAL_H__ +#define __SAMSUNG_DECON_CAL_H__ + +#include "../panels/decon_lcd.h" + +#define CEIL(x) ((x-(u32)(x) > 0 ? (u32)(x+1) : (u32)(x))) + +#define CHIP_VER (9820) +#define MAX_DECON_CNT 3 +#define MAX_DECON_WIN 6 +#define MAX_DPP_SUBDEV 7 + +enum decon_idma_type { + IDMA_G0 = 0, + IDMA_G1, + IDMA_VG0, + IDMA_VG1, + IDMA_VGF0, + IDMA_VGF1, /* VGRF in case of Exynos9810 */ + ODMA_WB, + MAX_DECON_DMA_TYPE, +}; + +#define IDMA_GF0 IDMA_G0 +#define IDMA_GF1 IDMA_G1 +#define IDMA_VG IDMA_VG0 +#define IDMA_VGF IDMA_VG1 +#define IDMA_VGS IDMA_VGF0 +#define IDMA_VGRFS IDMA_VGF1 + +enum decon_fifo_mode { + DECON_FIFO_00K = 0, + DECON_FIFO_04K, + DECON_FIFO_08K, + DECON_FIFO_12K, + DECON_FIFO_16K, +}; + +enum decon_dsi_mode { + DSI_MODE_SINGLE = 0, + DSI_MODE_DUAL_DSI, + DSI_MODE_DUAL_DISPLAY, + DSI_MODE_NONE +}; + +enum decon_data_path { + /* No comp - OUTFIFO0 DSIM_IF0 */ + DPATH_NOCOMP_OUTFIFO0_DSIMIF0 = 0x001, + /* No comp - FF0 - FORMATTER1 - DSIM_IF1 */ + DPATH_NOCOMP_OUTFIFO0_DSIMIF1 = 0x002, + /* No comp - SPLITTER - FF0/1 - FORMATTER0/1 - DSIM_IF0/1 */ + DPATH_NOCOMP_SPLITTER_OUTFIFO01_DSIMIF01 = 0x003, + + /* DSC_ENC0 - OUTFIFO0 - DSIM_IF0 */ + DPATH_DSCENC0_OUTFIFO0_DSIMIF0 = 0x011, + /* DSC_ENC0 - OUTFIFO0 - DSIM_IF1 */ + DPATH_DSCENC0_OUTFIFO0_DSIMIF1 = 0x012, + + /* DSCC,DSC_ENC0/1 - OUTFIFO01 DSIM_IF0 */ + DPATH_DSCC_DSCENC01_OUTFIFO01_DSIMIF0 = 0x0B1, + /* DSCC,DSC_ENC0/1 - OUTFIFO01 DSIM_IF1 */ + DPATH_DSCC_DSCENC01_OUTFIFO01_DSIMIF1 = 0x0B2, + /* DSCC,DSC_ENC0/1 - OUTFIFO01 DSIM_IF0/1*/ + DPATH_DSCC_DSCENC01_OUTFIFO01_DSIMIF01 = 0x0B3, + + /* WB_PRE */ + DPATH_WBPRE_ONLY = 0x100, + + /* No comp - OUTFIFO0 DSIM_IF0 */ + DECON1_NOCOMP_OUTFIFO0_DSIMIF0 = 0x001, + /* No comp - OUTFIFO0 DP_IF */ + DECON1_NOCOMP_OUTFIFO0_DPIF = 0x008, + /* DSC_ENC1 - OUTFIFO0 - DSIM_IF0 */ + DECON1_DSCENC1_OUTFIFO0_DSIMIF0 = 0x021, + /* DSC_ENC1 - OUTFIFO0 - DP_IF */ + DECON1_DSCENC1_OUTFIFO0_DPIF = 0x028, + + /* No comp - OUTFIFO0 DP_IF */ + DECON2_NOCOMP_OUTFIFO0_DPIF = 0x008, + /* DSC_ENC2 - OUTFIFO0 - DP_IF0 */ + DECON2_DSCENC2_OUTFIFO0_DPIF = 0x048, +}; + +enum decon_scaler_path { + SCALERPATH_OFF = 0x0, + SCALERPATH_VGF = 0x1, + SCALERPATH_VGRF = 0x2, +}; + +enum decon_path_cfg { + PATH_CON_ID_DSIM_IF0 = 0, + PATH_CON_ID_DSIM_IF1 = 1, + PATH_CON_ID_DP = 3, + PATH_CON_ID_DUAL_DSC = 4, + PATH_CON_ID_DSCC_EN = 7, +}; + +enum decon_trig_mode { + DECON_HW_TRIG = 0, + DECON_SW_TRIG +}; + +enum decon_out_type { + DECON_OUT_DSI = 0, + DECON_OUT_EDP, + DECON_OUT_DP, + DECON_OUT_WB +}; + +enum decon_rgb_order { + DECON_RGB = 0x0, + DECON_GBR = 0x1, + DECON_BRG = 0x2, + DECON_BGR = 0x4, + DECON_RBG = 0x5, + DECON_GRB = 0x6, +}; + +enum decon_win_func { + PD_FUNC_CLEAR = 0x0, + PD_FUNC_COPY = 0x1, + PD_FUNC_DESTINATION = 0x2, + PD_FUNC_SOURCE_OVER = 0x3, + PD_FUNC_DESTINATION_OVER = 0x4, + PD_FUNC_SOURCE_IN = 0x5, + PD_FUNC_DESTINATION_IN = 0x6, + PD_FUNC_SOURCE_OUT = 0x7, + PD_FUNC_DESTINATION_OUT = 0x8, + PD_FUNC_SOURCE_A_TOP = 0x9, + PD_FUNC_DESTINATION_A_TOP = 0xa, + PD_FUNC_XOR = 0xb, + PD_FUNC_PLUS = 0xc, + PD_FUNC_USER_DEFINED = 0xd, +}; + +enum decon_blending { + DECON_BLENDING_NONE = 0, + DECON_BLENDING_PREMULT = 1, + DECON_BLENDING_COVERAGE = 2, + DECON_BLENDING_MAX = 3, +}; + +enum decon_set_trig { + DECON_TRIG_DISABLE = 0, + DECON_TRIG_ENABLE +}; + +enum decon_pixel_format { + /* RGB 8bit display */ + /* 4byte */ + DECON_PIXEL_FORMAT_ARGB_8888 = 0, + DECON_PIXEL_FORMAT_ABGR_8888, + DECON_PIXEL_FORMAT_RGBA_8888, + DECON_PIXEL_FORMAT_BGRA_8888, + DECON_PIXEL_FORMAT_XRGB_8888, + DECON_PIXEL_FORMAT_XBGR_8888, + DECON_PIXEL_FORMAT_RGBX_8888, + DECON_PIXEL_FORMAT_BGRX_8888, + /* 2byte */ + DECON_PIXEL_FORMAT_RGBA_5551, + DECON_PIXEL_FORMAT_BGRA_5551, + DECON_PIXEL_FORMAT_ABGR_4444, + DECON_PIXEL_FORMAT_RGBA_4444, + DECON_PIXEL_FORMAT_BGRA_4444, + DECON_PIXEL_FORMAT_RGB_565, + DECON_PIXEL_FORMAT_BGR_565, + + /* RGB 10bit display */ + /* 4byte */ + DECON_PIXEL_FORMAT_ARGB_2101010, + DECON_PIXEL_FORMAT_ABGR_2101010, + DECON_PIXEL_FORMAT_RGBA_1010102, + DECON_PIXEL_FORMAT_BGRA_1010102, + + /* YUV 8bit display */ + /* YUV422 2P */ + DECON_PIXEL_FORMAT_NV16, + DECON_PIXEL_FORMAT_NV61, + /* YUV422 3P */ + DECON_PIXEL_FORMAT_YVU422_3P, + /* YUV420 2P */ + DECON_PIXEL_FORMAT_NV12, + DECON_PIXEL_FORMAT_NV21, + DECON_PIXEL_FORMAT_NV12M, + DECON_PIXEL_FORMAT_NV21M, + /* YUV420 3P */ + DECON_PIXEL_FORMAT_YUV420, + DECON_PIXEL_FORMAT_YVU420, + DECON_PIXEL_FORMAT_YUV420M, + DECON_PIXEL_FORMAT_YVU420M, + /* YUV - 2 planes but 1 buffer */ + DECON_PIXEL_FORMAT_NV12N, + DECON_PIXEL_FORMAT_NV12N_10B, + + /* YUV 10bit display */ + /* YUV420 2P */ + DECON_PIXEL_FORMAT_NV12M_P010, + DECON_PIXEL_FORMAT_NV21M_P010, + + /* YUV420(P8+2) 4P */ + DECON_PIXEL_FORMAT_NV12M_S10B, + DECON_PIXEL_FORMAT_NV21M_S10B, + + /* YUV422 2P */ + DECON_PIXEL_FORMAT_NV16M_P210, + DECON_PIXEL_FORMAT_NV61M_P210, + + /* YUV422(P8+2) 4P */ + DECON_PIXEL_FORMAT_NV16M_S10B, + DECON_PIXEL_FORMAT_NV61M_S10B, + + DECON_PIXEL_FORMAT_MAX, +}; + +struct decon_mode_info { + enum decon_psr_mode psr_mode; + enum decon_trig_mode trig_mode; + enum decon_out_type out_type; + enum decon_dsi_mode dsi_mode; +}; + +struct decon_param { + struct decon_mode_info psr; + struct decon_lcd *lcd_info; + u32 nr_windows; + void __iomem *disp_ss_regs; /* TODO: remove ? */ +}; + +struct decon_window_regs { + u32 wincon; + u32 start_pos; + u32 end_pos; + u32 colormap; + u32 start_time; + u32 pixel_count; + u32 whole_w; + u32 whole_h; + u32 offset_x; + u32 offset_y; + u32 winmap_state; + enum decon_idma_type type; + int plane_alpha; + enum decon_pixel_format format; + enum decon_blending blend; +}; + +u32 DPU_DMA2CH(u32 dma); +u32 DPU_CH2DMA(u32 ch); +int decon_check_supported_formats(enum decon_pixel_format format); + +/*************** DECON CAL APIs exposed to DECON driver ***************/ +/* DECON control */ +int decon_reg_init(u32 id, u32 dsi_idx, struct decon_param *p); +int decon_reg_start(u32 id, struct decon_mode_info *psr); +int decon_reg_stop(u32 id, u32 dsi_idx, struct decon_mode_info *psr, bool rst, + u32 fps); + +/* DECON window control */ +void decon_reg_win_enable_and_update(u32 id, u32 win_idx, u32 en); +void decon_reg_set_window_control(u32 id, int win_idx, + struct decon_window_regs *regs, u32 winmap_en); +void decon_reg_update_req_window_mask(u32 id, u32 win_idx); + +/* DECON shadow update and trigger control */ +void decon_reg_set_trigger(u32 id, struct decon_mode_info *psr, + enum decon_set_trig en); +void decon_reg_update_req_and_unmask(u32 id, struct decon_mode_info *psr); +int decon_reg_wait_update_done_timeout(u32 id, unsigned long timeout); +int decon_reg_wait_update_done_and_mask(u32 id, + struct decon_mode_info *psr, u32 timeout); + +/* For window update and multi resolution feature */ +int decon_reg_wait_idle_status_timeout(u32 id, unsigned long timeout); +void decon_reg_set_partial_update(u32 id, enum decon_dsi_mode dsi_mode, + struct decon_lcd *lcd_info, bool in_slice[], + u32 partial_w, u32 partial_h); +void decon_reg_set_mres(u32 id, struct decon_param *p); + +/* For writeback configuration */ +void decon_reg_release_resource(u32 id, struct decon_mode_info *psr); +void decon_reg_config_wb_size(u32 id, struct decon_lcd *lcd_info, + struct decon_param *param); + +/* DECON interrupt control */ +void decon_reg_set_int(u32 id, struct decon_mode_info *psr, u32 en); +int decon_reg_get_interrupt_and_clear(u32 id, u32 *ext_irq); + +void decon_reg_set_start_crc(u32 id, u32 en); +void decon_reg_set_select_crc_bits(u32 id, u32 bit_sel); +void decon_reg_get_crc_data(u32 id, u32 *w0_data, u32 *w1_data); + +/* TODO: this will be removed later */ +void decon_reg_update_req_global(u32 id); +/*********************************************************************/ + +#endif /* __SAMSUNG_DECON_CAL_H__ */ diff --git a/drivers/video/fbdev/exynos/dpu20/cal_9610/decon_reg.c b/drivers/video/fbdev/exynos/dpu20/cal_9610/decon_reg.c new file mode 100644 index 000000000000..308eaa64e347 --- /dev/null +++ b/drivers/video/fbdev/exynos/dpu20/cal_9610/decon_reg.c @@ -0,0 +1,2226 @@ +/* + * linux/drivers/video/fbdev/exynos/dpu_9810/decon_reg.c + * + * Copyright 2013-2017 Samsung Electronics + * SeungBeom Park + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "../decon.h" + +/******************* DECON CAL functions *************************/ +static void dpu_reg_set_qactive_pll(u32 id, u32 en) +{ + sysreg_write_mask(id, DISP_DPU_TE_QACTIVE_PLL_EN, en ? ~0 : 0, + TE_QACTIVE_PLL_EN); +} + +static int decon_reg_reset(u32 id) +{ + int tries; + + decon_write_mask(id, GLOBAL_CONTROL, ~0, GLOBAL_CONTROL_SRESET); + for (tries = 2000; tries; --tries) { + if (~decon_read(id, GLOBAL_CONTROL) & GLOBAL_CONTROL_SRESET) + break; + udelay(10); + } + + if (!tries) { + decon_err("failed to reset Decon\n"); + return -EBUSY; + } + + return 0; +} + +/* select op mode */ +static void decon_reg_set_operation_mode(u32 id, enum decon_psr_mode mode) +{ + u32 val, mask; + + mask = GLOBAL_CONTROL_OPERATION_MODE_F; + if (mode == DECON_MIPI_COMMAND_MODE) + val = GLOBAL_CONTROL_OPERATION_MODE_CMD_F; + else + val = GLOBAL_CONTROL_OPERATION_MODE_VIDEO_F; + decon_write_mask(id, GLOBAL_CONTROL, val, mask); +} + +static void decon_reg_direct_on_off(u32 id, u32 en) +{ + u32 val, mask; + + val = en ? ~0 : 0; + mask = (GLOBAL_CONTROL_DECON_EN | GLOBAL_CONTROL_DECON_EN_F); + decon_write_mask(id, GLOBAL_CONTROL, val, mask); +} + +static void decon_reg_per_frame_off(u32 id) +{ + decon_write_mask(id, GLOBAL_CONTROL, 0, GLOBAL_CONTROL_DECON_EN_F); +} + +static u32 decon_reg_get_idle_status(u32 id) +{ + u32 val; + + val = decon_read(id, GLOBAL_CONTROL); + if (val & GLOBAL_CONTROL_IDLE_STATUS) + return 1; + + return 0; +} + +static u32 decon_reg_get_run_status(u32 id) +{ + u32 val; + + val = decon_read(id, GLOBAL_CONTROL); + if (val & GLOBAL_CONTROL_RUN_STATUS) + return 1; + + return 0; +} + +static int decon_reg_wait_run_status_timeout(u32 id, unsigned long timeout) +{ + unsigned long delay_time = 10; + unsigned long cnt = timeout / delay_time; + u32 status; + + do { + status = decon_reg_get_run_status(id); + cnt--; + udelay(delay_time); + } while (!status && cnt); + + if (!cnt) { + decon_err("decon%d wait timeout decon run status(%u)\n", + id, status); + return -EBUSY; + } + + return 0; +} + +/* Determine that DECON is perfectly shuttled off through + * checking this function + */ +static int decon_reg_wait_run_is_off_timeout(u32 id, unsigned long timeout) +{ + unsigned long delay_time = 10; + unsigned long cnt = timeout / delay_time; + u32 status; + + do { + status = decon_reg_get_run_status(id); + cnt--; + udelay(delay_time); + } while (status && cnt); + + if (!cnt) { + decon_err("decon%d wait timeout decon run is shut-off(%u)\n", + id, status); + return -EBUSY; + } + + return 0; +} + +/* In bring-up, all bits are disabled */ +static void decon_reg_set_clkgate_mode(u32 id, u32 en) +{ + u32 val, mask; + + val = en ? ~0 : 0; + /* all unmask */ + mask = CLOCK_CONTROL_0_CG_MASK | CLOCK_CONTROL_0_QACTIVE_MASK; + decon_write_mask(id, CLOCK_CONTROL_0, val, mask); +} + +static void decon_reg_set_te_qactive_pll_mode(u32 id, u32 en) +{ + u32 val, mask; + + val = en ? ~0 : 0; + /* all unmask */ + mask = CLOCK_CONTROL_0_TE_QACTIVE_PLL_ON; + decon_write_mask(0, CLOCK_CONTROL_0, val, mask); +} + +/* + * API is considering real possible Display Scenario + * such as following examples + * < Single display > + * < Dual/Triple display > + * < Dual display + DP > + * + * Current API does not configure various 8K case fully! + * Therefore, modify/add configuration cases if necessary + * "Resource Confliction" will happen if enabled simultaneously + */ +static void decon_reg_set_sram_share(u32 id, enum decon_fifo_mode fifo_mode) +{ + u32 val = 0; + + switch (fifo_mode) { + case DECON_FIFO_04K: + if (id == 0) + val = SRAM0_SHARE_ENABLE_F; + else if (id == 1) + val = SRAM1_SHARE_ENABLE_F; + else if (id == 2) + val = SRAM2_SHARE_ENABLE_F; + break; + case DECON_FIFO_08K: + if (id == 0) + val = SRAM0_SHARE_ENABLE_F | SRAM1_SHARE_ENABLE_F; + else if (id == 1) + val = 0; + else if (id == 2) + val = SRAM2_SHARE_ENABLE_F | SRAM3_SHARE_ENABLE_F; + break; + case DECON_FIFO_12K: + if (id == 2) { + val = SRAM1_SHARE_ENABLE_F | SRAM2_SHARE_ENABLE_F + | SRAM3_SHARE_ENABLE_F; + } else { + decon_err("decon%d can't support SRAM 12KB\n", + id); + } + break; + + case DECON_FIFO_16K: + val = ALL_SRAM_SHARE_ENABLE; + break; + case DECON_FIFO_00K: + default: + break; + } + + decon_write(id, SRAM_SHARE_ENABLE, val); +} + +static void decon_reg_set_scaled_image_size(u32 id, + enum decon_dsi_mode dsi_mode, struct decon_lcd *lcd_info) +{ + u32 val, mask; + + val = SCALED_SIZE_HEIGHT_F(lcd_info->yres) | + SCALED_SIZE_WIDTH_F(lcd_info->xres); + mask = SCALED_SIZE_HEIGHT_MASK | SCALED_SIZE_WIDTH_MASK; + decon_write_mask(id, SCALED_SIZE_CONTROL_0, val, mask); +} + +static void decon_reg_set_outfifo_size_ctl0(u32 id, u32 width, u32 height) +{ + u32 val; + u32 th, mask; + + /* OUTFIFO_0 */ + val = OUTFIFO_HEIGHT_F(height) | OUTFIFO_WIDTH_F(width); + mask = OUTFIFO_HEIGHT_MASK | OUTFIFO_WIDTH_MASK; + decon_write(id, OUTFIFO_SIZE_CONTROL_0, val); + + /* may be implemented later by considering 1/2H transfer */ + th = OUTFIFO_TH_1H_F; /* 1H transfer */ + mask = OUTFIFO_TH_MASK; + decon_write_mask(id, OUTFIFO_TH_CONTROL_0, th, mask); +} + +static void decon_reg_set_outfifo_size_ctl1(u32 id, u32 width, u32 height) +{ + u32 val, mask; + + val = OUTFIFO_1_WIDTH_F(width); + mask = OUTFIFO_1_WIDTH_MASK; + + /* OUTFIFO_1 */ + decon_write_mask(id, OUTFIFO_SIZE_CONTROL_1, val, mask); +} + +static void decon_reg_set_outfifo_size_ctl2(u32 id, u32 width, u32 height) +{ + u32 val, mask; + + val = OUTFIFO_COMPRESSED_SLICE_HEIGHT_F(height) | + OUTFIFO_COMPRESSED_SLICE_WIDTH_F(width); + mask = OUTFIFO_COMPRESSED_SLICE_HEIGHT_MASK | + OUTFIFO_COMPRESSED_SLICE_WIDTH_MASK; + + /* OUTFIFO_2 */ + decon_write_mask(id, OUTFIFO_SIZE_CONTROL_2, val, mask); +} + +static void decon_reg_set_rgb_order(u32 id, enum decon_rgb_order order) +{ + u32 val, mask; + + val = OUTFIFO_PIXEL_ORDER_SWAP_F(order); + mask = OUTFIFO_PIXEL_ORDER_SWAP_MASK; + decon_write_mask(id, OUTFIFO_DATA_ORDER_CONTROL, val, mask); +} + +static void decon_reg_set_blender_bg_image_size(u32 id, + enum decon_dsi_mode dsi_mode, struct decon_lcd *lcd_info) +{ + u32 width, val, mask; + + width = lcd_info->xres; + + if (dsi_mode == DSI_MODE_DUAL_DSI) + width = width * 2; + + val = BLENDER_BG_HEIGHT_F(lcd_info->yres) | BLENDER_BG_WIDTH_F(width); + mask = BLENDER_BG_HEIGHT_MASK | BLENDER_BG_WIDTH_MASK; + decon_write_mask(id, BLENDER_BG_IMAGE_SIZE_0, val, mask); + +} + +static void decon_reg_set_data_path(u32 id, enum decon_data_path d_path, + enum decon_scaler_path s_path) +{ + u32 val, mask; + + val = SCALER_PATH_F(s_path) | COMP_OUTIF_PATH_F(d_path); + mask = SCALER_PATH_MASK | COMP_OUTIF_PATH_MASK; + decon_write_mask(id, DATA_PATH_CONTROL_2, val, mask); +} + +/* + * Check major configuration of data_path_control + * DSCC[7] + * DSC_ENC1[5] DSC_ENC0[4] + * DP_IF[3] + * DSIM_IF1[1] DSIM_IF0[0] + */ +static u32 decon_reg_get_data_path_cfg(u32 id, enum decon_path_cfg con_id) +{ + u32 val; + u32 d_path; + u32 bRet = 0; + + val = decon_read(id, DATA_PATH_CONTROL_2); + d_path = COMP_OUTIF_PATH_GET(val); + + switch (con_id) { + case PATH_CON_ID_DSCC_EN: + if (d_path & (0x1 << PATH_CON_ID_DSCC_EN)) + bRet = 1; + break; + case PATH_CON_ID_DUAL_DSC: + if ((d_path & (0x3 << PATH_CON_ID_DUAL_DSC)) == 0x30) + bRet = 1; + break; + case PATH_CON_ID_DP: + if (d_path & (0x3 << PATH_CON_ID_DP)) + bRet = 1; + break; + case PATH_CON_ID_DSIM_IF0: + if (d_path & (0x1 << PATH_CON_ID_DSIM_IF0)) + bRet = 1; + break; + case PATH_CON_ID_DSIM_IF1: + if (d_path & (0x1 << PATH_CON_ID_DSIM_IF1)) + bRet = 1; + break; + default: + break; + } + + return bRet; +} + +static void decon_reg_set_scaled_size(u32 id, u32 scaled_w, u32 scaled_h) +{ + u32 val, mask; + + val = SCALED_SIZE_HEIGHT_F(scaled_h) | + SCALED_SIZE_WIDTH_F(scaled_w); + mask = SCALED_SIZE_HEIGHT_MASK | SCALED_SIZE_WIDTH_MASK; + decon_write_mask(id, SCALED_SIZE_CONTROL_0, val, mask); +} + +/* + * width : width of updated LCD region + * height : height of updated LCD region + * is_dsc : 1: DSC is enabled 0: DSC is disabled + */ +static void decon_reg_set_data_path_size(u32 id, u32 width, u32 height, bool is_dsc, + u32 dsc_cnt, u32 slice_w, u32 slice_h) +{ + u32 outfifo_w; + + if (is_dsc) + outfifo_w = width / 3 / dsc_cnt; + else + outfifo_w = width; + + /* OUTFIFO size is compressed size if DSC is enabled */ + decon_reg_set_outfifo_size_ctl0(id, outfifo_w, height); + if (dsc_cnt == 2) + decon_reg_set_outfifo_size_ctl1(id, outfifo_w, 0); + if (is_dsc) + decon_reg_set_outfifo_size_ctl2(id, slice_w / 3, slice_h); + + /* + * SCALED size is updated LCD size if partial update is operating, + * this indicates partial size. + */ + decon_reg_set_scaled_size(id, width, height); +} + +/* + * 'DATA_PATH_CONTROL_2' SFR must be set before calling this function!! + * [width] + * - no compression : x-resolution + * - dsc compression : width_per_enc + */ +static void decon_reg_config_data_path_size(u32 id, + u32 width, u32 height, u32 overlap_w, + struct decon_dsc *p, struct decon_param *param) +{ + u32 dual_dsc = 0; + u32 dual_dsi = 0; + u32 dsim_if0 = 1; + u32 dsim_if1 = 0; + u32 width_f; + u32 sw, ds_en; + + dual_dsc = decon_reg_get_data_path_cfg(id, PATH_CON_ID_DUAL_DSC); + dsim_if0 = decon_reg_get_data_path_cfg(id, PATH_CON_ID_DSIM_IF0); + dsim_if1 = decon_reg_get_data_path_cfg(id, PATH_CON_ID_DSIM_IF1); + if (dsim_if0 && dsim_if1) + dual_dsi = 1; + + /* OUTFIFO */ + if (param->lcd_info->dsc_enabled) { + ds_en = (param->lcd_info->dsc_slice_num + / param->lcd_info->dsc_cnt == 2) ? 1 : 0; + /* only 8bpp case : check ceil */ + sw = CEIL(p->slice_width / 6) * 2; + width_f = (ds_en) ? sw * 2 : sw; + /* DSC 1EA */ + if (param->lcd_info->dsc_cnt == 1) { + decon_reg_set_outfifo_size_ctl0(id, width_f, height); + decon_reg_set_outfifo_size_ctl2(id, + sw, p->slice_height); + } else if (param->lcd_info->dsc_cnt == 2) { /* DSC 2EA */ + decon_reg_set_outfifo_size_ctl0(id, width_f, height); + decon_reg_set_outfifo_size_ctl1(id, width_f, 0); + decon_reg_set_outfifo_size_ctl2(id, + sw, p->slice_height); + } + } else { + decon_reg_set_outfifo_size_ctl0(id, width, height); + } +} + +/* + * [ CAUTION ] + * 'DATA_PATH_CONTROL_2' SFR must be set before calling this function!! + * + * [ Implementation Info about CONNECTION ] + * 1) DECON 0 - DSIMIF0 + * 2) DECON 0 - DSIMIF1 + * 3) DECON 1 - DSIMIF0 + * --> modify code if you want to change connection between dsim_if and dsim + */ + +/* + * later check. + * Dual DSI connection + * DP connection + * + */ + +static void decon_reg_set_interface(u32 id, struct decon_mode_info *psr) +{ + /* connection sfrs are changed in Lhotse */ + u32 val = DSIM_CONNECTION_DSIM0_F(0); + u32 mask = DSIM_CONNECTION_DSIM0_MASK; + u32 dsim_if0 = 1; + u32 dsim_if1 = 0; + u32 dual_dsi = 0; + + if (psr->out_type == DECON_OUT_DSI) { + dsim_if0 = decon_reg_get_data_path_cfg(id, PATH_CON_ID_DSIM_IF0); + dsim_if1 = decon_reg_get_data_path_cfg(id, PATH_CON_ID_DSIM_IF1); + if (dsim_if0 && dsim_if1) + dual_dsi = 1; + + if (dual_dsi) { + /* decon0 - (dsim_if0-dsim0) - (dsim_if1-dsim1) */ + val = DSIM_CONNECTION_DSIM0_F(0) + | DSIM_CONNECTION_DSIM1_F(1); + mask = DSIM_CONNECTION_DSIM0_MASK + | DSIM_CONNECTION_DSIM1_MASK; + } else { /* single dsi : DSIM0 only */ + if (dsim_if0) { + if (id == 0) { + /* DECON0 - DSIMIF0 - DSIM0 */ + val = DSIM_CONNECTION_DSIM0_F(0); + mask = DSIM_CONNECTION_DSIM0_MASK; + } else if (id == 1) { + /* DECON1 - DSIMIF0 - DSIM0 */ + val = DSIM_CONNECTION_DSIM0_F(2); + mask = DSIM_CONNECTION_DSIM0_MASK; + } + } + if (dsim_if1) { + if (id == 0) { + /* DECON0 - DSIMIF1 - DSIM0 */ + val = DSIM_CONNECTION_DSIM0_F(1); + mask = DSIM_CONNECTION_DSIM0_MASK; + } + } + } + + decon_write_mask(0, DSIM_CONNECTION_CONTROL, val, mask); + } else if (psr->out_type == DECON_OUT_DP) { + /* for MST support */ + if (id == 2) { + /* decon2 - DP0 : default for single DP */ + val = DP_CONNECTION_SEL_DP0(2); + mask = DP_CONNECTION_SEL_DP0_MASK; + } else if (id == 1) { + /* decon1 - DP1 */ + val = DP_CONNECTION_SEL_DP1(1); + mask = DP_CONNECTION_SEL_DP0_MASK; + } + + decon_write_mask(0, DP_CONNECTION_CONTROL, val, mask); + } +} + +static void decon_reg_set_bpc(u32 id, struct decon_lcd *lcd_info) +{ + u32 val = 0, mask; + + if (lcd_info->bpc == 10) + val = GLOBAL_CONTROL_TEN_BPC_MODE_F; + + mask = GLOBAL_CONTROL_TEN_BPC_MODE_MASK; + + decon_write_mask(id, GLOBAL_CONTROL, val, mask); +} + +static void decon_reg_update_req_window(u32 id, u32 win_idx) +{ + u32 mask; + + mask = SHADOW_REG_UPDATE_REQ_WIN(win_idx); + decon_write_mask(id, SHADOW_REG_UPDATE_REQ, ~0, mask); +} + +static void decon_reg_config_win_channel(u32 id, u32 win_idx, + enum decon_idma_type type) +{ + u32 ch_id; + u32 val, mask; + + ch_id = DPU_DMA2CH(type); + + val = WIN_CHMAP_F(win_idx, ch_id); + mask = WIN_CHMAP_MASK(win_idx); + decon_write_mask(id, DATA_PATH_CONTROL_1, val, mask); +} + +static void decon_reg_configure_trigger(u32 id, enum decon_trig_mode mode) +{ + u32 val, mask; + + mask = HW_TRIG_EN; + val = (mode == DECON_SW_TRIG) ? 0 : ~0; + decon_write_mask(id, HW_SW_TRIG_CONTROL, val, mask); +} + +static void dsc_reg_swreset(u32 dsc_id) +{ + dsc_write_mask(dsc_id, DSC_CONTROL0, 1, DSC_SW_RESET); +} + +static void dsc_reg_set_dcg_all(u32 dsc_id, u32 en) +{ + u32 val = 0; + + val = en ? DSC_DCG_EN_ALL_MASK : 0; + dsc_write_mask(dsc_id, DSC_CONTROL0, val, DSC_DCG_EN_ALL_MASK); +} + +static void dsc_reg_set_swap(u32 dsc_id, u32 bit_s, u32 byte_s, u32 word_s) +{ + u32 val; + + val = DSC_SWAP(bit_s, byte_s, word_s); + dsc_write_mask(dsc_id, DSC_CONTROL0, val, DSC_SWAP_MASK); +} + +static void dsc_reg_set_flatness_det_th(u32 dsc_id, u32 th) +{ + u32 val; + + val = DSC_FLATNESS_DET_TH_F(th); + dsc_write_mask(dsc_id, DSC_CONTROL0, val, DSC_FLATNESS_DET_TH_MASK); +} + +static void dsc_reg_set_slice_mode_change(u32 dsc_id, u32 en) +{ + u32 val; + + val = DSC_SLICE_MODE_CH_F(en); + dsc_write_mask(dsc_id, DSC_CONTROL0, val, DSC_SLICE_MODE_CH_MASK); +} + +static void dsc_reg_set_auto_clock_gate(u32 dsc_id, u32 en) +{ + u32 val; + + val = DSC_CG_EN_F(en); + dsc_write_mask(dsc_id, DSC_CONTROL0, val, DSC_CG_EN_MASK); +} + +static void dsc_reg_set_dual_slice(u32 dsc_id, u32 en) +{ + u32 val; + + val = DSC_DUAL_SLICE_EN_F(en); + dsc_write_mask(dsc_id, DSC_CONTROL0, val, DSC_DUAL_SLICE_EN_MASK); +} + +static void dsc_reg_set_remainder(u32 dsc_id, u32 remain) +{ + u32 val; + + val = DSC_REMAINDER_F(remain); + dsc_write_mask(dsc_id, DSC_CONTROL3, val, DSC_REMAINDER_MASK); +} + +static void dsc_reg_set_grpcntline(u32 dsc_id, u32 line) +{ + u32 val; + + val = DSC_GRPCNTLINE_F(line); + dsc_write_mask(dsc_id, DSC_CONTROL3, val, DSC_GRPCNTLINE_MASK); +} + +/* + * dsc PPS Configuration + */ + +/* + * APIs which user setting or calculation is required are implemented + * - PPS04 ~ PPS35 except reserved + * - PPS58 ~ PPS59 + */ +static void dsc_reg_set_pps_04_comp_cfg(u32 dsc_id, u32 comp_cfg) +{ + u32 val, mask; + + val = PPS04_COMP_CFG(comp_cfg); + mask = PPS04_COMP_CFG_MASK; + dsc_write_mask(dsc_id, DSC_PPS04_07, val, mask); +} + +static void dsc_reg_set_pps_05_bit_per_pixel(u32 dsc_id, u32 bpp) +{ + u32 val, mask; + + val = PPS05_BPP(bpp); + mask = PPS05_BPP_MASK; + dsc_write_mask(dsc_id, DSC_PPS04_07, val, mask); +} + +static void dsc_reg_set_pps_06_07_picture_height(u32 dsc_id, u32 height) +{ + u32 val, mask; + + val = PPS06_07_PIC_HEIGHT(height); + mask = PPS06_07_PIC_HEIGHT_MASK; + dsc_write_mask(dsc_id, DSC_PPS04_07, val, mask); +} + +static void dsc_reg_set_pps_08_09_picture_width(u32 dsc_id, u32 width) +{ + u32 val, mask; + + val = PPS08_09_PIC_WIDHT(width); + mask = PPS08_09_PIC_WIDHT_MASK; + dsc_write_mask(dsc_id, DSC_PPS08_11, val, mask); +} + +static void dsc_reg_set_pps_10_11_slice_height(u32 dsc_id, u32 slice_height) +{ + u32 val, mask; + + val = PPS10_11_SLICE_HEIGHT(slice_height); + mask = PPS10_11_SLICE_HEIGHT_MASK; + dsc_write_mask(dsc_id, DSC_PPS08_11, val, mask); +} + +static void dsc_reg_set_pps_12_13_slice_width(u32 dsc_id, u32 slice_width) +{ + u32 val, mask; + + val = PPS12_13_SLICE_WIDTH(slice_width); + mask = PPS12_13_SLICE_WIDTH_MASK; + dsc_write_mask(dsc_id, DSC_PPS12_15, val, mask); +} + +/* chunk_size = slice_width */ +static void dsc_reg_set_pps_14_15_chunk_size(u32 dsc_id, u32 chunk_size) +{ + u32 val, mask; + + val = PPS14_15_CHUNK_SIZE(chunk_size); + mask = PPS14_15_CHUNK_SIZE_MASK; + dsc_write_mask(dsc_id, DSC_PPS12_15, val, mask); +} + +static void dsc_reg_set_pps_16_17_init_xmit_delay(u32 dsc_id, u32 xmit_delay) +{ + u32 val, mask; + + val = PPS16_17_INIT_XMIT_DELAY(xmit_delay); + mask = PPS16_17_INIT_XMIT_DELAY_MASK; + dsc_write_mask(dsc_id, DSC_PPS16_19, val, mask); +} + +static void dsc_reg_set_pps_18_19_init_dec_delay(u32 dsc_id, u32 dec_delay) +{ + u32 val, mask; + + val = PPS18_19_INIT_DEC_DELAY(dec_delay); + mask = PPS18_19_INIT_DEC_DELAY_MASK; + dsc_write_mask(dsc_id, DSC_PPS16_19, val, mask); +} + +static void dsc_reg_set_pps_21_initial_scale_value(u32 dsc_id, u32 scale_value) +{ + u32 val, mask; + + val = PPS21_INIT_SCALE_VALUE(scale_value); + mask = PPS21_INIT_SCALE_VALUE_MASK; + dsc_write_mask(dsc_id, DSC_PPS20_23, val, mask); +} + +static void dsc_reg_set_pps_22_23_scale_increment_interval(u32 dsc_id, u32 sc_inc) +{ + u32 val, mask; + + val = PPS22_23_SCALE_INC_INTERVAL(sc_inc); + mask = PPS22_23_SCALE_INC_INTERVAL_MASK; + dsc_write_mask(dsc_id, DSC_PPS20_23, val, mask); +} + +static void dsc_reg_set_pps_24_25_scale_decrement_interval(u32 dsc_id, u32 sc_dec) +{ + u32 val, mask; + + val = PPS24_25_SCALE_DEC_INTERVAL(sc_dec); + mask = PPS24_25_SCALE_DEC_INTERVAL_MASK; + dsc_write_mask(dsc_id, DSC_PPS24_27, val, mask); +} + +static void dsc_reg_set_pps_27_first_line_bpg_offset(u32 dsc_id, u32 fl_bpg_off) +{ + u32 val, mask; + + val = PPS27_FL_BPG_OFFSET(fl_bpg_off); + mask = PPS27_FL_BPG_OFFSET_MASK; + dsc_write_mask(dsc_id, DSC_PPS24_27, val, mask); +} + +static void dsc_reg_set_pps_28_29_nfl_bpg_offset(u32 dsc_id, u32 nfl_bpg_off) +{ + u32 val, mask; + + val = PPS28_29_NFL_BPG_OFFSET(nfl_bpg_off); + mask = PPS28_29_NFL_BPG_OFFSET_MASK; + dsc_write_mask(dsc_id, DSC_PPS28_31, val, mask); +} + +static void dsc_reg_set_pps_30_31_slice_bpg_offset(u32 dsc_id, u32 slice_bpg_off) +{ + u32 val, mask; + + val = PPS30_31_SLICE_BPG_OFFSET(slice_bpg_off); + mask = PPS30_31_SLICE_BPG_OFFSET_MASK; + dsc_write_mask(dsc_id, DSC_PPS28_31, val, mask); +} + +static void dsc_reg_set_pps_32_33_initial_offset(u32 dsc_id, u32 init_off) +{ + u32 val, mask; + + val = PPS32_33_INIT_OFFSET(init_off); + mask = PPS32_33_INIT_OFFSET_MASK; + dsc_write_mask(dsc_id, DSC_PPS32_35, val, mask); +} + +static void dsc_reg_set_pps_34_35_final_offset(u32 dsc_id, u32 fin_off) +{ + u32 val, mask; + + val = PPS34_35_FINAL_OFFSET(fin_off); + mask = PPS34_35_FINAL_OFFSET_MASK; + dsc_write_mask(dsc_id, DSC_PPS32_35, val, mask); +} + +static void dsc_reg_set_pps_58_59_rc_range_param0(u32 dsc_id, u32 rc_range_param) +{ + u32 val, mask; + + val = PPS58_59_RC_RANGE_PARAM(rc_range_param); + mask = PPS58_59_RC_RANGE_PARAM_MASK; + dsc_write_mask(dsc_id, DSC_PPS56_59, val, mask); +} + +/* full size default value */ +static u32 dsc_get_dual_slice_mode(struct decon_lcd *lcd_info) +{ + u32 dual_slice_en = 0; + + if (lcd_info->dsc_cnt == 1) { + if (lcd_info->dsc_slice_num == 2) + dual_slice_en = 1; + } else if (lcd_info->dsc_cnt == 2) { + if (lcd_info->dsc_slice_num == 4) + dual_slice_en = 1; + } else { + dual_slice_en = 0; + } + + return dual_slice_en; +} + +/* full size default value */ +static u32 dsc_get_slice_mode_change(struct decon_lcd *lcd_info) +{ + u32 slice_mode_ch = 0; + + if (lcd_info->dsc_cnt == 2) { + if (lcd_info->dsc_slice_num == 2) + slice_mode_ch = 1; + } + + return slice_mode_ch; +} + +static void dsc_get_partial_update_info(u32 slice_cnt, u32 dsc_cnt, bool in_slice[4], + u32 ds_en[2], u32 sm_ch[2]) +{ + switch (slice_cnt) { + case 4: + if ((in_slice[0] + in_slice[1]) % 2) { + ds_en[DECON_DSC_ENC0] = 0; + sm_ch[DECON_DSC_ENC0] = 1; + } else { + ds_en[DECON_DSC_ENC0] = 1; + sm_ch[DECON_DSC_ENC0] = 0; + } + + if ((in_slice[2] + in_slice[3]) % 2) { + ds_en[DECON_DSC_ENC1] = 0; + sm_ch[DECON_DSC_ENC1] = 1; + } else { + ds_en[DECON_DSC_ENC1] = 1; + sm_ch[DECON_DSC_ENC1] = 0; + } + + break; + case 2: + if (dsc_cnt == 2) { + ds_en[DECON_DSC_ENC0] = 0; + sm_ch[DECON_DSC_ENC0] = 1; + + ds_en[DECON_DSC_ENC1] = 0; + sm_ch[DECON_DSC_ENC1] = 1; + } else { + if (in_slice[0]) { + ds_en[DECON_DSC_ENC0] = 0; + sm_ch[DECON_DSC_ENC0] = 1; + } else if (in_slice[1]) { + ds_en[DECON_DSC_ENC0] = 0; + sm_ch[DECON_DSC_ENC0] = 1; + } else { + ds_en[DECON_DSC_ENC0] = 1; + sm_ch[DECON_DSC_ENC0] = 0; + } + + ds_en[DECON_DSC_ENC1] = ds_en[DECON_DSC_ENC0]; + sm_ch[DECON_DSC_ENC1] = sm_ch[DECON_DSC_ENC0]; + } + break; + case 1: + ds_en[DECON_DSC_ENC0] = 0; + sm_ch[DECON_DSC_ENC0] = 0; + + ds_en[DECON_DSC_ENC1] = 0; + sm_ch[DECON_DSC_ENC1] = 0; + break; + default: + decon_err("Not specified case for Partial Update in DSC!\n"); + break; + } +} + +static void dsc_reg_config_control(u32 dsc_id, u32 ds_en, u32 sm_ch) +{ + dsc_reg_set_dcg_all(dsc_id, 0); /* No clock gating */ + dsc_reg_set_swap(dsc_id, 0x0, 0x1, 0x0); + /* flatness detection is fixed 2@8bpc / 8@10bpc / 32@12bpc */ + dsc_reg_set_flatness_det_th(dsc_id, 0x2); + dsc_reg_set_auto_clock_gate(dsc_id, 0); /* No auto clock gating */ + dsc_reg_set_dual_slice(dsc_id, ds_en); + dsc_reg_set_slice_mode_change(dsc_id, sm_ch); +} + +static void dsc_reg_config_control_width(u32 dsc_id, u32 slice_width) +{ + + u32 dsc_remainder; + u32 dsc_grpcntline; + + if (slice_width % 3) + dsc_remainder = slice_width % 3; + else + dsc_remainder = 3; + + dsc_reg_set_remainder(dsc_id, dsc_remainder); + dsc_grpcntline = (slice_width + 2) / 3; + dsc_reg_set_grpcntline(dsc_id, dsc_grpcntline); +} + +/* + * overlap_w + * - default : 0 + * - range : [0, 32] & (multiples of 2) + * if non-zero value is applied, this means slice_w increasing. + * therefore, DECON & DSIM setting must also be aligned. + * --> must check if DDI module is supporting this feature !!! + */ +static void dsc_calc_pps_info(struct decon_lcd *lcd_info, u32 dscc_en, + struct decon_dsc *dsc_enc) +{ + u32 width, height; + u32 slice_width, slice_height; + u32 pic_width, pic_height; + u32 width_eff; + u32 dual_slice_en = 0; + u32 bpp, chunk_size; + u32 slice_bits; + u32 groups_per_line, groups_total; + + /* initial values, also used for other pps calcualtion */ + u32 rc_model_size = 0x2000; + u32 num_extra_mux_bits = 246; + u32 initial_xmit_delay = 0x200; + u32 initial_dec_delay = 0x4c0; + /* when 'slice_w >= 70' */ + u32 initial_scale_value = 0x20; + u32 first_line_bpg_offset = 0x0c; + u32 initial_offset = 0x1800; + u32 rc_range_parameters = 0x0102; + + u32 final_offset, final_scale; + u32 flag, nfl_bpg_offset, slice_bpg_offset; + u32 scale_increment_interval, scale_decrement_interval; + u32 slice_width_byte_unit, comp_slice_width_byte_unit; + u32 comp_slice_width_pixel_unit; + u32 overlap_w = 0; + u32 dsc_enc0_w = 0, dsc_enc0_h; + u32 dsc_enc1_w = 0, dsc_enc1_h; + u32 i, j; + + width = lcd_info->xres; + height = lcd_info->yres; + + overlap_w = dsc_enc->overlap_w; + + if (dscc_en) + /* OVERLAP can be used in the dual-slice case (if one ENC) */ + width_eff = (width >> 1) + overlap_w; + else + width_eff = width + overlap_w; + + pic_width = width_eff; + dual_slice_en = dsc_get_dual_slice_mode(lcd_info); + if (dual_slice_en) + slice_width = width_eff >> 1; + else + slice_width = width_eff; + + pic_height = height; + slice_height = lcd_info->dsc_slice_h; + + bpp = 8; + chunk_size = slice_width; + slice_bits = 8 * chunk_size * slice_height; + + while ((slice_bits - num_extra_mux_bits) % 48) + num_extra_mux_bits--; + + groups_per_line = (slice_width + 2) / 3; + groups_total = groups_per_line * slice_height; + + final_offset = rc_model_size - ((initial_xmit_delay * (8<<4) + 8)>>4) + + num_extra_mux_bits; + final_scale = 8 * rc_model_size / (rc_model_size - final_offset); + + flag = (first_line_bpg_offset * 2048) % (slice_height - 1); + nfl_bpg_offset = (first_line_bpg_offset * 2048) / (slice_height - 1); + if (flag) + nfl_bpg_offset = nfl_bpg_offset + 1; + + flag = 2048 * (rc_model_size - initial_offset + num_extra_mux_bits) + % groups_total; + slice_bpg_offset = 2048 + * (rc_model_size - initial_offset + num_extra_mux_bits) + / groups_total; + if (flag) + slice_bpg_offset = slice_bpg_offset + 1; + + scale_increment_interval = (2048 * final_offset) / ((final_scale - 9) + * (nfl_bpg_offset + slice_bpg_offset)); + scale_decrement_interval = groups_per_line / (initial_scale_value - 8); + + /* 3bytes per pixel */ + slice_width_byte_unit = slice_width * 3; + /* integer value, /3 for 1/3 compression */ + comp_slice_width_byte_unit = slice_width_byte_unit / 3; + /* integer value, /3 for pixel unit */ + comp_slice_width_pixel_unit = comp_slice_width_byte_unit / 3; + + i = comp_slice_width_byte_unit % 3; + j = comp_slice_width_pixel_unit % 2; + + if (i == 0 && j == 0) { + dsc_enc0_w = comp_slice_width_pixel_unit; + dsc_enc0_h = pic_height; + if (dscc_en) { + dsc_enc1_w = comp_slice_width_pixel_unit; + dsc_enc1_h = pic_height; + } + } else if (i == 0 && j != 0) { + dsc_enc0_w = comp_slice_width_pixel_unit + 1; + dsc_enc0_h = pic_height; + if (dscc_en) { + dsc_enc1_w = comp_slice_width_pixel_unit + 1; + dsc_enc1_h = pic_height; + } + } else if (i != 0) { + while (1) { + comp_slice_width_pixel_unit++; + j = comp_slice_width_pixel_unit % 2; + if (j == 0) + break; + } + dsc_enc0_w = comp_slice_width_pixel_unit; + dsc_enc0_h = pic_height; + if (dscc_en) { + dsc_enc1_w = comp_slice_width_pixel_unit; + dsc_enc1_h = pic_height; + } + } + + if (dual_slice_en) { + dsc_enc0_w = dsc_enc0_w * 2; + if (dscc_en) + dsc_enc1_w = dsc_enc1_w * 2; + } + + /* Save information to structure variable */ + dsc_enc->comp_cfg = 0x30; + dsc_enc->bit_per_pixel = bpp << 4; + dsc_enc->pic_height = pic_height; + dsc_enc->pic_width = pic_width; + dsc_enc->slice_height = slice_height; + dsc_enc->slice_width = slice_width; + dsc_enc->chunk_size = chunk_size; + dsc_enc->initial_xmit_delay = initial_xmit_delay; + dsc_enc->initial_dec_delay = initial_dec_delay; + dsc_enc->initial_scale_value = initial_scale_value; + dsc_enc->scale_increment_interval = scale_increment_interval; + dsc_enc->scale_decrement_interval = scale_decrement_interval; + dsc_enc->first_line_bpg_offset = first_line_bpg_offset; + dsc_enc->nfl_bpg_offset = nfl_bpg_offset; + dsc_enc->slice_bpg_offset = slice_bpg_offset; + dsc_enc->initial_offset = initial_offset; + dsc_enc->final_offset = final_offset; + dsc_enc->rc_range_parameters = rc_range_parameters; + + dsc_enc->width_per_enc = dsc_enc0_w; +} + +static void dsc_reg_set_pps(u32 dsc_id, struct decon_dsc *dsc_enc) +{ + dsc_reg_set_pps_04_comp_cfg(dsc_id, dsc_enc->comp_cfg); + dsc_reg_set_pps_05_bit_per_pixel(dsc_id, dsc_enc->bit_per_pixel); + dsc_reg_set_pps_06_07_picture_height(dsc_id, dsc_enc->pic_height); + + dsc_reg_set_pps_08_09_picture_width(dsc_id, dsc_enc->pic_width); + dsc_reg_set_pps_10_11_slice_height(dsc_id, dsc_enc->slice_height); + dsc_reg_set_pps_12_13_slice_width(dsc_id, dsc_enc->slice_width); + dsc_reg_set_pps_14_15_chunk_size(dsc_id, dsc_enc->chunk_size); + + dsc_reg_set_pps_16_17_init_xmit_delay(dsc_id, + dsc_enc->initial_xmit_delay); +#ifndef VESA_SCR_V4 + dsc_reg_set_pps_18_19_init_dec_delay(dsc_id, 0x01B4); +#else + dsc_reg_set_pps_18_19_init_dec_delay(dsc_id, + dsc_enc->initial_dec_delay); +#endif + dsc_reg_set_pps_21_initial_scale_value(dsc_id, + dsc_enc->initial_scale_value); + + dsc_reg_set_pps_22_23_scale_increment_interval(dsc_id, + dsc_enc->scale_increment_interval); + dsc_reg_set_pps_24_25_scale_decrement_interval(dsc_id, + dsc_enc->scale_decrement_interval); + + dsc_reg_set_pps_27_first_line_bpg_offset(dsc_id, + dsc_enc->first_line_bpg_offset); + dsc_reg_set_pps_28_29_nfl_bpg_offset(dsc_id, dsc_enc->nfl_bpg_offset); + + dsc_reg_set_pps_30_31_slice_bpg_offset(dsc_id, + dsc_enc->slice_bpg_offset); + dsc_reg_set_pps_32_33_initial_offset(dsc_id, dsc_enc->initial_offset); + dsc_reg_set_pps_34_35_final_offset(dsc_id, dsc_enc->final_offset); + + /* min_qp0 = 0 , max_qp0 = 4 , bpg_off0 = 2 */ + dsc_reg_set_pps_58_59_rc_range_param0(dsc_id, + dsc_enc->rc_range_parameters); +#ifndef VESA_SCR_V4 + /* PPS79 ~ PPS87 : 3HF4 is different with VESA SCR v4 */ + dsc_write(dsc_id, 0x006C, 0x1AB62AF6); + dsc_write(dsc_id, 0x0070, 0x2B342B74); + dsc_write(dsc_id, 0x0074, 0x3B746BF4); +#endif +} + +/* + * Following PPS SFRs will be set from DDI PPS Table (DSC Decoder) + * : not 'fix' type + * - PPS04 ~ PPS35 + * - PPS58 ~ PPS59 + * SEQ_PPS_SLICE4[] @ s6e3hf4_param.h + */ +static void dsc_get_decoder_pps_info(struct decon_dsc *dsc_dec, + const unsigned char pps_t[90]) +{ + dsc_dec->comp_cfg = (u32) pps_t[4]; + dsc_dec->bit_per_pixel = (u32) pps_t[5]; + dsc_dec->pic_height = (u32) (pps_t[6] << 8 | pps_t[7]); + dsc_dec->pic_width = (u32) (pps_t[8] << 8 | pps_t[9]); + dsc_dec->slice_height = (u32) (pps_t[10] << 8 | pps_t[11]); + dsc_dec->slice_width = (u32) (pps_t[12] << 8 | pps_t[13]); + dsc_dec->chunk_size = (u32) (pps_t[14] << 8 | pps_t[15]); + dsc_dec->initial_xmit_delay = (u32) (pps_t[16] << 8 | pps_t[17]); + dsc_dec->initial_dec_delay = (u32) (pps_t[18] << 8 | pps_t[19]); + dsc_dec->initial_scale_value = (u32) pps_t[21]; + dsc_dec->scale_increment_interval = (u32) (pps_t[22] << 8 | pps_t[23]); + dsc_dec->scale_decrement_interval = (u32) (pps_t[24] << 8 | pps_t[25]); + dsc_dec->first_line_bpg_offset = (u32) pps_t[27]; + dsc_dec->nfl_bpg_offset = (u32) (pps_t[28] << 8 | pps_t[29]); + dsc_dec->slice_bpg_offset = (u32) (pps_t[30] << 8 | pps_t[31]); + dsc_dec->initial_offset = (u32) (pps_t[32] << 8 | pps_t[33]); + dsc_dec->final_offset = (u32) (pps_t[34] << 8 | pps_t[35]); + dsc_dec->rc_range_parameters = (u32) (pps_t[58] << 8 | pps_t[59]); +} + +static u32 dsc_cmp_pps_enc_dec(struct decon_dsc *p_enc, struct decon_dsc *p_dec) +{ + u32 diff_cnt = 0; + + if (p_enc->comp_cfg != p_dec->comp_cfg) { + diff_cnt++; + decon_dbg("[dsc_pps] comp_cfg (enc:dec = %d:%d)\n", + p_enc->comp_cfg, p_dec->comp_cfg); + } + if (p_enc->bit_per_pixel != p_dec->bit_per_pixel) { + diff_cnt++; + decon_dbg("[dsc_pps] bit_per_pixel (enc:dec = %d:%d)\n", + p_enc->bit_per_pixel, p_dec->bit_per_pixel); + } + if (p_enc->pic_height != p_dec->pic_height) { + diff_cnt++; + decon_dbg("[dsc_pps] pic_height (enc:dec = %d:%d)\n", + p_enc->pic_height, p_dec->pic_height); + } + if (p_enc->pic_width != p_dec->pic_width) { + diff_cnt++; + decon_dbg("[dsc_pps] pic_width (enc:dec = %d:%d)\n", + p_enc->pic_width, p_dec->pic_width); + } + if (p_enc->slice_height != p_dec->slice_height) { + diff_cnt++; + decon_dbg("[dsc_pps] slice_height (enc:dec = %d:%d)\n", + p_enc->slice_height, p_dec->slice_height); + } + if (p_enc->slice_width != p_dec->slice_width) { + diff_cnt++; + decon_dbg("[dsc_pps] slice_width (enc:dec = %d:%d)\n", + p_enc->slice_width, p_dec->slice_width); + } + if (p_enc->chunk_size != p_dec->chunk_size) { + diff_cnt++; + decon_dbg("[dsc_pps] chunk_size (enc:dec = %d:%d)\n", + p_enc->chunk_size, p_dec->chunk_size); + } + if (p_enc->initial_xmit_delay != p_dec->initial_xmit_delay) { + diff_cnt++; + decon_dbg("[dsc_pps] initial_xmit_delay (enc:dec = %d:%d)\n", + p_enc->initial_xmit_delay, p_dec->initial_xmit_delay); + } + if (p_enc->initial_dec_delay != p_dec->initial_dec_delay) { + diff_cnt++; + decon_dbg("[dsc_pps] initial_dec_delay (enc:dec = %d:%d)\n", + p_enc->initial_dec_delay, p_dec->initial_dec_delay); + } + if (p_enc->initial_scale_value != p_dec->initial_scale_value) { + diff_cnt++; + decon_dbg("[dsc_pps] initial_scale_value (enc:dec = %d:%d)\n", + p_enc->initial_scale_value, + p_dec->initial_scale_value); + } + if (p_enc->scale_increment_interval != + p_dec->scale_increment_interval) { + diff_cnt++; + decon_dbg("[dsc_pps] scale_inc_interval (enc:dec = %d:%d)\n", + p_enc->scale_increment_interval, + p_dec->scale_increment_interval); + } + if (p_enc->scale_decrement_interval != + p_dec->scale_decrement_interval) { + diff_cnt++; + decon_dbg("[dsc_pps] scale_dec_interval (enc:dec = %d:%d)\n", + p_enc->scale_decrement_interval, + p_dec->scale_decrement_interval); + } + if (p_enc->first_line_bpg_offset != p_dec->first_line_bpg_offset) { + diff_cnt++; + decon_dbg("[dsc_pps] first_line_bpg_offset (enc:dec = %d:%d)\n", + p_enc->first_line_bpg_offset, + p_dec->first_line_bpg_offset); + } + if (p_enc->nfl_bpg_offset != p_dec->nfl_bpg_offset) { + diff_cnt++; + decon_dbg("[dsc_pps] nfl_bpg_offset (enc:dec = %d:%d)\n", + p_enc->nfl_bpg_offset, p_dec->nfl_bpg_offset); + } + if (p_enc->slice_bpg_offset != p_dec->slice_bpg_offset) { + diff_cnt++; + decon_dbg("[dsc_pps] slice_bpg_offset (enc:dec = %d:%d)\n", + p_enc->slice_bpg_offset, p_dec->slice_bpg_offset); + } + if (p_enc->initial_offset != p_dec->initial_offset) { + diff_cnt++; + decon_dbg("[dsc_pps] initial_offset (enc:dec = %d:%d)\n", + p_enc->initial_offset, p_dec->initial_offset); + } + if (p_enc->final_offset != p_dec->final_offset) { + diff_cnt++; + decon_dbg("[dsc_pps] final_offset (enc:dec = %d:%d)\n", + p_enc->final_offset, p_dec->final_offset); + } + if (p_enc->rc_range_parameters != p_dec->rc_range_parameters) { + diff_cnt++; + decon_dbg("[dsc_pps] rc_range_parameters (enc:dec = %d:%d)\n", + p_enc->rc_range_parameters, + p_dec->rc_range_parameters); + } + + decon_dbg("[dsc_pps] total different count : %d\n", diff_cnt); + + return diff_cnt; +} + +static void dsc_reg_set_partial_update(u32 dsc_id, u32 dual_slice_en, + u32 slice_mode_ch, u32 pic_h) +{ + /* + * Following SFRs must be considered + * - dual_slice_en + * - slice_mode_change + * - picture_height + * - picture_width (don't care @KC) : decided by DSI (-> dual: /2) + */ + dsc_reg_set_dual_slice(dsc_id, dual_slice_en); + dsc_reg_set_slice_mode_change(dsc_id, slice_mode_ch); + dsc_reg_set_pps_06_07_picture_height(dsc_id, pic_h); +} + +/* + * This table is only used to check DSC setting value when debugging + * Copy or Replace table's data from current using LCD information + * ( e.g. : SEQ_PPS_SLICE4 @ s6e3hf4_param.h ) + */ +static const unsigned char DDI_PPS_INFO[] = { + 0x11, 0x00, 0x00, 0x89, 0x30, + 0x80, 0x0A, 0x00, 0x05, 0xA0, + 0x00, 0x40, 0x01, 0x68, 0x01, + 0x68, 0x02, 0x00, 0x01, 0xB4, + + 0x00, 0x20, 0x04, 0xF2, 0x00, + 0x05, 0x00, 0x0C, 0x01, 0x87, + 0x02, 0x63, 0x18, 0x00, 0x10, + 0xF0, 0x03, 0x0C, 0x20, 0x00, + + 0x06, 0x0B, 0x0B, 0x33, 0x0E, + 0x1C, 0x2A, 0x38, 0x46, 0x54, + 0x62, 0x69, 0x70, 0x77, 0x79, + 0x7B, 0x7D, 0x7E, 0x01, 0x02, + + 0x01, 0x00, 0x09, 0x40, 0x09, + 0xBE, 0x19, 0xFC, 0x19, 0xFA, + 0x19, 0xF8, 0x1A, 0x38, 0x1A, + 0x78, 0x1A, 0xB6, 0x2A, 0xF6, + + 0x2B, 0x34, 0x2B, 0x74, 0x3B, + 0x74, 0x6B, 0xF4, 0x00, 0x00 +}; + +static void dsc_reg_set_encoder(u32 id, struct decon_param *p, + struct decon_dsc *dsc_enc, u32 chk_en) +{ + u32 dsc_id; + u32 dscc_en = 1; + u32 ds_en = 0; + u32 sm_ch = 0; + struct decon_lcd *lcd_info = p->lcd_info; + /* DDI PPS table : for compare with ENC PPS value */ + struct decon_dsc dsc_dec; + /* set corresponding table like 'SEQ_PPS_SLICE4' */ + const unsigned char *pps_t = DDI_PPS_INFO; + + ds_en = dsc_get_dual_slice_mode(lcd_info); + decon_dbg("dual slice(%d)\n", ds_en); + + sm_ch = dsc_get_slice_mode_change(lcd_info); + decon_dbg("slice mode change(%d)\n", sm_ch); + + dscc_en = decon_reg_get_data_path_cfg(id, PATH_CON_ID_DSCC_EN); + dsc_calc_pps_info(lcd_info, dscc_en, dsc_enc); + + if (id == 1) { + dsc_reg_config_control(DECON_DSC_ENC1, ds_en, sm_ch); + dsc_reg_config_control_width(DECON_DSC_ENC1, + dsc_enc->slice_width); + dsc_reg_set_pps(DECON_DSC_ENC1, dsc_enc); + } else if (id == 2) { /* only for DP */ + dsc_reg_config_control(DECON_DSC_ENC2, ds_en, sm_ch); + dsc_reg_config_control_width(DECON_DSC_ENC2, + dsc_enc->slice_width); + dsc_reg_set_pps(DECON_DSC_ENC2, dsc_enc); + } else { + for (dsc_id = 0; dsc_id < lcd_info->dsc_cnt; dsc_id++) { + dsc_reg_config_control(dsc_id, ds_en, sm_ch); + dsc_reg_config_control_width(dsc_id, + dsc_enc->slice_width); + dsc_reg_set_pps(dsc_id, dsc_enc); + } + } + + if (chk_en) { + dsc_get_decoder_pps_info(&dsc_dec, pps_t); + if (dsc_cmp_pps_enc_dec(dsc_enc, &dsc_dec)) + decon_dbg("[WARNING] Check PPS value!!\n"); + } + +} + +static int dsc_reg_init(u32 id, struct decon_param *p, u32 overlap_w, u32 swrst) +{ + u32 dsc_id; + struct decon_lcd *lcd_info = p->lcd_info; + struct decon_dsc dsc_enc; + + /* Basically, all SW-resets in DPU are not necessary */ + if (swrst) { + for (dsc_id = 0; dsc_id < lcd_info->dsc_cnt; dsc_id++) + dsc_reg_swreset(dsc_id); + } + + dsc_enc.overlap_w = overlap_w; + dsc_reg_set_encoder(id, p, &dsc_enc, 0); + decon_reg_config_data_path_size(id, + dsc_enc.width_per_enc, lcd_info->yres, overlap_w, &dsc_enc, p); + + return 0; +} + +static void decon_reg_clear_int_all(u32 id) +{ + u32 mask; + + mask = (DPU_FRAME_DONE_INT_EN + | DPU_FRAME_START_INT_EN); + decon_write_mask(id, INTERRUPT_PENDING, ~0, mask); + + mask = (DPU_RESOURCE_CONFLICT_INT_EN + | DPU_TIME_OUT_INT_EN); + decon_write_mask(id, EXTRA_INTERRUPT_PENDING, ~0, mask); +} + +static void decon_reg_configure_lcd(u32 id, struct decon_param *p) +{ + u32 overlap_w = 0; + enum decon_data_path d_path = DPATH_DSCENC0_OUTFIFO0_DSIMIF0; + enum decon_scaler_path s_path = SCALERPATH_OFF; + + struct decon_lcd *lcd_info = p->lcd_info; + struct decon_mode_info *psr = &p->psr; + enum decon_dsi_mode dsi_mode = psr->dsi_mode; + enum decon_rgb_order rgb_order = DECON_RGB; + + if ((psr->out_type == DECON_OUT_DSI) + && !(lcd_info->dsc_enabled)) + rgb_order = DECON_BGR; + else + rgb_order = DECON_RGB; + decon_reg_set_rgb_order(id, rgb_order); + + if (lcd_info->dsc_enabled) { + if (lcd_info->dsc_cnt == 1) + d_path = (id == 0) ? + DPATH_DSCENC0_OUTFIFO0_DSIMIF0 : + DECON2_DSCENC2_OUTFIFO0_DPIF; + else if (lcd_info->dsc_cnt == 2 && !id) + d_path = DPATH_DSCC_DSCENC01_OUTFIFO01_DSIMIF0; + else + decon_err("[decon%d] dsc_cnt=%d : not supported\n", + id, lcd_info->dsc_cnt); + + decon_reg_set_data_path(id, d_path, s_path); + /* call decon_reg_config_data_path_size () inside */ + dsc_reg_init(id, p, overlap_w, 0); + } else { + if (dsi_mode == DSI_MODE_DUAL_DSI) + d_path = DPATH_NOCOMP_SPLITTER_OUTFIFO01_DSIMIF01; + else + d_path = (id == 0) ? + DPATH_NOCOMP_OUTFIFO0_DSIMIF0 : + DECON2_NOCOMP_OUTFIFO0_DPIF; + + decon_reg_set_data_path(id, d_path, s_path); + + decon_reg_config_data_path_size(id, + lcd_info->xres, lcd_info->yres, overlap_w, NULL, p); + + if (id == 2) + decon_reg_set_bpc(id, lcd_info); + } + + decon_reg_per_frame_off(id); +} + +static void decon_reg_init_probe(u32 id, u32 dsi_idx, struct decon_param *p) +{ + struct decon_lcd *lcd_info = p->lcd_info; + struct decon_mode_info *psr = &p->psr; + enum decon_data_path d_path = DPATH_DSCENC0_OUTFIFO0_DSIMIF0; + enum decon_scaler_path s_path = SCALERPATH_OFF; + enum decon_rgb_order rgb_order = DECON_RGB; + enum decon_dsi_mode dsi_mode = psr->dsi_mode; + u32 overlap_w = 0; /* default=0 : range=[0, 32] & (multiples of 2) */ + + dpu_reg_set_qactive_pll(id, true); + + decon_reg_set_clkgate_mode(id, 0); + + decon_reg_set_sram_share(id, DECON_FIFO_04K); + + decon_reg_set_operation_mode(id, psr->psr_mode); + + decon_reg_set_blender_bg_image_size(id, psr->dsi_mode, lcd_info); + + decon_reg_set_scaled_image_size(id, psr->dsi_mode, lcd_info); + + /* + * same as decon_reg_configure_lcd(...) function + * except using decon_reg_update_req_global(id) + * instead of decon_reg_direct_on_off(id, 0) + */ + if (lcd_info->dsc_enabled) + rgb_order = DECON_RGB; + else + rgb_order = DECON_BGR; + decon_reg_set_rgb_order(id, rgb_order); + + if (lcd_info->dsc_enabled) { + if (lcd_info->dsc_cnt == 1) + d_path = (id == 0) ? + DPATH_DSCENC0_OUTFIFO0_DSIMIF0 : + DECON2_DSCENC2_OUTFIFO0_DPIF; + else if (lcd_info->dsc_cnt == 2 && !id) + d_path = DPATH_DSCC_DSCENC01_OUTFIFO01_DSIMIF0; + else + decon_err("[decon%d] dsc_cnt=%d : not supported\n", + id, lcd_info->dsc_cnt); + + decon_reg_set_data_path(id, d_path, s_path); + /* call decon_reg_config_data_path_size () inside */ + dsc_reg_init(id, p, overlap_w, 0); + } else { + if (dsi_mode == DSI_MODE_DUAL_DSI) + d_path = DPATH_NOCOMP_SPLITTER_OUTFIFO01_DSIMIF01; + else + d_path = (id == 0) ? + DPATH_NOCOMP_OUTFIFO0_DSIMIF0 : + DECON2_NOCOMP_OUTFIFO0_DPIF; + + decon_reg_set_data_path(id, d_path, s_path); + + decon_reg_config_data_path_size(id, + lcd_info->xres, lcd_info->yres, overlap_w, NULL, p); + } +} + + +static void decon_reg_set_blender_bg_size(u32 id, enum decon_dsi_mode dsi_mode, + u32 bg_w, u32 bg_h) +{ + u32 width, val, mask; + + width = bg_w; + + if (dsi_mode == DSI_MODE_DUAL_DSI) + width = width * 2; + + val = BLENDER_BG_HEIGHT_F(bg_h) | BLENDER_BG_WIDTH_F(width); + mask = BLENDER_BG_HEIGHT_MASK | BLENDER_BG_WIDTH_MASK; + decon_write_mask(id, BLENDER_BG_IMAGE_SIZE_0, val, mask); +} + +static int decon_reg_stop_perframe(u32 id, u32 dsi_idx, + struct decon_mode_info *psr, u32 fps) +{ + int ret = 0; + int timeout_value = 0; + + decon_dbg("%s +\n", __func__); + + if ((psr->psr_mode == DECON_MIPI_COMMAND_MODE) && + (psr->trig_mode == DECON_HW_TRIG)) { + decon_reg_set_trigger(id, psr, DECON_TRIG_DISABLE); + } + + /* perframe stop */ + decon_reg_per_frame_off(id); + + decon_reg_update_req_global(id); + + /* timeout : 1 / fps + 20% margin */ + timeout_value = 1000 / fps * 12 / 10 + 5; + ret = decon_reg_wait_run_is_off_timeout(id, timeout_value * MSEC); + + decon_dbg("%s -\n", __func__); + return ret; +} + +static int decon_reg_stop_inst(u32 id, u32 dsi_idx, struct decon_mode_info *psr, + u32 fps) +{ + int ret = 0; + int timeout_value = 0; + + decon_dbg("%s +\n", __func__); + + if ((psr->psr_mode == DECON_MIPI_COMMAND_MODE) && + (psr->trig_mode == DECON_HW_TRIG)) { + decon_reg_set_trigger(id, psr, DECON_TRIG_DISABLE); + } + + /* instant stop */ + decon_reg_direct_on_off(id, 0); + + decon_reg_update_req_global(id); + +#if defined(CONFIG_EXYNOS_DISPLAYPORT) + if (psr->out_type == DECON_OUT_DP) + displayport_reg_lh_p_ch_power(0); +#endif + + /* timeout : 1 / fps + 20% margin */ + timeout_value = 1000 / fps * 12 / 10 + 5; + ret = decon_reg_wait_run_is_off_timeout(id, timeout_value * MSEC); + + decon_dbg("%s -\n", __func__); + return ret; +} + + +static void decon_reg_set_win_enable(u32 id, u32 win_idx, u32 en) +{ + u32 val, mask; + + val = en ? ~0 : 0; + mask = WIN_EN_F(win_idx); + decon_write_mask(id, DATA_PATH_CONTROL_0, val, mask); + decon_dbg("%s: 0x%x\n", __func__, decon_read(id, DATA_PATH_CONTROL_0)); +} + +/* + * argb_color : 32-bit + * A[31:24] - R[23:16] - G[15:8] - B[7:0] + */ +static void decon_reg_set_win_mapcolor(u32 id, u32 win_idx, u32 argb_color) +{ + u32 val, mask; + u32 mc_alpha = 0, mc_red = 0; + u32 mc_green = 0, mc_blue = 0; + + mc_alpha = (argb_color >> 24) & 0xFF; + mc_red = (argb_color >> 16) & 0xFF; + mc_green = (argb_color >> 8) & 0xFF; + mc_blue = (argb_color >> 0) & 0xFF; + + val = WIN_MAPCOLOR_A_F(mc_alpha) | WIN_MAPCOLOR_R_F(mc_red); + mask = WIN_MAPCOLOR_A_MASK | WIN_MAPCOLOR_R_MASK; + decon_write_mask(id, WIN_COLORMAP_0(win_idx), val, mask); + + val = WIN_MAPCOLOR_G_F(mc_green) | WIN_MAPCOLOR_B_F(mc_blue); + mask = WIN_MAPCOLOR_G_MASK | WIN_MAPCOLOR_B_MASK; + decon_write_mask(id, WIN_COLORMAP_1(win_idx), val, mask); +} + +static void decon_reg_set_win_plane_alpha(u32 id, u32 win_idx, u32 a0, u32 a1) +{ + u32 val, mask; + + val = WIN_ALPHA1_F(a1) | WIN_ALPHA0_F(a0); + mask = WIN_ALPHA1_MASK | WIN_ALPHA0_MASK; + decon_write_mask(id, WIN_CONTROL_0(win_idx), val, mask); +} + +static void decon_reg_set_winmap(u32 id, u32 win_idx, u32 color, u32 en) +{ + u32 val, mask; + + /* Enable */ + val = en ? ~0 : 0; + mask = WIN_MAPCOLOR_EN_F(win_idx); + decon_write_mask(id, DATA_PATH_CONTROL_0, val, mask); + decon_dbg("%s: 0x%x\n", __func__, decon_read(id, DATA_PATH_CONTROL_0)); + + /* Color Set */ + decon_reg_set_win_mapcolor(0, win_idx, color); +} + +/* ALPHA_MULT selection used in (a',b',c',d') coefficient */ +static void decon_reg_set_win_alpha_mult(u32 id, u32 win_idx, u32 a_sel) +{ + u32 val, mask; + + val = WIN_ALPHA_MULT_SRC_SEL_F(a_sel); + mask = WIN_ALPHA_MULT_SRC_SEL_MASK; + decon_write_mask(id, WIN_CONTROL_0(win_idx), val, mask); +} + +static void decon_reg_set_win_sub_coeff(u32 id, u32 win_idx, + u32 fgd, u32 bgd, u32 fga, u32 bga) +{ + u32 val, mask; + + /* + * [ Blending Equation ] + * Color : Cr = (a x Cf) + (b x Cb) + * Alpha : Ar = (c x Af) + (d x Ab) + * + * [ User-defined ] + * a' = WINx_FG_ALPHA_D_SEL : Af' that is multiplied by FG Pixel Color + * b' = WINx_BG_ALPHA_D_SEL : Ab' that is multiplied by BG Pixel Color + * c' = WINx_FG_ALPHA_A_SEL : Af' that is multiplied by FG Pixel Alpha + * d' = WINx_BG_ALPHA_A_SEL : Ab' that is multiplied by BG Pixel Alpha + */ + + val = (WIN_FG_ALPHA_D_SEL_F(fgd) + | WIN_BG_ALPHA_D_SEL_F(bgd) + | WIN_FG_ALPHA_A_SEL_F(fga) + | WIN_BG_ALPHA_A_SEL_F(bga)); + mask = (WIN_FG_ALPHA_D_SEL_MASK + | WIN_BG_ALPHA_D_SEL_MASK + | WIN_FG_ALPHA_A_SEL_MASK + | WIN_BG_ALPHA_A_SEL_MASK); + decon_write_mask(id, WIN_CONTROL_1(win_idx), val, mask); +} + +static void decon_reg_set_win_func(u32 id, u32 win_idx, enum decon_win_func pd_func) +{ + u32 val, mask; + + val = WIN_FUNC_F(pd_func); + mask = WIN_FUNC_MASK; + decon_write_mask(id, WIN_CONTROL_0(win_idx), val, mask); +} + +static void decon_reg_set_win_bnd_function(u32 id, u32 win_idx, + struct decon_window_regs *regs) +{ + int plane_a = regs->plane_alpha; + enum decon_blending blend = regs->blend; + enum decon_win_func pd_func = PD_FUNC_USER_DEFINED; + u8 alpha0 = 0xff; + u8 alpha1 = 0xff; + bool is_plane_a = false; + u32 af_d = BND_COEF_ONE, ab_d = BND_COEF_ZERO, + af_a = BND_COEF_ONE, ab_a = BND_COEF_ZERO; + + if (blend == DECON_BLENDING_NONE) + pd_func = PD_FUNC_COPY; + + if ((plane_a >= 0) && (plane_a <= 0xff)) { + alpha0 = plane_a; + alpha1 = 0; + is_plane_a = true; + } + + if ((blend == DECON_BLENDING_COVERAGE) && !is_plane_a) { + af_d = BND_COEF_AF; + ab_d = BND_COEF_1_M_AF; + af_a = BND_COEF_AF; + ab_a = BND_COEF_1_M_AF; + } else if ((blend == DECON_BLENDING_COVERAGE) && is_plane_a) { + af_d = BND_COEF_ALPHA_MULT; + ab_d = BND_COEF_1_M_ALPHA_MULT; + af_a = BND_COEF_ALPHA_MULT; + ab_a = BND_COEF_1_M_ALPHA_MULT; + } else if ((blend == DECON_BLENDING_PREMULT) && !is_plane_a) { + af_d = BND_COEF_ONE; + ab_d = BND_COEF_1_M_AF; + af_a = BND_COEF_ONE; + ab_a = BND_COEF_1_M_AF; + } else if ((blend == DECON_BLENDING_PREMULT) && is_plane_a) { + af_d = BND_COEF_PLNAE_ALPHA0; + ab_d = BND_COEF_1_M_ALPHA_MULT; + af_a = BND_COEF_PLNAE_ALPHA0; + ab_a = BND_COEF_1_M_ALPHA_MULT; + } else if (blend == DECON_BLENDING_NONE) { + decon_dbg("%s:%d none blending mode\n", __func__, __LINE__); + } else { + decon_warn("%s:%d undefined blending mode\n", + __func__, __LINE__); + } + + decon_reg_set_win_plane_alpha(id, win_idx, alpha0, alpha1); + decon_reg_set_win_alpha_mult(id, win_idx, ALPHA_MULT_SRC_SEL_AF); + decon_reg_set_win_func(id, win_idx, pd_func); + if (pd_func == PD_FUNC_USER_DEFINED) + decon_reg_set_win_sub_coeff(id, + win_idx, af_d, ab_d, af_a, ab_a); +} + + +/******************** EXPORTED DECON CAL APIs ********************/ +/* TODO: maybe this function will be moved to internal DECON CAL function */ +void decon_reg_update_req_global(u32 id) +{ + decon_write_mask(id, SHADOW_REG_UPDATE_REQ, ~0, + SHADOW_REG_UPDATE_REQ_GLOBAL); +} + +int decon_reg_init(u32 id, u32 dsi_idx, struct decon_param *p) +{ + struct decon_lcd *lcd_info = p->lcd_info; + struct decon_mode_info *psr = &p->psr; + enum decon_scaler_path s_path = SCALERPATH_OFF; + + /* + * DECON does not need to start, if DECON is already + * running(enabled in LCD_ON_UBOOT) + */ + if (decon_reg_get_run_status(id)) { + decon_info("decon_reg_init already called by BOOTLOADER\n"); + decon_reg_init_probe(id, dsi_idx, p); + if (psr->psr_mode == DECON_MIPI_COMMAND_MODE) + decon_reg_set_trigger(id, psr, DECON_TRIG_DISABLE); + return -EBUSY; + } + + dpu_reg_set_qactive_pll(id, true); + + decon_reg_set_clkgate_mode(id, 0); + + if (psr->out_type == DECON_OUT_DP) + decon_reg_set_te_qactive_pll_mode(id, 1); + + if (id == 0) + decon_reg_set_sram_share(id, DECON_FIFO_04K); + else if (id == 2) + decon_reg_set_sram_share(id, DECON_FIFO_12K); + + decon_reg_set_operation_mode(id, psr->psr_mode); + + decon_reg_set_blender_bg_image_size(id, psr->dsi_mode, lcd_info); + + decon_reg_set_scaled_image_size(id, psr->dsi_mode, lcd_info); + + if (id == 2) { + /* Set a TRIG mode */ + /* This code is for only DECON 2 s/w trigger mode */ + decon_reg_configure_trigger(id, psr->trig_mode); + decon_reg_configure_lcd(id, p); + } else { + decon_reg_configure_lcd(id, p); + if (psr->psr_mode == DECON_MIPI_COMMAND_MODE) + decon_reg_set_trigger(id, psr, DECON_TRIG_DISABLE); + } + + /* FIXME: DECON_T dedicated to PRE_WB */ + if (p->psr.out_type == DECON_OUT_WB) + decon_reg_set_data_path(id, DPATH_WBPRE_ONLY, s_path); + + /* asserted interrupt should be cleared before initializing decon hw */ + decon_reg_clear_int_all(id); + + /* Configure DECON dsim connection : 'data_path' setting is required */ + decon_reg_set_interface(id, psr); + + return 0; +} + +int decon_reg_start(u32 id, struct decon_mode_info *psr) +{ + int ret = 0; + + decon_reg_direct_on_off(id, 1); + decon_reg_update_req_global(id); + + /* + * DECON goes to run-status as soon as + * request shadow update without HW_TE + */ + ret = decon_reg_wait_run_status_timeout(id, 20 * 1000); + + /* wait until run-status, then trigger */ + if (psr->psr_mode == DECON_MIPI_COMMAND_MODE) + decon_reg_set_trigger(id, psr, DECON_TRIG_ENABLE); + return ret; +} + +/* + * stop sequence should be carefully for stability + * try sequecne + * 1. perframe off + * 2. instant off + */ +int decon_reg_stop(u32 id, u32 dsi_idx, struct decon_mode_info *psr, bool rst, + u32 fps) +{ + int ret = 0; + + if (psr->out_type == DECON_OUT_DP) { + ret = decon_reg_stop_inst(id, dsi_idx, psr, fps); + if (ret < 0) + decon_err("%s, failed to DP instant_stop\n", __func__); + decon_reg_set_te_qactive_pll_mode(id, 0); + } else { + /* call perframe stop */ + ret = decon_reg_stop_perframe(id, dsi_idx, psr, fps); + if (ret < 0) { + decon_err("%s, failed to perframe_stop\n", __func__); + /* if fails, call decon instant off */ + ret = decon_reg_stop_inst(id, dsi_idx, psr, fps); + if (ret < 0) + decon_err("%s, failed to instant_stop\n", __func__); + } + } + + /* assert reset when stopped normally or requested */ + if (!ret && rst) + decon_reg_reset(id); + + decon_reg_clear_int_all(id); + + return ret; +} + +void decon_reg_win_enable_and_update(u32 id, u32 win_idx, u32 en) +{ + decon_reg_set_win_enable(id, win_idx, en); + decon_reg_update_req_window(id, win_idx); +} + +void decon_reg_set_window_control(u32 id, int win_idx, + struct decon_window_regs *regs, u32 winmap_en) +{ + u32 win_en = regs->wincon & WIN_EN_F(win_idx) ? 1 : 0; + + if (win_en) { + decon_dbg("%s: win id = %d\n", __func__, win_idx); + decon_reg_set_win_bnd_function(0, win_idx, regs); + decon_write(0, WIN_START_POSITION(win_idx), regs->start_pos); + decon_write(0, WIN_END_POSITION(win_idx), regs->end_pos); + decon_write(0, WIN_START_TIME_CONTROL(win_idx), + regs->start_time); + decon_reg_set_winmap(id, win_idx, regs->colormap, winmap_en); + } + + decon_reg_config_win_channel(id, win_idx, regs->type); + decon_reg_win_enable_and_update(id, win_idx, win_en); + + decon_dbg("%s: regs->type(%d)\n", __func__, regs->type); +} + +void decon_reg_update_req_window_mask(u32 id, u32 win_idx) +{ + u32 mask; + + mask = SHADOW_REG_UPDATE_REQ_FOR_DECON; + mask &= ~(SHADOW_REG_UPDATE_REQ_WIN(win_idx)); + decon_write_mask(id, SHADOW_REG_UPDATE_REQ, ~0, mask); +} + +void decon_reg_set_trigger(u32 id, struct decon_mode_info *psr, + enum decon_set_trig en) +{ + u32 val, mask; + + if (psr->psr_mode == DECON_VIDEO_MODE) + return; + + if (psr->trig_mode == DECON_SW_TRIG) { + val = (en == DECON_TRIG_ENABLE) ? SW_TRIG_EN : 0; + mask = HW_TRIG_EN | SW_TRIG_EN; + } else { /* DECON_HW_TRIG */ + val = (en == DECON_TRIG_ENABLE) ? + HW_TRIG_EN : HW_TRIG_MASK_DECON; + mask = HW_TRIG_EN | HW_TRIG_MASK_DECON; + } + + decon_write_mask(id, HW_SW_TRIG_CONTROL, val, mask); +} + +void decon_reg_update_req_and_unmask(u32 id, struct decon_mode_info *psr) +{ + decon_reg_update_req_global(id); + + if (psr->psr_mode == DECON_MIPI_COMMAND_MODE) + decon_reg_set_trigger(id, psr, DECON_TRIG_ENABLE); +} + +int decon_reg_wait_update_done_timeout(u32 id, unsigned long timeout) +{ + unsigned long delay_time = 100; + unsigned long cnt = timeout / delay_time; + + while (decon_read(id, SHADOW_REG_UPDATE_REQ) && --cnt) + udelay(delay_time); + + if (!cnt) { + decon_err("decon%d timeout of updating decon registers\n", id); + return -EBUSY; + } + + return 0; +} + +int decon_reg_wait_update_done_and_mask(u32 id, + struct decon_mode_info *psr, u32 timeout) +{ + int result; + + result = decon_reg_wait_update_done_timeout(id, timeout); + + if (psr->psr_mode == DECON_MIPI_COMMAND_MODE) + decon_reg_set_trigger(id, psr, DECON_TRIG_DISABLE); + + return result; +} + +int decon_reg_wait_idle_status_timeout(u32 id, unsigned long timeout) +{ + unsigned long delay_time = 10; + unsigned long cnt = timeout / delay_time; + u32 status; + + do { + status = decon_reg_get_idle_status(id); + cnt--; + udelay(delay_time); + } while (!status && cnt); + + if (!cnt) { + decon_err("decon%d wait timeout decon idle status(%u)\n", + id, status); + return -EBUSY; + } + + return 0; +} + +void decon_reg_set_partial_update(u32 id, enum decon_dsi_mode dsi_mode, + struct decon_lcd *lcd_info, bool in_slice[], + u32 partial_w, u32 partial_h) +{ + u32 slice_w; + u32 dual_slice_en[2] = {1, 1}; + u32 slice_mode_ch[2] = {0, 0}; + + /* Here, lcd_info contains the size to be updated */ + decon_reg_set_blender_bg_size(id, dsi_mode, partial_w, partial_h); + + slice_w = lcd_info->xres / lcd_info->dsc_slice_num; + decon_reg_set_data_path_size(id, partial_w, partial_h, + lcd_info->dsc_enabled, lcd_info->dsc_cnt, slice_w, + lcd_info->dsc_slice_h); + + if (lcd_info->dsc_enabled) { + /* get correct DSC configuration */ + dsc_get_partial_update_info(lcd_info->dsc_slice_num, + lcd_info->dsc_cnt, in_slice, + dual_slice_en, slice_mode_ch); + /* To support dual-display : DECON1 have to set DSC1 */ + dsc_reg_set_partial_update(id, dual_slice_en[0], + slice_mode_ch[0], partial_h); + if (lcd_info->dsc_cnt == 2) + dsc_reg_set_partial_update(1, dual_slice_en[1], + slice_mode_ch[1], partial_h); + } +} + +void decon_reg_set_mres(u32 id, struct decon_param *p) +{ + struct decon_lcd *lcd_info = p->lcd_info; + struct decon_mode_info *psr = &p->psr; + u32 overlap_w = 0; + + if (lcd_info->mode != DECON_MIPI_COMMAND_MODE) { + dsim_info("%s: mode[%d] doesn't support multi resolution\n", + __func__, lcd_info->mode); + return; + } + + decon_reg_set_blender_bg_image_size(id, psr->dsi_mode, lcd_info); + decon_reg_set_scaled_image_size(id, psr->dsi_mode, lcd_info); + + if (lcd_info->dsc_enabled) + dsc_reg_init(id, p, overlap_w, 0); + else + decon_reg_config_data_path_size(id, lcd_info->xres, + lcd_info->yres, overlap_w, NULL, p); +} + +void decon_reg_release_resource(u32 id, struct decon_mode_info *psr) +{ + decon_reg_per_frame_off(id); + decon_reg_update_req_global(id); + decon_reg_set_trigger(id, psr, DECON_TRIG_ENABLE); +} + +void decon_reg_config_wb_size(u32 id, struct decon_lcd *lcd_info, + struct decon_param *param) +{ + decon_reg_set_blender_bg_image_size(id, DSI_MODE_SINGLE, + lcd_info); + decon_reg_config_data_path_size(id, lcd_info->xres, + lcd_info->yres, 0, NULL, param); +} + +void decon_reg_set_int(u32 id, struct decon_mode_info *psr, u32 en) +{ + u32 val, mask; + + decon_reg_clear_int_all(id); + + if (en) { + val = (DPU_FRAME_DONE_INT_EN + | DPU_FRAME_START_INT_EN + | DPU_EXTRA_INT_EN + | DPU_INT_EN); + + decon_write_mask(id, INTERRUPT_ENABLE, + val, INTERRUPT_ENABLE_MASK); + decon_dbg("decon %d, interrupt val = %x\n", id, val); + + val = (DPU_RESOURCE_CONFLICT_INT_EN + | DPU_TIME_OUT_INT_EN); + decon_write(id, EXTRA_INTERRUPT_ENABLE, val); + } else { + mask = (DPU_EXTRA_INT_EN | DPU_INT_EN); + decon_write_mask(id, INTERRUPT_ENABLE, 0, mask); + } +} + +int decon_reg_get_interrupt_and_clear(u32 id, u32 *ext_irq) +{ + u32 val, val1; + u32 reg_id; + + reg_id = INTERRUPT_PENDING; + val = decon_read(id, reg_id); + + if (val & DPU_FRAME_START_INT_PEND) + decon_write(id, reg_id, DPU_FRAME_START_INT_PEND); + + if (val & DPU_FRAME_DONE_INT_PEND) + decon_write(id, reg_id, DPU_FRAME_DONE_INT_PEND); + + if (val & DPU_EXTRA_INT_PEND) { + decon_write(id, reg_id, DPU_EXTRA_INT_PEND); + + reg_id = EXTRA_INTERRUPT_PENDING; + val1 = decon_read(id, reg_id); + *ext_irq = val1; + + if (val1 & DPU_RESOURCE_CONFLICT_INT_PEND) { + decon_write(id, reg_id, DPU_RESOURCE_CONFLICT_INT_PEND); + decon_warn("decon%d INFO0: SRAM_RSC & DSC = 0x%x\n", + id, + decon_read(id, RESOURCE_OCCUPANCY_INFO_0)); + decon_warn("decon%d INFO1: DMA_CH_RSC= 0x%x\n", + id, + decon_read(id, RESOURCE_OCCUPANCY_INFO_1)); + decon_warn("decon%d INFO2: WIN_RSC= 0x%x\n", + id, + decon_read(id, RESOURCE_OCCUPANCY_INFO_2)); + } + + if (val1 & DPU_TIME_OUT_INT_PEND) + decon_write(id, reg_id, DPU_TIME_OUT_INT_PEND); + } + + return val; +} + +u32 decon_reg_get_cam_status(void __iomem *cam_status) +{ + if (cam_status) + return readl(cam_status); + else + return 0xF; +} + +void decon_reg_set_start_crc(u32 id, u32 en) +{ + decon_write_mask(id, CRC_CONTROL, en ? ~0 : 0, CRC_START); +} + +/* bit_sel : 0=B, 1=G, 2=R */ +void decon_reg_set_select_crc_bits(u32 id, u32 bit_sel) +{ + u32 val; + + val = CRC_COLOR_SEL(bit_sel); + decon_write_mask(id, CRC_CONTROL, val, CRC_COLOR_SEL_MASK); +} + +void decon_reg_get_crc_data(u32 id, u32 *w0_data, u32 *w1_data) +{ + u32 val; + + val = decon_read(id, CRC_DATA_0); + *w0_data = CRC_DATA_DSIMIF0_GET(val); + *w1_data = CRC_DATA_DSIMIF1_GET(val); +} + +u32 DPU_DMA2CH(u32 dma) +{ + u32 ch_id; + + switch (dma) { + case IDMA_GF0: + ch_id = 0; + break; + case IDMA_GF1: + ch_id = 2; + break; + case IDMA_VG: + ch_id = 4; + break; + case IDMA_VGF: + ch_id = 3; + break; + case IDMA_VGS: + ch_id = 5; + break; + case IDMA_VGRFS: + ch_id = 1; + break; + default: + decon_dbg("channel(0x%x) is not valid\n", dma); + return -1; + } + + return ch_id; +} + +u32 DPU_CH2DMA(u32 ch) +{ + u32 dma; + + switch (ch) { + case 0: + dma = IDMA_GF0; + break; + case 1: + dma = IDMA_VGRFS; + break; + case 2: + dma = IDMA_GF1; + break; + case 3: + dma = IDMA_VGF; + break; + case 4: + dma = IDMA_VG; + break; + case 5: + dma = IDMA_VGS; + break; + default: + decon_warn("channal(%d) is invalid\n", ch); + return -1; + } + + return dma; +} + +int decon_check_supported_formats(enum decon_pixel_format format) +{ + switch (format) { + case DECON_PIXEL_FORMAT_ARGB_8888: + case DECON_PIXEL_FORMAT_ABGR_8888: + case DECON_PIXEL_FORMAT_RGBA_8888: + case DECON_PIXEL_FORMAT_BGRA_8888: + case DECON_PIXEL_FORMAT_XRGB_8888: + case DECON_PIXEL_FORMAT_XBGR_8888: + case DECON_PIXEL_FORMAT_RGBX_8888: + case DECON_PIXEL_FORMAT_BGRX_8888: + case DECON_PIXEL_FORMAT_RGB_565: + case DECON_PIXEL_FORMAT_NV12: + case DECON_PIXEL_FORMAT_NV12M: + case DECON_PIXEL_FORMAT_NV21: + case DECON_PIXEL_FORMAT_NV21M: + case DECON_PIXEL_FORMAT_NV12N: + case DECON_PIXEL_FORMAT_NV12N_10B: + + case DECON_PIXEL_FORMAT_ARGB_2101010: + case DECON_PIXEL_FORMAT_ABGR_2101010: + case DECON_PIXEL_FORMAT_RGBA_1010102: + case DECON_PIXEL_FORMAT_BGRA_1010102: + + case DECON_PIXEL_FORMAT_NV12M_P010: + case DECON_PIXEL_FORMAT_NV21M_P010: + case DECON_PIXEL_FORMAT_NV12M_S10B: + case DECON_PIXEL_FORMAT_NV21M_S10B: + + case DECON_PIXEL_FORMAT_NV16: + case DECON_PIXEL_FORMAT_NV61: + case DECON_PIXEL_FORMAT_NV16M_P210: + case DECON_PIXEL_FORMAT_NV61M_P210: + case DECON_PIXEL_FORMAT_NV16M_S10B: + case DECON_PIXEL_FORMAT_NV61M_S10B: + return 0; + default: + break; + } + + return -EINVAL; +} diff --git a/drivers/video/fbdev/exynos/dpu20/cal_9610/displayport_reg.c b/drivers/video/fbdev/exynos/dpu20/cal_9610/displayport_reg.c new file mode 100644 index 000000000000..e9e3afc6724f --- /dev/null +++ b/drivers/video/fbdev/exynos/dpu20/cal_9610/displayport_reg.c @@ -0,0 +1,1698 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * SFR access functions for Samsung EXYNOS SoC DisplayPort driver. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include "../displayport.h" +#if defined(CONFIG_PHY_SAMSUNG_USB_CAL) +#include "../../../../drivers/phy/samsung/phy-samsung-usb-cal.h" +#include "../../../../drivers/phy/samsung/phy-exynos-usbdp.h" +#endif + +u32 phy_tune_parameters[4][4][3] = { + /* Swing Level_0 */ { {4, 0, 0}, {0, 7, 0}, {2, 9, 1}, {0, 13, 1} }, + /* Swing Level_1 */ { {1, 0, 0}, {2, 6, 1}, {0, 10, 1}, {0, 10, 1} }, + /* Swing Level_2 */ { {2, 0, 1}, {0, 6, 1}, {0, 6, 1}, {0, 6, 1} }, + /* Swing Level_3 */ { {0, 0, 1}, {0, 0, 1}, {0, 0, 1}, {0, 0, 1} }, +}; + +/* supported_videos[] is to be arranged in the order of pixel clock */ +struct displayport_supported_preset supported_videos[] = { + {V640X480P60, V4L2_DV_BT_DMT_640X480P60, 60, SYNC_NEGATIVE, SYNC_NEGATIVE, 1, "V640X480P60"}, + {V720X480P60, V4L2_DV_BT_CEA_720X480P59_94, 60, SYNC_NEGATIVE, SYNC_NEGATIVE, 2, "V720X480P60"}, + {V720X576P50, V4L2_DV_BT_CEA_720X576P50, 50, SYNC_NEGATIVE, SYNC_NEGATIVE, 17, "V720X576P50"}, + {V1280X720P50, V4L2_DV_BT_CEA_1280X720P50, 50, SYNC_POSITIVE, SYNC_POSITIVE, 19, "V1280X720P50"}, + {V1280X720P60, V4L2_DV_BT_CEA_1280X720P60, 60, SYNC_POSITIVE, SYNC_POSITIVE, 4, "V1280X720P60"}, + {V1280X800P60RB, V4L2_DV_BT_DMT_1280X800P60_RB, 60, SYNC_POSITIVE, SYNC_NEGATIVE, 0, "V1280X800P60RB"}, + {V1280X1024P60, V4L2_DV_BT_DMT_1280X1024P60, 60, SYNC_POSITIVE, SYNC_POSITIVE, 0, "V1280X1024P60"}, + {V1920X1080P24, V4L2_DV_BT_CEA_1920X1080P24, 24, SYNC_POSITIVE, SYNC_POSITIVE, 32, "V1920X1080P24"}, + {V1920X1080P25, V4L2_DV_BT_CEA_1920X1080P25, 25, SYNC_POSITIVE, SYNC_POSITIVE, 33, "V1920X1080P25"}, + {V1920X1080P30, V4L2_DV_BT_CEA_1920X1080P30, 30, SYNC_POSITIVE, SYNC_POSITIVE, 34, "V1920X1080P30"}, + {V1600X900P60RB, V4L2_DV_BT_DMT_1600X900P60_RB, 60, SYNC_POSITIVE, SYNC_POSITIVE, 0, "V1600X900P60RB"}, + {V1920X1080P50, V4L2_DV_BT_CEA_1920X1080P50, 50, SYNC_POSITIVE, SYNC_POSITIVE, 31, "V1920X1080P50"}, + {V1920X1080P60, V4L2_DV_BT_CEA_1920X1080P60, 60, SYNC_POSITIVE, SYNC_POSITIVE, 16, "V1920X1080P60"}, + {V2048X1536P60, V4L2_DV_BT_CVT_2048X1536P60_ADDED, 60, SYNC_POSITIVE, SYNC_POSITIVE, 0, "V2048X1536P60"}, + {V1920X1440P60, V4L2_DV_BT_DMT_1920X1440P60, 60, SYNC_POSITIVE, SYNC_POSITIVE, 0, "V1920X1440P60"}, + {V2560X1440P59, V4L2_DV_BT_CVT_2560X1440P59_ADDED, 59, SYNC_POSITIVE, SYNC_POSITIVE, 0, "V2560X1440P59"}, + {V2560X1440P60, V4L2_DV_BT_CVT_2560X1440P60_ADDED, 60, SYNC_POSITIVE, SYNC_POSITIVE, 0, "V2560X1440P60"}, + {V3840X2160P24, V4L2_DV_BT_CEA_3840X2160P24, 24, SYNC_POSITIVE, SYNC_POSITIVE, 93, "V3840X2160P24"}, + {V3840X2160P25, V4L2_DV_BT_CEA_3840X2160P25, 25, SYNC_POSITIVE, SYNC_POSITIVE, 94, "V3840X2160P25"}, + {V3840X2160P30, V4L2_DV_BT_CEA_3840X2160P30, 30, SYNC_POSITIVE, SYNC_POSITIVE, 95, "V3840X2160P30"}, + {V4096X2160P24, V4L2_DV_BT_CEA_4096X2160P24, 24, SYNC_POSITIVE, SYNC_POSITIVE, 98, "V4096X2160P24"}, + {V4096X2160P25, V4L2_DV_BT_CEA_4096X2160P25, 25, SYNC_POSITIVE, SYNC_POSITIVE, 99, "V4096X2160P25"}, + {V4096X2160P30, V4L2_DV_BT_CEA_4096X2160P30, 30, SYNC_POSITIVE, SYNC_POSITIVE, 100, "V4096X2160P30"}, + {V3840X2160P59RB, V4L2_DV_BT_CVT_3840X2160P59_ADDED, 59, SYNC_POSITIVE, SYNC_POSITIVE, 0, "V3840X2160P59RB"}, + {V3840X2160P50, V4L2_DV_BT_CEA_3840X2160P50, 50, SYNC_POSITIVE, SYNC_POSITIVE, 96, "V3840X2160P50"}, + {V3840X2160P60, V4L2_DV_BT_CEA_3840X2160P60, 60, SYNC_POSITIVE, SYNC_POSITIVE, 97, "V3840X2160P60"}, + {V4096X2160P50, V4L2_DV_BT_CEA_4096X2160P50, 50, SYNC_POSITIVE, SYNC_POSITIVE, 101, "V4096X2160P50"}, + {V4096X2160P60, V4L2_DV_BT_CEA_4096X2160P60, 60, SYNC_POSITIVE, SYNC_POSITIVE, 102, "V4096X2160P60"}, + {V640X10P60SACRC, V4L2_DV_BT_CVT_640x10P60_ADDED, 60, SYNC_POSITIVE, SYNC_POSITIVE, 0, "V640X10P60SACRC"}, +}; + +const int supported_videos_pre_cnt = ARRAY_SIZE(supported_videos); + +u32 audio_async_m_n[2][3][7] = { + { /* M value set */ + {3314, 4567, 4971, 9134, 9942, 18269, 19884}, + {1988, 2740, 2983, 5481, 5695, 10961, 11930}, + { 994, 1370, 1491, 2740, 2983, 5481, 5965}, + }, + { /* N value set */ + {32768, 32768, 32768, 32768, 32768, 32768, 32768}, + {32768, 32768, 32768, 32768, 32768, 32768, 32768}, + {32768, 32768, 32768, 32768, 32768, 32768, 32768}, + } +}; + +u32 audio_sync_m_n[2][3][7] = { + { /* M value set */ + {1024, 784, 512, 1568, 1024, 3136, 2048}, + {1024, 784, 512, 1568, 1024, 3136, 2048}, + {1024, 784, 512, 784, 512, 1568, 1024}, + }, + { /* N value set */ + {10125, 5625, 3375, 5625, 3375, 5625, 3375}, + {16875, 9375, 5625, 9375, 5625, 9375, 5625}, + {33750, 18750, 11250, 9375, 5625, 9375, 5625}, + } +}; + +u32 m_aud_master[7] = {32000, 44100, 48000, 88200, 96000, 176000, 192000}; + +u32 n_aud_master[3] = {81000000, 135000000, 270000000}; + +void displayport_reg_sw_reset(void) +{ + u32 cnt = 10; + u32 state; + + displayport_write_mask(SYSTEM_SW_RESET_CONTROL, ~0, SW_RESET); + + do { + state = displayport_read(SYSTEM_SW_RESET_CONTROL) & SW_RESET; + cnt--; + udelay(1); + } while (state && cnt); + + if (!cnt) + displayport_err("%s is timeout.\n", __func__); +} + +void displayport_reg_phy_reset(u32 en) +{ + if (en) + displayport_phy_write_mask(DP_REG_1, 0, CMN_INIT_RSTN); + else + displayport_phy_write_mask(DP_REG_1, ~0, CMN_INIT_RSTN); +} + +void displayport_reg_phy_txclk_source_setting(u8 lane_num) +{ + displayport_phy_write_mask(DP_REG_B, lane_num, LN_TXCLK_SOURCE_LANE); +} + +void displayport_reg_phy_init_setting(void) +{ + displayport_phy_write(DP_REG_11, 0x00); + displayport_phy_write(DP_REG_31, 0x00); + displayport_phy_write(DP_REG_51, 0x00); + displayport_phy_write(DP_REG_71, 0x00); + displayport_phy_write(DP_REG_C6, 0x20); + displayport_phy_write(DP_REG_C9, 0x1E); + displayport_phy_write(DP_REG_D9, 0x30); + displayport_phy_write(DP_REG_E9, 0x30); + displayport_phy_write(DP_REG_CF, 0x0D); + displayport_phy_write(DP_REG_DF, 0x08); + displayport_phy_write(DP_REG_EF, 0x08); +} + +void displayport_reg_phy_mode_setting(void) +{ +#if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) + struct displayport_device *displayport = get_displayport_drvdata(); +#endif + u32 val = 0; + +#if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) + switch (displayport->ccic_notify_dp_conf) { + case CCIC_NOTIFY_DP_PIN_UNKNOWN: + displayport_dbg("CCIC_NOTIFY_DP_PIN_UNKNOWN\n"); + break; + + case CCIC_NOTIFY_DP_PIN_A: + case CCIC_NOTIFY_DP_PIN_C: + case CCIC_NOTIFY_DP_PIN_E: +#if defined(CONFIG_PHY_SAMSUNG_USB_CAL) + exynos_usbdrd_inform_dp_use(1, 4); +#endif + + displayport_phy_write_mask(CMN_REG2C, 1, MAN_USBDP_MODE_EN); + + displayport_phy_write_mask(CMN_REG2C, 0x02, MAN_USBDP_MODE); + + displayport_phy_write_mask(CMN_REG2D, 0, USB_TX1_SEL); + displayport_phy_write_mask(CMN_REG2D, 0, USB_TX3_SEL); + + displayport_phy_write_mask(DP_REG_B3, 0x02, CMN_DUMMY_CTRL_1_0); + displayport_phy_write_mask(DP_REG_B3, 0x00, CMN_DUMMY_CTRL_7_6); + + val = LN0_LANE_EN | LN1_LANE_EN | LN2_LANE_EN | LN3_LANE_EN; + displayport_reg_phy_txclk_source_setting(0); + break; + + case CCIC_NOTIFY_DP_PIN_B: + case CCIC_NOTIFY_DP_PIN_D: + case CCIC_NOTIFY_DP_PIN_F: +#if defined(CONFIG_PHY_SAMSUNG_USB_CAL) + exynos_usbdrd_inform_dp_use(1, 2); +#endif + + displayport_phy_write_mask(CMN_REG2C, 1, MAN_USBDP_MODE_EN); + + if (displayport->dp_sw_sel) { + displayport_phy_write_mask(CMN_REG2C, 0x03, MAN_USBDP_MODE); + + displayport_phy_write_mask(CMN_REG2D, 1, USB_TX1_SEL); + displayport_phy_write_mask(CMN_REG2D, 0, USB_TX3_SEL); + + displayport_phy_write_mask(DP_REG_B3, 0x03, CMN_DUMMY_CTRL_1_0); + displayport_phy_write_mask(DP_REG_B3, 0x01, CMN_DUMMY_CTRL_7_6); + + val = LN2_LANE_EN | LN3_LANE_EN; + displayport_reg_phy_txclk_source_setting(3); + } else { + displayport_phy_write_mask(CMN_REG2C, 0x00, MAN_USBDP_MODE); + + displayport_phy_write_mask(CMN_REG2D, 0, USB_TX1_SEL); + displayport_phy_write_mask(CMN_REG2D, 1, USB_TX3_SEL); + + displayport_phy_write_mask(DP_REG_B3, 0x00, CMN_DUMMY_CTRL_1_0); + displayport_phy_write_mask(DP_REG_B3, 0x02, CMN_DUMMY_CTRL_7_6); + + val = LN0_LANE_EN | LN1_LANE_EN; + displayport_reg_phy_txclk_source_setting(0); + } + break; + + default: + displayport_dbg("CCIC_NOTIFY_DP_PIN_UNKNOWN\n"); + break; + } +#endif + + val |= 0xF0; + displayport_phy_write(DP_REG_0, val); +} + +void displayport_reg_phy_ssc_enable(u32 en) +{ + displayport_phy_write_mask(DP_REG_97, en, SSC_EN); +} + +void displayport_reg_wait_phy_pll_lock(void) +{ + u32 cnt = 165; /* wait for 150us + 10% margin */ + u32 state; + + do { + state = displayport_read(SYSTEM_PLL_LOCK_CONTROL) & PLL_LOCK_STATUS; + cnt--; + udelay(1); + } while (!state && cnt); + + if (!cnt) + displayport_err("%s is timeout.\n", __func__); +} + +void displayport_reg_set_link_bw(u8 link_rate) +{ + displayport_write(SYSTEM_MAIN_LINK_BANDWIDTH, link_rate); +} + +u32 displayport_reg_get_link_bw(void) +{ + return displayport_read(SYSTEM_MAIN_LINK_BANDWIDTH); +} + +void displayport_reg_set_lane_count(u8 lane_cnt) +{ + displayport_write(SYSTEM_MAIN_LINK_LANE_COUNT, lane_cnt); +} + +u32 displayport_reg_get_lane_count(void) +{ + return displayport_read(SYSTEM_MAIN_LINK_LANE_COUNT); +} + +void displayport_reg_set_training_pattern(displayport_training_pattern pattern) +{ + displayport_write_mask(PCS_TEST_PATTERN_CONTROL, 0, LINK_QUALITY_PATTERN_SET); + displayport_write_mask(PCS_CONTROL, pattern, LINK_TRAINING_PATTERN_SET); + + if (pattern == NORAMAL_DATA) + displayport_write_mask(PCS_CONTROL, 0, SCRAMBLE_BYPASS); + else + displayport_write_mask(PCS_CONTROL, 1, SCRAMBLE_BYPASS); +} + +void displayport_reg_set_qual_pattern(displayport_qual_pattern pattern, displayport_scrambling scramble) +{ + displayport_write_mask(PCS_CONTROL, 0, LINK_TRAINING_PATTERN_SET); + displayport_write_mask(PCS_TEST_PATTERN_CONTROL, pattern, LINK_QUALITY_PATTERN_SET); + displayport_write_mask(PCS_CONTROL, scramble, SCRAMBLE_BYPASS); +} + +void displayport_reg_set_hbr2_scrambler_reset(u32 uResetCount) +{ + uResetCount /= 2; /* only even value@Istor EVT1, ?*/ + displayport_write_mask(PCS_HBR2_EYE_SR_CONTROL, uResetCount, HBR2_EYE_SR_COUNT); +} + +void displayport_reg_set_pattern_PLTPAT(void) +{ + displayport_write(PCS_TEST_PATTERN_SET0, 0x3E0F83E0); /* 00111110 00001111 10000011 11100000 */ + displayport_write(PCS_TEST_PATTERN_SET1, 0x0F83E0F8); /* 00001111 10000011 11100000 11111000 */ + displayport_write(PCS_TEST_PATTERN_SET2, 0x0000F83E); /* 11111000 00111110 */ +} + +void displayport_reg_set_phy_tune(u32 phy_lane_num, u32 amplitude_level, u32 emphasis_level) +{ + u32 amplitude_address = 0; + u32 emphasis_address = 0; + u32 val = 0; + + switch (phy_lane_num) { + case 0: + amplitude_address = DP_REG_16; + emphasis_address = DP_REG_1A; + break; + + case 1: + amplitude_address = DP_REG_36; + emphasis_address = DP_REG_3A; + break; + + case 2: + amplitude_address = DP_REG_56; + emphasis_address = DP_REG_5A; + break; + + case 3: + amplitude_address = DP_REG_76; + emphasis_address = DP_REG_7A; + break; + + default: + break; + } + + amplitude_address += amplitude_level * 4; + val = phy_tune_parameters[amplitude_level][emphasis_level][PHY_AMP_PARAM] + | (phy_tune_parameters[amplitude_level][emphasis_level][PHY_IDRV_EN_PARAM] << TX_DRV_IDRV_EN_CTRL_BIT_POS); + displayport_phy_write(amplitude_address, val); + displayport_dbg("DP_REG_%02x = 0x%02x\n", amplitude_address, val); + + emphasis_address += emphasis_level * 4; + val = phy_tune_parameters[amplitude_level][emphasis_level][PHY_EMP_PARAM]; + displayport_phy_write(emphasis_address, val); + displayport_dbg("DP_REG_%02x = 0x%02x\n", emphasis_address, val); +} + +void displayport_reg_set_phy_voltage_and_pre_emphasis(u8 *voltage, u8 *pre_emphasis) +{ +#if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) + struct displayport_device *displayport = get_displayport_drvdata(); + + switch (displayport->ccic_notify_dp_conf) { + case CCIC_NOTIFY_DP_PIN_UNKNOWN: + break; + + case CCIC_NOTIFY_DP_PIN_A: + if (displayport->dp_sw_sel) { + displayport_reg_set_phy_tune(0, voltage[1], pre_emphasis[1]); + displayport_reg_set_phy_tune(1, voltage[2], pre_emphasis[2]); + displayport_reg_set_phy_tune(2, voltage[3], pre_emphasis[3]); + displayport_reg_set_phy_tune(3, voltage[0], pre_emphasis[0]); + } else { + displayport_reg_set_phy_tune(0, voltage[0], pre_emphasis[0]); + displayport_reg_set_phy_tune(1, voltage[3], pre_emphasis[3]); + displayport_reg_set_phy_tune(2, voltage[2], pre_emphasis[2]); + displayport_reg_set_phy_tune(3, voltage[1], pre_emphasis[1]); + } + break; + case CCIC_NOTIFY_DP_PIN_B: + if (displayport->dp_sw_sel) { + displayport_reg_set_phy_tune(2, voltage[0], pre_emphasis[0]); + displayport_reg_set_phy_tune(3, voltage[1], pre_emphasis[1]); + displayport_phy_write(DP_REG_16, 0x00); + displayport_phy_write(DP_REG_1A, 0x00); + } else { + displayport_reg_set_phy_tune(0, voltage[1], pre_emphasis[1]); + displayport_reg_set_phy_tune(1, voltage[0], pre_emphasis[0]); + displayport_phy_write(DP_REG_56, 0x00); + displayport_phy_write(DP_REG_5A, 0x00); + } + break; + + case CCIC_NOTIFY_DP_PIN_C: + case CCIC_NOTIFY_DP_PIN_E: + if (displayport->dp_sw_sel) { + displayport_reg_set_phy_tune(0, voltage[2], pre_emphasis[2]); + displayport_reg_set_phy_tune(1, voltage[3], pre_emphasis[3]); + displayport_reg_set_phy_tune(2, voltage[1], pre_emphasis[1]); + displayport_reg_set_phy_tune(3, voltage[0], pre_emphasis[0]); + } else { + displayport_reg_set_phy_tune(0, voltage[0], pre_emphasis[0]); + displayport_reg_set_phy_tune(1, voltage[1], pre_emphasis[1]); + displayport_reg_set_phy_tune(2, voltage[3], pre_emphasis[3]); + displayport_reg_set_phy_tune(3, voltage[2], pre_emphasis[2]); + } + break; + + case CCIC_NOTIFY_DP_PIN_D: + case CCIC_NOTIFY_DP_PIN_F: + if (displayport->dp_sw_sel) { + displayport_reg_set_phy_tune(2, voltage[1], pre_emphasis[1]); + displayport_reg_set_phy_tune(3, voltage[0], pre_emphasis[0]); + displayport_phy_write(DP_REG_16, 0x00); + displayport_phy_write(DP_REG_1A, 0x00); + } else { + displayport_reg_set_phy_tune(0, voltage[0], pre_emphasis[0]); + displayport_reg_set_phy_tune(1, voltage[1], pre_emphasis[1]); + displayport_phy_write(DP_REG_56, 0x00); + displayport_phy_write(DP_REG_5A, 0x00); + } + break; + + default: + break; + } +#endif +} + +void displayport_reg_set_voltage_and_pre_emphasis(u8 *voltage, u8 *pre_emphasis) +{ + u32 val = 0; + + displayport_reg_set_phy_voltage_and_pre_emphasis(voltage, pre_emphasis); + + val = (voltage[0] << LN0_TX_AMP_CTRL_BIT_POS) | (voltage[1] << LN1_TX_AMP_CTRL_BIT_POS) + | (voltage[2] << LN2_TX_AMP_CTRL_BIT_POS) | (voltage[3] << LN3_TX_AMP_CTRL_BIT_POS); + displayport_phy_write(DP_REG_3, val); + + val = (pre_emphasis[0] << LN0_TX_EMP_CTRL_BIT_POS) | (pre_emphasis[1] << LN1_TX_EMP_CTRL_BIT_POS) + | (pre_emphasis[2] << LN2_TX_EMP_CTRL_BIT_POS) | (pre_emphasis[3] << LN3_TX_EMP_CTRL_BIT_POS); + displayport_phy_write(DP_REG_4, val); +} + +void displayport_reg_function_enable(void) +{ + displayport_write_mask(SYSTEM_COMMON_FUNCTION_ENABLE, 1, PCS_FUNC_EN); + displayport_write_mask(SYSTEM_COMMON_FUNCTION_ENABLE, 1, AUX_FUNC_EN); + displayport_write_mask(SYSTEM_SST1_FUNCTION_ENABLE, 1, SST1_VIDEO_FUNC_EN); +} + +void displayport_reg_set_interrupt_mask(enum displayport_interrupt_mask param, u8 set) +{ + u32 val = set ? ~0 : 0; + + switch (param) { + case HOTPLUG_CHG_INT_MASK: + displayport_write_mask(SYSTEM_IRQ_COMMON_STATUS_MASK, val, HPD_CHG_MASK); + break; + + case HPD_LOST_INT_MASK: + displayport_write_mask(SYSTEM_IRQ_COMMON_STATUS_MASK, val, HPD_LOST_MASK); + break; + + case PLUG_INT_MASK: + displayport_write_mask(SYSTEM_IRQ_COMMON_STATUS_MASK, val, HPD_PLUG_MASK); + break; + + case HPD_IRQ_INT_MASK: + displayport_write_mask(SYSTEM_IRQ_COMMON_STATUS_MASK, val, HPD_IRQ_MASK); + break; + + case RPLY_RECEIV_INT_MASK: + displayport_write_mask(SYSTEM_IRQ_COMMON_STATUS_MASK, val, AUX_REPLY_RECEIVED_MASK); + break; + + case AUX_ERR_INT_MASK: + displayport_write_mask(SYSTEM_IRQ_COMMON_STATUS_MASK, val, AUX_ERR_MASK); + break; + + case HDCP_LINK_CHECK_INT_MASK: + displayport_write_mask(SYSTEM_IRQ_COMMON_STATUS_MASK, val, HDCP_R0_CHECK_FLAG_MASK); + break; + + case HDCP_LINK_FAIL_INT_MASK: + displayport_write_mask(SYSTEM_IRQ_COMMON_STATUS_MASK, val, HDCP_LINK_CHK_FAIL_MASK); + break; + + case HDCP_R0_READY_INT_MASK: + displayport_write_mask(SYSTEM_IRQ_COMMON_STATUS_MASK, val, HDCP_R0_CHECK_FLAG_MASK); + break; + + case PLL_LOCK_CHG_INT_MASK: + displayport_write_mask(SYSTEM_IRQ_COMMON_STATUS_MASK, val, PLL_LOCK_CHG_MASK); + break; + + case VIDEO_FIFO_UNDER_FLOW_MASK: + displayport_write_mask(SST1_INTERRUPT_MASK_SET0, val, MAPI_FIFO_UNDER_FLOW_MASK); + break; + + case VSYNC_DET_INT_MASK: + displayport_write_mask(SST1_INTERRUPT_MASK_SET0, val, VSYNC_DET_MASK); + break; + + case AUDIO_FIFO_UNDER_RUN_INT_MASK: + displayport_write_mask(SST1_AUDIO_BUFFER_CONTROL, val, MASTER_AUDIO_BUFFER_EMPTY_INT_EN); + displayport_write_mask(SST1_AUDIO_BUFFER_CONTROL, val, MASTER_AUDIO_BUFFER_EMPTY_INT_MASK); + break; + + case AUDIO_FIFO_OVER_RUN_INT_MASK: + displayport_write_mask(SST1_INTERRUPT_STATUS_SET1, val, AFIFO_OVER); + break; + + case ALL_INT_MASK: + displayport_write(SYSTEM_IRQ_COMMON_STATUS_MASK, 0xFF); + displayport_write(SST1_INTERRUPT_MASK_SET0, 0xFF); + displayport_write(SST1_INTERRUPT_STATUS_SET1, 0xFF); + break; + } +} + +void displayport_reg_set_interrupt(u32 en) +{ + u32 val = en ? ~0 : 0; + + displayport_write(SYSTEM_IRQ_COMMON_STATUS, ~0); + displayport_write(SST1_INTERRUPT_STATUS_SET0, ~0); + displayport_write(SST1_INTERRUPT_STATUS_SET1, ~0); + displayport_write(SST2_INTERRUPT_STATUS_SET0, ~0); + displayport_write(SST2_INTERRUPT_STATUS_SET1, ~0); + +#if !defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) + displayport_reg_set_interrupt_mask(HPD_IRQ_INT_MASK, val); + displayport_reg_set_interrupt_mask(HOTPLUG_CHG_INT_MASK, val); + displayport_reg_set_interrupt_mask(HPD_LOST_INT_MASK, val); + displayport_reg_set_interrupt_mask(PLUG_INT_MASK, val); +#endif + displayport_reg_set_interrupt_mask(VSYNC_DET_INT_MASK, val); + displayport_reg_set_interrupt_mask(VIDEO_FIFO_UNDER_FLOW_MASK, val); + displayport_reg_set_interrupt_mask(AUDIO_FIFO_UNDER_RUN_INT_MASK, val); +} + +u32 displayport_reg_get_interrupt_and_clear(u32 interrupt_status_register) +{ + u32 val = 0; + + if (interrupt_status_register != SST1_AUDIO_BUFFER_CONTROL) { + val = displayport_read(interrupt_status_register); + + displayport_write(interrupt_status_register, ~0); + } else { + val = displayport_read_mask(SST1_AUDIO_BUFFER_CONTROL, + MASTER_AUDIO_BUFFER_EMPTY_INT); + + displayport_write_mask(SST1_AUDIO_BUFFER_CONTROL, + 1, MASTER_AUDIO_BUFFER_EMPTY_INT); + } + + return val; +} + +void displayport_reg_set_daynamic_range(enum displayport_dynamic_range_type dynamic_range) +{ + displayport_write_mask(SST1_VIDEO_CONTROL, dynamic_range, DYNAMIC_RANGE_MODE); +} + +void displayport_reg_set_video_bist_mode(u32 en) +{ + u32 val = en ? ~0 : 0; + + displayport_write_mask(SST1_VIDEO_CONTROL, val, STRM_VALID_FORCE | STRM_VALID_CTRL); + displayport_write_mask(SST1_VIDEO_BIST_CONTROL, val, BIST_EN); +} + +void displayport_reg_set_audio_bist_mode(u32 en) +{ + u32 val = en ? ~0 : 0; + + displayport_write_mask(SST1_AUDIO_BIST_CONTROL, 0x0F, SIN_AMPL); + displayport_write_mask(SST1_AUDIO_BIST_CONTROL, val, AUD_BIST_EN); +} + +void displayport_reg_video_format_register_setting(videoformat video_format) +{ + u32 val = 0; + + val += supported_videos[video_format].dv_timings.bt.height; + val += supported_videos[video_format].dv_timings.bt.vfrontporch; + val += supported_videos[video_format].dv_timings.bt.vsync; + val += supported_videos[video_format].dv_timings.bt.vbackporch; + displayport_write(SST1_VIDEO_VERTICAL_TOTAL_PIXELS, val); + + val = 0; + val += supported_videos[video_format].dv_timings.bt.width; + val += supported_videos[video_format].dv_timings.bt.hfrontporch; + val += supported_videos[video_format].dv_timings.bt.hsync; + val += supported_videos[video_format].dv_timings.bt.hbackporch; + displayport_write(SST1_VIDEO_HORIZONTAL_TOTAL_PIXELS, val); + + val = supported_videos[video_format].dv_timings.bt.height; + displayport_write(SST1_VIDEO_VERTICAL_ACTIVE, val); + + val = supported_videos[video_format].dv_timings.bt.vfrontporch; + displayport_write(SST1_VIDEO_VERTICAL_FRONT_PORCH, val); + + val = supported_videos[video_format].dv_timings.bt.vbackporch; + displayport_write(SST1_VIDEO_VERTICAL_BACK_PORCH, val); + + val = supported_videos[video_format].dv_timings.bt.width; + displayport_write(SST1_VIDEO_HORIZONTAL_ACTIVE, val); + + val = supported_videos[video_format].dv_timings.bt.hfrontporch; + displayport_write(SST1_VIDEO_HORIZONTAL_FRONT_PORCH, val); + + val = supported_videos[video_format].dv_timings.bt.hbackporch; + displayport_write(SST1_VIDEO_HORIZONTAL_BACK_PORCH, val); + + val = supported_videos[video_format].v_sync_pol; + displayport_write_mask(SST1_VIDEO_CONTROL, val, VSYNC_POLARITY); + + val = supported_videos[video_format].h_sync_pol; + displayport_write_mask(SST1_VIDEO_CONTROL, val, HSYNC_POLARITY); +} + +u32 displayport_reg_get_video_clk(void) +{ + struct displayport_device *displayport = get_displayport_drvdata(); + + return supported_videos[displayport->cur_video].dv_timings.bt.pixelclock; +} + +u32 displayport_reg_get_ls_clk(void) +{ + u32 val; + u32 ls_clk; + + val = displayport_reg_get_link_bw(); + + if (val == LINK_RATE_5_4Gbps) + ls_clk = 540000000; + else if (val == LINK_RATE_2_7Gbps) + ls_clk = 270000000; + else /* LINK_RATE_1_62Gbps */ + ls_clk = 162000000; + + return ls_clk; +} + +void displayport_reg_set_video_clock(void) +{ + u32 stream_clk = 0; + u32 ls_clk = 0; + u32 mvid_master = 0; + u32 nvid_master = 0; + + stream_clk = displayport_reg_get_video_clk() / 1000; + ls_clk = displayport_reg_get_ls_clk() / 1000; + + mvid_master = stream_clk >> 1; + nvid_master = ls_clk; + + displayport_write(SST1_MVID_MASTER_MODE, mvid_master); + displayport_write(SST1_NVID_MASTER_MODE, nvid_master); + + displayport_write_mask(SST1_MAIN_CONTROL, 1, MVID_MODE); + + displayport_write(SST1_MVID_SFR_CONFIGURE, stream_clk); + displayport_write(SST1_NVID_SFR_CONFIGURE, ls_clk); +} + +void displayport_reg_set_active_symbol(void) +{ + u64 TU_off = 0; /* TU Size when FEC is off*/ + u64 TU_on = 0; /* TU Size when FEC is on*/ + u32 bpp = 0; /* Bit Per Pixel */ + u32 lanecount = 0; + u32 bandwidth = 0; + u32 integer_fec_off = 0; + u32 fraction_fec_off = 0; + u32 threshold_fec_off = 0; + u32 integer_fec_on = 0; + u32 fraction_fec_on = 0; + u32 threshold_fec_on = 0; + u32 clk = 0; + struct displayport_device *displayport = get_displayport_drvdata(); + + displayport_write_mask(SST1_ACTIVE_SYMBOL_MODE_CONTROL, 1, ACTIVE_SYMBOL_MODE_CONTROL); + displayport_write_mask(SST1_ACTIVE_SYMBOL_THRESHOLD_SEL_FEC_OFF, 1, ACTIVE_SYMBOL_THRESHOLD_SEL_FEC_OFF); + displayport_write_mask(SST1_ACTIVE_SYMBOL_THRESHOLD_SEL_FEC_ON, 1, ACTIVE_SYMBOL_THRESHOLD_SEL_FEC_ON); + + switch (displayport->bpc) { + case BPC_8: + bpp = 24; + break; + case BPC_10: + bpp = 30; + break; + default: + bpp = 18; + break; + } /* if DSC on, bpp / 3 */ + + /* change to Mbps from bps of pixel clock*/ + clk = displayport_reg_get_video_clk() / 1000; + + bandwidth = displayport_reg_get_ls_clk() / 1000; + lanecount = displayport_reg_get_lane_count(); + + TU_off = ((clk * bpp * 32) * 10000000000) / (lanecount * bandwidth * 8); + TU_on = (TU_off * 1000) / 976; + + integer_fec_off = (u32)(TU_off / 10000000000); + fraction_fec_off = (u32)((TU_off - (integer_fec_off * 10000000000)) / 10); + integer_fec_on = (u32)(TU_on / 10000000000); + fraction_fec_on = (u32)((TU_on - (integer_fec_on * 10000000000)) / 10); + + if (integer_fec_off <= 2) + threshold_fec_off = 7; + else if (integer_fec_off > 2 && integer_fec_off <= 5) + threshold_fec_off = 8; + else if (integer_fec_off > 5) + threshold_fec_off = 9; + + if (integer_fec_on <= 2) + threshold_fec_on = 7; + else if (integer_fec_on > 2 && integer_fec_on <= 5) + threshold_fec_on = 8; + else if (integer_fec_on > 5) + threshold_fec_on = 9; + + displayport_info("integer_fec_off = %d\n", integer_fec_off); + displayport_info("fraction_fec_off = %d\n", fraction_fec_off); + displayport_info("threshold_fec_off = %d\n", threshold_fec_off); + displayport_info("integer_fec_on = %d\n", integer_fec_on); + displayport_info("fraction_fec_on = %d\n", fraction_fec_on); + displayport_info("threshold_fec_on = %d\n", threshold_fec_on); + + displayport_write_mask(SST1_ACTIVE_SYMBOL_INTEGER_FEC_OFF, integer_fec_off, ACTIVE_SYMBOL_INTEGER_FEC_OFF); + displayport_write_mask(SST1_ACTIVE_SYMBOL_FRACTION_FEC_OFF, fraction_fec_off, ACTIVE_SYMBOL_FRACTION_FEC_OFF); + displayport_write_mask(SST1_ACTIVE_SYMBOL_THRESHOLD_FEC_OFF, threshold_fec_off, ACTIVE_SYMBOL_FRACTION_FEC_OFF); + + displayport_write_mask(SST1_ACTIVE_SYMBOL_INTEGER_FEC_ON, integer_fec_on, ACTIVE_SYMBOL_INTEGER_FEC_ON); + displayport_write_mask(SST1_ACTIVE_SYMBOL_FRACTION_FEC_ON, fraction_fec_on, ACTIVE_SYMBOL_FRACTION_FEC_OFF); + displayport_write_mask(SST1_ACTIVE_SYMBOL_THRESHOLD_FEC_ON, threshold_fec_on, ACTIVE_SYMBOL_THRESHOLD_FEC_ON); +} + +void displayport_reg_enable_interface_crc(u32 en) +{ + u32 val = en ? ~0 : 0; + + displayport_write_mask(SST1_STREAM_IF_CRC_CONTROL_1, val, IF_CRC_EN); + displayport_write_mask(SST1_STREAM_IF_CRC_CONTROL_1, val, IF_CRC_SW_COMPARE); + + if (val == 0) { + displayport_write_mask(SST1_STREAM_IF_CRC_CONTROL_1, 1, IF_CRC_CLEAR); + displayport_write_mask(SST1_STREAM_IF_CRC_CONTROL_1, 0, IF_CRC_CLEAR); + } +} + +void displayport_reg_get_interface_crc(u32 *crc_r_result, u32 *crc_g_result, u32 *crc_b_result) +{ + *crc_r_result = displayport_read_mask(SST1_STREAM_IF_CRC_CONTROL_2, IF_CRC_R_RESULT); + *crc_g_result = displayport_read_mask(SST1_STREAM_IF_CRC_CONTROL_3, IF_CRC_G_RESULT); + *crc_b_result = displayport_read_mask(SST1_STREAM_IF_CRC_CONTROL_4, IF_CRC_B_RESULT); +} + +void displayport_reg_enable_stand_alone_crc(u32 en) +{ + u32 val = en ? ~0 : 0; + + displayport_write_mask(PCS_SA_CRC_CONTROL_1, val, + SA_CRC_LANE_0_ENABLE | SA_CRC_LANE_1_ENABLE | + SA_CRC_LANE_2_ENABLE | SA_CRC_LANE_3_ENABLE); + displayport_write_mask(PCS_SA_CRC_CONTROL_1, val, SA_CRC_SW_COMPARE); + + if (val == 0) { + displayport_write_mask(PCS_SA_CRC_CONTROL_1, 1, SA_CRC_CLEAR); + displayport_write_mask(PCS_SA_CRC_CONTROL_1, 0, SA_CRC_CLEAR); + } +} + +void displayport_reg_get_stand_alone_crc(u32 *ln0, u32 *ln1, u32 *ln2, u32 *ln3) +{ + *ln0 = displayport_read_mask(PCS_SA_CRC_CONTROL_2, SA_CRC_LN0_RESULT); + *ln1 = displayport_read_mask(PCS_SA_CRC_CONTROL_3, SA_CRC_LN1_RESULT); + *ln2 = displayport_read_mask(PCS_SA_CRC_CONTROL_4, SA_CRC_LN2_RESULT); + *ln3 = displayport_read_mask(PCS_SA_CRC_CONTROL_5, SA_CRC_LN3_RESULT); +} + +void displayport_reg_aux_ch_buf_clr(void) +{ + displayport_write_mask(AUX_BUFFER_CLEAR, 1, AUX_BUF_CLR); +} + +void displayport_reg_aux_defer_ctrl(u32 en) +{ + u32 val = en ? ~0 : 0; + + displayport_write_mask(AUX_COMMAND_CONTROL, val, DEFER_CTRL_EN); +} + +void displayport_reg_set_aux_reply_timeout(void) +{ + displayport_write_mask(AUX_CONTROL, AUX_TIMEOUT_1800us, AUX_REPLY_TIMER_MODE); +} + +void displayport_reg_set_aux_ch_command(enum displayport_aux_ch_command_type aux_ch_mode) +{ + displayport_write_mask(AUX_REQUEST_CONTROL, aux_ch_mode, REQ_COMM); +} + +void displayport_reg_set_aux_ch_address(u32 aux_ch_address) +{ + displayport_write_mask(AUX_REQUEST_CONTROL, aux_ch_address, REQ_ADDR); +} + +void displayport_reg_set_aux_ch_length(u32 aux_ch_length) +{ + displayport_write_mask(AUX_REQUEST_CONTROL, aux_ch_length - 1, REQ_LENGTH); +} + +void displayport_reg_aux_ch_send_buf(u8 *aux_ch_send_buf, u32 aux_ch_length) +{ + int i; + + for (i = 0; i < aux_ch_length; i++) { + displayport_write_mask(AUX_TX_DATA_SET0 + ((i / 4) * 4), + aux_ch_send_buf[i], (0x000000FF << ((i % 4) * 8))); + } +} + +void displayport_reg_aux_ch_received_buf(u8 *aux_ch_received_buf, u32 aux_ch_length) +{ + int i; + + for (i = 0; i < aux_ch_length; i++) { + aux_ch_received_buf[i] = + (displayport_read_mask(AUX_RX_DATA_SET0 + ((i / 4) * 4), + 0xFF << ((i % 4) * 8)) >> (i % 4) * 8); + } +} + +int displayport_reg_set_aux_ch_operation_enable(void) +{ + u32 cnt = 5000; + u32 state; + u32 val0, val1; + + displayport_write_mask(AUX_TRANSACTION_START, 1, AUX_TRAN_START); + + do { + state = displayport_read(AUX_TRANSACTION_START) & AUX_TRAN_START; + cnt--; + udelay(10); + } while (state && cnt); + + if (!cnt) { + displayport_err("AUX_TRAN_START waiting timeout.\n"); + return -ETIME; + } + + val0 = displayport_read(AUX_MONITOR_1); + val1 = displayport_read(AUX_MONITOR_2); + + if ((val0 & AUX_CMD_STATUS) != 0x00 || val1 != 0x00) { + displayport_dbg("AUX_MONITOR_1 : 0x%X, AUX_MONITOR_2 : 0x%X\n", val0, val1); + displayport_dbg("AUX_CONTROL : 0x%X, AUX_REQUEST_CONTROL : 0x%X, AUX_COMMAND_CONTROL : 0x%X\n", + displayport_read(AUX_CONTROL), + displayport_read(AUX_REQUEST_CONTROL), + displayport_read(AUX_COMMAND_CONTROL)); + + udelay(400); + return -EIO; + } + + return 0; +} + +void displayport_reg_set_aux_ch_address_only_command(u32 en) +{ + displayport_write_mask(AUX_ADDR_ONLY_COMMAND, en, ADDR_ONLY_CMD); +} + +int displayport_reg_dpcd_write(u32 address, u32 length, u8 *data) +{ + int ret; + int retry_cnt = AUX_RETRY_COUNT; + struct displayport_device *displayport = get_displayport_drvdata(); + + mutex_lock(&displayport->aux_lock); + while(retry_cnt > 0) { + displayport_reg_aux_ch_buf_clr(); + displayport_reg_aux_defer_ctrl(1); + displayport_reg_set_aux_reply_timeout(); + displayport_reg_set_aux_ch_command(DPCD_WRITE); + displayport_reg_set_aux_ch_address(address); + displayport_reg_set_aux_ch_length(length); + displayport_reg_aux_ch_send_buf(data, length); + ret = displayport_reg_set_aux_ch_operation_enable(); + if (ret == 0) + break; + + retry_cnt--; + } + + mutex_unlock(&displayport->aux_lock); + + return ret; +} + +int displayport_reg_dpcd_read(u32 address, u32 length, u8 *data) +{ + int ret; + struct displayport_device *displayport = get_displayport_drvdata(); + int retry_cnt = AUX_RETRY_COUNT; + + mutex_lock(&displayport->aux_lock); + while(retry_cnt > 0) { + displayport_reg_set_aux_ch_command(DPCD_READ); + displayport_reg_set_aux_ch_address(address); + displayport_reg_set_aux_ch_length(length); + displayport_reg_aux_ch_buf_clr(); + displayport_reg_aux_defer_ctrl(1); + displayport_reg_set_aux_reply_timeout(); + ret = displayport_reg_set_aux_ch_operation_enable(); + + if (ret == 0) + break; + retry_cnt--; + } + + if (ret == 0) + displayport_reg_aux_ch_received_buf(data, length); + + mutex_unlock(&displayport->aux_lock); + + return ret; +} + +int displayport_reg_dpcd_write_burst(u32 address, u32 length, u8 *data) +{ + int ret = 0; + u32 i, buf_length, length_calculation; + + length_calculation = length; + for (i = 0; i < length; i += AUX_DATA_BUF_COUNT) { + if (length_calculation >= AUX_DATA_BUF_COUNT) { + buf_length = AUX_DATA_BUF_COUNT; + length_calculation -= AUX_DATA_BUF_COUNT; + } else { + buf_length = length % AUX_DATA_BUF_COUNT; + length_calculation = 0; + } + + ret = displayport_reg_dpcd_write(address + i, buf_length, data + i); + if (ret != 0) { + displayport_err("displayport_reg_dpcd_write_burst fail\n"); + break; + } + } + + return ret; +} + +int displayport_reg_dpcd_read_burst(u32 address, u32 length, u8 *data) +{ + int ret = 0; + u32 i, buf_length, length_calculation; + + length_calculation = length; + + for (i = 0; i < length; i += AUX_DATA_BUF_COUNT) { + if (length_calculation >= AUX_DATA_BUF_COUNT) { + buf_length = AUX_DATA_BUF_COUNT; + length_calculation -= AUX_DATA_BUF_COUNT; + } else { + buf_length = length % AUX_DATA_BUF_COUNT; + length_calculation = 0; + } + + ret = displayport_reg_dpcd_read(address + i, buf_length, data + i); + + if (ret != 0) { + displayport_err("displayport_reg_dpcd_read_burst fail\n"); + break; + } + } + + return ret; +} + +int displayport_reg_edid_write(u8 edid_addr_offset, u32 length, u8 *data) +{ + u32 i, buf_length, length_calculation; + int ret; + int retry_cnt = AUX_RETRY_COUNT; + + while(retry_cnt > 0) { + displayport_reg_aux_ch_buf_clr(); + displayport_reg_aux_defer_ctrl(1); + displayport_reg_set_aux_reply_timeout(); + displayport_reg_set_aux_ch_command(I2C_WRITE); + displayport_reg_set_aux_ch_address(EDID_ADDRESS); + displayport_reg_set_aux_ch_length(1); + displayport_reg_aux_ch_send_buf(&edid_addr_offset, 1); + ret = displayport_reg_set_aux_ch_operation_enable(); + + if (ret == 0) { + length_calculation = length; + + + /* displayport_write_mask(AUX_Ch_MISC_Ctrl_1, 0x3, 3 << 6); */ + for (i = 0; i < length; i += AUX_DATA_BUF_COUNT) { + if (length_calculation >= AUX_DATA_BUF_COUNT) { + buf_length = AUX_DATA_BUF_COUNT; + length_calculation -= AUX_DATA_BUF_COUNT; + } else { + buf_length = length%AUX_DATA_BUF_COUNT; + length_calculation = 0; + } + + displayport_reg_set_aux_ch_length(buf_length); + displayport_reg_aux_ch_send_buf(data+((i/AUX_DATA_BUF_COUNT)*AUX_DATA_BUF_COUNT), buf_length); + ret = displayport_reg_set_aux_ch_operation_enable(); + + if (ret == 0) + break; + } + } + + if (ret == 0) { + displayport_reg_set_aux_ch_address_only_command(1); + ret = displayport_reg_set_aux_ch_operation_enable(); + displayport_reg_set_aux_ch_address_only_command(0); + } + if (ret == 0) + break; + + retry_cnt--; + } + + + return ret; +} + +int displayport_reg_edid_read(u8 edid_addr_offset, u32 length, u8 *data) +{ + u32 i, buf_length, length_calculation; + int ret; + struct displayport_device *displayport = get_displayport_drvdata(); + int retry_cnt = AUX_RETRY_COUNT; + + mutex_lock(&displayport->aux_lock); + + while(retry_cnt > 0) { + displayport_reg_set_aux_ch_command(I2C_WRITE); + displayport_reg_set_aux_ch_address(EDID_ADDRESS); + displayport_reg_set_aux_ch_address_only_command(1); + ret = displayport_reg_set_aux_ch_operation_enable(); + displayport_reg_set_aux_ch_address_only_command(0); + displayport_dbg("1st address only request in EDID read\n"); + + displayport_reg_aux_ch_buf_clr(); + displayport_reg_aux_defer_ctrl(1); + displayport_reg_set_aux_reply_timeout(); + displayport_reg_set_aux_ch_address_only_command(0); + displayport_reg_set_aux_ch_command(I2C_WRITE); + displayport_reg_set_aux_ch_address(EDID_ADDRESS); + displayport_reg_set_aux_ch_length(1); + displayport_reg_aux_ch_send_buf(&edid_addr_offset, 1); + ret = displayport_reg_set_aux_ch_operation_enable(); + + displayport_dbg("EDID address command in EDID read\n"); + + if (ret == 0) { + displayport_reg_set_aux_ch_command(I2C_READ); + length_calculation = length; + + for (i = 0; i < length; i += AUX_DATA_BUF_COUNT) { + if (length_calculation >= AUX_DATA_BUF_COUNT) { + buf_length = AUX_DATA_BUF_COUNT; + length_calculation -= AUX_DATA_BUF_COUNT; + } else { + buf_length = length%AUX_DATA_BUF_COUNT; + length_calculation = 0; + } + + displayport_reg_set_aux_ch_length(buf_length); + displayport_reg_aux_ch_buf_clr(); + ret = displayport_reg_set_aux_ch_operation_enable(); + + if (ret == 0) { + displayport_reg_aux_ch_received_buf(data+((i/AUX_DATA_BUF_COUNT)*AUX_DATA_BUF_COUNT), buf_length); + displayport_dbg("AUX buffer read count = %d in EDID read\n", i); + } else { + displayport_dbg("AUX buffer read fail in EDID read\n"); + break; + } + } + } + + if (ret == 0) { + displayport_reg_set_aux_ch_command(I2C_WRITE); + displayport_reg_set_aux_ch_address(EDID_ADDRESS); + displayport_reg_set_aux_ch_address_only_command(1); + ret = displayport_reg_set_aux_ch_operation_enable(); + displayport_reg_set_aux_ch_address_only_command(0); + + displayport_dbg("2nd address only request in EDID read\n"); + } + + if (ret == 0) + break; + + retry_cnt--; + } + + mutex_unlock(&displayport->aux_lock); + + return ret; +} + +void displayport_reg_set_lane_map(u32 lane0, u32 lane1, u32 lane2, u32 lane3) +{ + displayport_write_mask(PCS_LANE_CONTROL, lane0, LANE0_MAP); + displayport_write_mask(PCS_LANE_CONTROL, lane1, LANE1_MAP); + displayport_write_mask(PCS_LANE_CONTROL, lane2, LANE2_MAP); + displayport_write_mask(PCS_LANE_CONTROL, lane3, LANE3_MAP); +} + +void displayport_reg_set_lane_map_config(void) +{ +#if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) + struct displayport_device *displayport = get_displayport_drvdata(); + + switch (displayport->ccic_notify_dp_conf) { + case CCIC_NOTIFY_DP_PIN_UNKNOWN: + break; + + case CCIC_NOTIFY_DP_PIN_A: + if (displayport->dp_sw_sel) + displayport_reg_set_lane_map(3, 1, 2, 0); + else + displayport_reg_set_lane_map(2, 0, 3, 1); + break; + + case CCIC_NOTIFY_DP_PIN_B: + if (displayport->dp_sw_sel) + displayport_reg_set_lane_map(3, 2, 1, 0); + else + displayport_reg_set_lane_map(1, 0, 2, 3); + break; + + case CCIC_NOTIFY_DP_PIN_C: + case CCIC_NOTIFY_DP_PIN_E: + case CCIC_NOTIFY_DP_PIN_D: + case CCIC_NOTIFY_DP_PIN_F: + if (displayport->dp_sw_sel) + displayport_reg_set_lane_map(3, 2, 0, 1); + else + displayport_reg_set_lane_map(0, 1, 3, 2); + break; + + default: + break; + } +#endif +} + +void displayport_reg_lh_p_ch_power(u32 en) +{ + u32 cnt = 20 * 1000; /* wait 1ms */ + u32 state; + + if (en) { + displayport_write_mask(SYSTEM_SST1_FUNCTION_ENABLE, 1, + SST1_LH_PWR_ON); + do { + state = displayport_read_mask( + SYSTEM_SST1_FUNCTION_ENABLE, + SST1_LH_PWR_ON_STATUS); + cnt--; + udelay(1); + } while (!state && cnt); + + if (!cnt) + displayport_err("%s on is timeout[%d].\n", __func__, state); + } else { + displayport_write_mask(SYSTEM_SST1_FUNCTION_ENABLE, 0, + SST1_LH_PWR_ON); + do { + state = displayport_read_mask( + SYSTEM_SST1_FUNCTION_ENABLE, + SST1_LH_PWR_ON_STATUS); + cnt--; + udelay(1); + } while (state && cnt); + + if (!cnt) { + displayport_err("SYSTEM_CLK_CONTROL[0x%08x]\n", + displayport_read(SYSTEM_CLK_CONTROL)); + displayport_err("SYSTEM_PLL_LOCK_CONTROL[0x%08x]\n", + displayport_read(SYSTEM_PLL_LOCK_CONTROL)); + displayport_err("SYSTEM_DEBUG[0x%08x]\n", + displayport_read(SYSTEM_DEBUG)); + displayport_err("SYSTEM_DEBUG_LH_PCH[0x%08x]\n", + displayport_read(SYSTEM_DEBUG_LH_PCH)); + displayport_err("SST1_VIDEO_CONTROL[0x%08x]\n", + displayport_read(SST1_VIDEO_CONTROL)); + displayport_err("SST1_VIDEO_DEBUG_FSM_STATE[0x%08x]\n", + displayport_read(SST1_VIDEO_DEBUG_FSM_STATE)); + displayport_err("SST1_VIDEO_DEBUG_MAPI[0x%08x]\n", + displayport_read(SST1_VIDEO_DEBUG_MAPI)); + displayport_err("SYSTEM_SW_FUNCTION_ENABLE[0x%08x]\n", + displayport_read(SYSTEM_SW_FUNCTION_ENABLE)); + displayport_err("SYSTEM_COMMON_FUNCTION_ENABLE[0x%08x]\n", + displayport_read(SYSTEM_COMMON_FUNCTION_ENABLE)); + displayport_err("SYSTEM_SST1_FUNCTION_ENABLE[0x%08x]\n", + displayport_read(SYSTEM_SST1_FUNCTION_ENABLE)); + } + } +} + +void displayport_reg_sw_function_en(void) +{ + displayport_write_mask(SYSTEM_SW_FUNCTION_ENABLE, 1, SW_FUNC_EN); +} + +void displayport_reg_phy_aux_level_setting(void) +{ + displayport_phy_write_mask(DP_REG_F, 0x0F, AUX_TX_LVL_CTRL); +} + +void displayport_reg_phy_init(void) +{ + displayport_reg_phy_reset(1); + displayport_reg_phy_init_setting(); + displayport_reg_phy_mode_setting(); + displayport_reg_phy_reset(0); + displayport_reg_wait_phy_pll_lock(); + displayport_reg_phy_aux_level_setting(); +} + +void displayport_reg_phy_disable(void) +{ + displayport_reg_phy_reset(1); + displayport_phy_write(DP_REG_0, 0x00); + +#if defined(CONFIG_PHY_SAMSUNG_USB_CAL) + exynos_usbdrd_inform_dp_use(0, displayport_reg_get_lane_count()); + exynos_usbdrd_request_phy_isol(); +#endif +} + +void displayport_reg_init(void) +{ + displayport_reg_sw_reset(); + + displayport_reg_phy_init(); + + displayport_reg_function_enable(); + displayport_reg_lh_p_ch_power(1); + displayport_reg_sw_function_en(); + + displayport_reg_set_interrupt(1); + displayport_reg_set_lane_map_config(); +} + +void displayport_reg_set_video_configuration(videoformat video_format, u8 bpc, u8 range) +{ + displayport_reg_set_daynamic_range((range)?CEA_RANGE:VESA_RANGE); + displayport_write_mask(SST1_VIDEO_CONTROL, bpc, BPC); /* 0 : 6bits, 1 : 8bits */ + displayport_write_mask(SST1_VIDEO_CONTROL, 0, COLOR_FORMAT); /* RGB */ + displayport_reg_video_format_register_setting(video_format); + displayport_reg_set_video_clock(); + displayport_reg_set_active_symbol(); + displayport_write_mask(SST1_VIDEO_MASTER_TIMING_GEN, 1, VIDEO_MASTER_TIME_GEN); + displayport_write_mask(SST1_MAIN_CONTROL, 0, VIDEO_MODE); +} + +void displayport_reg_set_bist_video_configuration(videoformat video_format, u8 bpc, u8 type, u8 range) +{ + displayport_reg_set_video_configuration(video_format, bpc, range); + displayport_write_mask(SST1_VIDEO_BIST_CONTROL, type, BIST_TYPE); /* Display BIST type */ + displayport_reg_set_video_bist_mode(1); + + displayport_info("set bist video config format:%d range:%d bpc:%d type:%d\n", + video_format, (range)?1:0, (bpc)?1:0, type); +} + +void displayport_reg_set_bist_video_configuration_for_blue_screen(videoformat video_format) +{ + displayport_reg_set_video_configuration(video_format, BPC_8, CEA_RANGE); /* 8 bits */ + displayport_write(SST1_VIDEO_BIST_USER_DATA_R, 0x00); + displayport_write(SST1_VIDEO_BIST_USER_DATA_G, 0x00); + displayport_write(SST1_VIDEO_BIST_USER_DATA_B, 0xFF); + displayport_write_mask(SST1_VIDEO_BIST_CONTROL, 1, BIST_USER_DATA_EN); + displayport_reg_set_video_bist_mode(1); + + displayport_dbg("set bist video config for blue screen\n"); +} + +void displayport_reg_set_avi_infoframe(struct infoframe avi_infoframe) +{ + u32 avi_infoframe_data = 0; + + avi_infoframe_data = ((u32)avi_infoframe.data[3] << 24) || ((u32)avi_infoframe.data[2] << 16) + || ((u32)avi_infoframe.data[1] << 8) || (u32)avi_infoframe.data[0]; + displayport_write(SST1_INFOFRAME_AVI_PACKET_DATA_SET0, avi_infoframe_data); + + avi_infoframe_data = ((u32)avi_infoframe.data[7] << 24) || ((u32)avi_infoframe.data[6] << 16) + || ((u32)avi_infoframe.data[5] << 8) || (u32)avi_infoframe.data[4]; + displayport_write(SST1_INFOFRAME_AVI_PACKET_DATA_SET1, avi_infoframe_data); + + avi_infoframe_data = ((u32)avi_infoframe.data[11] << 24) || ((u32)avi_infoframe.data[10] << 16) + || ((u32)avi_infoframe.data[9] << 8) || (u32)avi_infoframe.data[8]; + displayport_write(SST1_INFOFRAME_AVI_PACKET_DATA_SET2, avi_infoframe_data); + + avi_infoframe_data = (u32)avi_infoframe.data[12]; + displayport_write(SST1_INFOFRAME_AVI_PACKET_DATA_SET3, avi_infoframe_data); + + displayport_write_mask(SST1_INFOFRAME_UPDATE_CONTROL, 1, AVI_INFO_UPDATE); + displayport_write_mask(SST1_INFOFRAME_SEND_CONTROL, 1, AVI_INFO_SEND); +} + +void displayport_reg_set_audio_infoframe(struct infoframe audio_infoframe, u32 en) +{ + u32 audio_infoframe_data = 0; + + audio_infoframe_data = ((u32)audio_infoframe.data[3] << 24) || ((u32)audio_infoframe.data[2] << 16) + || ((u32)audio_infoframe.data[1] << 8) || (u32)audio_infoframe.data[0]; + displayport_write(SST1_INFOFRAME_AUDIO_PACKET_DATA_SET0, audio_infoframe_data); + + audio_infoframe_data = ((u32)audio_infoframe.data[7] << 24) || ((u32)audio_infoframe.data[6] << 16) + || ((u32)audio_infoframe.data[5] << 8) || (u32)audio_infoframe.data[4]; + displayport_write(SST1_INFOFRAME_AUDIO_PACKET_DATA_SET1, audio_infoframe_data); + + audio_infoframe_data = ((u32)audio_infoframe.data[10] << 8) || (u32)audio_infoframe.data[9]; + displayport_write(SST1_INFOFRAME_AUDIO_PACKET_DATA_SET2, audio_infoframe_data); + + displayport_write_mask(SST1_INFOFRAME_UPDATE_CONTROL, en, AUDIO_INFO_UPDATE); + displayport_write_mask(SST1_INFOFRAME_SEND_CONTROL, en, AUDIO_INFO_SEND); +} + +void displayport_reg_set_hdr_infoframe(struct infoframe hdr_infoframe, u32 en) +{ + int i, j; + u32 hdr_infoframe_data = 0; + + if (en == 1) { + for (i = 0; i < HDR_INFOFRAME_LENGTH; i++) { + for (j = 0; j < DATA_NUM_PER_REG; j++) { + hdr_infoframe_data |= + (u32)hdr_infoframe.data[i] + << ((j % DATA_NUM_PER_REG) * INFOFRAME_DATA_SIZE); + + if (j < DATA_NUM_PER_REG - 1) + i++; + + if (i >= HDR_INFOFRAME_LENGTH) + break; + } + + displayport_write(SST1_HDR_PACKET_DATA_SET_0 + + i / DATA_NUM_PER_REG * DATA_NUM_PER_REG, + hdr_infoframe_data); + + hdr_infoframe_data = 0; + } + } + + for (i = 0; i <= SST1_HDR_PACKET_DATA_SET_7 - SST1_HDR_PACKET_DATA_SET_0; + i += DATA_NUM_PER_REG) { + displayport_dbg("SST1_HDR_PACKET_DATA_SET_%d = 0x%x", + i / DATA_NUM_PER_REG, + displayport_read(SST1_HDR_PACKET_DATA_SET_0 + i)); + } + + displayport_write_mask(SST1_INFOFRAME_UPDATE_CONTROL, en, HDR_INFO_UPDATE); + displayport_write_mask(SST1_INFOFRAME_SEND_CONTROL, en, HDR_INFO_SEND); +} + +void displayport_reg_start(void) +{ + displayport_write_mask(SST1_VIDEO_ENABLE, 1, VIDEO_EN); +} + +void displayport_reg_video_mute(u32 en) +{ +/* displayport_dbg("set mute %d\n", en); + * displayport_write_mask(Video_Control_1, en, VIDEO_MUTE); + */ +} + +void displayport_reg_stop(void) +{ + displayport_write_mask(SST1_VIDEO_ENABLE, 0, VIDEO_EN); +} + +/* Set SA CRC, For Sorting Vector */ +void displayport_reg_set_stand_alone_crc(u32 crc_ln0_ref, u32 crc_ln1_ref, u32 crc_ln2_ref, u32 crc_ln3_ref) +{ + displayport_write_mask(PCS_SA_CRC_CONTROL_2, crc_ln0_ref, SA_CRC_LN0_REF); + displayport_write_mask(PCS_SA_CRC_CONTROL_3, crc_ln1_ref, SA_CRC_LN1_REF); + displayport_write_mask(PCS_SA_CRC_CONTROL_4, crc_ln2_ref, SA_CRC_LN2_REF); + displayport_write_mask(PCS_SA_CRC_CONTROL_5, crc_ln3_ref, SA_CRC_LN3_REF); +} + +void displayport_reg_set_result_flag_clear(void) +{ + displayport_write_mask(PCS_SA_CRC_CONTROL_1, 1, SA_CRC_CLEAR); + displayport_write_mask(PCS_SA_CRC_CONTROL_1, 0, SA_CRC_CLEAR); +} + +void displayport_reg_enable_stand_alone_crc_hw(u32 en) +{ + u32 val = en ? ~0 : 0; + + displayport_write_mask(PCS_SA_CRC_CONTROL_1, 0, SA_CRC_SW_COMPARE); /* use H/W compare */ + + displayport_write_mask(PCS_SA_CRC_CONTROL_1, val, + SA_CRC_LANE_0_ENABLE | SA_CRC_LANE_1_ENABLE | SA_CRC_LANE_2_ENABLE | SA_CRC_LANE_3_ENABLE); +} + +int displayport_reg_get_stand_alone_crc_result(void) +{ + u32 val; + int err = 0; + + val = displayport_read_mask(PCS_SA_CRC_CONTROL_1, 0x00000FF0); + val = val >> 4; + + if (val == 0xF0) { + displayport_info("DisplayPort SA CRC Pass !!!\n"); + } else { + err = -1; + displayport_info("DisplayPort SA CRC Fail : 0x%02X !!!\n", val); + } + + return err; +} + +/* SA CRC Condition : 8bpc, 4lane, 640x10 size, BIST_TYPE=0, BIST_WIDTH =0 */ +int displayport_reg_stand_alone_crc_sorting(void) +{ + int ret; + + displayport_reg_init(); + displayport_reg_set_lane_count(4); + displayport_reg_set_bist_video_configuration(V640X10P60SACRC, BPC_8, COLOR_BAR, VESA_RANGE); + displayport_reg_set_stand_alone_crc(0x135E, 0x135E, 0x135E, 0x135E); + displayport_reg_enable_stand_alone_crc_hw(1); + displayport_reg_start(); + + mdelay(20); + + displayport_reg_set_result_flag_clear(); + + mdelay(20); + + ret = displayport_reg_get_stand_alone_crc_result(); + + displayport_reg_set_result_flag_clear(); + displayport_reg_enable_stand_alone_crc_hw(0); + + displayport_reg_set_video_bist_mode(0); + displayport_reg_stop(); + + return ret; +} + +void displayport_reg_set_audio_m_n(audio_sync_mode audio_sync_mode, + enum audio_sampling_frequency audio_sampling_freq) +{ + u32 link_bandwidth_set; + u32 array_set; + u32 m_value; + u32 n_value; + + link_bandwidth_set = displayport_reg_get_link_bw(); + if (link_bandwidth_set == LINK_RATE_1_62Gbps) + array_set = 0; + else if (link_bandwidth_set == LINK_RATE_2_7Gbps) + array_set = 1; + else/* if (link_bandwidth_set == LINK_RATE_5_4Gbps)*/ + array_set = 2; + + if (audio_sync_mode == ASYNC_MODE) { + m_value = audio_async_m_n[0][array_set][audio_sampling_freq]; + n_value = audio_async_m_n[1][array_set][audio_sampling_freq]; + displayport_write_mask(SST1_MAIN_CONTROL, 0, MAUD_MODE); + } else { + m_value = audio_sync_m_n[0][array_set][audio_sampling_freq]; + n_value = audio_sync_m_n[1][array_set][audio_sampling_freq]; + displayport_write_mask(SST1_MAIN_CONTROL, 1, MAUD_MODE); + } + + displayport_write(SST1_MAUD_SFR_CONFIGURE, m_value); + displayport_write(SST1_NAUD_SFR_CONFIGURE, n_value); +} + +void displayport_reg_set_audio_function_enable(u32 en) +{ + displayport_write_mask(SYSTEM_SST1_FUNCTION_ENABLE, en, SST1_AUDIO_FUNC_EN); +} + +void displayport_reg_set_dma_burst_size(enum audio_dma_word_length word_length) +{ + displayport_write_mask(SST1_AUDIO_CONTROL, word_length, DMA_BURST_SEL); +} + +void displayport_reg_set_dma_pack_mode(enum audio_16bit_dma_mode dma_mode) +{ + displayport_write_mask(SST1_AUDIO_CONTROL, dma_mode, AUDIO_BIT_MAPPING_TYPE); +} + +void displayport_reg_set_pcm_size(enum audio_bit_per_channel audio_bit_size) +{ + displayport_write_mask(SST1_AUDIO_CONTROL, audio_bit_size, PCM_SIZE); +} + +void displayport_reg_set_audio_ch_status_same(u32 en) +{ + displayport_write_mask(SST1_AUDIO_CONTROL, en, AUDIO_CH_STATUS_SAME); +} + +void displayport_reg_set_audio_ch(u32 audio_ch_cnt) +{ + displayport_write_mask(SST1_AUDIO_BUFFER_CONTROL, + audio_ch_cnt - 1, MASTER_AUDIO_CHANNEL_COUNT); +} + +void displayport_reg_set_audio_ch_mapping(u8 pkt_1, u8 pkt_2, u8 pkt_3, u8 pkt_4, + u8 pkt_5, u8 pkt_6, u8 pkt_7, u8 pkt_8) +{ + displayport_write_mask(SST1_AUDIO_CHANNEL_1_4_REMAP, pkt_1, AUD_CH_01_REMAP); + displayport_write_mask(SST1_AUDIO_CHANNEL_1_4_REMAP, pkt_2, AUD_CH_02_REMAP); + displayport_write_mask(SST1_AUDIO_CHANNEL_1_4_REMAP, pkt_3, AUD_CH_03_REMAP); + displayport_write_mask(SST1_AUDIO_CHANNEL_1_4_REMAP, pkt_4, AUD_CH_04_REMAP); + + displayport_write_mask(SST1_AUDIO_CHANNEL_5_8_REMAP, pkt_5, AUD_CH_05_REMAP); + displayport_write_mask(SST1_AUDIO_CHANNEL_5_8_REMAP, pkt_6, AUD_CH_06_REMAP); + displayport_write_mask(SST1_AUDIO_CHANNEL_5_8_REMAP, pkt_7, AUD_CH_07_REMAP); + displayport_write_mask(SST1_AUDIO_CHANNEL_5_8_REMAP, pkt_8, AUD_CH_08_REMAP); + + displayport_dbg("audio 1~4 channel mapping = 0x%X\n", + displayport_read(SST1_AUDIO_CHANNEL_1_4_REMAP)); + displayport_dbg("audio 5~8 channel mapping = 0x%X\n", + displayport_read(SST1_AUDIO_CHANNEL_5_8_REMAP)); +} + +void displayport_reg_set_audio_fifo_function_enable(u32 en) +{ + displayport_write_mask(SYSTEM_SST1_FUNCTION_ENABLE, en, SST1_AUDIO_FIFO_FUNC_EN); +} + +void displayport_reg_set_audio_sampling_frequency(enum audio_sampling_frequency audio_sampling_freq) +{ + u32 link_bandwidth_set; + u32 n_aud_master_set; + + link_bandwidth_set = displayport_reg_get_link_bw(); + if (link_bandwidth_set == LINK_RATE_1_62Gbps) + n_aud_master_set = 0; + else if (link_bandwidth_set == LINK_RATE_2_7Gbps) + n_aud_master_set = 1; + else/* if (link_bandwidth_set == LINK_RATE_5_4Gbps)*/ + n_aud_master_set = 2; + + displayport_write(SST1_MAUD_MASTER_MODE, m_aud_master[audio_sampling_freq]); + displayport_write(SST1_NAUD_MASTER_MODE, n_aud_master[n_aud_master_set]); +} + +void displayport_reg_set_dp_audio_enable(u32 en) +{ + u32 val = en ? ~0 : 0; + + displayport_write_mask(SST1_AUDIO_ENABLE, val, AUDIO_EN); +} + +void displayport_reg_set_audio_master_mode_enable(u32 en) +{ + u32 val = en ? ~0 : 0; + + displayport_write_mask(SST1_AUDIO_MASTER_TIMING_GEN, val, AUDIO_MASTER_TIME_GEN); +} + +void displayport_reg_set_ch_status_ch_cnt(u32 audio_ch_cnt) +{ + displayport_write_mask(SST1_AUDIO_BIST_CHANNEL_STATUS_SET0, + audio_ch_cnt, CH_NUM); + + displayport_write_mask(SST1_AUDIO_BIST_CHANNEL_STATUS_SET0, + audio_ch_cnt, SOURCE_NUM); + + displayport_write_mask(SST1_AUDIO_CHANNEL_1_2_STATUS_CTRL_0, + audio_ch_cnt, CH_NUM); + + displayport_write_mask(SST1_AUDIO_CHANNEL_1_2_STATUS_CTRL_0, + audio_ch_cnt, SOURCE_NUM); +} + +void displayport_reg_set_ch_status_word_length(enum audio_bit_per_channel audio_bit_size) +{ + u32 word_max = 0; + u32 sample_word_length = 0; + + switch (audio_bit_size) { + case AUDIO_24_BIT: + word_max = 1; + sample_word_length = 0x05; + break; + + case AUDIO_16_BIT: + word_max = 0; + sample_word_length = 0x01; + break; + + case AUDIO_20_BIT: + word_max = 0; + sample_word_length = 0x05; + break; + + default: + word_max = 0; + sample_word_length = 0x00; + break; + } + + displayport_write_mask(SST1_AUDIO_BIST_CHANNEL_STATUS_SET1, + word_max, WORD_MAX); + + displayport_write_mask(SST1_AUDIO_BIST_CHANNEL_STATUS_SET1, + sample_word_length, WORD_LENGTH); + + displayport_write_mask(SST1_AUDIO_CHANNEL_1_2_STATUS_CTRL_1, + word_max, WORD_MAX); + + displayport_write_mask(SST1_AUDIO_CHANNEL_1_2_STATUS_CTRL_1, + sample_word_length, WORD_LENGTH); +} + +void displayport_reg_set_ch_status_sampling_frequency(enum audio_sampling_frequency audio_sampling_freq) +{ + u32 fs_freq = 0; + + switch (audio_sampling_freq) { + case FS_32KHZ: + fs_freq = 0x03; + break; + case FS_44KHZ: + fs_freq = 0x00; + break; + case FS_48KHZ: + fs_freq = 0x02; + break; + case FS_88KHZ: + fs_freq = 0x08; + break; + case FS_96KHZ: + fs_freq = 0x0A; + break; + case FS_176KHZ: + fs_freq = 0x0C; + break; + case FS_192KHZ: + fs_freq = 0x0E; + break; + default: + fs_freq = 0x00; + break; + } + + displayport_write_mask(SST1_AUDIO_BIST_CHANNEL_STATUS_SET0, fs_freq, FS_FREQ); + displayport_write_mask(SST1_AUDIO_CHANNEL_1_2_STATUS_CTRL_0, fs_freq, FS_FREQ); +} + +void displayport_reg_set_ch_status_clock_accuracy(enum audio_clock_accuracy clock_accuracy) +{ + displayport_write_mask(SST1_AUDIO_BIST_CHANNEL_STATUS_SET0, clock_accuracy, CLK_ACCUR); + displayport_write_mask(SST1_AUDIO_CHANNEL_1_2_STATUS_CTRL_0, clock_accuracy, CLK_ACCUR); +} + +void displayport_reg_wait_buf_full(void) +{ + u32 cnt = 1000; + u32 state = 0; + + do { + state = (displayport_read(SST1_AUDIO_BUFFER_CONTROL) & MASTER_AUDIO_BUFFER_LEVEL) + >> MASTER_AUDIO_BUFFER_LEVEL_BIT_POS; + cnt--; + udelay(1); + } while ((state < AUDIO_BUF_FULL_SIZE) && cnt); + + if (!cnt) + displayport_err("%s is timeout.\n", __func__); +} + +void displayport_reg_set_hdcp22_system_enable(u32 en) +{ + u32 val = en ? ~0 : 0; + + displayport_write_mask(HDCP22_SYS_EN, val, SYSTEM_ENABLE); +} + +void displayport_reg_set_hdcp22_mode(u32 en) +{ + u32 val = en ? ~0 : 0; + + displayport_write_mask(SYSTEM_COMMON_FUNCTION_ENABLE, val, HDCP22_FUNC_EN); +} + +void displayport_reg_set_hdcp22_encryption_enable(u32 en) +{ + u32 val = en ? ~0 : 0; + + displayport_write_mask(HDCP22_CONTROL, val, HDCP22_ENC_EN); +} + +u32 displayport_reg_get_hdcp22_encryption_enable(void) +{ + return displayport_read_mask(HDCP22_CONTROL, HDCP22_ENC_EN); +} + +void displayport_reg_set_aux_pn_inv(u32 val) +{ + displayport_write_mask(AUX_CONTROL, val, AUX_PN_INV); +} diff --git a/drivers/video/fbdev/exynos/dpu20/cal_9610/dpp_cal.h b/drivers/video/fbdev/exynos/dpu20/cal_9610/dpp_cal.h new file mode 100644 index 000000000000..c9a04885185f --- /dev/null +++ b/drivers/video/fbdev/exynos/dpu20/cal_9610/dpp_cal.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Header file for Exynos9820 DPP CAL + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __SAMSUNG_DPP_CAL_H__ +#define __SAMSUNG_DPP_CAL_H__ + +#include "../decon.h" + +#define MAX_DPP_CNT 6 + +#define SRC_SIZE_MULTIPLE 1 +#define SRC_WIDTH_MIN 16 +#define SRC_WIDTH_MAX 65534 +#define SRC_HEIGHT_MIN 16 +#define SRC_HEIGHT_MAX 8190 +#define IMG_SIZE_MULTIPLE 1 +#define IMG_WIDTH_MIN 16 +#define IMG_WIDTH_MAX 4096 +#define IMG_HEIGHT_MIN 16 +#define IMG_HEIGHT_MAX 4096 +#define IMG_ROT_HEIGHT_MAX 2160 +#define SRC_OFFSET_MULTIPLE 1 + +#define SCALED_WIDTH_MIN 16 +#define SCALED_WIDTH_MAX 4096 +#define SCALED_HEIGHT_MIN 16 +#define SCALED_HEIGHT_MAX 4096 +#define SCALED_SIZE_MULTIPLE 1 +#define SCALED_SIZE_MULTIPLE 1 + +#define BLK_WIDTH_MIN 4 +#define BLK_WIDTH_MAX 4096 +#define BLK_HEIGHT_MIN 1 +#define BLK_HEIGHT_MAX 4096 +#define BLK_SIZE_MULTIPLE 1 +#define BLK_SIZE_MULTIPLE 1 + +#define DST_SIZE_MULTIPLE 1 +#define DST_SIZE_WIDTH_MIN 16 +#define DST_SIZE_WIDTH_MAX 8190 +#define DST_SIZE_HEIGHT_MIN 16 +#define DST_SIZE_HEIGHT_MAX 8190 +#define DST_OFFSET_MULTIPLE 1 +#define DST_IMG_MULTIPLE 1 +#define DST_IMG_WIDTH_MIN 16 +#define DST_IMG_WIDTH_MAX 4096 +#define DST_IMG_HEIGHT_MIN 16 +#define DST_IMG_HEIGHT_MAX 4096 + +struct dpp_params_info { + struct decon_frame src; + struct decon_frame dst; + struct decon_win_rect block; + u32 rot; + + enum dpp_hdr_standard hdr; + u32 min_luminance; + u32 max_luminance; + bool is_4p; + u32 y_2b_strd; + u32 c_2b_strd; + + bool is_comp; + bool is_scale; + bool is_block; + enum decon_pixel_format format; + dma_addr_t addr[MAX_PLANE_ADDR_CNT]; + enum dpp_csc_eq eq_mode; + int h_ratio; + int v_ratio; + + unsigned long rcv_num; +}; + +struct dpp_size_constraints { + u32 src_mul_w; + u32 src_mul_h; + u32 src_w_min; + u32 src_w_max; + u32 src_h_min; + u32 src_h_max; + u32 img_mul_w; + u32 img_mul_h; + u32 img_w_min; + u32 img_w_max; + u32 img_h_min; + u32 img_h_max; + u32 blk_w_min; + u32 blk_w_max; + u32 blk_h_min; + u32 blk_h_max; + u32 blk_mul_w; + u32 blk_mul_h; + u32 src_mul_x; + u32 src_mul_y; + u32 sca_w_min; + u32 sca_w_max; + u32 sca_h_min; + u32 sca_h_max; + u32 sca_mul_w; + u32 sca_mul_h; + u32 dst_mul_w; + u32 dst_mul_h; + u32 dst_w_min; + u32 dst_w_max; + u32 dst_h_min; + u32 dst_h_max; + u32 dst_mul_x; + u32 dst_mul_y; +}; + +struct dpp_img_format { + u32 vgr; + u32 normal; + u32 rot; + u32 scale; + u32 format; + u32 afbc_en; + u32 yuv; + u32 yuv422; + u32 yuv420; + u32 wb; +}; + +/* DPP CAL APIs exposed to DPP driver */ +void dpp_reg_init(u32 id, const unsigned long attr); +int dpp_reg_deinit(u32 id, bool reset, const unsigned long attr); +void dpp_reg_configure_params(u32 id, struct dpp_params_info *p, + const unsigned long attr); +void dpp_constraints_params(struct dpp_size_constraints *vc, + struct dpp_img_format *vi); + +/* DPU DMA DEBUG */ +void dma_reg_dump_com_debug_regs(int id); +void dma_reg_dump_debug_regs(int id); +void dpp_reg_dump_debug_regs(int id); + +/* DPU_DMA and DPP interrupt handler */ +u32 dpp_reg_get_irq_and_clear(u32 id); +u32 idma_reg_get_irq_and_clear(u32 id); +u32 odma_reg_get_irq_and_clear(u32 id); + +#endif /* __SAMSUNG_DPP_CAL_H__ */ diff --git a/drivers/video/fbdev/exynos/dpu20/cal_9610/dpp_reg.c b/drivers/video/fbdev/exynos/dpu20/cal_9610/dpp_reg.c new file mode 100644 index 000000000000..c304b7d05f44 --- /dev/null +++ b/drivers/video/fbdev/exynos/dpu20/cal_9610/dpp_reg.c @@ -0,0 +1,1272 @@ +/* linux/drivers/video/exynos/fbdev/dpu_9810/dpp_regs.c + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Samsung EXYNOS9 SoC series Display Pre Processor driver + * + * 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. + */ + +#include +#include +#include +#if defined(CONFIG_EXYNOS_HDR_TUNABLE_TONEMAPPING) +#include