18486 lines
504 KiB
Diff
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", ®, &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", ®, &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(®, 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, ®, 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(®, 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
|
|
|