summaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_display.c6
-rw-r--r--drivers/gpu/drm/amd/amdgpu/dce_v10_0.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/dce_v11_0.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/dce_v6_0.c1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/dce_v8_0.c2
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c3
-rw-r--r--drivers/gpu/drm/armada/armada_drv.c3
-rw-r--r--drivers/gpu/drm/aspeed/aspeed_gfx_drv.c3
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c3
-rw-r--r--drivers/gpu/drm/bridge/Kconfig8
-rw-r--r--drivers/gpu/drm/bridge/Makefile1
-rw-r--r--drivers/gpu/drm/bridge/ite-it6505.c3352
-rw-r--r--drivers/gpu/drm/bridge/lontium-lt9611.c6
-rw-r--r--drivers/gpu/drm/bridge/nwl-dsi.c18
-rw-r--r--drivers/gpu/drm/drm_cache.c9
-rw-r--r--drivers/gpu/drm/drm_edid.c3
-rw-r--r--drivers/gpu/drm/drm_fb_helper.c9
-rw-r--r--drivers/gpu/drm/drm_framebuffer.c6
-rw-r--r--drivers/gpu/drm/drm_ioctl.c2
-rw-r--r--drivers/gpu/drm/drm_plane.c23
-rw-r--r--drivers/gpu/drm/drm_privacy_screen.c1
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.c3
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c3
-rw-r--r--drivers/gpu/drm/gma500/psb_drv.c3
-rw-r--r--drivers/gpu/drm/hisilicon/hibmc/Kconfig2
-rw-r--r--drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c3
-rw-r--r--drivers/gpu/drm/hyperv/hyperv_drm_drv.c3
-rw-r--r--drivers/gpu/drm/imx/imx-drm-core.c3
-rw-r--r--drivers/gpu/drm/ingenic/ingenic-drm-drv.c3
-rw-r--r--drivers/gpu/drm/kmb/kmb_drv.c3
-rw-r--r--drivers/gpu/drm/mcde/mcde_drv.c3
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_drv.c3
-rw-r--r--drivers/gpu/drm/meson/meson_drv.c3
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_drv.c20
-rw-r--r--drivers/gpu/drm/msm/msm_drv.c3
-rw-r--r--drivers/gpu/drm/mxsfb/mxsfb_drv.c6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.c6
-rw-r--r--drivers/gpu/drm/omapdrm/omap_drv.c3
-rw-r--r--drivers/gpu/drm/panel/Kconfig1
-rw-r--r--drivers/gpu/drm/qxl/qxl_drv.c31
-rw-r--r--drivers/gpu/drm/qxl/qxl_drv.h13
-rw-r--r--drivers/gpu/drm/qxl/qxl_ioctl.c41
-rw-r--r--drivers/gpu/drm/radeon/radeon_display.c2
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_drv.c3
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_drv.c3
-rw-r--r--drivers/gpu/drm/selftests/test-drm_framebuffer.c1
-rw-r--r--drivers/gpu/drm/shmobile/shmob_drm_drv.c3
-rw-r--r--drivers/gpu/drm/sprd/sprd_drm.c4
-rw-r--r--drivers/gpu/drm/sti/sti_drv.c3
-rw-r--r--drivers/gpu/drm/stm/drv.c3
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_drv.c3
-rw-r--r--drivers/gpu/drm/tegra/drm.c3
-rw-r--r--drivers/gpu/drm/tidss/tidss_drv.c3
-rw-r--r--drivers/gpu/drm/tilcdc/tilcdc_drv.c3
-rw-r--r--drivers/gpu/drm/tiny/arcpgu.c3
-rw-r--r--drivers/gpu/drm/tve200/tve200_drv.c3
-rw-r--r--drivers/gpu/drm/v3d/v3d_gem.c6
-rw-r--r--drivers/gpu/drm/vboxvideo/vbox_drv.c20
-rw-r--r--drivers/gpu/drm/vc4/vc4_hdmi.c21
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.c24
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c2
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fence.c46
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fence.h3
-rw-r--r--drivers/gpu/drm/xen/xen_drm_front.c3
-rw-r--r--drivers/gpu/drm/xlnx/zynqmp_dpsub.c3
65 files changed, 3553 insertions, 237 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
index 3f21a13882a8..e76b96d55551 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
@@ -956,7 +956,7 @@ static int amdgpu_display_verify_sizes(struct amdgpu_framebuffer *rfb)
int ret;
unsigned int i, block_width, block_height, block_size_log2;
- if (!rfb->base.dev->mode_config.allow_fb_modifiers)
+ if (rfb->base.dev->mode_config.fb_modifiers_not_supported)
return 0;
for (i = 0; i < format_info->num_planes; ++i) {
@@ -1143,7 +1143,7 @@ int amdgpu_display_framebuffer_init(struct drm_device *dev,
if (ret)
return ret;
- if (!dev->mode_config.allow_fb_modifiers) {
+ if (dev->mode_config.fb_modifiers_not_supported) {
drm_WARN_ONCE(dev, adev->family >= AMDGPU_FAMILY_AI,
"GFX9+ requires FB check based on format modifier\n");
ret = check_tiling_flags_gfx6(rfb);
@@ -1151,7 +1151,7 @@ int amdgpu_display_framebuffer_init(struct drm_device *dev,
return ret;
}
- if (dev->mode_config.allow_fb_modifiers &&
+ if (!dev->mode_config.fb_modifiers_not_supported &&
!(rfb->base.flags & DRM_MODE_FB_MODIFIERS)) {
ret = convert_tiling_flags_to_modifier(rfb);
if (ret) {
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
index d1570a462a51..fb61c0814115 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
@@ -2798,6 +2798,8 @@ static int dce_v10_0_sw_init(void *handle)
adev_to_drm(adev)->mode_config.preferred_depth = 24;
adev_to_drm(adev)->mode_config.prefer_shadow = 1;
+ adev_to_drm(adev)->mode_config.fb_modifiers_not_supported = true;
+
adev_to_drm(adev)->mode_config.fb_base = adev->gmc.aper_base;
r = amdgpu_display_modeset_create_props(adev);
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
index 18a7b3bd633b..17942a11366d 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
@@ -2916,6 +2916,8 @@ static int dce_v11_0_sw_init(void *handle)
adev_to_drm(adev)->mode_config.preferred_depth = 24;
adev_to_drm(adev)->mode_config.prefer_shadow = 1;
+ adev_to_drm(adev)->mode_config.fb_modifiers_not_supported = true;
+
adev_to_drm(adev)->mode_config.fb_base = adev->gmc.aper_base;
r = amdgpu_display_modeset_create_props(adev);
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c
index c7803dc2b2d5..2ec99ec8e1a3 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c
@@ -2674,6 +2674,7 @@ static int dce_v6_0_sw_init(void *handle)
adev_to_drm(adev)->mode_config.max_height = 16384;
adev_to_drm(adev)->mode_config.preferred_depth = 24;
adev_to_drm(adev)->mode_config.prefer_shadow = 1;
+ adev_to_drm(adev)->mode_config.fb_modifiers_not_supported = true;
adev_to_drm(adev)->mode_config.fb_base = adev->gmc.aper_base;
r = amdgpu_display_modeset_create_props(adev);
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
index 8318ee8339f1..de11fbe5aba2 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
@@ -2695,6 +2695,8 @@ static int dce_v8_0_sw_init(void *handle)
adev_to_drm(adev)->mode_config.preferred_depth = 24;
adev_to_drm(adev)->mode_config.prefer_shadow = 1;
+ adev_to_drm(adev)->mode_config.fb_modifiers_not_supported = true;
+
adev_to_drm(adev)->mode_config.fb_base = adev->gmc.aper_base;
r = amdgpu_display_modeset_create_props(adev);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 526076e4bde3..0eeb394e949c 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -7894,6 +7894,9 @@ static int amdgpu_dm_plane_init(struct amdgpu_display_manager *dm,
if (res)
return res;
+ if (modifiers == NULL)
+ adev_to_drm(dm->adev)->mode_config.fb_modifiers_not_supported = true;
+
res = drm_universal_plane_init(adev_to_drm(dm->adev), plane, possible_crtcs,
&dm_plane_funcs, formats, num_formats,
modifiers, plane->type, NULL);
diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
index 8e3e98f13db4..4f9b0a9f13e3 100644
--- a/drivers/gpu/drm/armada/armada_drv.c
+++ b/drivers/gpu/drm/armada/armada_drv.c
@@ -273,6 +273,9 @@ static int __init armada_drm_init(void)
{
int ret;
+ if (drm_firmware_drivers_only())
+ return -ENODEV;
+
ret = platform_driver_register(&armada_lcd_platform_driver);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c
index 65f172807a0d..13f496473b9e 100644
--- a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c
+++ b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c
@@ -20,6 +20,7 @@
#include <drm/drm_fb_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_module.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_vblank.h>
@@ -359,7 +360,7 @@ static struct platform_driver aspeed_gfx_platform_driver = {
},
};
-module_platform_driver(aspeed_gfx_platform_driver);
+drm_module_platform_driver(aspeed_gfx_platform_driver);
MODULE_AUTHOR("Joel Stanley <joel@jms.id.au>");
MODULE_DESCRIPTION("ASPEED BMC DRM/KMS driver");
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
index 1656d27b78b6..651e3c109360 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
@@ -22,6 +22,7 @@
#include <drm/drm_fb_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_module.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
@@ -833,7 +834,7 @@ static struct platform_driver atmel_hlcdc_dc_platform_driver = {
.of_match_table = atmel_hlcdc_dc_of_match,
},
};
-module_platform_driver(atmel_hlcdc_dc_platform_driver);
+drm_module_platform_driver(atmel_hlcdc_dc_platform_driver);
MODULE_AUTHOR("Jean-Jacques Hiblot <jjhiblot@traphandler.com>");
MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index fcd93f1aec90..c86f5be4dfe0 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -75,6 +75,14 @@ config DRM_DISPLAY_CONNECTOR
on ARM-based platforms. Saying Y here when this driver is not needed
will not cause any issue.
+config DRM_ITE_IT6505
+ tristate "ITE IT6505 DisplayPort bridge"
+ depends on OF
+ select DRM_KMS_HELPER
+ select EXTCON
+ help
+ ITE IT6505 DisplayPort bridge chip driver.
+
config DRM_LONTIUM_LT8912B
tristate "Lontium LT8912B DSI/HDMI bridge"
depends on OF
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index f2c73683cfcb..425844c30495 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_DRM_CHIPONE_ICN6211) += chipone-icn6211.o
obj-$(CONFIG_DRM_CHRONTEL_CH7033) += chrontel-ch7033.o
obj-$(CONFIG_DRM_CROS_EC_ANX7688) += cros-ec-anx7688.o
obj-$(CONFIG_DRM_DISPLAY_CONNECTOR) += display-connector.o
+obj-$(CONFIG_DRM_ITE_IT6505) += ite-it6505.o
obj-$(CONFIG_DRM_LONTIUM_LT8912B) += lontium-lt8912b.o
obj-$(CONFIG_DRM_LONTIUM_LT9611) += lontium-lt9611.o
obj-$(CONFIG_DRM_LONTIUM_LT9611UXC) += lontium-lt9611uxc.o
diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c
new file mode 100644
index 000000000000..fb16a176822d
--- /dev/null
+++ b/drivers/gpu/drm/bridge/ite-it6505.c
@@ -0,0 +1,3352 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/extcon.h>
+#include <linux/fs.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+
+#include <crypto/hash.h>
+
+#include <drm/dp/drm_dp_helper.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_hdcp.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+
+#include <sound/hdmi-codec.h>
+
+#define REG_IC_VER 0x04
+
+#define REG_RESET_CTRL 0x05
+#define VIDEO_RESET BIT(0)
+#define AUDIO_RESET BIT(1)
+#define ALL_LOGIC_RESET BIT(2)
+#define AUX_RESET BIT(3)
+#define HDCP_RESET BIT(4)
+
+#define INT_STATUS_01 0x06
+#define INT_MASK_01 0x09
+#define INT_HPD_CHANGE 0
+#define INT_RECEIVE_HPD_IRQ 1
+#define INT_SCDT_CHANGE 2
+#define INT_HDCP_FAIL 3
+#define INT_HDCP_DONE 4
+#define BIT_OFFSET(x) (((x) - INT_STATUS_01) * BITS_PER_BYTE)
+#define BIT_INT_HPD INT_HPD_CHANGE
+#define BIT_INT_HPD_IRQ INT_RECEIVE_HPD_IRQ
+#define BIT_INT_SCDT INT_SCDT_CHANGE
+#define BIT_INT_HDCP_FAIL INT_HDCP_FAIL
+#define BIT_INT_HDCP_DONE INT_HDCP_DONE
+
+#define INT_STATUS_02 0x07
+#define INT_MASK_02 0x0A
+#define INT_AUX_CMD_FAIL 0
+#define INT_HDCP_KSV_CHECK 1
+#define INT_AUDIO_FIFO_ERROR 2
+#define BIT_INT_AUX_CMD_FAIL (BIT_OFFSET(0x07) + INT_AUX_CMD_FAIL)
+#define BIT_INT_HDCP_KSV_CHECK (BIT_OFFSET(0x07) + INT_HDCP_KSV_CHECK)
+#define BIT_INT_AUDIO_FIFO_ERROR (BIT_OFFSET(0x07) + INT_AUDIO_FIFO_ERROR)
+
+#define INT_STATUS_03 0x08
+#define INT_MASK_03 0x0B
+#define INT_LINK_TRAIN_FAIL 4
+#define INT_VID_FIFO_ERROR 5
+#define INT_IO_LATCH_FIFO_OVERFLOW 7
+#define BIT_INT_LINK_TRAIN_FAIL (BIT_OFFSET(0x08) + INT_LINK_TRAIN_FAIL)
+#define BIT_INT_VID_FIFO_ERROR (BIT_OFFSET(0x08) + INT_VID_FIFO_ERROR)
+#define BIT_INT_IO_FIFO_OVERFLOW (BIT_OFFSET(0x08) + INT_IO_LATCH_FIFO_OVERFLOW)
+
+#define REG_SYSTEM_STS 0x0D
+#define INT_STS BIT(0)
+#define HPD_STS BIT(1)
+#define VIDEO_STB BIT(2)
+
+#define REG_LINK_TRAIN_STS 0x0E
+#define LINK_STATE_CR BIT(2)
+#define LINK_STATE_EQ BIT(3)
+#define LINK_STATE_NORP BIT(4)
+
+#define REG_BANK_SEL 0x0F
+#define REG_CLK_CTRL0 0x10
+#define M_PCLK_DELAY 0x03
+
+#define REG_AUX_OPT 0x11
+#define AUX_AUTO_RST BIT(0)
+#define AUX_FIX_FREQ BIT(3)
+
+#define REG_DATA_CTRL0 0x12
+#define VIDEO_LATCH_EDGE BIT(4)
+#define ENABLE_PCLK_COUNTER BIT(7)
+
+#define REG_PCLK_COUNTER_VALUE 0x13
+
+#define REG_501_FIFO_CTRL 0x15
+#define RST_501_FIFO BIT(1)
+
+#define REG_TRAIN_CTRL0 0x16
+#define FORCE_LBR BIT(0)
+#define LANE_COUNT_MASK 0x06
+#define LANE_SWAP BIT(3)
+#define SPREAD_AMP_5 BIT(4)
+#define FORCE_CR_DONE BIT(5)
+#define FORCE_EQ_DONE BIT(6)
+
+#define REG_TRAIN_CTRL1 0x17
+#define AUTO_TRAIN BIT(0)
+#define MANUAL_TRAIN BIT(1)
+#define FORCE_RETRAIN BIT(2)
+
+#define REG_AUX_CTRL 0x23
+#define CLR_EDID_FIFO BIT(0)
+#define AUX_USER_MODE BIT(1)
+#define AUX_NO_SEGMENT_WR BIT(6)
+#define AUX_EN_FIFO_READ BIT(7)
+
+#define REG_AUX_ADR_0_7 0x24
+#define REG_AUX_ADR_8_15 0x25
+#define REG_AUX_ADR_16_19 0x26
+#define REG_AUX_OUT_DATA0 0x27
+
+#define REG_AUX_CMD_REQ 0x2B
+#define AUX_BUSY BIT(5)
+
+#define REG_AUX_DATA_0_7 0x2C
+#define REG_AUX_DATA_8_15 0x2D
+#define REG_AUX_DATA_16_23 0x2E
+#define REG_AUX_DATA_24_31 0x2F
+
+#define REG_AUX_DATA_FIFO 0x2F
+
+#define REG_AUX_ERROR_STS 0x9F
+#define M_AUX_REQ_FAIL 0x03
+
+#define REG_HDCP_CTRL1 0x38
+#define HDCP_CP_ENABLE BIT(0)
+
+#define REG_HDCP_TRIGGER 0x39
+#define HDCP_TRIGGER_START BIT(0)
+#define HDCP_TRIGGER_CPIRQ BIT(1)
+#define HDCP_TRIGGER_KSV_DONE BIT(4)
+#define HDCP_TRIGGER_KSV_FAIL BIT(5)
+
+#define REG_HDCP_CTRL2 0x3A
+#define HDCP_AN_SEL BIT(0)
+#define HDCP_AN_GEN BIT(1)
+#define HDCP_HW_HPDIRQ_ACT BIT(2)
+#define HDCP_EN_M0_READ BIT(5)
+
+#define REG_M0_0_7 0x4C
+#define REG_AN_0_7 0x4C
+#define REG_SP_CTRL0 0x58
+#define REG_IP_CTRL1 0x59
+#define REG_IP_CTRL2 0x5A
+
+#define REG_LINK_DRV 0x5C
+#define DRV_HS BIT(1)
+
+#define REG_DRV_LN_DATA_SEL 0x5D
+
+#define REG_AUX 0x5E
+
+#define REG_VID_BUS_CTRL0 0x60
+#define IN_DDR BIT(2)
+#define DDR_CD (0x01 << 6)
+
+#define REG_VID_BUS_CTRL1 0x61
+#define TX_FIFO_RESET BIT(1)
+
+#define REG_INPUT_CTRL 0xA0
+#define INPUT_HSYNC_POL BIT(0)
+#define INPUT_VSYNC_POL BIT(2)
+#define INPUT_INTERLACED BIT(4)
+
+#define REG_INPUT_HTOTAL 0xA1
+#define REG_INPUT_HACTIVE_START 0xA3
+#define REG_INPUT_HACTIVE_WIDTH 0xA5
+#define REG_INPUT_HFRONT_PORCH 0xA7
+#define REG_INPUT_HSYNC_WIDTH 0xA9
+#define REG_INPUT_VTOTAL 0xAB
+#define REG_INPUT_VACTIVE_START 0xAD
+#define REG_INPUT_VACTIVE_WIDTH 0xAF
+#define REG_INPUT_VFRONT_PORCH 0xB1
+#define REG_INPUT_VSYNC_WIDTH 0xB3
+
+#define REG_AUDIO_SRC_CTRL 0xB8
+#define M_AUDIO_I2S_EN 0x0F
+#define EN_I2S0 BIT(0)
+#define EN_I2S1 BIT(1)
+#define EN_I2S2 BIT(2)
+#define EN_I2S3 BIT(3)
+#define AUDIO_FIFO_RESET BIT(7)
+
+#define REG_AUDIO_FMT 0xB9
+#define REG_AUDIO_FIFO_SEL 0xBA
+
+#define REG_AUDIO_CTRL0 0xBB
+#define AUDIO_FULL_PKT BIT(4)
+#define AUDIO_16B_BOUND BIT(5)
+
+#define REG_AUDIO_CTRL1 0xBC
+#define REG_AUDIO_INPUT_FREQ 0xBE
+
+#define REG_IEC958_STS0 0xBF
+#define REG_IEC958_STS1 0xC0
+#define REG_IEC958_STS2 0xC1
+#define REG_IEC958_STS3 0xC2
+#define REG_IEC958_STS4 0xC3
+
+#define REG_HPD_IRQ_TIME 0xC9
+#define REG_AUX_DEBUG_MODE 0xCA
+#define REG_AUX_OPT2 0xCB
+#define REG_HDCP_OPT 0xCE
+#define REG_USER_DRV_PRE 0xCF
+
+#define REG_DATA_MUTE_CTRL 0xD3
+#define ENABLE_ENHANCED_FRAME BIT(0)
+#define ENABLE_AUTO_VIDEO_FIFO_RESET BIT(1)
+#define EN_VID_MUTE BIT(4)
+#define EN_AUD_MUTE BIT(5)
+
+#define REG_TIME_STMP_CTRL 0xD4
+#define EN_ENHANCE_VID_STMP BIT(0)
+#define EN_ENHANCE_AUD_STMP BIT(2)
+#define M_STAMP_STEP 0x30
+#define EN_SSC_GAT BIT(6)
+
+#define REG_INFOFRAME_CTRL 0xE8
+#define EN_AVI_PKT BIT(0)
+#define EN_AUD_PKT BIT(1)
+#define EN_MPG_PKT BIT(2)
+#define EN_GEN_PKT BIT(3)
+#define EN_VID_TIME_STMP BIT(4)
+#define EN_AUD_TIME_STMP BIT(5)
+#define EN_VID_CTRL_PKT (EN_AVI_PKT | EN_VID_TIME_STMP)
+#define EN_AUD_CTRL_PKT (EN_AUD_PKT | EN_AUD_TIME_STMP)
+
+#define REG_AUDIO_N_0_7 0xDE
+#define REG_AUDIO_N_8_15 0xDF
+#define REG_AUDIO_N_16_23 0xE0
+
+#define REG_AVI_INFO_DB1 0xE9
+#define REG_AVI_INFO_DB2 0xEA
+#define REG_AVI_INFO_DB3 0xEB
+#define REG_AVI_INFO_DB4 0xEC
+#define REG_AVI_INFO_DB5 0xED
+#define REG_AVI_INFO_SUM 0xF6
+
+#define REG_AUD_INFOFRAM_DB1 0xF7
+#define REG_AUD_INFOFRAM_DB2 0xF8
+#define REG_AUD_INFOFRAM_DB3 0xF9
+#define REG_AUD_INFOFRAM_DB4 0xFA
+#define REG_AUD_INFOFRAM_SUM 0xFB
+
+/* the following six registers are in bank1 */
+#define REG_DRV_0_DB_800_MV 0x7E
+#define REG_PRE_0_DB_800_MV 0x7F
+#define REG_PRE_3P5_DB_800_MV 0x81
+#define REG_SSC_CTRL0 0x88
+#define REG_SSC_CTRL1 0x89
+#define REG_SSC_CTRL2 0x8A
+
+#define RBR DP_LINK_BW_1_62
+#define HBR DP_LINK_BW_2_7
+#define HBR2 DP_LINK_BW_5_4
+#define HBR3 DP_LINK_BW_8_1
+
+#define DPCD_V_1_1 0x11
+#define MISC_VERB 0xF0
+#define MISC_VERC 0x70
+#define I2S_INPUT_FORMAT_STANDARD 0
+#define I2S_INPUT_FORMAT_32BIT 1
+#define I2S_INPUT_LEFT_JUSTIFIED 0
+#define I2S_INPUT_RIGHT_JUSTIFIED 1
+#define I2S_DATA_1T_DELAY 0
+#define I2S_DATA_NO_DELAY 1
+#define I2S_WS_LEFT_CHANNEL 0
+#define I2S_WS_RIGHT_CHANNEL 1
+#define I2S_DATA_MSB_FIRST 0
+#define I2S_DATA_LSB_FIRST 1
+#define WORD_LENGTH_16BIT 0
+#define WORD_LENGTH_18BIT 1
+#define WORD_LENGTH_20BIT 2
+#define WORD_LENGTH_24BIT 3
+#define DEBUGFS_DIR_NAME "it6505-debugfs"
+#define READ_BUFFER_SIZE 200
+
+/* Vendor option */
+#define HDCP_DESIRED 1
+#define MAX_LANE_COUNT 4
+#define MAX_LINK_RATE HBR
+#define AUTO_TRAIN_RETRY 3
+#define MAX_HDCP_DOWN_STREAM_COUNT 10
+#define MAX_CR_LEVEL 0x03
+#define MAX_EQ_LEVEL 0x03
+#define AUX_WAIT_TIMEOUT_MS 15
+#define AUX_FIFO_MAX_SIZE 32
+#define PIXEL_CLK_DELAY 1
+#define PIXEL_CLK_INVERSE 0
+#define ADJUST_PHASE_THRESHOLD 80000
+#define DPI_PIXEL_CLK_MAX 95000
+#define HDCP_SHA1_FIFO_LEN (MAX_HDCP_DOWN_STREAM_COUNT * 5 + 10)
+#define DEFAULT_PWR_ON 0
+#define DEFAULT_DRV_HOLD 0
+
+#define AUDIO_SELECT I2S
+#define AUDIO_TYPE LPCM
+#define AUDIO_SAMPLE_RATE SAMPLE_RATE_48K
+#define AUDIO_CHANNEL_COUNT 2
+#define I2S_INPUT_FORMAT I2S_INPUT_FORMAT_32BIT
+#define I2S_JUSTIFIED I2S_INPUT_LEFT_JUSTIFIED
+#define I2S_DATA_DELAY I2S_DATA_1T_DELAY
+#define I2S_WS_CHANNEL I2S_WS_LEFT_CHANNEL
+#define I2S_DATA_SEQUENCE I2S_DATA_MSB_FIRST
+#define AUDIO_WORD_LENGTH WORD_LENGTH_24BIT
+
+enum aux_cmd_type {
+ CMD_AUX_NATIVE_READ = 0x0,
+ CMD_AUX_NATIVE_WRITE = 0x5,
+ CMD_AUX_I2C_EDID_READ = 0xB,
+};
+
+enum aux_cmd_reply {
+ REPLY_ACK,
+ REPLY_NACK,
+ REPLY_DEFER,
+};
+
+enum link_train_status {
+ LINK_IDLE,
+ LINK_BUSY,
+ LINK_OK,
+};
+
+enum hdcp_state {
+ HDCP_AUTH_IDLE,
+ HDCP_AUTH_GOING,
+ HDCP_AUTH_DONE,
+};
+
+struct it6505_platform_data {
+ struct regulator *pwr18;
+ struct regulator *ovdd;
+ struct gpio_desc *gpiod_reset;
+};
+
+enum it6505_audio_select {
+ I2S = 0,
+ SPDIF,
+};
+
+enum it6505_audio_sample_rate {
+ SAMPLE_RATE_24K = 0x6,
+ SAMPLE_RATE_32K = 0x3,
+ SAMPLE_RATE_48K = 0x2,
+ SAMPLE_RATE_96K = 0xA,
+ SAMPLE_RATE_192K = 0xE,
+ SAMPLE_RATE_44_1K = 0x0,
+ SAMPLE_RATE_88_2K = 0x8,
+ SAMPLE_RATE_176_4K = 0xC,
+};
+
+enum it6505_audio_type {
+ LPCM = 0,
+ NLPCM,
+ DSS,
+};
+
+struct it6505_audio_data {
+ enum it6505_audio_select select;
+ enum it6505_audio_sample_rate sample_rate;
+ enum it6505_audio_type type;
+ u8 word_length;
+ u8 channel_count;
+ u8 i2s_input_format;
+ u8 i2s_justified;
+ u8 i2s_data_delay;
+ u8 i2s_ws_channel;
+ u8 i2s_data_sequence;
+};
+
+struct it6505_audio_sample_rate_map {
+ enum it6505_audio_sample_rate rate;
+ int sample_rate_value;
+};
+
+struct it6505_drm_dp_link {
+ unsigned char revision;
+ unsigned int rate;
+ unsigned int num_lanes;
+ unsigned long capabilities;
+};
+
+struct debugfs_entries {
+ char *name;
+ const struct file_operations *fops;
+};
+
+struct it6505 {
+ struct drm_dp_aux aux;
+ struct drm_bridge bridge;
+ struct i2c_client *client;
+ struct it6505_drm_dp_link link;
+ struct it6505_platform_data pdata;
+ /*
+ * Mutex protects extcon and interrupt functions from interfering
+ * each other.
+ */
+ struct mutex extcon_lock;
+ struct mutex mode_lock; /* used to bridge_detect */
+ struct mutex aux_lock; /* used to aux data transfers */
+ struct regmap *regmap;
+ struct drm_display_mode source_output_mode;
+ struct drm_display_mode video_info;
+ struct notifier_block event_nb;
+ struct extcon_dev *extcon;
+ struct work_struct extcon_wq;
+ enum drm_connector_status connector_status;
+ enum link_train_status link_state;
+ struct work_struct link_works;
+ u8 dpcd[DP_RECEIVER_CAP_SIZE];
+ u8 lane_count;
+ u8 link_rate_bw_code;
+ u8 sink_count;
+ bool step_train;
+ bool branch_device;
+ bool enable_ssc;
+ bool lane_swap_disabled;
+ bool lane_swap;
+ bool powered;
+ bool hpd_state;
+ u32 afe_setting;
+ enum hdcp_state hdcp_status;
+ struct delayed_work hdcp_work;
+ struct work_struct hdcp_wait_ksv_list;
+ struct completion wait_edid_complete;
+ u8 auto_train_retry;
+ bool hdcp_desired;
+ bool is_repeater;
+ u8 hdcp_down_stream_count;
+ u8 bksvs[DRM_HDCP_KSV_LEN];
+ u8 sha1_input[HDCP_SHA1_FIFO_LEN];
+ bool enable_enhanced_frame;
+ hdmi_codec_plugged_cb plugged_cb;
+ struct device *codec_dev;
+ struct delayed_work delayed_audio;
+ struct it6505_audio_data audio;
+ struct dentry *debugfs;
+
+ /* it6505 driver hold option */
+ bool enable_drv_hold;
+};
+
+struct it6505_step_train_para {
+ u8 voltage_swing[MAX_LANE_COUNT];
+ u8 pre_emphasis[MAX_LANE_COUNT];
+};
+
+/*
+ * Vendor option afe settings for different platforms
+ * 0: without FPC cable
+ * 1: with FPC cable
+ */
+
+static const u8 afe_setting_table[][3] = {
+ {0x82, 0x00, 0x45},
+ {0x93, 0x2A, 0x85}
+};
+
+static const struct it6505_audio_sample_rate_map audio_sample_rate_map[] = {
+ {SAMPLE_RATE_24K, 24000},
+ {SAMPLE_RATE_32K, 32000},
+ {SAMPLE_RATE_48K, 48000},
+ {SAMPLE_RATE_96K, 96000},
+ {SAMPLE_RATE_192K, 192000},
+ {SAMPLE_RATE_44_1K, 44100},
+ {SAMPLE_RATE_88_2K, 88200},
+ {SAMPLE_RATE_176_4K, 176400},
+};
+
+static const struct regmap_range it6505_bridge_volatile_ranges[] = {
+ { .range_min = 0, .range_max = 0xFF },
+};
+
+static const struct regmap_access_table it6505_bridge_volatile_table = {
+ .yes_ranges = it6505_bridge_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(it6505_bridge_volatile_ranges),
+};
+
+static const struct regmap_config it6505_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_table = &it6505_bridge_volatile_table,
+ .cache_type = REGCACHE_NONE,
+};
+
+static int it6505_read(struct it6505 *it6505, unsigned int reg_addr)
+{
+ unsigned int value;
+ int err;
+ struct device *dev = &it6505->client->dev;
+
+ err = regmap_read(it6505->regmap, reg_addr, &value);
+ if (err < 0) {
+ dev_err(dev, "read failed reg[0x%x] err: %d", reg_addr, err);
+ return err;
+ }
+
+ return value;
+}
+
+static int it6505_write(struct it6505 *it6505, unsigned int reg_addr,
+ unsigned int reg_val)
+{
+ int err;
+ struct device *dev = &it6505->client->dev;
+
+ err = regmap_write(it6505->regmap, reg_addr, reg_val);
+
+ if (err < 0) {
+ dev_err(dev, "write failed reg[0x%x] = 0x%x err = %d",
+ reg_addr, reg_val, err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int it6505_set_bits(struct it6505 *it6505, unsigned int reg,
+ unsigned int mask, unsigned int value)
+{
+ int err;
+ struct device *dev = &it6505->client->dev;
+
+ err = regmap_update_bits(it6505->regmap, reg, mask, value);
+ if (err < 0) {
+ dev_err(dev, "write reg[0x%x] = 0x%x mask = 0x%x failed err %d",
+ reg, value, mask, err);
+ return err;
+ }
+
+ return 0;
+}
+
+static void it6505_debug_print(struct it6505 *it6505, unsigned int reg,
+ const char *prefix)
+{
+ struct device *dev = &it6505->client->dev;
+ int val;
+
+ if (likely(!(__drm_debug & DRM_UT_DRIVER)))
+ return;
+
+ val = it6505_read(it6505, reg);
+ if (val < 0)
+ DRM_DEV_DEBUG_DRIVER(dev, "%s reg[%02x] read error (%d)",
+ prefix, reg, val);
+ else
+ DRM_DEV_DEBUG_DRIVER(dev, "%s reg[%02x] = 0x%02x", prefix, reg,
+ val);
+}
+
+static int it6505_dpcd_read(struct it6505 *it6505, unsigned long offset)
+{
+ u8 value;
+ int ret;
+ struct device *dev = &it6505->client->dev;
+
+ ret = drm_dp_dpcd_readb(&it6505->aux, offset, &value);
+ if (ret < 0) {
+ dev_err(dev, "DPCD read failed [0x%lx] ret: %d", offset, ret);
+ return ret;
+ }
+ return value;
+}
+
+static int it6505_dpcd_write(struct it6505 *it6505, unsigned long offset,
+ u8 datain)
+{
+ int ret;
+ struct device *dev = &it6505->client->dev;
+
+ ret = drm_dp_dpcd_writeb(&it6505->aux, offset, datain);
+ if (ret < 0) {
+ dev_err(dev, "DPCD write failed [0x%lx] ret: %d", offset, ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int it6505_get_dpcd(struct it6505 *it6505, int offset, u8 *dpcd, int num)
+{
+ int ret;
+ struct device *dev = &it6505->client->dev;
+
+ ret = drm_dp_dpcd_read(&it6505->aux, offset, dpcd, num);
+
+ if (ret < 0)
+ return ret;
+
+ DRM_DEV_DEBUG_DRIVER(dev, "ret = %d DPCD[0x%x] = 0x%*ph", ret, offset,
+ num, dpcd);
+
+ return 0;
+}
+
+static void it6505_dump(struct it6505 *it6505)
+{
+ unsigned int i, j;
+ u8 regs[16];
+ struct device *dev = &it6505->client->dev;
+
+ for (i = 0; i <= 0xff; i += 16) {
+ for (j = 0; j < 16; j++)
+ regs[j] = it6505_read(it6505, i + j);
+
+ DRM_DEV_DEBUG_DRIVER(dev, "[0x%02x] = %16ph", i, regs);
+ }
+}
+
+static bool it6505_get_sink_hpd_status(struct it6505 *it6505)
+{
+ int reg_0d;
+
+ reg_0d = it6505_read(it6505, REG_SYSTEM_STS);
+
+ if (reg_0d < 0)
+ return false;
+
+ return reg_0d & HPD_STS;
+}
+
+static int it6505_read_word(struct it6505 *it6505, unsigned int reg)
+{
+ int val0, val1;
+
+ val0 = it6505_read(it6505, reg);
+ if (val0 < 0)
+ return val0;
+
+ val1 = it6505_read(it6505, reg + 1);
+ if (val1 < 0)
+ return val1;
+
+ return (val1 << 8) | val0;
+}
+
+static void it6505_calc_video_info(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+ int hsync_pol, vsync_pol, interlaced;
+ int htotal, hdes, hdew, hfph, hsyncw;
+ int vtotal, vdes, vdew, vfph, vsyncw;
+ int rddata, i, pclk, sum = 0;
+
+ usleep_range(10000, 15000);
+ rddata = it6505_read(it6505, REG_INPUT_CTRL);
+ hsync_pol = rddata & INPUT_HSYNC_POL;
+ vsync_pol = (rddata & INPUT_VSYNC_POL) >> 2;
+ interlaced = (rddata & INPUT_INTERLACED) >> 4;
+
+ htotal = it6505_read_word(it6505, REG_INPUT_HTOTAL) & 0x1FFF;
+ hdes = it6505_read_word(it6505, REG_INPUT_HACTIVE_START) & 0x1FFF;
+ hdew = it6505_read_word(it6505, REG_INPUT_HACTIVE_WIDTH) & 0x1FFF;
+ hfph = it6505_read_word(it6505, REG_INPUT_HFRONT_PORCH) & 0x1FFF;
+ hsyncw = it6505_read_word(it6505, REG_INPUT_HSYNC_WIDTH) & 0x1FFF;
+
+ vtotal = it6505_read_word(it6505, REG_INPUT_VTOTAL) & 0xFFF;
+ vdes = it6505_read_word(it6505, REG_INPUT_VACTIVE_START) & 0xFFF;
+ vdew = it6505_read_word(it6505, REG_INPUT_VACTIVE_WIDTH) & 0xFFF;
+ vfph = it6505_read_word(it6505, REG_INPUT_VFRONT_PORCH) & 0xFFF;
+ vsyncw = it6505_read_word(it6505, REG_INPUT_VSYNC_WIDTH) & 0xFFF;
+
+ DRM_DEV_DEBUG_DRIVER(dev, "hsync_pol:%d, vsync_pol:%d, interlaced:%d",
+ hsync_pol, vsync_pol, interlaced);
+ DRM_DEV_DEBUG_DRIVER(dev, "hactive_start:%d, vactive_start:%d",
+ hdes, vdes);
+
+ for (i = 0; i < 10; i++) {
+ it6505_set_bits(it6505, REG_DATA_CTRL0, ENABLE_PCLK_COUNTER,
+ ENABLE_PCLK_COUNTER);
+ usleep_range(10000, 15000);
+ it6505_set_bits(it6505, REG_DATA_CTRL0, ENABLE_PCLK_COUNTER,
+ 0x00);
+ rddata = it6505_read_word(it6505, REG_PCLK_COUNTER_VALUE) &
+ 0xFFF;
+
+ sum += rddata;
+ }
+
+ if (sum == 0) {
+ DRM_DEV_DEBUG_DRIVER(dev, "calc video timing error");
+ return;
+ }
+
+ sum /= 10;
+ pclk = 13500 * 2048 / sum;
+ it6505->video_info.clock = pclk;
+ it6505->video_info.hdisplay = hdew;
+ it6505->video_info.hsync_start = hdew + hfph;
+ it6505->video_info.hsync_end = hdew + hfph + hsyncw;
+ it6505->video_info.htotal = htotal;
+ it6505->video_info.vdisplay = vdew;
+ it6505->video_info.vsync_start = vdew + vfph;
+ it6505->video_info.vsync_end = vdew + vfph + vsyncw;
+ it6505->video_info.vtotal = vtotal;
+
+ DRM_DEV_DEBUG_DRIVER(dev, DRM_MODE_FMT,
+ DRM_MODE_ARG(&it6505->video_info));
+}
+
+static int it6505_drm_dp_link_probe(struct drm_dp_aux *aux,
+ struct it6505_drm_dp_link *link)
+{
+ u8 values[3];
+ int err;
+
+ memset(link, 0, sizeof(*link));
+
+ err = drm_dp_dpcd_read(aux, DP_DPCD_REV, values, sizeof(values));
+ if (err < 0)
+ return err;
+
+ link->revision = values[0];
+ link->rate = drm_dp_bw_code_to_link_rate(values[1]);
+ link->num_lanes = values[2] & DP_MAX_LANE_COUNT_MASK;
+
+ if (values[2] & DP_ENHANCED_FRAME_CAP)
+ link->capabilities = DP_ENHANCED_FRAME_CAP;
+
+ return 0;
+}
+
+static int it6505_drm_dp_link_power_up(struct drm_dp_aux *aux,
+ struct it6505_drm_dp_link *link)
+{
+ u8 value;
+ int err;
+
+ /* DP_SET_POWER register is only available on DPCD v1.1 and later */
+ if (link->revision < DPCD_V_1_1)
+ return 0;
+
+ err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value);
+ if (err < 0)
+ return err;
+
+ value &= ~DP_SET_POWER_MASK;
+ value |= DP_SET_POWER_D0;
+
+ err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
+ if (err < 0)
+ return err;
+
+ /*
+ * According to the DP 1.1 specification, a "Sink Device must exit the
+ * power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink
+ * Control Field" (register 0x600).
+ */
+ usleep_range(1000, 2000);
+
+ return 0;
+}
+
+static void it6505_clear_int(struct it6505 *it6505)
+{
+ it6505_write(it6505, INT_STATUS_01, 0xFF);
+ it6505_write(it6505, INT_STATUS_02, 0xFF);
+ it6505_write(it6505, INT_STATUS_03, 0xFF);
+}
+
+static void it6505_int_mask_enable(struct it6505 *it6505)
+{
+ it6505_write(it6505, INT_MASK_01, BIT(INT_HPD_CHANGE) |
+ BIT(INT_RECEIVE_HPD_IRQ) | BIT(INT_SCDT_CHANGE) |
+ BIT(INT_HDCP_FAIL) | BIT(INT_HDCP_DONE));
+
+ it6505_write(it6505, INT_MASK_02, BIT(INT_AUX_CMD_FAIL) |
+ BIT(INT_HDCP_KSV_CHECK) | BIT(INT_AUDIO_FIFO_ERROR));
+
+ it6505_write(it6505, INT_MASK_03, BIT(INT_LINK_TRAIN_FAIL) |
+ BIT(INT_VID_FIFO_ERROR) | BIT(INT_IO_LATCH_FIFO_OVERFLOW));
+}
+
+static void it6505_int_mask_disable(struct it6505 *it6505)
+{
+ it6505_write(it6505, INT_MASK_01, 0x00);
+ it6505_write(it6505, INT_MASK_02, 0x00);
+ it6505_write(it6505, INT_MASK_03, 0x00);
+}
+
+static void it6505_lane_termination_on(struct it6505 *it6505)
+{
+ int regcf;
+
+ regcf = it6505_read(it6505, REG_USER_DRV_PRE);
+
+ if (regcf == MISC_VERB)
+ it6505_set_bits(it6505, REG_DRV_LN_DATA_SEL, 0x80, 0x00);
+
+ if (regcf == MISC_VERC) {
+ if (it6505->lane_swap) {
+ switch (it6505->lane_count) {
+ case 1:
+ case 2:
+ it6505_set_bits(it6505, REG_DRV_LN_DATA_SEL,
+ 0x0C, 0x08);
+ break;
+ default:
+ it6505_set_bits(it6505, REG_DRV_LN_DATA_SEL,
+ 0x0C, 0x0C);
+ break;
+ }
+ } else {
+ switch (it6505->lane_count) {
+ case 1:
+ case 2:
+ it6505_set_bits(it6505, REG_DRV_LN_DATA_SEL,
+ 0x0C, 0x04);
+ break;
+ default:
+ it6505_set_bits(it6505, REG_DRV_LN_DATA_SEL,
+ 0x0C, 0x0C);
+ break;
+ }
+ }
+ }
+}
+
+static void it6505_lane_termination_off(struct it6505 *it6505)
+{
+ int regcf;
+
+ regcf = it6505_read(it6505, REG_USER_DRV_PRE);
+
+ if (regcf == MISC_VERB)
+ it6505_set_bits(it6505, REG_DRV_LN_DATA_SEL, 0x80, 0x80);
+
+ if (regcf == MISC_VERC)
+ it6505_set_bits(it6505, REG_DRV_LN_DATA_SEL, 0x0C, 0x00);
+}
+
+static void it6505_lane_power_on(struct it6505 *it6505)
+{
+ it6505_set_bits(it6505, REG_LINK_DRV, 0xF1,
+ (it6505->lane_swap ?
+ GENMASK(7, 8 - it6505->lane_count) :
+ GENMASK(3 + it6505->lane_count, 4)) |
+ 0x01);
+}
+
+static void it6505_lane_power_off(struct it6505 *it6505)
+{
+ it6505_set_bits(it6505, REG_LINK_DRV, 0xF0, 0x00);
+}
+
+static void it6505_lane_off(struct it6505 *it6505)
+{
+ it6505_lane_power_off(it6505);
+ it6505_lane_termination_off(it6505);
+}
+
+static void it6505_aux_termination_on(struct it6505 *it6505)
+{
+ int regcf;
+
+ regcf = it6505_read(it6505, REG_USER_DRV_PRE);
+
+ if (regcf == MISC_VERB)
+ it6505_lane_termination_on(it6505);
+
+ if (regcf == MISC_VERC)
+ it6505_set_bits(it6505, REG_DRV_LN_DATA_SEL, 0x80, 0x80);
+}
+
+static void it6505_aux_power_on(struct it6505 *it6505)
+{
+ it6505_set_bits(it6505, REG_AUX, 0x02, 0x02);
+}
+
+static void it6505_aux_on(struct it6505 *it6505)
+{
+ it6505_aux_power_on(it6505);
+ it6505_aux_termination_on(it6505);
+}
+
+static void it6505_aux_reset(struct it6505 *it6505)
+{
+ it6505_set_bits(it6505, REG_RESET_CTRL, AUX_RESET, AUX_RESET);
+ it6505_set_bits(it6505, REG_RESET_CTRL, AUX_RESET, 0x00);
+}
+
+static void it6505_reset_logic(struct it6505 *it6505)
+{
+ regmap_write(it6505->regmap, REG_RESET_CTRL, ALL_LOGIC_RESET);
+ usleep_range(1000, 1500);
+}
+
+static bool it6505_aux_op_finished(struct it6505 *it6505)
+{
+ int reg2b = it6505_read(it6505, REG_AUX_CMD_REQ);
+
+ if (reg2b < 0)
+ return false;
+
+ return (reg2b & AUX_BUSY) == 0;
+}
+
+static int it6505_aux_wait(struct it6505 *it6505)
+{
+ int status;
+ unsigned long timeout;
+ struct device *dev = &it6505->client->dev;
+
+ timeout = jiffies + msecs_to_jiffies(AUX_WAIT_TIMEOUT_MS) + 1;
+
+ while (!it6505_aux_op_finished(it6505)) {
+ if (time_after(jiffies, timeout)) {
+ dev_err(dev, "Timed out waiting AUX to finish");
+ return -ETIMEDOUT;
+ }
+ usleep_range(1000, 2000);
+ }
+
+ status = it6505_read(it6505, REG_AUX_ERROR_STS);
+ if (status < 0) {
+ dev_err(dev, "Failed to read AUX channel: %d", status);
+ return status;
+ }
+
+ return 0;
+}
+
+static ssize_t it6505_aux_operation(struct it6505 *it6505,
+ enum aux_cmd_type cmd,
+ unsigned int address, u8 *buffer,
+ size_t size, enum aux_cmd_reply *reply)
+{
+ int i, ret;
+ bool aux_write_check = false;
+
+ if (!it6505_get_sink_hpd_status(it6505))
+ return -EIO;
+
+ /* set AUX user mode */
+ it6505_set_bits(it6505, REG_AUX_CTRL, AUX_USER_MODE, AUX_USER_MODE);
+
+aux_op_start:
+ if (cmd == CMD_AUX_I2C_EDID_READ) {
+ /* AUX EDID FIFO has max length of AUX_FIFO_MAX_SIZE bytes. */
+ size = min_t(size_t, size, AUX_FIFO_MAX_SIZE);
+ /* Enable AUX FIFO read back and clear FIFO */
+ it6505_set_bits(it6505, REG_AUX_CTRL,
+ AUX_EN_FIFO_READ | CLR_EDID_FIFO,
+ AUX_EN_FIFO_READ | CLR_EDID_FIFO);
+
+ it6505_set_bits(it6505, REG_AUX_CTRL,
+ AUX_EN_FIFO_READ | CLR_EDID_FIFO,
+ AUX_EN_FIFO_READ);
+ } else {
+ /* The DP AUX transmit buffer has 4 bytes. */
+ size = min_t(size_t, size, 4);
+ it6505_set_bits(it6505, REG_AUX_CTRL, AUX_NO_SEGMENT_WR,
+ AUX_NO_SEGMENT_WR);
+ }
+
+ /* Start Address[7:0] */
+ it6505_write(it6505, REG_AUX_ADR_0_7, (address >> 0) & 0xFF);
+ /* Start Address[15:8] */
+ it6505_write(it6505, REG_AUX_ADR_8_15, (address >> 8) & 0xFF);
+ /* WriteNum[3:0]+StartAdr[19:16] */
+ it6505_write(it6505, REG_AUX_ADR_16_19,
+ ((address >> 16) & 0x0F) | ((size - 1) << 4));
+
+ if (cmd == CMD_AUX_NATIVE_WRITE)
+ regmap_bulk_write(it6505->regmap, REG_AUX_OUT_DATA0, buffer,
+ size);
+
+ /* Aux Fire */
+ it6505_write(it6505, REG_AUX_CMD_REQ, cmd);
+
+ ret = it6505_aux_wait(it6505);
+ if (ret < 0)
+ goto aux_op_err;
+
+ ret = it6505_read(it6505, REG_AUX_ERROR_STS);
+ if (ret < 0)
+ goto aux_op_err;
+
+ switch ((ret >> 6) & 0x3) {
+ case 0:
+ *reply = REPLY_ACK;
+ break;
+ case 1:
+ *reply = REPLY_DEFER;
+ ret = -EAGAIN;
+ goto aux_op_err;
+ case 2:
+ *reply = REPLY_NACK;
+ ret = -EIO;
+ goto aux_op_err;
+ case 3:
+ ret = -ETIMEDOUT;
+ goto aux_op_err;
+ }
+
+ /* Read back Native Write data */
+ if (cmd == CMD_AUX_NATIVE_WRITE) {
+ aux_write_check = true;
+ cmd = CMD_AUX_NATIVE_READ;
+ goto aux_op_start;
+ }
+
+ if (cmd == CMD_AUX_I2C_EDID_READ) {
+ for (i = 0; i < size; i++) {
+ ret = it6505_read(it6505, REG_AUX_DATA_FIFO);
+ if (ret < 0)
+ goto aux_op_err;
+ buffer[i] = ret;
+ }
+ } else {
+ for (i = 0; i < size; i++) {
+ ret = it6505_read(it6505, REG_AUX_DATA_0_7 + i);
+ if (ret < 0)
+ goto aux_op_err;
+
+ if (aux_write_check && buffer[size - 1 - i] != ret) {
+ ret = -EINVAL;
+ goto aux_op_err;
+ }
+
+ buffer[size - 1 - i] = ret;
+ }
+ }
+
+ ret = i;
+
+aux_op_err:
+ if (cmd == CMD_AUX_I2C_EDID_READ) {
+ /* clear AUX FIFO */
+ it6505_set_bits(it6505, REG_AUX_CTRL,
+ AUX_EN_FIFO_READ | CLR_EDID_FIFO,
+ AUX_EN_FIFO_READ | CLR_EDID_FIFO);
+ it6505_set_bits(it6505, REG_AUX_CTRL,
+ AUX_EN_FIFO_READ | CLR_EDID_FIFO, 0x00);
+ }
+
+ /* Leave AUX user mode */
+ it6505_set_bits(it6505, REG_AUX_CTRL, AUX_USER_MODE, 0);
+
+ return ret;
+}
+
+static ssize_t it6505_aux_do_transfer(struct it6505 *it6505,
+ enum aux_cmd_type cmd,
+ unsigned int address, u8 *buffer,
+ size_t size, enum aux_cmd_reply *reply)
+{
+ int i, ret_size, ret = 0, request_size;
+
+ mutex_lock(&it6505->aux_lock);
+ for (i = 0; i < size; i += 4) {
+ request_size = min((int)size - i, 4);
+ ret_size = it6505_aux_operation(it6505, cmd, address + i,
+ buffer + i, request_size,
+ reply);
+ if (ret_size < 0) {
+ ret = ret_size;
+ goto aux_op_err;
+ }
+
+ ret += ret_size;
+ }
+
+aux_op_err:
+ mutex_unlock(&it6505->aux_lock);
+ return ret;
+}
+
+static ssize_t it6505_aux_transfer(struct drm_dp_aux *aux,
+ struct drm_dp_aux_msg *msg)
+{
+ struct it6505 *it6505 = container_of(aux, struct it6505, aux);
+ u8 cmd;
+ bool is_i2c = !(msg->request & DP_AUX_NATIVE_WRITE);
+ int ret;
+ enum aux_cmd_reply reply;
+
+ /* IT6505 doesn't support arbitrary I2C read / write. */
+ if (is_i2c)
+ return -EINVAL;
+
+ switch (msg->request) {
+ case DP_AUX_NATIVE_READ:
+ cmd = CMD_AUX_NATIVE_READ;
+ break;
+ case DP_AUX_NATIVE_WRITE:
+ cmd = CMD_AUX_NATIVE_WRITE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = it6505_aux_do_transfer(it6505, cmd, msg->address, msg->buffer,
+ msg->size, &reply);
+ if (ret < 0)
+ return ret;
+
+ switch (reply) {
+ case REPLY_ACK:
+ msg->reply = DP_AUX_NATIVE_REPLY_ACK;
+ break;
+ case REPLY_NACK:
+ msg->reply = DP_AUX_NATIVE_REPLY_NACK;
+ break;
+ case REPLY_DEFER:
+ msg->reply = DP_AUX_NATIVE_REPLY_DEFER;
+ break;
+ }
+
+ return ret;
+}
+
+static int it6505_get_edid_block(void *data, u8 *buf, unsigned int block,
+ size_t len)
+{
+ struct it6505 *it6505 = data;
+ struct device *dev = &it6505->client->dev;
+ enum aux_cmd_reply reply;
+ int offset, ret, aux_retry = 100;
+
+ it6505_aux_reset(it6505);
+ DRM_DEV_DEBUG_DRIVER(dev, "block number = %d", block);
+
+ for (offset = 0; offset < EDID_LENGTH;) {
+ ret = it6505_aux_do_transfer(it6505, CMD_AUX_I2C_EDID_READ,
+ block * EDID_LENGTH + offset,
+ buf + offset, 8, &reply);
+
+ if (ret < 0 && ret != -EAGAIN)
+ return ret;
+
+ switch (reply) {
+ case REPLY_ACK:
+ DRM_DEV_DEBUG_DRIVER(dev, "[0x%02x]: %8ph", offset,
+ buf + offset);
+ offset += 8;
+ aux_retry = 100;
+ break;
+ case REPLY_NACK:
+ return -EIO;
+ case REPLY_DEFER:
+ msleep(20);
+ if (!(--aux_retry))
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+static void it6505_variable_config(struct it6505 *it6505)
+{
+ it6505->link_rate_bw_code = HBR;
+ it6505->lane_count = MAX_LANE_COUNT;
+ it6505->link_state = LINK_IDLE;
+ it6505->hdcp_desired = HDCP_DESIRED;
+ it6505->auto_train_retry = AUTO_TRAIN_RETRY;
+ it6505->audio.select = AUDIO_SELECT;
+ it6505->audio.sample_rate = AUDIO_SAMPLE_RATE;
+ it6505->audio.channel_count = AUDIO_CHANNEL_COUNT;
+ it6505->audio.type = AUDIO_TYPE;
+ it6505->audio.i2s_input_format = I2S_INPUT_FORMAT;
+ it6505->audio.i2s_justified = I2S_JUSTIFIED;
+ it6505->audio.i2s_data_delay = I2S_DATA_DELAY;
+ it6505->audio.i2s_ws_channel = I2S_WS_CHANNEL;
+ it6505->audio.i2s_data_sequence = I2S_DATA_SEQUENCE;
+ it6505->audio.word_length = AUDIO_WORD_LENGTH;
+ memset(it6505->sha1_input, 0, sizeof(it6505->sha1_input));
+ memset(it6505->bksvs, 0, sizeof(it6505->bksvs));
+}
+
+static int it6505_send_video_infoframe(struct it6505 *it6505,
+ struct hdmi_avi_infoframe *frame)
+{
+ u8 buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE];
+ int err;
+ struct device *dev = &it6505->client->dev;
+
+ err = hdmi_avi_infoframe_pack(frame, buffer, sizeof(buffer));
+ if (err < 0) {
+ dev_err(dev, "Failed to pack AVI infoframe: %d", err);
+ return err;
+ }
+
+ err = it6505_set_bits(it6505, REG_INFOFRAME_CTRL, EN_AVI_PKT, 0x00);
+ if (err)
+ return err;
+
+ err = regmap_bulk_write(it6505->regmap, REG_AVI_INFO_DB1,
+ buffer + HDMI_INFOFRAME_HEADER_SIZE,
+ frame->length);
+ if (err)
+ return err;
+
+ err = it6505_set_bits(it6505, REG_INFOFRAME_CTRL, EN_AVI_PKT,
+ EN_AVI_PKT);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static void it6505_get_extcon_property(struct it6505 *it6505)
+{
+ int err;
+ union extcon_property_value property;
+ struct device *dev = &it6505->client->dev;
+
+ if (it6505->extcon && !it6505->lane_swap_disabled) {
+ err = extcon_get_property(it6505->extcon, EXTCON_DISP_DP,
+ EXTCON_PROP_USB_TYPEC_POLARITY,
+ &property);
+ if (err) {
+ dev_err(dev, "get property fail!");
+ return;
+ }
+ it6505->lane_swap = property.intval;
+ }
+}
+
+static void it6505_clk_phase_adjustment(struct it6505 *it6505,
+ const struct drm_display_mode *mode)
+{
+ int clock = mode->clock;
+
+ it6505_set_bits(it6505, REG_CLK_CTRL0, M_PCLK_DELAY,
+ clock < ADJUST_PHASE_THRESHOLD ? PIXEL_CLK_DELAY : 0);
+ it6505_set_bits(it6505, REG_DATA_CTRL0, VIDEO_LATCH_EDGE,
+ PIXEL_CLK_INVERSE << 4);
+}
+
+static void it6505_link_reset_step_train(struct it6505 *it6505)
+{
+ it6505_set_bits(it6505, REG_TRAIN_CTRL0,
+ FORCE_CR_DONE | FORCE_EQ_DONE, 0x00);
+ it6505_dpcd_write(it6505, DP_TRAINING_PATTERN_SET,
+ DP_TRAINING_PATTERN_DISABLE);
+}
+
+static void it6505_init(struct it6505 *it6505)
+{
+ it6505_write(it6505, REG_AUX_OPT, AUX_AUTO_RST | AUX_FIX_FREQ);
+ it6505_write(it6505, REG_AUX_CTRL, AUX_NO_SEGMENT_WR);
+ it6505_write(it6505, REG_HDCP_CTRL2, HDCP_AN_SEL | HDCP_HW_HPDIRQ_ACT);
+ it6505_write(it6505, REG_VID_BUS_CTRL0, IN_DDR | DDR_CD);
+ it6505_write(it6505, REG_VID_BUS_CTRL1, 0x01);
+ it6505_write(it6505, REG_AUDIO_CTRL0, AUDIO_16B_BOUND);
+
+ /* chip internal setting, don't modify */
+ it6505_write(it6505, REG_HPD_IRQ_TIME, 0xF5);
+ it6505_write(it6505, REG_AUX_DEBUG_MODE, 0x4D);
+ it6505_write(it6505, REG_AUX_OPT2, 0x17);
+ it6505_write(it6505, REG_HDCP_OPT, 0x60);
+ it6505_write(it6505, REG_DATA_MUTE_CTRL,
+ EN_VID_MUTE | EN_AUD_MUTE | ENABLE_AUTO_VIDEO_FIFO_RESET);
+ it6505_write(it6505, REG_TIME_STMP_CTRL,
+ EN_SSC_GAT | EN_ENHANCE_VID_STMP | EN_ENHANCE_AUD_STMP);
+ it6505_write(it6505, REG_INFOFRAME_CTRL, 0x00);
+ it6505_write(it6505, REG_BANK_SEL, 0x01);
+ it6505_write(it6505, REG_DRV_0_DB_800_MV,
+ afe_setting_table[it6505->afe_setting][0]);
+ it6505_write(it6505, REG_PRE_0_DB_800_MV,
+ afe_setting_table[it6505->afe_setting][1]);
+ it6505_write(it6505, REG_PRE_3P5_DB_800_MV,
+ afe_setting_table[it6505->afe_setting][2]);
+ it6505_write(it6505, REG_SSC_CTRL0, 0x9E);
+ it6505_write(it6505, REG_SSC_CTRL1, 0x1C);
+ it6505_write(it6505, REG_SSC_CTRL2, 0x42);
+ it6505_write(it6505, REG_BANK_SEL, 0x00);
+}
+
+static void it6505_video_disable(struct it6505 *it6505)
+{
+ it6505_set_bits(it6505, REG_DATA_MUTE_CTRL, EN_VID_MUTE, EN_VID_MUTE);
+ it6505_set_bits(it6505, REG_INFOFRAME_CTRL, EN_VID_CTRL_PKT, 0x00);
+ it6505_set_bits(it6505, REG_RESET_CTRL, VIDEO_RESET, VIDEO_RESET);
+}
+
+static void it6505_video_reset(struct it6505 *it6505)
+{
+ it6505_link_reset_step_train(it6505);
+ it6505_set_bits(it6505, REG_DATA_MUTE_CTRL, EN_VID_MUTE, EN_VID_MUTE);
+ it6505_set_bits(it6505, REG_INFOFRAME_CTRL, EN_VID_CTRL_PKT, 0x00);
+ it6505_set_bits(it6505, REG_RESET_CTRL, VIDEO_RESET, VIDEO_RESET);
+ it6505_set_bits(it6505, REG_501_FIFO_CTRL, RST_501_FIFO, RST_501_FIFO);
+ it6505_set_bits(it6505, REG_501_FIFO_CTRL, RST_501_FIFO, 0x00);
+ it6505_set_bits(it6505, REG_RESET_CTRL, VIDEO_RESET, 0x00);
+}
+
+static void it6505_update_video_parameter(struct it6505 *it6505,
+ const struct drm_display_mode *mode)
+{
+ it6505_clk_phase_adjustment(it6505, mode);
+ it6505_video_disable(it6505);
+}
+
+static bool it6505_audio_input(struct it6505 *it6505)
+{
+ int reg05, regbe;
+
+ reg05 = it6505_read(it6505, REG_RESET_CTRL);
+ it6505_set_bits(it6505, REG_RESET_CTRL, AUDIO_RESET, 0x00);
+ usleep_range(3000, 4000);
+ regbe = it6505_read(it6505, REG_AUDIO_INPUT_FREQ);
+ it6505_write(it6505, REG_RESET_CTRL, reg05);
+
+ return regbe != 0xFF;
+}
+
+static void it6505_setup_audio_channel_status(struct it6505 *it6505)
+{
+ enum it6505_audio_sample_rate sample_rate = it6505->audio.sample_rate;
+ u8 audio_word_length_map[] = { 0x02, 0x04, 0x03, 0x0B };
+
+ /* Channel Status */
+ it6505_write(it6505, REG_IEC958_STS0, it6505->audio.type << 1);
+ it6505_write(it6505, REG_IEC958_STS1, 0x00);
+ it6505_write(it6505, REG_IEC958_STS2, 0x00);
+ it6505_write(it6505, REG_IEC958_STS3, sample_rate);
+ it6505_write(it6505, REG_IEC958_STS4, (~sample_rate << 4) |
+ audio_word_length_map[it6505->audio.word_length]);
+}
+
+static void it6505_setup_audio_format(struct it6505 *it6505)
+{
+ /* I2S MODE */
+ it6505_write(it6505, REG_AUDIO_FMT,
+ (it6505->audio.word_length << 5) |
+ (it6505->audio.i2s_data_sequence << 4) |
+ (it6505->audio.i2s_ws_channel << 3) |
+ (it6505->audio.i2s_data_delay << 2) |
+ (it6505->audio.i2s_justified << 1) |
+ it6505->audio.i2s_input_format);
+ if (it6505->audio.select == SPDIF) {
+ it6505_write(it6505, REG_AUDIO_FIFO_SEL, 0x00);
+ /* 0x30 = 128*FS */
+ it6505_set_bits(it6505, REG_AUX_OPT, 0xF0, 0x30);
+ } else {
+ it6505_write(it6505, REG_AUDIO_FIFO_SEL, 0xE4);
+ }
+
+ it6505_write(it6505, REG_AUDIO_CTRL0, 0x20);
+ it6505_write(it6505, REG_AUDIO_CTRL1, 0x00);
+}
+
+static void it6505_enable_audio_source(struct it6505 *it6505)
+{
+ unsigned int audio_source_count;
+
+ audio_source_count = BIT(DIV_ROUND_UP(it6505->audio.channel_count, 2))
+ - 1;
+
+ audio_source_count |= it6505->audio.select << 4;
+
+ it6505_write(it6505, REG_AUDIO_SRC_CTRL, audio_source_count);
+}
+
+static void it6505_enable_audio_infoframe(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+ u8 audio_info_ca[] = { 0x00, 0x00, 0x01, 0x03, 0x07, 0x0B, 0x0F, 0x1F };
+
+ DRM_DEV_DEBUG_DRIVER(dev, "infoframe channel_allocation:0x%02x",
+ audio_info_ca[it6505->audio.channel_count - 1]);
+
+ it6505_write(it6505, REG_AUD_INFOFRAM_DB1, it6505->audio.channel_count
+ - 1);
+ it6505_write(it6505, REG_AUD_INFOFRAM_DB2, 0x00);
+ it6505_write(it6505, REG_AUD_INFOFRAM_DB3,
+ audio_info_ca[it6505->audio.channel_count - 1]);
+ it6505_write(it6505, REG_AUD_INFOFRAM_DB4, 0x00);
+ it6505_write(it6505, REG_AUD_INFOFRAM_SUM, 0x00);
+
+ /* Enable Audio InfoFrame */
+ it6505_set_bits(it6505, REG_INFOFRAME_CTRL, EN_AUD_CTRL_PKT,
+ EN_AUD_CTRL_PKT);
+}
+
+static void it6505_disable_audio(struct it6505 *it6505)
+{
+ it6505_set_bits(it6505, REG_DATA_MUTE_CTRL, EN_AUD_MUTE, EN_AUD_MUTE);
+ it6505_set_bits(it6505, REG_AUDIO_SRC_CTRL, M_AUDIO_I2S_EN, 0x00);
+ it6505_set_bits(it6505, REG_INFOFRAME_CTRL, EN_AUD_CTRL_PKT, 0x00);
+ it6505_set_bits(it6505, REG_RESET_CTRL, AUDIO_RESET, AUDIO_RESET);
+}
+
+static void it6505_enable_audio(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+ int regbe;
+
+ DRM_DEV_DEBUG_DRIVER(dev, "start");
+ it6505_disable_audio(it6505);
+
+ it6505_setup_audio_channel_status(it6505);
+ it6505_setup_audio_format(it6505);
+ it6505_enable_audio_source(it6505);
+ it6505_enable_audio_infoframe(it6505);
+
+ it6505_write(it6505, REG_AUDIO_N_0_7, 0x00);
+ it6505_write(it6505, REG_AUDIO_N_8_15, 0x80);
+ it6505_write(it6505, REG_AUDIO_N_16_23, 0x00);
+
+ it6505_set_bits(it6505, REG_AUDIO_SRC_CTRL, AUDIO_FIFO_RESET,
+ AUDIO_FIFO_RESET);
+ it6505_set_bits(it6505, REG_AUDIO_SRC_CTRL, AUDIO_FIFO_RESET, 0x00);
+ it6505_set_bits(it6505, REG_RESET_CTRL, AUDIO_RESET, 0x00);
+ regbe = it6505_read(it6505, REG_AUDIO_INPUT_FREQ);
+ DRM_DEV_DEBUG_DRIVER(dev, "regbe:0x%02x audio input fs: %d.%d kHz",
+ regbe, 6750 / regbe, (6750 % regbe) * 10 / regbe);
+ it6505_set_bits(it6505, REG_DATA_MUTE_CTRL, EN_AUD_MUTE, 0x00);
+}
+
+static bool it6505_use_step_train_check(struct it6505 *it6505)
+{
+ if (it6505->link.revision >= 0x12)
+ return it6505->dpcd[DP_TRAINING_AUX_RD_INTERVAL] >= 0x01;
+
+ return true;
+}
+
+static void it6505_parse_link_capabilities(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+ struct it6505_drm_dp_link *link = &it6505->link;
+ int bcaps;
+
+ if (it6505->dpcd[0] == 0) {
+ it6505_aux_on(it6505);
+ it6505_get_dpcd(it6505, DP_DPCD_REV, it6505->dpcd,
+ ARRAY_SIZE(it6505->dpcd));
+ }
+
+ DRM_DEV_DEBUG_DRIVER(dev, "DPCD Rev.: %d.%d",
+ link->revision >> 4, link->revision & 0x0F);
+
+ DRM_DEV_DEBUG_DRIVER(dev, "Sink max link rate: %d.%02d Gbps per lane",
+ link->rate / 100000, link->rate / 1000 % 100);
+
+ it6505->link_rate_bw_code = drm_dp_link_rate_to_bw_code(link->rate);
+ DRM_DEV_DEBUG_DRIVER(dev, "link rate bw code:0x%02x",
+ it6505->link_rate_bw_code);
+ it6505->link_rate_bw_code = min_t(int, it6505->link_rate_bw_code,
+ MAX_LINK_RATE);
+
+ it6505->lane_count = link->num_lanes;
+ DRM_DEV_DEBUG_DRIVER(dev, "Sink support %d lanes training",
+ it6505->lane_count);
+ it6505->lane_count = min_t(int, it6505->lane_count, MAX_LANE_COUNT);
+
+ it6505->branch_device = drm_dp_is_branch(it6505->dpcd);
+ DRM_DEV_DEBUG_DRIVER(dev, "Sink %sbranch device",
+ it6505->branch_device ? "" : "Not ");
+
+ it6505->enable_enhanced_frame = link->capabilities;
+ DRM_DEV_DEBUG_DRIVER(dev, "Sink %sSupport Enhanced Framing",
+ it6505->enable_enhanced_frame ? "" : "Not ");
+
+ it6505->enable_ssc = (it6505->dpcd[DP_MAX_DOWNSPREAD] &
+ DP_MAX_DOWNSPREAD_0_5);
+ DRM_DEV_DEBUG_DRIVER(dev, "Maximum Down-Spread: %s, %ssupport SSC!",
+ it6505->enable_ssc ? "0.5" : "0",
+ it6505->enable_ssc ? "" : "Not ");
+
+ it6505->step_train = it6505_use_step_train_check(it6505);
+ if (it6505->step_train)
+ DRM_DEV_DEBUG_DRIVER(dev, "auto train fail, will step train");
+
+ bcaps = it6505_dpcd_read(it6505, DP_AUX_HDCP_BCAPS);
+ DRM_DEV_DEBUG_DRIVER(dev, "bcaps:0x%02x", bcaps);
+ if (bcaps & DP_BCAPS_HDCP_CAPABLE) {
+ it6505->is_repeater = (bcaps & DP_BCAPS_REPEATER_PRESENT);
+ DRM_DEV_DEBUG_DRIVER(dev, "Support HDCP! Downstream is %s!",
+ it6505->is_repeater ? "repeater" :
+ "receiver");
+ } else {
+ DRM_DEV_DEBUG_DRIVER(dev, "Sink not support HDCP!");
+ it6505->hdcp_desired = false;
+ }
+ DRM_DEV_DEBUG_DRIVER(dev, "HDCP %s",
+ it6505->hdcp_desired ? "desired" : "undesired");
+}
+
+static void it6505_setup_ssc(struct it6505 *it6505)
+{
+ it6505_set_bits(it6505, REG_TRAIN_CTRL0, SPREAD_AMP_5,
+ it6505->enable_ssc ? SPREAD_AMP_5 : 0x00);
+ if (it6505->enable_ssc) {
+ it6505_write(it6505, REG_BANK_SEL, 0x01);
+ it6505_write(it6505, REG_SSC_CTRL0, 0x9E);
+ it6505_write(it6505, REG_SSC_CTRL1, 0x1C);
+ it6505_write(it6505, REG_SSC_CTRL2, 0x42);
+ it6505_write(it6505, REG_BANK_SEL, 0x00);
+ it6505_write(it6505, REG_SP_CTRL0, 0x07);
+ it6505_write(it6505, REG_IP_CTRL1, 0x29);
+ it6505_write(it6505, REG_IP_CTRL2, 0x03);
+ /* Stamp Interrupt Step */
+ it6505_set_bits(it6505, REG_TIME_STMP_CTRL, M_STAMP_STEP,
+ 0x10);
+ it6505_dpcd_write(it6505, DP_DOWNSPREAD_CTRL,
+ DP_SPREAD_AMP_0_5);
+ } else {
+ it6505_dpcd_write(it6505, DP_DOWNSPREAD_CTRL, 0x00);
+ it6505_set_bits(it6505, REG_TIME_STMP_CTRL, M_STAMP_STEP,
+ 0x00);
+ }
+}
+
+static inline void it6505_link_rate_setup(struct it6505 *it6505)
+{
+ it6505_set_bits(it6505, REG_TRAIN_CTRL0, FORCE_LBR,
+ (it6505->link_rate_bw_code == RBR) ? FORCE_LBR : 0x00);
+ it6505_set_bits(it6505, REG_LINK_DRV, DRV_HS,
+ (it6505->link_rate_bw_code == RBR) ? 0x00 : DRV_HS);
+}
+
+static void it6505_lane_count_setup(struct it6505 *it6505)
+{
+ it6505_get_extcon_property(it6505);
+ it6505_set_bits(it6505, REG_TRAIN_CTRL0, LANE_SWAP,
+ it6505->lane_swap ? LANE_SWAP : 0x00);
+ it6505_set_bits(it6505, REG_TRAIN_CTRL0, LANE_COUNT_MASK,
+ (it6505->lane_count - 1) << 1);
+}
+
+static void it6505_link_training_setup(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+
+ if (it6505->enable_enhanced_frame)
+ it6505_set_bits(it6505, REG_DATA_MUTE_CTRL,
+ ENABLE_ENHANCED_FRAME, ENABLE_ENHANCED_FRAME);
+
+ it6505_link_rate_setup(it6505);
+ it6505_lane_count_setup(it6505);
+ it6505_setup_ssc(it6505);
+ DRM_DEV_DEBUG_DRIVER(dev,
+ "%s, %d lanes, %sable ssc, %sable enhanced frame",
+ it6505->link_rate_bw_code != RBR ? "HBR" : "RBR",
+ it6505->lane_count,
+ it6505->enable_ssc ? "en" : "dis",
+ it6505->enable_enhanced_frame ? "en" : "dis");
+}
+
+static bool it6505_link_start_auto_train(struct it6505 *it6505)
+{
+ int timeout = 500, link_training_state;
+ bool state = false;
+
+ mutex_lock(&it6505->aux_lock);
+ it6505_set_bits(it6505, REG_TRAIN_CTRL0,
+ FORCE_CR_DONE | FORCE_EQ_DONE, 0x00);
+ it6505_write(it6505, REG_TRAIN_CTRL1, FORCE_RETRAIN);
+ it6505_write(it6505, REG_TRAIN_CTRL1, AUTO_TRAIN);
+
+ while (timeout > 0) {
+ usleep_range(1000, 2000);
+ link_training_state = it6505_read(it6505, REG_LINK_TRAIN_STS);
+
+ if (link_training_state > 0 &&
+ (link_training_state & LINK_STATE_NORP)) {
+ state = true;
+ goto unlock;
+ }
+
+ timeout--;
+ }
+unlock:
+ mutex_unlock(&it6505->aux_lock);
+
+ return state;
+}
+
+static int it6505_drm_dp_link_configure(struct it6505 *it6505)
+{
+ u8 values[2];
+ int err;
+ struct drm_dp_aux *aux = &it6505->aux;
+
+ values[0] = it6505->link_rate_bw_code;
+ values[1] = it6505->lane_count;
+
+ if (it6505->enable_enhanced_frame)
+ values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
+
+ err = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, values, sizeof(values));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static bool it6505_check_voltage_swing_max(u8 lane_voltage_swing_pre_emphasis)
+{
+ return ((lane_voltage_swing_pre_emphasis & 0x03) == MAX_CR_LEVEL);
+}
+
+static bool it6505_check_pre_emphasis_max(u8 lane_voltage_swing_pre_emphasis)
+{
+ return ((lane_voltage_swing_pre_emphasis & 0x03) == MAX_EQ_LEVEL);
+}
+
+static bool it6505_check_max_voltage_swing_reached(u8 *lane_voltage_swing,
+ u8 lane_count)
+{
+ u8 i;
+
+ for (i = 0; i < lane_count; i++) {
+ if (lane_voltage_swing[i] & DP_TRAIN_MAX_SWING_REACHED)
+ return true;
+ }
+
+ return false;
+}
+
+static bool
+step_train_lane_voltage_para_set(struct it6505 *it6505,
+ struct it6505_step_train_para
+ *lane_voltage_pre_emphasis,
+ u8 *lane_voltage_pre_emphasis_set)
+{
+ u8 *voltage_swing = lane_voltage_pre_emphasis->voltage_swing;
+ u8 *pre_emphasis = lane_voltage_pre_emphasis->pre_emphasis;
+ u8 i;
+
+ for (i = 0; i < it6505->lane_count; i++) {
+ voltage_swing[i] &= 0x03;
+ lane_voltage_pre_emphasis_set[i] = voltage_swing[i];
+ if (it6505_check_voltage_swing_max(voltage_swing[i]))
+ lane_voltage_pre_emphasis_set[i] |=
+ DP_TRAIN_MAX_SWING_REACHED;
+
+ pre_emphasis[i] &= 0x03;
+ lane_voltage_pre_emphasis_set[i] |= pre_emphasis[i]
+ << DP_TRAIN_PRE_EMPHASIS_SHIFT;
+ if (it6505_check_pre_emphasis_max(pre_emphasis[i]))
+ lane_voltage_pre_emphasis_set[i] |=
+ DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+ it6505_dpcd_write(it6505, DP_TRAINING_LANE0_SET + i,
+ lane_voltage_pre_emphasis_set[i]);
+
+ if (lane_voltage_pre_emphasis_set[i] !=
+ it6505_dpcd_read(it6505, DP_TRAINING_LANE0_SET + i))
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+it6505_step_cr_train(struct it6505 *it6505,
+ struct it6505_step_train_para *lane_voltage_pre_emphasis)
+{
+ u8 loop_count = 0, i = 0, j;
+ u8 link_status[DP_LINK_STATUS_SIZE] = { 0 };
+ u8 lane_level_config[MAX_LANE_COUNT] = { 0 };
+ int pre_emphasis_adjust = -1, voltage_swing_adjust = -1;
+ const struct drm_dp_aux *aux = &it6505->aux;
+
+ it6505_dpcd_write(it6505, DP_DOWNSPREAD_CTRL,
+ it6505->enable_ssc ? DP_SPREAD_AMP_0_5 : 0x00);
+ it6505_dpcd_write(it6505, DP_TRAINING_PATTERN_SET,
+ DP_TRAINING_PATTERN_1);
+
+ while (loop_count < 5 && i < 10) {
+ i++;
+ if (!step_train_lane_voltage_para_set(it6505,
+ lane_voltage_pre_emphasis,
+ lane_level_config))
+ continue;
+ drm_dp_link_train_clock_recovery_delay(aux, it6505->dpcd);
+ drm_dp_dpcd_read_link_status(&it6505->aux, link_status);
+
+ if (drm_dp_clock_recovery_ok(link_status, it6505->lane_count)) {
+ it6505_set_bits(it6505, REG_TRAIN_CTRL0, FORCE_CR_DONE,
+ FORCE_CR_DONE);
+ return true;
+ }
+ DRM_DEV_DEBUG_DRIVER(&it6505->client->dev, "cr not done");
+
+ if (it6505_check_max_voltage_swing_reached(lane_level_config,
+ it6505->lane_count))
+ goto cr_train_fail;
+
+ for (j = 0; j < it6505->lane_count; j++) {
+ lane_voltage_pre_emphasis->voltage_swing[j] =
+ drm_dp_get_adjust_request_voltage(link_status,
+ j) >>
+ DP_TRAIN_VOLTAGE_SWING_SHIFT;
+ lane_voltage_pre_emphasis->pre_emphasis[j] =
+ drm_dp_get_adjust_request_pre_emphasis(link_status,
+ j) >>
+ DP_TRAIN_PRE_EMPHASIS_SHIFT;
+ if (voltage_swing_adjust ==
+ lane_voltage_pre_emphasis->voltage_swing[j] &&
+ pre_emphasis_adjust ==
+ lane_voltage_pre_emphasis->pre_emphasis[j]) {
+ loop_count++;
+ continue;
+ }
+
+ voltage_swing_adjust =
+ lane_voltage_pre_emphasis->voltage_swing[j];
+ pre_emphasis_adjust =
+ lane_voltage_pre_emphasis->pre_emphasis[j];
+ loop_count = 0;
+
+ if (voltage_swing_adjust + pre_emphasis_adjust >
+ MAX_EQ_LEVEL)
+ lane_voltage_pre_emphasis->voltage_swing[j] =
+ MAX_EQ_LEVEL -
+ lane_voltage_pre_emphasis
+ ->pre_emphasis[j];
+ }
+ }
+
+cr_train_fail:
+ it6505_dpcd_write(it6505, DP_TRAINING_PATTERN_SET,
+ DP_TRAINING_PATTERN_DISABLE);
+
+ return false;
+}
+
+static bool
+it6505_step_eq_train(struct it6505 *it6505,
+ struct it6505_step_train_para *lane_voltage_pre_emphasis)
+{
+ u8 loop_count = 0, i, link_status[DP_LINK_STATUS_SIZE] = { 0 };
+ u8 lane_level_config[MAX_LANE_COUNT] = { 0 };
+ const struct drm_dp_aux *aux = &it6505->aux;
+
+ it6505_dpcd_write(it6505, DP_TRAINING_PATTERN_SET,
+ DP_TRAINING_PATTERN_2);
+
+ while (loop_count < 6) {
+ loop_count++;
+
+ if (!step_train_lane_voltage_para_set(it6505,
+ lane_voltage_pre_emphasis,
+ lane_level_config))
+ continue;
+
+ drm_dp_link_train_channel_eq_delay(aux, it6505->dpcd);
+ drm_dp_dpcd_read_link_status(&it6505->aux, link_status);
+
+ if (!drm_dp_clock_recovery_ok(link_status, it6505->lane_count))
+ goto eq_train_fail;
+
+ if (drm_dp_channel_eq_ok(link_status, it6505->lane_count)) {
+ it6505_dpcd_write(it6505, DP_TRAINING_PATTERN_SET,
+ DP_TRAINING_PATTERN_DISABLE);
+ it6505_set_bits(it6505, REG_TRAIN_CTRL0, FORCE_EQ_DONE,
+ FORCE_EQ_DONE);
+ return true;
+ }
+ DRM_DEV_DEBUG_DRIVER(&it6505->client->dev, "eq not done");
+
+ for (i = 0; i < it6505->lane_count; i++) {
+ lane_voltage_pre_emphasis->voltage_swing[i] =
+ drm_dp_get_adjust_request_voltage(link_status,
+ i) >>
+ DP_TRAIN_VOLTAGE_SWING_SHIFT;
+ lane_voltage_pre_emphasis->pre_emphasis[i] =
+ drm_dp_get_adjust_request_pre_emphasis(link_status,
+ i) >>
+ DP_TRAIN_PRE_EMPHASIS_SHIFT;
+
+ if (lane_voltage_pre_emphasis->voltage_swing[i] +
+ lane_voltage_pre_emphasis->pre_emphasis[i] >
+ MAX_EQ_LEVEL)
+ lane_voltage_pre_emphasis->voltage_swing[i] =
+ 0x03 - lane_voltage_pre_emphasis
+ ->pre_emphasis[i];
+ }
+ }
+
+eq_train_fail:
+ it6505_dpcd_write(it6505, DP_TRAINING_PATTERN_SET,
+ DP_TRAINING_PATTERN_DISABLE);
+ return false;
+}
+
+static bool it6505_link_start_step_train(struct it6505 *it6505)
+{
+ int err;
+ struct it6505_step_train_para lane_voltage_pre_emphasis = {
+ .voltage_swing = { 0 },
+ .pre_emphasis = { 0 },
+ };
+
+ DRM_DEV_DEBUG_DRIVER(&it6505->client->dev, "start");
+ err = it6505_drm_dp_link_configure(it6505);
+
+ if (err < 0)
+ return false;
+ if (!it6505_step_cr_train(it6505, &lane_voltage_pre_emphasis))
+ return false;
+ if (!it6505_step_eq_train(it6505, &lane_voltage_pre_emphasis))
+ return false;
+ return true;
+}
+
+static bool it6505_get_video_status(struct it6505 *it6505)
+{
+ int reg_0d;
+
+ reg_0d = it6505_read(it6505, REG_SYSTEM_STS);
+
+ if (reg_0d < 0)
+ return false;
+
+ return reg_0d & VIDEO_STB;
+}
+
+static void it6505_reset_hdcp(struct it6505 *it6505)
+{
+ it6505->hdcp_status = HDCP_AUTH_IDLE;
+ /* Disable CP_Desired */
+ it6505_set_bits(it6505, REG_HDCP_CTRL1, HDCP_CP_ENABLE, 0x00);
+ it6505_set_bits(it6505, REG_RESET_CTRL, HDCP_RESET, HDCP_RESET);
+}
+
+static void it6505_start_hdcp(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+
+ DRM_DEV_DEBUG_DRIVER(dev, "start");
+ it6505_reset_hdcp(it6505);
+ queue_delayed_work(system_wq, &it6505->hdcp_work,
+ msecs_to_jiffies(2400));
+}
+
+static void it6505_stop_hdcp(struct it6505 *it6505)
+{
+ it6505_reset_hdcp(it6505);
+ cancel_delayed_work(&it6505->hdcp_work);
+}
+
+static bool it6505_hdcp_is_ksv_valid(u8 *ksv)
+{
+ int i, ones = 0;
+
+ /* KSV has 20 1's and 20 0's */
+ for (i = 0; i < DRM_HDCP_KSV_LEN; i++)
+ ones += hweight8(ksv[i]);
+ if (ones != 20)
+ return false;
+ return true;
+}
+
+static void it6505_hdcp_part1_auth(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+ u8 hdcp_bcaps;
+
+ it6505_set_bits(it6505, REG_RESET_CTRL, HDCP_RESET, 0x00);
+ /* Disable CP_Desired */
+ it6505_set_bits(it6505, REG_HDCP_CTRL1, HDCP_CP_ENABLE, 0x00);
+
+ usleep_range(1000, 1500);
+ hdcp_bcaps = it6505_dpcd_read(it6505, DP_AUX_HDCP_BCAPS);
+ DRM_DEV_DEBUG_DRIVER(dev, "DPCD[0x68028]: 0x%02x",
+ hdcp_bcaps);
+
+ if (!hdcp_bcaps)
+ return;
+
+ /* clear the repeater List Chk Done and fail bit */
+ it6505_set_bits(it6505, REG_HDCP_TRIGGER,
+ HDCP_TRIGGER_KSV_DONE | HDCP_TRIGGER_KSV_FAIL,
+ 0x00);
+
+ /* Enable An Generator */
+ it6505_set_bits(it6505, REG_HDCP_CTRL2, HDCP_AN_GEN, HDCP_AN_GEN);
+ /* delay1ms(10);*/
+ usleep_range(10000, 15000);
+ /* Stop An Generator */
+ it6505_set_bits(it6505, REG_HDCP_CTRL2, HDCP_AN_GEN, 0x00);
+
+ it6505_set_bits(it6505, REG_HDCP_CTRL1, HDCP_CP_ENABLE, HDCP_CP_ENABLE);
+
+ it6505_set_bits(it6505, REG_HDCP_TRIGGER, HDCP_TRIGGER_START,
+ HDCP_TRIGGER_START);
+
+ it6505->hdcp_status = HDCP_AUTH_GOING;
+}
+
+static int it6505_sha1_digest(struct it6505 *it6505, u8 *sha1_input,
+ unsigned int size, u8 *output_av)
+{
+ struct shash_desc *desc;
+ struct crypto_shash *tfm;
+ int err;
+ struct device *dev = &it6505->client->dev;
+
+ tfm = crypto_alloc_shash("sha1", 0, 0);
+ if (IS_ERR(tfm)) {
+ dev_err(dev, "crypto_alloc_shash sha1 failed");
+ return PTR_ERR(tfm);
+ }
+ desc = kzalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL);
+ if (!desc) {
+ crypto_free_shash(tfm);
+ return -ENOMEM;
+ }
+
+ desc->tfm = tfm;
+ err = crypto_shash_digest(desc, sha1_input, size, output_av);
+ if (err)
+ dev_err(dev, "crypto_shash_digest sha1 failed");
+
+ crypto_free_shash(tfm);
+ kfree(desc);
+ return err;
+}
+
+static int it6505_setup_sha1_input(struct it6505 *it6505, u8 *sha1_input)
+{
+ struct device *dev = &it6505->client->dev;
+ u8 binfo[2];
+ int down_stream_count, i, err, msg_count = 0;
+
+ err = it6505_get_dpcd(it6505, DP_AUX_HDCP_BINFO, binfo,
+ ARRAY_SIZE(binfo));
+
+ if (err < 0) {
+ dev_err(dev, "Read binfo value Fail");
+ return err;
+ }
+
+ down_stream_count = binfo[0] & 0x7F;
+ DRM_DEV_DEBUG_DRIVER(dev, "binfo:0x%*ph", (int)ARRAY_SIZE(binfo),
+ binfo);
+
+ if ((binfo[0] & BIT(7)) || (binfo[1] & BIT(3))) {
+ dev_err(dev, "HDCP max cascade device exceed");
+ return 0;
+ }
+
+ if (!down_stream_count ||
+ down_stream_count > MAX_HDCP_DOWN_STREAM_COUNT) {
+ dev_err(dev, "HDCP down stream count Error %d",
+ down_stream_count);
+ return 0;
+ }
+
+ for (i = 0; i < down_stream_count; i++) {
+ err = it6505_get_dpcd(it6505, DP_AUX_HDCP_KSV_FIFO +
+ (i % 3) * DRM_HDCP_KSV_LEN,
+ sha1_input + msg_count,
+ DRM_HDCP_KSV_LEN);
+
+ if (err < 0)
+ return err;
+
+ msg_count += 5;
+ }
+
+ it6505->hdcp_down_stream_count = down_stream_count;
+ sha1_input[msg_count++] = binfo[0];
+ sha1_input[msg_count++] = binfo[1];
+
+ it6505_set_bits(it6505, REG_HDCP_CTRL2, HDCP_EN_M0_READ,
+ HDCP_EN_M0_READ);
+
+ err = regmap_bulk_read(it6505->regmap, REG_M0_0_7,
+ sha1_input + msg_count, 8);
+
+ it6505_set_bits(it6505, REG_HDCP_CTRL2, HDCP_EN_M0_READ, 0x00);
+
+ if (err < 0) {
+ dev_err(dev, " Warning, Read M value Fail");
+ return err;
+ }
+
+ msg_count += 8;
+
+ return msg_count;
+}
+
+static bool it6505_hdcp_part2_ksvlist_check(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+ u8 av[5][4], bv[5][4];
+ int i, err;
+
+ i = it6505_setup_sha1_input(it6505, it6505->sha1_input);
+ if (i <= 0) {
+ dev_err(dev, "SHA-1 Input length error %d", i);
+ return false;
+ }
+
+ it6505_sha1_digest(it6505, it6505->sha1_input, i, (u8 *)av);
+
+ err = it6505_get_dpcd(it6505, DP_AUX_HDCP_V_PRIME(0), (u8 *)bv,
+ sizeof(bv));
+
+ if (err < 0) {
+ dev_err(dev, "Read V' value Fail");
+ return false;
+ }
+
+ for (i = 0; i < 5; i++)
+ if (bv[i][3] != av[i][0] || bv[i][2] != av[i][1] ||
+ bv[i][1] != av[i][2] || bv[i][0] != av[i][3])
+ return false;
+
+ DRM_DEV_DEBUG_DRIVER(dev, "V' all match!!");
+ return true;
+}
+
+static void it6505_hdcp_wait_ksv_list(struct work_struct *work)
+{
+ struct it6505 *it6505 = container_of(work, struct it6505,
+ hdcp_wait_ksv_list);
+ struct device *dev = &it6505->client->dev;
+ unsigned int timeout = 5000;
+ u8 bstatus = 0;
+ bool ksv_list_check;
+
+ timeout /= 20;
+ while (timeout > 0) {
+ if (!it6505_get_sink_hpd_status(it6505))
+ return;
+
+ bstatus = it6505_dpcd_read(it6505, DP_AUX_HDCP_BSTATUS);
+
+ if (bstatus & DP_BSTATUS_READY)
+ break;
+
+ msleep(20);
+ timeout--;
+ }
+
+ if (timeout == 0) {
+ DRM_DEV_DEBUG_DRIVER(dev, "timeout and ksv list wait failed");
+ goto timeout;
+ }
+
+ ksv_list_check = it6505_hdcp_part2_ksvlist_check(it6505);
+ DRM_DEV_DEBUG_DRIVER(dev, "ksv list ready, ksv list check %s",
+ ksv_list_check ? "pass" : "fail");
+ if (ksv_list_check) {
+ it6505_set_bits(it6505, REG_HDCP_TRIGGER,
+ HDCP_TRIGGER_KSV_DONE, HDCP_TRIGGER_KSV_DONE);
+ return;
+ }
+timeout:
+ it6505_set_bits(it6505, REG_HDCP_TRIGGER,
+ HDCP_TRIGGER_KSV_DONE | HDCP_TRIGGER_KSV_FAIL,
+ HDCP_TRIGGER_KSV_DONE | HDCP_TRIGGER_KSV_FAIL);
+}
+
+static void it6505_hdcp_work(struct work_struct *work)
+{
+ struct it6505 *it6505 = container_of(work, struct it6505,
+ hdcp_work.work);
+ struct device *dev = &it6505->client->dev;
+ int ret;
+ u8 link_status[DP_LINK_STATUS_SIZE] = { 0 };
+
+ DRM_DEV_DEBUG_DRIVER(dev, "start");
+
+ if (!it6505_get_sink_hpd_status(it6505))
+ return;
+
+ ret = drm_dp_dpcd_read_link_status(&it6505->aux, link_status);
+ DRM_DEV_DEBUG_DRIVER(dev, "ret: %d link_status: %*ph", ret,
+ (int)sizeof(link_status), link_status);
+
+ if (ret < 0 || !drm_dp_channel_eq_ok(link_status, it6505->lane_count) ||
+ !it6505_get_video_status(it6505)) {
+ DRM_DEV_DEBUG_DRIVER(dev, "link train not done or no video");
+ return;
+ }
+
+ ret = it6505_get_dpcd(it6505, DP_AUX_HDCP_BKSV, it6505->bksvs,
+ ARRAY_SIZE(it6505->bksvs));
+ if (ret < 0) {
+ dev_err(dev, "fail to get bksv ret: %d", ret);
+ it6505_set_bits(it6505, REG_HDCP_TRIGGER,
+ HDCP_TRIGGER_KSV_FAIL, HDCP_TRIGGER_KSV_FAIL);
+ }
+
+ DRM_DEV_DEBUG_DRIVER(dev, "bksv = 0x%*ph",
+ (int)ARRAY_SIZE(it6505->bksvs), it6505->bksvs);
+
+ if (!it6505_hdcp_is_ksv_valid(it6505->bksvs)) {
+ dev_err(dev, "Display Port bksv not valid");
+ it6505_set_bits(it6505, REG_HDCP_TRIGGER,
+ HDCP_TRIGGER_KSV_FAIL, HDCP_TRIGGER_KSV_FAIL);
+ }
+
+ it6505_hdcp_part1_auth(it6505);
+}
+
+static void it6505_show_hdcp_info(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+ int i;
+ u8 *sha1 = it6505->sha1_input;
+
+ DRM_DEV_DEBUG_DRIVER(dev, "hdcp_status: %d is_repeater: %d",
+ it6505->hdcp_status, it6505->is_repeater);
+ DRM_DEV_DEBUG_DRIVER(dev, "bksv = 0x%*ph",
+ (int)ARRAY_SIZE(it6505->bksvs), it6505->bksvs);
+
+ if (it6505->is_repeater) {
+ DRM_DEV_DEBUG_DRIVER(dev, "hdcp_down_stream_count: %d",
+ it6505->hdcp_down_stream_count);
+ DRM_DEV_DEBUG_DRIVER(dev, "sha1_input: 0x%*ph",
+ (int)ARRAY_SIZE(it6505->sha1_input),
+ it6505->sha1_input);
+ for (i = 0; i < it6505->hdcp_down_stream_count; i++) {
+ DRM_DEV_DEBUG_DRIVER(dev, "KSV_%d = 0x%*ph", i,
+ DRM_HDCP_KSV_LEN, sha1);
+ sha1 += DRM_HDCP_KSV_LEN;
+ }
+ DRM_DEV_DEBUG_DRIVER(dev, "binfo: 0x%2ph M0: 0x%8ph",
+ sha1, sha1 + 2);
+ }
+}
+
+static void it6505_stop_link_train(struct it6505 *it6505)
+{
+ it6505->link_state = LINK_IDLE;
+ cancel_work_sync(&it6505->link_works);
+ it6505_write(it6505, REG_TRAIN_CTRL1, FORCE_RETRAIN);
+}
+
+static void it6505_link_train_ok(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+
+ it6505->link_state = LINK_OK;
+ /* disalbe mute enable avi info frame */
+ it6505_set_bits(it6505, REG_DATA_MUTE_CTRL, EN_VID_MUTE, 0x00);
+ it6505_set_bits(it6505, REG_INFOFRAME_CTRL,
+ EN_VID_CTRL_PKT, EN_VID_CTRL_PKT);
+
+ if (it6505_audio_input(it6505)) {
+ DRM_DEV_DEBUG_DRIVER(dev, "Enable audio!");
+ it6505_enable_audio(it6505);
+ }
+
+ if (it6505->hdcp_desired)
+ it6505_start_hdcp(it6505);
+}
+
+static void it6505_link_step_train_process(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+ int ret, i, step_retry = 3;
+
+ DRM_DEV_DEBUG_DRIVER(dev, "Start step train");
+
+ if (it6505->sink_count == 0) {
+ DRM_DEV_DEBUG_DRIVER(dev, "it6505->sink_count:%d, force eq",
+ it6505->sink_count);
+ it6505_set_bits(it6505, REG_TRAIN_CTRL0, FORCE_EQ_DONE,
+ FORCE_EQ_DONE);
+ return;
+ }
+
+ if (!it6505->step_train) {
+ DRM_DEV_DEBUG_DRIVER(dev, "not support step train");
+ return;
+ }
+
+ /* step training start here */
+ for (i = 0; i < step_retry; i++) {
+ it6505_link_reset_step_train(it6505);
+ ret = it6505_link_start_step_train(it6505);
+ DRM_DEV_DEBUG_DRIVER(dev, "step train %s, retry:%d times",
+ ret ? "pass" : "failed", i + 1);
+ if (ret) {
+ it6505_link_train_ok(it6505);
+ return;
+ }
+ }
+
+ DRM_DEV_DEBUG_DRIVER(dev, "training fail");
+ it6505->link_state = LINK_IDLE;
+ it6505_video_reset(it6505);
+}
+
+static void it6505_link_training_work(struct work_struct *work)
+{
+ struct it6505 *it6505 = container_of(work, struct it6505, link_works);
+ struct device *dev = &it6505->client->dev;
+ int ret;
+
+ DRM_DEV_DEBUG_DRIVER(dev, "it6505->sink_count: %d",
+ it6505->sink_count);
+
+ if (!it6505_get_sink_hpd_status(it6505))
+ return;
+
+ it6505_link_training_setup(it6505);
+ it6505_reset_hdcp(it6505);
+ it6505_aux_reset(it6505);
+
+ if (it6505->auto_train_retry < 1) {
+ it6505_link_step_train_process(it6505);
+ return;
+ }
+
+ ret = it6505_link_start_auto_train(it6505);
+ DRM_DEV_DEBUG_DRIVER(dev, "auto train %s, auto_train_retry: %d",
+ ret ? "pass" : "failed", it6505->auto_train_retry);
+ it6505->auto_train_retry--;
+
+ if (ret) {
+ it6505_link_train_ok(it6505);
+ return;
+ }
+
+ it6505_dump(it6505);
+}
+
+static void it6505_plugged_status_to_codec(struct it6505 *it6505)
+{
+ enum drm_connector_status status = it6505->connector_status;
+
+ if (it6505->plugged_cb && it6505->codec_dev)
+ it6505->plugged_cb(it6505->codec_dev,
+ status == connector_status_connected);
+}
+
+static int it6505_process_hpd_irq(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+ int ret, dpcd_sink_count, dp_irq_vector, bstatus;
+ u8 link_status[DP_LINK_STATUS_SIZE];
+
+ if (!it6505_get_sink_hpd_status(it6505)) {
+ DRM_DEV_DEBUG_DRIVER(dev, "HPD_IRQ HPD low");
+ it6505->sink_count = 0;
+ return 0;
+ }
+
+ ret = it6505_dpcd_read(it6505, DP_SINK_COUNT);
+ if (ret < 0)
+ return ret;
+
+ dpcd_sink_count = DP_GET_SINK_COUNT(ret);
+ DRM_DEV_DEBUG_DRIVER(dev, "dpcd_sink_count: %d it6505->sink_count:%d",
+ dpcd_sink_count, it6505->sink_count);
+
+ if (it6505->branch_device && dpcd_sink_count != it6505->sink_count) {
+ memset(it6505->dpcd, 0, sizeof(it6505->dpcd));
+ it6505->sink_count = dpcd_sink_count;
+ it6505_reset_logic(it6505);
+ it6505_int_mask_enable(it6505);
+ it6505_init(it6505);
+ return 0;
+ }
+
+ dp_irq_vector = it6505_dpcd_read(it6505, DP_DEVICE_SERVICE_IRQ_VECTOR);
+ if (dp_irq_vector < 0)
+ return dp_irq_vector;
+
+ DRM_DEV_DEBUG_DRIVER(dev, "dp_irq_vector = 0x%02x", dp_irq_vector);
+
+ if (dp_irq_vector & DP_CP_IRQ) {
+ it6505_set_bits(it6505, REG_HDCP_TRIGGER, HDCP_TRIGGER_CPIRQ,
+ HDCP_TRIGGER_CPIRQ);
+
+ bstatus = it6505_dpcd_read(it6505, DP_AUX_HDCP_BSTATUS);
+ if (bstatus < 0)
+ return bstatus;
+
+ DRM_DEV_DEBUG_DRIVER(dev, "Bstatus = 0x%02x", bstatus);
+ }
+
+ ret = drm_dp_dpcd_read_link_status(&it6505->aux, link_status);
+ if (ret < 0) {
+ dev_err(dev, "Fail to read link status ret: %d", ret);
+ return ret;
+ }
+
+ DRM_DEV_DEBUG_DRIVER(dev, "link status = 0x%*ph",
+ (int)ARRAY_SIZE(link_status), link_status);
+
+ if (!drm_dp_channel_eq_ok(link_status, it6505->lane_count)) {
+ it6505->auto_train_retry = AUTO_TRAIN_RETRY;
+ it6505_video_reset(it6505);
+ }
+
+ return 0;
+}
+
+static void it6505_irq_hpd(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+
+ it6505->hpd_state = it6505_get_sink_hpd_status(it6505);
+ DRM_DEV_DEBUG_DRIVER(dev, "hpd change interrupt, change to %s",
+ it6505->hpd_state ? "high" : "low");
+
+ if (it6505->bridge.dev)
+ drm_helper_hpd_irq_event(it6505->bridge.dev);
+ DRM_DEV_DEBUG_DRIVER(dev, "it6505->sink_count: %d",
+ it6505->sink_count);
+
+ if (it6505->hpd_state) {
+ wait_for_completion_timeout(&it6505->wait_edid_complete,
+ msecs_to_jiffies(6000));
+ it6505_lane_termination_on(it6505);
+ it6505_lane_power_on(it6505);
+
+ /*
+ * for some dongle which issue HPD_irq
+ * when sink count change from 0->1
+ * it6505 not able to receive HPD_IRQ
+ * if HW never go into trainig done
+ */
+
+ if (it6505->branch_device && it6505->sink_count == 0)
+ schedule_work(&it6505->link_works);
+
+ if (!it6505_get_video_status(it6505))
+ it6505_video_reset(it6505);
+
+ it6505_calc_video_info(it6505);
+ } else {
+ memset(it6505->dpcd, 0, sizeof(it6505->dpcd));
+
+ if (it6505->hdcp_desired)
+ it6505_stop_hdcp(it6505);
+
+ it6505_video_disable(it6505);
+ it6505_disable_audio(it6505);
+ it6505_stop_link_train(it6505);
+ it6505_lane_off(it6505);
+ it6505_link_reset_step_train(it6505);
+ }
+}
+
+static void it6505_irq_hpd_irq(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+
+ DRM_DEV_DEBUG_DRIVER(dev, "hpd_irq interrupt");
+
+ if (it6505_process_hpd_irq(it6505) < 0)
+ DRM_DEV_DEBUG_DRIVER(dev, "process hpd_irq fail!");
+}
+
+static void it6505_irq_scdt(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+ bool data;
+
+ data = it6505_get_video_status(it6505);
+ DRM_DEV_DEBUG_DRIVER(dev, "video stable change interrupt, %s",
+ data ? "stable" : "unstable");
+ it6505_calc_video_info(it6505);
+ it6505_link_reset_step_train(it6505);
+
+ if (data)
+ schedule_work(&it6505->link_works);
+}
+
+static void it6505_irq_hdcp_done(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+
+ DRM_DEV_DEBUG_DRIVER(dev, "hdcp done interrupt");
+ it6505->hdcp_status = HDCP_AUTH_DONE;
+ it6505_show_hdcp_info(it6505);
+}
+
+static void it6505_irq_hdcp_fail(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+
+ DRM_DEV_DEBUG_DRIVER(dev, "hdcp fail interrupt");
+ it6505->hdcp_status = HDCP_AUTH_IDLE;
+ it6505_show_hdcp_info(it6505);
+ it6505_start_hdcp(it6505);
+}
+
+static void it6505_irq_aux_cmd_fail(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+
+ DRM_DEV_DEBUG_DRIVER(dev, "AUX PC Request Fail Interrupt");
+}
+
+static void it6505_irq_hdcp_ksv_check(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+
+ DRM_DEV_DEBUG_DRIVER(dev, "HDCP event Interrupt");
+ schedule_work(&it6505->hdcp_wait_ksv_list);
+}
+
+static void it6505_irq_audio_fifo_error(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+
+ DRM_DEV_DEBUG_DRIVER(dev, "audio fifo error Interrupt");
+
+ if (it6505_audio_input(it6505))
+ it6505_enable_audio(it6505);
+}
+
+static void it6505_irq_link_train_fail(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+
+ DRM_DEV_DEBUG_DRIVER(dev, "link training fail interrupt");
+ schedule_work(&it6505->link_works);
+}
+
+static void it6505_irq_video_fifo_error(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+
+ DRM_DEV_DEBUG_DRIVER(dev, "video fifo overflow interrupt");
+ it6505->auto_train_retry = AUTO_TRAIN_RETRY;
+ flush_work(&it6505->link_works);
+ it6505_stop_hdcp(it6505);
+ it6505_video_reset(it6505);
+}
+
+static void it6505_irq_io_latch_fifo_overflow(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+
+ DRM_DEV_DEBUG_DRIVER(dev, "IO latch fifo overflow interrupt");
+ it6505->auto_train_retry = AUTO_TRAIN_RETRY;
+ flush_work(&it6505->link_works);
+ it6505_stop_hdcp(it6505);
+ it6505_video_reset(it6505);
+}
+
+static bool it6505_test_bit(unsigned int bit, const unsigned int *addr)
+{
+ return 1 & (addr[bit / BITS_PER_BYTE] >> (bit % BITS_PER_BYTE));
+}
+
+static irqreturn_t it6505_int_threaded_handler(int unused, void *data)
+{
+ struct it6505 *it6505 = data;
+ struct device *dev = &it6505->client->dev;
+ static const struct {
+ int bit;
+ void (*handler)(struct it6505 *it6505);
+ } irq_vec[] = {
+ { BIT_INT_HPD, it6505_irq_hpd },
+ { BIT_INT_HPD_IRQ, it6505_irq_hpd_irq },
+ { BIT_INT_SCDT, it6505_irq_scdt },
+ { BIT_INT_HDCP_FAIL, it6505_irq_hdcp_fail },
+ { BIT_INT_HDCP_DONE, it6505_irq_hdcp_done },
+ { BIT_INT_AUX_CMD_FAIL, it6505_irq_aux_cmd_fail },
+ { BIT_INT_HDCP_KSV_CHECK, it6505_irq_hdcp_ksv_check },
+ { BIT_INT_AUDIO_FIFO_ERROR, it6505_irq_audio_fifo_error },
+ { BIT_INT_LINK_TRAIN_FAIL, it6505_irq_link_train_fail },
+ { BIT_INT_VID_FIFO_ERROR, it6505_irq_video_fifo_error },
+ { BIT_INT_IO_FIFO_OVERFLOW, it6505_irq_io_latch_fifo_overflow },
+ };
+ int int_status[3], i;
+
+ msleep(100);
+ mutex_lock(&it6505->extcon_lock);
+
+ if (it6505->enable_drv_hold || !it6505->powered)
+ goto unlock;
+
+ int_status[0] = it6505_read(it6505, INT_STATUS_01);
+ int_status[1] = it6505_read(it6505, INT_STATUS_02);
+ int_status[2] = it6505_read(it6505, INT_STATUS_03);
+
+ it6505_write(it6505, INT_STATUS_01, int_status[0]);
+ it6505_write(it6505, INT_STATUS_02, int_status[1]);
+ it6505_write(it6505, INT_STATUS_03, int_status[2]);
+
+ DRM_DEV_DEBUG_DRIVER(dev, "reg06 = 0x%02x", int_status[0]);
+ DRM_DEV_DEBUG_DRIVER(dev, "reg07 = 0x%02x", int_status[1]);
+ DRM_DEV_DEBUG_DRIVER(dev, "reg08 = 0x%02x", int_status[2]);
+ it6505_debug_print(it6505, REG_SYSTEM_STS, "");
+
+ if (it6505_test_bit(irq_vec[0].bit, (unsigned int *)int_status))
+ irq_vec[0].handler(it6505);
+
+ if (!it6505->hpd_state)
+ goto unlock;
+
+ for (i = 1; i < ARRAY_SIZE(irq_vec); i++) {
+ if (it6505_test_bit(irq_vec[i].bit, (unsigned int *)int_status))
+ irq_vec[i].handler(it6505);
+ }
+
+unlock:
+ mutex_unlock(&it6505->extcon_lock);
+
+ return IRQ_HANDLED;
+}
+
+static int it6505_poweron(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+ struct it6505_platform_data *pdata = &it6505->pdata;
+ int err;
+
+ DRM_DEV_DEBUG_DRIVER(dev, "it6505 start powered on");
+
+ if (it6505->powered) {
+ DRM_DEV_DEBUG_DRIVER(dev, "it6505 already powered on");
+ return 0;
+ }
+
+ if (pdata->pwr18) {
+ err = regulator_enable(pdata->pwr18);
+ if (err) {
+ DRM_DEV_DEBUG_DRIVER(dev, "Failed to enable VDD18: %d",
+ err);
+ return err;
+ }
+ }
+
+ if (pdata->ovdd) {
+ /* time interval between IVDD and OVDD at least be 1ms */
+ usleep_range(1000, 2000);
+ err = regulator_enable(pdata->ovdd);
+ if (err) {
+ regulator_disable(pdata->pwr18);
+ return err;
+ }
+ }
+ /* time interval between OVDD and SYSRSTN at least be 10ms */
+ if (pdata->gpiod_reset) {
+ usleep_range(10000, 20000);
+ gpiod_set_value_cansleep(pdata->gpiod_reset, 0);
+ usleep_range(1000, 2000);
+ gpiod_set_value_cansleep(pdata->gpiod_reset, 1);
+ usleep_range(10000, 20000);
+ }
+
+ it6505_reset_logic(it6505);
+ it6505_int_mask_enable(it6505);
+ it6505_init(it6505);
+ it6505_lane_off(it6505);
+
+ it6505->powered = true;
+
+ return 0;
+}
+
+static int it6505_poweroff(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+ struct it6505_platform_data *pdata = &it6505->pdata;
+ int err;
+
+ DRM_DEV_DEBUG_DRIVER(dev, "it6505 start power off");
+
+ if (!it6505->powered) {
+ DRM_DEV_DEBUG_DRIVER(dev, "power had been already off");
+ return 0;
+ }
+
+ if (pdata->gpiod_reset)
+ gpiod_set_value_cansleep(pdata->gpiod_reset, 0);
+
+ if (pdata->pwr18) {
+ err = regulator_disable(pdata->pwr18);
+ if (err)
+ return err;
+ }
+
+ if (pdata->ovdd) {
+ err = regulator_disable(pdata->ovdd);
+ if (err)
+ return err;
+ }
+
+ it6505->powered = false;
+ it6505->sink_count = 0;
+
+ return 0;
+}
+
+static enum drm_connector_status it6505_detect(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+ enum drm_connector_status status = connector_status_disconnected;
+ int dp_sink_count;
+
+ DRM_DEV_DEBUG_DRIVER(dev, "it6505->sink_count:%d powered:%d",
+ it6505->sink_count, it6505->powered);
+
+ mutex_lock(&it6505->mode_lock);
+
+ if (!it6505->powered)
+ goto unlock;
+
+ if (it6505->enable_drv_hold) {
+ status = it6505_get_sink_hpd_status(it6505) ?
+ connector_status_connected :
+ connector_status_disconnected;
+ goto unlock;
+ }
+
+ if (it6505_get_sink_hpd_status(it6505)) {
+ it6505_aux_on(it6505);
+ it6505_drm_dp_link_probe(&it6505->aux, &it6505->link);
+ it6505_drm_dp_link_power_up(&it6505->aux, &it6505->link);
+ it6505->auto_train_retry = AUTO_TRAIN_RETRY;
+
+ if (it6505->dpcd[0] == 0) {
+ it6505_get_dpcd(it6505, DP_DPCD_REV, it6505->dpcd,
+ ARRAY_SIZE(it6505->dpcd));
+ it6505_variable_config(it6505);
+ it6505_parse_link_capabilities(it6505);
+ }
+
+ dp_sink_count = it6505_dpcd_read(it6505, DP_SINK_COUNT);
+ it6505->sink_count = DP_GET_SINK_COUNT(dp_sink_count);
+ DRM_DEV_DEBUG_DRIVER(dev, "it6505->sink_count:%d branch:%d",
+ it6505->sink_count, it6505->branch_device);
+
+ if (it6505->branch_device) {
+ status = (it6505->sink_count != 0) ?
+ connector_status_connected :
+ connector_status_disconnected;
+ } else {
+ status = connector_status_connected;
+ }
+ } else {
+ it6505->sink_count = 0;
+ memset(it6505->dpcd, 0, sizeof(it6505->dpcd));
+ }
+
+unlock:
+ if (it6505->connector_status != status) {
+ it6505->connector_status = status;
+ it6505_plugged_status_to_codec(it6505);
+ }
+
+ mutex_unlock(&it6505->mode_lock);
+
+ return status;
+}
+
+static int it6505_extcon_notifier(struct notifier_block *self,
+ unsigned long event, void *ptr)
+{
+ struct it6505 *it6505 = container_of(self, struct it6505, event_nb);
+
+ schedule_work(&it6505->extcon_wq);
+ return NOTIFY_DONE;
+}
+
+static void it6505_extcon_work(struct work_struct *work)
+{
+ struct it6505 *it6505 = container_of(work, struct it6505, extcon_wq);
+ struct device *dev = &it6505->client->dev;
+ int state = extcon_get_state(it6505->extcon, EXTCON_DISP_DP);
+ unsigned int pwroffretry = 0;
+
+ if (it6505->enable_drv_hold)
+ return;
+
+ mutex_lock(&it6505->extcon_lock);
+
+ DRM_DEV_DEBUG_DRIVER(dev, "EXTCON_DISP_DP = 0x%02x", state);
+ if (state > 0) {
+ DRM_DEV_DEBUG_DRIVER(dev, "start to power on");
+ msleep(100);
+ it6505_poweron(it6505);
+ } else {
+ DRM_DEV_DEBUG_DRIVER(dev, "start to power off");
+ while (it6505_poweroff(it6505) && pwroffretry++ < 5) {
+ DRM_DEV_DEBUG_DRIVER(dev, "power off fail %d times",
+ pwroffretry);
+ }
+
+ drm_helper_hpd_irq_event(it6505->bridge.dev);
+ memset(it6505->dpcd, 0, sizeof(it6505->dpcd));
+ DRM_DEV_DEBUG_DRIVER(dev, "power off it6505 success!");
+ }
+
+ mutex_unlock(&it6505->extcon_lock);
+}
+
+static int it6505_use_notifier_module(struct it6505 *it6505)
+{
+ int ret;
+ struct device *dev = &it6505->client->dev;
+
+ it6505->event_nb.notifier_call = it6505_extcon_notifier;
+ INIT_WORK(&it6505->extcon_wq, it6505_extcon_work);
+ ret = devm_extcon_register_notifier(&it6505->client->dev,
+ it6505->extcon, EXTCON_DISP_DP,
+ &it6505->event_nb);
+ if (ret) {
+ dev_err(dev, "failed to register notifier for DP");
+ return ret;
+ }
+
+ schedule_work(&it6505->extcon_wq);
+
+ return 0;
+}
+
+static void it6505_remove_notifier_module(struct it6505 *it6505)
+{
+ if (it6505->extcon) {
+ devm_extcon_unregister_notifier(&it6505->client->dev,
+ it6505->extcon, EXTCON_DISP_DP,
+ &it6505->event_nb);
+
+ flush_work(&it6505->extcon_wq);
+ }
+}
+
+static void __maybe_unused it6505_delayed_audio(struct work_struct *work)
+{
+ struct it6505 *it6505 = container_of(work, struct it6505,
+ delayed_audio.work);
+
+ DRM_DEV_DEBUG_DRIVER(&it6505->client->dev, "start");
+
+ if (!it6505->powered)
+ return;
+
+ if (!it6505->enable_drv_hold)
+ it6505_enable_audio(it6505);
+}
+
+static int __maybe_unused it6505_audio_setup_hw_params(struct it6505 *it6505,
+ struct hdmi_codec_params
+ *params)
+{
+ struct device *dev = &it6505->client->dev;
+ int i = 0;
+
+ DRM_DEV_DEBUG_DRIVER(dev, "%s %d Hz, %d bit, %d channels\n", __func__,
+ params->sample_rate, params->sample_width,
+ params->cea.channels);
+
+ if (!it6505->bridge.encoder)
+ return -ENODEV;
+
+ if (params->cea.channels <= 1 || params->cea.channels > 8) {
+ DRM_DEV_DEBUG_DRIVER(dev, "channel number: %d not support",
+ it6505->audio.channel_count);
+ return -EINVAL;
+ }
+
+ it6505->audio.channel_count = params->cea.channels;
+
+ while (i < ARRAY_SIZE(audio_sample_rate_map) &&
+ params->sample_rate !=
+ audio_sample_rate_map[i].sample_rate_value) {
+ i++;
+ }
+ if (i == ARRAY_SIZE(audio_sample_rate_map)) {
+ DRM_DEV_DEBUG_DRIVER(dev, "sample rate: %d Hz not support",
+ params->sample_rate);
+ return -EINVAL;
+ }
+ it6505->audio.sample_rate = audio_sample_rate_map[i].rate;
+
+ switch (params->sample_width) {
+ case 16:
+ it6505->audio.word_length = WORD_LENGTH_16BIT;
+ break;
+ case 18:
+ it6505->audio.word_length = WORD_LENGTH_18BIT;
+ break;
+ case 20:
+ it6505->audio.word_length = WORD_LENGTH_20BIT;
+ break;
+ case 24:
+ case 32:
+ it6505->audio.word_length = WORD_LENGTH_24BIT;
+ break;
+ default:
+ DRM_DEV_DEBUG_DRIVER(dev, "wordlength: %d bit not support",
+ params->sample_width);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void __maybe_unused it6505_audio_shutdown(struct device *dev, void *data)
+{
+ struct it6505 *it6505 = dev_get_drvdata(dev);
+
+ if (it6505->powered)
+ it6505_disable_audio(it6505);
+}
+
+static int __maybe_unused it6505_audio_hook_plugged_cb(struct device *dev,
+ void *data,
+ hdmi_codec_plugged_cb fn,
+ struct device *codec_dev)
+{
+ struct it6505 *it6505 = data;
+
+ it6505->plugged_cb = fn;
+ it6505->codec_dev = codec_dev;
+ it6505_plugged_status_to_codec(it6505);
+
+ return 0;
+}
+
+static inline struct it6505 *bridge_to_it6505(struct drm_bridge *bridge)
+{
+ return container_of(bridge, struct it6505, bridge);
+}
+
+static int it6505_bridge_attach(struct drm_bridge *bridge,
+ enum drm_bridge_attach_flags flags)
+{
+ struct it6505 *it6505 = bridge_to_it6505(bridge);
+ struct device *dev = &it6505->client->dev;
+ int ret;
+
+ if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
+ DRM_ERROR("DRM_BRIDGE_ATTACH_NO_CONNECTOR must be supplied");
+ return -EINVAL;
+ }
+
+ if (!bridge->encoder) {
+ dev_err(dev, "Parent encoder object not found");
+ return -ENODEV;
+ }
+
+ /* Register aux channel */
+ it6505->aux.name = "DP-AUX";
+ it6505->aux.dev = dev;
+ it6505->aux.drm_dev = bridge->dev;
+ it6505->aux.transfer = it6505_aux_transfer;
+
+ ret = drm_dp_aux_register(&it6505->aux);
+
+ if (ret < 0) {
+ dev_err(dev, "Failed to register aux: %d", ret);
+ return ret;
+ }
+
+ if (it6505->extcon) {
+ ret = it6505_use_notifier_module(it6505);
+ if (ret < 0) {
+ dev_err(dev, "use notifier module failed");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void it6505_bridge_detach(struct drm_bridge *bridge)
+{
+ struct it6505 *it6505 = bridge_to_it6505(bridge);
+
+ flush_work(&it6505->link_works);
+ it6505_remove_notifier_module(it6505);
+}
+
+static enum drm_mode_status
+it6505_bridge_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_info *info,
+ const struct drm_display_mode *mode)
+{
+ struct it6505 *it6505 = bridge_to_it6505(bridge);
+
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ return MODE_NO_INTERLACE;
+
+ if (mode->clock > DPI_PIXEL_CLK_MAX)
+ return MODE_CLOCK_HIGH;
+
+ it6505->video_info.clock = mode->clock;
+
+ return MODE_OK;
+}
+
+static void it6505_bridge_atomic_enable(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_state)
+{
+ struct it6505 *it6505 = bridge_to_it6505(bridge);
+ struct device *dev = &it6505->client->dev;
+ struct drm_atomic_state *state = old_state->base.state;
+ struct hdmi_avi_infoframe frame;
+ struct drm_crtc_state *crtc_state;
+ struct drm_connector_state *conn_state;
+ struct drm_display_mode *mode;
+ struct drm_connector *connector;
+ int ret;
+
+ DRM_DEV_DEBUG_DRIVER(dev, "start");
+
+ connector = drm_atomic_get_new_connector_for_encoder(state,
+ bridge->encoder);
+
+ if (WARN_ON(!connector))
+ return;
+
+ conn_state = drm_atomic_get_new_connector_state(state, connector);
+
+ if (WARN_ON(!conn_state))
+ return;
+
+ crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
+
+ if (WARN_ON(!crtc_state))
+ return;
+
+ mode = &crtc_state->adjusted_mode;
+
+ if (WARN_ON(!mode))
+ return;
+
+ ret = drm_hdmi_avi_infoframe_from_display_mode(&frame,
+ connector,
+ mode);
+ if (ret)
+ dev_err(dev, "Failed to setup AVI infoframe: %d", ret);
+
+ it6505_update_video_parameter(it6505, mode);
+
+ ret = it6505_send_video_infoframe(it6505, &frame);
+
+ if (ret)
+ dev_err(dev, "Failed to send AVI infoframe: %d", ret);
+
+ it6505_int_mask_enable(it6505);
+ it6505_video_reset(it6505);
+}
+
+static void it6505_bridge_atomic_disable(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_state)
+{
+ struct it6505 *it6505 = bridge_to_it6505(bridge);
+ struct device *dev = &it6505->client->dev;
+
+ DRM_DEV_DEBUG_DRIVER(dev, "start");
+
+ if (it6505->powered)
+ it6505_video_disable(it6505);
+}
+
+static enum drm_connector_status
+it6505_bridge_detect(struct drm_bridge *bridge)
+{
+ struct it6505 *it6505 = bridge_to_it6505(bridge);
+
+ return it6505_detect(it6505);
+}
+
+static struct edid *it6505_bridge_get_edid(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+ struct it6505 *it6505 = bridge_to_it6505(bridge);
+ struct device *dev = &it6505->client->dev;
+ struct edid *edid;
+
+ edid = drm_do_get_edid(connector, it6505_get_edid_block, it6505);
+
+ if (!edid) {
+ DRM_DEV_DEBUG_DRIVER(dev, "failed to get edid!");
+ return NULL;
+ }
+
+ return edid;
+}
+
+static const struct drm_bridge_funcs it6505_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 = it6505_bridge_attach,
+ .detach = it6505_bridge_detach,
+ .mode_valid = it6505_bridge_mode_valid,
+ .atomic_enable = it6505_bridge_atomic_enable,
+ .atomic_disable = it6505_bridge_atomic_disable,
+ .detect = it6505_bridge_detect,
+ .get_edid = it6505_bridge_get_edid,
+};
+
+static __maybe_unused int it6505_bridge_resume(struct device *dev)
+{
+ struct it6505 *it6505 = dev_get_drvdata(dev);
+
+ return it6505_poweron(it6505);
+}
+
+static __maybe_unused int it6505_bridge_suspend(struct device *dev)
+{
+ struct it6505 *it6505 = dev_get_drvdata(dev);
+
+ return it6505_poweroff(it6505);
+}
+
+static SIMPLE_DEV_PM_OPS(it6505_bridge_pm_ops, it6505_bridge_suspend,
+ it6505_bridge_resume);
+
+static int it6505_init_pdata(struct it6505 *it6505)
+{
+ struct it6505_platform_data *pdata = &it6505->pdata;
+ struct device *dev = &it6505->client->dev;
+
+ /* 1.0V digital core power regulator */
+ pdata->pwr18 = devm_regulator_get(dev, "pwr18");
+ if (IS_ERR(pdata->pwr18)) {
+ dev_err(dev, "pwr18 regulator not found");
+ return PTR_ERR(pdata->pwr18);
+ }
+
+ pdata->ovdd = devm_regulator_get(dev, "ovdd");
+ if (IS_ERR(pdata->ovdd)) {
+ dev_err(dev, "ovdd regulator not found");
+ return PTR_ERR(pdata->ovdd);
+ }
+
+ pdata->gpiod_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(pdata->gpiod_reset)) {
+ dev_err(dev, "gpiod_reset gpio not found");
+ return PTR_ERR(pdata->gpiod_reset);
+ }
+
+ return 0;
+}
+
+static void it6505_parse_dt(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+ u32 *afe_setting = &it6505->afe_setting;
+
+ it6505->lane_swap_disabled =
+ device_property_read_bool(dev, "no-laneswap");
+
+ if (it6505->lane_swap_disabled)
+ it6505->lane_swap = false;
+
+ if (device_property_read_u32(dev, "afe-setting", afe_setting) == 0) {
+ if (*afe_setting >= ARRAY_SIZE(afe_setting_table)) {
+ dev_err(dev, "afe setting error, use default");
+ *afe_setting = 0;
+ }
+ } else {
+ *afe_setting = 0;
+ }
+ DRM_DEV_DEBUG_DRIVER(dev, "using afe_setting: %d", *afe_setting);
+}
+
+static ssize_t receive_timing_debugfs_show(struct file *file, char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ struct it6505 *it6505 = file->private_data;
+ struct drm_display_mode *vid = &it6505->video_info;
+ u8 read_buf[READ_BUFFER_SIZE];
+ u8 *str = read_buf, *end = read_buf + PAGE_SIZE;
+ ssize_t ret, count;
+
+ if (!it6505)
+ return -ENODEV;
+
+ it6505_calc_video_info(it6505);
+ str += scnprintf(str, end - str, "---video timing---\n");
+ str += scnprintf(str, end - str, "PCLK:%d.%03dMHz\n",
+ vid->clock / 1000, vid->clock % 1000);
+ str += scnprintf(str, end - str, "HTotal:%d\n", vid->htotal);
+ str += scnprintf(str, end - str, "HActive:%d\n", vid->hdisplay);
+ str += scnprintf(str, end - str, "HFrontPorch:%d\n",
+ vid->hsync_start - vid->hdisplay);
+ str += scnprintf(str, end - str, "HSyncWidth:%d\n",
+ vid->hsync_end - vid->hsync_start);
+ str += scnprintf(str, end - str, "HBackPorch:%d\n",
+ vid->htotal - vid->hsync_end);
+ str += scnprintf(str, end - str, "VTotal:%d\n", vid->vtotal);
+ str += scnprintf(str, end - str, "VActive:%d\n", vid->vdisplay);
+ str += scnprintf(str, end - str, "VFrontPorch:%d\n",
+ vid->vsync_start - vid->vdisplay);
+ str += scnprintf(str, end - str, "VSyncWidth:%d\n",
+ vid->vsync_end - vid->vsync_start);
+ str += scnprintf(str, end - str, "VBackPorch:%d\n",
+ vid->vtotal - vid->vsync_end);
+
+ count = str - read_buf;
+ ret = simple_read_from_buffer(buf, len, ppos, read_buf, count);
+
+ return ret;
+}
+
+static int force_power_on_off_debugfs_write(void *data, u64 value)
+{
+ struct it6505 *it6505 = data;
+
+ if (!it6505)
+ return -ENODEV;
+
+ if (value)
+ it6505_poweron(it6505);
+ else
+ it6505_poweroff(it6505);
+
+ return 0;
+}
+
+static int enable_drv_hold_debugfs_show(void *data, u64 *buf)
+{
+ struct it6505 *it6505 = data;
+
+ if (!it6505)
+ return -ENODEV;
+
+ *buf = it6505->enable_drv_hold;
+
+ return 0;
+}
+
+static int enable_drv_hold_debugfs_write(void *data, u64 drv_hold)
+{
+ struct it6505 *it6505 = data;
+
+ if (!it6505)
+ return -ENODEV;
+
+ it6505->enable_drv_hold = drv_hold;
+
+ if (it6505->enable_drv_hold) {
+ it6505_int_mask_disable(it6505);
+ } else {
+ it6505_clear_int(it6505);
+ it6505_int_mask_enable(it6505);
+
+ if (it6505->powered) {
+ it6505->connector_status =
+ it6505_get_sink_hpd_status(it6505) ?
+ connector_status_connected :
+ connector_status_disconnected;
+ } else {
+ it6505->connector_status =
+ connector_status_disconnected;
+ }
+ }
+
+ return 0;
+}
+
+static const struct file_operations receive_timing_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = receive_timing_debugfs_show,
+ .llseek = default_llseek,
+};
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_force_power, NULL,
+ force_power_on_off_debugfs_write, "%llu\n");
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_enable_drv_hold, enable_drv_hold_debugfs_show,
+ enable_drv_hold_debugfs_write, "%llu\n");
+
+static const struct debugfs_entries debugfs_entry[] = {
+ { "receive_timing", &receive_timing_fops },
+ { "force_power_on_off", &fops_force_power },
+ { "enable_drv_hold", &fops_enable_drv_hold },
+ { NULL, NULL },
+};
+
+static void debugfs_create_files(struct it6505 *it6505)
+{
+ int i = 0;
+
+ while (debugfs_entry[i].name && debugfs_entry[i].fops) {
+ debugfs_create_file(debugfs_entry[i].name, 0644,
+ it6505->debugfs, it6505,
+ debugfs_entry[i].fops);
+ i++;
+ }
+}
+
+static void debugfs_init(struct it6505 *it6505)
+{
+ struct device *dev = &it6505->client->dev;
+
+ it6505->debugfs = debugfs_create_dir(DEBUGFS_DIR_NAME, NULL);
+
+ if (IS_ERR(it6505->debugfs)) {
+ dev_err(dev, "failed to create debugfs root");
+ return;
+ }
+
+ debugfs_create_files(it6505);
+}
+
+static void it6505_debugfs_remove(struct it6505 *it6505)
+{
+ debugfs_remove_recursive(it6505->debugfs);
+}
+
+static void it6505_shutdown(struct i2c_client *client)
+{
+ struct it6505 *it6505 = dev_get_drvdata(&client->dev);
+
+ if (it6505->powered)
+ it6505_lane_off(it6505);
+}
+
+static int it6505_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct it6505 *it6505;
+ struct device *dev = &client->dev;
+ struct extcon_dev *extcon;
+ int err, intp_irq;
+
+ it6505 = devm_kzalloc(&client->dev, sizeof(*it6505), GFP_KERNEL);
+ if (!it6505)
+ return -ENOMEM;
+
+ mutex_init(&it6505->extcon_lock);
+ mutex_init(&it6505->mode_lock);
+ mutex_init(&it6505->aux_lock);
+
+ it6505->bridge.of_node = client->dev.of_node;
+ it6505->connector_status = connector_status_disconnected;
+ it6505->client = client;
+ i2c_set_clientdata(client, it6505);
+
+ /* get extcon device from DTS */
+ extcon = extcon_get_edev_by_phandle(dev, 0);
+ if (PTR_ERR(extcon) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ if (IS_ERR(extcon)) {
+ dev_err(dev, "can not get extcon device!");
+ return PTR_ERR(extcon);
+ }
+
+ it6505->extcon = extcon;
+
+ it6505->regmap = devm_regmap_init_i2c(client, &it6505_regmap_config);
+ if (IS_ERR(it6505->regmap)) {
+ dev_err(dev, "regmap i2c init failed");
+ err = PTR_ERR(it6505->regmap);
+ return err;
+ }
+
+ err = it6505_init_pdata(it6505);
+ if (err) {
+ dev_err(dev, "Failed to initialize pdata: %d", err);
+ return err;
+ }
+
+ it6505_parse_dt(it6505);
+
+ intp_irq = client->irq;
+
+ if (!intp_irq) {
+ dev_err(dev, "Failed to get INTP IRQ");
+ err = -ENODEV;
+ return err;
+ }
+
+ err = devm_request_threaded_irq(&client->dev, intp_irq, NULL,
+ it6505_int_threaded_handler,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "it6505-intp", it6505);
+ if (err) {
+ dev_err(dev, "Failed to request INTP threaded IRQ: %d", err);
+ return err;
+ }
+
+ INIT_WORK(&it6505->link_works, it6505_link_training_work);
+ INIT_WORK(&it6505->hdcp_wait_ksv_list, it6505_hdcp_wait_ksv_list);
+ INIT_DELAYED_WORK(&it6505->hdcp_work, it6505_hdcp_work);
+ init_completion(&it6505->wait_edid_complete);
+ memset(it6505->dpcd, 0, sizeof(it6505->dpcd));
+ it6505->powered = false;
+ it6505->enable_drv_hold = DEFAULT_DRV_HOLD;
+
+ if (DEFAULT_PWR_ON)
+ it6505_poweron(it6505);
+
+ DRM_DEV_DEBUG_DRIVER(dev, "it6505 device name: %s", dev_name(dev));
+ debugfs_init(it6505);
+
+ it6505->bridge.funcs = &it6505_bridge_funcs;
+ it6505->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
+ it6505->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID |
+ DRM_BRIDGE_OP_HPD;
+ drm_bridge_add(&it6505->bridge);
+
+ return 0;
+}
+
+static int it6505_i2c_remove(struct i2c_client *client)
+{
+ struct it6505 *it6505 = i2c_get_clientdata(client);
+
+ drm_bridge_remove(&it6505->bridge);
+ drm_dp_aux_unregister(&it6505->aux);
+ it6505_debugfs_remove(it6505);
+ it6505_poweroff(it6505);
+
+ return 0;
+}
+
+static const struct i2c_device_id it6505_id[] = {
+ { "it6505", 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, it6505_id);
+
+static const struct of_device_id it6505_of_match[] = {
+ { .compatible = "ite,it6505" },
+ { }
+};
+
+static struct i2c_driver it6505_i2c_driver = {
+ .driver = {
+ .name = "it6505",
+ .of_match_table = it6505_of_match,
+ .pm = &it6505_bridge_pm_ops,
+ },
+ .probe = it6505_i2c_probe,
+ .remove = it6505_i2c_remove,
+ .shutdown = it6505_shutdown,
+ .id_table = it6505_id,
+};
+
+module_i2c_driver(it6505_i2c_driver);
+
+MODULE_AUTHOR("Allen Chen <allen.chen@ite.com.tw>");
+MODULE_DESCRIPTION("IT6505 DisplayPort Transmitter driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/bridge/lontium-lt9611.c b/drivers/gpu/drm/bridge/lontium-lt9611.c
index feb128a4557d..63df2e8a8abc 100644
--- a/drivers/gpu/drm/bridge/lontium-lt9611.c
+++ b/drivers/gpu/drm/bridge/lontium-lt9611.c
@@ -1164,7 +1164,11 @@ static int lt9611_probe(struct i2c_client *client,
lt9611_enable_hpd_interrupts(lt9611);
- return lt9611_audio_init(dev, lt9611);
+ ret = lt9611_audio_init(dev, lt9611);
+ if (ret)
+ goto err_remove_bridge;
+
+ return 0;
err_remove_bridge:
drm_bridge_remove(&lt9611->bridge);
diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi.c
index 9282e61dfbf0..30aacd939dc3 100644
--- a/drivers/gpu/drm/bridge/nwl-dsi.c
+++ b/drivers/gpu/drm/bridge/nwl-dsi.c
@@ -862,18 +862,19 @@ nwl_dsi_bridge_mode_set(struct drm_bridge *bridge,
memcpy(&dsi->mode, adjusted_mode, sizeof(dsi->mode));
drm_mode_debug_printmodeline(adjusted_mode);
- pm_runtime_get_sync(dev);
+ if (pm_runtime_resume_and_get(dev) < 0)
+ return;
if (clk_prepare_enable(dsi->lcdif_clk) < 0)
- return;
+ goto runtime_put;
if (clk_prepare_enable(dsi->core_clk) < 0)
- return;
+ goto runtime_put;
/* Step 1 from DSI reset-out instructions */
ret = reset_control_deassert(dsi->rst_pclk);
if (ret < 0) {
DRM_DEV_ERROR(dev, "Failed to deassert PCLK: %d\n", ret);
- return;
+ goto runtime_put;
}
/* Step 2 from DSI reset-out instructions */
@@ -883,13 +884,18 @@ nwl_dsi_bridge_mode_set(struct drm_bridge *bridge,
ret = reset_control_deassert(dsi->rst_esc);
if (ret < 0) {
DRM_DEV_ERROR(dev, "Failed to deassert ESC: %d\n", ret);
- return;
+ goto runtime_put;
}
ret = reset_control_deassert(dsi->rst_byte);
if (ret < 0) {
DRM_DEV_ERROR(dev, "Failed to deassert BYTE: %d\n", ret);
- return;
+ goto runtime_put;
}
+
+ return;
+
+runtime_put:
+ pm_runtime_put_sync(dev);
}
static void
diff --git a/drivers/gpu/drm/drm_cache.c b/drivers/gpu/drm/drm_cache.c
index f19d9acbe959..2c3fa5677f7e 100644
--- a/drivers/gpu/drm/drm_cache.c
+++ b/drivers/gpu/drm/drm_cache.c
@@ -112,8 +112,7 @@ drm_clflush_pages(struct page *pages[], unsigned long num_pages)
kunmap_atomic(page_virtual);
}
#else
- pr_err("Architecture has no drm_cache.c support\n");
- WARN_ON_ONCE(1);
+ WARN_ONCE(1, "Architecture has no drm_cache.c support\n");
#endif
}
EXPORT_SYMBOL(drm_clflush_pages);
@@ -143,8 +142,7 @@ drm_clflush_sg(struct sg_table *st)
if (wbinvd_on_all_cpus())
pr_err("Timed out waiting for cache flush\n");
#else
- pr_err("Architecture has no drm_cache.c support\n");
- WARN_ON_ONCE(1);
+ WARN_ONCE(1, "Architecture has no drm_cache.c support\n");
#endif
}
EXPORT_SYMBOL(drm_clflush_sg);
@@ -177,8 +175,7 @@ drm_clflush_virt_range(void *addr, unsigned long length)
if (wbinvd_on_all_cpus())
pr_err("Timed out waiting for cache flush\n");
#else
- pr_err("Architecture has no drm_cache.c support\n");
- WARN_ON_ONCE(1);
+ WARN_ONCE(1, "Architecture has no drm_cache.c support\n");
#endif
}
EXPORT_SYMBOL(drm_clflush_virt_range);
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index a504542238ed..a7663f9a11d2 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -5340,6 +5340,9 @@ drm_reset_display_info(struct drm_connector *connector)
info->rgb_quant_range_selectable = false;
memset(&info->hdmi, 0, sizeof(info->hdmi));
+ info->edid_hdmi_rgb444_dc_modes = 0;
+ info->edid_hdmi_ycbcr444_dc_modes = 0;
+
info->non_desktop = 0;
memset(&info->monitor_range, 0, sizeof(info->monitor_range));
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index ed43b987d306..f15127a32f7a 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -2346,6 +2346,7 @@ static int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
fbi->fbops = &drm_fbdev_fb_ops;
fbi->screen_size = sizes->surface_height * fb->pitches[0];
fbi->fix.smem_len = fbi->screen_size;
+ fbi->flags = FBINFO_DEFAULT;
drm_fb_helper_fill_info(fbi, fb_helper, sizes);
@@ -2353,19 +2354,21 @@ static int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
fbi->screen_buffer = vzalloc(fbi->screen_size);
if (!fbi->screen_buffer)
return -ENOMEM;
+ fbi->flags |= FBINFO_VIRTFB | FBINFO_READS_FAST;
fbi->fbdefio = &drm_fbdev_defio;
-
fb_deferred_io_init(fbi);
} else {
/* buffer is mapped for HW framebuffer */
ret = drm_client_buffer_vmap(fb_helper->buffer, &map);
if (ret)
return ret;
- if (map.is_iomem)
+ if (map.is_iomem) {
fbi->screen_base = map.vaddr_iomem;
- else
+ } else {
fbi->screen_buffer = map.vaddr;
+ fbi->flags |= FBINFO_VIRTFB;
+ }
/*
* Shamelessly leak the physical address to user-space. As
diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c
index 07f5abc875e9..4562a8b86579 100644
--- a/drivers/gpu/drm/drm_framebuffer.c
+++ b/drivers/gpu/drm/drm_framebuffer.c
@@ -309,7 +309,7 @@ drm_internal_framebuffer_create(struct drm_device *dev,
}
if (r->flags & DRM_MODE_FB_MODIFIERS &&
- !dev->mode_config.allow_fb_modifiers) {
+ dev->mode_config.fb_modifiers_not_supported) {
DRM_DEBUG_KMS("driver does not support fb modifiers\n");
return ERR_PTR(-EINVAL);
}
@@ -594,7 +594,7 @@ int drm_mode_getfb2_ioctl(struct drm_device *dev,
r->pixel_format = fb->format->format;
r->flags = 0;
- if (dev->mode_config.allow_fb_modifiers)
+ if (!dev->mode_config.fb_modifiers_not_supported)
r->flags |= DRM_MODE_FB_MODIFIERS;
for (i = 0; i < ARRAY_SIZE(r->handles); i++) {
@@ -607,7 +607,7 @@ int drm_mode_getfb2_ioctl(struct drm_device *dev,
for (i = 0; i < fb->format->num_planes; i++) {
r->pitches[i] = fb->pitches[i];
r->offsets[i] = fb->offsets[i];
- if (dev->mode_config.allow_fb_modifiers)
+ if (!dev->mode_config.fb_modifiers_not_supported)
r->modifier[i] = fb->modifier;
}
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index 8b8744dcf691..51fcf1298023 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -297,7 +297,7 @@ static int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_
req->value = 64;
break;
case DRM_CAP_ADDFB2_MODIFIERS:
- req->value = dev->mode_config.allow_fb_modifiers;
+ req->value = !dev->mode_config.fb_modifiers_not_supported;
break;
case DRM_CAP_CRTC_IN_VBLANK_EVENT:
req->value = 1;
diff --git a/drivers/gpu/drm/drm_plane.c b/drivers/gpu/drm/drm_plane.c
index deeec60a3315..bf0daa8d9bbd 100644
--- a/drivers/gpu/drm/drm_plane.c
+++ b/drivers/gpu/drm/drm_plane.c
@@ -237,6 +237,9 @@ static int __drm_universal_plane_init(struct drm_device *dev,
const char *name, va_list ap)
{
struct drm_mode_config *config = &dev->mode_config;
+ static const uint64_t default_modifiers[] = {
+ DRM_FORMAT_MOD_LINEAR,
+ };
unsigned int format_modifier_count = 0;
int ret;
@@ -277,16 +280,16 @@ static int __drm_universal_plane_init(struct drm_device *dev,
while (*temp_modifiers++ != DRM_FORMAT_MOD_INVALID)
format_modifier_count++;
+ } else {
+ if (!dev->mode_config.fb_modifiers_not_supported) {
+ format_modifiers = default_modifiers;
+ format_modifier_count = ARRAY_SIZE(default_modifiers);
+ }
}
/* autoset the cap and check for consistency across all planes */
- if (format_modifier_count) {
- drm_WARN_ON(dev, !config->allow_fb_modifiers &&
- !list_empty(&config->plane_list));
- config->allow_fb_modifiers = true;
- } else {
- drm_WARN_ON(dev, config->allow_fb_modifiers);
- }
+ drm_WARN_ON(dev, config->fb_modifiers_not_supported &&
+ format_modifier_count);
plane->modifier_count = format_modifier_count;
plane->modifiers = kmalloc_array(format_modifier_count,
@@ -341,7 +344,7 @@ static int __drm_universal_plane_init(struct drm_device *dev,
drm_object_attach_property(&plane->base, config->prop_src_h, 0);
}
- if (config->allow_fb_modifiers)
+ if (format_modifier_count)
create_in_format_blob(dev, plane);
return 0;
@@ -368,8 +371,8 @@ static int __drm_universal_plane_init(struct drm_device *dev,
* drm_universal_plane_init() to let the DRM managed resource infrastructure
* take care of cleanup and deallocation.
*
- * Drivers supporting modifiers must set @format_modifiers on all their planes,
- * even those that only support DRM_FORMAT_MOD_LINEAR.
+ * Drivers that only support the DRM_FORMAT_MOD_LINEAR modifier support may set
+ * @format_modifiers to NULL. The plane will advertise the linear modifier.
*
* Returns:
* Zero on success, error code on failure.
diff --git a/drivers/gpu/drm/drm_privacy_screen.c b/drivers/gpu/drm/drm_privacy_screen.c
index 03b149cc455b..45c080134488 100644
--- a/drivers/gpu/drm/drm_privacy_screen.c
+++ b/drivers/gpu/drm/drm_privacy_screen.c
@@ -379,6 +379,7 @@ static void drm_privacy_screen_device_release(struct device *dev)
* drm_privacy_screen_register - register a privacy-screen
* @parent: parent-device for the privacy-screen
* @ops: &struct drm_privacy_screen_ops pointer with ops for the privacy-screen
+ * @data: Private data owned by the privacy screen provider
*
* Create and register a privacy-screen.
*
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
index 9743b6b17447..c68498497c0b 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -455,6 +455,9 @@ static int exynos_drm_init(void)
{
int ret;
+ if (drm_firmware_drivers_only())
+ return -ENODEV;
+
ret = exynos_drm_register_devices();
if (ret)
return ret;
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
index 660fe573db96..7a503bf08d0f 100644
--- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
@@ -24,6 +24,7 @@
#include <drm/drm_fb_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_modeset_helper.h>
+#include <drm/drm_module.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
@@ -368,7 +369,7 @@ static struct platform_driver fsl_dcu_drm_platform_driver = {
},
};
-module_platform_driver(fsl_dcu_drm_platform_driver);
+drm_module_platform_driver(fsl_dcu_drm_platform_driver);
MODULE_DESCRIPTION("Freescale DCU DRM Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c
index 65cf1c79dd7c..eeb681be9c95 100644
--- a/drivers/gpu/drm/gma500/psb_drv.c
+++ b/drivers/gpu/drm/gma500/psb_drv.c
@@ -538,6 +538,9 @@ static struct pci_driver psb_pci_driver = {
static int __init psb_init(void)
{
+ if (drm_firmware_drivers_only())
+ return -ENODEV;
+
return pci_register_driver(&psb_pci_driver);
}
diff --git a/drivers/gpu/drm/hisilicon/hibmc/Kconfig b/drivers/gpu/drm/hisilicon/hibmc/Kconfig
index 43943e980203..073adfe438dd 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/Kconfig
+++ b/drivers/gpu/drm/hisilicon/hibmc/Kconfig
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
config DRM_HISI_HIBMC
tristate "DRM Support for Hisilicon Hibmc"
- depends on DRM && PCI && ARM64
+ depends on DRM && PCI && (ARM64 || COMPILE_TEST)
select DRM_KMS_HELPER
select DRM_VRAM_HELPER
select DRM_TTM
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c
index 98ae9a48f3fe..3cf057269f2a 100644
--- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c
+++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c
@@ -23,6 +23,7 @@
#include <drm/drm_fb_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_module.h>
#include <drm/drm_of.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
@@ -307,7 +308,7 @@ static struct platform_driver kirin_drm_platform_driver = {
},
};
-module_platform_driver(kirin_drm_platform_driver);
+drm_module_platform_driver(kirin_drm_platform_driver);
MODULE_AUTHOR("Xinliang Liu <xinliang.liu@linaro.org>");
MODULE_AUTHOR("Xinliang Liu <z.liuxinliang@hisilicon.com>");
diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c
index 00e53de4812b..4a8941fa0815 100644
--- a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c
+++ b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c
@@ -305,6 +305,9 @@ static int __init hyperv_init(void)
{
int ret;
+ if (drm_firmware_drivers_only())
+ return -ENODEV;
+
ret = pci_register_driver(&hyperv_pci_driver);
if (ret != 0)
return ret;
diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c
index cb685fe2039b..a57812ec36b1 100644
--- a/drivers/gpu/drm/imx/imx-drm-core.c
+++ b/drivers/gpu/drm/imx/imx-drm-core.c
@@ -341,6 +341,9 @@ static struct platform_driver * const drivers[] = {
static int __init imx_drm_init(void)
{
+ if (drm_firmware_drivers_only())
+ return -ENODEV;
+
return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
}
module_init(imx_drm_init);
diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
index 542c4af70661..7f10d6eed549 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
@@ -1543,6 +1543,9 @@ static int ingenic_drm_init(void)
{
int err;
+ if (drm_firmware_drivers_only())
+ return -ENODEV;
+
if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU)) {
err = platform_driver_register(ingenic_ipu_driver_ptr);
if (err)
diff --git a/drivers/gpu/drm/kmb/kmb_drv.c b/drivers/gpu/drm/kmb/kmb_drv.c
index ed2424350773..76fef0880504 100644
--- a/drivers/gpu/drm/kmb/kmb_drv.c
+++ b/drivers/gpu/drm/kmb/kmb_drv.c
@@ -18,6 +18,7 @@
#include <drm/drm_fb_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_module.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
@@ -628,7 +629,7 @@ static struct platform_driver kmb_platform_driver = {
},
};
-module_platform_driver(kmb_platform_driver);
+drm_module_platform_driver(kmb_platform_driver);
MODULE_AUTHOR("Intel Corporation");
MODULE_DESCRIPTION("Keembay Display driver");
diff --git a/drivers/gpu/drm/mcde/mcde_drv.c b/drivers/gpu/drm/mcde/mcde_drv.c
index 5b5afc6aaf8e..0b2910e69b42 100644
--- a/drivers/gpu/drm/mcde/mcde_drv.c
+++ b/drivers/gpu/drm/mcde/mcde_drv.c
@@ -491,6 +491,9 @@ static int __init mcde_drm_register(void)
{
int ret;
+ if (drm_firmware_drivers_only())
+ return -ENODEV;
+
ret = platform_register_drivers(component_drivers,
ARRAY_SIZE(component_drivers));
if (ret)
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index 56ff8c57ef8f..dd029307be7d 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -236,6 +236,9 @@ static int mtk_drm_kms_init(struct drm_device *drm)
struct device *dma_dev;
int ret;
+ if (drm_firmware_drivers_only())
+ return -ENODEV;
+
if (!iommu_present(&platform_bus_type))
return -EPROBE_DEFER;
diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c
index 26aeaf0ab86e..93a7a033a3e8 100644
--- a/drivers/gpu/drm/meson/meson_drv.c
+++ b/drivers/gpu/drm/meson/meson_drv.c
@@ -22,6 +22,7 @@
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_module.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
@@ -542,7 +543,7 @@ static struct platform_driver meson_drm_platform_driver = {
},
};
-module_platform_driver(meson_drm_platform_driver);
+drm_module_platform_driver(meson_drm_platform_driver);
MODULE_AUTHOR("Jasper St. Pierre <jstpierre@mecheye.net>");
MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c
index 740108a006ba..217844d71ab5 100644
--- a/drivers/gpu/drm/mgag200/mgag200_drv.c
+++ b/drivers/gpu/drm/mgag200/mgag200_drv.c
@@ -14,6 +14,7 @@
#include <drm/drm_drv.h>
#include <drm/drm_file.h>
#include <drm/drm_ioctl.h>
+#include <drm/drm_module.h>
#include <drm/drm_pciids.h>
#include "mgag200_drv.h"
@@ -375,24 +376,7 @@ static struct pci_driver mgag200_pci_driver = {
.remove = mgag200_pci_remove,
};
-static int __init mgag200_init(void)
-{
- if (drm_firmware_drivers_only() && mgag200_modeset == -1)
- return -EINVAL;
-
- if (mgag200_modeset == 0)
- return -EINVAL;
-
- return pci_register_driver(&mgag200_pci_driver);
-}
-
-static void __exit mgag200_exit(void)
-{
- pci_unregister_driver(&mgag200_pci_driver);
-}
-
-module_init(mgag200_init);
-module_exit(mgag200_exit);
+drm_module_pci_driver_if_modeset(mgag200_pci_driver, mgag200_modeset);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 555666e3f960..ff19c5a94d6f 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -512,6 +512,9 @@ static int msm_drm_init(struct device *dev, const struct drm_driver *drv)
struct msm_kms *kms;
int ret, i;
+ if (drm_firmware_drivers_only())
+ return -ENODEV;
+
ddev = drm_dev_alloc(drv, dev);
if (IS_ERR(ddev)) {
DRM_DEV_ERROR(dev, "failed to allocate drm_device\n");
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c
index 375f26d4a417..9d71c55a31c0 100644
--- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c
+++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c
@@ -25,6 +25,7 @@
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_mode_config.h>
+#include <drm/drm_module.h>
#include <drm/drm_of.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
@@ -258,8 +259,7 @@ static int mxsfb_load(struct drm_device *drm,
ret = mxsfb_attach_bridge(mxsfb);
if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(drm->dev, "Cannot connect bridge: %d\n", ret);
+ dev_err_probe(drm->dev, ret, "Cannot connect bridge\n");
goto err_vblank;
}
@@ -419,7 +419,7 @@ static struct platform_driver mxsfb_platform_driver = {
},
};
-module_platform_driver(mxsfb_platform_driver);
+drm_module_platform_driver(mxsfb_platform_driver);
MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
MODULE_DESCRIPTION("Freescale MXS DRM/KMS driver");
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index 2b460835a438..2cd0932b3d68 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -708,10 +708,12 @@ nouveau_display_create(struct drm_device *dev)
&disp->disp);
if (ret == 0) {
nouveau_display_create_properties(dev);
- if (disp->disp.object.oclass < NV50_DISP)
+ if (disp->disp.object.oclass < NV50_DISP) {
+ dev->mode_config.fb_modifiers_not_supported = true;
ret = nv04_display_create(dev);
- else
+ } else {
ret = nv50_display_create(dev);
+ }
}
} else {
ret = 0;
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
index 2720a58ccd90..eaf67b9e5f12 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -727,6 +727,9 @@ static int omapdrm_init(struct omap_drm_private *priv, struct device *dev)
DBG("%s", dev_name(dev));
+ if (drm_firmware_drivers_only())
+ return -ENODEV;
+
/* Allocate and initialize the DRM device. */
ddev = drm_dev_alloc(&omap_drm_driver, dev);
if (IS_ERR(ddev))
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 434c2861bb40..0aec5a10b064 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -106,6 +106,7 @@ config DRM_PANEL_EDP
depends on PM
select VIDEOMODE_HELPERS
select DRM_DP_AUX_BUS
+ select DRM_DP_HELPER
help
DRM panel driver for dumb eDP panels that need at most a regulator and
a GPIO to be powered up. Optionally a backlight can be attached so
diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c
index e4b16421500b..1cb6f0c224bb 100644
--- a/drivers/gpu/drm/qxl/qxl_drv.c
+++ b/drivers/gpu/drm/qxl/qxl_drv.c
@@ -39,6 +39,7 @@
#include <drm/drm_drv.h>
#include <drm/drm_file.h>
#include <drm/drm_gem_ttm_helper.h>
+#include <drm/drm_module.h>
#include <drm/drm_modeset_helper.h>
#include <drm/drm_prime.h>
#include <drm/drm_probe_helper.h>
@@ -269,6 +270,16 @@ static struct pci_driver qxl_pci_driver = {
.driver.pm = &qxl_pm_ops,
};
+static const struct drm_ioctl_desc qxl_ioctls[] = {
+ DRM_IOCTL_DEF_DRV(QXL_ALLOC, qxl_alloc_ioctl, DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(QXL_MAP, qxl_map_ioctl, DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(QXL_EXECBUFFER, qxl_execbuffer_ioctl, DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(QXL_UPDATE_AREA, qxl_update_area_ioctl, DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(QXL_GETPARAM, qxl_getparam_ioctl, DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(QXL_CLIENTCAP, qxl_clientcap_ioctl, DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(QXL_ALLOC_SURF, qxl_alloc_surf_ioctl, DRM_AUTH),
+};
+
static struct drm_driver qxl_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
@@ -282,6 +293,7 @@ static struct drm_driver qxl_driver = {
.gem_prime_import_sg_table = qxl_gem_prime_import_sg_table,
.fops = &qxl_fops,
.ioctls = qxl_ioctls,
+ .num_ioctls = ARRAY_SIZE(qxl_ioctls),
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.date = DRIVER_DATE,
@@ -292,24 +304,7 @@ static struct drm_driver qxl_driver = {
.release = qxl_drm_release,
};
-static int __init qxl_init(void)
-{
- if (drm_firmware_drivers_only() && qxl_modeset == -1)
- return -EINVAL;
-
- if (qxl_modeset == 0)
- return -EINVAL;
- qxl_driver.num_ioctls = qxl_max_ioctls;
- return pci_register_driver(&qxl_pci_driver);
-}
-
-static void __exit qxl_exit(void)
-{
- pci_unregister_driver(&qxl_pci_driver);
-}
-
-module_init(qxl_init);
-module_exit(qxl_exit);
+drm_module_pci_driver_if_modeset(qxl_pci_driver, qxl_modeset);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h
index 359266d9e860..29641ceaab7d 100644
--- a/drivers/gpu/drm/qxl/qxl_drv.h
+++ b/drivers/gpu/drm/qxl/qxl_drv.h
@@ -65,7 +65,6 @@ struct dma_buf_map;
#define QXL_DEBUGFS_MAX_COMPONENTS 32
extern int qxl_num_crtc;
-extern int qxl_max_ioctls;
#define QXL_INTERRUPT_MASK (\
QXL_INTERRUPT_DISPLAY |\
@@ -261,9 +260,6 @@ struct qxl_device {
int qxl_debugfs_fence_init(struct qxl_device *rdev);
-extern const struct drm_ioctl_desc qxl_ioctls[];
-extern int qxl_max_ioctl;
-
int qxl_device_init(struct qxl_device *qdev, struct pci_dev *pdev);
void qxl_device_fini(struct qxl_device *qdev);
@@ -457,4 +453,13 @@ struct qxl_drv_surface *
qxl_surface_lookup(struct drm_device *dev, int surface_id);
void qxl_surface_evict(struct qxl_device *qdev, struct qxl_bo *surf, bool freeing);
+/* qxl_ioctl.c */
+int qxl_alloc_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
+int qxl_map_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
+int qxl_execbuffer_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
+int qxl_update_area_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
+int qxl_getparam_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
+int qxl_clientcap_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
+int qxl_alloc_surf_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
+
#endif
diff --git a/drivers/gpu/drm/qxl/qxl_ioctl.c b/drivers/gpu/drm/qxl/qxl_ioctl.c
index 38aabcbe2238..30f58b21372a 100644
--- a/drivers/gpu/drm/qxl/qxl_ioctl.c
+++ b/drivers/gpu/drm/qxl/qxl_ioctl.c
@@ -33,8 +33,7 @@
* TODO: allocating a new gem(in qxl_bo) for each request.
* This is wasteful since bo's are page aligned.
*/
-static int qxl_alloc_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+int qxl_alloc_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
struct qxl_device *qdev = to_qxl(dev);
struct drm_qxl_alloc *qxl_alloc = data;
@@ -61,8 +60,7 @@ static int qxl_alloc_ioctl(struct drm_device *dev, void *data,
return 0;
}
-static int qxl_map_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+int qxl_map_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
struct qxl_device *qdev = to_qxl(dev);
struct drm_qxl_map *qxl_map = data;
@@ -272,8 +270,7 @@ out_free_reloc:
return ret;
}
-static int qxl_execbuffer_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+int qxl_execbuffer_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
struct qxl_device *qdev = to_qxl(dev);
struct drm_qxl_execbuffer *execbuffer = data;
@@ -297,8 +294,7 @@ static int qxl_execbuffer_ioctl(struct drm_device *dev, void *data,
return 0;
}
-static int qxl_update_area_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file)
+int qxl_update_area_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
{
struct qxl_device *qdev = to_qxl(dev);
struct drm_qxl_update_area *update_area = data;
@@ -347,8 +343,7 @@ out:
return ret;
}
-static int qxl_getparam_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+int qxl_getparam_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
struct qxl_device *qdev = to_qxl(dev);
struct drm_qxl_getparam *param = data;
@@ -366,8 +361,7 @@ static int qxl_getparam_ioctl(struct drm_device *dev, void *data,
return 0;
}
-static int qxl_clientcap_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+int qxl_clientcap_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
struct qxl_device *qdev = to_qxl(dev);
struct pci_dev *pdev = to_pci_dev(dev->dev);
@@ -388,8 +382,7 @@ static int qxl_clientcap_ioctl(struct drm_device *dev, void *data,
return -ENOSYS;
}
-static int qxl_alloc_surf_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file)
+int qxl_alloc_surf_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
{
struct qxl_device *qdev = to_qxl(dev);
struct drm_qxl_alloc_surf *param = data;
@@ -422,23 +415,3 @@ static int qxl_alloc_surf_ioctl(struct drm_device *dev, void *data,
param->handle = handle;
return ret;
}
-
-const struct drm_ioctl_desc qxl_ioctls[] = {
- DRM_IOCTL_DEF_DRV(QXL_ALLOC, qxl_alloc_ioctl, DRM_AUTH),
-
- DRM_IOCTL_DEF_DRV(QXL_MAP, qxl_map_ioctl, DRM_AUTH),
-
- DRM_IOCTL_DEF_DRV(QXL_EXECBUFFER, qxl_execbuffer_ioctl,
- DRM_AUTH),
- DRM_IOCTL_DEF_DRV(QXL_UPDATE_AREA, qxl_update_area_ioctl,
- DRM_AUTH),
- DRM_IOCTL_DEF_DRV(QXL_GETPARAM, qxl_getparam_ioctl,
- DRM_AUTH),
- DRM_IOCTL_DEF_DRV(QXL_CLIENTCAP, qxl_clientcap_ioctl,
- DRM_AUTH),
-
- DRM_IOCTL_DEF_DRV(QXL_ALLOC_SURF, qxl_alloc_surf_ioctl,
- DRM_AUTH),
-};
-
-int qxl_max_ioctls = ARRAY_SIZE(qxl_ioctls);
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index 573154268d43..b9a07677a71e 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -1596,6 +1596,8 @@ int radeon_modeset_init(struct radeon_device *rdev)
rdev->ddev->mode_config.preferred_depth = 24;
rdev->ddev->mode_config.prefer_shadow = 1;
+ rdev->ddev->mode_config.fb_modifiers_not_supported = true;
+
rdev->ddev->mode_config.fb_base = rdev->mc.aper_base;
ret = radeon_modeset_create_props(rdev);
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
index 5a8131ef81d5..982e450233ed 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
@@ -701,6 +701,9 @@ static struct platform_driver rcar_du_platform_driver = {
static int __init rcar_du_init(void)
{
+ if (drm_firmware_drivers_only())
+ return -ENODEV;
+
rcar_du_of_init(rcar_du_of_table);
return platform_driver_register(&rcar_du_platform_driver);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index bec207de4544..ac190e2b1f7a 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -457,6 +457,9 @@ static int __init rockchip_drm_init(void)
{
int ret;
+ if (drm_firmware_drivers_only())
+ return -ENODEV;
+
num_rockchip_sub_drivers = 0;
ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_DRM_ROCKCHIP);
ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver,
diff --git a/drivers/gpu/drm/selftests/test-drm_framebuffer.c b/drivers/gpu/drm/selftests/test-drm_framebuffer.c
index 61b44d3a6a61..f6d66285c5fc 100644
--- a/drivers/gpu/drm/selftests/test-drm_framebuffer.c
+++ b/drivers/gpu/drm/selftests/test-drm_framebuffer.c
@@ -323,7 +323,6 @@ static struct drm_device mock_drm_device = {
.max_width = MAX_WIDTH,
.min_height = MIN_HEIGHT,
.max_height = MAX_HEIGHT,
- .allow_fb_modifiers = true,
.funcs = &mock_config_funcs,
},
};
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c
index 80078a9fd7f6..731cbad7520f 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_drv.c
+++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.c
@@ -18,6 +18,7 @@
#include <drm/drm_crtc_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_module.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
@@ -295,7 +296,7 @@ static struct platform_driver shmob_drm_platform_driver = {
},
};
-module_platform_driver(shmob_drm_platform_driver);
+drm_module_platform_driver(shmob_drm_platform_driver);
MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
MODULE_DESCRIPTION("Renesas SH Mobile DRM Driver");
diff --git a/drivers/gpu/drm/sprd/sprd_drm.c b/drivers/gpu/drm/sprd/sprd_drm.c
index a077e2d4d721..286edae95189 100644
--- a/drivers/gpu/drm/sprd/sprd_drm.c
+++ b/drivers/gpu/drm/sprd/sprd_drm.c
@@ -43,7 +43,6 @@ static void sprd_drm_mode_config_init(struct drm_device *drm)
drm->mode_config.min_height = 0;
drm->mode_config.max_width = 8192;
drm->mode_config.max_height = 8192;
- drm->mode_config.allow_fb_modifiers = true;
drm->mode_config.funcs = &sprd_drm_mode_config_funcs;
drm->mode_config.helper_private = &sprd_drm_mode_config_helper;
@@ -186,6 +185,9 @@ static struct platform_driver *sprd_drm_drivers[] = {
static int __init sprd_drm_init(void)
{
+ if (drm_firmware_drivers_only())
+ return -ENODEV;
+
return platform_register_drivers(sprd_drm_drivers,
ARRAY_SIZE(sprd_drm_drivers));
}
diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c
index c7efb43b83ee..860b2230aa08 100644
--- a/drivers/gpu/drm/sti/sti_drv.c
+++ b/drivers/gpu/drm/sti/sti_drv.c
@@ -287,6 +287,9 @@ static struct platform_driver * const drivers[] = {
static int sti_drm_init(void)
{
+ if (drm_firmware_drivers_only())
+ return -ENODEV;
+
return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
}
module_init(sti_drm_init);
diff --git a/drivers/gpu/drm/stm/drv.c b/drivers/gpu/drm/stm/drv.c
index 9f441aadf2d5..0da7cce2a1a2 100644
--- a/drivers/gpu/drm/stm/drv.c
+++ b/drivers/gpu/drm/stm/drv.c
@@ -22,6 +22,7 @@
#include <drm/drm_fb_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_module.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
@@ -241,7 +242,7 @@ static struct platform_driver stm_drm_platform_driver = {
},
};
-module_platform_driver(stm_drm_platform_driver);
+drm_module_platform_driver(stm_drm_platform_driver);
MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
index b630614b3d72..a3fd441dd9ad 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -19,6 +19,7 @@
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_module.h>
#include <drm/drm_of.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
@@ -441,7 +442,7 @@ static struct platform_driver sun4i_drv_platform_driver = {
.pm = &sun4i_drv_drm_pm_ops,
},
};
-module_platform_driver(sun4i_drv_platform_driver);
+drm_module_platform_driver(sun4i_drv_platform_driver);
MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index e9de91a4e7e8..9464f522e257 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -1410,6 +1410,9 @@ static int __init host1x_drm_init(void)
{
int err;
+ if (drm_firmware_drivers_only())
+ return -ENODEV;
+
err = host1x_driver_register(&host1x_drm_driver);
if (err < 0)
return err;
diff --git a/drivers/gpu/drm/tidss/tidss_drv.c b/drivers/gpu/drm/tidss/tidss_drv.c
index 7c784e90e40e..04cfff89ee51 100644
--- a/drivers/gpu/drm/tidss/tidss_drv.c
+++ b/drivers/gpu/drm/tidss/tidss_drv.c
@@ -17,6 +17,7 @@
#include <drm/drm_fb_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_managed.h>
+#include <drm/drm_module.h>
#include <drm/drm_probe_helper.h>
#include "tidss_dispc.h"
@@ -251,7 +252,7 @@ static struct platform_driver tidss_platform_driver = {
},
};
-module_platform_driver(tidss_platform_driver);
+drm_module_platform_driver(tidss_platform_driver);
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
MODULE_DESCRIPTION("TI Keystone DSS Driver");
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c
index cc567c87057d..eee3c447fbac 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c
@@ -604,6 +604,9 @@ static struct platform_driver tilcdc_platform_driver = {
static int __init tilcdc_drm_init(void)
{
+ if (drm_firmware_drivers_only())
+ return -ENODEV;
+
DBG("init");
tilcdc_panel_init();
return platform_driver_register(&tilcdc_platform_driver);
diff --git a/drivers/gpu/drm/tiny/arcpgu.c b/drivers/gpu/drm/tiny/arcpgu.c
index f8531c50a072..f0fa3b15c341 100644
--- a/drivers/gpu/drm/tiny/arcpgu.c
+++ b/drivers/gpu/drm/tiny/arcpgu.c
@@ -15,6 +15,7 @@
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_module.h>
#include <drm/drm_of.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
@@ -427,7 +428,7 @@ static struct platform_driver arcpgu_platform_driver = {
},
};
-module_platform_driver(arcpgu_platform_driver);
+drm_module_platform_driver(arcpgu_platform_driver);
MODULE_AUTHOR("Carlos Palminha <palminha@synopsys.com>");
MODULE_DESCRIPTION("ARC PGU DRM driver");
diff --git a/drivers/gpu/drm/tve200/tve200_drv.c b/drivers/gpu/drm/tve200/tve200_drv.c
index 7fa71c8bb828..6d9d2921abf4 100644
--- a/drivers/gpu/drm/tve200/tve200_drv.c
+++ b/drivers/gpu/drm/tve200/tve200_drv.c
@@ -43,6 +43,7 @@
#include <drm/drm_fb_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_module.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_probe_helper.h>
@@ -266,7 +267,7 @@ static struct platform_driver tve200_driver = {
.probe = tve200_probe,
.remove = tve200_remove,
};
-module_platform_driver(tve200_driver);
+drm_module_platform_driver(tve200_driver);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c
index c7ed2e1cbab6..92bc0faee84f 100644
--- a/drivers/gpu/drm/v3d/v3d_gem.c
+++ b/drivers/gpu/drm/v3d/v3d_gem.c
@@ -798,7 +798,7 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data,
if (!render->base.perfmon) {
ret = -ENOENT;
- goto fail;
+ goto fail_perfmon;
}
}
@@ -847,6 +847,7 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data,
fail_unreserve:
mutex_unlock(&v3d->sched_lock);
+fail_perfmon:
drm_gem_unlock_reservations(last_job->bo,
last_job->bo_count, &acquire_ctx);
fail:
@@ -1027,7 +1028,7 @@ v3d_submit_csd_ioctl(struct drm_device *dev, void *data,
args->perfmon_id);
if (!job->base.perfmon) {
ret = -ENOENT;
- goto fail;
+ goto fail_perfmon;
}
}
@@ -1056,6 +1057,7 @@ v3d_submit_csd_ioctl(struct drm_device *dev, void *data,
fail_unreserve:
mutex_unlock(&v3d->sched_lock);
+fail_perfmon:
drm_gem_unlock_reservations(clean_job->bo, clean_job->bo_count,
&acquire_ctx);
fail:
diff --git a/drivers/gpu/drm/vboxvideo/vbox_drv.c b/drivers/gpu/drm/vboxvideo/vbox_drv.c
index f35d9e44c6b7..f4f2bd79a7cb 100644
--- a/drivers/gpu/drm/vboxvideo/vbox_drv.c
+++ b/drivers/gpu/drm/vboxvideo/vbox_drv.c
@@ -18,6 +18,7 @@
#include <drm/drm_file.h>
#include <drm/drm_ioctl.h>
#include <drm/drm_managed.h>
+#include <drm/drm_module.h>
#include "vbox_drv.h"
@@ -190,24 +191,7 @@ static const struct drm_driver driver = {
DRM_GEM_VRAM_DRIVER,
};
-static int __init vbox_init(void)
-{
- if (drm_firmware_drivers_only() && vbox_modeset == -1)
- return -EINVAL;
-
- if (vbox_modeset == 0)
- return -EINVAL;
-
- return pci_register_driver(&vbox_pci_driver);
-}
-
-static void __exit vbox_exit(void)
-{
- pci_unregister_driver(&vbox_pci_driver);
-}
-
-module_init(vbox_init);
-module_exit(vbox_exit);
+drm_module_pci_driver_if_modeset(vbox_pci_driver, vbox_modeset);
MODULE_AUTHOR("Oracle Corporation");
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index e3121eb5f605..aef0e78f86bb 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -1018,30 +1018,15 @@ static void vc4_hdmi_recenter_fifo(struct vc4_hdmi *vc4_hdmi)
"VC4_HDMI_FIFO_CTL_RECENTER_DONE");
}
-static struct drm_connector_state *
-vc4_hdmi_encoder_get_connector_state(struct drm_encoder *encoder,
- struct drm_atomic_state *state)
-{
- struct drm_connector_state *conn_state;
- struct drm_connector *connector;
- unsigned int i;
-
- for_each_new_connector_in_state(state, connector, conn_state, i) {
- if (conn_state->best_encoder == encoder)
- return conn_state;
- }
-
- return NULL;
-}
-
static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder,
struct drm_atomic_state *state)
{
+ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+ struct drm_connector *connector = &vc4_hdmi->connector;
struct drm_connector_state *conn_state =
- vc4_hdmi_encoder_get_connector_state(encoder, state);
+ drm_atomic_get_new_connector_state(state, connector);
struct vc4_hdmi_connector_state *vc4_conn_state =
conn_state_to_vc4_hdmi_conn_state(conn_state);
- struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
unsigned long pixel_rate = vc4_conn_state->pixel_rate;
unsigned long bvb_rate, hsm_rate;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
index fe36efdb7ff5..26eb5478394a 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
@@ -32,9 +32,10 @@
#include <drm/drm_aperture.h>
#include <drm/drm_drv.h>
+#include <drm/drm_gem_ttm_helper.h>
#include <drm/drm_ioctl.h>
+#include <drm/drm_module.h>
#include <drm/drm_sysfs.h>
-#include <drm/drm_gem_ttm_helper.h>
#include <drm/ttm/ttm_bo_driver.h>
#include <drm/ttm/ttm_range_manager.h>
#include <drm/ttm/ttm_placement.h>
@@ -1643,26 +1644,7 @@ out_error:
return ret;
}
-static int __init vmwgfx_init(void)
-{
- int ret;
-
- if (drm_firmware_drivers_only())
- return -EINVAL;
-
- ret = pci_register_driver(&vmw_pci_driver);
- if (ret)
- DRM_ERROR("Failed initializing DRM.\n");
- return ret;
-}
-
-static void __exit vmwgfx_exit(void)
-{
- pci_unregister_driver(&vmw_pci_driver);
-}
-
-module_init(vmwgfx_init);
-module_exit(vmwgfx_exit);
+drm_module_pci_driver(vmw_pci_driver);
MODULE_AUTHOR("VMware Inc. and others");
MODULE_DESCRIPTION("Standalone drm driver for the VMware SVGA device");
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
index dd2ff441068e..d49de4905efa 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
@@ -4501,7 +4501,7 @@ int vmw_execbuf_ioctl(struct drm_device *dev, void *data,
goto mksstats_out;
}
- ret = vmw_wait_dma_fence(dev_priv->fman, in_fence);
+ ret = dma_fence_wait(in_fence, true);
if (ret)
goto out;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
index 5001b87aebe8..59d6a2dd4c2e 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
@@ -621,52 +621,6 @@ out_no_object:
return ret;
}
-
-/**
- * vmw_wait_dma_fence - Wait for a dma fence
- *
- * @fman: pointer to a fence manager
- * @fence: DMA fence to wait on
- *
- * This function handles the case when the fence is actually a fence
- * array. If that's the case, it'll wait on each of the child fence
- */
-int vmw_wait_dma_fence(struct vmw_fence_manager *fman,
- struct dma_fence *fence)
-{
- struct dma_fence_array *fence_array;
- int ret = 0;
- int i;
-
-
- if (dma_fence_is_signaled(fence))
- return 0;
-
- if (!dma_fence_is_array(fence))
- return dma_fence_wait(fence, true);
-
- /* From i915: Note that if the fence-array was created in
- * signal-on-any mode, we should *not* decompose it into its individual
- * fences. However, we don't currently store which mode the fence-array
- * is operating in. Fortunately, the only user of signal-on-any is
- * private to amdgpu and we should not see any incoming fence-array
- * from sync-file being in signal-on-any mode.
- */
-
- fence_array = to_dma_fence_array(fence);
- for (i = 0; i < fence_array->num_fences; i++) {
- struct dma_fence *child = fence_array->fences[i];
-
- ret = dma_fence_wait(child, true);
-
- if (ret < 0)
- return ret;
- }
-
- return 0;
-}
-
-
/*
* vmw_fence_fifo_down - signal all unsignaled fence objects.
*/
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h
index 079ab4f3ba51..a7eee579c76a 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h
@@ -104,9 +104,6 @@ extern int vmw_user_fence_create(struct drm_file *file_priv,
struct vmw_fence_obj **p_fence,
uint32_t *p_handle);
-extern int vmw_wait_dma_fence(struct vmw_fence_manager *fman,
- struct dma_fence *fence);
-
extern void vmw_fence_fifo_up(struct vmw_fence_manager *fman);
extern void vmw_fence_fifo_down(struct vmw_fence_manager *fman);
diff --git a/drivers/gpu/drm/xen/xen_drm_front.c b/drivers/gpu/drm/xen/xen_drm_front.c
index e63088c2121d..0d8e6bd1ccbf 100644
--- a/drivers/gpu/drm/xen/xen_drm_front.c
+++ b/drivers/gpu/drm/xen/xen_drm_front.c
@@ -495,6 +495,9 @@ static int xen_drm_drv_init(struct xen_drm_front_info *front_info)
struct drm_device *drm_dev;
int ret;
+ if (drm_firmware_drivers_only())
+ return -ENODEV;
+
DRM_INFO("Creating %s\n", xen_drm_driver.desc);
drm_info = kzalloc(sizeof(*drm_info), GFP_KERNEL);
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
index ac37053412a1..824b510e337b 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
@@ -25,6 +25,7 @@
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_managed.h>
#include <drm/drm_mode_config.h>
+#include <drm/drm_module.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
@@ -286,7 +287,7 @@ static struct platform_driver zynqmp_dpsub_driver = {
},
};
-module_platform_driver(zynqmp_dpsub_driver);
+drm_module_platform_driver(zynqmp_dpsub_driver);
MODULE_AUTHOR("Xilinx, Inc.");
MODULE_DESCRIPTION("ZynqMP DP Subsystem Driver");