kernel/0013-feat-Add-VO-support-for-linux-6.6.patch
2024-12-19 16:34:44 -05:00

18486 lines
504 KiB
Diff

From a2a86f43fbbd34d7f2ba5fee2db45b709d97e122 Mon Sep 17 00:00:00 2001
From: fanglifei <fanglifei@eswincomputing.com>
Date: Tue, 21 May 2024 15:57:24 +0800
Subject: [PATCH 013/219] feat: Add VO support for linux 6.6
Changelogs:
1. Add support for DC8000 and virtual display
---
arch/riscv/boot/dts/Makefile | 1 +
arch/riscv/configs/win2030_defconfig | 4 +-
drivers/gpu/drm/Kconfig | 2 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/eswin/Kconfig | 72 +
drivers/gpu/drm/eswin/Makefile | 23 +
drivers/gpu/drm/eswin/dw_hdmi.c | 4617 +++++++++++++++++
drivers/gpu/drm/eswin/dw_hdmi.h | 1408 +++++
drivers/gpu/drm/eswin/dw_hdmi_audio.h | 23 +
drivers/gpu/drm/eswin/dw_hdmi_cec.c | 334 ++
drivers/gpu/drm/eswin/dw_hdmi_cec.h | 18 +
drivers/gpu/drm/eswin/dw_hdmi_hdcp.c | 1055 ++++
drivers/gpu/drm/eswin/dw_hdmi_hdcp.h | 71 +
drivers/gpu/drm/eswin/dw_hdmi_hdcp2.c | 775 +++
drivers/gpu/drm/eswin/dw_hdmi_i2s_audio.c | 237 +
drivers/gpu/drm/eswin/es_crtc.c | 436 ++
drivers/gpu/drm/eswin/es_crtc.h | 71 +
drivers/gpu/drm/eswin/es_dc.c | 1083 ++++
drivers/gpu/drm/eswin/es_dc.h | 48 +
drivers/gpu/drm/eswin/es_dc_hw.c | 1614 ++++++
drivers/gpu/drm/eswin/es_dc_hw.h | 473 ++
drivers/gpu/drm/eswin/es_dc_mmu.c | 778 +++
drivers/gpu/drm/eswin/es_dc_mmu.h | 100 +
drivers/gpu/drm/eswin/es_drm.h | 23 +
drivers/gpu/drm/eswin/es_drv.c | 517 ++
drivers/gpu/drm/eswin/es_drv.h | 71 +
drivers/gpu/drm/eswin/es_fb.c | 141 +
drivers/gpu/drm/eswin/es_fb.h | 13 +
drivers/gpu/drm/eswin/es_gem.c | 598 +++
drivers/gpu/drm/eswin/es_gem.h | 76 +
drivers/gpu/drm/eswin/es_plane.c | 399 ++
drivers/gpu/drm/eswin/es_plane.h | 87 +
drivers/gpu/drm/eswin/es_simple_enc.c | 266 +
drivers/gpu/drm/eswin/es_simple_enc.h | 27 +
drivers/gpu/drm/eswin/es_type.h | 58 +
drivers/gpu/drm/eswin/es_virtual.c | 1310 +++++
drivers/gpu/drm/eswin/es_virtual.h | 34 +
drivers/gpu/drm/eswin/eswin_dw_hdmi.c | 1129 ++++
.../gpu/drm/eswin/host_lib_driver_linux_if.h | 146 +
39 files changed, 18138 insertions(+), 1 deletion(-)
create mode 100644 drivers/gpu/drm/eswin/Kconfig
create mode 100644 drivers/gpu/drm/eswin/Makefile
create mode 100644 drivers/gpu/drm/eswin/dw_hdmi.c
create mode 100644 drivers/gpu/drm/eswin/dw_hdmi.h
create mode 100644 drivers/gpu/drm/eswin/dw_hdmi_audio.h
create mode 100644 drivers/gpu/drm/eswin/dw_hdmi_cec.c
create mode 100644 drivers/gpu/drm/eswin/dw_hdmi_cec.h
create mode 100644 drivers/gpu/drm/eswin/dw_hdmi_hdcp.c
create mode 100644 drivers/gpu/drm/eswin/dw_hdmi_hdcp.h
create mode 100644 drivers/gpu/drm/eswin/dw_hdmi_hdcp2.c
create mode 100644 drivers/gpu/drm/eswin/dw_hdmi_i2s_audio.c
create mode 100644 drivers/gpu/drm/eswin/es_crtc.c
create mode 100644 drivers/gpu/drm/eswin/es_crtc.h
create mode 100644 drivers/gpu/drm/eswin/es_dc.c
create mode 100644 drivers/gpu/drm/eswin/es_dc.h
create mode 100644 drivers/gpu/drm/eswin/es_dc_hw.c
create mode 100644 drivers/gpu/drm/eswin/es_dc_hw.h
create mode 100644 drivers/gpu/drm/eswin/es_dc_mmu.c
create mode 100644 drivers/gpu/drm/eswin/es_dc_mmu.h
create mode 100644 drivers/gpu/drm/eswin/es_drm.h
create mode 100644 drivers/gpu/drm/eswin/es_drv.c
create mode 100644 drivers/gpu/drm/eswin/es_drv.h
create mode 100644 drivers/gpu/drm/eswin/es_fb.c
create mode 100644 drivers/gpu/drm/eswin/es_fb.h
create mode 100644 drivers/gpu/drm/eswin/es_gem.c
create mode 100644 drivers/gpu/drm/eswin/es_gem.h
create mode 100644 drivers/gpu/drm/eswin/es_plane.c
create mode 100644 drivers/gpu/drm/eswin/es_plane.h
create mode 100644 drivers/gpu/drm/eswin/es_simple_enc.c
create mode 100644 drivers/gpu/drm/eswin/es_simple_enc.h
create mode 100644 drivers/gpu/drm/eswin/es_type.h
create mode 100644 drivers/gpu/drm/eswin/es_virtual.c
create mode 100644 drivers/gpu/drm/eswin/es_virtual.h
create mode 100644 drivers/gpu/drm/eswin/eswin_dw_hdmi.c
create mode 100644 drivers/gpu/drm/eswin/host_lib_driver_linux_if.h
diff --git a/arch/riscv/boot/dts/Makefile b/arch/riscv/boot/dts/Makefile
index f60a280abb15..5c3eda88e8af 100644
--- a/arch/riscv/boot/dts/Makefile
+++ b/arch/riscv/boot/dts/Makefile
@@ -6,5 +6,6 @@ subdir-y += renesas
subdir-y += sifive
subdir-y += starfive
subdir-y += thead
+subdir-y += eswin
obj-$(CONFIG_BUILTIN_DTB) := $(addsuffix /, $(subdir-y))
diff --git a/arch/riscv/configs/win2030_defconfig b/arch/riscv/configs/win2030_defconfig
index 6733030403b9..dab66c993601 100644
--- a/arch/riscv/configs/win2030_defconfig
+++ b/arch/riscv/configs/win2030_defconfig
@@ -167,6 +167,9 @@ CONFIG_DRM_NOUVEAU=y
CONFIG_DRM_DISPLAY_CONNECTOR=y
CONFIG_DRM_SIMPLE_BRIDGE=y
CONFIG_DRM_TOSHIBA_TC358768=m
+CONFIG_DRM_ESWIN=y
+CONFIG_ESWIN_VIRTUAL_DISPLAY=y
+CONFIG_ESWIN_MMU=y
CONFIG_DRM_LEGACY=y
CONFIG_FB=y
CONFIG_SOUND=y
@@ -277,7 +280,6 @@ CONFIG_CONSOLE_LOGLEVEL_DEFAULT=15
CONFIG_CONSOLE_LOGLEVEL_QUIET=15
CONFIG_MESSAGE_LOGLEVEL_DEFAULT=7
CONFIG_DYNAMIC_DEBUG=y
-CONFIG_DEBUG_FS=y
CONFIG_DEBUG_PAGEALLOC=y
CONFIG_SCHED_STACK_END_CHECK=y
CONFIG_DEBUG_VM=y
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index ec4abf9ff47b..fb30fa9500b3 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -388,6 +388,8 @@ source "drivers/gpu/drm/solomon/Kconfig"
source "drivers/gpu/drm/sprd/Kconfig"
+source "drivers/gpu/drm/eswin/Kconfig"
+
config DRM_HYPERV
tristate "DRM Support for Hyper-V synthetic video device"
depends on DRM && PCI && MMU && HYPERV
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 215e78e79125..a2b855de24bf 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -198,3 +198,4 @@ obj-$(CONFIG_DRM_HYPERV) += hyperv/
obj-y += solomon/
obj-$(CONFIG_DRM_SPRD) += sprd/
obj-$(CONFIG_DRM_LOONGSON) += loongson/
+obj-$(CONFIG_DRM_ESWIN) += eswin/
diff --git a/drivers/gpu/drm/eswin/Kconfig b/drivers/gpu/drm/eswin/Kconfig
new file mode 100644
index 000000000000..6a7771e6ac29
--- /dev/null
+++ b/drivers/gpu/drm/eswin/Kconfig
@@ -0,0 +1,72 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config DRM_ESWIN
+ tristate "DRM Support for Eswin"
+ depends on DRM
+ select DRM_KMS_HELPER
+ help
+ Choose this option if you have a Eswin soc chipset.
+ This driver provides Eswin kernel mode
+ setting and buffer management. It does not
+ provide 2D or 3D acceleration.
+
+config ESWIN_VIRTUAL_DISPLAY
+ bool "display content output to debugfs file"
+ depends on DRM_ESWIN
+ select DEBUG_FS
+ help
+ This is a debug feature which capture video content output
+ from display controller. Output path is debugfs/dri/connector/.
+ The content format is ARGB which Alpha is 0 for 8bits.
+ Disabled in default.
+
+config ESWIN_MMU
+ bool "MMU support for Eswin display controller"
+ depends on DRM_ESWIN
+ help
+ This is a memory management function which translate virtual address
+ to physical address. DPU MMU only do address translate, doesn't
+ support security and shareable.
+
+config ESWIN_DW_HDMI
+ bool "ESWIN specific extensions for Synopsys DW HDMI"
+ depends on DRM_ESWIN
+ select CEC_CORE if CEC_NOTIFIER
+ select DW_HDMI
+ help
+ This selects support for ESWIN SoC specific extensions
+ for the Synopsys DesignWare HDMI driver. If you want to
+ enable HDMI on win2030 based SoC, you should select
+ this option.
+
+config DW_HDMI_I2S_AUDIO
+ bool "Synopsys Designware I2S Audio interface"
+ depends on SND_SOC
+ depends on ESWIN_DW_HDMI
+ select SND_SOC_HDMI_CODEC
+ help
+ Support the I2S Audio interface which is part of the Synopsys
+ Designware HDMI block.
+
+config DW_HDMI_CEC
+ bool "Synopsis Designware CEC interface"
+ depends on ESWIN_DW_HDMI
+ select CEC_CORE
+ select CEC_NOTIFIER
+ help
+ Support the CEC interface which is part of the Synopsys
+ Designware HDMI block.
+
+config DW_HDMI_HDCP
+ bool "Synopsis Designware HDCP interface"
+ depends on ESWIN_DW_HDMI
+ help
+ Support the HDCP interface which is part of the Synopsys
+ Designware HDMI block.
+
+config DW_HDMI_HDCP2
+ tristate "Synopsis Designware HDCP2 interface"
+ select DRM_ESWIN
+ help
+ Support the HDCP2 interface which is part of the Synopsys
+ Designware HDMI block.
diff --git a/drivers/gpu/drm/eswin/Makefile b/drivers/gpu/drm/eswin/Makefile
new file mode 100644
index 000000000000..e84e28f388d6
--- /dev/null
+++ b/drivers/gpu/drm/eswin/Makefile
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: GPL-2.0
+
+es_drm-objs := es_dc_hw.o \
+ es_dc.o \
+ es_crtc.o \
+ es_drv.o \
+ es_fb.o \
+ es_gem.o \
+ es_plane.o \
+ es_simple_enc.o
+
+es_drm-$(CONFIG_ESWIN_VIRTUAL_DISPLAY) += es_virtual.o
+es_drm-$(CONFIG_ESWIN_MMU) += es_dc_mmu.o
+es_drm-$(CONFIG_ESWIN_DW_HDMI) += eswin_dw_hdmi.o dw_hdmi.o
+
+es_drm-$(CONFIG_DW_HDMI_I2S_AUDIO) += dw_hdmi_i2s_audio.o
+es_drm-$(CONFIG_DW_HDMI_CEC) += dw_hdmi_cec.o
+es_drm-$(CONFIG_DW_HDMI_HDCP) += dw_hdmi_hdcp.o
+
+dw_hdcp2-$(CONFIG_DW_HDMI_HDCP2) += dw_hdmi_hdcp2.o
+
+obj-$(CONFIG_DRM_ESWIN) += es_drm.o
+obj-$(CONFIG_DW_HDMI_HDCP2) += dw_hdcp2.o
\ No newline at end of file
diff --git a/drivers/gpu/drm/eswin/dw_hdmi.c b/drivers/gpu/drm/eswin/dw_hdmi.c
new file mode 100644
index 000000000000..ae13cbcb6e29
--- /dev/null
+++ b/drivers/gpu/drm/eswin/dw_hdmi.c
@@ -0,0 +1,4617 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * DesignWare High-Definition Multimedia Interface (HDMI) driver
+ *
+ * Copyright (C) 2013-2015 Mentor Graphics Inc.
+ * Copyright (C) 2011-2013 Freescale Semiconductor, Inc.
+ * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/hdmi.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/regmap.h>
+#include <linux/dma-mapping.h>
+#include <linux/spinlock.h>
+
+#include <media/cec-notifier.h>
+
+#include <uapi/linux/media-bus-format.h>
+#include <uapi/linux/videodev2.h>
+#include <linux/reset.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_scdc_helper.h>
+
+#include "dw_hdmi_audio.h"
+#include "dw_hdmi_cec.h"
+#include "dw_hdmi.h"
+#include "dw_hdmi_hdcp.h"
+
+#define DDC_CI_ADDR 0x37
+#define DDC_SEGMENT_ADDR 0x30
+
+#define HDMI_EDID_LEN 512
+
+/* DW-HDMI Controller >= 0x200a are at least compliant with SCDC version 1 */
+#define SCDC_MIN_SOURCE_VERSION 0x1
+
+#define HDMI14_MAX_TMDSCLK 340000000
+
+static bool hpd_flag = false;
+
+enum hdmi_datamap {
+ RGB444_8B = 0x01,
+ RGB444_10B = 0x03,
+ RGB444_12B = 0x05,
+ RGB444_16B = 0x07,
+ YCbCr444_8B = 0x09,
+ YCbCr444_10B = 0x0B,
+ YCbCr444_12B = 0x0D,
+ YCbCr444_16B = 0x0F,
+ YCbCr422_8B = 0x16,
+ YCbCr422_10B = 0x14,
+ YCbCr422_12B = 0x12,
+};
+
+static const u16 csc_coeff_default[3][4] = { { 0x2000, 0x0000, 0x0000, 0x0000 },
+ { 0x0000, 0x2000, 0x0000, 0x0000 },
+ { 0x0000, 0x0000, 0x2000,
+ 0x0000 } };
+
+static const u16 csc_coeff_rgb_out_eitu601[3][4] = {
+ { 0x2000, 0x6926, 0x74fd, 0x010e },
+ { 0x2000, 0x2cdd, 0x0000, 0x7e9a },
+ { 0x2000, 0x0000, 0x38b4, 0x7e3b }
+};
+
+static const u16 csc_coeff_rgb_out_eitu709[3][4] = {
+ { 0x2000, 0x7106, 0x7a02, 0x00a7 },
+ { 0x2000, 0x3264, 0x0000, 0x7e6d },
+ { 0x2000, 0x0000, 0x3b61, 0x7e25 }
+};
+
+static const u16 csc_coeff_rgb_in_eitu601[3][4] = {
+ { 0x2591, 0x1322, 0x074b, 0x0000 },
+ { 0x6535, 0x2000, 0x7acc, 0x0200 },
+ { 0x6acd, 0x7534, 0x2000, 0x0200 }
+};
+
+static const u16 csc_coeff_rgb_in_eitu709[3][4] = {
+ { 0x2dc5, 0x0d9b, 0x049e, 0x0000 },
+ { 0x62f0, 0x2000, 0x7d11, 0x0200 },
+ { 0x6756, 0x78ab, 0x2000, 0x0200 }
+};
+
+static const u16 csc_coeff_rgb_full_to_rgb_limited[3][4] = {
+ { 0x1b7c, 0x0000, 0x0000, 0x0020 },
+ { 0x0000, 0x1b7c, 0x0000, 0x0020 },
+ { 0x0000, 0x0000, 0x1b7c, 0x0020 }
+};
+
+static const u16 csc_coeff_rgb_to_yuv_8bit_eitu601[3][4] = {
+ { 0x2591, 0x1323, 0x074c, 0x0000 },
+ { 0xe534, 0x2000, 0xfacc, 0x0200 },
+ { 0xeacd, 0xf533, 0x2000, 0x0200 }
+};
+
+static const u16 csc_coeff_rgb_to_yuv_8bit_eitu709[3][4] = {
+ { 0x2dc6, 0x0d9b, 0x049f, 0x0000 },
+ { 0xe2ef, 0x2000, 0xfd11, 0x0200 },
+ { 0xe755, 0xf8ab, 0x2000, 0x0200 }
+};
+
+static const u16 csc_coeff_rgb_to_yuv_10bit_eitu601[3][4] = {
+ { 0x2591, 0x1323, 0x074c, 0x0000 },
+ { 0xe534, 0x2000, 0xfacc, 0x0800 },
+ { 0xeacd, 0xf533, 0x2000, 0x0800 }
+};
+
+static const u16 csc_coeff_rgb_to_yuv_10bit_eitu709[3][4] = {
+ { 0x2dc6, 0x0d9b, 0x049f, 0x0000 },
+ { 0xe2ef, 0x2000, 0xfd11, 0x0800 },
+ { 0xe755, 0xf8ab, 0x2000, 0x0800 }
+};
+
+struct hdmi_vmode {
+ bool mdataenablepolarity;
+ unsigned int previous_pixelclock;
+ unsigned int mpixelclock;
+ unsigned int mpixelrepetitioninput;
+ unsigned int mpixelrepetitionoutput;
+ unsigned int previous_tmdsclock;
+ unsigned int mtmdsclock;
+};
+
+struct hdmi_data_info {
+ unsigned int enc_in_bus_format;
+ unsigned int enc_out_bus_format;
+ unsigned int enc_in_encoding;
+ unsigned int enc_out_encoding;
+ unsigned int pix_repet_factor;
+ unsigned int hdcp_enable;
+ struct hdmi_vmode video_mode;
+ bool rgb_limited_range;
+};
+
+struct dw_hdmi_i2c {
+ struct i2c_adapter adap;
+
+ struct mutex lock; /* used to serialize data transfers */
+ struct completion cmp;
+ u8 stat;
+
+ u8 slave_reg;
+ bool is_regaddr;
+ bool is_segment;
+ unsigned int scl_high_ns;
+ unsigned int scl_low_ns;
+};
+
+struct dw_hdmi_phy_data {
+ enum dw_hdmi_phy_type type;
+ const char *name;
+ unsigned int gen;
+ bool has_svsret;
+ int (*configure)(struct dw_hdmi *hdmi,
+ const struct dw_hdmi_plat_data *pdata,
+ unsigned long mpixelclock);
+};
+
+struct dw_hdmi {
+ struct drm_connector connector;
+ struct drm_bridge bridge;
+ struct drm_bridge *next_bridge;
+ unsigned int version;
+
+ struct platform_device *hdcp_dev;
+ struct platform_device *audio;
+ struct platform_device *cec;
+ struct device *dev;
+ struct clk *isfr_clk;
+ struct clk *hclk_vio;
+ struct clk *iahb_clk;
+ struct clk *cec_clk;
+ struct dw_hdmi_i2c *i2c;
+
+ struct hdmi_data_info hdmi_data;
+ const struct dw_hdmi_plat_data *plat_data;
+ struct dw_hdcp *hdcp;
+ int vic;
+
+ u8 edid[HDMI_EDID_LEN];
+
+ struct {
+ const struct dw_hdmi_phy_ops *ops;
+ const char *name;
+ void *data;
+ bool enabled;
+ } phy;
+
+ struct drm_display_mode previous_mode;
+
+ struct i2c_adapter *ddc;
+ void __iomem *regs;
+ bool sink_is_hdmi;
+ bool sink_has_audio;
+ bool hpd_state;
+
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *default_state;
+ struct pinctrl_state *unwedge_state;
+
+ struct mutex mutex; /* for state below and previous_mode */
+ enum drm_connector_force force; /* mutex-protected force state */
+ struct drm_connector
+ *curr_conn; /* current connector (only valid when !disabled) */
+ bool disabled; /* DRM has disabled our bridge */
+ bool bridge_is_on; /* indicates the bridge is on */
+ bool rxsense; /* rxsense state */
+ u8 phy_mask; /* desired phy int mask settings */
+ u8 mc_clkdis; /* clock disable register */
+
+ spinlock_t audio_lock;
+ struct mutex audio_mutex;
+ struct dentry *debugfs_dir;
+ unsigned int sample_rate;
+ unsigned int audio_cts;
+ unsigned int audio_n;
+ bool audio_enable;
+
+ unsigned int reg_shift;
+ struct regmap *regm;
+ void (*enable_audio)(struct dw_hdmi *hdmi);
+ void (*disable_audio)(struct dw_hdmi *hdmi);
+
+ struct mutex cec_notifier_mutex;
+ struct cec_notifier *cec_notifier;
+
+ hdmi_codec_plugged_cb plugged_cb;
+ struct device *codec_dev;
+ enum drm_connector_status last_connector_result;
+
+ int irq;
+ struct delayed_work work;
+ struct workqueue_struct *workqueue;
+ struct reset_control *rst_hdmi_prstn;
+ struct reset_control *rst_hdmi_phyrstn;
+ struct reset_control *rst_hdmi_rstn;
+
+ u32 eswin_plat;
+};
+
+#define HDMI_IH_PHY_STAT0_RX_SENSE \
+ (HDMI_IH_PHY_STAT0_RX_SENSE0 | HDMI_IH_PHY_STAT0_RX_SENSE1 | \
+ HDMI_IH_PHY_STAT0_RX_SENSE2 | HDMI_IH_PHY_STAT0_RX_SENSE3)
+
+#define HDMI_PHY_RX_SENSE \
+ (HDMI_PHY_RX_SENSE0 | HDMI_PHY_RX_SENSE1 | HDMI_PHY_RX_SENSE2 | \
+ HDMI_PHY_RX_SENSE3)
+
+static struct edid *dw_hdmi_get_edid(struct dw_hdmi *hdmi,
+ struct drm_connector *connector);
+static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi,
+ const struct drm_display_mode *mode);
+
+static inline void hdmi_writeb(struct dw_hdmi *hdmi, u8 val, int offset)
+{
+ int ret = regmap_write(hdmi->regm, offset << hdmi->reg_shift, val);
+ if (ret != 0) {
+ dev_err(hdmi->dev,
+ "hdmi reg write error, reg:0x%x, val:0x%x, ret:%d\n",
+ offset, val, ret);
+ }
+}
+
+static inline u8 hdmi_readb(struct dw_hdmi *hdmi, int offset)
+{
+ unsigned int val = 0;
+
+ int ret = regmap_read(hdmi->regm, offset << hdmi->reg_shift, &val);
+ if (ret != 0) {
+ dev_err(hdmi->dev,
+ "hdmi reg read error, reg:0x%x, val:0x%x, ret:%d\n",
+ offset, val, ret);
+ }
+
+ return val;
+}
+
+static void handle_plugged_change(struct dw_hdmi *hdmi, bool plugged)
+{
+ if (hdmi->plugged_cb && hdmi->codec_dev)
+ hdmi->plugged_cb(hdmi->codec_dev, plugged);
+}
+
+int dw_hdmi_set_plugged_cb(struct dw_hdmi *hdmi, hdmi_codec_plugged_cb fn,
+ struct device *codec_dev)
+{
+ bool plugged;
+
+ mutex_lock(&hdmi->mutex);
+ hdmi->plugged_cb = fn;
+ hdmi->codec_dev = codec_dev;
+ plugged = hdmi->last_connector_result == connector_status_connected;
+ handle_plugged_change(hdmi, plugged);
+ mutex_unlock(&hdmi->mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_set_plugged_cb);
+
+static void hdmi_modb(struct dw_hdmi *hdmi, u8 data, u8 mask, unsigned reg)
+{
+ regmap_update_bits(hdmi->regm, reg << hdmi->reg_shift, mask, data);
+}
+
+static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg,
+ u8 shift, u8 mask)
+{
+ hdmi_modb(hdmi, data << shift, mask, reg);
+}
+
+static void repo_hpd_event(struct work_struct *p_work)
+{
+ struct dw_hdmi *hdmi = container_of(p_work, struct dw_hdmi, work.work);
+ u8 phy_stat = hdmi_readb(hdmi, HDMI_PHY_STAT0);
+
+ mutex_lock(&hdmi->mutex);
+ if (!(phy_stat & HDMI_PHY_RX_SENSE))
+ hdmi->rxsense = false;
+ if (phy_stat & HDMI_PHY_HPD)
+ hdmi->rxsense = true;
+ mutex_unlock(&hdmi->mutex);
+
+ if (hdmi->bridge.dev) {
+ bool change;
+
+ change = drm_helper_hpd_irq_event(hdmi->bridge.dev);
+ if (change) {
+#ifdef CONFIG_CEC_NOTIFIER
+ cec_notifier_repo_cec_hpd(hdmi->cec_notifier,
+ hdmi->hpd_state, ktime_get());
+#endif
+ if (hdmi->hpd_state) {
+#ifdef CONFIG_CEC_NOTIFIER
+ struct edid *edid;
+ edid = dw_hdmi_get_edid(hdmi, &hdmi->connector);
+ if (!edid)
+ return;
+ drm_connector_update_edid_property(
+ &hdmi->connector, edid);
+ cec_notifier_set_phys_addr_from_edid(
+ hdmi->cec_notifier, edid);
+ kfree(edid);
+#endif
+ }
+ }
+ }
+
+ msleep(150);
+ if (hdmi->hpd_state) {
+ if (hdmi->hdcp && hdmi->hdcp->hdcp2 &&
+ hdmi->hdcp->hdcp2->enable &&
+ (tv_hdmi_hdcp2_support(hdmi)) == 1) {
+ hdmi->hdcp->hdcp2->start();
+ } else {
+ hdmi_tx_hdcp_config(hdmi, &hdmi->previous_mode);
+ }
+ }
+}
+
+static bool check_hdmi_irq(struct dw_hdmi *hdmi, int intr_stat, int phy_int_pol)
+{
+ int msecs;
+
+ /* To determine whether interrupt type is HPD */
+ if (!(intr_stat & HDMI_IH_PHY_STAT0_HPD))
+ return false;
+
+ if (phy_int_pol & HDMI_PHY_HPD) {
+ dev_info(hdmi->dev, "dw hdmi plug in\n");
+ msecs = 100;
+ hdmi->hpd_state = true;
+ } else {
+ dev_info(hdmi->dev, "dw hdmi plug out\n");
+ msecs = 1000;
+ hdmi->hpd_state = false;
+ if (hdmi->hdcp && hdmi->hdcp->hdcp2 &&
+ hdmi->hdcp->hdcp2->enable) {
+ hdmi->hdcp->hdcp2->stop();
+ }
+ if (hdmi->hdcp && hdmi->hdcp->hdcp_stop) {
+ hdmi->hdcp->hdcp_stop(hdmi->hdcp);
+ }
+ }
+ mod_delayed_work(hdmi->workqueue, &hdmi->work, msecs_to_jiffies(msecs));
+
+ return true;
+}
+
+static void init_hpd_work(struct dw_hdmi *hdmi)
+{
+ hdmi->workqueue = create_workqueue("hpd_queue");
+ INIT_DELAYED_WORK(&hdmi->work, repo_hpd_event);
+}
+
+static void dw_hdmi_i2c_set_divs(struct dw_hdmi *hdmi)
+{
+ unsigned long clk_rate_khz;
+ unsigned long low_ns, high_ns;
+ unsigned long div_low, div_high;
+
+ /* Standard-mode */
+ if (hdmi->i2c->scl_high_ns < 4000)
+ high_ns = 4708;
+ else
+ high_ns = hdmi->i2c->scl_high_ns;
+
+ if (hdmi->i2c->scl_low_ns < 4700)
+ low_ns = 4916;
+ else
+ low_ns = hdmi->i2c->scl_low_ns;
+
+ /* Adjust to avoid overflow */
+ clk_rate_khz = DIV_ROUND_UP(clk_get_rate(hdmi->isfr_clk), 1000);
+
+ div_low = (clk_rate_khz * low_ns) / 1000000;
+ if ((clk_rate_khz * low_ns) % 1000000)
+ div_low++;
+
+ div_high = (clk_rate_khz * high_ns) / 1000000;
+ if ((clk_rate_khz * high_ns) % 1000000)
+ div_high++;
+
+ /* Maximum divider supported by hw is 0xffff */
+ if (div_low > 0xffff)
+ div_low = 0xffff;
+
+ if (div_high > 0xffff)
+ div_high = 0xffff;
+
+ hdmi_writeb(hdmi, div_high & 0xff, HDMI_I2CM_SS_SCL_HCNT_0_ADDR);
+ hdmi_writeb(hdmi, (div_high >> 8) & 0xff, HDMI_I2CM_SS_SCL_HCNT_1_ADDR);
+ hdmi_writeb(hdmi, div_low & 0xff, HDMI_I2CM_SS_SCL_LCNT_0_ADDR);
+ hdmi_writeb(hdmi, (div_low >> 8) & 0xff, HDMI_I2CM_SS_SCL_LCNT_1_ADDR);
+ if (!hdmi->eswin_plat) {
+ hdmi_writeb(hdmi, 0x13, HDMI_I2CM_SS_SCL_HCNT_0_ADDR);
+ hdmi_writeb(hdmi, 0x00, HDMI_I2CM_SS_SCL_HCNT_1_ADDR);
+ hdmi_writeb(hdmi, 0x16, HDMI_I2CM_SS_SCL_LCNT_0_ADDR);
+ hdmi_writeb(hdmi, 0x00, HDMI_I2CM_SS_SCL_LCNT_1_ADDR);
+ }
+}
+
+static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi)
+{
+ hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL,
+ HDMI_PHY_I2CM_INT_ADDR);
+
+ hdmi_writeb(hdmi,
+ HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL |
+ HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL,
+ HDMI_PHY_I2CM_CTLINT_ADDR);
+
+ /* Software reset */
+ hdmi_writeb(hdmi, 0x00, HDMI_I2CM_SOFTRSTZ);
+
+ /* Set Standard Mode speed (determined to be 100KHz on iMX6) */
+ hdmi_writeb(hdmi, 0x00, HDMI_I2CM_DIV);
+
+ /* Set done, not acknowledged and arbitration interrupt polarities */
+ hdmi_writeb(hdmi, HDMI_I2CM_INT_DONE_POL, HDMI_I2CM_INT);
+ hdmi_writeb(hdmi, HDMI_I2CM_CTLINT_NAC_POL | HDMI_I2CM_CTLINT_ARB_POL,
+ HDMI_I2CM_CTLINT);
+
+ /* Clear DONE and ERROR interrupts */
+ hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE,
+ HDMI_IH_I2CM_STAT0);
+
+ /* Mute DONE and ERROR interrupts */
+ hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE,
+ HDMI_IH_MUTE_I2CM_STAT0);
+
+ /* set SDA high level holding time */
+ hdmi_writeb(hdmi, 0x48, HDMI_I2CM_SDA_HOLD);
+ if (!hdmi->eswin_plat) {
+ hdmi_writeb(hdmi, 0x0f, HDMI_I2CM_SDA_HOLD);
+ }
+ dw_hdmi_i2c_set_divs(hdmi);
+}
+
+static bool dw_hdmi_i2c_unwedge(struct dw_hdmi *hdmi)
+{
+ /* If no unwedge state then give up */
+ if (!hdmi->unwedge_state)
+ return false;
+
+ dev_info(hdmi->dev, "Attempting to unwedge stuck i2c bus\n");
+
+ /*
+ * This is a huge hack to workaround a problem where the dw_hdmi i2c
+ * bus could sometimes get wedged. Once wedged there doesn't appear
+ * to be any way to unwedge it (including the HDMI_I2CM_SOFTRSTZ)
+ * other than pulsing the SDA line.
+ *
+ * We appear to be able to pulse the SDA line (in the eyes of dw_hdmi)
+ * by:
+ * 1. Remux the pin as a GPIO output, driven low.
+ * 2. Wait a little while. 1 ms seems to work, but we'll do 10.
+ * 3. Immediately jump to remux the pin as dw_hdmi i2c again.
+ *
+ * At the moment of remuxing, the line will still be low due to its
+ * recent stint as an output, but then it will be pulled high by the
+ * (presumed) external pullup. dw_hdmi seems to see this as a rising
+ * edge and that seems to get it out of its jam.
+ *
+ * This wedging was only ever seen on one TV, and only on one of
+ * its HDMI ports. It happened when the TV was powered on while the
+ * device was plugged in. A scope trace shows the TV bringing both SDA
+ * and SCL low, then bringing them both back up at roughly the same
+ * time. Presumably this confuses dw_hdmi because it saw activity but
+ * no real STOP (maybe it thinks there's another master on the bus?).
+ * Giving it a clean rising edge of SDA while SCL is already high
+ * presumably makes dw_hdmi see a STOP which seems to bring dw_hdmi out
+ * of its stupor.
+ *
+ * Note that after coming back alive, transfers seem to immediately
+ * resume, so if we unwedge due to a timeout we should wait a little
+ * longer for our transfer to finish, since it might have just started
+ * now.
+ */
+ pinctrl_select_state(hdmi->pinctrl, hdmi->unwedge_state);
+ msleep(10);
+ pinctrl_select_state(hdmi->pinctrl, hdmi->default_state);
+
+ return true;
+}
+
+static int dw_hdmi_i2c_wait(struct dw_hdmi *hdmi)
+{
+ struct dw_hdmi_i2c *i2c = hdmi->i2c;
+ int stat;
+
+ stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
+ if (!stat) {
+ /* If we can't unwedge, return timeout */
+ if (!dw_hdmi_i2c_unwedge(hdmi))
+ return -EAGAIN;
+
+ /* We tried to unwedge; give it another chance */
+ stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
+ if (!stat)
+ return -EAGAIN;
+ }
+
+ /* Check for error condition on the bus */
+ if (i2c->stat & HDMI_IH_I2CM_STAT0_ERROR)
+ return -EIO;
+
+ return 0;
+}
+
+static int dw_hdmi_i2c_read(struct dw_hdmi *hdmi, unsigned char *buf,
+ unsigned int length)
+{
+ struct dw_hdmi_i2c *i2c = hdmi->i2c;
+ int ret;
+
+ if (!i2c->is_regaddr) {
+ dev_dbg(hdmi->dev, "set read register address to 0\n");
+ i2c->slave_reg = 0x00;
+ i2c->is_regaddr = true;
+ }
+
+ while (length--) {
+ reinit_completion(&i2c->cmp);
+
+ hdmi_writeb(hdmi, i2c->slave_reg++, HDMI_I2CM_ADDRESS);
+ if (i2c->is_segment)
+ hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ_EXT,
+ HDMI_I2CM_OPERATION);
+ else
+ hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ,
+ HDMI_I2CM_OPERATION);
+
+ ret = dw_hdmi_i2c_wait(hdmi);
+ if (ret)
+ return ret;
+
+ /* Check for error condition on the bus */
+ if (i2c->stat & HDMI_IH_I2CM_STAT0_ERROR)
+ return -EIO;
+
+ *buf++ = hdmi_readb(hdmi, HDMI_I2CM_DATAI);
+ }
+ i2c->is_segment = false;
+
+ return 0;
+}
+
+static int dw_hdmi_i2c_write(struct dw_hdmi *hdmi, unsigned char *buf,
+ unsigned int length)
+{
+ struct dw_hdmi_i2c *i2c = hdmi->i2c;
+ int ret;
+
+ if (!i2c->is_regaddr) {
+ /* Use the first write byte as register address */
+ i2c->slave_reg = buf[0];
+ length--;
+ buf++;
+ i2c->is_regaddr = true;
+ }
+
+ while (length--) {
+ reinit_completion(&i2c->cmp);
+
+ hdmi_writeb(hdmi, *buf++, HDMI_I2CM_DATAO);
+ hdmi_writeb(hdmi, i2c->slave_reg++, HDMI_I2CM_ADDRESS);
+ hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_WRITE,
+ HDMI_I2CM_OPERATION);
+
+ ret = dw_hdmi_i2c_wait(hdmi);
+ if (ret)
+ return ret;
+
+ /* Check for error condition on the bus */
+ if (i2c->stat & HDMI_IH_I2CM_STAT0_ERROR)
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int dw_hdmi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
+{
+ struct dw_hdmi *hdmi = i2c_get_adapdata(adap);
+ struct dw_hdmi_i2c *i2c = hdmi->i2c;
+ u8 addr = msgs[0].addr;
+ int i, ret = 0;
+
+ if (addr == DDC_CI_ADDR)
+ /*
+ * The internal I2C controller does not support the multi-byte
+ * read and write operations needed for DDC/CI.
+ * TOFIX: Blacklist the DDC/CI address until we filter out
+ * unsupported I2C operations.
+ */
+ return -EOPNOTSUPP;
+
+ dev_dbg(hdmi->dev, "xfer: num: %d, addr: %#x\n", num, addr);
+
+ for (i = 0; i < num; i++) {
+ if (msgs[i].len == 0) {
+ dev_dbg(hdmi->dev,
+ "unsupported transfer %d/%d, no data\n", i + 1,
+ num);
+ return -EOPNOTSUPP;
+ }
+ }
+
+ mutex_lock(&i2c->lock);
+
+ /* Unmute DONE and ERROR interrupts */
+ hdmi_writeb(hdmi, 0x00, HDMI_IH_MUTE_I2CM_STAT0);
+
+ /* Set slave device address taken from the first I2C message */
+ if (addr == DDC_SEGMENT_ADDR && msgs[0].len == 1)
+ addr = DDC_ADDR;
+ hdmi_writeb(hdmi, addr, HDMI_I2CM_SLAVE);
+
+ /* Set slave device register address on transfer */
+ i2c->is_regaddr = false;
+
+ /* Set segment pointer for I2C extended read mode operation */
+ i2c->is_segment = false;
+
+ for (i = 0; i < num; i++) {
+ dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n",
+ i + 1, num, msgs[i].len, msgs[i].flags);
+ if (msgs[i].addr == DDC_SEGMENT_ADDR && msgs[i].len == 1) {
+ i2c->is_segment = true;
+ hdmi_writeb(hdmi, DDC_SEGMENT_ADDR, HDMI_I2CM_SEGADDR);
+ hdmi_writeb(hdmi, *msgs[i].buf, HDMI_I2CM_SEGPTR);
+ } else {
+ if (msgs[i].flags & I2C_M_RD)
+ ret = dw_hdmi_i2c_read(hdmi, msgs[i].buf,
+ msgs[i].len);
+ else
+ ret = dw_hdmi_i2c_write(hdmi, msgs[i].buf,
+ msgs[i].len);
+ }
+ if (ret < 0) {
+ dev_info(hdmi->dev, "i2c transfer fail\n");
+ break;
+ }
+ }
+
+ if (!ret)
+ ret = num;
+
+ /* Mute DONE and ERROR interrupts */
+ hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE,
+ HDMI_IH_MUTE_I2CM_STAT0);
+
+ mutex_unlock(&i2c->lock);
+
+ return ret;
+}
+
+static u32 dw_hdmi_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm dw_hdmi_algorithm = {
+ .master_xfer = dw_hdmi_i2c_xfer,
+ .functionality = dw_hdmi_i2c_func,
+};
+
+u8 tv_hdmi_hdcp2_support(struct dw_hdmi *hdmi)
+{
+ int ret = 0;
+ int try_times = 10;
+ unsigned char start = 0x50;
+ u8 buf;
+ int i;
+
+ struct i2c_msg msgs[] = { {
+ .addr = 0x3a,
+ .flags = 0,
+ .len = 1,
+ .buf = &start,
+ },
+ {
+ .addr = 0x3a,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = &buf,
+ } };
+
+ for (i = 0; i < try_times; i++) {
+ //if (hdmi->bridge_is_on)
+ ret = dw_hdmi_i2c_xfer(hdmi->ddc, msgs, 2);
+
+ printk("ret = %d; buf = %d\n", ret, buf);
+ if (ret == 2 && (buf & 0x4))
+ return ((buf & 0x4) >> 2);
+ udelay(100);
+ }
+
+ return -1;
+}
+EXPORT_SYMBOL_GPL(tv_hdmi_hdcp2_support);
+
+static struct i2c_adapter *dw_hdmi_i2c_adapter(struct dw_hdmi *hdmi)
+{
+ struct i2c_adapter *adap;
+ struct dw_hdmi_i2c *i2c;
+ int ret;
+
+ i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL);
+ if (!i2c)
+ return ERR_PTR(-ENOMEM);
+
+ mutex_init(&i2c->lock);
+ init_completion(&i2c->cmp);
+
+ adap = &i2c->adap;
+ adap->class = I2C_CLASS_DDC;
+ adap->owner = THIS_MODULE;
+ adap->dev.parent = hdmi->dev;
+ adap->dev.of_node = hdmi->dev->of_node;
+ adap->algo = &dw_hdmi_algorithm;
+ strlcpy(adap->name, "DesignWare HDMI", sizeof(adap->name));
+ i2c_set_adapdata(adap, hdmi);
+
+ ret = i2c_add_adapter(adap);
+ if (ret) {
+ dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name);
+ devm_kfree(hdmi->dev, i2c);
+ return ERR_PTR(ret);
+ }
+
+ hdmi->i2c = i2c;
+
+ dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name);
+
+ return adap;
+}
+
+static void hdmi_set_cts_n(struct dw_hdmi *hdmi, unsigned int cts,
+ unsigned int n)
+{
+ /* Must be set/cleared first */
+ hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
+
+ /* nshift factor = 0 */
+ hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3);
+
+ /* Use automatic CTS generation mode when CTS is not set */
+ if (cts)
+ hdmi_writeb(hdmi,
+ ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) |
+ HDMI_AUD_CTS3_CTS_MANUAL,
+ HDMI_AUD_CTS3);
+ else
+ hdmi_writeb(hdmi, 0, HDMI_AUD_CTS3);
+ hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2);
+ hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1);
+
+ hdmi_writeb(hdmi, (n >> 16) & 0x0f, HDMI_AUD_N3);
+ hdmi_writeb(hdmi, (n >> 8) & 0xff, HDMI_AUD_N2);
+ hdmi_writeb(hdmi, n & 0xff, HDMI_AUD_N1);
+}
+
+static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk)
+{
+ unsigned int n = (128 * freq) / 1000;
+ unsigned int mult = 1;
+
+ while (freq > 48000) {
+ mult *= 2;
+ freq /= 2;
+ }
+
+ switch (freq) {
+ case 32000:
+ if (pixel_clk == 25175000)
+ n = 4576;
+ else if (pixel_clk == 27027000)
+ n = 4096;
+ else if (pixel_clk == 74176000 || pixel_clk == 148352000)
+ n = 11648;
+ else
+ n = 4096;
+ n *= mult;
+ break;
+
+ case 44100:
+ if (pixel_clk == 25175000)
+ n = 7007;
+ else if (pixel_clk == 74176000)
+ n = 17836;
+ else if (pixel_clk == 148352000)
+ n = 8918;
+ else
+ n = 6272;
+ n *= mult;
+ break;
+
+ case 48000:
+ if (pixel_clk == 25175000)
+ n = 6864;
+ else if (pixel_clk == 27027000)
+ n = 6144;
+ else if (pixel_clk == 74176000)
+ n = 11648;
+ else if (pixel_clk == 148352000)
+ n = 5824;
+ else
+ n = 6144;
+ n *= mult;
+ break;
+
+ default:
+ break;
+ }
+
+ return n;
+}
+
+/*
+ * When transmitting IEC60958 linear PCM audio, these registers allow to
+ * configure the channel status information of all the channel status
+ * bits in the IEC60958 frame. For the moment this configuration is only
+ * used when the I2S audio interface, General Purpose Audio (GPA),
+ * or AHB audio DMA (AHBAUDDMA) interface is active
+ * (for S/PDIF interface this information comes from the stream).
+ */
+void dw_hdmi_set_channel_status(struct dw_hdmi *hdmi, u8 *channel_status)
+{
+ /*
+ * Set channel status register for frequency and word length.
+ * Use default values for other registers.
+ */
+ hdmi_writeb(hdmi, channel_status[3], HDMI_FC_AUDSCHNLS7);
+ hdmi_writeb(hdmi, channel_status[4], HDMI_FC_AUDSCHNLS8);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_set_channel_status);
+
+static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
+ unsigned long pixel_clk,
+ unsigned int sample_rate)
+{
+ unsigned long ftdms = pixel_clk;
+ unsigned int n, cts;
+ u8 config3;
+ u64 tmp;
+
+ n = hdmi_compute_n(sample_rate, pixel_clk);
+
+ config3 = hdmi_readb(hdmi, HDMI_CONFIG3_ID);
+
+ /* Only compute CTS when using internal AHB audio */
+ if (config3 & HDMI_CONFIG3_AHBAUDDMA) {
+ /*
+ * Compute the CTS value from the N value. Note that CTS and N
+ * can be up to 20 bits in total, so we need 64-bit math. Also
+ * note that our TDMS clock is not fully accurate; it is
+ * accurate to kHz. This can introduce an unnecessary remainder
+ * in the calculation below, so we don't try to warn about that.
+ */
+ tmp = (u64)ftdms * n;
+ do_div(tmp, 128 * sample_rate);
+ cts = tmp;
+
+ dev_dbg(hdmi->dev,
+ "%s: fs=%uHz ftdms=%lu.%03luMHz N=%d cts=%d\n",
+ __func__, sample_rate, ftdms / 1000000,
+ (ftdms / 1000) % 1000, n, cts);
+ } else {
+ cts = 0;
+ }
+
+ spin_lock_irq(&hdmi->audio_lock);
+ hdmi->audio_n = n;
+ hdmi->audio_cts = cts;
+ hdmi_set_cts_n(hdmi, cts, hdmi->audio_enable ? n : 0);
+ spin_unlock_irq(&hdmi->audio_lock);
+}
+
+static void hdmi_init_clk_regenerator(struct dw_hdmi *hdmi)
+{
+ mutex_lock(&hdmi->audio_mutex);
+ hdmi_set_clk_regenerator(hdmi, 74250000, hdmi->sample_rate);
+ mutex_unlock(&hdmi->audio_mutex);
+}
+
+static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi)
+{
+ mutex_lock(&hdmi->audio_mutex);
+ hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock,
+ hdmi->sample_rate);
+ mutex_unlock(&hdmi->audio_mutex);
+}
+
+void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate)
+{
+ mutex_lock(&hdmi->audio_mutex);
+ hdmi->sample_rate = rate;
+ hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock,
+ hdmi->sample_rate);
+ mutex_unlock(&hdmi->audio_mutex);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_rate);
+
+void dw_hdmi_set_channel_count(struct dw_hdmi *hdmi, unsigned int cnt)
+{
+ u8 layout;
+
+ mutex_lock(&hdmi->audio_mutex);
+
+ /*
+ * For >2 channel PCM audio, we need to select layout 1
+ * and set an appropriate channel map.
+ */
+ if (cnt > 2)
+ layout = HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT1;
+ else
+ layout = HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT0;
+
+ hdmi_modb(hdmi, layout, HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_MASK,
+ HDMI_FC_AUDSCONF);
+
+ /* Set the audio infoframes channel count */
+ hdmi_modb(hdmi, (cnt - 1) << HDMI_FC_AUDICONF0_CC_OFFSET,
+ HDMI_FC_AUDICONF0_CC_MASK, HDMI_FC_AUDICONF0);
+
+ mutex_unlock(&hdmi->audio_mutex);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_set_channel_count);
+
+void dw_hdmi_set_channel_allocation(struct dw_hdmi *hdmi, unsigned int ca)
+{
+ mutex_lock(&hdmi->audio_mutex);
+
+ hdmi_writeb(hdmi, ca, HDMI_FC_AUDICONF2);
+
+ mutex_unlock(&hdmi->audio_mutex);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_set_channel_allocation);
+
+static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi, bool enable)
+{
+ if (enable)
+ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_AUDCLK_DISABLE;
+ else
+ hdmi->mc_clkdis |= HDMI_MC_CLKDIS_AUDCLK_DISABLE;
+ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+}
+
+static u8 *hdmi_audio_get_eld(struct dw_hdmi *hdmi)
+{
+ if (!hdmi->curr_conn)
+ return NULL;
+
+ return hdmi->curr_conn->eld;
+}
+
+static void dw_hdmi_ahb_audio_enable(struct dw_hdmi *hdmi)
+{
+ hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
+}
+
+static void dw_hdmi_ahb_audio_disable(struct dw_hdmi *hdmi)
+{
+ hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0);
+}
+
+static void dw_hdmi_i2s_audio_enable(struct dw_hdmi *hdmi)
+{
+ hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
+ hdmi_enable_audio_clk(hdmi, true);
+}
+
+static void dw_hdmi_i2s_audio_disable(struct dw_hdmi *hdmi)
+{
+ hdmi_enable_audio_clk(hdmi, false);
+}
+
+void dw_hdmi_audio_enable(struct dw_hdmi *hdmi)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hdmi->audio_lock, flags);
+ hdmi->audio_enable = true;
+ if (hdmi->enable_audio)
+ hdmi->enable_audio(hdmi);
+ spin_unlock_irqrestore(&hdmi->audio_lock, flags);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_audio_enable);
+
+void dw_hdmi_audio_disable(struct dw_hdmi *hdmi)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hdmi->audio_lock, flags);
+ hdmi->audio_enable = false;
+ if (hdmi->disable_audio)
+ hdmi->disable_audio(hdmi);
+ spin_unlock_irqrestore(&hdmi->audio_lock, flags);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_audio_disable);
+
+static bool hdmi_bus_fmt_is_rgb(unsigned int bus_format)
+{
+ switch (bus_format) {
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ case MEDIA_BUS_FMT_RGB101010_1X30:
+ case MEDIA_BUS_FMT_RGB121212_1X36:
+ case MEDIA_BUS_FMT_RGB161616_1X48:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool hdmi_bus_fmt_is_yuv444(unsigned int bus_format)
+{
+ switch (bus_format) {
+ case MEDIA_BUS_FMT_YUV8_1X24:
+ case MEDIA_BUS_FMT_YUV10_1X30:
+ case MEDIA_BUS_FMT_YUV12_1X36:
+ case MEDIA_BUS_FMT_YUV16_1X48:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format)
+{
+ switch (bus_format) {
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_UYVY10_1X20:
+ case MEDIA_BUS_FMT_UYVY12_1X24:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool hdmi_bus_fmt_is_yuv420(unsigned int bus_format)
+{
+ switch (bus_format) {
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+ case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
+ case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static int hdmi_bus_fmt_color_depth(unsigned int bus_format)
+{
+ switch (bus_format) {
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ case MEDIA_BUS_FMT_YUV8_1X24:
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+ return 8;
+
+ case MEDIA_BUS_FMT_RGB101010_1X30:
+ case MEDIA_BUS_FMT_YUV10_1X30:
+ case MEDIA_BUS_FMT_UYVY10_1X20:
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+ return 10;
+
+ case MEDIA_BUS_FMT_RGB121212_1X36:
+ case MEDIA_BUS_FMT_YUV12_1X36:
+ case MEDIA_BUS_FMT_UYVY12_1X24:
+ case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
+ return 12;
+
+ case MEDIA_BUS_FMT_RGB161616_1X48:
+ case MEDIA_BUS_FMT_YUV16_1X48:
+ case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
+ return 16;
+
+ default:
+ return 0;
+ }
+}
+
+/*
+ * this submodule is responsible for the video data synchronization.
+ * for example, for RGB 4:4:4 input, the data map is defined as
+ * pin{47~40} <==> R[7:0]
+ * pin{31~24} <==> G[7:0]
+ * pin{15~8} <==> B[7:0]
+ */
+static void hdmi_video_sample(struct dw_hdmi *hdmi)
+{
+ int color_format = 0;
+ u8 val;
+
+ switch (hdmi->hdmi_data.enc_in_bus_format) {
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ color_format = 0x01;
+ break;
+ case MEDIA_BUS_FMT_RGB101010_1X30:
+ color_format = 0x03;
+ break;
+ case MEDIA_BUS_FMT_RGB121212_1X36:
+ color_format = 0x05;
+ break;
+ case MEDIA_BUS_FMT_RGB161616_1X48:
+ color_format = 0x07;
+ break;
+
+ case MEDIA_BUS_FMT_YUV8_1X24:
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+ color_format = 0x09;
+ break;
+ case MEDIA_BUS_FMT_YUV10_1X30:
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+ color_format = 0x0B;
+ break;
+ case MEDIA_BUS_FMT_YUV12_1X36:
+ case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
+ color_format = 0x0D;
+ break;
+ case MEDIA_BUS_FMT_YUV16_1X48:
+ case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
+ color_format = 0x0F;
+ break;
+
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ color_format = 0x16;
+ break;
+ case MEDIA_BUS_FMT_UYVY10_1X20:
+ color_format = 0x14;
+ break;
+ case MEDIA_BUS_FMT_UYVY12_1X24:
+ color_format = 0x12;
+ break;
+
+ default:
+ return;
+ }
+
+ val = HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE |
+ ((color_format << HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET) &
+ HDMI_TX_INVID0_VIDEO_MAPPING_MASK);
+ hdmi_writeb(hdmi, val, HDMI_TX_INVID0);
+
+ /* Enable TX stuffing: When DE is inactive, fix the output data to 0 */
+ val = HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE |
+ HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE |
+ HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE;
+ hdmi_writeb(hdmi, val, HDMI_TX_INSTUFFING);
+ hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA0);
+ hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA1);
+ hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA0);
+ hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA1);
+ hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA0);
+ hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA1);
+}
+
+static int is_color_space_conversion(struct dw_hdmi *hdmi)
+{
+ struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data;
+ bool is_input_rgb, is_output_rgb;
+
+ is_input_rgb = hdmi_bus_fmt_is_rgb(hdmi_data->enc_in_bus_format);
+ is_output_rgb = hdmi_bus_fmt_is_rgb(hdmi_data->enc_out_bus_format);
+
+ return (is_input_rgb != is_output_rgb) ||
+ (is_input_rgb && is_output_rgb && hdmi_data->rgb_limited_range);
+}
+
+static int is_color_space_decimation(struct dw_hdmi *hdmi)
+{
+ if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
+ return 0;
+
+ if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format) ||
+ hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_in_bus_format))
+ return 1;
+
+ return 0;
+}
+
+static int is_color_space_interpolation(struct dw_hdmi *hdmi)
+{
+ if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_in_bus_format))
+ return 0;
+
+ if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) ||
+ hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format))
+ return 1;
+
+ return 0;
+}
+
+static bool is_csc_needed(struct dw_hdmi *hdmi)
+{
+ return is_color_space_conversion(hdmi) ||
+ is_color_space_decimation(hdmi) ||
+ is_color_space_interpolation(hdmi);
+}
+
+static void dw_hdmi_update_csc_coeffs(struct dw_hdmi *hdmi)
+{
+ const u16(*csc_coeff)[3][4] = &csc_coeff_default;
+ bool is_input_rgb, is_output_rgb, is_output_yuv;
+ unsigned i;
+ u32 csc_scale = 1;
+ unsigned int depth =
+ hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format);
+
+ is_input_rgb = hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format);
+ is_output_rgb = hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format);
+ is_output_yuv =
+ hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format) ||
+ hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format);
+
+ if (!is_input_rgb && is_output_rgb) {
+ if (hdmi->hdmi_data.enc_out_encoding == V4L2_YCBCR_ENC_601)
+ csc_coeff = &csc_coeff_rgb_out_eitu601;
+ else
+ csc_coeff = &csc_coeff_rgb_out_eitu709;
+ } else if (is_input_rgb && !is_output_rgb) {
+ if (hdmi->hdmi_data.enc_out_encoding == V4L2_YCBCR_ENC_601) {
+ if (is_output_yuv && depth == 8)
+ csc_coeff = &csc_coeff_rgb_to_yuv_8bit_eitu601;
+ else if (is_output_yuv && depth == 10)
+ csc_coeff = &csc_coeff_rgb_to_yuv_10bit_eitu601;
+ else
+ csc_coeff = &csc_coeff_rgb_in_eitu601;
+ } else {
+ if (is_output_yuv && depth == 8)
+ csc_coeff = &csc_coeff_rgb_to_yuv_8bit_eitu709;
+ else if (is_output_yuv && depth == 10)
+ csc_coeff = &csc_coeff_rgb_to_yuv_10bit_eitu709;
+ else
+ csc_coeff = &csc_coeff_rgb_in_eitu709;
+ }
+ csc_scale = 0;
+ } else if (is_input_rgb && is_output_rgb &&
+ hdmi->hdmi_data.rgb_limited_range) {
+ csc_coeff = &csc_coeff_rgb_full_to_rgb_limited;
+ }
+
+ /* The CSC registers are sequential, alternating MSB then LSB */
+ for (i = 0; i < ARRAY_SIZE(csc_coeff_default[0]); i++) {
+ u16 coeff_a = (*csc_coeff)[0][i];
+ u16 coeff_b = (*csc_coeff)[1][i];
+ u16 coeff_c = (*csc_coeff)[2][i];
+
+ hdmi_writeb(hdmi, coeff_a & 0xff, HDMI_CSC_COEF_A1_LSB + i * 2);
+ hdmi_writeb(hdmi, coeff_a >> 8, HDMI_CSC_COEF_A1_MSB + i * 2);
+ hdmi_writeb(hdmi, coeff_b & 0xff, HDMI_CSC_COEF_B1_LSB + i * 2);
+ hdmi_writeb(hdmi, coeff_b >> 8, HDMI_CSC_COEF_B1_MSB + i * 2);
+ hdmi_writeb(hdmi, coeff_c & 0xff, HDMI_CSC_COEF_C1_LSB + i * 2);
+ hdmi_writeb(hdmi, coeff_c >> 8, HDMI_CSC_COEF_C1_MSB + i * 2);
+ }
+
+ hdmi_modb(hdmi, csc_scale, HDMI_CSC_SCALE_CSCSCALE_MASK,
+ HDMI_CSC_SCALE);
+}
+
+static void hdmi_video_csc(struct dw_hdmi *hdmi)
+{
+ int color_depth = 0;
+ int interpolation = HDMI_CSC_CFG_INTMODE_DISABLE;
+ int decimation = 0;
+
+ /* YCC422 interpolation to 444 mode */
+ if (is_color_space_interpolation(hdmi))
+ interpolation = HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA1;
+ else if (is_color_space_decimation(hdmi))
+ decimation = HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA3;
+
+ switch (hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format)) {
+ case 8:
+ color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_24BPP;
+ break;
+ case 10:
+ color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_30BPP;
+ break;
+ case 12:
+ color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_36BPP;
+ break;
+ case 16:
+ color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_48BPP;
+ break;
+
+ default:
+ return;
+ }
+
+ /* Configure the CSC registers */
+ hdmi_writeb(hdmi, interpolation | decimation, HDMI_CSC_CFG);
+ hdmi_modb(hdmi, color_depth, HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK,
+ HDMI_CSC_SCALE);
+
+ dw_hdmi_update_csc_coeffs(hdmi);
+}
+
+/*
+ * HDMI video packetizer is used to packetize the data.
+ * for example, if input is YCC422 mode or repeater is used,
+ * data should be repacked this module can be bypassed.
+ */
+static void hdmi_video_packetize(struct dw_hdmi *hdmi)
+{
+ unsigned int color_depth = 0;
+ unsigned int remap_size = HDMI_VP_REMAP_YCC422_16bit;
+ unsigned int output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_PP;
+ struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data;
+ u8 val, vp_conf;
+
+ if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) ||
+ hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format) ||
+ hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) {
+ switch (hdmi_bus_fmt_color_depth(
+ hdmi->hdmi_data.enc_out_bus_format)) {
+ case 8:
+ color_depth = 4;
+ output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
+ break;
+ case 10:
+ color_depth = 5;
+ break;
+ case 12:
+ color_depth = 6;
+ break;
+ case 16:
+ color_depth = 7;
+ break;
+ default:
+ output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
+ }
+ } else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) {
+ switch (hdmi_bus_fmt_color_depth(
+ hdmi->hdmi_data.enc_out_bus_format)) {
+ case 0:
+ case 8:
+ remap_size = HDMI_VP_REMAP_YCC422_16bit;
+ break;
+ case 10:
+ remap_size = HDMI_VP_REMAP_YCC422_20bit;
+ break;
+ case 12:
+ remap_size = HDMI_VP_REMAP_YCC422_24bit;
+ break;
+
+ default:
+ return;
+ }
+ output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422;
+ } else {
+ return;
+ }
+
+ /* set the packetizer registers */
+ val = ((color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) &
+ HDMI_VP_PR_CD_COLOR_DEPTH_MASK) |
+ ((hdmi_data->pix_repet_factor
+ << HDMI_VP_PR_CD_DESIRED_PR_FACTOR_OFFSET) &
+ HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK);
+ hdmi_writeb(hdmi, val, HDMI_VP_PR_CD);
+
+ hdmi_modb(hdmi, HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE,
+ HDMI_VP_STUFF_PR_STUFFING_MASK, HDMI_VP_STUFF);
+
+ /* Data from pixel repeater block */
+ if (hdmi_data->pix_repet_factor > 1) {
+ vp_conf = HDMI_VP_CONF_PR_EN_ENABLE |
+ HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER;
+ } else { /* data from packetizer block */
+ vp_conf = HDMI_VP_CONF_PR_EN_DISABLE |
+ HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER;
+ }
+
+ hdmi_modb(hdmi, vp_conf,
+ HDMI_VP_CONF_PR_EN_MASK | HDMI_VP_CONF_BYPASS_SELECT_MASK,
+ HDMI_VP_CONF);
+
+ hdmi_modb(hdmi, 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET,
+ HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, HDMI_VP_STUFF);
+
+ hdmi_writeb(hdmi, remap_size, HDMI_VP_REMAP);
+
+ if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_PP) {
+ vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE |
+ HDMI_VP_CONF_PP_EN_ENABLE |
+ HDMI_VP_CONF_YCC422_EN_DISABLE;
+ } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422) {
+ vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE |
+ HDMI_VP_CONF_PP_EN_DISABLE |
+ HDMI_VP_CONF_YCC422_EN_ENABLE;
+ } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS) {
+ vp_conf = HDMI_VP_CONF_BYPASS_EN_ENABLE |
+ HDMI_VP_CONF_PP_EN_DISABLE |
+ HDMI_VP_CONF_YCC422_EN_DISABLE;
+ } else {
+ return;
+ }
+
+ hdmi_modb(hdmi, vp_conf,
+ HDMI_VP_CONF_BYPASS_EN_MASK | HDMI_VP_CONF_PP_EN_ENMASK |
+ HDMI_VP_CONF_YCC422_EN_MASK,
+ HDMI_VP_CONF);
+
+ hdmi_modb(hdmi,
+ HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE |
+ HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE,
+ HDMI_VP_STUFF_PP_STUFFING_MASK |
+ HDMI_VP_STUFF_YCC422_STUFFING_MASK,
+ HDMI_VP_STUFF);
+
+ hdmi_modb(hdmi, output_select, HDMI_VP_CONF_OUTPUT_SELECTOR_MASK,
+ HDMI_VP_CONF);
+}
+
+/* -----------------------------------------------------------------------------
+ * Synopsys PHY Handling
+ */
+
+static inline void hdmi_phy_test_clear(struct dw_hdmi *hdmi, unsigned char bit)
+{
+ hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLR_OFFSET,
+ HDMI_PHY_TST0_TSTCLR_MASK, HDMI_PHY_TST0);
+}
+
+static inline void hdmi_phy_test_enable(struct dw_hdmi *hdmi, unsigned char bit)
+{
+ hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTEN_OFFSET,
+ HDMI_PHY_TST0_TSTEN_MASK, HDMI_PHY_TST0);
+}
+
+static inline void hdmi_phy_test_clock(struct dw_hdmi *hdmi, unsigned char bit)
+{
+ hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLK_OFFSET,
+ HDMI_PHY_TST0_TSTCLK_MASK, HDMI_PHY_TST0);
+}
+
+static inline void hdmi_phy_test_din(struct dw_hdmi *hdmi, unsigned char bit)
+{
+ hdmi_writeb(hdmi, bit, HDMI_PHY_TST1);
+}
+
+static inline void hdmi_phy_test_dout(struct dw_hdmi *hdmi, unsigned char bit)
+{
+ hdmi_writeb(hdmi, bit, HDMI_PHY_TST2);
+}
+
+static bool hdmi_phy_wait_i2c_done(struct dw_hdmi *hdmi, int msec)
+{
+ u32 val;
+
+ while ((val = hdmi_readb(hdmi, HDMI_IH_I2CMPHY_STAT0) & 0x3) == 0) {
+ if (msec-- == 0)
+ return false;
+ udelay(1000);
+ }
+ hdmi_writeb(hdmi, val, HDMI_IH_I2CMPHY_STAT0);
+
+ return true;
+}
+
+void dw_hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
+ unsigned char addr)
+{
+ hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0);
+ hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR);
+ hdmi_writeb(hdmi, (unsigned char)(data >> 8),
+ HDMI_PHY_I2CM_DATAO_1_ADDR);
+ hdmi_writeb(hdmi, (unsigned char)(data >> 0),
+ HDMI_PHY_I2CM_DATAO_0_ADDR);
+ hdmi_writeb(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE,
+ HDMI_PHY_I2CM_OPERATION_ADDR);
+ hdmi_phy_wait_i2c_done(hdmi, 1000);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_phy_i2c_write);
+
+static int hdmi_phy_i2c_read(struct dw_hdmi *hdmi, unsigned char addr)
+{
+ int val;
+
+ hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0);
+ hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR);
+ hdmi_writeb(hdmi, 0, HDMI_PHY_I2CM_DATAI_1_ADDR);
+ hdmi_writeb(hdmi, 0, HDMI_PHY_I2CM_DATAI_0_ADDR);
+ hdmi_writeb(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_READ,
+ HDMI_PHY_I2CM_OPERATION_ADDR);
+ hdmi_phy_wait_i2c_done(hdmi, 1000);
+ val = hdmi_readb(hdmi, HDMI_PHY_I2CM_DATAI_1_ADDR);
+ val = (val & 0xff) << 8;
+ val += hdmi_readb(hdmi, HDMI_PHY_I2CM_DATAI_0_ADDR) & 0xff;
+ return val;
+}
+
+/* Filter out invalid setups to avoid configuring SCDC and scrambling */
+static bool dw_hdmi_support_scdc(struct dw_hdmi *hdmi,
+ const struct drm_display_info *display)
+{
+ /* Completely disable SCDC support for older controllers */
+ if (hdmi->version < 0x200a)
+ return false;
+
+ /* Disable if no DDC bus */
+ if (!hdmi->ddc)
+ return false;
+
+ /* Disable if SCDC is not supported, or if an HF-VSDB block is absent */
+ if (!display->hdmi.scdc.supported ||
+ !display->hdmi.scdc.scrambling.supported)
+ return false;
+
+ /*
+ * Disable if display only support low TMDS rates and scrambling
+ * for low rates is not supported either
+ */
+ if (!display->hdmi.scdc.scrambling.low_rates &&
+ display->max_tmds_clock <= 340000)
+ return false;
+
+ return true;
+}
+
+/*
+ * HDMI2.0 Specifies the following procedure for High TMDS Bit Rates:
+ * - The Source shall suspend transmission of the TMDS clock and data
+ * - The Source shall write to the TMDS_Bit_Clock_Ratio bit to change it
+ * from a 0 to a 1 or from a 1 to a 0
+ * - The Source shall allow a minimum of 1 ms and a maximum of 100 ms from
+ * the time the TMDS_Bit_Clock_Ratio bit is written until resuming
+ * transmission of TMDS clock and data
+ *
+ * To respect the 100ms maximum delay, the dw_hdmi_set_high_tmds_clock_ratio()
+ * helper should called right before enabling the TMDS Clock and Data in
+ * the PHY configuration callback.
+ */
+void dw_hdmi_set_high_tmds_clock_ratio(struct dw_hdmi *hdmi,
+ const struct drm_display_info *display)
+{
+ unsigned long mtmdsclock = hdmi->hdmi_data.video_mode.mtmdsclock;
+
+ /* Control for TMDS Bit Period/TMDS Clock-Period Ratio */
+ if (dw_hdmi_support_scdc(hdmi, display)) {
+ if (mtmdsclock > HDMI14_MAX_TMDSCLK)
+ drm_scdc_set_high_tmds_clock_ratio(hdmi->ddc, 1);
+ else
+ drm_scdc_set_high_tmds_clock_ratio(hdmi->ddc, 0);
+ }
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_set_high_tmds_clock_ratio);
+
+static void dw_hdmi_phy_enable_powerdown(struct dw_hdmi *hdmi, bool enable)
+{
+ hdmi_mask_writeb(hdmi, !enable, HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_PDZ_OFFSET, HDMI_PHY_CONF0_PDZ_MASK);
+}
+
+static void dw_hdmi_phy_enable_tmds(struct dw_hdmi *hdmi, u8 enable)
+{
+ hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_ENTMDS_OFFSET,
+ HDMI_PHY_CONF0_ENTMDS_MASK);
+}
+
+static void dw_hdmi_phy_enable_svsret(struct dw_hdmi *hdmi, u8 enable)
+{
+ hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_SVSRET_OFFSET,
+ HDMI_PHY_CONF0_SVSRET_MASK);
+}
+
+void dw_hdmi_phy_gen2_pddq(struct dw_hdmi *hdmi, u8 enable)
+{
+ hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET,
+ HDMI_PHY_CONF0_GEN2_PDDQ_MASK);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_phy_gen2_pddq);
+
+void dw_hdmi_phy_gen2_txpwron(struct dw_hdmi *hdmi, u8 enable)
+{
+ hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET,
+ HDMI_PHY_CONF0_GEN2_TXPWRON_MASK);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_phy_gen2_txpwron);
+
+static void dw_hdmi_phy_sel_data_en_pol(struct dw_hdmi *hdmi, u8 enable)
+{
+ hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_SELDATAENPOL_OFFSET,
+ HDMI_PHY_CONF0_SELDATAENPOL_MASK);
+}
+
+static void dw_hdmi_phy_sel_interface_control(struct dw_hdmi *hdmi, u8 enable)
+{
+ hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_SELDIPIF_OFFSET,
+ HDMI_PHY_CONF0_SELDIPIF_MASK);
+}
+
+void dw_hdmi_phy_reset(struct dw_hdmi *hdmi)
+{
+ /* PHY reset. The reset signal is active high on Gen2 PHYs. */
+ hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_PHYRSTZ, HDMI_MC_PHYRSTZ);
+ hdmi_writeb(hdmi, 0, HDMI_MC_PHYRSTZ);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_phy_reset);
+
+void dw_hdmi_phy_i2c_set_addr(struct dw_hdmi *hdmi, u8 address)
+{
+ hdmi_phy_test_clear(hdmi, 1);
+ hdmi_writeb(hdmi, address, HDMI_PHY_I2CM_SLAVE_ADDR);
+ hdmi_phy_test_clear(hdmi, 0);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_phy_i2c_set_addr);
+
+static void dw_hdmi_phy_power_off(struct dw_hdmi *hdmi)
+{
+ const struct dw_hdmi_phy_data *phy = hdmi->phy.data;
+ unsigned int i;
+ u16 val;
+
+ if (phy->gen == 1) {
+ dw_hdmi_phy_enable_tmds(hdmi, 0);
+ dw_hdmi_phy_enable_powerdown(hdmi, true);
+ return;
+ }
+
+ dw_hdmi_phy_gen2_txpwron(hdmi, 0);
+
+ /*
+ * Wait for TX_PHY_LOCK to be deasserted to indicate that the PHY went
+ * to low power mode.
+ */
+ for (i = 0; i < 5; ++i) {
+ val = hdmi_readb(hdmi, HDMI_PHY_STAT0);
+ if (!(val & HDMI_PHY_TX_PHY_LOCK))
+ break;
+
+ usleep_range(1000, 2000);
+ }
+
+ if (val & HDMI_PHY_TX_PHY_LOCK)
+ dev_warn(hdmi->dev, "PHY failed to power down\n");
+ else
+ dev_dbg(hdmi->dev, "PHY powered down in %u iterations\n", i);
+
+ dw_hdmi_phy_gen2_pddq(hdmi, 1);
+}
+
+static int dw_hdmi_phy_power_on(struct dw_hdmi *hdmi)
+{
+ const struct dw_hdmi_phy_data *phy = hdmi->phy.data;
+ unsigned int i;
+ u8 val;
+
+ if (phy->gen == 1) {
+ dw_hdmi_phy_enable_powerdown(hdmi, false);
+
+ /* Toggle TMDS enable. */
+ dw_hdmi_phy_enable_tmds(hdmi, 0);
+ dw_hdmi_phy_enable_tmds(hdmi, 1);
+ return 0;
+ }
+
+ dw_hdmi_phy_gen2_pddq(hdmi, 0);
+ dw_hdmi_phy_gen2_txpwron(hdmi, 1);
+ /* Wait for PHY PLL lock */
+ for (i = 0; i < 5; ++i) {
+ val = hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
+ if (val)
+ break;
+
+ usleep_range(1000, 2000);
+ }
+
+ if (!val) {
+ dev_err(hdmi->dev, "PHY PLL failed to lock\n");
+ return -ETIMEDOUT;
+ }
+
+ dev_dbg(hdmi->dev, "PHY PLL locked %u iterations\n", i);
+
+ return 0;
+}
+
+/*
+ * PHY configuration function for the DWC HDMI 3D TX PHY. Based on the available
+ * information the DWC MHL PHY has the same register layout and is thus also
+ * supported by this function.
+ */
+static int
+hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
+ const struct dw_hdmi_plat_data *pdata,
+ unsigned long mpixelclock)
+{
+ const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg;
+ const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
+ const struct dw_hdmi_phy_config *phy_config = pdata->phy_config;
+
+ /* TOFIX Will need 420 specific PHY configuration tables */
+ unsigned int depth =
+ hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format);
+
+ /* PLL/MPLL Cfg - always match on final entry */
+ for (; mpll_config->mpixelclock != ~0UL; mpll_config++)
+ if (mpixelclock <= mpll_config->mpixelclock)
+ break;
+
+ for (; curr_ctrl->mpixelclock != ~0UL; curr_ctrl++)
+ if (mpixelclock <= curr_ctrl->mpixelclock)
+ break;
+
+ for (; phy_config->mpixelclock != ~0UL; phy_config++)
+ if (mpixelclock <= phy_config->mpixelclock)
+ break;
+
+ if (mpll_config->mpixelclock == ~0UL ||
+ curr_ctrl->mpixelclock == ~0UL || phy_config->mpixelclock == ~0UL)
+ return -EINVAL;
+
+ if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
+ depth = fls(depth - 8);
+ else
+ depth = 0;
+ if (depth)
+ depth--;
+
+ dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[depth].cpce,
+ HDMI_3D_TX_PHY_CPCE_CTRL);
+ dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[depth].gmp,
+ HDMI_3D_TX_PHY_DRVANACTRL);
+ dw_hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[depth],
+ HDMI_3D_TX_PHY_CURRCTRL);
+
+ dw_hdmi_phy_i2c_write(hdmi, 0, HDMI_3D_TX_PHY_PLLPHBYCTRL);
+ dw_hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_FB_CLK,
+ HDMI_3D_TX_PHY_MSM_CTRL);
+
+ dw_hdmi_phy_i2c_write(hdmi, phy_config->term, HDMI_3D_TX_PHY_TXTERM);
+ dw_hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr,
+ HDMI_3D_TX_PHY_CKSYMTXCTRL);
+ dw_hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr,
+ HDMI_3D_TX_PHY_VLEVCTRL);
+
+ /* Override and disable clock termination. */
+ dw_hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_CKCALCTRL_OVERRIDE,
+ HDMI_3D_TX_PHY_CKCALCTRL);
+
+ return 0;
+}
+
+static int hdmi_phy_configure(struct dw_hdmi *hdmi,
+ const struct drm_display_info *display)
+{
+ const struct dw_hdmi_phy_data *phy = hdmi->phy.data;
+ const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
+ unsigned long mpixelclock = hdmi->hdmi_data.video_mode.mpixelclock;
+ unsigned long mtmdsclock = hdmi->hdmi_data.video_mode.mtmdsclock;
+ int ret;
+
+ dw_hdmi_phy_power_off(hdmi);
+
+ dw_hdmi_set_high_tmds_clock_ratio(hdmi, display);
+
+ /* Leave low power consumption mode by asserting SVSRET. */
+ if (phy->has_svsret)
+ dw_hdmi_phy_enable_svsret(hdmi, 1);
+
+ dw_hdmi_phy_reset(hdmi);
+
+ hdmi_writeb(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST);
+ if (!hdmi->eswin_plat) {
+ dw_hdmi_phy_i2c_set_addr(hdmi, 0x54);
+ } else {
+ dw_hdmi_phy_i2c_set_addr(hdmi,
+ HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2);
+ }
+
+ /* Write to the PHY as configured by the platform */
+ if (pdata->configure_phy)
+ ret = pdata->configure_phy(hdmi, pdata->priv_data, mpixelclock);
+ else
+ ret = phy->configure(hdmi, pdata, mpixelclock);
+ if (ret) {
+ dev_err(hdmi->dev, "PHY configuration failed (clock %lu)\n",
+ mpixelclock);
+ return ret;
+ }
+
+ /* Wait for resuming transmission of TMDS clock and data */
+ if (mtmdsclock > HDMI14_MAX_TMDSCLK)
+ msleep(100);
+
+ return dw_hdmi_phy_power_on(hdmi);
+}
+
+static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data,
+ const struct drm_display_info *display,
+ const struct drm_display_mode *mode)
+{
+ int i, ret;
+
+ /* HDMI Phy spec says to do the phy initialization sequence twice */
+ for (i = 0; i < 2; i++) {
+ dw_hdmi_phy_sel_data_en_pol(hdmi, 1);
+ dw_hdmi_phy_sel_interface_control(hdmi, 0);
+
+ ret = hdmi_phy_configure(hdmi, display);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
+{
+ dw_hdmi_phy_power_off(hdmi);
+}
+
+enum drm_connector_status dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi, void *data)
+{
+ return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ?
+ connector_status_connected :
+ connector_status_disconnected;
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_phy_read_hpd);
+
+void dw_hdmi_phy_update_hpd(struct dw_hdmi *hdmi, void *data, bool force,
+ bool disabled, bool rxsense)
+{
+ u8 old_mask = hdmi->phy_mask;
+
+ if (force || disabled || !rxsense)
+ hdmi->phy_mask |= HDMI_PHY_RX_SENSE;
+ else
+ hdmi->phy_mask &= ~HDMI_PHY_RX_SENSE;
+
+ if (old_mask != hdmi->phy_mask)
+ hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_phy_update_hpd);
+
+void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi, void *data)
+{
+ /*
+ * Configure the PHY RX SENSE and HPD interrupts polarities and clear
+ * any pending interrupt.
+ */
+ hdmi_writeb(hdmi, HDMI_PHY_HPD | HDMI_PHY_RX_SENSE, HDMI_PHY_POL0);
+ hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE,
+ HDMI_IH_PHY_STAT0);
+
+ /* Enable cable hot plug irq. */
+ hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0);
+
+ /* Clear and unmute interrupts. */
+ hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE,
+ HDMI_IH_PHY_STAT0);
+ hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
+ HDMI_IH_MUTE_PHY_STAT0);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_phy_setup_hpd);
+
+static const struct dw_hdmi_phy_ops dw_hdmi_synopsys_phy_ops = {
+ .init = dw_hdmi_phy_init,
+ .disable = dw_hdmi_phy_disable,
+ .read_hpd = dw_hdmi_phy_read_hpd,
+ .update_hpd = dw_hdmi_phy_update_hpd,
+ .setup_hpd = dw_hdmi_phy_setup_hpd,
+};
+
+/* -----------------------------------------------------------------------------
+ * HDMI TX Setup
+ */
+
+static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi,
+ const struct drm_display_mode *mode)
+{
+ u8 de, vsync_pol, hsync_pol, hdmi_dvi;
+
+ /* Configure the video polarity */
+ if (hdmi->hdmi_data.video_mode.mdataenablepolarity)
+ de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH;
+ else
+ de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW;
+
+ vsync_pol = mode->flags & DRM_MODE_FLAG_PVSYNC ?
+ HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_HIGH :
+ HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_LOW;
+
+ hsync_pol = mode->flags & DRM_MODE_FLAG_PHSYNC ?
+ HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_HIGH :
+ HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_LOW;
+
+ /* Config the display mode */
+ hdmi_dvi = hdmi->sink_is_hdmi ? HDMI_A_HDCPCFG0_HDMIDVI_HDMI :
+ HDMI_A_HDCPCFG0_HDMIDVI_DVI;
+
+ hdmi_modb(hdmi, hdmi_dvi, HDMI_A_HDCPCFG0_HDMIDVI_MASK,
+ HDMI_A_HDCPCFG0);
+
+ /* disable rx detect */
+ hdmi_modb(hdmi, HDMI_A_HDCPCFG0_RXDETECT_DISABLE,
+ HDMI_A_HDCPCFG0_RXDETECT_MASK, HDMI_A_HDCPCFG0);
+
+ hdmi_modb(hdmi, vsync_pol | hsync_pol | de,
+ HDMI_A_VIDPOLCFG_VSYNCPOL_MASK |
+ HDMI_A_VIDPOLCFG_HSYNCPOL_MASK |
+ HDMI_A_VIDPOLCFG_DATAENPOL_MASK,
+ HDMI_A_VIDPOLCFG);
+
+ hdmi_modb(hdmi, HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE,
+ HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK, HDMI_A_HDCPCFG1);
+
+ if (hdmi->hdcp && hdmi->hdcp->hdcp_start) {
+ hdmi->hdcp->hdcp_start(hdmi->hdcp);
+ }
+}
+
+static void hdmi_config_AVI(struct dw_hdmi *hdmi,
+ const struct drm_connector *connector,
+ const struct drm_display_mode *mode)
+{
+ struct hdmi_avi_infoframe frame;
+ u8 val;
+
+ /* Initialise info frame from DRM mode */
+ drm_hdmi_avi_infoframe_from_display_mode(&frame, connector, mode);
+
+ if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) {
+ drm_hdmi_avi_infoframe_quant_range(
+ &frame, connector, mode,
+ hdmi->hdmi_data.rgb_limited_range ?
+ HDMI_QUANTIZATION_RANGE_LIMITED :
+ HDMI_QUANTIZATION_RANGE_FULL);
+ } else {
+ frame.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT;
+ frame.ycc_quantization_range =
+ HDMI_YCC_QUANTIZATION_RANGE_LIMITED;
+ }
+
+ if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format))
+ frame.colorspace = HDMI_COLORSPACE_YUV444;
+ else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
+ frame.colorspace = HDMI_COLORSPACE_YUV422;
+ else if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
+ frame.colorspace = HDMI_COLORSPACE_YUV420;
+ else
+ frame.colorspace = HDMI_COLORSPACE_RGB;
+
+ /* Set up colorimetry */
+ if (!hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) {
+ switch (hdmi->hdmi_data.enc_out_encoding) {
+ case V4L2_YCBCR_ENC_601:
+ if (hdmi->hdmi_data.enc_in_encoding ==
+ V4L2_YCBCR_ENC_XV601)
+ frame.colorimetry = HDMI_COLORIMETRY_EXTENDED;
+ else
+ frame.colorimetry = HDMI_COLORIMETRY_ITU_601;
+ frame.extended_colorimetry =
+ HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
+ break;
+ case V4L2_YCBCR_ENC_709:
+ if (hdmi->hdmi_data.enc_in_encoding ==
+ V4L2_YCBCR_ENC_XV709)
+ frame.colorimetry = HDMI_COLORIMETRY_EXTENDED;
+ else
+ frame.colorimetry = HDMI_COLORIMETRY_ITU_709;
+ frame.extended_colorimetry =
+ HDMI_EXTENDED_COLORIMETRY_XV_YCC_709;
+ break;
+
+ case V4L2_YCBCR_ENC_BT2020:
+ if (hdmi->hdmi_data.enc_in_encoding ==
+ V4L2_YCBCR_ENC_BT2020)
+ frame.colorimetry = HDMI_COLORIMETRY_EXTENDED;
+ else
+ frame.colorimetry = HDMI_COLORIMETRY_ITU_709;
+ frame.extended_colorimetry =
+ HDMI_EXTENDED_COLORIMETRY_BT2020;
+ break;
+
+ default: /* Carries no data */
+ frame.colorimetry = HDMI_COLORIMETRY_ITU_601;
+ frame.extended_colorimetry =
+ HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
+ break;
+ }
+ } else {
+ frame.colorimetry = HDMI_COLORIMETRY_NONE;
+ frame.extended_colorimetry =
+ HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
+ }
+
+ frame.scan_mode = HDMI_SCAN_MODE_NONE;
+
+ /*
+ * The Designware IP uses a different byte format from standard
+ * AVI info frames, though generally the bits are in the correct
+ * bytes.
+ */
+
+ /*
+ * AVI data byte 1 differences: Colorspace in bits 0,1 rather than 5,6,
+ * scan info in bits 4,5 rather than 0,1 and active aspect present in
+ * bit 6 rather than 4.
+ */
+ val = (frame.scan_mode & 3) << 4 | (frame.colorspace & 3);
+ if (frame.active_aspect & 15)
+ val |= HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT;
+ if (frame.top_bar || frame.bottom_bar)
+ val |= HDMI_FC_AVICONF0_BAR_DATA_HORIZ_BAR;
+ if (frame.left_bar || frame.right_bar)
+ val |= HDMI_FC_AVICONF0_BAR_DATA_VERT_BAR;
+ hdmi_writeb(hdmi, val, HDMI_FC_AVICONF0);
+
+ /* AVI data byte 2 differences: none */
+ val = ((frame.colorimetry & 0x3) << 6) |
+ ((frame.picture_aspect & 0x3) << 4) | (frame.active_aspect & 0xf);
+ hdmi_writeb(hdmi, val, HDMI_FC_AVICONF1);
+
+ /* AVI data byte 3 differences: none */
+ val = ((frame.extended_colorimetry & 0x7) << 4) |
+ ((frame.quantization_range & 0x3) << 2) | (frame.nups & 0x3);
+ if (frame.itc)
+ val |= HDMI_FC_AVICONF2_IT_CONTENT_VALID;
+ hdmi_writeb(hdmi, val, HDMI_FC_AVICONF2);
+
+ /* AVI data byte 4 differences: none */
+ val = frame.video_code & 0x7f;
+ hdmi_writeb(hdmi, val, HDMI_FC_AVIVID);
+
+ /* AVI Data Byte 5- set up input and output pixel repetition */
+ val = (((hdmi->hdmi_data.video_mode.mpixelrepetitioninput + 1)
+ << HDMI_FC_PRCONF_INCOMING_PR_FACTOR_OFFSET) &
+ HDMI_FC_PRCONF_INCOMING_PR_FACTOR_MASK) |
+ ((hdmi->hdmi_data.video_mode.mpixelrepetitionoutput
+ << HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_OFFSET) &
+ HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_MASK);
+ hdmi_writeb(hdmi, val, HDMI_FC_PRCONF);
+
+ /*
+ * AVI data byte 5 differences: content type in 0,1 rather than 4,5,
+ * ycc range in bits 2,3 rather than 6,7
+ */
+ val = ((frame.ycc_quantization_range & 0x3) << 2) |
+ (frame.content_type & 0x3);
+ hdmi_writeb(hdmi, val, HDMI_FC_AVICONF3);
+
+ /* AVI Data Bytes 6-13 */
+ hdmi_writeb(hdmi, frame.top_bar & 0xff, HDMI_FC_AVIETB0);
+ hdmi_writeb(hdmi, (frame.top_bar >> 8) & 0xff, HDMI_FC_AVIETB1);
+ hdmi_writeb(hdmi, frame.bottom_bar & 0xff, HDMI_FC_AVISBB0);
+ hdmi_writeb(hdmi, (frame.bottom_bar >> 8) & 0xff, HDMI_FC_AVISBB1);
+ hdmi_writeb(hdmi, frame.left_bar & 0xff, HDMI_FC_AVIELB0);
+ hdmi_writeb(hdmi, (frame.left_bar >> 8) & 0xff, HDMI_FC_AVIELB1);
+ hdmi_writeb(hdmi, frame.right_bar & 0xff, HDMI_FC_AVISRB0);
+ hdmi_writeb(hdmi, (frame.right_bar >> 8) & 0xff, HDMI_FC_AVISRB1);
+}
+
+static void
+hdmi_config_vendor_specific_infoframe(struct dw_hdmi *hdmi,
+ const struct drm_connector *connector,
+ const struct drm_display_mode *mode)
+{
+ struct hdmi_vendor_infoframe frame;
+ u8 buffer[10];
+ ssize_t err;
+
+ err = drm_hdmi_vendor_infoframe_from_display_mode(&frame, connector,
+ mode);
+ if (err < 0)
+ /*
+ * Going into that statement does not means vendor infoframe
+ * fails. It just informed us that vendor infoframe is not
+ * needed for the selected mode. Only 4k or stereoscopic 3D
+ * mode requires vendor infoframe. So just simply return.
+ */
+ return;
+
+ err = hdmi_vendor_infoframe_pack(&frame, buffer, sizeof(buffer));
+ if (err < 0) {
+ dev_err(hdmi->dev, "Failed to pack vendor infoframe: %zd\n",
+ err);
+ return;
+ }
+ hdmi_mask_writeb(hdmi, 0, HDMI_FC_DATAUTO0, HDMI_FC_DATAUTO0_VSD_OFFSET,
+ HDMI_FC_DATAUTO0_VSD_MASK);
+
+ /* Set the length of HDMI vendor specific InfoFrame payload */
+ hdmi_writeb(hdmi, buffer[2], HDMI_FC_VSDSIZE);
+
+ /* Set 24bit IEEE Registration Identifier */
+ hdmi_writeb(hdmi, buffer[4], HDMI_FC_VSDIEEEID0);
+ hdmi_writeb(hdmi, buffer[5], HDMI_FC_VSDIEEEID1);
+ hdmi_writeb(hdmi, buffer[6], HDMI_FC_VSDIEEEID2);
+
+ /* Set HDMI_Video_Format and HDMI_VIC/3D_Structure */
+ hdmi_writeb(hdmi, buffer[7], HDMI_FC_VSDPAYLOAD0);
+ hdmi_writeb(hdmi, buffer[8], HDMI_FC_VSDPAYLOAD1);
+
+ if (frame.s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF)
+ hdmi_writeb(hdmi, buffer[9], HDMI_FC_VSDPAYLOAD2);
+
+ /* Packet frame interpolation */
+ hdmi_writeb(hdmi, 1, HDMI_FC_DATAUTO1);
+
+ /* Auto packets per frame and line spacing */
+ hdmi_writeb(hdmi, 0x11, HDMI_FC_DATAUTO2);
+
+ /* Configures the Frame Composer On RDRB mode */
+ hdmi_mask_writeb(hdmi, 1, HDMI_FC_DATAUTO0, HDMI_FC_DATAUTO0_VSD_OFFSET,
+ HDMI_FC_DATAUTO0_VSD_MASK);
+}
+
+static void hdmi_config_drm_infoframe(struct dw_hdmi *hdmi,
+ const struct drm_connector *connector)
+{
+ const struct drm_connector_state *conn_state = connector->state;
+ struct hdmi_drm_infoframe frame;
+ u8 buffer[30];
+ ssize_t err;
+ int i;
+
+ if (!hdmi->plat_data->use_drm_infoframe)
+ return;
+
+ hdmi_modb(hdmi, HDMI_FC_PACKET_TX_EN_DRM_DISABLE,
+ HDMI_FC_PACKET_TX_EN_DRM_MASK, HDMI_FC_PACKET_TX_EN);
+
+ err = drm_hdmi_infoframe_set_hdr_metadata(&frame, conn_state);
+ if (err < 0)
+ return;
+
+ err = hdmi_drm_infoframe_pack(&frame, buffer, sizeof(buffer));
+ if (err < 0) {
+ dev_err(hdmi->dev, "Failed to pack drm infoframe: %zd\n", err);
+ return;
+ }
+
+ hdmi_writeb(hdmi, frame.version, HDMI_FC_DRM_HB0);
+ hdmi_writeb(hdmi, frame.length, HDMI_FC_DRM_HB1);
+
+ for (i = 0; i < frame.length; i++)
+ hdmi_writeb(hdmi, buffer[4 + i], HDMI_FC_DRM_PB0 + i);
+
+ hdmi_writeb(hdmi, 1, HDMI_FC_DRM_UP);
+ hdmi_modb(hdmi, HDMI_FC_PACKET_TX_EN_DRM_ENABLE,
+ HDMI_FC_PACKET_TX_EN_DRM_MASK, HDMI_FC_PACKET_TX_EN);
+}
+
+static void hdmi_av_composer(struct dw_hdmi *hdmi,
+ const struct drm_display_info *display,
+ const struct drm_display_mode *mode)
+{
+ u8 inv_val, bytes;
+ const struct drm_hdmi_info *hdmi_info = &display->hdmi;
+ struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
+ int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len;
+ unsigned int vdisplay, hdisplay;
+
+ vmode->previous_pixelclock = vmode->mpixelclock;
+ vmode->mpixelclock = mode->clock * 1000;
+ if ((mode->flags & DRM_MODE_FLAG_3D_MASK) ==
+ DRM_MODE_FLAG_3D_FRAME_PACKING)
+ vmode->mpixelclock *= 2;
+
+ dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock);
+
+ vmode->previous_tmdsclock = vmode->mtmdsclock;
+ vmode->mtmdsclock = vmode->mpixelclock;
+
+ if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) {
+ switch (hdmi_bus_fmt_color_depth(
+ hdmi->hdmi_data.enc_out_bus_format)) {
+ case 16:
+ vmode->mtmdsclock = vmode->mpixelclock * 2;
+ break;
+ case 12:
+ vmode->mtmdsclock = vmode->mpixelclock * 3 / 2;
+ break;
+ case 10:
+ vmode->mtmdsclock = vmode->mpixelclock * 5 / 4;
+ break;
+ }
+ }
+
+ if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
+ vmode->mtmdsclock /= 2;
+
+ dev_dbg(hdmi->dev, "final tmdsclock = %d\n", vmode->mtmdsclock);
+
+ /* Set up HDMI_FC_INVIDCONF */
+ inv_val = (hdmi->hdmi_data.hdcp_enable ||
+ (dw_hdmi_support_scdc(hdmi, display) &&
+ (vmode->mtmdsclock > HDMI14_MAX_TMDSCLK ||
+ hdmi_info->scdc.scrambling.low_rates)) ?
+ HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE :
+ HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE);
+
+ inv_val |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
+ HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH :
+ HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW;
+
+ inv_val |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
+ HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH :
+ HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW;
+
+ inv_val |= (vmode->mdataenablepolarity ?
+ HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH :
+ HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_LOW);
+
+ if (hdmi->vic == 39)
+ inv_val |= HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH;
+ else
+ inv_val |=
+ mode->flags & DRM_MODE_FLAG_INTERLACE ?
+ HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH :
+ HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW;
+
+ inv_val |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
+ HDMI_FC_INVIDCONF_IN_I_P_INTERLACED :
+ HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE;
+
+ inv_val |= hdmi->sink_is_hdmi ? HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE :
+ HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE;
+
+ hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF);
+
+ hdisplay = mode->hdisplay;
+ hblank = mode->htotal - mode->hdisplay;
+ h_de_hs = mode->hsync_start - mode->hdisplay;
+ hsync_len = mode->hsync_end - mode->hsync_start;
+
+ /*
+ * When we're setting a YCbCr420 mode, we need
+ * to adjust the horizontal timing to suit.
+ */
+ if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) {
+ hdisplay /= 2;
+ hblank /= 2;
+ h_de_hs /= 2;
+ hsync_len /= 2;
+ }
+
+ vdisplay = mode->vdisplay;
+ vblank = mode->vtotal - mode->vdisplay;
+ v_de_vs = mode->vsync_start - mode->vdisplay;
+ vsync_len = mode->vsync_end - mode->vsync_start;
+
+ /*
+ * When we're setting an interlaced mode, we need
+ * to adjust the vertical timing to suit.
+ */
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
+ vdisplay /= 2;
+ vblank /= 2;
+ v_de_vs /= 2;
+ vsync_len /= 2;
+ } else if ((mode->flags & DRM_MODE_FLAG_3D_MASK) ==
+ DRM_MODE_FLAG_3D_FRAME_PACKING) {
+ vdisplay += mode->vtotal;
+ }
+
+ /* Scrambling Control */
+ if (dw_hdmi_support_scdc(hdmi, display)) {
+ if (vmode->mtmdsclock > HDMI14_MAX_TMDSCLK ||
+ hdmi_info->scdc.scrambling.low_rates) {
+ /*
+ * HDMI2.0 Specifies the following procedure:
+ * After the Source Device has determined that
+ * SCDC_Present is set (=1), the Source Device should
+ * write the accurate Version of the Source Device
+ * to the Source Version field in the SCDCS.
+ * Source Devices compliant shall set the
+ * Source Version = 1.
+ */
+ drm_scdc_readb(hdmi->ddc, SCDC_SINK_VERSION, &bytes);
+ drm_scdc_writeb(hdmi->ddc, SCDC_SOURCE_VERSION,
+ min_t(u8, bytes,
+ SCDC_MIN_SOURCE_VERSION));
+
+ /* Enabled Scrambling in the Sink */
+ drm_scdc_set_scrambling(hdmi->ddc, 1);
+
+ /*
+ * To activate the scrambler feature, you must ensure
+ * that the quasi-static configuration bit
+ * fc_invidconf.HDCP_keepout is set at configuration
+ * time, before the required mc_swrstzreq.tmdsswrst_req
+ * reset request is issued.
+ */
+ hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ,
+ HDMI_MC_SWRSTZ);
+ hdmi_writeb(hdmi, 1, HDMI_FC_SCRAMBLER_CTRL);
+ } else {
+ hdmi_writeb(hdmi, 0, HDMI_FC_SCRAMBLER_CTRL);
+ hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ,
+ HDMI_MC_SWRSTZ);
+ drm_scdc_set_scrambling(hdmi->ddc, 0);
+ }
+ } else {
+ hdmi_writeb(hdmi, 0, HDMI_FC_SCRAMBLER_CTRL);
+ }
+
+ /* Set up horizontal active pixel width */
+ hdmi_writeb(hdmi, hdisplay >> 8, HDMI_FC_INHACTV1);
+ hdmi_writeb(hdmi, hdisplay, HDMI_FC_INHACTV0);
+
+ /* Set up vertical active lines */
+ hdmi_writeb(hdmi, vdisplay >> 8, HDMI_FC_INVACTV1);
+ hdmi_writeb(hdmi, vdisplay, HDMI_FC_INVACTV0);
+
+ /* Set up horizontal blanking pixel region width */
+ hdmi_writeb(hdmi, hblank >> 8, HDMI_FC_INHBLANK1);
+ hdmi_writeb(hdmi, hblank, HDMI_FC_INHBLANK0);
+
+ /* Set up vertical blanking pixel region width */
+ hdmi_writeb(hdmi, vblank, HDMI_FC_INVBLANK);
+
+ /* Set up HSYNC active edge delay width (in pixel clks) */
+ hdmi_writeb(hdmi, h_de_hs >> 8, HDMI_FC_HSYNCINDELAY1);
+ hdmi_writeb(hdmi, h_de_hs, HDMI_FC_HSYNCINDELAY0);
+
+ /* Set up VSYNC active edge delay (in lines) */
+ hdmi_writeb(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY);
+
+ /* Set up HSYNC active pulse width (in pixel clks) */
+ hdmi_writeb(hdmi, hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1);
+ hdmi_writeb(hdmi, hsync_len, HDMI_FC_HSYNCINWIDTH0);
+
+ /* Set up VSYNC active edge delay (in lines) */
+ hdmi_writeb(hdmi, vsync_len, HDMI_FC_VSYNCINWIDTH);
+}
+
+/* HDMI Initialization Step B.4 */
+static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi)
+{
+ /* control period minimum duration */
+ hdmi_writeb(hdmi, 12, HDMI_FC_CTRLDUR);
+ hdmi_writeb(hdmi, 32, HDMI_FC_EXCTRLDUR);
+ hdmi_writeb(hdmi, 1, HDMI_FC_EXCTRLSPAC);
+
+ /* Set to fill TMDS data channels */
+ hdmi_writeb(hdmi, 0x0B, HDMI_FC_CH0PREAM);
+ hdmi_writeb(hdmi, 0x16, HDMI_FC_CH1PREAM);
+ hdmi_writeb(hdmi, 0x21, HDMI_FC_CH2PREAM);
+
+ /* Enable pixel clock and tmds data path */
+ hdmi->mc_clkdis |=
+ HDMI_MC_CLKDIS_HDCPCLK_DISABLE | HDMI_MC_CLKDIS_CSCCLK_DISABLE |
+ HDMI_MC_CLKDIS_AUDCLK_DISABLE | HDMI_MC_CLKDIS_PREPCLK_DISABLE |
+ HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
+ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
+ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+
+ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
+ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+
+ /* Enable csc path */
+ if (is_csc_needed(hdmi)) {
+ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
+ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+
+ hdmi_writeb(hdmi, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH,
+ HDMI_MC_FLOWCTRL);
+ } else {
+ hdmi->mc_clkdis |= HDMI_MC_CLKDIS_CSCCLK_DISABLE;
+ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+
+ hdmi_writeb(hdmi, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS,
+ HDMI_MC_FLOWCTRL);
+ }
+}
+
+/* Workaround to clear the overflow condition */
+static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi)
+{
+ unsigned int count;
+ unsigned int i;
+ u8 val;
+
+ /*
+ * Under some circumstances the Frame Composer arithmetic unit can miss
+ * an FC register write due to being busy processing the previous one.
+ * The issue can be worked around by issuing a TMDS software reset and
+ * then write one of the FC registers several times.
+ *
+ * The number of iterations matters and depends on the HDMI TX revision
+ * (and possibly on the platform). So far i.MX6Q (v1.30a), i.MX6DL
+ * (v1.31a) and multiple Allwinner SoCs (v1.32a) have been identified
+ * as needing the workaround, with 4 iterations for v1.30a and 1
+ * iteration for others.
+ * The Amlogic Meson GX SoCs (v2.01a) have been identified as needing
+ * the workaround with a single iteration.
+ * The ESWIN SOC WIN2030(v2.14a) have been identified as needing the
+ * workaround with a single iteration.
+ */
+
+ switch (hdmi->version) {
+ case 0x130a:
+ count = 4;
+ break;
+ case 0x131a:
+ case 0x132a:
+ case 0x200a:
+ case 0x201a:
+ case 0x211a:
+ case 0x212a:
+ case 0x214a:
+ count = 1;
+ break;
+ default:
+ return;
+ }
+
+ /* TMDS software reset */
+ hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ);
+
+ val = hdmi_readb(hdmi, HDMI_FC_INVIDCONF);
+ for (i = 0; i < count; i++)
+ hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF);
+ if (!hdmi->eswin_plat) {
+ val = hdmi_readb(hdmi, HDMI_FC_VSYNCINWIDTH);
+ for (i = 0; i < count; i++)
+ hdmi_writeb(hdmi, val, HDMI_FC_VSYNCINWIDTH);
+ }
+ /* Audio software reset */
+ if (hdmi->sink_has_audio) {
+ val = hdmi_readb(hdmi, HDMI_AUD_CONF0);
+ val &= HDMI_AUD_CONF0_I2S_SELECT_MASK;
+ hdmi_modb(hdmi, ~val, HDMI_AUD_CONF0_I2S_SELECT_MASK,
+ HDMI_AUD_CONF0);
+ udelay(10);
+ hdmi_modb(hdmi, val | HDMI_AUD_CONF0_SW_RESET,
+ HDMI_AUD_CONF0_SW_RESET |
+ HDMI_AUD_CONF0_I2S_SELECT_MASK,
+ HDMI_AUD_CONF0);
+ }
+}
+
+static void hdmi_disable_overflow_interrupts(struct dw_hdmi *hdmi)
+{
+ hdmi_writeb(hdmi, HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK,
+ HDMI_IH_MUTE_FC_STAT2);
+}
+
+static int dw_hdmi_setup(struct dw_hdmi *hdmi,
+ const struct drm_connector *connector,
+ const struct drm_display_mode *mode)
+{
+ void *data = hdmi->plat_data->phy_data;
+ int ret;
+
+ hdmi_disable_overflow_interrupts(hdmi);
+
+ hdmi->vic = drm_match_cea_mode(mode);
+
+ if (!hdmi->vic) {
+ dev_dbg(hdmi->dev, "Non-CEA mode used in HDMI\n");
+ } else {
+ dev_dbg(hdmi->dev, "CEA mode used vic=%d\n", hdmi->vic);
+ }
+
+ if (hdmi->plat_data->get_enc_out_encoding)
+ hdmi->hdmi_data.enc_out_encoding =
+ hdmi->plat_data->get_enc_out_encoding(data);
+ else if ((hdmi->vic == 6) || (hdmi->vic == 7) || (hdmi->vic == 21) ||
+ (hdmi->vic == 22) || (hdmi->vic == 2) || (hdmi->vic == 3) ||
+ (hdmi->vic == 17) || (hdmi->vic == 18))
+ hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_601;
+ else
+ hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_709;
+
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
+ hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 1;
+ hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 1;
+ } else {
+ hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0;
+ hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0;
+ }
+
+ /* TOFIX: Get input format from plat data or fallback to RGB888 */
+ if (hdmi->plat_data->get_input_bus_format)
+ hdmi->hdmi_data.enc_in_bus_format =
+ hdmi->plat_data->get_input_bus_format(data);
+ else if (hdmi->hdmi_data.enc_in_bus_format == MEDIA_BUS_FMT_FIXED)
+ hdmi->hdmi_data.enc_in_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+ else
+ hdmi->hdmi_data.enc_in_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+
+ /* TOFIX: Get input encoding from plat data or fallback to none */
+ if (hdmi->plat_data->get_enc_in_encoding)
+ hdmi->hdmi_data.enc_in_encoding =
+ hdmi->plat_data->get_enc_in_encoding(data);
+ else
+ hdmi->hdmi_data.enc_in_encoding = V4L2_YCBCR_ENC_DEFAULT;
+
+ /* TOFIX: Default to RGB888 output format */
+ if (hdmi->plat_data->get_output_bus_format)
+ hdmi->hdmi_data.enc_out_bus_format =
+ hdmi->plat_data->get_output_bus_format(data);
+ else if (hdmi->hdmi_data.enc_out_bus_format == MEDIA_BUS_FMT_FIXED)
+ hdmi->hdmi_data.enc_out_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+ else
+ hdmi->hdmi_data.enc_out_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+
+ hdmi->hdmi_data.rgb_limited_range =
+ hdmi->sink_is_hdmi && drm_default_rgb_quant_range(mode) ==
+ HDMI_QUANTIZATION_RANGE_LIMITED;
+
+ hdmi->hdmi_data.pix_repet_factor = 0;
+ hdmi->hdmi_data.hdcp_enable = 0;
+ hdmi->hdmi_data.video_mode.mdataenablepolarity = true;
+
+ /* HDMI Initialization Step B.1 */
+ hdmi_av_composer(hdmi, &connector->display_info, mode);
+ /* HDMI Initializateion Step B.2 */
+ ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data,
+ &connector->display_info,
+ &hdmi->previous_mode);
+ if (ret)
+ return ret;
+ hdmi->phy.enabled = true;
+ /* HDMI Initialization Step B.3 */
+ dw_hdmi_enable_video_path(hdmi);
+
+ if (hdmi->sink_has_audio) {
+ dev_dbg(hdmi->dev, "sink has audio support\n");
+
+ /* HDMI Initialization Step E - Configure audio */
+ hdmi_clk_regenerator_update_pixel_clock(hdmi);
+ hdmi_enable_audio_clk(hdmi, hdmi->audio_enable);
+ }
+
+ /* not for DVI mode */
+ if (hdmi->sink_is_hdmi) {
+ dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__);
+
+ /* HDMI Initialization Step F - Configure AVI InfoFrame */
+ hdmi_config_AVI(hdmi, connector, mode);
+ hdmi_config_vendor_specific_infoframe(hdmi, connector, mode);
+ hdmi_config_drm_infoframe(hdmi, connector);
+ } else {
+ dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
+ }
+ hdmi_video_packetize(hdmi);
+ hdmi_video_csc(hdmi);
+ hdmi_video_sample(hdmi);
+ if (!hpd_flag) {
+ hdmi_tx_hdcp_config(hdmi, mode);
+ }
+ dw_hdmi_clear_overflow(hdmi);
+
+ return 0;
+}
+
+static void initialize_hdmi_ih_mutes(struct dw_hdmi *hdmi)
+{
+ u8 ih_mute;
+ /*
+ * Boot up defaults are:
+ * HDMI_IH_MUTE = 0x03 (disabled)
+ * HDMI_IH_MUTE_* = 0x00 (enabled)
+ *
+ * Disable top level interrupt bits in HDMI block
+ */
+ ih_mute = hdmi_readb(hdmi, HDMI_IH_MUTE) |
+ HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT |
+ HDMI_IH_MUTE_MUTE_ALL_INTERRUPT;
+
+ hdmi_writeb(hdmi, ih_mute, HDMI_IH_MUTE);
+
+ /* by default mask all interrupts */
+ hdmi_writeb(hdmi, 0xff, HDMI_VP_MASK);
+ hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK0);
+ hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK1);
+ hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK2);
+ hdmi_writeb(hdmi, 0xff, HDMI_PHY_MASK0);
+ hdmi_writeb(hdmi, 0xff, HDMI_PHY_I2CM_INT_ADDR);
+ hdmi_writeb(hdmi, 0xff, HDMI_PHY_I2CM_CTLINT_ADDR);
+ hdmi_writeb(hdmi, 0xff, HDMI_AUD_INT);
+ hdmi_writeb(hdmi, 0xff, HDMI_AUD_SPDIFINT);
+ hdmi_writeb(hdmi, 0xff, HDMI_AUD_HBR_MASK);
+ hdmi_writeb(hdmi, 0xff, HDMI_GP_MASK);
+ hdmi_writeb(hdmi, 0xff, HDMI_A_APIINTMSK);
+ hdmi_writeb(hdmi, 0xff, HDMI_I2CM_INT);
+ hdmi_writeb(hdmi, 0xff, HDMI_I2CM_CTLINT);
+
+ /* Disable interrupts in the IH_MUTE_* registers */
+ hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT0);
+ hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT1);
+ hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT2);
+ hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AS_STAT0);
+ hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_PHY_STAT0);
+ hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CM_STAT0);
+ hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_CEC_STAT0);
+ hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_VP_STAT0);
+ hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CMPHY_STAT0);
+ hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+ /* Enable top level interrupt bits in HDMI block */
+ ih_mute &= ~(HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT |
+ HDMI_IH_MUTE_MUTE_ALL_INTERRUPT);
+ hdmi_writeb(hdmi, ih_mute, HDMI_IH_MUTE);
+}
+
+static void dw_hdmi_poweron(struct dw_hdmi *hdmi)
+{
+ hdmi->bridge_is_on = true;
+ /*
+ * The curr_conn field is guaranteed to be valid here, as this function
+ * is only be called when !hdmi->disabled.
+ */
+ dev_dbg(hdmi->dev, "%s", __func__);
+ dw_hdmi_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode);
+}
+
+static void dw_hdmi_poweroff(struct dw_hdmi *hdmi)
+{
+ dev_dbg(hdmi->dev, "%s", __func__);
+ if (hdmi->phy.enabled) {
+ hdmi->phy.ops->disable(hdmi, hdmi->phy.data);
+ hdmi->phy.enabled = false;
+ }
+ hdmi->bridge_is_on = false;
+}
+
+static void dw_hdmi_update_power(struct dw_hdmi *hdmi)
+{
+ int force = hdmi->force;
+
+ if (hdmi->disabled) {
+ force = DRM_FORCE_OFF;
+ } else if (force == DRM_FORCE_UNSPECIFIED) {
+ if (hdmi->rxsense)
+ force = DRM_FORCE_ON;
+ else
+ force = DRM_FORCE_OFF;
+ }
+
+ if (force == DRM_FORCE_OFF) {
+ if (hdmi->bridge_is_on)
+ dw_hdmi_poweroff(hdmi);
+ } else {
+ if (!hdmi->bridge_is_on) {
+ struct edid *edid;
+ edid = dw_hdmi_get_edid(hdmi, &hdmi->connector);
+ dw_hdmi_poweron(hdmi);
+ }
+ }
+}
+
+/*
+ * Adjust the detection of RXSENSE according to whether we have a forced
+ * connection mode enabled, or whether we have been disabled. There is
+ * no point processing RXSENSE interrupts if we have a forced connection
+ * state, or DRM has us disabled.
+ *
+ * We also disable rxsense interrupts when we think we're disconnected
+ * to avoid floating TDMS signals giving false rxsense interrupts.
+ *
+ * Note: we still need to listen for HPD interrupts even when DRM has us
+ * disabled so that we can detect a connect event.
+ */
+static void dw_hdmi_update_phy_mask(struct dw_hdmi *hdmi)
+{
+ if (hdmi->phy.ops->update_hpd)
+ hdmi->phy.ops->update_hpd(hdmi, hdmi->phy.data, hdmi->force,
+ hdmi->disabled, hdmi->rxsense);
+}
+
+static enum drm_connector_status dw_hdmi_detect(struct dw_hdmi *hdmi)
+{
+ enum drm_connector_status result;
+ result = hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data);
+
+ mutex_lock(&hdmi->mutex);
+ if (result != hdmi->last_connector_result) {
+ dev_info(hdmi->dev, "read_hpd result: %d", result);
+ handle_plugged_change(hdmi,
+ result == connector_status_connected);
+ hdmi->last_connector_result = result;
+ }
+ mutex_unlock(&hdmi->mutex);
+
+ return result;
+}
+
+static struct edid *dw_hdmi_get_edid(struct dw_hdmi *hdmi,
+ struct drm_connector *connector)
+{
+ struct edid *edid;
+
+ if (!hdmi->ddc)
+ return NULL;
+
+ edid = drm_get_edid(connector, hdmi->ddc);
+ if (!edid) {
+ dev_err(hdmi->dev, "failed to get edid\n");
+ return NULL;
+ }
+
+ dev_info(hdmi->dev, "got edid: width[%d] x height[%d]\n",
+ edid->width_cm, edid->height_cm);
+
+ hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
+ hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
+
+ return edid;
+}
+
+/* -----------------------------------------------------------------------------
+ * DRM Connector Operations
+ */
+
+static enum drm_connector_status
+dw_hdmi_connector_detect(struct drm_connector *connector, bool force)
+{
+ struct dw_hdmi *hdmi =
+ container_of(connector, struct dw_hdmi, connector);
+ return dw_hdmi_detect(hdmi);
+}
+
+static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+ struct dw_hdmi *hdmi =
+ container_of(connector, struct dw_hdmi, connector);
+ struct edid *edid;
+ int ret;
+
+ edid = dw_hdmi_get_edid(hdmi, connector);
+ if (!edid)
+ return 0;
+
+ drm_connector_update_edid_property(connector, edid);
+ cec_notifier_set_phys_addr_from_edid(hdmi->cec_notifier, edid);
+ ret = drm_add_edid_modes(connector, edid);
+ kfree(edid);
+
+ return ret;
+}
+
+static int dw_hdmi_connector_atomic_check(struct drm_connector *connector,
+ struct drm_atomic_state *state)
+{
+ struct drm_connector_state *old_state =
+ drm_atomic_get_old_connector_state(state, connector);
+ struct drm_connector_state *new_state =
+ drm_atomic_get_new_connector_state(state, connector);
+ struct drm_crtc *crtc = new_state->crtc;
+ struct drm_crtc_state *crtc_state;
+
+ if (!crtc)
+ return 0;
+
+ if (!drm_connector_atomic_hdr_metadata_equal(old_state, new_state)) {
+ crtc_state = drm_atomic_get_crtc_state(state, crtc);
+ if (IS_ERR(crtc_state))
+ return PTR_ERR(crtc_state);
+
+ crtc_state->mode_changed = true;
+ }
+
+ return 0;
+}
+
+static void dw_hdmi_connector_force(struct drm_connector *connector)
+{
+ struct dw_hdmi *hdmi =
+ container_of(connector, struct dw_hdmi, connector);
+
+ mutex_lock(&hdmi->mutex);
+ hdmi->force = connector->force;
+ dw_hdmi_update_power(hdmi);
+ dw_hdmi_update_phy_mask(hdmi);
+ mutex_unlock(&hdmi->mutex);
+}
+
+static int dw_hdmi_atomic_connector_set_property(
+ struct drm_connector *connector, struct drm_connector_state *state,
+ struct drm_property *property, uint64_t val)
+{
+ struct dw_hdmi *hdmi =
+ container_of(connector, struct dw_hdmi, connector);
+ const struct dw_hdmi_property_ops *ops = hdmi->plat_data->property_ops;
+
+ if (ops && ops->set_property)
+ return ops->set_property(connector, state, property, val,
+ hdmi->plat_data->phy_data);
+ else
+ return -EINVAL;
+}
+
+static int
+dw_hdmi_atomic_connector_get_property(struct drm_connector *connector,
+ const struct drm_connector_state *state,
+ struct drm_property *property,
+ uint64_t *val)
+{
+ struct dw_hdmi *hdmi =
+ container_of(connector, struct dw_hdmi, connector);
+ const struct dw_hdmi_property_ops *ops = hdmi->plat_data->property_ops;
+
+ if (ops && ops->get_property)
+ return ops->get_property(connector, state, property, val,
+ hdmi->plat_data->phy_data);
+ else
+ return -EINVAL;
+}
+
+static int dw_hdmi_connector_set_property(struct drm_connector *connector,
+ struct drm_property *property,
+ uint64_t val)
+{
+ return dw_hdmi_atomic_connector_set_property(connector, NULL, property,
+ val);
+}
+
+static const struct drm_connector_funcs dw_hdmi_connector_funcs = {
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = dw_hdmi_connector_detect,
+ .destroy = drm_connector_cleanup,
+ .force = dw_hdmi_connector_force,
+ .reset = drm_atomic_helper_connector_reset,
+ .set_property = dw_hdmi_connector_set_property,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+ .atomic_set_property = dw_hdmi_atomic_connector_set_property,
+ .atomic_get_property = dw_hdmi_atomic_connector_get_property,
+};
+
+static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = {
+ .get_modes = dw_hdmi_connector_get_modes,
+ .atomic_check = dw_hdmi_connector_atomic_check,
+};
+
+static void dw_hdmi_attatch_properties(struct dw_hdmi *hdmi)
+{
+ unsigned int color = MEDIA_BUS_FMT_RGB888_1X24;
+ int video_mapping, colorspace;
+
+ enum drm_connector_status connect_status =
+ hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data);
+ const struct dw_hdmi_property_ops *ops = hdmi->plat_data->property_ops;
+
+ if (connect_status == connector_status_connected) {
+ video_mapping = (hdmi_readb(hdmi, HDMI_TX_INVID0) &
+ HDMI_TX_INVID0_VIDEO_MAPPING_MASK);
+ colorspace = (hdmi_readb(hdmi, HDMI_FC_AVICONF0) &
+ HDMI_FC_AVICONF0_PIX_FMT_MASK);
+ switch (video_mapping) {
+ case 0x01:
+ color = MEDIA_BUS_FMT_RGB888_1X24;
+ break;
+ case 0x03:
+ color = MEDIA_BUS_FMT_RGB101010_1X30;
+ break;
+ case 0x09:
+ if (colorspace == HDMI_COLORSPACE_YUV420)
+ color = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
+ else if (colorspace == HDMI_COLORSPACE_YUV422)
+ color = MEDIA_BUS_FMT_UYVY8_1X16;
+ else
+ color = MEDIA_BUS_FMT_YUV8_1X24;
+ break;
+ case 0x0b:
+ if (colorspace == HDMI_COLORSPACE_YUV420)
+ color = MEDIA_BUS_FMT_UYYVYY10_0_5X30;
+ else if (colorspace == HDMI_COLORSPACE_YUV422)
+ color = MEDIA_BUS_FMT_UYVY10_1X20;
+ else
+ color = MEDIA_BUS_FMT_YUV10_1X30;
+ break;
+ case 0x14:
+ color = MEDIA_BUS_FMT_UYVY10_1X20;
+ break;
+ case 0x16:
+ color = MEDIA_BUS_FMT_UYVY8_1X16;
+ break;
+ default:
+ color = MEDIA_BUS_FMT_RGB888_1X24;
+ dev_err(hdmi->dev, "unexpected mapping: 0x%x\n",
+ video_mapping);
+ }
+
+ hdmi->hdmi_data.enc_in_bus_format = color;
+ hdmi->hdmi_data.enc_out_bus_format = color;
+ /*
+ * input format will be set as yuv444 when output
+ * format is yuv420
+ */
+ if (color == MEDIA_BUS_FMT_UYVY10_1X20)
+ hdmi->hdmi_data.enc_in_bus_format =
+ MEDIA_BUS_FMT_YUV10_1X30;
+ else if (color == MEDIA_BUS_FMT_UYVY8_1X16)
+ hdmi->hdmi_data.enc_in_bus_format =
+ MEDIA_BUS_FMT_YUV8_1X24;
+ }
+
+ if (ops && ops->attatch_properties)
+ return ops->attatch_properties(&hdmi->connector, color,
+ hdmi->version,
+ hdmi->plat_data->phy_data);
+}
+
+static void dw_hdmi_destroy_properties(struct dw_hdmi *hdmi)
+{
+ const struct dw_hdmi_property_ops *ops = hdmi->plat_data->property_ops;
+
+ if (ops && ops->destroy_properties)
+ return ops->destroy_properties(&hdmi->connector,
+ hdmi->plat_data->phy_data);
+}
+
+static int dw_hdmi_connector_create(struct dw_hdmi *hdmi)
+{
+ struct drm_connector *connector = &hdmi->connector;
+ struct cec_connector_info conn_info;
+ struct cec_notifier *notifier;
+
+ if (hdmi->version >= 0x200a)
+ connector->ycbcr_420_allowed =
+ hdmi->plat_data->ycbcr_420_allowed;
+ else
+ connector->ycbcr_420_allowed = false;
+
+ connector->interlace_allowed = 1;
+ connector->polled = DRM_CONNECTOR_POLL_HPD;
+
+ drm_connector_helper_add(connector, &dw_hdmi_connector_helper_funcs);
+
+ drm_connector_init_with_ddc(hdmi->bridge.dev, connector,
+ &dw_hdmi_connector_funcs,
+ DRM_MODE_CONNECTOR_HDMIA, hdmi->ddc);
+
+ /*
+ * drm_connector_attach_max_bpc_property() requires the
+ * connector to have a state.
+ */
+ drm_atomic_helper_connector_reset(connector);
+
+ drm_connector_attach_max_bpc_property(connector, 8, 16);
+
+ if (hdmi->version >= 0x200a && hdmi->plat_data->use_drm_infoframe)
+ drm_connector_attach_hdr_output_metadata_property(connector);
+
+ drm_connector_attach_encoder(connector, hdmi->bridge.encoder);
+
+ dw_hdmi_attatch_properties(hdmi);
+
+ cec_fill_conn_info_from_drm(&conn_info, connector);
+
+ notifier = cec_notifier_conn_register(hdmi->dev, NULL, &conn_info);
+ if (!notifier)
+ return -ENOMEM;
+
+ mutex_lock(&hdmi->cec_notifier_mutex);
+ hdmi->cec_notifier = notifier;
+ mutex_unlock(&hdmi->cec_notifier_mutex);
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * DRM Bridge Operations
+ */
+
+/*
+ * Possible output formats :
+ * - MEDIA_BUS_FMT_UYYVYY16_0_5X48,
+ * - MEDIA_BUS_FMT_UYYVYY12_0_5X36,
+ * - MEDIA_BUS_FMT_UYYVYY10_0_5X30,
+ * - MEDIA_BUS_FMT_UYYVYY8_0_5X24,
+ * - MEDIA_BUS_FMT_YUV16_1X48,
+ * - MEDIA_BUS_FMT_RGB161616_1X48,
+ * - MEDIA_BUS_FMT_UYVY12_1X24,
+ * - MEDIA_BUS_FMT_YUV12_1X36,
+ * - MEDIA_BUS_FMT_RGB121212_1X36,
+ * - MEDIA_BUS_FMT_UYVY10_1X20,
+ * - MEDIA_BUS_FMT_YUV10_1X30,
+ * - MEDIA_BUS_FMT_RGB101010_1X30,
+ * - MEDIA_BUS_FMT_UYVY8_1X16,
+ * - MEDIA_BUS_FMT_YUV8_1X24,
+ * - MEDIA_BUS_FMT_RGB888_1X24,
+ */
+
+/* Can return a maximum of 11 possible output formats for a mode/connector */
+#define MAX_OUTPUT_SEL_FORMATS 11
+
+static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(
+ struct drm_bridge *bridge, struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state, unsigned int *num_output_fmts)
+{
+ struct drm_connector *conn = conn_state->connector;
+ struct drm_display_info *info = &conn->display_info;
+ struct drm_display_mode *mode = &crtc_state->mode;
+ u8 max_bpc = conn_state->max_requested_bpc;
+ bool is_hdmi2_sink = info->hdmi.scdc.supported ||
+ (info->color_formats & DRM_COLOR_FORMAT_YCRCB420);
+ u32 *output_fmts;
+ unsigned int i = 0;
+
+ *num_output_fmts = 0;
+
+ output_fmts = kcalloc(MAX_OUTPUT_SEL_FORMATS, sizeof(*output_fmts),
+ GFP_KERNEL);
+ if (!output_fmts)
+ return NULL;
+
+ /* If dw-hdmi is the only bridge, avoid negociating with ourselves */
+ if (list_is_singular(&bridge->encoder->bridge_chain)) {
+ *num_output_fmts = 1;
+ output_fmts[0] = MEDIA_BUS_FMT_FIXED;
+
+ return output_fmts;
+ }
+
+ /*
+ * If the current mode enforces 4:2:0, force the output but format
+ * to 4:2:0 and do not add the YUV422/444/RGB formats
+ */
+ if (conn->ycbcr_420_allowed &&
+ (drm_mode_is_420_only(info, mode) ||
+ (is_hdmi2_sink && drm_mode_is_420_also(info, mode)))) {
+ /* Order bus formats from 16bit to 8bit if supported */
+ if (max_bpc >= 16 && info->bpc == 16 &&
+ (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48))
+ output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY16_0_5X48;
+
+ if (max_bpc >= 12 && info->bpc >= 12 &&
+ (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36))
+ output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY12_0_5X36;
+
+ if (max_bpc >= 10 && info->bpc >= 10 &&
+ (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30))
+ output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY10_0_5X30;
+
+ /* Default 8bit fallback */
+ output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
+
+ *num_output_fmts = i;
+
+ return output_fmts;
+ }
+
+ /*
+ * Order bus formats from 16bit to 8bit and from YUV422 to RGB
+ * if supported. In any case the default RGB888 format is added
+ */
+
+ if (max_bpc >= 16 && info->bpc == 16) {
+ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+ output_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
+
+ output_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
+ }
+
+ if (max_bpc >= 12 && info->bpc >= 12) {
+ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+ output_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
+
+ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+ output_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
+
+ output_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
+ }
+
+ if (max_bpc >= 10 && info->bpc >= 10) {
+ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+ output_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
+
+ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+ output_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
+
+ output_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
+ }
+
+ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+ output_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
+
+ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+ output_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
+
+ /* Default 8bit RGB fallback */
+ output_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
+
+ *num_output_fmts = i;
+
+ return output_fmts;
+}
+
+/*
+ * Possible input formats :
+ * - MEDIA_BUS_FMT_RGB888_1X24
+ * - MEDIA_BUS_FMT_YUV8_1X24
+ * - MEDIA_BUS_FMT_UYVY8_1X16
+ * - MEDIA_BUS_FMT_UYYVYY8_0_5X24
+ * - MEDIA_BUS_FMT_RGB101010_1X30
+ * - MEDIA_BUS_FMT_YUV10_1X30
+ * - MEDIA_BUS_FMT_UYVY10_1X20
+ * - MEDIA_BUS_FMT_UYYVYY10_0_5X30
+ * - MEDIA_BUS_FMT_RGB121212_1X36
+ * - MEDIA_BUS_FMT_YUV12_1X36
+ * - MEDIA_BUS_FMT_UYVY12_1X24
+ * - MEDIA_BUS_FMT_UYYVYY12_0_5X36
+ * - MEDIA_BUS_FMT_RGB161616_1X48
+ * - MEDIA_BUS_FMT_YUV16_1X48
+ * - MEDIA_BUS_FMT_UYYVYY16_0_5X48
+ */
+
+/* Can return a maximum of 3 possible input formats for an output format */
+#define MAX_INPUT_SEL_FORMATS 3
+
+static u32 *dw_hdmi_bridge_atomic_get_input_bus_fmts(
+ struct drm_bridge *bridge, struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state, u32 output_fmt,
+ unsigned int *num_input_fmts)
+{
+ u32 *input_fmts;
+ unsigned int i = 0;
+
+ *num_input_fmts = 0;
+
+ input_fmts =
+ kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts), GFP_KERNEL);
+ if (!input_fmts)
+ return NULL;
+
+ switch (output_fmt) {
+ /* If MEDIA_BUS_FMT_FIXED is tested, return default bus format */
+ case MEDIA_BUS_FMT_FIXED:
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
+ break;
+ /* 8bit */
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
+ input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
+ break;
+ case MEDIA_BUS_FMT_YUV8_1X24:
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
+ input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
+ break;
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
+ break;
+
+ /* 10bit */
+ case MEDIA_BUS_FMT_RGB101010_1X30:
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
+ input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
+ break;
+ case MEDIA_BUS_FMT_YUV10_1X30:
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
+ input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
+ break;
+ case MEDIA_BUS_FMT_UYVY10_1X20:
+ input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
+ break;
+
+ /* 12bit */
+ case MEDIA_BUS_FMT_RGB121212_1X36:
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
+ input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
+ break;
+ case MEDIA_BUS_FMT_YUV12_1X36:
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
+ input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
+ break;
+ case MEDIA_BUS_FMT_UYVY12_1X24:
+ input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
+ break;
+
+ /* 16bit */
+ case MEDIA_BUS_FMT_RGB161616_1X48:
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
+ break;
+ case MEDIA_BUS_FMT_YUV16_1X48:
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
+ break;
+
+ /*YUV 4:2:0 */
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+ case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
+ case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
+ input_fmts[i++] = output_fmt;
+ break;
+ }
+
+ *num_input_fmts = i;
+
+ if (*num_input_fmts == 0) {
+ kfree(input_fmts);
+ input_fmts = NULL;
+ }
+
+ return input_fmts;
+}
+
+static int dw_hdmi_bridge_atomic_check(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ struct dw_hdmi *hdmi = bridge->driver_private;
+
+ hdmi->hdmi_data.enc_out_bus_format =
+ bridge_state->output_bus_cfg.format;
+
+ hdmi->hdmi_data.enc_in_bus_format = bridge_state->input_bus_cfg.format;
+
+ dev_dbg(hdmi->dev, "input format 0x%04x, output format 0x%04x\n",
+ bridge_state->input_bus_cfg.format,
+ bridge_state->output_bus_cfg.format);
+
+ return 0;
+}
+
+static int dw_hdmi_bridge_attach(struct drm_bridge *bridge,
+ enum drm_bridge_attach_flags flags)
+{
+ struct dw_hdmi *hdmi = bridge->driver_private;
+
+ if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
+ return drm_bridge_attach(bridge->encoder, hdmi->next_bridge,
+ bridge, flags);
+
+ return dw_hdmi_connector_create(hdmi);
+}
+
+static void dw_hdmi_bridge_detach(struct drm_bridge *bridge)
+{
+ struct dw_hdmi *hdmi = bridge->driver_private;
+
+ mutex_lock(&hdmi->cec_notifier_mutex);
+ cec_notifier_conn_unregister(hdmi->cec_notifier);
+ hdmi->cec_notifier = NULL;
+ mutex_unlock(&hdmi->cec_notifier_mutex);
+}
+
+static enum drm_mode_status
+dw_hdmi_bridge_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_info *info,
+ const struct drm_display_mode *mode)
+{
+ struct dw_hdmi *hdmi = bridge->driver_private;
+ const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
+ enum drm_mode_status mode_status = MODE_OK;
+
+ /* We don't support double-clocked modes */
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+ return MODE_BAD;
+
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ return MODE_NO_INTERLACE;
+
+ if (mode->clock != 594000 && mode->clock != 297000 &&
+ mode->clock != 148500 && mode->clock != 108000 &&
+ mode->clock != 74250 && mode->clock != 54000 &&
+ mode->clock != 27000) {
+ return MODE_NOCLOCK;
+ }
+
+ if (pdata->mode_valid)
+ mode_status =
+ pdata->mode_valid(hdmi, pdata->priv_data, info, mode);
+
+ return mode_status;
+}
+
+static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
+ const struct drm_display_mode *orig_mode,
+ const struct drm_display_mode *mode)
+{
+ struct dw_hdmi *hdmi = bridge->driver_private;
+
+ mutex_lock(&hdmi->mutex);
+
+ /* Store the display mode for plugin/DKMS poweron events */
+ memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
+
+ mutex_unlock(&hdmi->mutex);
+}
+
+static void dw_hdmi_bridge_atomic_disable(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_state)
+{
+ struct dw_hdmi *hdmi = bridge->driver_private;
+
+ mutex_lock(&hdmi->mutex);
+ hdmi->disabled = true;
+ hdmi->curr_conn = NULL;
+ dw_hdmi_update_power(hdmi);
+ dw_hdmi_update_phy_mask(hdmi);
+ mutex_unlock(&hdmi->mutex);
+}
+
+static void dw_hdmi_bridge_atomic_enable(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_state)
+{
+ struct dw_hdmi *hdmi = bridge->driver_private;
+ struct drm_atomic_state *state = old_state->base.state;
+ struct drm_connector *connector;
+
+ connector = drm_atomic_get_new_connector_for_encoder(state,
+ bridge->encoder);
+
+ mutex_lock(&hdmi->mutex);
+ hdmi->disabled = false;
+ hdmi->curr_conn = connector;
+ dw_hdmi_update_power(hdmi);
+ dw_hdmi_update_phy_mask(hdmi);
+ mutex_unlock(&hdmi->mutex);
+}
+
+static enum drm_connector_status
+dw_hdmi_bridge_detect(struct drm_bridge *bridge)
+{
+ struct dw_hdmi *hdmi = bridge->driver_private;
+
+ return dw_hdmi_detect(hdmi);
+}
+
+static struct edid *dw_hdmi_bridge_get_edid(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+ struct dw_hdmi *hdmi = bridge->driver_private;
+
+ return dw_hdmi_get_edid(hdmi, connector);
+}
+
+static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+ .atomic_reset = drm_atomic_helper_bridge_reset,
+ .attach = dw_hdmi_bridge_attach,
+ .detach = dw_hdmi_bridge_detach,
+ .atomic_check = dw_hdmi_bridge_atomic_check,
+ .atomic_get_output_bus_fmts = dw_hdmi_bridge_atomic_get_output_bus_fmts,
+ .atomic_get_input_bus_fmts = dw_hdmi_bridge_atomic_get_input_bus_fmts,
+ .atomic_enable = dw_hdmi_bridge_atomic_enable,
+ .atomic_disable = dw_hdmi_bridge_atomic_disable,
+ .mode_set = dw_hdmi_bridge_mode_set,
+ .mode_valid = dw_hdmi_bridge_mode_valid,
+ .detect = dw_hdmi_bridge_detect,
+ .get_edid = dw_hdmi_bridge_get_edid,
+};
+
+/* -----------------------------------------------------------------------------
+ * IRQ Handling
+ */
+
+static irqreturn_t dw_hdmi_i2c_irq(struct dw_hdmi *hdmi)
+{
+ struct dw_hdmi_i2c *i2c = hdmi->i2c;
+ unsigned int stat;
+
+ stat = hdmi_readb(hdmi, HDMI_IH_I2CM_STAT0);
+ if (!stat)
+ return IRQ_NONE;
+
+ hdmi_writeb(hdmi, stat, HDMI_IH_I2CM_STAT0);
+
+ i2c->stat = stat;
+
+ complete(&i2c->cmp);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t dw_hdmi_hardirq(int irq, void *dev_id)
+{
+ struct dw_hdmi *hdmi = dev_id;
+ u8 intr_stat, hdcp_stat;
+ irqreturn_t ret = IRQ_NONE;
+
+ if (hdmi->i2c)
+ ret = dw_hdmi_i2c_irq(hdmi);
+ if (ret == IRQ_HANDLED)
+ return ret;
+
+ intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
+ hdcp_stat = hdmi_readb(hdmi, HDMI_A_APIINTSTAT);
+ if (intr_stat | hdcp_stat) {
+ if (intr_stat) {
+ dev_info(hdmi->dev, "intr status %#x\n", intr_stat);
+ hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_PHY_STAT0);
+ }
+ if (hdcp_stat) {
+ dev_info(hdmi->dev, "HDCP status %#x\n", hdcp_stat);
+ hdmi_writeb(hdmi, 0xff, HDMI_A_APIINTMSK);
+ }
+ return IRQ_WAKE_THREAD;
+ }
+
+ return ret;
+}
+
+void dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense)
+{
+ mutex_lock(&hdmi->mutex);
+
+ if (!hdmi->force) {
+ /*
+ * If the RX sense status indicates we're disconnected,
+ * clear the software rxsense status.
+ */
+ if (!rx_sense)
+ hdmi->rxsense = false;
+
+ /*
+ * Only set the software rxsense status when both
+ * rxsense and hpd indicates we're connected.
+ * This avoids what seems to be bad behaviour in
+ * at least iMX6S versions of the phy.
+ */
+ if (hpd)
+ hdmi->rxsense = true;
+
+ dw_hdmi_update_power(hdmi);
+ dw_hdmi_update_phy_mask(hdmi);
+ }
+ mutex_unlock(&hdmi->mutex);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_setup_rx_sense);
+
+static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
+{
+ struct dw_hdmi *hdmi = dev_id;
+ u8 intr_stat, phy_int_pol, phy_pol_mask, phy_stat, hdcp_stat;
+
+ intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
+ if (intr_stat) {
+ phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
+ phy_stat = hdmi_readb(hdmi, HDMI_PHY_STAT0);
+ dev_dbg(hdmi->dev, "phy_int_pol:0x%x, phy_stat:0x%x\n",
+ phy_int_pol, phy_stat);
+
+ phy_pol_mask = 0;
+ if (intr_stat & HDMI_IH_PHY_STAT0_HPD)
+ phy_pol_mask |= HDMI_PHY_HPD;
+ if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE0)
+ phy_pol_mask |= HDMI_PHY_RX_SENSE0;
+ if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE1)
+ phy_pol_mask |= HDMI_PHY_RX_SENSE1;
+ if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE2)
+ phy_pol_mask |= HDMI_PHY_RX_SENSE2;
+ if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE3)
+ phy_pol_mask |= HDMI_PHY_RX_SENSE3;
+
+ if (phy_pol_mask)
+ hdmi_modb(hdmi, ~phy_int_pol, phy_pol_mask,
+ HDMI_PHY_POL0);
+ if (phy_int_pol & HDMI_PHY_HPD)
+ hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_BUSCLEAR,
+ HDMI_I2CM_OPERATION);
+
+ /*
+ * RX sense tells us whether the TDMS transmitters are detecting
+ * load - in other words, there's something listening on the
+ * other end of the link. Use this to decide whether we should
+ * power on the phy as HPD may be toggled by the sink to merely
+ * ask the source to re-read the EDID.
+ */
+ if (intr_stat &
+ (HDMI_IH_PHY_STAT0_RX_SENSE | HDMI_IH_PHY_STAT0_HPD)) {
+ hpd_flag = true;
+ dw_hdmi_setup_rx_sense(hdmi, phy_stat & HDMI_PHY_HPD,
+ phy_stat & HDMI_PHY_RX_SENSE);
+
+ if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) ==
+ 0) {
+ mutex_lock(&hdmi->cec_notifier_mutex);
+ cec_notifier_phys_addr_invalidate(
+ hdmi->cec_notifier);
+ mutex_unlock(&hdmi->cec_notifier_mutex);
+ }
+ }
+
+ check_hdmi_irq(hdmi, intr_stat, phy_int_pol);
+ hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0);
+ hdmi_writeb(hdmi,
+ ~(HDMI_IH_PHY_STAT0_HPD |
+ HDMI_IH_PHY_STAT0_RX_SENSE),
+ HDMI_IH_MUTE_PHY_STAT0);
+ }
+
+ hpd_flag = false;
+
+ hdcp_stat = hdmi_readb(hdmi, HDMI_A_APIINTSTAT);
+ if (hdcp_stat) {
+ if (hdmi->hdcp && hdmi->hdcp->hdcp_isr)
+ hdmi->hdcp->hdcp_isr(hdmi->hdcp, hdcp_stat);
+ hdmi_writeb(hdmi, hdcp_stat, HDMI_A_APIINTCLR);
+ hdmi_writeb(hdmi, 0x00, HDMI_A_APIINTMSK);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static const struct dw_hdmi_phy_data dw_hdmi_phys[] = {
+ {
+ .type = DW_HDMI_PHY_DWC_HDMI_TX_PHY,
+ .name = "DWC HDMI TX PHY",
+ .gen = 1,
+ },
+ {
+ .type = DW_HDMI_PHY_DWC_MHL_PHY_HEAC,
+ .name = "DWC MHL PHY + HEAC PHY",
+ .gen = 2,
+ .has_svsret = true,
+ .configure = hdmi_phy_configure_dwc_hdmi_3d_tx,
+ },
+ {
+ .type = DW_HDMI_PHY_DWC_MHL_PHY,
+ .name = "DWC MHL PHY",
+ .gen = 2,
+ .has_svsret = true,
+ .configure = hdmi_phy_configure_dwc_hdmi_3d_tx,
+ },
+ {
+ .type = DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY_HEAC,
+ .name = "DWC HDMI 3D TX PHY + HEAC PHY",
+ .gen = 2,
+ .configure = hdmi_phy_configure_dwc_hdmi_3d_tx,
+ },
+ {
+ .type = DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY,
+ .name = "DWC HDMI 3D TX PHY",
+ .gen = 2,
+ .configure = hdmi_phy_configure_dwc_hdmi_3d_tx,
+ },
+ {
+ .type = DW_HDMI_PHY_DWC_HDMI20_TX_PHY,
+ .name = "DWC HDMI 2.0 TX PHY",
+ .gen = 2,
+ .has_svsret = true,
+ .configure = hdmi_phy_configure_dwc_hdmi_3d_tx,
+ },
+ {
+ .type = DW_HDMI_PHY_VENDOR_PHY,
+ .name = "Vendor PHY",
+ }
+};
+
+static int dw_hdmi_detect_phy(struct dw_hdmi *hdmi)
+{
+ unsigned int i;
+ u8 phy_type;
+ if (hdmi->eswin_plat) {
+ phy_type = hdmi->plat_data->phy_force_vendor ?
+ DW_HDMI_PHY_VENDOR_PHY :
+ hdmi_readb(hdmi, HDMI_CONFIG2_ID);
+
+ if (phy_type == DW_HDMI_PHY_VENDOR_PHY) {
+ /* Vendor PHYs require support from the glue layer. */
+ if (!hdmi->plat_data->phy_ops ||
+ !hdmi->plat_data->phy_name) {
+ dev_err(hdmi->dev,
+ "Vendor HDMI PHY not supported by glue layer\n");
+ return -ENODEV;
+ }
+
+ hdmi->phy.ops = hdmi->plat_data->phy_ops;
+ hdmi->phy.data = hdmi->plat_data->phy_data;
+ hdmi->phy.name = hdmi->plat_data->phy_name;
+ return 0;
+ }
+ } else {
+ phy_type = 0xf3;
+ }
+ /* Synopsys PHYs are handled internally. */
+ for (i = 0; i < ARRAY_SIZE(dw_hdmi_phys); ++i) {
+ if (dw_hdmi_phys[i].type == phy_type) {
+ hdmi->phy.ops = &dw_hdmi_synopsys_phy_ops;
+ hdmi->phy.name = dw_hdmi_phys[i].name;
+ hdmi->phy.data = (void *)&dw_hdmi_phys[i];
+
+ if (!dw_hdmi_phys[i].configure &&
+ !hdmi->plat_data->configure_phy) {
+ dev_err(hdmi->dev,
+ "%s requires platform support\n",
+ hdmi->phy.name);
+ return -ENODEV;
+ }
+
+ return 0;
+ }
+ }
+
+ dev_err(hdmi->dev, "Unsupported HDMI PHY type (%02x)\n", phy_type);
+ return -ENODEV;
+}
+
+static void dw_hdmi_cec_enable(struct dw_hdmi *hdmi)
+{
+ mutex_lock(&hdmi->mutex);
+ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CECCLK_DISABLE;
+ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+ mutex_unlock(&hdmi->mutex);
+}
+
+static void dw_hdmi_cec_disable(struct dw_hdmi *hdmi)
+{
+ mutex_lock(&hdmi->mutex);
+ hdmi->mc_clkdis |= HDMI_MC_CLKDIS_CECCLK_DISABLE;
+ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+ mutex_unlock(&hdmi->mutex);
+}
+
+static const struct dw_hdmi_cec_ops dw_hdmi_cec_ops = {
+ .write = hdmi_writeb,
+ .read = hdmi_readb,
+ .enable = dw_hdmi_cec_enable,
+ .disable = dw_hdmi_cec_disable,
+};
+
+static int dw_hdmi_status_show(struct seq_file *s, void *v)
+{
+ struct dw_hdmi *hdmi = s->private;
+ u32 val;
+
+ seq_puts(s, "PHY: ");
+ if (!hdmi->phy.enabled) {
+ seq_puts(s, "disabled\n");
+ return 0;
+ }
+ seq_puts(s, "enabled\t\t\tMode: ");
+ if (hdmi->sink_is_hdmi)
+ seq_puts(s, "HDMI\n");
+ else
+ seq_puts(s, "DVI\n");
+ if (hdmi->hdmi_data.video_mode.mtmdsclock > 340000000)
+ val = hdmi->hdmi_data.video_mode.mtmdsclock / 4;
+ else
+ val = hdmi->hdmi_data.video_mode.mtmdsclock;
+ seq_printf(s, "Pixel Clk: %uHz\t\tTMDS Clk: %uHz\n",
+ hdmi->hdmi_data.video_mode.mpixelclock, val);
+ seq_puts(s, "Color Format: ");
+ if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format))
+ seq_puts(s, "RGB");
+ else if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format))
+ seq_puts(s, "YUV444");
+ else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
+ seq_puts(s, "YUV422");
+ else if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
+ seq_puts(s, "YUV420");
+ else
+ seq_puts(s, "UNKNOWN");
+ val = hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format);
+ seq_printf(s, "\t\tColor Depth: %d bit\n", val);
+ seq_puts(s, "Colorimetry: ");
+ switch (hdmi->hdmi_data.enc_out_encoding) {
+ case V4L2_YCBCR_ENC_601:
+ seq_puts(s, "ITU.BT601");
+ break;
+ case V4L2_YCBCR_ENC_709:
+ seq_puts(s, "ITU.BT709");
+ break;
+ case V4L2_YCBCR_ENC_BT2020:
+ seq_puts(s, "ITU.BT2020");
+ break;
+ default: /* Carries no data */
+ seq_puts(s, "ITU.BT601");
+ break;
+ }
+
+ seq_puts(s, "\t\tEOTF: ");
+
+ if (hdmi->version < 0x211a) {
+ seq_puts(s, "Unsupported\n");
+ return 0;
+ }
+
+ val = hdmi_readb(hdmi, HDMI_FC_PACKET_TX_EN);
+ if (!(val & HDMI_FC_PACKET_TX_EN_DRM_MASK)) {
+ seq_puts(s, "Off\n");
+ return 0;
+ }
+
+ switch (hdmi_readb(hdmi, HDMI_FC_DRM_PB0)) {
+ case TRADITIONAL_GAMMA_SDR:
+ seq_puts(s, "SDR");
+ break;
+ case TRADITIONAL_GAMMA_HDR:
+ seq_puts(s, "HDR");
+ break;
+ case SMPTE_ST2084:
+ seq_puts(s, "ST2084");
+ break;
+ case HLG:
+ seq_puts(s, "HLG");
+ break;
+ default:
+ seq_puts(s, "Not Defined\n");
+ return 0;
+ }
+
+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB3) << 8;
+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB2);
+ seq_printf(s, "\nx0: %d", val);
+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB5) << 8;
+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB4);
+ seq_printf(s, "\t\t\t\ty0: %d\n", val);
+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB7) << 8;
+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB6);
+ seq_printf(s, "x1: %d", val);
+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB9) << 8;
+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB8);
+ seq_printf(s, "\t\t\t\ty1: %d\n", val);
+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB11) << 8;
+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB10);
+ seq_printf(s, "x2: %d", val);
+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB13) << 8;
+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB12);
+ seq_printf(s, "\t\t\t\ty2: %d\n", val);
+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB15) << 8;
+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB14);
+ seq_printf(s, "white x: %d", val);
+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB17) << 8;
+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB16);
+ seq_printf(s, "\t\t\twhite y: %d\n", val);
+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB19) << 8;
+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB18);
+ seq_printf(s, "max lum: %d", val);
+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB21) << 8;
+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB20);
+ seq_printf(s, "\t\t\tmin lum: %d\n", val);
+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB23) << 8;
+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB22);
+ seq_printf(s, "max cll: %d", val);
+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB25) << 8;
+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB24);
+ seq_printf(s, "\t\t\tmax fall: %d\n", val);
+ return 0;
+}
+
+static int dw_hdmi_status_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dw_hdmi_status_show, inode->i_private);
+}
+
+static const struct file_operations dw_hdmi_status_fops = {
+ .owner = THIS_MODULE,
+ .open = dw_hdmi_status_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+struct dw_hdmi_reg_table {
+ int reg_base;
+ int reg_end;
+};
+
+static const struct dw_hdmi_reg_table hdmi_reg_table[] = {
+ { HDMI_DESIGN_ID, HDMI_CONFIG3_ID },
+ { HDMI_IH_FC_STAT0, HDMI_IH_MUTE },
+ { HDMI_TX_INVID0, HDMI_TX_BCBDATA1 },
+ { HDMI_VP_STATUS, HDMI_VP_POL },
+ { HDMI_FC_INVIDCONF, HDMI_FC_DBGTMDS2 },
+ { HDMI_PHY_CONF0, HDMI_PHY_POL0 },
+ { HDMI_PHY_I2CM_SLAVE_ADDR, HDMI_PHY_I2CM_FS_SCL_LCNT_0_ADDR },
+ { HDMI_AUD_CONF0, 0x3624 },
+ { HDMI_MC_SFRDIV, HDMI_MC_HEACPHY_RST },
+ { HDMI_CSC_CFG, HDMI_CSC_COEF_C4_LSB },
+ { HDMI_A_HDCPCFG0, 0x52bb },
+ { 0x7800, 0x7818 },
+ { 0x7900, 0x790e },
+ { HDMI_CEC_CTRL, HDMI_CEC_WKUPCTRL },
+ { HDMI_I2CM_SLAVE, HDMI_I2CM_MAX_REG },
+};
+
+static int dw_hdmi_ctrl_show(struct seq_file *s, void *v)
+{
+ struct dw_hdmi *hdmi = s->private;
+ u32 i = 0, j = 0, val = 0;
+
+ seq_puts(s, "\n>>>hdmi_ctl reg ");
+ for (i = 0; i < 16; i++)
+ seq_printf(s, " %2x", i);
+ seq_puts(s, "\n---------------------------------------------------");
+
+ for (i = 0; i < ARRAY_SIZE(hdmi_reg_table); i++) {
+ for (j = hdmi_reg_table[i].reg_base;
+ j <= hdmi_reg_table[i].reg_end; j++) {
+ val = hdmi_readb(hdmi, j);
+ if ((j - hdmi_reg_table[i].reg_base) % 16 == 0)
+ seq_printf(s, "\n>>>hdmi_ctl %04x:", j);
+ seq_printf(s, " %02x", val);
+ }
+ }
+ seq_puts(s, "\n---------------------------------------------------\n");
+
+ return 0;
+}
+
+static int dw_hdmi_ctrl_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dw_hdmi_ctrl_show, inode->i_private);
+}
+
+static ssize_t dw_hdmi_ctrl_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct dw_hdmi *hdmi = ((struct seq_file *)file->private_data)->private;
+ u32 reg, val;
+ char kbuf[25];
+
+ if (copy_from_user(kbuf, buf, count))
+ return -EFAULT;
+ if (sscanf(kbuf, "%x%x", &reg, &val) == -1)
+ return -EFAULT;
+ if ((reg < 0) || (reg > HDMI_I2CM_SCDC_READ_UPDATE_ON)) {
+ dev_err(hdmi->dev, "it is no a hdmi register\n");
+ return count;
+ }
+ dev_info(hdmi->dev, "/**********hdmi register config******/");
+ dev_info(hdmi->dev, "\n reg=%x val=%x\n", reg, val);
+ hdmi_writeb(hdmi, val, reg);
+ return count;
+}
+
+static const struct file_operations dw_hdmi_ctrl_fops = {
+ .owner = THIS_MODULE,
+ .open = dw_hdmi_ctrl_open,
+ .read = seq_read,
+ .write = dw_hdmi_ctrl_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int dw_hdmi_phy_show(struct seq_file *s, void *v)
+{
+ struct dw_hdmi *hdmi = s->private;
+ u32 i, val;
+
+ seq_puts(s, "\n>>>hdmi_phy reg\n");
+ for (i = 0; i < 0x28; i++) {
+ val = hdmi_phy_i2c_read(hdmi, i);
+ seq_printf(s, "regs %02x val %04x\n", i, val);
+ }
+ return 0;
+}
+
+static int dw_hdmi_phy_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dw_hdmi_phy_show, inode->i_private);
+}
+
+static ssize_t dw_hdmi_phy_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct dw_hdmi *hdmi = ((struct seq_file *)file->private_data)->private;
+ u32 reg, val;
+ char kbuf[25];
+
+ if (copy_from_user(kbuf, buf, count))
+ return -EFAULT;
+ if (sscanf(kbuf, "%x%x", &reg, &val) == -1)
+ return -EFAULT;
+ if ((reg < 0) || (reg > 0x100)) {
+ dev_err(hdmi->dev, "it is not a hdmi phy register\n");
+ return count;
+ }
+ dev_info(hdmi->dev, "/*******hdmi phy register config******/");
+ dev_info(hdmi->dev, "\n reg=%x val=%x\n", reg, val);
+ dw_hdmi_phy_i2c_write(hdmi, val, reg);
+ return count;
+}
+
+static const struct file_operations dw_hdmi_phy_fops = {
+ .owner = THIS_MODULE,
+ .open = dw_hdmi_phy_open,
+ .read = seq_read,
+ .write = dw_hdmi_phy_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void dw_hdmi_register_debugfs(struct device *dev, struct dw_hdmi *hdmi)
+{
+ hdmi->debugfs_dir = debugfs_create_dir("dw-hdmi", NULL);
+ if (IS_ERR(hdmi->debugfs_dir)) {
+ dev_err(dev, "failed to create debugfs dir!\n");
+ return;
+ }
+
+ debugfs_create_file("status", 0400, hdmi->debugfs_dir, hdmi,
+ &dw_hdmi_status_fops);
+
+ debugfs_create_file("ctrl", 0400, hdmi->debugfs_dir, hdmi,
+ &dw_hdmi_ctrl_fops);
+
+ debugfs_create_file("phy", 0400, hdmi->debugfs_dir, hdmi,
+ &dw_hdmi_phy_fops);
+}
+
+static void dw_hdmi_register_hdcp(struct device *dev, struct dw_hdmi *hdmi,
+ u32 val, bool hdcp1x_enable)
+{
+ struct dw_hdcp hdmi_hdcp = {
+ .hdmi = hdmi,
+ .write = hdmi_writeb,
+ .read = hdmi_readb,
+ .regs = hdmi->regs,
+ .reg_io_width = val,
+ .enable = hdcp1x_enable,
+ };
+ struct platform_device_info hdcp_device_info = {
+ .parent = dev,
+ .id = PLATFORM_DEVID_AUTO,
+ .res = NULL,
+ .num_res = 0,
+ .name = DW_HDCP_DRIVER_NAME,
+ .data = &hdmi_hdcp,
+ .size_data = sizeof(hdmi_hdcp),
+ .dma_mask = DMA_BIT_MASK(32),
+ };
+
+ hdmi->hdcp_dev = platform_device_register_full(&hdcp_device_info);
+ if (IS_ERR(hdmi->hdcp_dev))
+ dev_err(dev, "failed to register hdcp!\n");
+ else
+ hdmi->hdcp = hdmi->hdcp_dev->dev.platform_data;
+}
+
+static const struct regmap_config hdmi_regmap_8bit_config = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .reg_stride = 1,
+ .max_register = HDMI_I2CM_MAX_REG,
+};
+
+static const struct regmap_config hdmi_regmap_32bit_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = HDMI_I2CM_MAX_REG << 2,
+};
+
+static void dw_hdmi_reg_initial(struct dw_hdmi *hdmi)
+{
+ if (hdmi_readb(hdmi, HDMI_IH_MUTE)) {
+ initialize_hdmi_ih_mutes(hdmi);
+ hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL,
+ HDMI_PHY_I2CM_INT_ADDR);
+
+ hdmi_writeb(hdmi,
+ HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL |
+ HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL,
+ HDMI_PHY_I2CM_CTLINT_ADDR);
+
+ hdmi_writeb(hdmi, HDMI_PHY_HPD | HDMI_PHY_RX_SENSE,
+ HDMI_PHY_POL0);
+ hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0);
+ hdmi_writeb(hdmi,
+ ~(HDMI_IH_PHY_STAT0_HPD |
+ HDMI_IH_PHY_STAT0_RX_SENSE),
+ HDMI_IH_MUTE_PHY_STAT0);
+ }
+}
+
+static void dw_hdmi_init_hw(struct dw_hdmi *hdmi)
+{
+ if (!hdmi) {
+ printk("Hdmi has not been initialized\n");
+ return;
+ }
+
+ mutex_lock(&hdmi->mutex);
+ dw_hdmi_reg_initial(hdmi);
+ /*
+ * Reset HDMI DDC I2C master controller and mute I2CM interrupts.
+ * Even if we are using a separate i2c adapter doing this doesn't
+ * hurt.
+ */
+ if (hdmi->i2c)
+ dw_hdmi_i2c_init(hdmi);
+ if (hdmi->irq)
+ enable_irq(hdmi->irq);
+ if (hdmi->phy.ops->setup_hpd)
+ hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data);
+
+ /*
+ * HDMI status maybe incorrect in the following condition:
+ * HDMI plug in -> system sleep -> HDMI plug out -> system wake up.
+ * At this time, cat /sys/class/drm/card 0-HDMI-A-1/status is connected.
+ * There is no hpd interrupt, because HDMI is powerdown during suspend.
+ * So we need check the current HDMI status in this case.
+ */
+ if (hdmi->connector.status == connector_status_connected)
+ if (hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data) ==
+ connector_status_disconnected) {
+ hdmi->hpd_state = false;
+ mod_delayed_work(hdmi->workqueue, &hdmi->work,
+ msecs_to_jiffies(20));
+ }
+ mutex_unlock(&hdmi->mutex);
+}
+
+/* -----------------------------------------------------------------------------
+ * Probe/remove API, used from platforms based on the DRM bridge API.
+ */
+
+static int dw_hdmi_parse_dt(struct dw_hdmi *hdmi)
+{
+ struct device_node *endpoint;
+ struct device_node *remote;
+
+ if (!hdmi->plat_data->output_port)
+ return 0;
+
+ endpoint = of_graph_get_endpoint_by_regs(
+ hdmi->dev->of_node, hdmi->plat_data->output_port, -1);
+ if (!endpoint) {
+ /*
+ * On platforms whose bindings don't make the output port
+ * mandatory (such as Rockchip) the plat_data->output_port
+ * field isn't set, so it's safe to make this a fatal error.
+ */
+ dev_err(hdmi->dev, "Missing endpoint in port@%u\n",
+ hdmi->plat_data->output_port);
+ return -ENODEV;
+ }
+
+ remote = of_graph_get_remote_port_parent(endpoint);
+ of_node_put(endpoint);
+ if (!remote) {
+ dev_err(hdmi->dev, "Endpoint in port@%u unconnected\n",
+ hdmi->plat_data->output_port);
+ return -ENODEV;
+ }
+
+ if (!of_device_is_available(remote)) {
+ dev_err(hdmi->dev, "port@%u remote device is disabled\n",
+ hdmi->plat_data->output_port);
+ of_node_put(remote);
+ return -ENODEV;
+ }
+
+ hdmi->next_bridge = of_drm_find_bridge(remote);
+ of_node_put(remote);
+ if (!hdmi->next_bridge)
+ return -EPROBE_DEFER;
+
+ return 0;
+}
+
+void dw_hdmi_enable_video(struct dw_hdmi *hdmi)
+{
+ dev_info(hdmi->dev, "%s", __func__);
+ hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP);
+}
+void dw_hdmi_disable_video(struct dw_hdmi *hdmi)
+{
+ dev_info(hdmi->dev, "%s", __func__);
+ hdmi_writeb(hdmi, HDMI_FC_GCP_SET_AVMUTE, HDMI_FC_GCP);
+}
+
+struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
+ const struct dw_hdmi_plat_data *plat_data)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct platform_device_info pdevinfo;
+ struct device_node *ddc_node;
+ struct dw_hdmi_cec_data cec;
+ struct dw_hdmi *hdmi;
+ struct resource *iores = NULL;
+ int irq;
+ int ret;
+ u32 val = 1;
+ u8 prod_id0;
+ u8 prod_id1;
+ u8 config0;
+ u8 config3;
+ bool hdcp1x_enable = false;
+
+ hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
+ if (!hdmi)
+ return ERR_PTR(-ENOMEM);
+
+ hdmi->plat_data = plat_data;
+ hdmi->dev = dev;
+ hdmi->sample_rate = 48000;
+ hdmi->disabled = true;
+ hdmi->rxsense = true;
+ hdmi->phy_mask = (u8) ~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE);
+ hdmi->mc_clkdis = 0x7f;
+ hdmi->last_connector_result = connector_status_disconnected;
+
+ mutex_init(&hdmi->mutex);
+ mutex_init(&hdmi->audio_mutex);
+ mutex_init(&hdmi->cec_notifier_mutex);
+ spin_lock_init(&hdmi->audio_lock);
+
+ ret = dw_hdmi_parse_dt(hdmi);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0);
+ if (ddc_node) {
+ hdmi->ddc = of_get_i2c_adapter_by_node(ddc_node);
+ of_node_put(ddc_node);
+ if (!hdmi->ddc) {
+ dev_dbg(hdmi->dev, "failed to read ddc node\n");
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ } else {
+ dev_dbg(hdmi->dev, "no ddc property found\n");
+ }
+
+ ret = device_property_read_u32(&pdev->dev, "eswin-plat",
+ &hdmi->eswin_plat);
+ if (0 != ret) {
+ dev_warn(&pdev->dev, "Failed to get eswin platform\n");
+ hdmi->eswin_plat = 0;
+ }
+ dev_info(hdmi->dev, "eswin platform:%d\n", hdmi->eswin_plat);
+
+ if (!plat_data->regm) {
+ const struct regmap_config *reg_config;
+
+ of_property_read_u32(np, "reg-io-width", &val);
+ switch (val) {
+ case 4:
+ reg_config = &hdmi_regmap_32bit_config;
+ hdmi->reg_shift = 2;
+ break;
+ case 1:
+ reg_config = &hdmi_regmap_8bit_config;
+ break;
+ default:
+ dev_err(dev, "reg-io-width must be 1 or 4\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hdmi->regs = devm_ioremap_resource(dev, iores);
+ if (IS_ERR(hdmi->regs)) {
+ ret = PTR_ERR(hdmi->regs);
+ goto err_res;
+ }
+
+ hdmi->regm = devm_regmap_init_mmio(dev, hdmi->regs, reg_config);
+ if (IS_ERR(hdmi->regm)) {
+ dev_err(dev, "Failed to configure regmap\n");
+ ret = PTR_ERR(hdmi->regm);
+ goto err_res;
+ }
+ } else {
+ hdmi->regm = plat_data->regm;
+ }
+
+ hdmi->isfr_clk = devm_clk_get(hdmi->dev, "isfr");
+ if (IS_ERR(hdmi->isfr_clk)) {
+ ret = PTR_ERR(hdmi->isfr_clk);
+ dev_err(hdmi->dev, "Unable to get HDMI isfr clk: %d\n", ret);
+ goto err_res;
+ }
+
+ ret = clk_prepare_enable(hdmi->isfr_clk);
+ if (ret) {
+ dev_err(hdmi->dev, "Cannot enable HDMI isfr clock: %d\n", ret);
+ goto err_res;
+ }
+
+ hdmi->iahb_clk = devm_clk_get(hdmi->dev, "iahb");
+ if (IS_ERR(hdmi->iahb_clk)) {
+ ret = PTR_ERR(hdmi->iahb_clk);
+ dev_err(hdmi->dev, "Unable to get HDMI iahb clk: %d\n", ret);
+ goto err_isfr;
+ }
+
+ ret = clk_prepare_enable(hdmi->iahb_clk);
+ if (ret) {
+ dev_err(hdmi->dev, "Cannot enable HDMI iahb clock: %d\n", ret);
+ goto err_isfr;
+ }
+
+ hdmi->cec_clk = devm_clk_get(hdmi->dev, "cec");
+ if (PTR_ERR(hdmi->cec_clk) == -ENOENT) {
+ hdmi->cec_clk = NULL;
+ } else if (IS_ERR(hdmi->cec_clk)) {
+ ret = PTR_ERR(hdmi->cec_clk);
+ if (ret != -EPROBE_DEFER)
+ dev_err(hdmi->dev, "Cannot get HDMI cec clock: %d\n",
+ ret);
+
+ hdmi->cec_clk = NULL;
+ goto err_iahb;
+ } else {
+ ret = clk_prepare_enable(hdmi->cec_clk);
+ if (ret) {
+ dev_err(hdmi->dev, "Cannot enable HDMI cec clock: %d\n",
+ ret);
+ goto err_iahb;
+ }
+ }
+
+ /* hdmi prstn reset */
+ hdmi->rst_hdmi_prstn =
+ devm_reset_control_get_optional(hdmi->dev, "prstn");
+ if (IS_ERR_OR_NULL(hdmi->rst_hdmi_prstn)) {
+ dev_err(hdmi->dev, "Failed to get hdmi prstn reset handle\n");
+ ret = -EFAULT;
+ goto err_iahb;
+ }
+
+ /* hdmi phyctl reset */
+ hdmi->rst_hdmi_phyrstn =
+ devm_reset_control_get_optional(hdmi->dev, "phyrstn");
+ if (IS_ERR_OR_NULL(hdmi->rst_hdmi_phyrstn)) {
+ dev_err(hdmi->dev, "Failed to get hdmi phyrstn reset handle\n");
+ ret = -EFAULT;
+ goto err_iahb;
+ }
+
+ /* hdmi rstn reset */
+ hdmi->rst_hdmi_rstn =
+ devm_reset_control_get_optional(hdmi->dev, "rstn");
+ if (IS_ERR_OR_NULL(hdmi->rst_hdmi_rstn)) {
+ dev_err(hdmi->dev, "Failed to get hdmi rstn reset handle\n");
+ ret = -EFAULT;
+ goto err_iahb;
+ }
+
+ if (hdmi->rst_hdmi_prstn) {
+ ret = reset_control_reset(hdmi->rst_hdmi_prstn);
+ WARN_ON(0 != ret);
+ }
+
+ if (hdmi->rst_hdmi_phyrstn) {
+ ret = reset_control_reset(hdmi->rst_hdmi_phyrstn);
+ WARN_ON(0 != ret);
+ }
+
+ if (hdmi->rst_hdmi_rstn) {
+ ret = reset_control_reset(hdmi->rst_hdmi_rstn);
+ WARN_ON(0 != ret);
+ }
+
+ /* Product and revision IDs */
+ hdmi->version = (hdmi_readb(hdmi, HDMI_DESIGN_ID) << 8) |
+ (hdmi_readb(hdmi, HDMI_REVISION_ID) << 0);
+ prod_id0 = hdmi_readb(hdmi, HDMI_PRODUCT_ID0);
+ prod_id1 = hdmi_readb(hdmi, HDMI_PRODUCT_ID1);
+
+ if (prod_id0 != HDMI_PRODUCT_ID0_HDMI_TX ||
+ (prod_id1 & ~HDMI_PRODUCT_ID1_HDCP) != HDMI_PRODUCT_ID1_HDMI_TX) {
+ dev_err(dev, "Unsupported HDMI controller (%04x:%02x:%02x)\n",
+ hdmi->version, prod_id0, prod_id1);
+ ret = -ENODEV;
+ goto err_iahb;
+ }
+
+ ret = dw_hdmi_detect_phy(hdmi);
+ if (ret < 0)
+ goto err_iahb;
+
+ dev_info(dev, "Detected HDMI TX controller v%x.%03x %s HDCP (%s)\n",
+ hdmi->version >> 12, hdmi->version & 0xfff,
+ prod_id1 & HDMI_PRODUCT_ID1_HDCP ? "with" : "without",
+ hdmi->phy.name);
+
+ init_hpd_work(hdmi);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ ret = irq;
+ goto err_iahb;
+ }
+
+ ret = devm_request_threaded_irq(dev, irq, dw_hdmi_hardirq, dw_hdmi_irq,
+ IRQF_SHARED, dev_name(dev), hdmi);
+ if (ret)
+ goto err_iahb;
+
+ /*
+ * To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator
+ * N and cts values before enabling phy
+ */
+ hdmi_init_clk_regenerator(hdmi);
+
+ /* If DDC bus is not specified, try to register HDMI I2C bus */
+ if (!hdmi->ddc) {
+ /* Look for (optional) stuff related to unwedging */
+ hdmi->pinctrl = devm_pinctrl_get(dev);
+ if (!IS_ERR(hdmi->pinctrl)) {
+ hdmi->unwedge_state =
+ pinctrl_lookup_state(hdmi->pinctrl, "unwedge");
+ hdmi->default_state =
+ pinctrl_lookup_state(hdmi->pinctrl, "default");
+
+ if (IS_ERR(hdmi->default_state) ||
+ IS_ERR(hdmi->unwedge_state)) {
+ if (!IS_ERR(hdmi->unwedge_state))
+ dev_warn(
+ dev,
+ "Unwedge requires default pinctrl\n");
+ hdmi->default_state = NULL;
+ hdmi->unwedge_state = NULL;
+ }
+ }
+
+ hdmi->ddc = dw_hdmi_i2c_adapter(hdmi);
+ if (IS_ERR(hdmi->ddc))
+ hdmi->ddc = NULL;
+
+ /*
+ * Read high and low time from device tree. If not available use
+ * the default timing scl clock rate is about 99.6KHz.
+ */
+ if (of_property_read_u32(np, "ddc-i2c-scl-high-time-ns",
+ &hdmi->i2c->scl_high_ns))
+ hdmi->i2c->scl_high_ns = 4708;
+ if (of_property_read_u32(np, "ddc-i2c-scl-low-time-ns",
+ &hdmi->i2c->scl_low_ns))
+ hdmi->i2c->scl_low_ns = 4916;
+ }
+
+ dw_hdmi_init_hw(hdmi);
+ hdmi->bridge.driver_private = hdmi;
+ hdmi->bridge.funcs = &dw_hdmi_bridge_funcs;
+ hdmi->bridge.ops =
+ DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD;
+ hdmi->bridge.interlace_allowed = true;
+#ifdef CONFIG_OF
+ hdmi->bridge.of_node = pdev->dev.of_node;
+#endif
+
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ pdevinfo.parent = dev;
+ pdevinfo.id = PLATFORM_DEVID_AUTO;
+
+ config0 = hdmi_readb(hdmi, HDMI_CONFIG0_ID);
+ config3 = hdmi_readb(hdmi, HDMI_CONFIG3_ID);
+
+ if (iores && config3 & HDMI_CONFIG3_AHBAUDDMA) {
+ struct dw_hdmi_audio_data audio;
+
+ audio.phys = iores->start;
+ audio.base = hdmi->regs;
+ audio.irq = irq;
+ audio.hdmi = hdmi;
+ audio.get_eld = hdmi_audio_get_eld;
+ hdmi->enable_audio = dw_hdmi_ahb_audio_enable;
+ hdmi->disable_audio = dw_hdmi_ahb_audio_disable;
+
+ pdevinfo.name = "dw-hdmi-ahb-audio";
+ pdevinfo.data = &audio;
+ pdevinfo.size_data = sizeof(audio);
+ pdevinfo.dma_mask = DMA_BIT_MASK(32);
+ hdmi->audio = platform_device_register_full(&pdevinfo);
+ } else if (config0 & HDMI_CONFIG0_I2S) {
+ struct dw_hdmi_i2s_audio_data audio;
+
+ audio.hdmi = hdmi;
+ audio.get_eld = hdmi_audio_get_eld;
+ audio.write = hdmi_writeb;
+ audio.read = hdmi_readb;
+ hdmi->enable_audio = dw_hdmi_i2s_audio_enable;
+ hdmi->disable_audio = dw_hdmi_i2s_audio_disable;
+
+ pdevinfo.name = "dw-hdmi-i2s-audio";
+ pdevinfo.data = &audio;
+ pdevinfo.size_data = sizeof(audio);
+ pdevinfo.dma_mask = DMA_BIT_MASK(32);
+ hdmi->audio = platform_device_register_full(&pdevinfo);
+ }
+
+ if (!plat_data->disable_cec && (config0 & HDMI_CONFIG0_CEC)) {
+ cec.hdmi = hdmi;
+ cec.ops = &dw_hdmi_cec_ops;
+ cec.irq = irq;
+
+ pdevinfo.name = "dw-hdmi-cec";
+ pdevinfo.data = &cec;
+ pdevinfo.size_data = sizeof(cec);
+ pdevinfo.dma_mask = 0;
+
+ hdmi->cec = platform_device_register_full(&pdevinfo);
+ }
+
+ drm_bridge_add(&hdmi->bridge);
+
+ dw_hdmi_register_debugfs(dev, hdmi);
+
+ if (of_property_read_bool(np, "hdcp1x-enable"))
+ hdcp1x_enable = true;
+ dw_hdmi_register_hdcp(dev, hdmi, val, hdcp1x_enable);
+
+ return hdmi;
+
+err_iahb:
+ if (hdmi->i2c)
+ i2c_del_adapter(&hdmi->i2c->adap);
+ clk_disable_unprepare(hdmi->iahb_clk);
+ if (hdmi->cec_clk)
+ clk_disable_unprepare(hdmi->cec_clk);
+err_isfr:
+ clk_disable_unprepare(hdmi->isfr_clk);
+err_res:
+ i2c_put_adapter(hdmi->ddc);
+
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_probe);
+
+void dw_hdmi_remove(struct dw_hdmi *hdmi)
+{
+ int ret;
+
+ drm_bridge_remove(&hdmi->bridge);
+
+ if (hdmi->irq)
+ disable_irq(hdmi->irq);
+
+ cancel_delayed_work(&hdmi->work);
+ flush_workqueue(hdmi->workqueue);
+ destroy_workqueue(hdmi->workqueue);
+
+ if (hdmi->audio && !IS_ERR(hdmi->audio))
+ platform_device_unregister(hdmi->audio);
+ if (!IS_ERR(hdmi->cec))
+ platform_device_unregister(hdmi->cec);
+ if (hdmi->hdcp_dev && !IS_ERR(hdmi->hdcp_dev))
+ platform_device_unregister(hdmi->hdcp_dev);
+
+ /* Disable all interrupts */
+ hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
+
+ dw_hdmi_destroy_properties(hdmi);
+ hdmi->connector.funcs->destroy(&hdmi->connector);
+
+ clk_disable_unprepare(hdmi->iahb_clk);
+ clk_disable_unprepare(hdmi->isfr_clk);
+ clk_disable_unprepare(hdmi->cec_clk);
+
+ if (hdmi->i2c)
+ i2c_del_adapter(&hdmi->i2c->adap);
+ else
+ i2c_put_adapter(hdmi->ddc);
+
+ debugfs_remove_recursive(hdmi->debugfs_dir);
+
+ if (hdmi->rst_hdmi_prstn) {
+ ret = reset_control_assert(hdmi->rst_hdmi_prstn);
+ WARN_ON(0 != ret);
+ }
+
+ if (hdmi->rst_hdmi_phyrstn) {
+ ret = reset_control_assert(hdmi->rst_hdmi_phyrstn);
+ WARN_ON(0 != ret);
+ }
+
+ if (hdmi->rst_hdmi_rstn) {
+ ret = reset_control_assert(hdmi->rst_hdmi_rstn);
+ WARN_ON(0 != ret);
+ }
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_remove);
+
+/* -----------------------------------------------------------------------------
+ * Bind/unbind API, used from platforms based on the component framework.
+ */
+struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev,
+ struct drm_encoder *encoder,
+ const struct dw_hdmi_plat_data *plat_data)
+{
+ struct dw_hdmi *hdmi;
+ int ret;
+
+ hdmi = dw_hdmi_probe(pdev, plat_data);
+ if (IS_ERR(hdmi))
+ return hdmi;
+
+ ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL, 0);
+ if (ret) {
+ dw_hdmi_remove(hdmi);
+ DRM_ERROR("Failed to initialize bridge with drm\n");
+ return ERR_PTR(ret);
+ }
+
+ return hdmi;
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_bind);
+
+void dw_hdmi_unbind(struct dw_hdmi *hdmi)
+{
+ dw_hdmi_remove(hdmi);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_unbind);
+
+void dw_hdmi_suspend(struct dw_hdmi *hdmi)
+{
+ if (!hdmi) {
+ dev_warn(hdmi->dev, "HDMI has not been initialized\n");
+ return;
+ }
+
+ mutex_lock(&hdmi->mutex);
+
+ /*
+ * When system shutdown, hdmi should be disabled.
+ * When system suspend, dw_hdmi_bridge_disable will disable hdmi first.
+ * To prevent duplicate operation, we should determine whether hdmi
+ * has been disabled.
+ */
+ if (!hdmi->disabled) {
+ hdmi->disabled = true;
+ dw_hdmi_update_power(hdmi);
+ dw_hdmi_update_phy_mask(hdmi);
+ }
+ mutex_unlock(&hdmi->mutex);
+
+ if (hdmi->irq)
+ disable_irq(hdmi->irq);
+ cancel_delayed_work(&hdmi->work);
+ flush_workqueue(hdmi->workqueue);
+ pinctrl_pm_select_sleep_state(hdmi->dev);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_suspend);
+
+void dw_hdmi_resume(struct dw_hdmi *hdmi)
+{
+ dw_hdmi_init_hw(hdmi);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_resume);
+
+MODULE_DESCRIPTION("DW HDMI transmitter driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:dw-hdmi");
diff --git a/drivers/gpu/drm/eswin/dw_hdmi.h b/drivers/gpu/drm/eswin/dw_hdmi.h
new file mode 100644
index 000000000000..6281fd8bfc53
--- /dev/null
+++ b/drivers/gpu/drm/eswin/dw_hdmi.h
@@ -0,0 +1,1408 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2011 Freescale Semiconductor, Inc.
+ */
+
+#ifndef __DW_HDMI_H__
+#define __DW_HDMI_H__
+
+#include <sound/hdmi-codec.h>
+#include <drm/drm_property.h>
+
+extern struct platform_driver dw_hdmi_eswin_pltfm_driver;
+extern struct platform_driver snd_dw_hdmi_driver;
+extern struct platform_driver dw_hdmi_cec_driver;
+extern struct platform_driver dw_hdmi_hdcp_driver;
+extern struct platform_driver dw_hdmi_hdcp2_driver;
+
+/* Identification Registers */
+#define HDMI_DESIGN_ID 0x0000
+#define HDMI_REVISION_ID 0x0001
+#define HDMI_PRODUCT_ID0 0x0002
+#define HDMI_PRODUCT_ID1 0x0003
+#define HDMI_CONFIG0_ID 0x0004
+#define HDMI_CONFIG1_ID 0x0005
+#define HDMI_CONFIG2_ID 0x0006
+#define HDMI_CONFIG3_ID 0x0007
+
+/* Interrupt Registers */
+#define HDMI_IH_FC_STAT0 0x0100
+#define HDMI_IH_FC_STAT1 0x0101
+#define HDMI_IH_FC_STAT2 0x0102
+#define HDMI_IH_AS_STAT0 0x0103
+#define HDMI_IH_PHY_STAT0 0x0104
+#define HDMI_IH_I2CM_STAT0 0x0105
+#define HDMI_IH_CEC_STAT0 0x0106
+#define HDMI_IH_VP_STAT0 0x0107
+#define HDMI_IH_I2CMPHY_STAT0 0x0108
+#define HDMI_IH_AHBDMAAUD_STAT0 0x0109
+
+#define HDMI_IH_MUTE_FC_STAT0 0x0180
+#define HDMI_IH_MUTE_FC_STAT1 0x0181
+#define HDMI_IH_MUTE_FC_STAT2 0x0182
+#define HDMI_IH_MUTE_AS_STAT0 0x0183
+#define HDMI_IH_MUTE_PHY_STAT0 0x0184
+#define HDMI_IH_MUTE_I2CM_STAT0 0x0185
+#define HDMI_IH_MUTE_CEC_STAT0 0x0186
+#define HDMI_IH_MUTE_VP_STAT0 0x0187
+#define HDMI_IH_MUTE_I2CMPHY_STAT0 0x0188
+#define HDMI_IH_MUTE_AHBDMAAUD_STAT0 0x0189
+#define HDMI_IH_MUTE 0x01FF
+
+/* Video Sample Registers */
+#define HDMI_TX_INVID0 0x0200
+#define HDMI_TX_INSTUFFING 0x0201
+#define HDMI_TX_GYDATA0 0x0202
+#define HDMI_TX_GYDATA1 0x0203
+#define HDMI_TX_RCRDATA0 0x0204
+#define HDMI_TX_RCRDATA1 0x0205
+#define HDMI_TX_BCBDATA0 0x0206
+#define HDMI_TX_BCBDATA1 0x0207
+
+/* Video Packetizer Registers */
+#define HDMI_VP_STATUS 0x0800
+#define HDMI_VP_PR_CD 0x0801
+#define HDMI_VP_STUFF 0x0802
+#define HDMI_VP_REMAP 0x0803
+#define HDMI_VP_CONF 0x0804
+#define HDMI_VP_STAT 0x0805
+#define HDMI_VP_INT 0x0806
+#define HDMI_VP_MASK 0x0807
+#define HDMI_VP_POL 0x0808
+
+/* Frame Composer Registers */
+#define HDMI_FC_INVIDCONF 0x1000
+#define HDMI_FC_INHACTV0 0x1001
+#define HDMI_FC_INHACTV1 0x1002
+#define HDMI_FC_INHBLANK0 0x1003
+#define HDMI_FC_INHBLANK1 0x1004
+#define HDMI_FC_INVACTV0 0x1005
+#define HDMI_FC_INVACTV1 0x1006
+#define HDMI_FC_INVBLANK 0x1007
+#define HDMI_FC_HSYNCINDELAY0 0x1008
+#define HDMI_FC_HSYNCINDELAY1 0x1009
+#define HDMI_FC_HSYNCINWIDTH0 0x100A
+#define HDMI_FC_HSYNCINWIDTH1 0x100B
+#define HDMI_FC_VSYNCINDELAY 0x100C
+#define HDMI_FC_VSYNCINWIDTH 0x100D
+#define HDMI_FC_INFREQ0 0x100E
+#define HDMI_FC_INFREQ1 0x100F
+#define HDMI_FC_INFREQ2 0x1010
+#define HDMI_FC_CTRLDUR 0x1011
+#define HDMI_FC_EXCTRLDUR 0x1012
+#define HDMI_FC_EXCTRLSPAC 0x1013
+#define HDMI_FC_CH0PREAM 0x1014
+#define HDMI_FC_CH1PREAM 0x1015
+#define HDMI_FC_CH2PREAM 0x1016
+#define HDMI_FC_AVICONF3 0x1017
+#define HDMI_FC_GCP 0x1018
+#define HDMI_FC_AVICONF0 0x1019
+#define HDMI_FC_AVICONF1 0x101A
+#define HDMI_FC_AVICONF2 0x101B
+#define HDMI_FC_AVIVID 0x101C
+#define HDMI_FC_AVIETB0 0x101D
+#define HDMI_FC_AVIETB1 0x101E
+#define HDMI_FC_AVISBB0 0x101F
+#define HDMI_FC_AVISBB1 0x1020
+#define HDMI_FC_AVIELB0 0x1021
+#define HDMI_FC_AVIELB1 0x1022
+#define HDMI_FC_AVISRB0 0x1023
+#define HDMI_FC_AVISRB1 0x1024
+#define HDMI_FC_AUDICONF0 0x1025
+#define HDMI_FC_AUDICONF1 0x1026
+#define HDMI_FC_AUDICONF2 0x1027
+#define HDMI_FC_AUDICONF3 0x1028
+#define HDMI_FC_VSDIEEEID0 0x1029
+#define HDMI_FC_VSDSIZE 0x102A
+#define HDMI_FC_VSDIEEEID1 0x1030
+#define HDMI_FC_VSDIEEEID2 0x1031
+#define HDMI_FC_VSDPAYLOAD0 0x1032
+#define HDMI_FC_VSDPAYLOAD1 0x1033
+#define HDMI_FC_VSDPAYLOAD2 0x1034
+#define HDMI_FC_VSDPAYLOAD3 0x1035
+#define HDMI_FC_VSDPAYLOAD4 0x1036
+#define HDMI_FC_VSDPAYLOAD5 0x1037
+#define HDMI_FC_VSDPAYLOAD6 0x1038
+#define HDMI_FC_VSDPAYLOAD7 0x1039
+#define HDMI_FC_VSDPAYLOAD8 0x103A
+#define HDMI_FC_VSDPAYLOAD9 0x103B
+#define HDMI_FC_VSDPAYLOAD10 0x103C
+#define HDMI_FC_VSDPAYLOAD11 0x103D
+#define HDMI_FC_VSDPAYLOAD12 0x103E
+#define HDMI_FC_VSDPAYLOAD13 0x103F
+#define HDMI_FC_VSDPAYLOAD14 0x1040
+#define HDMI_FC_VSDPAYLOAD15 0x1041
+#define HDMI_FC_VSDPAYLOAD16 0x1042
+#define HDMI_FC_VSDPAYLOAD17 0x1043
+#define HDMI_FC_VSDPAYLOAD18 0x1044
+#define HDMI_FC_VSDPAYLOAD19 0x1045
+#define HDMI_FC_VSDPAYLOAD20 0x1046
+#define HDMI_FC_VSDPAYLOAD21 0x1047
+#define HDMI_FC_VSDPAYLOAD22 0x1048
+#define HDMI_FC_VSDPAYLOAD23 0x1049
+#define HDMI_FC_SPDVENDORNAME0 0x104A
+#define HDMI_FC_SPDVENDORNAME1 0x104B
+#define HDMI_FC_SPDVENDORNAME2 0x104C
+#define HDMI_FC_SPDVENDORNAME3 0x104D
+#define HDMI_FC_SPDVENDORNAME4 0x104E
+#define HDMI_FC_SPDVENDORNAME5 0x104F
+#define HDMI_FC_SPDVENDORNAME6 0x1050
+#define HDMI_FC_SPDVENDORNAME7 0x1051
+#define HDMI_FC_SDPPRODUCTNAME0 0x1052
+#define HDMI_FC_SDPPRODUCTNAME1 0x1053
+#define HDMI_FC_SDPPRODUCTNAME2 0x1054
+#define HDMI_FC_SDPPRODUCTNAME3 0x1055
+#define HDMI_FC_SDPPRODUCTNAME4 0x1056
+#define HDMI_FC_SDPPRODUCTNAME5 0x1057
+#define HDMI_FC_SDPPRODUCTNAME6 0x1058
+#define HDMI_FC_SDPPRODUCTNAME7 0x1059
+#define HDMI_FC_SDPPRODUCTNAME8 0x105A
+#define HDMI_FC_SDPPRODUCTNAME9 0x105B
+#define HDMI_FC_SDPPRODUCTNAME10 0x105C
+#define HDMI_FC_SDPPRODUCTNAME11 0x105D
+#define HDMI_FC_SDPPRODUCTNAME12 0x105E
+#define HDMI_FC_SDPPRODUCTNAME13 0x105F
+#define HDMI_FC_SDPPRODUCTNAME14 0x1060
+#define HDMI_FC_SPDPRODUCTNAME15 0x1061
+#define HDMI_FC_SPDDEVICEINF 0x1062
+#define HDMI_FC_AUDSCONF 0x1063
+#define HDMI_FC_AUDSSTAT 0x1064
+#define HDMI_FC_AUDSCHNLS7 0x106e
+#define HDMI_FC_AUDSCHNLS8 0x106f
+#define HDMI_FC_DATACH0FILL 0x1070
+#define HDMI_FC_DATACH1FILL 0x1071
+#define HDMI_FC_DATACH2FILL 0x1072
+#define HDMI_FC_CTRLQHIGH 0x1073
+#define HDMI_FC_CTRLQLOW 0x1074
+#define HDMI_FC_ACP0 0x1075
+#define HDMI_FC_ACP28 0x1076
+#define HDMI_FC_ACP27 0x1077
+#define HDMI_FC_ACP26 0x1078
+#define HDMI_FC_ACP25 0x1079
+#define HDMI_FC_ACP24 0x107A
+#define HDMI_FC_ACP23 0x107B
+#define HDMI_FC_ACP22 0x107C
+#define HDMI_FC_ACP21 0x107D
+#define HDMI_FC_ACP20 0x107E
+#define HDMI_FC_ACP19 0x107F
+#define HDMI_FC_ACP18 0x1080
+#define HDMI_FC_ACP17 0x1081
+#define HDMI_FC_ACP16 0x1082
+#define HDMI_FC_ACP15 0x1083
+#define HDMI_FC_ACP14 0x1084
+#define HDMI_FC_ACP13 0x1085
+#define HDMI_FC_ACP12 0x1086
+#define HDMI_FC_ACP11 0x1087
+#define HDMI_FC_ACP10 0x1088
+#define HDMI_FC_ACP9 0x1089
+#define HDMI_FC_ACP8 0x108A
+#define HDMI_FC_ACP7 0x108B
+#define HDMI_FC_ACP6 0x108C
+#define HDMI_FC_ACP5 0x108D
+#define HDMI_FC_ACP4 0x108E
+#define HDMI_FC_ACP3 0x108F
+#define HDMI_FC_ACP2 0x1090
+#define HDMI_FC_ACP1 0x1091
+#define HDMI_FC_ISCR1_0 0x1092
+#define HDMI_FC_ISCR1_16 0x1093
+#define HDMI_FC_ISCR1_15 0x1094
+#define HDMI_FC_ISCR1_14 0x1095
+#define HDMI_FC_ISCR1_13 0x1096
+#define HDMI_FC_ISCR1_12 0x1097
+#define HDMI_FC_ISCR1_11 0x1098
+#define HDMI_FC_ISCR1_10 0x1099
+#define HDMI_FC_ISCR1_9 0x109A
+#define HDMI_FC_ISCR1_8 0x109B
+#define HDMI_FC_ISCR1_7 0x109C
+#define HDMI_FC_ISCR1_6 0x109D
+#define HDMI_FC_ISCR1_5 0x109E
+#define HDMI_FC_ISCR1_4 0x109F
+#define HDMI_FC_ISCR1_3 0x10A0
+#define HDMI_FC_ISCR1_2 0x10A1
+#define HDMI_FC_ISCR1_1 0x10A2
+#define HDMI_FC_ISCR2_15 0x10A3
+#define HDMI_FC_ISCR2_14 0x10A4
+#define HDMI_FC_ISCR2_13 0x10A5
+#define HDMI_FC_ISCR2_12 0x10A6
+#define HDMI_FC_ISCR2_11 0x10A7
+#define HDMI_FC_ISCR2_10 0x10A8
+#define HDMI_FC_ISCR2_9 0x10A9
+#define HDMI_FC_ISCR2_8 0x10AA
+#define HDMI_FC_ISCR2_7 0x10AB
+#define HDMI_FC_ISCR2_6 0x10AC
+#define HDMI_FC_ISCR2_5 0x10AD
+#define HDMI_FC_ISCR2_4 0x10AE
+#define HDMI_FC_ISCR2_3 0x10AF
+#define HDMI_FC_ISCR2_2 0x10B0
+#define HDMI_FC_ISCR2_1 0x10B1
+#define HDMI_FC_ISCR2_0 0x10B2
+#define HDMI_FC_DATAUTO0 0x10B3
+#define HDMI_FC_DATAUTO1 0x10B4
+#define HDMI_FC_DATAUTO2 0x10B5
+#define HDMI_FC_DATMAN 0x10B6
+#define HDMI_FC_DATAUTO3 0x10B7
+#define HDMI_FC_RDRB0 0x10B8
+#define HDMI_FC_RDRB1 0x10B9
+#define HDMI_FC_RDRB2 0x10BA
+#define HDMI_FC_RDRB3 0x10BB
+#define HDMI_FC_RDRB4 0x10BC
+#define HDMI_FC_RDRB5 0x10BD
+#define HDMI_FC_RDRB6 0x10BE
+#define HDMI_FC_RDRB7 0x10BF
+#define HDMI_FC_STAT0 0x10D0
+#define HDMI_FC_INT0 0x10D1
+#define HDMI_FC_MASK0 0x10D2
+#define HDMI_FC_POL0 0x10D3
+#define HDMI_FC_STAT1 0x10D4
+#define HDMI_FC_INT1 0x10D5
+#define HDMI_FC_MASK1 0x10D6
+#define HDMI_FC_POL1 0x10D7
+#define HDMI_FC_STAT2 0x10D8
+#define HDMI_FC_INT2 0x10D9
+#define HDMI_FC_MASK2 0x10DA
+#define HDMI_FC_POL2 0x10DB
+#define HDMI_FC_PRCONF 0x10E0
+#define HDMI_FC_SCRAMBLER_CTRL 0x10E1
+#define HDMI_FC_PACKET_TX_EN 0x10E3
+
+#define HDMI_FC_GMD_STAT 0x1100
+#define HDMI_FC_GMD_EN 0x1101
+#define HDMI_FC_GMD_UP 0x1102
+#define HDMI_FC_GMD_CONF 0x1103
+#define HDMI_FC_GMD_HB 0x1104
+#define HDMI_FC_GMD_PB0 0x1105
+#define HDMI_FC_GMD_PB1 0x1106
+#define HDMI_FC_GMD_PB2 0x1107
+#define HDMI_FC_GMD_PB3 0x1108
+#define HDMI_FC_GMD_PB4 0x1109
+#define HDMI_FC_GMD_PB5 0x110A
+#define HDMI_FC_GMD_PB6 0x110B
+#define HDMI_FC_GMD_PB7 0x110C
+#define HDMI_FC_GMD_PB8 0x110D
+#define HDMI_FC_GMD_PB9 0x110E
+#define HDMI_FC_GMD_PB10 0x110F
+#define HDMI_FC_GMD_PB11 0x1110
+#define HDMI_FC_GMD_PB12 0x1111
+#define HDMI_FC_GMD_PB13 0x1112
+#define HDMI_FC_GMD_PB14 0x1113
+#define HDMI_FC_GMD_PB15 0x1114
+#define HDMI_FC_GMD_PB16 0x1115
+#define HDMI_FC_GMD_PB17 0x1116
+#define HDMI_FC_GMD_PB18 0x1117
+#define HDMI_FC_GMD_PB19 0x1118
+#define HDMI_FC_GMD_PB20 0x1119
+#define HDMI_FC_GMD_PB21 0x111A
+#define HDMI_FC_GMD_PB22 0x111B
+#define HDMI_FC_GMD_PB23 0x111C
+#define HDMI_FC_GMD_PB24 0x111D
+#define HDMI_FC_GMD_PB25 0x111E
+#define HDMI_FC_GMD_PB26 0x111F
+#define HDMI_FC_GMD_PB27 0x1120
+
+#define HDMI_FC_DRM_UP 0x1167
+#define HDMI_FC_DRM_HB0 0x1168
+#define HDMI_FC_DRM_HB1 0x1169
+#define HDMI_FC_DRM_PB0 0x116A
+#define HDMI_FC_DRM_PB1 0x116B
+#define HDMI_FC_DRM_PB2 0x116C
+#define HDMI_FC_DRM_PB3 0x116D
+#define HDMI_FC_DRM_PB4 0x116E
+#define HDMI_FC_DRM_PB5 0x116F
+#define HDMI_FC_DRM_PB6 0x1170
+#define HDMI_FC_DRM_PB7 0x1171
+#define HDMI_FC_DRM_PB8 0x1172
+#define HDMI_FC_DRM_PB9 0x1173
+#define HDMI_FC_DRM_PB10 0x1174
+#define HDMI_FC_DRM_PB11 0x1175
+#define HDMI_FC_DRM_PB12 0x1176
+#define HDMI_FC_DRM_PB13 0x1177
+#define HDMI_FC_DRM_PB14 0x1178
+#define HDMI_FC_DRM_PB15 0x1179
+#define HDMI_FC_DRM_PB16 0x117A
+#define HDMI_FC_DRM_PB17 0x117B
+#define HDMI_FC_DRM_PB18 0x117C
+#define HDMI_FC_DRM_PB19 0x117D
+#define HDMI_FC_DRM_PB20 0x117E
+#define HDMI_FC_DRM_PB21 0x117F
+#define HDMI_FC_DRM_PB22 0x1180
+#define HDMI_FC_DRM_PB23 0x1181
+#define HDMI_FC_DRM_PB24 0x1182
+#define HDMI_FC_DRM_PB25 0x1183
+#define HDMI_FC_DRM_PB26 0x1184
+
+#define HDMI_FC_DBGFORCE 0x1200
+#define HDMI_FC_DBGAUD0CH0 0x1201
+#define HDMI_FC_DBGAUD1CH0 0x1202
+#define HDMI_FC_DBGAUD2CH0 0x1203
+#define HDMI_FC_DBGAUD0CH1 0x1204
+#define HDMI_FC_DBGAUD1CH1 0x1205
+#define HDMI_FC_DBGAUD2CH1 0x1206
+#define HDMI_FC_DBGAUD0CH2 0x1207
+#define HDMI_FC_DBGAUD1CH2 0x1208
+#define HDMI_FC_DBGAUD2CH2 0x1209
+#define HDMI_FC_DBGAUD0CH3 0x120A
+#define HDMI_FC_DBGAUD1CH3 0x120B
+#define HDMI_FC_DBGAUD2CH3 0x120C
+#define HDMI_FC_DBGAUD0CH4 0x120D
+#define HDMI_FC_DBGAUD1CH4 0x120E
+#define HDMI_FC_DBGAUD2CH4 0x120F
+#define HDMI_FC_DBGAUD0CH5 0x1210
+#define HDMI_FC_DBGAUD1CH5 0x1211
+#define HDMI_FC_DBGAUD2CH5 0x1212
+#define HDMI_FC_DBGAUD0CH6 0x1213
+#define HDMI_FC_DBGAUD1CH6 0x1214
+#define HDMI_FC_DBGAUD2CH6 0x1215
+#define HDMI_FC_DBGAUD0CH7 0x1216
+#define HDMI_FC_DBGAUD1CH7 0x1217
+#define HDMI_FC_DBGAUD2CH7 0x1218
+#define HDMI_FC_DBGTMDS0 0x1219
+#define HDMI_FC_DBGTMDS1 0x121A
+#define HDMI_FC_DBGTMDS2 0x121B
+
+/* HDMI Source PHY Registers */
+#define HDMI_PHY_CONF0 0x3000
+#define HDMI_PHY_TST0 0x3001
+#define HDMI_PHY_TST1 0x3002
+#define HDMI_PHY_TST2 0x3003
+#define HDMI_PHY_STAT0 0x3004
+#define HDMI_PHY_INT0 0x3005
+#define HDMI_PHY_MASK0 0x3006
+#define HDMI_PHY_POL0 0x3007
+
+/* HDMI Master PHY Registers */
+#define HDMI_PHY_I2CM_SLAVE_ADDR 0x3020
+#define HDMI_PHY_I2CM_ADDRESS_ADDR 0x3021
+#define HDMI_PHY_I2CM_DATAO_1_ADDR 0x3022
+#define HDMI_PHY_I2CM_DATAO_0_ADDR 0x3023
+#define HDMI_PHY_I2CM_DATAI_1_ADDR 0x3024
+#define HDMI_PHY_I2CM_DATAI_0_ADDR 0x3025
+#define HDMI_PHY_I2CM_OPERATION_ADDR 0x3026
+#define HDMI_PHY_I2CM_INT_ADDR 0x3027
+#define HDMI_PHY_I2CM_CTLINT_ADDR 0x3028
+#define HDMI_PHY_I2CM_DIV_ADDR 0x3029
+#define HDMI_PHY_I2CM_SOFTRSTZ_ADDR 0x302a
+#define HDMI_PHY_I2CM_SS_SCL_HCNT_1_ADDR 0x302b
+#define HDMI_PHY_I2CM_SS_SCL_HCNT_0_ADDR 0x302c
+#define HDMI_PHY_I2CM_SS_SCL_LCNT_1_ADDR 0x302d
+#define HDMI_PHY_I2CM_SS_SCL_LCNT_0_ADDR 0x302e
+#define HDMI_PHY_I2CM_FS_SCL_HCNT_1_ADDR 0x302f
+#define HDMI_PHY_I2CM_FS_SCL_HCNT_0_ADDR 0x3030
+#define HDMI_PHY_I2CM_FS_SCL_LCNT_1_ADDR 0x3031
+#define HDMI_PHY_I2CM_FS_SCL_LCNT_0_ADDR 0x3032
+#define HDMI_PHY_I2CM_SDA_HOLD 0x3033
+#define HDMI_JTAG_PHY_CONFIG 0x3034
+
+/* Audio Sampler Registers */
+#define HDMI_AUD_CONF0 0x3100
+#define HDMI_AUD_CONF1 0x3101
+#define HDMI_AUD_INT 0x3102
+#define HDMI_AUD_CONF2 0x3103
+#define HDMI_AUD_INT1 0x3104
+#define HDMI_AUD_N1 0x3200
+#define HDMI_AUD_N2 0x3201
+#define HDMI_AUD_N3 0x3202
+#define HDMI_AUD_CTS1 0x3203
+#define HDMI_AUD_CTS2 0x3204
+#define HDMI_AUD_CTS3 0x3205
+#define HDMI_AUD_INPUTCLKFS 0x3206
+#define HDMI_AUD_SPDIFINT 0x3302
+#define HDMI_AUD_CONF0_HBR 0x3400
+#define HDMI_AUD_HBR_STATUS 0x3401
+#define HDMI_AUD_HBR_INT 0x3402
+#define HDMI_AUD_HBR_POL 0x3403
+#define HDMI_AUD_HBR_MASK 0x3404
+
+/*
+ * Generic Parallel Audio Interface Registers
+ * Not used as GPAUD interface is not enabled in hw
+ */
+#define HDMI_GP_CONF0 0x3500
+#define HDMI_GP_CONF1 0x3501
+#define HDMI_GP_CONF2 0x3502
+#define HDMI_GP_MASK 0x3506
+
+/* Audio DMA Registers */
+#define HDMI_AHB_DMA_CONF0 0x3600
+#define HDMI_AHB_DMA_START 0x3601
+#define HDMI_AHB_DMA_STOP 0x3602
+#define HDMI_AHB_DMA_THRSLD 0x3603
+#define HDMI_AHB_DMA_STRADDR0 0x3604
+#define HDMI_AHB_DMA_STRADDR1 0x3605
+#define HDMI_AHB_DMA_STRADDR2 0x3606
+#define HDMI_AHB_DMA_STRADDR3 0x3607
+#define HDMI_AHB_DMA_STPADDR0 0x3608
+#define HDMI_AHB_DMA_STPADDR1 0x3609
+#define HDMI_AHB_DMA_STPADDR2 0x360a
+#define HDMI_AHB_DMA_STPADDR3 0x360b
+#define HDMI_AHB_DMA_BSTADDR0 0x360c
+#define HDMI_AHB_DMA_BSTADDR1 0x360d
+#define HDMI_AHB_DMA_BSTADDR2 0x360e
+#define HDMI_AHB_DMA_BSTADDR3 0x360f
+#define HDMI_AHB_DMA_MBLENGTH0 0x3610
+#define HDMI_AHB_DMA_MBLENGTH1 0x3611
+#define HDMI_AHB_DMA_STAT 0x3612
+#define HDMI_AHB_DMA_INT 0x3613
+#define HDMI_AHB_DMA_MASK 0x3614
+#define HDMI_AHB_DMA_POL 0x3615
+#define HDMI_AHB_DMA_CONF1 0x3616
+#define HDMI_AHB_DMA_BUFFSTAT 0x3617
+#define HDMI_AHB_DMA_BUFFINT 0x3618
+#define HDMI_AHB_DMA_BUFFMASK 0x3619
+#define HDMI_AHB_DMA_BUFFPOL 0x361a
+
+/* Main Controller Registers */
+#define HDMI_MC_SFRDIV 0x4000
+#define HDMI_MC_CLKDIS 0x4001
+#define HDMI_MC_SWRSTZ 0x4002
+#define HDMI_MC_OPCTRL 0x4003
+#define HDMI_MC_FLOWCTRL 0x4004
+#define HDMI_MC_PHYRSTZ 0x4005
+#define HDMI_MC_LOCKONCLOCK 0x4006
+#define HDMI_MC_HEACPHY_RST 0x4007
+
+/* Color Space Converter Registers */
+#define HDMI_CSC_CFG 0x4100
+#define HDMI_CSC_SCALE 0x4101
+#define HDMI_CSC_COEF_A1_MSB 0x4102
+#define HDMI_CSC_COEF_A1_LSB 0x4103
+#define HDMI_CSC_COEF_A2_MSB 0x4104
+#define HDMI_CSC_COEF_A2_LSB 0x4105
+#define HDMI_CSC_COEF_A3_MSB 0x4106
+#define HDMI_CSC_COEF_A3_LSB 0x4107
+#define HDMI_CSC_COEF_A4_MSB 0x4108
+#define HDMI_CSC_COEF_A4_LSB 0x4109
+#define HDMI_CSC_COEF_B1_MSB 0x410A
+#define HDMI_CSC_COEF_B1_LSB 0x410B
+#define HDMI_CSC_COEF_B2_MSB 0x410C
+#define HDMI_CSC_COEF_B2_LSB 0x410D
+#define HDMI_CSC_COEF_B3_MSB 0x410E
+#define HDMI_CSC_COEF_B3_LSB 0x410F
+#define HDMI_CSC_COEF_B4_MSB 0x4110
+#define HDMI_CSC_COEF_B4_LSB 0x4111
+#define HDMI_CSC_COEF_C1_MSB 0x4112
+#define HDMI_CSC_COEF_C1_LSB 0x4113
+#define HDMI_CSC_COEF_C2_MSB 0x4114
+#define HDMI_CSC_COEF_C2_LSB 0x4115
+#define HDMI_CSC_COEF_C3_MSB 0x4116
+#define HDMI_CSC_COEF_C3_LSB 0x4117
+#define HDMI_CSC_COEF_C4_MSB 0x4118
+#define HDMI_CSC_COEF_C4_LSB 0x4119
+
+/* HDCP Encryption Engine Registers */
+#define HDMI_A_HDCPCFG0 0x5000
+#define HDMI_A_HDCPCFG1 0x5001
+#define HDMI_A_HDCPOBS0 0x5002
+#define HDMI_A_HDCPOBS1 0x5003
+#define HDMI_A_HDCPOBS2 0x5004
+#define HDMI_A_HDCPOBS3 0x5005
+#define HDMI_A_APIINTCLR 0x5006
+#define HDMI_A_APIINTSTAT 0x5007
+#define HDMI_A_APIINTMSK 0x5008
+#define HDMI_A_VIDPOLCFG 0x5009
+#define HDMI_A_OESSWCFG 0x500A
+#define HDMI_A_TIMER1SETUP0 0x500B
+#define HDMI_A_TIMER1SETUP1 0x500C
+#define HDMI_A_TIMER2SETUP0 0x500D
+#define HDMI_A_TIMER2SETUP1 0x500E
+#define HDMI_A_100MSCFG 0x500F
+#define HDMI_A_2SCFG0 0x5010
+#define HDMI_A_2SCFG1 0x5011
+#define HDMI_A_5SCFG0 0x5012
+#define HDMI_A_5SCFG1 0x5013
+#define HDMI_A_SRMVERLSB 0x5014
+#define HDMI_A_SRMVERMSB 0x5015
+#define HDMI_A_SRMCTRL 0x5016
+#define HDMI_A_SFRSETUP 0x5017
+#define HDMI_A_I2CHSETUP 0x5018
+#define HDMI_A_INTSETUP 0x5019
+#define HDMI_A_PRESETUP 0x501A
+#define HDMI_A_SRM_BASE 0x5020
+
+/* HDCP Registers */
+#define HDMI_HDCPREG_RMCTL 0x780e
+#define HDMI_HDCPREG_RMSTS 0x780f
+#define HDMI_HDCPREG_SEED0 0x7810
+#define HDMI_HDCPREG_SEED1 0x7811
+#define HDMI_HDCPREG_DPK0 0x7812
+#define HDMI_HDCPREG_DPK1 0x7813
+#define HDMI_HDCPREG_DPK2 0x7814
+#define HDMI_HDCPREG_DPK3 0x7815
+#define HDMI_HDCPREG_DPK4 0x7816
+#define HDMI_HDCPREG_DPK5 0x7817
+#define HDMI_HDCPREG_DPK6 0x7818
+#define HDMI_HDCP2REG_CTRL 0x7904
+#define HDMI_HDCP2REG_MASK 0x790c
+#define HDMI_HDCP2REG_MUTE 0x7912
+
+/* CEC Engine Registers */
+#define HDMI_CEC_CTRL 0x7D00
+#define HDMI_CEC_STAT 0x7D01
+#define HDMI_CEC_MASK 0x7D02
+#define HDMI_CEC_POLARITY 0x7D03
+#define HDMI_CEC_INT 0x7D04
+#define HDMI_CEC_ADDR_L 0x7D05
+#define HDMI_CEC_ADDR_H 0x7D06
+#define HDMI_CEC_TX_CNT 0x7D07
+#define HDMI_CEC_RX_CNT 0x7D08
+#define HDMI_CEC_TX_DATA0 0x7D10
+#define HDMI_CEC_TX_DATA1 0x7D11
+#define HDMI_CEC_TX_DATA2 0x7D12
+#define HDMI_CEC_TX_DATA3 0x7D13
+#define HDMI_CEC_TX_DATA4 0x7D14
+#define HDMI_CEC_TX_DATA5 0x7D15
+#define HDMI_CEC_TX_DATA6 0x7D16
+#define HDMI_CEC_TX_DATA7 0x7D17
+#define HDMI_CEC_TX_DATA8 0x7D18
+#define HDMI_CEC_TX_DATA9 0x7D19
+#define HDMI_CEC_TX_DATA10 0x7D1a
+#define HDMI_CEC_TX_DATA11 0x7D1b
+#define HDMI_CEC_TX_DATA12 0x7D1c
+#define HDMI_CEC_TX_DATA13 0x7D1d
+#define HDMI_CEC_TX_DATA14 0x7D1e
+#define HDMI_CEC_TX_DATA15 0x7D1f
+#define HDMI_CEC_RX_DATA0 0x7D20
+#define HDMI_CEC_RX_DATA1 0x7D21
+#define HDMI_CEC_RX_DATA2 0x7D22
+#define HDMI_CEC_RX_DATA3 0x7D23
+#define HDMI_CEC_RX_DATA4 0x7D24
+#define HDMI_CEC_RX_DATA5 0x7D25
+#define HDMI_CEC_RX_DATA6 0x7D26
+#define HDMI_CEC_RX_DATA7 0x7D27
+#define HDMI_CEC_RX_DATA8 0x7D28
+#define HDMI_CEC_RX_DATA9 0x7D29
+#define HDMI_CEC_RX_DATA10 0x7D2a
+#define HDMI_CEC_RX_DATA11 0x7D2b
+#define HDMI_CEC_RX_DATA12 0x7D2c
+#define HDMI_CEC_RX_DATA13 0x7D2d
+#define HDMI_CEC_RX_DATA14 0x7D2e
+#define HDMI_CEC_RX_DATA15 0x7D2f
+#define HDMI_CEC_LOCK 0x7D30
+#define HDMI_CEC_WKUPCTRL 0x7D31
+
+/* I2C Master Registers (E-DDC) */
+#define HDMI_I2CM_SLAVE 0x7E00
+#define HDMI_I2CM_ADDRESS 0x7E01
+#define HDMI_I2CM_DATAO 0x7E02
+#define HDMI_I2CM_DATAI 0x7E03
+#define HDMI_I2CM_OPERATION 0x7E04
+#define HDMI_I2CM_INT 0x7E05
+#define HDMI_I2CM_CTLINT 0x7E06
+#define HDMI_I2CM_DIV 0x7E07
+#define HDMI_I2CM_SEGADDR 0x7E08
+#define HDMI_I2CM_SOFTRSTZ 0x7E09
+#define HDMI_I2CM_SEGPTR 0x7E0A
+#define HDMI_I2CM_SS_SCL_HCNT_1_ADDR 0x7E0B
+#define HDMI_I2CM_SS_SCL_HCNT_0_ADDR 0x7E0C
+#define HDMI_I2CM_SS_SCL_LCNT_1_ADDR 0x7E0D
+#define HDMI_I2CM_SS_SCL_LCNT_0_ADDR 0x7E0E
+#define HDMI_I2CM_FS_SCL_HCNT_1_ADDR 0x7E0F
+#define HDMI_I2CM_FS_SCL_HCNT_0_ADDR 0x7E10
+#define HDMI_I2CM_FS_SCL_LCNT_1_ADDR 0x7E11
+#define HDMI_I2CM_FS_SCL_LCNT_0_ADDR 0x7E12
+#define HDMI_I2CM_SDA_HOLD 0x7E13
+#define HDMI_I2CM_SCDC_READ_UPDATE_ON 0x7E14
+
+#define HDMI_I2CM_MAX_REG 0x7E31
+
+enum {
+ /* PRODUCT_ID0 field values */
+ HDMI_PRODUCT_ID0_HDMI_TX = 0xa0,
+
+ /* PRODUCT_ID1 field values */
+ HDMI_PRODUCT_ID1_HDCP = 0xc0,
+ HDMI_PRODUCT_ID1_HDMI_RX = 0x02,
+ HDMI_PRODUCT_ID1_HDMI_TX = 0x01,
+
+ /* CONFIG0_ID field values */
+ HDMI_CONFIG0_I2S = 0x10,
+ HDMI_CONFIG0_CEC = 0x02,
+
+ /* CONFIG1_ID field values */
+ HDMI_CONFIG1_AHB = 0x01,
+
+ /* CONFIG3_ID field values */
+ HDMI_CONFIG3_AHBAUDDMA = 0x02,
+ HDMI_CONFIG3_GPAUD = 0x01,
+
+ /* IH_FC_INT2 field values */
+ HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03,
+ HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
+ HDMI_IH_FC_INT2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
+ /* IH_FC_STAT2 field values */
+ HDMI_IH_FC_STAT2_OVERFLOW_MASK = 0x03,
+ HDMI_IH_FC_STAT2_LOW_PRIORITY_OVERFLOW = 0x02,
+ HDMI_IH_FC_STAT2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
+ /* IH_PHY_STAT0 field values */
+ HDMI_IH_PHY_STAT0_RX_SENSE3 = 0x20,
+ HDMI_IH_PHY_STAT0_RX_SENSE2 = 0x10,
+ HDMI_IH_PHY_STAT0_RX_SENSE1 = 0x8,
+ HDMI_IH_PHY_STAT0_RX_SENSE0 = 0x4,
+ HDMI_IH_PHY_STAT0_TX_PHY_LOCK = 0x2,
+ HDMI_IH_PHY_STAT0_HPD = 0x1,
+
+ /* IH_I2CM_STAT0 and IH_MUTE_I2CM_STAT0 field values */
+ HDMI_IH_I2CM_STAT0_DONE = 0x2,
+ HDMI_IH_I2CM_STAT0_ERROR = 0x1,
+
+ /* IH_MUTE_I2CMPHY_STAT0 field values */
+ HDMI_IH_MUTE_I2CMPHY_STAT0_I2CMPHYDONE = 0x2,
+ HDMI_IH_MUTE_I2CMPHY_STAT0_I2CMPHYERROR = 0x1,
+
+ /* IH_AHBDMAAUD_STAT0 field values */
+ HDMI_IH_AHBDMAAUD_STAT0_ERROR = 0x20,
+ HDMI_IH_AHBDMAAUD_STAT0_LOST = 0x10,
+ HDMI_IH_AHBDMAAUD_STAT0_RETRY = 0x08,
+ HDMI_IH_AHBDMAAUD_STAT0_DONE = 0x04,
+ HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = 0x02,
+ HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = 0x01,
+
+ /* IH_MUTE_FC_STAT2 field values */
+ HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK = 0x03,
+ HDMI_IH_MUTE_FC_STAT2_LOW_PRIORITY_OVERFLOW = 0x02,
+ HDMI_IH_MUTE_FC_STAT2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
+ /* IH_MUTE_AHBDMAAUD_STAT0 field values */
+ HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = 0x20,
+ HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = 0x10,
+ HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = 0x08,
+ HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = 0x04,
+ HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = 0x02,
+ HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = 0x01,
+
+ /* IH_MUTE field values */
+ HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT = 0x2,
+ HDMI_IH_MUTE_MUTE_ALL_INTERRUPT = 0x1,
+
+ /* TX_INVID0 field values */
+ HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_MASK = 0x80,
+ HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_ENABLE = 0x80,
+ HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE = 0x00,
+ HDMI_TX_INVID0_VIDEO_MAPPING_MASK = 0x1F,
+ HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET = 0,
+
+ /* TX_INSTUFFING field values */
+ HDMI_TX_INSTUFFING_BDBDATA_STUFFING_MASK = 0x4,
+ HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE = 0x4,
+ HDMI_TX_INSTUFFING_BDBDATA_STUFFING_DISABLE = 0x0,
+ HDMI_TX_INSTUFFING_RCRDATA_STUFFING_MASK = 0x2,
+ HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE = 0x2,
+ HDMI_TX_INSTUFFING_RCRDATA_STUFFING_DISABLE = 0x0,
+ HDMI_TX_INSTUFFING_GYDATA_STUFFING_MASK = 0x1,
+ HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE = 0x1,
+ HDMI_TX_INSTUFFING_GYDATA_STUFFING_DISABLE = 0x0,
+
+ /* VP_PR_CD field values */
+ HDMI_VP_PR_CD_COLOR_DEPTH_MASK = 0xF0,
+ HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET = 4,
+ HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK = 0x0F,
+ HDMI_VP_PR_CD_DESIRED_PR_FACTOR_OFFSET = 0,
+
+ /* VP_STUFF field values */
+ HDMI_VP_STUFF_IDEFAULT_PHASE_MASK = 0x20,
+ HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET = 5,
+ HDMI_VP_STUFF_IFIX_PP_TO_LAST_MASK = 0x10,
+ HDMI_VP_STUFF_IFIX_PP_TO_LAST_OFFSET = 4,
+ HDMI_VP_STUFF_ICX_GOTO_P0_ST_MASK = 0x8,
+ HDMI_VP_STUFF_ICX_GOTO_P0_ST_OFFSET = 3,
+ HDMI_VP_STUFF_YCC422_STUFFING_MASK = 0x4,
+ HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE = 0x4,
+ HDMI_VP_STUFF_YCC422_STUFFING_DIRECT_MODE = 0x0,
+ HDMI_VP_STUFF_PP_STUFFING_MASK = 0x2,
+ HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE = 0x2,
+ HDMI_VP_STUFF_PP_STUFFING_DIRECT_MODE = 0x0,
+ HDMI_VP_STUFF_PR_STUFFING_MASK = 0x1,
+ HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE = 0x1,
+ HDMI_VP_STUFF_PR_STUFFING_DIRECT_MODE = 0x0,
+
+ /* VP_CONF field values */
+ HDMI_VP_CONF_BYPASS_EN_MASK = 0x40,
+ HDMI_VP_CONF_BYPASS_EN_ENABLE = 0x40,
+ HDMI_VP_CONF_BYPASS_EN_DISABLE = 0x00,
+ HDMI_VP_CONF_PP_EN_ENMASK = 0x20,
+ HDMI_VP_CONF_PP_EN_ENABLE = 0x20,
+ HDMI_VP_CONF_PP_EN_DISABLE = 0x00,
+ HDMI_VP_CONF_PR_EN_MASK = 0x10,
+ HDMI_VP_CONF_PR_EN_ENABLE = 0x10,
+ HDMI_VP_CONF_PR_EN_DISABLE = 0x00,
+ HDMI_VP_CONF_YCC422_EN_MASK = 0x8,
+ HDMI_VP_CONF_YCC422_EN_ENABLE = 0x8,
+ HDMI_VP_CONF_YCC422_EN_DISABLE = 0x0,
+ HDMI_VP_CONF_BYPASS_SELECT_MASK = 0x4,
+ HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER = 0x4,
+ HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER = 0x0,
+ HDMI_VP_CONF_OUTPUT_SELECTOR_MASK = 0x3,
+ HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS = 0x3,
+ HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422 = 0x1,
+ HDMI_VP_CONF_OUTPUT_SELECTOR_PP = 0x0,
+
+ /* VP_REMAP field values */
+ HDMI_VP_REMAP_MASK = 0x3,
+ HDMI_VP_REMAP_YCC422_24bit = 0x2,
+ HDMI_VP_REMAP_YCC422_20bit = 0x1,
+ HDMI_VP_REMAP_YCC422_16bit = 0x0,
+
+ /* FC_INVIDCONF field values */
+ HDMI_FC_INVIDCONF_HDCP_KEEPOUT_MASK = 0x80,
+ HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE = 0x80,
+ HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE = 0x00,
+ HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_MASK = 0x40,
+ HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH = 0x40,
+ HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW = 0x00,
+ HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_MASK = 0x20,
+ HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH = 0x20,
+ HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW = 0x00,
+ HDMI_FC_INVIDCONF_DE_IN_POLARITY_MASK = 0x10,
+ HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH = 0x10,
+ HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_LOW = 0x00,
+ HDMI_FC_INVIDCONF_DVI_MODEZ_MASK = 0x8,
+ HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE = 0x8,
+ HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE = 0x0,
+ HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_MASK = 0x2,
+ HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH = 0x2,
+ HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW = 0x0,
+ HDMI_FC_INVIDCONF_IN_I_P_MASK = 0x1,
+ HDMI_FC_INVIDCONF_IN_I_P_INTERLACED = 0x1,
+ HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE = 0x0,
+
+ /* FC_AUDICONF0 field values */
+ HDMI_FC_AUDICONF0_CC_OFFSET = 4,
+ HDMI_FC_AUDICONF0_CC_MASK = 0x70,
+ HDMI_FC_AUDICONF0_CT_OFFSET = 0,
+ HDMI_FC_AUDICONF0_CT_MASK = 0xF,
+
+ /* FC_AUDICONF1 field values */
+ HDMI_FC_AUDICONF1_SS_OFFSET = 4,
+ HDMI_FC_AUDICONF1_SS_MASK = 0x30,
+ HDMI_FC_AUDICONF1_SF_OFFSET = 0,
+ HDMI_FC_AUDICONF1_SF_MASK = 0x7,
+
+ /* FC_AUDICONF3 field values */
+ HDMI_FC_AUDICONF3_LFEPBL_OFFSET = 5,
+ HDMI_FC_AUDICONF3_LFEPBL_MASK = 0x60,
+ HDMI_FC_AUDICONF3_DM_INH_OFFSET = 4,
+ HDMI_FC_AUDICONF3_DM_INH_MASK = 0x10,
+ HDMI_FC_AUDICONF3_LSV_OFFSET = 0,
+ HDMI_FC_AUDICONF3_LSV_MASK = 0xF,
+
+ /* FC_AUDSCHNLS0 field values */
+ HDMI_FC_AUDSCHNLS0_CGMSA_OFFSET = 4,
+ HDMI_FC_AUDSCHNLS0_CGMSA_MASK = 0x30,
+ HDMI_FC_AUDSCHNLS0_COPYRIGHT_OFFSET = 0,
+ HDMI_FC_AUDSCHNLS0_COPYRIGHT_MASK = 0x01,
+
+ /* FC_AUDSCHNLS3-6 field values */
+ HDMI_FC_AUDSCHNLS3_OIEC_CH0_OFFSET = 0,
+ HDMI_FC_AUDSCHNLS3_OIEC_CH0_MASK = 0x0f,
+ HDMI_FC_AUDSCHNLS3_OIEC_CH1_OFFSET = 4,
+ HDMI_FC_AUDSCHNLS3_OIEC_CH1_MASK = 0xf0,
+ HDMI_FC_AUDSCHNLS4_OIEC_CH2_OFFSET = 0,
+ HDMI_FC_AUDSCHNLS4_OIEC_CH2_MASK = 0x0f,
+ HDMI_FC_AUDSCHNLS4_OIEC_CH3_OFFSET = 4,
+ HDMI_FC_AUDSCHNLS4_OIEC_CH3_MASK = 0xf0,
+
+ HDMI_FC_AUDSCHNLS5_OIEC_CH0_OFFSET = 0,
+ HDMI_FC_AUDSCHNLS5_OIEC_CH0_MASK = 0x0f,
+ HDMI_FC_AUDSCHNLS5_OIEC_CH1_OFFSET = 4,
+ HDMI_FC_AUDSCHNLS5_OIEC_CH1_MASK = 0xf0,
+ HDMI_FC_AUDSCHNLS6_OIEC_CH2_OFFSET = 0,
+ HDMI_FC_AUDSCHNLS6_OIEC_CH2_MASK = 0x0f,
+ HDMI_FC_AUDSCHNLS6_OIEC_CH3_OFFSET = 4,
+ HDMI_FC_AUDSCHNLS6_OIEC_CH3_MASK = 0xf0,
+
+ /* HDMI_FC_AUDSCHNLS7 field values */
+ HDMI_FC_AUDSCHNLS7_ACCURACY_OFFSET = 4,
+ HDMI_FC_AUDSCHNLS7_ACCURACY_MASK = 0x30,
+
+ /* HDMI_FC_AUDSCHNLS8 field values */
+ HDMI_FC_AUDSCHNLS8_ORIGSAMPFREQ_MASK = 0xf0,
+ HDMI_FC_AUDSCHNLS8_ORIGSAMPFREQ_OFFSET = 4,
+ HDMI_FC_AUDSCHNLS8_WORDLEGNTH_MASK = 0x0f,
+ HDMI_FC_AUDSCHNLS8_WORDLEGNTH_OFFSET = 0,
+
+ /* FC_AUDSCONF field values */
+ HDMI_FC_AUDSCONF_AUD_PACKET_SAMPFIT_MASK = 0xF0,
+ HDMI_FC_AUDSCONF_AUD_PACKET_SAMPFIT_OFFSET = 4,
+ HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_MASK = 0x1,
+ HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_OFFSET = 0,
+ HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT1 = 0x1,
+ HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT0 = 0x0,
+
+ /* FC_STAT2 field values */
+ HDMI_FC_STAT2_OVERFLOW_MASK = 0x03,
+ HDMI_FC_STAT2_LOW_PRIORITY_OVERFLOW = 0x02,
+ HDMI_FC_STAT2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
+ /* FC_INT2 field values */
+ HDMI_FC_INT2_OVERFLOW_MASK = 0x03,
+ HDMI_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
+ HDMI_FC_INT2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
+ /* FC_MASK2 field values */
+ HDMI_FC_MASK2_OVERFLOW_MASK = 0x03,
+ HDMI_FC_MASK2_LOW_PRIORITY_OVERFLOW = 0x02,
+ HDMI_FC_MASK2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
+ /* FC_PRCONF field values */
+ HDMI_FC_PRCONF_INCOMING_PR_FACTOR_MASK = 0xF0,
+ HDMI_FC_PRCONF_INCOMING_PR_FACTOR_OFFSET = 4,
+ HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_MASK = 0x0F,
+ HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_OFFSET = 0,
+
+ /* FC_PACKET_TX_EN field values */
+ HDMI_FC_PACKET_TX_EN_DRM_MASK = 0x80,
+ HDMI_FC_PACKET_TX_EN_DRM_ENABLE = 0x80,
+ HDMI_FC_PACKET_TX_EN_DRM_DISABLE = 0x00,
+
+ /* FC_AVICONF0-FC_AVICONF3 field values */
+ HDMI_FC_AVICONF0_PIX_FMT_MASK = 0x03,
+ HDMI_FC_AVICONF0_PIX_FMT_RGB = 0x00,
+ HDMI_FC_AVICONF0_PIX_FMT_YCBCR422 = 0x01,
+ HDMI_FC_AVICONF0_PIX_FMT_YCBCR444 = 0x02,
+ HDMI_FC_AVICONF0_ACTIVE_FMT_MASK = 0x40,
+ HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT = 0x40,
+ HDMI_FC_AVICONF0_ACTIVE_FMT_NO_INFO = 0x00,
+ HDMI_FC_AVICONF0_BAR_DATA_MASK = 0x0C,
+ HDMI_FC_AVICONF0_BAR_DATA_NO_DATA = 0x00,
+ HDMI_FC_AVICONF0_BAR_DATA_VERT_BAR = 0x04,
+ HDMI_FC_AVICONF0_BAR_DATA_HORIZ_BAR = 0x08,
+ HDMI_FC_AVICONF0_BAR_DATA_VERT_HORIZ_BAR = 0x0C,
+ HDMI_FC_AVICONF0_SCAN_INFO_MASK = 0x30,
+ HDMI_FC_AVICONF0_SCAN_INFO_OVERSCAN = 0x10,
+ HDMI_FC_AVICONF0_SCAN_INFO_UNDERSCAN = 0x20,
+ HDMI_FC_AVICONF0_SCAN_INFO_NODATA = 0x00,
+
+ HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_MASK = 0x0F,
+ HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_USE_CODED = 0x08,
+ HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_4_3 = 0x09,
+ HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_16_9 = 0x0A,
+ HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_14_9 = 0x0B,
+ HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_MASK = 0x30,
+ HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_NO_DATA = 0x00,
+ HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_4_3 = 0x10,
+ HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_16_9 = 0x20,
+ HDMI_FC_AVICONF1_COLORIMETRY_MASK = 0xC0,
+ HDMI_FC_AVICONF1_COLORIMETRY_NO_DATA = 0x00,
+ HDMI_FC_AVICONF1_COLORIMETRY_SMPTE = 0x40,
+ HDMI_FC_AVICONF1_COLORIMETRY_ITUR = 0x80,
+ HDMI_FC_AVICONF1_COLORIMETRY_EXTENDED_INFO = 0xC0,
+
+ HDMI_FC_AVICONF2_SCALING_MASK = 0x03,
+ HDMI_FC_AVICONF2_SCALING_NONE = 0x00,
+ HDMI_FC_AVICONF2_SCALING_HORIZ = 0x01,
+ HDMI_FC_AVICONF2_SCALING_VERT = 0x02,
+ HDMI_FC_AVICONF2_SCALING_HORIZ_VERT = 0x03,
+ HDMI_FC_AVICONF2_RGB_QUANT_MASK = 0x0C,
+ HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT = 0x00,
+ HDMI_FC_AVICONF2_RGB_QUANT_LIMITED_RANGE = 0x04,
+ HDMI_FC_AVICONF2_RGB_QUANT_FULL_RANGE = 0x08,
+ HDMI_FC_AVICONF2_EXT_COLORIMETRY_MASK = 0x70,
+ HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC601 = 0x00,
+ HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC709 = 0x10,
+ HDMI_FC_AVICONF2_EXT_COLORIMETRY_SYCC601 = 0x20,
+ HDMI_FC_AVICONF2_EXT_COLORIMETRY_ADOBE_YCC601 = 0x30,
+ HDMI_FC_AVICONF2_EXT_COLORIMETRY_ADOBE_RGB = 0x40,
+ HDMI_FC_AVICONF2_IT_CONTENT_MASK = 0x80,
+ HDMI_FC_AVICONF2_IT_CONTENT_NO_DATA = 0x00,
+ HDMI_FC_AVICONF2_IT_CONTENT_VALID = 0x80,
+
+ HDMI_FC_AVICONF3_IT_CONTENT_TYPE_MASK = 0x03,
+ HDMI_FC_AVICONF3_IT_CONTENT_TYPE_GRAPHICS = 0x00,
+ HDMI_FC_AVICONF3_IT_CONTENT_TYPE_PHOTO = 0x01,
+ HDMI_FC_AVICONF3_IT_CONTENT_TYPE_CINEMA = 0x02,
+ HDMI_FC_AVICONF3_IT_CONTENT_TYPE_GAME = 0x03,
+ HDMI_FC_AVICONF3_QUANT_RANGE_MASK = 0x0C,
+ HDMI_FC_AVICONF3_QUANT_RANGE_LIMITED = 0x00,
+ HDMI_FC_AVICONF3_QUANT_RANGE_FULL = 0x04,
+
+ /* HDMI_FC_GCP */
+ HDMI_FC_GCP_SET_AVMUTE = 0x2,
+ HDMI_FC_GCP_CLEAR_AVMUTE = 0x1,
+
+ /* FC_DBGFORCE field values */
+ HDMI_FC_DBGFORCE_FORCEAUDIO = 0x10,
+ HDMI_FC_DBGFORCE_FORCEVIDEO = 0x1,
+
+ /* FC_DATAUTO0 field values */
+ HDMI_FC_DATAUTO0_VSD_MASK = 0x08,
+ HDMI_FC_DATAUTO0_VSD_OFFSET = 3,
+
+ /* PHY_CONF0 field values */
+ HDMI_PHY_CONF0_PDZ_MASK = 0x80,
+ HDMI_PHY_CONF0_PDZ_OFFSET = 7,
+ HDMI_PHY_CONF0_ENTMDS_MASK = 0x40,
+ HDMI_PHY_CONF0_ENTMDS_OFFSET = 6,
+ HDMI_PHY_CONF0_SVSRET_MASK = 0x20,
+ HDMI_PHY_CONF0_SVSRET_OFFSET = 5,
+ HDMI_PHY_CONF0_GEN2_PDDQ_MASK = 0x10,
+ HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET = 4,
+ HDMI_PHY_CONF0_GEN2_TXPWRON_MASK = 0x8,
+ HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET = 3,
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_MASK = 0x4,
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_OFFSET = 2,
+ HDMI_PHY_CONF0_SELDATAENPOL_MASK = 0x2,
+ HDMI_PHY_CONF0_SELDATAENPOL_OFFSET = 1,
+ HDMI_PHY_CONF0_SELDIPIF_MASK = 0x1,
+ HDMI_PHY_CONF0_SELDIPIF_OFFSET = 0,
+
+ /* PHY_TST0 field values */
+ HDMI_PHY_TST0_TSTCLR_MASK = 0x20,
+ HDMI_PHY_TST0_TSTCLR_OFFSET = 5,
+ HDMI_PHY_TST0_TSTEN_MASK = 0x10,
+ HDMI_PHY_TST0_TSTEN_OFFSET = 4,
+ HDMI_PHY_TST0_TSTCLK_MASK = 0x1,
+ HDMI_PHY_TST0_TSTCLK_OFFSET = 0,
+
+ /* PHY_STAT0 field values */
+ HDMI_PHY_RX_SENSE3 = 0x80,
+ HDMI_PHY_RX_SENSE2 = 0x40,
+ HDMI_PHY_RX_SENSE1 = 0x20,
+ HDMI_PHY_RX_SENSE0 = 0x10,
+ HDMI_PHY_HPD = 0x02,
+ HDMI_PHY_TX_PHY_LOCK = 0x01,
+
+ /* PHY_I2CM_SLAVE_ADDR field values */
+ HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2 = 0x69,
+ HDMI_PHY_I2CM_SLAVE_ADDR_HEAC_PHY = 0x49,
+
+ /* PHY_I2CM_OPERATION_ADDR field values */
+ HDMI_PHY_I2CM_OPERATION_ADDR_WRITE = 0x10,
+ HDMI_PHY_I2CM_OPERATION_ADDR_READ = 0x1,
+
+ /* HDMI_PHY_I2CM_INT_ADDR */
+ HDMI_PHY_I2CM_INT_ADDR_DONE_POL = 0x08,
+ HDMI_PHY_I2CM_INT_ADDR_DONE_MASK = 0x04,
+
+ /* HDMI_PHY_I2CM_CTLINT_ADDR */
+ HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL = 0x80,
+ HDMI_PHY_I2CM_CTLINT_ADDR_NAC_MASK = 0x40,
+ HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL = 0x08,
+ HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_MASK = 0x04,
+
+ /* AUD */
+ HDMI_AUD_CONF0_I2S_SELECT_MASK = 0x20,
+ HDMI_AUD_CONF0_I2S_2CHANNEL_ENABLE = 0x21,
+ HDMI_AUD_CONF0_I2S_4CHANNEL_ENABLE = 0x23,
+ HDMI_AUD_CONF0_I2S_6CHANNEL_ENABLE = 0x27,
+ HDMI_AUD_CONF0_I2S_8CHANNEL_ENABLE = 0x2F,
+ HDMI_AUD_CONF0_I2S_ALL_ENABLE = 0x2F,
+
+ /* AUD_CONF0 field values */
+ HDMI_AUD_CONF0_SW_RESET = 0x80,
+ HDMI_AUD_CONF0_I2S_SELECT = 0x20,
+ HDMI_AUD_CONF0_I2S_EN3 = 0x08,
+ HDMI_AUD_CONF0_I2S_EN2 = 0x04,
+ HDMI_AUD_CONF0_I2S_EN1 = 0x02,
+ HDMI_AUD_CONF0_I2S_EN0 = 0x01,
+
+ /* AUD_CONF1 field values */
+ HDMI_AUD_CONF1_MODE_I2S = 0x00,
+ HDMI_AUD_CONF1_MODE_RIGHT_J = 0x20,
+ HDMI_AUD_CONF1_MODE_LEFT_J = 0x40,
+ HDMI_AUD_CONF1_MODE_BURST_1 = 0x60,
+ HDMI_AUD_CONF1_MODE_BURST_2 = 0x80,
+ HDMI_AUD_CONF1_WIDTH_16 = 0x10,
+ HDMI_AUD_CONF1_WIDTH_24 = 0x18,
+
+ /* AUD_CTS3 field values */
+ HDMI_AUD_CTS3_N_SHIFT_OFFSET = 5,
+ HDMI_AUD_CTS3_N_SHIFT_MASK = 0xe0,
+ HDMI_AUD_CTS3_N_SHIFT_1 = 0,
+ HDMI_AUD_CTS3_N_SHIFT_16 = 0x20,
+ HDMI_AUD_CTS3_N_SHIFT_32 = 0x40,
+ HDMI_AUD_CTS3_N_SHIFT_64 = 0x60,
+ HDMI_AUD_CTS3_N_SHIFT_128 = 0x80,
+ HDMI_AUD_CTS3_N_SHIFT_256 = 0xa0,
+ /* note that the CTS3 MANUAL bit has been removed
+ from our part. Can't set it, will read as 0. */
+ HDMI_AUD_CTS3_CTS_MANUAL = 0x10,
+ HDMI_AUD_CTS3_AUDCTS19_16_MASK = 0x0f,
+
+ /* HDMI_AUD_INPUTCLKFS field values */
+ HDMI_AUD_INPUTCLKFS_128FS = 0,
+ HDMI_AUD_INPUTCLKFS_256FS = 1,
+ HDMI_AUD_INPUTCLKFS_512FS = 2,
+ HDMI_AUD_INPUTCLKFS_64FS = 4,
+
+ /* AHB_DMA_CONF0 field values */
+ HDMI_AHB_DMA_CONF0_SW_FIFO_RST_OFFSET = 7,
+ HDMI_AHB_DMA_CONF0_SW_FIFO_RST_MASK = 0x80,
+ HDMI_AHB_DMA_CONF0_HBR = 0x10,
+ HDMI_AHB_DMA_CONF0_EN_HLOCK_OFFSET = 3,
+ HDMI_AHB_DMA_CONF0_EN_HLOCK_MASK = 0x08,
+ HDMI_AHB_DMA_CONF0_INCR_TYPE_OFFSET = 1,
+ HDMI_AHB_DMA_CONF0_INCR_TYPE_MASK = 0x06,
+ HDMI_AHB_DMA_CONF0_INCR4 = 0x0,
+ HDMI_AHB_DMA_CONF0_INCR8 = 0x2,
+ HDMI_AHB_DMA_CONF0_INCR16 = 0x4,
+ HDMI_AHB_DMA_CONF0_BURST_MODE = 0x1,
+
+ /* HDMI_AHB_DMA_START field values */
+ HDMI_AHB_DMA_START_START_OFFSET = 0,
+ HDMI_AHB_DMA_START_START_MASK = 0x01,
+
+ /* HDMI_AHB_DMA_STOP field values */
+ HDMI_AHB_DMA_STOP_STOP_OFFSET = 0,
+ HDMI_AHB_DMA_STOP_STOP_MASK = 0x01,
+
+ /* AHB_DMA_STAT, AHB_DMA_INT, AHB_DMA_MASK, AHB_DMA_POL field values */
+ HDMI_AHB_DMA_DONE = 0x80,
+ HDMI_AHB_DMA_RETRY_SPLIT = 0x40,
+ HDMI_AHB_DMA_LOSTOWNERSHIP = 0x20,
+ HDMI_AHB_DMA_ERROR = 0x10,
+ HDMI_AHB_DMA_FIFO_THREMPTY = 0x04,
+ HDMI_AHB_DMA_FIFO_FULL = 0x02,
+ HDMI_AHB_DMA_FIFO_EMPTY = 0x01,
+
+ /* AHB_DMA_BUFFSTAT, AHB_DMA_BUFFINT,AHB_DMA_BUFFMASK,AHB_DMA_BUFFPOL values */
+ HDMI_AHB_DMA_BUFFSTAT_FULL = 0x02,
+ HDMI_AHB_DMA_BUFFSTAT_EMPTY = 0x01,
+
+ /* MC_CLKDIS field values */
+ HDMI_MC_CLKDIS_HDCPCLK_DISABLE = 0x40,
+ HDMI_MC_CLKDIS_CECCLK_DISABLE = 0x20,
+ HDMI_MC_CLKDIS_CSCCLK_DISABLE = 0x10,
+ HDMI_MC_CLKDIS_AUDCLK_DISABLE = 0x8,
+ HDMI_MC_CLKDIS_PREPCLK_DISABLE = 0x4,
+ HDMI_MC_CLKDIS_TMDSCLK_DISABLE = 0x2,
+ HDMI_MC_CLKDIS_PIXELCLK_DISABLE = 0x1,
+ HDMI_MC_CLKDIS_PIXELCLK_MASK = 0x1,
+ HDMI_MC_CLKDIS_PIXELCLK_ENABLE = 0,
+
+ /* MC_SWRSTZ field values */
+ HDMI_MC_SWRSTZ_I2SSWRST_REQ = 0x08,
+ HDMI_MC_SWRSTZ_TMDSSWRST_REQ = 0x02,
+
+ /* MC_FLOWCTRL field values */
+ HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_MASK = 0x1,
+ HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH = 0x1,
+ HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS = 0x0,
+
+ /* MC_PHYRSTZ field values */
+ HDMI_MC_PHYRSTZ_PHYRSTZ = 0x01,
+
+ /* MC_HEACPHY_RST field values */
+ HDMI_MC_HEACPHY_RST_ASSERT = 0x1,
+ HDMI_MC_HEACPHY_RST_DEASSERT = 0x0,
+
+ /* CSC_CFG field values */
+ HDMI_CSC_CFG_INTMODE_MASK = 0x30,
+ HDMI_CSC_CFG_INTMODE_OFFSET = 4,
+ HDMI_CSC_CFG_INTMODE_DISABLE = 0x00,
+ HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA1 = 0x10,
+ HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA2 = 0x20,
+ HDMI_CSC_CFG_DECMODE_MASK = 0x3,
+ HDMI_CSC_CFG_DECMODE_OFFSET = 0,
+ HDMI_CSC_CFG_DECMODE_DISABLE = 0x0,
+ HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA1 = 0x1,
+ HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA2 = 0x2,
+ HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA3 = 0x3,
+
+ /* CSC_SCALE field values */
+ HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK = 0xF0,
+ HDMI_CSC_SCALE_CSC_COLORDE_PTH_24BPP = 0x00,
+ HDMI_CSC_SCALE_CSC_COLORDE_PTH_30BPP = 0x50,
+ HDMI_CSC_SCALE_CSC_COLORDE_PTH_36BPP = 0x60,
+ HDMI_CSC_SCALE_CSC_COLORDE_PTH_48BPP = 0x70,
+ HDMI_CSC_SCALE_CSCSCALE_MASK = 0x03,
+
+ /* A_HDCPCFG0 field values */
+ HDMI_A_HDCPCFG0_ELVENA_MASK = 0x80,
+ HDMI_A_HDCPCFG0_ELVENA_ENABLE = 0x80,
+ HDMI_A_HDCPCFG0_ELVENA_DISABLE = 0x00,
+ HDMI_A_HDCPCFG0_I2CFASTMODE_MASK = 0x40,
+ HDMI_A_HDCPCFG0_I2CFASTMODE_ENABLE = 0x40,
+ HDMI_A_HDCPCFG0_I2CFASTMODE_DISABLE = 0x00,
+ HDMI_A_HDCPCFG0_BYPENCRYPTION_MASK = 0x20,
+ HDMI_A_HDCPCFG0_BYPENCRYPTION_ENABLE = 0x20,
+ HDMI_A_HDCPCFG0_BYPENCRYPTION_DISABLE = 0x00,
+ HDMI_A_HDCPCFG0_SYNCRICHECK_MASK = 0x10,
+ HDMI_A_HDCPCFG0_SYNCRICHECK_ENABLE = 0x10,
+ HDMI_A_HDCPCFG0_SYNCRICHECK_DISABLE = 0x00,
+ HDMI_A_HDCPCFG0_AVMUTE_MASK = 0x8,
+ HDMI_A_HDCPCFG0_AVMUTE_ENABLE = 0x8,
+ HDMI_A_HDCPCFG0_AVMUTE_DISABLE = 0x0,
+ HDMI_A_HDCPCFG0_RXDETECT_MASK = 0x4,
+ HDMI_A_HDCPCFG0_RXDETECT_ENABLE = 0x4,
+ HDMI_A_HDCPCFG0_RXDETECT_DISABLE = 0x0,
+ HDMI_A_HDCPCFG0_EN11FEATURE_MASK = 0x2,
+ HDMI_A_HDCPCFG0_EN11FEATURE_ENABLE = 0x2,
+ HDMI_A_HDCPCFG0_EN11FEATURE_DISABLE = 0x0,
+ HDMI_A_HDCPCFG0_HDMIDVI_MASK = 0x1,
+ HDMI_A_HDCPCFG0_HDMIDVI_HDMI = 0x1,
+ HDMI_A_HDCPCFG0_HDMIDVI_DVI = 0x0,
+
+ /* A_HDCPCFG1 field values */
+ HDMI_A_HDCPCFG1_DISSHA1CHECK_MASK = 0x8,
+ HDMI_A_HDCPCFG1_DISSHA1CHECK_DISABLE = 0x8,
+ HDMI_A_HDCPCFG1_DISSHA1CHECK_ENABLE = 0x0,
+ HDMI_A_HDCPCFG1_PH2UPSHFTENC_MASK = 0x4,
+ HDMI_A_HDCPCFG1_PH2UPSHFTENC_ENABLE = 0x4,
+ HDMI_A_HDCPCFG1_PH2UPSHFTENC_DISABLE = 0x0,
+ HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK = 0x2,
+ HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE = 0x2,
+ HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_ENABLE = 0x0,
+ HDMI_A_HDCPCFG1_SWRESET_MASK = 0x1,
+ HDMI_A_HDCPCFG1_SWRESET_ASSERT = 0x0,
+
+ /* A_VIDPOLCFG field values */
+ HDMI_A_VIDPOLCFG_UNENCRYPTCONF_MASK = 0x60,
+ HDMI_A_VIDPOLCFG_UNENCRYPTCONF_OFFSET = 5,
+ HDMI_A_VIDPOLCFG_DATAENPOL_MASK = 0x10,
+ HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH = 0x10,
+ HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW = 0x0,
+ HDMI_A_VIDPOLCFG_VSYNCPOL_MASK = 0x8,
+ HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_HIGH = 0x8,
+ HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_LOW = 0x0,
+ HDMI_A_VIDPOLCFG_HSYNCPOL_MASK = 0x2,
+ HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_HIGH = 0x2,
+ HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_LOW = 0x0,
+
+ /* I2CM_OPERATION field values */
+ HDMI_I2CM_OPERATION_BUSCLEAR = 0x20,
+ HDMI_I2CM_OPERATION_WRITE = 0x10,
+ HDMI_I2CM_OPERATION_READ_EXT = 0x2,
+ HDMI_I2CM_OPERATION_READ = 0x1,
+
+ /* I2CM_INT field values */
+ HDMI_I2CM_INT_DONE_POL = 0x8,
+ HDMI_I2CM_INT_DONE_MASK = 0x4,
+
+ /* I2CM_CTLINT field values */
+ HDMI_I2CM_CTLINT_NAC_POL = 0x80,
+ HDMI_I2CM_CTLINT_NAC_MASK = 0x40,
+ HDMI_I2CM_CTLINT_ARB_POL = 0x8,
+ HDMI_I2CM_CTLINT_ARB_MASK = 0x4,
+};
+
+/*
+ * HDMI 3D TX PHY registers
+ */
+#define HDMI_3D_TX_PHY_PWRCTRL 0x00
+#define HDMI_3D_TX_PHY_SERDIVCTRL 0x01
+#define HDMI_3D_TX_PHY_SERCKCTRL 0x02
+#define HDMI_3D_TX_PHY_SERCKKILLCTRL 0x03
+#define HDMI_3D_TX_PHY_TXRESCTRL 0x04
+#define HDMI_3D_TX_PHY_CKCALCTRL 0x05
+#define HDMI_3D_TX_PHY_CPCE_CTRL 0x06
+#define HDMI_3D_TX_PHY_TXCLKMEASCTRL 0x07
+#define HDMI_3D_TX_PHY_TXMEASCTRL 0x08
+#define HDMI_3D_TX_PHY_CKSYMTXCTRL 0x09
+#define HDMI_3D_TX_PHY_CMPSEQCTRL 0x0a
+#define HDMI_3D_TX_PHY_CMPPWRCTRL 0x0b
+#define HDMI_3D_TX_PHY_CMPMODECTRL 0x0c
+#define HDMI_3D_TX_PHY_MEASCTRL 0x0d
+#define HDMI_3D_TX_PHY_VLEVCTRL 0x0e
+#define HDMI_3D_TX_PHY_D2ACTRL 0x0f
+#define HDMI_3D_TX_PHY_CURRCTRL 0x10
+#define HDMI_3D_TX_PHY_DRVANACTRL 0x11
+#define HDMI_3D_TX_PHY_PLLMEASCTRL 0x12
+#define HDMI_3D_TX_PHY_PLLPHBYCTRL 0x13
+#define HDMI_3D_TX_PHY_GRP_CTRL 0x14
+#define HDMI_3D_TX_PHY_GMPCTRL 0x15
+#define HDMI_3D_TX_PHY_MPLLMEASCTRL 0x16
+#define HDMI_3D_TX_PHY_MSM_CTRL 0x17
+#define HDMI_3D_TX_PHY_SCRPB_STATUS 0x18
+#define HDMI_3D_TX_PHY_TXTERM 0x19
+#define HDMI_3D_TX_PHY_PTRPT_ENBL 0x1a
+#define HDMI_3D_TX_PHY_PATTERNGEN 0x1b
+#define HDMI_3D_TX_PHY_SDCAP_MODE 0x1c
+#define HDMI_3D_TX_PHY_SCOPEMODE 0x1d
+#define HDMI_3D_TX_PHY_DIGTXMODE 0x1e
+#define HDMI_3D_TX_PHY_STR_STATUS 0x1f
+#define HDMI_3D_TX_PHY_SCOPECNT0 0x20
+#define HDMI_3D_TX_PHY_SCOPECNT1 0x21
+#define HDMI_3D_TX_PHY_SCOPECNT2 0x22
+#define HDMI_3D_TX_PHY_SCOPECNTCLK 0x23
+#define HDMI_3D_TX_PHY_SCOPESAMPLE 0x24
+#define HDMI_3D_TX_PHY_SCOPECNTMSB01 0x25
+#define HDMI_3D_TX_PHY_SCOPECNTMSB2CK 0x26
+
+/* HDMI_3D_TX_PHY_CKCALCTRL values */
+#define HDMI_3D_TX_PHY_CKCALCTRL_OVERRIDE BIT(15)
+
+/* HDMI_3D_TX_PHY_MSM_CTRL values */
+#define HDMI_3D_TX_PHY_MSM_CTRL_MPLL_PH_SEL_CK BIT(13)
+#define HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_CLK_REF_MPLL (0 << 1)
+#define HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_OFF (1 << 1)
+#define HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_PCLK (2 << 1)
+#define HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_FB_CLK (3 << 1)
+#define HDMI_3D_TX_PHY_MSM_CTRL_SCOPE_CK_SEL BIT(0)
+
+/* HDMI_3D_TX_PHY_PTRPT_ENBL values */
+#define HDMI_3D_TX_PHY_PTRPT_ENBL_OVERRIDE BIT(15)
+#define HDMI_3D_TX_PHY_PTRPT_ENBL_PG_SKIP_BIT2 BIT(8)
+#define HDMI_3D_TX_PHY_PTRPT_ENBL_PG_SKIP_BIT1 BIT(7)
+#define HDMI_3D_TX_PHY_PTRPT_ENBL_PG_SKIP_BIT0 BIT(6)
+#define HDMI_3D_TX_PHY_PTRPT_ENBL_CK_REF_ENB BIT(5)
+#define HDMI_3D_TX_PHY_PTRPT_ENBL_RCAL_ENB BIT(4)
+#define HDMI_3D_TX_PHY_PTRPT_ENBL_TX_CLK_ALIGN_ENB BIT(3)
+#define HDMI_3D_TX_PHY_PTRPT_ENBL_TX_READY BIT(2)
+#define HDMI_3D_TX_PHY_PTRPT_ENBL_CKO_WORD_ENB BIT(1)
+#define HDMI_3D_TX_PHY_PTRPT_ENBL_REFCLK_ENB BIT(0)
+
+struct drm_display_info;
+struct drm_display_mode;
+struct drm_encoder;
+struct dw_hdmi;
+struct platform_device;
+
+enum {
+ DW_HDMI_RES_8,
+ DW_HDMI_RES_10,
+ DW_HDMI_RES_12,
+ DW_HDMI_RES_MAX,
+};
+
+struct dw_hdmi_audio_tmds_n {
+ unsigned long tmds;
+ unsigned int n_32k;
+ unsigned int n_44k1;
+ unsigned int n_48k;
+};
+
+enum dw_hdmi_phy_type {
+ DW_HDMI_PHY_DWC_HDMI_TX_PHY = 0x00,
+ DW_HDMI_PHY_DWC_MHL_PHY_HEAC = 0xb2,
+ DW_HDMI_PHY_DWC_MHL_PHY = 0xc2,
+ DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY_HEAC = 0xe2,
+ DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY = 0xf2,
+ DW_HDMI_PHY_DWC_HDMI20_TX_PHY = 0xf3,
+ DW_HDMI_PHY_VENDOR_PHY = 0xfe,
+};
+
+enum supported_eotf_type {
+ TRADITIONAL_GAMMA_SDR = 0,
+ TRADITIONAL_GAMMA_HDR,
+ SMPTE_ST2084,
+ HLG,
+ FUTURE_EOTF
+};
+
+struct dw_hdmi_mpll_config {
+ unsigned long mpixelclock;
+ struct {
+ u16 cpce;
+ u16 gmp;
+ } res[DW_HDMI_RES_MAX];
+};
+
+struct dw_hdmi_curr_ctrl {
+ unsigned long mpixelclock;
+ u16 curr[DW_HDMI_RES_MAX];
+};
+
+struct dw_hdmi_phy_config {
+ unsigned long mpixelclock;
+ u16 sym_ctr; /*clock symbol and transmitter control*/
+ u16 term; /*transmission termination value*/
+ u16 vlev_ctr; /* voltage level control */
+};
+
+struct dw_hdmi_phy_ops {
+ int (*init)(struct dw_hdmi *hdmi, void *data,
+ const struct drm_display_info *display,
+ const struct drm_display_mode *mode);
+ void (*disable)(struct dw_hdmi *hdmi, void *data);
+ enum drm_connector_status (*read_hpd)(struct dw_hdmi *hdmi, void *data);
+ void (*update_hpd)(struct dw_hdmi *hdmi, void *data, bool force,
+ bool disabled, bool rxsense);
+ void (*setup_hpd)(struct dw_hdmi *hdmi, void *data);
+};
+
+struct dw_hdmi_property_ops {
+ void (*attatch_properties)(struct drm_connector *connector,
+ unsigned int color, int version, void *data);
+ void (*destroy_properties)(struct drm_connector *connector, void *data);
+ int (*set_property)(struct drm_connector *connector,
+ struct drm_connector_state *state,
+ struct drm_property *property, u64 val, void *data);
+ int (*get_property)(struct drm_connector *connector,
+ const struct drm_connector_state *state,
+ struct drm_property *property, u64 *val,
+ void *data);
+};
+
+struct dw_hdmi_plat_data {
+ struct regmap *regm;
+ unsigned long input_bus_encoding;
+ bool use_drm_infoframe;
+ bool ycbcr_420_allowed;
+ unsigned int output_port;
+ /*
+ ¦* Private data passed to all the .mode_valid() and .configure_phy()
+ ¦* callback functions.
+ ¦*/
+ void *priv_data;
+
+ /* Platform-specific mode validation (optional). */
+ enum drm_mode_status (*mode_valid)(struct dw_hdmi *hdmi, void *data,
+ const struct drm_display_info *info,
+ const struct drm_display_mode *mode);
+
+ /* Vendor PHY support */
+ const struct dw_hdmi_phy_ops *phy_ops;
+ const char *phy_name;
+ void *phy_data;
+ unsigned int phy_force_vendor;
+
+ /* Synopsys PHY support */
+ const struct dw_hdmi_mpll_config *mpll_cfg;
+ const struct dw_hdmi_curr_ctrl *cur_ctr;
+ const struct dw_hdmi_phy_config *phy_config;
+ int (*configure_phy)(struct dw_hdmi *hdmi, void *data,
+ unsigned long mpixelclock);
+ unsigned long (*get_input_bus_format)(void *data);
+ unsigned long (*get_output_bus_format)(void *data);
+ unsigned long (*get_enc_in_encoding)(void *data);
+ unsigned long (*get_enc_out_encoding)(void *data);
+
+ /* Vendor Property support */
+ const struct dw_hdmi_property_ops *property_ops;
+
+ unsigned int disable_cec : 1;
+};
+
+struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
+ const struct dw_hdmi_plat_data *plat_data);
+void dw_hdmi_remove(struct dw_hdmi *hdmi);
+void dw_hdmi_unbind(struct dw_hdmi *hdmi);
+struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev,
+ struct drm_encoder *encoder,
+ const struct dw_hdmi_plat_data *plat_data);
+
+void dw_hdmi_suspend(struct dw_hdmi *hdmi);
+void dw_hdmi_resume(struct dw_hdmi *hdmi);
+
+void dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense);
+
+int dw_hdmi_set_plugged_cb(struct dw_hdmi *hdmi, hdmi_codec_plugged_cb fn,
+ struct device *codec_dev);
+void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate);
+void dw_hdmi_set_channel_count(struct dw_hdmi *hdmi, unsigned int cnt);
+void dw_hdmi_set_channel_status(struct dw_hdmi *hdmi, u8 *channel_status);
+void dw_hdmi_set_channel_allocation(struct dw_hdmi *hdmi, unsigned int ca);
+void dw_hdmi_audio_enable(struct dw_hdmi *hdmi);
+void dw_hdmi_audio_disable(struct dw_hdmi *hdmi);
+void dw_hdmi_set_high_tmds_clock_ratio(struct dw_hdmi *hdmi,
+ const struct drm_display_info *display);
+
+/* PHY configuration */
+void dw_hdmi_phy_i2c_set_addr(struct dw_hdmi *hdmi, u8 address);
+void dw_hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
+ unsigned char addr);
+
+void dw_hdmi_phy_gen2_pddq(struct dw_hdmi *hdmi, u8 enable);
+void dw_hdmi_phy_gen2_txpwron(struct dw_hdmi *hdmi, u8 enable);
+void dw_hdmi_phy_reset(struct dw_hdmi *hdmi);
+
+enum drm_connector_status dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi,
+ void *data);
+void dw_hdmi_phy_update_hpd(struct dw_hdmi *hdmi, void *data, bool force,
+ bool disabled, bool rxsense);
+void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi, void *data);
+
+void dw_hdmi_enable_video(struct dw_hdmi *hdmi);
+void dw_hdmi_disable_video(struct dw_hdmi *hdmi);
+#endif /* __DW_HDMI_H__ */
diff --git a/drivers/gpu/drm/eswin/dw_hdmi_audio.h b/drivers/gpu/drm/eswin/dw_hdmi_audio.h
new file mode 100644
index 000000000000..f72d27208ebe
--- /dev/null
+++ b/drivers/gpu/drm/eswin/dw_hdmi_audio.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef DW_HDMI_AUDIO_H
+#define DW_HDMI_AUDIO_H
+
+struct dw_hdmi;
+
+struct dw_hdmi_audio_data {
+ phys_addr_t phys;
+ void __iomem *base;
+ int irq;
+ struct dw_hdmi *hdmi;
+ u8 *(*get_eld)(struct dw_hdmi *hdmi);
+};
+
+struct dw_hdmi_i2s_audio_data {
+ struct dw_hdmi *hdmi;
+
+ void (*write)(struct dw_hdmi *hdmi, u8 val, int offset);
+ u8 (*read)(struct dw_hdmi *hdmi, int offset);
+ u8 *(*get_eld)(struct dw_hdmi *hdmi);
+};
+
+#endif
diff --git a/drivers/gpu/drm/eswin/dw_hdmi_cec.c b/drivers/gpu/drm/eswin/dw_hdmi_cec.c
new file mode 100644
index 000000000000..42c57cf1dbc0
--- /dev/null
+++ b/drivers/gpu/drm/eswin/dw_hdmi_cec.c
@@ -0,0 +1,334 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Designware HDMI CEC driver
+ *
+ * Copyright (C) 2015-2017 Russell King.
+ */
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <drm/drm_edid.h>
+
+#include <media/cec.h>
+#include <media/cec-notifier.h>
+
+#include "dw_hdmi_cec.h"
+
+#define CEC_IO_ADDR 0x516000fc
+
+enum {
+ HDMI_IH_CEC_STAT0 = 0x0106,
+ HDMI_IH_MUTE_CEC_STAT0 = 0x0186,
+
+ HDMI_CEC_CTRL = 0x7d00,
+ CEC_CTRL_START = BIT(0),
+ CEC_CTRL_FRAME_TYP = 3 << 1,
+ CEC_CTRL_RETRY = 0 << 1,
+ CEC_CTRL_NORMAL = 1 << 1,
+ CEC_CTRL_IMMED = 2 << 1,
+
+ HDMI_CEC_STAT = 0x7d01,
+ CEC_STAT_DONE = BIT(0),
+ CEC_STAT_EOM = BIT(1),
+ CEC_STAT_NACK = BIT(2),
+ CEC_STAT_ARBLOST = BIT(3),
+ CEC_STAT_ERROR_INIT = BIT(4),
+ CEC_STAT_ERROR_FOLL = BIT(5),
+ CEC_STAT_WAKEUP = BIT(6),
+
+ HDMI_CEC_MASK = 0x7d02,
+ HDMI_CEC_POLARITY = 0x7d03,
+ HDMI_CEC_INT = 0x7d04,
+ HDMI_CEC_ADDR_L = 0x7d05,
+ HDMI_CEC_ADDR_H = 0x7d06,
+ HDMI_CEC_TX_CNT = 0x7d07,
+ HDMI_CEC_RX_CNT = 0x7d08,
+ HDMI_CEC_TX_DATA0 = 0x7d10,
+ HDMI_CEC_RX_DATA0 = 0x7d20,
+ HDMI_CEC_LOCK = 0x7d30,
+ HDMI_CEC_WKUPCTRL = 0x7d31,
+};
+
+struct dw_hdmi_cec {
+ struct dw_hdmi *hdmi;
+ const struct dw_hdmi_cec_ops *ops;
+ u32 addresses;
+ struct cec_adapter *adap;
+ struct cec_msg rx_msg;
+ unsigned int tx_status;
+ bool tx_done;
+ bool rx_done;
+ struct cec_notifier *notify;
+ int irq;
+};
+
+static void dw_hdmi_write(struct dw_hdmi_cec *cec, u8 val, int offset)
+{
+ cec->ops->write(cec->hdmi, val, offset);
+}
+
+static u8 dw_hdmi_read(struct dw_hdmi_cec *cec, int offset)
+{
+ return cec->ops->read(cec->hdmi, offset);
+}
+
+static int dw_hdmi_cec_log_addr(struct cec_adapter *adap, u8 logical_addr)
+{
+ struct dw_hdmi_cec *cec = cec_get_drvdata(adap);
+
+ if (logical_addr == CEC_LOG_ADDR_INVALID)
+ cec->addresses = 0;
+ else
+ cec->addresses |= BIT(logical_addr) | BIT(15);
+
+ dw_hdmi_write(cec, cec->addresses & 255, HDMI_CEC_ADDR_L);
+ dw_hdmi_write(cec, cec->addresses >> 8, HDMI_CEC_ADDR_H);
+
+ return 0;
+}
+
+static int dw_hdmi_cec_transmit(struct cec_adapter *adap, u8 attempts,
+ u32 signal_free_time, struct cec_msg *msg)
+{
+ struct dw_hdmi_cec *cec = cec_get_drvdata(adap);
+ unsigned int i, ctrl;
+
+ switch (signal_free_time) {
+ case CEC_SIGNAL_FREE_TIME_RETRY:
+ ctrl = CEC_CTRL_RETRY;
+ break;
+ case CEC_SIGNAL_FREE_TIME_NEW_INITIATOR:
+ default:
+ ctrl = CEC_CTRL_NORMAL;
+ break;
+ case CEC_SIGNAL_FREE_TIME_NEXT_XFER:
+ ctrl = CEC_CTRL_IMMED;
+ break;
+ }
+
+ for (i = 0; i < msg->len; i++)
+ dw_hdmi_write(cec, msg->msg[i], HDMI_CEC_TX_DATA0 + i);
+
+ dw_hdmi_write(cec, msg->len, HDMI_CEC_TX_CNT);
+ dw_hdmi_write(cec, ctrl | CEC_CTRL_START, HDMI_CEC_CTRL);
+
+ return 0;
+}
+
+static irqreturn_t dw_hdmi_cec_hardirq(int irq, void *data)
+{
+ struct cec_adapter *adap = data;
+ struct dw_hdmi_cec *cec = cec_get_drvdata(adap);
+ unsigned int stat = dw_hdmi_read(cec, HDMI_IH_CEC_STAT0);
+ irqreturn_t ret = IRQ_HANDLED;
+
+ if (stat == 0)
+ return IRQ_NONE;
+
+ dw_hdmi_write(cec, stat, HDMI_IH_CEC_STAT0);
+
+ if (stat & CEC_STAT_ERROR_INIT) {
+ cec->tx_status = CEC_TX_STATUS_ERROR;
+ cec->tx_done = true;
+ ret = IRQ_WAKE_THREAD;
+ } else if (stat & CEC_STAT_DONE) {
+ cec->tx_status = CEC_TX_STATUS_OK;
+ cec->tx_done = true;
+ ret = IRQ_WAKE_THREAD;
+ } else if (stat & CEC_STAT_NACK) {
+ cec->tx_status = CEC_TX_STATUS_NACK;
+ cec->tx_done = true;
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ if (stat & CEC_STAT_EOM) {
+ unsigned int len, i;
+
+ len = dw_hdmi_read(cec, HDMI_CEC_RX_CNT);
+ if (len > sizeof(cec->rx_msg.msg))
+ len = sizeof(cec->rx_msg.msg);
+
+ for (i = 0; i < len; i++)
+ cec->rx_msg.msg[i] =
+ dw_hdmi_read(cec, HDMI_CEC_RX_DATA0 + i);
+
+ dw_hdmi_write(cec, 0, HDMI_CEC_LOCK);
+
+ cec->rx_msg.len = len;
+ smp_wmb();
+ cec->rx_done = true;
+
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ return ret;
+}
+
+static irqreturn_t dw_hdmi_cec_thread(int irq, void *data)
+{
+ struct cec_adapter *adap = data;
+ struct dw_hdmi_cec *cec = cec_get_drvdata(adap);
+
+ if (cec->tx_done) {
+ cec->tx_done = false;
+ cec_transmit_attempt_done(adap, cec->tx_status);
+ }
+ if (cec->rx_done) {
+ cec->rx_done = false;
+ smp_rmb();
+ cec_received_msg(adap, &cec->rx_msg);
+ }
+ return IRQ_HANDLED;
+}
+
+static int dw_hdmi_cec_enable(struct cec_adapter *adap, bool enable)
+{
+ struct dw_hdmi_cec *cec = cec_get_drvdata(adap);
+
+ if (!enable) {
+ dw_hdmi_write(cec, ~0, HDMI_CEC_MASK);
+ dw_hdmi_write(cec, ~0, HDMI_IH_MUTE_CEC_STAT0);
+ dw_hdmi_write(cec, 0, HDMI_CEC_POLARITY);
+
+ cec->ops->disable(cec->hdmi);
+ } else {
+ unsigned int irqs;
+
+ dw_hdmi_write(cec, 0, HDMI_CEC_CTRL);
+ dw_hdmi_write(cec, ~0, HDMI_IH_CEC_STAT0);
+ dw_hdmi_write(cec, 0, HDMI_CEC_LOCK);
+
+ dw_hdmi_cec_log_addr(cec->adap, CEC_LOG_ADDR_INVALID);
+
+ cec->ops->enable(cec->hdmi);
+
+ irqs = CEC_STAT_ERROR_INIT | CEC_STAT_NACK | CEC_STAT_EOM |
+ CEC_STAT_DONE;
+ dw_hdmi_write(cec, irqs, HDMI_CEC_POLARITY);
+ dw_hdmi_write(cec, ~irqs, HDMI_CEC_MASK);
+ dw_hdmi_write(cec, ~irqs, HDMI_IH_MUTE_CEC_STAT0);
+ }
+ return 0;
+}
+
+static const struct cec_adap_ops dw_hdmi_cec_ops = {
+ .adap_enable = dw_hdmi_cec_enable,
+ .adap_log_addr = dw_hdmi_cec_log_addr,
+ .adap_transmit = dw_hdmi_cec_transmit,
+};
+
+static void dw_hdmi_cec_del(void *data)
+{
+ struct dw_hdmi_cec *cec = data;
+
+ cec_delete_adapter(cec->adap);
+}
+
+static int dw_hdmi_cec_probe(struct platform_device *pdev)
+{
+ struct dw_hdmi_cec_data *data = dev_get_platdata(&pdev->dev);
+ struct dw_hdmi_cec *cec;
+ int ret;
+ void __iomem *cec_io_base;
+ int reg_val;
+
+ if (!data)
+ return -ENXIO;
+
+ /*
+ * Our device is just a convenience - we want to link to the real
+ * hardware device here, so that userspace can see the association
+ * between the HDMI hardware and its associated CEC chardev.
+ */
+ cec = devm_kzalloc(&pdev->dev, sizeof(*cec), GFP_KERNEL);
+ if (!cec)
+ return -ENOMEM;
+
+ cec->irq = data->irq;
+ cec->ops = data->ops;
+ cec->hdmi = data->hdmi;
+
+ platform_set_drvdata(pdev, cec);
+
+ dw_hdmi_write(cec, 0, HDMI_CEC_TX_CNT);
+ dw_hdmi_write(cec, ~0, HDMI_CEC_MASK);
+ dw_hdmi_write(cec, ~0, HDMI_IH_MUTE_CEC_STAT0);
+ dw_hdmi_write(cec, 0, HDMI_CEC_POLARITY);
+
+ cec->adap =
+ cec_allocate_adapter(&dw_hdmi_cec_ops, cec, "dw_hdmi",
+ CEC_CAP_DEFAULTS | CEC_CAP_CONNECTOR_INFO,
+ CEC_MAX_LOG_ADDRS);
+ if (IS_ERR(cec->adap))
+ return PTR_ERR(cec->adap);
+
+ /* override the module pointer */
+ cec->adap->owner = THIS_MODULE;
+
+ ret = devm_add_action_or_reset(&pdev->dev, dw_hdmi_cec_del, cec);
+ if (ret)
+ return ret;
+
+ ret = devm_request_threaded_irq(&pdev->dev, cec->irq,
+ dw_hdmi_cec_hardirq, dw_hdmi_cec_thread,
+ IRQF_SHARED, "dw-hdmi-cec", cec->adap);
+ if (ret < 0)
+ return ret;
+
+ cec->notify = cec_notifier_cec_adap_register(pdev->dev.parent, NULL,
+ cec->adap);
+ if (!cec->notify)
+ return -ENOMEM;
+
+ ret = cec_register_adapter(cec->adap, pdev->dev.parent);
+ if (ret < 0) {
+ cec_notifier_cec_adap_unregister(cec->notify, cec->adap);
+ return ret;
+ }
+
+ /*
+ * CEC documentation says we must not call cec_delete_adapter
+ * after a successful call to cec_register_adapter().
+ */
+ devm_remove_action(&pdev->dev, dw_hdmi_cec_del, cec);
+
+ cec_io_base = devm_ioremap(&pdev->dev, CEC_IO_ADDR, 4);
+ if (!cec_io_base) {
+ dev_err(&pdev->dev, "failed to remap cec io ctl\n");
+ return -ENOMEM;
+ }
+
+ /* set the cec io to output */
+ reg_val = readl((char *)cec_io_base);
+ reg_val |= 0x1;
+ writel(reg_val, (char *)cec_io_base);
+
+ return 0;
+}
+
+static int dw_hdmi_cec_remove(struct platform_device *pdev)
+{
+ struct dw_hdmi_cec *cec = platform_get_drvdata(pdev);
+
+ cec_notifier_cec_adap_unregister(cec->notify, cec->adap);
+ cec_unregister_adapter(cec->adap);
+
+ return 0;
+}
+
+struct platform_driver dw_hdmi_cec_driver = {
+ .probe = dw_hdmi_cec_probe,
+ .remove = dw_hdmi_cec_remove,
+ .driver = {
+ .name = "dw-hdmi-cec",
+ },
+};
+//module_platform_driver(dw_hdmi_cec_driver);
+
+MODULE_DESCRIPTION("Synopsys Designware HDMI CEC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS(PLATFORM_MODULE_PREFIX "dw-hdmi-cec");
diff --git a/drivers/gpu/drm/eswin/dw_hdmi_cec.h b/drivers/gpu/drm/eswin/dw_hdmi_cec.h
new file mode 100644
index 000000000000..9d2547f1ea16
--- /dev/null
+++ b/drivers/gpu/drm/eswin/dw_hdmi_cec.h
@@ -0,0 +1,18 @@
+#ifndef DW_HDMI_CEC_H
+#define DW_HDMI_CEC_H
+
+struct dw_hdmi;
+
+struct dw_hdmi_cec_ops {
+ void (*write)(struct dw_hdmi *hdmi, u8 val, int offset);
+ u8 (*read)(struct dw_hdmi *hdmi, int offset);
+ void (*enable)(struct dw_hdmi *hdmi);
+ void (*disable)(struct dw_hdmi *hdmi);
+};
+
+struct dw_hdmi_cec_data {
+ struct dw_hdmi *hdmi;
+ const struct dw_hdmi_cec_ops *ops;
+ int irq;
+};
+#endif
diff --git a/drivers/gpu/drm/eswin/dw_hdmi_hdcp.c b/drivers/gpu/drm/eswin/dw_hdmi_hdcp.c
new file mode 100644
index 000000000000..1b4e7433b718
--- /dev/null
+++ b/drivers/gpu/drm/eswin/dw_hdmi_hdcp.c
@@ -0,0 +1,1055 @@
+/*
+ * Copyright (C) ESWIN Electronics Co.Ltd
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/hdmi.h>
+#include <linux/iopoll.h>
+#include <linux/irq.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/spinlock.h>
+#include <linux/soc/eswin/eswin_vendor_storage.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include "dw_hdmi.h"
+#include "dw_hdmi_hdcp.h"
+
+#define HDCP_KEY_PATH "/usr/hdcp1.4_key/Tx_A2_TestDPK_encrypted"
+
+#define HDCP_KEY_SIZE 308
+#define HDCP_KEY_SEED_SIZE 2
+
+#define KSV_LEN 5
+#define HEADER 10
+#define SHAMAX 20
+
+#define MAX_DOWNSTREAM_DEVICE_NUM 5
+#define DPK_WR_OK_TIMEOUT_US 30000
+#define HDMI_HDCP1X_ID 5
+
+/* HDCP DCP KEY & SEED */
+const u8 hdcp_const_data[320] = {
+ /* 0 1 2 3 4 5 6 */
+ 0x00,
+ 0x00,
+ 0xf0,
+ 0xff,
+ 0xff,
+ 0x00,
+ 0x00,
+ 0x00, //KSV
+ 0x91,
+ 0x71,
+ 0x7,
+ 0x42,
+ 0x86,
+ 0xC1,
+ 0xD1,
+ 0x89,
+ 0x0E,
+ 0x2D,
+ 0xFF,
+ 0x92,
+ 0x95,
+ 0x28,
+ 0xF4,
+ 0x7D,
+ 0x7B,
+ 0x1F,
+ 0x2A,
+ 0xD9,
+ 0xBB,
+ 0xE4,
+ 0xFD,
+ 0x10,
+ 0x18,
+ 0xAA,
+ 0xFB,
+ 0x99,
+ 0x5A,
+ 0x83,
+ 0x97,
+ 0xD5,
+ 0xDA,
+ 0x85,
+ 0x2D,
+ 0x52,
+ 0x8B,
+ 0xB5,
+ 0xB2,
+ 0x49,
+ 0xDC,
+ 0x64,
+ 0xC6,
+ 0x62,
+ 0xF0,
+ 0xDB,
+ 0xAA,
+ 0x48,
+ 0x2E,
+ 0x84,
+ 0xAD,
+ 0x21,
+ 0xCD,
+ 0xB9,
+ 0xD6,
+ 0x47,
+ 0xC7,
+ 0xD7,
+ 0xD1,
+ 0x9F,
+ 0xD4,
+ 0xB1,
+ 0x29,
+ 0x4E,
+ 0x98,
+ 0xC6,
+ 0xAE,
+ 0xA4,
+ 0xF5,
+ 0xA6,
+ 0xFE,
+ 0x68,
+ 0x3D,
+ 0x43,
+ 0x97,
+ 0x7B,
+ 0x52,
+ 0xC7,
+ 0xA1,
+ 0x65,
+ 0x7B,
+ 0xF9,
+ 0x8C,
+ 0xCC,
+ 0x20,
+ 0x8C,
+ 0xCB,
+ 0x2F,
+ 0x7D,
+ 0xFA,
+ 0xC5,
+ 0x80,
+ 0xD8,
+ 0xDB,
+ 0x5A,
+ 0x72,
+ 0x2D,
+ 0xE1,
+ 0xA6,
+ 0x79,
+ 0xF4,
+ 0xAE,
+ 0x96,
+ 0x1D,
+ 0xE8,
+ 0x28,
+ 0x85,
+ 0x5F,
+ 0xBD,
+ 0x64,
+ 0xF8,
+ 0xBF,
+ 0x7A,
+ 0xE7,
+ 0xFF,
+ 0xBC,
+ 0x1F,
+ 0xC6,
+ 0x75,
+ 0x56,
+ 0xB9,
+ 0xF9,
+ 0x0F,
+ 0x36,
+ 0x29,
+ 0x5A,
+ 0x3B,
+ 0xF3,
+ 0x76,
+ 0x7B,
+ 0x8B,
+ 0xF8,
+ 0xFD,
+ 0x13,
+ 0x80,
+ 0x49,
+ 0xAB,
+ 0x5C,
+ 0x12,
+ 0x63,
+ 0xB9,
+ 0xE7,
+ 0x91,
+ 0x2A,
+ 0xBA,
+ 0x82,
+ 0xF3,
+ 0xCD,
+ 0xFA,
+ 0xFB,
+ 0x4E,
+ 0xA7,
+ 0xE1,
+ 0xBD,
+ 0x8B,
+ 0xC3,
+ 0x24,
+ 0xEC,
+ 0x31,
+ 0xBC,
+ 0x1,
+ 0xB1,
+ 0xCE,
+ 0x9A,
+ 0x4,
+ 0x9C,
+ 0x69,
+ 0x5D,
+ 0xBA,
+ 0x3C,
+ 0xF7,
+ 0x97,
+ 0x50,
+ 0x88,
+ 0xE2,
+ 0xA2,
+ 0xE1,
+ 0x3,
+ 0xDB,
+ 0x39,
+ 0xDD,
+ 0x93,
+ 0x0A,
+ 0x24,
+ 0x5C,
+ 0x6E,
+ 0x17,
+ 0xE9,
+ 0x1,
+ 0x4C,
+ 0x25,
+ 0xF5,
+ 0x9,
+ 0x24,
+ 0xC6,
+ 0x91,
+ 0xC6,
+ 0x6A,
+ 0x7A,
+ 0x40,
+ 0x89,
+ 0x62,
+ 0x7F,
+ 0xED,
+ 0x6B,
+ 0x8E,
+ 0x5F,
+ 0x79,
+ 0xAD,
+ 0xF2,
+ 0x50,
+ 0x59,
+ 0xC4,
+ 0x11,
+ 0x2E,
+ 0x1,
+ 0xC2,
+ 0xDC,
+ 0x8,
+ 0xCE,
+ 0xDC,
+ 0x51,
+ 0x14,
+ 0xF4,
+ 0x8C,
+ 0x3D,
+ 0x9E,
+ 0xB7,
+ 0x16,
+ 0xB3,
+ 0x9C,
+ 0xF3,
+ 0x55,
+ 0xC0,
+ 0xCE,
+ 0x74,
+ 0x5B,
+ 0x19,
+ 0x4E,
+ 0xF5,
+ 0x39,
+ 0x37,
+ 0xA6,
+ 0xEA,
+ 0xB5,
+ 0x20,
+ 0xBF,
+ 0xD7,
+ 0x79,
+ 0x24,
+ 0xE2,
+ 0x8D,
+ 0x13,
+ 0xBC,
+ 0x38,
+ 0x10,
+ 0x60,
+ 0x93,
+ 0xAE,
+ 0x70,
+ 0xA9,
+ 0x66,
+ 0x81,
+ 0xF3,
+ 0x19,
+ 0xEC,
+ 0x45,
+ 0xEC,
+ 0xE5,
+ 0x5,
+ 0x47,
+ 0xE4,
+ 0x67,
+ 0x65,
+ 0x4C,
+ 0x62,
+ 0x1,
+ 0x98,
+ 0xA3,
+ 0x52,
+ //SHA1
+ 0x18,
+ 0xb4,
+ 0x70,
+ 0x59,
+ 0xfe,
+ 0x13,
+ 0x38,
+ 0xc4,
+ 0x15,
+ 0xae,
+ 0xf0,
+ 0x81,
+ 0xcb,
+ 0x96,
+ 0x27,
+ 0xe7,
+ 0xd9,
+ 0x7b,
+ 0xc5,
+ 0x27,
+ 0x20, //seed 0x2020
+ 0x20,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+};
+
+/* HDCP Registers */
+#define HDMI_A_KSVMEMCTRL 0x5016
+#define HDMI_HDCPREG_ANCONF 0x7805
+#define HDMI_HDCPREG_AN0 0x7806
+#define HDMI_HDCPREG_AN1 0x7807
+#define HDMI_HDCPREG_AN2 0x7808
+#define HDMI_HDCPREG_AN3 0x7809
+#define HDMI_HDCPREG_AN4 0x780a
+#define HDMI_HDCPREG_AN5 0x780b
+#define HDMI_HDCPREG_AN6 0x780c
+#define HDMI_HDCPREG_AN7 0x780d
+#define HDMI_HDCPREG_RMCTL 0x780e
+#define HDMI_HDCPREG_RMSTS 0x780f
+#define HDMI_HDCPREG_SEED0 0x7810
+#define HDMI_HDCPREG_SEED1 0x7811
+#define HDMI_HDCPREG_DPK0 0x7812
+#define HDMI_HDCPREG_DPK1 0x7813
+#define HDMI_HDCPREG_DPK2 0x7814
+#define HDMI_HDCPREG_DPK3 0x7815
+#define HDMI_HDCPREG_DPK4 0x7816
+#define HDMI_HDCPREG_DPK5 0x7817
+#define HDMI_HDCPREG_DPK6 0x7818
+#define HDMI_HDCP2REG_CTRL 0x7904
+#define HDMI_HDCP2REG_MASK 0x790c
+#define HDMI_HDCP2REG_MUTE 0x7912
+
+enum dw_hdmi_hdcp_state {
+ DW_HDCP_DISABLED,
+ DW_HDCP_AUTH_START,
+ DW_HDCP_AUTH_SUCCESS,
+ DW_HDCP_AUTH_FAIL,
+};
+
+enum {
+ DW_HDMI_HDCP_KSV_LEN = 8,
+ DW_HDMI_HDCP_SHA_LEN = 20,
+ DW_HDMI_HDCP_DPK_LEN = 280,
+ DW_HDMI_HDCP_KEY_LEN = 308,
+ DW_HDMI_HDCP_SEED_LEN = 2,
+};
+
+enum {
+ HDMI_MC_CLKDIS_HDCPCLK_MASK = 0x40,
+ HDMI_MC_CLKDIS_HDCPCLK_ENABLE = 0x00,
+
+ HDMI_A_SRMCTRL_SHA1_FAIL_MASK = 0X08,
+ HDMI_A_SRMCTRL_SHA1_FAIL_DISABLE = 0X00,
+ HDMI_A_SRMCTRL_SHA1_FAIL_ENABLE = 0X08,
+
+ HDMI_A_SRMCTRL_KSV_UPDATE_MASK = 0X04,
+ HDMI_A_SRMCTRL_KSV_UPDATE_DISABLE = 0X00,
+ HDMI_A_SRMCTRL_KSV_UPDATE_ENABLE = 0X04,
+
+ HDMI_A_SRMCTRL_KSV_MEM_REQ_MASK = 0X01,
+ HDMI_A_SRMCTRL_KSV_MEM_REQ_DISABLE = 0X00,
+ HDMI_A_SRMCTRL_KSV_MEM_REQ_ENABLE = 0X01,
+
+ HDMI_A_SRMCTRL_KSV_MEM_ACCESS_MASK = 0X02,
+ HDMI_A_SRMCTRL_KSV_MEM_ACCESS_DISABLE = 0X00,
+ HDMI_A_SRMCTRL_KSV_MEM_ACCESS_ENABLE = 0X02,
+
+ HDMI_A_SRM_BASE_MAX_DEVS_EXCEEDED = 0x80,
+ HDMI_A_SRM_BASE_DEVICE_COUNT = 0x7f,
+
+ HDMI_A_SRM_BASE_MAX_CASCADE_EXCEEDED = 0x08,
+
+ HDMI_A_APIINTSTAT_KSVSHA1_CALC_INT = 0x02,
+ HDMI_A_APIINTSTAT_KSVSHA1_CALC_DONE_INT = 0x20,
+ /* HDCPREG_RMSTS field values */
+ DPK_WR_OK_STS = 0x40,
+
+ HDMI_A_HDCP22_MASK = 0x40,
+
+ HDMI_HDCP2_OVR_EN_MASK = 0x02,
+ HDMI_HDCP2_OVR_ENABLE = 0x02,
+ HDMI_HDCP2_OVR_DISABLE = 0x00,
+
+ HDMI_HDCP2_FORCE_MASK = 0x04,
+ HDMI_HDCP2_FORCE_ENABLE = 0x04,
+ HDMI_HDCP2_FORCE_DISABLE = 0x00,
+ HDMI_A_KSVMEMCTRL_KSV_SHA1_STATUS = 0x08,
+};
+
+static struct dw_hdcp *g_hdcp;
+static int trytimes = 0;
+
+static void hdcp_modb(struct dw_hdcp *hdcp, u8 data, u8 mask, unsigned int reg)
+{
+ struct dw_hdmi *hdmi = hdcp->hdmi;
+ u8 val = hdcp->read(hdmi, reg) & ~mask;
+
+ val |= data & mask;
+ hdcp->write(hdmi, val, reg);
+}
+
+static int hdcp_load_keys_cb(struct dw_hdcp *hdcp)
+{
+ u32 size;
+ u8 hdcp_vendor_data[320];
+ int i;
+#if 0
+ int j;
+ struct file *fp;
+ loff_t pos = 0;
+ ssize_t nread;
+#endif
+ hdcp->keys = kmalloc(HDCP_KEY_SIZE, GFP_KERNEL);
+ if (!hdcp->keys)
+ return -ENOMEM;
+
+ hdcp->seeds = kmalloc(HDCP_KEY_SEED_SIZE, GFP_KERNEL);
+ if (!hdcp->seeds) {
+ kfree(hdcp->keys);
+ return -ENOMEM;
+ }
+#if 1
+ size = eswin_vendor_read(HDMI_HDCP1X_ID, hdcp_vendor_data, 314);
+
+ for (i = 0; i < sizeof(hdcp_vendor_data); i++)
+ hdcp_vendor_data[i] = hdcp_const_data[i];
+ size = 320;
+
+ if (size < (HDCP_KEY_SIZE + HDCP_KEY_SEED_SIZE)) {
+ dev_dbg(hdcp->dev, "HDCP: read size %d\n", size);
+ memset(hdcp->keys, 0, HDCP_KEY_SIZE);
+ memset(hdcp->seeds, 0, HDCP_KEY_SEED_SIZE);
+ } else {
+ memcpy(hdcp->keys, hdcp_vendor_data, HDCP_KEY_SIZE);
+ memcpy(hdcp->seeds, hdcp_vendor_data + HDCP_KEY_SIZE,
+ HDCP_KEY_SEED_SIZE);
+ }
+#else
+ fp = filp_open(HDCP_KEY_PATH, O_RDONLY, 0644);
+ if (IS_ERR(fp)) {
+ printk("Error, Tx_A2_TestDPK_encrypted.txt doesn't exist.\n");
+ return 0;
+ }
+
+ nread = kernel_read(fp, hdcp_vendor_data, sizeof(hdcp_vendor_data),
+ &pos);
+
+ if (nread != sizeof(hdcp_vendor_data)) {
+ printk("Error, failed to read %ld bytes to non volatile memory area,ret %ld\n",
+ sizeof(hdcp_vendor_data), nread);
+ return -EIO;
+ }
+
+ memcpy(hdcp->keys, hdcp_vendor_data, HDCP_KEY_SIZE);
+ memcpy(hdcp->seeds, hdcp_vendor_data + HDCP_KEY_SIZE,
+ HDCP_KEY_SEED_SIZE);
+
+ filp_close(fp, NULL);
+#endif
+
+ return 0;
+}
+
+static int dw_hdmi_hdcp_load_key(struct dw_hdcp *hdcp)
+{
+ int i, j;
+ int ret, val;
+ void __iomem *reg_rmsts_addr;
+ struct hdcp_keys *hdcp_keys;
+ struct dw_hdmi *hdmi = hdcp->hdmi;
+
+ if (!hdcp->keys) {
+ ret = hdcp_load_keys_cb(hdcp);
+ if (ret)
+ return ret;
+ }
+ hdcp_keys = hdcp->keys;
+
+ if (hdcp->reg_io_width == 4)
+ reg_rmsts_addr = hdcp->regs + (HDMI_HDCPREG_RMSTS << 2);
+ else if (hdcp->reg_io_width == 1)
+ reg_rmsts_addr = hdcp->regs + HDMI_HDCPREG_RMSTS;
+ else
+ return -EPERM;
+
+ /* Disable decryption logic */
+ hdcp->write(hdmi, 0, HDMI_HDCPREG_RMCTL);
+ ret = readx_poll_timeout(readl, reg_rmsts_addr, val,
+ val & DPK_WR_OK_STS, 1000,
+ DPK_WR_OK_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ hdcp->write(hdmi, 0, HDMI_HDCPREG_DPK6);
+ hdcp->write(hdmi, 0, HDMI_HDCPREG_DPK5);
+
+ /* The useful data in ksv should be 5 byte */
+ for (i = 4; i >= 0; i--)
+ hdcp->write(hdmi, hdcp_keys->KSV[i], HDMI_HDCPREG_DPK0 + i);
+ ret = readx_poll_timeout(readl, reg_rmsts_addr, val,
+ val & DPK_WR_OK_STS, 1000,
+ DPK_WR_OK_TIMEOUT_US);
+
+ if (ret)
+ return ret;
+
+ /* Enable decryption logic */
+ if (hdcp->seeds) {
+ hdcp->write(hdmi, 1, HDMI_HDCPREG_RMCTL);
+ hdcp->write(hdmi, hdcp->seeds[0], HDMI_HDCPREG_SEED1);
+ hdcp->write(hdmi, hdcp->seeds[1], HDMI_HDCPREG_SEED0);
+ } else {
+ hdcp->write(hdmi, 0, HDMI_HDCPREG_RMCTL);
+ }
+
+ /* Write encrypt device private key */
+ for (i = 0; i < DW_HDMI_HDCP_DPK_LEN - 6; i += 7) {
+ for (j = 6; j >= 0; j--)
+ hdcp->write(hdmi, hdcp_keys->devicekey[i + j],
+ HDMI_HDCPREG_DPK0 + j);
+ ret = readx_poll_timeout(readl, reg_rmsts_addr, val,
+ val & DPK_WR_OK_STS, 1000,
+ DPK_WR_OK_TIMEOUT_US);
+
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int dw_hdmi_hdcp1x_start(struct dw_hdcp *hdcp)
+{
+ struct dw_hdmi *hdmi = hdcp->hdmi;
+ int i;
+ int val;
+ u8 An[8];
+
+ if (!hdcp->enable)
+ return -EPERM;
+
+ if (hdcp->status == DW_HDCP_AUTH_START ||
+ hdcp->status == DW_HDCP_AUTH_SUCCESS)
+ return 0;
+
+ /* disable the pixel clock*/
+ dev_dbg(hdcp->dev, "start hdcp with disable hdmi pixel clock\n");
+ hdcp_modb(hdcp, HDMI_MC_CLKDIS_PIXELCLK_DISABLE,
+ HDMI_MC_CLKDIS_PIXELCLK_MASK, HDMI_MC_CLKDIS);
+
+ /* Update An */
+ get_random_bytes(&An, sizeof(An));
+ for (i = 0; i < 8; i++)
+ hdcp->write(hdmi, An[i], HDMI_HDCPREG_AN0 + i);
+
+ hdcp->write(hdmi, 0x01, HDMI_HDCPREG_ANCONF);
+
+ if (!(hdcp->read(hdmi, HDMI_HDCPREG_RMSTS) & 0x3f))
+ dw_hdmi_hdcp_load_key(hdcp);
+
+ if (hdcp->hdcp2) {
+ for (i = 0; i < 100; i++) {
+ if (hdcp->hdcp2->wait_hdcp2_reset) {
+ msleep(80);
+ } else {
+ break;
+ }
+ }
+ printk("wait_hdcp2_reset i = %d\n", i);
+ }
+ val = hdcp->read(hdmi, HDMI_HDCP2REG_CTRL);
+ dev_dbg(hdcp->dev, "before set HDMI_HDCP2REG_CTRL val = %d\n", val);
+
+ hdcp_modb(hdcp, HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE,
+ HDMI_FC_INVIDCONF_HDCP_KEEPOUT_MASK, HDMI_FC_INVIDCONF);
+
+ hdcp->remaining_times = hdcp->retry_times;
+ if (hdcp->read(hdmi, HDMI_CONFIG1_ID) & HDMI_A_HDCP22_MASK) {
+ hdcp_modb(hdcp,
+ HDMI_HDCP2_OVR_ENABLE | HDMI_HDCP2_FORCE_DISABLE,
+ HDMI_HDCP2_OVR_EN_MASK | HDMI_HDCP2_FORCE_MASK,
+ HDMI_HDCP2REG_CTRL);
+ hdcp->write(hdmi, 0xff, HDMI_HDCP2REG_MASK);
+ hdcp->write(hdmi, 0xff, HDMI_HDCP2REG_MUTE);
+ }
+
+ hdcp->write(hdmi, 0x40, HDMI_A_OESSWCFG);
+ hdcp_modb(hdcp,
+ HDMI_A_HDCPCFG0_BYPENCRYPTION_DISABLE |
+ HDMI_A_HDCPCFG0_EN11FEATURE_DISABLE |
+ HDMI_A_HDCPCFG0_SYNCRICHECK_ENABLE,
+ HDMI_A_HDCPCFG0_BYPENCRYPTION_MASK |
+ HDMI_A_HDCPCFG0_EN11FEATURE_MASK |
+ HDMI_A_HDCPCFG0_SYNCRICHECK_MASK,
+ HDMI_A_HDCPCFG0);
+
+ hdcp_modb(hdcp,
+ HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_ENABLE |
+ HDMI_A_HDCPCFG1_PH2UPSHFTENC_ENABLE,
+ HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK |
+ HDMI_A_HDCPCFG1_PH2UPSHFTENC_MASK,
+ HDMI_A_HDCPCFG1);
+
+ /* Reset HDCP Engine */
+ if (hdcp->read(hdmi, HDMI_MC_CLKDIS) & HDMI_MC_CLKDIS_HDCPCLK_MASK) {
+ hdcp_modb(hdcp, HDMI_A_HDCPCFG1_SWRESET_ASSERT,
+ HDMI_A_HDCPCFG1_SWRESET_MASK, HDMI_A_HDCPCFG1);
+ }
+
+ hdcp->write(hdmi, 0x00, HDMI_A_APIINTMSK);
+ hdcp_modb(hdcp, HDMI_A_HDCPCFG0_RXDETECT_ENABLE,
+ HDMI_A_HDCPCFG0_RXDETECT_MASK, HDMI_A_HDCPCFG0);
+
+ /*
+ * XXX: to sleep 500ms here between output hdmi and enable hdcpclk,
+ * otherwise hdcp auth fail when Connect to repeater
+ */
+ msleep(500);
+ hdcp_modb(hdcp, HDMI_MC_CLKDIS_HDCPCLK_ENABLE,
+ HDMI_MC_CLKDIS_HDCPCLK_MASK, HDMI_MC_CLKDIS);
+
+ hdcp->status = DW_HDCP_AUTH_START;
+ dev_info(hdcp->dev, "%s success\n", __func__);
+
+ /* enable the pixel clock*/
+ dev_dbg(hdcp->dev, "start hdcp with enable hdmi pixel clock\n");
+ hdcp_modb(hdcp, HDMI_MC_CLKDIS_PIXELCLK_ENABLE,
+ HDMI_MC_CLKDIS_PIXELCLK_MASK, HDMI_MC_CLKDIS);
+
+ return 0;
+}
+
+static int dw_hdmi_hdcp1x_stop(struct dw_hdcp *hdcp)
+{
+ struct dw_hdmi *hdmi = hdcp->hdmi;
+ u8 val;
+ bool phy_enable = false;
+
+ if (!hdcp->enable)
+ return -EPERM;
+
+ val = hdcp->read(hdmi, HDMI_PHY_CONF0);
+ if (val & HDMI_PHY_CONF0_GEN2_TXPWRON_MASK) {
+ phy_enable = true;
+ }
+
+ dev_dbg(hdcp->dev, "dw_hdmi_hdcp1x_stop\n");
+ if (phy_enable) {
+ dev_dbg(hdcp->dev, "stop hdcp with disable hdmi pixel clock\n");
+ hdcp_modb(hdcp, HDMI_MC_CLKDIS_PIXELCLK_DISABLE,
+ HDMI_MC_CLKDIS_PIXELCLK_MASK, HDMI_MC_CLKDIS);
+ }
+
+ hdcp_modb(hdcp, HDMI_MC_CLKDIS_HDCPCLK_DISABLE,
+ HDMI_MC_CLKDIS_HDCPCLK_MASK, HDMI_MC_CLKDIS);
+ hdcp->write(hdmi, 0xff, HDMI_A_APIINTMSK);
+
+ hdcp_modb(hdcp, HDMI_A_HDCPCFG0_RXDETECT_DISABLE,
+ HDMI_A_HDCPCFG0_RXDETECT_MASK, HDMI_A_HDCPCFG0);
+
+ hdcp_modb(hdcp,
+ HDMI_A_SRMCTRL_SHA1_FAIL_DISABLE |
+ HDMI_A_SRMCTRL_KSV_UPDATE_DISABLE,
+ HDMI_A_SRMCTRL_SHA1_FAIL_MASK |
+ HDMI_A_SRMCTRL_KSV_UPDATE_MASK,
+ HDMI_A_SRMCTRL);
+
+ hdcp->status = DW_HDCP_DISABLED;
+
+ if (phy_enable) {
+ dev_dbg(hdcp->dev, "stop hdcp with enable hdmi pixel clock\n");
+ hdcp_modb(hdcp, HDMI_MC_CLKDIS_PIXELCLK_ENABLE,
+ HDMI_MC_CLKDIS_PIXELCLK_MASK, HDMI_MC_CLKDIS);
+ }
+
+ return 0;
+}
+
+void dw_hdmi_hdcp2_init(struct dw_hdcp2 *hdcp2)
+{
+ if (g_hdcp)
+ g_hdcp->hdcp2 = hdcp2;
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_hdcp2_init);
+
+void dw_hdmi_hdcp2_remove(void)
+{
+ printk("func: %s; line: %d\n", __func__, __LINE__);
+ if (g_hdcp->hdcp2)
+ g_hdcp->hdcp2->stop();
+ g_hdcp->hdcp2 = NULL;
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_hdcp2_remove);
+
+void dw_hdmi_hdcp2_start(int enable)
+{
+ int val;
+
+ if (!(g_hdcp->hdcp2))
+ return;
+
+ dev_dbg(g_hdcp->dev, "%s enable = %d\n", __func__, enable);
+ if (enable == 0) {
+ hdcp_modb(g_hdcp,
+ HDMI_HDCP2_OVR_ENABLE | HDMI_HDCP2_FORCE_DISABLE,
+ HDMI_HDCP2_OVR_EN_MASK | HDMI_HDCP2_FORCE_MASK,
+ HDMI_HDCP2REG_CTRL);
+ hdcp_modb(g_hdcp, HDMI_MC_CLKDIS_HDCPCLK_DISABLE,
+ HDMI_MC_CLKDIS_HDCPCLK_MASK, HDMI_MC_CLKDIS);
+ } else if (enable == 1) {
+ hdcp_modb(g_hdcp, HDMI_MC_CLKDIS_HDCPCLK_ENABLE,
+ HDMI_MC_CLKDIS_HDCPCLK_MASK, HDMI_MC_CLKDIS);
+ hdcp_modb(g_hdcp,
+ HDMI_HDCP2_OVR_ENABLE | HDMI_HDCP2_FORCE_ENABLE,
+ HDMI_HDCP2_OVR_EN_MASK | HDMI_HDCP2_FORCE_MASK,
+ HDMI_HDCP2REG_CTRL);
+ hdcp_modb(g_hdcp, HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE,
+ HDMI_FC_INVIDCONF_HDCP_KEEPOUT_MASK,
+ HDMI_FC_INVIDCONF);
+ } else if (enable == 2) {
+ val = g_hdcp->read(g_hdcp->hdmi, HDMI_PHY_STAT0);
+ if (val & HDMI_PHY_HPD)
+ dw_hdmi_hdcp1x_start(g_hdcp);
+ } else if (enable == 3) {
+ if (g_hdcp->hdcp2 && g_hdcp->hdcp2->enable &&
+ (tv_hdmi_hdcp2_support(g_hdcp->hdmi) == 1)) {
+ if (g_hdcp->status != DW_HDCP_DISABLED)
+ dw_hdmi_hdcp1x_stop(g_hdcp);
+ g_hdcp->hdcp2->start();
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_hdcp2_start);
+
+static int dw_hdmi_hdcp_start(struct dw_hdcp *hdcp)
+{
+ if (hdcp->hdcp2 && hdcp->hdcp2->enable &&
+ (tv_hdmi_hdcp2_support(hdcp->hdmi) == 1)) {
+ // hdcp->hdcp2->start();
+ return 0;
+ }
+ return dw_hdmi_hdcp1x_start(hdcp);
+}
+
+static int dw_hdmi_hdcp_stop(struct dw_hdcp *hdcp)
+{
+ if (hdcp->hdcp2 && hdcp->hdcp2->hot_plug) {
+ // g_hdcp->hdcp2->stop();
+ printk("func: %s; line: %d\n", __func__, __LINE__);
+ }
+
+ return dw_hdmi_hdcp1x_stop(hdcp);
+}
+
+static void dw_hdmi_hdcp_isr(struct dw_hdcp *hdcp, int hdcp_int)
+{
+ struct dw_hdmi *hdmi = hdcp->hdmi;
+ int val;
+
+ dev_info(hdcp->dev, "hdcp_int is 0x%02x\n", hdcp_int);
+
+ if (hdcp_int & HDMI_A_APIINTSTAT_KSVSHA1_CALC_DONE_INT) {
+ dev_dbg(hdcp->dev, "hdcp sink is a repeater\n");
+ val = hdcp->read(hdmi, HDMI_A_KSVMEMCTRL);
+ if (val | HDMI_A_KSVMEMCTRL_KSV_SHA1_STATUS) {
+ dev_dbg(hdcp->dev,
+ "hdcp verifivation failed, waiting hdmi controller re-authentication!\n");
+ } else {
+ dev_dbg(hdcp->dev, "hdcp verifivation succeeded!\n");
+ /* reset HDCP */
+ hdcp_modb(hdcp, HDMI_A_HDCPCFG1_SWRESET_ASSERT,
+ HDMI_A_HDCPCFG1_SWRESET_MASK,
+ HDMI_A_HDCPCFG1);
+ }
+ }
+
+ if (hdcp_int & 0x40) {
+ hdcp->status = DW_HDCP_AUTH_FAIL;
+ dev_info(hdcp->dev, "hdcp auth fail\n");
+ if (hdcp->remaining_times > 1)
+ hdcp->remaining_times--;
+ else if (hdcp->remaining_times == 1)
+ hdcp_modb(hdcp,
+ HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE,
+ HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK,
+ HDMI_A_HDCPCFG1);
+ } else if (hdcp_int & 0x80) {
+ dev_info(hdcp->dev, "hdcp auth success\n");
+ hdcp->status = DW_HDCP_AUTH_SUCCESS;
+ } else if (hdcp_int & 0x10) {
+ dev_info(hdcp->dev, "i2c nack\n");
+ trytimes++;
+ if (trytimes == 20) {
+ trytimes = 0;
+ dw_hdmi_hdcp1x_stop(hdcp);
+ }
+ }
+}
+
+static ssize_t hdcp_enable_read(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ bool enable = 0;
+ struct dw_hdcp *hdcp = g_hdcp;
+
+ if (hdcp)
+ enable = hdcp->enable;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", enable);
+}
+
+static ssize_t hdcp_enable_write(struct device *device,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ bool enable;
+ struct dw_hdcp *hdcp = g_hdcp;
+
+ if (!hdcp)
+ return -EINVAL;
+
+ if (kstrtobool(buf, &enable))
+ return -EINVAL;
+
+ if (hdcp->enable != enable) {
+ if (enable) {
+ hdcp->enable = enable;
+ if (hdcp->hdcp2 && hdcp->hdcp2->hot_plug) {
+ return count;
+ }
+
+ if (hdcp->read(hdcp->hdmi, HDMI_PHY_STAT0) &
+ HDMI_PHY_HPD) {
+ dw_hdmi_hdcp1x_start(hdcp);
+ }
+ } else {
+ if (hdcp->status != DW_HDCP_DISABLED) {
+ dw_hdmi_hdcp1x_stop(hdcp);
+ }
+ hdcp->enable = enable;
+ }
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(enable, 0644, hdcp_enable_read, hdcp_enable_write);
+
+static ssize_t hdcp_trytimes_read(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ int trytimes = 0;
+ struct dw_hdcp *hdcp = g_hdcp;
+
+ if (hdcp)
+ trytimes = hdcp->retry_times;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", trytimes);
+}
+
+static ssize_t hdcp_trytimes_write(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int trytimes;
+ struct dw_hdcp *hdcp = g_hdcp;
+
+ if (!hdcp)
+ return -EINVAL;
+
+ if (kstrtoint(buf, 0, &trytimes))
+ return -EINVAL;
+
+ if (hdcp->retry_times != trytimes) {
+ hdcp->retry_times = trytimes;
+ hdcp->remaining_times = hdcp->retry_times;
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(trytimes, 0644, hdcp_trytimes_read, hdcp_trytimes_write);
+
+static ssize_t hdcp_status_read(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ int status = DW_HDCP_DISABLED;
+ struct dw_hdcp *hdcp = g_hdcp;
+
+ if (hdcp)
+ status = hdcp->status;
+
+ if (status == DW_HDCP_DISABLED)
+ return snprintf(buf, PAGE_SIZE, "hdcp disable\n");
+ else if (status == DW_HDCP_AUTH_START)
+ return snprintf(buf, PAGE_SIZE, "hdcp_auth_start\n");
+ else if (status == DW_HDCP_AUTH_SUCCESS)
+ return snprintf(buf, PAGE_SIZE, "hdcp_auth_success\n");
+ else if (status == DW_HDCP_AUTH_FAIL)
+ return snprintf(buf, PAGE_SIZE, "hdcp_auth_fail\n");
+ else
+ return snprintf(buf, PAGE_SIZE, "unknown status\n");
+}
+
+static DEVICE_ATTR(status, 0444, hdcp_status_read, NULL);
+
+static int dw_hdmi_hdcp_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct dw_hdcp *hdcp = pdev->dev.platform_data;
+
+ dev_info(&pdev->dev, "%s...\n", __func__);
+ g_hdcp = hdcp;
+ hdcp->mdev.minor = MISC_DYNAMIC_MINOR;
+ hdcp->mdev.name = "hdmi_hdcp1x";
+ hdcp->mdev.mode = 0666;
+
+ if (misc_register(&hdcp->mdev)) {
+ dev_err(&pdev->dev, "HDCP: Could not add character driver\n");
+ return -EINVAL;
+ }
+
+ ret = device_create_file(hdcp->mdev.this_device, &dev_attr_enable);
+ if (ret) {
+ dev_err(&pdev->dev, "HDCP: Could not add sys file enable\n");
+ ret = -EINVAL;
+ goto error0;
+ }
+
+ ret = device_create_file(hdcp->mdev.this_device, &dev_attr_trytimes);
+ if (ret) {
+ dev_err(&pdev->dev, "HDCP: Could not add sys file trytimes\n");
+ ret = -EINVAL;
+ goto error1;
+ }
+
+ ret = device_create_file(hdcp->mdev.this_device, &dev_attr_status);
+ if (ret) {
+ dev_err(&pdev->dev, "HDCP: Could not add sys file status\n");
+ ret = -EINVAL;
+ goto error2;
+ }
+
+ /* retry time if hdcp auth fail. unlimited time if set 0 */
+ hdcp->retry_times = 0;
+ hdcp->dev = &pdev->dev;
+ hdcp->hdcp_start = dw_hdmi_hdcp_start;
+ hdcp->hdcp_stop = dw_hdmi_hdcp_stop;
+ hdcp->hdcp_isr = dw_hdmi_hdcp_isr;
+
+#ifdef CONFIG_DW_HDMI_HDCP1X_ENABLED
+ hdcp_enable_write(NULL, NULL, "1", 1);
+#endif
+
+ dev_dbg(hdcp->dev, "%s success\n", __func__);
+ return 0;
+
+error2:
+ device_remove_file(hdcp->mdev.this_device, &dev_attr_trytimes);
+error1:
+ device_remove_file(hdcp->mdev.this_device, &dev_attr_enable);
+error0:
+ misc_deregister(&hdcp->mdev);
+ return ret;
+}
+
+static int dw_hdmi_hdcp_remove(struct platform_device *pdev)
+{
+ struct dw_hdcp *hdcp = pdev->dev.platform_data;
+
+ device_remove_file(hdcp->mdev.this_device, &dev_attr_trytimes);
+ device_remove_file(hdcp->mdev.this_device, &dev_attr_enable);
+ device_remove_file(hdcp->mdev.this_device, &dev_attr_status);
+ misc_deregister(&hdcp->mdev);
+
+ kfree(hdcp->keys);
+ kfree(hdcp->seeds);
+
+ return 0;
+}
+
+struct platform_driver dw_hdmi_hdcp_driver = {
+ .probe = dw_hdmi_hdcp_probe,
+ .remove = dw_hdmi_hdcp_remove,
+ .driver = {
+ .name = DW_HDCP_DRIVER_NAME,
+ },
+};
+
+//module_platform_driver(dw_hdmi_hdcp_driver);
+MODULE_DESCRIPTION("DW HDMI transmitter HDCP driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/eswin/dw_hdmi_hdcp.h b/drivers/gpu/drm/eswin/dw_hdmi_hdcp.h
new file mode 100644
index 000000000000..849de9037652
--- /dev/null
+++ b/drivers/gpu/drm/eswin/dw_hdmi_hdcp.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) ESWIN Electronics Co.Ltd
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef DW_HDMI_HDCP_H
+#define DW_HDMI_HDCP_H
+
+#include <linux/miscdevice.h>
+
+#define DW_HDCP_DRIVER_NAME "dw-hdmi-hdcp"
+#define HDCP_PRIVATE_KEY_SIZE 280
+#define HDCP_KEY_SHA_SIZE 20
+
+struct hdcp_keys {
+ u8 KSV[8];
+ u8 devicekey[HDCP_PRIVATE_KEY_SIZE];
+ u8 sha1[HDCP_KEY_SHA_SIZE];
+};
+
+struct dw_hdcp2 {
+ int enable;
+ void (*start)(void);
+ void (*stop)(void);
+
+ struct device *dev;
+ int wait_hdcp2_reset;
+ int hot_plug;
+ struct miscdevice mdev;
+ int auth_sucess;
+};
+
+struct dw_hdcp {
+ bool enable;
+ int retry_times;
+ int remaining_times;
+ char *seeds;
+ int invalidkey;
+ char *invalidkeys;
+ int hdcp2_enable;
+ int status;
+ u32 reg_io_width;
+
+ struct dw_hdcp2 *hdcp2;
+ struct miscdevice mdev;
+ struct hdcp_keys *keys;
+ struct device *dev;
+ struct dw_hdmi *hdmi;
+ void __iomem *regs;
+
+ void (*write)(struct dw_hdmi *hdmi, u8 val, int offset);
+ u8 (*read)(struct dw_hdmi *hdmi, int offset);
+ int (*hdcp_start)(struct dw_hdcp *hdcp);
+ int (*hdcp_stop)(struct dw_hdcp *hdcp);
+ void (*hdcp_isr)(struct dw_hdcp *hdcp, int hdcp_int);
+};
+
+extern u8 tv_hdmi_hdcp2_support(struct dw_hdmi *hdmi);
+extern void dw_hdmi_hdcp2_init(struct dw_hdcp2 *hdcp2);
+extern void dw_hdmi_hdcp2_remove(void);
+extern void dw_hdmi_hdcp2_start(int enable);
+extern void get_random_bytes(void *buf, int nbytes);
+#endif
diff --git a/drivers/gpu/drm/eswin/dw_hdmi_hdcp2.c b/drivers/gpu/drm/eswin/dw_hdmi_hdcp2.c
new file mode 100644
index 000000000000..dd553bd9b72e
--- /dev/null
+++ b/drivers/gpu/drm/eswin/dw_hdmi_hdcp2.c
@@ -0,0 +1,775 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/miscdevice.h>
+#include <linux/dma-mapping.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+
+#include <linux/version.h>
+#include <linux/netlink.h>
+#include <linux/proc_fs.h>
+#include <linux/moduleparam.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+
+#include "host_lib_driver_linux_if.h"
+#include "dw_hdmi_hdcp.h"
+
+/**
+ * \file
+ * \ingroup HL_Driver_Kernel
+ * \brief Sample Linux Host Library Driver
+ * \copydoc HL_Driver_Kernel
+ */
+
+/**
+ * \defgroup HL_Driver_Linux Sample Linux Host Library Driver
+ * \ingroup HL_Driver
+ * \brief Sample code for the Linux Host Library Driver.
+ * The Linux Host Library Driver is composed of 2 parts:
+ * 1. A kernel driver.
+ * 2. A file access instance.
+ *
+ * The kernel driver is the kernel executable code enabling the firmware to execute.
+ * It provides the access to the hardware register to interact with the firmware.
+ *
+ * The file access instance initializes the #hl_driver_t structure for the
+ * host library access. The Host Library references the file access to request the
+ * kernel operations.
+ */
+
+/**
+ * \defgroup HL_Driver_Kernel Sample Linux Kernel Host Library Driver
+ * \ingroup HL_Driver_Linux
+ * \brief Example code for the Linux Kernel Host Library Driver.
+ *
+ * The Sample Linux Kernel Driver operates on the linux kernel.
+ * To install (requires root access):
+ * \code
+ insmod bin/linux_hld_module.ko verbose=0
+ * \endcode
+ *
+ * To remove (requires root access):
+ * \code
+ rmmod linux_hld_module
+ * \endcode
+ *
+ * Example Linux Host Library Code:
+ * \code
+ */
+
+#define MAX_HL_DEVICES 16
+
+static bool randomize_mem = false;
+module_param(randomize_mem, bool, 0);
+MODULE_PARM_DESC(noverify, "Wipe memory allocations on startup (for debug)");
+
+static struct dw_hdcp2 *g_dw_hdcp2;
+
+static void dw_hdcp2_stop(void)
+{
+ printk("func: %s; line: %d\n", __func__, __LINE__);
+ g_dw_hdcp2->hot_plug = 0;
+ dw_hdmi_hdcp2_start(0);
+}
+
+static void dw_hdcp2_start(void)
+{
+ printk("func: %s; line: %d\n", __func__, __LINE__);
+ dw_hdmi_hdcp2_start(1);
+ g_dw_hdcp2->hot_plug = 1;
+}
+
+//
+// HL Device
+//
+typedef struct {
+ int allocated, initialized;
+ int code_loaded;
+
+ int code_is_phys_mem;
+ dma_addr_t code_base;
+ uint32_t code_size;
+ uint8_t *code;
+ int data_is_phys_mem;
+ dma_addr_t data_base;
+ uint32_t data_size;
+ uint8_t *data;
+
+ struct resource *hpi_resource;
+ uint8_t __iomem *hpi;
+} hl_device;
+
+static hl_device hl_devices[MAX_HL_DEVICES];
+
+/* HL_DRV_IOC_MEMINFO implementation */
+static long get_meminfo(hl_device *hl_dev, void __user *arg)
+{
+ struct hl_drv_ioc_meminfo info;
+
+ if ((hl_dev == 0) || (arg == 0) || (hl_dev->hpi_resource == 0)) {
+ return -EFAULT;
+ }
+
+ info.hpi_base = hl_dev->hpi_resource->start;
+ info.code_base = hl_dev->code_base;
+ info.code_size = hl_dev->code_size;
+ info.data_base = hl_dev->data_base;
+ info.data_size = hl_dev->data_size;
+
+ if (copy_to_user(arg, &info, sizeof info) != 0) {
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/* HL_DRV_IOC_LOAD_CODE implementation */
+static long load_code(hl_device *hl_dev, struct hl_drv_ioc_code __user *arg)
+{
+ struct hl_drv_ioc_code head;
+
+ if ((hl_dev == 0) || (arg == 0) || (hl_dev->code == 0) ||
+ (hl_dev->data == 0)) {
+ return -EFAULT;
+ }
+
+ if (copy_from_user(&head, arg, sizeof head) != 0)
+ return -EFAULT;
+
+ if (head.len > hl_dev->code_size)
+ return -ENOSPC;
+
+ if (hl_dev->code_loaded)
+ return -EBUSY;
+
+ if (randomize_mem) {
+ prandom_bytes(hl_dev->code, hl_dev->code_size);
+ prandom_bytes(hl_dev->data, hl_dev->data_size);
+ }
+
+ if (copy_from_user(hl_dev->code, &arg->data, head.len) != 0)
+ return -EFAULT;
+
+ hl_dev->code_loaded = 1;
+ return 0;
+}
+
+/* HL_DRV_IOC_WRITE_DATA implementation */
+static long write_data(hl_device *hl_dev, struct hl_drv_ioc_data __user *arg)
+{
+ struct hl_drv_ioc_data head;
+
+ if ((hl_dev == 0) || (arg == 0) || (hl_dev->data == 0)) {
+ return -EFAULT;
+ }
+
+ if (copy_from_user(&head, arg, sizeof head) != 0)
+ return -EFAULT;
+
+ if (hl_dev->data_size < head.len)
+ return -ENOSPC;
+ if (hl_dev->data_size - head.len < head.offset)
+ return -ENOSPC;
+
+ if (copy_from_user(hl_dev->data + head.offset, &arg->data, head.len) !=
+ 0)
+ return -EFAULT;
+
+ return 0;
+}
+
+/* HL_DRV_IOC_READ_DATA implementation */
+static long read_data(hl_device *hl_dev, struct hl_drv_ioc_data __user *arg)
+{
+ struct hl_drv_ioc_data head;
+
+ if ((hl_dev == 0) || (arg == 0) || (hl_dev->data == 0)) {
+ return -EFAULT;
+ }
+
+ if (copy_from_user(&head, arg, sizeof head) != 0)
+ return -EFAULT;
+
+ if (hl_dev->data_size < head.len)
+ return -ENOSPC;
+ if (hl_dev->data_size - head.len < head.offset)
+ return -ENOSPC;
+
+ if (copy_to_user(&arg->data, hl_dev->data + head.offset, head.len) != 0)
+ return -EFAULT;
+
+ return 0;
+}
+
+/* HL_DRV_IOC_MEMSET_DATA implementation */
+static long set_data(hl_device *hl_dev, void __user *arg)
+{
+ union {
+ struct hl_drv_ioc_data data;
+ unsigned char buf[sizeof(struct hl_drv_ioc_data) + 1];
+ } u;
+
+ if ((hl_dev == 0) || (arg == 0) || (hl_dev->data == 0)) {
+ return -EFAULT;
+ }
+
+ if (copy_from_user(&u.data, arg, sizeof u.buf) != 0)
+ return -EFAULT;
+
+ if (hl_dev->data_size < u.data.len)
+ return -ENOSPC;
+ if (hl_dev->data_size - u.data.len < u.data.offset)
+ return -ENOSPC;
+
+ memset(hl_dev->data + u.data.offset, u.data.data[0], u.data.len);
+ return 0;
+}
+
+/* HL_DRV_IOC_READ_HPI implementation */
+static long hpi_read(hl_device *hl_dev, void __user *arg)
+{
+ struct hl_drv_ioc_hpi_reg reg;
+
+ if ((hl_dev == 0) || (arg == 0) || (hl_dev->hpi_resource == 0)) {
+ return -EFAULT;
+ }
+
+ if (copy_from_user(&reg, arg, sizeof reg) != 0)
+ return -EFAULT;
+
+ if ((reg.offset & 3) ||
+ reg.offset >= resource_size(hl_dev->hpi_resource))
+ return -EINVAL;
+
+ reg.value = ioread32(hl_dev->hpi + reg.offset);
+
+ if (copy_to_user(arg, &reg, sizeof reg) != 0)
+ return -EFAULT;
+
+ return 0;
+}
+
+/* HL_DRV_IOC_WRITE_HPI implementation */
+static long hpi_write(hl_device *hl_dev, void __user *arg)
+{
+ struct hl_drv_ioc_hpi_reg reg;
+
+ if ((hl_dev == 0) || (arg == 0)) {
+ return -EFAULT;
+ }
+
+ if (copy_from_user(&reg, arg, sizeof reg) != 0)
+ return -EFAULT;
+
+ if ((reg.offset & 3) ||
+ reg.offset >= resource_size(hl_dev->hpi_resource))
+ return -EINVAL;
+
+ iowrite32(reg.value, hl_dev->hpi + reg.offset);
+
+#ifdef TROOT_GRIFFIN
+ // If Kill command
+ // (HL_GET_CMD_EVENT(krequest.data) == TROOT_CMD_SYSTEM_ON_EXIT_REQ))
+ //
+ if ((reg.offset == 0x38) && ((reg.value & 0x000000ff) == 0x08)) {
+ hl_dev->code_loaded = 0;
+ }
+#endif
+ return 0;
+}
+
+static hl_device *alloc_hl_dev_slot(const struct hl_drv_ioc_meminfo *info)
+{
+ int i;
+
+ if (info == 0) {
+ return 0;
+ }
+
+ /* Check if we have a matching device (same HPI base) */
+ for (i = 0; i < MAX_HL_DEVICES; i++) {
+ hl_device *slot = &hl_devices[i];
+ if (slot->allocated &&
+ (info->hpi_base == slot->hpi_resource->start))
+ return slot;
+ }
+
+ /* Find unused slot */
+ for (i = 0; i < MAX_HL_DEVICES; i++) {
+ hl_device *slot = &hl_devices[i];
+ if (!slot->allocated) {
+ slot->allocated = 1;
+ return slot;
+ }
+ }
+
+ return 0;
+}
+
+static void free_dma_areas(hl_device *hl_dev)
+{
+ if (hl_dev == 0) {
+ return;
+ }
+
+ if (!hl_dev->code_is_phys_mem && hl_dev->code) {
+ dma_free_coherent(0, hl_dev->code_size, hl_dev->code,
+ hl_dev->code_base);
+ hl_dev->code = 0;
+ }
+
+ if (!hl_dev->data_is_phys_mem && hl_dev->data) {
+ dma_free_coherent(0, hl_dev->data_size, hl_dev->data,
+ hl_dev->data_base);
+ hl_dev->data = 0;
+ }
+}
+
+static int alloc_dma_areas(hl_device *hl_dev,
+ const struct hl_drv_ioc_meminfo *info)
+{
+ if ((hl_dev == 0) || (info == 0)) {
+ return -EFAULT;
+ }
+
+ hl_dev->code_size = info->code_size;
+ hl_dev->code_is_phys_mem =
+ (info->code_base != HL_DRIVER_ALLOCATE_DYNAMIC_MEM);
+
+ if (hl_dev->code_is_phys_mem && (hl_dev->code == 0)) {
+ /* TODO: support highmem */
+ hl_dev->code_base = info->code_base;
+ hl_dev->code = phys_to_virt(hl_dev->code_base);
+ } else {
+ dma_set_mask_and_coherent(g_dw_hdcp2->dev, DMA_BIT_MASK(32));
+ hl_dev->code =
+ dma_alloc_coherent(g_dw_hdcp2->dev, hl_dev->code_size,
+ &hl_dev->code_base, GFP_KERNEL);
+ if (!hl_dev->code) {
+ return -ENOMEM;
+ }
+ }
+
+ hl_dev->data_size = info->data_size;
+ hl_dev->data_is_phys_mem =
+ (info->data_base != HL_DRIVER_ALLOCATE_DYNAMIC_MEM);
+
+ if (hl_dev->data_is_phys_mem && (hl_dev->data == 0)) {
+ hl_dev->data_base = info->data_base;
+ hl_dev->data = phys_to_virt(hl_dev->data_base);
+ } else {
+ hl_dev->data =
+ dma_alloc_coherent(g_dw_hdcp2->dev, hl_dev->data_size,
+ &hl_dev->data_base, GFP_KERNEL);
+ if (!hl_dev->data) {
+ free_dma_areas(hl_dev);
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+/* HL_DRV_IOC_INIT implementation */
+static long init(struct file *f, void __user *arg)
+{
+ struct resource *hpi_mem;
+ struct hl_drv_ioc_meminfo info;
+ hl_device *hl_dev;
+ int rc;
+
+ if ((f == 0) || (arg == 0)) {
+ return -EFAULT;
+ }
+
+ if (copy_from_user(&info, arg, sizeof info) != 0)
+ return -EFAULT;
+
+ hl_dev = alloc_hl_dev_slot(&info);
+ if (!hl_dev)
+ return -EMFILE;
+
+ if (!hl_dev->initialized) {
+ rc = alloc_dma_areas(hl_dev, &info);
+ if (rc < 0)
+ goto err_free;
+
+ hpi_mem = request_mem_region(info.hpi_base, 128, "hl_dev-hpi");
+ if (!hpi_mem) {
+ rc = -EADDRNOTAVAIL;
+ goto err_free;
+ }
+
+ hl_dev->hpi = ioremap(hpi_mem->start, resource_size(hpi_mem));
+ if (!hl_dev->hpi) {
+ rc = -ENOMEM;
+ goto err_release_region;
+ }
+ hl_dev->hpi_resource = hpi_mem;
+ hl_dev->initialized = 1;
+ }
+
+ f->private_data = hl_dev;
+ return 0;
+
+err_release_region:
+ release_resource(hpi_mem);
+err_free:
+ free_dma_areas(hl_dev);
+ hl_dev->initialized = 0;
+ hl_dev->allocated = 0;
+ hl_dev->hpi_resource = 0;
+ hl_dev->hpi = 0;
+
+ return rc;
+}
+
+static void free_hl_dev_slot(hl_device *slot)
+{
+ if (slot == 0) {
+ return;
+ }
+
+ if (!slot->allocated)
+ return;
+
+ if (slot->initialized) {
+ if (slot->hpi) {
+ iounmap(slot->hpi);
+ slot->hpi = 0;
+ }
+
+ if (slot->hpi_resource) {
+ release_mem_region(slot->hpi_resource->start, 128);
+ slot->hpi_resource = 0;
+ }
+
+ free_dma_areas(slot);
+ }
+
+ slot->initialized = 0;
+ slot->allocated = 0;
+}
+
+static long hld_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
+{
+ hl_device *hl_dev;
+ void __user *data;
+
+ if (f == 0) {
+ return -EFAULT;
+ }
+
+ hl_dev = f->private_data;
+ data = (void __user *)arg;
+
+ if (cmd == HL_DRV_IOC_INIT) {
+ return init(f, data);
+ } else if (!hl_dev) {
+ return -EAGAIN;
+ }
+
+ switch (cmd) {
+ case HL_DRV_IOC_INIT:
+ return init(f, data);
+ case HL_DRV_IOC_MEMINFO:
+ return get_meminfo(hl_dev, data);
+ case HL_DRV_IOC_READ_HPI:
+ return hpi_read(hl_dev, data);
+ case HL_DRV_IOC_WRITE_HPI:
+ return hpi_write(hl_dev, data);
+ case HL_DRV_IOC_LOAD_CODE:
+ return load_code(hl_dev, data);
+ case HL_DRV_IOC_WRITE_DATA:
+ return write_data(hl_dev, data);
+ case HL_DRV_IOC_READ_DATA:
+ return read_data(hl_dev, data);
+ case HL_DRV_IOC_MEMSET_DATA:
+ return set_data(hl_dev, data);
+
+ case DW_DRV_IOC_CONNECT_STATUS:
+ return g_dw_hdcp2->hot_plug;
+ case DW_DRV_IOC_CONNECT_SET:
+ printk("set hdcp2 reset one\n");
+ g_dw_hdcp2->wait_hdcp2_reset = 1;
+ dw_hdmi_hdcp2_start(1);
+ return 0;
+ case DW_DRV_IOC_DISCONNECT_SET:
+ if (g_dw_hdcp2->wait_hdcp2_reset == 1) {
+ printk("set hdcp2 reset zero\n");
+ g_dw_hdcp2->wait_hdcp2_reset = 0;
+ dw_hdmi_hdcp2_start(0);
+ }
+ if (g_dw_hdcp2->auth_sucess == 1) {
+ g_dw_hdcp2->auth_sucess = 0;
+ }
+ return 0;
+ case DW_DRV_IOC_AUTH_SUCCESS:
+ g_dw_hdcp2->auth_sucess = 1;
+ return 0;
+ case DW_DRV_IOC_AUTH_FAIL:
+ g_dw_hdcp2->auth_sucess = 0;
+ return 0;
+ case DW_DRV_IOC_NO_CAPACITY:
+ printk("set hdcp2 reset zero 3005\n");
+ g_dw_hdcp2->hot_plug = 0;
+ g_dw_hdcp2->wait_hdcp2_reset = 0;
+ dw_hdmi_hdcp2_start(0);
+ dw_hdmi_hdcp2_start(2);
+ return 0;
+ }
+
+ return -ENOTTY;
+}
+
+static const struct file_operations hld_file_operations = {
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = hld_ioctl,
+#else
+ .unlocked_ioctl = hld_ioctl,
+#endif
+ .owner = THIS_MODULE,
+};
+
+static struct miscdevice hld_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "hl_dev",
+ .fops = &hld_file_operations,
+};
+
+static int hld_init(void)
+{
+ int i;
+
+ printk("%s...\n", __func__);
+ for (i = 0; i < MAX_HL_DEVICES; i++) {
+ hl_devices[i].allocated = 0;
+ hl_devices[i].initialized = 0;
+ hl_devices[i].code_loaded = 0;
+ hl_devices[i].code = 0;
+ hl_devices[i].data = 0;
+ hl_devices[i].hpi_resource = 0;
+ hl_devices[i].hpi = 0;
+ }
+ return misc_register(&hld_device);
+}
+
+static void hld_exit(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_HL_DEVICES; i++) {
+ free_hl_dev_slot(&hl_devices[i]);
+ }
+
+ misc_deregister(&hld_device);
+}
+
+static int dw_hdmi2_hdcp2_clk_enable(struct device *dev)
+{
+ struct clk *pclk;
+ //struct clk *aclk;
+ struct clk *hdcp2_clk_hdmi;
+
+ pclk = devm_clk_get(dev, "pclk_hdcp2");
+ if (IS_ERR(pclk)) {
+ pr_err("Unable to get hdcp2 pclk\n");
+ return -1;
+ }
+ clk_prepare_enable(pclk);
+#if 0
+ aclk = devm_clk_get(dev, "aclk_hdcp2");
+ if (IS_ERR(aclk)) {
+ pr_err("Unable to get hdcp2 aclk\n");
+ return -1;
+ }
+ clk_prepare_enable(aclk);
+#endif
+ hdcp2_clk_hdmi = devm_clk_get(dev, "hdcp2_clk_hdmi");
+ if (IS_ERR(hdcp2_clk_hdmi)) {
+ pr_err("Unable to get hdcp2_clk_hdmi\n");
+ return -1;
+ }
+ clk_prepare_enable(hdcp2_clk_hdmi);
+
+ return 0;
+}
+
+static ssize_t hdcp2_show_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", g_dw_hdcp2->enable);
+}
+
+static ssize_t hdcp2_store_enable(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ int enable;
+ if (kstrtoint(buf, 0, &enable))
+ return size;
+
+ if (g_dw_hdcp2->enable != enable) {
+ g_dw_hdcp2->enable = enable;
+ if (enable) {
+ dw_hdmi_hdcp2_start(3);
+ } else {
+ if (g_dw_hdcp2->hot_plug) {
+ g_dw_hdcp2->stop();
+ dw_hdmi_hdcp2_start(2);
+ }
+ }
+ }
+ return size;
+}
+
+static ssize_t hdcp2_show_status(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (g_dw_hdcp2->enable != 1) {
+ return snprintf(buf, PAGE_SIZE, "%s\n", "no enable hdcp2");
+ } else if (!g_dw_hdcp2->hot_plug) {
+ return snprintf(buf, PAGE_SIZE, "%s\n", "hdcp2 no auth");
+ } else {
+ if (g_dw_hdcp2->auth_sucess)
+ return snprintf(buf, PAGE_SIZE, "%s\n",
+ "hdcp2 auth sucess");
+ else
+ return snprintf(buf, PAGE_SIZE, "%s\n",
+ "no already auth sucess");
+ }
+}
+
+static DEVICE_ATTR(enable, 0644, hdcp2_show_enable, hdcp2_store_enable);
+static DEVICE_ATTR(status, 0444, hdcp2_show_status, NULL);
+
+static int create_device_node(void)
+{
+ int ret;
+
+ if (!g_dw_hdcp2)
+ return -1;
+ g_dw_hdcp2->mdev.minor = MISC_DYNAMIC_MINOR;
+ g_dw_hdcp2->mdev.name = "hdcp2_node";
+ g_dw_hdcp2->mdev.mode = 0666;
+ if (misc_register(&(g_dw_hdcp2->mdev))) {
+ pr_err("HDCP2: Could not add character driver\n");
+ return -1;
+ }
+
+ ret = device_create_file(g_dw_hdcp2->mdev.this_device,
+ &dev_attr_enable);
+ if (ret) {
+ pr_err("HDCP: Could not add sys file enable\n");
+ ret = -EINVAL;
+ goto error0;
+ }
+
+ ret = device_create_file(g_dw_hdcp2->mdev.this_device,
+ &dev_attr_status);
+ if (ret) {
+ pr_err("HDCP: Could not add sys file status\n");
+ ret = -EINVAL;
+ goto error1;
+ }
+
+ return 0;
+
+error1:
+ device_remove_file(g_dw_hdcp2->mdev.this_device, &dev_attr_enable);
+error0:
+ misc_deregister(&g_dw_hdcp2->mdev);
+ return ret;
+}
+
+static void end_device_node(void)
+{
+ if (g_dw_hdcp2)
+ misc_deregister(&(g_dw_hdcp2->mdev));
+}
+
+static int eswin_hdmi_hdcp2_probe(struct platform_device *pdev)
+{
+ struct device *hdcp2_dev = &pdev->dev;
+
+ printk("%s...\n", __func__);
+ g_dw_hdcp2 = kmalloc(sizeof(*g_dw_hdcp2), GFP_KERNEL);
+ if (!g_dw_hdcp2) {
+ printk("malloc g_dw_hdcp2 error\n");
+ return -ENOMEM;
+ }
+ memset(g_dw_hdcp2, 0, sizeof(*g_dw_hdcp2));
+
+ g_dw_hdcp2->dev = hdcp2_dev;
+ g_dw_hdcp2->stop = dw_hdcp2_stop;
+ g_dw_hdcp2->start = dw_hdcp2_start;
+ hld_init();
+ dw_hdmi2_hdcp2_clk_enable(hdcp2_dev);
+ dw_hdmi_hdcp2_init(g_dw_hdcp2);
+ dw_hdmi_hdcp2_start(3);
+
+ create_device_node();
+ return 0;
+}
+
+static int eswin_hdmi_hdcp2_remove(struct platform_device *pdev)
+{
+ printk("%s...\n", __func__);
+ dw_hdmi_hdcp2_remove();
+ end_device_node();
+ hld_exit();
+ kfree(g_dw_hdcp2);
+ g_dw_hdcp2 = NULL;
+
+ return 0;
+}
+
+static void eswin_hdmi_hdcp2_shutdown(struct platform_device *pdev)
+{
+ printk("%s...\n", __func__);
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id dw_hdmi_hdcp2_dt_ids[] = {
+ {
+ .compatible = "eswin,dw-hdmi-hdcp2",
+ },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, dw_hdmi_hdcp2_dt_ids);
+#endif
+
+struct platform_driver dw_hdmi_hdcp2_driver = {
+ .probe = eswin_hdmi_hdcp2_probe,
+ .remove = eswin_hdmi_hdcp2_remove,
+ .shutdown = eswin_hdmi_hdcp2_shutdown,
+ .driver = {
+ .name = "dw-hdmi-hdcp2",
+ .owner = THIS_MODULE,
+#if defined(CONFIG_OF)
+ .of_match_table = of_match_ptr(dw_hdmi_hdcp2_dt_ids),
+#endif
+ },
+};
+
+static int __init hdmi_hdcp2_init(void)
+{
+ printk("%s...\n", __func__);
+ return platform_driver_register(&dw_hdmi_hdcp2_driver);
+}
+
+static void hdmi_hdcp2_exit(void)
+{
+ platform_driver_unregister(&dw_hdmi_hdcp2_driver);
+}
+
+late_initcall_sync(hdmi_hdcp2_init);
+module_exit(hdmi_hdcp2_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Synopsys, Inc.");
+MODULE_DESCRIPTION("Linux Host Library Driver");
diff --git a/drivers/gpu/drm/eswin/dw_hdmi_i2s_audio.c b/drivers/gpu/drm/eswin/dw_hdmi_i2s_audio.c
new file mode 100644
index 000000000000..a2f3099b6bd2
--- /dev/null
+++ b/drivers/gpu/drm/eswin/dw_hdmi_i2s_audio.c
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * dw-hdmi-i2s-audio.c
+ *
+ * Copyright (c) 2017 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+
+#include <drm/drm_crtc.h>
+
+#include <sound/hdmi-codec.h>
+
+#include "dw_hdmi.h"
+#include "dw_hdmi_audio.h"
+
+#define DRIVER_NAME "dw-hdmi-i2s-audio"
+
+static inline void hdmi_write(struct dw_hdmi_i2s_audio_data *audio, u8 val,
+ int offset)
+{
+ struct dw_hdmi *hdmi = audio->hdmi;
+
+ audio->write(hdmi, val, offset);
+}
+
+static inline u8 hdmi_read(struct dw_hdmi_i2s_audio_data *audio, int offset)
+{
+ struct dw_hdmi *hdmi = audio->hdmi;
+
+ return audio->read(hdmi, offset);
+}
+
+static int dw_hdmi_i2s_hw_params(struct device *dev, void *data,
+ struct hdmi_codec_daifmt *fmt,
+ struct hdmi_codec_params *hparms)
+{
+ struct dw_hdmi_i2s_audio_data *audio = data;
+ struct dw_hdmi *hdmi = audio->hdmi;
+ u8 conf0 = 0;
+ u8 conf1 = 0;
+ u8 inputclkfs = 0;
+
+ /* it cares I2S only */
+ if (fmt->bit_clk_master | fmt->frame_clk_master) {
+ dev_err(dev, "unsupported clock settings\n");
+ return -EINVAL;
+ }
+
+ /* Reset the FIFOs before applying new params */
+ hdmi_write(audio, HDMI_AUD_CONF0_SW_RESET, HDMI_AUD_CONF0);
+ hdmi_write(audio, (u8)~HDMI_MC_SWRSTZ_I2SSWRST_REQ, HDMI_MC_SWRSTZ);
+
+ inputclkfs = HDMI_AUD_INPUTCLKFS_64FS;
+ conf0 = (HDMI_AUD_CONF0_I2S_SELECT | HDMI_AUD_CONF0_I2S_EN0);
+
+ /* Enable the required i2s lanes */
+ switch (hparms->channels) {
+ case 7 ... 8:
+ conf0 |= HDMI_AUD_CONF0_I2S_EN3;
+ fallthrough;
+ case 5 ... 6:
+ conf0 |= HDMI_AUD_CONF0_I2S_EN2;
+ fallthrough;
+ case 3 ... 4:
+ conf0 |= HDMI_AUD_CONF0_I2S_EN1;
+ /* Fall-thru */
+ }
+
+ switch (hparms->sample_width) {
+ case 16:
+ conf1 = HDMI_AUD_CONF1_WIDTH_16;
+ break;
+ case 24:
+ case 32:
+ conf1 = HDMI_AUD_CONF1_WIDTH_24;
+ break;
+ }
+
+ switch (fmt->fmt) {
+ case HDMI_I2S:
+ conf1 |= HDMI_AUD_CONF1_MODE_I2S;
+ break;
+ case HDMI_RIGHT_J:
+ conf1 |= HDMI_AUD_CONF1_MODE_RIGHT_J;
+ break;
+ case HDMI_LEFT_J:
+ conf1 |= HDMI_AUD_CONF1_MODE_LEFT_J;
+ break;
+ case HDMI_DSP_A:
+ conf1 |= HDMI_AUD_CONF1_MODE_BURST_1;
+ break;
+ case HDMI_DSP_B:
+ conf1 |= HDMI_AUD_CONF1_MODE_BURST_2;
+ break;
+ default:
+ dev_err(dev, "unsupported format\n");
+ return -EINVAL;
+ }
+
+ dw_hdmi_set_sample_rate(hdmi, hparms->sample_rate);
+ dw_hdmi_set_channel_status(hdmi, hparms->iec.status);
+ dw_hdmi_set_channel_count(hdmi, hparms->channels);
+ dw_hdmi_set_channel_allocation(hdmi, hparms->cea.channel_allocation);
+
+ hdmi_write(audio, inputclkfs, HDMI_AUD_INPUTCLKFS);
+ hdmi_write(audio, conf0, HDMI_AUD_CONF0);
+ hdmi_write(audio, conf1, HDMI_AUD_CONF1);
+
+ return 0;
+}
+
+static int dw_hdmi_i2s_audio_startup(struct device *dev, void *data)
+{
+ struct dw_hdmi_i2s_audio_data *audio = data;
+ struct dw_hdmi *hdmi = audio->hdmi;
+
+ dw_hdmi_audio_enable(hdmi);
+
+ return 0;
+}
+
+static void dw_hdmi_i2s_audio_shutdown(struct device *dev, void *data)
+{
+ struct dw_hdmi_i2s_audio_data *audio = data;
+ struct dw_hdmi *hdmi = audio->hdmi;
+
+ dw_hdmi_audio_disable(hdmi);
+}
+
+static int dw_hdmi_i2s_get_eld(struct device *dev, void *data, uint8_t *buf,
+ size_t len)
+{
+ struct dw_hdmi_i2s_audio_data *audio = data;
+ u8 *eld;
+
+ eld = audio->get_eld(audio->hdmi);
+ if (eld)
+ memcpy(buf, eld, min_t(size_t, MAX_ELD_BYTES, len));
+ else
+ /* Pass en empty ELD if connector not available */
+ memset(buf, 0, len);
+
+ return 0;
+}
+
+static int dw_hdmi_i2s_get_dai_id(struct snd_soc_component *component,
+ struct device_node *endpoint)
+{
+ struct of_endpoint of_ep;
+ int ret;
+
+ ret = of_graph_parse_endpoint(endpoint, &of_ep);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * HDMI sound should be located as reg = <2>
+ * Then, it is sound port 0
+ */
+ if (of_ep.port == 2)
+ return 0;
+
+ return -EINVAL;
+}
+
+static int dw_hdmi_i2s_hook_plugged_cb(struct device *dev, void *data,
+ hdmi_codec_plugged_cb fn,
+ struct device *codec_dev)
+{
+ struct dw_hdmi_i2s_audio_data *audio = data;
+ struct dw_hdmi *hdmi = audio->hdmi;
+
+ return dw_hdmi_set_plugged_cb(hdmi, fn, codec_dev);
+}
+
+static const struct hdmi_codec_ops dw_hdmi_i2s_ops = {
+ .hw_params = dw_hdmi_i2s_hw_params,
+ .audio_startup = dw_hdmi_i2s_audio_startup,
+ .audio_shutdown = dw_hdmi_i2s_audio_shutdown,
+ .get_eld = dw_hdmi_i2s_get_eld,
+ .get_dai_id = dw_hdmi_i2s_get_dai_id,
+ .hook_plugged_cb = dw_hdmi_i2s_hook_plugged_cb,
+};
+
+static int snd_dw_hdmi_probe(struct platform_device *pdev)
+{
+ struct dw_hdmi_i2s_audio_data *audio = pdev->dev.platform_data;
+ struct platform_device_info pdevinfo;
+ struct hdmi_codec_pdata pdata;
+ struct platform_device *platform;
+
+ pdata.ops = &dw_hdmi_i2s_ops;
+ pdata.i2s = 1;
+ pdata.max_i2s_channels = 8;
+ pdata.data = audio;
+
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ pdevinfo.parent = pdev->dev.parent;
+ pdevinfo.id = PLATFORM_DEVID_AUTO;
+ pdevinfo.name = HDMI_CODEC_DRV_NAME;
+ pdevinfo.data = &pdata;
+ pdevinfo.size_data = sizeof(pdata);
+ pdevinfo.dma_mask = DMA_BIT_MASK(32);
+
+ platform = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(platform))
+ return PTR_ERR(platform);
+
+ dev_set_drvdata(&pdev->dev, platform);
+
+ return 0;
+}
+
+static int snd_dw_hdmi_remove(struct platform_device *pdev)
+{
+ struct platform_device *platform = dev_get_drvdata(&pdev->dev);
+
+ platform_device_unregister(platform);
+
+ return 0;
+}
+
+struct platform_driver snd_dw_hdmi_driver = {
+ .probe = snd_dw_hdmi_probe,
+ .remove = snd_dw_hdmi_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+};
+//module_platform_driver(snd_dw_hdmi_driver);
+
+MODULE_DESCRIPTION("Synopsis Designware HDMI I2S ALSA SoC interface");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/gpu/drm/eswin/es_crtc.c b/drivers/gpu/drm/eswin/es_crtc.c
new file mode 100644
index 000000000000..96cbe4badf91
--- /dev/null
+++ b/drivers/gpu/drm/eswin/es_crtc.c
@@ -0,0 +1,436 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Eswin Holdings Co., Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/media-bus-format.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_vblank.h>
+
+#include "es_drm.h"
+#include "es_crtc.h"
+
+void es_crtc_destroy(struct drm_crtc *crtc)
+{
+ struct es_crtc *es_crtc = to_es_crtc(crtc);
+
+ drm_crtc_cleanup(crtc);
+ kfree(es_crtc);
+}
+
+static void es_crtc_reset(struct drm_crtc *crtc)
+{
+ struct es_crtc_state *state;
+
+ if (crtc->state) {
+ __drm_atomic_helper_crtc_destroy_state(crtc->state);
+
+ state = to_es_crtc_state(crtc->state);
+ kfree(state);
+ crtc->state = NULL;
+ }
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (state == NULL)
+ return;
+
+ __drm_atomic_helper_crtc_reset(crtc, &state->base);
+
+ state->sync_mode = ES_SINGLE_DC;
+ state->output_fmt = MEDIA_BUS_FMT_RBG888_1X24;
+ state->encoder_type = DRM_MODE_ENCODER_NONE;
+#ifdef CONFIG_ESWIN_MMU
+ state->mmu_prefetch = ES_MMU_PREFETCH_DISABLE;
+#endif
+}
+
+static struct drm_crtc_state *
+es_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
+{
+ struct es_crtc_state *ori_state;
+ struct es_crtc_state *state;
+
+ if (WARN_ON(!crtc->state))
+ return NULL;
+
+ ori_state = to_es_crtc_state(crtc->state);
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return NULL;
+
+ __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
+
+ state->sync_mode = ori_state->sync_mode;
+ state->output_fmt = ori_state->output_fmt;
+ state->encoder_type = ori_state->encoder_type;
+ state->bpp = ori_state->bpp;
+ state->dither_enable = ori_state->dither_enable;
+ state->underflow = ori_state->underflow;
+ state->bg_color = ori_state->bg_color;
+#ifdef CONFIG_ESWIN_MMU
+ state->mmu_prefetch = ori_state->mmu_prefetch;
+#endif
+
+ return &state->base;
+}
+
+static void es_crtc_atomic_destroy_state(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+{
+ __drm_atomic_helper_crtc_destroy_state(state);
+ kfree(to_es_crtc_state(state));
+}
+
+static int es_crtc_atomic_set_property(struct drm_crtc *crtc,
+ struct drm_crtc_state *state,
+ struct drm_property *property,
+ uint64_t val)
+{
+ struct es_crtc *es_crtc = to_es_crtc(crtc);
+ struct es_crtc_state *es_crtc_state = to_es_crtc_state(state);
+
+ if (property == es_crtc->sync_mode)
+ es_crtc_state->sync_mode = val;
+ else if (property == es_crtc->mmu_prefetch)
+ es_crtc_state->mmu_prefetch = val;
+ else if (property == es_crtc->dither)
+ es_crtc_state->dither_enable = val;
+ else if (property == es_crtc->bg_color)
+ es_crtc_state->bg_color = val;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int es_crtc_atomic_get_property(struct drm_crtc *crtc,
+ const struct drm_crtc_state *state,
+ struct drm_property *property,
+ uint64_t *val)
+{
+ struct es_crtc *es_crtc = to_es_crtc(crtc);
+ const struct es_crtc_state *es_crtc_state =
+ container_of(state, const struct es_crtc_state, base);
+
+ if (property == es_crtc->sync_mode)
+ *val = es_crtc_state->sync_mode;
+ else if (property == es_crtc->mmu_prefetch)
+ *val = es_crtc_state->mmu_prefetch;
+ else if (property == es_crtc->dither)
+ *val = es_crtc_state->dither_enable;
+ else if (property == es_crtc->bg_color)
+ *val = es_crtc_state->bg_color;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int es_crtc_debugfs_show(struct seq_file *s, void *data)
+{
+ struct drm_crtc *crtc = s->private;
+ struct es_crtc_state *crtc_state = to_es_crtc_state(crtc->state);
+ struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+
+ seq_printf(s, "crtc[%u]: %s\n", crtc->base.id, crtc->name);
+ seq_printf(s, "\tactive = %d\n", crtc->state->active);
+ seq_printf(s, "\tsize = %dx%d\n", mode->hdisplay, mode->vdisplay);
+ seq_printf(s, "\tbpp = %u\n", crtc_state->bpp);
+ seq_printf(s, "\tunderflow = %d\n", crtc_state->underflow);
+
+ return 0;
+}
+
+static int es_crtc_debugfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, es_crtc_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations es_crtc_debugfs_fops = {
+ .open = es_crtc_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int es_crtc_debugfs_init(struct drm_crtc *crtc)
+{
+ debugfs_create_file("status", 0444, crtc->debugfs_entry, crtc,
+ &es_crtc_debugfs_fops);
+
+ return 0;
+}
+#else
+static int es_crtc_debugfs_init(struct drm_crtc *crtc)
+{
+ return 0;
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static int es_crtc_late_register(struct drm_crtc *crtc)
+{
+ return es_crtc_debugfs_init(crtc);
+}
+
+static int es_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+ struct es_crtc *es_crtc = to_es_crtc(crtc);
+
+ es_crtc->funcs->enable_vblank(es_crtc->dev, true);
+
+ return 0;
+}
+
+static void es_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+ struct es_crtc *es_crtc = to_es_crtc(crtc);
+
+ es_crtc->funcs->enable_vblank(es_crtc->dev, false);
+}
+
+static const struct drm_crtc_funcs es_crtc_funcs = {
+ .set_config = drm_atomic_helper_set_config,
+ .destroy = es_crtc_destroy,
+ .page_flip = drm_atomic_helper_page_flip,
+ .reset = es_crtc_reset,
+ .atomic_duplicate_state = es_crtc_atomic_duplicate_state,
+ .atomic_destroy_state = es_crtc_atomic_destroy_state,
+ .atomic_set_property = es_crtc_atomic_set_property,
+ .atomic_get_property = es_crtc_atomic_get_property,
+ // .gamma_set = drm_atomic_helper_legacy_gamma_set, /* TODO: */
+ .late_register = es_crtc_late_register,
+ .enable_vblank = es_crtc_enable_vblank,
+ .disable_vblank = es_crtc_disable_vblank,
+};
+
+static u8 cal_pixel_bits(u32 bus_format)
+{
+ u8 bpp;
+
+ switch (bus_format) {
+ case MEDIA_BUS_FMT_RGB565_1X16:
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ bpp = 16;
+ break;
+ case MEDIA_BUS_FMT_RGB666_1X18:
+ case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
+ bpp = 18;
+ break;
+ case MEDIA_BUS_FMT_UYVY10_1X20:
+ bpp = 20;
+ break;
+ case MEDIA_BUS_FMT_BGR888_1X24:
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+ case MEDIA_BUS_FMT_YUV8_1X24:
+ bpp = 24;
+ break;
+ case MEDIA_BUS_FMT_RGB101010_1X30:
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+ case MEDIA_BUS_FMT_YUV10_1X30:
+ bpp = 30;
+ break;
+ default:
+ bpp = 24;
+ break;
+ }
+
+ return bpp;
+}
+
+static bool es_crtc_mode_fixup(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct es_crtc *es_crtc = to_es_crtc(crtc);
+
+ return es_crtc->funcs->mode_fixup(es_crtc->dev, mode, adjusted_mode);
+}
+
+static void es_crtc_atomic_enable(struct drm_crtc *crtc,
+ struct drm_atomic_state *old_state)
+{
+ struct es_crtc *es_crtc = to_es_crtc(crtc);
+ struct es_crtc_state *es_crtc_state = to_es_crtc_state(crtc->state);
+
+ es_crtc_state->bpp = cal_pixel_bits(es_crtc_state->output_fmt);
+
+ es_crtc->funcs->enable(es_crtc->dev, crtc);
+
+ drm_crtc_vblank_on(crtc);
+}
+
+static void es_crtc_atomic_disable(struct drm_crtc *crtc,
+ struct drm_atomic_state *old_state)
+{
+ struct es_crtc *es_crtc = to_es_crtc(crtc);
+
+ drm_crtc_vblank_off(crtc);
+
+ es_crtc->funcs->disable(es_crtc->dev);
+
+ if (crtc->state->event && !crtc->state->active) {
+ spin_lock_irq(&crtc->dev->event_lock);
+ drm_crtc_send_vblank_event(crtc, crtc->state->event);
+ spin_unlock_irq(&crtc->dev->event_lock);
+
+ crtc->state->event = NULL;
+ }
+}
+
+static void es_crtc_atomic_begin(struct drm_crtc *crtc,
+ struct drm_atomic_state *old_crtc_state)
+{
+ struct es_crtc *es_crtc = to_es_crtc(crtc);
+ struct device *dev = es_crtc->dev;
+ struct drm_property_blob *blob = crtc->state->gamma_lut;
+ struct drm_color_lut *lut;
+
+ if (crtc->state->color_mgmt_changed) {
+ if ((blob) && (blob->length)) {
+ lut = blob->data;
+ es_crtc->funcs->set_gamma(dev, lut,
+ blob->length / sizeof(*lut));
+ es_crtc->funcs->enable_gamma(dev, true);
+ } else {
+ es_crtc->funcs->enable_gamma(dev, false);
+ }
+ }
+}
+
+static void es_crtc_atomic_flush(struct drm_crtc *crtc,
+ struct drm_atomic_state *old_crtc_state)
+{
+ struct es_crtc *es_crtc = to_es_crtc(crtc);
+ struct drm_pending_vblank_event *event = crtc->state->event;
+
+ es_crtc->funcs->commit(es_crtc->dev);
+
+ if (event) {
+ WARN_ON(drm_crtc_vblank_get(crtc) != 0);
+
+ spin_lock_irq(&crtc->dev->event_lock);
+ drm_crtc_arm_vblank_event(crtc, event);
+ spin_unlock_irq(&crtc->dev->event_lock);
+ crtc->state->event = NULL;
+ }
+}
+
+static const struct drm_crtc_helper_funcs es_crtc_helper_funcs = {
+ .mode_fixup = es_crtc_mode_fixup,
+ .atomic_enable = es_crtc_atomic_enable,
+ .atomic_disable = es_crtc_atomic_disable,
+ .atomic_begin = es_crtc_atomic_begin,
+ .atomic_flush = es_crtc_atomic_flush,
+};
+
+static const struct drm_prop_enum_list es_sync_mode_enum_list[] = {
+ { ES_SINGLE_DC, "single dc mode" },
+ { ES_MULTI_DC_PRIMARY, "primary dc for multi dc mode" },
+ { ES_MULTI_DC_SECONDARY, "secondary dc for multi dc mode" },
+};
+
+#ifdef CONFIG_ESWIN_MMU
+static const struct drm_prop_enum_list es_mmu_prefetch_enum_list[] = {
+ { ES_MMU_PREFETCH_DISABLE, "disable mmu prefetch" },
+ { ES_MMU_PREFETCH_ENABLE, "enable mmu prefetch" },
+};
+#endif
+
+struct es_crtc *es_crtc_create(struct drm_device *drm_dev,
+ struct es_dc_info *info)
+{
+ struct es_crtc *crtc;
+ int ret;
+
+ if (!info)
+ return NULL;
+
+ crtc = kzalloc(sizeof(struct es_crtc), GFP_KERNEL);
+ if (!crtc)
+ return NULL;
+
+ ret = drm_crtc_init_with_planes(drm_dev, &crtc->base, NULL, NULL,
+ &es_crtc_funcs,
+ info->name ? info->name : NULL);
+ if (ret)
+ goto err_free_crtc;
+
+ drm_crtc_helper_add(&crtc->base, &es_crtc_helper_funcs);
+
+ /* Set up the crtc properties */
+ if (info->pipe_sync) {
+ crtc->sync_mode = drm_property_create_enum(
+ drm_dev, 0, "SYNC_MODE", es_sync_mode_enum_list,
+ ARRAY_SIZE(es_sync_mode_enum_list));
+
+ if (!crtc->sync_mode)
+ goto err_cleanup_crts;
+
+ drm_object_attach_property(&crtc->base.base, crtc->sync_mode,
+ ES_SINGLE_DC);
+ }
+
+ if (info->gamma_size) {
+ ret = drm_mode_crtc_set_gamma_size(&crtc->base,
+ info->gamma_size);
+ if (ret)
+ goto err_cleanup_crts;
+
+ drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
+ info->gamma_size);
+ }
+
+ if (info->background) {
+ crtc->bg_color = drm_property_create_range(
+ drm_dev, 0, "BG_COLOR", 0, 0xffffffff);
+
+ if (!crtc->bg_color)
+ goto err_cleanup_crts;
+
+ drm_object_attach_property(&crtc->base.base, crtc->bg_color, 0);
+ }
+
+ crtc->dither = drm_property_create_bool(drm_dev, 0, "DITHER_ENABLED");
+ if (!crtc->dither)
+ goto err_cleanup_crts;
+
+ drm_object_attach_property(&crtc->base.base, crtc->dither, 0);
+
+#ifdef CONFIG_ESWIN_MMU
+ if (info->mmu_prefetch) {
+ crtc->mmu_prefetch = drm_property_create_enum(
+ drm_dev, 0, "MMU_PREFETCH", es_mmu_prefetch_enum_list,
+ ARRAY_SIZE(es_mmu_prefetch_enum_list));
+ if (!crtc->mmu_prefetch)
+ goto err_cleanup_crts;
+
+ drm_object_attach_property(&crtc->base.base, crtc->mmu_prefetch,
+ ES_MMU_PREFETCH_DISABLE);
+ }
+#endif
+
+ crtc->max_bpc = info->max_bpc;
+ crtc->color_formats = info->color_formats;
+ return crtc;
+
+err_cleanup_crts:
+ drm_crtc_cleanup(&crtc->base);
+
+err_free_crtc:
+ kfree(crtc);
+ return NULL;
+}
+
+void es_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow)
+{
+ struct es_crtc_state *es_crtc_state = to_es_crtc_state(crtc->state);
+
+ drm_crtc_handle_vblank(crtc);
+
+ es_crtc_state->underflow = underflow;
+}
diff --git a/drivers/gpu/drm/eswin/es_crtc.h b/drivers/gpu/drm/eswin/es_crtc.h
new file mode 100644
index 000000000000..5f7cf9009d4f
--- /dev/null
+++ b/drivers/gpu/drm/eswin/es_crtc.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Eswin Holdings Co., Ltd.
+ */
+
+#ifndef __ES_CRTC_H__
+#define __ES_CRTC_H__
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "es_type.h"
+
+struct es_crtc_funcs {
+ void (*enable)(struct device *dev, struct drm_crtc *crtc);
+ void (*disable)(struct device *dev);
+ bool (*mode_fixup)(struct device *dev,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
+ void (*set_gamma)(struct device *dev, struct drm_color_lut *lut,
+ unsigned int size);
+ void (*enable_gamma)(struct device *dev, bool enable);
+ void (*enable_vblank)(struct device *dev, bool enable);
+ void (*commit)(struct device *dev);
+};
+
+struct es_crtc_state {
+ struct drm_crtc_state base;
+
+ u32 sync_mode;
+ u32 output_fmt;
+ u32 bg_color;
+ u8 encoder_type;
+ u8 mmu_prefetch;
+ u8 bpp;
+ bool dither_enable;
+ bool underflow;
+};
+
+struct es_crtc {
+ struct drm_crtc base;
+ struct device *dev;
+ struct drm_pending_vblank_event *event;
+ unsigned int max_bpc;
+ unsigned int color_formats; /* supported color format */
+
+ struct drm_property *sync_mode;
+ struct drm_property *mmu_prefetch;
+ struct drm_property *dither;
+ struct drm_property *bg_color;
+
+ const struct es_crtc_funcs *funcs;
+};
+
+void es_crtc_destroy(struct drm_crtc *crtc);
+
+struct es_crtc *es_crtc_create(struct drm_device *drm_dev,
+ struct es_dc_info *info);
+void es_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow);
+
+static inline struct es_crtc *to_es_crtc(struct drm_crtc *crtc)
+{
+ return container_of(crtc, struct es_crtc, base);
+}
+
+static inline struct es_crtc_state *
+to_es_crtc_state(struct drm_crtc_state *state)
+{
+ return container_of(state, struct es_crtc_state, base);
+}
+#endif /* __ES_CRTC_H__ */
diff --git a/drivers/gpu/drm/eswin/es_dc.c b/drivers/gpu/drm/eswin/es_dc.c
new file mode 100644
index 000000000000..9e485f7edcc4
--- /dev/null
+++ b/drivers/gpu/drm/eswin/es_dc.c
@@ -0,0 +1,1083 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Eswin Holdings Co., Ltd.
+ */
+
+#include <linux/component.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/reset.h>
+#include <linux/delay.h>
+#include <linux/media-bus-format.h>
+
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_blend.h>
+
+#include "es_drm.h"
+#include "es_type.h"
+#include "es_dc_hw.h"
+#include "es_dc.h"
+#include "es_crtc.h"
+#include "es_drv.h"
+
+#define VO_ACLK_HIGHEST 800000000
+
+static inline void update_format(u32 format, struct dc_hw_fb *fb)
+{
+ u8 f;
+
+ switch (format) {
+ case DRM_FORMAT_XRGB4444:
+ case DRM_FORMAT_XBGR4444:
+ case DRM_FORMAT_RGBX4444:
+ case DRM_FORMAT_BGRX4444:
+ f = FORMAT_X4R4G4B4;
+ break;
+ case DRM_FORMAT_ARGB4444:
+ case DRM_FORMAT_RGBA4444:
+ case DRM_FORMAT_ABGR4444:
+ case DRM_FORMAT_BGRA4444:
+ f = FORMAT_A4R4G4B4;
+ break;
+ case DRM_FORMAT_XRGB1555:
+ case DRM_FORMAT_XBGR1555:
+ case DRM_FORMAT_RGBX5551:
+ case DRM_FORMAT_BGRX5551:
+ f = FORMAT_X1R5G5B5;
+ break;
+ case DRM_FORMAT_ARGB1555:
+ case DRM_FORMAT_RGBA5551:
+ case DRM_FORMAT_ABGR1555:
+ case DRM_FORMAT_BGRA5551:
+ f = FORMAT_A1R5G5B5;
+ break;
+ case DRM_FORMAT_BGR565:
+ case DRM_FORMAT_RGB565:
+ f = FORMAT_R5G6B5;
+ break;
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_XBGR8888:
+ case DRM_FORMAT_RGBX8888:
+ case DRM_FORMAT_BGRX8888:
+ f = FORMAT_X8R8G8B8;
+ break;
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_RGBA8888:
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_BGRA8888:
+ f = FORMAT_A8R8G8B8;
+ break;
+ case DRM_FORMAT_YUYV:
+ case DRM_FORMAT_YVYU:
+ f = FORMAT_YUY2;
+ break;
+ case DRM_FORMAT_UYVY:
+ case DRM_FORMAT_VYUY:
+ f = FORMAT_UYVY;
+ break;
+ case DRM_FORMAT_YVU420:
+ case DRM_FORMAT_YUV420:
+ f = FORMAT_YV12;
+ break;
+ case DRM_FORMAT_NV12:
+ case DRM_FORMAT_NV21:
+ f = FORMAT_NV12;
+ break;
+ case DRM_FORMAT_NV16:
+ case DRM_FORMAT_NV61:
+ f = FORMAT_NV16;
+ break;
+ case DRM_FORMAT_P010:
+ f = FORMAT_P010;
+ break;
+ case DRM_FORMAT_ARGB2101010:
+ case DRM_FORMAT_RGBA1010102:
+ case DRM_FORMAT_ABGR2101010:
+ case DRM_FORMAT_BGRA1010102:
+ f = FORMAT_A2R10G10B10;
+ break;
+ default:
+ f = FORMAT_A8R8G8B8;
+ break;
+ }
+ fb->format = f;
+}
+
+static inline void update_swizzle(u32 format, struct dc_hw_fb *fb)
+{
+ fb->swizzle = SWIZZLE_ARGB;
+ fb->uv_swizzle = 0;
+
+ switch (format) {
+ case DRM_FORMAT_RGBA4444:
+ case DRM_FORMAT_RGBX4444:
+ case DRM_FORMAT_RGBX5551:
+ case DRM_FORMAT_RGBX8888:
+ case DRM_FORMAT_RGBA5551:
+ case DRM_FORMAT_RGBA8888:
+ case DRM_FORMAT_RGBA1010102:
+ fb->swizzle = SWIZZLE_RGBA;
+ break;
+ case DRM_FORMAT_ABGR4444:
+ case DRM_FORMAT_XBGR4444:
+ case DRM_FORMAT_XBGR1555:
+ case DRM_FORMAT_XBGR8888:
+ case DRM_FORMAT_ABGR1555:
+ case DRM_FORMAT_BGR565:
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_ABGR2101010:
+ fb->swizzle = SWIZZLE_ABGR;
+ break;
+ case DRM_FORMAT_BGRA4444:
+ case DRM_FORMAT_BGRX4444:
+ case DRM_FORMAT_BGRX5551:
+ case DRM_FORMAT_BGRX8888:
+ case DRM_FORMAT_BGRA5551:
+ case DRM_FORMAT_BGRA8888:
+ case DRM_FORMAT_BGRA1010102:
+ fb->swizzle = SWIZZLE_BGRA;
+ break;
+ case DRM_FORMAT_YVYU:
+ case DRM_FORMAT_VYUY:
+ case DRM_FORMAT_NV21:
+ case DRM_FORMAT_NV61:
+ fb->uv_swizzle = 1;
+ break;
+ default:
+ break;
+ }
+}
+
+static inline u8 to_es_rotation(unsigned int rotation)
+{
+ u8 rot;
+
+ switch (rotation & DRM_MODE_REFLECT_MASK) {
+ case DRM_MODE_REFLECT_X:
+ rot = FLIP_X;
+ return rot;
+ case DRM_MODE_REFLECT_Y:
+ rot = FLIP_Y;
+ return rot;
+ case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
+ rot = FLIP_XY;
+ return rot;
+ default:
+ break;
+ }
+
+ switch (rotation & DRM_MODE_ROTATE_MASK) {
+ case DRM_MODE_ROTATE_0:
+ rot = ROT_0;
+ break;
+ case DRM_MODE_ROTATE_90:
+ rot = ROT_90;
+ break;
+ case DRM_MODE_ROTATE_180:
+ rot = ROT_180;
+ break;
+ case DRM_MODE_ROTATE_270:
+ rot = ROT_270;
+ break;
+ default:
+ rot = ROT_0;
+ break;
+ }
+
+ return rot;
+}
+
+static inline u8 to_es_yuv_color_space(u32 color_space)
+{
+ u8 cs;
+
+ switch (color_space) {
+ case DRM_COLOR_YCBCR_BT601:
+ cs = COLOR_SPACE_601;
+ break;
+ case DRM_COLOR_YCBCR_BT709:
+ cs = COLOR_SPACE_709;
+ break;
+ case DRM_COLOR_YCBCR_BT2020:
+ cs = COLOR_SPACE_2020;
+ break;
+ default:
+ cs = COLOR_SPACE_601;
+ break;
+ }
+
+ return cs;
+}
+
+static inline u8 to_es_tile_mode(u64 modifier)
+{
+ // DRM_FORMAT_MOD_ES_NORM_MODE_MASK 0x1F
+ return (u8)(modifier & 0x1F);
+}
+
+static void dc_deinit(struct device *dev)
+{
+ struct es_dc *dc = dev_get_drvdata(dev);
+
+ dc_hw_enable_interrupt(&dc->hw, 0);
+ dc_hw_deinit(&dc->hw);
+ clk_disable_unprepare(dc->cfg_clk);
+ clk_disable_unprepare(dc->pix_clk);
+ clk_disable_unprepare(dc->axi_clk);
+}
+
+static int dc_init(struct device *dev)
+{
+ struct es_dc *dc = dev_get_drvdata(dev);
+ int ret;
+ long rate;
+
+ dc->first_frame = true;
+
+ ret = clk_set_parent(dc->vo_mux, dc->spll0_fout1);
+ if (ret < 0) {
+ pr_err("DC: failed to set core clk parent: %d\n", ret);
+ return ret;
+ }
+
+ rate = clk_round_rate(dc->axi_clk, VO_ACLK_HIGHEST);
+ if (rate > 0) {
+ ret = clk_set_rate(dc->axi_clk, rate);
+ if (ret) {
+ pr_err("DC: failed to set axi clk: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = clk_prepare_enable(dc->cfg_clk);
+ if (ret < 0) {
+ dev_err(dev, "failed to prepare/enable cfg_clk\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(dc->pix_clk);
+ if (ret < 0) {
+ dev_err(dev, "failed to prepare/enable pix_clk\n");
+ goto err_unprepare_cfg_clk;
+ }
+
+ ret = clk_prepare_enable(dc->axi_clk);
+ if (ret < 0) {
+ dev_err(dev, "failed to prepare/enable axi_clk\n");
+ goto err_unprepare_pix_clk;
+ }
+
+ dc->pix_clk_rate = clk_get_rate(dc->pix_clk);
+
+ ret = dc_hw_init(&dc->hw);
+ if (ret) {
+ dev_err(dev, "failed to init DC HW\n");
+ goto err_unprepare_axi_clk;
+ }
+
+ return 0;
+
+err_unprepare_axi_clk:
+ clk_disable_unprepare(dc->axi_clk);
+err_unprepare_cfg_clk:
+ clk_disable_unprepare(dc->cfg_clk);
+err_unprepare_pix_clk:
+ clk_disable_unprepare(dc->pix_clk);
+ return ret;
+}
+
+static void es_dc_dump_enable(struct device *dev, dma_addr_t addr,
+ unsigned int pitch)
+{
+ struct es_dc *dc = dev_get_drvdata(dev);
+
+ dc_hw_enable_dump(&dc->hw, addr, pitch);
+}
+
+static void es_dc_dump_disable(struct device *dev)
+{
+ struct es_dc *dc = dev_get_drvdata(dev);
+
+ dc_hw_disable_dump(&dc->hw);
+}
+
+static void es_dc_enable(struct device *dev, struct drm_crtc *crtc)
+{
+ struct es_dc *dc = dev_get_drvdata(dev);
+ struct es_crtc_state *crtc_state = to_es_crtc_state(crtc->state);
+ struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+ struct dc_hw_display display;
+
+ display.bus_format = crtc_state->output_fmt;
+ display.h_active = mode->hdisplay;
+ display.h_total = mode->htotal;
+ display.h_sync_start = mode->hsync_start;
+ display.h_sync_end = mode->hsync_end;
+ if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+ display.h_sync_polarity = true;
+ else
+ display.h_sync_polarity = false;
+
+ display.v_active = mode->vdisplay;
+ display.v_total = mode->vtotal;
+ display.v_sync_start = mode->vsync_start;
+ display.v_sync_end = mode->vsync_end;
+ if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+ display.v_sync_polarity = true;
+ else
+ display.v_sync_polarity = false;
+
+ display.sync_mode = crtc_state->sync_mode;
+ display.bg_color = crtc_state->bg_color;
+ display.dither_enable = crtc_state->dither_enable;
+
+ display.enable = true;
+
+ if (dc->pix_clk_rate != mode->clock) {
+ clk_set_rate(dc->pix_clk, mode->clock * 1000);
+ dc->pix_clk_rate = mode->clock;
+ }
+
+ if (crtc_state->encoder_type == DRM_MODE_ENCODER_DSI ||
+ crtc_state->encoder_type == DRM_MODE_ENCODER_VIRTUAL ||
+ crtc_state->encoder_type == DRM_MODE_ENCODER_NONE)
+ dc_hw_set_out(&dc->hw, OUT_DPI);
+ else
+ dc_hw_set_out(&dc->hw, OUT_DP);
+
+#ifdef CONFIG_ESWIN_MMU
+ if (crtc_state->mmu_prefetch == ES_MMU_PREFETCH_ENABLE)
+ dc_hw_enable_mmu_prefetch(&dc->hw, true);
+ else
+ dc_hw_enable_mmu_prefetch(&dc->hw, false);
+#endif
+
+ dc_hw_setup_display(&dc->hw, &display);
+}
+
+static void es_dc_disable(struct device *dev)
+{
+ struct es_dc *dc = dev_get_drvdata(dev);
+ struct dc_hw_display display;
+
+ display.enable = false;
+
+ dc_hw_setup_display(&dc->hw, &display);
+}
+
+static bool es_dc_mode_fixup(struct device *dev,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct es_dc *dc = dev_get_drvdata(dev);
+ long clk_rate;
+
+ if (dc->pix_clk) {
+ clk_rate = clk_round_rate(dc->pix_clk,
+ adjusted_mode->clock * 1000);
+ adjusted_mode->clock = DIV_ROUND_UP(clk_rate, 1000);
+ }
+
+ return true;
+}
+
+static void es_dc_set_gamma(struct device *dev, struct drm_color_lut *lut,
+ unsigned int size)
+{
+ struct es_dc *dc = dev_get_drvdata(dev);
+ u16 i, r, g, b;
+ u8 bits;
+
+ if (size != dc->hw.info->gamma_size) {
+ dev_err(dev, "gamma size does not match!\n");
+ return;
+ }
+
+ bits = dc->hw.info->gamma_bits;
+ for (i = 0; i < size; i++) {
+ r = drm_color_lut_extract(lut[i].red, bits);
+ g = drm_color_lut_extract(lut[i].green, bits);
+ b = drm_color_lut_extract(lut[i].blue, bits);
+ dc_hw_update_gamma(&dc->hw, i, r, g, b);
+ }
+}
+
+static void es_dc_enable_gamma(struct device *dev, bool enable)
+{
+ struct es_dc *dc = dev_get_drvdata(dev);
+
+ dc_hw_enable_gamma(&dc->hw, enable);
+}
+
+static void es_dc_enable_vblank(struct device *dev, bool enable)
+{
+ struct es_dc *dc = dev_get_drvdata(dev);
+
+ dc_hw_enable_interrupt(&dc->hw, enable);
+}
+
+static u32 calc_factor(u32 src, u32 dest)
+{
+ u32 factor = 1 << 16;
+
+ if ((src > 1) && (dest > 1))
+ factor = ((src - 1) << 16) / (dest - 1);
+
+ return factor;
+}
+
+static void update_scale(struct dc_hw_scale *scale, int width, int height,
+ int dst_w, int dst_h, unsigned int rotation,
+ struct dc_hw_roi *roi)
+{
+ int src_w, src_h, temp;
+
+ scale->enable = false;
+ if (roi->enable) {
+ src_w = roi->width;
+ src_h = roi->height;
+ } else {
+ src_w = width;
+ src_h = height;
+ }
+
+ if (drm_rotation_90_or_270(rotation)) {
+ temp = src_w;
+ src_w = src_h;
+ src_h = temp;
+ }
+
+ if (src_w != dst_w) {
+ scale->scale_factor_x = calc_factor(src_w, dst_w);
+ scale->enable = true;
+ } else {
+ scale->scale_factor_x = 1 << 16;
+ }
+ if (src_h != dst_h) {
+ scale->scale_factor_y = calc_factor(src_h, dst_h);
+ scale->enable = true;
+ } else {
+ scale->scale_factor_y = 1 << 16;
+ }
+}
+
+static void update_fb(struct es_plane *plane, struct dc_hw_fb *fb)
+{
+ struct drm_plane_state *state = plane->base.state;
+ struct drm_framebuffer *drm_fb = state->fb;
+ struct drm_rect *src = &state->src;
+
+ fb->y_address = plane->dma_addr[0];
+ fb->y_stride = drm_fb->pitches[0];
+ if (drm_fb->format->format == DRM_FORMAT_YVU420) {
+ fb->u_address = plane->dma_addr[2];
+ fb->v_address = plane->dma_addr[1];
+ fb->u_stride = drm_fb->pitches[2];
+ fb->v_stride = drm_fb->pitches[1];
+ } else {
+ fb->u_address = plane->dma_addr[1];
+ fb->v_address = plane->dma_addr[2];
+ fb->u_stride = drm_fb->pitches[1];
+ fb->v_stride = drm_fb->pitches[2];
+ }
+ fb->width = drm_rect_width(src) >> 16;
+ fb->height = drm_rect_height(src) >> 16;
+ fb->tile_mode = to_es_tile_mode(drm_fb->modifier);
+ fb->rotation = to_es_rotation(state->rotation);
+ fb->yuv_color_space = to_es_yuv_color_space(state->color_encoding);
+ fb->enable = state->visible;
+ update_format(drm_fb->format->format, fb);
+ update_swizzle(drm_fb->format->format, fb);
+}
+
+static void update_degamma(struct es_dc *dc, struct es_plane *plane,
+ struct es_plane_state *plane_state)
+{
+ dc_hw_update_degamma(&dc->hw, plane->id, plane_state->degamma);
+ plane_state->degamma_changed = false;
+}
+
+void update_roi(struct es_dc *dc, enum dc_hw_plane_id id,
+ struct es_plane_state *plane_state, struct dc_hw_roi *roi,
+ struct dc_hw_fb *fb)
+{
+ struct drm_es_roi *data;
+ u16 src_w = fb->width;
+ u16 src_h = fb->height;
+
+ if (plane_state->roi) {
+ data = (struct drm_es_roi *)plane_state->roi->data;
+ if (data->enable) {
+ roi->x = data->roi_x;
+ roi->y = data->roi_y;
+ roi->width = (data->roi_w + data->roi_x > src_w) ?
+ (src_w - data->roi_x) :
+ data->roi_w;
+ roi->height = (data->roi_h + data->roi_y > src_h) ?
+ (src_h - data->roi_y) :
+ data->roi_h;
+ roi->enable = true;
+ } else {
+ roi->enable = false;
+ roi->width = src_w;
+ roi->height = src_h;
+ }
+
+ dc_hw_update_roi(&dc->hw, id, roi);
+ } else {
+ roi->enable = false;
+ }
+}
+
+static void update_color_mgmt(struct es_dc *dc, u8 id, struct dc_hw_fb *fb,
+ struct es_plane_state *plane_state)
+{
+ struct drm_es_color_mgmt *data;
+ struct dc_hw_colorkey colorkey;
+
+ if (plane_state->color_mgmt) {
+ data = plane_state->color_mgmt->data;
+
+ fb->clear_enable = data->clear_enable;
+ fb->clear_value = data->clear_value;
+
+ if (data->colorkey > data->colorkey_high)
+ data->colorkey = data->colorkey_high;
+
+ colorkey.colorkey = data->colorkey;
+ colorkey.colorkey_high = data->colorkey_high;
+ colorkey.transparency = (data->transparency) ?
+ DC_TRANSPARENCY_KEY :
+ DC_TRANSPARENCY_OPAQUE;
+ dc_hw_update_colorkey(&dc->hw, id, &colorkey);
+ }
+}
+
+static void update_primary_plane(struct es_dc *dc, struct es_plane *plane)
+{
+ struct dc_hw_fb fb = { 0 };
+ struct dc_hw_scale scale;
+ struct drm_plane_state *state = plane->base.state;
+ struct es_plane_state *plane_state = to_es_plane_state(state);
+ struct drm_crtc *crtc = state->crtc;
+ struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+ struct dc_hw_roi roi;
+
+ update_fb(plane, &fb);
+
+ update_roi(dc, plane->id, plane_state, &roi, &fb);
+
+ update_scale(&scale, fb.width, fb.height, mode->hdisplay,
+ mode->vdisplay, state->rotation, &roi);
+
+ if (plane_state->degamma_changed)
+ update_degamma(dc, plane, plane_state);
+
+ update_color_mgmt(dc, plane->id, &fb, plane_state);
+
+ dc_hw_update_plane(&dc->hw, plane->id, &fb, &scale);
+}
+
+static void update_overlay_plane(struct es_dc *dc, struct es_plane *plane)
+{
+ struct dc_hw_fb fb = { 0 };
+ struct dc_hw_scale scale;
+ struct dc_hw_position pos;
+ struct dc_hw_blend blend;
+ struct drm_plane_state *state = plane->base.state;
+ struct es_plane_state *plane_state = to_es_plane_state(state);
+ struct drm_rect *dest = &state->dst;
+ struct dc_hw_roi roi;
+
+ update_fb(plane, &fb);
+ update_roi(dc, plane->id, plane_state, &roi, &fb);
+ update_scale(&scale, fb.width, fb.height, drm_rect_width(dest),
+ drm_rect_height(dest), state->rotation, &roi);
+
+ if (plane_state->degamma_changed)
+ update_degamma(dc, plane, plane_state);
+
+ update_color_mgmt(dc, plane->id, &fb, plane_state);
+
+ dc_hw_update_plane(&dc->hw, plane->id, &fb, &scale);
+
+ pos.start_x = dest->x1;
+ pos.start_y = dest->y1;
+ pos.end_x = dest->x2;
+ pos.end_y = dest->y2;
+ dc_hw_set_position(&dc->hw, &pos);
+
+ blend.alpha = (u8)(state->alpha >> 8);
+ blend.blend_mode = (u8)(state->pixel_blend_mode);
+ dc_hw_set_blend(&dc->hw, &blend);
+}
+
+static void update_cursor_plane(struct es_dc *dc, struct es_plane *plane)
+{
+ struct drm_plane_state *state = plane->base.state;
+ struct drm_framebuffer *drm_fb = state->fb;
+ struct dc_hw_cursor cursor;
+
+ cursor.address = plane->dma_addr[0];
+ cursor.x = state->crtc_x;
+ cursor.y = state->crtc_y;
+ cursor.hot_x = drm_fb->hot_x;
+ cursor.hot_y = drm_fb->hot_y;
+ cursor.enable = true;
+
+ dc_hw_update_cursor(&dc->hw, &cursor);
+}
+
+static void es_dc_update_plane(struct device *dev, struct es_plane *plane)
+{
+ struct es_dc *dc = dev_get_drvdata(dev);
+ enum drm_plane_type type = plane->base.type;
+
+ switch (type) {
+ case DRM_PLANE_TYPE_PRIMARY:
+ update_primary_plane(dc, plane);
+ break;
+ case DRM_PLANE_TYPE_OVERLAY:
+ update_overlay_plane(dc, plane);
+ break;
+ case DRM_PLANE_TYPE_CURSOR:
+ update_cursor_plane(dc, plane);
+ break;
+ default:
+ break;
+ }
+}
+
+static void es_dc_disable_plane(struct device *dev, struct es_plane *plane)
+{
+ struct es_dc *dc = dev_get_drvdata(dev);
+ enum drm_plane_type type = plane->base.type;
+ struct dc_hw_fb fb = { 0 };
+ struct dc_hw_cursor cursor = { 0 };
+
+ switch (type) {
+ case DRM_PLANE_TYPE_PRIMARY:
+ case DRM_PLANE_TYPE_OVERLAY:
+ fb.enable = false;
+ dc_hw_update_plane(&dc->hw, plane->id, &fb, NULL);
+ break;
+ case DRM_PLANE_TYPE_CURSOR:
+ cursor.enable = false;
+ dc_hw_update_cursor(&dc->hw, &cursor);
+ break;
+ default:
+ break;
+ }
+}
+
+static bool es_dc_mod_supported(const struct es_plane_info *plane_info,
+ u64 modifier)
+{
+ const u64 *mods;
+
+ if (plane_info->modifiers == NULL)
+ return false;
+
+ for (mods = plane_info->modifiers; *mods != DRM_FORMAT_MOD_INVALID;
+ mods++) {
+ if (*mods == modifier)
+ return true;
+ }
+
+ return false;
+}
+
+static int es_dc_check_plane(struct device *dev, struct es_plane *plane,
+ struct drm_plane_state *state)
+{
+ struct es_dc *dc = dev_get_drvdata(dev);
+ struct drm_framebuffer *fb = state->fb;
+ const struct es_plane_info *plane_info;
+ struct drm_crtc *crtc = state->crtc;
+ struct drm_crtc_state *crtc_state;
+
+ plane_info = &dc->hw.info->planes[plane->id];
+ if (plane_info == NULL)
+ return -EINVAL;
+
+ if (fb->width < plane_info->min_width ||
+ fb->width > plane_info->max_width ||
+ fb->height < plane_info->min_height ||
+ fb->height > plane_info->max_height)
+ dev_err_once(dev, "buffer size may not support on plane%d.\n",
+ plane->id);
+
+ if ((plane->base.type != DRM_PLANE_TYPE_CURSOR) &&
+ (!es_dc_mod_supported(plane_info, fb->modifier))) {
+ dev_err(dev, "unsupported modifier on plane%d.\n", plane->id);
+ return -EINVAL;
+ }
+
+ crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc);
+ if (IS_ERR(crtc_state))
+ return -EINVAL;
+
+ return drm_atomic_helper_check_plane_state(state, crtc_state,
+ plane_info->min_scale,
+ plane_info->max_scale, true,
+ true);
+}
+
+static irqreturn_t dc_isr(int irq, void *data)
+{
+ struct es_dc *dc = data;
+
+ dc_hw_get_interrupt(&dc->hw);
+
+ if (!dc->dc_initialized) {
+ return IRQ_HANDLED;
+ }
+
+ es_crtc_handle_vblank(&dc->crtc->base, dc_hw_check_underflow(&dc->hw));
+
+ return IRQ_HANDLED;
+}
+
+static void es_dc_commit(struct device *dev)
+{
+ struct es_dc *dc = dev_get_drvdata(dev);
+
+#ifdef CONFIG_ESWIN_MMU
+ dc_mmu_flush(&dc->hw);
+#endif
+
+ if (!dc->first_frame) {
+ if (dc_hw_flip_in_progress(&dc->hw))
+ udelay(100);
+
+ dc_hw_enable_shadow_register(&dc->hw, false);
+ }
+
+ dc_hw_commit(&dc->hw);
+
+ if (dc->first_frame)
+ dc->first_frame = false;
+
+ if (!dc->dc_initialized)
+ dc->dc_initialized = true;
+
+ dc_hw_enable_shadow_register(&dc->hw, true);
+}
+
+static const struct es_crtc_funcs dc_crtc_funcs = {
+ .enable = es_dc_enable,
+ .disable = es_dc_disable,
+ .mode_fixup = es_dc_mode_fixup,
+ .set_gamma = es_dc_set_gamma,
+ .enable_gamma = es_dc_enable_gamma,
+ .enable_vblank = es_dc_enable_vblank,
+ .commit = es_dc_commit,
+};
+
+static const struct es_plane_funcs dc_plane_funcs = {
+ .update = es_dc_update_plane,
+ .disable = es_dc_disable_plane,
+ .check = es_dc_check_plane,
+};
+
+static const struct es_dc_funcs dc_funcs = {
+ .dump_enable = es_dc_dump_enable,
+ .dump_disable = es_dc_dump_disable,
+};
+
+static int dc_bind(struct device *dev, struct device *master, void *data)
+{
+ struct drm_device *drm_dev = data;
+#ifdef CONFIG_ESWIN_MMU
+ struct es_drm_private *priv = drm_dev->dev_private;
+#endif
+ struct es_dc *dc = dev_get_drvdata(dev);
+ struct device_node *port;
+ struct es_crtc *crtc;
+ struct es_dc_info *dc_info;
+ struct es_plane *plane;
+ struct drm_plane *drm_plane, *tmp;
+ struct es_plane_info *plane_info;
+ int i, ret;
+
+ if (!drm_dev || !dc) {
+ dev_err(dev, "devices are not created.\n");
+ return -ENODEV;
+ }
+
+ ret = dc_init(dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to initialize DC hardware.\n");
+ return ret;
+ }
+
+#ifdef CONFIG_ESWIN_MMU
+ ret = dc_mmu_construct(priv->dma_dev, &priv->mmu);
+ if (ret) {
+ dev_err(dev, "failed to construct DC MMU\n");
+ goto err_clean_dc;
+ }
+
+ ret = dc_hw_mmu_init(&dc->hw, priv->mmu);
+ if (ret) {
+ dev_err(dev, "failed to init DC MMU\n");
+ goto err_clean_dc;
+ }
+#endif
+
+ ret = es_drm_iommu_attach_device(drm_dev, dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to attached iommu device.\n");
+ goto err_clean_dc;
+ }
+
+ port = of_get_child_by_name(dev->of_node, "port");
+ if (!port) {
+ dev_err(dev, "no port node found\n");
+ goto err_detach_dev;
+ }
+ of_node_put(port);
+
+ dc_info = dc->hw.info;
+ crtc = es_crtc_create(drm_dev, dc_info);
+ if (!crtc) {
+ dev_err(dev, "Failed to create CRTC.\n");
+ ret = -ENOMEM;
+ goto err_detach_dev;
+ }
+
+ crtc->base.port = port;
+ crtc->dev = dev;
+ crtc->funcs = &dc_crtc_funcs;
+
+ for (i = 0; i < dc_info->plane_num; i++) {
+ plane_info = (struct es_plane_info *)&dc_info->planes[i];
+
+ plane = es_plane_create(drm_dev, plane_info,
+ drm_crtc_mask(&crtc->base));
+ if (!plane)
+ goto err_cleanup_planes;
+
+ plane->funcs = &dc_plane_funcs;
+
+ if (plane_info->type == DRM_PLANE_TYPE_PRIMARY) {
+ crtc->base.primary = &plane->base;
+ drm_dev->mode_config.min_width = plane_info->min_width;
+ drm_dev->mode_config.min_height =
+ plane_info->min_height;
+ drm_dev->mode_config.max_width = plane_info->max_width;
+ drm_dev->mode_config.max_height =
+ plane_info->max_height;
+ }
+
+ if (plane_info->type == DRM_PLANE_TYPE_CURSOR) {
+ crtc->base.cursor = &plane->base;
+ drm_dev->mode_config.cursor_width =
+ plane_info->max_width;
+ drm_dev->mode_config.cursor_height =
+ plane_info->max_height;
+ }
+ }
+
+ dc->crtc = crtc;
+ dc->funcs = &dc_funcs;
+
+ es_drm_update_pitch_alignment(drm_dev, dc_info->pitch_alignment);
+
+ return 0;
+
+err_cleanup_planes:
+ list_for_each_entry_safe (drm_plane, tmp,
+ &drm_dev->mode_config.plane_list, head)
+ if (drm_plane->possible_crtcs == drm_crtc_mask(&crtc->base))
+ es_plane_destory(drm_plane);
+
+ es_crtc_destroy(&crtc->base);
+err_detach_dev:
+ es_drm_iommu_detach_device(drm_dev, dev);
+err_clean_dc:
+ dc_deinit(dev);
+ return ret;
+}
+
+static void dc_unbind(struct device *dev, struct device *master, void *data)
+{
+ struct drm_device *drm_dev = data;
+
+ dc_deinit(dev);
+
+ es_drm_iommu_detach_device(drm_dev, dev);
+}
+
+const struct component_ops dc_component_ops = {
+ .bind = dc_bind,
+ .unbind = dc_unbind,
+};
+
+static const struct of_device_id dc_driver_dt_match[] = {
+ {
+ .compatible = "eswin,dc",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, dc_driver_dt_match);
+
+static int dc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct es_dc *dc;
+ int irq, ret;
+
+ dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL);
+ if (!dc)
+ return -ENOMEM;
+
+ dc->hw.hi_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(dc->hw.hi_base))
+ return PTR_ERR(dc->hw.hi_base);
+
+#ifdef CONFIG_ESWIN_MMU
+ dc->hw.mmu_base = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(dc->hw.mmu_base))
+ return PTR_ERR(dc->hw.mmu_base);
+#endif
+
+ dc->hw.reg_base = devm_platform_ioremap_resource(pdev, 2);
+ if (IS_ERR(dc->hw.reg_base))
+ return PTR_ERR(dc->hw.reg_base);
+
+ irq = platform_get_irq(pdev, 0);
+ ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc);
+ if (ret < 0) {
+ dev_err(dev, "Failed to install irq:%u.\n", irq);
+ return ret;
+ }
+
+ dc->vo_mux = devm_clk_get(dev, "vo_mux");
+ if (IS_ERR(dc->vo_mux)) {
+ ret = PTR_ERR(dc->vo_mux);
+ dev_err(dev, "failed to get vo_mux: %d\n", ret);
+ return ret;
+ }
+
+ dc->spll0_fout1 = devm_clk_get_optional(dev, "spll0_fout1");
+ if (IS_ERR(dc->spll0_fout1)) {
+ dev_err(dev, "failed to get spll0_fout1 source\n");
+ return PTR_ERR(dc->spll0_fout1);
+ }
+
+ dc->cfg_clk = devm_clk_get_optional(dev, "cfg_clk");
+ if (IS_ERR(dc->cfg_clk)) {
+ dev_err(dev, "failed to get cfg_clk source\n");
+ return PTR_ERR(dc->cfg_clk);
+ }
+
+ dc->pix_clk = devm_clk_get_optional(dev, "pix_clk");
+ if (IS_ERR(dc->pix_clk)) {
+ dev_err(dev, "failed to get pix_clk source\n");
+ return PTR_ERR(dc->pix_clk);
+ }
+
+ dc->axi_clk = devm_clk_get_optional(dev, "axi_clk");
+ if (IS_ERR(dc->axi_clk)) {
+ dev_err(dev, "failed to get axi_clk source\n");
+ return PTR_ERR(dc->axi_clk);
+ }
+
+ dc->vo_arst = devm_reset_control_get_optional(dev, "vo_arst");
+ if (IS_ERR_OR_NULL(dc->vo_arst)) {
+ dev_err(dev, "Failed to vo_arst handle\n");
+ return PTR_ERR(dc->vo_arst);
+ }
+
+ dc->vo_prst = devm_reset_control_get_optional(dev, "vo_prst");
+ if (IS_ERR_OR_NULL(dc->vo_prst)) {
+ dev_err(dev, "Failed to vo_prst handle\n");
+ return PTR_ERR(dc->vo_prst);
+ }
+
+ dc->dc_arst = devm_reset_control_get_optional(dev, "dc_arst");
+ if (IS_ERR_OR_NULL(dc->dc_arst)) {
+ dev_err(dev, "Failed to dc_arst handle\n");
+ return PTR_ERR(dc->dc_arst);
+ }
+
+ dc->dc_prst = devm_reset_control_get_optional(dev, "dc_prst");
+ if (IS_ERR_OR_NULL(dc->dc_prst)) {
+ dev_err(dev, "Failed to dc_prst handle\n");
+ return PTR_ERR(dc->dc_prst);
+ }
+
+ /* reset dc first to ensure no data on axi bus */
+ if (dc->dc_arst) {
+ ret = reset_control_reset(dc->dc_arst);
+ WARN_ON(0 != ret);
+ }
+
+ if (dc->dc_prst) {
+ ret = reset_control_reset(dc->dc_prst);
+ WARN_ON(0 != ret);
+ }
+
+ if (dc->vo_arst) {
+ ret = reset_control_reset(dc->vo_arst);
+ WARN_ON(0 != ret);
+ }
+
+ if (dc->vo_prst) {
+ ret = reset_control_reset(dc->vo_prst);
+ WARN_ON(0 != ret);
+ }
+
+ dev_set_drvdata(dev, dc);
+
+ return component_add(dev, &dc_component_ops);
+}
+
+static int dc_remove(struct platform_device *pdev)
+{
+ int ret;
+ struct device *dev = &pdev->dev;
+ struct es_dc *dc = dev_get_drvdata(dev);
+
+ component_del(dev, &dc_component_ops);
+
+ if (dc->dc_arst) {
+ ret = reset_control_assert(dc->dc_arst);
+ WARN_ON(0 != ret);
+ }
+
+ if (dc->dc_prst) {
+ ret = reset_control_assert(dc->dc_prst);
+ WARN_ON(0 != ret);
+ }
+
+ if (dc->vo_arst) {
+ ret = reset_control_assert(dc->vo_arst);
+ WARN_ON(0 != ret);
+ }
+
+ if (dc->vo_prst) {
+ ret = reset_control_assert(dc->vo_prst);
+ WARN_ON(0 != ret);
+ }
+
+ dev_set_drvdata(dev, NULL);
+
+ return 0;
+}
+
+struct platform_driver dc_platform_driver = {
+ .probe = dc_probe,
+ .remove = dc_remove,
+ .driver = {
+ .name = "es-dc",
+ .of_match_table = of_match_ptr(dc_driver_dt_match),
+ },
+};
+
+MODULE_DESCRIPTION("Eswin DC Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/eswin/es_dc.h b/drivers/gpu/drm/eswin/es_dc.h
new file mode 100644
index 000000000000..3a20987aa799
--- /dev/null
+++ b/drivers/gpu/drm/eswin/es_dc.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Eswin Holdings Co., Ltd.
+ */
+
+#ifndef __ES_DC_H__
+#define __ES_DC_H__
+
+#include <linux/mm_types.h>
+#include <drm/drm_modes.h>
+
+#include "es_plane.h"
+#include "es_crtc.h"
+#include "es_dc_hw.h"
+#ifdef CONFIG_ESWIN_MMU
+#include "es_dc_mmu.h"
+#endif
+
+struct es_dc_funcs {
+ void (*dump_enable)(struct device *dev, dma_addr_t addr,
+ unsigned int pitch);
+ void (*dump_disable)(struct device *dev);
+};
+
+struct es_dc {
+ struct es_crtc *crtc;
+ struct dc_hw hw;
+
+ struct clk *vo_mux;
+ struct clk *spll0_fout1;
+ struct clk *cfg_clk;
+ struct clk *pix_clk;
+ struct clk *axi_clk;
+ unsigned int pix_clk_rate; /* in KHz */
+
+ struct reset_control *vo_arst;
+ struct reset_control *vo_prst;
+ struct reset_control *dc_arst;
+ struct reset_control *dc_prst;
+
+ bool first_frame;
+ bool dc_initialized;
+
+ const struct es_dc_funcs *funcs;
+};
+
+extern struct platform_driver dc_platform_driver;
+#endif /* __ES_DC_H__ */
diff --git a/drivers/gpu/drm/eswin/es_dc_hw.c b/drivers/gpu/drm/eswin/es_dc_hw.c
new file mode 100644
index 000000000000..010de5b65250
--- /dev/null
+++ b/drivers/gpu/drm/eswin/es_dc_hw.c
@@ -0,0 +1,1614 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Eswin Holdings Co., Ltd.
+ */
+
+#include <linux/io.h>
+#include <linux/bits.h>
+#include <linux/media-bus-format.h>
+#include <linux/delay.h>
+
+#include <drm/drm_fourcc.h>
+#include <drm/drm_blend.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_atomic_helper.h>
+
+#include "es_drm.h"
+#include "es_type.h"
+#include "es_dc_hw.h"
+
+static const u32 horKernel[] = {
+ 0x00000000, 0x20000000, 0x00002000, 0x00000000, 0x00000000, 0x00000000,
+ 0x23fd1c03, 0x00000000, 0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
+ 0x00000000, 0x00000000, 0x00000000, 0x2b981468, 0x00000000, 0x00000000,
+ 0x00000000, 0x10f00000, 0x00002f10, 0x00000000, 0x00000000, 0x00000000,
+ 0x32390dc7, 0x00000000, 0x00000000, 0x00000000, 0x0af50000, 0x0000350b,
+ 0x00000000, 0x00000000, 0x00000000, 0x3781087f, 0x00000000, 0x00000000,
+ 0x00000000, 0x06660000, 0x0000399a, 0x00000000, 0x00000000, 0x00000000,
+ 0x3b5904a7, 0x00000000, 0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
+ 0x00000000, 0x00000000, 0x00000000, 0x3de1021f, 0x00000000, 0x00000000,
+ 0x00000000, 0x01470000, 0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
+ 0x3f5300ad, 0x00000000, 0x00000000, 0x00000000, 0x00480000, 0x00003fb8,
+ 0x00000000, 0x00000000, 0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00004000, 0x00000000, 0x00000000, 0x00000000,
+ 0x20002000, 0x00000000, 0x00000000, 0x00000000, 0x1c030000, 0x000023fd,
+ 0x00000000, 0x00000000, 0x00000000, 0x27e1181f, 0x00000000, 0x00000000,
+ 0x00000000, 0x14680000, 0x00002b98, 0x00000000, 0x00000000, 0x00000000,
+ 0x2f1010f0, 0x00000000, 0x00000000, 0x00000000, 0x0dc70000, 0x00003239,
+ 0x00000000, 0x00000000, 0x00000000, 0x350b0af5, 0x00000000, 0x00000000,
+ 0x00000000, 0x087f0000, 0x00003781, 0x00000000, 0x00000000, 0x00000000,
+ 0x399a0666, 0x00000000, 0x00000000, 0x00000000, 0x04a70000, 0x00003b59,
+ 0x00000000, 0x00000000, 0x00000000, 0x3cc4033c, 0x00000000, 0x00000000,
+ 0x00000000, 0x021f0000,
+};
+#define H_COEF_SIZE (sizeof(horKernel) / sizeof(u32))
+
+static const u32 verKernel[] = {
+ 0x00000000, 0x20000000, 0x00002000, 0x00000000, 0x00000000, 0x00000000,
+ 0x23fd1c03, 0x00000000, 0x00000000, 0x00000000, 0x181f0000, 0x000027e1,
+ 0x00000000, 0x00000000, 0x00000000, 0x2b981468, 0x00000000, 0x00000000,
+ 0x00000000, 0x10f00000, 0x00002f10, 0x00000000, 0x00000000, 0x00000000,
+ 0x32390dc7, 0x00000000, 0x00000000, 0x00000000, 0x0af50000, 0x0000350b,
+ 0x00000000, 0x00000000, 0x00000000, 0x3781087f, 0x00000000, 0x00000000,
+ 0x00000000, 0x06660000, 0x0000399a, 0x00000000, 0x00000000, 0x00000000,
+ 0x3b5904a7, 0x00000000, 0x00000000, 0x00000000, 0x033c0000, 0x00003cc4,
+ 0x00000000, 0x00000000, 0x00000000, 0x3de1021f, 0x00000000, 0x00000000,
+ 0x00000000, 0x01470000, 0x00003eb9, 0x00000000, 0x00000000, 0x00000000,
+ 0x3f5300ad, 0x00000000, 0x00000000, 0x00000000, 0x00480000, 0x00003fb8,
+ 0x00000000, 0x00000000, 0x00000000, 0x3fef0011, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00004000, 0x00000000, 0xcdcd0000, 0xfdfdfdfd,
+ 0xabababab, 0xabababab, 0x00000000, 0x00000000, 0x5ff5f456, 0x000f5f58,
+ 0x02cc6c78, 0x02cc0c28, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+ 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+ 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+ 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+ 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+ 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+ 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee,
+ 0xfeeefeee, 0xfeeefeee,
+};
+#define V_COEF_SIZE (sizeof(verKernel) / sizeof(u32))
+
+/*
+ * RGB 709->2020 conversion parameters
+ */
+static u16 RGB2RGB[RGB_TO_RGB_TABLE_SIZE] = { 10279, 5395, 709, 1132, 15065,
+ 187, 269, 1442, 14674 };
+
+/*
+ * YUV601 to RGB conversion parameters
+ * YUV2RGB[0] - [8] : C0 - C8;
+ * YUV2RGB[9] - [11]: D0 - D2;
+ * YUV2RGB[12] - [13]: Y clamp min & max calue;
+ * YUV2RGB[14] - [15]: UV clamp min & max calue;
+ */
+static s32 YUV601_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
+ 1196, 0, 1640, 1196, -404, -836, 1196, 2076,
+ 0, -916224, 558336, -1202944, 64, 940, 64, 960
+};
+
+/*
+ * YUV709 to RGB conversion parameters
+ * YUV2RGB[0] - [8] : C0 - C8;
+ * YUV2RGB[9] - [11]: D0 - D2;
+ * YUV2RGB[12] - [13]: Y clamp min & max calue;
+ * YUV2RGB[14] - [15]: UV clamp min & max calue;
+ */
+static s32 YUV709_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
+ 1196, 0, 1844, 1196, -220, -548, 1196, 2172,
+ 0, -1020672, 316672, -1188608, 64, 940, 64, 960
+};
+
+/*
+ * YUV2020 to RGB conversion parameters
+ * YUV2RGB[0] - [8] : C0 - C8;
+ * YUV2RGB[9] - [11]: D0 - D2;
+ * YUV2RGB[12] - [13]: Y clamp min & max calue;
+ * YUV2RGB[14] - [15]: UV clamp min & max calue;
+ */
+static s32 YUV2020_2RGB[YUV_TO_RGB_TABLE_SIZE] = {
+ 1196, 0, 1724, 1196, -192, -668, 1196, 2200,
+ 0, -959232, 363776, -1202944, 64, 940, 64, 960
+};
+
+/*
+ * RGB to YUV2020 conversion parameters
+ * RGB2YUV[0] - [8] : C0 - C8;
+ * RGB2YUV[9] - [11]: D0 - D2;
+ */
+static s16 RGB2YUV[RGB_TO_YUV_TABLE_SIZE] = { 230, 594, 52, -125, -323, 448,
+ 448, -412, -36, 64, 512, 512 };
+
+/*
+ * Degamma table for 709 color space data.
+ */
+static u16 DEGAMMA_709[DEGAMMA_SIZE] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0005, 0x0007,
+ 0x000a, 0x000d, 0x0011, 0x0015, 0x0019, 0x001e, 0x0024, 0x002a, 0x0030,
+ 0x0038, 0x003f, 0x0048, 0x0051, 0x005a, 0x0064, 0x006f, 0x007b, 0x0087,
+ 0x0094, 0x00a1, 0x00af, 0x00be, 0x00ce, 0x00de, 0x00ef, 0x0101, 0x0114,
+ 0x0127, 0x013b, 0x0150, 0x0166, 0x017c, 0x0193, 0x01ac, 0x01c4, 0x01de,
+ 0x01f9, 0x0214, 0x0230, 0x024d, 0x026b, 0x028a, 0x02aa, 0x02ca, 0x02ec,
+ 0x030e, 0x0331, 0x0355, 0x037a, 0x03a0, 0x03c7, 0x03ef, 0x0418, 0x0441,
+ 0x046c, 0x0498, 0x04c4, 0x04f2, 0x0520, 0x0550, 0x0581, 0x05b2, 0x05e5,
+ 0x0618, 0x064d, 0x0682, 0x06b9, 0x06f0, 0x0729, 0x0763, 0x079d, 0x07d9,
+ 0x0816, 0x0854, 0x0893, 0x08d3, 0x0914, 0x0956, 0x0999, 0x09dd, 0x0a23,
+ 0x0a69, 0x0ab1, 0x0afa, 0x0b44, 0x0b8f, 0x0bdb, 0x0c28, 0x0c76, 0x0cc6,
+ 0x0d17, 0x0d69, 0x0dbb, 0x0e10, 0x0e65, 0x0ebb, 0x0f13, 0x0f6c, 0x0fc6,
+ 0x1021, 0x107d, 0x10db, 0x113a, 0x119a, 0x11fb, 0x125d, 0x12c1, 0x1325,
+ 0x138c, 0x13f3, 0x145b, 0x14c5, 0x1530, 0x159c, 0x160a, 0x1678, 0x16e8,
+ 0x175a, 0x17cc, 0x1840, 0x18b5, 0x192b, 0x19a3, 0x1a1c, 0x1a96, 0x1b11,
+ 0x1b8e, 0x1c0c, 0x1c8c, 0x1d0c, 0x1d8e, 0x1e12, 0x1e96, 0x1f1c, 0x1fa3,
+ 0x202c, 0x20b6, 0x2141, 0x21ce, 0x225c, 0x22eb, 0x237c, 0x240e, 0x24a1,
+ 0x2536, 0x25cc, 0x2664, 0x26fc, 0x2797, 0x2832, 0x28cf, 0x296e, 0x2a0e,
+ 0x2aaf, 0x2b51, 0x2bf5, 0x2c9b, 0x2d41, 0x2dea, 0x2e93, 0x2f3e, 0x2feb,
+ 0x3099, 0x3148, 0x31f9, 0x32ab, 0x335f, 0x3414, 0x34ca, 0x3582, 0x363c,
+ 0x36f7, 0x37b3, 0x3871, 0x3930, 0x39f1, 0x3ab3, 0x3b77, 0x3c3c, 0x3d02,
+ 0x3dcb, 0x3e94, 0x3f5f, 0x402c, 0x40fa, 0x41ca, 0x429b, 0x436d, 0x4442,
+ 0x4517, 0x45ee, 0x46c7, 0x47a1, 0x487d, 0x495a, 0x4a39, 0x4b19, 0x4bfb,
+ 0x4cde, 0x4dc3, 0x4eaa, 0x4f92, 0x507c, 0x5167, 0x5253, 0x5342, 0x5431,
+ 0x5523, 0x5616, 0x570a, 0x5800, 0x58f8, 0x59f1, 0x5aec, 0x5be9, 0x5ce7,
+ 0x5de6, 0x5ee7, 0x5fea, 0x60ef, 0x61f5, 0x62fc, 0x6406, 0x6510, 0x661d,
+ 0x672b, 0x683b, 0x694c, 0x6a5f, 0x6b73, 0x6c8a, 0x6da2, 0x6ebb, 0x6fd6,
+ 0x70f3, 0x7211, 0x7331, 0x7453, 0x7576, 0x769b, 0x77c2, 0x78ea, 0x7a14,
+ 0x7b40, 0x7c6d, 0x7d9c, 0x7ecd, 0x3f65, 0x3f8c, 0x3fb2, 0x3fd8
+};
+
+/*
+ * Degamma table for 2020 color space data.
+ */
+static u16 DEGAMMA_2020[DEGAMMA_SIZE] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0002, 0x0002,
+ 0x0002, 0x0002, 0x0003, 0x0003, 0x0003, 0x0003, 0x0004, 0x0004, 0x0004,
+ 0x0005, 0x0005, 0x0006, 0x0006, 0x0006, 0x0007, 0x0007, 0x0008, 0x0008,
+ 0x0009, 0x000a, 0x000a, 0x000b, 0x000c, 0x000c, 0x000d, 0x000e, 0x000f,
+ 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x0018,
+ 0x0019, 0x001b, 0x001c, 0x001e, 0x001f, 0x0021, 0x0022, 0x0024, 0x0026,
+ 0x0028, 0x002a, 0x002c, 0x002e, 0x0030, 0x0033, 0x0035, 0x0038, 0x003a,
+ 0x003d, 0x0040, 0x0043, 0x0046, 0x0049, 0x004d, 0x0050, 0x0054, 0x0057,
+ 0x005b, 0x005f, 0x0064, 0x0068, 0x006d, 0x0071, 0x0076, 0x007c, 0x0081,
+ 0x0086, 0x008c, 0x0092, 0x0098, 0x009f, 0x00a5, 0x00ac, 0x00b4, 0x00bb,
+ 0x00c3, 0x00cb, 0x00d3, 0x00dc, 0x00e5, 0x00ee, 0x00f8, 0x0102, 0x010c,
+ 0x0117, 0x0123, 0x012e, 0x013a, 0x0147, 0x0154, 0x0161, 0x016f, 0x017e,
+ 0x018d, 0x019c, 0x01ac, 0x01bd, 0x01ce, 0x01e0, 0x01f3, 0x0206, 0x021a,
+ 0x022f, 0x0244, 0x025a, 0x0272, 0x0289, 0x02a2, 0x02bc, 0x02d6, 0x02f2,
+ 0x030f, 0x032c, 0x034b, 0x036b, 0x038b, 0x03ae, 0x03d1, 0x03f5, 0x041b,
+ 0x0443, 0x046b, 0x0495, 0x04c1, 0x04ee, 0x051d, 0x054e, 0x0580, 0x05b4,
+ 0x05ea, 0x0622, 0x065c, 0x0698, 0x06d6, 0x0717, 0x075a, 0x079f, 0x07e7,
+ 0x0831, 0x087e, 0x08cd, 0x0920, 0x0976, 0x09ce, 0x0a2a, 0x0a89, 0x0aec,
+ 0x0b52, 0x0bbc, 0x0c2a, 0x0c9b, 0x0d11, 0x0d8b, 0x0e0a, 0x0e8d, 0x0f15,
+ 0x0fa1, 0x1033, 0x10ca, 0x1167, 0x120a, 0x12b2, 0x1360, 0x1415, 0x14d1,
+ 0x1593, 0x165d, 0x172e, 0x1806, 0x18e7, 0x19d0, 0x1ac1, 0x1bbb, 0x1cbf,
+ 0x1dcc, 0x1ee3, 0x2005, 0x2131, 0x2268, 0x23ab, 0x24fa, 0x2656, 0x27be,
+ 0x2934, 0x2ab8, 0x2c4a, 0x2dec, 0x2f9d, 0x315f, 0x3332, 0x3516, 0x370d,
+ 0x3916, 0x3b34, 0x3d66, 0x3fad, 0x420b, 0x4480, 0x470d, 0x49b3, 0x4c73,
+ 0x4f4e, 0x5246, 0x555a, 0x588e, 0x5be1, 0x5f55, 0x62eb, 0x66a6, 0x6a86,
+ 0x6e8c, 0x72bb, 0x7714, 0x7b99, 0x3dcb, 0x3e60, 0x3ef5, 0x3f8c
+};
+
+/* one is for primary plane and the other is for all overlay planes */
+static const struct dc_hw_plane_reg dc_plane_reg[] = {
+ {
+ .y_address = DC_FRAMEBUFFER_ADDRESS,
+ .u_address = DC_FRAMEBUFFER_U_ADDRESS,
+ .v_address = DC_FRAMEBUFFER_V_ADDRESS,
+ .y_stride = DC_FRAMEBUFFER_STRIDE,
+ .u_stride = DC_FRAMEBUFFER_U_STRIDE,
+ .v_stride = DC_FRAMEBUFFER_V_STRIDE,
+ .size = DC_FRAMEBUFFER_SIZE,
+ .scale_factor_x = DC_FRAMEBUFFER_SCALE_FACTOR_X,
+ .scale_factor_y = DC_FRAMEBUFFER_SCALE_FACTOR_Y,
+ .h_filter_coef_index = DC_FRAMEBUFFER_H_FILTER_COEF_INDEX,
+ .h_filter_coef_data = DC_FRAMEBUFFER_H_FILTER_COEF_DATA,
+ .v_filter_coef_index = DC_FRAMEBUFFER_V_FILTER_COEF_INDEX,
+ .v_filter_coef_data = DC_FRAMEBUFFER_V_FILTER_COEF_DATA,
+ .init_offset = DC_FRAMEBUFFER_INIT_OFFSET,
+ .color_key = DC_FRAMEBUFFER_COLOR_KEY,
+ .color_key_high = DC_FRAMEBUFFER_COLOR_KEY_HIGH,
+ .clear_value = DC_FRAMEBUFFER_CLEAR_VALUE,
+ .color_table_index = DC_FRAMEBUFFER_COLOR_TABLE_INDEX,
+ .color_table_data = DC_FRAMEBUFFER_COLOR_TABLE_DATA,
+ .scale_config = DC_FRAMEBUFFER_SCALE_CONFIG,
+ .degamma_index = DC_FRAMEBUFFER_DEGAMMA_INDEX,
+ .degamma_data = DC_FRAMEBUFFER_DEGAMMA_DATA,
+ .degamma_ex_data = DC_FRAMEBUFFER_DEGAMMA_EX_DATA,
+ .roi_origin = DC_FRAMEBUFFER_ROI_ORIGIN,
+ .roi_size = DC_FRAMEBUFFER_ROI_SIZE,
+ .YUVToRGBCoef0 = DC_FRAMEBUFFER_YUVTORGB_COEF0,
+ .YUVToRGBCoef1 = DC_FRAMEBUFFER_YUVTORGB_COEF1,
+ .YUVToRGBCoef2 = DC_FRAMEBUFFER_YUVTORGB_COEF2,
+ .YUVToRGBCoef3 = DC_FRAMEBUFFER_YUVTORGB_COEF3,
+ .YUVToRGBCoef4 = DC_FRAMEBUFFER_YUVTORGB_COEF4,
+ .YUVToRGBCoefD0 = DC_FRAMEBUFFER_YUVTORGB_COEFD0,
+ .YUVToRGBCoefD1 = DC_FRAMEBUFFER_YUVTORGB_COEFD1,
+ .YUVToRGBCoefD2 = DC_FRAMEBUFFER_YUVTORGB_COEFD2,
+ .YClampBound = DC_FRAMEBUFFER_Y_CLAMP_BOUND,
+ .UVClampBound = DC_FRAMEBUFFER_UV_CLAMP_BOUND,
+ .RGBToRGBCoef0 = DC_FRAMEBUFFER_RGBTORGB_COEF0,
+ .RGBToRGBCoef1 = DC_FRAMEBUFFER_RGBTORGB_COEF1,
+ .RGBToRGBCoef2 = DC_FRAMEBUFFER_RGBTORGB_COEF2,
+ .RGBToRGBCoef3 = DC_FRAMEBUFFER_RGBTORGB_COEF3,
+ .RGBToRGBCoef4 = DC_FRAMEBUFFER_RGBTORGB_COEF4,
+ },
+ {
+ .y_address = DC_OVERLAY_ADDRESS,
+ .u_address = DC_OVERLAY_U_ADDRESS,
+ .v_address = DC_OVERLAY_V_ADDRESS,
+ .y_stride = DC_OVERLAY_STRIDE,
+ .u_stride = DC_OVERLAY_U_STRIDE,
+ .v_stride = DC_OVERLAY_V_STRIDE,
+ .size = DC_OVERLAY_SIZE,
+ .scale_factor_x = DC_OVERLAY_SCALE_FACTOR_X,
+ .scale_factor_y = DC_OVERLAY_SCALE_FACTOR_Y,
+ .h_filter_coef_index = DC_OVERLAY_H_FILTER_COEF_INDEX,
+ .h_filter_coef_data = DC_OVERLAY_H_FILTER_COEF_DATA,
+ .v_filter_coef_index = DC_OVERLAY_V_FILTER_COEF_INDEX,
+ .v_filter_coef_data = DC_OVERLAY_V_FILTER_COEF_DATA,
+ .init_offset = DC_OVERLAY_INIT_OFFSET,
+ .color_key = DC_OVERLAY_COLOR_KEY,
+ .color_key_high = DC_OVERLAY_COLOR_KEY_HIGH,
+ .clear_value = DC_OVERLAY_CLEAR_VALUE,
+ .color_table_index = DC_OVERLAY_COLOR_TABLE_INDEX,
+ .color_table_data = DC_OVERLAY_COLOR_TABLE_DATA,
+ .scale_config = DC_OVERLAY_SCALE_CONFIG,
+ .degamma_index = DC_OVERLAY_DEGAMMA_INDEX,
+ .degamma_data = DC_OVERLAY_DEGAMMA_DATA,
+ .degamma_ex_data = DC_OVERLAY_DEGAMMA_EX_DATA,
+ .roi_origin = DC_OVERLAY_ROI_ORIGIN,
+ .roi_size = DC_OVERLAY_ROI_SIZE,
+ .YUVToRGBCoef0 = DC_OVERLAY_YUVTORGB_COEF0,
+ .YUVToRGBCoef1 = DC_OVERLAY_YUVTORGB_COEF1,
+ .YUVToRGBCoef2 = DC_OVERLAY_YUVTORGB_COEF2,
+ .YUVToRGBCoef3 = DC_OVERLAY_YUVTORGB_COEF3,
+ .YUVToRGBCoef4 = DC_OVERLAY_YUVTORGB_COEF4,
+ .YUVToRGBCoefD0 = DC_OVERLAY_YUVTORGB_COEFD0,
+ .YUVToRGBCoefD1 = DC_OVERLAY_YUVTORGB_COEFD1,
+ .YUVToRGBCoefD2 = DC_OVERLAY_YUVTORGB_COEFD2,
+ .YClampBound = DC_OVERLAY_Y_CLAMP_BOUND,
+ .UVClampBound = DC_OVERLAY_UV_CLAMP_BOUND,
+ .RGBToRGBCoef0 = DC_OVERLAY_RGBTORGB_COEF0,
+ .RGBToRGBCoef1 = DC_OVERLAY_RGBTORGB_COEF1,
+ .RGBToRGBCoef2 = DC_OVERLAY_RGBTORGB_COEF2,
+ .RGBToRGBCoef3 = DC_OVERLAY_RGBTORGB_COEF3,
+ .RGBToRGBCoef4 = DC_OVERLAY_RGBTORGB_COEF4,
+ },
+};
+
+static const u32 read_reg[] = {
+ DC_FRAMEBUFFER_CONFIG,
+ DC_OVERLAY_CONFIG,
+ DC_OVERLAY_SCALE_CONFIG,
+};
+#define READ_REG_COUNT ARRAY_SIZE(read_reg)
+
+static const u32 primary_overlay_formats[] = {
+ DRM_FORMAT_XRGB4444, DRM_FORMAT_ARGB4444, DRM_FORMAT_XRGB1555,
+ DRM_FORMAT_ARGB1555, DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ARGB8888, DRM_FORMAT_ARGB2101010, DRM_FORMAT_RGBA4444,
+ DRM_FORMAT_RGBA5551, DRM_FORMAT_RGBA8888, DRM_FORMAT_RGBA1010102,
+ DRM_FORMAT_ABGR4444, DRM_FORMAT_ABGR1555, DRM_FORMAT_BGR565,
+ DRM_FORMAT_ABGR8888, DRM_FORMAT_ABGR2101010, DRM_FORMAT_BGRA4444,
+ DRM_FORMAT_BGRA5551, DRM_FORMAT_BGRA8888, DRM_FORMAT_BGRA1010102,
+ DRM_FORMAT_XBGR4444, DRM_FORMAT_RGBX4444, DRM_FORMAT_BGRX4444,
+ DRM_FORMAT_XBGR1555, DRM_FORMAT_RGBX5551, DRM_FORMAT_BGRX5551,
+ DRM_FORMAT_XBGR8888, DRM_FORMAT_RGBX8888, DRM_FORMAT_BGRX8888,
+ DRM_FORMAT_YUYV, DRM_FORMAT_YVYU, DRM_FORMAT_UYVY,
+ DRM_FORMAT_VYUY, DRM_FORMAT_NV12, DRM_FORMAT_NV21,
+ DRM_FORMAT_NV16, DRM_FORMAT_NV61, DRM_FORMAT_P010,
+ /* TODO. */
+};
+
+static const u32 cursor_formats[] = { DRM_FORMAT_ARGB8888 };
+
+static const u64 cursor_dumy_modifiers[] = { DRM_FORMAT_MOD_LINEAR,
+ DRM_FORMAT_MOD_INVALID };
+
+static const u64 format_modifiers[] = { DRM_FORMAT_MOD_LINEAR,
+ DRM_FORMAT_MOD_INVALID };
+
+#define FRAC_16_16(mult, div) (((mult) << 16) / (div))
+
+static const struct es_plane_info dc_hw_planes[][PLANE_NUM] = {
+ {
+ /* DC_REV_5551 */
+ {
+ .name = "Primary",
+ .id = PRIMARY_PLANE,
+ .type = DRM_PLANE_TYPE_PRIMARY,
+ .num_formats = ARRAY_SIZE(primary_overlay_formats),
+ .formats = primary_overlay_formats,
+ .modifiers = format_modifiers,
+ .min_width = 0,
+ .min_height = 0,
+ .max_width = 4096,
+ .max_height = 4096,
+ .rotation = DRM_MODE_ROTATE_0,
+ .color_encoding = BIT(DRM_COLOR_YCBCR_BT601) |
+ BIT(DRM_COLOR_YCBCR_BT709) |
+ BIT(DRM_COLOR_YCBCR_BT2020),
+ .degamma_size = 0,
+ .min_scale = FRAC_16_16(1, 3),
+ .max_scale = FRAC_16_16(10, 1),
+ },
+ {
+ .name = "Cursor",
+ .id = CURSOR_PLANE,
+ .type = DRM_PLANE_TYPE_CURSOR,
+ .num_formats = ARRAY_SIZE(cursor_formats),
+ .formats = cursor_formats,
+ .modifiers = NULL,
+ .min_width = 32,
+ .min_height = 32,
+ .max_width = 32,
+ .max_height = 32,
+ .rotation = DRM_MODE_ROTATE_0,
+ .degamma_size = 0,
+ .min_scale = DRM_PLANE_NO_SCALING,
+ .max_scale = DRM_PLANE_NO_SCALING,
+ },
+ {
+ .name = "Overlay",
+ .id = OVERLAY_PLANE,
+ .type = DRM_PLANE_TYPE_OVERLAY,
+ .num_formats = ARRAY_SIZE(primary_overlay_formats),
+ .formats = primary_overlay_formats,
+ .modifiers = format_modifiers,
+ .min_width = 0,
+ .min_height = 0,
+ .max_width = 4096,
+ .max_height = 4096,
+ .rotation = DRM_MODE_ROTATE_0,
+ .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+ BIT(DRM_MODE_BLEND_PREMULTI) |
+ BIT(DRM_MODE_BLEND_COVERAGE),
+ .color_encoding = BIT(DRM_COLOR_YCBCR_BT601) |
+ BIT(DRM_COLOR_YCBCR_BT709) |
+ BIT(DRM_COLOR_YCBCR_BT2020),
+ .degamma_size = 0,
+ .min_scale = FRAC_16_16(1, 3),
+ .max_scale = FRAC_16_16(10, 1),
+ },
+ },
+ {
+ /* DC_REV_5551_306 */
+ {
+ .name = "Primary",
+ .id = PRIMARY_PLANE,
+ .type = DRM_PLANE_TYPE_PRIMARY,
+ .num_formats = ARRAY_SIZE(primary_overlay_formats),
+ .formats = primary_overlay_formats,
+ .modifiers = format_modifiers,
+ .min_width = 0,
+ .min_height = 0,
+ .max_width = 4096,
+ .max_height = 4096,
+ .rotation = DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_90 |
+ DRM_MODE_ROTATE_180 | DRM_MODE_ROTATE_270 |
+ DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
+ .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+ BIT(DRM_COLOR_YCBCR_BT2020),
+ .degamma_size = 0,
+ .min_scale = FRAC_16_16(1, 3),
+ .max_scale = FRAC_16_16(10, 1),
+ },
+ {
+ .name = "Cursor",
+ .id = CURSOR_PLANE,
+ .type = DRM_PLANE_TYPE_CURSOR,
+ .num_formats = ARRAY_SIZE(cursor_formats),
+ .formats = cursor_formats,
+ .modifiers = NULL,
+ .min_width = 32,
+ .min_height = 32,
+ .max_width = 32,
+ .max_height = 32,
+ .rotation = 0,
+ .color_encoding = 0,
+ .degamma_size = 0,
+ .min_scale = DRM_PLANE_NO_SCALING,
+ .max_scale = DRM_PLANE_NO_SCALING,
+ },
+ {
+ .name = "Overlay",
+ .id = OVERLAY_PLANE,
+ .type = DRM_PLANE_TYPE_OVERLAY,
+ .num_formats = ARRAY_SIZE(primary_overlay_formats),
+ .formats = primary_overlay_formats,
+ .modifiers = format_modifiers,
+ .min_width = 0,
+ .min_height = 0,
+ .max_width = 4096,
+ .max_height = 4096,
+ .rotation = DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_90 |
+ DRM_MODE_ROTATE_180 | DRM_MODE_ROTATE_270 |
+ DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
+ .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+ BIT(DRM_MODE_BLEND_PREMULTI) |
+ BIT(DRM_MODE_BLEND_COVERAGE),
+ .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) |
+ BIT(DRM_COLOR_YCBCR_BT2020),
+ .degamma_size = 0,
+ .min_scale = FRAC_16_16(1, 3),
+ .max_scale = FRAC_16_16(10, 1),
+ },
+ },
+ {
+ /* DC_REV_5701_303 */
+ {
+ .name = "Primary",
+ .id = PRIMARY_PLANE,
+ .type = DRM_PLANE_TYPE_PRIMARY,
+ .num_formats = ARRAY_SIZE(primary_overlay_formats),
+ .formats = primary_overlay_formats,
+ .modifiers = format_modifiers,
+ .min_width = 0,
+ .min_height = 0,
+ .max_width = 4096,
+ .max_height = 4096,
+ .rotation = DRM_MODE_ROTATE_0,
+ .color_encoding = BIT(DRM_COLOR_YCBCR_BT601) |
+ BIT(DRM_COLOR_YCBCR_BT709) |
+ BIT(DRM_COLOR_YCBCR_BT2020),
+ .degamma_size = DEGAMMA_SIZE,
+ .min_scale = FRAC_16_16(1, 3),
+ .max_scale = FRAC_16_16(10, 1),
+ },
+ {
+ .name = "Cursor",
+ .id = CURSOR_PLANE,
+ .type = DRM_PLANE_TYPE_CURSOR,
+ .num_formats = ARRAY_SIZE(cursor_formats),
+ .formats = cursor_formats,
+ .modifiers = NULL,
+ .min_width = 32,
+ .min_height = 32,
+ .max_width = 256,
+ .max_height = 256,
+ .rotation = DRM_MODE_ROTATE_0,
+ .degamma_size = 0,
+ .min_scale = DRM_PLANE_NO_SCALING,
+ .max_scale = DRM_PLANE_NO_SCALING,
+ },
+ {
+ .name = "Overlay",
+ .id = OVERLAY_PLANE,
+ .type = DRM_PLANE_TYPE_OVERLAY,
+ .num_formats = ARRAY_SIZE(primary_overlay_formats),
+ .formats = primary_overlay_formats,
+ .modifiers = format_modifiers,
+ .min_width = 0,
+ .min_height = 0,
+ .max_width = 4096,
+ .max_height = 4096,
+ .rotation = DRM_MODE_ROTATE_0,
+ .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+ BIT(DRM_MODE_BLEND_PREMULTI) |
+ BIT(DRM_MODE_BLEND_COVERAGE),
+ .color_encoding = BIT(DRM_COLOR_YCBCR_BT601) |
+ BIT(DRM_COLOR_YCBCR_BT709) |
+ BIT(DRM_COLOR_YCBCR_BT2020),
+ .degamma_size = DEGAMMA_SIZE,
+ .min_scale = FRAC_16_16(1, 3),
+ .max_scale = FRAC_16_16(10, 1),
+ },
+ },
+ {
+ /* DC_REV_5701_309 */
+ {
+ .name = "Primary",
+ .id = PRIMARY_PLANE,
+ .type = DRM_PLANE_TYPE_PRIMARY,
+ .num_formats = ARRAY_SIZE(primary_overlay_formats),
+ .formats = primary_overlay_formats,
+ .modifiers = format_modifiers,
+ .min_width = 0,
+ .min_height = 0,
+ .max_width = 4096,
+ .max_height = 4096,
+ .rotation = DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180,
+ .color_encoding = BIT(DRM_COLOR_YCBCR_BT601) |
+ BIT(DRM_COLOR_YCBCR_BT709) |
+ BIT(DRM_COLOR_YCBCR_BT2020),
+ .degamma_size = DEGAMMA_SIZE,
+ .min_scale = FRAC_16_16(1, 32),
+ .max_scale = FRAC_16_16(2, 1),
+ .roi = true,
+ .color_mgmt = true,
+ .background = true,
+ },
+ {
+ .name = "Cursor",
+ .id = CURSOR_PLANE,
+ .type = DRM_PLANE_TYPE_CURSOR,
+ .num_formats = ARRAY_SIZE(cursor_formats),
+ .formats = cursor_formats,
+ .modifiers = cursor_dumy_modifiers,
+ .min_width = 32,
+ .min_height = 32,
+ .max_width = 256,
+ .max_height = 256,
+ .rotation = DRM_MODE_ROTATE_0,
+ .degamma_size = 0,
+ .min_scale = DRM_PLANE_NO_SCALING,
+ .max_scale = DRM_PLANE_NO_SCALING,
+ },
+ {
+ .name = "Overlay",
+ .id = OVERLAY_PLANE,
+ .type = DRM_PLANE_TYPE_OVERLAY,
+ .num_formats = ARRAY_SIZE(primary_overlay_formats),
+ .formats = primary_overlay_formats,
+ .modifiers = format_modifiers,
+ .min_width = 0,
+ .min_height = 0,
+ .max_width = 4096,
+ .max_height = 4096,
+ .rotation = DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180,
+ .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+ BIT(DRM_MODE_BLEND_PREMULTI) |
+ BIT(DRM_MODE_BLEND_COVERAGE),
+ .color_encoding = BIT(DRM_COLOR_YCBCR_BT601) |
+ BIT(DRM_COLOR_YCBCR_BT709) |
+ BIT(DRM_COLOR_YCBCR_BT2020),
+ .degamma_size = DEGAMMA_SIZE,
+ .min_scale = FRAC_16_16(1, 32),
+ .max_scale = FRAC_16_16(2, 1),
+ .roi = true,
+ .color_mgmt = true,
+ },
+ },
+};
+
+static const struct es_dc_info dc_info[] = {
+ {
+ /* DC_REV_5551 */
+ .name = "DisplayControl",
+ .plane_num = PLANE_NUM,
+ .planes = dc_hw_planes[DC_REV_5551],
+ .max_bpc = 10,
+ .color_formats = DRM_COLOR_FORMAT_RGB444,
+ .gamma_size = GAMMA_SIZE,
+ .gamma_bits = 10,
+ .pitch_alignment = 128,
+ .pipe_sync = false,
+ .mmu_prefetch = false,
+ },
+ {
+ /* DC_REV_5551_306 */
+ .name = "DisplayControl",
+ .plane_num = PLANE_NUM,
+ .planes = dc_hw_planes[DC_REV_5551_306],
+ .max_bpc = 10,
+ .color_formats = DRM_COLOR_FORMAT_RGB444,
+ .gamma_size = GAMMA_SIZE,
+ .gamma_bits = 10,
+ .pitch_alignment = 128,
+ .pipe_sync = false,
+ .mmu_prefetch = false,
+ },
+ {
+ /* DC_REV_5701_303 */
+ .name = "DisplayControl",
+ .plane_num = PLANE_NUM,
+ .planes = dc_hw_planes[DC_REV_5701_303],
+ .max_bpc = 10,
+ .color_formats =
+ DRM_COLOR_FORMAT_RGB444 | DRM_COLOR_FORMAT_YCBCR444 |
+ DRM_COLOR_FORMAT_YCBCR422 | DRM_COLOR_FORMAT_YCBCR420,
+ .gamma_size = GAMMA_EX_SIZE,
+ .gamma_bits = 12,
+ .pitch_alignment = 128,
+ .pipe_sync = false,
+ .mmu_prefetch = false,
+ },
+ {
+ /* DC_REV_5701_309 */
+ .name = "DisplayControl",
+ .plane_num = PLANE_NUM,
+ .planes = dc_hw_planes[DC_REV_5701_309],
+ .max_bpc = 10,
+ .color_formats =
+ DRM_COLOR_FORMAT_RGB444 | DRM_COLOR_FORMAT_YCBCR444 |
+ DRM_COLOR_FORMAT_YCBCR422 | DRM_COLOR_FORMAT_YCBCR420,
+ .gamma_size = GAMMA_EX_SIZE,
+ .gamma_bits = 12,
+ .pitch_alignment = 128,
+ .pipe_sync = true,
+ .mmu_prefetch = true,
+ .background = true,
+ },
+};
+
+static const struct dc_hw_funcs hw_func[];
+
+static inline u32 hi_write(struct dc_hw *hw, u32 reg, u32 val)
+{
+ writel(val, hw->hi_base + reg);
+
+ return 0;
+}
+
+static inline u32 hi_read(struct dc_hw *hw, u32 reg)
+{
+ u32 value;
+
+ value = readl(hw->hi_base + reg);
+ return value;
+}
+
+static inline int dc_get_read_index(struct dc_hw *hw, u32 reg)
+{
+ int i;
+
+ for (i = 0; i < READ_REG_COUNT; i++) {
+ if (hw->read_block[i].reg == reg)
+ return i;
+ }
+ return i;
+}
+
+static inline void dc_write(struct dc_hw *hw, u32 reg, u32 value)
+{
+ writel(value, hw->reg_base + reg - DC_REG_BASE);
+
+ if (hw->read_block) {
+ int i = dc_get_read_index(hw, reg);
+
+ if (i < READ_REG_COUNT)
+ hw->read_block[i].value = value;
+ }
+}
+
+static inline u32 dc_read(struct dc_hw *hw, u32 reg)
+{
+ u32 value = readl(hw->reg_base + reg - DC_REG_BASE);
+
+ if (hw->read_block) {
+ int i = dc_get_read_index(hw, reg);
+
+ if (i < READ_REG_COUNT) {
+ value &= ~BIT(0);
+ value |= hw->read_block[i].value & BIT(0);
+ }
+ }
+
+ return value;
+}
+
+static inline void dc_set_clear(struct dc_hw *hw, u32 reg, u32 set, u32 clear)
+{
+ u32 value = dc_read(hw, reg);
+
+ value &= ~clear;
+ value |= set;
+ dc_write(hw, reg, value);
+}
+
+static void load_default_filter(struct dc_hw *hw,
+ const struct dc_hw_plane_reg *reg)
+{
+ u8 i;
+
+ dc_write(hw, reg->scale_config, 0x33);
+ dc_write(hw, reg->init_offset, 0x80008000);
+ dc_write(hw, reg->h_filter_coef_index, 0x00);
+ for (i = 0; i < H_COEF_SIZE; i++)
+ dc_write(hw, reg->h_filter_coef_data, horKernel[i]);
+
+ dc_write(hw, reg->v_filter_coef_index, 0x00);
+ for (i = 0; i < V_COEF_SIZE; i++)
+ dc_write(hw, reg->v_filter_coef_data, verKernel[i]);
+}
+
+static void load_rgb_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
+ u16 *table)
+{
+ dc_write(hw, reg->RGBToRGBCoef0, table[0] | (table[1] << 16));
+ dc_write(hw, reg->RGBToRGBCoef1, table[2] | (table[3] << 16));
+ dc_write(hw, reg->RGBToRGBCoef2, table[4] | (table[5] << 16));
+ dc_write(hw, reg->RGBToRGBCoef3, table[6] | (table[7] << 16));
+ dc_write(hw, reg->RGBToRGBCoef4, table[8]);
+}
+
+static void load_yuv_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg,
+ s32 *table)
+{
+ dc_write(hw, reg->YUVToRGBCoef0,
+ (0xFFFF & table[0]) | (table[1] << 16));
+ dc_write(hw, reg->YUVToRGBCoef1,
+ (0xFFFF & table[2]) | (table[3] << 16));
+ dc_write(hw, reg->YUVToRGBCoef2,
+ (0xFFFF & table[4]) | (table[5] << 16));
+ dc_write(hw, reg->YUVToRGBCoef3,
+ (0xFFFF & table[6]) | (table[7] << 16));
+ dc_write(hw, reg->YUVToRGBCoef4, table[8]);
+ dc_write(hw, reg->YUVToRGBCoefD0, table[9]);
+ dc_write(hw, reg->YUVToRGBCoefD1, table[10]);
+ dc_write(hw, reg->YUVToRGBCoefD2, table[11]);
+ dc_write(hw, reg->YClampBound, table[12] | (table[13] << 16));
+ dc_write(hw, reg->UVClampBound, table[14] | (table[15] << 16));
+}
+
+static void load_rgb_to_yuv(struct dc_hw *hw, s16 *table)
+{
+ dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF0, table[0] | (table[1] << 16));
+ dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF1, table[2] | (table[3] << 16));
+ dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF2, table[4] | (table[5] << 16));
+ dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF3, table[6] | (table[7] << 16));
+ dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF4, table[8]);
+ dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD0, table[9]);
+ dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD1, table[10]);
+ dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD2, table[11]);
+}
+
+static bool is_rgb(enum dc_hw_color_format format)
+{
+ switch (format) {
+ case FORMAT_X4R4G4B4:
+ case FORMAT_A4R4G4B4:
+ case FORMAT_X1R5G5B5:
+ case FORMAT_A1R5G5B5:
+ case FORMAT_R5G6B5:
+ case FORMAT_X8R8G8B8:
+ case FORMAT_A8R8G8B8:
+ case FORMAT_A2R10G10B10:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void load_degamma_table(struct dc_hw *hw,
+ const struct dc_hw_plane_reg *reg, u16 *table)
+{
+ u16 i;
+ u32 value;
+
+ for (i = 0; i < DEGAMMA_SIZE; i++) {
+ dc_write(hw, reg->degamma_index, i);
+ value = table[i] | (table[i] << 16);
+ dc_write(hw, reg->degamma_data, value);
+ dc_write(hw, reg->degamma_ex_data, table[i]);
+ }
+}
+
+int dc_hw_init(struct dc_hw *hw)
+{
+ u8 i;
+ u32 revision = hi_read(hw, DC_HW_REVISION);
+ u32 cid = hi_read(hw, DC_HW_CHIP_CID);
+
+ switch (revision) {
+ case 0x5551:
+ if (cid < 0x306)
+ hw->rev = DC_REV_5551;
+ else
+ hw->rev = DC_REV_5551_306;
+ break;
+ case 0x5701:
+ if (cid < 0x309)
+ hw->rev = DC_REV_5701_303;
+ else
+ hw->rev = DC_REV_5701_309;
+ break;
+ default:
+ return -ENXIO;
+ }
+
+ if (hw->rev == DC_REV_5551) {
+ hw->read_block = kzalloc(
+ sizeof(struct dc_hw_read) * READ_REG_COUNT, GFP_KERNEL);
+ if (!hw->read_block)
+ return -ENOMEM;
+ for (i = 0; i < READ_REG_COUNT; i++)
+ hw->read_block[i].reg = read_reg[i];
+ }
+
+ /*
+ * Do soft reset before configuring DC
+ * Sleep 50ms to ensure no data transfer on AXI bus,
+ * because dc may be initialized in uboot
+ */
+ {
+ u32 val;
+
+ val = dc_read(hw, DC_FRAMEBUFFER_CONFIG);
+ val &= ~BIT(0);
+ val |= BIT(3);
+ dc_write(hw, DC_FRAMEBUFFER_CONFIG, val);
+ mdelay(50);
+ hi_write(hw, 0x0, 0x1000);
+ mdelay(10);
+ }
+
+ hw->info = (struct es_dc_info *)&dc_info[hw->rev];
+ hw->func = (struct dc_hw_funcs *)&hw_func[hw->rev];
+
+ for (i = 0; i < ARRAY_SIZE(dc_plane_reg); i++) {
+ load_default_filter(hw, &dc_plane_reg[i]);
+ if ((hw->rev == DC_REV_5701_303) ||
+ (hw->rev == DC_REV_5701_309))
+ load_rgb_to_rgb(hw, &dc_plane_reg[i], RGB2RGB);
+ }
+
+ if (hw->rev == DC_REV_5701_303)
+ load_rgb_to_yuv(hw, RGB2YUV);
+
+ dc_write(hw, DC_CURSOR_BACKGROUND, 0x00FFFFFF);
+ dc_write(hw, DC_CURSOR_FOREGROUND, 0x00AAAAAA);
+ dc_write(hw, DC_DISPLAY_PANEL_CONFIG, 0x111);
+
+ return 0;
+}
+
+void dc_hw_deinit(struct dc_hw *hw)
+{
+ if (hw->read_block)
+ kfree(hw->read_block);
+}
+
+void dc_hw_update_plane(struct dc_hw *hw, enum dc_hw_plane_id id,
+ struct dc_hw_fb *fb, struct dc_hw_scale *scale)
+{
+ struct dc_hw_plane *plane = NULL;
+
+ if (id == PRIMARY_PLANE)
+ plane = &hw->primary;
+ else if (id == OVERLAY_PLANE)
+ plane = &hw->overlay.plane;
+
+ if (plane) {
+ if (fb) {
+ if (fb->enable == false)
+ plane->fb.enable = false;
+ else
+ memcpy(&plane->fb, fb,
+ sizeof(*fb) - sizeof(fb->dirty));
+ plane->fb.dirty = true;
+ }
+ if (scale) {
+ memcpy(&plane->scale, scale,
+ sizeof(*scale) - sizeof(scale->dirty));
+ plane->scale.dirty = true;
+ }
+ }
+}
+
+void dc_hw_update_degamma(struct dc_hw *hw, enum dc_hw_plane_id id, u32 mode)
+{
+ struct dc_hw_plane *plane = NULL;
+ int i = (int)id;
+
+ if (id == PRIMARY_PLANE)
+ plane = &hw->primary;
+ else if (id == OVERLAY_PLANE)
+ plane = &hw->overlay.plane;
+
+ if (plane) {
+ if (hw->info->planes[i].degamma_size) {
+ plane->degamma.mode = mode;
+ plane->degamma.dirty = true;
+ } else {
+ plane->degamma.dirty = false;
+ }
+ }
+}
+
+void dc_hw_set_position(struct dc_hw *hw, struct dc_hw_position *pos)
+{
+ memcpy(&hw->overlay.pos, pos, sizeof(*pos) - sizeof(pos->dirty));
+ hw->overlay.pos.dirty = true;
+}
+
+void dc_hw_set_blend(struct dc_hw *hw, struct dc_hw_blend *blend)
+{
+ memcpy(&hw->overlay.blend, blend,
+ sizeof(*blend) - sizeof(blend->dirty));
+ hw->overlay.blend.dirty = true;
+}
+
+void dc_hw_update_cursor(struct dc_hw *hw, struct dc_hw_cursor *cursor)
+{
+ memcpy(&hw->cursor, cursor, sizeof(*cursor) - sizeof(cursor->dirty));
+ hw->cursor.dirty = true;
+}
+
+void dc_hw_update_gamma(struct dc_hw *hw, u16 index, u16 r, u16 g, u16 b)
+{
+ if (index >= hw->info->gamma_size)
+ return;
+
+ hw->gamma.gamma[index][0] = r;
+ hw->gamma.gamma[index][1] = g;
+ hw->gamma.gamma[index][2] = b;
+ hw->gamma.dirty = true;
+}
+
+void dc_hw_enable_gamma(struct dc_hw *hw, bool enable)
+{
+ hw->gamma.enable = enable;
+ hw->gamma.dirty = true;
+}
+
+void dc_hw_enable_dump(struct dc_hw *hw, u32 addr, u32 pitch)
+{
+ dc_write(hw, 0x14F0, addr);
+ dc_write(hw, 0x14E8, addr);
+ dc_write(hw, 0x1500, pitch);
+ dc_write(hw, 0x14F8, 0x30000);
+}
+
+void dc_hw_disable_dump(struct dc_hw *hw)
+{
+ dc_write(hw, 0x14F8, 0x00);
+}
+
+void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display)
+{
+ memcpy(&hw->display, display, sizeof(*display));
+
+ hw->func->display(hw, display);
+}
+
+void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable)
+{
+ dc_write(hw, DC_DISPLAY_INT_ENABLE, enable);
+}
+
+u32 dc_hw_get_interrupt(struct dc_hw *hw)
+{
+ return dc_read(hw, DC_DISPLAY_INT);
+}
+
+bool dc_hw_flip_in_progress(struct dc_hw *hw)
+{
+ return dc_read(hw, DC_FRAMEBUFFER_CONFIG) & BIT(6);
+}
+
+bool dc_hw_check_underflow(struct dc_hw *hw)
+{
+ return dc_read(hw, DC_FRAMEBUFFER_CONFIG) & BIT(5);
+}
+
+void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable)
+{
+ if (enable)
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG, 0, BIT(3));
+ else
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG, BIT(3), 0);
+}
+
+void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out)
+{
+ if (out <= OUT_DP)
+ hw->out = out;
+}
+
+static void gamma_commit(struct dc_hw *hw)
+{
+ u16 i;
+ u32 value;
+
+ if (hw->gamma.dirty) {
+ if (hw->gamma.enable) {
+ dc_write(hw, DC_DISPLAY_GAMMA_INDEX, 0x00);
+ for (i = 0; i < GAMMA_SIZE; i++) {
+ value = hw->gamma.gamma[i][2] |
+ (hw->gamma.gamma[i][1] << 10) |
+ (hw->gamma.gamma[i][0] << 20);
+ dc_write(hw, DC_DISPLAY_GAMMA_DATA, value);
+ }
+
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG, BIT(2), 0);
+ } else {
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG, 0, BIT(2));
+ }
+ hw->gamma.dirty = false;
+ }
+}
+
+static void gamma_ex_commit(struct dc_hw *hw)
+{
+ u16 i;
+ u32 value;
+
+ if (hw->gamma.dirty) {
+ if (hw->gamma.enable) {
+ dc_write(hw, DC_DISPLAY_GAMMA_EX_INDEX, 0x00);
+ for (i = 0; i < GAMMA_EX_SIZE; i++) {
+ value = hw->gamma.gamma[i][2] |
+ (hw->gamma.gamma[i][1] << 16);
+ dc_write(hw, DC_DISPLAY_GAMMA_EX_DATA, value);
+ dc_write(hw, DC_DISPLAY_GAMMA_EX_ONE_DATA,
+ hw->gamma.gamma[i][0]);
+ }
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG, BIT(2), 0);
+ } else {
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG, 0, BIT(2));
+ }
+ hw->gamma.dirty = false;
+ }
+}
+
+static void plane_commit(struct dc_hw *hw)
+{
+ struct dc_hw_plane *plane;
+ const struct dc_hw_plane_reg *reg;
+ u16 i;
+
+ for (i = 0; i < PLANE_NUM; i++) {
+ if (i == PRIMARY_PLANE) {
+ plane = &hw->primary;
+ reg = &dc_plane_reg[0];
+ } else if (i == OVERLAY_PLANE) {
+ plane = &hw->overlay.plane;
+ reg = &dc_plane_reg[1];
+ } else {
+ continue;
+ }
+
+ if (plane->fb.dirty) {
+ if (plane->fb.enable) {
+ // sifive_l2_flush64_range(plane->fb.y_address, plane->fb.width * plane->fb.height * 4);
+
+ dc_write(hw, reg->y_address,
+ plane->fb.y_address);
+ dc_write(hw, reg->u_address,
+ plane->fb.u_address);
+ dc_write(hw, reg->v_address,
+ plane->fb.v_address);
+ dc_write(hw, reg->y_stride, plane->fb.y_stride);
+ dc_write(hw, reg->u_stride, plane->fb.u_stride);
+ dc_write(hw, reg->v_stride, plane->fb.v_stride);
+ dc_write(hw, reg->size,
+ plane->fb.width |
+ (plane->fb.height << 15));
+ if (plane->fb.clear_enable)
+ dc_write(hw, reg->clear_value,
+ plane->fb.clear_value);
+ } else {
+ dc_hw_enable_shadow_register(hw, true);
+ if (i == PRIMARY_PLANE) {
+ u32 reg_clr = BIT(24), reg_set = 0;
+ dc_set_clear(hw, DC_OVERLAY_CONFIG,
+ reg_set, reg_clr);
+
+ reg_clr = (0x1F << 26),
+ reg_set = BIT(8) | (05 << 26);
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG,
+ reg_set, reg_clr);
+ }
+ mdelay(50);
+ dc_hw_enable_shadow_register(hw, false);
+ }
+
+ if (i == PRIMARY_PLANE) {
+ u32 reg_clr = 0, reg_set = 0;
+
+ reg_clr = (0x1F << 26) | BIT(25) |
+ (0x03 << 23) | (0x1F << 17) |
+ (0x07 << 14) | (0x07 << 11) | BIT(0);
+ reg_set = (plane->fb.format << 26) |
+ (plane->fb.uv_swizzle << 25) |
+ (plane->fb.swizzle << 23) |
+ (plane->fb.tile_mode << 17) |
+ (plane->fb.yuv_color_space << 14) |
+ (plane->fb.rotation << 11) |
+ (plane->fb.clear_enable << 8) |
+ plane->fb.enable;
+ if (plane->fb.enable) {
+ reg_clr |= BIT(8);
+ reg_set |= BIT(4);
+ }
+
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG, reg_set,
+ reg_clr);
+ } else
+ dc_set_clear(
+ hw, DC_OVERLAY_CONFIG,
+ (plane->fb.format << 16) |
+ (plane->fb.uv_swizzle << 15) |
+ (plane->fb.swizzle << 13) |
+ (plane->fb.tile_mode << 8) |
+ (plane->fb.yuv_color_space
+ << 5) |
+ (plane->fb.rotation << 2) |
+ (plane->fb.enable << 24) |
+ (plane->fb.clear_enable << 25),
+ (0x1F << 16) | BIT(15) | (0x03 << 13) |
+ (0x1F << 8) | (0x07 << 5) |
+ (0x07 << 2) | BIT(24));
+ plane->fb.dirty = false;
+ }
+
+ if (plane->scale.dirty) {
+ if (plane->scale.enable) {
+ dc_write(hw, reg->scale_factor_x,
+ plane->scale.scale_factor_x);
+ dc_write(hw, reg->scale_factor_y,
+ plane->scale.scale_factor_y);
+ if (i == PRIMARY_PLANE)
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG,
+ BIT(22), 0);
+ else
+ dc_set_clear(hw,
+ DC_OVERLAY_SCALE_CONFIG,
+ BIT(8), 0);
+
+ } else {
+ if (i == PRIMARY_PLANE)
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG,
+ 0, BIT(22));
+ else
+ dc_set_clear(hw,
+ DC_OVERLAY_SCALE_CONFIG, 0,
+ BIT(8));
+ }
+ plane->scale.dirty = false;
+ }
+
+ if (plane->colorkey.dirty) {
+ dc_write(hw, reg->color_key, plane->colorkey.colorkey);
+ dc_write(hw, reg->color_key_high,
+ plane->colorkey.colorkey_high);
+
+ if (i == PRIMARY_PLANE)
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG,
+ plane->colorkey.transparency << 9,
+ 0x03 << 9);
+ else
+ dc_set_clear(hw, DC_OVERLAY_CONFIG,
+ plane->colorkey.transparency,
+ 0x03);
+
+ plane->colorkey.dirty = false;
+ }
+
+ if (plane->roi.dirty) {
+ if (plane->roi.enable) {
+ dc_write(hw, reg->roi_origin,
+ plane->roi.x | (plane->roi.y << 16));
+ dc_write(hw, reg->roi_size,
+ plane->roi.width |
+ (plane->roi.height << 16));
+ if (i == PRIMARY_PLANE)
+ dc_set_clear(hw,
+ DC_FRAMEBUFFER_CONFIG_EX,
+ BIT(0), 0);
+ } else {
+ // if (i == PRIMARY_PLANE)
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX, 0,
+ BIT(0));
+ }
+ plane->roi.dirty = false;
+ }
+ }
+}
+
+static void plane_ex_commit(struct dc_hw *hw)
+{
+ struct dc_hw_plane *plane;
+ const struct dc_hw_plane_reg *reg;
+ u16 i;
+
+ for (i = 0; i < PLANE_NUM; i++) {
+ if (i == PRIMARY_PLANE) {
+ plane = &hw->primary;
+ reg = &dc_plane_reg[0];
+ } else if (i == OVERLAY_PLANE) {
+ plane = &hw->overlay.plane;
+ reg = &dc_plane_reg[1];
+ } else {
+ continue;
+ }
+
+ if (plane->fb.dirty) {
+ if (is_rgb(plane->fb.format)) {
+ if (i == PRIMARY_PLANE)
+ dc_set_clear(hw,
+ DC_FRAMEBUFFER_CONFIG_EX,
+ BIT(6), BIT(8));
+ else
+ dc_set_clear(hw, DC_OVERLAY_CONFIG,
+ BIT(29), BIT(30));
+ } else {
+ if (i == PRIMARY_PLANE)
+ dc_set_clear(hw,
+ DC_FRAMEBUFFER_CONFIG_EX,
+ BIT(8), BIT(6));
+ else
+ dc_set_clear(hw, DC_OVERLAY_CONFIG,
+ BIT(30), BIT(29));
+ switch (plane->fb.yuv_color_space) {
+ case COLOR_SPACE_601:
+ load_yuv_to_rgb(hw, reg, YUV601_2RGB);
+ break;
+ case COLOR_SPACE_709:
+ load_yuv_to_rgb(hw, reg, YUV709_2RGB);
+ break;
+ case COLOR_SPACE_2020:
+ load_yuv_to_rgb(hw, reg, YUV2020_2RGB);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if (plane->degamma.dirty) {
+ switch (plane->degamma.mode) {
+ case ES_DEGAMMA_DISABLE:
+ if (i == PRIMARY_PLANE)
+ dc_set_clear(hw,
+ DC_FRAMEBUFFER_CONFIG_EX,
+ 0, BIT(5));
+ else
+ dc_set_clear(hw, DC_OVERLAY_CONFIG, 0,
+ BIT(28));
+ break;
+ case ES_DEGAMMA_BT709:
+ load_degamma_table(hw, reg, DEGAMMA_709);
+ if (i == PRIMARY_PLANE)
+ dc_set_clear(hw,
+ DC_FRAMEBUFFER_CONFIG_EX,
+ BIT(5), 0);
+ else
+ dc_set_clear(hw, DC_OVERLAY_CONFIG,
+ BIT(28), 0);
+ break;
+ case ES_DEGAMMA_BT2020:
+ load_degamma_table(hw, reg, DEGAMMA_2020);
+ if (i == PRIMARY_PLANE)
+ dc_set_clear(hw,
+ DC_FRAMEBUFFER_CONFIG_EX,
+ BIT(5), 0);
+ else
+ dc_set_clear(hw, DC_OVERLAY_CONFIG,
+ BIT(28), 0);
+ break;
+ default:
+ break;
+ }
+ plane->degamma.dirty = false;
+ }
+ }
+ plane_commit(hw);
+}
+
+static void setup_display(struct dc_hw *hw, struct dc_hw_display *display)
+{
+ u32 dpi_cfg;
+
+ if (hw->display.enable) {
+ switch (display->bus_format) {
+ case MEDIA_BUS_FMT_RGB565_1X16:
+ dpi_cfg = 0;
+ break;
+ case MEDIA_BUS_FMT_RGB666_1X18:
+ dpi_cfg = 3;
+ break;
+ case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
+ dpi_cfg = 4;
+ break;
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ dpi_cfg = 5;
+ break;
+ case MEDIA_BUS_FMT_RGB101010_1X30:
+ dpi_cfg = 6;
+ break;
+ default:
+ dpi_cfg = 5;
+ break;
+ }
+ dc_write(hw, DC_DISPLAY_DPI_CONFIG, dpi_cfg);
+
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG, 0, BIT(4));
+
+ dc_write(hw, DC_DISPLAY_H,
+ hw->display.h_active | (hw->display.h_total << 16));
+ dc_write(hw, DC_DISPLAY_H_SYNC,
+ hw->display.h_sync_start |
+ (hw->display.h_sync_end << 15) |
+ (hw->display.h_sync_polarity ? 0 : BIT(31)) |
+ BIT(30));
+ dc_write(hw, DC_DISPLAY_V,
+ hw->display.v_active | (hw->display.v_total << 16));
+ dc_write(hw, DC_DISPLAY_V_SYNC,
+ hw->display.v_sync_start |
+ (hw->display.v_sync_end << 15) |
+ (hw->display.v_sync_polarity ? 0 : BIT(31)) |
+ BIT(30));
+
+ if (hw->info->pipe_sync) {
+ switch (display->sync_mode) {
+ case ES_SINGLE_DC:
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX, 0,
+ BIT(3) | BIT(4));
+ break;
+ case ES_MULTI_DC_PRIMARY:
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
+ BIT(3) | BIT(4), 0);
+ break;
+ case ES_MULTI_DC_SECONDARY:
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX,
+ BIT(3), BIT(4));
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (hw->display.dither_enable) {
+ dc_write(hw, DC_DISPLAY_DITHER_TABLE_LOW,
+ DC_DISPLAY_DITHERTABLE_LOW);
+ dc_write(hw, DC_DISPLAY_DITHER_TABLE_HIGH,
+ DC_DISPLAY_DITHERTABLE_HIGH);
+ dc_write(hw, DC_DISPLAY_DITHER_CONFIG, BIT(31));
+ } else {
+ dc_write(hw, DC_DISPLAY_DITHER_CONFIG, 0);
+ }
+
+ if (hw->info->background)
+ dc_write(hw, DC_FRAMEBUFFER_BG_COLOR,
+ hw->display.bg_color);
+
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG, BIT(4), 0);
+ } else {
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG, 0, BIT(4) | BIT(0));
+ }
+}
+
+static void setup_display_ex(struct dc_hw *hw, struct dc_hw_display *display)
+{
+ u32 dp_cfg;
+ bool is_yuv = false;
+
+ if (hw->display.enable && hw->out == OUT_DP) {
+ switch (display->bus_format) {
+ case MEDIA_BUS_FMT_RGB565_1X16:
+ dp_cfg = 0;
+ break;
+ case MEDIA_BUS_FMT_RGB666_1X18:
+ dp_cfg = 1;
+ break;
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ dp_cfg = 2;
+ break;
+ case MEDIA_BUS_FMT_RGB101010_1X30:
+ dp_cfg = 3;
+ break;
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+ dp_cfg = 0;
+ is_yuv = true;
+ break;
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ dp_cfg = 2 << 4;
+ is_yuv = true;
+ break;
+ case MEDIA_BUS_FMT_YUV8_1X24:
+ dp_cfg = 4 << 4;
+ is_yuv = true;
+ break;
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+ dp_cfg = 6 << 4;
+ is_yuv = true;
+ break;
+ case MEDIA_BUS_FMT_UYVY10_1X20:
+ dp_cfg = 8 << 4;
+ is_yuv = true;
+ break;
+ case MEDIA_BUS_FMT_YUV10_1X30:
+ dp_cfg = 10 << 4;
+ is_yuv = true;
+ break;
+ default:
+ dp_cfg = 2;
+ break;
+ }
+ if (is_yuv)
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX, BIT(7), 0);
+ else
+ dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX, 0, BIT(7));
+ dc_write(hw, DC_DISPLAY_DP_CONFIG, dp_cfg | BIT(3));
+ }
+
+ if (hw->out == OUT_DPI)
+ dc_set_clear(hw, DC_DISPLAY_DP_CONFIG, 0, BIT(3));
+
+ setup_display(hw, display);
+}
+
+static const struct dc_hw_funcs hw_func[] = {
+ {
+ .gamma = &gamma_commit,
+ .plane = &plane_commit,
+ .display = setup_display,
+ },
+ {
+ .gamma = &gamma_commit,
+ .plane = &plane_commit,
+ .display = setup_display,
+ },
+ {
+ .gamma = &gamma_ex_commit,
+ .plane = &plane_ex_commit,
+ .display = setup_display_ex,
+ },
+ {
+ .gamma = &gamma_ex_commit,
+ .plane = &plane_ex_commit,
+ .display = setup_display_ex,
+ },
+};
+
+void dc_hw_commit(struct dc_hw *hw)
+{
+ hw->func->gamma(hw);
+ hw->func->plane(hw);
+
+ if (hw->overlay.pos.dirty) {
+ dc_write(hw, DC_OVERLAY_TOP_LEFT,
+ hw->overlay.pos.start_x |
+ (hw->overlay.pos.start_y << 15));
+ dc_write(hw, DC_OVERLAY_BOTTOM_RIGHT,
+ hw->overlay.pos.end_x | (hw->overlay.pos.end_y << 15));
+ hw->overlay.pos.dirty = false;
+ }
+
+ if (hw->overlay.blend.dirty) {
+ dc_write(hw, DC_OVERLAY_SRC_GLOBAL_COLOR,
+ hw->overlay.blend.alpha << 24);
+ dc_write(hw, DC_OVERLAY_DST_GLOBAL_COLOR,
+ hw->overlay.blend.alpha << 24);
+ switch (hw->overlay.blend.blend_mode) {
+ case BLEND_PREMULTI:
+ dc_write(hw, DC_OVERLAY_BLEND_CONFIG, 0x3450);
+ break;
+ case BLEND_COVERAGE:
+ dc_write(hw, DC_OVERLAY_BLEND_CONFIG, 0x3950);
+ break;
+ case BLEND_PIXEL_NONE:
+ dc_write(hw, DC_OVERLAY_BLEND_CONFIG, 0x3548);
+ break;
+ default:
+ break;
+ }
+ hw->overlay.blend.dirty = false;
+ }
+
+ if (hw->cursor.dirty) {
+ if (hw->cursor.enable) {
+ dc_write(hw, DC_CURSOR_ADDRESS, hw->cursor.address);
+ dc_write(hw, DC_CURSOR_LOCATION,
+ hw->cursor.x | (hw->cursor.y << 16));
+ dc_write(hw, DC_CURSOR_CONFIG,
+ (hw->cursor.hot_x << 16) |
+ (hw->cursor.hot_y << 8) | 0x02);
+ } else {
+ dc_write(hw, DC_CURSOR_CONFIG, 0x00);
+ }
+ hw->cursor.dirty = false;
+ }
+}
+
+#ifdef CONFIG_ESWIN_MMU
+static u32 mmu_read(struct dc_hw *hw, u32 reg)
+{
+ return readl(hw->mmu_base + reg - MMU_REG_BASE);
+}
+
+static void mmu_write(struct dc_hw *hw, u32 reg, u32 value)
+{
+ writel(value, hw->mmu_base + reg - MMU_REG_BASE);
+}
+
+static void mmu_set_clear(struct dc_hw *hw, u32 reg, u32 set, u32 clear)
+{
+ u32 value = mmu_read(hw, reg);
+
+ value &= ~clear;
+ value |= set;
+ mmu_write(hw, reg, value);
+}
+
+int dc_hw_mmu_init(struct dc_hw *hw, dc_mmu_pt mmu)
+{
+ u32 mtlb = 0, ext_mtlb = 0;
+ u32 safe_addr = 0, ext_safe_addr = 0;
+ u32 config = 0;
+
+ mtlb = (u32)(mmu->mtlb_physical & 0xFFFFFFFF);
+ ext_mtlb = (u32)(mmu->mtlb_physical >> 32);
+
+ /* more than 40bit physical address */
+ if (ext_mtlb & 0xFFFFFF00) {
+ pr_err("Mtlb address out of range.\n");
+ return -EFAULT;
+ }
+
+ config = (ext_mtlb << 20) | (mtlb >> 12);
+ if (mmu->mode == MMU_MODE_1K)
+ mmu_set_clear(hw, MMU_REG_CONTEXT_PD, (config << 4) | BIT(0),
+ (0xFFFFFFF << 4) | (0x03));
+ else
+ mmu_set_clear(hw, MMU_REG_CONTEXT_PD, (config << 4),
+ (0xFFFFFFF << 4) | (0x03));
+
+ safe_addr = (u32)(mmu->safe_page_physical & 0xFFFFFFFF);
+ ext_safe_addr = (u32)(mmu->safe_page_physical >> 32);
+
+ if ((safe_addr & 0x3F) || (ext_safe_addr & 0xFFFFFF00)) {
+ pr_err("Invalid safe_address.\n");
+ return -EFAULT;
+ }
+
+ mmu_write(hw, MMU_REG_TABLE_ARRAY_SIZE, 1);
+ mmu_write(hw, MMU_REG_SAFE_SECURE, safe_addr);
+ mmu_write(hw, MMU_REG_SAFE_NON_SECURE, safe_addr);
+
+ mmu_set_clear(hw, MMU_REG_SAFE_EXT_ADDRESS,
+ (ext_safe_addr << 16) | ext_safe_addr,
+ BIT(31) | (0xFF << 16) | BIT(15) | 0xFF);
+
+ mmu_write(hw, MMU_REG_CONTROL, BIT(5) | BIT(0));
+
+ mmu_write(hw, DEC_REG_CONTROL, DEC_REG_CONTROL_VALUE);
+
+ return 0;
+}
+
+void dc_hw_enable_mmu_prefetch(struct dc_hw *hw, bool enable)
+{
+ if (enable)
+ dc_write(hw, DC_MMU_PREFETCH, BIT(0));
+ else
+ dc_write(hw, DC_MMU_PREFETCH, 0);
+}
+
+void dc_mmu_flush(struct dc_hw *hw)
+{
+ u32 config, read;
+
+ read = mmu_read(hw, MMU_REG_CONFIG);
+ config = read | BIT(4);
+
+ mmu_write(hw, MMU_REG_CONFIG, config);
+ mmu_write(hw, MMU_REG_CONFIG, read);
+}
+#endif
+
+void dc_hw_update_roi(struct dc_hw *hw, enum dc_hw_plane_id id,
+ struct dc_hw_roi *roi)
+{
+ struct dc_hw_plane *plane = NULL;
+
+ if (id == PRIMARY_PLANE)
+ plane = &hw->primary;
+ else if (id == OVERLAY_PLANE)
+ plane = &hw->overlay.plane;
+
+ if (plane) {
+ memcpy(&plane->roi, roi, sizeof(*roi) - sizeof(roi->dirty));
+ plane->roi.dirty = true;
+ }
+}
+
+void dc_hw_update_colorkey(struct dc_hw *hw, enum dc_hw_plane_id id,
+ struct dc_hw_colorkey *colorkey)
+{
+ struct dc_hw_plane *plane = NULL;
+
+ if (id == PRIMARY_PLANE)
+ plane = &hw->primary;
+ else if (id == OVERLAY_PLANE)
+ plane = &hw->overlay.plane;
+
+ if (plane) {
+ memcpy(&plane->colorkey, colorkey,
+ sizeof(*colorkey) - sizeof(colorkey->dirty));
+ plane->colorkey.dirty = true;
+ }
+}
diff --git a/drivers/gpu/drm/eswin/es_dc_hw.h b/drivers/gpu/drm/eswin/es_dc_hw.h
new file mode 100644
index 000000000000..885d225c9203
--- /dev/null
+++ b/drivers/gpu/drm/eswin/es_dc_hw.h
@@ -0,0 +1,473 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Eswin Holdings Co., Ltd.
+ */
+
+#ifndef __ES_DC_HW_H__
+#define __ES_DC_HW_H__
+
+#ifdef CONFIG_ESWIN_MMU
+#include "es_dc_mmu.h"
+#endif
+
+#define DC_HW_REVISION 0x24
+#define DC_HW_CHIP_CID 0x30
+
+#define DC_REG_BASE 0x1400
+
+#define DC_FRAMEBUFFER_CONFIG 0x1518
+#define DC_FRAMEBUFFER_CONFIG_EX 0x1CC0
+#define DC_FRAMEBUFFER_SCALE_CONFIG 0x1520
+#define DC_FRAMEBUFFER_ADDRESS 0x1400
+#define DC_FRAMEBUFFER_U_ADDRESS 0x1530
+#define DC_FRAMEBUFFER_V_ADDRESS 0x1538
+#define DC_FRAMEBUFFER_STRIDE 0x1408
+#define DC_FRAMEBUFFER_U_STRIDE 0x1800
+#define DC_FRAMEBUFFER_V_STRIDE 0x1808
+#define DC_FRAMEBUFFER_SIZE 0x1810
+#define DC_FRAMEBUFFER_SCALE_FACTOR_X 0x1828
+#define DC_FRAMEBUFFER_SCALE_FACTOR_Y 0x1830
+#define DC_FRAMEBUFFER_H_FILTER_COEF_INDEX 0x1838
+#define DC_FRAMEBUFFER_H_FILTER_COEF_DATA 0x1A00
+#define DC_FRAMEBUFFER_V_FILTER_COEF_INDEX 0x1A08
+#define DC_FRAMEBUFFER_V_FILTER_COEF_DATA 0x1A10
+#define DC_FRAMEBUFFER_INIT_OFFSET 0x1A20
+#define DC_FRAMEBUFFER_COLOR_KEY 0x1508
+#define DC_FRAMEBUFFER_COLOR_KEY_HIGH 0x1510
+#define DC_FRAMEBUFFER_CLEAR_VALUE 0x1A18
+#define DC_FRAMEBUFFER_COLOR_TABLE_INDEX 0x1818
+#define DC_FRAMEBUFFER_COLOR_TABLE_DATA 0x1820
+#define DC_FRAMEBUFFER_BG_COLOR 0x1528
+#define DC_FRAMEBUFFER_ROI_ORIGIN 0x1CB0
+#define DC_FRAMEBUFFER_ROI_SIZE 0x1CB8
+#define DC_FRAMEBUFFER_DEGAMMA_INDEX 0x1D88
+#define DC_FRAMEBUFFER_DEGAMMA_DATA 0x1D90
+#define DC_FRAMEBUFFER_DEGAMMA_EX_DATA 0x1D98
+#define DC_FRAMEBUFFER_YUVTORGB_COEF0 0x1DA0
+#define DC_FRAMEBUFFER_YUVTORGB_COEF1 0x1DA8
+#define DC_FRAMEBUFFER_YUVTORGB_COEF2 0x1DB0
+#define DC_FRAMEBUFFER_YUVTORGB_COEF3 0x1DB8
+#define DC_FRAMEBUFFER_YUVTORGB_COEF4 0x1E00
+#define DC_FRAMEBUFFER_YUVTORGB_COEFD0 0x1E08
+#define DC_FRAMEBUFFER_YUVTORGB_COEFD1 0x1E10
+#define DC_FRAMEBUFFER_YUVTORGB_COEFD2 0x1E18
+#define DC_FRAMEBUFFER_Y_CLAMP_BOUND 0x1E88
+#define DC_FRAMEBUFFER_UV_CLAMP_BOUND 0x1E90
+#define DC_FRAMEBUFFER_RGBTORGB_COEF0 0x1E20
+#define DC_FRAMEBUFFER_RGBTORGB_COEF1 0x1E28
+#define DC_FRAMEBUFFER_RGBTORGB_COEF2 0x1E30
+#define DC_FRAMEBUFFER_RGBTORGB_COEF3 0x1E38
+#define DC_FRAMEBUFFER_RGBTORGB_COEF4 0x1E40
+
+#define DC_OVERLAY_CONFIG 0x1540
+#define DC_OVERLAY_SCALE_CONFIG 0x1C00
+#define DC_OVERLAY_BLEND_CONFIG 0x1580
+#define DC_OVERLAY_TOP_LEFT 0x1640
+#define DC_OVERLAY_BOTTOM_RIGHT 0x1680
+#define DC_OVERLAY_ADDRESS 0x15C0
+#define DC_OVERLAY_U_ADDRESS 0x1840
+#define DC_OVERLAY_V_ADDRESS 0x1880
+#define DC_OVERLAY_STRIDE 0x1600
+#define DC_OVERLAY_U_STRIDE 0x18C0
+#define DC_OVERLAY_V_STRIDE 0x1900
+#define DC_OVERLAY_SIZE 0x17C0
+#define DC_OVERLAY_SCALE_FACTOR_X 0x1A40
+#define DC_OVERLAY_SCALE_FACTOR_Y 0x1A80
+#define DC_OVERLAY_H_FILTER_COEF_INDEX 0x1AC0
+#define DC_OVERLAY_H_FILTER_COEF_DATA 0x1B00
+#define DC_OVERLAY_V_FILTER_COEF_INDEX 0x1B40
+#define DC_OVERLAY_V_FILTER_COEF_DATA 0x1B80
+#define DC_OVERLAY_INIT_OFFSET 0x1BC0
+#define DC_OVERLAY_COLOR_KEY 0x1740
+#define DC_OVERLAY_COLOR_KEY_HIGH 0x1780
+#define DC_OVERLAY_CLEAR_VALUE 0x1940
+#define DC_OVERLAY_COLOR_TABLE_INDEX 0x1980
+#define DC_OVERLAY_COLOR_TABLE_DATA 0x19C0
+#define DC_OVERLAY_SRC_GLOBAL_COLOR 0x16C0
+#define DC_OVERLAY_DST_GLOBAL_COLOR 0x1700
+#define DC_OVERLAY_ROI_ORIGIN 0x1D00
+#define DC_OVERLAY_ROI_SIZE 0x1D40
+#define DC_OVERLAY_DEGAMMA_INDEX 0x2200
+#define DC_OVERLAY_DEGAMMA_DATA 0x2240
+#define DC_OVERLAY_DEGAMMA_EX_DATA 0x2280
+#define DC_OVERLAY_YUVTORGB_COEF0 0x1EC0
+#define DC_OVERLAY_YUVTORGB_COEF1 0x1F00
+#define DC_OVERLAY_YUVTORGB_COEF2 0x1F40
+#define DC_OVERLAY_YUVTORGB_COEF3 0x1F80
+#define DC_OVERLAY_YUVTORGB_COEF4 0x1FC0
+#define DC_OVERLAY_YUVTORGB_COEFD0 0x2000
+#define DC_OVERLAY_YUVTORGB_COEFD1 0x2040
+#define DC_OVERLAY_YUVTORGB_COEFD2 0x2080
+#define DC_OVERLAY_Y_CLAMP_BOUND 0x22C0
+#define DC_OVERLAY_UV_CLAMP_BOUND 0x2300
+#define DC_OVERLAY_RGBTORGB_COEF0 0x20C0
+#define DC_OVERLAY_RGBTORGB_COEF1 0x2100
+#define DC_OVERLAY_RGBTORGB_COEF2 0x2140
+#define DC_OVERLAY_RGBTORGB_COEF3 0x2180
+#define DC_OVERLAY_RGBTORGB_COEF4 0x21C0
+
+#define DC_CURSOR_CONFIG 0x1468
+#define DC_CURSOR_ADDRESS 0x146C
+#define DC_CURSOR_LOCATION 0x1470
+#define DC_CURSOR_BACKGROUND 0x1474
+#define DC_CURSOR_FOREGROUND 0x1478
+#define DC_CURSOR_CLK_GATING 0x1484
+
+#define DC_DISPLAY_DITHER_CONFIG 0x1410
+#define DC_DISPLAY_PANEL_CONFIG 0x1418
+#define DC_DISPLAY_DITHER_TABLE_LOW 0x1420
+#define DC_DISPLAY_DITHER_TABLE_HIGH 0x1428
+#define DC_DISPLAY_H 0x1430
+#define DC_DISPLAY_H_SYNC 0x1438
+#define DC_DISPLAY_V 0x1440
+#define DC_DISPLAY_V_SYNC 0x1448
+#define DC_DISPLAY_CURRENT_LOCATION 0x1450
+#define DC_DISPLAY_GAMMA_INDEX 0x1458
+#define DC_DISPLAY_GAMMA_DATA 0x1460
+#define DC_DISPLAY_INT 0x147C
+#define DC_DISPLAY_INT_ENABLE 0x1480
+#define DC_DISPLAY_DBI_CONFIG 0x1488
+#define DC_DISPLAY_GENERAL_CONFIG 0x14B0
+#define DC_DISPLAY_DPI_CONFIG 0x14B8
+#define DC_DISPLAY_DEBUG_COUNTER_SELECT 0x14D0
+#define DC_DISPLAY_DEBUG_COUNTER_VALUE 0x14D8
+#define DC_DISPLAY_DP_CONFIG 0x1CD0
+#define DC_DISPLAY_GAMMA_EX_INDEX 0x1CF0
+#define DC_DISPLAY_GAMMA_EX_DATA 0x1CF8
+#define DC_DISPLAY_GAMMA_EX_ONE_DATA 0x1D80
+#define DC_DISPLAY_RGBTOYUV_COEF0 0x1E48
+#define DC_DISPLAY_RGBTOYUV_COEF1 0x1E50
+#define DC_DISPLAY_RGBTOYUV_COEF2 0x1E58
+#define DC_DISPLAY_RGBTOYUV_COEF3 0x1E60
+#define DC_DISPLAY_RGBTOYUV_COEF4 0x1E68
+#define DC_DISPLAY_RGBTOYUV_COEFD0 0x1E70
+#define DC_DISPLAY_RGBTOYUV_COEFD1 0x1E78
+#define DC_DISPLAY_RGBTOYUV_COEFD2 0x1E80
+
+#define DC_CLK_GATTING 0x1A28
+
+#define DC_TRANSPARENCY_OPAQUE 0x00
+#define DC_TRANSPARENCY_KEY 0x02
+#define DC_DISPLAY_DITHERTABLE_LOW 0x7B48F3C0
+#define DC_DISPLAY_DITHERTABLE_HIGH 0x596AD1E2
+
+#define GAMMA_SIZE 256
+#define GAMMA_EX_SIZE 300
+#define DEGAMMA_SIZE 260
+
+#define RGB_TO_RGB_TABLE_SIZE 9
+#define YUV_TO_RGB_TABLE_SIZE 16
+#define RGB_TO_YUV_TABLE_SIZE 12
+
+#ifdef CONFIG_ESWIN_MMU
+#define DC_MMU_PREFETCH 0x1E98
+
+#define MMU_REG_BASE 0x0180
+
+#define MMU_REG_CONFIG 0x0184
+#define MMU_REG_CONTROL 0x0388
+#define MMU_REG_TABLE_ARRAY_SIZE 0x0394
+#define MMU_REG_SAFE_NON_SECURE 0x0398
+#define MMU_REG_SAFE_SECURE 0x039C
+#define MMU_REG_SAFE_EXT_ADDRESS 0x03A0
+#define MMU_REG_CONTEXT_PD 0x03B4
+
+#define DEC_REG_CONTROL 0x0800
+#define DEC_REG_CONTROL_VALUE 0x02010188
+#endif
+
+enum dc_hw_chip_rev {
+ DC_REV_5551,
+ DC_REV_5551_306,
+ DC_REV_5701_303,
+ DC_REV_5701_309,
+};
+
+enum dc_hw_plane_id { PRIMARY_PLANE, CURSOR_PLANE, OVERLAY_PLANE, PLANE_NUM };
+
+enum dc_hw_color_format {
+ FORMAT_X4R4G4B4,
+ FORMAT_A4R4G4B4,
+ FORMAT_X1R5G5B5,
+ FORMAT_A1R5G5B5,
+ FORMAT_R5G6B5,
+ FORMAT_X8R8G8B8,
+ FORMAT_A8R8G8B8,
+ FORMAT_YUY2,
+ FORMAT_UYVY,
+ FORMAT_INDEX8,
+ FORMAT_MONOCHROME,
+ FORMAT_YV12 = 0xf,
+ FORMAT_A8,
+ FORMAT_NV12,
+ FORMAT_NV16,
+ FORMAT_RG16,
+ FORMAT_R8,
+ FORMAT_NV12_10BIT,
+ FORMAT_A2R10G10B10,
+ FORMAT_NV16_10BIT,
+ FORMAT_INDEX1,
+ FORMAT_INDEX2,
+ FORMAT_INDEX4,
+ FORMAT_P010,
+};
+
+enum dc_hw_yuv_color_space {
+ COLOR_SPACE_601 = 0,
+ COLOR_SPACE_709 = 1,
+ COLOR_SPACE_2020 = 3,
+};
+
+enum dc_hw_rotation {
+ ROT_0 = 0,
+ ROT_90 = 4,
+ ROT_180 = 5,
+ ROT_270 = 6,
+ FLIP_X = 1,
+ FLIP_Y = 2,
+ FLIP_XY = 3,
+};
+
+enum dc_hw_swizzle {
+ SWIZZLE_ARGB = 0,
+ SWIZZLE_RGBA,
+ SWIZZLE_ABGR,
+ SWIZZLE_BGRA,
+};
+
+enum dc_hw_out {
+ OUT_DPI,
+ OUT_DP,
+};
+
+enum dc_hw_blend_mode {
+ /* out.rgb = plane_alpha * fg.rgb +
+ * (1 - (plane_alpha * fg.alpha)) * bg.rgb
+ */
+ BLEND_PREMULTI,
+ /* out.rgb = plane_alpha * fg.alpha * fg.rgb +
+ * (1 - (plane_alpha * fg.alpha)) * bg.rgb
+ */
+ BLEND_COVERAGE,
+ /* out.rgb = plane_alpha * fg.rgb +
+ * (1 - plane_alpha) * bg.rgb
+ */
+ BLEND_PIXEL_NONE,
+};
+
+struct dc_hw_plane_reg {
+ u32 y_address;
+ u32 u_address;
+ u32 v_address;
+ u32 y_stride;
+ u32 u_stride;
+ u32 v_stride;
+ u32 size;
+ u32 scale_factor_x;
+ u32 scale_factor_y;
+ u32 h_filter_coef_index;
+ u32 h_filter_coef_data;
+ u32 v_filter_coef_index;
+ u32 v_filter_coef_data;
+ u32 init_offset;
+ u32 color_key;
+ u32 color_key_high;
+ u32 clear_value;
+ u32 color_table_index;
+ u32 color_table_data;
+ u32 scale_config;
+ u32 degamma_index;
+ u32 degamma_data;
+ u32 degamma_ex_data;
+ u32 roi_origin;
+ u32 roi_size;
+ u32 YUVToRGBCoef0;
+ u32 YUVToRGBCoef1;
+ u32 YUVToRGBCoef2;
+ u32 YUVToRGBCoef3;
+ u32 YUVToRGBCoef4;
+ u32 YUVToRGBCoefD0;
+ u32 YUVToRGBCoefD1;
+ u32 YUVToRGBCoefD2;
+ u32 YClampBound;
+ u32 UVClampBound;
+ u32 RGBToRGBCoef0;
+ u32 RGBToRGBCoef1;
+ u32 RGBToRGBCoef2;
+ u32 RGBToRGBCoef3;
+ u32 RGBToRGBCoef4;
+};
+
+struct dc_hw_fb {
+ u32 y_address;
+ u32 u_address;
+ u32 v_address;
+ u32 clear_value;
+ u16 y_stride;
+ u16 u_stride;
+ u16 v_stride;
+ u16 width;
+ u16 height;
+ u8 format;
+ u8 tile_mode;
+ u8 rotation;
+ u8 yuv_color_space;
+ u8 swizzle;
+ u8 uv_swizzle;
+ bool clear_enable;
+ bool enable;
+ bool dirty;
+};
+
+struct dc_hw_scale {
+ u32 scale_factor_x;
+ u32 scale_factor_y;
+ bool enable;
+ bool dirty;
+};
+
+struct dc_hw_position {
+ u16 start_x;
+ u16 start_y;
+ u16 end_x;
+ u16 end_y;
+ bool dirty;
+};
+
+struct dc_hw_blend {
+ u8 alpha;
+ u8 blend_mode;
+ bool dirty;
+};
+
+struct dc_hw_colorkey {
+ u32 colorkey;
+ u32 colorkey_high;
+ u8 transparency;
+ bool dirty;
+};
+
+struct dc_hw_roi {
+ u16 x;
+ u16 y;
+ u16 width;
+ u16 height;
+ bool enable;
+ bool dirty;
+};
+
+struct dc_hw_cursor {
+ u32 address;
+ u16 x;
+ u16 y;
+ u16 hot_x;
+ u16 hot_y;
+ bool enable;
+ bool dirty;
+};
+
+struct dc_hw_display {
+ u32 bus_format;
+ u16 h_active;
+ u16 h_total;
+ u16 h_sync_start;
+ u16 h_sync_end;
+ u16 v_active;
+ u16 v_total;
+ u16 v_sync_start;
+ u16 v_sync_end;
+ u16 sync_mode;
+ u32 bg_color;
+ bool h_sync_polarity;
+ bool v_sync_polarity;
+ bool enable;
+ bool dither_enable;
+};
+
+struct dc_hw_gamma {
+ u16 gamma[GAMMA_EX_SIZE][3];
+ bool enable;
+ bool dirty;
+};
+
+struct dc_hw_degamma {
+ u16 degamma[DEGAMMA_SIZE][3];
+ u32 mode;
+ bool dirty;
+};
+
+struct dc_hw_plane {
+ struct dc_hw_fb fb;
+ struct dc_hw_scale scale;
+ struct dc_hw_roi roi;
+ struct dc_hw_colorkey colorkey;
+ struct dc_hw_degamma degamma;
+};
+
+struct dc_hw_overlay {
+ struct dc_hw_plane plane;
+ struct dc_hw_position pos;
+ struct dc_hw_blend blend;
+};
+
+struct dc_hw_read {
+ u32 reg;
+ u32 value;
+};
+
+struct dc_hw;
+struct dc_hw_funcs {
+ void (*gamma)(struct dc_hw *hw);
+ void (*plane)(struct dc_hw *hw);
+ void (*display)(struct dc_hw *hw, struct dc_hw_display *display);
+};
+
+struct dc_hw {
+ enum dc_hw_chip_rev rev;
+ enum dc_hw_out out;
+ void *hi_base;
+ void *reg_base;
+#ifdef CONFIG_ESWIN_MMU
+ void *mmu_base;
+#endif
+ struct dc_hw_read *read_block;
+ struct dc_hw_display display;
+ struct dc_hw_gamma gamma;
+ struct dc_hw_plane primary;
+ struct dc_hw_overlay overlay;
+ struct dc_hw_cursor cursor;
+ struct dc_hw_funcs *func;
+ struct es_dc_info *info;
+};
+
+int dc_hw_init(struct dc_hw *hw);
+void dc_hw_deinit(struct dc_hw *hw);
+void dc_hw_update_plane(struct dc_hw *hw, enum dc_hw_plane_id id,
+ struct dc_hw_fb *fb, struct dc_hw_scale *scale);
+void dc_hw_update_degamma(struct dc_hw *hw, enum dc_hw_plane_id id, u32 mode);
+void dc_hw_set_position(struct dc_hw *hw, struct dc_hw_position *pos);
+void dc_hw_set_blend(struct dc_hw *hw, struct dc_hw_blend *blend);
+void dc_hw_update_cursor(struct dc_hw *hw, struct dc_hw_cursor *cursor);
+void dc_hw_update_gamma(struct dc_hw *hw, u16 index, u16 r, u16 g, u16 b);
+void dc_hw_enable_gamma(struct dc_hw *hw, bool enable);
+void dc_hw_enable_dump(struct dc_hw *hw, u32 addr, u32 pitch);
+void dc_hw_disable_dump(struct dc_hw *hw);
+void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display);
+void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable);
+u32 dc_hw_get_interrupt(struct dc_hw *hw);
+bool dc_hw_flip_in_progress(struct dc_hw *hw);
+bool dc_hw_check_underflow(struct dc_hw *hw);
+void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable);
+void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out);
+void dc_hw_commit(struct dc_hw *hw);
+#ifdef CONFIG_ESWIN_MMU
+int dc_hw_mmu_init(struct dc_hw *hw, dc_mmu_pt mmu);
+void dc_hw_enable_mmu_prefetch(struct dc_hw *hw, bool enable);
+void dc_mmu_flush(struct dc_hw *hw);
+#endif
+void dc_hw_update_roi(struct dc_hw *hw, enum dc_hw_plane_id id,
+ struct dc_hw_roi *roi);
+void dc_hw_update_colorkey(struct dc_hw *hw, enum dc_hw_plane_id id,
+ struct dc_hw_colorkey *colorkey);
+
+#endif /* __ES_DC_HW_H__ */
diff --git a/drivers/gpu/drm/eswin/es_dc_mmu.c b/drivers/gpu/drm/eswin/es_dc_mmu.c
new file mode 100644
index 000000000000..80cbeb421740
--- /dev/null
+++ b/drivers/gpu/drm/eswin/es_dc_mmu.c
@@ -0,0 +1,778 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Eswin Holdings Co., Ltd.
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/pagemap.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+
+#include <asm/io.h>
+#include <asm/delay.h>
+
+#include "es_dc_mmu.h"
+
+static bool mmu_construct = false;
+
+int _allocate_memory(u32 bytes, void **memory)
+{
+ void *mem = NULL;
+
+ if (bytes == 0 || memory == NULL) {
+ pr_err("%s has invalid arguments.\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ if (bytes > PAGE_SIZE) {
+ mem = vmalloc(bytes);
+ } else {
+ mem = kmalloc(bytes, GFP_KERNEL);
+ }
+
+ if (!mem) {
+ pr_err("%s out of memory.\n", __FUNCTION__);
+ return -ENOMEM;
+ }
+
+ memset((u8 *)mem, 0, bytes);
+ *memory = mem;
+
+ return 0;
+}
+
+static int _create_mutex(void **mutex)
+{
+ int ret = 0;
+
+ if (mutex == NULL) {
+ return -EINVAL;
+ }
+
+ ret = _allocate_memory(sizeof(struct mutex), mutex);
+ if (ret)
+ return ret;
+
+ mutex_init(*(struct mutex **)mutex);
+
+ return 0;
+}
+
+static int _acquire_mutex(void *mutex, u32 timeout)
+{
+ if (mutex == NULL) {
+ pr_err("%s has invalid argument.\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ if (timeout == DC_INFINITE) {
+ mutex_lock(mutex);
+ return 0;
+ }
+
+ for (;;) {
+ /* Try to acquire the mutex. */
+ if (mutex_trylock(mutex)) {
+ /* Success. */
+ return 0;
+ }
+
+ if (timeout-- == 0) {
+ break;
+ }
+
+ /* Wait for 1 millisecond. */
+ udelay(1000);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int _release_mutex(void *mutex)
+{
+ if (mutex == NULL) {
+ pr_err("%s has invalid argument.\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ mutex_unlock(mutex);
+
+ return 0;
+}
+
+static u32 _mtlb_offset(u32 address)
+{
+ return (address & MMU_MTLB_MASK) >> MMU_MTLB_SHIFT;
+}
+
+static u32 _stlb_offset(u32 address)
+{
+ return (address & MMU_STLB_4K_MASK) >> MMU_STLB_4K_SHIFT;
+}
+
+static u32 _address_to_index(dc_mmu_pt mmu, u32 address)
+{
+ return _mtlb_offset(address) * MMU_STLB_4K_ENTRY_NUM +
+ _stlb_offset(address);
+}
+
+static u32 _set_page(u32 page_address, u32 page_address_ext, bool writable)
+{
+ u32 entry = page_address
+ /* AddressExt */
+ | (page_address_ext << 4)
+ /* Ignore exception */
+ | (0 << 1)
+ /* Present */
+ | (1 << 0);
+
+ if (writable) {
+ /* writable */
+ entry |= (1 << 2);
+ }
+
+ return entry;
+}
+
+static void _write_page_entry(u32 *page_entry, u32 entry_value)
+{
+ *page_entry = entry_value;
+}
+
+static u32 _read_page_entry(u32 *page_entry)
+{
+ return *page_entry;
+}
+
+int _allocate_stlb(dc_mmu_stlb_pt *stlb)
+{
+ dc_mmu_stlb_pt stlb_t = NULL;
+ void *mem = NULL;
+
+ mem = kzalloc(sizeof(dc_mmu_stlb), GFP_KERNEL);
+ if (!mem)
+ return -ENOMEM;
+
+ stlb_t = (dc_mmu_stlb_pt)mem;
+
+ stlb_t->size = MMU_STLB_4K_SIZE;
+
+ *stlb = stlb_t;
+
+ return 0;
+}
+
+int _allocate_all_stlb(struct device *dev, dc_mmu_stlb_pt *stlb)
+{
+ dc_mmu_stlb_pt stlb_t = NULL;
+ void *mem = NULL;
+ void *cookie = NULL;
+ dma_addr_t dma_addr;
+ size_t size;
+
+ mem = kzalloc(sizeof(dc_mmu_stlb), GFP_KERNEL);
+ if (!mem)
+ return -ENOMEM;
+
+ stlb_t = (dc_mmu_stlb_pt)mem;
+
+ stlb_t->size = MMU_STLB_4K_SIZE * MMU_MTLB_ENTRY_NUM;
+ size = PAGE_ALIGN(stlb_t->size);
+
+ cookie = dma_alloc_wc(dev, size, &dma_addr, GFP_KERNEL);
+ if (!cookie) {
+ dev_err(dev, "Failed to alloc stlb buffer.\n");
+ return -ENOMEM;
+ }
+
+ stlb_t->logical = cookie;
+ stlb_t->physBase = (u64)dma_addr;
+ memset(stlb_t->logical, 0, size);
+
+ *stlb = stlb_t;
+
+ return 0;
+}
+
+int _setup_process_address_space(struct device *dev, dc_mmu_pt mmu)
+{
+ u32 *map = NULL;
+ u32 free, i;
+ u32 dynamic_mapping_entries, address;
+ dc_mmu_stlb_pt all_stlb;
+ int ret = 0;
+
+ dynamic_mapping_entries = MMU_MTLB_ENTRY_NUM;
+ mmu->dynamic_mapping_start = 0;
+ mmu->page_table_size = dynamic_mapping_entries * MMU_STLB_4K_SIZE;
+
+ mmu->page_table_entries = mmu->page_table_size / sizeof(u32);
+
+ ret = _allocate_memory(mmu->page_table_size,
+ (void **)&mmu->map_logical);
+ if (ret) {
+ pr_err("Failed to alloc mmu map buffer.\n");
+ return ret;
+ ;
+ }
+
+ ret = _allocate_memory(mmu->page_table_size,
+ (void **)&mmu->page_entry_info);
+ if (ret) {
+ pr_err("Failed to alloc entry info buffer.\n");
+ return ret;
+ ;
+ }
+
+ map = mmu->map_logical;
+
+ /* Initialize free area */
+ free = mmu->page_table_entries;
+ _write_page_entry(map, (free << 8) | DC_MMU_FREE);
+ _write_page_entry(map + 1, ~0U);
+
+ mmu->heap_list = 0;
+ mmu->free_nodes = false;
+
+ ret = _allocate_all_stlb(dev, &all_stlb);
+ if (ret)
+ return ret;
+
+ BUG_ON((all_stlb->physBase & (0xffUL << 32)) !=
+ (mmu->mtlb_physical & (0xffUL << 32)));
+
+ mmu->stlb_physical = all_stlb->physBase;
+ mmu->stlb_bytes = all_stlb->size;
+
+ for (i = 0; i < dynamic_mapping_entries; i++) {
+ dc_mmu_stlb_pt stlb;
+ dc_mmu_stlb_pt *stlbs = (dc_mmu_stlb_pt *)mmu->stlbs;
+
+ ret = _allocate_stlb(&stlb);
+ if (ret)
+ return ret;
+
+ stlb->physBase = all_stlb->physBase + i * MMU_STLB_4K_SIZE;
+ stlb->logical =
+ all_stlb->logical + i * MMU_STLB_4K_SIZE / sizeof(u32);
+
+ stlbs[i] = stlb;
+ }
+
+ address = (u32)all_stlb->physBase;
+
+ ret = _acquire_mutex(mmu->page_table_mutex, DC_INFINITE);
+ if (ret)
+ return ret;
+
+ for (i = mmu->dynamic_mapping_start;
+ i < mmu->dynamic_mapping_start + dynamic_mapping_entries; i++) {
+ u32 mtlb_entry;
+
+ mtlb_entry = address | MMU_MTLB_4K_PAGE | MMU_MTLB_PRESENT;
+
+ address += MMU_STLB_4K_SIZE;
+
+ /* Insert Slave TLB address to Master TLB entry.*/
+ _write_page_entry(mmu->mtlb_logical + i, mtlb_entry);
+ }
+
+ kfree(all_stlb);
+ _release_mutex(mmu->page_table_mutex);
+
+ return 0;
+}
+
+/* MMU Construct */
+int dc_mmu_construct(struct device *dev, dc_mmu_pt *mmu)
+{
+ dc_mmu_pt mmu_t = NULL;
+ void *mem = NULL;
+ void *cookie = NULL, *cookie_safe = NULL;
+ dma_addr_t dma_addr, dma_addr_safe;
+ u32 size = 0;
+ int ret = 0;
+
+ if (mmu_construct)
+ return 0;
+
+ mem = kzalloc(sizeof(dc_mmu), GFP_KERNEL);
+ if (!mem)
+ return -ENOMEM;
+
+ mmu_t = (dc_mmu_pt)mem;
+ mmu_t->mtlb_bytes = MMU_MTLB_SIZE;
+ size = PAGE_ALIGN(mmu_t->mtlb_bytes);
+ if (size == PAGE_SIZE) {
+ size = PAGE_SIZE * 2;
+ }
+
+ /* Allocate MTLB */
+ cookie = dma_alloc_wc(dev, size, &dma_addr, GFP_KERNEL);
+ if (!cookie) {
+ dev_err(dev, "Failed to alloc mtlb buffer.\n");
+ return -ENOMEM;
+ }
+
+ mmu_t->mtlb_logical = cookie;
+ mmu_t->mtlb_physical = (u64)dma_addr;
+ memset(mmu_t->mtlb_logical, 0, size);
+
+ size = MMU_MTLB_ENTRY_NUM * sizeof(dc_mmu_stlb_pt);
+
+ ret = _allocate_memory(size, &mmu_t->stlbs);
+ if (ret)
+ return ret;
+
+ ret = _create_mutex(&mmu_t->page_table_mutex);
+ if (ret)
+ return ret;
+
+ mmu_t->mode = MMU_MODE_1K;
+
+ ret = _setup_process_address_space(dev, mmu_t);
+ if (ret)
+ return ret;
+
+ /* Allocate safe page */
+ cookie_safe = dma_alloc_wc(dev, 4096, &dma_addr_safe, GFP_KERNEL);
+ if (!cookie_safe) {
+ dev_err(dev, "Failed to alloc safe page.\n");
+ return -ENOMEM;
+ }
+
+ mmu_t->safe_page_logical = cookie_safe;
+ mmu_t->safe_page_physical = (u64)dma_addr_safe;
+ memset(mmu_t->safe_page_logical, 0, size);
+
+ *mmu = mmu_t;
+ mmu_construct = true;
+
+ return 0;
+}
+
+int dc_mmu_get_page_entry(dc_mmu_pt mmu, u32 address, u32 **page_table)
+{
+ dc_mmu_stlb_pt stlb;
+ dc_mmu_stlb_pt *stlbs = (dc_mmu_stlb_pt *)mmu->stlbs;
+ u32 mtlb_offset = _mtlb_offset(address);
+ u32 stlb_offset = _stlb_offset(address);
+
+ stlb = stlbs[mtlb_offset - mmu->dynamic_mapping_start];
+ if (stlb == NULL) {
+ pr_err("BUG: invalid stlb, mmu=%p stlbs=%p mtlb_offset=0x%x %s(%d)\n",
+ mmu, stlbs, mtlb_offset, __FUNCTION__, __LINE__);
+ return -ENXIO;
+ }
+
+ *page_table = &stlb->logical[stlb_offset];
+
+ return 0;
+}
+
+int _link(dc_mmu_pt mmu, u32 index, u32 node)
+{
+ if (index >= mmu->page_table_entries) {
+ mmu->heap_list = node;
+ } else {
+ u32 *map = mmu->map_logical;
+
+ switch (DC_ENTRY_TYPE(_read_page_entry(&map[index]))) {
+ case DC_MMU_SINGLE:
+ /* Previous is a single node, link to it*/
+ _write_page_entry(&map[index],
+ (node << 8) | DC_MMU_SINGLE);
+ break;
+ case DC_MMU_FREE:
+ /* Link to FREE TYPE node */
+ _write_page_entry(&map[index + 1], node);
+ break;
+ default:
+ pr_err("MMU table corrupted at index %u!", index);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+int _add_free(dc_mmu_pt mmu, u32 index, u32 node, u32 count)
+{
+ u32 *map = mmu->map_logical;
+
+ if (count == 1) {
+ /* Initialize a single page node */
+ _write_page_entry(map + node, DC_SINGLE_PAGE_NODE_INITIALIZE |
+ DC_MMU_SINGLE);
+ } else {
+ /* Initialize the FREE node*/
+ _write_page_entry(map + node, (count << 8) | DC_MMU_FREE);
+ _write_page_entry(map + node + 1, ~0U);
+ }
+
+ return _link(mmu, index, node);
+}
+
+/* Collect free nodes */
+int _collect(dc_mmu_pt mmu)
+{
+ u32 *map = mmu->map_logical;
+ u32 count = 0, start = 0, i = 0;
+ u32 previous = ~0U;
+ int ret = 0;
+
+ mmu->heap_list = ~0U;
+ mmu->free_nodes = false;
+
+ /* Walk the entire page table */
+ for (i = 0; i < mmu->page_table_entries; i++) {
+ switch (DC_ENTRY_TYPE(_read_page_entry(&map[i]))) {
+ case DC_MMU_SINGLE:
+ if (count++ == 0) {
+ /* Set new start node */
+ start = i;
+ }
+ break;
+ case DC_MMU_FREE:
+ if (count == 0) {
+ /* Set new start node */
+ start = i;
+ }
+
+ count += _read_page_entry(&map[i]) >> 8;
+ /* Advance the index of the page table */
+ i += (_read_page_entry(&map[i]) >> 8) - 1;
+ break;
+ case DC_MMU_USED:
+ /* Meet used node, start to collect */
+ if (count > 0) {
+ /* Add free node to list*/
+ ret = _add_free(mmu, previous, start, count);
+ if (ret)
+ return ret;
+ /* Reset previous unused node index */
+ previous = start;
+ count = 0;
+ }
+ break;
+ default:
+ pr_err("MMU page table corrupted at index %u!", i);
+ return -EINVAL;
+ }
+ }
+
+ /* If left node is an open node. */
+ if (count > 0) {
+ ret = _add_free(mmu, previous, start, count);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int _fill_page_table(u32 *page_table, u32 page_count, u32 entry_value)
+{
+ u32 i;
+
+ for (i = 0; i < page_count; i++) {
+ _write_page_entry(page_table + i, entry_value);
+ }
+
+ return 0;
+}
+
+int dc_mmu_allocate_pages(dc_mmu_pt mmu, u32 page_count, u32 *address)
+{
+ bool got = false, acquired = false;
+ u32 *map;
+ u32 index = 0, vaddr, left;
+ u32 previous = ~0U;
+ u32 mtlb_offset, stlb_offset;
+ int ret = 0;
+
+ if (page_count == 0 || page_count > mmu->page_table_entries) {
+ pr_err("%s has invalid arguments.\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ _acquire_mutex(mmu->page_table_mutex, DC_INFINITE);
+ acquired = true;
+
+ for (map = mmu->map_logical; !got;) {
+ for (index = mmu->heap_list;
+ !got && (index < mmu->page_table_entries);) {
+ switch (DC_ENTRY_TYPE(_read_page_entry(&map[index]))) {
+ case DC_MMU_SINGLE:
+ if ((index != 0) && (page_count == 1)) {
+ got = true;
+ } else {
+ /* Move to next node */
+ previous = index;
+ index = _read_page_entry(&map[index]) >>
+ 8;
+ }
+ break;
+ case DC_MMU_FREE:
+ if ((index == 0) &&
+ (page_count <
+ ((_read_page_entry(&map[index]) >> 8)))) {
+ got = true;
+ } else if ((index != 0) &&
+ (page_count <=
+ ((_read_page_entry(&map[index]) >>
+ 8)))) {
+ got = true;
+ } else {
+ /* Move to next node */
+ previous = index;
+ index = _read_page_entry(
+ &map[index + 1]);
+ }
+ break;
+ default:
+ /* Only link SINGLE and FREE node */
+ pr_err("MMU table corrupted at index %u!",
+ index);
+ ret = -EINVAL;
+ goto OnError;
+ }
+ }
+
+ /* If out of index */
+ if (index >= mmu->page_table_entries) {
+ if (mmu->free_nodes) {
+ /* Collect the free node */
+ ret = _collect(mmu);
+ if (ret)
+ goto OnError;
+ previous = ~0U;
+ } else {
+ ret = -ENODATA;
+ goto OnError;
+ }
+ }
+ }
+
+ switch (DC_ENTRY_TYPE(_read_page_entry(&map[index]))) {
+ case DC_MMU_SINGLE:
+ /* Unlink single node from node list */
+ ret = _link(mmu, previous, _read_page_entry(&map[index]) >> 8);
+ if (ret)
+ goto OnError;
+ break;
+
+ case DC_MMU_FREE:
+ left = (_read_page_entry(&map[index]) >> 8) - page_count;
+ switch (left) {
+ case 0:
+ /* Unlink the entire FREE type node */
+ ret = _link(mmu, previous,
+ _read_page_entry(&map[index + 1]));
+ if (ret)
+ goto OnError;
+ break;
+ case 1:
+ /* Keep the map[index] as a single node,
+ * mark the left as used
+ */
+ _write_page_entry(&map[index],
+ (_read_page_entry(&map[index + 1])
+ << 8) | DC_MMU_SINGLE);
+ index++;
+ break;
+ default:
+ /* FREE type node left */
+ _write_page_entry(&map[index],
+ (left << 8) | DC_MMU_FREE);
+ index += left;
+ if (index + page_count > mmu->page_table_entries) {
+ pr_err("Error: index %d , left %d\n", index,
+ left);
+ ret = -EINVAL;
+ goto OnError;
+ }
+
+ break;
+ }
+ break;
+ default:
+ /* Only link SINGLE and FREE node */
+ pr_err("MMU table corrupted at index %u!", index);
+ ret = -EINVAL;
+ goto OnError;
+ }
+
+ if (index + page_count > mmu->page_table_entries) {
+ pr_err("Fatal ERROR: This may caused by free pages more than allocated pages\n");
+ _write_page_entry(mmu->map_logical,
+ (mmu->page_table_entries << 8) | DC_MMU_FREE);
+ _write_page_entry(mmu->map_logical + 1, ~0U);
+ previous = ~0U;
+ mmu->heap_list = 0;
+ memset(mmu->page_entry_info, 0,
+ mmu->page_table_entries * sizeof(u32));
+
+ ret = -EINVAL;
+ goto OnError;
+ }
+
+ /* Mark node as used */
+ ret = _fill_page_table(&map[index], page_count, DC_MMU_USED);
+ if (ret)
+ goto OnError;
+
+ _release_mutex(mmu->page_table_mutex);
+
+ mtlb_offset =
+ index / MMU_STLB_4K_ENTRY_NUM + mmu->dynamic_mapping_start;
+ stlb_offset = index % MMU_STLB_4K_ENTRY_NUM;
+
+ vaddr = (mtlb_offset << MMU_MTLB_SHIFT) |
+ (stlb_offset << MMU_STLB_4K_SHIFT);
+
+ if (vaddr == 0) {
+ pr_err("Error occured on alloc vaddr\n");
+ ret = -ENODATA;
+ goto OnError;
+ }
+
+ mmu->page_entry_info[index] = (index << 12) | (page_count & 0xfff);
+
+ if (address != NULL) {
+ *address = vaddr;
+ }
+
+ return 0;
+
+OnError:
+ if (acquired) {
+ _release_mutex(mmu->page_table_mutex);
+ }
+
+ return ret;
+}
+
+int dc_mmu_free_pages(dc_mmu_pt mmu, u32 address, u32 page_count)
+{
+ u32 *node, index;
+
+ if (page_count == 0)
+ return -EINVAL;
+
+ index = _address_to_index(mmu, address);
+ node = mmu->map_logical + _address_to_index(mmu, address);
+
+ _acquire_mutex(mmu->page_table_mutex, DC_INFINITE);
+
+ if (((mmu->page_entry_info[index] & 0xfff) != (page_count & 0xfff)) ||
+ ((mmu->page_entry_info[index] >> 12) != index)) {
+ pr_err("index %d, page_count %d: free pages not equal to alloc\n",
+ index, page_count);
+ _release_mutex(mmu->page_table_mutex);
+ return -EINVAL;
+ } else {
+ mmu->page_entry_info[index] = 0;
+ }
+
+ if (page_count == 1) {
+ /* Mark the Single page node free */
+ _write_page_entry(node, DC_SINGLE_PAGE_NODE_INITIALIZE |
+ DC_MMU_SINGLE);
+ } else {
+ /* Mark the FREE type node free */
+ _write_page_entry(node, (page_count << 8) | DC_MMU_FREE);
+ _write_page_entry(node + 1, ~0U);
+ }
+
+ mmu->free_nodes = true;
+
+ _release_mutex(mmu->page_table_mutex);
+
+ return 0;
+}
+
+int dc_mmu_set_page(dc_mmu_pt mmu, u64 page_address, u32 *page_entry)
+{
+ u32 address_ext;
+ u32 address;
+
+ if (page_entry == NULL || (page_address & 0xFFF)) {
+ return -EINVAL;
+ }
+
+ /* [31:0]. */
+ address = (u32)(page_address & 0xFFFFFFFF);
+ /* [39:32]. */
+ address_ext = (u32)((page_address >> 32) & 0xFF);
+
+ _write_page_entry(page_entry, _set_page(address, address_ext, true));
+
+ return 0;
+}
+
+int dc_mmu_map_memory(dc_mmu_pt mmu, u64 physical, u32 page_count, u32 *address,
+ bool continuous)
+{
+ u32 virutal_address, i = 0;
+ bool allocated = false;
+ int ret = 0;
+
+ ret = dc_mmu_allocate_pages(mmu, page_count, &virutal_address);
+ if (ret)
+ goto OnError;
+
+ if (continuous)
+ *address = virutal_address | (physical & MMU_PAGE_4K_MASK);
+ else
+ *address = virutal_address;
+
+ allocated = true;
+
+ /* Fill in page table */
+ for (i = 0; i < page_count; i++) {
+ u64 page_phy;
+ u32 *page_entry;
+ struct page **pages;
+
+ if (continuous == true) {
+ page_phy = physical + i * MMU_PAGE_4K_SIZE;
+ } else {
+ pages = (struct page **)physical;
+ page_phy = page_to_phys(pages[i]);
+ }
+
+ ret = dc_mmu_get_page_entry(mmu, virutal_address, &page_entry);
+ if (ret)
+ goto OnError;
+
+ /* Write the page address to the page entry */
+ ret = dc_mmu_set_page(mmu, page_phy, page_entry);
+ if (ret)
+ goto OnError;
+
+ /* Get next page */
+ virutal_address += MMU_PAGE_4K_SIZE;
+ }
+
+ wmb();
+
+ return 0;
+
+OnError:
+ if (allocated)
+ dc_mmu_free_pages(mmu, virutal_address, page_count);
+ pr_info("%s fail!\n", __FUNCTION__);
+
+ return ret;
+}
+
+int dc_mmu_unmap_memory(dc_mmu_pt mmu, u32 gpu_address, u32 page_count)
+{
+ return dc_mmu_free_pages(mmu, gpu_address, page_count);
+}
diff --git a/drivers/gpu/drm/eswin/es_dc_mmu.h b/drivers/gpu/drm/eswin/es_dc_mmu.h
new file mode 100644
index 000000000000..ee049e3f40ec
--- /dev/null
+++ b/drivers/gpu/drm/eswin/es_dc_mmu.h
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Eswin Holdings Co., Ltd.
+ */
+
+#ifndef _ES_DC_MMU_H_
+#define _ES_DC_MMU_H_
+
+#include "es_type.h"
+
+#define DC_INFINITE ((u32)(~0U))
+
+#define DC_ENTRY_TYPE(x) (x & 0xF0)
+#define DC_SINGLE_PAGE_NODE_INITIALIZE (~((1U << 8) - 1))
+
+#define DC_INVALID_PHYSICAL_ADDRESS ~0ULL
+#define DC_INVALID_ADDRESS ~0U
+
+/* 1k mode */
+#define MMU_MTLB_SHIFT 24
+#define MMU_STLB_4K_SHIFT 12
+
+#define MMU_MTLB_BITS (32 - MMU_MTLB_SHIFT)
+#define MMU_PAGE_4K_BITS MMU_STLB_4K_SHIFT
+#define MMU_STLB_4K_BITS (32 - MMU_MTLB_BITS - MMU_PAGE_4K_BITS)
+
+#define MMU_MTLB_ENTRY_NUM (1 << MMU_MTLB_BITS)
+#define MMU_MTLB_SIZE (MMU_MTLB_ENTRY_NUM << 2)
+#define MMU_STLB_4K_ENTRY_NUM (1 << MMU_STLB_4K_BITS)
+#define MMU_STLB_4K_SIZE (MMU_STLB_4K_ENTRY_NUM << 2)
+#define MMU_PAGE_4K_SIZE (1 << MMU_STLB_4K_SHIFT)
+
+#define MMU_MTLB_MASK (~((1U << MMU_MTLB_SHIFT) - 1))
+#define MMU_STLB_4K_MASK ((~0U << MMU_STLB_4K_SHIFT) ^ MMU_MTLB_MASK)
+#define MMU_PAGE_4K_MASK (MMU_PAGE_4K_SIZE - 1)
+
+/* page offset definitions. */
+#define MMU_OFFSET_4K_BITS (32 - MMU_MTLB_BITS - MMU_STLB_4K_BITS)
+#define MMU_OFFSET_4K_MASK ((1U << MMU_OFFSET_4K_BITS) - 1)
+
+#define MMU_MTLB_PRESENT 0x00000001
+#define MMU_MTLB_EXCEPTION 0x00000002
+#define MMU_MTLB_4K_PAGE 0x00000000
+
+typedef enum _dc_mmu_type {
+ DC_MMU_USED = (0 << 4),
+ DC_MMU_SINGLE = (1 << 4),
+ DC_MMU_FREE = (2 << 4),
+} dc_mmu_type;
+
+typedef enum _dc_mmu_mode {
+ MMU_MODE_1K,
+ MMU_MODE_4K,
+} dc_mmu_mode;
+
+typedef struct _dc_mmu_stlb {
+ u32 *logical;
+ void *physical;
+ u32 size;
+ u64 physBase;
+ u32 pageCount;
+} dc_mmu_stlb, *dc_mmu_stlb_pt;
+
+typedef struct _dc_mmu {
+ u32 mtlb_bytes;
+ u64 mtlb_physical;
+ u32 *mtlb_logical;
+
+ void *safe_page_logical;
+ u64 safe_page_physical;
+
+ u32 dynamic_mapping_start;
+
+ void *stlbs;
+
+ // u64 stlb_physicals[MMU_MTLB_ENTRY_NUM];
+ u32 stlb_bytes;
+ u64 stlb_physical;
+
+ u32 page_table_entries;
+ u32 page_table_size;
+ u32 heap_list;
+
+ u32 *map_logical;
+ u32 *page_entry_info;
+ bool free_nodes;
+
+ void *page_table_mutex;
+
+ dc_mmu_mode mode;
+
+ void *static_stlb;
+} dc_mmu, *dc_mmu_pt;
+
+int dc_mmu_construct(struct device *dev, dc_mmu_pt *mmu);
+int dc_mmu_map_memory(dc_mmu_pt mmu, u64 physical, u32 page_count, u32 *address,
+ bool continuous);
+int dc_mmu_unmap_memory(dc_mmu_pt mmu, u32 gpu_address, u32 page_count);
+
+#endif /* _ES_DC_MMU_H_ */
diff --git a/drivers/gpu/drm/eswin/es_drm.h b/drivers/gpu/drm/eswin/es_drm.h
new file mode 100644
index 000000000000..73817dcf024b
--- /dev/null
+++ b/drivers/gpu/drm/eswin/es_drm.h
@@ -0,0 +1,23 @@
+#ifndef __ES_DRM_H__
+#define __ES_DRM_H__
+
+#include <drm/drm.h>
+
+enum drm_es_degamma_mode {
+ ES_DEGAMMA_DISABLE = 0,
+ ES_DEGAMMA_BT709 = 1,
+ ES_DEGAMMA_BT2020 = 2,
+};
+
+enum drm_es_sync_dc_mode {
+ ES_SINGLE_DC = 0,
+ ES_MULTI_DC_PRIMARY = 1,
+ ES_MULTI_DC_SECONDARY = 2,
+};
+
+enum drm_es_mmu_prefetch_mode {
+ ES_MMU_PREFETCH_DISABLE = 0,
+ ES_MMU_PREFETCH_ENABLE = 1,
+};
+
+#endif /* __ES_DRM_H__ */
\ No newline at end of file
diff --git a/drivers/gpu/drm/eswin/es_drv.c b/drivers/gpu/drm/eswin/es_drv.c
new file mode 100644
index 000000000000..1d15deda7c7b
--- /dev/null
+++ b/drivers/gpu/drm/eswin/es_drv.c
@@ -0,0 +1,517 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Eswin Holdings Co., Ltd.
+ */
+
+#include <linux/of_graph.h>
+#include <linux/component.h>
+#include <linux/iommu.h>
+#include <linux/version.h>
+#include <linux/of_address.h>
+#include <linux/dma-map-ops.h>
+
+#include <drm/drm_drv.h>
+#include <drm/drm_file.h>
+#include <drm/drm_ioctl.h>
+#include <drm/drm_of.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_debugfs.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_atomic_helper.h>
+
+#include "es_drv.h"
+#include "es_fb.h"
+#include "es_gem.h"
+#include "es_plane.h"
+#include "es_crtc.h"
+#include "es_simple_enc.h"
+#include "es_dc.h"
+#include "es_virtual.h"
+#ifdef CONFIG_ESWIN_DW_HDMI
+#include "dw_hdmi.h"
+#endif
+
+#define DRV_NAME "es_drm"
+#define DRV_DESC "Eswin DRM driver"
+#define DRV_DATE "20191101"
+#define DRV_MAJOR 1
+#define DRV_MINOR 0
+
+static bool has_iommu = true;
+static struct platform_driver es_drm_platform_driver;
+
+static const struct file_operations fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+ .unlocked_ioctl = drm_ioctl,
+ .compat_ioctl = drm_compat_ioctl,
+ .poll = drm_poll,
+ .read = drm_read,
+ .mmap = es_gem_mmap,
+};
+
+#ifdef CONFIG_DEBUG_FS
+static int es_debugfs_planes_show(struct seq_file *s, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *)s->private;
+ struct drm_device *dev = node->minor->dev;
+ struct drm_plane *plane = NULL;
+
+ list_for_each_entry (plane, &dev->mode_config.plane_list, head) {
+ struct drm_plane_state *state = plane->state;
+ struct es_plane_state *plane_state = to_es_plane_state(state);
+
+ seq_printf(s, "plane[%u]: %s\n", plane->base.id, plane->name);
+ seq_printf(s, "\tcrtc = %s\n",
+ state->crtc ? state->crtc->name : "(null)");
+ seq_printf(s, "\tcrtc id = %u\n",
+ state->crtc ? state->crtc->base.id : 0);
+ seq_printf(s, "\tcrtc-pos = " DRM_RECT_FMT "\n",
+ DRM_RECT_ARG(&plane_state->status.dest));
+ seq_printf(s, "\tsrc-pos = " DRM_RECT_FP_FMT "\n",
+ DRM_RECT_FP_ARG(&plane_state->status.src));
+ seq_printf(s, "\tformat = %p4cc\n",
+ state->fb ? &state->fb->format->format : NULL);
+ seq_printf(s, "\trotation = 0x%x\n", state->rotation);
+ seq_printf(s, "\ttiling = %u\n", plane_state->status.tile_mode);
+
+ seq_puts(s, "\n");
+ }
+
+ return 0;
+}
+
+static struct drm_info_list es_debugfs_list[] = {
+ { "planes", es_debugfs_planes_show, 0, NULL },
+};
+
+static void es_debugfs_init(struct drm_minor *minor)
+{
+ drm_debugfs_create_files(es_debugfs_list, ARRAY_SIZE(es_debugfs_list),
+ minor->debugfs_root, minor);
+}
+#endif
+
+static struct drm_driver es_drm_driver = {
+ .driver_features =
+ DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM | DRIVER_SYNCOBJ,
+ .lastclose = drm_fb_helper_lastclose,
+ .gem_prime_import = es_gem_prime_import,
+ .gem_prime_import_sg_table = es_gem_prime_import_sg_table,
+ .dumb_create = es_gem_dumb_create,
+#ifdef CONFIG_DEBUG_FS
+ .debugfs_init = es_debugfs_init,
+#endif
+ .fops = &fops,
+ .name = DRV_NAME,
+ .desc = DRV_DESC,
+ .date = DRV_DATE,
+ .major = DRV_MAJOR,
+ .minor = DRV_MINOR,
+};
+
+int es_drm_iommu_attach_device(struct drm_device *drm_dev, struct device *dev)
+{
+ struct es_drm_private *priv = drm_dev->dev_private;
+ int ret;
+
+ if (!has_iommu)
+ return 0;
+
+ if (!priv->domain) {
+ priv->domain = iommu_get_domain_for_dev(dev);
+ if (IS_ERR(priv->domain))
+ return PTR_ERR(priv->domain);
+ priv->dma_dev = dev;
+ }
+
+ ret = iommu_attach_device(priv->domain, dev);
+ if (ret) {
+ DRM_DEV_ERROR(dev, "Failed to attach iommu device\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+void es_drm_iommu_detach_device(struct drm_device *drm_dev, struct device *dev)
+{
+ struct es_drm_private *priv = drm_dev->dev_private;
+
+ if (!has_iommu)
+ return;
+
+ iommu_detach_device(priv->domain, dev);
+
+ if (priv->dma_dev == dev)
+ priv->dma_dev = drm_dev->dev;
+}
+
+void es_drm_update_pitch_alignment(struct drm_device *drm_dev,
+ unsigned int alignment)
+{
+ struct es_drm_private *priv = drm_dev->dev_private;
+
+ if (alignment > priv->pitch_alignment)
+ priv->pitch_alignment = alignment;
+}
+
+#ifdef CONFIG_ESWIN_DW_HDMI
+static int es_drm_create_properties(struct drm_device *dev)
+{
+ struct drm_property *prop;
+ struct es_drm_private *private = dev->dev_private;
+
+ prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, "EOTF", 0,
+ 5);
+ if (!prop)
+ return -ENOMEM;
+ private->eotf_prop = prop;
+
+ prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+ "COLOR_SPACE", 0, 12);
+ if (!prop)
+ return -ENOMEM;
+ private->color_space_prop = prop;
+
+ prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+ "GLOBAL_ALPHA", 0, 255);
+ if (!prop)
+ return -ENOMEM;
+ private->global_alpha_prop = prop;
+
+ prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+ "BLEND_MODE", 0, 1);
+ if (!prop)
+ return -ENOMEM;
+ private->blend_mode_prop = prop;
+
+ prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+ "ALPHA_SCALE", 0, 1);
+ if (!prop)
+ return -ENOMEM;
+ private->alpha_scale_prop = prop;
+
+ prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+ "ASYNC_COMMIT", 0, 1);
+ if (!prop)
+ return -ENOMEM;
+ private->async_commit_prop = prop;
+
+ prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, "SHARE_ID",
+ 0, UINT_MAX);
+ if (!prop)
+ return -ENOMEM;
+ private->share_id_prop = prop;
+
+ prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+ "CONNECTOR_ID", 0, 0xf);
+ if (!prop)
+ return -ENOMEM;
+ private->connector_id_prop = prop;
+
+ return drm_mode_create_tv_properties(dev, 0, NULL);
+}
+#endif
+
+static int es_drm_bind(struct device *dev)
+{
+ struct drm_device *drm_dev;
+ struct es_drm_private *priv;
+ int ret;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+ static u64 dma_mask = DMA_BIT_MASK(40);
+#else
+ static u64 dma_mask = DMA_40BIT_MASK;
+#endif
+
+ drm_dev = drm_dev_alloc(&es_drm_driver, dev);
+ if (IS_ERR(drm_dev))
+ return PTR_ERR(drm_dev);
+
+ dev_set_drvdata(dev, drm_dev);
+
+ priv = devm_kzalloc(drm_dev->dev, sizeof(struct es_drm_private),
+ GFP_KERNEL);
+ if (!priv) {
+ ret = -ENOMEM;
+ goto err_put_dev;
+ }
+
+ priv->pitch_alignment = 64;
+ priv->dma_dev = drm_dev->dev;
+ priv->dma_dev->coherent_dma_mask = dma_mask;
+ dma_set_mask_and_coherent(priv->dma_dev, DMA_BIT_MASK(40));
+
+ drm_dev->dev_private = priv;
+
+ drm_mode_config_init(drm_dev);
+
+#ifdef CONFIG_ESWIN_DW_HDMI
+ es_drm_create_properties(drm_dev);
+#endif
+
+ /* Now try and bind all our sub-components */
+ ret = component_bind_all(dev, drm_dev);
+ if (ret)
+ goto err_mode;
+
+ es_mode_config_init(drm_dev);
+
+ ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
+ if (ret)
+ goto err_bind;
+
+ drm_mode_config_reset(drm_dev);
+
+ drm_kms_helper_poll_init(drm_dev);
+
+ ret = drm_dev_register(drm_dev, 0);
+ if (ret)
+ goto err_helper;
+
+ return 0;
+
+err_helper:
+ drm_kms_helper_poll_fini(drm_dev);
+err_bind:
+ component_unbind_all(drm_dev->dev, drm_dev);
+err_mode:
+ drm_mode_config_cleanup(drm_dev);
+ if (priv->domain)
+ iommu_domain_free(priv->domain);
+err_put_dev:
+ drm_dev->dev_private = NULL;
+ dev_set_drvdata(dev, NULL);
+ drm_dev_put(drm_dev);
+ return ret;
+}
+
+static void es_drm_unbind(struct device *dev)
+{
+ struct drm_device *drm_dev = dev_get_drvdata(dev);
+ struct es_drm_private *priv = drm_dev->dev_private;
+
+ drm_dev_unregister(drm_dev);
+ drm_kms_helper_poll_fini(drm_dev);
+ drm_atomic_helper_shutdown(drm_dev);
+ component_unbind_all(drm_dev->dev, drm_dev);
+ drm_mode_config_cleanup(drm_dev);
+
+ if (priv->domain) {
+ iommu_domain_free(priv->domain);
+ priv->domain = NULL;
+ }
+
+ drm_dev->dev_private = NULL;
+ dev_set_drvdata(dev, NULL);
+ drm_dev_put(drm_dev);
+}
+
+static const struct component_master_ops es_drm_ops = {
+ .bind = es_drm_bind,
+ .unbind = es_drm_unbind,
+};
+
+static struct platform_driver *drm_sub_drivers[] = {
+ /* put display control driver at start */
+ &dc_platform_driver,
+
+/* connector */
+
+/* bridge */
+#if 1
+#ifdef CONFIG_ESWIN_DW_HDMI
+ &dw_hdmi_eswin_pltfm_driver,
+#endif
+#ifdef CONFIG_DW_HDMI_I2S_AUDIO
+ &snd_dw_hdmi_driver,
+#endif
+
+#ifdef CONFIG_DW_HDMI_CEC
+ &dw_hdmi_cec_driver,
+#endif
+
+#ifdef CONFIG_DW_HDMI_HDCP
+ &dw_hdmi_hdcp_driver,
+#endif
+#endif
+
+#ifdef CONFIG_ESWIN_VIRTUAL_DISPLAY
+ &virtual_display_platform_driver,
+#endif
+};
+#define NUM_DRM_DRIVERS \
+ (sizeof(drm_sub_drivers) / sizeof(struct platform_driver *))
+
+static int compare_dev(struct device *dev, void *data)
+{
+ return dev == (struct device *)data;
+}
+
+static struct component_match *es_drm_match_add(struct device *dev)
+{
+ struct component_match *match = NULL;
+ int i;
+
+ for (i = 0; i < NUM_DRM_DRIVERS; ++i) {
+ struct platform_driver *drv = drm_sub_drivers[i];
+ struct device *p = NULL, *d;
+
+ while ((d = platform_find_device_by_driver(p, &drv->driver))) {
+ put_device(p);
+
+ component_match_add(dev, &match, compare_dev, d);
+ p = d;
+ }
+ put_device(p);
+ }
+
+ return match ?: ERR_PTR(-ENODEV);
+}
+
+static int es_drm_platform_of_probe(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct device_node *port;
+ bool found = false;
+ int i;
+
+ if (!np)
+ return -ENODEV;
+
+ for (i = 0;; i++) {
+ struct device_node *iommu;
+
+ port = of_parse_phandle(np, "ports", i);
+ if (!port)
+ break;
+
+ if (!of_device_is_available(port->parent)) {
+ of_node_put(port);
+ continue;
+ }
+
+ iommu = of_parse_phandle(port->parent, "iommus", 0);
+
+ /*
+ * if there is a crtc not support iommu, force set all
+ * crtc use non-iommu buffer.
+ */
+ if (!iommu || !of_device_is_available(iommu->parent))
+ has_iommu = false;
+
+ found = true;
+
+ of_node_put(iommu);
+ of_node_put(port);
+ }
+
+ if (i == 0) {
+ DRM_DEV_ERROR(dev, "missing 'ports' property\n");
+ return -ENODEV;
+ }
+
+ if (!found) {
+ DRM_DEV_ERROR(dev, "No available DC found.\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int es_drm_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct component_match *match;
+ int ret;
+
+ ret = es_drm_platform_of_probe(dev);
+ if (ret)
+ return ret;
+
+ match = es_drm_match_add(dev);
+ if (IS_ERR(match))
+ return PTR_ERR(match);
+
+ return component_master_add_with_match(dev, &es_drm_ops, match);
+}
+
+static int es_drm_platform_remove(struct platform_device *pdev)
+{
+ component_master_del(&pdev->dev, &es_drm_ops);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int es_drm_suspend(struct device *dev)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+
+ return drm_mode_config_helper_suspend(drm);
+}
+
+static int es_drm_resume(struct device *dev)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+
+ return drm_mode_config_helper_resume(drm);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(es_drm_pm_ops, es_drm_suspend, es_drm_resume);
+
+static const struct of_device_id es_drm_dt_ids[] = {
+
+ {
+ .compatible = "eswin,display-subsystem",
+ },
+
+ { /* sentinel */ },
+
+};
+
+MODULE_DEVICE_TABLE(of, es_drm_dt_ids);
+
+static struct platform_driver es_drm_platform_driver = {
+ .probe = es_drm_platform_probe,
+ .remove = es_drm_platform_remove,
+
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = es_drm_dt_ids,
+ .pm = &es_drm_pm_ops,
+ },
+};
+
+static int __init es_drm_init(void)
+{
+ int ret;
+
+ ret = platform_register_drivers(drm_sub_drivers, NUM_DRM_DRIVERS);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&es_drm_platform_driver);
+ if (ret)
+ platform_unregister_drivers(drm_sub_drivers, NUM_DRM_DRIVERS);
+
+ return ret;
+}
+
+static void __exit es_drm_fini(void)
+{
+ platform_driver_unregister(&es_drm_platform_driver);
+ platform_unregister_drivers(drm_sub_drivers, NUM_DRM_DRIVERS);
+}
+
+module_init(es_drm_init);
+module_exit(es_drm_fini);
+
+MODULE_DESCRIPTION("Eswin DRM Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/eswin/es_drv.h b/drivers/gpu/drm/eswin/es_drv.h
new file mode 100644
index 000000000000..85465f559de0
--- /dev/null
+++ b/drivers/gpu/drm/eswin/es_drv.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Eswin Holdings Co., Ltd.
+ */
+
+#ifndef __ES_DRV_H__
+#define __ES_DRV_H__
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <drm/drm_gem.h>
+#include <drm/drm_device.h>
+
+#include "es_plane.h"
+#ifdef CONFIG_ESWIN_MMU
+#include "es_dc_mmu.h"
+#endif
+
+/*
+ *
+ * @dma_dev: device for DMA API.
+ * - use the first attached device if support iommu
+ else use drm device (only contiguous buffer support)
+ * @domain: iommu domain for DRM.
+ * - all DC IOMMU share same domain to reduce mapping
+ * @pitch_alignment: buffer pitch alignment required by sub-devices.
+ *
+ */
+struct es_drm_private {
+ struct device *dma_dev;
+ struct iommu_domain *domain;
+#ifdef CONFIG_ESWIN_DW_HDMI
+ struct drm_property *connector_id_prop;
+ struct drm_property *eotf_prop;
+ struct drm_property *color_space_prop;
+ struct drm_property *global_alpha_prop;
+ struct drm_property *blend_mode_prop;
+ struct drm_property *alpha_scale_prop;
+ struct drm_property *async_commit_prop;
+ struct drm_property *share_id_prop;
+#endif
+
+#ifdef CONFIG_ESWIN_MMU
+ dc_mmu *mmu;
+#endif
+
+ unsigned int pitch_alignment;
+};
+
+int es_drm_iommu_attach_device(struct drm_device *drm_dev, struct device *dev);
+
+void es_drm_iommu_detach_device(struct drm_device *drm_dev, struct device *dev);
+
+void es_drm_update_pitch_alignment(struct drm_device *drm_dev,
+ unsigned int alignment);
+
+static inline struct device *to_dma_dev(struct drm_device *dev)
+{
+ struct es_drm_private *priv = dev->dev_private;
+
+ return priv->dma_dev;
+}
+
+static inline bool is_iommu_enabled(struct drm_device *dev)
+{
+ struct es_drm_private *priv = dev->dev_private;
+
+ return priv->domain != NULL ? true : false;
+}
+#endif /* __ES_DRV_H__ */
diff --git a/drivers/gpu/drm/eswin/es_fb.c b/drivers/gpu/drm/eswin/es_fb.c
new file mode 100644
index 000000000000..9254b784003d
--- /dev/null
+++ b/drivers/gpu/drm/eswin/es_fb.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Eswin Holdings Co., Ltd.
+ */
+
+#include <linux/module.h>
+
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_damage_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+
+#include "es_fb.h"
+#include "es_gem.h"
+
+static struct drm_framebuffer_funcs es_fb_funcs = {
+ .create_handle = drm_gem_fb_create_handle,
+ .destroy = drm_gem_fb_destroy,
+ .dirty = drm_atomic_helper_dirtyfb,
+};
+
+static struct drm_framebuffer *
+es_fb_alloc(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cmd,
+ struct es_gem_object **obj, unsigned int num_planes)
+{
+ struct drm_framebuffer *fb;
+ int ret, i;
+
+ fb = kzalloc(sizeof(*fb), GFP_KERNEL);
+ if (!fb)
+ return ERR_PTR(-ENOMEM);
+
+ drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);
+
+ for (i = 0; i < num_planes; i++)
+ fb->obj[i] = &obj[i]->base;
+
+ ret = drm_framebuffer_init(dev, fb, &es_fb_funcs);
+ if (ret) {
+ dev_err(dev->dev, "Failed to initialize framebuffer: %d\n",
+ ret);
+ kfree(fb);
+ return ERR_PTR(ret);
+ }
+
+ return fb;
+}
+
+static struct drm_framebuffer *
+es_fb_create(struct drm_device *dev, struct drm_file *file_priv,
+ const struct drm_mode_fb_cmd2 *mode_cmd)
+{
+ struct drm_framebuffer *fb;
+ const struct drm_format_info *info;
+ struct es_gem_object *objs[MAX_NUM_PLANES];
+ struct drm_gem_object *obj;
+ unsigned int height, size;
+ unsigned char i, num_planes;
+ int ret = 0;
+
+ info = drm_format_info(mode_cmd->pixel_format);
+ if (!info)
+ return ERR_PTR(-EINVAL);
+
+ num_planes = info->num_planes;
+ if (num_planes > MAX_NUM_PLANES)
+ return ERR_PTR(-EINVAL);
+
+ for (i = 0; i < num_planes; i++) {
+ obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[i]);
+ if (!obj) {
+ dev_err(dev->dev, "Failed to lookup GEM object.\n");
+ ret = -ENXIO;
+ goto err;
+ }
+
+ height =
+ drm_format_info_plane_height(info, mode_cmd->height, i);
+
+ size = height * mode_cmd->pitches[i] + mode_cmd->offsets[i];
+
+ if (obj->size < size) {
+ drm_gem_object_put(obj);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ objs[i] = to_es_gem_object(obj);
+ }
+
+ fb = es_fb_alloc(dev, mode_cmd, objs, i);
+ if (IS_ERR(fb)) {
+ ret = PTR_ERR(fb);
+ goto err;
+ }
+
+ return fb;
+
+err:
+ for (; i > 0; i--) {
+ drm_gem_object_put(&objs[i - 1]->base);
+ }
+
+ return ERR_PTR(ret);
+}
+
+struct es_gem_object *es_fb_get_gem_obj(struct drm_framebuffer *fb,
+ unsigned char plane)
+{
+ if (plane > MAX_NUM_PLANES)
+ return NULL;
+
+ return to_es_gem_object(fb->obj[plane]);
+}
+
+static const struct drm_mode_config_funcs es_mode_config_funcs = {
+ .fb_create = es_fb_create,
+ .output_poll_changed = drm_fb_helper_output_poll_changed,
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
+};
+
+static struct drm_mode_config_helper_funcs es_mode_config_helpers = {
+ .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
+};
+
+void es_mode_config_init(struct drm_device *dev)
+{
+ if (dev->mode_config.max_width == 0 ||
+ dev->mode_config.max_height == 0) {
+ dev->mode_config.min_width = 0;
+ dev->mode_config.min_height = 0;
+ dev->mode_config.max_width = 4096;
+ dev->mode_config.max_height = 4096;
+ }
+ dev->mode_config.funcs = &es_mode_config_funcs;
+ dev->mode_config.helper_private = &es_mode_config_helpers;
+}
diff --git a/drivers/gpu/drm/eswin/es_fb.h b/drivers/gpu/drm/eswin/es_fb.h
new file mode 100644
index 000000000000..6cd8b31684aa
--- /dev/null
+++ b/drivers/gpu/drm/eswin/es_fb.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Eswin Holdings Co., Ltd.
+ */
+
+#ifndef __ES_FB_H__
+#define __ES_FB_H__
+
+struct es_gem_object *es_fb_get_gem_obj(struct drm_framebuffer *fb,
+ unsigned char plane);
+
+void es_mode_config_init(struct drm_device *dev);
+#endif /* __ES_FB_H__ */
diff --git a/drivers/gpu/drm/eswin/es_gem.c b/drivers/gpu/drm/eswin/es_gem.c
new file mode 100644
index 000000000000..4898c86d5d87
--- /dev/null
+++ b/drivers/gpu/drm/eswin/es_gem.c
@@ -0,0 +1,598 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Eswin Holdings Co., Ltd.
+ */
+
+#include <linux/dma-buf.h>
+#include <drm/drm_prime.h>
+
+#include "es_drv.h"
+#include "es_gem.h"
+
+MODULE_IMPORT_NS(DMA_BUF);
+
+static const struct drm_gem_object_funcs es_gem_default_funcs;
+
+static void nonseq_free(struct page **pages, unsigned int nr_page)
+{
+ u32 i;
+
+ if (!pages)
+ return;
+
+ for (i = 0; i < nr_page; i++)
+ __free_page(pages[i]);
+}
+
+#ifdef CONFIG_ESWIN_MMU
+static int get_pages(unsigned int nr_page, struct es_gem_object *es_obj)
+{
+ struct page *pages;
+ u32 i, num_page, page_count = 0;
+ int order = 0;
+ gfp_t gfp = GFP_KERNEL;
+
+ if (!es_obj->pages)
+ return -EINVAL;
+
+ gfp &= ~__GFP_HIGHMEM;
+ gfp |= __GFP_DMA32;
+
+ num_page = nr_page;
+
+ do {
+ pages = NULL;
+ order = get_order(num_page * PAGE_SIZE);
+ num_page = 1 << order;
+
+ if ((num_page + page_count > nr_page) || (order >= MAX_ORDER)) {
+ num_page = num_page >> 1;
+ continue;
+ }
+
+ pages = alloc_pages(gfp, order);
+ if (!pages) {
+ if (num_page == 1) {
+ nonseq_free(es_obj->pages, page_count);
+ return -ENOMEM;
+ }
+
+ num_page = num_page >> 1;
+ } else {
+ for (i = 0; i < num_page; i++) {
+ es_obj->pages[page_count + i] = &pages[i];
+ SetPageReserved(es_obj->pages[page_count + i]);
+ }
+
+ page_count += num_page;
+ num_page = nr_page - page_count;
+ }
+
+ } while (page_count < nr_page);
+
+ es_obj->get_pages = true;
+
+ return 0;
+}
+#endif
+
+static void put_pages(unsigned int nr_page, struct es_gem_object *es_obj)
+{
+ u32 i;
+
+ for (i = 0; i < nr_page; i++)
+ ClearPageReserved(es_obj->pages[i]);
+
+ nonseq_free(es_obj->pages, nr_page);
+
+ return;
+}
+
+static int es_gem_alloc_buf(struct es_gem_object *es_obj)
+{
+ struct drm_device *dev = es_obj->base.dev;
+ unsigned int nr_pages;
+ struct sg_table sgt;
+ int ret = -ENOMEM;
+#ifdef CONFIG_ESWIN_MMU
+ struct es_drm_private *priv = dev->dev_private;
+#endif
+
+ if (es_obj->dma_addr) {
+ DRM_DEV_DEBUG_KMS(dev->dev, "already allocated.\n");
+ return 0;
+ }
+
+ es_obj->dma_attrs = DMA_ATTR_WRITE_COMBINE | DMA_ATTR_NO_KERNEL_MAPPING;
+
+ if (!is_iommu_enabled(dev))
+ es_obj->dma_attrs |= DMA_ATTR_FORCE_CONTIGUOUS;
+
+ nr_pages = es_obj->size >> PAGE_SHIFT;
+
+ es_obj->pages = kvmalloc_array(nr_pages, sizeof(struct page *),
+ GFP_KERNEL | __GFP_ZERO);
+ if (!es_obj->pages) {
+ DRM_DEV_ERROR(dev->dev, "failed to allocate pages.\n");
+ return -ENOMEM;
+ }
+
+ es_obj->cookie = dma_alloc_attrs(to_dma_dev(dev), es_obj->size,
+ &es_obj->dma_addr, GFP_KERNEL,
+ es_obj->dma_attrs);
+ if (!es_obj->cookie) {
+#ifdef CONFIG_ESWIN_MMU
+ ret = get_pages(nr_pages, es_obj);
+ if (ret) {
+ DRM_DEV_ERROR(dev->dev, "fail to allocate buffer.\n");
+ goto err_free;
+ }
+#else
+ DRM_DEV_ERROR(dev->dev, "failed to allocate buffer.\n");
+ goto err_free;
+#endif
+ }
+
+#ifdef CONFIG_ESWIN_MMU
+ /* MMU map*/
+ if (!priv->mmu) {
+ DRM_DEV_ERROR(dev->dev, "invalid mmu.\n");
+ ret = -EINVAL;
+ goto err_mem_free;
+ }
+
+ if (!es_obj->get_pages)
+ ret = dc_mmu_map_memory(priv->mmu, (u64)es_obj->dma_addr,
+ nr_pages, &es_obj->iova, true);
+ else
+ ret = dc_mmu_map_memory(priv->mmu, (u64)es_obj->pages, nr_pages,
+ &es_obj->iova, false);
+
+ if (ret) {
+ DRM_DEV_ERROR(dev->dev, "failed to do mmu map.\n");
+ goto err_mem_free;
+ }
+#else
+ es_obj->iova = es_obj->dma_addr;
+#endif
+
+ if (!es_obj->get_pages) {
+ ret = dma_get_sgtable_attrs(to_dma_dev(dev), &sgt,
+ es_obj->cookie, es_obj->dma_addr,
+ es_obj->size, es_obj->dma_attrs);
+ if (ret < 0) {
+ DRM_DEV_ERROR(dev->dev, "failed to get sgtable.\n");
+ goto err_mem_free;
+ }
+
+ if (drm_prime_sg_to_page_array(&sgt, es_obj->pages, nr_pages)) {
+ DRM_DEV_ERROR(dev->dev, "invalid sgtable.\n");
+ ret = -EINVAL;
+ goto err_sgt_free;
+ }
+
+ sg_free_table(&sgt);
+ }
+
+ return 0;
+
+err_sgt_free:
+ sg_free_table(&sgt);
+err_mem_free:
+ if (!es_obj->get_pages)
+ dma_free_attrs(to_dma_dev(dev), es_obj->size, es_obj->cookie,
+ es_obj->dma_addr, es_obj->dma_attrs);
+ else
+ put_pages(nr_pages, es_obj);
+err_free:
+ kvfree(es_obj->pages);
+
+ return ret;
+}
+
+static void es_gem_free_buf(struct es_gem_object *es_obj)
+{
+ struct drm_device *dev = es_obj->base.dev;
+#ifdef CONFIG_ESWIN_MMU
+ struct es_drm_private *priv = dev->dev_private;
+ unsigned int nr_pages;
+#endif
+
+ if ((!es_obj->get_pages) && (!es_obj->dma_addr)) {
+ DRM_DEV_DEBUG_KMS(dev->dev, "dma_addr is invalid.\n");
+ return;
+ }
+
+#ifdef CONFIG_ESWIN_MMU
+ if (!priv->mmu) {
+ DRM_DEV_ERROR(dev->dev, "invalid mmu.\n");
+ return;
+ }
+
+ if (!es_obj->sgt) { // dumb buffer release
+ nr_pages = es_obj->size >> PAGE_SHIFT;
+ if (es_obj->iova) {
+ dc_mmu_unmap_memory(priv->mmu, es_obj->iova, nr_pages);
+ }
+ } else { // prime buffer release
+ if (es_obj->iova_list) {
+ if (es_obj->iova_list->iova) {
+ dc_mmu_unmap_memory(
+ priv->mmu, es_obj->iova_list->iova,
+ es_obj->iova_list->nr_pages);
+ kfree(es_obj->iova_list);
+ }
+ }
+ }
+#endif
+
+ if (!es_obj->get_pages) {
+ dma_free_attrs(to_dma_dev(dev), es_obj->size, es_obj->cookie,
+ (dma_addr_t)es_obj->dma_addr, es_obj->dma_attrs);
+ } else {
+ if (!es_obj->dma_addr) {
+ DRM_DEV_ERROR(dev->dev, "No dma addr allocated, no need to free\n");
+ return;
+ }
+ put_pages(es_obj->size >> PAGE_SHIFT, es_obj);
+ }
+
+ kvfree(es_obj->pages);
+}
+
+static void es_gem_free_object(struct drm_gem_object *obj)
+{
+ struct es_gem_object *es_obj = to_es_gem_object(obj);
+
+#ifdef CONFIG_ESWIN_MMU
+ if (es_obj)
+ es_gem_free_buf(es_obj);
+#endif
+ if (obj->import_attach) {
+ drm_prime_gem_destroy(obj, es_obj->sgt);
+ }
+
+ drm_gem_object_release(obj);
+
+ kfree(es_obj);
+}
+
+static struct es_gem_object *es_gem_alloc_object(struct drm_device *dev,
+ size_t size)
+{
+ struct es_gem_object *es_obj;
+ struct drm_gem_object *obj;
+ int ret;
+
+ es_obj = kzalloc(sizeof(*es_obj), GFP_KERNEL);
+ if (!es_obj)
+ return ERR_PTR(-ENOMEM);
+
+ es_obj->size = size;
+ obj = &es_obj->base;
+
+ ret = drm_gem_object_init(dev, obj, size);
+ if (ret)
+ goto err_free;
+
+ es_obj->base.funcs = &es_gem_default_funcs;
+
+ ret = drm_gem_create_mmap_offset(obj);
+ if (ret) {
+ drm_gem_object_release(obj);
+ goto err_free;
+ }
+
+ return es_obj;
+
+err_free:
+ kfree(es_obj);
+ return ERR_PTR(ret);
+}
+
+struct es_gem_object *es_gem_create_object(struct drm_device *dev, size_t size)
+{
+ struct es_gem_object *es_obj;
+ int ret;
+
+ size = PAGE_ALIGN(size);
+
+ es_obj = es_gem_alloc_object(dev, size);
+ if (IS_ERR(es_obj))
+ return es_obj;
+
+ ret = es_gem_alloc_buf(es_obj);
+ if (ret) {
+ drm_gem_object_release(&es_obj->base);
+ kfree(es_obj);
+ return ERR_PTR(ret);
+ }
+
+ return es_obj;
+}
+
+static struct es_gem_object *es_gem_create_with_handle(struct drm_device *dev,
+ struct drm_file *file,
+ size_t size,
+ unsigned int *handle)
+{
+ struct es_gem_object *es_obj;
+ struct drm_gem_object *obj;
+ int ret;
+
+ es_obj = es_gem_create_object(dev, size);
+ if (IS_ERR(es_obj))
+ return es_obj;
+
+ obj = &es_obj->base;
+
+ ret = drm_gem_handle_create(file, obj, handle);
+ drm_gem_object_put(obj);
+ if (ret) {
+ pr_err("Drm GEM handle create failed\n");
+ return ERR_PTR(ret);
+ }
+
+ return es_obj;
+}
+
+static int es_gem_mmap_obj(struct drm_gem_object *obj,
+ struct vm_area_struct *vma)
+{
+ struct es_gem_object *es_obj = to_es_gem_object(obj);
+ struct drm_device *drm_dev = es_obj->base.dev;
+ unsigned long vm_size;
+ int ret = 0;
+
+ vm_size = vma->vm_end - vma->vm_start;
+ if (vm_size > es_obj->size)
+ return -EINVAL;
+
+ vma->vm_pgoff = 0;
+
+ if (!es_obj->get_pages) {
+ vm_flags_clear(vma, VM_PFNMAP);
+
+ ret = dma_mmap_attrs(to_dma_dev(drm_dev), vma, es_obj->cookie,
+ es_obj->dma_addr, es_obj->size,
+ es_obj->dma_attrs);
+ } else {
+ u32 i, nr_pages, pfn = 0U;
+ unsigned long start;
+
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+ vm_flags_set(vma, VM_IO | VM_DONTCOPY | VM_DONTEXPAND | VM_DONTDUMP);
+ start = vma->vm_start;
+ vm_size = PAGE_ALIGN(vm_size);
+ nr_pages = vm_size >> PAGE_SHIFT;
+
+ for (i = 0; i < nr_pages; i++) {
+ pfn = page_to_pfn(es_obj->pages[i]);
+
+ ret = remap_pfn_range(vma, start, pfn, PAGE_SIZE,
+ vma->vm_page_prot);
+ if (ret < 0)
+ break;
+
+ start += PAGE_SIZE;
+ }
+ }
+
+ if (ret)
+ drm_gem_vm_close(vma);
+
+ return ret;
+}
+
+struct sg_table *es_gem_prime_get_sg_table(struct drm_gem_object *obj)
+{
+ struct es_gem_object *es_obj = to_es_gem_object(obj);
+
+ return drm_prime_pages_to_sg(obj->dev, es_obj->pages,
+ es_obj->size >> PAGE_SHIFT);
+}
+
+static int es_gem_prime_vmap(struct drm_gem_object *obj,
+ struct iosys_map *map)
+{
+ return 0;
+}
+
+static void es_gem_prime_vunmap(struct drm_gem_object *obj,
+ struct iosys_map *map)
+{
+ /* Nothing to do */
+}
+
+static const struct vm_operations_struct es_vm_ops = {
+ .open = drm_gem_vm_open,
+ .close = drm_gem_vm_close,
+};
+
+static const struct drm_gem_object_funcs es_gem_default_funcs = {
+ .free = es_gem_free_object,
+ .get_sg_table = es_gem_prime_get_sg_table,
+ .vmap = es_gem_prime_vmap,
+ .vunmap = es_gem_prime_vunmap,
+ .vm_ops = &es_vm_ops,
+};
+
+int es_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
+ struct drm_mode_create_dumb *args)
+{
+ struct es_drm_private *priv = dev->dev_private;
+ struct es_gem_object *es_obj;
+ unsigned int pitch = args->width * DIV_ROUND_UP(args->bpp, 8);
+
+ args->pitch = ALIGN(pitch, priv->pitch_alignment);
+ args->size = PAGE_ALIGN(args->pitch * args->height);
+
+ es_obj =
+ es_gem_create_with_handle(dev, file, args->size, &args->handle);
+ return PTR_ERR_OR_ZERO(es_obj);
+}
+
+struct drm_gem_object *es_gem_prime_import(struct drm_device *dev,
+ struct dma_buf *dma_buf)
+{
+ return drm_gem_prime_import_dev(dev, dma_buf, to_dma_dev(dev));
+}
+
+struct drm_gem_object *
+es_gem_prime_import_sg_table(struct drm_device *dev,
+ struct dma_buf_attachment *attach,
+ struct sg_table *sgt)
+{
+ struct es_gem_object *es_obj;
+ int npages;
+ int ret;
+ struct scatterlist *s = NULL;
+ u32 i = 0;
+ dma_addr_t expected;
+ size_t size = attach->dmabuf->size;
+#ifdef CONFIG_ESWIN_MMU
+ u32 iova, j;
+ struct scatterlist **splist;
+ struct es_drm_private *priv = dev->dev_private;
+
+ if (!priv->mmu) {
+ DRM_ERROR("invalid mmu.\n");
+ ret = -EINVAL;
+ return ERR_PTR(ret);
+ }
+#endif
+
+ size = PAGE_ALIGN(size);
+
+ es_obj = es_gem_alloc_object(dev, size);
+ if (IS_ERR(es_obj))
+ return ERR_CAST(es_obj);
+
+ npages = es_obj->size >> PAGE_SHIFT;
+ es_obj->pages =
+ kvmalloc_array(npages, sizeof(struct page *), GFP_KERNEL);
+ if (!es_obj->pages) {
+ ret = -ENOMEM;
+ goto err_gemalloc;
+ }
+
+ ret = drm_prime_sg_to_page_array(sgt, es_obj->pages, npages);
+ if (ret)
+ goto err_free_page;
+
+ expected = sg_dma_address(sgt->sgl);
+#ifdef CONFIG_ESWIN_MMU
+ splist = (struct scatterlist **)kzalloc(sizeof(s) * sgt->nents,
+ GFP_KERNEL);
+ if (!splist) {
+ DRM_ERROR("Allocate splist failed");
+ ret = -ENOMEM;
+ goto err_free_page;
+ }
+
+ es_obj->iova_list =
+ (iova_info_t *)kzalloc(sizeof(iova_info_t), GFP_KERNEL);
+ if (!es_obj->iova_list) {
+ DRM_ERROR("Allocate splist failed");
+ ret = -ENOMEM;
+ goto err_sp;
+ }
+
+ for_each_sg (sgt->sgl, s, sgt->nents, i) {
+ splist[i] = s;
+ }
+ i = 0;
+ es_obj->nr_iova = sgt->nents;
+
+ for (j = sgt->nents; j > 0; j--) {
+ s = splist[j - 1];
+#else
+ for_each_sg (sgt->sgl, s, sgt->nents, i) {
+#endif
+ if (sg_dma_address(s) != expected) {
+#ifndef CONFIG_ESWIN_MMU
+ DRM_ERROR("sg_table is not contiguous");
+ ret = -EINVAL;
+ goto err;
+#endif
+ }
+
+ if (sg_dma_len(s) & (PAGE_SIZE - 1)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+#ifdef CONFIG_ESWIN_MMU
+ iova = 0;
+
+ if (j == 1) {
+ ret = dc_mmu_map_memory(priv->mmu, (u64)es_obj->pages,
+ npages, &iova, false);
+ if (ret) {
+ DRM_ERROR("failed to do mmu map.\n");
+ goto err;
+ }
+ es_obj->iova_list->iova = iova;
+ es_obj->iova_list->nr_pages = npages;
+ }
+
+ if (i == 0)
+ es_obj->iova = iova;
+#else
+ if (i == 0)
+ es_obj->iova = sg_dma_address(s);
+#endif
+
+ expected = sg_dma_address(s) + sg_dma_len(s);
+ }
+
+ es_obj->dma_addr = sg_dma_address(sgt->sgl);
+
+ es_obj->sgt = sgt;
+#ifdef CONFIG_ESWIN_MMU
+ kfree(splist);
+#endif
+
+ return &es_obj->base;
+
+#ifdef CONFIG_ESWIN_MMU
+err:
+ kfree(es_obj->iova_list);
+err_sp:
+ kfree(splist);
+#endif
+err_free_page:
+ kvfree(es_obj->pages);
+err_gemalloc:
+ es_gem_free_object(&es_obj->base);
+
+ return ERR_PTR(ret);
+}
+
+int es_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
+{
+ int ret = 0;
+
+ ret = drm_gem_mmap_obj(obj, obj->size, vma);
+ if (ret < 0)
+ return ret;
+
+ return es_gem_mmap_obj(obj, vma);
+}
+
+int es_gem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct drm_gem_object *obj;
+ int ret;
+
+ ret = drm_gem_mmap(filp, vma);
+ if (ret)
+ return ret;
+
+ obj = vma->vm_private_data;
+
+ if (obj->import_attach)
+ return dma_buf_mmap(obj->dma_buf, vma, 0);
+
+ return es_gem_mmap_obj(obj, vma);
+}
diff --git a/drivers/gpu/drm/eswin/es_gem.h b/drivers/gpu/drm/eswin/es_gem.h
new file mode 100644
index 000000000000..b14b32ea5cb4
--- /dev/null
+++ b/drivers/gpu/drm/eswin/es_gem.h
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Eswin Holdings Co., Ltd.
+ */
+
+#ifndef __ES_GEM_H__
+#define __ES_GEM_H__
+
+#include <linux/dma-buf.h>
+
+#include <drm/drm_gem.h>
+
+#include "es_drv.h"
+
+#ifdef CONFIG_ESWIN_MMU
+typedef struct _iova_info {
+ u32 iova;
+ u32 nr_pages;
+} iova_info_t;
+#endif
+
+/*
+ *
+ * @base: drm gem object.
+ * @size: size requested from user
+ * @cookie: cookie returned by dma_alloc_attrs
+ * - not kernel virtual address with DMA_ATTR_NO_KERNEL_MAPPING
+ * @dma_addr: bus address(accessed by dma) to allocated memory region.
+ * - this address could be physical address without IOMMU and
+ * device address with IOMMU.
+ * @dma_attrs: attribute for DMA API
+ * @get_pages: flag for manually applying for non-contiguous memory.
+ * @pages: Array of backing pages.
+ * @sgt: Imported sg_table.
+ *
+ */
+struct es_gem_object {
+ struct drm_gem_object base;
+ size_t size;
+ void *cookie;
+ dma_addr_t dma_addr;
+ u32 iova;
+ unsigned long dma_attrs;
+ bool get_pages;
+ struct page **pages;
+ struct sg_table *sgt;
+#ifdef CONFIG_ESWIN_MMU
+ iova_info_t *iova_list;
+ u32 nr_iova;
+#endif
+};
+
+static inline struct es_gem_object *to_es_gem_object(struct drm_gem_object *obj)
+{
+ return container_of(obj, struct es_gem_object, base);
+}
+
+struct es_gem_object *es_gem_create_object(struct drm_device *dev, size_t size);
+
+int es_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma);
+
+int es_gem_dumb_create(struct drm_file *file_priv, struct drm_device *drm,
+ struct drm_mode_create_dumb *args);
+
+int es_gem_mmap(struct file *filp, struct vm_area_struct *vma);
+
+struct sg_table *es_gem_prime_get_sg_table(struct drm_gem_object *obj);
+
+struct drm_gem_object *es_gem_prime_import(struct drm_device *dev,
+ struct dma_buf *dma_buf);
+struct drm_gem_object *
+es_gem_prime_import_sg_table(struct drm_device *dev,
+ struct dma_buf_attachment *attach,
+ struct sg_table *sgt);
+
+#endif /* __ES_GEM_H__ */
diff --git a/drivers/gpu/drm/eswin/es_plane.c b/drivers/gpu/drm/eswin/es_plane.c
new file mode 100644
index 000000000000..68e14d921de5
--- /dev/null
+++ b/drivers/gpu/drm/eswin/es_plane.c
@@ -0,0 +1,399 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Eswin Holdings Co., Ltd.
+ */
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_blend.h>
+
+#include "es_drm.h"
+#include "es_type.h"
+#include "es_crtc.h"
+#include "es_plane.h"
+#include "es_gem.h"
+#include "es_fb.h"
+
+void es_plane_destory(struct drm_plane *plane)
+{
+ struct es_plane *es_plane = to_es_plane(plane);
+
+ drm_plane_cleanup(plane);
+ kfree(es_plane);
+}
+
+static void es_plane_reset(struct drm_plane *plane)
+{
+ struct es_plane_state *state;
+
+ if (plane->state) {
+ __drm_atomic_helper_plane_destroy_state(plane->state);
+
+ state = to_es_plane_state(plane->state);
+ kfree(state);
+ plane->state = NULL;
+ }
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (state == NULL)
+ return;
+
+ __drm_atomic_helper_plane_reset(plane, &state->base);
+
+ state->degamma = ES_DEGAMMA_DISABLE;
+ state->degamma_changed = false;
+ memset(&state->status, 0, sizeof(state->status));
+}
+
+static void _es_plane_duplicate_blob(struct es_plane_state *state,
+ struct es_plane_state *ori_state)
+{
+ state->roi = ori_state->roi;
+ state->color_mgmt = ori_state->color_mgmt;
+ if (state->roi)
+ drm_property_blob_get(state->roi);
+ if (state->color_mgmt)
+ drm_property_blob_get(state->color_mgmt);
+}
+
+static struct drm_plane_state *
+es_plane_atomic_duplicate_state(struct drm_plane *plane)
+{
+ struct es_plane_state *ori_state;
+ struct es_plane_state *state;
+
+ if (WARN_ON(!plane->state))
+ return NULL;
+
+ ori_state = to_es_plane_state(plane->state);
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return NULL;
+
+ __drm_atomic_helper_plane_duplicate_state(plane, &state->base);
+
+ state->degamma = ori_state->degamma;
+ state->degamma_changed = ori_state->degamma_changed;
+
+ _es_plane_duplicate_blob(state, ori_state);
+ memcpy(&state->status, &ori_state->status, sizeof(ori_state->status));
+
+ return &state->base;
+}
+
+static void es_plane_atomic_destroy_state(struct drm_plane *plane,
+ struct drm_plane_state *state)
+{
+ struct es_plane_state *es_plane_state = to_es_plane_state(state);
+
+ __drm_atomic_helper_plane_destroy_state(state);
+ drm_property_blob_put(es_plane_state->roi);
+ drm_property_blob_put(es_plane_state->color_mgmt);
+ kfree(es_plane_state);
+}
+
+static int _es_plane_set_property_blob_from_id(struct drm_device *dev,
+ struct drm_property_blob **blob,
+ u64 blob_id,
+ size_t expected_size)
+{
+ struct drm_property_blob *new_blob = NULL;
+
+ if (blob_id) {
+ new_blob = drm_property_lookup_blob(dev, blob_id);
+ if (!new_blob) {
+ return -EINVAL;
+ }
+
+ if (new_blob->length != expected_size) {
+ drm_property_blob_put(new_blob);
+ return -EINVAL;
+ }
+ }
+ drm_property_replace_blob(blob, new_blob);
+ drm_property_blob_put(new_blob);
+ return 0;
+}
+
+static int es_plane_atomic_set_property(struct drm_plane *plane,
+ struct drm_plane_state *state,
+ struct drm_property *property,
+ uint64_t val)
+{
+ struct drm_device *dev = plane->dev;
+ struct es_plane *es_plane = to_es_plane(plane);
+ struct es_plane_state *es_plane_state = to_es_plane_state(state);
+ int ret = 0;
+
+ if (property == es_plane->degamma_mode) {
+ if (es_plane_state->degamma != val) {
+ es_plane_state->degamma = val;
+ es_plane_state->degamma_changed = true;
+ } else {
+ es_plane_state->degamma_changed = false;
+ }
+ } else if (property == es_plane->roi_prop) {
+ ret = _es_plane_set_property_blob_from_id(
+ dev, &es_plane_state->roi, val,
+ sizeof(struct drm_es_roi));
+ return ret;
+ } else if (property == es_plane->color_mgmt_prop) {
+ ret = _es_plane_set_property_blob_from_id(
+ dev, &es_plane_state->color_mgmt, val,
+ sizeof(struct drm_es_color_mgmt));
+ return ret;
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int es_plane_atomic_get_property(struct drm_plane *plane,
+ const struct drm_plane_state *state,
+ struct drm_property *property,
+ uint64_t *val)
+{
+ struct es_plane *es_plane = to_es_plane(plane);
+ const struct es_plane_state *es_plane_state =
+ container_of(state, const struct es_plane_state, base);
+
+ if (property == es_plane->degamma_mode)
+ *val = es_plane_state->degamma;
+ else if (property == es_plane->roi_prop)
+ *val = (es_plane_state->roi) ? es_plane_state->roi->base.id : 0;
+ else if (property == es_plane->color_mgmt_prop)
+ *val = (es_plane_state->color_mgmt) ?
+ es_plane_state->color_mgmt->base.id :
+ 0;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static bool es_format_mod_supported(struct drm_plane *plane, u32 format,
+ u64 modifier)
+{
+ int i;
+
+ /* We always have to allow these modifiers:
+ * 1. Core DRM checks for LINEAR support if userspace does not provide
+ modifiers.
+ * 2. Not passing any modifiers is the same as explicitly passing
+ INVALID.
+ */
+ if (modifier == DRM_FORMAT_MOD_LINEAR)
+ return true;
+
+ /* Check that the modifier is on the list of the plane's supported
+ modifiers. */
+ for (i = 0; i < plane->modifier_count; i++) {
+ if (modifier == plane->modifiers[i])
+ break;
+ }
+
+ if (i == plane->modifier_count)
+ return false;
+
+ return true;
+}
+
+const struct drm_plane_funcs es_plane_funcs = {
+ .update_plane = drm_atomic_helper_update_plane,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .destroy = es_plane_destory,
+ .reset = es_plane_reset,
+ .atomic_duplicate_state = es_plane_atomic_duplicate_state,
+ .atomic_destroy_state = es_plane_atomic_destroy_state,
+ .atomic_set_property = es_plane_atomic_set_property,
+ .atomic_get_property = es_plane_atomic_get_property,
+ .format_mod_supported = es_format_mod_supported,
+};
+
+static unsigned char es_get_plane_number(struct drm_framebuffer *fb)
+{
+ const struct drm_format_info *info;
+
+ if (!fb)
+ return 0;
+
+ info = drm_format_info(fb->format->format);
+ if (!info || info->num_planes > MAX_NUM_PLANES)
+ return 0;
+
+ return info->num_planes;
+}
+
+static int es_plane_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_state *state)
+{
+ struct drm_plane_state *new_plane_state =
+ drm_atomic_get_new_plane_state(state, plane);
+ struct es_plane *es_plane = to_es_plane(plane);
+ struct drm_framebuffer *fb = new_plane_state->fb;
+ struct drm_crtc *crtc = new_plane_state->crtc;
+ struct es_crtc *es_crtc = to_es_crtc(crtc);
+
+ if (!crtc || !fb)
+ return 0;
+
+ return es_plane->funcs->check(es_crtc->dev, es_plane, new_plane_state);
+}
+
+static void es_plane_atomic_update(struct drm_plane *plane,
+ struct drm_atomic_state *old_state)
+{
+ unsigned char i, num_planes;
+ struct drm_framebuffer *fb;
+ struct es_plane *es_plane = to_es_plane(plane);
+ struct drm_plane_state *state = plane->state;
+ struct es_crtc *es_crtc = to_es_crtc(state->crtc);
+ struct es_plane_state *plane_state = to_es_plane_state(state);
+ // struct drm_format_name_buf *name = &plane_state->status.format_name;
+
+ if (!state->fb || !state->crtc)
+ return;
+
+ fb = state->fb;
+
+ num_planes = es_get_plane_number(fb);
+
+ for (i = 0; i < num_planes; i++) {
+ struct es_gem_object *es_obj;
+
+ es_obj = es_fb_get_gem_obj(fb, i);
+ es_plane->dma_addr[i] = es_obj->iova + fb->offsets[i];
+ }
+
+ plane_state->status.tile_mode = 0; /* to be updated */
+ plane_state->status.src = drm_plane_state_src(state);
+ plane_state->status.dest = drm_plane_state_dest(state);
+ // drm_get_format_name(fb->format->format, name);
+
+ es_plane->funcs->update(es_crtc->dev, es_plane);
+}
+
+static void es_plane_atomic_disable(struct drm_plane *plane,
+ struct drm_atomic_state *state)
+{
+ struct drm_plane_state *old_plane_state =
+ drm_atomic_get_old_plane_state(state, plane);
+ struct es_plane *es_plane = to_es_plane(plane);
+ struct es_crtc *es_crtc = to_es_crtc(old_plane_state->crtc);
+
+ if (!old_plane_state->crtc)
+ return;
+
+ if (es_plane->funcs && es_plane->funcs->disable)
+ es_plane->funcs->disable(es_crtc->dev, es_plane);
+}
+
+const struct drm_plane_helper_funcs es_plane_helper_funcs = {
+ .atomic_check = es_plane_atomic_check,
+ .atomic_update = es_plane_atomic_update,
+ .atomic_disable = es_plane_atomic_disable,
+};
+
+static const struct drm_prop_enum_list es_degamma_mode_enum_list[] = {
+ { ES_DEGAMMA_DISABLE, "disabled" },
+ { ES_DEGAMMA_BT709, "preset degamma for BT709" },
+ { ES_DEGAMMA_BT2020, "preset degamma for BT2020" },
+};
+
+struct es_plane *es_plane_create(struct drm_device *drm_dev,
+ struct es_plane_info *info,
+ unsigned int possible_crtcs)
+{
+ struct es_plane *plane;
+ int ret;
+
+ if (!info)
+ return NULL;
+
+ plane = kzalloc(sizeof(struct es_plane), GFP_KERNEL);
+ if (!plane)
+ return NULL;
+
+ plane->id = info->id;
+
+ ret = drm_universal_plane_init(drm_dev, &plane->base, possible_crtcs,
+ &es_plane_funcs, info->formats,
+ info->num_formats, info->modifiers,
+ info->type,
+ info->name ? info->name : NULL);
+ if (ret)
+ goto err_free_plane;
+
+ drm_plane_helper_add(&plane->base, &es_plane_helper_funcs);
+
+ /* Set up the plane properties */
+ if (info->degamma_size) {
+ plane->degamma_mode = drm_property_create_enum(
+ drm_dev, 0, "DEGAMMA_MODE", es_degamma_mode_enum_list,
+ ARRAY_SIZE(es_degamma_mode_enum_list));
+
+ if (!plane->degamma_mode)
+ goto error_cleanup_plane;
+
+ drm_object_attach_property(&plane->base.base,
+ plane->degamma_mode,
+ ES_DEGAMMA_DISABLE);
+ }
+
+ if (info->rotation) {
+ ret = drm_plane_create_rotation_property(
+ &plane->base, DRM_MODE_ROTATE_0, info->rotation);
+ if (ret)
+ goto error_cleanup_plane;
+ }
+
+ if (info->blend_mode) {
+ ret = drm_plane_create_blend_mode_property(&plane->base,
+ info->blend_mode);
+ if (ret)
+ goto error_cleanup_plane;
+ ret = drm_plane_create_alpha_property(&plane->base);
+ if (ret)
+ goto error_cleanup_plane;
+ }
+
+ if (info->color_encoding) {
+ ret = drm_plane_create_color_properties(
+ &plane->base, info->color_encoding,
+ BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
+ DRM_COLOR_YCBCR_BT709, DRM_COLOR_YCBCR_LIMITED_RANGE);
+ if (ret)
+ goto error_cleanup_plane;
+ }
+
+ if (info->roi) {
+ plane->roi_prop = drm_property_create(
+ drm_dev, DRM_MODE_PROP_BLOB, "ROI", 0);
+ if (!plane->roi_prop)
+ goto error_cleanup_plane;
+
+ drm_object_attach_property(&plane->base.base, plane->roi_prop,
+ 0);
+ }
+
+ if (info->color_mgmt) {
+ plane->color_mgmt_prop = drm_property_create(
+ drm_dev, DRM_MODE_PROP_BLOB, "COLOR_CONFIG", 0);
+ if (!plane->color_mgmt_prop)
+ return NULL;
+
+ drm_object_attach_property(&plane->base.base,
+ plane->color_mgmt_prop, 0);
+ }
+
+ return plane;
+
+error_cleanup_plane:
+ drm_plane_cleanup(&plane->base);
+err_free_plane:
+ kfree(plane);
+ return NULL;
+}
diff --git a/drivers/gpu/drm/eswin/es_plane.h b/drivers/gpu/drm/eswin/es_plane.h
new file mode 100644
index 000000000000..cc41d0741361
--- /dev/null
+++ b/drivers/gpu/drm/eswin/es_plane.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Eswin Holdings Co., Ltd.
+ */
+
+#ifndef __ES_PLANE_H__
+#define __ES_PLANE_H__
+
+#include <drm/drm_fourcc.h>
+#include <drm/drm_plane_helper.h>
+
+#include "es_type.h"
+#include "es_fb.h"
+
+#define MAX_NUM_PLANES 3 /* colour format plane */
+
+struct es_plane;
+
+struct es_plane_funcs {
+ void (*update)(struct device *dev, struct es_plane *plane);
+ void (*disable)(struct device *dev, struct es_plane *plane);
+ int (*check)(struct device *dev, struct es_plane *plane,
+ struct drm_plane_state *state);
+};
+
+struct drm_es_roi {
+ __u16 enable;
+ __u16 roi_x;
+ __u16 roi_y;
+ __u16 roi_w;
+ __u16 roi_h;
+};
+
+struct drm_es_color_mgmt {
+ __u32 colorkey;
+ __u32 colorkey_high;
+ __u32 clear_value;
+ __u16 clear_enable;
+ __u16 transparency;
+};
+
+struct es_plane_status {
+ u32 tile_mode;
+ struct drm_rect src;
+ struct drm_rect dest;
+ // struct drm_format_name_buf format_name;
+};
+
+struct es_plane_state {
+ struct drm_plane_state base;
+ struct es_plane_status status; /* for debugfs */
+ struct drm_property_blob *roi;
+ struct drm_property_blob *color_mgmt;
+
+ u32 degamma;
+ bool degamma_changed;
+};
+
+struct es_plane {
+ struct drm_plane base;
+ u8 id;
+ dma_addr_t dma_addr[MAX_NUM_PLANES];
+
+ struct drm_property *degamma_mode;
+ struct drm_property *roi_prop;
+ struct drm_property *color_mgmt_prop;
+
+ const struct es_plane_funcs *funcs;
+};
+
+void es_plane_destory(struct drm_plane *plane);
+
+struct es_plane *es_plane_create(struct drm_device *drm_dev,
+ struct es_plane_info *info,
+ unsigned int possible_crtcs);
+
+static inline struct es_plane *to_es_plane(struct drm_plane *plane)
+{
+ return container_of(plane, struct es_plane, base);
+}
+
+static inline struct es_plane_state *
+to_es_plane_state(struct drm_plane_state *state)
+{
+ return container_of(state, struct es_plane_state, base);
+}
+#endif /* __ES_PLANE_H__ */
diff --git a/drivers/gpu/drm/eswin/es_simple_enc.c b/drivers/gpu/drm/eswin/es_simple_enc.c
new file mode 100644
index 000000000000..fbc725fc64ea
--- /dev/null
+++ b/drivers/gpu/drm/eswin/es_simple_enc.c
@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Eswin Holdings Co., Ltd.
+ */
+
+#include <linux/component.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/media-bus-format.h>
+
+#include <drm/drm_bridge.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_blend.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_modeset_helper_vtables.h>
+
+#include "es_crtc.h"
+#include "es_simple_enc.h"
+
+static const struct simple_encoder_priv hdmi_priv = {
+ .encoder_type = DRM_MODE_ENCODER_TMDS
+};
+
+static const struct simple_encoder_priv dsi_priv = {
+ .encoder_type = DRM_MODE_ENCODER_DSI
+};
+
+static const struct drm_encoder_funcs encoder_funcs = {
+ .destroy = drm_encoder_cleanup
+};
+
+static inline struct simple_encoder *to_simple_encoder(struct drm_encoder *enc)
+{
+ return container_of(enc, struct simple_encoder, encoder);
+}
+
+static int encoder_parse_dt(struct device *dev)
+{
+ struct simple_encoder *simple = dev_get_drvdata(dev);
+ int ret = 0;
+ int cnt, i;
+ u32 *vals;
+ u32 *masks;
+
+ simple->dss_regmap = syscon_regmap_lookup_by_phandle(
+ dev->of_node, "eswin,dss-syscon");
+
+ if (IS_ERR(simple->dss_regmap)) {
+ if (PTR_ERR(simple->dss_regmap) != -ENODEV) {
+ dev_err(dev, "failed to get dss-syscon\n");
+ ret = PTR_ERR(simple->dss_regmap);
+ goto err;
+ }
+ simple->dss_regmap = NULL;
+ goto err;
+ }
+
+ cnt = of_property_count_elems_of_size(dev->of_node, "eswin,mux-mask",
+ 4);
+ if (!cnt) {
+ ret = cnt;
+ goto err;
+ }
+
+ simple->dss_regdatas = devm_kzalloc(
+ dev, sizeof(*simple->dss_regdatas) * cnt, GFP_KERNEL);
+
+ masks = kcalloc(cnt, sizeof(*masks), GFP_KERNEL);
+ if (!masks) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ vals = kcalloc(cnt, sizeof(*vals), GFP_KERNEL);
+ if (!vals) {
+ ret = -ENOMEM;
+ goto err_free_masks;
+ }
+
+ ret = of_property_read_u32_array(dev->of_node, "eswin,mux-mask", masks,
+ cnt);
+ if (ret)
+ goto err_free_vals;
+
+ ret = of_property_read_u32_array(dev->of_node, "eswin,mux-val", vals,
+ cnt);
+ if (ret)
+ goto err_free_vals;
+
+ for (i = 0; i < cnt; i++) {
+ simple->dss_regdatas[i].mask = masks[i];
+ simple->dss_regdatas[i].value = vals[i];
+ }
+
+err_free_vals:
+ kfree(vals);
+err_free_masks:
+ kfree(masks);
+err:
+ return ret;
+}
+
+void encoder_atomic_enable(struct drm_encoder *encoder,
+ struct drm_atomic_state *state)
+{
+ struct simple_encoder *simple = to_simple_encoder(encoder);
+ struct dss_data *data = simple->dss_regdatas;
+ int crtc_id;
+
+ if (!simple->dss_regmap)
+ return;
+
+ crtc_id = drm_of_encoder_active_endpoint_id(simple->dev->of_node,
+ encoder);
+
+ regmap_update_bits(simple->dss_regmap, 0, data[crtc_id].mask,
+ data[crtc_id].value);
+}
+
+int encoder_atomic_check(struct drm_encoder *encoder,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ struct es_crtc_state *es_crtc_state = to_es_crtc_state(crtc_state);
+ struct drm_connector *connector = conn_state->connector;
+
+ es_crtc_state->encoder_type = encoder->encoder_type;
+
+ if (connector->display_info.num_bus_formats)
+ es_crtc_state->output_fmt =
+ connector->display_info.bus_formats[0];
+ else
+ es_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
+
+ switch (es_crtc_state->output_fmt) {
+ case MEDIA_BUS_FMT_RGB565_1X16:
+ case MEDIA_BUS_FMT_RGB666_1X18:
+ case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ case MEDIA_BUS_FMT_RGB101010_1X30:
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_YUV8_1X24:
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+ case MEDIA_BUS_FMT_UYVY10_1X20:
+ case MEDIA_BUS_FMT_YUV10_1X30:
+ break;
+ default:
+ es_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
+ break;
+ }
+
+ return 0;
+}
+
+static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
+ .atomic_enable = encoder_atomic_enable,
+ .atomic_check = encoder_atomic_check,
+};
+
+static int encoder_bind(struct device *dev, struct device *master, void *data)
+{
+ struct drm_device *drm_dev = data;
+ struct simple_encoder *simple = dev_get_drvdata(dev);
+ struct drm_encoder *encoder;
+ struct drm_bridge *bridge;
+ int ret;
+
+ encoder = &simple->encoder;
+
+ /* Encoder. */
+ ret = drm_encoder_init(drm_dev, encoder, &encoder_funcs,
+ simple->priv->encoder_type, NULL);
+ if (ret)
+ return ret;
+
+ drm_encoder_helper_add(encoder, &encoder_helper_funcs);
+
+ encoder->possible_crtcs =
+ drm_of_find_possible_crtcs(drm_dev, dev->of_node);
+
+ /* output port is port1*/
+ ret = drm_of_find_panel_or_bridge(dev->of_node, 1, -1, NULL, &bridge);
+ if (ret)
+ goto err;
+
+ ret = drm_bridge_attach(encoder, bridge, NULL, 0);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ drm_encoder_cleanup(encoder);
+
+ return ret;
+}
+
+static void encoder_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct simple_encoder *simple = dev_get_drvdata(dev);
+
+ drm_encoder_cleanup(&simple->encoder);
+}
+
+static const struct component_ops encoder_component_ops = {
+ .bind = encoder_bind,
+ .unbind = encoder_unbind,
+};
+
+static const struct of_device_id simple_encoder_dt_match[] = {
+ { .compatible = "eswin,hdmi-encoder", .data = &hdmi_priv },
+ { .compatible = "eswin,dp-encoder", .data = &hdmi_priv },
+ { .compatible = "eswin,dsi-encoder", .data = &dsi_priv },
+ {},
+};
+MODULE_DEVICE_TABLE(of, simple_encoder_dt_match);
+
+static int encoder_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct simple_encoder *simple;
+ int ret;
+
+ simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL);
+ if (!simple)
+ return -ENOMEM;
+
+ simple->priv = of_device_get_match_data(dev);
+
+ simple->dev = dev;
+
+ dev_set_drvdata(dev, simple);
+
+ ret = encoder_parse_dt(dev);
+ if (ret)
+ return ret;
+
+ return component_add(dev, &encoder_component_ops);
+}
+
+static int encoder_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ component_del(dev, &encoder_component_ops);
+
+ dev_set_drvdata(dev, NULL);
+
+ return 0;
+}
+
+struct platform_driver simple_encoder_driver = {
+ .probe = encoder_probe,
+ .remove = encoder_remove,
+ .driver = {
+ .name = "es-simple-encoder",
+ .of_match_table = of_match_ptr(simple_encoder_dt_match),
+ },
+};
+
+MODULE_DESCRIPTION("Simple Encoder Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/eswin/es_simple_enc.h b/drivers/gpu/drm/eswin/es_simple_enc.h
new file mode 100644
index 000000000000..bc461e1231e3
--- /dev/null
+++ b/drivers/gpu/drm/eswin/es_simple_enc.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Eswin Holdings Co., Ltd.
+ */
+
+#ifndef __ES_SIMPLE_ENC_H_
+#define __ES_SIMPLE_ENC_H_
+
+struct simple_encoder_priv {
+ unsigned char encoder_type;
+};
+
+struct dss_data {
+ u32 mask;
+ u32 value;
+};
+
+struct simple_encoder {
+ struct drm_encoder encoder;
+ struct device *dev;
+ const struct simple_encoder_priv *priv;
+ struct regmap *dss_regmap;
+ struct dss_data *dss_regdatas;
+};
+
+extern struct platform_driver simple_encoder_driver;
+#endif /* __ES_SIMPLE_ENC_H_ */
diff --git a/drivers/gpu/drm/eswin/es_type.h b/drivers/gpu/drm/eswin/es_type.h
new file mode 100644
index 000000000000..aca0a83b0017
--- /dev/null
+++ b/drivers/gpu/drm/eswin/es_type.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Eswin Holdings Co., Ltd.
+ */
+
+#ifndef __ES_TYPE_H__
+#define __ES_TYPE_H__
+
+#include <drm/drm_plane.h>
+#include <drm/drm_plane_helper.h>
+
+struct es_plane_info {
+ const char *name;
+ u8 id;
+ enum drm_plane_type type;
+ unsigned int num_formats;
+ const u32 *formats;
+ const u64 *modifiers;
+ unsigned int min_width;
+ unsigned int min_height;
+ unsigned int max_width;
+ unsigned int max_height;
+ unsigned int rotation;
+ unsigned int blend_mode;
+ unsigned int color_encoding;
+
+ /* 0 means no de-gamma LUT */
+ unsigned int degamma_size;
+
+ int min_scale; /* 16.16 fixed point */
+ int max_scale; /* 16.16 fixed point */
+ bool roi;
+ bool color_mgmt;
+ bool background;
+};
+
+struct es_dc_info {
+ const char *name;
+
+ /* planes */
+ unsigned char plane_num;
+ const struct es_plane_info *planes;
+
+ unsigned int max_bpc;
+ unsigned int color_formats;
+
+ /* 0 means no gamma LUT */
+ u16 gamma_size;
+ u8 gamma_bits;
+
+ u16 pitch_alignment;
+
+ bool pipe_sync;
+ bool mmu_prefetch;
+ bool background;
+};
+
+#endif /* __ES_TYPE_H__ */
diff --git a/drivers/gpu/drm/eswin/es_virtual.c b/drivers/gpu/drm/eswin/es_virtual.c
new file mode 100644
index 000000000000..1d35ada1c0c0
--- /dev/null
+++ b/drivers/gpu/drm/eswin/es_virtual.c
@@ -0,0 +1,1310 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Eswin Holdings Co., Ltd.
+ */
+
+#include <linux/component.h>
+#include <linux/of_platform.h>
+#include <linux/media-bus-format.h>
+#include <linux/debugfs.h>
+
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_atomic_helper.h>
+
+#include "es_virtual.h"
+#include "es_dc.h"
+#include "es_gem.h"
+
+static unsigned char __get_bpp(struct es_virtual_display *vd)
+{
+ if (vd->bus_format == MEDIA_BUS_FMT_RGB101010_1X30)
+ return 10;
+ return 8;
+}
+
+static void vd_dump_destroy(struct es_virtual_display *vd)
+{
+ struct drm_device *drm_dev = vd->encoder.dev;
+
+ if (vd->dump_blob.data) {
+ vunmap(vd->dump_blob.data);
+ vd->dump_blob.data = NULL;
+ }
+ vd->dump_blob.size = 0;
+
+ debugfs_remove(vd->dump_debugfs);
+ vd->dump_debugfs = NULL;
+
+ if (vd->dump_obj) {
+ mutex_lock(&drm_dev->struct_mutex);
+ drm_gem_object_put(&vd->dump_obj->base);
+ mutex_unlock(&drm_dev->struct_mutex);
+ vd->dump_obj = NULL;
+ }
+}
+
+static void vd_dump_create(struct es_virtual_display *vd,
+ struct drm_display_mode *mode)
+{
+ struct drm_device *drm_dev = vd->encoder.dev;
+ struct es_dc *dc = dev_get_drvdata(vd->dc);
+ struct es_gem_object *obj;
+ unsigned int pitch, size;
+ void *kvaddr;
+ char *name;
+
+ if (!dc->funcs)
+ return;
+
+ vd_dump_destroy(vd);
+
+ /* dump in 4bytes XRGB format */
+ pitch = mode->hdisplay * 4;
+ pitch = ALIGN(pitch, dc->hw.info->pitch_alignment);
+ size = PAGE_ALIGN(pitch * mode->vdisplay);
+
+ obj = es_gem_create_object(drm_dev, size);
+ if (IS_ERR(obj)) {
+ return;
+ }
+
+ vd->dump_obj = obj;
+ vd->pitch = pitch;
+
+ kvaddr = vmap(obj->pages, obj->size >> PAGE_SHIFT, VM_MAP,
+ pgprot_writecombine(PAGE_KERNEL));
+ if (!kvaddr)
+ goto err;
+
+ vd->dump_blob.data = kvaddr;
+ vd->dump_blob.size = obj->size;
+
+ name = kasprintf(GFP_KERNEL, "%dx%d-XRGB-%d.raw", mode->hdisplay,
+ mode->vdisplay, __get_bpp(vd));
+ if (!name)
+ goto err;
+
+ vd->dump_debugfs = debugfs_create_blob(
+ name, 0444, vd->connector.debugfs_entry, &vd->dump_blob);
+ kfree(name);
+
+ return;
+
+err:
+ vd_dump_destroy(vd);
+}
+
+static void vd_encoder_destroy(struct drm_encoder *encoder)
+{
+ struct es_virtual_display *vd;
+
+ drm_encoder_cleanup(encoder);
+ vd = to_virtual_display_with_encoder(encoder);
+ vd_dump_destroy(vd);
+}
+
+static const struct drm_encoder_funcs vd_encoder_funcs = {
+ .destroy = vd_encoder_destroy
+};
+
+static void vd_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct es_virtual_display *vd;
+
+ vd = to_virtual_display_with_encoder(encoder);
+ vd_dump_create(vd, adjusted_mode);
+}
+
+static void vd_encoder_disable(struct drm_encoder *encoder)
+{
+ struct es_virtual_display *vd;
+ struct es_dc *dc;
+
+ vd = to_virtual_display_with_encoder(encoder);
+ dc = dev_get_drvdata(vd->dc);
+ if (dc->funcs && dc->funcs->dump_disable)
+ dc->funcs->dump_disable(vd->dc);
+}
+
+static void vd_encoder_enable(struct drm_encoder *encoder)
+{
+ struct es_virtual_display *vd;
+ struct es_dc *dc;
+
+ vd = to_virtual_display_with_encoder(encoder);
+ dc = dev_get_drvdata(vd->dc);
+ if (dc->funcs && dc->funcs->dump_enable && vd->dump_obj)
+ dc->funcs->dump_enable(vd->dc, vd->dump_obj->iova, vd->pitch);
+}
+
+int vd_encoder_atomic_check(struct drm_encoder *encoder,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ struct es_crtc_state *es_crtc_state = to_es_crtc_state(crtc_state);
+
+ es_crtc_state->encoder_type = encoder->encoder_type;
+
+ return 0;
+}
+
+static const struct drm_encoder_helper_funcs vd_encoder_helper_funcs = {
+ .mode_set = vd_mode_set,
+ .enable = vd_encoder_enable,
+ .disable = vd_encoder_disable,
+ .atomic_check = vd_encoder_atomic_check,
+};
+
+static const struct drm_display_mode edid_cea_modes_1[] = {
+ /* 1 - 640x480@60Hz 4:3 */
+ {
+ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656, 752,
+ 800, 0, 480, 490, 492, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
+ },
+ /* 2 - 720x480@60Hz 4:3 */
+ {
+ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736, 798,
+ 858, 0, 480, 489, 495, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
+ },
+ /* 3 - 720x480@60Hz 16:9 */
+ {
+ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736, 798,
+ 858, 0, 480, 489, 495, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 4 - 1280x720@60Hz 16:9 */
+ {
+ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
+ 1430, 1650, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 5 - 1920x1080i@60Hz 16:9 */
+ {
+ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008,
+ 2052, 2200, 0, 1080, 1084, 1094, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
+ DRM_MODE_FLAG_INTERLACE),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 6 - 720(1440)x480i@60Hz 4:3 */
+ {
+ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500, 720, 739, 801,
+ 858, 0, 480, 488, 494, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
+ DRM_MODE_FLAG_INTERLACE |
+ DRM_MODE_FLAG_DBLCLK),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
+ },
+ /* 7 - 720(1440)x480i@60Hz 16:9 */
+ {
+ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500, 720, 739, 801,
+ 858, 0, 480, 488, 494, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
+ DRM_MODE_FLAG_INTERLACE |
+ DRM_MODE_FLAG_DBLCLK),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 8 - 720(1440)x240@60Hz 4:3 */
+ {
+ DRM_MODE("720x240", DRM_MODE_TYPE_DRIVER, 13500, 720, 739, 801,
+ 858, 0, 240, 244, 247, 262, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
+ DRM_MODE_FLAG_DBLCLK),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
+ },
+ /* 9 - 720(1440)x240@60Hz 16:9 */
+ {
+ DRM_MODE("720x240", DRM_MODE_TYPE_DRIVER, 13500, 720, 739, 801,
+ 858, 0, 240, 244, 247, 262, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
+ DRM_MODE_FLAG_DBLCLK),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 10 - 2880x480i@60Hz 4:3 */
+ {
+ DRM_MODE("2880x480i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
+ 3204, 3432, 0, 480, 488, 494, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
+ DRM_MODE_FLAG_INTERLACE),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
+ },
+ /* 11 - 2880x480i@60Hz 16:9 */
+ {
+ DRM_MODE("2880x480i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
+ 3204, 3432, 0, 480, 488, 494, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
+ DRM_MODE_FLAG_INTERLACE),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 12 - 2880x240@60Hz 4:3 */
+ {
+ DRM_MODE("2880x240", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
+ 3204, 3432, 0, 240, 244, 247, 262, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
+ },
+ /* 13 - 2880x240@60Hz 16:9 */
+ {
+ DRM_MODE("2880x240", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
+ 3204, 3432, 0, 240, 244, 247, 262, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 14 - 1440x480@60Hz 4:3 */
+ {
+ DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1472,
+ 1596, 1716, 0, 480, 489, 495, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
+ },
+ /* 15 - 1440x480@60Hz 16:9 */
+ {
+ DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1472,
+ 1596, 1716, 0, 480, 489, 495, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 16 - 1920x1080@60Hz 16:9 */
+ {
+ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
+ 2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 17 - 720x576@50Hz 4:3 */
+ {
+ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732, 796,
+ 864, 0, 576, 581, 586, 625, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
+ },
+ /* 18 - 720x576@50Hz 16:9 */
+ {
+ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732, 796,
+ 864, 0, 576, 581, 586, 625, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 19 - 1280x720@50Hz 16:9 */
+ {
+ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720,
+ 1760, 1980, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 20 - 1920x1080i@50Hz 16:9 */
+ {
+ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448,
+ 2492, 2640, 0, 1080, 1084, 1094, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
+ DRM_MODE_FLAG_INTERLACE),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 21 - 720(1440)x576i@50Hz 4:3 */
+ {
+ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500, 720, 732, 795,
+ 864, 0, 576, 580, 586, 625, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
+ DRM_MODE_FLAG_INTERLACE |
+ DRM_MODE_FLAG_DBLCLK),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
+ },
+ /* 22 - 720(1440)x576i@50Hz 16:9 */
+ {
+ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500, 720, 732, 795,
+ 864, 0, 576, 580, 586, 625, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
+ DRM_MODE_FLAG_INTERLACE |
+ DRM_MODE_FLAG_DBLCLK),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 23 - 720(1440)x288@50Hz 4:3 */
+ {
+ DRM_MODE("720x288", DRM_MODE_TYPE_DRIVER, 13500, 720, 732, 795,
+ 864, 0, 288, 290, 293, 312, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
+ DRM_MODE_FLAG_DBLCLK),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
+ },
+ /* 24 - 720(1440)x288@50Hz 16:9 */
+ {
+ DRM_MODE("720x288", DRM_MODE_TYPE_DRIVER, 13500, 720, 732, 795,
+ 864, 0, 288, 290, 293, 312, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
+ DRM_MODE_FLAG_DBLCLK),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 25 - 2880x576i@50Hz 4:3 */
+ {
+ DRM_MODE("2880x576i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
+ 3180, 3456, 0, 576, 580, 586, 625, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
+ DRM_MODE_FLAG_INTERLACE),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
+ },
+ /* 26 - 2880x576i@50Hz 16:9 */
+ {
+ DRM_MODE("2880x576i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
+ 3180, 3456, 0, 576, 580, 586, 625, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
+ DRM_MODE_FLAG_INTERLACE),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 27 - 2880x288@50Hz 4:3 */
+ {
+ DRM_MODE("2880x288", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
+ 3180, 3456, 0, 288, 290, 293, 312, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
+ },
+ /* 28 - 2880x288@50Hz 16:9 */
+ {
+ DRM_MODE("2880x288", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
+ 3180, 3456, 0, 288, 290, 293, 312, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 29 - 1440x576@50Hz 4:3 */
+ {
+ DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464,
+ 1592, 1728, 0, 576, 581, 586, 625, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
+ },
+ /* 30 - 1440x576@50Hz 16:9 */
+ {
+ DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464,
+ 1592, 1728, 0, 576, 581, 586, 625, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 31 - 1920x1080@50Hz 16:9 */
+ {
+ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
+ 2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 32 - 1920x1080@24Hz 16:9 */
+ {
+ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2558,
+ 2602, 2750, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 33 - 1920x1080@25Hz 16:9 */
+ {
+ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448,
+ 2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 34 - 1920x1080@30Hz 16:9 */
+ {
+ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008,
+ 2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 35 - 2880x480@60Hz 4:3 */
+ {
+ DRM_MODE("2880x480", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2944,
+ 3192, 3432, 0, 480, 489, 495, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
+ },
+ /* 36 - 2880x480@60Hz 16:9 */
+ {
+ DRM_MODE("2880x480", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2944,
+ 3192, 3432, 0, 480, 489, 495, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 37 - 2880x576@50Hz 4:3 */
+ {
+ DRM_MODE("2880x576", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2928,
+ 3184, 3456, 0, 576, 581, 586, 625, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
+ },
+ /* 38 - 2880x576@50Hz 16:9 */
+ {
+ DRM_MODE("2880x576", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2928,
+ 3184, 3456, 0, 576, 581, 586, 625, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 39 - 1920x1080i@50Hz 16:9 */
+ {
+ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 72000, 1920, 1952,
+ 2120, 2304, 0, 1080, 1126, 1136, 1250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC |
+ DRM_MODE_FLAG_INTERLACE),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 40 - 1920x1080i@100Hz 16:9 */
+ {
+ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
+ 2492, 2640, 0, 1080, 1084, 1094, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
+ DRM_MODE_FLAG_INTERLACE),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 41 - 1280x720@100Hz 16:9 */
+ {
+ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1720,
+ 1760, 1980, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 42 - 720x576@100Hz 4:3 */
+ {
+ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 54000, 720, 732, 796,
+ 864, 0, 576, 581, 586, 625, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
+ },
+ /* 43 - 720x576@100Hz 16:9 */
+ {
+ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 54000, 720, 732, 796,
+ 864, 0, 576, 581, 586, 625, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 44 - 720(1440)x576i@100Hz 4:3 */
+ {
+ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 27000, 720, 732, 795,
+ 864, 0, 576, 580, 586, 625, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
+ DRM_MODE_FLAG_INTERLACE |
+ DRM_MODE_FLAG_DBLCLK),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
+ },
+ /* 45 - 720(1440)x576i@100Hz 16:9 */
+ {
+ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 27000, 720, 732, 795,
+ 864, 0, 576, 580, 586, 625, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
+ DRM_MODE_FLAG_INTERLACE |
+ DRM_MODE_FLAG_DBLCLK),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 46 - 1920x1080i@120Hz 16:9 */
+ {
+ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
+ 2052, 2200, 0, 1080, 1084, 1094, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
+ DRM_MODE_FLAG_INTERLACE),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 47 - 1280x720@120Hz 16:9 */
+ {
+ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1390,
+ 1430, 1650, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 48 - 720x480@120Hz 4:3 */
+ {
+ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 54000, 720, 736, 798,
+ 858, 0, 480, 489, 495, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
+ },
+ /* 49 - 720x480@120Hz 16:9 */
+ {
+ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 54000, 720, 736, 798,
+ 858, 0, 480, 489, 495, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 50 - 720(1440)x480i@120Hz 4:3 */
+ {
+ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 27000, 720, 739, 801,
+ 858, 0, 480, 488, 494, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
+ DRM_MODE_FLAG_INTERLACE |
+ DRM_MODE_FLAG_DBLCLK),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
+ },
+ /* 51 - 720(1440)x480i@120Hz 16:9 */
+ {
+ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 27000, 720, 739, 801,
+ 858, 0, 480, 488, 494, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
+ DRM_MODE_FLAG_INTERLACE |
+ DRM_MODE_FLAG_DBLCLK),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 52 - 720x576@200Hz 4:3 */
+ {
+ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 108000, 720, 732, 796,
+ 864, 0, 576, 581, 586, 625, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
+ },
+ /* 53 - 720x576@200Hz 16:9 */
+ {
+ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 108000, 720, 732, 796,
+ 864, 0, 576, 581, 586, 625, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 54 - 720(1440)x576i@200Hz 4:3 */
+ {
+ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 54000, 720, 732, 795,
+ 864, 0, 576, 580, 586, 625, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
+ DRM_MODE_FLAG_INTERLACE |
+ DRM_MODE_FLAG_DBLCLK),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
+ },
+ /* 55 - 720(1440)x576i@200Hz 16:9 */
+ {
+ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 54000, 720, 732, 795,
+ 864, 0, 576, 580, 586, 625, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
+ DRM_MODE_FLAG_INTERLACE |
+ DRM_MODE_FLAG_DBLCLK),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 56 - 720x480@240Hz 4:3 */
+ {
+ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 108000, 720, 736, 798,
+ 858, 0, 480, 489, 495, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
+ },
+ /* 57 - 720x480@240Hz 16:9 */
+ {
+ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 108000, 720, 736, 798,
+ 858, 0, 480, 489, 495, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 58 - 720(1440)x480i@240Hz 4:3 */
+ {
+ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 54000, 720, 739, 801,
+ 858, 0, 480, 488, 494, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
+ DRM_MODE_FLAG_INTERLACE |
+ DRM_MODE_FLAG_DBLCLK),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
+ },
+ /* 59 - 720(1440)x480i@240Hz 16:9 */
+ {
+ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 54000, 720, 739, 801,
+ 858, 0, 480, 488, 494, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
+ DRM_MODE_FLAG_INTERLACE |
+ DRM_MODE_FLAG_DBLCLK),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 60 - 1280x720@24Hz 16:9 */
+ {
+ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 59400, 1280, 3040,
+ 3080, 3300, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 61 - 1280x720@25Hz 16:9 */
+ {
+ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3700,
+ 3740, 3960, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 62 - 1280x720@30Hz 16:9 */
+ {
+ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3040,
+ 3080, 3300, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 63 - 1920x1080@120Hz 16:9 */
+ {
+ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2008,
+ 2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 64 - 1920x1080@100Hz 16:9 */
+ {
+ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2448,
+ 2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 65 - 1280x720@24Hz 64:27 */
+ {
+ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 59400, 1280, 3040,
+ 3080, 3300, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 66 - 1280x720@25Hz 64:27 */
+ {
+ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3700,
+ 3740, 3960, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 67 - 1280x720@30Hz 64:27 */
+ {
+ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3040,
+ 3080, 3300, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 68 - 1280x720@50Hz 64:27 */
+ {
+ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720,
+ 1760, 1980, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 69 - 1280x720@60Hz 64:27 */
+ {
+ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
+ 1430, 1650, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 70 - 1280x720@100Hz 64:27 */
+ {
+ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1720,
+ 1760, 1980, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 71 - 1280x720@120Hz 64:27 */
+ {
+ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1390,
+ 1430, 1650, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 72 - 1920x1080@24Hz 64:27 */
+ {
+ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2558,
+ 2602, 2750, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 73 - 1920x1080@25Hz 64:27 */
+ {
+ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448,
+ 2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 74 - 1920x1080@30Hz 64:27 */
+ {
+ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008,
+ 2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 75 - 1920x1080@50Hz 64:27 */
+ {
+ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
+ 2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 76 - 1920x1080@60Hz 64:27 */
+ {
+ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
+ 2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 77 - 1920x1080@100Hz 64:27 */
+ {
+ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2448,
+ 2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 78 - 1920x1080@120Hz 64:27 */
+ {
+ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2008,
+ 2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 79 - 1680x720@24Hz 64:27 */
+ {
+ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 3040,
+ 3080, 3300, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 80 - 1680x720@25Hz 64:27 */
+ {
+ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 2908,
+ 2948, 3168, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 81 - 1680x720@30Hz 64:27 */
+ {
+ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 2380,
+ 2420, 2640, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 82 - 1680x720@50Hz 64:27 */
+ {
+ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 82500, 1680, 1940,
+ 1980, 2200, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 83 - 1680x720@60Hz 64:27 */
+ {
+ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 99000, 1680, 1940,
+ 1980, 2200, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 84 - 1680x720@100Hz 64:27 */
+ {
+ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 165000, 1680, 1740,
+ 1780, 2000, 0, 720, 725, 730, 825, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 85 - 1680x720@120Hz 64:27 */
+ {
+ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 198000, 1680, 1740,
+ 1780, 2000, 0, 720, 725, 730, 825, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 86 - 2560x1080@24Hz 64:27 */
+ {
+ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 99000, 2560, 3558,
+ 3602, 3750, 0, 1080, 1084, 1089, 1100, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 87 - 2560x1080@25Hz 64:27 */
+ {
+ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 90000, 2560, 3008,
+ 3052, 3200, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 88 - 2560x1080@30Hz 64:27 */
+ {
+ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 118800, 2560, 3328,
+ 3372, 3520, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 89 - 2560x1080@50Hz 64:27 */
+ {
+ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 185625, 2560, 3108,
+ 3152, 3300, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 90 - 2560x1080@60Hz 64:27 */
+ {
+ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 198000, 2560, 2808,
+ 2852, 3000, 0, 1080, 1084, 1089, 1100, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 91 - 2560x1080@100Hz 64:27 */
+ {
+ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 371250, 2560, 2778,
+ 2822, 2970, 0, 1080, 1084, 1089, 1250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 92 - 2560x1080@120Hz 64:27 */
+ {
+ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 495000, 2560, 3108,
+ 3152, 3300, 0, 1080, 1084, 1089, 1250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 93 - 3840x2160@24Hz 16:9 */
+ {
+ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 5116,
+ 5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 94 - 3840x2160@25Hz 16:9 */
+ {
+ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4896,
+ 4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 95 - 3840x2160@30Hz 16:9 */
+ {
+ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4016,
+ 4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 96 - 3840x2160@50Hz 16:9 */
+ {
+ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4896,
+ 4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 97 - 3840x2160@60Hz 16:9 */
+ {
+ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4016,
+ 4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 98 - 4096x2160@24Hz 256:135 */
+ {
+ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 5116,
+ 5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135,
+ },
+ /* 99 - 4096x2160@25Hz 256:135 */
+ {
+ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 5064,
+ 5152, 5280, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135,
+ },
+ /* 100 - 4096x2160@30Hz 256:135 */
+ {
+ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 4184,
+ 4272, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135,
+ },
+ /* 101 - 4096x2160@50Hz 256:135 */
+ {
+ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 594000, 4096, 5064,
+ 5152, 5280, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135,
+ },
+ /* 102 - 4096x2160@60Hz 256:135 */
+ {
+ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 594000, 4096, 4184,
+ 4272, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135,
+ },
+ /* 103 - 3840x2160@24Hz 64:27 */
+ {
+ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 5116,
+ 5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 104 - 3840x2160@25Hz 64:27 */
+ {
+ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4896,
+ 4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 105 - 3840x2160@30Hz 64:27 */
+ {
+ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4016,
+ 4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 106 - 3840x2160@50Hz 64:27 */
+ {
+ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4896,
+ 4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 107 - 3840x2160@60Hz 64:27 */
+ {
+ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4016,
+ 4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 108 - 1280x720@48Hz 16:9 */
+ {
+ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 90000, 1280, 2240,
+ 2280, 2500, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 109 - 1280x720@48Hz 64:27 */
+ {
+ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 90000, 1280, 2240,
+ 2280, 2500, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 110 - 1680x720@48Hz 64:27 */
+ {
+ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 99000, 1680, 2490,
+ 2530, 2750, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 111 - 1920x1080@48Hz 16:9 */
+ {
+ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2558,
+ 2602, 2750, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 112 - 1920x1080@48Hz 64:27 */
+ {
+ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2558,
+ 2602, 2750, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 113 - 2560x1080@48Hz 64:27 */
+ {
+ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 198000, 2560, 3558,
+ 3602, 3750, 0, 1080, 1084, 1089, 1100, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 114 - 3840x2160@48Hz 16:9 */
+ {
+ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 5116,
+ 5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 115 - 4096x2160@48Hz 256:135 */
+ {
+ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 594000, 4096, 5116,
+ 5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135,
+ },
+ /* 116 - 3840x2160@48Hz 64:27 */
+ {
+ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 5116,
+ 5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 117 - 3840x2160@100Hz 16:9 */
+ {
+ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 1188000, 3840, 4896,
+ 4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 118 - 3840x2160@120Hz 16:9 */
+ {
+ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 1188000, 3840, 4016,
+ 4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
+ },
+ /* 119 - 3840x2160@100Hz 64:27 */
+ {
+ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 1188000, 3840, 4896,
+ 4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 120 - 3840x2160@120Hz 64:27 */
+ {
+ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 1188000, 3840, 4016,
+ 4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 121 - 5120x2160@24Hz 64:27 */
+ {
+ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 396000, 5120, 7116,
+ 7204, 7500, 0, 2160, 2168, 2178, 2200, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 122 - 5120x2160@25Hz 64:27 */
+ {
+ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 396000, 5120, 6816,
+ 6904, 7200, 0, 2160, 2168, 2178, 2200, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 123 - 5120x2160@30Hz 64:27 */
+ {
+ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 396000, 5120, 5784,
+ 5872, 6000, 0, 2160, 2168, 2178, 2200, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 124 - 5120x2160@48Hz 64:27 */
+ {
+ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 742500, 5120, 5866,
+ 5954, 6250, 0, 2160, 2168, 2178, 2475, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 125 - 5120x2160@50Hz 64:27 */
+ {
+ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 742500, 5120, 6216,
+ 6304, 6600, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 126 - 5120x2160@60Hz 64:27 */
+ {
+ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 742500, 5120, 5284,
+ 5372, 5500, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+ /* 127 - 5120x2160@100Hz 64:27 */
+ {
+ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 1485000, 5120, 6216,
+ 6304, 6600, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27,
+ },
+};
+
+static int vd_get_modes(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_display_mode *mode = NULL;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(edid_cea_modes_1); i++) {
+ if (edid_cea_modes_1[i].clock == 594000 ||
+ edid_cea_modes_1[i].clock == 297000 ||
+ edid_cea_modes_1[i].clock == 148500 ||
+ edid_cea_modes_1[i].clock == 108000 ||
+ edid_cea_modes_1[i].clock == 74250 ||
+ edid_cea_modes_1[i].clock == 54000 ||
+ edid_cea_modes_1[i].clock == 27000) {
+ mode = drm_mode_duplicate(dev, &edid_cea_modes_1[i]);
+ drm_mode_probed_add(connector, mode);
+ }
+ }
+
+ return 0;
+}
+
+static struct drm_encoder *vd_best_encoder(struct drm_connector *connector)
+{
+ struct es_virtual_display *vd;
+
+ vd = to_virtual_display_with_connector(connector);
+ return &vd->encoder;
+}
+
+static enum drm_mode_status vd_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ if (mode->clock != 594000 && mode->clock != 297000 &&
+ mode->clock != 148500 && mode->clock != 108000 &&
+ mode->clock != 74250 && mode->clock != 54000 &&
+ mode->clock != 27000) {
+ return MODE_NOCLOCK;
+ }
+
+ return MODE_OK;
+}
+
+static const struct drm_connector_helper_funcs vd_connector_helper_funcs = {
+ .get_modes = vd_get_modes,
+ .mode_valid = vd_mode_valid,
+ .best_encoder = vd_best_encoder,
+};
+
+static void vd_connector_destroy(struct drm_connector *connector)
+{
+ drm_connector_unregister(connector);
+ drm_connector_cleanup(connector);
+}
+
+static enum drm_connector_status
+vd_connector_detect(struct drm_connector *connector, bool force)
+{
+ return connector_status_connected;
+}
+
+static const struct drm_connector_funcs vd_connector_funcs = {
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = vd_connector_destroy,
+ .detect = vd_connector_detect,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+ .reset = drm_atomic_helper_connector_reset,
+};
+
+static int vd_bind(struct device *dev, struct device *master, void *data)
+{
+ struct drm_device *drm_dev = data;
+ struct es_virtual_display *vd = dev_get_drvdata(dev);
+ struct drm_encoder *encoder;
+ struct drm_connector *connector;
+ struct device_node *ep, *np;
+ struct platform_device *pdev;
+ int ret;
+
+ /* Encoder */
+ encoder = &vd->encoder;
+ ret = drm_encoder_init(drm_dev, encoder, &vd_encoder_funcs,
+ DRM_MODE_ENCODER_VIRTUAL, NULL);
+ if (ret) {
+ return ret;
+ }
+
+ encoder->encoder_type = DRM_MODE_ENCODER_VIRTUAL;
+ drm_encoder_helper_add(encoder, &vd_encoder_helper_funcs);
+
+ encoder->possible_crtcs =
+ drm_of_find_possible_crtcs(drm_dev, dev->of_node);
+
+ /* Connector */
+ connector = &vd->connector;
+ ret = drm_connector_init(drm_dev, connector, &vd_connector_funcs,
+ DRM_MODE_CONNECTOR_VIRTUAL);
+ if (ret)
+ goto connector_init_err;
+ drm_connector_helper_add(connector, &vd_connector_helper_funcs);
+ connector->interlace_allowed = false;
+ connector->doublescan_allowed = false;
+ connector->dpms = DRM_MODE_DPMS_OFF;
+ connector->polled =
+ DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
+ ret = drm_connector_register(connector);
+ if (ret)
+ goto connector_reg_err;
+
+ drm_display_info_set_bus_formats(&connector->display_info,
+ &vd->bus_format, 1);
+
+ /* attach */
+ ret = drm_connector_attach_encoder(connector, encoder);
+ if (ret)
+ goto attach_err;
+
+ ep = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1);
+ if (!ep) {
+ ret = -EINVAL;
+ goto attach_err;
+ }
+
+ np = of_graph_get_remote_port_parent(ep);
+ of_node_put(ep);
+ if (!np) {
+ ret = -EINVAL;
+ goto attach_err;
+ }
+
+ pdev = of_find_device_by_node(np);
+ of_node_put(np);
+ if (!pdev) {
+ ret = -EPROBE_DEFER;
+ goto attach_err;
+ }
+ get_device(&pdev->dev);
+ vd->dc = &pdev->dev;
+
+ return 0;
+
+attach_err:
+ drm_connector_unregister(connector);
+connector_reg_err:
+ drm_connector_cleanup(connector);
+connector_init_err:
+ drm_encoder_cleanup(encoder);
+ return ret;
+}
+
+static void vd_unbind(struct device *dev, struct device *master, void *data)
+{
+ struct es_virtual_display *vd = dev_get_drvdata(dev);
+
+ drm_connector_unregister(&vd->connector);
+ drm_connector_cleanup(&vd->connector);
+ drm_encoder_cleanup(&vd->encoder);
+ if (vd->dump_obj) {
+ drm_gem_object_put(&vd->dump_obj->base);
+ vd->dump_obj = NULL;
+ }
+}
+
+const struct component_ops vd_component_ops = {
+ .bind = vd_bind,
+ .unbind = vd_unbind,
+};
+
+static const struct of_device_id vd_driver_dt_match[] = {
+ {
+ .compatible = "eswin,virtual_display",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, vd_driver_dt_match);
+
+static int vd_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct es_virtual_display *vd;
+ unsigned char bpp;
+
+ vd = devm_kzalloc(dev, sizeof(*vd), GFP_KERNEL);
+ if (!vd)
+ return -ENOMEM;
+
+ vd->bus_format = MEDIA_BUS_FMT_RGB101010_1X30;
+ of_property_read_u8(dev->of_node, "bpp", &bpp);
+ if (bpp == 8)
+ vd->bus_format = MEDIA_BUS_FMT_RBG888_1X24;
+
+ dev_set_drvdata(dev, vd);
+
+ return component_add(dev, &vd_component_ops);
+}
+
+static int vd_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ component_del(dev, &vd_component_ops);
+
+ dev_set_drvdata(dev, NULL);
+
+ return 0;
+}
+
+struct platform_driver virtual_display_platform_driver = {
+ .probe = vd_probe,
+ .remove = vd_remove,
+ .driver = {
+ .name = "es-virtual-display",
+ .of_match_table = of_match_ptr(vd_driver_dt_match),
+ },
+};
+
+MODULE_DESCRIPTION("Eswin Virtual Display Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/eswin/es_virtual.h b/drivers/gpu/drm/eswin/es_virtual.h
new file mode 100644
index 000000000000..16371232d605
--- /dev/null
+++ b/drivers/gpu/drm/eswin/es_virtual.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Eswin Holdings Co., Ltd.
+ */
+
+#ifndef __ES_VIRTUAL_H_
+#define __ES_VIRTUAL_H_
+
+struct es_virtual_display {
+ struct drm_encoder encoder;
+ struct drm_connector connector;
+ struct device *dc;
+ u32 bus_format;
+
+ struct dentry *dump_debugfs;
+ struct debugfs_blob_wrapper dump_blob;
+ struct es_gem_object *dump_obj;
+ unsigned int pitch;
+};
+
+static inline struct es_virtual_display *
+to_virtual_display_with_connector(struct drm_connector *connector)
+{
+ return container_of(connector, struct es_virtual_display, connector);
+}
+
+static inline struct es_virtual_display *
+to_virtual_display_with_encoder(struct drm_encoder *encoder)
+{
+ return container_of(encoder, struct es_virtual_display, encoder);
+}
+
+extern struct platform_driver virtual_display_platform_driver;
+#endif /* __ES_VIRTUAL_H_ */
diff --git a/drivers/gpu/drm/eswin/eswin_dw_hdmi.c b/drivers/gpu/drm/eswin/eswin_dw_hdmi.c
new file mode 100644
index 000000000000..330837047580
--- /dev/null
+++ b/drivers/gpu/drm/eswin/eswin_dw_hdmi.c
@@ -0,0 +1,1129 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2021, ESWIN Electronics Co., Ltd
+ */
+
+#include <linux/component.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+
+#include <drm/drm_mode_config.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+
+#include <uapi/drm/drm_mode.h>
+#include <uapi/linux/videodev2.h>
+#include <linux/pm_runtime.h>
+
+#include "dw_hdmi.h"
+#include "es_drv.h"
+#include "es_crtc.h"
+
+#define HIWORD_UPDATE(val, mask) (val | (mask) << 16)
+#define ESWIN_HDMI_COLORIMETRY_BT2020 \
+ (HDMI_COLORIMETRY_EXTENDED + HDMI_EXTENDED_COLORIMETRY_BT2020)
+
+/* HDMI output pixel format */
+enum drm_hdmi_output_type {
+ DRM_HDMI_OUTPUT_DEFAULT_RGB, /* default RGB */
+ DRM_HDMI_OUTPUT_YCBCR444, /* YCBCR 444 */
+ DRM_HDMI_OUTPUT_YCBCR422, /* YCBCR 422 */
+ DRM_HDMI_OUTPUT_YCBCR420, /* YCBCR 420 */
+ DRM_HDMI_OUTPUT_YCBCR_HQ, /* Highest subsampled YUV */
+ DRM_HDMI_OUTPUT_YCBCR_LQ, /* Lowest subsampled YUV */
+ DRM_HDMI_OUTPUT_INVALID, /* Guess what ? */
+};
+
+enum dw_hdmi_eswin_color_depth {
+ ESWIN_HDMI_DEPTH_8,
+ ESWIN_HDMI_DEPTH_10,
+ ESWIN_HDMI_DEPTH_12,
+ ESWIN_HDMI_DEPTH_16,
+ ESWIN_HDMI_DEPTH_420_10,
+ ESWIN_HDMI_DEPTH_420_12,
+ ESWIN_HDMI_DEPTH_420_16
+};
+
+struct eswin_hdmi {
+ struct device *dev;
+ struct regmap *regmap;
+ struct drm_encoder encoder;
+ struct clk *vpll_clk;
+ struct clk *hclk_vio;
+ struct clk *dclk;
+ struct dw_hdmi *hdmi;
+ struct phy *phy;
+ u8 id;
+ unsigned long bus_format;
+ unsigned long output_bus_format;
+ unsigned long enc_out_encoding;
+
+ struct drm_property *color_depth_property;
+ struct drm_property *output_format_property;
+ struct drm_property *colorimetry_property;
+ struct drm_property *video_enable_property;
+
+ struct drm_property *color_depth_capacity;
+ struct drm_property *output_format_capacity;
+ struct drm_property *is_hdmi_capacity;
+ struct drm_property *width_heigth_capacity;
+ struct drm_property *quant_range_select_capacity;
+ struct drm_property *max_tmds_clock_capacity;
+
+ unsigned int colordepth;
+ unsigned int colorimetry;
+ unsigned int phy_bus_width;
+ unsigned int hdmi_quant_range;
+ enum drm_hdmi_output_type hdmi_output;
+ bool video_enable;
+};
+
+#define to_eswin_hdmi(x) container_of(x, struct eswin_hdmi, x)
+
+static const struct dw_hdmi_mpll_config eswin_mpll_cfg[] = {
+ {
+ 27000000,
+ {
+ { 0x0003, 0x0628 },
+ { 0x1003, 0x0632 },
+ { 0x2003, 0x023c },
+ },
+ },
+ {
+ 54000000,
+ {
+ { 0x0002, 0x0614 },
+ { 0x1002, 0x0619 },
+ { 0x2002, 0x021e },
+ },
+ },
+ {
+ 74250000,
+ {
+ { 0x0002, 0x0214 },
+ { 0x1009, 0x0619 },
+ { 0x2001, 0x060f },
+ },
+ },
+ {
+ 108000000,
+ {
+ { 0x0001, 0x060a },
+ { 0x1009, 0x0619 },
+ { 0x2001, 0x020f },
+ },
+ },
+ {
+ 148500000,
+ {
+ { 0x0001, 0x020a },
+ { 0x1018, 0x0619 },
+ { 0x2008, 0x060f },
+ },
+ },
+ {
+ 297000000,
+ {
+ { 0x0000, 0x0205 },
+ { 0x1658, 0x0219 },
+ { 0x2648, 0x020f },
+ },
+ },
+ {
+ 594000000,
+ {
+ { 0x0640, 0x0005 },
+ { 0x1658, 0x0019 },
+ { 0x2648, 0x000f },
+ },
+ },
+ {
+ ~0UL,
+ {
+ { 0x0000, 0x0000 },
+ { 0x0000, 0x0000 },
+ { 0x0000, 0x0000 },
+ },
+ }
+};
+
+static const struct dw_hdmi_curr_ctrl eswin_cur_ctr[] = {
+ /* pixelclk bpp8 bpp10 bpp12 */
+ {
+ 27000000,
+ { 0x0283, 0x0281, 0x02c2 },
+ },
+ {
+ 54000000,
+ { 0x1183, 0x1203, 0x1202 },
+ },
+ {
+ 74250000,
+ { 0x1142, 0x2203, 0x2141 },
+ },
+ {
+ 108000000,
+ { 0x20c0, 0x2203, 0x2100 },
+ },
+ {
+ 148500000,
+ { 0x2080, 0x3203, 0x3141 },
+ },
+ {
+ 297000000,
+ { 0x3041, 0x3182, 0x3100 },
+ },
+ {
+ 594000000,
+ { 0x3080, 0x31c0, 0x3100 },
+ },
+ {
+ ~0UL,
+ { 0x0000, 0x0000, 0x0000 },
+ }
+};
+
+static struct dw_hdmi_phy_config eswin_phy_config[] = {
+ /*pixelclk symbol term vlev*/
+ { 165000000, 0x8088, 0x0007, 0x0180 },
+ { 297000000, 0x80c8, 0x0004, 0x0180 },
+ { 594000000, 0x80f8, 0x0000, 0x0180 },
+ { ~0UL, 0x0000, 0x0000, 0x0000 }
+};
+
+static int eswin_hdmi_update_phy_table(struct eswin_hdmi *hdmi, u32 *config,
+ int phy_table_size)
+{
+ int i;
+
+ if (phy_table_size > ARRAY_SIZE(eswin_phy_config)) {
+ dev_err(hdmi->dev, "phy table array number is out of range\n");
+ return -E2BIG;
+ }
+
+ for (i = 0; i < phy_table_size; i++) {
+ if (config[i * 4] != 0)
+ eswin_phy_config[i].mpixelclock = (u64)config[i * 4];
+ else
+ eswin_phy_config[i].mpixelclock = ~0UL;
+ eswin_phy_config[i].sym_ctr = (u16)config[i * 4 + 1];
+ eswin_phy_config[i].term = (u16)config[i * 4 + 2];
+ eswin_phy_config[i].vlev_ctr = (u16)config[i * 4 + 3];
+ }
+
+ return 0;
+}
+
+static int eswin_hdmi_parse_dt(struct eswin_hdmi *hdmi)
+{
+ struct device_node *np = hdmi->dev->of_node;
+ int ret, val, phy_table_size;
+ u32 *phy_config;
+
+ hdmi->vpll_clk = devm_clk_get(hdmi->dev, "vpll");
+ if (PTR_ERR(hdmi->vpll_clk) == -ENOENT) {
+ hdmi->vpll_clk = NULL;
+ } else if (PTR_ERR(hdmi->vpll_clk) == -EPROBE_DEFER) {
+ return -EPROBE_DEFER;
+ } else if (IS_ERR(hdmi->vpll_clk)) {
+ DRM_DEV_ERROR(hdmi->dev, "failed to get vpll clock\n");
+ return PTR_ERR(hdmi->vpll_clk);
+ }
+
+ hdmi->hclk_vio = devm_clk_get(hdmi->dev, "hclk_vio");
+ if (PTR_ERR(hdmi->hclk_vio) == -ENOENT) {
+ hdmi->hclk_vio = NULL;
+ } else if (PTR_ERR(hdmi->hclk_vio) == -EPROBE_DEFER) {
+ return -EPROBE_DEFER;
+ } else if (IS_ERR(hdmi->hclk_vio)) {
+ dev_err(hdmi->dev, "failed to get hclk_vio clock\n");
+ return PTR_ERR(hdmi->hclk_vio);
+ }
+ hdmi->dclk = devm_clk_get(hdmi->dev, "dclk");
+ if (PTR_ERR(hdmi->dclk) == -ENOENT) {
+ hdmi->dclk = NULL;
+ } else if (PTR_ERR(hdmi->dclk) == -EPROBE_DEFER) {
+ return -EPROBE_DEFER;
+ } else if (IS_ERR(hdmi->dclk)) {
+ dev_err(hdmi->dev, "failed to get dclk\n");
+ return PTR_ERR(hdmi->dclk);
+ }
+
+ ret = clk_prepare_enable(hdmi->vpll_clk);
+ if (ret) {
+ dev_err(hdmi->dev, "Failed to enable HDMI vpll: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(hdmi->hclk_vio);
+ if (ret) {
+ dev_err(hdmi->dev, "Failed to eanble HDMI hclk_vio: %d\n", ret);
+ return ret;
+ }
+
+ if (of_get_property(np, "eswin,phy-table", &val)) {
+ phy_config = kmalloc(val, GFP_KERNEL);
+ if (!phy_config) {
+ /* use default table when kmalloc failed. */
+ dev_err(hdmi->dev, "kmalloc phy table failed\n");
+
+ return -ENOMEM;
+ }
+ phy_table_size = val / 16;
+ of_property_read_u32_array(np, "eswin,phy-table", phy_config,
+ val / sizeof(u32));
+ ret = eswin_hdmi_update_phy_table(hdmi, phy_config,
+ phy_table_size);
+ if (ret) {
+ kfree(phy_config);
+ return ret;
+ }
+ kfree(phy_config);
+ } else {
+ dev_dbg(hdmi->dev, "use default hdmi phy table\n");
+ }
+
+ return 0;
+}
+
+static enum drm_mode_status
+dw_hdmi_eswin_mode_valid(struct dw_hdmi *hdmi, void *data,
+ const struct drm_display_info *info,
+ const struct drm_display_mode *mode)
+{
+ const struct dw_hdmi_mpll_config *mpll_cfg = eswin_mpll_cfg;
+ int pclk = mode->clock * 1000;
+ bool valid = false;
+ int i;
+
+ for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) {
+ if (pclk == mpll_cfg[i].mpixelclock) {
+ valid = true;
+ break;
+ }
+ }
+
+ return (valid) ? MODE_OK : MODE_BAD;
+}
+
+static void dw_hdmi_eswin_encoder_disable(struct drm_encoder *encoder)
+{
+ struct eswin_hdmi *hdmi = to_eswin_hdmi(encoder);
+
+ /*
+ * when plug out hdmi it will be switch cvbs and then phy bus width
+ * must be set as 8
+ */
+ if (hdmi->phy)
+ phy_set_bus_width(hdmi->phy, 8);
+ clk_disable_unprepare(hdmi->dclk);
+}
+
+static bool
+dw_hdmi_eswin_encoder_mode_fixup(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adj_mode)
+{
+ return true;
+}
+
+static void dw_hdmi_eswin_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adj_mode)
+{
+ struct eswin_hdmi *hdmi = to_eswin_hdmi(encoder);
+
+ clk_set_rate(hdmi->vpll_clk, adj_mode->clock * 1000);
+}
+
+static void dw_hdmi_eswin_encoder_enable(struct drm_encoder *encoder)
+{
+ struct eswin_hdmi *hdmi = to_eswin_hdmi(encoder);
+ struct drm_crtc *crtc = encoder->crtc;
+
+ if (WARN_ON(!crtc || !crtc->state))
+ return;
+
+ if (hdmi->phy)
+ phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width);
+
+ clk_set_rate(hdmi->vpll_clk,
+ crtc->state->adjusted_mode.crtc_clock * 1000);
+
+ clk_set_rate(hdmi->dclk, crtc->state->adjusted_mode.crtc_clock * 1000);
+ clk_prepare_enable(hdmi->dclk);
+
+ DRM_DEV_DEBUG(hdmi->dev, "dc output to hdmi\n");
+}
+
+static void dw_hdmi_eswin_select_output(struct drm_connector_state *conn_state,
+ struct drm_crtc_state *crtc_state,
+ struct eswin_hdmi *hdmi,
+ unsigned int *color_format,
+ unsigned int *color_depth,
+ unsigned long *enc_out_encoding,
+ unsigned int *eotf)
+{
+ struct drm_display_info *info = &conn_state->connector->display_info;
+ struct drm_display_mode *mode = &crtc_state->mode;
+ struct hdr_output_metadata *hdr_metadata;
+ u32 vic = drm_match_cea_mode(mode);
+ unsigned long tmdsclock, pixclock = mode->crtc_clock;
+ bool support_dc = false;
+ int max_tmds_clock = info->max_tmds_clock;
+ int output_eotf;
+
+ *color_format = DRM_HDMI_OUTPUT_DEFAULT_RGB;
+
+ switch (hdmi->hdmi_output) {
+ case DRM_HDMI_OUTPUT_YCBCR_HQ:
+ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+ *color_format = DRM_HDMI_OUTPUT_YCBCR444;
+ else if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+ *color_format = DRM_HDMI_OUTPUT_YCBCR422;
+ else if (conn_state->connector->ycbcr_420_allowed &&
+ drm_mode_is_420(info, mode))
+ *color_format = DRM_HDMI_OUTPUT_YCBCR420;
+ break;
+ case DRM_HDMI_OUTPUT_YCBCR_LQ:
+ if (conn_state->connector->ycbcr_420_allowed &&
+ drm_mode_is_420(info, mode))
+ *color_format = DRM_HDMI_OUTPUT_YCBCR420;
+ else if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+ *color_format = DRM_HDMI_OUTPUT_YCBCR422;
+ else if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+ *color_format = DRM_HDMI_OUTPUT_YCBCR444;
+ break;
+ case DRM_HDMI_OUTPUT_YCBCR420:
+ if (conn_state->connector->ycbcr_420_allowed &&
+ drm_mode_is_420(info, mode))
+ *color_format = DRM_HDMI_OUTPUT_YCBCR420;
+ break;
+ case DRM_HDMI_OUTPUT_YCBCR422:
+ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+ *color_format = DRM_HDMI_OUTPUT_YCBCR422;
+ break;
+ case DRM_HDMI_OUTPUT_YCBCR444:
+ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+ *color_format = DRM_HDMI_OUTPUT_YCBCR444;
+ break;
+ case DRM_HDMI_OUTPUT_DEFAULT_RGB:
+ default:
+ break;
+ }
+
+ if (*color_format == DRM_HDMI_OUTPUT_DEFAULT_RGB &&
+ info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_30)
+ support_dc = true;
+ if (*color_format == DRM_HDMI_OUTPUT_YCBCR444 &&
+ info->edid_hdmi_dc_modes &
+ (DRM_EDID_HDMI_DC_Y444 | DRM_EDID_HDMI_DC_30))
+ support_dc = true;
+ if (*color_format == DRM_HDMI_OUTPUT_YCBCR422)
+ support_dc = true;
+ if (*color_format == DRM_HDMI_OUTPUT_YCBCR420 &&
+ info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30)
+ support_dc = true;
+
+ if (hdmi->colordepth > 8 && support_dc)
+ *color_depth = 10;
+ else
+ *color_depth = 8;
+
+ *eotf = TRADITIONAL_GAMMA_SDR;
+ if (conn_state->hdr_output_metadata) {
+ hdr_metadata = (struct hdr_output_metadata *)
+ conn_state->hdr_output_metadata->data;
+ output_eotf = hdr_metadata->hdmi_metadata_type1.eotf;
+ if (output_eotf > TRADITIONAL_GAMMA_HDR &&
+ output_eotf < FUTURE_EOTF)
+ *eotf = output_eotf;
+ }
+
+ if ((*eotf > TRADITIONAL_GAMMA_HDR &&
+ conn_state->connector->hdr_sink_metadata.hdmi_type1.eotf &
+ BIT(*eotf)) ||
+ (hdmi->colorimetry == ESWIN_HDMI_COLORIMETRY_BT2020))
+ *enc_out_encoding = V4L2_YCBCR_ENC_BT2020;
+ else if ((vic == 6) || (vic == 7) || (vic == 21) || (vic == 22) ||
+ (vic == 2) || (vic == 3) || (vic == 17) || (vic == 18))
+ *enc_out_encoding = V4L2_YCBCR_ENC_601;
+ else
+ *enc_out_encoding = V4L2_YCBCR_ENC_709;
+
+ if (*enc_out_encoding == V4L2_YCBCR_ENC_BT2020) {
+ /* BT2020 require color depth at lest 10bit */
+ *color_depth = 10;
+ /* We prefer use YCbCr422 to send 10bit */
+ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+ *color_format = DRM_HDMI_OUTPUT_YCBCR422;
+ }
+
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+ pixclock *= 2;
+ if ((mode->flags & DRM_MODE_FLAG_3D_MASK) ==
+ DRM_MODE_FLAG_3D_FRAME_PACKING)
+ pixclock *= 2;
+
+ if (*color_format == DRM_HDMI_OUTPUT_YCBCR422 || *color_depth == 8)
+ tmdsclock = pixclock;
+ else
+ tmdsclock = pixclock * (*color_depth) / 8;
+
+ if (*color_format == DRM_HDMI_OUTPUT_YCBCR420)
+ tmdsclock /= 2;
+
+ /* XXX: max_tmds_clock of some sink is 0, we think it is 340MHz. */
+ if (!max_tmds_clock)
+ max_tmds_clock = 340000;
+
+ max_tmds_clock = min(max_tmds_clock, 594000);
+
+ if (tmdsclock > max_tmds_clock) {
+ if (max_tmds_clock >= 594000) {
+ *color_depth = 8;
+ } else if (max_tmds_clock > 340000) {
+ if (drm_mode_is_420(info, mode) || tmdsclock >= 594000)
+ *color_format = DRM_HDMI_OUTPUT_YCBCR420;
+ } else {
+ *color_depth = 8;
+ if (drm_mode_is_420(info, mode) || tmdsclock >= 594000)
+ *color_format = DRM_HDMI_OUTPUT_YCBCR420;
+ }
+ }
+}
+
+static int
+dw_hdmi_eswin_encoder_atomic_check(struct drm_encoder *encoder,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ struct es_crtc_state *s = to_es_crtc_state(crtc_state);
+ struct eswin_hdmi *hdmi = to_eswin_hdmi(encoder);
+ unsigned int colordepth, colorformat, bus_width, eotf;
+
+ dw_hdmi_eswin_select_output(conn_state, crtc_state, hdmi, &colorformat,
+ &colordepth, &hdmi->enc_out_encoding,
+ &eotf);
+ if (colordepth > 8)
+ hdmi->bus_format = MEDIA_BUS_FMT_RGB101010_1X30;
+ else
+ hdmi->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+
+ /* DC does not support YUV output */
+ s->output_fmt = hdmi->bus_format;
+
+ if (colorformat == DRM_HDMI_OUTPUT_YCBCR420) {
+ if (colordepth > 8)
+ hdmi->output_bus_format = MEDIA_BUS_FMT_UYYVYY10_0_5X30;
+ else
+ hdmi->output_bus_format = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
+
+ bus_width = colordepth / 2;
+ } else {
+ if ((colordepth > 8) &&
+ (colorformat != DRM_HDMI_OUTPUT_YCBCR422)) {
+ if (colorformat != DRM_HDMI_OUTPUT_DEFAULT_RGB)
+ hdmi->output_bus_format =
+ MEDIA_BUS_FMT_YUV10_1X30;
+ else
+ hdmi->output_bus_format =
+ MEDIA_BUS_FMT_RGB101010_1X30;
+ } else {
+ if (colorformat != DRM_HDMI_OUTPUT_DEFAULT_RGB)
+ hdmi->output_bus_format =
+ MEDIA_BUS_FMT_YUV8_1X24;
+ else
+ hdmi->output_bus_format =
+ MEDIA_BUS_FMT_RGB888_1X24;
+ }
+ if (colorformat == DRM_HDMI_OUTPUT_YCBCR422) {
+ bus_width = 8;
+ if (colordepth > 8)
+ hdmi->output_bus_format =
+ MEDIA_BUS_FMT_UYVY10_1X20;
+ else
+ hdmi->output_bus_format =
+ MEDIA_BUS_FMT_UYVY8_1X16;
+ } else {
+ bus_width = colordepth;
+ }
+ }
+
+ hdmi->phy_bus_width = bus_width;
+ if (hdmi->phy)
+ phy_set_bus_width(hdmi->phy, bus_width);
+
+ s->encoder_type = DRM_MODE_ENCODER_TMDS;
+
+ return 0;
+}
+
+static unsigned long dw_hdmi_eswin_get_input_bus_format(void *data)
+{
+ struct eswin_hdmi *hdmi = (struct eswin_hdmi *)data;
+
+ return hdmi->bus_format;
+}
+
+static unsigned long dw_hdmi_eswin_get_output_bus_format(void *data)
+{
+ struct eswin_hdmi *hdmi = (struct eswin_hdmi *)data;
+
+ return hdmi->output_bus_format;
+}
+
+static unsigned long dw_hdmi_eswin_get_enc_in_encoding(void *data)
+{
+ struct eswin_hdmi *hdmi = (struct eswin_hdmi *)data;
+
+ return hdmi->enc_out_encoding;
+}
+
+static unsigned long dw_hdmi_eswin_get_enc_out_encoding(void *data)
+{
+ struct eswin_hdmi *hdmi = (struct eswin_hdmi *)data;
+
+ return hdmi->enc_out_encoding;
+}
+
+static const struct drm_prop_enum_list color_depth_enum_list[] = {
+ { 0, "Automatic" }, /* Same as 24bit */
+ { 8, "24bit" },
+ { 10, "30bit" },
+};
+
+static const struct drm_prop_enum_list drm_hdmi_output_enum_list[] = {
+ { DRM_HDMI_OUTPUT_DEFAULT_RGB, "output_rgb" },
+ { DRM_HDMI_OUTPUT_YCBCR444, "output_ycbcr444" },
+ { DRM_HDMI_OUTPUT_YCBCR422, "output_ycbcr422" },
+ { DRM_HDMI_OUTPUT_YCBCR420, "output_ycbcr420" },
+ { DRM_HDMI_OUTPUT_YCBCR_HQ, "output_ycbcr_high_subsampling" },
+ { DRM_HDMI_OUTPUT_YCBCR_LQ, "output_ycbcr_low_subsampling" },
+ { DRM_HDMI_OUTPUT_INVALID, "invalid_output" },
+};
+
+static const struct drm_prop_enum_list colorimetry_enum_list[] = {
+ { HDMI_COLORIMETRY_NONE, "None" },
+ { ESWIN_HDMI_COLORIMETRY_BT2020, "ITU_2020" },
+};
+
+static const struct drm_prop_enum_list quant_range_enum_list[] = {
+ { HDMI_QUANTIZATION_RANGE_DEFAULT, "default" },
+ { HDMI_QUANTIZATION_RANGE_LIMITED, "limit" },
+ { HDMI_QUANTIZATION_RANGE_FULL, "full" },
+};
+
+static const struct drm_prop_enum_list color_depth_capacity_list[] = {
+ { BIT(ESWIN_HDMI_DEPTH_8), "8bit" },
+ { BIT(ESWIN_HDMI_DEPTH_10), "10bit" },
+ { BIT(ESWIN_HDMI_DEPTH_12), "12bit" },
+ { BIT(ESWIN_HDMI_DEPTH_16), "16bit" },
+ { BIT(ESWIN_HDMI_DEPTH_420_10), "yuv420_10bit" },
+ { BIT(ESWIN_HDMI_DEPTH_420_12), "yuv420_12bit" },
+ { BIT(ESWIN_HDMI_DEPTH_420_16), "yuv420_16bit" },
+};
+
+static const struct drm_prop_enum_list output_format_capacity_list[] = {
+ { BIT(DRM_HDMI_OUTPUT_DEFAULT_RGB), "rgb" },
+ { BIT(DRM_HDMI_OUTPUT_YCBCR444), "yuv444" },
+ { BIT(DRM_HDMI_OUTPUT_YCBCR422), "yuv422" },
+ { BIT(DRM_HDMI_OUTPUT_YCBCR420), "yuv420" },
+ { BIT(DRM_HDMI_OUTPUT_YCBCR_HQ), "yuv_hq" },
+ { BIT(DRM_HDMI_OUTPUT_YCBCR_LQ), "yuv_lq" },
+};
+
+static void dw_hdmi_eswin_attatch_properties(struct drm_connector *connector,
+ unsigned int color, int version,
+ void *data)
+{
+ struct eswin_hdmi *hdmi = (struct eswin_hdmi *)data;
+ struct drm_property *prop;
+#ifdef CONFIG_ESWIN_DW_HDMI
+ struct es_drm_private *private = connector->dev->dev_private;
+#endif
+ switch (color) {
+ case MEDIA_BUS_FMT_RGB101010_1X30:
+ hdmi->hdmi_output = DRM_HDMI_OUTPUT_DEFAULT_RGB;
+ hdmi->colordepth = 10;
+ break;
+ case MEDIA_BUS_FMT_YUV8_1X24:
+ hdmi->hdmi_output = DRM_HDMI_OUTPUT_YCBCR444;
+ hdmi->colordepth = 8;
+ break;
+ case MEDIA_BUS_FMT_YUV10_1X30:
+ hdmi->hdmi_output = DRM_HDMI_OUTPUT_YCBCR444;
+ hdmi->colordepth = 10;
+ break;
+ case MEDIA_BUS_FMT_UYVY10_1X20:
+ hdmi->hdmi_output = DRM_HDMI_OUTPUT_YCBCR422;
+ hdmi->colordepth = 10;
+ break;
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ hdmi->hdmi_output = DRM_HDMI_OUTPUT_YCBCR422;
+ hdmi->colordepth = 8;
+ break;
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+ hdmi->hdmi_output = DRM_HDMI_OUTPUT_YCBCR420;
+ hdmi->colordepth = 8;
+ break;
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+ hdmi->hdmi_output = DRM_HDMI_OUTPUT_YCBCR420;
+ hdmi->colordepth = 10;
+ break;
+ default:
+ hdmi->hdmi_output = DRM_HDMI_OUTPUT_DEFAULT_RGB;
+ hdmi->colordepth = 8;
+ }
+
+ if (!hdmi->color_depth_property) {
+ prop = drm_property_create_enum(
+ connector->dev, 0, "hdmi_output_color_depth",
+ color_depth_enum_list,
+ ARRAY_SIZE(color_depth_enum_list));
+ if (prop) {
+ hdmi->color_depth_property = prop;
+ drm_object_attach_property(&connector->base, prop, 0);
+ }
+ }
+
+ prop = drm_property_create_enum(connector->dev, 0, "hdmi_output_format",
+ drm_hdmi_output_enum_list,
+ ARRAY_SIZE(drm_hdmi_output_enum_list));
+ if (prop) {
+ hdmi->output_format_property = prop;
+ drm_object_attach_property(&connector->base, prop, 0);
+ }
+
+ prop = drm_property_create_enum(connector->dev, 0,
+ "hdmi_output_colorimetry",
+ colorimetry_enum_list,
+ ARRAY_SIZE(colorimetry_enum_list));
+ if (prop) {
+ hdmi->colorimetry_property = prop;
+ drm_object_attach_property(&connector->base, prop, 0);
+ }
+
+ prop = drm_property_create_bool(connector->dev, 0, "video_enable");
+ if (prop) {
+ hdmi->video_enable_property = prop;
+ drm_object_attach_property(&connector->base, prop, 1);
+ hdmi->video_enable = true;
+ }
+
+ prop = drm_property_create_enum(connector->dev, 0,
+ "hdmi_color_depth_capacity",
+ color_depth_capacity_list,
+ ARRAY_SIZE(color_depth_capacity_list));
+ if (prop) {
+ hdmi->color_depth_capacity = prop;
+ drm_object_attach_property(&connector->base, prop, 0);
+ }
+
+ prop = drm_property_create_enum(
+ connector->dev, 0, "hdmi_output_format_capacity",
+ output_format_capacity_list,
+ ARRAY_SIZE(output_format_capacity_list));
+ if (prop) {
+ hdmi->output_format_capacity = prop;
+ drm_object_attach_property(&connector->base, prop, 0);
+ }
+
+ prop = drm_property_create_bool(connector->dev, 0, "is_hdmi_capacity");
+ if (prop) {
+ hdmi->is_hdmi_capacity = prop;
+ drm_object_attach_property(&connector->base, prop, 0);
+ }
+
+ prop = drm_property_create_range(
+ connector->dev, 0, "hdmi_width_height_mm_capacity", 0, 0xff);
+ if (prop) {
+ hdmi->width_heigth_capacity = prop;
+ drm_object_attach_property(&connector->base, prop, 0);
+ }
+
+ prop = drm_property_create_bool(connector->dev, 0,
+ "hdmi_quant_range_sel_capacity");
+ if (prop) {
+ hdmi->quant_range_select_capacity = prop;
+ drm_object_attach_property(&connector->base, prop, 0);
+ }
+
+ prop = drm_property_create_range(
+ connector->dev, 0, "hdmi_max_tmds_clock_capacity", 0, 340000);
+ if (prop) {
+ hdmi->max_tmds_clock_capacity = prop;
+ drm_object_attach_property(&connector->base, prop, 0);
+ }
+
+ prop = connector->dev->mode_config.hdr_output_metadata_property;
+ if (version >= 0x211a)
+ drm_object_attach_property(&connector->base, prop, 0);
+
+#ifdef CONFIG_ESWIN_DW_HDMI
+ drm_object_attach_property(&connector->base, private->connector_id_prop,
+ 0);
+#endif
+}
+
+static void dw_hdmi_eswin_destroy_properties(struct drm_connector *connector,
+ void *data)
+{
+ struct eswin_hdmi *hdmi = (struct eswin_hdmi *)data;
+
+ if (hdmi->color_depth_property) {
+ drm_property_destroy(connector->dev,
+ hdmi->color_depth_property);
+ hdmi->color_depth_property = NULL;
+ }
+
+ if (hdmi->output_format_property) {
+ drm_property_destroy(connector->dev,
+ hdmi->output_format_property);
+ hdmi->output_format_property = NULL;
+ }
+
+ if (hdmi->colorimetry_property) {
+ drm_property_destroy(connector->dev,
+ hdmi->colorimetry_property);
+ hdmi->colorimetry_property = NULL;
+ }
+
+ if (hdmi->video_enable_property) {
+ drm_property_destroy(connector->dev,
+ hdmi->video_enable_property);
+ hdmi->video_enable_property = NULL;
+ }
+
+ if (hdmi->color_depth_capacity) {
+ drm_property_destroy(connector->dev,
+ hdmi->color_depth_capacity);
+ hdmi->color_depth_capacity = NULL;
+ }
+
+ if (hdmi->output_format_capacity) {
+ drm_property_destroy(connector->dev,
+ hdmi->output_format_capacity);
+ hdmi->output_format_capacity = NULL;
+ }
+
+ if (hdmi->is_hdmi_capacity) {
+ drm_property_destroy(connector->dev, hdmi->is_hdmi_capacity);
+ hdmi->is_hdmi_capacity = NULL;
+ }
+
+ if (hdmi->width_heigth_capacity) {
+ drm_property_destroy(connector->dev,
+ hdmi->width_heigth_capacity);
+ hdmi->width_heigth_capacity = NULL;
+ }
+
+ if (hdmi->quant_range_select_capacity) {
+ drm_property_destroy(connector->dev,
+ hdmi->quant_range_select_capacity);
+ hdmi->quant_range_select_capacity = NULL;
+ }
+
+ if (hdmi->max_tmds_clock_capacity) {
+ drm_property_destroy(connector->dev,
+ hdmi->max_tmds_clock_capacity);
+ hdmi->max_tmds_clock_capacity = NULL;
+ }
+}
+
+static int dw_hdmi_eswin_set_property(struct drm_connector *connector,
+ struct drm_connector_state *state,
+ struct drm_property *property,
+ uint64_t val, void *data)
+{
+ struct eswin_hdmi *hdmi = (struct eswin_hdmi *)data;
+
+ if (property == hdmi->color_depth_property) {
+ hdmi->colordepth = val;
+ } else if (property == hdmi->output_format_property) {
+ hdmi->hdmi_output = val;
+ } else if (property == hdmi->colorimetry_property) {
+ hdmi->colorimetry = val;
+ } else if (property == hdmi->video_enable_property) {
+ if (hdmi->video_enable != val) {
+ if (val == true) {
+ dw_hdmi_enable_video(hdmi->hdmi);
+ } else {
+ dw_hdmi_disable_video(hdmi->hdmi);
+ }
+ hdmi->video_enable = val;
+ }
+ } else {
+ DRM_ERROR("don't support set %s property\n", property->name);
+ return 0;
+ }
+ return 0;
+}
+
+static int dw_hdmi_eswin_get_property(struct drm_connector *connector,
+ const struct drm_connector_state *state,
+ struct drm_property *property,
+ uint64_t *val, void *data)
+{
+ struct eswin_hdmi *hdmi = (struct eswin_hdmi *)data;
+ struct drm_display_info *info = &connector->display_info;
+ struct drm_mode_config *config = &connector->dev->mode_config;
+#ifdef CONFIG_ESWIN_DW_HDMI
+ struct es_drm_private *private = connector->dev->dev_private;
+#endif
+ if (property == hdmi->color_depth_property) {
+ *val = hdmi->colordepth;
+ } else if (property == hdmi->output_format_property) {
+ *val = hdmi->hdmi_output;
+ } else if (property == hdmi->color_depth_capacity) {
+ *val = BIT(ESWIN_HDMI_DEPTH_8);
+ if (info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_30)
+ *val |= BIT(ESWIN_HDMI_DEPTH_10);
+ if (info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_36)
+ *val |= BIT(ESWIN_HDMI_DEPTH_12);
+ if (info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_48)
+ *val |= BIT(ESWIN_HDMI_DEPTH_16);
+ if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30)
+ *val |= BIT(ESWIN_HDMI_DEPTH_420_10);
+ if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36)
+ *val |= BIT(ESWIN_HDMI_DEPTH_420_12);
+ if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48)
+ *val |= BIT(ESWIN_HDMI_DEPTH_420_16);
+ } else if (property == hdmi->output_format_capacity) {
+ *val = BIT(DRM_HDMI_OUTPUT_DEFAULT_RGB);
+ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+ *val |= BIT(DRM_HDMI_OUTPUT_YCBCR444);
+ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+ *val |= BIT(DRM_HDMI_OUTPUT_YCBCR422);
+ if (connector->ycbcr_420_allowed &&
+ info->color_formats & DRM_COLOR_FORMAT_YCRCB420)
+ *val |= BIT(DRM_HDMI_OUTPUT_YCBCR420);
+ } else if (property == config->hdr_output_metadata_property) {
+ *val = state->hdr_output_metadata ?
+ state->hdr_output_metadata->base.id :
+ 0;
+ } else if (property == hdmi->colorimetry_property) {
+ *val = hdmi->colorimetry;
+ }
+#ifdef CONFIG_ESWIN_DW_HDMI
+ else if (property == private->connector_id_prop) {
+ *val = hdmi->id;
+ }
+#endif
+ else if (property == hdmi->is_hdmi_capacity) {
+ *val = info->is_hdmi;
+ } else if (property == hdmi->quant_range_select_capacity) {
+ *val = info->rgb_quant_range_selectable;
+ } else if (property == hdmi->width_heigth_capacity) {
+ property->values[0] = info->width_mm;
+ property->values[1] = info->height_mm;
+ *val = 0;
+ } else if (property == hdmi->max_tmds_clock_capacity) {
+ *val = info->max_tmds_clock;
+ } else if (property == hdmi->video_enable_property) {
+ *val = hdmi->video_enable;
+ } else {
+ DRM_ERROR("failed to get eswin hdmi connector %s property\n",
+ property->name);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct dw_hdmi_property_ops dw_hdmi_eswin_property_ops = {
+ .attatch_properties = dw_hdmi_eswin_attatch_properties,
+ .destroy_properties = dw_hdmi_eswin_destroy_properties,
+ .set_property = dw_hdmi_eswin_set_property,
+ .get_property = dw_hdmi_eswin_get_property,
+};
+
+static const struct drm_encoder_helper_funcs
+ dw_hdmi_eswin_encoder_helper_funcs = {
+ .mode_fixup = dw_hdmi_eswin_encoder_mode_fixup,
+ .mode_set = dw_hdmi_eswin_encoder_mode_set,
+ .enable = dw_hdmi_eswin_encoder_enable,
+ .disable = dw_hdmi_eswin_encoder_disable,
+ .atomic_check = dw_hdmi_eswin_encoder_atomic_check,
+ };
+
+static const struct dw_hdmi_plat_data win2030_hdmi_drv_data = {
+ .mode_valid = dw_hdmi_eswin_mode_valid,
+ .mpll_cfg = eswin_mpll_cfg,
+ .cur_ctr = eswin_cur_ctr,
+ .phy_config = eswin_phy_config,
+ .use_drm_infoframe = true,
+ .ycbcr_420_allowed = false,
+};
+
+static const struct of_device_id dw_hdmi_eswin_dt_ids[] = {
+ { .compatible = "eswin,eswin-dw-hdmi", .data = &win2030_hdmi_drv_data },
+ {},
+};
+MODULE_DEVICE_TABLE(of, dw_hdmi_eswin_dt_ids);
+
+static int dw_hdmi_eswin_bind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct dw_hdmi_plat_data *plat_data;
+ const struct of_device_id *match;
+ struct drm_device *drm = data;
+ struct drm_encoder *encoder;
+ struct eswin_hdmi *hdmi;
+ int ret;
+
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
+ hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
+ if (!hdmi)
+ return -ENOMEM;
+
+ match = of_match_node(dw_hdmi_eswin_dt_ids, pdev->dev.of_node);
+ plat_data = devm_kmemdup(&pdev->dev, match->data, sizeof(*plat_data),
+ GFP_KERNEL);
+ if (!plat_data)
+ return -ENOMEM;
+
+ hdmi->dev = &pdev->dev;
+
+ plat_data->phy_data = hdmi;
+ encoder = &hdmi->encoder;
+
+ encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
+ /*
+ * If we failed to find the CRTC(s) which this encoder is
+ * supposed to be connected to, it's because the CRTC has
+ * not been registered yet. Defer probing, and hope that
+ * the required CRTC is added later.
+ */
+ if (encoder->possible_crtcs == 0)
+ return -EPROBE_DEFER;
+
+ ret = eswin_hdmi_parse_dt(hdmi);
+ if (ret) {
+ DRM_DEV_ERROR(hdmi->dev, "Unable to parse OF data\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(hdmi->vpll_clk);
+ if (ret) {
+ DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI vpll: %d\n",
+ ret);
+ return ret;
+ }
+
+ plat_data->phy_data = hdmi;
+ plat_data->get_input_bus_format = dw_hdmi_eswin_get_input_bus_format;
+ plat_data->get_output_bus_format = dw_hdmi_eswin_get_output_bus_format;
+ plat_data->get_enc_in_encoding = dw_hdmi_eswin_get_enc_in_encoding;
+ plat_data->get_enc_out_encoding = dw_hdmi_eswin_get_enc_out_encoding;
+ plat_data->property_ops = &dw_hdmi_eswin_property_ops;
+
+ hdmi->phy = devm_phy_optional_get(dev, "hdmi");
+ if (IS_ERR(hdmi->phy)) {
+ ret = PTR_ERR(hdmi->phy);
+ if (ret != -EPROBE_DEFER)
+ DRM_DEV_ERROR(hdmi->dev, "failed to get phy\n");
+ return ret;
+ }
+
+ drm_encoder_helper_add(encoder, &dw_hdmi_eswin_encoder_helper_funcs);
+ drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
+
+ platform_set_drvdata(pdev, hdmi);
+
+ hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data);
+
+ /*
+ * If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(),
+ * which would have called the encoder cleanup. Do it manually.
+ */
+ if (IS_ERR(hdmi->hdmi)) {
+ ret = PTR_ERR(hdmi->hdmi);
+ drm_encoder_cleanup(encoder);
+ clk_disable_unprepare(hdmi->vpll_clk);
+ }
+
+ return ret;
+}
+
+static void dw_hdmi_eswin_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct eswin_hdmi *hdmi = dev_get_drvdata(dev);
+
+ dw_hdmi_unbind(hdmi->hdmi);
+ clk_disable_unprepare(hdmi->vpll_clk);
+}
+
+static const struct component_ops dw_hdmi_eswin_ops = {
+ .bind = dw_hdmi_eswin_bind,
+ .unbind = dw_hdmi_eswin_unbind,
+};
+
+static int dw_hdmi_eswin_probe(struct platform_device *pdev)
+{
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
+ return component_add(&pdev->dev, &dw_hdmi_eswin_ops);
+}
+
+static void dw_hdmi_eswin_shutdown(struct platform_device *pdev)
+{
+ struct eswin_hdmi *hdmi = dev_get_drvdata(&pdev->dev);
+
+ dw_hdmi_suspend(hdmi->hdmi);
+ pm_runtime_put_sync(&pdev->dev);
+}
+
+static int dw_hdmi_eswin_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &dw_hdmi_eswin_ops);
+ pm_runtime_disable(&pdev->dev);
+ return 0;
+}
+
+static int __maybe_unused dw_hdmi_eswin_suspend(struct device *dev)
+{
+ struct eswin_hdmi *hdmi = dev_get_drvdata(dev);
+
+ dw_hdmi_suspend(hdmi->hdmi);
+ pm_runtime_put_sync(dev);
+
+ return 0;
+}
+
+static int __maybe_unused dw_hdmi_eswin_resume(struct device *dev)
+{
+ struct eswin_hdmi *hdmi = dev_get_drvdata(dev);
+
+ dw_hdmi_resume(hdmi->hdmi);
+
+ return 0;
+}
+
+static const struct dev_pm_ops dw_hdmi_eswin_pm = { SET_SYSTEM_SLEEP_PM_OPS(
+ dw_hdmi_eswin_suspend, dw_hdmi_eswin_resume) };
+
+struct platform_driver dw_hdmi_eswin_pltfm_driver = {
+ .probe = dw_hdmi_eswin_probe,
+ .remove = dw_hdmi_eswin_remove,
+ .shutdown = dw_hdmi_eswin_shutdown,
+ .driver = {
+ .name = "dw-hdmi-eswin",
+ .pm = &dw_hdmi_eswin_pm,
+ .of_match_table = dw_hdmi_eswin_dt_ids,
+ },
+};
+
+//module_platform_driver(dw_hdmi_eswin_pltfm_driver);
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/eswin/host_lib_driver_linux_if.h b/drivers/gpu/drm/eswin/host_lib_driver_linux_if.h
new file mode 100644
index 000000000000..0cde34421994
--- /dev/null
+++ b/drivers/gpu/drm/eswin/host_lib_driver_linux_if.h
@@ -0,0 +1,146 @@
+#ifndef _HOST_LIB_DRIVER_LINUX_IF_H_
+#define _HOST_LIB_DRIVER_LINUX_IF_H_
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+#define HL_DRIVER_ALLOCATE_DYNAMIC_MEM 0xffffffff
+// hl_drv_ioctl numbers
+enum {
+ HL_DRV_NR_MIN = 0x10,
+ HL_DRV_NR_INIT,
+ HL_DRV_NR_MEMINFO,
+ HL_DRV_NR_LOAD_CODE,
+ HL_DRV_NR_READ_DATA,
+ HL_DRV_NR_WRITE_DATA,
+ HL_DRV_NR_MEMSET_DATA,
+ HL_DRV_NR_READ_HPI,
+ HL_DRV_NR_WRITE_HPI,
+
+ DW_DRV_NR_CONNECT_STATUS,
+ DW_DRV_NR_CONNECT_SET,
+ DW_DRV_NR_DISCONNECT_SET,
+ DW_DRV_NR_AUTH_SUCCESS,
+ DW_DRV_NR_AUTH_FAIL,
+ DW_DRV_NR_NO_CAPACITY,
+
+ HL_DRV_NR_MAX
+};
+
+/*
+ * HL_DRV_IOC_INIT: associate file descriptor with the indicated memory. This
+ * must be called before any other hl_drv_ioctl on the file descriptor.
+ *
+ * - hpi_base = base address of HPI registers.
+ * - code_base = base address of firmware memory (0 to allocate internally)
+ * - data_base = base address of data memory (0 to allocate internally)
+ * - code_len, data_len = length of firmware and data memory, respectively.
+ */
+#define HL_DRV_IOC_INIT _IOW('H', HL_DRV_NR_INIT, struct hl_drv_ioc_meminfo)
+
+/*
+ * HL_DRV_IOC_MEMINFO: retrieve memory information from file descriptor.
+ *
+ * Fills out the meminfo struct, returning the values passed to HL_DRV_IOC_INIT
+ * except that the actual base addresses of internal allocations (if any) are
+ * reported.
+ */
+#define HL_DRV_IOC_MEMINFO \
+ _IOR('H', HL_DRV_NR_MEMINFO, struct hl_drv_ioc_meminfo)
+
+struct hl_drv_ioc_meminfo {
+ __u32 hpi_base;
+ __u32 code_base;
+ __u32 code_size;
+ __u32 data_base;
+ __u32 data_size;
+};
+
+/*
+ * HL_DRV_IOC_LOAD_CODE: write the provided buffer to the firmware memory.
+ *
+ * - len = number of bytes in data buffer
+ * - data = data to write to firmware memory.
+ *
+ * This can only be done once (successfully). Subsequent attempts will
+ * return -EBUSY.
+ */
+#define HL_DRV_IOC_LOAD_CODE \
+ _IOW('H', HL_DRV_NR_LOAD_CODE, struct hl_drv_ioc_code)
+
+struct hl_drv_ioc_code {
+ __u32 len;
+ __u8 data[];
+};
+
+/*
+ * HL_DRV_IOC_READ_DATA: copy from data memory.
+ * HL_DRV_IOC_WRITE_DATA: copy to data memory.
+ *
+ * - offset = start copying at this byte offset into the data memory.
+ * - len = number of bytes to copy.
+ * - data = for write, buffer containing data to copy.
+ * for read, buffer to which read data will be written.
+ *
+ */
+#define HL_DRV_IOC_READ_DATA \
+ _IOWR('H', HL_DRV_NR_READ_DATA, struct hl_drv_ioc_data)
+#define HL_DRV_IOC_WRITE_DATA \
+ _IOW('H', HL_DRV_NR_WRITE_DATA, struct hl_drv_ioc_data)
+
+/*
+ * HL_DRV_IOC_MEMSET_DATA: initialize data memory.
+ *
+ * - offset = start initializatoin at this byte offset into the data memory.
+ * - len = number of bytes to set.
+ * - data[0] = byte value to write to all indicated memory locations.
+ */
+#define HL_DRV_IOC_MEMSET_DATA \
+ _IOW('H', HL_DRV_NR_MEMSET_DATA, struct hl_drv_ioc_data)
+
+struct hl_drv_ioc_data {
+ __u32 offset;
+ __u32 len;
+ __u8 data[];
+};
+
+/*
+ * HL_DRV_IOC_READ_HPI: read HPI register.
+ * HL_DRV_IOC_WRITE_HPI: write HPI register.
+ *
+ * - offset = byte offset of HPI register to access.
+ * - value = for write, value to write.
+ * for read, location to which result is stored.
+ */
+#define HL_DRV_IOC_READ_HPI \
+ _IOWR('H', HL_DRV_NR_READ_HPI, struct hl_drv_ioc_hpi_reg)
+#define HL_DRV_IOC_WRITE_HPI \
+ _IOW('H', HL_DRV_NR_WRITE_HPI, struct hl_drv_ioc_hpi_reg)
+
+struct hl_drv_ioc_hpi_reg {
+ __u32 offset;
+ __u32 value;
+};
+
+#define DW_DRV_IOC_CONNECT_STATUS \
+ _IOR('H', DW_DRV_NR_CONNECT_STATUS, struct hl_drv_ioc_info)
+#define DW_DRV_IOC_CONNECT_SET \
+ _IOW('H', DW_DRV_NR_CONNECT_SET, struct hl_drv_ioc_info)
+#define DW_DRV_IOC_DISCONNECT_SET \
+ _IOW('H', DW_DRV_NR_DISCONNECT_SET, struct hl_drv_ioc_info)
+#define DW_DRV_IOC_AUTH_SUCCESS \
+ _IOW('H', DW_DRV_NR_AUTH_SUCCESS, struct hl_drv_ioc_info)
+#define DW_DRV_IOC_AUTH_FAIL \
+ _IOW('H', DW_DRV_NR_AUTH_FAIL, struct hl_drv_ioc_info)
+#define DW_DRV_IOC_NO_CAPACITY \
+ _IOW('H', DW_DRV_NR_NO_CAPACITY, struct hl_drv_ioc_info)
+
+struct hl_drv_ioc_info {
+ union {
+ __u32 connect;
+ __u32 auth;
+ __u32 capacity;
+ };
+};
+
+#endif // _HOST_LIB_DRIVER_LINUX_IF_H_
--
2.47.0