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
obj-y += core/
+obj-y += exynos/
+
obj-$(CONFIG_FB_MACMODES) += macmodes.o
obj-$(CONFIG_FB_WMT_GE_ROPS) += wmt_ge_rops.o
--- /dev/null
+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"
--- /dev/null
+#
+# 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/
--- /dev/null
+ /*
+ * 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 <soc/samsung/bts.h>
+#include <media/v4l2-subdev.h>
+#if defined(CONFIG_CAL_IF)
+#include <soc/samsung/cal-if.h>
+#endif
+#if defined(CONFIG_SOC_EXYNOS9610)
+#include <dt-bindings/clock/exynos9610.h>
+#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,
+};
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * linux/drivers/video/fbdev/exynos/dpu_9810/decon_reg.c
+ *
+ * Copyright 2013-2017 Samsung Electronics
+ * SeungBeom Park <sb1.park@samsung.com>
+ *
+ * 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
+ * <PPS Table e.g.> 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) <Cf=FG pxl_C, Cb=BG pxl_C>
+ * Alpha : Ar = (c x Af) + (d x Ab) <Af=FG pxl_A, Ab=BG pxl_A>
+ *
+ * [ 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;
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/* 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 <linux/io.h>
+#include <linux/delay.h>
+#include <linux/ktime.h>
+#if defined(CONFIG_EXYNOS_HDR_TUNABLE_TONEMAPPING)
+#include <video/exynos_hdr_tunables.h>
+#endif
+
+#include "../dpp.h"
+#include "../dpp_coef.h"
+#include "../hdr_lut.h"
+
+#define DPP_SC_RATIO_MAX ((1 << 20) * 8 / 8)
+#define DPP_SC_RATIO_7_8 ((1 << 20) * 8 / 7)
+#define DPP_SC_RATIO_6_8 ((1 << 20) * 8 / 6)
+#define DPP_SC_RATIO_5_8 ((1 << 20) * 8 / 5)
+#define DPP_SC_RATIO_4_8 ((1 << 20) * 8 / 4)
+#define DPP_SC_RATIO_3_8 ((1 << 20) * 8 / 3)
+
+/****************** IDMA CAL functions ******************/
+static void idma_reg_set_irq_mask_all(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dma_write_mask(id, IDMA_IRQ, val, IDMA_ALL_IRQ_MASK);
+}
+
+static void idma_reg_set_irq_enable(u32 id)
+{
+ dma_write_mask(id, IDMA_IRQ, ~0, IDMA_IRQ_ENABLE);
+}
+
+static void idma_reg_set_clock_gate_en_all(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dma_write_mask(id, IDMA_ENABLE, val, IDMA_ALL_CLOCK_GATE_EN_MASK);
+}
+
+static void idma_reg_set_in_qos_lut(u32 id, u32 lut_id, u32 qos_t)
+{
+ u32 reg_id;
+
+ if (lut_id == 0)
+ reg_id = DPU_DMA_QOS_LUT07_00;
+ else
+ reg_id = DPU_DMA_QOS_LUT15_08;
+ dma_com_write(id, reg_id, qos_t);
+}
+
+static void idma_reg_set_dynamic_gating_en_all(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dma_write_mask(id, IDMA_DYNAMIC_GATING_EN, val, IDMA_DG_EN_ALL);
+}
+
+static void idma_reg_set_out_frame_alpha(u32 id, u32 alpha)
+{
+ dma_write_mask(id, IDMA_OUT_CON, IDMA_OUT_FRAME_ALPHA(alpha),
+ IDMA_OUT_FRAME_ALPHA_MASK);
+}
+
+static void idma_reg_clear_irq(u32 id, u32 irq)
+{
+ dma_write_mask(id, IDMA_IRQ, ~0, irq);
+}
+
+static void idma_reg_set_sw_reset(u32 id)
+{
+ dma_write_mask(id, IDMA_ENABLE, ~0, IDMA_SRESET);
+}
+
+static int idma_reg_wait_sw_reset_status(u32 id)
+{
+ u32 cfg = 0;
+ unsigned long cnt = 100000;
+
+ do {
+ cfg = dma_read(id, IDMA_ENABLE);
+ if (!(cfg & (IDMA_SRESET)))
+ return 0;
+ udelay(10);
+ } while (--cnt);
+
+ dpp_err("[idma] timeout sw-reset\n");
+
+ return -1;
+}
+
+static void idma_reg_set_coordinates(u32 id, struct decon_frame *src)
+{
+ dma_write(id, IDMA_SRC_OFFSET,
+ IDMA_SRC_OFFSET_Y(src->y) | IDMA_SRC_OFFSET_X(src->x));
+ dma_write(id, IDMA_SRC_SIZE,
+ IDMA_SRC_HEIGHT(src->f_h) | IDMA_SRC_WIDTH(src->f_w));
+ dma_write(id, IDMA_IMG_SIZE,
+ IDMA_IMG_HEIGHT(src->h) | IDMA_IMG_WIDTH(src->w));
+}
+
+static void idma_reg_set_rotation(u32 id, u32 rot)
+{
+ dma_write_mask(id, IDMA_IN_CON, IDMA_ROTATION(rot), IDMA_ROTATION_MASK);
+}
+
+static void idma_reg_set_block_mode(u32 id, bool en, int x, int y, u32 w, u32 h)
+{
+ if (!en) {
+ dma_write_mask(id, IDMA_IN_CON, 0, IDMA_BLOCK_EN);
+ return;
+ }
+
+ dma_write(id, IDMA_BLOCK_OFFSET,
+ IDMA_BLK_OFFSET_Y(y) | IDMA_BLK_OFFSET_X(x));
+ dma_write(id, IDMA_BLOCK_SIZE, IDMA_BLK_HEIGHT(h) | IDMA_BLK_WIDTH(w));
+ dma_write_mask(id, IDMA_IN_CON, ~0, IDMA_BLOCK_EN);
+
+ dpp_dbg("dpp%d: block x(%d) y(%d) w(%d) h(%d)\n", id, x, y, w, h);
+}
+
+static void idma_reg_set_format(u32 id, u32 fmt)
+{
+ dma_write_mask(id, IDMA_IN_CON, IDMA_IMG_FORMAT(fmt),
+ IDMA_IMG_FORMAT_MASK);
+}
+
+#if defined(DMA_BIST)
+static void idma_reg_set_test_pattern(u32 id, u32 pat_id, u32 *pat_dat)
+{
+ dma_write_mask(id, IDMA_IN_REQ_DEST, ~0, IDMA_IN_REG_DEST_SEL_MASK);
+
+ if (pat_id == 0) {
+ dma_com_write(id, DPU_DMA_TEST_PATTERN0_0, pat_dat[0]);
+ dma_com_write(id, DPU_DMA_TEST_PATTERN0_1, pat_dat[1]);
+ dma_com_write(id, DPU_DMA_TEST_PATTERN0_2, pat_dat[2]);
+ dma_com_write(id, DPU_DMA_TEST_PATTERN0_3, pat_dat[3]);
+ } else {
+ dma_com_write(id, DPU_DMA_TEST_PATTERN1_0, pat_dat[4]);
+ dma_com_write(id, DPU_DMA_TEST_PATTERN1_1, pat_dat[5]);
+ dma_com_write(id, DPU_DMA_TEST_PATTERN1_2, pat_dat[6]);
+ dma_com_write(id, DPU_DMA_TEST_PATTERN1_3, pat_dat[7]);
+ }
+}
+#endif
+
+static void idma_reg_set_afbc(u32 id, u32 en, u32 rcv_num)
+{
+ u32 val = en ? ~0 : 0;
+
+ dma_write_mask(id, IDMA_IN_CON, val, IDMA_AFBC_EN);
+ dma_write_mask(id, IDMA_RECOVERY_CTRL, val, IDMA_RECOVERY_EN);
+ dma_com_write_mask(id, DPU_DMA_RECOVERY_NUM_CTRL,
+ DPU_DMA_RECOVERY_NUM(rcv_num),
+ DPU_DMA_RECOVERY_NUM_MASK);
+}
+
+/****************** ODMA CAL functions ******************/
+static void odma_reg_set_irq_mask_all(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dma_write_mask(id, ODMA_IRQ, val, ODMA_ALL_IRQ_MASK);
+}
+
+static void odma_reg_set_irq_enable(u32 id)
+{
+ dma_write_mask(id, ODMA_IRQ, ~0, ODMA_IRQ_ENABLE);
+}
+#if 0
+static void odma_reg_set_clock_gate_en_all(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dma_write_mask(id, ODMA_ENABLE, val, ODMA_ALL_CLOCK_GATE_EN_MASK);
+}
+#endif
+static void odma_reg_set_in_qos_lut(u32 id, u32 lut_id, u32 qos_t)
+{
+ u32 reg_id;
+
+ if (lut_id == 0)
+ reg_id = ODMA_OUT_QOS_LUT07_00;
+ else
+ reg_id = ODMA_OUT_QOS_LUT15_08;
+ dma_write(id, reg_id, qos_t);
+}
+
+static void odma_reg_set_dynamic_gating_en_all(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dma_write_mask(id, ODMA_DYNAMIC_GATING_EN, val, ODMA_DG_EN_ALL);
+}
+
+static void odma_reg_set_out_frame_alpha(u32 id, u32 alpha)
+{
+ dma_write_mask(id, ODMA_OUT_CON1, ODMA_OUT_FRAME_ALPHA(alpha),
+ ODMA_OUT_FRAME_ALPHA_MASK);
+}
+
+static void odma_reg_clear_irq(u32 id, u32 irq)
+{
+ dma_write_mask(id, ODMA_IRQ, ~0, irq);
+}
+
+static void odma_reg_set_sw_reset(u32 id)
+{
+ dma_write_mask(id, ODMA_ENABLE, ~0, ODMA_SRSET);
+}
+
+static int odma_reg_wait_sw_reset_status(u32 id)
+{
+ u32 cfg = 0;
+ unsigned long cnt = 100000;
+
+ do {
+ cfg = dma_read(id, ODMA_ENABLE);
+ if (!(cfg & (ODMA_SRSET)))
+ return 0;
+ udelay(10);
+ } while (--cnt);
+
+ dpp_err("[odma] timeout sw-reset\n");
+
+ return -1;
+}
+
+static void odma_reg_set_coordinates(u32 id, struct decon_frame *dst)
+{
+ dma_write(id, ODMA_DST_OFFSET,
+ ODMA_DST_OFFSET_Y(dst->y) | ODMA_DST_OFFSET_X(dst->x));
+ dma_write(id, ODMA_DST_SIZE,
+ ODMA_DST_HEIGHT(dst->f_h) | ODMA_DST_WIDTH(dst->f_w));
+ dma_write(id, ODMA_OUT_IMG_SIZE,
+ ODMA_OUT_IMG_HEIGHT(dst->h) | ODMA_OUT_IMG_WIDTH(dst->w));
+}
+
+static void odma_reg_set_format(u32 id, u32 fmt)
+{
+ dma_write_mask(id, ODMA_OUT_CON0, ODMA_IMG_FORMAT(fmt),
+ ODMA_IMG_FORMAT_MASK);
+}
+
+/****************** DPP CAL functions ******************/
+static void dpp_reg_set_irq_mask_all(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dpp_write_mask(id, DPP_IRQ, val, DPP_ALL_IRQ_MASK);
+}
+
+static void dpp_reg_set_irq_enable(u32 id)
+{
+ dpp_write_mask(id, DPP_IRQ, ~0, DPP_IRQ_ENABLE);
+}
+
+static void dpp_reg_set_clock_gate_en_all(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dpp_write_mask(id, DPP_ENABLE, val, DPP_ALL_CLOCK_GATE_EN_MASK);
+}
+
+static void dpp_reg_set_dynamic_gating_en_all(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dpp_write_mask(id, DPP_DYNAMIC_GATING_EN, val, DPP_DG_EN_ALL);
+}
+
+static void dpp_reg_set_linecnt(u32 id, u32 en)
+{
+ if (en)
+ dpp_write_mask(id, DPP_LINECNT_CON,
+ DPP_LC_MODE(0) | DPP_LC_ENABLE(1),
+ DPP_LC_MODE_MASK | DPP_LC_ENABLE_MASK);
+ else
+ dpp_write_mask(id, DPP_LINECNT_CON, DPP_LC_ENABLE(0),
+ DPP_LC_ENABLE_MASK);
+}
+
+static void dpp_reg_clear_irq(u32 id, u32 irq)
+{
+ dpp_write_mask(id, DPP_IRQ, ~0, irq);
+}
+
+static void dpp_reg_set_sw_reset(u32 id)
+{
+ dpp_write_mask(id, DPP_ENABLE, ~0, DPP_SRSET);
+}
+
+static int dpp_reg_wait_sw_reset_status(u32 id)
+{
+ u32 cfg = 0;
+ unsigned long cnt = 100000;
+
+ do {
+ cfg = dpp_read(id, DPP_ENABLE);
+ if (!(cfg & (DPP_SRSET)))
+ return 0;
+ udelay(10);
+ } while (--cnt);
+
+ dpp_err("[dpp] timeout sw reset\n");
+
+ return -EBUSY;
+}
+
+static void dpp_reg_set_csc_coef(u32 id, u32 csc_std, u32 csc_rng)
+{
+#if defined(SUPPORT_USER_COEF)
+ u32 val, mask;
+ u32 csc_id = DPP_CSC_ID_BT_2020 + CSC_RANGE_LIMITED;
+ u32 c00, c01, c02;
+ u32 c10, c11, c12;
+ u32 c20, c21, c22;
+
+ if (csc_std == CSC_BT_2020)
+ csc_id = DPP_CSC_ID_BT_2020 + csc_rng;
+ else if (csc_std == CSC_DCI_P3)
+ csc_id = DPP_CSC_ID_DCI_P3 + csc_rng;
+ else
+ dpp_err("Undefined CSC Type!!!\n");
+
+ c00 = csc_3x3_t[csc_id][0][0];
+ c01 = csc_3x3_t[csc_id][0][1];
+ c02 = csc_3x3_t[csc_id][0][2];
+
+ c10 = csc_3x3_t[csc_id][1][0];
+ c11 = csc_3x3_t[csc_id][1][1];
+ c12 = csc_3x3_t[csc_id][1][2];
+
+ c20 = csc_3x3_t[csc_id][2][0];
+ c21 = csc_3x3_t[csc_id][2][1];
+ c22 = csc_3x3_t[csc_id][2][2];
+
+ mask = (DPP_CSC_COEF_H_MASK | DPP_CSC_COEF_L_MASK);
+ val = (DPP_CSC_COEF_H(c01) | DPP_CSC_COEF_L(c00));
+ dpp_write_mask(id, DPP_CSC_COEF0, val, mask);
+
+ val = (DPP_CSC_COEF_H(c10) | DPP_CSC_COEF_L(c02));
+ dpp_write_mask(id, DPP_CSC_COEF1, val, mask);
+
+ val = (DPP_CSC_COEF_H(c12) | DPP_CSC_COEF_L(c11));
+ dpp_write_mask(id, DPP_CSC_COEF2, val, mask);
+
+ val = (DPP_CSC_COEF_H(c21) | DPP_CSC_COEF_L(c20));
+ dpp_write_mask(id, DPP_CSC_COEF3, val, mask);
+
+ mask = DPP_CSC_COEF_L_MASK;
+ val = DPP_CSC_COEF_L(c22);
+ dpp_write_mask(id, DPP_CSC_COEF4, val, mask);
+
+ dpp_dbg("---[CSC Type = %s_%s]---\n",
+ csc_std == 3 ? "DCI_P3" : "BT_2020",
+ csc_rng == 0 ? "LTD" : "FULL");
+ dpp_dbg("0x%3x 0x%3x 0x%3x\n", c00, c01, c02);
+ dpp_dbg("0x%3x 0x%3x 0x%3x\n", c10, c11, c12);
+ dpp_dbg("0x%3x 0x%3x 0x%3x\n", c20, c21, c22);
+#endif
+}
+
+static void dpp_reg_set_csc_params(u32 id, u32 csc_eq)
+{
+ u32 type = (csc_eq >> CSC_STANDARD_SHIFT) & 0x3F;
+ u32 range = (csc_eq >> CSC_RANGE_SHIFT) & 0x7;
+ u32 mode = (type <= CSC_DCI_P3) ? CSC_COEF_HARDWIRED : CSC_COEF_CUSTOMIZED;
+ u32 val, mask;
+
+ val = (DPP_CSC_TYPE(type) | DPP_CSC_RANGE(range) | DPP_CSC_MODE(mode));
+ mask = (DPP_CSC_TYPE_MASK | DPP_CSC_RANGE_MASK | DPP_CSC_MODE_MASK);
+ dpp_write_mask(id, DPP_IN_CON, val, mask);
+
+ if (mode == CSC_COEF_CUSTOMIZED)
+ dpp_reg_set_csc_coef(id, type, range);
+}
+
+static void dpp_reg_set_h_coef(u32 id, u32 h_ratio)
+{
+ int i, j, k, sc_ratio;
+
+ if (h_ratio <= DPP_SC_RATIO_MAX)
+ sc_ratio = 0;
+ else if (h_ratio <= DPP_SC_RATIO_7_8)
+ sc_ratio = 1;
+ else if (h_ratio <= DPP_SC_RATIO_6_8)
+ sc_ratio = 2;
+ else if (h_ratio <= DPP_SC_RATIO_5_8)
+ sc_ratio = 3;
+ else if (h_ratio <= DPP_SC_RATIO_4_8)
+ sc_ratio = 4;
+ else if (h_ratio <= DPP_SC_RATIO_3_8)
+ sc_ratio = 5;
+ else
+ sc_ratio = 6;
+
+ for (i = 0; i < 9; i++)
+ for (j = 0; j < 8; j++)
+ for (k = 0; k < 2; k++)
+ dpp_write(id, DPP_H_COEF(i, j, k),
+ h_coef_8t[sc_ratio][i][j]);
+}
+
+static void dpp_reg_set_v_coef(u32 id, u32 v_ratio)
+{
+ int i, j, k, sc_ratio;
+
+ if (v_ratio <= DPP_SC_RATIO_MAX)
+ sc_ratio = 0;
+ else if (v_ratio <= DPP_SC_RATIO_7_8)
+ sc_ratio = 1;
+ else if (v_ratio <= DPP_SC_RATIO_6_8)
+ sc_ratio = 2;
+ else if (v_ratio <= DPP_SC_RATIO_5_8)
+ sc_ratio = 3;
+ else if (v_ratio <= DPP_SC_RATIO_4_8)
+ sc_ratio = 4;
+ else if (v_ratio <= DPP_SC_RATIO_3_8)
+ sc_ratio = 5;
+ else
+ sc_ratio = 6;
+
+ for (i = 0; i < 9; i++)
+ for (j = 0; j < 4; j++)
+ for (k = 0; k < 2; k++)
+ dpp_write(id, DPP_V_COEF(i, j, k),
+ v_coef_4t[sc_ratio][i][j]);
+}
+
+static void dpp_reg_set_scale_ratio(u32 id, struct dpp_params_info *p)
+{
+ dpp_write_mask(id, DPP_MAIN_H_RATIO, DPP_H_RATIO(p->h_ratio),
+ DPP_H_RATIO_MASK);
+ dpp_write_mask(id, DPP_MAIN_V_RATIO, DPP_V_RATIO(p->v_ratio),
+ DPP_V_RATIO_MASK);
+
+ dpp_reg_set_h_coef(id, p->h_ratio);
+ dpp_reg_set_v_coef(id, p->v_ratio);
+
+ dpp_dbg("h_ratio : %#x, v_ratio : %#x\n", p->h_ratio, p->v_ratio);
+}
+
+static void dpp_reg_set_img_size(u32 id, u32 w, u32 h)
+{
+ dpp_write(id, DPP_IMG_SIZE, DPP_IMG_HEIGHT(h) | DPP_IMG_WIDTH(w));
+}
+
+static void dpp_reg_set_scaled_img_size(u32 id, u32 w, u32 h)
+{
+ dpp_write(id, DPP_SCALED_IMG_SIZE,
+ DPP_SCALED_IMG_HEIGHT(h) | DPP_SCALED_IMG_WIDTH(w));
+}
+
+static void dpp_reg_set_alpha_type(u32 id, u32 type)
+{
+ /* [type] 0=per-frame, 1=per-pixel */
+ dpp_write_mask(id, DPP_IN_CON, DPP_ALPHA_SEL(type), DPP_ALPHA_SEL_MASK);
+}
+
+static void dpp_reg_set_format(u32 id, u32 fmt)
+{
+ dpp_write_mask(id, DPP_IN_CON, DPP_IMG_FORMAT(fmt), DPP_IMG_FORMAT_MASK);
+}
+
+static void dpp_reg_set_eotf_lut(u32 id, struct dpp_params_info *p)
+{
+ u32 i = 0;
+ u32 *lut_x = NULL;
+ u32 *lut_y = NULL;
+
+ if (p->hdr == DPP_HDR_ST2084) {
+ if (p->max_luminance > 1000) {
+ lut_x = eotf_x_axis_st2084_4000;
+ lut_y = eotf_y_axis_st2084_4000;
+ } else {
+ lut_x = eotf_x_axis_st2084_1000;
+ lut_y = eotf_y_axis_st2084_1000;
+ }
+ } else if (p->hdr == DPP_HDR_HLG) {
+ lut_x = eotf_x_axis_hlg;
+ lut_y = eotf_y_axis_hlg;
+ } else {
+ dpp_err("Undefined HDR standard Type!!!\n");
+ return;
+ }
+
+ for (i = 0; i < MAX_EOTF; i++) {
+ dpp_write_mask(id,
+ DPP_HDR_EOTF_X_AXIS_ADDR(i),
+ DPP_HDR_EOTF_X_AXIS_VAL(i, lut_x[i]),
+ DPP_HDR_EOTF_MASK(i));
+ dpp_write_mask(id,
+ DPP_HDR_EOTF_Y_AXIS_ADDR(i),
+ DPP_HDR_EOTF_Y_AXIS_VAL(i, lut_y[i]),
+ DPP_HDR_EOTF_MASK(i));
+ }
+}
+
+static void dpp_reg_set_gm_lut(u32 id, struct dpp_params_info *p)
+{
+ u32 i = 0;
+ u32 *lut_gm = NULL;
+
+ if (p->eq_mode == CSC_BT_2020) {
+ lut_gm = gm_coef_2020_p3;
+ } else if (p->eq_mode == CSC_DCI_P3) {
+ return;
+ } else {
+ dpp_err("Undefined HDR CSC Type!!!\n");
+ return;
+ }
+
+ for (i = 0; i < MAX_GM; i++) {
+ dpp_write_mask(id,
+ DPP_HDR_GM_COEF_ADDR(i),
+ lut_gm[i],
+ DPP_HDR_GM_COEF_MASK);
+ }
+}
+
+static void dpp_reg_set_tm_lut(u32 id, struct dpp_params_info *p)
+{
+#if defined(CONFIG_EXYNOS_HDR_TUNABLE_TONEMAPPING)
+ u32 i = 0;
+ u32 *lut_x = NULL;
+ u32 *lut_y = NULL;
+
+ if (!exynos_hdr_get_tm_lut_xy(tm_x_tune, tm_y_tune)) {
+ if ((p->max_luminance > 1000) && (p->max_luminance < 10000)) {
+ lut_x = tm_x_axis_gamma_2P2_4000;
+ lut_y = tm_y_axis_gamma_2P2_4000;
+ } else {
+ lut_x = tm_x_axis_gamma_2P2_1000;
+ lut_y = tm_y_axis_gamma_2P2_1000;
+ }
+ } else {
+ lut_x = tm_x_tune;
+ lut_y = tm_y_tune;
+ }
+
+ for (i = 0; i < MAX_TM; i++) {
+ dpp_write_mask(id,
+ DPP_HDR_TM_X_AXIS_ADDR(i),
+ DPP_HDR_TM_X_AXIS_VAL(i, lut_x[i]),
+ DPP_HDR_TM_MASK(i));
+ dpp_write_mask(id,
+ DPP_HDR_TM_Y_AXIS_ADDR(i),
+ DPP_HDR_TM_Y_AXIS_VAL(i, lut_y[i]),
+ DPP_HDR_TM_MASK(i));
+ }
+#endif
+}
+
+static void dpp_reg_set_hdr_params(u32 id, struct dpp_params_info *p)
+{
+ u32 val, val2, mask;
+
+ val = (p->hdr == DPP_HDR_ST2084 || p->hdr == DPP_HDR_HLG) ? ~0 : 0;
+ mask = DPP_HDR_ON_MASK | DPP_EOTF_ON_MASK | DPP_TM_ON_MASK;
+ dpp_write_mask(id, DPP_VGRF_HDR_CON, val, mask);
+
+ val2 = (p->eq_mode != CSC_DCI_P3) ? ~0 : 0;
+ dpp_write_mask(id, DPP_VGRF_HDR_CON, val2, DPP_GM_ON_MASK);
+
+ if (val) {
+ dpp_reg_set_eotf_lut(id, p);
+ dpp_reg_set_gm_lut(id, p);
+ dpp_reg_set_tm_lut(id, p);
+ }
+}
+
+/****************** WB MUX CAL functions ******************/
+static void wb_mux_reg_set_clock_gate_en_all(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dpp_write_mask(id, DPU_WB_ENABLE, val, WB_ALL_CLOCK_GATE_EN_MASK);
+}
+
+static void wb_mux_reg_set_dynamic_gating_en_all(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dpp_write_mask(id, DPU_WB_DYNAMIC_GATING_EN, val, WB_DG_EN_ALL);
+}
+
+static void wb_mux_reg_set_out_frame_alpha(u32 id, u32 alpha)
+{
+ dpp_write_mask(id, DPU_WB_OUT_CON1, WB_OUT_FRAME_ALPHA(alpha),
+ WB_OUT_FRAME_ALPHA_MASK);
+}
+
+static void wb_mux_reg_set_sw_reset(u32 id)
+{
+ dpp_write_mask(id, DPU_WB_ENABLE, ~0, WB_SRSET);
+}
+
+static int wb_mux_reg_wait_sw_reset_status(u32 id)
+{
+ u32 cfg = 0;
+ unsigned long cnt = 100000;
+
+ do {
+ cfg = dpp_read(id, DPU_WB_ENABLE);
+ if (!(cfg & (WB_SRSET)))
+ return 0;
+ udelay(10);
+ } while (--cnt);
+
+ dpp_err("[wb] timeout sw-reset\n");
+
+ return -1;
+}
+
+static void wb_mux_reg_set_csc_params(u32 id, u32 csc_eq)
+{
+ u32 type = (csc_eq >> CSC_STANDARD_SHIFT) & 0x3F;
+ u32 range = (csc_eq >> CSC_RANGE_SHIFT) & 0x7;
+
+ u32 rgb_type = (type << 1) | (range << 0);
+ /* only support {601, 709, N, W} */
+ if (rgb_type > 3) {
+ dpp_warn("[WB] Unsupported RGB type(%d) !\n", rgb_type);
+ dpp_warn("[WB] -> forcing BT_601_LIMITTED\n");
+ rgb_type = ((CSC_BT_601 << 1) | CSC_RANGE_LIMITED);
+ }
+
+ dpp_write_mask(id, DPU_WB_OUT_CON0, WB_RGB_TYPE(rgb_type),
+ WB_RGB_TYPE_MASK);
+}
+
+static void wb_mux_reg_set_dst_size(u32 id, u32 w, u32 h)
+{
+ dpp_write(id, DPU_WB_DST_SIZE, WB_DST_HEIGHT(h) | WB_DST_WIDTH(w));
+}
+
+static void wb_mux_reg_set_csc_r2y(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dpp_write_mask(id, DPU_WB_OUT_CON0, val, WB_CSC_R2Y_MASK);
+}
+
+static void wb_mux_reg_set_uv_offset(u32 id, u32 off_x, u32 off_y)
+{
+ dpp_write_mask(id, DPU_WB_OUT_CON1,
+ WB_UV_OFFSET_Y(off_y) | WB_UV_OFFSET_X(off_x),
+ WB_UV_OFFSET_Y_MASK | WB_UV_OFFSET_X_MASK);
+}
+
+/********** IDMA and ODMA combination CAL functions **********/
+static void dma_reg_set_base_addr(u32 id, struct dpp_params_info *p,
+ const unsigned long attr)
+{
+ if (test_bit(DPP_ATTR_IDMA, &attr)) {
+ dma_write(id, IDMA_IN_BASE_ADDR_Y, p->addr[0]);
+ if (p->is_comp)
+ dma_write(id, IDMA_IN_BASE_ADDR_C, p->addr[0]);
+ else
+ dma_write(id, IDMA_IN_BASE_ADDR_C, p->addr[1]);
+ if (p->is_4p) {
+ dma_write(id, IDMA_IN_BASE_ADDR_Y2, p->addr[2]);
+ dma_write(id, IDMA_IN_BASE_ADDR_C2, p->addr[3]);
+ dma_write_mask(id, IDMA_2BIT_STRIDE,
+ IDMA_LUMA_2B_STRIDE(p->y_2b_strd),
+ IDMA_LUMA_2B_STRIDE_MASK);
+ dma_write_mask(id, IDMA_2BIT_STRIDE,
+ IDMA_CHROMA_2B_STRIDE(p->c_2b_strd),
+ IDMA_CHROMA_2B_STRIDE_MASK);
+ }
+ } else if (test_bit(DPP_ATTR_ODMA, &attr)) {
+ dma_write(id, ODMA_IN_BASE_ADDR_Y, p->addr[0]);
+ dma_write(id, ODMA_IN_BASE_ADDR_C, p->addr[1]);
+ }
+ dpp_dbg("dpp%d: base addr 1p(0x%p) 2p(0x%p) 3p(0x%p) 4p(0x%p)\n", id,
+ (void *)p->addr[0], (void *)p->addr[1],
+ (void *)p->addr[2], (void *)p->addr[3]);
+}
+
+/********** IDMA, ODMA, DPP and WB MUX combination CAL functions **********/
+static void dma_dpp_reg_set_coordinates(u32 id, struct dpp_params_info *p,
+ const unsigned long attr)
+{
+ if (test_bit(DPP_ATTR_IDMA, &attr)) {
+ idma_reg_set_coordinates(id, &p->src);
+
+ if (test_bit(DPP_ATTR_DPP, &attr)) {
+ if (p->rot > DPP_ROT_180)
+ dpp_reg_set_img_size(id, p->src.h, p->src.w);
+ else
+ dpp_reg_set_img_size(id, p->src.w, p->src.h);
+ }
+
+ if (test_bit(DPP_ATTR_SCALE, &attr))
+ dpp_reg_set_scaled_img_size(id, p->dst.w, p->dst.h);
+ } else if (test_bit(DPP_ATTR_ODMA, &attr)) {
+ odma_reg_set_coordinates(id, &p->src);
+ wb_mux_reg_set_dst_size(id, p->src.w, p->src.h);
+ }
+}
+
+static int dma_dpp_reg_set_format(u32 id, struct dpp_params_info *p,
+ const unsigned long attr)
+{
+ u32 fmt;
+ u32 alpha_type = 0; /* 0: per-frame, 1: per-pixel */
+ u32 fmt_type = 0;
+ u32 is_yuv = 0;
+
+ switch (p->format) {
+ case DECON_PIXEL_FORMAT_ARGB_8888:
+ fmt = IDMA_IMG_FORMAT_ARGB8888;
+ fmt_type = DPP_IMG_FORMAT_ARGB8888;
+ alpha_type = 1;
+ break;
+ case DECON_PIXEL_FORMAT_ABGR_8888:
+ fmt = IDMA_IMG_FORMAT_ABGR8888;
+ fmt_type = DPP_IMG_FORMAT_ARGB8888;
+ alpha_type = 1;
+ break;
+ case DECON_PIXEL_FORMAT_RGBA_8888:
+ fmt = IDMA_IMG_FORMAT_RGBA8888;
+ fmt_type = DPP_IMG_FORMAT_ARGB8888;
+ alpha_type = 1;
+ break;
+ case DECON_PIXEL_FORMAT_BGRA_8888:
+ fmt = IDMA_IMG_FORMAT_BGRA8888;
+ fmt_type = DPP_IMG_FORMAT_ARGB8888;
+ alpha_type = 1;
+ break;
+ case DECON_PIXEL_FORMAT_XRGB_8888:
+ fmt = IDMA_IMG_FORMAT_XRGB8888;
+ fmt_type = DPP_IMG_FORMAT_ARGB8888;
+ break;
+ case DECON_PIXEL_FORMAT_XBGR_8888:
+ fmt = IDMA_IMG_FORMAT_XBGR8888;
+ fmt_type = DPP_IMG_FORMAT_ARGB8888;
+ break;
+ case DECON_PIXEL_FORMAT_RGBX_8888:
+ fmt = IDMA_IMG_FORMAT_RGBX8888;
+ fmt_type = DPP_IMG_FORMAT_ARGB8888;
+ break;
+ case DECON_PIXEL_FORMAT_BGRX_8888:
+ fmt = IDMA_IMG_FORMAT_BGRX8888;
+ fmt_type = DPP_IMG_FORMAT_ARGB8888;
+ break;
+ case DECON_PIXEL_FORMAT_RGB_565:
+ if (p->is_comp)
+ fmt = IDMA_IMG_FORMAT_BGR565;
+ else
+ fmt = IDMA_IMG_FORMAT_RGB565;
+ fmt_type = DPP_IMG_FORMAT_ARGB8888;
+ break;
+ /* TODO: add ARGB1555 & ARGB4444 */
+ case DECON_PIXEL_FORMAT_ARGB_2101010:
+ fmt = IDMA_IMG_FORMAT_ARGB2101010;
+ fmt_type = DPP_IMG_FORMAT_ARGB8101010;
+ alpha_type = 1;
+ break;
+ case DECON_PIXEL_FORMAT_ABGR_2101010:
+ fmt = IDMA_IMG_FORMAT_ABGR2101010;
+ fmt_type = DPP_IMG_FORMAT_ARGB8101010;
+ alpha_type = 1;
+ break;
+ case DECON_PIXEL_FORMAT_RGBA_1010102:
+ fmt = IDMA_IMG_FORMAT_RGBA2101010;
+ fmt_type = DPP_IMG_FORMAT_ARGB8101010;
+ alpha_type = 1;
+ break;
+ case DECON_PIXEL_FORMAT_BGRA_1010102:
+ fmt = IDMA_IMG_FORMAT_BGRA2101010;
+ fmt_type = DPP_IMG_FORMAT_ARGB8101010;
+ alpha_type = 1;
+ break;
+
+ case DECON_PIXEL_FORMAT_NV12:
+ case DECON_PIXEL_FORMAT_NV12M:
+ fmt = IDMA_IMG_FORMAT_YUV420_2P;
+ fmt_type = DPP_IMG_FORMAT_YUV420_8P;
+ is_yuv = 1;
+ break;
+ case DECON_PIXEL_FORMAT_NV21:
+ case DECON_PIXEL_FORMAT_NV21M:
+ case DECON_PIXEL_FORMAT_NV12N:
+ fmt = IDMA_IMG_FORMAT_YVU420_2P;
+ fmt_type = DPP_IMG_FORMAT_YUV420_8P;
+ is_yuv = 1;
+ break;
+
+ case DECON_PIXEL_FORMAT_NV12N_10B:
+ fmt = IDMA_IMG_FORMAT_YVU420_8P2;
+ fmt_type = DPP_IMG_FORMAT_YUV420_8P2;
+ is_yuv = 1;
+ break;
+ case DECON_PIXEL_FORMAT_NV12M_P010:
+ fmt = IDMA_IMG_FORMAT_YUV420_P010;
+ fmt_type = DPP_IMG_FORMAT_YUV420_P010;
+ is_yuv = 1;
+ break;
+ case DECON_PIXEL_FORMAT_NV21M_P010:
+ fmt = IDMA_IMG_FORMAT_YVU420_P010;
+ fmt_type = DPP_IMG_FORMAT_YUV420_P010;
+ is_yuv = 1;
+ break;
+ case DECON_PIXEL_FORMAT_NV12M_S10B:
+ fmt = IDMA_IMG_FORMAT_YVU420_8P2;
+ fmt_type = DPP_IMG_FORMAT_YUV420_8P2;
+ is_yuv = 1;
+ break;
+ case DECON_PIXEL_FORMAT_NV21M_S10B:
+ fmt = IDMA_IMG_FORMAT_YUV420_8P2;
+ fmt_type = DPP_IMG_FORMAT_YUV420_8P2;
+ is_yuv = 1;
+ break;
+ case DECON_PIXEL_FORMAT_NV16:
+ fmt = IDMA_IMG_FORMAT_YVU422_2P;
+ fmt_type = DPP_IMG_FORMAT_YUV422_8P;
+ is_yuv = 1;
+ break;
+ case DECON_PIXEL_FORMAT_NV61:
+ fmt = IDMA_IMG_FORMAT_YUV422_2P;
+ fmt_type = DPP_IMG_FORMAT_YUV422_8P;
+ is_yuv = 1;
+ break;
+ case DECON_PIXEL_FORMAT_NV16M_P210:
+ fmt = IDMA_IMG_FORMAT_YUV422_P210;
+ fmt_type = DPP_IMG_FORMAT_YUV422_P210;
+ is_yuv = 1;
+ break;
+ case DECON_PIXEL_FORMAT_NV61M_P210:
+ fmt = IDMA_IMG_FORMAT_YVU422_P210;
+ fmt_type = DPP_IMG_FORMAT_YUV422_P210;
+ is_yuv = 1;
+ break;
+ case DECON_PIXEL_FORMAT_NV16M_S10B:
+ fmt = IDMA_IMG_FORMAT_YUV422_8P2;
+ fmt_type = DPP_IMG_FORMAT_YUV422_8P2;
+ is_yuv = 1;
+ break;
+ case DECON_PIXEL_FORMAT_NV61M_S10B:
+ fmt = IDMA_IMG_FORMAT_YVU422_8P2;
+ fmt_type = DPP_IMG_FORMAT_YUV422_8P2;
+ is_yuv = 1;
+ break;
+ default:
+ dpp_err("Unsupported Format\n");
+ return -EINVAL;
+ }
+
+ if (test_bit(DPP_ATTR_IDMA, &attr)) {
+ idma_reg_set_format(id, fmt);
+ if (test_bit(DPP_ATTR_DPP, &attr)) {
+ dpp_reg_set_alpha_type(id, alpha_type);
+ dpp_reg_set_format(id, fmt_type);
+ }
+ } else if (test_bit(DPP_ATTR_ODMA, &attr)) {
+ odma_reg_set_format(id, fmt);
+ wb_mux_reg_set_csc_r2y(id, is_yuv);
+ wb_mux_reg_set_uv_offset(id, 0, 0);
+ }
+
+ return 0;
+}
+
+/******************** EXPORTED DPP CAL APIs ********************/
+void dpp_constraints_params(struct dpp_size_constraints *vc,
+ struct dpp_img_format *vi)
+{
+ u32 sz_align = 1;
+
+ if (vi->yuv)
+ sz_align = 2;
+
+ vc->src_mul_w = SRC_SIZE_MULTIPLE * sz_align;
+ vc->src_mul_h = SRC_SIZE_MULTIPLE * sz_align;
+ vc->src_w_min = SRC_WIDTH_MIN * sz_align;
+ vc->src_w_max = SRC_WIDTH_MAX;
+ vc->src_h_min = SRC_HEIGHT_MIN;
+ vc->src_h_max = SRC_HEIGHT_MAX;
+ vc->img_mul_w = IMG_SIZE_MULTIPLE * sz_align;
+ vc->img_mul_h = IMG_SIZE_MULTIPLE * sz_align;
+ vc->img_w_min = IMG_WIDTH_MIN * sz_align;
+ vc->img_w_max = IMG_WIDTH_MAX;
+ vc->img_h_min = IMG_HEIGHT_MIN * sz_align;
+ if (vi->rot > DPP_ROT_180)
+ vc->img_h_max = IMG_ROT_HEIGHT_MAX;
+ else
+ vc->img_h_max = IMG_HEIGHT_MAX;
+ vc->src_mul_x = SRC_OFFSET_MULTIPLE * sz_align;
+ vc->src_mul_y = SRC_OFFSET_MULTIPLE * sz_align;
+
+ vc->sca_w_min = SCALED_WIDTH_MIN;
+ vc->sca_w_max = SCALED_WIDTH_MAX;
+ vc->sca_h_min = SCALED_HEIGHT_MIN;
+ vc->sca_h_max = SCALED_HEIGHT_MAX;
+ vc->sca_mul_w = SCALED_SIZE_MULTIPLE;
+ vc->sca_mul_h = SCALED_SIZE_MULTIPLE;
+
+ vc->blk_w_min = BLK_WIDTH_MIN;
+ vc->blk_w_max = BLK_WIDTH_MAX;
+ vc->blk_h_min = BLK_HEIGHT_MIN;
+ vc->blk_h_max = BLK_HEIGHT_MAX;
+ vc->blk_mul_w = BLK_SIZE_MULTIPLE;
+ vc->blk_mul_h = BLK_SIZE_MULTIPLE;
+
+ if (vi->wb) {
+ vc->src_mul_w = DST_SIZE_MULTIPLE * sz_align;
+ vc->src_mul_h = DST_SIZE_MULTIPLE * sz_align;
+ vc->src_w_min = DST_SIZE_WIDTH_MIN;
+ vc->src_w_max = DST_SIZE_WIDTH_MAX;
+ vc->src_h_min = DST_SIZE_HEIGHT_MIN;
+ vc->src_h_max = DST_SIZE_HEIGHT_MAX;
+ vc->img_mul_w = DST_IMG_MULTIPLE * sz_align;
+ vc->img_mul_h = DST_IMG_MULTIPLE * sz_align;
+ vc->img_w_min = DST_IMG_WIDTH_MIN;
+ vc->img_w_max = DST_IMG_WIDTH_MAX;
+ vc->img_h_min = DST_IMG_HEIGHT_MIN;
+ vc->img_h_max = DST_IMG_HEIGHT_MAX;
+ vc->src_mul_x = DST_OFFSET_MULTIPLE * sz_align;
+ vc->src_mul_y = DST_OFFSET_MULTIPLE * sz_align;
+ }
+}
+
+void dpp_reg_init(u32 id, const unsigned long attr)
+{
+ if (test_bit(DPP_ATTR_IDMA, &attr)) {
+ idma_reg_set_irq_mask_all(id, 0);
+ idma_reg_set_irq_enable(id);
+ idma_reg_set_clock_gate_en_all(id, 0);
+ idma_reg_set_in_qos_lut(id, 0, 0x44444444);
+ idma_reg_set_in_qos_lut(id, 1, 0x44444444);
+ idma_reg_set_dynamic_gating_en_all(id, 0);
+ idma_reg_set_out_frame_alpha(id, 0xFF);
+ }
+
+ if (test_bit(DPP_ATTR_DPP, &attr)) {
+ dpp_reg_set_irq_mask_all(id, 0);
+ dpp_reg_set_irq_enable(id);
+ dpp_reg_set_clock_gate_en_all(id, 0);
+ dpp_reg_set_dynamic_gating_en_all(id, 0);
+ dpp_reg_set_linecnt(id, 1);
+ }
+
+ if (test_bit(DPP_ATTR_ODMA, &attr)) {
+ odma_reg_set_irq_mask_all(id, 0);
+ odma_reg_set_irq_enable(id);
+ //odma_reg_set_clock_gate_en_all(id, 0);
+ odma_reg_set_in_qos_lut(id, 0, 0x44444444);
+ odma_reg_set_in_qos_lut(id, 1, 0x44444444);
+ odma_reg_set_dynamic_gating_en_all(id, 0);
+ odma_reg_set_out_frame_alpha(id, 0xFF);
+ wb_mux_reg_set_clock_gate_en_all(id, 1);
+ wb_mux_reg_set_dynamic_gating_en_all(id, 0); /* TODO: enable or disable ? */
+ wb_mux_reg_set_out_frame_alpha(id, 0xFF);
+ }
+}
+
+int dpp_reg_deinit(u32 id, bool reset, const unsigned long attr)
+{
+ if (test_bit(DPP_ATTR_IDMA, &attr)) {
+ idma_reg_clear_irq(id, IDMA_ALL_IRQ_CLEAR);
+ idma_reg_set_irq_mask_all(id, 1);
+ }
+
+ if (test_bit(DPP_ATTR_DPP, &attr)) {
+ dpp_reg_clear_irq(id, DPP_ALL_IRQ_CLEAR);
+ dpp_reg_set_irq_mask_all(id, 1);
+ }
+
+ if (test_bit(DPP_ATTR_ODMA, &attr)) {
+ odma_reg_clear_irq(id, ODMA_ALL_IRQ_CLEAR);
+ odma_reg_set_irq_mask_all(id, 1);
+ }
+
+ if (reset) {
+ if (test_bit(DPP_ATTR_IDMA, &attr) &&
+ !test_bit(DPP_ATTR_DPP, &attr)) { /* IDMA only */
+ idma_reg_set_sw_reset(id);
+ if (idma_reg_wait_sw_reset_status(id))
+ return -1;
+ } else if (test_bit(DPP_ATTR_IDMA, &attr) &&
+ test_bit(DPP_ATTR_DPP, &attr)) { /* IDMA/DPP */
+ idma_reg_set_sw_reset(id);
+ dpp_reg_set_sw_reset(id);
+ if (idma_reg_wait_sw_reset_status(id) ||
+ dpp_reg_wait_sw_reset_status(id))
+ return -1;
+ } else if (test_bit(DPP_ATTR_ODMA, &attr)) { /* writeback */
+ odma_reg_set_sw_reset(id);
+ wb_mux_reg_set_sw_reset(id);
+ if (odma_reg_wait_sw_reset_status(id) ||
+ wb_mux_reg_wait_sw_reset_status(id))
+ return -1;
+ } else {
+ dpp_err("%s: not support attribute case(0x%lx)\n",
+ __func__, attr);
+ }
+ }
+
+ return 0;
+}
+
+#if defined(DMA_BIST)
+u32 pattern_data[] = {
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0x000000ff,
+ 0x000000ff,
+ 0x000000ff,
+ 0x000000ff,
+};
+#endif
+
+void dpp_reg_configure_params(u32 id, struct dpp_params_info *p,
+ const unsigned long attr)
+{
+ if (test_bit(DPP_ATTR_CSC, &attr) && test_bit(DPP_ATTR_DPP, &attr))
+ dpp_reg_set_csc_params(id, p->eq_mode);
+ else if (test_bit(DPP_ATTR_CSC, &attr) && test_bit(DPP_ATTR_ODMA, &attr))
+ wb_mux_reg_set_csc_params(id, p->eq_mode);
+
+ if (test_bit(DPP_ATTR_SCALE, &attr))
+ dpp_reg_set_scale_ratio(id, p);
+
+ /* configure coordinates and size of IDMA, DPP, ODMA and WB MUX */
+ dma_dpp_reg_set_coordinates(id, p, attr);
+
+ if (test_bit(DPP_ATTR_ROT, &attr) || test_bit(DPP_ATTR_FLIP, &attr))
+ idma_reg_set_rotation(id, p->rot);
+
+ /* configure base address of IDMA and ODMA */
+ dma_reg_set_base_addr(id, p, attr);
+
+ if (test_bit(DPP_ATTR_BLOCK, &attr))
+ idma_reg_set_block_mode(id, p->is_block, p->block.x, p->block.y,
+ p->block.w, p->block.h);
+
+ /* configure image format of IDMA, DPP, ODMA and WB MUX */
+ dma_dpp_reg_set_format(id, p, attr);
+
+ if (test_bit(DPP_ATTR_HDR, &attr))
+ dpp_reg_set_hdr_params(id, p);
+
+ if (test_bit(DPP_ATTR_AFBC, &attr))
+ idma_reg_set_afbc(id, p->is_comp, p->rcv_num);
+
+#if defined(DMA_BIST)
+ idma_reg_set_test_pattern(id, 0, pattern_data);
+#endif
+}
+
+u32 dpp_reg_get_irq_and_clear(u32 id)
+{
+ u32 val, cfg_err;
+
+ val = dpp_read(id, DPP_IRQ);
+ dpp_reg_clear_irq(id, val);
+
+ if (val & DPP_CONFIG_ERROR) {
+ cfg_err = dpp_read(id, DPP_CFG_ERR_STATE);
+ dpp_err("dpp%d config error occur(0x%x)\n", id, cfg_err);
+ }
+
+ return val;
+}
+
+u32 idma_reg_get_irq_and_clear(u32 id)
+{
+ u32 val, cfg_err;
+
+ val = dma_read(id, IDMA_IRQ);
+ idma_reg_clear_irq(id, val);
+
+ if (val & IDMA_CONFIG_ERROR) {
+ cfg_err = dma_read(id, IDMA_CFG_ERR_STATE);
+ dpp_err("dpp%d idma config error occur(0x%x)\n", id, cfg_err);
+ }
+
+ return val;
+}
+
+u32 odma_reg_get_irq_and_clear(u32 id)
+{
+ u32 val, cfg_err;
+
+ val = dma_read(id, ODMA_IRQ);
+ odma_reg_clear_irq(id, val);
+
+ if (val & ODMA_CONFIG_ERROR) {
+ cfg_err = dma_read(id, ODMA_CFG_ERR_STATE);
+ dpp_err("dpp%d odma config error occur(0x%x)\n", id, cfg_err);
+ }
+
+ return val;
+}
+
+#if 0
+static void dpp_reg_dump_ch_data(int id, enum dpp_reg_area reg_area,
+ u32 sel[], u32 cnt)
+{
+ unsigned char linebuf[128] = {0, };
+ int i, ret;
+ int len = 0;
+ u32 data;
+
+ for (i = 0; i < cnt; i++) {
+ if (!(i % 4) && i != 0) {
+ linebuf[len] = '\0';
+ len = 0;
+ dpp_info("%s\n", linebuf);
+ }
+
+ if (reg_area == REG_AREA_DPP) {
+ dpp_write(id, 0xC04, sel[i]);
+ data = dpp_read(id, 0xC10);
+ } else if (reg_area == REG_AREA_DMA) {
+ dma_write(id, IDMA_DEBUG_CONTROL,
+ IDMA_DEBUG_CONTROL_SEL(sel[i]) |
+ IDMA_DEBUG_CONTROL_EN);
+ data = dma_read(id, IDMA_DEBUG_DATA);
+ } else { /* REG_AREA_DMA_COM */
+ dma_com_write(0, DPU_DMA_DEBUG_CONTROL,
+ DPU_DMA_DEBUG_CONTROL_SEL(sel[i]) |
+ DPU_DMA_DEBUG_CONTROL_EN);
+ data = dma_com_read(0, DPU_DMA_DEBUG_DATA);
+ }
+
+ ret = snprintf(linebuf + len, sizeof(linebuf) - len,
+ "[0x%08x: %08x] ", sel[i], data);
+ if (ret >= sizeof(linebuf) - len) {
+ dpp_err("overflow: %d %ld %d\n",
+ ret, sizeof(linebuf), len);
+ return;
+ }
+ len += ret;
+ }
+ dpp_info("%s\n", linebuf);
+}
+static bool checked;
+#endif
+
+void dma_reg_dump_com_debug_regs(int id)
+{
+#if 0 /* TODO: This will be implemented */
+ u32 sel[12] = {0x0000, 0x0100, 0x0200, 0x0204, 0x0205, 0x0300, 0x4000,
+ 0x4001, 0x4005, 0x8000, 0x8001, 0x8005};
+
+ dpp_info("%s: checked = %d\n", __func__, checked);
+ if (checked)
+ return;
+
+ dpp_info("-< DMA COMMON DEBUG SFR >-\n");
+ dpp_reg_dump_ch_data(id, REG_AREA_DMA_COM, sel, 12);
+
+ checked = true;
+#endif
+}
+
+void dma_reg_dump_debug_regs(int id)
+{
+#if 0 /* TODO: This will be implemented */
+ u32 sel_g[11] = {
+ 0x0000, 0x0001, 0x0002, 0x0004, 0x000A, 0x000B, 0x0400, 0x0401,
+ 0x0402, 0x0405, 0x0406
+ };
+ u32 sel_v[39] = {
+ 0x1000, 0x1001, 0x1002, 0x1004, 0x100A, 0x100B, 0x1400, 0x1401,
+ 0x1402, 0x1405, 0x1406, 0x2000, 0x2001, 0x2002, 0x2004, 0x200A,
+ 0x200B, 0x2400, 0x2401, 0x2402, 0x2405, 0x2406, 0x3000, 0x3001,
+ 0x3002, 0x3004, 0x300A, 0x300B, 0x3400, 0x3401, 0x3402, 0x3405,
+ 0x3406, 0x4002, 0x4003, 0x4004, 0x4005, 0x4006, 0x4007
+ };
+ u32 sel_f[12] = {
+ 0x5100, 0x5101, 0x5104, 0x5105, 0x5200, 0x5202, 0x5204, 0x5205,
+ 0x5300, 0x5302, 0x5303, 0x5306
+ };
+ u32 sel_r[22] = {
+ 0x6100, 0x6101, 0x6102, 0x6103, 0x6104, 0x6105, 0x6200, 0x6201,
+ 0x6202, 0x6203, 0x6204, 0x6205, 0x6300, 0x6301, 0x6302, 0x6306,
+ 0x6307, 0x6400, 0x6401, 0x6402, 0x6406, 0x6407
+ };
+ u32 sel_com[4] = {
+ 0x7000, 0x7001, 0x7002, 0x7003
+ };
+
+ dpp_info("-< DPU_DMA%d DEBUG SFR >-\n", id);
+ switch (DPU_CH2DMA(id)) {
+ case IDMA_G0:
+ case IDMA_G1:
+ dpp_reg_dump_ch_data(id, REG_AREA_DMA, sel_g, 11);
+ dpp_reg_dump_ch_data(id, REG_AREA_DMA, sel_com, 4);
+ break;
+ case IDMA_VG0:
+ case IDMA_VG1:
+ dpp_reg_dump_ch_data(id, REG_AREA_DMA, sel_g, 11);
+ dpp_reg_dump_ch_data(id, REG_AREA_DMA, sel_v, 39);
+ dpp_reg_dump_ch_data(id, REG_AREA_DMA, sel_com, 4);
+ break;
+ case IDMA_VGF0:
+ dpp_reg_dump_ch_data(id, REG_AREA_DMA, sel_g, 11);
+ dpp_reg_dump_ch_data(id, REG_AREA_DMA, sel_v, 39);
+ dpp_reg_dump_ch_data(id, REG_AREA_DMA, sel_f, 12);
+ dpp_reg_dump_ch_data(id, REG_AREA_DMA, sel_com, 4);
+ break;
+ case IDMA_VGF1:
+ dpp_reg_dump_ch_data(id, REG_AREA_DMA, sel_g, 11);
+ dpp_reg_dump_ch_data(id, REG_AREA_DMA, sel_v, 39);
+ dpp_reg_dump_ch_data(id, REG_AREA_DMA, sel_f, 12);
+ dpp_reg_dump_ch_data(id, REG_AREA_DMA, sel_r, 22);
+ dpp_reg_dump_ch_data(id, REG_AREA_DMA, sel_com, 4);
+ break;
+ default:
+ dpp_err("DPP%d is wrong ID\n", id);
+ return;
+ }
+#endif
+}
+
+void dpp_reg_dump_debug_regs(int id)
+{
+#if 0 /* TODO: This will be implemented */
+ u32 sel_g[3] = {0x0000, 0x0100, 0x0101};
+ u32 sel_vg[19] = {0x0000, 0x0100, 0x0101, 0x0200, 0x0201, 0x0202,
+ 0x0203, 0x0204, 0x0205, 0x0206, 0x0207, 0x0208, 0x0300, 0x0301,
+ 0x0302, 0x0303, 0x0304, 0x0400, 0x0401};
+ u32 sel_vgf[37] = {0x0000, 0x0100, 0x0101, 0x0200, 0x0201, 0x0210,
+ 0x0211, 0x0220, 0x0221, 0x0230, 0x0231, 0x0240, 0x0241, 0x0250,
+ 0x0251, 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306,
+ 0x0307, 0x0308, 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0500,
+ 0x0501, 0x0502, 0x0503, 0x0504, 0x0505, 0x0600, 0x0601};
+ u32 cnt;
+ u32 *sel = NULL;
+ switch (DPU_CH2DMA(id)) {
+ case IDMA_G0:
+ case IDMA_G1:
+ sel = sel_g;
+ cnt = 3;
+ break;
+ case IDMA_VG0:
+ case IDMA_VG1:
+ sel = sel_vg;
+ cnt = 19;
+ break;
+ case IDMA_VGF0:
+ case IDMA_VGF1:
+ sel = sel_vgf;
+ cnt = 37;
+ break;
+ default:
+ dpp_err("DPP%d is wrong ID\n", id);
+ return;
+ }
+
+ dpp_write(id, 0x0C00, 0x1);
+ dpp_info("-< DPP%d DEBUG SFR >-\n", id);
+ dpp_reg_dump_ch_data(id, REG_AREA_DPP, sel, cnt);
+#endif
+}
--- /dev/null
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Header file for Exynos9820 DSIM 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_DSIM_CAL_H__
+#define __SAMSUNG_DSIM_CAL_H__
+
+#include "../panels/decon_lcd.h"
+
+#define MAX_DSIM_CNT 2
+
+struct dsim_clks {
+ u32 hs_clk;
+ u32 esc_clk;
+ u32 byte_clk;
+ u32 word_clk;
+};
+
+/*************** DSIM CAL APIs exposed to DSIM driver ***************/
+/* DPHY system register control */
+void dpu_sysreg_select_dphy_rst_control(void __iomem *sysreg, u32 dsim_id, u32 sel);
+
+/* DSIM control */
+void dsim_reg_init(u32 id, struct decon_lcd *lcd_info, struct dsim_clks *clks,
+ bool panel_ctrl);
+void dsim_reg_start(u32 id);
+int dsim_reg_stop(u32 id, u32 lanes);
+
+/* ULPS control */
+int dsim_reg_exit_ulps_and_start(u32 id, u32 ddi_type, u32 lanes);
+int dsim_reg_stop_and_enter_ulps(u32 id, u32 ddi_type, u32 lanes);
+
+/* DSIM interrupt control */
+int dsim_reg_get_int_and_clear(u32 id);
+void dsim_reg_clear_int(u32 id, u32 int_src);
+
+/* DSIM read/write command control */
+void dsim_reg_wr_tx_header(u32 id, u32 d_id, unsigned long d0, u32 d1, u32 bta);
+void dsim_reg_wr_tx_payload(u32 id, u32 payload);
+u32 dsim_reg_header_fifo_is_empty(u32 id);
+u32 dsim_reg_is_writable_fifo_state(u32 id);
+u32 dsim_reg_get_rx_fifo(u32 id);
+u32 dsim_reg_rx_fifo_is_empty(u32 id);
+int dsim_reg_rx_err_handler(u32 id, u32 rx_fifo);
+
+/* For reading DSIM shadow SFR */
+void dsim_reg_enable_shadow_read(u32 id, u32 en);
+
+/* For window update and multi resolution feature */
+void dsim_reg_function_reset(u32 id);
+void dsim_reg_set_partial_update(u32 id, struct decon_lcd *lcd_info);
+void dsim_reg_set_mres(u32 id, struct decon_lcd *lcd_info);
+
+/* DSIM BIST for test */
+void dsim_reg_set_bist(u32 id, u32 en);
+
+void dsim_reg_set_cmd_transfer_mode(u32 id, u32 lp);
+#endif /* __SAMSUNG_DSIM_CAL_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * SFR access functions for Samsung EXYNOS SoC MIPI-DSI Master 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 "../dsim.h"
+#include "regs-decon.h"
+
+/* dsim version */
+#define DSIM_VER_EVT0 0x02020000
+#define DSIM_VER_EVT1 0x02030000
+
+/* These definitions are need to guide from AP team */
+#define DSIM_STOP_STATE_CNT 0xA
+#define DSIM_BTA_TIMEOUT 0xff
+#define DSIM_LP_RX_TIMEOUT 0xffff
+#define DSIM_MULTI_PACKET_CNT 0xffff
+#define DSIM_PLL_STABLE_TIME 0x682A
+#define DSIM_FIFOCTRL_THRESHOLD 0x1 /* 1 ~ 32 */
+
+/* If below values depend on panel. These values wil be move to panel file.
+ * And these values are valid in case of video mode only.
+ */
+#define DSIM_CMD_ALLOW_VALUE 4
+#define DSIM_STABLE_VFP_VALUE 2
+#define TE_PROTECT_ON_TIME 158 /* 15.8ms*/
+#define TE_TIMEOUT_TIME 180 /* 18ms */
+
+u32 DSIM_PHY_BIAS_CON_VAL[] = {
+ 0x00000010,
+ 0x00000110,
+ 0x00003223,
+ 0x00000000,
+ 0x00000000,
+};
+
+u32 DSIM_PHY_PLL_CON_VAL[] = {
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000500,
+ 0x00000000,
+ 0x00001450,
+};
+
+u32 DSIM_PHY_MC_GNR_CON_VAL[] = {
+ 0x00000000,
+ 0x00001450,
+};
+u32 DSIM_PHY_MC_ANA_CON_VAL[] = {
+ /* EDGE_CON[14:12] DPHY=3'b111, CPHY=3'b001 */
+ 0x00007133,
+ 0x00000000,
+};
+
+/* same value in all master data lane */
+u32 DSIM_PHY_MD_GNR_CON_VAL[] = {
+ 0x00000000,
+ 0x00001450,
+};
+u32 DSIM_PHY_MD_ANA_CON_VAL[] = {
+ 0x00007133,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+};
+
+
+/* DPHY timing table */
+/* below table have to be changed to meet MK DPHY spec*/
+const u32 dphy_timing[][10] = {
+ /* bps, clk_prepare, clk_zero, clk_post, clk_trail,
+ * hs_prepare, hs_zero, hs_trail, lpx, hs_exit
+ */
+ {2500, 12, 43, 11, 11, 11, 20, 10, 9 , 17,},
+ {2490, 12, 43, 11, 11, 11, 20, 10, 9 , 17,},
+ {2480, 12, 42, 11, 11, 11, 20, 10, 9 , 17,},
+ {2470, 11, 43, 11, 11, 11, 20, 10, 9 , 16,},
+ {2460, 11, 43, 11, 11, 11, 20, 10, 9 , 16,},
+ {2450, 11, 43, 11, 11, 11, 20, 10, 9 , 16,},
+ {2440, 11, 42, 11, 11, 11, 19, 10, 9 , 16,},
+ {2430, 11, 42, 11, 11, 11, 19, 10, 9 , 16,},
+ {2420, 11, 42, 11, 10, 11, 19, 10, 9 , 16,},
+ {2410, 11, 42, 11, 10, 11, 19, 10, 9 , 16,},
+ {2400, 11, 41, 10, 10, 11, 19, 10, 8 , 16,},
+ {2390, 11, 41, 10, 10, 11, 19, 10, 8 , 16,},
+ {2380, 11, 41, 10, 10, 11, 19, 9 , 8 , 16,},
+ {2370, 11, 41, 10, 10, 11, 18, 9 , 8 , 16,},
+ {2360, 11, 41, 10, 10, 11, 18, 9 , 8 , 16,},
+ {2350, 11, 40, 10, 10, 11, 18, 9 , 8 , 16,},
+ {2340, 11, 40, 10, 10, 11, 18, 9 , 8 , 16,},
+ {2330, 11, 40, 10, 10, 10, 19, 9 , 8 , 16,},
+ {2320, 11, 40, 10, 10, 10, 19, 9 , 8 , 15,},
+ {2310, 11, 39, 10, 10, 10, 19, 9 , 8 , 15,},
+ {2300, 11, 39, 10, 10, 10, 18, 9 , 8 , 15,},
+ {2290, 11, 39, 10, 10, 10, 18, 9 , 8 , 15,},
+ {2280, 11, 39, 10, 10, 10, 18, 9 , 8 , 15,},
+ {2270, 10, 39, 10, 10, 10, 18, 9 , 8 , 15,},
+ {2260, 10, 39, 10, 10, 10, 18, 9 , 8 , 15,},
+ {2250, 10, 39, 10, 10, 10, 18, 9 , 8 , 15,},
+ {2240, 10, 39, 10, 10, 10, 18, 9 , 8 , 15,},
+ {2230, 10, 38, 10, 10, 10, 18, 9 , 8 , 15,},
+ {2220, 10, 38, 10, 10, 10, 17, 9 , 8 , 15,},
+ {2210, 10, 38, 10, 10, 10, 17, 9 , 8 , 15,},
+ {2200, 10, 38, 9 , 10, 10, 17, 9 , 8 , 15,},
+ {2190, 10, 38, 9 , 9 , 10, 17, 9 , 8 , 15,},
+ {2180, 10, 37, 9 , 9 , 10, 17, 9 , 8 , 14,},
+ {2170, 10, 37, 9 , 9 , 10, 17, 9 , 8 , 14,},
+ {2160, 10, 37, 9 , 9 , 10, 17, 9 , 8 , 14,},
+ {2150, 10, 37, 9 , 9 , 10, 16, 8 , 8 , 14,},
+ {2140, 10, 36, 9 , 9 , 10, 16, 8 , 8 , 14,},
+ {2130, 10, 36, 9 , 9 , 10, 16, 8 , 7 , 14,},
+ {2120, 10, 36, 9 , 9 , 9 , 17, 8 , 7 , 14,},
+ {2110, 10, 36, 9 , 9 , 9 , 17, 8 , 7 , 14,},
+ {2100, 10, 35, 9 , 9 , 9 , 17, 8 , 7 , 14,},
+ {2090, 10, 35, 9 , 9 , 9 , 17, 8 , 7 , 14,},
+ {2080, 9 , 36, 9 , 9 , 9 , 16, 8 , 7 , 14,},
+ {2070, 9 , 36, 9 , 9 , 9 , 16, 8 , 7 , 14,},
+ {2060, 9 , 35, 9 , 9 , 9 , 16, 8 , 7 , 14,},
+ {2050, 9 , 35, 9 , 9 , 9 , 16, 8 , 7 , 14,},
+ {2040, 9 , 35, 9 , 9 , 9 , 16, 8 , 7 , 14,},
+ {2030, 9 , 35, 9 , 9 , 9 , 16, 8 , 7 , 13,},
+ {2020, 9 , 35, 9 , 9 , 9 , 16, 8 , 7 , 13,},
+ {2010, 9 , 34, 9 , 9 , 9 , 15, 8 , 7 , 13,},
+ {2000, 9 , 34, 8 , 9 , 9 , 15, 8 , 7 , 13,},
+ {1990, 9 , 34, 8 , 9 , 9 , 15, 8 , 7 , 13,},
+ {1980, 9 , 34, 8 , 9 , 9 , 15, 8 , 7 , 13,},
+ {1970, 9 , 33, 8 , 9 , 9 , 15, 8 , 7 , 13,},
+ {1960, 9 , 33, 8 , 9 , 9 , 15, 8 , 7 , 13,},
+ {1950, 9 , 33, 8 , 8 , 9 , 15, 8 , 7 , 13,},
+ {1940, 9 , 33, 8 , 8 , 9 , 15, 8 , 7 , 13,},
+ {1930, 9 , 32, 8 , 8 , 9 , 14, 8 , 7 , 13,},
+ {1920, 9 , 32, 8 , 8 , 9 , 14, 8 , 7 , 13,},
+ {1910, 9 , 32, 8 , 8 , 8 , 15, 7 , 7 , 13,},
+ {1900, 9 , 32, 8 , 8 , 8 , 15, 7 , 7 , 13,},
+ {1890, 9 , 31, 8 , 8 , 8 , 15, 7 , 7 , 12,},
+ {1880, 8 , 32, 8 , 8 , 8 , 15, 7 , 7 , 12,},
+ {1870, 8 , 32, 8 , 8 , 8 , 15, 7 , 7 , 12,},
+ {1860, 8 , 32, 8 , 8 , 8 , 14, 7 , 6 , 12,},
+ {1850, 8 , 32, 8 , 8 , 8 , 14, 7 , 6 , 12,},
+ {1840, 8 , 31, 8 , 8 , 8 , 14, 7 , 6 , 12,},
+ {1830, 8 , 31, 8 , 8 , 8 , 14, 7 , 6 , 12,},
+ {1820, 8 , 31, 8 , 8 , 8 , 14, 7 , 6 , 12,},
+ {1810, 8 , 31, 8 , 8 , 8 , 14, 7 , 6 , 12,},
+ {1800, 8 , 30, 7 , 8 , 8 , 14, 7 , 6 , 12,},
+ {1790, 8 , 30, 7 , 8 , 8 , 13, 7 , 6 , 12,},
+ {1780, 8 , 30, 7 , 8 , 8 , 13, 7 , 6 , 12,},
+ {1770, 8 , 30, 7 , 8 , 8 , 13, 7 , 6 , 12,},
+ {1760, 8 , 29, 7 , 8 , 8 , 13, 7 , 6 , 12,},
+ {1750, 8 , 29, 7 , 8 , 8 , 13, 7 , 6 , 12,},
+ {1740, 8 , 29, 7 , 8 , 8 , 13, 7 , 6 , 11,},
+ {1730, 8 , 29, 7 , 8 , 8 , 13, 7 , 6 , 11,},
+ {1720, 8 , 29, 7 , 7 , 8 , 13, 7 , 6 , 11,},
+ {1710, 8 , 28, 7 , 7 , 8 , 12, 7 , 6 , 11,},
+ {1700, 8 , 28, 7 , 7 , 7 , 13, 7 , 6 , 11,},
+ {1690, 8 , 28, 7 , 7 , 7 , 13, 7 , 6 , 11,},
+ {1680, 7 , 29, 7 , 7 , 7 , 13, 6 , 6 , 11,},
+ {1670, 7 , 28, 7 , 7 , 7 , 13, 6 , 6 , 11,},
+ {1660, 7 , 28, 7 , 7 , 7 , 13, 6 , 6 , 11,},
+ {1650, 7 , 28, 7 , 7 , 7 , 13, 6 , 6 , 11,},
+ {1640, 7 , 28, 7 , 7 , 7 , 12, 6 , 6 , 11,},
+ {1630, 7 , 27, 7 , 7 , 7 , 12, 6 , 6 , 11,},
+ {1620, 7 , 27, 7 , 7 , 7 , 12, 6 , 6 , 11,},
+ {1610, 7 , 27, 7 , 7 , 7 , 12, 6 , 6 , 11,},
+ {1600, 7 , 27, 6 , 7 , 7 , 12, 6 , 5 , 10,},
+ {1590, 7 , 26, 6 , 7 , 7 , 12, 6 , 5 , 10,},
+ {1580, 7 , 26, 6 , 7 , 7 , 12, 6 , 5 , 10,},
+ {1570, 7 , 26, 6 , 7 , 7 , 11, 6 , 5 , 10,},
+ {1560, 7 , 26, 6 , 7 , 7 , 11, 6 , 5 , 10,},
+ {1550, 7 , 26, 6 , 7 , 7 , 11, 6 , 5 , 10,},
+ {1540, 7 , 25, 6 , 7 , 7 , 11, 6 , 5 , 10,},
+ {1530, 7 , 25, 6 , 7 , 7 , 11, 6 , 5 , 10,},
+ {1520, 7 , 25, 6 , 7 , 7 , 11, 6 , 5 , 10,},
+ {1510, 7 , 25, 6 , 7 , 7 , 11, 6 , 5 , 10,},
+ {1500, 7 , 24, 6 , 7 , 7 , 10, 6 , 5 , 10,},
+ {1490, 59, 25, 6 , 77, 59, 10, 70, 44, 9 ,},
+ {1480, 59, 24, 6 , 76, 58, 10, 70, 44, 9 ,},
+ {1470, 58, 24, 6 , 76, 58, 10, 69, 44, 9 ,},
+ {1460, 58, 24, 6 , 76, 58, 10, 69, 43, 9 ,},
+ {1450, 58, 24, 6 , 75, 57, 10, 68, 43, 9 ,},
+ {1440, 57, 24, 6 , 75, 57, 10, 68, 43, 9 ,},
+ {1430, 57, 23, 6 , 75, 56, 10, 68, 42, 8 ,},
+ {1420, 56, 23, 6 , 74, 56, 9 , 67, 42, 8 ,},
+ {1410, 56, 23, 6 , 74, 56, 9 , 67, 42, 8 ,},
+ {1400, 56, 23, 5 , 74, 55, 9 , 67, 41, 8 ,},
+ {1390, 55, 23, 5 , 73, 55, 9 , 66, 41, 8 ,},
+ {1380, 55, 23, 5 , 73, 54, 9 , 66, 41, 8 ,},
+ {1370, 54, 22, 5 , 72, 54, 9 , 66, 41, 8 ,},
+ {1360, 54, 22, 5 , 72, 54, 9 , 65, 40, 8 ,},
+ {1350, 54, 22, 5 , 72, 53, 9 , 65, 40, 8 ,},
+ {1340, 53, 22, 5 , 71, 53, 9 , 65, 40, 8 ,},
+ {1330, 53, 22, 5 , 71, 53, 9 , 64, 39, 8 ,},
+ {1320, 52, 22, 5 , 71, 52, 8 , 64, 39, 8 ,},
+ {1310, 52, 21, 5 , 70, 52, 8 , 64, 39, 8 ,},
+ {1300, 51, 21, 5 , 70, 51, 8 , 63, 38, 8 ,},
+ {1290, 51, 21, 5 , 70, 51, 8 , 63, 38, 7 ,},
+ {1280, 51, 21, 5 , 69, 51, 8 , 63, 38, 7 ,},
+ {1270, 50, 21, 5 , 69, 50, 8 , 62, 38, 7 ,},
+ {1260, 50, 20, 5 , 69, 50, 8 , 62, 37, 7 ,},
+ {1250, 49, 20, 5 , 68, 49, 8 , 62, 37, 7 ,},
+ {1240, 49, 20, 5 , 68, 49, 8 , 61, 37, 7 ,},
+ {1230, 49, 20, 5 , 68, 49, 8 , 61, 36, 7 ,},
+ {1220, 48, 20, 5 , 67, 48, 8 , 61, 36, 7 ,},
+ {1210, 48, 19, 5 , 67, 48, 7 , 60, 36, 7 ,},
+ {1200, 47, 19, 4 , 67, 48, 7 , 60, 35, 7 ,},
+ {1190, 47, 19, 4 , 66, 47, 7 , 60, 35, 7 ,},
+ {1180, 47, 19, 4 , 66, 47, 7 , 59, 35, 7 ,},
+ {1170, 46, 19, 4 , 66, 46, 7 , 59, 35, 7 ,},
+ {1160, 46, 18, 4 , 65, 46, 7 , 59, 34, 7 ,},
+ {1150, 45, 18, 4 , 65, 46, 7 , 58, 34, 7 ,},
+ {1140, 45, 18, 4 , 65, 45, 7 , 58, 34, 6 ,},
+ {1130, 45, 18, 4 , 64, 45, 7 , 58, 33, 6 ,},
+ {1120, 44, 18, 4 , 64, 44, 7 , 57, 33, 6 ,},
+ {1110, 44, 18, 4 , 64, 44, 7 , 57, 33, 6 ,},
+ {1100, 43, 17, 4 , 63, 44, 6 , 57, 32, 6 ,},
+ {1090, 43, 17, 4 , 63, 43, 6 , 56, 32, 6 ,},
+ {1080, 43, 17, 4 , 63, 43, 6 , 56, 32, 6 ,},
+ {1070, 42, 17, 4 , 62, 43, 6 , 56, 32, 6 ,},
+ {1060, 42, 17, 4 , 62, 42, 6 , 55, 31, 6 ,},
+ {1050, 41, 17, 4 , 62, 42, 6 , 55, 31, 6 ,},
+ {1040, 41, 16, 4 , 61, 41, 6 , 54, 31, 6 ,},
+ {1030, 41, 16, 4 , 61, 41, 6 , 54, 30, 6 ,},
+ {1020, 40, 16, 4 , 61, 41, 6 , 54, 30, 6 ,},
+ {1010, 40, 16, 4 , 60, 40, 6 , 53, 30, 6 ,},
+ {1000, 39, 16, 3 , 60, 40, 6 , 53, 29, 5 ,},
+ {990 , 39, 15, 3 , 60, 39, 6 , 53, 29, 5 ,},
+ {980 , 39, 15, 3 , 59, 39, 5 , 52, 29, 5 ,},
+ {970 , 38, 15, 3 , 59, 39, 5 , 52, 29, 5 ,},
+ {960 , 38, 15, 3 , 59, 38, 5 , 52, 28, 5 ,},
+ {950 , 37, 15, 3 , 58, 38, 5 , 51, 28, 5 ,},
+ {940 , 37, 14, 3 , 58, 38, 5 , 51, 28, 5 ,},
+ {930 , 37, 14, 3 , 57, 37, 5 , 51, 27, 5 ,},
+ {920 , 36, 14, 3 , 57, 37, 5 , 50, 27, 5 ,},
+ {910 , 36, 14, 3 , 57, 36, 5 , 50, 27, 5 ,},
+ {900 , 35, 14, 3 , 56, 36, 5 , 50, 26, 5 ,},
+ {890 , 35, 14, 3 , 56, 36, 5 , 49, 26, 5 ,},
+ {880 , 35, 13, 3 , 56, 35, 5 , 49, 26, 5 ,},
+ {870 , 34, 13, 3 , 55, 35, 4 , 49, 26, 5 ,},
+ {860 , 34, 13, 3 , 55, 35, 4 , 48, 25, 5 ,},
+ {850 , 33, 13, 3 , 55, 34, 4 , 48, 25, 4 ,},
+ {840 , 33, 13, 3 , 54, 34, 4 , 48, 25, 4 ,},
+ {830 , 33, 12, 3 , 54, 33, 4 , 47, 24, 4 ,},
+ {820 , 32, 12, 3 , 54, 33, 4 , 47, 24, 4 ,},
+ {810 , 32, 12, 3 , 53, 33, 4 , 47, 24, 4 ,},
+ {800 , 31, 12, 2 , 53, 32, 4 , 46, 23, 4 ,},
+ {790 , 31, 12, 2 , 53, 32, 4 , 46, 23, 4 ,},
+ {780 , 30, 12, 2 , 52, 31, 4 , 46, 23, 4 ,},
+ {770 , 30, 11, 2 , 52, 31, 4 , 45, 23, 4 ,},
+ {760 , 30, 11, 2 , 52, 31, 3 , 45, 22, 4 ,},
+ {750 , 29, 11, 2 , 51, 30, 3 , 45, 22, 4 ,},
+ {740 , 29, 11, 2 , 51, 30, 3 , 44, 22, 4 ,},
+ {730 , 28, 11, 2 , 51, 30, 3 , 44, 21, 4 ,},
+ {720 , 28, 10, 2 , 50, 29, 3 , 44, 21, 4 ,},
+ {710 , 28, 10, 2 , 50, 29, 3 , 43, 21, 4 ,},
+ {700 , 27, 10, 2 , 50, 28, 3 , 43, 20, 3 ,},
+ {690 , 27, 10, 2 , 49, 28, 3 , 43, 20, 3 ,},
+ {680 , 26, 10, 2 , 49, 28, 3 , 42, 20, 3 ,},
+ {670 , 26, 10, 2 , 49, 27, 3 , 42, 20, 3 ,},
+ {660 , 26, 9 , 2 , 48, 27, 3 , 42, 19, 3 ,},
+ {650 , 25, 9 , 2 , 48, 26, 3 , 41, 19, 3 ,},
+ {640 , 25, 9 , 2 , 48, 26, 2 , 41, 19, 3 ,},
+ {630 , 24, 9 , 2 , 47, 26, 2 , 40, 18, 3 ,},
+ {620 , 24, 9 , 2 , 47, 25, 2 , 40, 18, 3 ,},
+ {610 , 24, 8 , 2 , 47, 25, 2 , 40, 18, 3 ,},
+ {600 , 23, 8 , 1 , 46, 25, 2 , 39, 17, 3 ,},
+ {590 , 23, 8 , 1 , 46, 24, 2 , 39, 17, 3 ,},
+ {580 , 22, 8 , 1 , 46, 24, 2 , 39, 17, 3 ,},
+ {570 , 22, 8 , 1 , 45, 23, 2 , 38, 17, 3 ,},
+ {560 , 22, 7 , 1 , 45, 23, 2 , 38, 16, 2 ,},
+ {550 , 21, 7 , 1 , 45, 23, 2 , 38, 16, 2 ,},
+ {540 , 21, 7 , 1 , 44, 22, 2 , 37, 16, 2 ,},
+ {530 , 20, 7 , 1 , 44, 22, 1 , 37, 15, 2 ,},
+ {520 , 20, 7 , 1 , 43, 21, 1 , 37, 15, 2 ,},
+ {510 , 20, 6 , 1 , 43, 21, 1 , 36, 15, 2 ,},
+ {500 , 19, 6 , 1 , 43, 21, 1 , 36, 14, 2 ,},
+};
+
+const u32 b_dphyctl[14] = {
+ 0x0af, 0x0c8, 0x0e1, 0x0fa, /* esc 7 ~ 10 */
+ 0x113, 0x12c, 0x145, 0x15e, 0x177, /* esc 11 ~ 15 */
+ 0x190, 0x1a9, 0x1c2, 0x1db, 0x1f4 /* esc 16 ~ 20 */
+};
+
+/***************************** DPHY CAL functions *******************************/
+#if defined(CONFIG_EXYNOS_DSIM_DITHER)
+static void dsim_reg_set_dphy_dither_en(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dsim_phy_write_mask(id, DSIM_PHY_PLL_CON4, val, DSIM_PHY_DITHER_EN);
+}
+#endif
+
+#ifdef DPHY_LOOP
+void dsim_reg_set_dphy_loop_back_test(u32 id)
+{
+ dsim_phy_write_mask(id, 0x0370, 1, (0x3 << 0));
+ dsim_phy_write_mask(id, 0x0470, 1, (0x3 << 0));
+ dsim_phy_write_mask(id, 0x0570, 1, (0x3 << 0));
+ dsim_phy_write_mask(id, 0x0670, 1, (0x3 << 0));
+ dsim_phy_write_mask(id, 0x0770, 1, (0x3 << 0));
+}
+
+static void dsim_reg_set_dphy_loop_test(u32 id)
+{
+ dsim_phy_write_mask(id, 0x0374, ~0, (1 << 3));
+ dsim_phy_write_mask(id, 0x0474, ~0, (1 << 3));
+ dsim_phy_write_mask(id, 0x0574, ~0, (1 << 3));
+ dsim_phy_write_mask(id, 0x0674, ~0, (1 << 3));
+ dsim_phy_write_mask(id, 0x0774, ~0, (1 << 3));
+
+ dsim_phy_write_mask(id, 0x0374, 0x6, (0x7 << 0));
+ dsim_phy_write_mask(id, 0x0474, 0x6, (0x7 << 0));
+ dsim_phy_write_mask(id, 0x0574, 0x6, (0x7 << 0));
+ dsim_phy_write_mask(id, 0x0674, 0x6, (0x7 << 0));
+ dsim_phy_write_mask(id, 0x0774, 0x6, (0x7 << 0));
+
+ dsim_phy_write_mask(id, 0x037c, 0x2, (0xffff << 0));
+ dsim_phy_write_mask(id, 0x047c, 0x2, (0xffff << 0));
+ dsim_phy_write_mask(id, 0x057c, 0x2, (0xffff << 0));
+ dsim_phy_write_mask(id, 0x067c, 0x2, (0xffff << 0));
+ dsim_phy_write_mask(id, 0x077c, 0x2, (0xffff << 0));
+}
+#endif
+
+static void dsim_reg_set_dphy_wclk_buf_sft(u32 id, u32 cnt)
+{
+ u32 val = DSIM_PHY_WCLK_BUF_SFT_CNT(cnt);
+
+ dsim_phy_write_mask(id, DSIM_PHY_PLL_CON6, val, DSIM_PHY_WCLK_BUF_SFT_CNT_MASK);
+}
+
+/* DPHY setting */
+static void dsim_reg_set_pll_freq(u32 id, u32 p, u32 m, u32 s, u32 k)
+{
+ u32 val, mask;
+
+ /* K value */
+ val = DSIM_PHY_PMS_K(k);
+ mask = DSIM_PHY_PMS_K_MASK;
+ dsim_phy_write_mask(id, DSIM_PHY_PLL_CON1, val, mask);
+
+ /* P value */
+ val = DSIM_PHY_PMS_P(p);
+ mask = DSIM_PHY_PMS_P_MASK;
+ dsim_phy_write_mask(id, DSIM_PHY_PLL_CON0, val, mask);
+
+ /* M value */
+ val = DSIM_PHY_PMS_M(m);
+ mask = DSIM_PHY_PMS_M_MASK;
+ dsim_phy_write_mask(id, DSIM_PHY_PLL_CON2, val, mask);
+
+ /* S value */
+ val = DSIM_PHY_PMS_S(s);
+ mask = DSIM_PHY_PMS_S_MASK;
+ dsim_phy_write_mask(id, DSIM_PHY_PLL_CON0, val, mask);
+}
+
+static void dsim_reg_set_dphy_timing_values(u32 id,
+ struct dphy_timing_value *t, u32 hsmode)
+{
+ u32 val, mask;
+ u32 hs_en, skewcal_en;
+ u32 i;
+
+ /* HS mode setting */
+ if (hsmode) {
+ /* under 1500Mbps : don't need SKEWCAL enable */
+ hs_en = DSIM_PHY_HSTX_CLK_SEL;
+ skewcal_en = 0;
+ } else {
+ /* above 1500Mbps : need SKEWCAL enable */
+ hs_en = 0;
+ skewcal_en = 1;
+ }
+
+ /* clock lane setting */
+ val = DSIM_PHY_ULPS_EXIT(t->b_dphyctl);
+ mask = DSIM_PHY_ULPS_EXIT_MASK;
+ dsim_phy_write_mask(id, DSIM_PHY_MC_TIME_CON4, val, mask);
+
+ val = hs_en;
+ mask = DSIM_PHY_HSTX_CLK_SEL;
+ dsim_phy_write_mask(id, DSIM_PHY_MC_TIME_CON0, val, mask);
+
+ /* skew cal implementation : disable */
+ val = skewcal_en;
+ mask = DSIM_PHY_SKEWCAL_EN;
+ dsim_phy_write_mask(id, DSIM_PHY_MC_DESKEW_CON0, val, mask);
+ /* add 'run|init_run|wait_run time' if skewcal is enabled */
+
+ val = DSIM_PHY_TLPX(t->lpx);
+ mask = DSIM_PHY_TLPX_MASK;
+ dsim_phy_write_mask(id, DSIM_PHY_MC_TIME_CON0, val, mask);
+
+ val = DSIM_PHY_TCLK_PREPARE(t->clk_prepare);
+ mask = DSIM_PHY_TCLK_PREPARE_MASK;
+ dsim_phy_write_mask(id, DSIM_PHY_MC_TIME_CON1, val, mask);
+
+ val = DSIM_PHY_TCLK_ZERO(t->clk_zero);
+ mask = DSIM_PHY_TCLK_ZERO_MASK;
+ dsim_phy_write_mask(id, DSIM_PHY_MC_TIME_CON1, val, mask);
+
+ val = DSIM_PHY_THS_EXIT(t->hs_exit);
+ mask = DSIM_PHY_THS_EXIT_MASK;
+ dsim_phy_write_mask(id, DSIM_PHY_MC_TIME_CON2, val, mask);
+
+ val = DSIM_PHY_TCLK_TRAIL(t->clk_trail);
+ mask = DSIM_PHY_TCLK_TRAIL_MASK;
+ dsim_phy_write_mask(id, DSIM_PHY_MC_TIME_CON2, val, mask);
+
+ val = DSIM_PHY_TCLK_POST(t->clk_post);
+ mask = DSIM_PHY_TCLK_POST_MASK;
+ dsim_phy_write_mask(id, DSIM_PHY_MC_TIME_CON3, val, mask);
+
+ /* add other clock lane setting if necessary */
+
+ /* data lane setting : D0 ~ D3 */
+ for (i = 0; i < 4; i++) {
+ val = DSIM_PHY_ULPS_EXIT(t->b_dphyctl);
+ mask = DSIM_PHY_ULPS_EXIT_MASK;
+ dsim_phy_write_mask(id, DSIM_PHY_MD_TIME_CON4(i), val, mask);
+
+ val = hs_en;
+ mask = DSIM_PHY_HSTX_CLK_SEL;
+ dsim_phy_write_mask(id, DSIM_PHY_MD_TIME_CON0(i), val, mask);
+
+ /* skew cal implementation later */
+ val = DSIM_PHY_THS_PREPARE(t->hs_prepare);
+ mask = DSIM_PHY_THS_PREPARE_MASK;
+ dsim_phy_write_mask(id, DSIM_PHY_MD_TIME_CON1(i), val, mask);
+
+ val = DSIM_PHY_TLPX(t->lpx) | DSIM_PHY_TLP_EXIT_SKEW(0)
+ | DSIM_PHY_TLP_ENTRY_SKEW(0);
+ mask = DSIM_PHY_TLPX_MASK | DSIM_PHY_TLP_EXIT_SKEW_MASK
+ | DSIM_PHY_TLP_ENTRY_SKEW_MASK;
+ dsim_phy_write_mask(id, DSIM_PHY_MD_TIME_CON0(i), val, mask);
+
+ val = DSIM_PHY_THS_EXIT(t->hs_exit);
+ mask = DSIM_PHY_THS_EXIT_MASK;
+ dsim_phy_write_mask(id, DSIM_PHY_MD_TIME_CON2(i), val, mask);
+
+ val = DSIM_PHY_THS_ZERO(t->hs_zero);
+ mask = DSIM_PHY_THS_ZERO_MASK;
+ dsim_phy_write_mask(id, DSIM_PHY_MD_TIME_CON1(i), val, mask);
+
+ val = DSIM_PHY_THS_TRAIL(t->hs_trail);
+ mask = DSIM_PHY_THS_TRAIL_MASK;
+ dsim_phy_write_mask(id, DSIM_PHY_MD_TIME_CON2(i), val, mask);
+
+ val = DSIM_PHY_TTA_GET(3) | DSIM_PHY_TTA_GO(0);
+ mask = DSIM_PHY_TTA_GET_MASK | DSIM_PHY_TTA_GO_MASK;
+ dsim_phy_write_mask(id, DSIM_PHY_MD_TIME_CON3(i), val, mask);
+
+ /* add other clock lane setting if necessary */
+ }
+}
+
+#if defined(CONFIG_EXYNOS_DSIM_DITHER)
+static void dsim_reg_set_dphy_param_dither(u32 id, struct stdphy_pms *dphy_pms)
+{
+ u32 val, mask;
+
+ /* MFR */
+ val = DSIM_PHY_DITHER_MFR(dphy_pms->mfr);
+ mask = DSIM_PHY_DITHER_MFR_MASK;
+ dsim_phy_write_mask(id, DSIM_PHY_PLL_CON3, val, mask);
+
+ /* MRR */
+ val = DSIM_PHY_DITHER_MRR(dphy_pms->mrr);
+ mask = DSIM_PHY_DITHER_MRR_MASK;
+ dsim_phy_write_mask(id, DSIM_PHY_PLL_CON3, val, mask);
+
+ /* SEL_PF */
+ val = DSIM_PHY_DITHER_SEL_PF(dphy_pms->sel_pf);
+ mask = DSIM_PHY_DITHER_SEL_PF_MASK;
+ dsim_phy_write_mask(id, DSIM_PHY_PLL_CON5, val, mask);
+
+ /* ICP */
+ val = DSIM_PHY_DITHER_ICP(dphy_pms->icp);
+ mask = DSIM_PHY_DITHER_ICP_MASK;
+ dsim_phy_write_mask(id, DSIM_PHY_PLL_CON5, val, mask);
+
+ /* AFC_ENB */
+ val = (dphy_pms->afc_enb) ? ~0 : 0;
+ mask = DSIM_PHY_DITHER_AFC_ENB;
+ dsim_phy_write_mask(id, DSIM_PHY_PLL_CON4, val, mask);
+
+ /* EXTAFC */
+ val = DSIM_PHY_DITHER_EXTAFC(dphy_pms->extafc);
+ mask = DSIM_PHY_DITHER_EXTAFC_MASK;
+ dsim_phy_write_mask(id, DSIM_PHY_PLL_CON4, val, mask);
+
+ /* FEED_EN */
+ val = (dphy_pms->feed_en) ? ~0 : 0;
+ mask = DSIM_PHY_DITHER_FEED_EN;
+ dsim_phy_write_mask(id, DSIM_PHY_PLL_CON2, val, mask);
+
+ /* FSEL */
+ val = (dphy_pms->fsel) ? ~0 : 0;
+ mask = DSIM_PHY_DITHER_FSEL;
+ dsim_phy_write_mask(id, DSIM_PHY_PLL_CON4, val, mask);
+
+ /* FOUT_MASK */
+ val = (dphy_pms->fout_mask) ? ~0 : 0;
+ mask = DSIM_PHY_DITHER_FOUT_MASK;
+ dsim_phy_write_mask(id, DSIM_PHY_PLL_CON2, val, mask);
+
+ /* RSEL */
+ val = DSIM_PHY_DITHER_RSEL(dphy_pms->rsel);
+ mask = DSIM_PHY_DITHER_RSEL_MASK;
+ dsim_phy_write_mask(id, DSIM_PHY_PLL_CON4, val, mask);
+}
+#endif
+
+/* BIAS Block Control Register */
+static void dsim_reg_set_bias_con(u32 id, u32 *blk_ctl)
+{
+ u32 i;
+
+ for (i = 0; i < 5; i++)
+ dsim_phy_extra_write(id, DSIM_PHY_BIAS_CON(i), blk_ctl[i]);
+}
+
+/* PLL Control Register */
+static void dsim_reg_set_pll_con(u32 id, u32 *blk_ctl)
+{
+ u32 i;
+
+ for (i = 0; i < 8; i++)
+ dsim_phy_write(id, DSIM_PHY_PLL_CON(i), blk_ctl[i]);
+}
+
+/* Master Clock Lane General Control Register */
+static void dsim_reg_set_mc_gnr_con(u32 id, u32 *blk_ctl)
+{
+ u32 i;
+
+ for (i = 0; i < 2; i++)
+ dsim_phy_write(id, DSIM_PHY_MC_GNR_CON(i), blk_ctl[i]);
+}
+
+/* Master Clock Lane Analog Block Control Register */
+static void dsim_reg_set_mc_ana_con(u32 id, u32 *blk_ctl)
+{
+ u32 i;
+
+ for (i = 0; i < 2; i++)
+ dsim_phy_write(id, DSIM_PHY_MC_ANA_CON(i), blk_ctl[i]);
+}
+
+/* Master Data Lane General Control Register */
+static void dsim_reg_set_md_gnr_con(u32 id, u32 *blk_ctl)
+{
+ u32 i;
+
+ for (i = 0; i < MAX_DSIM_DATALANE_CNT; i++) {
+ dsim_phy_write(id, DSIM_PHY_MD_GNR_CON0(i), blk_ctl[0]);
+ dsim_phy_write(id, DSIM_PHY_MD_GNR_CON1(i), blk_ctl[1]);
+ }
+}
+
+/* Master Data Lane Analog Block Control Register */
+static void dsim_reg_set_md_ana_con(u32 id, u32 *blk_ctl)
+{
+ u32 i;
+
+ for (i = 0; i < MAX_DSIM_DATALANE_CNT; i++) {
+ dsim_phy_write(id, DSIM_PHY_MD_ANA_CON0(i), blk_ctl[0]);
+ dsim_phy_write(id, DSIM_PHY_MD_ANA_CON1(i), blk_ctl[1]);
+ dsim_phy_write(id, DSIM_PHY_MD_ANA_CON2(i), blk_ctl[2]);
+ dsim_phy_write(id, DSIM_PHY_MD_ANA_CON3(i), blk_ctl[3]);
+ }
+}
+
+#ifdef DPDN_INV_SWAP
+void dsim_reg_set_inv_dpdn(u32 id, u32 inv_clk, u32 inv_data[4])
+{
+ u32 i;
+ u32 val, mask;
+
+ val = inv_clk ? (DSIM_PHY_CLK_INV) : 0;
+ mask = DSIM_PHY_CLK_INV;
+ dsim_phy_write_mask(id, DSIM_PHY_MC_DATA_CON0, val, mask);
+
+ for (i = 0; i < MAX_DSIM_DATALANE_CNT; i++) {
+ val = inv_data[i] ? (DSIM_PHY_DATA_INV) : 0;
+ mask = DSIM_PHY_DATA_INV;
+ dsim_phy_write_mask(id, DSIM_PHY_MD_DATA_CON0(i), val, mask);
+ }
+}
+
+static void dsim_reg_set_dpdn_swap(u32 id, u32 clk_swap)
+{
+ u32 val, mask;
+
+ val = DSIM_PHY_DPDN_SWAP(clk_swap);
+ mask = DSIM_PHY_DPDN_SWAP_MASK;
+ dsim_phy_write_mask(id, DSIM_PHY_MC_ANA_CON1, val, mask);
+}
+#endif
+
+/******************* DSIM CAL functions *************************/
+static void dsim_reg_sw_reset(u32 id)
+{
+ u32 cnt = 1000;
+ u32 state;
+
+ dsim_write_mask(id, DSIM_SWRST, ~0, DSIM_SWRST_RESET);
+
+ do {
+ state = dsim_read(id, DSIM_SWRST) & DSIM_SWRST_RESET;
+ cnt--;
+ udelay(10);
+ } while (state && cnt);
+
+ if (!cnt)
+ dsim_err("%s is timeout.\n", __func__);
+}
+
+#if 0
+/* this function may be used for later use */
+static void dsim_reg_dphy_resetn(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_SWRST, val, DSIM_DPHY_RST); /* reset high */
+}
+#endif
+
+static void dsim_reg_set_num_of_lane(u32 id, u32 lane)
+{
+ u32 val = DSIM_CONFIG_NUM_OF_DATA_LANE(lane);
+
+ dsim_write_mask(id, DSIM_CONFIG, val,
+ DSIM_CONFIG_NUM_OF_DATA_LANE_MASK);
+}
+
+static void dsim_reg_enable_lane(u32 id, u32 lane, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_CONFIG, val, DSIM_CONFIG_LANES_EN(lane));
+}
+
+/*
+ * lane_id : 0 = MC, 1 = MD0, 2 = MD1, 3 = MD2, 4 = MD3
+ */
+static int dsim_reg_wait_phy_ready(u32 id, u32 lane_id, u32 en)
+{
+ u32 ready, reg_id, val;
+ u32 cnt = 1000;
+
+ if (lane_id == 0)
+ reg_id = DSIM_PHY_MC_GNR_CON0;
+ else
+ reg_id = DSIM_PHY_MD_GNR_CON0(lane_id-1);
+
+ do {
+ val = dsim_phy_read(id, reg_id);
+ ready = DSIM_PHY_PHY_READY_GET(val);
+ /* enable case */
+ if ((en == 1) && (ready == 1))
+ break;
+ /* disable case */
+ if ((en == 0) && (ready == 0))
+ break;
+
+ cnt--;
+ udelay(10);
+ } while (cnt);
+
+ if (!cnt) {
+ dsim_err("PHY lane(%d) is not ready[timeout]\n", lane_id);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int dsim_reg_enable_lane_phy(u32 id, u32 lane, u32 en)
+{
+ u32 i, lane_cnt = 0;
+ u32 reg_id;
+ u32 ret = 0;
+ u32 val = en ? ~0 : 0;
+
+ /* check enabled data lane count */
+ for (i = 0; i < MAX_DSIM_DATALANE_CNT; i++) {
+ if ((lane >> i) & 0x1)
+ lane_cnt++;
+ }
+
+ /*
+ * [step1] enable phy_enable
+ */
+
+ /* (1.1) clock lane on|off */
+ reg_id = DSIM_PHY_MC_GNR_CON0;
+ dsim_phy_write_mask(id, reg_id, val, DSIM_PHY_PHY_ENABLE);
+
+ /* (1.2) data lane on|off */
+ for (i = 0; i < lane_cnt; i++) {
+ reg_id = DSIM_PHY_MD_GNR_CON0(i);
+ dsim_phy_write_mask(id, reg_id, val, DSIM_PHY_PHY_ENABLE);
+ }
+
+ /*
+ * [step2] wait for phy_ready
+ */
+
+ /* (2.1) check ready of clock lane */
+ if (dsim_reg_wait_phy_ready(id, 0, en))
+ ret++;
+
+ /* (2.2) check ready of data lanes (index : from '1') */
+ for (i = 1; i <= lane_cnt; i++) {
+ if (dsim_reg_wait_phy_ready(id, i, en))
+ ret++;
+ }
+
+ if (ret) {
+ dsim_err("Error to enable PHY lane(err=%d)\n", ret);
+ return -EBUSY;
+ } else
+ return 0;
+}
+
+static void dsim_reg_pll_stable_time(u32 id, u32 lock_cnt)
+{
+ u32 val, mask;
+
+ val = DSIM_PHY_PLL_LOCK_CNT(lock_cnt);
+ mask = DSIM_PHY_PLL_LOCK_CNT_MASK;
+ dsim_phy_write_mask(id, DSIM_PHY_PLL_CON7, val, mask);
+}
+
+static void dsim_reg_set_pll(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dsim_phy_write_mask(id, DSIM_PHY_PLL_CON0, val, DSIM_PHY_PLL_EN_MASK);
+}
+
+static u32 dsim_reg_is_pll_stable(u32 id)
+{
+ u32 val, pll_lock;
+
+ val = dsim_phy_read(id, DSIM_PHY_PLL_STAT0);
+ pll_lock = DSIM_PHY_PLL_LOCK_GET(val);
+ if (pll_lock)
+ return 1;
+
+ return 0;
+}
+
+static int dsim_reg_enable_pll(u32 id, u32 en)
+{
+
+ u32 cnt;
+
+ if (en) {
+ cnt = 1000;
+ dsim_reg_clear_int(id, DSIM_INTSRC_PLL_STABLE);
+
+ dsim_reg_set_pll(id, 1);
+ while (1) {
+ cnt--;
+ if (dsim_reg_is_pll_stable(id))
+ return 0;
+ if (cnt == 0)
+ return -EBUSY;
+ udelay(10);
+ }
+ } else {
+ dsim_reg_set_pll(id, 0);
+ while (1) {
+ cnt--;
+ if (!dsim_reg_is_pll_stable(id))
+ return 0;
+ if (cnt == 0)
+ return -EBUSY;
+ udelay(10);
+ }
+ }
+
+ return 0;
+}
+
+static void dsim_reg_set_esc_clk_en(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_CLK_CTRL, val, DSIM_CLK_CTRL_ESCCLK_EN);
+}
+
+static void dsim_reg_set_esc_clk_prescaler(u32 id, u32 en, u32 p)
+{
+ u32 val = en ? DSIM_CLK_CTRL_ESCCLK_EN : 0;
+ u32 mask = DSIM_CLK_CTRL_ESCCLK_EN | DSIM_CLK_CTRL_ESC_PRESCALER_MASK;
+
+ val |= DSIM_CLK_CTRL_ESC_PRESCALER(p);
+ dsim_write_mask(id, DSIM_CLK_CTRL, val, mask);
+}
+
+static void dsim_reg_set_esc_clk_on_lane(u32 id, u32 en, u32 lane)
+{
+ u32 val;
+
+ lane = (lane >> 1) | (1 << 4);
+
+ val = en ? DSIM_CLK_CTRL_LANE_ESCCLK_EN(lane) : 0;
+ dsim_write_mask(id, DSIM_CLK_CTRL, val,
+ DSIM_CLK_CTRL_LANE_ESCCLK_EN_MASK);
+}
+
+static void dsim_reg_set_stop_state_cnt(u32 id)
+{
+ u32 val = DSIM_ESCMODE_STOP_STATE_CNT(DSIM_STOP_STATE_CNT);
+
+ dsim_write_mask(id, DSIM_ESCMODE, val,
+ DSIM_ESCMODE_STOP_STATE_CNT_MASK);
+}
+
+static void dsim_reg_set_bta_timeout(u32 id)
+{
+ u32 val = DSIM_TIMEOUT_BTA_TOUT(DSIM_BTA_TIMEOUT);
+
+ dsim_write_mask(id, DSIM_TIMEOUT, val, DSIM_TIMEOUT_BTA_TOUT_MASK);
+}
+
+static void dsim_reg_set_lpdr_timeout(u32 id)
+{
+ u32 val = DSIM_TIMEOUT_LPDR_TOUT(DSIM_LP_RX_TIMEOUT);
+
+ dsim_write_mask(id, DSIM_TIMEOUT, val, DSIM_TIMEOUT_LPDR_TOUT_MASK);
+}
+
+static void dsim_reg_disable_hsa(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_CONFIG, val, DSIM_CONFIG_HSA_DISABLE);
+}
+
+static void dsim_reg_disable_hbp(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_CONFIG, val, DSIM_CONFIG_HBP_DISABLE);
+}
+
+static void dsim_reg_disable_hfp(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_CONFIG, val, DSIM_CONFIG_HFP_DISABLE);
+}
+
+static void dsim_reg_disable_hse(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_CONFIG, val, DSIM_CONFIG_HSE_DISABLE);
+}
+
+static void dsim_reg_set_burst_mode(u32 id, u32 burst)
+{
+ u32 val = burst ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_CONFIG, val, DSIM_CONFIG_BURST_MODE);
+}
+
+static void dsim_reg_set_sync_inform(u32 id, u32 inform)
+{
+ u32 val = inform ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_CONFIG, val, DSIM_CONFIG_SYNC_INFORM);
+}
+
+/*
+ * Following functions will not be used untile the correct guide is given.
+ */
+static void dsim_reg_set_pll_clk_gate_enable(u32 id, u32 en)
+{
+ u32 ver, mask, reg_id;
+ u32 val = en ? ~0 : 0;
+
+ ver = dsim_read(id, DSIM_VERSION);
+ if (ver == DSIM_VER_EVT0) {
+ reg_id = 0x0200;
+ mask = 0x1;
+ } else {
+ reg_id = DSIM_CONFIG;
+ mask = DSIM_CONFIG_PLL_CLOCK_GATING;
+ }
+ dsim_write_mask(id, reg_id, val, mask);
+}
+
+#if 0
+/* This function is available from EVT1 */
+static void dsim_reg_set_pll_sleep_enable(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_CONFIG, val, DSIM_CONFIG_PLL_SLEEP);
+}
+#endif
+
+/* 0=D-PHY, 1=C-PHY */
+void dsim_reg_set_phy_selection(u32 id, u32 sel)
+{
+ u32 val = sel ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_CONFIG, val, DSIM_CONFIG_PHY_SELECTION);
+}
+
+static void dsim_reg_set_vfp(u32 id, u32 vfp)
+{
+ u32 val = DSIM_VPORCH_VFP_TOTAL(vfp);
+
+ dsim_write_mask(id, DSIM_VPORCH, val, DSIM_VPORCH_VFP_TOTAL_MASK);
+}
+
+static void dsim_reg_set_cmdallow(u32 id, u32 cmdallow)
+{
+ u32 val = DSIM_VPORCH_VFP_CMD_ALLOW(cmdallow);
+
+ dsim_write_mask(id, DSIM_VPORCH, val, DSIM_VPORCH_VFP_CMD_ALLOW_MASK);
+}
+
+static void dsim_reg_set_stable_vfp(u32 id, u32 stablevfp)
+{
+ u32 val = DSIM_VPORCH_STABLE_VFP(stablevfp);
+
+ dsim_write_mask(id, DSIM_VPORCH, val, DSIM_VPORCH_STABLE_VFP_MASK);
+}
+
+static void dsim_reg_set_vbp(u32 id, u32 vbp)
+{
+ u32 val = DSIM_VPORCH_VBP(vbp);
+
+ dsim_write_mask(id, DSIM_VPORCH, val, DSIM_VPORCH_VBP_MASK);
+}
+
+static void dsim_reg_set_hfp(u32 id, u32 hfp)
+{
+ u32 val = DSIM_HPORCH_HFP(hfp);
+
+ dsim_write_mask(id, DSIM_HPORCH, val, DSIM_HPORCH_HFP_MASK);
+}
+
+static void dsim_reg_set_hbp(u32 id, u32 hbp)
+{
+ u32 val = DSIM_HPORCH_HBP(hbp);
+
+ dsim_write_mask(id, DSIM_HPORCH, val, DSIM_HPORCH_HBP_MASK);
+}
+
+static void dsim_reg_set_vsa(u32 id, u32 vsa)
+{
+ u32 val = DSIM_SYNC_VSA(vsa);
+
+ dsim_write_mask(id, DSIM_SYNC, val, DSIM_SYNC_VSA_MASK);
+}
+
+static void dsim_reg_set_hsa(u32 id, u32 hsa)
+{
+ u32 val = DSIM_SYNC_HSA(hsa);
+
+ dsim_write_mask(id, DSIM_SYNC, val, DSIM_SYNC_HSA_MASK);
+}
+
+static void dsim_reg_set_vresol(u32 id, u32 vresol)
+{
+ u32 val = DSIM_RESOL_VRESOL(vresol);
+
+ dsim_write_mask(id, DSIM_RESOL, val, DSIM_RESOL_VRESOL_MASK);
+}
+
+static void dsim_reg_set_hresol(u32 id, u32 hresol, struct decon_lcd *lcd)
+{
+ u32 width, val;
+
+ if (lcd->dsc_enabled)
+ width = hresol / 3;
+ else
+ width = hresol;
+
+ val = DSIM_RESOL_HRESOL(width);
+
+ dsim_write_mask(id, DSIM_RESOL, val, DSIM_RESOL_HRESOL_MASK);
+}
+
+static void dsim_reg_set_porch(u32 id, struct decon_lcd *lcd)
+{
+ if (lcd->mode == DECON_VIDEO_MODE) {
+ dsim_reg_set_vbp(id, lcd->vbp);
+ dsim_reg_set_vfp(id, lcd->vfp);
+ dsim_reg_set_stable_vfp(id, DSIM_STABLE_VFP_VALUE);
+ dsim_reg_set_cmdallow(id, DSIM_CMD_ALLOW_VALUE);
+ dsim_reg_set_hbp(id, lcd->hbp);
+ dsim_reg_set_hfp(id, lcd->hfp);
+ dsim_reg_set_vsa(id, lcd->vsa);
+ dsim_reg_set_hsa(id, lcd->hsa);
+ }
+}
+
+static void dsim_reg_set_pixel_format(u32 id, u32 pixformat)
+{
+ u32 val = DSIM_CONFIG_PIXEL_FORMAT(pixformat);
+
+ dsim_write_mask(id, DSIM_CONFIG, val, DSIM_CONFIG_PIXEL_FORMAT_MASK);
+}
+
+static void dsim_reg_set_vc_id(u32 id, u32 vcid)
+{
+ u32 val = DSIM_CONFIG_VC_ID(vcid);
+
+ dsim_write_mask(id, DSIM_CONFIG, val, DSIM_CONFIG_VC_ID_MASK);
+}
+
+static void dsim_reg_set_video_mode(u32 id, u32 mode)
+{
+ u32 val = mode ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_CONFIG, val, DSIM_CONFIG_VIDEO_MODE);
+}
+
+static int dsim_reg_wait_idle_status(u32 id, u32 is_vm)
+{
+ u32 cnt = 1000;
+ u32 reg_id, val, status;
+
+ if (is_vm)
+ reg_id = DSIM_LINK_STATUS0;
+ else
+ reg_id = DSIM_LINK_STATUS1;
+
+ do {
+ val = dsim_read(id, reg_id);
+ status = is_vm ? DSIM_LINK_STATUS0_VIDEO_MODE_STATUS_GET(val) :
+ DSIM_LINK_STATUS1_CMD_MODE_STATUS_GET(val);
+ if (status == DSIM_STATUS_IDLE)
+ break;
+ cnt--;
+ udelay(10);
+ } while (cnt);
+
+ if (!cnt) {
+ dsim_err("dsim%d wait timeout idle status(%u)\n", id, status);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+/* 0 = command, 1 = video mode */
+static u32 dsim_reg_get_display_mode(u32 id)
+{
+ u32 val;
+
+ val = dsim_read(id, DSIM_CONFIG);
+ return DSIM_CONFIG_DISPLAY_MODE_GET(val);
+}
+
+static void dsim_reg_enable_dsc(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_CONFIG, val, DSIM_CONFIG_CPRS_EN);
+}
+
+static void dsim_reg_set_num_of_slice(u32 id, u32 num_of_slice)
+{
+ u32 val = DSIM_CPRS_CTRL_NUM_OF_SLICE(num_of_slice);
+
+ dsim_write_mask(id, DSIM_CPRS_CTRL, val,
+ DSIM_CPRS_CTRL_NUM_OF_SLICE_MASK);
+}
+
+static void dsim_reg_get_num_of_slice(u32 id, u32 *num_of_slice)
+{
+ u32 val = dsim_read(id, DSIM_CPRS_CTRL);
+
+ *num_of_slice = DSIM_CPRS_CTRL_NUM_OF_SLICE_GET(val);
+}
+
+static void dsim_reg_set_multi_slice(u32 id, struct decon_lcd *lcd_info)
+{
+ u32 multi_slice = 1;
+ u32 val;
+
+ /* if multi-slice(2~4 slices) DSC compression is used in video mode
+ * MULTI_SLICE_PACKET configuration must be matched
+ * to DDI's configuration
+ */
+ if (lcd_info->mode == DECON_MIPI_COMMAND_MODE)
+ multi_slice = 1;
+ else if (lcd_info->mode == DECON_VIDEO_MODE)
+ multi_slice = lcd_info->dsc_slice_num > 1 ? 1 : 0;
+
+ /* if MULTI_SLICE_PACKET is enabled,
+ * only one packet header is transferred
+ * for multi slice
+ */
+ val = multi_slice ? ~0 : 0;
+ dsim_write_mask(id, DSIM_CPRS_CTRL, val,
+ DSIM_CPRS_CTRL_MULI_SLICE_PACKET);
+}
+
+static void dsim_reg_set_size_of_slice(u32 id, struct decon_lcd *lcd_info)
+{
+ u32 slice_w = lcd_info->xres / lcd_info->dsc_slice_num;
+ u32 val_01 = 0, mask_01 = 0;
+ u32 val_23 = 0, mask_23 = 0;
+
+ if (lcd_info->dsc_slice_num == 4) {
+ val_01 = DSIM_SLICE01_SIZE_OF_SLICE1(slice_w) |
+ DSIM_SLICE01_SIZE_OF_SLICE0(slice_w);
+ mask_01 = DSIM_SLICE01_SIZE_OF_SLICE1_MASK |
+ DSIM_SLICE01_SIZE_OF_SLICE0_MASK;
+ val_23 = DSIM_SLICE23_SIZE_OF_SLICE3(slice_w) |
+ DSIM_SLICE23_SIZE_OF_SLICE2(slice_w);
+ mask_23 = DSIM_SLICE23_SIZE_OF_SLICE3_MASK |
+ DSIM_SLICE23_SIZE_OF_SLICE2_MASK;
+
+ dsim_write_mask(id, DSIM_SLICE01, val_01, mask_01);
+ dsim_write_mask(id, DSIM_SLICE23, val_23, mask_23);
+ } else if (lcd_info->dsc_slice_num == 2) {
+ val_01 = DSIM_SLICE01_SIZE_OF_SLICE1(slice_w) |
+ DSIM_SLICE01_SIZE_OF_SLICE0(slice_w);
+ mask_01 = DSIM_SLICE01_SIZE_OF_SLICE1_MASK |
+ DSIM_SLICE01_SIZE_OF_SLICE0_MASK;
+
+ dsim_write_mask(id, DSIM_SLICE01, val_01, mask_01);
+ } else if (lcd_info->dsc_slice_num == 1) {
+ val_01 = DSIM_SLICE01_SIZE_OF_SLICE0(slice_w);
+ mask_01 = DSIM_SLICE01_SIZE_OF_SLICE0_MASK;
+
+ dsim_write_mask(id, DSIM_SLICE01, val_01, mask_01);
+ } else {
+ dsim_err("not supported slice mode. dsc(%d), slice(%d)\n",
+ lcd_info->dsc_cnt, lcd_info->dsc_slice_num);
+ }
+}
+
+static void dsim_reg_print_size_of_slice(u32 id)
+{
+ u32 val;
+ u32 slice0_w, slice1_w, slice2_w, slice3_w;
+
+ val = dsim_read(id, DSIM_SLICE01);
+ slice0_w = DSIM_SLICE01_SIZE_OF_SLICE0_GET(val);
+ slice1_w = DSIM_SLICE01_SIZE_OF_SLICE1_GET(val);
+
+ val = dsim_read(id, DSIM_SLICE23);
+ slice2_w = DSIM_SLICE23_SIZE_OF_SLICE2_GET(val);
+ slice3_w = DSIM_SLICE23_SIZE_OF_SLICE3_GET(val);
+
+ dsim_dbg("dsim%d: slice0 w(%d), slice1 w(%d), slice2 w(%d), slice3(%d)\n",
+ id, slice0_w, slice1_w, slice2_w, slice3_w);
+}
+
+static void dsim_reg_set_multi_packet_count(u32 id, u32 multipacketcnt)
+{
+ u32 val = DSIM_CMD_CONFIG_MULTI_PKT_CNT(multipacketcnt);
+
+ dsim_write_mask(id, DSIM_CMD_CONFIG, val,
+ DSIM_CMD_CONFIG_MULTI_PKT_CNT_MASK);
+}
+
+static void dsim_reg_set_time_stable_vfp(u32 id, u32 stablevfp)
+{
+ u32 val = DSIM_CMD_TE_CTRL0_TIME_STABLE_VFP(stablevfp);
+
+ dsim_write_mask(id, DSIM_CMD_TE_CTRL0, val,
+ DSIM_CMD_TE_CTRL0_TIME_STABLE_VFP_MASK);
+}
+
+static void dsim_reg_set_time_te_protect_on(u32 id, u32 teprotecton)
+{
+ u32 val = DSIM_CMD_TE_CTRL1_TIME_TE_PROTECT_ON(teprotecton);
+
+ dsim_write_mask(id, DSIM_CMD_TE_CTRL1, val,
+ DSIM_CMD_TE_CTRL1_TIME_TE_PROTECT_ON_MASK);
+}
+
+static void dsim_reg_set_time_te_timeout(u32 id, u32 tetout)
+{
+ u32 val = DSIM_CMD_TE_CTRL1_TIME_TE_TOUT(tetout);
+
+ dsim_write_mask(id, DSIM_CMD_TE_CTRL1, val,
+ DSIM_CMD_TE_CTRL1_TIME_TE_TOUT_MASK);
+}
+
+static void dsim_reg_set_cmd_ctrl(u32 id, struct decon_lcd *lcd_info,
+ struct dsim_clks *clks)
+{
+ unsigned int time_stable_vfp;
+ unsigned int time_te_protect_on;
+ unsigned int time_te_tout;
+
+ time_stable_vfp = lcd_info->xres * DSIM_STABLE_VFP_VALUE * 3 / 100;
+ time_te_protect_on = (clks->hs_clk * TE_PROTECT_ON_TIME) / 16;
+ time_te_tout = (clks->hs_clk * TE_TIMEOUT_TIME) / 16;
+ dsim_reg_set_time_stable_vfp(id, time_stable_vfp);
+ dsim_reg_set_time_te_protect_on(id, time_te_protect_on);
+ dsim_reg_set_time_te_timeout(id, time_te_tout);
+}
+
+static void dsim_reg_enable_noncont_clock(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_CLK_CTRL, val,
+ DSIM_CLK_CTRL_NONCONT_CLOCK_LANE);
+}
+
+static void dsim_reg_enable_clocklane(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_CLK_CTRL, val,
+ DSIM_CLK_CTRL_CLKLANE_ONOFF);
+}
+
+static void dsim_reg_enable_packetgo(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_CMD_CONFIG, val,
+ DSIM_CMD_CONFIG_PKT_GO_EN);
+}
+
+static void dsim_reg_enable_multi_cmd_packet(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_CMD_CONFIG, val,
+ DSIM_CMD_CONFIG_MULTI_CMD_PKT_EN);
+}
+
+static void dsim_reg_enable_shadow(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_SFR_CTRL, val,
+ DSIM_SFR_CTRL_SHADOW_EN);
+}
+
+static void dsim_reg_set_link_clock(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_CLK_CTRL, val, DSIM_CLK_CTRL_CLOCK_SEL);
+}
+
+static int dsim_reg_get_link_clock(u32 id)
+{
+ int val = 0;
+
+ val = dsim_read_mask(id, DSIM_CLK_CTRL, DSIM_CLK_CTRL_CLOCK_SEL);
+
+ return val;
+}
+
+static void dsim_reg_enable_hs_clock(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_CLK_CTRL, val, DSIM_CLK_CTRL_TX_REQUEST_HSCLK);
+}
+
+static void dsim_reg_enable_word_clock(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_CLK_CTRL, val, DSIM_CLK_CTRL_WORDCLK_EN);
+}
+
+u32 dsim_reg_is_hs_clk_ready(u32 id)
+{
+ if (dsim_read(id, DSIM_DPHY_STATUS) & DSIM_DPHY_STATUS_TX_READY_HS_CLK)
+ return 1;
+
+ return 0;
+}
+
+u32 dsim_reg_is_clk_stop(u32 id)
+{
+ if (dsim_read(id, DSIM_DPHY_STATUS) & DSIM_DPHY_STATUS_STOP_STATE_CLK)
+ return 1;
+
+ return 0;
+}
+
+void dsim_reg_enable_per_frame_read(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_CONFIG, val, DSIM_CONFIG_PER_FRAME_READ_EN);
+}
+
+int dsim_reg_wait_hs_clk_ready(u32 id)
+{
+ u32 state;
+ u32 cnt = 1000;
+
+ do {
+ state = dsim_reg_is_hs_clk_ready(id);
+ cnt--;
+ udelay(10);
+ } while (!state && cnt);
+
+ if (!cnt) {
+ dsim_err("DSI Master is not HS state.\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+int dsim_reg_wait_hs_clk_disable(u32 id)
+{
+ u32 state;
+ u32 cnt = 1000;
+
+ do {
+ state = dsim_reg_is_clk_stop(id);
+ cnt--;
+ udelay(10);
+ } while (!state && cnt);
+
+ if (!cnt) {
+ dsim_err("DSI Master clock isn't disabled.\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+void dsim_reg_force_dphy_stop_state(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_ESCMODE, val, DSIM_ESCMODE_FORCE_STOP_STATE);
+}
+
+void dsim_reg_enter_ulps(u32 id, u32 enter)
+{
+ u32 val = enter ? ~0 : 0;
+ u32 mask = DSIM_ESCMODE_TX_ULPS_CLK | DSIM_ESCMODE_TX_ULPS_DATA;
+
+ dsim_write_mask(id, DSIM_ESCMODE, val, mask);
+}
+
+void dsim_reg_exit_ulps(u32 id, u32 exit)
+{
+ u32 val = exit ? ~0 : 0;
+ u32 mask = DSIM_ESCMODE_TX_ULPS_CLK_EXIT |
+ DSIM_ESCMODE_TX_ULPS_DATA_EXIT;
+
+ dsim_write_mask(id, DSIM_ESCMODE, val, mask);
+}
+
+void dsim_reg_set_num_of_transfer(u32 id, u32 num_of_transfer)
+{
+ u32 val = DSIM_NUM_OF_TRANSFER_PER_FRAME(num_of_transfer);
+
+ dsim_write_mask(id, DSIM_NUM_OF_TRANSFER, val,
+ DSIM_NUM_OF_TRANSFER_PER_FRAME_MASK);
+
+ dsim_dbg("%s, write value : 0x%x, read value : 0x%x\n", __func__,
+ val, dsim_read(id, DSIM_NUM_OF_TRANSFER));
+}
+
+static u32 dsim_reg_is_ulps_state(u32 id, u32 lanes)
+{
+ u32 val = dsim_read(id, DSIM_DPHY_STATUS);
+ u32 data_lane = lanes >> DSIM_LANE_CLOCK;
+
+ if ((DSIM_DPHY_STATUS_ULPS_DATA_LANE_GET(val) == data_lane)
+ && (val & DSIM_DPHY_STATUS_ULPS_CLK))
+ return 1;
+
+ return 0;
+}
+
+static void dsim_reg_set_deskew_hw_interval(u32 id, u32 interval)
+{
+ u32 val = DSIM_DESKEW_CTRL_HW_INTERVAL(interval);
+
+ dsim_write_mask(id, DSIM_DESKEW_CTRL, val,
+ DSIM_DESKEW_CTRL_HW_INTERVAL_MASK);
+}
+
+static void dsim_reg_set_deskew_hw_position(u32 id, u32 position)
+{
+ u32 val = position ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_DESKEW_CTRL, val,
+ DSIM_DESKEW_CTRL_HW_POSITION);
+}
+
+static void dsim_reg_enable_deskew_hw_enable(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_DESKEW_CTRL, val, DSIM_DESKEW_CTRL_HW_EN);
+}
+
+static void dsim_reg_set_cm_underrun_lp_ref(u32 id, u32 lp_ref)
+{
+ u32 val = DSIM_UNDERRUN_CTRL_CM_UNDERRUN_LP_REF(lp_ref);
+
+ dsim_write_mask(id, DSIM_UNDERRUN_CTRL, val,
+ DSIM_UNDERRUN_CTRL_CM_UNDERRUN_LP_REF_MASK);
+}
+
+static void dsim_reg_set_threshold(u32 id, u32 threshold)
+{
+ u32 val = DSIM_THRESHOLD_LEVEL(threshold);
+
+ dsim_write_mask(id, DSIM_THRESHOLD, val, DSIM_THRESHOLD_LEVEL_MASK);
+
+}
+
+static void dsim_reg_enable_eotp(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_CONFIG, val, DSIM_CONFIG_EOTP_EN);
+}
+
+static void dsim_reg_set_vt_compensate(u32 id, u32 compensate)
+{
+ u32 val = DSIM_VIDEO_TIMER_COMPENSATE(compensate);
+
+ dsim_write_mask(id, DSIM_VIDEO_TIMER, val,
+ DSIM_VIDEO_TIMER_COMPENSATE_MASK);
+}
+
+static void dsim_reg_set_vstatus_int(u32 id, u32 vstatus)
+{
+ u32 val = DSIM_VIDEO_TIMER_VSTATUS_INTR_SEL(vstatus);
+
+ dsim_write_mask(id, DSIM_VIDEO_TIMER, val,
+ DSIM_VIDEO_TIMER_VSTATUS_INTR_SEL_MASK);
+}
+
+static void dsim_reg_set_bist_te_interval(u32 id, u32 interval)
+{
+ u32 val = DSIM_BIST_CTRL0_BIST_TE_INTERVAL(interval);
+
+ dsim_write_mask(id, DSIM_BIST_CTRL0, val,
+ DSIM_BIST_CTRL0_BIST_TE_INTERVAL_MASK);
+}
+
+static void dsim_reg_set_bist_mode(u32 id, u32 bist_mode)
+{
+ u32 val = DSIM_BIST_CTRL0_BIST_PTRN_MODE(bist_mode);
+
+ dsim_write_mask(id, DSIM_BIST_CTRL0, val,
+ DSIM_BIST_CTRL0_BIST_PTRN_MODE_MASK);
+}
+
+static void dsim_reg_enable_bist_pattern_move(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_BIST_CTRL0, val,
+ DSIM_BIST_CTRL0_BIST_PTRN_MOVE_EN);
+}
+
+static void dsim_reg_enable_bist(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_BIST_CTRL0, val, DSIM_BIST_CTRL0_BIST_EN);
+}
+
+static void dsim_set_hw_deskew(u32 id, u32 en)
+{
+ u32 hw_interval = 1;
+
+ if (en) {
+ dsim_reg_set_deskew_hw_interval(id, hw_interval);
+ /* 0 : VBP first line, 1 : VFP last line*/
+ dsim_reg_set_deskew_hw_position(id, 0);
+ dsim_reg_enable_deskew_hw_enable(id, en);
+ } else {
+ dsim_reg_enable_deskew_hw_enable(id, en);
+ }
+}
+
+static int dsim_reg_wait_enter_ulps_state(u32 id, u32 lanes)
+{
+ u32 state;
+ u32 cnt = 1000;
+
+ do {
+ state = dsim_reg_is_ulps_state(id, lanes);
+ cnt--;
+ udelay(10);
+ } while (!state && cnt);
+
+ if (!cnt) {
+ dsim_err("DSI Master is not ULPS state.\n");
+ return -EBUSY;
+ }
+
+ dsim_dbg("DSI Master is ULPS state.\n");
+
+ return 0;
+}
+
+static u32 dsim_reg_is_not_ulps_state(u32 id)
+{
+ u32 val = dsim_read(id, DSIM_DPHY_STATUS);
+
+ if (!(DSIM_DPHY_STATUS_ULPS_DATA_LANE_GET(val))
+ && !(val & DSIM_DPHY_STATUS_ULPS_CLK))
+ return 1;
+
+ return 0;
+}
+
+static int dsim_reg_wait_exit_ulps_state(u32 id)
+{
+ u32 state;
+ u32 cnt = 1000;
+
+ do {
+ state = dsim_reg_is_not_ulps_state(id);
+ cnt--;
+ udelay(10);
+ } while (!state && cnt);
+
+ if (!cnt) {
+ dsim_err("DSI Master is not stop state.\n");
+ return -EBUSY;
+ }
+
+ dsim_dbg("DSI Master is stop state.\n");
+
+ return 0;
+}
+
+static int dsim_reg_get_dphy_timing(u32 hs_clk, u32 esc_clk,
+ struct dphy_timing_value *t)
+{
+ int i;
+
+ i = ARRAY_SIZE(dphy_timing) - 1;
+
+ for (; i >= 0; i--) {
+ if (dphy_timing[i][0] < hs_clk) {
+ continue;
+ } else {
+ t->bps = hs_clk;
+ t->clk_prepare = dphy_timing[i][1];
+ t->clk_zero = dphy_timing[i][2];
+ t->clk_post = dphy_timing[i][3];
+ t->clk_trail = dphy_timing[i][4];
+ t->hs_prepare = dphy_timing[i][5];
+ t->hs_zero = dphy_timing[i][6];
+ t->hs_trail = dphy_timing[i][7];
+ t->lpx = dphy_timing[i][8];
+ t->hs_exit = dphy_timing[i][9];
+ break;
+ }
+ }
+
+ if (i < 0) {
+ dsim_err("%u Mhz hs clock can't find proper dphy timing values\n",
+ hs_clk);
+ return -EINVAL;
+ }
+
+ dsim_dbg("%s: bps(%u) clk_prepare(%u) clk_zero(%u) clk_post(%u)\n",
+ __func__, t->bps, t->clk_prepare, t->clk_zero,
+ t->clk_post);
+ dsim_dbg("clk_trail(%u) hs_prepare(%u) hs_zero(%u) hs_trail(%u)\n",
+ t->clk_trail, t->hs_prepare, t->hs_zero, t->hs_trail);
+ dsim_dbg("lpx(%u) hs_exit(%u)\n", t->lpx, t->hs_exit);
+
+ if ((esc_clk > 20) || (esc_clk < 7)) {
+ dsim_err("%u Mhz cann't be used as escape clock\n", esc_clk);
+ return -EINVAL;
+ }
+
+ t->b_dphyctl = b_dphyctl[esc_clk - 7];
+ dsim_dbg("b_dphyctl(%u)\n", t->b_dphyctl);
+
+ return 0;
+}
+
+static void dsim_reg_set_config(u32 id, struct decon_lcd *lcd_info,
+ struct dsim_clks *clks)
+{
+ u32 threshold;
+ u32 num_of_slice;
+ u32 num_of_transfer;
+ int idx;
+
+ /* shadow read disable */
+ dsim_reg_enable_shadow_read(id, 1);
+ /* dsim_reg_enable_shadow(id, 1); */
+
+ if (lcd_info->mode == DECON_VIDEO_MODE)
+ dsim_reg_enable_clocklane(id, 0);
+ else
+ dsim_reg_enable_noncont_clock(id, 1);
+
+ dsim_set_hw_deskew(id, 0); /* second param is to control enable bit */
+
+ dsim_reg_set_bta_timeout(id);
+ dsim_reg_set_lpdr_timeout(id);
+
+ dsim_reg_set_cmd_transfer_mode(id, 0);
+ dsim_reg_set_stop_state_cnt(id);
+
+ if (lcd_info->mode == DECON_MIPI_COMMAND_MODE) {
+ /* DSU_MODE_1 is used in stead of 1 in MCD */
+ idx = lcd_info->mres_mode;
+ dsim_reg_set_cm_underrun_lp_ref(id,
+ lcd_info->cmd_underrun_lp_ref[idx]);
+ }
+
+ if (lcd_info->dsc_enabled)
+ threshold = lcd_info->xres / 3;
+ else
+ threshold = lcd_info->xres;
+
+ dsim_reg_set_threshold(id, threshold);
+
+ dsim_reg_set_vresol(id, lcd_info->yres);
+ dsim_reg_set_hresol(id, lcd_info->xres, lcd_info);
+ dsim_reg_set_porch(id, lcd_info);
+
+ if (lcd_info->mode == DECON_MIPI_COMMAND_MODE) {
+ if (lcd_info->dsc_enabled)
+ num_of_transfer = lcd_info->xres * lcd_info->yres
+ / threshold / 3;
+ else
+ num_of_transfer = lcd_info->xres * lcd_info->yres
+ / threshold;
+
+ dsim_reg_set_num_of_transfer(id, num_of_transfer);
+ } else {
+ num_of_transfer = lcd_info->xres * lcd_info->yres / threshold;
+ dsim_reg_set_num_of_transfer(id, num_of_transfer);
+ }
+
+ dsim_reg_set_num_of_lane(id, lcd_info->data_lane - 1);
+ dsim_reg_enable_eotp(id, 1);
+ dsim_reg_enable_per_frame_read(id, 0);
+ dsim_reg_set_pixel_format(id, DSIM_PIXEL_FORMAT_RGB24);
+ dsim_reg_set_vc_id(id, 0);
+
+ /* CPSR & VIDEO MODE can be set when shadow enable on */
+ /* shadow enable */
+ dsim_reg_enable_shadow(id, 1);
+ if (lcd_info->mode == DECON_VIDEO_MODE) {
+ dsim_reg_set_video_mode(id, DSIM_CONFIG_VIDEO_MODE);
+ dsim_dbg("%s: video mode set\n", __func__);
+ } else {
+ dsim_reg_set_video_mode(id, 0);
+ dsim_dbg("%s: command mode set\n", __func__);
+ }
+
+ dsim_reg_enable_dsc(id, lcd_info->dsc_enabled);
+
+ /* shadow disable */
+ dsim_reg_enable_shadow(id, 0);
+
+ if (lcd_info->mode == DECON_VIDEO_MODE) {
+ dsim_reg_disable_hsa(id, 0);
+ dsim_reg_disable_hbp(id, 0);
+ dsim_reg_disable_hfp(id, 1);
+ dsim_reg_disable_hse(id, 0);
+ dsim_reg_set_burst_mode(id, 1);
+ dsim_reg_set_sync_inform(id, 0);
+ dsim_reg_enable_clocklane(id, 0);
+ }
+
+ if (lcd_info->dsc_enabled) {
+ dsim_dbg("%s: dsc configuration is set\n", __func__);
+ dsim_reg_set_num_of_slice(id, lcd_info->dsc_slice_num);
+ dsim_reg_set_multi_slice(id, lcd_info); /* multi slice */
+ dsim_reg_set_size_of_slice(id, lcd_info);
+
+ dsim_reg_get_num_of_slice(id, &num_of_slice);
+ dsim_dbg("dsim%d: number of DSC slice(%d)\n", id, num_of_slice);
+ dsim_reg_print_size_of_slice(id);
+ }
+
+ if (lcd_info->mode == DECON_VIDEO_MODE) {
+ dsim_reg_set_multi_packet_count(id, 0xff);
+ dsim_reg_enable_multi_cmd_packet(id, 0);
+ }
+ dsim_reg_enable_packetgo(id, 0);
+
+ if (lcd_info->mode == DECON_MIPI_COMMAND_MODE) {
+ dsim_reg_set_cmd_ctrl(id, lcd_info, clks);
+ } else if (lcd_info->mode == DECON_VIDEO_MODE) {
+ dsim_reg_set_vt_compensate(id, lcd_info->vt_compensation);
+ dsim_reg_set_vstatus_int(id, DSIM_VSYNC);
+ }
+
+ /* dsim_reg_enable_shadow_read(id, 1); */
+ /* dsim_reg_enable_shadow(id, 1); */
+
+}
+
+/*
+ * configure and set DPHY PLL, byte clock, escape clock and hs clock
+ * - PMS value have to be optained by using PMS Gen.
+ * tool (MSC_PLL_WIZARD2_00.exe)
+ * - PLL out is source clock of HS clock
+ * - byte clock = HS clock / 16
+ * - calculate divider of escape clock using requested escape clock
+ * from driver
+ * - DPHY PLL, byte clock, escape clock are enabled.
+ * - HS clock will be enabled another function.
+ *
+ * Parameters
+ * - hs_clk : in/out parameter.
+ * in : requested hs clock. out : calculated hs clock
+ * - esc_clk : in/out paramter.
+ * in : requested escape clock. out : calculated escape clock
+ * - word_clk : out parameter. byte clock = hs clock / 16
+ */
+static int dsim_reg_set_clocks(u32 id, struct dsim_clks *clks,
+ struct stdphy_pms *dphy_pms, u32 en)
+{
+ unsigned int esc_div;
+ struct dsim_pll_param pll;
+ struct dphy_timing_value t;
+ int ret = 0;
+ u32 hsmode = 0;
+#ifdef DPDN_INV_SWAP
+ u32 inv_data[4] = {0, };
+#endif
+
+ if (en) {
+ /*
+ * Do not need to set clocks related with PLL,
+ * if DPHY_PLL is already stabled because of LCD_ON_UBOOT.
+ */
+ if (dsim_reg_is_pll_stable(id)) {
+ dsim_info("dsim%d DPHY PLL is already stable\n", id);
+ return -EBUSY;
+ }
+
+ /*
+ * set p, m, s to DPHY PLL
+ * PMS value has to be optained by PMS calculation tool
+ * released to customer
+ */
+ pll.p = dphy_pms->p;
+ pll.m = dphy_pms->m;
+ pll.s = dphy_pms->s;
+ pll.k = dphy_pms->k;
+
+ /* get word clock */
+ /* clks ->hs_clk is from DT */
+ clks->word_clk = clks->hs_clk / 16;
+ dsim_dbg("word clock is %u MHz\n", clks->word_clk);
+
+ /* requeseted escape clock */
+ dsim_dbg("requested escape clock %u MHz\n", clks->esc_clk);
+
+ /* escape clock divider */
+ esc_div = clks->word_clk / clks->esc_clk;
+
+ /* adjust escape clock */
+ if ((clks->word_clk / esc_div) > clks->esc_clk)
+ esc_div += 1;
+
+ /* adjusted escape clock */
+ clks->esc_clk = clks->word_clk / esc_div;
+ dsim_dbg("escape clock divider is 0x%x\n", esc_div);
+ dsim_dbg("escape clock is %u MHz\n", clks->esc_clk);
+
+ /* set BIAS ctrl : default value */
+ dsim_reg_set_bias_con(id, DSIM_PHY_BIAS_CON_VAL);
+
+ /* set PLL ctrl : default value */
+ dsim_reg_set_pll_con(id, DSIM_PHY_PLL_CON_VAL);
+
+ if (clks->hs_clk < 1500)
+ hsmode = 1;
+
+ dsim_reg_set_esc_clk_prescaler(id, 1, esc_div);
+ /* get DPHY timing values using hs clock and escape clock */
+ dsim_reg_get_dphy_timing(clks->hs_clk, clks->esc_clk, &t);
+ dsim_reg_set_dphy_timing_values(id, &t, hsmode);
+#if defined(CONFIG_EXYNOS_DSIM_DITHER)
+ /* check dither sequence */
+ dsim_reg_set_dphy_param_dither(id, dphy_pms);
+ dsim_reg_set_dphy_dither_en(id, 1);
+#endif
+
+ /* set clock lane General Control Register control */
+ dsim_reg_set_mc_gnr_con(id, DSIM_PHY_MC_GNR_CON_VAL);
+
+ /* set clock lane Analog Block Control Register control */
+ dsim_reg_set_mc_ana_con(id, DSIM_PHY_MC_ANA_CON_VAL);
+
+#ifdef DPDN_INV_SWAP
+ dsim_reg_set_dpdn_swap(id, 1);
+#endif
+
+ /* set data lane General Control Register control */
+ dsim_reg_set_md_gnr_con(id, DSIM_PHY_MD_GNR_CON_VAL);
+
+#ifdef DPDN_INV_SWAP
+ inv_data[0] = 0;
+ inv_data[1] = 1;
+ inv_data[2] = 1;
+ inv_data[3] = 0;
+ dsim_reg_set_inv_dpdn(id, 0, inv_data);
+#endif
+
+ /* set data lane Analog Block Control Register control */
+ dsim_reg_set_md_ana_con(id, DSIM_PHY_MD_ANA_CON_VAL);
+
+ /* set PMSK on PHY */
+ dsim_reg_set_pll_freq(id, pll.p, pll.m, pll.s, pll.k);
+
+ /*set wclk buf sft cnt */
+ dsim_reg_set_dphy_wclk_buf_sft(id, 3);
+
+ /* set PLL's lock time (lock_cnt) */
+ /* It depends on project guide */
+ dsim_reg_pll_stable_time(id, 0x1450);
+
+#ifdef DPHY_LOOP
+ dsim_reg_set_dphy_loop_test(id);
+#endif
+ /* enable PLL */
+ ret = dsim_reg_enable_pll(id, 1);
+ } else {
+ /* check disable PHY timing */
+ /* TBD */
+ dsim_reg_set_esc_clk_prescaler(id, 0, 0xff);
+#if defined(CONFIG_EXYNOS_DSIM_DITHER)
+ /* check dither sequence */
+ dsim_reg_set_dphy_dither_en(id, 0);
+#endif
+ dsim_reg_enable_pll(id, 0);
+ }
+
+ return ret;
+}
+
+static int dsim_reg_set_lanes(u32 id, u32 lanes, u32 en)
+{
+ /* LINK lanes */
+ dsim_reg_enable_lane(id, lanes, en);
+
+ return 0;
+}
+
+static int dsim_reg_set_lanes_dphy(u32 id, u32 lanes, u32 en)
+{
+ /* PHY lanes */
+ if (dsim_reg_enable_lane_phy(id, (lanes >> 1), en))
+ return -EBUSY;
+
+ return 0;
+}
+
+static u32 dsim_reg_is_noncont_clk_enabled(u32 id)
+{
+ int ret;
+
+ ret = dsim_read_mask(id, DSIM_CLK_CTRL,
+ DSIM_CLK_CTRL_NONCONT_CLOCK_LANE);
+ return ret;
+}
+
+static int dsim_reg_set_hs_clock(u32 id, u32 en)
+{
+ int reg = 0;
+ int is_noncont = dsim_reg_is_noncont_clk_enabled(id);
+
+ if (en) {
+ dsim_reg_enable_hs_clock(id, 1);
+ if (!is_noncont)
+ reg = dsim_reg_wait_hs_clk_ready(id);
+ } else {
+ dsim_reg_enable_hs_clock(id, 0);
+ reg = dsim_reg_wait_hs_clk_disable(id);
+ }
+ return reg;
+}
+
+static void dsim_reg_set_int(u32 id, u32 en)
+{
+ u32 val = en ? 0 : ~0;
+ u32 mask;
+
+ /*
+ * TODO: underrun irq will be unmasked in the future.
+ * underrun irq(dsim_reg_set_config) is ignored in zebu emulator.
+ * it's not meaningful
+ */
+ mask = DSIM_INTMSK_SW_RST_RELEASE | DSIM_INTMSK_SFR_PL_FIFO_EMPTY |
+ DSIM_INTMSK_SFR_PH_FIFO_EMPTY |
+ DSIM_INTMSK_FRAME_DONE | DSIM_INTMSK_INVALID_SFR_VALUE |
+ DSIM_INTMSK_UNDER_RUN | DSIM_INTMSK_RX_DATA_DONE |
+ DSIM_INTMSK_ERR_RX_ECC | DSIM_INTMSK_VT_STATUS;
+
+ dsim_write_mask(id, DSIM_INTMSK, val, mask);
+}
+
+/*
+ * enter or exit ulps mode
+ *
+ * Parameter
+ * 1 : enter ULPS mode
+ * 0 : exit ULPS mode
+ */
+static int dsim_reg_set_ulps(u32 id, u32 en, u32 lanes)
+{
+ int ret = 0;
+
+ if (en) {
+ /* Enable ULPS clock and data lane */
+ dsim_reg_enter_ulps(id, 1);
+
+ /* Check ULPS request for data lane */
+ ret = dsim_reg_wait_enter_ulps_state(id, lanes);
+ if (ret)
+ return ret;
+
+ } else {
+ /* Exit ULPS clock and data lane */
+ dsim_reg_exit_ulps(id, 1);
+
+ ret = dsim_reg_wait_exit_ulps_state(id);
+ if (ret)
+ return ret;
+
+ /* wait at least 1ms : Twakeup time for MARK1 state */
+ udelay(1000);
+
+ /* Clear ULPS exit request */
+ dsim_reg_exit_ulps(id, 0);
+
+ /* Clear ULPS enter request */
+ dsim_reg_enter_ulps(id, 0);
+ }
+
+ return ret;
+}
+
+/*
+ * enter or exit ulps mode for LSI DDI
+ *
+ * Parameter
+ * 1 : enter ULPS mode
+ * 0 : exit ULPS mode
+ * assume that disp block power is off after ulps mode enter
+ */
+static int dsim_reg_set_smddi_ulps(u32 id, u32 en, u32 lanes)
+{
+ int ret = 0;
+
+ if (en) {
+ /* Enable ULPS clock and data lane */
+ dsim_reg_enter_ulps(id, 1);
+
+ /* Check ULPS request for data lane */
+ ret = dsim_reg_wait_enter_ulps_state(id, lanes);
+ if (ret)
+ return ret;
+ /* Clear ULPS enter request */
+ dsim_reg_enter_ulps(id, 0);
+ } else {
+ /* Enable ULPS clock and data lane */
+ dsim_reg_enter_ulps(id, 1);
+
+ /* Check ULPS request for data lane */
+ ret = dsim_reg_wait_enter_ulps_state(id, lanes);
+ if (ret)
+ return ret;
+
+ /* Exit ULPS clock and data lane */
+ dsim_reg_exit_ulps(id, 1);
+
+ ret = dsim_reg_wait_exit_ulps_state(id);
+ if (ret)
+ return ret;
+
+ /* wait at least 1ms : Twakeup time for MARK1 state */
+ udelay(1000);
+
+ /* Clear ULPS enter request */
+ dsim_reg_enter_ulps(id, 0);
+
+ /* Clear ULPS exit request */
+ dsim_reg_exit_ulps(id, 0);
+ }
+
+ return ret;
+}
+
+static int dsim_reg_set_ulps_by_ddi(u32 id, u32 ddi_type, u32 lanes, u32 en)
+{
+ int ret;
+
+ switch (ddi_type) {
+ case TYPE_OF_SM_DDI:
+ ret = dsim_reg_set_smddi_ulps(id, en, lanes);
+ break;
+ case TYPE_OF_MAGNA_DDI:
+ dsim_err("This ddi(%d) doesn't support ULPS\n", ddi_type);
+ ret = -EINVAL;
+ break;
+ case TYPE_OF_NORMAL_DDI:
+ default:
+ ret = dsim_reg_set_ulps(id, en, lanes);
+ break;
+ }
+
+ if (ret < 0)
+ dsim_err("%s: failed to %s ULPS", __func__,
+ en ? "enter" : "exit");
+
+ return ret;
+}
+
+/******************** EXPORTED DSIM CAL APIs ********************/
+
+void dpu_sysreg_select_dphy_rst_control(void __iomem *sysreg, u32 dsim_id, u32 sel)
+{
+ u32 phy_num = dsim_id ? 0 : 1;
+ u32 old = readl(sysreg + DISP_DPU_MIPI_PHY_CON);
+ u32 val = sel ? ~0 : 0;
+ u32 mask = SEL_RESET_DPHY_MASK(phy_num);
+
+ val = (val & mask) | (old & ~mask);
+ writel(val, sysreg + DISP_DPU_MIPI_PHY_CON);
+}
+
+void dpu_sysreg_dphy_reset(void __iomem *sysreg, u32 dsim_id, u32 rst)
+{
+ u32 old = readl(sysreg + DISP_DPU_MIPI_PHY_CON);
+ u32 val = rst ? ~0 : 0;
+ u32 mask = dsim_id ? M_RESETN_M4S4_TOP_MASK : M_RESETN_M4S4_MODULE_MASK;
+
+ val = (val & mask) | (old & ~mask);
+ writel(val, sysreg + DISP_DPU_MIPI_PHY_CON);
+}
+
+static u32 dsim_reg_translate_lanecnt_to_lanes(int lanecnt)
+{
+ u32 lanes, i;
+
+ lanes = DSIM_LANE_CLOCK;
+ for (i = 1; i <= lanecnt; ++i)
+ lanes |= 1 << i;
+
+ return lanes;
+}
+
+void dsim_reg_init(u32 id, struct decon_lcd *lcd_info, struct dsim_clks *clks,
+ bool panel_ctrl)
+{
+ u32 lanes;
+#if !defined(CONFIG_EXYNOS_LCD_ON_UBOOT)
+ struct dsim_device *dsim = get_dsim_drvdata(id);
+#endif
+ if (dsim->state == DSIM_STATE_INIT) {
+ if (dsim_reg_get_link_clock(dsim->id)) {
+ dsim_info("dsim%d is already enabled in bootloader\n", dsim->id);
+ return;
+ }
+ }
+
+ lanes = dsim_reg_translate_lanecnt_to_lanes(lcd_info->data_lane);
+
+ /* choose OSC_CLK */
+ dsim_reg_set_link_clock(id, 0);
+
+ dsim_reg_sw_reset(id);
+
+ dsim_reg_set_lanes(id, lanes, 1);
+
+ dsim_reg_set_esc_clk_on_lane(id, 1, lanes);
+
+ dsim_reg_set_pll_clk_gate_enable(id, 1);
+
+ dsim_reg_enable_word_clock(id, 1);
+
+ /* Enable DPHY reset : DPHY reset start */
+ dpu_sysreg_dphy_reset(dsim->res.ss_regs, id, 0);
+
+#if defined(CONFIG_EXYNOS_LCD_ON_UBOOT)
+ /* TODO: This code will be implemented as uboot style */
+#else
+ /* Panel power on */
+ if (panel_ctrl)
+ dsim_set_panel_power(dsim, 1);
+#endif
+
+ dsim_reg_set_clocks(id, clks, &lcd_info->dphy_pms, 1);
+
+ dsim_reg_set_lanes_dphy(id, lanes, 1);
+ dpu_sysreg_dphy_reset(dsim->res.ss_regs, id, 1); /* Release DPHY reset */
+
+ dsim_reg_sw_reset(id);
+ dsim_reg_set_lanes(id, lanes, 1);
+
+ dsim_reg_set_link_clock(id, 1); /* Selection to word clock */
+
+ dsim_reg_set_config(id, lcd_info, clks);
+
+ dsim_reg_set_pll_clk_gate_enable(id, 0); /* phy clockgate enable */
+ dsim_reg_set_pll_clk_gate_enable(id, 1); /* phy clockgate disable */
+
+#if defined(CONFIG_EXYNOS_LCD_ON_UBOOT)
+ /* TODO: This code will be implemented as uboot style */
+#else
+ if (panel_ctrl)
+ dsim_reset_panel(dsim);
+#endif
+}
+
+/* Set clocks and lanes and HS ready */
+void dsim_reg_start(u32 id)
+{
+ dsim_reg_set_hs_clock(id, 1);
+ dsim_reg_set_int(id, 1);
+}
+
+/* Unset clocks and lanes and stop_state */
+int dsim_reg_stop(u32 id, u32 lanes)
+{
+ int err = 0;
+ u32 is_vm;
+
+ struct dsim_device *dsim = get_dsim_drvdata(id);
+
+ dsim_reg_clear_int(id, 0xffffffff);
+ /* disable interrupts */
+ dsim_reg_set_int(id, 0);
+
+ /* first disable HS clock */
+ if (dsim_reg_set_hs_clock(id, 0) < 0)
+ dsim_err("The CLK lane doesn't be switched to LP mode\n");
+
+ /* 0. wait the IDLE status */
+ is_vm = dsim_reg_get_display_mode(id);
+ err = dsim_reg_wait_idle_status(id, is_vm);
+ if (err < 0)
+ dsim_err("DSIM status is not IDLE!\n");
+
+ /* 1. clock selection : OSC */
+ dsim_reg_set_link_clock(id, 0);
+
+ /* 2. master resetn */
+ dpu_sysreg_dphy_reset(dsim->res.ss_regs, id, 0);
+ /* 3. disable lane */
+ dsim_reg_set_lanes_dphy(id, lanes, 0);
+ /* 4. turn off WORDCLK and ESCCLK */
+ dsim_reg_set_esc_clk_on_lane(id, 0, lanes);
+ dsim_reg_set_esc_clk_en(id, 0);
+ /* 5. disable PLL */
+ dsim_reg_set_clocks(id, NULL, NULL, 0);
+
+ if (err == 0)
+ dsim_reg_sw_reset(id);
+
+ return err;
+}
+
+/* Exit ULPS mode and set clocks and lanes */
+int dsim_reg_exit_ulps_and_start(u32 id, u32 ddi_type, u32 lanes)
+{
+ int ret = 0;
+
+ #if 0
+ /*
+ * Guarantee 1.2v signal level for data lane(positive) when exit ULPS.
+ * DSIM Should be set standby. If not, lane goes to 600mv sometimes.
+ */
+ dsim_reg_set_hs_clock(id, 1);
+ dsim_reg_set_hs_clock(id, 0);
+ #endif
+
+ /* try to exit ULPS mode. The sequence is depends on DDI type */
+ ret = dsim_reg_set_ulps_by_ddi(id, ddi_type, lanes, 0);
+ dsim_reg_start(id);
+ return ret;
+}
+
+/* Unset clocks and lanes and enter ULPS mode */
+int dsim_reg_stop_and_enter_ulps(u32 id, u32 ddi_type, u32 lanes)
+{
+ int ret = 0;
+ u32 is_vm;
+ struct dsim_device *dsim = get_dsim_drvdata(id);
+
+ dsim_reg_clear_int(id, 0xffffffff);
+ /* disable interrupts */
+ dsim_reg_set_int(id, 0);
+
+ /* 1. turn off clk lane & wait for stopstate_clk */
+ ret = dsim_reg_set_hs_clock(id, 0);
+ if (ret < 0)
+ dsim_err("The CLK lane doesn't be switched to LP mode\n");
+
+ /* 2. enter to ULPS & wait for ulps state of clk and data */
+ dsim_reg_set_ulps_by_ddi(id, ddi_type, lanes, 1);
+
+ /* 3. sequence for BLK_DPU off */
+ /* 3.1 wait idle */
+ is_vm = dsim_reg_get_display_mode(id);
+ ret = dsim_reg_wait_idle_status(id, is_vm);
+ if (ret < 0)
+ dsim_err("%s : DSIM_status is not IDLE!\n", __func__);
+ /* 3.2 OSC clock */
+ dsim_reg_set_link_clock(id, 0);
+ /* 3.3 off DPHY */
+ dpu_sysreg_dphy_reset(dsim->res.ss_regs, id, 0);
+ dsim_reg_set_lanes_dphy(id, lanes, 0);
+ dsim_reg_set_clocks(id, NULL, NULL, 0);
+ /* 3.4 sw reset */
+ dsim_reg_sw_reset(id);
+
+ return ret;
+}
+
+int dsim_reg_get_int_and_clear(u32 id)
+{
+ u32 val;
+
+ val = dsim_read(id, DSIM_INTSRC);
+ dsim_reg_clear_int(id, val);
+
+ return val;
+}
+
+void dsim_reg_clear_int(u32 id, u32 int_src)
+{
+ dsim_write(id, DSIM_INTSRC, int_src);
+}
+
+void dsim_reg_wr_tx_header(u32 id, u32 d_id, unsigned long d0, u32 d1, u32 bta)
+{
+ u32 val = DSIM_PKTHDR_BTA_TYPE(bta) | DSIM_PKTHDR_ID(d_id) |
+ DSIM_PKTHDR_DATA0(d0) | DSIM_PKTHDR_DATA1(d1);
+
+ dsim_write_mask(id, DSIM_PKTHDR, val, DSIM_PKTHDR_DATA);
+}
+
+void dsim_reg_wr_tx_payload(u32 id, u32 payload)
+{
+ dsim_write(id, DSIM_PAYLOAD, payload);
+}
+
+u32 dsim_reg_header_fifo_is_empty(u32 id)
+{
+ return dsim_read_mask(id, DSIM_FIFOCTRL, DSIM_FIFOCTRL_EMPTY_PH_SFR);
+}
+
+u32 dsim_reg_is_writable_fifo_state(u32 id)
+{
+ u32 val = dsim_read(id, DSIM_FIFOCTRL);
+
+ if (DSIM_FIFOCTRL_NUMBER_OF_PH_SFR_GET(val) < DSIM_FIFOCTRL_THRESHOLD)
+ return 1;
+ else
+ return 0;
+}
+
+u32 dsim_reg_get_rx_fifo(u32 id)
+{
+ return dsim_read(id, DSIM_RXFIFO);
+}
+
+u32 dsim_reg_rx_fifo_is_empty(u32 id)
+{
+ return dsim_read_mask(id, DSIM_FIFOCTRL, DSIM_FIFOCTRL_EMPTY_RX);
+}
+
+int dsim_reg_rx_err_handler(u32 id, u32 rx_fifo)
+{
+ int ret = 0;
+ u32 err_bit = rx_fifo >> 8; /* Error_Range [23:8] */
+
+ if ((err_bit & MIPI_DSI_ERR_BIT_MASK) == 0) {
+ dsim_dbg("dsim%d, Non error reporting format (rx_fifo=0x%x)\n",
+ id, rx_fifo);
+ return ret;
+ }
+
+ /* Parse error report bit*/
+ if (err_bit & MIPI_DSI_ERR_SOT)
+ dsim_err("SoT error!\n");
+ if (err_bit & MIPI_DSI_ERR_SOT_SYNC)
+ dsim_err("SoT sync error!\n");
+ if (err_bit & MIPI_DSI_ERR_EOT_SYNC)
+ dsim_err("EoT error!\n");
+ if (err_bit & MIPI_DSI_ERR_ESCAPE_MODE_ENTRY_CMD)
+ dsim_err("Escape mode entry command error!\n");
+ if (err_bit & MIPI_DSI_ERR_LOW_POWER_TRANSMIT_SYNC)
+ dsim_err("Low-power transmit sync error!\n");
+ if (err_bit & MIPI_DSI_ERR_HS_RECEIVE_TIMEOUT)
+ dsim_err("HS receive timeout error!\n");
+ if (err_bit & MIPI_DSI_ERR_FALSE_CONTROL)
+ dsim_err("False control error!\n");
+ if (err_bit & MIPI_DSI_ERR_ECC_SINGLE_BIT)
+ dsim_err("ECC error, single-bit(detected and corrected)!\n");
+ if (err_bit & MIPI_DSI_ERR_ECC_MULTI_BIT)
+ dsim_err("ECC error, multi-bit(detected, not corrected)!\n");
+ if (err_bit & MIPI_DSI_ERR_CHECKSUM)
+ dsim_err("Checksum error(long packet only)!\n");
+ if (err_bit & MIPI_DSI_ERR_DATA_TYPE_NOT_RECOGNIZED)
+ dsim_err("DSI data type not recognized!\n");
+ if (err_bit & MIPI_DSI_ERR_VCHANNEL_ID_INVALID)
+ dsim_err("DSI VC ID invalid!\n");
+ if (err_bit & MIPI_DSI_ERR_INVALID_TRANSMIT_LENGTH)
+ dsim_err("Invalid transmission length!\n");
+
+ dsim_err("dsim%d, (rx_fifo=0x%x) Check DPHY values about HS clk.\n",
+ id, rx_fifo);
+ return -EINVAL;
+}
+
+void dsim_reg_enable_shadow_read(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_SFR_CTRL, val,
+ DSIM_SFR_CTRL_SHADOW_REG_READ_EN);
+}
+
+void dsim_reg_function_reset(u32 id)
+{
+ u32 cnt = 1000;
+ u32 state;
+
+ dsim_write_mask(id, DSIM_SWRST, ~0, DSIM_SWRST_FUNCRST);
+
+ do {
+ state = dsim_read(id, DSIM_SWRST) & DSIM_SWRST_FUNCRST;
+ cnt--;
+ udelay(10);
+ } while (state && cnt);
+
+ if (!cnt)
+ dsim_err("%s is timeout.\n", __func__);
+
+}
+
+/* Set porch and resolution to support Partial update */
+void dsim_reg_set_partial_update(u32 id, struct decon_lcd *lcd_info)
+{
+ dsim_reg_set_vresol(id, lcd_info->yres);
+ dsim_reg_set_hresol(id, lcd_info->xres, lcd_info);
+ dsim_reg_set_porch(id, lcd_info);
+ dsim_reg_set_num_of_transfer(id, lcd_info->yres);
+}
+
+void dsim_reg_set_mres(u32 id, struct decon_lcd *lcd_info)
+{
+ u32 threshold;
+ u32 num_of_slice;
+ u32 num_of_transfer;
+ int idx;
+
+ if (lcd_info->mode != DECON_MIPI_COMMAND_MODE) {
+ dsim_info("%s: mode[%d] doesn't support multi resolution\n",
+ __func__, lcd_info->mode);
+ return;
+ }
+
+ idx = lcd_info->mres_mode;
+ dsim_reg_set_cm_underrun_lp_ref(id, lcd_info->cmd_underrun_lp_ref[idx]);
+
+ if (lcd_info->dsc_enabled) {
+ threshold = lcd_info->xres / 3;
+ num_of_transfer = lcd_info->xres * lcd_info->yres / threshold / 3;
+ } else {
+ threshold = lcd_info->xres;
+ num_of_transfer = lcd_info->xres * lcd_info->yres / threshold;
+ }
+
+ dsim_reg_set_threshold(id, threshold);
+ dsim_reg_set_vresol(id, lcd_info->yres);
+ dsim_reg_set_hresol(id, lcd_info->xres, lcd_info);
+ dsim_reg_set_porch(id, lcd_info);
+ dsim_reg_set_num_of_transfer(id, num_of_transfer);
+
+ dsim_reg_enable_dsc(id, lcd_info->dsc_enabled);
+ if (lcd_info->dsc_enabled) {
+ dsim_dbg("%s: dsc configuration is set\n", __func__);
+ dsim_reg_set_num_of_slice(id, lcd_info->dsc_slice_num);
+ dsim_reg_set_multi_slice(id, lcd_info); /* multi slice */
+ dsim_reg_set_size_of_slice(id, lcd_info);
+
+ dsim_reg_get_num_of_slice(id, &num_of_slice);
+ dsim_dbg("dsim%d: number of DSC slice(%d)\n", id, num_of_slice);
+ dsim_reg_print_size_of_slice(id);
+ }
+}
+
+void dsim_reg_set_bist(u32 id, u32 en)
+{
+ if (en) {
+ dsim_reg_set_bist_te_interval(id, 4505);
+ dsim_reg_set_bist_mode(id, DSIM_GRAY_GRADATION);
+ dsim_reg_enable_bist_pattern_move(id, true);
+ dsim_reg_enable_bist(id, en);
+ }
+}
+
+void dsim_reg_set_cmd_transfer_mode(u32 id, u32 lp)
+{
+ u32 val = lp ? ~0 : 0;
+
+ dsim_write_mask(id, DSIM_ESCMODE, val, DSIM_ESCMODE_CMD_LPDT);
+}
--- /dev/null
+/*
+ * linux/drivers/video/fbdev/exynos/dpu_9810/regs_decon.h
+ *
+ * Register definition file for Samsung DECON driver
+ *
+ * Copyright (c) 2014 Samsung Electronics
+ * SeungBeom park<sb1.park@samsung.com>
+ *
+ * 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 _REGS_DISP_SS_H
+#define _REGS_DISP_SS_H
+
+#define DISP_DPU_MIPI_PHY_CON 0x0008
+/* _v : [0,1] */
+#define SEL_RESET_DPHY_MASK(_v) (0x1 << (4 + (_v)))
+#define M_RESETN_M4S4_MODULE_MASK (0x1 << 1)
+#define M_RESETN_M4S4_TOP_MASK (0x1 << 0)
+
+#define DISP_DPU_TE_QACTIVE_PLL_EN 0x0010
+#define TE_QACTIVE_PLL_EN (0x1 << 0)
+
+#endif /* _REGS_DISP_SS_H */
+
+#ifndef _REGS_DECON_H
+#define _REGS_DECON_H
+
+/*
+ * [ BLK_DPU BASE ADDRESS ]
+ *
+ * - CMU_DPU 0x1900_0000
+ * - SYSREG_DPU 0x1901_0000
+ * - DPP 0x1902_0000
+ * - DECON0 0x1903_0000
+ * - DECON1 0x1904_0000
+ * - DECON2 0x1905_0000
+ * - DPU_WB_MUX 0x1906_0000
+ * - DPU_DMA 0x1907_0000
+ * - MIPI_DSIM0 0x1908_0000
+ * - MIPI_DSIM1 0x1909_0000
+ * - SYSMMU_DPUD0 0x190A_0000
+ * - SYSMMU_DPUD1 0x190B_0000
+ * - SYSMMU_DPUD2 0x190C_0000
+ * - SYSMMU_DPUD0_S 0x190D_0000
+ * - SYSMMU_DPUD1_S 0x190E_0000
+ * - SYSMMU_DPUD2_S 0x190F_0000
+ * - PPMU_DPUD0 0x1910_0000
+ * - PPMU_DPUD1 0x1911_0000
+ * - PPMU_DPUD2 0x1912_0000
+ * - BTM_DPUD0 0x1913_0000
+ * - BTM_DPUD1 0x1914_0000
+ * - BTM_DPUD2 0x1915_0000
+ * - MIPI_DCPHY 0x1916_0000
+ * - DPU_PGEN 0x191A_0000
+ * - HDR 0x191C_0000
+ */
+
+/*
+ * IP start_offset end_offset
+ *=================================================
+ * DECON 0x0000 0x0fff
+ * BLENDER 0x1000 0x1fff
+ *-------------------------------------------------
+ * DSC0 0x4000 0x4fff
+ * DSC1 0x5000 0x5fff
+ * DSC2 0x6000 0x6fff
+ *-------------------------------------------------
+ * SHD_DECON 0x7000 0x7FFF
+ *-------------------------------------------------
+ * SHD_BLENDER 0x8000 0x8FFF
+ *-------------------------------------------------
+ * SHD_DSC0 0xB000 0xBFFF
+ * SHD_DSC1 0xC000 0xCFFF
+ * SHD_DSC2 0xD000 0xCFFF
+ *-------------------------------------------------
+ */
+
+
+/*
+ * DECON registers
+ * ->
+ * updated by SHADOW_REG_UPDATE_REQ[31] : SHADOW_REG_UPDATE_REQ
+ * (0x0000~0x011C, 0x0230~0x209C )
+ */
+
+#define GLOBAL_CONTROL 0x0000
+#define GLOBAL_CONTROL_SRESET (1 << 28)
+#define GLOBAL_CONTROL_PSLVERR_EN (1 << 24)
+#define GLOBAL_CONTROL_TEN_BPC_MODE_F (1 << 20)
+#define GLOBAL_CONTROL_TEN_BPC_MODE_MASK (1 << 20)
+#define GLOBAL_CONTROL_MASTER_MODE_F(_v) ((_v) << 12)
+#define GLOBAL_CONTROL_MASTER_MODE_MASK (0xF << 12)
+#define GLOBAL_CONTROL_OPERATION_MODE_F (1 << 8)
+#define GLOBAL_CONTROL_OPERATION_MODE_VIDEO_F (0 << 8)
+#define GLOBAL_CONTROL_OPERATION_MODE_CMD_F (1 << 8)
+#define GLOBAL_CONTROL_IDLE_STATUS (1 << 5)
+#define GLOBAL_CONTROL_RUN_STATUS (1 << 4)
+#define GLOBAL_CONTROL_DECON_EN (1 << 1)
+#define GLOBAL_CONTROL_DECON_EN_F (1 << 0)
+
+#define RESOURCE_OCCUPANCY_INFO_0 0x0010
+#define RESOURCE_OCCUPANCY_INFO_1 0x0014
+#define RESOURCE_OCCUPANCY_INFO_2 0x0018
+
+#define SRAM_SHARE_ENABLE 0x0030
+#define SRAM0_SHARE_ENABLE_F (1 << 12)
+#define SRAM1_SHARE_ENABLE_F (1 << 16)
+#define SRAM2_SHARE_ENABLE_F (1 << 20)
+#define SRAM3_SHARE_ENABLE_F (1 << 24)
+#define ALL_SRAM_SHARE_ENABLE (0x1111 << 12)
+#define ALL_SRAM_SHARE_DISABLE (0x0)
+
+#define INTERRUPT_ENABLE 0x0040
+#define DPU_DQE_DIMMING_END_INT_EN (1 << 21)
+#define DPU_DQE_DIMMING_START_INT_EN (1 << 20)
+#define DPU_FRAME_DONE_INT_EN (1 << 13)
+#define DPU_FRAME_START_INT_EN (1 << 12)
+#define DPU_EXTRA_INT_EN (1 << 4)
+#define DPU_INT_EN (1 << 0)
+#define INTERRUPT_ENABLE_MASK 0x3011
+
+#define EXTRA_INTERRUPT_ENABLE 0x0044
+#define DPU_RESOURCE_CONFLICT_INT_EN (1 << 8)
+#define DPU_TIME_OUT_INT_EN (1 << 4)
+
+#define TIME_OUT_VALUE 0x0048
+
+#define INTERRUPT_PENDING 0x004C
+#define DPU_DQE_DIMMING_END_INT_PEND (1 << 21)
+#define DPU_DQE_DIMMING_START_INT_PEND (1 << 20)
+#define DPU_FRAME_DONE_INT_PEND (1 << 13)
+#define DPU_FRAME_START_INT_PEND (1 << 12)
+#define DPU_EXTRA_INT_PEND (1 << 4)
+
+#define EXTRA_INTERRUPT_PENDING 0x0050
+#define DPU_RESOURCE_CONFLICT_INT_PEND (1 << 8)
+#define DPU_TIME_OUT_INT_PEND (1 << 4)
+
+#define SHADOW_REG_UPDATE_REQ 0x0060
+#define SHADOW_REG_UPDATE_REQ_GLOBAL (1 << 31)
+#define SHADOW_REG_UPDATE_REQ_DQE (1 << 28)
+#define SHADOW_REG_UPDATE_REQ_WIN(_win) (1 << (_win))
+#define SHADOW_REG_UPDATE_REQ_FOR_DECON (0x3f)
+
+#define SECURE_CONTROL 0x0064
+#define TZPC_FLAG_WIN(_win) (1 << (_win))
+
+#define HW_SW_TRIG_CONTROL 0x0070
+#define HW_SW_TRIG_HS_STATUS (1 << 28)
+#define HW_TRIG_SEL(_v) ((_v) << 24)
+#define HW_TRIG_SEL_MASK (0x3 << 24)
+#define HW_TRIG_SEL_FROM_DDI1 (1 << 24)
+#define HW_TRIG_SEL_FROM_DDI0 (0 << 24)
+#define HW_TRIG_SKIP(_v) ((_v) << 16)
+#define HW_TRIG_SKIP_MASK (0xff << 16)
+#define HW_TRIG_ACTIVE_VALUE (1 << 13)
+#define HW_TRIG_EDGE_POLARITY (1 << 12)
+#define SW_TRIG_EN (1 << 8)
+#define HW_TRIG_MASK_SLAVE0 (1 << 5)
+#define HW_TRIG_MASK_DECON (1 << 4)
+#define HW_SW_TRIG_TIMER_CLEAR (1 << 3)
+#define HW_SW_TRIG_TIMER_EN (1 << 2)
+#define HW_TRIG_EN (1 << 0)
+
+#define HW_SW_TRIG_TIMER 0x0074
+
+#define HW_TE_CNT 0x0078
+#define HW_TRIG_CNT_B_GET(_v) (((_v) >> 16) & 0xffff)
+#define HW_TRIG_CNT_A_GET(_v) (((_v) >> 0) & 0xffff)
+
+#define HW_SW_TRIG_CONTROL_SECURE 0x007C
+#define HW_TRIG_MASK_SECURE_SLAVE1 (1 << 6)
+#define HW_TRIG_MASK_SECURE_SLAVE0 (1 << 5)
+#define HW_TRIG_MASK_SECURE (1 << 4)
+
+#define PLL_SLEEP_CONTROL 0x0090
+#define PLL_SLEEP_MASK_OUTIF1 (1 << 5)
+#define PLL_SLEEP_MASK_OUTIF0 (1 << 4)
+#define PLL_SLEEP_EN_OUTIF1_F (1 << 1)
+#define PLL_SLEEP_EN_OUTIF0_F (1 << 0)
+
+#define SCALED_SIZE_CONTROL_0 0x00A0
+#define SCALED_SIZE_HEIGHT_F(_v) ((_v) << 16)
+#define SCALED_SIZE_HEIGHT_MASK (0x3fff << 16)
+#define SCALED_SIZE_HEIGHT_GET(_v) (((_v) >> 16) & 0x3fff)
+#define SCALED_SIZE_WIDTH_F(_v) ((_v) << 0)
+#define SCALED_SIZE_WIDTH_MASK (0x3fff << 0)
+#define SCALED_SIZE_WIDTH_GET(_v) (((_v) >> 0) & 0x3fff)
+
+#define CLOCK_CONTROL_0 0x00F0
+/*
+ * [28] QACTIVE_PLL_VALUE = 0
+ * [24] QACTIVE_VALUE = 0
+ * 0: QACTIVE is dynamically changed by DECON h/w,
+ * 1: QACTIVE is stuck to 1'b1
+ * [16][12][8][4][0] AUTO_CG_EN_xxx
+ */
+/* clock gating is disabled on bringup */
+#define CLOCK_CONTROL_0_CG_MASK (0x11111 << 0)
+#define CLOCK_CONTROL_0_QACTIVE_MASK ((0x1 << 24) | (0x1 << 28))
+#define CLOCK_CONTROL_0_TE_QACTIVE_PLL_ON (0x1 << 28)
+
+#define SPLITTER_SIZE_CONTROL_0 0x0100
+#define SPLITTER_HEIGHT_F(_v) ((_v) << 16)
+#define SPLITTER_HEIGHT_MASK (0x3fff << 16)
+#define SPLITTER_HEIGHT_GET(_v) (((_v) >> 16) & 0x3fff)
+#define SPLITTER_WIDTH_F(_v) ((_v) << 0)
+#define SPLITTER_WIDTH_MASK (0x3fff << 0)
+#define SPLITTER_WIDTH_GET(_v) (((_v) >> 0) & 0x3fff)
+
+#define SPLITTER_SPLIT_IDX_CONTROL 0x0104
+#define SPLITTER_SPLIT_IDX_F(_v) ((_v) << 0)
+#define SPLITTER_SPLIT_IDX_MASK (0x3fff << 0)
+#define SPLITTER_OVERLAP_F(_v) ((_v) << 16)
+#define SPLITTER_OVERLAP_MASK (0x7f << 16)
+
+#define OUTFIFO_SIZE_CONTROL_0 0x0120
+#define OUTFIFO_HEIGHT_F(_v) ((_v) << 16)
+#define OUTFIFO_HEIGHT_MASK (0x3fff << 16)
+#define OUTFIFO_HEIGHT_GET(_v) (((_v) >> 16) & 0x3fff)
+#define OUTFIFO_WIDTH_F(_v) ((_v) << 0)
+#define OUTFIFO_WIDTH_MASK (0x3fff << 0)
+#define OUTFIFO_WIDTH_GET(_v) (((_v) >> 0) & 0x3fff)
+
+#define OUTFIFO_SIZE_CONTROL_1 0x0124
+#define OUTFIFO_1_WIDTH_F(_v) ((_v) << 0)
+#define OUTFIFO_1_WIDTH_MASK (0x3fff << 0)
+#define OUTFIFO_1_WIDTH_GET(_v) (((_v) >> 0) & 0x3fff)
+
+#define OUTFIFO_SIZE_CONTROL_2 0x0128
+#define OUTFIFO_COMPRESSED_SLICE_HEIGHT_F(_v) ((_v) << 16)
+#define OUTFIFO_COMPRESSED_SLICE_HEIGHT_MASK (0x3fff << 16)
+#define OUTFIFO_COMPRESSED_SLICE_HEIGHT_GET(_v) (((_v) >> 16) & 0x3fff)
+#define OUTFIFO_COMPRESSED_SLICE_WIDTH_F(_v) ((_v) << 0)
+#define OUTFIFO_COMPRESSED_SLICE_WIDTH_MASK (0x3fff << 0)
+#define OUTFIFO_COMPRESSED_SLICE_WIDTH_GET(_v) (((_v) >> 0) & 0x3fff)
+
+#define OUTFIFO_TH_CONTROL_0 0x012C
+#define OUTFIFO_TH_1H_F (0x5 << 0)
+#define OUTFIFO_TH_2H_F (0x6 << 0)
+#define OUTFIFO_TH_F(_v) ((_v) << 0)
+#define OUTFIFO_TH_MASK (0x7 << 0)
+#define OUTFIFO_TH_GET(_v) ((_v) >> 0 & 0x7)
+
+#define OUTFIFO_DATA_ORDER_CONTROL 0x0130
+#define OUTFIFO_PIXEL_ORDER_SWAP_F(_v) ((_v) << 4)
+#define OUTFIFO_PIXEL_ORDER_SWAP_MASK (0x7 << 4)
+#define OUTFIFO_PIXEL_ORDER_SWAP_GET(_v) (((_v) >> 4) & 0x7)
+
+#define READ_URGENT_CONTROL_0 0x0140
+#define READ_URGETN_GENERATION_EN_F (0x1 << 0)
+
+#define READ_URGENT_CONTROL_1 0x0144
+#define READ_URGENT_HIGH_THRESHOLD_F(_v) ((_v) << 16)
+#define READ_URGENT_HIGH_THRESHOLD_MASK (0xffff << 16)
+#define READ_URGENT_HIGH_THRESHOLD_GET(_v) (((_v) >> 16) & 0xffff)
+#define READ_URGENT_LOW_THRESHOLD_F(_v) ((_v) << 0)
+#define READ_URGENT_LOW_THRESHOLD_MASK (0xffff << 0)
+#define READ_URGENT_LOW_THRESHOLD_GET(_v) (((_v) >> 0) & 0xffff)
+
+#define READ_URGENT_CONTROL_2 0x0148
+#define READ_URGENT_WAIT_CYCLE_F(_v) ((_v) << 0)
+#define READ_URGENT_WAIT_CYCLE_GET(_v) ((_v) >> 0)
+
+#define DTA_CONTROL 0x0180
+#define DTA_EN_F (1 << 0)
+
+#define DTA_THRESHOLD 0x0184
+#define DTA_HIGH_TH_F(_v) ((_v) << 16)
+#define DTA_HIGH_TH_MASK (0xffff << 16)
+#define DTA_HIGH_TH_GET(_v) (((_v) >> 16) & 0xffff)
+#define DTA_LOW_TH_F(_v) ((_v) << 0)
+#define DTA_LOW_TH_MASK (0xffff << 0)
+#define DTA_LOW_TH_GET(_v) (((_v) >> 0) & 0xffff)
+
+#define BLENDER_BG_IMAGE_SIZE_0 0x0200
+#define BLENDER_BG_HEIGHT_F(_v) ((_v) << 16)
+#define BLENDER_BG_HEIGHT_MASK (0x3fff << 16)
+#define BLENDER_BG_HEIGHT_GET(_v) (((_v) >> 16) & 0x3fff)
+#define BLENDER_BG_WIDTH_F(_v) ((_v) << 0)
+#define BLENDER_BG_WIDTH_MASK (0x3fff << 0)
+#define BLENDER_BG_WIDTH_GET(_v) (((_v) >> 0) & 0x3fff)
+
+#define BLENDER_BG_IMAGE_COLOR_0 0x0208
+#define BLENDER_BG_A_F(_v) ((_v) << 16)
+#define BLENDER_BG_A_MASK (0xff << 16)
+#define BLENDER_BG_A_GET(_v) (((_v) >> 16) & 0xff)
+#define BLENDER_BG_R_F(_v) ((_v) << 0)
+#define BLENDER_BG_R_MASK (0x3ff << 0)
+#define BLENDER_BG_R_GET(_v) (((_v) >> 0) & 0x3ff)
+
+#define BLENDER_BG_IMAGE_COLOR_1 0x020C
+#define BLENDER_BG_G_F(_v) ((_v) << 16)
+#define BLENDER_BG_G_MASK (0x3ff << 16)
+#define BLENDER_BG_G_GET(_v) (((_v) >> 16) & 0x3ff)
+#define BLENDER_BG_B_F(_v) ((_v) << 0)
+#define BLENDER_BG_B_MASK (0x3ff << 0)
+#define BLENDER_BG_B_GET(_v) (((_v) >> 0) & 0x3ff)
+
+#define LRMERGER_MODE_CONTROL 0x0210
+#define LRM23_MODE_F(_v) ((_v) << 16)
+#define LRM23_MODE_MASK (0x7 << 16)
+#define LRM01_MODE_F(_v) ((_v) << 0)
+#define LRM01_MODE_MASK (0x7 << 0)
+
+#define DATA_PATH_CONTROL_0 0x0214
+#define WIN_MAPCOLOR_EN_F(_win) (1 << (4*_win + 1))
+#define WIN_EN_F(_win) (1 << (4*_win + 0))
+
+#define DATA_PATH_CONTROL_1 0x0218
+#define WIN_CHMAP_F(_win, _ch) (((_ch) & 0x7) << (4*_win))
+#define WIN_CHMAP_MASK(_win) (0x7 << (4*_win))
+
+#define DATA_PATH_CONTROL_2 0x0230
+#define SCALER_PATH_F(_v) ((_v) << 24)
+#define SCALER_PATH_MASK (0x3 << 24)
+#define SCALER_PATH_GET(_v) (((_v) >> 24) & 0x3)
+#define EHNANCE_PATH_F(_v) ((_v) << 12)
+#define EHNANCE_PATH_MASK (0x7 << 12)
+#define EHNANCE_PATH_GET(_v) (((_v) >> 12) & 0x7)
+#define COMP_OUTIF_PATH_F(_v) ((_v) << 0)
+#define COMP_OUTIF_PATH_MASK (0xff << 0)
+#define COMP_OUTIF_PATH_GET(_v) (((_v) >> 0) & 0xff)
+
+#define DSIM_CONNECTION_CONTROL 0x0250
+#define DSIM_CONNECTION_DSIM1_F(_v) ((_v) << 4)
+#define DSIM_CONNECTION_DSIM1_MASK (0x7 << 4)
+#define DSIM_CONNECTION_DSIM1_GET(_v) (((_v) >> 4) & 0x7)
+#define DSIM_CONNECTION_DSIM0_F(_v) ((_v) << 0)
+#define DSIM_CONNECTION_DSIM0_MASK (0x7 << 0)
+#define DSIM_CONNECTION_DSIM0_GET(_v) (((_v) >> 0) & 0x7)
+
+#define DSIM0_TE_TIMEOUT_CONTROL 0x0254
+#define DSIM0_TE_TIMEOUT_CNT(_v) ((_v) << 0)
+#define DSIM0_TE_TIMEOUT_CNT_MASK (0xffff << 0)
+#define DSIM0_TE_TIMEOUT_CNT_GET(_v) (((_v) >> 0) & 0xffff)
+
+#define DSIM1_TE_TIMEOUT_CONTROL 0x0258
+#define DSIM1_TE_TIMEOUT_CNT(_v) ((_v) << 0)
+#define DSIM1_TE_TIMEOUT_CNT_MASK (0xffff << 0)
+#define DSIM1_TE_TIMEOUT_CNT_GET(_v) (((_v) >> 0) & 0xffff)
+
+#define DSIM0_START_TIME_CONTROL 0x0260
+#define DSIM0_START_TIME(_v) ((_v) << 0)
+
+#define DSIM1_START_TIME_CONTROL 0x0264
+#define DSIM1_START_TIME(_v) ((_v) << 0)
+
+#define DP_CONNECTION_CONTROL 0x0270
+#define DP_CONNECTION_SEL_DP1(_v) ((_v) << 4)
+#define DP_CONNECTION_SEL_DP1_MASK (0x7 << 4)
+#define DP_CONNECTION_SEL_DP1_GET(_v) (((_v) >> 4) & 0x7)
+#define DP_CONNECTION_SEL_DP0(_v) ((_v) << 0)
+#define DP_CONNECTION_SEL_DP0_MASK (0x7 << 0)
+#define DP_CONNECTION_SEL_DP0_GET(_v) (((_v) >> 0) & 0x7)
+
+/* DECON CRC for ASB */
+#define CRC_DATA_0 0x0280
+#define CRC_DATA_DSIMIF1_GET(_v) (((_v) >> 16) & 0xffff)
+#define CRC_DATA_DSIMIF0_GET(_v) (((_v) >> 0) & 0xffff)
+
+#define CRC_DATA_2 0x0288
+#define CRC_DATA_DP1_GET(_v) (((_v) >> 16) & 0xffff)
+#define CRC_DATA_DP0_GET(_v) (((_v) >> 0) & 0xffff)
+
+#define CRC_CONTROL 0x028C
+#define CRC_COLOR_SEL(_v) ((_v) << 16)
+#define CRC_COLOR_SEL_MASK (0x3 << 16)
+#define CRC_START (1 << 0)
+
+#define FRAME_COUNT 0x02A0
+
+/* BLENDER */
+#define WIN_CONTROL_0(_win) (0x1000 + ((_win) * 0x30))
+#define WIN_ALPHA1_F(_v) (((_v) & 0xFF) << 24)
+#define WIN_ALPHA1_MASK (0xFF << 24)
+#define WIN_ALPHA0_F(_v) (((_v) & 0xFF) << 16)
+#define WIN_ALPHA0_MASK (0xFF << 16)
+#define WIN_ALPHA_GET(_v, _n) (((_v) >> (16 + 8 * (_n))) & 0xFF)
+#define WIN_FUNC_F(_v) (((_v) & 0xF) << 8)
+#define WIN_FUNC_MASK (0xF << 8)
+#define WIN_FUNC_GET(_v) (((_v) >> 8) & 0xf)
+#define WIN_SRESET (1 << 4)
+#define WIN_ALPHA_MULT_SRC_SEL_F(_v) (((_v) & 0x3) << 0)
+#define WIN_ALPHA_MULT_SRC_SEL_MASK (0x3 << 0)
+
+#define WIN_CONTROL_1(_win) (0x1004 + ((_win) * 0x30))
+#define WIN_FG_ALPHA_D_SEL_F(_v) (((_v) & 0xF) << 24)
+#define WIN_FG_ALPHA_D_SEL_MASK (0xF << 24)
+#define WIN_BG_ALPHA_D_SEL_F(_v) (((_v) & 0xF) << 16)
+#define WIN_BG_ALPHA_D_SEL_MASK (0xF << 16)
+#define WIN_FG_ALPHA_A_SEL_F(_v) (((_v) & 0xF) << 8)
+#define WIN_FG_ALPHA_A_SEL_MASK (0xF << 8)
+#define WIN_BG_ALPHA_A_SEL_F(_v) (((_v) & 0xF) << 0)
+#define WIN_BG_ALPHA_A_SEL_MASK (0xF << 0)
+
+#define WIN_START_POSITION(_win) (0x1008 + ((_win) * 0x30))
+#define WIN_STRPTR_Y_F(_v) (((_v) & 0x3FFF) << 16)
+#define WIN_STRPTR_X_F(_v) (((_v) & 0x3FFF) << 0)
+
+#define WIN_END_POSITION(_win) (0x100C + ((_win) * 0x30))
+#define WIN_ENDPTR_Y_F(_v) (((_v) & 0x3FFF) << 16)
+#define WIN_ENDPTR_X_F(_v) (((_v) & 0x3FFF) << 0)
+
+#define WIN_COLORMAP_0(_win) (0x1010 + ((_win) * 0x30))
+#define WIN_MAPCOLOR_A_F(_v) ((_v) << 16)
+#define WIN_MAPCOLOR_A_MASK (0xff << 16)
+#define WIN_MAPCOLOR_R_F(_v) ((_v) << 0)
+#define WIN_MAPCOLOR_R_MASK (0x3ff << 0)
+
+#define WIN_COLORMAP_1(_win) (0x1014 + ((_win) * 0x30))
+#define WIN_MAPCOLOR_G_F(_v) ((_v) << 16)
+#define WIN_MAPCOLOR_G_MASK (0x3ff << 16)
+#define WIN_MAPCOLOR_B_F(_v) ((_v) << 0)
+#define WIN_MAPCOLOR_B_MASK (0x3ff << 0)
+
+#define WIN_START_TIME_CONTROL(_win) (0x1018 + ((_win) * 0x30))
+#define WIN_START_TIME_CONTROL_F(_v) ((_v) << 0)
+#define WIN_START_TIME_CONTROL_MASK (0x3fff << 0)
+
+/*
+ * DSC registers
+ * ->
+ * 0x4000 ~
+ * DSC 0 : 0x4000
+ * DSC 1 : 0x5000
+ * DSC 2 : 0x6000
+ *
+ * <-
+ * DSC registers
+ */
+
+#define DSC0_OFFSET 0x4000
+#define DSC1_OFFSET 0x5000
+#define DSC2_OFFSET 0x6000
+
+#define DSC_CONTROL0 0x0000
+#define DSC_SW_RESET (0x1 << 28)
+#define DSC_DCG_EN_REF(_v) ((_v) << 19)
+#define DSC_DCG_EN_SSM(_v) ((_v) << 18)
+#define DSC_DCG_EN_ICH(_v) ((_v) << 17)
+#define DSC_DCG_EN_ALL_OFF (0x0 << 17)
+#define DSC_DCG_EN_ALL_MASK (0x7 << 17)
+#define DSC_BIT_SWAP(_v) ((_v) << 10)
+#define DSC_BYTE_SWAP(_v) ((_v) << 9)
+#define DSC_WORD_SWAP(_v) ((_v) << 8)
+#define DSC_SWAP(_b, _c, _w) ((_b << 10) |\
+ (_c << 9) | (_w << 8))
+#define DSC_SWAP_MASK ((1 << 10) |\
+ (1 << 9) | (1 << 8))
+#define DSC_FLATNESS_DET_TH_MASK (0xf << 4)
+#define DSC_FLATNESS_DET_TH_F(_v) ((_v) << 4)
+#define DSC_SLICE_MODE_CH_MASK (0x1 << 3)
+#define DSC_SLICE_MODE_CH_F(_v) ((_v) << 3)
+#define DSC_CG_EN_MASK (0x1 << 1)
+#define DSC_CG_EN_F(_v) ((_v) << 1)
+#define DSC_DUAL_SLICE_EN_MASK (0x1 << 0)
+#define DSC_DUAL_SLICE_EN_F(_v) ((_v) << 0)
+
+#define DSC_CONTROL3 0x000C
+#define DSC_REMAINDER_F(_v) ((_v) << 12)
+#define DSC_REMAINDER_MASK (0x3 << 12)
+#define DSC_REMAINDER_GET(_v) (((_v) >> 12) & 0x3)
+#define DSC_GRPCNTLINE_F(_v) ((_v) << 0)
+#define DSC_GRPCNTLINE_MASK (0x7ff << 0)
+#define DSC_GRPCNTLINE_GET(_v) (((_v) >> 0) & 0x7ff)
+
+#define DSC_CRC_0 0x0010
+#define DSC_CRC_EN_MASK (0x1 << 16)
+#define DSC_CRC_EN_F(_v) ((_v) << 16)
+#define DSC_CRC_CODE_MASK (0xffff << 0)
+#define DSC_CRC_CODE_F(_v) ((_v) << 0)
+
+#define DSC_CRC_1 0x0014
+#define DSC_CRC_Y_S0_MASK (0xffff << 16)
+#define DSC_CRC_Y_S0_F(_v) ((_v) << 16)
+#define DSC_CRC_CO_S0_MASK (0xffff << 0)
+#define DSC_CRC_CO_S0_F(_v) ((_v) << 0)
+
+#define DSC_CRC_2 0x0018
+#define DSC_CRC_CG_S0_MASK (0xffff << 16)
+#define DSC_CRC_CG_S0_F(_v) ((_v) << 16)
+#define DSC_CRC_Y_S1_MASK (0xffff << 0)
+#define DSC_CRC_Y_S1_F(_v) ((_v) << 0)
+
+#define DSC_CRC_3 0x001C
+#define DSC_CRC_CO_S1_MASK (0xffff << 16)
+#define DSC_CRC_CO_S1_F(_v) ((_v) << 16)
+#define DSC_CRC_CG_S1_MASK (0xffff << 0)
+#define DSC_CRC_CG_S1_F(_v) ((_v) << 0)
+
+#define DSC_PPS00_03 0x0020
+#define PPS00_VER(_v) ((_v) << 24)
+#define PPS00_VER_MASK (0xff << 24)
+#define PPS01_ID(_v) (_v << 16)
+#define PPS01_ID_MASK (0xff << 16)
+#define PPS03_BPC_LBD_MASK (0xffff << 0)
+#define PPS03_BPC_LBD(_v) (_v << 0)
+
+#define DSC_PPS04_07 0x0024
+#define PPS04_COMP_CFG(_v) ((_v) << 24)
+#define PPS04_COMP_CFG_MASK (0x3f << 24)
+#define PPS05_BPP(_v) (_v << 16)
+#define PPS05_BPP_MASK (0xff << 16)
+#define PPS06_07_PIC_HEIGHT_MASK (0xffff << 0)
+#define PPS06_07_PIC_HEIGHT(_v) (_v << 0)
+
+#define DSC_PPS08_11 0x0028
+#define PPS08_09_PIC_WIDHT_MASK (0xffff << 16)
+#define PPS08_09_PIC_WIDHT(_v) ((_v) << 16)
+#define PPS10_11_SLICE_HEIGHT_MASK (0xffff << 0)
+#define PPS10_11_SLICE_HEIGHT(_v) (_v << 0)
+
+#define DSC_PPS12_15 0x002C
+#define PPS12_13_SLICE_WIDTH_MASK (0xffff << 16)
+#define PPS12_13_SLICE_WIDTH(_v) ((_v) << 16)
+#define PPS14_15_CHUNK_SIZE_MASK (0xffff << 0)
+#define PPS14_15_CHUNK_SIZE(_v) (_v << 0)
+
+#define DSC_PPS16_19 0x0030
+#define PPS16_17_INIT_XMIT_DELAY_MASK (0x3ff << 16)
+#define PPS16_17_INIT_XMIT_DELAY(_v) ((_v) << 16)
+#define PPS18_19_INIT_DEC_DELAY_MASK (0xffff << 0)
+#define PPS18_19_INIT_DEC_DELAY(_v) ((_v) << 0)
+
+#define DSC_PPS20_23 0x0034
+#define PPS21_INIT_SCALE_VALUE_MASK (0x3f << 16)
+#define PPS21_INIT_SCALE_VALUE(_v) ((_v) << 16)
+#define PPS22_23_SCALE_INC_INTERVAL_MASK (0xffff << 0)
+#define PPS22_23_SCALE_INC_INTERVAL(_v) (_v << 0)
+
+#define DSC_PPS24_27 0x0038
+#define PPS24_25_SCALE_DEC_INTERVAL_MASK (0xfff << 16)
+#define PPS24_25_SCALE_DEC_INTERVAL(_v) ((_v) << 16)
+/* FL : First Line */
+#define PPS27_FL_BPG_OFFSET_MASK (0x1f << 0)
+#define PPS27_FL_BPG_OFFSET(_v) (_v << 0)
+
+#define DSC_PPS28_31 0x003C
+/* NFL : Not First Line */
+#define PPS28_29_NFL_BPG_OFFSET_MASK (0xffff << 16)
+#define PPS28_29_NFL_BPG_OFFSET(_v) ((_v) << 16)
+#define PPS30_31_SLICE_BPG_OFFSET_MASK (0xffff << 0)
+#define PPS30_31_SLICE_BPG_OFFSET(_v) (_v << 0)
+
+#define DSC_PPS32_35 0x0040
+#define PPS32_33_INIT_OFFSET_MASK (0xffff << 16)
+#define PPS32_33_INIT_OFFSET(_v) ((_v) << 16)
+#define PPS34_35_FINAL_OFFSET_MASK (0xffff << 0)
+#define PPS34_35_FINAL_OFFSET(_v) (_v << 0)
+
+#define DSC_PPS36_39 0x0044
+#define PPS36_FLATNESS_MIN_QP_MASK (0xff << 24)
+#define PPS36_FLATNESS_MIN_QP(_v) ((_v) << 24)
+#define PPS37_FLATNESS_MAX_QP_MASK (0xff << 16)
+#define PPS37_FLATNESS_MAX_QP(_v) ((_v) << 16)
+#define PPS38_39_RC_MODEL_SIZE_MASK (0xffff << 0)
+#define PPS38_39_RC_MODEL_SIZE(_v) (_v << 0)
+
+#define DSC_PPS40_43 0x0048
+#define PPS40_RC_EDGE_FACTOR_MASK (0xff << 24)
+#define PPS40_RC_EDGE_FACTOR(_v) ((_v) << 24)
+#define PPS41_RC_QUANT_INCR_LIMIT0_MASK (0xff << 16)
+#define PPS41_RC_QUANT_INCR_LIMIT0(_v) ((_v) << 16)
+#define PPS42_RC_QUANT_INCR_LIMIT1_MASK (0xff << 8)
+#define PPS42_RC_QUANT_INCR_LIMIT1(_v) ((_v) << 8)
+#define PPS44_RC_TGT_OFFSET_HI_MASK (0xf << 4)
+#define PPS44_RC_TGT_OFFSET_HI(_v) ((_v) << 4)
+#define PPS44_RC_TGT_OFFSET_LO_MASK (0xf << 0)
+#define PPS44_RC_TGT_OFFSET_LO(_v) ((_v) << 0)
+
+#define DSC_PPS44_47 0x004C
+#define PPS44_RC_BUF_THRESH_0_MASK (0xff << 24)
+#define PPS44_RC_BUF_THRESH_0(_v) ((_v) << 24)
+#define PPS45_RC_BUF_THRESH_1_MASK (0xff << 16)
+#define PPS45_RC_BUF_THRESH_1(_v) ((_v) << 16)
+#define PPS46_RC_BUF_THRESH_2_MASK (0xff << 8)
+#define PPS46_RC_BUF_THRESH_3(_v) ((_v) << 8)
+#define PPS47_RC_BUF_THRESH_3_MASK (0xff << 0)
+#define PPS47_RC_BUF_THRESH_3(_v) ((_v) << 0)
+
+#define DSC_PPS48_51 0x0050
+#define PPS48_RC_BUF_THRESH_4_MASK (0xff << 24)
+#define PPS48_RC_BUF_THRESH_4(_v) ((_v) << 24)
+#define PPS49_RC_BUF_THRESH_5_MASK (0xff << 16)
+#define PPS49_RC_BUF_THRESH_5(_v) ((_v) << 16)
+#define PPS50_RC_BUF_THRESH_6_MASK (0xff << 8)
+#define PPS50_RC_BUF_THRESH_6(_v) ((_v) << 8)
+#define PPS51_RC_BUF_THRESH_7_MASK (0xff << 0)
+#define PPS51_RC_BUF_THRESH_7(_v) ((_v) << 0)
+
+#define DSC_PPS52_55 0x0054
+#define PPS52_RC_BUF_THRESH_8_MASK (0xff << 24)
+#define PPS52_RC_BUF_THRESH_8(_v) ((_v) << 24)
+#define PPS53_RC_BUF_THRESH_9_MASK (0xff << 16)
+#define PPS53_RC_BUF_THRESH_9(_v) ((_v) << 16)
+#define PPS54_RC_BUF_THRESH_A_MASK (0xff << 8)
+#define PPS54_RC_BUF_THRESH_A(_v) ((_v) << 8)
+#define PPS55_RC_BUF_THRESH_B_MASK (0xff << 0)
+#define PPS55_RC_BUF_THRESH_B(_v) ((_v) << 0)
+
+#define DSC_PPS56_59 0x0058
+#define PPS56_RC_BUF_THRESH_C_MASK (0xff << 24)
+#define PPS56_RC_BUF_THRESH_C(_v) ((_v) << 24)
+#define PPS57_RC_BUF_THRESH_D_MASK (0xff << 16)
+#define PPS57_RC_BUF_THRESH_D(_v) ((_v) << 16)
+#define PPS58_RC_RANGE_PARAM_MASK (0xff << 8)
+#define PPS58_RC_RANGE_PARAM(_v) (_v << 8)
+#define PPS59_RC_RANGE_PARAM_MASK (0xff << 0)
+#define PPS59_RC_RANGE_PARAM(_v) (_v << 0)
+#define PPS58_59_RC_RANGE_PARAM_MASK (0xFFFF << 0)
+#define PPS58_59_RC_RANGE_PARAM(_v) (_v << 0)
+
+
+#define DSC_DEBUG_EN 0x0078
+#define DSC_DBG_EN_MASK (1 << 31)
+#define DSC_DBG_EN(_v) ((_v) << 31)
+#define DSC_DBG_SEL_MASK (0xffff << 0)
+#define DSC_DBG_SEL(_v) ((_v) << 0)
+
+#define DSC_DEBUG_DATA 0x007C
+
+#define DSCC_DEBUG_EN 0x0080
+#define DSCC_DBG_EN_MASK (1 << 31)
+#define DSCC_DBG_EN(_v) ((_v) << 31)
+#define DSCC_DBG_SEL_MASK (0xffff << 0)
+#define DSCC_DBG_SEL(_v) ((_v) << 0)
+
+#define DSCC_DEBUG_DATA 0x0084
+
+
+
+#define SHADOW_OFFSET 0x7000
+
+#endif /* _REGS_DECON_H */
--- /dev/null
+/* linux/drivers/video/fbdev/exynos/dpu/regs-displayport.h
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Register definition file for Samsung vpp 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.
+ */
+
+#ifndef DISPLAYPORT_REGS_H_
+#define DISPLAYPORT_REGS_H_
+
+#define SYSTEM_DEVICE_VERSION (0x0000)
+#define DEVICE_VERSION (0xFFFFFFFF << 0)
+
+#define SYSTEM_SW_RESET_CONTROL (0x0004)
+#define SW_RESET (0x01 << 0)
+
+#define SYSTEM_CLK_CONTROL (0x0008)
+#define GFMUX_STATUS_TXCLK (0x03 << 8)
+#define TXCLK_SEL (0x01 << 5)
+#define TXCLK_SEL_MODE (0x01 << 4)
+#define OSC_CLK_SEL (0x01 << 0)
+
+#define SYSTEM_MAIN_LINK_BANDWIDTH (0x000C)
+#define LINK_BW_SET (0x1F << 0)
+
+#define SYSTEM_MAIN_LINK_LANE_COUNT (0x0010)
+#define LANE_COUNT_SET (0x07 << 0)
+
+#define SYSTEM_SW_FUNCTION_ENABLE (0x0014)
+#define SW_FUNC_EN (0x01 << 0)
+
+#define SYSTEM_COMMON_FUNCTION_ENABLE (0x0018)
+#define HDCP22_FUNC_EN (0x01 << 4)
+#define HDCP13_FUNC_EN (0x01 << 3)
+#define GTC_FUNC_EN (0x01 << 2)
+#define PCS_FUNC_EN (0x01 << 1)
+#define AUX_FUNC_EN (0x01 << 0)
+
+#define SYSTEM_SST1_FUNCTION_ENABLE (0x001C)
+#define SST1_LH_PWR_ON_STATUS (0x01 << 5)
+#define SST1_LH_PWR_ON (0x01 << 4)
+#define SST1_AUDIO_FIFO_FUNC_EN (0x01 << 2)
+#define SST1_AUDIO_FUNC_EN (0x01 << 1)
+#define SST1_VIDEO_FUNC_EN (0x01 << 0)
+
+#define SYSTEM_SST2_FUNCTION_ENABLE (0x0020)
+#define SST2_LH_PWR_ON_STATUS (0x01 << 5)
+#define SST2_LH_PWR_ON (0x01 << 4)
+#define SST2_AUDIO_FIFO_FUNC_EN (0x01 << 2)
+#define SST2_AUDIO_FUNC_EN (0x01 << 1)
+#define SST2_VIDEO_FUNC_EN (0x01 << 0)
+
+#define SYSTEM_MISC_CONTROL (0x0024)
+#define MISC_CTRL_EN (0x01 << 1)
+#define HDCP_HPD_RST (0x01 << 0)
+
+#define SYSTEM_HPD_CONTROL (0x0028)
+#define HPD_HDCP (0x01 << 30)
+#define HPD_DEGLITCH_COUNT (0x3FFF << 16)
+#define HPD_STATUS (0x01 << 8)
+#define HPD_EVENT_UNPLUG (0x01 << 7)
+#define HPD_EVENT_PLUG (0x01 << 6)
+#define HPD_EVENT_IRQ (0x01 << 5)
+#define HPD_EVENT_CTRL_EN (0x01 << 4)
+#define HPD_FORCE (0x01 << 1)
+#define HPD_FORCE_EN (0x01 << 0)
+
+#define SYSTEM_PLL_LOCK_CONTROL (0x002C)
+#define PLL_LOCK_STATUS (0x01 << 4)
+#define PLL_LOCK_FORCE (0x01 << 3)
+#define PLL_LOCK_FORCE_EN (0x01 << 2)
+
+#define SYSTEM_INTERRUPT_CONTROL (0x0100)
+#define SW_INTR_CTRL (0x01 << 1)
+#define INTR_POL (0x01 << 0)
+
+#define SYSTEM_INTERRUPT_REQUEST (0x0104)
+#define IRQ_MST (0x01 << 3)
+#define IRQ_STR2 (0x01 << 2)
+#define IRQ_STR1 (0x01 << 1)
+#define IRQ_COMMON (0x01 << 0)
+
+#define SYSTEM_IRQ_COMMON_STATUS (0x0108)
+#define HDCP_ENC_EN_CHG (0x01 << 22)
+#define HDCP_LINK_CHK_FAIL (0x01 << 21)
+#define HDCP_R0_CHECK_FLAG (0x01 << 20)
+#define HDCP_BKSV_RDY (0x01 << 19)
+#define HDCP_SHA_DONE (0x01 << 18)
+#define HDCP_AUTH_STATE_CHG (0x01 << 17)
+#define HDCP_AUTH_DONE (0x01 << 16)
+#define HPD_IRQ_FLAG (0x01 << 11)
+#define HPD_CHG (0x01 << 10)
+#define HPD_LOST (0x01 << 9)
+#define HPD_PLUG_INT (0x01 << 8)
+#define AUX_REPLY_RECEIVED (0x01 << 5)
+#define AUX_ERR (0x01 << 4)
+#define PLL_LOCK_CHG (0x01 << 1)
+#define SW_INTR (0x01 << 0)
+
+#define SYSTEM_IRQ_COMMON_STATUS_MASK (0x010C)
+#define MST_BUF_OVERFLOW_MASK (0x01 << 23)
+#define HDCP_ENC_EN_CHG_MASK (0x01 << 22)
+#define HDCP_LINK_CHK_FAIL_MASK (0x01 << 21)
+#define HDCP_R0_CHECK_FLAG_MASK (0x01 << 20)
+#define HDCP_BKSV_RDY_MASK (0x01 << 19)
+#define HDCP_SHA_DONE_MASK (0x01 << 18)
+#define HDCP_AUTH_STATE_CHG_MASK (0x01 << 17)
+#define HDCP_AUTH_DONE_MASK (0x01 << 16)
+#define HPD_IRQ_MASK (0x01 << 11)
+#define HPD_CHG_MASK (0x01 << 10)
+#define HPD_LOST_MASK (0x01 << 9)
+#define HPD_PLUG_MASK (0x01 << 8)
+#define AUX_REPLY_RECEIVED_MASK (0x01 << 5)
+#define AUX_ERR_MASK (0x01 << 4)
+#define PLL_LOCK_CHG_MASK (0x01 << 1)
+#define SW_INTR_MASK (0x01 << 0)
+
+#define SYSTEM_DEBUG (0x0200)
+#define AUX_HPD_CONTROL (0x01 << 2)
+#define CLKGATE_DISABLE (0x01 << 1)
+
+#define SYSTEM_DEBUG_LH_PCH (0x0204)
+#define SST2_LH_PSTATE (0x01 << 12)
+#define SST2_LH_PCH_FSM_STATE (0x0F << 8)
+#define SST1_LH_PSTATE (0x01 << 4)
+#define SST1_LH_PCH_FSM_STATE (0x0F << 0)
+
+#define AUX_CONTROL (0x1000)
+#define AUX_POWER_DOWN (0x01 << 16)
+#define AUX_REPLY_TIMER_MODE (0x03 << 12)
+#define AUX_RETRY_TIMER (0x07 << 8)
+#define AUX_PN_INV (0x01 << 1)
+#define REG_MODE_SEL (0x01 << 0)
+
+#define AUX_TRANSACTION_START (0x1004)
+#define AUX_TRAN_START (0x01 << 0)
+
+#define AUX_BUFFER_CLEAR (0x1008)
+#define AUX_BUF_CLR (0x01 << 0)
+
+#define AUX_ADDR_ONLY_COMMAND (0x100C)
+#define ADDR_ONLY_CMD (0x01 << 0)
+
+#define AUX_REQUEST_CONTROL (0x1010)
+#define REQ_COMM (0x0F << 28)
+#define REQ_ADDR (0xFFFFF << 8)
+#define REQ_LENGTH (0x3F << 0)
+
+#define AUX_COMMAND_CONTROL (0x1014)
+#define DEFER_CTRL_EN (0x01 << 8)
+#define DEFER_COUNT (0x7F << 0)
+
+#define AUX_MONITOR_1 (0x1018)
+#define AUX_BUF_DATA_COUNT (0x7F << 24)
+#define AUX_DETECTED_PERIOD_MON (0x1FF << 12)
+#define AUX_CMD_STATUS (0x0F << 8)
+#define AUX_RX_COMM (0x0F << 4)
+#define AUX_LAST_MODE (0x01 << 3)
+#define AUX_BUSY (0x01 << 2)
+#define AUX_REQ_WAIT_GRANT (0x01 << 1)
+#define AUX_REQ_SIGNAL (0x01 << 0)
+
+#define AUX_MONITOR_2 (0x101C)
+#define AUX_ERROR_NUMBER (0xFF << 0)
+
+#define AUX_TX_DATA_SET0 (0x1030)
+#define TX_DATA_3 (0xFF << 24)
+#define TX_DATA_2 (0xFF << 16)
+#define TX_DATA_1 (0xFF << 8)
+#define TX_DATA_0 (0xFF << 0)
+
+#define AUX_TX_DATA_SET1 (0x1034)
+#define TX_DATA_7 (0xFF << 24)
+#define TX_DATA_6 (0xFF << 16)
+#define TX_DATA_5 (0xFF << 8)
+#define TX_DATA_4 (0xFF << 0)
+
+#define AUX_TX_DATA_SET2 (0x1038)
+#define TX_DATA_11 (0xFF << 24)
+#define TX_DATA_10 (0xFF << 16)
+#define TX_DATA_9 (0xFF << 8)
+#define TX_DATA_8 (0xFF << 0)
+
+#define AUX_TX_DATA_SET3 (0x103C)
+#define TX_DATA_15 (0xFF << 24)
+#define TX_DATA_14 (0xFF << 16)
+#define TX_DATA_13 (0xFF << 8)
+#define TX_DATA_12 (0xFF << 0)
+
+#define AUX_RX_DATA_SET0 (0x1040)
+#define RX_DATA_3 (0xFF << 24)
+#define RX_DATA_2 (0xFF << 16)
+#define RX_DATA_1 (0xFF << 8)
+#define RX_DATA_0 (0xFF << 0)
+
+#define AUX_RX_DATA_SET1 (0x1044)
+#define RX_DATA_7 (0xFF << 24)
+#define RX_DATA_6 (0xFF << 16)
+#define RX_DATA_5 (0xFF << 8)
+#define RX_DATA_4 (0xFF << 0)
+
+#define AUX_RX_DATA_SET2 (0x1048)
+#define RX_DATA_11 (0xFF << 24)
+#define RX_DATA_10 (0xFF << 16)
+#define RX_DATA_9 (0xFF << 8)
+#define RX_DATA_8 (0xFF << 0)
+
+#define AUX_RX_DATA_SET3 (0x104C)
+#define RX_DATA_15 (0xFF << 24)
+#define RX_DATA_14 (0xFF << 16)
+#define RX_DATA_13 (0xFF << 8)
+#define RX_DATA_12 (0xFF << 0)
+
+#define GTC_CONTROL (0x1100)
+#define GTC_DELTA_ADJ_EN (0x01 << 2)
+#define IMPL_OPTION (0x01 << 1)
+#define GTC_TX_MASTER (0x01 << 0)
+
+#define GTC_WORK_ENABLE (0x1104)
+#define GTC_WORK_EN (0x01 << 0)
+
+#define GTC_TIME_CONTROL (0x1108)
+#define TIME_PERIOD_SEL (0x03 << 28)
+#define TIME_PERIOD_FRACTIONAL (0xFFFFF << 8)
+#define TIME_PERIOD_INT1 (0x0F << 4)
+#define TIME_PERIOD_INT2 (0x0F << 0)
+
+#define GTC_ATTEMPT_CONTROL (0x110C)
+#define GTC_STATE_CHANGE_CTRL (0x01 << 8)
+#define NUM_GTC_ATTEMPT (0xFF << 0)
+
+#define GTC_TX_VALUE_MONITOR (0x1110)
+#define GTC_TX_VAL (0xFFFFFFFF << 0)
+
+#define GTC_RX_VALUE_MONITOR (0x1114)
+#define GTC_RX_VAL (0xFFFFFFFF << 0)
+
+#define GTC_STATUS_MONITOR (0x1118)
+#define FREQ_ADJ_10_3 (0xFF << 8)
+#define FREQ_ADJ_2_0 (0x07 << 5)
+#define TXGTC_LOCK_DONE_FLAG (0x01 << 1)
+#define RXGTC_LOCK_DONE_FLAG (0x01 << 0)
+
+#define AUX_GTC_DEBUG (0x1200)
+#define DEBUG_STATE_SEL (0xFF << 8)
+#define DEBUG_STATE (0xFF << 0)
+
+#define MST_ENABLE (0x2000)
+#define MST_EN (0x01 << 0)
+
+#define MST_VC_PAYLOAD_UPDATE_FLAG (0x2004)
+#define VC_PAYLOAD_UPDATE_FLAG (0x01 << 0)
+
+#define MST_STREAM_1_ENABLE (0x2010)
+#define STRM_1_EN (0x01 << 0)
+
+#define MST_STREAM_1_HDCP_CTRL (0x2014)
+#define STRM_1_HDCP22_TYPE (0x01 << 1)
+#define STRM_1_HDCP_EN (0x01 << 0)
+
+#define MST_STREAM_1_X_VALUE (0x2018)
+#define STRM_1_X_VALUE (0xFF << 0)
+
+#define MST_STREAM_1_Y_VALUE (0x201C)
+#define STRM_1_Y_VALUE (0xFF << 0)
+
+#define MST_STREAM_2_ENABLE (0x2020)
+#define STRM_2_EN (0x01 << 0)
+
+#define MST_STREAM_2_HDCP_CTRL (0x2024)
+#define STRM_2_HDCP22_TYPE (0x01 << 1)
+#define STRM_2_HDCP_EN (0x01 << 0)
+
+#define MST_STREAM_2_X_VALUE (0x2028)
+#define STRM_2_X_VALUE (0xFF << 0)
+
+#define MST_STREAM_2_Y_VALUE (0x202C)
+#define STRM_2_Y_VALUE (0xFF << 0)
+
+#define MST_VC_PAYLOAD_ID_TIMESLOT_01_08 (0x2040)
+#define VC_PAYLOAD_ID_TIMESLOT_01 (0x03 << 28)
+#define VC_PAYLOAD_ID_TIMESLOT_02 (0x03 << 24)
+#define VC_PAYLOAD_ID_TIMESLOT_03 (0x03 << 20)
+#define VC_PAYLOAD_ID_TIMESLOT_04 (0x03 << 16)
+#define VC_PAYLOAD_ID_TIMESLOT_05 (0x03 << 12)
+#define VC_PAYLOAD_ID_TIMESLOT_06 (0x03 << 8)
+#define VC_PAYLOAD_ID_TIMESLOT_07 (0x03 << 4)
+#define VC_PAYLOAD_ID_TIMESLOT_08 (0x03 << 0)
+
+#define MST_VC_PAYLOAD_ID_TIMESLOT_09_16 (0x2044)
+#define VC_PAYLOAD_ID_TIMESLOT_09 (0x03 << 28)
+#define VC_PAYLOAD_ID_TIMESLOT_10 (0x03 << 24)
+#define VC_PAYLOAD_ID_TIMESLOT_11 (0x03 << 20)
+#define VC_PAYLOAD_ID_TIMESLOT_12 (0x03 << 16)
+#define VC_PAYLOAD_ID_TIMESLOT_13 (0x03 << 12)
+#define VC_PAYLOAD_ID_TIMESLOT_14 (0x03 << 8)
+#define VC_PAYLOAD_ID_TIMESLOT_15 (0x03 << 4)
+#define VC_PAYLOAD_ID_TIMESLOT_16 (0x03 << 0)
+
+#define MST_VC_PAYLOAD_ID_TIMESLOT_17_24 (0x2048)
+#define VC_PAYLOAD_ID_TIMESLOT_17 (0x03 << 28)
+#define VC_PAYLOAD_ID_TIMESLOT_18 (0x03 << 24)
+#define VC_PAYLOAD_ID_TIMESLOT_19 (0x03 << 20)
+#define VC_PAYLOAD_ID_TIMESLOT_20 (0x03 << 16)
+#define VC_PAYLOAD_ID_TIMESLOT_21 (0x03 << 12)
+#define VC_PAYLOAD_ID_TIMESLOT_22 (0x03 << 8)
+#define VC_PAYLOAD_ID_TIMESLOT_23 (0x03 << 4)
+#define VC_PAYLOAD_ID_TIMESLOT_24 (0x03 << 0)
+
+#define MST_VC_PAYLOAD_ID_TIMESLOT_25_32 (0x204C)
+#define VC_PAYLOAD_ID_TIMESLOT_25 (0x03 << 28)
+#define VC_PAYLOAD_ID_TIMESLOT_26 (0x03 << 24)
+#define VC_PAYLOAD_ID_TIMESLOT_27 (0x03 << 20)
+#define VC_PAYLOAD_ID_TIMESLOT_28 (0x03 << 16)
+#define VC_PAYLOAD_ID_TIMESLOT_29 (0x03 << 12)
+#define VC_PAYLOAD_ID_TIMESLOT_30 (0x03 << 8)
+#define VC_PAYLOAD_ID_TIMESLOT_31 (0x03 << 4)
+#define VC_PAYLOAD_ID_TIMESLOT_32 (0x03 << 0)
+
+#define MST_VC_PAYLOAD_ID_TIMESLOT_33_40 (0x2050)
+#define VC_PAYLOAD_ID_TIMESLOT_33 (0x03 << 28)
+#define VC_PAYLOAD_ID_TIMESLOT_34 (0x03 << 24)
+#define VC_PAYLOAD_ID_TIMESLOT_35 (0x03 << 20)
+#define VC_PAYLOAD_ID_TIMESLOT_36 (0x03 << 16)
+#define VC_PAYLOAD_ID_TIMESLOT_37 (0x03 << 12)
+#define VC_PAYLOAD_ID_TIMESLOT_38 (0x03 << 8)
+#define VC_PAYLOAD_ID_TIMESLOT_39 (0x03 << 4)
+#define VC_PAYLOAD_ID_TIMESLOT_40 (0x03 << 0)
+
+#define MST_VC_PAYLOAD_ID_TIMESLOT_41_48 (0x2054)
+#define VC_PAYLOAD_ID_TIMESLOT_41 (0x03 << 28)
+#define VC_PAYLOAD_ID_TIMESLOT_42 (0x03 << 24)
+#define VC_PAYLOAD_ID_TIMESLOT_43 (0x03 << 20)
+#define VC_PAYLOAD_ID_TIMESLOT_44 (0x03 << 16)
+#define VC_PAYLOAD_ID_TIMESLOT_45 (0x03 << 12)
+#define VC_PAYLOAD_ID_TIMESLOT_46 (0x03 << 8)
+#define VC_PAYLOAD_ID_TIMESLOT_47 (0x03 << 4)
+#define VC_PAYLOAD_ID_TIMESLOT_48 (0x03 << 0)
+
+#define MST_VC_PAYLOAD_ID_TIMESLOT_49_56 (0x2058)
+#define VC_PAYLOAD_ID_TIMESLOT_49 (0x03 << 28)
+#define VC_PAYLOAD_ID_TIMESLOT_50 (0x03 << 24)
+#define VC_PAYLOAD_ID_TIMESLOT_51 (0x03 << 20)
+#define VC_PAYLOAD_ID_TIMESLOT_52 (0x03 << 16)
+#define VC_PAYLOAD_ID_TIMESLOT_53 (0x03 << 12)
+#define VC_PAYLOAD_ID_TIMESLOT_54 (0x03 << 8)
+#define VC_PAYLOAD_ID_TIMESLOT_55 (0x03 << 4)
+#define VC_PAYLOAD_ID_TIMESLOT_56 (0x03 << 0)
+
+#define MST_VC_PAYLOAD_ID_TIMESLOT_57_63 (0x205C)
+#define VC_PAYLOAD_ID_TIMESLOT_57 (0x03 << 28)
+#define VC_PAYLOAD_ID_TIMESLOT_58 (0x03 << 24)
+#define VC_PAYLOAD_ID_TIMESLOT_59 (0x03 << 20)
+#define VC_PAYLOAD_ID_TIMESLOT_60 (0x03 << 16)
+#define VC_PAYLOAD_ID_TIMESLOT_61 (0x03 << 12)
+#define VC_PAYLOAD_ID_TIMESLOT_62 (0x03 << 8)
+#define VC_PAYLOAD_ID_TIMESLOT_63 (0x03 << 4)
+
+#define MST_ETC_OPTION (0x2060)
+#define ALLOCATE_TIMESLOT_CHECK_TO_ACT (0x01 << 1)
+#define HDCP22_LVP_TYPE (0x01 << 0)
+
+#define MST_BUF_STATUS (0x2064)
+#define STRM_2_BUF_OVERFLOW (0x01 << 5)
+#define STRM_1_BUF_OVERFLOW (0x01 << 4)
+#define STRM_2_BUF_OVERFLOW_CLEAR (0x01 << 1)
+#define STRM_1_BUF_OVERFLOW_CLEAR (0x01 << 0)
+
+#define PCS_CONTROL (0x3000)
+#define FEC_FUNC_EN (0x01 << 8)
+#define LINK_TRAINING_PATTERN_SET (0x03 << 4)
+#define BYTE_SWAP (0x01 << 3)
+#define BIT_SWAP (0x01 << 2)
+#define SCRAMBLE_RESET_VALUE (0x01 << 1)
+#define SCRAMBLE_BYPASS (0x01 << 0)
+
+#define PCS_LANE_CONTROL (0x3004)
+#define LANE_DATA_INV_EN (0x01 << 20)
+#define LANE3_DATA_INV (0x01 << 19)
+#define LANE2_DATA_INV (0x01 << 18)
+#define LANE1_DATA_INV (0x01 << 17)
+#define LANE0_DATA_INV (0x01 << 16)
+#define LANE3_MAP (0x03 << 12)
+#define LANE2_MAP (0x03 << 8)
+#define LANE1_MAP (0x03 << 4)
+#define LANE0_MAP (0x03 << 0)
+
+#define PCS_TEST_PATTERN_CONTROL (0x3008)
+#define TEST_PATTERN (0x3F << 8)
+#define LINK_QUALITY_PATTERN_SET (0x07 << 0)
+
+#define PCS_TEST_PATTERN_SET0 (0x300C)
+#define TEST_80BIT_PATTERN_SET0 (0xFFFFFFFF << 0)
+
+#define PCS_TEST_PATTERN_SET1 (0x3010)
+#define TEST_80BIT_PATTERN_SET1 (0xFFFFFFFF << 0)
+
+#define PCS_TEST_PATTERN_SET2 (0x3014)
+#define TEST_80BIT_PATTERN_SET2 (0xFFFF << 0)
+
+#define PCS_DEBUG_CONTROL (0x3018)
+#define FEC_FLIP_CDADJ_CODES_CASE4 (0x01 << 6)
+#define FEC_FLIP_CDADJ_CODES_CASE2 (0x01 << 5)
+#define FEC_DECODE_EN_4TH_SEL (0x01 << 4)
+#define FEC_DECODE_DIS_4TH_SEL (0x01 << 3)
+#define PRBS7_OPTION (0x01 << 2)
+#define DISABLE_AUTO_RESET_ENCODE (0x01 << 1)
+#define PRBS31_EN (0x01 << 0)
+
+#define PCS_HBR2_EYE_SR_CONTROL (0x3020)
+#define HBR2_EYE_SR_CTRL (0x03 << 16)
+#define HBR2_EYE_SR_COUNT (0xFFFF << 0)
+
+#define PCS_SA_CRC_CONTROL_1 (0x3100)
+#define SA_CRC_CLEAR (0x01 << 13)
+#define SA_CRC_SW_COMPARE (0x01 << 12)
+#define SA_CRC_LN3_PASS (0x01 << 11)
+#define SA_CRC_LN2_PASS (0x01 << 10)
+#define SA_CRC_LN1_PASS (0x01 << 9)
+#define SA_CRC_LN0_PASS (0x01 << 8)
+#define SA_CRC_LN3_FAIL (0x01 << 7)
+#define SA_CRC_LN2_FAIL (0x01 << 6)
+#define SA_CRC_LN1_FAIL (0x01 << 5)
+#define SA_CRC_LN0_FAIL (0x01 << 4)
+#define SA_CRC_LANE_3_ENABLE (0x01 << 3)
+#define SA_CRC_LANE_2_ENABLE (0x01 << 2)
+#define SA_CRC_LANE_1_ENABLE (0x01 << 1)
+#define SA_CRC_LANE_0_ENABLE (0x01 << 0)
+
+#define PCS_SA_CRC_CONTROL_2 (0x3104)
+#define SA_CRC_LN0_REF (0xFFFF << 16)
+#define SA_CRC_LN0_RESULT (0xFFFF << 0)
+
+#define PCS_SA_CRC_CONTROL_3 (0x3108)
+#define SA_CRC_LN1_REF (0xFFFF << 16)
+#define SA_CRC_LN1_RESULT (0xFFFF << 0)
+
+#define PCS_SA_CRC_CONTROL_4 (0x310C)
+#define SA_CRC_LN2_REF (0xFFFF << 16)
+#define SA_CRC_LN2_RESULT (0xFFFF << 0)
+
+#define PCS_SA_CRC_CONTROL_5 (0x3110)
+#define SA_CRC_LN3_REF (0xFFFF << 16)
+#define SA_CRC_LN3_RESULT (0xFFFF << 0)
+
+#define HDCP13_STATUS (0x4000)
+#define REAUTH_REQUEST (0x01 << 7)
+#define AUTH_FAIL (0x01 << 6)
+#define HW_1ST_AUTHEN_PASS (0x01 << 5)
+#define BKSV_VALID (0x01 << 3)
+#define ENCRYPT (0x01 << 2)
+#define HW_AUTHEN_PASS (0x01 << 1)
+#define AKSV_VALID (0x01 << 0)
+
+#define HDCP13_CONTROL_0 (0x4004)
+#define SW_STORE_AN (0x01 << 7)
+#define SW_RX_REPEATER (0x01 << 6)
+#define HW_RE_AUTHEN (0x01 << 5)
+#define SW_AUTH_OK (0x01 << 4)
+#define HW_AUTH_EN (0x01 << 3)
+#define HDCP13_ENC_EN (0x01 << 2)
+#define HW_1ST_PART_ATHENTICATION_EN (0x01 << 1)
+#define HW_2ND_PART_ATHENTICATION_EN (0x01 << 0)
+
+#define HDCP13_CONTROL_1 (0x4008)
+#define DPCD_REV_1_2 (0x01 << 3)
+#define HW_AUTH_POLLING_MODE (0x01 << 1)
+#define HDCP_INT (0x01 << 0)
+
+#define HDCP13_AKSV_0 (0x4010)
+#define AKSV0 (0xFFFFFFFF << 0)
+
+#define HDCP13_AKSV_1 (0x4014)
+#define AKSV1 (0xFF << 0)
+
+#define HDCP13_AN_0 (0x4018)
+#define AN0 (0xFFFFFFFF << 0)
+
+#define HDCP13_AN_1 (0x401C)
+#define AN1 (0xFFFFFFFF << 0)
+
+#define HDCP13_BKSV_0 (0x4020)
+#define BKSV0 (0xFFFFFFFF << 0)
+
+#define HDCP13_BKSV_1 (0x4024)
+#define BKSV1 (0xFF << 0)
+
+#define HDCP13_R0_REG (0x4028)
+#define R0 (0xFFFF << 0)
+
+#define HDCP13_BCAPS (0x4030)
+#define BCAPS (0xFF << 0)
+
+#define HDCP13_BINFO_REG (0x4034)
+#define BINFO (0xFF << 0)
+
+#define HDCP13_DEBUG_CONTROL (0x4038)
+#define CHECK_KSV (0x01 << 2)
+#define REVOCATION_CHK_DONE (0x01 << 1)
+#define HW_SKIP_RPT_ZERO_DEV (0x01 << 0)
+
+#define HDCP13_AUTH_DBG (0x4040)
+#define DDC_STATE (0x07 << 5)
+#define AUTH_STATE (0x1F << 0)
+
+#define HDCP13_ENC_DBG (0x4044)
+#define ENC_STATE (0x07 << 3)
+
+#define HDCP13_AM0_0 (0x4048)
+#define AM0_0 (0xFFFFFFFF << 0)
+
+#define HDCP13_AM0_1 (0x404C)
+#define AM0_1 (0xFFFFFFFF << 0)
+
+#define HDCP13_WAIT_R0_TIME (0x4054)
+#define HW_WRITE_AKSV_WAIT (0xFF << 0)
+
+#define HDCP13_LINK_CHK_TIME (0x4058)
+#define LINK_CHK_TIMER (0xFF << 0)
+
+#define HDCP13_REPEATER_READY_WAIT_TIME (0x405C)
+#define HW_RPTR_RDY_TIMER (0xFF << 0)
+
+#define HDCP13_READY_POLL_TIME (0x4060)
+#define POLLING_TIMER_TH (0xFF << 0)
+
+#define HDCP13_STREAM_ID_ENCRYPTION_CONTROL (0x4068)
+#define STRM_ID_ENC_UPDATE (0x01 << 7)
+#define STRM_ID_ENC (0x7F << 0)
+
+#define HDCP22_SYS_EN (0x4400)
+#define SYSTEM_ENABLE (0x01 << 0)
+
+#define HDCP22_CONTROL (0x4404)
+#define HDCP22_BYPASS_MODE (0x01 << 1)
+#define HDCP22_ENC_EN (0x01 << 0)
+
+#define HDCP22_CONTROL (0x4404)
+#define HDCP22_BYPASS_MODE (0x01 << 1)
+#define HDCP22_ENC_EN (0x01 << 0)
+
+#define HDCP22_STREAM_TYPE (0x4454)
+#define STREAM_TYPE (0x01 << 0)
+
+#define HDCP22_LVP (0x4460)
+#define LINK_VERIFICATION_PATTERN (0xFFFF << 0)
+
+#define HDCP22_LVP_GEN (0x4464)
+#define LVP_GEN (0x01 << 0)
+
+#define HDCP22_LVP_CNT_KEEP (0x4468)
+#define LVP_COUNT_KEEP_ENABLE (0x01 << 0)
+
+#define HDCP22_LANE_DECODE_CTRL (0x4470)
+#define ENHANCED_FRAMING_MODE (0x01 << 3)
+#define LVP_EN_DECODE_ENABLE (0x01 << 2)
+#define ENCRYPTION_SIGNAL_DECODE_ENABLE (0x01 << 1)
+#define LANE_DECODE_ENABLE (0x01 << 0)
+
+#define HDCP22_SR_VALUE (0x4480)
+#define SR_VALUE (0xFF << 0)
+
+#define HDCP22_CP_VALUE (0x4484)
+#define CP_VALUE (0xFF << 0)
+
+#define HDCP22_BF_VALUE (0x4488)
+#define BF_VALUE (0xFF << 0)
+
+#define HDCP22_BS_VALUE (0x448C)
+#define BS_VALUE (0xFF << 0)
+
+#define HDCP22_RIV_XOR (0x4490)
+#define RIV_XOR_LOCATION (0x01 << 0)
+
+#define HDCP22_RIV_0 (0x4500)
+#define RIV_KEY_0 (0xFFFFFFFF << 0)
+
+#define HDCP22_RIV_1 (0x4504)
+#define RIV_KEY_1 (0xFFFFFFFF << 0)
+
+#define SST1_MAIN_CONTROL (0x5000)
+#define MVID_MODE (0x01 << 11)
+#define MAUD_MODE (0x01 << 10)
+#define MVID_UPDATE_RATE (0x03 << 8)
+#define VIDEO_MODE (0x01 << 6)
+#define ENHANCED_MODE (0x01 << 5)
+#define ODD_TU_CONTROL (0x01 << 4)
+
+#define SST1_MAIN_FIFO_CONTROL (0x5004)
+#define CLEAR_AUDIO_FIFO (0x03 << 3)
+#define CLEAR_PIXEL_MAPPING_FIFO (0x01 << 2)
+#define CLEAR_MAPI_FIFO (0x01 << 1)
+#define CLEAR_GL_DATA_FIFO (0x01 << 0)
+
+#define SST1_GNS_CONTROL (0x5008)
+#define RS_CTRL (0x01 << 0)
+
+#define SST1_SR_CONTROL (0x500C)
+#define SR_COUNT_RESET_VALUE (0x1FF << 16)
+#define SR_REPLACE_BS_COUNT (0x1F << 4)
+#define SR_START_CTRL (0x01 << 1)
+#define SR_REPLACE_BS_EN (0x01 << 0)
+
+#define SST1_INTERRUPT_MONITOR (0x5020)
+#define INT_STATE (0x01 << 0)
+
+#define SST1_INTERRUPT_STATUS_SET0 (0x5024)
+#define VSC_SDP_TX_INCOMPLETE (0x01 << 9)
+#define MAPI_FIFO_UNDER_FLOW (0x01 << 8)
+#define VSYNC_DET (0x01 << 7)
+
+#define SST1_INTERRUPT_STATUS_SET1 (0x5028)
+#define AFIFO_UNDER (0x01 << 7)
+#define AFIFO_OVER (0x01 << 6)
+
+#define SST1_INTERRUPT_MASK_SET0 (0x502C)
+#define VSC_SDP_TX_INCOMPLETE_MASK (0x01 << 9)
+#define MAPI_FIFO_UNDER_FLOW_MASK (0x01 << 8)
+#define VSYNC_DET_MASK (0x01 << 7)
+
+#define SST1_INTERRUPT_MASK_SET1 (0x5030)
+#define AFIFO_UNDER_MASK (0x01 << 7)
+#define AFIFO_OVER_MASK (0x01 << 6)
+
+#define SST1_MVID_CALCULATION_CONTROL (0x5040)
+#define MVID_GEN_FILTER_TH (0xFF << 8)
+#define MVID_GEN_FILTER_EN (0x01 << 0)
+
+#define SST1_MVID_MASTER_MODE (0x5044)
+#define MVID_MASTER (0xFFFFFFFF << 0)
+
+#define SST1_NVID_MASTER_MODE (0x5048)
+#define NVID_MASTER (0xFFFFFFFF << 0)
+
+#define SST1_MVID_SFR_CONFIGURE (0x504C)
+#define MVID_SFR_CONFIG (0xFFFFFF << 0)
+
+#define SST1_NVID_SFR_CONFIGURE (0x5050)
+#define NVID_SFR_CONFIG (0xFFFFFF << 0)
+
+#define SST1_MVID_MONITOR (0x5054)
+#define MVID_MON (0xFFFFFF << 0)
+
+#define SST1_MAUD_CALCULATION_CONTROL (0x5058)
+#define M_AUD_GEN_FILTER_TH (0xFF << 8)
+#define M_AUD_GEN_FILTER_EN (0x01 << 0)
+
+#define SST1_MAUD_MASTER_MODE (0x505C)
+#define MAUD_MASTER (0xFFFFFFFF << 0)
+
+#define SST1_NAUD_MASTER_MODE (0x5060)
+#define NAUD_MASTER (0xFFFFFF << 0)
+
+#define SST1_MAUD_SFR_CONFIGURE (0x5064)
+#define MAUD_SFR_CONFIG (0xFFFFFF << 0)
+
+#define SST1_NAUD_SFR_CONFIGURE (0x5068)
+#define NAUD_SFR_CONFIG (0xFFFFFF << 0)
+
+#define SST1_NARROW_BLANK_CONTROL (0x506C)
+#define NARROW_BLANK_EN (0x01 << 1)
+#define VIDEO_FIFO_FLUSH_DISABLE (0x01 << 0)
+
+#define SST1_LOW_TU_CONTROL (0x5070)
+#define NULL_TU_CONTROL (0x01 << 1)
+#define HALF_FREQUENCY_CONTROL (0x01 << 0)
+
+#define SST1_ACTIVE_SYMBOL_INTEGER_FEC_OFF (0x5080)
+#define ACTIVE_SYMBOL_INTEGER_FEC_OFF (0x3F << 0)
+
+#define SST1_ACTIVE_SYMBOL_FRACTION_FEC_OFF (0x5084)
+#define ACTIVE_SYMBOL_FRACTION_FEC_OFF (0x3FFFFFFF << 0)
+
+#define SST1_ACTIVE_SYMBOL_THRESHOLD_FEC_OFF (0x5088)
+#define ACTIVE_SYMBOL_THRESHOLD_FEC_OFF (0x0F << 0)
+
+#define SST1_ACTIVE_SYMBOL_THRESHOLD_SEL_FEC_OFF (0x508C)
+#define ACTIVE_SYMBOL_THRESHOLD_SEL_FEC_OFF (0x01 << 0)
+
+#define SST1_ACTIVE_SYMBOL_INTEGER_FEC_ON (0x5090)
+#define ACTIVE_SYMBOL_INTEGER_FEC_ON (0x3F << 0)
+
+#define SST1_ACTIVE_SYMBOL_FRACTION_FEC_ON (0x5094)
+#define ACTIVE_SYMBOL_FRACTION_FEC_ON (0x3FFFFFFF << 0)
+
+#define SST1_ACTIVE_SYMBOL_THRESHOLD_FEC_ON (0x5098)
+#define ACTIVE_SYMBOL_THRESHOLD_FEC_ON (0x0F << 0)
+
+#define SST1_ACTIVE_SYMBOL_THRESHOLD_SEL_FEC_ON (0x509C)
+#define ACTIVE_SYMBOL_THRESHOLD_SEL_FEC_ON (0x01 << 0)
+
+#define SST1_FEC_DISABLE_SEND_CONTROL (0x5100)
+#define FEC_DISABLE_SEND_CONTROL (0x01 << 0)
+
+#define SST1_ACTIVE_SYMBOL_MODE_CONTROL (0x5104)
+#define ACTIVE_SYMBOL_MODE_CONTROL (0x01 << 0)
+
+#define SST1_VIDEO_CONTROL (0x5400)
+#define STRM_VALID_MON (0x01 << 10)
+#define STRM_VALID_FORCE (0x01 << 9)
+#define STRM_VALID_CTRL (0x01 << 8)
+#define DYNAMIC_RANGE_MODE (0x01 << 7)
+#define BPC (0x07 << 4)
+#define COLOR_FORMAT (0x03 << 2)
+#define VSYNC_POLARITY (0x01 << 1)
+#define HSYNC_POLARITY (0x01 << 0)
+
+#define SST1_VIDEO_ENABLE (0x5404)
+#define VIDEO_EN (0x01 << 0)
+
+#define SST1_VIDEO_MASTER_TIMING_GEN (0x5408)
+#define VIDEO_MASTER_TIME_GEN (0x01 << 0)
+
+#define SST1_VIDEO_MUTE (0x540C)
+#define VIDEO_MUTE (0x01 << 0)
+
+#define SST1_VIDEO_FIFO_THRESHOLD_CONTROL (0x5410)
+#define GL_FIFO_TH_CTRL (0x01 << 5)
+#define GL_FIFO_TH_VALUE (0x1F << 0)
+
+#define SST1_VIDEO_HORIZONTAL_TOTAL_PIXELS (0x5414)
+#define H_TOTAL_MASTER (0xFFFFFFFF << 0)
+
+#define SST1_VIDEO_VERTICAL_TOTAL_PIXELS (0x5418)
+#define V_TOTAL_MASTER (0xFFFFFFFF << 0)
+
+#define SST1_VIDEO_HORIZONTAL_FRONT_PORCH (0x541C)
+#define H_F_PORCH_MASTER (0xFFFFFFFF << 0)
+
+#define SST1_VIDEO_HORIZONTAL_BACK_PORCH (0x5420)
+#define H_B_PORCH_MASTER (0xFFFFFFFF << 0)
+
+#define SST1_VIDEO_HORIZONTAL_ACTIVE (0x5424)
+#define H_ACTIVE_MASTER (0xFFFFFFFF << 0)
+
+#define SST1_VIDEO_VERTICAL_FRONT_PORCH (0x5428)
+#define V_F_PORCH_MASTER (0xFFFFFFFF << 0)
+
+#define SST1_VIDEO_VERTICAL_BACK_PORCH (0x542C)
+#define V_B_PORCH_MASTER (0xFFFFFFFF << 0)
+
+#define SST1_VIDEO_VERTICAL_ACTIVE (0x5430)
+#define V_ACTIVE_MASTER (0xFFFFFFFF << 0)
+
+#define SST1_VIDEO_DSC_STREAM_CONTROL_0 (0x5434)
+#define DSC_ENABLE (0x01 << 4)
+#define SLICE_COUNT_PER_LINE (0x07 << 0)
+
+#define SST1_VIDEO_DSC_STREAM_CONTROL_1 (0x5438)
+#define CHUNK_SIZE_1 (0xFFFF << 16)
+#define CHUNK_SIZE_0 (0xFFFF << 0)
+
+#define SST1_VIDEO_DSC_STREAM_CONTROL_2 (0x543C)
+#define CHUNK_SIZE_3 (0xFFFF << 16)
+#define CHUNK_SIZE_2 (0xFFFF << 0)
+
+#define SST1_VIDEO_BIST_CONTROL (0x5450)
+#define BIST_PRBS7_SEED (0x7F << 8)
+#define BIST_USER_DATA_EN (0x01 << 4)
+#define BIST_EN (0x01 << 3)
+#define BIST_WIDTH (0x01 << 2)
+#define BIST_TYPE (0x03 << 0)
+
+#define SST1_VIDEO_BIST_USER_DATA_R (0x5454)
+#define BIST_USER_DATA_R (0x3FF << 0)
+
+#define SST1_VIDEO_BIST_USER_DATA_G (0x5458)
+#define BIST_USER_DATA_G (0x3FF << 0)
+
+#define SST1_VIDEO_BIST_USER_DATA_B (0x545C)
+#define BIST_USER_DATA_B (0x3FF << 0)
+
+#define SST1_VIDEO_DEBUG_FSM_STATE (0x5460)
+#define DATA_PACK_FSM_STATE (0x3F << 16)
+#define LINE_FSM_STATE (0x07 << 8)
+#define PIXEL_FSM_STATE (0x07 << 0)
+
+#define SST1_VIDEO_DEBUG_MAPI (0x5464)
+#define MAPI_UNDERFLOW_STATUS (0x01 << 0)
+
+#define SST1_VIDEO_DEBUG_ACTV_SYM_STEP_CNTL (0x5468)
+#define ACTV_SYM_STEP_CNT_VAL (0x3FF << 1)
+#define ACTV_SYM_STEP_CNT_EN (0x01 << 0)
+
+#define SST1_VIDEO_DEBUG_HOR_BLANK_AUD_BW_ADJ (0x546C)
+#define HOR_BLANK_AUD_BW_ADJ (0x01 << 0)
+
+#define SST1_AUDIO_CONTROL (0x5800)
+#define SW_AUD_CODING_TYPE (0x07 << 27)
+#define AUD_DMA_IF_LTNCY_TRG_MODE (0x01 << 26)
+#define AUD_DMA_IF_MODE_CONFIG (0x01 << 25)
+#define AUD_ODD_CHANNEL_DUMMY (0x01 << 24)
+#define AUD_M_VALUE_CMP_SPD_MASTER (0x07 << 21)
+#define DMA_BURST_SEL (0x07 << 18)
+#define AUDIO_BIT_MAPPING_TYPE (0x03 << 16)
+#define PCM_SIZE (0x03 << 13)
+#define AUDIO_CH_STATUS_SAME (0x01 << 5)
+#define AUD_GTC_CHST_EN (0x01 << 1)
+
+#define SST1_AUDIO_ENABLE (0x5804)
+#define AUDIO_EN (0x01 << 0)
+
+#define SST1_AUDIO_MASTER_TIMING_GEN (0x5808)
+#define AUDIO_MASTER_TIME_GEN (0x01 << 0)
+
+#define SST1_AUDIO_DMA_REQUEST_LATENCY_CONFIG (0x580C)
+#define AUD_DMA_ACK_STATUS (0x01 << 21)
+#define AUD_DMA_FORCE_ACK (0x01 << 20)
+#define AUD_DMA_FORCE_ACK_SEL (0x01 << 19)
+#define AUD_DMA_REQ_STATUS (0x01 << 18)
+#define AUD_DMA_FORCE_REQ_VAL (0x01 << 17)
+#define AUD_DMA_FORCE_REQ_SEL (0x01 << 16)
+#define MASTER_DMA_REQ_LTNCY_CONFIG (0xFF << 0)
+
+#define SST1_AUDIO_MUTE_CONTROL (0x5810)
+#define AUD_MUTE_UNDRUN_EN (0x01 << 5)
+#define AUD_MUTE_OVFLOW_EN (0x01 << 4)
+#define AUD_MUTE_CLKCHG_EN (0x01 << 1)
+
+#define SST1_AUDIO_MARGIN_CONTROL (0x5814)
+#define FORCE_AUDIO_MARGIN (0x01 << 16)
+#define AUDIO_MARGIN (0x1FFF << 0)
+
+#define SST1_AUDIO_DATA_WRITE_FIFO (0x5818)
+#define AUDIO_DATA_FIFO (0xFFFFFFFF << 0)
+
+#define SST1_AUDIO_GTC_CONTROL (0x5824)
+#define AUD_GTC_DELTA (0xFFFFFFFF << 0)
+
+#define SST1_AUDIO_GTC_VALID_BIT_CONTROL (0x5828)
+#define AUDIO_GTC_VALID_CONTROL (0x01 << 1)
+#define AUDIO_GTC_VALID (0x01 << 0)
+
+#define SST1_AUDIO_3DLPCM_PACKET_WAIT_TIMER (0x582C)
+#define AUDIO_3D_PKT_WAIT_TIMER (0x3F << 0)
+
+#define SST1_AUDIO_BIST_CONTROL (0x5830)
+#define SIN_AMPL (0x0F << 4)
+#define AUD_BIST_EN (0x01 << 0)
+
+#define SST1_AUDIO_BIST_CHANNEL_STATUS_SET0 (0x5834)
+#define CHNL_BIT1 (0x03 << 30)
+#define CLK_ACCUR (0x03 << 28)
+#define FS_FREQ (0x0F << 24)
+#define CH_NUM (0x0F << 20)
+#define SOURCE_NUM (0x0F << 16)
+#define CAT_CODE (0xFF << 8)
+#define MODE (0x03 << 6)
+#define PCM_MODE (0x07 << 3)
+#define SW_CPRGT (0x01 << 2)
+#define NON_PCM (0x01 << 1)
+#define PROF_APP (0x01 << 0)
+
+#define SST1_AUDIO_BIST_CHANNEL_STATUS_SET1 (0x5838)
+#define CHNL_BIT2 (0x0F << 4)
+#define WORD_LENGTH (0x07 << 1)
+#define WORD_MAX (0x01 << 0)
+
+#define SST1_AUDIO_BUFFER_CONTROL (0x583C)
+#define MASTER_AUDIO_INIT_BUFFER_THRD (0x7F << 24)
+#define MASTER_AUDIO_BUFFER_THRD (0x3F << 18)
+#define MASTER_AUDIO_BUFFER_EMPTY_INT_MASK (0x01 << 17)
+#define MASTER_AUDIO_CHANNEL_COUNT (0x1F << 12)
+#define MASTER_AUDIO_BUFFER_LEVEL (0x7F << 5)
+#define MASTER_AUDIO_BUFFER_LEVEL_BIT_POS (5)
+#define AUD_DMA_NOISE_INT_MASK (0x01 << 4)
+#define AUD_DMA_NOISE_INT (0x01 << 3)
+#define AUD_DMA_NOISE_INT_EN (0x01 << 2)
+#define MASTER_AUDIO_BUFFER_EMPTY_INT (0x01 << 1)
+#define MASTER_AUDIO_BUFFER_EMPTY_INT_EN (0x01 << 0)
+
+#define SST1_AUDIO_CHANNEL_1_4_REMAP (0x5840)
+#define AUD_CH_04_REMAP (0x3F << 24)
+#define AUD_CH_03_REMAP (0x3F << 16)
+#define AUD_CH_02_REMAP (0x3F << 8)
+#define AUD_CH_01_REMAP (0x3F << 0)
+
+#define SST1_AUDIO_CHANNEL_5_8_REMAP (0x5844)
+#define AUD_CH_08_REMAP (0x3F << 24)
+#define AUD_CH_07_REMAP (0x3F << 16)
+#define AUD_CH_06_REMAP (0x3F << 8)
+#define AUD_CH_05_REMAP (0x3F << 0)
+
+#define SST1_AUDIO_CHANNEL_9_12_REMAP (0x5848)
+#define AUD_CH_12_REMAP (0x3F << 24)
+#define AUD_CH_11_REMAP (0x3F << 16)
+#define AUD_CH_10_REMAP (0x3F << 8)
+#define AUD_CH_09_REMAP (0x3F << 0)
+
+#define SST1_AUDIO_CHANNEL_13_16_REMAP (0x584C)
+#define AUD_CH_16_REMAP (0x3F << 24)
+#define AUD_CH_15_REMAP (0x3F << 16)
+#define AUD_CH_14_REMAP (0x3F << 8)
+#define AUD_CH_13_REMAP (0x3F << 0)
+
+#define SST1_AUDIO_CHANNEL_17_20_REMAP (0x5850)
+#define AUD_CH_20_REMAP (0x3F << 24)
+#define AUD_CH_19_REMAP (0x3F << 16)
+#define AUD_CH_18_REMAP (0x3F << 8)
+#define AUD_CH_17_REMAP (0x3F << 0)
+
+#define SST1_AUDIO_CHANNEL_21_24_REMAP (0x5854)
+#define AUD_CH_24_REMAP (0x3F << 24)
+#define AUD_CH_23_REMAP (0x3F << 16)
+#define AUD_CH_22_REMAP (0x3F << 8)
+#define AUD_CH_21_REMAP (0x3F << 0)
+
+#define SST1_AUDIO_CHANNEL_25_28_REMAP (0x5858)
+#define AUD_CH_28_REMAP (0x3F << 24)
+#define AUD_CH_27_REMAP (0x3F << 16)
+#define AUD_CH_26_REMAP (0x3F << 8)
+#define AUD_CH_25_REMAP (0x3F << 0)
+
+#define SST1_AUDIO_CHANNEL_29_32_REMAP (0x585C)
+#define AUD_CH_32_REMAP (0x3F << 24)
+#define AUD_CH_31_REMAP (0x3F << 16)
+#define AUD_CH_30_REMAP (0x3F << 8)
+#define AUD_CH_29_REMAP (0x3F << 0)
+
+#define SST1_AUDIO_CHANNEL_1_2_STATUS_CTRL_0 (0x5860)
+#define MASTER_AUD_GP0_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP0_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP0_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP0_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_1_2_STATUS_CTRL_1 (0x5864)
+#define MASTER_AUD_GP0_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_3_4_STATUS_CTRL_0 (0x5868)
+#define MASTER_AUD_GP1_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP1_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP1_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP1_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_3_4_STATUS_CTRL_1 (0x586C)
+#define MASTER_AUD_GP1_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_5_6_STATUS_CTRL_0 (0x5870)
+#define MASTER_AUD_GP2_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP2_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP2_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP2_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_5_6_STATUS_CTRL_1 (0x5874)
+#define MASTER_AUD_GP2_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_7_8_STATUS_CTRL_0 (0x5878)
+#define MASTER_AUD_GP3_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP3_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP3_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP3_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_7_8_STATUS_CTRL_1 (0x587C)
+#define MASTER_AUD_GP3_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_9_10_STATUS_CTRL_0 (0x5880)
+#define MASTER_AUD_GP4_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP4_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP4_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP4_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_9_10_STATUS_CTRL_1 (0x5884)
+#define MASTER_AUD_GP4_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_11_12_STATUS_CTRL_0 (0x5888)
+#define MASTER_AUD_GP5_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP5_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP5_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP5_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_11_12_STATUS_CTRL_1 (0x588C)
+#define MASTER_AUD_GP5_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_13_14_STATUS_CTRL_0 (0x5890)
+#define MASTER_AUD_GP6_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP6_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP6_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP6_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_13_14_STATUS_CTRL_1 (0x5894)
+#define MASTER_AUD_GP6_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_15_16_STATUS_CTRL_0 (0x5898)
+#define MASTER_AUD_GP7_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP7_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP7_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP7_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_15_16_STATUS_CTRL_1 (0x589C)
+#define MASTER_AUD_GP7_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_17_18_STATUS_CTRL_0 (0x58A0)
+#define MASTER_AUD_GP8_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP8_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP8_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP8_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_17_18_STATUS_CTRL_1 (0x58A4)
+#define MASTER_AUD_GP8_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_19_20_STATUS_CTRL_0 (0x58A8)
+#define MASTER_AUD_GP9_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP9_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP9_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP9_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_19_20_STATUS_CTRL_1 (0x58AC)
+#define MASTER_AUD_GP9_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_21_22_STATUS_CTRL_0 (0x58B0)
+#define MASTER_AUD_GP10_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP10_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP10_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP10_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_21_22_STATUS_CTRL_1 (0x58B4)
+#define MASTER_AUD_GP10_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_23_24_STATUS_CTRL_0 (0x58B8)
+#define MASTER_AUD_GP11_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP11_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP11_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP11_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_23_24_STATUS_CTRL_1 (0x58BC)
+#define MASTER_AUD_GP11_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_25_26_STATUS_CTRL_0 (0x58C0)
+#define MASTER_AUD_GP12_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP12_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP12_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP12_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_25_26_STATUS_CTRL_1 (0x58C4)
+#define MASTER_AUD_GP12_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_27_28_STATUS_CTRL_0 (0x58C8)
+#define MASTER_AUD_GP13_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP13_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP13_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP13_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_27_28_STATUS_CTRL_1 (0x58CC)
+#define MASTER_AUD_GP13_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_29_30_STATUS_CTRL_0 (0x58D0)
+#define MASTER_AUD_GP14_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP14_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP14_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP14_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_29_30_STATUS_CTRL_1 (0x58D4)
+#define MASTER_AUD_GP14_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_31_32_STATUS_CTRL_0 (0x58D8)
+#define MASTER_AUD_GP15_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP15_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP15_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP15_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_31_32_STATUS_CTRL_1 (0x58DC)
+#define MASTER_AUD_GP15_STA_4 (0xFF << 0)
+
+#define SST1_STREAM_IF_CRC_CONTROL_1 (0x58E0)
+#define IF_CRC_CLEAR (0x01 << 13)
+#define IF_CRC_PASS (0x01 << 12)
+#define IF_CRC_FAIL (0x01 << 8)
+#define IF_CRC_SW_COMPARE (0x01 << 4)
+#define IF_CRC_EN (0x01 << 0)
+
+#define SST1_STREAM_IF_CRC_CONTROL_2 (0x58E4)
+#define IF_CRC_R_REF (0xFF << 16)
+#define IF_CRC_R_RESULT (0xFF << 0)
+
+#define SST1_STREAM_IF_CRC_CONTROL_3 (0x58E8)
+#define IF_CRC_G_REF (0xFF << 16)
+#define IF_CRC_G_RESULT (0xFF << 0)
+
+#define SST1_STREAM_IF_CRC_CONTROL_4 (0x58EC)
+#define IF_CRC_B_REF (0xFF << 16)
+#define IF_CRC_B_RESULT (0xFF << 0)
+
+#define SST1_AUDIO_DEBUG_MARGIN_CONTROL (0x5900)
+#define AUDIO_DEBUG_MARGIN_EN (0x01 << 6)
+#define AUDIO_DEBUG_MARGIN_VAL (0x3F << 0)
+
+#define SST1_SDP_SPLITTING_CONTROL (0x5C00)
+#define SDP_SPLITTING_EN (0x01 << 0)
+
+#define SST1_INFOFRAME_UPDATE_CONTROL (0x5C04)
+#define HDR_INFO_UPDATE (0x01 << 4)
+#define AUDIO_INFO_UPDATE (0x01 << 3)
+#define AVI_INFO_UPDATE (0x01 << 2)
+#define MPEG_INFO_UPDATE (0x01 << 1)
+#define SPD_INFO_UPDATE (0x01 << 0)
+
+#define SST1_INFOFRAME_SEND_CONTROL (0x5C08)
+#define HDR_INFO_SEND (0x01 << 4)
+#define AUDIO_INFO_SEND (0x01 << 3)
+#define AVI_INFO_SEND (0x01 << 2)
+#define MPEG_INFO_SEND (0x01 << 1)
+#define SPD_INFO_SEND (0x01 << 0)
+
+#define SST1_INFOFRAME_SDP_VERSION_CONTROL (0x5C0C)
+#define INFOFRAME_SDP_HB3_SEL (0x01 << 1)
+#define INFOFRAME_SDP_VERSION_SEL (0x01 << 0)
+
+#define SST1_INFOFRAME_SPD_PACKET_TYPE (0x5C10)
+#define SPD_TYPE (0xFF << 0)
+
+#define SST1_INFOFRAME_SPD_REUSE_PACKET_CONTROL (0x5C14)
+#define SPD_REUSE_EN (0x01 << 0)
+
+#define SST1_PPS_SDP_CONTROL (0x5C20)
+#define PPS_SDP_CHANGE_STATUS (0x01 << 2)
+#define PPS_SDP_FRAME_SEND_ENABLE (0x01 << 1)
+#define PPS_SDP_UPDATE (0x01 << 0)
+
+#define SST1_VSC_SDP_CONTROL_1 (0x5C24)
+#define VSC_TOTAL_BYTES_IN_SDP (0xFFF << 8)
+#define VSC_CHANGE_STATUS (0x01 << 5)
+#define VSC_FORCE_PACKET_MARGIN (0x01 << 4)
+#define VSC_FIX_PACKET_SEQUENCE (0x01 << 3)
+#define VSC_EXT_VESA_CEA (0x01 << 2)
+#define VSC_SDP_FRAME_SEND_ENABLE (0x01 << 1)
+#define VSC_SDP_UPDATE (0x01 << 0)
+
+#define SST1_VSC_SDP_CONTROL_2 (0x5C28)
+#define VSC_SETUP_TIME (0xFFF << 20)
+#define VSC_PACKET_MARGIN (0x1FFF << 0)
+
+#define SST1_MST_WAIT_TIMER_CONTROL_1 (0x5C2C)
+#define AUDIO_WAIT_TIMER (0x1FFF << 16)
+#define INFOFRAME_WAIT_TIMER (0x1FFF << 0)
+
+#define SST1_MST_WAIT_TIMER_CONTROL_2 (0x5C30)
+#define PPS_WAIT_TIMER (0x1FFF << 16)
+#define VSC_PACKET_WAIT_TIMER (0x1FFF << 0)
+
+#define SST1_INFOFRAME_AVI_PACKET_DATA_SET0 (0x5C40)
+#define AVI_DB4 (0xFF << 24)
+#define AVI_DB3 (0xFF << 16)
+#define AVI_DB2 (0xFF << 8)
+#define AVI_DB1 (0xFF << 0)
+
+#define SST1_INFOFRAME_AVI_PACKET_DATA_SET1 (0x5C44)
+#define AVI_DB8 (0xFF << 24)
+#define AVI_DB7 (0xFF << 16)
+#define AVI_DB6 (0xFF << 8)
+#define AVI_DB5 (0xFF << 0)
+
+#define SST1_INFOFRAME_AVI_PACKET_DATA_SET2 (0x5C48)
+#define AVI_DB12 (0xFF << 24)
+#define AVI_DB11 (0xFF << 16)
+#define AVI_DB10 (0xFF << 8)
+#define AVI_DB9 (0xFF << 0)
+
+#define SST1_INFOFRAME_AVI_PACKET_DATA_SET3 (0x5C4C)
+#define AVI_DB13 (0xFF << 0)
+
+#define SST1_INFOFRAME_AUDIO_PACKET_DATA_SET0 (0x5C50)
+#define AUDIO_DB4 (0xFF << 24)
+#define AUDIO_DB3 (0xFF << 16)
+#define AUDIO_DB2 (0xFF << 8)
+#define AUDIO_DB1 (0xFF << 0)
+
+#define SST1_INFOFRAME_AUDIO_PACKET_DATA_SET1 (0x5C54)
+#define AUDIO_DB8 (0xFF << 24)
+#define AUDIO_DB7 (0xFF << 16)
+#define AUDIO_DB6 (0xFF << 8)
+#define AUDIO_DB5 (0xFF << 0)
+
+#define SST1_INFOFRAME_AUDIO_PACKET_DATA_SET2 (0x5C58)
+#define AVI_DB10 (0xFF << 8)
+#define AVI_DB9 (0xFF << 0)
+
+#define SST1_INFOFRAME_SPD_PACKET_DATA_SET0 (0x5C60)
+#define SPD_DB4 (0xFF << 24)
+#define SPD_DB3 (0xFF << 16)
+#define SPD_DB2 (0xFF << 8)
+#define SPD_DB1 (0xFF << 0)
+
+#define SST1_INFOFRAME_SPD_PACKET_DATA_SET1 (0x5C64)
+#define SPD_DB8 (0xFF << 24)
+#define SPD_DB7 (0xFF << 16)
+#define SPD_DB6 (0xFF << 8)
+#define SPD_DB5 (0xFF << 0)
+
+#define SST1_INFOFRAME_SPD_PACKET_DATA_SET2 (0x5C68)
+#define SPD_DB12 (0xFF << 24)
+#define SPD_DB11 (0xFF << 16)
+#define SPD_DB10 (0xFF << 8)
+#define SPD_DB9 (0xFF << 0)
+
+#define SST1_INFOFRAME_SPD_PACKET_DATA_SET3 (0x5C6C)
+#define SPD_DB16 (0xFF << 24)
+#define SPD_DB15 (0xFF << 16)
+#define SPD_DB14 (0xFF << 8)
+#define SPD_DB13 (0xFF << 0)
+
+#define SST1_INFOFRAME_SPD_PACKET_DATA_SET4 (0x5C70)
+#define SPD_DB20 (0xFF << 24)
+#define SPD_DB19 (0xFF << 16)
+#define SPD_DB18 (0xFF << 8)
+#define SPD_DB17 (0xFF << 0)
+
+#define SST1_INFOFRAME_SPD_PACKET_DATA_SET5 (0x5C74)
+#define SPD_DB24 (0xFF << 24)
+#define SPD_DB23 (0xFF << 16)
+#define SPD_DB22 (0xFF << 8)
+#define SPD_DB21 (0xFF << 0)
+
+#define SST1_INFOFRAME_SPD_PACKET_DATA_SET6 (0x5C78)
+#define SPD_DB25 (0xFF << 0)
+
+#define SST1_INFOFRAME_MPEG_PACKET_DATA_SET0 (0x5C80)
+#define MPEG_DB4 (0xFF << 24)
+#define MPEG_DB3 (0xFF << 16)
+#define MPEG_DB2 (0xFF << 8)
+#define MPEG_DB1 (0xFF << 0)
+
+#define SST1_INFOFRAME_MPEG_PACKET_DATA_SET1 (0x5C84)
+#define MPEG_DB8 (0xFF << 24)
+#define MPEG_DB7 (0xFF << 16)
+#define MPEG_DB6 (0xFF << 8)
+#define MPEG_DB5 (0xFF << 0)
+
+#define SST1_INFOFRAME_MPEG_PACKET_DATA_SET2 (0x5C88)
+#define MPEG_DB10 (0xFF << 8)
+#define MPEG_DB9 (0xFF << 0)
+
+#define SST1_INFOFRAME_SPD_REUSE_PACKET_HEADER_SET (0x5C90)
+#define SPD_REUSE_HB3 (0xFF << 24)
+#define SPD_REUSE_HB2 (0xFF << 16)
+#define SPD_REUSE_HB1 (0xFF << 8)
+#define SPD_REUSE_HB0 (0xFF << 0)
+
+#define SST1_INFOFRAME_SPD_REUSE_PACKET_PARITY_SET (0x5C94)
+#define SPD_REUSE_PB3 (0xFF << 24)
+#define SPD_REUSE_PB2 (0xFF << 16)
+#define SPD_REUSE_PB1 (0xFF << 8)
+#define SPD_REUSE_PB0 (0xFF << 0)
+
+#define SST1_HDR_PACKET_DATA_SET_0 (0x5CA0)
+#define HDR_INFOFRAME_DATA_0 (0xFFFFFFFF << 0)
+
+#define SST1_HDR_PACKET_DATA_SET_1 (0x5CA4)
+#define HDR_INFOFRAME_DATA_1 (0xFFFFFFFF << 0)
+
+#define SST1_HDR_PACKET_DATA_SET_2 (0x5CA8)
+#define HDR_INFOFRAME_DATA_2 (0xFFFFFFFF << 0)
+
+#define SST1_HDR_PACKET_DATA_SET_3 (0x5CAC)
+#define HDR_INFOFRAME_DATA_3 (0xFFFFFFFF << 0)
+
+#define SST1_HDR_PACKET_DATA_SET_4 (0x5CB0)
+#define HDR_INFOFRAME_DATA_4 (0xFFFFFFFF << 0)
+
+#define SST1_HDR_PACKET_DATA_SET_5 (0x5CB4)
+#define HDR_INFOFRAME_DATA_5 (0xFFFFFFFF << 0)
+
+#define SST1_HDR_PACKET_DATA_SET_6 (0x5CB8)
+#define HDR_INFOFRAME_DATA_6 (0xFFFFFFFF << 0)
+
+#define SST1_HDR_PACKET_DATA_SET_7 (0x5CBC)
+#define HDR_INFOFRAME_DATA_7 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_0 (0x5D00)
+#define PPS_SDP_DATA_0 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_1 (0x5D04)
+#define PPS_SDP_DATA_1 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_2 (0x5D08)
+#define PPS_SDP_DATA_2 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_3 (0x5D0C)
+#define PPS_SDP_DATA_3 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_4 (0x5D10)
+#define PPS_SDP_DATA_4 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_5 (0x5D14)
+#define PPS_SDP_DATA_5 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_6 (0x5D18)
+#define PPS_SDP_DATA_6 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_7 (0x5D1C)
+#define PPS_SDP_DATA_7 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_8 (0x5D20)
+#define PPS_SDP_DATA_8 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_9 (0x5D24)
+#define PPS_SDP_DATA_9 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_10 (0x5D28)
+#define PPS_SDP_DATA_10 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_11 (0x5D2C)
+#define PPS_SDP_DATA_11 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_12 (0x5D30)
+#define PPS_SDP_DATA_12 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_13 (0x5D34)
+#define PPS_SDP_DATA_13 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_14 (0x5D38)
+#define PPS_SDP_DATA_14 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_15 (0x5D3C)
+#define PPS_SDP_DATA_15 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_16 (0x5D40)
+#define PPS_SDP_DATA_16 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_17 (0x5D44)
+#define PPS_SDP_DATA_17 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_18 (0x5D48)
+#define PPS_SDP_DATA_18 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_19 (0x5D4C)
+#define PPS_SDP_DATA_19 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_20 (0x5D50)
+#define PPS_SDP_DATA_20 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_21 (0x5D54)
+#define PPS_SDP_DATA_21 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_22 (0x5D58)
+#define PPS_SDP_DATA_22 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_23 (0x5D5C)
+#define PPS_SDP_DATA_23 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_24 (0x5D60)
+#define PPS_SDP_DATA_24 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_25 (0x5D64)
+#define PPS_SDP_DATA_25 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_26 (0x5D68)
+#define PPS_SDP_DATA_26 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_27 (0x5D6C)
+#define PPS_SDP_DATA_27 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_28 (0x5D70)
+#define PPS_SDP_DATA_28 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_29 (0x5D74)
+#define PPS_SDP_DATA_29 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_30 (0x5D78)
+#define PPS_SDP_DATA_30 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_31 (0x5D7C)
+#define PPS_SDP_DATA_31 (0xFFFFFFFF << 0)
+
+#define SST1_VSC_SDP_DATA_PAYLOAD_FIFO (0x5D80)
+#define VSC_SDP_DATA_PAYLOAD_FIFO (0xFFFFFFFF << 0)
+
+#define SST2_MAIN_CONTROL (0x6000)
+#define SST2_MAIN_FIFO_CONTROL (0x6004)
+#define SST2_GNS_CONTROL (0x6008)
+#define SST2_SR_CONTROL (0x600C)
+#define SST2_INTERRUPT_MONITOR (0x6020)
+#define SST2_INTERRUPT_STATUS_SET0 (0x6024)
+#define SST2_INTERRUPT_STATUS_SET1 (0x6028)
+#define SST2_INTERRUPT_MASK_SET0 (0x602C)
+#define SST2_INTERRUPT_MASK_SET1 (0x6030)
+#define SST2_MVID_CALCULATION_CONTROL (0x6040)
+#define SST2_MVID_MASTER_MODE (0x6044)
+#define SST2_NVID_MASTER_MODE (0x6048)
+#define SST2_MVID_SFR_CONFIGURE (0x604C)
+#define SST2_NVID_SFR_CONFIGURE (0x6050)
+#define SST2_MVID_MONITOR (0x6054)
+#define SST2_MAUD_CALCULATION_CONTROL (0x6058)
+#define SST2_MAUD_MASTER_MODE (0x605C)
+#define SST2_NAUD_MASTER_MODE (0x6060)
+#define SST2_MAUD_SFR_CONFIGURE (0x6064)
+#define SST2_NAUD_SFR_CONFIGURE (0x6068)
+#define SST2_NARROW_BLANK_CONTROL (0x606C)
+#define SST2_LOW_TU_CONTROL (0x6070)
+#define SST2_ACTIVE_SYMBOL_INTEGER_FEC_OFF (0x6080)
+#define SST2_ACTIVE_SYMBOL_FRACTION_FEC_OFF (0x6084)
+#define SST2_ACTIVE_SYMBOL_THRESHOLD_FEC_OFF (0x6088)
+#define SST2_ACTIVE_SYMBOL_THRESHOLD_SEL_FEC_OFF (0x608C)
+#define SST2_ACTIVE_SYMBOL_INTEGER_FEC_ON (0x6090)
+#define SST2_ACTIVE_SYMBOL_FRACTION_FEC_ON (0x6094)
+#define SST2_ACTIVE_SYMBOL_THRESHOLD_FEC_ON (0x6098)
+#define SST2_ACTIVE_SYMBOL_THRESHOLD_SEL_FEC_ON (0x609C)
+#define SST2_FEC_DISABLE_SEND_CONTROL (0x6100)
+#define SST2_ACTIVE_SYMBOL_MODE_CONTROL (0x6104)
+#define SST2_VIDEO_CONTROL (0x6400)
+#define SST2_VIDEO_ENABLE (0x6404)
+#define SST2_VIDEO_MASTER_TIMING_GEN (0x6408)
+#define SST2_VIDEO_MUTE (0x640C)
+#define SST2_VIDEO_FIFO_THRESHOLD_CONTROL (0x6410)
+#define SST2_VIDEO_HORIZONTAL_TOTAL_PIXELS (0x6414)
+#define SST2_VIDEO_VERTICAL_TOTAL_PIXELS (0x6418)
+#define SST2_VIDEO_HORIZONTAL_FRONT_PORCH (0x641C)
+#define SST2_VIDEO_HORIZONTAL_BACK_PORCH (0x6420)
+#define SST2_VIDEO_HORIZONTAL_ACTIVE (0x6424)
+#define SST2_VIDEO_VERTICAL_FRONT_PORCH (0x6428)
+#define SST2_VIDEO_VERTICAL_BACK_PORCH (0x642C)
+#define SST2_VIDEO_VERTICAL_ACTIVE (0x6430)
+#define SST2_VIDEO_DSC_STREAM_CONTROL_0 (0x6434)
+#define SST2_VIDEO_DSC_STREAM_CONTROL_1 (0x6438)
+#define SST2_VIDEO_DSC_STREAM_CONTROL_2 (0x643C)
+#define SST2_VIDEO_BIST_CONTROL (0x6450)
+#define SST2_VIDEO_BIST_USER_DATA_R (0x6454)
+#define SST2_VIDEO_BIST_USER_DATA_G (0x6458)
+#define SST2_VIDEO_BIST_USER_DATA_B (0x645C)
+#define SST2_VIDEO_DEBUG_FSM_STATE (0x6460)
+#define SST2_VIDEO_DEBUG_MAPI (0x6464)
+#define SST2_VIDEO_DEBUG_ACTV_SYM_STEP_CNTL (0x6468)
+#define SST2_VIDEO_DEBUG_HOR_BLANK_AUD_BW_ADJ (0x646C)
+#define SST2_AUDIO_CONTROL (0x6800)
+#define SST2_AUDIO_ENABLE (0x6804)
+#define SST2_AUDIO_MASTER_TIMING_GEN (0x6808)
+#define SST2_AUDIO_DMA_REQUEST_LATENCY_CONFIG (0x680C)
+#define SST2_AUDIO_MUTE_CONTROL (0x6810)
+#define SST2_AUDIO_MARGIN_CONTROL (0x6814)
+#define SST2_AUDIO_DATA_WRITE_FIFO (0x6818)
+#define SST2_AUDIO_GTC_CONTROL (0x6824)
+#define SST2_AUDIO_GTC_VALID_BIT_CONTROL (0x6828)
+#define SST2_AUDIO_3DLPCM_PACKET_WAIT_TIMER (0x682C)
+#define SST2_AUDIO_BIST_CONTROL (0x6830)
+#define SST2_AUDIO_BIST_CHANNEL_STATUS_SET0 (0x6834)
+#define SST2_AUDIO_BIST_CHANNEL_STATUS_SET1 (0x6838)
+#define SST2_AUDIO_BUFFER_CONTROL (0x683C)
+#define SST2_AUDIO_CHANNEL_1_4_REMAP (0x6840)
+#define SST2_AUDIO_CHANNEL_5_8_REMAP (0x6844)
+#define SST2_AUDIO_CHANNEL_9_12_REMAP (0x6848)
+#define SST2_AUDIO_CHANNEL_13_16_REMAP (0x684C)
+#define SST2_AUDIO_CHANNEL_17_20_REMAP (0x6850)
+#define SST2_AUDIO_CHANNEL_21_24_REMAP (0x6854)
+#define SST2_AUDIO_CHANNEL_25_28_REMAP (0x6858)
+#define SST2_AUDIO_CHANNEL_29_32_REMAP (0x685C)
+#define SST2_AUDIO_CHANNEL_1_2_STATUS_CTRL_0 (0x6860)
+#define SST2_AUDIO_CHANNEL_1_2_STATUS_CTRL_1 (0x6864)
+#define SST2_AUDIO_CHANNEL_3_4_STATUS_CTRL_0 (0x6868)
+#define SST2_AUDIO_CHANNEL_3_4_STATUS_CTRL_1 (0x686C)
+#define SST2_AUDIO_CHANNEL_5_6_STATUS_CTRL_0 (0x6870)
+#define SST2_AUDIO_CHANNEL_5_6_STATUS_CTRL_1 (0x6874)
+#define SST2_AUDIO_CHANNEL_7_8_STATUS_CTRL_0 (0x6878)
+#define SST2_AUDIO_CHANNEL_7_8_STATUS_CTRL_1 (0x687C)
+#define SST2_AUDIO_CHANNEL_9_10_STATUS_CTRL_0 (0x6880)
+#define SST2_AUDIO_CHANNEL_9_10_STATUS_CTRL_1 (0x6884)
+#define SST2_AUDIO_CHANNEL_11_12_STATUS_CTRL_0 (0x6888)
+#define SST2_AUDIO_CHANNEL_11_12_STATUS_CTRL_1 (0x688C)
+#define SST2_AUDIO_CHANNEL_13_14_STATUS_CTRL_0 (0x6890)
+#define SST2_AUDIO_CHANNEL_13_14_STATUS_CTRL_1 (0x6894)
+#define SST2_AUDIO_CHANNEL_15_16_STATUS_CTRL_0 (0x6898)
+#define SST2_AUDIO_CHANNEL_15_16_STATUS_CTRL_1 (0x689C)
+#define SST2_AUDIO_CHANNEL_17_18_STATUS_CTRL_0 (0x68A0)
+#define SST2_AUDIO_CHANNEL_17_18_STATUS_CTRL_1 (0x68A4)
+#define SST2_AUDIO_CHANNEL_19_20_STATUS_CTRL_0 (0x68A8)
+#define SST2_AUDIO_CHANNEL_19_20_STATUS_CTRL_1 (0x68AC)
+#define SST2_AUDIO_CHANNEL_21_22_STATUS_CTRL_0 (0x68B0)
+#define SST2_AUDIO_CHANNEL_21_22_STATUS_CTRL_1 (0x68B4)
+#define SST2_AUDIO_CHANNEL_23_24_STATUS_CTRL_0 (0x68B8)
+#define SST2_AUDIO_CHANNEL_23_24_STATUS_CTRL_1 (0x68BC)
+#define SST2_AUDIO_CHANNEL_25_26_STATUS_CTRL_0 (0x68C0)
+#define SST2_AUDIO_CHANNEL_25_26_STATUS_CTRL_1 (0x68C4)
+#define SST2_AUDIO_CHANNEL_27_28_STATUS_CTRL_0 (0x68C8)
+#define SST2_AUDIO_CHANNEL_27_28_STATUS_CTRL_1 (0x68CC)
+#define SST2_AUDIO_CHANNEL_29_30_STATUS_CTRL_0 (0x68D0)
+#define SST2_AUDIO_CHANNEL_29_30_STATUS_CTRL_1 (0x68D4)
+#define SST2_AUDIO_CHANNEL_31_32_STATUS_CTRL_0 (0x68D8)
+#define SST2_AUDIO_CHANNEL_31_32_STATUS_CTRL_1 (0x68DC)
+#define SST2_STREAM_IF_CRC_CONTROL_1 (0x68E0)
+#define SST2_STREAM_IF_CRC_CONTROL_2 (0x68E4)
+#define SST2_STREAM_IF_CRC_CONTROL_3 (0x68E8)
+#define SST2_STREAM_IF_CRC_CONTROL_4 (0x68EC)
+#define SST2_AUDIO_DEBUG_MARGIN_CONTROL (0x6900)
+#define SST2_SDP_SPLITTING_CONTROL (0x6C00)
+#define SST2_INFOFRAME_UPDATE_CONTROL (0x6C04)
+#define SST2_INFOFRAME_SEND_CONTROL (0x6C08)
+#define SST2_INFOFRAME_SDP_VERSION_CONTROL (0x6C0C)
+#define SST2_INFOFRAME_SPD_PACKET_TYPE (0x6C10)
+#define SST2_INFOFRAME_SPD_REUSE_PACKET_CONTROL (0x6C14)
+#define SST2_PPS_SDP_CONTROL (0x6C20)
+#define SST2_VSC_SDP_CONTROL_1 (0x6C24)
+#define SST2_VSC_SDP_CONTROL_2 (0x6C28)
+#define SST2_MST_WAIT_TIMER_CONTROL_1 (0x6C2C)
+#define SST2_MST_WAIT_TIMER_CONTROL_2 (0x6C30)
+#define SST2_INFOFRAME_AVI_PACKET_DATA_SET0 (0x6C40)
+#define SST2_INFOFRAME_AVI_PACKET_DATA_SET1 (0x6C44)
+#define SST2_INFOFRAME_AVI_PACKET_DATA_SET2 (0x6C48)
+#define SST2_INFOFRAME_AVI_PACKET_DATA_SET3 (0x6C4C)
+#define SST2_INFOFRAME_AUDIO_PACKET_DATA_SET0 (0x6C50)
+#define SST2_INFOFRAME_AUDIO_PACKET_DATA_SET1 (0x6C54)
+#define SST2_INFOFRAME_AUDIO_PACKET_DATA_SET2 (0x6C58)
+#define SST2_INFOFRAME_SPD_PACKET_DATA_SET0 (0x6C60)
+#define SST2_INFOFRAME_SPD_PACKET_DATA_SET1 (0x6C64)
+#define SST2_INFOFRAME_SPD_PACKET_DATA_SET2 (0x6C68)
+#define SST2_INFOFRAME_SPD_PACKET_DATA_SET3 (0x6C6C)
+#define SST2_INFOFRAME_SPD_PACKET_DATA_SET4 (0x6C70)
+#define SST2_INFOFRAME_SPD_PACKET_DATA_SET5 (0x6C74)
+#define SST2_INFOFRAME_SPD_PACKET_DATA_SET6 (0x6C78)
+#define SST2_INFOFRAME_MPEG_PACKET_DATA_SET0 (0x6C80)
+#define SST2_INFOFRAME_MPEG_PACKET_DATA_SET1 (0x6C84)
+#define SST2_INFOFRAME_MPEG_PACKET_DATA_SET2 (0x6C88)
+#define SST2_INFOFRAME_SPD_REUSE_PACKET_HEADER_SET (0x6C90)
+#define SST2_INFOFRAME_SPD_REUSE_PACKET_PARITY_SET (0x6C94)
+#define SST2_HDR_PACKET_DATA_SET_0 (0x6CA0)
+#define SST2_HDR_PACKET_DATA_SET_1 (0x6CA4)
+#define SST2_HDR_PACKET_DATA_SET_2 (0x6CA8)
+#define SST2_HDR_PACKET_DATA_SET_3 (0x6CAC)
+#define SST2_HDR_PACKET_DATA_SET_4 (0x6CB0)
+#define SST2_HDR_PACKET_DATA_SET_5 (0x6CB4)
+#define SST2_HDR_PACKET_DATA_SET_6 (0x6CB8)
+#define SST2_HDR_PACKET_DATA_SET_7 (0x6CBC)
+#define SST2_PPS_SDP_PAYLOAD_0 (0x6D00)
+#define SST2_PPS_SDP_PAYLOAD_1 (0x6D04)
+#define SST2_PPS_SDP_PAYLOAD_2 (0x6D08)
+#define SST2_PPS_SDP_PAYLOAD_3 (0x6D0C)
+#define SST2_PPS_SDP_PAYLOAD_4 (0x6D10)
+#define SST2_PPS_SDP_PAYLOAD_5 (0x6D14)
+#define SST2_PPS_SDP_PAYLOAD_6 (0x6D18)
+#define SST2_PPS_SDP_PAYLOAD_7 (0x6D1C)
+#define SST2_PPS_SDP_PAYLOAD_8 (0x6D20)
+#define SST2_PPS_SDP_PAYLOAD_9 (0x6D24)
+#define SST2_PPS_SDP_PAYLOAD_10 (0x6D28)
+#define SST2_PPS_SDP_PAYLOAD_11 (0x6D2C)
+#define SST2_PPS_SDP_PAYLOAD_12 (0x6D30)
+#define SST2_PPS_SDP_PAYLOAD_13 (0x6D34)
+#define SST2_PPS_SDP_PAYLOAD_14 (0x6D38)
+#define SST2_PPS_SDP_PAYLOAD_15 (0x6D3C)
+#define SST2_PPS_SDP_PAYLOAD_16 (0x6D40)
+#define SST2_PPS_SDP_PAYLOAD_17 (0x6D44)
+#define SST2_PPS_SDP_PAYLOAD_18 (0x6D48)
+#define SST2_PPS_SDP_PAYLOAD_19 (0x6D4C)
+#define SST2_PPS_SDP_PAYLOAD_20 (0x6D50)
+#define SST2_PPS_SDP_PAYLOAD_21 (0x6D54)
+#define SST2_PPS_SDP_PAYLOAD_22 (0x6D58)
+#define SST2_PPS_SDP_PAYLOAD_23 (0x6D5C)
+#define SST2_PPS_SDP_PAYLOAD_24 (0x6D60)
+#define SST2_PPS_SDP_PAYLOAD_25 (0x6D64)
+#define SST2_PPS_SDP_PAYLOAD_26 (0x6D68)
+#define SST2_PPS_SDP_PAYLOAD_27 (0x6D6C)
+#define SST2_PPS_SDP_PAYLOAD_28 (0x6D70)
+#define SST2_PPS_SDP_PAYLOAD_29 (0x6D74)
+#define SST2_PPS_SDP_PAYLOAD_30 (0x6D78)
+#define SST2_PPS_SDP_PAYLOAD_31 (0x6D7C)
+#define SST2_VSC_SDP_DATA_PAYLOAD_FIFO (0x6D80)
+
+/* PHY register */
+#define CMN_REG2C (0x00B0)
+#define MAN_USBDP_MODE (0x03 << 1)
+#define MAN_USBDP_MODE_EN (0x01 << 0)
+
+#define CMN_REG2D (0x00B4)
+#define USB_TX1_SEL (0x01 << 5)
+#define USB_TX3_SEL (0x01 << 4)
+
+#define DP_REG_0 (0x0800)
+#define AUX_EN (1 << 7)
+#define BGR_EN (1 << 6)
+#define BIAS_EN (1 << 5)
+#define ROPLL_EN (1 << 4)
+#define LN0_LANE_EN (1 << 3)
+#define LN1_LANE_EN (1 << 2)
+#define LN2_LANE_EN (1 << 1)
+#define LN3_LANE_EN (1 << 0)
+
+#define DP_REG_1 (0x0804)
+#define CMN_INIT_RSTN (0x01 << 0)
+
+#define DP_REG_3 (0x080C)
+#define LN0_TX_AMP_CTRL (3 << 6)
+#define LN0_TX_AMP_CTRL_BIT_POS (6)
+#define LN1_TX_AMP_CTRL (3 << 4)
+#define LN1_TX_AMP_CTRL_BIT_POS (4)
+#define LN2_TX_AMP_CTRL (3 << 2)
+#define LN2_TX_AMP_CTRL_BIT_POS (2)
+#define LN3_TX_AMP_CTRL (3 << 0)
+#define LN3_TX_AMP_CTRL_BIT_POS (0)
+
+#define DP_REG_4 (0x0810)
+#define LN0_TX_EMP_CTRL (3 << 6)
+#define LN0_TX_EMP_CTRL_BIT_POS (6)
+#define LN1_TX_EMP_CTRL (3 << 4)
+#define LN1_TX_EMP_CTRL_BIT_POS (4)
+#define LN2_TX_EMP_CTRL (3 << 2)
+#define LN2_TX_EMP_CTRL_BIT_POS (2)
+#define LN3_TX_EMP_CTRL (3 << 0)
+#define LN3_TX_EMP_CTRL_BIT_POS (0)
+
+#define DP_REG_B (0x082C)
+#define LN_TXCLK_SOURCE_LANE (0x03 << 0)
+
+#define DP_REG_F (0x083C)
+#define AUX_TX_LVL_CTRL (0x0F << 0)
+
+#define DP_REG_11 (0x0844)
+#define DP_REG_13 (0x084C)
+#define DP_REG_16 (0x0858)
+#define TX_DRV_IDRV_EN_CTRL_BIT_POS (4)
+#define DP_REG_17 (0x085C)
+#define DP_REG_18 (0x0860)
+#define DP_REG_19 (0x0864)
+#define DP_REG_1A (0x0868)
+#define DP_REG_1B (0x086C)
+#define DP_REG_1C (0x0870)
+#define DP_REG_1D (0x0874)
+#define DP_REG_31 (0x08C4)
+#define DP_REG_36 (0x08D8)
+#define DP_REG_3A (0x08E8)
+#define DP_REG_51 (0x0944)
+#define DP_REG_56 (0x0958)
+#define DP_REG_5A (0x0968)
+#define DP_REG_71 (0x09C4)
+#define DP_REG_76 (0x09D8)
+#define DP_REG_7A (0x09E8)
+
+#define DP_REG_97 (0x0A5C)
+#define SSC_EN (0x01 << 4)
+
+#define DP_REG_B3 (0x0ACC)
+#define CMN_DUMMY_CTRL_7_6 (0x03 << 6)
+#define CMN_DUMMY_CTRL_1_0 (0x03 << 0)
+
+#define DP_REG_C6 (0x0B18)
+#define DP_REG_C9 (0x0B24)
+#define DP_REG_CF (0x0B3C)
+#define DP_REG_D9 (0x0B64)
+#define DP_REG_DF (0x0B7C)
+#define DP_REG_E9 (0x0BA4)
+#define DP_REG_EF (0x0BBC)
+#endif
--- /dev/null
+/* linux/drivers/video/fbdev/dpu20/cal_9820/regs-dpp.h
+ *
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Register definition file for Samsung dpp 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.
+ */
+
+#ifndef DPP_REGS_H_
+#define DPP_REGS_H_
+
+/*
+ * DPU_DMA SFR base address : 0x19070000
+ * - GLOBAL : 0x19070000
+ * - IDMA GF0 : 0x19071000
+ * - IDMA GF1 : 0x19072000
+ * - IDMA VG : 0x19073000
+ * - IDMA VGS : 0x19074000
+ * - IDMA VGF : 0x19075000
+ * - IDMA VGRFS : 0x19076000
+ * - ODMA : 0x19077000
+ */
+#define DPU_DMA_VERSION 0x0000
+
+#define DPU_DMA_QCH_EN 0x000C
+#define DMA_QCH_EN (1 << 0)
+
+#define DPU_DMA_SWRST 0x0010
+#define DMA_CH2_SWRST (1 << 3)
+#define DMA_CH1_SWRST (1 << 2)
+#define DMA_CH0_SWRST (1 << 1)
+#define DMA_ALL_SWRST (1 << 0)
+#define DMA_CH_SWRST(_ch) (1 << ((_ch)))
+
+#define DPU_DMA_GLB_CGEN 0x0014
+#define DMA_SFR_CGEN(_v) ((_v) << 31)
+#define DMA_SFR_CGEN_MASK (1 << 31)
+#define DMA_INT_CGEN(_v) ((_v) << 0)
+/* [9820] bit range is changed [28:0] -> [30:0] */
+#define DMA_INT_CGEN_MASK (0x7FFFFFFF << 0)
+
+#define DPU_DMA_TEST_PATTERN0_3 0x0020
+#define DPU_DMA_TEST_PATTERN0_2 0x0024
+#define DPU_DMA_TEST_PATTERN0_1 0x0028
+#define DPU_DMA_TEST_PATTERN0_0 0x002C
+#define DPU_DMA_TEST_PATTERN1_3 0x0030
+#define DPU_DMA_TEST_PATTERN1_2 0x0034
+#define DPU_DMA_TEST_PATTERN1_1 0x0038
+#define DPU_DMA_TEST_PATTERN1_0 0x003C
+
+/* [9820] add AFBC QoS value */
+#define DPU_DMA_AFBC_QOS 0x0060
+#define FBC_L5_PAYLOAD_QOS(_v) ((_v) << 28)
+#define FBC_L5_PAYLOAD_QOS_MASK (0xF << 28)
+#define FBC_L4_PAYLOAD_QOS(_v) ((_v) << 24)
+#define FBC_L4_PAYLOAD_QOS_MASK (0xF << 24)
+#define FBC_L1_PAYLOAD_QOS(_v) ((_v) << 20)
+#define FBC_L1_PAYLOAD_QOS_MASK (0xF << 20)
+#define FBC_L0_PAYLOAD_QOS(_v) ((_v) << 16)
+#define FBC_L0_PAYLOAD_QOS_MASK (0xF << 16)
+#define FBC_L5_HEADER_QOS(_v) ((_v) << 12)
+#define FBC_L5_HEADER_QOS_MASK (0xF << 12)
+#define FBC_L4_HEADER_QOS(_v) ((_v) << 8)
+#define FBC_L4_HEADER_QOS_MASK (0xF << 8)
+#define FBC_L1_HEADER_QOS(_v) ((_v) << 4)
+#define FBC_L1_HEADER_QOS_MASK (0xF << 4)
+#define FBC_L0_HEADER_QOS(_v) ((_v) << 0)
+#define FBC_L0_HEADER_QOS_MASK (0xF << 0)
+
+/* _n: [0,7], _v: [0x0, 0xF] */
+#define DPU_DMA_QOS_LUT07_00 0x0070
+#define DPU_DMA_QOS_LUT15_08 0x0074
+#define DPU_DMA_QOS_LUT(_n, _v) ((_v) << (4*(_n)))
+#define DPU_DMA_QOS_LUT_MASK(_n) (0xF << (4*(_n)))
+
+#define DPU_DMA_PERFORMANCE_CON0 0x0078
+#define DPU_DMA_DEGRADATION_TIME(_v) ((_v) << 16)
+#define DPU_DMA_DEGRADATION_TIME_MASK (0xFFFF << 16)
+#define DPU_DMA_IN_IC_MAX_DEG(_v) ((_v) << 4)
+#define DPU_DMA_IN_IC_MAX_DEG_MASK (0x7F << 4)
+#define DPU_DMA_DEGRADATION_EN (1 << 0)
+
+/* Recovery constraint */
+#define DPU_DMA_RECOVERY_NUM_CTRL 0x0080
+#define DPU_DMA_RECOVERY_NUM(_v) ((_v) << 0)
+#define DPU_DMA_RECOVERY_NUM_MASK (0x7FFFFFFF << 0)
+
+#define DPU_DMA_PSLV_ERR_CTRL 0x0090
+#define DPU_DMA_PSLV_ERR_EN (1 << 0)
+
+#define DPU_DMA_DEBUG_CONTROL 0x0100
+#define DPU_DMA_DEBUG_CONTROL_SEL(_v) ((_v) << 16)
+#define DPU_DMA_DEBUG_CONTROL_EN (0x1 << 0)
+
+#define DPU_DMA_DEBUG_DATA 0x0104
+
+/*
+ * 1.1 - IDMA Register
+ * < DMA.offset >
+ * G0 G1 VG0 VG1 VGF0 VGRF
+ * 0x1000 0x2000 0x3000 0x4000 0x5000 0x6000
+ */
+#define IDMA_ENABLE 0x0000
+#define IDMA_SRESET (1 << 24)
+#define IDMA_SRAM_CLOCK_GATE_EN (1 << 9)
+#define IDMA_ALL_CLOCK_GATE_EN_MASK (0x1 << 9)
+#define IDMA_SFR_UPDATE_FORCE (1 << 4)
+#define IDMA_OP_STATUS (1 << 2)
+#define OP_STATUS_IDLE (0)
+#define OP_STATUS_BUSY (1)
+#define IDMA_INSTANT_OFF_PENDING (1 << 1)
+#define INSTANT_OFF_PENDING (1)
+#define INSTANT_OFF_NOT_PENDING (0)
+
+#define IDMA_IRQ 0x0004
+/* [9820] AFBC_CONFLICT_IRQ is added */
+#define IDMA_AFBC_CONFLICT_IRQ (1 << 25)
+/* [9820] MO_CONFLICT_IRQ -> VR_CONFLICT_IRQ */
+#define IDMA_VR_CONFLICT_IRQ (1 << 24)
+#define IDMA_AFBC_TIMEOUT_IRQ (1 << 23)
+#define IDMA_RECOVERY_START_IRQ (1 << 22)
+#define IDMA_CONFIG_ERROR (1 << 21)
+#define IDMA_LOCAL_HW_RESET_DONE (1 << 20)
+#define IDMA_READ_SLAVE_ERROR (1 << 19)
+#define IDMA_STATUS_DEADLOCK_IRQ (1 << 17)
+#define IDMA_STATUS_FRAMEDONE_IRQ (1 << 16)
+#define IDMA_ALL_IRQ_CLEAR (0x3FB << 16)
+/* [9820] AFBC_CONFLICT_IRQ_MASK is added */
+#define IDMA_AFBC_CONFLICT_MASK (1 << 10)
+/* [9820] MO_CONFLICT_IRQ_MASK -> VR_CONFLICT_IRQ_MASK */
+#define IDMA_VR_CONFLICT_MASK (1 << 9)
+#define IDMA_AFBC_TIMEOUT_MASK (1 << 8)
+#define IDMA_RECOVERY_START_MASK (1 << 7)
+#define IDMA_CONFIG_ERROR_MASK (1 << 6)
+#define IDMA_LOCAL_HW_RESET_DONE_MASK (1 << 5)
+#define IDMA_READ_SLAVE_ERROR_MASK (1 << 4)
+#define IDMA_IRQ_DEADLOCK_MASK (1 << 2)
+#define IDMA_IRQ_FRAMEDONE_MASK (1 << 1)
+#define IDMA_ALL_IRQ_MASK (0x3FB << 1)
+#define IDMA_IRQ_ENABLE (1 << 0)
+
+#define IDMA_IN_CON 0x0008
+#define IDMA_VR_MODE_EN (1 << 31)
+#define IDMA_IN_IC_MAX(_v) ((_v) << 19)
+#define IDMA_IN_IC_MAX_MASK (0xff << 19)
+#define IDMA_IMG_FORMAT(_v) ((_v) << 11)
+#define IDMA_IMG_FORMAT_MASK (0x3f << 11)
+#define IDMA_IMG_FORMAT_ARGB8888 (0)
+#define IDMA_IMG_FORMAT_ABGR8888 (1)
+#define IDMA_IMG_FORMAT_RGBA8888 (2)
+#define IDMA_IMG_FORMAT_BGRA8888 (3)
+#define IDMA_IMG_FORMAT_XRGB8888 (4)
+#define IDMA_IMG_FORMAT_XBGR8888 (5)
+#define IDMA_IMG_FORMAT_RGBX8888 (6)
+#define IDMA_IMG_FORMAT_BGRX8888 (7)
+#define IDMA_IMG_FORMAT_RGB565 (8)
+#define IDMA_IMG_FORMAT_BGR565 (9)
+#define IDMA_IMG_FORMAT_ARGB1555 (12)
+#define IDMA_IMG_FORMAT_ARGB4444 (13)
+#define IDMA_IMG_FORMAT_ARGB2101010 (16)
+#define IDMA_IMG_FORMAT_ABGR2101010 (17)
+#define IDMA_IMG_FORMAT_RGBA2101010 (18)
+#define IDMA_IMG_FORMAT_BGRA2101010 (19)
+#define IDMA_IMG_FORMAT_YUV420_2P (24)
+#define IDMA_IMG_FORMAT_YVU420_2P (25)
+#define IDMA_IMG_FORMAT_YUV420_8P2 (26)
+#define IDMA_IMG_FORMAT_YVU420_8P2 (27)
+#define IDMA_IMG_FORMAT_YUV420_P010 (29)
+#define IDMA_IMG_FORMAT_YVU420_P010 (28)
+/* [9820] Below IDMA formats are added */
+#define IDMA_IMG_FORMAT_YVU422_2P (56)
+#define IDMA_IMG_FORMAT_YUV422_2P (57)
+#define IDMA_IMG_FORMAT_YVU422_8P2 (58)
+#define IDMA_IMG_FORMAT_YUV422_8P2 (59)
+#define IDMA_IMG_FORMAT_YVU422_P210 (60)
+#define IDMA_IMG_FORMAT_YUV422_P210 (61)
+#define IDMA_ROTATION(_v) ((_v) << 8)
+#define IDMA_ROTATION_MASK (7 << 8)
+#define IDMA_ROTATION_X_FLIP (1 << 8)
+#define IDMA_ROTATION_Y_FLIP (2 << 8)
+#define IDMA_ROTATION_180 (3 << 8)
+#define IDMA_ROTATION_90 (4 << 8)
+#define IDMA_ROTATION_90_X_FLIP (5 << 8)
+#define IDMA_ROTATION_90_Y_FLIP (6 << 8)
+#define IDMA_ROTATION_270 (7 << 8)
+#define IDMA_IN_FLIP(_v) ((_v) << 8)
+#define IDMA_IN_FLIP_MASK (0x3 << 8)
+#define IDMA_AFBC_EN (1 << 7)
+#define IDMA_AFBC_TO_EN (1 << 6)
+#define IDMA_IN_CHROMINANCE_STRIDE_SEL (1 << 4)
+#define IDMA_BLOCK_EN (1 << 3)
+
+#define IDMA_OUT_CON 0x000C
+#define IDMA_OUT_FRAME_ALPHA(_v) ((_v) << 24)
+#define IDMA_OUT_FRAME_ALPHA_MASK (0xff << 24)
+
+#define IDMA_SRC_SIZE 0x0010
+#define IDMA_SRC_HEIGHT(_v) ((_v) << 16)
+#define IDMA_SRC_HEIGHT_MASK (0x3FFF << 16)
+#define IDMA_SRC_WIDTH(_v) ((_v) << 0)
+#define IDMA_SRC_WIDTH_MASK (0xFFFF << 0)
+
+#define IDMA_SRC_OFFSET 0x0014
+#define IDMA_SRC_OFFSET_Y(_v) ((_v) << 16)
+#define IDMA_SRC_OFFSET_Y_MASK (0x1FFF << 16)
+#define IDMA_SRC_OFFSET_X(_v) ((_v) << 0)
+#define IDMA_SRC_OFFSET_X_MASK (0x1FFF << 0)
+
+#define IDMA_IMG_SIZE 0x0018
+#define IDMA_IMG_HEIGHT(_v) ((_v) << 16)
+#define IDMA_IMG_HEIGHT_MASK (0x1FFF << 16)
+#define IDMA_IMG_WIDTH(_v) ((_v) << 0)
+#define IDMA_IMG_WIDTH_MASK (0x1FFF << 0)
+
+#define IDMA_CHROMINANCE_STRIDE 0x0020
+#define IDMA_CHROMA_STRIDE(_v) ((_v) << 0)
+#define IDMA_CHROMA_STRIDE_MASK (0xFFFF << 0)
+
+#define IDMA_BLOCK_OFFSET 0x0024
+#define IDMA_BLK_OFFSET_Y(_v) ((_v) << 16)
+#define IDMA_BLK_OFFSET_Y_MASK (0x1FFF << 16)
+#define IDMA_BLK_OFFSET_X(_v) ((_v) << 0)
+#define IDMA_BLK_OFFSET_X_MASK (0x1FFF << 0)
+
+#define IDMA_BLOCK_SIZE 0x0028
+#define IDMA_BLK_HEIGHT(_v) ((_v) << 16)
+#define IDMA_BLK_HEIGHT_MASK (0x1FFF << 16)
+#define IDMA_BLK_WIDTH(_v) ((_v) << 0)
+#define IDMA_BLK_WIDTH_MASK (0x1FFF << 0)
+
+#define IDMA_2BIT_STRIDE 0x0030
+#define IDMA_CHROMA_2B_STRIDE(_v) ((_v) << 16)
+#define IDMA_CHROMA_2B_STRIDE_MASK (0xFFFF << 16)
+#define IDMA_LUMA_2B_STRIDE(_v) ((_v) << 0)
+#define IDMA_LUMA_2B_STRIDE_MASK (0xFFFF << 0)
+
+#define IDMA_IN_BASE_ADDR_Y 0x0040
+#define IDMA_IN_BASE_ADDR_C 0x0044
+#define IDMA_IN_BASE_ADDR_Y2 0x0048
+#define IDMA_IN_BASE_ADDR_C2 0x004C
+
+#define IDMA_DEADLOCK_NUM 0x0050
+#define IDMA_DEADLOCK_VAL(_v) ((_v) << 1)
+#define IDMA_DEADLOCK_VAL_MASK (0x7FFFFFFF << 1)
+#define IDMA_DEADLOCK_EN (1 << 0)
+
+#define IDMA_BUS_CON 0x0054
+
+#define IDMA_DYNAMIC_GATING_EN 0x0058
+#define IDMA_DG_EN(_n, _v) ((_v) << (_n))
+#define IDMA_DG_EN_MASK(_n) (1 << (_n))
+#define IDMA_DG_EN_ALL (0x7FFFFFF << 0)
+
+#define IDMA_RECOVERY_CTRL 0x005C
+#define IDMA_RECOVERY_EN (1 << 0)
+
+#define IDMA_DEBUG_CONTROL 0x0060
+#define IDMA_DEBUG_CONTROL_SEL(_v) ((_v) << 16)
+#define IDMA_DEBUG_CONTROL_EN (0x1 << 0)
+
+#define IDMA_DEBUG_DATA 0x0064
+
+#define IDMA_IN_REQ_DEST 0x0068
+#define IDMA_IN_REG_DEST_SEL(_v) ((_v) << 0)
+#define IDMA_IN_REG_DEST_SEL_MASK (0x3 << 0)
+
+#define IDMA_CFG_ERR_STATE 0x0870
+#define IDMA_CFG_ERR_SRC_WIDTH (1 << 10)
+#define IDMA_CFG_ERR_CHROM_STRIDE (1 << 9)
+#define IDMA_CFG_ERR_BASE_ADDR_Y (1 << 8)
+#define IDMA_CFG_ERR_BASE_ADDR_C (1 << 7)
+#define IDMA_CFG_ERR_IMG_WIDTH_AFBC (1 << 6)
+#define IDMA_CFG_ERR_IMG_WIDTH (1 << 5)
+#define IDMA_CFG_ERR_IMG_HEIGHT_ROTATION (1 << 4)
+#define IDMA_CFG_ERR_IMG_HEIGHT (1 << 3)
+#define IDMA_CFG_ERR_BLOCKING (1 << 2)
+#define IDMA_CFG_ERR_SRC_OFFSET_X (1 << 1)
+#define IDMA_CFG_ERR_SRC_OFFSET_Y (1 << 0)
+#define IDMA_CFG_ERR_GET(_v) (((_v) >> 0) & 0x7FF)
+
+/*
+ * ODMA SFR list
+ * base address : 0x19077000
+ */
+#define ODMA_ENABLE 0x0000
+#define ODMA_SRSET (1 << 24)
+/* [9820] removed ? */
+//#define ODMA_SFR_CLOCK_GATE_EN (1 << 10)
+//#define ODMA_SRAM_CLOCK_GATE_EN (1 << 9)
+//#define ODMA_ALL_CLOCK_GATE_EN_MASK (0x3 << 9)
+//#define ODMA_FRAME_START_FORCE (1 << 5)
+#define ODMA_SFR_UPDATE_FORCE (1 << 4)
+#define ODMA_OP_STATUS (1 << 2)
+
+#define ODMA_IRQ 0x0004
+#define ODMA_CONFIG_ERROR (1 << 28)
+#define ODMA_SLICE_DONE(_n) (1 << (21 + (_n)))
+#define ODMA_ALL_SLICE_DONE_CLEAR (0x7F << 21)
+#define ODMA_LOCAL_HW_RESET_DONE (1 << 20)
+#define ODMA_WRITE_SLAVE_ERROR (1 << 19)
+#define ODMA_STATUS_DEADLOCK_IRQ (1 << 17)
+#define ODMA_STATUS_FRAMEDONE_IRQ (1 << 16)
+#define ODMA_ALL_IRQ_CLEAR (0x1FFB << 16)
+
+#define ODMA_CONFIG_ERROR_MASK (1 << 13)
+#define ODMA_SLICE_DONE_MASK(_n) (1 << (6 + (_n)))
+#define ODMA_ALL_SLICE_DONE_MASK (0x7F << 6)
+#define ODMA_LOCAL_HW_RESET_DONE_MASK (1 << 5)
+#define ODMA_WRITE_SLAVE_ERROR_MASK (1 << 4)
+#define ODMA_IRQ_DEADLOCK_MASK (1 << 2)
+#define ODMA_IRQ_FRAMEDONE_MASK (1 << 1)
+#define ODMA_ALL_IRQ_MASK (0x1FFB << 1)
+#define ODMA_IRQ_ENABLE (1 << 0)
+
+#define ODMA_CHROMINANCE_STRIDE 0x0020
+#define ODMA_CHROMA_STRIDE(_v) ((_v) << 0)
+#define ODMA_CHROMA_STRIDE_MASK (0xFFFF << 0)
+
+#define ODMA_PERFORMANCE_CON0 0x0030
+#define ODMA_DEGRADATION_TIME(_v) ((_v) << 16)
+#define ODMA_DEGRADATION_TIME_MASK (0xFFFF << 16)
+#define ODMA_DEGRADATION_EN (1 << 15)
+#define ODMA_IN_IC_MAX_DEG(_v) ((_v) << 0)
+#define ODMA_IN_IC_MAX_DEG_MASK (0x7F << 0)
+
+#define ODMA_OUT_CON0 0x004C
+#define ODMA_IN_IC_MAX(_v) ((_v) << 19)
+#define ODMA_IN_IC_MAX_MASK (0x7f << 19)
+#define ODMA_IMG_FORMAT(_v) ((_v) << 11)
+#define ODMA_IMG_FORMAT_MASK (0x1f << 11)
+#define ODMA_IN_CHROMINANCE_STRIDE_SEL (1 << 4)
+
+#define ODMA_OUT_CON1 0x0050
+#define ODMA_OUT_FRAME_ALPHA(_v) ((_v) << 24)
+#define ODMA_OUT_FRAME_ALPHA_MASK (0xff << 24)
+
+#define ODMA_DST_SIZE 0x0054
+#define ODMA_DST_HEIGHT(_v) ((_v) << 16)
+#define ODMA_DST_HEIGHT_MASK (0x3FFF << 16)
+#define ODMA_DST_WIDTH(_v) ((_v) << 0)
+#define ODMA_DST_WIDTH_MASK (0x3FFF << 0)
+
+#define ODMA_DST_OFFSET 0x0058
+#define ODMA_DST_OFFSET_Y(_v) ((_v) << 16)
+#define ODMA_DST_OFFSET_Y_MASK (0x1FFF << 16)
+#define ODMA_DST_OFFSET_X(_v) ((_v) << 0)
+#define ODMA_DST_OFFSET_X_MASK (0x1FFF << 0)
+
+#define ODMA_OUT_IMG_SIZE 0x005C
+#define ODMA_OUT_IMG_HEIGHT(_v) ((_v) << 16)
+#define ODMA_OUT_IMG_HEIGHT_MASK (0x1FFF << 16)
+#define ODMA_OUT_IMG_WIDTH(_v) ((_v) << 0)
+#define ODMA_OUT_IMG_WIDTH_MASK (0x1FFF << 0)
+
+#define ODMA_OUT_QOS_LUT07_00 0x0060
+#define ODMA_OUT_QOS_LUT15_08 0x0064
+#define ODMA_OUT_QOS_LUT(_n, _v) ((_v) << (4*(_n)))
+#define ODMA_OUT_QOS_LUT_MASK(_n) (0xF << (4*(_n)))
+
+#define ODMA_IN_BASE_ADDR_Y 0x0074
+#define ODMA_IN_BASE_ADDR_C 0x0094
+
+#define ODMA_SLICE0_BYTE_CNT 0x0100
+#define ODMA_SLICE1_BYTE_CNT 0x0104
+#define ODMA_SLICE2_BYTE_CNT 0x0108
+#define ODMA_SLICE3_BYTE_CNT 0x010C
+#define ODMA_SLICE4_BYTE_CNT 0x0110
+#define ODMA_SLICE5_BYTE_CNT 0x0114
+#define ODMA_SLICE6_BYTE_CNT 0x0118
+#define ODMA_FRAME_BYTE_CNT 0x011C
+#define ODMA_SLICE_BYTE_CNT(_n) (0x0100 + ((_n) * 0x4))
+
+#define ODMA_USB_TV_WB_CON 0x0120
+#define ODMA_USB_WB_PATH_SEL (1 << 3)
+#define USB_WB_PATH_MEM (0)
+#define USB_WB_PATH_OTF (1)
+#define ODMA_USB_WB_EN (1 << 2)
+
+#define ODMA_DEADLOCK_NUM 0x0300
+#define ODMA_DEADLOCK_VAL(_v) ((_v) << 1)
+#define ODMA_DEADLOCK_VAL_MASK (0x7FFFFFFF << 1)
+#define ODMA_DEADLOCK_EN (1 << 0)
+
+#define ODMA_BUS_CON 0x0304
+
+/* _n: [0,4], v: [0,1] */
+#define ODMA_DYNAMIC_GATING_EN 0x0354
+#define ODMA_DG_EN(_n, _v) ((_v) << (_n))
+#define ODMA_DG_EN_MASK(_n) (1 << (_n))
+#define ODMA_DG_EN_ALL (0x7FF << 0) /* 9820 */
+
+#define ODMA_CHAN_CONTROL 0x0360
+#define ODMA_CHAN_DATA 0x0364
+
+#define ODMA_CFG_ERR_STATE 0x0C00
+#define ODMA_CFG_ERR_GET(_v) (((_v) >> 0) & 0x7FF)
+// ADD field def
+
+/*
+ * 2 - DPU_WB_MUX.base
+ * Non-secure : 0x1289_0000
+ */
+#define DPU_WB_ENABLE 0x0000
+#define WB_SRSET (1 << 24)
+#define WB_SFR_CLOCK_GATE_EN (1 << 10)
+#define WB_SRAM_CLOCK_GATE_EN (1 << 9)
+#define WB_INT_CLOCK_GATE_EN (1 << 8)
+#define WB_ALL_CLOCK_GATE_EN_MASK (0x7 << 8)
+#define WB_SFR_UPDATE_FORCE (1 << 4)
+#define WB_QCHANNEL_EN (1 << 3)
+#define WB_OP_STATUS (1 << 2)
+
+#define DPU_WB_OUT_CON0 0x004C
+#define WB_RGB_TYPE_MASK (0x3 << 17)
+#define WB_RGB_TYPE(_v) ((_v) << 17)
+#define WB_CSC_R2Y_MASK (0x1 << 0)
+#define WB_CSC_R2Y(_v) ((_v) << 0)
+
+#define DPU_WB_OUT_CON1 0x0050
+#define WB_OUT_FRAME_ALPHA(_v) ((_v) << 24)
+#define WB_OUT_FRAME_ALPHA_MASK (0xff << 24)
+#define WB_UV_OFFSET_Y(_v) ((_v) << 5)
+#define WB_UV_OFFSET_Y_MASK (0x7 << 5)
+#define WB_UV_OFFSET_X(_v) ((_v) << 0)
+#define WB_UV_OFFSET_X_MASK (0x7 << 0)
+
+#define DPU_WB_DST_SIZE 0x0054
+#define WB_DST_HEIGHT(_v) ((_v) << 16)
+#define WB_DST_HEIGHT_MASK (0x1FFF << 16)
+#define WB_DST_WIDTH(_v) ((_v) << 0)
+#define WB_DST_WIDTH_MASK (0x1FFF << 0)
+
+#define DPU_WB_USB_TV_WB_SIZE 0x091C
+#define WB_FRAME_BYTE_CNT(_v) ((_v) << 0)
+#define WB_FRAME_BYTE_CNT_MASK (0xFFFFFFFF << 0)
+
+#define DPU_WB_USB_TV_WB_CON 0x0920
+#define WB_USB_WB_EN(_v) ((_v) << 2)
+#define WB_USB_WB_EN_MASK (0x1 << 2)
+#define WB_SWAP_OPTION(_v) ((_v) << 0)
+#define WB_SWAP_OPTION_MASK (0x3 << 0)
+
+/* _n: [0,6], v: [0,1] */
+#define DPU_WB_DYNAMIC_GATING_EN 0x0A54
+#define WB_DG_EN(_n, _v) ((_v) << (_n))
+#define WB_DG_EN_MASK(_n) (1 << (_n))
+#define WB_DG_EN_ALL (0xF << 0) /* 9820 */
+
+#define DPU_WB_CFG_ERR_STATE 0x0D08
+#define WB_CFG_ERR_GET(_v) (((_v) >> 0) & 0xF)
+#define WB_CFG_ERR_WRONG_PATH (1 << 3)
+#define WB_CFG_ERR_ODD_SIZE (1 << 2)
+#define WB_CFG_ERR_MAX_SIZE (1 << 1)
+#define WB_CFG_ERR_MIN_SIZE (1 << 0)
+
+/*
+ * DPP SFR base address : 0x19020000
+ * - DPP GF0 : 0x19021000
+ * - DPP GF1 : 0x19022000
+ * - DPP VG : 0x19023000
+ * - DPP VGF : 0x19024000
+ * - DPP VGS : 0x19025000
+ * - DPP VGRFS : 0x19026000
+ */
+#define DPP_ENABLE 0x0000
+#define DPP_SRSET (1 << 24)
+#define DPP_HDR_SEL (1 << 11)
+#define DPP_SFR_CLOCK_GATE_EN (1 << 10)
+#define DPP_SRAM_CLOCK_GATE_EN (1 << 9)
+#define DPP_INT_CLOCK_GATE_EN (1 << 8)
+#define DPP_ALL_CLOCK_GATE_EN_MASK (0x7 << 8)
+#define DPP_PSLVERR_EN (1 << 5)
+#define DPP_SFR_UPDATE_FORCE (1 << 4)
+#define DPP_QCHANNEL_EN (1 << 3)
+#define DPP_OP_STATUS (1 << 2)
+#define DPP_TZPC_FLAG (1 << 0)
+
+#define DPP_IRQ 0x0004
+#define DPP_CONFIG_ERROR (1 << 21)
+#define DPP_STATUS_FRAMEDONE_IRQ (1 << 16)
+#define DPP_ALL_IRQ_CLEAR (0x21 << 16)
+#define DPP_CONFIG_ERROR_MASK (1 << 6)
+#define DPP_IRQ_FRAMEDONE_MASK (1 << 1)
+#define DPP_ALL_IRQ_MASK (0x21 << 1)
+#define DPP_IRQ_ENABLE (1 << 0)
+
+#define DPP_IN_CON 0x0008
+#define DPP_CSC_TYPE(_v) ((_v) << 18)
+#define DPP_CSC_TYPE_MASK (3 << 18)
+#define DPP_CSC_RANGE(_v) ((_v) << 17)
+#define DPP_CSC_RANGE_MASK (1 << 17)
+#define DPP_CSC_MODE(_v) ((_v) << 16)
+#define DPP_CSC_MODE_MASK (1 << 16)
+#define DPP_DITH_MASK_SEL (1 << 5)
+#define DPP_DITH_MASK_SPIN (1 << 4)
+#define DPP_ALPHA_SEL(_v) ((_v) << 3)
+#define DPP_ALPHA_SEL_MASK (1 << 3)
+#define DPP_IMG_FORMAT(_v) ((_v) << 0)
+#define DPP_IMG_FORMAT_MASK (0x7 << 0)
+#define DPP_IMG_FORMAT_ARGB8888 (0 << 0)
+#define DPP_IMG_FORMAT_ARGB8101010 (1 << 0)
+#define DPP_IMG_FORMAT_YUV420_8P (2 << 0)
+#define DPP_IMG_FORMAT_YUV420_P010 (3 << 0)
+#define DPP_IMG_FORMAT_YUV420_8P2 (4 << 0)
+/* [9820] 3 kinds of YUV422 formats are added to DPP format */
+#define DPP_IMG_FORMAT_YUV422_8P (5 << 0)
+#define DPP_IMG_FORMAT_YUV422_P210 (6 << 0)
+#define DPP_IMG_FORMAT_YUV422_8P2 (7 << 0)
+#define DPP_IMG_SIZE 0x0018
+#define DPP_IMG_HEIGHT(_v) ((_v) << 16)
+#define DPP_IMG_HEIGHT_MASK (0x1FFF << 16)
+#define DPP_IMG_WIDTH(_v) ((_v) << 0)
+#define DPP_IMG_WIDTH_MASK (0x1FFF << 0)
+
+/* scaler configuration only */
+#define DPP_SCALED_IMG_SIZE 0x002C
+#define DPP_SCALED_IMG_HEIGHT(_v) ((_v) << 16)
+#define DPP_SCALED_IMG_HEIGHT_MASK (0x1FFF << 16)
+#define DPP_SCALED_IMG_WIDTH(_v) ((_v) << 0)
+#define DPP_SCALED_IMG_WIDTH_MASK (0x1FFF << 0)
+
+/*
+ * (00-01-02) : Reg0.L-Reg0.H-Reg1.L
+ * (10-11-12) : Reg1.H-Reg2.L-Reg2.H
+ * (20-21-22) : Reg3.L-Reg3.H-Reg4.L
+ */
+#define DPP_CSC_COEF0 0x0030
+#define DPP_CSC_COEF1 0x0034
+#define DPP_CSC_COEF2 0x0038
+#define DPP_CSC_COEF3 0x003C
+#define DPP_CSC_COEF4 0x0040
+#define DPP_CSC_COEF_H(_v) ((_v) << 16)
+#define DPP_CSC_COEF_H_MASK (0xFFFF << 16)
+#define DPP_CSC_COEF_L(_v) ((_v) << 0)
+#define DPP_CSC_COEF_L_MASK (0xFFFF << 0)
+#define DPP_CSC_COEF_XX(_n, _v) ((_v) << (0 + (16 * (_n))))
+#define DPP_CSC_COEF_XX_MASK(_n) (0xFFF << (0 + (16 * (_n))))
+
+#define DPP_MAIN_H_RATIO 0x0044
+#define DPP_H_RATIO(_v) ((_v) << 0)
+#define DPP_H_RATIO_MASK (0xFFFFFF << 0)
+
+#define DPP_MAIN_V_RATIO 0x0048
+#define DPP_V_RATIO(_v) ((_v) << 0)
+#define DPP_V_RATIO_MASK (0xFFFFFF << 0)
+
+#define DPP_Y_VCOEF_0A 0x0200
+#define DPP_Y_HCOEF_0A 0x0290
+#define DPP_C_VCOEF_0A 0x0400
+#define DPP_C_HCOEF_0A 0x0490
+#define DPP_SCL_COEF(_v) ((_v) << 0)
+#define DPP_SCL_COEF_MASK (0x7FF << 0)
+#define DPP_H_COEF(n, s, x) (0x290 + (n) * 0x4 + (s) * 0x24 + (x) * 0x200)
+#define DPP_V_COEF(n, s, x) (0x200 + (n) * 0x4 + (s) * 0x24 + (x) * 0x200)
+
+#define DPP_YHPOSITION 0x05B0
+#define DPP_YVPOSITION 0x05B4
+#define DPP_CHPOSITION 0x05B8
+#define DPP_CVPOSITION 0x05BC
+#define DPP_POS_I(_v) ((_v) << 20)
+#define DPP_POS_I_MASK (0xFFF << 20)
+#define DPP_POS_I_GET(_v) (((_v) >> 20) & 0xFFF)
+#define DPP_POS_F(_v) ((_v) << 0)
+#define DPP_POS_F_MASK (0xFFFFF << 0)
+#define DPP_POS_F_GET(_v) (((_v) >> 0) & 0xFFFFF)
+
+#define DPP_DYNAMIC_GATING_EN 0x0A54
+#define DPP_DG_EN(_n, _v) ((_v) << (_n))
+#define DPP_DG_EN_MASK(_n) (1 << (_n))
+#define DPP_DG_EN_ALL (0x7F << 0)
+
+#define DPP_LINECNT_CON 0x0D00
+#define DPP_LC_CAPTURE(_v) ((_v) << 2)
+#define DPP_LC_CAPTURE_MASK (1 << 2)
+#define DPP_LC_MODE(_V) ((_V) << 1)
+#define DPP_LC_MODE_MASK (1 << 1)
+#define DPP_LC_ENABLE(_v) ((_v) << 0)
+#define DPP_LC_ENABLE_MASK (1 << 0)
+
+#define DPP_LINECNT_VAL 0x0D04
+#define DPP_LC_COUNTER(_v) ((_v) << 0)
+#define DPP_LC_COUNTER_MASK (0x1FFF << 0)
+#define DPP_LC_COUNTER_GET(_v) (((_v) >> 0) & 0x1FFF)
+
+#define DPP_CFG_ERR_STATE 0x0D08
+#define DPP_CFG_ERR_SCL_POS (1 << 4)
+#define DPP_CFG_ERR_SCALE_RATIO (1 << 3)
+#define DPP_CFG_ERR_ODD_SIZE (1 << 2)
+#define DPP_CFG_ERR_MAX_SIZE (1 << 1)
+#define DPP_CFG_ERR_MIN_SIZE (1 << 0)
+#define DPP_CFG_ERR_GET(_v) (((_v) >> 0) & 0x1F)
+
+/* HDR section */
+/* Enable/Disable HDR processing */
+#define DPP_VGRF_HDR_CON 0x600
+#define DPP_TM_ON(_v) ((_v) << 3)
+#define DPP_TM_ON_MASK (1 << 3)
+#define DPP_GM_ON(_v) ((_v) << 2)
+#define DPP_GM_ON_MASK (1 << 2)
+#define DPP_EOTF_ON(_v) ((_v) << 1)
+#define DPP_EOTF_ON_MASK (1 << 1)
+#define DPP_HDR_ON(_v) ((_v) << 0)
+#define DPP_HDR_ON_MASK (1 << 0)
+
+/* EOTF */
+#define DPP_HDR_EOTF_X_AXIS_ADDR(_n) (((_n) / 2) * (0x4) + (0x610))
+#define DPP_HDR_EOTF_X_AXIS_VAL(_n, _v) \
+ (((_n) % (2)) ? (((_v) & 0x3FFF) << 16) : (((_v) & 0x3FFF) << 0))
+
+#define DPP_HDR_EOTF_Y_AXIS_ADDR(_n) (((_n) / 2) * (0x4) + (0x694))
+#define DPP_HDR_EOTF_Y_AXIS_VAL(_n, _v) \
+ (((_n) % (2)) ? (((_v) & 0x3FFF) << 16) : (((_v) & 0x3FFF) << 0))
+
+#define DPP_HDR_EOTF_MASK(_n) (((_n) % 2) ? (0x3FFF << 16) : (0x3FFF << 0))
+
+
+/* GM */
+#define DPP_HDR_GM_COEF_ADDR(_n) ((_n) * (0x4) + (0x720))
+#define DPP_HDR_GM_COEF_MASK (0x1FFFF << 0)
+
+/* TM */
+#define DPP_HDR_TM_X_AXIS_ADDR(_n) (((_n) / 2) * (0x4) + (0x750))
+#define DPP_HDR_TM_X_AXIS_VAL(_n, _v) \
+ (((_n) % (2)) ? (((_v) & 0x3FFF) << 16) : (((_v) & 0x3FFF) << 0))
+
+#define DPP_HDR_TM_Y_AXIS_ADDR(_n) (((_n) / 2) * (0x4) + (0x794))
+#define DPP_HDR_TM_Y_AXIS_VAL(_n, _v) \
+ (((_n) % (2)) ? (((_v) & 0x3FFF) << 16) : (((_v) & 0x3FFF) << 0))
+
+#define DPP_HDR_TM_MASK(_n) (((_n) % 2) ? (0x3FFF << 16) : (0x3FFF << 0))
+
+#define DPP_VGRF_HDR_EOTF_X_AXIS_0 0x0610
+#define DPP_VGRF_HDR_EOTF_X_AXIS_1 0x0614
+#define DPP_VGRF_HDR_EOTF_X_AXIS_2 0x0618
+#define DPP_VGRF_HDR_EOTF_X_AXIS_3 0x061C
+#define DPP_VGRF_HDR_EOTF_X_AXIS_4 0x0620
+#define DPP_VGRF_HDR_EOTF_X_AXIS_5 0x0624
+#define DPP_VGRF_HDR_EOTF_X_AXIS_6 0x0628
+#define DPP_VGRF_HDR_EOTF_X_AXIS_7 0x062C
+#define DPP_VGRF_HDR_EOTF_X_AXIS_8 0x0630
+#define DPP_VGRF_HDR_EOTF_X_AXIS_9 0x0634
+#define DPP_VGRF_HDR_EOTF_X_AXIS_10 0x0638
+#define DPP_VGRF_HDR_EOTF_X_AXIS_11 0x063C
+#define DPP_VGRF_HDR_EOTF_X_AXIS_12 0x0640
+#define DPP_VGRF_HDR_EOTF_X_AXIS_13 0x0644
+#define DPP_VGRF_HDR_EOTF_X_AXIS_14 0x0648
+#define DPP_VGRF_HDR_EOTF_X_AXIS_15 0x064C
+#define DPP_VGRF_HDR_EOTF_X_AXIS_16 0x0650
+#define DPP_VGRF_HDR_EOTF_X_AXIS_17 0x0654
+#define DPP_VGRF_HDR_EOTF_X_AXIS_18 0x0658
+#define DPP_VGRF_HDR_EOTF_X_AXIS_19 0x065C
+#define DPP_VGRF_HDR_EOTF_X_AXIS_20 0x0660
+#define DPP_VGRF_HDR_EOTF_X_AXIS_21 0x0664
+#define DPP_VGRF_HDR_EOTF_X_AXIS_22 0x0668
+#define DPP_VGRF_HDR_EOTF_X_AXIS_23 0x066C
+#define DPP_VGRF_HDR_EOTF_X_AXIS_24 0x0670
+#define DPP_VGRF_HDR_EOTF_X_AXIS_25 0x0674
+#define DPP_VGRF_HDR_EOTF_X_AXIS_26 0x0678
+#define DPP_VGRF_HDR_EOTF_X_AXIS_27 0x067C
+#define DPP_VGRF_HDR_EOTF_X_AXIS_28 0x0680
+#define DPP_VGRF_HDR_EOTF_X_AXIS_29 0x0684
+#define DPP_VGRF_HDR_EOTF_X_AXIS_30 0x0688
+#define DPP_VGRF_HDR_EOTF_X_AXIS_31 0x068C
+#define DPP_VGRF_HDR_EOTF_X_AXIS_32 0x0690
+
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_0 0x0694
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_1 0x0698
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_2 0x069C
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_3 0x06A0
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_4 0x06A4
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_5 0x06A8
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_6 0x06AC
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_7 0x06B0
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_8 0x06B4
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_9 0x06B8
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_10 0x06BC
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_11 0x06C0
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_12 0x06C4
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_13 0x06C8
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_14 0x06CC
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_15 0x06D0
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_16 0x06D4
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_17 0x06D8
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_18 0x06DC
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_19 0x06E0
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_20 0x06E4
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_21 0x06E8
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_22 0x06EC
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_23 0x06F0
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_24 0x06F4
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_25 0x06F8
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_26 0x06FC
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_27 0x0700
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_28 0x0704
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_29 0x0708
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_30 0x070C
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_31 0x0710
+#define DPP_VGRF_HDR_EOTF_Y_AXIS_32 0x0714
+
+#define DPP_VGRF_HDR_GM_COEF_0_0 0x0720
+#define DPP_VGRF_HDR_GM_COEF_0_1 0x0724
+#define DPP_VGRF_HDR_GM_COEF_0_2 0x0728
+#define DPP_VGRF_HDR_GM_COEF_1_0 0x072C
+#define DPP_VGRF_HDR_GM_COEF_1_1 0x0730
+#define DPP_VGRF_HDR_GM_COEF_1_2 0x0734
+#define DPP_VGRF_HDR_GM_COEF_2_0 0x0738
+#define DPP_VGRF_HDR_GM_COEF_2_1 0x073C
+#define DPP_VGRF_HDR_GM_COEF_2_2 0x0740
+
+#define DPP_VGRF_HDR_TM_X_AXIS_0 0x0750
+#define DPP_VGRF_HDR_TM_X_AXIS_1 0x0754
+#define DPP_VGRF_HDR_TM_X_AXIS_2 0x0758
+#define DPP_VGRF_HDR_TM_X_AXIS_3 0x075C
+#define DPP_VGRF_HDR_TM_X_AXIS_4 0x0760
+#define DPP_VGRF_HDR_TM_X_AXIS_5 0x0764
+#define DPP_VGRF_HDR_TM_X_AXIS_6 0x0768
+#define DPP_VGRF_HDR_TM_X_AXIS_7 0x076C
+#define DPP_VGRF_HDR_TM_X_AXIS_8 0x0770
+#define DPP_VGRF_HDR_TM_X_AXIS_9 0x0774
+#define DPP_VGRF_HDR_TM_X_AXIS_10 0x0778
+#define DPP_VGRF_HDR_TM_X_AXIS_11 0x077C
+#define DPP_VGRF_HDR_TM_X_AXIS_12 0x0780
+#define DPP_VGRF_HDR_TM_X_AXIS_13 0x0784
+#define DPP_VGRF_HDR_TM_X_AXIS_14 0x0788
+#define DPP_VGRF_HDR_TM_X_AXIS_15 0x078C
+#define DPP_VGRF_HDR_TM_X_AXIS_16 0x0790
+
+#define DPP_VGRF_HDR_TM_Y_AXIS_0 0x0794
+#define DPP_VGRF_HDR_TM_Y_AXIS_1 0x0798
+#define DPP_VGRF_HDR_TM_Y_AXIS_2 0x079C
+#define DPP_VGRF_HDR_TM_Y_AXIS_3 0x07A0
+#define DPP_VGRF_HDR_TM_Y_AXIS_4 0x07A4
+#define DPP_VGRF_HDR_TM_Y_AXIS_5 0x07A8
+#define DPP_VGRF_HDR_TM_Y_AXIS_6 0x07AC
+#define DPP_VGRF_HDR_TM_Y_AXIS_7 0x07B0
+#define DPP_VGRF_HDR_TM_Y_AXIS_8 0x07B4
+#define DPP_VGRF_HDR_TM_Y_AXIS_9 0x07B8
+#define DPP_VGRF_HDR_TM_Y_AXIS_10 0x07BC
+#define DPP_VGRF_HDR_TM_Y_AXIS_11 0x07C0
+#define DPP_VGRF_HDR_TM_Y_AXIS_12 0x07C4
+#define DPP_VGRF_HDR_TM_Y_AXIS_13 0x07C8
+#define DPP_VGRF_HDR_TM_Y_AXIS_14 0x07CC
+#define DPP_VGRF_HDR_TM_Y_AXIS_15 0x07D0
+#define DPP_VGRF_HDR_TM_Y_AXIS_16 0x07D4
+
+#endif
--- /dev/null
+/* regs-dsim.h
+ *
+ * Register definition file for Samsung MIPI-DSIM driver
+ *
+ * Copyright (c) 2015 Samsung Electronics
+ * Seungbeom Park <sb1.park@samsung.com>
+ * Jiun Yu <jiun.yu@samsung.com>
+ * Seuni Park <seuni.park@samsung.com>
+ *
+ * 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 _REGS_DSIM_H
+#define _REGS_DSIM_H
+
+#define DSIM_VERSION (0x0)
+
+#define DSIM_SWRST (0x4)
+#define DSIM_DPHY_RST (1 << 16)
+#define DSIM_SWRST_FUNCRST (1 << 8)
+#define DSIM_SWRST_RESET (1 << 0)
+
+#define DSIM_LINK_STATUS0 (0x8)
+#define DSIM_LINK_STATUS0_VIDEO_MODE_STATUS_GET(x) ((x >> 24) & 0x1)
+#define DSIM_LINK_STATUS0_VM_LINE_CNT_GET(x) ((x >> 0) & 0x1fff)
+
+#define DSIM_LINK_STATUS1 (0xc)
+#define DSIM_LINK_STATUS1_CMD_MODE_STATUS_GET(x) ((x >> 26) & 0x1)
+
+#define DSIM_STATUS_IDLE 0
+#define DSIM_STATUS_ACTIVE 1
+
+#define DSIM_LINK_STATUS3 (0x14)
+#define DSIM_LINK_STATUS3_PLL_STABLE (1 << 0)
+
+#define DSIM_MIPI_STATUS (0x18)
+#define DSIM_MIPI_STATUS_FRM_PROCESSING (1 << 29)
+#define DSIM_MIPI_STATUS_FRM_DONE (1 << 28)
+#define DSIM_MIPI_STATUS_SHADOW_REG_UP_EN (1 << 25)
+#define DSIM_MIPI_STATUS_SHADOW_REG_UP_DONE (1 << 24)
+#define DSIM_MIPI_STATUS_INSTANT_OFF_REQ (1 << 21)
+#define DSIM_MIPI_STATUS_INSTANT_OFF_ACK (1 << 20)
+#define DSIM_MIPI_STATUS_FRM_MASK (1 << 1)
+#define DSIM_MIPI_STATUS_TE (1 << 0)
+
+#define DSIM_DPHY_STATUS (0x1c)
+#define DSIM_DPHY_STATUS_TX_READY_HS_CLK (1 << 10)
+#define DSIM_DPHY_STATUS_ULPS_CLK (1 << 9)
+#define DSIM_DPHY_STATUS_STOP_STATE_CLK (1 << 8)
+#define DSIM_DPHY_STATUS_ULPS_DATA_LANE_GET(x) (((x) >> 4) & 0xf)
+#define DSIM_DPHY_STATUS_ULPS_DAT(_x) (((_x) & 0xf) << 4)
+#define DSIM_DPHY_STATUS_STOP_STATE_DAT(_x) (((_x) & 0xf) << 0)
+
+#define DSIM_CLK_CTRL (0x20)
+#define DSIM_CLK_CTRL_CLOCK_SEL (1 << 26)
+#define DSIM_CLK_CTRL_NONCONT_CLOCK_LANE (1 << 25)
+#define DSIM_CLK_CTRL_CLKLANE_ONOFF (1 << 24)
+#define DSIM_CLK_CTRL_TX_REQUEST_HSCLK (1 << 20)
+#define DSIM_CLK_CTRL_WORDCLK_EN (1 << 17)
+#define DSIM_CLK_CTRL_ESCCLK_EN (1 << 16)
+#define DSIM_CLK_CTRL_LANE_ESCCLK_EN(_x) ((_x) << 8)
+#define DSIM_CLK_CTRL_LANE_ESCCLK_EN_MASK (0x1f << 8)
+#define DSIM_CLK_CTRL_ESC_PRESCALER(_x) ((_x) << 0)
+#define DSIM_CLK_CTRL_ESC_PRESCALER_MASK (0xff << 0)
+
+#define DSIM_DESKEW_CTRL (0x24)
+#define DSIM_DESKEW_CTRL_HW_EN (1 << 15)
+#define DSIM_DESKEW_CTRL_HW_POSITION (1 << 14)
+#define DSIM_DESKEW_CTRL_HW_INTERVAL(_x) ((_x) << 2)
+#define DSIM_DESKEW_CTRL_HW_INTERVAL_MASK (0xfff << 2)
+#define DSIM_DESKEW_CTRL_HW_INIT (1 << 1)
+#define DSIM_DESKEW_CTRL_SW_SEND (1 << 0)
+
+/* Time out register */
+#define DSIM_TIMEOUT (0x28)
+#define DSIM_TIMEOUT_BTA_TOUT(_x) ((_x) << 16)
+#define DSIM_TIMEOUT_BTA_TOUT_MASK (0xffff << 16)
+#define DSIM_TIMEOUT_LPDR_TOUT(_x) ((_x) << 0)
+#define DSIM_TIMEOUT_LPDR_TOUT_MASK (0xffff << 0)
+
+/* Escape mode register */
+#define DSIM_ESCMODE (0x2c)
+#define DSIM_ESCMODE_STOP_STATE_CNT(_x) ((_x) << 21)
+#define DSIM_ESCMODE_STOP_STATE_CNT_MASK (0x7ff << 21)
+#define DSIM_ESCMODE_FORCE_STOP_STATE (1 << 20)
+#define DSIM_ESCMODE_FORCE_BTA (1 << 16)
+#define DSIM_ESCMODE_CMD_LPDT (1 << 7)
+#define DSIM_ESCMODE_TRIGGER_RST (1 << 4)
+#define DSIM_ESCMODE_TX_ULPS_DATA (1 << 3)
+#define DSIM_ESCMODE_TX_ULPS_DATA_EXIT (1 << 2)
+#define DSIM_ESCMODE_TX_ULPS_CLK (1 << 1)
+#define DSIM_ESCMODE_TX_ULPS_CLK_EXIT (1 << 0)
+
+#define DSIM_NUM_OF_TRANSFER (0x30)
+#define DSIM_NUM_OF_TRANSFER_PER_FRAME(_x) ((_x) << 0)
+#define DSIM_NUM_OF_TRANSFER_PER_FRAME_MASK (0xfffff << 0)
+#define DSIM_NUM_OF_TRANSFER_PER_FRAME_GET(x) (((x) >> 0) & 0xfffff)
+
+#define DSIM_UNDERRUN_CTRL (0x34)
+#define DSIM_UNDERRUN_CTRL_CM_UNDERRUN_LP_REF(_x) ((_x) << 0)
+#define DSIM_UNDERRUN_CTRL_CM_UNDERRUN_LP_REF_MASK (0xffff << 0)
+
+#define DSIM_THRESHOLD (0x38)
+#define DSIM_THRESHOLD_LEVEL(_x) ((_x) << 0)
+#define DSIM_THRESHOLD_LEVEL_MASK (0xffff << 0)
+
+/* Display image resolution register */
+#define DSIM_RESOL (0x3c)
+#define DSIM_RESOL_VRESOL(x) (((x) & 0x1fff) << 16)
+#define DSIM_RESOL_VRESOL_MASK (0x1fff << 16)
+#define DSIM_RESOL_HRESOL(x) (((x) & 0x1fff) << 0)
+#define DSIM_RESOL_HRESOL_MASK (0x1fff << 0)
+#define DSIM_RESOL_LINEVAL_GET(_v) (((_v) >> 16) & 0x1fff)
+#define DSIM_RESOL_HOZVAL_GET(_v) (((_v) >> 0) & 0x1fff)
+
+/* Main display Vporch register */
+#define DSIM_VPORCH (0x40)
+#define DSIM_VPORCH_VFP_CMD_ALLOW(_x) ((_x) << 24)
+#define DSIM_VPORCH_VFP_CMD_ALLOW_MASK (0xff << 24)
+#define DSIM_VPORCH_STABLE_VFP(_x) ((_x) << 16)
+#define DSIM_VPORCH_STABLE_VFP_MASK (0xff << 16)
+#define DSIM_VPORCH_VFP_TOTAL(_x) ((_x) << 8)
+#define DSIM_VPORCH_VFP_TOTAL_MASK (0xff << 8)
+#define DSIM_VPORCH_VBP(_x) ((_x) << 0)
+#define DSIM_VPORCH_VBP_MASK (0xff << 0)
+
+/* Main display Hporch register */
+#define DSIM_HPORCH (0x44)
+#define DSIM_HPORCH_HFP(_x) ((_x) << 16)
+#define DSIM_HPORCH_HFP_MASK (0xffff << 16)
+#define DSIM_HPORCH_HBP(_x) ((_x) << 0)
+#define DSIM_HPORCH_HBP_MASK (0xffff << 0)
+
+/* Main display sync area register */
+#define DSIM_SYNC (0x48)
+#define DSIM_SYNC_VSA(_x) ((_x) << 16)
+#define DSIM_SYNC_VSA_MASK (0xff << 16)
+#define DSIM_SYNC_HSA(_x) ((_x) << 0)
+#define DSIM_SYNC_HSA_MASK (0xffff << 0)
+
+/* Configuration register */
+#define DSIM_CONFIG (0x4c)
+#define DSIM_CONFIG_PLL_CLOCK_GATING (1 << 30)
+#define DSIM_CONFIG_PLL_SLEEP (1 << 29)
+#define DSIM_CONFIG_PHY_SELECTION (1 << 28)
+#define DSIM_CONFIG_SYNC_INFORM (1 << 27)
+#define DSIM_CONFIG_BURST_MODE (1 << 26)
+#define DSIM_CONFIG_LP_FORCE_EN (1 << 24)
+#define DSIM_CONFIG_HSE_DISABLE (1 << 23)
+#define DSIM_CONFIG_HFP_DISABLE (1 << 22)
+#define DSIM_CONFIG_HBP_DISABLE (1 << 21)
+#define DSIM_CONFIG_HSA_DISABLE (1 << 20)
+#define DSIM_CONFIG_CPRS_EN (1 << 19)
+#define DSIM_CONFIG_VIDEO_MODE (1 << 18)
+#define DSIM_CONFIG_DISPLAY_MODE_GET(_v) (((_v) >> 18) & 0x1)
+#define DSIM_CONFIG_VC_ID(_x) ((_x) << 15)
+#define DSIM_CONFIG_VC_ID_MASK (0x3 << 15)
+#define DSIM_CONFIG_PIXEL_FORMAT(_x) ((_x) << 9)
+#define DSIM_CONFIG_PIXEL_FORMAT_MASK (0x3f << 9)
+#define DSIM_CONFIG_PER_FRAME_READ_EN (1 << 8)
+#define DSIM_CONFIG_EOTP_EN (1 << 7)
+#define DSIM_CONFIG_NUM_OF_DATA_LANE(_x) ((_x) << 5)
+#define DSIM_CONFIG_NUM_OF_DATA_LANE_MASK (0x3 << 5)
+#define DSIM_CONFIG_LANES_EN(_x) (((_x) & 0x1f) << 0)
+#define DSIM_CONFIG_CLK_LANES_EN (1 << 0)
+
+/* Interrupt source register */
+#define DSIM_INTSRC (0x50)
+#define DSIM_INTSRC_PLL_STABLE (1 << 31)
+#define DSIM_INTSRC_SW_RST_RELEASE (1 << 30)
+#define DSIM_INTSRC_SFR_PL_FIFO_EMPTY (1 << 29)
+#define DSIM_INTSRC_SFR_PH_FIFO_EMPTY (1 << 28)
+#define DSIM_INTSRC_SFR_PH_FIFO_OVERFLOW (1 << 27)
+#define DSIM_INTSRC_SW_DESKEW_DONE (1 << 26)
+#define DSIM_INTSRC_BUS_TURN_OVER (1 << 25)
+#define DSIM_INTSRC_FRAME_DONE (1 << 24)
+#define DSIM_INTSRC_INVALID_SFR_VALUE (1 << 23)
+#define DSIM_INTSRC_ABNRMAL_CMD_ST (1 << 22)
+#define DSIM_INTSRC_LPRX_TOUT (1 << 21)
+#define DSIM_INTSRC_BTA_TOUT (1 << 20)
+#define DSIM_INTSRC_UNDER_RUN (1 << 19)
+#define DSIM_INTSRC_RX_DATA_DONE (1 << 18)
+#define DSIM_INTSRC_RX_TE (1 << 17)
+#define DSIM_INTSRC_RX_ACK (1 << 16)
+#define DSIM_INTSRC_ERR_RX_ECC (1 << 15)
+#define DSIM_INTSRC_RX_CRC (1 << 14)
+#define DSIM_INTSRC_VT_STATUS (1 << 13)
+
+/* Interrupt mask register */
+#define DSIM_INTMSK (0x54)
+#define DSIM_INTMSK_PLL_STABLE (1 << 31)
+#define DSIM_INTMSK_SW_RST_RELEASE (1 << 30)
+#define DSIM_INTMSK_SFR_PL_FIFO_EMPTY (1 << 29)
+#define DSIM_INTMSK_SFR_PH_FIFO_EMPTY (1 << 28)
+#define DSIM_INTMSK_SFR_PH_FIFO_OVERFLOW (1 << 27)
+#define DSIM_INTMSK_SW_DESKEW_DONE (1 << 26)
+#define DSIM_INTMSK_BUS_TURN_OVER (1 << 25)
+#define DSIM_INTMSK_FRAME_DONE (1 << 24)
+#define DSIM_INTMSK_INVALID_SFR_VALUE (1 << 23)
+#define DSIM_INTMSK_ABNRMAL_CMD_ST (1 << 22)
+#define DSIM_INTMSK_LPRX_TOUT (1 << 21)
+#define DSIM_INTMSK_BTA_TOUT (1 << 20)
+#define DSIM_INTMSK_UNDER_RUN (1 << 19)
+#define DSIM_INTMSK_RX_DATA_DONE (1 << 18)
+#define DSIM_INTMSK_RX_TE (1 << 17)
+#define DSIM_INTMSK_RX_ACK (1 << 16)
+#define DSIM_INTMSK_ERR_RX_ECC (1 << 15)
+#define DSIM_INTMSK_RX_CRC (1 << 14)
+#define DSIM_INTMSK_VT_STATUS (1 << 13)
+
+/* Packet Header FIFO register */
+#define DSIM_PKTHDR (0x58)
+#define DSIM_PKTHDR_BTA_TYPE(_x) ((_x) << 24)
+#define DSIM_PKTHDR_DATA1(_x) ((_x) << 16)
+#define DSIM_PKTHDR_DATA0(_x) ((_x) << 8)
+#define DSIM_PKTHDR_ID(_x) ((_x) << 0)
+#define DSIM_PKTHDR_DATA (0x1ffffff << 0)
+
+/* Payload FIFO register */
+#define DSIM_PAYLOAD (0x5c)
+
+/* Read FIFO register */
+#define DSIM_RXFIFO (0x60)
+
+/* SFR control Register for Stanby & Shadow*/
+#define DSIM_SFR_CTRL (0x64)
+#define DSIM_SFR_CTRL_SHADOW_REG_READ_EN (1 << 1)
+#define DSIM_SFR_CTRL_SHADOW_EN (1 << 0)
+
+/* FIFO status and control register */
+#define DSIM_FIFOCTRL (0x68)
+#define DSIM_FIFOCTRL_NUMBER_OF_PH_SFR(_x) (((_x) & 0x3f) << 16)
+#define DSIM_FIFOCTRL_NUMBER_OF_PH_SFR_GET(x) (((x) >> 16) & 0x3f)
+#define DSIM_FIFOCTRL_EMPTY_RX (1 << 12)
+#define DSIM_FIFOCTRL_FULL_PH_SFR (1 << 11)
+#define DSIM_FIFOCTRL_EMPTY_PH_SFR (1 << 10)
+#define DSIM_FIFOCTRL_FULL_PL_SFR (1 << 9)
+#define DSIM_FIFOCTRL_EMPTY_PL_SFR (1 << 8)
+#define DSIM_FIFOCTRL_INIT_RX (1 << 2)
+#define DSIM_FIFOCTRL_INIT_PL_SFR (1 << 1)
+#define DSIM_FIFOCTRL_INIT_PH_SFR (1 << 0)
+
+#define DSIM_LP_SCATTER (0x6c)
+#define DSIM_LP_SCATTER_PATTERN(_x) ((_x) << 16)
+#define DSIM_LP_SCATTER_PATTERN_MASK (0xffff << 16)
+#define DSIM_LP_SCATTER_EN (1 << 0)
+
+#define DSIM_S3D_CTRL (0x70)
+#define DSIM_S3D_CTRL_3D_PRESENT (1 << 11)
+#define DSIM_S3D_CTRL_3D_ORDER (1 << 5)
+#define DSIM_S3D_CTRL_3D_VSYNC (1 << 4)
+#define DSIM_S3D_CTRL_3D_FORMAT(_x) (((_x) & 0x3) << 2)
+#define DSIM_S3D_CTRL_3D_FORMAT_GET(x) (((x) >> 2) & 0x3)
+#define DSIM_S3D_CTRL_3D_MODE(_x) (((_x) & 0x3) << 0)
+#define DSIM_S3D_CTRL_3D_MODE_GET(x) (((x) >> 0) & 0x3)
+
+/* Multi slice setting register*/
+#define DSIM_CPRS_CTRL (0x74)
+#define DSIM_CPRS_CTRL_MULI_SLICE_PACKET (1 << 3)
+#define DSIM_CPRS_CTRL_NUM_OF_SLICE(_x) ((_x) << 0)
+#define DSIM_CPRS_CTRL_NUM_OF_SLICE_MASK (0x7 << 0)
+#define DSIM_CPRS_CTRL_NUM_OF_SLICE_GET(x) (((x) >> 0) & 0x7)
+
+/*Slice01 size register*/
+#define DSIM_SLICE01 (0x78)
+#define DSIM_SLICE01_SIZE_OF_SLICE1(_x) ((_x) << 16)
+#define DSIM_SLICE01_SIZE_OF_SLICE1_MASK (0x1fff << 16)
+#define DSIM_SLICE01_SIZE_OF_SLICE1_GET(x) (((x) >> 16) & 0x1fff)
+#define DSIM_SLICE01_SIZE_OF_SLICE0(_x) ((_x) << 0)
+#define DSIM_SLICE01_SIZE_OF_SLICE0_MASK (0x1fff << 0)
+#define DSIM_SLICE01_SIZE_OF_SLICE0_GET(x) (((x) >> 0) & 0x1fff)
+
+/*Slice23 size register*/
+#define DSIM_SLICE23 (0x7c)
+#define DSIM_SLICE23_SIZE_OF_SLICE3(_x) ((_x) << 16)
+#define DSIM_SLICE23_SIZE_OF_SLICE3_MASK (0x1fff << 16)
+#define DSIM_SLICE23_SIZE_OF_SLICE3_GET(x) (((x) >> 16) & 0x1fff)
+#define DSIM_SLICE23_SIZE_OF_SLICE2(_x) ((_x) << 0)
+#define DSIM_SLICE23_SIZE_OF_SLICE2_MASK (0x1fff << 0)
+#define DSIM_SLICE23_SIZE_OF_SLICE2_GET(x) (((x) >> 0) & 0x1fff)
+
+/* Command configuration register */
+#define DSIM_CMD_CONFIG (0x80)
+#define DSIM_CMD_CONFIG_PKT_GO_RDY (1 << 17)
+#define DSIM_CMD_CONFIG_PKT_GO_EN (1 << 16)
+#define DSIM_CMD_CONFIG_MULTI_CMD_PKT_EN (1 << 8)
+#define DSIM_CMD_CONFIG_MULTI_PKT_CNT(_x) ((_x) << 0)
+#define DSIM_CMD_CONFIG_MULTI_PKT_CNT_MASK (0x3f << 0)
+
+/* TE based command register*/
+#define DSIM_CMD_TE_CTRL0 (0x84)
+#define DSIM_CMD_TE_CTRL0_TIME_STABLE_VFP(_x) ((_x) << 0)
+#define DSIM_CMD_TE_CTRL0_TIME_STABLE_VFP_MASK (0xffff << 0)
+
+/* TE based command register*/
+#define DSIM_CMD_TE_CTRL1 (0x88)
+#define DSIM_CMD_TE_CTRL1_TIME_TE_PROTECT_ON(_x) ((_x) << 16)
+#define DSIM_CMD_TE_CTRL1_TIME_TE_PROTECT_ON_MASK (0xffff << 16)
+#define DSIM_CMD_TE_CTRL1_TIME_TE_TOUT(_x) ((_x) << 0)
+#define DSIM_CMD_TE_CTRL1_TIME_TE_TOUT_MASK (0xffff << 0)
+
+/*Command Mode Status register*/
+#define DSIM_CMD_STATUS (0x8c)
+#define DSIM_CMD_STATUS_ABNORMAL_CAUSE_ST_GET(x) (((x) >> 0) & 0xff)
+
+#define DSIM_VIDEO_TIMER (0x90)
+#define DSIM_VIDEO_TIMER_COMPENSATE(_x) ((_x) << 8)
+#define DSIM_VIDEO_TIMER_COMPENSATE_MASK (0xffffff << 8)
+#define DSIM_VIDEO_TIMER_VSTATUS_INTR_SEL(_x) ((_x) << 1)
+#define DSIM_VIDEO_TIMER_VSTATUS_INTR_SEL_MASK (0x3 << 1)
+#define DSIM_VIDEO_TIMER_SYNC_MODE (1 << 0)
+
+/*BIST generation register*/
+#define DSIM_BIST_CTRL0 (0x94)
+#define DSIM_BIST_CTRL0_BIST_TE_INTERVAL(_x) ((_x) << 8)
+#define DSIM_BIST_CTRL0_BIST_TE_INTERVAL_MASK (0xffffff << 8)
+#define DSIM_BIST_CTRL0_BIST_PTRN_MOVE_EN (1 << 4)
+#define DSIM_BIST_CTRL0_BIST_PTRN_MODE(_x) ((_x) << 1)
+#define DSIM_BIST_CTRL0_BIST_PTRN_MODE_MASK (0x7 << 1)
+#define DSIM_BIST_CTRL0_BIST_EN (1 << 0)
+
+/*BIST generation register*/
+#define DSIM_BIST_CTRL1 (0x98)
+#define DSIM_BIST_CTRL1_BIST_PTRN_PRBS7_SEED(_x) ((_x) << 24)
+#define DSIM_BIST_CTRL1_BIST_PTRN_PRBS7_SEED_MASK (0x7f << 24)
+#define DSIM_BIST_CTRL1_BIST_PTRN_USER_R(_x) ((_x) << 16)
+#define DSIM_BIST_CTRL1_BIST_PTRN_USER_R_MASK (0XFF << 16)
+#define DSIM_BIST_CTRL1_BIST_PTRN_USER_G(_x) ((_x) << 8)
+#define DSIM_BIST_CTRL1_BIST_PTRN_USER_G_MASK (0xFF << 8)
+#define DSIM_BIST_CTRL1_BIST_PTRN_USER_B(_x) ((_x) << 0)
+#define DSIM_BIST_CTRL1_BIST_PTRN_USER_B_MASK (0xFF << 0)
+
+/*DSIM to CSI loopback register*/
+#define DSIM_CSIS_LB (0x9C)
+#define DSIM_CSIS_LB_1BYTEPPI_MODE (1 << 9)
+#define DSIM_CSIS_LB_CSIS_LB_EN (1 << 8)
+#define DSIM_CSIS_LB_CSIS_PH(_x) ((_x) << 0)
+#define DSIM_CSIS_LB_CSIS_PH_MASK (0xff << 0)
+
+/* PLL control register */
+#define DSIM_PLLCTRL (0xa0)
+#define DSIM_PLLCTRL_PLL_EN (1 << 23)
+
+/* PLL timer register */
+#define DSIM_PLLTMR (0xac)
+
+/* IF CRC registers */
+#define DSIM_IF_CRC_CTRL0 (0xdc)
+#define DSIM_IF_CRC_FAIL (1 << 16)
+#define DSIM_IF_CRC_PASS (1 << 12)
+#define DSIM_IF_CRC_VALID (1 << 8)
+#define DSIM_IF_CRC_CMP_MODE (1 << 4)
+#define DSIM_IF_CRC_CLEAR (1 << 1)
+#define DSIM_IF_CRC_EN (1 << 0)
+
+#define DSIM_IF_CRC_CTRL1 (0xe0)
+#define DSIM_IF_CRC_REF_R(_x) ((_x) << 16)
+#define DSIM_IF_CRC_RESULT_R_MASK (0xffff << 0)
+#define DSIM_IF_CRC_RESULT_R_GET(x) (((x) >> 0) & 0xffff)
+
+#define DSIM_IF_CRC_CTRL2 (0xe4)
+#define DSIM_IF_CRC_REF_G(_x) ((_x) << 16)
+#define DSIM_IF_CRC_RESULT_G_MASK (0xffff << 0)
+#define DSIM_IF_CRC_RESULT_G_GET(x) (((x) >> 0) & 0xffff)
+
+#define DSIM_IF_CRC_CTRL3 (0xe8)
+#define DSIM_IF_CRC_REF_B(_x) ((_x) << 16)
+#define DSIM_IF_CRC_RESULT_B_MASK (0xffff << 0)
+#define DSIM_IF_CRC_RESULT_B_GET(x) (((x) >> 0) & 0xffff)
+
+/* SA CRC registers */
+#define DSIM_SA_CRC_CTRL0 (0xec)
+#define DSIM_SA_CRC_FAIL (1 << 16)
+#define DSIM_SA_CRC_PASS (1 << 12)
+#define DSIM_SA_CRC_VALID (1 << 8)
+#define DSIM_SA_CRC_CMP_MODE (1 << 4)
+#define DSIM_SA_CRC_CLEAR (1 << 1)
+#define DSIM_SA_CRC_EN (1 << 0)
+
+#define DSIM_SA_CRC_CTRL1 (0xf0)
+#define DSIM_SA_CRC_REF_LN0(_x) ((_x) << 16)
+#define DSIM_SA_CRC_RESULT_LN0_MASK (0xffff << 0)
+#define DSIM_SA_CRC_RESULT_LN0_GET(x) (((x) >> 0) & 0xffff)
+
+#define DSIM_SA_CRC_CTRL2 (0xf4)
+#define DSIM_SA_CRC_REF_LN1(_x) ((_x) << 16)
+#define DSIM_SA_CRC_RESULT_LN1_MASK (0xffff << 0)
+#define DSIM_SA_CRC_RESULT_LN1_GET(x) (((x) >> 0) & 0xffff)
+
+#define DSIM_SA_CRC_CTRL3 (0xf8)
+#define DSIM_SA_CRC_REF_LN2(_x) ((_x) << 16)
+#define DSIM_SA_CRC_RESULT_LN2_MASK (0xffff << 0)
+#define DSIM_SA_CRC_RESULT_LN2_GET(x) (((x) >> 0) & 0xffff)
+
+#define DSIM_SA_CRC_CTRL4 (0xfc)
+#define DSIM_SA_CRC_REF_LN3(_x) ((_x) << 16)
+#define DSIM_SA_CRC_RESULT_LN3_MASK (0xffff << 0)
+#define DSIM_SA_CRC_RESULT_LN3_GET(x) (((x) >> 0) & 0xffff)
+
+
+/*
+ * DPHY registers
+ */
+
+/* MK */
+#define DCPHY_TOP_M4S4 0x0000
+#define DCPHY_MOD_M4S0 0x1000
+
+
+/* DPHY BIAS setting */
+#define DSIM_PHY_BIAS_CON(_id) (0x0000 + (4 * (_id)))
+
+/* DPHY PLL setting */
+#define DSIM_PHY_PLL_CON(_id) (0x0100 + (4 * (_id)))
+#define DSIM_PHY_PLL_CON0 (0x0100)
+#define DSIM_PHY_PLL_CON1 (0x0104)
+#define DSIM_PHY_PLL_CON2 (0x0108)
+#define DSIM_PHY_PLL_CON3 (0x010C)
+#define DSIM_PHY_PLL_CON4 (0x0110)
+#define DSIM_PHY_PLL_CON5 (0x0114)
+#define DSIM_PHY_PLL_CON6 (0x0118)
+#define DSIM_PHY_PLL_CON7 (0x011C)
+#define DSIM_PHY_PLL_STAT0 (0x0120)
+
+/* PLL_CON0 */
+#define DSIM_PHY_PLL_EN(_x) (((_x) & 0x1) << 12)
+#define DSIM_PHY_PLL_EN_MASK (0x1 << 12)
+#define DSIM_PHY_PMS_S(_x) (((_x) & 0x7) << 8)
+#define DSIM_PHY_PMS_S_MASK (0x7 << 8)
+#define DSIM_PHY_PMS_P(_x) (((_x) & 0x3f) << 0)
+#define DSIM_PHY_PMS_P_MASK (0x3f << 0)
+
+/* PLL_CON1 */
+#define DSIM_PHY_PMS_K(_x) (((_x) & 0xffff) << 0)
+#define DSIM_PHY_PMS_K_MASK (0xffff << 0)
+
+/* PLL_CON2 */
+#define DSIM_PHY_PMS_M(_x) (((_x) & 0x3ff) << 0)
+#define DSIM_PHY_PMS_M_MASK (0x3ff << 0)
+
+#if defined(CONFIG_EXYNOS_DSIM_DITHER)
+/* PLL_CON2 */
+#define DSIM_PHY_DITHER_FOUT_MASK (0x1 << 13)
+#define DSIM_PHY_DITHER_FEED_EN (0x1 << 12)
+/* PLL_CON3 */
+#define DSIM_PHY_DITHER_MRR(_x) (((_x) & 0x3f) << 8)
+#define DSIM_PHY_DITHER_MRR_MASK (0x3f << 8)
+#define DSIM_PHY_DITHER_MFR(_x) (((_x) & 0xff) << 0)
+#define DSIM_PHY_DITHER_MFR_MASK (0xff << 0)
+/* PLL_CON4 */
+#define DSIM_PHY_DITHER_RSEL(_x) (((_x) & 0xf) << 12)
+#define DSIM_PHY_DITHER_RSEL_MASK (0xf << 12)
+#define DSIM_PHY_DITHER_EN (0x1 << 11)
+#define DSIM_PHY_DITHER_FSEL (0x1 << 10)
+#define DSIM_PHY_DITHER_BYPASS (0x1 << 9)
+#define DSIM_PHY_DITHER_AFC_ENB (0x1 << 8)
+#define DSIM_PHY_DITHER_EXTAFC(_x) (((_x) & 0x1f) << 0)
+#define DSIM_PHY_DITHER_EXTAFC_MASK (0x1f << 0)
+/* PLL_CON5 */
+#define DSIM_PHY_DITHER_ICP(_x) (((_x) & 0x3) << 4)
+#define DSIM_PHY_DITHER_ICP_MASK (0x3 << 4)
+#define DSIM_PHY_DITHER_SEL_PF(_x) (((_x) & 0x3) << 0)
+#define DSIM_PHY_DITHER_SEL_PF_MASK (0x3 << 0)
+#endif
+
+/* PLL_CON6 */
+/*
+ * WCLK_BUF_SFT_CNT = Roundup((Word Clock Period) / 38.46 + 2)
+ */
+#define DSIM_PHY_WCLK_BUF_SFT_CNT(_x) (((_x) & 0xf) << 8)
+#define DSIM_PHY_WCLK_BUF_SFT_CNT_MASK (0xf << 8)
+/* PLL_CON7 */
+#define DSIM_PHY_PLL_LOCK_CNT(_x) (((_x) & 0xffff) << 0)
+#define DSIM_PHY_PLL_LOCK_CNT_MASK (0xffff << 0)
+/* PLL_STAT0 */
+#define DSIM_PHY_PLL_LOCK_GET(x) (((x) >> 0) & 0x1)
+
+/* master clock lane General Control Register : GNR */
+#define DSIM_PHY_MC_GNR_CON(_id) (0x0300 + (4 * (_id)))
+#define DSIM_PHY_MC_GNR_CON0 (0x0300)
+#define DSIM_PHY_MC_GNR_CON1 (0x0304)
+/* GNR0 */
+#define DSIM_PHY_PHY_READY (0x1 << 1)
+#define DSIM_PHY_PHY_READY_GET(x) (((x) >> 1) & 0x1)
+#define DSIM_PHY_PHY_ENABLE (0x1 << 0)
+/* GNR1 */
+#define DSIM_PHY_T_PHY_READY(_x) (((_x) & 0xffff) << 0)
+#define DSIM_PHY_T_PHY_READY_MASK (0xffff << 0)
+
+
+/* master clock lane Analog Block Control Register : ANA */
+#define DSIM_PHY_MC_ANA_CON(_id) (0x0308 + (4 * (_id)))
+#define DSIM_PHY_MC_ANA_CON0 (0x0308)
+#define DSIM_PHY_MC_ANA_CON1 (0x030C)
+
+#define DSIM_PHY_EDGE_CON_EN (0x1 << 8)
+#define DSIM_PHY_RES_UP(_x) (((_x) & 0xf) << 4)
+#define DSIM_PHY_RES_UP_MASK (0xf << 4)
+#define DSIM_PHY_RES_DN(_x) (((_x) & 0xf) << 0)
+#define DSIM_PHY_RES_DN_MASK (0xf << 0)
+
+#define DSIM_PHY_DPDN_SWAP(_x) (((_x) & 0x) << 12)
+#define DSIM_PHY_DPDN_SWAP_MASK (0x1 << 12)
+
+
+/* master clock lane setting */
+#define DSIM_PHY_MC_TIME_CON0 (0x0330)
+#define DSIM_PHY_MC_TIME_CON1 (0x0334)
+#define DSIM_PHY_MC_TIME_CON2 (0x0338)
+#define DSIM_PHY_MC_TIME_CON3 (0x033C)
+#define DSIM_PHY_MC_TIME_CON4 (0x0340)
+#define DSIM_PHY_MC_DATA_CON0 (0x0344)
+#define DSIM_PHY_MC_DESKEW_CON0 (0x0350)
+
+/*
+ * master data lane setting : D0 ~ D3
+ * D0~D2 : COMBO
+ * D3 : DPHY
+ */
+#define DSIM_PHY_MD_GNR_CON0(_x) (0x0400 + (0x100 * (_x)))
+#define DSIM_PHY_MD_GNR_CON1(_x) (0x0404 + (0x100 * (_x)))
+#define DSIM_PHY_MD_ANA_CON0(_x) (0x0408 + (0x100 * (_x)))
+#define DSIM_PHY_MD_ANA_CON1(_x) (0x040C + (0x100 * (_x)))
+#define DSIM_PHY_MD_ANA_CON2(_x) (0x0410 + (0x100 * (_x)))
+#define DSIM_PHY_MD_ANA_CON3(_x) (0x0414 + (0x100 * (_x)))
+#define DSIM_PHY_MD_TIME_CON0(_x) (0x0430 + (0x100 * (_x)))
+#define DSIM_PHY_MD_TIME_CON1(_x) (0x0434 + (0x100 * (_x)))
+#define DSIM_PHY_MD_TIME_CON2(_x) (0x0438 + (0x100 * (_x)))
+#define DSIM_PHY_MD_TIME_CON3(_x) (0x043C + (0x100 * (_x)))
+#define DSIM_PHY_MD_TIME_CON4(_x) (0x0440 + (0x100 * (_x)))
+#define DSIM_PHY_MD_DATA_CON0(_x) (0x0444 + (0x100 * (_x)))
+
+/* master data lane(COMBO) setting : D0 */
+#define DSIM_PHY_MD0_TIME_CON0 (0x0430)
+#define DSIM_PHY_MD0_TIME_CON1 (0x0434)
+#define DSIM_PHY_MD0_TIME_CON2 (0x0438)
+#define DSIM_PHY_MD0_TIME_CON3 (0x043C)
+#define DSIM_PHY_MD0_TIME_CON4 (0x0440)
+#define DSIM_PHY_MD0_DATA_CON0 (0x0444)
+
+/* master data lane(COMBO) setting : D1 */
+#define DSIM_PHY_MD1_TIME_CON0 (0x0530)
+#define DSIM_PHY_MD1_TIME_CON1 (0x0534)
+#define DSIM_PHY_MD1_TIME_CON2 (0x0538)
+#define DSIM_PHY_MD1_TIME_CON3 (0x053C)
+#define DSIM_PHY_MD1_TIME_CON4 (0x0540)
+#define DSIM_PHY_MD1_DATA_CON0 (0x0544)
+
+/* master data lane(COMBO) setting : D2 */
+#define DSIM_PHY_MD2_TIME_CON0 (0x0630)
+#define DSIM_PHY_MD2_TIME_CON1 (0x0634)
+#define DSIM_PHY_MD2_TIME_CON2 (0x0638)
+#define DSIM_PHY_MD2_TIME_CON3 (0x063C)
+#define DSIM_PHY_MD2_TIME_CON4 (0x0640)
+#define DSIM_PHY_MD2_DATA_CON0 (0x0644)
+
+/* master data lane setting : D3 */
+#define DSIM_PHY_MD3_TIME_CON0 (0x0730)
+#define DSIM_PHY_MD3_TIME_CON1 (0x0734)
+#define DSIM_PHY_MD3_TIME_CON2 (0x0738)
+#define DSIM_PHY_MD3_TIME_CON3 (0x073C)
+#define DSIM_PHY_MD3_TIME_CON4 (0x0740)
+#define DSIM_PHY_MD3_DATA_CON0 (0x0744)
+
+
+/* macros for DPHY timing controls */
+/* MC/MD_TIME_CON0 */
+#define DSIM_PHY_HSTX_CLK_SEL (0x1 << 12)
+#define DSIM_PHY_TLPX(_x) (((_x) & 0xff) << 4)
+#define DSIM_PHY_TLPX_MASK (0xff << 4)
+/* MD only */
+#define DSIM_PHY_TLP_EXIT_SKEW(_x) (((_x) & 0x3) << 2)
+#define DSIM_PHY_TLP_EXIT_SKEW_MASK (0x3 << 2)
+#define DSIM_PHY_TLP_ENTRY_SKEW(_x) (((_x) & 0x3) << 0)
+#define DSIM_PHY_TLP_ENTRY_SKEW_MASK (0x3 << 0)
+
+/* MC/MD_TIME_CON1 */
+#define DSIM_PHY_TCLK_ZERO(_x) (((_x) & 0xff) << 8)
+#define DSIM_PHY_TCLK_ZERO_MASK (0xff << 8)
+#define DSIM_PHY_TCLK_PREPARE(_x) (((_x) & 0xff) << 0)
+#define DSIM_PHY_TCLK_PREPARE_MASK (0xff << 0)
+/* MD case */
+#define DSIM_PHY_THS_ZERO(_x) (((_x) & 0xff) << 8)
+#define DSIM_PHY_THS_ZERO_MASK (0xff << 8)
+#define DSIM_PHY_THS_PREPARE(_x) (((_x) & 0xff) << 0)
+#define DSIM_PHY_THS_PREPARE_MASK (0xff << 0)
+
+/* MC/MD_TIME_CON2 */
+#define DSIM_PHY_THS_EXIT(_x) (((_x) & 0xff) << 8)
+#define DSIM_PHY_THS_EXIT_MASK (0xff << 8)
+#define DSIM_PHY_TCLK_TRAIL(_x) (((_x) & 0xff) << 0)
+#define DSIM_PHY_TCLK_TRAIL_MASK (0xff << 0)
+/* MD case */
+#define DSIM_PHY_THS_TRAIL(_x) (((_x) & 0xff) << 0)
+#define DSIM_PHY_THS_TRAIL_MASK (0xff << 0)
+
+/* MC_TIME_CON3 */
+#define DSIM_PHY_TCLK_POST(_x) (((_x) & 0xff) << 0)
+#define DSIM_PHY_TCLK_POST_MASK (0xff << 0)
+/* MD_TIME_CON3 */
+#define DSIM_PHY_TTA_GET(_x) (((_x) & 0xf) << 4)
+#define DSIM_PHY_TTA_GET_MASK (0xf << 4)
+#define DSIM_PHY_TTA_GO(_x) (((_x) & 0xf) << 0)
+#define DSIM_PHY_TTA_GO_MASK (0xf << 0)
+
+/* MC/MD_TIME_CON4 */
+#define DSIM_PHY_ULPS_EXIT(_x) (((_x) & 0x3ff) << 0)
+#define DSIM_PHY_ULPS_EXIT_MASK (0x3ff << 0)
+
+/* MC_DATA_CON0 */
+#define DSIM_PHY_CLK_INV (0x1 << 1)
+/* MC_DATA_CON0 */
+#define DSIM_PHY_DATA_INV (0x1 << 1)
+
+/* MC_DESKEW_CON0 */
+#define DSIM_PHY_SKEWCAL_RUN_TIME(_x) (((_x) & 0xf) << 12)
+#define DSIM_PHY_SKEWCAL_RUN_TIME_MASK (0xf << 12)
+#define DSIM_PHY_SKEWCAL_INIT_RUN_TIME(_x) (((_x) & 0xf) << 8)
+#define DSIM_PHY_SKEWCAL_INIT_RUN_TIME_MASK (0xf << 8)
+#define DSIM_PHY_SKEWCAL_INIT_WAIT_TIME(_x) (((_x) & 0xf) << 4)
+#define DSIM_PHY_SKEWCAL_INIT_WAIT_TIME_MASK (0xf << 4)
+#define DSIM_PHY_SKEWCAL_EN (0x1 << 0)
+
+#endif /* _REGS_DSIM_H */
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Cursor Async 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 "dpp.h"
+
+void decon_set_cursor_reset(struct decon_device *decon,
+ struct decon_reg_data *regs)
+{
+ if (!decon->cursor.enabled)
+ return;
+
+ mutex_lock(&decon->cursor.lock);
+ decon->cursor.unmask = false;
+ memcpy(&decon->cursor.regs, regs, sizeof(struct decon_reg_data));
+ mutex_unlock(&decon->cursor.lock);
+}
+
+void decon_set_cursor_unmask(struct decon_device *decon, bool unmask)
+{
+ if (!decon->cursor.enabled)
+ return;
+
+ mutex_lock(&decon->cursor.lock);
+ decon->cursor.unmask = unmask;
+ mutex_unlock(&decon->cursor.lock);
+}
+
+static void decon_set_cursor_pos(struct decon_device *decon, int x, int y)
+{
+ if (!decon->cursor.enabled)
+ return;
+
+ if (x < 0)
+ x = 0;
+ if (y < 0)
+ y = 0;
+
+ decon->cursor.xpos = x;
+ decon->cursor.ypos = y;
+}
+
+static int decon_set_cursor_dpp_config(struct decon_device *decon,
+ struct decon_reg_data *regs)
+{
+ int i, ret = 0, err_cnt = 0;
+ struct v4l2_subdev *sd;
+ struct decon_win *win;
+
+ if (!decon->cursor.enabled)
+ return 0;
+
+ if (!regs->is_cursor_win[regs->cursor_win])
+ return -1;
+
+ i = regs->cursor_win;
+ win = decon->win[i];
+ if (!test_bit(win->dpp_id, &decon->cur_using_dpp))
+ return -2;
+
+ sd = decon->dpp_sd[win->dpp_id];
+ ret = v4l2_subdev_call(sd, core, ioctl,
+ DPP_WIN_CONFIG, ®s->dpp_config[i]);
+ if (ret) {
+ decon_err("failed to config (WIN%d : DPP%d)\n",
+ i, win->dpp_id);
+ regs->win_regs[i].wincon &= (~WIN_EN_F(i));
+ decon_reg_win_enable_and_update(decon->id, i, false);
+ if (regs->num_of_window != 0)
+ regs->num_of_window--;
+
+ clear_bit(win->dpp_id, &decon->cur_using_dpp);
+ set_bit(win->dpp_id, &decon->dpp_err_stat);
+ err_cnt++;
+ }
+ return err_cnt;
+}
+
+void dpu_cursor_win_update_config(struct decon_device *decon,
+ struct decon_reg_data *regs)
+{
+ struct decon_frame src, dst;
+ unsigned short cur = regs->cursor_win;
+
+ if (!decon->cursor.enabled)
+ return;
+
+ if (!decon->id) {
+ decon_dbg("%s, decon[%d] is not support cursor a-sync\n",
+ __func__, decon->id);
+ return;
+ }
+ if (!(regs->win_regs[cur].wincon & WIN_EN_F(cur))) {
+ decon_err("%s, window[%d] is not enabled\n", __func__, cur);
+ return;
+ }
+
+ if (!regs->is_cursor_win[cur]) {
+ decon_err("%s, window[%d] is not cursor layer\n",
+ __func__, cur);
+ return;
+ }
+
+ memcpy(&src, ®s->dpp_config[cur].src, sizeof(struct decon_frame));
+ memcpy(&dst, ®s->dpp_config[cur].dst, sizeof(struct decon_frame));
+
+ dst.x = decon->cursor.xpos;
+ dst.y = decon->cursor.ypos;
+
+ if ((dst.x + dst.w) > decon->lcd_info->xres)
+ dst.w = dst.w - ((dst.x + dst.w) - decon->lcd_info->xres);
+ if ((dst.y + dst.h) > decon->lcd_info->yres)
+ dst.h = dst.h - ((dst.y + dst.h) - decon->lcd_info->yres);
+
+ if (dst.w > SRC_WIDTH_MAX || dst.w < SRC_WIDTH_MIN ||
+ dst.h > SRC_HEIGHT_MAX || dst.h < SRC_HEIGHT_MIN) {
+ decon_info("not supported cursor: [%d] [%d %d] ",
+ cur, decon->lcd_info->xres,
+ decon->lcd_info->yres);
+ decon_info("src:(%d,%d,%d,%d)", src.x, src.y, src.w, src.h);
+ decon_info("dst:(%d,%d,%d,%d) (%d,%d,%d,%d)\n",
+ regs->dpp_config[cur].dst.x,
+ regs->dpp_config[cur].dst.y,
+ regs->dpp_config[cur].dst.w,
+ regs->dpp_config[cur].dst.h,
+ dst.x, dst.y, dst.w, dst.h);
+ memcpy(&dst, ®s->dpp_config[cur].dst,
+ sizeof(struct decon_frame));
+ }
+
+ regs->win_regs[cur].start_pos = win_start_pos(dst.x, dst.y);
+ regs->win_regs[cur].end_pos = win_end_pos(dst.x, dst.y, dst.w, dst.h);
+
+ if ((dst.w != regs->dpp_config[cur].dst.w) ||
+ (dst.h != regs->dpp_config[cur].dst.h)) {
+ decon_dbg("cursor update: [%d] [%d %d] src:(%d,%d,%d,%d)",
+ cur, decon->lcd_info->xres,
+ decon->lcd_info->yres,
+ src.x, src.y, src.w, src.h);
+ decon_dbg("dst:(%d,%d,%d,%d) (%d,%d,%d,%d)\n",
+ regs->dpp_config[cur].dst.x,
+ regs->dpp_config[cur].dst.y,
+ regs->dpp_config[cur].dst.w,
+ regs->dpp_config[cur].dst.h,
+ dst.x, dst.y, dst.w, dst.h);
+ }
+ regs->dpp_config[cur].src.w = dst.w;
+ regs->dpp_config[cur].src.h = dst.h;
+ regs->dpp_config[cur].dst.x = dst.x;
+ regs->dpp_config[cur].dst.y = dst.y;
+ regs->dpp_config[cur].dst.w = dst.w;
+ regs->dpp_config[cur].dst.h = dst.h;
+}
+
+int decon_set_cursor_win_config(struct decon_device *decon, int x, int y)
+{
+ int err_cnt = 0;
+ struct decon_reg_data *regs;
+ struct decon_mode_info psr;
+ int ret = 0;
+
+ DPU_EVENT_START();
+
+ if (!decon->cursor.enabled)
+ return 0;
+
+ decon_set_cursor_pos(decon, x, y);
+
+ mutex_lock(&decon->cursor.lock);
+
+ decon_to_psr_info(decon, &psr);
+
+ if (decon->state == DECON_STATE_OFF ||
+ decon->state == DECON_STATE_TUI) {
+ decon_info("decon%d: cursor win is not active :%d\n",
+ decon->id, decon->state);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ regs = &decon->cursor.regs;
+ if (!regs) {
+ decon_err("decon%d: cursor regs is null\n",
+ decon->id);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ if (!regs->is_cursor_win[regs->cursor_win]) {
+ decon_err("decon%d: cursor win(%d) disable\n",
+ decon->id, regs->cursor_win);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ if (!decon->cursor.unmask) {
+ decon_dbg("decon%d: cursor win(%d) update not ready\n",
+ decon->id, regs->cursor_win);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ if (psr.trig_mode == DECON_HW_TRIG)
+ decon_reg_set_trigger(decon->id, &psr, DECON_TRIG_DISABLE);
+
+ decon_reg_update_req_window_mask(decon->id, regs->cursor_win);
+
+ dpu_cursor_win_update_config(decon, regs);
+
+ DPU_EVENT_LOG_CURSOR(&decon->sd, regs);
+
+ err_cnt = decon_set_cursor_dpp_config(decon, regs);
+ if (err_cnt) {
+ decon_err("decon%d: cursor win(%d) during dpp_config(err_cnt:%d)\n",
+ decon->id, regs->cursor_win, err_cnt);
+ if (psr.trig_mode == DECON_HW_TRIG)
+ decon_reg_set_trigger(decon->id, &psr, DECON_TRIG_ENABLE);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ /* set decon registers for each window */
+ decon_reg_set_window_control(decon->id, regs->cursor_win,
+ ®s->win_regs[regs->cursor_win],
+ regs->win_regs[regs->cursor_win].winmap_state);
+
+ if (psr.trig_mode == DECON_HW_TRIG)
+ decon_reg_set_trigger(decon->id, &psr, DECON_TRIG_ENABLE);
+
+end:
+ if (psr.trig_mode == DECON_HW_TRIG)
+ decon->cursor.unmask = false;
+
+ mutex_unlock(&decon->cursor.lock);
+
+ DPU_EVENT_LOG(DPU_EVT_CURSOR_POS, &decon->sd, start);
+
+ return ret;
+}
+
+void dpu_init_cursor_mode(struct decon_device *decon)
+{
+ decon->cursor.enabled = false;
+
+ if (!IS_ENABLED(CONFIG_EXYNOS_CURSOR)) {
+ decon_info("display doesn't support cursor async mode\n");
+ return;
+ }
+
+ decon->cursor.enabled = true;
+ decon_info("display supports cursor async mode\n");
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Header file for Exynos DECON 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.
+*/
+
+#ifndef ___SAMSUNG_DECON_H__
+#define ___SAMSUNG_DECON_H__
+
+#include <linux/fb.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/kthread.h>
+#include <linux/pm_qos.h>
+#include <linux/delay.h>
+#include <linux/seq_file.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-core.h>
+#if defined(CONFIG_EXYNOS9610_BTS)
+#include <soc/samsung/bts.h>
+#endif
+#if defined(CONFIG_EXYNOS_ITMON)
+#include <soc/samsung/exynos-itmon.h>
+#endif
+#if defined(CONFIG_SUPPORT_LEGACY_ION)
+#include <linux/exynos_ion.h>
+#include <linux/ion.h>
+#endif
+#include <linux/exynos_iovmm.h>
+#include <linux/sync_file.h>
+
+/* TODO: SoC dependency will be removed */
+#if defined(CONFIG_SOC_EXYNOS9610)
+#include "./cal_9610/regs-decon.h"
+#include "./cal_9610/decon_cal.h"
+#endif
+
+#include "./panels/decon_lcd.h"
+#include "dsim.h"
+#include "displayport.h"
+#if defined(CONFIG_SUPPORT_LEGACY_FENCE)
+#include "../../../../dma-buf/sync_debug.h"
+#endif
+#include "hdr_metadata.h"
+
+#define SUCCESS_EXYNOS_SMC 0
+
+#if defined(CONFIG_SUPPORT_LEGACY_ION)
+extern struct ion_device *ion_exynos;
+#endif
+extern struct decon_device *decon_drvdata[MAX_DECON_CNT];
+extern int decon_log_level;
+extern int dpu_bts_log_level;
+extern int win_update_log_level;
+extern int dpu_mres_log_level;
+extern int decon_systrace_enable;
+extern struct decon_bts_ops decon_bts_control;
+
+#define DECON_MODULE_NAME "exynos-decon"
+#define MAX_NAME_SIZE 32
+#define MAX_PLANE_CNT 3
+#define MAX_PLANE_ADDR_CNT 4
+#define DECON_ENTER_HIBER_CNT 3
+#define DECON_ENTER_LPD_CNT 3
+#define MIN_BLK_MODE_WIDTH 144
+#define MIN_BLK_MODE_HEIGHT 16
+#define VSYNC_TIMEOUT_MSEC 200
+#define DEFAULT_BPP 32
+#define MIN_WIN_BLOCK_WIDTH 8
+#define MIN_WIN_BLOCK_HEIGHT 1
+#define FD_TRY_CNT 3
+#define VALID_FD_VAL 3
+#define DECON_TRACE_BUF_SIZE 40
+
+#define DECON_WIN_UPDATE_IDX MAX_DECON_WIN
+
+#ifndef KHZ
+#define KHZ (1000)
+#endif
+#ifndef MHZ
+#define MHZ (1000*1000)
+#endif
+#ifndef MSEC
+#define MSEC (1000)
+#endif
+
+#define SHADOW_UPDATE_TIMEOUT (300 * 1000) /* 300ms */
+#define IDLE_WAIT_TIMEOUT (50 * 1000) /* 50ms */
+#define RUN_WAIT_TIMEOUT IDLE_WAIT_TIMEOUT
+#define DSC_INIT_XMIT_DELAY 0x200
+
+#define EINT_PEND(x) ((x == 0) ? 2 : ((x == 1) ? 4 : 1))
+
+#define MAX_DSC_SLICE_CNT 4
+
+void dpu_debug_printk(const char *function_name, const char *format, ...);
+#define decon_err(fmt, ...) \
+ do { \
+ if (decon_log_level >= 3) { \
+ pr_err(pr_fmt(fmt), ##__VA_ARGS__); \
+ } \
+ } while (0)
+
+#define decon_warn(fmt, ...) \
+ do { \
+ if (decon_log_level >= 4) { \
+ pr_warn(pr_fmt(fmt), ##__VA_ARGS__); \
+ } \
+ } while (0)
+
+#define decon_info(fmt, ...) \
+ do { \
+ if (decon_log_level >= 6) \
+ pr_info(pr_fmt(fmt), ##__VA_ARGS__); \
+ } while (0)
+
+#define decon_dbg(fmt, ...) \
+ do { \
+ if (decon_log_level >= 7) \
+ pr_info(pr_fmt(fmt), ##__VA_ARGS__); \
+ } while (0)
+
+#define DPU_DEBUG_WIN(fmt, args...) \
+ do { \
+ if (win_update_log_level >= 7) \
+ dpu_debug_printk("WIN_UPDATE", fmt, ##args); \
+ } while (0)
+
+#define DPU_DEBUG_BTS(fmt, args...) \
+ do { \
+ if (dpu_bts_log_level >= 7) \
+ dpu_debug_printk("BTS", fmt, ##args); \
+ } while (0)
+
+#define DPU_INFO_BTS(fmt, args...) \
+ do { \
+ if (dpu_bts_log_level >= 6) \
+ dpu_debug_printk("BTS", fmt, ##args); \
+ } while (0)
+
+#define DPU_ERR_BTS(fmt, args...) \
+ do { \
+ if (dpu_bts_log_level >= 3) \
+ dpu_debug_printk("BTS", fmt, ##args); \
+ } while (0)
+
+#define DPU_DEBUG_MRES(fmt, args...) \
+ do { \
+ if (dpu_mres_log_level >= 7) \
+ dpu_debug_printk("MRES", fmt, ##args); \
+ } while (0)
+
+#define DPU_INFO_MRES(fmt, args...) \
+ do { \
+ if (dpu_mres_log_level >= 6) \
+ dpu_debug_printk("MRES", fmt, ##args); \
+ } while (0)
+
+#define DPU_ERR_MRES(fmt, args...) \
+ do { \
+ if (dpu_mres_log_level >= 3) \
+ dpu_debug_printk("MRES", fmt, ##args); \
+ } while (0)
+
+/* DECON systrace related */
+void tracing_mark_write(struct decon_device *decon, char id, char *str1, int value);
+#define decon_systrace(decon, id, str1, value) \
+ do { \
+ if (decon_systrace_enable) \
+ tracing_mark_write(decon, id, str1, value); \
+ } while (0)
+
+enum decon_hold_scheme {
+ /* should be set to this value in case of DSIM video mode */
+ DECON_VCLK_HOLD_ONLY = 0x00,
+ /* should be set to this value in case of DSIM command mode */
+ DECON_VCLK_RUNNING_VDEN_DISABLE = 0x01,
+ DECON_VCLK_HOLD_VDEN_DISABLE = 0x02,
+ /* should be set to this value in case of HDMI, eDP */
+ DECON_VCLK_NOT_AFFECTED = 0x03,
+};
+
+enum decon_win_alpha_coef {
+ BND_COEF_ZERO = 0x0,
+ BND_COEF_ONE = 0x1,
+ BND_COEF_AF = 0x2,
+ BND_COEF_1_M_AF = 0x3,
+ BND_COEF_AB = 0x4,
+ BND_COEF_1_M_AB = 0x5,
+ BND_COEF_PLNAE_ALPHA0 = 0x6,
+ BND_COEF_1_M_PLNAE_ALPHA0 = 0x7,
+ BND_COEF_PLNAE_ALPHA1 = 0x8,
+ BND_COEF_1_M_PLNAE_ALPHA1 = 0x9,
+ BND_COEF_ALPHA_MULT = 0xA,
+ BND_COEF_1_M_ALPHA_MULT = 0xB,
+};
+
+enum decon_win_alpha_sel {
+ ALPHA_MULT_SRC_SEL_ALPHA0 = 0,
+ ALPHA_MULT_SRC_SEL_ALPHA1 = 1,
+ ALPHA_MULT_SRC_SEL_AF = 2,
+ ALPHA_MULT_SRC_SEL_AB = 3,
+};
+
+enum decon_merger_mode {
+ DECON_LRM_NO = 0x0,
+ DECON_LRM_NOSWAP_RF = 0x4,
+ DECON_LRM_NOSWAP_LF = 0x5,
+ DECON_LRM_SWAP_RF = 0x6,
+ DECON_LRM_SWAP_LF = 0x7,
+};
+
+enum decon_te_src {
+ DECON_TE_FROM_DDI0 = 0,
+ DECON_TE_FROM_DDI1,
+ DECON_TE_FROM_DDI2,
+ DECON_TE_FROM_USB,
+};
+
+/*
+ * DECON_STATE_ON : disp power on, decon/dsim clock on & lcd on
+ * DECON_HIBER : disp power off, decon/dsim clock off & lcd on
+ * DECON_STATE_OFF : disp power off, decon/dsim clock off & lcd off
+ */
+enum decon_state {
+ DECON_STATE_INIT = 0,
+ DECON_STATE_ON,
+ DECON_STATE_DOZE,
+ DECON_STATE_HIBER,
+ DECON_STATE_DOZE_SUSPEND,
+ DECON_STATE_OFF,
+ DECON_STATE_TUI,
+};
+
+/* To find a proper CLOCK ratio */
+enum decon_clk_id {
+ CLK_ID_VCLK = 0,
+ CLK_ID_ECLK,
+ CLK_ID_ACLK,
+ CLK_ID_PCLK,
+ CLK_ID_DPLL, /* DPU_PLL */
+ CLK_ID_RESOLUTION,
+ CLK_ID_MIC_RATIO,
+ CLK_ID_DSC_RATIO,
+ CLK_ID_MAX,
+};
+
+enum decon_dsc_id {
+ DECON_DSC_ENC0 = 0x0,
+ DECON_DSC_ENC1 = 0x1,
+ DECON_DSC_ENC2 = 0x2,
+};
+
+enum decon_share_path {
+ SHAREPATH_DQE_USE = 0x0,
+ SHAREPATH_VG0_USE = 0x1,
+ SHAREPATH_VG1_USE = 0x2,
+ SHAREPATH_VGF1_USE = 0x3,
+ SHAREPATH_VGF0_USE = 0x4,
+};
+
+enum dpp_rotate {
+ DPP_ROT_NORMAL = 0x0,
+ DPP_ROT_XFLIP,
+ DPP_ROT_YFLIP,
+ DPP_ROT_180,
+ DPP_ROT_90,
+ DPP_ROT_90_XFLIP,
+ DPP_ROT_90_YFLIP,
+ DPP_ROT_270,
+};
+
+enum dpp_csc_eq {
+ /* eq_mode : 6bits [5:0] */
+ CSC_STANDARD_SHIFT = 0,
+ CSC_BT_601 = 0,
+ CSC_BT_709 = 1,
+ CSC_BT_2020 = 2,
+ CSC_DCI_P3 = 3,
+ /* eq_mode : 3bits [8:6] */
+ CSC_RANGE_SHIFT = 6,
+ CSC_RANGE_LIMITED = 0x0,
+ CSC_RANGE_FULL = 0x1,
+};
+
+enum dpp_comp_src {
+ DPP_COMP_SRC_NONE = 0,
+ DPP_COMP_SRC_G2D,
+ DPP_COMP_SRC_GPU
+};
+
+enum dpp_hdr_standard {
+ DPP_HDR_OFF = 0,
+ DPP_HDR_ST2084,
+ DPP_HDR_HLG,
+};
+
+struct decon_clocks {
+ unsigned long decon[CLK_ID_DPLL + 1];
+};
+
+struct decon_dma_buf_data {
+#if defined(CONFIG_SUPPORT_LEGACY_ION)
+ struct ion_handle *ion_handle;
+#endif
+ struct dma_buf *dma_buf;
+ struct dma_buf_attachment *attachment;
+ struct sg_table *sg_table;
+ dma_addr_t dma_addr;
+#if defined(CONFIG_SUPPORT_LEGACY_FENCE)
+ struct sync_file *fence;
+#else
+ struct dma_fence *fence;
+#endif
+};
+
+struct decon_win_rect {
+ int x;
+ int y;
+ u32 w;
+ u32 h;
+};
+
+struct decon_rect {
+ u32 left;
+ u32 top;
+ u32 right;
+ u32 bottom;
+};
+
+struct dpp_params {
+ dma_addr_t addr[MAX_PLANE_CNT];
+ enum dpp_rotate rot;
+ enum dpp_csc_eq eq_mode;
+ enum dpp_comp_src comp_src;
+ enum dpp_hdr_standard hdr_std;
+ u32 min_luminance;
+ u32 max_luminance;
+};
+
+struct decon_frame {
+ int x;
+ int y;
+ u32 w;
+ u32 h;
+ u32 f_w;
+ u32 f_h;
+};
+
+struct decon_win_config {
+ enum {
+ DECON_WIN_STATE_DISABLED = 0,
+ DECON_WIN_STATE_COLOR,
+ DECON_WIN_STATE_BUFFER,
+ DECON_WIN_STATE_UPDATE,
+ DECON_WIN_STATE_CURSOR,
+ DECON_WIN_STATE_MRESOL = 0x10000,
+ } state;
+
+ /* Reusability:This struct is used for IDMA and ODMA */
+ union {
+ __u32 color;
+ struct {
+ int fd_idma[3];
+ int acq_fence;
+ int rel_fence;
+ int plane_alpha;
+ enum decon_blending blending;
+ enum decon_idma_type idma_type;
+ enum decon_pixel_format format;
+ struct dpp_params dpp_parm;
+ /* no read area of IDMA */
+ struct decon_win_rect block_area;
+ struct decon_win_rect transparent_area;
+ struct decon_win_rect opaque_area;
+ /* source framebuffer coordinates */
+ struct decon_frame src;
+ };
+ };
+
+ /* destination OSD coordinates */
+ struct decon_frame dst;
+ bool protection;
+ bool compression;
+};
+
+struct decon_reg_data {
+ u32 num_of_window;
+ int plane_cnt[MAX_DECON_WIN + 1];
+ struct list_head list;
+ struct decon_rect blender_bg;
+ struct decon_win_config dpp_config[MAX_DECON_WIN + 1];
+ struct decon_win_rect block_rect[MAX_DECON_WIN];
+ struct decon_window_regs win_regs[MAX_DECON_WIN];
+ struct decon_dma_buf_data dma_buf_data[MAX_DECON_WIN + 1][MAX_PLANE_CNT];
+#if !defined(CONFIG_SUPPORT_LEGACY_FENCE)
+ struct dma_fence *retire_fence;
+#endif
+
+ /*
+ * If window update size is changed, that size has to be applied to
+ * DECON, DSIM and panel in case of below
+ * - full size -> partial size
+ * - partial size -> different partial size
+ * - partial size -> full size
+ *
+ * need_update flag indicates whether changes are applied to hw or not
+ */
+ bool need_update;
+ /* current update region */
+ struct decon_rect up_region;
+ /* protected contents playback */
+ bool protection[MAX_DECON_WIN + 1];
+ /* cursor async */
+ bool is_cursor_win[MAX_DECON_WIN];
+ int cursor_win;
+
+ bool mres_update;
+ u32 lcd_width;
+ u32 lcd_height;
+ int mres_idx;
+};
+
+struct decon_win_config_data {
+ int retire_fence;
+ int fd_odma;
+ struct decon_win_config config[MAX_DECON_WIN + 1];
+};
+
+enum hwc_ver {
+ HWC_INIT = 0,
+ HWC_1_0 = 1,
+ HWC_2_0 = 2,
+};
+
+struct decon_disp_info {
+ enum hwc_ver ver;
+ enum decon_psr_mode psr_mode;
+ struct lcd_mres_info mres_info;
+ u32 chip_ver;
+ unsigned char reverved[128];
+};
+
+struct dpu_size_info {
+ u32 w_in;
+ u32 h_in;
+ u32 w_out;
+ u32 h_out;
+};
+
+/**
+ * Display Subsystem event management status.
+ *
+ * These status labels are used internally by the DECON to indicate the
+ * current status of a device with operations.
+ */
+typedef enum dpu_event_type {
+ /* Related with FB interface */
+ DPU_EVT_BLANK = 0,
+ DPU_EVT_UNBLANK,
+ DPU_EVT_ACT_VSYNC,
+ DPU_EVT_DEACT_VSYNC,
+ DPU_EVT_WIN_CONFIG,
+ DPU_EVT_DISP_INFO,
+
+ /* Related with interrupt */
+ DPU_EVT_TE_INTERRUPT,
+ DPU_EVT_UNDERRUN,
+ DPU_EVT_DECON_FRAMEDONE,
+ DPU_EVT_DSIM_FRAMEDONE,
+ DPU_EVT_RSC_CONFLICT,
+
+ /* Related with async event */
+ DPU_EVT_UPDATE_HANDLER,
+ DPU_EVT_DSIM_COMMAND,
+ DPU_EVT_TRIG_MASK,
+ DPU_EVT_TRIG_UNMASK,
+ DPU_EVT_FENCE_RELEASE,
+ DPU_EVT_DECON_FRAMEDONE_WAIT,
+ DPU_EVT_DECON_SHUTDOWN,
+ DPU_EVT_DSIM_SHUTDOWN,
+ DPU_EVT_DECON_FRAMESTART,
+
+ /* Related with DPP */
+ DPU_EVT_DPP_WINCON,
+ DPU_EVT_DPP_FRAMEDONE,
+ DPU_EVT_DPP_STOP,
+ DPU_EVT_DPP_UPDATE_DONE,
+ DPU_EVT_DPP_SHADOW_UPDATE,
+ DPU_EVT_DPP_SUSPEND,
+ DPU_EVT_DPP_RESUME,
+
+ /* Related with PM */
+ DPU_EVT_DECON_SUSPEND,
+ DPU_EVT_DECON_RESUME,
+ DPU_EVT_ENTER_HIBER,
+ DPU_EVT_EXIT_HIBER,
+ DPU_EVT_DSIM_SUSPEND,
+ DPU_EVT_DSIM_RESUME,
+ DPU_EVT_ENTER_ULPS,
+ DPU_EVT_EXIT_ULPS,
+
+ DPU_EVT_LINECNT_ZERO,
+
+ /* write-back events */
+ DPU_EVT_WB_SET_BUFFER,
+ DPU_EVT_WB_SW_TRIGGER,
+
+ DPU_EVT_DMA_FRAMEDONE,
+ DPU_EVT_DMA_RECOVERY,
+
+ DPU_EVT_DECON_SET_BUFFER,
+ /* cursor async */
+ DPU_EVT_CURSOR_POS,
+ DPU_EVT_CURSOR_UPDATE,
+
+ /* window update */
+ DPU_EVT_WINUP_UPDATE_REGION,
+ DPU_EVT_WINUP_FLAGS,
+ DPU_EVT_WINUP_APPLY_REGION,
+
+ DPU_EVT_DOZE,
+ DPU_EVT_DOZE_SUSPEND,
+
+ DPU_EVT_MAX, /* End of EVENT */
+} dpu_event_t;
+
+/* Related with Cursor */
+struct disp_log_cursor {
+ u32 xpos;
+ u32 ypos;
+ ktime_t elapsed; /* End time - Start time */
+};
+
+/* Related with Fence */
+struct disp_log_fence {
+ u32 timeline_value;
+ int timeline_max;
+};
+
+/* Related with PM */
+struct disp_log_pm {
+ u32 pm_status; /* ACTIVE(1) or SUSPENDED(0) */
+ ktime_t elapsed; /* End time - Start time */
+};
+
+/* Related with S3CFB_WIN_CONFIG */
+struct decon_update_reg_data {
+ struct decon_window_regs win_regs[MAX_DECON_WIN];
+ struct decon_win_config win_config[MAX_DECON_WIN + 1];
+ struct decon_win_rect win;
+};
+
+/* Related with MIPI COMMAND read/write */
+#define DPU_CALLSTACK_MAX 10
+struct dsim_log_cmd_buf {
+ u32 id;
+ u8 buf;
+ void *caller[DPU_CALLSTACK_MAX];
+};
+
+/* Related with DPP */
+struct disp_log_dpp {
+ u32 id;
+ u32 start_cnt;
+ u32 done_cnt;
+ u32 comp_src;
+ struct decon_frame src;
+ struct decon_frame dst;
+};
+
+/* Related with window update information */
+struct disp_log_winup {
+ struct decon_frame req_region;
+ struct decon_frame adj_region;
+ struct decon_frame apl_region;
+ bool need_update;
+ bool reconfigure;
+};
+
+/**
+ * struct dpu_log - Display Subsystem Log
+ * This struct includes DECON/DSIM/DPP
+ */
+struct dpu_log {
+ ktime_t time;
+ dpu_event_t type;
+ union {
+ struct disp_log_dpp dpp;
+ struct decon_update_reg_data reg;
+ struct dsim_log_cmd_buf cmd_buf;
+ struct disp_log_pm pm;
+ struct disp_log_fence fence;
+ struct disp_log_cursor cursor;
+ struct disp_log_winup winup;
+ } data;
+};
+
+struct dpu_size_err_info {
+ ktime_t time;
+ struct dpu_size_info info;
+};
+
+/* Definitions below are used in the DECON */
+#define DPU_EVENT_LOG_MAX SZ_512
+#define DPU_EVENT_PRINT_MAX (DPU_EVENT_LOG_MAX >> 1)
+#define DPU_EVENT_LOG_RETRY 3
+typedef enum dpu_event_log_level_type {
+ DPU_EVENT_LEVEL_LOW = 0,
+ DPU_EVENT_LEVEL_HIGH,
+} dpu_log_level_t;
+
+/* APIs below are used in the DECON/DSIM/DPP driver */
+#define DPU_EVENT_START() ktime_t start = ktime_get()
+void DPU_EVENT_LOG(dpu_event_t type, struct v4l2_subdev *sd, ktime_t time);
+void DPU_EVENT_LOG_WINCON(struct v4l2_subdev *sd, struct decon_reg_data *regs);
+void DPU_EVENT_LOG_CMD(struct v4l2_subdev *sd, u32 cmd_id, unsigned long data);
+void DPU_EVENT_LOG_CURSOR(struct v4l2_subdev *sd, struct decon_reg_data *regs); /* cursor async */
+void DPU_EVENT_LOG_UPDATE_REGION(struct v4l2_subdev *sd,
+ struct decon_frame *req_region, struct decon_frame *adj_region);
+void DPU_EVENT_LOG_WINUP_FLAGS(struct v4l2_subdev *sd, bool need_update,
+ bool reconfigure);
+void DPU_EVENT_LOG_APPLY_REGION(struct v4l2_subdev *sd,
+ struct decon_rect *apl_rect);
+void DPU_EVENT_SHOW(struct seq_file *s, struct decon_device *decon);
+int decon_create_debugfs(struct decon_device *decon);
+void decon_destroy_debugfs(struct decon_device *decon);
+
+/* HDR information of panel */
+enum decon_hdr_type {
+ HDR_NONE = 0,
+ HDR_DOLBY_VISION = 1,
+ HDR_HDR10 = 2,
+ HDR_HLG = 3,
+};
+
+struct decon_hdr_capabilities {
+ unsigned int out_types[HDR_CAPA_NUM];
+};
+
+struct decon_hdr_capabilities_info {
+ int out_num;
+ int max_luminance;
+ int max_average_luminance;
+ int min_luminance;
+};
+
+struct decon_resources {
+ int irq;
+ void __iomem *regs;
+ void __iomem *ss_regs;
+ struct clk *aclk;
+ struct clk *dpll;
+ struct clk *pclk;
+ struct clk *eclk;
+ struct clk *eclk_leaf;
+ struct clk *vclk;
+ struct clk *vclk_leaf;
+ struct clk *busp;
+ struct clk *busd;
+ struct clk *busc;
+ struct clk *core;
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *hw_te_on;
+ struct pinctrl_state *hw_te_off;
+};
+
+struct decon_dt_info {
+ enum decon_psr_mode psr_mode;
+ enum decon_trig_mode trig_mode;
+ enum decon_dsi_mode dsi_mode;
+ enum decon_out_type out_type;
+ int out_idx[MAX_DSIM_CNT];
+ int max_win;
+ int dft_win;
+ int dft_idma;
+};
+
+struct decon_win {
+ struct decon_device *decon;
+ struct fb_info *fbinfo;
+
+ struct fb_videomode videomode;
+ struct decon_dma_buf_data dma_buf_data[MAX_PLANE_CNT];
+#if defined(CONFIG_FB_TEST)
+ struct decon_dma_buf_data fb_buf_data;
+#endif
+ int plane_cnt;
+
+ int idx;
+ int dpp_id;
+ u32 pseudo_palette[16];
+};
+/* cursor async */
+struct decon_user_window {
+ int x;
+ int y;
+};
+
+struct dpu_afbc_info {
+ dma_addr_t dma_addr[2];
+ struct dma_buf *dma_buf[2];
+ bool is_afbc[2];
+};
+
+struct decon_debug {
+ void __iomem *eint_pend;
+ struct dentry *debug_root;
+ struct dentry *debug_event;
+ struct dentry *debug_dump;
+ struct dentry *debug_bts;
+ struct dentry *debug_win;
+ struct dentry *debug_systrace;
+#if defined(CONFIG_DSIM_CMD_TEST)
+ struct dentry *debug_cmd;
+#endif
+ struct dentry *debug_recovery_cnt;
+ struct dentry *debug_cmd_lp_ref;
+ struct dentry *debug_mres;
+
+ struct dpu_log *event_log;
+ u32 event_log_cnt;
+ atomic_t event_log_idx;
+ dpu_log_level_t event_log_level;
+ struct dentry *debug_low_persistence;
+ struct dpu_afbc_info prev_afbc_info;
+ struct dpu_afbc_info cur_afbc_info;
+#if defined(CONFIG_SUPPORT_LEGACY_ION)
+ struct ion_handle *handle[MAX_DECON_WIN][MAX_PLANE_CNT];
+#else
+ struct dma_buf *dmabuf[MAX_DECON_WIN][MAX_PLANE_CNT];
+#endif
+ int prev_vgf_win_id[2];
+};
+
+struct decon_update_regs {
+ struct mutex lock;
+ struct list_head list;
+ struct list_head saved_list;
+ struct task_struct *thread;
+ struct kthread_worker worker;
+ struct kthread_work work;
+};
+
+struct decon_vsync {
+ wait_queue_head_t wait;
+ ktime_t timestamp;
+ bool active;
+ int irq_refcount;
+ struct mutex lock;
+ struct task_struct *thread;
+};
+
+struct decon_hiber {
+ struct mutex lock;
+ struct task_struct *thread;
+ struct kthread_worker worker;
+ struct kthread_work work;
+ atomic_t trig_cnt;
+ atomic_t block_cnt;
+ void __iomem *cam_status;
+ u32 enter_cnt;
+ u32 exit_cnt;
+ bool enabled;
+};
+
+struct decon_win_update {
+ bool enabled;
+ u32 rect_w;
+ u32 rect_h;
+ u32 hori_cnt;
+ u32 verti_cnt;
+ /* previous update region */
+ struct decon_rect prev_up_region;
+};
+
+struct decon_bts_ops {
+ void (*bts_init)(struct decon_device *decon);
+ void (*bts_calc_bw)(struct decon_device *decon,
+ struct decon_reg_data *regs);
+ void (*bts_update_bw)(struct decon_device *decon,
+ struct decon_reg_data *regs, u32 is_after);
+ void (*bts_acquire_bw)(struct decon_device *decon);
+ void (*bts_release_bw)(struct decon_device *decon);
+ void (*bts_deinit)(struct decon_device *decon);
+};
+
+struct decon_bts {
+ bool enabled;
+ u32 resol_clk;
+ u32 peak;
+ u32 total_bw;
+ u32 prev_total_bw;
+ u32 max_disp_freq;
+ u32 prev_max_disp_freq;
+#if defined(CONFIG_EXYNOS9610_BTS)
+ u32 bw[BTS_DPP_MAX];
+ /* each decon must know other decon's BW to get overall BW */
+ u32 ch_bw[3][BTS_DPU_MAX];
+ enum bts_bw_type type;
+ struct bts_decon_info bts_info;
+#endif
+ struct decon_bts_ops *ops;
+ struct pm_qos_request mif_qos;
+ struct pm_qos_request int_qos;
+ struct pm_qos_request disp_qos;
+ u32 scen_updated;
+};
+
+/* cursor async */
+struct decon_cursor {
+ struct decon_reg_data regs;
+ struct mutex lock;
+ u32 xpos;
+ u32 ypos;
+ bool unmask; /* if true, cursor unmask period */
+ bool enabled;
+};
+
+/* systrace */
+struct decon_systrace_data {
+ pid_t pid;
+};
+#if !defined(CONFIG_SUPPORT_LEGACY_FENCE)
+struct decon_fence {
+ char name[8];
+ u64 context;
+ atomic_t timeline;
+ spinlock_t lock;
+};
+#endif
+
+struct decon_device {
+ int id;
+ enum decon_state state;
+
+ unsigned long prev_used_dpp;
+ unsigned long cur_using_dpp;
+ unsigned long dpp_err_stat;
+
+ struct mutex lock;
+ struct mutex pm_lock;
+ spinlock_t slock;
+
+#if defined(CONFIG_SUPPORT_LEGACY_ION)
+ struct ion_client *ion_client;
+#endif
+
+#if defined(CONFIG_SUPPORT_LEGACY_FENCE)
+ struct sync_timeline *timeline;
+ int timeline_max;
+#endif
+
+ struct v4l2_subdev *out_sd[MAX_DSIM_CNT];
+ struct v4l2_subdev *dsim_sd[MAX_DSIM_CNT];
+ struct v4l2_subdev *dpp_sd[MAX_DPP_SUBDEV];
+ struct v4l2_subdev *displayport_sd;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_subdev sd;
+
+ struct device *dev;
+ struct decon_dt_info dt;
+ struct decon_win *win[MAX_DECON_WIN];
+ struct decon_resources res;
+ struct decon_debug d;
+ struct decon_update_regs up;
+ struct decon_vsync vsync;
+ struct decon_lcd *lcd_info;
+ struct decon_win_update win_up;
+ struct decon_hiber hiber;
+ struct decon_bts bts;
+ struct decon_cursor cursor;
+#if !defined(CONFIG_SUPPORT_LEGACY_FENCE)
+ struct decon_fence fence;
+#endif
+
+ int frame_cnt;
+ int frame_cnt_target;
+ wait_queue_head_t wait_vstatus;
+ int eint_status;
+
+ u32 prev_protection_bitmask;
+ unsigned long prev_aclk_khz;
+
+ atomic_t is_shutdown;
+ bool up_list_saved;
+
+#if defined(CONFIG_EXYNOS_ITMON)
+ struct notifier_block itmon_nb;
+ bool notified;
+#endif
+ unsigned long prev_hdr_bits;
+ struct exynos_hdr_static_info prev_hdr_info;
+ enum hwc_ver ver;
+ /* systrace */
+ struct decon_systrace_data systrace;
+
+ bool mres_enabled;
+ bool low_persistence;
+};
+
+static inline struct decon_device *get_decon_drvdata(u32 id)
+{
+ return decon_drvdata[id];
+}
+
+/* register access subroutines */
+static inline u32 decon_read(u32 id, u32 reg_id)
+{
+ struct decon_device *decon = get_decon_drvdata(id);
+ return readl(decon->res.regs + reg_id);
+}
+
+static inline u32 decon_read_mask(u32 id, u32 reg_id, u32 mask)
+{
+ u32 val = decon_read(id, reg_id);
+ val &= (mask);
+ return val;
+}
+
+static inline void decon_write(u32 id, u32 reg_id, u32 val)
+{
+ struct decon_device *decon = get_decon_drvdata(id);
+ writel(val, decon->res.regs + reg_id);
+}
+
+static inline void decon_write_mask(u32 id, u32 reg_id, u32 val, u32 mask)
+{
+ u32 old = decon_read(id, reg_id);
+
+ val = (val & mask) | (old & ~mask);
+ decon_write(id, reg_id, val);
+}
+
+static inline u32 dsc_read(u32 dsc_id, u32 reg_id)
+{
+ struct decon_device *decon = get_decon_drvdata(0);
+ u32 dsc_offset = dsc_id ? DSC1_OFFSET : DSC0_OFFSET;
+
+ return readl(decon->res.regs + dsc_offset + reg_id);
+}
+
+static inline void dsc_write(u32 dsc_id, u32 reg_id, u32 val)
+{
+ struct decon_device *decon = get_decon_drvdata(0);
+ u32 dsc_offset = dsc_id ? DSC1_OFFSET : DSC0_OFFSET;
+
+ writel(val, decon->res.regs + dsc_offset + reg_id);
+}
+
+static inline void dsc_write_mask(u32 dsc_id, u32 reg_id, u32 val, u32 mask)
+{
+ u32 old = dsc_read(dsc_id, reg_id);
+
+ val = (val & mask) | (old & ~mask);
+ dsc_write(dsc_id, reg_id, val);
+}
+
+static inline u32 sysreg_read(u32 id, u32 reg_id)
+{
+ struct decon_device *decon = get_decon_drvdata(id);
+ return readl(decon->res.ss_regs + reg_id);
+}
+
+static inline u32 sysreg_read_mask(u32 id, u32 reg_id, u32 mask)
+{
+ u32 val = sysreg_read(id, reg_id);
+ val &= (mask);
+ return val;
+}
+
+static inline void sysreg_write(u32 id, u32 reg_id, u32 val)
+{
+ struct decon_device *decon = get_decon_drvdata(id);
+ writel(val, decon->res.ss_regs + reg_id);
+}
+
+static inline void sysreg_write_mask(u32 id, u32 reg_id, u32 val, u32 mask)
+{
+ u32 old = sysreg_read(id, reg_id);
+
+ val = (val & mask) | (old & ~mask);
+ sysreg_write(id, reg_id, val);
+}
+
+/* common function API */
+bool decon_validate_x_alignment(struct decon_device *decon, int x, u32 w,
+ u32 bits_per_pixel);
+int decon_wait_for_vsync(struct decon_device *decon, u32 timeout);
+int decon_check_limitation(struct decon_device *decon, int idx,
+ struct decon_win_config *config);
+
+/* DECON to DSI interface functions */
+int decon_register_irq(struct decon_device *decon);
+int decon_get_clocks(struct decon_device *decon);
+void decon_set_clocks(struct decon_device *decon);
+int decon_get_out_sd(struct decon_device *decon);
+int decon_get_pinctrl(struct decon_device *decon);
+int decon_register_ext_irq(struct decon_device *decon);
+int decon_create_vsync_thread(struct decon_device *decon);
+void decon_destroy_vsync_thread(struct decon_device *decon);
+int decon_create_psr_info(struct decon_device *decon);
+void decon_destroy_psr_info(struct decon_device *decon);
+
+/* DECON to writeback interface functions */
+int decon_wb_register_irq(struct decon_device *decon);
+void decon_wb_free_irq(struct decon_device *decon);
+int decon_wb_get_clocks(struct decon_device *decon);
+void decon_wb_set_clocks(struct decon_device *decon);
+int decon_wb_get_out_sd(struct decon_device *decon);
+
+/* DECON to DISPLAYPORT interface functions */
+int decon_displayport_register_irq(struct decon_device *decon);
+void decon_displayport_free_irq(struct decon_device *decon);
+int decon_displayport_create_vsync_thread(struct decon_device *decon);
+int decon_displayport_get_clocks(struct decon_device *decon);
+int decon_displayport_get_out_sd(struct decon_device *decon);
+int decon_displayport_get_hdr_capa(struct decon_device *decon,
+ struct decon_hdr_capabilities *hdr_capa);
+int decon_displayport_get_hdr_capa_info(struct decon_device *decon,
+ struct decon_hdr_capabilities_info *hdr_capa_info);
+int decon_displayport_get_config(struct decon_device *dex,
+ struct exynos_displayport_data *displayport_data);
+int decon_displayport_set_config(struct decon_device *dex,
+ struct exynos_displayport_data *displayport_data);
+
+/* window update related function */
+#define DPU_FULL_RECT(r, lcd) \
+ do { \
+ (r)->left = 0; \
+ (r)->top = 0; \
+ (r)->right = (lcd)->xres - 1; \
+ (r)->bottom = (lcd)->yres - 1; \
+ } while (0)
+void dpu_init_win_update(struct decon_device *decon);
+void dpu_prepare_win_update_config(struct decon_device *decon,
+ struct decon_win_config_data *win_data,
+ struct decon_reg_data *regs);
+void dpu_set_win_update_config(struct decon_device *decon,
+ struct decon_reg_data *regs);
+void dpu_set_win_update_partial_size(struct decon_device *decon,
+ struct decon_rect *up_region);
+
+/* low persistence mode */
+void decon_init_low_persistence_mode(struct decon_device *decon);
+
+/* multi-resolution related function */
+void dpu_set_mres_config(struct decon_device *decon, struct decon_reg_data *regs);
+
+/* internal only function API */
+int decon_check_var(struct fb_var_screeninfo *var, struct fb_info *info);
+int decon_set_par(struct fb_info *info);
+int decon_pan_display(struct fb_var_screeninfo *var, struct fb_info *info);
+int decon_setcolreg(unsigned regno,
+ unsigned red, unsigned green, unsigned blue,
+ unsigned transp, struct fb_info *info);
+int decon_mmap(struct fb_info *info, struct vm_area_struct *vma);
+
+u32 wincon(u32 transp_len, u32 a0, u32 a1, int plane_alpha,
+ enum decon_blending blending, int idx);
+
+static inline u32 win_start_pos(int x, int y)
+{
+ return (WIN_STRPTR_Y_F(y) | WIN_STRPTR_X_F(x));
+}
+
+static inline u32 win_end_pos(int x, int y, u32 xres, u32 yres)
+{
+ return (WIN_ENDPTR_Y_F(y + yres - 1) | WIN_ENDPTR_X_F(x + xres - 1));
+}
+
+
+/* HIBER releated */
+int decon_exit_hiber(struct decon_device *decon);
+int decon_enter_hiber(struct decon_device *decon);
+int decon_lcd_off(struct decon_device *decon);
+int decon_register_hiber_work(struct decon_device *decon);
+int decon_hiber_block_exit(struct decon_device *decon);
+u32 decon_reg_get_cam_status(void __iomem *cam_status);
+
+static inline void decon_hiber_block(struct decon_device *decon)
+{
+ if (decon)
+ atomic_inc(&decon->hiber.block_cnt);
+}
+
+static inline bool decon_is_hiber_blocked(struct decon_device *decon)
+{
+ return (atomic_read(&decon->hiber.block_cnt) > 0);
+}
+
+static inline int decon_get_hiber_block_cnt(struct decon_device *decon)
+{
+ return (atomic_read(&decon->hiber.block_cnt));
+}
+
+static inline void decon_hiber_unblock(struct decon_device *decon)
+{
+ if (decon) {
+ if (decon_is_hiber_blocked(decon))
+ atomic_dec(&decon->hiber.block_cnt);
+ }
+}
+
+static inline void decon_hiber_block_reset(struct decon_device *decon)
+{
+ if (decon)
+ atomic_set(&decon->hiber.block_cnt, 0);
+}
+
+static inline void decon_hiber_trig_reset(struct decon_device *decon)
+{
+ if (decon)
+ atomic_set(&decon->hiber.trig_cnt, 0);
+}
+
+static inline bool decon_min_lock_cond(struct decon_device *decon)
+{
+ return (atomic_read(&decon->hiber.block_cnt) <= 0);
+}
+
+static inline bool is_cam_not_running(struct decon_device *decon)
+{
+ if (!decon->id)
+ return (!(decon_reg_get_cam_status(decon->hiber.cam_status) & 0xF));
+ else
+ return true;
+}
+static inline bool decon_hiber_enter_cond(struct decon_device *decon)
+{
+ return ((atomic_read(&decon->hiber.block_cnt) <= 0)
+ && is_cam_not_running(decon)
+#if defined(CONFIG_EXYNOS_DISPLAYPORT)
+ && is_displayport_not_running()
+#endif
+ && (!decon->low_persistence)
+ && (atomic_inc_return(&decon->hiber.trig_cnt) >
+ DECON_ENTER_HIBER_CNT));
+}
+
+static inline void decon_enter_shutdown(struct decon_device *decon)
+{
+ atomic_inc(&decon->is_shutdown);
+}
+
+static inline bool decon_is_enter_shutdown(struct decon_device *decon)
+{
+ return atomic_read(&decon->is_shutdown);
+}
+
+static inline void decon_enter_shutdown_reset(struct decon_device *decon)
+{
+ atomic_set(&decon->is_shutdown, 0);
+}
+
+enum disp_pwr_mode {
+ DISP_PWR_OFF = 0,
+ DISP_PWR_DOZE,
+ DISP_PWR_NORMAL,
+ DISP_PWR_DOZE_SUSPEND,
+ DISP_PWR_MAX,
+};
+
+typedef int (*set_pwr_state_t)(void *);
+
+struct disp_pwr_state {
+ u32 state;
+ set_pwr_state_t set_pwr_state;
+};
+
+static inline bool IS_DECON_ON_STATE(struct decon_device *decon)
+{
+ return decon->state == DECON_STATE_INIT ||
+ decon->state == DECON_STATE_ON ||
+ decon->state == DECON_STATE_DOZE ||
+ decon->state == DECON_STATE_TUI;
+}
+
+static inline bool IS_DECON_OFF_STATE(struct decon_device *decon)
+{
+ return decon->state == DECON_STATE_HIBER ||
+ decon->state == DECON_STATE_DOZE_SUSPEND ||
+ decon->state == DECON_STATE_OFF;
+}
+
+static inline bool IS_DECON_HIBER_STATE(struct decon_device *decon)
+{
+ return decon->state == DECON_STATE_HIBER;
+}
+
+/* tui feature support external to security driver(gud) */
+int decon_tui_protection(bool tui_en);
+
+/* helper functions */
+int dpu_get_sd_by_drvname(struct decon_device *decon, char *drvname);
+u32 dpu_translate_fmt_to_dpp(u32 format);
+u32 dpu_get_bpp(enum decon_pixel_format fmt);
+int dpu_get_meta_plane_cnt(enum decon_pixel_format format);
+int dpu_get_plane_cnt(enum decon_pixel_format format, bool is_hdr);
+u32 dpu_get_alpha_len(int format);
+void dpu_unify_rect(struct decon_rect *r1, struct decon_rect *r2,
+ struct decon_rect *dst);
+
+void decon_dump(struct decon_device *decon);
+void decon_to_psr_info(struct decon_device *decon, struct decon_mode_info *psr);
+void decon_to_init_param(struct decon_device *decon, struct decon_param *p);
+void decon_create_timeline(struct decon_device *decon, char *name);
+void decon_create_release_fences(struct decon_device *decon,
+ struct decon_win_config_data *win_data,
+ struct sync_file *sync_file);
+int decon_create_fence(struct decon_device *decon, struct sync_file **sync_file);
+#if defined(CONFIG_SUPPORT_LEGACY_FENCE)
+void decon_wait_fence(struct sync_file *fence);
+void decon_signal_fence(struct decon_device *decon);
+#else
+void decon_wait_fence(struct dma_fence *fence);
+void decon_signal_fence(struct dma_fence *fence);
+#endif
+
+bool decon_intersect(struct decon_rect *r1, struct decon_rect *r2);
+int decon_intersection(struct decon_rect *r1,
+ struct decon_rect *r2, struct decon_rect *r3);
+
+bool is_decon_rect_differ(struct decon_rect *r1, struct decon_rect *r2);
+bool is_rgb32(int format);
+bool is_scaling(struct decon_win_config *config);
+bool is_full(struct decon_rect *r, struct decon_lcd *lcd);
+bool is_decon_opaque_format(int format);
+void __iomem *dpu_get_sysreg_addr(void);
+void dpu_dump_afbc_info(void);
+#if defined(CONFIG_EXYNOS_CONTENT_PATH_PROTECTION)
+void decon_set_protected_content(struct decon_device *decon,
+ struct decon_reg_data *regs);
+#endif
+
+int decon_runtime_suspend(struct device *dev);
+int decon_runtime_resume(struct device *dev);
+void decon_dpp_stop(struct decon_device *decon, bool do_reset);
+
+/* cursor async mode functions */
+void decon_set_cursor_reset(struct decon_device *decon,
+ struct decon_reg_data *regs);
+void decon_set_cursor_unmask(struct decon_device *decon, bool unmask);
+void dpu_cursor_win_update_config(struct decon_device *decon,
+ struct decon_reg_data *regs);
+int decon_set_cursor_win_config(struct decon_device *decon, int x, int y);
+void dpu_init_cursor_mode(struct decon_device *decon);
+
+int dpu_sysmmu_fault_handler(struct iommu_domain *domain,
+ struct device *dev, unsigned long iova, int flags, void *token);
+
+int decon_set_out_sd_state(struct decon_device *decon, enum decon_state state);
+
+/* IOCTL commands */
+#define S3CFB_SET_VSYNC_INT _IOW('F', 206, __u32)
+#define S3CFB_DECON_SELF_REFRESH _IOW('F', 207, __u32)
+#define S3CFB_WIN_CONFIG _IOW('F', 209, \
+ struct decon_win_config_data)
+
+/* cursor async */
+#define DECON_WIN_CURSOR_POS _IOW('F', 222, struct decon_user_window)
+#define S3CFB_POWER_MODE _IOW('F', 223, __u32)
+#define EXYNOS_DISP_INFO _IOW('F', 260, \
+ struct decon_disp_info)
+
+#define S3CFB_START_CRC _IOW('F', 270, u32)
+#define S3CFB_SEL_CRC_BITS _IOW('F', 271, u32)
+#define S3CFB_GET_CRC_DATA _IOR('F', 272, u32)
+
+#define EXYNOS_GET_DISPLAYPORT_CONFIG _IOW('F', 300, \
+ struct exynos_displayport_data)
+#define EXYNOS_SET_DISPLAYPORT_CONFIG _IOW('F', 301, \
+ struct exynos_displayport_data)
+
+#define EXYNOS_DPU_DUMP _IOW('F', 302, \
+ struct decon_win_config_data)
+
+/* HDR support */
+#define S3CFB_GET_HDR_CAPABILITIES _IOW('F', 400, struct decon_hdr_capabilities)
+#define S3CFB_GET_HDR_CAPABILITIES_NUM _IOW('F', 401, struct decon_hdr_capabilities_info)
+
+/* DPU aclk */
+#define EXYNOS_DPU_GET_ACLK _IOR('F', 500, u32)
+#endif /* ___SAMSUNG_DECON_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Core file for Samsung EXYNOS DECON 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <linux/clk-provider.h>
+#include <linux/console.h>
+#include <linux/dma-buf.h>
+#if defined(CONFIG_SUPPORT_LEGACY_ION)
+#include <linux/exynos_ion.h>
+#include <linux/ion.h>
+#include <linux/exynos_iovmm.h>
+#else
+#include <linux/ion_exynos.h>
+#endif
+#if defined(CONFIG_SUPPORT_KERNEL_4_9)
+#include <linux/sched.h>
+#else
+#include <linux/sched/types.h>
+#endif
+#include <linux/highmem.h>
+#include <linux/memblock.h>
+#include <linux/bug.h>
+#include <linux/of_address.h>
+#include <linux/debugfs.h>
+#include <linux/pinctrl/consumer.h>
+#include <video/mipi_display.h>
+#include <media/v4l2-subdev.h>
+#if defined(CONFIG_CAL_IF)
+#include <soc/samsung/cal-if.h>
+#endif
+#if defined(CONFIG_SOC_EXYNOS9610)
+#include <dt-bindings/clock/exynos9610.h>
+#endif
+
+#include "decon.h"
+#include "dsim.h"
+#include "./panels/lcd_ctrl.h"
+#include "../../../../dma-buf/sync_debug.h"
+#include "dpp.h"
+#include "displayport.h"
+
+int decon_log_level = 6;
+module_param(decon_log_level, int, 0644);
+int dpu_bts_log_level = 6;
+module_param(dpu_bts_log_level, int, 0644);
+int win_update_log_level = 6;
+module_param(win_update_log_level, int, 0644);
+int dpu_mres_log_level = 6;
+module_param(dpu_mres_log_level, int, 0644);
+int decon_systrace_enable;
+
+struct decon_device *decon_drvdata[MAX_DECON_CNT];
+EXPORT_SYMBOL(decon_drvdata);
+
+static char *decon_state_names[] = {
+ "INIT",
+ "ON",
+ "DOZE",
+ "HIBER",
+ "DOZE_SUSPEND",
+ "OFF",
+ "TUI",
+};
+
+void tracing_mark_write(struct decon_device *decon, char id, char *str1, int value)
+{
+ char buf[DECON_TRACE_BUF_SIZE] = {0,};
+
+ if (!decon->systrace.pid)
+ return;
+
+ switch (id) {
+ case 'B': /* B : Begin */
+ snprintf(buf, DECON_TRACE_BUF_SIZE, "B|%d|%s",
+ decon->systrace.pid, str1);
+ break;
+ case 'E': /* E : End */
+ strcpy(buf, "E");
+ break;
+ case 'C': /* C : Category */
+ snprintf(buf, DECON_TRACE_BUF_SIZE,
+ "C|%d|%s|%d", decon->systrace.pid, str1, value);
+ break;
+ default:
+ decon_err("%s:argument fail\n", __func__);
+ return;
+ }
+ trace_puts(buf);
+
+}
+
+static void decon_dump_using_dpp(struct decon_device *decon)
+{
+ int i;
+
+ for (i = 0; i < MAX_DPP_SUBDEV; i++) {
+ if (test_bit(i, &decon->prev_used_dpp)) {
+ struct v4l2_subdev *sd = NULL;
+ sd = decon->dpp_sd[i];
+ BUG_ON(!sd);
+ v4l2_subdev_call(sd, core, ioctl, DPP_DUMP, NULL);
+ }
+ }
+}
+
+static void decon_up_list_saved(void)
+{
+ int i;
+ struct decon_device *decon;
+
+ for (i = 0; i < MAX_DECON_CNT; i++) {
+ decon = get_decon_drvdata(i);
+ if (decon) {
+ if (!list_empty(&decon->up.list) || !list_empty(&decon->up.saved_list)) {
+ decon->up_list_saved = true;
+#if defined(CONFIG_SUPPORT_LEGACY_FENCE)
+ decon_info("\n=== DECON%d TIMELINE %d MAX %d ===\n",
+ decon->id, decon->timeline->value,
+ decon->timeline_max);
+#else
+ decon_info("\n=== DECON%d TIMELINE %d ===\n",
+ decon->id, atomic_read(&decon->fence.timeline));
+#endif
+ } else {
+ decon->up_list_saved = false;
+ }
+ }
+ }
+}
+
+static void __decon_dump(bool en_dsc)
+{
+ struct decon_device *decon = get_decon_drvdata(0);
+
+ decon_info("\n=== DECON0 WINDOW SFR DUMP ===\n");
+ print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ decon->res.regs + 0x1000, 0x340, false);
+
+ decon_info("\n=== DECON0 WINDOW SHADOW SFR DUMP ===\n");
+ print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ decon->res.regs + SHADOW_OFFSET + 0x1000, 0x220, false);
+
+ if (decon->lcd_info->dsc_enabled && en_dsc) {
+ decon_info("\n=== DECON0 DSC0 SFR DUMP ===\n");
+ print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ decon->res.regs + 0x4000, 0x80, false);
+
+ decon_info("\n=== DECON0 DSC1 SFR DUMP ===\n");
+ print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ decon->res.regs + 0x5000, 0x80, false);
+
+ decon_info("\n=== DECON0 DSC0 SHADOW SFR DUMP ===\n");
+ print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ decon->res.regs + SHADOW_OFFSET + 0x4000, 0x80, false);
+
+ decon_info("\n=== DECON0 DSC1 SHADOW SFR DUMP ===\n");
+ print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ decon->res.regs + SHADOW_OFFSET + 0x5000, 0x80, false);
+ }
+}
+
+void decon_dump(struct decon_device *decon)
+{
+ int acquired = console_trylock();
+
+ if (IS_DECON_OFF_STATE(decon)) {
+ decon_info("%s: DECON%d is disabled, state(%d)\n",
+ __func__, decon->id, decon->state);
+ return;
+ }
+
+ decon_info("\n=== DECON%d SFR DUMP ===\n", decon->id);
+ print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ decon->res.regs, 0x620, false);
+
+ decon_info("\n=== DECON%d SHADOW SFR DUMP ===\n", decon->id);
+ print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ decon->res.regs + SHADOW_OFFSET, 0x304, false);
+
+ switch (decon->id) {
+ case 0:
+ __decon_dump(1);
+ break;
+ case 1:
+ if (decon->dt.out_type != DECON_OUT_WB)
+ __decon_dump(0);
+ else
+ __decon_dump(1);
+ break;
+ case 2:
+ default:
+ __decon_dump(0);
+ break;
+ }
+
+ if (decon->dt.out_type == DECON_OUT_DSI)
+ v4l2_subdev_call(decon->out_sd[0], core, ioctl,
+ DSIM_IOC_DUMP, NULL);
+ decon_dump_using_dpp(decon);
+
+ if (acquired)
+ console_unlock();
+}
+
+/* ---------- CHECK FUNCTIONS ----------- */
+static void decon_win_config_to_regs_param
+ (int transp_length, struct decon_win_config *win_config,
+ struct decon_window_regs *win_regs, enum decon_idma_type idma_type,
+ int idx)
+{
+ u8 alpha0 = 0, alpha1 = 0;
+
+ win_regs->wincon = wincon(transp_length, alpha0, alpha1,
+ win_config->plane_alpha, win_config->blending, idx);
+ win_regs->start_pos = win_start_pos(win_config->dst.x, win_config->dst.y);
+ win_regs->end_pos = win_end_pos(win_config->dst.x, win_config->dst.y,
+ win_config->dst.w, win_config->dst.h);
+ win_regs->pixel_count = (win_config->dst.w * win_config->dst.h);
+ win_regs->whole_w = win_config->dst.f_w;
+ win_regs->whole_h = win_config->dst.f_h;
+ win_regs->offset_x = win_config->dst.x;
+ win_regs->offset_y = win_config->dst.y;
+ win_regs->type = idma_type;
+ win_regs->plane_alpha = win_config->plane_alpha;
+ win_regs->format = win_config->format;
+ win_regs->blend = win_config->blending;
+
+ decon_dbg("DMATYPE_%d@ SRC:(%d,%d) %dx%d DST:(%d,%d) %dx%d\n",
+ idma_type,
+ win_config->src.x, win_config->src.y,
+ win_config->src.f_w, win_config->src.f_h,
+ win_config->dst.x, win_config->dst.y,
+ win_config->dst.w, win_config->dst.h);
+}
+
+u32 wincon(u32 transp_len, u32 a0, u32 a1,
+ int plane_alpha, enum decon_blending blending, int idx)
+{
+ u32 data = 0;
+
+ data |= WIN_EN_F(idx);
+
+ return data;
+}
+
+bool decon_validate_x_alignment(struct decon_device *decon, int x, u32 w,
+ u32 bits_per_pixel)
+{
+ uint8_t pixel_alignment = 32 / bits_per_pixel;
+
+ if (x % pixel_alignment) {
+ decon_err("left x not aligned to %u-pixel(bpp = %u, x = %u)\n",
+ pixel_alignment, bits_per_pixel, x);
+ return 0;
+ }
+ if ((x + w) % pixel_alignment) {
+ decon_err("right X not aligned to %u-pixel(bpp = %u, x = %u, w = %u)\n",
+ pixel_alignment, bits_per_pixel, x, w);
+ return 0;
+ }
+
+ return 1;
+}
+
+void decon_dpp_stop(struct decon_device *decon, bool do_reset)
+{
+ int i;
+ bool rst = false;
+ struct v4l2_subdev *sd;
+
+ for (i = 0; i < MAX_DPP_SUBDEV; i++) {
+ if (test_bit(i, &decon->prev_used_dpp) &&
+ !test_bit(i, &decon->cur_using_dpp)) {
+ sd = decon->dpp_sd[i];
+ BUG_ON(!sd);
+ if (test_bit(i, &decon->dpp_err_stat) || do_reset)
+ rst = true;
+
+ v4l2_subdev_call(sd, core, ioctl, DPP_STOP, (bool *)rst);
+
+ clear_bit(i, &decon->prev_used_dpp);
+ clear_bit(i, &decon->dpp_err_stat);
+ }
+ }
+}
+
+static void decon_free_unused_buf(struct decon_device *decon,
+ struct decon_reg_data *regs, int win, int plane)
+{
+ struct decon_dma_buf_data *dma = ®s->dma_buf_data[win][plane];
+
+ decon_info("%s, win[%d]plane[%d]\n", __func__, win, plane);
+
+ if (dma->attachment && dma->dma_addr)
+ ion_iovmm_unmap(dma->attachment, dma->dma_addr);
+ if (dma->attachment && dma->sg_table)
+ dma_buf_unmap_attachment(dma->attachment,
+ dma->sg_table, DMA_TO_DEVICE);
+ if (dma->dma_buf && dma->attachment)
+ dma_buf_detach(dma->dma_buf, dma->attachment);
+ if (dma->dma_buf)
+ dma_buf_put(dma->dma_buf);
+
+ memset(dma, 0, sizeof(struct decon_dma_buf_data));
+}
+
+static void decon_free_dma_buf(struct decon_device *decon,
+ struct decon_dma_buf_data *dma)
+{
+ if (!dma->dma_addr)
+ return;
+
+ if (dma->fence) {
+#if defined(CONFIG_SUPPORT_LEGACY_FENCE)
+ fput(dma->fence->file);
+#else
+ dma_fence_put(dma->fence);
+ dma->fence = NULL;
+#endif
+ }
+ ion_iovmm_unmap(dma->attachment, dma->dma_addr);
+
+ dma_buf_unmap_attachment(dma->attachment, dma->sg_table,
+ DMA_TO_DEVICE);
+
+ dma_buf_detach(dma->dma_buf, dma->attachment);
+ dma_buf_put(dma->dma_buf);
+#if defined(CONFIG_SUPPORT_LEGACY_ION)
+ ion_free(decon->ion_client, dma->ion_handle);
+#endif
+ memset(dma, 0, sizeof(struct decon_dma_buf_data));
+}
+
+static void decon_set_black_window(struct decon_device *decon)
+{
+ struct decon_window_regs win_regs;
+ struct decon_lcd *lcd = decon->lcd_info;
+
+ memset(&win_regs, 0, sizeof(struct decon_window_regs));
+ win_regs.wincon = wincon(0x8, 0xFF, 0xFF, 0xFF, DECON_BLENDING_NONE,
+ decon->dt.dft_win);
+ win_regs.start_pos = win_start_pos(0, 0);
+ win_regs.end_pos = win_end_pos(0, 0, lcd->xres, lcd->yres);
+ decon_info("xres %d yres %d win_start_pos %x win_end_pos %x\n",
+ lcd->xres, lcd->yres, win_regs.start_pos,
+ win_regs.end_pos);
+ win_regs.colormap = 0x000000;
+ win_regs.pixel_count = lcd->xres * lcd->yres;
+ win_regs.whole_w = lcd->xres;
+ win_regs.whole_h = lcd->yres;
+ win_regs.offset_x = 0;
+ win_regs.offset_y = 0;
+ decon_info("pixel_count(%d), whole_w(%d), whole_h(%d), x(%d), y(%d)\n",
+ win_regs.pixel_count, win_regs.whole_w,
+ win_regs.whole_h, win_regs.offset_x,
+ win_regs.offset_y);
+ decon_reg_set_window_control(decon->id, decon->dt.dft_win,
+ &win_regs, true);
+}
+
+int decon_tui_protection(bool tui_en)
+{
+ int ret = 0;
+ int win_idx;
+ struct decon_mode_info psr;
+ struct decon_device *decon = decon_drvdata[0];
+ unsigned long aclk_khz = 0;
+
+ decon_info("%s:state %d: out_type %d:+\n", __func__,
+ tui_en, decon->dt.out_type);
+ if (tui_en) {
+ mutex_lock(&decon->lock);
+ decon_hiber_block_exit(decon);
+
+ kthread_flush_worker(&decon->up.worker);
+
+ decon_wait_for_vsync(decon, VSYNC_TIMEOUT_MSEC);
+ dpu_set_win_update_config(decon, NULL);
+ decon_to_psr_info(decon, &psr);
+ decon_reg_stop(decon->id, decon->dt.out_idx[0], &psr, false,
+ decon->lcd_info->fps);
+
+ decon->cur_using_dpp = 0;
+ decon_dpp_stop(decon, false);
+
+ /* after stopping decon, we can now update registers
+ * without considering per frame condition (8895) */
+ for (win_idx = 0; win_idx < decon->dt.max_win; win_idx++)
+ decon_reg_win_enable_and_update(decon->id, win_idx, false);
+ decon_reg_update_req_global(decon->id);
+ decon_wait_for_vsync(decon, VSYNC_TIMEOUT_MSEC);
+
+ decon->state = DECON_STATE_TUI;
+ aclk_khz = v4l2_subdev_call(decon->out_sd[0], core, ioctl,
+ EXYNOS_DPU_GET_ACLK, NULL) / 1000U;
+ decon_info("%s:DPU_ACLK(%ld khz)\n", __func__, aclk_khz);
+#if defined(CONFIG_EXYNOS9610_BTS)
+ decon_info("MIF(%lu), INT(%lu), DISP(%lu), total bw(%u, %u)\n",
+ cal_dfs_get_rate(ACPM_DVFS_MIF),
+ cal_dfs_get_rate(ACPM_DVFS_INT),
+ cal_dfs_get_rate(ACPM_DVFS_DISP),
+ decon->bts.prev_total_bw,
+ decon->bts.total_bw);
+#endif
+ mutex_unlock(&decon->lock);
+ } else {
+ mutex_lock(&decon->lock);
+ aclk_khz = v4l2_subdev_call(decon->out_sd[0], core, ioctl,
+ EXYNOS_DPU_GET_ACLK, NULL) / 1000U;
+ decon_info("%s:DPU_ACLK(%ld khz)\n", __func__, aclk_khz);
+#if defined(CONFIG_EXYNOS9610_BTS)
+ decon_info("MIF(%lu), INT(%lu), DISP(%lu), total bw(%u, %u)\n",
+ cal_dfs_get_rate(ACPM_DVFS_MIF),
+ cal_dfs_get_rate(ACPM_DVFS_INT),
+ cal_dfs_get_rate(ACPM_DVFS_DISP),
+ decon->bts.prev_total_bw,
+ decon->bts.total_bw);
+#endif
+ decon->state = DECON_STATE_ON;
+ decon_hiber_unblock(decon);
+ mutex_unlock(&decon->lock);
+ }
+ decon_info("%s:state %d: out_type %d:-\n", __func__,
+ tui_en, decon->dt.out_type);
+ return ret;
+}
+
+int decon_set_out_sd_state(struct decon_device *decon, enum decon_state state)
+{
+ int i, ret = 0;
+ int num_dsi = (decon->dt.dsi_mode == DSI_MODE_DUAL_DSI) ? 2 : 1;
+ enum decon_state prev_state = decon->state;
+
+ for (i = 0; i < num_dsi; i++) {
+ decon_dbg("decon-%d state:%s -> %s, set dsi-%d\n", decon->id,
+ decon_state_names[prev_state],
+ decon_state_names[state], i);
+ if (state == DECON_STATE_OFF) {
+ ret = v4l2_subdev_call(decon->out_sd[i], video, s_stream, 0);
+ if (ret) {
+ decon_err("stopping stream failed for %s\n",
+ decon->out_sd[i]->name);
+ goto err;
+ }
+ } else if (state == DECON_STATE_DOZE) {
+ ret = v4l2_subdev_call(decon->out_sd[i], core, ioctl,
+ DSIM_IOC_DOZE, NULL);
+ if (ret < 0) {
+ decon_err("decon-%d failed to set %s (ret %d)\n",
+ decon->id,
+ decon_state_names[state], ret);
+ goto err;
+ }
+ } else if (state == DECON_STATE_ON) {
+ if (prev_state == DECON_STATE_HIBER) {
+ ret = v4l2_subdev_call(decon->out_sd[i], core, ioctl,
+ DSIM_IOC_ENTER_ULPS, (unsigned long *)0);
+ if (ret) {
+ decon_warn("starting(ulps) stream failed for %s\n",
+ decon->out_sd[i]->name);
+ goto err;
+ }
+ } else {
+ ret = v4l2_subdev_call(decon->out_sd[i], video, s_stream, 1);
+ if (ret) {
+ decon_err("starting stream failed for %s\n",
+ decon->out_sd[i]->name);
+ goto err;
+ }
+ }
+ } else if (state == DECON_STATE_DOZE_SUSPEND) {
+ ret = v4l2_subdev_call(decon->out_sd[i], core, ioctl,
+ DSIM_IOC_DOZE_SUSPEND, NULL);
+ if (ret < 0) {
+ decon_err("decon-%d failed to set %s (ret %d)\n",
+ decon->id,
+ decon_state_names[state], ret);
+ goto err;
+ }
+ } else if (state == DECON_STATE_HIBER) {
+ ret = v4l2_subdev_call(decon->out_sd[i], core, ioctl,
+ DSIM_IOC_ENTER_ULPS, (unsigned long *)1);
+ if (ret) {
+ decon_warn("stopping(ulps) stream failed for %s\n",
+ decon->out_sd[i]->name);
+ goto err;
+ }
+ }
+ }
+
+err:
+ return ret;
+}
+
+/* ---------- FB_BLANK INTERFACE ----------- */
+static int _decon_enable(struct decon_device *decon, enum decon_state state)
+{
+ struct decon_mode_info psr;
+ struct decon_param p;
+ int ret = 0;
+
+ if (IS_DECON_ON_STATE(decon)) {
+ decon_warn("%s decon-%d already on(%s) state\n", __func__,
+ decon->id, decon_state_names[decon->state]);
+ ret = decon_set_out_sd_state(decon, state);
+ if (ret < 0) {
+ decon_err("%s decon-%d failed to set subdev %s state\n",
+ __func__, decon->id, decon_state_names[state]);
+ return ret;
+ }
+ decon->state = state;
+ return 0;
+ }
+
+ pm_stay_awake(decon->dev);
+ dev_warn(decon->dev, "pm_stay_awake");
+
+#if defined(CONFIG_EXYNOS9610_BTS)
+ decon->bts.ops->bts_acquire_bw(decon);
+#endif
+
+ if (decon->dt.psr_mode != DECON_VIDEO_MODE) {
+ if (decon->res.pinctrl && decon->res.hw_te_on) {
+ if (pinctrl_select_state(decon->res.pinctrl,
+ decon->res.hw_te_on)) {
+ decon_err("failed to turn on Decon_TE\n");
+ }
+ }
+ }
+
+ ret = decon_set_out_sd_state(decon, state);
+ if (ret < 0) {
+ decon_err("%s decon-%d failed to set subdev %s state\n",
+ __func__, decon->id, decon_state_names[state]);
+ goto err;
+ }
+
+ decon_to_init_param(decon, &p);
+ decon_reg_init(decon->id, decon->dt.out_idx[0], &p);
+
+ decon_to_psr_info(decon, &psr);
+
+ if ((decon->dt.out_type == DECON_OUT_DSI) && (state != DECON_STATE_DOZE)) {
+ if (psr.trig_mode == DECON_HW_TRIG) {
+ decon_set_black_window(decon);
+ /*
+ * Blender configuration must be set before DECON start.
+ * If DECON goes to start without window and
+ * blender configuration,
+ * DECON will go into abnormal state.
+ * DECON2(for DISPLAYPORT) start in winconfig
+ */
+ decon_reg_start(decon->id, &psr);
+ }
+ }
+
+ /*
+ * After turned on LCD, previous update region must be set as FULL size.
+ * DECON, DSIM and Panel are initialized as FULL size during UNBLANK
+ */
+ DPU_FULL_RECT(&decon->win_up.prev_up_region, decon->lcd_info);
+
+ if (!decon->id && !decon->eint_status) {
+ enable_irq(decon->res.irq);
+ decon->eint_status = 1;
+ }
+
+ decon->state = state;
+ decon_reg_set_int(decon->id, &psr, 1);
+
+err:
+ return ret;
+}
+
+static int decon_enable(struct decon_device *decon)
+{
+ int ret = 0;
+ enum decon_state prev_state = decon->state;
+ enum decon_state next_state = DECON_STATE_ON;
+
+ mutex_lock(&decon->lock);
+ if (decon->state == next_state) {
+ decon_warn("decon-%d %s already %s state\n", decon->id,
+ __func__, decon_state_names[decon->state]);
+ goto out;
+ }
+
+ DPU_EVENT_LOG(DPU_EVT_UNBLANK, &decon->sd, ktime_set(0, 0));
+ decon_info("decon-%d %s +\n", decon->id, __func__);
+ ret = _decon_enable(decon, next_state);
+ if (ret < 0) {
+ decon_err("decon-%d failed to set %s (ret %d)\n",
+ decon->id, decon_state_names[next_state], ret);
+ goto out;
+ }
+ decon_info("decon-%d %s - (state:%s -> %s)\n", decon->id, __func__,
+ decon_state_names[prev_state],
+ decon_state_names[decon->state]);
+
+out:
+ mutex_unlock(&decon->lock);
+ return ret;
+};
+
+static int decon_doze(struct decon_device *decon)
+{
+ int ret = 0;
+ enum decon_state prev_state = decon->state;
+ enum decon_state next_state = DECON_STATE_DOZE;
+
+ mutex_lock(&decon->lock);
+ if (decon->state == next_state) {
+ decon_warn("decon-%d %s already %s state\n", decon->id,
+ __func__, decon_state_names[decon->state]);
+ goto out;
+ }
+
+ DPU_EVENT_LOG(DPU_EVT_DOZE, &decon->sd, ktime_set(0, 0));
+ decon_info("decon-%d %s +\n", decon->id, __func__);
+ ret = _decon_enable(decon, next_state);
+ if (ret < 0) {
+ decon_err("decon-%d failed to set %s (ret %d)\n",
+ decon->id, decon_state_names[next_state], ret);
+ goto out;
+ }
+ decon_info("decon-%d %s - (state:%s -> %s)\n", decon->id, __func__,
+ decon_state_names[prev_state],
+ decon_state_names[decon->state]);
+
+out:
+ mutex_unlock(&decon->lock);
+ return ret;
+}
+
+int cmu_dpu_dump(void)
+{
+ void __iomem *cmu_regs;
+ void __iomem *pmu_regs;
+
+ decon_info("\n=== CMU_DPU0 SFR DUMP 0x12800100 ===\n");
+ cmu_regs = ioremap(0x12800100, 0x10);
+ print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ cmu_regs, 0x0C, false);
+
+ decon_info("\n=== CMU_DPU0 SFR DUMP 0x12800800 ===\n");
+ cmu_regs = ioremap(0x12800800, 0x08);
+ print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ cmu_regs, 0x04, false);
+
+ cmu_regs = ioremap(0x12800810, 0x10);
+ print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ cmu_regs, 0x08, false);
+
+ decon_info("\n=== CMUdd_DPU0 SFR DUMP 0x12801800 ===\n");
+ cmu_regs = ioremap(0x12801808, 0x08);
+ print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ cmu_regs, 0x04, false);
+
+ decon_info("\n=== CMU_DPU0 SFR DUMP 0x12802000 ===\n");
+ cmu_regs = ioremap(0x12802000, 0x74);
+ print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ cmu_regs, 0x70, false);
+
+ cmu_regs = ioremap(0x1280207c, 0x100);
+ print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ cmu_regs, 0x94, false);
+
+ decon_info("\n=== CMU_DPU0 SFR DUMP 0x12803000 ===\n");
+ cmu_regs = ioremap(0x12803004, 0x10);
+ print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ cmu_regs, 0x0c, false);
+
+ cmu_regs = ioremap(0x12803014, 0x2C);
+ print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ cmu_regs, 0x28, false);
+
+ cmu_regs = ioremap(0x1280304c, 0x20);
+ print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ cmu_regs, 0x18, false);
+
+ decon_info("\n=== PMU_DPU0 SFR DUMP 0x16484064 ===\n");
+ pmu_regs = ioremap(0x16484064, 0x08);
+ print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ pmu_regs, 0x04, false);
+
+ decon_info("\n=== PMU_DPU1 SFR DUMP 0x16484084 ===\n");
+ pmu_regs = ioremap(0x16484084, 0x08);
+ print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ pmu_regs, 0x04, false);
+
+ return 0;
+}
+
+static int _decon_disable(struct decon_device *decon, enum decon_state state)
+{
+ struct decon_mode_info psr;
+ int ret = 0;
+
+ if (decon->state == DECON_STATE_TUI)
+ decon_tui_protection(false);
+
+ if (IS_DECON_OFF_STATE(decon)) {
+ decon_warn("%s decon-%d already off (%s)\n", __func__,
+ decon->id, decon_state_names[decon->state]);
+ ret = decon_set_out_sd_state(decon, state);
+ if (ret < 0) {
+ decon_err("%s decon-%d failed to set subdev %s state\n",
+ __func__, decon->id,
+ decon_state_names[state]);
+ return ret;
+ }
+ decon->state = state;
+ return 0;
+ }
+
+ kthread_flush_worker(&decon->up.worker);
+
+ decon_to_psr_info(decon, &psr);
+ decon_reg_set_int(decon->id, &psr, 0);
+
+ if (!decon->id && (decon->vsync.irq_refcount <= 0) &&
+ decon->eint_status) {
+ disable_irq(decon->res.irq);
+ decon->eint_status = 0;
+ }
+
+ ret = decon_reg_stop(decon->id, decon->dt.out_idx[0], &psr, true,
+ decon->lcd_info->fps);
+ if (ret < 0)
+ decon_dump(decon);
+
+ /* DMA protection disable must be happen on dpp domain is alive */
+#if defined(CONFIG_EXYNOS_CONTENT_PATH_PROTECTION)
+ decon_set_protected_content(decon, NULL);
+#endif
+ decon->cur_using_dpp = 0;
+ decon_dpp_stop(decon, false);
+
+#if defined(CONFIG_EXYNOS9610_BTS)
+ decon->bts.ops->bts_release_bw(decon);
+#endif
+
+ ret = decon_set_out_sd_state(decon, state);
+ if (ret < 0) {
+ decon_err("%s decon-%d failed to set subdev %s state\n",
+ __func__, decon->id, decon_state_names[state]);
+ goto err;
+ }
+
+ pm_relax(decon->dev);
+ dev_warn(decon->dev, "pm_relax");
+
+ if (decon->dt.psr_mode != DECON_VIDEO_MODE) {
+ if (decon->res.pinctrl && decon->res.hw_te_off) {
+ if (pinctrl_select_state(decon->res.pinctrl,
+ decon->res.hw_te_off)) {
+ decon_err("failed to turn off Decon_TE\n");
+ }
+ }
+ }
+
+ decon->state = state;
+err:
+ return ret;
+}
+
+static int decon_disable(struct decon_device *decon)
+{
+ int ret = 0;
+ enum decon_state prev_state = decon->state;
+ enum decon_state next_state = DECON_STATE_OFF;
+
+ mutex_lock(&decon->lock);
+ if (decon->state == next_state) {
+ decon_warn("decon-%d %s already %s state\n", decon->id,
+ __func__, decon_state_names[decon->state]);
+ goto out;
+ }
+
+ DPU_EVENT_LOG(DPU_EVT_BLANK, &decon->sd, ktime_set(0, 0));
+ decon_info("decon-%d %s +\n", decon->id, __func__);
+ ret = _decon_disable(decon, next_state);
+ if (ret < 0) {
+ decon_err("decon-%d failed to set %s (ret %d)\n",
+ decon->id, decon_state_names[next_state], ret);
+ goto out;
+ }
+ decon_info("decon-%d %s - (state:%s -> %s)\n", decon->id, __func__,
+ decon_state_names[prev_state],
+ decon_state_names[decon->state]);
+
+out:
+ mutex_unlock(&decon->lock);
+ return ret;
+}
+
+static int decon_doze_suspend(struct decon_device *decon)
+{
+ int ret = 0;
+ enum decon_state prev_state = decon->state;
+ enum decon_state next_state = DECON_STATE_DOZE_SUSPEND;
+
+ mutex_lock(&decon->lock);
+ if (decon->state == next_state) {
+ decon_warn("decon-%d %s already %s state\n", decon->id,
+ __func__, decon_state_names[decon->state]);
+ goto out;
+ }
+
+ DPU_EVENT_LOG(DPU_EVT_DOZE_SUSPEND, &decon->sd, ktime_set(0, 0));
+ decon_info("decon-%d %s +\n", decon->id, __func__);
+ ret = _decon_disable(decon, next_state);
+ if (ret < 0) {
+ decon_err("decon-%d failed to set %s (ret %d)\n",
+ decon->id, decon_state_names[next_state], ret);
+ goto out;
+ }
+ decon_info("decon-%d %s - (state:%s -> %s)\n", decon->id, __func__,
+ decon_state_names[prev_state],
+ decon_state_names[decon->state]);
+
+out:
+ mutex_unlock(&decon->lock);
+ return ret;
+}
+
+struct disp_pwr_state decon_pwr_state[] = {
+ [DISP_PWR_OFF] = {
+ .state = DECON_STATE_OFF,
+ .set_pwr_state = (set_pwr_state_t)decon_disable,
+ },
+ [DISP_PWR_DOZE] = {
+ .state = DECON_STATE_DOZE,
+ .set_pwr_state = (set_pwr_state_t)decon_doze,
+ },
+ [DISP_PWR_NORMAL] = {
+ .state = DECON_STATE_ON,
+ .set_pwr_state = (set_pwr_state_t)decon_enable,
+ },
+ [DISP_PWR_DOZE_SUSPEND] = {
+ .state = DECON_STATE_DOZE_SUSPEND,
+ .set_pwr_state = (set_pwr_state_t)decon_doze_suspend,
+ },
+};
+
+int decon_update_pwr_state(struct decon_device *decon, u32 mode)
+{
+ int ret = 0;
+
+ if (mode >= DISP_PWR_MAX) {
+ decon_err("DECON:ERR:%s:invalid mode : %d\n", __func__, mode);
+ return -EINVAL;
+ }
+
+ if (decon_pwr_state[mode].state == decon->state) {
+ decon_warn("decon-%d already %s state\n",
+ decon->id, decon_state_names[decon->state]);
+ return 0;
+ }
+
+ if (IS_DECON_OFF_STATE(decon)) {
+ if (mode == DISP_PWR_OFF) {
+ ret = decon_enable(decon);
+ if (ret < 0) {
+ decon_err("DECON:ERR:%s: failed to set mode(%d)\n",
+ __func__, DISP_PWR_NORMAL);
+ return -EIO;
+ }
+ } else if (mode == DISP_PWR_DOZE_SUSPEND) {
+ ret = decon_doze(decon);
+ if (ret < 0) {
+ decon_err("DECON:ERR:%s: failed to set mode(%d)\n",
+ __func__, DISP_PWR_DOZE);
+ return -EIO;
+ }
+ }
+ }
+
+ ret = decon_pwr_state[mode].set_pwr_state((void *)decon);
+ if (ret < 0) {
+ decon_err("DECON:ERR:%s: failed to set mode(%d)\n",
+ __func__, mode);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int decon_dp_disable(struct decon_device *decon)
+{
+ struct decon_mode_info psr;
+ int ret = 0;
+
+ decon_info("disable decon displayport\n");
+
+ mutex_lock(&decon->lock);
+
+ if (IS_DECON_OFF_STATE(decon)) {
+ decon_info("decon%d already disabled\n", decon->id);
+ goto err;
+ }
+
+ kthread_flush_worker(&decon->up.worker);
+
+ decon_to_psr_info(decon, &psr);
+ decon_reg_set_int(decon->id, &psr, 0);
+ ret = decon_reg_stop(decon->id, decon->dt.out_idx[0], &psr, true,
+ decon->lcd_info->fps);
+ if (ret < 0)
+ decon_dump(decon);
+
+ /* DMA protection disable must be happen on dpp domain is alive */
+ if (decon->dt.out_type != DECON_OUT_WB) {
+#if defined(CONFIG_EXYNOS_CONTENT_PATH_PROTECTION)
+ decon_set_protected_content(decon, NULL);
+#endif
+ decon->cur_using_dpp = 0;
+ decon_dpp_stop(decon, false);
+ }
+
+#if defined(CONFIG_EXYNOS9610_BTS)
+ decon->bts.ops->bts_release_bw(decon);
+#endif
+
+ decon->state = DECON_STATE_OFF;
+err:
+ mutex_unlock(&decon->lock);
+ return ret;
+}
+
+static int decon_blank(int blank_mode, struct fb_info *info)
+{
+ struct decon_win *win = info->par;
+ struct decon_device *decon = win->decon;
+ int ret = 0;
+
+ decon_info("decon-%d %s mode: %d type (0: DSI, 1: eDP, 2:DP, 3: WB)\n",
+ decon->id,
+ blank_mode == FB_BLANK_UNBLANK ? "UNBLANK" : "POWERDOWN",
+ decon->dt.out_type);
+
+ if (IS_ENABLED(CONFIG_EXYNOS_VIRTUAL_DISPLAY)) {
+ decon_info("decon%d virtual display mode\n", decon->id);
+ return 0;
+ }
+
+ decon_hiber_block_exit(decon);
+
+ switch (blank_mode) {
+ case FB_BLANK_POWERDOWN:
+ case FB_BLANK_NORMAL:
+ DPU_EVENT_LOG(DPU_EVT_BLANK, &decon->sd, ktime_set(0, 0));
+ ret = decon_update_pwr_state(decon, DISP_PWR_OFF);
+ if (ret) {
+ decon_err("failed to disable decon\n");
+ goto blank_exit;
+ }
+ break;
+ case FB_BLANK_UNBLANK:
+ DPU_EVENT_LOG(DPU_EVT_UNBLANK, &decon->sd, ktime_set(0, 0));
+ ret = decon_update_pwr_state(decon, DISP_PWR_NORMAL);
+ if (ret) {
+ decon_err("failed to enable decon\n");
+ goto blank_exit;
+ }
+ break;
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ default:
+ ret = -EINVAL;
+ }
+
+blank_exit:
+ decon_hiber_unblock(decon);
+ decon_info("%s -\n", __func__);
+ return ret;
+}
+
+/* ---------- FB_IOCTL INTERFACE ----------- */
+static void decon_activate_vsync(struct decon_device *decon)
+{
+ int prev_refcount;
+
+ mutex_lock(&decon->vsync.lock);
+
+ prev_refcount = decon->vsync.irq_refcount++;
+ if (!prev_refcount)
+ DPU_EVENT_LOG(DPU_EVT_ACT_VSYNC, &decon->sd, ktime_set(0, 0));
+
+ mutex_unlock(&decon->vsync.lock);
+}
+
+static void decon_deactivate_vsync(struct decon_device *decon)
+{
+ int new_refcount;
+
+ mutex_lock(&decon->vsync.lock);
+
+ new_refcount = --decon->vsync.irq_refcount;
+ WARN_ON(new_refcount < 0);
+ if (!new_refcount)
+ DPU_EVENT_LOG(DPU_EVT_DEACT_VSYNC, &decon->sd, ktime_set(0, 0));
+
+ mutex_unlock(&decon->vsync.lock);
+}
+
+int decon_wait_for_vsync(struct decon_device *decon, u32 timeout)
+{
+ ktime_t timestamp;
+ struct decon_mode_info psr;
+ int ret;
+
+ decon_to_psr_info(decon, &psr);
+
+ if (psr.trig_mode != DECON_HW_TRIG)
+ return 0;
+
+ timestamp = decon->vsync.timestamp;
+ decon_activate_vsync(decon);
+
+#if defined(CONFIG_SUPPORT_KERNEL_4_9)
+ if (timeout) {
+ ret = wait_event_interruptible_timeout(decon->vsync.wait,
+ !ktime_equal(timestamp,
+ decon->vsync.timestamp),
+ msecs_to_jiffies(timeout));
+ } else {
+ ret = wait_event_interruptible(decon->vsync.wait,
+ !ktime_equal(timestamp,
+ decon->vsync.timestamp));
+ }
+#else
+ if (timeout) {
+ ret = wait_event_interruptible_timeout(decon->vsync.wait,
+ timestamp != decon->vsync.timestamp,
+ msecs_to_jiffies(timeout));
+ } else {
+ ret = wait_event_interruptible(decon->vsync.wait,
+ timestamp != decon->vsync.timestamp);
+ }
+#endif
+
+ decon_deactivate_vsync(decon);
+
+ if (timeout && ret == 0) {
+ if (decon->d.eint_pend) {
+ decon_err("decon%d wait for vsync timeout(p:0x%x)\n",
+ decon->id, readl(decon->d.eint_pend));
+ } else {
+ decon_err("decon%d wait for vsync timeout\n", decon->id);
+ }
+
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int decon_find_biggest_block_rect(struct decon_device *decon,
+ int win_no, struct decon_win_config *win_config,
+ struct decon_rect *block_rect, bool *enabled)
+{
+ struct decon_rect r1, r2, overlap_rect;
+ unsigned int overlap_size = 0, blocking_size = 0;
+ struct decon_win_config *config;
+ int j;
+
+ /* Get the rect in which we try to get the block region */
+ config = &win_config[win_no];
+ r1.left = config->dst.x;
+ r1.top = config->dst.y;
+ r1.right = r1.left + config->dst.w - 1;
+ r1.bottom = r1.top + config->dst.h - 1;
+
+ /* Find the biggest block region from overlays by the top windows */
+ for (j = win_no + 1; j < MAX_DECON_WIN; j++) {
+ config = &win_config[j];
+ if (config->state != DECON_WIN_STATE_BUFFER)
+ continue;
+
+ /* If top window has plane alpha, blocking mode not appliable */
+ if ((config->plane_alpha < 255) && (config->plane_alpha > 0))
+ continue;
+
+ if (is_decon_opaque_format(config->format)) {
+ config->opaque_area.x = config->dst.x;
+ config->opaque_area.y = config->dst.y;
+ config->opaque_area.w = config->dst.w;
+ config->opaque_area.h = config->dst.h;
+ } else
+ continue;
+
+ r2.left = config->opaque_area.x;
+ r2.top = config->opaque_area.y;
+ r2.right = r2.left + config->opaque_area.w - 1;
+ r2.bottom = r2.top + config->opaque_area.h - 1;
+ /* overlaps or not */
+ if (decon_intersect(&r1, &r2)) {
+ decon_intersection(&r1, &r2, &overlap_rect);
+ if (!is_decon_rect_differ(&r1, &overlap_rect)) {
+ /* if overlaping area intersects the window
+ * completely then disable the window */
+ win_config[win_no].state = DECON_WIN_STATE_DISABLED;
+ return 1;
+ }
+
+ if (overlap_rect.right - overlap_rect.left + 1 <
+ MIN_BLK_MODE_WIDTH ||
+ overlap_rect.bottom - overlap_rect.top + 1 <
+ MIN_BLK_MODE_HEIGHT)
+ continue;
+
+ overlap_size = (overlap_rect.right - overlap_rect.left) *
+ (overlap_rect.bottom - overlap_rect.top);
+
+ if (overlap_size > blocking_size) {
+ memcpy(block_rect, &overlap_rect,
+ sizeof(struct decon_rect));
+ blocking_size =
+ (block_rect->right - block_rect->left) *
+ (block_rect->bottom - block_rect->top);
+ *enabled = true;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int decon_set_win_blocking_mode(struct decon_device *decon,
+ int win_no, struct decon_win_config *win_config,
+ struct decon_reg_data *regs)
+{
+ struct decon_rect block_rect;
+ bool enabled;
+ int ret = 0;
+ struct decon_win_config *config = &win_config[win_no];
+
+ enabled = false;
+
+ if (!IS_ENABLED(CONFIG_EXYNOS_BLOCK_MODE))
+ return ret;
+
+ if (config->state != DECON_WIN_STATE_BUFFER)
+ return ret;
+
+ if (config->compression)
+ return ret;
+
+ /* Blocking mode is supported only for RGB32 color formats */
+ if (!is_rgb32(config->format))
+ return ret;
+
+ /* Blocking Mode is not supported if there is a rotation */
+ if (config->dpp_parm.rot || is_scaling(config))
+ return ret;
+
+ /* Initialization */
+ memset(&block_rect, 0, sizeof(struct decon_rect));
+
+ /* Find the biggest block region from possible block regions
+ * Possible block regions
+ * - overlays by top windows
+ *
+ * returns :
+ * 1 - corresponding window is blocked whole way,
+ * meaning that the window could be disabled
+ *
+ * 0, enabled = true - blocking area has been found
+ * 0, enabled = false - blocking area has not been found
+ */
+ ret = decon_find_biggest_block_rect(decon, win_no, win_config,
+ &block_rect, &enabled);
+ if (ret)
+ return ret;
+
+ /* If there was a block region, set regs with results */
+ if (enabled) {
+ regs->block_rect[win_no].w = block_rect.right - block_rect.left + 1;
+ regs->block_rect[win_no].h = block_rect.bottom - block_rect.top + 1;
+ regs->block_rect[win_no].x = block_rect.left - config->dst.x;
+ regs->block_rect[win_no].y = block_rect.top - config->dst.y;
+ decon_dbg("win-%d: block_rect[%d %d %d %d]\n", win_no,
+ regs->block_rect[win_no].x, regs->block_rect[win_no].y,
+ regs->block_rect[win_no].w, regs->block_rect[win_no].h);
+ memcpy(&config->block_area, ®s->block_rect[win_no],
+ sizeof(struct decon_win_rect));
+ }
+
+ return ret;
+}
+
+int decon_set_vsync_int(struct fb_info *info, bool active)
+{
+ struct decon_win *win = info->par;
+ struct decon_device *decon = win->decon;
+ bool prev_active = decon->vsync.active;
+
+ decon->vsync.active = active;
+ smp_wmb();
+
+ if (active && !prev_active)
+ decon_activate_vsync(decon);
+ else if (!active && prev_active)
+ decon_deactivate_vsync(decon);
+
+ return 0;
+}
+
+#if defined(CONFIG_SUPPORT_LEGACY_ION)
+static unsigned int decon_map_ion_handle(struct decon_device *decon,
+ struct device *dev, struct decon_dma_buf_data *dma,
+ struct ion_handle *ion_handle, struct dma_buf *buf, int win_no)
+#else
+static unsigned int decon_map_ion_handle(struct decon_device *decon,
+ struct device *dev, struct decon_dma_buf_data *dma,
+ struct dma_buf *buf, int win_no)
+#endif
+{
+ dma->fence = NULL;
+ dma->dma_buf = buf;
+
+ dma->attachment = dma_buf_attach(dma->dma_buf, dev);
+ if (IS_ERR_OR_NULL(dma->attachment)) {
+ decon_err("dma_buf_attach() failed: %ld\n",
+ PTR_ERR(dma->attachment));
+ goto err_buf_map_attach;
+ }
+
+ dma->sg_table = dma_buf_map_attachment(dma->attachment,
+ DMA_TO_DEVICE);
+ if (IS_ERR_OR_NULL(dma->sg_table)) {
+ decon_err("dma_buf_map_attachment() failed: %ld\n",
+ PTR_ERR(dma->sg_table));
+ goto err_buf_map_attachment;
+ }
+
+ /* This is DVA(Device Virtual Address) for setting base address SFR */
+ dma->dma_addr = ion_iovmm_map(dma->attachment, 0, dma->dma_buf->size,
+ DMA_TO_DEVICE, 0);
+ if (IS_ERR_VALUE(dma->dma_addr)) {
+ decon_err("ion_iovmm_map() failed: %pa\n", &dma->dma_addr);
+ goto err_iovmm_map;
+ }
+
+#if defined(CONFIG_SUPPORT_LEGACY_ION)
+ dma->ion_handle = ion_handle;
+#endif
+
+ return dma->dma_buf->size;
+
+err_iovmm_map:
+ dma_buf_unmap_attachment(dma->attachment, dma->sg_table,
+ DMA_TO_DEVICE);
+err_buf_map_attachment:
+ dma_buf_detach(dma->dma_buf, dma->attachment);
+err_buf_map_attach:
+ return 0;
+}
+
+static int decon_import_buffer(struct decon_device *decon, int idx,
+ struct decon_win_config *config,
+ struct decon_reg_data *regs)
+{
+#if defined(CONFIG_SUPPORT_LEGACY_ION)
+ struct ion_handle *handle;
+#endif
+ struct dma_buf *buf = NULL;
+ struct decon_dma_buf_data *dma_buf_data = NULL;
+ struct displayport_device *displayport;
+ struct dsim_device *dsim;
+ struct device *dev;
+ int i;
+ size_t buf_size = 0;
+
+ decon_dbg("%s +\n", __func__);
+
+ DPU_EVENT_LOG(DPU_EVT_DECON_SET_BUFFER, &decon->sd, ktime_set(0, 0));
+
+ regs->plane_cnt[idx] =
+ dpu_get_plane_cnt(config->format, config->dpp_parm.hdr_std);
+
+ memset(®s->dma_buf_data[idx], 0,
+ sizeof(struct decon_dma_buf_data) * MAX_PLANE_CNT);
+
+ for (i = 0; i < regs->plane_cnt[idx]; ++i) {
+#if defined(CONFIG_SUPPORT_LEGACY_ION)
+ handle = ion_import_dma_buf_fd(decon->ion_client,
+ config->fd_idma[i]);
+ if (IS_ERR(handle)) {
+ decon_err("failed to import fd:%d\n", config->fd_idma[i]);
+ return PTR_ERR(handle);
+ }
+#endif
+
+ dma_buf_data = ®s->dma_buf_data[idx][i];
+
+ buf = dma_buf_get(config->fd_idma[i]);
+ if (IS_ERR_OR_NULL(buf)) {
+ decon_err("failed to get dma_buf:%ld\n", PTR_ERR(buf));
+ return PTR_ERR(buf);
+ }
+ if (decon->dt.out_type == DECON_OUT_DP) {
+ displayport = v4l2_get_subdevdata(decon->out_sd[0]);
+ dev = displayport->dev;
+ } else { /* DSI case */
+ dsim = v4l2_get_subdevdata(decon->out_sd[0]);
+ dev = dsim->dev;
+ }
+#if defined(CONFIG_SUPPORT_LEGACY_ION)
+ buf_size = decon_map_ion_handle(decon, dev, dma_buf_data,
+ handle, buf, idx);
+#else
+ buf_size = decon_map_ion_handle(decon, dev, dma_buf_data, buf, idx);
+#endif
+ if (!buf_size) {
+ decon_err("failed to map buffer\n");
+ return -ENOMEM;
+ }
+
+ /* DVA is passed to DPP parameters structure */
+ config->dpp_parm.addr[i] = dma_buf_data->dma_addr;
+ }
+
+ decon_dbg("%s -\n", __func__);
+
+ return 0;
+}
+
+int decon_check_limitation(struct decon_device *decon, int idx,
+ struct decon_win_config *config)
+{
+ if (config->format >= DECON_PIXEL_FORMAT_MAX) {
+ decon_err("unknown pixel format %u\n", config->format);
+ return -EINVAL;
+ }
+
+ if (decon_check_supported_formats(config->format)) {
+ decon_err("not supported pixel format\n");
+ return -EINVAL;
+ }
+
+ if (config->blending >= DECON_BLENDING_MAX) {
+ decon_err("unknown blending %u\n", config->blending);
+ return -EINVAL;
+ }
+
+ if ((config->plane_alpha < 0) || (config->plane_alpha > 0xff)) {
+ decon_err("plane alpha value(%d) is out of range(0~255)\n",
+ config->plane_alpha);
+ return -EINVAL;
+ }
+
+ if (config->dst.w == 0 || config->dst.h == 0 ||
+ config->dst.x < 0 || config->dst.y < 0) {
+ decon_err("win[%d] size is abnormal (w:%d, h:%d, x:%d, y:%d)\n",
+ idx, config->dst.w, config->dst.h,
+ config->dst.x, config->dst.y);
+ return -EINVAL;
+ }
+
+ if (config->dst.w < 16) {
+ decon_err("window wide < 16pixels, width = %u)\n",
+ config->dst.w);
+ return -EINVAL;
+ }
+
+ if ((config->dst.x + config->dst.w > config->dst.f_w) ||
+ (config->dst.y + config->dst.h > config->dst.f_h) ||
+ (config->dst.x + config->dst.w > decon->lcd_info->xres) ||
+ (config->dst.y + config->dst.h > decon->lcd_info->yres)) {
+ decon_err("dst coordinate is out of range(%d %d %d %d %d %d %d %d)\n",
+ config->dst.x, config->dst.w, config->dst.f_w,
+ config->dst.y, config->dst.h, config->dst.f_h,
+ decon->lcd_info->xres, decon->lcd_info->yres);
+ return -EINVAL;
+ }
+
+ if (config->idma_type >= MAX_DECON_DMA_TYPE) {
+ decon_err("idma_type(%d) is wrong\n", config->idma_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int decon_set_win_buffer(struct decon_device *decon,
+ struct decon_win_config *config,
+ struct decon_reg_data *regs, int idx)
+{
+ int ret;
+ u32 alpha_length;
+ struct decon_rect r;
+#if defined(CONFIG_SUPPORT_LEGACY_FENCE)
+ struct sync_file *fence = NULL;
+#else
+ struct dma_fence *fence = NULL;
+#endif
+ u32 config_size = 0;
+ u32 alloc_size = 0;
+ u32 byte_per_pixel = 4;
+
+ ret = decon_check_limitation(decon, idx, config);
+ if (ret)
+ goto err;
+
+ if (decon->dt.out_type == DECON_OUT_WB) {
+ r.left = config->dst.x;
+ r.top = config->dst.y;
+ r.right = r.left + config->dst.w - 1;
+ r.bottom = r.top + config->dst.h - 1;
+ dpu_unify_rect(®s->blender_bg, &r, ®s->blender_bg);
+ }
+
+ ret = decon_import_buffer(decon, idx, config, regs);
+ if (ret)
+ goto err;
+
+ if (config->acq_fence >= 0) {
+ /* fence is managed by buffer not plane */
+#if defined(CONFIG_SUPPORT_LEGACY_FENCE)
+ fence = sync_file_fdget(config->acq_fence);
+#else
+ fence = sync_file_get_fence(config->acq_fence);
+#endif
+ regs->dma_buf_data[idx][0].fence = fence;
+ if (!fence) {
+ decon_err("failed to import fence fd\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ decon_dbg("acq_fence(%d), fence(%p)\n", config->acq_fence, fence);
+ }
+
+ /*
+ * To avoid SysMMU page fault due to small buffer allocation
+ * bpp = 12 : (NV12, NV21) check LUMA side for simplication
+ * bpp = 15 : (8+2_10bit)
+ * bpp = 16 : (RGB16 formats)
+ * bpp = 32 : (RGB32 formats)
+ */
+ /* TODO : We should check also YUV format, because YUV has more than 2 palnes.
+ * Also bpp macro is not matched with this. In case of YUV format, each plane's
+ * bpp is needed.
+ */
+ if (dpu_get_bpp(config->format) == 12) {
+ byte_per_pixel = 1;
+ } else if (dpu_get_bpp(config->format) == 15) {
+ /* It should be 1.25 byte per pixel of Y plane.
+ * So 1 byte is used instead of floating point.
+ */
+ byte_per_pixel = 1;
+ } else if (dpu_get_bpp(config->format) == 16) {
+ byte_per_pixel = 2;
+ } else {
+ byte_per_pixel = 4;
+ }
+
+ config_size = config->src.f_w * config->src.f_h * byte_per_pixel;
+ alloc_size = (u32)(regs->dma_buf_data[idx][0].dma_buf->size);
+ if (config_size > alloc_size) {
+ decon_err("alloc buf size is less than required size ([w%d] alloc=%x : cfg=%x)\n",
+ idx, alloc_size, config_size);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ alpha_length = dpu_get_alpha_len(config->format);
+ regs->protection[idx] = config->protection;
+ decon_win_config_to_regs_param(alpha_length, config,
+ ®s->win_regs[idx], config->idma_type, idx);
+
+ return 0;
+
+err:
+ return ret;
+}
+
+void decon_reg_chmap_validate(struct decon_device *decon,
+ struct decon_reg_data *regs)
+{
+ unsigned short i, bitmap = 0;
+
+ for (i = 0; i < decon->dt.max_win; i++) {
+ if (!(regs->win_regs[i].wincon & WIN_EN_F(i)) ||
+ (regs->win_regs[i].winmap_state))
+ continue;
+
+ if (bitmap & (1 << regs->dpp_config[i].idma_type)) {
+ decon_warn("Channel-%d is mapped to multiple windows\n",
+ regs->dpp_config[i].idma_type);
+ regs->win_regs[i].wincon &= (~WIN_EN_F(i));
+ }
+ bitmap |= 1 << regs->dpp_config[i].idma_type;
+ }
+}
+
+static void decon_check_used_dpp(struct decon_device *decon,
+ struct decon_reg_data *regs)
+{
+ int i = 0;
+ decon->cur_using_dpp = 0;
+
+ for (i = 0; i < decon->dt.max_win; i++) {
+ struct decon_win *win = decon->win[i];
+ if (!regs->win_regs[i].winmap_state)
+ win->dpp_id = DPU_DMA2CH(regs->dpp_config[i].idma_type);
+ else
+ win->dpp_id = 0xF;
+
+ if ((regs->win_regs[i].wincon & WIN_EN_F(i)) &&
+ (!regs->win_regs[i].winmap_state)) {
+ set_bit(win->dpp_id, &decon->cur_using_dpp);
+ set_bit(win->dpp_id, &decon->prev_used_dpp);
+ }
+ }
+
+ if (decon->dt.out_type == DECON_OUT_WB) {
+ set_bit(ODMA_WB, &decon->cur_using_dpp);
+ set_bit(ODMA_WB, &decon->prev_used_dpp);
+ }
+}
+
+void decon_dpp_wait_wb_framedone(struct decon_device *decon)
+{
+ struct v4l2_subdev *sd = NULL;
+
+ sd = decon->dpp_sd[ODMA_WB];
+ v4l2_subdev_call(sd, core, ioctl,
+ DPP_WB_WAIT_FOR_FRAMEDONE, NULL);
+
+}
+
+static int decon_set_dpp_config(struct decon_device *decon,
+ struct decon_reg_data *regs)
+{
+ int i, ret = 0, err_cnt = 0;
+ struct v4l2_subdev *sd;
+ struct decon_win *win;
+ struct dpp_config dpp_config;
+ unsigned long aclk_khz;
+
+ /* 1 msec */
+ aclk_khz = v4l2_subdev_call(decon->out_sd[0], core, ioctl,
+ EXYNOS_DPU_GET_ACLK, NULL) / 1000U;
+
+ for (i = 0; i < decon->dt.max_win; i++) {
+ win = decon->win[i];
+ /*
+ * Although DPP number is set in cur_using_dpp, connected window
+ * can be disabled. If window related parameter has problem,
+ * requested window from user will be disabled because of
+ * error handling code.
+ */
+ if (!test_bit(win->dpp_id, &decon->cur_using_dpp) ||
+ !(regs->win_regs[i].wincon & WIN_EN_F(i)))
+ continue;
+
+ sd = decon->dpp_sd[win->dpp_id];
+ memcpy(&dpp_config.config, ®s->dpp_config[i],
+ sizeof(struct decon_win_config));
+ dpp_config.rcv_num = aclk_khz;
+ ret = v4l2_subdev_call(sd, core, ioctl,
+ DPP_WIN_CONFIG, &dpp_config);
+ if (ret) {
+ decon_err("failed to config (WIN%d : DPP%d)\n",
+ i, win->dpp_id);
+ regs->win_regs[i].wincon &= (~WIN_EN_F(i));
+ decon_reg_win_enable_and_update(decon->id, i, false);
+ if (regs->num_of_window != 0)
+ regs->num_of_window--;
+ clear_bit(win->dpp_id, &decon->cur_using_dpp);
+ set_bit(win->dpp_id, &decon->dpp_err_stat);
+ err_cnt++;
+ }
+ }
+
+ if (decon->dt.out_type == DECON_OUT_WB) {
+ sd = decon->dpp_sd[ODMA_WB];
+ memcpy(&dpp_config.config, ®s->dpp_config[MAX_DECON_WIN],
+ sizeof(struct decon_win_config));
+ dpp_config.rcv_num = aclk_khz;
+ ret = v4l2_subdev_call(sd, core, ioctl, DPP_WIN_CONFIG,
+ &dpp_config);
+ if (ret) {
+ decon_err("failed to config ODMA_WB\n");
+ clear_bit(ODMA_WB, &decon->cur_using_dpp);
+ set_bit(ODMA_WB, &decon->dpp_err_stat);
+ err_cnt++;
+ }
+ }
+
+ if (decon->prev_aclk_khz != aclk_khz)
+ decon_info("DPU_ACLK(%ld khz), Recovery_num(%ld)\n",
+ aclk_khz, aclk_khz);
+
+ decon->prev_aclk_khz = aclk_khz;
+
+ return err_cnt;
+}
+
+#if defined(CONFIG_EXYNOS_AFBC_DEBUG)
+static void decon_save_vgf_connected_win_id(struct decon_device *decon,
+ struct decon_reg_data *regs)
+{
+ int i;
+
+ decon->d.prev_vgf_win_id[0] = -1;
+ decon->d.prev_vgf_win_id[1] = -1;
+
+ for (i = 0; i < decon->dt.max_win; ++i) {
+ if (regs->dpp_config[i].idma_type == IDMA_VGF0)
+ decon->d.prev_vgf_win_id[0] = i;
+ if (regs->dpp_config[i].idma_type == IDMA_VGF1)
+ decon->d.prev_vgf_win_id[1] = i;
+ }
+}
+
+static void decon_dump_afbc_handle(struct decon_device *decon,
+ struct decon_dma_buf_data (*dma_bufs)[MAX_PLANE_CNT])
+{
+ int size;
+ int win_id = 0;
+ void *v_addr;
+
+ decon_info("%s +\n", __func__);
+
+ if (test_bit(DPU_DMA2CH(IDMA_VGF0), &decon->prev_used_dpp)) {
+ win_id = decon->d.prev_vgf_win_id[0];
+ if (win_id < 0) {
+ decon_err("%s: win_id(%d) is invalid\n", __func__, win_id);
+ return;
+ }
+#if defined(CONFIG_SUPPORT_LEGACY_ION)
+ decon->d.handle[win_id][0] = dma_bufs[win_id][0].ion_handle;
+ decon_info("VGF0(WIN%d): handle=0x%p\n",
+ win_id, decon->d.handle[win_id][0]);
+
+ v_addr = ion_map_kernel(decon->ion_client,
+ dma_bufs[win_id][0].ion_handle);
+ if (IS_ERR_OR_NULL(v_addr)) {
+ decon_err("%s: failed to map afbc buffer\n", __func__);
+ return;
+ }
+#else
+ decon->d.dmabuf[win_id][0] = dma_bufs[win_id][0].dma_buf;
+ decon_info("VGF0(WIN%d): dmabuf=0x%p\n",
+ win_id, decon->d.dmabuf[win_id][0]);
+ v_addr = dma_buf_vmap(dma_bufs[win_id][0].dma_buf);
+ if (IS_ERR_OR_NULL(v_addr)) {
+ decon_err("%s: failed to map afbc buffer\n", __func__);
+ return;
+ }
+#endif
+ size = dma_bufs[win_id][0].dma_buf->size;
+
+ decon_info("DV(0x%p), KV(0x%p), size(%d)\n",
+ (void *)dma_bufs[win_id][0].dma_addr,
+ v_addr, size);
+ }
+
+ if (test_bit(DPU_DMA2CH(IDMA_VGF1), &decon->prev_used_dpp)) {
+ win_id = decon->d.prev_vgf_win_id[1];
+ if (win_id < 0) {
+ decon_err("%s: win_id(%d) is invalid\n", __func__, win_id);
+ return;
+ }
+#if defined(CONFIG_SUPPORT_LEGACY_ION)
+ decon->d.handle[win_id][0] = dma_bufs[win_id][0].ion_handle;
+ decon_info("VGF1(WIN%d): handle=0x%p\n",
+ win_id, decon->d.handle[win_id][0]);
+
+ v_addr = ion_map_kernel(decon->ion_client,
+ dma_bufs[win_id][0].ion_handle);
+ if (IS_ERR_OR_NULL(v_addr)) {
+ decon_err("%s: failed to map afbc buffer\n", __func__);
+ return;
+ }
+#else
+ decon->d.dmabuf[win_id][0] = dma_bufs[win_id][0].dma_buf;
+ decon_info("VGF1(WIN%d): dmabuf=0x%p\n",
+ win_id, decon->d.dmabuf[win_id][0]);
+ v_addr = dma_buf_vmap(dma_bufs[win_id][0].dma_buf);
+ if (IS_ERR_OR_NULL(v_addr)) {
+ decon_err("%s: failed to map afbc buffer\n", __func__);
+ return;
+ }
+#endif
+ size = dma_bufs[win_id][0].dma_buf->size;
+
+ decon_info("DV(0x%p), KV(0x%p), size(%d)\n",
+ (void *)dma_bufs[win_id][0].dma_addr,
+ v_addr, size);
+ }
+
+ decon_info("%s -\n", __func__);
+}
+#endif
+
+static int __decon_update_regs(struct decon_device *decon, struct decon_reg_data *regs)
+{
+ int err_cnt = 0;
+ unsigned short i, j;
+ struct decon_mode_info psr;
+ struct decon_param p;
+ bool has_cursor_win = false;
+
+ decon_to_psr_info(decon, &psr);
+ /*
+ * Shadow update bit must be cleared before setting window configuration,
+ * If shadow update bit is not cleared, decon initial state or previous
+ * window configuration has problem.
+ */
+ if (decon_reg_wait_update_done_and_mask(decon->id, &psr,
+ SHADOW_UPDATE_TIMEOUT) > 0) {
+ decon_warn("decon SHADOW_UPDATE_TIMEOUT\n");
+ return -ETIMEDOUT;
+ }
+
+ /* TODO: check and wait until the required IDMA is free */
+ decon_reg_chmap_validate(decon, regs);
+
+ /* apply multi-resolution configuration */
+ dpu_set_mres_config(decon, regs);
+
+ /* apply window update configuration to DECON, DSIM and panel */
+ dpu_set_win_update_config(decon, regs);
+
+ err_cnt = decon_set_dpp_config(decon, regs);
+ if (!regs->num_of_window) {
+ decon_err("decon%d: num_of_window=0 during dpp_config(err_cnt:%d)\n",
+ decon->id, err_cnt);
+ return 0;
+ }
+
+ for (i = 0; i < decon->dt.max_win; i++) {
+ if (regs->is_cursor_win[i]) {
+ dpu_cursor_win_update_config(decon, regs);
+ has_cursor_win = true;
+ }
+ /* set decon registers for each window */
+ decon_reg_set_window_control(decon->id, i, ®s->win_regs[i],
+ regs->win_regs[i].winmap_state);
+
+ if (decon->dt.out_type != DECON_OUT_WB) {
+ /* backup cur dma_buf_data for freeing next update_handler_regs */
+ for (j = 0; j < regs->plane_cnt[i]; ++j)
+ decon->win[i]->dma_buf_data[j] = regs->dma_buf_data[i][j];
+ decon->win[i]->plane_cnt = regs->plane_cnt[i];
+ }
+ }
+
+ if (decon->dt.out_type == DECON_OUT_WB) {
+ /* update resolution info for decon blender size */
+ decon->lcd_info->xres = regs->dpp_config[ODMA_WB].dst.w;
+ decon->lcd_info->yres = regs->dpp_config[ODMA_WB].dst.h;
+
+ decon_to_init_param(decon, &p);
+ decon_reg_config_wb_size(decon->id, decon->lcd_info, &p);
+ }
+
+#if defined(CONFIG_EXYNOS_CONTENT_PATH_PROTECTION)
+ decon_set_protected_content(decon, regs);
+#endif
+
+ decon_to_psr_info(decon, &psr);
+ if (decon_reg_start(decon->id, &psr) < 0) {
+ decon_up_list_saved();
+ decon_dump(decon);
+ BUG();
+ }
+
+ decon_set_cursor_unmask(decon, has_cursor_win);
+
+ DPU_EVENT_LOG(DPU_EVT_TRIG_UNMASK, &decon->sd, ktime_set(0, 0));
+
+ return 0;
+}
+
+void decon_wait_for_vstatus(struct decon_device *decon, u32 timeout)
+{
+ int ret;
+
+ if (decon->id)
+ return;
+
+ decon_systrace(decon, 'C', "decon_frame_start", 1);
+ ret = wait_event_interruptible_timeout(decon->wait_vstatus,
+ (decon->frame_cnt_target <= decon->frame_cnt),
+ msecs_to_jiffies(timeout));
+ decon_systrace(decon, 'C', "decon_frame_start", 0);
+ DPU_EVENT_LOG(DPU_EVT_DECON_FRAMESTART, &decon->sd, ktime_set(0, 0));
+ if (!ret)
+ decon_warn("%s:timeout\n", __func__);
+}
+
+static void __decon_update_clear(struct decon_device *decon, struct decon_reg_data *regs)
+{
+ unsigned short i, j;
+
+ for (i = 0; i < decon->dt.max_win; i++) {
+ for (j = 0; j < regs->plane_cnt[i]; ++j)
+ decon->win[i]->dma_buf_data[j] = regs->dma_buf_data[i][j];
+
+ decon->win[i]->plane_cnt = regs->plane_cnt[i];
+ }
+
+ return;
+}
+
+static void decon_acquire_old_bufs(struct decon_device *decon,
+ struct decon_reg_data *regs,
+ struct decon_dma_buf_data (*dma_bufs)[MAX_PLANE_CNT],
+ int *plane_cnt)
+{
+ int i, j;
+
+ for (i = 0; i < decon->dt.max_win; i++) {
+ for (j = 0; j < MAX_PLANE_CNT; ++j)
+ memset(&dma_bufs[i][j], 0, sizeof(struct decon_dma_buf_data));
+ plane_cnt[i] = 0;
+ }
+
+ for (i = 0; i < decon->dt.max_win; i++) {
+ if (decon->dt.out_type == DECON_OUT_WB)
+ plane_cnt[i] = regs->plane_cnt[i];
+ else
+ plane_cnt[i] = decon->win[i]->plane_cnt;
+ for (j = 0; j < plane_cnt[i]; ++j)
+ dma_bufs[i][j] = decon->win[i]->dma_buf_data[j];
+ }
+}
+
+static void decon_release_old_bufs(struct decon_device *decon,
+ struct decon_reg_data *regs,
+ struct decon_dma_buf_data (*dma_bufs)[MAX_PLANE_CNT],
+ int *plane_cnt)
+{
+ int i, j;
+
+ for (i = 0; i < decon->dt.max_win; i++) {
+ for (j = 0; j < plane_cnt[i]; ++j)
+ if (decon->dt.out_type == DECON_OUT_WB)
+ decon_free_dma_buf(decon, ®s->dma_buf_data[i][j]);
+ else
+ decon_free_dma_buf(decon, &dma_bufs[i][j]);
+ }
+
+ if (decon->dt.out_type == DECON_OUT_WB) {
+ for (j = 0; j < plane_cnt[0]; ++j)
+ decon_free_dma_buf(decon,
+ ®s->dma_buf_data[MAX_DECON_WIN][j]);
+ }
+}
+
+static int decon_set_hdr_info(struct decon_device *decon,
+ struct decon_reg_data *regs, int win_num, bool on)
+{
+ struct exynos_video_meta *video_meta;
+ int ret = 0, hdr_cmp = 0;
+ int meta_plane = 0;
+
+ if (!on) {
+ struct exynos_hdr_static_info hdr_static_info;
+
+ hdr_static_info.mid = -1;
+ decon->prev_hdr_info.mid = -1;
+ ret = v4l2_subdev_call(decon->displayport_sd, core, ioctl,
+ DISPLAYPORT_IOC_SET_HDR_METADATA,
+ &hdr_static_info);
+ if (ret)
+ goto err_hdr_io;
+
+ return 0;
+ }
+
+ meta_plane = dpu_get_meta_plane_cnt(regs->dpp_config[win_num].format);
+ if (meta_plane < 0) {
+ decon_err("Unsupported hdr metadata format\n");
+ return -EINVAL;
+ }
+
+ if (!regs->dma_buf_data[win_num][meta_plane].dma_addr) {
+ decon_err("hdr metadata address is NULL\n");
+ return -EINVAL;
+ }
+#if defined(CONFIG_SUPPORT_LEGACY_ION)
+ video_meta = (struct exynos_video_meta *)ion_map_kernel(
+ decon->ion_client,
+ regs->dma_buf_data[win_num][meta_plane].ion_handle);
+#else
+ video_meta = (struct exynos_video_meta *)dma_buf_vmap(
+ regs->dma_buf_data[win_num][meta_plane].dma_buf);
+#endif
+
+ hdr_cmp = memcmp(&decon->prev_hdr_info,
+ &video_meta->data.dec.shdr_static_info,
+ sizeof(struct exynos_hdr_static_info));
+
+ /* HDR metadata is same, so skip subdev call.
+ * Also current hdr_static_info is not copied.
+ */
+ if (hdr_cmp == 0) {
+#if !defined(CONFIG_SUPPORT_LEGACY_ION)
+ dma_buf_vunmap(regs->dma_buf_data[win_num][meta_plane].dma_buf, video_meta);
+#endif
+ return 0;
+ }
+
+ ret = v4l2_subdev_call(decon->displayport_sd, core, ioctl,
+ DISPLAYPORT_IOC_SET_HDR_METADATA,
+ &video_meta->data.dec.shdr_static_info);
+ if (ret)
+ goto err_hdr_io;
+
+ memcpy(&decon->prev_hdr_info,
+ &video_meta->data.dec.shdr_static_info,
+ sizeof(struct exynos_hdr_static_info));
+#if !defined(CONFIG_SUPPORT_LEGACY_ION)
+ dma_buf_vunmap(regs->dma_buf_data[win_num][meta_plane].dma_buf, video_meta);
+#endif
+ return 0;
+
+err_hdr_io:
+ /* When the subdev call is failed,
+ * current hdr_static_info is not copied to prev.
+ */
+ decon_err("hdr metadata info subdev call is failed\n");
+
+#if !defined(CONFIG_SUPPORT_LEGACY_ION)
+ dma_buf_vunmap(regs->dma_buf_data[win_num][meta_plane].dma_buf, video_meta);
+#endif
+ return -EFAULT;
+}
+
+static void decon_update_hdr_info(struct decon_device *decon,
+ struct decon_reg_data *regs)
+{
+
+ int i, ret = 0, win_num = 0, hdr_cnt = 0;
+ unsigned long cur_hdr_bits = 0;
+
+ /* On only DP case, hdr static info could be transfered to DP */
+ if (decon->dt.out_type != DECON_OUT_DP)
+ return;
+
+ decon_dbg("%s +\n", __func__);
+ /* Check hdr configuration of enabled window */
+ for (i = 0; i < decon->dt.max_win; i++) {
+ if (regs->dpp_config[i].state == DECON_WIN_STATE_BUFFER
+ && regs->dpp_config[i].dpp_parm.hdr_std) {
+ set_bit(i, &cur_hdr_bits);
+
+ if (cur_hdr_bits)
+ win_num = i;
+
+ hdr_cnt++;
+ if (hdr_cnt > 1) {
+ decon_err("DP support Only signle HDR\n");
+ ret = -EINVAL;
+ goto err_hdr;
+ }
+ }
+ }
+
+ /* Check hdr configuration or hdr window is chagned */
+ if (decon->prev_hdr_bits != cur_hdr_bits) {
+ if (cur_hdr_bits == 0) {
+ /* Case : HDR ON -> HDR OFF, turn off hdr */
+ ret = decon_set_hdr_info(decon, regs, win_num, false);
+ if (ret)
+ goto err_hdr;
+ } else {
+ /* Case : HDR OFF -> HDR ON, turn on hdr*/
+ /* Case : HDR ON -> HDR ON, hdr window is changed */
+ ret = decon_set_hdr_info(decon, regs, win_num, true);
+ if (ret)
+ goto err_hdr;
+ }
+ /* Save current hdr configuration information */
+ decon->prev_hdr_bits = cur_hdr_bits;
+ } else {
+ if (cur_hdr_bits == 0) {
+ /* Case : HDR OFF -> HDR OFF, Do nothing */
+ } else {
+ /* Case : HDR ON -> HDR ON, compare hdr metadata with prev info */
+ ret = decon_set_hdr_info(decon, regs, win_num, true);
+ if (ret)
+ goto err_hdr;
+ }
+ }
+ decon_dbg("%s -\n", __func__);
+
+err_hdr:
+ /* HDR STANDARD information should be changed to OFF.
+ * Because DP doesn't use the HDR engine
+ */
+ for (i = 0; i < decon->dt.max_win; i++)
+ if (regs->dpp_config[i].dpp_parm.hdr_std != DPP_HDR_OFF)
+ regs->dpp_config[i].dpp_parm.hdr_std = DPP_HDR_OFF;
+ if (ret) {
+ decon_err("set hdr metadata is failed, err no is %d\n", ret);
+ decon->prev_hdr_bits = 0;
+ }
+}
+
+#if defined(CONFIG_EXYNOS_AFBC_DEBUG)
+static void decon_update_vgf_info(struct decon_device *decon,
+ struct decon_reg_data *regs, bool cur)
+{
+ int i;
+ struct dpu_afbc_info *afbc_info;
+
+ decon_dbg("%s +\n", __func__);
+
+ if (cur) /* save current AFBC information */
+ afbc_info = &decon->d.cur_afbc_info;
+ else /* save previous AFBC information */
+ afbc_info = &decon->d.prev_afbc_info;
+
+ memset(afbc_info, 0, sizeof(struct dpu_afbc_info));
+
+ for (i = 0; i < decon->dt.max_win; i++) {
+ if (!regs->dpp_config[i].compression)
+ continue;
+
+ if (test_bit(DPU_DMA2CH(IDMA_VGF0), &decon->cur_using_dpp)) {
+ afbc_info->is_afbc[0] = true;
+
+ if (regs->dma_buf_data[i][0].dma_buf == NULL)
+ continue;
+
+ afbc_info->dma_addr[0] =
+ regs->dma_buf_data[i][0].dma_addr;
+ afbc_info->dma_buf[0] =
+ regs->dma_buf_data[i][0].dma_buf;
+ }
+
+ if (test_bit(DPU_DMA2CH(IDMA_VGF1), &decon->cur_using_dpp)) {
+ afbc_info->is_afbc[1] = true;
+
+ if (regs->dma_buf_data[i][0].dma_buf == NULL)
+ continue;
+
+ afbc_info->dma_addr[1] =
+ regs->dma_buf_data[i][0].dma_addr;
+ afbc_info->dma_buf[1] =
+ regs->dma_buf_data[i][0].dma_buf;
+ }
+ }
+
+ decon_dbg("%s -\n", __func__);
+}
+#endif
+
+static void decon_update_regs(struct decon_device *decon,
+ struct decon_reg_data *regs)
+{
+ struct decon_dma_buf_data old_dma_bufs[decon->dt.max_win][MAX_PLANE_CNT];
+ int old_plane_cnt[MAX_DECON_WIN];
+ struct decon_mode_info psr;
+ int i;
+
+ if (!decon->systrace.pid)
+ decon->systrace.pid = current->pid;
+
+ decon_systrace(decon, 'B', "decon_update_regs", 0);
+
+ decon_exit_hiber(decon);
+
+ decon_acquire_old_bufs(decon, regs, old_dma_bufs, old_plane_cnt);
+
+ decon_systrace(decon, 'C', "decon_fence_wait", 1);
+ for (i = 0; i < decon->dt.max_win; i++) {
+ if (regs->dma_buf_data[i][0].fence)
+ decon_wait_fence(regs->dma_buf_data[i][0].fence);
+ }
+
+ decon_systrace(decon, 'C', "decon_fence_wait", 0);
+
+ decon_check_used_dpp(decon, regs);
+
+#if defined(CONFIG_EXYNOS_AFBC_DEBUG)
+ decon_update_vgf_info(decon, regs, true);
+#endif
+
+ decon_update_hdr_info(decon, regs);
+
+#if defined(CONFIG_EXYNOS9610_BTS)
+ /* add calc and update bw : cur > prev */
+ decon->bts.ops->bts_calc_bw(decon, regs);
+ decon->bts.ops->bts_update_bw(decon, regs, 0);
+#endif
+
+ DPU_EVENT_LOG_WINCON(&decon->sd, regs);
+
+ decon_to_psr_info(decon, &psr);
+ if (regs->num_of_window) {
+ if (__decon_update_regs(decon, regs) < 0) {
+#if defined(CONFIG_EXYNOS_AFBC_DEBUG)
+ decon_dump_afbc_handle(decon, old_dma_bufs);
+#endif
+ decon_dump(decon);
+ BUG();
+ }
+ if (!regs->num_of_window) {
+ __decon_update_clear(decon, regs);
+ decon_wait_for_vsync(decon, VSYNC_TIMEOUT_MSEC);
+ goto end;
+ }
+ } else {
+ __decon_update_clear(decon, regs);
+ decon_wait_for_vsync(decon, VSYNC_TIMEOUT_MSEC);
+ goto end;
+ }
+
+ if (decon->dt.out_type == DECON_OUT_WB) {
+ decon_reg_release_resource(decon->id, &psr);
+ decon_dpp_wait_wb_framedone(decon);
+ /* Stop to prevent resource conflict */
+ decon->cur_using_dpp = 0;
+#if defined(CONFIG_EXYNOS_CONTENT_PATH_PROTECTION)
+ decon_set_protected_content(decon, NULL);
+#endif
+ } else {
+ decon->frame_cnt_target = decon->frame_cnt + 1;
+
+ decon_systrace(decon, 'C', "decon_wait_vsync", 1);
+ decon_wait_for_vsync(decon, VSYNC_TIMEOUT_MSEC);
+ decon_systrace(decon, 'C', "decon_wait_vsync", 0);
+
+ if (decon->cursor.unmask)
+ decon_set_cursor_unmask(decon, false);
+
+ decon_wait_for_vstatus(decon, 50);
+ if (decon_reg_wait_update_done_timeout(decon->id, SHADOW_UPDATE_TIMEOUT) < 0) {
+ decon_up_list_saved();
+#if defined(CONFIG_EXYNOS_AFBC_DEBUG)
+ decon_dump_afbc_handle(decon, old_dma_bufs);
+#endif
+ decon_dump(decon);
+ BUG();
+ }
+
+ if (!decon->low_persistence)
+ decon_reg_set_trigger(decon->id, &psr, DECON_TRIG_DISABLE);
+ }
+
+end:
+ DPU_EVENT_LOG(DPU_EVT_TRIG_MASK, &decon->sd, ktime_set(0, 0));
+
+ decon_release_old_bufs(decon, regs, old_dma_bufs, old_plane_cnt);
+#if defined(CONFIG_SUPPORT_LEGACY_FENCE)
+ decon_signal_fence(decon);
+#else
+ decon_signal_fence(regs->retire_fence);
+ dma_fence_put(regs->retire_fence);
+#endif
+
+ decon_systrace(decon, 'E', "decon_update_regs", 0);
+
+ DPU_EVENT_LOG(DPU_EVT_FENCE_RELEASE, &decon->sd, ktime_set(0, 0));
+
+#if defined(CONFIG_EXYNOS_AFBC_DEBUG)
+ decon_save_vgf_connected_win_id(decon, regs);
+ decon_update_vgf_info(decon, regs, false);
+#endif
+
+#if defined(CONFIG_EXYNOS9610_BTS)
+ /* add update bw : cur < prev */
+ decon->bts.ops->bts_update_bw(decon, regs, 1);
+#endif
+
+ decon_dpp_stop(decon, false);
+}
+
+static void decon_update_regs_handler(struct kthread_work *work)
+{
+ struct decon_update_regs *up =
+ container_of(work, struct decon_update_regs, work);
+ struct decon_device *decon =
+ container_of(up, struct decon_device, up);
+
+ struct decon_reg_data *data, *next;
+ struct list_head saved_list;
+
+ mutex_lock(&decon->up.lock);
+ decon->up.saved_list = decon->up.list;
+ saved_list = decon->up.list;
+ if (!decon->up_list_saved)
+ list_replace_init(&decon->up.list, &saved_list);
+ else
+ list_replace(&decon->up.list, &saved_list);
+ mutex_unlock(&decon->up.lock);
+
+ list_for_each_entry_safe(data, next, &saved_list, list) {
+ decon_systrace(decon, 'C', "update_regs_list", 1);
+
+ decon_set_cursor_reset(decon, data);
+ decon_update_regs(decon, data);
+ decon_hiber_unblock(decon);
+ if (!decon->up_list_saved) {
+ list_del(&data->list);
+ decon_systrace(decon, 'C',
+ "update_regs_list", 0);
+ kfree(data);
+ }
+ }
+}
+
+static int decon_get_active_win_count(struct decon_device *decon,
+ struct decon_win_config_data *win_data)
+{
+ int i;
+ int win_cnt = 0;
+ struct decon_win_config *config;
+ struct decon_win_config *win_config = win_data->config;
+
+ for (i = 0; i < decon->dt.max_win; i++) {
+ config = &win_config[i];
+
+ switch (config->state) {
+ case DECON_WIN_STATE_DISABLED:
+ break;
+
+ case DECON_WIN_STATE_COLOR:
+ case DECON_WIN_STATE_BUFFER:
+ case DECON_WIN_STATE_CURSOR:
+ win_cnt++;
+ break;
+
+ default:
+ decon_warn("DECON:WARN:%s:unrecognized window state %u",
+ __func__, config->state);
+ break;
+ }
+ }
+
+ return win_cnt;
+}
+
+void decon_set_full_size_win(struct decon_device *decon,
+ struct decon_win_config *config)
+{
+ config->dst.x = 0;
+ config->dst.y = 0;
+ config->dst.w = decon->lcd_info->xres;
+ config->dst.h = decon->lcd_info->yres;
+ config->dst.f_w = decon->lcd_info->xres;
+ config->dst.f_h = decon->lcd_info->yres;
+}
+
+static int decon_prepare_win_config(struct decon_device *decon,
+ struct decon_win_config_data *win_data,
+ struct decon_reg_data *regs)
+{
+ int ret = 0;
+ int i;
+ bool color_map;
+ struct decon_win_config *win_config = win_data->config;
+ struct decon_win_config *config;
+ struct decon_window_regs *win_regs;
+
+ decon_dbg("%s +\n", __func__);
+ for (i = 0; i < decon->dt.max_win && !ret; i++) {
+ config = &win_config[i];
+ win_regs = ®s->win_regs[i];
+ color_map = true;
+
+ switch (config->state) {
+ case DECON_WIN_STATE_DISABLED:
+ win_regs->wincon &= ~WIN_EN_F(i);
+ break;
+ case DECON_WIN_STATE_COLOR:
+ regs->num_of_window++;
+ config->color |= (0xFF << 24);
+ win_regs->colormap = config->color;
+
+ /* decon_set_full_size_win(decon, config); */
+ decon_win_config_to_regs_param(0, config, win_regs,
+ config->idma_type, i);
+ ret = 0;
+ break;
+ case DECON_WIN_STATE_BUFFER:
+ case DECON_WIN_STATE_CURSOR: /* cursor async */
+ if (decon_set_win_blocking_mode(decon, i, win_config, regs))
+ break;
+
+ regs->num_of_window++;
+ ret = decon_set_win_buffer(decon, config, regs, i);
+ if (!ret)
+ color_map = false;
+
+ regs->is_cursor_win[i] = false;
+ if (config->state == DECON_WIN_STATE_CURSOR) {
+ regs->is_cursor_win[i] = true;
+ regs->cursor_win = i;
+ }
+ config->state = DECON_WIN_STATE_BUFFER;
+ break;
+ default:
+ win_regs->wincon &= ~WIN_EN_F(i);
+ decon_warn("unrecognized window state %u",
+ config->state);
+ ret = -EINVAL;
+ break;
+ }
+ win_regs->winmap_state = color_map;
+ }
+
+ if (decon->dt.out_type == DECON_OUT_WB) {
+ regs->protection[MAX_DECON_WIN] = win_config[MAX_DECON_WIN].protection;
+ ret = decon_import_buffer(decon, MAX_DECON_WIN,
+ &win_config[MAX_DECON_WIN], regs);
+ }
+
+ for (i = 0; i < MAX_DPP_SUBDEV; i++) {
+ memcpy(®s->dpp_config[i], &win_config[i],
+ sizeof(struct decon_win_config));
+ regs->dpp_config[i].format =
+ dpu_translate_fmt_to_dpp(regs->dpp_config[i].format);
+ }
+
+ decon_dbg("%s -\n", __func__);
+
+ return ret;
+}
+
+static int decon_set_win_config(struct decon_device *decon,
+ struct decon_win_config_data *win_data)
+{
+ int num_of_window = 0;
+ struct decon_reg_data *regs;
+ struct sync_file *sync_file;
+ int i, j, ret = 0;
+ decon_dbg("%s +\n", __func__);
+
+ mutex_lock(&decon->lock);
+
+ if (IS_DECON_OFF_STATE(decon) ||
+ decon->state == DECON_STATE_TUI ||
+ IS_ENABLED(CONFIG_EXYNOS_VIRTUAL_DISPLAY)) {
+ win_data->retire_fence = decon_create_fence(decon, &sync_file);
+ if (win_data->retire_fence < 0)
+ goto err;
+ fd_install(win_data->retire_fence, sync_file->file);
+#if defined(CONFIG_SUPPORT_LEGACY_FENCE)
+ decon_signal_fence(decon);
+#else
+ decon_signal_fence(sync_file->fence);
+#endif
+ goto err;
+ }
+
+ regs = kzalloc(sizeof(struct decon_reg_data), GFP_KERNEL);
+ if (!regs) {
+ decon_err("could not allocate decon_reg_data\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ num_of_window = decon_get_active_win_count(decon, win_data);
+ if (num_of_window) {
+ win_data->retire_fence = decon_create_fence(decon, &sync_file);
+ if (win_data->retire_fence < 0)
+ goto err_prepare;
+ } else {
+#if defined(CONFIG_SUPPORT_LEGACY_FENCE)
+ decon->timeline_max++;
+#endif
+ win_data->retire_fence = -1;
+ }
+
+ dpu_prepare_win_update_config(decon, win_data, regs);
+
+ ret = decon_prepare_win_config(decon, win_data, regs);
+ if (ret)
+ goto err_prepare;
+
+ /*
+ * If dpu_prepare_win_update_config returns error, prev_up_region is
+ * updated but that partial size is not applied to HW in previous code.
+ * So, updating prev_up_region is moved here.
+ */
+ memcpy(&decon->win_up.prev_up_region, ®s->up_region,
+ sizeof(struct decon_rect));
+
+ if (num_of_window) {
+ fd_install(win_data->retire_fence, sync_file->file);
+ decon_create_release_fences(decon, win_data, sync_file);
+#if !defined(CONFIG_SUPPORT_LEGACY_FENCE)
+ regs->retire_fence = dma_fence_get(sync_file->fence);
+#endif
+ }
+
+ decon_hiber_block(decon);
+
+ mutex_lock(&decon->up.lock);
+ list_add_tail(®s->list, &decon->up.list);
+ mutex_unlock(&decon->up.lock);
+ kthread_queue_work(&decon->up.worker, &decon->up.work);
+
+ mutex_unlock(&decon->lock);
+ decon_systrace(decon, 'C', "decon_win_config", 0);
+
+ decon_dbg("%s -\n", __func__);
+
+ return ret;
+
+err_prepare:
+ if (win_data->retire_fence >= 0) {
+ /* video mode should keep previous buffer object */
+ if (decon->lcd_info->mode == DECON_MIPI_COMMAND_MODE) {
+#if defined(CONFIG_SUPPORT_LEGACY_FENCE)
+ decon_signal_fence(decon);
+#else
+ decon_signal_fence(sync_file->fence);
+#endif
+ }
+ fput(sync_file->file);
+ put_unused_fd(win_data->retire_fence);
+ }
+ win_data->retire_fence = -1;
+
+ for (i = 0; i < decon->dt.max_win; i++)
+ for (j = 0; j < regs->plane_cnt[i]; ++j)
+ decon_free_unused_buf(decon, regs, i, j);
+
+ kfree(regs);
+err:
+ mutex_unlock(&decon->lock);
+ return ret;
+}
+
+static int decon_get_hdr_capa(struct decon_device *decon,
+ struct decon_hdr_capabilities *hdr_capa)
+{
+ int ret = 0;
+ int k;
+
+ decon_dbg("%s +\n", __func__);
+ mutex_lock(&decon->lock);
+
+ if (decon->dt.out_type == DECON_OUT_DSI) {
+ for (k = 0; k < decon->lcd_info->dt_lcd_hdr.hdr_num; k++)
+ hdr_capa->out_types[k] =
+ decon->lcd_info->dt_lcd_hdr.hdr_type[k];
+ } else if (decon->dt.out_type == DECON_OUT_DP) {
+#if defined(CONFIG_EXYNOS_DISPLAYPORT)
+ decon_displayport_get_hdr_capa(decon, hdr_capa);
+#endif
+ } else
+ memset(hdr_capa, 0, sizeof(struct decon_hdr_capabilities));
+
+ mutex_unlock(&decon->lock);
+ decon_dbg("%s -\n", __func__);
+
+ return ret;
+}
+
+static int decon_get_hdr_capa_info(struct decon_device *decon,
+ struct decon_hdr_capabilities_info *hdr_capa_info)
+{
+ int ret = 0;
+
+ decon_dbg("%s +\n", __func__);
+ mutex_lock(&decon->lock);
+
+ if (decon->dt.out_type == DECON_OUT_DSI) {
+ hdr_capa_info->out_num =
+ decon->lcd_info->dt_lcd_hdr.hdr_num;
+ hdr_capa_info->max_luminance =
+ decon->lcd_info->dt_lcd_hdr.hdr_max_luma;
+ hdr_capa_info->max_average_luminance =
+ decon->lcd_info->dt_lcd_hdr.hdr_max_avg_luma;
+ hdr_capa_info->min_luminance =
+ decon->lcd_info->dt_lcd_hdr.hdr_min_luma;
+ } else if (decon->dt.out_type == DECON_OUT_DP) {
+#if defined(CONFIG_EXYNOS_DISPLAYPORT)
+ decon_displayport_get_hdr_capa_info(decon, hdr_capa_info);
+#endif
+ } else
+ memset(hdr_capa_info, 0, sizeof(struct decon_hdr_capabilities_info));
+
+ mutex_unlock(&decon->lock);
+ decon_dbg("%s -\n", __func__);
+
+ return ret;
+
+}
+
+static int decon_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
+{
+ struct decon_win *win = info->par;
+ struct decon_device *decon = win->decon;
+ struct decon_lcd *lcd_info = decon->lcd_info;
+ struct lcd_mres_info *mres_info = &lcd_info->dt_lcd_mres;
+ struct decon_win_config_data win_data;
+ struct decon_disp_info disp_info;
+#if defined(CONFIG_EXYNOS_DISPLAYPORT)
+ struct exynos_displayport_data displayport_data;
+#endif
+ struct decon_hdr_capabilities hdr_capa;
+ struct decon_hdr_capabilities_info hdr_capa_info;
+ struct decon_user_window user_window; /* cursor async */
+ struct decon_win_config_data __user *argp;
+ struct decon_disp_info __user *argp_info;
+ int ret = 0;
+ u32 crtc;
+ bool active;
+ u32 crc_bit, crc_start;
+ u32 crc_data[2];
+ u32 pwr;
+
+ decon_hiber_block_exit(decon);
+ switch (cmd) {
+ case FBIO_WAITFORVSYNC:
+ if (get_user(crtc, (u32 __user *)arg)) {
+ ret = -EFAULT;
+ break;
+ }
+
+ if (crtc == 0)
+ ret = decon_wait_for_vsync(decon, VSYNC_TIMEOUT_MSEC);
+ else
+ ret = -ENODEV;
+
+ break;
+
+ case S3CFB_SET_VSYNC_INT:
+ if (get_user(active, (bool __user *)arg)) {
+ ret = -EFAULT;
+ break;
+ }
+
+ ret = decon_set_vsync_int(info, active);
+ break;
+
+ case S3CFB_WIN_CONFIG:
+ argp = (struct decon_win_config_data __user *)arg;
+ DPU_EVENT_LOG(DPU_EVT_WIN_CONFIG, &decon->sd, ktime_set(0, 0));
+ decon_systrace(decon, 'C', "decon_win_config", 1);
+ if (copy_from_user(&win_data,
+ (struct decon_win_config_data __user *)arg,
+ sizeof(struct decon_win_config_data))) {
+ ret = -EFAULT;
+ break;
+ }
+
+ ret = decon_set_win_config(decon, &win_data);
+ if (ret)
+ break;
+
+ if (copy_to_user((void __user *)arg, &win_data, _IOC_SIZE(cmd))) {
+ ret = -EFAULT;
+ break;
+ }
+ break;
+
+ case S3CFB_GET_HDR_CAPABILITIES:
+ ret = decon_get_hdr_capa(decon, &hdr_capa);
+ if (ret)
+ break;
+
+ if (copy_to_user((struct decon_hdr_capabilities __user *)arg,
+ &hdr_capa,
+ sizeof(struct decon_hdr_capabilities))) {
+ ret = -EFAULT;
+ break;
+ }
+ break;
+
+ case S3CFB_GET_HDR_CAPABILITIES_NUM:
+ ret = decon_get_hdr_capa_info(decon, &hdr_capa_info);
+ if (ret)
+ break;
+
+ if (copy_to_user((struct decon_hdr_capabilities_info __user *)arg,
+ &hdr_capa_info,
+ sizeof(struct decon_hdr_capabilities_info))) {
+ ret = -EFAULT;
+ break;
+ }
+ break;
+
+ case EXYNOS_DISP_INFO:
+ argp_info = (struct decon_disp_info __user *)arg;
+ if (copy_from_user(&disp_info, argp_info,
+ sizeof(struct decon_disp_info))) {
+ ret = -EFAULT;
+ break;
+ }
+
+ decon_info("HWC version %d.0 is operating\n", disp_info.ver);
+ disp_info.psr_mode = decon->dt.psr_mode;
+ disp_info.chip_ver = CHIP_VER;
+ disp_info.mres_info = *mres_info;
+
+ if (copy_to_user(argp_info,
+ &disp_info, sizeof(struct decon_disp_info))) {
+ ret = -EFAULT;
+ break;
+ }
+ break;
+
+ case S3CFB_START_CRC:
+ if (get_user(crc_start, (u32 __user *)arg)) {
+ ret = -EFAULT;
+ break;
+ }
+ mutex_lock(&decon->lock);
+ if (!IS_DECON_ON_STATE(decon)) {
+ decon_err("DECON:WRN:%s:decon%d is not active:cmd=%d\n",
+ __func__, decon->id, cmd);
+ ret = -EIO;
+ mutex_unlock(&decon->lock);
+ break;
+ }
+ mutex_unlock(&decon->lock);
+ decon_reg_set_start_crc(decon->id, crc_start);
+ break;
+
+ case S3CFB_SEL_CRC_BITS:
+ if (get_user(crc_bit, (u32 __user *)arg)) {
+ ret = -EFAULT;
+ break;
+ }
+ mutex_lock(&decon->lock);
+ if (!IS_DECON_ON_STATE(decon)) {
+ decon_err("DECON:WRN:%s:decon%d is not active:cmd=%d\n",
+ __func__, decon->id, cmd);
+ ret = -EIO;
+ mutex_unlock(&decon->lock);
+ break;
+ }
+ mutex_unlock(&decon->lock);
+ decon_reg_set_select_crc_bits(decon->id, crc_bit);
+ break;
+
+ case S3CFB_GET_CRC_DATA:
+ mutex_lock(&decon->lock);
+ if (!IS_DECON_ON_STATE(decon)) {
+ decon_err("DECON:WRN:%s:decon%d is not active:cmd=%d\n",
+ __func__, decon->id, cmd);
+ ret = -EIO;
+ mutex_unlock(&decon->lock);
+ break;
+ }
+ mutex_unlock(&decon->lock);
+ decon_reg_get_crc_data(decon->id, &crc_data[0], &crc_data[1]);
+ if (copy_to_user((u32 __user *)arg, &crc_data[0], sizeof(u32))) {
+ ret = -EFAULT;
+ break;
+ }
+ break;
+
+#if defined(CONFIG_EXYNOS_DISPLAYPORT)
+ case EXYNOS_GET_DISPLAYPORT_CONFIG:
+ if (copy_from_user(&displayport_data,
+ (struct exynos_displayport_data __user *)arg,
+ sizeof(displayport_data))) {
+ ret = -EFAULT;
+ break;
+ }
+
+ ret = decon_displayport_get_config(decon, &displayport_data);
+
+ if (copy_to_user((struct exynos_displayport_data __user *)arg,
+ &displayport_data, sizeof(displayport_data))) {
+ ret = -EFAULT;
+ break;
+ }
+
+ decon_dbg("DECON DISPLAYPORT IOCTL EXYNOS_GET_DISPLAYPORT_CONFIG\n");
+ break;
+
+ case EXYNOS_SET_DISPLAYPORT_CONFIG:
+ if (copy_from_user(&displayport_data,
+ (struct exynos_displayport_data __user *)arg,
+ sizeof(displayport_data))) {
+ ret = -EFAULT;
+ break;
+ }
+
+ ret = decon_displayport_set_config(decon, &displayport_data);
+ decon_dbg("DECON DISPLAYPORT IOCTL EXYNOS_SET_DISPLAYPORT_CONFIG\n");
+ break;
+
+ case DISPLAYPORT_IOC_DP_SA_SORTING:
+ decon_info("DISPLAY_IOC_DP_SA_SORTING is called\n");
+ ret = displayport_reg_stand_alone_crc_sorting();
+ break;
+#endif
+ case EXYNOS_DPU_DUMP:
+ mutex_lock(&decon->lock);
+ if (!IS_DECON_ON_STATE(decon)) {
+ decon_err("DECON:WRN:%s:decon%d is not active:cmd=%d\n",
+ __func__, decon->id, cmd);
+ ret = -EIO;
+ mutex_unlock(&decon->lock);
+ break;
+ }
+ mutex_unlock(&decon->lock);
+ decon_dump(decon);
+ break;
+ case DECON_WIN_CURSOR_POS: /* cursor async */
+ if (copy_from_user(&user_window,
+ (struct decon_user_window __user *)arg,
+ sizeof(struct decon_user_window))) {
+ ret = -EFAULT;
+ break;
+ }
+ if ((decon->id == 2) &&
+ (decon->lcd_info->mode == DECON_VIDEO_MODE)) {
+ decon_dbg("cursor pos : x:%d, y:%d\n",
+ user_window.x, user_window.y);
+ ret = decon_set_cursor_win_config(decon, user_window.x,
+ user_window.y);
+ } else {
+ decon_err("decon[%d] is not support CURSOR ioctl\n",
+ decon->id);
+ ret = -EPERM;
+ }
+ break;
+
+ case S3CFB_POWER_MODE:
+ if (!IS_ENABLED(CONFIG_EXYNOS_DOZE)) {
+ decon_info("DOZE mode is disabled\n");
+ break;
+ }
+
+ if (get_user(pwr, (int __user *)arg)) {
+ ret = -EFAULT;
+ break;
+ }
+
+
+ if (pwr != DISP_PWR_DOZE && pwr != DISP_PWR_DOZE_SUSPEND) {
+ decon_err("%s: decon%d invalid pwr_state %d\n",
+ __func__, decon->id, pwr);
+ break;
+ }
+
+ ret = decon_update_pwr_state(decon, pwr);
+ if (ret) {
+ decon_err("%s: decon%d failed to set doze %d, ret %d\n",
+ __func__, decon->id, pwr, ret);
+ break;
+ }
+ break;
+
+ default:
+ ret = -ENOTTY;
+ }
+
+ decon_hiber_unblock(decon);
+ return ret;
+}
+
+static ssize_t decon_fb_read(struct fb_info *info, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return 0;
+}
+
+static ssize_t decon_fb_write(struct fb_info *info, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return 0;
+}
+
+int decon_release(struct fb_info *info, int user)
+{
+ struct decon_win *win = info->par;
+ struct decon_device *decon = win->decon;
+
+ decon_info("%s + : %d\n", __func__, decon->id);
+
+ if (decon->id && decon->dt.out_type == DECON_OUT_DSI) {
+ decon_get_out_sd(decon);
+ decon_info("output device of decon%d is changed to %s\n",
+ decon->id, decon->out_sd[0]->name);
+ }
+
+ if (decon->dt.out_type == DECON_OUT_DSI) {
+ decon_hiber_block_exit(decon);
+ /* Unused DECON state is DECON_STATE_INIT */
+ if (IS_DECON_ON_STATE(decon))
+ decon_disable(decon);
+ decon_hiber_unblock(decon);
+ }
+
+ if (decon->dt.out_type == DECON_OUT_DP) {
+ /* Unused DECON state is DECON_STATE_INIT */
+ if (IS_DECON_ON_STATE(decon))
+ decon_dp_disable(decon);
+ }
+
+ decon_info("%s - : %d\n", __func__, decon->id);
+
+ return 0;
+}
+
+#ifdef CONFIG_COMPAT
+static int decon_compat_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
+{
+ arg = (unsigned long) compat_ptr(arg);
+ return decon_ioctl(info, cmd, arg);
+}
+#endif
+
+/* ---------- FREAMBUFFER INTERFACE ----------- */
+static struct fb_ops decon_fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = decon_check_var,
+ .fb_set_par = decon_set_par,
+ .fb_blank = decon_blank,
+ .fb_setcolreg = decon_setcolreg,
+ .fb_fillrect = cfb_fillrect,
+#ifdef CONFIG_COMPAT
+ .fb_compat_ioctl = decon_compat_ioctl,
+#endif
+ .fb_ioctl = decon_ioctl,
+ .fb_read = decon_fb_read,
+ .fb_write = decon_fb_write,
+ .fb_pan_display = decon_pan_display,
+ .fb_mmap = decon_mmap,
+ .fb_release = decon_release,
+};
+
+/* ---------- POWER MANAGEMENT ----------- */
+void decon_clocks_info(struct decon_device *decon)
+{
+}
+
+void decon_put_clocks(struct decon_device *decon)
+{
+}
+
+int decon_runtime_resume(struct device *dev)
+{
+ struct decon_device *decon = dev_get_drvdata(dev);
+
+ decon_dbg("decon%d %s +\n", decon->id, __func__);
+ clk_prepare_enable(decon->res.aclk);
+/*
+ * TODO :
+ * There was an under-run issue when DECON suspend/resume
+ * was operating while DP was operating.
+ */
+
+ DPU_EVENT_LOG(DPU_EVT_DECON_RESUME, &decon->sd, ktime_set(0, 0));
+ decon_dbg("decon%d %s -\n", decon->id, __func__);
+
+ return 0;
+}
+
+int decon_runtime_suspend(struct device *dev)
+{
+ struct decon_device *decon = dev_get_drvdata(dev);
+
+ decon_dbg("decon%d %s +\n", decon->id, __func__);
+ clk_disable_unprepare(decon->res.aclk);
+
+/*
+ * TODO :
+ * There was an under-run issue when DECON suspend/resume
+ * was operating while DP was operating.
+ */
+
+ DPU_EVENT_LOG(DPU_EVT_DECON_SUSPEND, &decon->sd, ktime_set(0, 0));
+ decon_dbg("decon%d %s -\n", decon->id, __func__);
+
+ return 0;
+}
+
+const struct dev_pm_ops decon_pm_ops = {
+ .runtime_suspend = decon_runtime_suspend,
+ .runtime_resume = decon_runtime_resume,
+};
+
+static int decon_register_subdevs(struct decon_device *decon)
+{
+ struct v4l2_device *v4l2_dev = &decon->v4l2_dev;
+ int i, ret = 0;
+
+ snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s",
+ dev_name(decon->dev));
+ ret = v4l2_device_register(decon->dev, &decon->v4l2_dev);
+ if (ret) {
+ decon_err("failed to register v4l2 device : %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < MAX_DPP_SUBDEV; ++i)
+ decon->dpp_sd[i] = NULL;
+ ret = dpu_get_sd_by_drvname(decon, DPP_MODULE_NAME);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < MAX_DSIM_CNT; ++i)
+ decon->dsim_sd[i] = NULL;
+ ret = dpu_get_sd_by_drvname(decon, DSIM_MODULE_NAME);
+ if (ret)
+ return ret;
+#if defined(CONFIG_EXYNOS_DISPLAYPORT)
+ ret = dpu_get_sd_by_drvname(decon, DISPLAYPORT_MODULE_NAME);
+ if (ret)
+ return ret;
+#endif
+
+ if (!decon->id) {
+ for (i = 0; i < MAX_DPP_SUBDEV; i++) {
+ if (IS_ERR_OR_NULL(decon->dpp_sd[i]))
+ continue;
+ ret = v4l2_device_register_subdev(v4l2_dev,
+ decon->dpp_sd[i]);
+ if (ret) {
+ decon_err("failed to register dpp%d sd\n", i);
+ return ret;
+ }
+ }
+
+ for (i = 0; i < MAX_DSIM_CNT; i++) {
+ if (decon->dsim_sd[i] == NULL || i == 1)
+ continue;
+
+ ret = v4l2_device_register_subdev(v4l2_dev,
+ decon->dsim_sd[i]);
+ if (ret) {
+ decon_err("failed to register dsim%d sd\n", i);
+ return ret;
+ }
+ }
+#if defined(CONFIG_EXYNOS_DISPLAYPORT)
+ ret = v4l2_device_register_subdev(v4l2_dev, decon->displayport_sd);
+ if (ret) {
+ decon_err("failed to register displayport sd\n");
+ return ret;
+ }
+#endif
+ }
+
+ ret = v4l2_device_register_subdev_nodes(&decon->v4l2_dev);
+ if (ret) {
+ decon_err("failed to make nodes for subdev\n");
+ return ret;
+ }
+
+ decon_dbg("Register V4L2 subdev nodes for DECON\n");
+
+ if (decon->dt.out_type == DECON_OUT_DSI)
+ ret = decon_get_out_sd(decon);
+ else if (decon->dt.out_type == DECON_OUT_WB)
+ ret = decon_wb_get_out_sd(decon);
+#if defined(CONFIG_EXYNOS_DISPLAYPORT)
+ else if (decon->dt.out_type == DECON_OUT_DP)
+ ret = decon_displayport_get_out_sd(decon);
+#endif
+
+ return ret;
+}
+
+static void decon_unregister_subdevs(struct decon_device *decon)
+{
+ int i;
+
+ if (!decon->id) {
+ for (i = 0; i < MAX_DPP_SUBDEV; i++) {
+ if (decon->dpp_sd[i] == NULL)
+ continue;
+ v4l2_device_unregister_subdev(decon->dpp_sd[i]);
+ }
+
+ for (i = 0; i < MAX_DSIM_CNT; i++) {
+ if (decon->dsim_sd[i] == NULL || i == 1)
+ continue;
+ v4l2_device_unregister_subdev(decon->dsim_sd[i]);
+ }
+
+ if (decon->displayport_sd != NULL)
+ v4l2_device_unregister_subdev(decon->displayport_sd);
+ }
+
+ v4l2_device_unregister(&decon->v4l2_dev);
+}
+
+static void decon_release_windows(struct decon_win *win)
+{
+ if (win->fbinfo)
+ framebuffer_release(win->fbinfo);
+}
+
+static int decon_fb_alloc_memory(struct decon_device *decon, struct decon_win *win)
+{
+ struct decon_lcd *lcd_info = decon->lcd_info;
+ struct fb_info *fbi = win->fbinfo;
+ struct displayport_device *displayport;
+ struct dsim_device *dsim;
+ struct device *dev;
+ unsigned int real_size, virt_size, size;
+ dma_addr_t map_dma;
+#if defined(CONFIG_SUPPORT_LEGACY_ION)
+ struct ion_handle *handle;
+#endif
+ struct dma_buf *buf;
+ void *vaddr;
+ unsigned int ret;
+
+ decon_dbg("%s +\n", __func__);
+ dev_info(decon->dev, "allocating memory for display\n");
+
+ real_size = lcd_info->xres * lcd_info->yres;
+ virt_size = lcd_info->xres * (lcd_info->yres * 2);
+
+ dev_info(decon->dev, "real_size=%u (%u.%u), virt_size=%u (%u.%u)\n",
+ real_size, lcd_info->xres, lcd_info->yres,
+ virt_size, lcd_info->xres, lcd_info->yres * 2);
+
+ size = (real_size > virt_size) ? real_size : virt_size;
+ size *= DEFAULT_BPP / 8;
+
+ fbi->fix.smem_len = size;
+ size = PAGE_ALIGN(size);
+
+ dev_info(decon->dev, "want %u bytes for window[%d]\n", size, win->idx);
+#if defined(CONFIG_SUPPORT_LEGACY_ION)
+ handle = ion_alloc(decon->ion_client, (size_t)size, 0,
+ EXYNOS_ION_HEAP_SYSTEM_MASK, 0);
+ if (IS_ERR(handle)) {
+ dev_err(decon->dev, "failed to ion_alloc\n");
+ return -ENOMEM;
+ }
+
+ buf = ion_share_dma_buf(decon->ion_client, handle);
+ if (IS_ERR_OR_NULL(buf)) {
+ dev_err(decon->dev, "ion_share_dma_buf() failed\n");
+ goto err_share_dma_buf;
+ }
+
+ vaddr = ion_map_kernel(decon->ion_client, handle);
+#else
+ buf = ion_alloc_dmabuf("ion_system_heap", (size_t)size, 0);
+ if (IS_ERR(buf)) {
+ dev_err(decon->dev, "ion_share_dma_buf() failed\n");
+ goto err_share_dma_buf;
+ }
+
+ vaddr = dma_buf_vmap(buf);
+#endif
+
+ memset(vaddr, 0x00, size);
+
+ fbi->screen_base = vaddr;
+
+#if !defined(CONFIG_SUPPORT_LEGACY_ION)
+ dma_buf_vunmap(buf, vaddr);
+#endif
+
+ fbi->screen_base = NULL;
+
+ win->dma_buf_data[1].fence = NULL;
+ win->dma_buf_data[2].fence = NULL;
+ win->plane_cnt = 1;
+
+ if (decon->dt.out_type == DECON_OUT_DP) {
+ displayport = v4l2_get_subdevdata(decon->out_sd[0]);
+ dev = displayport->dev;
+ } else { /* DSI case */
+ dsim = v4l2_get_subdevdata(decon->out_sd[0]);
+ dev = dsim->dev;
+ }
+#if defined(CONFIG_SUPPORT_LEGACY_ION)
+ ret = decon_map_ion_handle(decon, dev, &win->dma_buf_data[0], handle,
+ buf, win->idx);
+#else
+ ret = decon_map_ion_handle(decon, dev, &win->dma_buf_data[0],
+ buf, win->idx);
+#endif
+ if (!ret)
+ goto err_map;
+ map_dma = win->dma_buf_data[0].dma_addr;
+
+ dev_info(decon->dev, "alloated memory\n");
+
+ fbi->fix.smem_start = map_dma;
+
+ dev_info(decon->dev, "fb start addr = 0x%x\n", (u32)fbi->fix.smem_start);
+
+ decon_dbg("%s -\n", __func__);
+
+ return 0;
+
+err_map:
+ dma_buf_put(buf);
+err_share_dma_buf:
+#if defined(CONFIG_SUPPORT_LEGACY_ION)
+ ion_free(decon->ion_client, handle);
+#endif
+ return -ENOMEM;
+}
+
+#if defined(CONFIG_FB_TEST)
+static int decon_fb_test_alloc_memory(struct decon_device *decon, u32 size)
+{
+ struct fb_info *fbi = decon->win[decon->dt.dft_win]->fbinfo;
+ struct decon_win *win = decon->win[decon->dt.dft_win];
+ struct displayport_device *displayport;
+ struct dsim_device *dsim;
+ struct device *dev;
+ dma_addr_t map_dma;
+ struct ion_handle *handle;
+ struct dma_buf *buf;
+ void *vaddr;
+ unsigned int ret;
+
+ decon_dbg("%s +\n", __func__);
+ dev_info(decon->dev, "allocating memory for fb test\n");
+
+ size = PAGE_ALIGN(size);
+ fbi->fix.smem_len = size;
+
+ dev_info(decon->dev, "want %u bytes for window[%d]\n", size, win->idx);
+
+ handle = ion_alloc(decon->ion_client, (size_t)size, 0,
+ EXYNOS_ION_HEAP_SYSTEM_MASK, 0);
+ if (IS_ERR(handle)) {
+ dev_err(decon->dev, "failed to ion_alloc\n");
+ return -ENOMEM;
+ }
+
+ buf = ion_share_dma_buf(decon->ion_client, handle);
+ if (IS_ERR_OR_NULL(buf)) {
+ dev_err(decon->dev, "ion_share_dma_buf() failed\n");
+ goto err_share_dma_buf;
+ }
+
+ vaddr = ion_map_kernel(decon->ion_client, handle);
+
+ memset(vaddr, 0x00, size);
+
+ fbi->screen_base = vaddr;
+
+ if (decon->dt.out_type == DECON_OUT_DP) {
+ displayport = v4l2_get_subdevdata(decon->out_sd[0]);
+ dev = displayport->dev;
+ } else { /* DSI case */
+ dsim = v4l2_get_subdevdata(decon->out_sd[0]);
+ dev = dsim->dev;
+ }
+ ret = decon_map_ion_handle(decon, dev, &win->fb_buf_data, handle,
+ buf, win->idx);
+ if (!ret)
+ goto err_map;
+ map_dma = win->fb_buf_data.dma_addr;
+
+ dev_info(decon->dev, "alloated memory\n");
+ fbi->fix.smem_start = map_dma;
+
+ dev_info(decon->dev, "fb start addr = 0x%x\n", (u32)fbi->fix.smem_start);
+
+ decon_dbg("%s -\n", __func__);
+
+ return 0;
+
+err_map:
+ dma_buf_put(buf);
+err_share_dma_buf:
+ ion_free(decon->ion_client, handle);
+ return -ENOMEM;
+}
+#endif
+
+static int decon_acquire_window(struct decon_device *decon, int idx)
+{
+ struct decon_win *win;
+ struct fb_info *fbinfo;
+ struct fb_var_screeninfo *var;
+ struct decon_lcd *lcd_info = decon->lcd_info;
+ int ret, i;
+
+ decon_dbg("acquire DECON window%d\n", idx);
+
+ fbinfo = framebuffer_alloc(sizeof(struct decon_win), decon->dev);
+ if (!fbinfo) {
+ decon_err("failed to allocate framebuffer\n");
+ return -ENOENT;
+ }
+
+ win = fbinfo->par;
+ decon->win[idx] = win;
+ var = &fbinfo->var;
+ win->fbinfo = fbinfo;
+ win->decon = decon;
+ win->idx = idx;
+
+ if (decon->dt.out_type == DECON_OUT_DSI
+ || decon->dt.out_type == DECON_OUT_DP) {
+ win->videomode.left_margin = lcd_info->hbp;
+ win->videomode.right_margin = lcd_info->hfp;
+ win->videomode.upper_margin = lcd_info->vbp;
+ win->videomode.lower_margin = lcd_info->vfp;
+ win->videomode.hsync_len = lcd_info->hsa;
+ win->videomode.vsync_len = lcd_info->vsa;
+ win->videomode.xres = lcd_info->xres;
+ win->videomode.yres = lcd_info->yres;
+ fb_videomode_to_var(&fbinfo->var, &win->videomode);
+ }
+
+ for (i = 0; i < MAX_PLANE_CNT; ++i)
+ memset(&win->dma_buf_data[i], 0, sizeof(win->dma_buf_data[i]));
+
+ if (((decon->dt.out_type == DECON_OUT_DSI) || (decon->dt.out_type == DECON_OUT_DP))
+ && (idx == decon->dt.dft_win)) {
+ ret = decon_fb_alloc_memory(decon, win);
+ if (ret) {
+ dev_err(decon->dev, "failed to alloc display memory\n");
+ return ret;
+ }
+#if defined(CONFIG_FB_TEST)
+ ret = decon_fb_test_alloc_memory(decon,
+ win->fbinfo->fix.smem_len);
+ if (ret) {
+ dev_err(decon->dev, "failed to alloc test fb memory\n");
+ return ret;
+ }
+#endif
+ }
+
+ fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
+ fbinfo->fix.accel = FB_ACCEL_NONE;
+ fbinfo->var.activate = FB_ACTIVATE_NOW;
+ fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
+ fbinfo->var.bits_per_pixel = DEFAULT_BPP;
+ fbinfo->var.width = lcd_info->width;
+ fbinfo->var.height = lcd_info->height;
+ fbinfo->var.yres_virtual = lcd_info->yres * 2;
+ fbinfo->fbops = &decon_fb_ops;
+ fbinfo->flags = FBINFO_FLAG_DEFAULT;
+ fbinfo->pseudo_palette = &win->pseudo_palette;
+ /* 'divide by 8' means converting bit to byte number */
+ fbinfo->fix.line_length = fbinfo->var.xres * fbinfo->var.bits_per_pixel / 8;
+ fbinfo->fix.ypanstep = 1;
+ decon_info("default_win %d win_idx %d xres %d yres %d\n",
+ decon->dt.dft_win, idx,
+ fbinfo->var.xres, fbinfo->var.yres);
+
+ ret = decon_check_var(&fbinfo->var, fbinfo);
+ if (ret < 0) {
+ dev_err(decon->dev, "check_var failed on initial video params\n");
+ return ret;
+ }
+
+ decon_dbg("decon%d window[%d] create\n", decon->id, idx);
+ return 0;
+}
+
+static int decon_acquire_windows(struct decon_device *decon)
+{
+ int i, ret;
+
+ for (i = 0; i < decon->dt.max_win; i++) {
+ ret = decon_acquire_window(decon, i);
+ if (ret < 0) {
+ decon_err("failed to create decon-int window[%d]\n", i);
+ for (; i >= 0; i--)
+ decon_release_windows(decon->win[i]);
+ return ret;
+ }
+ }
+
+ ret = register_framebuffer(decon->win[decon->dt.dft_win]->fbinfo);
+ if (ret) {
+ decon_err("failed to register framebuffer\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void decon_parse_dt(struct decon_device *decon)
+{
+ struct device_node *te_eint;
+ struct device_node *cam_stat;
+ struct device *dev = decon->dev;
+ int ret;
+
+ if (!dev->of_node) {
+ decon_warn("no device tree information\n");
+ return;
+ }
+
+ decon->id = of_alias_get_id(dev->of_node, "decon");
+ of_property_read_u32(dev->of_node, "max_win",
+ &decon->dt.max_win);
+ of_property_read_u32(dev->of_node, "default_win",
+ &decon->dt.dft_win);
+ of_property_read_u32(dev->of_node, "default_idma",
+ &decon->dt.dft_idma);
+ /* video mode: 0, dp: 1 mipi command mode: 2 */
+ of_property_read_u32(dev->of_node, "psr_mode",
+ &decon->dt.psr_mode);
+ /* H/W trigger: 0, S/W trigger: 1 */
+ of_property_read_u32(dev->of_node, "trig_mode",
+ &decon->dt.trig_mode);
+ decon_info("decon-%s: max win%d, %s mode, %s trigger\n",
+ (decon->id == 0) ? "f" : ((decon->id == 1) ? "s" : "t"),
+ decon->dt.max_win,
+ decon->dt.psr_mode ? "command" : "video",
+ decon->dt.trig_mode ? "sw" : "hw");
+
+ /* 0: DSI_MODE_SINGLE, 1: DSI_MODE_DUAL_DSI */
+ of_property_read_u32(dev->of_node, "dsi_mode", &decon->dt.dsi_mode);
+ decon_info("dsi mode(%d). 0: SINGLE 1: DUAL\n", decon->dt.dsi_mode);
+
+ of_property_read_u32(dev->of_node, "out_type", &decon->dt.out_type);
+ decon_info("out type(%d). 0: DSI 1: DISPLAYPORT 2: HDMI 3: WB\n",
+ decon->dt.out_type);
+
+ if (decon->dt.out_type == DECON_OUT_DSI) {
+ ret = of_property_read_u32_index(dev->of_node, "out_idx", 0,
+ &decon->dt.out_idx[0]);
+ if (ret) {
+ decon->dt.out_idx[0] = 0;
+ decon_info("failed to parse out_idx[0].\n");
+ }
+ decon_info("out idx(%d). 0: DSI0 1: DSI1 2: DSI2\n",
+ decon->dt.out_idx[0]);
+
+ if (decon->dt.dsi_mode == DSI_MODE_DUAL_DSI) {
+ ret = of_property_read_u32_index(dev->of_node,
+ "out_idx", 1, &decon->dt.out_idx[1]);
+ if (ret) {
+ decon->dt.out_idx[1] = 1;
+ decon_info("failed to parse out_idx[1].\n");
+ }
+ decon_info("out1 idx(%d). 0: DSI0 1: DSI1 2: DSI2\n",
+ decon->dt.out_idx[1]);
+ }
+
+ te_eint = of_get_child_by_name(decon->dev->of_node, "te_eint");
+ if (!te_eint) {
+ decon_info("No DT node for te_eint\n");
+ } else {
+ decon->d.eint_pend = of_iomap(te_eint, 0);
+ if (!decon->d.eint_pend)
+ decon_info("Failed to get te eint pend\n");
+ }
+
+ cam_stat = of_get_child_by_name(decon->dev->of_node, "cam-stat");
+ if (!cam_stat) {
+ decon_info("No DT node for cam_stat\n");
+ } else {
+ decon->hiber.cam_status = of_iomap(cam_stat, 0);
+ if (!decon->hiber.cam_status)
+ decon_info("Failed to get CAM0-STAT Reg\n");
+ }
+ }
+}
+
+static int decon_init_resources(struct decon_device *decon,
+ struct platform_device *pdev, char *name)
+{
+ struct resource *res;
+ int ret;
+
+ /* Get memory resource and map SFR region. */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ decon->res.regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR_OR_NULL(decon->res.regs)) {
+ decon_err("failed to remap register region\n");
+ ret = -ENOENT;
+ goto err;
+ }
+
+ if (decon->dt.out_type == DECON_OUT_DSI) {
+ decon_get_clocks(decon);
+ ret = decon_register_irq(decon);
+ if (ret)
+ goto err;
+
+ if (decon->dt.psr_mode != DECON_VIDEO_MODE) {
+ ret = decon_register_ext_irq(decon);
+ if (ret)
+ goto err;
+ }
+ } else if (decon->dt.out_type == DECON_OUT_WB) {
+ decon_wb_get_clocks(decon);
+ ret = decon_wb_register_irq(decon);
+ if (ret)
+ goto err;
+ } else if (decon->dt.out_type == DECON_OUT_DP) {
+#if defined(CONFIG_EXYNOS_DISPLAYPORT)
+ decon_displayport_get_clocks(decon);
+ ret = decon_displayport_register_irq(decon);
+ if (ret)
+ goto err;
+#endif
+ } else {
+ decon_err("not supported output type(%d)\n", decon->dt.out_type);
+ }
+
+ decon->res.ss_regs = dpu_get_sysreg_addr();
+ if (IS_ERR_OR_NULL(decon->res.ss_regs)) {
+ decon_err("failed to get sysreg addr\n");
+ goto err;
+ }
+
+#if defined(CONFIG_SUPPORT_LEGACY_ION)
+ decon->ion_client = exynos_ion_client_create(name);
+ if (IS_ERR(decon->ion_client)) {
+ decon_err("failed to ion_client_create\n");
+ ret = PTR_ERR(decon->ion_client);
+ goto err_ion;
+ }
+#endif
+
+ return 0;
+#if defined(CONFIG_SUPPORT_LEGACY_ION)
+err_ion:
+ iounmap(decon->res.ss_regs);
+#endif
+err:
+ return ret;
+}
+
+static void decon_destroy_update_thread(struct decon_device *decon)
+{
+ if (decon->up.thread)
+ kthread_stop(decon->up.thread);
+}
+
+static int decon_create_update_thread(struct decon_device *decon, char *name)
+{
+ struct sched_param param;
+
+ INIT_LIST_HEAD(&decon->up.list);
+ INIT_LIST_HEAD(&decon->up.saved_list);
+ decon->up_list_saved = false;
+ kthread_init_worker(&decon->up.worker);
+ decon->up.thread = kthread_run(kthread_worker_fn,
+ &decon->up.worker, name);
+ if (IS_ERR(decon->up.thread)) {
+ decon->up.thread = NULL;
+ decon_err("failed to run update_regs thread\n");
+ return PTR_ERR(decon->up.thread);
+ }
+ param.sched_priority = 20;
+ sched_setscheduler_nocheck(decon->up.thread, SCHED_FIFO, ¶m);
+ kthread_init_work(&decon->up.work, decon_update_regs_handler);
+
+ return 0;
+}
+
+#if defined(CONFIG_EXYNOS_ITMON)
+static int decon_itmon_notifier(struct notifier_block *nb,
+ unsigned long action, void *nb_data)
+{
+ struct decon_device *decon;
+ struct itmon_notifier *itmon_data = nb_data;
+ struct dsim_device *dsim = NULL;
+ bool active;
+
+ decon = container_of(nb, struct decon_device, itmon_nb);
+
+ decon_info("%s: DECON%d +\n", __func__, decon->id);
+
+ if (decon->notified)
+ return NOTIFY_DONE;
+
+ if (IS_ERR_OR_NULL(itmon_data))
+ return NOTIFY_DONE;
+
+ /* port is master and dest is target */
+ if ((itmon_data->port && (strncmp("DPU", itmon_data->port,
+ sizeof("DPU") - 1) == 0)) ||
+ (itmon_data->dest && (strncmp("DPU", itmon_data->dest,
+ sizeof("DPU") - 1) == 0))) {
+ decon_info("%s: port: %s, dest: %s, action: %lu\n", __func__,
+ itmon_data->port, itmon_data->dest, action);
+ if (decon->dt.out_type == DECON_OUT_DSI) {
+ dsim = v4l2_get_subdevdata(decon->out_sd[0]);
+ if (IS_ERR_OR_NULL(dsim))
+ return NOTIFY_OK;
+ active = pm_runtime_active(dsim->dev);
+ decon_info("DPU power %s state\n", active ? "on" : "off");
+ }
+
+ decon_dump(decon);
+ decon->notified = true;
+ return NOTIFY_OK;
+ }
+
+ decon_info("%s -\n", __func__);
+
+ return NOTIFY_DONE;
+}
+#endif
+
+static int decon_initial_display(struct decon_device *decon, bool is_colormap)
+{
+ struct decon_param p;
+ struct fb_info *fbinfo = decon->win[decon->dt.dft_win]->fbinfo;
+ struct decon_window_regs win_regs;
+ struct decon_win_config config;
+ struct v4l2_subdev *sd = NULL;
+ struct decon_mode_info psr;
+ struct dsim_device *dsim;
+ struct dsim_device *dsim1;
+
+ if (decon->id || (decon->dt.out_type != DECON_OUT_DSI) ||
+ IS_ENABLED(CONFIG_EXYNOS_VIRTUAL_DISPLAY)) {
+ decon->state = DECON_STATE_OFF;
+ decon_info("decon%d doesn't need to display\n", decon->id);
+ return 0;
+ }
+
+ pm_stay_awake(decon->dev);
+ dev_warn(decon->dev, "pm_stay_awake");
+
+ if (decon->dt.psr_mode != DECON_VIDEO_MODE) {
+ if (decon->res.pinctrl && decon->res.hw_te_on) {
+ if (pinctrl_select_state(decon->res.pinctrl,
+ decon->res.hw_te_on)) {
+ decon_err("failed to turn on Decon_TE\n");
+ return -EINVAL;
+ }
+ }
+ }
+
+ decon_to_init_param(decon, &p);
+ if (decon_reg_init(decon->id, decon->dt.out_idx[0], &p) < 0)
+ goto decon_init_done;
+
+ memset(&win_regs, 0, sizeof(struct decon_window_regs));
+ win_regs.wincon = wincon(0x8, 0xFF, 0xFF, 0xFF, DECON_BLENDING_NONE,
+ decon->dt.dft_win);
+ win_regs.start_pos = win_start_pos(0, 0);
+ win_regs.end_pos = win_end_pos(0, 0, fbinfo->var.xres, fbinfo->var.yres);
+ decon_dbg("xres %d yres %d win_start_pos %x win_end_pos %x\n",
+ fbinfo->var.xres, fbinfo->var.yres, win_regs.start_pos,
+ win_regs.end_pos);
+ win_regs.colormap = 0x00ff00;
+ win_regs.pixel_count = fbinfo->var.xres * fbinfo->var.yres;
+ win_regs.whole_w = fbinfo->var.xres_virtual;
+ win_regs.whole_h = fbinfo->var.yres_virtual;
+ win_regs.offset_x = fbinfo->var.xoffset;
+ win_regs.offset_y = fbinfo->var.yoffset;
+ win_regs.type = DPU_CH2DMA(decon->dt.dft_idma);
+ decon_dbg("pixel_count(%d), whole_w(%d), whole_h(%d), x(%d), y(%d)\n",
+ win_regs.pixel_count, win_regs.whole_w,
+ win_regs.whole_h, win_regs.offset_x,
+ win_regs.offset_y);
+
+ set_bit(decon->dt.dft_idma, &decon->cur_using_dpp);
+ set_bit(decon->dt.dft_idma, &decon->prev_used_dpp);
+ memset(&config, 0, sizeof(struct decon_win_config));
+ config.dpp_parm.addr[0] = fbinfo->fix.smem_start;
+ config.format = DECON_PIXEL_FORMAT_BGRA_8888;
+ config.src.w = fbinfo->var.xres;
+ config.src.h = fbinfo->var.yres;
+ config.src.f_w = fbinfo->var.xres;
+ config.src.f_h = fbinfo->var.yres;
+ config.dst.w = config.src.w;
+ config.dst.h = config.src.h;
+ config.dst.f_w = config.src.f_w;
+ config.dst.f_h = config.src.f_h;
+ sd = decon->dpp_sd[decon->dt.dft_idma];
+ if (v4l2_subdev_call(sd, core, ioctl, DPP_WIN_CONFIG, &config)) {
+ decon_err("Failed to config DPP-%d\n",
+ decon->dt.dft_idma);
+ clear_bit(decon->dt.dft_idma, &decon->cur_using_dpp);
+ set_bit(decon->dt.dft_idma, &decon->dpp_err_stat);
+ }
+
+ decon_reg_set_window_control(decon->id, decon->dt.dft_win,
+ &win_regs, is_colormap);
+
+ decon_to_psr_info(decon, &psr);
+
+ /* TODO:
+ * 1. If below code is called after turning on 1st LCD.
+ * 2nd LCD is not turned on
+ * 2. It needs small delay between decon start and LCD on
+ * for avoiding garbage display when dual dsi mode is used. */
+ if (decon->dt.dsi_mode == DSI_MODE_DUAL_DSI) {
+ decon_info("2nd LCD is on\n");
+ msleep(1);
+ dsim1 = container_of(decon->out_sd[1], struct dsim_device, sd);
+ call_panel_ops(dsim1, displayon, dsim1);
+ }
+
+ dsim = container_of(decon->out_sd[0], struct dsim_device, sd);
+ decon_reg_start(decon->id, &psr);
+ decon_reg_set_int(decon->id, &psr, 1);
+ call_panel_ops(dsim, displayon, dsim);
+ decon_wait_for_vsync(decon, VSYNC_TIMEOUT_MSEC);
+ if (decon_reg_wait_update_done_and_mask(decon->id, &psr,
+ SHADOW_UPDATE_TIMEOUT) < 0)
+ decon_err("%s: wait_for_update_timeout\n", __func__);
+
+decon_init_done:
+
+ decon->state = DECON_STATE_INIT;
+
+ return 0;
+}
+
+/* --------- DRIVER INITIALIZATION ---------- */
+static int decon_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct decon_device *decon;
+ int ret = 0;
+ char device_name[MAX_NAME_SIZE];
+
+ dev_info(dev, "%s start\n", __func__);
+
+ decon = devm_kzalloc(dev, sizeof(struct decon_device), GFP_KERNEL);
+ if (!decon) {
+ decon_err("no memory for decon device\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+#if !defined(CONFIG_SUPPORT_LEGACY_ION)
+ dma_set_mask(dev, DMA_BIT_MASK(36));
+#endif
+
+ decon->dev = dev;
+ decon_parse_dt(decon);
+
+ decon_drvdata[decon->id] = decon;
+
+ spin_lock_init(&decon->slock);
+ init_waitqueue_head(&decon->vsync.wait);
+ init_waitqueue_head(&decon->wait_vstatus);
+ mutex_init(&decon->vsync.lock);
+ mutex_init(&decon->lock);
+ mutex_init(&decon->pm_lock);
+ mutex_init(&decon->up.lock);
+ mutex_init(&decon->cursor.lock);
+
+ decon_enter_shutdown_reset(decon);
+
+ snprintf(device_name, MAX_NAME_SIZE, "decon%d", decon->id);
+ decon_create_timeline(decon, device_name);
+
+ /* systrace */
+ decon_systrace_enable = 0;
+ decon->systrace.pid = 0;
+
+ ret = decon_init_resources(decon, pdev, device_name);
+ if (ret)
+ goto err_res;
+
+ ret = decon_create_vsync_thread(decon);
+ if (ret)
+ goto err_vsync;
+
+#if defined(CONFIG_EXYNOS_DISPLAYPORT)
+ ret = decon_displayport_create_vsync_thread(decon);
+ if (ret)
+ goto err_vsync;
+#endif
+
+ ret = decon_create_psr_info(decon);
+ if (ret)
+ goto err_psr;
+
+ ret = decon_get_pinctrl(decon);
+ if (ret)
+ goto err_pinctrl;
+
+ ret = decon_create_debugfs(decon);
+ if (ret)
+ goto err_pinctrl;
+
+ ret = decon_register_hiber_work(decon);
+ if (ret)
+ goto err_pinctrl;
+
+ ret = decon_register_subdevs(decon);
+ if (ret)
+ goto err_subdev;
+
+ ret = decon_acquire_windows(decon);
+ if (ret)
+ goto err_win;
+
+ ret = decon_create_update_thread(decon, device_name);
+ if (ret)
+ goto err_win;
+
+ dpu_init_win_update(decon);
+ decon_init_low_persistence_mode(decon);
+ dpu_init_cursor_mode(decon);
+
+#if defined(CONFIG_EXYNOS9610_BTS)
+ decon->bts.ops = &decon_bts_control;
+ decon->bts.ops->bts_init(decon);
+#endif
+
+ platform_set_drvdata(pdev, decon);
+ pm_runtime_enable(dev);
+
+ /* prevent sleep enter during display(LCD, DP) on */
+ ret = device_init_wakeup(decon->dev, true);
+ if (ret) {
+ dev_err(decon->dev, "failed to init wakeup device\n");
+ goto err_display;
+ }
+
+#if defined(CONFIG_EXYNOS_ITMON)
+ decon->itmon_nb.notifier_call = decon_itmon_notifier;
+ itmon_notifier_chain_register(&decon->itmon_nb);
+#endif
+
+ ret = decon_initial_display(decon, false);
+ if (ret)
+ goto err_display;
+
+ decon_info("decon%d registered successfully", decon->id);
+
+ return 0;
+
+err_display:
+ decon_destroy_update_thread(decon);
+err_win:
+ decon_unregister_subdevs(decon);
+err_subdev:
+ decon_destroy_debugfs(decon);
+err_pinctrl:
+ decon_destroy_psr_info(decon);
+err_psr:
+ decon_destroy_vsync_thread(decon);
+err_vsync:
+ iounmap(decon->res.ss_regs);
+err_res:
+ kfree(decon);
+err:
+ decon_err("decon probe fail");
+ return ret;
+}
+
+static int decon_remove(struct platform_device *pdev)
+{
+ struct decon_device *decon = platform_get_drvdata(pdev);
+ int i;
+
+ decon->bts.ops->bts_deinit(decon);
+
+ pm_runtime_disable(&pdev->dev);
+ decon_put_clocks(decon);
+ unregister_framebuffer(decon->win[0]->fbinfo);
+
+ if (decon->up.thread)
+ kthread_stop(decon->up.thread);
+
+ for (i = 0; i < decon->dt.max_win; i++)
+ decon_release_windows(decon->win[i]);
+
+ debugfs_remove_recursive(decon->d.debug_root);
+ kfree(decon->d.event_log);
+
+ decon_info("remove sucessful\n");
+ return 0;
+}
+
+static void decon_shutdown(struct platform_device *pdev)
+{
+ struct decon_device *decon = platform_get_drvdata(pdev);
+ struct fb_info *fbinfo = decon->win[decon->dt.dft_win]->fbinfo;
+
+ decon_enter_shutdown(decon);
+
+ if (!lock_fb_info(fbinfo)) {
+ decon_warn("%s: fblock is failed\n", __func__);
+ return;
+ }
+
+ decon_info("%s + state:%d\n", __func__, decon->state);
+ DPU_EVENT_LOG(DPU_EVT_DECON_SHUTDOWN, &decon->sd, ktime_set(0, 0));
+
+ decon_hiber_block_exit(decon);
+ /* Unused DECON state is DECON_STATE_INIT */
+ if (IS_DECON_ON_STATE(decon))
+ decon_disable(decon);
+
+ unlock_fb_info(fbinfo);
+
+ decon_info("%s -\n", __func__);
+ return;
+}
+
+static const struct of_device_id decon_of_match[] = {
+ { .compatible = "samsung,exynos9-decon" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, decon_of_match);
+
+static struct platform_driver decon_driver __refdata = {
+ .probe = decon_probe,
+ .remove = decon_remove,
+ .shutdown = decon_shutdown,
+ .driver = {
+ .name = DECON_MODULE_NAME,
+ .owner = THIS_MODULE,
+ .pm = &decon_pm_ops,
+ .of_match_table = of_match_ptr(decon_of_match),
+ .suppress_bind_attrs = true,
+ }
+};
+
+static int exynos_decon_register(void)
+{
+ platform_driver_register(&decon_driver);
+
+ return 0;
+}
+
+static void exynos_decon_unregister(void)
+{
+ platform_driver_unregister(&decon_driver);
+}
+late_initcall(exynos_decon_register);
+module_exit(exynos_decon_unregister);
+
+MODULE_AUTHOR("Jaehoe Yang <jaehoe.yang@samsung.com>");
+MODULE_AUTHOR("Yeongran Shin <yr613.shin@samsung.com>");
+MODULE_AUTHOR("Minho Kim <m8891.kim@samsung.com>");
+MODULE_DESCRIPTION("Samsung EXYNOS DECON driver");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Interface file betwen DECON and DISPLAYPORT 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 <linux/clk-provider.h>
+#include <linux/videodev2_exynos_media.h>
+#include <media/v4l2-subdev.h>
+#if defined(CONFIG_CAL_IF)
+#include <soc/samsung/cal-if.h>
+#endif
+#include <dt-bindings/clock/exynos9810.h>
+#include "decon.h"
+#include "displayport.h"
+
+static irqreturn_t decon_displayport_irq_handler(int irq, void *dev_data)
+{
+ struct decon_device *decon = dev_data;
+ u32 irq_sts_reg;
+ u32 ext_irq = 0;
+
+ spin_lock(&decon->slock);
+ if (decon->state == DECON_STATE_OFF)
+ goto irq_end;
+
+ irq_sts_reg = decon_reg_get_interrupt_and_clear(decon->id, &ext_irq);
+
+ if (irq_sts_reg & DPU_FRAME_DONE_INT_PEND)
+ DPU_EVENT_LOG(DPU_EVT_DECON_FRAMEDONE, &decon->sd, ktime_set(0, 0));
+
+ if (ext_irq & DPU_TIME_OUT_INT_PEND)
+ decon_err("%s: DECON%d timeout irq occurs\n", __func__, decon->id);
+
+irq_end:
+ spin_unlock(&decon->slock);
+ return IRQ_HANDLED;
+}
+
+int decon_displayport_register_irq(struct decon_device *decon)
+{
+ struct device *dev = decon->dev;
+ struct platform_device *pdev;
+ struct resource *res;
+ int ret = 0;
+
+ pdev = container_of(dev, struct platform_device, dev);
+
+ /* 1: FRAME START */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ ret = devm_request_irq(dev, res->start, decon_displayport_irq_handler,
+ 0, pdev->name, decon);
+ if (ret) {
+ decon_err("failed to install FRAME START irq\n");
+ return ret;
+ }
+
+ /* 2: FRAME DONE */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ ret = devm_request_irq(dev, res->start, decon_displayport_irq_handler,
+ 0, pdev->name, decon);
+ if (ret) {
+ decon_err("failed to install FRAME DONE irq\n");
+ return ret;
+ }
+
+ /* 3: EXTRA: resource conflict, timeout and error irq */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 2);
+ ret = devm_request_irq(dev, res->start, decon_displayport_irq_handler,
+ 0, pdev->name, decon);
+ if (ret) {
+ decon_err("failed to install EXTRA irq\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+void decon_displayport_free_irq(struct decon_device *decon)
+{
+ struct device *dev = decon->dev;
+ struct platform_device *pdev;
+ struct resource *res;
+
+ pdev = container_of(dev, struct platform_device, dev);
+
+ /* 1: FRAME START */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ devm_free_irq(dev, res->start, decon);
+
+ /* 2: FRAME DONE */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ devm_free_irq(dev, res->start, decon);
+
+ /* 3: EXTRA: resource conflict, timeout and error irq */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 2);
+ devm_free_irq(dev, res->start, decon);
+}
+
+int decon_displayport_get_clocks(struct decon_device *decon)
+{
+ return 0;
+}
+
+static ssize_t decon_displayport_show_vsync(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct decon_device *decon = dev_get_drvdata(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%llu\n",
+ ktime_to_ns(decon->vsync.timestamp));
+}
+static DEVICE_ATTR(vsync, S_IRUGO, decon_displayport_show_vsync, NULL);
+
+static int decon_displayport_vsync_thread(void *data)
+{
+ struct decon_device *decon = data;
+
+ while (!kthread_should_stop()) {
+ ktime_t timestamp = decon->vsync.timestamp;
+#if defined(CONFIG_SUPPORT_KERNEL_4_9)
+ int ret = wait_event_interruptible(decon->vsync.wait,
+ !ktime_equal(timestamp, decon->vsync.timestamp) &&
+ decon->vsync.active);
+#else
+ int ret = wait_event_interruptible(decon->vsync.wait,
+ (timestamp != decon->vsync.timestamp) &&
+ decon->vsync.active);
+#endif
+ if (!ret)
+ sysfs_notify(&decon->dev->kobj, NULL, "vsync");
+ }
+
+ return 0;
+}
+
+int decon_displayport_create_vsync_thread(struct decon_device *decon)
+{
+ int ret = 0;
+ char name[16];
+
+ if (decon->id != 2) {
+ decon_info("decon_displayport_create_vsync_thread is needed for displayport path\n");
+ return 0;
+ }
+
+ ret = device_create_file(decon->dev, &dev_attr_vsync);
+ if (ret) {
+ decon_err("decon[%d] failed to create vsync file\n", decon->id);
+ return ret;
+ }
+
+ sprintf(name, "decon%d-vsync", decon->id);
+
+ decon->vsync.thread = kthread_run(decon_displayport_vsync_thread, decon, name);
+ if (IS_ERR_OR_NULL(decon->vsync.thread)) {
+ decon_err("failed to run vsync thread\n");
+ decon->vsync.thread = NULL;
+ ret = PTR_ERR(decon->vsync.thread);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ device_remove_file(decon->dev, &dev_attr_vsync);
+ return ret;
+}
+
+static int decon_displayport_set_lcd_info(struct decon_device *decon)
+{
+#if defined(CONFIG_EXYNOS_DISPLAYPORT)
+ struct decon_lcd *lcd_info;
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ if (decon->lcd_info == NULL) {
+ lcd_info = kzalloc(sizeof(struct decon_lcd), GFP_KERNEL);
+ if (!lcd_info) {
+ decon_err("could not allocate decon_lcd for displayport\n");
+ return -ENOMEM;
+ }
+
+ decon->lcd_info = lcd_info;
+ }
+
+ decon->lcd_info->width = supported_videos[displayport->cur_video].dv_timings.bt.width;
+ decon->lcd_info->height = supported_videos[displayport->cur_video].dv_timings.bt.height;
+ decon->lcd_info->xres = supported_videos[displayport->cur_video].dv_timings.bt.width;
+ decon->lcd_info->yres = supported_videos[displayport->cur_video].dv_timings.bt.height;
+ decon->lcd_info->vfp = supported_videos[displayport->cur_video].dv_timings.bt.vfrontporch;
+ decon->lcd_info->vbp = supported_videos[displayport->cur_video].dv_timings.bt.vbackporch;
+ decon->lcd_info->hfp = supported_videos[displayport->cur_video].dv_timings.bt.hfrontporch;
+ decon->lcd_info->hbp = supported_videos[displayport->cur_video].dv_timings.bt.hbackporch;
+ decon->lcd_info->vsa = supported_videos[displayport->cur_video].dv_timings.bt.vsync;
+ decon->lcd_info->hsa = supported_videos[displayport->cur_video].dv_timings.bt.hsync;
+ decon->lcd_info->fps = supported_videos[displayport->cur_video].fps;
+ decon->dt.psr_mode = DECON_VIDEO_MODE;
+ decon->dt.trig_mode = DECON_HW_TRIG;
+ decon->dt.out_type = DECON_OUT_DP;
+
+ if (displayport->bpc == BPC_10)
+ decon->lcd_info->bpc = 10; /* 10pbc */
+ else
+ decon->lcd_info->bpc = 8; /* 8pbc */
+
+ decon_info("decon_%d output size for displayport %dx%d\n", decon->id,
+ decon->lcd_info->width, decon->lcd_info->height);
+#else
+ decon_info("Not compiled displayport driver\n");
+#endif
+ return 0;
+}
+
+int decon_displayport_get_out_sd(struct decon_device *decon)
+{
+ decon->out_sd[0] = decon->displayport_sd;
+ if (IS_ERR_OR_NULL(decon->out_sd[0])) {
+ decon_err("failed to get displayport sd\n");
+ return -ENOMEM;
+ }
+ decon->out_sd[1] = NULL;
+
+ decon_displayport_set_lcd_info(decon);
+
+ return 0;
+}
+
+int decon_displayport_get_hdr_capa(struct decon_device *decon,
+ struct decon_hdr_capabilities *hdr_capa)
+{
+#if defined(CONFIG_EXYNOS_DISPLAYPORT)
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ if (displayport->rx_edid_data.hdr_support)
+ hdr_capa->out_types[0] = HDR_HDR10;
+#else
+ decon_info("Not compiled displayport driver\n");
+#endif
+ return 0;
+}
+
+int decon_displayport_get_hdr_capa_info(struct decon_device *decon,
+ struct decon_hdr_capabilities_info *hdr_capa_info)
+{
+#if defined(CONFIG_EXYNOS_DISPLAYPORT)
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ if (displayport->rx_edid_data.hdr_support)
+ hdr_capa_info->out_num = 1;
+ else
+ hdr_capa_info->out_num = 0;
+
+ /* Need CEA-861.3 EDID value calculation on platform part */
+ hdr_capa_info->max_luminance =
+ displayport->rx_edid_data.max_lumi_data;
+ hdr_capa_info->max_average_luminance =
+ displayport->rx_edid_data.max_average_lumi_data;
+ hdr_capa_info->min_luminance =
+ displayport->rx_edid_data.min_lumi_data;
+#else
+ decon_info("Not compiled displayport driver\n");
+#endif
+ return 0;
+}
+
+int decon_displayport_get_config(struct decon_device *decon,
+ struct exynos_displayport_data *displayport_data)
+{
+ struct v4l2_subdev *displayport_sd;
+ struct v4l2_control ctrl;
+ int ret = 0;
+
+ decon_dbg("state : %d\n", displayport_data->state);
+
+ ctrl.id = 0;
+ ctrl.value = 0;
+
+ displayport_sd = decon->displayport_sd;
+ if (displayport_sd == NULL)
+ return -EINVAL;
+
+ mutex_lock(&decon->lock);
+
+ switch (displayport_data->state) {
+ case EXYNOS_DISPLAYPORT_STATE_PRESET:
+ ret = v4l2_subdev_call(displayport_sd, video, g_dv_timings, &displayport_data->timings);
+
+ decon_dbg("%displayport%d@%s %lldHz %s(%#x)\n",
+ displayport_data->timings.bt.width,
+ displayport_data->timings.bt.height,
+ displayport_data->timings.bt.interlaced ? "I" : "P",
+ displayport_data->timings.bt.pixelclock,
+ displayport_data->timings.type ? "S3D" : "2D",
+ displayport_data->timings.type);
+
+ decon_dbg("EXYNOS_DISPLAYPORT_STATE_PRESET\n");
+ break;
+
+ case EXYNOS_DISPLAYPORT_STATE_ENUM_PRESET:
+ ret = v4l2_subdev_call(displayport_sd, core, ioctl,
+ DISPLAYPORT_IOC_GET_ENUM_DV_TIMINGS, &displayport_data->etimings);
+
+ decon_dbg("EXYNOS_DISPLAYPORT_STATE_ENUM_PRESET\n");
+ break;
+
+ default:
+ decon_warn("unrecongnized state %u\n", displayport_data->state);
+ ret = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&decon->lock);
+ return ret;
+}
+
+int decon_displayport_set_config(struct decon_device *decon,
+ struct exynos_displayport_data *displayport_data)
+{
+ struct v4l2_subdev *displayport_sd;
+ int ret = 0;
+
+ decon_dbg("state : %d\n", displayport_data->state);
+
+ displayport_sd = decon->displayport_sd;
+ if (displayport_sd == NULL)
+ return -EINVAL;
+
+ mutex_lock(&decon->lock);
+
+ switch (displayport_data->state) {
+ case EXYNOS_DISPLAYPORT_STATE_PRESET:
+ ret = v4l2_subdev_call(displayport_sd, video, s_dv_timings, &displayport_data->timings);
+
+ if (ret)
+ decon_err("failed to set timings newly\n");
+
+ decon_dbg("%displayport%d@%s %lldHz %s(%#x)\n",
+ displayport_data->timings.bt.width,
+ displayport_data->timings.bt.height,
+ displayport_data->timings.bt.interlaced ? "I" : "P",
+ displayport_data->timings.bt.pixelclock,
+ displayport_data->timings.type ? "S3D" : "2D",
+ displayport_data->timings.type);
+
+ decon_dbg("EXYNOS_DISPLAYPORT_STATE_PRESET\n");
+ break;
+
+ case EXYNOS_DISPLAYPORT_STATE_RECONNECTION:
+ ret = v4l2_subdev_call(displayport_sd, core, ioctl,
+ DISPLAYPORT_IOC_SET_RECONNECTION, &displayport_data->etimings);
+
+ decon_dbg("EXYNOS_DISPLAYPORT_STATE_RECONNECTION\n");
+ break;
+
+ default:
+ decon_warn("unrecongnized state %u\n", displayport_data->state);
+ ret = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&decon->lock);
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Interface file between DECON and DSIM 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 <linux/of_gpio.h>
+#include <linux/of.h>
+#include <linux/clk-provider.h>
+#include <linux/pm_runtime.h>
+#include <linux/exynos_iovmm.h>
+#if defined(CONFIG_SUPPORT_KERNEL_4_9)
+#include <linux/sched.h>
+#else
+#include <linux/sched/types.h>
+#endif
+#include <linux/of_address.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/irq.h>
+#include <media/v4l2-subdev.h>
+#if defined(CONFIG_EXYNOS_WD_DVFS)
+#include <linux/exynos-wd.h>
+#endif
+
+#include "decon.h"
+#include "dsim.h"
+#include "dpp.h"
+//#include "../../../../soc/samsung/pwrcal/pwrcal.h"
+//#include "../../../../soc/samsung/pwrcal/S5E8890/S5E8890-vclk.h"
+#include "../../../../../kernel/irq/internals.h"
+#ifdef CONFIG_EXYNOS_WD_DVFS
+struct task_struct *devfreq_change_task;
+#endif
+
+/* DECON irq handler for DSI interface */
+static irqreturn_t decon_irq_handler(int irq, void *dev_data)
+{
+ struct decon_device *decon = dev_data;
+ u32 irq_sts_reg;
+ u32 ext_irq = 0;
+
+ spin_lock(&decon->slock);
+ if (IS_DECON_OFF_STATE(decon))
+ goto irq_end;
+
+ irq_sts_reg = decon_reg_get_interrupt_and_clear(decon->id, &ext_irq);
+ decon_dbg("%s: irq_sts_reg = %x, ext_irq = %x\n", __func__,
+ irq_sts_reg, ext_irq);
+
+ if (irq_sts_reg & DPU_FRAME_START_INT_PEND) {
+ /* VSYNC interrupt, accept it */
+ decon->frame_cnt++;
+ wake_up_interruptible_all(&decon->wait_vstatus);
+ if (decon->state == DECON_STATE_TUI)
+ decon_info("%s:%d TUI Frame Start\n", __func__, __LINE__);
+ }
+
+ if (irq_sts_reg & DPU_FRAME_DONE_INT_PEND) {
+ DPU_EVENT_LOG(DPU_EVT_DECON_FRAMEDONE, &decon->sd, ktime_set(0, 0));
+ decon_hiber_trig_reset(decon);
+ if (decon->state == DECON_STATE_TUI)
+ decon_info("%s:%d TUI Frame Done\n", __func__, __LINE__);
+ }
+
+ if (ext_irq & DPU_RESOURCE_CONFLICT_INT_PEND)
+ DPU_EVENT_LOG(DPU_EVT_RSC_CONFLICT, &decon->sd, ktime_set(0, 0));
+
+ if (ext_irq & DPU_TIME_OUT_INT_PEND) {
+ decon_err("%s: DECON%d timeout irq occurs\n", __func__, decon->id);
+#if defined(CONFIG_EXYNOS_AFBC_DEBUG)
+ dpu_dump_afbc_info();
+ BUG();
+#endif
+ }
+
+irq_end:
+ spin_unlock(&decon->slock);
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_EXYNOS_WD_DVFS
+static int decon_devfreq_change_task(void *data)
+{
+ while (!kthread_should_stop()) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ schedule();
+
+ set_current_state(TASK_RUNNING);
+
+ exynos_wd_call_chain();
+ }
+
+ return 0;
+}
+#endif
+
+int decon_register_irq(struct decon_device *decon)
+{
+ struct device *dev = decon->dev;
+ struct platform_device *pdev;
+ struct resource *res;
+ int ret = 0;
+
+ pdev = container_of(dev, struct platform_device, dev);
+
+ /* 1: FRAME START */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ ret = devm_request_irq(dev, res->start, decon_irq_handler,
+ 0, pdev->name, decon);
+ if (ret) {
+ decon_err("failed to install FRAME START irq\n");
+ return ret;
+ }
+
+ /* 2: FRAME DONE */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ ret = devm_request_irq(dev, res->start, decon_irq_handler,
+ 0, pdev->name, decon);
+ if (ret) {
+ decon_err("failed to install FRAME DONE irq\n");
+ return ret;
+ }
+
+ /* 3: EXTRA: resource conflict, timeout and error irq */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 2);
+ ret = devm_request_irq(dev, res->start, decon_irq_handler,
+ 0, pdev->name, decon);
+ if (ret) {
+ decon_err("failed to install EXTRA irq\n");
+ return ret;
+ }
+
+ /*
+ * If below IRQs are needed, please define irq number sequence
+ * like below
+ *
+ * DECON0
+ * 4: DIMMING_START
+ * 5: DIMMING_END
+ * 6: DQE_DIMMING_START
+ * 7: DQE_DIMMING_END
+ *
+ * DECON2
+ * 4: VSTATUS
+ */
+
+ return ret;
+}
+
+int decon_get_clocks(struct decon_device *decon)
+{
+ return 0;
+}
+
+void decon_set_clocks(struct decon_device *decon)
+{
+}
+
+int decon_get_out_sd(struct decon_device *decon)
+{
+ decon->out_sd[0] = decon->dsim_sd[decon->dt.out_idx[0]];
+ if (IS_ERR_OR_NULL(decon->out_sd[0])) {
+ decon_err("failed to get dsim%d sd\n", decon->dt.out_idx[0]);
+ return -ENOMEM;
+ }
+
+ if (decon->dt.dsi_mode == DSI_MODE_DUAL_DSI) {
+ decon->out_sd[1] = decon->dsim_sd[decon->dt.out_idx[1]];
+ if (IS_ERR_OR_NULL(decon->out_sd[1])) {
+ decon_err("failed to get 2nd dsim%d sd\n",
+ decon->dt.out_idx[1]);
+ return -ENOMEM;
+ }
+ }
+
+ v4l2_subdev_call(decon->out_sd[0], core, ioctl, DSIM_IOC_GET_LCD_INFO, NULL);
+ decon->lcd_info =
+ (struct decon_lcd *)v4l2_get_subdev_hostdata(decon->out_sd[0]);
+ if (IS_ERR_OR_NULL(decon->lcd_info)) {
+ decon_err("failed to get lcd information\n");
+ return -EINVAL;
+ }
+
+ decon_info("lcd_info: hfp %d hbp %d hsa %d vfp %d vbp %d vsa %d",
+ decon->lcd_info->hfp, decon->lcd_info->hbp,
+ decon->lcd_info->hsa, decon->lcd_info->vfp,
+ decon->lcd_info->vbp, decon->lcd_info->vsa);
+ decon_info("xres %d yres %d\n",
+ decon->lcd_info->xres, decon->lcd_info->yres);
+
+ return 0;
+}
+
+int decon_get_pinctrl(struct decon_device *decon)
+{
+ int ret = 0;
+
+ if ((decon->dt.out_type != DECON_OUT_DSI) ||
+ (decon->dt.psr_mode == DECON_VIDEO_MODE) ||
+ (decon->dt.trig_mode != DECON_HW_TRIG)) {
+ decon_warn("decon%d doesn't need pinctrl\n", decon->id);
+ return 0;
+ }
+
+ decon->res.pinctrl = devm_pinctrl_get(decon->dev);
+ if (IS_ERR(decon->res.pinctrl)) {
+ decon_err("failed to get decon-%d pinctrl\n", decon->id);
+ ret = PTR_ERR(decon->res.pinctrl);
+ decon->res.pinctrl = NULL;
+ goto err;
+ }
+
+ decon->res.hw_te_on = pinctrl_lookup_state(decon->res.pinctrl, "hw_te_on");
+ if (IS_ERR(decon->res.hw_te_on)) {
+ decon_err("failed to get hw_te_on pin state\n");
+ ret = PTR_ERR(decon->res.hw_te_on);
+ decon->res.hw_te_on = NULL;
+ goto err;
+ }
+ decon->res.hw_te_off = pinctrl_lookup_state(decon->res.pinctrl, "hw_te_off");
+ if (IS_ERR(decon->res.hw_te_off)) {
+ decon_err("failed to get hw_te_off pin state\n");
+ ret = PTR_ERR(decon->res.hw_te_off);
+ decon->res.hw_te_off = NULL;
+ goto err;
+ }
+
+err:
+ return ret;
+}
+
+static irqreturn_t decon_ext_irq_handler(int irq, void *dev_id)
+{
+ struct decon_device *decon = dev_id;
+ struct decon_mode_info psr;
+ ktime_t timestamp = ktime_get();
+
+ decon_systrace(decon, 'C', "decon_te_signal", 1);
+ DPU_EVENT_LOG(DPU_EVT_TE_INTERRUPT, &decon->sd, timestamp);
+
+ spin_lock(&decon->slock);
+
+ if (decon->dt.trig_mode == DECON_SW_TRIG) {
+ decon_to_psr_info(decon, &psr);
+ decon_reg_set_trigger(decon->id, &psr, DECON_TRIG_ENABLE);
+ }
+
+ if (decon->hiber.enabled && decon->state == DECON_STATE_ON &&
+ decon->dt.out_type == DECON_OUT_DSI) {
+ if (decon_min_lock_cond(decon))
+ kthread_queue_work(&decon->hiber.worker, &decon->hiber.work);
+ }
+
+ decon_systrace(decon, 'C', "decon_te_signal", 0);
+ decon->vsync.timestamp = timestamp;
+ wake_up_interruptible_all(&decon->vsync.wait);
+
+ spin_unlock(&decon->slock);
+#ifdef CONFIG_EXYNOS_WD_DVFS
+ if (devfreq_change_task)
+ wake_up_process(devfreq_change_task);
+#endif
+
+ return IRQ_HANDLED;
+}
+
+int decon_register_ext_irq(struct decon_device *decon)
+{
+ struct device *dev = decon->dev;
+ struct platform_device *pdev;
+ int gpio = -EINVAL, gpio1 = -EINVAL;
+ int ret = 0;
+
+ pdev = container_of(dev, struct platform_device, dev);
+
+ /* Get IRQ resource and register IRQ handler. */
+ if (of_get_property(dev->of_node, "gpios", NULL) != NULL) {
+ gpio = of_get_gpio(dev->of_node, 0);
+ if (gpio < 0) {
+ decon_err("failed to get proper gpio number\n");
+ return -EINVAL;
+ }
+
+ gpio1 = of_get_gpio(dev->of_node, 1);
+ if (gpio1 < 0)
+ decon_info("This board doesn't support TE GPIO of 2nd LCD\n");
+ } else {
+ decon_err("failed to find gpio node from device tree\n");
+ return -EINVAL;
+ }
+
+ decon->res.irq = gpio_to_irq(gpio);
+
+ decon_info("%s: gpio(%d)\n", __func__, decon->res.irq);
+ ret = devm_request_irq(dev, decon->res.irq, decon_ext_irq_handler,
+ IRQF_TRIGGER_RISING, pdev->name, decon);
+
+ decon->eint_status = 1;
+
+#ifdef CONFIG_EXYNOS_WD_DVFS
+ devfreq_change_task =
+ kthread_create(decon_devfreq_change_task, NULL,
+ "devfreq_change");
+ if (IS_ERR(devfreq_change_task))
+ return PTR_ERR(devfreq_change_task);
+#endif
+
+ return ret;
+}
+
+static ssize_t decon_show_vsync(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct decon_device *decon = dev_get_drvdata(dev);
+ return scnprintf(buf, PAGE_SIZE, "%llu\n",
+ ktime_to_ns(decon->vsync.timestamp));
+}
+static DEVICE_ATTR(vsync, S_IRUGO, decon_show_vsync, NULL);
+
+static int decon_vsync_thread(void *data)
+{
+ struct decon_device *decon = data;
+
+ while (!kthread_should_stop()) {
+ ktime_t timestamp = decon->vsync.timestamp;
+#if defined(CONFIG_SUPPORT_KERNEL_4_9)
+ int ret = wait_event_interruptible(decon->vsync.wait,
+ !ktime_equal(timestamp, decon->vsync.timestamp) &&
+ decon->vsync.active);
+#else
+ int ret = wait_event_interruptible(decon->vsync.wait,
+ (timestamp != decon->vsync.timestamp) &&
+ decon->vsync.active);
+#endif
+ if (!ret)
+ sysfs_notify(&decon->dev->kobj, NULL, "vsync");
+ }
+
+ return 0;
+}
+
+int decon_create_vsync_thread(struct decon_device *decon)
+{
+ int ret = 0;
+ char name[16];
+
+ if (decon->dt.out_type != DECON_OUT_DSI) {
+ decon_info("vsync thread is only needed for DSI path\n");
+ return 0;
+ }
+
+ ret = device_create_file(decon->dev, &dev_attr_vsync);
+ if (ret) {
+ decon_err("failed to create vsync file\n");
+ return ret;
+ }
+
+ sprintf(name, "decon%d-vsync", decon->id);
+ decon->vsync.thread = kthread_run(decon_vsync_thread, decon, name);
+ if (IS_ERR_OR_NULL(decon->vsync.thread)) {
+ decon_err("failed to run vsync thread\n");
+ decon->vsync.thread = NULL;
+ ret = PTR_ERR(decon->vsync.thread);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ device_remove_file(decon->dev, &dev_attr_vsync);
+ return ret;
+}
+
+void decon_destroy_vsync_thread(struct decon_device *decon)
+{
+ device_remove_file(decon->dev, &dev_attr_vsync);
+
+ if (decon->vsync.thread)
+ kthread_stop(decon->vsync.thread);
+}
+
+/*
+ * Variable Descriptions
+ * dsc_en : comp_mode (0=No Comp, 1=DSC, 2=MIC, 3=LEGO)
+ * dsc_width : min_window_update_width depending on compression mode
+ * dsc_height : min_window_update_height depending on compression mode
+ */
+static ssize_t decon_show_psr_info(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct decon_device *decon = dev_get_drvdata(dev);
+ struct decon_lcd *lcd_info = decon->lcd_info;
+ int i;
+ char *p = buf;
+ struct lcd_mres_info *mres_info = &lcd_info->dt_lcd_mres;
+ int len;
+
+ len = sprintf(p, "%d\n", decon->dt.psr_mode);
+ len += sprintf(p + len, "%d\n", mres_info->mres_number);
+ for (i = 0; i < mres_info->mres_number; i++) {
+ if (mres_info->res_info[i].dsc_en)
+ len += sprintf(p + len, "%d\n%d\n%d\n%d\n%d\n",
+ mres_info->res_info[i].width,
+ mres_info->res_info[i].height,
+ mres_info->res_info[i].dsc_width,
+ mres_info->res_info[i].dsc_height,
+ mres_info->res_info[i].dsc_en);
+ else
+ len += sprintf(p + len, "%d\n%d\n%d\n%d\n%d\n",
+ mres_info->res_info[i].width,
+ mres_info->res_info[i].height,
+ MIN_WIN_BLOCK_WIDTH,
+ MIN_WIN_BLOCK_HEIGHT,
+ mres_info->res_info[i].dsc_en);
+ }
+ return len;
+}
+static DEVICE_ATTR(psr_info, S_IRUGO, decon_show_psr_info, NULL);
+
+int decon_create_psr_info(struct decon_device *decon)
+{
+ int ret = 0;
+
+ /* It's enought to make a file for PSR information */
+ if (decon->id != 0)
+ return 0;
+
+ ret = device_create_file(decon->dev, &dev_attr_psr_info);
+ if (ret) {
+ decon_err("failed to create psr info file\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+void decon_destroy_psr_info(struct decon_device *decon)
+{
+ device_remove_file(decon->dev, &dev_attr_psr_info);
+}
+
+
+/* Framebuffer interface related callback functions */
+static u32 fb_visual(u32 bits_per_pixel, unsigned short palette_sz)
+{
+ switch (bits_per_pixel) {
+ case 32:
+ case 24:
+ case 16:
+ case 12:
+ return FB_VISUAL_TRUECOLOR;
+ case 8:
+ if (palette_sz >= 256)
+ return FB_VISUAL_PSEUDOCOLOR;
+ else
+ return FB_VISUAL_TRUECOLOR;
+ case 1:
+ return FB_VISUAL_MONO01;
+ default:
+ return FB_VISUAL_PSEUDOCOLOR;
+ }
+}
+
+static inline u32 fb_linelength(u32 xres_virtual, u32 bits_per_pixel)
+{
+ return (xres_virtual * bits_per_pixel) / 8;
+}
+
+static u16 fb_panstep(u32 res, u32 res_virtual)
+{
+ return res_virtual > res ? 1 : 0;
+}
+
+int decon_set_par(struct fb_info *info)
+{
+ struct fb_var_screeninfo *var = &info->var;
+ struct decon_win *win = info->par;
+ struct decon_device *decon = win->decon;
+ struct decon_window_regs win_regs;
+ int win_no = win->idx;
+
+ if ((!IS_DECON_HIBER_STATE(decon) && IS_DECON_OFF_STATE(decon)) ||
+ decon->state == DECON_STATE_INIT)
+ return 0;
+
+ memset(&win_regs, 0, sizeof(struct decon_window_regs));
+
+ decon_hiber_block_exit(decon);
+
+ decon_reg_wait_update_done_timeout(decon->id, SHADOW_UPDATE_TIMEOUT);
+ info->fix.visual = fb_visual(var->bits_per_pixel, 0);
+
+ info->fix.line_length = fb_linelength(var->xres_virtual,
+ var->bits_per_pixel);
+ info->fix.xpanstep = fb_panstep(var->xres, var->xres_virtual);
+ info->fix.ypanstep = fb_panstep(var->yres, var->yres_virtual);
+
+ win_regs.wincon |= wincon(var->transp.length, 0, 0xFF,
+ 0xFF, DECON_BLENDING_NONE, win_no);
+ win_regs.start_pos = win_start_pos(0, 0);
+ win_regs.end_pos = win_end_pos(0, 0, var->xres, var->yres);
+ win_regs.pixel_count = (var->xres * var->yres);
+ win_regs.whole_w = var->xoffset + var->xres;
+ win_regs.whole_h = var->yoffset + var->yres;
+ win_regs.offset_x = var->xoffset;
+ win_regs.offset_y = var->yoffset;
+ win_regs.type = decon->dt.dft_idma;
+ decon_reg_set_window_control(decon->id, win_no, &win_regs, false);
+
+ decon_hiber_unblock(decon);
+ return 0;
+}
+EXPORT_SYMBOL(decon_set_par);
+
+int decon_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ struct decon_win *win = info->par;
+ struct decon_device *decon = win->decon;
+
+ var->xres_virtual = max(var->xres_virtual, var->xres);
+ var->yres_virtual = max(var->yres_virtual, var->yres);
+
+ if (!decon_validate_x_alignment(decon, 0, var->xres,
+ var->bits_per_pixel))
+ return -EINVAL;
+
+ /* always ensure these are zero, for drop through cases below */
+ var->transp.offset = 0;
+ var->transp.length = 0;
+
+ switch (var->bits_per_pixel) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ var->red.offset = 4;
+ var->green.offset = 2;
+ var->blue.offset = 0;
+ var->red.length = 5;
+ var->green.length = 3;
+ var->blue.length = 2;
+ var->transp.offset = 7;
+ var->transp.length = 1;
+ break;
+
+ case 19:
+ /* 666 with one bit alpha/transparency */
+ var->transp.offset = 18;
+ var->transp.length = 1;
+ case 18:
+ var->bits_per_pixel = 32;
+
+ /* 666 format */
+ var->red.offset = 12;
+ var->green.offset = 6;
+ var->blue.offset = 0;
+ var->red.length = 6;
+ var->green.length = 6;
+ var->blue.length = 6;
+ break;
+
+ case 16:
+ /* 16 bpp, 565 format */
+ var->red.offset = 11;
+ var->green.offset = 5;
+ var->blue.offset = 0;
+ var->red.length = 5;
+ var->green.length = 6;
+ var->blue.length = 5;
+ break;
+
+ case 32:
+ case 28:
+ case 25:
+ var->transp.length = var->bits_per_pixel - 24;
+ var->transp.offset = 24;
+ /* drop through */
+ case 24:
+ /* our 24bpp is unpacked, so 32bpp */
+ var->bits_per_pixel = 32;
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ break;
+
+ default:
+ decon_err("invalid bpp %d\n", var->bits_per_pixel);
+ return -EINVAL;
+ }
+
+ decon_dbg("xres:%d, yres:%d, v_xres:%d, v_yres:%d, bpp:%d\n",
+ var->xres, var->yres, var->xres_virtual,
+ var->yres_virtual, var->bits_per_pixel);
+
+ return 0;
+}
+
+static inline unsigned int chan_to_field(unsigned int chan,
+ struct fb_bitfield *bf)
+{
+ chan &= 0xffff;
+ chan >>= 16 - bf->length;
+ return chan << bf->offset;
+}
+
+int decon_setcolreg(unsigned regno,
+ unsigned red, unsigned green, unsigned blue,
+ unsigned transp, struct fb_info *info)
+{
+ struct decon_win *win = info->par;
+ struct decon_device *decon = win->decon;
+ unsigned int val;
+
+ printk("@@@@ %s\n", __func__);
+ decon_dbg("%s: win %d: %d => rgb=%d/%d/%d\n", __func__, win->idx,
+ regno, red, green, blue);
+
+ if (IS_DECON_OFF_STATE(decon))
+ return 0;
+
+ switch (info->fix.visual) {
+ case FB_VISUAL_TRUECOLOR:
+ /* true-colour, use pseudo-palette */
+
+ if (regno < 16) {
+ u32 *pal = info->pseudo_palette;
+
+ val = chan_to_field(red, &info->var.red);
+ val |= chan_to_field(green, &info->var.green);
+ val |= chan_to_field(blue, &info->var.blue);
+
+ pal[regno] = val;
+ }
+ break;
+ default:
+ return 1; /* unknown type */
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(decon_setcolreg);
+
+int decon_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ struct decon_win *win = info->par;
+ struct decon_device *decon = win->decon;
+ struct v4l2_subdev *sd = NULL;
+ struct decon_win_config config;
+ int ret = 0;
+ struct decon_mode_info psr;
+
+ if (decon->dt.out_type != DECON_OUT_DSI) {
+ decon_warn("%s: decon%d unspported on out_type(%d)\n",
+ __func__, decon->id, decon->dt.out_type);
+ return 0;
+ }
+
+ if ((!IS_DECON_HIBER_STATE(decon) && IS_DECON_OFF_STATE(decon)) ||
+ decon->state == DECON_STATE_INIT) {
+ decon_warn("%s: decon%d state(%d), UNBLANK missed\n",
+ __func__, decon->id, decon->state);
+ return 0;
+ }
+
+ decon_dbg("%s: [%d %d %d %d %d %d]\n", __func__,
+ var->xoffset, var->yoffset,
+ var->xres, var->yres,
+ var->xres_virtual, var->yres_virtual);
+
+ memset(&config, 0, sizeof(struct decon_win_config));
+ switch (var->bits_per_pixel) {
+ case 16:
+ config.format = DECON_PIXEL_FORMAT_RGB_565;
+ break;
+ case 24:
+ case 32:
+ config.format = DECON_PIXEL_FORMAT_BGRA_8888;
+ break;
+ default:
+ decon_err("%s: Not supported bpp %d\n", __func__,
+ var->bits_per_pixel);
+ return -EINVAL;
+ }
+
+ config.dpp_parm.addr[0] = info->fix.smem_start;
+ config.src.x = var->xoffset;
+ config.src.y = var->yoffset;
+ config.src.w = var->xres;
+ config.src.h = var->yres;
+ config.src.f_w = var->xres_virtual;
+ config.src.f_h = var->yres_virtual;
+ config.dst.w = config.src.w;
+ config.dst.h = config.src.h;
+ config.dst.f_w = decon->lcd_info->xres;
+ config.dst.f_h = decon->lcd_info->yres;
+ if (decon_check_limitation(decon, decon->dt.dft_win, &config) < 0)
+ return -EINVAL;
+
+ decon_hiber_block_exit(decon);
+
+ decon_to_psr_info(decon, &psr);
+
+ /*
+ * info->var is old parameters and var is new requested parameters.
+ * var must be copied to info->var before decon_set_par function
+ * is called.
+ *
+ * If not, old parameters are set to window configuration
+ * and new parameters are set to DMA and DPP configuration.
+ */
+ memcpy(&info->var, var, sizeof(struct fb_var_screeninfo));
+
+ set_bit(decon->dt.dft_idma, &decon->cur_using_dpp);
+ set_bit(decon->dt.dft_idma, &decon->prev_used_dpp);
+ sd = decon->dpp_sd[decon->dt.dft_idma];
+ if (v4l2_subdev_call(sd, core, ioctl, DPP_WIN_CONFIG, &config)) {
+ decon_err("%s: Failed to config DPP-%d\n", __func__, win->dpp_id);
+ decon_reg_win_enable_and_update(decon->id, decon->dt.dft_win, false);
+ clear_bit(decon->dt.dft_idma, &decon->cur_using_dpp);
+ set_bit(decon->dt.dft_idma, &decon->dpp_err_stat);
+ goto err;
+ }
+
+ decon_set_par(info);
+
+ decon_reg_start(decon->id, &psr);
+err:
+ decon_wait_for_vsync(decon, VSYNC_TIMEOUT_MSEC);
+
+ if (decon_reg_wait_update_done_and_mask(decon->id, &psr, SHADOW_UPDATE_TIMEOUT)
+ < 0)
+ decon_err("%s: wait_for_update_timeout\n", __func__);
+
+ decon_hiber_unblock(decon);
+ return ret;
+}
+EXPORT_SYMBOL(decon_pan_display);
+
+int decon_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+ int ret;
+ struct decon_win *win = info->par;
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+#if defined(CONFIG_FB_TEST)
+ ret = dma_buf_mmap(win->fb_buf_data.dma_buf, vma, 0);
+#else
+ ret = dma_buf_mmap(win->dma_buf_data[0].dma_buf, vma, 0);
+#endif
+
+ return ret;
+}
+EXPORT_SYMBOL(decon_mmap);
+
+int decon_exit_hiber(struct decon_device *decon)
+{
+ int ret = 0;
+ struct decon_param p;
+ struct decon_mode_info psr;
+ enum decon_state prev_state = decon->state;
+
+ DPU_EVENT_START();
+
+ if (!decon->hiber.enabled)
+ return 0;
+
+ decon_hiber_block(decon);
+ kthread_flush_worker(&decon->hiber.worker);
+ mutex_lock(&decon->hiber.lock);
+
+ if (decon->state != DECON_STATE_HIBER)
+ goto err;
+
+ decon_dbg("enable decon-%d\n", decon->id);
+
+ ret = decon_set_out_sd_state(decon, DECON_STATE_ON);
+ if (ret < 0) {
+ decon_err("%s decon-%d failed to set subdev EXIT_ULPS state\n",
+ __func__, decon->id);
+ }
+
+ decon_to_init_param(decon, &p);
+ decon_reg_init(decon->id, decon->dt.out_idx[0], &p);
+
+ /*
+ * After hibernation exit, If panel is partial size, DECON and DSIM
+ * are also set as same partial size.
+ */
+ if (!is_full(&decon->win_up.prev_up_region, decon->lcd_info))
+ dpu_set_win_update_partial_size(decon, &decon->win_up.prev_up_region);
+
+ if (!decon->id && !decon->eint_status) {
+ struct irq_desc *desc = irq_to_desc(decon->res.irq);
+ /* Pending IRQ clear */
+ if ((!IS_ERR_OR_NULL(desc)) && (desc->irq_data.chip->irq_ack)) {
+ desc->irq_data.chip->irq_ack(&desc->irq_data);
+ desc->istate &= ~IRQS_PENDING;
+ }
+ enable_irq(decon->res.irq);
+ decon->eint_status = 1;
+ }
+
+ decon->state = DECON_STATE_ON;
+ decon_to_psr_info(decon, &psr);
+ decon_reg_set_int(decon->id, &psr, 1);
+
+ decon_hiber_trig_reset(decon);
+
+ decon_dbg("decon-%d %s - (state:%d -> %d)\n",
+ decon->id, __func__, prev_state, decon->state);
+ decon->hiber.exit_cnt++;
+ DPU_EVENT_LOG(DPU_EVT_EXIT_HIBER, &decon->sd, start);
+
+err:
+ decon_hiber_unblock(decon);
+ mutex_unlock(&decon->hiber.lock);
+
+ return ret;
+}
+
+int decon_enter_hiber(struct decon_device *decon)
+{
+ int ret = 0;
+ struct decon_mode_info psr;
+ enum decon_state prev_state = decon->state;
+
+ DPU_EVENT_START();
+
+ if (!decon->hiber.enabled)
+ return 0;
+
+ mutex_lock(&decon->hiber.lock);
+
+ if (decon_is_enter_shutdown(decon))
+ goto err2;
+
+ if (decon_is_hiber_blocked(decon))
+ goto err2;
+
+ decon_hiber_block(decon);
+ if (decon->state != DECON_STATE_ON)
+ goto err;
+
+ decon_dbg("decon-%d %s +\n", decon->id, __func__);
+ decon_hiber_trig_reset(decon);
+
+ kthread_flush_worker(&decon->up.worker);
+
+ decon_to_psr_info(decon, &psr);
+ decon_reg_set_int(decon->id, &psr, 0);
+
+ if (!decon->id && (decon->vsync.irq_refcount <= 0) &&
+ decon->eint_status) {
+ disable_irq(decon->res.irq);
+ decon->eint_status = 0;
+ }
+
+ ret = decon_reg_stop(decon->id, decon->dt.out_idx[0], &psr, true,
+ decon->lcd_info->fps);
+ if (ret < 0)
+ decon_dump(decon);
+
+ /* DMA protection disable must be happen on dpp domain is alive */
+ if (decon->dt.out_type != DECON_OUT_WB) {
+#if defined(CONFIG_EXYNOS_CONTENT_PATH_PROTECTION)
+ decon_set_protected_content(decon, NULL);
+#endif
+ decon->cur_using_dpp = 0;
+ decon_dpp_stop(decon, false);
+ }
+
+#if defined(CONFIG_EXYNOS9820_BTS)
+ decon->bts.ops->bts_release_bw(decon);
+#endif
+
+ ret = decon_set_out_sd_state(decon, DECON_STATE_HIBER);
+ if (ret < 0)
+ decon_err("%s decon-%d failed to set subdev ENTER_ULPS state\n",
+ __func__, decon->id);
+
+ decon->state = DECON_STATE_HIBER;
+
+ decon->hiber.enter_cnt++;
+ DPU_EVENT_LOG(DPU_EVT_ENTER_HIBER, &decon->sd, start);
+
+err:
+ decon_hiber_unblock(decon);
+err2:
+ mutex_unlock(&decon->hiber.lock);
+
+ decon_dbg("decon-%d %s - (state:%d -> %d)\n",
+ decon->id, __func__, prev_state, decon->state);
+
+ return ret;
+}
+
+int decon_hiber_block_exit(struct decon_device *decon)
+{
+ int ret = 0;
+
+ if (!decon || !decon->hiber.enabled)
+ return 0;
+
+ decon_hiber_block(decon);
+ ret = decon_exit_hiber(decon);
+
+ return ret;
+}
+
+static void decon_hiber_handler(struct kthread_work *work)
+{
+ struct decon_hiber *hiber =
+ container_of(work, struct decon_hiber, work);
+ struct decon_device *decon =
+ container_of(hiber, struct decon_device, hiber);
+
+ if (!decon || !decon->hiber.enabled)
+ return;
+
+ if (decon_hiber_enter_cond(decon))
+ decon_enter_hiber(decon);
+}
+
+int decon_register_hiber_work(struct decon_device *decon)
+{
+ struct sched_param param;
+
+ decon->hiber.enabled = false;
+ if (!IS_ENABLED(CONFIG_EXYNOS_HIBERNATION)) {
+ decon_info("display doesn't support hibernation mode\n");
+ return 0;
+ }
+
+ mutex_init(&decon->hiber.lock);
+
+ atomic_set(&decon->hiber.trig_cnt, 0);
+ atomic_set(&decon->hiber.block_cnt, 0);
+
+ kthread_init_worker(&decon->hiber.worker);
+ decon->hiber.thread = kthread_run(kthread_worker_fn,
+ &decon->hiber.worker, "decon_hiber");
+ if (IS_ERR(decon->hiber.thread)) {
+ decon->hiber.thread = NULL;
+ decon_err("failed to run hibernation thread\n");
+ return PTR_ERR(decon->hiber.thread);
+ }
+ param.sched_priority = 20;
+ sched_setscheduler_nocheck(decon->hiber.thread, SCHED_FIFO, ¶m);
+ kthread_init_work(&decon->hiber.work, decon_hiber_handler);
+
+ decon->hiber.enabled = true;
+ decon_info("display supports hibernation mode\n");
+
+ return 0;
+}
+
+void decon_init_low_persistence_mode(struct decon_device *decon)
+{
+ decon->low_persistence = false;
+
+ if (!IS_ENABLED(CONFIG_EXYNOS_LOW_PERSISTENCE)) {
+ decon_info("display doesn't support low persistence mode\n");
+ return;
+ }
+
+ decon->low_persistence = true;
+ decon_info("display supports low persistence mode\n");
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Interface file betwen DECON and Writeback 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 <linux/clk-provider.h>
+#include "decon.h"
+
+static irqreturn_t decon_wb_irq_handler(int irq, void *dev_data)
+{
+ struct decon_device *decon = dev_data;
+ u32 irq_sts_reg, ext_irq;
+ ext_irq = 0;
+
+ spin_lock(&decon->slock);
+ if (IS_DECON_OFF_STATE(decon))
+ goto irq_end;
+
+ irq_sts_reg = decon_reg_get_interrupt_and_clear(decon->id, &ext_irq);
+
+ if (irq_sts_reg & DPU_FRAME_DONE_INT_EN)
+ DPU_EVENT_LOG(DPU_EVT_DECON_FRAMEDONE, &decon->sd, ktime_set(0, 0));
+
+ if (ext_irq & DPU_RESOURCE_CONFLICT_INT_EN) {
+ DPU_EVENT_LOG(DPU_EVT_RSC_CONFLICT, &decon->sd, ktime_set(0, 0));
+ decon_err("DECON%d Resource Conflict(ext_irq=0x%x, irq_sts=0x%x)\n",
+ decon->id, ext_irq, irq_sts_reg);
+ }
+irq_end:
+ spin_unlock(&decon->slock);
+ return IRQ_HANDLED;
+}
+
+int decon_wb_register_irq(struct decon_device *decon)
+{
+ struct device *dev = decon->dev;
+ struct platform_device *pdev;
+ struct resource *res;
+ int ret = 0;
+
+ pdev = container_of(dev, struct platform_device, dev);
+
+ /* Get IRQ resource and register IRQ handler. */
+ /* 0: Under Flow irq */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ ret = devm_request_irq(dev, res->start, decon_wb_irq_handler, 0,
+ pdev->name, decon);
+ if (ret) {
+ decon_err("failed to install irq\n");
+ return ret;
+ }
+
+ /* 1: FrameStart irq */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ ret = devm_request_irq(dev, res->start, decon_wb_irq_handler, 0,
+ pdev->name, decon);
+ if (ret) {
+ decon_err("failed to install irq\n");
+ return ret;
+ }
+
+ /* 2: FrameDone irq */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 2);
+ ret = devm_request_irq(dev, res->start, decon_wb_irq_handler, 0,
+ pdev->name, decon);
+ if (ret) {
+ decon_err("failed to install irq\n");
+ return ret;
+ }
+
+ /* 3: Extra irq */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 3);
+ ret = devm_request_irq(dev, res->start, decon_wb_irq_handler, 0,
+ pdev->name, decon);
+ if (ret) {
+ decon_err("failed to install irq\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+void decon_wb_free_irq(struct decon_device *decon)
+{
+ struct device *dev = decon->dev;
+ struct platform_device *pdev;
+ struct resource *res;
+
+ pdev = container_of(dev, struct platform_device, dev);
+
+ /* Unregister IRQ handler. */
+ /* 0: Under Flow irq */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ devm_free_irq(dev, res->start, decon);
+
+ /* 1: FrameStart irq */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ devm_free_irq(dev, res->start, decon);
+
+ /* 2: FrameDone irq */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 2);
+ devm_free_irq(dev, res->start, decon);
+
+ /* 3: Extra irq */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 3);
+ devm_free_irq(dev, res->start, decon);
+}
+
+int decon_wb_get_clocks(struct decon_device *decon)
+{
+ decon->res.aclk = devm_clk_get(decon->dev, "aclk");
+ if (IS_ERR_OR_NULL(decon->res.aclk)) {
+ decon_err("failed to get aclk\n");
+ return PTR_ERR(decon->res.aclk);
+ }
+ decon->res.busd = devm_clk_get(decon->dev, "busd");
+ if (IS_ERR_OR_NULL(decon->res.busd)) {
+ decon_err("failed to get decon_busd\n");
+ return PTR_ERR(decon->res.busd);
+ }
+ decon->res.busp = devm_clk_get(decon->dev, "busp");
+ if (IS_ERR_OR_NULL(decon->res.busp)) {
+ decon_err("failed to get decon_busp\n");
+ return PTR_ERR(decon->res.busp);
+ }
+
+ return 0;
+}
+
+void decon_wb_set_clocks(struct decon_device *decon)
+{
+}
+
+static int decon_wb_set_lcd_info(struct decon_device *decon)
+{
+ struct decon_lcd *lcd_info;
+
+ if (decon->lcd_info == NULL) {
+ lcd_info = kzalloc(sizeof(struct decon_lcd), GFP_KERNEL);
+ if (!lcd_info) {
+ decon_err("could not allocate decon_lcd for wb\n");
+ return -ENOMEM;
+ }
+
+ decon->lcd_info = lcd_info;
+ }
+
+ decon->lcd_info->width = 1440;
+ decon->lcd_info->height = 2560;
+ decon->lcd_info->xres = 1440;
+ decon->lcd_info->yres = 2560;
+ decon->lcd_info->vfp = 2;
+ decon->lcd_info->vbp = 20;
+ decon->lcd_info->hfp = 20;
+ decon->lcd_info->hbp = 20;
+ decon->lcd_info->vsa = 2;
+ decon->lcd_info->hsa = 20;
+ decon->lcd_info->fps = 60;
+ decon->dt.out_type = DECON_OUT_WB;
+ decon->dt.psr_mode = DECON_MIPI_COMMAND_MODE;
+ decon->dt.trig_mode = DECON_SW_TRIG;
+
+ decon_info("decon_%d output size for writeback %dx%d\n", decon->id,
+ decon->lcd_info->width, decon->lcd_info->height);
+
+ return 0;
+}
+
+int decon_wb_get_out_sd(struct decon_device *decon)
+{
+ decon->out_sd[0] = decon->dpp_sd[ODMA_WB];
+ if (IS_ERR_OR_NULL(decon->out_sd[0])) {
+ decon_err("failed to get dpp%d sd\n", ODMA_WB);
+ return -ENOMEM;
+ }
+ decon->out_sd[1] = NULL;
+
+ decon_wb_set_lcd_info(decon);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Header file 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.
+*/
+
+#ifndef _DISPLAYPORT_H_
+#define _DISPLAYPORT_H_
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-dv-timings.h>
+#include <uapi/linux/v4l2-dv-timings.h>
+#include <linux/phy/phy.h>
+#if defined(CONFIG_EXTCON)
+#include <linux/extcon.h>
+#endif
+#if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER)
+#include <linux/usb/manager/usb_typec_manager_notifier.h>
+#include <linux/notifier.h>
+#include <linux/ccic/ccic_notifier.h>
+#endif
+
+#include "./cal_9610/regs-displayport.h"
+#include "./panels/decon_lcd.h"
+#include "hdr_metadata.h"
+
+extern int displayport_log_level;
+
+#define DISPLAYPORT_MODULE_NAME "exynos-displayport"
+
+#define displayport_err(fmt, ...) \
+ do { \
+ if (displayport_log_level >= 3) { \
+ pr_err("Displayport: " pr_fmt(fmt), ##__VA_ARGS__); \
+ } \
+ } while (0)
+
+#define displayport_warn(fmt, ...) \
+ do { \
+ if (displayport_log_level >= 4) { \
+ pr_warn("Displayport: " pr_fmt(fmt), ##__VA_ARGS__); \
+ } \
+ } while (0)
+
+#define displayport_info(fmt, ...) \
+ do { \
+ if (displayport_log_level >= 6) \
+ pr_info("Displayport: " pr_fmt(fmt), ##__VA_ARGS__); \
+ } while (0)
+
+#define displayport_dbg(fmt, ...) \
+ do { \
+ if (displayport_log_level >= 7) \
+ pr_info("Displayport: " pr_fmt(fmt), ##__VA_ARGS__); \
+ } while (0)
+
+extern struct displayport_device *displayport_drvdata;
+
+enum displayport_state {
+ DISPLAYPORT_STATE_INIT,
+ DISPLAYPORT_STATE_ON,
+ DISPLAYPORT_STATE_OFF
+};
+
+enum displayport_dynamic_range_type {
+ VESA_RANGE = 0, /* (0 ~ 255) */
+ CEA_RANGE = 1, /* (16 ~ 235) */
+};
+
+struct displayport_resources {
+ int aux_ch_mux_gpio;
+ int irq;
+ void __iomem *link_regs;
+ void __iomem *phy_regs;
+ struct clk *aclk;
+};
+
+enum displayport_aux_ch_command_type {
+ I2C_WRITE = 0x4,
+ I2C_READ = 0x5,
+ DPCD_WRITE = 0x8,
+ DPCD_READ = 0x9,
+};
+
+typedef enum {
+ NORAMAL_DATA = 0,
+ TRAINING_PATTERN_1 = 1,
+ TRAINING_PATTERN_2 = 2,
+ TRAINING_PATTERN_3 = 3,
+} displayport_training_pattern;
+
+typedef enum {
+ DISABLE_PATTEN = 0,
+ D10_2_PATTERN = 1,
+ SERP_PATTERN = 2,
+ PRBS7 = 3,
+ CUSTOM_80BIT = 4,
+ HBR2_COMPLIANCE = 5,
+} displayport_qual_pattern;
+
+typedef enum {
+ ENABLE_SCRAM = 0,
+ DISABLE_SCRAM = 1,
+}displayport_scrambling;
+
+enum displayport_interrupt_mask {
+ PLL_LOCK_CHG_INT_MASK,
+ HOTPLUG_CHG_INT_MASK,
+ HPD_LOST_INT_MASK,
+ PLUG_INT_MASK,
+ HPD_IRQ_INT_MASK,
+ RPLY_RECEIV_INT_MASK,
+ AUX_ERR_INT_MASK,
+ HDCP_LINK_CHECK_INT_MASK,
+ HDCP_LINK_FAIL_INT_MASK,
+ HDCP_R0_READY_INT_MASK,
+ VIDEO_FIFO_UNDER_FLOW_MASK,
+ VSYNC_DET_INT_MASK,
+ AUDIO_FIFO_UNDER_RUN_INT_MASK,
+ AUDIO_FIFO_OVER_RUN_INT_MASK,
+
+ ALL_INT_MASK
+};
+
+#define MAX_LANE_CNT 4
+#define DPCD_BUF_SIZE 12
+
+#define FB_AUDIO_LPCM 1
+
+#define FB_AUDIO_192KHZ (1 << 6)
+#define FB_AUDIO_176KHZ (1 << 5)
+#define FB_AUDIO_96KHZ (1 << 4)
+#define FB_AUDIO_88KHZ (1 << 3)
+#define FB_AUDIO_48KHZ (1 << 2)
+#define FB_AUDIO_44KHZ (1 << 1)
+#define FB_AUDIO_32KHZ (1 << 0)
+
+#define FB_AUDIO_24BIT (1 << 2)
+#define FB_AUDIO_20BIT (1 << 1)
+#define FB_AUDIO_16BIT (1 << 0)
+
+struct fb_audio {
+ u8 format;
+ u8 channel_count;
+ u8 sample_rates;
+ u8 bit_rates;
+ u8 speaker;
+};
+
+struct fb_vendor {
+ u8 vic_len;
+ u8 vic_data[16];
+};
+
+#define MAX_REACHED_CNT 3
+#define MAX_SWING_REACHED_BIT_POS 2
+#define MAX_PRE_EMPHASIS_REACHED_BIT_POS 5
+#define DPCP_LINK_SINK_STATUS_FIELD_LENGTH 8
+
+#define DPCD_ADD_REVISION_NUMBER 0x00000
+#define DPCD_ADD_MAX_LINK_RATE 0x00001
+#define LINK_RATE_1_62Gbps 0x06
+#define LINK_RATE_2_7Gbps 0x0A
+#define LINK_RATE_5_4Gbps 0x14
+
+#define DPCD_ADD_MAX_LANE_COUNT 0x00002
+#define MAX_LANE_COUNT (0x1F << 0)
+#define TPS3_SUPPORTED (1 << 6)
+#define ENHANCED_FRAME_CAP (1 << 7)
+
+#define DPCD_ADD_MAX_DOWNSPREAD 0x00003
+#define NO_AUX_HANDSHAKE_LINK_TRANING (1 << 6)
+
+#define DPCD_ADD_DOWN_STREAM_PORT_PRESENT 0x00005
+#define BIT_DFP_TYPE 0x6
+#define DFP_TYPE_DP 0x00
+#define DFP_TYPE_VGA 0x01 /* analog video */
+#define DFP_TYPE_HDMI 0x2
+#define DFP_TYPE_OTHERS 0x3 /* not have EDID like composite and Svideo port */
+
+#define DPCD_ADD_I2C_SPEED_CONTROL_CAPABILITES 0x0000C
+#define I2C_1Kbps 0x01
+#define I2C_5Kbps 0x02
+#define I2C_10Kbps 0x04
+#define I2C_100Kbps 0x08
+#define I2C_400Kbps 0x10
+#define I2C_1Mbps 0x20
+
+#define DPCD_ADD_TRAINING_AUX_RD_INTERVAL 0x0000E
+#define TRANING_AUX_RD_INTERVAL_400us 0x00
+#define TRANING_AUX_RD_INTERVAL_4ms 0x01
+#define TRANING_AUX_RD_INTERVAL_8ms 0x02
+#define TRANING_AUX_RD_INTERVAL_12ms 0x03
+#define TRANING_AUX_RD_INTERVAL_16ms 0x04
+
+#define DPCD_ADD_LINK_BW_SET 0x00100
+
+#define DPCD_ADD_LANE_COUNT_SET 0x00101
+
+#define DPCD_ADD_TRANING_PATTERN_SET 0x00102
+#define TRAINING_PTTERN_SELECT (3 << 0)
+#define RECOVERED_CLOCK_OUT_EN (1 << 4)
+#define DPCD_SCRAMBLING_DISABLE (1 << 5)
+#define SYMBOL_ERROR_COUNT_SEL (3 << 6)
+
+#define DPCD_ADD_TRANING_LANE0_SET 0x00103
+#define DPCD_ADD_TRANING_LANE1_SET 0x00104
+#define DPCD_ADD_TRANING_LANE2_SET 0x00105
+#define DPCD_ADD_TRANING_LANE3_SET 0x00106
+#define VOLTAGE_SWING_SET (3 << 0)
+#define MAX_SWING_REACHED (1 << 2)
+#define PRE_EMPHASIS_SWING_SET (3 << 3)
+#define MAX_PRE_EMPHASIS_REACHED (1 << 5)
+
+#define DPCD_ADD_I2C_SPEED_CONTROL_STATUS 0x00109
+
+#define DPCD_ADD_LINK_QUAL_LANE0_SET 0x0010B
+#define DPCD_ADD_LINK_QUAL_LANE1_SET 0x0010C
+#define DPCD_ADD_LINK_QUAL_LANE2_SET 0x0010D
+#define DPCD_ADD_LINK_QUAL_LANE3_SET 0x0010E
+#define DPCD_LINK_QUAL_PATTERN_SET (7 << 0)
+
+#define DPCD_ADD_SINK_COUNT 0x00200
+#define SINK_COUNT2 (1 << 7)
+#define CP_READY (1 << 6)
+#define SINK_COUNT1 (0x3F << 0)
+
+#define DPCD_ADD_DEVICE_SERVICE_IRQ_VECTOR 0x00201
+#define AUTOMATED_TEST_REQUEST (1 << 1)
+#define CP_IRQ (1 << 2)
+#define MCCS_IRQ (1 << 3)
+#define DOWN_REP_MSG_RDY (1 << 4)
+#define UP_REQ_MSG_RDY (1 << 5)
+#define SINK_SPECIFIC_IRQ (1 << 6)
+
+#define DPCD_ADD_LANE0_1_STATUS 0x00202
+#define LANE0_CR_DONE (1 << 0)
+#define LANE0_CHANNEL_EQ_DONE (1 << 1)
+#define LANE0_SYMBOL_LOCKED (1 << 2)
+#define LANE1_CR_DONE (1 << 4)
+#define LANE1_CHANNEL_EQ_DONE (1 << 5)
+#define LANE1_SYMBOL_LOCKED (1 << 6)
+
+#define DPCD_ADD_LANE2_3_STATUS 0x00203
+#define LANE2_CR_DONE (1 << 0)
+#define LANE2_CHANNEL_EQ_DONE (1 << 1)
+#define LANE2_SYMBOL_LOCKED (1 << 2)
+#define LANE3_CR_DONE (1 << 4)
+#define LANE3_CHANNEL_EQ_DONE (1 << 5)
+#define LANE3_SYMBOL_LOCKED (1 << 6)
+
+#define DPCD_ADD_LANE_ALIGN_STATUS_UPDATE 0x00204
+#define INTERLANE_ALIGN_DONE (1 << 0)
+#define DOWNSTREAM_PORT_STATUS_CHANGED (1 << 6)
+#define LINK_STATUS_UPDATE (1 << 7)
+
+#define DPCD_ADD_SINK_STATUS 0x00205
+#define RECEIVE_PORT_0_STATUS (1 << 0)
+#define RECEIVE_PORT_1_STATUS (1 << 1)
+
+#define DPCD_ADD_ADJUST_REQUEST_LANE0_1 0x00206
+#define VOLTAGE_SWING_LANE0 (3 << 0)
+#define PRE_EMPHASIS_LANE0 (3 << 2)
+#define VOLTAGE_SWING_LANE1 (3 << 4)
+#define PRE_EMPHASIS_LANE1 (3 << 6)
+
+#define DPCD_ADD_ADJUST_REQUEST_LANE2_3 0x00207
+#define VOLTAGE_SWING_LANE2 (3 << 0)
+#define PRE_EMPHASIS_LANE2 (3 << 2)
+#define VOLTAGE_SWING_LANE3 (3 << 4)
+#define PRE_EMPHASIS_LANE3 (3 << 6)
+
+#define DPCD_TEST_REQUEST 0x00218
+#define TEST_LINK_TRAINING (1 << 0)
+#define TEST_VIDEO_PATTERN (1 << 1)
+#define TEST_EDID_READ (1 << 2)
+#define TEST_PHY_TEST_PATTERN (1 << 3)
+#define TEST_FAUX_TEST_PATTERN (1<<4)
+#define TEST_AUDIO_PATTERN (1<<5)
+#define TEST_AUDIO_DISABLED_VIDEO (1<<6)
+
+#define DPCD_TEST_LINK_RATE 0x00219
+#define TEST_LINK_RATE (0xFF << 0)
+
+#define DPCD_TEST_LANE_COUNT 0x00220
+#define TEST_LANE_COUNT (0x1F << 0)
+
+#define DPCD_TEST_PATTERN 0x00221
+
+#define DPCD_TEST_H_TOTAL_1 0x00222 //[15:8]
+#define DPCD_TEST_H_TOTAL_2 0x00223 //[7:0]
+#define DPCD_TEST_V_TOTAL_1 0x00224 //[15:8]
+#define DPCD_TEST_V_TOTAL_2 0x00225 //[7:0]
+
+#define DPCD_TEST_H_START_1 0x00226 //[15:8]
+#define DPCD_TEST_H_START_2 0x00227 //[7:0]
+#define DPCD_TEST_V_START_1 0x00228 //[15:8]
+#define DPCD_TEST_V_START_2 0x00229 //[7:0]
+
+#define DPCD_TEST_H_SYNC_1 0x0022A //[15:8]
+#define DPCD_TEST_H_SYNC_2 0x0022B //[7:0]
+#define DPCD_TEST_V_SYNC_1 0x0022C //[15:8]
+#define DPCD_TEST_V_SYNC_2 0x0022D //[7:0]
+
+#define DPCD_TEST_H_WIDTH_1 0x0022E //[15:8]
+#define DPCD_TEST_H_WIDTH_2 0x0022F //[7:0]
+#define DPCD_TEST_V_HEIGHT_1 0x00230 //[15:8]
+#define DPCD_TEST_V_HEIGHT_2 0x00231 //[7:0]
+
+#define DPCD_TEST_MISC_1 0x00232
+#define TEST_SYNCHRONOUS_CLOCK (1 << 0)
+#define TEST_COLOR_FORMAT (3 << 1)
+#define TEST_DYNAMIC_RANGE (1 << 3)
+#define TEST_YCBCR_COEFFICIENTS (1 << 4)
+#define TEST_BIT_DEPTH (7 << 5)
+
+#define DPCD_TEST_MISC_2 0x00233
+#define TEST_REFRESH_DENOMINATOR (1 << 0)
+#define TEST_INTERLACED (1 << 1)
+
+#define DPCD_TEST_REFRESH_RATE_NUMERATOR 0x00234
+
+#define DCDP_ADD_PHY_TEST_PATTERN 0x00248
+#define PHY_TEST_PATTERN_SEL (3 << 0)
+
+#define DPCD_TEST_RESPONSE 0x00260
+#define TEST_ACK (1 << 0)
+#define TEST_NAK (1 << 1)
+#define TEST_EDID_CHECKSUM_WRITE (1 << 2)
+
+#define DPCD_TEST_EDID_CHECKSUM 0x00261
+
+#define DPCD_TEST_AUDIO_MODE 0x00271
+#define TEST_AUDIO_SAMPLING_RATE (0x0F << 0)
+#define TEST_AUDIO_CHANNEL_COUNT (0xF0 << 0)
+
+#define DPCD_TEST_AUDIO_PATTERN_TYPE 0x00272
+
+#define DPCD_BRANCH_HW_REVISION 0x509
+#define DPCD_BRANCH_SW_REVISION_MAJOR 0x50A
+#define DPCD_BRANCH_SW_REVISION_MINOR 0x50B
+
+#define DPCD_ADD_SET_POWER 0x00600
+#define SET_POWER_STATE (3 << 0)
+#define SET_POWER_DOWN 0x02
+#define SET_POWER_NORMAL 0x01
+
+#define DPCD_HDCP22_RX_CAPS 0x6921D
+#define VERSION (0xFF << 16)
+#define HDCP_CAPABLE (1 << 1)
+
+#define DPCD_HDCP22_RX_INFO 0x69330
+
+#define DPCD_HDCP22_RX_CAPS_LENGTH 3
+#define DPCD_HDCP_VERSION_BIT_POSITION 16
+
+#define DPCD_HDCP22_RXSTATUS_READY (0x1 << 0)
+#define DPCD_HDCP22_RXSTATUS_HPRIME_AVAILABLE (0x1 << 1)
+#define DPCD_HDCP22_RXSTATUS_PAIRING_AVAILABLE (0x1 << 2)
+#define DPCD_HDCP22_RXSTATUS_REAUTH_REQ (0x1 << 3)
+#define DPCD_HDCP22_RXSTATUS_LINK_INTEGRITY_FAIL (0x1 << 4)
+
+#define HDCP_VERSION_1_3 0x13
+#define HDCP_VERSION_2_2 0x02
+
+#define SYNC_POSITIVE 0
+#define SYNC_NEGATIVE 1
+
+#define AUDIO_BUF_FULL_SIZE 33
+#define AUDIO_DISABLE 0
+#define AUDIO_ENABLE 1
+#define AUDIO_WAIT_BUF_FULL 2
+
+enum phy_tune_info {
+ PHY_AMP_PARAM = 0,
+ PHY_EMP_PARAM = 1,
+ PHY_IDRV_EN_PARAM = 2,
+};
+
+typedef enum {
+ V640X480P60,
+ V720X480P60,
+ V720X576P50,
+ V1280X720P50,
+ V1280X720P60,
+ V1280X800P60RB,
+ V1280X1024P60,
+ V1920X1080P24,
+ V1920X1080P25,
+ V1920X1080P30,
+ V1600X900P60RB,
+ V1920X1080P50,
+ V1920X1080P60,
+ V2048X1536P60,
+ V1920X1440P60,
+ V2560X1440P59,
+ V2560X1440P60,
+ V3840X2160P24,
+ V3840X2160P25,
+ V3840X2160P30,
+ V4096X2160P24,
+ V4096X2160P25,
+ V4096X2160P30,
+ V3840X2160P59RB,
+ V3840X2160P50,
+ V3840X2160P60,
+ V4096X2160P50,
+ V4096X2160P60,
+ V640X10P60SACRC,
+} videoformat;
+
+typedef enum{
+ ASYNC_MODE = 0,
+ SYNC_MODE,
+} audio_sync_mode;
+
+enum audio_sampling_frequency {
+ FS_32KHZ = 0,
+ FS_44KHZ = 1,
+ FS_48KHZ = 2,
+ FS_88KHZ = 3,
+ FS_96KHZ = 4,
+ FS_176KHZ = 5,
+ FS_192KHZ = 6,
+};
+
+enum audio_bit_per_channel {
+ AUDIO_16_BIT = 0,
+ AUDIO_20_BIT,
+ AUDIO_24_BIT,
+};
+
+enum audio_16bit_dma_mode {
+ NORMAL_MODE = 0,
+ PACKED_MODE = 1,
+ PACKED_MODE2 = 2,
+};
+
+enum audio_dma_word_length {
+ WORD_LENGTH_1 = 0,
+ WORD_LENGTH_2,
+ WORD_LENGTH_3,
+ WORD_LENGTH_4,
+ WORD_LENGTH_5,
+ WORD_LENGTH_6,
+ WORD_LENGTH_7,
+ WORD_LENGTH_8,
+};
+
+enum audio_clock_accuracy {
+ Level2 = 0,
+ Level1 = 1,
+ Level3 = 2,
+ NOT_MATCH = 3,
+};
+
+enum bit_depth{
+ BPC_6 = 0,
+ BPC_8,
+ BPC_10,
+ BPC_12,
+ BPC_16,
+};
+
+enum test_pattern{
+ COLOR_BAR = 0,
+ WGB_BAR,
+ MW_BAR,
+};
+
+enum hotplug_state{
+ HPD_UNPLUG = 0,
+ HPD_PLUG,
+ HPD_IRQ,
+};
+
+#if defined(CONFIG_EXTCON)
+static const unsigned int extcon_id[] = {
+ EXTCON_DISP_DP,
+
+ EXTCON_NONE,
+};
+#endif
+
+struct edid_data {
+ int max_support_clk;
+ bool support_10bpc;
+ bool hdr_support;
+ u8 eotf;
+ u8 max_lumi_data;
+ u8 max_average_lumi_data;
+ u8 min_lumi_data;
+};
+
+struct displayport_device {
+ enum displayport_state state;
+ struct device *dev;
+ struct displayport_resources res;
+
+ unsigned int data_lane;
+ u32 data_lane_cnt;
+ struct phy *phy;
+ spinlock_t slock;
+
+ struct dsim_lcd_driver *panel_ops;
+ struct decon_lcd lcd_info;
+
+ struct v4l2_subdev sd;
+ struct v4l2_dv_timings cur_timings;
+
+ struct workqueue_struct *dp_wq;
+ struct workqueue_struct *hdcp2_wq;
+ struct delayed_work hpd_plug_work;
+ struct delayed_work hpd_unplug_work;
+ struct delayed_work hpd_irq_work;
+#if defined(CONFIG_EXTCON)
+ struct extcon_dev *extcon_displayport;
+ //struct extcon_dev audio_switch;
+#endif
+ struct delayed_work hdcp13_work;
+ struct delayed_work hdcp22_work;
+ struct delayed_work hdcp13_integrity_check_work;
+ int hdcp_ver;
+
+ struct mutex cmd_lock;
+ struct mutex hpd_lock;
+ struct mutex aux_lock;
+ struct mutex training_lock;
+ wait_queue_head_t dp_wait;
+ int audio_state;
+ int audio_buf_empty_check;
+ wait_queue_head_t audio_wait;
+#if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER)
+ struct delayed_work notifier_register_work;
+ struct notifier_block dp_typec_nb;
+ ccic_notifier_dp_pinconf_t ccic_notify_dp_conf;
+ int notifier_registered;
+ bool ccic_link_conf;
+ bool ccic_hpd;
+#endif
+ int hpd_current_state;
+ enum hotplug_state hpd_state;
+ int dp_sw_sel;
+ int gpio_sw_oe;
+ int gpio_sw_sel;
+ int gpio_usb_dir;
+ int dfp_type;
+ const char *aux_vdd;
+
+ int auto_test_mode;
+ enum bit_depth bpc;
+ u8 bist_used;
+ enum test_pattern bist_type;
+ enum displayport_dynamic_range_type dyn_range;
+ videoformat cur_video;
+
+ struct edid_data rx_edid_data;
+
+ int idle_ip_index;
+};
+
+struct displayport_debug_param {
+ u8 param_used;
+ u8 link_rate;
+ u8 lane_cnt;
+};
+
+/* EDID functions */
+/* default preset configured on probe */
+#define EDID_DEFAULT_TIMINGS_IDX (0) /* 640x480@60Hz */
+
+#define EDID_ADDRESS 0x50
+#define AUX_DATA_BUF_COUNT 16
+#define EDID_BUF_COUNT 256
+#define AUX_RETRY_COUNT 3
+#define AUX_TIMEOUT_1800us 0x03
+
+#define EDID_BLOCK_SIZE 128
+#define DATA_BLOCK_TAG_CODE_MASK 0xE0
+#define DATA_BLOCK_LENGTH_MASK 0x1F
+#define DATA_BLOCK_TAG_CODE_BIT_POSITION 5
+
+#define VSDB_TAG_CODE 3
+#define HDMI14_IEEE_OUI_0 0x03
+#define HDMI14_IEEE_OUI_1 0x0C
+#define HDMI14_IEEE_OUI_2 0x00
+#define IEEE_OUI_0_BYTE_NUM 1
+#define IEEE_OUI_1_BYTE_NUM 2
+#define IEEE_OUI_2_BYTE_NUM 3
+#define VSDB_LATENCY_FILEDS_PRESETNT_MASK 0x80
+#define VSDB_I_LATENCY_FILEDS_PRESETNT_MASK 0x40
+#define VSDB_HDMI_VIDEO_PRESETNT_MASK 0x20
+#define VSDB_VIC_FIELD_OFFSET 14
+#define VSDB_VIC_LENGTH_MASK 0xE0
+#define VSDB_VIC_LENGTH_BIT_POSITION 5
+
+#define HDMI20_IEEE_OUI_0 0xD8
+#define HDMI20_IEEE_OUI_1 0x5D
+#define HDMI20_IEEE_OUI_2 0xC4
+#define MAX_TMDS_RATE_BYTE_NUM 5
+#define DC_SUPPORT_BYTE_NUM 7
+#define DC_30BIT (0x01 << 0)
+
+#define USE_EXTENDED_TAG_CODE 7
+#define EXTENDED_HDR_TAG_CODE 6
+#define EXTENDED_TAG_CODE_BYTE_NUM 1
+#define SUPPORTED_EOTF_BYTE_NUM 2
+#define SDR_LUMI (0x01 << 0)
+#define HDR_LUMI (0x01 << 1)
+#define SMPTE_ST_2084 (0x01 << 2)
+#define MAX_LUMI_BYTE_NUM 4
+#define MAX_AVERAGE_LUMI_BYTE_NUM 5
+#define MIN_LUMI_BYTE_NUM 6
+
+#define DETAILED_TIMING_DESCRIPTION_SIZE 18
+#define AUDIO_DATA_BLOCK 1
+#define VIDEO_DATA_BLOCK 2
+#define SPEAKER_DATA_BLOCK 4
+#define SVD_VIC_MASK 0x7F
+
+struct displayport_supported_preset {
+ videoformat video_format;
+ struct v4l2_dv_timings dv_timings;
+ u32 fps;
+ u32 v_sync_pol;
+ u32 h_sync_pol;
+ u8 vic;
+ char *name;
+ bool edid_support_match;
+};
+
+#define V4L2_DV_BT_CVT_3840X2160P59_ADDED { \
+ .type = V4L2_DV_BT_656_1120, \
+ V4L2_INIT_BT_TIMINGS(3840, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
+ 533250000, 48, 32, 80, 3, 5, 54, 0, 0, 0, \
+ V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \
+ V4L2_DV_FL_REDUCED_BLANKING) \
+}
+
+#define V4L2_DV_BT_CVT_2560X1440P60_ADDED { \
+ .type = V4L2_DV_BT_656_1120, \
+ V4L2_INIT_BT_TIMINGS(2560, 1440, 0, V4L2_DV_HSYNC_POS_POL, \
+ 312250000, 192, 272, 464, 3, 5, 45, 0, 0, 0, \
+ V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \
+}
+
+#define V4L2_DV_BT_CVT_2560X1440P59_ADDED { \
+ .type = V4L2_DV_BT_656_1120, \
+ V4L2_INIT_BT_TIMINGS(2560, 1440, 0, V4L2_DV_HSYNC_POS_POL, \
+ 241500000, 48, 32, 80, 3, 5, 33, 0, 0, 0, \
+ V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \
+}
+
+#define V4L2_DV_BT_CVT_2048X1536P60_ADDED { \
+ .type = V4L2_DV_BT_656_1120, \
+ V4L2_INIT_BT_TIMINGS(2048, 1536, 0, V4L2_DV_HSYNC_POS_POL, \
+ 209250000, 48, 32, 80, 3, 4, 37, 0, 0, 0, \
+ V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \
+}
+
+#define V4L2_DV_BT_CVT_640x10P60_ADDED { \
+ .type = V4L2_DV_BT_656_1120, \
+ V4L2_INIT_BT_TIMINGS(640, 10, 0, V4L2_DV_HSYNC_POS_POL, \
+ 27000000, 16, 96, 48, 2, 2, 12, 0, 0, 0, \
+ V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \
+}
+
+extern const int supported_videos_pre_cnt;
+extern struct displayport_supported_preset supported_videos[];
+
+struct exynos_displayport_data {
+ enum {
+ EXYNOS_DISPLAYPORT_STATE_PRESET = 0,
+ EXYNOS_DISPLAYPORT_STATE_ENUM_PRESET,
+ EXYNOS_DISPLAYPORT_STATE_RECONNECTION,
+ EXYNOS_DISPLAYPORT_STATE_HDCP,
+ EXYNOS_DISPLAYPORT_STATE_AUDIO,
+ } state;
+ struct v4l2_dv_timings timings;
+ struct v4l2_enum_dv_timings etimings;
+ __u32 audio_info;
+ int hdcp;
+};
+
+struct displayport_audio_config_data {
+ u32 audio_enable;
+ u32 audio_channel_cnt;
+ enum audio_sampling_frequency audio_fs;
+ enum audio_bit_per_channel audio_bit;
+ enum audio_16bit_dma_mode audio_packed_mode;
+ enum audio_dma_word_length audio_word_length;
+};
+
+/* InfoFrame */
+#define INFOFRAME_PACKET_TYPE_AVI 0x82 /** Auxiliary Video information InfoFrame */
+#define INFOFRAME_PACKET_TYPE_AUDIO 0x84 /** Audio information InfoFrame */
+#define INFOFRAME_PACKET_TYPE_HDR 0x87 /** HDR Metadata InfoFrame */
+#define MAX_INFOFRAME_LENGTH 27
+#define INFOFRAME_REGISTER_SIZE 32
+#define INFOFRAME_DATA_SIZE 8
+#define DATA_NUM_PER_REG (INFOFRAME_REGISTER_SIZE / INFOFRAME_DATA_SIZE)
+
+#define AVI_INFOFRAME_VERSION 0x02
+#define AVI_INFOFRAME_LENGTH 0x0D
+#define ACTIVE_FORMAT_INFOMATION_PRESENT (1 << 4) /* No Active Format Infomation */
+#define ACITVE_PORTION_ASPECT_RATIO (0x8 << 0) /* Same as Picture Aspect Ratio */
+
+#define AUDIO_INFOFRAME_VERSION 0x01
+#define AUDIO_INFOFRAME_LENGTH 0x0A
+#define AUDIO_INFOFRAME_PCM (1 << 4)
+#define AUDIO_INFOFRAME_SF_BIT_POSITION 2
+
+#define HDR_INFOFRAME_VERSION 0x01
+#define HDR_INFOFRAME_LENGTH 26
+#define HDR_INFOFRAME_EOTF_BYTE_NUM 0
+#define HDR_INFOFRAME_SMPTE_ST_2084 2
+#define STATIC_MATADATA_TYPE_1 0
+#define HDR_INFOFRAME_METADATA_ID_BYTE_NUM 1
+#define HDR_INFOFRAME_DISP_PRI_X_0_LSB 2
+#define HDR_INFOFRAME_DISP_PRI_X_0_MSB 3
+#define HDR_INFOFRAME_DISP_PRI_Y_0_LSB 4
+#define HDR_INFOFRAME_DISP_PRI_Y_0_MSB 5
+#define HDR_INFOFRAME_DISP_PRI_X_1_LSB 6
+#define HDR_INFOFRAME_DISP_PRI_X_1_MSB 7
+#define HDR_INFOFRAME_DISP_PRI_Y_1_LSB 8
+#define HDR_INFOFRAME_DISP_PRI_Y_1_MSB 9
+#define HDR_INFOFRAME_DISP_PRI_X_2_LSB 10
+#define HDR_INFOFRAME_DISP_PRI_X_2_MSB 11
+#define HDR_INFOFRAME_DISP_PRI_Y_2_LSB 12
+#define HDR_INFOFRAME_DISP_PRI_Y_2_MSB 13
+#define HDR_INFOFRAME_WHITE_POINT_X_LSB 14
+#define HDR_INFOFRAME_WHITE_POINT_X_MSB 15
+#define HDR_INFOFRAME_WHITE_POINT_Y_LSB 16
+#define HDR_INFOFRAME_WHITE_POINT_Y_MSB 17
+#define HDR_INFOFRAME_MAX_LUMI_LSB 18
+#define HDR_INFOFRAME_MAX_LUMI_MSB 19
+#define HDR_INFOFRAME_MIN_LUMI_LSB 20
+#define HDR_INFOFRAME_MIN_LUMI_MSB 21
+#define HDR_INFOFRAME_MAX_LIGHT_LEVEL_LSB 22
+#define HDR_INFOFRAME_MAX_LIGHT_LEVEL_MSB 23
+#define HDR_INFOFRAME_MAX_AVERAGE_LEVEL_LSB 24
+#define HDR_INFOFRAME_MAX_AVERAGE_LEVEL_MSB 25
+#define LSB_MASK 0x00FF
+#define MSB_MASK 0xFF00
+#define SHIFT_8BIT 8
+
+struct infoframe {
+ u8 type_code;
+ u8 version_number;
+ u8 length;
+ u8 data[MAX_INFOFRAME_LENGTH];
+};
+
+typedef struct{
+ u8 HDCP13_BKSV[5];
+ u8 HDCP13_R0[2];
+ u8 HDCP13_AKSV[5];
+ u8 HDCP13_AN[8];
+ u8 HDCP13_V_H[20];
+ u8 HDCP13_BCAP[1];
+ u8 HDCP13_BSTATUS[1];
+ u8 HDCP13_BINFO[2];
+ u8 HDCP13_KSV_FIFO[15];
+ u8 HDCP13_AINFO[1];
+} HDCP13;
+
+enum HDCP13_STATE {
+ HDCP13_STATE_NOT_AUTHENTICATED,
+ HDCP13_STATE_AUTH_PROCESS,
+ HDCP13_STATE_SECOND_AUTH_DONE,
+ HDCP13_STATE_AUTHENTICATED,
+ HDCP13_STATE_FAIL
+};
+
+struct hdcp13_info {
+ u8 cp_irq_flag;
+ u8 is_repeater;
+ u8 device_cnt;
+ u8 revocation_check;
+ u8 r0_read_flag;
+ int link_check;
+ enum HDCP13_STATE auth_state;
+};
+
+/* HDCP 1.3 */
+extern HDCP13 HDCP13_DPCD;
+extern struct hdcp13_info hdcp13_info;
+
+#define ADDR_HDCP13_BKSV 0x68000
+#define ADDR_HDCP13_R0 0x68005
+#define ADDR_HDCP13_AKSV 0x68007
+#define ADDR_HDCP13_AN 0x6800C
+#define ADDR_HDCP13_V_H0 0x68014
+#define ADDR_HDCP13_V_H1 0x68018
+#define ADDR_HDCP13_V_H2 0x6801C
+#define ADDR_HDCP13_V_H3 0x68020
+#define ADDR_HDCP13_V_H4 0x68024
+#define ADDR_HDCP13_BCAP 0x68028
+#define ADDR_HDCP13_BSTATUS 0x68029
+#define ADDR_HDCP13_BINFO 0x6802A
+#define ADDR_HDCP13_KSV_FIFO 0x6802C
+#define ADDR_HDCP13_AINFO 0x6803B
+#define ADDR_HDCP13_RSVD 0x6803C
+#define ADDR_HDCP13_DBG 0x680C0
+
+#define BCAPS_RESERVED_BIT_MASK 0xFC
+#define BCAPS_REPEATER (1 << 1)
+#define BCAPS_HDCP_CAPABLE (1 << 0)
+
+#define BSTATUS_READY (1<<0)
+#define BSTATUS_R0_AVAILABLE (1<<1)
+#define BSTATUS_LINK_INTEGRITY_FAIL (1<<2)
+#define BSTATUS_REAUTH_REQ (1<<3)
+
+#define BINFO_DEVICE_COUNT (0x7F<<0)
+#define BINFO_MAX_DEVS_EXCEEDED (1<<7)
+#define BINFO_DEPTH (7<<8)
+#define BINFO_MAX_CASCADE_EXCEEDED (1<<11)
+
+#define RI_READ_RETRY_CNT 3
+#define RI_AVAILABLE_WAITING 2
+#define RI_DELAY 100
+#define RI_WAIT_COUNT (RI_DELAY / RI_AVAILABLE_WAITING)
+#define REPEATER_READY_MAX_WAIT_DELAY 5000
+#define REPEATER_READY_WAIT_COUNT (REPEATER_READY_MAX_WAIT_DELAY / RI_AVAILABLE_WAITING)
+#define HDCP_RETRY_COUNT 1
+#define KSV_SIZE 5
+#define KSV_FIFO_SIZE 15
+#define MAX_KSV_LIST_COUNT 127
+#define M0_SIZE 8
+#define SHA1_SIZE 20
+#define BINFO_SIZE 2
+#define V_READ_RETRY_CNT 3
+
+enum{
+ LINK_CHECK_PASS = 0,
+ LINK_CHECK_NEED = 1,
+ LINK_CHECK_FAIL = 2,
+};
+
+enum{
+ FIRST_AUTH = 0,
+ SECOND_AUTH = 1,
+};
+
+static inline struct displayport_device *get_displayport_drvdata(void)
+{
+ return displayport_drvdata;
+}
+
+/* register access subroutines */
+static inline u32 displayport_read(u32 reg_id)
+{
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ return readl(displayport->res.link_regs + reg_id);
+}
+
+static inline u32 displayport_read_mask(u32 reg_id, u32 mask)
+{
+ u32 val = displayport_read(reg_id);
+
+ val &= (mask);
+ return val;
+}
+
+static inline void displayport_write(u32 reg_id, u32 val)
+{
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ writel(val, displayport->res.link_regs + reg_id);
+}
+
+static inline void displayport_write_mask(u32 reg_id, u32 val, u32 mask)
+{
+ struct displayport_device *displayport = get_displayport_drvdata();
+ u32 old = displayport_read(reg_id);
+ u32 bit_shift;
+
+ for (bit_shift = 0; bit_shift < 32; bit_shift++) {
+ if ((mask >> bit_shift) & 0x00000001)
+ break;
+ }
+
+ val = ((val<<bit_shift) & mask) | (old & ~mask);
+ writel(val, displayport->res.link_regs + reg_id);
+}
+
+static inline u32 displayport_phy_read(u32 reg_id)
+{
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ return readl(displayport->res.phy_regs + reg_id);
+}
+
+static inline u32 displayport_phy_read_mask(u32 reg_id, u32 mask)
+{
+ u32 val = displayport_phy_read(reg_id);
+
+ val &= (mask);
+ return val;
+}
+
+static inline void displayport_phy_write(u32 reg_id, u32 val)
+{
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ writel(val, displayport->res.phy_regs + reg_id);
+}
+
+static inline void displayport_phy_write_mask(u32 reg_id, u32 val, u32 mask)
+{
+ struct displayport_device *displayport = get_displayport_drvdata();
+ u32 old = displayport_phy_read(reg_id);
+ u32 bit_shift;
+
+ for (bit_shift = 0; bit_shift < 32; bit_shift++) {
+ if ((mask >> bit_shift) & 0x00000001)
+ break;
+ }
+
+ val = ((val<<bit_shift) & mask) | (old & ~mask);
+ writel(val, displayport->res.phy_regs + reg_id);
+}
+
+void displayport_reg_init(void);
+void displayport_reg_set_interrupt_mask(enum displayport_interrupt_mask param, u8 set);
+u32 displayport_reg_get_interrupt_and_clear(u32 interrupt_status_register);
+void displayport_reg_start(void);
+void displayport_reg_video_mute(u32 en);
+void displayport_reg_stop(void);
+void displayport_reg_set_video_configuration(videoformat video_format, u8 bpc, u8 range);
+int displayport_reg_dpcd_write(u32 address, u32 length, u8 *data);
+int displayport_reg_dpcd_read(u32 address, u32 length, u8 *data);
+int displayport_reg_dpcd_write_burst(u32 address, u32 length, u8 *data);
+int displayport_reg_dpcd_read_burst(u32 address, u32 length, u8 *data);
+int displayport_reg_edid_write(u8 edid_addr_offset, u32 length, u8 *data);
+int displayport_reg_edid_read(u8 edid_addr_offset, u32 length, u8 *data);
+void displayport_reg_phy_reset(u32 en);
+void displayport_reg_phy_disable(void);
+void displayport_reg_phy_init_setting(void);
+void displayport_reg_phy_mode_setting(void);
+void displayport_reg_phy_ssc_enable(u32 en);
+void displayport_reg_set_link_bw(u8 link_rate);
+u32 displayport_reg_get_link_bw(void);
+void displayport_reg_set_lane_count(u8 lane_cnt);
+u32 displayport_reg_get_lane_count(void);
+void displayport_reg_wait_phy_pll_lock(void);
+void displayport_reg_set_training_pattern(displayport_training_pattern pattern);
+void displayport_reg_set_qual_pattern(displayport_qual_pattern pattern, displayport_scrambling scramble);
+void displayport_reg_set_hbr2_scrambler_reset(u32 uResetCount);
+void displayport_reg_set_pattern_PLTPAT(void);
+void displayport_reg_set_voltage_and_pre_emphasis(u8 *voltage, u8 *pre_emphasis);
+void displayport_reg_set_bist_video_configuration(videoformat video_format, u8 bpc, u8 type, u8 range);
+void displayport_reg_set_bist_video_configuration_for_blue_screen(videoformat video_format);
+void displayport_reg_set_video_bist_mode(u32 en);
+void displayport_reg_set_audio_bist_mode(u32 en);
+void displayport_reg_lh_p_ch_power(u32 en);
+
+void displayport_reg_set_audio_m_n(audio_sync_mode audio_sync_mode,
+ enum audio_sampling_frequency audio_sampling_freq);
+void displayport_reg_set_audio_function_enable(u32 en);
+void displayport_reg_set_dma_burst_size(enum audio_dma_word_length word_length);
+void displayport_reg_set_dma_pack_mode(enum audio_16bit_dma_mode dma_mode);
+void displayport_reg_set_pcm_size(enum audio_bit_per_channel audio_bit_size);
+void displayport_reg_set_audio_ch_status_same(u32 en);
+void displayport_reg_set_audio_ch(u32 audio_ch_cnt);
+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);
+void displayport_reg_set_audio_fifo_function_enable(u32 en);
+void displayport_reg_set_audio_sampling_frequency
+ (enum audio_sampling_frequency audio_sampling_freq);
+void displayport_reg_set_dp_audio_enable(u32 en);
+void displayport_reg_set_audio_master_mode_enable(u32 en);
+void displayport_reg_set_ch_status_ch_cnt(u32 audio_ch_cnt);
+void displayport_reg_set_ch_status_word_length(enum audio_bit_per_channel audio_bit_size);
+void displayport_reg_set_ch_status_sampling_frequency(enum audio_sampling_frequency audio_sampling_freq);
+void displayport_reg_set_ch_status_clock_accuracy(enum audio_clock_accuracy clock_accuracy);
+void displayport_reg_wait_buf_full(void);
+
+void displayport_reg_set_hdcp22_system_enable(u32 en);
+void displayport_reg_set_hdcp22_mode(u32 en);
+void displayport_reg_set_hdcp22_encryption_enable(u32 en);
+u32 displayport_reg_get_hdcp22_encryption_enable(void);
+void displayport_reg_set_aux_pn_inv(u32 val);
+void displayport_hpd_changed(int state);
+int displayport_get_hpd_state(void);
+bool is_displayport_not_running(void);
+
+int displayport_reg_stand_alone_crc_sorting(void);
+
+int edid_read(struct displayport_device *hdev, u8 **data);
+int edid_update(struct displayport_device *hdev);
+struct v4l2_dv_timings edid_preferred_preset(void);
+void edid_set_preferred_preset(int mode);
+int edid_find_resolution(u16 xres, u16 yres, u16 refresh);
+u8 edid_read_checksum(void);
+u32 edid_audio_informs(void);
+
+int displayport_audio_bist_enable(struct displayport_audio_config_data audio_config_data);
+void displayport_reg_set_avi_infoframe(struct infoframe avi_infofrmae);
+void displayport_reg_set_audio_infoframe(struct infoframe audio_infofrmae, u32 en);
+void displayport_reg_set_hdr_infoframe(struct infoframe hdr_infofrmae, u32 en);
+
+void hdcp13_run(void);
+void hdcp13_dpcd_buffer(void);
+u8 hdcp13_read_bcap(void);
+void hdcp13_link_integrity_check(void);
+
+extern int hdcp_calc_sha1(u8 *digest, const u8 *buf, unsigned int buflen);
+extern int hdcp_dplink_authenticate(void); /* hdcp 2.2 */
+extern int hdcp_dplink_get_rxstatus(uint8_t *status);
+extern int hdcp_dplink_set_paring_available(void);
+extern int hdcp_dplink_set_hprime_available(void);
+extern int hdcp_dplink_set_rp_ready(void);
+extern int hdcp_dplink_set_reauth(void);
+extern int hdcp_dplink_set_integrity_fail(void);
+extern int hdcp_dplink_hpd_changed(void);
+
+#define DISPLAYPORT_IOC_DUMP _IOW('V', 0, u32)
+#define DISPLAYPORT_IOC_GET_ENUM_DV_TIMINGS _IOW('V', 1, u8)
+#define DISPLAYPORT_IOC_SET_RECONNECTION _IOW('V', 2, u8)
+#define DISPLAYPORT_IOC_DP_SA_SORTING _IOW('V', 3, int)
+#define DISPLAYPORT_IOC_SET_HDR_METADATA _IOW('V', 4, struct exynos_hdr_static_info *)
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Samsung 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 <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_gpio.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <video/mipi_display.h>
+#include <linux/regulator/consumer.h>
+#include <media/v4l2-dv-timings.h>
+#if defined(CONFIG_CPU_IDLE)
+#include <soc/samsung/exynos-powermode.h>
+#endif
+#if defined(CONFIG_SND_SOC_SAMSUNG_DISPLAYPORT)
+#include <sound/samsung/dp_ado.h>
+#endif
+#include <linux/exynos_iovmm.h>
+
+#if defined(CONFIG_PHY_EXYNOS_USBDRD)
+#include "../../../drivers/phy/samsung/phy-exynos-usbdrd.h"
+#endif
+#include "displayport.h"
+#include "decon.h"
+
+#define PIXELCLK_2160P30HZ 297000000 /* UHD 30hz */
+#define PIXELCLK_1080P60HZ 148500000 /* FHD 60Hz */
+#define PIXELCLK_1080P30HZ 74250000 /* FHD 30Hz */
+
+#define HDCP_SUPPORT
+#define HDCP_2_2
+
+#define HDCP_2_2_AUTH_DONE 1
+#define HDCP_2_2_NOT_AUTH 0
+
+int displayport_log_level = 6;
+static u8 max_lane_cnt;
+static u8 max_link_rate;
+static u64 reduced_resolution;
+struct displayport_debug_param g_displayport_debug_param;
+int auth_done = HDCP_2_2_NOT_AUTH;
+
+struct displayport_device *displayport_drvdata;
+EXPORT_SYMBOL(displayport_drvdata);
+
+static int displayport_runtime_suspend(struct device *dev);
+static int displayport_runtime_resume(struct device *dev);
+
+void displayport_hdcp22_enable(u32 en);
+
+static void displayport_dump_registers(struct displayport_device *displayport)
+{
+ displayport_info("=== DisplayPort SFR DUMP ===\n");
+
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ displayport->res.link_regs, 0xC0, false);
+}
+
+static int displayport_remove(struct platform_device *pdev)
+{
+ struct displayport_device *displayport = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(&pdev->dev);
+#if defined(CONFIG_EXTCON)
+ devm_extcon_dev_unregister(displayport->dev, displayport->extcon_displayport);
+ //devm_extcon_dev_unregister(displayport->dev, displayport->audio_switch);
+#else
+ displayport_info("Not compiled EXTCON driver\n");
+#endif
+ mutex_destroy(&displayport->cmd_lock);
+ mutex_destroy(&displayport->hpd_lock);
+ mutex_destroy(&displayport->aux_lock);
+ mutex_destroy(&displayport->training_lock);
+ destroy_workqueue(displayport->dp_wq);
+ destroy_workqueue(displayport->hdcp2_wq);
+ displayport_info("displayport driver removed\n");
+
+ return 0;
+}
+
+static u64 displayport_find_edid_max_pixelclock(void)
+{
+ int i;
+
+ for (i = supported_videos_pre_cnt - 1; i > 0; i--) {
+ if (supported_videos[i].edid_support_match) {
+ displayport_info("max video_format : %s\n",
+ supported_videos[i].name);
+ break;
+ }
+ }
+
+ return supported_videos[i].dv_timings.bt.pixelclock;
+}
+
+static int displayport_check_edid_max_clock(struct displayport_device *displayport,
+ videoformat video_format)
+{
+ int ret_val = 0;
+
+ if (supported_videos[video_format].dv_timings.bt.pixelclock * 12 / 10 <
+ displayport->rx_edid_data.max_support_clk * 5 * MHZ) {
+ displayport_info("RX support Max TMDS Clock = %d Mhz\n",
+ displayport->rx_edid_data.max_support_clk * 5);
+ ret_val = -EINVAL;
+ }
+
+ return ret_val;
+}
+
+static int displayport_get_min_link_rate(u8 rx_link_rate, u8 lane_cnt)
+{
+ int i;
+ u64 pc1lane[] = {54000000, 90000000, 180000000};
+ int link_rate[] = {LINK_RATE_1_62Gbps, LINK_RATE_2_7Gbps, LINK_RATE_5_4Gbps};
+ u64 max_pclk;
+ u8 min_link_rate;
+
+ if (rx_link_rate == LINK_RATE_1_62Gbps)
+ return rx_link_rate;
+
+ if (lane_cnt > 4)
+ return LINK_RATE_5_4Gbps;
+
+ max_pclk = displayport_find_edid_max_pixelclock();
+ for (i = 0; i < 2; i++) {
+ if ((u64)lane_cnt * pc1lane[i] >= max_pclk)
+ break;
+ }
+
+ min_link_rate = link_rate[i] > rx_link_rate ? rx_link_rate : link_rate[i];
+ displayport_info("set link late: 0x%x, lane cnt:%d\n", min_link_rate, lane_cnt);
+
+ return min_link_rate;
+}
+
+void displayport_get_voltage_and_pre_emphasis_max_reach(u8 *drive_current, u8 *pre_emphasis, u8 *max_reach_value)
+{
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ if (drive_current[i] >= MAX_REACHED_CNT) {
+ max_reach_value[i] &= ~(1 << MAX_SWING_REACHED_BIT_POS);
+ max_reach_value[i] |= (1 << MAX_SWING_REACHED_BIT_POS);
+ } else
+ max_reach_value[i] &= ~(1 << MAX_SWING_REACHED_BIT_POS);
+
+ if (pre_emphasis[i] >= MAX_REACHED_CNT) {
+ max_reach_value[i] &= ~(1 << MAX_PRE_EMPHASIS_REACHED_BIT_POS);
+ max_reach_value[i] |= (1 << MAX_PRE_EMPHASIS_REACHED_BIT_POS);
+ } else
+ max_reach_value[i] &= ~(1 << MAX_PRE_EMPHASIS_REACHED_BIT_POS);
+ }
+}
+
+static int displayport_full_link_training(void)
+{
+ u8 link_rate;
+ u8 lane_cnt;
+ u8 training_aux_rd_interval;
+ u8 pre_emphasis[MAX_LANE_CNT];
+ u8 drive_current[MAX_LANE_CNT];
+ u8 voltage_swing_lane[MAX_LANE_CNT];
+ u8 pre_emphasis_lane[MAX_LANE_CNT];
+ u8 max_reach_value[MAX_LANE_CNT];
+ int training_retry_no, eq_training_retry_no, i;
+ u8 val[DPCD_BUF_SIZE] = {0,};
+ u8 eq_val[DPCD_BUF_SIZE] = {0,};
+ u8 lane_cr_done;
+ u8 lane_channel_eq_done;
+ u8 lane_symbol_locked_done;
+ u8 interlane_align_done;
+ u8 enhanced_frame_cap;
+ int ret = 0;
+ int tps3_supported = 0;
+ struct displayport_device *displayport = get_displayport_drvdata();
+ struct decon_device *decon = get_decon_drvdata(2);
+
+ displayport_reg_dpcd_read_burst(DPCD_ADD_REVISION_NUMBER, DPCD_BUF_SIZE, val);
+ displayport_info("Full Link Training Start + : %02x %02x\n", val[1], val[2]);
+
+ link_rate = val[1];
+ lane_cnt = val[2] & MAX_LANE_COUNT;
+ max_lane_cnt = lane_cnt;
+ tps3_supported = val[2] & TPS3_SUPPORTED;
+ enhanced_frame_cap = val[2] & ENHANCED_FRAME_CAP;
+
+ if (!displayport->auto_test_mode) {
+ link_rate = displayport_get_min_link_rate(link_rate, lane_cnt);
+ displayport->auto_test_mode = 0;
+ }
+
+ if (g_displayport_debug_param.param_used) {
+ link_rate = g_displayport_debug_param.link_rate;
+ lane_cnt = g_displayport_debug_param.lane_cnt;
+ }
+
+ displayport_reg_dpcd_read(DPCD_ADD_TRAINING_AUX_RD_INTERVAL, 1, val);
+ training_aux_rd_interval = val[0];
+
+Reduce_Link_Rate_Retry:
+ displayport_info("Reduce_Link_Rate_Retry\n");
+
+ for (i = 0; i < 4; i++) {
+ pre_emphasis[i] = 0;
+ drive_current[i] = 0;
+ max_reach_value[i] = 0;
+ }
+
+ training_retry_no = 0;
+
+ if (decon->state != DECON_STATE_ON
+ || displayport_reg_get_link_bw() != link_rate
+ || displayport_reg_get_lane_count() != lane_cnt) {
+ displayport_reg_phy_reset(1);
+ displayport_reg_phy_init_setting();
+ displayport_reg_phy_mode_setting();
+
+ displayport_reg_set_link_bw(link_rate);
+ displayport_info("link_rate = %x\n", link_rate);
+
+ displayport_reg_set_lane_count(lane_cnt);
+ displayport_info("lane_cnt = %x\n", lane_cnt);
+
+ if (enhanced_frame_cap)
+ displayport_write_mask(SST1_MAIN_CONTROL, 1, ENHANCED_MODE);
+
+ /* wait for 60us */
+ udelay(60);
+
+ displayport_reg_phy_reset(0);
+ } else
+ displayport_info("skip phy_reset in link training\n");
+
+ val[0] = link_rate;
+ val[1] = lane_cnt;
+
+ if (enhanced_frame_cap)
+ val[1] |= ENHANCED_FRAME_CAP;
+
+ displayport_reg_dpcd_write_burst(DPCD_ADD_LINK_BW_SET, 2, val);
+
+ displayport_reg_wait_phy_pll_lock();
+
+ displayport_reg_set_training_pattern(TRAINING_PATTERN_1);
+
+ val[0] = 0x21; /* SCRAMBLING_DISABLE, TRAINING_PATTERN_1 */
+ displayport_reg_dpcd_write(DPCD_ADD_TRANING_PATTERN_SET, 1, val);
+
+Voltage_Swing_Retry:
+ displayport_dbg("Voltage_Swing_Retry\n");
+
+ displayport_reg_set_voltage_and_pre_emphasis((u8 *)drive_current, (u8 *)pre_emphasis);
+ displayport_get_voltage_and_pre_emphasis_max_reach((u8 *)drive_current,
+ (u8 *)pre_emphasis, (u8 *)max_reach_value);
+
+ val[0] = (pre_emphasis[0]<<3) | drive_current[0] | max_reach_value[0];
+ val[1] = (pre_emphasis[1]<<3) | drive_current[1] | max_reach_value[1];
+ val[2] = (pre_emphasis[2]<<3) | drive_current[2] | max_reach_value[2];
+ val[3] = (pre_emphasis[3]<<3) | drive_current[3] | max_reach_value[3];
+ displayport_info("Voltage_Swing_Retry %02x %02x %02x %02x\n", val[0], val[1], val[2], val[3]);
+ displayport_reg_dpcd_write_burst(DPCD_ADD_TRANING_LANE0_SET, 4, val);
+
+ udelay((training_aux_rd_interval*4000)+400);
+
+ lane_cr_done = 0;
+
+ displayport_reg_dpcd_read(DPCD_ADD_LANE0_1_STATUS, 2, val);
+ lane_cr_done |= ((val[0] & LANE0_CR_DONE) >> 0);
+ lane_cr_done |= ((val[0] & LANE1_CR_DONE) >> 3);
+ lane_cr_done |= ((val[1] & LANE2_CR_DONE) << 2);
+ lane_cr_done |= ((val[1] & LANE3_CR_DONE) >> 1);
+
+ displayport_dbg("lane_cr_done = %x\n", lane_cr_done);
+
+ if (lane_cnt == 0x04) {
+ if (lane_cr_done == 0x0F) {
+ displayport_dbg("lane_cr_done\n");
+ goto EQ_Training_Start;
+ }
+ } else if (lane_cnt == 0x02) {
+ if (lane_cr_done == 0x03) {
+ displayport_dbg("lane_cr_done\n");
+ goto EQ_Training_Start;
+ }
+ } else if (lane_cnt == 0x01) {
+ if (lane_cr_done == 0x01) {
+ displayport_dbg("lane_cr_done\n");
+ goto EQ_Training_Start;
+ }
+ } else {
+ val[0] = 0x00; /* SCRAMBLING_ENABLE, NORMAL_DATA */
+ displayport_reg_dpcd_write(DPCD_ADD_TRANING_PATTERN_SET, 1, val);
+ displayport_err("Full Link Training Fail : Link Rate %02x, lane Count %02x -",
+ link_rate, lane_cnt);
+ return -EINVAL;
+ }
+
+ if (!(drive_current[0] == 3 && drive_current[1] == 3
+ && drive_current[2] == 3 && drive_current[3] == 3)) {
+ displayport_reg_dpcd_read_burst(DPCD_ADD_ADJUST_REQUEST_LANE0_1, 2, val);
+ voltage_swing_lane[0] = (val[0] & VOLTAGE_SWING_LANE0);
+ pre_emphasis_lane[0] = (val[0] & PRE_EMPHASIS_LANE0) >> 2;
+ voltage_swing_lane[1] = (val[0] & VOLTAGE_SWING_LANE1) >> 4;
+ pre_emphasis_lane[1] = (val[0] & PRE_EMPHASIS_LANE1) >> 6;
+
+ voltage_swing_lane[2] = (val[1] & VOLTAGE_SWING_LANE2);
+ pre_emphasis_lane[2] = (val[1] & PRE_EMPHASIS_LANE2) >> 2;
+ voltage_swing_lane[3] = (val[1] & VOLTAGE_SWING_LANE3) >> 4;
+ pre_emphasis_lane[3] = (val[1] & PRE_EMPHASIS_LANE3) >> 6;
+
+ if (drive_current[0] == voltage_swing_lane[0] &&
+ drive_current[1] == voltage_swing_lane[1] &&
+ drive_current[2] == voltage_swing_lane[2] &&
+ drive_current[3] == voltage_swing_lane[3]) {
+ if (training_retry_no == 4)
+ goto Check_Link_rate;
+ else
+ training_retry_no++;
+ } else
+ training_retry_no = 1;
+
+ for (i = 0; i < 4; i++) {
+ drive_current[i] = voltage_swing_lane[i];
+ pre_emphasis[i] = pre_emphasis_lane[i];
+ displayport_dbg("v drive_current[%d] = %x\n",
+ i, drive_current[i]);
+ displayport_dbg("v pre_emphasis[%d] = %x\n",
+ i, pre_emphasis[i]);
+ }
+
+ goto Voltage_Swing_Retry;
+ }
+
+Check_Link_rate:
+ displayport_info("Check_Link_rate\n");
+
+ if (link_rate == LINK_RATE_5_4Gbps) {
+ link_rate = LINK_RATE_2_7Gbps;
+ goto Reduce_Link_Rate_Retry;
+ } else if (link_rate == LINK_RATE_2_7Gbps) {
+ link_rate = LINK_RATE_1_62Gbps;
+ goto Reduce_Link_Rate_Retry;
+ } else if (link_rate == LINK_RATE_1_62Gbps) {
+ val[0] = 0x00; /* SCRAMBLING_ENABLE, NORMAL_DATA */
+ displayport_reg_dpcd_write(DPCD_ADD_TRANING_PATTERN_SET, 1, val);
+ displayport_err("Full Link Training Fail : Link_Rate Retry -");
+ return -EINVAL;
+ }
+
+EQ_Training_Start:
+ displayport_info("EQ_Training_Start\n");
+
+ eq_training_retry_no = 0;
+ for (i = 0; i < DPCD_BUF_SIZE; i++)
+ eq_val[i] = 0;
+
+ if (tps3_supported) {
+ displayport_reg_set_training_pattern(TRAINING_PATTERN_3);
+
+ val[0] = 0x23; /* SCRAMBLING_DISABLE, TRAINING_PATTERN_3 */
+ displayport_reg_dpcd_write(DPCD_ADD_TRANING_PATTERN_SET, 1, val);
+ } else {
+ displayport_reg_set_training_pattern(TRAINING_PATTERN_2);
+
+ val[0] = 0x22; /* SCRAMBLING_DISABLE, TRAINING_PATTERN_2 */
+ displayport_reg_dpcd_write(DPCD_ADD_TRANING_PATTERN_SET, 1, val);
+ }
+
+EQ_Training_Retry:
+ displayport_dbg("EQ_Training_Retry\n");
+
+ displayport_reg_set_voltage_and_pre_emphasis((u8 *)drive_current, (u8 *)pre_emphasis);
+ displayport_get_voltage_and_pre_emphasis_max_reach((u8 *)drive_current,
+ (u8 *)pre_emphasis, (u8 *)max_reach_value);
+
+ val[0] = (pre_emphasis[0]<<3) | drive_current[0] | max_reach_value[0];
+ val[1] = (pre_emphasis[1]<<3) | drive_current[1] | max_reach_value[1];
+ val[2] = (pre_emphasis[2]<<3) | drive_current[2] | max_reach_value[2];
+ val[3] = (pre_emphasis[3]<<3) | drive_current[3] | max_reach_value[3];
+ displayport_info("EQ_Training_Retry %02x %02x %02x %02x\n", val[0], val[1], val[2], val[3]);
+ displayport_reg_dpcd_write_burst(DPCD_ADD_TRANING_LANE0_SET, 4, val);
+
+ for (i = 0; i < 4; i++)
+ eq_val[i] = val[i];
+
+ lane_cr_done = 0;
+ lane_channel_eq_done = 0;
+ lane_symbol_locked_done = 0;
+ interlane_align_done = 0;
+
+ udelay((training_aux_rd_interval*4000)+400);
+
+ displayport_reg_dpcd_read_burst(DPCD_ADD_LANE0_1_STATUS, 3, val);
+ lane_cr_done |= ((val[0] & LANE0_CR_DONE) >> 0);
+ lane_cr_done |= ((val[0] & LANE1_CR_DONE) >> 3);
+ lane_channel_eq_done |= ((val[0] & LANE0_CHANNEL_EQ_DONE) >> 1);
+ lane_channel_eq_done |= ((val[0] & LANE1_CHANNEL_EQ_DONE) >> 4);
+ lane_symbol_locked_done |= ((val[0] & LANE0_SYMBOL_LOCKED) >> 2);
+ lane_symbol_locked_done |= ((val[0] & LANE1_SYMBOL_LOCKED) >> 5);
+
+ lane_cr_done |= ((val[1] & LANE2_CR_DONE) << 2);
+ lane_cr_done |= ((val[1] & LANE3_CR_DONE) >> 1);
+ lane_channel_eq_done |= ((val[1] & LANE2_CHANNEL_EQ_DONE) << 1);
+ lane_channel_eq_done |= ((val[1] & LANE3_CHANNEL_EQ_DONE) >> 2);
+ lane_symbol_locked_done |= ((val[1] & LANE2_SYMBOL_LOCKED) >> 0);
+ lane_symbol_locked_done |= ((val[1] & LANE3_SYMBOL_LOCKED) >> 3);
+
+ interlane_align_done |= (val[2] & INTERLANE_ALIGN_DONE);
+
+ if (lane_cnt == 0x04) {
+ if (lane_cr_done != 0x0F)
+ goto Check_Link_rate;
+ } else if (lane_cnt == 0x02) {
+ if (lane_cr_done != 0x03)
+ goto Check_Link_rate;
+ } else {
+ if (lane_cr_done != 0x01)
+ goto Check_Link_rate;
+ }
+
+ displayport_info("lane_cr_done = %x\n", lane_cr_done);
+ displayport_info("lane_channel_eq_done = %x\n", lane_channel_eq_done);
+ displayport_info("lane_symbol_locked_done = %x\n", lane_symbol_locked_done);
+ displayport_info("interlane_align_done = %x\n", interlane_align_done);
+
+ max_link_rate = link_rate;
+
+ if (lane_cnt == 0x04) {
+ if ((lane_channel_eq_done == 0x0F) && (lane_symbol_locked_done == 0x0F)
+ && (interlane_align_done == 1)) {
+ displayport_reg_set_training_pattern(NORAMAL_DATA);
+
+ val[0] = 0x00; /* SCRAMBLING_ENABLE, NORMAL_DATA */
+ displayport_reg_dpcd_write(DPCD_ADD_TRANING_PATTERN_SET, 1, val);
+
+ displayport_info("Full Link Training Finish - : %02x %02x\n", link_rate, lane_cnt);
+ displayport_info("LANE_SET [%d] : %02x %02x %02x %02x\n",
+ eq_training_retry_no, eq_val[0], eq_val[1], eq_val[2], eq_val[3]);
+ return ret;
+ }
+ } else if (lane_cnt == 0x02) {
+ if ((lane_channel_eq_done == 0x03) && (lane_symbol_locked_done == 0x03)
+ && (interlane_align_done == 1)) {
+ displayport_reg_set_training_pattern(NORAMAL_DATA);
+
+ val[0] = 0x00; /* SCRAMBLING_ENABLE, NORMAL_DATA */
+ displayport_reg_dpcd_write(DPCD_ADD_TRANING_PATTERN_SET, 1, val);
+
+ displayport_info("Full Link Training Finish - : %02x %02x\n", link_rate, lane_cnt);
+ displayport_info("LANE_SET [%d] : %02x %02x %02x %02x\n",
+ eq_training_retry_no, eq_val[0], eq_val[1], eq_val[2], eq_val[3]);
+ return ret;
+ }
+ } else {
+ if ((lane_channel_eq_done == 0x01) && (lane_symbol_locked_done == 0x01)
+ && (interlane_align_done == 1)) {
+ displayport_reg_set_training_pattern(NORAMAL_DATA);
+
+ val[0] = 0x00; /* SCRAMBLING_ENABLE, NORMAL_DATA */
+ displayport_reg_dpcd_write(DPCD_ADD_TRANING_PATTERN_SET, 1, val);
+
+ displayport_info("Full Link Training Finish - : %02x %02x\n", link_rate, lane_cnt);
+ displayport_info("LANE_SET [%d] : %02x %02x %02x %02x\n",
+ eq_training_retry_no, eq_val[0], eq_val[1], eq_val[2], eq_val[3]);
+ return ret;
+ }
+ }
+
+ if (training_retry_no == 4)
+ goto Check_Link_rate;
+
+ if (eq_training_retry_no >= 5) {
+ val[0] = 0x00; /* SCRAMBLING_ENABLE, NORMAL_DATA */
+ displayport_reg_dpcd_write(DPCD_ADD_TRANING_PATTERN_SET, 1, val);
+ displayport_err("Full Link Training Fail : EQ_training Retry -");
+ return -EINVAL;
+ }
+
+ displayport_reg_dpcd_read_burst(DPCD_ADD_ADJUST_REQUEST_LANE0_1, 2, val);
+ voltage_swing_lane[0] = (val[0] & VOLTAGE_SWING_LANE0);
+ pre_emphasis_lane[0] = (val[0] & PRE_EMPHASIS_LANE0) >> 2;
+ voltage_swing_lane[1] = (val[0] & VOLTAGE_SWING_LANE1) >> 4;
+ pre_emphasis_lane[1] = (val[0] & PRE_EMPHASIS_LANE1) >> 6;
+
+ voltage_swing_lane[2] = (val[1] & VOLTAGE_SWING_LANE2);
+ pre_emphasis_lane[2] = (val[1] & PRE_EMPHASIS_LANE2) >> 2;
+ voltage_swing_lane[3] = (val[1] & VOLTAGE_SWING_LANE3) >> 4;
+ pre_emphasis_lane[3] = (val[1] & PRE_EMPHASIS_LANE3) >> 6;
+
+ for (i = 0; i < 4; i++) {
+ drive_current[i] = voltage_swing_lane[i];
+ pre_emphasis[i] = pre_emphasis_lane[i];
+
+ displayport_dbg("eq drive_current[%d] = %x\n", i, drive_current[i]);
+ displayport_dbg("eq pre_emphasis[%d] = %x\n", i, pre_emphasis[i]);
+ }
+
+ eq_training_retry_no++;
+ goto EQ_Training_Retry;
+}
+
+static int displayport_fast_link_training(void)
+{
+ u8 link_rate;
+ u8 lane_cnt;
+ u8 pre_emphasis[4];
+ u8 drive_current[4];
+ u8 max_reach_value[4];
+ int i;
+ u8 val;
+ u8 lane_cr_done;
+ u8 lane_channel_eq_done;
+ u8 lane_symbol_locked_done;
+ u8 interlane_align_done;
+ int ret = 0;
+
+ displayport_info("Fast Link Training start +\n");
+
+ displayport_reg_dpcd_read(DPCD_ADD_MAX_LINK_RATE, 1, &val);
+ link_rate = val;
+
+ displayport_reg_dpcd_read(DPCD_ADD_MAX_LANE_COUNT, 1, &val);
+ lane_cnt = val & MAX_LANE_COUNT;
+ max_lane_cnt = lane_cnt;
+
+ if (g_displayport_debug_param.param_used) {
+ link_rate = g_displayport_debug_param.link_rate;
+ lane_cnt = g_displayport_debug_param.lane_cnt;
+ }
+
+ for (i = 0; i < 4; i++) {
+ pre_emphasis[i] = 1;
+ drive_current[i] = 2;
+ }
+
+ displayport_reg_phy_reset(1);
+ displayport_reg_phy_init_setting();
+ displayport_reg_phy_mode_setting();
+
+ displayport_reg_set_link_bw(link_rate);
+ displayport_info("link_rate = %x\n", link_rate);
+
+ displayport_reg_set_lane_count(lane_cnt);
+ displayport_info("lane_cnt = %x\n", lane_cnt);
+
+ /* wait for 60us */
+ udelay(60);
+ displayport_reg_phy_reset(0);
+
+ displayport_reg_dpcd_write(DPCD_ADD_LINK_BW_SET, 1, &link_rate);
+ displayport_reg_dpcd_write(DPCD_ADD_LANE_COUNT_SET, 1, &lane_cnt);
+
+ displayport_reg_set_voltage_and_pre_emphasis((u8 *)drive_current, (u8 *)pre_emphasis);
+ displayport_get_voltage_and_pre_emphasis_max_reach((u8 *)drive_current,
+ (u8 *)pre_emphasis, (u8 *)max_reach_value);
+
+ val = (pre_emphasis[0]<<3) | drive_current[0] | max_reach_value[0];
+ displayport_reg_dpcd_write(DPCD_ADD_TRANING_LANE0_SET, 1, &val);
+ val = (pre_emphasis[1]<<3) | drive_current[1] | max_reach_value[1];
+ displayport_reg_dpcd_write(DPCD_ADD_TRANING_LANE1_SET, 1, &val);
+ val = (pre_emphasis[2]<<3) | drive_current[2] | max_reach_value[2];
+ displayport_reg_dpcd_write(DPCD_ADD_TRANING_LANE2_SET, 1, &val);
+ val = (pre_emphasis[3]<<3) | drive_current[3] | max_reach_value[3];
+ displayport_reg_dpcd_write(DPCD_ADD_TRANING_LANE3_SET, 1, &val);
+
+ displayport_reg_wait_phy_pll_lock();
+
+ displayport_reg_set_training_pattern(TRAINING_PATTERN_1);
+
+ udelay(500);
+
+ lane_cr_done = 0;
+
+ displayport_reg_dpcd_read(DPCD_ADD_LANE0_1_STATUS, 1, &val);
+ lane_cr_done |= ((val & LANE0_CR_DONE) >> 0);
+ lane_cr_done |= ((val & LANE1_CR_DONE) >> 3);
+
+ displayport_reg_dpcd_read(DPCD_ADD_LANE2_3_STATUS, 1, &val);
+ lane_cr_done |= ((val & LANE2_CR_DONE) << 2);
+ lane_cr_done |= ((val & LANE3_CR_DONE) >> 1);
+
+ displayport_dbg("lane_cr_done = %x\n", lane_cr_done);
+
+ if (lane_cnt == 0x04) {
+ if (lane_cr_done != 0x0F) {
+ displayport_err("Fast Link Training Fail : lane_cnt %d -", lane_cnt);
+ return -EINVAL;
+ }
+ } else if (lane_cnt == 0x02) {
+ if (lane_cr_done != 0x03) {
+ displayport_err("Fast Link Training Fail : lane_cnt %d -", lane_cnt);
+ return -EINVAL;
+ }
+ } else {
+ if (lane_cr_done != 0x01) {
+ displayport_err("Fast Link Training Fail : lane_cnt %d -", lane_cnt);
+ return -EINVAL;
+ }
+ }
+
+ displayport_reg_set_training_pattern(TRAINING_PATTERN_2);
+
+ udelay(500);
+
+ lane_cr_done = 0;
+ lane_channel_eq_done = 0;
+ lane_symbol_locked_done = 0;
+ interlane_align_done = 0;
+
+ displayport_reg_dpcd_read(DPCD_ADD_LANE0_1_STATUS, 1, &val);
+ lane_cr_done |= ((val & LANE0_CR_DONE) >> 0);
+ lane_cr_done |= ((val & LANE1_CR_DONE) >> 3);
+ lane_channel_eq_done |= ((val & LANE0_CHANNEL_EQ_DONE) >> 1);
+ lane_channel_eq_done |= ((val & LANE1_CHANNEL_EQ_DONE) >> 4);
+ lane_symbol_locked_done |= ((val & LANE0_SYMBOL_LOCKED) >> 2);
+ lane_symbol_locked_done |= ((val & LANE1_SYMBOL_LOCKED) >> 5);
+
+ displayport_reg_dpcd_read(DPCD_ADD_LANE2_3_STATUS, 1, &val);
+ lane_cr_done |= ((val & LANE2_CR_DONE) << 2);
+ lane_cr_done |= ((val & LANE3_CR_DONE) >> 1);
+ lane_channel_eq_done |= ((val & LANE2_CHANNEL_EQ_DONE) << 1);
+ lane_channel_eq_done |= ((val & LANE3_CHANNEL_EQ_DONE) >> 2);
+ lane_symbol_locked_done |= ((val & LANE2_SYMBOL_LOCKED) >> 0);
+ lane_symbol_locked_done |= ((val & LANE3_SYMBOL_LOCKED) >> 3);
+
+ displayport_reg_dpcd_read(DPCD_ADD_LANE_ALIGN_STATUS_UPDATE, 1, &val);
+ interlane_align_done |= (val & INTERLANE_ALIGN_DONE);
+
+ if (lane_cnt == 0x04) {
+ if (lane_cr_done != 0x0F) {
+ displayport_err("Fast Link Training Fail : lane_cnt %d -", lane_cnt);
+ return -EINVAL;
+ }
+ } else if (lane_cnt == 0x02) {
+ if (lane_cr_done != 0x03) {
+ displayport_err("Fast Link Training Fail : lane_cnt %d -", lane_cnt);
+ return -EINVAL;
+ }
+ } else {
+ if (lane_cr_done != 0x01)
+ displayport_err("Fast Link Training Fail : lane_cnt %d -", lane_cnt);
+ return -EINVAL;
+ }
+
+ displayport_info("lane_cr_done = %x\n", lane_cr_done);
+ displayport_info("lane_channel_eq_done = %x\n", lane_channel_eq_done);
+ displayport_info("lane_symbol_locked_done = %x\n", lane_symbol_locked_done);
+ displayport_info("interlane_align_done = %x\n", interlane_align_done);
+
+ max_link_rate = link_rate;
+
+ if (lane_cnt == 0x04) {
+ if ((lane_channel_eq_done == 0x0F) && (lane_symbol_locked_done == 0x0F)
+ && (interlane_align_done == 1)) {
+ displayport_reg_set_training_pattern(NORAMAL_DATA);
+
+ val = 0x00; /* SCRAMBLING_ENABLE, NORMAL_DATA */
+ displayport_reg_dpcd_write(DPCD_ADD_TRANING_PATTERN_SET, 1, &val);
+
+ displayport_info("Fast Link Training Finish -\n");
+ return ret;
+ }
+ } else if (lane_cnt == 0x02) {
+ if ((lane_channel_eq_done == 0x03) && (lane_symbol_locked_done == 0x03)
+ && (interlane_align_done == 1)) {
+ displayport_reg_set_training_pattern(NORAMAL_DATA);
+
+ val = 0x00; /* SCRAMBLING_ENABLE, NORMAL_DATA */
+ displayport_reg_dpcd_write(DPCD_ADD_TRANING_PATTERN_SET, 1, &val);
+
+ displayport_info("Fast Link Training Finish -\n");
+ return ret;
+ }
+ } else {
+ if ((lane_channel_eq_done == 0x01) && (lane_symbol_locked_done == 0x01)
+ && (interlane_align_done == 1)) {
+ displayport_reg_set_training_pattern(NORAMAL_DATA);
+
+ val = 0x00; /* SCRAMBLING_ENABLE, NORMAL_DATA */
+ displayport_reg_dpcd_write(DPCD_ADD_TRANING_PATTERN_SET, 1, &val);
+
+ displayport_info("Fast Link Training Finish -\n");
+ return ret;
+ }
+ }
+
+ displayport_reg_set_training_pattern(NORAMAL_DATA);
+
+ val = 0x00; /* SCRAMBLING_ENABLE, NORMAL_DATA */
+ displayport_reg_dpcd_write(DPCD_ADD_TRANING_PATTERN_SET, 1, &val);
+
+ displayport_err("Fast Link Training Fail -");
+ return -EINVAL;
+}
+
+static int displayport_check_dfp_type(void)
+{
+ u8 val = 0;
+ int port_type = 0;
+ char *dfp[] = {"Displayport", "VGA", "HDMI", "Others"};
+
+ displayport_reg_dpcd_read(DPCD_ADD_DOWN_STREAM_PORT_PRESENT, 1, &val);
+ port_type = (val & BIT_DFP_TYPE) >> 1;
+ displayport_info("DFP type: %s(0x%X)\n", dfp[port_type], val);
+
+ return port_type;
+}
+
+static void displayport_link_sink_status_read(void)
+{
+ u8 val[DPCP_LINK_SINK_STATUS_FIELD_LENGTH] = {0, };
+
+ displayport_reg_dpcd_read_burst(DPCD_ADD_SINK_COUNT,
+ DPCP_LINK_SINK_STATUS_FIELD_LENGTH, val);
+ displayport_info("Read link status %02x %02x %02x %02x %02x %02x\n",
+ val[0], val[1], val[2], val[3], val[4], val[5]);
+
+ displayport_reg_dpcd_read_burst(DPCD_ADD_LINK_BW_SET, 2, val);
+ displayport_info("Read link rate %02x, count %02x\n", val[0], val[1]);
+}
+
+static int displayport_read_branch_revision(struct displayport_device *displayport)
+{
+ int ret = 0;
+ u8 val[4] = {0, };
+
+ ret = displayport_reg_dpcd_read_burst(DPCD_BRANCH_HW_REVISION, 3, val);
+ if (!ret) {
+ displayport_info("Branch revision: HW(0x%X), SW(0x%X, 0x%X)\n",
+ val[0], val[1], val[2]);
+ }
+
+ return ret;
+}
+
+static int displayport_link_status_read(void)
+{
+ u8 val[DPCP_LINK_SINK_STATUS_FIELD_LENGTH] = {0, };
+ int count = 200;
+
+ /* for Link CTS : Branch Device Detection*/
+ displayport_link_sink_status_read();
+ do {
+ displayport_reg_dpcd_read(DPCD_ADD_SINK_COUNT, 1, val);
+ if ((val[0] & (SINK_COUNT1 | SINK_COUNT2)) != 0)
+ break;
+ msleep(20);
+ } while (--count > 0);
+
+ displayport_reg_dpcd_read_burst(DPCD_ADD_DEVICE_SERVICE_IRQ_VECTOR,
+ DPCP_LINK_SINK_STATUS_FIELD_LENGTH - 1, &val[1]);
+
+ displayport_info("Read link status %02x %02x %02x %02x %02x %02x\n",
+ val[0], val[1], val[2], val[3], val[4], val[5]);
+
+ if ((val[0] & val[1] & val[2] & val[3] & val[4] & val[5]) == 0xff)
+ return -EINVAL;
+
+ if (count == 0)
+ return -EINVAL;
+
+ if (count < 10 && count > 0)
+ usleep_range(10000, 11000); /* need delay after SINK count is changed to 1 */
+
+ if (val[1] == AUTOMATED_TEST_REQUEST) {
+ u8 data = 0;
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ displayport_reg_dpcd_read(DPCD_TEST_REQUEST, 1, &data);
+
+ if ((data & TEST_EDID_READ) == TEST_EDID_READ) {
+ val[0] = edid_read_checksum();
+ displayport_info("TEST_EDID_CHECKSUM %02x\n", val[0]);
+
+ displayport_reg_dpcd_write(DPCD_TEST_EDID_CHECKSUM, 1, val);
+
+ val[0] = 0x04; /*TEST_EDID_CHECKSUM_WRITE*/
+ displayport_reg_dpcd_write(DPCD_TEST_RESPONSE, 1, val);
+
+ displayport->bist_used = 0;
+ }
+ }
+
+ return 0;
+}
+
+static int displayport_link_training(void)
+{
+ u8 val;
+ struct displayport_device *displayport = get_displayport_drvdata();
+ int ret = 0;
+
+ mutex_lock(&displayport->training_lock);
+
+ ret = edid_update(displayport);
+ if (ret < 0)
+ displayport_err("failed to update edid\n");
+
+ displayport_reg_dpcd_read(DPCD_ADD_MAX_DOWNSPREAD, 1, &val);
+ displayport_dbg("DPCD_ADD_MAX_DOWNSPREAD = %x\n", val);
+
+ if (val & NO_AUX_HANDSHAKE_LINK_TRANING) {
+ ret = displayport_fast_link_training();
+ if (ret < 0)
+ ret = displayport_full_link_training();
+ } else
+ ret = displayport_full_link_training();
+
+ mutex_unlock(&displayport->training_lock);
+
+ return ret;
+}
+
+static void displayport_set_switch_state(struct displayport_device *displayport, int state)
+{
+#if defined(CONFIG_EXTCON)
+ if (state) {
+ extcon_set_state_sync(displayport->extcon_displayport, EXTCON_DISP_DP, 1);
+#if defined(CONFIG_SND_SOC_SAMSUNG_DISPLAYPORT)
+ dp_ado_switch_set_state(edid_audio_informs());
+#endif
+ } else {
+#if defined(CONFIG_SND_SOC_SAMSUNG_DISPLAYPORT)
+ dp_ado_switch_set_state(-1);
+#endif
+ extcon_set_state_sync(displayport->extcon_displayport, EXTCON_DISP_DP, 0);
+ }
+#else
+ displayport_info("Not compiled EXTCON driver\n");
+#endif
+
+ displayport_info("HPD status = %d\n", state);
+}
+
+void displayport_audio_init_config(void)
+{
+ displayport_reg_set_audio_m_n(ASYNC_MODE, FS_48KHZ);
+ displayport_reg_set_audio_function_enable(1);
+ displayport_reg_set_audio_sampling_frequency(FS_48KHZ);
+ displayport_reg_set_dp_audio_enable(1);
+ displayport_reg_set_audio_master_mode_enable(1);
+ displayport_info("displayport_audio_init_config\n");
+}
+
+void displayport_hpd_changed(int state)
+{
+ int ret;
+ int timeout = 0;
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ mutex_lock(&displayport->hpd_lock);
+ if (displayport->hpd_current_state == state) {
+ displayport_info("hpd same state skip %x\n", state);
+ mutex_unlock(&displayport->hpd_lock);
+ return;
+ }
+
+ displayport_info("displayport hpd changed %d\n", state);
+ displayport->hpd_current_state = state;
+
+ if (state) {
+ pm_stay_awake(displayport->dev);
+
+ displayport->bpc = BPC_8; /*default setting*/
+ displayport->bist_used = 0;
+ displayport->bist_type = COLOR_BAR;
+ displayport->dyn_range = VESA_RANGE;
+ displayport->hpd_state = HPD_PLUG;
+ displayport->auto_test_mode = 0;
+ /* PHY power on */
+ displayport_reg_init(); /* for AUX ch read/write. */
+ usleep_range(10000, 11000);
+
+ /* for Link CTS : (4.2.2.3) EDID Read */
+ if (displayport_link_status_read()) {
+ displayport_err("link_status_read fail\n");
+ goto HPD_FAIL;
+ }
+
+ if (displayport_read_branch_revision(displayport))
+ displayport_err("branch_revision_read fail\n");
+
+ displayport_info("link training in hpd_changed\n");
+ ret = displayport_link_training();
+ if (ret < 0) {
+ displayport_dbg("link training fail\n");
+ goto HPD_FAIL;
+ }
+
+ displayport_audio_init_config();
+
+ displayport->dfp_type = displayport_check_dfp_type();
+ /* Enable it! if you want to prevent output according to type
+ * if (displayport->dfp_type != DFP_TYPE_DP &&
+ * displayport->dfp_type != DFP_TYPE_HDMI) {
+ * displayport_err("not supported DFT type\n");
+ * goto HPD_FAIL;
+ * }
+ */
+ displayport_set_switch_state(displayport, 1);
+ timeout = wait_event_interruptible_timeout(displayport->dp_wait,
+ (displayport->state == DISPLAYPORT_STATE_ON), msecs_to_jiffies(1000));
+ if (!timeout)
+ displayport_err("enable timeout\n");
+ } else {
+#if defined(CONFIG_EXYNOS_HDCP2)
+ if (displayport->hdcp_ver == HDCP_VERSION_2_2) {
+
+ hdcp_dplink_hpd_changed();
+ displayport_hdcp22_enable(0);
+ }
+#endif
+ cancel_delayed_work_sync(&displayport->hpd_plug_work);
+ cancel_delayed_work_sync(&displayport->hpd_unplug_work);
+ cancel_delayed_work_sync(&displayport->hpd_irq_work);
+ cancel_delayed_work_sync(&displayport->hdcp13_work);
+ cancel_delayed_work_sync(&displayport->hdcp22_work);
+ cancel_delayed_work_sync(&displayport->hdcp13_integrity_check_work);
+ displayport->hpd_state = HPD_UNPLUG;
+ displayport->cur_video = V640X480P60;
+
+ pm_relax(displayport->dev);
+
+ displayport_set_switch_state(displayport, 0);
+ timeout = wait_event_interruptible_timeout(displayport->dp_wait,
+ (displayport->state == DISPLAYPORT_STATE_INIT), msecs_to_jiffies(1000));
+ if (!timeout)
+ displayport_err("disable timeout\n");
+ displayport->hdcp_ver = 0;
+ }
+ mutex_unlock(&displayport->hpd_lock);
+
+ return;
+HPD_FAIL:
+ displayport_reg_phy_disable();
+ pm_relax(displayport->dev);
+ displayport->hpd_current_state = 0;
+ displayport->hpd_state = HPD_UNPLUG;
+ mutex_unlock(&displayport->hpd_lock);
+
+ return;
+}
+
+void displayport_hpd_unplug(void)
+{
+ int timeout = 0;
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ displayport->hpd_state = HPD_UNPLUG;
+ displayport->hpd_current_state = HPD_UNPLUG;
+ displayport->cur_video = V640X480P60;
+
+ displayport_set_switch_state(displayport, 0);
+ timeout = wait_event_interruptible_timeout(displayport->dp_wait,
+ (displayport->state == DISPLAYPORT_STATE_INIT), msecs_to_jiffies(1000));
+ if (!timeout)
+ displayport_err("disable timeout\n");
+ displayport->hdcp_ver = 0;
+}
+
+void displayport_set_reconnection(void)
+{
+ int ret;
+
+ /* PHY power on */
+ displayport_reg_init(); /* for AUX ch read/write. */
+
+ displayport_info("link training in reconnection\n");
+ ret = displayport_link_training();
+ if (ret < 0) {
+ displayport_dbg("link training fail\n");
+ return;
+ }
+}
+
+int displayport_get_hpd_state(void)
+{
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ return displayport->hpd_current_state;
+}
+
+static void displayport_hpd_plug_work(struct work_struct *work)
+{
+ displayport_hpd_changed(1);
+}
+
+static void displayport_hpd_unplug_work(struct work_struct *work)
+{
+ displayport_hpd_unplug();
+}
+
+static int displayport_check_dpcd_lane_status(u8 lane0_1_status,
+ u8 lane2_3_status, u8 lane_align_status)
+{
+ u8 val[2] = {0,};
+ u32 link_rate = displayport_reg_get_link_bw();
+ u32 lane_cnt = displayport_reg_get_lane_count();
+
+ displayport_reg_dpcd_read(DPCD_ADD_LINK_BW_SET, 2, val);
+
+ displayport_info("check lane %02x %02x %02x %02x\n", link_rate, lane_cnt,
+ val[0], (val[1] & MAX_LANE_COUNT));
+
+ if ((link_rate != val[0]) || (lane_cnt != (val[1] & MAX_LANE_COUNT))) {
+ displayport_err("%s(%d)\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ if ((lane_align_status & INTERLANE_ALIGN_DONE) != INTERLANE_ALIGN_DONE) {
+ displayport_err("%s(%d)\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ if ((lane_align_status & LINK_STATUS_UPDATE) == LINK_STATUS_UPDATE) {
+ displayport_err("%s(%d)\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ if (lane_cnt >= 1) {
+ if ((lane0_1_status & (LANE0_CR_DONE | LANE0_CHANNEL_EQ_DONE | LANE0_SYMBOL_LOCKED))
+ != (LANE0_CR_DONE | LANE0_CHANNEL_EQ_DONE | LANE0_SYMBOL_LOCKED)) {
+ displayport_err("%s(%d)\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ }
+
+ if (lane_cnt >= 2) {
+ if ((lane0_1_status & (LANE1_CR_DONE | LANE1_CHANNEL_EQ_DONE | LANE1_SYMBOL_LOCKED))
+ != (LANE1_CR_DONE | LANE1_CHANNEL_EQ_DONE | LANE1_SYMBOL_LOCKED)) {
+ displayport_err("%s(%d)\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ }
+
+ if (lane_cnt == 4) {
+ if ((lane2_3_status & (LANE2_CR_DONE | LANE2_CHANNEL_EQ_DONE | LANE2_SYMBOL_LOCKED))
+ != (LANE2_CR_DONE | LANE2_CHANNEL_EQ_DONE | LANE2_SYMBOL_LOCKED)) {
+ displayport_err("%s(%d)\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ if ((lane2_3_status & (LANE3_CR_DONE | LANE3_CHANNEL_EQ_DONE | LANE3_SYMBOL_LOCKED))
+ != (LANE3_CR_DONE | LANE3_CHANNEL_EQ_DONE | LANE3_SYMBOL_LOCKED)) {
+ displayport_err("%s(%d)\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int displayport_automated_test_set_lane_req(u8 *val)
+{
+ u8 drive_current[MAX_LANE_CNT];
+ u8 pre_emphasis[MAX_LANE_CNT];
+ u8 voltage_swing_lane[MAX_LANE_CNT];
+ u8 pre_emphasis_lane[MAX_LANE_CNT];
+ u8 max_reach_value[MAX_LANE_CNT];
+ u8 val2[MAX_LANE_CNT];
+ int i;
+
+ voltage_swing_lane[0] = (val[0] & VOLTAGE_SWING_LANE0);
+ pre_emphasis_lane[0] = (val[0] & PRE_EMPHASIS_LANE0) >> 2;
+ voltage_swing_lane[1] = (val[0] & VOLTAGE_SWING_LANE1) >> 4;
+ pre_emphasis_lane[1] = (val[0] & PRE_EMPHASIS_LANE1) >> 6;
+
+ voltage_swing_lane[2] = (val[1] & VOLTAGE_SWING_LANE2);
+ pre_emphasis_lane[2] = (val[1] & PRE_EMPHASIS_LANE2) >> 2;
+ voltage_swing_lane[3] = (val[1] & VOLTAGE_SWING_LANE3) >> 4;
+ pre_emphasis_lane[3] = (val[1] & PRE_EMPHASIS_LANE3) >> 6;
+
+ for (i = 0; i < 4; i++) {
+ drive_current[i] = voltage_swing_lane[i];
+ pre_emphasis[i] = pre_emphasis_lane[i];
+
+ displayport_info("AutoTest: swing[%d] = %x\n", i, drive_current[i]);
+ displayport_info("AutoTest: pre_emphasis[%d] = %x\n", i, pre_emphasis[i]);
+ }
+ displayport_reg_set_voltage_and_pre_emphasis((u8 *)drive_current, (u8 *)pre_emphasis);
+ displayport_get_voltage_and_pre_emphasis_max_reach((u8 *)drive_current,
+ (u8 *)pre_emphasis, (u8 *)max_reach_value);
+
+ val2[0] = (pre_emphasis[0]<<3) | drive_current[0] | max_reach_value[0];
+ val2[1] = (pre_emphasis[1]<<3) | drive_current[1] | max_reach_value[1];
+ val2[2] = (pre_emphasis[2]<<3) | drive_current[2] | max_reach_value[2];
+ val2[3] = (pre_emphasis[3]<<3) | drive_current[3] | max_reach_value[3];
+ displayport_info("AutoTest: set %02x %02x %02x %02x\n", val2[0], val2[1], val2[2], val2[3]);
+ displayport_reg_dpcd_write_burst(DPCD_ADD_TRANING_LANE0_SET, 4, val2);
+
+ return 0;
+}
+
+static int displayport_Automated_Test_Request(void)
+{
+ u8 data = 0;
+ u8 val[DPCP_LINK_SINK_STATUS_FIELD_LENGTH] = {0, };
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ displayport_reg_dpcd_read(DPCD_TEST_REQUEST, 1, &data);
+ displayport_info("TEST_REQUEST %02x\n", data);
+
+ displayport->auto_test_mode = 1;
+ val[0] = 0x01; /*TEST_ACK*/
+ displayport_reg_dpcd_write(DPCD_TEST_RESPONSE, 1, val);
+
+ if ((data & TEST_LINK_TRAINING) == TEST_LINK_TRAINING) {
+ displayport_reg_dpcd_read(DPCD_TEST_LINK_RATE, 1, val);
+ displayport_info("TEST_LINK_RATE %02x\n", val[0]);
+ g_displayport_debug_param.link_rate = (val[0]&TEST_LINK_RATE);
+
+ displayport_reg_dpcd_read(DPCD_TEST_LANE_COUNT, 1, val);
+ displayport_info("TEST_LANE_COUNT %02x\n", val[0]);
+ g_displayport_debug_param.lane_cnt = (val[0]&TEST_LANE_COUNT);
+
+ g_displayport_debug_param.param_used = 1;
+
+ displayport_reg_init();
+ displayport_link_training();
+
+ g_displayport_debug_param.param_used = 0;
+ } else if ((data & TEST_VIDEO_PATTERN) == TEST_VIDEO_PATTERN) {
+ u16 hactive, vactive, fps, vmode;
+ int edid_preset;
+
+ displayport_set_switch_state(displayport, 0);
+
+ msleep(300);
+
+ /* PHY power on */
+ displayport_reg_init(); /* for AUX ch read/write. */
+
+ g_displayport_debug_param.param_used = 1;
+
+ displayport_link_training();
+
+ g_displayport_debug_param.param_used = 0;
+
+ displayport_reg_dpcd_read(DPCD_TEST_PATTERN, 1, val);
+ displayport_info("TEST_PATTERN %02x\n", val[0]);
+
+ displayport_reg_dpcd_read(DPCD_TEST_H_WIDTH_1, 2, val);
+ hactive = val[0];
+ hactive = (hactive<<8)|val[1];
+ displayport_info("TEST_H_WIDTH %d\n", hactive);
+
+ displayport_reg_dpcd_read(DPCD_TEST_V_HEIGHT_1, 2, val);
+ vactive = val[0];
+ vactive = (vactive<<8)|val[1];
+ displayport_info("TEST_V_HEIGHT %d\n", vactive);
+
+ displayport_reg_dpcd_read(DPCD_TEST_MISC_1, 1, val);
+ displayport_info("TEST_MISC_1 %02x", val[0]);
+ displayport->dyn_range = (val[0] & TEST_DYNAMIC_RANGE)?CEA_RANGE:VESA_RANGE;
+ displayport->bpc = (val[0] & TEST_BIT_DEPTH)?BPC_8:BPC_6;
+
+ displayport_reg_dpcd_read(DPCD_TEST_MISC_2, 1, val);
+ displayport_info("TEST_MISC_2 %02x\n", val[0]);
+ vmode = (val[0]&TEST_INTERLACED);
+
+ displayport_reg_dpcd_read(DPCD_TEST_REFRESH_RATE_NUMERATOR, 1, val);
+ fps = val[0];
+ displayport_info("TEST_REFRESH_RATE_NUMERATOR %02x", fps);
+ displayport_info("%d %d %d %d dyn:%d %dbpc\n", hactive, vactive, fps, vmode,
+ displayport->dyn_range, (displayport->bpc == BPC_6)?6:8);
+
+ displayport->bist_used = 1;
+ displayport->bist_type = COLOR_BAR;
+
+ edid_preset = edid_find_resolution(hactive, vactive, fps);
+ edid_set_preferred_preset(edid_preset);
+
+ displayport_set_switch_state(displayport, 1);
+
+ } else if ((data & TEST_PHY_TEST_PATTERN) == TEST_PHY_TEST_PATTERN) {
+ displayport_reg_stop();
+ msleep(120);
+ displayport_reg_dpcd_read(DPCD_ADD_ADJUST_REQUEST_LANE0_1, 2, val);
+ displayport_info("ADJUST_REQUEST_LANE0_1 %02x %02x\n", val[0], val[1]);
+
+ /*set swing, preemp*/
+ displayport_automated_test_set_lane_req(val);
+
+ displayport_reg_dpcd_read(DCDP_ADD_PHY_TEST_PATTERN, 4, val);
+ displayport_info("PHY_TEST_PATTERN %02x %02x %02x %02x\n", val[0], val[1], val[2], val[3]);
+
+ switch (val[0]) {
+ case DISABLE_PATTEN:
+ displayport_reg_set_qual_pattern(DISABLE_PATTEN, ENABLE_SCRAM);
+ break;
+ case D10_2_PATTERN:
+ displayport_reg_set_qual_pattern(D10_2_PATTERN, DISABLE_SCRAM);
+ break;
+ case SERP_PATTERN:
+ displayport_reg_set_qual_pattern(SERP_PATTERN, ENABLE_SCRAM);
+ break;
+ case PRBS7:
+ displayport_reg_set_qual_pattern(PRBS7, DISABLE_SCRAM);
+ break;
+ case CUSTOM_80BIT:
+ displayport_reg_set_pattern_PLTPAT();
+ displayport_reg_set_qual_pattern(CUSTOM_80BIT, DISABLE_SCRAM);
+ break;
+ case HBR2_COMPLIANCE:
+ /*option 0*/
+ /*displayport_reg_set_hbr2_scrambler_reset(252);*/
+
+ /*option 1*/
+ displayport_reg_set_hbr2_scrambler_reset(252*2);
+
+ /*option 2*/
+ /*displayport_reg_set_hbr2_scrambler_reset(252);*/
+ /*displayport_reg_set_PN_Inverse_PHY_Lane(1);*/
+
+ /*option 3*/
+ /*displayport_reg_set_hbr2_scrambler_reset(252*2);*/
+ /*displayport_reg_set_PN_Inverse_PHY_Lane(1);*/
+
+ displayport_reg_set_qual_pattern(HBR2_COMPLIANCE, ENABLE_SCRAM);
+ break;
+ default:
+ displayport_err("not supported link qual pattern");
+ break;
+ }
+ } else if ((data & TEST_AUDIO_PATTERN) == TEST_AUDIO_PATTERN) {
+ struct displayport_audio_config_data audio_config_data;
+
+ displayport_reg_dpcd_read(DPCD_TEST_AUDIO_MODE, 1, val);
+ displayport_info("TEST_AUDIO_MODE %02x %02x\n",
+ (val[0] & TEST_AUDIO_SAMPLING_RATE),
+ (val[0] & TEST_AUDIO_CHANNEL_COUNT) >> 4);
+
+ displayport_reg_dpcd_read(DPCD_TEST_AUDIO_PATTERN_TYPE, 1, val);
+ displayport_info("TEST_AUDIO_PATTERN_TYPE %02x\n", val[0]);
+
+ msleep(300);
+
+ audio_config_data.audio_enable = 1;
+ audio_config_data.audio_fs = (val[0] & TEST_AUDIO_SAMPLING_RATE);
+ audio_config_data.audio_channel_cnt = (val[0] & TEST_AUDIO_CHANNEL_COUNT) >> 4;
+ audio_config_data.audio_channel_cnt++;
+ displayport_audio_bist_enable(audio_config_data);
+
+ } else {
+
+ displayport_err("Not Supported AUTOMATED_TEST_REQUEST\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int displayport_hdcp22_irq_handler(void)
+{
+#if defined(CONFIG_EXYNOS_HDCP2)
+ struct displayport_device *displayport = get_displayport_drvdata();
+ uint8_t rxstatus = 0;
+ int ret = 0;
+ int active;
+
+ active = pm_runtime_active(displayport->dev);
+ if (!active) {
+ displayport_info("displayport power(%d), state(%d)\n",
+ active, displayport->state);
+ spin_unlock(&displayport->slock);
+ return IRQ_HANDLED;
+ }
+
+ hdcp_dplink_get_rxstatus(&rxstatus);
+
+ if (rxstatus & DPCD_HDCP22_RXSTATUS_LINK_INTEGRITY_FAIL) {
+ /* hdcp22 disable while re-authentication */
+ ret = hdcp_dplink_set_integrity_fail();
+
+ if (displayport_reg_get_hdcp22_encryption_enable()) {
+ queue_delayed_work(displayport->dp_wq,
+ &displayport->hpd_unplug_work, 0);
+
+ displayport_info("LINK_INTEGRITY_FAIL HDCP2 enc on\n");
+ } else {
+ queue_delayed_work(displayport->hdcp2_wq,
+ &displayport->hdcp22_work, msecs_to_jiffies(2000));
+
+ displayport_info("LINK_INTEGRITY_FAIL HDCP2 enc off\n");
+ }
+ } else if (rxstatus & DPCD_HDCP22_RXSTATUS_REAUTH_REQ) {
+ /* hdcp22 disable while re-authentication */
+ ret = hdcp_dplink_set_reauth();
+
+ displayport_hdcp22_enable(0);
+ if (displayport_reg_get_hdcp22_encryption_enable()) {
+ queue_delayed_work(displayport->dp_wq, &displayport->hpd_unplug_work, 0);
+
+ displayport_info("REAUTH_REQ HDCP2 enc on\n");
+ } else {
+ queue_delayed_work(displayport->hdcp2_wq,
+ &displayport->hdcp22_work, msecs_to_jiffies(1000));
+
+ displayport_info("REAUTH_REQ HDCP2 enc off\n");
+ }
+ } else if (rxstatus & DPCD_HDCP22_RXSTATUS_PAIRING_AVAILABLE) {
+ /* set pairing avaible flag */
+ ret = hdcp_dplink_set_paring_available();
+ } else if (rxstatus & DPCD_HDCP22_RXSTATUS_HPRIME_AVAILABLE) {
+ /* set hprime avaible flag */
+ ret = hdcp_dplink_set_hprime_available();
+ } else if (rxstatus & DPCD_HDCP22_RXSTATUS_READY) {
+ /* set ready avaible flag */
+ /* todo update stream Management */
+ ret = hdcp_dplink_set_rp_ready();
+ if (auth_done) {
+ if (hdcp_dplink_authenticate() != 0) {
+ auth_done = HDCP_2_2_NOT_AUTH;
+ displayport_reg_video_mute(1);
+ }
+ else {
+ auth_done = HDCP_2_2_AUTH_DONE;
+ displayport_reg_video_mute(0);
+ }
+ }
+ } else {
+ displayport_info("undefined RxStatus(0x%x). ignore\n", rxstatus);
+ ret = -EINVAL;
+ }
+
+ return ret;
+#else
+ displayport_info("Not compiled EXYNOS_HDCP2 driver\n");
+ return 0;
+#endif
+}
+
+static void displayport_hpd_irq_work(struct work_struct *work)
+{
+ u8 val[DPCP_LINK_SINK_STATUS_FIELD_LENGTH] = {0, };
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ if (!displayport->hpd_current_state) {
+ displayport_info("HPD IRQ work: hpd is low\n");
+ return;
+ }
+
+ displayport->hpd_state = HPD_IRQ;
+ displayport_dbg("detect HPD_IRQ\n");
+
+ if (displayport->hdcp_ver == HDCP_VERSION_2_2) {
+ int ret = displayport_reg_dpcd_read_burst(DPCD_ADD_SINK_COUNT,
+ DPCP_LINK_SINK_STATUS_FIELD_LENGTH, val);
+
+ displayport_info("HPD IRQ work %02x %02x %02x %02x %02x %02x\n",
+ val[0], val[1], val[2], val[3], val[4], val[5]);
+ if (ret < 0 || (val[0] & val[1] & val[2] & val[3] & val[4] & val[5]) == 0xff) {
+ displayport_info("dpcd_read error in HPD IRQ work\n");
+ return;
+ }
+
+ if ((val[1] & CP_IRQ) == CP_IRQ) {
+ displayport_info("hdcp22: detect CP_IRQ\n");
+ ret = displayport_hdcp22_irq_handler();
+ if (ret == 0)
+ return;
+ } else {
+ displayport_info("hdcp22: detect hpd_irq!!!!\n");
+
+ if ((val[1] & AUTOMATED_TEST_REQUEST) == AUTOMATED_TEST_REQUEST) {
+ if (displayport_Automated_Test_Request() == 0)
+ return;
+ }
+
+ if (displayport_check_dpcd_lane_status(val[2], val[3], val[4]) != 0) {
+ displayport_info("link training in HPD IRQ work\n");
+ displayport_link_training();
+ }
+ }
+ return;
+ }
+
+ if (hdcp13_info.auth_state != HDCP13_STATE_AUTH_PROCESS) {
+ int ret = displayport_reg_dpcd_read_burst(DPCD_ADD_SINK_COUNT,
+ DPCP_LINK_SINK_STATUS_FIELD_LENGTH, val);
+
+ displayport_info("HPD IRQ work %02x %02x %02x %02x %02x %02x\n",
+ val[0], val[1], val[2], val[3], val[4], val[5]);
+ if (ret < 0 || (val[0] & val[1] & val[2] & val[3] & val[4] & val[5]) == 0xff) {
+ displayport_info("dpcd_read error in HPD IRQ work\n");
+ return;
+ }
+
+ if ((val[1] & CP_IRQ) == CP_IRQ && displayport->hdcp_ver == HDCP_VERSION_1_3) {
+ displayport_info("detect CP_IRQ\n");
+ hdcp13_info.cp_irq_flag = 1;
+ hdcp13_info.link_check = LINK_CHECK_NEED;
+ hdcp13_link_integrity_check();
+
+ if (hdcp13_info.auth_state == HDCP13_STATE_FAIL) {
+ queue_delayed_work(displayport->dp_wq,
+ &displayport->hdcp13_work, msecs_to_jiffies(2000));
+ } else
+ return;
+ }
+
+ if ((val[1] & AUTOMATED_TEST_REQUEST) == AUTOMATED_TEST_REQUEST) {
+ if (displayport_Automated_Test_Request() == 0)
+ return;
+ }
+
+ if (displayport_check_dpcd_lane_status(val[2], val[3], val[4]) != 0) {
+ displayport_info("link training in HPD IRQ work\n");
+ displayport_link_training();
+ }
+ } else {
+ displayport_reg_dpcd_read(DPCD_ADD_DEVICE_SERVICE_IRQ_VECTOR, 1, val);
+
+ if ((val[0] & CP_IRQ) == CP_IRQ) {
+ displayport_info("detect CP_IRQ\n");
+ hdcp13_info.cp_irq_flag = 1;
+ displayport_reg_dpcd_read(ADDR_HDCP13_BSTATUS, 1, HDCP13_DPCD.HDCP13_BSTATUS);
+ }
+ }
+}
+
+static void displayport_hdcp13_integrity_check_work(struct work_struct *work)
+{
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ if (displayport->hdcp_ver == HDCP_VERSION_1_3) {
+ hdcp13_info.link_check = LINK_CHECK_NEED;
+ hdcp13_link_integrity_check();
+ }
+}
+
+static irqreturn_t displayport_irq_handler(int irq, void *dev_data)
+{
+ struct displayport_device *displayport = dev_data;
+ struct decon_device *decon = get_decon_drvdata(2);
+ int active;
+ ktime_t timestamp = ktime_get();
+ u32 irq_status_reg;
+
+ spin_lock(&displayport->slock);
+
+ active = pm_runtime_active(displayport->dev);
+ if (!active) {
+ displayport_info("displayport power(%d), state(%d)\n",
+ active, displayport->state);
+ spin_unlock(&displayport->slock);
+ return IRQ_HANDLED;
+ }
+
+ /* Common interrupt */
+ irq_status_reg = displayport_reg_get_interrupt_and_clear(SYSTEM_IRQ_COMMON_STATUS);
+
+#if !defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER)
+ if (irq_status_reg & HPD_CHG)
+ displayport_info("HPD_CHG detect\n");
+
+ if (irq_status_reg & HPD_LOST) {
+ /*queue_delayed_work(displayport->dp_wq, &displayport->hpd_unplug_work, 0);*/
+ displayport_info("HPD_LOST detect\n");
+ }
+
+ if (irq_status_reg & HPD_PLUG_INT) {
+ /*queue_delayed_work(displayport->dp_wq, &displayport->hpd_plug_work, 0);*/
+ displayport_info("HPD_PLUG detect\n");
+ }
+
+ if (irq_status_reg & HPD_IRQ_FLAG) {
+ displayport->hpd_state = HPD_IRQ;
+ /*queue_delayed_work(displayport->dp_wq, &displayport->hpd_irq_work, 0);*/
+ displayport_info("HPD IRQ detect\n");
+ }
+#endif
+
+ if (irq_status_reg & HDCP_LINK_CHK_FAIL) {
+ queue_delayed_work(displayport->dp_wq, &displayport->hdcp13_integrity_check_work, 0);
+ displayport_info("HDCP_LINK_CHK detect\n");
+ }
+
+ if (irq_status_reg & HDCP_R0_CHECK_FLAG) {
+ hdcp13_info.r0_read_flag = 1;
+ displayport_info("R0_CHECK_FLAG detect\n");
+ }
+
+ /* SST1 interrupt */
+ irq_status_reg = displayport_reg_get_interrupt_and_clear(SST1_INTERRUPT_STATUS_SET0);
+
+ if (irq_status_reg & MAPI_FIFO_UNDER_FLOW)
+ displayport_info("VIDEO FIFO_UNDER_FLOW detect\n");
+
+ if (irq_status_reg & VSYNC_DET) {
+ /* VSYNC interrupt, accept it */
+ decon->frame_cnt++;
+ wake_up_interruptible_all(&decon->wait_vstatus);
+
+ if (decon->dt.psr_mode == DECON_VIDEO_MODE) {
+ decon->vsync.timestamp = timestamp;
+ wake_up_interruptible_all(&decon->vsync.wait);
+ }
+ }
+
+ irq_status_reg = displayport_reg_get_interrupt_and_clear(SST1_AUDIO_BUFFER_CONTROL);
+
+ if (irq_status_reg & MASTER_AUDIO_BUFFER_EMPTY_INT) {
+ displayport_dbg("AFIFO_UNDER detect\n");
+ displayport->audio_buf_empty_check = 1;
+ }
+
+ spin_unlock(&displayport->slock);
+
+ return IRQ_HANDLED;
+}
+
+static u8 displayport_get_vic(void)
+{
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ return supported_videos[displayport->cur_video].vic;
+}
+
+static int displayport_make_avi_infoframe_data(struct infoframe *avi_infoframe)
+{
+ int i;
+
+ avi_infoframe->type_code = INFOFRAME_PACKET_TYPE_AVI;
+ avi_infoframe->version_number = AVI_INFOFRAME_VERSION;
+ avi_infoframe->length = AVI_INFOFRAME_LENGTH;
+
+ for (i = 0; i < AVI_INFOFRAME_LENGTH; i++)
+ avi_infoframe->data[i] = 0x00;
+
+ avi_infoframe->data[0] |= ACTIVE_FORMAT_INFOMATION_PRESENT;
+ avi_infoframe->data[1] |= ACITVE_PORTION_ASPECT_RATIO;
+ avi_infoframe->data[3] = displayport_get_vic();
+
+ return 0;
+}
+
+static int displayport_make_audio_infoframe_data(struct infoframe *audio_infoframe,
+ struct displayport_audio_config_data *audio_config_data)
+{
+ int i;
+
+ audio_infoframe->type_code = INFOFRAME_PACKET_TYPE_AUDIO;
+ audio_infoframe->version_number = AUDIO_INFOFRAME_VERSION;
+ audio_infoframe->length = AUDIO_INFOFRAME_LENGTH;
+
+ for (i = 0; i < AUDIO_INFOFRAME_LENGTH; i++)
+ audio_infoframe->data[i] = 0x00;
+
+ /* Data Byte 1, PCM type and audio channel count */
+ audio_infoframe->data[0] = ((u8)audio_config_data->audio_channel_cnt - 1);
+
+ /* Data Byte 4, how various speaker locations are allocated */
+ audio_infoframe->data[3] = 0;
+
+ displayport_info("audio_infoframe: type and ch_cnt %02x, SF and bit size %02x, ch_allocation %02x\n",
+ audio_infoframe->data[0], audio_infoframe->data[1], audio_infoframe->data[3]);
+
+ return 0;
+}
+
+static int displayport_make_hdr_infoframe_data
+ (struct infoframe *hdr_infoframe, struct exynos_hdr_static_info *hdr_info)
+{
+ int i;
+
+ hdr_infoframe->type_code = INFOFRAME_PACKET_TYPE_HDR;
+ hdr_infoframe->version_number = HDR_INFOFRAME_VERSION;
+ hdr_infoframe->length = HDR_INFOFRAME_LENGTH;
+
+ for (i = 0; i < HDR_INFOFRAME_LENGTH; i++)
+ hdr_infoframe->data[i] = 0x00;
+
+ hdr_infoframe->data[HDR_INFOFRAME_EOTF_BYTE_NUM] = HDR_INFOFRAME_SMPTE_ST_2084;
+ hdr_infoframe->data[HDR_INFOFRAME_METADATA_ID_BYTE_NUM]
+ = STATIC_MATADATA_TYPE_1;
+ hdr_infoframe->data[HDR_INFOFRAME_DISP_PRI_X_0_LSB]
+ = hdr_info->stype1.mr.x & LSB_MASK;
+ hdr_infoframe->data[HDR_INFOFRAME_DISP_PRI_X_0_MSB]
+ = (hdr_info->stype1.mr.x & MSB_MASK) >> SHIFT_8BIT;
+ hdr_infoframe->data[HDR_INFOFRAME_DISP_PRI_Y_0_LSB]
+ = hdr_info->stype1.mr.y & LSB_MASK;
+ hdr_infoframe->data[HDR_INFOFRAME_DISP_PRI_Y_0_MSB]
+ = (hdr_info->stype1.mr.y & MSB_MASK) >> SHIFT_8BIT;
+ hdr_infoframe->data[HDR_INFOFRAME_DISP_PRI_X_1_LSB]
+ = hdr_info->stype1.mg.x & LSB_MASK;
+ hdr_infoframe->data[HDR_INFOFRAME_DISP_PRI_X_1_MSB]
+ = (hdr_info->stype1.mg.x & MSB_MASK) >> SHIFT_8BIT;
+ hdr_infoframe->data[HDR_INFOFRAME_DISP_PRI_Y_1_LSB]
+ = hdr_info->stype1.mg.y & LSB_MASK;
+ hdr_infoframe->data[HDR_INFOFRAME_DISP_PRI_Y_1_MSB]
+ = (hdr_info->stype1.mg.y & MSB_MASK) >> SHIFT_8BIT;
+ hdr_infoframe->data[HDR_INFOFRAME_DISP_PRI_X_2_LSB]
+ = hdr_info->stype1.mb.x & LSB_MASK;
+ hdr_infoframe->data[HDR_INFOFRAME_DISP_PRI_X_2_MSB]
+ = (hdr_info->stype1.mb.x & MSB_MASK) >> SHIFT_8BIT;
+ hdr_infoframe->data[HDR_INFOFRAME_DISP_PRI_Y_2_LSB]
+ = hdr_info->stype1.mb.y & LSB_MASK;
+ hdr_infoframe->data[HDR_INFOFRAME_DISP_PRI_Y_2_MSB]
+ = (hdr_info->stype1.mb.y & MSB_MASK) >> SHIFT_8BIT;
+ hdr_infoframe->data[HDR_INFOFRAME_WHITE_POINT_X_LSB]
+ = hdr_info->stype1.mw.x & LSB_MASK;
+ hdr_infoframe->data[HDR_INFOFRAME_WHITE_POINT_X_MSB]
+ = (hdr_info->stype1.mw.x & MSB_MASK) >> SHIFT_8BIT;
+ hdr_infoframe->data[HDR_INFOFRAME_WHITE_POINT_Y_LSB]
+ = hdr_info->stype1.mw.y & LSB_MASK;
+ hdr_infoframe->data[HDR_INFOFRAME_WHITE_POINT_Y_MSB]
+ = (hdr_info->stype1.mw.y & MSB_MASK) >> SHIFT_8BIT;
+ hdr_infoframe->data[HDR_INFOFRAME_MAX_LUMI_LSB]
+ = hdr_info->stype1.mmax_display_luminance & LSB_MASK;
+ hdr_infoframe->data[HDR_INFOFRAME_MAX_LUMI_MSB]
+ = (hdr_info->stype1.mmax_display_luminance & MSB_MASK) >> SHIFT_8BIT;
+ hdr_infoframe->data[HDR_INFOFRAME_MIN_LUMI_LSB]
+ = hdr_info->stype1.mmin_display_luminance & LSB_MASK;
+ hdr_infoframe->data[HDR_INFOFRAME_MIN_LUMI_MSB]
+ = (hdr_info->stype1.mmin_display_luminance & MSB_MASK) >> SHIFT_8BIT;
+ hdr_infoframe->data[HDR_INFOFRAME_MAX_LIGHT_LEVEL_LSB]
+ = hdr_info->stype1.mmax_content_light_level & LSB_MASK;
+ hdr_infoframe->data[HDR_INFOFRAME_MAX_LIGHT_LEVEL_MSB]
+ = (hdr_info->stype1.mmax_content_light_level & MSB_MASK) >> SHIFT_8BIT;
+ hdr_infoframe->data[HDR_INFOFRAME_MAX_AVERAGE_LEVEL_LSB]
+ = hdr_info->stype1.mmax_frame_average_light_level & LSB_MASK;
+ hdr_infoframe->data[HDR_INFOFRAME_MAX_AVERAGE_LEVEL_MSB]
+ = (hdr_info->stype1.mmax_frame_average_light_level & MSB_MASK) >> SHIFT_8BIT;
+
+ for (i = 0; i < HDR_INFOFRAME_LENGTH; i++) {
+ displayport_dbg("hdr_infoframe->data[%d] = 0x%02x", i,
+ hdr_infoframe->data[i]);
+ }
+
+ return 0;
+}
+
+static int displayport_set_avi_infoframe(void)
+{
+ struct infoframe avi_infoframe;
+
+ displayport_make_avi_infoframe_data(&avi_infoframe);
+ displayport_reg_set_avi_infoframe(avi_infoframe);
+
+ return 0;
+}
+
+static int displayport_set_audio_infoframe(struct displayport_audio_config_data *audio_config_data)
+{
+ struct infoframe audio_infoframe;
+
+ displayport_make_audio_infoframe_data(&audio_infoframe, audio_config_data);
+ displayport_reg_set_audio_infoframe(audio_infoframe, audio_config_data->audio_enable);
+
+ return 0;
+}
+
+static int displayport_set_hdr_infoframe(struct exynos_hdr_static_info *hdr_info)
+{
+ struct infoframe hdr_infoframe;
+
+ if (hdr_info->mid >= 0) {
+ displayport_dbg("displayport_set_hdr_infoframe 1\n");
+ displayport_make_hdr_infoframe_data(&hdr_infoframe, hdr_info);
+ displayport_reg_set_hdr_infoframe(hdr_infoframe, 1);
+ } else {
+ displayport_dbg("displayport_set_hdr_infoframe 0\n");
+ displayport_reg_set_hdr_infoframe(hdr_infoframe, 0);
+ }
+
+ return 0;
+}
+
+void displayport_audio_wait_buf_full(void)
+{
+ displayport_reg_set_audio_master_mode_enable(0);
+ displayport_reg_wait_buf_full();
+
+ displayport_info("displayport_audio_wait_buf_full\n");
+}
+
+void displayport_wait_audio_buf_empty(struct displayport_device *displayport)
+{
+ u32 cnt = 1000;
+
+ do {
+ cnt--;
+ udelay(1);
+ } while (!displayport->audio_buf_empty_check && cnt);
+
+ if (!cnt)
+ displayport_err("%s is timeout.\n", __func__);
+}
+
+void displayport_audio_disable(void)
+{
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ if (displayport_read_mask(SST1_AUDIO_ENABLE, AUDIO_EN) == 1) {
+ displayport_reg_set_dp_audio_enable(0);
+ displayport_reg_set_audio_fifo_function_enable(0);
+ displayport_reg_set_audio_ch(1);
+ displayport->audio_buf_empty_check = 0;
+ displayport_reg_set_audio_master_mode_enable(1);
+ displayport_wait_audio_buf_empty(displayport);
+ displayport_reg_set_audio_master_mode_enable(0);
+ displayport_info("audio_disable\n");
+ displayport->audio_state = 0;
+ wake_up_interruptible(&displayport->audio_wait);
+ }
+}
+
+static int displayport_set_audio_ch_status(struct displayport_audio_config_data *audio_config_data)
+{
+ displayport_reg_set_ch_status_ch_cnt(audio_config_data->audio_channel_cnt);
+ displayport_reg_set_ch_status_word_length(audio_config_data->audio_bit);
+ displayport_reg_set_ch_status_sampling_frequency(audio_config_data->audio_fs);
+ displayport_reg_set_ch_status_clock_accuracy(NOT_MATCH);
+
+ return 0;
+}
+
+int displayport_audio_config(struct displayport_audio_config_data *audio_config_data)
+{
+ struct displayport_device *displayport = get_displayport_drvdata();
+ int ret = 0;
+
+ displayport_info("audio en:%d, ch:%d, fs:%d, bit:%d, packed:%d, word_len:%d\n",
+ audio_config_data->audio_enable, audio_config_data->audio_channel_cnt,
+ audio_config_data->audio_fs, audio_config_data->audio_bit,
+ audio_config_data->audio_packed_mode, audio_config_data->audio_word_length);
+
+ if (audio_config_data->audio_enable == displayport->audio_state)
+ return 0;
+
+ if (audio_config_data->audio_enable == AUDIO_ENABLE) {
+ /* channel mapping: FL, FR, C, SW, RL, RR */
+ displayport_reg_set_audio_ch_mapping(1, 2, 4, 3, 5, 6, 7, 8);
+
+ displayport_reg_set_audio_m_n(ASYNC_MODE, audio_config_data->audio_fs);
+ displayport_reg_set_audio_function_enable(audio_config_data->audio_enable);
+ displayport_reg_set_dma_burst_size(audio_config_data->audio_word_length);
+ displayport_reg_set_dma_pack_mode(audio_config_data->audio_packed_mode);
+ displayport_reg_set_pcm_size(audio_config_data->audio_bit);
+ displayport_reg_set_audio_ch(audio_config_data->audio_channel_cnt);
+ displayport_reg_set_audio_fifo_function_enable(audio_config_data->audio_enable);
+ displayport_reg_set_audio_ch_status_same(1);
+ displayport_reg_set_audio_sampling_frequency(audio_config_data->audio_fs);
+ displayport_reg_set_dp_audio_enable(audio_config_data->audio_enable);
+ displayport_set_audio_infoframe(audio_config_data);
+ displayport_set_audio_ch_status(audio_config_data);
+ displayport_reg_set_audio_master_mode_enable(audio_config_data->audio_enable);
+
+ displayport->audio_state = 1;
+ } else if (audio_config_data->audio_enable == AUDIO_DISABLE)
+ displayport_audio_disable();
+ else if (audio_config_data->audio_enable == AUDIO_WAIT_BUF_FULL)
+ displayport_audio_wait_buf_full();
+ else
+ displayport_info("Not support audio_enable = %d\n", audio_config_data->audio_enable);
+
+ return ret;
+}
+EXPORT_SYMBOL(displayport_audio_config);
+
+int displayport_audio_bist_enable(struct displayport_audio_config_data audio_config_data)
+{
+ int ret = 0;
+
+ displayport_info("displayport_audio_bist\n");
+ displayport_info("audio_enable = %d\n", audio_config_data.audio_enable);
+ displayport_info("audio_channel_cnt = %d\n", audio_config_data.audio_channel_cnt);
+ displayport_info("audio_fs = %d\n", audio_config_data.audio_fs);
+
+ if (audio_config_data.audio_enable == 1) {
+ displayport_reg_set_audio_m_n(ASYNC_MODE, audio_config_data.audio_fs);
+ displayport_reg_set_audio_function_enable(audio_config_data.audio_enable);
+
+ displayport_reg_set_audio_ch(audio_config_data.audio_channel_cnt);
+ displayport_reg_set_audio_fifo_function_enable(audio_config_data.audio_enable);
+ displayport_reg_set_audio_ch_status_same(1);
+ displayport_reg_set_audio_sampling_frequency(audio_config_data.audio_fs);
+ displayport_reg_set_dp_audio_enable(audio_config_data.audio_enable);
+ displayport_reg_set_audio_bist_mode(1);
+ displayport_set_audio_infoframe(&audio_config_data);
+ displayport_set_audio_ch_status(&audio_config_data);
+ displayport_reg_set_audio_master_mode_enable(audio_config_data.audio_enable);
+ } else
+ displayport_audio_disable();
+
+ return ret;
+}
+
+int displayport_dpcd_read_for_hdcp22(u32 address, u32 length, u8 *data)
+{
+ int ret;
+
+ ret = displayport_reg_dpcd_read_burst(address, length, data);
+
+ if (ret != 0)
+ displayport_err("dpcd_read_for_hdcp22 fail: 0x%Xn", address);
+
+ return ret;
+}
+
+int displayport_dpcd_write_for_hdcp22(u32 address, u32 length, u8 *data)
+{
+ int ret;
+
+ ret = displayport_reg_dpcd_write_burst(address, length, data);
+
+ if (ret != 0)
+ displayport_err("dpcd_write_for_hdcp22 fail: 0x%X\n", address);
+
+ return ret;
+}
+
+void displayport_hdcp22_enable(u32 en)
+{
+ struct decon_device *decon = get_decon_drvdata(2);
+
+ /* wait 2 frames for hdcp encryption enable/disable */
+ decon_wait_for_vsync(decon, VSYNC_TIMEOUT_MSEC);
+ decon_wait_for_vsync(decon, VSYNC_TIMEOUT_MSEC);
+
+ if (en) {
+ displayport_reg_set_hdcp22_system_enable(1);
+ displayport_reg_set_hdcp22_mode(1);
+ displayport_reg_set_hdcp22_encryption_enable(1);
+ } else {
+ displayport_reg_set_hdcp22_system_enable(0);
+ displayport_reg_set_hdcp22_mode(0);
+ displayport_reg_set_hdcp22_encryption_enable(0);
+ }
+}
+
+static void displayport_hdcp13_run(struct work_struct *work)
+{
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ if (displayport->hdcp_ver != HDCP_VERSION_1_3 ||
+ !displayport->hpd_current_state)
+ return;
+
+ displayport_dbg("[HDCP 1.3] run\n");
+ hdcp13_run();
+ if (hdcp13_info.auth_state == HDCP13_STATE_FAIL) {
+ queue_delayed_work(displayport->dp_wq, &displayport->hdcp13_work,
+ msecs_to_jiffies(2000));
+ }
+}
+
+static void displayport_hdcp22_run(struct work_struct *work)
+{
+#if defined(CONFIG_EXYNOS_HDCP2)
+ u8 val[2] = {0, };
+
+ if (hdcp_dplink_authenticate() != 0) {
+ auth_done = HDCP_2_2_NOT_AUTH;
+ displayport_reg_video_mute(1);
+ }
+ else {
+ auth_done = HDCP_2_2_AUTH_DONE;
+ displayport_reg_video_mute(0);
+ }
+ displayport_dpcd_read_for_hdcp22(DPCD_HDCP22_RX_INFO, 2, val);
+ displayport_info("HDCP2.2 rx_info: 0:0x%X, 8:0x%X\n", val[1], val[0]);
+#else
+ displayport_info("Not compiled EXYNOS_HDCP2 driver\n");
+#endif
+}
+
+static int displayport_check_hdcp_version(void)
+{
+ int ret = 0;
+ u8 val[DPCD_HDCP22_RX_CAPS_LENGTH];
+ u32 rx_caps = 0;
+ int i;
+
+ hdcp13_dpcd_buffer();
+
+ if (hdcp13_read_bcap() != 0)
+ displayport_dbg("[HDCP 1.3] NONE HDCP CAPABLE\n");
+#if defined(HDCP_SUPPORT)
+ else
+ ret = HDCP_VERSION_1_3;
+#endif
+ displayport_dpcd_read_for_hdcp22(DPCD_HDCP22_RX_CAPS, DPCD_HDCP22_RX_CAPS_LENGTH, val);
+
+ for (i = 0; i < DPCD_HDCP22_RX_CAPS_LENGTH; i++)
+ rx_caps |= (u32)val[i] << ((DPCD_HDCP22_RX_CAPS_LENGTH - (i + 1)) * 8);
+
+ displayport_info("HDCP2.2 rx_caps = 0x%x\n", rx_caps);
+
+ if ((((rx_caps & VERSION) >> DPCD_HDCP_VERSION_BIT_POSITION) == (HDCP_VERSION_2_2))
+ && ((rx_caps & HDCP_CAPABLE) != 0)) {
+#if defined(HDCP_2_2)
+ ret = HDCP_VERSION_2_2;
+#endif
+ displayport_dbg("displayport_rx supports hdcp2.2\n");
+ }
+
+ return ret;
+}
+
+static void hdcp_start(struct displayport_device *displayport)
+{
+ displayport->hdcp_ver = displayport_check_hdcp_version();
+#if defined(HDCP_SUPPORT)
+ if (displayport->hdcp_ver == HDCP_VERSION_2_2)
+ queue_delayed_work(displayport->hdcp2_wq, &displayport->hdcp22_work,
+ msecs_to_jiffies(2500));
+ else if (displayport->hdcp_ver == HDCP_VERSION_1_3)
+ queue_delayed_work(displayport->dp_wq, &displayport->hdcp13_work,
+ msecs_to_jiffies(4500));
+ else
+ displayport_info("HDCP is not supported\n");
+#endif
+}
+
+static int displayport_enable(struct displayport_device *displayport)
+{
+ int ret = 0;
+ u8 bpc = (u8)displayport->bpc;
+ u8 bist_type = (u8)displayport->bist_type;
+ u8 dyn_range = (u8)displayport->dyn_range;
+
+ if (displayport->state == DISPLAYPORT_STATE_ON)
+ return 0;
+
+ displayport_info("displayport_enable\n");
+
+#if defined(CONFIG_CPU_IDLE)
+ /* block to enter SICD mode */
+ exynos_update_ip_idle_status(displayport->idle_ip_index, 0);
+#endif
+
+ pm_runtime_get_sync(displayport->dev);
+
+ enable_irq(displayport->res.irq);
+
+ displayport_info("cur_video = %s in displayport_enable!!!\n",
+ supported_videos[displayport->cur_video].name);
+
+ if (displayport->bist_used)
+ displayport_reg_set_bist_video_configuration(displayport->cur_video,
+ bpc, bist_type, dyn_range);
+ else {
+ if (displayport->bpc == BPC_6 && displayport->dfp_type != DFP_TYPE_DP)
+ bpc = BPC_8;
+
+ displayport_reg_set_video_configuration(displayport->cur_video,
+ bpc, dyn_range);
+ }
+
+ displayport_set_avi_infoframe();
+#ifdef HDCP_SUPPORT
+ displayport_reg_video_mute(0);
+#endif
+ displayport_reg_start();
+
+ displayport->state = DISPLAYPORT_STATE_ON;
+ wake_up_interruptible(&displayport->dp_wait);
+ hdcp_start(displayport);
+
+ return ret;
+}
+
+static int displayport_disable(struct displayport_device *displayport)
+{
+ int timeout;
+
+ if (displayport->state != DISPLAYPORT_STATE_ON)
+ return 0;
+
+ /* Wait for current read & write CMDs. */
+ mutex_lock(&displayport->cmd_lock);
+ displayport->state = DISPLAYPORT_STATE_OFF;
+ hdcp13_info.auth_state = HDCP13_STATE_NOT_AUTHENTICATED;
+ mutex_unlock(&displayport->cmd_lock);
+
+ timeout = wait_event_interruptible_timeout(displayport->audio_wait,
+ (displayport->audio_state == 0), msecs_to_jiffies(3000));
+ if (!timeout)
+ displayport_info("audio disable timeout\n");
+
+ displayport_audio_disable();
+
+ displayport_reg_set_video_bist_mode(0);
+ displayport_reg_stop();
+ disable_irq(displayport->res.irq);
+
+ displayport_reg_phy_disable();
+
+ pm_runtime_put_sync(displayport->dev);
+
+ displayport->state = DISPLAYPORT_STATE_INIT;
+ wake_up_interruptible(&displayport->dp_wait);
+ displayport_info("displayport_disable\n");
+
+#if defined(CONFIG_CPU_IDLE)
+ /* unblock to enter SICD mode */
+ exynos_update_ip_idle_status(displayport->idle_ip_index, 1);
+#endif
+
+ return 0;
+}
+
+bool displayport_match_timings(const struct v4l2_dv_timings *t1,
+ const struct v4l2_dv_timings *t2,
+ unsigned pclock_delta)
+{
+ if (t1->type != t2->type)
+ return false;
+
+ if (t1->bt.width == t2->bt.width &&
+ t1->bt.height == t2->bt.height &&
+ t1->bt.interlaced == t2->bt.interlaced &&
+ t1->bt.polarities == t2->bt.polarities &&
+ t1->bt.pixelclock >= t2->bt.pixelclock - pclock_delta &&
+ t1->bt.pixelclock <= t2->bt.pixelclock + pclock_delta &&
+ t1->bt.hfrontporch == t2->bt.hfrontporch &&
+ t1->bt.vfrontporch == t2->bt.vfrontporch &&
+ t1->bt.vsync == t2->bt.vsync &&
+ t1->bt.vbackporch == t2->bt.vbackporch &&
+ (!t1->bt.interlaced ||
+ (t1->bt.il_vfrontporch == t2->bt.il_vfrontporch &&
+ t1->bt.il_vsync == t2->bt.il_vsync &&
+ t1->bt.il_vbackporch == t2->bt.il_vbackporch)))
+ return true;
+
+ return false;
+}
+
+static int displayport_timing2conf(struct v4l2_dv_timings *timings)
+{
+ int i;
+
+ for (i = 0; i < supported_videos_pre_cnt; i++) {
+ if (displayport_match_timings(&supported_videos[i].dv_timings,
+ timings, 0))
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int displayport_s_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings *timings)
+{
+ struct displayport_device *displayport = container_of(sd, struct displayport_device, sd);
+ struct decon_device *decon = get_decon_drvdata(2);
+ videoformat displayport_setting_videoformat;
+ int ret = 0;
+
+ v4l2_print_dv_timings("Displayport:", " set ", timings, false);
+ ret = displayport_timing2conf(timings);
+ if (ret < 0) {
+ displayport_err("displayport timings not supported\n");
+ return -EINVAL;
+ }
+ displayport_setting_videoformat = ret;
+
+ if (displayport->bist_used == 0) {
+ if (displayport->rx_edid_data.hdr_support &&
+ !(displayport_check_edid_max_clock(displayport,
+ displayport_setting_videoformat) < 0))
+ displayport->bpc = BPC_10;
+ else
+ displayport->bpc = BPC_8;
+
+ /*fail safe mode (640x480) with 6 bpc*/
+ if (displayport_setting_videoformat == V640X480P60)
+ displayport->bpc = BPC_6;
+ }
+
+ displayport->cur_video = displayport_setting_videoformat;
+ displayport->cur_timings = *timings;
+
+ decon_displayport_get_out_sd(decon);
+
+ displayport_dbg("New cur_video = %s\n",
+ supported_videos[displayport->cur_video].name);
+
+ return 0;
+}
+
+static int displayport_g_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings *timings)
+{
+ struct displayport_device *displayport = container_of(sd, struct displayport_device, sd);
+
+ *timings = displayport->cur_timings;
+
+ displayport_dbg("displayport_g_dv_timings\n");
+
+ return 0;
+}
+
+static u64 displayport_get_max_pixelclock(void)
+{
+ u64 pc;
+ u64 pc1lane;
+
+ if (max_link_rate == LINK_RATE_5_4Gbps) {
+ pc1lane = 180000000;
+ } else if (max_link_rate == LINK_RATE_2_7Gbps) {
+ pc1lane = 90000000;
+ } else {/* LINK_RATE_1_62Gbps */
+ pc1lane = 54000000;
+ }
+
+ pc = max_lane_cnt * pc1lane;
+
+ return pc;
+}
+
+static int displayport_enum_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_enum_dv_timings *timings)
+{
+ if (timings->index >= supported_videos_pre_cnt) {
+ displayport_dbg("displayport_enum_dv_timings -EOVERFLOW\n");
+ return -E2BIG;
+ }
+
+ /* reduce the timing by lane count and link rate */
+ if (supported_videos[timings->index].dv_timings.bt.pixelclock >
+ displayport_get_max_pixelclock()) {
+ displayport_info("Max pixelclock = %llu, lane:%d, rate:0x%x\n",
+ displayport_get_max_pixelclock(), max_lane_cnt, max_link_rate);
+ return -E2BIG;
+ }
+
+ if (reduced_resolution && reduced_resolution <
+ supported_videos[timings->index].dv_timings.bt.pixelclock) {
+ displayport_info("reduced_resolution: %llu\n", reduced_resolution);
+ return -E2BIG;
+ }
+
+ if (supported_videos[timings->index].edid_support_match) {
+ displayport_info("matched video_format : %s\n",
+ supported_videos[timings->index].name);
+ timings->timings = supported_videos[timings->index].dv_timings;
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int displayport_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct displayport_device *displayport = container_of(sd, struct displayport_device, sd);
+
+ if (enable)
+ return displayport_enable(displayport);
+ else
+ return displayport_disable(displayport);
+}
+
+int displayport_set_hdr_config(struct exynos_hdr_static_info *hdr_info)
+{
+ int ret = 0;
+
+ displayport_set_hdr_infoframe(hdr_info);
+
+ return ret;
+}
+
+bool is_displayport_not_running(void)
+{
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ if (displayport->state == DISPLAYPORT_STATE_ON)
+ return false;
+ else
+ return true;
+}
+
+static long displayport_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+ struct displayport_device *displayport = container_of(sd, struct displayport_device, sd);
+ int ret = 0;
+ struct v4l2_enum_dv_timings *enum_timings;
+ struct exynos_hdr_static_info *hdr_info;
+
+ switch (cmd) {
+ case DISPLAYPORT_IOC_DUMP:
+ displayport_dump_registers(displayport);
+ break;
+
+ case DISPLAYPORT_IOC_GET_ENUM_DV_TIMINGS:
+ enum_timings = (struct v4l2_enum_dv_timings *)arg;
+
+ ret = displayport_enum_dv_timings(sd, enum_timings);
+ break;
+
+ case DISPLAYPORT_IOC_SET_RECONNECTION: /* for restart without hpd change */
+ displayport_set_reconnection();
+ break;
+
+ case DISPLAYPORT_IOC_SET_HDR_METADATA:
+ hdr_info = (struct exynos_hdr_static_info *)arg;
+ /* set info frame for hdr contents */
+ ret = displayport_set_hdr_config(hdr_info);
+ if (ret)
+ displayport_err("failed to configure hdr info\n");
+ break;
+
+ case EXYNOS_DPU_GET_ACLK:
+ return clk_get_rate(displayport->res.aclk);
+
+ default:
+ displayport_err("unsupported ioctl");
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct v4l2_subdev_core_ops displayport_sd_core_ops = {
+ .ioctl = displayport_ioctl,
+};
+
+static const struct v4l2_subdev_video_ops displayport_sd_video_ops = {
+ .s_dv_timings = displayport_s_dv_timings,
+ .g_dv_timings = displayport_g_dv_timings,
+ .s_stream = displayport_s_stream,
+};
+
+static const struct v4l2_subdev_ops displayport_subdev_ops = {
+ .core = &displayport_sd_core_ops,
+ .video = &displayport_sd_video_ops,
+};
+
+static void displayport_init_subdev(struct displayport_device *displayport)
+{
+ struct v4l2_subdev *sd = &displayport->sd;
+
+ v4l2_subdev_init(sd, &displayport_subdev_ops);
+ sd->owner = THIS_MODULE;
+ snprintf(sd->name, sizeof(sd->name), "%s", "displayport-sd");
+ v4l2_set_subdevdata(sd, displayport);
+}
+
+static int displayport_parse_dt(struct displayport_device *displayport, struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ int ret;
+
+ if (IS_ERR_OR_NULL(dev->of_node)) {
+ displayport_err("no device tree information\n");
+ return -EINVAL;
+ }
+
+ displayport->phy = devm_phy_get(dev, "displayport_phy");
+ if (IS_ERR_OR_NULL(displayport->phy)) {
+ displayport_err("failed to get displayport phy\n");
+ return PTR_ERR(displayport->phy);
+ }
+
+ displayport->dev = dev;
+
+ displayport->gpio_sw_oe = of_get_named_gpio(np, "dp,aux_sw_oe", 0);
+ if (gpio_is_valid(displayport->gpio_sw_oe)) {
+ ret = gpio_request(displayport->gpio_sw_oe, "dp_aux_sw_oe");
+ if (ret)
+ displayport_err("failed to get gpio dp_aux_sw_oe\n");
+ else
+ gpio_direction_output(displayport->gpio_sw_oe, 1);
+ } else
+ displayport_err("failed to get gpio dp_aux_sw_oe\n");
+
+ displayport->gpio_sw_sel = of_get_named_gpio(np, "dp,sbu_sw_sel", 0);
+ if (gpio_is_valid(displayport->gpio_sw_sel)) {
+ ret = gpio_request(displayport->gpio_sw_sel, "dp_sbu_sw_sel");
+ if (ret)
+ displayport_err("failed to get gpio dp_sbu_sw_sel\n");
+ } else
+ displayport_err("failed to get gpio dp_sbu_sw_sel\n");
+
+ displayport->gpio_usb_dir = of_get_named_gpio(np, "dp,usb_con_sel", 0);
+ if (!gpio_is_valid(displayport->gpio_usb_dir))
+ displayport_err("failed to get gpio dp_usb_con_sel\n");
+
+ displayport_info("%s done\n", __func__);
+
+ return 0;
+}
+
+static int displayport_init_resources(struct displayport_device *displayport, struct platform_device *pdev)
+{
+ struct resource *res;
+ int ret = 0;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ displayport_err("failed to get mem resource\n");
+ return -ENOENT;
+ }
+
+ displayport_info("link_regs: start(0x%x), end(0x%x)\n", (u32)res->start, (u32)res->end);
+
+ displayport->res.link_regs = devm_ioremap_resource(displayport->dev, res);
+ if (!displayport->res.link_regs) {
+ displayport_err("failed to remap DisplayPort LINK SFR region\n");
+ return -EINVAL;
+ }
+
+#if defined(CONFIG_PHY_EXYNOS_USBDRD)
+ displayport->res.phy_regs = phy_exynos_usbdp_get_address();
+ if (!displayport->res.phy_regs) {
+ displayport_err("failed to get USBDP combo PHY SFR region\n");
+ return -EINVAL;
+ }
+#endif
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ displayport_err("failed to get irq resource\n");
+ return -ENOENT;
+ }
+
+ displayport->res.irq = res->start;
+ ret = devm_request_irq(displayport->dev, res->start,
+ displayport_irq_handler, 0, pdev->name, displayport);
+ if (ret) {
+ displayport_err("failed to install DisplayPort irq\n");
+ return -EINVAL;
+ }
+ disable_irq(displayport->res.irq);
+
+ displayport->res.aclk = devm_clk_get(displayport->dev, "aclk");
+ if (IS_ERR_OR_NULL(displayport->res.aclk)) {
+ displayport_err("failed to get aclk\n");
+ return PTR_ERR(displayport->res.aclk);
+ }
+
+ return 0;
+}
+
+#if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER)
+static int displayport_aux_onoff(struct displayport_device *displayport, int
+ onoff)
+{
+ int rc = 0;
+
+ displayport_info("aux vdd onoff = %d\n", onoff);
+
+ if (onoff == 1)
+ gpio_direction_output(displayport->gpio_sw_oe, 0);
+ else
+ gpio_direction_output(displayport->gpio_sw_oe, 1);
+
+ return rc;
+}
+
+static void displayport_aux_sel(struct displayport_device *displayport)
+{
+ if (gpio_is_valid(displayport->gpio_usb_dir) &&
+ gpio_is_valid(displayport->gpio_sw_sel)) {
+ displayport->dp_sw_sel = gpio_get_value(displayport->gpio_usb_dir);
+ gpio_direction_output(displayport->gpio_sw_sel, !(displayport->dp_sw_sel));
+ displayport_info("Get direction from ccic %d\n", displayport->dp_sw_sel);
+ } else if (gpio_is_valid(displayport->gpio_usb_dir)) {
+ /* for old H/W - AUX switch is controlled by CCIC */
+ displayport->dp_sw_sel = !gpio_get_value(displayport->gpio_usb_dir);
+ displayport_info("Get Direction From CCIC %d\n", !displayport->dp_sw_sel);
+ }
+}
+
+static int usb_typec_displayport_notification(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct displayport_device *displayport = container_of(nb,
+ struct displayport_device, dp_typec_nb);
+ CC_NOTI_TYPEDEF usb_typec_info = *(CC_NOTI_TYPEDEF *)data;
+
+ if (usb_typec_info.dest != CCIC_NOTIFY_DEV_DP)
+ return 0;
+
+ displayport_dbg("%s: action (%ld) dump(0x%01x, 0x%01x, 0x%02x, 0x%04x, 0x%04x, 0x%04x)\n",
+ __func__, action, usb_typec_info.src, usb_typec_info.dest, usb_typec_info.id,
+ usb_typec_info.sub1, usb_typec_info.sub2, usb_typec_info.sub3);
+
+ switch (usb_typec_info.id) {
+ case CCIC_NOTIFY_ID_DP_CONNECT:
+ displayport_info("CCIC_NOTIFY_ID_DP_CONNECT, %x\n", usb_typec_info.sub1);
+ switch (usb_typec_info.sub1) {
+ case CCIC_NOTIFY_DETACH:
+ displayport->ccic_notify_dp_conf = CCIC_NOTIFY_DP_PIN_UNKNOWN;
+ displayport->ccic_link_conf = false;
+ displayport->ccic_hpd = false;
+ displayport_hpd_changed(0);
+ displayport_aux_onoff(displayport, 0);
+ break;
+ case CCIC_NOTIFY_ATTACH:
+ displayport_aux_onoff(displayport, 1);
+ break;
+ default:
+ break;
+ }
+
+ break;
+
+ case CCIC_NOTIFY_ID_DP_LINK_CONF:
+ displayport_info("CCIC_NOTIFY_ID_DP_LINK_CONF %x\n",
+ usb_typec_info.sub1);
+ displayport_aux_sel(displayport);
+ switch (usb_typec_info.sub1) {
+ case CCIC_NOTIFY_DP_PIN_UNKNOWN:
+ displayport->ccic_notify_dp_conf = CCIC_NOTIFY_DP_PIN_UNKNOWN;
+ break;
+ case CCIC_NOTIFY_DP_PIN_A:
+ displayport->ccic_notify_dp_conf = CCIC_NOTIFY_DP_PIN_A;
+ break;
+ case CCIC_NOTIFY_DP_PIN_B:
+ displayport->dp_sw_sel = !displayport->dp_sw_sel;
+ displayport->ccic_notify_dp_conf = CCIC_NOTIFY_DP_PIN_B;
+ break;
+ case CCIC_NOTIFY_DP_PIN_C:
+ displayport->ccic_notify_dp_conf = CCIC_NOTIFY_DP_PIN_C;
+ break;
+ case CCIC_NOTIFY_DP_PIN_D:
+ displayport->ccic_notify_dp_conf = CCIC_NOTIFY_DP_PIN_D;
+ break;
+ case CCIC_NOTIFY_DP_PIN_E:
+ displayport->ccic_notify_dp_conf = CCIC_NOTIFY_DP_PIN_E;
+ break;
+ case CCIC_NOTIFY_DP_PIN_F:
+ displayport->ccic_notify_dp_conf = CCIC_NOTIFY_DP_PIN_F;
+ break;
+ default:
+ displayport->ccic_notify_dp_conf = CCIC_NOTIFY_DP_PIN_UNKNOWN;
+ break;
+ }
+ displayport->ccic_link_conf = true;
+ if (displayport->ccic_hpd) {
+ displayport_hpd_changed(1);
+ }
+ break;
+
+ case CCIC_NOTIFY_ID_DP_HPD:
+ displayport_info("CCIC_NOTIFY_ID_DP_HPD, %x, %x\n",
+ usb_typec_info.sub1, usb_typec_info.sub2);
+ switch (usb_typec_info.sub1) {
+ case CCIC_NOTIFY_IRQ:
+ break;
+ case CCIC_NOTIFY_LOW:
+ displayport->ccic_hpd = false;
+ displayport_hpd_changed(0);
+ break;
+ case CCIC_NOTIFY_HIGH:
+ if (displayport->hpd_current_state &&
+ usb_typec_info.sub2 == CCIC_NOTIFY_IRQ) {
+ queue_delayed_work(displayport->dp_wq, &displayport->hpd_irq_work, 0);
+ return 0;
+ } else {
+ displayport->ccic_hpd = true;
+ if (displayport->ccic_link_conf) {
+ displayport_hpd_changed(1);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void displayport_notifier_register_work(struct work_struct *work)
+{
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ if (!displayport->notifier_registered) {
+ displayport->notifier_registered = 1;
+ manager_notifier_register(&displayport->dp_typec_nb,
+ usb_typec_displayport_notification, MANAGER_NOTIFY_CCIC_DP);
+ }
+}
+#endif
+
+#ifdef DISPLAYPORT_TEST
+static ssize_t displayport_link_show(struct class *class,
+ struct class_attribute *attr,
+ char *buf)
+{
+ /*struct displayport_device *displayport = get_displayport_drvdata();*/
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", __func__);
+}
+
+static ssize_t displayport_link_store(struct class *dev,
+ struct class_attribute *attr, const char *buf, size_t size)
+{
+ int mode = 0;
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ if (kstrtoint(buf, 10, &mode))
+ return size;
+ pr_info("%s mode=%d\n", __func__, mode);
+
+ if (mode == 0) {
+ displayport_hpd_changed(0);
+ displayport_link_sink_status_read();
+ } else if (mode == 8) {
+ queue_delayed_work(displayport->dp_wq, &displayport->hpd_irq_work, 0);
+ } else if (mode == 9) {
+ displayport_hpd_changed(1);
+ } else {
+ u8 link_rate = mode/10;
+ u8 lane_cnt = mode%10;
+
+ if ((link_rate >= 1 && link_rate <= 3) &&
+ (lane_cnt == 1 || lane_cnt == 2 || lane_cnt == 4)) {
+ if (link_rate == 3)
+ link_rate = LINK_RATE_5_4Gbps;
+ else if (link_rate == 2)
+ link_rate = LINK_RATE_2_7Gbps;
+ else
+ link_rate = LINK_RATE_1_62Gbps;
+
+ pr_info("%s: %02x %02x\n", __func__, link_rate, lane_cnt);
+ displayport_reg_init(); /* for AUX ch read/write. */
+
+ displayport_link_status_read();
+
+ g_displayport_debug_param.param_used = 1;
+ g_displayport_debug_param.link_rate = link_rate;
+ g_displayport_debug_param.lane_cnt = lane_cnt;
+
+ displayport_full_link_training();
+
+ g_displayport_debug_param.param_used = 0;
+
+ displayport_set_switch_state(displayport, 1);
+ displayport_info("HPD status = %d\n", 1);
+ } else {
+ pr_err("%s: Not support command[%d]\n",
+ __func__, mode);
+ }
+ }
+
+ return size;
+}
+
+static CLASS_ATTR(link, 0664, displayport_link_show, displayport_link_store);
+
+static ssize_t displayport_test_bpc_show(struct class *class,
+ struct class_attribute *attr,
+ char *buf)
+{
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ return snprintf(buf, PAGE_SIZE, "displayport bpc %d\n", (displayport->bpc == BPC_6)?6:8);
+}
+static ssize_t displayport_test_bpc_store(struct class *dev,
+ struct class_attribute *attr,
+ const char *buf, size_t size)
+{
+ int mode = 0;
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ if (kstrtoint(buf, 10, &mode))
+ return size;
+ pr_info("%s mode=%d\n", __func__, mode);
+
+ switch (mode) {
+ case 6:
+ displayport->bpc = BPC_6;
+ break;
+ case 8:
+ displayport->bpc = BPC_8;
+ break;
+ default:
+ pr_err("%s: Not support command[%d]\n",
+ __func__, mode);
+ break;
+ }
+
+ return size;
+}
+
+static CLASS_ATTR(bpc, 0664, displayport_test_bpc_show, displayport_test_bpc_store);
+
+static ssize_t displayport_test_range_show(struct class *class,
+ struct class_attribute *attr,
+ char *buf)
+{
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ return sprintf(buf, "displayport range %s\n",
+ (displayport->dyn_range == VESA_RANGE)?"VESA_RANGE":"CEA_RANGE");
+}
+static ssize_t displayport_test_range_store(struct class *dev,
+ struct class_attribute *attr,
+ const char *buf, size_t size)
+{
+ int mode = 0;
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ if (kstrtoint(buf, 10, &mode))
+ return size;
+ pr_info("%s mode=%d\n", __func__, mode);
+
+ switch (mode) {
+ case 0:
+ displayport->dyn_range = VESA_RANGE;
+ break;
+ case 1:
+ displayport->dyn_range = CEA_RANGE;
+ break;
+ default:
+ pr_err("%s: Not support command[%d]\n",
+ __func__, mode);
+ break;
+ }
+
+ return size;
+}
+
+static CLASS_ATTR(range, 0664, displayport_test_range_show, displayport_test_range_store);
+
+static ssize_t displayport_test_edid_show(struct class *class,
+ struct class_attribute *attr,
+ char *buf)
+{
+ struct v4l2_dv_timings edid_preset;
+ int i;
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ edid_preset = edid_preferred_preset();
+
+ i = displayport_timing2conf(&edid_preset);
+ if (i < 0) {
+ i = displayport->cur_video;
+ pr_err("displayport timings not supported\n");
+ }
+
+ return snprintf(buf, PAGE_SIZE, "displayport preferred_preset = %d %d %d\n",
+ videoformat_parameters[i].active_pixel,
+ videoformat_parameters[i].active_line,
+ videoformat_parameters[i].fps);
+}
+
+static ssize_t displayport_test_edid_store(struct class *dev,
+ struct class_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct v4l2_dv_timings edid_preset;
+ int i;
+ int mode = 0;
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ if (kstrtoint(buf, 10, &mode))
+ return size;
+ pr_info("%s mode=%d\n", __func__, mode);
+
+ edid_set_preferred_preset(mode);
+ edid_preset = edid_preferred_preset();
+ i = displayport_timing2conf(&edid_preset);
+ if (i < 0) {
+ i = displayport->cur_video;
+ pr_err("displayport timings not supported\n");
+ }
+
+ pr_info("displayport preferred_preset = %d %d %d\n",
+ videoformat_parameters[i].active_pixel,
+ videoformat_parameters[i].active_line,
+ videoformat_parameters[i].fps);
+
+ return size;
+}
+static CLASS_ATTR(edid, 0664, displayport_test_edid_show, displayport_test_edid_store);
+
+static ssize_t displayport_test_bist_show(struct class *class,
+ struct class_attribute *attr,
+ char *buf)
+{
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ return snprintf(buf, PAGE_SIZE, "displayport bist used %d type %d\n",
+ displayport->bist_used,
+ displayport->bist_type);
+}
+
+static ssize_t displayport_test_bist_store(struct class *dev,
+ struct class_attribute *attr,
+ const char *buf, size_t size)
+{
+ int mode = 0;
+ struct displayport_audio_config_data audio_config_data;
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ if (kstrtoint(buf, 10, &mode))
+ return size;
+ pr_info("%s mode=%d\n", __func__, mode);
+
+ switch (mode) {
+ case 0:
+ displayport->bist_used = 0;
+ displayport->bist_type = COLOR_BAR;
+ break;
+ case 1:
+ displayport->bist_used = 1;
+ displayport->bist_type = COLOR_BAR;
+ break;
+ case 2:
+ displayport->bist_used = 1;
+ displayport->bist_type = WGB_BAR;
+ break;
+ case 3:
+ displayport->bist_used = 1;
+ displayport->bist_type = MW_BAR;
+ break;
+ case 11:
+ case 12:
+ audio_config_data.audio_enable = 1;
+ audio_config_data.audio_fs = FS_192KHZ;
+ audio_config_data.audio_channel_cnt = mode-10;
+ audio_config_data.audio_bit = 0;
+ audio_config_data.audio_packed_mode = 0;
+ audio_config_data.audio_word_length = 0;
+ displayport_audio_bist_enable(audio_config_data);
+ break;
+ default:
+ pr_err("%s: Not support command[%d]\n",
+ __func__, mode);
+ break;
+ }
+
+ return size;
+}
+static CLASS_ATTR(bist, 0664, displayport_test_bist_show, displayport_test_bist_store);
+
+static ssize_t displayport_test_show(struct class *class,
+ struct class_attribute *attr,
+ char *buf)
+{
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ return snprintf(buf, PAGE_SIZE, "displayport gpio oe %d, sel %d, direction %d\n",
+ gpio_get_value(displayport->gpio_sw_oe),
+ gpio_get_value(displayport->gpio_sw_sel),
+ gpio_get_value(displayport->gpio_usb_dir));
+}
+static ssize_t displayport_test_store(struct class *dev,
+ struct class_attribute *attr,
+ const char *buf, size_t size)
+{
+ /* struct displayport_device *displayport = get_displayport_drvdata(); */
+
+ return size;
+}
+
+static CLASS_ATTR(dp, 0664, displayport_test_show, displayport_test_store);
+
+extern int forced_resolution;
+static ssize_t displayport_forced_resolution_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ int ret = 0;
+ int i;
+
+ for (i = 0; i < supported_videos_pre_cnt; i++) {
+ ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%c %2d : %s\n",
+ forced_resolution == i ? '*':' ', i,
+ supported_videos[i].name);
+ }
+
+ return ret;
+}
+
+static ssize_t displayport_forced_resolution_store(struct class *dev,
+ struct class_attribute *attr, const char *buf, size_t size)
+{
+ int val[4] = {0,};
+
+ get_options(buf, 4, val);
+
+ reduced_resolution = 0;
+
+ if (val[1] < 0 || val[1] >= supported_videos_pre_cnt || val[0] < 1)
+ forced_resolution = -1;
+ else {
+ struct displayport_device *displayport = get_displayport_drvdata();
+ int hpd_stat = displayport->hpd_current_state;
+
+ forced_resolution = val[1];
+ if (hpd_stat) {
+ displayport_hpd_changed(0);
+ msleep(100);
+ displayport_hpd_changed(1);
+ }
+ }
+
+ return size;
+}
+static CLASS_ATTR(forced_resolution, 0664, displayport_forced_resolution_show,
+ displayport_forced_resolution_store);
+
+static ssize_t displayport_reduced_resolution_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ int ret = 0;
+
+ ret = scnprintf(buf, PAGE_SIZE, "%llu\n", reduced_resolution);
+
+ return ret;
+}
+
+static ssize_t displayport_reduced_resolution_store(struct class *dev,
+ struct class_attribute *attr, const char *buf, size_t size)
+{
+ int val[4] = {0,};
+
+ get_options(buf, 4, val);
+
+ forced_resolution = -1;
+
+ if (val[1] < 0 || val[1] >= supported_videos_pre_cnt || val[0] < 1)
+ reduced_resolution = 0;
+ else {
+ switch (val[1]) {
+ case 1:
+ reduced_resolution = PIXELCLK_2160P30HZ;
+ break;
+ case 2:
+ reduced_resolution = PIXELCLK_1080P60HZ;
+ break;
+ case 3:
+ reduced_resolution = PIXELCLK_1080P30HZ;
+ break;
+ default:
+ reduced_resolution = 0;
+ };
+ }
+
+ return size;
+}
+static CLASS_ATTR(reduced_resolution, 0664, displayport_reduced_resolution_show,
+ displayport_reduced_resolution_store);
+
+static ssize_t displayport_aux_sw_sel_store(struct class *dev,
+ struct class_attribute *attr, const char *buf, size_t size)
+{
+ struct displayport_device *displayport = get_displayport_drvdata();
+ int val[10] = {0,};
+ int aux_sw_sel, aux_sw_oe;
+
+ get_options(buf, 10, val);
+
+ aux_sw_sel = val[1];
+ aux_sw_oe = val[2];
+ displayport_info("sbu_sw_sel(%d), sbu_sw_oe(%d)\n", aux_sw_sel, aux_sw_oe);
+
+ if ((aux_sw_sel == 0 || aux_sw_sel == 1) && (aux_sw_oe == 0 || aux_sw_oe == 1)) {
+ if (gpio_is_valid(displayport->gpio_sw_sel))
+ gpio_direction_output(displayport->gpio_sw_sel, aux_sw_sel);
+ displayport_aux_onoff(displayport, !aux_sw_oe);
+ } else
+ displayport_err("invalid aux switch parameter\n");
+
+ return size;
+}
+static CLASS_ATTR(dp_sbu_sw_sel, 0664, NULL, displayport_aux_sw_sel_store);
+
+static ssize_t displayport_log_level_show(struct class *class,
+ struct class_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "displayport log level %1d\n", displayport_log_level);
+}
+static ssize_t displayport_log_level_store(struct class *dev,
+ struct class_attribute *attr,
+ const char *buf, size_t size)
+{
+ int mode = 0;
+
+ if (kstrtoint(buf, 10, &mode))
+ return size;
+ displayport_log_level = mode;
+ displayport_err("log level = %d\n", displayport_log_level);
+
+ return size;
+}
+
+static CLASS_ATTR(log_level, 0664, displayport_log_level_show, displayport_log_level_store);
+#endif
+
+static int displayport_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct device *dev = &pdev->dev;
+ struct displayport_device *displayport = NULL;
+#ifdef DISPLAYPORT_TEST
+ struct class *dp_class;
+#endif
+ dev_info(dev, "%s start\n", __func__);
+
+ displayport = devm_kzalloc(dev, sizeof(struct displayport_device), GFP_KERNEL);
+ if (!displayport) {
+ displayport_err("failed to allocate displayport device.\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+#if !defined(CONFIG_SUPPORT_LEGACY_ION)
+ dma_set_mask(dev, DMA_BIT_MASK(36));
+#endif
+ ret = displayport_parse_dt(displayport, dev);
+ if (ret)
+ goto err_dt;
+
+ displayport_drvdata = displayport;
+
+ spin_lock_init(&displayport->slock);
+ mutex_init(&displayport->cmd_lock);
+ mutex_init(&displayport->hpd_lock);
+ mutex_init(&displayport->aux_lock);
+ mutex_init(&displayport->training_lock);
+ init_waitqueue_head(&displayport->dp_wait);
+ init_waitqueue_head(&displayport->audio_wait);
+
+ ret = displayport_init_resources(displayport, pdev);
+ if (ret)
+ goto err_dt;
+
+ displayport_init_subdev(displayport);
+ platform_set_drvdata(pdev, displayport);
+
+ displayport->dp_wq = create_singlethread_workqueue(dev_name(&pdev->dev));
+ if (!displayport->dp_wq) {
+ displayport_err("create wq failed.\n");
+ goto err_dt;
+ }
+
+ displayport->hdcp2_wq = create_singlethread_workqueue(dev_name(&pdev->dev));
+ if (!displayport->hdcp2_wq) {
+ displayport_err("create hdcp2_wq failed.\n");
+ goto err_dt;
+ }
+
+ INIT_DELAYED_WORK(&displayport->hpd_plug_work, displayport_hpd_plug_work);
+ INIT_DELAYED_WORK(&displayport->hpd_unplug_work, displayport_hpd_unplug_work);
+ INIT_DELAYED_WORK(&displayport->hpd_irq_work, displayport_hpd_irq_work);
+ INIT_DELAYED_WORK(&displayport->hdcp13_work, displayport_hdcp13_run);
+ INIT_DELAYED_WORK(&displayport->hdcp22_work, displayport_hdcp22_run);
+ INIT_DELAYED_WORK(&displayport->hdcp13_integrity_check_work, displayport_hdcp13_integrity_check_work);
+
+#if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER)
+ INIT_DELAYED_WORK(&displayport->notifier_register_work,
+ displayport_notifier_register_work);
+ queue_delayed_work(displayport->dp_wq, &displayport->notifier_register_work,
+ msecs_to_jiffies(30000));
+#endif
+
+#if defined(CONFIG_EXTCON)
+ /* register the extcon device for HPD */
+ displayport->extcon_displayport = devm_extcon_dev_allocate(displayport->dev, extcon_id);
+ if (IS_ERR(displayport->extcon_displayport)) {
+ displayport_err("displayport extcon dev_allocate failed.\n");
+ goto err_dt;
+ }
+
+ ret = devm_extcon_dev_register(displayport->dev, displayport->extcon_displayport);
+ if (ret) {
+ displayport_err("hdmi extcon register failed.\n");
+ goto err_dt;
+ }
+#if 0
+ displayport->audio_switch.name = "ch_hdmi_audio";
+
+ ret = devm_extcon_dev_allocate(displayport->dev, 2);
+ if (ret) {
+ displayport_err("audio extcon dev_allocate failed.\n");
+ goto err_dt;
+ }
+
+ ret = devm_extcon_dev_register(displayport->dev, extcon_id);
+ if (ret) {
+ displayport_err("audio extcon register failed.\n");
+ goto err_dt;
+ }
+#endif
+#else
+ displayport_info("Not compiled EXTCON driver\n");
+#endif
+ displayport->hpd_state = HPD_UNPLUG;
+
+ pm_runtime_enable(dev);
+
+ ret = iovmm_activate(dev);
+ if (ret) {
+ displayport_err("failed to activate iovmm\n");
+ goto err_dt;
+ }
+ iovmm_set_fault_handler(dev, dpu_sysmmu_fault_handler, NULL);
+
+#if defined(CONFIG_CPU_IDLE)
+ displayport->idle_ip_index =
+ exynos_get_idle_ip_index(dev_name(&pdev->dev));
+ if (displayport->idle_ip_index < 0)
+ displayport_warn("idle ip index is not provided for DP\n");
+ exynos_update_ip_idle_status(displayport->idle_ip_index, 1);
+#endif
+
+ phy_init(displayport->phy);
+
+ displayport->state = DISPLAYPORT_STATE_INIT;
+
+ ret = device_init_wakeup(displayport->dev, true);
+ if (ret) {
+ dev_err(displayport->dev, "failed to init wakeup device\n");
+ return -EINVAL;
+ }
+
+#ifdef DISPLAYPORT_TEST
+ dp_class = class_create(THIS_MODULE, "dp_sec");
+ if (IS_ERR(dp_class))
+ displayport_err("failed to creat dp_class\n");
+ else {
+ ret = class_create_file(dp_class, &class_attr_link);
+ if (ret)
+ displayport_err("failed to create attr_link\n");
+ ret = class_create_file(dp_class, &class_attr_bpc);
+ if (ret)
+ displayport_err("failed to create attr_bpc\n");
+ ret = class_create_file(dp_class, &class_attr_range);
+ if (ret)
+ displayport_err("failed to create attr_range\n");
+ ret = class_create_file(dp_class, &class_attr_edid);
+ if (ret)
+ displayport_err("failed to create attr_edid\n");
+ ret = class_create_file(dp_class, &class_attr_bist);
+ if (ret)
+ displayport_err("failed to create attr_bist\n");
+ ret = class_create_file(dp_class, &class_attr_dp);
+ if (ret)
+ displayport_err("failed to create attr_test\n");
+ ret = class_create_file(dp_class, &class_attr_forced_resolution);
+ if (ret)
+ displayport_err("failed to create attr_dp_forced_resolution\n");
+ ret = class_create_file(dp_class, &class_attr_reduced_resolution);
+ if (ret)
+ displayport_err("failed to create attr_dp_reduced_resolution\n");
+ ret = class_create_file(dp_class, &class_attr_dp_sbu_sw_sel);
+ if (ret)
+ displayport_err("failed to create class_attr_dp_sbu_sw_sel\n");
+ ret = class_create_file(dp_class, &class_attr_log_level);
+ if (ret)
+ displayport_err("failed to create class_attr_log_level\n");
+ }
+#endif
+ g_displayport_debug_param.param_used = 0;
+ g_displayport_debug_param.link_rate = LINK_RATE_2_7Gbps;
+ g_displayport_debug_param.lane_cnt = 0x04;
+
+ displayport->bpc = BPC_8;
+ displayport->bist_used = 0;
+ displayport->bist_type = COLOR_BAR;
+ displayport->dyn_range = CEA_RANGE;
+ displayport_info("displayport driver has been probed.\n");
+ return 0;
+
+err_dt:
+ kfree(displayport);
+err:
+ return ret;
+}
+
+static void displayport_shutdown(struct platform_device *pdev)
+{
+ struct displayport_device *displayport = platform_get_drvdata(pdev);
+
+ /* DPU_EVENT_LOG(DPU_EVT_DP_SHUTDOWN, &displayport->sd, ktime_set(0, 0)); */
+ displayport_info("%s + state:%d\n", __func__, displayport->state);
+
+ displayport_disable(displayport);
+
+ displayport_info("%s -\n", __func__);
+}
+
+static int displayport_runtime_suspend(struct device *dev)
+{
+ struct displayport_device *displayport = dev_get_drvdata(dev);
+
+ /* DPU_EVENT_LOG(DPU_EVT_DP_SUSPEND, &displayport->sd, ktime_set(0, 0)); */
+ displayport_dbg("%s +\n", __func__);
+ clk_disable_unprepare(displayport->res.aclk);
+ displayport_dbg("%s -\n", __func__);
+ return 0;
+}
+
+static int displayport_runtime_resume(struct device *dev)
+{
+ struct displayport_device *displayport = dev_get_drvdata(dev);
+
+ /* DPU_EVENT_LOG(DPU_EVT_DP_RESUME, &displayport->sd, ktime_set(0, 0)); */
+ displayport_dbg("%s: +\n", __func__);
+ clk_prepare_enable(displayport->res.aclk);
+ displayport_dbg("%s -\n", __func__);
+ return 0;
+}
+
+static const struct of_device_id displayport_of_match[] = {
+ { .compatible = "samsung,exynos-displayport" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, displayport_of_match);
+
+static const struct dev_pm_ops displayport_pm_ops = {
+ .runtime_suspend = displayport_runtime_suspend,
+ .runtime_resume = displayport_runtime_resume,
+};
+
+static struct platform_driver displayport_driver __refdata = {
+ .probe = displayport_probe,
+ .remove = displayport_remove,
+ .shutdown = displayport_shutdown,
+ .driver = {
+ .name = DISPLAYPORT_MODULE_NAME,
+ .owner = THIS_MODULE,
+ .pm = &displayport_pm_ops,
+ .of_match_table = of_match_ptr(displayport_of_match),
+ .suppress_bind_attrs = true,
+ }
+};
+
+static int __init displayport_init(void)
+{
+ int ret = platform_driver_register(&displayport_driver);
+
+ if (ret)
+ pr_err("displayport driver register failed\n");
+
+ return ret;
+}
+late_initcall(displayport_init);
+
+static void __exit displayport_exit(void)
+{
+ platform_driver_unregister(&displayport_driver);
+}
+
+module_exit(displayport_exit);
+MODULE_AUTHOR("Kwangje Kim <kj1.kim@samsung.com>");
+MODULE_DESCRIPTION("Samusung EXYNOS DisplayPort driver");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Samsung SoC DisplayPort EDID 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 <linux/fb.h>
+#include "displayport.h"
+
+#define EDID_SEGMENT_ADDR (0x60 >> 1)
+#define EDID_ADDR (0xA0 >> 1)
+#define EDID_SEGMENT_IGNORE (2)
+#define EDID_BLOCK_SIZE 128
+#define EDID_SEGMENT(x) ((x) >> 1)
+#define EDID_OFFSET(x) (((x) & 1) * EDID_BLOCK_SIZE)
+#define EDID_EXTENSION_FLAG 0x7E
+#define EDID_NATIVE_FORMAT 0x83
+#define EDID_BASIC_AUDIO (1 << 6)
+
+int forced_resolution = -1;
+
+static struct fb_videomode ud_mode_h14b_vsdb[] = {
+ {"3840x2160p@30", 30, 3840, 2160, 297000000, 0, 0, 0, 0, 0, 0, 0, FB_VMODE_NONINTERLACED, 0},
+ {"3840x2160p@25", 25, 3840, 2160, 297000000, 0, 0, 0, 0, 0, 0, 0, FB_VMODE_NONINTERLACED, 0},
+ {"3840x2160p@24", 24, 3840, 2160, 297000000, 0, 0, 0, 0, 0, 0, 0, FB_VMODE_NONINTERLACED, 0},
+ {"4096x2160p@24", 24, 4096, 2160, 297000000, 0, 0, 0, 0, 0, 0, 0, FB_VMODE_NONINTERLACED, 0},
+};
+
+static struct v4l2_dv_timings preferred_preset = V4L2_DV_BT_DMT_640X480P60;
+static u32 edid_misc;
+static int audio_channels;
+static int audio_bit_rates;
+static int audio_sample_rates;
+static int audio_speaker_alloc;
+
+void edid_check_set_i2c_capabilities(void)
+{
+ u8 val[1];
+
+ displayport_reg_dpcd_read(DPCD_ADD_I2C_SPEED_CONTROL_CAPABILITES, 1, val);
+ displayport_info("DPCD_ADD_I2C_SPEED_CONTROL_CAPABILITES = 0x%x\n", val[0]);
+
+ if (val[0] != 0) {
+ if (val[0] & I2C_1Mbps)
+ val[0] = I2C_1Mbps;
+ else if (val[0] & I2C_400Kbps)
+ val[0] = I2C_400Kbps;
+ else if (val[0] & I2C_100Kbps)
+ val[0] = I2C_100Kbps;
+ else if (val[0] & I2C_10Kbps)
+ val[0] = I2C_10Kbps;
+ else if (val[0] & I2C_1Kbps)
+ val[0] = I2C_1Kbps;
+ else
+ val[0] = I2C_400Kbps;
+
+ displayport_reg_dpcd_write(DPCD_ADD_I2C_SPEED_CONTROL_STATUS, 1, val);
+ displayport_dbg("DPCD_ADD_I2C_SPEED_CONTROL_STATUS = 0x%x\n", val[0]);
+ }
+}
+
+static int edid_read_block(struct displayport_device *hdev, int block, u8 *buf, size_t len)
+{
+ int ret, i;
+ u8 offset = EDID_OFFSET(block);
+ int sum = 0;
+
+ if (len < EDID_BLOCK_SIZE)
+ return -EINVAL;
+
+ edid_check_set_i2c_capabilities();
+ ret = displayport_reg_edid_read(offset, EDID_BLOCK_SIZE, buf);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < EDID_BLOCK_SIZE; i++)
+ sum += buf[i];
+
+ print_hex_dump(KERN_INFO, "EDID = ", DUMP_PREFIX_OFFSET, 16, 1,
+ buf, 128, false);
+ sum%=0x100; //Checksum. Sum of all 128 bytes should equal 0 (mod 256).
+ if (sum) {
+ displayport_err("%s: checksum error block = %d sum = %02x\n", __func__, block, sum);
+ return -EPROTO;
+ }
+
+ return 0;
+}
+
+int edid_read(struct displayport_device *hdev, u8 **data)
+{
+ u8 block0[EDID_BLOCK_SIZE];
+ u8 *edid;
+ int block = 0;
+ int block_cnt, ret;
+
+ ret = edid_read_block(hdev, 0, block0, sizeof(block0));
+ if (ret)
+ return ret;
+
+ block_cnt = block0[EDID_EXTENSION_FLAG] + 1;
+ displayport_info("block_cnt = %d\n", block_cnt);
+
+ edid = kmalloc(block_cnt * EDID_BLOCK_SIZE, GFP_KERNEL);
+ if (!edid)
+ return -ENOMEM;
+
+ memcpy(edid, block0, sizeof(block0));
+
+ while (++block < block_cnt) {
+ ret = edid_read_block(hdev, block,
+ edid + block * EDID_BLOCK_SIZE,
+ EDID_BLOCK_SIZE);
+
+ if (ret) {
+ kfree(edid);
+ return ret;
+ }
+ }
+
+ *data = edid;
+
+ return block_cnt;
+}
+
+static int get_ud_timing(struct fb_vendor *vsdb, int vic_idx)
+{
+ unsigned char val = 0;
+ int idx = -EINVAL;
+
+ val = vsdb->vic_data[vic_idx];
+ switch (val) {
+ case 0x01:
+ idx = 0;
+ break;
+ case 0x02:
+ idx = 1;
+ break;
+ case 0x03:
+ idx = 2;
+ break;
+ case 0x04:
+ idx = 3;
+ break;
+ }
+
+ return idx;
+}
+
+bool edid_find_max_resolution(const struct v4l2_dv_timings *t1,
+ const struct v4l2_dv_timings *t2)
+{
+ if ((t1->bt.width * t1->bt.height < t2->bt.width * t2->bt.height) ||
+ ((t1->bt.width * t1->bt.height == t2->bt.width * t2->bt.height) &&
+ (t1->bt.pixelclock < t2->bt.pixelclock)))
+ return true;
+
+ return false;
+}
+
+static void edid_find_preset(const struct fb_videomode *mode)
+{
+ int i;
+
+ for (i = 0; i < supported_videos_pre_cnt; i++) {
+ if (mode->refresh == supported_videos[i].fps &&
+ mode->xres == supported_videos[i].dv_timings.bt.width &&
+ mode->yres == supported_videos[i].dv_timings.bt.height) {
+ if (supported_videos[i].edid_support_match == false) {
+ displayport_info("EDID: found supported_videos : %s\n", supported_videos[i].name);
+ supported_videos[i].edid_support_match = true;
+ preferred_preset = supported_videos[i].dv_timings;
+ }
+ }
+ }
+}
+
+static void edid_use_default_preset(void)
+{
+ int i;
+
+ if (forced_resolution >= 0)
+ preferred_preset = supported_videos[forced_resolution].dv_timings;
+ else
+ preferred_preset = supported_videos[EDID_DEFAULT_TIMINGS_IDX].dv_timings;
+
+ for (i = 0; i < supported_videos_pre_cnt; i++) {
+ supported_videos[i].edid_support_match =
+ v4l2_match_dv_timings(&supported_videos[i].dv_timings,
+ &preferred_preset, 0, 0);
+ }
+
+ audio_channels = 2;
+}
+
+void edid_set_preferred_preset(int mode)
+{
+ int i;
+
+ preferred_preset = supported_videos[mode].dv_timings;
+ for (i = 0; i < supported_videos_pre_cnt; i++) {
+ supported_videos[i].edid_support_match =
+ v4l2_match_dv_timings(&supported_videos[i].dv_timings,
+ &preferred_preset, 0, 0);
+ }
+}
+
+int edid_find_resolution(u16 xres, u16 yres, u16 refresh)
+{
+ int i;
+ int ret=0;
+
+ for (i = 0; i < supported_videos_pre_cnt; i++) {
+ if (refresh == supported_videos[i].fps &&
+ xres == supported_videos[i].dv_timings.bt.width &&
+ yres == supported_videos[i].dv_timings.bt.height) {
+ return i;
+ }
+ }
+ return ret;
+}
+
+void edid_parse_hdmi14_vsdb(unsigned char *edid_ext_blk,
+ struct fb_vendor *vsdb, int block_cnt)
+{
+ int i, j;
+ int hdmi_vic_len;
+ int vsdb_offset_calc = VSDB_VIC_FIELD_OFFSET;
+
+ for (i = 0; i < (block_cnt - 1) * EDID_BLOCK_SIZE; i++) {
+ if ((edid_ext_blk[i] & DATA_BLOCK_TAG_CODE_MASK)
+ == (VSDB_TAG_CODE << DATA_BLOCK_TAG_CODE_BIT_POSITION)
+ && edid_ext_blk[i + IEEE_OUI_0_BYTE_NUM] == HDMI14_IEEE_OUI_0
+ && edid_ext_blk[i + IEEE_OUI_1_BYTE_NUM] == HDMI14_IEEE_OUI_1
+ && edid_ext_blk[i + IEEE_OUI_2_BYTE_NUM] == HDMI14_IEEE_OUI_2) {
+ displayport_dbg("EDID: find VSDB for HDMI 1.4\n");
+
+ if (edid_ext_blk[i + 8] & VSDB_HDMI_VIDEO_PRESETNT_MASK) {
+ displayport_dbg("EDID: Find HDMI_Video_present in VSDB\n");
+
+ if (!(edid_ext_blk[i + 8] & VSDB_LATENCY_FILEDS_PRESETNT_MASK)) {
+ vsdb_offset_calc = vsdb_offset_calc - 2;
+ displayport_dbg("EDID: Not support LATENCY_FILEDS_PRESETNT in VSDB\n");
+ }
+
+ if (!(edid_ext_blk[i + 8] & VSDB_I_LATENCY_FILEDS_PRESETNT_MASK)) {
+ vsdb_offset_calc = vsdb_offset_calc - 2;
+ displayport_dbg("EDID: Not support I_LATENCY_FILEDS_PRESETNT in VSDB\n");
+ }
+
+ hdmi_vic_len = (edid_ext_blk[i + vsdb_offset_calc]
+ & VSDB_VIC_LENGTH_MASK) >> VSDB_VIC_LENGTH_BIT_POSITION;
+
+ if (hdmi_vic_len > 0) {
+ vsdb->vic_len = hdmi_vic_len;
+
+ for (j = 0; j < hdmi_vic_len; j++)
+ vsdb->vic_data[j] = edid_ext_blk[i + vsdb_offset_calc + j + 1];
+
+ break;
+ } else {
+ vsdb->vic_len = 0;
+ displayport_dbg("EDID: No hdmi vic data in VSDB\n");
+ break;
+ }
+ } else
+ displayport_dbg("EDID: Not support HDMI_Video_present in VSDB\n");
+ }
+ }
+
+ if (i >= (block_cnt - 1) * EDID_BLOCK_SIZE) {
+ vsdb->vic_len = 0;
+ displayport_dbg("EDID: can't find VSDB for HDMI 1.4 block\n");
+ }
+}
+
+void edid_find_hdmi14_vsdb_update(struct fb_vendor *vsdb)
+{
+ int udmode_idx, vic_idx;
+
+ if (!vsdb)
+ return;
+
+ /* find UHD preset in HDMI 1.4 vsdb block*/
+ if (vsdb->vic_len) {
+ for (vic_idx = 0; vic_idx < vsdb->vic_len; vic_idx++) {
+ udmode_idx = get_ud_timing(vsdb, vic_idx);
+
+ displayport_dbg("EDID: udmode_idx = %d\n", udmode_idx);
+
+ if (udmode_idx >= 0)
+ edid_find_preset(&ud_mode_h14b_vsdb[udmode_idx]);
+ }
+ }
+}
+
+void edid_parse_hdmi20_vsdb(unsigned char *edid_ext_blk,
+ struct fb_vendor *vsdb, int block_cnt)
+{
+ int i;
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ displayport->rx_edid_data.max_support_clk = 0;
+ displayport->rx_edid_data.support_10bpc = 0;
+
+ for (i = 0; i < (block_cnt - 1) * EDID_BLOCK_SIZE; i++) {
+ if ((edid_ext_blk[i] & DATA_BLOCK_TAG_CODE_MASK)
+ == (VSDB_TAG_CODE << DATA_BLOCK_TAG_CODE_BIT_POSITION)
+ && edid_ext_blk[i + IEEE_OUI_0_BYTE_NUM] == HDMI20_IEEE_OUI_0
+ && edid_ext_blk[i + IEEE_OUI_1_BYTE_NUM] == HDMI20_IEEE_OUI_1
+ && edid_ext_blk[i + IEEE_OUI_2_BYTE_NUM] == HDMI20_IEEE_OUI_2) {
+ displayport_dbg("EDID: find VSDB for HDMI 2.0\n");
+
+ /* Max_TMDS_Character_Rate * 5Mhz */
+ displayport->rx_edid_data.max_support_clk =
+ edid_ext_blk[i + MAX_TMDS_RATE_BYTE_NUM] * 5;
+ displayport_dbg("EDID: Max_TMDS_Character_Rate = %d Mhz\n",
+ displayport->rx_edid_data.max_support_clk);
+
+ if (edid_ext_blk[i + DC_SUPPORT_BYTE_NUM] & DC_30BIT)
+ displayport->rx_edid_data.support_10bpc = 1;
+ else
+ displayport->rx_edid_data.support_10bpc = 0;
+
+ displayport_dbg("EDID: 10 bpc support = %d\n",
+ displayport->rx_edid_data.support_10bpc);
+
+ break;
+ }
+ }
+
+ if (i >= (block_cnt - 1) * EDID_BLOCK_SIZE) {
+ vsdb->vic_len = 0;
+ displayport_dbg("EDID: can't find VSDB for HDMI 2.0 block\n");
+ }
+}
+
+void edid_parse_hdr_metadata(unsigned char *edid_ext_blk, int block_cnt)
+{
+ int i;
+ struct displayport_device *displayport = get_displayport_drvdata();
+
+ displayport->rx_edid_data.hdr_support = 0;
+ displayport->rx_edid_data.eotf = 0;
+ displayport->rx_edid_data.max_lumi_data = 0;
+ displayport->rx_edid_data.max_average_lumi_data = 0;
+ displayport->rx_edid_data.min_lumi_data = 0;
+
+ for (i = 0; i < (block_cnt - 1) * EDID_BLOCK_SIZE; i++) {
+ if ((edid_ext_blk[i] & DATA_BLOCK_TAG_CODE_MASK)
+ == (USE_EXTENDED_TAG_CODE << DATA_BLOCK_TAG_CODE_BIT_POSITION)
+ && edid_ext_blk[i + EXTENDED_TAG_CODE_BYTE_NUM]
+ == EXTENDED_HDR_TAG_CODE) {
+ displayport_dbg("EDID: find HDR Metadata Data Block\n");
+
+ displayport->rx_edid_data.eotf =
+ edid_ext_blk[i + SUPPORTED_EOTF_BYTE_NUM];
+ displayport_dbg("EDID: SUPPORTED_EOTF = 0x%x\n",
+ displayport->rx_edid_data.eotf);
+
+ if (displayport->rx_edid_data.eotf & SMPTE_ST_2084) {
+ displayport->rx_edid_data.hdr_support = 1;
+ displayport_dbg("EDID: SMPTE_ST_2084 support\n");
+ }
+
+ displayport->rx_edid_data.max_lumi_data =
+ edid_ext_blk[i + MAX_LUMI_BYTE_NUM];
+ displayport_dbg("EDID: MAX_LUMI = 0x%x\n",
+ displayport->rx_edid_data.max_lumi_data);
+
+ displayport->rx_edid_data.max_average_lumi_data =
+ edid_ext_blk[i + MAX_AVERAGE_LUMI_BYTE_NUM];
+ displayport_dbg("EDID: MAX_AVERAGE_LUMI = 0x%x\n",
+ displayport->rx_edid_data.max_average_lumi_data);
+
+ displayport->rx_edid_data.min_lumi_data =
+ edid_ext_blk[i + MIN_LUMI_BYTE_NUM];
+ displayport_dbg("EDID: MIN_LUMI = 0x%x\n",
+ displayport->rx_edid_data.min_lumi_data);
+
+ break;
+ }
+ }
+
+ if (i >= (block_cnt - 1) * EDID_BLOCK_SIZE)
+ displayport_dbg("EDID: can't find HDR Metadata Data Block\n");
+}
+
+void edid_find_preset_in_video_data_block(u8 vic)
+{
+ int i;
+
+ for (i = 0; i < supported_videos_pre_cnt; i++) {
+ if ((vic != 0) && (supported_videos[i].vic == vic))
+ supported_videos[i].edid_support_match = true;
+ }
+}
+
+static int edid_parse_audio_video_db(unsigned char *edid, struct fb_audio *sad)
+{
+ int i;
+ u8 pos = 4;
+
+ if (!edid)
+ return -EINVAL;
+
+ if (edid[0] != 0x2 || edid[1] != 0x3 ||
+ edid[2] < 4 || edid[2] > 128 - DETAILED_TIMING_DESCRIPTION_SIZE)
+ return -EINVAL;
+
+ if (!sad)
+ return -EINVAL;
+
+ while (pos < edid[2]) {
+ u8 len = edid[pos] & DATA_BLOCK_LENGTH_MASK;
+ u8 type = (edid[pos] >> DATA_BLOCK_TAG_CODE_BIT_POSITION) & 7;
+ displayport_dbg("Data block %u of %u bytes\n", type, len);
+
+ if (len == 0)
+ break;
+
+ pos++;
+ if (type == AUDIO_DATA_BLOCK) {
+ for (i = pos; i < pos + len; i += 3) {
+ if (((edid[i] >> 3) & 0xf) != 1)
+ continue; /* skip non-lpcm */
+
+ displayport_dbg("LPCM ch=%d\n", (edid[i] & 7) + 1);
+
+ sad->channel_count |= 1 << (edid[i] & 0x7);
+ sad->sample_rates |= (edid[i + 1] & 0x7F);
+ sad->bit_rates |= (edid[i + 2] & 0x7);
+
+ displayport_dbg("ch:0x%X, sample:0x%X, bitrate:0x%X\n",
+ sad->channel_count, sad->sample_rates, sad->bit_rates);
+ }
+ } else if (type == VIDEO_DATA_BLOCK) {
+ for (i = pos; i < pos + len; i++) {
+ u8 vic = edid[i] & SVD_VIC_MASK;
+ edid_find_preset_in_video_data_block(vic);
+ displayport_dbg("EDID: Video data block vic:%d %s\n",
+ vic, supported_videos[i].name);
+ }
+ } else if (type == SPEAKER_DATA_BLOCK) {
+ sad->speaker |= edid[pos] & 0xff;
+ displayport_dbg("EDID: speaker 0x%X\n", sad->speaker);
+ }
+
+ pos += len;
+ }
+
+ return 0;
+}
+
+int edid_update(struct displayport_device *hdev)
+{
+ struct fb_monspecs specs;
+ struct fb_vendor vsdb;
+ struct fb_audio sad;
+ bool first = true;
+ u8 *edid = NULL;
+ int block_cnt = 0;
+ int i;
+ int basic_audio = 0;
+
+ audio_channels = 0;
+ audio_sample_rates = 0;
+ audio_bit_rates = 0;
+ audio_speaker_alloc = 0;
+
+ edid_misc = 0;
+ memset(&vsdb, 0, sizeof(vsdb));
+ memset(&specs, 0, sizeof(specs));
+ memset(&sad, 0, sizeof(sad));
+
+ block_cnt = edid_read(hdev, &edid);
+ if (block_cnt < 0)
+ goto out;
+
+ preferred_preset = supported_videos[EDID_DEFAULT_TIMINGS_IDX].dv_timings;
+
+ for (i = 0; i < supported_videos_pre_cnt; i++)
+ supported_videos[i].edid_support_match = false;
+
+ fb_edid_to_monspecs(edid, &specs);
+
+ for (i = 1; i < block_cnt; i++)
+ fb_edid_add_monspecs(edid + i * EDID_BLOCK_SIZE, &specs);
+
+ /* find 2D preset */
+ for (i = 0; i < specs.modedb_len; i++)
+ edid_find_preset(&specs.modedb[i]);
+
+ /* number of 128bytes blocks to follow */
+ if (block_cnt <= 1)
+ goto out;
+
+ if (edid[EDID_NATIVE_FORMAT] & EDID_BASIC_AUDIO) {
+ basic_audio = 1;
+ edid_misc = FB_MISC_HDMI;
+ }
+
+ edid_parse_hdmi14_vsdb(edid + EDID_BLOCK_SIZE, &vsdb, block_cnt);
+ edid_find_hdmi14_vsdb_update(&vsdb);
+
+ edid_parse_hdmi20_vsdb(edid + EDID_BLOCK_SIZE, &vsdb, block_cnt);
+
+ edid_parse_hdr_metadata(edid + EDID_BLOCK_SIZE, block_cnt);
+
+ for (i = 1; i < block_cnt; i++)
+ edid_parse_audio_video_db(edid + (EDID_BLOCK_SIZE * i), &sad);
+
+ if (!edid_misc)
+ edid_misc = specs.misc;
+
+ for (i = 0; i < supported_videos_pre_cnt; i++) {
+ displayport_dbg("%s edid_support_match = %d\n",
+ supported_videos[i].name, supported_videos[i].edid_support_match);
+
+ if (supported_videos[i].edid_support_match)
+ first = false;
+ }
+
+ if (edid_misc & FB_MISC_HDMI) {
+ audio_speaker_alloc = sad.speaker;
+ if (sad.channel_count) {
+ audio_channels = sad.channel_count;
+ audio_sample_rates = sad.sample_rates;
+ audio_bit_rates = sad.bit_rates;
+ } else if (basic_audio) {
+ audio_channels = 2;
+ audio_sample_rates = FB_AUDIO_44KHZ; /*default audio info*/
+ audio_bit_rates = FB_AUDIO_16BIT;
+ }
+ }
+
+ displayport_info("misc:0x%X, Audio ch:0x%X, sf:0x%X, br:0x%X",
+ edid_misc, audio_channels, audio_sample_rates, audio_bit_rates);
+
+out:
+ /* No supported preset found, use default */
+ if (forced_resolution >= 0 || first == true) {
+ displayport_info("edid_use_default_preset\n");
+ edid_use_default_preset();
+ }
+
+ if (block_cnt == -EPROTO)
+ edid_misc = FB_MISC_HDMI;
+
+ kfree(edid);
+ return block_cnt;
+}
+
+struct v4l2_dv_timings edid_preferred_preset(void)
+{
+ return preferred_preset;
+}
+
+bool edid_supports_hdmi(struct displayport_device *hdev)
+{
+ return edid_misc & FB_MISC_HDMI;
+}
+
+u32 edid_audio_informs(void)
+{
+ u32 value = 0, ch_info = 0;
+
+ if (audio_channels > 0)
+ ch_info = audio_channels;
+ if (audio_channels > (1 << 5))
+ ch_info |= (1 << 5);
+
+ value = ((audio_sample_rates << 19) | (audio_bit_rates << 16) |
+ (audio_speaker_alloc << 8) | ch_info);
+ value |= (1 << 26); /* 1: DP, 0: HDMI */
+
+ displayport_info("audio info = 0x%X\n", value);
+
+ return value;
+}
+
+u8 edid_read_checksum(void)
+{
+ int ret, i;
+ u8 buf[EDID_BLOCK_SIZE];
+ u8 offset = EDID_OFFSET(0);
+ int sum = 0;
+
+ ret = displayport_reg_edid_read(offset, EDID_BLOCK_SIZE, buf);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < EDID_BLOCK_SIZE; i++)
+ sum += buf[i];
+
+ displayport_info("edid_read_checksum %02x, %02x", sum%265, buf[EDID_BLOCK_SIZE-1]);
+
+ return buf[EDID_BLOCK_SIZE-1];
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Samsung SoC DisplayPort HDCP1.3 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"
+#include "decon.h"
+
+HDCP13 HDCP13_DPCD;
+struct hdcp13_info hdcp13_info;
+
+void hdcp13_dpcd_buffer(void)
+{
+ u8 i = 0;
+
+ for (i = 0; i < sizeof(HDCP13_DPCD.HDCP13_BKSV); i++)
+ HDCP13_DPCD.HDCP13_BKSV[i] = 0x0;
+
+ for (i = 0; i < sizeof(HDCP13_DPCD.HDCP13_R0); i++)
+ HDCP13_DPCD.HDCP13_R0[i] = 0x0;
+
+ for (i = 0; i < sizeof(HDCP13_DPCD.HDCP13_AKSV); i++)
+ HDCP13_DPCD.HDCP13_AKSV[i] = 0x0;
+
+ for (i = 0; i < sizeof(HDCP13_DPCD.HDCP13_AN); i++)
+ HDCP13_DPCD.HDCP13_AN[i] = 0x0;
+
+ for (i = 0; i < sizeof(HDCP13_DPCD.HDCP13_V_H); i++)
+ HDCP13_DPCD.HDCP13_V_H[i] = 0x0;
+
+ HDCP13_DPCD.HDCP13_BCAP[0] = 0x0;
+ HDCP13_DPCD.HDCP13_BSTATUS[0] = 0x0;
+
+ for (i = 0; i < sizeof(HDCP13_DPCD.HDCP13_BINFO); i++)
+ HDCP13_DPCD.HDCP13_BINFO[i] = 0x0;
+
+ for (i = 0; i < sizeof(HDCP13_DPCD.HDCP13_KSV_FIFO); i++)
+ HDCP13_DPCD.HDCP13_KSV_FIFO[i] = 0x0;
+
+ HDCP13_DPCD.HDCP13_AINFO[0] = 0x0;
+}
+
+void hdcp13_dump(char *str, u8 *buf, int size)
+{
+ int i;
+ u8 *buffer = buf;
+
+ displayport_dbg("[HDCP 1.3] %s = 0x", str);
+
+ for (i = 0; i < size; i++)
+ displayport_dbg("%02x ", *(buffer+i));
+
+ displayport_dbg("\n");
+}
+
+void hdcp13_func_en(u32 en)
+{
+ displayport_write_mask(SYSTEM_HPD_CONTROL, en, HPD_EVENT_CTRL_EN);
+ displayport_write_mask(SYSTEM_HPD_CONTROL, en, HPD_FORCE_EN);
+ displayport_write_mask(SYSTEM_HPD_CONTROL, en, HPD_FORCE);
+ displayport_write_mask(SYSTEM_COMMON_FUNCTION_ENABLE, en, HDCP13_FUNC_EN);
+}
+
+u8 hdcp13_read_bcap(void)
+{
+ u8 return_val = 0;
+ u8 hdcp_capa = 0;
+
+ displayport_reg_dpcd_read(ADDR_HDCP13_BCAP, 1, HDCP13_DPCD.HDCP13_BCAP);
+
+ displayport_info("[HDCP 1.3] HDCP13_BCAP= 0x%x\n", HDCP13_DPCD.HDCP13_BCAP[0]);
+
+ if (!(HDCP13_DPCD.HDCP13_BCAP[0] & BCAPS_RESERVED_BIT_MASK)) {
+ hdcp13_info.is_repeater = (HDCP13_DPCD.HDCP13_BCAP[0] & BCAPS_REPEATER) >> 1;
+
+ hdcp_capa = HDCP13_DPCD.HDCP13_BCAP[0] & BCAPS_HDCP_CAPABLE;
+
+ if (hdcp_capa)
+ return_val = 0;
+ else
+ return_val = -EINVAL;
+ } else
+ return_val = -EINVAL;
+
+ return return_val;
+}
+
+void hdcp13_repeater_set(void)
+{
+ displayport_write_mask(HDCP13_CONTROL_0, hdcp13_info.is_repeater, SW_RX_REPEATER);
+}
+
+void hdcp13_write_bksv(void)
+{
+ int i;
+ u32 val = 0;
+
+ for (i = 0; i < 4; i++)
+ val |= HDCP13_DPCD.HDCP13_BKSV[i] << (i * 8);
+
+ displayport_write(HDCP13_BKSV_0, val);
+
+ val = 0;
+ val |= (u32)HDCP13_DPCD.HDCP13_BKSV[4];
+ displayport_write(HDCP13_BKSV_1, val);
+}
+
+u8 hdcp13_read_bksv(void)
+{
+ u8 i = 0;
+ u8 j = 0;
+ int one = 0;
+ u8 ret;
+
+ displayport_reg_dpcd_read_burst(ADDR_HDCP13_BKSV, sizeof(HDCP13_DPCD.HDCP13_BKSV), HDCP13_DPCD.HDCP13_BKSV);
+
+ hdcp13_dump("BKSV", &(HDCP13_DPCD.HDCP13_BKSV[0]), 5);
+
+ for (i = 0; i < sizeof(HDCP13_DPCD.HDCP13_BKSV); i++) {
+ for (j = 0; j < 8; j++) {
+ if (HDCP13_DPCD.HDCP13_BKSV[i] & (0x1 << j))
+ one++;
+ }
+ }
+
+ if (one == 20) {
+ hdcp13_write_bksv();
+
+ displayport_dbg("[HDCP 1.3] Valid Bksv\n");
+ ret = 0;
+ } else {
+ displayport_info("[HDCP 1.3] Invalid Bksv\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+void hdcp13_set_an_val(void)
+{
+ displayport_write_mask(HDCP13_CONTROL_0, 1, SW_STORE_AN);
+
+ displayport_write_mask(HDCP13_CONTROL_0, 0, SW_STORE_AN);
+}
+
+void hdcp13_write_an_val(void)
+{
+ u8 i = 0;
+ u32 val = 0;
+
+ hdcp13_set_an_val();
+
+ val = displayport_read(HDCP13_AN_0);
+ for (i = 0; i < 4; i++)
+ HDCP13_DPCD.HDCP13_AN[i] = (u8)((val >> (i * 8)) & 0xFF);
+
+ val = displayport_read(HDCP13_AN_1);
+ for (i = 0; i < 4; i++)
+ HDCP13_DPCD.HDCP13_AN[i + 4] = (u8)((val >> (i * 8)) & 0xFF);
+
+ displayport_reg_dpcd_write_burst(ADDR_HDCP13_AN, 8, HDCP13_DPCD.HDCP13_AN);
+}
+
+u8 hdcp13_write_aksv(void)
+{
+ u8 i = 0;
+ u32 val = 0;
+ u8 ret;
+
+ if (displayport_read_mask(HDCP13_STATUS, AKSV_VALID)) {
+ val = displayport_read(HDCP13_AKSV_0);
+ for (i = 0; i < 4; i++)
+ HDCP13_DPCD.HDCP13_AKSV[i] = (u8)((val >> (i * 8)) & 0xFF);
+
+ val = displayport_read(HDCP13_AKSV_1);
+ HDCP13_DPCD.HDCP13_AKSV[i] = (u8)(val & 0xFF);
+
+ hdcp13_info.cp_irq_flag = 0;
+ displayport_reg_dpcd_write_burst(ADDR_HDCP13_AKSV, 5, HDCP13_DPCD.HDCP13_AKSV);
+ displayport_dbg("[HDCP 1.3] Valid Aksv\n");
+
+ ret = 0;
+ } else {
+ displayport_info("[HDCP 1.3] Invalid Aksv\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+u8 hdcp13_cmp_ri(void)
+{
+ u8 cnt = 0;
+ u8 ri_retry_cnt = 0;
+ u8 ri[2];
+ u8 ret = 0;
+ u32 val = 0;
+
+ cnt = 0;
+ while (hdcp13_info.cp_irq_flag != 1 && cnt < RI_WAIT_COUNT) {
+ mdelay(RI_AVAILABLE_WAITING);
+ cnt++;
+ }
+
+ if (cnt >= RI_WAIT_COUNT) {
+ displayport_info("[HDCP 1.3] Don't receive CP_IRQ interrupt\n");
+ ret = -EFAULT;
+ }
+
+ hdcp13_info.cp_irq_flag = 0;
+
+ cnt = 0;
+ while ((HDCP13_DPCD.HDCP13_BSTATUS[0] & BSTATUS_R0_AVAILABLE) == 0 && cnt < RI_WAIT_COUNT) {
+ /* R0 Sink Available check */
+ displayport_reg_dpcd_read(ADDR_HDCP13_BSTATUS, 1, HDCP13_DPCD.HDCP13_BSTATUS);
+
+ mdelay(RI_AVAILABLE_WAITING);
+ cnt++;
+ }
+
+ if (cnt >= RI_WAIT_COUNT) {
+ displayport_info("[HDCP 1.3] R0 not available in RX part\n");
+ ret = -EFAULT;
+ }
+
+ while (ri_retry_cnt < RI_READ_RETRY_CNT) {
+ /* Read R0 from Sink */
+ displayport_reg_dpcd_read_burst(ADDR_HDCP13_R0, sizeof(HDCP13_DPCD.HDCP13_R0), HDCP13_DPCD.HDCP13_R0);
+
+ /* Read R0 from Source */
+ val = displayport_read(HDCP13_R0_REG);
+ ri[0] = (u8)(val & 0xFF);
+ ri[1] = (u8)((val >> 8) & 0xFF);
+
+ ri_retry_cnt++;
+
+ if ((ri[0] == HDCP13_DPCD.HDCP13_R0[0]) && (ri[1] == HDCP13_DPCD.HDCP13_R0[1])) {
+ displayport_dbg("[HDCP 1.3] Ri_Tx(0x%02x%02x) == Ri_Rx(0x%02x%02x)\n",
+ ri[1], ri[0], HDCP13_DPCD.HDCP13_R0[1], HDCP13_DPCD.HDCP13_R0[0]);
+
+ ret = 0;
+ break;
+ }
+
+ displayport_info("[HDCP 1.3] Ri_Tx(0x%02x%02x) != Ri_Rx(0x%02x%02x)\n",
+ ri[1], ri[0], HDCP13_DPCD.HDCP13_R0[1], HDCP13_DPCD.HDCP13_R0[0]);
+
+ mdelay(RI_DELAY);
+ ret = -EFAULT;
+ }
+
+ return ret;
+}
+
+void hdcp13_encryption_con(u8 enable)
+{
+ struct decon_device *decon = get_decon_drvdata(2);
+
+ /* wait 2 frames for hdcp encryption enable/disable */
+ decon_wait_for_vsync(decon, VSYNC_TIMEOUT_MSEC);
+ decon_wait_for_vsync(decon, VSYNC_TIMEOUT_MSEC);
+
+ if (enable == 1) {
+ displayport_write_mask(HDCP13_CONTROL_0, ~0, SW_AUTH_OK | HDCP13_ENC_EN);
+ /*displayport_reg_video_mute(0);*/
+ displayport_info("[HDCP 1.3] HDCP13 Encryption Enable\n");
+ } else {
+ /*displayport_reg_video_mute(1);*/
+ displayport_write_mask(HDCP13_CONTROL_0, 0, SW_AUTH_OK | HDCP13_ENC_EN);
+ displayport_info("[HDCP 1.3] HDCP13 Encryption Disable\n");
+ }
+}
+
+void hdcp13_link_integrity_check(void)
+{
+ int i;
+ if (hdcp13_info.link_check == LINK_CHECK_NEED) {
+ displayport_info("[HDCP 1.3] HDCP13_Link_integrity_check\n");
+
+ for (i = 0; i < 10; i++) {
+ displayport_reg_dpcd_read(ADDR_HDCP13_BSTATUS, 1,
+ HDCP13_DPCD.HDCP13_BSTATUS);
+ if ((HDCP13_DPCD.HDCP13_BSTATUS[0] & BSTATUS_REAUTH_REQ) ||
+ (HDCP13_DPCD.HDCP13_BSTATUS[0] & BSTATUS_LINK_INTEGRITY_FAIL)) {
+
+ displayport_info("[HDCP 1.3] HDCP13_DPCD.HDCP13_BSTATUS = %02x : retry(%d)\n",
+ HDCP13_DPCD.HDCP13_BSTATUS[0], i);
+ hdcp13_info.link_check = LINK_CHECK_FAIL;
+ hdcp13_dpcd_buffer();
+ hdcp13_info.auth_state = HDCP13_STATE_FAIL;
+ displayport_reg_video_mute(1);
+ hdcp13_info.cp_irq_flag = 0;
+
+ if (hdcp13_read_bcap() != 0) {
+ displayport_info("[HDCP 1.3] NOT HDCP CAPABLE\n");
+ hdcp13_encryption_con(0);
+ } else {
+ displayport_info("[HDCP 1.3] ReAuth\n");
+ hdcp13_run();
+ }
+ break;
+ }
+ msleep(20);
+ }
+ }
+}
+
+void hdcp13_irq_mask(void)
+{
+ displayport_reg_set_interrupt_mask(HDCP_LINK_CHECK_INT_MASK, 1);
+ displayport_reg_set_interrupt_mask(HDCP_LINK_FAIL_INT_MASK, 1);
+}
+
+void hdcp13_make_sha1_input_buf(u8 *sha1_input_buf, u8 *binfo, u8 device_cnt)
+{
+ int i = 0;
+ u32 val = 0;
+
+ for (i = 0; i < BINFO_SIZE; i++)
+ sha1_input_buf[KSV_SIZE * device_cnt + i] = binfo[i];
+
+ val = displayport_read(HDCP13_AM0_0);
+ for (i = 0; i < 4; i++)
+ sha1_input_buf[KSV_SIZE * device_cnt + BINFO_SIZE + i] =
+ (u8)((val >> (i * 8)) & 0xFF);
+
+ val = displayport_read(HDCP13_AM0_1);
+ for (i = 0; i < 4; i++)
+ sha1_input_buf[KSV_SIZE * device_cnt + BINFO_SIZE + i + 4] =
+ (u8)((val >> (i * 8)) & 0xFF);
+}
+
+void hdcp13_v_value_order_swap(u8 *v_value)
+{
+ int i;
+ u8 temp;
+
+ for (i = 0; i < SHA1_SIZE; i += 4) {
+ temp = v_value[i];
+ v_value[i] = v_value[i + 3];
+ v_value[i + 3] = temp;
+ temp = v_value[i + 1];
+ v_value[i + 1] = v_value[i + 2];
+ v_value[i + 2] = temp;
+ }
+}
+
+int hdcp13_compare_v(u8 *tx_v_value)
+{
+ int i = 0;
+ int ret = 0;
+ u8 v_read_retry_cnt = 0;
+
+ while(v_read_retry_cnt < V_READ_RETRY_CNT) {
+ ret = 0;
+
+ displayport_reg_dpcd_read_burst(ADDR_HDCP13_V_H0, SHA1_SIZE, HDCP13_DPCD.HDCP13_V_H);
+
+ v_read_retry_cnt++;
+
+ for (i = 0; i < SHA1_SIZE; i++) {
+ if (tx_v_value[i] != HDCP13_DPCD.HDCP13_V_H[i])
+ ret = -EFAULT;
+ }
+
+ if (ret == 0)
+ break;
+ }
+
+ return ret;
+}
+
+static int hdcp13_proceed_repeater(void)
+{
+ int retry_cnt = HDCP_RETRY_COUNT;
+ int cnt = 0;
+ int i;
+ u32 b_info = 0;
+ u8 device_cnt = 0;
+ u8 offset = 0;
+ int ksv_read_size = 0;
+ u8 sha1_input_buf[KSV_SIZE * MAX_KSV_LIST_COUNT + BINFO_SIZE + M0_SIZE];
+ u8 v_value[SHA1_SIZE];
+
+ displayport_info("[HDCP 1.3] HDCP repeater Start!!!\n");
+
+ while (hdcp13_info.cp_irq_flag != 1 && cnt < RI_WAIT_COUNT) {
+ mdelay(RI_AVAILABLE_WAITING);
+ cnt++;
+ }
+
+ if (cnt >= RI_WAIT_COUNT)
+ displayport_dbg("[HDCP 1.3] Don't receive CP_IRQ interrupt\n");
+
+ hdcp13_info.cp_irq_flag = 0;
+
+ cnt = 0;
+ while ((HDCP13_DPCD.HDCP13_BSTATUS[0] & BSTATUS_READY) == 0) {
+ displayport_reg_dpcd_read(ADDR_HDCP13_BSTATUS, 1,
+ HDCP13_DPCD.HDCP13_BSTATUS);
+
+ mdelay(RI_AVAILABLE_WAITING);
+ cnt++;
+
+ if (cnt > REPEATER_READY_WAIT_COUNT || !displayport_get_hpd_state()) {
+ displayport_info("[HDCP 1.3] Not repeater ready in RX part\n");
+ hdcp13_info.auth_state = HDCP13_STATE_FAIL;
+ goto repeater_err;
+ }
+ }
+
+ displayport_dbg("[HDCP 1.3] HDCP RX repeater ready!!!\n");
+
+ while ((hdcp13_info.auth_state != HDCP13_STATE_SECOND_AUTH_DONE) &&
+ (retry_cnt != 0)) {
+ retry_cnt--;
+
+ displayport_reg_dpcd_read(ADDR_HDCP13_BINFO, 2, HDCP13_DPCD.HDCP13_BINFO);
+
+ for (i = 0; i < 2; i++)
+ b_info |= (u32)HDCP13_DPCD.HDCP13_BINFO[i] << (i * 8);
+
+ displayport_dbg("[HDCP 1.3] b_info = 0x%x\n", b_info);
+
+ if ((b_info & BINFO_MAX_DEVS_EXCEEDED)
+ || (b_info & BINFO_MAX_CASCADE_EXCEEDED)) {
+ hdcp13_info.auth_state = HDCP13_STATE_FAIL;
+ displayport_info("[HDCP 1.3] MAXDEVS or CASCADE EXCEEDED!\n");
+ goto repeater_err;
+ }
+
+ device_cnt = b_info & BINFO_DEVICE_COUNT;
+
+ if (device_cnt != 0) {
+ displayport_info("[HDCP 1.3] device count = %d\n", device_cnt);
+
+ offset = 0;
+
+ while (device_cnt > offset) {
+ ksv_read_size = (device_cnt - offset) * KSV_SIZE;
+
+ if (ksv_read_size >= KSV_FIFO_SIZE)
+ ksv_read_size = KSV_FIFO_SIZE;
+
+ displayport_reg_dpcd_read(ADDR_HDCP13_KSV_FIFO,
+ ksv_read_size, HDCP13_DPCD.HDCP13_KSV_FIFO);
+
+ for (i = 0; i < ksv_read_size; i++)
+ sha1_input_buf[i + offset * KSV_SIZE] =
+ HDCP13_DPCD.HDCP13_KSV_FIFO[i];
+
+ offset += KSV_FIFO_SIZE / KSV_SIZE;
+ }
+ }
+
+ /* need calculation of V = SHA-1(KSV list || Binfo || M0) */
+ hdcp13_make_sha1_input_buf(sha1_input_buf, HDCP13_DPCD.HDCP13_BINFO, device_cnt);
+#if defined(CONFIG_EXYNOS_HDCP2)
+ hdcp_calc_sha1(v_value, sha1_input_buf, BINFO_SIZE + M0_SIZE + KSV_SIZE * device_cnt);
+#else
+ displayport_info("Not compiled EXYNOS_HDCP2 driver\n");
+#endif
+ hdcp13_v_value_order_swap(v_value);
+
+ if (hdcp13_compare_v(v_value) == 0) {
+ hdcp13_info.auth_state = HDCP13_STATE_SECOND_AUTH_DONE;
+ displayport_reg_video_mute(0);
+ displayport_info("[HDCP 1.3] 2nd Auth done!!!\n");
+ return 0;
+ }
+
+ hdcp13_info.auth_state = HDCP13_STATE_AUTH_PROCESS;
+ displayport_info("[HDCP 1.3] 2nd Auth fail!!!\n");
+ }
+
+repeater_err:
+ return -EINVAL;
+}
+
+void hdcp13_run(void)
+{
+ int retry_cnt = HDCP_RETRY_COUNT;
+
+ while ((hdcp13_info.auth_state != HDCP13_STATE_AUTHENTICATED)
+ && (hdcp13_info.auth_state != HDCP13_STATE_SECOND_AUTH_DONE)
+ && (retry_cnt != 0)) {
+ retry_cnt--;
+
+ hdcp13_info.auth_state = HDCP13_STATE_AUTH_PROCESS;
+
+ hdcp13_encryption_con(0);
+ hdcp13_func_en(1);
+
+ hdcp13_repeater_set();
+
+ displayport_dbg("[HDCP 1.3] SW Auth.\n");
+
+ if (hdcp13_read_bksv() != 0) {
+ displayport_info("[HDCP 1.3] ReAuthentication Start!!!\n");
+ continue;
+ }
+
+ hdcp13_write_an_val();
+
+ if (hdcp13_write_aksv() != 0) {
+ displayport_info("[HDCP 1.3] ReAuthentication Start!!!\n");
+ continue;
+ }
+
+ /* BKSV Rewrite */
+ hdcp13_write_bksv();
+
+ if (hdcp13_cmp_ri() != 0)
+ continue;
+
+ if (!hdcp13_info.is_repeater) {
+ hdcp13_info.auth_state = HDCP13_STATE_AUTHENTICATED;
+ displayport_reg_video_mute(0);
+ }
+
+ hdcp13_encryption_con(1);
+ displayport_dbg("[HDCP 1.3] HDCP 1st Authentication done!!!\n");
+
+ if (hdcp13_info.is_repeater) {
+ if (hdcp13_proceed_repeater())
+ goto HDCP13_END;
+ else
+ continue;
+ }
+ }
+
+HDCP13_END:
+ if ((hdcp13_info.auth_state != HDCP13_STATE_AUTHENTICATED) &&
+ (hdcp13_info.auth_state != HDCP13_STATE_SECOND_AUTH_DONE)) {
+ hdcp13_info.auth_state = HDCP13_STATE_FAIL;
+ displayport_reg_video_mute(1);
+ hdcp13_encryption_con(0);
+ displayport_dbg("[HDCP 1.3] HDCP Authentication fail!!!\n");
+ }
+}
--- /dev/null
+/* linux/drivers/video/fbdev/exynos/dpu/dpp.h
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *
+ * header file for Samsung EXYNOS SoC DPP 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.
+ */
+
+#ifndef __SAMSUNG_DPP_H__
+#define __SAMSUNG_DPP_H__
+
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#if defined(CONFIG_SUPPORT_LEGACY_ION)
+#include <linux/exynos_iovmm.h>
+#else
+#include <linux/ion_exynos.h>
+#endif
+#if defined(CONFIG_EXYNOS9610_BTS)
+#include <soc/samsung/bts.h>
+#endif
+
+#include "decon.h"
+/* TODO: SoC dependency will be removed */
+#if defined(CONFIG_SOC_EXYNOS9610)
+#include "./cal_9610/regs-dpp.h"
+#include "./cal_9610/dpp_cal.h"
+#endif
+
+extern int dpp_log_level;
+
+#define DPP_MODULE_NAME "exynos-dpp"
+
+/* about 1msec @ ACLK=630MHz */
+#define INIT_RCV_NUM 630000
+
+#define check_align(width, height, align_w, align_h)\
+ (IS_ALIGNED(width, align_w) && IS_ALIGNED(height, align_h))
+
+#define is_normal(config) (DPP_ROT_NORMAL)
+#define is_rotation(config) (config->dpp_parm.rot > DPP_ROT_180)
+#define is_yuv(config) ((config->format >= DECON_PIXEL_FORMAT_NV16) \
+ && (config->format < DECON_PIXEL_FORMAT_MAX))
+#define is_yuv422(config) ((config->format >= DECON_PIXEL_FORMAT_NV16) \
+ && (config->format <= DECON_PIXEL_FORMAT_YVU422_3P))
+#define is_yuv420(config) ((config->format >= DECON_PIXEL_FORMAT_NV12) \
+ && (config->format <= DECON_PIXEL_FORMAT_YVU420M))
+
+#define dpp_err(fmt, ...) \
+ do { \
+ if (dpp_log_level >= 3) { \
+ pr_err(pr_fmt(fmt), ##__VA_ARGS__); \
+ } \
+ } while (0)
+
+#define dpp_warn(fmt, ...) \
+ do { \
+ if (dpp_log_level >= 4) { \
+ pr_warn(pr_fmt(fmt), ##__VA_ARGS__); \
+ } \
+ } while (0)
+
+#define dpp_info(fmt, ...) \
+ do { \
+ if (dpp_log_level >= 6) \
+ pr_info(pr_fmt(fmt), ##__VA_ARGS__); \
+ } while (0)
+
+#define dpp_dbg(fmt, ...) \
+ do { \
+ if (dpp_log_level >= 7) \
+ pr_info(pr_fmt(fmt), ##__VA_ARGS__); \
+ } while (0)
+
+/* TODO: This will be removed */
+
+enum dpp_csc_defs {
+ /* csc_type */
+ DPP_CSC_BT_601 = 0,
+ DPP_CSC_BT_709 = 1,
+ /* csc_range */
+ DPP_CSC_NARROW = 0,
+ DPP_CSC_WIDE = 1,
+ /* csc_mode */
+ CSC_COEF_HARDWIRED = 0,
+ CSC_COEF_CUSTOMIZED = 1,
+ /* csc_id used in csc_3x3_t[] : increase by even value */
+ DPP_CSC_ID_BT_2020 = 0,
+ DPP_CSC_ID_DCI_P3 = 2,
+};
+
+enum dpp_state {
+ DPP_STATE_ON,
+ DPP_STATE_OFF,
+};
+
+enum dpp_reg_area {
+ REG_AREA_DPP = 0,
+ REG_AREA_DMA,
+ REG_AREA_DMA_COM,
+};
+
+enum dpp_attr {
+ DPP_ATTR_AFBC = 0,
+ DPP_ATTR_BLOCK = 1,
+ DPP_ATTR_FLIP = 2,
+ DPP_ATTR_ROT = 3,
+ DPP_ATTR_CSC = 4,
+ DPP_ATTR_SCALE = 5,
+ DPP_ATTR_HDR = 6,
+ DPP_ATTR_HDR10 = 7,
+
+ DPP_ATTR_IDMA = 16,
+ DPP_ATTR_ODMA = 17,
+ DPP_ATTR_DPP = 18,
+};
+
+struct dpp_resources {
+ struct clk *gate;
+ void __iomem *regs;
+ void __iomem *dma_regs;
+ void __iomem *dma_com_regs;
+ int irq;
+ int dma_irq;
+};
+
+struct dpp_debug {
+ struct timer_list op_timer;
+ u32 done_count;
+ u32 recovery_cnt;
+};
+
+struct dpp_config {
+ struct decon_win_config config;
+ unsigned long rcv_num;
+};
+
+struct dpp_device {
+ int id;
+ unsigned long attr;
+ enum dpp_state state;
+ struct device *dev;
+ struct v4l2_subdev sd;
+ struct dpp_resources res;
+ struct dpp_debug d;
+ wait_queue_head_t framedone_wq;
+ struct dpp_config *dpp_config;
+ spinlock_t slock;
+ spinlock_t dma_slock;
+ struct mutex lock;
+};
+
+extern struct dpp_device *dpp_drvdata[MAX_DPP_CNT];
+
+static inline struct dpp_device *get_dpp_drvdata(u32 id)
+{
+ if (id >= MAX_DPP_CNT)
+ return NULL;
+ else
+ return dpp_drvdata[id];
+}
+
+static inline u32 dpp_read(u32 id, u32 reg_id)
+{
+ struct dpp_device *dpp = get_dpp_drvdata(id);
+ return readl(dpp->res.regs + reg_id);
+}
+
+static inline u32 dpp_read_mask(u32 id, u32 reg_id, u32 mask)
+{
+ u32 val = dpp_read(id, reg_id);
+ val &= (~mask);
+ return val;
+}
+
+static inline void dpp_write(u32 id, u32 reg_id, u32 val)
+{
+ struct dpp_device *dpp = get_dpp_drvdata(id);
+ writel(val, dpp->res.regs + reg_id);
+}
+
+static inline void dpp_write_mask(u32 id, u32 reg_id, u32 val, u32 mask)
+{
+ struct dpp_device *dpp = get_dpp_drvdata(id);
+ u32 old = dpp_read(id, reg_id);
+
+ val = (val & mask) | (old & ~mask);
+ writel(val, dpp->res.regs + reg_id);
+}
+
+/* DPU_DMA Common part */
+static inline u32 dma_com_read(u32 id, u32 reg_id)
+{
+ struct dpp_device *dpp = get_dpp_drvdata(0);
+ return readl(dpp->res.dma_com_regs + reg_id);
+}
+
+static inline u32 dma_com_read_mask(u32 id, u32 reg_id, u32 mask)
+{
+ u32 val = dma_com_read(id, reg_id);
+ val &= (~mask);
+ return val;
+}
+
+static inline void dma_com_write(u32 id, u32 reg_id, u32 val)
+{
+ /* get reliable address when probing IDMA_G0 */
+ struct dpp_device *dpp = get_dpp_drvdata(0);
+ writel(val, dpp->res.dma_com_regs + reg_id);
+}
+
+static inline void dma_com_write_mask(u32 id, u32 reg_id, u32 val, u32 mask)
+{
+ struct dpp_device *dpp = get_dpp_drvdata(0);
+ u32 old = dma_com_read(id, reg_id);
+
+ val = (val & mask) | (old & ~mask);
+ writel(val, dpp->res.dma_com_regs + reg_id);
+}
+
+/* DPU_DMA */
+static inline u32 dma_read(u32 id, u32 reg_id)
+{
+ struct dpp_device *dpp = get_dpp_drvdata(id);
+ return readl(dpp->res.dma_regs + reg_id);
+}
+
+static inline u32 dma_read_mask(u32 id, u32 reg_id, u32 mask)
+{
+ u32 val = dma_read(id, reg_id);
+ val &= (~mask);
+ return val;
+}
+
+static inline void dma_write(u32 id, u32 reg_id, u32 val)
+{
+ struct dpp_device *dpp = get_dpp_drvdata(id);
+ writel(val, dpp->res.dma_regs + reg_id);
+}
+
+static inline void dma_write_mask(u32 id, u32 reg_id, u32 val, u32 mask)
+{
+ struct dpp_device *dpp = get_dpp_drvdata(id);
+ u32 old = dma_read(id, reg_id);
+
+ val = (val & mask) | (old & ~mask);
+ writel(val, dpp->res.dma_regs + reg_id);
+}
+
+static inline void dpp_select_format(struct dpp_device *dpp,
+ struct dpp_img_format *vi, struct dpp_params_info *p)
+{
+ struct decon_win_config *config = &dpp->dpp_config->config;
+
+ vi->normal = is_normal(dpp);
+ vi->rot = p->rot;
+ vi->scale = p->is_scale;
+ vi->format = p->format;
+ vi->afbc_en = p->is_comp;
+ vi->yuv = is_yuv(config);
+ vi->yuv422 = is_yuv422(config);
+ vi->yuv420 = is_yuv420(config);
+ vi->wb = test_bit(DPP_ATTR_ODMA, &dpp->attr);
+}
+
+void dpp_dump(struct dpp_device *dpp);
+
+#define DPP_WIN_CONFIG _IOW('P', 0, struct decon_win_config)
+#define DPP_STOP _IOW('P', 1, unsigned long)
+#define DPP_DUMP _IOW('P', 2, u32)
+#define DPP_WB_WAIT_FOR_FRAMEDONE _IOR('P', 3, u32)
+#define DPP_WAIT_IDLE _IOR('P', 4, unsigned long)
+#define DPP_SET_RECOVERY_NUM _IOR('P', 5, unsigned long)
+
+#endif /* __SAMSUNG_DPP_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * DPP poly-phase filter coefficients
+ *
+ * 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 <linux/types.h>
+#include "dpp.h"
+
+/* 8-tap Filter Coefficient */
+const s16 h_coef_8t[7][16][8] = {
+ { /* Ratio <= 65536 (~8:8) Selection = 0 */
+ { 0, 0, 0, 512, 0, 0, 0, 0 },
+ { -2, 8, -25, 509, 30, -9, 2, -1 },
+ { -4, 14, -46, 499, 64, -19, 5, -1 },
+ { -5, 20, -62, 482, 101, -30, 8, -2 },
+ { -6, 23, -73, 458, 142, -41, 12, -3 },
+ { -6, 25, -80, 429, 185, -53, 15, -3 },
+ { -6, 26, -83, 395, 228, -63, 19, -4 },
+ { -6, 25, -82, 357, 273, -71, 21, -5 },
+ { -5, 23, -78, 316, 316, -78, 23, -5 },
+ { -5, 21, -71, 273, 357, -82, 25, -6 },
+ { -4, 19, -63, 228, 395, -83, 26, -6 },
+ { -3, 15, -53, 185, 429, -80, 25, -6 },
+ { -3, 12, -41, 142, 458, -73, 23, -6 },
+ { -2, 8, -30, 101, 482, -62, 20, -5 },
+ { -1, 5, -19, 64, 499, -46, 14, -4 },
+ { -1, 2, -9, 30, 509, -25, 8, -2 }
+ },
+ { /* 65536 < Ratio <= 74898 (~8:7) Selection = 1 */
+ { 12, -32, 56, 444, 52, -32, 12, 0 },
+ { 9, -24, 29, 445, 82, -39, 13, -3 },
+ { 7, -16, 6, 438, 112, -46, 14, -3 },
+ { 5, -9, -14, 426, 144, -52, 15, -3 },
+ { 3, -3, -30, 410, 177, -58, 16, -3 },
+ { 2, 2, -43, 390, 211, -63, 16, -3 },
+ { 1, 7, -53, 365, 244, -66, 16, -2 },
+ { 0, 10, -60, 338, 277, -66, 15, -2 },
+ { -1, 13, -65, 309, 309, -65, 13, -1 },
+ { -2, 15, -66, 277, 338, -60, 10, 0 },
+ { -2, 16, -66, 244, 365, -53, 7, 1 },
+ { -3, 16, -63, 211, 390, -43, 2, 2 },
+ { -3, 16, -58, 177, 410, -30, -3, 3 },
+ { -3, 15, -52, 144, 426, -14, -9, 5 },
+ { -3, 14, -46, 112, 438, 6, -16, 7 },
+ { -3, 13, -39, 82, 445, 29, -24, 9 }
+ },
+ { /* 74898 < Ratio <= 87381 (~8:6) Selection = 2 */
+ { 8, -44, 100, 384, 100, -44, 8, 0 },
+ { 9, -40, 77, 382, 123, -47, 8, 0 },
+ { 8, -36, 57, 377, 147, -49, 7, 1 },
+ { 8, -32, 38, 369, 171, -49, 5, 2 },
+ { 8, -27, 20, 358, 196, -48, 3, 2 },
+ { 7, -22, 5, 344, 221, -47, 1, 3 },
+ { 7, -18, -9, 329, 245, -43, -2, 3 },
+ { 5, -13, -20, 310, 268, -37, -5, 4 },
+ { 5, -9, -30, 290, 290, -30, -9, 5 },
+ { 4, -5, -37, 268, 310, -20, -13, 5 },
+ { 3, -2, -43, 245, 329, -9, -18, 7 },
+ { 3, 1, -47, 221, 344, 5, -22, 7 },
+ { 2, 3, -48, 196, 358, 20, -27, 8 },
+ { 2, 5, -49, 171, 369, 38, -32, 8 },
+ { 1, 7, -49, 147, 377, 57, -36, 8 },
+ { 0, 8, -47, 123, 382, 77, -40, 9 }
+ },
+ { /* 87381 < Ratio <= 104857 (~8:5) Selection = 3 */
+ { -3, -31, 130, 320, 130, -31, -3, 0 },
+ { -3, -32, 113, 319, 147, -29, -6, 3 },
+ { -1, -33, 97, 315, 165, -26, -8, 3 },
+ { 0, -32, 81, 311, 182, -22, -11, 3 },
+ { 1, -31, 66, 304, 199, -17, -13, 3 },
+ { 2, -30, 52, 296, 216, -11, -16, 3 },
+ { 2, -28, 38, 286, 232, -3, -18, 3 },
+ { 3, -25, 26, 274, 247, 5, -21, 3 },
+ { 3, -23, 15, 261, 261, 15, -23, 3 },
+ { 3, -21, 5, 247, 274, 26, -25, 3 },
+ { 3, -18, -3, 232, 286, 38, -28, 2 },
+ { 3, -16, -11, 216, 296, 52, -30, 2 },
+ { 3, -13, -17, 199, 304, 66, -31, 1 },
+ { 3, -11, -22, 182, 311, 81, -32, 0 },
+ { 3, -8, -26, 165, 315, 97, -33, -1 },
+ { 3, -6, -29, 147, 319, 113, -32, -3 }
+ },
+ { /* 104857 < Ratio <= 131072 (~8:4) Selection = 4 */
+ { -11, 0, 140, 255, 140, 0, -12, 0 },
+ { -10, -4, 129, 254, 151, 5, -13, 0 },
+ { -9, -7, 117, 253, 163, 10, -14, -1 },
+ { -8, -10, 106, 250, 174, 16, -15, -1 },
+ { -7, -12, 95, 246, 185, 22, -16, -1 },
+ { -6, -14, 85, 241, 195, 29, -16, -2 },
+ { -5, -15, 74, 236, 204, 37, -17, -2 },
+ { -5, -16, 64, 229, 214, 46, -17, -3 },
+ { -4, -17, 55, 222, 222, 55, -17, -4 },
+ { -3, -17, 46, 214, 229, 64, -16, -5 },
+ { -2, -17, 37, 204, 236, 74, -15, -5 },
+ { -2, -16, 29, 195, 241, 85, -14, -6 },
+ { -1, -16, 22, 185, 246, 95, -12, -7 },
+ { -1, -15, 16, 174, 250, 106, -10, -8 },
+ { -1, -14, 10, 163, 253, 117, -7, -9 },
+ { 0, -13, 5, 151, 254, 129, -4, -10 }
+ },
+ { /* 131072 < Ratio <= 174762 (~8:3) Selection = 5 */
+ { -5, 31, 133, 195, 133, 31, -6, 0 },
+ { -5, 27, 126, 195, 139, 37, -4, -3 },
+ { -5, 23, 119, 194, 146, 41, -3, -3 },
+ { -5, 19, 112, 193, 152, 47, -2, -4 },
+ { -5, 16, 105, 191, 158, 53, -2, -4 },
+ { -5, 12, 98, 189, 163, 59, 0, -4 },
+ { -5, 10, 91, 185, 169, 65, 1, -4 },
+ { -5, 7, 84, 182, 174, 71, 3, -4 },
+ { -5, 5, 78, 178, 178, 78, 5, -5 },
+ { -4, 3, 71, 174, 182, 84, 7, -5 },
+ { -4, 1, 65, 169, 185, 91, 10, -5 },
+ { -4, 0, 59, 163, 189, 98, 12, -5 },
+ { -4, -2, 53, 158, 191, 105, 16, -5 },
+ { -4, -2, 47, 152, 193, 112, 19, -5 },
+ { -3, -3, 41, 146, 194, 119, 23, -5 },
+ { -3, -4, 37, 139, 195, 126, 27, -5 }
+ },
+ { /* 174762 < Ratio <= 262144 (~8:2) Selection = 6 */
+ { 10, 52, 118, 152, 118, 52, 10, 0 },
+ { 9, 48, 114, 152, 122, 56, 11, 0 },
+ { 7, 45, 110, 151, 125, 60, 13, 1 },
+ { 6, 41, 106, 150, 129, 64, 15, 1 },
+ { 5, 38, 102, 149, 132, 68, 17, 1 },
+ { 4, 35, 98, 148, 135, 72, 19, 1 },
+ { 4, 31, 94, 146, 138, 77, 21, 1 },
+ { 3, 29, 89, 145, 140, 81, 23, 2 },
+ { 2, 26, 85, 143, 143, 85, 26, 2 },
+ { 2, 23, 81, 140, 145, 89, 29, 3 },
+ { 1, 21, 77, 138, 146, 94, 31, 4 },
+ { 1, 19, 72, 135, 148, 98, 35, 4 },
+ { 1, 17, 68, 132, 149, 102, 38, 5 },
+ { 1, 15, 64, 129, 150, 106, 41, 6 },
+ { 1, 13, 60, 125, 151, 110, 45, 7 },
+ { 0, 11, 56, 122, 152, 114, 48, 9 }
+ }
+};
+
+/* 4-tap Filter Coefficient */
+const s16 v_coef_4t[7][16][4] = {
+ { /* Ratio <= 65536 (~8:8) Selection = 0 */
+ { 0, 512, 0, 0 },
+ { -15, 508, 20, -1 },
+ { -25, 495, 45, -3 },
+ { -31, 473, 75, -5 },
+ { -33, 443, 110, -8 },
+ { -33, 408, 148, -11 },
+ { -31, 367, 190, -14 },
+ { -27, 324, 234, -19 },
+ { -23, 279, 279, -23 },
+ { -19, 234, 324, -27 },
+ { -14, 190, 367, -31 },
+ { -11, 148, 408, -33 },
+ { -8, 110, 443, -33 },
+ { -5, 75, 473, -31 },
+ { -3, 45, 495, -25 },
+ { -1, 20, 508, -15 }
+ },
+ { /* 65536 < Ratio <= 74898 (~8:7) Selection = 1 */
+ { 32, 448, 32, 0 },
+ { 17, 446, 55, -6 },
+ { 3, 437, 79, -7 },
+ { -7, 421, 107, -9 },
+ { -14, 399, 138, -11 },
+ { -18, 373, 170, -13 },
+ { -20, 343, 204, -15 },
+ { -20, 310, 240, -18 },
+ { -19, 275, 275, -19 },
+ { -18, 240, 310, -20 },
+ { -15, 204, 343, -20 },
+ { -13, 170, 373, -18 },
+ { -11, 138, 399, -14 },
+ { -9, 107, 421, -7 },
+ { -7, 79, 437, 3 },
+ { -6, 55, 446, 17 }
+ },
+ { /* 74898 < Ratio <= 87381 (~8:6) Selection = 2 */
+ { 61, 390, 61, 0 },
+ { 46, 390, 83, -7 },
+ { 31, 383, 106, -8 },
+ { 19, 371, 130, -8 },
+ { 9, 356, 156, -9 },
+ { 2, 337, 183, -10 },
+ { -3, 315, 210, -10 },
+ { -7, 291, 238, -10 },
+ { -9, 265, 265, -9 },
+ { -10, 238, 291, -7 },
+ { -10, 210, 315, -3 },
+ { -10, 183, 337, 2 },
+ { -9, 156, 356, 9 },
+ { -8, 130, 371, 19 },
+ { -8, 106, 383, 31 },
+ { -7, 83, 390, 46 }
+ },
+ { /* 87381 < Ratio <= 104857 (~8:5) Selection = 3 */
+ { 85, 341, 86, 0 },
+ { 71, 341, 105, -5 },
+ { 56, 336, 124, -4 },
+ { 43, 328, 145, -4 },
+ { 32, 317, 166, -3 },
+ { 23, 304, 187, -2 },
+ { 16, 288, 209, -1 },
+ { 9, 271, 231, 1 },
+ { 5, 251, 251, 5 },
+ { 1, 231, 271, 9 },
+ { -1, 209, 288, 16 },
+ { -2, 187, 304, 23 },
+ { -3, 166, 317, 32 },
+ { -4, 145, 328, 43 },
+ { -4, 124, 336, 56 },
+ { -5, 105, 341, 71 }
+ },
+ { /* 104857 < Ratio <= 131072 (~8:4) Selection = 4 */
+ { 104, 304, 104, 0 },
+ { 89, 302, 120, 1 },
+ { 76, 298, 136, 2 },
+ { 63, 293, 153, 3 },
+ { 52, 285, 170, 5 },
+ { 42, 275, 188, 7 },
+ { 33, 264, 205, 10 },
+ { 26, 251, 221, 14 },
+ { 20, 236, 236, 20 },
+ { 14, 221, 251, 26 },
+ { 10, 205, 264, 33 },
+ { 7, 188, 275, 42 },
+ { 5, 170, 285, 52 },
+ { 3, 153, 293, 63 },
+ { 2, 136, 298, 76 },
+ { 1, 120, 302, 89 }
+ },
+ { /* 131072 < Ratio <= 174762 (~8:3) Selection = 5 */
+ { 118, 276, 118, 0 },
+ { 103, 273, 129, 7 },
+ { 90, 270, 143, 9 },
+ { 78, 266, 157, 11 },
+ { 67, 260, 171, 14 },
+ { 57, 253, 185, 17 },
+ { 48, 244, 199, 21 },
+ { 40, 234, 211, 27 },
+ { 33, 223, 223, 33 },
+ { 27, 211, 234, 40 },
+ { 21, 199, 244, 48 },
+ { 17, 185, 253, 57 },
+ { 14, 171, 260, 67 },
+ { 11, 157, 266, 78 },
+ { 9, 143, 270, 90 },
+ { 7, 129, 273, 103 }
+ },
+ { /* 174762 < Ratio <= 262144 (~8:2) Selection = 6 */
+ { 127, 258, 127, 0 },
+ { 111, 252, 135, 14 },
+ { 100, 250, 147, 15 },
+ { 88, 247, 159, 18 },
+ { 78, 242, 171, 21 },
+ { 68, 237, 182, 25 },
+ { 59, 230, 193, 30 },
+ { 50, 222, 204, 36 },
+ { 43, 213, 213, 43 },
+ { 36, 204, 222, 50 },
+ { 30, 193, 230, 59 },
+ { 25, 182, 237, 68 },
+ { 21, 171, 242, 78 },
+ { 18, 159, 247, 88 },
+ { 15, 147, 250, 100 },
+ { 14, 135, 252, 111 }
+ }
+};
+
+/* 3x3 CSC Coefficient for BT2020 : 3.9 format */
+const u16 csc_3x3_t[4][3][3] = {
+ /* CSC_ID = 0 : BT_2020_LIMITTED */
+ {
+ {0x254, 0x000, 0x36F},
+ {0x254, 0xF9E, 0xEAC},
+ {0x254, 0x461, 0x000},
+ },
+ /* CSC_ID = 0 : BT_2020_FULL */
+ {
+ {0x200, 0x000, 0x2F3},
+ {0x200, 0xFAC, 0xEDB},
+ {0x200, 0x3C3, 0x000},
+ },
+
+ /* CSC_ID = 1 : DCI_P3_LIMITTED */
+ {
+ {0x254, 0x000, 0x3AE},
+ {0x254, 0xF96, 0xEEE},
+ {0x254, 0x456, 0x000},
+ },
+ /* CSC_ID = 1 : DCI_P3_FULL */
+ {
+ {0x200, 0x000, 0x329},
+ {0x200, 0xFA5, 0xF15},
+ {0x200, 0x3B9, 0x000},
+ },
+
+ /* CSC_ID = 2 : Add if necessary */
+};
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Samsung EXYNOS8 SoC series DPP 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 <linux/module.h>
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/exynos_iovmm.h>
+#include <linux/videodev2_exynos_media.h>
+#include <linux/console.h>
+
+#include "dpp.h"
+#include "decon.h"
+
+int dpp_log_level = 6;
+
+struct dpp_device *dpp_drvdata[MAX_DPP_CNT];
+
+static void dma_dump_regs(struct dpp_device *dpp)
+{
+ dpp_info("\n=== DPU_DMA%d SFR DUMP ===\n", dpp->id);
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ dpp->res.dma_regs, 0x6C, false);
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ dpp->res.dma_regs + 0x100, 0x8, false);
+
+ dpp_info("=== DPU_DMA%d SHADOW SFR DUMP ===\n", dpp->id);
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ dpp->res.dma_regs + 0x800, 0x74, false);
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ dpp->res.dma_regs + 0x900, 0x8, false);
+}
+
+static void dpp_dump_regs(struct dpp_device *dpp)
+{
+ dpp_info("=== DPP%d SFR DUMP ===\n", dpp->id);
+
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ dpp->res.regs, 0x4C, false);
+ if (test_bit(DPP_ATTR_AFBC, &dpp->attr)) {
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ dpp->res.regs + 0x5B0, 0x10, false);
+ }
+ if (test_bit(DPP_ATTR_ROT, &dpp->attr)) {
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ dpp->res.regs + 0x600, 0x1E0, false);
+ }
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ dpp->res.regs + 0xA54, 0x4, false);
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ dpp->res.regs + 0xB00, 0x4C, false);
+ if (test_bit(DPP_ATTR_AFBC, &dpp->attr)) {
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ dpp->res.regs + 0xBB0, 0x10, false);
+ }
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ dpp->res.regs + 0xD00, 0xC, false);
+}
+
+void dpp_dump(struct dpp_device *dpp)
+{
+ int acquired = console_trylock();
+
+ dma_reg_dump_com_debug_regs(dpp->id);
+
+ dma_dump_regs(dpp);
+ dma_reg_dump_debug_regs(dpp->id);
+
+ dpp_dump_regs(dpp);
+ dpp_reg_dump_debug_regs(dpp->id);
+
+ if (acquired)
+ console_unlock();
+}
+
+void dpp_op_timer_handler(unsigned long arg)
+{
+ struct dpp_device *dpp = (struct dpp_device *)arg;
+
+ dpp_dump(dpp);
+
+ if (dpp->dpp_config->config.compression)
+ dpp_info("Compression Source is %s of DPP[%d]\n",
+ dpp->dpp_config->config.dpp_parm.comp_src == DPP_COMP_SRC_G2D ?
+ "G2D" : "GPU", dpp->id);
+
+ dpp_info("DPP[%d] irq hasn't been occured", dpp->id);
+}
+
+static int dpp_wb_wait_for_framedone(struct dpp_device *dpp)
+{
+ int ret;
+ int done_cnt;
+
+ if (!test_bit(DPP_ATTR_ODMA, &dpp->attr)) {
+ dpp_err("waiting for dpp's framedone is only for writeback\n");
+ return -EINVAL;
+ }
+
+ if (dpp->state == DPP_STATE_OFF) {
+ dpp_err("dpp%d power is off state(%d)\n", dpp->id, dpp->state);
+ return -EPERM;
+ }
+
+ done_cnt = dpp->d.done_count;
+ /* TODO: dma framedone should be wait */
+ ret = wait_event_interruptible_timeout(dpp->framedone_wq,
+ (done_cnt != dpp->d.done_count), msecs_to_jiffies(17));
+ if (ret == 0) {
+ dpp_err("timeout of dpp%d framedone\n", dpp->id);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static void dpp_get_params(struct dpp_device *dpp, struct dpp_params_info *p)
+{
+ u64 src_w, src_h, dst_w, dst_h;
+ struct decon_win_config *config = &dpp->dpp_config->config;
+
+ p->rcv_num = dpp->dpp_config->rcv_num;
+ memcpy(&p->src, &config->src, sizeof(struct decon_frame));
+ memcpy(&p->dst, &config->dst, sizeof(struct decon_frame));
+ memcpy(&p->block, &config->block_area, sizeof(struct decon_win_rect));
+ p->rot = config->dpp_parm.rot;
+ p->is_comp = config->compression;
+ p->format = config->format;
+ p->addr[0] = config->dpp_parm.addr[0];
+ p->addr[1] = config->dpp_parm.addr[1];
+ p->addr[2] = 0;
+ p->addr[3] = 0;
+ p->eq_mode = config->dpp_parm.eq_mode;
+ p->hdr = config->dpp_parm.hdr_std;
+ p->is_4p = false;
+ p->y_2b_strd = 0;
+ p->c_2b_strd = 0;
+
+ if (p->format == DECON_PIXEL_FORMAT_NV12N)
+ p->addr[1] = NV12N_CBCR_BASE(p->addr[0], p->src.f_w, p->src.f_h);
+
+ if (p->format == DECON_PIXEL_FORMAT_NV12M_S10B || p->format == DECON_PIXEL_FORMAT_NV21M_S10B) {
+ p->addr[2] = p->addr[0] + NV12M_Y_SIZE(p->src.f_w, p->src.f_h);
+ p->addr[3] = p->addr[1] + NV12M_CBCR_SIZE(p->src.f_w, p->src.f_h);
+ p->is_4p = true;
+ p->y_2b_strd = S10B_2B_STRIDE(p->src.f_w);
+ p->c_2b_strd = S10B_2B_STRIDE(p->src.f_w);
+ }
+
+ if (p->format == DECON_PIXEL_FORMAT_NV12N_10B) {
+ p->addr[1] = NV12N_10B_CBCR_BASE(p->addr[0], p->src.f_w, p->src.f_h);
+ p->addr[2] = p->addr[0] + NV12N_10B_Y_8B_SIZE(p->src.f_w, p->src.f_h);
+ p->addr[3] = p->addr[1] + NV12N_10B_CBCR_8B_SIZE(p->src.f_w, p->src.f_h);
+ p->is_4p = true;
+ p->y_2b_strd = S10B_2B_STRIDE(p->src.f_w);
+ p->c_2b_strd = S10B_2B_STRIDE(p->src.f_w);
+ }
+
+ if (p->format == DECON_PIXEL_FORMAT_NV16M_S10B || p->format == DECON_PIXEL_FORMAT_NV61M_S10B) {
+ p->addr[2] = p->addr[0] + NV16M_Y_SIZE(p->src.f_w, p->src.f_h);
+ p->addr[3] = p->addr[1] + NV16M_CBCR_SIZE(p->src.f_w, p->src.f_h);
+ p->is_4p = true;
+ p->y_2b_strd = S10B_2B_STRIDE(p->src.f_w);
+ p->c_2b_strd = S10B_2B_STRIDE(p->src.f_w);
+ }
+
+ if (is_rotation(config)) {
+ src_w = p->src.h;
+ src_h = p->src.w;
+ } else {
+ src_w = p->src.w;
+ src_h = p->src.h;
+ }
+ dst_w = p->dst.w;
+ dst_h = p->dst.h;
+
+ p->h_ratio = (src_w << 20) / dst_w;
+ p->v_ratio = (src_h << 20) / dst_h;
+
+ if ((p->h_ratio != (1 << 20)) || (p->v_ratio != (1 << 20)))
+ p->is_scale = true;
+ else
+ p->is_scale = false;
+
+ if ((config->dpp_parm.rot != DPP_ROT_NORMAL) || (p->is_scale) ||
+ (p->format >= DECON_PIXEL_FORMAT_NV16) ||
+ (p->block.w < BLK_WIDTH_MIN) || (p->block.h < BLK_HEIGHT_MIN))
+ p->is_block = false;
+ else
+ p->is_block = true;
+}
+
+static int dpp_check_size(struct dpp_device *dpp, struct dpp_img_format *vi)
+{
+ struct decon_win_config *config = &dpp->dpp_config->config;
+ struct decon_frame *src = &config->src;
+ struct decon_frame *dst = &config->dst;
+ struct dpp_size_constraints vc;
+
+ dpp_constraints_params(&vc, vi);
+
+ if ((!check_align(src->x, src->y, vc.src_mul_x, vc.src_mul_y)) ||
+ (!check_align(src->f_w, src->f_h, vc.src_mul_w, vc.src_mul_h)) ||
+ (!check_align(src->w, src->h, vc.img_mul_w, vc.img_mul_h)) ||
+ (!check_align(dst->w, dst->h, vc.sca_mul_w, vc.sca_mul_h))) {
+ dpp_err("Alignment error!\n");
+ goto err;
+ }
+
+ if (src->w > vc.img_w_max || src->w < vc.img_w_min ||
+ src->h > vc.img_h_max || src->h < vc.img_h_min) {
+ dpp_err("Unsupported SRC size!\n");
+ goto err;
+ }
+
+ if (dst->w > vc.sca_w_max || dst->w < vc.sca_w_min ||
+ dst->h > vc.sca_h_max || dst->h < vc.sca_h_min) {
+ dpp_err("Unsupported DST size!\n");
+ goto err;
+ }
+
+ /* check boundary */
+ if (src->x + src->w > vc.src_w_max || src->y + src->h > vc.src_h_max) {
+ dpp_err("Unsupported src boundary size!\n");
+ goto err;
+ }
+
+ if (src->x + src->w > src->f_w || src->y + src->h > src->f_h) {
+ dpp_err("Unsupported src range!\n");
+ goto err;
+ }
+
+ if (src->x < 0 || src->y < 0 ||
+ dst->x < 0 || dst->y < 0) {
+ dpp_err("Unsupported src/dst x,y position!\n");
+ goto err;
+ }
+
+ return 0;
+err:
+ dpp_err("offset x : %d, offset y: %d\n", src->x, src->y);
+ dpp_err("src_mul_x : %d, src_mul_y : %d\n", vc.src_mul_x, vc.src_mul_y);
+ dpp_err("src f_w : %d, src f_h: %d\n", src->f_w, src->f_h);
+ dpp_err("src_mul_w : %d, src_mul_h : %d\n", vc.src_mul_w, vc.src_mul_h);
+ dpp_err("src w : %d, src h: %d\n", src->w, src->h);
+ dpp_err("img_mul_w : %d, img_mul_h : %d\n", vc.img_mul_w, vc.img_mul_h);
+ dpp_err("dst w : %d, dst h: %d\n", dst->w, dst->h);
+ dpp_err("sca_mul_w : %d, sca_mul_h : %d\n", vc.sca_mul_w, vc.sca_mul_h);
+ dpp_err("rotation : %d, color_format : %d\n",
+ config->dpp_parm.rot, config->format);
+ dpp_err("hdr : %d, color_format : %d\n",
+ config->dpp_parm.hdr_std, config->format);
+ return -EINVAL;
+}
+
+static int dpp_check_scale_ratio(struct dpp_params_info *p)
+{
+ u32 sc_down_max_w, sc_down_max_h;
+ u32 sc_up_min_w, sc_up_min_h;
+ u32 sc_src_w, sc_src_h;
+
+ sc_down_max_w = p->dst.w * 2;
+ sc_down_max_h = p->dst.h * 2;
+ sc_up_min_w = (p->dst.w + 7) / 8;
+ sc_up_min_h = (p->dst.h + 7) / 8;
+ if (p->rot > DPP_ROT_180) {
+ sc_src_w = p->src.h;
+ sc_src_h = p->src.w;
+ } else {
+ sc_src_w = p->src.w;
+ sc_src_h = p->src.h;
+ }
+
+ if (sc_src_w > sc_down_max_w || sc_src_h > sc_down_max_h) {
+ dpp_err("Not support under 1/2x scale-down!\n");
+ goto err;
+ }
+
+ if (sc_src_w < sc_up_min_w || sc_src_h < sc_up_min_h) {
+ dpp_err("Not support over 8x scale-up\n");
+ goto err;
+ }
+
+ return 0;
+err:
+ dpp_err("src w(%d) h(%d), dst w(%d) h(%d), rotation(%d)\n",
+ p->src.w, p->src.h, p->dst.w, p->dst.h, p->rot);
+ return -EINVAL;
+}
+
+static int dpp_check_addr(struct dpp_device *dpp, struct dpp_params_info *p)
+{
+ int cnt = 0;
+
+ cnt = dpu_get_plane_cnt(p->format, false);
+
+ switch (cnt) {
+ case 1:
+ if (IS_ERR_OR_NULL((void *)p->addr[0])) {
+ dpp_err("Address[0] is 0x0 DPP%d\n", dpp->id);
+ return -EINVAL;
+ }
+ break;
+ case 2:
+ case 3:
+ if (IS_ERR_OR_NULL((void *)p->addr[0])) {
+ dpp_err("Address[0] is 0x0 DPP%d\n", dpp->id);
+ return -EINVAL;
+ }
+ if (IS_ERR_OR_NULL((void *)p->addr[1])) {
+ dpp_err("Address[1] is 0x0 DPP%d\n", dpp->id);
+ return -EINVAL;
+ }
+ break;
+ case 4:
+ if (IS_ERR_OR_NULL((void *)p->addr[0])) {
+ dpp_err("Address[0] is 0x0 DPP%d\n", dpp->id);
+ return -EINVAL;
+ }
+ if (IS_ERR_OR_NULL((void *)p->addr[1])) {
+ dpp_err("Address[1] is 0x0 DPP%d\n", dpp->id);
+ return -EINVAL;
+ }
+ if (IS_ERR_OR_NULL((void *)p->addr[2])) {
+ dpp_err("Address[2] is 0x0 DPP%d\n", dpp->id);
+ return -EINVAL;
+ }
+ if (IS_ERR_OR_NULL((void *)p->addr[3])) {
+ dpp_err("Address[3] is 0x0 DPP%d\n", dpp->id);
+ return -EINVAL;
+ }
+ break;
+ default:
+ dpp_err("Unsupport plane cnt\n");
+ return -EINVAL;
+ break;
+ }
+
+ return 0;
+}
+
+static int dpp_check_format(struct dpp_device *dpp, struct dpp_params_info *p)
+{
+ if (!test_bit(DPP_ATTR_ROT, &dpp->attr) && (p->rot > DPP_ROT_180)) {
+ dpp_err("Not support rotation in DPP%d - VGRF only!\n",
+ p->rot);
+ return -EINVAL;
+ }
+
+ if (!test_bit(DPP_ATTR_HDR, &dpp->attr) && (p->hdr > DPP_HDR_OFF)) {
+ dpp_err("Not support hdr in DPP%d - VGRF only!\n",
+ dpp->id);
+ return -EINVAL;
+ }
+
+ if ((p->hdr < DPP_HDR_OFF) || (p->hdr > DPP_HDR_HLG)) {
+ dpp_err("Unsupported HDR standard in DPP%d, HDR std(%d)\n",
+ dpp->id, p->hdr);
+ return -EINVAL;
+ }
+
+ if (!test_bit(DPP_ATTR_CSC, &dpp->attr) &&
+ (p->format >= DECON_PIXEL_FORMAT_NV16)) {
+ dpp_err("Not support YUV format(%d) in DPP%d - VG & VGF only!\n",
+ p->format, dpp->id);
+ return -EINVAL;
+ }
+
+ if (!test_bit(DPP_ATTR_AFBC, &dpp->attr) && p->is_comp) {
+ dpp_err("Not support AFBC decoding in DPP%d - VGF only!\n",
+ dpp->id);
+ return -EINVAL;
+ }
+
+ if (!test_bit(DPP_ATTR_SCALE, &dpp->attr) && p->is_scale) {
+ dpp_err("Not support SCALING in DPP%d - VGF only!\n", dpp->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * TODO: h/w limitation will be changed in KC
+ * This function must be modified for KC after releasing DPP constraints
+ */
+static int dpp_check_limitation(struct dpp_device *dpp, struct dpp_params_info *p)
+{
+ int ret;
+ struct dpp_img_format vi;
+
+ ret = dpp_check_scale_ratio(p);
+ if (ret) {
+ dpp_err("failed to set dpp%d scale information\n", dpp->id);
+ return -EINVAL;
+ }
+
+ dpp_select_format(dpp, &vi, p);
+
+ ret = dpp_check_format(dpp, p);
+ if (ret)
+ return -EINVAL;
+
+ ret = dpp_check_addr(dpp, p);
+ if (ret)
+ return -EINVAL;
+
+ if (p->is_comp && p->rot) {
+ dpp_err("Not support [AFBC+ROTATION] at the same time in DPP%d\n",
+ dpp->id);
+ return -EINVAL;
+ }
+
+ if (p->is_comp && p->is_block) {
+ dpp_err("Not support [AFBC+BLOCK] at the same time in DPP%d\n",
+ dpp->id);
+ return -EINVAL;
+ }
+
+ if (p->is_comp && vi.yuv420) {
+ dpp_err("Not support AFBC decoding for YUV format in DPP%d\n",
+ dpp->id);
+ return -EINVAL;
+ }
+
+ if (p->is_block && p->is_scale) {
+ dpp_err("Not support [BLOCK+SCALE] at the same time in DPP%d\n",
+ dpp->id);
+ return -EINVAL;
+ }
+
+ if (p->is_block && vi.yuv420) {
+ dpp_err("Not support BLOCK Mode for YUV format in DPP%d\n",
+ dpp->id);
+ return -EINVAL;
+ }
+
+ /* FIXME */
+ if (p->is_block && p->rot) {
+ dpp_err("Not support [BLOCK+ROTATION] at the same time in DPP%d\n",
+ dpp->id);
+ return -EINVAL;
+ }
+
+ /* HDR channel limitation */
+ if ((p->hdr != DPP_HDR_OFF) && p->is_comp) {
+ dpp_err("Not support [HDR+AFBC] at the same time in DPP%d\n",
+ dpp->id);
+ return -EINVAL;
+ }
+
+ /* HDR channel limitation */
+ if ((p->hdr != DPP_HDR_OFF) && p->rot) {
+ dpp_err("Not support [HDR+ROTATION] at the same time in DPP%d\n",
+ dpp->id);
+ return -EINVAL;
+ }
+
+ ret = dpp_check_size(dpp, &vi);
+ if (ret)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int dpp_set_config(struct dpp_device *dpp)
+{
+ struct dpp_params_info params;
+ int ret = 0;
+
+ mutex_lock(&dpp->lock);
+
+ /* parameters from decon driver are translated for dpp driver */
+ dpp_get_params(dpp, ¶ms);
+
+ /* all parameters must be passed dpp hw limitation */
+ ret = dpp_check_limitation(dpp, ¶ms);
+ if (ret)
+ goto err;
+
+ if (dpp->state == DPP_STATE_OFF) {
+ dpp_dbg("dpp%d is started\n", dpp->id);
+ dpp_reg_init(dpp->id, dpp->attr);
+
+ enable_irq(dpp->res.dma_irq);
+ if (test_bit(DPP_ATTR_DPP, &dpp->attr))
+ enable_irq(dpp->res.irq);
+ }
+
+ /* set all parameters to dpp hw */
+ dpp_reg_configure_params(dpp->id, ¶ms, dpp->attr);
+
+ dpp->d.op_timer.expires = (jiffies + 1 * HZ);
+ mod_timer(&dpp->d.op_timer, dpp->d.op_timer.expires);
+
+ DPU_EVENT_LOG(DPU_EVT_DPP_WINCON, &dpp->sd, ktime_set(0, 0));
+
+ dpp_dbg("dpp%d configuration\n", dpp->id);
+
+ dpp->state = DPP_STATE_ON;
+err:
+ mutex_unlock(&dpp->lock);
+ return ret;
+}
+
+static int dpp_stop(struct dpp_device *dpp, bool reset)
+{
+ int ret = 0;
+
+ mutex_lock(&dpp->lock);
+
+ if (dpp->state == DPP_STATE_OFF) {
+ dpp_warn("dpp%d is already disabled\n", dpp->id);
+ goto err;
+ }
+
+ DPU_EVENT_LOG(DPU_EVT_DPP_STOP, &dpp->sd, ktime_set(0, 0));
+
+ disable_irq(dpp->res.dma_irq);
+ if (test_bit(DPP_ATTR_DPP, &dpp->attr))
+ disable_irq(dpp->res.irq);
+
+ del_timer(&dpp->d.op_timer);
+ dpp_reg_deinit(dpp->id, reset, dpp->attr);
+
+ dpp_dbg("dpp%d is stopped\n", dpp->id);
+
+ dpp->state = DPP_STATE_OFF;
+err:
+ mutex_unlock(&dpp->lock);
+ return ret;
+}
+
+static long dpp_subdev_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+ struct dpp_device *dpp = v4l2_get_subdevdata(sd);
+ bool reset = (bool)arg;
+ int ret = 0;
+
+ switch (cmd) {
+ case DPP_WIN_CONFIG:
+ dpp->dpp_config = (struct dpp_config *)arg;
+ ret = dpp_set_config(dpp);
+ if (ret)
+ dpp_err("failed to configure dpp%d\n", dpp->id);
+ break;
+
+ case DPP_STOP:
+ ret = dpp_stop(dpp, reset);
+ if (ret)
+ dpp_err("failed to stop dpp%d\n", dpp->id);
+ break;
+
+ case DPP_DUMP:
+ dpp_dump(dpp);
+ break;
+
+ case DPP_WB_WAIT_FOR_FRAMEDONE:
+ ret = dpp_wb_wait_for_framedone(dpp);
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static const struct v4l2_subdev_core_ops dpp_subdev_core_ops = {
+ .ioctl = dpp_subdev_ioctl,
+};
+
+static struct v4l2_subdev_ops dpp_subdev_ops = {
+ .core = &dpp_subdev_core_ops,
+};
+
+static void dpp_init_subdev(struct dpp_device *dpp)
+{
+ struct v4l2_subdev *sd = &dpp->sd;
+
+ v4l2_subdev_init(sd, &dpp_subdev_ops);
+ sd->owner = THIS_MODULE;
+ sd->grp_id = dpp->id;
+ snprintf(sd->name, sizeof(sd->name), "%s.%d", "dpp-sd", dpp->id);
+ v4l2_set_subdevdata(sd, dpp);
+}
+
+static void dpp_parse_dt(struct dpp_device *dpp, struct device *dev)
+{
+ dpp->id = of_alias_get_id(dev->of_node, "dpp");
+ dpp_info("dpp(%d) probe start..\n", dpp->id);
+ of_property_read_u32(dev->of_node, "attr", (u32 *)&dpp->attr);
+ dpp_info("attributes = 0x%lx\n", dpp->attr);
+
+ dpp->dev = dev;
+}
+
+static irqreturn_t dpp_irq_handler(int irq, void *priv)
+{
+ struct dpp_device *dpp = priv;
+ u32 dpp_irq = 0;
+
+ spin_lock(&dpp->slock);
+ if (dpp->state == DPP_STATE_OFF)
+ goto irq_end;
+
+ dpp_irq = dpp_reg_get_irq_and_clear(dpp->id);
+
+irq_end:
+ del_timer(&dpp->d.op_timer);
+ spin_unlock(&dpp->slock);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t dma_irq_handler(int irq, void *priv)
+{
+ struct dpp_device *dpp = priv;
+ u32 irqs, val = 0;
+
+ spin_lock(&dpp->dma_slock);
+ if (dpp->state == DPP_STATE_OFF)
+ goto irq_end;
+
+ if (test_bit(DPP_ATTR_ODMA, &dpp->attr)) { /* ODMA case */
+ irqs = odma_reg_get_irq_and_clear(dpp->id);
+
+ if ((irqs & ODMA_WRITE_SLAVE_ERROR) ||
+ (irqs & ODMA_STATUS_DEADLOCK_IRQ)) {
+ dpp_err("odma%d error irq occur(0x%x)\n", dpp->id, irqs);
+ dpp_dump(dpp);
+ goto irq_end;
+ }
+ if (irqs & ODMA_STATUS_FRAMEDONE_IRQ) {
+ dpp->d.done_count++;
+ wake_up_interruptible_all(&dpp->framedone_wq);
+ DPU_EVENT_LOG(DPU_EVT_DPP_FRAMEDONE, &dpp->sd,
+ ktime_set(0, 0));
+ goto irq_end;
+ }
+ } else { /* IDMA case */
+ irqs = idma_reg_get_irq_and_clear(dpp->id);
+
+ if (irqs & IDMA_RECOVERY_START_IRQ) {
+ DPU_EVENT_LOG(DPU_EVT_DMA_RECOVERY, &dpp->sd,
+ ktime_set(0, 0));
+ val = (u32)dpp->dpp_config->config.dpp_parm.comp_src;
+ dpp->d.recovery_cnt++;
+#if 0 /* TODO: This will be implemented */
+ dpp_info("dma%d recovery start(0x%x).. [src=%s], cnt[%d %d]\n",
+ dpp->id, irqs,
+ val == DPP_COMP_SRC_G2D ? "G2D" : "GPU",
+ get_dpp_drvdata(DPU_DMA2CH(IDMA_VGF0))->d.recovery_cnt,
+ get_dpp_drvdata(DPU_DMA2CH(IDMA_VGF1))->d.recovery_cnt);
+#endif
+ goto irq_end;
+ }
+ if ((irqs & IDMA_AFBC_TIMEOUT_IRQ) ||
+ (irqs & IDMA_READ_SLAVE_ERROR) ||
+ (irqs & IDMA_STATUS_DEADLOCK_IRQ)) {
+ dpp_err("dma%d error irq occur(0x%x)\n", dpp->id, irqs);
+ dpp_dump(dpp);
+ goto irq_end;
+ }
+ /*
+ * TODO: Normally, DMA framedone occurs before DPP framedone.
+ * But DMA framedone can occur in case of AFBC crop mode
+ */
+ if (irqs & IDMA_STATUS_FRAMEDONE_IRQ) {
+ DPU_EVENT_LOG(DPU_EVT_DMA_FRAMEDONE, &dpp->sd,
+ ktime_set(0, 0));
+ goto irq_end;
+ }
+ }
+
+irq_end:
+ if (!test_bit(DPP_ATTR_DPP, &dpp->attr))
+ del_timer(&dpp->d.op_timer);
+
+ spin_unlock(&dpp->dma_slock);
+ return IRQ_HANDLED;
+}
+
+static int dpp_init_resources(struct dpp_device *dpp, struct platform_device *pdev)
+{
+ struct resource *res;
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dpp_err("failed to get mem resource\n");
+ return -ENOENT;
+ }
+ dpp_info("dma res: start(0x%x), end(0x%x)\n",
+ (u32)res->start, (u32)res->end);
+
+ dpp->res.dma_regs = devm_ioremap_resource(dpp->dev, res);
+ if (!dpp->res.dma_regs) {
+ dpp_err("failed to remap DPU_DMA SFR region\n");
+ return -EINVAL;
+ }
+
+ /* DPP0 channel can only access common area of DPU_DMA */
+ if (dpp->id == 0) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ if (!res) {
+ dpp_err("failed to get mem resource\n");
+ return -ENOENT;
+ }
+ dpp_info("dma common res: start(0x%x), end(0x%x)\n",
+ (u32)res->start, (u32)res->end);
+
+ dpp->res.dma_com_regs = devm_ioremap_resource(dpp->dev, res);
+ if (!dpp->res.dma_com_regs) {
+ dpp_err("failed to remap DPU_DMA COMMON SFR region\n");
+ return -EINVAL;
+ }
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dpp_err("failed to get dpu dma irq resource\n");
+ return -ENOENT;
+ }
+ dpp_info("dma irq no = %lld\n", res->start);
+
+ dpp->res.dma_irq = res->start;
+ ret = devm_request_irq(dpp->dev, res->start, dma_irq_handler, 0,
+ pdev->name, dpp);
+ if (ret) {
+ dpp_err("failed to install DPU DMA irq\n");
+ return -EINVAL;
+ }
+ disable_irq(dpp->res.dma_irq);
+
+ if (test_bit(DPP_ATTR_DPP, &dpp->attr)) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res) {
+ dpp_err("failed to get mem resource\n");
+ return -ENOENT;
+ }
+ dpp_info("res: start(0x%x), end(0x%x)\n",
+ (u32)res->start, (u32)res->end);
+
+ dpp->res.regs = devm_ioremap_resource(dpp->dev, res);
+ if (!dpp->res.regs) {
+ dpp_err("failed to remap DPP SFR region\n");
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ if (!res) {
+ dpp_err("failed to get dpp irq resource\n");
+ return -ENOENT;
+ }
+ dpp_info("dpp irq no = %lld\n", res->start);
+
+ dpp->res.irq = res->start;
+ ret = devm_request_irq(dpp->dev, res->start, dpp_irq_handler, 0,
+ pdev->name, dpp);
+ if (ret) {
+ dpp_err("failed to install DPP irq\n");
+ return -EINVAL;
+ }
+ disable_irq(dpp->res.irq);
+ }
+
+ return 0;
+}
+
+static int dpp_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct dpp_device *dpp;
+ int ret = 0;
+
+ dpp = devm_kzalloc(dev, sizeof(*dpp), GFP_KERNEL);
+ if (!dpp) {
+ dpp_err("failed to allocate dpp device.\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ dpp_parse_dt(dpp, dev);
+ dpp_drvdata[dpp->id] = dpp;
+
+ spin_lock_init(&dpp->slock);
+ spin_lock_init(&dpp->dma_slock);
+ mutex_init(&dpp->lock);
+ init_waitqueue_head(&dpp->framedone_wq);
+
+ ret = dpp_init_resources(dpp, pdev);
+ if (ret)
+ goto err_clk;
+
+ dpp_init_subdev(dpp);
+ platform_set_drvdata(pdev, dpp);
+ setup_timer(&dpp->d.op_timer, dpp_op_timer_handler, (unsigned long)dpp);
+
+ dpp->state = DPP_STATE_OFF;
+ dpp_info("dpp%d is probed successfully\n", dpp->id);
+
+ return 0;
+
+err_clk:
+ kfree(dpp);
+err:
+ return ret;
+}
+
+static int dpp_remove(struct platform_device *pdev)
+{
+ dpp_info("%s driver unloaded\n", pdev->name);
+ return 0;
+}
+
+static const struct of_device_id dpp_of_match[] = {
+ { .compatible = "samsung,exynos9-dpp" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, dpp_of_match);
+
+static struct platform_driver dpp_driver __refdata = {
+ .probe = dpp_probe,
+ .remove = dpp_remove,
+ .driver = {
+ .name = DPP_MODULE_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(dpp_of_match),
+ .suppress_bind_attrs = true,
+ }
+};
+
+static int dpp_register(void)
+{
+ return platform_driver_register(&dpp_driver);
+}
+
+device_initcall_sync(dpp_register);
+
+MODULE_AUTHOR("Jaehoe Yang <jaehoe.yang@samsung.com>");
+MODULE_AUTHOR("Minho Kim <m8891.kim@samsung.com>");
+MODULE_DESCRIPTION("Samsung EXYNOS DPP driver");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Header file for Samsung EXYNOS SoC MIPI-DSI Master 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.
+*/
+
+#ifndef __SAMSUNG_DSIM_H__
+#define __SAMSUNG_DSIM_H__
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/regulator/consumer.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <media/v4l2-subdev.h>
+
+#include "./panels/decon_lcd.h"
+#if defined(CONFIG_SOC_EXYNOS9610)
+#include "./cal_9610/regs-dsim.h"
+#include "./cal_9610/dsim_cal.h"
+#endif
+
+#if defined(CONFIG_EXYNOS_DECON_LCD_S6E3HA2K)
+#include "./panels/s6e3ha2k_param.h"
+#elif defined(CONFIG_EXYNOS_DECON_LCD_S6E3HF4)
+#include "./panels/s6e3hf4_param.h"
+#elif defined(CONFIG_EXYNOS_DECON_LCD_EMUL_DISP)
+#include "./panels/emul_disp_param.h"
+#elif defined(CONFIG_EXYNOS_DECON_LCD_S6E3HA6)
+#include "./panels/s6e3ha6_param.h"
+#elif defined(CONFIG_EXYNOS_DECON_LCD_S6E3AA2)
+#include "./panels/s6e3aa2_param.h"
+#elif defined(CONFIG_EXYNOS_DECON_LCD_S6E3FA0)
+#include "./panels/s6e3fa0_param.h"
+#endif
+
+extern int dsim_log_level;
+
+#define DSIM_MODULE_NAME "exynos-dsim"
+#define DSIM_DDI_ID_LEN 3
+
+#define DSIM_PIXEL_FORMAT_RGB24 0x3E
+#define DSIM_PIXEL_FORMAT_RGB18_PACKED 0x1E
+#define DSIM_PIXEL_FORMAT_RGB18 0x2E
+#define DSIM_PIXEL_FORMAT_RGB30_PACKED 0x0D
+#define DSIM_RX_FIFO_MAX_DEPTH 64
+#define MAX_DSIM_DATALANE_CNT 4
+
+#define MIPI_WR_TIMEOUT msecs_to_jiffies(50)
+#define MIPI_RD_TIMEOUT msecs_to_jiffies(100)
+
+#define dsim_err(fmt, ...) \
+ do { \
+ if (dsim_log_level >= 3) { \
+ pr_err(pr_fmt(fmt), ##__VA_ARGS__); \
+ } \
+ } while (0)
+
+#define dsim_warn(fmt, ...) \
+ do { \
+ if (dsim_log_level >= 4) { \
+ pr_warn(pr_fmt(fmt), ##__VA_ARGS__); \
+ } \
+ } while (0)
+
+#define dsim_info(fmt, ...) \
+ do { \
+ if (dsim_log_level >= 6) \
+ pr_info(pr_fmt(fmt), ##__VA_ARGS__); \
+ } while (0)
+
+#define dsim_dbg(fmt, ...) \
+ do { \
+ if (dsim_log_level >= 7) \
+ pr_info(pr_fmt(fmt), ##__VA_ARGS__); \
+ } while (0)
+
+#define call_panel_ops(q, op, args...) \
+ (((q)->panel_ops->op) ? ((q)->panel_ops->op(args)) : 0)
+
+extern struct dsim_device *dsim_drvdata[MAX_DSIM_CNT];
+extern struct dsim_lcd_driver s6e3ha2k_mipi_lcd_driver;
+extern struct dsim_lcd_driver emul_disp_mipi_lcd_driver;
+extern struct dsim_lcd_driver s6e3hf4_mipi_lcd_driver;
+extern struct dsim_lcd_driver s6e3ha6_mipi_lcd_driver;
+extern struct dsim_lcd_driver s6e3ha8_mipi_lcd_driver;
+extern struct dsim_lcd_driver s6e3aa2_mipi_lcd_driver;
+extern struct dsim_lcd_driver s6e3fa0_mipi_lcd_driver;
+
+/* define video timer interrupt */
+enum {
+ DSIM_VBP = 0,
+ DSIM_VSYNC,
+ DSIM_V_ACTIVE,
+ DSIM_VFP,
+};
+
+/* define dsi bist pattern */
+enum {
+ DSIM_COLOR_BAR = 0,
+ DSIM_GRAY_GRADATION,
+ DSIM_USER_DEFINED,
+ DSIM_PRB7_RANDOM,
+};
+
+/* define DSI lane types. */
+enum {
+ DSIM_LANE_CLOCK = (1 << 0),
+ DSIM_LANE_DATA0 = (1 << 1),
+ DSIM_LANE_DATA1 = (1 << 2),
+ DSIM_LANE_DATA2 = (1 << 3),
+ DSIM_LANE_DATA3 = (1 << 4),
+};
+
+/* DSI Error report bit definitions */
+enum {
+ MIPI_DSI_ERR_SOT = (1 << 0),
+ MIPI_DSI_ERR_SOT_SYNC = (1 << 1),
+ MIPI_DSI_ERR_EOT_SYNC = (1 << 2),
+ MIPI_DSI_ERR_ESCAPE_MODE_ENTRY_CMD = (1 << 3),
+ MIPI_DSI_ERR_LOW_POWER_TRANSMIT_SYNC = (1 << 4),
+ MIPI_DSI_ERR_HS_RECEIVE_TIMEOUT = (1 << 5),
+ MIPI_DSI_ERR_FALSE_CONTROL = (1 << 6),
+ /* Bit 7 is reserved */
+ MIPI_DSI_ERR_ECC_SINGLE_BIT = (1 << 8),
+ MIPI_DSI_ERR_ECC_MULTI_BIT = (1 << 9),
+ MIPI_DSI_ERR_CHECKSUM = (1 << 10),
+ MIPI_DSI_ERR_DATA_TYPE_NOT_RECOGNIZED = (1 << 11),
+ MIPI_DSI_ERR_VCHANNEL_ID_INVALID = (1 << 12),
+ MIPI_DSI_ERR_INVALID_TRANSMIT_LENGTH = (1 << 13),
+ /* Bit 14 is reserved */
+ MIPI_DSI_ERR_PROTOCAL_VIOLATION = (1 << 15),
+ /* DSI_PROTOCAL_VIOLATION[15] is for protocol violation that is caused EoTp
+ * missing So this bit is egnored because of not supportung @S.LSI AP */
+ /* FALSE_ERROR_CONTROL[6] is for detect invalid escape or turnaround sequence.
+ * This bit is not supporting @S.LSI AP because of non standard
+ * ULPS enter/exit sequence during power-gating */
+ /* Bit [14],[7] is reserved */
+ MIPI_DSI_ERR_BIT_MASK = (0x3f3f), /* Error_Range[13:0] */
+};
+
+/* operation state of dsim driver */
+enum dsim_state {
+ DSIM_STATE_INIT,
+ DSIM_STATE_ON, /* HS clock was enabled. */
+ DSIM_STATE_DOZE, /* HS clock was enabled. */
+ DSIM_STATE_ULPS, /* DSIM was entered ULPS state */
+ DSIM_STATE_DOZE_SUSPEND, /* DSIM is suspend state */
+ DSIM_STATE_OFF /* DSIM is suspend state */
+};
+
+enum dphy_charic_value {
+ M_PLL_CTRL1,
+ M_PLL_CTRL2,
+ B_DPHY_CTRL2,
+ B_DPHY_CTRL3,
+ B_DPHY_CTRL4,
+ M_DPHY_CTRL1,
+ M_DPHY_CTRL2,
+ M_DPHY_CTRL3,
+ M_DPHY_CTRL4
+};
+
+struct dsim_pll_param {
+ u32 p;
+ u32 m;
+ u32 s;
+ u32 k;
+ u32 pll_freq; /* in/out parameter: Mhz */
+};
+
+struct dphy_timing_value {
+ u32 bps;
+ u32 clk_prepare;
+ u32 clk_zero;
+ u32 clk_post;
+ u32 clk_trail;
+ u32 hs_prepare;
+ u32 hs_zero;
+ u32 hs_trail;
+ u32 lpx;
+ u32 hs_exit;
+ u32 b_dphyctl;
+};
+
+struct dsim_resources {
+ struct clk *pclk;
+ struct clk *dphy_esc;
+ struct clk *dphy_byte;
+ struct clk *rgb_vclk0;
+ struct clk *pclk_disp;
+ struct clk *aclk;
+ int lcd_power[2];
+ int lcd_reset;
+ int irq;
+ void __iomem *regs;
+ void __iomem *ss_regs;
+ void __iomem *phy_regs;
+ void __iomem *phy_regs_ex;
+ struct regulator *regulator_1p8v;
+ struct regulator *regulator_3p3v;
+};
+
+struct dsim_device {
+ int id;
+ enum dsim_state state;
+ struct device *dev;
+ struct dsim_resources res;
+
+ unsigned int data_lane;
+ u32 data_lane_cnt;
+ struct phy *phy;
+ struct phy *phy_ex;
+ spinlock_t slock;
+
+ struct dsim_lcd_driver *panel_ops;
+ struct decon_lcd lcd_info;
+
+ struct v4l2_subdev sd;
+ struct dsim_clks clks;
+ struct timer_list cmd_timer;
+
+ struct mutex cmd_lock;
+
+ struct completion ph_wr_comp;
+ struct completion rd_comp;
+
+ int total_underrun_cnt;
+ struct backlight_device *bd;
+};
+
+struct dsim_lcd_driver {
+ int (*probe)(struct dsim_device *dsim);
+ int (*suspend)(struct dsim_device *dsim);
+ int (*displayon)(struct dsim_device *dsim);
+ int (*resume)(struct dsim_device *dsim);
+ int (*dump)(struct dsim_device *dsim);
+ int (*mres)(struct dsim_device *dsim, int mres_idx);
+ int (*doze)(struct dsim_device *dsim);
+ int (*doze_suspend)(struct dsim_device *dsim);
+};
+
+int dsim_write_data(struct dsim_device *dsim, u32 id, unsigned long d0, u32 d1);
+int dsim_read_data(struct dsim_device *dsim, u32 id, u32 addr, u32 cnt, u8 *buf);
+int dsim_wait_for_cmd_done(struct dsim_device *dsim);
+
+int dsim_reset_panel(struct dsim_device *dsim);
+int dsim_set_panel_power(struct dsim_device *dsim, bool on);
+
+static inline struct dsim_device *get_dsim_drvdata(u32 id)
+{
+ return dsim_drvdata[id];
+}
+
+static inline int dsim_rd_data(u32 id, u32 cmd_id, u32 addr, u32 size, u8 *buf)
+{
+ int ret;
+ struct dsim_device *dsim = get_dsim_drvdata(id);
+
+ ret = dsim_read_data(dsim, cmd_id, addr, size, buf);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static inline int dsim_wr_data(u32 id, u32 cmd_id, unsigned long d0, u32 d1)
+{
+ int ret;
+ struct dsim_device *dsim = get_dsim_drvdata(id);
+
+ ret = dsim_write_data(dsim, cmd_id, d0, d1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static inline int dsim_wait_for_cmd_completion(u32 id)
+{
+ int ret;
+ struct dsim_device *dsim = get_dsim_drvdata(id);
+
+ ret = dsim_wait_for_cmd_done(dsim);
+
+ return ret;
+}
+
+/* register access subroutines */
+static inline u32 dsim_read(u32 id, u32 reg_id)
+{
+ struct dsim_device *dsim = get_dsim_drvdata(id);
+ return readl(dsim->res.regs + reg_id);
+}
+
+static inline u32 dsim_read_mask(u32 id, u32 reg_id, u32 mask)
+{
+ u32 val = dsim_read(id, reg_id);
+ val &= (mask);
+ return val;
+}
+
+static inline void dsim_write(u32 id, u32 reg_id, u32 val)
+{
+ struct dsim_device *dsim = get_dsim_drvdata(id);
+ writel(val, dsim->res.regs + reg_id);
+}
+
+static inline void dsim_write_mask(u32 id, u32 reg_id, u32 val, u32 mask)
+{
+ struct dsim_device *dsim = get_dsim_drvdata(id);
+ u32 old = dsim_read(id, reg_id);
+
+ val = (val & mask) | (old & ~mask);
+ writel(val, dsim->res.regs + reg_id);
+}
+
+/* DPHY register access subroutines */
+static inline u32 dsim_phy_read(u32 id, u32 reg_id)
+{
+ struct dsim_device *dsim = get_dsim_drvdata(id);
+
+ return readl(dsim->res.phy_regs + reg_id);
+}
+
+static inline u32 dsim_phy_read_mask(u32 id, u32 reg_id, u32 mask)
+{
+ u32 val = dsim_phy_read(id, reg_id);
+
+ val &= (mask);
+ return val;
+}
+static inline void dsim_phy_extra_write(u32 id, u32 reg_id, u32 val)
+{
+ struct dsim_device *dsim = get_dsim_drvdata(id);
+
+ writel(val, dsim->res.phy_regs_ex + reg_id);
+}
+static inline void dsim_phy_write(u32 id, u32 reg_id, u32 val)
+{
+ struct dsim_device *dsim = get_dsim_drvdata(id);
+
+ writel(val, dsim->res.phy_regs + reg_id);
+}
+
+static inline void dsim_phy_write_mask(u32 id, u32 reg_id, u32 val, u32 mask)
+{
+ struct dsim_device *dsim = get_dsim_drvdata(id);
+ u32 old = dsim_phy_read(id, reg_id);
+
+ val = (val & mask) | (old & ~mask);
+ writel(val, dsim->res.phy_regs + reg_id);
+ /* printk("offset : 0x%8x, value : 0x%x\n", reg_id, val); */
+}
+
+/* DPHY loop back for test */
+#ifdef DPHY_LOOP
+void dsim_reg_set_dphy_loop_back_test(u32 id);
+#endif
+
+static inline bool IS_DSIM_ON_STATE(struct dsim_device *dsim)
+{
+#ifdef CONFIG_EXYNOS_DOZE
+ return (dsim->state == DSIM_STATE_ON ||
+ dsim->state == DSIM_STATE_DOZE);
+#else
+ return (dsim->state == DSIM_STATE_ON);
+#endif
+}
+
+static inline bool IS_DSIM_OFF_STATE(struct dsim_device *dsim)
+{
+ return (dsim->state == DSIM_STATE_ULPS ||
+#ifdef CONFIG_EXYNOS_DOZE
+ dsim->state == DSIM_STATE_DOZE_SUSPEND ||
+#endif
+ dsim->state == DSIM_STATE_OFF);
+}
+
+#define DSIM_IOC_ENTER_ULPS _IOW('D', 0, u32)
+#define DSIM_IOC_GET_LCD_INFO _IOW('D', 5, struct decon_lcd *)
+#define DSIM_IOC_DUMP _IOW('D', 8, u32)
+#define DSIM_IOC_GET_WCLK _IOW('D', 9, u32)
+#define DSIM_IOC_SET_CONFIG _IOW('D', 10, u32)
+#define DSIM_IOC_DOZE _IOW('D', 20, u32)
+#define DSIM_IOC_DOZE_SUSPEND _IOW('D', 21, u32)
+
+#endif /* __SAMSUNG_DSIM_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Samsung SoC MIPI-DSIM 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 <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_gpio.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <video/mipi_display.h>
+#if defined(CONFIG_CAL_IF)
+#include <soc/samsung/cal-if.h>
+#endif
+#if defined(CONFIG_SOC_EXYNOS9610)
+#include <dt-bindings/clock/exynos9610.h>
+#endif
+#include <soc/samsung/exynos-pmu.h>
+#if defined(CONFIG_SUPPORT_LEGACY_ION)
+#include <linux/exynos_iovmm.h>
+#endif
+
+#include "decon.h"
+#include "dsim.h"
+
+int dsim_log_level = 6;
+
+struct dsim_device *dsim_drvdata[MAX_DSIM_CNT];
+EXPORT_SYMBOL(dsim_drvdata);
+
+static char *dsim_state_names[] = {
+ "INIT",
+ "ON",
+ "DOZE",
+ "ULPS",
+ "DOZE_SUSPEND",
+ "OFF",
+};
+
+static int dsim_runtime_suspend(struct device *dev);
+static int dsim_runtime_resume(struct device *dev);
+
+static void __dsim_dump(struct dsim_device *dsim)
+{
+ /* change to updated register read mode (meaning: SHADOW in DECON) */
+ dsim_info("=== DSIM %d LINK SFR DUMP ===\n", dsim->id);
+ dsim_reg_enable_shadow_read(dsim->id, 0);
+ print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ dsim->res.regs, 0xFC, false);
+ print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ dsim->res.regs + 0x200, 0x4, false);
+
+ dsim_reg_enable_shadow_read(dsim->id, 1);
+}
+
+static void dsim_dump(struct dsim_device *dsim)
+{
+ dsim_info("=== DSIM SFR DUMP ===\n");
+ __dsim_dump(dsim);
+
+ /* Show panel status */
+ call_panel_ops(dsim, dump, dsim);
+}
+
+static void dsim_long_data_wr(struct dsim_device *dsim, unsigned long d0, u32 d1)
+{
+ unsigned int data_cnt = 0, payload = 0;
+
+ /* in case that data count is more then 4 */
+ for (data_cnt = 0; data_cnt < d1; data_cnt += 4) {
+ /*
+ * after sending 4bytes per one time,
+ * send remainder data less then 4.
+ */
+ if ((d1 - data_cnt) < 4) {
+ if ((d1 - data_cnt) == 3) {
+ payload = *(u8 *)(d0 + data_cnt) |
+ (*(u8 *)(d0 + (data_cnt + 1))) << 8 |
+ (*(u8 *)(d0 + (data_cnt + 2))) << 16;
+ dsim_dbg("count = 3 payload = %x, %x %x %x\n",
+ payload, *(u8 *)(d0 + data_cnt),
+ *(u8 *)(d0 + (data_cnt + 1)),
+ *(u8 *)(d0 + (data_cnt + 2)));
+ } else if ((d1 - data_cnt) == 2) {
+ payload = *(u8 *)(d0 + data_cnt) |
+ (*(u8 *)(d0 + (data_cnt + 1))) << 8;
+ dsim_dbg("count = 2 payload = %x, %x %x\n", payload,
+ *(u8 *)(d0 + data_cnt),
+ *(u8 *)(d0 + (data_cnt + 1)));
+ } else if ((d1 - data_cnt) == 1) {
+ payload = *(u8 *)(d0 + data_cnt);
+ }
+
+ dsim_reg_wr_tx_payload(dsim->id, payload);
+ /* send 4bytes per one time. */
+ } else {
+ payload = *(u8 *)(d0 + data_cnt) |
+ (*(u8 *)(d0 + (data_cnt + 1))) << 8 |
+ (*(u8 *)(d0 + (data_cnt + 2))) << 16 |
+ (*(u8 *)(d0 + (data_cnt + 3))) << 24;
+
+ dsim_dbg("count = 4 payload = %x, %x %x %x %x\n",
+ payload, *(u8 *)(d0 + data_cnt),
+ *(u8 *)(d0 + (data_cnt + 1)),
+ *(u8 *)(d0 + (data_cnt + 2)),
+ *(u8 *)(d0 + (data_cnt + 3)));
+
+ dsim_reg_wr_tx_payload(dsim->id, payload);
+ }
+ }
+}
+
+static int dsim_wait_for_cmd_fifo_empty(struct dsim_device *dsim, bool must_wait)
+{
+ int ret = 0;
+
+ if (!must_wait) {
+ /* timer is running, but already command is transferred */
+ if (dsim_reg_header_fifo_is_empty(dsim->id))
+ del_timer(&dsim->cmd_timer);
+
+ dsim_dbg("%s Doesn't need to wait fifo_completion\n", __func__);
+ return ret;
+ } else {
+ del_timer(&dsim->cmd_timer);
+ dsim_dbg("%s Waiting for fifo_completion...\n", __func__);
+ }
+
+ if (!wait_for_completion_timeout(&dsim->ph_wr_comp, MIPI_WR_TIMEOUT)) {
+ if (dsim_reg_header_fifo_is_empty(dsim->id)) {
+ reinit_completion(&dsim->ph_wr_comp);
+ dsim_reg_clear_int(dsim->id, DSIM_INTSRC_SFR_PH_FIFO_EMPTY);
+ return 0;
+ }
+ ret = -ETIMEDOUT;
+ }
+
+ if (IS_DSIM_ON_STATE(dsim) && (ret == -ETIMEDOUT)) {
+ dsim_err("%s have timed out\n", __func__);
+ __dsim_dump(dsim);
+ }
+ return ret;
+}
+
+/* wait for until SFR fifo is empty */
+int dsim_wait_for_cmd_done(struct dsim_device *dsim)
+{
+ int ret = 0;
+ /* FIXME: hiber only support for DECON0 */
+ struct decon_device *decon = get_decon_drvdata(0);
+
+ decon_hiber_block_exit(decon);
+
+ mutex_lock(&dsim->cmd_lock);
+ ret = dsim_wait_for_cmd_fifo_empty(dsim, true);
+ mutex_unlock(&dsim->cmd_lock);
+
+ decon_hiber_unblock(decon);
+
+ return ret;
+}
+
+static bool dsim_fifo_empty_needed(struct dsim_device *dsim, unsigned int data_id,
+ unsigned long data0)
+{
+ /* read case or partial update command */
+ if (data_id == MIPI_DSI_DCS_READ
+ || data0 == MIPI_DCS_SET_COLUMN_ADDRESS
+ || data0 == MIPI_DCS_SET_PAGE_ADDRESS) {
+ dsim_dbg("%s: id:%d, data=%ld\n", __func__, data_id, data0);
+ return true;
+ }
+
+ /* Check a FIFO level whether writable or not */
+ if (!dsim_reg_is_writable_fifo_state(dsim->id))
+ return true;
+
+ return false;
+}
+
+int dsim_write_data(struct dsim_device *dsim, u32 id, unsigned long d0, u32 d1)
+{
+ int ret = 0;
+ bool must_wait = true;
+ struct decon_device *decon = get_decon_drvdata(0);
+
+ decon_hiber_block_exit(decon);
+
+ mutex_lock(&dsim->cmd_lock);
+ if (!IS_DSIM_ON_STATE(dsim)) {
+ dsim_err("DSIM is not ready. state(%d)\n", dsim->state);
+ ret = -EINVAL;
+ goto err_exit;
+ }
+ DPU_EVENT_LOG_CMD(&dsim->sd, id, d0);
+
+ reinit_completion(&dsim->ph_wr_comp);
+ dsim_reg_clear_int(dsim->id, DSIM_INTSRC_SFR_PH_FIFO_EMPTY);
+
+ /* Run write-fail dectector */
+ mod_timer(&dsim->cmd_timer, jiffies + MIPI_WR_TIMEOUT);
+
+ switch (id) {
+ /* short packet types of packet types for command. */
+ case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
+ case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
+ case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
+ case MIPI_DSI_DCS_SHORT_WRITE:
+ case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
+ case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
+ case MIPI_DSI_DSC_PRA:
+ case MIPI_DSI_COLOR_MODE_OFF:
+ case MIPI_DSI_COLOR_MODE_ON:
+ case MIPI_DSI_SHUTDOWN_PERIPHERAL:
+ case MIPI_DSI_TURN_ON_PERIPHERAL:
+ dsim_reg_wr_tx_header(dsim->id, id, d0, d1, false);
+ must_wait = dsim_fifo_empty_needed(dsim, id, d0);
+ break;
+
+ case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
+ case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
+ case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
+ case MIPI_DSI_DCS_READ:
+ dsim_reg_wr_tx_header(dsim->id, id, d0, d1, true);
+ must_wait = dsim_fifo_empty_needed(dsim, id, d0);
+ break;
+
+ /* long packet types of packet types for command. */
+ case MIPI_DSI_GENERIC_LONG_WRITE:
+ case MIPI_DSI_DCS_LONG_WRITE:
+ case MIPI_DSI_DSC_PPS:
+ dsim_long_data_wr(dsim, d0, d1);
+ dsim_reg_wr_tx_header(dsim->id, id, d1 & 0xff,
+ (d1 & 0xff00) >> 8, false);
+ must_wait = dsim_fifo_empty_needed(dsim, id, *(u8 *)d0);
+ break;
+
+ default:
+ dsim_info("data id %x is not supported.\n", id);
+ ret = -EINVAL;
+ }
+
+ ret = dsim_wait_for_cmd_fifo_empty(dsim, must_wait);
+ if (ret < 0)
+ dsim_err("ID(%d): DSIM cmd wr timeout 0x%lx\n", id, d0);
+
+err_exit:
+ mutex_unlock(&dsim->cmd_lock);
+ decon_hiber_unblock(decon);
+
+ return ret;
+}
+
+int dsim_read_data(struct dsim_device *dsim, u32 id, u32 addr, u32 cnt, u8 *buf)
+{
+ u32 rx_fifo, rx_size = 0;
+ int i, j, ret = 0;
+ u32 rx_fifo_depth = DSIM_RX_FIFO_MAX_DEPTH;
+ struct decon_device *decon = get_decon_drvdata(0);
+
+ decon_hiber_block_exit(decon);
+
+ if (IS_DSIM_OFF_STATE(dsim)) {
+ dsim_err("DSIM is not ready. state(%d)\n", dsim->state);
+ decon_hiber_unblock(decon);
+ return -EINVAL;
+ }
+
+ reinit_completion(&dsim->rd_comp);
+
+ /* Init RX FIFO before read and clear DSIM_INTSRC */
+ dsim_reg_clear_int(dsim->id, DSIM_INTSRC_RX_DATA_DONE);
+
+ /* Set the maximum packet size returned */
+ dsim_write_data(dsim,
+ MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, cnt, 0);
+
+ /* Read request */
+ dsim_write_data(dsim, id, addr, 0);
+ if (!wait_for_completion_timeout(&dsim->rd_comp, MIPI_RD_TIMEOUT)) {
+ dsim_err("MIPI DSIM read Timeout!\n");
+ return -ETIMEDOUT;
+ }
+
+ mutex_lock(&dsim->cmd_lock);
+ DPU_EVENT_LOG_CMD(&dsim->sd, id, (char)addr);
+
+ do {
+ rx_fifo = dsim_reg_get_rx_fifo(dsim->id);
+
+ /* Parse the RX packet data types */
+ switch (rx_fifo & 0xff) {
+ case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
+ ret = dsim_reg_rx_err_handler(dsim->id, rx_fifo);
+ if (ret < 0) {
+ __dsim_dump(dsim);
+ goto exit;
+ }
+ break;
+ case MIPI_DSI_RX_END_OF_TRANSMISSION:
+ dsim_dbg("EoTp was received from LCD module.\n");
+ break;
+ case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
+ case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
+ case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE:
+ case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE:
+ dsim_dbg("Short Packet was received from LCD module.\n");
+ for (i = 0; i <= cnt; i++)
+ buf[i] = (rx_fifo >> (8 + i * 8)) & 0xff;
+ break;
+ case MIPI_DSI_RX_DCS_LONG_READ_RESPONSE:
+ case MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE:
+ dsim_dbg("Long Packet was received from LCD module.\n");
+ rx_size = (rx_fifo & 0x00ffff00) >> 8;
+ dsim_dbg("rx fifo : %8x, response : %x, rx_size : %d\n",
+ rx_fifo, rx_fifo & 0xff, rx_size);
+ /* Read data from RX packet payload */
+ for (i = 0; i < rx_size >> 2; i++) {
+ rx_fifo = dsim_reg_get_rx_fifo(dsim->id);
+ for (j = 0; j < 4; j++)
+ buf[(i*4)+j] = (u8)(rx_fifo >> (j * 8)) & 0xff;
+ }
+ if (rx_size % 4) {
+ rx_fifo = dsim_reg_get_rx_fifo(dsim->id);
+ for (j = 0; j < rx_size % 4; j++)
+ buf[4 * i + j] =
+ (u8)(rx_fifo >> (j * 8)) & 0xff;
+ }
+ break;
+ default:
+ dsim_err("Packet format is invaild.\n");
+ __dsim_dump(dsim);
+ ret = -EBUSY;
+ goto exit;
+ }
+ } while (!dsim_reg_rx_fifo_is_empty(dsim->id) && --rx_fifo_depth);
+
+ ret = rx_size;
+ if (!rx_fifo_depth) {
+ dsim_err("Check DPHY values about HS clk.\n");
+ __dsim_dump(dsim);
+ ret = -EBUSY;
+ }
+exit:
+ mutex_unlock(&dsim->cmd_lock);
+ decon_hiber_unblock(decon);
+
+ return ret;
+}
+
+static void dsim_cmd_fail_detector(unsigned long arg)
+{
+ struct dsim_device *dsim = (struct dsim_device *)arg;
+ struct decon_device *decon = get_decon_drvdata(0);
+
+ decon_hiber_block(decon);
+
+ dsim_dbg("%s +\n", __func__);
+ if (IS_DSIM_OFF_STATE(dsim)) {
+ dsim_err("%s: DSIM is not ready. state(%d)\n", __func__,
+ dsim->state);
+ goto exit;
+ }
+
+ /* If already FIFO empty even though the timer is no pending */
+ if (!timer_pending(&dsim->cmd_timer)
+ && dsim_reg_header_fifo_is_empty(dsim->id)) {
+ reinit_completion(&dsim->ph_wr_comp);
+ dsim_reg_clear_int(dsim->id, DSIM_INTSRC_SFR_PH_FIFO_EMPTY);
+ goto exit;
+ }
+
+ __dsim_dump(dsim);
+
+exit:
+ decon_hiber_unblock(decon);
+ dsim_dbg("%s -\n", __func__);
+ return;
+}
+
+#if defined(CONFIG_EXYNOS9610_BTS)
+static void dsim_bts_print_info(struct bts_decon_info *info)
+{
+ int i;
+
+ for (i = 0; i < BTS_DPP_MAX; ++i) {
+ if (!info->dpp[i].used)
+ continue;
+
+ dsim_info("\t\tDPP[%d] b(%d) s(%d %d) d(%d %d %d %d) r(%d)\n",
+ i, info->dpp[i].bpp,
+ info->dpp[i].src_w, info->dpp[i].src_h,
+ info->dpp[i].dst.x1, info->dpp[i].dst.x2,
+ info->dpp[i].dst.y1, info->dpp[i].dst.y2,
+ info->dpp[i].rotation);
+ }
+}
+#endif
+
+static void dsim_underrun_info(struct dsim_device *dsim)
+{
+#if defined(CONFIG_EXYNOS9610_BTS)
+ struct decon_device *decon;
+ int i;
+
+ dsim_info("\tMIF(%lu), INT(%lu), DISP(%lu)\n",
+ cal_dfs_get_rate(ACPM_DVFS_MIF),
+ cal_dfs_get_rate(ACPM_DVFS_INT),
+ cal_dfs_get_rate(ACPM_DVFS_DISP));
+
+ for (i = 0; i < MAX_DECON_CNT; ++i) {
+ decon = get_decon_drvdata(i);
+
+ if (decon) {
+ dsim_info("\tDECON%d: bw(%u %u), disp(%u %u), p(%u)\n",
+ decon->id,
+ decon->bts.prev_total_bw,
+ decon->bts.total_bw,
+ decon->bts.prev_max_disp_freq,
+ decon->bts.max_disp_freq,
+ decon->bts.peak);
+ dsim_bts_print_info(&decon->bts.bts_info);
+ }
+ }
+#endif
+}
+
+static irqreturn_t dsim_irq_handler(int irq, void *dev_id)
+{
+ unsigned int int_src;
+ struct dsim_device *dsim = dev_id;
+ struct decon_device *decon = get_decon_drvdata(0);
+#ifdef CONFIG_EXYNOS_PD
+ int active;
+#endif
+
+ spin_lock(&dsim->slock);
+
+#ifdef CONFIG_EXYNOS_PD
+ active = pm_runtime_active(dsim->dev);
+ if (!active) {
+ dsim_info("dsim power(%d), state(%d)\n", active, dsim->state);
+ spin_unlock(&dsim->slock);
+ return IRQ_HANDLED;
+ }
+#endif
+
+ int_src = dsim_reg_get_int_and_clear(dsim->id);
+ if (int_src & DSIM_INTSRC_SFR_PH_FIFO_EMPTY) {
+ del_timer(&dsim->cmd_timer);
+ complete(&dsim->ph_wr_comp);
+ dsim_dbg("dsim%d PH_FIFO_EMPTY irq occurs\n", dsim->id);
+ }
+ if (int_src & DSIM_INTSRC_RX_DATA_DONE)
+ complete(&dsim->rd_comp);
+ if (int_src & DSIM_INTSRC_FRAME_DONE)
+ dsim_dbg("dsim%d framedone irq occurs\n", dsim->id);
+ if (int_src & DSIM_INTSRC_ERR_RX_ECC)
+ dsim_err("RX ECC Multibit error was detected!\n");
+
+ if (int_src & DSIM_INTSRC_UNDER_RUN) {
+ dsim->total_underrun_cnt++;
+ dsim_info("dsim%d underrun irq occurs(%d)\n", dsim->id,
+ dsim->total_underrun_cnt);
+ dsim_underrun_info(dsim);
+ }
+ if (int_src & DSIM_INTSRC_VT_STATUS) {
+ dsim_dbg("dsim%d vt_status(vsync) irq occurs\n", dsim->id);
+ if (decon) {
+ decon->vsync.timestamp = ktime_get();
+ wake_up_interruptible_all(&decon->vsync.wait);
+ }
+ }
+
+ spin_unlock(&dsim->slock);
+
+ return IRQ_HANDLED;
+}
+
+static void dsim_clocks_info(struct dsim_device *dsim)
+{
+}
+
+static int dsim_get_clocks(struct dsim_device *dsim)
+{
+ dsim->res.aclk = devm_clk_get(dsim->dev, "aclk");
+ if (IS_ERR_OR_NULL(dsim->res.aclk)) {
+ dsim_err("failed to get aclk\n");
+ return PTR_ERR(dsim->res.aclk);
+ }
+
+ return 0;
+}
+
+static int dsim_get_gpios(struct dsim_device *dsim)
+{
+ struct device *dev = dsim->dev;
+ struct dsim_resources *res = &dsim->res;
+
+ dsim_info("%s +\n", __func__);
+
+ if (of_get_property(dev->of_node, "gpios", NULL) != NULL) {
+ /* panel reset */
+ res->lcd_reset = of_get_gpio(dev->of_node, 0);
+ if (res->lcd_reset < 0) {
+ dsim_err("failed to get lcd reset GPIO");
+ return -ENODEV;
+ }
+ res->lcd_power[0] = of_get_gpio(dev->of_node, 1);
+ if (res->lcd_power[0] < 0) {
+ res->lcd_power[0] = -1;
+ dsim_info("This board doesn't support LCD power GPIO");
+ }
+ res->lcd_power[1] = of_get_gpio(dev->of_node, 2);
+ if (res->lcd_power[1] < 0) {
+ res->lcd_power[1] = -1;
+ dsim_info("This board doesn't support 2nd LCD power GPIO");
+ }
+ }
+
+ dsim_info("%s -\n", __func__);
+ return 0;
+}
+
+static int dsim_get_regulator(struct dsim_device *dsim)
+{
+ struct device *dev = dsim->dev;
+ struct dsim_resources *res = &dsim->res;
+
+ char *str_regulator_1p8v = NULL;
+ char *str_regulator_3p3v = NULL;
+
+ res->regulator_1p8v = NULL;
+ res->regulator_3p3v = NULL;
+
+ if(!of_property_read_string(dev->of_node, "regulator_1p8v",
+ (const char **)&str_regulator_1p8v)) {
+ res->regulator_1p8v = regulator_get(dev, str_regulator_1p8v);
+ if (IS_ERR(res->regulator_1p8v)) {
+ dsim_err("%s : dsim regulator 1.8V get failed\n", __func__);
+ res->regulator_1p8v = NULL;
+ }
+ }
+
+ if(!of_property_read_string(dev->of_node, "regulator_3p3v",
+ (const char **)&str_regulator_3p3v)) {
+ res->regulator_3p3v = regulator_get(dev, str_regulator_3p3v);
+ if (IS_ERR(res->regulator_3p3v)) {
+ dsim_err("%s : dsim regulator 3.3V get failed\n", __func__);
+ res->regulator_3p3v = NULL;
+ }
+ }
+
+ return 0;
+}
+
+int dsim_reset_panel(struct dsim_device *dsim)
+{
+ struct dsim_resources *res = &dsim->res;
+ int ret;
+
+ dsim_dbg("%s +\n", __func__);
+
+ ret = gpio_request_one(res->lcd_reset, GPIOF_OUT_INIT_HIGH, "lcd_reset");
+ if (ret < 0) {
+ dsim_err("failed to get LCD reset GPIO\n");
+ return -EINVAL;
+ }
+
+ usleep_range(5000, 6000);
+ gpio_set_value(res->lcd_reset, 0);
+ usleep_range(5000, 6000);
+ gpio_set_value(res->lcd_reset, 1);
+
+ gpio_free(res->lcd_reset);
+
+ usleep_range(10000, 11000);
+
+ dsim_dbg("%s -\n", __func__);
+ return 0;
+}
+
+int dsim_set_panel_power(struct dsim_device *dsim, bool on)
+{
+ struct dsim_resources *res = &dsim->res;
+ int ret;
+
+ dsim_dbg("%s(%d) +\n", __func__, on);
+
+
+ if (on) {
+ if (res->lcd_power[0] > 0) {
+ ret = gpio_request_one(res->lcd_power[0],
+ GPIOF_OUT_INIT_HIGH, "lcd_power0");
+ if (ret < 0) {
+ dsim_err("failed LCD power on\n");
+ return -EINVAL;
+ }
+ gpio_free(res->lcd_power[0]);
+ usleep_range(10000, 11000);
+ }
+
+ if (res->lcd_power[1] > 0) {
+ ret = gpio_request_one(res->lcd_power[1],
+ GPIOF_OUT_INIT_HIGH, "lcd_power1");
+ if (ret < 0) {
+ dsim_err("failed 2nd LCD power on\n");
+ return -EINVAL;
+ }
+ gpio_free(res->lcd_power[1]);
+ usleep_range(10000, 11000);
+ }
+ if (res->regulator_1p8v > 0) {
+ ret = regulator_enable(res->regulator_1p8v);
+ if (ret) {
+ dsim_err("%s : dsim regulator 1.8V enable failed\n", __func__);
+ return ret;
+ }
+ usleep_range(5000, 6000);
+ }
+
+ if (res->regulator_3p3v > 0) {
+ ret = regulator_enable(res->regulator_3p3v);
+ if (ret) {
+ dsim_err("%s : dsim regulator 3.3V enable failed\n", __func__);
+ return ret;
+ }
+ }
+ } else {
+ ret = gpio_request_one(res->lcd_reset, GPIOF_OUT_INIT_LOW,
+ "lcd_reset");
+ if (ret < 0) {
+ dsim_err("failed LCD reset off\n");
+ return -EINVAL;
+ }
+ gpio_free(res->lcd_reset);
+
+ if (res->lcd_power[0] > 0) {
+ ret = gpio_request_one(res->lcd_power[0],
+ GPIOF_OUT_INIT_LOW, "lcd_power0");
+ if (ret < 0) {
+ dsim_err("failed LCD power off\n");
+ return -EINVAL;
+ }
+ gpio_free(res->lcd_power[0]);
+ usleep_range(5000, 6000);
+ }
+
+ if (res->lcd_power[1] > 0) {
+ ret = gpio_request_one(res->lcd_power[1],
+ GPIOF_OUT_INIT_LOW, "lcd_power1");
+ if (ret < 0) {
+ dsim_err("failed 2nd LCD power off\n");
+ return -EINVAL;
+ }
+ gpio_free(res->lcd_power[1]);
+ usleep_range(5000, 6000);
+ }
+ if (res->regulator_1p8v > 0) {
+ ret = regulator_disable(res->regulator_1p8v);
+ if (ret) {
+ dsim_err("%s : dsim regulator 1.8V disable failed\n", __func__);
+ return ret;
+ }
+ }
+
+ if (res->regulator_3p3v > 0) {
+ ret = regulator_disable(res->regulator_3p3v);
+ if (ret) {
+ dsim_err("%s : dsim regulator 3.3V disable failed\n", __func__);
+ return ret;
+ }
+ }
+ }
+
+ dsim_dbg("%s(%d) -\n", __func__, on);
+
+ return 0;
+}
+
+static int _dsim_enable(struct dsim_device *dsim, enum dsim_state state)
+{
+ bool panel_ctrl;
+
+ if (IS_DSIM_ON_STATE(dsim)) {
+ dsim_warn("%s dsim already on(%s)\n",
+ __func__, dsim_state_names[dsim->state]);
+ dsim->state = state;
+ return 0;
+ }
+
+ dsim_dbg("%s %s +\n", __func__, dsim_state_names[dsim->state]);
+
+ pm_runtime_get_sync(dsim->dev);
+
+ /* DPHY reset control from DSIM */
+ dpu_sysreg_select_dphy_rst_control(dsim->res.ss_regs, dsim->id, 1);
+
+ /* DPHY power on : iso release */
+ phy_power_on(dsim->phy);
+ if (dsim->phy_ex)
+ phy_power_on(dsim->phy_ex);
+
+ panel_ctrl = (state == DSIM_STATE_ON) ? true : false;
+ dsim_reg_init(dsim->id, &dsim->lcd_info, &dsim->clks, panel_ctrl);
+ dsim_reg_start(dsim->id);
+
+ dsim->state = state;
+ enable_irq(dsim->res.irq);
+
+ return 0;
+}
+
+static int dsim_enable(struct dsim_device *dsim)
+{
+ int ret;
+ enum dsim_state prev_state = dsim->state;
+ enum dsim_state next_state = DSIM_STATE_ON;
+
+ if (prev_state == next_state) {
+ dsim_warn("dsim-%d %s already %s state\n", dsim->id,
+ __func__, dsim_state_names[dsim->state]);
+ return 0;
+ }
+
+ dsim_info("dsim-%d %s +\n", dsim->id, __func__);
+ ret = _dsim_enable(dsim, next_state);
+ if (ret < 0) {
+ dsim_err("dsim-%d failed to set %s (ret %d)\n",
+ dsim->id, dsim_state_names[next_state], ret);
+ goto out;
+ }
+
+ if (prev_state != DSIM_STATE_INIT)
+ call_panel_ops(dsim, displayon, dsim);
+
+ dsim_info("dsim-%d %s - (state:%s -> %s)\n", dsim->id, __func__,
+ dsim_state_names[prev_state],
+ dsim_state_names[dsim->state]);
+
+out:
+ return ret;
+}
+
+static int dsim_doze(struct dsim_device *dsim)
+{
+ int ret;
+ enum dsim_state prev_state = dsim->state;
+ enum dsim_state next_state = DSIM_STATE_DOZE;
+
+ if (prev_state == next_state) {
+ dsim_warn("dsim-%d %s already %s state\n", dsim->id,
+ __func__, dsim_state_names[dsim->state]);
+ return 0;
+ }
+
+ dsim_info("dsim-%d %s +\n", dsim->id, __func__);
+ ret = _dsim_enable(dsim, next_state);
+ if (ret < 0) {
+ dsim_err("dsim-%d failed to set %s (ret %d)\n",
+ dsim->id, dsim_state_names[next_state], ret);
+ goto out;
+ }
+ if (prev_state != DSIM_STATE_INIT)
+ call_panel_ops(dsim, doze, dsim);
+ dsim_info("dsim-%d %s - (state:%s -> %s)\n", dsim->id, __func__,
+ dsim_state_names[prev_state],
+ dsim_state_names[dsim->state]);
+
+out:
+ return ret;
+}
+
+static int _dsim_disable(struct dsim_device *dsim, enum dsim_state state)
+{
+ if (IS_DSIM_OFF_STATE(dsim)) {
+ dsim_warn("%s dsim already off(%s)\n",
+ __func__, dsim_state_names[dsim->state]);
+ if (state == DSIM_STATE_OFF)
+ dsim_set_panel_power(dsim, 0);
+ dsim->state = state;
+ return 0;
+ }
+
+ dsim_dbg("%s %s +\n", __func__, dsim_state_names[dsim->state]);
+
+ /* Wait for current read & write CMDs. */
+ mutex_lock(&dsim->cmd_lock);
+ del_timer(&dsim->cmd_timer);
+ dsim->state = state;
+ mutex_unlock(&dsim->cmd_lock);
+
+ if (dsim_reg_stop(dsim->id, dsim->data_lane) < 0)
+ __dsim_dump(dsim);
+ disable_irq(dsim->res.irq);
+
+ /* HACK */
+ phy_power_off(dsim->phy);
+ if (dsim->phy_ex)
+ phy_power_off(dsim->phy_ex);
+
+ if (state == DSIM_STATE_OFF)
+ dsim_set_panel_power(dsim, 0);
+
+ pm_runtime_put_sync(dsim->dev);
+
+ dsim_dbg("%s %s -\n", __func__, dsim_state_names[dsim->state]);
+
+ return 0;
+}
+
+static int dsim_disable(struct dsim_device *dsim)
+{
+ int ret;
+ enum dsim_state prev_state = dsim->state;
+ enum dsim_state next_state = DSIM_STATE_OFF;
+
+ if (prev_state == next_state) {
+ dsim_warn("dsim-%d %s already %s state\n", dsim->id,
+ __func__, dsim_state_names[dsim->state]);
+ return 0;
+ }
+
+ dsim_info("dsim-%d %s +\n", dsim->id, __func__);
+ call_panel_ops(dsim, suspend, dsim);
+ ret = _dsim_disable(dsim, next_state);
+ if (ret < 0) {
+ dsim_err("dsim-%d failed to set %s (ret %d)\n",
+ dsim->id, dsim_state_names[next_state], ret);
+ goto out;
+ }
+ dsim_info("dsim-%d %s - (state:%s -> %s)\n", dsim->id, __func__,
+ dsim_state_names[prev_state],
+ dsim_state_names[dsim->state]);
+
+out:
+ return ret;
+}
+
+static int dsim_doze_suspend(struct dsim_device *dsim)
+{
+ int ret;
+ enum dsim_state prev_state = dsim->state;
+ enum dsim_state next_state = DSIM_STATE_DOZE_SUSPEND;
+
+ if (prev_state == next_state) {
+ dsim_warn("dsim-%d %s already %s state\n", dsim->id,
+ __func__, dsim_state_names[dsim->state]);
+ return 0;
+ }
+
+ dsim_info("dsim-%d %s +\n", dsim->id, __func__);
+ call_panel_ops(dsim, doze_suspend, dsim);
+ ret = _dsim_disable(dsim, next_state);
+ if (ret < 0) {
+ dsim_err("dsim-%d failed to set %s (ret %d)\n",
+ dsim->id, dsim_state_names[next_state], ret);
+ goto out;
+ }
+ dsim_info("dsim-%d %s - (state:%s -> %s)\n", dsim->id, __func__,
+ dsim_state_names[prev_state],
+ dsim_state_names[dsim->state]);
+
+out:
+ return ret;
+}
+
+static int dsim_enter_ulps(struct dsim_device *dsim)
+{
+ int ret = 0;
+
+ DPU_EVENT_START();
+ dsim_dbg("%s +\n", __func__);
+
+ if (!IS_DSIM_ON_STATE(dsim)) {
+ ret = -EBUSY;
+ goto err;
+ }
+
+ /* Wait for current read & write CMDs. */
+ mutex_lock(&dsim->cmd_lock);
+ dsim->state = DSIM_STATE_ULPS;
+ mutex_unlock(&dsim->cmd_lock);
+
+ disable_irq(dsim->res.irq);
+ ret = dsim_reg_stop_and_enter_ulps(dsim->id, dsim->lcd_info.ddi_type,
+ dsim->data_lane);
+ if (ret < 0)
+ dsim_dump(dsim);
+
+ phy_power_off(dsim->phy);
+ if (dsim->phy_ex)
+ phy_power_off(dsim->phy_ex);
+
+ pm_runtime_put_sync(dsim->dev);
+
+ DPU_EVENT_LOG(DPU_EVT_ENTER_ULPS, &dsim->sd, start);
+err:
+ dsim_dbg("%s -\n", __func__);
+ return ret;
+}
+
+static int dsim_exit_ulps(struct dsim_device *dsim)
+{
+ int ret = 0;
+
+ DPU_EVENT_START();
+ dsim_dbg("%s +\n", __func__);
+
+ if (dsim->state != DSIM_STATE_ULPS) {
+ ret = -EBUSY;
+ goto err;
+ }
+
+ pm_runtime_get_sync(dsim->dev);
+
+ /* DPHY reset control from DSIM */
+ dpu_sysreg_select_dphy_rst_control(dsim->res.ss_regs, dsim->id, 1);
+ /* DPHY power on : iso release */
+ phy_power_on(dsim->phy);
+ if (dsim->phy_ex)
+ phy_power_on(dsim->phy_ex);
+
+ dsim_reg_init(dsim->id, &dsim->lcd_info, &dsim->clks, false);
+ ret = dsim_reg_exit_ulps_and_start(dsim->id, dsim->lcd_info.ddi_type,
+ dsim->data_lane);
+ if (ret < 0)
+ dsim_dump(dsim);
+
+ enable_irq(dsim->res.irq);
+
+ dsim->state = DSIM_STATE_ON;
+ DPU_EVENT_LOG(DPU_EVT_EXIT_ULPS, &dsim->sd, start);
+err:
+ dsim_dbg("%s -\n", __func__);
+
+ return 0;
+}
+
+static int dsim_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct dsim_device *dsim = container_of(sd, struct dsim_device, sd);
+
+ if (enable)
+ return dsim_enable(dsim);
+ else
+ return dsim_disable(dsim);
+}
+
+static long dsim_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+ struct dsim_device *dsim = container_of(sd, struct dsim_device, sd);
+ int ret = 0;
+
+ switch (cmd) {
+ case DSIM_IOC_GET_LCD_INFO:
+ v4l2_set_subdev_hostdata(sd, &dsim->lcd_info);
+ break;
+
+ case DSIM_IOC_ENTER_ULPS:
+ if ((unsigned long)arg)
+ ret = dsim_enter_ulps(dsim);
+ else
+ ret = dsim_exit_ulps(dsim);
+ break;
+
+ case DSIM_IOC_DUMP:
+ dsim_dump(dsim);
+ break;
+
+ case DSIM_IOC_GET_WCLK:
+ v4l2_set_subdev_hostdata(sd, &dsim->clks.word_clk);
+ break;
+
+ case EXYNOS_DPU_GET_ACLK:
+ return clk_get_rate(dsim->res.aclk);
+
+ case DSIM_IOC_DOZE:
+ ret = dsim_doze(dsim);
+ break;
+
+ case DSIM_IOC_DOZE_SUSPEND:
+ ret = dsim_doze_suspend(dsim);
+ break;
+
+ default:
+ dsim_err("unsupported ioctl");
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct v4l2_subdev_core_ops dsim_sd_core_ops = {
+ .ioctl = dsim_ioctl,
+};
+
+static const struct v4l2_subdev_video_ops dsim_sd_video_ops = {
+ .s_stream = dsim_s_stream,
+};
+
+static const struct v4l2_subdev_ops dsim_subdev_ops = {
+ .core = &dsim_sd_core_ops,
+ .video = &dsim_sd_video_ops,
+};
+
+static void dsim_init_subdev(struct dsim_device *dsim)
+{
+ struct v4l2_subdev *sd = &dsim->sd;
+
+ v4l2_subdev_init(sd, &dsim_subdev_ops);
+ sd->owner = THIS_MODULE;
+ sd->grp_id = dsim->id;
+ snprintf(sd->name, sizeof(sd->name), "%s.%d", "dsim-sd", dsim->id);
+ v4l2_set_subdevdata(sd, dsim);
+}
+
+static int dsim_cmd_sysfs_write(struct dsim_device *dsim, bool on)
+{
+ int ret = 0;
+
+ if (on)
+ ret = dsim_write_data(dsim, MIPI_DSI_DCS_SHORT_WRITE,
+ MIPI_DCS_SET_DISPLAY_ON, 0);
+ else
+ ret = dsim_write_data(dsim, MIPI_DSI_DCS_SHORT_WRITE,
+ MIPI_DCS_SET_DISPLAY_OFF, 0);
+ if (ret < 0)
+ dsim_err("Failed to write test data!\n");
+ else
+ dsim_dbg("Succeeded to write test data!\n");
+
+ return ret;
+}
+
+static int dsim_cmd_sysfs_read(struct dsim_device *dsim)
+{
+ int ret = 0;
+ unsigned int id;
+ u8 buf[4];
+
+ /* dsim sends the request for the lcd id and gets it buffer */
+ ret = dsim_read_data(dsim, MIPI_DSI_DCS_READ,
+ MIPI_DCS_GET_DISPLAY_ID, DSIM_DDI_ID_LEN, buf);
+ id = *(unsigned int *)buf;
+ if (ret < 0)
+ dsim_err("Failed to read panel id!\n");
+ else
+ dsim_info("Suceeded to read panel id : 0x%08x\n", id);
+
+ return ret;
+}
+
+static ssize_t dsim_cmd_sysfs_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return 0;
+}
+
+static ssize_t dsim_cmd_sysfs_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int ret;
+ unsigned long cmd;
+ struct dsim_device *dsim = dev_get_drvdata(dev);
+
+ ret = kstrtoul(buf, 0, &cmd);
+ if (ret)
+ return ret;
+
+ switch (cmd) {
+ case 1:
+ ret = dsim_cmd_sysfs_read(dsim);
+ call_panel_ops(dsim, dump, dsim);
+ if (ret)
+ return ret;
+ break;
+ case 2:
+ ret = dsim_cmd_sysfs_write(dsim, true);
+ dsim_info("Dsim write command, display on!!\n");
+ if (ret)
+ return ret;
+ break;
+ case 3:
+ ret = dsim_cmd_sysfs_write(dsim, false);
+ dsim_info("Dsim write command, display off!!\n");
+ if (ret)
+ return ret;
+ break;
+ default :
+ dsim_info("unsupportable command\n");
+ break;
+ }
+
+ return count;
+}
+static DEVICE_ATTR(cmd_rw, 0644, dsim_cmd_sysfs_show, dsim_cmd_sysfs_store);
+
+int dsim_create_cmd_rw_sysfs(struct dsim_device *dsim)
+{
+ int ret = 0;
+
+ ret = device_create_file(dsim->dev, &dev_attr_cmd_rw);
+ if (ret)
+ dsim_err("failed to create command read & write sysfs\n");
+
+ return ret;
+}
+
+static void dsim_parse_lcd_info(struct dsim_device *dsim)
+{
+ u32 res[14];
+ struct device_node *node;
+ unsigned int mres_num = 1;
+ u32 mres_w[3] = {0, };
+ u32 mres_h[3] = {0, };
+ u32 mres_dsc_w[3] = {0, };
+ u32 mres_dsc_h[3] = {0, };
+ u32 mres_dsc_en[3] = {0, };
+ u32 hdr_num = 0;
+ u32 hdr_type[HDR_CAPA_NUM] = {0, };
+ u32 hdr_mxl = 0;
+ u32 hdr_mal = 0;
+ u32 hdr_mnl = 0;
+ int k;
+
+ node = of_parse_phandle(dsim->dev->of_node, "lcd_info", 0);
+
+ of_property_read_u32(node, "mode", &dsim->lcd_info.mode);
+ dsim_info("%s mode\n", dsim->lcd_info.mode ? "command" : "video");
+
+ of_property_read_u32_array(node, "resolution", res, 2);
+ dsim->lcd_info.xres = res[0];
+ dsim->lcd_info.yres = res[1];
+ dsim_info("LCD(%s) resolution: xres(%d), yres(%d)\n",
+ of_node_full_name(node), res[0], res[1]);
+
+ of_property_read_u32_array(node, "size", res, 2);
+ dsim->lcd_info.width = res[0];
+ dsim->lcd_info.height = res[1];
+ dsim_dbg("LCD size: width(%d), height(%d)\n", res[0], res[1]);
+
+ of_property_read_u32(node, "timing,refresh", &dsim->lcd_info.fps);
+ dsim_dbg("LCD refresh rate(%d)\n", dsim->lcd_info.fps);
+
+ of_property_read_u32_array(node, "timing,h-porch", res, 3);
+ dsim->lcd_info.hbp = res[0];
+ dsim->lcd_info.hfp = res[1];
+ dsim->lcd_info.hsa = res[2];
+ dsim_dbg("hbp(%d), hfp(%d), hsa(%d)\n", res[0], res[1], res[2]);
+
+ of_property_read_u32_array(node, "timing,v-porch", res, 3);
+ dsim->lcd_info.vbp = res[0];
+ dsim->lcd_info.vfp = res[1];
+ dsim->lcd_info.vsa = res[2];
+ dsim_dbg("vbp(%d), vfp(%d), vsa(%d)\n", res[0], res[1], res[2]);
+
+ of_property_read_u32(node, "timing,dsi-hs-clk", &dsim->lcd_info.hs_clk);
+ dsim->clks.hs_clk = dsim->lcd_info.hs_clk;
+ dsim_dbg("requested hs clock(%d)\n", dsim->lcd_info.hs_clk);
+
+#if defined(CONFIG_EXYNOS_DSIM_DITHER)
+ of_property_read_u32_array(node, "timing,pmsk", res, 14);
+#else
+ of_property_read_u32_array(node, "timing,pmsk", res, 4);
+#endif
+ dsim->lcd_info.dphy_pms.p = res[0];
+ dsim->lcd_info.dphy_pms.m = res[1];
+ dsim->lcd_info.dphy_pms.s = res[2];
+ dsim->lcd_info.dphy_pms.k = res[3];
+ dsim_dbg("p(%d), m(%d), s(%d), k(%d)\n", res[0], res[1], res[2], res[3]);
+#if defined(CONFIG_EXYNOS_DSIM_DITHER)
+ dsim->lcd_info.dphy_pms.mfr = res[4];
+ dsim->lcd_info.dphy_pms.mrr = res[5];
+ dsim->lcd_info.dphy_pms.sel_pf = res[6];
+ dsim->lcd_info.dphy_pms.icp = res[7];
+ dsim->lcd_info.dphy_pms.afc_enb = res[8];
+ dsim->lcd_info.dphy_pms.extafc = res[9];
+ dsim->lcd_info.dphy_pms.feed_en = res[10];
+ dsim->lcd_info.dphy_pms.fsel = res[11];
+ dsim->lcd_info.dphy_pms.fout_mask = res[12];
+ dsim->lcd_info.dphy_pms.rsel = res[13];
+ dsim_dbg(" mfr(%d), mrr(0x%x), sel_pf(%d), icp(%d)\n",
+ res[4], res[5], res[6], res[7]);
+ dsim_dbg(" afc_enb(%d), extafc(%d), feed_en(%d), fsel(%d)\n",
+ res[8], res[9], res[10], res[11]);
+ dsim_dbg(" fout_mask(%d), rsel(%d)\n", res[12], res[13]);
+#endif
+
+ of_property_read_u32(node, "timing,dsi-escape-clk",
+ &dsim->lcd_info.esc_clk);
+ dsim->clks.esc_clk = dsim->lcd_info.esc_clk;
+ dsim_dbg("requested escape clock(%d)\n", dsim->lcd_info.esc_clk);
+
+ of_property_read_u32(node, "mic_en", &dsim->lcd_info.mic_enabled);
+ dsim_info("mic enabled (%d)\n", dsim->lcd_info.mic_enabled);
+
+ of_property_read_u32(node, "type_of_ddi", &dsim->lcd_info.ddi_type);
+ dsim_dbg("ddi type(%d)\n", dsim->lcd_info.ddi_type);
+
+ of_property_read_u32(node, "dsc_en", &dsim->lcd_info.dsc_enabled);
+ dsim_info("dsc is %s\n", dsim->lcd_info.dsc_enabled ? "enabled" : "disabled");
+
+ if (dsim->lcd_info.dsc_enabled) {
+ of_property_read_u32(node, "dsc_cnt", &dsim->lcd_info.dsc_cnt);
+ dsim_info("dsc count(%d)\n", dsim->lcd_info.dsc_cnt);
+ of_property_read_u32(node, "dsc_slice_num",
+ &dsim->lcd_info.dsc_slice_num);
+ dsim_info("dsc slice count(%d)\n", dsim->lcd_info.dsc_slice_num);
+ of_property_read_u32(node, "dsc_slice_h",
+ &dsim->lcd_info.dsc_slice_h);
+ dsim_info("dsc slice height(%d)\n", dsim->lcd_info.dsc_slice_h);
+ }
+
+ of_property_read_u32(node, "data_lane", &dsim->data_lane_cnt);
+ dsim_info("using data lane count(%d)\n", dsim->data_lane_cnt);
+
+ dsim->lcd_info.data_lane = dsim->data_lane_cnt;
+
+ of_property_read_u32(node, "mres_en", &dsim->lcd_info.dt_lcd_mres.mres_en);
+ dsim_info("mres_en(%d)\n", dsim->lcd_info.dt_lcd_mres.mres_en);
+ dsim->lcd_info.mres_mode = 0; /* 0=WQHD, 1=FHD, 2=HD */
+ dsim->lcd_info.dt_lcd_mres.mres_number = mres_num; /* default = 1 */
+
+ if (dsim->lcd_info.dt_lcd_mres.mres_en) {
+ of_property_read_u32(node, "mres_number", &mres_num);
+ dsim->lcd_info.dt_lcd_mres.mres_number = mres_num;
+ dsim_info("mres_number(%d)\n", mres_num);
+
+ of_property_read_u32_array(node, "mres_width", mres_w, mres_num);
+ of_property_read_u32_array(node, "mres_height", mres_h, mres_num);
+ of_property_read_u32_array(node, "mres_dsc_width", mres_dsc_w, mres_num);
+ of_property_read_u32_array(node, "mres_dsc_height", mres_dsc_h, mres_num);
+ of_property_read_u32_array(node, "mres_dsc_en", mres_dsc_en, mres_num);
+
+ switch (mres_num) {
+ case 3:
+ dsim->lcd_info.dt_lcd_mres.res_info[2].width = mres_w[2];
+ dsim->lcd_info.dt_lcd_mres.res_info[2].height = mres_h[2];
+ dsim->lcd_info.dt_lcd_mres.res_info[2].dsc_en = mres_dsc_en[2];
+ dsim->lcd_info.dt_lcd_mres.res_info[2].dsc_width = mres_dsc_w[2];
+ dsim->lcd_info.dt_lcd_mres.res_info[2].dsc_height = mres_dsc_h[2];
+ case 2:
+ dsim->lcd_info.dt_lcd_mres.res_info[1].width = mres_w[1];
+ dsim->lcd_info.dt_lcd_mres.res_info[1].height = mres_h[1];
+ dsim->lcd_info.dt_lcd_mres.res_info[1].dsc_en = mres_dsc_en[1];
+ dsim->lcd_info.dt_lcd_mres.res_info[1].dsc_width = mres_dsc_w[1];
+ dsim->lcd_info.dt_lcd_mres.res_info[1].dsc_height = mres_dsc_h[1];
+ case 1:
+ dsim->lcd_info.dt_lcd_mres.res_info[0].width = mres_w[0];
+ dsim->lcd_info.dt_lcd_mres.res_info[0].height = mres_h[0];
+ dsim->lcd_info.dt_lcd_mres.res_info[0].dsc_en = mres_dsc_en[0];
+ dsim->lcd_info.dt_lcd_mres.res_info[0].dsc_width = mres_dsc_w[0];
+ dsim->lcd_info.dt_lcd_mres.res_info[0].dsc_height = mres_dsc_h[0];
+ break;
+ default:
+ dsim->lcd_info.dt_lcd_mres.res_info[0].width = dsim->lcd_info.width;
+ dsim->lcd_info.dt_lcd_mres.res_info[0].height = dsim->lcd_info.height;
+ dsim_warn("check multi-resolution configurations at DT\n");
+ break;
+ }
+ dsim_info("[LCD multi(%d)-resolution info] 1st(%dx%d), 2nd(%dx%d), 3rd(%dx%d)\n",
+ mres_num, mres_w[0], mres_h[0],
+ mres_w[1], mres_h[1], mres_w[2], mres_h[2]);
+ } else {
+ dsim->lcd_info.dt_lcd_mres.res_info[0].width = dsim->lcd_info.width;
+ dsim->lcd_info.dt_lcd_mres.res_info[0].height = dsim->lcd_info.height;
+ }
+
+ if (dsim->lcd_info.mode == DECON_MIPI_COMMAND_MODE) {
+ of_property_read_u32_array(node, "cmd_underrun_lp_ref",
+ dsim->lcd_info.cmd_underrun_lp_ref,
+ dsim->lcd_info.dt_lcd_mres.mres_number);
+ for (k = 0; k < dsim->lcd_info.dt_lcd_mres.mres_number; k++)
+ dsim_info("mres[%d] cmd_underrun_lp_ref(%d)\n", k,
+ dsim->lcd_info.cmd_underrun_lp_ref[k]);
+ } else {
+ of_property_read_u32(node, "vt_compensation",
+ &dsim->lcd_info.vt_compensation);
+ dsim_info("vt_compensation(%d)\n", dsim->lcd_info.vt_compensation);
+ }
+
+ /* HDR info */
+ of_property_read_u32(node, "hdr_num", &hdr_num);
+ dsim->lcd_info.dt_lcd_hdr.hdr_num = hdr_num;
+ dsim_info("hdr_num(%d)\n", hdr_num);
+
+ if (hdr_num != 0) {
+ of_property_read_u32_array(node, "hdr_type", hdr_type, hdr_num);
+ for (k = 0; k < hdr_num; k++) {
+ dsim->lcd_info.dt_lcd_hdr.hdr_type[k] = hdr_type[k];
+ dsim_info("hdr_type[%d] = %d\n", k, hdr_type[k]);
+ }
+
+ of_property_read_u32(node, "hdr_max_luma", &hdr_mxl);
+ of_property_read_u32(node, "hdr_max_avg_luma", &hdr_mal);
+ of_property_read_u32(node, "hdr_min_luma", &hdr_mnl);
+ dsim->lcd_info.dt_lcd_hdr.hdr_max_luma = hdr_mxl;
+ dsim->lcd_info.dt_lcd_hdr.hdr_max_avg_luma = hdr_mal;
+ dsim->lcd_info.dt_lcd_hdr.hdr_min_luma = hdr_mnl;
+ dsim_info("hdr_max_luma(%d), hdr_max_avg_luma(%d), hdr_min_luma(%d)\n",
+ hdr_mxl, hdr_mal, hdr_mnl);
+ }
+}
+
+static int dsim_parse_dt(struct dsim_device *dsim, struct device *dev)
+{
+ if (IS_ERR_OR_NULL(dev->of_node)) {
+ dsim_err("no device tree information\n");
+ return -EINVAL;
+ }
+
+ dsim->id = of_alias_get_id(dev->of_node, "dsim");
+ dsim_info("dsim(%d) probe start..\n", dsim->id);
+
+ dsim->phy = devm_phy_get(dev, "dsim_dphy");
+ if (IS_ERR_OR_NULL(dsim->phy)) {
+ dsim_err("failed to get phy\n");
+ return PTR_ERR(dsim->phy);
+ }
+
+ dsim->phy_ex = devm_phy_get(dev, "dsim_dphy_extra");
+ if (IS_ERR_OR_NULL(dsim->phy_ex)) {
+ dsim_err("failed to get extra phy. It's not mandatary.\n");
+ dsim->phy_ex = NULL;
+ }
+
+ dsim->dev = dev;
+ dsim_get_gpios(dsim);
+ dsim_get_regulator(dsim);
+ dsim_parse_lcd_info(dsim);
+
+ return 0;
+}
+
+static void dsim_register_panel(struct dsim_device *dsim)
+{
+#if IS_ENABLED(CONFIG_EXYNOS_DECON_LCD_S6E3HA2K)
+ dsim->panel_ops = &s6e3ha2k_mipi_lcd_driver;
+#elif IS_ENABLED(CONFIG_EXYNOS_DECON_LCD_S6E3HF4)
+ dsim->panel_ops = &s6e3hf4_mipi_lcd_driver;
+#elif IS_ENABLED(CONFIG_EXYNOS_DECON_LCD_S6E3HA6)
+ dsim->panel_ops = &s6e3ha6_mipi_lcd_driver;
+#elif IS_ENABLED(CONFIG_EXYNOS_DECON_LCD_S6E3HA8)
+ dsim->panel_ops = &s6e3ha8_mipi_lcd_driver;
+#elif IS_ENABLED(CONFIG_EXYNOS_DECON_LCD_S6E3AA2)
+ dsim->panel_ops = &s6e3aa2_mipi_lcd_driver;
+#elif IS_ENABLED(CONFIG_EXYNOS_DECON_LCD_S6E3FA0)
+ dsim->panel_ops = &s6e3fa0_mipi_lcd_driver;
+#elif IS_ENABLED(CONFIG_EXYNOS_DECON_LCD_EMUL_DISP)
+ dsim->panel_ops = &emul_disp_mipi_lcd_driver;
+#else
+ dsim->panel_ops = &s6e3ha2k_mipi_lcd_driver;
+#endif
+}
+
+static int dsim_get_data_lanes(struct dsim_device *dsim)
+{
+ int i;
+
+ if (dsim->data_lane_cnt > MAX_DSIM_DATALANE_CNT) {
+ dsim_err("%d data lane couldn't be supported\n",
+ dsim->data_lane_cnt);
+ return -EINVAL;
+ }
+
+ dsim->data_lane = DSIM_LANE_CLOCK;
+ for (i = 1; i < dsim->data_lane_cnt + 1; ++i)
+ dsim->data_lane |= 1 << i;
+
+ dsim_info("%s: lanes(0x%x)\n", __func__, dsim->data_lane);
+
+ return 0;
+}
+
+static int dsim_init_resources(struct dsim_device *dsim, struct platform_device *pdev)
+{
+ struct resource *res;
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dsim_err("failed to get mem resource\n");
+ return -ENOENT;
+ }
+ dsim_info("res: start(0x%x), end(0x%x)\n", (u32)res->start, (u32)res->end);
+
+ dsim->res.regs = devm_ioremap_resource(dsim->dev, res);
+ if (!dsim->res.regs) {
+ dsim_err("failed to remap DSIM SFR region\n");
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dsim_err("failed to get irq resource\n");
+ return -ENOENT;
+ }
+
+ dsim->res.irq = res->start;
+ ret = devm_request_irq(dsim->dev, res->start,
+ dsim_irq_handler, 0, pdev->name, dsim);
+ if (ret) {
+ dsim_err("failed to install DSIM irq\n");
+ return -EINVAL;
+ }
+ disable_irq(dsim->res.irq);
+
+ dsim->res.ss_regs = dpu_get_sysreg_addr();
+ if (IS_ERR_OR_NULL(dsim->res.ss_regs)) {
+ dsim_err("failed to get sysreg addr\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int dsim_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct device *dev = &pdev->dev;
+ struct dsim_device *dsim = NULL;
+
+ dsim = devm_kzalloc(dev, sizeof(struct dsim_device), GFP_KERNEL);
+ if (!dsim) {
+ dsim_err("failed to allocate dsim device.\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+#if !defined(CONFIG_SUPPORT_LEGACY_ION)
+ dma_set_mask(dev, DMA_BIT_MASK(36));
+#endif
+ ret = dsim_parse_dt(dsim, dev);
+ if (ret)
+ goto err_dt;
+
+ dsim_drvdata[dsim->id] = dsim;
+ ret = dsim_get_clocks(dsim);
+ if (ret)
+ goto err_dt;
+
+ spin_lock_init(&dsim->slock);
+ mutex_init(&dsim->cmd_lock);
+ init_completion(&dsim->ph_wr_comp);
+ init_completion(&dsim->rd_comp);
+
+ ret = dsim_init_resources(dsim, pdev);
+ if (ret)
+ goto err_dt;
+
+ dsim_init_subdev(dsim);
+ platform_set_drvdata(pdev, dsim);
+ dsim_register_panel(dsim);
+ setup_timer(&dsim->cmd_timer, dsim_cmd_fail_detector,
+ (unsigned long)dsim);
+
+ pm_runtime_enable(dev);
+
+ ret = iovmm_activate(dev);
+ if (ret) {
+ dsim_err("failed to activate iovmm\n");
+ goto err_dt;
+ }
+ iovmm_set_fault_handler(dev, dpu_sysmmu_fault_handler, NULL);
+
+ ret = dsim_get_data_lanes(dsim);
+ if (ret)
+ goto err_dt;
+
+ phy_init(dsim->phy);
+ if (dsim->phy_ex)
+ phy_init(dsim->phy_ex);
+
+ dsim->state = DSIM_STATE_INIT;
+ dsim_enable(dsim);
+
+ /* TODO: If you want to enable DSIM BIST mode. you must turn on LCD here */
+#if !defined(BRINGUP_DSIM_BIST)
+ call_panel_ops(dsim, probe, dsim);
+#else
+ /* TODO: This is for dsim BIST mode in zebu emulator. only for test*/
+ call_panel_ops(dsim, displayon, dsim);
+ dsim_reg_set_bist(dsim->id, true);
+#endif
+
+ /* for debug */
+ /* dsim_dump(dsim); */
+
+ dsim_clocks_info(dsim);
+ dsim_create_cmd_rw_sysfs(dsim);
+
+#ifdef DPHY_LOOP
+ dsim_reg_set_dphy_loop_back_test(dsim->id);
+#endif
+
+ dsim_info("dsim%d driver(%s mode) has been probed.\n", dsim->id,
+ dsim->lcd_info.mode == DECON_MIPI_COMMAND_MODE ? "cmd" : "video");
+ return 0;
+
+err_dt:
+ kfree(dsim);
+err:
+ return ret;
+}
+
+static int dsim_remove(struct platform_device *pdev)
+{
+ struct dsim_device *dsim = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(&pdev->dev);
+ mutex_destroy(&dsim->cmd_lock);
+ dsim_info("dsim%d driver removed\n", dsim->id);
+
+ return 0;
+}
+
+static void dsim_shutdown(struct platform_device *pdev)
+{
+ struct dsim_device *dsim = platform_get_drvdata(pdev);
+
+ DPU_EVENT_LOG(DPU_EVT_DSIM_SHUTDOWN, &dsim->sd, ktime_set(0, 0));
+ dsim_info("%s + state:%d\n", __func__, dsim->state);
+
+ dsim_disable(dsim);
+
+ dsim_info("%s -\n", __func__);
+}
+
+static int dsim_runtime_suspend(struct device *dev)
+{
+ struct dsim_device *dsim = dev_get_drvdata(dev);
+
+ DPU_EVENT_LOG(DPU_EVT_DSIM_SUSPEND, &dsim->sd, ktime_set(0, 0));
+ dsim_dbg("%s +\n", __func__);
+ clk_disable_unprepare(dsim->res.aclk);
+ dsim_dbg("%s -\n", __func__);
+ return 0;
+}
+
+static int dsim_runtime_resume(struct device *dev)
+{
+ struct dsim_device *dsim = dev_get_drvdata(dev);
+
+ DPU_EVENT_LOG(DPU_EVT_DSIM_RESUME, &dsim->sd, ktime_set(0, 0));
+ dsim_dbg("%s: +\n", __func__);
+ clk_prepare_enable(dsim->res.aclk);
+ dsim_dbg("%s -\n", __func__);
+ return 0;
+}
+
+static const struct of_device_id dsim_of_match[] = {
+ { .compatible = "samsung,exynos9-dsim" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, dsim_of_match);
+
+static const struct dev_pm_ops dsim_pm_ops = {
+ .runtime_suspend = dsim_runtime_suspend,
+ .runtime_resume = dsim_runtime_resume,
+};
+
+static struct platform_driver dsim_driver __refdata = {
+ .probe = dsim_probe,
+ .remove = dsim_remove,
+ .shutdown = dsim_shutdown,
+ .driver = {
+ .name = DSIM_MODULE_NAME,
+ .owner = THIS_MODULE,
+ .pm = &dsim_pm_ops,
+ .of_match_table = of_match_ptr(dsim_of_match),
+ .suppress_bind_attrs = true,
+ }
+};
+
+static int __init dsim_init(void)
+{
+ int ret = platform_driver_register(&dsim_driver);
+ if (ret)
+ pr_err("dsim driver register failed\n");
+
+ return ret;
+}
+late_initcall(dsim_init);
+
+static void __exit dsim_exit(void)
+{
+ platform_driver_unregister(&dsim_driver);
+}
+
+module_exit(dsim_exit);
+MODULE_AUTHOR("Yeongran Shin <yr613.shin@samsung.com>");
+MODULE_DESCRIPTION("Samusung EXYNOS DSIM driver");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * DPU Event log 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 <linux/ktime.h>
+#include <linux/debugfs.h>
+#include <media/v4l2-subdev.h>
+#include <video/mipi_display.h>
+
+#include "decon.h"
+#include "dsim.h"
+#include "dpp.h"
+
+/* logging a event related with DECON */
+static inline void dpu_event_log_decon
+ (dpu_event_t type, struct v4l2_subdev *sd, ktime_t time)
+{
+ struct decon_device *decon = container_of(sd, struct decon_device, sd);
+ int idx = atomic_inc_return(&decon->d.event_log_idx) % DPU_EVENT_LOG_MAX;
+ struct dpu_log *log;
+
+ if (IS_ERR_OR_NULL(decon->d.event_log))
+ return;
+
+ log = &decon->d.event_log[idx];
+
+#if defined(CONFIG_SUPPORT_KERNEL_4_9)
+ if (time.tv64)
+#else
+ if (time)
+#endif
+ log->time = time;
+ else
+ log->time = ktime_get();
+ log->type = type;
+
+ switch (type) {
+ case DPU_EVT_DECON_SUSPEND:
+ case DPU_EVT_DECON_RESUME:
+ case DPU_EVT_ENTER_HIBER:
+ case DPU_EVT_EXIT_HIBER:
+ log->data.pm.pm_status = pm_runtime_active(decon->dev);
+ log->data.pm.elapsed = ktime_sub(ktime_get(), log->time);
+ break;
+ case DPU_EVT_WIN_CONFIG:
+ case DPU_EVT_TRIG_UNMASK:
+ case DPU_EVT_TRIG_MASK:
+ case DPU_EVT_FENCE_RELEASE:
+ case DPU_EVT_DECON_FRAMEDONE:
+#if defined(CONFIG_SUPPORT_LEGACY_FENCE)
+ log->data.fence.timeline_value = decon->timeline->value;
+ log->data.fence.timeline_max = decon->timeline_max;
+#else
+ log->data.fence.timeline_value = atomic_read(&decon->fence.timeline);
+ log->data.fence.timeline_max = atomic_read(&decon->fence.timeline);
+#endif
+ break;
+ case DPU_EVT_WB_SW_TRIGGER:
+ break;
+ case DPU_EVT_TE_INTERRUPT:
+ case DPU_EVT_UNDERRUN:
+ case DPU_EVT_LINECNT_ZERO:
+ break;
+ case DPU_EVT_CURSOR_POS: /* cursor async */
+ log->data.cursor.xpos = decon->cursor.xpos;
+ log->data.cursor.ypos = decon->cursor.ypos;
+ log->data.cursor.elapsed = ktime_sub(ktime_get(), log->time);
+ break;
+ default:
+ /* Any remaining types will be log just time and type */
+ break;
+ }
+}
+
+/* logging a event related with DSIM */
+static inline void dpu_event_log_dsim
+ (dpu_event_t type, struct v4l2_subdev *sd, ktime_t time)
+{
+ struct dsim_device *dsim = container_of(sd, struct dsim_device, sd);
+ struct decon_device *decon = get_decon_drvdata(dsim->id);
+ int idx = atomic_inc_return(&decon->d.event_log_idx) % DPU_EVENT_LOG_MAX;
+ struct dpu_log *log;
+
+ if (IS_ERR_OR_NULL(decon->d.event_log))
+ return;
+
+ log = &decon->d.event_log[idx];
+
+#if defined(CONFIG_SUPPORT_KERNEL_4_9)
+ if (time.tv64)
+#else
+ if (time)
+#endif
+ log->time = time;
+ else
+ log->time = ktime_get();
+ log->type = type;
+
+ switch (type) {
+ case DPU_EVT_DSIM_SUSPEND:
+ case DPU_EVT_DSIM_RESUME:
+ case DPU_EVT_ENTER_ULPS:
+ case DPU_EVT_EXIT_ULPS:
+ log->data.pm.pm_status = pm_runtime_active(dsim->dev);
+ log->data.pm.elapsed = ktime_sub(ktime_get(), log->time);
+ break;
+ default:
+ /* Any remaining types will be log just time and type */
+ break;
+ }
+}
+
+/* get decon's id used by dpp */
+static int __get_decon_id_for_dpp(struct v4l2_subdev *sd)
+{
+ struct decon_device *decon;
+ struct dpp_device *dpp = v4l2_get_subdevdata(sd);
+ int idx;
+ int ret = 0;
+
+ for (idx = 0; idx < MAX_DECON_CNT; idx++) {
+ decon = get_decon_drvdata(idx);
+ if (!decon || IS_ERR_OR_NULL(decon->d.debug_event))
+ continue;
+ if (test_bit(dpp->id, &decon->prev_used_dpp))
+ ret = decon->id;
+ }
+
+ return ret;
+}
+
+/* logging a event related with DPP */
+static inline void dpu_event_log_dpp
+ (dpu_event_t type, struct v4l2_subdev *sd, ktime_t time)
+{
+ struct decon_device *decon = get_decon_drvdata(__get_decon_id_for_dpp(sd));
+ int idx = atomic_inc_return(&decon->d.event_log_idx) % DPU_EVENT_LOG_MAX;
+ struct dpp_device *dpp = v4l2_get_subdevdata(sd);
+ struct dpu_log *log;
+
+ if (IS_ERR_OR_NULL(decon->d.event_log))
+ return;
+
+ log = &decon->d.event_log[idx];
+
+#if defined(CONFIG_SUPPORT_KERNEL_4_9)
+ if (time.tv64)
+#else
+ if (time)
+#endif
+ log->time = time;
+ else
+ log->time = ktime_get();
+ log->type = type;
+
+ switch (type) {
+ case DPU_EVT_DPP_SUSPEND:
+ case DPU_EVT_DPP_RESUME:
+ log->data.pm.pm_status = pm_runtime_active(dpp->dev);
+ log->data.pm.elapsed = ktime_sub(ktime_get(), log->time);
+ break;
+ case DPU_EVT_DPP_FRAMEDONE:
+ case DPU_EVT_DPP_STOP:
+ case DPU_EVT_DMA_FRAMEDONE:
+ case DPU_EVT_DMA_RECOVERY:
+ log->data.dpp.id = dpp->id;
+ log->data.dpp.done_cnt = dpp->d.done_count;
+ break;
+ case DPU_EVT_DPP_WINCON:
+ log->data.dpp.id = dpp->id;
+ memcpy(&log->data.dpp.src, &dpp->dpp_config->config.src, sizeof(struct decon_frame));
+ memcpy(&log->data.dpp.dst, &dpp->dpp_config->config.dst, sizeof(struct decon_frame));
+ break;
+ default:
+ log->data.dpp.id = dpp->id;
+ break;
+ }
+
+ return;
+}
+
+/* If event are happend continuously, then ignore */
+static bool dpu_event_ignore
+ (dpu_event_t type, struct decon_device *decon)
+{
+ int latest = atomic_read(&decon->d.event_log_idx) % DPU_EVENT_LOG_MAX;
+ struct dpu_log *log;
+ int idx;
+
+ if (IS_ERR_OR_NULL(decon->d.event_log))
+ return true;
+
+ /* Seek a oldest from current index */
+ idx = (latest + DPU_EVENT_LOG_MAX - DECON_ENTER_HIBER_CNT) % DPU_EVENT_LOG_MAX;
+ do {
+ if (++idx >= DPU_EVENT_LOG_MAX)
+ idx = 0;
+
+ log = &decon->d.event_log[idx];
+ if (log->type != type)
+ return false;
+ } while (latest != idx);
+
+ return true;
+}
+
+/* ===== EXTERN APIs ===== */
+/* Common API to log a event related with DECON/DSIM/DPP */
+void DPU_EVENT_LOG(dpu_event_t type, struct v4l2_subdev *sd, ktime_t time)
+{
+ struct decon_device *decon = get_decon_drvdata(0);
+
+ if (!decon || IS_ERR_OR_NULL(decon->d.debug_event) ||
+ IS_ERR_OR_NULL(decon->d.event_log))
+ return;
+
+ /* log a eventy softly */
+ switch (type) {
+ case DPU_EVT_TE_INTERRUPT:
+ case DPU_EVT_UNDERRUN:
+ /* If occurs continuously, skipped. It is a burden */
+ if (dpu_event_ignore(type, decon))
+ break;
+ case DPU_EVT_BLANK:
+ case DPU_EVT_UNBLANK:
+ case DPU_EVT_ENTER_HIBER:
+ case DPU_EVT_EXIT_HIBER:
+ case DPU_EVT_DECON_SUSPEND:
+ case DPU_EVT_DECON_RESUME:
+ case DPU_EVT_LINECNT_ZERO:
+ case DPU_EVT_TRIG_MASK:
+ case DPU_EVT_TRIG_UNMASK:
+ case DPU_EVT_FENCE_RELEASE:
+ case DPU_EVT_DECON_FRAMEDONE:
+ case DPU_EVT_DECON_FRAMEDONE_WAIT:
+ case DPU_EVT_WIN_CONFIG:
+ case DPU_EVT_WB_SW_TRIGGER:
+ case DPU_EVT_DECON_SHUTDOWN:
+ case DPU_EVT_RSC_CONFLICT:
+ case DPU_EVT_DECON_FRAMESTART:
+ case DPU_EVT_CURSOR_POS: /* cursor async */
+ dpu_event_log_decon(type, sd, time);
+ break;
+ case DPU_EVT_DSIM_FRAMEDONE:
+ case DPU_EVT_ENTER_ULPS:
+ case DPU_EVT_EXIT_ULPS:
+ case DPU_EVT_DSIM_SHUTDOWN:
+ dpu_event_log_dsim(type, sd, time);
+ break;
+ case DPU_EVT_DPP_FRAMEDONE:
+ case DPU_EVT_DPP_STOP:
+ case DPU_EVT_DPP_WINCON:
+ case DPU_EVT_DMA_FRAMEDONE:
+ case DPU_EVT_DMA_RECOVERY:
+ dpu_event_log_dpp(type, sd, time);
+ break;
+ default:
+ break;
+ }
+
+ if (decon->d.event_log_level == DPU_EVENT_LEVEL_LOW)
+ return;
+
+ /* additionally logging hardly */
+ switch (type) {
+ case DPU_EVT_ACT_VSYNC:
+ case DPU_EVT_DEACT_VSYNC:
+ case DPU_EVT_WB_SET_BUFFER:
+ case DPU_EVT_DECON_SET_BUFFER:
+ dpu_event_log_decon(type, sd, time);
+ break;
+ case DPU_EVT_DSIM_SUSPEND:
+ case DPU_EVT_DSIM_RESUME:
+ dpu_event_log_dsim(type, sd, time);
+ break;
+ case DPU_EVT_DPP_SUSPEND:
+ case DPU_EVT_DPP_RESUME:
+ case DPU_EVT_DPP_UPDATE_DONE:
+ case DPU_EVT_DPP_SHADOW_UPDATE:
+ dpu_event_log_dpp(type, sd, time);
+ default:
+ break;
+ }
+}
+
+void DPU_EVENT_LOG_WINCON(struct v4l2_subdev *sd, struct decon_reg_data *regs)
+{
+ struct decon_device *decon = container_of(sd, struct decon_device, sd);
+ struct dpu_log *log;
+ int idx = atomic_inc_return(&decon->d.event_log_idx) % DPU_EVENT_LOG_MAX;
+ int win = 0;
+ bool window_updated = false;
+
+ if (IS_ERR_OR_NULL(decon->d.event_log))
+ return;
+
+ log = &decon->d.event_log[idx];
+
+ log->time = ktime_get();
+ log->type = DPU_EVT_UPDATE_HANDLER;
+
+ for (win = 0; win < MAX_DECON_WIN; win++) {
+ if (regs->win_regs[win].wincon & WIN_EN_F(win)) {
+ memcpy(&log->data.reg.win_regs[win], ®s->win_regs[win],
+ sizeof(struct decon_window_regs));
+ memcpy(&log->data.reg.win_config[win], ®s->dpp_config[win],
+ sizeof(struct decon_win_config));
+ } else {
+ log->data.reg.win_config[win].state =
+ DECON_WIN_STATE_DISABLED;
+ }
+ }
+
+ /* window update case : last window */
+ win = DECON_WIN_UPDATE_IDX;
+ if (regs->dpp_config[win].state == DECON_WIN_STATE_UPDATE) {
+ window_updated = true;
+ memcpy(&log->data.reg.win_config[win], ®s->dpp_config[win],
+ sizeof(struct decon_win_config));
+ }
+
+ /* write-back case : last window */
+ if (decon->dt.out_type == DECON_OUT_WB)
+ memcpy(&log->data.reg.win_config[win], ®s->dpp_config[win],
+ sizeof(struct decon_win_config));
+
+ if (window_updated) {
+ log->data.reg.win.x = regs->dpp_config[win].dst.x;
+ log->data.reg.win.y = regs->dpp_config[win].dst.y;
+ log->data.reg.win.w = regs->dpp_config[win].dst.w;
+ log->data.reg.win.h = regs->dpp_config[win].dst.h;
+ } else {
+ log->data.reg.win.x = 0;
+ log->data.reg.win.y = 0;
+ log->data.reg.win.w = decon->lcd_info->xres;
+ log->data.reg.win.h = decon->lcd_info->yres;
+ }
+}
+
+extern void *return_address(int);
+
+/* Common API to log a event related with DSIM COMMAND */
+void DPU_EVENT_LOG_CMD(struct v4l2_subdev *sd, u32 cmd_id, unsigned long data)
+{
+ struct dsim_device *dsim = container_of(sd, struct dsim_device, sd);
+ struct decon_device *decon = get_decon_drvdata(dsim->id);
+ int idx, i;
+ struct dpu_log *log;
+
+ if (!decon || IS_ERR_OR_NULL(decon->d.debug_event) ||
+ IS_ERR_OR_NULL(decon->d.event_log))
+ return;
+
+ idx = atomic_inc_return(&decon->d.event_log_idx) % DPU_EVENT_LOG_MAX;
+ log = &decon->d.event_log[idx];
+
+ log->time = ktime_get();
+ log->type = DPU_EVT_DSIM_COMMAND;
+ log->data.cmd_buf.id = cmd_id;
+ if (cmd_id == MIPI_DSI_DCS_LONG_WRITE)
+ log->data.cmd_buf.buf = *(u8 *)(data);
+ else
+ log->data.cmd_buf.buf = (u8)data;
+
+ for (i = 0; i < DPU_CALLSTACK_MAX; i++)
+ log->data.cmd_buf.caller[i] = (void *)((size_t)return_address(i + 1));
+}
+
+/* cursor async */
+void DPU_EVENT_LOG_CURSOR(struct v4l2_subdev *sd, struct decon_reg_data *regs)
+{
+ struct decon_device *decon = container_of(sd, struct decon_device, sd);
+ struct dpu_log *log;
+ int idx = atomic_inc_return(&decon->d.event_log_idx) % DPU_EVENT_LOG_MAX;
+ int win = 0;
+
+ if (IS_ERR_OR_NULL(decon->d.event_log))
+ return;
+
+ log = &decon->d.event_log[idx];
+
+ log->time = ktime_get();
+ log->type = DPU_EVT_CURSOR_UPDATE;
+
+ for (win = 0; win < MAX_DECON_WIN; win++) {
+ if (regs->is_cursor_win[win] && regs->win_regs[win].wincon & WIN_EN_F(win)) {
+ memcpy(&log->data.reg.win_regs[win], ®s->win_regs[win],
+ sizeof(struct decon_window_regs));
+ memcpy(&log->data.reg.win_config[win], ®s->dpp_config[win],
+ sizeof(struct decon_win_config));
+ } else {
+ log->data.reg.win_config[win].state =
+ DECON_WIN_STATE_DISABLED;
+ }
+ }
+ win = DECON_WIN_UPDATE_IDX;
+ log->data.reg.win_config[win].state = DECON_WIN_STATE_DISABLED;
+}
+
+void DPU_EVENT_LOG_UPDATE_REGION(struct v4l2_subdev *sd,
+ struct decon_frame *req_region, struct decon_frame *adj_region)
+{
+ struct decon_device *decon = container_of(sd, struct decon_device, sd);
+ int idx = atomic_inc_return(&decon->d.event_log_idx) % DPU_EVENT_LOG_MAX;
+ struct dpu_log *log;
+
+ if (!decon || IS_ERR_OR_NULL(decon->d.debug_event) ||
+ IS_ERR_OR_NULL(decon->d.event_log))
+ return;
+
+ log = &decon->d.event_log[idx];
+ log->time = ktime_get();
+ log->type = DPU_EVT_WINUP_UPDATE_REGION;
+
+ memcpy(&log->data.winup.req_region, req_region, sizeof(struct decon_frame));
+ memcpy(&log->data.winup.adj_region, adj_region, sizeof(struct decon_frame));
+}
+
+void DPU_EVENT_LOG_WINUP_FLAGS(struct v4l2_subdev *sd, bool need_update,
+ bool reconfigure)
+{
+ struct decon_device *decon = container_of(sd, struct decon_device, sd);
+ struct dpu_log *log;
+ int idx = atomic_inc_return(&decon->d.event_log_idx) % DPU_EVENT_LOG_MAX;
+
+ if (!decon || IS_ERR_OR_NULL(decon->d.debug_event) ||
+ IS_ERR_OR_NULL(decon->d.event_log))
+ return;
+
+ log = &decon->d.event_log[idx];
+
+ log->time = ktime_get();
+ log->type = DPU_EVT_WINUP_FLAGS;
+
+ log->data.winup.need_update = need_update;
+ log->data.winup.reconfigure = reconfigure;
+}
+
+void DPU_EVENT_LOG_APPLY_REGION(struct v4l2_subdev *sd,
+ struct decon_rect *apl_rect)
+{
+ struct decon_device *decon = container_of(sd, struct decon_device, sd);
+ int idx = atomic_inc_return(&decon->d.event_log_idx) % DPU_EVENT_LOG_MAX;
+ struct dpu_log *log;
+
+ if (!decon || IS_ERR_OR_NULL(decon->d.debug_event) ||
+ IS_ERR_OR_NULL(decon->d.event_log))
+ return;
+
+ log = &decon->d.event_log[idx];
+
+ log->time = ktime_get();
+ log->type = DPU_EVT_WINUP_APPLY_REGION;
+
+ log->data.winup.apl_region.x = apl_rect->left;
+ log->data.winup.apl_region.y = apl_rect->top;
+ log->data.winup.apl_region.w = apl_rect->right - apl_rect->left + 1;
+ log->data.winup.apl_region.h = apl_rect->bottom - apl_rect->top + 1;
+}
+
+/* display logged events related with DECON */
+void DPU_EVENT_SHOW(struct seq_file *s, struct decon_device *decon)
+{
+ int idx = atomic_read(&decon->d.event_log_idx) % DPU_EVENT_LOG_MAX;
+ struct dpu_log *log;
+ int latest = idx;
+ struct timeval tv;
+ ktime_t prev_ktime;
+ struct dsim_device *dsim;
+
+ if (IS_ERR_OR_NULL(decon->d.event_log))
+ return;
+
+ if (!decon->id)
+ dsim = get_dsim_drvdata(decon->id);
+
+ /* TITLE */
+ seq_printf(s, "-------------------DECON%d EVENT LOGGER ----------------------\n",
+ decon->id);
+ seq_printf(s, "-- STATUS: Hibernation(%s) ",
+ IS_ENABLED(CONFIG_EXYNOS_HIBERNATION) ? "on" : "off");
+ seq_printf(s, "BlockMode(%s) ",
+ IS_ENABLED(CONFIG_EXYNOS_BLOCK_MODE) ? "on" : "off");
+ seq_printf(s, "Window_Update(%s)\n",
+ decon->win_up.enabled ? "on" : "off");
+ if (!decon->id)
+ seq_printf(s, "-- Total underrun count(%d)\n",
+ dsim->total_underrun_cnt);
+ seq_printf(s, "-- Hibernation enter/exit count(%d %d)\n",
+ decon->hiber.enter_cnt, decon->hiber.exit_cnt);
+ seq_puts(s, "-------------------------------------------------------------\n");
+ seq_printf(s, "%14s %20s %20s\n",
+ "Time", "Event ID", "Remarks");
+ seq_puts(s, "-------------------------------------------------------------\n");
+
+ /* Seek a oldest from current index */
+ idx = (idx + DPU_EVENT_LOG_MAX - DPU_EVENT_PRINT_MAX) % DPU_EVENT_LOG_MAX;
+ prev_ktime = ktime_set(0, 0);
+ do {
+ if (++idx >= DPU_EVENT_LOG_MAX)
+ idx = 0;
+
+ /* Seek a index */
+ log = &decon->d.event_log[idx];
+
+ /* TIME */
+ tv = ktime_to_timeval(log->time);
+ seq_printf(s, "[%6ld.%06ld] ", tv.tv_sec, tv.tv_usec);
+
+ /* If there is no timestamp, then exit directly */
+ if (!tv.tv_sec)
+ break;
+
+ /* EVETN ID + Information */
+ switch (log->type) {
+ case DPU_EVT_BLANK:
+ seq_printf(s, "%20s %20s", "FB_BLANK", "-\n");
+ break;
+ case DPU_EVT_UNBLANK:
+ seq_printf(s, "%20s %20s", "FB_UNBLANK", "-\n");
+ break;
+ case DPU_EVT_ACT_VSYNC:
+ seq_printf(s, "%20s %20s", "ACT_VSYNC", "-\n");
+ break;
+ case DPU_EVT_DEACT_VSYNC:
+ seq_printf(s, "%20s %20s", "DEACT_VSYNC", "-\n");
+ break;
+ case DPU_EVT_WIN_CONFIG:
+ seq_printf(s, "%20s %20s", "WIN_CONFIG", "-\n");
+ break;
+ case DPU_EVT_TE_INTERRUPT:
+ prev_ktime = ktime_sub(log->time, prev_ktime);
+ seq_printf(s, "%20s ", "TE_INTERRUPT");
+ seq_printf(s, "time_diff=[%ld.%04lds]\n",
+ ktime_to_timeval(prev_ktime).tv_sec,
+ ktime_to_timeval(prev_ktime).tv_usec/100);
+ /* Update for latest DPU_EVT_TE time */
+ prev_ktime = log->time;
+ break;
+ case DPU_EVT_UNDERRUN:
+ seq_printf(s, "%20s %20s", "UNDER_RUN", "-\n");
+ break;
+ case DPU_EVT_DECON_FRAMEDONE:
+ seq_printf(s, "%20s %20s", "DECON_FRAME_DONE", "-\n");
+ break;
+ case DPU_EVT_DSIM_FRAMEDONE:
+ seq_printf(s, "%20s %20s", "DSIM_FRAME_DONE", "-\n");
+ break;
+ case DPU_EVT_RSC_CONFLICT:
+ seq_printf(s, "%20s %20s", "RSC_CONFLICT", "-\n");
+ break;
+ case DPU_EVT_UPDATE_HANDLER:
+ seq_printf(s, "%20s ", "UPDATE_HANDLER");
+ seq_printf(s, "Partial Size (%d,%d,%d,%d)\n",
+ log->data.reg.win.x,
+ log->data.reg.win.y,
+ log->data.reg.win.w,
+ log->data.reg.win.h);
+ break;
+ case DPU_EVT_DSIM_COMMAND:
+ seq_printf(s, "%20s ", "DSIM_COMMAND");
+ seq_printf(s, "id=0x%x, command=0x%x\n",
+ log->data.cmd_buf.id,
+ log->data.cmd_buf.buf);
+ break;
+ case DPU_EVT_TRIG_MASK:
+ seq_printf(s, "%20s %20s", "TRIG_MASK", "-\n");
+ break;
+ case DPU_EVT_TRIG_UNMASK:
+ seq_printf(s, "%20s %20s", "TRIG_UNMASK", "-\n");
+ break;
+ case DPU_EVT_FENCE_RELEASE:
+ seq_printf(s, "%20s %20s", "FENCE_RELEASE", "-\n");
+ break;
+ case DPU_EVT_DECON_SHUTDOWN:
+ seq_printf(s, "%20s %20s", "DECON_SHUTDOWN", "-\n");
+ break;
+ case DPU_EVT_DSIM_SHUTDOWN:
+ seq_printf(s, "%20s %20s", "DSIM_SHUTDOWN", "-\n");
+ break;
+ case DPU_EVT_DECON_FRAMESTART:
+ seq_printf(s, "%20s %20s", "DECON_FRAMESTART", "-\n");
+ break;
+ case DPU_EVT_DPP_WINCON:
+ seq_printf(s, "%20s ", "DPP_WINCON");
+ seq_printf(s, "ID:%d, start= %d, done= %d\n",
+ log->data.dpp.id,
+ log->data.dpp.start_cnt,
+ log->data.dpp.done_cnt);
+ break;
+ case DPU_EVT_DPP_FRAMEDONE:
+ seq_printf(s, "%20s ", "DPP_FRAMEDONE");
+ seq_printf(s, "ID:%d, start=%d, done=%d\n",
+ log->data.dpp.id,
+ log->data.dpp.start_cnt,
+ log->data.dpp.done_cnt);
+ break;
+ case DPU_EVT_DPP_STOP:
+ seq_printf(s, "%20s ", "DPP_STOP");
+ seq_printf(s, "(id:%d)\n", log->data.dpp.id);
+ break;
+ case DPU_EVT_DPP_SUSPEND:
+ seq_printf(s, "%20s %20s", "DPP_SUSPEND", "-\n");
+ break;
+ case DPU_EVT_DPP_RESUME:
+ seq_printf(s, "%20s %20s", "DPP_RESUME", "-\n");
+ break;
+ case DPU_EVT_DECON_SUSPEND:
+ seq_printf(s, "%20s %20s", "DECON_SUSPEND", "-\n");
+ break;
+ case DPU_EVT_DECON_RESUME:
+ seq_printf(s, "%20s %20s", "DECON_RESUME", "-\n");
+ break;
+ case DPU_EVT_ENTER_HIBER:
+ seq_printf(s, "%20s ", "ENTER_HIBER");
+ tv = ktime_to_timeval(log->data.pm.elapsed);
+ seq_printf(s, "pm=%s, elapsed=[%ld.%03lds]\n",
+ log->data.pm.pm_status ? "active " : "suspend",
+ tv.tv_sec, tv.tv_usec/1000);
+ break;
+ case DPU_EVT_EXIT_HIBER:
+ seq_printf(s, "%20s ", "EXIT_HIBER");
+ tv = ktime_to_timeval(log->data.pm.elapsed);
+ seq_printf(s, "pm=%s, elapsed=[%ld.%03lds]\n",
+ log->data.pm.pm_status ? "active " : "suspend",
+ tv.tv_sec, tv.tv_usec/1000);
+ break;
+ case DPU_EVT_DSIM_SUSPEND:
+ seq_printf(s, "%20s %20s", "DSIM_SUSPEND", "-\n");
+ break;
+ case DPU_EVT_DSIM_RESUME:
+ seq_printf(s, "%20s %20s", "DSIM_RESUME", "-\n");
+ break;
+ case DPU_EVT_ENTER_ULPS:
+ seq_printf(s, "%20s ", "ENTER_ULPS");
+ tv = ktime_to_timeval(log->data.pm.elapsed);
+ seq_printf(s, "pm=%s, elapsed=[%ld.%03lds]\n",
+ log->data.pm.pm_status ? "active " : "suspend",
+ tv.tv_sec, tv.tv_usec/1000);
+ break;
+ case DPU_EVT_EXIT_ULPS:
+ seq_printf(s, "%20s ", "EXIT_ULPS");
+ tv = ktime_to_timeval(log->data.pm.elapsed);
+ seq_printf(s, "pm=%s, elapsed=[%ld.%03lds]\n",
+ log->data.pm.pm_status ? "active " : "suspend",
+ tv.tv_sec, tv.tv_usec/1000);
+ break;
+ case DPU_EVT_DMA_FRAMEDONE:
+ seq_printf(s, "%20s ", "DPP_FRAMEDONE");
+ seq_printf(s, "ID:%d\n", log->data.dpp.id);
+ break;
+ case DPU_EVT_DMA_RECOVERY:
+ seq_printf(s, "%20s %20s", "DMA_FRAMEDONE", "-\n");
+ break;
+ default:
+ seq_printf(s, "%20s (%2d)\n", "NO_DEFINED", log->type);
+ break;
+ }
+ } while (latest != idx);
+
+ seq_puts(s, "-------------------------------------------------------------\n");
+
+ return;
+}
+
+static int decon_debug_event_show(struct seq_file *s, void *unused)
+{
+ struct decon_device *decon = s->private;
+ DPU_EVENT_SHOW(s, decon);
+ return 0;
+}
+
+static int decon_debug_event_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, decon_debug_event_show, inode->i_private);
+}
+
+static const struct file_operations decon_event_fops = {
+ .open = decon_debug_event_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static int decon_debug_dump_show(struct seq_file *s, void *unused)
+{
+ struct decon_device *decon = s->private;
+
+ if (!IS_DECON_ON_STATE(decon)) {
+ decon_info("%s: decon is not ON(%d)\n", __func__, decon->state);
+ return 0;
+ }
+ decon_dump(decon);
+ return 0;
+}
+
+static int decon_debug_dump_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, decon_debug_dump_show, inode->i_private);
+}
+
+static const struct file_operations decon_dump_fops = {
+ .open = decon_debug_dump_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static int decon_debug_bts_show(struct seq_file *s, void *unused)
+{
+ seq_printf(s, "%u\n", dpu_bts_log_level);
+
+ return 0;
+}
+
+static int decon_debug_bts_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, decon_debug_bts_show, inode->i_private);
+}
+
+static ssize_t decon_debug_bts_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *f_ops)
+{
+ char *buf_data;
+ int ret;
+
+ buf_data = kmalloc(count, GFP_KERNEL);
+ if (buf_data == NULL)
+ return count;
+
+ ret = copy_from_user(buf_data, buf, count);
+ if (ret < 0)
+ goto out;
+
+ ret = sscanf(buf_data, "%u", &dpu_bts_log_level);
+ if (ret < 0)
+ goto out;
+
+out:
+ kfree(buf_data);
+ return count;
+}
+
+static const struct file_operations decon_bts_fops = {
+ .open = decon_debug_bts_open,
+ .write = decon_debug_bts_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static int decon_debug_win_show(struct seq_file *s, void *unused)
+{
+ seq_printf(s, "%u\n", win_update_log_level);
+
+ return 0;
+}
+
+static int decon_debug_win_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, decon_debug_win_show, inode->i_private);
+}
+
+static ssize_t decon_debug_win_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *f_ops)
+{
+ char *buf_data;
+ int ret;
+
+ buf_data = kmalloc(count, GFP_KERNEL);
+ if (buf_data == NULL)
+ return count;
+
+ ret = copy_from_user(buf_data, buf, count);
+ if (ret < 0)
+ goto out;
+
+ ret = sscanf(buf_data, "%u", &win_update_log_level);
+ if (ret < 0)
+ goto out;
+
+out:
+ kfree(buf_data);
+ return count;
+}
+
+static const struct file_operations decon_win_fops = {
+ .open = decon_debug_win_open,
+ .write = decon_debug_win_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static int decon_debug_mres_show(struct seq_file *s, void *unused)
+{
+ seq_printf(s, "%u\n", dpu_mres_log_level);
+
+ return 0;
+}
+
+static int decon_debug_mres_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, decon_debug_mres_show, inode->i_private);
+}
+
+static ssize_t decon_debug_mres_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *f_ops)
+{
+ char *buf_data;
+ int ret;
+
+ buf_data = kmalloc(count, GFP_KERNEL);
+ if (buf_data == NULL)
+ return count;
+
+ ret = copy_from_user(buf_data, buf, count);
+ if (ret < 0)
+ goto out;
+
+ ret = sscanf(buf_data, "%u", &dpu_mres_log_level);
+ if (ret < 0)
+ goto out;
+
+out:
+ kfree(buf_data);
+ return count;
+}
+
+static const struct file_operations decon_mres_fops = {
+ .open = decon_debug_mres_open,
+ .write = decon_debug_mres_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static int decon_systrace_show(struct seq_file *s, void *unused)
+{
+ seq_printf(s, "%u\n", decon_systrace_enable);
+ return 0;
+}
+
+static int decon_systrace_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, decon_systrace_show, inode->i_private);
+}
+
+static ssize_t decon_systrace_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *f_ops)
+{
+ char *buf_data;
+ int ret;
+
+ buf_data = kmalloc(count, GFP_KERNEL);
+ if (buf_data == NULL)
+ return count;
+
+ ret = copy_from_user(buf_data, buf, count);
+ if (ret < 0)
+ goto out;
+
+ ret = sscanf(buf_data, "%u", &decon_systrace_enable);
+ if (ret < 0)
+ goto out;
+
+out:
+ kfree(buf_data);
+ return count;
+}
+
+static const struct file_operations decon_systrace_fops = {
+ .open = decon_systrace_open,
+ .write = decon_systrace_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+#if defined(CONFIG_DSIM_CMD_TEST)
+static int decon_debug_cmd_show(struct seq_file *s, void *unused)
+{
+ return 0;
+}
+
+static int decon_debug_cmd_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, decon_debug_cmd_show, inode->i_private);
+}
+
+static ssize_t decon_debug_cmd_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *f_ops)
+{
+ char *buf_data;
+ int ret;
+ unsigned int cmd;
+ struct dsim_device *dsim;
+ u32 id, d1;
+ unsigned long d0;
+
+ buf_data = kmalloc(count, GFP_KERNEL);
+ if (buf_data == NULL)
+ return count;
+
+ ret = copy_from_user(buf_data, buf, count);
+ if (ret < 0)
+ goto out;
+
+ ret = sscanf(buf_data, "%u", &cmd);
+ if (ret < 0)
+ goto out;
+
+ dsim = get_dsim_drvdata(0);
+
+ switch (cmd) {
+ case 1:
+ id = MIPI_DSI_DCS_SHORT_WRITE;
+ d0 = (unsigned long)SEQ_DISPLAY_ON[0];
+ d1 = 0;
+ break;
+ case 2:
+ id = MIPI_DSI_DCS_SHORT_WRITE;
+ d0 = (unsigned long)SEQ_DISPLAY_OFF[0];
+ d1 = 0;
+ break;
+ case 3:
+ id = MIPI_DSI_DCS_SHORT_WRITE;
+ d0 = (unsigned long)SEQ_ALLPOFF[0];
+ d1 = 0;
+ break;
+ case 4:
+ id = MIPI_DSI_DCS_SHORT_WRITE;
+ d0 = (unsigned long)SEQ_ALLPON[0];
+ d1 = 0;
+ break;
+ case 5:
+ id = MIPI_DSI_DCS_LONG_WRITE;
+ d0 = (unsigned long)SEQ_ESD_FG;
+ d1 = ARRAY_SIZE(SEQ_ESD_FG);
+ break;
+ case 6:
+ id = MIPI_DSI_DCS_LONG_WRITE;
+ d0 = (unsigned long)SEQ_TEST_KEY_OFF_F0;
+ d1 = ARRAY_SIZE(SEQ_TEST_KEY_OFF_F0);
+ break;
+ case 7:
+ id = MIPI_DSI_DCS_LONG_WRITE;
+ d0 = (unsigned long)SEQ_TEST_KEY_OFF_F1;
+ d1 = ARRAY_SIZE(SEQ_TEST_KEY_OFF_F1);
+ break;
+ default:
+ dsim_info("unsupported command(%d)\n", cmd);
+ goto out;
+ }
+
+ ret = dsim_write_data(dsim, id, d0, d1);
+ if (ret < 0) {
+ decon_err("failed to write DSIM command(0x%lx)\n",
+ (id == MIPI_DSI_DCS_LONG_WRITE) ?
+ *(u8 *)(d0) : d0);
+ goto out;
+ }
+
+ decon_info("success to write DSIM command(0x%lx, %d)\n",
+ (id == MIPI_DSI_DCS_LONG_WRITE) ?
+ *(u8 *)(d0) : d0, d1);
+out:
+ kfree(buf_data);
+ return count;
+}
+
+static const struct file_operations decon_cmd_fops = {
+ .open = decon_debug_cmd_open,
+ .write = decon_debug_cmd_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+#endif
+
+static int decon_debug_cmd_lp_ref_show(struct seq_file *s, void *unused)
+{
+ struct dsim_device *dsim = get_dsim_drvdata(0);
+ int i;
+
+ /* DSU_MODE_1 is used in stead of 1 in MCD */
+ seq_printf(s, "%u\n", dsim->lcd_info.mres_mode);
+
+ for (i = 0; i < dsim->lcd_info.dt_lcd_mres.mres_number; i++)
+ seq_printf(s, "%u\n", dsim->lcd_info.cmd_underrun_lp_ref[i]);
+
+ return 0;
+}
+
+static int decon_debug_cmd_lp_ref_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, decon_debug_cmd_lp_ref_show, inode->i_private);
+}
+
+static ssize_t decon_debug_cmd_lp_ref_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *f_ops)
+{
+ char *buf_data;
+ int ret;
+ unsigned int cmd_lp_ref;
+ struct dsim_device *dsim;
+ int idx;
+
+ buf_data = kmalloc(count, GFP_KERNEL);
+ if (buf_data == NULL)
+ return count;
+
+ ret = copy_from_user(buf_data, buf, count);
+ if (ret < 0)
+ goto out;
+
+ ret = sscanf(buf_data, "%u", &cmd_lp_ref);
+ if (ret < 0)
+ goto out;
+
+ dsim = get_dsim_drvdata(0);
+
+ idx = dsim->lcd_info.mres_mode;
+ dsim->lcd_info.cmd_underrun_lp_ref[idx] = cmd_lp_ref;
+
+out:
+ kfree(buf_data);
+ return count;
+}
+
+static const struct file_operations decon_cmd_lp_ref_fops = {
+ .open = decon_debug_cmd_lp_ref_open,
+ .write = decon_debug_cmd_lp_ref_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static int decon_debug_rec_show(struct seq_file *s, void *unused)
+{
+#if 0 /* TODO: This will be implemented */
+ seq_printf(s, "VGF0[%u] VGF1[%u]\n",
+ get_dpp_drvdata(DPU_DMA2CH(IDMA_VGF0))->d.recovery_cnt,
+ get_dpp_drvdata(DPU_DMA2CH(IDMA_VGF1))->d.recovery_cnt);
+#endif
+ return 0;
+}
+
+static int decon_debug_rec_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, decon_debug_rec_show, inode->i_private);
+}
+
+static ssize_t decon_debug_rec_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *f_ops)
+{
+ return count;
+}
+
+static const struct file_operations decon_rec_fops = {
+ .open = decon_debug_rec_open,
+ .write = decon_debug_rec_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static int decon_debug_low_persistence_show(struct seq_file *s, void *unused)
+{
+ struct decon_device *decon = get_decon_drvdata(0);
+ seq_printf(s, "%u\n", decon->low_persistence);
+
+ return 0;
+}
+
+static int decon_debug_low_persistence_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, decon_debug_low_persistence_show, inode->i_private);
+}
+
+static ssize_t decon_debug_low_persistence_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *f_ops)
+{
+ struct decon_device *decon;
+ char *buf_data;
+ int ret;
+ unsigned int low_persistence;
+
+ buf_data = kmalloc(count, GFP_KERNEL);
+ if (buf_data == NULL)
+ return count;
+
+ ret = copy_from_user(buf_data, buf, count);
+ if (ret < 0)
+ goto out;
+
+ ret = sscanf(buf_data, "%u", &low_persistence);
+ if (ret < 0)
+ goto out;
+
+ decon = get_decon_drvdata(0);
+ decon->low_persistence = low_persistence;
+
+out:
+ kfree(buf_data);
+ return count;
+}
+
+static const struct file_operations decon_low_persistence_fops = {
+ .open = decon_debug_low_persistence_open,
+ .write = decon_debug_low_persistence_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+int decon_create_debugfs(struct decon_device *decon)
+{
+ char name[MAX_NAME_SIZE];
+ int ret = 0;
+ int i;
+ u32 event_cnt;
+
+ decon->d.event_log = NULL;
+ event_cnt = DPU_EVENT_LOG_MAX;
+
+ for (i = 0; i < DPU_EVENT_LOG_RETRY; ++i) {
+ event_cnt = event_cnt >> i;
+ decon->d.event_log = kzalloc(sizeof(struct dpu_log) * event_cnt,
+ GFP_KERNEL);
+ if (IS_ERR_OR_NULL(decon->d.event_log)) {
+ decon_warn("failed to alloc event log buf[%d]. retry\n",
+ event_cnt);
+ continue;
+ }
+
+ decon_info("#%d event log buffers are allocated\n", event_cnt);
+ break;
+ }
+ decon->d.event_log_cnt = event_cnt;
+
+ if (!decon->id) {
+ decon->d.debug_root = debugfs_create_dir("decon", NULL);
+ if (!decon->d.debug_root) {
+ decon_err("failed to create debugfs root directory.\n");
+ ret = -ENOENT;
+ goto err_event_log;
+ }
+ }
+
+ if (decon->id == 1 || decon->id == 2)
+ decon->d.debug_root = decon_drvdata[0]->d.debug_root;
+
+ snprintf(name, MAX_NAME_SIZE, "event%d", decon->id);
+ atomic_set(&decon->d.event_log_idx, -1);
+ decon->d.debug_event = debugfs_create_file(name, 0444,
+ decon->d.debug_root, decon, &decon_event_fops);
+ if (!decon->d.debug_event) {
+ decon_err("failed to create debugfs file(%d)\n", decon->id);
+ ret = -ENOENT;
+ goto err_debugfs;
+ }
+
+ snprintf(name, MAX_NAME_SIZE, "dump%d", decon->id);
+ decon->d.debug_dump = debugfs_create_file(name, 0444,
+ decon->d.debug_root, decon, &decon_dump_fops);
+ if (!decon->d.debug_dump) {
+ decon_err("failed to create SFR dump debugfs file(%d)\n",
+ decon->id);
+ ret = -ENOENT;
+ goto err_debugfs;
+ }
+
+ if (decon->id == 0) {
+ decon->d.debug_bts = debugfs_create_file("bts_log", 0444,
+ decon->d.debug_root, NULL, &decon_bts_fops);
+ if (!decon->d.debug_bts) {
+ decon_err("failed to create BTS log level file\n");
+ ret = -ENOENT;
+ goto err_debugfs;
+ }
+ decon->d.debug_win = debugfs_create_file("win_update_log", 0444,
+ decon->d.debug_root, NULL, &decon_win_fops);
+ if (!decon->d.debug_win) {
+ decon_err("failed to create win update log level file\n");
+ ret = -ENOENT;
+ goto err_debugfs;
+ }
+ decon->d.debug_mres = debugfs_create_file("mres_log", 0444,
+ decon->d.debug_root, NULL, &decon_mres_fops);
+ if (!decon->d.debug_mres) {
+ decon_err("failed to create mres log level file\n");
+ ret = -ENOENT;
+ goto err_debugfs;
+ }
+ decon->d.debug_systrace = debugfs_create_file("decon_systrace", 0444,
+ decon->d.debug_root, NULL, &decon_systrace_fops);
+ if (!decon->d.debug_systrace) {
+ decon_err("failed to create decon_systrace file\n");
+ ret = -ENOENT;
+ goto err_debugfs;
+ }
+#if defined(CONFIG_DSIM_CMD_TEST)
+ decon->d.debug_cmd = debugfs_create_file("cmd", 0444,
+ decon->d.debug_root, NULL, &decon_cmd_fops);
+ if (!decon->d.debug_cmd) {
+ decon_err("failed to create cmd_rw file\n");
+ ret = -ENOENT;
+ goto err_debugfs;
+ }
+#endif
+ decon->d.debug_recovery_cnt = debugfs_create_file("recovery_cnt",
+ 0444, decon->d.debug_root, NULL, &decon_rec_fops);
+ if (!decon->d.debug_recovery_cnt) {
+ decon_err("failed to create recovery_cnt file\n");
+ ret = -ENOENT;
+ goto err_debugfs;
+ }
+ decon->d.debug_cmd_lp_ref = debugfs_create_file("cmd_lp_ref",
+ 0444, decon->d.debug_root, NULL, &decon_cmd_lp_ref_fops);
+ if (!decon->d.debug_cmd_lp_ref) {
+ decon_err("failed to create cmd_lp_ref file\n");
+ ret = -ENOENT;
+ goto err_debugfs;
+ }
+ decon->d.debug_low_persistence = debugfs_create_file("low_persistence",
+ 0444, decon->d.debug_root, NULL, &decon_low_persistence_fops);
+ if (!decon->d.debug_low_persistence) {
+ decon_err("failed to create low persistence file\n");
+ ret = -ENOENT;
+ goto err_debugfs;
+ }
+ }
+
+ return 0;
+
+err_debugfs:
+ debugfs_remove_recursive(decon->d.debug_root);
+err_event_log:
+ kfree(decon->d.event_log);
+ decon->d.event_log = NULL;
+ return ret;
+}
+
+void decon_destroy_debugfs(struct decon_device *decon)
+{
+ if (decon->d.debug_root)
+ debugfs_remove(decon->d.debug_root);
+ if (decon->d.debug_event)
+ debugfs_remove(decon->d.debug_event);
+}
--- /dev/null
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * DPU fence 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.
+ */
+
+#if !defined(CONFIG_SUPPORT_LEGACY_FENCE)
+#include <linux/dma-fence.h>
+#endif
+#include <linux/sync_file.h>
+
+#include "decon.h"
+
+#if defined(CONFIG_SUPPORT_LEGACY_FENCE)
+/* sync fence related functions */
+void decon_create_timeline(struct decon_device *decon, char *name)
+{
+ decon->timeline = sync_timeline_create(name);
+ decon->timeline_max = 0;
+}
+
+int decon_get_valid_fd(void)
+{
+ int fd = 0;
+ int fd_idx = 0;
+ int unused_fd[FD_TRY_CNT] = {0};
+
+ fd = get_unused_fd_flags(O_CLOEXEC);
+ if (fd < 0)
+ return -EINVAL;
+
+ if (fd < VALID_FD_VAL) {
+ /*
+ * If fd from get_unused_fd() has value between 0 and 2,
+ * fd is tried to get value again except current fd vlaue.
+ */
+ while (fd < VALID_FD_VAL) {
+ decon_warn("%s, unvalid fd[%d] is assigned to DECON\n",
+ __func__, fd);
+ unused_fd[fd_idx++] = fd;
+ fd = get_unused_fd_flags(O_CLOEXEC);
+ if (fd < 0) {
+ decon_err("%s, unvalid fd[%d]\n", __func__,
+ fd);
+ break;
+ }
+ }
+
+ while (fd_idx-- > 0) {
+ decon_warn("%s, unvalid fd[%d] is released by DECON\n",
+ __func__, unused_fd[fd_idx]);
+ put_unused_fd(unused_fd[fd_idx]);
+ }
+
+ if (fd < 0)
+ return -EINVAL;
+ }
+ return fd;
+}
+
+void decon_create_release_fences(struct decon_device *decon,
+ struct decon_win_config_data *win_data,
+ struct sync_file *sync_file)
+{
+ int i = 0;
+
+ for (i = 0; i < MAX_DECON_WIN; i++) {
+ int state = win_data->config[i].state;
+ int rel_fence = -1;
+
+ if (state == DECON_WIN_STATE_BUFFER) {
+ rel_fence = decon_get_valid_fd();
+ if (rel_fence < 0) {
+ decon_err("%s: failed to get unused fd\n",
+ __func__);
+ goto err;
+ }
+
+ fd_install(rel_fence,
+ get_file(sync_file->file));
+ }
+ win_data->config[i].rel_fence = rel_fence;
+ }
+ return;
+err:
+ while (i-- > 0) {
+ if (win_data->config[i].state == DECON_WIN_STATE_BUFFER) {
+ put_unused_fd(win_data->config[i].rel_fence);
+ win_data->config[i].rel_fence = -1;
+ }
+ }
+ return;
+}
+
+int decon_create_fence(struct decon_device *decon, struct sync_file **sync_file)
+{
+ struct sync_pt *pt;
+ int fd = -EMFILE;
+
+ decon->timeline_max++;
+ pt = sync_pt_create(decon->timeline, sizeof(*pt), decon->timeline_max);
+ if (!pt) {
+ decon_err("%s: failed to create sync pt\n", __func__);
+ goto err;
+ }
+
+ *sync_file = sync_file_create(&pt->base);
+ fence_put(&pt->base);
+ if (!(*sync_file)) {
+ decon_err("%s: failed to create sync file\n", __func__);
+ goto err;
+ }
+
+ fd = decon_get_valid_fd();
+ if (fd < 0) {
+ decon_err("%s: failed to get unused fd\n", __func__);
+ fput((*sync_file)->file);
+ goto err;
+ }
+
+ return fd;
+
+err:
+ decon->timeline_max--;
+ return fd;
+}
+
+void decon_wait_fence(struct sync_file *sync_file)
+{
+ int err = sync_file_wait(sync_file, 900);
+ if (err >= 0)
+ return;
+
+ if (err < 0)
+ decon_warn("error waiting on acquire fence: %d\n", err);
+}
+
+void decon_signal_fence(struct decon_device *decon)
+{
+ sync_timeline_signal(decon->timeline, 1);
+}
+#else /* dma fence in kernel version 4.14 */
+/* sync fence related functions */
+void decon_create_timeline(struct decon_device *decon, char *name)
+{
+ decon->fence.context = dma_fence_context_alloc(1);
+ spin_lock_init(&decon->fence.lock);
+ strlcpy(decon->fence.name, name, sizeof(decon->fence.name));
+}
+
+static int decon_get_valid_fd(void)
+{
+ int fd = 0;
+ int fd_idx = 0;
+ int unused_fd[FD_TRY_CNT] = {0};
+
+ fd = get_unused_fd_flags(O_CLOEXEC);
+ if (fd < 0)
+ return -EINVAL;
+
+ if (fd < VALID_FD_VAL) {
+ /*
+ * If fd from get_unused_fd() has value between 0 and 2,
+ * fd is tried to get value again except current fd vlaue.
+ */
+ while (fd < VALID_FD_VAL) {
+ decon_warn("%s, unvalid fd[%d] is assigned to DECON\n",
+ __func__, fd);
+ unused_fd[fd_idx++] = fd;
+ fd = get_unused_fd_flags(O_CLOEXEC);
+ if (fd < 0) {
+ decon_err("%s, unvalid fd[%d]\n", __func__,
+ fd);
+ break;
+ }
+ }
+
+ while (fd_idx-- > 0) {
+ decon_warn("%s, unvalid fd[%d] is released by DECON\n",
+ __func__, unused_fd[fd_idx]);
+ put_unused_fd(unused_fd[fd_idx]);
+ }
+
+ if (fd < 0)
+ return -EINVAL;
+ }
+ return fd;
+}
+
+void decon_create_release_fences(struct decon_device *decon,
+ struct decon_win_config_data *win_data,
+ struct sync_file *sync_file)
+{
+ int i = 0;
+
+ for (i = 0; i < MAX_DECON_WIN; i++) {
+ int state = win_data->config[i].state;
+ int rel_fence = -1;
+
+ if (state == DECON_WIN_STATE_BUFFER) {
+ rel_fence = decon_get_valid_fd();
+ if (rel_fence < 0) {
+ decon_err("%s: failed to get unused fd\n",
+ __func__);
+ goto err;
+ }
+
+ fd_install(rel_fence,
+ get_file(sync_file->file));
+ }
+ win_data->config[i].rel_fence = rel_fence;
+ }
+ return;
+err:
+ while (i-- > 0) {
+ if (win_data->config[i].state == DECON_WIN_STATE_BUFFER) {
+ put_unused_fd(win_data->config[i].rel_fence);
+ win_data->config[i].rel_fence = -1;
+ }
+ }
+ return;
+}
+
+static const char *decon_fence_get_driver_name(struct dma_fence *fence)
+{
+ struct decon_fence *decon_fence;
+
+ decon_fence = container_of(fence->lock, struct decon_fence, lock);
+ return decon_fence->name;
+}
+
+static bool decon_fence_enable_signaling(struct dma_fence *fence)
+{
+ /* nothing to do */
+ return true;
+}
+
+static void decon_fence_value_str(struct dma_fence *fence, char *str, int size)
+{
+ snprintf(str, size, "%d", fence->seqno);
+}
+
+static struct dma_fence_ops decon_fence_ops = {
+ .get_driver_name = decon_fence_get_driver_name,
+ .get_timeline_name = decon_fence_get_driver_name,
+ .enable_signaling = decon_fence_enable_signaling,
+ .wait = dma_fence_default_wait,
+ .fence_value_str = decon_fence_value_str,
+};
+
+int decon_create_fence(struct decon_device *decon, struct sync_file **sync_file)
+{
+ struct dma_fence *fence;
+ int fd = -EMFILE;
+
+ fence = kzalloc(sizeof(*fence), GFP_KERNEL);
+ if (!fence)
+ return -ENOMEM;
+
+ dma_fence_init(fence, &decon_fence_ops, &decon->fence.lock,
+ decon->fence.context,
+ atomic_inc_return(&decon->fence.timeline));
+
+ *sync_file = sync_file_create(fence);
+ dma_fence_put(fence);
+ if (!(*sync_file)) {
+ decon_err("%s: failed to create sync file\n", __func__);
+ return -ENOMEM;
+ }
+
+ fd = decon_get_valid_fd();
+ if (fd < 0) {
+ decon_err("%s: failed to get unused fd\n", __func__);
+ fput((*sync_file)->file);
+ }
+
+ return fd;
+}
+
+void decon_wait_fence(struct dma_fence *fence)
+{
+ int err = dma_fence_wait_timeout(fence, false, 900);
+ if (err >= 0)
+ return;
+
+ if (err < 0)
+ decon_warn("error waiting on acquire fence: %d\n", err);
+}
+
+void decon_signal_fence(struct dma_fence *fence)
+{
+ if (dma_fence_signal(fence))
+ decon_warn("%s: fence[%p] #%d signal failed\n", __func__,
+ fence, fence->seqno);
+}
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * DPP HDR LUT(Look Up Table)
+ *
+ * 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 _HDR_LUT_
+#define _HDR_LUT_
+
+#include <linux/types.h>
+
+#define MAX_EOTF (65)
+#define MAX_GM (9)
+#define MAX_TM (33)
+
+/* EOTF section */
+// smpte 2084 1000nit
+u32 eotf_x_axis_st2084_1000[MAX_EOTF] = {
+ 0, 64, 128, 160, 192, 224, 256, 288, 304, 320,
+ 336, 352, 368, 384, 400, 416, 432, 448, 464, 480,
+ 496, 512, 528, 536, 544, 552, 560, 568, 576, 584,
+ 592, 600, 608, 616, 624, 632, 640, 648, 656, 664,
+ 672, 680, 688, 696, 704, 712, 720, 728, 736, 744,
+ 748, 752, 756, 760, 764, 768, 769, 770, 772, 776,
+ 784, 800, 832, 896, 128,
+};
+
+u32 eotf_y_axis_st2084_1000[MAX_EOTF] = {
+ 0, 2, 10, 19, 33, 54, 85, 130, 159, 194,
+ 235, 283, 339, 406, 483, 574, 679, 802, 944, 1110,
+ 1301, 1523, 1780, 1923, 2076, 2241, 2419, 2610, 2814, 3034,
+ 3271, 3524, 3797, 4089, 4403, 4740, 5102, 5490, 5907, 6354,
+ 6834, 7350, 7903, 8496, 9134, 9817, 10551, 11339, 12185, 13093,
+ 13571, 14067, 14581, 15114, 15665, 16237, 16383, 16383, 16383, 16383,
+ 16383, 16383, 16383, 16383, 0,
+};
+
+// smpte 2084 4000nit
+u32 eotf_x_axis_st2084_4000[MAX_EOTF] = {
+ 0, 32, 48, 56, 64, 72, 80, 84, 88, 92,
+ 94, 96, 100, 102, 104, 108, 112, 120, 128, 130,
+ 132, 134, 136, 138, 140, 142, 144, 146, 148, 152,
+ 156, 158, 160, 168, 176, 192, 224, 240, 256, 272,
+ 288, 304, 320, 352, 384, 416, 448, 480, 512, 544,
+ 576, 608, 640, 768, 832, 896, 912, 920, 922, 923,
+ 924, 928, 960, 992, 32,
+};
+
+u32 eotf_y_axis_st2084_4000[MAX_EOTF] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4,
+ 4, 5, 5, 5, 6, 6, 7, 8, 10, 10,
+ 11, 11, 12, 12, 13, 13, 14, 14, 15, 16,
+ 17, 18, 19, 21, 25, 32, 54, 68, 85, 105,
+ 130, 159, 193, 282, 404, 572, 799, 1106, 1519, 2071,
+ 2807, 3773, 4912, 10549, 13333, 15605, 16075, 16294, 16348, 16374,
+ 16383, 16383, 16383, 16383, 0,
+};
+
+// Rec709/601
+u32 eotf_x_axis_709_601[MAX_EOTF] = {
+ 0, 64, 96, 112, 128, 144, 160, 176, 192, 208,
+ 224, 240, 256, 272, 288, 304, 320, 336, 352, 368,
+ 384, 400, 416, 432, 448, 464, 480, 496, 512, 528,
+ 544, 560, 576, 592, 608, 624, 640, 656, 672, 688,
+ 704, 720, 736, 752, 768, 784, 800, 816, 832, 848,
+ 864, 880, 896, 912, 928, 944, 960, 976, 992, 1008,
+ 1016, 1020, 1022, 1023, 1,
+};
+
+u32 eotf_y_axis_709_601[MAX_EOTF] = {
+ 0, 227, 342, 407, 477, 555, 638, 728, 825, 928,
+ 1038, 1155, 1278, 1409, 1547, 1691, 1843, 2003, 2169, 2343,
+ 2524, 2712, 2908, 3112, 3323, 3542, 3769, 4003, 4245, 4495,
+ 4753, 5019, 5293, 5574, 5864, 6162, 6468, 6782, 7105, 7436,
+ 7775, 8122, 8478, 8842, 9215, 9596, 9985, 10383, 10790, 11205,
+ 11629, 12062, 12503, 12953, 13412, 13880, 14356, 14841, 15336, 15839,
+ 16094, 16222, 16286, 16318, 65,
+};
+
+// sRGB
+u32 eotf_x_axis_srgb[MAX_EOTF] = {
+ 0, 32, 64, 80, 96, 112, 128, 144, 160, 176,
+ 192, 208, 224, 240, 256, 272, 288, 304, 320, 336,
+ 352, 368, 384, 400, 416, 432, 448, 464, 480, 496,
+ 512, 528, 544, 560, 576, 592, 608, 624, 640, 656,
+ 672, 688, 704, 720, 736, 752, 768, 784, 800, 816,
+ 832, 848, 864, 880, 896, 912, 928, 944, 960, 976,
+ 992, 1008, 1016, 1020, 4,
+};
+
+u32 eotf_y_axis_srgb[MAX_EOTF] = {
+ 0, 40, 84, 114, 149, 189, 235, 287, 345, 409,
+ 480, 557, 642, 733, 832, 938, 1051, 1172, 1301, 1438,
+ 1583, 1736, 1897, 2066, 2245, 2431, 2627, 2831, 3045, 3267,
+ 3499, 3740, 3991, 4251, 4521, 4800, 5089, 5388, 5697, 6017,
+ 6346, 6686, 7036, 7396, 7768, 8149, 8542, 8945, 9359, 9784,
+ 10221, 10668, 11127, 11597, 12078, 12571, 13075, 13591, 14118, 14658,
+ 15209, 15772, 16058, 16202, 181,
+};
+
+// HLG
+u32 eotf_x_axis_hlg[MAX_EOTF] = {
+ 0, 32, 64, 96, 128, 160, 192, 224, 256, 288,
+ 320, 352, 384, 416, 448, 480, 512, 528, 544, 560,
+ 576, 592, 608, 624, 640, 656, 672, 688, 704, 720,
+ 736, 752, 768, 776, 784, 792, 800, 808, 816, 824,
+ 832, 840, 848, 856, 864, 872, 880, 888, 896, 904,
+ 912, 920, 928, 936, 944, 952, 960, 968, 976, 984,
+ 992, 1000, 1008, 1016, 8,
+};
+
+u32 eotf_y_axis_hlg[MAX_EOTF] = {
+ 0, 5, 21, 48, 85, 133, 192, 261, 341, 432,
+ 533, 645, 768, 901, 1045, 1200, 1365, 1454, 1552, 1658,
+ 1774, 1900, 2038, 2189, 2353, 2533, 2728, 2942, 3175, 3430,
+ 3707, 4010, 4341, 4517, 4702, 4894, 5096, 5306, 5525, 5755,
+ 5994, 6245, 6506, 6779, 7065, 7363, 7674, 7999, 8339, 8694,
+ 9065, 9453, 9857, 10280, 10722, 11183, 11665, 12169, 12695, 13245,
+ 13819, 14418, 15045, 15699, 684,
+};
+
+
+/* GM section */
+// 709 to P3
+u32 gm_coef_709_p3[MAX_GM] = {
+ 13475, 2909, 0,
+ 544, 15840, 0,
+ 280, 1186, 14918,
+};
+
+// 709 to 2020
+u32 gm_coef_709_2020[MAX_GM] = {
+ 10279, 5395, 710,
+ 1132, 15066, 186,
+ 269, 1442, 14673,
+};
+
+// P3 to 709
+u32 gm_coef_p3_709[MAX_GM] = {
+ 20069, -3685, 0,
+ -689, 17073, 0,
+ -322, -1288, 17994,
+};
+
+// P3 to 2020
+u32 gm_coef_p3_2020[MAX_GM] = {
+ 12351, 3254, 779,
+ 749, 15430, 204,
+ -20, 288, 16115,
+};
+
+// 2020 to P3
+u32 gm_coef_2020_p3[MAX_GM] = {
+ 22013, -4623, -1006,
+ -1070, 17626, -172,
+ 46, -321, 16659,
+};
+
+// 2020 to 709
+u32 gm_coef_2020_709[MAX_GM] = {
+ 27205, -9628, -1194,
+ -2041, 18561, -137,
+ -297, -1648, 18329,
+};
+
+/* TM section */
+// TUNE
+u32 tm_x_tune[MAX_TM] = { 0, };
+u32 tm_y_tune[MAX_TM] = { 0, };
+
+// sRGB
+u32 tm_x_axis_srgb[MAX_TM] = {
+ 0, 64, 128, 256, 384, 512, 640, 768, 1024, 1280,
+ 1536, 1792, 2048, 2560, 3072, 3584, 4096, 4608, 5120, 5376,
+ 5632, 6144, 7168, 8192, 8704, 9216, 10240, 11264, 12288, 13312,
+ 13824, 14336, 2048,
+};
+
+u32 tm_y_axis_srgb[MAX_TM] = {
+ 0, 51, 87, 135, 170, 198, 223, 245, 284, 317,
+ 346, 373, 398, 442, 481, 517, 549, 580, 608, 622,
+ 635, 661, 709, 752, 773, 793, 831, 867, 901, 934,
+ 949, 965, 58,
+};
+
+// rec.601/709
+u32 tm_x_axis_601_709[MAX_TM] = {
+ 0, 256, 384, 512, 640, 768, 1024, 1280, 1536, 1792,
+ 2048, 2304, 2560, 2816, 3072, 3584, 3840, 4096, 4352, 4608,
+ 5120, 5632, 6144, 7168, 8192, 9216, 10240, 11264, 12288, 13312,
+ 14336, 15360, 1024,
+};
+
+u32 tm_y_axis_601_709[MAX_TM] = {
+ 0, 72, 106, 135, 160, 182, 222, 256, 286, 314,
+ 340, 364, 386, 408, 428, 466, 484, 501, 518, 534,
+ 565, 594, 622, 674, 722, 767, 809, 849, 886, 923,
+ 957, 991, 32,
+};
+
+// smpte2084 1000nit
+u32 tm_x_axis_2084_1000nit[MAX_TM] = {
+ 0, 4, 8, 16, 32, 64, 96, 128, 192, 256,
+ 384, 512, 640, 768, 1024, 1280, 1536, 2048, 2560, 3072,
+ 3584, 4096, 4608, 5120, 6144, 7168, 8192, 9216, 10240, 11264,
+ 12288, 14336, 2048,
+};
+
+u32 tm_y_axis_2084_1000nit[MAX_TM] = {
+ 0, 91, 119, 152, 191, 236, 265, 286, 319, 343,
+ 379, 405, 426, 444, 472, 494, 513, 542, 566, 585,
+ 601, 616, 629, 640, 660, 677, 692, 705, 716, 727,
+ 737, 754, 15,
+};
+
+// 1/2.2 gamma 1000nit
+u32 tm_x_axis_gamma_2P2_1000[MAX_TM] = {
+ 0, 1, 2, 4, 8, 16, 32, 64, 96, 128,
+ 192, 256, 384, 512, 768, 1024, 1536, 2048, 2560, 3072,
+ 4096, 5120, 6144, 6656, 7168, 7680, 8192, 9216, 10240, 12288,
+ 14336, 15360, 1024,
+};
+
+u32 tm_y_axis_gamma_2P2_1000[MAX_TM] = {
+ 0, 16, 22, 31, 42, 57, 79, 108, 130, 148,
+ 178, 203, 244, 278, 334, 381, 458, 522, 577, 627,
+ 715, 791, 860, 891, 917, 936, 950, 971, 985, 1004,
+ 1015, 1019, 4,
+};
+
+// 1/2.2 gamma 4000nit
+u32 tm_x_axis_gamma_2P2_4000[MAX_TM] = {
+ 0, 1, 2, 4, 8, 16, 32, 64, 96, 128,
+ 192, 256, 384, 512, 768, 1024, 1536, 2048, 2560, 3072,
+ 3584, 4096, 4608, 5120, 6144, 7168, 8192, 9216, 10240, 12288,
+ 14336, 15360, 1024,
+};
+
+u32 tm_y_axis_gamma_2P2_4000[MAX_TM] = {
+ 0, 16, 22, 31, 42, 57, 79, 108, 130, 148,
+ 178, 203, 244, 278, 334, 381, 458, 522, 577, 627,
+ 672, 710, 743, 771, 818, 856, 888, 914, 936, 972,
+ 1001, 1012, 11,
+};
+
+// 1/2.4 gamma
+u32 tm_x_axis_gamma_2P4[MAX_TM] = {
+ 0, 16, 32, 64, 128, 256, 384, 512, 768, 1024,
+ 1280, 1536, 2048, 2560, 3072, 3584, 4096, 4608, 5120, 6144,
+ 7168, 7680, 8192, 8704, 9216, 10240, 11264, 12288, 12800, 13312,
+ 14336, 15360, 1024,
+};
+
+u32 tm_y_axis_gamma_2P4[MAX_TM] = {
+ 0, 57, 76, 101, 135, 181, 214, 241, 286, 322,
+ 354, 382, 430, 472, 509, 543, 574, 603, 630, 680,
+ 725, 746, 766, 786, 805, 841, 875, 907, 923, 938,
+ 968, 996, 27,
+};
+
+// 1/2.6 gamma
+u32 tm_x_axis_gamma_2P6[MAX_TM] = {
+ 0, 16, 32, 64, 128, 192, 256, 384, 512, 640,
+ 768, 1024, 1280, 1536, 1792, 2048, 2560, 2816, 3072, 3584,
+ 4096, 4608, 5120, 6144, 7168, 8192, 9216, 10240, 11264, 12288,
+ 14336, 15360, 1024,
+};
+
+u32 tm_y_axis_gamma_2P6[MAX_TM] = {
+ 0, 71, 93, 121, 158, 185, 207, 242, 270, 294,
+ 315, 352, 384, 412, 437, 460, 501, 520, 537, 570,
+ 600, 628, 654, 702, 744, 784, 820, 854, 886, 916,
+ 972, 998, 25,
+};
+
+/* FOR TEST */
+u32 eotf_x_axis_dft[MAX_EOTF] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 48, 64, 96,
+ 128, 192, 256, 320, 384, 448, 512, 576, 640, 704,
+ 768, 832, 896, 960, 64,
+};
+
+u32 eotf_y_axis_dft[MAX_EOTF] = {
+ 0, 1, 2, 4, 5, 6, 7, 9, 10, 11,
+ 12, 14, 15, 16, 17, 19, 20, 21, 22, 24,
+ 25, 26, 27, 29, 30, 31, 32, 33, 35, 36,
+ 37, 38, 40, 41, 42, 43, 45, 46, 47, 48,
+ 50, 51, 52, 53, 55, 56, 57, 60, 85, 149,
+ 235, 482, 835, 1306, 1905, 2638, 3514, 4540, 5722, 7066,
+ 8579, 10266, 12131, 14181, 2202,
+};
+
+s32 gm_coef_bypass[MAX_GM] = {
+ 16384, 0, 0,
+ 0, 16384, 0,
+ 0, 0, 16384,
+};
+
+u32 tm_x_axis_dft[MAX_TM] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 20, 24, 32,
+ 64, 128, 256, 512, 1024, 1536, 2048, 3072, 4096, 6144,
+ 8192, 12288, 4096,
+};
+
+u32 tm_y_axis_dft[MAX_TM] = {
+ 0, 12, 17, 20, 23, 26, 28, 30, 32, 34,
+ 35, 37, 38, 40, 41, 43, 44, 48, 53, 60,
+ 82, 113, 154, 212, 290, 349, 398, 478, 545, 655,
+ 747, 898, 125,
+};
+
+#endif /* _HDR_LUT_ */
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Header file for Exynos HDR metadata
+ *
+ * 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 VENDOR_VIDEO_API_H_
+#define VENDOR_VIDEO_API_H_
+
+#include <linux/types.h>
+
+enum exynos_video_info_type {
+ VIDEO_INFO_TYPE_INVALID = 0,
+ VIDEO_INFO_TYPE_HDR_STATIC = 0x1 << 0,
+ VIDEO_INFO_TYPE_COLOR_ASPECTS = 0x1 << 1,
+ VIDEO_INFO_TYPE_INTERLACED = 0x1 << 2,
+ VIDEO_INFO_TYPE_YSUM_DATA = 0x1 << 3,
+};
+
+struct exynos_video_ysum_data {
+ unsigned int high;
+ unsigned int low;
+};
+
+struct exynos_color_aspects {
+ int mrange;
+ int mprimaries;
+ int mtransfer;
+ int mmatrix_coeffs;
+};
+
+struct exynos_primaries {
+ unsigned int x;
+ unsigned int y;
+};
+
+struct exynos_type1 {
+ struct exynos_primaries mr;
+ struct exynos_primaries mg;
+ struct exynos_primaries mb;
+ struct exynos_primaries mw;
+ unsigned int mmax_display_luminance;
+ unsigned int mmin_display_luminance;
+ unsigned int mmax_content_light_level;
+ unsigned int mmax_frame_average_light_level;
+};
+
+struct exynos_hdr_static_info {
+ int mid;
+ union {
+ struct exynos_type1 stype1;
+ };
+};
+
+struct exynos_video_dec_data {
+ struct exynos_hdr_static_info shdr_static_info;
+ struct exynos_color_aspects scolor_aspects;
+ int ninterlaced_type;
+};
+
+struct exynos_video_enc_data {
+ struct exynos_hdr_static_info shdr_static_info;
+ struct exynos_color_aspects scolor_aspects;
+ struct exynos_video_ysum_data sysum_data;
+};
+
+struct exynos_video_meta {
+ enum exynos_video_info_type etype;
+
+ union {
+ struct exynos_video_dec_data dec;
+ struct exynos_video_enc_data enc;
+ } data;
+};
+
+#endif /* VENDOR_VIDEO_API_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Helper 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 <linux/clk.h>
+#include <linux/err.h>
+#include <linux/pm_runtime.h>
+#include <asm/cacheflush.h>
+#include <asm/page.h>
+#if defined(CONFIG_EXYNOS_CONTENT_PATH_PROTECTION)
+#include <linux/smc.h>
+#endif
+#if defined(CONFIG_SUPPORT_LEGACY_ION)
+#include <linux/exynos_iovmm.h>
+#endif
+
+#include "decon.h"
+#include "dsim.h"
+#include "dpp.h"
+#include "displayport.h"
+#include "./panels/lcd_ctrl.h"
+#include <video/mipi_display.h>
+
+static int __dpu_match_dev(struct device *dev, void *data)
+{
+ struct dpp_device *dpp;
+ struct dsim_device *dsim;
+ struct displayport_device *displayport;
+ struct decon_device *decon = (struct decon_device *)data;
+
+ decon_dbg("%s: drvname(%s)\n", __func__, dev->driver->name);
+
+ if (!strcmp(DPP_MODULE_NAME, dev->driver->name)) {
+ dpp = (struct dpp_device *)dev_get_drvdata(dev);
+ decon->dpp_sd[dpp->id] = &dpp->sd;
+ decon_dbg("dpp%d sd name(%s) attr(0x%lx)\n", dpp->id,
+ decon->dpp_sd[dpp->id]->name, dpp->attr);
+ } else if (!strcmp(DSIM_MODULE_NAME, dev->driver->name)) {
+ dsim = (struct dsim_device *)dev_get_drvdata(dev);
+ decon->dsim_sd[dsim->id] = &dsim->sd;
+ decon_dbg("dsim sd name(%s)\n", dsim->sd.name);
+ } else if (!strcmp(DISPLAYPORT_MODULE_NAME, dev->driver->name)) {
+ displayport = (struct displayport_device *)dev_get_drvdata(dev);
+ decon->displayport_sd = &displayport->sd;
+ decon_dbg("displayport sd name(%s)\n", displayport->sd.name);
+ } else {
+ decon_err("failed to get driver name\n");
+ }
+
+ return 0;
+}
+
+int dpu_get_sd_by_drvname(struct decon_device *decon, char *drvname)
+{
+ struct device_driver *drv;
+ struct device *dev;
+
+ drv = driver_find(drvname, &platform_bus_type);
+ if (IS_ERR_OR_NULL(drv)) {
+ decon_err("failed to find driver\n");
+ return -ENODEV;
+ }
+
+ dev = driver_find_device(drv, NULL, decon, __dpu_match_dev);
+
+ return 0;
+}
+
+u32 dpu_translate_fmt_to_dpp(u32 format)
+{
+ switch (format) {
+ /* YUV420 */
+ case DECON_PIXEL_FORMAT_NV12:
+ return DECON_PIXEL_FORMAT_NV21;
+ case DECON_PIXEL_FORMAT_NV21:
+ return DECON_PIXEL_FORMAT_NV12;
+ case DECON_PIXEL_FORMAT_NV12N_10B:
+ return DECON_PIXEL_FORMAT_NV12N_10B;
+ case DECON_PIXEL_FORMAT_NV12M:
+ return DECON_PIXEL_FORMAT_NV21M;
+ case DECON_PIXEL_FORMAT_NV21M:
+ return DECON_PIXEL_FORMAT_NV12M;
+ case DECON_PIXEL_FORMAT_NV12N:
+ return DECON_PIXEL_FORMAT_NV12N;
+ case DECON_PIXEL_FORMAT_YUV420:
+ return DECON_PIXEL_FORMAT_YVU420;
+ case DECON_PIXEL_FORMAT_YVU420:
+ return DECON_PIXEL_FORMAT_YUV420;
+ case DECON_PIXEL_FORMAT_YUV420M:
+ return DECON_PIXEL_FORMAT_YVU420M;
+ case DECON_PIXEL_FORMAT_YVU420M:
+ return DECON_PIXEL_FORMAT_YUV420M;
+ /* YUV422 */
+ case DECON_PIXEL_FORMAT_NV16:
+ return DECON_PIXEL_FORMAT_NV61;
+ case DECON_PIXEL_FORMAT_NV61:
+ return DECON_PIXEL_FORMAT_NV16;
+ /* RGB32 */
+ case DECON_PIXEL_FORMAT_ARGB_8888:
+ return DECON_PIXEL_FORMAT_BGRA_8888;
+ case DECON_PIXEL_FORMAT_ABGR_8888:
+ return DECON_PIXEL_FORMAT_RGBA_8888;
+ case DECON_PIXEL_FORMAT_RGBA_8888:
+ return DECON_PIXEL_FORMAT_ABGR_8888;
+ case DECON_PIXEL_FORMAT_BGRA_8888:
+ return DECON_PIXEL_FORMAT_ARGB_8888;
+ case DECON_PIXEL_FORMAT_XRGB_8888:
+ return DECON_PIXEL_FORMAT_BGRX_8888;
+ case DECON_PIXEL_FORMAT_XBGR_8888:
+ return DECON_PIXEL_FORMAT_RGBX_8888;
+ case DECON_PIXEL_FORMAT_RGBX_8888:
+ return DECON_PIXEL_FORMAT_XBGR_8888;
+ case DECON_PIXEL_FORMAT_BGRX_8888:
+ return DECON_PIXEL_FORMAT_XRGB_8888;
+ default:
+ return format;
+ }
+}
+
+u32 dpu_get_bpp(enum decon_pixel_format fmt)
+{
+ switch (fmt) {
+ 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_ARGB_2101010:
+ case DECON_PIXEL_FORMAT_ABGR_2101010:
+ case DECON_PIXEL_FORMAT_RGBA_1010102:
+ case DECON_PIXEL_FORMAT_BGRA_1010102:
+ return 32;
+
+ case DECON_PIXEL_FORMAT_RGBA_5551:
+ case DECON_PIXEL_FORMAT_RGB_565:
+ return 16;
+
+ case DECON_PIXEL_FORMAT_NV12N_10B:
+ case DECON_PIXEL_FORMAT_NV12M_S10B:
+ case DECON_PIXEL_FORMAT_NV21M_S10B:
+ case DECON_PIXEL_FORMAT_NV12M_P010:
+ case DECON_PIXEL_FORMAT_NV21M_P010:
+ /* YUV422 */
+ 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 15;
+
+ case DECON_PIXEL_FORMAT_NV12:
+ case DECON_PIXEL_FORMAT_NV21:
+ case DECON_PIXEL_FORMAT_NV12M:
+ case DECON_PIXEL_FORMAT_NV21M:
+ case DECON_PIXEL_FORMAT_YUV420:
+ case DECON_PIXEL_FORMAT_YVU420:
+ case DECON_PIXEL_FORMAT_YUV420M:
+ case DECON_PIXEL_FORMAT_YVU420M:
+ case DECON_PIXEL_FORMAT_NV12N:
+ /* YUV422 */
+ case DECON_PIXEL_FORMAT_NV16:
+ case DECON_PIXEL_FORMAT_NV61:
+ case DECON_PIXEL_FORMAT_YVU422_3P:
+ return 12;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int dpu_get_meta_plane_cnt(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_RGBA_5551:
+ case DECON_PIXEL_FORMAT_RGB_565:
+ case DECON_PIXEL_FORMAT_NV12N:
+ case DECON_PIXEL_FORMAT_NV16:
+ case DECON_PIXEL_FORMAT_NV61:
+ case DECON_PIXEL_FORMAT_NV12:
+ case DECON_PIXEL_FORMAT_NV21:
+ case DECON_PIXEL_FORMAT_NV12M:
+ case DECON_PIXEL_FORMAT_NV21M:
+ case DECON_PIXEL_FORMAT_YVU422_3P:
+ case DECON_PIXEL_FORMAT_YUV420:
+ case DECON_PIXEL_FORMAT_YVU420:
+ case DECON_PIXEL_FORMAT_YUV420M:
+ case DECON_PIXEL_FORMAT_YVU420M:
+ return -1;
+
+ 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:
+ return 1;
+
+ 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_NV16M_P210:
+ case DECON_PIXEL_FORMAT_NV61M_P210:
+ case DECON_PIXEL_FORMAT_NV16M_S10B:
+ case DECON_PIXEL_FORMAT_NV61M_S10B:
+ return 2;
+
+ default:
+ decon_err("%s: invalid format(%d)\n", __func__, format);
+ return -1;
+ }
+}
+
+int dpu_get_plane_cnt(enum decon_pixel_format format, bool is_hdr)
+{
+ 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_RGBA_5551:
+ case DECON_PIXEL_FORMAT_RGB_565:
+ case DECON_PIXEL_FORMAT_NV12N:
+ return 1;
+
+ 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:
+ if (is_hdr)
+ return 2;
+ else
+ return 1;
+
+ case DECON_PIXEL_FORMAT_NV16:
+ case DECON_PIXEL_FORMAT_NV61:
+ case DECON_PIXEL_FORMAT_NV12:
+ case DECON_PIXEL_FORMAT_NV21:
+ case DECON_PIXEL_FORMAT_NV12M:
+ case DECON_PIXEL_FORMAT_NV21M:
+ return 2;
+
+ 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_NV16M_P210:
+ case DECON_PIXEL_FORMAT_NV61M_P210:
+ case DECON_PIXEL_FORMAT_NV16M_S10B:
+ case DECON_PIXEL_FORMAT_NV61M_S10B:
+ if (is_hdr)
+ return 3;
+ else
+ return 2;
+
+ case DECON_PIXEL_FORMAT_YVU422_3P:
+ case DECON_PIXEL_FORMAT_YUV420:
+ case DECON_PIXEL_FORMAT_YVU420:
+ case DECON_PIXEL_FORMAT_YUV420M:
+ case DECON_PIXEL_FORMAT_YVU420M:
+ return 3;
+
+ default:
+ decon_err("%s: invalid format(%d)\n", __func__, format);
+ return 1;
+ }
+}
+
+u32 dpu_get_alpha_len(int 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:
+ return 8;
+
+ case DECON_PIXEL_FORMAT_ABGR_4444:
+ case DECON_PIXEL_FORMAT_RGBA_4444:
+ case DECON_PIXEL_FORMAT_BGRA_4444:
+ return 4;
+
+ case DECON_PIXEL_FORMAT_ARGB_2101010:
+ case DECON_PIXEL_FORMAT_ABGR_2101010:
+ case DECON_PIXEL_FORMAT_RGBA_1010102:
+ case DECON_PIXEL_FORMAT_BGRA_1010102:
+ return 2;
+
+ case DECON_PIXEL_FORMAT_RGBA_5551:
+ case DECON_PIXEL_FORMAT_BGRA_5551:
+ return 1;
+
+ 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_BGR_565:
+ return 0;
+
+ default:
+ return 0;
+ }
+}
+
+bool decon_intersect(struct decon_rect *r1, struct decon_rect *r2)
+{
+ return !(r1->left > r2->right || r1->right < r2->left ||
+ r1->top > r2->bottom || r1->bottom < r2->top);
+}
+
+int decon_intersection(struct decon_rect *r1,
+ struct decon_rect *r2, struct decon_rect *r3)
+{
+ r3->top = max(r1->top, r2->top);
+ r3->bottom = min(r1->bottom, r2->bottom);
+ r3->left = max(r1->left, r2->left);
+ r3->right = min(r1->right, r2->right);
+ return 0;
+}
+
+bool is_decon_rect_differ(struct decon_rect *r1, struct decon_rect *r2)
+{
+ return ((r1->left != r2->left) || (r1->top != r2->top) ||
+ (r1->right != r2->right) || (r1->bottom != r2->bottom));
+}
+
+bool is_scaling(struct decon_win_config *config)
+{
+ return (config->dst.w != config->src.w) || (config->dst.h != config->src.h);
+}
+
+bool is_full(struct decon_rect *r, struct decon_lcd *lcd)
+{
+ return (r->left == 0) && (r->top == 0) &&
+ (r->right == lcd->xres - 1) && (r->bottom == lcd->yres - 1);
+}
+
+bool is_rgb32(int 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:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool is_decon_opaque_format(int format)
+{
+ switch (format) {
+ case DECON_PIXEL_FORMAT_RGBA_8888:
+ case DECON_PIXEL_FORMAT_BGRA_8888:
+ case DECON_PIXEL_FORMAT_RGBA_5551:
+ case DECON_PIXEL_FORMAT_ARGB_8888:
+ case DECON_PIXEL_FORMAT_ABGR_8888:
+ case DECON_PIXEL_FORMAT_ARGB_2101010:
+ case DECON_PIXEL_FORMAT_ABGR_2101010:
+ case DECON_PIXEL_FORMAT_RGBA_1010102:
+ case DECON_PIXEL_FORMAT_BGRA_1010102:
+ return false;
+
+ default:
+ return true;
+ }
+}
+
+void dpu_unify_rect(struct decon_rect *r1, struct decon_rect *r2,
+ struct decon_rect *dst)
+{
+ dst->top = min(r1->top, r2->top);
+ dst->bottom = max(r1->bottom, r2->bottom);
+ dst->left = min(r1->right, r2->right);
+ dst->right = max(r1->right, r2->right);
+}
+
+void decon_to_psr_info(struct decon_device *decon, struct decon_mode_info *psr)
+{
+ psr->psr_mode = decon->dt.psr_mode;
+ psr->trig_mode = decon->dt.trig_mode;
+ psr->dsi_mode = decon->dt.dsi_mode;
+ psr->out_type = decon->dt.out_type;
+}
+
+void decon_to_init_param(struct decon_device *decon, struct decon_param *p)
+{
+ struct decon_lcd *lcd_info = decon->lcd_info;
+ struct v4l2_mbus_framefmt mbus_fmt;
+
+ mbus_fmt.width = 0;
+ mbus_fmt.height = 0;
+ mbus_fmt.code = 0;
+ mbus_fmt.field = 0;
+ mbus_fmt.colorspace = 0;
+
+ p->lcd_info = lcd_info;
+ p->psr.psr_mode = decon->dt.psr_mode;
+ p->psr.trig_mode = decon->dt.trig_mode;
+ p->psr.dsi_mode = decon->dt.dsi_mode;
+ p->psr.out_type = decon->dt.out_type;
+ p->nr_windows = decon->dt.max_win;
+ p->disp_ss_regs = decon->res.ss_regs;
+ decon_dbg("%s: psr(%d) trig(%d) dsi(%d) out(%d) wins(%d) LCD[%d %d]\n",
+ __func__, p->psr.psr_mode, p->psr.trig_mode,
+ p->psr.dsi_mode, p->psr.out_type, p->nr_windows,
+ decon->lcd_info->xres, decon->lcd_info->yres);
+}
+
+void dpu_debug_printk(const char *function_name, const char *format, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ va_start(args, format);
+ vaf.fmt = format;
+ vaf.va = &args;
+
+ printk(KERN_INFO "[%s] %pV", function_name, &vaf);
+
+ va_end(args);
+}
+
+void __iomem *dpu_get_sysreg_addr(void)
+{
+ void __iomem *regs;
+
+ if (of_have_populated_dt()) {
+ struct device_node *nd;
+ nd = of_find_compatible_node(NULL, NULL,
+ "samsung,exynos9-disp_ss");
+ if (!nd) {
+ decon_err("failed find compatible node(sysreg-disp)");
+ return NULL;
+ }
+
+ regs = of_iomap(nd, 0);
+ if (!regs) {
+ decon_err("Failed to get sysreg-disp address.");
+ return NULL;
+ }
+ } else {
+ decon_err("failed have populated device tree");
+ return NULL;
+ }
+
+ decon_dbg("%s: default sysreg value(0x%x)\n", __func__, readl(regs));
+
+ return regs;
+}
+
+#if defined(CONFIG_EXYNOS_CONTENT_PATH_PROTECTION)
+static int decon_get_protect_id(int dma_id)
+{
+ int prot_id = 0;
+
+ switch (dma_id) {
+ case IDMA_G0:
+ prot_id = PROT_G0;
+ break;
+ case IDMA_G1:
+ prot_id = PROT_G1;
+ break;
+ case IDMA_GF:
+ prot_id = PROT_GF;
+ break;
+ case IDMA_VG0:
+ prot_id = PROT_VG0;
+ break;
+ default:
+ decon_err("Unknown DMA_ID (%d)\n", dma_id);
+ break;
+ }
+
+ return prot_id;
+}
+
+static int decon_control_protection(int dma_id, bool en)
+{
+ int ret = SUCCESS_EXYNOS_SMC;
+ int prot_id;
+
+ prot_id = decon_get_protect_id(dma_id);
+ ret = exynos_smc(SMC_PROTECTION_SET, 0, prot_id,
+ (en ? SMC_PROTECTION_ENABLE : SMC_PROTECTION_DISABLE));
+
+ if (ret)
+ decon_err("DMA%d (en=%d): exynos_smc call fail (err=%d)\n",
+ dma_id, en, ret);
+ else
+ decon_dbg("DMA%d protection %s\n",
+ dma_id, en ? "enabled" : "disabled");
+
+ return ret;
+}
+
+void decon_set_protected_content(struct decon_device *decon,
+ struct decon_reg_data *regs)
+{
+ bool en;
+ int dma_id, i, ret = 0;
+ u32 change = 0;
+ u32 cur_protect_bits = 0;
+
+ /* IDMA protection configs (G0,G1,VG0,VG1,VGF0,VGF1) */
+ for (i = 0; i < decon->dt.max_win; i++) {
+ if (!regs)
+ break;
+
+ cur_protect_bits |=
+ (regs->protection[i] << regs->dpp_config[i].idma_type);
+ }
+
+ /* ODMA protection config (WB: writeback) */
+ if (decon->dt.out_type == DECON_OUT_WB)
+ if (regs)
+ cur_protect_bits |= (regs->protection[MAX_DECON_WIN] << ODMA_WB);
+
+ if (decon->prev_protection_bitmask != cur_protect_bits) {
+
+ /* apply protection configs for each DMA */
+ for (dma_id = 0; dma_id < MAX_DPP_CNT; dma_id++) {
+ en = cur_protect_bits & (1 << dma_id);
+
+ change = (cur_protect_bits & (1 << dma_id)) ^
+ (decon->prev_protection_bitmask & (1 << dma_id));
+
+ if (change)
+ ret = decon_control_protection(dma_id, en);
+ }
+ }
+
+ /* save current portection configs */
+ decon->prev_protection_bitmask = cur_protect_bits;
+}
+#endif
+
+#if defined(CONFIG_EXYNOS_AFBC_DEBUG)
+/* id : VGF0=0, VGF1=1 */
+static void dpu_dump_data_to_console(void *v_addr, int buf_size, int id)
+{
+ dpp_info("=== (CH#%d) Frame Buffer Data(128 Bytes) ===\n", id);
+
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ v_addr, buf_size, false);
+}
+
+void dpu_dump_afbc_info(void)
+{
+ int i, j;
+ struct decon_device *decon;
+ struct dpu_afbc_info *afbc_info;
+ void *v_addr[2];
+ int size[2];
+
+ for (i = 0; i < MAX_DECON_CNT; i++) {
+ decon = get_decon_drvdata(i);
+ if (decon == NULL)
+ continue;
+
+ afbc_info = &decon->d.prev_afbc_info;
+ decon_info("%s: previous AFBC channel information\n", __func__);
+ for (j = 0; j < 2; ++j) { /* VGF0(0), VGF1(1) */
+ if (!afbc_info->is_afbc[j])
+ continue;
+
+ v_addr[j] = dma_buf_vmap(afbc_info->dma_buf[j]);
+ size[j] = afbc_info->dma_buf[j]->size;
+ decon_info("\t[%s] Base(0x%p), KV(0x%p), size(%d)\n",
+ j ? "VGF1" : "VGF0",
+ (void *)afbc_info->dma_addr[j],
+ v_addr[j], size[j]);
+ dma_buf_vunmap(afbc_info->dma_buf[j], v_addr[j]);
+ }
+
+ afbc_info = &decon->d.cur_afbc_info;
+ decon_info("%s: current AFBC channel information\n", __func__);
+ for (j = 0; j < 2; ++j) { /* VGF0(0), VGF1(1) */
+ if (!afbc_info->is_afbc[j])
+ continue;
+
+ v_addr[j] = dma_buf_vmap(afbc_info->dma_buf[j]);
+ size[j] = afbc_info->dma_buf[j]->size;
+ decon_info("\t[%s] Base(0x%p), KV(0x%p), size(%d)\n",
+ j ? "VGF1" : "VGF0",
+ (void *)afbc_info->dma_addr[j],
+ v_addr[j], size[j]);
+ dma_buf_vunmap(afbc_info->dma_buf[j], v_addr[j]);
+ }
+ }
+}
+
+static int dpu_dump_buffer_data(struct dpp_device *dpp)
+{
+ int i;
+ int id_idx = 0;
+ int dump_size = 128;
+ struct decon_device *decon;
+ struct dpu_afbc_info *afbc_info;
+ void *v_addr;
+
+ if (dpp->state == DPP_STATE_ON) {
+
+ for (i = 0; i < MAX_DECON_CNT; i++) {
+ decon = get_decon_drvdata(i);
+ if (decon == NULL)
+ continue;
+
+ if (DPU_CH2DMA(dpp->id) == IDMA_GF)
+ id_idx = 1;
+
+ afbc_info = &decon->d.cur_afbc_info;
+ if (!afbc_info->is_afbc[id_idx])
+ continue;
+
+ if (afbc_info->dma_buf[id_idx]->size > 2048)
+ dump_size = 128;
+ else
+ dump_size = afbc_info->dma_buf[id_idx]->size / 16;
+
+ v_addr = dma_buf_vmap(afbc_info->dma_buf[id_idx]);
+ decon_info("Base(0x%p), KV(0x%p), size(%d)\n",
+ (void *)afbc_info->dma_addr[id_idx],
+ v_addr, dump_size);
+
+ if (IS_ERR_OR_NULL(v_addr))
+ continue;
+
+ dpu_dump_data_to_console(v_addr, dump_size, dpp->id);
+ dma_buf_vunmap(afbc_info->dma_buf[id_idx], v_addr);
+ }
+ }
+
+ return 0;
+}
+#endif
+
+int dpu_sysmmu_fault_handler(struct iommu_domain *domain,
+ struct device *dev, unsigned long iova, int flags, void *token)
+{
+ struct decon_device *decon = NULL;
+ struct dpp_device *dpp = NULL;
+ int i;
+
+ if (!strcmp(DSIM_MODULE_NAME, dev->driver->name)) {
+ decon = get_decon_drvdata(0);
+ } else if (!strcmp(DISPLAYPORT_MODULE_NAME, dev->driver->name)) {
+ decon = get_decon_drvdata(2);
+ } else {
+ decon_err("unknown driver for dpu sysmmu falut handler(%s)\n",
+ dev->driver->name);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < MAX_DPP_SUBDEV; i++) {
+ if (test_bit(i, &decon->prev_used_dpp)) {
+ dpp = get_dpp_drvdata(i);
+#if defined(CONFIG_EXYNOS_AFBC_DEBUG)
+ dpu_dump_buffer_data(dpp);
+#endif
+ }
+ }
+
+ decon_dump(decon);
+
+ return 0;
+}
--- /dev/null
+config EXYNOS_DECON_LCD
+ depends on EXYNOS_DPU20
+ bool "Select LCD panel driver"
+
+config EXYNOS_DECON_LCD_S6E3HA2K
+ depends on EXYNOS_DECON_LCD && EXYNOS_MIPI_DSIM
+ tristate "S6E3HA2K AMOLED WQHD LCD driver(1440 x 2560)"
+
+config EXYNOS_DECON_LCD_S6E3HF4
+ depends on EXYNOS_DECON_LCD && EXYNOS_MIPI_DSIM
+ tristate "S6E3HF4 AMOLED WQHD LCD driver(1440 x 2560)"
+ default n
+
+config EXYNOS_DECON_LCD_S6E3HA6
+ depends on EXYNOS_DECON_LCD && EXYNOS_MIPI_DSIM
+ tristate "S6E3HA6 AMOLED WQHD+ LCD driver(1440 x 2960)"
+ default n
+
+config EXYNOS_DECON_LCD_S6E3HA8
+ depends on EXYNOS_DECON_LCD && EXYNOS_MIPI_DSIM
+ tristate "S6E3HA8 AMOLED WQHD+ LCD driver(1440 x 2960)"
+ default n
+
+config EXYNOS_DECON_LCD_S6E3AA2
+ depends on EXYNOS_DECON_LCD && EXYNOS_MIPI_DSIM
+ tristate "S6E3AA2 AMOLED HD LCD driver(720 x 1280)"
+ default n
+
+config EXYNOS_DECON_LCD_EMUL_DISP
+ depends on EXYNOS_DECON_LCD && EXYNOS_MIPI_DSIM
+ tristate "Virtual LCD driver for emulator(800 x 1280)"
--- /dev/null
+obj-$(CONFIG_EXYNOS_DECON_LCD_S6E3HA2K) += s6e3ha2k_mipi_lcd.o s6e3ha2k_lcd_ctrl.o
+obj-$(CONFIG_EXYNOS_DECON_LCD_S6E3HF4) += s6e3hf4_mipi_lcd.o s6e3hf4_lcd_ctrl.o
+obj-$(CONFIG_EXYNOS_DECON_LCD_S6E3HA6) += s6e3ha6_mipi_lcd.o s6e3ha6_lcd_ctrl.o
+obj-$(CONFIG_EXYNOS_DECON_LCD_S6E3HA8) += s6e3ha8_mipi_lcd.o s6e3ha8_lcd_ctrl.o
+obj-$(CONFIG_EXYNOS_DECON_LCD_S6E3AA2) += s6e3aa2_mipi_lcd.o s6e3aa2_lcd_ctrl.o
+obj-$(CONFIG_EXYNOS_DECON_LCD_EMUL_DISP) += emul_disp_mipi_lcd.o emul_disp_lcd_ctrl.o
--- /dev/null
+/* drivers/video/exynos_decon/lcd.h
+ *
+ * Copyright (c) 2011 Samsung Electronics
+ * 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 __DECON_LCD__
+#define __DECON_LCD__
+
+enum decon_psr_mode {
+ DECON_VIDEO_MODE = 0,
+ DECON_DP_PSR_MODE = 1,
+ DECON_MIPI_COMMAND_MODE = 2,
+};
+
+/* Mic ratio: 0: 1/2 ratio, 1: 1/3 ratio */
+enum decon_mic_comp_ratio {
+ MIC_COMP_RATIO_1_2 = 0,
+ MIC_COMP_RATIO_1_3 = 1,
+ MIC_COMP_BYPASS
+};
+
+enum mic_ver {
+ MIC_VER_1_1,
+ MIC_VER_1_2,
+ MIC_VER_2_0,
+};
+
+enum type_of_ddi {
+ TYPE_OF_SM_DDI = 0,
+ TYPE_OF_MAGNA_DDI,
+ TYPE_OF_NORMAL_DDI,
+};
+
+#define MAX_RES_NUMBER 5
+#define HDR_CAPA_NUM 4
+
+struct lcd_res_info {
+ unsigned int width;
+ unsigned int height;
+ unsigned int dsc_en;
+ unsigned int dsc_width;
+ unsigned int dsc_height;
+};
+
+/* multi-resolution */
+struct lcd_mres_info {
+ unsigned int mres_en;
+ unsigned int mres_number;
+ struct lcd_res_info res_info[MAX_RES_NUMBER];
+};
+
+struct lcd_hdr_info {
+ unsigned int hdr_num;
+ unsigned int hdr_type[HDR_CAPA_NUM];
+ unsigned int hdr_max_luma;
+ unsigned int hdr_max_avg_luma;
+ unsigned int hdr_min_luma;
+};
+
+struct stdphy_pms {
+ unsigned int p;
+ unsigned int m;
+ unsigned int s;
+ unsigned int k;
+#if defined(CONFIG_EXYNOS_DSIM_DITHER)
+ unsigned int mfr;
+ unsigned int mrr;
+ unsigned int sel_pf;
+ unsigned int icp;
+ unsigned int afc_enb;
+ unsigned int extafc;
+ unsigned int feed_en;
+ unsigned int fsel;
+ unsigned int fout_mask;
+ unsigned int rsel;
+#endif
+};
+
+struct decon_lcd {
+ enum decon_psr_mode mode;
+ unsigned int vfp;
+ unsigned int vbp;
+ unsigned int hfp;
+ unsigned int hbp;
+
+ unsigned int vsa;
+ unsigned int hsa;
+
+ unsigned int xres;
+ unsigned int yres;
+
+ unsigned int width;
+ unsigned int height;
+
+ unsigned int hs_clk;
+ struct stdphy_pms dphy_pms;
+ unsigned int esc_clk;
+
+ unsigned int fps;
+ unsigned int mic_enabled;
+ enum decon_mic_comp_ratio mic_ratio;
+ unsigned int dsc_enabled;
+ unsigned int dsc_cnt;
+ unsigned int dsc_slice_num;
+ unsigned int dsc_slice_h;
+ enum mic_ver mic_ver;
+ enum type_of_ddi ddi_type;
+ unsigned int data_lane;
+ unsigned int cmd_underrun_lp_ref[MAX_RES_NUMBER];
+ unsigned int vt_compensation;
+ unsigned int mres_mode;
+ struct lcd_mres_info dt_lcd_mres;
+ struct lcd_hdr_info dt_lcd_hdr;
+ unsigned int bpc;
+};
+
+struct decon_dsc {
+/* 04 */ unsigned int comp_cfg;
+/* 05 */ unsigned int bit_per_pixel;
+/* 06-07 */ unsigned int pic_height;
+/* 08-09 */ unsigned int pic_width;
+/* 10-11 */ unsigned int slice_height;
+/* 12-13 */ unsigned int slice_width;
+/* 14-15 */ unsigned int chunk_size;
+/* 16-17 */ unsigned int initial_xmit_delay;
+/* 18-19 */ unsigned int initial_dec_delay;
+/* 21 */ unsigned int initial_scale_value;
+/* 22-23 */ unsigned int scale_increment_interval;
+/* 24-25 */ unsigned int scale_decrement_interval;
+/* 27 */ unsigned int first_line_bpg_offset;
+/* 28-29 */ unsigned int nfl_bpg_offset;
+/* 30-31 */ unsigned int slice_bpg_offset;
+/* 32-33 */ unsigned int initial_offset;
+/* 34-35 */ unsigned int final_offset;
+/* 58-59 */ unsigned int rc_range_parameters;
+
+ unsigned int overlap_w;
+ unsigned int width_per_enc;
+ unsigned char *dec_pps_t;
+};
+
+#endif
--- /dev/null
+/*
+ * drivers/video/dpu/panels/emul_disp_lcd_ctrl.c
+ *
+ * Samsung SoC MIPI LCD CONTROL functions
+ *
+ * Copyright (c) 2015 Samsung Electronics
+ *
+ * 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 "emul_disp_param.h"
+
+#include <video/mipi_display.h>
+#include "../dsim.h"
+
+static int dsim_write_hl_data(u32 id, const u8 *cmd, u32 cmdSize)
+{
+ int ret;
+ int retry;
+
+ retry = 5;
+
+try_write:
+ if (cmdSize == 1)
+ ret = dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE, cmd[0], 0);
+ else if (cmdSize == 2)
+ ret = dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE_PARAM, cmd[0], cmd[1]);
+ else
+ ret = dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)cmd,
+cmdSize);
+
+ if (ret != 0) {
+ if (--retry)
+ goto try_write;
+ else
+ dsim_err("dsim write failed, cmd : %x\n", cmd[0]);
+ }
+ return ret;
+}
+
+void lcd_enable(int id)
+{
+ int ret = 0;
+
+ dsim_info("MDD : %s was called\n", __func__);
+
+ ret = dsim_write_hl_data(id, SEQ_DISPLAY_ON, ARRAY_SIZE(SEQ_DISPLAY_ON));
+ if (ret < 0)
+ dsim_err("%s : fail to write CMD : DISPLAY_ON\n", __func__);
+}
+
+void lcd_disable(int id)
+{
+ /* This function needs to implement */
+}
+
+void lcd_init(int id, struct decon_lcd *lcd)
+{
+ int ret = 0;
+
+ dsim_dbg("MDD : %s was called\n", __func__);
+
+ ret = dsim_write_hl_data(id, SEQ_SLEEP_OUT, ARRAY_SIZE(SEQ_SLEEP_OUT));
+ if (ret < 0) {
+ dsim_err("%s : fail to write CMD : SEQ_SLEEP_OUT\n", __func__);
+ goto init_exit;
+ }
+
+ msleep(25);
+
+#if 0 /* If you want to configure LCD as command mode, below code is needed */
+ ret = dsim_write_hl_data(id, SEQ_TE_OUT, ARRAY_SIZE(SEQ_TE_OUT));
+ if (ret < 0) {
+ dsim_err(":%s fail to write CMD : SEQ_TE_OUT\n", __func__);
+ goto init_exit;
+ }
+
+ ret = dsim_write_hl_data(id, CA_SET_600, ARRAY_SIZE(CA_SET_600));
+ if (ret < 0) {
+ dsim_err(":%s fail to write CMD : CA_SET_600\n", __func__);
+ goto init_exit;
+ }
+
+ ret = dsim_write_hl_data(id, PA_SET_1280, ARRAY_SIZE(PA_SET_1280));
+ if (ret < 0) {
+ dsim_err(":%s fail to write CMD : PA_SET_1280\n", __func__);
+ goto init_exit;
+ }
+#endif
+init_exit:
+
+ dsim_dbg("%s -\n", __func__);
+
+ return;
+}
--- /dev/null
+/* drivers/video/fbdev/exynos/dpu/panels/emul_disp_mipi_lcd.c
+ *
+ * Samsung SoC MIPI LCD driver.
+ *
+ * Copyright (c) 2015 Samsung Electronics
+ *
+ * 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 <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/backlight.h>
+#include <video/mipi_display.h>
+
+#include "../dsim.h"
+#include "lcd_ctrl.h"
+#include "decon_lcd.h"
+
+#define MAX_BRIGHTNESS 255
+#define MIN_BRIGHTNESS 0
+#define DEFAULT_BRIGHTNESS 0
+
+static struct backlight_device *bd;
+
+static int emul_disp_get_brightness(struct backlight_device *bd)
+{
+ return bd->props.brightness;
+}
+
+static int emul_disp_set_brightness(struct backlight_device *bd)
+{
+ return 1;
+}
+
+static const struct backlight_ops emul_disp_backlight_ops = {
+ .get_brightness = emul_disp_get_brightness,
+ .update_status = emul_disp_set_brightness,
+};
+
+static int emul_disp_probe(struct dsim_device *dsim)
+{
+ bd = backlight_device_register("pwm-backlight.0", NULL,
+ NULL, &emul_disp_backlight_ops, NULL);
+ if (IS_ERR(bd))
+ pr_alert("failed to register backlight device!\n");
+
+ bd->props.max_brightness = MAX_BRIGHTNESS;
+ bd->props.brightness = DEFAULT_BRIGHTNESS;
+
+ return 1;
+}
+
+static int emul_disp_displayon(struct dsim_device *dsim)
+{
+ lcd_init(dsim->id, &dsim->lcd_info);
+ lcd_enable(dsim->id);
+ return 1;
+}
+
+static int emul_disp_suspend(struct dsim_device *dsim)
+{
+ return 1;
+}
+
+static int emul_disp_resume(struct dsim_device *dsim)
+{
+ return 1;
+}
+
+struct dsim_lcd_driver emul_disp_mipi_lcd_driver = {
+ .probe = emul_disp_probe,
+ .displayon = emul_disp_displayon,
+ .suspend = emul_disp_suspend,
+ .resume = emul_disp_resume,
+};
--- /dev/null
+/* linux/drivers/video/fbdev/exynos/dpu/panels/emul_disp_param.h
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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 __EMUL_DISP_PARAM_H__
+#define __EMUL_DISP_PARAM_H__
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+static const unsigned char SEQ_SLEEP_OUT[] = {
+ 0x11
+};
+
+static const unsigned char SEQ_TE_OUT[] = {
+ 0x35,
+ 0x00, 0x00
+};
+
+static const unsigned char SEQ_DISPLAY_ON[] = {
+ 0x29
+};
+
+static const unsigned char SEQ_DISPLAY_OFF[] = {
+ 0x28
+};
+
+static const unsigned char SEQ_SLEEP_IN[] = {
+ 0x10,
+ 0x00, 0x00
+};
+
+static const unsigned char CA_SET_600[] = {
+ 0x2a,
+ 0x0, 0x0, 0x2, 0x4e
+};
+
+static const unsigned char PA_SET_1280[] = {
+ 0x2b,
+ 0x0, 0x0, 0x4, 0xff
+};
+
+static const unsigned char SEQ_ESD_FG[] = {
+ 0xED,
+ 0x01, 0x04
+};
+
+static const unsigned char SEQ_ALLPOFF[] = {
+ 0x22
+};
+
+static const unsigned char SEQ_ALLPON[] = {
+ 0x23
+};
+
+static const unsigned char SEQ_NOP[] = {
+ 0x00,
+};
+
+static const unsigned char SEQ_TEST_KEY_OFF_F0[] = {
+ 0xF0, 0xA5, 0xA5
+};
+
+static const unsigned char SEQ_TEST_KEY_OFF_F1[] = {
+ 0xF1, 0xA5, 0xA5
+};
+
+#endif /* __EMUL_DISP_PARAM_H__ */
--- /dev/null
+/* linux/drivers/video/decon_display/s6e3fa0_gamma.h
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *
+ * Haowe Li <haowei.li@samsung.com>
+ *
+ * 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 __LCD_CTRL_H__
+#define __LCD_CTRL_H__
+
+#include "decon_lcd.h"
+
+void lcd_init(int id, struct decon_lcd *lcd);
+void lcd_enable(int id);
+void lcd_disable(int id);
+int lcd_gamma_ctrl(int id, unsigned int backlightlevel);
+int lcd_gamma_update(int id);
+int lcd_dump(int id);
+void lcd_mres(int id, int mres_idx, int dsc_en);
+void lcd_lane_ctl(int id, unsigned int lane_num);
+
+#endif /* __LCD_CTRL_H__ */
--- /dev/null
+/* s6e3aa2_lcd_ctrl.c
+ *
+ * Samsung SoC MIPI LCD CONTROL functions
+ *
+ * Copyright (c) 2015 Samsung Electronics
+ *
+ * SeungBeom, Park <sb1.park@samsung.com>
+ *
+ * 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 "s6e3aa2_param.h"
+#include "lcd_ctrl.h"
+
+#include "../dsim.h"
+#include <video/mipi_display.h>
+
+#define LDI_ID_REG 0x04
+#define LDI_ID_LEN 3
+
+#define ID 0
+
+#define VIDEO_MODE 1
+#define COMMAND_MODE 0
+
+struct decon_lcd s6e3aa2_lcd_info = {
+ /* Only availaable COMMAND MODE */
+ .mode = COMMAND_MODE,
+
+ .vfp = 2,
+ .vbp = 12,
+ .hfp = 1,
+ .hbp = 1,
+ .vsa = 1,
+ .hsa = 1,
+
+ .xres = 720,
+ .yres = 1280,
+
+ .width = 71,
+ .height = 114,
+
+ /* Mhz */
+ .hs_clk = 840,
+ .esc_clk = 20,
+
+ .fps = 60,
+};
+
+struct decon_lcd *decon_get_lcd_info(void)
+{
+ return &s6e3aa2_lcd_info;
+}
+
+void lcd_init(int id, struct decon_lcd * lcd)
+{
+
+ /* sleep out */
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE,
+ SLEEP_OUT[0], 0) < 0)
+ dsim_err("failed to send SLEEP_OUT.\n");
+
+ /* 20ms delay */
+ msleep(20);
+
+ /* Module Information Read */
+ /* skip */
+
+ /* Test Key Enable */
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned long) TEST_KEY_ON_F0,
+ ARRAY_SIZE(TEST_KEY_ON_F0)) < 0)
+ dsim_err("failed to send TEST_KEY_ON_F0.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned long) TEST_KEY_ON_F1,
+ ARRAY_SIZE(TEST_KEY_ON_F1)) < 0)
+ dsim_err("failed to send TEST_KEY_ON_F1.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned long) TEST_KEY_ON_FC,
+ ARRAY_SIZE(TEST_KEY_ON_FC)) < 0)
+ dsim_err("failed to send TEST_KEY_ON_FC.\n");
+
+ /* Common Setting */
+ /* TE(Vsync) ON/OFF */
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned long)TE_ON,
+ ARRAY_SIZE(TE_ON)) < 0)
+ dsim_err("failed to send TE_ON.\n");
+
+ /* PCD Setting */
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE_PARAM,
+ PCD_SET_DET_LOW[0], PCD_SET_DET_LOW[1]) < 0)
+ dsim_err("failed to send PCD_SET_DET_LOW.\n");
+
+ /* ERR_FG Setting */
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE_PARAM,
+ ERR_FG_SETTING[0], ERR_FG_SETTING[1]) < 0)
+ dsim_err("failed to send ERR_FG_SETTING.\n");
+
+ /* Brightness Setting */
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned long) GAMMA_CONDITION_SET,
+ ARRAY_SIZE(GAMMA_CONDITION_SET)) < 0)
+ dsim_err("failed to send GAMMA_CONDITION_SET.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned long) AID_SETTING,
+ ARRAY_SIZE(AID_SETTING)) < 0)
+ dsim_err("failed to send AID_SETTING.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned long) ELVSS_SET,
+ ARRAY_SIZE(ELVSS_SET)) < 0)
+ dsim_err("failed to send ELVSS_SET.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE_PARAM,
+ GAMMA_UPDATE[0], GAMMA_UPDATE[1]) < 0)
+ dsim_err("failed to send GAMMA_UPDATE.\n");
+
+ /* ACL ON/OFF */
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE_PARAM,
+ OPR_ACL_OFF[0], OPR_ACL_OFF[1]) < 0)
+ dsim_err("failed to send OPR_ACL_OFF.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE_PARAM,
+ ACL_OFF[0], ACL_OFF[1]) < 0)
+ dsim_err("failed to send ACL_OFF.\n");
+
+ /* HBM */
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE_PARAM,
+ HBM_OFF[0], HBM_OFF[1]) < 0)
+ dsim_err("failed to send HBM_OFF.\n");
+
+ /* ELVSS Temp Compensation */
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE_PARAM,
+ TSET_SETTING_1[0], TSET_SETTING_1[1]) < 0)
+ dsim_err("failed to send TSET_SETTING_1.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE_PARAM,
+ TSET_SETTING_2[0], TSET_SETTING_2[1]) < 0)
+ dsim_err("failed to send TSET_SETTING_2.\n");
+
+ if (lcd->mode == DECON_VIDEO_MODE) {
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned long) VIDEO_MODE_F2,
+ ARRAY_SIZE(VIDEO_MODE_F2)) < 0)
+ dsim_err("failed to send VIDEO_MODE_F2.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned long) MIPI_ILVL_E8,
+ ARRAY_SIZE(MIPI_ILVL_E8)) < 0)
+ dsim_err("failed to send MIPI_ILVL_E8.\n");
+ }
+
+ /* Test key disable */
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned long) TEST_KEY_OFF_F0,
+ ARRAY_SIZE(TEST_KEY_OFF_F0)) < 0)
+ dsim_err("failed to send TEST_KEY_OFF_F0.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned long) TEST_KEY_OFF_F1,
+ ARRAY_SIZE(TEST_KEY_OFF_F1)) < 0)
+ dsim_err("failed to send TEST_KEY_OFF_F1.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned long) TEST_KEY_OFF_FC,
+ ARRAY_SIZE(TEST_KEY_OFF_FC)) < 0)
+ dsim_err("failed to send TEST_KEY_OFF_FC.\n");
+
+ /* 120ms delay */
+// msleep(120);
+}
+
+void lcd_enable(int id)
+{
+ /* display on */
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE,
+ DISPLAY_ON[0], 0) < 0)
+ dsim_err("failed to send DISPLAY_ON.\n");
+}
+
+/* follow Panel Power off sequence */
+void lcd_disable(int id)
+{
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE,
+ DISPLAY_OFF[0],0) < 0)
+ dsim_err("fail to write DISPLAY_OFF .\n");
+
+ /* 10ms delay */
+ msleep(10);
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE,
+ SLEEP_IN[0], 0) < 0)
+ dsim_err("fail to write SLEEP_IN .\n");
+
+ /* 150ms delay */
+ msleep(150);
+}
+
+/* special function to change panel lane number 2 or 4 */
+void lcd_lane_ctl(int id, unsigned int lane_num)
+{
+ if (lane_num == 2) {
+ dsim_info("LANE_2.........\n");
+ /* Test Key Enable */
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned long) TEST_KEY_ON_F0,
+ ARRAY_SIZE(TEST_KEY_ON_F0)) < 0)
+ dsim_err("failed to send TEST_KEY_ON_F0.\n");
+ /* lane number change */
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE_PARAM,
+ LANE_2[0], LANE_2[1]) < 0)
+ dsim_err("failed to send LANE_2.\n");
+
+ /* Test Key Disable */
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned long) TEST_KEY_OFF_F0,
+ ARRAY_SIZE(TEST_KEY_OFF_F0)) < 0)
+ dsim_err("failed to send TEST_KEY_OFF_F0.\n");
+
+ } else if (lane_num == 4) {
+ /* Test Key Enable */
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned long) TEST_KEY_ON_F0,
+ ARRAY_SIZE(TEST_KEY_ON_F0)) < 0)
+ dsim_err("failed to send TEST_KEY_ON_F0.\n");
+ /* lane number change */
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE_PARAM,
+ LANE_4[0], LANE_4[1]) < 0)
+ dsim_err("failed to send LANE_4.\n");
+ /* Test Key Disable */
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned long) TEST_KEY_OFF_F0,
+ ARRAY_SIZE(TEST_KEY_OFF_F0)) < 0)
+ dsim_err("failed to send TEST_KEY_OFF_F0.\n");
+ }
+}
+
+int lcd_gamma_ctrl(int id, u32 backlightlevel)
+{
+ return 0;
+}
+
+int lcd_gamma_update(int id)
+{
+ return 0;
+}
--- /dev/null
+/* s6e3aa2_mipi_lcd.c
+ *
+ * Samsung SoC MIPI LCD driver.
+ *
+ * Copyright (c) 2015 Samsung Electronics
+ *
+ * SeungBeom, Park <sb1.parki@samsung.com>
+ *
+ * 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 <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/backlight.h>
+#include <video/mipi_display.h>
+#include <linux/platform_device.h>
+
+#include "../dsim.h"
+#include "lcd_ctrl.h"
+#include "decon_lcd.h"
+#include "s6e3aa2_param.h"
+
+#define MAX_BRIGHTNESS 255
+#define MIN_BRIGHTNESS 0
+#define DEFAULT_BRIGHTNESS 80
+
+static struct dsim_device *dsim_base;
+static struct backlight_device *bd;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static struct early_suspend s6e3aa2_early_suspend;
+#endif
+
+static int s6e3aa2_get_brightness(struct backlight_device *bd)
+{
+ return bd->props.brightness;
+}
+
+static int get_backlight_level(int brightness)
+{
+ int backlightlevel;
+
+ switch (brightness) {
+ case 0:
+ backlightlevel = 0;
+ break;
+ case 1 ... 29:
+ backlightlevel = 0;
+ break;
+ case 30 ... 34:
+ backlightlevel = 1;
+ break;
+ case 35 ... 39:
+ backlightlevel = 2;
+ break;
+ case 40 ... 44:
+ backlightlevel = 3;
+ break;
+ case 45 ... 49:
+ backlightlevel = 4;
+ break;
+ case 50 ... 54:
+ backlightlevel = 5;
+ break;
+ case 55 ... 64:
+ backlightlevel = 6;
+ break;
+ case 65 ... 74:
+ backlightlevel = 7;
+ break;
+ case 75 ... 83:
+ backlightlevel = 8;
+ break;
+ case 84 ... 93:
+ backlightlevel = 9;
+ break;
+ case 94 ... 103:
+ backlightlevel = 10;
+ break;
+ case 104 ... 113:
+ backlightlevel = 11;
+ break;
+ case 114 ... 122:
+ backlightlevel = 12;
+ break;
+ case 123 ... 132:
+ backlightlevel = 13;
+ break;
+ case 133 ... 142:
+ backlightlevel = 14;
+ break;
+ case 143 ... 152:
+ backlightlevel = 15;
+ break;
+ case 153 ... 162:
+ backlightlevel = 16;
+ break;
+ case 163 ... 171:
+ backlightlevel = 17;
+ break;
+ case 172 ... 181:
+ backlightlevel = 18;
+ break;
+ case 182 ... 191:
+ backlightlevel = 19;
+ break;
+ case 192 ... 201:
+ backlightlevel = 20;
+ break;
+ case 202 ... 210:
+ backlightlevel = 21;
+ break;
+ case 211 ... 220:
+ backlightlevel = 22;
+ break;
+ case 221 ... 230:
+ backlightlevel = 23;
+ break;
+ case 231 ... 240:
+ backlightlevel = 24;
+ break;
+ case 241 ... 250:
+ backlightlevel = 25;
+ break;
+ case 251 ... 255:
+ backlightlevel = 26;
+ break;
+ default:
+ backlightlevel = 12;
+ break;
+ }
+
+ return backlightlevel;
+}
+
+static int update_brightness(int brightness)
+{
+ int backlightlevel;
+
+ backlightlevel = get_backlight_level(brightness);
+ return 0;
+}
+
+static int s6e3aa2_set_brightness(struct backlight_device *bd)
+{
+ int brightness = bd->props.brightness;
+
+ if (brightness < MIN_BRIGHTNESS || brightness > MAX_BRIGHTNESS) {
+ pr_err("Brightness should be in the range of 0 ~ 255\n");
+ return -EINVAL;
+ }
+
+ update_brightness(brightness);
+
+ return 0;
+}
+
+static const struct backlight_ops s6e3aa2_backlight_ops = {
+ .get_brightness = s6e3aa2_get_brightness,
+ .update_status = s6e3aa2_set_brightness,
+};
+
+static int s6e3aa2_probe(struct dsim_device *dsim)
+{
+ const char *backlight_dev_name[2] = {
+ "panel1",
+ "panel1_1"
+ };
+
+ dsim_base = dsim;
+
+ bd = backlight_device_register(backlight_dev_name[dsim->id], NULL,
+ NULL, &s6e3aa2_backlight_ops, NULL);
+ if (IS_ERR(bd))
+ pr_err("failed to register backlight device!\n");
+
+ bd->props.max_brightness = MAX_BRIGHTNESS;
+ bd->props.brightness = DEFAULT_BRIGHTNESS;
+
+ return 0;
+}
+
+static int s6e3aa2_displayon(struct dsim_device *dsim)
+{
+ dsim_reg_set_cmd_transfer_mode(dsim->id, 1);
+ lcd_lane_ctl(dsim->id, 2);
+ dsim_reg_set_cmd_transfer_mode(dsim->id, 0);
+ lcd_init(dsim->id, &dsim->lcd_info);
+ lcd_enable(dsim->id);
+ return 0;
+}
+
+static int s6e3aa2_suspend(struct dsim_device *dsim)
+{
+ lcd_disable(dsim->id);
+ return 0;
+}
+
+static int s6e3aa2_resume(struct dsim_device *dsim)
+{
+ lcd_init(dsim->id, &dsim->lcd_info);
+ return 0;
+}
+
+static int s6e3aa2_dump(struct dsim_device *dsim)
+{
+ return 0;
+}
+
+struct dsim_lcd_driver s6e3aa2_mipi_lcd_driver = {
+ .probe = s6e3aa2_probe,
+ .displayon = s6e3aa2_displayon,
+ .suspend = s6e3aa2_suspend,
+ .resume = s6e3aa2_resume,
+ .dump = s6e3aa2_dump,
+};
--- /dev/null
+/* s6e3aa2_param.h
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * SeungBeom, Park <sb1.park@samsung.com>
+ *
+ * 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 __S6E3AA2_PARAM_H__
+#define __S6E3AA2_PARAM_H__
+
+static const unsigned char TEST_KEY_ON_0[] = {
+ 0xF0,
+ 0x5A, 0x5A,
+};
+
+static const unsigned char TEST_KEY_OFF_0[] = {
+ 0xF0,
+ 0xA5, 0xA5,
+};
+
+static const unsigned char TEST_KEY_ON_1[] = {
+ 0xF1,
+ 0x5A, 0x5A,
+};
+
+static const unsigned char TEST_KEY_OFF_1[] = {
+ 0xF1,
+ 0xA5, 0xA5,
+};
+
+static const unsigned char HIDDEN_KEY_ON[] = {
+ 0xFC,
+ 0x5A, 0x5A,
+};
+
+static const unsigned char HIDDEN_KEY_OFF[] = {
+ 0xFC,
+ 0xA5, 0xA5,
+};
+
+
+static const unsigned char LANE_2[] = {
+ 0xC4,
+ 0x02,
+};
+
+static const unsigned char LANE_4[] = {
+ 0xC4,
+ 0x04,
+};
+
+static const unsigned char SLEEP_OUT[] = {
+ 0x11,
+};
+
+static const unsigned char SLEEP_IN[] = {
+ 0x10,
+};
+
+static const unsigned char DISPLAY_ON[] = {
+ 0x29,
+};
+
+static const unsigned char DISPLAY_OFF[] = {
+ 0x28,
+};
+
+static const unsigned char TEST_KEY_ON_F0[] = {
+ 0xF0,
+ 0x5A, 0x5A
+};
+
+static const unsigned char TEST_KEY_OFF_F0[] = {
+ 0xF0,
+ 0xA5, 0xA5
+};
+
+static const unsigned char TEST_KEY_ON_F1[] = {
+ 0xF1,
+ 0x5A, 0x5A
+};
+
+static const unsigned char TEST_KEY_OFF_F1[] = {
+ 0xF1,
+ 0xA5, 0xA5
+};
+
+static const unsigned char TEST_KEY_ON_FC[] = {
+ 0xFC,
+ 0x5A, 0x5A
+};
+
+static const unsigned char TEST_KEY_OFF_FC[] = {
+ 0xFC,
+ 0xA5, 0xA5
+};
+
+static const unsigned char TE_ON[] = {
+ 0x35,
+ 0x00, 0x00,
+};
+
+static const unsigned char PCD_SET_DET_LOW[] = {
+ 0xCC,
+ 0x5C
+};
+
+static const unsigned char ERR_FG_SETTING[] = {
+ 0xED,
+ 0x44
+};
+
+static const unsigned char GAMMA_CONDITION_SET[] = {
+ 0xCA,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x00, 0x00
+};
+
+static const unsigned char AID_SETTING[] = {
+ 0xB1,
+ 0xFF, 0x20, 0x1A, 0x33, 0x5E, 0x8C, 0xB3, 0xD9, 0xFF, 0x60,
+ 0x0D
+};
+
+static const unsigned char ELVSS_SET[] = {
+ 0xB5,
+ 0xA0,
+ 0x1C, /* B5h 2nd Para: MPS_CON */
+ 0x44, /* B5h 3rd Para: ELVSS_Dim_offset */
+};
+
+static const unsigned char GAMMA_UPDATE[] = {
+ 0xF7,
+ 0x03
+};
+
+static const unsigned char HBM_OFF[] = {
+ 0x53,
+ 0x00
+};
+
+static const unsigned char OPR_ACL_OFF[] = {
+ 0xB4,
+ 0x40 /* 16 Frame Avg. at ACL Off */
+};
+
+static const unsigned char ACL_OFF[] = {
+ 0x55,
+ 0x00
+};
+
+static const unsigned char TSET_SETTING_1[] = {
+ 0xB0,
+ 0x1D
+};
+
+static const unsigned char TSET_SETTING_2[] = {
+ 0xB5,
+ 0x19
+};
+
+static const unsigned char VIDEO_MODE_F2[] = {
+ 0xF2,
+ 0x01, 0x0E, 0x39, 0x80, 0x5A, 0xA0, 0x0A, 0x0E, 0x00, 0x91, 0x20
+};
+
+static const unsigned char MIPI_ILVL_E8[] = {
+ 0xE8,
+ 0xA4, 0x08, 0x00 /* ILVL = 8 */
+};
+
+#endif /* __S6E3AA2_PARAM_H__ */
+
--- /dev/null
+/*
+ * drivers/video/decon/panels/s6e3ha2k_lcd_ctrl.c
+ *
+ * Samsung SoC MIPI LCD CONTROL functions
+ *
+ * Copyright (c) 2014 Samsung Electronics
+ *
+ * Jiun Yu, <jiun.yu@samsung.com>
+ *
+ * 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 "s6e3ha2k_param.h"
+#include "lcd_ctrl.h"
+
+/* use FW_TEST definition when you test CAL on firmware */
+/* #define FW_TEST */
+#ifdef FW_TEST
+#include "../dsim_fw.h"
+#include "mipi_display.h"
+#else
+#include "../dsim.h"
+#include <video/mipi_display.h>
+#endif
+
+/* Porch values. It depends on command or video mode */
+#define S6E3HA2K_CMD_VBP 15
+#define S6E3HA2K_CMD_VFP 1
+#define S6E3HA2K_CMD_VSA 1
+#define S6E3HA2K_CMD_HBP 1
+#define S6E3HA2K_CMD_HFP 1
+#define S6E3HA2K_CMD_HSA 1
+
+/* These need to define */
+#define S6E3HA2K_VIDEO_VBP 15
+#define S6E3HA2K_VIDEO_VFP 1
+#define S6E3HA2K_VIDEO_VSA 1
+#define S6E3HA2K_VIDEO_HBP 20
+#define S6E3HA2K_VIDEO_HFP 20
+#define S6E3HA2K_VIDEO_HSA 20
+
+#define S6E3HA2K_HORIZONTAL 1440
+#define S6E3HA2K_VERTICAL 2560
+
+#ifdef FW_TEST /* This information is moved to DT */
+#define CONFIG_FB_I80_COMMAND_MODE
+
+struct decon_lcd s6e3ha2k_lcd_info = {
+#ifdef CONFIG_FB_I80_COMMAND_MODE
+ .mode = DECON_MIPI_COMMAND_MODE,
+ .vfp = S6E3HA2K_CMD_VFP,
+ .vbp = S6E3HA2K_CMD_VBP,
+ .hfp = S6E3HA2K_CMD_HFP,
+ .hbp = S6E3HA2K_CMD_HBP,
+ .vsa = S6E3HA2K_CMD_VSA,
+ .hsa = S6E3HA2K_CMD_HSA,
+#else
+ .mode = DECON_VIDEO_MODE,
+ .vfp = S6E3HA2K_VIDEO_VFP,
+ .vbp = S6E3HA2K_VIDEO_VBP,
+ .hfp = S6E3HA2K_VIDEO_HFP,
+ .hbp = S6E3HA2K_VIDEO_HBP,
+ .vsa = S6E3HA2K_VIDEO_VSA,
+ .hsa = S6E3HA2K_VIDEO_HSA,
+#endif
+ .xres = S6E3HA2K_HORIZONTAL,
+ .yres = S6E3HA2K_VERTICAL,
+
+ /* Maybe, width and height will be removed */
+ .width = 70,
+ .height = 121,
+
+ /* Mhz */
+ .hs_clk = 1100,
+ .esc_clk = 20,
+
+ .fps = 60,
+ .mic_enabled = 1,
+ .mic_ver = MIC_VER_1_2,
+};
+#endif
+
+/*
+ * 3HA2K lcd init sequence
+ *
+ * Parameters
+ * - mic_enabled : if mic is enabled, MIC_ENABLE command must be sent
+ * - mode : LCD init sequence depends on command or video mode
+ */
+
+void lcd_init(int id, struct decon_lcd *lcd)
+{
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_TEST_KEY_ON_F0,
+ ARRAY_SIZE(SEQ_TEST_KEY_ON_F0)) < 0)
+ dsim_err("fail to write KEY_ON init command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_REG_F2,
+ ARRAY_SIZE(SEQ_REG_F2)) < 0)
+ dsim_err("fail to write F2 init command.\n");
+
+ if (lcd->mic_enabled)
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_REG_F9,
+ ARRAY_SIZE(SEQ_REG_F9)) < 0)
+ dsim_err("fail to write F9 init command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE, (unsigned long)SEQ_SLEEP_OUT[0], 0) < 0)
+ dsim_err("fail to write SLEEP_OUT init command.\n");
+
+ dsim_wait_for_cmd_completion(id);
+ msleep(10);
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_TEST_KEY_ON_F0,
+ ARRAY_SIZE(SEQ_TEST_KEY_ON_F0)) < 0)
+ dsim_err("fail to write KEY_ON init command.\n");
+
+ /* TE rising time change : 10 line earlier */
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_TE_START_SETTING,
+ ARRAY_SIZE(SEQ_TE_START_SETTING)) < 0)
+ dsim_err("fail to write TE_START_SETTING command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_REG_F2,
+ ARRAY_SIZE(SEQ_REG_F2)) < 0)
+ dsim_err("fail to write F2 init command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE, SEQ_TE_ON[0], 0) < 0)
+ dsim_err("fail to write TE_on init command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_TOUCH_HSYNC,
+ ARRAY_SIZE(SEQ_TOUCH_HSYNC)) < 0)
+ dsim_err("fail to write TOUCH_HSYNC init command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_PENTILE_CONTROL,
+ ARRAY_SIZE(SEQ_PENTILE_CONTROL)) < 0)
+ dsim_err("fail to write PENTILE_CONTROL init command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_COLUMN_ADDRESS,
+ ARRAY_SIZE(SEQ_COLUMN_ADDRESS)) < 0)
+ dsim_err("fail to write COLUMN_ADDRESS init command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_GAMMA_CONDITION_SET,
+ ARRAY_SIZE(SEQ_GAMMA_CONDITION_SET)) < 0)
+ dsim_err("fail to write GAMMA_CONDITION_SET init command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_AID_SET,
+ ARRAY_SIZE(SEQ_AID_SET)) < 0)
+ dsim_err("fail to write AID_SET init command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_ELVSS_SET,
+ ARRAY_SIZE(SEQ_ELVSS_SET)) < 0)
+ dsim_err("fail to write ELVSS_SET init command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_GAMMA_UPDATE,
+ ARRAY_SIZE(SEQ_GAMMA_UPDATE)) < 0)
+ dsim_err("fail to write GAMMA_UPDATE init command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_ACL_OFF,
+ ARRAY_SIZE(SEQ_ACL_OFF)) < 0)
+ dsim_err("fail to write ACL_OFF init command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_ACL_OPR,
+ ARRAY_SIZE(SEQ_ACL_OPR)) < 0)
+ dsim_err("fail to write ACL_OPR init command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_HBM_OFF,
+ ARRAY_SIZE(SEQ_HBM_OFF)) < 0)
+ dsim_err("fail to write HBM_OFF init command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_TSET_GLOBAL,
+ ARRAY_SIZE(SEQ_TSET_GLOBAL)) < 0)
+ dsim_err("fail to write TSET_GLOBAL init command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_TSET,
+ ARRAY_SIZE(SEQ_TSET)) < 0)
+ dsim_err("fail to write TSET init command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_TEST_KEY_OFF_F0,
+ ARRAY_SIZE(SEQ_TEST_KEY_OFF_F0)) < 0)
+ dsim_err("fail to write KEY_OFF init command.\n");
+
+ /* Added 120ms delay before SEQ_DISPLAY_ON */
+ dsim_wait_for_cmd_completion(id);
+ msleep(120);
+}
+
+void lcd_enable(int id)
+{
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE, (unsigned long)SEQ_DISPLAY_ON[0], 0) < 0)
+ dsim_err("fail to write DISPLAY_ON command.\n");
+}
+
+void lcd_disable(int id)
+{
+ /* This function needs to implement */
+}
+
+/*
+ * Set gamma values
+ *
+ * Parameter
+ * - backlightlevel : It is from 0 to 26.
+ */
+int lcd_gamma_ctrl(int id, u32 backlightlevel)
+{
+/* This will be implemented
+ int ret;
+ ret = dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (u32)gamma22_table[backlightlevel],
+ GAMMA_PARAM_SIZE);
+ if (ret) {
+ dsim_err("fail to write gamma value.\n");
+ return ret;
+ }
+*/
+ return 0;
+}
+
+int lcd_gamma_update(int id)
+{
+/* This will be implemented
+ int ret;
+ ret = dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (u32)SEQ_GAMMA_UPDATE,
+ ARRAY_SIZE(SEQ_GAMMA_UPDATE));
+ if (ret) {
+ dsim_err("fail to update gamma value.\n");
+ return ret;
+ }
+*/
+ return 0;
+}
+
+int dsim_write_by_panel(int id, const u8 *cmd, u32 cmdSize)
+{
+ int ret;
+
+ if (cmdSize == 1)
+ ret = dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE, cmd[0], 0);
+ else if (cmdSize == 2)
+ ret = dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE_PARAM, cmd[0], cmd[1]);
+ else
+ ret = dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)cmd, cmdSize);
+
+ return ret;
+}
+
+int dsim_read_from_panel(int id, u8 addr, u32 size, u8 *buf)
+{
+ int ret;
+
+ ret = dsim_rd_data(id, MIPI_DSI_DCS_READ, (u32)addr, size, buf);
+
+ return ret;
+}
+
+static int s6e3ha2_wqhd_dump(int dsim)
+{
+ int ret = 0;
+ unsigned char id[S6E3HA2_RD_LEN];
+ unsigned char rddpm[S6E3HA2_RD_LEN + 1];
+ unsigned char rddsm[S6E3HA2_RD_LEN + 1];
+ unsigned char err_buf[S6E3HA2_RD_LEN + 1];
+
+ dsim_info(" + %s\n", __func__);
+ ret = dsim_write_by_panel(dsim, SEQ_TEST_KEY_ON_F0, ARRAY_SIZE(SEQ_TEST_KEY_ON_F0));
+ if (ret < 0) {
+ dsim_err("%s : fail to write CMD : SEQ_TEST_KEY_ON_F0\n", __func__);
+ }
+
+ ret = dsim_write_by_panel(dsim, SEQ_TEST_KEY_ON_FC, ARRAY_SIZE(SEQ_TEST_KEY_ON_FC));
+ if (ret < 0) {
+ dsim_err("%s : fail to write CMD : SEQ_TEST_KEY_ON_FC\n", __func__);
+ }
+
+ ret = dsim_read_from_panel(dsim, 0xEA, S6E3HA2_RD_LEN, err_buf);
+ if (ret != S6E3HA2_RD_LEN) {
+ dsim_err("%s : can't read Panel's EA Reg\n",__func__);
+ goto dump_exit;
+ }
+
+ dsim_dbg("=== Panel's 0xEA Reg Value ===\n");
+ dsim_dbg("* 0xEA : buf[0] = %x\n", err_buf[0]);
+ dsim_dbg("* 0xEA : buf[1] = %x\n", err_buf[1]);
+
+ ret = dsim_read_from_panel(dsim, S6E3HA2_RDDPM_ADDR, S6E3HA2_RD_LEN, rddpm);
+ if (ret != S6E3HA2_RD_LEN) {
+ dsim_err("%s : can't read RDDPM Reg\n",__func__);
+ goto dump_exit;
+ }
+
+ dsim_info("=== Panel's RDDPM Reg Value : %x ===\n", rddpm[0]);
+
+ if (rddpm[0] & 0x80)
+ dsim_info("* Booster Voltage Status : ON\n");
+ else
+ dsim_info("* Booster Voltage Status : OFF\n");
+
+ if (rddpm[0] & 0x40)
+ dsim_info("* Idle Mode : On\n");
+ else
+ dsim_info("* Idle Mode : OFF\n");
+
+ if (rddpm[0] & 0x20)
+ dsim_info("* Partial Mode : On\n");
+ else
+ dsim_info("* Partial Mode : OFF\n");
+
+ if (rddpm[0] & 0x10)
+ dsim_info("* Sleep OUT and Working Ok\n");
+ else
+ dsim_info("* Sleep IN\n");
+
+ if (rddpm[0] & 0x08)
+ dsim_info("* Normal Mode On and Working Ok\n");
+ else
+ dsim_info("* Sleep IN\n");
+
+ if (rddpm[0] & 0x04)
+ dsim_info("* Display On and Working Ok\n");
+ else
+ dsim_info("* Display Off\n");
+
+ ret = dsim_read_from_panel(dsim, S6E3HA2_RDDSM_ADDR, S6E3HA2_RD_LEN, rddsm);
+ if (ret != S6E3HA2_RD_LEN) {
+ dsim_err("%s : can't read RDDSM Reg\n",__func__);
+ goto dump_exit;
+ }
+
+ dsim_info("=== Panel's RDDSM Reg Value : %x ===\n", rddsm[0]);
+
+ if (rddsm[0] & 0x80)
+ dsim_info("* TE On\n");
+ else
+ dsim_info("* TE OFF\n");
+
+ if (rddsm[0] & 0x02)
+ dsim_info("* S_DSI_ERR : Found\n");
+
+ if (rddsm[0] & 0x01)
+ dsim_info("* DSI_ERR : Found\n");
+
+ ret = dsim_read_from_panel(dsim, S6E3HA2_ID_REG, S6E3HA2_RD_LEN, id);
+ if (ret != S6E3HA2_RD_LEN) {
+ dsim_err("%s : can't read panel id\n",__func__);
+ goto dump_exit;
+ }
+
+ ret = dsim_write_by_panel(dsim, SEQ_TEST_KEY_OFF_FC, ARRAY_SIZE(SEQ_TEST_KEY_OFF_FC));
+ if (ret < 0) {
+ dsim_err("%s : fail to write CMD : SEQ_TEST_KEY_OFF_FC\n", __func__);
+ }
+
+ ret = dsim_write_by_panel(dsim, SEQ_TEST_KEY_OFF_F0, ARRAY_SIZE(SEQ_TEST_KEY_OFF_F0));
+ if (ret < 0) {
+ dsim_err("%s : fail to write CMD : SEQ_TEST_KEY_OFF_F0\n", __func__);
+ }
+dump_exit:
+ dsim_info(" - %s\n", __func__);
+ return ret;
+
+}
+
+int lcd_dump(int id)
+{
+ s6e3ha2_wqhd_dump(id);
+ return 0;
+}
--- /dev/null
+/* drivers/video/exynos/decon/panels/s6e3ha2k_mipi_lcd.c
+ *
+ * Samsung SoC MIPI LCD driver.
+ *
+ * Copyright (c) 2014 Samsung Electronics
+ *
+ * Haowei Li, <haowei.li@samsung.com>
+ *
+ * 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 <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/backlight.h>
+#include <video/mipi_display.h>
+#include <linux/platform_device.h>
+
+#include "../dsim.h"
+#include "lcd_ctrl.h"
+#include "decon_lcd.h"
+
+#define MAX_BRIGHTNESS 255
+#define MIN_BRIGHTNESS 0
+#define DEFAULT_BRIGHTNESS 0
+
+static struct dsim_device *dsim_base;
+static struct backlight_device *bd;
+
+static int s6e3ha2k_get_brightness(struct backlight_device *bd)
+{
+ return bd->props.brightness;
+}
+
+static int get_backlight_level(int brightness)
+{
+ int backlightlevel;
+
+ switch (brightness) {
+ case 0:
+ backlightlevel = 0;
+ break;
+ case 1 ... 29:
+ backlightlevel = 0;
+ break;
+ case 30 ... 34:
+ backlightlevel = 1;
+ break;
+ case 35 ... 39:
+ backlightlevel = 2;
+ break;
+ case 40 ... 44:
+ backlightlevel = 3;
+ break;
+ case 45 ... 49:
+ backlightlevel = 4;
+ break;
+ case 50 ... 54:
+ backlightlevel = 5;
+ break;
+ case 55 ... 64:
+ backlightlevel = 6;
+ break;
+ case 65 ... 74:
+ backlightlevel = 7;
+ break;
+ case 75 ... 83:
+ backlightlevel = 8;
+ break;
+ case 84 ... 93:
+ backlightlevel = 9;
+ break;
+ case 94 ... 103:
+ backlightlevel = 10;
+ break;
+ case 104 ... 113:
+ backlightlevel = 11;
+ break;
+ case 114 ... 122:
+ backlightlevel = 12;
+ break;
+ case 123 ... 132:
+ backlightlevel = 13;
+ break;
+ case 133 ... 142:
+ backlightlevel = 14;
+ break;
+ case 143 ... 152:
+ backlightlevel = 15;
+ break;
+ case 153 ... 162:
+ backlightlevel = 16;
+ break;
+ case 163 ... 171:
+ backlightlevel = 17;
+ break;
+ case 172 ... 181:
+ backlightlevel = 18;
+ break;
+ case 182 ... 191:
+ backlightlevel = 19;
+ break;
+ case 192 ... 201:
+ backlightlevel = 20;
+ break;
+ case 202 ... 210:
+ backlightlevel = 21;
+ break;
+ case 211 ... 220:
+ backlightlevel = 22;
+ break;
+ case 221 ... 230:
+ backlightlevel = 23;
+ break;
+ case 231 ... 240:
+ backlightlevel = 24;
+ break;
+ case 241 ... 250:
+ backlightlevel = 25;
+ break;
+ case 251 ... 255:
+ backlightlevel = 26;
+ break;
+ default:
+ backlightlevel = 12;
+ break;
+ }
+
+ return backlightlevel;
+}
+
+static int update_brightness(int brightness)
+{
+ int backlightlevel;
+
+ backlightlevel = get_backlight_level(brightness);
+ /* Need to implement
+ if (s5p_mipi_dsi_wr_data(dsim_base, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned int)gamma22_table[backlightlevel],
+ GAMMA_PARAM_SIZE) == -1)
+ printk(KERN_ERR "fail to write gamma value.\n");
+
+ if (s5p_mipi_dsi_wr_data(dsim_base, MIPI_DSI_DCS_SHORT_WRITE_PARAM,
+ (unsigned int)0xF7, (unsigned int)0x03) == -1)
+ printk(KERN_ERR "fail to update gamma value.\n");
+ */
+ return 0;
+}
+
+static int s6e3ha2k_set_brightness(struct backlight_device *bd)
+{
+ int brightness = bd->props.brightness;
+
+ if (brightness < MIN_BRIGHTNESS || brightness > MAX_BRIGHTNESS) {
+ pr_err("Brightness should be in the range of 0 ~ 255\n");
+ return -EINVAL;
+ }
+
+ update_brightness(brightness);
+
+ return 0;
+}
+
+static const struct backlight_ops s6e3ha2k_backlight_ops = {
+ .get_brightness = s6e3ha2k_get_brightness,
+ .update_status = s6e3ha2k_set_brightness,
+};
+
+static int s6e3ha2k_probe(struct dsim_device *dsim)
+{
+ const char *backlight_dev_name[2] = {
+ "panel1",
+ "panel1_1"
+ };
+
+ dsim_base = dsim;
+
+ bd = backlight_device_register(backlight_dev_name[dsim->id], NULL,
+ NULL, &s6e3ha2k_backlight_ops, NULL);
+ if (IS_ERR(bd))
+ pr_err("failed to register backlight device!\n");
+
+ bd->props.max_brightness = MAX_BRIGHTNESS;
+ bd->props.brightness = DEFAULT_BRIGHTNESS;
+
+ return 0;
+}
+
+static int s6e3ha2k_displayon(struct dsim_device *dsim)
+{
+ lcd_init(dsim->id, &dsim->lcd_info);
+ lcd_enable(dsim->id);
+ return 1;
+}
+
+static int s6e3ha2k_suspend(struct dsim_device *dsim)
+{
+ return 0;
+}
+
+static int s6e3ha2k_resume(struct dsim_device *dsim)
+{
+ return 0;
+}
+
+static int s6e3ha2k_dump(struct dsim_device *dsim)
+{
+ lcd_dump(dsim->id);
+ return 0;
+}
+
+struct dsim_lcd_driver s6e3ha2k_mipi_lcd_driver = {
+ .probe = s6e3ha2k_probe,
+ .displayon = s6e3ha2k_displayon,
+ .suspend = s6e3ha2k_suspend,
+ .resume = s6e3ha2k_resume,
+ .dump = s6e3ha2k_dump,
+};
--- /dev/null
+/* linux/drivers/video/decon_display/s6e3fa0_param.h
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Jiun Yu <jiun.yu@samsung.com>
+ *
+ * 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 __S6E3HA0K_PARAM_H__
+#define __S6E3HA0K_PARAM_H__
+
+#define S6E3HA2_ID_REG 0x04
+#define S6E3HA2_RD_LEN 3
+#define S6E3HA2_RDDPM_ADDR 0x0A
+#define S6E3HA2_RDDSM_ADDR 0x0E
+
+/* MIPI commands list */
+static const unsigned char SEQ_TEST_KEY_ON_F0[] = {
+ 0xF0,
+ 0x5A, 0x5A
+};
+
+static const unsigned char SEQ_TEST_KEY_ON_FC[] = {
+ 0xFC,
+ 0x5A, 0x5A
+};
+
+static const unsigned char SEQ_TEST_KEY_OFF_FC[] = {
+ 0xFC,
+ 0xA5, 0xA5
+};
+
+static const unsigned char SEQ_SLEEP_OUT[] = {
+ 0x11,
+};
+
+static const unsigned char SEQ_REG_F2[] = {
+ 0xF2,
+ 0x67, 0x41, 0xC3, 0x06, 0x0A
+};
+
+static const unsigned char SEQ_TE_START_SETTING[] = {
+ 0xB9,
+ 0x10, 0x09, 0xFF, 0x00, 0x09
+};
+
+static const unsigned char SEQ_REG_F9[] = {
+ 0xF9,
+ 0x29
+};
+
+static const unsigned char SEQ_TOUCH_HSYNC[] = {
+ 0xBD,
+ 0x30, 0x22, 0x02, 0x16, 0x02, 0x16
+};
+
+static const unsigned char SEQ_PENTILE_CONTROL[] = {
+ 0xC0,
+ 0x30, 0x00, 0xD8, 0xD8
+};
+
+static const unsigned char SEQ_COLUMN_ADDRESS[] = {
+ 0x2A,
+ 0x00, 0x00, 0x05, 0x9F
+};
+
+static const unsigned char SEQ_GAMMA_CONDITION_SET[] = {
+ 0xCA,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x00, 0x00
+};
+
+static const unsigned char SEQ_AID_SET[] = {
+ 0xB2,
+ 0x03, 0x10
+};
+
+static const unsigned char SEQ_ELVSS_SET[] = {
+ 0xB6,
+ 0x9C, 0x0A
+};
+
+static const unsigned char SEQ_GAMMA_UPDATE[] = {
+ 0xF7,
+ 0x03
+};
+
+static const unsigned char SEQ_ACL_OFF[] = {
+ 0x55,
+ 0x00
+};
+
+static const unsigned char SEQ_ACL_OPR[] = {
+ 0xB5,
+ 0x40
+};
+
+static const unsigned char SEQ_HBM_OFF[] = {
+ 0xB4,
+ 0x04
+};
+
+static const unsigned char SEQ_TSET_GLOBAL[] = {
+ 0xB0,
+ 0x07
+};
+
+static const unsigned char SEQ_TSET[] = {
+ 0xB8,
+ 0x19
+};
+
+static const unsigned char SEQ_TEST_KEY_OFF_F0[] = {
+ 0xF0,
+ 0xA5, 0xA5
+};
+
+static const unsigned char SEQ_TEST_KEY_OFF_F1[] = {
+ 0xF1, 0xA5, 0xA5
+};
+
+static const unsigned char SEQ_DISPLAY_ON[] = {
+ 0x29,
+};
+
+static const unsigned char SEQ_TE_ON[] = {
+ 0x35,
+ 0x00
+};
+
+static const unsigned char SEQ_ESD_FG[] = {
+ 0xED,
+ 0x01, 0x04
+};
+
+static const unsigned char SEQ_ALLPOFF[] = {
+ 0x22
+};
+
+static const unsigned char SEQ_ALLPON[] = {
+ 0x23
+};
+
+static const unsigned char SEQ_NOP[] = {
+ 0x00,
+};
+
+static const unsigned char SEQ_DISPLAY_OFF[] = {
+ 0x28
+};
+
+#endif /* __S6E3HA0K_PARAM_H__ */
--- /dev/null
+/*
+ * drivers/video/decon/panels/s6e3ha6_lcd_ctrl.c
+ *
+ * Samsung SoC MIPI LCD CONTROL functions
+ *
+ * Copyright (c) 2014 Samsung Electronics
+ *
+ * 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 <video/mipi_display.h>
+#include "s6e3ha6_param.h"
+#include "lcd_ctrl.h"
+#include "../dsim.h"
+
+/* Porch values. It depends on command or video mode */
+#define S6E3HA6_CMD_VBP 15
+#define S6E3HA6_CMD_VFP 1
+#define S6E3HA6_CMD_VSA 1
+#define S6E3HA6_CMD_HBP 1
+#define S6E3HA6_CMD_HFP 1
+#define S6E3HA6_CMD_HSA 1
+
+/* These need to define */
+#define S6E3HA6_VIDEO_VBP 15
+#define S6E3HA6_VIDEO_VFP 1
+#define S6E3HA6_VIDEO_VSA 1
+#define S6E3HA6_VIDEO_HBP 20
+#define S6E3HA6_VIDEO_HFP 20
+#define S6E3HA6_VIDEO_HSA 20
+
+#define S6E3HA6_HORIZONTAL 1440
+#define S6E3HA6_VERTICAL 2960
+
+#ifdef FW_TEST /* This information is moved to DT */
+#define CONFIG_FB_I80_COMMAND_MODE
+
+struct decon_lcd s6e3ha6_lcd_info = {
+#ifdef CONFIG_FB_I80_COMMAND_MODE
+ .mode = DECON_MIPI_COMMAND_MODE,
+ .vfp = S6E3HA6_CMD_VFP,
+ .vbp = S6E3HA6_CMD_VBP,
+ .hfp = S6E3HA6_CMD_HFP,
+ .hbp = S6E3HA6_CMD_HBP,
+ .vsa = S6E3HA6_CMD_VSA,
+ .hsa = S6E3HA6_CMD_HSA,
+#else
+ .mode = DECON_VIDEO_MODE,
+ .vfp = S6E3HA6_VIDEO_VFP,
+ .vbp = S6E3HA6_VIDEO_VBP,
+ .hfp = S6E3HA6_VIDEO_HFP,
+ .hbp = S6E3HA6_VIDEO_HBP,
+ .vsa = S6E3HA6_VIDEO_VSA,
+ .hsa = S6E3HA6_VIDEO_HSA,
+#endif
+ .xres = S6E3HA6_HORIZONTAL,
+ .yres = S6E3HA6_VERTICAL,
+
+ /* Maybe, width and height will be removed */
+ .width = 70,
+ .height = 121,
+
+ /* Mhz */
+ .hs_clk = 1100,
+ .esc_clk = 20,
+
+ .fps = 60,
+ .mic_enabled = 1,
+ .mic_ver = MIC_VER_1_2,
+};
+#endif
+
+/*
+ * 3HA6 lcd init sequence
+ *
+ * Parameters
+ * - mic_enabled : if mic is enabled, MIC_ENABLE command must be sent
+ * - mode : LCD init sequence depends on command or video mode
+ */
+
+void lcd_init(int id, struct decon_lcd *lcd)
+{
+ dsim_dbg("%s +\n", __func__);
+
+ msleep(5);
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_TEST_KEY_ON_F0,
+ ARRAY_SIZE(SEQ_TEST_KEY_ON_F0)) < 0)
+ dsim_err("fail to write SEQ_TEST_KEY_ON_F0 command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_TEST_KEY_ON_FC,
+ ARRAY_SIZE(SEQ_TEST_KEY_ON_FC)) < 0)
+ dsim_err("fail to write SEQ_TEST_KEY_ON_FC command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DSC_PRA, (unsigned long)SEQ_DSC_EN[0], 0) < 0)
+ dsim_err("fail to write SEQ_DSC_EN command.\n");
+
+ switch (lcd->dsc_slice_num) {
+ case 2:
+ if (dsim_wr_data(id, MIPI_DSI_DSC_PPS, (unsigned long)SEQ_PPS_SLICE2,
+ ARRAY_SIZE(SEQ_PPS_SLICE2)) < 0)
+ dsim_err("fail to write SEQ_PPS_SLICE2 command.\n");
+ break;
+ default:
+ dsim_err("fail to set MIPI_DSI_DSC_PPS command(no slice).\n");
+ break;
+ }
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE,
+ (unsigned long)SEQ_SLEEP_OUT[0], 0) < 0)
+ dsim_err("fail to send SEQ_SLEEP_OUT command.\n");
+
+ msleep(120);
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_TSP_HSYNC,
+ ARRAY_SIZE(SEQ_TSP_HSYNC)) < 0)
+ dsim_err("fail to write SEQ_TSP_HSYNC command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_SET_AREA,
+ ARRAY_SIZE(SEQ_SET_AREA)) < 0)
+ dsim_err("fail to write SEQ_SET_AREA command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE,
+ (unsigned long)SEQ_TE_ON[0], 0) < 0)
+ dsim_err("fail to send SEQ_TE_ON command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE_PARAM,
+ (unsigned long)SEQ_ERR_FG[0], (u32)SEQ_ERR_FG[1]) < 0)
+ dsim_err("fail to send SEQ_ERR_FG command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_TE_START_SETTING,
+ ARRAY_SIZE(SEQ_TE_START_SETTING)) < 0)
+ dsim_err("fail to write SEQ_TE_START_SETTING command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_FFC,
+ ARRAY_SIZE(SEQ_FFC)) < 0)
+ dsim_err("fail to write SEQ_FFC command.\n");
+
+ dsim_dbg("%s -\n", __func__);
+}
+
+void lcd_enable(int id)
+{
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE,
+ (unsigned long)SEQ_DISPLAY_ON[0], 0) < 0)
+ dsim_err("fail to send SEQ_DISPLAY_ON command.\n");
+}
+
+void lcd_disable(int id)
+{
+ /* This function needs to implement */
+}
+
+/*
+ * Set gamma values
+ *
+ * Parameter
+ * - backlightlevel : It is from 0 to 26.
+ */
+int lcd_gamma_ctrl(int id, u32 backlightlevel)
+{
+/* This will be implemented
+ int ret;
+ ret = dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (u32)gamma22_table[backlightlevel],
+ GAMMA_PARAM_SIZE);
+ if (ret) {
+ dsim_err("fail to write gamma value.\n");
+ return ret;
+ }
+*/
+ return 0;
+}
+
+int lcd_gamma_update(int id)
+{
+/* This will be implemented
+ int ret;
+ ret = dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (u32)SEQ_GAMMA_UPDATE,
+ ARRAY_SIZE(SEQ_GAMMA_UPDATE));
+ if (ret) {
+ dsim_err("fail to update gamma value.\n");
+ return ret;
+ }
+*/
+ return 0;
+}
+
+int dsim_write_by_panel(int id, const u8 *cmd, u32 cmdSize)
+{
+ int ret;
+
+ if (cmdSize == 1)
+ ret = dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE, cmd[0], 0);
+ else if (cmdSize == 2)
+ ret = dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE_PARAM, cmd[0], cmd[1]);
+ else
+ ret = dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)cmd, cmdSize);
+
+ return ret;
+}
+
+int dsim_read_from_panel(int id, u8 addr, u32 size, u8 *buf)
+{
+ int ret;
+
+ ret = dsim_rd_data(id, MIPI_DSI_DCS_READ, (u32)addr, size, buf);
+
+ return ret;
+}
+
+static int s6e3ha6_wqhd_dump(int dsim)
+{
+ int ret = 0;
+ dsim_info(" + %s\n", __func__);
+ dsim_info(" - %s\n", __func__);
+ return ret;
+
+}
+
+int lcd_dump(int id)
+{
+ s6e3ha6_wqhd_dump(id);
+ return 0;
+}
--- /dev/null
+/* drivers/video/exynos/decon/panels/s6e3ha6_mipi_lcd.c
+ *
+ * Samsung SoC MIPI LCD driver.
+ *
+ * Copyright (c) 2014 Samsung Electronics
+ *
+ * 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 <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/backlight.h>
+#include <video/mipi_display.h>
+#include <linux/platform_device.h>
+
+#include "../dsim.h"
+#include "lcd_ctrl.h"
+#include "decon_lcd.h"
+
+#define MAX_BRIGHTNESS 255
+#define MIN_BRIGHTNESS 0
+#define DEFAULT_BRIGHTNESS 0
+
+static struct dsim_device *dsim_base;
+static struct backlight_device *bd;
+
+static int s6e3ha6_get_brightness(struct backlight_device *bd)
+{
+ return bd->props.brightness;
+}
+
+static int get_backlight_level(int brightness)
+{
+ int backlightlevel;
+
+ switch (brightness) {
+ case 0:
+ backlightlevel = 0;
+ break;
+ case 1 ... 29:
+ backlightlevel = 0;
+ break;
+ case 30 ... 34:
+ backlightlevel = 1;
+ break;
+ case 35 ... 39:
+ backlightlevel = 2;
+ break;
+ case 40 ... 44:
+ backlightlevel = 3;
+ break;
+ case 45 ... 49:
+ backlightlevel = 4;
+ break;
+ case 50 ... 54:
+ backlightlevel = 5;
+ break;
+ case 55 ... 64:
+ backlightlevel = 6;
+ break;
+ case 65 ... 74:
+ backlightlevel = 7;
+ break;
+ case 75 ... 83:
+ backlightlevel = 8;
+ break;
+ case 84 ... 93:
+ backlightlevel = 9;
+ break;
+ case 94 ... 103:
+ backlightlevel = 10;
+ break;
+ case 104 ... 113:
+ backlightlevel = 11;
+ break;
+ case 114 ... 122:
+ backlightlevel = 12;
+ break;
+ case 123 ... 132:
+ backlightlevel = 13;
+ break;
+ case 133 ... 142:
+ backlightlevel = 14;
+ break;
+ case 143 ... 152:
+ backlightlevel = 15;
+ break;
+ case 153 ... 162:
+ backlightlevel = 16;
+ break;
+ case 163 ... 171:
+ backlightlevel = 17;
+ break;
+ case 172 ... 181:
+ backlightlevel = 18;
+ break;
+ case 182 ... 191:
+ backlightlevel = 19;
+ break;
+ case 192 ... 201:
+ backlightlevel = 20;
+ break;
+ case 202 ... 210:
+ backlightlevel = 21;
+ break;
+ case 211 ... 220:
+ backlightlevel = 22;
+ break;
+ case 221 ... 230:
+ backlightlevel = 23;
+ break;
+ case 231 ... 240:
+ backlightlevel = 24;
+ break;
+ case 241 ... 250:
+ backlightlevel = 25;
+ break;
+ case 251 ... 255:
+ backlightlevel = 26;
+ break;
+ default:
+ backlightlevel = 12;
+ break;
+ }
+
+ return backlightlevel;
+}
+
+static int update_brightness(int brightness)
+{
+ int backlightlevel;
+
+ backlightlevel = get_backlight_level(brightness);
+ /* Need to implement
+ if (s5p_mipi_dsi_wr_data(dsim_base, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned int)gamma22_table[backlightlevel],
+ GAMMA_PARAM_SIZE) == -1)
+ printk(KERN_ERR "fail to write gamma value.\n");
+
+ if (s5p_mipi_dsi_wr_data(dsim_base, MIPI_DSI_DCS_SHORT_WRITE_PARAM,
+ (unsigned int)0xF7, (unsigned int)0x03) == -1)
+ printk(KERN_ERR "fail to update gamma value.\n");
+ */
+ return 0;
+}
+
+static int s6e3ha6_set_brightness(struct backlight_device *bd)
+{
+ int brightness = bd->props.brightness;
+
+ if (brightness < MIN_BRIGHTNESS || brightness > MAX_BRIGHTNESS) {
+ pr_err("Brightness should be in the range of 0 ~ 255\n");
+ return -EINVAL;
+ }
+
+ update_brightness(brightness);
+
+ return 0;
+}
+
+static const struct backlight_ops s6e3ha6_backlight_ops = {
+ .get_brightness = s6e3ha6_get_brightness,
+ .update_status = s6e3ha6_set_brightness,
+};
+
+static int s6e3ha6_probe(struct dsim_device *dsim)
+{
+ const char *backlight_dev_name[2] = {
+ "panel1",
+ "panel1_1"
+ };
+
+ dsim_base = dsim;
+
+ bd = backlight_device_register(backlight_dev_name[dsim->id], NULL,
+ NULL, &s6e3ha6_backlight_ops, NULL);
+ if (IS_ERR(bd))
+ pr_err("failed to register backlight device!\n");
+
+ bd->props.max_brightness = MAX_BRIGHTNESS;
+ bd->props.brightness = DEFAULT_BRIGHTNESS;
+
+ return 0;
+}
+
+static int s6e3ha6_displayon(struct dsim_device *dsim)
+{
+ lcd_init(dsim->id, &dsim->lcd_info);
+ lcd_enable(dsim->id);
+ return 1;
+}
+
+static int s6e3ha6_suspend(struct dsim_device *dsim)
+{
+ return 0;
+}
+
+static int s6e3ha6_resume(struct dsim_device *dsim)
+{
+ return 0;
+}
+
+static int s6e3ha6_dump(struct dsim_device *dsim)
+{
+ lcd_dump(dsim->id);
+ return 0;
+}
+
+struct dsim_lcd_driver s6e3ha6_mipi_lcd_driver = {
+ .probe = s6e3ha6_probe,
+ .displayon = s6e3ha6_displayon,
+ .suspend = s6e3ha6_suspend,
+ .resume = s6e3ha6_resume,
+ .dump = s6e3ha6_dump,
+};
--- /dev/null
+/* linux/drivers/video/decon_display/s6e3fa0_param.h
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * 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 __S6E3HA6_PARAM_H__
+#define __S6E3HA6_PARAM_H__
+
+#define S6E3HA6_ID_REG 0x04
+#define S6E3HA6_RD_LEN 3
+#define S6E3HA6_RDDPM_ADDR 0x0A
+#define S6E3HA6_RDDSM_ADDR 0x0E
+
+/* MCD */
+static const unsigned char SEQ_TEST_KEY_ON_F0[] = {
+ 0xf0, 0x5a, 0x5a
+};
+
+static const unsigned char SEQ_TEST_KEY_ON_FC[] = {
+ 0xfc, 0x5a, 0x5a
+};
+
+static const unsigned char SEQ_DSC_EN[] = {
+ 0x01
+};
+
+static const unsigned char SEQ_PPS_SLICE2[] = {
+ // QHD :2960x1440
+ 0x11, 0x00, 0x00, 0x89, 0x30, 0x80, 0x0B, 0x90,
+ 0x05, 0xA0, 0x00, 0x28, 0x02, 0xD0, 0x02, 0xD0,
+ 0x02, 0x00, 0x02, 0x68, 0x00, 0x20, 0x04, 0x6C,
+ 0x00, 0x0A, 0x00, 0x0C, 0x02, 0x77, 0x01, 0xE9,
+ 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const unsigned char SEQ_SLEEP_OUT[] = {
+ 0x11
+};
+
+static const unsigned char SEQ_TSP_HSYNC[] = {
+ 0xB9,
+ 0x00, 0x00, 0x14, 0x00, 0x18, 0x11, 0x03
+};
+
+static const unsigned char SEQ_SET_AREA[] = {
+ 0x1A, 0x1F, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const unsigned char SEQ_TE_ON[] = {
+ 0x35
+};
+
+static const unsigned char SEQ_ERR_FG[] = {
+ 0xED, 0x44
+};
+
+static const unsigned char SEQ_TE_START_SETTING[] = {
+ 0xB9, 0x00, 0x0B, 0x8F, 0x00, 0x09
+};
+
+static const unsigned char SEQ_FFC[] = {
+ 0xCE,
+ 0x0D, 0x58, 0x14, 0x64, 0x38, 0xB8, 0xF2, 0x03,
+ 0x00, 0xFF, 0x02, 0x0A, 0x0A, 0x0A, 0x0A, 0x0F,
+ 0x23,
+};
+
+static const unsigned char SEQ_TEST_KEY_OFF_FC[] = {
+ 0xFC, 0xA5, 0xA5
+};
+
+static const unsigned char SEQ_TEST_KEY_OFF_F0[] = {
+ 0xF0, 0xA5, 0xA5
+};
+
+static const unsigned char SEQ_TEST_KEY_OFF_F1[] = {
+ 0xF1, 0xA5, 0xA5
+};
+
+static const unsigned char SEQ_DISPLAY_ON[] = {
+ 0x29
+};
+
+static const unsigned char SEQ_DISPLAY_OFF[] = {
+ 0x28
+};
+
+static const unsigned char SEQ_TSET_GLOBAL[] = {
+ 0xB0,
+ 0x01
+};
+
+static const unsigned char SEQ_ESD_FG[] = {
+ 0xED,
+ 0x01, 0x04
+};
+
+static const unsigned char SEQ_ALLPOFF[] = {
+ 0x22
+};
+
+static const unsigned char SEQ_ALLPON[] = {
+ 0x23
+};
+
+static const unsigned char SEQ_NOP[] = {
+ 0x00,
+};
+
+#endif /* __S6E3HA6_PARAM_H__ */
--- /dev/null
+/*
+ * drivers/video/decon/panels/s6e3ha8_lcd_ctrl.c
+ *
+ * Samsung SoC MIPI LCD CONTROL functions
+ *
+ * Copyright (c) 2014 Samsung Electronics
+ *
+ * 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 <video/mipi_display.h>
+#include "s6e3ha8_param.h"
+#include "lcd_ctrl.h"
+#include "../dsim.h"
+
+/* Porch values. It depends on command or video mode */
+#define S6E3HA8_CMD_VBP 15
+#define S6E3HA8_CMD_VFP 1
+#define S6E3HA8_CMD_VSA 1
+#define S6E3HA8_CMD_HBP 1
+#define S6E3HA8_CMD_HFP 1
+#define S6E3HA8_CMD_HSA 1
+
+/* These need to define */
+#define S6E3HA8_VIDEO_VBP 15
+#define S6E3HA8_VIDEO_VFP 1
+#define S6E3HA8_VIDEO_VSA 1
+#define S6E3HA8_VIDEO_HBP 20
+#define S6E3HA8_VIDEO_HFP 20
+#define S6E3HA8_VIDEO_HSA 20
+
+#define S6E3HA8_HORIZONTAL 1440
+#define S6E3HA8_VERTICAL 2960
+
+#ifdef FW_TEST /* This information is moved to DT */
+#define CONFIG_FB_I80_COMMAND_MODE
+
+struct decon_lcd s6e3ha8_lcd_info = {
+#ifdef CONFIG_FB_I80_COMMAND_MODE
+ .mode = DECON_MIPI_COMMAND_MODE,
+ .vfp = S6E3HA8_CMD_VFP,
+ .vbp = S6E3HA8_CMD_VBP,
+ .hfp = S6E3HA8_CMD_HFP,
+ .hbp = S6E3HA8_CMD_HBP,
+ .vsa = S6E3HA8_CMD_VSA,
+ .hsa = S6E3HA8_CMD_HSA,
+#else
+ .mode = DECON_VIDEO_MODE,
+ .vfp = S6E3HA8_VIDEO_VFP,
+ .vbp = S6E3HA8_VIDEO_VBP,
+ .hfp = S6E3HA8_VIDEO_HFP,
+ .hbp = S6E3HA8_VIDEO_HBP,
+ .vsa = S6E3HA8_VIDEO_VSA,
+ .hsa = S6E3HA8_VIDEO_HSA,
+#endif
+ .xres = S6E3HA8_HORIZONTAL,
+ .yres = S6E3HA8_VERTICAL,
+
+ /* Maybe, width and height will be removed */
+ .width = 70,
+ .height = 121,
+
+ /* Mhz */
+ .hs_clk = 1100,
+ .esc_clk = 20,
+
+ .fps = 60,
+ .mic_enabled = 1,
+ .mic_ver = MIC_VER_1_2,
+};
+#endif
+
+/*
+ * 3HA8 lcd init sequence
+ *
+ * Parameters
+ * - mic_enabled : if mic is enabled, MIC_ENABLE command must be sent
+ * - mode : LCD init sequence depends on command or video mode
+ */
+
+void lcd_init(int id, struct decon_lcd *lcd)
+{
+ dsim_dbg("%s +\n", __func__);
+
+ msleep(5);
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_TEST_KEY_ON_F0,
+ ARRAY_SIZE(SEQ_TEST_KEY_ON_F0)) < 0)
+ dsim_err("fail to write SEQ_TEST_KEY_ON_F0 command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_TEST_KEY_ON_FC,
+ ARRAY_SIZE(SEQ_TEST_KEY_ON_FC)) < 0)
+ dsim_err("fail to write SEQ_TEST_KEY_ON_FC command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DSC_PRA, (unsigned long)SEQ_DSC_EN[0], 0) < 0)
+ dsim_err("fail to write SEQ_DSC_EN command.\n");
+
+ switch (lcd->dsc_slice_num) {
+ case 2:
+ if (dsim_wr_data(id, MIPI_DSI_DSC_PPS, (unsigned long)SEQ_PPS_SLICE2,
+ ARRAY_SIZE(SEQ_PPS_SLICE2)) < 0)
+ dsim_err("fail to write SEQ_PPS_SLICE2 command.\n");
+ break;
+ default:
+ dsim_err("fail to set MIPI_DSI_DSC_PPS command(no slice).\n");
+ break;
+ }
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE,
+ (unsigned long)SEQ_SLEEP_OUT[0], 0) < 0)
+ dsim_err("fail to send SEQ_SLEEP_OUT command.\n");
+
+ msleep(120);
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_TSP_HSYNC,
+ ARRAY_SIZE(SEQ_TSP_HSYNC)) < 0)
+ dsim_err("fail to write SEQ_TSP_HSYNC command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_SET_AREA,
+ ARRAY_SIZE(SEQ_SET_AREA)) < 0)
+ dsim_err("fail to write SEQ_SET_AREA command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE,
+ (unsigned long)SEQ_TE_ON[0], 0) < 0)
+ dsim_err("fail to send SEQ_TE_ON command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE_PARAM,
+ (unsigned long)SEQ_ERR_FG[0], (u32)SEQ_ERR_FG[1]) < 0)
+ dsim_err("fail to send SEQ_ERR_FG command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_TE_START_SETTING,
+ ARRAY_SIZE(SEQ_TE_START_SETTING)) < 0)
+ dsim_err("fail to write SEQ_TE_START_SETTING command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_FFC,
+ ARRAY_SIZE(SEQ_FFC)) < 0)
+ dsim_err("fail to write SEQ_FFC command.\n");
+
+ dsim_dbg("%s -\n", __func__);
+}
+
+void lcd_enable(int id)
+{
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE,
+ (unsigned long)SEQ_DISPLAY_ON[0], 0) < 0)
+ dsim_err("fail to send SEQ_DISPLAY_ON command.\n");
+}
+
+void lcd_disable(int id)
+{
+ /* This function needs to implement */
+}
+
+/*
+ * Set gamma values
+ *
+ * Parameter
+ * - backlightlevel : It is from 0 to 26.
+ */
+int lcd_gamma_ctrl(int id, u32 backlightlevel)
+{
+ /* This will be implemented */
+ return 0;
+}
+
+int lcd_gamma_update(int id)
+{
+ /* This will be implemented */
+ return 0;
+}
+
+int dsim_write_by_panel(int id, const u8 *cmd, u32 cmdSize)
+{
+ int ret;
+
+ if (cmdSize == 1)
+ ret = dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE, cmd[0], 0);
+ else if (cmdSize == 2)
+ ret = dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE_PARAM, cmd[0], cmd[1]);
+ else
+ ret = dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)cmd, cmdSize);
+
+ return ret;
+}
+
+int dsim_read_from_panel(int id, u8 addr, u32 size, u8 *buf)
+{
+ int ret;
+
+ ret = dsim_rd_data(id, MIPI_DSI_DCS_READ, (u32)addr, size, buf);
+
+ return ret;
+}
+
+static int s6e3ha8_wqhd_dump(int dsim)
+{
+ int ret = 0;
+
+ dsim_info(" + %s\n", __func__);
+ dsim_info(" - %s\n", __func__);
+ return ret;
+
+}
+
+int lcd_dump(int id)
+{
+ s6e3ha8_wqhd_dump(id);
+ return 0;
+}
+
+void lcd_mres(int id, int mres_idx, int dsc_en)
+{
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)KEY1_ENABLE,
+ ARRAY_SIZE(KEY1_ENABLE)) < 0) {
+ dsim_err("failed to write KEY1_ENABLE\n");
+ return;
+ }
+ if (dsc_en) {
+ if (dsim_wr_data(id, MIPI_DSI_DSC_PRA,
+ (unsigned long)DSC_EN[1][0], 0) < 0) {
+ dsim_err("failed to write DSC_EN\n");
+ return;
+ }
+ if (dsim_wr_data(id, MIPI_DSI_DSC_PPS,
+ (unsigned long)PPS_TABLE[mres_idx],
+ ARRAY_SIZE(PPS_TABLE[mres_idx])) < 0) {
+ dsim_err("failed to write PPS_TABLE[%d]\n", mres_idx);
+ return;
+ }
+ } else {
+ if (dsim_wr_data(id, MIPI_DSI_DSC_PRA,
+ (unsigned long)DSC_EN[0][0], 0) < 0) {
+ dsim_err("failed to write DSC_EN\n");
+ return;
+ }
+ }
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)KEY1_DISABLE,
+ ARRAY_SIZE(KEY1_DISABLE)) < 0) {
+ dsim_err("failed to write KEY1_DISABLE\n");
+ return;
+ }
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned long)CASET_TABLE[mres_idx],
+ ARRAY_SIZE(CASET_TABLE[mres_idx])) < 0) {
+ dsim_err("failed to write CASET_TABLE[%d]\n", mres_idx);
+ return;
+ }
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned long)PASET_TABLE[mres_idx],
+ ARRAY_SIZE(PASET_TABLE[mres_idx])) < 0) {
+ dsim_err("failed to write PASET_TABLE[%d]\n", mres_idx);
+ return;
+ }
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)KEY2_ENABLE,
+ ARRAY_SIZE(KEY2_ENABLE)) < 0) {
+ dsim_err("failed to write KEY2_ENABLE\n");
+ return;
+ }
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned long)SCALER_TABLE[mres_idx],
+ ARRAY_SIZE(SCALER_TABLE[mres_idx])) < 0) {
+ dsim_err("failed to write SCALER_TABLE[%d]\n", mres_idx);
+ return;
+ }
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)KEY2_DISABLE,
+ ARRAY_SIZE(KEY2_DISABLE)) < 0) {
+ dsim_err("failed to write KEY2_DISABLE\n");
+ return;
+ }
+}
--- /dev/null
+/* drivers/video/exynos/decon/panels/s6e3ha8_mipi_lcd.c
+ *
+ * Samsung SoC MIPI LCD driver.
+ *
+ * Copyright (c) 2014 Samsung Electronics
+ *
+ * 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 <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/backlight.h>
+#include <video/mipi_display.h>
+#include <linux/platform_device.h>
+
+#include "../dsim.h"
+#include "lcd_ctrl.h"
+#include "decon_lcd.h"
+
+#define MAX_BRIGHTNESS 255
+#define MIN_BRIGHTNESS 0
+#define DEFAULT_BRIGHTNESS 0
+
+static struct dsim_device *dsim_base;
+static struct backlight_device *bd;
+
+static int s6e3ha8_get_brightness(struct backlight_device *bd)
+{
+ return bd->props.brightness;
+}
+
+static int get_backlight_level(int brightness)
+{
+ int backlightlevel;
+
+ switch (brightness) {
+ case 0:
+ backlightlevel = 0;
+ break;
+ case 1 ... 29:
+ backlightlevel = 0;
+ break;
+ case 30 ... 34:
+ backlightlevel = 1;
+ break;
+ case 35 ... 39:
+ backlightlevel = 2;
+ break;
+ case 40 ... 44:
+ backlightlevel = 3;
+ break;
+ case 45 ... 49:
+ backlightlevel = 4;
+ break;
+ case 50 ... 54:
+ backlightlevel = 5;
+ break;
+ case 55 ... 64:
+ backlightlevel = 6;
+ break;
+ case 65 ... 74:
+ backlightlevel = 7;
+ break;
+ case 75 ... 83:
+ backlightlevel = 8;
+ break;
+ case 84 ... 93:
+ backlightlevel = 9;
+ break;
+ case 94 ... 103:
+ backlightlevel = 10;
+ break;
+ case 104 ... 113:
+ backlightlevel = 11;
+ break;
+ case 114 ... 122:
+ backlightlevel = 12;
+ break;
+ case 123 ... 132:
+ backlightlevel = 13;
+ break;
+ case 133 ... 142:
+ backlightlevel = 14;
+ break;
+ case 143 ... 152:
+ backlightlevel = 15;
+ break;
+ case 153 ... 162:
+ backlightlevel = 16;
+ break;
+ case 163 ... 171:
+ backlightlevel = 17;
+ break;
+ case 172 ... 181:
+ backlightlevel = 18;
+ break;
+ case 182 ... 191:
+ backlightlevel = 19;
+ break;
+ case 192 ... 201:
+ backlightlevel = 20;
+ break;
+ case 202 ... 210:
+ backlightlevel = 21;
+ break;
+ case 211 ... 220:
+ backlightlevel = 22;
+ break;
+ case 221 ... 230:
+ backlightlevel = 23;
+ break;
+ case 231 ... 240:
+ backlightlevel = 24;
+ break;
+ case 241 ... 250:
+ backlightlevel = 25;
+ break;
+ case 251 ... 255:
+ backlightlevel = 26;
+ break;
+ default:
+ backlightlevel = 12;
+ break;
+ }
+
+ return backlightlevel;
+}
+
+static int update_brightness(int brightness)
+{
+ int backlightlevel;
+
+ backlightlevel = get_backlight_level(brightness);
+ /* Need to implement */
+ return 0;
+}
+
+static int s6e3ha8_set_brightness(struct backlight_device *bd)
+{
+ int brightness = bd->props.brightness;
+
+ if (brightness < MIN_BRIGHTNESS || brightness > MAX_BRIGHTNESS) {
+ pr_err("Brightness should be in the range of 0 ~ 255\n");
+ return -EINVAL;
+ }
+
+ update_brightness(brightness);
+
+ return 0;
+}
+
+static const struct backlight_ops s6e3ha8_backlight_ops = {
+ .get_brightness = s6e3ha8_get_brightness,
+ .update_status = s6e3ha8_set_brightness,
+};
+
+static int s6e3ha8_probe(struct dsim_device *dsim)
+{
+ const char *backlight_dev_name[2] = {
+ "panel1",
+ "panel1_1"
+ };
+
+ dsim_base = dsim;
+
+ bd = backlight_device_register(backlight_dev_name[dsim->id], NULL,
+ NULL, &s6e3ha8_backlight_ops, NULL);
+ if (IS_ERR(bd))
+ pr_err("failed to register backlight device!\n");
+
+ bd->props.max_brightness = MAX_BRIGHTNESS;
+ bd->props.brightness = DEFAULT_BRIGHTNESS;
+
+ return 0;
+}
+
+static int s6e3ha8_displayon(struct dsim_device *dsim)
+{
+ lcd_init(dsim->id, &dsim->lcd_info);
+ lcd_enable(dsim->id);
+ return 1;
+}
+
+static int s6e3ha8_suspend(struct dsim_device *dsim)
+{
+ return 0;
+}
+
+static int s6e3ha8_resume(struct dsim_device *dsim)
+{
+ return 0;
+}
+
+static int s6e3ha8_dump(struct dsim_device *dsim)
+{
+ lcd_dump(dsim->id);
+ return 0;
+}
+
+static int s6e3ha8_mres(struct dsim_device *dsim, int mres_idx)
+{
+ int dsc_en;
+ dsc_en = dsim->lcd_info.dt_lcd_mres.res_info[mres_idx].dsc_en;
+ lcd_mres(dsim->id, mres_idx, dsc_en);
+ return 0;
+}
+
+static int s6e3ha8_doze(struct dsim_device *dsim)
+{
+ pr_info("%s +\n", __func__);
+ return 0;
+}
+
+static int s6e3ha8_doze_suspend(struct dsim_device *dsim)
+{
+ pr_info("%s +\n", __func__);
+ return 0;
+}
+
+struct dsim_lcd_driver s6e3ha8_mipi_lcd_driver = {
+ .probe = s6e3ha8_probe,
+ .displayon = s6e3ha8_displayon,
+ .suspend = s6e3ha8_suspend,
+ .resume = s6e3ha8_resume,
+ .dump = s6e3ha8_dump,
+ .mres = s6e3ha8_mres,
+ .doze = s6e3ha8_doze,
+ .doze_suspend = s6e3ha8_doze_suspend,
+};
--- /dev/null
+/* linux/drivers/video/decon_display/s6e3fa8_param.h
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * 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 __S6E3HA8_PARAM_H__
+#define __S6E3HA8_PARAM_H__
+
+#define S6E3HA8_ID_REG 0x04
+#define S6E3HA8_RD_LEN 3
+#define S6E3HA8_RDDPM_ADDR 0x0A
+#define S6E3HA8_RDDSM_ADDR 0x0E
+
+/* MCD */
+static const unsigned char SEQ_TEST_KEY_ON_F0[] = {
+ 0xf0, 0x5a, 0x5a
+};
+
+static const unsigned char SEQ_TEST_KEY_ON_FC[] = {
+ 0xfc, 0x5a, 0x5a
+};
+
+static const unsigned char SEQ_DSC_EN[] = {
+ 0x01
+};
+
+static const unsigned char SEQ_PPS_SLICE2[] = {
+ // QHD :2960x1440
+ 0x11, 0x00, 0x00, 0x89, 0x30, 0x80, 0x0B, 0x90,
+ 0x05, 0xA0, 0x00, 0x28, 0x02, 0xD0, 0x02, 0xD0,
+ 0x02, 0x00, 0x02, 0x68, 0x00, 0x20, 0x04, 0x6C,
+ 0x00, 0x0A, 0x00, 0x0C, 0x02, 0x77, 0x01, 0xE9,
+ 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const unsigned char SEQ_SLEEP_OUT[] = {
+ 0x11
+};
+
+static const unsigned char SEQ_TSP_HSYNC[] = {
+ 0xB9,
+ 0x01, 0xB0, 0x81, 0x09, 0x00, 0x00, 0x00, 0x11, 0x01
+
+};
+
+static const unsigned char SEQ_TSP_VSYNC[] = {
+ 0xB0, 0x07
+};
+
+static const unsigned char SEQ_SET_AREA[] = {
+ 0x1A, 0x1F, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const unsigned char SEQ_TE_ON[] = {
+ 0x35
+};
+
+static const unsigned char SEQ_ERR_FG[] = {
+ 0xED, 0x44
+};
+
+static const unsigned char SEQ_TE_START_SETTING[] = {
+ 0xB9, 0x01, 0xB0, 0x96, 0x09
+};
+
+static const unsigned char SEQ_FFC[] = {
+ 0xCE,
+ 0x0D, 0x58, 0x14, 0x64, 0x38, 0xB8, 0xF2, 0x03,
+ 0x00, 0xFF, 0x02, 0x0A, 0x0A, 0x0A, 0x0A, 0x0F,
+ 0x23,
+};
+
+static const unsigned char SEQ_TEST_KEY_OFF_FC[] = {
+ 0xFC, 0xA5, 0xA5
+};
+
+static const unsigned char SEQ_TEST_KEY_OFF_F0[] = {
+ 0xF0, 0xA5, 0xA5
+};
+
+static const unsigned char SEQ_TEST_KEY_OFF_F1[] = {
+ 0xF1, 0xA5, 0xA5
+};
+
+static const unsigned char SEQ_DISPLAY_ON[] = {
+ 0x29
+};
+
+static const unsigned char SEQ_DISPLAY_OFF[] = {
+ 0x28
+};
+
+static const unsigned char SEQ_TSET_GLOBAL[] = {
+ 0xB0,
+ 0x01
+};
+
+static const unsigned char SEQ_ESD_FG[] = {
+ 0xED,
+ 0x01, 0x04
+};
+
+static const unsigned char SEQ_ALLPOFF[] = {
+ 0x22
+};
+
+static const unsigned char SEQ_ALLPON[] = {
+ 0x23
+};
+
+static const unsigned char SEQ_NOP[] = {
+ 0x00,
+};
+
+static const unsigned char KEY1_ENABLE[] = {
+ 0x9F, 0xA5, 0xA5
+};
+
+static const unsigned char KEY2_ENABLE[] = {
+ 0xF0, 0x5A, 0x5A
+};
+
+static const unsigned char KEY1_DISABLE[] = {
+ 0x9F, 0x5A, 0x5A
+};
+
+static const unsigned char KEY2_DISABLE[] = {
+ 0xF0, 0xA5, 0xA5
+};
+
+static const unsigned char DSC_EN[][1] = {
+ { 0x00 },
+ { 0x01 },
+};
+
+static const unsigned char PPS_TABLE[][128] = {
+ {
+ /* PPS MODE0 : 1440x2960, Slice Info : 720x40 */
+ 0x11, 0x00, 0x00, 0x89, 0x30, 0x80, 0x0B, 0x90,
+ 0x05, 0xA0, 0x00, 0x28, 0x02, 0xD0, 0x02, 0xD0,
+ 0x02, 0x00, 0x02, 0x68, 0x00, 0x20, 0x04, 0x6C,
+ 0x00, 0x0A, 0x00, 0x0C, 0x02, 0x77, 0x01, 0xE9,
+ 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ },
+ {
+ /* PPS MODE1 : 1080x2220, Slice Info : 540x30 */
+ 0x11, 0x00, 0x00, 0x89, 0x30, 0x80, 0x08, 0xAC,
+ 0x04, 0x38, 0x00, 0x1E, 0x02, 0x1C, 0x02, 0x1C,
+ 0x02, 0x00, 0x02, 0x0E, 0x00, 0x20, 0x02, 0xE3,
+ 0x00, 0x07, 0x00, 0x0C, 0x03, 0x50, 0x03, 0x64,
+ 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ },
+ {
+ /* PPS MODE2 : 720x1048, Slice Info : 360x74 */
+ 0x11, 0x00, 0x00, 0x89, 0x30, 0x80, 0x05, 0xC8,
+ 0x02, 0xD0, 0x00, 0x4A, 0x01, 0x68, 0x01, 0x68,
+ 0x02, 0x00, 0x01, 0xB4, 0x00, 0x20, 0x05, 0xBA,
+ 0x00, 0x05, 0x00, 0x0C, 0x01, 0x51, 0x02, 0x10,
+ 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+};
+
+static const unsigned char SCALER_TABLE[][6] = {
+ /* scaler off, 1440x2960 */
+ {0xBA, 0x01, 0x26, 0x08, 0x08, 0xF3},
+ /* 1.78x scaler on, 1080x2220 */
+ {0xBA, 0x02, 0x26, 0x08, 0x08, 0xF3},
+ /* 4x scaler on, 720x1048 */
+ {0xBA, 0x00, 0x26, 0x08, 0x08, 0xF3},
+};
+
+static const unsigned char CASET_TABLE[][5] = {
+ /* scaler off, 1440x2960 */
+ {0x2A, 0x00, 0x00, 0x05, 0x9F},
+ /* 1.78x scaler on, 1080x2220 */
+ {0x2A, 0x00, 0x00, 0x04, 0x37},
+ /* 4x scaler on, 720x1048 */
+ {0x2A, 0x00, 0x00, 0x02, 0xCF},
+};
+
+static const unsigned char PASET_TABLE[][5] = {
+ /* scaler off, 1440x2960 */
+ {0x2B, 0x00, 0x00, 0x0B, 0x8F},
+ /* 1.78x scaler on, 1080x2220 */
+ {0x2B, 0x00, 0x00, 0x08, 0xAB},
+ /* 4x scaler on, 720x1048 */
+ {0x2B, 0x00, 0x00, 0x05, 0xC7},
+};
+
+#endif /* __S6E3HA8_PARAM_H__ */
--- /dev/null
+/* drivers/video/fbdev/exynos/dpu/panels/s6e3hf4_lcd_ctrl.c
+ *
+ * Samsung SoC MIPI LCD CONTROL functions
+ *
+ * Copyright (c) 2016 Samsung Electronics
+ *
+ * Jiun Yu, <jiun.yu@samsung.com>
+ *
+ * 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 <video/mipi_display.h>
+#include "s6e3hf4_param.h"
+#include "lcd_ctrl.h"
+#include "../dsim.h"
+
+/*
+ * S6E3HF4 lcd init sequence
+ *
+ * Parameters
+ * - mic_enabled : if mic is enabled, MIC_ENABLE command must be sent
+ * - mode : LCD init sequence depends on command or video mode
+ * - 1/3 DSC 4 Slices
+ */
+void lcd_init(int id, struct decon_lcd *lcd)
+{
+ dsim_dbg("%s +\n", __func__);
+
+ /* DSC setting */
+ if (lcd->dsc_enabled) {
+ if (dsim_wr_data(id, MIPI_DSI_DSC_PRA, (unsigned long)SEQ_DSC_EN[0],
+ SEQ_DSC_EN[1]) < 0)
+ dsim_err("fail to write SEQ_DSC_EN command.\n");
+
+ switch (lcd->dsc_slice_num) {
+ case 4:
+ if (dsim_wr_data(id, MIPI_DSI_DSC_PPS, (unsigned long)SEQ_PPS_SLICE4,
+ ARRAY_SIZE(SEQ_PPS_SLICE4)) < 0)
+ dsim_err("fail to write SEQ_PPS_SLICE4 command.\n");
+ break;
+ case 2:
+ if (dsim_wr_data(id, MIPI_DSI_DSC_PPS, (unsigned long)SEQ_PPS_SLICE2,
+ ARRAY_SIZE(SEQ_PPS_SLICE2)) < 0)
+ dsim_err("fail to write SEQ_PPS_SLICE2 command.\n");
+ break;
+ default:
+ dsim_err("fail to set MIPI_DSI_DSC_PPS command(no slice).\n");
+ break;
+ }
+ }
+
+ /* Sleep Out(11h) */
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE,
+ (unsigned long)SEQ_SLEEP_OUT[0], 0) < 0)
+ dsim_err("fail to send SEQ_SLEEP_OUT command.\n");
+
+ dsim_wait_for_cmd_completion(id);
+ msleep(120);
+
+ /* Setting the TE timing to prevent LCD tearing */
+ /* KEY_ON -> Setting -> KEY_OFF */
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_TEST_KEY_ON_F0,
+ ARRAY_SIZE(SEQ_TEST_KEY_ON_F0)) < 0)
+ dsim_err("fail to write KEY_ON init command.\n");
+
+ if (!lcd->dsc_enabled) {
+ /* HACK : dsc disble */
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_TEST_KEY_ON_BA,
+ ARRAY_SIZE(SEQ_TEST_KEY_ON_BA)) < 0)
+ dsim_err("fail to write KEY_ON_BA init command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_TEST_KEY_OFF_F0,
+ ARRAY_SIZE(SEQ_TEST_KEY_OFF_F0)) < 0)
+ dsim_err("fail to write KEY_ON init command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)_SEQ_TEST_KEY_ON_2A,
+ ARRAY_SIZE(_SEQ_TEST_KEY_ON_2A)) < 0)
+ dsim_err("fail to write KEY_ON_2A init command.\n");
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)_SEQ_TEST_KEY_ON_2B,
+ ARRAY_SIZE(_SEQ_TEST_KEY_ON_2B)) < 0)
+ dsim_err("fail to write KEY_ON_2B init command.\n");
+ }
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_TE_START_SETTING,
+ ARRAY_SIZE(SEQ_TE_START_SETTING)) < 0)
+ dsim_err("fail to write TE_START_SETTING command.\n");
+
+ if (lcd->dsc_enabled) {
+ if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_TEST_KEY_OFF_F0,
+ ARRAY_SIZE(SEQ_TEST_KEY_OFF_F0)) < 0)
+ dsim_err("fail to write KEY_OFF init command.\n");
+ }
+
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE, SEQ_TE_ON[0], 0) < 0)
+ dsim_err("fail to write SEQ_TE_ON init command.\n");
+
+ dsim_dbg("%s -\n", __func__);
+}
+
+void lcd_enable(int id)
+{
+ dsim_dbg("%s +\n", __func__);
+ if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE, SEQ_DISPLAY_ON[0], 0) < 0)
+ dsim_err("fail to send SEQ_DISPLAY_ON command.\n");
+ dsim_dbg("%s -\n", __func__);
+}
+
+void lcd_disable(int id)
+{
+ /* This function needs to implement */
+}
+
+int dsim_write_by_panel(int id, const u8 *cmd, u32 cmd_size)
+{
+ int ret;
+
+ if (cmd_size == 1)
+ ret = dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE,
+ cmd[0], 0);
+ else if (cmd_size == 2)
+ ret = dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE_PARAM,
+ cmd[0], cmd[1]);
+ else
+ ret = dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned long)cmd, cmd_size);
+
+ return ret;
+}
+
+int dsim_read_from_panel(int id, u8 addr, u32 size, u8 *buf)
+{
+ int ret;
+
+ ret = dsim_rd_data(id, MIPI_DSI_DCS_READ, (u32)addr, size, buf);
+
+ return ret;
+}
+
+static int s6e3hf4_wqhd_dump(int dsim)
+{
+ int ret = 0;
+ unsigned char rx_buf[DSIM_DDI_ID_LEN + 1];
+
+ dsim_info(" + %s\n", __func__);
+ ret = dsim_write_by_panel(dsim, SEQ_TEST_KEY_ON_F0,
+ ARRAY_SIZE(SEQ_TEST_KEY_ON_F0));
+ if (ret < 0)
+ dsim_err("%s : fail to write CMD : KEY_ON_F0\n", __func__);
+
+ ret = dsim_write_by_panel(dsim, SEQ_TEST_KEY_ON_FC,
+ ARRAY_SIZE(SEQ_TEST_KEY_ON_FC));
+ if (ret < 0)
+ dsim_err("%s : fail to write CMD : KEY_ON_FC\n", __func__);
+
+ ret = dsim_read_from_panel(dsim, MIPI_DCS_GET_POWER_MODE,
+ DSIM_DDI_ID_LEN, rx_buf);
+ if (ret != DSIM_DDI_ID_LEN) {
+ dsim_err("%s : can't read POWER_MODE Reg\n",__func__);
+ goto dump_exit;
+ }
+
+ dsim_info("=== Panel's POWER_MODE Reg Value : %x ===\n", rx_buf[0]);
+
+ if (rx_buf[0] & 0x80)
+ dsim_info("* Booster Voltage Status : ON\n");
+ else
+ dsim_info("* Booster Voltage Status : OFF\n");
+
+ if (rx_buf[0] & 0x40)
+ dsim_info("* Idle Mode : On\n");
+ else
+ dsim_info("* Idle Mode : OFF\n");
+
+ if (rx_buf[0] & 0x20)
+ dsim_info("* Partial Mode : On\n");
+ else
+ dsim_info("* Partial Mode : OFF\n");
+
+ if (rx_buf[0] & 0x10)
+ dsim_info("* Sleep OUT and Working Ok\n");
+ else
+ dsim_info("* Sleep IN\n");
+
+ if (rx_buf[0] & 0x08)
+ dsim_info("* Normal Mode On and Working Ok\n");
+ else
+ dsim_info("* Sleep IN\n");
+
+ if (rx_buf[0] & 0x04)
+ dsim_info("* Display On and Working Ok\n");
+ else
+ dsim_info("* Display Off\n");
+
+ ret = dsim_read_from_panel(dsim, MIPI_DCS_GET_SIGNAL_MODE, DSIM_DDI_ID_LEN, rx_buf);
+ if (ret != DSIM_DDI_ID_LEN) {
+ dsim_err("%s : can't read SIGNAL_MODE Reg\n",__func__);
+ goto dump_exit;
+ }
+
+ dsim_info("=== Panel's SIGNAL_MODE Reg Value : %x ===\n", rx_buf[0]);
+
+ if (rx_buf[0] & 0x80)
+ dsim_info("* TE On\n");
+ else
+ dsim_info("* TE OFF\n");
+
+ if (rx_buf[0] & 0x40)
+ dsim_info("* TE MODE on\n");
+
+ if (rx_buf[0] & 0x01) {
+ /* get a value of protocol violation error */
+ ret = dsim_read_from_panel(dsim, 0xEA, DSIM_DDI_ID_LEN, rx_buf);
+ if (ret != DSIM_DDI_ID_LEN) {
+ dsim_err("%s : can't read Panel's Protocol\n",__func__);
+ goto dump_exit;
+ }
+
+ dsim_err("* Protocol violation: buf[0] = %x\n", rx_buf[0]);
+ dsim_err("* Protocol violation: buf[1] = %x\n", rx_buf[1]);
+ }
+
+ ret = dsim_write_by_panel(dsim, SEQ_TEST_KEY_OFF_FC,
+ ARRAY_SIZE(SEQ_TEST_KEY_OFF_FC));
+ if (ret < 0)
+ dsim_err("%s : fail to write CMD : KEY_OFF_FC\n", __func__);
+
+ ret = dsim_write_by_panel(dsim, SEQ_TEST_KEY_OFF_F0,
+ ARRAY_SIZE(SEQ_TEST_KEY_OFF_F0));
+ if (ret < 0)
+ dsim_err("%s : fail to write CMD : KEY_OFF_F0\n", __func__);
+
+dump_exit:
+ dsim_info(" - %s\n", __func__);
+
+ return ret;
+}
+
+int lcd_dump(int id)
+{
+ s6e3hf4_wqhd_dump(id);
+ return 0;
+}
+
+void lcd_mres(int id, int mres_idx, int dsc_en)
+{
+ dsim_info("%s +\n", __func__);
+ dsim_info("S6E3HF4 doesn't support\n");
+ dsim_info("%s -\n", __func__);
+}
--- /dev/null
+/* drivers/video/fbdev/exynos/decon_8890/panels/s6e3hf4_mipi_lcd.c
+ *
+ * Samsung SoC MIPI LCD driver.
+ *
+ * Copyright (c) 2015 Samsung Electronics
+ *
+ * Jiun Yu, <jiun.yu@samsung.com>
+ *
+ * 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 <linux/delay.h>
+#include <linux/gpio.h>
+#include <video/mipi_display.h>
+#include <linux/platform_device.h>
+#include <linux/backlight.h>
+
+#include "../dsim.h"
+#include "lcd_ctrl.h"
+#include "decon_lcd.h"
+
+#define MAX_BRIGHTNESS 255
+#define MIN_BRIGHTNESS 0
+#define DEFAULT_BRIGHTNESS 0
+
+static struct dsim_device *dsim_base;
+static struct backlight_device *bd;
+
+static int s6e3hf4_get_brightness(struct backlight_device *bd)
+{
+ return bd->props.brightness;
+}
+
+static int get_backlight_level(int brightness)
+{
+ int backlightlevel;
+
+ switch (brightness) {
+ case 0:
+ backlightlevel = 0;
+ break;
+ case 1 ... 29:
+ backlightlevel = 0;
+ break;
+ case 30 ... 34:
+ backlightlevel = 1;
+ break;
+ case 35 ... 39:
+ backlightlevel = 2;
+ break;
+ case 40 ... 44:
+ backlightlevel = 3;
+ break;
+ case 45 ... 49:
+ backlightlevel = 4;
+ break;
+ case 50 ... 54:
+ backlightlevel = 5;
+ break;
+ case 55 ... 64:
+ backlightlevel = 6;
+ break;
+ case 65 ... 74:
+ backlightlevel = 7;
+ break;
+ case 75 ... 83:
+ backlightlevel = 8;
+ break;
+ case 84 ... 93:
+ backlightlevel = 9;
+ break;
+ case 94 ... 103:
+ backlightlevel = 10;
+ break;
+ case 104 ... 113:
+ backlightlevel = 11;
+ break;
+ case 114 ... 122:
+ backlightlevel = 12;
+ break;
+ case 123 ... 132:
+ backlightlevel = 13;
+ break;
+ case 133 ... 142:
+ backlightlevel = 14;
+ break;
+ case 143 ... 152:
+ backlightlevel = 15;
+ break;
+ case 153 ... 162:
+ backlightlevel = 16;
+ break;
+ case 163 ... 171:
+ backlightlevel = 17;
+ break;
+ case 172 ... 181:
+ backlightlevel = 18;
+ break;
+ case 182 ... 191:
+ backlightlevel = 19;
+ break;
+ case 192 ... 201:
+ backlightlevel = 20;
+ break;
+ case 202 ... 210:
+ backlightlevel = 21;
+ break;
+ case 211 ... 220:
+ backlightlevel = 22;
+ break;
+ case 221 ... 230:
+ backlightlevel = 23;
+ break;
+ case 231 ... 240:
+ backlightlevel = 24;
+ break;
+ case 241 ... 250:
+ backlightlevel = 25;
+ break;
+ case 251 ... 255:
+ backlightlevel = 26;
+ break;
+ default:
+ backlightlevel = 12;
+ break;
+ }
+
+ return backlightlevel;
+}
+
+static int update_brightness(int brightness)
+{
+ int backlightlevel;
+
+ backlightlevel = get_backlight_level(brightness);
+ return 0;
+}
+
+static int s6e3hf4_set_brightness(struct backlight_device *bd)
+{
+ int brightness = bd->props.brightness;
+
+ if (brightness < MIN_BRIGHTNESS || brightness > MAX_BRIGHTNESS) {
+ pr_err("Brightness should be in the range of 0 ~ 255\n");
+ return -EINVAL;
+ }
+ update_brightness(brightness);
+
+ return 0;
+}
+
+static const struct backlight_ops s6e3hf4_backlight_ops = {
+ .get_brightness = s6e3hf4_get_brightness,
+ .update_status = s6e3hf4_set_brightness,
+};
+
+static int s6e3hf4_probe(struct dsim_device *dsim)
+{
+ dsim_base = dsim;
+
+ bd = backlight_device_register("pwm-backlight.0", NULL,
+ NULL, &s6e3hf4_backlight_ops, NULL);
+ if (IS_ERR(bd))
+ pr_err("failed to register backlight device!\n");
+
+ bd->props.max_brightness = MAX_BRIGHTNESS;
+ bd->props.brightness = DEFAULT_BRIGHTNESS;
+
+ return 0;
+}
+
+static int s6e3hf4_displayon(struct dsim_device *dsim)
+{
+ dsim_info("%s +\n", __func__);
+ lcd_init(dsim->id, &dsim->lcd_info);
+ lcd_enable(dsim->id);
+ dsim_info("%s -\n", __func__);
+ return 1;
+}
+
+static int s6e3hf4_suspend(struct dsim_device *dsim)
+{
+ return 0;
+}
+
+static int s6e3hf4_resume(struct dsim_device *dsim)
+{
+ return 0;
+}
+
+static int s6e3hf4_dump(struct dsim_device *dsim)
+{
+ lcd_dump(dsim->id);
+ return 0;
+}
+
+static int s6e3hf4_mres(struct dsim_device *dsim, int mres_idx)
+{
+ int dsc_en;
+
+ dsc_en = dsim->lcd_info.dt_lcd_mres.res_info[mres_idx].dsc_en;
+ lcd_mres(dsim->id, mres_idx, dsc_en);
+ return 0;
+}
+
+static int s6e3hf4_doze(struct dsim_device *dsim)
+{
+ pr_info("%s +\n", __func__);
+ return 0;
+}
+
+static int s6e3hf4_doze_suspend(struct dsim_device *dsim)
+{
+ pr_info("%s +\n", __func__);
+ return 0;
+}
+
+struct dsim_lcd_driver s6e3hf4_mipi_lcd_driver = {
+ .probe = s6e3hf4_probe,
+ .displayon = s6e3hf4_displayon,
+ .suspend = s6e3hf4_suspend,
+ .resume = s6e3hf4_resume,
+ .dump = s6e3hf4_dump,
+ .mres = s6e3hf4_mres,
+ .doze = s6e3hf4_doze,
+ .doze_suspend = s6e3hf4_doze_suspend,
+};
--- /dev/null
+/* drivers/video/fbdev/exynos/dpu/panels/s6e3hf4_param.h
+ *
+ * Samsung SoC MIPI LCD CONTROL functions
+ *
+ * Copyright (c) 2016 Samsung Electronics
+ *
+ * Jiun Yu, <jiun.yu@samsung.com>
+ *
+ * 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 __S6E3HF4_PARAM_H__
+#define __S6E3HF4_PARAM_H__
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#define S6E3HF4_CODE_REG 0xD6
+#define S6E3HF4_CODE_LEN 5
+
+#define S6E3HF4_MAX_BRIGHTNESS 360
+#define S6E3HF4_HBM_BRIGHTNESS 600
+
+static const unsigned char SEQ_TEST_KEY_ON_F0[] = {
+ 0xF0,
+ 0x5A, 0x5A
+};
+/* HACK for dsc err */
+static const unsigned char SEQ_TEST_KEY_ON_BA[] = {
+ 0xBA,
+ 0x00
+};
+
+static const unsigned char _SEQ_TEST_KEY_ON_2A[] = {
+ 0x2A,
+ 0x00, 0x00, 0x02, 0xCF, 0x00, 0x00
+};
+
+static const unsigned char _SEQ_TEST_KEY_ON_2B[] = {
+ 0x2B,
+ 0x00, 0x00, 0x04, 0xff
+};
+
+
+static const unsigned char SEQ_TEST_KEY_OFF_F0[] = {
+ 0xF0,
+ 0xA5, 0xA5
+};
+
+static const unsigned char SEQ_TEST_KEY_ON_F1[] = {
+ 0xF1,
+ 0x5A, 0x5A,
+};
+
+static const unsigned char SEQ_TEST_KEY_OFF_F1[] = {
+ 0xF1,
+ 0xA5, 0xA5,
+};
+
+static const unsigned char SEQ_TEST_KEY_ON_F2[] = {
+ 0xF2,
+ 0x41, 0x0E, 0x06, 0x28, 0xB8, 0x80,
+ 0x54, 0xE0, 0xB4, 0x40, 0x09, 0x22,
+};
+
+static const unsigned char SEQ_TEST_KEY_ON_F4[] = {
+ 0xF4,
+ 0xAB, 0x1E, 0x13, 0x8A, 0x1F, 0x0C, 0x09, 0x00, 0x00,
+};
+
+static const unsigned char SEQ_TEST_KEY_ON_F6[] = {
+ 0xF6,
+ 0x43, 0x07, 0x17, 0x30, 0xAA, 0x00, 0xC3, 0xC5, 0xD1, 0x01,
+};
+
+static const unsigned char SEQ_TEST_KEY_ON_B1[] = {
+ 0xB1,
+ 0x10, 0x03, 0x10, 0x10, 0x10, 0x80, 0x40,
+};
+
+static const unsigned char SEQ_TEST_KEY_ON_B3[] = {
+ 0xB3,
+ 0x68,
+};
+
+static const unsigned char SEQ_TEST_KEY_ON_B4[] = {
+ 0xB4,
+ 0x50, 0x99, 0x27, 0x0A, 0x45,
+};
+
+static const unsigned char SEQ_TEST_KEY_ON_B5[] = {
+ 0xB5,
+ 0x19, 0xBC, 0x4A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x55,
+ 0x54, 0x20, 0x00, 0x00, 0x0A, 0xAA, 0xAF, 0x0F, 0x01, 0x11,
+ 0x11, 0x10, 0x00, 0x00, 0x8F, 0x52, 0x30, 0x00, 0x00, 0x00,
+ 0x00,
+};
+
+static const unsigned char SEQ_TEST_KEY_ON_C7[] = {
+ 0xC7,
+ 0x00,
+};
+
+static const unsigned char SEQ_TEST_KEY_ON_CB[] = {
+ 0xCB,
+ 0x12, 0x11, 0x81, 0x01, 0x00, 0x63, 0x82, 0x00, 0xE2, 0x0A,
+ 0x05, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x15, 0x9A, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x12, 0xCF, 0x00, 0x00, 0xD5, 0x11, 0x13, 0x0E, 0x45, 0x46,
+ 0xC2, 0x15, 0x15, 0xD5, 0xD5, 0xD5, 0xD5, 0xD1, 0x53, 0xCE,
+ 0xC5, 0xC6, 0x02, 0x15, 0x15, 0x15, 0x15, 0x15, 0x00, 0xE2,
+ 0x00, 0x00, 0x7B, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
+};
+
+static const unsigned char SEQ_TEST_KEY_ON_EB[] = {
+ 0xEB,
+ 0xFF, 0x53,
+};
+
+static const unsigned char SEQ_TEST_KEY_ON_CA[] = {
+ 0xCA,
+ 0x01, 0x5A, 0x01, 0x26, 0x01, 0x80, 0xDF, 0xDE, 0xDF, 0xDA,
+ 0xDA, 0xDC, 0xC2, 0xBE, 0xC3, 0xC7, 0xC2, 0xC8, 0xD4, 0xD0,
+ 0xD4, 0xCF, 0xCB, 0xCE, 0xA6, 0xA8, 0xA7, 0xA7, 0xC5, 0xAA,
+ 0x00, 0x00, 0x00, 0x33, 0x04,
+};
+
+static const unsigned char SEQ_TEST_KEY_ON_F7[] = {
+ 0xF7,
+ 0x03,
+};
+
+static const unsigned char SEQ_TEST_KEY_ON_53[] = {
+ 0x53,
+ 0x28,
+};
+
+static const unsigned char SEQ_TEST_KEY_ON_51[] = {
+ 0x51,
+ 0xFF,
+};
+
+static const unsigned char SEQ_TEST_KEY_ON_2A[] = {
+ 0x2A,
+ 0x00, 0x00, 0x05, 0x9F, 0x00, 0x00, 0x00,
+};
+
+static const unsigned char SEQ_TEST_KEY_ON_2B[] = {
+ 0x2B,
+ 0x00, 0x00, 0x09, 0xFF,
+};
+
+static const unsigned char SEQ_TEST_KEY_ON_E5[] = {
+ 0xE5,
+ 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x20,
+};
+
+static const unsigned char SEQ_TEST_KEY_ON_FC[] = {
+ 0xFC,
+ 0x5A, 0x5A
+};
+
+static const unsigned char SEQ_TEST_KEY_OFF_FC[] = {
+ 0xFC,
+ 0xA5, 0xA5
+};
+
+static const unsigned char SEQ_SLEEP_OUT[] = {
+ 0x11
+};
+
+static const unsigned char SEQ_SLEEP_IN[] = {
+ 0x10
+};
+
+static const unsigned char SEQ_DISPLAY_ON[] = {
+ 0x29
+};
+
+static const unsigned char SEQ_DISPLAY_OFF[] = {
+ 0x28
+};
+
+
+static const unsigned char SEQ_GAMMA_CONDITION_SET[] = {
+ 0xCA,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80,
+ 0x00, 0x00, 0x00,
+ 0x00, 0x00
+};
+
+static const unsigned char SEQ_AOR_CONTROL[] = {
+ 0xB1,
+ 0x10, 0x03
+};
+
+static const unsigned char SEQ_TSET_ELVSS_SET[] = {
+ 0xB5,
+ 0x19, /* temperature 25 */
+ 0x9C, /* MPS_CON: ACL OFF */
+ 0x0A /* ELVSS: MAX*/
+};
+
+static const unsigned char SEQ_GAMMA_UPDATE[] = {
+ 0xF7,
+ 0x03
+};
+
+static const unsigned char SEQ_GAMMA_UPDATE_L[] = {
+ 0xF7,
+ 0x00,
+};
+
+static const unsigned char SEQ_TSET_GLOBAL[] = {
+ 0xB0,
+ 0x01
+};
+
+static const unsigned char SEQ_TSET[] = {
+ 0xB5,
+ 0x19
+};
+
+static const unsigned char SEQ_VINT_SET[] = {
+ 0xF4,
+ 0xAB, /* VINT */
+ 0x1E /* 360nit */
+};
+
+static const unsigned char SEQ_TE_ON[] = {
+ 0x35,
+ 0x00
+};
+static const unsigned char SEQ_TE_OFF[] = {
+ 0x34,
+};
+
+static const unsigned char SEQ_TSP_TE[] = {
+ 0xBD,
+ 0x11, 0x11, 0x02, 0x16, 0x02, 0x16
+};
+
+static const unsigned char SEQ_PENTILE_SETTING[] = {
+ 0xC0,
+ 0x00, 0x00, 0xD8, 0xD8
+};
+
+static const unsigned char SEQ_POC_SETTING1[] = {
+ 0xB0,
+ 0x20
+};
+
+static const unsigned char SEQ_POC_SETTING2[] = {
+ 0xFE,
+ 0x04
+};
+
+static const unsigned char SEQ_PCD_SETTING[] = {
+ 0xCC,
+ 0x40, 0x51
+};
+
+static const unsigned char SEQ_ERR_FG_SETTING[] = {
+ 0xED,
+ 0x44
+};
+
+static const unsigned char SEQ_TE_START_SETTING[] = {
+ 0xB9,
+ 0x01, 0x0A, 0x07, 0x00, 0x0D
+};
+
+static const unsigned char SEQ_HBM_OFF[] = {
+ 0x53,
+ 0x00
+};
+
+static const unsigned char SEQ_HBM_ON[] = {
+ 0x53,
+ 0xC0
+};
+static const unsigned char SEQ_ACL_OFF[] = {
+ 0x55,
+ 0x00
+};
+
+static const unsigned char SEQ_ACL_8[] = {
+ 0x55,
+ 0x02,
+};
+
+static const unsigned char SEQ_ACL_OFF_OPR_AVR[] = {
+ 0xB4,
+ 0x40
+};
+
+static const unsigned char SEQ_ACL_ON_OPR_AVR[] = {
+ 0xB4,
+ 0x50
+};
+
+static const unsigned char SEQ_DSC_EN[] = {
+ 0x01, 0x00
+};
+
+static const unsigned char SEQ_PPS_SLICE4[] = {
+ 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 const unsigned char SEQ_PPS_SLICE2[] = {
+ 0x11, 0x00, 0x00, 0x89, 0x30,
+ 0x80, 0x0A, 0x00, 0x05, 0xA0,
+ 0x00, 0x20, 0x02, 0xD0, 0x02,
+ 0xD0, 0x02, 0x00, 0x02, 0x68,
+
+ 0x00, 0x20, 0x03, 0x87, 0x00,
+ 0x0A, 0x00, 0x0C, 0x03, 0x19,
+ 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 const unsigned char SEQ_PPS_3[] = {
+ 0x11, 0x00, 0x00, 0x89,
+ 0x30, 0x80, 0x0a, 0x00, 0x05,
+ 0xa0, 0x00, 0x10, 0x05, 0xa0,
+ 0x05, 0xa0, 0x02, 0x00, 0x03,
+ 0xd0, 0x00, 0x20, 0x02, 0x33,
+ 0x00, 0x14, 0x00, 0x0c, 0x06,
+ 0x67, 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, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
+
+static const unsigned char SEQ_WQXGA_PARTIAL_UPDATE[] = {
+ 0x2A,
+ 0x00, 0x00, 0x05, 0x9F,
+};
+
+static const unsigned char SEQ_ESD_FG[] = {
+ 0xED,
+ 0x01, 0x04
+};
+
+static const unsigned char SEQ_ALLPOFF[] = {
+ 0x22
+};
+
+static const unsigned char SEQ_ALLPON[] = {
+ 0x23
+};
+
+static const unsigned char SEQ_NOP[] = {
+ 0x00,
+};
+
+#endif /* __S6E3HF4_PARAM_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Window update 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 <video/mipi_display.h>
+
+#include "decon.h"
+#include "dpp.h"
+#include "dsim.h"
+
+static void win_update_adjust_region(struct decon_device *decon,
+ struct decon_win_config *win_config,
+ struct decon_reg_data *regs)
+{
+ int i;
+ int div_w, div_h;
+ struct decon_rect r1, r2;
+ struct decon_win_config *update_config = &win_config[DECON_WIN_UPDATE_IDX];
+ struct decon_win_config *config;
+ struct decon_frame adj_region;
+
+ regs->need_update = false;
+ DPU_FULL_RECT(®s->up_region, decon->lcd_info);
+
+ if (!decon->win_up.enabled)
+ return;
+
+ if (update_config->state != DECON_WIN_STATE_UPDATE)
+ return;
+
+ if ((update_config->dst.x < 0) || (update_config->dst.y < 0)) {
+ update_config->state = DECON_WIN_STATE_DISABLED;
+ return;
+ }
+
+ r1.left = update_config->dst.x;
+ r1.top = update_config->dst.y;
+ r1.right = r1.left + update_config->dst.w - 1;
+ r1.bottom = r1.top + update_config->dst.h - 1;
+
+ for (i = 0; i < decon->dt.max_win; i++) {
+ config = &win_config[i];
+ if (config->state != DECON_WIN_STATE_DISABLED) {
+ if (config->dpp_parm.rot || is_scaling(config)) {
+ update_config->state = DECON_WIN_STATE_DISABLED;
+ return;
+ }
+ }
+ }
+
+ DPU_DEBUG_WIN("original update region[%d %d %d %d]\n",
+ update_config->dst.x, update_config->dst.y,
+ update_config->dst.w, update_config->dst.h);
+
+ r2.left = (r1.left / decon->win_up.rect_w) * decon->win_up.rect_w;
+ r2.top = (r1.top / decon->win_up.rect_h) * decon->win_up.rect_h;
+
+ div_w = (r1.right + 1) / decon->win_up.rect_w;
+ div_w = (div_w * decon->win_up.rect_w == r1.right + 1) ? div_w : div_w + 1;
+ r2.right = div_w * decon->win_up.rect_w - 1;
+
+ div_h = (r1.bottom + 1) / decon->win_up.rect_h;
+ div_h = (div_h * decon->win_up.rect_h == r1.bottom + 1) ? div_h : div_h + 1;
+ r2.bottom = div_h * decon->win_up.rect_h - 1;
+
+ /* TODO: Now, 4 slices must be used. This will be modified */
+ r2.left = 0;
+ r2.right = decon->lcd_info->xres - 1;
+
+ memcpy(®s->up_region, &r2, sizeof(struct decon_rect));
+
+ memset(&adj_region, 0, sizeof(struct decon_frame));
+ adj_region.x = regs->up_region.left;
+ adj_region.y = regs->up_region.top;
+ adj_region.w = regs->up_region.right - regs->up_region.left + 1;
+ adj_region.h = regs->up_region.bottom - regs->up_region.top + 1;
+ DPU_EVENT_LOG_UPDATE_REGION(&decon->sd, &update_config->dst, &adj_region);
+
+ DPU_DEBUG_WIN("adjusted update region[%d %d %d %d]\n",
+ adj_region.x, adj_region.y, adj_region.w, adj_region.h);
+}
+
+static void win_update_check_limitation(struct decon_device *decon,
+ struct decon_win_config *win_config,
+ struct decon_reg_data *regs)
+{
+ struct decon_win_config *config;
+ struct decon_win_rect update;
+ struct decon_rect r;
+ int i;
+ int sz_align = 1;
+ int adj_src_x = 0, adj_src_y = 0;
+
+ for (i = 0; i < decon->dt.max_win; i++) {
+ config = &win_config[i];
+ if (config->state == DECON_WIN_STATE_DISABLED)
+ continue;
+
+ r.left = config->dst.x;
+ r.top = config->dst.y;
+ r.right = config->dst.w + config->dst.x - 1;
+ r.bottom = config->dst.h + config->dst.y - 1;
+
+ if (!decon_intersect(®s->up_region, &r))
+ continue;
+
+ decon_intersection(®s->up_region, &r, &r);
+
+ if (!(r.right - r.left) && !(r.bottom - r.top))
+ continue;
+
+ if (is_yuv(config)) {
+ /* check alignment for NV12/NV21 format */
+ update.x = regs->up_region.left;
+ update.y = regs->up_region.top;
+ sz_align = 2;
+
+ if (update.y > config->dst.y)
+ adj_src_y = config->src.y + (update.y - config->dst.y);
+ if (update.x > config->dst.x)
+ adj_src_x = config->src.x + (update.x - config->dst.x);
+
+ if (adj_src_x & 0x1 || adj_src_y & 0x1)
+ goto change_full;
+ }
+
+ if (((r.right - r.left) < (SRC_WIDTH_MIN * sz_align)) ||
+ ((r.bottom - r.top) < (SRC_HEIGHT_MIN * sz_align))) {
+ goto change_full;
+ }
+
+ /* cursor async */
+ if (((r.right - r.left) > decon->lcd_info->xres) ||
+ ((r.bottom - r.top) > decon->lcd_info->yres)) {
+ goto change_full;
+ }
+ }
+
+ return;
+
+change_full:
+ DPU_DEBUG_WIN("changed full: win(%d) idma(%d) [%d %d %d %d]\n",
+ i, config->idma_type,
+ config->dst.x, config->dst.y,
+ config->dst.w, config->dst.h);
+ DPU_FULL_RECT(®s->up_region, decon->lcd_info);
+}
+
+static void win_update_reconfig_coordinates(struct decon_device *decon,
+ struct decon_win_config *win_config,
+ struct decon_reg_data *regs)
+{
+ struct decon_win_config *config;
+ struct decon_win_rect update;
+ struct decon_frame origin_dst, origin_src;
+ struct decon_rect r;
+ int i;
+
+ /* Assume that, window update doesn't support in case of scaling */
+ for (i = 0; i < decon->dt.max_win; i++) {
+ config = &win_config[i];
+
+ if (config->state == DECON_WIN_STATE_DISABLED)
+ continue;
+
+ r.left = config->dst.x;
+ r.top = config->dst.y;
+ r.right = r.left + config->dst.w - 1;
+ r.bottom = r.top + config->dst.h - 1;
+ if (!decon_intersect(®s->up_region, &r)) {
+ config->state = DECON_WIN_STATE_DISABLED;
+ continue;
+ }
+
+ update.x = regs->up_region.left;
+ update.y = regs->up_region.top;
+ update.w = regs->up_region.right - regs->up_region.left + 1;
+ update.h = regs->up_region.bottom - regs->up_region.top + 1;
+
+ memcpy(&origin_dst, &config->dst, sizeof(struct decon_frame));
+ memcpy(&origin_src, &config->src, sizeof(struct decon_frame));
+
+ /* reconfigure destination coordinates */
+ if (update.x > config->dst.x)
+ config->dst.w = min(update.w,
+ config->dst.x + config->dst.w - update.x);
+ else if (update.x + update.w < config->dst.x + config->dst.w)
+ config->dst.w = min(config->dst.w,
+ update.w + update.x - config->dst.x);
+
+ if (update.y > config->dst.y)
+ config->dst.h = min(update.h,
+ config->dst.y + config->dst.h - update.y);
+ else if (update.y + update.h < config->dst.y + config->dst.h)
+ config->dst.h = min(config->dst.h,
+ update.h + update.y - config->dst.y);
+ config->dst.x = max(config->dst.x - update.x, 0);
+ config->dst.y = max(config->dst.y - update.y, 0);
+
+ /* reconfigure source coordinates */
+ if (update.y > origin_dst.y)
+ config->src.y += (update.y - origin_dst.y);
+ if (update.x > origin_dst.x)
+ config->src.x += (update.x - origin_dst.x);
+ config->src.w = config->dst.w;
+ config->src.h = config->dst.h;
+
+ DPU_DEBUG_WIN("win(%d), idma(%d)\n", i, config->idma_type);
+ DPU_DEBUG_WIN("src: origin[%d %d %d %d] -> change[%d %d %d %d]\n",
+ origin_src.x, origin_src.y,
+ origin_src.w, origin_src.h,
+ config->src.x, config->src.y,
+ config->src.w, config->src.h);
+ DPU_DEBUG_WIN("dst: origin[%d %d %d %d] -> change[%d %d %d %d]\n",
+ origin_dst.x, origin_dst.y,
+ origin_dst.w, origin_dst.h,
+ config->dst.x, config->dst.y,
+ config->dst.w, config->dst.h);
+ }
+}
+
+static bool dpu_need_mres_config(struct decon_device *decon,
+ struct decon_win_config *win_config,
+ struct decon_reg_data *regs)
+{
+ struct decon_win_config *mres_config = &win_config[DECON_WIN_UPDATE_IDX];
+ struct lcd_res_info *supported_res;
+ int i;
+
+ regs->mres_update = false;
+
+ if (!decon->mres_enabled) {
+ DPU_DEBUG_MRES("multi-resolution feature is disabled\n");
+ goto end;
+ }
+
+ if (decon->dt.out_type != DECON_OUT_DSI) {
+ DPU_DEBUG_MRES("multi resolution only support DSI path\n");
+ goto end;
+ }
+
+ if (!decon->lcd_info->dt_lcd_mres.mres_en) {
+ DPU_DEBUG_MRES("panel doesn't support multi-resolution\n");
+ goto end;
+ }
+
+ if (!(mres_config->state & DECON_WIN_STATE_MRESOL))
+ goto end;
+
+ /* requested LCD resolution */
+ regs->lcd_width = mres_config->dst.f_w;
+ regs->lcd_height = mres_config->dst.f_h;
+
+ /* compare previous and requested LCD resolution */
+ if ((decon->lcd_info->xres == regs->lcd_width) &&
+ (decon->lcd_info->yres == regs->lcd_height)) {
+ DPU_DEBUG_MRES("prev & req LCD resolution is same(%d %d)\n",
+ regs->lcd_width, regs->lcd_height);
+ goto end;
+ }
+
+ /* match supported and requested LCD resolution */
+ for (i = 0; i < decon->lcd_info->dt_lcd_mres.mres_number; i++) {
+ supported_res = &decon->lcd_info->dt_lcd_mres.res_info[i];
+ if ((supported_res->width == regs->lcd_width) &&
+ (supported_res->height == regs->lcd_height)) {
+ regs->mres_update = true;
+ regs->mres_idx = i;
+ break;
+ }
+ }
+
+ DPU_DEBUG_MRES("update(%d), mode(%d), resolution(%d %d -> %d %d)\n",
+ regs->mres_update, regs->mres_idx,
+ decon->lcd_info->xres, decon->lcd_info->yres,
+ regs->lcd_width, regs->lcd_height);
+
+end:
+ return regs->mres_update;
+}
+
+void dpu_prepare_win_update_config(struct decon_device *decon,
+ struct decon_win_config_data *win_data,
+ struct decon_reg_data *regs)
+{
+ struct decon_win_config *win_config = win_data->config;
+ bool reconfigure = false;
+ struct decon_rect r;
+
+ if (!decon->win_up.enabled)
+ return;
+
+ if (decon->dt.out_type != DECON_OUT_DSI)
+ return;
+
+ /* If LCD resolution is changed, window update is ignored */
+ if (dpu_need_mres_config(decon, win_config, regs)) {
+ regs->up_region.left = 0;
+ regs->up_region.top = 0;
+ regs->up_region.right = regs->lcd_width - 1;
+ regs->up_region.bottom = regs->lcd_height - 1;
+ return;
+ }
+
+ /* find adjusted update region on LCD */
+ win_update_adjust_region(decon, win_config, regs);
+
+ /* check DPP hw limitation if violated, update region is changed to full */
+ win_update_check_limitation(decon, win_config, regs);
+
+ /*
+ * If update region is changed, need_update flag is set.
+ * That means hw configuration is needed
+ */
+ if (is_decon_rect_differ(&decon->win_up.prev_up_region, ®s->up_region))
+ regs->need_update = true;
+ else
+ regs->need_update = false;
+
+ /*
+ * If partial update region is requested, source and destination
+ * coordinates are needed to change if overlapped with update region.
+ */
+ DPU_FULL_RECT(&r, decon->lcd_info);
+ if (is_decon_rect_differ(®s->up_region, &r))
+ reconfigure = true;
+
+ if (regs->need_update || reconfigure) {
+ DPU_DEBUG_WIN("need_update(%d), reconfigure(%d)\n",
+ regs->need_update, reconfigure);
+ DPU_EVENT_LOG_WINUP_FLAGS(&decon->sd, regs->need_update, reconfigure);
+ }
+
+ /* Reconfigure source and destination coordinates, if needed. */
+ if (reconfigure)
+ win_update_reconfig_coordinates(decon, win_config, regs);
+}
+
+void dpu_set_mres_config(struct decon_device *decon, struct decon_reg_data *regs)
+{
+ struct dsim_device *dsim = get_dsim_drvdata(0);
+ struct lcd_mres_info *mres_info = &dsim->lcd_info.dt_lcd_mres;
+ struct decon_param p;
+ int idx;
+
+ if (!decon->mres_enabled) {
+ DPU_DEBUG_MRES("multi-resolution feature is disabled\n");
+ return;
+ }
+
+ if (decon->dt.out_type != DECON_OUT_DSI) {
+ DPU_DEBUG_MRES("multi resolution only support DSI path\n");
+ return;
+ }
+
+ if (!decon->lcd_info->dt_lcd_mres.mres_en) {
+ DPU_DEBUG_MRES("panel doesn't support multi-resolution\n");
+ return;
+ }
+
+ if (!regs->mres_update)
+ return;
+
+ if (IS_ERR_OR_NULL(dsim)) {
+ DPU_ERR_MRES("%s: dsim device ptr is invalid\n", __func__);
+ return;
+ }
+
+ /*
+ * Before LCD resolution is changed, previous frame data must be
+ * finished to transfer.
+ */
+ decon_reg_wait_idle_status_timeout(decon->id, IDLE_WAIT_TIMEOUT);
+
+ /* backup current LCD resolution information to previous one */
+ dsim->lcd_info.xres = regs->lcd_width;
+ dsim->lcd_info.yres = regs->lcd_height;
+ dsim->lcd_info.mres_mode = regs->mres_idx;
+ idx = regs->mres_idx;
+ dsim->lcd_info.dsc_enabled = mres_info->res_info[idx].dsc_en;
+ dsim->lcd_info.dsc_slice_h = mres_info->res_info[idx].dsc_height;
+
+ /* transfer LCD resolution change commands to panel */
+ dsim->panel_ops->mres(dsim, regs->mres_idx);
+
+ /* DECON and DSIM are reconfigured by changed LCD resolution */
+ dsim_reg_set_mres(dsim->id, &dsim->lcd_info);
+ decon_to_init_param(decon, &p);
+ decon_reg_set_mres(decon->id, &p);
+
+ /* If LCD resolution is changed, initial partial size is also changed */
+ dpu_init_win_update(decon);
+
+ DPU_DEBUG_MRES("changed LCD resolution(%d %d)\n",
+ decon->lcd_info->xres, decon->lcd_info->yres);
+}
+
+static int win_update_send_partial_command(struct dsim_device *dsim,
+ struct decon_rect *rect)
+{
+ char column[5];
+ char page[5];
+ int retry;
+
+ DPU_DEBUG_WIN("SET: [%d %d %d %d]\n", rect->left, rect->top,
+ rect->right - rect->left + 1, rect->bottom - rect->top + 1);
+
+ column[0] = MIPI_DCS_SET_COLUMN_ADDRESS;
+ column[1] = (rect->left >> 8) & 0xff;
+ column[2] = rect->left & 0xff;
+ column[3] = (rect->right >> 8) & 0xff;
+ column[4] = rect->right & 0xff;
+
+ page[0] = MIPI_DCS_SET_PAGE_ADDRESS;
+ page[1] = (rect->top >> 8) & 0xff;
+ page[2] = rect->top & 0xff;
+ page[3] = (rect->bottom >> 8) & 0xff;
+ page[4] = rect->bottom & 0xff;
+
+ retry = 2;
+ while (dsim_write_data(dsim, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned long)column, ARRAY_SIZE(column)) != 0) {
+ dsim_err("failed to write COLUMN_ADDRESS\n");
+ dsim_reg_function_reset(dsim->id);
+ if (--retry <= 0) {
+ dsim_err("COLUMN_ADDRESS is failed: exceed retry count\n");
+ return -EINVAL;
+ }
+ }
+
+ retry = 2;
+ while (dsim_write_data(dsim, MIPI_DSI_DCS_LONG_WRITE,
+ (unsigned long)page, ARRAY_SIZE(page)) != 0) {
+ dsim_err("failed to write PAGE_ADDRESS\n");
+ dsim_reg_function_reset(dsim->id);
+ if (--retry <= 0) {
+ dsim_err("PAGE_ADDRESS is failed: exceed retry count\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static void win_update_find_included_slice(struct decon_lcd *lcd,
+ struct decon_rect *rect, bool in_slice[])
+{
+ int slice_left, slice_right, slice_width;
+ int i;
+
+ slice_left = 0;
+ slice_right = 0;
+ slice_width = lcd->xres / lcd->dsc_slice_num;
+
+ for (i = 0; i < lcd->dsc_slice_num; ++i) {
+ slice_left = slice_width * i;
+ slice_right = slice_left + slice_width - 1;
+ in_slice[i] = false;
+
+ if ((slice_left >= rect->left) && (slice_right <= rect->right))
+ in_slice[i] = true;
+
+ DPU_DEBUG_WIN("slice_left(%d), right(%d)\n", slice_left, slice_right);
+ DPU_DEBUG_WIN("slice[%d] is %s\n", i,
+ in_slice[i] ? "included" : "not included");
+ }
+}
+
+static void win_update_set_partial_size(struct decon_device *decon,
+ struct decon_rect *rect)
+{
+ struct decon_lcd lcd_info;
+ struct dsim_device *dsim = get_dsim_drvdata(0);
+ bool in_slice[MAX_DSC_SLICE_CNT];
+
+ memcpy(&lcd_info, decon->lcd_info, sizeof(struct decon_lcd));
+ lcd_info.xres = rect->right - rect->left + 1;
+ lcd_info.yres = rect->bottom - rect->top + 1;
+
+ lcd_info.hfp = decon->lcd_info->hfp +
+ ((decon->lcd_info->xres - lcd_info.xres) >> 1);
+ lcd_info.vfp = decon->lcd_info->vfp + decon->lcd_info->yres - lcd_info.yres;
+
+ dsim_reg_set_partial_update(dsim->id, &lcd_info);
+
+ win_update_find_included_slice(decon->lcd_info, rect, in_slice);
+ decon_reg_set_partial_update(decon->id, decon->dt.dsi_mode,
+ decon->lcd_info, in_slice,
+ lcd_info.xres, lcd_info.yres);
+ DPU_DEBUG_WIN("SET: vfp %d vbp %d vsa %d hfp %d hbp %d hsa %d w %d h %d\n",
+ lcd_info.vfp, lcd_info.vbp, lcd_info.vsa,
+ lcd_info.hfp, lcd_info.hbp, lcd_info.hsa,
+ lcd_info.xres, lcd_info.yres);
+}
+
+void dpu_set_win_update_config(struct decon_device *decon,
+ struct decon_reg_data *regs)
+{
+ struct dsim_device *dsim = get_dsim_drvdata(0);
+ bool in_slice[MAX_DSC_SLICE_CNT];
+ bool full_partial_update = false;
+
+ if (!decon->win_up.enabled)
+ return;
+
+ if (decon->dt.out_type != DECON_OUT_DSI)
+ return;
+
+ if (regs == NULL) {
+ regs = kzalloc(sizeof(struct decon_reg_data), GFP_KERNEL);
+ if (!regs) {
+ decon_err("%s: reg_data allocation fail\n", __func__);
+ return;
+ }
+ DPU_FULL_RECT(®s->up_region, decon->lcd_info);
+ regs->need_update = true;
+ full_partial_update = true;
+ }
+
+ if (regs->need_update) {
+ win_update_find_included_slice(decon->lcd_info,
+ ®s->up_region, in_slice);
+
+ /* TODO: Is waiting framedone irq needed in KC ? */
+
+ /*
+ * hw configuration related to partial update must be set
+ * without DMA operation
+ */
+ decon_reg_wait_idle_status_timeout(decon->id, IDLE_WAIT_TIMEOUT);
+ win_update_send_partial_command(dsim, ®s->up_region);
+ win_update_set_partial_size(decon, ®s->up_region);
+ DPU_EVENT_LOG_APPLY_REGION(&decon->sd, ®s->up_region);
+ }
+
+ if (full_partial_update)
+ kfree(regs);
+}
+
+void dpu_set_win_update_partial_size(struct decon_device *decon,
+ struct decon_rect *up_region)
+{
+ if (!decon->win_up.enabled)
+ return;
+
+ win_update_set_partial_size(decon, up_region);
+}
+
+void dpu_init_win_update(struct decon_device *decon)
+{
+ struct decon_lcd *lcd = decon->lcd_info;
+
+ decon->win_up.enabled = false;
+ decon->cursor.xpos = lcd->xres / 2;
+ decon->cursor.ypos = lcd->yres / 2;
+
+ if (!IS_ENABLED(CONFIG_EXYNOS_WINDOW_UPDATE)) {
+ decon_info("window update feature is disabled\n");
+ return;
+ }
+
+ if (decon->dt.out_type != DECON_OUT_DSI) {
+ decon_info("out_type(%d) doesn't support window update\n",
+ decon->dt.out_type);
+ return;
+ }
+
+ if (lcd->dsc_enabled) {
+ decon->win_up.rect_w = lcd->xres / lcd->dsc_slice_num;
+ decon->win_up.rect_h = lcd->dsc_slice_h;
+ } else {
+ decon->win_up.rect_w = MIN_WIN_BLOCK_WIDTH;
+ decon->win_up.rect_h = MIN_WIN_BLOCK_HEIGHT;
+ }
+
+ DPU_FULL_RECT(&decon->win_up.prev_up_region, lcd);
+
+ decon->win_up.hori_cnt = decon->lcd_info->xres / decon->win_up.rect_w;
+ if (decon->lcd_info->xres - decon->win_up.hori_cnt * decon->win_up.rect_w) {
+ decon_warn("%s: parameters is wrong. lcd w(%d), win rect w(%d)\n",
+ __func__, decon->lcd_info->xres,
+ decon->win_up.rect_w);
+ return;
+ }
+
+ decon->win_up.verti_cnt = decon->lcd_info->yres / decon->win_up.rect_h;
+ if (decon->lcd_info->yres - decon->win_up.verti_cnt * decon->win_up.rect_h) {
+ decon_warn("%s: parameters is wrong. lcd h(%d), win rect h(%d)\n",
+ __func__, decon->lcd_info->yres,
+ decon->win_up.rect_h);
+ return;
+ }
+
+ decon_info("window update is enabled: win rectangle w(%d), h(%d)\n",
+ decon->win_up.rect_w, decon->win_up.rect_h);
+ decon_info("horizontal count(%d), vertical count(%d)\n",
+ decon->win_up.hori_cnt, decon->win_up.verti_cnt);
+
+ decon->win_up.enabled = true;
+
+ decon->mres_enabled = false;
+ if (!IS_ENABLED(CONFIG_EXYNOS_MULTIRESOLUTION)) {
+ decon_info("multi-resolution feature is disabled\n");
+ return;
+ }
+ /* TODO: will be printed supported resolution list */
+ decon_info("multi-resolution feature is enabled\n");
+ decon->mres_enabled = true;
+}