From af3b4fafec7057837de81ee25bc3d880ff3df427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Mon, 20 Nov 2023 00:30:26 +0300 Subject: [PATCH] Update rk3588-edge to 6.7-rc.1 and add support for VOP2, Crypto, HW RNG (#5928) * Update rk3588-edge to 6.7-rc1 kernel * Update patches for rk3588-edge and add support for crypto, trng, hdmi tx, vop2 --- .../kernel/linux-rockchip-rk3588-edge.config | 136 +- config/sources/families/rockchip-rk3588.conf | 4 +- .../0010-fix-clk-divisions.patch | 111 +- .../0020-USB3-Host-Support-For-Rock5.patch | 85 + ...=> 0021-RK3588-Add-USB3-DRD-Support.patch} | 853 +- ... => 0022-RK3588-Add-Cpufreq-Support.patch} | 221 +- ...3-Add-Initial-HDMITX-VOP2-For-RK3588.patch | 13411 ++++++++++++++++ .../0024-RK3588-Add-Crypto-Support.patch | 2365 +++ .../0025-RK3588-Add-HW-RNG-Support.patch | 665 + ...m64-dts-rockchip-rk3588-add-sfc-node.patch | 36 - ...026-Add-missing-nodes-to-Orange-Pi-5.patch | 322 + ...dd-missing-nodes-to-Orange-Pi-5-Plus.patch | 282 + .../dt/rk3588-orangepi-5-plus.dts | 1227 -- .../dt/rk3588s-nanopi-r6s.dts | 29 + .../dt/rk3588s-orangepi-5.dts | 1276 -- 15 files changed, 18168 insertions(+), 2855 deletions(-) create mode 100644 patch/kernel/rockchip-rk3588-edge/0020-USB3-Host-Support-For-Rock5.patch rename patch/kernel/rockchip-rk3588-edge/{0024-Add-RK3588-USB3-Support.patch => 0021-RK3588-Add-USB3-DRD-Support.patch} (83%) rename patch/kernel/rockchip-rk3588-edge/{0023-RK3588-Add-Cpufreq-Support.patch => 0022-RK3588-Add-Cpufreq-Support.patch} (86%) create mode 100644 patch/kernel/rockchip-rk3588-edge/0023-Add-Initial-HDMITX-VOP2-For-RK3588.patch create mode 100644 patch/kernel/rockchip-rk3588-edge/0024-RK3588-Add-Crypto-Support.patch create mode 100644 patch/kernel/rockchip-rk3588-edge/0025-RK3588-Add-HW-RNG-Support.patch delete mode 100644 patch/kernel/rockchip-rk3588-edge/0025-arm64-dts-rockchip-rk3588-add-sfc-node.patch create mode 100644 patch/kernel/rockchip-rk3588-edge/0026-Add-missing-nodes-to-Orange-Pi-5.patch create mode 100644 patch/kernel/rockchip-rk3588-edge/0027-arm64-dts-Add-missing-nodes-to-Orange-Pi-5-Plus.patch delete mode 100644 patch/kernel/rockchip-rk3588-edge/dt/rk3588-orangepi-5-plus.dts delete mode 100644 patch/kernel/rockchip-rk3588-edge/dt/rk3588s-orangepi-5.dts diff --git a/config/kernel/linux-rockchip-rk3588-edge.config b/config/kernel/linux-rockchip-rk3588-edge.config index fe15354216..b44f66668d 100644 --- a/config/kernel/linux-rockchip-rk3588-edge.config +++ b/config/kernel/linux-rockchip-rk3588-edge.config @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/arm64 6.6.1 Kernel Configuration +# Linux/arm64 6.7.0-rc1 Kernel Configuration # CONFIG_CC_VERSION_TEXT="aarch64-linux-gnu-gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0" CONFIG_CC_IS_GCC=y @@ -334,6 +334,7 @@ CONFIG_BUILTIN_RETURN_ADDRESS_STRIPS_PAC=y # CONFIG_ARCH_NXP is not set # CONFIG_ARCH_MA35 is not set # CONFIG_ARCH_NPCM is not set +# CONFIG_ARCH_PENSANDO is not set # CONFIG_ARCH_QCOM is not set # CONFIG_ARCH_REALTEK is not set # CONFIG_ARCH_RENESAS is not set @@ -445,6 +446,7 @@ CONFIG_ARCH_SUPPORTS_KEXEC_SIG=y CONFIG_ARCH_SUPPORTS_KEXEC_IMAGE_VERIFY_SIG=y CONFIG_ARCH_DEFAULT_KEXEC_IMAGE_VERIFY_SIG=y CONFIG_ARCH_SUPPORTS_CRASH_DUMP=y +CONFIG_ARCH_HAS_GENERIC_CRASHKERNEL_RESERVATION=y CONFIG_TRANS_TABLE=y CONFIG_XEN_DOM0=y CONFIG_XEN=y @@ -1003,6 +1005,7 @@ CONFIG_MIGRATION=y CONFIG_ARCH_ENABLE_HUGEPAGE_MIGRATION=y CONFIG_ARCH_ENABLE_THP_MIGRATION=y CONFIG_CONTIG_ALLOC=y +CONFIG_PCP_BATCH_SCALE_MAX=5 CONFIG_PHYS_ADDR_T_64BIT=y CONFIG_MMU_NOTIFIER=y CONFIG_KSM=y @@ -1159,6 +1162,8 @@ CONFIG_TCP_CONG_BBR=m # CONFIG_DEFAULT_CUBIC is not set CONFIG_DEFAULT_RENO=y CONFIG_DEFAULT_TCP_CONG="reno" +CONFIG_TCP_SIGPOOL=y +# CONFIG_TCP_AO is not set CONFIG_TCP_MD5SIG=y CONFIG_IPV6=y CONFIG_IPV6_ROUTER_PREF=y @@ -1636,9 +1641,6 @@ CONFIG_VLAN_8021Q_MVRP=y CONFIG_LLC=y CONFIG_LLC2=m CONFIG_ATALK=m -CONFIG_DEV_APPLETALK=m -CONFIG_IPDDP=m -CONFIG_IPDDP_ENCAP=y CONFIG_X25=m CONFIG_LAPB=m CONFIG_PHONET=m @@ -1932,6 +1934,7 @@ CONFIG_CFG80211_DEFAULT_PS=y CONFIG_CFG80211_CRDA_SUPPORT=y CONFIG_CFG80211_WEXT=y CONFIG_CFG80211_WEXT_EXPORT=y +# CONFIG_CFG80211_KUNIT_TEST is not set CONFIG_LIB80211=m CONFIG_LIB80211_CRYPT_WEP=m CONFIG_LIB80211_CRYPT_CCMP=m @@ -1942,6 +1945,7 @@ CONFIG_MAC80211_HAS_RC=y CONFIG_MAC80211_RC_MINSTREL=y CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +# CONFIG_MAC80211_KUNIT_TEST is not set CONFIG_MAC80211_MESH=y CONFIG_MAC80211_LEDS=y # CONFIG_MAC80211_DEBUGFS is not set @@ -2022,6 +2026,7 @@ CONFIG_PAGE_POOL_STATS=y CONFIG_FAILOVER=y CONFIG_ETHTOOL_NETLINK=y # CONFIG_NETDEV_ADDR_LIST_TEST is not set +# CONFIG_NET_TEST is not set # # Device Drivers @@ -2036,6 +2041,7 @@ CONFIG_PCIEPORTBUS=y CONFIG_HOTPLUG_PCI_PCIE=y CONFIG_PCIEAER=y CONFIG_PCIEAER_INJECT=m +CONFIG_PCIEAER_CXL=y CONFIG_PCIE_ECRC=y CONFIG_PCIEASPM=y CONFIG_PCIEASPM_DEFAULT=y @@ -2068,6 +2074,7 @@ CONFIG_VGA_ARB=y CONFIG_VGA_ARB_MAX_GPUS=16 CONFIG_HOTPLUG_PCI=y CONFIG_HOTPLUG_PCI_ACPI=y +# CONFIG_HOTPLUG_PCI_ACPI_AMPERE_ALTRA is not set # CONFIG_HOTPLUG_PCI_ACPI_IBM is not set # CONFIG_HOTPLUG_PCI_CPCI is not set # CONFIG_HOTPLUG_PCI_SHPC is not set @@ -2185,6 +2192,7 @@ CONFIG_DEV_COREDUMP=y CONFIG_HMEM_REPORTING=y # CONFIG_TEST_ASYNC_DRIVER_PROBE is not set # CONFIG_DM_KUNIT_TEST is not set +# CONFIG_DRIVER_PE_KUNIT_TEST is not set CONFIG_SYS_HYPERVISOR=y CONFIG_GENERIC_CPU_AUTOPROBE=y CONFIG_GENERIC_CPU_VULNERABILITIES=y @@ -2214,7 +2222,6 @@ CONFIG_GENERIC_ARCH_NUMA=y # CONFIG_ARM_CCI=y CONFIG_ARM_CCI400_COMMON=y -CONFIG_BRCMSTB_GISB_ARB=y # CONFIG_MOXTET is not set CONFIG_VEXPRESS_CONFIG=y CONFIG_MHI_BUS=m @@ -2250,6 +2257,7 @@ CONFIG_ARM_SCMI_TRANSPORT_VIRTIO=y CONFIG_ARM_SCMI_TRANSPORT_VIRTIO_VERSION1_COMPLIANCE=y # CONFIG_ARM_SCMI_TRANSPORT_VIRTIO_ATOMIC_ENABLE is not set CONFIG_ARM_SCMI_POWER_DOMAIN=y +CONFIG_ARM_SCMI_PERF_DOMAIN=y CONFIG_ARM_SCMI_POWER_CONTROL=m # end of ARM System Control and Management Interface Protocol @@ -2295,6 +2303,12 @@ CONFIG_UEFI_CPER=y CONFIG_UEFI_CPER_ARM=y CONFIG_ARM_PSCI_FW=y CONFIG_ARM_PSCI_CHECKER=y + +# +# Qualcomm firmware drivers +# +# end of Qualcomm firmware drivers + CONFIG_HAVE_ARM_SMCCC=y CONFIG_HAVE_ARM_SMCCC_DISCOVERY=y CONFIG_ARM_SMCCC_SOC_ID=y @@ -2317,7 +2331,6 @@ CONFIG_MTD=y # # Partition parsers # -# CONFIG_MTD_AR7_PARTS is not set CONFIG_MTD_CMDLINE_PARTS=m CONFIG_MTD_OF_PARTS=y CONFIG_MTD_AFS_PARTS=m @@ -2527,7 +2540,7 @@ CONFIG_BLKDEV_UBLK_LEGACY_OPCODES=y # # NVME Support # -CONFIG_NVME_COMMON=y +CONFIG_NVME_AUTH=m CONFIG_NVME_CORE=y CONFIG_BLK_DEV_NVME=y CONFIG_NVME_MULTIPATH=y @@ -2536,13 +2549,15 @@ CONFIG_NVME_HWMON=y CONFIG_NVME_FABRICS=m CONFIG_NVME_FC=m CONFIG_NVME_TCP=m -CONFIG_NVME_AUTH=y +# CONFIG_NVME_TCP_TLS is not set +# CONFIG_NVME_HOST_AUTH is not set CONFIG_NVME_TARGET=m CONFIG_NVME_TARGET_PASSTHRU=y CONFIG_NVME_TARGET_LOOP=m CONFIG_NVME_TARGET_FC=m CONFIG_NVME_TARGET_FCLOOP=m CONFIG_NVME_TARGET_TCP=m +# CONFIG_NVME_TARGET_TCP_TLS is not set CONFIG_NVME_TARGET_AUTH=y # end of NVME Support @@ -2584,7 +2599,6 @@ CONFIG_VCPU_STALL_DETECTOR=m # CONFIG_EEPROM_AT24=m CONFIG_EEPROM_AT25=m -CONFIG_EEPROM_LEGACY=m CONFIG_EEPROM_MAX6875=m CONFIG_EEPROM_93CX6=m # CONFIG_EEPROM_93XX46 is not set @@ -2854,7 +2868,6 @@ CONFIG_MD_FAULTY=m CONFIG_MD_CLUSTER=m CONFIG_BCACHE=m # CONFIG_BCACHE_DEBUG is not set -# CONFIG_BCACHE_CLOSURES_DEBUG is not set # CONFIG_BCACHE_ASYNC_REGISTRATION is not set CONFIG_BLK_DEV_DM_BUILTIN=y CONFIG_BLK_DEV_DM=m @@ -2957,6 +2970,7 @@ CONFIG_TUN_VNET_CROSS_LE=y CONFIG_VETH=m CONFIG_VIRTIO_NET=m CONFIG_NLMON=m +# CONFIG_NETKIT is not set CONFIG_NET_VRF=m CONFIG_MHI_NET=m # CONFIG_ARCNET is not set @@ -3163,6 +3177,7 @@ CONFIG_I40EVF=m # CONFIG_ICE is not set CONFIG_FM10K=m # CONFIG_IGC is not set +# CONFIG_IDPF is not set CONFIG_JME=m CONFIG_NET_VENDOR_ADI=y CONFIG_ADIN1110=m @@ -3202,6 +3217,7 @@ CONFIG_MLX5_CORE_EN_DCB=y # CONFIG_MLX5_EN_TLS is not set CONFIG_MLX5_SW_STEERING=y # CONFIG_MLX5_SF is not set +# CONFIG_MLX5_DPLL is not set # CONFIG_MLXSW_CORE is not set # CONFIG_MLXFW is not set CONFIG_MLXBF_GIGE=m @@ -3771,6 +3787,8 @@ CONFIG_MT7921E=m CONFIG_MT7921S=m CONFIG_MT7921U=m # CONFIG_MT7996E is not set +# CONFIG_MT7925E is not set +# CONFIG_MT7925U is not set CONFIG_WLAN_VENDOR_MICROCHIP=y CONFIG_WILC1000=m CONFIG_WILC1000_SDIO=m @@ -3883,14 +3901,14 @@ CONFIG_WL18XX=m CONFIG_WLCORE=m CONFIG_WLCORE_SPI=m CONFIG_WLCORE_SDIO=m -CONFIG_RTL8723DU=m +# CONFIG_RTL8723DU is not set CONFIG_RTL8723DS=m # CONFIG_RTL8822BU is not set # CONFIG_RTL8821CU is not set -CONFIG_88XXAU=m -CONFIG_RTL8192EU=m -CONFIG_RTL8189FS=m -CONFIG_RTL8189ES=m +# CONFIG_88XXAU is not set +# CONFIG_RTL8192EU is not set +# CONFIG_RTL8189FS is not set +# CONFIG_RTL8189ES is not set CONFIG_WLAN_VENDOR_ZYDAS=y CONFIG_USB_ZD1201=m CONFIG_ZD1211RW=m @@ -4369,6 +4387,7 @@ CONFIG_HW_RANDOM_VIRTIO=m CONFIG_HW_RANDOM_OPTEE=m # CONFIG_HW_RANDOM_CCTRNG is not set # CONFIG_HW_RANDOM_XIPHERA is not set +CONFIG_HW_RANDOM_ROCKCHIP=m CONFIG_HW_RANDOM_ARM_SMCCC_TRNG=m CONFIG_HW_RANDOM_CN10K=y # CONFIG_APPLICOM is not set @@ -4753,7 +4772,6 @@ CONFIG_W1_SLAVE_DS28E17=m # end of 1-wire Slaves CONFIG_POWER_RESET=y -CONFIG_POWER_RESET_BRCMSTB=y CONFIG_POWER_RESET_GPIO=y CONFIG_POWER_RESET_GPIO_RESTART=y # CONFIG_POWER_RESET_LTC2952 is not set @@ -4820,6 +4838,7 @@ CONFIG_CHARGER_CROS_PCHG=m CONFIG_CHARGER_UCS1002=m # CONFIG_CHARGER_BD99954 is not set # CONFIG_BATTERY_UG3105 is not set +# CONFIG_FUEL_GAUGE_MM8013 is not set CONFIG_HWMON=y CONFIG_HWMON_VID=m # CONFIG_HWMON_DEBUG_CHIP is not set @@ -4873,6 +4892,7 @@ CONFIG_SENSORS_IBMPEX=m CONFIG_SENSORS_IIO_HWMON=m CONFIG_SENSORS_IT87=m CONFIG_SENSORS_JC42=m +# CONFIG_SENSORS_POWERZ is not set CONFIG_SENSORS_POWR1220=m CONFIG_SENSORS_LINEAGE=m CONFIG_SENSORS_LTC2945=m @@ -4880,6 +4900,7 @@ CONFIG_SENSORS_LTC2947=m CONFIG_SENSORS_LTC2947_I2C=m CONFIG_SENSORS_LTC2947_SPI=m CONFIG_SENSORS_LTC2990=m +# CONFIG_SENSORS_LTC2991 is not set CONFIG_SENSORS_LTC2992=m CONFIG_SENSORS_LTC4151=m CONFIG_SENSORS_LTC4215=m @@ -5304,6 +5325,7 @@ CONFIG_REGULATOR_GPIO=y # CONFIG_REGULATOR_LTC3589 is not set # CONFIG_REGULATOR_LTC3676 is not set # CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX77503 is not set # CONFIG_REGULATOR_MAX77857 is not set # CONFIG_REGULATOR_MAX8649 is not set # CONFIG_REGULATOR_MAX8660 is not set @@ -5643,6 +5665,7 @@ CONFIG_MEDIA_PCI_SUPPORT=y # # Media capture support # +# CONFIG_VIDEO_MGB4 is not set CONFIG_VIDEO_SOLO6X10=m # CONFIG_VIDEO_TW5864 is not set CONFIG_VIDEO_TW68=m @@ -5785,6 +5808,10 @@ CONFIG_VIDEO_CAFE_CCIC=m # Microchip Technology, Inc. media platform drivers # +# +# Nuvoton media platform drivers +# + # # NVidia media platform drivers # @@ -5913,6 +5940,7 @@ CONFIG_VIDEO_IMX412=m CONFIG_VIDEO_MAX9271_LIB=m CONFIG_VIDEO_MT9M001=m CONFIG_VIDEO_MT9M111=m +# CONFIG_VIDEO_MT9M114 is not set CONFIG_VIDEO_MT9P031=m CONFIG_VIDEO_MT9T112=m CONFIG_VIDEO_MT9V011=m @@ -6418,10 +6446,12 @@ CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D=m CONFIG_DRM_PANEL_ILITEK_IL9322=m CONFIG_DRM_PANEL_ILITEK_ILI9341=m CONFIG_DRM_PANEL_ILITEK_ILI9881C=m +# CONFIG_DRM_PANEL_ILITEK_ILI9882T is not set CONFIG_DRM_PANEL_INNOLUX_EJ030NA=m CONFIG_DRM_PANEL_INNOLUX_P079ZCA=m # CONFIG_DRM_PANEL_JADARD_JD9365DA_H3 is not set CONFIG_DRM_PANEL_JDI_LT070ME05000=m +# CONFIG_DRM_PANEL_JDI_LPM102A188A is not set # CONFIG_DRM_PANEL_JDI_R63452 is not set CONFIG_DRM_PANEL_KHADAS_TS050=m CONFIG_DRM_PANEL_KINGDISPLAY_KD097D04=m @@ -6449,6 +6479,7 @@ CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00=m CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN=m CONFIG_DRM_PANEL_RAYDIUM_RM67191=m CONFIG_DRM_PANEL_RAYDIUM_RM68200=m +# CONFIG_DRM_PANEL_RAYDIUM_RM692E5 is not set CONFIG_DRM_PANEL_RONBO_RB070D30=m CONFIG_DRM_PANEL_SAMSUNG_ATNA33XC20=m CONFIG_DRM_PANEL_SAMSUNG_DB7430=m @@ -6634,6 +6665,7 @@ CONFIG_FB_SYS_IMAGEBLIT=y CONFIG_FB_SYS_FOPS=y CONFIG_FB_DEFERRED_IO=y CONFIG_FB_DMAMEM_HELPERS=y +CONFIG_FB_IOMEM_FOPS=y CONFIG_FB_IOMEM_HELPERS=y CONFIG_FB_SYSMEM_HELPERS=y CONFIG_FB_SYSMEM_HELPERS_DEFERRED=y @@ -6832,6 +6864,7 @@ CONFIG_SND_HDA_RECONFIG=y CONFIG_SND_HDA_INPUT_BEEP=y CONFIG_SND_HDA_INPUT_BEEP_MODE=1 CONFIG_SND_HDA_PATCH_LOADER=y +# CONFIG_SND_HDA_CIRRUS_SCODEC_KUNIT_TEST is not set CONFIG_SND_HDA_SCODEC_CS35L41=m CONFIG_SND_HDA_CS_DSP_CONTROLS=m CONFIG_SND_HDA_SCODEC_CS35L41_I2C=m @@ -6993,6 +7026,8 @@ CONFIG_SND_SOC_ALC5623=m CONFIG_SND_SOC_AW8738=m # CONFIG_SND_SOC_AW88395 is not set # CONFIG_SND_SOC_AW88261 is not set +# CONFIG_SND_SOC_AW87390 is not set +# CONFIG_SND_SOC_AW88399 is not set CONFIG_SND_SOC_BD28623=m CONFIG_SND_SOC_BT_SCO=m # CONFIG_SND_SOC_CHV3_CODEC is not set @@ -7116,6 +7151,7 @@ CONFIG_SND_SOC_RT715=m CONFIG_SND_SOC_RT715_SDW=m CONFIG_SND_SOC_RT715_SDCA_SDW=m # CONFIG_SND_SOC_RT9120 is not set +# CONFIG_SND_SOC_RTQ9128 is not set CONFIG_SND_SOC_SDW_MOCKUP=m CONFIG_SND_SOC_SGTL5000=m CONFIG_SND_SOC_SIGMADSP=m @@ -7401,6 +7437,7 @@ CONFIG_USB_CONN_GPIO=y CONFIG_USB_ARCH_HAS_HCD=y CONFIG_USB=y CONFIG_USB_PCI=y +# CONFIG_USB_PCI_AMD is not set CONFIG_USB_ANNOUNCE_NEW_DEVICES=y # @@ -7546,6 +7583,7 @@ CONFIG_USB_CHIPIDEA_UDC=y CONFIG_USB_CHIPIDEA_HOST=y CONFIG_USB_CHIPIDEA_PCI=y CONFIG_USB_CHIPIDEA_MSM=y +CONFIG_USB_CHIPIDEA_NPCM=y CONFIG_USB_CHIPIDEA_IMX=y CONFIG_USB_CHIPIDEA_GENERIC=y CONFIG_USB_CHIPIDEA_TEGRA=y @@ -7629,6 +7667,7 @@ CONFIG_USB_CYTHERM=m CONFIG_USB_IDMOUSE=m CONFIG_USB_APPLEDISPLAY=m CONFIG_APPLE_MFI_FASTCHARGE=m +# CONFIG_USB_LJCA is not set CONFIG_USB_SISUSBVGA=m CONFIG_USB_LD=m CONFIG_USB_TRANCEVIBRATOR=m @@ -7798,6 +7837,7 @@ CONFIG_TYPEC_MUX_FSA4480=m # CONFIG_TYPEC_MUX_GPIO_SBU is not set CONFIG_TYPEC_MUX_PI3USB30532=m # CONFIG_TYPEC_MUX_NB7VPQ904M is not set +# CONFIG_TYPEC_MUX_PTN36502 is not set # end of USB Type-C Multiplexer/DeMultiplexer Switch support # @@ -7951,6 +7991,7 @@ CONFIG_LEDS_TRIGGER_HEARTBEAT=y CONFIG_LEDS_TRIGGER_BACKLIGHT=m CONFIG_LEDS_TRIGGER_CPU=y CONFIG_LEDS_TRIGGER_ACTIVITY=y +# CONFIG_LEDS_TRIGGER_GPIO is not set CONFIG_LEDS_TRIGGER_DEFAULT_ON=y # @@ -8260,7 +8301,6 @@ CONFIG_XEN_FRONT_PGDIR_SHBUF=m # CONFIG_COMEDI is not set CONFIG_STAGING=y # CONFIG_PRISM2_USB is not set -CONFIG_RTL8192U=m CONFIG_RTLLIB=m CONFIG_RTLLIB_CRYPTO_CCMP=m CONFIG_RTLLIB_CRYPTO_TKIP=m @@ -8307,12 +8347,6 @@ CONFIG_AD9834=m # # CONFIG_AD5933 is not set # end of Network Analyzer, Impedance Converters - -# -# Resolver to digital converters -# -# CONFIG_AD2S1210 is not set -# end of Resolver to digital converters # end of IIO staging drivers CONFIG_FB_SM750=m @@ -8371,7 +8405,6 @@ CONFIG_FIELDBUS_DEV=m CONFIG_HMS_ANYBUSS_BUS=m # CONFIG_ARCX_ANYBUS_CONTROLLER is not set # CONFIG_HMS_PROFINET is not set -# CONFIG_QLGE is not set # CONFIG_VME_BUS is not set # CONFIG_RTL8723CS is not set # CONFIG_GOLDFISH is not set @@ -8396,7 +8429,7 @@ CONFIG_CROS_EC_TYPEC=m CONFIG_CROS_USBPD_NOTIFY=y # CONFIG_CHROMEOS_PRIVACY_SCREEN is not set CONFIG_CROS_TYPEC_SWITCH=m -# CONFIG_CROS_KUNIT is not set +# CONFIG_CROS_KUNIT_EC_PROTO_TEST is not set # CONFIG_MELLANOX_PLATFORM is not set CONFIG_SURFACE_PLATFORMS=y # CONFIG_SURFACE_3_POWER_OPREGION is not set @@ -8450,6 +8483,7 @@ CONFIG_CLK_RK3588=y # CONFIG_COMMON_CLK_XLNX_CLKWZRD is not set # CONFIG_CLK_KUNIT_TEST is not set # CONFIG_CLK_GATE_KUNIT_TEST is not set +# CONFIG_CLK_FD_KUNIT_TEST is not set # CONFIG_HWSPINLOCK is not set # @@ -8479,6 +8513,7 @@ CONFIG_PCC=y # CONFIG_MAILBOX_TEST is not set CONFIG_IOMMU_IOVA=y CONFIG_IOMMU_API=y +CONFIG_IOMMUFD_DRIVER=y CONFIG_IOMMU_SUPPORT=y # @@ -8548,7 +8583,6 @@ CONFIG_SOUNDWIRE_QCOM=m # # Broadcom SoC drivers # -CONFIG_SOC_BRCMSTB=y # end of Broadcom SoC drivers # @@ -8587,7 +8621,6 @@ CONFIG_QCOM_QMI_HELPERS=m CONFIG_ROCKCHIP_GRF=y CONFIG_ROCKCHIP_IODOMAIN=y -CONFIG_ROCKCHIP_PM_DOMAINS=y CONFIG_SOC_TI=y # @@ -8596,6 +8629,33 @@ CONFIG_SOC_TI=y # end of Xilinx SoC drivers # end of SOC (System On Chip) specific Drivers +# +# PM Domains +# + +# +# Amlogic PM Domains +# +# end of Amlogic PM Domains + +# +# Broadcom PM Domains +# +# end of Broadcom PM Domains + +# +# i.MX PM Domains +# +# end of i.MX PM Domains + +# +# Qualcomm PM Domains +# +# end of Qualcomm PM Domains + +CONFIG_ROCKCHIP_PM_DOMAINS=y +# end of PM Domains + CONFIG_PM_DEVFREQ=y # @@ -8744,6 +8804,7 @@ CONFIG_AD799X=m # CONFIG_HI8435 is not set # CONFIG_HX711 is not set # CONFIG_INA2XX_ADC is not set +# CONFIG_LTC2309 is not set # CONFIG_LTC2471 is not set # CONFIG_LTC2485 is not set CONFIG_LTC2496=m @@ -8758,6 +8819,7 @@ CONFIG_MAX1363=m CONFIG_MAX9611=m CONFIG_MCP320X=m CONFIG_MCP3422=m +# CONFIG_MCP3564 is not set CONFIG_MCP3911=m # CONFIG_MEDIATEK_MT6370_ADC is not set # CONFIG_NAU7802 is not set @@ -9176,6 +9238,7 @@ CONFIG_MCP41010=m # Pressure sensors # # CONFIG_ABP060MG is not set +# CONFIG_ROHM_BM1390 is not set CONFIG_BMP280=m CONFIG_BMP280_I2C=m CONFIG_BMP280_SPI=m @@ -9228,6 +9291,7 @@ CONFIG_VL53L0X_I2C=m # # CONFIG_AD2S90 is not set # CONFIG_AD2S1200 is not set +# CONFIG_AD2S1210 is not set # end of Resolver to digital converters # @@ -9255,6 +9319,7 @@ CONFIG_PWM_SYSFS=y CONFIG_PWM_ATMEL_TCB=m CONFIG_PWM_CLK=m CONFIG_PWM_CROS_EC=m +CONFIG_PWM_DWC_CORE=m CONFIG_PWM_DWC=m # CONFIG_PWM_FSL_FTM is not set CONFIG_PWM_NTXEC=m @@ -9320,6 +9385,7 @@ CONFIG_PHY_ROCKCHIP_INNO_CSIDPHY=m CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY=y CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY=y CONFIG_PHY_ROCKCHIP_PCIE=y +CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX_HDMI=m CONFIG_PHY_ROCKCHIP_SNPS_PCIE3=m CONFIG_PHY_ROCKCHIP_TYPEC=y CONFIG_PHY_ROCKCHIP_USB=y @@ -9436,6 +9502,7 @@ CONFIG_MOST_SND=m # CONFIG_PECI is not set # CONFIG_HTE is not set # CONFIG_CDX_BUS is not set +CONFIG_DPLL=y # end of Device Drivers # @@ -9491,7 +9558,6 @@ CONFIG_OCFS2_DEBUG_MASKLOG=y # CONFIG_OCFS2_DEBUG_FS is not set CONFIG_BTRFS_FS=y CONFIG_BTRFS_FS_POSIX_ACL=y -# CONFIG_BTRFS_FS_CHECK_INTEGRITY is not set # CONFIG_BTRFS_FS_RUN_SANITY_TESTS is not set # CONFIG_BTRFS_DEBUG is not set # CONFIG_BTRFS_ASSERT is not set @@ -9512,6 +9578,7 @@ CONFIG_F2FS_FS_LZ4HC=y CONFIG_F2FS_FS_ZSTD=y CONFIG_F2FS_IOSTAT=y # CONFIG_F2FS_UNFAIR_RWSEM is not set +# CONFIG_BCACHEFS_FS is not set CONFIG_ZONEFS_FS=m CONFIG_FS_POSIX_ACL=y CONFIG_EXPORTFS=y @@ -10132,7 +10199,9 @@ CONFIG_CRYPTO_DRBG_HMAC=y # CONFIG_CRYPTO_DRBG_CTR is not set CONFIG_CRYPTO_DRBG=y CONFIG_CRYPTO_JITTERENTROPY=y -# CONFIG_CRYPTO_JITTERENTROPY_TESTINTERFACE is not set +CONFIG_CRYPTO_JITTERENTROPY_MEMORY_BLOCKS=64 +CONFIG_CRYPTO_JITTERENTROPY_MEMORY_BLOCKSIZE=32 +CONFIG_CRYPTO_JITTERENTROPY_OSR=1 CONFIG_CRYPTO_KDF800108_CTR=y # end of Random number generation @@ -10198,6 +10267,8 @@ CONFIG_CRYPTO_DEV_NITROX_CNN55XX=m CONFIG_CRYPTO_DEV_CAVIUM_ZIP=m CONFIG_CRYPTO_DEV_ROCKCHIP=m # CONFIG_CRYPTO_DEV_ROCKCHIP_DEBUG is not set +CONFIG_CRYPTO_DEV_ROCKCHIP2=m +# CONFIG_CRYPTO_DEV_ROCKCHIP2_DEBUG is not set CONFIG_CRYPTO_DEV_VIRTIO=m CONFIG_CRYPTO_DEV_SAFEXCEL=m CONFIG_CRYPTO_DEV_CCREE=m @@ -10225,6 +10296,7 @@ CONFIG_SYSTEM_TRUSTED_KEYS="" CONFIG_SYSTEM_EXTRA_CERTIFICATE=y CONFIG_SYSTEM_EXTRA_CERTIFICATE_SIZE=4096 CONFIG_SECONDARY_TRUSTED_KEYRING=y +# CONFIG_SECONDARY_TRUSTED_KEYRING_SIGNED_BY_BUILTIN is not set CONFIG_SYSTEM_BLACKLIST_KEYRING=y CONFIG_SYSTEM_BLACKLIST_HASH_LIST="" # CONFIG_SYSTEM_REVOCATION_LIST is not set @@ -10314,7 +10386,6 @@ CONFIG_ZSTD_DECOMPRESS=y CONFIG_XZ_DEC=y CONFIG_XZ_DEC_X86=y CONFIG_XZ_DEC_POWERPC=y -CONFIG_XZ_DEC_IA64=y CONFIG_XZ_DEC_ARM=y CONFIG_XZ_DEC_ARMTHUMB=y CONFIG_XZ_DEC_SPARC=y @@ -10342,6 +10413,7 @@ CONFIG_BTREE=y CONFIG_INTERVAL_TREE=y CONFIG_XARRAY_MULTI=y CONFIG_ASSOCIATIVE_ARRAY=y +CONFIG_CLOSURES=y CONFIG_HAS_IOMEM=y CONFIG_HAS_IOPORT=y CONFIG_HAS_IOPORT_MAP=y @@ -10419,12 +10491,14 @@ CONFIG_MEMREGION=y CONFIG_ARCH_STACKWALK=y CONFIG_STACKDEPOT=y CONFIG_SBITMAP=y +# CONFIG_LWQ_TEST is not set # end of Library routines CONFIG_GENERIC_IOREMAP=y CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED=y CONFIG_ASN1_ENCODER=y CONFIG_POLYNOMIAL=m +CONFIG_FIRMWARE_TABLE=y # # Kernel hacking @@ -10600,6 +10674,7 @@ CONFIG_STACKTRACE=y # CONFIG_DEBUG_PLIST is not set # CONFIG_DEBUG_SG is not set # CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CLOSURES is not set # CONFIG_DEBUG_MAPLE_TREE is not set # end of Debug kernel data structures @@ -10767,6 +10842,7 @@ CONFIG_MEMCPY_SLOW_KUNIT_TEST=y CONFIG_TEST_MEMCAT_P=m # CONFIG_TEST_MEMINIT is not set # CONFIG_TEST_FREE_PAGES is not set +# CONFIG_TEST_OBJPOOL is not set CONFIG_ARCH_USE_MEMTEST=y CONFIG_MEMTEST=y # end of Kernel Testing and Coverage diff --git a/config/sources/families/rockchip-rk3588.conf b/config/sources/families/rockchip-rk3588.conf index 08c5f25cdf..72b974c954 100644 --- a/config/sources/families/rockchip-rk3588.conf +++ b/config/sources/families/rockchip-rk3588.conf @@ -34,8 +34,8 @@ case $BRANCH in SKIP_BOOTSPLASH="yes" LINUXFAMILY=rockchip-rk3588 LINUXCONFIG='linux-rockchip-rk3588-'$BRANCH - KERNEL_MAJOR_MINOR="6.6" # Major and minor versions of this kernel. - KERNELBRANCH='branch:linux-6.6.y' + KERNEL_MAJOR_MINOR="6.7" # Major and minor versions of this kernel. + KERNELBRANCH='tag:v6.7-rc1' KERNELPATCHDIR='rockchip-rk3588-edge' ;; diff --git a/patch/kernel/rockchip-rk3588-edge/0010-fix-clk-divisions.patch b/patch/kernel/rockchip-rk3588-edge/0010-fix-clk-divisions.patch index 202ee18ed9..3c0f689028 100644 --- a/patch/kernel/rockchip-rk3588-edge/0010-fix-clk-divisions.patch +++ b/patch/kernel/rockchip-rk3588-edge/0010-fix-clk-divisions.patch @@ -1,28 +1,74 @@ -From 0c7ec3f97f2dd703029b8a3250d04338623aea1a Mon Sep 17 00:00:00 2001 +From fab08a275f328e2e0a6fef73226e45eb1d4bb108 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel -Date: Thu, 18 May 2023 05:19:48 +0200 -Subject: [PATCH] clk: divider: Fix divisions +Date: Tue, 24 Oct 2023 16:09:35 +0200 +Subject: [PATCH 1/3] math.h: add DIV_ROUND_UP_NO_OVERFLOW + +Add a new DIV_ROUND_UP helper, which cannot overflow when +big numbers are being used. + +Signed-off-by: Sebastian Reichel +--- + include/linux/math.h | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/include/linux/math.h b/include/linux/math.h +index dd4152711de7..f80bfb375ab9 100644 +--- a/include/linux/math.h ++++ b/include/linux/math.h +@@ -36,6 +36,17 @@ + + #define DIV_ROUND_UP __KERNEL_DIV_ROUND_UP + ++/** ++ * DIV_ROUND_UP_NO_OVERFLOW - divide two numbers and always round up ++ * @n: numerator / dividend ++ * @d: denominator / divisor ++ * ++ * This functions does the same as DIV_ROUND_UP, but internally uses a ++ * division and a modulo operation instead of math tricks. This way it ++ * avoids overflowing when handling big numbers. ++ */ ++#define DIV_ROUND_UP_NO_OVERFLOW(n, d) (((n) / (d)) + !!((n) % (d))) ++ + #define DIV_ROUND_DOWN_ULL(ll, d) \ + ({ unsigned long long _tmp = (ll); do_div(_tmp, d); _tmp; }) + +-- +2.42.1 + + +From 967c218122840e468981031fd8888846727f5282 Mon Sep 17 00:00:00 2001 +From: Sebastian Reichel +Date: Tue, 24 Oct 2023 16:13:50 +0200 +Subject: [PATCH 2/3] clk: divider: Fix divisor masking on 64 bit platforms The clock framework handles clock rates as "unsigned long", so u32 on 32-bit architectures and u64 on 64-bit architectures. -The current code pointlessly casts the dividend to u64 on 32-bit -architectures and thus pointlessly reducing the performance. +The current code casts the dividend to u64 on 32-bit to avoid a +potential overflow. For example DIV_ROUND_UP(3000000000, 1500000000) += (3.0G + 1.5G - 1) / 1.5G = = OVERFLOW / 1.5G, which has been +introduced in commit 9556f9dad8f5 ("clk: divider: handle integer overflow +when dividing large clock rates"). -On the other hand on 64-bit architectures the divisor is masked and only -the lower 32-bit are used. Thus requesting a frequency >= 4.3GHz results +On 64 bit platforms this masks the divisor, so that only the lower +32 bit are used. Thus requesting a frequency >= 4.3GHz results in incorrect values. For example requesting 4300000000 (4.3 GHz) will effectively request ca. 5 MHz. Requesting clk_round_rate(clk, ULONG_MAX) is a bit of a special case, since that still returns correct values as long as the parent clock is below 8.5 GHz. +Fix this by switching to DIV_ROUND_UP_NO_OVERFLOW, which cannot +overflow. This avoids any requirements on the arguments (except +that divisor should not be 0 obviously). + Signed-off-by: Sebastian Reichel --- drivers/clk/clk-divider.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c -index a2c2b5203b0a9..c38e8aa60e547 100644 +index a2c2b5203b0a..94b4fb66a60f 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -220,7 +220,7 @@ static int _div_round_up(const struct clk_div_table *table, @@ -30,7 +76,7 @@ index a2c2b5203b0a9..c38e8aa60e547 100644 unsigned long flags) { - int div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); -+ int div = DIV_ROUND_UP(parent_rate, rate); ++ int div = DIV_ROUND_UP_NO_OVERFLOW(parent_rate, rate); if (flags & CLK_DIVIDER_POWER_OF_TWO) div = __roundup_pow_of_two(div); @@ -39,7 +85,7 @@ index a2c2b5203b0a9..c38e8aa60e547 100644 unsigned long up_rate, down_rate; - up = DIV_ROUND_UP_ULL((u64)parent_rate, rate); -+ up = DIV_ROUND_UP(parent_rate, rate); ++ up = DIV_ROUND_UP_NO_OVERFLOW(parent_rate, rate); down = parent_rate / rate; if (flags & CLK_DIVIDER_POWER_OF_TWO) { @@ -48,10 +94,51 @@ index a2c2b5203b0a9..c38e8aa60e547 100644 unsigned int div, value; - div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); -+ div = DIV_ROUND_UP(parent_rate, rate); ++ div = DIV_ROUND_UP_NO_OVERFLOW(parent_rate, rate); if (!_is_valid_div(table, div, flags)) return -EINVAL; -- -GitLab +2.42.1 + + +From 5747896098cee178de4bed1eb0052893690eb40e Mon Sep 17 00:00:00 2001 +From: Sebastian Reichel +Date: Tue, 24 Oct 2023 18:09:57 +0200 +Subject: [PATCH 3/3] clk: composite: replace open-coded abs_diff() + +Replace the open coded abs_diff() with the existing helper function. + +Suggested-by: Andy Shevchenko +Signed-off-by: Sebastian Reichel +--- + drivers/clk/clk-composite.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c +index 66759fe28fad..478a4e594336 100644 +--- a/drivers/clk/clk-composite.c ++++ b/drivers/clk/clk-composite.c +@@ -6,6 +6,7 @@ + #include + #include + #include ++#include + #include + + static u8 clk_composite_get_parent(struct clk_hw *hw) +@@ -119,10 +120,7 @@ static int clk_composite_determine_rate(struct clk_hw *hw, + if (ret) + continue; + +- if (req->rate >= tmp_req.rate) +- rate_diff = req->rate - tmp_req.rate; +- else +- rate_diff = tmp_req.rate - req->rate; ++ rate_diff = abs_diff(req->rate, tmp_req.rate); + + if (!rate_diff || !req->best_parent_hw + || best_rate_diff > rate_diff) { +-- +2.42.1 diff --git a/patch/kernel/rockchip-rk3588-edge/0020-USB3-Host-Support-For-Rock5.patch b/patch/kernel/rockchip-rk3588-edge/0020-USB3-Host-Support-For-Rock5.patch new file mode 100644 index 0000000000..361632ee3a --- /dev/null +++ b/patch/kernel/rockchip-rk3588-edge/0020-USB3-Host-Support-For-Rock5.patch @@ -0,0 +1,85 @@ +From a2439d839c103c029294042b5b3d4a065e5073d0 Mon Sep 17 00:00:00 2001 +From: Sebastian Reichel +Date: Wed, 19 Jul 2023 15:56:42 +0200 +Subject: [PATCH 1/2] arm64: dts: rockchip: add USB3 host to rock-5b + +Enable USB3 host controller for the Radxa ROCK 5 Model B. This adds +USB3 for the upper USB3 port (the one further away from the PCB). + +The lower USB3 and the USB-C ports use the RK3588 USB TypeC host +controller, which use a different PHY without upstream support. + +Signed-off-by: Sebastian Reichel +--- + arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts +index 741f631db345..61b937beca7f 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts +@@ -138,6 +138,10 @@ &combphy1_ps { + status = "okay"; + }; + ++&combphy2_psu { ++ status = "okay"; ++}; ++ + &cpu_b0 { + cpu-supply = <&vdd_cpu_big0_s0>; + }; +@@ -765,3 +769,7 @@ &usb_host1_ehci { + &usb_host1_ohci { + status = "okay"; + }; ++ ++&usb_host2_xhci { ++ status = "okay"; ++}; +-- +2.42.1 + + +From 292226fcc7af3e6d5e3b1587459146042fb8a2cf Mon Sep 17 00:00:00 2001 +From: Sebastian Reichel +Date: Fri, 21 Jul 2023 15:19:37 +0200 +Subject: [PATCH 2/2] arm64: dts: rockchip: add USB3 host to rock-5a + +Enable USB3 host controller for the Radxa ROCK 5 Model A. This adds +USB3 for the lower USB3 port (the one closer to the PCB). + +The upper USB3 port uses the RK3588 USB TypeC host controller, which +use a different PHY without upstream support. + +Signed-off-by: Sebastian Reichel +--- + arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts b/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts +index 8347adcbd003..6fb03294576c 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts +@@ -114,6 +114,10 @@ vcc_1v1_nldo_s3: vcc-1v1-nldo-s3-regulator { + }; + }; + ++&combphy2_psu { ++ status = "okay"; ++}; ++ + &cpu_b0 { + cpu-supply = <&vdd_cpu_big0_s0>; + }; +@@ -734,3 +738,7 @@ &usb_host1_ehci { + &usb_host1_ohci { + status = "okay"; + }; ++ ++&usb_host2_xhci { ++ status = "okay"; ++}; +-- +2.42.1 + diff --git a/patch/kernel/rockchip-rk3588-edge/0024-Add-RK3588-USB3-Support.patch b/patch/kernel/rockchip-rk3588-edge/0021-RK3588-Add-USB3-DRD-Support.patch similarity index 83% rename from patch/kernel/rockchip-rk3588-edge/0024-Add-RK3588-USB3-Support.patch rename to patch/kernel/rockchip-rk3588-edge/0021-RK3588-Add-USB3-DRD-Support.patch index d59e0a85a8..45c44f891b 100644 --- a/patch/kernel/rockchip-rk3588-edge/0024-Add-RK3588-USB3-Support.patch +++ b/patch/kernel/rockchip-rk3588-edge/0021-RK3588-Add-USB3-DRD-Support.patch @@ -1,219 +1,7 @@ -From 66ec39c995b0058005c94abdeef9b8cc98f3a045 Mon Sep 17 00:00:00 2001 -From: Sebastian Reichel -Date: Thu, 20 Jul 2023 18:05:56 +0200 -Subject: [PATCH 1/9] dt-bindings: usb: add rk3588 compatible to rockchip,dwc3 - -RK3588 has three DWC3 controllers. Two of them are fully functional in -host, device and OTG mode including USB2 support. They are connected to -dedicated PHYs, that also support USB-C's DisplayPort alternate mode. - -The third controller is connected to one of the combphy's shared -with PCIe and SATA. It can only be used in host mode and does not -support USB2. Compared to the other controllers this one needs -some extra clocks. - -Signed-off-by: Sebastian Reichel ---- - .../devicetree/bindings/usb/rockchip,dwc3.yaml | 11 +++++++++-- - 1 file changed, 9 insertions(+), 2 deletions(-) - -diff --git a/Documentation/devicetree/bindings/usb/rockchip,dwc3.yaml b/Documentation/devicetree/bindings/usb/rockchip,dwc3.yaml -index 291844c8f3e1..cbc3e55e05e1 100644 ---- a/Documentation/devicetree/bindings/usb/rockchip,dwc3.yaml -+++ b/Documentation/devicetree/bindings/usb/rockchip,dwc3.yaml -@@ -30,6 +30,7 @@ select: - enum: - - rockchip,rk3328-dwc3 - - rockchip,rk3568-dwc3 -+ - rockchip,rk3588-dwc3 - required: - - compatible - -@@ -39,6 +40,7 @@ properties: - - enum: - - rockchip,rk3328-dwc3 - - rockchip,rk3568-dwc3 -+ - rockchip,rk3588-dwc3 - - const: snps,dwc3 - - reg: -@@ -58,7 +60,9 @@ properties: - Master/Core clock, must to be >= 62.5 MHz for SS - operation and >= 30MHz for HS operation - - description: -- Controller grf clock -+ Controller grf clock OR UTMI clock -+ - description: -+ PIPE clock - - clock-names: - minItems: 3 -@@ -66,7 +70,10 @@ properties: - - const: ref_clk - - const: suspend_clk - - const: bus_clk -- - const: grf_clk -+ - enum: -+ - grf_clk -+ - utmi -+ - const: pipe - - power-domains: - maxItems: 1 --- -2.41.0 - - -From 4085e92d3e3980fcd074c0aee47ab14ed2b534a5 Mon Sep 17 00:00:00 2001 -From: Sebastian Reichel -Date: Thu, 20 Jul 2023 18:59:37 +0200 -Subject: [PATCH 2/9] usb: dwc3: add optional PHY interface clocks - -On Rockchip RK3588 one of the DWC3 cores is integrated weirdly and -requires two extra clocks to be enabled. Without these extra clocks -hot-plugging USB devices is broken. - -Signed-off-by: Sebastian Reichel ---- - drivers/usb/dwc3/core.c | 26 ++++++++++++++++++++++++++ - drivers/usb/dwc3/core.h | 4 ++++ - 2 files changed, 30 insertions(+) - -diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c -index f6689b731718..0618fcc4d0af 100644 ---- a/drivers/usb/dwc3/core.c -+++ b/drivers/usb/dwc3/core.c -@@ -817,8 +817,20 @@ static int dwc3_clk_enable(struct dwc3 *dwc) - if (ret) - goto disable_ref_clk; - -+ ret = clk_prepare_enable(dwc->utmi_clk); -+ if (ret) -+ goto disable_susp_clk; -+ -+ ret = clk_prepare_enable(dwc->pipe_clk); -+ if (ret) -+ goto disable_utmi_clk; -+ - return 0; - -+disable_utmi_clk: -+ clk_disable_unprepare(dwc->utmi_clk); -+disable_susp_clk: -+ clk_disable_unprepare(dwc->susp_clk); - disable_ref_clk: - clk_disable_unprepare(dwc->ref_clk); - disable_bus_clk: -@@ -828,6 +840,8 @@ static int dwc3_clk_enable(struct dwc3 *dwc) - - static void dwc3_clk_disable(struct dwc3 *dwc) - { -+ clk_disable_unprepare(dwc->pipe_clk); -+ clk_disable_unprepare(dwc->utmi_clk); - clk_disable_unprepare(dwc->susp_clk); - clk_disable_unprepare(dwc->ref_clk); - clk_disable_unprepare(dwc->bus_clk); -@@ -1764,6 +1778,18 @@ static int dwc3_get_clocks(struct dwc3 *dwc) - } - } - -+ dwc->utmi_clk = devm_clk_get_optional(dev, "utmi"); -+ if (IS_ERR(dwc->utmi_clk)) { -+ return dev_err_probe(dev, PTR_ERR(dwc->utmi_clk), -+ "could not get utmi clock\n"); -+ } -+ -+ dwc->pipe_clk = devm_clk_get_optional(dev, "pipe"); -+ if (IS_ERR(dwc->pipe_clk)) { -+ return dev_err_probe(dev, PTR_ERR(dwc->pipe_clk), -+ "could not get pipe clock\n"); -+ } -+ - return 0; - } - -diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h -index 8b1295e4dcdd..fa49a87025cf 100644 ---- a/drivers/usb/dwc3/core.h -+++ b/drivers/usb/dwc3/core.h -@@ -994,6 +994,8 @@ struct dwc3_scratchpad_array { - * @bus_clk: clock for accessing the registers - * @ref_clk: reference clock - * @susp_clk: clock used when the SS phy is in low power (S3) state -+ * @utmi_clk: clock used for USB2 PHY communication -+ * @pipe_clk: clock used for USB3 PHY communication - * @reset: reset control - * @regs: base address for our registers - * @regs_size: address space size -@@ -1159,6 +1161,8 @@ struct dwc3 { - struct clk *bus_clk; - struct clk *ref_clk; - struct clk *susp_clk; -+ struct clk *utmi_clk; -+ struct clk *pipe_clk; - - struct reset_control *reset; - --- -2.41.0 - - -From 3d9edd8eba42f0e738503ddce51b3e49a4d9f99e Mon Sep 17 00:00:00 2001 -From: Sebastian Reichel -Date: Tue, 18 Jul 2023 18:57:15 +0200 -Subject: [PATCH 3/9] arm64: dts: rockchip: rk3588s: Add USB3 host controller - -RK3588 has three USB3 controllers. This adds the host-only controller, -which is using the naneng-combphy shared with PCIe and SATA. - -The other two are dual-role and using a different PHY that is not yet -supported upstream. - -Signed-off-by: Sebastian Reichel ---- - arch/arm64/boot/dts/rockchip/rk3588s.dtsi | 21 +++++++++++++++++++++ - 1 file changed, 21 insertions(+) - -diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi -index 38be80dd7ded..2dfd6b40a5a6 100644 ---- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi -+++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi -@@ -885,6 +885,27 @@ usb_host1_ohci: usb@fc8c0000 { - status = "disabled"; - }; - -+ usb_host2_xhci: usb@fcd00000 { -+ compatible = "rockchip,rk3588-dwc3", "snps,dwc3"; -+ reg = <0x0 0xfcd00000 0x0 0x400000>; -+ interrupts = ; -+ clocks = <&cru REF_CLK_USB3OTG2>, <&cru SUSPEND_CLK_USB3OTG2>, -+ <&cru ACLK_USB3OTG2>, <&cru CLK_UTMI_OTG2>, -+ <&cru CLK_PIPEPHY2_PIPE_U3_G>; -+ clock-names = "ref_clk", "suspend_clk", "bus_clk", "utmi", "pipe"; -+ dr_mode = "host"; -+ phys = <&combphy2_psu PHY_TYPE_USB3>; -+ phy-names = "usb3-phy"; -+ phy_type = "utmi_wide"; -+ resets = <&cru SRST_A_USB3OTG2>; -+ snps,dis_enblslpm_quirk; -+ snps,dis-u2-freeclk-exists-quirk; -+ snps,dis-del-phy-power-chg-quirk; -+ snps,dis-tx-ipgap-linecheck-quirk; -+ snps,dis_rxdet_inp3_quirk; -+ status = "disabled"; -+ }; -+ - sys_grf: syscon@fd58c000 { - compatible = "rockchip,rk3588-sys-grf", "syscon"; - reg = <0x0 0xfd58c000 0x0 0x1000>; --- -2.41.0 - - -From 74b7b627481b7fcd662f170d493dc80658d89f70 Mon Sep 17 00:00:00 2001 +From d51dcd5c602d78fadc4cc8f83b851264c4ac18db Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 25 Apr 2023 17:38:57 +0200 -Subject: [PATCH 4/9] dt-bindings: phy: add rockchip usbdp combo phy document +Subject: [PATCH 01/10] dt-bindings: phy: add rockchip usbdp combo phy document Add device tree binding document for Rockchip USBDP Combo PHY with Samsung IP block. @@ -399,13 +187,13 @@ index 000000000000..dcca84d57e99 + status = "disabled"; + }; -- -2.41.0 +2.42.1 -From 5c99443bc11d4853e4cf83274c51286da83f63d0 Mon Sep 17 00:00:00 2001 +From b3b601dd3a6d35779385b716a898e43071f802e5 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 30 May 2023 18:49:48 +0200 -Subject: [PATCH 5/9] dt-bindings: soc: rockchip: add rk3588 USB3 syscon +Subject: [PATCH 02/10] dt-bindings: soc: rockchip: add rk3588 USB3 syscon RK3588 USB3 support requires the GRF for USB, USBDP PHY and VO. @@ -462,13 +250,13 @@ index e4fa6a07b4fa..ce1fd5b0d669 100644 - | #include -- -2.41.0 +2.42.1 -From 7c7a44487d20f8c2a90eafec4d4f399b5c6518de Mon Sep 17 00:00:00 2001 +From 5c34bb1e195fbbcaccf42a04f56e8d035d0864bb Mon Sep 17 00:00:00 2001 From: Frank Wang Date: Tue, 25 Apr 2023 15:55:54 +0200 -Subject: [PATCH 6/9] phy: rockchip: add usbdp combo phy driver +Subject: [PATCH 03/10] phy: rockchip: add usbdp combo phy driver This adds a new USBDP combo PHY with Samsung IP block driver. @@ -487,8 +275,8 @@ Signed-off-by: Sebastian Reichel --- drivers/phy/rockchip/Kconfig | 12 + drivers/phy/rockchip/Makefile | 1 + - drivers/phy/rockchip/phy-rockchip-usbdp.c | 1728 +++++++++++++++++++++ - 3 files changed, 1741 insertions(+) + drivers/phy/rockchip/phy-rockchip-usbdp.c | 1749 +++++++++++++++++++++ + 3 files changed, 1762 insertions(+) create mode 100644 drivers/phy/rockchip/phy-rockchip-usbdp.c diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig @@ -522,10 +310,10 @@ index 7eab129230d1..25d2e1355db7 100644 +obj-$(CONFIG_PHY_ROCKCHIP_USBDP) += phy-rockchip-usbdp.o diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c new file mode 100644 -index 000000000000..414081b1247d +index 000000000000..bb0beafb8578 --- /dev/null +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c -@@ -0,0 +1,1728 @@ +@@ -0,0 +1,1749 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Rockchip USBDP Combo PHY with Samsung IP block driver @@ -1002,6 +790,34 @@ index 000000000000..414081b1247d + return 0; +} + ++static int udphy_reset_assert_all(struct rockchip_udphy *udphy) ++{ ++ const struct rockchip_udphy_cfg *cfg = udphy->cfgs; ++ int idx, ret; ++ ++ for (idx = 0; idx < cfg->num_rsts; idx++) { ++ ret = reset_control_assert(udphy->rsts[idx]); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int udphy_reset_deassert_all(struct rockchip_udphy *udphy) ++{ ++ const struct rockchip_udphy_cfg *cfg = udphy->cfgs; ++ int idx, ret; ++ ++ for (idx = 0; idx < cfg->num_rsts; idx++) { ++ ret = reset_control_deassert(udphy->rsts[idx]); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ +static int udphy_get_rst_idx(const char * const *list, int num, char *name) +{ + int idx; @@ -1242,13 +1058,8 @@ index 000000000000..414081b1247d + +static int udphy_disable(struct rockchip_udphy *udphy) +{ -+ const struct rockchip_udphy_cfg *cfg = udphy->cfgs; -+ int i; -+ + clk_bulk_disable_unprepare(udphy->num_clks, udphy->clks); -+ -+ for (i = 0; i < cfg->num_rsts; i++) -+ reset_control_assert(udphy->rsts[i]); ++ udphy_reset_assert_all(udphy); + + return 0; +} @@ -1297,16 +1108,17 @@ index 000000000000..414081b1247d + } + + udphy->mode = UDPHY_MODE_DP; -+ if (num_lanes == 2) ++ if (num_lanes == 2) { + udphy->mode |= UDPHY_MODE_USB; ++ udphy->flip = (udphy->lane_mux_sel[0] == PHY_LANE_MUX_DP); ++ } + + return 0; +} + +static int udphy_get_initial_status(struct rockchip_udphy *udphy) +{ -+ const struct rockchip_udphy_cfg *cfg = udphy->cfgs; -+ int ret, i; ++ int ret; + u32 value; + + ret = clk_bulk_prepare_enable(udphy->num_clks, udphy->clks); @@ -1315,8 +1127,7 @@ index 000000000000..414081b1247d + return ret; + } + -+ for (i = 0; i < cfg->num_rsts; i++) -+ reset_control_deassert(udphy->rsts[i]); ++ udphy_reset_deassert_all(udphy); + + regmap_read(udphy->pma_regmap, CMN_LANE_MUX_AND_EN_OFFSET, &value); + if (FIELD_GET(CMN_DP_LANE_MUX_ALL, value) && FIELD_GET(CMN_DP_LANE_EN_ALL, value)) @@ -1787,7 +1598,7 @@ index 000000000000..414081b1247d + + phy_cfgs = device_get_match_data(dev); + if (!phy_cfgs) { -+ dev_err(dev, "no OF data can be matched with %p node\n", np); ++ dev_err(dev, "missing match data\n"); + return -EINVAL; + } + @@ -1916,9 +1727,7 @@ index 000000000000..414081b1247d + dev_err(udphy->dev, "cmn ana lcpll lock timeout\n"); + return ret; + } -+ } + -+ if (udphy->mode & UDPHY_MODE_USB) { + if (!udphy->flip) { + ret = regmap_read_poll_timeout(udphy->pma_regmap, + TRSV_LN0_MON_RX_CDR_DONE_OFFSET, val, @@ -2255,13 +2064,13 @@ index 000000000000..414081b1247d +MODULE_DESCRIPTION("Rockchip USBDP Combo PHY driver"); +MODULE_LICENSE("GPL"); -- -2.41.0 +2.42.1 -From 42b75ff827afe22965425ce3f11812ab281c67ca Mon Sep 17 00:00:00 2001 +From 27a37c0495193fcfef1787086821c57f20b759bc Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 25 Apr 2023 17:49:04 +0200 -Subject: [PATCH 7/9] arm64: dts: rockchip: rk3588s: Add USBDP phy nodes +Subject: [PATCH 04/10] arm64: dts: rockchip: rk3588s: Add USBDP phy nodes Add both USB3-Displayport PHYs from RK3588. @@ -2272,7 +2081,7 @@ Signed-off-by: Sebastian Reichel 2 files changed, 135 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3588.dtsi b/arch/arm64/boot/dts/rockchip/rk3588.dtsi -index 8f210f002fac..b18fb5dc793e 100644 +index 5519c1430cb7..c26288ec75ce 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588.dtsi @@ -17,6 +17,37 @@ pipe_phy1_grf: syscon@fd5c0000 { @@ -2352,10 +2161,10 @@ index 8f210f002fac..b18fb5dc793e 100644 compatible = "rockchip,rk3588-naneng-combphy"; reg = <0x0 0xfee10000 0x0 0x100>; diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi -index 2dfd6b40a5a6..1cf090a75221 100644 +index 7064c0e9179f..d75cf8cc6fb3 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi -@@ -936,6 +936,37 @@ pipe_phy2_grf: syscon@fd5c4000 { +@@ -489,6 +489,37 @@ pipe_phy2_grf: syscon@fd5c4000 { reg = <0x0 0xfd5c4000 0x0 0x100>; }; @@ -2393,7 +2202,7 @@ index 2dfd6b40a5a6..1cf090a75221 100644 usb2phy2_grf: syscon@fd5d8000 { compatible = "rockchip,rk3588-usb2phy-grf", "syscon", "simple-mfd"; reg = <0x0 0xfd5d8000 0x0 0x4000>; -@@ -961,6 +992,17 @@ u2phy2_host: host-port { +@@ -514,6 +545,17 @@ u2phy2_host: host-port { }; }; @@ -2411,7 +2220,7 @@ index 2dfd6b40a5a6..1cf090a75221 100644 usb2phy3_grf: syscon@fd5dc000 { compatible = "rockchip,rk3588-usb2phy-grf", "syscon", "simple-mfd"; reg = <0x0 0xfd5dc000 0x0 0x4000>; -@@ -2670,6 +2712,37 @@ dmac2: dma-controller@fed10000 { +@@ -2245,6 +2287,37 @@ dmac2: dma-controller@fed10000 { #dma-cells = <1>; }; @@ -2450,13 +2259,13 @@ index 2dfd6b40a5a6..1cf090a75221 100644 compatible = "rockchip,rk3588-naneng-combphy"; reg = <0x0 0xfee00000 0x0 0x100>; -- -2.41.0 +2.42.1 -From 5747a511fa71d43d504bfd9c6a14cd93fb9122c6 Mon Sep 17 00:00:00 2001 +From acb101c5f99c54d083427b2a07f8a9610a468bef Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 18 Jul 2023 19:05:38 +0200 -Subject: [PATCH 8/9] arm64: dts: rockchip: rk3588s: Add USB3 DRD controllers +Subject: [PATCH 05/10] arm64: dts: rockchip: rk3588s: Add USB3 DRD controllers Add both USB3 dual-role controllers to the RK3588 devicetree. @@ -2467,7 +2276,7 @@ Signed-off-by: Sebastian Reichel 2 files changed, 42 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3588.dtsi b/arch/arm64/boot/dts/rockchip/rk3588.dtsi -index b18fb5dc793e..01778f609f98 100644 +index c26288ec75ce..900ac0300f23 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588.dtsi @@ -7,6 +7,26 @@ @@ -2498,10 +2307,10 @@ index b18fb5dc793e..01778f609f98 100644 compatible = "rockchip,rk3588-pcie3-phy-grf", "syscon"; reg = <0x0 0xfd5b8000 0x0 0x10000>; diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi -index 1cf090a75221..5dde719c9cd7 100644 +index d75cf8cc6fb3..f3ddeb160557 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi -@@ -841,6 +841,28 @@ scmi_shmem: sram@0 { +@@ -399,6 +399,28 @@ scmi_shmem: sram@0 { }; }; @@ -2531,13 +2340,217 @@ index 1cf090a75221..5dde719c9cd7 100644 compatible = "rockchip,rk3588-ehci", "generic-ehci"; reg = <0x0 0xfc800000 0x0 0x40000>; -- -2.41.0 +2.42.1 -From 979d640533724306d8e8992ce2f97d9d1b5d6cb5 Mon Sep 17 00:00:00 2001 +From 479c1ae8a93f901a5898e2ed204b931c68de63fd Mon Sep 17 00:00:00 2001 +From: Sebastian Reichel +Date: Wed, 26 Apr 2023 21:18:43 +0200 +Subject: [PATCH 06/10] arm64: dts: rockchip: rk3588-evb1: add USB3 + +Add support for the boards USB3 type A, as well as its Type-C +connector. + +Signed-off-by: Sebastian Reichel +--- + .../boot/dts/rockchip/rk3588-evb1-v10.dts | 144 ++++++++++++++++++ + 1 file changed, 144 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts +index 3369065f897d..ee45b8a801ad 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include "rk3588.dtsi" + + / { +@@ -224,6 +225,18 @@ vcc5v0_usb: vcc5v0-usb-regulator { + regulator-max-microvolt = <5000000>; + vin-supply = <&vcc5v0_usbdcin>; + }; ++ ++ vbus5v0_typec: vbus5v0-typec { ++ compatible = "regulator-fixed"; ++ regulator-name = "vbus5v0_typec"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ enable-active-high; ++ gpio = <&gpio4 RK_PD0 GPIO_ACTIVE_HIGH>; ++ vin-supply = <&vcc5v0_usb>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&typec5v_pwren>; ++ }; + }; + + &combphy0_ps { +@@ -284,6 +297,56 @@ &gmac0_rgmii_clk + &i2c2 { + status = "okay"; + ++ usbc0: usb-typec@22 { ++ compatible = "fcs,fusb302"; ++ reg = <0x22>; ++ interrupt-parent = <&gpio3>; ++ interrupts = ; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&usbc0_int>; ++ vbus-supply = <&vbus5v0_typec>; ++ status = "okay"; ++ ++ usb_con: connector { ++ compatible = "usb-c-connector"; ++ label = "USB-C"; ++ data-role = "dual"; ++ power-role = "dual"; ++ try-power-role = "sink"; ++ op-sink-microwatt = <1000000>; ++ sink-pdos = ++ ; ++ source-pdos = ++ ; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ reg = <0>; ++ usbc0_orien_sw: endpoint { ++ remote-endpoint = <&usbdp_phy0_orientation_switch>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ usbc0_role_sw: endpoint { ++ remote-endpoint = <&dwc3_0_role_switch>; ++ }; ++ }; ++ ++ port@2 { ++ reg = <2>; ++ dp_altmode_mux: endpoint { ++ remote-endpoint = <&usbdp_phy0_dp_altmode_mux>; ++ }; ++ }; ++ }; ++ }; ++ }; ++ + hym8563: rtc@51 { + compatible = "haoyu,hym8563"; + reg = <0x51>; +@@ -410,6 +473,16 @@ vcc5v0_host_en: vcc5v0-host-en { + rockchip,pins = <4 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; ++ ++ usb-typec { ++ usbc0_int: usbc0-int { ++ rockchip,pins = <3 RK_PB4 RK_FUNC_GPIO &pcfg_pull_up>; ++ }; ++ ++ typec5v_pwren: typec5v-pwren { ++ rockchip,pins = <4 RK_PD0 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ }; + }; + + &pwm2 { +@@ -1040,6 +1113,22 @@ &sata0 { + status = "okay"; + }; + ++&u2phy0 { ++ status = "okay"; ++}; ++ ++&u2phy0_otg { ++ status = "okay"; ++}; ++ ++&u2phy1 { ++ status = "okay"; ++}; ++ ++&u2phy1_otg { ++ status = "okay"; ++}; ++ + &u2phy2 { + status = "okay"; + }; +@@ -1078,3 +1167,58 @@ &usb_host1_ehci { + &usb_host1_ohci { + status = "okay"; + }; ++ ++&usbdp_phy0 { ++ orientation-switch; ++ mode-switch; ++ sbu1-dc-gpios = <&gpio4 RK_PA6 GPIO_ACTIVE_HIGH>; ++ sbu2-dc-gpios = <&gpio4 RK_PA7 GPIO_ACTIVE_HIGH>; ++ status = "okay"; ++ ++ port { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ usbdp_phy0_orientation_switch: endpoint@0 { ++ reg = <0>; ++ remote-endpoint = <&usbc0_orien_sw>; ++ }; ++ ++ usbdp_phy0_dp_altmode_mux: endpoint@1 { ++ reg = <1>; ++ remote-endpoint = <&dp_altmode_mux>; ++ }; ++ }; ++}; ++ ++&usbdp_phy0_u3 { ++ status = "okay"; ++}; ++ ++&usbdp_phy1 { ++ rockchip,dp-lane-mux = <2 3>; ++ status = "okay"; ++}; ++ ++&usbdp_phy1_u3 { ++ status = "okay"; ++}; ++ ++&usb_host0_xhci { ++ dr_mode = "otg"; ++ usb-role-switch; ++ status = "okay"; ++ ++ port { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ dwc3_0_role_switch: endpoint@0 { ++ reg = <0>; ++ remote-endpoint = <&usbc0_role_sw>; ++ }; ++ }; ++}; ++ ++&usb_host1_xhci { ++ status = "okay"; ++}; +-- +2.42.1 + + +From ddb788db4d8b352742a6efcc8559f4c32c38925c Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 28 Jul 2023 16:43:16 +0200 -Subject: [PATCH 9/9] usb: typec: tcpm: avoid graph warning +Subject: [PATCH 07/10] usb: typec: tcpm: avoid graph warning When using a devicetree as described in commit d56de8c9a17d ("usb: typec: tcpm: try to get role switch from tcpc fwnode"), the kernel @@ -2563,10 +2576,10 @@ Signed-off-by: Sebastian Reichel 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c -index 829d75ebab42..6f505f45d33f 100644 +index 058d5b853b57..a3ef7d70304e 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c -@@ -6615,9 +6615,9 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) +@@ -6588,9 +6588,9 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) port->partner_desc.identity = &port->partner_ident; port->port_type = port->typec_caps.type; @@ -2579,5 +2592,329 @@ index 829d75ebab42..6f505f45d33f 100644 err = PTR_ERR(port->role_sw); goto out_destroy_wq; -- -2.41.0 +2.42.1 + + +From 0080918c3a50cad588fba468fb7934c80777aa07 Mon Sep 17 00:00:00 2001 +From: Sebastian Reichel +Date: Tue, 25 Jul 2023 16:30:46 +0200 +Subject: [PATCH 08/10] arm64: dts: rockchip: rk3588s-rock5a: add upper USB3 + port + +Enable full support (XHCI, EHCI, OHCI) for the upper USB3 port from +Radxa Rock 5 Model A. + +Signed-off-by: Sebastian Reichel +--- + .../boot/dts/rockchip/rk3588s-rock-5a.dts | 22 +++++++++++++++++++ + 1 file changed, 22 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts b/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts +index 6fb03294576c..a6ec5e770e71 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts +@@ -698,6 +698,14 @@ regulator-state-mem { + }; + }; + ++&u2phy0 { ++ status = "okay"; ++}; ++ ++&u2phy0_otg { ++ status = "okay"; ++}; ++ + &u2phy2 { + status = "okay"; + }; +@@ -721,6 +729,15 @@ &uart2 { + status = "okay"; + }; + ++&usbdp_phy0 { ++ status = "okay"; ++ rockchip,dp-lane-mux = <2 3>; ++}; ++ ++&usbdp_phy0_u3 { ++ status = "okay"; ++}; ++ + &usb_host0_ehci { + status = "okay"; + pinctrl-names = "default"; +@@ -731,6 +748,11 @@ &usb_host0_ohci { + status = "okay"; + }; + ++&usb_host0_xhci { ++ dr_mode = "host"; ++ status = "okay"; ++}; ++ + &usb_host1_ehci { + status = "okay"; + }; +-- +2.42.1 + + +From 5afa85f867c29153afc1b801a31f55cd3021f3a6 Mon Sep 17 00:00:00 2001 +From: Sebastian Reichel +Date: Tue, 25 Jul 2023 17:18:17 +0200 +Subject: [PATCH 09/10] arm64: dts: rockchip: rk3588-rock5b: add lower USB3 + port + +Enable full support (XHCI, EHCI, OHCI) for the lower USB3 port from +Radxa Rock 5 Model B. + +Signed-off-by: Sebastian Reichel +--- + .../boot/dts/rockchip/rk3588-rock-5b.dts | 20 +++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts +index 61b937beca7f..bd38c67b6c76 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts +@@ -735,6 +735,14 @@ &uart2 { + status = "okay"; + }; + ++&u2phy1 { ++ status = "okay"; ++}; ++ ++&u2phy1_otg { ++ status = "okay"; ++}; ++ + &u2phy2 { + status = "okay"; + }; +@@ -754,6 +762,14 @@ &u2phy3_host { + status = "okay"; + }; + ++&usbdp_phy1 { ++ status = "okay"; ++}; ++ ++&usbdp_phy1_u3 { ++ status = "okay"; ++}; ++ + &usb_host0_ehci { + status = "okay"; + }; +@@ -770,6 +786,10 @@ &usb_host1_ohci { + status = "okay"; + }; + ++&usb_host1_xhci { ++ status = "okay"; ++}; ++ + &usb_host2_xhci { + status = "okay"; + }; +-- +2.42.1 + + +From 8f64d6bcf306fa5b5de66fdee2458cb584a78b2e Mon Sep 17 00:00:00 2001 +From: Sebastian Reichel +Date: Tue, 25 Jul 2023 18:35:56 +0200 +Subject: [PATCH 10/10] arm64: dts: rockchip: rk3588-rock5b: add USB-C support + +Add support for using the Radxa Rock 5 Model B USB-C port for USB in +OHCI, EHCI or XHCI mode. Displayport AltMode is not yet supported. + +Signed-off-by: Sebastian Reichel +--- + .../boot/dts/rockchip/rk3588-rock-5b.dts | 119 ++++++++++++++++++ + 1 file changed, 119 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts +index bd38c67b6c76..419d0202f7fc 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts +@@ -4,6 +4,7 @@ + + #include + #include ++#include + #include "rk3588.dtsi" + + / { +@@ -59,6 +60,15 @@ fan: pwm-fan { + #cooling-cells = <2>; + }; + ++ vcc12v_dcin: vcc12v-dcin-regulator { ++ compatible = "regulator-fixed"; ++ regulator-name = "vcc12v_dcin"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <12000000>; ++ regulator-max-microvolt = <12000000>; ++ }; ++ + vcc3v3_pcie2x1l0: vcc3v3-pcie2x1l0-regulator { + compatible = "regulator-fixed"; + enable-active-high; +@@ -117,6 +127,7 @@ vcc5v0_sys: vcc5v0-sys-regulator { + regulator-boot-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; ++ vin-supply = <&vcc12v_dcin>; + }; + + vcc_1v1_nldo_s3: vcc-1v1-nldo-s3-regulator { +@@ -214,6 +225,61 @@ regulator-state-mem { + }; + }; + ++&i2c4 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c4m1_xfer>; ++ status = "okay"; ++ ++ usbc0: usb-typec@22 { ++ compatible = "fcs,fusb302"; ++ reg = <0x22>; ++ interrupt-parent = <&gpio3>; ++ interrupts = ; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&usbc0_int>; ++ vbus-supply = <&vcc12v_dcin>; ++ status = "okay"; ++ ++ usb_con: connector { ++ compatible = "usb-c-connector"; ++ label = "USB-C"; ++ data-role = "dual"; ++ power-role = "sink"; ++ try-power-role = "sink"; ++ op-sink-microwatt = <1000000>; ++ sink-pdos = ++ , ++ ; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ reg = <0>; ++ usbc0_hs: endpoint { ++ remote-endpoint = <&usb_host0_xhci_drd_sw>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ usbc0_ss: endpoint { ++ remote-endpoint = <&usbdp_phy0_typec_ss>; ++ }; ++ }; ++ ++ port@2 { ++ reg = <2>; ++ usbc0_sbu: endpoint { ++ remote-endpoint = <&usbdp_phy0_typec_sbu>; ++ }; ++ }; ++ }; ++ }; ++ }; ++}; ++ + &i2c6 { + status = "okay"; + +@@ -343,6 +409,10 @@ usb { + vcc5v0_host_en: vcc5v0-host-en { + rockchip,pins = <4 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>; + }; ++ ++ usbc0_int: usbc0-int { ++ rockchip,pins = <3 RK_PB4 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; + }; + }; + +@@ -735,6 +805,14 @@ &uart2 { + status = "okay"; + }; + ++&u2phy0 { ++ status = "okay"; ++}; ++ ++&u2phy0_otg { ++ status = "okay"; ++}; ++ + &u2phy1 { + status = "okay"; + }; +@@ -770,6 +848,33 @@ &usbdp_phy1_u3 { + status = "okay"; + }; + ++&usbdp_phy0 { ++ orientation-switch; ++ mode-switch; ++ sbu1-dc-gpios = <&gpio4 RK_PA6 GPIO_ACTIVE_HIGH>; ++ sbu2-dc-gpios = <&gpio4 RK_PA7 GPIO_ACTIVE_HIGH>; ++ status = "okay"; ++ ++ port { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ usbdp_phy0_typec_ss: endpoint@0 { ++ reg = <0>; ++ remote-endpoint = <&usbc0_ss>; ++ }; ++ ++ usbdp_phy0_typec_sbu: endpoint@1 { ++ reg = <1>; ++ remote-endpoint = <&usbc0_sbu>; ++ }; ++ }; ++}; ++ ++&usbdp_phy0_u3 { ++ status = "okay"; ++}; ++ + &usb_host0_ehci { + status = "okay"; + }; +@@ -778,6 +883,20 @@ &usb_host0_ohci { + status = "okay"; + }; + ++&usb_host0_xhci { ++ usb-role-switch; ++ status = "okay"; ++ ++ port { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ usb_host0_xhci_drd_sw: endpoint { ++ remote-endpoint = <&usbc0_hs>; ++ }; ++ }; ++}; ++ + &usb_host1_ehci { + status = "okay"; + }; +-- +2.42.1 diff --git a/patch/kernel/rockchip-rk3588-edge/0023-RK3588-Add-Cpufreq-Support.patch b/patch/kernel/rockchip-rk3588-edge/0022-RK3588-Add-Cpufreq-Support.patch similarity index 86% rename from patch/kernel/rockchip-rk3588-edge/0023-RK3588-Add-Cpufreq-Support.patch rename to patch/kernel/rockchip-rk3588-edge/0022-RK3588-Add-Cpufreq-Support.patch index b8da189711..7c064c4de4 100644 --- a/patch/kernel/rockchip-rk3588-edge/0023-RK3588-Add-Cpufreq-Support.patch +++ b/patch/kernel/rockchip-rk3588-edge/0022-RK3588-Add-Cpufreq-Support.patch @@ -1,7 +1,7 @@ -From f9c3292a2a99acf817f97a2b312a98e42a5eaa2e Mon Sep 17 00:00:00 2001 +From 8baebef8be9691a28f8efa284dfce9a5b9395130 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Thu, 18 Aug 2022 14:21:30 +0200 -Subject: [PATCH 1/2] cpufreq: rockchip: Introduce driver for rk3588 +Subject: [PATCH 1/5] cpufreq: rockchip: Introduce driver for rk3588 This is a heavily modified port from the downstream driver. Downstream used it for multiple rockchip generations, while @@ -23,7 +23,7 @@ Signed-off-by: Sebastian Reichel create mode 100644 drivers/cpufreq/rockchip-cpufreq.c diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm -index 123b4bbfcfee..6fdc2f0430bb 100644 +index f911606897b8..1e255210851e 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -189,6 +189,16 @@ config ARM_RASPBERRYPI_CPUFREQ @@ -44,7 +44,7 @@ index 123b4bbfcfee..6fdc2f0430bb 100644 bool "Samsung S3C64XX" depends on CPU_S3C6410 diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile -index ef8510774913..c3f8c9cd563f 100644 +index 8d141c71b016..14fb48863f0b 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -71,6 +71,7 @@ obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o @@ -56,12 +56,12 @@ index ef8510774913..c3f8c9cd563f 100644 obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c -index e2b20080de3a..7bc19a59be26 100644 +index bd1e1357cef8..cfd35aa52043 100644 --- a/drivers/cpufreq/cpufreq-dt-platdev.c +++ b/drivers/cpufreq/cpufreq-dt-platdev.c -@@ -159,6 +159,8 @@ static const struct of_device_id blocklist[] __initconst = { - { .compatible = "qcom,sm8250", }, - { .compatible = "qcom,sm8350", }, +@@ -168,6 +168,8 @@ static const struct of_device_id blocklist[] __initconst = { + { .compatible = "qcom,sm8450", }, + { .compatible = "qcom,sm8550", }, + { .compatible = "rockchip,rk3588", }, + @@ -720,13 +720,13 @@ index 000000000000..0bf57ac85e60 +MODULE_DESCRIPTION("Rockchip cpufreq driver"); +MODULE_LICENSE("GPL v2"); -- -2.41.0 +2.42.1 -From 1a4f292fcdb4b4a625dda97e8236911b896c8abe Mon Sep 17 00:00:00 2001 +From f542d93ac2d5c4b6458599494f90bd4021d34b2c Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 4 Apr 2023 17:30:46 +0200 -Subject: [PATCH 2/2] arm64: dts: rockchip: rk3588: add cpu frequency scaling +Subject: [PATCH 2/5] arm64: dts: rockchip: rk3588: add cpu frequency scaling support Add required bits for CPU frequency scaling to the Rockchip 3588 @@ -741,7 +741,7 @@ Signed-off-by: Sebastian Reichel 1 file changed, 452 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi -index a90ec13f50b4..aa78f535b27d 100644 +index be694aaef7d0..e7ebeda1c799 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi @@ -10,6 +10,7 @@ @@ -1263,7 +1263,7 @@ index a90ec13f50b4..aa78f535b27d 100644 timer { compatible = "arm,armv8-timer"; interrupts = , -@@ -491,6 +933,16 @@ sys_grf: syscon@fd58c000 { +@@ -496,6 +938,16 @@ sys_grf: syscon@fd58c000 { reg = <0x0 0xfd58c000 0x0 0x1000>; }; @@ -1281,5 +1281,198 @@ index a90ec13f50b4..aa78f535b27d 100644 compatible = "rockchip,rk3588-php-grf", "syscon"; reg = <0x0 0xfd5b0000 0x0 0x1000>; -- -2.41.0 +2.42.1 + + +From e84f55d8a9d849eac51f73c47cdb90eb7dbac90f Mon Sep 17 00:00:00 2001 +From: Sebastian Reichel +Date: Thu, 25 May 2023 19:48:49 +0200 +Subject: [PATCH 3/5] arm64: dts: rockchip: rk3588-evb1: add cpu mem regulator + info + +Add the second supply regulator for the CPU cores, which is used +for supplying the memory interface. + +Signed-off-by: Sebastian Reichel +--- + arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts +index ee45b8a801ad..cbee9e4b86da 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts +@@ -249,34 +249,42 @@ &combphy2_psu { + + &cpu_b0 { + cpu-supply = <&vdd_cpu_big0_s0>; ++ mem-supply = <&vdd_cpu_big0_mem_s0>; + }; + + &cpu_b1 { + cpu-supply = <&vdd_cpu_big0_s0>; ++ mem-supply = <&vdd_cpu_big0_mem_s0>; + }; + + &cpu_b2 { + cpu-supply = <&vdd_cpu_big1_s0>; ++ mem-supply = <&vdd_cpu_big1_mem_s0>; + }; + + &cpu_b3 { + cpu-supply = <&vdd_cpu_big1_s0>; ++ mem-supply = <&vdd_cpu_big1_mem_s0>; + }; + + &cpu_l0 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &cpu_l1 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &cpu_l2 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &cpu_l3 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &gmac0 { +-- +2.42.1 + + +From 643d555335b4c0dc228111a74cfa5189e17616df Mon Sep 17 00:00:00 2001 +From: Sebastian Reichel +Date: Mon, 24 Jul 2023 15:18:39 +0200 +Subject: [PATCH 4/5] arm64: dts: rockchip: rock5a: add cpu mem regulator info + +Add the second supply regulator for the CPU cores, which is used +for supplying the memory interface. + +Signed-off-by: Sebastian Reichel +--- + arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts b/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts +index a6ec5e770e71..58c58ec03a7f 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts +@@ -120,34 +120,42 @@ &combphy2_psu { + + &cpu_b0 { + cpu-supply = <&vdd_cpu_big0_s0>; ++ mem-supply = <&vdd_cpu_big0_s0>; + }; + + &cpu_b1 { + cpu-supply = <&vdd_cpu_big0_s0>; ++ mem-supply = <&vdd_cpu_big0_s0>; + }; + + &cpu_b2 { + cpu-supply = <&vdd_cpu_big1_s0>; ++ mem-supply = <&vdd_cpu_big1_s0>; + }; + + &cpu_b3 { + cpu-supply = <&vdd_cpu_big1_s0>; ++ mem-supply = <&vdd_cpu_big1_s0>; + }; + + &cpu_l0 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &cpu_l1 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &cpu_l2 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &cpu_l3 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &i2c0 { +-- +2.42.1 + + +From c8bd3a53671c48ccf642bbc6453fdb0274022bad Mon Sep 17 00:00:00 2001 +From: Sebastian Reichel +Date: Mon, 24 Jul 2023 15:07:49 +0200 +Subject: [PATCH 5/5] arm64: dts: rockchip: rock5b: add cpu mem regulator info + +Add the second supply regulator for the CPU cores, which is used +for supplying the memory interface. + +Signed-off-by: Sebastian Reichel +--- + arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts +index 419d0202f7fc..9ee415e6f498 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts +@@ -155,34 +155,42 @@ &combphy2_psu { + + &cpu_b0 { + cpu-supply = <&vdd_cpu_big0_s0>; ++ mem-supply = <&vdd_cpu_big0_s0>; + }; + + &cpu_b1 { + cpu-supply = <&vdd_cpu_big0_s0>; ++ mem-supply = <&vdd_cpu_big0_s0>; + }; + + &cpu_b2 { + cpu-supply = <&vdd_cpu_big1_s0>; ++ mem-supply = <&vdd_cpu_big1_s0>; + }; + + &cpu_b3 { + cpu-supply = <&vdd_cpu_big1_s0>; ++ mem-supply = <&vdd_cpu_big1_s0>; + }; + + &cpu_l0 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &cpu_l1 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &cpu_l2 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &cpu_l3 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &i2c0 { +-- +2.42.1 diff --git a/patch/kernel/rockchip-rk3588-edge/0023-Add-Initial-HDMITX-VOP2-For-RK3588.patch b/patch/kernel/rockchip-rk3588-edge/0023-Add-Initial-HDMITX-VOP2-For-RK3588.patch new file mode 100644 index 0000000000..ec5717df31 --- /dev/null +++ b/patch/kernel/rockchip-rk3588-edge/0023-Add-Initial-HDMITX-VOP2-For-RK3588.patch @@ -0,0 +1,13411 @@ +From e4e39588f8b46db104817795a703b1f701da9c36 Mon Sep 17 00:00:00 2001 +From: Sebastian Reichel +Date: Tue, 13 Jun 2023 16:45:05 +0200 +Subject: [PATCH 1/9] clk: rockchip: rk3588: fix pclk_vo0grf and pclk_vo1grf + +Currently pclk_vo1grf is not exposed, but it should be referenced +from the vo1_grf syscon, which needs it enabled. That syscon will +be required for HDMI-RX functionality among other things. + +Apart from that pclk_vo0grf and pclk_vo1grf are both linked gates +and need the VO's hclk enabled in addition to their parent clock. + +No Fixes tag has been added, since the logic requiring these clocks +is not yet upstream anyways. + +Signed-off-by: Sebastian Reichel +(cherry picked from commit a5f467748264d9a337cb40083efde04c748d8c3e) +Signed-off-by: Cristian Ciocaltea +--- + drivers/clk/rockchip/clk-rk3588.c | 11 +++++------ + include/dt-bindings/clock/rockchip,rk3588-cru.h | 3 ++- + 2 files changed, 7 insertions(+), 7 deletions(-) + +diff --git a/drivers/clk/rockchip/clk-rk3588.c b/drivers/clk/rockchip/clk-rk3588.c +index 6994165e0395..c6b605d6d769 100644 +--- a/drivers/clk/rockchip/clk-rk3588.c ++++ b/drivers/clk/rockchip/clk-rk3588.c +@@ -1851,8 +1851,6 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = { + RK3588_CLKGATE_CON(56), 0, GFLAGS), + GATE(PCLK_TRNG0, "pclk_trng0", "pclk_vo0_root", 0, + RK3588_CLKGATE_CON(56), 1, GFLAGS), +- GATE(PCLK_VO0GRF, "pclk_vo0grf", "pclk_vo0_root", CLK_IGNORE_UNUSED, +- RK3588_CLKGATE_CON(55), 10, GFLAGS), + COMPOSITE(CLK_I2S4_8CH_TX_SRC, "clk_i2s4_8ch_tx_src", gpll_aupll_p, 0, + RK3588_CLKSEL_CON(118), 5, 1, MFLAGS, 0, 5, DFLAGS, + RK3588_CLKGATE_CON(56), 11, GFLAGS), +@@ -1998,8 +1996,6 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = { + RK3588_CLKGATE_CON(60), 9, GFLAGS), + GATE(PCLK_TRNG1, "pclk_trng1", "pclk_vo1_root", 0, + RK3588_CLKGATE_CON(60), 10, GFLAGS), +- GATE(0, "pclk_vo1grf", "pclk_vo1_root", CLK_IGNORE_UNUSED, +- RK3588_CLKGATE_CON(59), 12, GFLAGS), + GATE(PCLK_S_EDP0, "pclk_s_edp0", "pclk_vo1_s_root", 0, + RK3588_CLKGATE_CON(59), 14, GFLAGS), + GATE(PCLK_S_EDP1, "pclk_s_edp1", "pclk_vo1_s_root", 0, +@@ -2447,12 +2443,15 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = { + GATE_LINK(HCLK_RKVDEC1_PRE, "hclk_rkvdec1_pre", "hclk_rkvdec1_root", "hclk_vdpu_root", 0, RK3588_CLKGATE_CON(41), 4, GFLAGS), + GATE_LINK(ACLK_RKVDEC1_PRE, "aclk_rkvdec1_pre", "aclk_rkvdec1_root", "aclk_vdpu_root", 0, RK3588_CLKGATE_CON(41), 5, GFLAGS), + GATE_LINK(ACLK_HDCP0_PRE, "aclk_hdcp0_pre", "aclk_vo0_root", "aclk_vop_low_root", 0, RK3588_CLKGATE_CON(55), 9, GFLAGS), +- GATE_LINK(HCLK_VO0, "hclk_vo0", "hclk_vo0_root", "hclk_vop_root", 0, RK3588_CLKGATE_CON(55), 5, GFLAGS), ++ GATE_LINK(HCLK_VO0, "hclk_vo0", "hclk_vo0_root", "hclk_vop_root", RK3588_LINKED_CLK, RK3588_CLKGATE_CON(55), 5, GFLAGS), + GATE_LINK(ACLK_HDCP1_PRE, "aclk_hdcp1_pre", "aclk_hdcp1_root", "aclk_vo1usb_top_root", 0, RK3588_CLKGATE_CON(59), 6, GFLAGS), +- GATE_LINK(HCLK_VO1, "hclk_vo1", "hclk_vo1_root", "hclk_vo1usb_top_root", 0, RK3588_CLKGATE_CON(59), 9, GFLAGS), ++ GATE_LINK(HCLK_VO1, "hclk_vo1", "hclk_vo1_root", "hclk_vo1usb_top_root", RK3588_LINKED_CLK, RK3588_CLKGATE_CON(59), 9, GFLAGS), + GATE_LINK(ACLK_AV1_PRE, "aclk_av1_pre", "aclk_av1_root", "aclk_vdpu_root", 0, RK3588_CLKGATE_CON(68), 1, GFLAGS), + GATE_LINK(PCLK_AV1_PRE, "pclk_av1_pre", "pclk_av1_root", "hclk_vdpu_root", 0, RK3588_CLKGATE_CON(68), 4, GFLAGS), + GATE_LINK(HCLK_SDIO_PRE, "hclk_sdio_pre", "hclk_sdio_root", "hclk_nvm", 0, RK3588_CLKGATE_CON(75), 1, GFLAGS), ++ GATE_LINK(PCLK_VO0GRF, "pclk_vo0grf", "pclk_vo0_root", "hclk_vo0", CLK_IGNORE_UNUSED, RK3588_CLKGATE_CON(55), 10, GFLAGS), ++ GATE_LINK(PCLK_VO1GRF, "pclk_vo1grf", "pclk_vo1_root", "hclk_vo1", CLK_IGNORE_UNUSED, RK3588_CLKGATE_CON(59), 12, GFLAGS), ++ + }; + + static void __init rk3588_clk_init(struct device_node *np) +diff --git a/include/dt-bindings/clock/rockchip,rk3588-cru.h b/include/dt-bindings/clock/rockchip,rk3588-cru.h +index 5790b1391201..50ba72980190 100644 +--- a/include/dt-bindings/clock/rockchip,rk3588-cru.h ++++ b/include/dt-bindings/clock/rockchip,rk3588-cru.h +@@ -733,8 +733,9 @@ + #define ACLK_AV1_PRE 718 + #define PCLK_AV1_PRE 719 + #define HCLK_SDIO_PRE 720 ++#define PCLK_VO1GRF 721 + +-#define CLK_NR_CLKS (HCLK_SDIO_PRE + 1) ++#define CLK_NR_CLKS (PCLK_VO1GRF + 1) + + /* scmi-clocks indices */ + +-- +2.42.1 + + +From 7ec0cb44173dd1a5357a66afa7f3b5de956df7ee Mon Sep 17 00:00:00 2001 +From: Cristian Ciocaltea +Date: Thu, 14 Sep 2023 19:10:30 +0300 +Subject: [PATCH 2/9] phy: rockchip: Add Samsung HDMI/DP Combo PHY HDMI driver + +Co-developed-by: Algea Cao +Signed-off-by: Algea Cao +Signed-off-by: Cristian Ciocaltea +--- + drivers/phy/rockchip/Kconfig | 7 + + drivers/phy/rockchip/Makefile | 1 + + .../phy-rockchip-samsung-hdptx-hdmi.c | 2347 +++++++++++++++++ + 3 files changed, 2355 insertions(+) + create mode 100644 drivers/phy/rockchip/phy-rockchip-samsung-hdptx-hdmi.c + +diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig +index d21b458c1d18..62c18e25b8e0 100644 +--- a/drivers/phy/rockchip/Kconfig ++++ b/drivers/phy/rockchip/Kconfig +@@ -83,6 +83,13 @@ config PHY_ROCKCHIP_PCIE + help + Enable this to support the Rockchip PCIe PHY. + ++config PHY_ROCKCHIP_SAMSUNG_HDPTX_HDMI ++ tristate "Rockchip Samsung HDMI/DP Combo PHY HDMI driver" ++ depends on OF && (ARCH_ROCKCHIP || COMPILE_TEST) ++ select GENERIC_PHY ++ help ++ Support for Rockchip HDMI/DP Combo PHY with Samsung IP block. ++ + config PHY_ROCKCHIP_SNPS_PCIE3 + tristate "Rockchip Snps PCIe3 PHY Driver" + depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST +diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile +index 25d2e1355db7..d266414a1ffb 100644 +--- a/drivers/phy/rockchip/Makefile ++++ b/drivers/phy/rockchip/Makefile +@@ -8,6 +8,7 @@ obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o + obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o + obj-$(CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY) += phy-rockchip-naneng-combphy.o + obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o ++obj-$(CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX_HDMI) += phy-rockchip-samsung-hdptx-hdmi.o + obj-$(CONFIG_PHY_ROCKCHIP_SNPS_PCIE3) += phy-rockchip-snps-pcie3.o + obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o + obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o +diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx-hdmi.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx-hdmi.c +new file mode 100644 +index 000000000000..036db08777cf +--- /dev/null ++++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx-hdmi.c +@@ -0,0 +1,2347 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright (C) Rockchip Electronics Co.Ltd ++ * Author: ++ * Algea Cao ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define UPDATE(x, h, l) (((x) << (l)) & GENMASK((h), (l))) ++ ++#define GRF_HDPTX_CON0 0x00 ++#define HDPTX_I_PLL_EN BIT(7) ++#define HDPTX_I_BIAS_EN BIT(6) ++#define HDPTX_I_BGR_EN BIT(5) ++#define GRF_HDPTX_STATUS 0x80 ++#define HDPTX_O_PLL_LOCK_DONE BIT(3) ++#define HDPTX_O_PHY_CLK_RDY BIT(2) ++#define HDPTX_O_PHY_RDY BIT(1) ++#define HDPTX_O_SB_RDY BIT(0) ++ ++#define CMN_REG0000 0x0000 ++#define CMN_REG0001 0x0004 ++#define CMN_REG0002 0x0008 ++#define CMN_REG0003 0x000C ++#define CMN_REG0004 0x0010 ++#define CMN_REG0005 0x0014 ++#define CMN_REG0006 0x0018 ++#define CMN_REG0007 0x001C ++#define CMN_REG0008 0x0020 ++#define LCPLL_EN_MASK BIT(6) ++#define LCPLL_EN(x) UPDATE(x, 4, 4) ++#define LCPLL_LCVCO_MODE_EN_MASK BIT(4) ++#define LCPLL_LCVCO_MODE_EN(x) UPDATE(x, 4, 4) ++#define CMN_REG0009 0x0024 ++#define CMN_REG000A 0x0028 ++#define CMN_REG000B 0x002C ++#define CMN_REG000C 0x0030 ++#define CMN_REG000D 0x0034 ++#define CMN_REG000E 0x0038 ++#define CMN_REG000F 0x003C ++#define CMN_REG0010 0x0040 ++#define CMN_REG0011 0x0044 ++#define CMN_REG0012 0x0048 ++#define CMN_REG0013 0x004C ++#define CMN_REG0014 0x0050 ++#define CMN_REG0015 0x0054 ++#define CMN_REG0016 0x0058 ++#define CMN_REG0017 0x005C ++#define CMN_REG0018 0x0060 ++#define CMN_REG0019 0x0064 ++#define CMN_REG001A 0x0068 ++#define CMN_REG001B 0x006C ++#define CMN_REG001C 0x0070 ++#define CMN_REG001D 0x0074 ++#define CMN_REG001E 0x0078 ++#define LCPLL_PI_EN_MASK BIT(5) ++#define LCPLL_PI_EN(x) UPDATE(x, 5, 5) ++#define LCPLL_100M_CLK_EN_MASK BIT(0) ++#define LCPLL_100M_CLK_EN(x) UPDATE(x, 0, 0) ++#define CMN_REG001F 0x007C ++#define CMN_REG0020 0x0080 ++#define CMN_REG0021 0x0084 ++#define CMN_REG0022 0x0088 ++#define CMN_REG0023 0x008C ++#define CMN_REG0024 0x0090 ++#define CMN_REG0025 0x0094 ++#define LCPLL_PMS_IQDIV_RSTN BIT(4) ++#define CMN_REG0026 0x0098 ++#define CMN_REG0027 0x009C ++#define CMN_REG0028 0x00A0 ++#define LCPLL_SDC_FRAC_EN BIT(2) ++#define LCPLL_SDC_FRAC_RSTN BIT(0) ++#define CMN_REG0029 0x00A4 ++#define CMN_REG002A 0x00A8 ++#define CMN_REG002B 0x00AC ++#define CMN_REG002C 0x00B0 ++#define CMN_REG002D 0x00B4 ++#define LCPLL_SDC_N_MASK GENMASK(3, 1) ++#define LCPLL_SDC_N(x) UPDATE(x, 3, 1) ++#define CMN_REG002E 0x00B8 ++#define LCPLL_SDC_NUMBERATOR_MASK GENMASK(5, 0) ++#define LCPLL_SDC_NUMBERATOR(x) UPDATE(x, 5, 0) ++#define CMN_REG002F 0x00BC ++#define LCPLL_SDC_DENOMINATOR_MASK GENMASK(7, 2) ++#define LCPLL_SDC_DENOMINATOR(x) UPDATE(x, 7, 2) ++#define LCPLL_SDC_NDIV_RSTN BIT(0) ++#define CMN_REG0030 0x00C0 ++#define CMN_REG0031 0x00C4 ++#define CMN_REG0032 0x00C8 ++#define CMN_REG0033 0x00CC ++#define CMN_REG0034 0x00D0 ++#define CMN_REG0035 0x00D4 ++#define CMN_REG0036 0x00D8 ++#define CMN_REG0037 0x00DC ++#define CMN_REG0038 0x00E0 ++#define CMN_REG0039 0x00E4 ++#define CMN_REG003A 0x00E8 ++#define CMN_REG003B 0x00EC ++#define CMN_REG003C 0x00F0 ++#define CMN_REG003D 0x00F4 ++#define ROPLL_LCVCO_EN BIT(4) ++#define CMN_REG003E 0x00F8 ++#define CMN_REG003F 0x00FC ++#define CMN_REG0040 0x0100 ++#define CMN_REG0041 0x0104 ++#define CMN_REG0042 0x0108 ++#define CMN_REG0043 0x010C ++#define CMN_REG0044 0x0110 ++#define CMN_REG0045 0x0114 ++#define CMN_REG0046 0x0118 ++#define CMN_REG0047 0x011C ++#define CMN_REG0048 0x0120 ++#define CMN_REG0049 0x0124 ++#define CMN_REG004A 0x0128 ++#define CMN_REG004B 0x012C ++#define CMN_REG004C 0x0130 ++#define CMN_REG004D 0x0134 ++#define CMN_REG004E 0x0138 ++#define ROPLL_PI_EN BIT(5) ++#define CMN_REG004F 0x013C ++#define CMN_REG0050 0x0140 ++#define CMN_REG0051 0x0144 ++#define CMN_REG0052 0x0148 ++#define CMN_REG0053 0x014C ++#define CMN_REG0054 0x0150 ++#define CMN_REG0055 0x0154 ++#define CMN_REG0056 0x0158 ++#define CMN_REG0057 0x015C ++#define CMN_REG0058 0x0160 ++#define CMN_REG0059 0x0164 ++#define CMN_REG005A 0x0168 ++#define CMN_REG005B 0x016C ++#define CMN_REG005C 0x0170 ++#define ROPLL_PMS_IQDIV_RSTN BIT(5) ++#define CMN_REG005D 0x0174 ++#define CMN_REG005E 0x0178 ++#define ROPLL_SDM_EN_MASK BIT(6) ++#define ROPLL_SDM_EN(x) UPDATE(x, 6, 6) ++#define ROPLL_SDM_FRAC_EN_RBR BIT(3) ++#define ROPLL_SDM_FRAC_EN_HBR BIT(2) ++#define ROPLL_SDM_FRAC_EN_HBR2 BIT(1) ++#define ROPLL_SDM_FRAC_EN_HBR3 BIT(0) ++#define CMN_REG005F 0x017C ++#define CMN_REG0060 0x0180 ++#define CMN_REG0061 0x0184 ++#define CMN_REG0062 0x0188 ++#define CMN_REG0063 0x018C ++#define CMN_REG0064 0x0190 ++#define ROPLL_SDM_NUM_SIGN_RBR_MASK BIT(3) ++#define ROPLL_SDM_NUM_SIGN_RBR(x) UPDATE(x, 3, 3) ++#define CMN_REG0065 0x0194 ++#define CMN_REG0066 0x0198 ++#define CMN_REG0067 0x019C ++#define CMN_REG0068 0x01A0 ++#define CMN_REG0069 0x01A4 ++#define ROPLL_SDC_N_RBR_MASK GENMASK(2, 0) ++#define ROPLL_SDC_N_RBR(x) UPDATE(x, 2, 0) ++#define CMN_REG006A 0x01A8 ++#define CMN_REG006B 0x01AC ++#define CMN_REG006C 0x01B0 ++#define CMN_REG006D 0x01B4 ++#define CMN_REG006E 0x01B8 ++#define CMN_REG006F 0x01BC ++#define CMN_REG0070 0x01C0 ++#define CMN_REG0071 0x01C4 ++#define CMN_REG0072 0x01C8 ++#define CMN_REG0073 0x01CC ++#define CMN_REG0074 0x01D0 ++#define ROPLL_SDC_NDIV_RSTN BIT(2) ++#define ROPLL_SSC_EN BIT(0) ++#define CMN_REG0075 0x01D4 ++#define CMN_REG0076 0x01D8 ++#define CMN_REG0077 0x01DC ++#define CMN_REG0078 0x01E0 ++#define CMN_REG0079 0x01E4 ++#define CMN_REG007A 0x01E8 ++#define CMN_REG007B 0x01EC ++#define CMN_REG007C 0x01F0 ++#define CMN_REG007D 0x01F4 ++#define CMN_REG007E 0x01F8 ++#define CMN_REG007F 0x01FC ++#define CMN_REG0080 0x0200 ++#define CMN_REG0081 0x0204 ++#define OVRD_PLL_CD_CLK_EN BIT(8) ++#define PLL_CD_HSCLK_EAST_EN BIT(0) ++#define CMN_REG0082 0x0208 ++#define CMN_REG0083 0x020C ++#define CMN_REG0084 0x0210 ++#define CMN_REG0085 0x0214 ++#define CMN_REG0086 0x0218 ++#define PLL_PCG_POSTDIV_SEL_MASK GENMASK(7, 4) ++#define PLL_PCG_POSTDIV_SEL(x) UPDATE(x, 7, 4) ++#define PLL_PCG_CLK_SEL_MASK GENMASK(3, 1) ++#define PLL_PCG_CLK_SEL(x) UPDATE(x, 3, 1) ++#define PLL_PCG_CLK_EN BIT(0) ++#define CMN_REG0087 0x021C ++#define PLL_FRL_MODE_EN BIT(3) ++#define PLL_TX_HS_CLK_EN BIT(2) ++#define CMN_REG0088 0x0220 ++#define CMN_REG0089 0x0224 ++#define LCPLL_ALONE_MODE BIT(1) ++#define CMN_REG008A 0x0228 ++#define CMN_REG008B 0x022C ++#define CMN_REG008C 0x0230 ++#define CMN_REG008D 0x0234 ++#define CMN_REG008E 0x0238 ++#define CMN_REG008F 0x023C ++#define CMN_REG0090 0x0240 ++#define CMN_REG0091 0x0244 ++#define CMN_REG0092 0x0248 ++#define CMN_REG0093 0x024C ++#define CMN_REG0094 0x0250 ++#define CMN_REG0095 0x0254 ++#define CMN_REG0096 0x0258 ++#define CMN_REG0097 0x025C ++#define DIG_CLK_SEL BIT(1) ++#define ROPLL_REF BIT(1) ++#define LCPLL_REF 0 ++#define CMN_REG0098 0x0260 ++#define CMN_REG0099 0x0264 ++#define CMN_ROPLL_ALONE_MODE BIT(2) ++#define ROPLL_ALONE_MODE BIT(2) ++#define CMN_REG009A 0x0268 ++#define HS_SPEED_SEL BIT(0) ++#define DIV_10_CLOCK BIT(0) ++#define CMN_REG009B 0x026C ++#define IS_SPEED_SEL BIT(4) ++#define LINK_SYMBOL_CLOCK BIT(4) ++#define LINK_SYMBOL_CLOCK1_2 0 ++#define CMN_REG009C 0x0270 ++#define CMN_REG009D 0x0274 ++#define CMN_REG009E 0x0278 ++#define CMN_REG009F 0x027C ++#define CMN_REG00A0 0x0280 ++#define CMN_REG00A1 0x0284 ++#define CMN_REG00A2 0x0288 ++#define CMN_REG00A3 0x028C ++#define CMN_REG00AD 0x0290 ++#define CMN_REG00A5 0x0294 ++#define CMN_REG00A6 0x0298 ++#define CMN_REG00A7 0x029C ++#define SB_REG0100 0x0400 ++#define SB_REG0101 0x0404 ++#define SB_REG0102 0x0408 ++#define OVRD_SB_RXTERM_EN_MASK BIT(5) ++#define OVRD_SB_RXTERM_EN(x) UPDATE(x, 5, 5) ++#define SB_RXTERM_EN_MASK BIT(4) ++#define SB_RXTERM_EN(x) UPDATE(x, 4, 4) ++#define ANA_SB_RXTERM_OFFSP_MASK GENMASK(3, 0) ++#define ANA_SB_RXTERM_OFFSP(x) UPDATE(x, 3, 0) ++#define SB_REG0103 0x040C ++#define ANA_SB_RXTERM_OFFSN_MASK GENMASK(6, 3) ++#define ANA_SB_RXTERM_OFFSN(x) UPDATE(x, 6, 3) ++#define OVRD_SB_RX_RESCAL_DONE_MASK BIT(1) ++#define OVRD_SB_RX_RESCAL_DONE(x) UPDATE(x, 1, 1) ++#define SB_RX_RESCAL_DONE_MASK BIT(0) ++#define SB_RX_RESCAL_DONE(x) UPDATE(x, 0, 0) ++#define SB_REG0104 0x0410 ++#define OVRD_SB_EN_MASK BIT(5) ++#define OVRD_SB_EN(x) UPDATE(x, 5, 5) ++#define SB_EN_MASK BIT(4) ++#define SB_EN(x) UPDATE(x, 4, 4) ++#define SB_REG0105 0x0414 ++#define OVRD_SB_EARC_CMDC_EN_MASK BIT(6) ++#define OVRD_SB_EARC_CMDC_EN(x) UPDATE(x, 6, 6) ++#define SB_EARC_CMDC_EN_MASK BIT(5) ++#define SB_EARC_CMDC_EN(x) UPDATE(x, 5, 5) ++#define ANA_SB_TX_HLVL_PROG_MASK GENMASK(2, 0) ++#define ANA_SB_TX_HLVL_PROG(x) UPDATE(x, 2, 0) ++#define SB_REG0106 0x0418 ++#define ANA_SB_TX_LLVL_PROG_MASK GENMASK(6, 4) ++#define ANA_SB_TX_LLVL_PROG(x) UPDATE(x, 6, 4) ++#define SB_REG0107 0x041C ++#define SB_REG0108 0x0420 ++#define SB_REG0109 0x0424 ++#define ANA_SB_DMRX_AFC_DIV_RATIO_MASK GENMASK(2, 0) ++#define ANA_SB_DMRX_AFC_DIV_RATIO(x) UPDATE(x, 2, 0) ++#define SB_REG010A 0x0428 ++#define SB_REG010B 0x042C ++#define SB_REG010C 0x0430 ++#define SB_REG010D 0x0434 ++#define SB_REG010E 0x0438 ++#define SB_REG010F 0x043C ++#define OVRD_SB_VREG_EN_MASK BIT(7) ++#define OVRD_SB_VREG_EN(x) UPDATE(x, 7, 7) ++#define SB_VREG_EN_MASK BIT(6) ++#define SB_VREG_EN(x) UPDATE(x, 6, 6) ++#define OVRD_SB_VREG_LPF_BYPASS_MASK BIT(5) ++#define OVRD_SB_VREG_LPF_BYPASS(x) UPDATE(x, 5, 5) ++#define SB_VREG_LPF_BYPASS_MASK BIT(4) ++#define SB_VREG_LPF_BYPASS(x) UPDATE(x, 4, 4) ++#define ANA_SB_VREG_GAIN_CTRL_MASK GENMASK(3, 0) ++#define ANA_SB_VREG_GAIN_CTRL(x) UPDATE(x, 3, 0) ++#define SB_REG0110 0x0440 ++#define ANA_SB_VREG_REF_SEL_MASK BIT(0) ++#define ANA_SB_VREG_REF_SEL(x) UPDATE(x, 0, 0) ++#define SB_REG0111 0x0444 ++#define SB_REG0112 0x0448 ++#define SB_REG0113 0x044C ++#define SB_RX_RCAL_OPT_CODE_MASK GENMASK(5, 4) ++#define SB_RX_RCAL_OPT_CODE(x) UPDATE(x, 5, 4) ++#define SB_RX_RTERM_CTRL_MASK GENMASK(3, 0) ++#define SB_RX_RTERM_CTRL(x) UPDATE(x, 3, 0) ++#define SB_REG0114 0x0450 ++#define SB_TG_SB_EN_DELAY_TIME_MASK GENMASK(5, 3) ++#define SB_TG_SB_EN_DELAY_TIME(x) UPDATE(x, 5, 3) ++#define SB_TG_RXTERM_EN_DELAY_TIME_MASK GENMASK(2, 0) ++#define SB_TG_RXTERM_EN_DELAY_TIME(x) UPDATE(x, 2, 0) ++#define SB_REG0115 0x0454 ++#define SB_READY_DELAY_TIME_MASK GENMASK(5, 3) ++#define SB_READY_DELAY_TIME(x) UPDATE(x, 5, 3) ++#define SB_TG_OSC_EN_DELAY_TIME_MASK GENMASK(2, 0) ++#define SB_TG_OSC_EN_DELAY_TIME(x) UPDATE(x, 2, 0) ++#define SB_REG0116 0x0458 ++#define AFC_RSTN_DELAY_TIME_MASK GENMASK(6, 4) ++#define AFC_RSTN_DELAY_TIME(x) UPDATE(x, 6, 4) ++#define SB_REG0117 0x045C ++#define FAST_PULSE_TIME_MASK GENMASK(3, 0) ++#define FAST_PULSE_TIME(x) UPDATE(x, 3, 0) ++#define SB_REG0118 0x0460 ++#define SB_REG0119 0x0464 ++#define SB_REG011A 0x0468 ++#define SB_REG011B 0x046C ++#define SB_EARC_SIG_DET_BYPASS_MASK BIT(4) ++#define SB_EARC_SIG_DET_BYPASS(x) UPDATE(x, 4, 4) ++#define SB_AFC_TOL_MASK GENMASK(3, 0) ++#define SB_AFC_TOL(x) UPDATE(x, 3, 0) ++#define SB_REG011C 0x0470 ++#define SB_REG011D 0x0474 ++#define SB_REG011E 0x0478 ++#define SB_REG011F 0x047C ++#define SB_PWM_AFC_CTRL_MASK GENMASK(7, 2) ++#define SB_PWM_AFC_CTRL(x) UPDATE(x, 7, 2) ++#define SB_RCAL_RSTN_MASK BIT(1) ++#define SB_RCAL_RSTN(x) UPDATE(x, 1, 1) ++#define SB_REG0120 0x0480 ++#define SB_EARC_EN_MASK BIT(1) ++#define SB_EARC_EN(x) UPDATE(x, 1, 1) ++#define SB_EARC_AFC_EN_MASK BIT(2) ++#define SB_EARC_AFC_EN(x) UPDATE(x, 2, 2) ++#define SB_REG0121 0x0484 ++#define SB_REG0122 0x0488 ++#define SB_REG0123 0x048C ++#define OVRD_SB_READY_MASK BIT(5) ++#define OVRD_SB_READY(x) UPDATE(x, 5, 5) ++#define SB_READY_MASK BIT(4) ++#define SB_READY(x) UPDATE(x, 4, 4) ++#define SB_REG0124 0x0490 ++#define SB_REG0125 0x0494 ++#define SB_REG0126 0x0498 ++#define SB_REG0127 0x049C ++#define SB_REG0128 0x04A0 ++#define SB_REG0129 0x04AD ++#define LNTOP_REG0200 0x0800 ++#define PROTOCOL_SEL BIT(2) ++#define HDMI_MODE BIT(2) ++#define HDMI_TMDS_FRL_SEL BIT(1) ++#define LNTOP_REG0201 0x0804 ++#define LNTOP_REG0202 0x0808 ++#define LNTOP_REG0203 0x080C ++#define LNTOP_REG0204 0x0810 ++#define LNTOP_REG0205 0x0814 ++#define LNTOP_REG0206 0x0818 ++#define DATA_BUS_WIDTH (0x3 << 1) ++#define WIDTH_40BIT (0x3 << 1) ++#define WIDTH_36BIT (0x2 << 1) ++#define DATA_BUS_SEL BIT(0) ++#define DATA_BUS_36_40 BIT(0) ++#define LNTOP_REG0207 0x081C ++#define LANE_EN 0xf ++#define ALL_LANE_EN 0xf ++#define LNTOP_REG0208 0x0820 ++#define LNTOP_REG0209 0x0824 ++#define LNTOP_REG020A 0x0828 ++#define LNTOP_REG020B 0x082C ++#define LNTOP_REG020C 0x0830 ++#define LNTOP_REG020D 0x0834 ++#define LNTOP_REG020E 0x0838 ++#define LNTOP_REG020F 0x083C ++#define LNTOP_REG0210 0x0840 ++#define LNTOP_REG0211 0x0844 ++#define LNTOP_REG0212 0x0848 ++#define LNTOP_REG0213 0x084C ++#define LNTOP_REG0214 0x0850 ++#define LNTOP_REG0215 0x0854 ++#define LNTOP_REG0216 0x0858 ++#define LNTOP_REG0217 0x085C ++#define LNTOP_REG0218 0x0860 ++#define LNTOP_REG0219 0x0864 ++#define LNTOP_REG021A 0x0868 ++#define LNTOP_REG021B 0x086C ++#define LNTOP_REG021C 0x0870 ++#define LNTOP_REG021D 0x0874 ++#define LNTOP_REG021E 0x0878 ++#define LNTOP_REG021F 0x087C ++#define LNTOP_REG0220 0x0880 ++#define LNTOP_REG0221 0x0884 ++#define LNTOP_REG0222 0x0888 ++#define LNTOP_REG0223 0x088C ++#define LNTOP_REG0224 0x0890 ++#define LNTOP_REG0225 0x0894 ++#define LNTOP_REG0226 0x0898 ++#define LNTOP_REG0227 0x089C ++#define LNTOP_REG0228 0x08A0 ++#define LNTOP_REG0229 0x08A4 ++#define LANE_REG0300 0x0C00 ++#define LANE_REG0301 0x0C04 ++#define LANE_REG0302 0x0C08 ++#define LANE_REG0303 0x0C0C ++#define LANE_REG0304 0x0C10 ++#define LANE_REG0305 0x0C14 ++#define LANE_REG0306 0x0C18 ++#define LANE_REG0307 0x0C1C ++#define LANE_REG0308 0x0C20 ++#define LANE_REG0309 0x0C24 ++#define LANE_REG030A 0x0C28 ++#define LANE_REG030B 0x0C2C ++#define LANE_REG030C 0x0C30 ++#define LANE_REG030D 0x0C34 ++#define LANE_REG030E 0x0C38 ++#define LANE_REG030F 0x0C3C ++#define LANE_REG0310 0x0C40 ++#define LANE_REG0311 0x0C44 ++#define LANE_REG0312 0x0C48 ++#define LN0_TX_SER_RATE_SEL_RBR BIT(5) ++#define LN0_TX_SER_RATE_SEL_HBR BIT(4) ++#define LN0_TX_SER_RATE_SEL_HBR2 BIT(3) ++#define LN0_TX_SER_RATE_SEL_HBR3 BIT(2) ++#define LANE_REG0313 0x0C4C ++#define LANE_REG0314 0x0C50 ++#define LANE_REG0315 0x0C54 ++#define LANE_REG0316 0x0C58 ++#define LANE_REG0317 0x0C5C ++#define LANE_REG0318 0x0C60 ++#define LANE_REG0319 0x0C64 ++#define LANE_REG031A 0x0C68 ++#define LANE_REG031B 0x0C6C ++#define LANE_REG031C 0x0C70 ++#define LANE_REG031D 0x0C74 ++#define LANE_REG031E 0x0C78 ++#define LANE_REG031F 0x0C7C ++#define LANE_REG0320 0x0C80 ++#define LANE_REG0321 0x0C84 ++#define LANE_REG0322 0x0C88 ++#define LANE_REG0323 0x0C8C ++#define LANE_REG0324 0x0C90 ++#define LANE_REG0325 0x0C94 ++#define LANE_REG0326 0x0C98 ++#define LANE_REG0327 0x0C9C ++#define LANE_REG0328 0x0CA0 ++#define LANE_REG0329 0x0CA4 ++#define LANE_REG032A 0x0CA8 ++#define LANE_REG032B 0x0CAC ++#define LANE_REG032C 0x0CB0 ++#define LANE_REG032D 0x0CB4 ++#define LANE_REG0400 0x1000 ++#define LANE_REG0401 0x1004 ++#define LANE_REG0402 0x1008 ++#define LANE_REG0403 0x100C ++#define LANE_REG0404 0x1010 ++#define LANE_REG0405 0x1014 ++#define LANE_REG0406 0x1018 ++#define LANE_REG0407 0x101C ++#define LANE_REG0408 0x1020 ++#define LANE_REG0409 0x1024 ++#define LANE_REG040A 0x1028 ++#define LANE_REG040B 0x102C ++#define LANE_REG040C 0x1030 ++#define LANE_REG040D 0x1034 ++#define LANE_REG040E 0x1038 ++#define LANE_REG040F 0x103C ++#define LANE_REG0410 0x1040 ++#define LANE_REG0411 0x1044 ++#define LANE_REG0412 0x1048 ++#define LN1_TX_SER_RATE_SEL_RBR BIT(5) ++#define LN1_TX_SER_RATE_SEL_HBR BIT(4) ++#define LN1_TX_SER_RATE_SEL_HBR2 BIT(3) ++#define LN1_TX_SER_RATE_SEL_HBR3 BIT(2) ++#define LANE_REG0413 0x104C ++#define LANE_REG0414 0x1050 ++#define LANE_REG0415 0x1054 ++#define LANE_REG0416 0x1058 ++#define LANE_REG0417 0x105C ++#define LANE_REG0418 0x1060 ++#define LANE_REG0419 0x1064 ++#define LANE_REG041A 0x1068 ++#define LANE_REG041B 0x106C ++#define LANE_REG041C 0x1070 ++#define LANE_REG041D 0x1074 ++#define LANE_REG041E 0x1078 ++#define LANE_REG041F 0x107C ++#define LANE_REG0420 0x1080 ++#define LANE_REG0421 0x1084 ++#define LANE_REG0422 0x1088 ++#define LANE_REG0423 0x108C ++#define LANE_REG0424 0x1090 ++#define LANE_REG0425 0x1094 ++#define LANE_REG0426 0x1098 ++#define LANE_REG0427 0x109C ++#define LANE_REG0428 0x10A0 ++#define LANE_REG0429 0x10A4 ++#define LANE_REG042A 0x10A8 ++#define LANE_REG042B 0x10AC ++#define LANE_REG042C 0x10B0 ++#define LANE_REG042D 0x10B4 ++#define LANE_REG0500 0x1400 ++#define LANE_REG0501 0x1404 ++#define LANE_REG0502 0x1408 ++#define LANE_REG0503 0x140C ++#define LANE_REG0504 0x1410 ++#define LANE_REG0505 0x1414 ++#define LANE_REG0506 0x1418 ++#define LANE_REG0507 0x141C ++#define LANE_REG0508 0x1420 ++#define LANE_REG0509 0x1424 ++#define LANE_REG050A 0x1428 ++#define LANE_REG050B 0x142C ++#define LANE_REG050C 0x1430 ++#define LANE_REG050D 0x1434 ++#define LANE_REG050E 0x1438 ++#define LANE_REG050F 0x143C ++#define LANE_REG0510 0x1440 ++#define LANE_REG0511 0x1444 ++#define LANE_REG0512 0x1448 ++#define LN2_TX_SER_RATE_SEL_RBR BIT(5) ++#define LN2_TX_SER_RATE_SEL_HBR BIT(4) ++#define LN2_TX_SER_RATE_SEL_HBR2 BIT(3) ++#define LN2_TX_SER_RATE_SEL_HBR3 BIT(2) ++#define LANE_REG0513 0x144C ++#define LANE_REG0514 0x1450 ++#define LANE_REG0515 0x1454 ++#define LANE_REG0516 0x1458 ++#define LANE_REG0517 0x145C ++#define LANE_REG0518 0x1460 ++#define LANE_REG0519 0x1464 ++#define LANE_REG051A 0x1468 ++#define LANE_REG051B 0x146C ++#define LANE_REG051C 0x1470 ++#define LANE_REG051D 0x1474 ++#define LANE_REG051E 0x1478 ++#define LANE_REG051F 0x147C ++#define LANE_REG0520 0x1480 ++#define LANE_REG0521 0x1484 ++#define LANE_REG0522 0x1488 ++#define LANE_REG0523 0x148C ++#define LANE_REG0524 0x1490 ++#define LANE_REG0525 0x1494 ++#define LANE_REG0526 0x1498 ++#define LANE_REG0527 0x149C ++#define LANE_REG0528 0x14A0 ++#define LANE_REG0529 0x14AD ++#define LANE_REG052A 0x14A8 ++#define LANE_REG052B 0x14AC ++#define LANE_REG052C 0x14B0 ++#define LANE_REG052D 0x14B4 ++#define LANE_REG0600 0x1800 ++#define LANE_REG0601 0x1804 ++#define LANE_REG0602 0x1808 ++#define LANE_REG0603 0x180C ++#define LANE_REG0604 0x1810 ++#define LANE_REG0605 0x1814 ++#define LANE_REG0606 0x1818 ++#define LANE_REG0607 0x181C ++#define LANE_REG0608 0x1820 ++#define LANE_REG0609 0x1824 ++#define LANE_REG060A 0x1828 ++#define LANE_REG060B 0x182C ++#define LANE_REG060C 0x1830 ++#define LANE_REG060D 0x1834 ++#define LANE_REG060E 0x1838 ++#define LANE_REG060F 0x183C ++#define LANE_REG0610 0x1840 ++#define LANE_REG0611 0x1844 ++#define LANE_REG0612 0x1848 ++#define LN3_TX_SER_RATE_SEL_RBR BIT(5) ++#define LN3_TX_SER_RATE_SEL_HBR BIT(4) ++#define LN3_TX_SER_RATE_SEL_HBR2 BIT(3) ++#define LN3_TX_SER_RATE_SEL_HBR3 BIT(2) ++#define LANE_REG0613 0x184C ++#define LANE_REG0614 0x1850 ++#define LANE_REG0615 0x1854 ++#define LANE_REG0616 0x1858 ++#define LANE_REG0617 0x185C ++#define LANE_REG0618 0x1860 ++#define LANE_REG0619 0x1864 ++#define LANE_REG061A 0x1868 ++#define LANE_REG061B 0x186C ++#define LANE_REG061C 0x1870 ++#define LANE_REG061D 0x1874 ++#define LANE_REG061E 0x1878 ++#define LANE_REG061F 0x187C ++#define LANE_REG0620 0x1880 ++#define LANE_REG0621 0x1884 ++#define LANE_REG0622 0x1888 ++#define LANE_REG0623 0x188C ++#define LANE_REG0624 0x1890 ++#define LANE_REG0625 0x1894 ++#define LANE_REG0626 0x1898 ++#define LANE_REG0627 0x189C ++#define LANE_REG0628 0x18A0 ++#define LANE_REG0629 0x18A4 ++#define LANE_REG062A 0x18A8 ++#define LANE_REG062B 0x18AC ++#define LANE_REG062C 0x18B0 ++#define LANE_REG062D 0x18B4 ++ ++#define HDMI20_MAX_RATE 600000000 ++#define DATA_RATE_MASK 0xFFFFFFF ++#define COLOR_DEPTH_MASK BIT(31) ++#define HDMI_MODE_MASK BIT(30) ++#define HDMI_EARC_MASK BIT(29) ++ ++enum hdptx_combphy_type { ++ SS_HDMI, ++ SS_DP ++}; ++ ++ ++struct lcpll_config { ++ u32 bit_rate; ++ u8 lcvco_mode_en; ++ u8 pi_en; ++ u8 clk_en_100m; ++ u8 pms_mdiv; ++ u8 pms_mdiv_afc; ++ u8 pms_pdiv; ++ u8 pms_refdiv; ++ u8 pms_sdiv; ++ u8 pi_cdiv_rstn; ++ u8 pi_cdiv_sel; ++ u8 sdm_en; ++ u8 sdm_rstn; ++ u8 sdc_frac_en; ++ u8 sdc_rstn; ++ u8 sdm_deno; ++ u8 sdm_num_sign; ++ u8 sdm_num; ++ u8 sdc_n; ++ u8 sdc_n2; ++ u8 sdc_num; ++ u8 sdc_deno; ++ u8 sdc_ndiv_rstn; ++ u8 ssc_en; ++ u8 ssc_fm_dev; ++ u8 ssc_fm_freq; ++ u8 ssc_clk_div_sel; ++ u8 cd_tx_ser_rate_sel; ++}; ++ ++struct ropll_config { ++ u32 bit_rate; ++ u8 pms_mdiv; ++ u8 pms_mdiv_afc; ++ u8 pms_pdiv; ++ u8 pms_refdiv; ++ u8 pms_sdiv; ++ u8 pms_iqdiv_rstn; ++ u8 ref_clk_sel; ++ u8 sdm_en; ++ u8 sdm_rstn; ++ u8 sdc_frac_en; ++ u8 sdc_rstn; ++ u8 sdm_clk_div; ++ u8 sdm_deno; ++ u8 sdm_num_sign; ++ u8 sdm_num; ++ u8 sdc_n; ++ u8 sdc_num; ++ u8 sdc_deno; ++ u8 sdc_ndiv_rstn; ++ u8 ssc_en; ++ u8 ssc_fm_dev; ++ u8 ssc_fm_freq; ++ u8 ssc_clk_div_sel; ++ u8 ana_cpp_ctrl; ++ u8 ana_lpf_c_sel; ++ u8 cd_tx_ser_rate_sel; ++}; ++ ++struct rockchip_hdptx_phy { ++ struct device *dev; ++ struct regmap *regmap; ++ struct regmap *grf; ++ ++ int irq; ++ int id; ++ ++ struct phy *phy; ++ struct clk_bulk_data *clks; ++ int nr_clks; ++ struct phy_config *phy_cfg; ++ ++ /* clk provider */ ++ struct clk_hw hw; ++ struct clk *dclk; ++ unsigned long rate; ++ ++ struct reset_control *phy_reset; ++ struct reset_control *apb_reset; ++ struct reset_control *cmn_reset; ++ struct reset_control *init_reset; ++ struct reset_control *lane_reset; ++ struct reset_control *ropll_reset; ++ struct reset_control *lcpll_reset; ++ ++ bool earc_en; ++ int count; ++}; ++ ++struct lcpll_config lcpll_cfg[] = { ++ { 48000000, 1, 0, 0, 0x7d, 0x7d, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 2, ++ 0, 0x13, 0x18, 1, 0, 0x20, 0x0c, 1, 0, ++ }, ++ { 40000000, 1, 1, 0, 0x68, 0x68, 1, 1, 0, 0, 0, 1, 1, 1, 1, 9, 0, 1, 1, ++ 0, 2, 3, 1, 0, 0x20, 0x0c, 1, 0, ++ }, ++ { 32000000, 1, 1, 1, 0x6b, 0x6b, 1, 1, 0, 1, 2, 1, 1, 1, 1, 9, 1, 2, 1, ++ 0, 0x0d, 0x18, 1, 0, 0x20, 0x0c, 1, 1, ++ }, ++ { ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, ++ }, ++}; ++ ++struct ropll_config ropll_frl_cfg[] = { ++ { 24000000, 0x19, 0x19, 1, 1, 0, 1, 2, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, ++ 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 18000000, 0x7d, 0x7d, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, ++ 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 9000000, 0x7d, 0x7d, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, ++ 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, ++ }, ++}; ++ ++struct ropll_config ropll_tmds_cfg[] = { ++ { 5940000, 124, 124, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 3712500, 155, 155, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 2970000, 124, 124, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 1620000, 135, 135, 1, 1, 3, 1, 1, 0, 1, 1, 1, 1, 4, 0, 3, 5, 5, 0x10, ++ 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 1856250, 155, 155, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 1540000, 193, 193, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 193, 1, 32, 2, 1, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 1485000, 0x7b, 0x7b, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 4, 0, 3, 5, 5, 0x10, ++ 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 1462500, 122, 122, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 244, 1, 16, 2, 1, 1, ++ 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 1190000, 149, 149, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 149, 1, 16, 2, 1, 1, ++ 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 1065000, 89, 89, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 89, 1, 16, 1, 0, 1, ++ 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 1080000, 135, 135, 1, 1, 5, 1, 1, 0, 1, 0, 1, 1, 0x9, 0, 0x05, 0, 0x14, ++ 0x18, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 855000, 214, 214, 1, 1, 11, 1, 1, 1, 1, 1, 1, 1, 214, 1, 16, 2, 1, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 835000, 105, 105, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 42, 1, 16, 1, 0, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 928125, 155, 155, 1, 1, 7, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 742500, 124, 124, 1, 1, 7, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 650000, 162, 162, 1, 1, 11, 1, 1, 1, 1, 1, 1, 1, 54, 0, 16, 4, 1, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 337500, 0x70, 0x70, 1, 1, 0xf, 1, 1, 1, 1, 1, 1, 1, 0x2, 0, 0x01, 5, 1, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 400000, 100, 100, 1, 1, 11, 1, 1, 0, 1, 0, 1, 1, 0x9, 0, 0x05, 0, 0x14, ++ 0x18, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 270000, 0x5a, 0x5a, 1, 1, 0xf, 1, 1, 0, 1, 0, 1, 1, 0x9, 0, 0x05, 0, 0x14, ++ 0x18, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { 251750, 84, 84, 1, 1, 0xf, 1, 1, 1, 1, 1, 1, 1, 168, 1, 16, 4, 1, ++ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, ++ }, ++ { ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, ++ }, ++}; ++ ++static bool rockchip_hdptx_phy_is_accissible_reg(struct device *dev, ++ unsigned int reg) ++{ ++ switch (reg) { ++ case 0x0000 ... 0x029c: ++ case 0x0400 ... 0x04a4: ++ case 0x0800 ... 0x08a4: ++ case 0x0c00 ... 0x0cb4: ++ case 0x1000 ... 0x10b4: ++ case 0x1400 ... 0x14b4: ++ case 0x1800 ... 0x18b4: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static const struct regmap_config rockchip_hdptx_phy_regmap_config = { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .fast_io = true, ++ .max_register = 0x18b4, ++ .name = "hdptx-combphy", ++ ++ .readable_reg = rockchip_hdptx_phy_is_accissible_reg, ++ .writeable_reg = rockchip_hdptx_phy_is_accissible_reg, ++}; ++ ++static inline struct rockchip_hdptx_phy *to_rockchip_hdptx_phy(struct clk_hw *hw) ++{ ++ return container_of(hw, struct rockchip_hdptx_phy, hw); ++} ++ ++static inline void hdptx_write(struct rockchip_hdptx_phy *hdptx, u32 reg, u8 val) ++{ ++ regmap_write(hdptx->regmap, reg, val); ++} ++ ++static inline u8 hdptx_read(struct rockchip_hdptx_phy *hdptx, u32 reg) ++{ ++ u32 val; ++ ++ regmap_read(hdptx->regmap, reg, &val); ++ ++ return val; ++} ++ ++static inline void hdptx_update_bits(struct rockchip_hdptx_phy *hdptx, u32 reg, ++ u8 mask, u8 val) ++{ ++ regmap_update_bits(hdptx->regmap, reg, mask, val); ++} ++ ++static inline void hdptx_grf_write(struct rockchip_hdptx_phy *hdptx, u32 reg, u32 val) ++{ ++ regmap_write(hdptx->grf, reg, val); ++} ++ ++static inline u8 hdptx_grf_read(struct rockchip_hdptx_phy *hdptx, u32 reg) ++{ ++ u32 val; ++ ++ regmap_read(hdptx->grf, reg, &val); ++ ++ return val; ++} ++ ++static void hdptx_pre_power_up(struct rockchip_hdptx_phy *hdptx) ++{ ++ u32 val = 0; ++ ++ reset_control_assert(hdptx->apb_reset); ++ udelay(20); ++ reset_control_deassert(hdptx->apb_reset); ++ ++ reset_control_assert(hdptx->lane_reset); ++ reset_control_assert(hdptx->cmn_reset); ++ reset_control_assert(hdptx->init_reset); ++ ++ val = (HDPTX_I_PLL_EN | HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16; ++ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val); ++} ++ ++static int hdptx_post_enable_lane(struct rockchip_hdptx_phy *hdptx) ++{ ++ u32 val = 0; ++ int i; ++ ++ reset_control_deassert(hdptx->lane_reset); ++ ++ val = (HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16 | HDPTX_I_BIAS_EN | ++ HDPTX_I_BGR_EN; ++ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val); ++ ++ for (i = 0; i < 50; i++) { ++ val = hdptx_grf_read(hdptx, GRF_HDPTX_STATUS); ++ ++ if (val & HDPTX_O_PHY_RDY && val & HDPTX_O_PLL_LOCK_DONE) ++ break; ++ udelay(100); ++ } ++ ++ if (i == 50) { ++ dev_err(hdptx->dev, "hdptx phy lane can't ready!\n"); ++ return -EINVAL; ++ } ++ ++ dev_err(hdptx->dev, "hdptx phy lane locked!\n"); ++ ++ return 0; ++} ++ ++static int hdptx_post_enable_pll(struct rockchip_hdptx_phy *hdptx) ++{ ++ u32 val = 0; ++ int i; ++ ++ val = (HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16 | HDPTX_I_BIAS_EN | ++ HDPTX_I_BGR_EN; ++ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val); ++ udelay(10); ++ reset_control_deassert(hdptx->init_reset); ++ udelay(10); ++ val = HDPTX_I_PLL_EN << 16 | HDPTX_I_PLL_EN; ++ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val); ++ udelay(10); ++ reset_control_deassert(hdptx->cmn_reset); ++ ++ for (i = 0; i < 20; i++) { ++ val = hdptx_grf_read(hdptx, GRF_HDPTX_STATUS); ++ ++ if (val & HDPTX_O_PHY_CLK_RDY) ++ break; ++ udelay(20); ++ } ++ ++ if (i == 20) { ++ dev_err(hdptx->dev, "hdptx phy pll can't lock!\n"); ++ return -EINVAL; ++ } ++ ++ dev_err(hdptx->dev, "hdptx phy pll locked!\n"); ++ ++ return 0; ++} ++ ++static int hdptx_post_power_up(struct rockchip_hdptx_phy *hdptx) ++{ ++ u32 val = 0; ++ int i; ++ ++ val = (HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16 | HDPTX_I_BIAS_EN | ++ HDPTX_I_BGR_EN; ++ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val); ++ udelay(10); ++ reset_control_deassert(hdptx->init_reset); ++ udelay(10); ++ val = HDPTX_I_PLL_EN << 16 | HDPTX_I_PLL_EN; ++ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val); ++ udelay(10); ++ reset_control_deassert(hdptx->cmn_reset); ++ ++ for (i = 0; i < 20; i++) { ++ val = hdptx_grf_read(hdptx, GRF_HDPTX_STATUS); ++ ++ if (val & HDPTX_O_PLL_LOCK_DONE) ++ break; ++ udelay(20); ++ } ++ ++ if (i == 20) { ++ dev_err(hdptx->dev, "hdptx phy can't lock!\n"); ++ return -EINVAL; ++ } ++ ++ udelay(20); ++ ++ reset_control_deassert(hdptx->lane_reset); ++ ++ for (i = 0; i < 50; i++) { ++ val = hdptx_grf_read(hdptx, GRF_HDPTX_STATUS); ++ ++ if (val & HDPTX_O_PHY_RDY) ++ break; ++ udelay(100); ++ } ++ ++ if (i == 50) { ++ dev_err(hdptx->dev, "hdptx phy can't ready!\n"); ++ return -EINVAL; ++ } ++ ++ dev_err(hdptx->dev, "hdptx phy locked!\n"); ++ ++ return 0; ++} ++ ++static void hdptx_phy_disable(struct rockchip_hdptx_phy *hdptx) ++{ ++ u32 val; ++ ++ /* reset phy and apb, or phy locked flag may keep 1 */ ++ reset_control_assert(hdptx->phy_reset); ++ udelay(20); ++ reset_control_deassert(hdptx->phy_reset); ++ ++ reset_control_assert(hdptx->apb_reset); ++ udelay(20); ++ reset_control_deassert(hdptx->apb_reset); ++ ++ hdptx_write(hdptx, LANE_REG0300, 0x82); ++ hdptx_write(hdptx, SB_REG010F, 0xc1); ++ hdptx_write(hdptx, SB_REG0110, 0x1); ++ hdptx_write(hdptx, LANE_REG0301, 0x80); ++ hdptx_write(hdptx, LANE_REG0401, 0x80); ++ hdptx_write(hdptx, LANE_REG0501, 0x80); ++ hdptx_write(hdptx, LANE_REG0601, 0x80); ++ ++ reset_control_assert(hdptx->lane_reset); ++ reset_control_assert(hdptx->cmn_reset); ++ reset_control_assert(hdptx->init_reset); ++ ++ val = (HDPTX_I_PLL_EN | HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16; ++ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val); ++} ++ ++static void hdptx_earc_config(struct rockchip_hdptx_phy *hdptx) ++{ ++ hdptx_update_bits(hdptx, SB_REG0113, SB_RX_RCAL_OPT_CODE_MASK, ++ SB_RX_RCAL_OPT_CODE(1)); ++ hdptx_write(hdptx, SB_REG011C, 0x04); ++ hdptx_update_bits(hdptx, SB_REG011B, SB_AFC_TOL_MASK, ++ SB_AFC_TOL(3)); ++ hdptx_write(hdptx, SB_REG0109, 0x05); ++ hdptx_update_bits(hdptx, SB_REG0120, SB_EARC_EN_MASK | SB_EARC_AFC_EN_MASK, ++ SB_EARC_EN(1) | SB_EARC_AFC_EN(1)); ++ hdptx_update_bits(hdptx, SB_REG011B, SB_EARC_SIG_DET_BYPASS_MASK, ++ SB_EARC_SIG_DET_BYPASS(1)); ++ hdptx_update_bits(hdptx, SB_REG011F, SB_PWM_AFC_CTRL_MASK | SB_RCAL_RSTN_MASK, ++ SB_PWM_AFC_CTRL(0xc) | SB_RCAL_RSTN(1)); ++ hdptx_update_bits(hdptx, SB_REG0115, SB_READY_DELAY_TIME_MASK, ++ SB_READY_DELAY_TIME(2)); ++ hdptx_update_bits(hdptx, SB_REG0113, SB_RX_RTERM_CTRL_MASK, ++ SB_RX_RTERM_CTRL(3)); ++ hdptx_update_bits(hdptx, SB_REG0102, ANA_SB_RXTERM_OFFSP_MASK, ++ ANA_SB_RXTERM_OFFSP(3)); ++ hdptx_update_bits(hdptx, SB_REG0103, ANA_SB_RXTERM_OFFSN_MASK, ++ ANA_SB_RXTERM_OFFSN(3)); ++ hdptx_write(hdptx, SB_REG011A, 0x03); ++ hdptx_write(hdptx, SB_REG0118, 0x0a); ++ hdptx_write(hdptx, SB_REG011E, 0x6a); ++ hdptx_write(hdptx, SB_REG011D, 0x67); ++ hdptx_update_bits(hdptx, SB_REG0117, FAST_PULSE_TIME_MASK, ++ FAST_PULSE_TIME(4)); ++ hdptx_update_bits(hdptx, SB_REG0114, SB_TG_SB_EN_DELAY_TIME_MASK | ++ SB_TG_RXTERM_EN_DELAY_TIME_MASK, ++ SB_TG_SB_EN_DELAY_TIME(2) | ++ SB_TG_RXTERM_EN_DELAY_TIME(2)); ++ hdptx_update_bits(hdptx, SB_REG0105, ANA_SB_TX_HLVL_PROG_MASK, ++ ANA_SB_TX_HLVL_PROG(7)); ++ hdptx_update_bits(hdptx, SB_REG0106, ANA_SB_TX_LLVL_PROG_MASK, ++ ANA_SB_TX_LLVL_PROG(7)); ++ hdptx_update_bits(hdptx, SB_REG010F, ANA_SB_VREG_GAIN_CTRL_MASK, ++ ANA_SB_VREG_GAIN_CTRL(0)); ++ hdptx_update_bits(hdptx, SB_REG0110, ANA_SB_VREG_REF_SEL_MASK, ++ ANA_SB_VREG_REF_SEL(1)); ++ hdptx_update_bits(hdptx, SB_REG0115, SB_TG_OSC_EN_DELAY_TIME_MASK, ++ SB_TG_OSC_EN_DELAY_TIME(2)); ++ hdptx_update_bits(hdptx, SB_REG0116, AFC_RSTN_DELAY_TIME_MASK, ++ AFC_RSTN_DELAY_TIME(2)); ++ hdptx_update_bits(hdptx, SB_REG0109, ANA_SB_DMRX_AFC_DIV_RATIO_MASK, ++ ANA_SB_DMRX_AFC_DIV_RATIO(5)); ++ hdptx_update_bits(hdptx, SB_REG0103, OVRD_SB_RX_RESCAL_DONE_MASK, ++ OVRD_SB_RX_RESCAL_DONE(1)); ++ hdptx_update_bits(hdptx, SB_REG0104, OVRD_SB_EN_MASK, ++ OVRD_SB_EN(1)); ++ hdptx_update_bits(hdptx, SB_REG0102, OVRD_SB_RXTERM_EN_MASK, ++ OVRD_SB_RXTERM_EN(1)); ++ hdptx_update_bits(hdptx, SB_REG0105, OVRD_SB_EARC_CMDC_EN_MASK, ++ OVRD_SB_EARC_CMDC_EN(1)); ++ hdptx_update_bits(hdptx, SB_REG010F, OVRD_SB_VREG_EN_MASK | ++ OVRD_SB_VREG_LPF_BYPASS_MASK, ++ OVRD_SB_VREG_EN(1) | OVRD_SB_VREG_LPF_BYPASS(1)); ++ hdptx_update_bits(hdptx, SB_REG0123, OVRD_SB_READY_MASK, ++ OVRD_SB_READY(1)); ++ udelay(1000); ++ hdptx_update_bits(hdptx, SB_REG0103, SB_RX_RESCAL_DONE_MASK, ++ SB_RX_RESCAL_DONE(1)); ++ udelay(50); ++ hdptx_update_bits(hdptx, SB_REG0104, SB_EN_MASK, SB_EN(1)); ++ udelay(50); ++ hdptx_update_bits(hdptx, SB_REG0102, SB_RXTERM_EN_MASK, ++ SB_RXTERM_EN(1)); ++ udelay(50); ++ hdptx_update_bits(hdptx, SB_REG0105, SB_EARC_CMDC_EN_MASK, ++ SB_EARC_CMDC_EN(1)); ++ hdptx_update_bits(hdptx, SB_REG010F, SB_VREG_EN_MASK, ++ SB_VREG_EN(1)); ++ udelay(50); ++ hdptx_update_bits(hdptx, SB_REG010F, OVRD_SB_VREG_LPF_BYPASS_MASK, ++ OVRD_SB_VREG_LPF_BYPASS(1)); ++ udelay(250); ++ hdptx_update_bits(hdptx, SB_REG010F, OVRD_SB_VREG_LPF_BYPASS_MASK, ++ OVRD_SB_VREG_LPF_BYPASS(0)); ++ udelay(100); ++ hdptx_update_bits(hdptx, SB_REG0123, SB_READY_MASK, SB_READY(1)); ++} ++ ++static bool hdptx_phy_clk_pll_calc(unsigned int data_rate, ++ struct ropll_config *cfg) ++{ ++ unsigned int fref = 24000; ++ unsigned int sdc; ++ unsigned int fout = data_rate / 2; ++ unsigned int fvco; ++ u32 mdiv, sdiv, n = 8; ++ unsigned long k = 0, lc, k_sub, lc_sub; ++ ++ for (sdiv = 16; sdiv >= 1; sdiv--) { ++ if (sdiv % 2 && sdiv != 1) ++ continue; ++ ++ fvco = fout * sdiv; ++ ++ if (fvco < 2000000 || fvco > 4000000) ++ continue; ++ ++ mdiv = DIV_ROUND_UP(fvco, fref); ++ if (mdiv < 20 || mdiv > 255) ++ continue; ++ ++ if (fref * mdiv - fvco) { ++ for (sdc = 264000; sdc <= 750000; sdc += fref) ++ if (sdc * n > fref * mdiv) ++ break; ++ ++ if (sdc > 750000) ++ continue; ++ ++ rational_best_approximation(fref * mdiv - fvco, ++ sdc / 16, ++ GENMASK(6, 0), ++ GENMASK(7, 0), ++ &k, &lc); ++ ++ rational_best_approximation(sdc * n - fref * mdiv, ++ sdc, ++ GENMASK(6, 0), ++ GENMASK(7, 0), ++ &k_sub, &lc_sub); ++ } ++ ++ break; ++ } ++ ++ if (sdiv < 1) ++ return false; ++ ++ if (cfg) { ++ cfg->pms_mdiv = mdiv; ++ cfg->pms_mdiv_afc = mdiv; ++ cfg->pms_pdiv = 1; ++ cfg->pms_refdiv = 1; ++ cfg->pms_sdiv = sdiv - 1; ++ ++ cfg->sdm_en = k > 0 ? 1 : 0; ++ if (cfg->sdm_en) { ++ cfg->sdm_deno = lc; ++ cfg->sdm_num_sign = 1; ++ cfg->sdm_num = k; ++ cfg->sdc_n = n - 3; ++ cfg->sdc_num = k_sub; ++ cfg->sdc_deno = lc_sub; ++ } ++ } ++ ++ return true; ++} ++ ++static int hdptx_ropll_cmn_config(struct rockchip_hdptx_phy *hdptx, unsigned long bit_rate) ++{ ++ int bus_width = phy_get_bus_width(hdptx->phy); ++ u8 color_depth = (bus_width & COLOR_DEPTH_MASK) ? 1 : 0; ++ struct ropll_config *cfg = ropll_tmds_cfg; ++ struct ropll_config rc = {0}; ++ ++ dev_info(hdptx->dev, "%s bus_width:%x rate:%lu\n", __func__, bus_width, bit_rate); ++ hdptx->rate = bit_rate * 100; ++ ++ if (color_depth) ++ bit_rate = bit_rate * 10 / 8; ++ ++ for (; cfg->bit_rate != ~0; cfg++) ++ if (bit_rate == cfg->bit_rate) ++ break; ++ ++ if (cfg->bit_rate == ~0) { ++ if (hdptx_phy_clk_pll_calc(bit_rate, &rc)) { ++ cfg = &rc; ++ } else { ++ dev_err(hdptx->dev, "%s can't find pll cfg\n", __func__); ++ return -EINVAL; ++ } ++ } ++ ++ dev_dbg(hdptx->dev, "mdiv=%u, sdiv=%u\n", ++ cfg->pms_mdiv, cfg->pms_sdiv + 1); ++ dev_dbg(hdptx->dev, "sdm_en=%u, k_sign=%u, k=%u, lc=%u", ++ cfg->sdm_en, cfg->sdm_num_sign, cfg->sdm_num, cfg->sdm_deno); ++ dev_dbg(hdptx->dev, "n=%u, k_sub=%u, lc_sub=%u\n", ++ cfg->sdc_n + 3, cfg->sdc_num, cfg->sdc_deno); ++ ++ hdptx_pre_power_up(hdptx); ++ ++ reset_control_assert(hdptx->ropll_reset); ++ udelay(20); ++ reset_control_deassert(hdptx->ropll_reset); ++ ++ hdptx_write(hdptx, CMN_REG0008, 0x00); ++ hdptx_write(hdptx, CMN_REG0009, 0x0c); ++ hdptx_write(hdptx, CMN_REG000A, 0x83); ++ hdptx_write(hdptx, CMN_REG000B, 0x06); ++ hdptx_write(hdptx, CMN_REG000C, 0x20); ++ hdptx_write(hdptx, CMN_REG000D, 0xb8); ++ hdptx_write(hdptx, CMN_REG000E, 0x0f); ++ hdptx_write(hdptx, CMN_REG000F, 0x0f); ++ hdptx_write(hdptx, CMN_REG0010, 0x04); ++ hdptx_write(hdptx, CMN_REG0011, 0x01); ++ hdptx_write(hdptx, CMN_REG0012, 0x26); ++ hdptx_write(hdptx, CMN_REG0013, 0x22); ++ hdptx_write(hdptx, CMN_REG0014, 0x24); ++ hdptx_write(hdptx, CMN_REG0015, 0x77); ++ hdptx_write(hdptx, CMN_REG0016, 0x08); ++ hdptx_write(hdptx, CMN_REG0017, 0x20); ++ hdptx_write(hdptx, CMN_REG0018, 0x04); ++ hdptx_write(hdptx, CMN_REG0019, 0x48); ++ hdptx_write(hdptx, CMN_REG001A, 0x01); ++ hdptx_write(hdptx, CMN_REG001B, 0x00); ++ hdptx_write(hdptx, CMN_REG001C, 0x01); ++ hdptx_write(hdptx, CMN_REG001D, 0x64); ++ hdptx_write(hdptx, CMN_REG001E, 0x14); ++ hdptx_write(hdptx, CMN_REG001F, 0x00); ++ hdptx_write(hdptx, CMN_REG0020, 0x00); ++ hdptx_write(hdptx, CMN_REG0021, 0x00); ++ hdptx_write(hdptx, CMN_REG0022, 0x11); ++ hdptx_write(hdptx, CMN_REG0023, 0x00); ++ hdptx_write(hdptx, CMN_REG0024, 0x00); ++ hdptx_write(hdptx, CMN_REG0025, 0x53); ++ hdptx_write(hdptx, CMN_REG0026, 0x00); ++ hdptx_write(hdptx, CMN_REG0027, 0x00); ++ hdptx_write(hdptx, CMN_REG0028, 0x01); ++ hdptx_write(hdptx, CMN_REG0029, 0x01); ++ hdptx_write(hdptx, CMN_REG002A, 0x00); ++ hdptx_write(hdptx, CMN_REG002B, 0x00); ++ hdptx_write(hdptx, CMN_REG002C, 0x00); ++ hdptx_write(hdptx, CMN_REG002D, 0x00); ++ hdptx_write(hdptx, CMN_REG002E, 0x04); ++ hdptx_write(hdptx, CMN_REG002F, 0x00); ++ hdptx_write(hdptx, CMN_REG0030, 0x20); ++ hdptx_write(hdptx, CMN_REG0031, 0x30); ++ hdptx_write(hdptx, CMN_REG0032, 0x0b); ++ hdptx_write(hdptx, CMN_REG0033, 0x23); ++ hdptx_write(hdptx, CMN_REG0034, 0x00); ++ hdptx_write(hdptx, CMN_REG0035, 0x00); ++ hdptx_write(hdptx, CMN_REG0038, 0x00); ++ hdptx_write(hdptx, CMN_REG0039, 0x00); ++ hdptx_write(hdptx, CMN_REG003A, 0x00); ++ hdptx_write(hdptx, CMN_REG003B, 0x00); ++ hdptx_write(hdptx, CMN_REG003C, 0x80); ++ hdptx_write(hdptx, CMN_REG003D, 0x40); ++ hdptx_write(hdptx, CMN_REG003E, 0x0c); ++ hdptx_write(hdptx, CMN_REG003F, 0x83); ++ hdptx_write(hdptx, CMN_REG0040, 0x06); ++ hdptx_write(hdptx, CMN_REG0041, 0x20); ++ hdptx_write(hdptx, CMN_REG0042, 0x78); ++ hdptx_write(hdptx, CMN_REG0043, 0x00); ++ hdptx_write(hdptx, CMN_REG0044, 0x46); ++ hdptx_write(hdptx, CMN_REG0045, 0x24); ++ hdptx_write(hdptx, CMN_REG0046, 0xff); ++ hdptx_write(hdptx, CMN_REG0047, 0x00); ++ hdptx_write(hdptx, CMN_REG0048, 0x44); ++ hdptx_write(hdptx, CMN_REG0049, 0xfa); ++ hdptx_write(hdptx, CMN_REG004A, 0x08); ++ hdptx_write(hdptx, CMN_REG004B, 0x00); ++ hdptx_write(hdptx, CMN_REG004C, 0x01); ++ hdptx_write(hdptx, CMN_REG004D, 0x64); ++ hdptx_write(hdptx, CMN_REG004E, 0x34); ++ hdptx_write(hdptx, CMN_REG004F, 0x00); ++ hdptx_write(hdptx, CMN_REG0050, 0x00); ++ ++ hdptx_write(hdptx, CMN_REG0051, cfg->pms_mdiv); ++ hdptx_write(hdptx, CMN_REG0055, cfg->pms_mdiv_afc); ++ ++ hdptx_write(hdptx, CMN_REG0059, (cfg->pms_pdiv << 4) | cfg->pms_refdiv); ++ ++ hdptx_write(hdptx, CMN_REG005A, (cfg->pms_sdiv << 4)); ++ ++ hdptx_write(hdptx, CMN_REG005C, 0x25); ++ hdptx_write(hdptx, CMN_REG005D, 0x0c); ++ hdptx_write(hdptx, CMN_REG005E, 0x4f); ++ hdptx_update_bits(hdptx, CMN_REG005E, ROPLL_SDM_EN_MASK, ++ ROPLL_SDM_EN(cfg->sdm_en)); ++ if (!cfg->sdm_en) ++ hdptx_update_bits(hdptx, CMN_REG005E, 0xf, 0); ++ ++ hdptx_write(hdptx, CMN_REG005F, 0x01); ++ ++ hdptx_update_bits(hdptx, CMN_REG0064, ROPLL_SDM_NUM_SIGN_RBR_MASK, ++ ROPLL_SDM_NUM_SIGN_RBR(cfg->sdm_num_sign)); ++ hdptx_write(hdptx, CMN_REG0065, cfg->sdm_num); ++ hdptx_write(hdptx, CMN_REG0060, cfg->sdm_deno); ++ ++ hdptx_update_bits(hdptx, CMN_REG0069, ROPLL_SDC_N_RBR_MASK, ++ ROPLL_SDC_N_RBR(cfg->sdc_n)); ++ ++ hdptx_write(hdptx, CMN_REG006C, cfg->sdc_num); ++ hdptx_write(hdptx, CMN_REG0070, cfg->sdc_deno); ++ ++ hdptx_write(hdptx, CMN_REG006B, 0x04); ++ ++ hdptx_write(hdptx, CMN_REG0073, 0x30); ++ hdptx_write(hdptx, CMN_REG0074, 0x04); ++ hdptx_write(hdptx, CMN_REG0075, 0x20); ++ hdptx_write(hdptx, CMN_REG0076, 0x30); ++ hdptx_write(hdptx, CMN_REG0077, 0x08); ++ hdptx_write(hdptx, CMN_REG0078, 0x0c); ++ hdptx_write(hdptx, CMN_REG0079, 0x00); ++ hdptx_write(hdptx, CMN_REG007B, 0x00); ++ hdptx_write(hdptx, CMN_REG007C, 0x00); ++ hdptx_write(hdptx, CMN_REG007D, 0x00); ++ hdptx_write(hdptx, CMN_REG007E, 0x00); ++ hdptx_write(hdptx, CMN_REG007F, 0x00); ++ hdptx_write(hdptx, CMN_REG0080, 0x00); ++ hdptx_write(hdptx, CMN_REG0081, 0x01); ++ hdptx_write(hdptx, CMN_REG0082, 0x04); ++ hdptx_write(hdptx, CMN_REG0083, 0x24); ++ hdptx_write(hdptx, CMN_REG0084, 0x20); ++ hdptx_write(hdptx, CMN_REG0085, 0x03); ++ ++ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_POSTDIV_SEL_MASK, ++ PLL_PCG_POSTDIV_SEL(cfg->pms_sdiv)); ++ ++ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_CLK_SEL_MASK, ++ PLL_PCG_CLK_SEL(color_depth)); ++ ++ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_CLK_EN, PLL_PCG_CLK_EN); ++ ++ hdptx_write(hdptx, CMN_REG0087, 0x04); ++ hdptx_write(hdptx, CMN_REG0089, 0x00); ++ hdptx_write(hdptx, CMN_REG008A, 0x55); ++ hdptx_write(hdptx, CMN_REG008B, 0x25); ++ hdptx_write(hdptx, CMN_REG008C, 0x2c); ++ hdptx_write(hdptx, CMN_REG008D, 0x22); ++ hdptx_write(hdptx, CMN_REG008E, 0x14); ++ hdptx_write(hdptx, CMN_REG008F, 0x20); ++ hdptx_write(hdptx, CMN_REG0090, 0x00); ++ hdptx_write(hdptx, CMN_REG0091, 0x00); ++ hdptx_write(hdptx, CMN_REG0092, 0x00); ++ hdptx_write(hdptx, CMN_REG0093, 0x00); ++ hdptx_write(hdptx, CMN_REG0095, 0x00); ++ hdptx_write(hdptx, CMN_REG0097, 0x02); ++ hdptx_write(hdptx, CMN_REG0099, 0x04); ++ hdptx_write(hdptx, CMN_REG009A, 0x11); ++ hdptx_write(hdptx, CMN_REG009B, 0x00); ++ ++ return hdptx_post_enable_pll(hdptx); ++} ++ ++static int hdptx_ropll_tmds_mode_config(struct rockchip_hdptx_phy *hdptx, u32 rate) ++{ ++ u32 bit_rate = rate & DATA_RATE_MASK; ++ ++ if (!(hdptx_grf_read(hdptx, GRF_HDPTX_STATUS) & HDPTX_O_PLL_LOCK_DONE)) { ++ int ret; ++ ++ ret = hdptx_ropll_cmn_config(hdptx, bit_rate); ++ if (ret) ++ return ret; ++ } ++ ++ hdptx_write(hdptx, SB_REG0114, 0x00); ++ hdptx_write(hdptx, SB_REG0115, 0x00); ++ hdptx_write(hdptx, SB_REG0116, 0x00); ++ hdptx_write(hdptx, SB_REG0117, 0x00); ++ hdptx_write(hdptx, LNTOP_REG0200, 0x06); ++ ++ if (bit_rate >= 3400000) { ++ /* For 1/40 bitrate clk */ ++ hdptx_write(hdptx, LNTOP_REG0201, 0x00); ++ hdptx_write(hdptx, LNTOP_REG0202, 0x00); ++ hdptx_write(hdptx, LNTOP_REG0203, 0x0f); ++ hdptx_write(hdptx, LNTOP_REG0204, 0xff); ++ hdptx_write(hdptx, LNTOP_REG0205, 0xff); ++ } else { ++ /* For 1/10 bitrate clk */ ++ hdptx_write(hdptx, LNTOP_REG0201, 0x07); ++ hdptx_write(hdptx, LNTOP_REG0202, 0xc1); ++ hdptx_write(hdptx, LNTOP_REG0203, 0xf0); ++ hdptx_write(hdptx, LNTOP_REG0204, 0x7c); ++ hdptx_write(hdptx, LNTOP_REG0205, 0x1f); ++ } ++ ++ hdptx_write(hdptx, LNTOP_REG0206, 0x07); ++ hdptx_write(hdptx, LNTOP_REG0207, 0x0f); ++ hdptx_write(hdptx, LANE_REG0303, 0x0c); ++ hdptx_write(hdptx, LANE_REG0307, 0x20); ++ hdptx_write(hdptx, LANE_REG030A, 0x17); ++ hdptx_write(hdptx, LANE_REG030B, 0x77); ++ hdptx_write(hdptx, LANE_REG030C, 0x77); ++ hdptx_write(hdptx, LANE_REG030D, 0x77); ++ hdptx_write(hdptx, LANE_REG030E, 0x38); ++ hdptx_write(hdptx, LANE_REG0310, 0x03); ++ hdptx_write(hdptx, LANE_REG0311, 0x0f); ++ hdptx_write(hdptx, LANE_REG0312, 0x00); ++ hdptx_write(hdptx, LANE_REG0316, 0x02); ++ hdptx_write(hdptx, LANE_REG031B, 0x01); ++ hdptx_write(hdptx, LANE_REG031E, 0x00); ++ hdptx_write(hdptx, LANE_REG031F, 0x15); ++ hdptx_write(hdptx, LANE_REG0320, 0xa0); ++ hdptx_write(hdptx, LANE_REG0403, 0x0c); ++ hdptx_write(hdptx, LANE_REG0407, 0x20); ++ hdptx_write(hdptx, LANE_REG040A, 0x17); ++ hdptx_write(hdptx, LANE_REG040B, 0x77); ++ hdptx_write(hdptx, LANE_REG040C, 0x77); ++ hdptx_write(hdptx, LANE_REG040D, 0x77); ++ hdptx_write(hdptx, LANE_REG040E, 0x38); ++ hdptx_write(hdptx, LANE_REG0410, 0x03); ++ hdptx_write(hdptx, LANE_REG0411, 0x0f); ++ hdptx_write(hdptx, LANE_REG0412, 0x00); ++ hdptx_write(hdptx, LANE_REG0416, 0x02); ++ hdptx_write(hdptx, LANE_REG041B, 0x01); ++ hdptx_write(hdptx, LANE_REG041E, 0x00); ++ hdptx_write(hdptx, LANE_REG041F, 0x15); ++ hdptx_write(hdptx, LANE_REG0420, 0xa0); ++ hdptx_write(hdptx, LANE_REG0503, 0x0c); ++ hdptx_write(hdptx, LANE_REG0507, 0x20); ++ hdptx_write(hdptx, LANE_REG050A, 0x17); ++ hdptx_write(hdptx, LANE_REG050B, 0x77); ++ hdptx_write(hdptx, LANE_REG050C, 0x77); ++ hdptx_write(hdptx, LANE_REG050D, 0x77); ++ hdptx_write(hdptx, LANE_REG050E, 0x38); ++ hdptx_write(hdptx, LANE_REG0510, 0x03); ++ hdptx_write(hdptx, LANE_REG0511, 0x0f); ++ hdptx_write(hdptx, LANE_REG0512, 0x00); ++ hdptx_write(hdptx, LANE_REG0516, 0x02); ++ hdptx_write(hdptx, LANE_REG051B, 0x01); ++ hdptx_write(hdptx, LANE_REG051E, 0x00); ++ hdptx_write(hdptx, LANE_REG051F, 0x15); ++ hdptx_write(hdptx, LANE_REG0520, 0xa0); ++ hdptx_write(hdptx, LANE_REG0603, 0x0c); ++ hdptx_write(hdptx, LANE_REG0607, 0x20); ++ hdptx_write(hdptx, LANE_REG060A, 0x17); ++ hdptx_write(hdptx, LANE_REG060B, 0x77); ++ hdptx_write(hdptx, LANE_REG060C, 0x77); ++ hdptx_write(hdptx, LANE_REG060D, 0x77); ++ hdptx_write(hdptx, LANE_REG060E, 0x38); ++ hdptx_write(hdptx, LANE_REG0610, 0x03); ++ hdptx_write(hdptx, LANE_REG0611, 0x0f); ++ hdptx_write(hdptx, LANE_REG0612, 0x00); ++ hdptx_write(hdptx, LANE_REG0616, 0x02); ++ hdptx_write(hdptx, LANE_REG061B, 0x01); ++ hdptx_write(hdptx, LANE_REG061E, 0x08); ++ hdptx_write(hdptx, LANE_REG061F, 0x15); ++ hdptx_write(hdptx, LANE_REG0620, 0xa0); ++ ++ hdptx_write(hdptx, LANE_REG0303, 0x2f); ++ hdptx_write(hdptx, LANE_REG0403, 0x2f); ++ hdptx_write(hdptx, LANE_REG0503, 0x2f); ++ hdptx_write(hdptx, LANE_REG0603, 0x2f); ++ hdptx_write(hdptx, LANE_REG0305, 0x03); ++ hdptx_write(hdptx, LANE_REG0405, 0x03); ++ hdptx_write(hdptx, LANE_REG0505, 0x03); ++ hdptx_write(hdptx, LANE_REG0605, 0x03); ++ hdptx_write(hdptx, LANE_REG0306, 0x1c); ++ hdptx_write(hdptx, LANE_REG0406, 0x1c); ++ hdptx_write(hdptx, LANE_REG0506, 0x1c); ++ hdptx_write(hdptx, LANE_REG0606, 0x1c); ++ ++ if (hdptx->earc_en) ++ hdptx_earc_config(hdptx); ++ ++ return hdptx_post_enable_lane(hdptx); ++} ++ ++static int hdptx_ropll_frl_mode_config(struct rockchip_hdptx_phy *hdptx, u32 rate) ++{ ++ u32 bit_rate = rate & DATA_RATE_MASK; ++ u8 color_depth = (rate & COLOR_DEPTH_MASK) ? 1 : 0; ++ struct ropll_config *cfg = ropll_frl_cfg; ++ ++ for (; cfg->bit_rate != ~0; cfg++) ++ if (bit_rate == cfg->bit_rate) ++ break; ++ ++ if (cfg->bit_rate == ~0) { ++ dev_err(hdptx->dev, "%s can't find pll cfg\n", __func__); ++ return -EINVAL; ++ } ++ ++ hdptx_pre_power_up(hdptx); ++ ++ reset_control_assert(hdptx->ropll_reset); ++ usleep_range(10, 20); ++ reset_control_deassert(hdptx->ropll_reset); ++ ++ hdptx_write(hdptx, CMN_REG0008, 0x00); ++ hdptx_write(hdptx, CMN_REG0009, 0x0c); ++ hdptx_write(hdptx, CMN_REG000A, 0x83); ++ hdptx_write(hdptx, CMN_REG000B, 0x06); ++ hdptx_write(hdptx, CMN_REG000C, 0x20); ++ hdptx_write(hdptx, CMN_REG000D, 0xb8); ++ hdptx_write(hdptx, CMN_REG000E, 0x0f); ++ hdptx_write(hdptx, CMN_REG000F, 0x0f); ++ hdptx_write(hdptx, CMN_REG0010, 0x04); ++ hdptx_write(hdptx, CMN_REG0011, 0x00); ++ hdptx_write(hdptx, CMN_REG0012, 0x26); ++ hdptx_write(hdptx, CMN_REG0013, 0x22); ++ hdptx_write(hdptx, CMN_REG0014, 0x24); ++ hdptx_write(hdptx, CMN_REG0015, 0x77); ++ hdptx_write(hdptx, CMN_REG0016, 0x08); ++ hdptx_write(hdptx, CMN_REG0017, 0x00); ++ hdptx_write(hdptx, CMN_REG0018, 0x04); ++ hdptx_write(hdptx, CMN_REG0019, 0x48); ++ hdptx_write(hdptx, CMN_REG001A, 0x01); ++ hdptx_write(hdptx, CMN_REG001B, 0x00); ++ hdptx_write(hdptx, CMN_REG001C, 0x01); ++ hdptx_write(hdptx, CMN_REG001D, 0x64); ++ hdptx_write(hdptx, CMN_REG001E, 0x14); ++ hdptx_write(hdptx, CMN_REG001F, 0x00); ++ hdptx_write(hdptx, CMN_REG0020, 0x00); ++ hdptx_write(hdptx, CMN_REG0021, 0x00); ++ hdptx_write(hdptx, CMN_REG0022, 0x11); ++ hdptx_write(hdptx, CMN_REG0023, 0x00); ++ hdptx_write(hdptx, CMN_REG0025, 0x00); ++ hdptx_write(hdptx, CMN_REG0026, 0x53); ++ hdptx_write(hdptx, CMN_REG0027, 0x00); ++ hdptx_write(hdptx, CMN_REG0028, 0x00); ++ hdptx_write(hdptx, CMN_REG0029, 0x01); ++ hdptx_write(hdptx, CMN_REG002A, 0x01); ++ hdptx_write(hdptx, CMN_REG002B, 0x00); ++ hdptx_write(hdptx, CMN_REG002C, 0x00); ++ hdptx_write(hdptx, CMN_REG002D, 0x00); ++ hdptx_write(hdptx, CMN_REG002E, 0x00); ++ hdptx_write(hdptx, CMN_REG002F, 0x04); ++ hdptx_write(hdptx, CMN_REG0030, 0x00); ++ hdptx_write(hdptx, CMN_REG0031, 0x20); ++ hdptx_write(hdptx, CMN_REG0032, 0x30); ++ hdptx_write(hdptx, CMN_REG0033, 0x0b); ++ hdptx_write(hdptx, CMN_REG0034, 0x23); ++ hdptx_write(hdptx, CMN_REG0035, 0x00); ++ hdptx_write(hdptx, CMN_REG0038, 0x00); ++ hdptx_write(hdptx, CMN_REG0039, 0x00); ++ hdptx_write(hdptx, CMN_REG003A, 0x00); ++ hdptx_write(hdptx, CMN_REG003B, 0x00); ++ hdptx_write(hdptx, CMN_REG003C, 0x80); ++ hdptx_write(hdptx, CMN_REG003D, 0x40); ++ hdptx_write(hdptx, CMN_REG003E, 0x0c); ++ hdptx_write(hdptx, CMN_REG003F, 0x83); ++ hdptx_write(hdptx, CMN_REG0040, 0x06); ++ hdptx_write(hdptx, CMN_REG0041, 0x20); ++ hdptx_write(hdptx, CMN_REG0042, 0xb8); ++ hdptx_write(hdptx, CMN_REG0043, 0x00); ++ hdptx_write(hdptx, CMN_REG0044, 0x46); ++ hdptx_write(hdptx, CMN_REG0045, 0x24); ++ hdptx_write(hdptx, CMN_REG0046, 0xff); ++ hdptx_write(hdptx, CMN_REG0047, 0x00); ++ hdptx_write(hdptx, CMN_REG0048, 0x44); ++ hdptx_write(hdptx, CMN_REG0049, 0xfa); ++ hdptx_write(hdptx, CMN_REG004A, 0x08); ++ hdptx_write(hdptx, CMN_REG004B, 0x00); ++ hdptx_write(hdptx, CMN_REG004C, 0x01); ++ hdptx_write(hdptx, CMN_REG004D, 0x64); ++ hdptx_write(hdptx, CMN_REG004E, 0x14); ++ hdptx_write(hdptx, CMN_REG004F, 0x00); ++ hdptx_write(hdptx, CMN_REG0050, 0x00); ++ hdptx_write(hdptx, CMN_REG0051, cfg->pms_mdiv); ++ hdptx_write(hdptx, CMN_REG0055, cfg->pms_mdiv_afc); ++ hdptx_write(hdptx, CMN_REG0059, (cfg->pms_pdiv << 4) | cfg->pms_refdiv); ++ hdptx_write(hdptx, CMN_REG005A, (cfg->pms_sdiv << 4)); ++ hdptx_write(hdptx, CMN_REG005C, 0x25); ++ hdptx_write(hdptx, CMN_REG005D, 0x0c); ++ hdptx_update_bits(hdptx, CMN_REG005E, ROPLL_SDM_EN_MASK, ++ ROPLL_SDM_EN(cfg->sdm_en)); ++ if (!cfg->sdm_en) ++ hdptx_update_bits(hdptx, CMN_REG005E, 0xf, 0); ++ hdptx_write(hdptx, CMN_REG005F, 0x01); ++ hdptx_update_bits(hdptx, CMN_REG0064, ROPLL_SDM_NUM_SIGN_RBR_MASK, ++ ROPLL_SDM_NUM_SIGN_RBR(cfg->sdm_num_sign)); ++ hdptx_write(hdptx, CMN_REG0065, cfg->sdm_num); ++ hdptx_write(hdptx, CMN_REG0060, cfg->sdm_deno); ++ hdptx_update_bits(hdptx, CMN_REG0069, ROPLL_SDC_N_RBR_MASK, ++ ROPLL_SDC_N_RBR(cfg->sdc_n)); ++ hdptx_write(hdptx, CMN_REG006C, cfg->sdc_num); ++ hdptx_write(hdptx, CMN_REG0070, cfg->sdc_deno); ++ hdptx_write(hdptx, CMN_REG006B, 0x04); ++ hdptx_write(hdptx, CMN_REG0073, 0x30); ++ hdptx_write(hdptx, CMN_REG0074, 0x00); ++ hdptx_write(hdptx, CMN_REG0075, 0x20); ++ hdptx_write(hdptx, CMN_REG0076, 0x30); ++ hdptx_write(hdptx, CMN_REG0077, 0x08); ++ hdptx_write(hdptx, CMN_REG0078, 0x0c); ++ hdptx_write(hdptx, CMN_REG0079, 0x00); ++ hdptx_write(hdptx, CMN_REG007B, 0x00); ++ hdptx_write(hdptx, CMN_REG007C, 0x00); ++ hdptx_write(hdptx, CMN_REG007D, 0x00); ++ hdptx_write(hdptx, CMN_REG007E, 0x00); ++ hdptx_write(hdptx, CMN_REG007F, 0x00); ++ hdptx_write(hdptx, CMN_REG0080, 0x00); ++ hdptx_write(hdptx, CMN_REG0081, 0x09); ++ hdptx_write(hdptx, CMN_REG0082, 0x04); ++ hdptx_write(hdptx, CMN_REG0083, 0x24); ++ hdptx_write(hdptx, CMN_REG0084, 0x20); ++ hdptx_write(hdptx, CMN_REG0085, 0x03); ++ hdptx_write(hdptx, CMN_REG0086, 0x01); ++ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_POSTDIV_SEL_MASK, ++ PLL_PCG_POSTDIV_SEL(cfg->pms_sdiv)); ++ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_CLK_SEL_MASK, ++ PLL_PCG_CLK_SEL(color_depth)); ++ hdptx_write(hdptx, CMN_REG0087, 0x0c); ++ hdptx_write(hdptx, CMN_REG0089, 0x00); ++ hdptx_write(hdptx, CMN_REG008A, 0x55); ++ hdptx_write(hdptx, CMN_REG008B, 0x25); ++ hdptx_write(hdptx, CMN_REG008C, 0x2c); ++ hdptx_write(hdptx, CMN_REG008D, 0x22); ++ hdptx_write(hdptx, CMN_REG008E, 0x14); ++ hdptx_write(hdptx, CMN_REG008F, 0x20); ++ hdptx_write(hdptx, CMN_REG0090, 0x00); ++ hdptx_write(hdptx, CMN_REG0091, 0x00); ++ hdptx_write(hdptx, CMN_REG0092, 0x00); ++ hdptx_write(hdptx, CMN_REG0093, 0x00); ++ hdptx_write(hdptx, CMN_REG0094, 0x00); ++ hdptx_write(hdptx, CMN_REG0097, 0x02); ++ hdptx_write(hdptx, CMN_REG0099, 0x04); ++ hdptx_write(hdptx, CMN_REG009A, 0x11); ++ hdptx_write(hdptx, CMN_REG009B, 0x10); ++ hdptx_write(hdptx, SB_REG0114, 0x00); ++ hdptx_write(hdptx, SB_REG0115, 0x00); ++ hdptx_write(hdptx, SB_REG0116, 0x00); ++ hdptx_write(hdptx, SB_REG0117, 0x00); ++ hdptx_write(hdptx, LNTOP_REG0200, 0x04); ++ hdptx_write(hdptx, LNTOP_REG0201, 0x00); ++ hdptx_write(hdptx, LNTOP_REG0202, 0x00); ++ hdptx_write(hdptx, LNTOP_REG0203, 0xf0); ++ hdptx_write(hdptx, LNTOP_REG0204, 0xff); ++ hdptx_write(hdptx, LNTOP_REG0205, 0xff); ++ hdptx_write(hdptx, LNTOP_REG0206, 0x05); ++ hdptx_write(hdptx, LNTOP_REG0207, 0x0f); ++ hdptx_write(hdptx, LANE_REG0303, 0x0c); ++ hdptx_write(hdptx, LANE_REG0307, 0x20); ++ hdptx_write(hdptx, LANE_REG030A, 0x17); ++ hdptx_write(hdptx, LANE_REG030B, 0x77); ++ hdptx_write(hdptx, LANE_REG030C, 0x77); ++ hdptx_write(hdptx, LANE_REG030D, 0x77); ++ hdptx_write(hdptx, LANE_REG030E, 0x38); ++ hdptx_write(hdptx, LANE_REG0310, 0x03); ++ hdptx_write(hdptx, LANE_REG0311, 0x0f); ++ hdptx_write(hdptx, LANE_REG0312, 0x3c); ++ hdptx_write(hdptx, LANE_REG0316, 0x02); ++ hdptx_write(hdptx, LANE_REG031B, 0x01); ++ hdptx_write(hdptx, LANE_REG031F, 0x15); ++ hdptx_write(hdptx, LANE_REG0320, 0xa0); ++ hdptx_write(hdptx, LANE_REG0403, 0x0c); ++ hdptx_write(hdptx, LANE_REG0407, 0x20); ++ hdptx_write(hdptx, LANE_REG040A, 0x17); ++ hdptx_write(hdptx, LANE_REG040B, 0x77); ++ hdptx_write(hdptx, LANE_REG040C, 0x77); ++ hdptx_write(hdptx, LANE_REG040D, 0x77); ++ hdptx_write(hdptx, LANE_REG040E, 0x38); ++ hdptx_write(hdptx, LANE_REG0410, 0x03); ++ hdptx_write(hdptx, LANE_REG0411, 0x0f); ++ hdptx_write(hdptx, LANE_REG0412, 0x3c); ++ hdptx_write(hdptx, LANE_REG0416, 0x02); ++ hdptx_write(hdptx, LANE_REG041B, 0x01); ++ hdptx_write(hdptx, LANE_REG041F, 0x15); ++ hdptx_write(hdptx, LANE_REG0420, 0xa0); ++ hdptx_write(hdptx, LANE_REG0503, 0x0c); ++ hdptx_write(hdptx, LANE_REG0507, 0x20); ++ hdptx_write(hdptx, LANE_REG050A, 0x17); ++ hdptx_write(hdptx, LANE_REG050B, 0x77); ++ hdptx_write(hdptx, LANE_REG050C, 0x77); ++ hdptx_write(hdptx, LANE_REG050D, 0x77); ++ hdptx_write(hdptx, LANE_REG050E, 0x38); ++ hdptx_write(hdptx, LANE_REG0510, 0x03); ++ hdptx_write(hdptx, LANE_REG0511, 0x0f); ++ hdptx_write(hdptx, LANE_REG0512, 0x3c); ++ hdptx_write(hdptx, LANE_REG0516, 0x02); ++ hdptx_write(hdptx, LANE_REG051B, 0x01); ++ hdptx_write(hdptx, LANE_REG051F, 0x15); ++ hdptx_write(hdptx, LANE_REG0520, 0xa0); ++ hdptx_write(hdptx, LANE_REG0603, 0x0c); ++ hdptx_write(hdptx, LANE_REG0607, 0x20); ++ hdptx_write(hdptx, LANE_REG060A, 0x17); ++ hdptx_write(hdptx, LANE_REG060B, 0x77); ++ hdptx_write(hdptx, LANE_REG060C, 0x77); ++ hdptx_write(hdptx, LANE_REG060D, 0x77); ++ hdptx_write(hdptx, LANE_REG060E, 0x38); ++ hdptx_write(hdptx, LANE_REG0610, 0x03); ++ hdptx_write(hdptx, LANE_REG0611, 0x0f); ++ hdptx_write(hdptx, LANE_REG0612, 0x3c); ++ hdptx_write(hdptx, LANE_REG0616, 0x02); ++ hdptx_write(hdptx, LANE_REG061B, 0x01); ++ hdptx_write(hdptx, LANE_REG061F, 0x15); ++ hdptx_write(hdptx, LANE_REG0620, 0xa0); ++ ++ if (hdptx->earc_en) ++ hdptx_earc_config(hdptx); ++ ++ return hdptx_post_power_up(hdptx); ++} ++ ++static int hdptx_lcpll_frl_mode_config(struct rockchip_hdptx_phy *hdptx, u32 rate) ++{ ++ u32 bit_rate = rate & DATA_RATE_MASK; ++ u8 color_depth = (rate & COLOR_DEPTH_MASK) ? 1 : 0; ++ struct lcpll_config *cfg = lcpll_cfg; ++ ++ for (; cfg->bit_rate != ~0; cfg++) ++ if (bit_rate == cfg->bit_rate) ++ break; ++ ++ if (cfg->bit_rate == ~0) ++ return -EINVAL; ++ ++ hdptx_pre_power_up(hdptx); ++ ++ hdptx_update_bits(hdptx, CMN_REG0008, LCPLL_EN_MASK | ++ LCPLL_LCVCO_MODE_EN_MASK, LCPLL_EN(1) | ++ LCPLL_LCVCO_MODE_EN(cfg->lcvco_mode_en)); ++ hdptx_write(hdptx, CMN_REG0009, 0x0c); ++ hdptx_write(hdptx, CMN_REG000A, 0x83); ++ hdptx_write(hdptx, CMN_REG000B, 0x06); ++ hdptx_write(hdptx, CMN_REG000C, 0x20); ++ hdptx_write(hdptx, CMN_REG000D, 0xb8); ++ hdptx_write(hdptx, CMN_REG000E, 0x0f); ++ hdptx_write(hdptx, CMN_REG000F, 0x0f); ++ hdptx_write(hdptx, CMN_REG0010, 0x04); ++ hdptx_write(hdptx, CMN_REG0011, 0x00); ++ hdptx_write(hdptx, CMN_REG0012, 0x26); ++ hdptx_write(hdptx, CMN_REG0013, 0x22); ++ hdptx_write(hdptx, CMN_REG0014, 0x24); ++ hdptx_write(hdptx, CMN_REG0015, 0x77); ++ hdptx_write(hdptx, CMN_REG0016, 0x08); ++ hdptx_write(hdptx, CMN_REG0017, 0x00); ++ hdptx_write(hdptx, CMN_REG0018, 0x04); ++ hdptx_write(hdptx, CMN_REG0019, 0x48); ++ hdptx_write(hdptx, CMN_REG001A, 0x01); ++ hdptx_write(hdptx, CMN_REG001B, 0x00); ++ hdptx_write(hdptx, CMN_REG001C, 0x01); ++ hdptx_write(hdptx, CMN_REG001D, 0x64); ++ hdptx_update_bits(hdptx, CMN_REG001E, LCPLL_PI_EN_MASK | ++ LCPLL_100M_CLK_EN_MASK, ++ LCPLL_PI_EN(cfg->pi_en) | ++ LCPLL_100M_CLK_EN(cfg->clk_en_100m)); ++ hdptx_write(hdptx, CMN_REG001F, 0x00); ++ hdptx_write(hdptx, CMN_REG0020, cfg->pms_mdiv); ++ hdptx_write(hdptx, CMN_REG0021, cfg->pms_mdiv_afc); ++ hdptx_write(hdptx, CMN_REG0022, (cfg->pms_pdiv << 4) | cfg->pms_refdiv); ++ hdptx_write(hdptx, CMN_REG0023, (cfg->pms_sdiv << 4) | cfg->pms_sdiv); ++ hdptx_write(hdptx, CMN_REG0025, 0x10); ++ hdptx_write(hdptx, CMN_REG0026, 0x53); ++ hdptx_write(hdptx, CMN_REG0027, 0x01); ++ hdptx_write(hdptx, CMN_REG0028, 0x0d); ++ hdptx_write(hdptx, CMN_REG0029, 0x01); ++ hdptx_write(hdptx, CMN_REG002A, cfg->sdm_deno); ++ hdptx_write(hdptx, CMN_REG002B, cfg->sdm_num_sign); ++ hdptx_write(hdptx, CMN_REG002C, cfg->sdm_num); ++ hdptx_update_bits(hdptx, CMN_REG002D, LCPLL_SDC_N_MASK, ++ LCPLL_SDC_N(cfg->sdc_n)); ++ hdptx_write(hdptx, CMN_REG002E, 0x02); ++ hdptx_write(hdptx, CMN_REG002F, 0x0d); ++ hdptx_write(hdptx, CMN_REG0030, 0x00); ++ hdptx_write(hdptx, CMN_REG0031, 0x20); ++ hdptx_write(hdptx, CMN_REG0032, 0x30); ++ hdptx_write(hdptx, CMN_REG0033, 0x0b); ++ hdptx_write(hdptx, CMN_REG0034, 0x23); ++ hdptx_write(hdptx, CMN_REG0035, 0x00); ++ hdptx_write(hdptx, CMN_REG0038, 0x00); ++ hdptx_write(hdptx, CMN_REG0039, 0x00); ++ hdptx_write(hdptx, CMN_REG003A, 0x00); ++ hdptx_write(hdptx, CMN_REG003B, 0x00); ++ hdptx_write(hdptx, CMN_REG003C, 0x80); ++ hdptx_write(hdptx, CMN_REG003D, 0x00); ++ hdptx_write(hdptx, CMN_REG003E, 0x0c); ++ hdptx_write(hdptx, CMN_REG003F, 0x83); ++ hdptx_write(hdptx, CMN_REG0040, 0x06); ++ hdptx_write(hdptx, CMN_REG0041, 0x20); ++ hdptx_write(hdptx, CMN_REG0042, 0xb8); ++ hdptx_write(hdptx, CMN_REG0043, 0x00); ++ hdptx_write(hdptx, CMN_REG0044, 0x46); ++ hdptx_write(hdptx, CMN_REG0045, 0x24); ++ hdptx_write(hdptx, CMN_REG0046, 0xff); ++ hdptx_write(hdptx, CMN_REG0047, 0x00); ++ hdptx_write(hdptx, CMN_REG0048, 0x44); ++ hdptx_write(hdptx, CMN_REG0049, 0xfa); ++ hdptx_write(hdptx, CMN_REG004A, 0x08); ++ hdptx_write(hdptx, CMN_REG004B, 0x00); ++ hdptx_write(hdptx, CMN_REG004C, 0x01); ++ hdptx_write(hdptx, CMN_REG004D, 0x64); ++ hdptx_write(hdptx, CMN_REG004E, 0x14); ++ hdptx_write(hdptx, CMN_REG004F, 0x00); ++ hdptx_write(hdptx, CMN_REG0050, 0x00); ++ hdptx_write(hdptx, CMN_REG0051, 0x00); ++ hdptx_write(hdptx, CMN_REG0055, 0x00); ++ hdptx_write(hdptx, CMN_REG0059, 0x11); ++ hdptx_write(hdptx, CMN_REG005A, 0x03); ++ hdptx_write(hdptx, CMN_REG005C, 0x05); ++ hdptx_write(hdptx, CMN_REG005D, 0x0c); ++ hdptx_write(hdptx, CMN_REG005E, 0x07); ++ hdptx_write(hdptx, CMN_REG005F, 0x01); ++ hdptx_write(hdptx, CMN_REG0060, 0x01); ++ hdptx_write(hdptx, CMN_REG0064, 0x07); ++ hdptx_write(hdptx, CMN_REG0065, 0x00); ++ hdptx_write(hdptx, CMN_REG0069, 0x00); ++ hdptx_write(hdptx, CMN_REG006B, 0x04); ++ hdptx_write(hdptx, CMN_REG006C, 0x00); ++ hdptx_write(hdptx, CMN_REG0070, 0x01); ++ hdptx_write(hdptx, CMN_REG0073, 0x30); ++ hdptx_write(hdptx, CMN_REG0074, 0x00); ++ hdptx_write(hdptx, CMN_REG0075, 0x20); ++ hdptx_write(hdptx, CMN_REG0076, 0x30); ++ hdptx_write(hdptx, CMN_REG0077, 0x08); ++ hdptx_write(hdptx, CMN_REG0078, 0x0c); ++ hdptx_write(hdptx, CMN_REG0079, 0x00); ++ hdptx_write(hdptx, CMN_REG007B, 0x00); ++ hdptx_write(hdptx, CMN_REG007C, 0x00); ++ hdptx_write(hdptx, CMN_REG007D, 0x00); ++ hdptx_write(hdptx, CMN_REG007E, 0x00); ++ hdptx_write(hdptx, CMN_REG007F, 0x00); ++ hdptx_write(hdptx, CMN_REG0080, 0x00); ++ hdptx_write(hdptx, CMN_REG0081, 0x09); ++ hdptx_write(hdptx, CMN_REG0082, 0x04); ++ hdptx_write(hdptx, CMN_REG0083, 0x24); ++ hdptx_write(hdptx, CMN_REG0084, 0x20); ++ hdptx_write(hdptx, CMN_REG0085, 0x03); ++ hdptx_write(hdptx, CMN_REG0086, 0x01); ++ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_POSTDIV_SEL_MASK, ++ PLL_PCG_POSTDIV_SEL(cfg->pms_sdiv)); ++ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_CLK_SEL_MASK, ++ PLL_PCG_CLK_SEL(color_depth)); ++ hdptx_write(hdptx, CMN_REG0087, 0x0c); ++ hdptx_write(hdptx, CMN_REG0089, 0x02); ++ hdptx_write(hdptx, CMN_REG008A, 0x55); ++ hdptx_write(hdptx, CMN_REG008B, 0x25); ++ hdptx_write(hdptx, CMN_REG008C, 0x2c); ++ hdptx_write(hdptx, CMN_REG008D, 0x22); ++ hdptx_write(hdptx, CMN_REG008E, 0x14); ++ hdptx_write(hdptx, CMN_REG008F, 0x20); ++ hdptx_write(hdptx, CMN_REG0090, 0x00); ++ hdptx_write(hdptx, CMN_REG0091, 0x00); ++ hdptx_write(hdptx, CMN_REG0092, 0x00); ++ hdptx_write(hdptx, CMN_REG0093, 0x00); ++ hdptx_write(hdptx, CMN_REG0095, 0x00); ++ hdptx_write(hdptx, CMN_REG0097, 0x00); ++ hdptx_write(hdptx, CMN_REG0099, 0x00); ++ hdptx_write(hdptx, CMN_REG009A, 0x11); ++ hdptx_write(hdptx, CMN_REG009B, 0x10); ++ hdptx_write(hdptx, SB_REG0114, 0x00); ++ hdptx_write(hdptx, SB_REG0115, 0x00); ++ hdptx_write(hdptx, SB_REG0116, 0x00); ++ hdptx_write(hdptx, SB_REG0117, 0x00); ++ hdptx_write(hdptx, LNTOP_REG0200, 0x04); ++ hdptx_write(hdptx, LNTOP_REG0201, 0x00); ++ hdptx_write(hdptx, LNTOP_REG0202, 0x00); ++ hdptx_write(hdptx, LNTOP_REG0203, 0xf0); ++ hdptx_write(hdptx, LNTOP_REG0204, 0xff); ++ hdptx_write(hdptx, LNTOP_REG0205, 0xff); ++ hdptx_write(hdptx, LNTOP_REG0206, 0x05); ++ hdptx_write(hdptx, LNTOP_REG0207, 0x0f); ++ hdptx_write(hdptx, LANE_REG0303, 0x0c); ++ hdptx_write(hdptx, LANE_REG0307, 0x20); ++ hdptx_write(hdptx, LANE_REG030A, 0x17); ++ hdptx_write(hdptx, LANE_REG030B, 0x77); ++ hdptx_write(hdptx, LANE_REG030C, 0x77); ++ hdptx_write(hdptx, LANE_REG030D, 0x77); ++ hdptx_write(hdptx, LANE_REG030E, 0x38); ++ hdptx_write(hdptx, LANE_REG0310, 0x03); ++ hdptx_write(hdptx, LANE_REG0311, 0x0f); ++ hdptx_write(hdptx, LANE_REG0312, 0x3c); ++ hdptx_write(hdptx, LANE_REG0316, 0x02); ++ hdptx_write(hdptx, LANE_REG031B, 0x01); ++ hdptx_write(hdptx, LANE_REG031F, 0x15); ++ hdptx_write(hdptx, LANE_REG0320, 0xa0); ++ hdptx_write(hdptx, LANE_REG0403, 0x0c); ++ hdptx_write(hdptx, LANE_REG0407, 0x20); ++ hdptx_write(hdptx, LANE_REG040A, 0x17); ++ hdptx_write(hdptx, LANE_REG040B, 0x77); ++ hdptx_write(hdptx, LANE_REG040C, 0x77); ++ hdptx_write(hdptx, LANE_REG040D, 0x77); ++ hdptx_write(hdptx, LANE_REG040E, 0x38); ++ hdptx_write(hdptx, LANE_REG0410, 0x03); ++ hdptx_write(hdptx, LANE_REG0411, 0x0f); ++ hdptx_write(hdptx, LANE_REG0412, 0x3c); ++ hdptx_write(hdptx, LANE_REG0416, 0x02); ++ hdptx_write(hdptx, LANE_REG041B, 0x01); ++ hdptx_write(hdptx, LANE_REG041F, 0x15); ++ hdptx_write(hdptx, LANE_REG0420, 0xa0); ++ hdptx_write(hdptx, LANE_REG0503, 0x0c); ++ hdptx_write(hdptx, LANE_REG0507, 0x20); ++ hdptx_write(hdptx, LANE_REG050A, 0x17); ++ hdptx_write(hdptx, LANE_REG050B, 0x77); ++ hdptx_write(hdptx, LANE_REG050C, 0x77); ++ hdptx_write(hdptx, LANE_REG050D, 0x77); ++ hdptx_write(hdptx, LANE_REG050E, 0x38); ++ hdptx_write(hdptx, LANE_REG0510, 0x03); ++ hdptx_write(hdptx, LANE_REG0511, 0x0f); ++ hdptx_write(hdptx, LANE_REG0512, 0x3c); ++ hdptx_write(hdptx, LANE_REG0516, 0x02); ++ hdptx_write(hdptx, LANE_REG051B, 0x01); ++ hdptx_write(hdptx, LANE_REG051F, 0x15); ++ hdptx_write(hdptx, LANE_REG0520, 0xa0); ++ hdptx_write(hdptx, LANE_REG0603, 0x0c); ++ hdptx_write(hdptx, LANE_REG0607, 0x20); ++ hdptx_write(hdptx, LANE_REG060A, 0x17); ++ hdptx_write(hdptx, LANE_REG060B, 0x77); ++ hdptx_write(hdptx, LANE_REG060C, 0x77); ++ hdptx_write(hdptx, LANE_REG060D, 0x77); ++ hdptx_write(hdptx, LANE_REG060E, 0x38); ++ hdptx_write(hdptx, LANE_REG0610, 0x03); ++ hdptx_write(hdptx, LANE_REG0611, 0x0f); ++ hdptx_write(hdptx, LANE_REG0612, 0x3c); ++ hdptx_write(hdptx, LANE_REG0616, 0x02); ++ hdptx_write(hdptx, LANE_REG061B, 0x01); ++ hdptx_write(hdptx, LANE_REG061F, 0x15); ++ hdptx_write(hdptx, LANE_REG0620, 0xa0); ++ ++ hdptx_write(hdptx, LANE_REG0303, 0x2f); ++ hdptx_write(hdptx, LANE_REG0403, 0x2f); ++ hdptx_write(hdptx, LANE_REG0503, 0x2f); ++ hdptx_write(hdptx, LANE_REG0603, 0x2f); ++ hdptx_write(hdptx, LANE_REG0305, 0x03); ++ hdptx_write(hdptx, LANE_REG0405, 0x03); ++ hdptx_write(hdptx, LANE_REG0505, 0x03); ++ hdptx_write(hdptx, LANE_REG0605, 0x03); ++ hdptx_write(hdptx, LANE_REG0306, 0xfc); ++ hdptx_write(hdptx, LANE_REG0406, 0xfc); ++ hdptx_write(hdptx, LANE_REG0506, 0xfc); ++ hdptx_write(hdptx, LANE_REG0606, 0xfc); ++ ++ hdptx_write(hdptx, LANE_REG0305, 0x4f); ++ hdptx_write(hdptx, LANE_REG0405, 0x4f); ++ hdptx_write(hdptx, LANE_REG0505, 0x4f); ++ hdptx_write(hdptx, LANE_REG0605, 0x4f); ++ hdptx_write(hdptx, LANE_REG0304, 0x14); ++ hdptx_write(hdptx, LANE_REG0404, 0x14); ++ hdptx_write(hdptx, LANE_REG0504, 0x14); ++ hdptx_write(hdptx, LANE_REG0604, 0x14); ++ ++ if (hdptx->earc_en) ++ hdptx_earc_config(hdptx); ++ ++ return hdptx_post_power_up(hdptx); ++} ++ ++static int rockchip_hdptx_phy_power_on(struct phy *phy) ++{ ++ struct rockchip_hdptx_phy *hdptx = phy_get_drvdata(phy); ++ int bus_width = phy_get_bus_width(hdptx->phy); ++ int bit_rate = bus_width & DATA_RATE_MASK; ++ int ret; ++ ++ if (!hdptx->count) { ++ ret = clk_bulk_enable(hdptx->nr_clks, hdptx->clks); ++ if (ret) { ++ dev_err(hdptx->dev, "failed to enable clocks\n"); ++ return ret; ++ } ++ } ++ ++ dev_info(hdptx->dev, "bus_width:0x%x,bit_rate:%d\n", bus_width, bit_rate); ++ if (bus_width & HDMI_EARC_MASK) ++ hdptx->earc_en = true; ++ else ++ hdptx->earc_en = false; ++ ++ if (bus_width & HDMI_MODE_MASK) { ++ if (bit_rate > 24000000) ++ return hdptx_lcpll_frl_mode_config(hdptx, bus_width); ++ else ++ return hdptx_ropll_frl_mode_config(hdptx, bus_width); ++ } else { ++ return hdptx_ropll_tmds_mode_config(hdptx, bus_width); ++ } ++} ++ ++static int rockchip_hdptx_phy_power_off(struct phy *phy) ++{ ++ struct rockchip_hdptx_phy *hdptx = phy_get_drvdata(phy); ++ ++ if (hdptx->count) ++ return 0; ++ ++ if (!(hdptx_grf_read(hdptx, GRF_HDPTX_STATUS) & HDPTX_O_PLL_LOCK_DONE)) ++ return 0; ++ ++ hdptx_phy_disable(hdptx); ++ clk_bulk_disable(hdptx->nr_clks, hdptx->clks); ++ ++ return 0; ++} ++ ++static const struct phy_ops rockchip_hdptx_phy_ops = { ++ .owner = THIS_MODULE, ++ .power_on = rockchip_hdptx_phy_power_on, ++ .power_off = rockchip_hdptx_phy_power_off, ++}; ++ ++static const struct of_device_id rockchip_hdptx_phy_of_match[] = { ++ { .compatible = "rockchip,rk3588-hdptx-phy-hdmi", ++ }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, rockchip_hdptx_phy_of_match); ++ ++static void rockchip_hdptx_phy_runtime_disable(void *data) ++{ ++ struct rockchip_hdptx_phy *hdptx = data; ++ ++ clk_bulk_unprepare(hdptx->nr_clks, hdptx->clks); ++ pm_runtime_disable(hdptx->dev); ++} ++ ++static unsigned long hdptx_phy_clk_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ struct rockchip_hdptx_phy *hdptx = to_rockchip_hdptx_phy(hw); ++ ++ return hdptx->rate; ++} ++ ++static long hdptx_phy_clk_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *parent_rate) ++{ ++ struct ropll_config *cfg = ropll_tmds_cfg; ++ u32 bit_rate = rate / 100; ++ ++ if (rate > HDMI20_MAX_RATE) ++ return rate; ++ ++ for (; cfg->bit_rate != ~0; cfg++) ++ if (bit_rate == cfg->bit_rate) ++ break; ++ ++ if (cfg->bit_rate == ~0 && !hdptx_phy_clk_pll_calc(bit_rate, NULL)) ++ return -EINVAL; ++ ++ return rate; ++} ++ ++static int hdptx_phy_clk_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct rockchip_hdptx_phy *hdptx = to_rockchip_hdptx_phy(hw); ++ ++ if (hdptx_grf_read(hdptx, GRF_HDPTX_STATUS) & HDPTX_O_PLL_LOCK_DONE) ++ hdptx_phy_disable(hdptx); ++ ++ rate = rate / 100; ++ ++ return hdptx_ropll_cmn_config(hdptx, rate); ++} ++ ++static int hdptx_phy_clk_enable(struct clk_hw *hw) ++{ ++ struct rockchip_hdptx_phy *hdptx = to_rockchip_hdptx_phy(hw); ++ int ret; ++ ++ if (hdptx->count) { ++ hdptx->count++; ++ return 0; ++ } ++ ++ ret = clk_bulk_enable(hdptx->nr_clks, hdptx->clks); ++ if (ret) { ++ dev_err(hdptx->dev, "failed to enable clocks\n"); ++ return ret; ++ } ++ ++ if (hdptx->rate) { ++ ret = hdptx_ropll_cmn_config(hdptx, hdptx->rate / 100); ++ if (ret < 0) { ++ dev_err(hdptx->dev, "hdmi phy pll init failed\n"); ++ return ret; ++ } ++ } ++ ++ hdptx->count++; ++ ++ return 0; ++} ++ ++static void hdptx_phy_clk_disable(struct clk_hw *hw) ++{ ++ struct rockchip_hdptx_phy *hdptx = to_rockchip_hdptx_phy(hw); ++ ++ if (hdptx->count > 1) { ++ hdptx->count--; ++ return; ++ } ++ ++ if (hdptx_grf_read(hdptx, GRF_HDPTX_STATUS) & HDPTX_O_PLL_LOCK_DONE) ++ hdptx_phy_disable(hdptx); ++ clk_bulk_disable(hdptx->nr_clks, hdptx->clks); ++ hdptx->count--; ++} ++ ++static const struct clk_ops hdptx_phy_clk_ops = { ++ .recalc_rate = hdptx_phy_clk_recalc_rate, ++ .round_rate = hdptx_phy_clk_round_rate, ++ .set_rate = hdptx_phy_clk_set_rate, ++ .enable = hdptx_phy_clk_enable, ++ .disable = hdptx_phy_clk_disable, ++}; ++ ++static int rockchip_hdptx_phy_clk_register(struct rockchip_hdptx_phy *hdptx) ++{ ++ struct device *dev = hdptx->dev; ++ struct device_node *np = dev->of_node; ++ struct device_node *clk_np; ++ struct platform_device *pdev; ++ struct clk_init_data init = {}; ++ struct clk *refclk; ++ const char *parent_name; ++ int ret; ++ ++ clk_np = of_get_child_by_name(np, "clk-port"); ++ if (!clk_np) ++ return 0; ++ ++ pdev = of_platform_device_create(clk_np, NULL, dev); ++ if (!pdev) ++ return 0; ++ ++ refclk = devm_clk_get(dev, "ref"); ++ if (IS_ERR(refclk)) { ++ dev_err(dev, "failed to get ref clock\n"); ++ return PTR_ERR(refclk); ++ } ++ ++ parent_name = __clk_get_name(refclk); ++ ++ init.parent_names = &parent_name; ++ init.num_parents = 1; ++ init.flags = CLK_GET_RATE_NOCACHE; ++ if (!hdptx->id) ++ init.name = "clk_hdmiphy_pixel0"; ++ else ++ init.name = "clk_hdmiphy_pixel1"; ++ init.ops = &hdptx_phy_clk_ops; ++ ++ /* optional override of the clock name */ ++ of_property_read_string(np, "clock-output-names", &init.name); ++ ++ hdptx->hw.init = &init; ++ ++ hdptx->dclk = devm_clk_register(&pdev->dev, &hdptx->hw); ++ if (IS_ERR(hdptx->dclk)) { ++ ret = PTR_ERR(hdptx->dclk); ++ dev_err(dev, "failed to register clock: %d\n", ret); ++ return ret; ++ } ++ ++ ret = of_clk_add_provider(clk_np, of_clk_src_simple_get, hdptx->dclk); ++ if (ret) { ++ dev_err(dev, "failed to register OF clock provider: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int rockchip_hdptx_phy_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ struct rockchip_hdptx_phy *hdptx; ++ struct phy_provider *phy_provider; ++ struct resource *res; ++ void __iomem *regs; ++ int ret; ++ ++ hdptx = devm_kzalloc(dev, sizeof(*hdptx), GFP_KERNEL); ++ if (!hdptx) ++ return -ENOMEM; ++ ++ hdptx->dev = dev; ++ ++ hdptx->id = of_alias_get_id(dev->of_node, "hdptxhdmi"); ++ if (hdptx->id < 0) ++ hdptx->id = 0; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ regs = devm_ioremap_resource(dev, res); ++ if (IS_ERR(regs)) ++ return PTR_ERR(regs); ++ ++ ret = devm_clk_bulk_get_all(dev, &hdptx->clks); ++ if (ret < 1) ++ return dev_err_probe(dev, ret, "failed to get clocks\n"); ++ ++ hdptx->nr_clks = ret; ++ ++ ret = clk_bulk_prepare(hdptx->nr_clks, hdptx->clks); ++ if (ret) { ++ dev_err(hdptx->dev, "failed to prepare clocks\n"); ++ return ret; ++ } ++ ++ hdptx->regmap = devm_regmap_init_mmio(dev, regs, ++ &rockchip_hdptx_phy_regmap_config); ++ if (IS_ERR(hdptx->regmap)) { ++ ret = PTR_ERR(hdptx->regmap); ++ dev_err(dev, "failed to init regmap: %d\n", ret); ++ goto err_regsmap; ++ } ++ ++ hdptx->phy_reset = devm_reset_control_get(dev, "phy"); ++ if (IS_ERR(hdptx->phy_reset)) { ++ ret = PTR_ERR(hdptx->phy_reset); ++ dev_err(dev, "failed to get phy reset: %d\n", ret); ++ goto err_regsmap; ++ } ++ ++ hdptx->apb_reset = devm_reset_control_get(dev, "apb"); ++ if (IS_ERR(hdptx->apb_reset)) { ++ ret = PTR_ERR(hdptx->apb_reset); ++ dev_err(dev, "failed to get apb reset: %d\n", ret); ++ goto err_regsmap; ++ } ++ ++ hdptx->init_reset = devm_reset_control_get(dev, "init"); ++ if (IS_ERR(hdptx->init_reset)) { ++ ret = PTR_ERR(hdptx->init_reset); ++ dev_err(dev, "failed to get init reset: %d\n", ret); ++ goto err_regsmap; ++ } ++ ++ hdptx->cmn_reset = devm_reset_control_get(dev, "cmn"); ++ if (IS_ERR(hdptx->cmn_reset)) { ++ ret = PTR_ERR(hdptx->cmn_reset); ++ dev_err(dev, "failed to get apb reset: %d\n", ret); ++ goto err_regsmap; ++ } ++ ++ hdptx->lane_reset = devm_reset_control_get(dev, "lane"); ++ if (IS_ERR(hdptx->lane_reset)) { ++ ret = PTR_ERR(hdptx->lane_reset); ++ dev_err(dev, "failed to get lane reset: %d\n", ret); ++ goto err_regsmap; ++ } ++ ++ hdptx->ropll_reset = devm_reset_control_get(dev, "ropll"); ++ if (IS_ERR(hdptx->ropll_reset)) { ++ ret = PTR_ERR(hdptx->ropll_reset); ++ dev_err(dev, "failed to get ropll reset: %d\n", ret); ++ goto err_regsmap; ++ } ++ ++ hdptx->lcpll_reset = devm_reset_control_get(dev, "lcpll"); ++ if (IS_ERR(hdptx->lcpll_reset)) { ++ ret = PTR_ERR(hdptx->lcpll_reset); ++ dev_err(dev, "failed to get lcpll reset: %d\n", ret); ++ goto err_regsmap; ++ } ++ ++ hdptx->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); ++ if (IS_ERR(hdptx->grf)) { ++ ret = PTR_ERR(hdptx->grf); ++ dev_err(hdptx->dev, "Unable to get rockchip,grf\n"); ++ goto err_regsmap; ++ } ++ ++ hdptx->phy = devm_phy_create(dev, NULL, &rockchip_hdptx_phy_ops); ++ if (IS_ERR(hdptx->phy)) { ++ dev_err(dev, "failed to create HDMI PHY\n"); ++ ret = PTR_ERR(hdptx->phy); ++ goto err_regsmap; ++ } ++ ++ phy_set_drvdata(hdptx->phy, hdptx); ++ phy_set_bus_width(hdptx->phy, 8); ++ ++ pm_runtime_enable(dev); ++ ret = devm_add_action_or_reset(dev, rockchip_hdptx_phy_runtime_disable, ++ hdptx); ++ if (ret) ++ goto err_regsmap; ++ ++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ if (IS_ERR(phy_provider)) { ++ dev_err(dev, "failed to register PHY provider\n"); ++ ret = PTR_ERR(phy_provider); ++ goto err_regsmap; ++ } ++ ++ reset_control_deassert(hdptx->apb_reset); ++ reset_control_deassert(hdptx->cmn_reset); ++ reset_control_deassert(hdptx->init_reset); ++ ++ ret = rockchip_hdptx_phy_clk_register(hdptx); ++ if (ret) ++ goto err_regsmap; ++ ++ platform_set_drvdata(pdev, hdptx); ++ dev_info(dev, "hdptx phy init success\n"); ++ return 0; ++ ++err_regsmap: ++ clk_bulk_unprepare(hdptx->nr_clks, hdptx->clks); ++ return ret; ++} ++ ++static struct platform_driver rockchip_hdptx_phy_driver = { ++ .probe = rockchip_hdptx_phy_probe, ++ .driver = { ++ .name = "rockchip-hdptx-phy-hdmi", ++ .of_match_table = of_match_ptr(rockchip_hdptx_phy_of_match), ++ }, ++}; ++ ++module_platform_driver(rockchip_hdptx_phy_driver); ++ ++MODULE_DESCRIPTION("Samsung HDMI-DP Transmitter Combphy Driver"); ++MODULE_LICENSE("GPL v2"); +-- +2.42.1 + + +From 171fd53717525f0f6dc56e90e6f38a9038c5c779 Mon Sep 17 00:00:00 2001 +From: Cristian Ciocaltea +Date: Wed, 1 Nov 2023 18:50:38 +0200 +Subject: [PATCH 3/9] drm/bridge: synopsys: Add initial support for DW HDMI QP + TX Controller + +Co-developed-by: Algea Cao +Signed-off-by: Algea Cao +Signed-off-by: Cristian Ciocaltea +--- + drivers/gpu/drm/bridge/synopsys/Makefile | 2 +- + drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c | 2997 ++++++++++++++++++ + drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h | 831 +++++ + drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 89 + + drivers/gpu/drm/bridge/synopsys/dw-hdmi.h | 4 + + drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 2742 +++++++++++++++- + include/drm/bridge/dw_hdmi.h | 117 + + 7 files changed, 6716 insertions(+), 66 deletions(-) + create mode 100644 drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c + create mode 100644 drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h + +diff --git a/drivers/gpu/drm/bridge/synopsys/Makefile b/drivers/gpu/drm/bridge/synopsys/Makefile +index ce715562e9e5..8354e4879f70 100644 +--- a/drivers/gpu/drm/bridge/synopsys/Makefile ++++ b/drivers/gpu/drm/bridge/synopsys/Makefile +@@ -1,5 +1,5 @@ + # SPDX-License-Identifier: GPL-2.0-only +-obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o ++obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o dw-hdmi-qp.o + obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o + obj-$(CONFIG_DRM_DW_HDMI_GP_AUDIO) += dw-hdmi-gp-audio.o + obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw-hdmi-i2s-audio.o +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c +new file mode 100644 +index 000000000000..935b116c2a3d +--- /dev/null ++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c +@@ -0,0 +1,2997 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright (C) Rockchip Electronics Co.Ltd ++ * Author: ++ * Algea Cao ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "dw-hdmi-qp.h" ++// #include "dw-hdmi-qp-audio.h" ++// #include "dw-hdmi-qp-cec.h" ++ ++#include ++ ++#define DDC_CI_ADDR 0x37 ++#define DDC_SEGMENT_ADDR 0x30 ++ ++#define HDMI_EDID_LEN 512 ++ ++/* DW-HDMI Controller >= 0x200a are at least compliant with SCDC version 1 */ ++#define SCDC_MIN_SOURCE_VERSION 0x1 ++ ++#define HDMI14_MAX_TMDSCLK 340000000 ++#define HDMI20_MAX_TMDSCLK_KHZ 600000 ++ ++static const unsigned int dw_hdmi_cable[] = { ++ EXTCON_DISP_HDMI, ++ EXTCON_NONE, ++}; ++ ++static const struct drm_display_mode dw_hdmi_default_modes[] = { ++ /* 16 - 1920x1080@60Hz 16:9 */ ++ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008, ++ 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, ++ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), ++ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, ++ /* 2 - 720x480@60Hz 4:3 */ ++ { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736, ++ 798, 858, 0, 480, 489, 495, 525, 0, ++ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), ++ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, ++ /* 4 - 1280x720@60Hz 16:9 */ ++ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390, ++ 1430, 1650, 0, 720, 725, 730, 750, 0, ++ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), ++ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, ++ /* 31 - 1920x1080@50Hz 16:9 */ ++ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448, ++ 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, ++ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), ++ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, ++ /* 19 - 1280x720@50Hz 16:9 */ ++ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720, ++ 1760, 1980, 0, 720, 725, 730, 750, 0, ++ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), ++ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, ++ /* 17 - 720x576@50Hz 4:3 */ ++ { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732, ++ 796, 864, 0, 576, 581, 586, 625, 0, ++ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), ++ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, ++ /* 2 - 720x480@60Hz 4:3 */ ++ { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736, ++ 798, 858, 0, 480, 489, 495, 525, 0, ++ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), ++ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, ++}; ++ ++enum frl_mask { ++ FRL_3GBPS_3LANE = 1, ++ FRL_6GBPS_3LANE, ++ FRL_6GBPS_4LANE, ++ FRL_8GBPS_4LANE, ++ FRL_10GBPS_4LANE, ++ FRL_12GBPS_4LANE, ++}; ++ ++struct hdmi_vmode_qp { ++ bool mdataenablepolarity; ++ ++ unsigned int previous_pixelclock; ++ unsigned long mpixelclock; ++ unsigned int mpixelrepetitioninput; ++ unsigned int mpixelrepetitionoutput; ++ unsigned long previous_tmdsclock; ++ unsigned int mtmdsclock; ++}; ++ ++struct hdmi_qp_data_info { ++ unsigned int enc_in_bus_format; ++ unsigned int enc_out_bus_format; ++ unsigned int enc_in_encoding; ++ unsigned int enc_out_encoding; ++ unsigned int quant_range; ++ unsigned int pix_repet_factor; ++ struct hdmi_vmode_qp video_mode; ++ bool update; ++}; ++ ++struct dw_hdmi_qp_i2c { ++ struct i2c_adapter adap; ++ ++ struct mutex lock; /* used to serialize data transfers */ ++ struct completion cmp; ++ u32 stat; ++ ++ u8 slave_reg; ++ bool is_regaddr; ++ bool is_segment; ++ ++ unsigned int scl_high_ns; ++ unsigned int scl_low_ns; ++}; ++ ++// struct dw_hdmi_phy_data { ++// enum dw_hdmi_phy_type type; ++// const char *name; ++// unsigned int gen; ++// bool has_svsret; ++// int (*configure)(struct dw_hdmi_qp *hdmi, ++// const struct dw_hdmi_plat_data *pdata, ++// unsigned long mpixelclock); ++// }; ++ ++struct dw_hdmi_qp { ++ struct drm_connector connector; ++ struct drm_bridge bridge; ++ struct platform_device *hdcp_dev; ++ struct platform_device *audio; ++ struct platform_device *cec; ++ struct device *dev; ++ struct dw_hdmi_qp_i2c *i2c; ++ ++ struct hdmi_qp_data_info hdmi_data; ++ const struct dw_hdmi_plat_data *plat_data; ++ ++ int vic; ++ int main_irq; ++ int avp_irq; ++ int earc_irq; ++ ++ u8 edid[HDMI_EDID_LEN]; ++ ++ struct { ++ const struct dw_hdmi_qp_phy_ops *ops; ++ const char *name; ++ void *data; ++ bool enabled; ++ } phy; ++ ++ struct drm_display_mode previous_mode; ++ ++ struct i2c_adapter *ddc; ++ void __iomem *regs; ++ bool sink_is_hdmi; ++ bool sink_has_audio; ++ bool dclk_en; ++ ++ struct mutex mutex; /* for state below and previous_mode */ ++ //[CC:] curr_conn should be removed ++ struct drm_connector *curr_conn;/* current connector (only valid when !disabled) */ ++ enum drm_connector_force force; /* mutex-protected force state */ ++ bool disabled; /* DRM has disabled our bridge */ ++ bool bridge_is_on; /* indicates the bridge is on */ ++ bool rxsense; /* rxsense state */ ++ u8 phy_mask; /* desired phy int mask settings */ ++ u8 mc_clkdis; /* clock disable register */ ++ ++ u32 scdc_intr; ++ u32 flt_intr; ++ u32 earc_intr; ++ ++ struct mutex audio_mutex; ++ unsigned int sample_rate; ++ unsigned int audio_cts; ++ unsigned int audio_n; ++ bool audio_enable; ++ void (*enable_audio)(struct dw_hdmi_qp *hdmi); ++ void (*disable_audio)(struct dw_hdmi_qp *hdmi); ++ ++ struct dentry *debugfs_dir; ++ bool scramble_low_rates; ++ ++ struct extcon_dev *extcon; ++ ++ struct regmap *regm; ++ ++ bool initialized; /* hdmi is enabled before bind */ ++ struct completion flt_cmp; ++ struct completion earc_cmp; ++ ++ struct cec_notifier *cec_notifier; ++ struct cec_adapter *cec_adap; ++ struct mutex cec_notifier_mutex; ++ ++ hdmi_codec_plugged_cb plugged_cb; ++ struct device *codec_dev; ++ enum drm_connector_status last_connector_result; ++}; ++ ++static inline void hdmi_writel(struct dw_hdmi_qp *hdmi, u32 val, int offset) ++{ ++ regmap_write(hdmi->regm, offset, val); ++} ++ ++static inline u32 hdmi_readl(struct dw_hdmi_qp *hdmi, int offset) ++{ ++ unsigned int val = 0; ++ ++ regmap_read(hdmi->regm, offset, &val); ++ ++ return val; ++} ++ ++static void handle_plugged_change(struct dw_hdmi_qp *hdmi, bool plugged) ++{ ++ if (hdmi->plugged_cb && hdmi->codec_dev) ++ hdmi->plugged_cb(hdmi->codec_dev, plugged); ++} ++ ++int dw_hdmi_qp_set_plugged_cb(struct dw_hdmi_qp *hdmi, hdmi_codec_plugged_cb fn, ++ struct device *codec_dev) ++{ ++ bool plugged; ++ ++ mutex_lock(&hdmi->mutex); ++ hdmi->plugged_cb = fn; ++ hdmi->codec_dev = codec_dev; ++ plugged = hdmi->last_connector_result == connector_status_connected; ++ handle_plugged_change(hdmi, plugged); ++ mutex_unlock(&hdmi->mutex); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_plugged_cb); ++ ++static void hdmi_modb(struct dw_hdmi_qp *hdmi, u32 data, u32 mask, u32 reg) ++{ ++ regmap_update_bits(hdmi->regm, reg, mask, data); ++} ++ ++static void hdmi_set_cts_n(struct dw_hdmi_qp *hdmi, unsigned int cts, ++ unsigned int n) ++{ ++ /* Set N */ ++ hdmi_modb(hdmi, n, AUDPKT_ACR_N_VALUE, AUDPKT_ACR_CONTROL0); ++ ++ /* Set CTS */ ++ if (cts) ++ hdmi_modb(hdmi, AUDPKT_ACR_CTS_OVR_EN, AUDPKT_ACR_CTS_OVR_EN_MSK, ++ AUDPKT_ACR_CONTROL1); ++ else ++ hdmi_modb(hdmi, 0, AUDPKT_ACR_CTS_OVR_EN_MSK, ++ AUDPKT_ACR_CONTROL1); ++ ++ hdmi_modb(hdmi, AUDPKT_ACR_CTS_OVR_VAL(cts), AUDPKT_ACR_CTS_OVR_VAL_MSK, ++ AUDPKT_ACR_CONTROL1); ++} ++ ++// static int hdmi_match_tmds_n_table(struct dw_hdmi_qp *hdmi, ++// unsigned long pixel_clk, ++// unsigned long freq) ++// { ++// const struct dw_hdmi_plat_data *plat_data = hdmi->plat_data; ++// const struct dw_hdmi_audio_tmds_n *tmds_n = NULL; ++// int i; ++// ++// if (plat_data->tmds_n_table) { ++// for (i = 0; plat_data->tmds_n_table[i].tmds != 0; i++) { ++// if (pixel_clk == plat_data->tmds_n_table[i].tmds) { ++// tmds_n = &plat_data->tmds_n_table[i]; ++// break; ++// } ++// } ++// } ++// ++// if (tmds_n == NULL) { ++// for (i = 0; common_tmds_n_table[i].tmds != 0; i++) { ++// if (pixel_clk == common_tmds_n_table[i].tmds) { ++// tmds_n = &common_tmds_n_table[i]; ++// break; ++// } ++// } ++// } ++// ++// if (tmds_n == NULL) ++// return -ENOENT; ++// ++// switch (freq) { ++// case 32000: ++// return tmds_n->n_32k; ++// case 44100: ++// case 88200: ++// case 176400: ++// return (freq / 44100) * tmds_n->n_44k1; ++// case 48000: ++// case 96000: ++// case 192000: ++// return (freq / 48000) * tmds_n->n_48k; ++// default: ++// return -ENOENT; ++// } ++// } ++// ++static u64 hdmi_audio_math_diff(unsigned int freq, unsigned int n, ++ unsigned int pixel_clk) ++{ ++ u64 final, diff; ++ u64 cts; ++ ++ final = (u64)pixel_clk * n; ++ ++ cts = final; ++ do_div(cts, 128 * freq); ++ ++ diff = final - (u64)cts * (128 * freq); ++ ++ return diff; ++} ++ ++static unsigned int hdmi_compute_n(struct dw_hdmi_qp *hdmi, ++ unsigned long pixel_clk, ++ unsigned long freq) ++{ ++ unsigned int min_n = DIV_ROUND_UP((128 * freq), 1500); ++ unsigned int max_n = (128 * freq) / 300; ++ unsigned int ideal_n = (128 * freq) / 1000; ++ unsigned int best_n_distance = ideal_n; ++ unsigned int best_n = 0; ++ u64 best_diff = U64_MAX; ++ int n; ++ ++ /* If the ideal N could satisfy the audio math, then just take it */ ++ if (hdmi_audio_math_diff(freq, ideal_n, pixel_clk) == 0) ++ return ideal_n; ++ ++ for (n = min_n; n <= max_n; n++) { ++ u64 diff = hdmi_audio_math_diff(freq, n, pixel_clk); ++ ++ if (diff < best_diff || (diff == best_diff && ++ abs(n - ideal_n) < best_n_distance)) { ++ best_n = n; ++ best_diff = diff; ++ best_n_distance = abs(best_n - ideal_n); ++ } ++ ++ /* ++ * The best N already satisfy the audio math, and also be ++ * the closest value to ideal N, so just cut the loop. ++ */ ++ if ((best_diff == 0) && (abs(n - ideal_n) > best_n_distance)) ++ break; ++ } ++ ++ return best_n; ++} ++ ++static unsigned int hdmi_find_n(struct dw_hdmi_qp *hdmi, unsigned long pixel_clk, ++ unsigned long sample_rate) ++{ ++ int n; ++ ++ // n = hdmi_match_tmds_n_table(hdmi, pixel_clk, sample_rate); ++ n = 0; ++ if (n > 0) ++ return n; ++ ++ dev_warn(hdmi->dev, "Rate %lu missing; compute N dynamically\n", ++ pixel_clk); ++ ++ return hdmi_compute_n(hdmi, pixel_clk, sample_rate); ++} ++ ++/* ++ * When transmitting IEC60958 linear PCM audio, these registers allow to ++ * configure the channel status information of all the channel status ++ * bits in the IEC60958 frame. For the moment this configuration is only ++ * used when the I2S audio interface, General Purpose Audio (GPA), ++ * or AHB audio DMA (AHBAUDDMA) interface is active ++ * (for S/PDIF interface this information comes from the stream). ++ */ ++void dw_hdmi_qp_set_channel_status(struct dw_hdmi_qp *hdmi, ++ u8 *channel_status, bool ref2stream) ++{ ++ mutex_lock(&hdmi->audio_mutex); ++ if (!hdmi->dclk_en) { ++ mutex_unlock(&hdmi->audio_mutex); ++ return; ++ } ++ ++ /* Set channel status */ ++ hdmi_writel(hdmi, channel_status[3] | (channel_status[4] << 8), ++ AUDPKT_CHSTATUS_OVR1); ++ ++ if (ref2stream) ++ hdmi_modb(hdmi, 0, ++ AUDPKT_PBIT_FORCE_EN_MASK | AUDPKT_CHSTATUS_OVR_EN_MASK, ++ AUDPKT_CONTROL0); ++ else ++ hdmi_modb(hdmi, AUDPKT_PBIT_FORCE_EN | AUDPKT_CHSTATUS_OVR_EN, ++ AUDPKT_PBIT_FORCE_EN_MASK | AUDPKT_CHSTATUS_OVR_EN_MASK, ++ AUDPKT_CONTROL0); ++ ++ mutex_unlock(&hdmi->audio_mutex); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_channel_status); ++ ++static void hdmi_set_clk_regenerator(struct dw_hdmi_qp *hdmi, ++ unsigned long pixel_clk, unsigned int sample_rate) ++{ ++ unsigned int n = 0, cts = 0; ++ ++ n = hdmi_find_n(hdmi, pixel_clk, sample_rate); ++ ++ hdmi->audio_n = n; ++ hdmi->audio_cts = cts; ++ hdmi_set_cts_n(hdmi, cts, hdmi->audio_enable ? n : 0); ++} ++ ++static void hdmi_init_clk_regenerator(struct dw_hdmi_qp *hdmi) ++{ ++ mutex_lock(&hdmi->audio_mutex); ++ if (hdmi->dclk_en) ++ hdmi_set_clk_regenerator(hdmi, 74250000, hdmi->sample_rate); ++ mutex_unlock(&hdmi->audio_mutex); ++} ++ ++static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi_qp *hdmi) ++{ ++ mutex_lock(&hdmi->audio_mutex); ++ if (hdmi->dclk_en) ++ hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock, ++ hdmi->sample_rate); ++ mutex_unlock(&hdmi->audio_mutex); ++} ++ ++void dw_hdmi_qp_set_sample_rate(struct dw_hdmi_qp *hdmi, unsigned int rate) ++{ ++ mutex_lock(&hdmi->audio_mutex); ++ if (hdmi->dclk_en) { ++ hdmi->sample_rate = rate; ++ hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock, ++ hdmi->sample_rate); ++ } ++ mutex_unlock(&hdmi->audio_mutex); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_sample_rate); ++ ++void dw_hdmi_qp_set_channel_count(struct dw_hdmi_qp *hdmi, unsigned int cnt) ++{ ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_channel_count); ++ ++void dw_hdmi_qp_set_channel_allocation(struct dw_hdmi_qp *hdmi, unsigned int ca) ++{ ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_channel_allocation); ++ ++void dw_hdmi_qp_set_audio_infoframe(struct dw_hdmi_qp *hdmi, ++ struct hdmi_codec_params *hparms) ++{ ++ u8 infoframe_buf[HDMI_INFOFRAME_SIZE(AUDIO)]; ++ int ret = 0; ++ ++ ret = hdmi_audio_infoframe_pack(&hparms->cea, infoframe_buf, ++ sizeof(infoframe_buf)); ++ if (!ret) { ++ dev_err(hdmi->dev, "%s: Failed to pack audio infoframe: %d\n", ++ __func__, ret); ++ return; ++ } ++ ++ mutex_lock(&hdmi->audio_mutex); ++ if (!hdmi->dclk_en) { ++ mutex_unlock(&hdmi->audio_mutex); ++ return; ++ } ++ ++ /* ++ * AUDI_CONTENTS0: { RSV, HB2, HB1, RSV } ++ * AUDI_CONTENTS1: { PB3, PB2, PB1, PB0 } ++ * AUDI_CONTENTS2: { PB7, PB6, PB5, PB4 } ++ * ++ * PB0: CheckSum ++ * PB1: | CT3 | CT2 | CT1 | CT0 | F13 | CC2 | CC1 | CC0 | ++ * PB2: | F27 | F26 | F25 | SF2 | SF1 | SF0 | SS1 | SS0 | ++ * PB3: | F37 | F36 | F35 | F34 | F33 | F32 | F31 | F30 | ++ * PB4: | CA7 | CA6 | CA5 | CA4 | CA3 | CA2 | CA1 | CA0 | ++ * PB5: | DM_INH | LSV3 | LSV2 | LSV1 | LSV0 | F52 | F51 | F50 | ++ * PB6~PB10: Reserved ++ * ++ * AUDI_CONTENTS0 default value defined by HDMI specification, ++ * and shall only be changed for debug purposes. ++ * So, we only configure payload byte from PB0~PB7(2 word total). ++ */ ++ regmap_bulk_write(hdmi->regm, PKT_AUDI_CONTENTS1, &infoframe_buf[3], 2); ++ ++ /* Enable ACR, AUDI, AMD */ ++ hdmi_modb(hdmi, ++ PKTSCHED_ACR_TX_EN | PKTSCHED_AUDI_TX_EN | PKTSCHED_AMD_TX_EN, ++ PKTSCHED_ACR_TX_EN | PKTSCHED_AUDI_TX_EN | PKTSCHED_AMD_TX_EN, ++ PKTSCHED_PKT_EN); ++ ++ /* Enable AUDS */ ++ hdmi_modb(hdmi, PKTSCHED_AUDS_TX_EN, PKTSCHED_AUDS_TX_EN, PKTSCHED_PKT_EN); ++ mutex_unlock(&hdmi->audio_mutex); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_audio_infoframe); ++ ++static void hdmi_enable_audio_clk(struct dw_hdmi_qp *hdmi, bool enable) ++{ ++ if (enable) ++ hdmi_modb(hdmi, 0, ++ AVP_DATAPATH_PACKET_AUDIO_SWDISABLE, GLOBAL_SWDISABLE); ++ else ++ hdmi_modb(hdmi, AVP_DATAPATH_PACKET_AUDIO_SWDISABLE, ++ AVP_DATAPATH_PACKET_AUDIO_SWDISABLE, GLOBAL_SWDISABLE); ++} ++ ++// static void dw_hdmi_i2s_audio_enable(struct dw_hdmi_qp *hdmi) ++// { ++// hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n); ++// hdmi_enable_audio_clk(hdmi, true); ++// } ++// ++// static void dw_hdmi_i2s_audio_disable(struct dw_hdmi_qp *hdmi) ++// { ++// /* Disable AUDS, ACR, AUDI, AMD */ ++// hdmi_modb(hdmi, 0, ++// PKTSCHED_ACR_TX_EN | PKTSCHED_AUDS_TX_EN | ++// PKTSCHED_AUDI_TX_EN | PKTSCHED_AMD_TX_EN, ++// PKTSCHED_PKT_EN); ++// ++// hdmi_enable_audio_clk(hdmi, false); ++// } ++ ++void dw_hdmi_qp_audio_enable(struct dw_hdmi_qp *hdmi) ++{ ++ mutex_lock(&hdmi->audio_mutex); ++ if (hdmi->dclk_en) { ++ hdmi->audio_enable = true; ++ if (hdmi->enable_audio) ++ hdmi->enable_audio(hdmi); ++ } ++ mutex_unlock(&hdmi->audio_mutex); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_audio_enable); ++ ++void dw_hdmi_qp_audio_disable(struct dw_hdmi_qp *hdmi) ++{ ++ mutex_lock(&hdmi->audio_mutex); ++ if (hdmi->dclk_en) { ++ hdmi->audio_enable = false; ++ if (hdmi->disable_audio) ++ hdmi->disable_audio(hdmi); ++ } ++ mutex_unlock(&hdmi->audio_mutex); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_audio_disable); ++ ++static bool hdmi_bus_fmt_is_rgb(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_RGB888_1X24: ++ case MEDIA_BUS_FMT_RGB101010_1X30: ++ case MEDIA_BUS_FMT_RGB121212_1X36: ++ case MEDIA_BUS_FMT_RGB161616_1X48: ++ return true; ++ ++ default: ++ return false; ++ } ++} ++ ++static bool hdmi_bus_fmt_is_yuv444(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_YUV8_1X24: ++ case MEDIA_BUS_FMT_YUV10_1X30: ++ case MEDIA_BUS_FMT_YUV12_1X36: ++ case MEDIA_BUS_FMT_YUV16_1X48: ++ return true; ++ ++ default: ++ return false; ++ } ++} ++ ++static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_UYVY8_1X16: ++ case MEDIA_BUS_FMT_UYVY10_1X20: ++ case MEDIA_BUS_FMT_UYVY12_1X24: ++ return true; ++ ++ default: ++ return false; ++ } ++} ++ ++static bool hdmi_bus_fmt_is_yuv420(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: ++ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: ++ case MEDIA_BUS_FMT_UYYVYY12_0_5X36: ++ case MEDIA_BUS_FMT_UYYVYY16_0_5X48: ++ return true; ++ ++ default: ++ return false; ++ } ++} ++ ++static int hdmi_bus_fmt_color_depth(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_RGB888_1X24: ++ case MEDIA_BUS_FMT_YUV8_1X24: ++ case MEDIA_BUS_FMT_UYVY8_1X16: ++ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: ++ return 8; ++ ++ case MEDIA_BUS_FMT_RGB101010_1X30: ++ case MEDIA_BUS_FMT_YUV10_1X30: ++ case MEDIA_BUS_FMT_UYVY10_1X20: ++ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: ++ return 10; ++ ++ case MEDIA_BUS_FMT_RGB121212_1X36: ++ case MEDIA_BUS_FMT_YUV12_1X36: ++ case MEDIA_BUS_FMT_UYVY12_1X24: ++ case MEDIA_BUS_FMT_UYYVYY12_0_5X36: ++ return 12; ++ ++ case MEDIA_BUS_FMT_RGB161616_1X48: ++ case MEDIA_BUS_FMT_YUV16_1X48: ++ case MEDIA_BUS_FMT_UYYVYY16_0_5X48: ++ return 16; ++ ++ default: ++ return 0; ++ } ++} ++ ++static void dw_hdmi_i2c_init(struct dw_hdmi_qp *hdmi) ++{ ++ /* Software reset */ ++ hdmi_writel(hdmi, 0x01, I2CM_CONTROL0); ++ ++ hdmi_writel(hdmi, 0x085c085c, I2CM_FM_SCL_CONFIG0); ++ ++ hdmi_modb(hdmi, 0, I2CM_FM_EN, I2CM_INTERFACE_CONTROL0); ++ ++ /* Clear DONE and ERROR interrupts */ ++ hdmi_writel(hdmi, I2CM_OP_DONE_CLEAR | I2CM_NACK_RCVD_CLEAR, ++ MAINUNIT_1_INT_CLEAR); ++} ++ ++static int dw_hdmi_i2c_read(struct dw_hdmi_qp *hdmi, ++ unsigned char *buf, unsigned int length) ++{ ++ struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; ++ int stat; ++ ++ if (!i2c->is_regaddr) { ++ dev_dbg(hdmi->dev, "set read register address to 0\n"); ++ i2c->slave_reg = 0x00; ++ i2c->is_regaddr = true; ++ } ++ ++ while (length--) { ++ reinit_completion(&i2c->cmp); ++ ++ hdmi_modb(hdmi, i2c->slave_reg++ << 12, I2CM_ADDR, ++ I2CM_INTERFACE_CONTROL0); ++ ++ hdmi_modb(hdmi, I2CM_FM_READ, I2CM_WR_MASK, ++ I2CM_INTERFACE_CONTROL0); ++ ++ stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10); ++ if (!stat) { ++ dev_err(hdmi->dev, "i2c read time out!\n"); ++ hdmi_writel(hdmi, 0x01, I2CM_CONTROL0); ++ return -EAGAIN; ++ } ++ ++ /* Check for error condition on the bus */ ++ if (i2c->stat & I2CM_NACK_RCVD_IRQ) { ++ dev_err(hdmi->dev, "i2c read err!\n"); ++ hdmi_writel(hdmi, 0x01, I2CM_CONTROL0); ++ return -EIO; ++ } ++ ++ *buf++ = hdmi_readl(hdmi, I2CM_INTERFACE_RDDATA_0_3) & 0xff; ++ // dev_dbg(hdmi->dev, "i2c read done! i2c->stat:%02x 0x%02x\n", ++ // i2c->stat, hdmi_readl(hdmi, I2CM_INTERFACE_RDDATA_0_3)); ++ hdmi_modb(hdmi, 0, I2CM_WR_MASK, I2CM_INTERFACE_CONTROL0); ++ } ++ i2c->is_segment = false; ++ ++ return 0; ++} ++ ++static int dw_hdmi_i2c_write(struct dw_hdmi_qp *hdmi, ++ unsigned char *buf, unsigned int length) ++{ ++ struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; ++ int stat; ++ ++ if (!i2c->is_regaddr) { ++ /* Use the first write byte as register address */ ++ i2c->slave_reg = buf[0]; ++ length--; ++ buf++; ++ i2c->is_regaddr = true; ++ } ++ ++ while (length--) { ++ reinit_completion(&i2c->cmp); ++ ++ hdmi_writel(hdmi, *buf++, I2CM_INTERFACE_WRDATA_0_3); ++ hdmi_modb(hdmi, i2c->slave_reg++ << 12, I2CM_ADDR, ++ I2CM_INTERFACE_CONTROL0); ++ hdmi_modb(hdmi, I2CM_FM_WRITE, I2CM_WR_MASK, ++ I2CM_INTERFACE_CONTROL0); ++ ++ stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10); ++ if (!stat) { ++ dev_err(hdmi->dev, "i2c write time out!\n"); ++ hdmi_writel(hdmi, 0x01, I2CM_CONTROL0); ++ return -EAGAIN; ++ } ++ ++ /* Check for error condition on the bus */ ++ if (i2c->stat & I2CM_NACK_RCVD_IRQ) { ++ dev_err(hdmi->dev, "i2c write nack!\n"); ++ hdmi_writel(hdmi, 0x01, I2CM_CONTROL0); ++ return -EIO; ++ } ++ hdmi_modb(hdmi, 0, I2CM_WR_MASK, I2CM_INTERFACE_CONTROL0); ++ } ++ // dev_dbg(hdmi->dev, "i2c write done!\n"); ++ return 0; ++} ++ ++static int dw_hdmi_i2c_xfer(struct i2c_adapter *adap, ++ struct i2c_msg *msgs, int num) ++{ ++ struct dw_hdmi_qp *hdmi = i2c_get_adapdata(adap); ++ struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; ++ u8 addr = msgs[0].addr; ++ int i, ret = 0; ++ ++ if (addr == DDC_CI_ADDR) ++ /* ++ * The internal I2C controller does not support the multi-byte ++ * read and write operations needed for DDC/CI. ++ * TOFIX: Blacklist the DDC/CI address until we filter out ++ * unsupported I2C operations. ++ */ ++ return -EOPNOTSUPP; ++ ++ // dev_dbg(hdmi->dev, "i2c xfer: num: %d, addr: %#x\n", num, addr); ++ ++ for (i = 0; i < num; i++) { ++ if (msgs[i].len == 0) { ++ dev_err(hdmi->dev, ++ "unsupported transfer %d/%d, no data\n", ++ i + 1, num); ++ return -EOPNOTSUPP; ++ } ++ } ++ ++ mutex_lock(&i2c->lock); ++ ++ /* Unmute DONE and ERROR interrupts */ ++ hdmi_modb(hdmi, I2CM_NACK_RCVD_MASK_N | I2CM_OP_DONE_MASK_N, ++ I2CM_NACK_RCVD_MASK_N | I2CM_OP_DONE_MASK_N, ++ MAINUNIT_1_INT_MASK_N); ++ ++ /* Set slave device address taken from the first I2C message */ ++ if (addr == DDC_SEGMENT_ADDR && msgs[0].len == 1) ++ addr = DDC_ADDR; ++ ++ hdmi_modb(hdmi, addr << 5, I2CM_SLVADDR, I2CM_INTERFACE_CONTROL0); ++ ++ /* Set slave device register address on transfer */ ++ i2c->is_regaddr = false; ++ ++ /* Set segment pointer for I2C extended read mode operation */ ++ i2c->is_segment = false; ++ ++ for (i = 0; i < num; i++) { ++ // dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n", ++ // i + 1, num, msgs[i].len, msgs[i].flags); ++ ++ if (msgs[i].addr == DDC_SEGMENT_ADDR && msgs[i].len == 1) { ++ i2c->is_segment = true; ++ hdmi_modb(hdmi, DDC_SEGMENT_ADDR, I2CM_SEG_ADDR, ++ I2CM_INTERFACE_CONTROL1); ++ hdmi_modb(hdmi, *msgs[i].buf, I2CM_SEG_PTR, ++ I2CM_INTERFACE_CONTROL1); ++ } else { ++ if (msgs[i].flags & I2C_M_RD) ++ ret = dw_hdmi_i2c_read(hdmi, msgs[i].buf, ++ msgs[i].len); ++ else ++ ret = dw_hdmi_i2c_write(hdmi, msgs[i].buf, ++ msgs[i].len); ++ } ++ if (ret < 0) ++ break; ++ } ++ ++ if (!ret) ++ ret = num; ++ ++ /* Mute DONE and ERROR interrupts */ ++ hdmi_modb(hdmi, 0, I2CM_OP_DONE_MASK_N | I2CM_NACK_RCVD_MASK_N, ++ MAINUNIT_1_INT_MASK_N); ++ ++ mutex_unlock(&i2c->lock); ++ ++ return ret; ++} ++ ++static u32 dw_hdmi_i2c_func(struct i2c_adapter *adapter) ++{ ++ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; ++} ++ ++static const struct i2c_algorithm dw_hdmi_algorithm = { ++ .master_xfer = dw_hdmi_i2c_xfer, ++ .functionality = dw_hdmi_i2c_func, ++}; ++ ++static struct i2c_adapter *dw_hdmi_i2c_adapter(struct dw_hdmi_qp *hdmi) ++{ ++ struct i2c_adapter *adap; ++ struct dw_hdmi_qp_i2c *i2c; ++ int ret; ++ ++ i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL); ++ if (!i2c) ++ return ERR_PTR(-ENOMEM); ++ ++ mutex_init(&i2c->lock); ++ init_completion(&i2c->cmp); ++ ++ adap = &i2c->adap; ++ adap->class = I2C_CLASS_DDC; ++ adap->owner = THIS_MODULE; ++ adap->dev.parent = hdmi->dev; ++ adap->algo = &dw_hdmi_algorithm; ++ strscpy(adap->name, "ddc", sizeof(adap->name)); ++ i2c_set_adapdata(adap, hdmi); ++ ++ ret = i2c_add_adapter(adap); ++ if (ret) { ++ dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name); ++ devm_kfree(hdmi->dev, i2c); ++ return ERR_PTR(ret); ++ } ++ ++ hdmi->i2c = i2c; ++ ++ dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name); ++ ++ return adap; ++} ++ ++#define HDMI_PHY_EARC_MASK BIT(29) ++ ++int dw_hdmi_qp_set_earc(struct dw_hdmi_qp *hdmi) ++{ ++ u32 stat, ret; ++ ++ /* set hdmi phy earc mode */ ++ hdmi->phy.ops->set_mode(hdmi, hdmi->phy.data, HDMI_PHY_EARC_MASK, ++ true); ++ ++ ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data, ++ &hdmi->previous_mode); ++ if (ret) ++ return ret; ++ ++ reinit_completion(&hdmi->earc_cmp); ++ ++ hdmi_modb(hdmi, EARCRX_CMDC_DISCOVERY_TIMEOUT_IRQ | ++ EARCRX_CMDC_DISCOVERY_DONE_IRQ, ++ EARCRX_CMDC_DISCOVERY_TIMEOUT_IRQ | ++ EARCRX_CMDC_DISCOVERY_DONE_IRQ, EARCRX_0_INT_MASK_N); ++ ++ /* start discovery */ ++ hdmi_modb(hdmi, EARCRX_CMDC_DISCOVERY_EN, EARCRX_CMDC_DISCOVERY_EN, ++ EARCRX_CMDC_CONTROL); ++ ++ /* ++ * The eARC TX device drives a logic-high-voltage-level ++ * pulse on the physical HPD connector pin, after ++ * at least 100 ms of low voltage level to start the ++ * eARC Discovery process. ++ */ ++ hdmi_modb(hdmi, EARCRX_CONNECTOR_HPD, EARCRX_CONNECTOR_HPD, ++ EARCRX_CMDC_CONTROL); ++ ++ stat = wait_for_completion_timeout(&hdmi->earc_cmp, HZ / 10); ++ if (!stat) ++ return -EAGAIN; ++ ++ if (hdmi->earc_intr & EARCRX_CMDC_DISCOVERY_TIMEOUT_IRQ) { ++ dev_err(hdmi->dev, "discovery timeout\n"); ++ return -ETIMEDOUT; ++ } else if (hdmi->earc_intr & EARCRX_CMDC_DISCOVERY_DONE_IRQ) { ++ dev_info(hdmi->dev, "discovery done\n"); ++ } else { ++ dev_err(hdmi->dev, "discovery failed\n"); ++ return -EINVAL; ++ } ++ ++ hdmi_writel(hdmi, 1, EARCRX_DMAC_PHY_CONTROL); ++ hdmi_modb(hdmi, EARCRX_CMDC_SWINIT_P, EARCRX_CMDC_SWINIT_P, ++ EARCRX_CMDC_CONFIG0); ++ ++ hdmi_writel(hdmi, 0xf3, EARCRX_DMAC_CONFIG); ++ hdmi_writel(hdmi, 0x63, EARCRX_DMAC_CONTROL0); ++ hdmi_writel(hdmi, 0xff, EARCRX_DMAC_CONTROL1); ++ ++ hdmi_modb(hdmi, EARCRX_XACTREAD_STOP_CFG | EARCRX_XACTREAD_RETRY_CFG | ++ EARCRX_CMDC_DSCVR_EARCVALID0_TO_DISC1 | EARCRX_CMDC_XACT_RESTART_EN, ++ EARCRX_XACTREAD_STOP_CFG | EARCRX_XACTREAD_RETRY_CFG | ++ EARCRX_CMDC_DSCVR_EARCVALID0_TO_DISC1 | EARCRX_CMDC_XACT_RESTART_EN, ++ EARCRX_CMDC_CONFIG0); ++ ++ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER0); ++ hdmi_writel(hdmi, 0x1b0e, EARCRX_DMAC_CHSTATUS_STREAMER1); ++ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER2); ++ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER3); ++ hdmi_writel(hdmi, 0xf2000000, EARCRX_DMAC_CHSTATUS_STREAMER4); ++ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER5); ++ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER6); ++ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER7); ++ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER8); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_earc); ++ ++/* ----------------------------------------------------------------------------- ++ * HDMI TX Setup ++ */ ++ ++static void hdmi_infoframe_set_checksum(u8 *ptr, int size) ++{ ++ u8 csum = 0; ++ int i; ++ ++ ptr[3] = 0; ++ /* compute checksum */ ++ for (i = 0; i < size; i++) ++ csum += ptr[i]; ++ ++ ptr[3] = 256 - csum; ++} ++ ++static void hdmi_config_AVI(struct dw_hdmi_qp *hdmi, ++ const struct drm_connector *connector, ++ const struct drm_display_mode *mode) ++{ ++ struct hdmi_avi_infoframe frame; ++ u32 val, i, j; ++ u8 buff[17]; ++ enum hdmi_quantization_range rgb_quant_range = ++ hdmi->hdmi_data.quant_range; ++ ++ /* Initialise info frame from DRM mode */ ++ drm_hdmi_avi_infoframe_from_display_mode(&frame, connector, mode); ++ ++ /* ++ * Ignore monitor selectable quantization, use quantization set ++ * by the user ++ */ ++ drm_hdmi_avi_infoframe_quant_range(&frame, connector, mode, rgb_quant_range); ++ if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format)) ++ frame.colorspace = HDMI_COLORSPACE_YUV444; ++ else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) ++ frame.colorspace = HDMI_COLORSPACE_YUV422; ++ else if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) ++ frame.colorspace = HDMI_COLORSPACE_YUV420; ++ else ++ frame.colorspace = HDMI_COLORSPACE_RGB; ++ ++ /* Set up colorimetry */ ++ if (!hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) { ++ switch (hdmi->hdmi_data.enc_out_encoding) { ++ case V4L2_YCBCR_ENC_601: ++ if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_XV601) ++ frame.colorimetry = HDMI_COLORIMETRY_EXTENDED; ++ else ++ frame.colorimetry = HDMI_COLORIMETRY_ITU_601; ++ frame.extended_colorimetry = ++ HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; ++ break; ++ case V4L2_YCBCR_ENC_709: ++ if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_XV709) ++ frame.colorimetry = HDMI_COLORIMETRY_EXTENDED; ++ else ++ frame.colorimetry = HDMI_COLORIMETRY_ITU_709; ++ frame.extended_colorimetry = ++ HDMI_EXTENDED_COLORIMETRY_XV_YCC_709; ++ break; ++ case V4L2_YCBCR_ENC_BT2020: ++ if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_BT2020) ++ frame.colorimetry = HDMI_COLORIMETRY_EXTENDED; ++ else ++ frame.colorimetry = HDMI_COLORIMETRY_ITU_709; ++ frame.extended_colorimetry = ++ HDMI_EXTENDED_COLORIMETRY_BT2020; ++ break; ++ default: /* Carries no data */ ++ frame.colorimetry = HDMI_COLORIMETRY_ITU_601; ++ frame.extended_colorimetry = ++ HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; ++ break; ++ } ++ } else { ++ if (hdmi->hdmi_data.enc_out_encoding == V4L2_YCBCR_ENC_BT2020) { ++ frame.colorimetry = HDMI_COLORIMETRY_EXTENDED; ++ frame.extended_colorimetry = ++ HDMI_EXTENDED_COLORIMETRY_BT2020; ++ } else { ++ frame.colorimetry = HDMI_COLORIMETRY_NONE; ++ frame.extended_colorimetry = ++ HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; ++ } ++ } ++ ++ frame.scan_mode = HDMI_SCAN_MODE_NONE; ++ frame.video_code = hdmi->vic; ++ ++ hdmi_avi_infoframe_pack_only(&frame, buff, 17); ++ ++ /* mode which vic >= 128 must use avi version 3 */ ++ if (hdmi->vic >= 128) { ++ frame.version = 3; ++ buff[1] = frame.version; ++ buff[4] &= 0x1f; ++ buff[4] |= ((frame.colorspace & 0x7) << 5); ++ buff[7] = frame.video_code; ++ hdmi_infoframe_set_checksum(buff, 17); ++ } ++ ++ /* ++ * The Designware IP uses a different byte format from standard ++ * AVI info frames, though generally the bits are in the correct ++ * bytes. ++ */ ++ ++ val = (frame.version << 8) | (frame.length << 16); ++ hdmi_writel(hdmi, val, PKT_AVI_CONTENTS0); ++ ++ for (i = 0; i < 4; i++) { ++ for (j = 0; j < 4; j++) { ++ if (i * 4 + j >= 14) ++ break; ++ if (!j) ++ val = buff[i * 4 + j + 3]; ++ val |= buff[i * 4 + j + 3] << (8 * j); ++ } ++ ++ hdmi_writel(hdmi, val, PKT_AVI_CONTENTS1 + i * 4); ++ } ++ ++ hdmi_modb(hdmi, 0, PKTSCHED_AVI_FIELDRATE, PKTSCHED_PKT_CONFIG1); ++ ++ hdmi_modb(hdmi, PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN, ++ PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN, ++ PKTSCHED_PKT_EN); ++} ++ ++static void hdmi_config_CVTEM(struct dw_hdmi_qp *hdmi) ++{ ++ u8 ds_type = 0; ++ u8 sync = 1; ++ u8 vfr = 1; ++ u8 afr = 0; ++ u8 new = 1; ++ u8 end = 0; ++ u8 data_set_length = 136; ++ u8 hb1[6] = { 0x80, 0, 0, 0, 0, 0x40 }; ++ u8 *pps_body; ++ u32 val, i, reg; ++ struct drm_display_mode *mode = &hdmi->previous_mode; ++ int hsync, hfront, hback; ++ struct dw_hdmi_link_config *link_cfg; ++ void *data = hdmi->plat_data->phy_data; ++ ++ hdmi_modb(hdmi, 0, PKTSCHED_EMP_CVTEM_TX_EN, PKTSCHED_PKT_EN); ++ ++ if (hdmi->plat_data->get_link_cfg) { ++ link_cfg = hdmi->plat_data->get_link_cfg(data); ++ } else { ++ dev_err(hdmi->dev, "can't get frl link cfg\n"); ++ return; ++ } ++ ++ if (!link_cfg->dsc_mode) { ++ dev_info(hdmi->dev, "don't use dsc mode\n"); ++ return; ++ } ++ ++ pps_body = link_cfg->pps_payload; ++ ++ hsync = mode->hsync_end - mode->hsync_start; ++ hback = mode->htotal - mode->hsync_end; ++ hfront = mode->hsync_start - mode->hdisplay; ++ ++ for (i = 0; i < 6; i++) { ++ val = i << 16 | hb1[i] << 8; ++ hdmi_writel(hdmi, val, PKT0_EMP_CVTEM_CONTENTS0 + i * 0x20); ++ } ++ ++ val = new << 7 | end << 6 | ds_type << 4 | afr << 3 | ++ vfr << 2 | sync << 1; ++ hdmi_writel(hdmi, val, PKT0_EMP_CVTEM_CONTENTS1); ++ ++ val = data_set_length << 16 | pps_body[0] << 24; ++ hdmi_writel(hdmi, val, PKT0_EMP_CVTEM_CONTENTS2); ++ ++ reg = PKT0_EMP_CVTEM_CONTENTS3; ++ for (i = 1; i < 125; i++) { ++ if (reg == PKT1_EMP_CVTEM_CONTENTS0 || ++ reg == PKT2_EMP_CVTEM_CONTENTS0 || ++ reg == PKT3_EMP_CVTEM_CONTENTS0 || ++ reg == PKT4_EMP_CVTEM_CONTENTS0 || ++ reg == PKT5_EMP_CVTEM_CONTENTS0) { ++ reg += 4; ++ i--; ++ continue; ++ } ++ if (i % 4 == 1) ++ val = pps_body[i]; ++ if (i % 4 == 2) ++ val |= pps_body[i] << 8; ++ if (i % 4 == 3) ++ val |= pps_body[i] << 16; ++ if (!(i % 4)) { ++ val |= pps_body[i] << 24; ++ hdmi_writel(hdmi, val, reg); ++ reg += 4; ++ } ++ } ++ ++ val = (hfront & 0xff) << 24 | pps_body[127] << 16 | ++ pps_body[126] << 8 | pps_body[125]; ++ hdmi_writel(hdmi, val, PKT4_EMP_CVTEM_CONTENTS6); ++ ++ val = (hback & 0xff) << 24 | ((hsync >> 8) & 0xff) << 16 | ++ (hsync & 0xff) << 8 | ((hfront >> 8) & 0xff); ++ hdmi_writel(hdmi, val, PKT4_EMP_CVTEM_CONTENTS7); ++ ++ val = link_cfg->hcactive << 8 | ((hback >> 8) & 0xff); ++ hdmi_writel(hdmi, val, PKT5_EMP_CVTEM_CONTENTS1); ++ ++ for (i = PKT5_EMP_CVTEM_CONTENTS2; i <= PKT5_EMP_CVTEM_CONTENTS7; i += 4) ++ hdmi_writel(hdmi, 0, i); ++ ++ hdmi_modb(hdmi, PKTSCHED_EMP_CVTEM_TX_EN, PKTSCHED_EMP_CVTEM_TX_EN, ++ PKTSCHED_PKT_EN); ++} ++ ++static void hdmi_config_drm_infoframe(struct dw_hdmi_qp *hdmi, ++ const struct drm_connector *connector) ++{ ++ const struct drm_connector_state *conn_state = connector->state; ++ struct hdr_output_metadata *hdr_metadata; ++ struct hdmi_drm_infoframe frame; ++ u8 buffer[30]; ++ ssize_t err; ++ int i; ++ u32 val; ++ ++ if (!hdmi->plat_data->use_drm_infoframe) ++ return; ++ ++ hdmi_modb(hdmi, 0, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN); ++ ++ if (!hdmi->connector.hdr_sink_metadata.hdmi_type1.eotf) { ++ DRM_DEBUG("No need to set HDR metadata in infoframe\n"); ++ return; ++ } ++ ++ if (!conn_state->hdr_output_metadata) { ++ DRM_DEBUG("source metadata not set yet\n"); ++ return; ++ } ++ ++ hdr_metadata = (struct hdr_output_metadata *) ++ conn_state->hdr_output_metadata->data; ++ ++ if (!(hdmi->connector.hdr_sink_metadata.hdmi_type1.eotf & ++ BIT(hdr_metadata->hdmi_metadata_type1.eotf))) { ++ DRM_ERROR("Not support EOTF %d\n", ++ hdr_metadata->hdmi_metadata_type1.eotf); ++ return; ++ } ++ ++ err = drm_hdmi_infoframe_set_hdr_metadata(&frame, conn_state); ++ if (err < 0) ++ return; ++ ++ err = hdmi_drm_infoframe_pack(&frame, buffer, sizeof(buffer)); ++ if (err < 0) { ++ dev_err(hdmi->dev, "Failed to pack drm infoframe: %zd\n", err); ++ return; ++ } ++ ++ val = (frame.version << 8) | (frame.length << 16); ++ hdmi_writel(hdmi, val, PKT_DRMI_CONTENTS0); ++ ++ for (i = 0; i <= frame.length; i++) { ++ if (i % 4 == 0) ++ val = buffer[3 + i]; ++ val |= buffer[3 + i] << ((i % 4) * 8); ++ ++ if (i % 4 == 3 || (i == (frame.length))) ++ hdmi_writel(hdmi, val, PKT_DRMI_CONTENTS1 + ((i / 4) * 4)); ++ } ++ ++ hdmi_modb(hdmi, 0, PKTSCHED_DRMI_FIELDRATE, PKTSCHED_PKT_CONFIG1); ++ ++ hdmi_modb(hdmi, PKTSCHED_DRMI_TX_EN, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN); ++ ++ DRM_DEBUG("%s eotf %d end\n", __func__, ++ hdr_metadata->hdmi_metadata_type1.eotf); ++} ++ ++/* Filter out invalid setups to avoid configuring SCDC and scrambling */ ++static bool dw_hdmi_support_scdc(struct dw_hdmi_qp *hdmi, ++ const struct drm_display_info *display) ++{ ++ /* Disable if no DDC bus */ ++ if (!hdmi->ddc) ++ return false; ++ ++ /* Disable if SCDC is not supported, or if an HF-VSDB block is absent */ ++ if (!display->hdmi.scdc.supported || ++ !display->hdmi.scdc.scrambling.supported) ++ return false; ++ ++ /* ++ * Disable if display only support low TMDS rates and scrambling ++ * for low rates is not supported either ++ */ ++ if (!display->hdmi.scdc.scrambling.low_rates && ++ display->max_tmds_clock <= 340000) ++ return false; ++ ++ return true; ++} ++ ++static int hdmi_set_frl_mask(int frl_rate) ++{ ++ switch (frl_rate) { ++ case 48: ++ return FRL_12GBPS_4LANE; ++ case 40: ++ return FRL_10GBPS_4LANE; ++ case 32: ++ return FRL_8GBPS_4LANE; ++ case 24: ++ return FRL_6GBPS_4LANE; ++ case 18: ++ return FRL_6GBPS_3LANE; ++ case 9: ++ return FRL_3GBPS_3LANE; ++ } ++ ++ return 0; ++} ++ ++static int hdmi_start_flt(struct dw_hdmi_qp *hdmi, u8 rate) ++{ ++ u8 val; ++ u8 ffe_lv = 0; ++ int i = 0, stat; ++ ++ /* FLT_READY & FFE_LEVELS read */ ++ for (i = 0; i < 20; i++) { ++ drm_scdc_readb(hdmi->ddc, SCDC_STATUS_FLAGS_0, &val); ++ if (val & BIT(6)) ++ break; ++ msleep(20); ++ } ++ ++ if (i == 20) { ++ dev_err(hdmi->dev, "sink flt isn't ready\n"); ++ return -EINVAL; ++ } ++ ++ hdmi_modb(hdmi, SCDC_UPD_FLAGS_RD_IRQ, SCDC_UPD_FLAGS_RD_IRQ, ++ MAINUNIT_1_INT_MASK_N); ++ hdmi_modb(hdmi, SCDC_UPD_FLAGS_POLL_EN | SCDC_UPD_FLAGS_AUTO_CLR, ++ SCDC_UPD_FLAGS_POLL_EN | SCDC_UPD_FLAGS_AUTO_CLR, ++ SCDC_CONFIG0); ++ ++ /* max ffe level 3 */ ++ val = 3 << 4 | hdmi_set_frl_mask(rate); ++ drm_scdc_writeb(hdmi->ddc, 0x31, val); ++ ++ /* select FRL_RATE & FFE_LEVELS */ ++ hdmi_writel(hdmi, ffe_lv, FLT_CONFIG0); ++ ++ /* Start LTS_3 state in source DUT */ ++ reinit_completion(&hdmi->flt_cmp); ++ hdmi_modb(hdmi, FLT_EXIT_TO_LTSP_IRQ, FLT_EXIT_TO_LTSP_IRQ, ++ MAINUNIT_1_INT_MASK_N); ++ hdmi_writel(hdmi, 1, FLT_CONTROL0); ++ ++ /* wait for completed link training at source side */ ++ stat = wait_for_completion_timeout(&hdmi->flt_cmp, HZ * 2); ++ if (!stat) { ++ dev_err(hdmi->dev, "wait lts3 finish time out\n"); ++ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_POLL_EN | ++ SCDC_UPD_FLAGS_AUTO_CLR, SCDC_CONFIG0); ++ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_RD_IRQ, ++ MAINUNIT_1_INT_MASK_N); ++ return -EAGAIN; ++ } ++ ++ if (!(hdmi->flt_intr & FLT_EXIT_TO_LTSP_IRQ)) { ++ dev_err(hdmi->dev, "not to ltsp\n"); ++ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_POLL_EN | ++ SCDC_UPD_FLAGS_AUTO_CLR, SCDC_CONFIG0); ++ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_RD_IRQ, ++ MAINUNIT_1_INT_MASK_N); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++#define HDMI_MODE_FRL_MASK BIT(30) ++ ++static void hdmi_set_op_mode(struct dw_hdmi_qp *hdmi, ++ struct dw_hdmi_link_config *link_cfg, ++ const struct drm_connector *connector) ++{ ++ int frl_rate; ++ int i; ++ ++ /* set sink frl mode disable and wait sink ready */ ++ hdmi_writel(hdmi, 0, FLT_CONFIG0); ++ if (dw_hdmi_support_scdc(hdmi, &connector->display_info)) ++ drm_scdc_writeb(hdmi->ddc, 0x31, 0); ++ /* ++ * some TVs must wait a while before switching frl mode resolution, ++ * or the signal may not be recognized. ++ */ ++ msleep(200); ++ ++ if (!link_cfg->frl_mode) { ++ dev_info(hdmi->dev, "dw hdmi qp use tmds mode\n"); ++ hdmi_modb(hdmi, 0, OPMODE_FRL, LINK_CONFIG0); ++ hdmi_modb(hdmi, 0, OPMODE_FRL_4LANES, LINK_CONFIG0); ++ return; ++ } ++ ++ if (link_cfg->frl_lanes == 4) ++ hdmi_modb(hdmi, OPMODE_FRL_4LANES, OPMODE_FRL_4LANES, ++ LINK_CONFIG0); ++ else ++ hdmi_modb(hdmi, 0, OPMODE_FRL_4LANES, LINK_CONFIG0); ++ ++ hdmi_modb(hdmi, 1, OPMODE_FRL, LINK_CONFIG0); ++ ++ frl_rate = link_cfg->frl_lanes * link_cfg->rate_per_lane; ++ hdmi_start_flt(hdmi, frl_rate); ++ ++ for (i = 0; i < 50; i++) { ++ hdmi_modb(hdmi, PKTSCHED_NULL_TX_EN, PKTSCHED_NULL_TX_EN, PKTSCHED_PKT_EN); ++ mdelay(1); ++ hdmi_modb(hdmi, 0, PKTSCHED_NULL_TX_EN, PKTSCHED_PKT_EN); ++ } ++} ++ ++static unsigned long ++hdmi_get_tmdsclock(struct dw_hdmi_qp *hdmi, unsigned long mpixelclock) ++{ ++ unsigned long tmdsclock = mpixelclock; ++ unsigned int depth = ++ hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format); ++ ++ if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) { ++ switch (depth) { ++ case 16: ++ tmdsclock = mpixelclock * 2; ++ break; ++ case 12: ++ tmdsclock = mpixelclock * 3 / 2; ++ break; ++ case 10: ++ tmdsclock = mpixelclock * 5 / 4; ++ break; ++ default: ++ break; ++ } ++ } ++ ++ return tmdsclock; ++} ++ ++//[CC:] is connector param different from hdmi->connector? ++//[CC:] probably it possible to hook the whole implementation into dw-hdmi.c ++static int dw_hdmi_qp_setup(struct dw_hdmi_qp *hdmi, ++ struct drm_connector *connector, ++ struct drm_display_mode *mode) ++{ ++ int ret; ++ void *data = hdmi->plat_data->phy_data; ++ struct hdmi_vmode_qp *vmode = &hdmi->hdmi_data.video_mode; ++ struct dw_hdmi_link_config *link_cfg; ++ u8 bytes = 0; ++ ++ hdmi->vic = drm_match_cea_mode(mode); ++ if (!hdmi->vic) ++ dev_dbg(hdmi->dev, "Non-CEA mode used in HDMI\n"); ++ else ++ dev_dbg(hdmi->dev, "CEA mode used vic=%d\n", hdmi->vic); ++ ++ if (hdmi->plat_data->get_enc_out_encoding) ++ hdmi->hdmi_data.enc_out_encoding = ++ hdmi->plat_data->get_enc_out_encoding(data); ++ else if ((hdmi->vic == 6) || (hdmi->vic == 7) || ++ (hdmi->vic == 21) || (hdmi->vic == 22) || ++ (hdmi->vic == 2) || (hdmi->vic == 3) || ++ (hdmi->vic == 17) || (hdmi->vic == 18)) ++ hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_601; ++ else ++ hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_709; ++ ++ if (mode->flags & DRM_MODE_FLAG_DBLCLK) { ++ hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 1; ++ hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 1; ++ } else { ++ hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0; ++ hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0; ++ } ++ /* Get input format from plat data or fallback to RGB888 */ ++ if (hdmi->plat_data->get_input_bus_format) ++ hdmi->hdmi_data.enc_in_bus_format = ++ hdmi->plat_data->get_input_bus_format(data); ++ else if (hdmi->plat_data->input_bus_format) ++ hdmi->hdmi_data.enc_in_bus_format = ++ hdmi->plat_data->input_bus_format; ++ else ++ hdmi->hdmi_data.enc_in_bus_format = MEDIA_BUS_FMT_RGB888_1X24; ++ ++ /* Default to RGB888 output format */ ++ if (hdmi->plat_data->get_output_bus_format) ++ hdmi->hdmi_data.enc_out_bus_format = ++ hdmi->plat_data->get_output_bus_format(data); ++ else ++ hdmi->hdmi_data.enc_out_bus_format = MEDIA_BUS_FMT_RGB888_1X24; ++ ++ /* Get input encoding from plat data or fallback to none */ ++ if (hdmi->plat_data->get_enc_in_encoding) ++ hdmi->hdmi_data.enc_in_encoding = ++ hdmi->plat_data->get_enc_in_encoding(data); ++ else if (hdmi->plat_data->input_bus_encoding) ++ hdmi->hdmi_data.enc_in_encoding = ++ hdmi->plat_data->input_bus_encoding; ++ else ++ hdmi->hdmi_data.enc_in_encoding = V4L2_YCBCR_ENC_DEFAULT; ++ ++ if (hdmi->plat_data->get_quant_range) ++ hdmi->hdmi_data.quant_range = ++ hdmi->plat_data->get_quant_range(data); ++ else ++ hdmi->hdmi_data.quant_range = HDMI_QUANTIZATION_RANGE_DEFAULT; ++ ++ if (hdmi->plat_data->get_link_cfg) ++ link_cfg = hdmi->plat_data->get_link_cfg(data); ++ else ++ return -EINVAL; ++ ++ hdmi->phy.ops->set_mode(hdmi, hdmi->phy.data, HDMI_MODE_FRL_MASK, ++ link_cfg->frl_mode); ++ ++ /* ++ * According to the dw-hdmi specification 6.4.2 ++ * vp_pr_cd[3:0]: ++ * 0000b: No pixel repetition (pixel sent only once) ++ * 0001b: Pixel sent two times (pixel repeated once) ++ */ ++ hdmi->hdmi_data.pix_repet_factor = ++ (mode->flags & DRM_MODE_FLAG_DBLCLK) ? 1 : 0; ++ hdmi->hdmi_data.video_mode.mdataenablepolarity = true; ++ ++ vmode->previous_pixelclock = vmode->mpixelclock; ++ //[CC:] no split mode ++ // if (hdmi->plat_data->split_mode) ++ // mode->crtc_clock /= 2; ++ vmode->mpixelclock = mode->crtc_clock * 1000; ++ if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) ++ vmode->mpixelclock *= 2; ++ dev_dbg(hdmi->dev, "final pixclk = %ld\n", vmode->mpixelclock); ++ vmode->previous_tmdsclock = vmode->mtmdsclock; ++ vmode->mtmdsclock = hdmi_get_tmdsclock(hdmi, vmode->mpixelclock); ++ if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) ++ vmode->mtmdsclock /= 2; ++ dev_info(hdmi->dev, "final tmdsclk = %d\n", vmode->mtmdsclock); ++ ++ ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data, &hdmi->previous_mode); ++ if (ret) ++ return ret; ++ ++ if (hdmi->plat_data->set_grf_cfg) ++ hdmi->plat_data->set_grf_cfg(data); ++ ++ if (hdmi->sink_has_audio) { ++ dev_dbg(hdmi->dev, "sink has audio support\n"); ++ ++ /* HDMI Initialization Step E - Configure audio */ ++ hdmi_clk_regenerator_update_pixel_clock(hdmi); ++ hdmi_enable_audio_clk(hdmi, hdmi->audio_enable); ++ } ++ ++ /* not for DVI mode */ ++ if (hdmi->sink_is_hdmi) { ++ dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__); ++ hdmi_modb(hdmi, 0, OPMODE_DVI, LINK_CONFIG0); ++ hdmi_modb(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0); ++ if (!link_cfg->frl_mode) { ++ if (vmode->mtmdsclock > HDMI14_MAX_TMDSCLK) { ++ drm_scdc_readb(hdmi->ddc, SCDC_SINK_VERSION, &bytes); ++ drm_scdc_writeb(hdmi->ddc, SCDC_SOURCE_VERSION, ++ min_t(u8, bytes, SCDC_MIN_SOURCE_VERSION)); ++ //[CC:] use dw_hdmi_set_high_tmds_clock_ratio() ++ drm_scdc_set_high_tmds_clock_ratio(connector, 1); ++ drm_scdc_set_scrambling(connector, 1); ++ hdmi_writel(hdmi, 1, SCRAMB_CONFIG0); ++ } else { ++ if (dw_hdmi_support_scdc(hdmi, &connector->display_info)) { ++ drm_scdc_set_high_tmds_clock_ratio(connector, 0); ++ drm_scdc_set_scrambling(connector, 0); ++ } ++ hdmi_writel(hdmi, 0, SCRAMB_CONFIG0); ++ } ++ } ++ /* HDMI Initialization Step F - Configure AVI InfoFrame */ ++ hdmi_config_AVI(hdmi, connector, mode); ++ hdmi_config_CVTEM(hdmi); ++ hdmi_config_drm_infoframe(hdmi, connector); ++ hdmi_set_op_mode(hdmi, link_cfg, connector); ++ } else { ++ hdmi_modb(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0); ++ hdmi_modb(hdmi, OPMODE_DVI, OPMODE_DVI, LINK_CONFIG0); ++ dev_info(hdmi->dev, "%s DVI mode\n", __func__); ++ } ++ ++ return 0; ++} ++ ++static enum drm_connector_status ++dw_hdmi_connector_detect(struct drm_connector *connector, bool force) ++{ ++ struct dw_hdmi_qp *hdmi = ++ container_of(connector, struct dw_hdmi_qp, connector); ++ struct dw_hdmi_qp *secondary = NULL; ++ enum drm_connector_status result, result_secondary; ++ ++ mutex_lock(&hdmi->mutex); ++ hdmi->force = DRM_FORCE_UNSPECIFIED; ++ mutex_unlock(&hdmi->mutex); ++ ++ if (hdmi->plat_data->left) ++ secondary = hdmi->plat_data->left; ++ else if (hdmi->plat_data->right) ++ secondary = hdmi->plat_data->right; ++ ++ result = hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data); ++ ++ if (secondary) { ++ result_secondary = secondary->phy.ops->read_hpd(secondary, secondary->phy.data); ++ if (result == connector_status_connected && ++ result_secondary == connector_status_connected) ++ result = connector_status_connected; ++ else ++ result = connector_status_disconnected; ++ } ++ ++ mutex_lock(&hdmi->mutex); ++ if (result != hdmi->last_connector_result) { ++ dev_dbg(hdmi->dev, "read_hpd result: %d", result); ++ handle_plugged_change(hdmi, ++ result == connector_status_connected); ++ hdmi->last_connector_result = result; ++ } ++ mutex_unlock(&hdmi->mutex); ++ ++ return result; ++} ++ ++static int ++dw_hdmi_update_hdr_property(struct drm_connector *connector) ++{ ++ struct drm_device *dev = connector->dev; ++ struct dw_hdmi_qp *hdmi = container_of(connector, struct dw_hdmi_qp, ++ connector); ++ void *data = hdmi->plat_data->phy_data; ++ const struct hdr_static_metadata *metadata = ++ &connector->hdr_sink_metadata.hdmi_type1; ++ size_t size = sizeof(*metadata); ++ struct drm_property *property; ++ struct drm_property_blob *blob; ++ int ret; ++ ++ if (hdmi->plat_data->get_hdr_property) ++ property = hdmi->plat_data->get_hdr_property(data); ++ else ++ return -EINVAL; ++ ++ if (hdmi->plat_data->get_hdr_blob) ++ blob = hdmi->plat_data->get_hdr_blob(data); ++ else ++ return -EINVAL; ++ ++ ret = drm_property_replace_global_blob(dev, &blob, size, metadata, ++ &connector->base, property); ++ return ret; ++} ++ ++static int dw_hdmi_connector_get_modes(struct drm_connector *connector) ++{ ++ struct dw_hdmi_qp *hdmi = ++ container_of(connector, struct dw_hdmi_qp, connector); ++ struct hdr_static_metadata *metedata = ++ &connector->hdr_sink_metadata.hdmi_type1; ++ struct edid *edid; ++ struct drm_display_mode *mode; ++ struct drm_display_info *info = &connector->display_info; ++ void *data = hdmi->plat_data->phy_data; ++ int i, ret = 0; ++ ++ if (!hdmi->ddc) ++ return 0; ++ ++ memset(metedata, 0, sizeof(*metedata)); ++ edid = drm_get_edid(connector, hdmi->ddc); ++ if (edid) { ++ dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n", ++ edid->width_cm, edid->height_cm); ++ ++ hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid); ++ hdmi->sink_has_audio = drm_detect_monitor_audio(edid); ++ drm_connector_update_edid_property(connector, edid); ++ cec_notifier_set_phys_addr_from_edid(hdmi->cec_notifier, edid); ++ if (hdmi->plat_data->get_edid_dsc_info) ++ hdmi->plat_data->get_edid_dsc_info(data, edid); ++ ret = drm_add_edid_modes(connector, edid); ++ dw_hdmi_update_hdr_property(connector); ++ if (ret > 0 && hdmi->plat_data->split_mode) { ++ struct dw_hdmi_qp *secondary = NULL; ++ void *secondary_data; ++ ++ if (hdmi->plat_data->left) ++ secondary = hdmi->plat_data->left; ++ else if (hdmi->plat_data->right) ++ secondary = hdmi->plat_data->right; ++ ++ if (!secondary) ++ return -ENOMEM; ++ secondary_data = secondary->plat_data->phy_data; ++ ++ list_for_each_entry(mode, &connector->probed_modes, head) ++ hdmi->plat_data->convert_to_split_mode(mode); ++ ++ secondary->sink_is_hdmi = drm_detect_hdmi_monitor(edid); ++ secondary->sink_has_audio = drm_detect_monitor_audio(edid); ++ cec_notifier_set_phys_addr_from_edid(secondary->cec_notifier, edid); ++ if (secondary->plat_data->get_edid_dsc_info) ++ secondary->plat_data->get_edid_dsc_info(secondary_data, edid); ++ } ++ kfree(edid); ++ } else { ++ hdmi->sink_is_hdmi = true; ++ hdmi->sink_has_audio = true; ++ ++ if (hdmi->plat_data->split_mode) { ++ if (hdmi->plat_data->left) { ++ hdmi->plat_data->left->sink_is_hdmi = true; ++ hdmi->plat_data->left->sink_has_audio = true; ++ } else if (hdmi->plat_data->right) { ++ hdmi->plat_data->right->sink_is_hdmi = true; ++ hdmi->plat_data->right->sink_has_audio = true; ++ } ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(dw_hdmi_default_modes); i++) { ++ const struct drm_display_mode *ptr = ++ &dw_hdmi_default_modes[i]; ++ ++ mode = drm_mode_duplicate(connector->dev, ptr); ++ if (mode) { ++ if (!i) ++ mode->type = DRM_MODE_TYPE_PREFERRED; ++ drm_mode_probed_add(connector, mode); ++ ret++; ++ } ++ } ++ if (ret > 0 && hdmi->plat_data->split_mode) { ++ struct drm_display_mode *mode; ++ ++ list_for_each_entry(mode, &connector->probed_modes, head) ++ hdmi->plat_data->convert_to_split_mode(mode); ++ } ++ info->edid_hdmi_rgb444_dc_modes = 0; ++ info->hdmi.y420_dc_modes = 0; ++ info->color_formats = 0; ++ ++ dev_info(hdmi->dev, "failed to get edid\n"); ++ } ++ ++ return ret; ++} ++ ++static int ++dw_hdmi_atomic_connector_set_property(struct drm_connector *connector, ++ struct drm_connector_state *state, ++ struct drm_property *property, ++ uint64_t val) ++{ ++ struct dw_hdmi_qp *hdmi = ++ container_of(connector, struct dw_hdmi_qp, connector); ++ const struct dw_hdmi_property_ops *ops = hdmi->plat_data->property_ops; ++ ++ if (ops && ops->set_property) ++ return ops->set_property(connector, state, property, ++ val, hdmi->plat_data->phy_data); ++ else ++ return -EINVAL; ++} ++ ++static int ++dw_hdmi_atomic_connector_get_property(struct drm_connector *connector, ++ const struct drm_connector_state *state, ++ struct drm_property *property, ++ uint64_t *val) ++{ ++ struct dw_hdmi_qp *hdmi = ++ container_of(connector, struct dw_hdmi_qp, connector); ++ const struct dw_hdmi_property_ops *ops = hdmi->plat_data->property_ops; ++ ++ if (ops && ops->get_property) ++ return ops->get_property(connector, state, property, ++ val, hdmi->plat_data->phy_data); ++ else ++ return -EINVAL; ++} ++ ++static int ++dw_hdmi_connector_set_property(struct drm_connector *connector, ++ struct drm_property *property, uint64_t val) ++{ ++ return dw_hdmi_atomic_connector_set_property(connector, NULL, ++ property, val); ++} ++ ++static void dw_hdmi_attach_properties(struct dw_hdmi_qp *hdmi) ++{ ++ unsigned int color = MEDIA_BUS_FMT_RGB888_1X24; ++ const struct dw_hdmi_property_ops *ops = ++ hdmi->plat_data->property_ops; ++ ++ if (ops && ops->attach_properties) ++ return ops->attach_properties(&hdmi->connector, color, 0, ++ hdmi->plat_data->phy_data); ++} ++ ++static void dw_hdmi_destroy_properties(struct dw_hdmi_qp *hdmi) ++{ ++ const struct dw_hdmi_property_ops *ops = ++ hdmi->plat_data->property_ops; ++ ++ if (ops && ops->destroy_properties) ++ return ops->destroy_properties(&hdmi->connector, ++ hdmi->plat_data->phy_data); ++} ++ ++static struct drm_encoder * ++dw_hdmi_connector_best_encoder(struct drm_connector *connector) ++{ ++ struct dw_hdmi_qp *hdmi = ++ container_of(connector, struct dw_hdmi_qp, connector); ++ ++ return hdmi->bridge.encoder; ++} ++ ++static bool dw_hdmi_color_changed(struct drm_connector *connector, ++ struct drm_atomic_state *state) ++{ ++ struct dw_hdmi_qp *hdmi = ++ container_of(connector, struct dw_hdmi_qp, connector); ++ void *data = hdmi->plat_data->phy_data; ++ struct drm_connector_state *old_state = ++ drm_atomic_get_old_connector_state(state, connector); ++ struct drm_connector_state *new_state = ++ drm_atomic_get_new_connector_state(state, connector); ++ bool ret = false; ++ ++ if (hdmi->plat_data->get_color_changed) ++ ret = hdmi->plat_data->get_color_changed(data); ++ ++ if (new_state->colorspace != old_state->colorspace) ++ ret = true; ++ ++ return ret; ++} ++ ++static bool hdr_metadata_equal(const struct drm_connector_state *old_state, ++ const struct drm_connector_state *new_state) ++{ ++ struct drm_property_blob *old_blob = old_state->hdr_output_metadata; ++ struct drm_property_blob *new_blob = new_state->hdr_output_metadata; ++ ++ if (!old_blob || !new_blob) ++ return old_blob == new_blob; ++ ++ if (old_blob->length != new_blob->length) ++ return false; ++ ++ return !memcmp(old_blob->data, new_blob->data, old_blob->length); ++} ++ ++static int dw_hdmi_connector_atomic_check(struct drm_connector *connector, ++ struct drm_atomic_state *state) ++{ ++ struct drm_connector_state *old_state = ++ drm_atomic_get_old_connector_state(state, connector); ++ struct drm_connector_state *new_state = ++ drm_atomic_get_new_connector_state(state, connector); ++ struct drm_crtc *crtc = new_state->crtc; ++ struct drm_crtc_state *crtc_state; ++ struct dw_hdmi_qp *hdmi = ++ container_of(connector, struct dw_hdmi_qp, connector); ++ struct drm_display_mode *mode = NULL; ++ void *data = hdmi->plat_data->phy_data; ++ struct hdmi_vmode_qp *vmode = &hdmi->hdmi_data.video_mode; ++ ++ if (!crtc) ++ return 0; ++ ++ crtc_state = drm_atomic_get_crtc_state(state, crtc); ++ if (IS_ERR(crtc_state)) ++ return PTR_ERR(crtc_state); ++ ++ /* ++ * If HDMI is enabled in uboot, it's need to record ++ * drm_display_mode and set phy status to enabled. ++ */ ++ if (!vmode->mpixelclock) { ++ crtc_state = drm_atomic_get_crtc_state(state, crtc); ++ if (hdmi->plat_data->get_enc_in_encoding) ++ hdmi->hdmi_data.enc_in_encoding = ++ hdmi->plat_data->get_enc_in_encoding(data); ++ if (hdmi->plat_data->get_enc_out_encoding) ++ hdmi->hdmi_data.enc_out_encoding = ++ hdmi->plat_data->get_enc_out_encoding(data); ++ if (hdmi->plat_data->get_input_bus_format) ++ hdmi->hdmi_data.enc_in_bus_format = ++ hdmi->plat_data->get_input_bus_format(data); ++ if (hdmi->plat_data->get_output_bus_format) ++ hdmi->hdmi_data.enc_out_bus_format = ++ hdmi->plat_data->get_output_bus_format(data); ++ ++ mode = &crtc_state->mode; ++ if (hdmi->plat_data->split_mode) { ++ hdmi->plat_data->convert_to_origin_mode(mode); ++ mode->crtc_clock /= 2; ++ } ++ memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode)); ++ vmode->mpixelclock = mode->crtc_clock * 1000; ++ vmode->previous_pixelclock = mode->clock; ++ vmode->previous_tmdsclock = mode->clock; ++ vmode->mtmdsclock = hdmi_get_tmdsclock(hdmi, ++ vmode->mpixelclock); ++ if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) ++ vmode->mtmdsclock /= 2; ++ ++ /* ++ * If uboot logo enabled, atomic_enable won't be called, ++ * but atomic_disable will be called when hdmi plug out. ++ * That will cause dclk enable count is incorrect. So ++ * we should check ipi/link/video clk to determine whether ++ * uboot logo is enabled. ++ */ ++ if (hdmi->initialized && !hdmi->dclk_en) { ++ mutex_lock(&hdmi->audio_mutex); ++ if (hdmi->plat_data->dclk_set) ++ hdmi->plat_data->dclk_set(data, true); ++ hdmi->dclk_en = true; ++ mutex_unlock(&hdmi->audio_mutex); ++ } ++ } ++ ++ if (!hdr_metadata_equal(old_state, new_state) || ++ dw_hdmi_color_changed(connector, state)) { ++ crtc_state = drm_atomic_get_crtc_state(state, crtc); ++ if (IS_ERR(crtc_state)) ++ return PTR_ERR(crtc_state); ++ ++ crtc_state->mode_changed = true; ++ } ++ ++ return 0; ++} ++ ++static void dw_hdmi_connector_force(struct drm_connector *connector) ++{ ++ struct dw_hdmi_qp *hdmi = ++ container_of(connector, struct dw_hdmi_qp, connector); ++ ++ mutex_lock(&hdmi->mutex); ++ ++ if (hdmi->force != connector->force) { ++ if (!hdmi->disabled && connector->force == DRM_FORCE_OFF) ++ extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, ++ false); ++ else if (hdmi->disabled && connector->force == DRM_FORCE_ON) ++ extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, ++ true); ++ } ++ ++ hdmi->force = connector->force; ++ mutex_unlock(&hdmi->mutex); ++} ++ ++static int dw_hdmi_qp_fill_modes(struct drm_connector *connector, u32 max_x, ++ u32 max_y) ++{ ++ return drm_helper_probe_single_connector_modes(connector, 9000, 9000); ++} ++ ++static const struct drm_connector_funcs dw_hdmi_connector_funcs = { ++ .fill_modes = dw_hdmi_qp_fill_modes, ++ .detect = dw_hdmi_connector_detect, ++ .destroy = drm_connector_cleanup, ++ .force = dw_hdmi_connector_force, ++ .reset = drm_atomic_helper_connector_reset, ++ .set_property = dw_hdmi_connector_set_property, ++ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, ++ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, ++ .atomic_set_property = dw_hdmi_atomic_connector_set_property, ++ .atomic_get_property = dw_hdmi_atomic_connector_get_property, ++}; ++ ++static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = { ++ .get_modes = dw_hdmi_connector_get_modes, ++ .best_encoder = dw_hdmi_connector_best_encoder, ++ .atomic_check = dw_hdmi_connector_atomic_check, ++}; ++ ++static int dw_hdmi_qp_bridge_attach(struct drm_bridge *bridge, ++ enum drm_bridge_attach_flags flags) ++{ ++ struct dw_hdmi_qp *hdmi = bridge->driver_private; ++ struct drm_encoder *encoder = bridge->encoder; ++ struct drm_connector *connector = &hdmi->connector; ++ struct cec_connector_info conn_info; ++ struct cec_notifier *notifier; ++ ++ if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) ++ return 0; ++ ++ connector->interlace_allowed = 1; ++ connector->polled = DRM_CONNECTOR_POLL_HPD; ++ ++ drm_connector_helper_add(connector, &dw_hdmi_connector_helper_funcs); ++ ++ // [CC:] use drm_connector_init_with_ddc or drmm_connector_init ++ // to provide ddc reference ++ drm_connector_init_with_ddc(bridge->dev, connector, ++ &dw_hdmi_connector_funcs, ++ DRM_MODE_CONNECTOR_HDMIA, ++ hdmi->ddc); ++ ++ drm_connector_attach_encoder(connector, encoder); ++ dw_hdmi_attach_properties(hdmi); ++ ++ cec_fill_conn_info_from_drm(&conn_info, connector); ++ notifier = cec_notifier_conn_register(hdmi->dev, NULL, &conn_info); ++ if (!notifier) ++ return -ENOMEM; ++ ++ mutex_lock(&hdmi->cec_notifier_mutex); ++ hdmi->cec_notifier = notifier; ++ mutex_unlock(&hdmi->cec_notifier_mutex); ++ ++ return 0; ++} ++ ++static void dw_hdmi_qp_bridge_detach(struct drm_bridge *bridge) ++{ ++ struct dw_hdmi_qp *hdmi = bridge->driver_private; ++ ++ mutex_lock(&hdmi->cec_notifier_mutex); ++ cec_notifier_conn_unregister(hdmi->cec_notifier); ++ hdmi->cec_notifier = NULL; ++ mutex_unlock(&hdmi->cec_notifier_mutex); ++} ++ ++static void dw_hdmi_qp_bridge_mode_set(struct drm_bridge *bridge, ++ const struct drm_display_mode *orig_mode, ++ const struct drm_display_mode *mode) ++{ ++ struct dw_hdmi_qp *hdmi = bridge->driver_private; ++ ++ mutex_lock(&hdmi->mutex); ++ ++ /* Store the display mode for plugin/DKMS poweron events */ ++ memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode)); ++ if (hdmi->plat_data->split_mode) ++ hdmi->plat_data->convert_to_origin_mode(&hdmi->previous_mode); ++ ++ mutex_unlock(&hdmi->mutex); ++} ++ ++static enum drm_mode_status ++dw_hdmi_qp_bridge_mode_valid(struct drm_bridge *bridge, ++ const struct drm_display_info *info, ++ const struct drm_display_mode *mode) ++{ ++ return MODE_OK; ++} ++ ++static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge, ++ struct drm_bridge_state *old_state) ++{ ++ struct dw_hdmi_qp *hdmi = bridge->driver_private; ++ struct drm_atomic_state *state = old_state->base.state; ++ struct drm_connector *connector; ++ void *data = hdmi->plat_data->phy_data; ++ ++ connector = drm_atomic_get_new_connector_for_encoder(state, ++ bridge->encoder); ++ ++ mutex_lock(&hdmi->mutex); ++ hdmi->curr_conn = connector; ++ dw_hdmi_qp_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode); ++ hdmi->disabled = false; ++ mutex_unlock(&hdmi->mutex); ++ ++ if (!hdmi->dclk_en) { ++ mutex_lock(&hdmi->audio_mutex); ++ if (hdmi->plat_data->dclk_set) ++ hdmi->plat_data->dclk_set(data, true); ++ hdmi->dclk_en = true; ++ mutex_unlock(&hdmi->audio_mutex); ++ } ++ ++ extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, true); ++ handle_plugged_change(hdmi, true); ++} ++ ++static void dw_hdmi_qp_bridge_atomic_disable(struct drm_bridge *bridge, ++ struct drm_bridge_state *old_state) ++{ ++ struct dw_hdmi_qp *hdmi = bridge->driver_private; ++ void *data = hdmi->plat_data->phy_data; ++ ++ extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, false); ++ handle_plugged_change(hdmi, false); ++ mutex_lock(&hdmi->mutex); ++ ++ hdmi->curr_conn = NULL; ++ ++ if (hdmi->dclk_en) { ++ mutex_lock(&hdmi->audio_mutex); ++ if (hdmi->plat_data->dclk_set) ++ hdmi->plat_data->dclk_set(data, false); ++ hdmi->dclk_en = false; ++ mutex_unlock(&hdmi->audio_mutex); ++ }; ++ ++ if (hdmi->phy.ops->disable) ++ hdmi->phy.ops->disable(hdmi, hdmi->phy.data); ++ hdmi->disabled = true; ++ mutex_unlock(&hdmi->mutex); ++} ++ ++static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = { ++ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, ++ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, ++ .atomic_reset = drm_atomic_helper_bridge_reset, ++ .attach = dw_hdmi_qp_bridge_attach, ++ .detach = dw_hdmi_qp_bridge_detach, ++ .mode_set = dw_hdmi_qp_bridge_mode_set, ++ .mode_valid = dw_hdmi_qp_bridge_mode_valid, ++ .atomic_enable = dw_hdmi_qp_bridge_atomic_enable, ++ .atomic_disable = dw_hdmi_qp_bridge_atomic_disable, ++}; ++ ++void dw_hdmi_qp_set_cec_adap(struct dw_hdmi_qp *hdmi, struct cec_adapter *adap) ++{ ++ hdmi->cec_adap = adap; ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_cec_adap); ++ ++static irqreturn_t dw_hdmi_qp_main_hardirq(int irq, void *dev_id) ++{ ++ struct dw_hdmi_qp *hdmi = dev_id; ++ struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; ++ u32 stat; ++ ++ stat = hdmi_readl(hdmi, MAINUNIT_1_INT_STATUS); ++ ++ i2c->stat = stat & (I2CM_OP_DONE_IRQ | I2CM_READ_REQUEST_IRQ | ++ I2CM_NACK_RCVD_IRQ); ++ hdmi->scdc_intr = stat & (SCDC_UPD_FLAGS_RD_IRQ | ++ SCDC_UPD_FLAGS_CHG_IRQ | ++ SCDC_UPD_FLAGS_CLR_IRQ | ++ SCDC_RR_REPLY_STOP_IRQ | ++ SCDC_NACK_RCVD_IRQ); ++ hdmi->flt_intr = stat & (FLT_EXIT_TO_LTSP_IRQ | ++ FLT_EXIT_TO_LTS4_IRQ | ++ FLT_EXIT_TO_LTSL_IRQ); ++ ++ // dev_dbg(hdmi->dev, "i2c main unit irq:%#x\n", stat); ++ if (i2c->stat) { ++ hdmi_writel(hdmi, i2c->stat, MAINUNIT_1_INT_CLEAR); ++ complete(&i2c->cmp); ++ } ++ ++ if (hdmi->flt_intr) { ++ dev_dbg(hdmi->dev, "i2c flt irq:%#x\n", hdmi->flt_intr); ++ hdmi_writel(hdmi, hdmi->flt_intr, MAINUNIT_1_INT_CLEAR); ++ complete(&hdmi->flt_cmp); ++ } ++ ++ if (hdmi->scdc_intr) { ++ u8 val; ++ ++ dev_dbg(hdmi->dev, "i2c scdc irq:%#x\n", hdmi->scdc_intr); ++ hdmi_writel(hdmi, hdmi->scdc_intr, MAINUNIT_1_INT_CLEAR); ++ val = hdmi_readl(hdmi, SCDC_STATUS0); ++ ++ /* frl start */ ++ if (val & BIT(4)) { ++ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_POLL_EN | ++ SCDC_UPD_FLAGS_AUTO_CLR, SCDC_CONFIG0); ++ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_RD_IRQ, ++ MAINUNIT_1_INT_MASK_N); ++ dev_info(hdmi->dev, "frl start\n"); ++ } ++ ++ } ++ ++ if (stat) ++ return IRQ_HANDLED; ++ ++ return IRQ_NONE; ++} ++ ++static irqreturn_t dw_hdmi_qp_avp_hardirq(int irq, void *dev_id) ++{ ++ struct dw_hdmi_qp *hdmi = dev_id; ++ u32 stat; ++ ++ stat = hdmi_readl(hdmi, AVP_1_INT_STATUS); ++ if (stat) { ++ dev_dbg(hdmi->dev, "HDCP irq %#x\n", stat); ++ stat &= ~stat; ++ hdmi_writel(hdmi, stat, AVP_1_INT_MASK_N); ++ return IRQ_WAKE_THREAD; ++ } ++ ++ return IRQ_NONE; ++} ++ ++static irqreturn_t dw_hdmi_qp_earc_hardirq(int irq, void *dev_id) ++{ ++ struct dw_hdmi_qp *hdmi = dev_id; ++ u32 stat; ++ ++ stat = hdmi_readl(hdmi, EARCRX_0_INT_STATUS); ++ if (stat) { ++ dev_dbg(hdmi->dev, "earc irq %#x\n", stat); ++ stat &= ~stat; ++ hdmi_writel(hdmi, stat, EARCRX_0_INT_MASK_N); ++ return IRQ_WAKE_THREAD; ++ } ++ ++ return IRQ_NONE; ++} ++ ++static irqreturn_t dw_hdmi_qp_avp_irq(int irq, void *dev_id) ++{ ++ struct dw_hdmi_qp *hdmi = dev_id; ++ u32 stat; ++ ++ stat = hdmi_readl(hdmi, AVP_1_INT_STATUS); ++ ++ if (!stat) ++ return IRQ_NONE; ++ ++ hdmi_writel(hdmi, stat, AVP_1_INT_CLEAR); ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t dw_hdmi_qp_earc_irq(int irq, void *dev_id) ++{ ++ struct dw_hdmi_qp *hdmi = dev_id; ++ u32 stat; ++ ++ stat = hdmi_readl(hdmi, EARCRX_0_INT_STATUS); ++ ++ if (!stat) ++ return IRQ_NONE; ++ ++ hdmi_writel(hdmi, stat, EARCRX_0_INT_CLEAR); ++ ++ hdmi->earc_intr = stat; ++ complete(&hdmi->earc_cmp); ++ ++ return IRQ_HANDLED; ++} ++ ++static int dw_hdmi_detect_phy(struct dw_hdmi_qp *hdmi) ++{ ++ u8 phy_type; ++ ++ phy_type = hdmi->plat_data->phy_force_vendor ? ++ DW_HDMI_PHY_VENDOR_PHY : 0; ++ ++ if (phy_type == DW_HDMI_PHY_VENDOR_PHY) { ++ /* Vendor PHYs require support from the glue layer. */ ++ if (!hdmi->plat_data->qp_phy_ops || !hdmi->plat_data->phy_name) { ++ dev_err(hdmi->dev, ++ "Vendor HDMI PHY not supported by glue layer\n"); ++ return -ENODEV; ++ } ++ ++ hdmi->phy.ops = hdmi->plat_data->qp_phy_ops; ++ hdmi->phy.data = hdmi->plat_data->phy_data; ++ hdmi->phy.name = hdmi->plat_data->phy_name; ++ } ++ ++ return 0; ++} ++ ++void dw_hdmi_qp_cec_set_hpd(struct dw_hdmi_qp *hdmi, bool plug_in, bool change) ++{ ++ enum drm_connector_status status = plug_in ? ++ connector_status_connected : connector_status_disconnected; ++ ++ if (!plug_in) ++ cec_notifier_set_phys_addr(hdmi->cec_notifier, ++ CEC_PHYS_ADDR_INVALID); ++ ++ if (hdmi->bridge.dev) { ++ if (change && hdmi->cec_adap && hdmi->cec_adap->devnode.registered) ++ cec_queue_pin_hpd_event(hdmi->cec_adap, plug_in, ktime_get()); ++ drm_bridge_hpd_notify(&hdmi->bridge, status); ++ } ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_cec_set_hpd); ++ ++// static void dw_hdmi_qp_cec_enable(struct dw_hdmi_qp *hdmi) ++// { ++// mutex_lock(&hdmi->mutex); ++// hdmi_modb(hdmi, 0, CEC_SWDISABLE, GLOBAL_SWDISABLE); ++// mutex_unlock(&hdmi->mutex); ++// } ++// ++// static void dw_hdmi_qp_cec_disable(struct dw_hdmi_qp *hdmi) ++// { ++// mutex_lock(&hdmi->mutex); ++// hdmi_modb(hdmi, CEC_SWDISABLE, CEC_SWDISABLE, GLOBAL_SWDISABLE); ++// mutex_unlock(&hdmi->mutex); ++// } ++// ++// static const struct dw_hdmi_qp_cec_ops dw_hdmi_qp_cec_ops = { ++// .enable = dw_hdmi_qp_cec_enable, ++// .disable = dw_hdmi_qp_cec_disable, ++// .write = hdmi_writel, ++// .read = hdmi_readl, ++// }; ++ ++static const struct regmap_config hdmi_regmap_config = { ++ .reg_bits = 32, ++ .val_bits = 32, ++ .reg_stride = 4, ++ .max_register = EARCRX_1_INT_FORCE, ++}; ++ ++struct dw_hdmi_qp_reg_table { ++ int reg_base; ++ int reg_end; ++}; ++ ++static const struct dw_hdmi_qp_reg_table hdmi_reg_table[] = { ++ {0x0, 0xc}, ++ {0x14, 0x1c}, ++ {0x44, 0x48}, ++ {0x50, 0x58}, ++ {0x80, 0x84}, ++ {0xa0, 0xc4}, ++ {0xe0, 0xe8}, ++ {0xf0, 0x118}, ++ {0x140, 0x140}, ++ {0x150, 0x150}, ++ {0x160, 0x168}, ++ {0x180, 0x180}, ++ {0x800, 0x800}, ++ {0x808, 0x808}, ++ {0x814, 0x814}, ++ {0x81c, 0x824}, ++ {0x834, 0x834}, ++ {0x840, 0x864}, ++ {0x86c, 0x86c}, ++ {0x880, 0x89c}, ++ {0x8e0, 0x8e8}, ++ {0x900, 0x900}, ++ {0x908, 0x90c}, ++ {0x920, 0x938}, ++ {0x920, 0x938}, ++ {0x960, 0x960}, ++ {0x968, 0x968}, ++ {0xa20, 0xa20}, ++ {0xa30, 0xa30}, ++ {0xa40, 0xa40}, ++ {0xa54, 0xa54}, ++ {0xa80, 0xaac}, ++ {0xab4, 0xab8}, ++ {0xb00, 0xcbc}, ++ {0xce0, 0xce0}, ++ {0xd00, 0xddc}, ++ {0xe20, 0xe24}, ++ {0xe40, 0xe44}, ++ {0xe4c, 0xe4c}, ++ {0xe60, 0xe80}, ++ {0xea0, 0xf24}, ++ {0x1004, 0x100c}, ++ {0x1020, 0x1030}, ++ {0x1040, 0x1050}, ++ {0x1060, 0x1068}, ++ {0x1800, 0x1820}, ++ {0x182c, 0x182c}, ++ {0x1840, 0x1940}, ++ {0x1960, 0x1a60}, ++ {0x1b00, 0x1b00}, ++ {0x1c00, 0x1c00}, ++ {0x3000, 0x3000}, ++ {0x3010, 0x3014}, ++ {0x3020, 0x3024}, ++ {0x3800, 0x3800}, ++ {0x3810, 0x3814}, ++ {0x3820, 0x3824}, ++ {0x3830, 0x3834}, ++ {0x3840, 0x3844}, ++ {0x3850, 0x3854}, ++ {0x3860, 0x3864}, ++ {0x3870, 0x3874}, ++ {0x4000, 0x4004}, ++ {0x4800, 0x4800}, ++ {0x4810, 0x4814}, ++}; ++ ++static int dw_hdmi_ctrl_show(struct seq_file *s, void *v) ++{ ++ struct dw_hdmi_qp *hdmi = s->private; ++ u32 i = 0, j = 0, val = 0; ++ ++ seq_puts(s, "\n---------------------------------------------------"); ++ ++ for (i = 0; i < ARRAY_SIZE(hdmi_reg_table); i++) { ++ for (j = hdmi_reg_table[i].reg_base; ++ j <= hdmi_reg_table[i].reg_end; j += 4) { ++ val = hdmi_readl(hdmi, j); ++ ++ if ((j - hdmi_reg_table[i].reg_base) % 16 == 0) ++ seq_printf(s, "\n>>>hdmi_ctl %04x:", j); ++ seq_printf(s, " %08x", val); ++ } ++ } ++ seq_puts(s, "\n---------------------------------------------------\n"); ++ ++ return 0; ++} ++ ++static int dw_hdmi_ctrl_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, dw_hdmi_ctrl_show, inode->i_private); ++} ++ ++static ssize_t ++dw_hdmi_ctrl_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct dw_hdmi_qp *hdmi = ++ ((struct seq_file *)file->private_data)->private; ++ u32 reg, val; ++ char kbuf[25]; ++ ++ if (count > 24) { ++ dev_err(hdmi->dev, "out of buf range\n"); ++ return count; ++ } ++ ++ if (copy_from_user(kbuf, buf, count)) ++ return -EFAULT; ++ kbuf[count - 1] = '\0'; ++ ++ if (sscanf(kbuf, "%x %x", ®, &val) == -1) ++ return -EFAULT; ++ if (reg > EARCRX_1_INT_FORCE) { ++ dev_err(hdmi->dev, "it is no a hdmi register\n"); ++ return count; ++ } ++ dev_info(hdmi->dev, "/**********hdmi register config******/"); ++ dev_info(hdmi->dev, "\n reg=%x val=%x\n", reg, val); ++ hdmi_writel(hdmi, val, reg); ++ return count; ++} ++ ++static const struct file_operations dw_hdmi_ctrl_fops = { ++ .owner = THIS_MODULE, ++ .open = dw_hdmi_ctrl_open, ++ .read = seq_read, ++ .write = dw_hdmi_ctrl_write, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static int dw_hdmi_status_show(struct seq_file *s, void *v) ++{ ++ struct dw_hdmi_qp *hdmi = s->private; ++ u32 val; ++ ++ seq_puts(s, "PHY: "); ++ if (hdmi->disabled) { ++ seq_puts(s, "disabled\n"); ++ return 0; ++ } ++ seq_puts(s, "enabled\t\t\tMode: "); ++ if (hdmi->sink_is_hdmi) ++ seq_puts(s, "HDMI\n"); ++ else ++ seq_puts(s, "DVI\n"); ++ ++ if (hdmi->hdmi_data.video_mode.mpixelclock > 600000000) { ++ seq_printf(s, "FRL Mode Pixel Clk: %luHz\n", ++ hdmi->hdmi_data.video_mode.mpixelclock); ++ } else { ++ if (hdmi->hdmi_data.video_mode.mtmdsclock > 340000000) ++ val = hdmi->hdmi_data.video_mode.mtmdsclock / 4; ++ else ++ val = hdmi->hdmi_data.video_mode.mtmdsclock; ++ seq_printf(s, "TMDS Mode Pixel Clk: %luHz\t\tTMDS Clk: %uHz\n", ++ hdmi->hdmi_data.video_mode.mpixelclock, val); ++ } ++ ++ seq_puts(s, "Color Format: "); ++ if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) ++ seq_puts(s, "RGB"); ++ else if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format)) ++ seq_puts(s, "YUV444"); ++ else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) ++ seq_puts(s, "YUV422"); ++ else if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) ++ seq_puts(s, "YUV420"); ++ else ++ seq_puts(s, "UNKNOWN"); ++ val = hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format); ++ seq_printf(s, "\t\tColor Depth: %d bit\n", val); ++ seq_puts(s, "Colorimetry: "); ++ switch (hdmi->hdmi_data.enc_out_encoding) { ++ case V4L2_YCBCR_ENC_601: ++ seq_puts(s, "ITU.BT601"); ++ break; ++ case V4L2_YCBCR_ENC_709: ++ seq_puts(s, "ITU.BT709"); ++ break; ++ case V4L2_YCBCR_ENC_BT2020: ++ seq_puts(s, "ITU.BT2020"); ++ break; ++ default: /* Carries no data */ ++ seq_puts(s, "ITU.BT601"); ++ break; ++ } ++ ++ seq_puts(s, "\t\tEOTF: "); ++ ++ val = hdmi_readl(hdmi, PKTSCHED_PKT_EN); ++ if (!(val & PKTSCHED_DRMI_TX_EN)) { ++ seq_puts(s, "Off\n"); ++ return 0; ++ } ++ ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS1); ++ val = (val >> 8) & 0x7; ++ switch (val) { ++ case HDMI_EOTF_TRADITIONAL_GAMMA_SDR: ++ seq_puts(s, "SDR"); ++ break; ++ case HDMI_EOTF_TRADITIONAL_GAMMA_HDR: ++ seq_puts(s, "HDR"); ++ break; ++ case HDMI_EOTF_SMPTE_ST2084: ++ seq_puts(s, "ST2084"); ++ break; ++ case HDMI_EOTF_BT_2100_HLG: ++ seq_puts(s, "HLG"); ++ break; ++ default: ++ seq_puts(s, "Not Defined\n"); ++ return 0; ++ } ++ ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS1); ++ val = (val >> 16) & 0xffff; ++ seq_printf(s, "\nx0: %d", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS2); ++ val = val & 0xffff; ++ seq_printf(s, "\t\t\t\ty0: %d\n", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS2); ++ val = (val >> 16) & 0xffff; ++ seq_printf(s, "x1: %d", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS3); ++ val = val & 0xffff; ++ seq_printf(s, "\t\t\t\ty1: %d\n", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS3); ++ val = (val >> 16) & 0xffff; ++ seq_printf(s, "x2: %d", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS4); ++ val = val & 0xffff; ++ seq_printf(s, "\t\t\t\ty2: %d\n", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS4); ++ val = (val >> 16) & 0xffff; ++ seq_printf(s, "white x: %d", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS5); ++ val = val & 0xffff; ++ seq_printf(s, "\t\t\twhite y: %d\n", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS5); ++ val = (val >> 16) & 0xffff; ++ seq_printf(s, "max lum: %d", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS6); ++ val = val & 0xffff; ++ seq_printf(s, "\t\t\tmin lum: %d\n", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS6); ++ val = (val >> 16) & 0xffff; ++ seq_printf(s, "max cll: %d", val); ++ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS7); ++ val = val & 0xffff; ++ seq_printf(s, "\t\t\tmax fall: %d\n", val); ++ return 0; ++} ++ ++static int dw_hdmi_status_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, dw_hdmi_status_show, inode->i_private); ++} ++ ++static const struct file_operations dw_hdmi_status_fops = { ++ .owner = THIS_MODULE, ++ .open = dw_hdmi_status_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static void dw_hdmi_register_debugfs(struct device *dev, struct dw_hdmi_qp *hdmi) ++{ ++ u8 buf[11]; ++ ++ snprintf(buf, sizeof(buf), "dw-hdmi%d", hdmi->plat_data->id); ++ hdmi->debugfs_dir = debugfs_create_dir(buf, NULL); ++ if (IS_ERR(hdmi->debugfs_dir)) { ++ dev_err(dev, "failed to create debugfs dir!\n"); ++ return; ++ } ++ ++ debugfs_create_file("status", 0400, hdmi->debugfs_dir, ++ hdmi, &dw_hdmi_status_fops); ++ debugfs_create_file("ctrl", 0600, hdmi->debugfs_dir, ++ hdmi, &dw_hdmi_ctrl_fops); ++} ++ ++static struct dw_hdmi_qp * ++__dw_hdmi_probe(struct platform_device *pdev, ++ const struct dw_hdmi_plat_data *plat_data) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ struct device_node *ddc_node; ++ struct dw_hdmi_qp *hdmi; ++ // struct dw_hdmi_qp_i2s_audio_data audio; ++ // struct platform_device_info pdevinfo; ++ // struct dw_hdmi_qp_cec_data cec; ++ struct resource *iores = NULL; ++ int irq; ++ int ret; ++ ++ hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); ++ if (!hdmi) ++ return ERR_PTR(-ENOMEM); ++ ++ hdmi->connector.stereo_allowed = 1; ++ hdmi->plat_data = plat_data; ++ hdmi->dev = dev; ++ hdmi->sample_rate = 48000; ++ hdmi->disabled = true; ++ ++ mutex_init(&hdmi->mutex); ++ mutex_init(&hdmi->audio_mutex); ++ mutex_init(&hdmi->cec_notifier_mutex); ++ ++ ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0); ++ if (ddc_node) { ++ hdmi->ddc = of_get_i2c_adapter_by_node(ddc_node); ++ of_node_put(ddc_node); ++ if (!hdmi->ddc) { ++ dev_dbg(hdmi->dev, "failed to read ddc node\n"); ++ return ERR_PTR(-EPROBE_DEFER); ++ } ++ ++ } else { ++ dev_dbg(hdmi->dev, "no ddc property found\n"); ++ } ++ ++ if (!plat_data->regm) { ++ const struct regmap_config *reg_config; ++ ++ reg_config = &hdmi_regmap_config; ++ ++ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ hdmi->regs = devm_ioremap_resource(dev, iores); ++ if (IS_ERR(hdmi->regs)) { ++ ret = PTR_ERR(hdmi->regs); ++ goto err_res; ++ } ++ ++ hdmi->regm = devm_regmap_init_mmio(dev, hdmi->regs, reg_config); ++ if (IS_ERR(hdmi->regm)) { ++ dev_err(dev, "Failed to configure regmap\n"); ++ ret = PTR_ERR(hdmi->regm); ++ goto err_res; ++ } ++ } else { ++ hdmi->regm = plat_data->regm; ++ } ++ ++ ret = dw_hdmi_detect_phy(hdmi); ++ if (ret < 0) ++ goto err_res; ++ ++ hdmi_writel(hdmi, 0, MAINUNIT_0_INT_MASK_N); ++ hdmi_writel(hdmi, 0, MAINUNIT_1_INT_MASK_N); ++ hdmi_writel(hdmi, 428571429, TIMER_BASE_CONFIG0); ++ if ((hdmi_readl(hdmi, CMU_STATUS) & DISPLAY_CLK_MONITOR) == DISPLAY_CLK_LOCKED) { ++ hdmi->initialized = true; ++ hdmi->disabled = false; ++ } ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ ret = irq; ++ goto err_res; ++ } ++ ++ hdmi->avp_irq = irq; ++ ret = devm_request_threaded_irq(dev, hdmi->avp_irq, ++ dw_hdmi_qp_avp_hardirq, ++ dw_hdmi_qp_avp_irq, IRQF_SHARED, ++ dev_name(dev), hdmi); ++ if (ret) ++ goto err_res; ++ ++ irq = platform_get_irq(pdev, 1); ++ if (irq < 0) { ++ ret = irq; ++ goto err_res; ++ } ++ ++ // cec.irq = irq; ++ ++ irq = platform_get_irq(pdev, 2); ++ if (irq < 0) { ++ ret = irq; ++ goto err_res; ++ } ++ ++ hdmi->earc_irq = irq; ++ ret = devm_request_threaded_irq(dev, hdmi->earc_irq, ++ dw_hdmi_qp_earc_hardirq, ++ dw_hdmi_qp_earc_irq, IRQF_SHARED, ++ dev_name(dev), hdmi); ++ if (ret) ++ goto err_res; ++ ++ irq = platform_get_irq(pdev, 3); ++ if (irq < 0) { ++ ret = irq; ++ goto err_res; ++ } ++ ++ hdmi->main_irq = irq; ++ ret = devm_request_threaded_irq(dev, hdmi->main_irq, ++ dw_hdmi_qp_main_hardirq, NULL, ++ IRQF_SHARED, dev_name(dev), hdmi); ++ if (ret) ++ goto err_res; ++ ++ hdmi_init_clk_regenerator(hdmi); ++ ++ /* If DDC bus is not specified, try to register HDMI I2C bus */ ++ if (!hdmi->ddc) { ++ hdmi->ddc = dw_hdmi_i2c_adapter(hdmi); ++ if (IS_ERR(hdmi->ddc)) ++ hdmi->ddc = NULL; ++ /* ++ * Read high and low time from device tree. If not available use ++ * the default timing scl clock rate is about 99.6KHz. ++ */ ++ if (of_property_read_u32(np, "ddc-i2c-scl-high-time-ns", ++ &hdmi->i2c->scl_high_ns)) ++ hdmi->i2c->scl_high_ns = 4708; ++ if (of_property_read_u32(np, "ddc-i2c-scl-low-time-ns", ++ &hdmi->i2c->scl_low_ns)) ++ hdmi->i2c->scl_low_ns = 4916; ++ } ++ ++ hdmi->bridge.driver_private = hdmi; ++ hdmi->bridge.funcs = &dw_hdmi_bridge_funcs; ++#ifdef CONFIG_OF ++ hdmi->bridge.of_node = pdev->dev.of_node; ++#endif ++ ++ if (hdmi->phy.ops->setup_hpd) ++ hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data); ++ ++ hdmi->connector.ycbcr_420_allowed = hdmi->plat_data->ycbcr_420_allowed; ++ ++ // audio.hdmi = hdmi; ++ // audio.eld = hdmi->connector.eld; ++ // audio.write = hdmi_writel; ++ // audio.read = hdmi_readl; ++ // audio.mod = hdmi_modb; ++ // hdmi->enable_audio = dw_hdmi_i2s_audio_enable; ++ // hdmi->disable_audio = dw_hdmi_i2s_audio_disable; ++ ++ // memset(&pdevinfo, 0, sizeof(pdevinfo)); ++ // pdevinfo.parent = dev; ++ // pdevinfo.id = PLATFORM_DEVID_AUTO; ++ // pdevinfo.name = "dw-hdmi-qp-i2s-audio"; ++ // pdevinfo.data = &audio; ++ // pdevinfo.size_data = sizeof(audio); ++ // pdevinfo.dma_mask = DMA_BIT_MASK(32); ++ // hdmi->audio = platform_device_register_full(&pdevinfo); ++ ++ hdmi->extcon = devm_extcon_dev_allocate(hdmi->dev, dw_hdmi_cable); ++ if (IS_ERR(hdmi->extcon)) { ++ dev_err(hdmi->dev, "allocate extcon failed\n"); ++ ret = PTR_ERR(hdmi->extcon); ++ goto err_res; ++ } ++ ++ ret = devm_extcon_dev_register(hdmi->dev, hdmi->extcon); ++ if (ret) { ++ dev_err(hdmi->dev, "failed to register extcon: %d\n", ret); ++ goto err_res; ++ } ++ ++ ret = extcon_set_property_capability(hdmi->extcon, EXTCON_DISP_HDMI, ++ EXTCON_PROP_DISP_HPD); ++ if (ret) { ++ dev_err(hdmi->dev, ++ "failed to set USB property capability: %d\n", ret); ++ goto err_res; ++ } ++ ++ // cec.hdmi = hdmi; ++ // cec.ops = &dw_hdmi_qp_cec_ops; ++ // pdevinfo.name = "dw-hdmi-qp-cec"; ++ // pdevinfo.data = &cec; ++ // pdevinfo.size_data = sizeof(cec); ++ // pdevinfo.dma_mask = 0; ++ // hdmi->cec = platform_device_register_full(&pdevinfo); ++ ++ /* Reset HDMI DDC I2C master controller and mute I2CM interrupts */ ++ if (hdmi->i2c) ++ dw_hdmi_i2c_init(hdmi); ++ ++ init_completion(&hdmi->flt_cmp); ++ init_completion(&hdmi->earc_cmp); ++ ++ if (of_property_read_bool(np, "scramble-low-rates")) ++ hdmi->scramble_low_rates = true; ++ ++ dw_hdmi_register_debugfs(dev, hdmi); ++ ++ return hdmi; ++ ++err_res: ++ if (hdmi->i2c) ++ i2c_del_adapter(&hdmi->i2c->adap); ++ else ++ i2c_put_adapter(hdmi->ddc); ++ ++ return ERR_PTR(ret); ++} ++ ++static void __dw_hdmi_remove(struct dw_hdmi_qp *hdmi) ++{ ++ if (hdmi->avp_irq) ++ disable_irq(hdmi->avp_irq); ++ ++ if (hdmi->main_irq) ++ disable_irq(hdmi->main_irq); ++ ++ if (hdmi->earc_irq) ++ disable_irq(hdmi->earc_irq); ++ ++ debugfs_remove_recursive(hdmi->debugfs_dir); ++ ++ if (!hdmi->plat_data->first_screen) { ++ dw_hdmi_destroy_properties(hdmi); ++ hdmi->connector.funcs->destroy(&hdmi->connector); ++ } ++ ++ if (hdmi->audio && !IS_ERR(hdmi->audio)) ++ platform_device_unregister(hdmi->audio); ++ ++ // [CC:] dw_hdmi_rockchip_unbind() also calls drm_encoder_cleanup() ++ // and causes a seg fault due to NULL ptr dererence ++ // if (hdmi->bridge.encoder && !hdmi->plat_data->first_screen) ++ // hdmi->bridge.encoder->funcs->destroy(hdmi->bridge.encoder); ++ // ++ if (!IS_ERR(hdmi->cec)) ++ platform_device_unregister(hdmi->cec); ++ if (hdmi->i2c) ++ i2c_del_adapter(&hdmi->i2c->adap); ++ else ++ i2c_put_adapter(hdmi->ddc); ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Bind/unbind API, used from platforms based on the component framework. ++ */ ++struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev, ++ struct drm_encoder *encoder, ++ struct dw_hdmi_plat_data *plat_data) ++{ ++ struct dw_hdmi_qp *hdmi; ++ int ret; ++ ++ hdmi = __dw_hdmi_probe(pdev, plat_data); ++ if (IS_ERR(hdmi)) ++ return hdmi; ++ ++ if (!plat_data->first_screen) { ++ ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL, 0); ++ if (ret) { ++ __dw_hdmi_remove(hdmi); ++ dev_err(hdmi->dev, "Failed to initialize bridge with drm\n"); ++ return ERR_PTR(ret); ++ } ++ ++ plat_data->connector = &hdmi->connector; ++ } ++ ++ if (plat_data->split_mode && !hdmi->plat_data->first_screen) { ++ struct dw_hdmi_qp *secondary = NULL; ++ ++ if (hdmi->plat_data->left) ++ secondary = hdmi->plat_data->left; ++ else if (hdmi->plat_data->right) ++ secondary = hdmi->plat_data->right; ++ ++ if (!secondary) ++ return ERR_PTR(-ENOMEM); ++ ret = drm_bridge_attach(encoder, &secondary->bridge, &hdmi->bridge, ++ DRM_BRIDGE_ATTACH_NO_CONNECTOR); ++ if (ret) ++ return ERR_PTR(ret); ++ } ++ ++ return hdmi; ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_bind); ++ ++void dw_hdmi_qp_unbind(struct dw_hdmi_qp *hdmi) ++{ ++ __dw_hdmi_remove(hdmi); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_unbind); ++ ++void dw_hdmi_qp_suspend(struct device *dev, struct dw_hdmi_qp *hdmi) ++{ ++ if (!hdmi) { ++ dev_warn(dev, "Hdmi has not been initialized\n"); ++ return; ++ } ++ ++ mutex_lock(&hdmi->mutex); ++ ++ /* ++ * When system shutdown, hdmi should be disabled. ++ * When system suspend, dw_hdmi_qp_bridge_disable will disable hdmi first. ++ * To prevent duplicate operation, we should determine whether hdmi ++ * has been disabled. ++ */ ++ if (!hdmi->disabled) ++ hdmi->disabled = true; ++ mutex_unlock(&hdmi->mutex); ++ ++ if (hdmi->avp_irq) ++ disable_irq(hdmi->avp_irq); ++ ++ if (hdmi->main_irq) ++ disable_irq(hdmi->main_irq); ++ ++ if (hdmi->earc_irq) ++ disable_irq(hdmi->earc_irq); ++ ++ pinctrl_pm_select_sleep_state(dev); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_suspend); ++ ++void dw_hdmi_qp_resume(struct device *dev, struct dw_hdmi_qp *hdmi) ++{ ++ if (!hdmi) { ++ dev_warn(dev, "Hdmi has not been initialized\n"); ++ return; ++ } ++ ++ hdmi_writel(hdmi, 0, MAINUNIT_0_INT_MASK_N); ++ hdmi_writel(hdmi, 0, MAINUNIT_1_INT_MASK_N); ++ hdmi_writel(hdmi, 428571429, TIMER_BASE_CONFIG0); ++ ++ pinctrl_pm_select_default_state(dev); ++ ++ hdmi->cec_adap->ops->adap_enable(hdmi->cec_adap, true); ++ ++ mutex_lock(&hdmi->mutex); ++ if (hdmi->i2c) ++ dw_hdmi_i2c_init(hdmi); ++ if (hdmi->avp_irq) ++ enable_irq(hdmi->avp_irq); ++ ++ if (hdmi->main_irq) ++ enable_irq(hdmi->main_irq); ++ ++ if (hdmi->earc_irq) ++ enable_irq(hdmi->earc_irq); ++ ++ mutex_unlock(&hdmi->mutex); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_qp_resume); ++ ++MODULE_AUTHOR("Algea Cao "); ++MODULE_DESCRIPTION("DW HDMI QP transmitter driver"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:dw-hdmi-qp"); +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h +new file mode 100644 +index 000000000000..4cac70f2d11d +--- /dev/null ++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h +@@ -0,0 +1,831 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) Rockchip Electronics Co.Ltd ++ * Author: ++ * Algea Cao ++ */ ++#ifndef __DW_HDMI_QP_H__ ++#define __DW_HDMI_QP_H__ ++/* Main Unit Registers */ ++#define CORE_ID 0x0 ++#define VER_NUMBER 0x4 ++#define VER_TYPE 0x8 ++#define CONFIG_REG 0xc ++#define CONFIG_CEC BIT(28) ++#define CONFIG_AUD_UD BIT(23) ++#define CORE_TIMESTAMP_HHMM 0x14 ++#define CORE_TIMESTAMP_MMDD 0x18 ++#define CORE_TIMESTAMP_YYYY 0x1c ++/* Reset Manager Registers */ ++#define GLOBAL_SWRESET_REQUEST 0x40 ++#define EARCRX_CMDC_SWINIT_P BIT(27) ++#define AVP_DATAPATH_PACKET_AUDIO_SWINIT_P BIT(10) ++#define GLOBAL_SWDISABLE 0x44 ++#define CEC_SWDISABLE BIT(17) ++#define AVP_DATAPATH_PACKET_AUDIO_SWDISABLE BIT(10) ++#define AVP_DATAPATH_VIDEO_SWDISABLE BIT(6) ++#define RESET_MANAGER_CONFIG0 0x48 ++#define RESET_MANAGER_STATUS0 0x50 ++#define RESET_MANAGER_STATUS1 0x54 ++#define RESET_MANAGER_STATUS2 0x58 ++/* Timer Base Registers */ ++#define TIMER_BASE_CONFIG0 0x80 ++#define TIMER_BASE_STATUS0 0x84 ++/* CMU Registers */ ++#define CMU_CONFIG0 0xa0 ++#define CMU_CONFIG1 0xa4 ++#define CMU_CONFIG2 0xa8 ++#define CMU_CONFIG3 0xac ++#define CMU_STATUS 0xb0 ++#define DISPLAY_CLK_MONITOR 0x3f ++#define DISPLAY_CLK_LOCKED 0X15 ++#define EARC_BPCLK_OFF BIT(9) ++#define AUDCLK_OFF BIT(7) ++#define LINKQPCLK_OFF BIT(5) ++#define VIDQPCLK_OFF BIT(3) ++#define IPI_CLK_OFF BIT(1) ++#define CMU_IPI_CLK_FREQ 0xb4 ++#define CMU_VIDQPCLK_FREQ 0xb8 ++#define CMU_LINKQPCLK_FREQ 0xbc ++#define CMU_AUDQPCLK_FREQ 0xc0 ++#define CMU_EARC_BPCLK_FREQ 0xc4 ++/* I2CM Registers */ ++#define I2CM_SM_SCL_CONFIG0 0xe0 ++#define I2CM_FM_SCL_CONFIG0 0xe4 ++#define I2CM_CONFIG0 0xe8 ++#define I2CM_CONTROL0 0xec ++#define I2CM_STATUS0 0xf0 ++#define I2CM_INTERFACE_CONTROL0 0xf4 ++#define I2CM_ADDR 0xff000 ++#define I2CM_SLVADDR 0xfe0 ++#define I2CM_WR_MASK 0x1e ++#define I2CM_EXT_READ BIT(4) ++#define I2CM_SHORT_READ BIT(3) ++#define I2CM_FM_READ BIT(2) ++#define I2CM_FM_WRITE BIT(1) ++#define I2CM_FM_EN BIT(0) ++#define I2CM_INTERFACE_CONTROL1 0xf8 ++#define I2CM_SEG_PTR 0x7f80 ++#define I2CM_SEG_ADDR 0x7f ++#define I2CM_INTERFACE_WRDATA_0_3 0xfc ++#define I2CM_INTERFACE_WRDATA_4_7 0x100 ++#define I2CM_INTERFACE_WRDATA_8_11 0x104 ++#define I2CM_INTERFACE_WRDATA_12_15 0x108 ++#define I2CM_INTERFACE_RDDATA_0_3 0x10c ++#define I2CM_INTERFACE_RDDATA_4_7 0x110 ++#define I2CM_INTERFACE_RDDATA_8_11 0x114 ++#define I2CM_INTERFACE_RDDATA_12_15 0x118 ++/* SCDC Registers */ ++#define SCDC_CONFIG0 0x140 ++#define SCDC_I2C_FM_EN BIT(12) ++#define SCDC_UPD_FLAGS_AUTO_CLR BIT(6) ++#define SCDC_UPD_FLAGS_POLL_EN BIT(4) ++#define SCDC_CONTROL0 0x148 ++#define SCDC_STATUS0 0x150 ++#define STATUS_UPDATE BIT(0) ++#define FRL_START BIT(4) ++#define FLT_UPDATE BIT(5) ++/* FLT Registers */ ++#define FLT_CONFIG0 0x160 ++#define FLT_CONFIG1 0x164 ++#define FLT_CONFIG2 0x168 ++#define FLT_CONTROL0 0x170 ++/* Main Unit 2 Registers */ ++#define MAINUNIT_STATUS0 0x180 ++/* Video Interface Registers */ ++#define VIDEO_INTERFACE_CONFIG0 0x800 ++#define VIDEO_INTERFACE_CONFIG1 0x804 ++#define VIDEO_INTERFACE_CONFIG2 0x808 ++#define VIDEO_INTERFACE_CONTROL0 0x80c ++#define VIDEO_INTERFACE_STATUS0 0x814 ++/* Video Packing Registers */ ++#define VIDEO_PACKING_CONFIG0 0x81c ++/* Audio Interface Registers */ ++#define AUDIO_INTERFACE_CONFIG0 0x820 ++#define AUD_IF_SEL_MSK 0x3 ++#define AUD_IF_SPDIF 0x2 ++#define AUD_IF_I2S 0x1 ++#define AUD_IF_PAI 0x0 ++#define AUD_FIFO_INIT_ON_OVF_MSK BIT(2) ++#define AUD_FIFO_INIT_ON_OVF_EN BIT(2) ++#define I2S_LINES_EN_MSK GENMASK(7, 4) ++#define I2S_LINES_EN(x) BIT(x + 4) ++#define I2S_BPCUV_RCV_MSK BIT(12) ++#define I2S_BPCUV_RCV_EN BIT(12) ++#define I2S_BPCUV_RCV_DIS 0 ++#define SPDIF_LINES_EN GENMASK(19, 16) ++#define AUD_FORMAT_MSK GENMASK(26, 24) ++#define AUD_3DOBA (0x7 << 24) ++#define AUD_3DASP (0x6 << 24) ++#define AUD_MSOBA (0x5 << 24) ++#define AUD_MSASP (0x4 << 24) ++#define AUD_HBR (0x3 << 24) ++#define AUD_DST (0x2 << 24) ++#define AUD_OBA (0x1 << 24) ++#define AUD_ASP (0x0 << 24) ++#define AUDIO_INTERFACE_CONFIG1 0x824 ++#define AUDIO_INTERFACE_CONTROL0 0x82c ++#define AUDIO_FIFO_CLR_P BIT(0) ++#define AUDIO_INTERFACE_STATUS0 0x834 ++/* Frame Composer Registers */ ++#define FRAME_COMPOSER_CONFIG0 0x840 ++#define FRAME_COMPOSER_CONFIG1 0x844 ++#define FRAME_COMPOSER_CONFIG2 0x848 ++#define FRAME_COMPOSER_CONFIG3 0x84c ++#define FRAME_COMPOSER_CONFIG4 0x850 ++#define FRAME_COMPOSER_CONFIG5 0x854 ++#define FRAME_COMPOSER_CONFIG6 0x858 ++#define FRAME_COMPOSER_CONFIG7 0x85c ++#define FRAME_COMPOSER_CONFIG8 0x860 ++#define FRAME_COMPOSER_CONFIG9 0x864 ++#define FRAME_COMPOSER_CONTROL0 0x86c ++/* Video Monitor Registers */ ++#define VIDEO_MONITOR_CONFIG0 0x880 ++#define VIDEO_MONITOR_STATUS0 0x884 ++#define VIDEO_MONITOR_STATUS1 0x888 ++#define VIDEO_MONITOR_STATUS2 0x88c ++#define VIDEO_MONITOR_STATUS3 0x890 ++#define VIDEO_MONITOR_STATUS4 0x894 ++#define VIDEO_MONITOR_STATUS5 0x898 ++#define VIDEO_MONITOR_STATUS6 0x89c ++/* HDCP2 Logic Registers */ ++#define HDCP2LOGIC_CONFIG0 0x8e0 ++#define HDCP2_BYPASS BIT(0) ++#define HDCP2LOGIC_ESM_GPIO_IN 0x8e4 ++#define HDCP2LOGIC_ESM_GPIO_OUT 0x8e8 ++/* HDCP14 Registers */ ++#define HDCP14_CONFIG0 0x900 ++#define HDCP14_CONFIG1 0x904 ++#define HDCP14_CONFIG2 0x908 ++#define HDCP14_CONFIG3 0x90c ++#define HDCP14_KEY_SEED 0x914 ++#define HDCP14_KEY_H 0x918 ++#define HDCP14_KEY_L 0x91c ++#define HDCP14_KEY_STATUS 0x920 ++#define HDCP14_AKSV_H 0x924 ++#define HDCP14_AKSV_L 0x928 ++#define HDCP14_AN_H 0x92c ++#define HDCP14_AN_L 0x930 ++#define HDCP14_STATUS0 0x934 ++#define HDCP14_STATUS1 0x938 ++/* Scrambler Registers */ ++#define SCRAMB_CONFIG0 0x960 ++/* Video Configuration Registers */ ++#define LINK_CONFIG0 0x968 ++#define OPMODE_FRL_4LANES BIT(8) ++#define OPMODE_DVI BIT(4) ++#define OPMODE_FRL BIT(0) ++/* TMDS FIFO Registers */ ++#define TMDS_FIFO_CONFIG0 0x970 ++#define TMDS_FIFO_CONTROL0 0x974 ++/* FRL RSFEC Registers */ ++#define FRL_RSFEC_CONFIG0 0xa20 ++#define FRL_RSFEC_STATUS0 0xa30 ++/* FRL Packetizer Registers */ ++#define FRL_PKTZ_CONFIG0 0xa40 ++#define FRL_PKTZ_CONTROL0 0xa44 ++#define FRL_PKTZ_CONTROL1 0xa50 ++#define FRL_PKTZ_STATUS1 0xa54 ++/* Packet Scheduler Registers */ ++#define PKTSCHED_CONFIG0 0xa80 ++#define PKTSCHED_PRQUEUE0_CONFIG0 0xa84 ++#define PKTSCHED_PRQUEUE1_CONFIG0 0xa88 ++#define PKTSCHED_PRQUEUE2_CONFIG0 0xa8c ++#define PKTSCHED_PRQUEUE2_CONFIG1 0xa90 ++#define PKTSCHED_PRQUEUE2_CONFIG2 0xa94 ++#define PKTSCHED_PKT_CONFIG0 0xa98 ++#define PKTSCHED_PKT_CONFIG1 0xa9c ++#define PKTSCHED_DRMI_FIELDRATE BIT(13) ++#define PKTSCHED_AVI_FIELDRATE BIT(12) ++#define PKTSCHED_PKT_CONFIG2 0xaa0 ++#define PKTSCHED_PKT_CONFIG3 0xaa4 ++#define PKTSCHED_PKT_EN 0xaa8 ++#define PKTSCHED_DRMI_TX_EN BIT(17) ++#define PKTSCHED_AUDI_TX_EN BIT(15) ++#define PKTSCHED_AVI_TX_EN BIT(13) ++#define PKTSCHED_EMP_CVTEM_TX_EN BIT(10) ++#define PKTSCHED_AMD_TX_EN BIT(8) ++#define PKTSCHED_GCP_TX_EN BIT(3) ++#define PKTSCHED_AUDS_TX_EN BIT(2) ++#define PKTSCHED_ACR_TX_EN BIT(1) ++#define PKTSCHED_NULL_TX_EN BIT(0) ++#define PKTSCHED_PKT_CONTROL0 0xaac ++#define PKTSCHED_PKT_SEND 0xab0 ++#define PKTSCHED_PKT_STATUS0 0xab4 ++#define PKTSCHED_PKT_STATUS1 0xab8 ++#define PKT_NULL_CONTENTS0 0xb00 ++#define PKT_NULL_CONTENTS1 0xb04 ++#define PKT_NULL_CONTENTS2 0xb08 ++#define PKT_NULL_CONTENTS3 0xb0c ++#define PKT_NULL_CONTENTS4 0xb10 ++#define PKT_NULL_CONTENTS5 0xb14 ++#define PKT_NULL_CONTENTS6 0xb18 ++#define PKT_NULL_CONTENTS7 0xb1c ++#define PKT_ACP_CONTENTS0 0xb20 ++#define PKT_ACP_CONTENTS1 0xb24 ++#define PKT_ACP_CONTENTS2 0xb28 ++#define PKT_ACP_CONTENTS3 0xb2c ++#define PKT_ACP_CONTENTS4 0xb30 ++#define PKT_ACP_CONTENTS5 0xb34 ++#define PKT_ACP_CONTENTS6 0xb38 ++#define PKT_ACP_CONTENTS7 0xb3c ++#define PKT_ISRC1_CONTENTS0 0xb40 ++#define PKT_ISRC1_CONTENTS1 0xb44 ++#define PKT_ISRC1_CONTENTS2 0xb48 ++#define PKT_ISRC1_CONTENTS3 0xb4c ++#define PKT_ISRC1_CONTENTS4 0xb50 ++#define PKT_ISRC1_CONTENTS5 0xb54 ++#define PKT_ISRC1_CONTENTS6 0xb58 ++#define PKT_ISRC1_CONTENTS7 0xb5c ++#define PKT_ISRC2_CONTENTS0 0xb60 ++#define PKT_ISRC2_CONTENTS1 0xb64 ++#define PKT_ISRC2_CONTENTS2 0xb68 ++#define PKT_ISRC2_CONTENTS3 0xb6c ++#define PKT_ISRC2_CONTENTS4 0xb70 ++#define PKT_ISRC2_CONTENTS5 0xb74 ++#define PKT_ISRC2_CONTENTS6 0xb78 ++#define PKT_ISRC2_CONTENTS7 0xb7c ++#define PKT_GMD_CONTENTS0 0xb80 ++#define PKT_GMD_CONTENTS1 0xb84 ++#define PKT_GMD_CONTENTS2 0xb88 ++#define PKT_GMD_CONTENTS3 0xb8c ++#define PKT_GMD_CONTENTS4 0xb90 ++#define PKT_GMD_CONTENTS5 0xb94 ++#define PKT_GMD_CONTENTS6 0xb98 ++#define PKT_GMD_CONTENTS7 0xb9c ++#define PKT_AMD_CONTENTS0 0xba0 ++#define PKT_AMD_CONTENTS1 0xba4 ++#define PKT_AMD_CONTENTS2 0xba8 ++#define PKT_AMD_CONTENTS3 0xbac ++#define PKT_AMD_CONTENTS4 0xbb0 ++#define PKT_AMD_CONTENTS5 0xbb4 ++#define PKT_AMD_CONTENTS6 0xbb8 ++#define PKT_AMD_CONTENTS7 0xbbc ++#define PKT_VSI_CONTENTS0 0xbc0 ++#define PKT_VSI_CONTENTS1 0xbc4 ++#define PKT_VSI_CONTENTS2 0xbc8 ++#define PKT_VSI_CONTENTS3 0xbcc ++#define PKT_VSI_CONTENTS4 0xbd0 ++#define PKT_VSI_CONTENTS5 0xbd4 ++#define PKT_VSI_CONTENTS6 0xbd8 ++#define PKT_VSI_CONTENTS7 0xbdc ++#define PKT_AVI_CONTENTS0 0xbe0 ++#define HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT BIT(4) ++#define HDMI_FC_AVICONF0_BAR_DATA_VERT_BAR 0x04 ++#define HDMI_FC_AVICONF0_BAR_DATA_HORIZ_BAR 0x08 ++#define HDMI_FC_AVICONF2_IT_CONTENT_VALID 0x80 ++#define PKT_AVI_CONTENTS1 0xbe4 ++#define PKT_AVI_CONTENTS2 0xbe8 ++#define PKT_AVI_CONTENTS3 0xbec ++#define PKT_AVI_CONTENTS4 0xbf0 ++#define PKT_AVI_CONTENTS5 0xbf4 ++#define PKT_AVI_CONTENTS6 0xbf8 ++#define PKT_AVI_CONTENTS7 0xbfc ++#define PKT_SPDI_CONTENTS0 0xc00 ++#define PKT_SPDI_CONTENTS1 0xc04 ++#define PKT_SPDI_CONTENTS2 0xc08 ++#define PKT_SPDI_CONTENTS3 0xc0c ++#define PKT_SPDI_CONTENTS4 0xc10 ++#define PKT_SPDI_CONTENTS5 0xc14 ++#define PKT_SPDI_CONTENTS6 0xc18 ++#define PKT_SPDI_CONTENTS7 0xc1c ++#define PKT_AUDI_CONTENTS0 0xc20 ++#define PKT_AUDI_CONTENTS1 0xc24 ++#define PKT_AUDI_CONTENTS2 0xc28 ++#define PKT_AUDI_CONTENTS3 0xc2c ++#define PKT_AUDI_CONTENTS4 0xc30 ++#define PKT_AUDI_CONTENTS5 0xc34 ++#define PKT_AUDI_CONTENTS6 0xc38 ++#define PKT_AUDI_CONTENTS7 0xc3c ++#define PKT_NVI_CONTENTS0 0xc40 ++#define PKT_NVI_CONTENTS1 0xc44 ++#define PKT_NVI_CONTENTS2 0xc48 ++#define PKT_NVI_CONTENTS3 0xc4c ++#define PKT_NVI_CONTENTS4 0xc50 ++#define PKT_NVI_CONTENTS5 0xc54 ++#define PKT_NVI_CONTENTS6 0xc58 ++#define PKT_NVI_CONTENTS7 0xc5c ++#define PKT_DRMI_CONTENTS0 0xc60 ++#define PKT_DRMI_CONTENTS1 0xc64 ++#define PKT_DRMI_CONTENTS2 0xc68 ++#define PKT_DRMI_CONTENTS3 0xc6c ++#define PKT_DRMI_CONTENTS4 0xc70 ++#define PKT_DRMI_CONTENTS5 0xc74 ++#define PKT_DRMI_CONTENTS6 0xc78 ++#define PKT_DRMI_CONTENTS7 0xc7c ++#define PKT_GHDMI1_CONTENTS0 0xc80 ++#define PKT_GHDMI1_CONTENTS1 0xc84 ++#define PKT_GHDMI1_CONTENTS2 0xc88 ++#define PKT_GHDMI1_CONTENTS3 0xc8c ++#define PKT_GHDMI1_CONTENTS4 0xc90 ++#define PKT_GHDMI1_CONTENTS5 0xc94 ++#define PKT_GHDMI1_CONTENTS6 0xc98 ++#define PKT_GHDMI1_CONTENTS7 0xc9c ++#define PKT_GHDMI2_CONTENTS0 0xca0 ++#define PKT_GHDMI2_CONTENTS1 0xca4 ++#define PKT_GHDMI2_CONTENTS2 0xca8 ++#define PKT_GHDMI2_CONTENTS3 0xcac ++#define PKT_GHDMI2_CONTENTS4 0xcb0 ++#define PKT_GHDMI2_CONTENTS5 0xcb4 ++#define PKT_GHDMI2_CONTENTS6 0xcb8 ++#define PKT_GHDMI2_CONTENTS7 0xcbc ++/* EMP Packetizer Registers */ ++#define PKT_EMP_CONFIG0 0xce0 ++#define PKT_EMP_CONTROL0 0xcec ++#define PKT_EMP_CONTROL1 0xcf0 ++#define PKT_EMP_CONTROL2 0xcf4 ++#define PKT_EMP_VTEM_CONTENTS0 0xd00 ++#define PKT_EMP_VTEM_CONTENTS1 0xd04 ++#define PKT_EMP_VTEM_CONTENTS2 0xd08 ++#define PKT_EMP_VTEM_CONTENTS3 0xd0c ++#define PKT_EMP_VTEM_CONTENTS4 0xd10 ++#define PKT_EMP_VTEM_CONTENTS5 0xd14 ++#define PKT_EMP_VTEM_CONTENTS6 0xd18 ++#define PKT_EMP_VTEM_CONTENTS7 0xd1c ++#define PKT0_EMP_CVTEM_CONTENTS0 0xd20 ++#define PKT0_EMP_CVTEM_CONTENTS1 0xd24 ++#define PKT0_EMP_CVTEM_CONTENTS2 0xd28 ++#define PKT0_EMP_CVTEM_CONTENTS3 0xd2c ++#define PKT0_EMP_CVTEM_CONTENTS4 0xd30 ++#define PKT0_EMP_CVTEM_CONTENTS5 0xd34 ++#define PKT0_EMP_CVTEM_CONTENTS6 0xd38 ++#define PKT0_EMP_CVTEM_CONTENTS7 0xd3c ++#define PKT1_EMP_CVTEM_CONTENTS0 0xd40 ++#define PKT1_EMP_CVTEM_CONTENTS1 0xd44 ++#define PKT1_EMP_CVTEM_CONTENTS2 0xd48 ++#define PKT1_EMP_CVTEM_CONTENTS3 0xd4c ++#define PKT1_EMP_CVTEM_CONTENTS4 0xd50 ++#define PKT1_EMP_CVTEM_CONTENTS5 0xd54 ++#define PKT1_EMP_CVTEM_CONTENTS6 0xd58 ++#define PKT1_EMP_CVTEM_CONTENTS7 0xd5c ++#define PKT2_EMP_CVTEM_CONTENTS0 0xd60 ++#define PKT2_EMP_CVTEM_CONTENTS1 0xd64 ++#define PKT2_EMP_CVTEM_CONTENTS2 0xd68 ++#define PKT2_EMP_CVTEM_CONTENTS3 0xd6c ++#define PKT2_EMP_CVTEM_CONTENTS4 0xd70 ++#define PKT2_EMP_CVTEM_CONTENTS5 0xd74 ++#define PKT2_EMP_CVTEM_CONTENTS6 0xd78 ++#define PKT2_EMP_CVTEM_CONTENTS7 0xd7c ++#define PKT3_EMP_CVTEM_CONTENTS0 0xd80 ++#define PKT3_EMP_CVTEM_CONTENTS1 0xd84 ++#define PKT3_EMP_CVTEM_CONTENTS2 0xd88 ++#define PKT3_EMP_CVTEM_CONTENTS3 0xd8c ++#define PKT3_EMP_CVTEM_CONTENTS4 0xd90 ++#define PKT3_EMP_CVTEM_CONTENTS5 0xd94 ++#define PKT3_EMP_CVTEM_CONTENTS6 0xd98 ++#define PKT3_EMP_CVTEM_CONTENTS7 0xd9c ++#define PKT4_EMP_CVTEM_CONTENTS0 0xda0 ++#define PKT4_EMP_CVTEM_CONTENTS1 0xda4 ++#define PKT4_EMP_CVTEM_CONTENTS2 0xda8 ++#define PKT4_EMP_CVTEM_CONTENTS3 0xdac ++#define PKT4_EMP_CVTEM_CONTENTS4 0xdb0 ++#define PKT4_EMP_CVTEM_CONTENTS5 0xdb4 ++#define PKT4_EMP_CVTEM_CONTENTS6 0xdb8 ++#define PKT4_EMP_CVTEM_CONTENTS7 0xdbc ++#define PKT5_EMP_CVTEM_CONTENTS0 0xdc0 ++#define PKT5_EMP_CVTEM_CONTENTS1 0xdc4 ++#define PKT5_EMP_CVTEM_CONTENTS2 0xdc8 ++#define PKT5_EMP_CVTEM_CONTENTS3 0xdcc ++#define PKT5_EMP_CVTEM_CONTENTS4 0xdd0 ++#define PKT5_EMP_CVTEM_CONTENTS5 0xdd4 ++#define PKT5_EMP_CVTEM_CONTENTS6 0xdd8 ++#define PKT5_EMP_CVTEM_CONTENTS7 0xddc ++/* Audio Packetizer Registers */ ++#define AUDPKT_CONTROL0 0xe20 ++#define AUDPKT_PBIT_FORCE_EN_MASK BIT(12) ++#define AUDPKT_PBIT_FORCE_EN BIT(12) ++#define AUDPKT_CHSTATUS_OVR_EN_MASK BIT(0) ++#define AUDPKT_CHSTATUS_OVR_EN BIT(0) ++#define AUDPKT_CONTROL1 0xe24 ++#define AUDPKT_ACR_CONTROL0 0xe40 ++#define AUDPKT_ACR_N_VALUE 0xfffff ++#define AUDPKT_ACR_CONTROL1 0xe44 ++#define AUDPKT_ACR_CTS_OVR_VAL_MSK GENMASK(23, 4) ++#define AUDPKT_ACR_CTS_OVR_VAL(x) ((x) << 4) ++#define AUDPKT_ACR_CTS_OVR_EN_MSK BIT(1) ++#define AUDPKT_ACR_CTS_OVR_EN BIT(1) ++#define AUDPKT_ACR_STATUS0 0xe4c ++#define AUDPKT_CHSTATUS_OVR0 0xe60 ++#define AUDPKT_CHSTATUS_OVR1 0xe64 ++/* IEC60958 Byte 3: Sampleing frenuency Bits 24 to 27 */ ++#define AUDPKT_CHSTATUS_SR_MASK GENMASK(3, 0) ++#define AUDPKT_CHSTATUS_SR_22050 0x4 ++#define AUDPKT_CHSTATUS_SR_24000 0x6 ++#define AUDPKT_CHSTATUS_SR_32000 0x3 ++#define AUDPKT_CHSTATUS_SR_44100 0x0 ++#define AUDPKT_CHSTATUS_SR_48000 0x2 ++#define AUDPKT_CHSTATUS_SR_88200 0x8 ++#define AUDPKT_CHSTATUS_SR_96000 0xa ++#define AUDPKT_CHSTATUS_SR_176400 0xc ++#define AUDPKT_CHSTATUS_SR_192000 0xe ++#define AUDPKT_CHSTATUS_SR_768000 0x9 ++#define AUDPKT_CHSTATUS_SR_NOT_INDICATED 0x1 ++/* IEC60958 Byte 4: Original Sampleing frenuency Bits 36 to 39 */ ++#define AUDPKT_CHSTATUS_0SR_MASK GENMASK(15, 12) ++#define AUDPKT_CHSTATUS_OSR_8000 0x6 ++#define AUDPKT_CHSTATUS_OSR_11025 0xa ++#define AUDPKT_CHSTATUS_OSR_12000 0x2 ++#define AUDPKT_CHSTATUS_OSR_16000 0x8 ++#define AUDPKT_CHSTATUS_OSR_22050 0xb ++#define AUDPKT_CHSTATUS_OSR_24000 0x9 ++#define AUDPKT_CHSTATUS_OSR_32000 0xc ++#define AUDPKT_CHSTATUS_OSR_44100 0xf ++#define AUDPKT_CHSTATUS_OSR_48000 0xd ++#define AUDPKT_CHSTATUS_OSR_88200 0x7 ++#define AUDPKT_CHSTATUS_OSR_96000 0x5 ++#define AUDPKT_CHSTATUS_OSR_176400 0x3 ++#define AUDPKT_CHSTATUS_OSR_192000 0x1 ++#define AUDPKT_CHSTATUS_OSR_NOT_INDICATED 0x0 ++#define AUDPKT_CHSTATUS_OVR2 0xe68 ++#define AUDPKT_CHSTATUS_OVR3 0xe6c ++#define AUDPKT_CHSTATUS_OVR4 0xe70 ++#define AUDPKT_CHSTATUS_OVR5 0xe74 ++#define AUDPKT_CHSTATUS_OVR6 0xe78 ++#define AUDPKT_CHSTATUS_OVR7 0xe7c ++#define AUDPKT_CHSTATUS_OVR8 0xe80 ++#define AUDPKT_CHSTATUS_OVR9 0xe84 ++#define AUDPKT_CHSTATUS_OVR10 0xe88 ++#define AUDPKT_CHSTATUS_OVR11 0xe8c ++#define AUDPKT_CHSTATUS_OVR12 0xe90 ++#define AUDPKT_CHSTATUS_OVR13 0xe94 ++#define AUDPKT_CHSTATUS_OVR14 0xe98 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC0 0xea0 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC1 0xea4 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC2 0xea8 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC3 0xeac ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC4 0xeb0 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC5 0xeb4 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC6 0xeb8 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC7 0xebc ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC8 0xec0 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC9 0xec4 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC10 0xec8 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC11 0xecc ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC12 0xed0 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC13 0xed4 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC14 0xed8 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC15 0xedc ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC16 0xee0 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC17 0xee4 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC18 0xee8 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC19 0xeec ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC20 0xef0 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC21 0xef4 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC22 0xef8 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC23 0xefc ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC24 0xf00 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC25 0xf04 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC26 0xf08 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC27 0xf0c ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC28 0xf10 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC29 0xf14 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC30 0xf18 ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC31 0xf1c ++#define AUDPKT_USRDATA_OVR_MSG_GENERIC32 0xf20 ++#define AUDPKT_VBIT_OVR0 0xf24 ++/* CEC Registers */ ++#define CEC_TX_CONTROL 0x1000 ++#define CEC_STATUS 0x1004 ++#define CEC_CONFIG 0x1008 ++#define CEC_ADDR 0x100c ++#define CEC_TX_COUNT 0x1020 ++#define CEC_TX_DATA3_0 0x1024 ++#define CEC_TX_DATA7_4 0x1028 ++#define CEC_TX_DATA11_8 0x102c ++#define CEC_TX_DATA15_12 0x1030 ++#define CEC_RX_COUNT_STATUS 0x1040 ++#define CEC_RX_DATA3_0 0x1044 ++#define CEC_RX_DATA7_4 0x1048 ++#define CEC_RX_DATA11_8 0x104c ++#define CEC_RX_DATA15_12 0x1050 ++#define CEC_LOCK_CONTROL 0x1054 ++#define CEC_RXQUAL_BITTIME_CONFIG 0x1060 ++#define CEC_RX_BITTIME_CONFIG 0x1064 ++#define CEC_TX_BITTIME_CONFIG 0x1068 ++/* eARC RX CMDC Registers */ ++#define EARCRX_CMDC_CONFIG0 0x1800 ++#define EARCRX_XACTREAD_STOP_CFG BIT(26) ++#define EARCRX_XACTREAD_RETRY_CFG BIT(25) ++#define EARCRX_CMDC_DSCVR_EARCVALID0_TO_DISC1 BIT(24) ++#define EARCRX_CMDC_XACT_RESTART_EN BIT(18) ++#define EARCRX_CMDC_CONFIG1 0x1804 ++#define EARCRX_CMDC_CONTROL 0x1808 ++#define EARCRX_CMDC_HEARTBEAT_LOSS_EN BIT(4) ++#define EARCRX_CMDC_DISCOVERY_EN BIT(3) ++#define EARCRX_CONNECTOR_HPD BIT(1) ++#define EARCRX_CMDC_WHITELIST0_CONFIG 0x180c ++#define EARCRX_CMDC_WHITELIST1_CONFIG 0x1810 ++#define EARCRX_CMDC_WHITELIST2_CONFIG 0x1814 ++#define EARCRX_CMDC_WHITELIST3_CONFIG 0x1818 ++#define EARCRX_CMDC_STATUS 0x181c ++#define EARCRX_CMDC_XACT_INFO 0x1820 ++#define EARCRX_CMDC_XACT_ACTION 0x1824 ++#define EARCRX_CMDC_HEARTBEAT_RXSTAT_SE 0x1828 ++#define EARCRX_CMDC_HEARTBEAT_STATUS 0x182c ++#define EARCRX_CMDC_XACT_WR0 0x1840 ++#define EARCRX_CMDC_XACT_WR1 0x1844 ++#define EARCRX_CMDC_XACT_WR2 0x1848 ++#define EARCRX_CMDC_XACT_WR3 0x184c ++#define EARCRX_CMDC_XACT_WR4 0x1850 ++#define EARCRX_CMDC_XACT_WR5 0x1854 ++#define EARCRX_CMDC_XACT_WR6 0x1858 ++#define EARCRX_CMDC_XACT_WR7 0x185c ++#define EARCRX_CMDC_XACT_WR8 0x1860 ++#define EARCRX_CMDC_XACT_WR9 0x1864 ++#define EARCRX_CMDC_XACT_WR10 0x1868 ++#define EARCRX_CMDC_XACT_WR11 0x186c ++#define EARCRX_CMDC_XACT_WR12 0x1870 ++#define EARCRX_CMDC_XACT_WR13 0x1874 ++#define EARCRX_CMDC_XACT_WR14 0x1878 ++#define EARCRX_CMDC_XACT_WR15 0x187c ++#define EARCRX_CMDC_XACT_WR16 0x1880 ++#define EARCRX_CMDC_XACT_WR17 0x1884 ++#define EARCRX_CMDC_XACT_WR18 0x1888 ++#define EARCRX_CMDC_XACT_WR19 0x188c ++#define EARCRX_CMDC_XACT_WR20 0x1890 ++#define EARCRX_CMDC_XACT_WR21 0x1894 ++#define EARCRX_CMDC_XACT_WR22 0x1898 ++#define EARCRX_CMDC_XACT_WR23 0x189c ++#define EARCRX_CMDC_XACT_WR24 0x18a0 ++#define EARCRX_CMDC_XACT_WR25 0x18a4 ++#define EARCRX_CMDC_XACT_WR26 0x18a8 ++#define EARCRX_CMDC_XACT_WR27 0x18ac ++#define EARCRX_CMDC_XACT_WR28 0x18b0 ++#define EARCRX_CMDC_XACT_WR29 0x18b4 ++#define EARCRX_CMDC_XACT_WR30 0x18b8 ++#define EARCRX_CMDC_XACT_WR31 0x18bc ++#define EARCRX_CMDC_XACT_WR32 0x18c0 ++#define EARCRX_CMDC_XACT_WR33 0x18c4 ++#define EARCRX_CMDC_XACT_WR34 0x18c8 ++#define EARCRX_CMDC_XACT_WR35 0x18cc ++#define EARCRX_CMDC_XACT_WR36 0x18d0 ++#define EARCRX_CMDC_XACT_WR37 0x18d4 ++#define EARCRX_CMDC_XACT_WR38 0x18d8 ++#define EARCRX_CMDC_XACT_WR39 0x18dc ++#define EARCRX_CMDC_XACT_WR40 0x18e0 ++#define EARCRX_CMDC_XACT_WR41 0x18e4 ++#define EARCRX_CMDC_XACT_WR42 0x18e8 ++#define EARCRX_CMDC_XACT_WR43 0x18ec ++#define EARCRX_CMDC_XACT_WR44 0x18f0 ++#define EARCRX_CMDC_XACT_WR45 0x18f4 ++#define EARCRX_CMDC_XACT_WR46 0x18f8 ++#define EARCRX_CMDC_XACT_WR47 0x18fc ++#define EARCRX_CMDC_XACT_WR48 0x1900 ++#define EARCRX_CMDC_XACT_WR49 0x1904 ++#define EARCRX_CMDC_XACT_WR50 0x1908 ++#define EARCRX_CMDC_XACT_WR51 0x190c ++#define EARCRX_CMDC_XACT_WR52 0x1910 ++#define EARCRX_CMDC_XACT_WR53 0x1914 ++#define EARCRX_CMDC_XACT_WR54 0x1918 ++#define EARCRX_CMDC_XACT_WR55 0x191c ++#define EARCRX_CMDC_XACT_WR56 0x1920 ++#define EARCRX_CMDC_XACT_WR57 0x1924 ++#define EARCRX_CMDC_XACT_WR58 0x1928 ++#define EARCRX_CMDC_XACT_WR59 0x192c ++#define EARCRX_CMDC_XACT_WR60 0x1930 ++#define EARCRX_CMDC_XACT_WR61 0x1934 ++#define EARCRX_CMDC_XACT_WR62 0x1938 ++#define EARCRX_CMDC_XACT_WR63 0x193c ++#define EARCRX_CMDC_XACT_WR64 0x1940 ++#define EARCRX_CMDC_XACT_RD0 0x1960 ++#define EARCRX_CMDC_XACT_RD1 0x1964 ++#define EARCRX_CMDC_XACT_RD2 0x1968 ++#define EARCRX_CMDC_XACT_RD3 0x196c ++#define EARCRX_CMDC_XACT_RD4 0x1970 ++#define EARCRX_CMDC_XACT_RD5 0x1974 ++#define EARCRX_CMDC_XACT_RD6 0x1978 ++#define EARCRX_CMDC_XACT_RD7 0x197c ++#define EARCRX_CMDC_XACT_RD8 0x1980 ++#define EARCRX_CMDC_XACT_RD9 0x1984 ++#define EARCRX_CMDC_XACT_RD10 0x1988 ++#define EARCRX_CMDC_XACT_RD11 0x198c ++#define EARCRX_CMDC_XACT_RD12 0x1990 ++#define EARCRX_CMDC_XACT_RD13 0x1994 ++#define EARCRX_CMDC_XACT_RD14 0x1998 ++#define EARCRX_CMDC_XACT_RD15 0x199c ++#define EARCRX_CMDC_XACT_RD16 0x19a0 ++#define EARCRX_CMDC_XACT_RD17 0x19a4 ++#define EARCRX_CMDC_XACT_RD18 0x19a8 ++#define EARCRX_CMDC_XACT_RD19 0x19ac ++#define EARCRX_CMDC_XACT_RD20 0x19b0 ++#define EARCRX_CMDC_XACT_RD21 0x19b4 ++#define EARCRX_CMDC_XACT_RD22 0x19b8 ++#define EARCRX_CMDC_XACT_RD23 0x19bc ++#define EARCRX_CMDC_XACT_RD24 0x19c0 ++#define EARCRX_CMDC_XACT_RD25 0x19c4 ++#define EARCRX_CMDC_XACT_RD26 0x19c8 ++#define EARCRX_CMDC_XACT_RD27 0x19cc ++#define EARCRX_CMDC_XACT_RD28 0x19d0 ++#define EARCRX_CMDC_XACT_RD29 0x19d4 ++#define EARCRX_CMDC_XACT_RD30 0x19d8 ++#define EARCRX_CMDC_XACT_RD31 0x19dc ++#define EARCRX_CMDC_XACT_RD32 0x19e0 ++#define EARCRX_CMDC_XACT_RD33 0x19e4 ++#define EARCRX_CMDC_XACT_RD34 0x19e8 ++#define EARCRX_CMDC_XACT_RD35 0x19ec ++#define EARCRX_CMDC_XACT_RD36 0x19f0 ++#define EARCRX_CMDC_XACT_RD37 0x19f4 ++#define EARCRX_CMDC_XACT_RD38 0x19f8 ++#define EARCRX_CMDC_XACT_RD39 0x19fc ++#define EARCRX_CMDC_XACT_RD40 0x1a00 ++#define EARCRX_CMDC_XACT_RD41 0x1a04 ++#define EARCRX_CMDC_XACT_RD42 0x1a08 ++#define EARCRX_CMDC_XACT_RD43 0x1a0c ++#define EARCRX_CMDC_XACT_RD44 0x1a10 ++#define EARCRX_CMDC_XACT_RD45 0x1a14 ++#define EARCRX_CMDC_XACT_RD46 0x1a18 ++#define EARCRX_CMDC_XACT_RD47 0x1a1c ++#define EARCRX_CMDC_XACT_RD48 0x1a20 ++#define EARCRX_CMDC_XACT_RD49 0x1a24 ++#define EARCRX_CMDC_XACT_RD50 0x1a28 ++#define EARCRX_CMDC_XACT_RD51 0x1a2c ++#define EARCRX_CMDC_XACT_RD52 0x1a30 ++#define EARCRX_CMDC_XACT_RD53 0x1a34 ++#define EARCRX_CMDC_XACT_RD54 0x1a38 ++#define EARCRX_CMDC_XACT_RD55 0x1a3c ++#define EARCRX_CMDC_XACT_RD56 0x1a40 ++#define EARCRX_CMDC_XACT_RD57 0x1a44 ++#define EARCRX_CMDC_XACT_RD58 0x1a48 ++#define EARCRX_CMDC_XACT_RD59 0x1a4c ++#define EARCRX_CMDC_XACT_RD60 0x1a50 ++#define EARCRX_CMDC_XACT_RD61 0x1a54 ++#define EARCRX_CMDC_XACT_RD62 0x1a58 ++#define EARCRX_CMDC_XACT_RD63 0x1a5c ++#define EARCRX_CMDC_XACT_RD64 0x1a60 ++#define EARCRX_CMDC_SYNC_CONFIG 0x1b00 ++/* eARC RX DMAC Registers */ ++#define EARCRX_DMAC_PHY_CONTROL 0x1c00 ++#define EARCRX_DMAC_CONFIG 0x1c08 ++#define EARCRX_DMAC_CONTROL0 0x1c0c ++#define EARCRX_DMAC_AUDIO_EN BIT(1) ++#define EARCRX_DMAC_EN BIT(0) ++#define EARCRX_DMAC_CONTROL1 0x1c10 ++#define EARCRX_DMAC_STATUS 0x1c14 ++#define EARCRX_DMAC_CHSTATUS0 0x1c18 ++#define EARCRX_DMAC_CHSTATUS1 0x1c1c ++#define EARCRX_DMAC_CHSTATUS2 0x1c20 ++#define EARCRX_DMAC_CHSTATUS3 0x1c24 ++#define EARCRX_DMAC_CHSTATUS4 0x1c28 ++#define EARCRX_DMAC_CHSTATUS5 0x1c2c ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC0 0x1c30 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC1 0x1c34 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC2 0x1c38 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC3 0x1c3c ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC4 0x1c40 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC5 0x1c44 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC6 0x1c48 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC7 0x1c4c ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC8 0x1c50 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC9 0x1c54 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC10 0x1c58 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC11 0x1c5c ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT0 0x1c60 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT1 0x1c64 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT2 0x1c68 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT3 0x1c6c ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT4 0x1c70 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT5 0x1c74 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT6 0x1c78 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT7 0x1c7c ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT8 0x1c80 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT9 0x1c84 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT10 0x1c88 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT11 0x1c8c ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT0 0x1c90 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT1 0x1c94 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT2 0x1c98 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT3 0x1c9c ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT4 0x1ca0 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT5 0x1ca4 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT6 0x1ca8 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT7 0x1cac ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT8 0x1cb0 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT9 0x1cb4 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT10 0x1cb8 ++#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT11 0x1cbc ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC0 0x1cc0 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC1 0x1cc4 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC2 0x1cc8 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC3 0x1ccc ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC4 0x1cd0 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC5 0x1cd4 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC6 0x1cd8 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC7 0x1cdc ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC8 0x1ce0 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC9 0x1ce4 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC10 0x1ce8 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC11 0x1cec ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC12 0x1cf0 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC13 0x1cf4 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC14 0x1cf8 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC15 0x1cfc ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC16 0x1d00 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC17 0x1d04 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC18 0x1d08 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC19 0x1d0c ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC20 0x1d10 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC21 0x1d14 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC22 0x1d18 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC23 0x1d1c ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC24 0x1d20 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC25 0x1d24 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC26 0x1d28 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC27 0x1d2c ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC28 0x1d30 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC29 0x1d34 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC30 0x1d38 ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC31 0x1d3c ++#define EARCRX_DMAC_USRDATA_MSG_GENERIC32 0x1d40 ++#define EARCRX_DMAC_CHSTATUS_STREAMER0 0x1d44 ++#define EARCRX_DMAC_CHSTATUS_STREAMER1 0x1d48 ++#define EARCRX_DMAC_CHSTATUS_STREAMER2 0x1d4c ++#define EARCRX_DMAC_CHSTATUS_STREAMER3 0x1d50 ++#define EARCRX_DMAC_CHSTATUS_STREAMER4 0x1d54 ++#define EARCRX_DMAC_CHSTATUS_STREAMER5 0x1d58 ++#define EARCRX_DMAC_CHSTATUS_STREAMER6 0x1d5c ++#define EARCRX_DMAC_CHSTATUS_STREAMER7 0x1d60 ++#define EARCRX_DMAC_CHSTATUS_STREAMER8 0x1d64 ++#define EARCRX_DMAC_CHSTATUS_STREAMER9 0x1d68 ++#define EARCRX_DMAC_CHSTATUS_STREAMER10 0x1d6c ++#define EARCRX_DMAC_CHSTATUS_STREAMER11 0x1d70 ++#define EARCRX_DMAC_CHSTATUS_STREAMER12 0x1d74 ++#define EARCRX_DMAC_CHSTATUS_STREAMER13 0x1d78 ++#define EARCRX_DMAC_CHSTATUS_STREAMER14 0x1d7c ++#define EARCRX_DMAC_USRDATA_STREAMER0 0x1d80 ++/* Main Unit Interrupt Registers */ ++#define MAIN_INTVEC_INDEX 0x3000 ++#define MAINUNIT_0_INT_STATUS 0x3010 ++#define MAINUNIT_0_INT_MASK_N 0x3014 ++#define MAINUNIT_0_INT_CLEAR 0x3018 ++#define MAINUNIT_0_INT_FORCE 0x301c ++#define MAINUNIT_1_INT_STATUS 0x3020 ++#define FLT_EXIT_TO_LTSL_IRQ BIT(22) ++#define FLT_EXIT_TO_LTS4_IRQ BIT(21) ++#define FLT_EXIT_TO_LTSP_IRQ BIT(20) ++#define SCDC_NACK_RCVD_IRQ BIT(12) ++#define SCDC_RR_REPLY_STOP_IRQ BIT(11) ++#define SCDC_UPD_FLAGS_CLR_IRQ BIT(10) ++#define SCDC_UPD_FLAGS_CHG_IRQ BIT(9) ++#define SCDC_UPD_FLAGS_RD_IRQ BIT(8) ++#define I2CM_NACK_RCVD_IRQ BIT(2) ++#define I2CM_READ_REQUEST_IRQ BIT(1) ++#define I2CM_OP_DONE_IRQ BIT(0) ++#define MAINUNIT_1_INT_MASK_N 0x3024 ++#define I2CM_NACK_RCVD_MASK_N BIT(2) ++#define I2CM_READ_REQUEST_MASK_N BIT(1) ++#define I2CM_OP_DONE_MASK_N BIT(0) ++#define MAINUNIT_1_INT_CLEAR 0x3028 ++#define I2CM_NACK_RCVD_CLEAR BIT(2) ++#define I2CM_READ_REQUEST_CLEAR BIT(1) ++#define I2CM_OP_DONE_CLEAR BIT(0) ++#define MAINUNIT_1_INT_FORCE 0x302c ++/* AVPUNIT Interrupt Registers */ ++#define AVP_INTVEC_INDEX 0x3800 ++#define AVP_0_INT_STATUS 0x3810 ++#define AVP_0_INT_MASK_N 0x3814 ++#define AVP_0_INT_CLEAR 0x3818 ++#define AVP_0_INT_FORCE 0x381c ++#define AVP_1_INT_STATUS 0x3820 ++#define AVP_1_INT_MASK_N 0x3824 ++#define HDCP14_AUTH_CHG_MASK_N BIT(6) ++#define AVP_1_INT_CLEAR 0x3828 ++#define AVP_1_INT_FORCE 0x382c ++#define AVP_2_INT_STATUS 0x3830 ++#define AVP_2_INT_MASK_N 0x3834 ++#define AVP_2_INT_CLEAR 0x3838 ++#define AVP_2_INT_FORCE 0x383c ++#define AVP_3_INT_STATUS 0x3840 ++#define AVP_3_INT_MASK_N 0x3844 ++#define AVP_3_INT_CLEAR 0x3848 ++#define AVP_3_INT_FORCE 0x384c ++#define AVP_4_INT_STATUS 0x3850 ++#define AVP_4_INT_MASK_N 0x3854 ++#define AVP_4_INT_CLEAR 0x3858 ++#define AVP_4_INT_FORCE 0x385c ++#define AVP_5_INT_STATUS 0x3860 ++#define AVP_5_INT_MASK_N 0x3864 ++#define AVP_5_INT_CLEAR 0x3868 ++#define AVP_5_INT_FORCE 0x386c ++#define AVP_6_INT_STATUS 0x3870 ++#define AVP_6_INT_MASK_N 0x3874 ++#define AVP_6_INT_CLEAR 0x3878 ++#define AVP_6_INT_FORCE 0x387c ++/* CEC Interrupt Registers */ ++#define CEC_INT_STATUS 0x4000 ++#define CEC_INT_MASK_N 0x4004 ++#define CEC_INT_CLEAR 0x4008 ++#define CEC_INT_FORCE 0x400c ++/* eARC RX Interrupt Registers */ ++#define EARCRX_INTVEC_INDEX 0x4800 ++#define EARCRX_0_INT_STATUS 0x4810 ++#define EARCRX_CMDC_DISCOVERY_TIMEOUT_IRQ BIT(9) ++#define EARCRX_CMDC_DISCOVERY_DONE_IRQ BIT(8) ++#define EARCRX_0_INT_MASK_N 0x4814 ++#define EARCRX_0_INT_CLEAR 0x4818 ++#define EARCRX_0_INT_FORCE 0x481c ++#define EARCRX_1_INT_STATUS 0x4820 ++#define EARCRX_1_INT_MASK_N 0x4824 ++#define EARCRX_1_INT_CLEAR 0x4828 ++#define EARCRX_1_INT_FORCE 0x482c ++ ++#endif /* __DW_HDMI_QP_H__ */ +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +index 52d91a0df85e..0d6fd9578a5a 100644 +--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c ++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +@@ -162,6 +162,8 @@ struct dw_hdmi { + void __iomem *regs; + bool sink_is_hdmi; + bool sink_has_audio; ++ bool support_hdmi; ++ int force_output; + + struct pinctrl *pinctrl; + struct pinctrl_state *default_state; +@@ -254,6 +256,25 @@ static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg, + hdmi_modb(hdmi, data << shift, mask, reg); + } + ++static bool dw_hdmi_check_output_type_changed(struct dw_hdmi *hdmi) ++{ ++ bool sink_hdmi; ++ ++ sink_hdmi = hdmi->sink_is_hdmi; ++ ++ if (hdmi->force_output == 1) ++ hdmi->sink_is_hdmi = true; ++ else if (hdmi->force_output == 2) ++ hdmi->sink_is_hdmi = false; ++ else ++ hdmi->sink_is_hdmi = hdmi->support_hdmi; ++ ++ if (sink_hdmi != hdmi->sink_is_hdmi) ++ return true; ++ ++ return false; ++} ++ + static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi) + { + hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL, +@@ -2532,6 +2553,45 @@ static int dw_hdmi_connector_atomic_check(struct drm_connector *connector, + return 0; + } + ++void dw_hdmi_set_quant_range(struct dw_hdmi *hdmi) ++{ ++ if (!hdmi->bridge_is_on) ++ return; ++ ++ hdmi_writeb(hdmi, HDMI_FC_GCP_SET_AVMUTE, HDMI_FC_GCP); ++ dw_hdmi_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode); ++ hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_set_quant_range); ++ ++void dw_hdmi_set_output_type(struct dw_hdmi *hdmi, u64 val) ++{ ++ hdmi->force_output = val; ++ ++ if (!dw_hdmi_check_output_type_changed(hdmi)) ++ return; ++ ++ if (!hdmi->bridge_is_on) ++ return; ++ ++ hdmi_writeb(hdmi, HDMI_FC_GCP_SET_AVMUTE, HDMI_FC_GCP); ++ dw_hdmi_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode); ++ hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_set_output_type); ++ ++bool dw_hdmi_get_output_whether_hdmi(struct dw_hdmi *hdmi) ++{ ++ return hdmi->sink_is_hdmi; ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_get_output_whether_hdmi); ++ ++int dw_hdmi_get_output_type_cap(struct dw_hdmi *hdmi) ++{ ++ return hdmi->support_hdmi; ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_get_output_type_cap); ++ + static void dw_hdmi_connector_force(struct drm_connector *connector) + { + struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, +@@ -3683,6 +3743,35 @@ void dw_hdmi_unbind(struct dw_hdmi *hdmi) + } + EXPORT_SYMBOL_GPL(dw_hdmi_unbind); + ++void dw_hdmi_suspend(struct dw_hdmi *hdmi) ++{ ++ if (!hdmi) ++ return; ++ ++ mutex_lock(&hdmi->mutex); ++ ++ /* ++ * When system shutdown, hdmi should be disabled. ++ * When system suspend, dw_hdmi_bridge_disable will disable hdmi first. ++ * To prevent duplicate operation, we should determine whether hdmi ++ * has been disabled. ++ */ ++ if (!hdmi->disabled) { ++ hdmi->disabled = true; ++ dw_hdmi_update_power(hdmi); ++ dw_hdmi_update_phy_mask(hdmi); ++ } ++ mutex_unlock(&hdmi->mutex); ++ ++ //[CC: needed?] ++ // if (hdmi->irq) ++ // disable_irq(hdmi->irq); ++ // cancel_delayed_work(&hdmi->work); ++ // flush_workqueue(hdmi->workqueue); ++ pinctrl_pm_select_sleep_state(hdmi->dev); ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_suspend); ++ + void dw_hdmi_resume(struct dw_hdmi *hdmi) + { + dw_hdmi_init_hw(hdmi); +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h +index af43a0414b78..8ebdec7254f2 100644 +--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h ++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h +@@ -851,6 +851,10 @@ enum { + HDMI_FC_AVICONF3_QUANT_RANGE_LIMITED = 0x00, + HDMI_FC_AVICONF3_QUANT_RANGE_FULL = 0x04, + ++/* HDMI_FC_GCP */ ++ HDMI_FC_GCP_SET_AVMUTE = 0x2, ++ HDMI_FC_GCP_CLEAR_AVMUTE = 0x1, ++ + /* FC_DBGFORCE field values */ + HDMI_FC_DBGFORCE_FORCEAUDIO = 0x10, + HDMI_FC_DBGFORCE_FORCEVIDEO = 0x1, +diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +index 341550199111..52f457b16900 100644 +--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c ++++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +@@ -4,22 +4,33 @@ + */ + + #include ++#include ++#include + #include + #include + #include + #include ++#include + #include + #include + ++#include ++#include ++#include ++#include + #include + #include + #include + #include + #include + ++#include ++ + #include "rockchip_drm_drv.h" + #include "rockchip_drm_vop.h" + ++#define HIWORD_UPDATE(val, mask) (val | (mask) << 16) ++ + #define RK3228_GRF_SOC_CON2 0x0408 + #define RK3228_HDMI_SDAIN_MSK BIT(14) + #define RK3228_HDMI_SCLIN_MSK BIT(13) +@@ -30,8 +41,11 @@ + + #define RK3288_GRF_SOC_CON6 0x025C + #define RK3288_HDMI_LCDC_SEL BIT(4) +-#define RK3328_GRF_SOC_CON2 0x0408 ++#define RK3288_GRF_SOC_CON16 0x03a8 ++#define RK3288_HDMI_LCDC0_YUV420 BIT(2) ++#define RK3288_HDMI_LCDC1_YUV420 BIT(3) + ++#define RK3328_GRF_SOC_CON2 0x0408 + #define RK3328_HDMI_SDAIN_MSK BIT(11) + #define RK3328_HDMI_SCLIN_MSK BIT(10) + #define RK3328_HDMI_HPD_IOE BIT(2) +@@ -55,32 +69,154 @@ + #define RK3568_HDMI_SDAIN_MSK BIT(15) + #define RK3568_HDMI_SCLIN_MSK BIT(14) + +-#define HIWORD_UPDATE(val, mask) (val | (mask) << 16) ++#define RK3588_GRF_SOC_CON2 0x0308 ++#define RK3588_HDMI1_HPD_INT_MSK BIT(15) ++#define RK3588_HDMI1_HPD_INT_CLR BIT(14) ++#define RK3588_HDMI0_HPD_INT_MSK BIT(13) ++#define RK3588_HDMI0_HPD_INT_CLR BIT(12) ++#define RK3588_GRF_SOC_CON7 0x031c ++#define RK3588_SET_HPD_PATH_MASK (0x3 << 12) ++#define RK3588_GRF_SOC_STATUS1 0x0384 ++#define RK3588_HDMI0_LOW_MORETHAN100MS BIT(20) ++#define RK3588_HDMI0_HPD_PORT_LEVEL BIT(19) ++#define RK3588_HDMI0_IHPD_PORT BIT(18) ++#define RK3588_HDMI0_OHPD_INT BIT(17) ++#define RK3588_HDMI0_LEVEL_INT BIT(16) ++#define RK3588_HDMI0_INTR_CHANGE_CNT (0x7 << 13) ++#define RK3588_HDMI1_LOW_MORETHAN100MS BIT(28) ++#define RK3588_HDMI1_HPD_PORT_LEVEL BIT(27) ++#define RK3588_HDMI1_IHPD_PORT BIT(26) ++#define RK3588_HDMI1_OHPD_INT BIT(25) ++#define RK3588_HDMI1_LEVEL_INT BIT(24) ++#define RK3588_HDMI1_INTR_CHANGE_CNT (0x7 << 21) ++ ++#define RK3588_GRF_VO1_CON3 0x000c ++#define RK3588_COLOR_FORMAT_MASK 0xf ++#define RK3588_YUV444 0x2 ++#define RK3588_YUV420 0x3 ++#define RK3588_COMPRESSED_DATA 0xb ++#define RK3588_COLOR_DEPTH_MASK (0xf << 4) ++#define RK3588_8BPC (0x5 << 4) ++#define RK3588_10BPC (0x6 << 4) ++#define RK3588_CECIN_MASK BIT(8) ++#define RK3588_SCLIN_MASK BIT(9) ++#define RK3588_SDAIN_MASK BIT(10) ++#define RK3588_MODE_MASK BIT(11) ++#define RK3588_COMPRESS_MODE_MASK BIT(12) ++#define RK3588_I2S_SEL_MASK BIT(13) ++#define RK3588_SPDIF_SEL_MASK BIT(14) ++#define RK3588_GRF_VO1_CON4 0x0010 ++#define RK3588_HDMI21_MASK BIT(0) ++#define RK3588_GRF_VO1_CON9 0x0024 ++#define RK3588_HDMI0_GRANT_SEL BIT(10) ++#define RK3588_HDMI0_GRANT_SW BIT(11) ++#define RK3588_HDMI1_GRANT_SEL BIT(12) ++#define RK3588_HDMI1_GRANT_SW BIT(13) ++#define RK3588_GRF_VO1_CON6 0x0018 ++#define RK3588_GRF_VO1_CON7 0x001c ++ ++#define COLOR_DEPTH_10BIT BIT(31) ++#define HDMI_FRL_MODE BIT(30) ++#define HDMI_EARC_MODE BIT(29) ++ ++#define HDMI20_MAX_RATE 600000 ++#define HDMI_8K60_RATE 2376000 + + /** + * struct rockchip_hdmi_chip_data - splite the grf setting of kind of chips + * @lcdsel_grf_reg: grf register offset of lcdc select ++ * @ddc_en_reg: grf register offset of hdmi ddc enable + * @lcdsel_big: reg value of selecting vop big for HDMI + * @lcdsel_lit: reg value of selecting vop little for HDMI + */ + struct rockchip_hdmi_chip_data { + int lcdsel_grf_reg; ++ int ddc_en_reg; + u32 lcdsel_big; + u32 lcdsel_lit; ++ bool split_mode; ++}; ++ ++enum hdmi_frl_rate_per_lane { ++ FRL_12G_PER_LANE = 12, ++ FRL_10G_PER_LANE = 10, ++ FRL_8G_PER_LANE = 8, ++ FRL_6G_PER_LANE = 6, ++ FRL_3G_PER_LANE = 3, + }; + + struct rockchip_hdmi { + struct device *dev; + struct regmap *regmap; ++ struct regmap *vo1_regmap; + struct rockchip_encoder encoder; ++ struct drm_device *drm_dev; + const struct rockchip_hdmi_chip_data *chip_data; +- const struct dw_hdmi_plat_data *plat_data; ++ struct dw_hdmi_plat_data *plat_data; ++ struct clk *aud_clk; + struct clk *ref_clk; + struct clk *grf_clk; ++ struct clk *hclk_vio; ++ struct clk *hclk_vo1; ++ struct clk *hclk_vop; ++ struct clk *hpd_clk; ++ struct clk *pclk; ++ struct clk *earc_clk; ++ struct clk *hdmitx_ref; + struct dw_hdmi *hdmi; ++ struct dw_hdmi_qp *hdmi_qp; ++ + struct regulator *avdd_0v9; + struct regulator *avdd_1v8; + struct phy *phy; ++ ++ u32 max_tmdsclk; ++ bool unsupported_yuv_input; ++ bool unsupported_deep_color; ++ bool skip_check_420_mode; ++ u8 force_output; ++ u8 id; ++ bool hpd_stat; ++ bool is_hdmi_qp; ++ bool user_split_mode; ++ ++ unsigned long bus_format; ++ unsigned long output_bus_format; ++ unsigned long enc_out_encoding; ++ int color_changed; ++ int hpd_irq; ++ int vp_id; ++ ++ struct drm_property *color_depth_property; ++ struct drm_property *hdmi_output_property; ++ struct drm_property *colordepth_capacity; ++ struct drm_property *outputmode_capacity; ++ struct drm_property *quant_range; ++ struct drm_property *hdr_panel_metadata_property; ++ struct drm_property *next_hdr_sink_data_property; ++ struct drm_property *output_hdmi_dvi; ++ struct drm_property *output_type_capacity; ++ struct drm_property *user_split_mode_prop; ++ ++ struct drm_property_blob *hdr_panel_blob_ptr; ++ struct drm_property_blob *next_hdr_data_ptr; ++ ++ unsigned int colordepth; ++ unsigned int colorimetry; ++ unsigned int hdmi_quant_range; ++ unsigned int phy_bus_width; ++ enum rk_if_color_format hdmi_output; ++ struct rockchip_drm_sub_dev sub_dev; ++ ++ u8 max_frl_rate_per_lane; ++ u8 max_lanes; ++ struct rockchip_drm_dsc_cap dsc_cap; ++ struct next_hdr_sink_data next_hdr_data; ++ struct dw_hdmi_link_config link_cfg; ++ struct gpio_desc *enable_gpio; ++ ++ struct delayed_work work; ++ struct workqueue_struct *workqueue; + }; + + static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_encoder *encoder) +@@ -203,13 +339,834 @@ static const struct dw_hdmi_phy_config rockchip_phy_config[] = { + /*pixelclk symbol term vlev*/ + { 74250000, 0x8009, 0x0004, 0x0272}, + { 148500000, 0x802b, 0x0004, 0x028d}, ++ { 165000000, 0x802b, 0x0004, 0x0209}, + { 297000000, 0x8039, 0x0005, 0x028d}, ++ { 594000000, 0x8039, 0x0000, 0x019d}, + { ~0UL, 0x0000, 0x0000, 0x0000} + }; + ++enum ROW_INDEX_BPP { ++ ROW_INDEX_6BPP = 0, ++ ROW_INDEX_8BPP, ++ ROW_INDEX_10BPP, ++ ROW_INDEX_12BPP, ++ ROW_INDEX_23BPP, ++ MAX_ROW_INDEX ++}; ++ ++enum COLUMN_INDEX_BPC { ++ COLUMN_INDEX_8BPC = 0, ++ COLUMN_INDEX_10BPC, ++ COLUMN_INDEX_12BPC, ++ COLUMN_INDEX_14BPC, ++ COLUMN_INDEX_16BPC, ++ MAX_COLUMN_INDEX ++}; ++ ++#define PPS_TABLE_LEN 8 ++#define PPS_BPP_LEN 4 ++#define PPS_BPC_LEN 2 ++ ++struct pps_data { ++ u32 pic_width; ++ u32 pic_height; ++ u32 slice_width; ++ u32 slice_height; ++ bool convert_rgb; ++ u8 bpc; ++ u8 bpp; ++ u8 raw_pps[128]; ++}; ++ ++/* ++ * Selected Rate Control Related Parameter Recommended Values ++ * from DSC_v1.11 spec & C Model release: DSC_model_20161212 ++ */ ++static struct pps_data pps_datas[PPS_TABLE_LEN] = { ++ { ++ /* 7680x4320/960X96 rgb 8bpc 12bpp */ ++ 7680, 4320, 960, 96, 1, 8, 192, ++ { ++ 0x12, 0x00, 0x00, 0x8d, 0x30, 0xc0, 0x10, 0xe0, ++ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0xa0, ++ 0x01, 0x55, 0x03, 0x90, 0x00, 0x0a, 0x05, 0xc9, ++ 0x00, 0xa0, 0x00, 0x0f, 0x01, 0x44, 0x01, 0xaa, ++ 0x08, 0x00, 0x10, 0xf4, 0x03, 0x0c, 0x20, 0x00, ++ 0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38, ++ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, ++ 0x7d, 0x7e, 0x00, 0x82, 0x00, 0xc0, 0x09, 0x00, ++ 0x09, 0x7e, 0x19, 0xbc, 0x19, 0xba, 0x19, 0xf8, ++ 0x1a, 0x38, 0x1a, 0x38, 0x1a, 0x76, 0x2a, 0x76, ++ 0x2a, 0x76, 0x2a, 0x74, 0x3a, 0xb4, 0x52, 0xf4, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ++ }, ++ }, ++ { ++ /* 7680x4320/960X96 rgb 8bpc 11bpp */ ++ 7680, 4320, 960, 96, 1, 8, 176, ++ { ++ 0x12, 0x00, 0x00, 0x8d, 0x30, 0xb0, 0x10, 0xe0, ++ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0x28, ++ 0x01, 0x74, 0x03, 0x40, 0x00, 0x0f, 0x06, 0xe0, ++ 0x00, 0x2d, 0x00, 0x0f, 0x01, 0x44, 0x01, 0x33, ++ 0x0f, 0x00, 0x10, 0xf4, 0x03, 0x0c, 0x20, 0x00, ++ 0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38, ++ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, ++ 0x7d, 0x7e, 0x00, 0x82, 0x01, 0x00, 0x09, 0x40, ++ 0x09, 0xbe, 0x19, 0xfc, 0x19, 0xfa, 0x19, 0xf8, ++ 0x1a, 0x38, 0x1a, 0x38, 0x1a, 0x76, 0x2a, 0x76, ++ 0x2a, 0x76, 0x2a, 0xb4, 0x3a, 0xb4, 0x52, 0xf4, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ++ }, ++ }, ++ { ++ /* 7680x4320/960X96 rgb 8bpc 10bpp */ ++ 7680, 4320, 960, 96, 1, 8, 160, ++ { ++ 0x12, 0x00, 0x00, 0x8d, 0x30, 0xa0, 0x10, 0xe0, ++ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0xb0, ++ 0x01, 0x9a, 0x02, 0xe0, 0x00, 0x19, 0x09, 0xb0, ++ 0x00, 0x12, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xbb, ++ 0x16, 0x00, 0x10, 0xec, 0x03, 0x0c, 0x20, 0x00, ++ 0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38, ++ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, ++ 0x7d, 0x7e, 0x00, 0xc2, 0x01, 0x00, 0x09, 0x40, ++ 0x09, 0xbe, 0x19, 0xfc, 0x19, 0xfa, 0x19, 0xf8, ++ 0x1a, 0x38, 0x1a, 0x78, 0x1a, 0x76, 0x2a, 0xb6, ++ 0x2a, 0xb6, 0x2a, 0xf4, 0x3a, 0xf4, 0x5b, 0x34, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ++ }, ++ }, ++ { ++ /* 7680x4320/960X96 rgb 8bpc 9bpp */ ++ 7680, 4320, 960, 96, 1, 8, 144, ++ { ++ 0x12, 0x00, 0x00, 0x8d, 0x30, 0x90, 0x10, 0xe0, ++ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0x38, ++ 0x01, 0xc7, 0x03, 0x16, 0x00, 0x1c, 0x08, 0xc7, ++ 0x00, 0x10, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xaa, ++ 0x17, 0x00, 0x10, 0xf1, 0x03, 0x0c, 0x20, 0x00, ++ 0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38, ++ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, ++ 0x7d, 0x7e, 0x00, 0xc2, 0x01, 0x00, 0x09, 0x40, ++ 0x09, 0xbe, 0x19, 0xfc, 0x19, 0xfa, 0x19, 0xf8, ++ 0x1a, 0x38, 0x1a, 0x78, 0x1a, 0x76, 0x2a, 0xb6, ++ 0x2a, 0xb6, 0x2a, 0xf4, 0x3a, 0xf4, 0x63, 0x74, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ++ }, ++ }, ++ { ++ /* 7680x4320/960X96 rgb 10bpc 12bpp */ ++ 7680, 4320, 960, 96, 1, 10, 192, ++ { ++ 0x12, 0x00, 0x00, 0xad, 0x30, 0xc0, 0x10, 0xe0, ++ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0xa0, ++ 0x01, 0x55, 0x03, 0x90, 0x00, 0x0a, 0x05, 0xc9, ++ 0x00, 0xa0, 0x00, 0x0f, 0x01, 0x44, 0x01, 0xaa, ++ 0x08, 0x00, 0x10, 0xf4, 0x07, 0x10, 0x20, 0x00, ++ 0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38, ++ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, ++ 0x7d, 0x7e, 0x01, 0x02, 0x11, 0x80, 0x22, 0x00, ++ 0x22, 0x7e, 0x32, 0xbc, 0x32, 0xba, 0x3a, 0xf8, ++ 0x3b, 0x38, 0x3b, 0x38, 0x3b, 0x76, 0x4b, 0x76, ++ 0x4b, 0x76, 0x4b, 0x74, 0x5b, 0xb4, 0x73, 0xf4, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ++ }, ++ }, ++ { ++ /* 7680x4320/960X96 rgb 10bpc 11bpp */ ++ 7680, 4320, 960, 96, 1, 10, 176, ++ { ++ 0x12, 0x00, 0x00, 0xad, 0x30, 0xb0, 0x10, 0xe0, ++ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0x28, ++ 0x01, 0x74, 0x03, 0x40, 0x00, 0x0f, 0x06, 0xe0, ++ 0x00, 0x2d, 0x00, 0x0f, 0x01, 0x44, 0x01, 0x33, ++ 0x0f, 0x00, 0x10, 0xf4, 0x07, 0x10, 0x20, 0x00, ++ 0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38, ++ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, ++ 0x7d, 0x7e, 0x01, 0x42, 0x19, 0xc0, 0x2a, 0x40, ++ 0x2a, 0xbe, 0x3a, 0xfc, 0x3a, 0xfa, 0x3a, 0xf8, ++ 0x3b, 0x38, 0x3b, 0x38, 0x3b, 0x76, 0x4b, 0x76, ++ 0x4b, 0x76, 0x4b, 0xb4, 0x5b, 0xb4, 0x73, 0xf4, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ++ }, ++ }, ++ { ++ /* 7680x4320/960X96 rgb 10bpc 10bpp */ ++ 7680, 4320, 960, 96, 1, 10, 160, ++ { ++ 0x12, 0x00, 0x00, 0xad, 0x30, 0xa0, 0x10, 0xe0, ++ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0xb0, ++ 0x01, 0x9a, 0x02, 0xe0, 0x00, 0x19, 0x09, 0xb0, ++ 0x00, 0x12, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xbb, ++ 0x16, 0x00, 0x10, 0xec, 0x07, 0x10, 0x20, 0x00, ++ 0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38, ++ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, ++ 0x7d, 0x7e, 0x01, 0xc2, 0x22, 0x00, 0x2a, 0x40, ++ 0x2a, 0xbe, 0x3a, 0xfc, 0x3a, 0xfa, 0x3a, 0xf8, ++ 0x3b, 0x38, 0x3b, 0x78, 0x3b, 0x76, 0x4b, 0xb6, ++ 0x4b, 0xb6, 0x4b, 0xf4, 0x63, 0xf4, 0x7c, 0x34, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ++ }, ++ }, ++ { ++ /* 7680x4320/960X96 rgb 10bpc 9bpp */ ++ 7680, 4320, 960, 96, 1, 10, 144, ++ { ++ 0x12, 0x00, 0x00, 0xad, 0x30, 0x90, 0x10, 0xe0, ++ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0x38, ++ 0x01, 0xc7, 0x03, 0x16, 0x00, 0x1c, 0x08, 0xc7, ++ 0x00, 0x10, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xaa, ++ 0x17, 0x00, 0x10, 0xf1, 0x07, 0x10, 0x20, 0x00, ++ 0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38, ++ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, ++ 0x7d, 0x7e, 0x01, 0xc2, 0x22, 0x00, 0x2a, 0x40, ++ 0x2a, 0xbe, 0x3a, 0xfc, 0x3a, 0xfa, 0x3a, 0xf8, ++ 0x3b, 0x38, 0x3b, 0x78, 0x3b, 0x76, 0x4b, 0xb6, ++ 0x4b, 0xb6, 0x4b, 0xf4, 0x63, 0xf4, 0x84, 0x74, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ++ }, ++ }, ++}; ++ ++static bool hdmi_bus_fmt_is_rgb(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_RGB888_1X24: ++ case MEDIA_BUS_FMT_RGB101010_1X30: ++ case MEDIA_BUS_FMT_RGB121212_1X36: ++ case MEDIA_BUS_FMT_RGB161616_1X48: ++ return true; ++ ++ default: ++ return false; ++ } ++} ++ ++static bool hdmi_bus_fmt_is_yuv444(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_YUV8_1X24: ++ case MEDIA_BUS_FMT_YUV10_1X30: ++ case MEDIA_BUS_FMT_YUV12_1X36: ++ case MEDIA_BUS_FMT_YUV16_1X48: ++ return true; ++ ++ default: ++ return false; ++ } ++} ++ ++static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_UYVY8_1X16: ++ case MEDIA_BUS_FMT_UYVY10_1X20: ++ case MEDIA_BUS_FMT_UYVY12_1X24: ++ return true; ++ ++ default: ++ return false; ++ } ++} ++ ++static bool hdmi_bus_fmt_is_yuv420(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: ++ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: ++ case MEDIA_BUS_FMT_UYYVYY12_0_5X36: ++ case MEDIA_BUS_FMT_UYYVYY16_0_5X48: ++ return true; ++ ++ default: ++ return false; ++ } ++} ++ ++static int hdmi_bus_fmt_color_depth(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_RGB888_1X24: ++ case MEDIA_BUS_FMT_YUV8_1X24: ++ case MEDIA_BUS_FMT_UYVY8_1X16: ++ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: ++ return 8; ++ ++ case MEDIA_BUS_FMT_RGB101010_1X30: ++ case MEDIA_BUS_FMT_YUV10_1X30: ++ case MEDIA_BUS_FMT_UYVY10_1X20: ++ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: ++ return 10; ++ ++ case MEDIA_BUS_FMT_RGB121212_1X36: ++ case MEDIA_BUS_FMT_YUV12_1X36: ++ case MEDIA_BUS_FMT_UYVY12_1X24: ++ case MEDIA_BUS_FMT_UYYVYY12_0_5X36: ++ return 12; ++ ++ case MEDIA_BUS_FMT_RGB161616_1X48: ++ case MEDIA_BUS_FMT_YUV16_1X48: ++ case MEDIA_BUS_FMT_UYYVYY16_0_5X48: ++ return 16; ++ ++ default: ++ return 0; ++ } ++} ++ ++static unsigned int ++hdmi_get_tmdsclock(struct rockchip_hdmi *hdmi, unsigned long pixelclock) ++{ ++ unsigned int tmdsclock = pixelclock; ++ unsigned int depth = ++ hdmi_bus_fmt_color_depth(hdmi->output_bus_format); ++ ++ if (!hdmi_bus_fmt_is_yuv422(hdmi->output_bus_format)) { ++ switch (depth) { ++ case 16: ++ tmdsclock = pixelclock * 2; ++ break; ++ case 12: ++ tmdsclock = pixelclock * 3 / 2; ++ break; ++ case 10: ++ tmdsclock = pixelclock * 5 / 4; ++ break; ++ default: ++ break; ++ } ++ } ++ ++ return tmdsclock; ++} ++ ++static int rockchip_hdmi_match_by_id(struct device *dev, const void *data) ++{ ++ struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); ++ const unsigned int *id = data; ++ ++ return hdmi->id == *id; ++} ++ ++static struct rockchip_hdmi * ++rockchip_hdmi_find_by_id(struct device_driver *drv, unsigned int id) ++{ ++ struct device *dev; ++ ++ dev = driver_find_device(drv, NULL, &id, rockchip_hdmi_match_by_id); ++ if (!dev) ++ return NULL; ++ ++ return dev_get_drvdata(dev); ++} ++ ++static void hdmi_select_link_config(struct rockchip_hdmi *hdmi, ++ struct drm_crtc_state *crtc_state, ++ unsigned int tmdsclk) ++{ ++ struct drm_display_mode mode; ++ int max_lanes, max_rate_per_lane; ++ int max_dsc_lanes, max_dsc_rate_per_lane; ++ unsigned long max_frl_rate; ++ ++ drm_mode_copy(&mode, &crtc_state->mode); ++ if (hdmi->plat_data->split_mode) ++ drm_mode_convert_to_origin_mode(&mode); ++ ++ max_lanes = hdmi->max_lanes; ++ max_rate_per_lane = hdmi->max_frl_rate_per_lane; ++ max_frl_rate = max_lanes * max_rate_per_lane * 1000000; ++ ++ hdmi->link_cfg.dsc_mode = false; ++ hdmi->link_cfg.frl_lanes = max_lanes; ++ hdmi->link_cfg.rate_per_lane = max_rate_per_lane; ++ ++ if (!max_frl_rate || (tmdsclk < HDMI20_MAX_RATE && mode.clock < HDMI20_MAX_RATE)) { ++ dev_info(hdmi->dev, "use tmds mode\n"); ++ hdmi->link_cfg.frl_mode = false; ++ return; ++ } ++ ++ hdmi->link_cfg.frl_mode = true; ++ ++ if (!hdmi->dsc_cap.v_1p2) ++ return; ++ ++ max_dsc_lanes = hdmi->dsc_cap.max_lanes; ++ max_dsc_rate_per_lane = ++ hdmi->dsc_cap.max_frl_rate_per_lane; ++ ++ if (mode.clock >= HDMI_8K60_RATE && ++ !hdmi_bus_fmt_is_yuv420(hdmi->bus_format) && ++ !hdmi_bus_fmt_is_yuv422(hdmi->bus_format)) { ++ hdmi->link_cfg.dsc_mode = true; ++ hdmi->link_cfg.frl_lanes = max_dsc_lanes; ++ hdmi->link_cfg.rate_per_lane = max_dsc_rate_per_lane; ++ } else { ++ hdmi->link_cfg.dsc_mode = false; ++ hdmi->link_cfg.frl_lanes = max_lanes; ++ hdmi->link_cfg.rate_per_lane = max_rate_per_lane; ++ } ++} ++ ++///////////////////////////////////////////////////////////////////////////////////// ++ ++static int hdmi_dsc_get_slice_height(int vactive) ++{ ++ int slice_height; ++ ++ /* ++ * Slice Height determination : HDMI2.1 Section 7.7.5.2 ++ * Select smallest slice height >=96, that results in a valid PPS and ++ * requires minimum padding lines required for final slice. ++ * ++ * Assumption : Vactive is even. ++ */ ++ for (slice_height = 96; slice_height <= vactive; slice_height += 2) ++ if (vactive % slice_height == 0) ++ return slice_height; ++ ++ return 0; ++} ++ ++static int hdmi_dsc_get_num_slices(struct rockchip_hdmi *hdmi, ++ struct drm_crtc_state *crtc_state, ++ int src_max_slices, int src_max_slice_width, ++ int hdmi_max_slices, int hdmi_throughput) ++{ ++/* Pixel rates in KPixels/sec */ ++#define HDMI_DSC_PEAK_PIXEL_RATE 2720000 ++/* ++ * Rates at which the source and sink are required to process pixels in each ++ * slice, can be two levels: either at least 340000KHz or at least 40000KHz. ++ */ ++#define HDMI_DSC_MAX_ENC_THROUGHPUT_0 340000 ++#define HDMI_DSC_MAX_ENC_THROUGHPUT_1 400000 ++ ++/* Spec limits the slice width to 2720 pixels */ ++#define MAX_HDMI_SLICE_WIDTH 2720 ++ int kslice_adjust; ++ int adjusted_clk_khz; ++ int min_slices; ++ int target_slices; ++ int max_throughput; /* max clock freq. in khz per slice */ ++ int max_slice_width; ++ int slice_width; ++ int pixel_clock = crtc_state->mode.clock; ++ ++ if (!hdmi_throughput) ++ return 0; ++ ++ /* ++ * Slice Width determination : HDMI2.1 Section 7.7.5.1 ++ * kslice_adjust factor for 4:2:0, and 4:2:2 formats is 0.5, where as ++ * for 4:4:4 is 1.0. Multiplying these factors by 10 and later ++ * dividing adjusted clock value by 10. ++ */ ++ if (hdmi_bus_fmt_is_yuv444(hdmi->output_bus_format) || ++ hdmi_bus_fmt_is_rgb(hdmi->output_bus_format)) ++ kslice_adjust = 10; ++ else ++ kslice_adjust = 5; ++ ++ /* ++ * As per spec, the rate at which the source and the sink process ++ * the pixels per slice are at two levels: at least 340Mhz or 400Mhz. ++ * This depends upon the pixel clock rate and output formats ++ * (kslice adjust). ++ * If pixel clock * kslice adjust >= 2720MHz slices can be processed ++ * at max 340MHz, otherwise they can be processed at max 400MHz. ++ */ ++ ++ adjusted_clk_khz = DIV_ROUND_UP(kslice_adjust * pixel_clock, 10); ++ ++ if (adjusted_clk_khz <= HDMI_DSC_PEAK_PIXEL_RATE) ++ max_throughput = HDMI_DSC_MAX_ENC_THROUGHPUT_0; ++ else ++ max_throughput = HDMI_DSC_MAX_ENC_THROUGHPUT_1; ++ ++ /* ++ * Taking into account the sink's capability for maximum ++ * clock per slice (in MHz) as read from HF-VSDB. ++ */ ++ max_throughput = min(max_throughput, hdmi_throughput * 1000); ++ ++ min_slices = DIV_ROUND_UP(adjusted_clk_khz, max_throughput); ++ max_slice_width = min(MAX_HDMI_SLICE_WIDTH, src_max_slice_width); ++ ++ /* ++ * Keep on increasing the num of slices/line, starting from min_slices ++ * per line till we get such a number, for which the slice_width is ++ * just less than max_slice_width. The slices/line selected should be ++ * less than or equal to the max horizontal slices that the combination ++ * of PCON encoder and HDMI decoder can support. ++ */ ++ do { ++ if (min_slices <= 1 && src_max_slices >= 1 && hdmi_max_slices >= 1) ++ target_slices = 1; ++ else if (min_slices <= 2 && src_max_slices >= 2 && hdmi_max_slices >= 2) ++ target_slices = 2; ++ else if (min_slices <= 4 && src_max_slices >= 4 && hdmi_max_slices >= 4) ++ target_slices = 4; ++ else if (min_slices <= 8 && src_max_slices >= 8 && hdmi_max_slices >= 8) ++ target_slices = 8; ++ else if (min_slices <= 12 && src_max_slices >= 12 && hdmi_max_slices >= 12) ++ target_slices = 12; ++ else if (min_slices <= 16 && src_max_slices >= 16 && hdmi_max_slices >= 16) ++ target_slices = 16; ++ else ++ return 0; ++ ++ slice_width = DIV_ROUND_UP(crtc_state->mode.hdisplay, target_slices); ++ if (slice_width > max_slice_width) ++ min_slices = target_slices + 1; ++ } while (slice_width > max_slice_width); ++ ++ return target_slices; ++} ++ ++static int hdmi_dsc_slices(struct rockchip_hdmi *hdmi, ++ struct drm_crtc_state *crtc_state) ++{ ++ int hdmi_throughput = hdmi->dsc_cap.clk_per_slice; ++ int hdmi_max_slices = hdmi->dsc_cap.max_slices; ++ int rk_max_slices = 8; ++ int rk_max_slice_width = 2048; ++ ++ return hdmi_dsc_get_num_slices(hdmi, crtc_state, rk_max_slices, ++ rk_max_slice_width, ++ hdmi_max_slices, hdmi_throughput); ++} ++ ++static int ++hdmi_dsc_get_bpp(struct rockchip_hdmi *hdmi, int src_fractional_bpp, ++ int slice_width, int num_slices, bool hdmi_all_bpp, ++ int hdmi_max_chunk_bytes) ++{ ++ int max_dsc_bpp, min_dsc_bpp; ++ int target_bytes; ++ bool bpp_found = false; ++ int bpp_decrement_x16; ++ int bpp_target; ++ int bpp_target_x16; ++ ++ /* ++ * Get min bpp and max bpp as per Table 7.23, in HDMI2.1 spec ++ * Start with the max bpp and keep on decrementing with ++ * fractional bpp, if supported by PCON DSC encoder ++ * ++ * for each bpp we check if no of bytes can be supported by HDMI sink ++ */ ++ ++ /* only 9\10\12 bpp was tested */ ++ min_dsc_bpp = 9; ++ max_dsc_bpp = 12; ++ ++ /* ++ * Taking into account if all dsc_all_bpp supported by HDMI2.1 sink ++ * Section 7.7.34 : Source shall not enable compressed Video ++ * Transport with bpp_target settings above 12 bpp unless ++ * DSC_all_bpp is set to 1. ++ */ ++ if (!hdmi_all_bpp) ++ max_dsc_bpp = min(max_dsc_bpp, 12); ++ ++ /* ++ * The Sink has a limit of compressed data in bytes for a scanline, ++ * as described in max_chunk_bytes field in HFVSDB block of edid. ++ * The no. of bytes depend on the target bits per pixel that the ++ * source configures. So we start with the max_bpp and calculate ++ * the target_chunk_bytes. We keep on decrementing the target_bpp, ++ * till we get the target_chunk_bytes just less than what the sink's ++ * max_chunk_bytes, or else till we reach the min_dsc_bpp. ++ * ++ * The decrement is according to the fractional support from PCON DSC ++ * encoder. For fractional BPP we use bpp_target as a multiple of 16. ++ * ++ * bpp_target_x16 = bpp_target * 16 ++ * So we need to decrement by {1, 2, 4, 8, 16} for fractional bpps ++ * {1/16, 1/8, 1/4, 1/2, 1} respectively. ++ */ ++ ++ bpp_target = max_dsc_bpp; ++ ++ /* src does not support fractional bpp implies decrement by 16 for bppx16 */ ++ if (!src_fractional_bpp) ++ src_fractional_bpp = 1; ++ bpp_decrement_x16 = DIV_ROUND_UP(16, src_fractional_bpp); ++ bpp_target_x16 = bpp_target * 16; ++ ++ while (bpp_target_x16 > (min_dsc_bpp * 16)) { ++ int bpp; ++ ++ bpp = DIV_ROUND_UP(bpp_target_x16, 16); ++ target_bytes = DIV_ROUND_UP((num_slices * slice_width * bpp), 8); ++ if (target_bytes <= hdmi_max_chunk_bytes) { ++ bpp_found = true; ++ break; ++ } ++ bpp_target_x16 -= bpp_decrement_x16; ++ } ++ if (bpp_found) ++ return bpp_target_x16; ++ ++ return 0; ++} ++ ++static int ++dw_hdmi_dsc_bpp(struct rockchip_hdmi *hdmi, ++ int num_slices, int slice_width) ++{ ++ bool hdmi_all_bpp = hdmi->dsc_cap.all_bpp; ++ int fractional_bpp = 0; ++ int hdmi_max_chunk_bytes = hdmi->dsc_cap.total_chunk_kbytes * 1024; ++ ++ return hdmi_dsc_get_bpp(hdmi, fractional_bpp, slice_width, ++ num_slices, hdmi_all_bpp, ++ hdmi_max_chunk_bytes); ++} ++ ++static int dw_hdmi_qp_set_link_cfg(struct rockchip_hdmi *hdmi, ++ u16 pic_width, u16 pic_height, ++ u16 slice_width, u16 slice_height, ++ u16 bits_per_pixel, u8 bits_per_component) ++{ ++ int i; ++ ++ for (i = 0; i < PPS_TABLE_LEN; i++) ++ if (pic_width == pps_datas[i].pic_width && ++ pic_height == pps_datas[i].pic_height && ++ slice_width == pps_datas[i].slice_width && ++ slice_height == pps_datas[i].slice_height && ++ bits_per_component == pps_datas[i].bpc && ++ bits_per_pixel == pps_datas[i].bpp && ++ hdmi_bus_fmt_is_rgb(hdmi->output_bus_format) == pps_datas[i].convert_rgb) ++ break; ++ ++ if (i == PPS_TABLE_LEN) { ++ dev_err(hdmi->dev, "can't find pps cfg!\n"); ++ return -EINVAL; ++ } ++ ++ memcpy(hdmi->link_cfg.pps_payload, pps_datas[i].raw_pps, 128); ++ hdmi->link_cfg.hcactive = DIV_ROUND_UP(slice_width * (bits_per_pixel / 16), 8) * ++ (pic_width / slice_width); ++ ++ return 0; ++} ++ ++static void dw_hdmi_qp_dsc_configure(struct rockchip_hdmi *hdmi, ++ struct rockchip_crtc_state *s, ++ struct drm_crtc_state *crtc_state) ++{ ++ int ret; ++ int slice_height; ++ int slice_width; ++ int bits_per_pixel; ++ int slice_count; ++ bool hdmi_is_dsc_1_2; ++ unsigned int depth = hdmi_bus_fmt_color_depth(hdmi->output_bus_format); ++ ++ if (!crtc_state) ++ return; ++ ++ hdmi_is_dsc_1_2 = hdmi->dsc_cap.v_1p2; ++ ++ if (!hdmi_is_dsc_1_2) ++ return; ++ ++ slice_height = hdmi_dsc_get_slice_height(crtc_state->mode.vdisplay); ++ if (!slice_height) ++ return; ++ ++ slice_count = hdmi_dsc_slices(hdmi, crtc_state); ++ if (!slice_count) ++ return; ++ ++ slice_width = DIV_ROUND_UP(crtc_state->mode.hdisplay, slice_count); ++ ++ bits_per_pixel = dw_hdmi_dsc_bpp(hdmi, slice_count, slice_width); ++ if (!bits_per_pixel) ++ return; ++ ++ ret = dw_hdmi_qp_set_link_cfg(hdmi, crtc_state->mode.hdisplay, ++ crtc_state->mode.vdisplay, slice_width, ++ slice_height, bits_per_pixel, depth); ++ ++ if (ret) { ++ dev_err(hdmi->dev, "set vdsc cfg failed\n"); ++ return; ++ } ++ dev_info(hdmi->dev, "dsc_enable\n"); ++ s->dsc_enable = 1; ++ s->dsc_sink_cap.version_major = 1; ++ s->dsc_sink_cap.version_minor = 2; ++ s->dsc_sink_cap.slice_width = slice_width; ++ s->dsc_sink_cap.slice_height = slice_height; ++ s->dsc_sink_cap.target_bits_per_pixel_x16 = bits_per_pixel; ++ s->dsc_sink_cap.block_pred = 1; ++ s->dsc_sink_cap.native_420 = 0; ++ ++ memcpy(&s->pps, hdmi->link_cfg.pps_payload, 128); ++} ++///////////////////////////////////////////////////////////////////////////////////////// ++ ++// static int rockchip_hdmi_update_phy_table(struct rockchip_hdmi *hdmi, ++// u32 *config, ++// int phy_table_size) ++// { ++// int i; ++// ++// if (phy_table_size > ARRAY_SIZE(rockchip_phy_config)) { ++// dev_err(hdmi->dev, "phy table array number is out of range\n"); ++// return -E2BIG; ++// } ++// ++// for (i = 0; i < phy_table_size; i++) { ++// if (config[i * 4] != 0) ++// rockchip_phy_config[i].mpixelclock = (u64)config[i * 4]; ++// else ++// rockchip_phy_config[i].mpixelclock = ~0UL; ++// rockchip_phy_config[i].sym_ctr = (u16)config[i * 4 + 1]; ++// rockchip_phy_config[i].term = (u16)config[i * 4 + 2]; ++// rockchip_phy_config[i].vlev_ctr = (u16)config[i * 4 + 3]; ++// } ++// ++// return 0; ++// } ++ ++static void repo_hpd_event(struct work_struct *p_work) ++{ ++ struct rockchip_hdmi *hdmi = container_of(p_work, struct rockchip_hdmi, work.work); ++ bool change; ++ ++ change = drm_helper_hpd_irq_event(hdmi->drm_dev); ++ if (change) { ++ dev_dbg(hdmi->dev, "hpd stat changed:%d\n", hdmi->hpd_stat); ++ // dw_hdmi_qp_cec_set_hpd(hdmi->hdmi_qp, hdmi->hpd_stat, change); ++ } ++} ++ ++static irqreturn_t rockchip_hdmi_hardirq(int irq, void *dev_id) ++{ ++ struct rockchip_hdmi *hdmi = dev_id; ++ u32 intr_stat, val; ++ ++ regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat); ++ ++ if (intr_stat) { ++ dev_dbg(hdmi->dev, "hpd irq %#x\n", intr_stat); ++ ++ if (!hdmi->id) ++ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK, ++ RK3588_HDMI0_HPD_INT_MSK); ++ else ++ val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_MSK, ++ RK3588_HDMI1_HPD_INT_MSK); ++ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); ++ return IRQ_WAKE_THREAD; ++ } ++ ++ return IRQ_NONE; ++} ++ ++static irqreturn_t rockchip_hdmi_irq(int irq, void *dev_id) ++{ ++ struct rockchip_hdmi *hdmi = dev_id; ++ u32 intr_stat, val; ++ int msecs; ++ bool stat; ++ ++ regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat); ++ ++ if (!intr_stat) ++ return IRQ_NONE; ++ ++ if (!hdmi->id) { ++ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR, ++ RK3588_HDMI0_HPD_INT_CLR); ++ if (intr_stat & RK3588_HDMI0_LEVEL_INT) ++ stat = true; ++ else ++ stat = false; ++ } else { ++ val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR, ++ RK3588_HDMI1_HPD_INT_CLR); ++ if (intr_stat & RK3588_HDMI1_LEVEL_INT) ++ stat = true; ++ else ++ stat = false; ++ } ++ ++ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); ++ ++ if (stat) { ++ hdmi->hpd_stat = true; ++ msecs = 150; ++ } else { ++ hdmi->hpd_stat = false; ++ msecs = 20; ++ } ++ mod_delayed_work(hdmi->workqueue, &hdmi->work, msecs_to_jiffies(msecs)); ++ ++ if (!hdmi->id) { ++ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR, ++ RK3588_HDMI0_HPD_INT_CLR) | ++ HIWORD_UPDATE(0, RK3588_HDMI0_HPD_INT_MSK); ++ } else { ++ val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR, ++ RK3588_HDMI1_HPD_INT_CLR) | ++ HIWORD_UPDATE(0, RK3588_HDMI1_HPD_INT_MSK); ++ } ++ ++ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); ++ ++ return IRQ_HANDLED; ++} ++ ++static void init_hpd_work(struct rockchip_hdmi *hdmi) ++{ ++ hdmi->workqueue = create_workqueue("hpd_queue"); ++ INIT_DELAYED_WORK(&hdmi->work, repo_hpd_event); ++} ++ + static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) + { + struct device_node *np = hdmi->dev->of_node; ++ int ret; + + hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR(hdmi->regmap)) { +@@ -217,6 +1174,14 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) + return PTR_ERR(hdmi->regmap); + } + ++ if (hdmi->is_hdmi_qp) { ++ hdmi->vo1_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,vo1_grf"); ++ if (IS_ERR(hdmi->vo1_regmap)) { ++ DRM_DEV_ERROR(hdmi->dev, "Unable to get rockchip,vo1_grf\n"); ++ return PTR_ERR(hdmi->vo1_regmap); ++ } ++ } ++ + hdmi->ref_clk = devm_clk_get_optional(hdmi->dev, "ref"); + if (!hdmi->ref_clk) + hdmi->ref_clk = devm_clk_get_optional(hdmi->dev, "vpll"); +@@ -246,6 +1211,79 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) + if (IS_ERR(hdmi->avdd_1v8)) + return PTR_ERR(hdmi->avdd_1v8); + ++ hdmi->hclk_vio = devm_clk_get(hdmi->dev, "hclk_vio"); ++ if (PTR_ERR(hdmi->hclk_vio) == -ENOENT) { ++ hdmi->hclk_vio = NULL; ++ } else if (PTR_ERR(hdmi->hclk_vio) == -EPROBE_DEFER) { ++ return -EPROBE_DEFER; ++ } else if (IS_ERR(hdmi->hclk_vio)) { ++ dev_err(hdmi->dev, "failed to get hclk_vio clock\n"); ++ return PTR_ERR(hdmi->hclk_vio); ++ } ++ ++ hdmi->hclk_vop = devm_clk_get(hdmi->dev, "hclk"); ++ if (PTR_ERR(hdmi->hclk_vop) == -ENOENT) { ++ hdmi->hclk_vop = NULL; ++ } else if (PTR_ERR(hdmi->hclk_vop) == -EPROBE_DEFER) { ++ return -EPROBE_DEFER; ++ } else if (IS_ERR(hdmi->hclk_vop)) { ++ dev_err(hdmi->dev, "failed to get hclk_vop clock\n"); ++ return PTR_ERR(hdmi->hclk_vop); ++ } ++ ++ hdmi->aud_clk = devm_clk_get_optional(hdmi->dev, "aud"); ++ if (IS_ERR(hdmi->aud_clk)) { ++ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->aud_clk), ++ "failed to get aud_clk clock\n"); ++ return PTR_ERR(hdmi->aud_clk); ++ } ++ ++ hdmi->hpd_clk = devm_clk_get_optional(hdmi->dev, "hpd"); ++ if (IS_ERR(hdmi->hpd_clk)) { ++ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hpd_clk), ++ "failed to get hpd_clk clock\n"); ++ return PTR_ERR(hdmi->hpd_clk); ++ } ++ ++ hdmi->hclk_vo1 = devm_clk_get_optional(hdmi->dev, "hclk_vo1"); ++ if (IS_ERR(hdmi->hclk_vo1)) { ++ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hclk_vo1), ++ "failed to get hclk_vo1 clock\n"); ++ return PTR_ERR(hdmi->hclk_vo1); ++ } ++ ++ hdmi->earc_clk = devm_clk_get_optional(hdmi->dev, "earc"); ++ if (IS_ERR(hdmi->earc_clk)) { ++ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->earc_clk), ++ "failed to get earc_clk clock\n"); ++ return PTR_ERR(hdmi->earc_clk); ++ } ++ ++ hdmi->hdmitx_ref = devm_clk_get_optional(hdmi->dev, "hdmitx_ref"); ++ if (IS_ERR(hdmi->hdmitx_ref)) { ++ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hdmitx_ref), ++ "failed to get hdmitx_ref clock\n"); ++ return PTR_ERR(hdmi->hdmitx_ref); ++ } ++ ++ hdmi->pclk = devm_clk_get_optional(hdmi->dev, "pclk"); ++ if (IS_ERR(hdmi->pclk)) { ++ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->pclk), ++ "failed to get pclk clock\n"); ++ return PTR_ERR(hdmi->pclk); ++ } ++ ++ hdmi->enable_gpio = devm_gpiod_get_optional(hdmi->dev, "enable", ++ GPIOD_OUT_HIGH); ++ if (IS_ERR(hdmi->enable_gpio)) { ++ ret = PTR_ERR(hdmi->enable_gpio); ++ dev_err(hdmi->dev, "failed to request enable GPIO: %d\n", ret); ++ return ret; ++ } ++ ++ hdmi->skip_check_420_mode = ++ of_property_read_bool(np, "skip-check-420-mode"); ++ + return 0; + } + +@@ -284,9 +1322,114 @@ dw_hdmi_rockchip_mode_valid(struct dw_hdmi *dw_hdmi, void *data, + + return MODE_BAD; + } ++/* [CC:] enable downstream mode_valid() */ ++// static enum drm_mode_status ++// dw_hdmi_rockchip_mode_valid(struct drm_connector *connector, void *data, ++// const struct drm_display_info *info, ++// const struct drm_display_mode *mode) ++// { ++// struct drm_encoder *encoder = connector->encoder; ++// enum drm_mode_status status = MODE_OK; ++// struct drm_device *dev = connector->dev; ++// struct rockchip_drm_private *priv = dev->dev_private; ++// struct drm_crtc *crtc; ++// struct rockchip_hdmi *hdmi; ++// ++// /* ++// * Pixel clocks we support are always < 2GHz and so fit in an ++// * int. We should make sure source rate does too so we don't get ++// * overflow when we multiply by 1000. ++// */ ++// if (mode->clock > INT_MAX / 1000) ++// return MODE_BAD; ++// ++// if (!encoder) { ++// const struct drm_connector_helper_funcs *funcs; ++// ++// funcs = connector->helper_private; ++// if (funcs->atomic_best_encoder) ++// encoder = funcs->atomic_best_encoder(connector, ++// connector->state); ++// else ++// encoder = funcs->best_encoder(connector); ++// } ++// ++// if (!encoder || !encoder->possible_crtcs) ++// return MODE_BAD; ++// ++// hdmi = to_rockchip_hdmi(encoder); ++// ++// /* ++// * If sink max TMDS clock < 340MHz, we should check the mode pixel ++// * clock > 340MHz is YCbCr420 or not and whether the platform supports ++// * YCbCr420. ++// */ ++// if (!hdmi->skip_check_420_mode) { ++// if (mode->clock > 340000 && ++// connector->display_info.max_tmds_clock < 340000 && ++// (!drm_mode_is_420(&connector->display_info, mode) || ++// !connector->ycbcr_420_allowed)) ++// return MODE_BAD; ++// ++// if (hdmi->max_tmdsclk <= 340000 && mode->clock > 340000 && ++// !drm_mode_is_420(&connector->display_info, mode)) ++// return MODE_BAD; ++// }; ++// ++// if (hdmi->phy) { ++// if (hdmi->is_hdmi_qp) ++// phy_set_bus_width(hdmi->phy, mode->clock * 10); ++// else ++// phy_set_bus_width(hdmi->phy, 8); ++// } ++// ++// /* ++// * ensure all drm display mode can work, if someone want support more ++// * resolutions, please limit the possible_crtc, only connect to ++// * needed crtc. ++// */ ++// drm_for_each_crtc(crtc, connector->dev) { ++// int pipe = drm_crtc_index(crtc); ++// const struct rockchip_crtc_funcs *funcs = ++// priv->crtc_funcs[pipe]; ++// ++// if (!(encoder->possible_crtcs & drm_crtc_mask(crtc))) ++// continue; ++// if (!funcs || !funcs->mode_valid) ++// continue; ++// ++// status = funcs->mode_valid(crtc, mode, ++// DRM_MODE_CONNECTOR_HDMIA); ++// if (status != MODE_OK) ++// return status; ++// } ++// ++// return status; ++// } ++// + + static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder) + { ++ struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); ++ struct drm_crtc *crtc = encoder->crtc; ++ struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state); ++ ++ if (crtc->state->active_changed) { ++ if (hdmi->plat_data->split_mode) { ++ s->output_if &= ~(VOP_OUTPUT_IF_HDMI0 | VOP_OUTPUT_IF_HDMI1); ++ } else { ++ if (!hdmi->id) ++ s->output_if &= ~VOP_OUTPUT_IF_HDMI1; ++ else ++ s->output_if &= ~VOP_OUTPUT_IF_HDMI0; ++ } ++ } ++ /* ++ * when plug out hdmi it will be switch cvbs and then phy bus width ++ * must be set as 8 ++ */ ++ if (hdmi->phy) ++ phy_set_bus_width(hdmi->phy, 8); + } + + static bool +@@ -302,6 +1445,27 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *adj_mode) + { + struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); ++ struct drm_crtc *crtc; ++ struct rockchip_crtc_state *s; ++ ++ if (!encoder->crtc) ++ return; ++ crtc = encoder->crtc; ++ ++ if (!crtc->state) ++ return; ++ s = to_rockchip_crtc_state(crtc->state); ++ ++ if (!s) ++ return; ++ ++ if (hdmi->is_hdmi_qp) { ++ s->dsc_enable = 0; ++ if (hdmi->link_cfg.dsc_mode) ++ dw_hdmi_qp_dsc_configure(hdmi, s, crtc->state); ++ ++ phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width); ++ } + + clk_set_rate(hdmi->ref_clk, adj_mode->clock * 1000); + } +@@ -309,14 +1473,25 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder, + static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder) + { + struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); ++ struct drm_crtc *crtc = encoder->crtc; + u32 val; ++ int mux; + int ret; + ++ if (WARN_ON(!crtc || !crtc->state)) ++ return; ++ ++ if (hdmi->phy) ++ phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width); ++ ++ clk_set_rate(hdmi->ref_clk, ++ crtc->state->adjusted_mode.crtc_clock * 1000); ++ + if (hdmi->chip_data->lcdsel_grf_reg < 0) + return; + +- ret = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder); +- if (ret) ++ mux = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder); ++ if (mux) + val = hdmi->chip_data->lcdsel_lit; + else + val = hdmi->chip_data->lcdsel_big; +@@ -331,24 +1506,1018 @@ static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder) + if (ret != 0) + DRM_DEV_ERROR(hdmi->dev, "Could not write to GRF: %d\n", ret); + ++ if (hdmi->chip_data->lcdsel_grf_reg == RK3288_GRF_SOC_CON6) { ++ struct rockchip_crtc_state *s = ++ to_rockchip_crtc_state(crtc->state); ++ u32 mode_mask = mux ? RK3288_HDMI_LCDC1_YUV420 : ++ RK3288_HDMI_LCDC0_YUV420; ++ ++ if (s->output_mode == ROCKCHIP_OUT_MODE_YUV420) ++ val = HIWORD_UPDATE(mode_mask, mode_mask); ++ else ++ val = HIWORD_UPDATE(0, mode_mask); ++ ++ regmap_write(hdmi->regmap, RK3288_GRF_SOC_CON16, val); ++ } ++ + clk_disable_unprepare(hdmi->grf_clk); + DRM_DEV_DEBUG(hdmi->dev, "vop %s output to hdmi\n", + ret ? "LIT" : "BIG"); + } + +-static int +-dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder, +- struct drm_crtc_state *crtc_state, +- struct drm_connector_state *conn_state) ++static void rk3588_set_link_mode(struct rockchip_hdmi *hdmi) + { +- struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); ++ int val; ++ bool is_hdmi0; + +- s->output_mode = ROCKCHIP_OUT_MODE_AAAA; +- s->output_type = DRM_MODE_CONNECTOR_HDMIA; ++ if (!hdmi->id) ++ is_hdmi0 = true; ++ else ++ is_hdmi0 = false; ++ ++ if (!hdmi->link_cfg.frl_mode) { ++ val = HIWORD_UPDATE(0, RK3588_HDMI21_MASK); ++ if (is_hdmi0) ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON4, val); ++ else ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON7, val); ++ ++ val = HIWORD_UPDATE(0, RK3588_COMPRESS_MODE_MASK | RK3588_COLOR_FORMAT_MASK); ++ if (is_hdmi0) ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); ++ else ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); ++ ++ return; ++ } ++ ++ val = HIWORD_UPDATE(RK3588_HDMI21_MASK, RK3588_HDMI21_MASK); ++ if (is_hdmi0) ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON4, val); ++ else ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON7, val); ++ ++ if (hdmi->link_cfg.dsc_mode) { ++ val = HIWORD_UPDATE(RK3588_COMPRESS_MODE_MASK | RK3588_COMPRESSED_DATA, ++ RK3588_COMPRESS_MODE_MASK | RK3588_COLOR_FORMAT_MASK); ++ if (is_hdmi0) ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); ++ else ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); ++ } else { ++ val = HIWORD_UPDATE(0, RK3588_COMPRESS_MODE_MASK | RK3588_COLOR_FORMAT_MASK); ++ if (is_hdmi0) ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); ++ else ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); ++ } ++} ++ ++static void rk3588_set_color_format(struct rockchip_hdmi *hdmi, u64 bus_format, ++ u32 depth) ++{ ++ u32 val = 0; ++ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_RGB888_1X24: ++ case MEDIA_BUS_FMT_RGB101010_1X30: ++ val = HIWORD_UPDATE(0, RK3588_COLOR_FORMAT_MASK); ++ break; ++ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: ++ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: ++ val = HIWORD_UPDATE(RK3588_YUV420, RK3588_COLOR_FORMAT_MASK); ++ break; ++ case MEDIA_BUS_FMT_YUV8_1X24: ++ case MEDIA_BUS_FMT_YUV10_1X30: ++ val = HIWORD_UPDATE(RK3588_YUV444, RK3588_COLOR_FORMAT_MASK); ++ break; ++ default: ++ dev_err(hdmi->dev, "can't set correct color format\n"); ++ return; ++ } ++ ++ if (hdmi->link_cfg.dsc_mode) ++ val = HIWORD_UPDATE(RK3588_COMPRESSED_DATA, RK3588_COLOR_FORMAT_MASK); ++ ++ if (depth == 8) ++ val |= HIWORD_UPDATE(RK3588_8BPC, RK3588_COLOR_DEPTH_MASK); ++ else ++ val |= HIWORD_UPDATE(RK3588_10BPC, RK3588_COLOR_DEPTH_MASK); ++ ++ if (!hdmi->id) ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); ++ else ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); ++} ++ ++static void rk3588_set_grf_cfg(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ int color_depth; ++ ++ rk3588_set_link_mode(hdmi); ++ color_depth = hdmi_bus_fmt_color_depth(hdmi->bus_format); ++ rk3588_set_color_format(hdmi, hdmi->bus_format, color_depth); ++} ++ ++static void ++dw_hdmi_rockchip_select_output(struct drm_connector_state *conn_state, ++ struct drm_crtc_state *crtc_state, ++ struct rockchip_hdmi *hdmi, ++ unsigned int *color_format, ++ unsigned int *output_mode, ++ unsigned long *bus_format, ++ unsigned int *bus_width, ++ unsigned long *enc_out_encoding, ++ unsigned int *eotf) ++{ ++ struct drm_display_info *info = &conn_state->connector->display_info; ++ struct drm_display_mode mode; ++ struct hdr_output_metadata *hdr_metadata; ++ u32 vic; ++ unsigned long tmdsclock, pixclock; ++ unsigned int color_depth; ++ bool support_dc = false; ++ bool sink_is_hdmi = true; ++ u32 max_tmds_clock = info->max_tmds_clock; ++ int output_eotf; ++ ++ drm_mode_copy(&mode, &crtc_state->mode); ++ pixclock = mode.crtc_clock; ++ if (hdmi->plat_data->split_mode) { ++ drm_mode_convert_to_origin_mode(&mode); ++ pixclock /= 2; ++ } ++ ++ vic = drm_match_cea_mode(&mode); ++ ++ if (!hdmi->is_hdmi_qp) ++ sink_is_hdmi = dw_hdmi_get_output_whether_hdmi(hdmi->hdmi); ++ ++ *color_format = RK_IF_FORMAT_RGB; ++ ++ switch (hdmi->hdmi_output) { ++ case RK_IF_FORMAT_YCBCR_HQ: ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444) ++ *color_format = RK_IF_FORMAT_YCBCR444; ++ else if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) ++ *color_format = RK_IF_FORMAT_YCBCR422; ++ else if (conn_state->connector->ycbcr_420_allowed && ++ drm_mode_is_420(info, &mode) && ++ (pixclock >= 594000 && !hdmi->is_hdmi_qp)) ++ *color_format = RK_IF_FORMAT_YCBCR420; ++ break; ++ case RK_IF_FORMAT_YCBCR_LQ: ++ if (conn_state->connector->ycbcr_420_allowed && ++ drm_mode_is_420(info, &mode) && pixclock >= 594000) ++ *color_format = RK_IF_FORMAT_YCBCR420; ++ else if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) ++ *color_format = RK_IF_FORMAT_YCBCR422; ++ else if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444) ++ *color_format = RK_IF_FORMAT_YCBCR444; ++ break; ++ case RK_IF_FORMAT_YCBCR420: ++ if (conn_state->connector->ycbcr_420_allowed && ++ drm_mode_is_420(info, &mode) && pixclock >= 594000) ++ *color_format = RK_IF_FORMAT_YCBCR420; ++ break; ++ case RK_IF_FORMAT_YCBCR422: ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) ++ *color_format = RK_IF_FORMAT_YCBCR422; ++ break; ++ case RK_IF_FORMAT_YCBCR444: ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444) ++ *color_format = RK_IF_FORMAT_YCBCR444; ++ break; ++ case RK_IF_FORMAT_RGB: ++ default: ++ break; ++ } ++ ++ if (*color_format == RK_IF_FORMAT_RGB && ++ info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30) ++ support_dc = true; ++ if (*color_format == RK_IF_FORMAT_YCBCR444 && ++ info->edid_hdmi_rgb444_dc_modes & ++ (DRM_EDID_HDMI_DC_Y444 | DRM_EDID_HDMI_DC_30)) ++ support_dc = true; ++ if (*color_format == RK_IF_FORMAT_YCBCR422) ++ support_dc = true; ++ if (*color_format == RK_IF_FORMAT_YCBCR420 && ++ info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30) ++ support_dc = true; ++ ++ if (hdmi->colordepth > 8 && support_dc) ++ color_depth = 10; ++ else ++ color_depth = 8; ++ ++ if (!sink_is_hdmi) { ++ *color_format = RK_IF_FORMAT_RGB; ++ color_depth = 8; ++ } ++ ++ *eotf = HDMI_EOTF_TRADITIONAL_GAMMA_SDR; ++ if (conn_state->hdr_output_metadata) { ++ hdr_metadata = (struct hdr_output_metadata *) ++ conn_state->hdr_output_metadata->data; ++ output_eotf = hdr_metadata->hdmi_metadata_type1.eotf; ++ if (output_eotf > HDMI_EOTF_TRADITIONAL_GAMMA_SDR && ++ output_eotf <= HDMI_EOTF_BT_2100_HLG) ++ *eotf = output_eotf; ++ } ++ ++ hdmi->colorimetry = conn_state->colorspace; ++ ++ if ((*eotf > HDMI_EOTF_TRADITIONAL_GAMMA_SDR && ++ conn_state->connector->hdr_sink_metadata.hdmi_type1.eotf & ++ BIT(*eotf)) || ((hdmi->colorimetry >= DRM_MODE_COLORIMETRY_BT2020_CYCC) && ++ (hdmi->colorimetry <= DRM_MODE_COLORIMETRY_BT2020_YCC))) ++ *enc_out_encoding = V4L2_YCBCR_ENC_BT2020; ++ else if ((vic == 6) || (vic == 7) || (vic == 21) || (vic == 22) || ++ (vic == 2) || (vic == 3) || (vic == 17) || (vic == 18)) ++ *enc_out_encoding = V4L2_YCBCR_ENC_601; ++ else ++ *enc_out_encoding = V4L2_YCBCR_ENC_709; ++ ++ if (*enc_out_encoding == V4L2_YCBCR_ENC_BT2020) { ++ /* BT2020 require color depth at lest 10bit */ ++ color_depth = 10; ++ /* We prefer use YCbCr422 to send 10bit */ ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) ++ *color_format = RK_IF_FORMAT_YCBCR422; ++ if (hdmi->is_hdmi_qp) { ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR420) { ++ if (mode.clock >= 340000) ++ *color_format = RK_IF_FORMAT_YCBCR420; ++ else ++ *color_format = RK_IF_FORMAT_RGB; ++ } else { ++ *color_format = RK_IF_FORMAT_RGB; ++ } ++ } ++ } ++ ++ if (mode.flags & DRM_MODE_FLAG_DBLCLK) ++ pixclock *= 2; ++ if ((mode.flags & DRM_MODE_FLAG_3D_MASK) == ++ DRM_MODE_FLAG_3D_FRAME_PACKING) ++ pixclock *= 2; ++ ++ if (*color_format == RK_IF_FORMAT_YCBCR422 || color_depth == 8) ++ tmdsclock = pixclock; ++ else ++ tmdsclock = pixclock * (color_depth) / 8; ++ ++ if (*color_format == RK_IF_FORMAT_YCBCR420) ++ tmdsclock /= 2; ++ ++ /* XXX: max_tmds_clock of some sink is 0, we think it is 340MHz. */ ++ if (!max_tmds_clock) ++ max_tmds_clock = 340000; ++ ++ max_tmds_clock = min(max_tmds_clock, hdmi->max_tmdsclk); ++ ++ if ((tmdsclock > max_tmds_clock) && !hdmi->is_hdmi_qp) { ++ if (max_tmds_clock >= 594000) { ++ color_depth = 8; ++ } else if (max_tmds_clock > 340000) { ++ if (drm_mode_is_420(info, &mode) || tmdsclock >= 594000) ++ *color_format = RK_IF_FORMAT_YCBCR420; ++ } else { ++ color_depth = 8; ++ if (drm_mode_is_420(info, &mode) || tmdsclock >= 594000) ++ *color_format = RK_IF_FORMAT_YCBCR420; ++ } ++ } ++ ++ if (hdmi->is_hdmi_qp) { ++ if (mode.clock >= 340000) { ++ if (drm_mode_is_420(info, &mode)) ++ *color_format = RK_IF_FORMAT_YCBCR420; ++ else ++ *color_format = RK_IF_FORMAT_RGB; ++ } else if (tmdsclock > max_tmds_clock) { ++ color_depth = 8; ++ if (drm_mode_is_420(info, &mode)) ++ *color_format = RK_IF_FORMAT_YCBCR420; ++ } ++ } ++ ++ if (*color_format == RK_IF_FORMAT_YCBCR420) { ++ *output_mode = ROCKCHIP_OUT_MODE_YUV420; ++ if (color_depth > 8) ++ *bus_format = MEDIA_BUS_FMT_UYYVYY10_0_5X30; ++ else ++ *bus_format = MEDIA_BUS_FMT_UYYVYY8_0_5X24; ++ *bus_width = color_depth / 2; ++ } else { ++ *output_mode = ROCKCHIP_OUT_MODE_AAAA; ++ if (color_depth > 8) { ++ if (*color_format != RK_IF_FORMAT_RGB && ++ !hdmi->unsupported_yuv_input) ++ *bus_format = MEDIA_BUS_FMT_YUV10_1X30; ++ else ++ *bus_format = MEDIA_BUS_FMT_RGB101010_1X30; ++ } else { ++ if (*color_format != RK_IF_FORMAT_RGB && ++ !hdmi->unsupported_yuv_input) ++ *bus_format = MEDIA_BUS_FMT_YUV8_1X24; ++ else ++ *bus_format = MEDIA_BUS_FMT_RGB888_1X24; ++ } ++ if (*color_format == RK_IF_FORMAT_YCBCR422) ++ *bus_width = 8; ++ else ++ *bus_width = color_depth; ++ } ++ ++ hdmi->bus_format = *bus_format; ++ ++ if (*color_format == RK_IF_FORMAT_YCBCR422) { ++ if (color_depth == 12) ++ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY12_1X24; ++ else if (color_depth == 10) ++ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY10_1X20; ++ else ++ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY8_1X16; ++ } else { ++ hdmi->output_bus_format = *bus_format; ++ } ++} ++ ++static bool ++dw_hdmi_rockchip_check_color(struct drm_connector_state *conn_state, ++ struct rockchip_hdmi *hdmi) ++{ ++ struct drm_crtc_state *crtc_state = conn_state->crtc->state; ++ unsigned int colorformat; ++ unsigned long bus_format; ++ unsigned long output_bus_format = hdmi->output_bus_format; ++ unsigned long enc_out_encoding = hdmi->enc_out_encoding; ++ unsigned int eotf, bus_width; ++ unsigned int output_mode; ++ ++ dw_hdmi_rockchip_select_output(conn_state, crtc_state, hdmi, ++ &colorformat, ++ &output_mode, &bus_format, &bus_width, ++ &hdmi->enc_out_encoding, &eotf); ++ ++ if (output_bus_format != hdmi->output_bus_format || ++ enc_out_encoding != hdmi->enc_out_encoding) ++ return true; ++ else ++ return false; ++} ++ ++static int ++dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder, ++ struct drm_crtc_state *crtc_state, ++ struct drm_connector_state *conn_state) ++{ ++ struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); ++ struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); ++ unsigned int colorformat, bus_width, tmdsclk; ++ struct drm_display_mode mode; ++ unsigned int output_mode; ++ unsigned long bus_format; ++ int color_depth; ++ bool secondary = false; ++ ++ /* ++ * There are two hdmi but only one encoder in split mode, ++ * so we need to check twice. ++ */ ++secondary: ++ drm_mode_copy(&mode, &crtc_state->mode); ++ ++ hdmi->vp_id = s->vp_id; ++ if (hdmi->plat_data->split_mode) ++ drm_mode_convert_to_origin_mode(&mode); ++ ++ dw_hdmi_rockchip_select_output(conn_state, crtc_state, hdmi, ++ &colorformat, ++ &output_mode, &bus_format, &bus_width, ++ &hdmi->enc_out_encoding, &s->eotf); ++ ++ s->bus_format = bus_format; ++ if (hdmi->is_hdmi_qp) { ++ color_depth = hdmi_bus_fmt_color_depth(bus_format); ++ tmdsclk = hdmi_get_tmdsclock(hdmi, crtc_state->mode.clock); ++ if (hdmi_bus_fmt_is_yuv420(hdmi->output_bus_format)) ++ tmdsclk /= 2; ++ hdmi_select_link_config(hdmi, crtc_state, tmdsclk); ++ ++ if (hdmi->link_cfg.frl_mode) { ++ gpiod_set_value(hdmi->enable_gpio, 0); ++ /* in the current version, support max 40G frl */ ++ if (hdmi->link_cfg.rate_per_lane >= 10) { ++ hdmi->link_cfg.frl_lanes = 4; ++ hdmi->link_cfg.rate_per_lane = 10; ++ } ++ bus_width = hdmi->link_cfg.frl_lanes * ++ hdmi->link_cfg.rate_per_lane * 1000000; ++ /* 10 bit color depth and frl mode */ ++ if (color_depth == 10) ++ bus_width |= ++ COLOR_DEPTH_10BIT | HDMI_FRL_MODE; ++ else ++ bus_width |= HDMI_FRL_MODE; ++ } else { ++ gpiod_set_value(hdmi->enable_gpio, 1); ++ bus_width = hdmi_get_tmdsclock(hdmi, mode.clock * 10); ++ if (hdmi_bus_fmt_is_yuv420(hdmi->output_bus_format)) ++ bus_width /= 2; ++ ++ if (color_depth == 10) ++ bus_width |= COLOR_DEPTH_10BIT; ++ } ++ } ++ ++ hdmi->phy_bus_width = bus_width; ++ ++ if (hdmi->phy) ++ phy_set_bus_width(hdmi->phy, bus_width); ++ ++ s->output_type = DRM_MODE_CONNECTOR_HDMIA; ++ s->tv_state = &conn_state->tv; ++ ++ if (hdmi->plat_data->split_mode) { ++ s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE; ++ if (hdmi->plat_data->right && hdmi->id) ++ s->output_flags |= ROCKCHIP_OUTPUT_DATA_SWAP; ++ s->output_if |= VOP_OUTPUT_IF_HDMI0 | VOP_OUTPUT_IF_HDMI1; ++ } else { ++ if (!hdmi->id) ++ s->output_if |= VOP_OUTPUT_IF_HDMI0; ++ else ++ s->output_if |= VOP_OUTPUT_IF_HDMI1; ++ } ++ ++ s->output_mode = output_mode; ++ hdmi->bus_format = s->bus_format; ++ ++ if (hdmi->enc_out_encoding == V4L2_YCBCR_ENC_BT2020) ++ s->color_space = V4L2_COLORSPACE_BT2020; ++ else if (colorformat == RK_IF_FORMAT_RGB) ++ s->color_space = V4L2_COLORSPACE_DEFAULT; ++ else if (hdmi->enc_out_encoding == V4L2_YCBCR_ENC_709) ++ s->color_space = V4L2_COLORSPACE_REC709; ++ else ++ s->color_space = V4L2_COLORSPACE_SMPTE170M; ++ ++ if (hdmi->plat_data->split_mode && !secondary) { ++ hdmi = rockchip_hdmi_find_by_id(hdmi->dev->driver, !hdmi->id); ++ secondary = true; ++ goto secondary; ++ } ++ ++ return 0; ++} ++ ++static unsigned long ++dw_hdmi_rockchip_get_input_bus_format(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ return hdmi->bus_format; ++} ++ ++static unsigned long ++dw_hdmi_rockchip_get_output_bus_format(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ return hdmi->output_bus_format; ++} ++ ++static unsigned long ++dw_hdmi_rockchip_get_enc_in_encoding(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ return hdmi->enc_out_encoding; ++} ++ ++static unsigned long ++dw_hdmi_rockchip_get_enc_out_encoding(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ return hdmi->enc_out_encoding; ++} ++ ++static unsigned long ++dw_hdmi_rockchip_get_quant_range(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ return hdmi->hdmi_quant_range; ++} ++ ++static struct drm_property * ++dw_hdmi_rockchip_get_hdr_property(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ return hdmi->hdr_panel_metadata_property; ++} ++ ++static struct drm_property_blob * ++dw_hdmi_rockchip_get_hdr_blob(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ return hdmi->hdr_panel_blob_ptr; ++} ++ ++static bool ++dw_hdmi_rockchip_get_color_changed(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ bool ret = false; ++ ++ if (hdmi->color_changed) ++ ret = true; ++ hdmi->color_changed = 0; ++ ++ return ret; ++} ++ ++static int ++dw_hdmi_rockchip_get_yuv422_format(struct drm_connector *connector, ++ struct edid *edid) ++{ ++ if (!connector || !edid) ++ return -EINVAL; ++ ++ return rockchip_drm_get_yuv422_format(connector, edid); ++} ++ ++static int ++dw_hdmi_rockchip_get_edid_dsc_info(void *data, struct edid *edid) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ if (!edid) ++ return -EINVAL; ++ ++ return rockchip_drm_parse_cea_ext(&hdmi->dsc_cap, ++ &hdmi->max_frl_rate_per_lane, ++ &hdmi->max_lanes, edid); ++} ++ ++static int ++dw_hdmi_rockchip_get_next_hdr_data(void *data, struct edid *edid, ++ struct drm_connector *connector) ++{ ++ int ret; ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ struct next_hdr_sink_data *sink_data = &hdmi->next_hdr_data; ++ size_t size = sizeof(*sink_data); ++ struct drm_property *property = hdmi->next_hdr_sink_data_property; ++ struct drm_property_blob *blob = hdmi->hdr_panel_blob_ptr; ++ ++ if (!edid) ++ return -EINVAL; ++ ++ rockchip_drm_parse_next_hdr(sink_data, edid); ++ ++ ret = drm_property_replace_global_blob(connector->dev, &blob, size, sink_data, ++ &connector->base, property); ++ ++ return ret; ++}; ++ ++static ++struct dw_hdmi_link_config *dw_hdmi_rockchip_get_link_cfg(void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ return &hdmi->link_cfg; ++} ++ ++static int dw_hdmi_dclk_set(void *data, bool enable) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ char clk_name[16]; ++ struct clk *dclk; ++ int ret; ++ ++ snprintf(clk_name, sizeof(clk_name), "dclk_vp%d", hdmi->vp_id); ++ ++ dclk = devm_clk_get(hdmi->dev, clk_name); ++ if (IS_ERR(dclk)) { ++ DRM_DEV_ERROR(hdmi->dev, "failed to get %s\n", clk_name); ++ return PTR_ERR(dclk); ++ } ++ ++ if (enable) { ++ ret = clk_prepare_enable(dclk); ++ if (ret < 0) ++ DRM_DEV_ERROR(hdmi->dev, "failed to enable dclk for video port%d - %d\n", ++ hdmi->vp_id, ret); ++ } else { ++ clk_disable_unprepare(dclk); ++ } + + return 0; + } + ++static const struct drm_prop_enum_list color_depth_enum_list[] = { ++ { 0, "Automatic" }, /* Prefer highest color depth */ ++ { 8, "24bit" }, ++ { 10, "30bit" }, ++}; ++ ++static const struct drm_prop_enum_list drm_hdmi_output_enum_list[] = { ++ { RK_IF_FORMAT_RGB, "rgb" }, ++ { RK_IF_FORMAT_YCBCR444, "ycbcr444" }, ++ { RK_IF_FORMAT_YCBCR422, "ycbcr422" }, ++ { RK_IF_FORMAT_YCBCR420, "ycbcr420" }, ++ { RK_IF_FORMAT_YCBCR_HQ, "ycbcr_high_subsampling" }, ++ { RK_IF_FORMAT_YCBCR_LQ, "ycbcr_low_subsampling" }, ++ { RK_IF_FORMAT_MAX, "invalid_output" }, ++}; ++ ++static const struct drm_prop_enum_list quant_range_enum_list[] = { ++ { HDMI_QUANTIZATION_RANGE_DEFAULT, "default" }, ++ { HDMI_QUANTIZATION_RANGE_LIMITED, "limit" }, ++ { HDMI_QUANTIZATION_RANGE_FULL, "full" }, ++}; ++ ++static const struct drm_prop_enum_list output_hdmi_dvi_enum_list[] = { ++ { 0, "auto" }, ++ { 1, "force_hdmi" }, ++ { 2, "force_dvi" }, ++}; ++ ++static const struct drm_prop_enum_list output_type_cap_list[] = { ++ { 0, "DVI" }, ++ { 1, "HDMI" }, ++}; ++ ++static void ++dw_hdmi_rockchip_attach_properties(struct drm_connector *connector, ++ unsigned int color, int version, ++ void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ struct drm_property *prop; ++ struct rockchip_drm_private *private = connector->dev->dev_private; ++ ++ switch (color) { ++ case MEDIA_BUS_FMT_RGB101010_1X30: ++ hdmi->hdmi_output = RK_IF_FORMAT_RGB; ++ hdmi->colordepth = 10; ++ break; ++ case MEDIA_BUS_FMT_YUV8_1X24: ++ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR444; ++ hdmi->colordepth = 8; ++ break; ++ case MEDIA_BUS_FMT_YUV10_1X30: ++ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR444; ++ hdmi->colordepth = 10; ++ break; ++ case MEDIA_BUS_FMT_UYVY10_1X20: ++ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR422; ++ hdmi->colordepth = 10; ++ break; ++ case MEDIA_BUS_FMT_UYVY8_1X16: ++ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR422; ++ hdmi->colordepth = 8; ++ break; ++ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: ++ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR420; ++ hdmi->colordepth = 8; ++ break; ++ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: ++ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR420; ++ hdmi->colordepth = 10; ++ break; ++ default: ++ hdmi->hdmi_output = RK_IF_FORMAT_RGB; ++ hdmi->colordepth = 8; ++ } ++ ++ hdmi->bus_format = color; ++ ++ if (hdmi->hdmi_output == RK_IF_FORMAT_YCBCR422) { ++ if (hdmi->colordepth == 12) ++ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY12_1X24; ++ else if (hdmi->colordepth == 10) ++ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY10_1X20; ++ else ++ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY8_1X16; ++ } else { ++ hdmi->output_bus_format = hdmi->bus_format; ++ } ++ ++ /* RK3368 does not support deep color mode */ ++ if (!hdmi->color_depth_property && !hdmi->unsupported_deep_color) { ++ prop = drm_property_create_enum(connector->dev, 0, ++ RK_IF_PROP_COLOR_DEPTH, ++ color_depth_enum_list, ++ ARRAY_SIZE(color_depth_enum_list)); ++ if (prop) { ++ hdmi->color_depth_property = prop; ++ drm_object_attach_property(&connector->base, prop, 0); ++ } ++ } ++ ++ prop = drm_property_create_enum(connector->dev, 0, RK_IF_PROP_COLOR_FORMAT, ++ drm_hdmi_output_enum_list, ++ ARRAY_SIZE(drm_hdmi_output_enum_list)); ++ if (prop) { ++ hdmi->hdmi_output_property = prop; ++ drm_object_attach_property(&connector->base, prop, 0); ++ } ++ ++ prop = drm_property_create_range(connector->dev, 0, ++ RK_IF_PROP_COLOR_DEPTH_CAPS, ++ 0, 0xff); ++ if (prop) { ++ hdmi->colordepth_capacity = prop; ++ drm_object_attach_property(&connector->base, prop, 0); ++ } ++ ++ prop = drm_property_create_range(connector->dev, 0, ++ RK_IF_PROP_COLOR_FORMAT_CAPS, ++ 0, 0xf); ++ if (prop) { ++ hdmi->outputmode_capacity = prop; ++ drm_object_attach_property(&connector->base, prop, 0); ++ } ++ ++ prop = drm_property_create(connector->dev, ++ DRM_MODE_PROP_BLOB | ++ DRM_MODE_PROP_IMMUTABLE, ++ "HDR_PANEL_METADATA", 0); ++ if (prop) { ++ hdmi->hdr_panel_metadata_property = prop; ++ drm_object_attach_property(&connector->base, prop, 0); ++ } ++ ++ prop = drm_property_create(connector->dev, ++ DRM_MODE_PROP_BLOB | ++ DRM_MODE_PROP_IMMUTABLE, ++ "NEXT_HDR_SINK_DATA", 0); ++ if (prop) { ++ hdmi->next_hdr_sink_data_property = prop; ++ drm_object_attach_property(&connector->base, prop, 0); ++ } ++ ++ prop = drm_property_create_bool(connector->dev, DRM_MODE_PROP_IMMUTABLE, ++ "USER_SPLIT_MODE"); ++ if (prop) { ++ hdmi->user_split_mode_prop = prop; ++ drm_object_attach_property(&connector->base, prop, ++ hdmi->user_split_mode ? 1 : 0); ++ } ++ ++ if (!hdmi->is_hdmi_qp) { ++ prop = drm_property_create_enum(connector->dev, 0, ++ "output_hdmi_dvi", ++ output_hdmi_dvi_enum_list, ++ ARRAY_SIZE(output_hdmi_dvi_enum_list)); ++ if (prop) { ++ hdmi->output_hdmi_dvi = prop; ++ drm_object_attach_property(&connector->base, prop, 0); ++ } ++ ++ prop = drm_property_create_enum(connector->dev, 0, ++ "output_type_capacity", ++ output_type_cap_list, ++ ARRAY_SIZE(output_type_cap_list)); ++ if (prop) { ++ hdmi->output_type_capacity = prop; ++ drm_object_attach_property(&connector->base, prop, 0); ++ } ++ ++ prop = drm_property_create_enum(connector->dev, 0, ++ "hdmi_quant_range", ++ quant_range_enum_list, ++ ARRAY_SIZE(quant_range_enum_list)); ++ if (prop) { ++ hdmi->quant_range = prop; ++ drm_object_attach_property(&connector->base, prop, 0); ++ } ++ } ++ ++ prop = connector->dev->mode_config.hdr_output_metadata_property; ++ if (version >= 0x211a || hdmi->is_hdmi_qp) ++ drm_object_attach_property(&connector->base, prop, 0); ++ ++ if (!drm_mode_create_hdmi_colorspace_property(connector, 0)) ++ drm_object_attach_property(&connector->base, ++ connector->colorspace_property, 0); ++ ++ // [CC:] if this is not needed, also drop connector_id_prop ++ if (!private->connector_id_prop) ++ private->connector_id_prop = drm_property_create_range(connector->dev, ++ DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_IMMUTABLE, ++ "CONNECTOR_ID", 0, 0xf); ++ if (private->connector_id_prop) ++ drm_object_attach_property(&connector->base, private->connector_id_prop, hdmi->id); ++} ++ ++static void ++dw_hdmi_rockchip_destroy_properties(struct drm_connector *connector, ++ void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ if (hdmi->color_depth_property) { ++ drm_property_destroy(connector->dev, ++ hdmi->color_depth_property); ++ hdmi->color_depth_property = NULL; ++ } ++ ++ if (hdmi->hdmi_output_property) { ++ drm_property_destroy(connector->dev, ++ hdmi->hdmi_output_property); ++ hdmi->hdmi_output_property = NULL; ++ } ++ ++ if (hdmi->colordepth_capacity) { ++ drm_property_destroy(connector->dev, ++ hdmi->colordepth_capacity); ++ hdmi->colordepth_capacity = NULL; ++ } ++ ++ if (hdmi->outputmode_capacity) { ++ drm_property_destroy(connector->dev, ++ hdmi->outputmode_capacity); ++ hdmi->outputmode_capacity = NULL; ++ } ++ ++ if (hdmi->quant_range) { ++ drm_property_destroy(connector->dev, ++ hdmi->quant_range); ++ hdmi->quant_range = NULL; ++ } ++ ++ if (hdmi->hdr_panel_metadata_property) { ++ drm_property_destroy(connector->dev, ++ hdmi->hdr_panel_metadata_property); ++ hdmi->hdr_panel_metadata_property = NULL; ++ } ++ ++ if (hdmi->next_hdr_sink_data_property) { ++ drm_property_destroy(connector->dev, ++ hdmi->next_hdr_sink_data_property); ++ hdmi->next_hdr_sink_data_property = NULL; ++ } ++ ++ if (hdmi->output_hdmi_dvi) { ++ drm_property_destroy(connector->dev, ++ hdmi->output_hdmi_dvi); ++ hdmi->output_hdmi_dvi = NULL; ++ } ++ ++ if (hdmi->output_type_capacity) { ++ drm_property_destroy(connector->dev, ++ hdmi->output_type_capacity); ++ hdmi->output_type_capacity = NULL; ++ } ++ ++ if (hdmi->user_split_mode_prop) { ++ drm_property_destroy(connector->dev, ++ hdmi->user_split_mode_prop); ++ hdmi->user_split_mode_prop = NULL; ++ } ++} ++ ++static int ++dw_hdmi_rockchip_set_property(struct drm_connector *connector, ++ struct drm_connector_state *state, ++ struct drm_property *property, ++ u64 val, ++ void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ struct drm_mode_config *config = &connector->dev->mode_config; ++ ++ if (property == hdmi->color_depth_property) { ++ hdmi->colordepth = val; ++ /* If hdmi is disconnected, state->crtc is null */ ++ if (!state->crtc) ++ return 0; ++ if (dw_hdmi_rockchip_check_color(state, hdmi)) ++ hdmi->color_changed++; ++ return 0; ++ } else if (property == hdmi->hdmi_output_property) { ++ hdmi->hdmi_output = val; ++ if (!state->crtc) ++ return 0; ++ if (dw_hdmi_rockchip_check_color(state, hdmi)) ++ hdmi->color_changed++; ++ return 0; ++ } else if (property == hdmi->quant_range) { ++ u64 quant_range = hdmi->hdmi_quant_range; ++ ++ hdmi->hdmi_quant_range = val; ++ if (quant_range != hdmi->hdmi_quant_range) ++ dw_hdmi_set_quant_range(hdmi->hdmi); ++ return 0; ++ } else if (property == config->hdr_output_metadata_property) { ++ return 0; ++ } else if (property == hdmi->output_hdmi_dvi) { ++ if (hdmi->force_output != val) ++ hdmi->color_changed++; ++ hdmi->force_output = val; ++ dw_hdmi_set_output_type(hdmi->hdmi, val); ++ return 0; ++ } else if (property == hdmi->colordepth_capacity) { ++ return 0; ++ } else if (property == hdmi->outputmode_capacity) { ++ return 0; ++ } else if (property == hdmi->output_type_capacity) { ++ return 0; ++ } ++ ++ DRM_ERROR("Unknown property [PROP:%d:%s]\n", ++ property->base.id, property->name); ++ ++ return -EINVAL; ++} ++ ++static int ++dw_hdmi_rockchip_get_property(struct drm_connector *connector, ++ const struct drm_connector_state *state, ++ struct drm_property *property, ++ u64 *val, ++ void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ struct drm_display_info *info = &connector->display_info; ++ struct drm_mode_config *config = &connector->dev->mode_config; ++ ++ if (property == hdmi->color_depth_property) { ++ *val = hdmi->colordepth; ++ return 0; ++ } else if (property == hdmi->hdmi_output_property) { ++ *val = hdmi->hdmi_output; ++ return 0; ++ } else if (property == hdmi->colordepth_capacity) { ++ *val = BIT(RK_IF_DEPTH_8); ++ /* RK3368 only support 8bit */ ++ if (hdmi->unsupported_deep_color) ++ return 0; ++ if (info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30) ++ *val |= BIT(RK_IF_DEPTH_10); ++ if (info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_36) ++ *val |= BIT(RK_IF_DEPTH_12); ++ if (info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_48) ++ *val |= BIT(RK_IF_DEPTH_16); ++ if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30) ++ *val |= BIT(RK_IF_DEPTH_420_10); ++ if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36) ++ *val |= BIT(RK_IF_DEPTH_420_12); ++ if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48) ++ *val |= BIT(RK_IF_DEPTH_420_16); ++ return 0; ++ } else if (property == hdmi->outputmode_capacity) { ++ *val = BIT(RK_IF_FORMAT_RGB); ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444) ++ *val |= BIT(RK_IF_FORMAT_YCBCR444); ++ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) ++ *val |= BIT(RK_IF_FORMAT_YCBCR422); ++ if (connector->ycbcr_420_allowed && ++ info->color_formats & DRM_COLOR_FORMAT_YCBCR420) ++ *val |= BIT(RK_IF_FORMAT_YCBCR420); ++ return 0; ++ } else if (property == hdmi->quant_range) { ++ *val = hdmi->hdmi_quant_range; ++ return 0; ++ } else if (property == config->hdr_output_metadata_property) { ++ *val = state->hdr_output_metadata ? ++ state->hdr_output_metadata->base.id : 0; ++ return 0; ++ } else if (property == hdmi->output_hdmi_dvi) { ++ *val = hdmi->force_output; ++ return 0; ++ } else if (property == hdmi->output_type_capacity) { ++ *val = dw_hdmi_get_output_type_cap(hdmi->hdmi); ++ return 0; ++ } else if (property == hdmi->user_split_mode_prop) { ++ *val = hdmi->user_split_mode; ++ return 0; ++ } ++ ++ DRM_ERROR("Unknown property [PROP:%d:%s]\n", ++ property->base.id, property->name); ++ ++ return -EINVAL; ++} ++ ++static const struct dw_hdmi_property_ops dw_hdmi_rockchip_property_ops = { ++ .attach_properties = dw_hdmi_rockchip_attach_properties, ++ .destroy_properties = dw_hdmi_rockchip_destroy_properties, ++ .set_property = dw_hdmi_rockchip_set_property, ++ .get_property = dw_hdmi_rockchip_get_property, ++}; ++ + static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = { + .mode_fixup = dw_hdmi_rockchip_encoder_mode_fixup, + .mode_set = dw_hdmi_rockchip_encoder_mode_set, +@@ -357,20 +2526,24 @@ static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_fun + .atomic_check = dw_hdmi_rockchip_encoder_atomic_check, + }; + +-static int dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data, +- const struct drm_display_info *display, +- const struct drm_display_mode *mode) ++static void dw_hdmi_rockchip_genphy_disable(struct dw_hdmi *dw_hdmi, void *data) + { + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + +- return phy_power_on(hdmi->phy); ++ while (hdmi->phy->power_count > 0) ++ phy_power_off(hdmi->phy); + } + +-static void dw_hdmi_rockchip_genphy_disable(struct dw_hdmi *dw_hdmi, void *data) ++static int dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data, ++ const struct drm_display_info *display, ++ const struct drm_display_mode *mode) + { + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + +- phy_power_off(hdmi->phy); ++ dw_hdmi_rockchip_genphy_disable(dw_hdmi, data); ++ dw_hdmi_set_high_tmds_clock_ratio(dw_hdmi, display); ++ ++ return phy_power_on(hdmi->phy); + } + + static void dw_hdmi_rk3228_setup_hpd(struct dw_hdmi *dw_hdmi, void *data) +@@ -437,6 +2610,90 @@ static void dw_hdmi_rk3328_setup_hpd(struct dw_hdmi *dw_hdmi, void *data) + RK3328_HDMI_HPD_IOE)); + } + ++static void dw_hdmi_qp_rockchip_phy_disable(struct dw_hdmi_qp *dw_hdmi, ++ void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ while (hdmi->phy->power_count > 0) ++ phy_power_off(hdmi->phy); ++} ++ ++static int dw_hdmi_qp_rockchip_genphy_init(struct dw_hdmi_qp *dw_hdmi, void *data, ++ struct drm_display_mode *mode) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ dw_hdmi_qp_rockchip_phy_disable(dw_hdmi, data); ++ ++ return phy_power_on(hdmi->phy); ++} ++ ++static enum drm_connector_status ++dw_hdmi_rk3588_read_hpd(struct dw_hdmi_qp *dw_hdmi, void *data) ++{ ++ u32 val; ++ int ret; ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &val); ++ ++ if (!hdmi->id) { ++ if (val & RK3588_HDMI0_LEVEL_INT) { ++ hdmi->hpd_stat = true; ++ ret = connector_status_connected; ++ } else { ++ hdmi->hpd_stat = false; ++ ret = connector_status_disconnected; ++ } ++ } else { ++ if (val & RK3588_HDMI1_LEVEL_INT) { ++ hdmi->hpd_stat = true; ++ ret = connector_status_connected; ++ } else { ++ hdmi->hpd_stat = false; ++ ret = connector_status_disconnected; ++ } ++ } ++ ++ return ret; ++} ++ ++static void dw_hdmi_rk3588_setup_hpd(struct dw_hdmi_qp *dw_hdmi, void *data) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ u32 val; ++ ++ if (!hdmi->id) { ++ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR, ++ RK3588_HDMI0_HPD_INT_CLR) | ++ HIWORD_UPDATE(0, RK3588_HDMI0_HPD_INT_MSK); ++ } else { ++ val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR, ++ RK3588_HDMI1_HPD_INT_CLR) | ++ HIWORD_UPDATE(0, RK3588_HDMI1_HPD_INT_MSK); ++ } ++ ++ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); ++} ++ ++static void dw_hdmi_rk3588_phy_set_mode(struct dw_hdmi_qp *dw_hdmi, void *data, ++ u32 mode_mask, bool enable) ++{ ++ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; ++ ++ if (!hdmi->phy) ++ return; ++ ++ /* set phy earc/frl mode */ ++ if (enable) ++ hdmi->phy_bus_width |= mode_mask; ++ else ++ hdmi->phy_bus_width &= ~mode_mask; ++ ++ phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width); ++} ++ + static const struct dw_hdmi_phy_ops rk3228_hdmi_phy_ops = { + .init = dw_hdmi_rockchip_genphy_init, + .disable = dw_hdmi_rockchip_genphy_disable, +@@ -526,6 +2783,30 @@ static const struct dw_hdmi_plat_data rk3568_hdmi_drv_data = { + .use_drm_infoframe = true, + }; + ++static const struct dw_hdmi_qp_phy_ops rk3588_hdmi_phy_ops = { ++ .init = dw_hdmi_qp_rockchip_genphy_init, ++ .disable = dw_hdmi_qp_rockchip_phy_disable, ++ .read_hpd = dw_hdmi_rk3588_read_hpd, ++ .setup_hpd = dw_hdmi_rk3588_setup_hpd, ++ .set_mode = dw_hdmi_rk3588_phy_set_mode, ++}; ++ ++struct rockchip_hdmi_chip_data rk3588_hdmi_chip_data = { ++ .lcdsel_grf_reg = -1, ++ .ddc_en_reg = RK3588_GRF_VO1_CON3, ++ .split_mode = true, ++}; ++ ++static const struct dw_hdmi_plat_data rk3588_hdmi_drv_data = { ++ .phy_data = &rk3588_hdmi_chip_data, ++ .qp_phy_ops = &rk3588_hdmi_phy_ops, ++ .phy_name = "samsung_hdptx_phy", ++ .phy_force_vendor = true, ++ .ycbcr_420_allowed = true, ++ .is_hdmi_qp = true, ++ .use_drm_infoframe = true, ++}; ++ + static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = { + { .compatible = "rockchip,rk3228-dw-hdmi", + .data = &rk3228_hdmi_drv_data +@@ -542,6 +2823,9 @@ static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = { + { .compatible = "rockchip,rk3568-dw-hdmi", + .data = &rk3568_hdmi_drv_data + }, ++ { .compatible = "rockchip,rk3588-dw-hdmi", ++ .data = &rk3588_hdmi_drv_data ++ }, + {}, + }; + MODULE_DEVICE_TABLE(of, dw_hdmi_rockchip_dt_ids); +@@ -551,44 +2835,107 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, + { + struct platform_device *pdev = to_platform_device(dev); + struct dw_hdmi_plat_data *plat_data; +- const struct of_device_id *match; + struct drm_device *drm = data; + struct drm_encoder *encoder; + struct rockchip_hdmi *hdmi; ++ struct rockchip_hdmi *secondary; + int ret; ++ u32 val; + + if (!pdev->dev.of_node) + return -ENODEV; + +- hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); ++ hdmi = platform_get_drvdata(pdev); + if (!hdmi) + return -ENOMEM; + +- match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node); +- plat_data = devm_kmemdup(&pdev->dev, match->data, +- sizeof(*plat_data), GFP_KERNEL); +- if (!plat_data) +- return -ENOMEM; ++ plat_data = hdmi->plat_data; ++ hdmi->drm_dev = drm; + +- hdmi->dev = &pdev->dev; +- hdmi->plat_data = plat_data; +- hdmi->chip_data = plat_data->phy_data; + plat_data->phy_data = hdmi; +- plat_data->priv_data = hdmi; +- encoder = &hdmi->encoder.encoder; ++ plat_data->get_input_bus_format = ++ dw_hdmi_rockchip_get_input_bus_format; ++ plat_data->get_output_bus_format = ++ dw_hdmi_rockchip_get_output_bus_format; ++ plat_data->get_enc_in_encoding = ++ dw_hdmi_rockchip_get_enc_in_encoding; ++ plat_data->get_enc_out_encoding = ++ dw_hdmi_rockchip_get_enc_out_encoding; ++ plat_data->get_quant_range = ++ dw_hdmi_rockchip_get_quant_range; ++ plat_data->get_hdr_property = ++ dw_hdmi_rockchip_get_hdr_property; ++ plat_data->get_hdr_blob = ++ dw_hdmi_rockchip_get_hdr_blob; ++ plat_data->get_color_changed = ++ dw_hdmi_rockchip_get_color_changed; ++ plat_data->get_yuv422_format = ++ dw_hdmi_rockchip_get_yuv422_format; ++ plat_data->get_edid_dsc_info = ++ dw_hdmi_rockchip_get_edid_dsc_info; ++ plat_data->get_next_hdr_data = ++ dw_hdmi_rockchip_get_next_hdr_data; ++ plat_data->get_link_cfg = dw_hdmi_rockchip_get_link_cfg; ++ plat_data->set_grf_cfg = rk3588_set_grf_cfg; ++ plat_data->convert_to_split_mode = drm_mode_convert_to_split_mode; ++ plat_data->convert_to_origin_mode = drm_mode_convert_to_origin_mode; ++ plat_data->dclk_set = dw_hdmi_dclk_set; ++ ++ plat_data->property_ops = &dw_hdmi_rockchip_property_ops; ++ ++ secondary = rockchip_hdmi_find_by_id(dev->driver, !hdmi->id); ++ /* If don't enable hdmi0 and hdmi1, we don't enable split mode */ ++ if (hdmi->chip_data->split_mode && secondary) { + +- encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); +- rockchip_drm_encoder_set_crtc_endpoint_id(&hdmi->encoder, +- dev->of_node, 0, 0); ++ /* ++ * hdmi can only attach bridge and init encoder/connector in the ++ * last bind hdmi in split mode, or hdmi->hdmi_qp will not be initialized ++ * and plat_data->left/right will be null pointer. we must check if split ++ * mode is on and determine the sequence of hdmi bind. ++ */ ++ if (device_property_read_bool(dev, "split-mode") || ++ device_property_read_bool(secondary->dev, "split-mode")) { ++ plat_data->split_mode = true; ++ secondary->plat_data->split_mode = true; ++ if (!secondary->plat_data->first_screen) ++ plat_data->first_screen = true; ++ } ++ ++ if (device_property_read_bool(dev, "user-split-mode") || ++ device_property_read_bool(secondary->dev, "user-split-mode")) { ++ hdmi->user_split_mode = true; ++ secondary->user_split_mode = true; ++ } ++ } + +- /* +- * If we failed to find the CRTC(s) which this encoder is +- * supposed to be connected to, it's because the CRTC has +- * not been registered yet. Defer probing, and hope that +- * the required CRTC is added later. +- */ +- if (encoder->possible_crtcs == 0) +- return -EPROBE_DEFER; ++ if (!plat_data->first_screen) { ++ encoder = &hdmi->encoder.encoder; ++ encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); ++ rockchip_drm_encoder_set_crtc_endpoint_id(&hdmi->encoder, ++ dev->of_node, 0, 0); ++ /* ++ * If we failed to find the CRTC(s) which this encoder is ++ * supposed to be connected to, it's because the CRTC has ++ * not been registered yet. Defer probing, and hope that ++ * the required CRTC is added later. ++ */ ++ if (encoder->possible_crtcs == 0) ++ return -EPROBE_DEFER; ++ ++ drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs); ++ // [CC:] consider using drmm_simple_encoder_alloc() ++ drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); ++ } ++ ++ if (!plat_data->max_tmdsclk) ++ hdmi->max_tmdsclk = 594000; ++ else ++ hdmi->max_tmdsclk = plat_data->max_tmdsclk; ++ ++ hdmi->is_hdmi_qp = plat_data->is_hdmi_qp; ++ ++ hdmi->unsupported_yuv_input = plat_data->unsupported_yuv_input; ++ hdmi->unsupported_deep_color = plat_data->unsupported_deep_color; + + ret = rockchip_hdmi_parse_dt(hdmi); + if (ret) { +@@ -597,34 +2944,44 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, + return ret; + } + +- hdmi->phy = devm_phy_optional_get(dev, "hdmi"); +- if (IS_ERR(hdmi->phy)) { +- ret = PTR_ERR(hdmi->phy); +- if (ret != -EPROBE_DEFER) +- DRM_DEV_ERROR(hdmi->dev, "failed to get phy\n"); ++ ret = clk_prepare_enable(hdmi->aud_clk); ++ if (ret) { ++ dev_err(hdmi->dev, "Failed to enable HDMI aud_clk: %d\n", ret); + return ret; + } + +- ret = regulator_enable(hdmi->avdd_0v9); ++ ret = clk_prepare_enable(hdmi->hpd_clk); + if (ret) { +- DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd0v9: %d\n", ret); +- goto err_avdd_0v9; ++ dev_err(hdmi->dev, "Failed to enable HDMI hpd_clk: %d\n", ret); ++ return ret; + } + +- ret = regulator_enable(hdmi->avdd_1v8); ++ ret = clk_prepare_enable(hdmi->hclk_vo1); + if (ret) { +- DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd1v8: %d\n", ret); +- goto err_avdd_1v8; ++ dev_err(hdmi->dev, "Failed to enable HDMI hclk_vo1: %d\n", ret); ++ return ret; + } + +- ret = clk_prepare_enable(hdmi->ref_clk); ++ ret = clk_prepare_enable(hdmi->earc_clk); + if (ret) { +- DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI reference clock: %d\n", +- ret); +- goto err_clk; ++ dev_err(hdmi->dev, "Failed to enable HDMI earc_clk: %d\n", ret); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(hdmi->hdmitx_ref); ++ if (ret) { ++ dev_err(hdmi->dev, "Failed to enable HDMI hdmitx_ref: %d\n", ++ ret); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(hdmi->pclk); ++ if (ret) { ++ dev_err(hdmi->dev, "Failed to enable HDMI pclk: %d\n", ret); ++ return ret; + } + +- if (hdmi->chip_data == &rk3568_chip_data) { ++ if (hdmi->chip_data->ddc_en_reg == RK3568_GRF_VO_CON1) { + regmap_write(hdmi->regmap, RK3568_GRF_VO_CON1, + HIWORD_UPDATE(RK3568_HDMI_SDAIN_MSK | + RK3568_HDMI_SCLIN_MSK, +@@ -632,12 +2989,132 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, + RK3568_HDMI_SCLIN_MSK)); + } + +- drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs); +- drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); ++ if (hdmi->is_hdmi_qp) { ++ if (!hdmi->id) { ++ val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) | ++ HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) | ++ HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) | ++ HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK); ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); ++ ++ val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK, ++ RK3588_SET_HPD_PATH_MASK); ++ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val); ++ ++ val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL, ++ RK3588_HDMI0_GRANT_SEL); ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val); ++ } else { ++ val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) | ++ HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) | ++ HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) | ++ HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK); ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); ++ ++ val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK, ++ RK3588_SET_HPD_PATH_MASK); ++ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val); ++ ++ val = HIWORD_UPDATE(RK3588_HDMI1_GRANT_SEL, ++ RK3588_HDMI1_GRANT_SEL); ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val); ++ } ++ init_hpd_work(hdmi); ++ } + +- platform_set_drvdata(pdev, hdmi); ++ ret = clk_prepare_enable(hdmi->hclk_vio); ++ if (ret) { ++ dev_err(hdmi->dev, "Failed to enable HDMI hclk_vio: %d\n", ++ ret); ++ return ret; ++ } + +- hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data); ++ ret = clk_prepare_enable(hdmi->hclk_vop); ++ if (ret) { ++ dev_err(hdmi->dev, "Failed to enable HDMI hclk_vop: %d\n", ++ ret); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(hdmi->ref_clk); ++ if (ret) { ++ DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI reference clock: %d\n", ++ ret); ++ goto err_clk; ++ } ++ ++ ret = regulator_enable(hdmi->avdd_0v9); ++ if (ret) { ++ DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd0v9: %d\n", ret); ++ goto err_avdd_0v9; ++ } ++ ++ ret = regulator_enable(hdmi->avdd_1v8); ++ if (ret) { ++ DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd1v8: %d\n", ret); ++ goto err_avdd_1v8; ++ } ++ ++ if (!hdmi->id) ++ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK, RK3588_HDMI0_HPD_INT_MSK); ++ else ++ val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_MSK, RK3588_HDMI1_HPD_INT_MSK); ++ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); ++ ++ if (hdmi->is_hdmi_qp) { ++ hdmi->hpd_irq = platform_get_irq(pdev, 4); ++ if (hdmi->hpd_irq < 0) ++ return hdmi->hpd_irq; ++ ++ ret = devm_request_threaded_irq(hdmi->dev, hdmi->hpd_irq, ++ rockchip_hdmi_hardirq, ++ rockchip_hdmi_irq, ++ IRQF_SHARED, "dw-hdmi-qp-hpd", ++ hdmi); ++ if (ret) ++ return ret; ++ } ++ ++ hdmi->phy = devm_phy_optional_get(dev, "hdmi"); ++ if (IS_ERR(hdmi->phy)) { ++ hdmi->phy = devm_phy_optional_get(dev, "hdmi_phy"); ++ if (IS_ERR(hdmi->phy)) { ++ ret = PTR_ERR(hdmi->phy); ++ if (ret != -EPROBE_DEFER) ++ DRM_DEV_ERROR(hdmi->dev, "failed to get phy\n"); ++ return ret; ++ } ++ } ++ ++ if (hdmi->is_hdmi_qp) { ++ // [CC:] do proper error handling, e.g. clk_disable_unprepare ++ hdmi->hdmi_qp = dw_hdmi_qp_bind(pdev, &hdmi->encoder.encoder, plat_data); ++ ++ if (IS_ERR(hdmi->hdmi_qp)) { ++ ret = PTR_ERR(hdmi->hdmi_qp); ++ drm_encoder_cleanup(&hdmi->encoder.encoder); ++ } ++ ++ if (plat_data->connector) { ++ hdmi->sub_dev.connector = plat_data->connector; ++ hdmi->sub_dev.of_node = dev->of_node; ++ rockchip_drm_register_sub_dev(&hdmi->sub_dev); ++ } ++ ++ if (plat_data->split_mode && secondary) { ++ if (device_property_read_bool(dev, "split-mode")) { ++ plat_data->right = secondary->hdmi_qp; ++ secondary->plat_data->left = hdmi->hdmi_qp; ++ } else { ++ plat_data->left = secondary->hdmi_qp; ++ secondary->plat_data->right = hdmi->hdmi_qp; ++ } ++ } ++ ++ return ret; ++ } ++ ++ hdmi->hdmi = dw_hdmi_bind(pdev, &hdmi->encoder.encoder, plat_data); + + /* + * If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(), +@@ -648,11 +3125,24 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, + goto err_bind; + } + ++ if (plat_data->connector) { ++ hdmi->sub_dev.connector = plat_data->connector; ++ hdmi->sub_dev.of_node = dev->of_node; ++ rockchip_drm_register_sub_dev(&hdmi->sub_dev); ++ } ++ + return 0; + + err_bind: +- drm_encoder_cleanup(encoder); ++ drm_encoder_cleanup(&hdmi->encoder.encoder); ++ clk_disable_unprepare(hdmi->aud_clk); + clk_disable_unprepare(hdmi->ref_clk); ++ clk_disable_unprepare(hdmi->hclk_vop); ++ clk_disable_unprepare(hdmi->hpd_clk); ++ clk_disable_unprepare(hdmi->hclk_vo1); ++ clk_disable_unprepare(hdmi->earc_clk); ++ clk_disable_unprepare(hdmi->hdmitx_ref); ++ clk_disable_unprepare(hdmi->pclk); + err_clk: + regulator_disable(hdmi->avdd_1v8); + err_avdd_1v8: +@@ -666,9 +3156,30 @@ static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master, + { + struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); + +- dw_hdmi_unbind(hdmi->hdmi); ++ if (hdmi->is_hdmi_qp) { ++ cancel_delayed_work(&hdmi->work); ++ flush_workqueue(hdmi->workqueue); ++ destroy_workqueue(hdmi->workqueue); ++ } ++ ++ if (hdmi->sub_dev.connector) ++ rockchip_drm_unregister_sub_dev(&hdmi->sub_dev); ++ ++ if (hdmi->is_hdmi_qp) ++ dw_hdmi_qp_unbind(hdmi->hdmi_qp); ++ else ++ dw_hdmi_unbind(hdmi->hdmi); ++ + drm_encoder_cleanup(&hdmi->encoder.encoder); ++ ++ clk_disable_unprepare(hdmi->aud_clk); + clk_disable_unprepare(hdmi->ref_clk); ++ clk_disable_unprepare(hdmi->hclk_vop); ++ clk_disable_unprepare(hdmi->hpd_clk); ++ clk_disable_unprepare(hdmi->hclk_vo1); ++ clk_disable_unprepare(hdmi->earc_clk); ++ clk_disable_unprepare(hdmi->hdmitx_ref); ++ clk_disable_unprepare(hdmi->pclk); + + regulator_disable(hdmi->avdd_1v8); + regulator_disable(hdmi->avdd_0v9); +@@ -681,30 +3192,131 @@ static const struct component_ops dw_hdmi_rockchip_ops = { + + static int dw_hdmi_rockchip_probe(struct platform_device *pdev) + { ++ struct rockchip_hdmi *hdmi; ++ const struct of_device_id *match; ++ struct dw_hdmi_plat_data *plat_data; ++ int id; ++ ++ hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); ++ if (!hdmi) ++ return -ENOMEM; ++ ++ id = of_alias_get_id(pdev->dev.of_node, "hdmi"); ++ if (id < 0) ++ id = 0; ++ ++ hdmi->id = id; ++ hdmi->dev = &pdev->dev; ++ ++ match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node); ++ plat_data = devm_kmemdup(&pdev->dev, match->data, ++ sizeof(*plat_data), GFP_KERNEL); ++ if (!plat_data) ++ return -ENOMEM; ++ ++ plat_data->id = hdmi->id; ++ hdmi->plat_data = plat_data; ++ hdmi->chip_data = plat_data->phy_data; ++ ++ platform_set_drvdata(pdev, hdmi); ++ pm_runtime_enable(&pdev->dev); ++ pm_runtime_get_sync(&pdev->dev); ++ + return component_add(&pdev->dev, &dw_hdmi_rockchip_ops); + } + ++static void dw_hdmi_rockchip_shutdown(struct platform_device *pdev) ++{ ++ struct rockchip_hdmi *hdmi = dev_get_drvdata(&pdev->dev); ++ ++ if (!hdmi) ++ return; ++ ++ if (hdmi->is_hdmi_qp) { ++ cancel_delayed_work(&hdmi->work); ++ flush_workqueue(hdmi->workqueue); ++ dw_hdmi_qp_suspend(hdmi->dev, hdmi->hdmi_qp); ++ } else { ++ dw_hdmi_suspend(hdmi->hdmi); ++ } ++ pm_runtime_put_sync(&pdev->dev); ++} ++ + static void dw_hdmi_rockchip_remove(struct platform_device *pdev) + { + component_del(&pdev->dev, &dw_hdmi_rockchip_ops); ++ pm_runtime_disable(&pdev->dev); ++} ++ ++static int dw_hdmi_rockchip_suspend(struct device *dev) ++{ ++ struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); ++ ++ if (hdmi->is_hdmi_qp) ++ dw_hdmi_qp_suspend(dev, hdmi->hdmi_qp); ++ else ++ dw_hdmi_suspend(hdmi->hdmi); ++ ++ pm_runtime_put_sync(dev); ++ ++ return 0; + } + + static int __maybe_unused dw_hdmi_rockchip_resume(struct device *dev) + { + struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); ++ u32 val; + +- dw_hdmi_resume(hdmi->hdmi); ++ if (hdmi->is_hdmi_qp) { ++ if (!hdmi->id) { ++ val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) | ++ HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) | ++ HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) | ++ HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK); ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val); ++ ++ val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK, ++ RK3588_SET_HPD_PATH_MASK); ++ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val); ++ ++ val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL, ++ RK3588_HDMI0_GRANT_SEL); ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val); ++ } else { ++ val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) | ++ HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) | ++ HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) | ++ HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK); ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val); ++ ++ val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK, ++ RK3588_SET_HPD_PATH_MASK); ++ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val); ++ ++ val = HIWORD_UPDATE(RK3588_HDMI1_GRANT_SEL, ++ RK3588_HDMI1_GRANT_SEL); ++ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val); ++ } ++ ++ dw_hdmi_qp_resume(dev, hdmi->hdmi_qp); ++ drm_helper_hpd_irq_event(hdmi->drm_dev); ++ } else { ++ dw_hdmi_resume(hdmi->hdmi); ++ } ++ pm_runtime_get_sync(dev); + + return 0; + } + + static const struct dev_pm_ops dw_hdmi_rockchip_pm = { +- SET_SYSTEM_SLEEP_PM_OPS(NULL, dw_hdmi_rockchip_resume) ++ SET_SYSTEM_SLEEP_PM_OPS(dw_hdmi_rockchip_suspend, ++ dw_hdmi_rockchip_resume) + }; + + struct platform_driver dw_hdmi_rockchip_pltfm_driver = { + .probe = dw_hdmi_rockchip_probe, + .remove_new = dw_hdmi_rockchip_remove, ++ .shutdown = dw_hdmi_rockchip_shutdown, + .driver = { + .name = "dwhdmi-rockchip", + .pm = &dw_hdmi_rockchip_pm, +diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h +index 6a46baa0737c..9042039f2222 100644 +--- a/include/drm/bridge/dw_hdmi.h ++++ b/include/drm/bridge/dw_hdmi.h +@@ -6,12 +6,14 @@ + #ifndef __DW_HDMI__ + #define __DW_HDMI__ + ++#include + #include + + struct drm_display_info; + struct drm_display_mode; + struct drm_encoder; + struct dw_hdmi; ++struct dw_hdmi_qp; + struct platform_device; + + /** +@@ -92,6 +94,13 @@ enum dw_hdmi_phy_type { + DW_HDMI_PHY_VENDOR_PHY = 0xfe, + }; + ++struct dw_hdmi_audio_tmds_n { ++ unsigned long tmds; ++ unsigned int n_32k; ++ unsigned int n_44k1; ++ unsigned int n_48k; ++}; ++ + struct dw_hdmi_mpll_config { + unsigned long mpixelclock; + struct { +@@ -112,6 +121,15 @@ struct dw_hdmi_phy_config { + u16 vlev_ctr; /* voltage level control */ + }; + ++struct dw_hdmi_link_config { ++ bool dsc_mode; ++ bool frl_mode; ++ int frl_lanes; ++ int rate_per_lane; ++ int hcactive; ++ u8 pps_payload[128]; ++}; ++ + struct dw_hdmi_phy_ops { + int (*init)(struct dw_hdmi *hdmi, void *data, + const struct drm_display_info *display, +@@ -123,14 +141,52 @@ struct dw_hdmi_phy_ops { + void (*setup_hpd)(struct dw_hdmi *hdmi, void *data); + }; + ++struct dw_hdmi_qp_phy_ops { ++ int (*init)(struct dw_hdmi_qp *hdmi, void *data, ++ struct drm_display_mode *mode); ++ void (*disable)(struct dw_hdmi_qp *hdmi, void *data); ++ enum drm_connector_status (*read_hpd)(struct dw_hdmi_qp *hdmi, ++ void *data); ++ void (*update_hpd)(struct dw_hdmi_qp *hdmi, void *data, ++ bool force, bool disabled, bool rxsense); ++ void (*setup_hpd)(struct dw_hdmi_qp *hdmi, void *data); ++ void (*set_mode)(struct dw_hdmi_qp *dw_hdmi, void *data, ++ u32 mode_mask, bool enable); ++}; ++ ++struct dw_hdmi_property_ops { ++ void (*attach_properties)(struct drm_connector *connector, ++ unsigned int color, int version, ++ void *data); ++ void (*destroy_properties)(struct drm_connector *connector, ++ void *data); ++ int (*set_property)(struct drm_connector *connector, ++ struct drm_connector_state *state, ++ struct drm_property *property, ++ u64 val, ++ void *data); ++ int (*get_property)(struct drm_connector *connector, ++ const struct drm_connector_state *state, ++ struct drm_property *property, ++ u64 *val, ++ void *data); ++}; ++ + struct dw_hdmi_plat_data { + struct regmap *regm; + ++ //[CC:] not in dowstream + unsigned int output_port; + ++ unsigned long input_bus_format; + unsigned long input_bus_encoding; ++ unsigned int max_tmdsclk; ++ int id; + bool use_drm_infoframe; + bool ycbcr_420_allowed; ++ bool unsupported_yuv_input; ++ bool unsupported_deep_color; ++ bool is_hdmi_qp; + + /* + * Private data passed to all the .mode_valid() and .configure_phy() +@@ -139,6 +195,7 @@ struct dw_hdmi_plat_data { + void *priv_data; + + /* Platform-specific mode validation (optional). */ ++ //[CC:] downstream changed "struct dw_hdmi *hdmi" to "struct drm_connector *connector" + enum drm_mode_status (*mode_valid)(struct dw_hdmi *hdmi, void *data, + const struct drm_display_info *info, + const struct drm_display_mode *mode); +@@ -150,18 +207,51 @@ struct dw_hdmi_plat_data { + + /* Vendor PHY support */ + const struct dw_hdmi_phy_ops *phy_ops; ++ const struct dw_hdmi_qp_phy_ops *qp_phy_ops; + const char *phy_name; + void *phy_data; + unsigned int phy_force_vendor; + ++ /* split mode */ ++ bool split_mode; ++ bool first_screen; ++ struct dw_hdmi_qp *left; ++ struct dw_hdmi_qp *right; ++ + /* Synopsys PHY support */ + const struct dw_hdmi_mpll_config *mpll_cfg; ++ const struct dw_hdmi_mpll_config *mpll_cfg_420; + const struct dw_hdmi_curr_ctrl *cur_ctr; + const struct dw_hdmi_phy_config *phy_config; + int (*configure_phy)(struct dw_hdmi *hdmi, void *data, + unsigned long mpixelclock); + + unsigned int disable_cec : 1; ++ ++ //[CC:] 7b29b5f29585 ("drm/rockchip: dw_hdmi: Support HDMI 2.0 YCbCr 4:2:0") ++ unsigned long (*get_input_bus_format)(void *data); ++ unsigned long (*get_output_bus_format)(void *data); ++ unsigned long (*get_enc_in_encoding)(void *data); ++ unsigned long (*get_enc_out_encoding)(void *data); ++ ++ unsigned long (*get_quant_range)(void *data); ++ struct drm_property *(*get_hdr_property)(void *data); ++ struct drm_property_blob *(*get_hdr_blob)(void *data); ++ bool (*get_color_changed)(void *data); ++ int (*get_yuv422_format)(struct drm_connector *connector, ++ struct edid *edid); ++ int (*get_edid_dsc_info)(void *data, struct edid *edid); ++ int (*get_next_hdr_data)(void *data, struct edid *edid, ++ struct drm_connector *connector); ++ struct dw_hdmi_link_config *(*get_link_cfg)(void *data); ++ void (*set_grf_cfg)(void *data); ++ void (*convert_to_split_mode)(struct drm_display_mode *mode); ++ void (*convert_to_origin_mode)(struct drm_display_mode *mode); ++ int (*dclk_set)(void *data, bool enable); ++ ++ /* Vendor Property support */ ++ const struct dw_hdmi_property_ops *property_ops; ++ struct drm_connector *connector; + }; + + struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, +@@ -172,6 +262,7 @@ struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev, + struct drm_encoder *encoder, + const struct dw_hdmi_plat_data *plat_data); + ++void dw_hdmi_suspend(struct dw_hdmi *hdmi); + void dw_hdmi_resume(struct dw_hdmi *hdmi); + + void dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense); +@@ -205,6 +296,32 @@ enum drm_connector_status dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi, + void dw_hdmi_phy_update_hpd(struct dw_hdmi *hdmi, void *data, + bool force, bool disabled, bool rxsense); + void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi, void *data); ++void dw_hdmi_set_quant_range(struct dw_hdmi *hdmi); ++void dw_hdmi_set_output_type(struct dw_hdmi *hdmi, u64 val); ++bool dw_hdmi_get_output_whether_hdmi(struct dw_hdmi *hdmi); ++int dw_hdmi_get_output_type_cap(struct dw_hdmi *hdmi); ++//void dw_hdmi_set_cec_adap(struct dw_hdmi *hdmi, struct cec_adapter *adap); ++ ++void dw_hdmi_qp_unbind(struct dw_hdmi_qp *hdmi); ++struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev, ++ struct drm_encoder *encoder, ++ struct dw_hdmi_plat_data *plat_data); ++void dw_hdmi_qp_suspend(struct device *dev, struct dw_hdmi_qp *hdmi); ++void dw_hdmi_qp_resume(struct device *dev, struct dw_hdmi_qp *hdmi); ++//void dw_hdmi_qp_cec_set_hpd(struct dw_hdmi_qp *hdmi, bool plug_in, bool change); ++//void dw_hdmi_qp_set_cec_adap(struct dw_hdmi_qp *hdmi, struct cec_adapter *adap); ++int dw_hdmi_qp_set_earc(struct dw_hdmi_qp *hdmi); ++void dw_hdmi_qp_set_sample_rate(struct dw_hdmi_qp *hdmi, unsigned int rate); ++void dw_hdmi_qp_set_channel_count(struct dw_hdmi_qp *hdmi, unsigned int cnt); ++void dw_hdmi_qp_set_channel_status(struct dw_hdmi_qp *hdmi, u8 *channel_status, ++ bool ref2stream); ++void dw_hdmi_qp_set_channel_allocation(struct dw_hdmi_qp *hdmi, unsigned int ca); ++//void dw_hdmi_qp_set_audio_infoframe(struct dw_hdmi_qp *hdmi, ++// struct hdmi_codec_params *hparms); ++//void dw_hdmi_qp_audio_enable(struct dw_hdmi_qp *hdmi); ++//void dw_hdmi_qp_audio_disable(struct dw_hdmi_qp *hdmi); ++int dw_hdmi_qp_set_plugged_cb(struct dw_hdmi_qp *hdmi, hdmi_codec_plugged_cb fn, ++ struct device *codec_dev); + + bool dw_hdmi_bus_fmt_is_420(struct dw_hdmi *hdmi); + +-- +2.42.1 + + +From 140267c1c11d90f4889e57ae6d58280b261081c0 Mon Sep 17 00:00:00 2001 +From: Cristian Ciocaltea +Date: Wed, 1 Nov 2023 19:02:20 +0200 +Subject: [PATCH 4/9] drm/rockchip: vop2: Add basic support for rk3588 + +WARNING: This is work in progress and most features are missing (e.g. +8K support, overlay, etc). + +Currently only tested on Rock5B where it enables basic HDMI output on +port 1 (located next to USB-C). + +The following command can be used to display a basic test pattern: + + modetest -s @: + +e.g.: + + modetest -M rockchip -s 77@73:1920x1080-60 + setting mode 1920x1080-60.00Hz on connectors 77, crtc 73 + +So far, it works for the following modes: + +1920x1080-60.00Hz, 1920x1080-50.00Hz, 1920x1080-30.00Hz, +1920x1080-25.00Hz, 1920x1080-24.00Hz, 1280x1024-60.02Hz, +1280x960-60.00Hz, 1280x720-60.00Hz, 1280x720-50.00Hz, +800x600-75.00Hz, 720x576-50.00Hz + +For some reason (clocking?!) it's not possible to use refresh rates like +59.94, 29.97, 23.98, etc. + +Unexpectedly, it also doesn't work for 2560x1440-75.00Hz, +2048x1152-60.00Hz and 1024x768-60.00Hz. + +I don't have a display supporting higher resolution, hence my tests +were limited to the above modes. + +Signed-off-by: Cristian Ciocaltea +--- + drivers/gpu/drm/drm_displayid.c | 4 + + drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 627 +++++++++++++++++++ + drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 431 ++++++++++++- + drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 23 +- + drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 414 +++++++++++- + drivers/gpu/drm/rockchip/rockchip_drm_vop2.h | 70 ++- + drivers/gpu/drm/rockchip/rockchip_vop2_reg.c | 231 ++++++- + include/dt-bindings/soc/rockchip,vop2.h | 4 + + include/uapi/drm/rockchip_drm.h | 134 ++++ + 9 files changed, 1916 insertions(+), 22 deletions(-) + create mode 100644 include/uapi/drm/rockchip_drm.h + +diff --git a/drivers/gpu/drm/drm_displayid.c b/drivers/gpu/drm/drm_displayid.c +index 9edc111be7ee..a578f918f46d 100644 +--- a/drivers/gpu/drm/drm_displayid.c ++++ b/drivers/gpu/drm/drm_displayid.c +@@ -3,6 +3,7 @@ + * Copyright © 2021 Intel Corporation + */ + ++#include "linux/export.h" + #include + #include + #include +@@ -79,6 +80,7 @@ void displayid_iter_edid_begin(const struct drm_edid *drm_edid, + + iter->drm_edid = drm_edid; + } ++EXPORT_SYMBOL_GPL(displayid_iter_edid_begin); + + static const struct displayid_block * + displayid_iter_block(const struct displayid_iter *iter) +@@ -154,11 +156,13 @@ __displayid_iter_next(struct displayid_iter *iter) + return block; + } + } ++EXPORT_SYMBOL_GPL(__displayid_iter_next); + + void displayid_iter_end(struct displayid_iter *iter) + { + memset(iter, 0, sizeof(*iter)); + } ++EXPORT_SYMBOL_GPL(displayid_iter_end); + + /* DisplayID Structure Version/Revision from the Base Section. */ + u8 displayid_version(const struct displayid_iter *iter) +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +index ab55d7132550..44d49c86b7bd 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +@@ -17,8 +17,12 @@ + #include + + #include ++#include ++#include ++#include + #include + #include ++//#include + #include + #include + #include +@@ -44,6 +48,629 @@ + + static const struct drm_driver rockchip_drm_driver; + ++void drm_mode_convert_to_split_mode(struct drm_display_mode *mode) ++{ ++ u16 hactive, hfp, hsync, hbp; ++ ++ hactive = mode->hdisplay; ++ hfp = mode->hsync_start - mode->hdisplay; ++ hsync = mode->hsync_end - mode->hsync_start; ++ hbp = mode->htotal - mode->hsync_end; ++ ++ mode->clock *= 2; ++ mode->hdisplay = hactive * 2; ++ mode->hsync_start = mode->hdisplay + hfp * 2; ++ mode->hsync_end = mode->hsync_start + hsync * 2; ++ mode->htotal = mode->hsync_end + hbp * 2; ++ drm_mode_set_name(mode); ++} ++EXPORT_SYMBOL(drm_mode_convert_to_split_mode); ++ ++void drm_mode_convert_to_origin_mode(struct drm_display_mode *mode) ++{ ++ u16 hactive, hfp, hsync, hbp; ++ ++ hactive = mode->hdisplay; ++ hfp = mode->hsync_start - mode->hdisplay; ++ hsync = mode->hsync_end - mode->hsync_start; ++ hbp = mode->htotal - mode->hsync_end; ++ ++ mode->clock /= 2; ++ mode->hdisplay = hactive / 2; ++ mode->hsync_start = mode->hdisplay + hfp / 2; ++ mode->hsync_end = mode->hsync_start + hsync / 2; ++ mode->htotal = mode->hsync_end + hbp / 2; ++} ++EXPORT_SYMBOL(drm_mode_convert_to_origin_mode); ++ ++static DEFINE_MUTEX(rockchip_drm_sub_dev_lock); ++static LIST_HEAD(rockchip_drm_sub_dev_list); ++ ++void rockchip_drm_register_sub_dev(struct rockchip_drm_sub_dev *sub_dev) ++{ ++ mutex_lock(&rockchip_drm_sub_dev_lock); ++ list_add_tail(&sub_dev->list, &rockchip_drm_sub_dev_list); ++ mutex_unlock(&rockchip_drm_sub_dev_lock); ++} ++EXPORT_SYMBOL(rockchip_drm_register_sub_dev); ++ ++void rockchip_drm_unregister_sub_dev(struct rockchip_drm_sub_dev *sub_dev) ++{ ++ mutex_lock(&rockchip_drm_sub_dev_lock); ++ list_del(&sub_dev->list); ++ mutex_unlock(&rockchip_drm_sub_dev_lock); ++} ++EXPORT_SYMBOL(rockchip_drm_unregister_sub_dev); ++ ++static int ++cea_db_tag(const u8 *db) ++{ ++ return db[0] >> 5; ++} ++ ++static int ++cea_db_payload_len(const u8 *db) ++{ ++ return db[0] & 0x1f; ++} ++ ++#define for_each_cea_db(cea, i, start, end) \ ++ for ((i) = (start); \ ++ (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); \ ++ (i) += cea_db_payload_len(&(cea)[(i)]) + 1) ++ ++#define HDMI_NEXT_HDR_VSDB_OUI 0xd04601 ++ ++static bool cea_db_is_hdmi_next_hdr_block(const u8 *db) ++{ ++ unsigned int oui; ++ ++ if (cea_db_tag(db) != 0x07) ++ return false; ++ ++ if (cea_db_payload_len(db) < 11) ++ return false; ++ ++ oui = db[3] << 16 | db[2] << 8 | db[1]; ++ ++ return oui == HDMI_NEXT_HDR_VSDB_OUI; ++} ++ ++static bool cea_db_is_hdmi_forum_vsdb(const u8 *db) ++{ ++ unsigned int oui; ++ ++ if (cea_db_tag(db) != 0x03) ++ return false; ++ ++ if (cea_db_payload_len(db) < 7) ++ return false; ++ ++ oui = db[3] << 16 | db[2] << 8 | db[1]; ++ ++ return oui == HDMI_FORUM_IEEE_OUI; ++} ++ ++static int ++cea_db_offsets(const u8 *cea, int *start, int *end) ++{ ++ /* DisplayID CTA extension blocks and top-level CEA EDID ++ * block header definitions differ in the following bytes: ++ * 1) Byte 2 of the header specifies length differently, ++ * 2) Byte 3 is only present in the CEA top level block. ++ * ++ * The different definitions for byte 2 follow. ++ * ++ * DisplayID CTA extension block defines byte 2 as: ++ * Number of payload bytes ++ * ++ * CEA EDID block defines byte 2 as: ++ * Byte number (decimal) within this block where the 18-byte ++ * DTDs begin. If no non-DTD data is present in this extension ++ * block, the value should be set to 04h (the byte after next). ++ * If set to 00h, there are no DTDs present in this block and ++ * no non-DTD data. ++ */ ++ if (cea[0] == 0x81) { ++ /* ++ * for_each_displayid_db() has already verified ++ * that these stay within expected bounds. ++ */ ++ *start = 3; ++ *end = *start + cea[2]; ++ } else if (cea[0] == 0x02) { ++ /* Data block offset in CEA extension block */ ++ *start = 4; ++ *end = cea[2]; ++ if (*end == 0) ++ *end = 127; ++ if (*end < 4 || *end > 127) ++ return -ERANGE; ++ } else { ++ return -EOPNOTSUPP; ++ } ++ ++ return 0; ++} ++ ++static u8 *find_edid_extension(const struct edid *edid, ++ int ext_id, int *ext_index) ++{ ++ u8 *edid_ext = NULL; ++ int i; ++ ++ /* No EDID or EDID extensions */ ++ if (edid == NULL || edid->extensions == 0) ++ return NULL; ++ ++ /* Find CEA extension */ ++ for (i = *ext_index; i < edid->extensions; i++) { ++ edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1); ++ if (edid_ext[0] == ext_id) ++ break; ++ } ++ ++ if (i >= edid->extensions) ++ return NULL; ++ ++ *ext_index = i + 1; ++ ++ return edid_ext; ++} ++ ++static int validate_displayid(u8 *displayid, int length, int idx) ++{ ++ int i, dispid_length; ++ u8 csum = 0; ++ struct displayid_header *base; ++ ++ base = (struct displayid_header *)&displayid[idx]; ++ ++ DRM_DEBUG_KMS("base revision 0x%x, length %d, %d %d\n", ++ base->rev, base->bytes, base->prod_id, base->ext_count); ++ ++ /* +1 for DispID checksum */ ++ dispid_length = sizeof(*base) + base->bytes + 1; ++ if (dispid_length > length - idx) ++ return -EINVAL; ++ ++ for (i = 0; i < dispid_length; i++) ++ csum += displayid[idx + i]; ++ if (csum) { ++ DRM_NOTE("DisplayID checksum invalid, remainder is %d\n", csum); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static u8 *find_displayid_extension(const struct edid *edid, ++ int *length, int *idx, ++ int *ext_index) ++{ ++ u8 *displayid = find_edid_extension(edid, 0x70, ext_index); ++ struct displayid_header *base; ++ int ret; ++ ++ if (!displayid) ++ return NULL; ++ ++ /* EDID extensions block checksum isn't for us */ ++ *length = EDID_LENGTH - 1; ++ *idx = 1; ++ ++ ret = validate_displayid(displayid, *length, *idx); ++ if (ret) ++ return NULL; ++ ++ base = (struct displayid_header *)&displayid[*idx]; ++ *length = *idx + sizeof(*base) + base->bytes; ++ ++ return displayid; ++} ++ ++static u8 *find_cea_extension(const struct edid *edid) ++{ ++ int length, idx; ++ const struct displayid_block *block; ++ u8 *cea; ++ u8 *displayid; ++ int ext_index; ++ ++ /* Look for a top level CEA extension block */ ++ /* FIXME: make callers iterate through multiple CEA ext blocks? */ ++ ext_index = 0; ++ cea = find_edid_extension(edid, 0x02, &ext_index); ++ if (cea) ++ return cea; ++ ++ /* CEA blocks can also be found embedded in a DisplayID block */ ++ ext_index = 0; ++ for (;;) { ++ struct displayid_iter iter; ++ const struct drm_edid *drm_edid; ++ int edid_size = (edid->extensions + 1) * EDID_LENGTH; ++ ++ drm_edid = drm_edid_alloc(edid, edid_size); ++ if (!drm_edid) ++ return NULL; ++ displayid = find_displayid_extension(edid, &length, &idx, ++ &ext_index); ++ if (!displayid) { ++ drm_edid_free(drm_edid); ++ return NULL; ++ } ++ ++ displayid_iter_edid_begin(drm_edid, &iter); ++ idx += sizeof(struct displayid_header); ++ displayid_iter_for_each(block, &iter) { ++ if (block->tag == 0x81) { ++ displayid_iter_end(&iter); ++ drm_edid_free(drm_edid); ++ return (u8 *)block; ++ } ++ } ++ ++ displayid_iter_end(&iter); ++ drm_edid_free(drm_edid); ++ } ++ ++ return NULL; ++} ++ ++#define EDID_CEA_YCRCB422 (1 << 4) ++ ++int rockchip_drm_get_yuv422_format(struct drm_connector *connector, ++ struct edid *edid) ++{ ++ struct drm_display_info *info; ++ const u8 *edid_ext; ++ ++ if (!connector || !edid) ++ return -EINVAL; ++ ++ info = &connector->display_info; ++ ++ edid_ext = find_cea_extension(edid); ++ if (!edid_ext) ++ return -EINVAL; ++ ++ if (edid_ext[3] & EDID_CEA_YCRCB422) ++ info->color_formats |= DRM_COLOR_FORMAT_YCBCR422; ++ ++ return 0; ++} ++EXPORT_SYMBOL(rockchip_drm_get_yuv422_format); ++ ++static ++void get_max_frl_rate(int max_frl_rate, u8 *max_lanes, u8 *max_rate_per_lane) ++{ ++ switch (max_frl_rate) { ++ case 1: ++ *max_lanes = 3; ++ *max_rate_per_lane = 3; ++ break; ++ case 2: ++ *max_lanes = 3; ++ *max_rate_per_lane = 6; ++ break; ++ case 3: ++ *max_lanes = 4; ++ *max_rate_per_lane = 6; ++ break; ++ case 4: ++ *max_lanes = 4; ++ *max_rate_per_lane = 8; ++ break; ++ case 5: ++ *max_lanes = 4; ++ *max_rate_per_lane = 10; ++ break; ++ case 6: ++ *max_lanes = 4; ++ *max_rate_per_lane = 12; ++ break; ++ case 0: ++ default: ++ *max_lanes = 0; ++ *max_rate_per_lane = 0; ++ } ++} ++ ++#define EDID_DSC_10BPC (1 << 0) ++#define EDID_DSC_12BPC (1 << 1) ++#define EDID_DSC_16BPC (1 << 2) ++#define EDID_DSC_ALL_BPP (1 << 3) ++#define EDID_DSC_NATIVE_420 (1 << 6) ++#define EDID_DSC_1P2 (1 << 7) ++#define EDID_DSC_MAX_FRL_RATE_MASK 0xf0 ++#define EDID_DSC_MAX_SLICES 0xf ++#define EDID_DSC_TOTAL_CHUNK_KBYTES 0x3f ++#define EDID_MAX_FRL_RATE_MASK 0xf0 ++ ++static ++void parse_edid_forum_vsdb(struct rockchip_drm_dsc_cap *dsc_cap, ++ u8 *max_frl_rate_per_lane, u8 *max_lanes, ++ const u8 *hf_vsdb) ++{ ++ u8 max_frl_rate; ++ u8 dsc_max_frl_rate; ++ u8 dsc_max_slices; ++ ++ if (!hf_vsdb[7]) ++ return; ++ ++ DRM_DEBUG_KMS("hdmi_21 sink detected. parsing edid\n"); ++ max_frl_rate = (hf_vsdb[7] & EDID_MAX_FRL_RATE_MASK) >> 4; ++ get_max_frl_rate(max_frl_rate, max_lanes, ++ max_frl_rate_per_lane); ++ ++ if (cea_db_payload_len(hf_vsdb) < 13) ++ return; ++ ++ dsc_cap->v_1p2 = hf_vsdb[11] & EDID_DSC_1P2; ++ ++ if (!dsc_cap->v_1p2) ++ return; ++ ++ dsc_cap->native_420 = hf_vsdb[11] & EDID_DSC_NATIVE_420; ++ dsc_cap->all_bpp = hf_vsdb[11] & EDID_DSC_ALL_BPP; ++ ++ if (hf_vsdb[11] & EDID_DSC_16BPC) ++ dsc_cap->bpc_supported = 16; ++ else if (hf_vsdb[11] & EDID_DSC_12BPC) ++ dsc_cap->bpc_supported = 12; ++ else if (hf_vsdb[11] & EDID_DSC_10BPC) ++ dsc_cap->bpc_supported = 10; ++ else ++ dsc_cap->bpc_supported = 0; ++ ++ dsc_max_frl_rate = (hf_vsdb[12] & EDID_DSC_MAX_FRL_RATE_MASK) >> 4; ++ get_max_frl_rate(dsc_max_frl_rate, &dsc_cap->max_lanes, ++ &dsc_cap->max_frl_rate_per_lane); ++ dsc_cap->total_chunk_kbytes = hf_vsdb[13] & EDID_DSC_TOTAL_CHUNK_KBYTES; ++ ++ dsc_max_slices = hf_vsdb[12] & EDID_DSC_MAX_SLICES; ++ switch (dsc_max_slices) { ++ case 1: ++ dsc_cap->max_slices = 1; ++ dsc_cap->clk_per_slice = 340; ++ break; ++ case 2: ++ dsc_cap->max_slices = 2; ++ dsc_cap->clk_per_slice = 340; ++ break; ++ case 3: ++ dsc_cap->max_slices = 4; ++ dsc_cap->clk_per_slice = 340; ++ break; ++ case 4: ++ dsc_cap->max_slices = 8; ++ dsc_cap->clk_per_slice = 340; ++ break; ++ case 5: ++ dsc_cap->max_slices = 8; ++ dsc_cap->clk_per_slice = 400; ++ break; ++ case 6: ++ dsc_cap->max_slices = 12; ++ dsc_cap->clk_per_slice = 400; ++ break; ++ case 7: ++ dsc_cap->max_slices = 16; ++ dsc_cap->clk_per_slice = 400; ++ break; ++ case 0: ++ default: ++ dsc_cap->max_slices = 0; ++ dsc_cap->clk_per_slice = 0; ++ } ++} ++ ++enum { ++ VER_26_BYTE_V0, ++ VER_15_BYTE_V1, ++ VER_12_BYTE_V1, ++ VER_12_BYTE_V2, ++}; ++ ++static int check_next_hdr_version(const u8 *next_hdr_db) ++{ ++ u16 ver; ++ ++ ver = (next_hdr_db[5] & 0xf0) << 8 | next_hdr_db[0]; ++ ++ switch (ver) { ++ case 0x00f9: ++ return VER_26_BYTE_V0; ++ case 0x20ee: ++ return VER_15_BYTE_V1; ++ case 0x20eb: ++ return VER_12_BYTE_V1; ++ case 0x40eb: ++ return VER_12_BYTE_V2; ++ default: ++ return -ENOENT; ++ } ++} ++ ++static void parse_ver_26_v0_data(struct ver_26_v0 *hdr, const u8 *data) ++{ ++ hdr->yuv422_12bit = data[5] & BIT(0); ++ hdr->support_2160p_60 = (data[5] & BIT(1)) >> 1; ++ hdr->global_dimming = (data[5] & BIT(2)) >> 2; ++ ++ hdr->dm_major_ver = (data[21] & 0xf0) >> 4; ++ hdr->dm_minor_ver = data[21] & 0xf; ++ ++ hdr->t_min_pq = (data[19] << 4) | ((data[18] & 0xf0) >> 4); ++ hdr->t_max_pq = (data[20] << 4) | (data[18] & 0xf); ++ ++ hdr->rx = (data[7] << 4) | ((data[6] & 0xf0) >> 4); ++ hdr->ry = (data[8] << 4) | (data[6] & 0xf); ++ hdr->gx = (data[10] << 4) | ((data[9] & 0xf0) >> 4); ++ hdr->gy = (data[11] << 4) | (data[9] & 0xf); ++ hdr->bx = (data[13] << 4) | ((data[12] & 0xf0) >> 4); ++ hdr->by = (data[14] << 4) | (data[12] & 0xf); ++ hdr->wx = (data[16] << 4) | ((data[15] & 0xf0) >> 4); ++ hdr->wy = (data[17] << 4) | (data[15] & 0xf); ++} ++ ++static void parse_ver_15_v1_data(struct ver_15_v1 *hdr, const u8 *data) ++{ ++ hdr->yuv422_12bit = data[5] & BIT(0); ++ hdr->support_2160p_60 = (data[5] & BIT(1)) >> 1; ++ hdr->global_dimming = data[6] & BIT(0); ++ ++ hdr->dm_version = (data[5] & 0x1c) >> 2; ++ ++ hdr->colorimetry = data[7] & BIT(0); ++ ++ hdr->t_max_lum = (data[6] & 0xfe) >> 1; ++ hdr->t_min_lum = (data[7] & 0xfe) >> 1; ++ ++ hdr->rx = data[9]; ++ hdr->ry = data[10]; ++ hdr->gx = data[11]; ++ hdr->gy = data[12]; ++ hdr->bx = data[13]; ++ hdr->by = data[14]; ++} ++ ++static void parse_ver_12_v1_data(struct ver_12_v1 *hdr, const u8 *data) ++{ ++ hdr->yuv422_12bit = data[5] & BIT(0); ++ hdr->support_2160p_60 = (data[5] & BIT(1)) >> 1; ++ hdr->global_dimming = data[6] & BIT(0); ++ ++ hdr->dm_version = (data[5] & 0x1c) >> 2; ++ ++ hdr->colorimetry = data[7] & BIT(0); ++ ++ hdr->t_max_lum = (data[6] & 0xfe) >> 1; ++ hdr->t_min_lum = (data[7] & 0xfe) >> 1; ++ ++ hdr->low_latency = data[8] & 0x3; ++ ++ hdr->unique_rx = (data[11] & 0xf8) >> 3; ++ hdr->unique_ry = (data[11] & 0x7) << 2 | (data[10] & BIT(0)) << 1 | ++ (data[9] & BIT(0)); ++ hdr->unique_gx = (data[9] & 0xfe) >> 1; ++ hdr->unique_gy = (data[10] & 0xfe) >> 1; ++ hdr->unique_bx = (data[8] & 0xe0) >> 5; ++ hdr->unique_by = (data[8] & 0x1c) >> 2; ++} ++ ++static void parse_ver_12_v2_data(struct ver_12_v2 *hdr, const u8 *data) ++{ ++ hdr->yuv422_12bit = data[5] & BIT(0); ++ hdr->backlt_ctrl = (data[5] & BIT(1)) >> 1; ++ hdr->global_dimming = (data[6] & BIT(2)) >> 2; ++ ++ hdr->dm_version = (data[5] & 0x1c) >> 2; ++ hdr->backlt_min_luma = data[6] & 0x3; ++ hdr->interface = data[7] & 0x3; ++ hdr->yuv444_10b_12b = (data[8] & BIT(0)) << 1 | (data[9] & BIT(0)); ++ ++ hdr->t_min_pq_v2 = (data[6] & 0xf8) >> 3; ++ hdr->t_max_pq_v2 = (data[7] & 0xf8) >> 3; ++ ++ hdr->unique_rx = (data[10] & 0xf8) >> 3; ++ hdr->unique_ry = (data[11] & 0xf8) >> 3; ++ hdr->unique_gx = (data[8] & 0xfe) >> 1; ++ hdr->unique_gy = (data[9] & 0xfe) >> 1; ++ hdr->unique_bx = data[10] & 0x7; ++ hdr->unique_by = data[11] & 0x7; ++} ++ ++static ++void parse_next_hdr_block(struct next_hdr_sink_data *sink_data, ++ const u8 *next_hdr_db) ++{ ++ int version; ++ ++ version = check_next_hdr_version(next_hdr_db); ++ if (version < 0) ++ return; ++ ++ sink_data->version = version; ++ ++ switch (version) { ++ case VER_26_BYTE_V0: ++ parse_ver_26_v0_data(&sink_data->ver_26_v0, next_hdr_db); ++ break; ++ case VER_15_BYTE_V1: ++ parse_ver_15_v1_data(&sink_data->ver_15_v1, next_hdr_db); ++ break; ++ case VER_12_BYTE_V1: ++ parse_ver_12_v1_data(&sink_data->ver_12_v1, next_hdr_db); ++ break; ++ case VER_12_BYTE_V2: ++ parse_ver_12_v2_data(&sink_data->ver_12_v2, next_hdr_db); ++ break; ++ default: ++ break; ++ } ++} ++ ++int rockchip_drm_parse_cea_ext(struct rockchip_drm_dsc_cap *dsc_cap, ++ u8 *max_frl_rate_per_lane, u8 *max_lanes, ++ const struct edid *edid) ++{ ++ const u8 *edid_ext; ++ int i, start, end; ++ ++ if (!dsc_cap || !max_frl_rate_per_lane || !max_lanes || !edid) ++ return -EINVAL; ++ ++ edid_ext = find_cea_extension(edid); ++ if (!edid_ext) ++ return -EINVAL; ++ ++ if (cea_db_offsets(edid_ext, &start, &end)) ++ return -EINVAL; ++ ++ for_each_cea_db(edid_ext, i, start, end) { ++ const u8 *db = &edid_ext[i]; ++ ++ if (cea_db_is_hdmi_forum_vsdb(db)) ++ parse_edid_forum_vsdb(dsc_cap, max_frl_rate_per_lane, ++ max_lanes, db); ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(rockchip_drm_parse_cea_ext); ++ ++int rockchip_drm_parse_next_hdr(struct next_hdr_sink_data *sink_data, ++ const struct edid *edid) ++{ ++ const u8 *edid_ext; ++ int i, start, end; ++ ++ if (!sink_data || !edid) ++ return -EINVAL; ++ ++ memset(sink_data, 0, sizeof(struct next_hdr_sink_data)); ++ ++ edid_ext = find_cea_extension(edid); ++ if (!edid_ext) ++ return -EINVAL; ++ ++ if (cea_db_offsets(edid_ext, &start, &end)) ++ return -EINVAL; ++ ++ for_each_cea_db(edid_ext, i, start, end) { ++ const u8 *db = &edid_ext[i]; ++ ++ if (cea_db_is_hdmi_next_hdr_block(db)) ++ parse_next_hdr_block(sink_data, db); ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(rockchip_drm_parse_next_hdr); ++ + /* + * Attach a (component) device to the shared drm dma mapping from master drm + * device. This is used by the VOPs to map GEM buffers to a common DMA +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +index aeb03a57240f..581496f043b1 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +@@ -10,8 +10,11 @@ + #define _ROCKCHIP_DRM_DRV_H + + #include ++#include ++#include ++#include + #include +- ++#include + #include + #include + #include +@@ -19,25 +22,367 @@ + #define ROCKCHIP_MAX_FB_BUFFER 3 + #define ROCKCHIP_MAX_CONNECTOR 2 + #define ROCKCHIP_MAX_CRTC 4 ++#define ROCKCHIP_MAX_LAYER 16 + + struct drm_device; + struct drm_connector; + struct iommu_domain; + ++#define VOP_COLOR_KEY_NONE (0 << 31) ++#define VOP_COLOR_KEY_MASK (1 << 31) ++ ++#define VOP_OUTPUT_IF_RGB BIT(0) ++#define VOP_OUTPUT_IF_BT1120 BIT(1) ++#define VOP_OUTPUT_IF_BT656 BIT(2) ++#define VOP_OUTPUT_IF_LVDS0 BIT(3) ++#define VOP_OUTPUT_IF_LVDS1 BIT(4) ++#define VOP_OUTPUT_IF_MIPI0 BIT(5) ++#define VOP_OUTPUT_IF_MIPI1 BIT(6) ++#define VOP_OUTPUT_IF_eDP0 BIT(7) ++#define VOP_OUTPUT_IF_eDP1 BIT(8) ++#define VOP_OUTPUT_IF_DP0 BIT(9) ++#define VOP_OUTPUT_IF_DP1 BIT(10) ++#define VOP_OUTPUT_IF_HDMI0 BIT(11) ++#define VOP_OUTPUT_IF_HDMI1 BIT(12) ++ ++#ifndef DRM_FORMAT_NV20 ++#define DRM_FORMAT_NV20 fourcc_code('N', 'V', '2', '0') /* 2x1 subsampled Cr:Cb plane */ ++#endif ++ ++#ifndef DRM_FORMAT_NV30 ++#define DRM_FORMAT_NV30 fourcc_code('N', 'V', '3', '0') /* non-subsampled Cr:Cb plane */ ++#endif ++ ++#define RK_IF_PROP_COLOR_DEPTH "color_depth" ++#define RK_IF_PROP_COLOR_FORMAT "color_format" ++#define RK_IF_PROP_COLOR_DEPTH_CAPS "color_depth_caps" ++#define RK_IF_PROP_COLOR_FORMAT_CAPS "color_format_caps" ++ ++enum rk_if_color_depth { ++ RK_IF_DEPTH_8, ++ RK_IF_DEPTH_10, ++ RK_IF_DEPTH_12, ++ RK_IF_DEPTH_16, ++ RK_IF_DEPTH_420_10, ++ RK_IF_DEPTH_420_12, ++ RK_IF_DEPTH_420_16, ++ RK_IF_DEPTH_6, ++ RK_IF_DEPTH_MAX, ++}; ++ ++enum rk_if_color_format { ++ RK_IF_FORMAT_RGB, /* default RGB */ ++ RK_IF_FORMAT_YCBCR444, /* YCBCR 444 */ ++ RK_IF_FORMAT_YCBCR422, /* YCBCR 422 */ ++ RK_IF_FORMAT_YCBCR420, /* YCBCR 420 */ ++ RK_IF_FORMAT_YCBCR_HQ, /* Highest subsampled YUV */ ++ RK_IF_FORMAT_YCBCR_LQ, /* Lowest subsampled YUV */ ++ RK_IF_FORMAT_MAX, ++}; ++ ++struct rockchip_drm_sub_dev { ++ struct list_head list; ++ struct drm_connector *connector; ++ struct device_node *of_node; ++ void (*loader_protect)(struct drm_encoder *encoder, bool on); ++ void (*oob_hotplug_event)(struct drm_connector *connector); ++}; ++ ++struct rockchip_sdr2hdr_state { ++ int sdr2hdr_func; ++ ++ bool bt1886eotf_pre_conv_en; ++ bool rgb2rgb_pre_conv_en; ++ bool rgb2rgb_pre_conv_mode; ++ bool st2084oetf_pre_conv_en; ++ ++ bool bt1886eotf_post_conv_en; ++ bool rgb2rgb_post_conv_en; ++ bool rgb2rgb_post_conv_mode; ++ bool st2084oetf_post_conv_en; ++}; ++ ++struct rockchip_hdr_state { ++ bool pre_overlay; ++ bool hdr2sdr_en; ++ struct rockchip_sdr2hdr_state sdr2hdr_state; ++}; ++ ++struct rockchip_bcsh_state { ++ int brightness; ++ int contrast; ++ int saturation; ++ int sin_hue; ++ int cos_hue; ++}; ++ ++struct rockchip_crtc { ++ struct drm_crtc crtc; ++#if defined(CONFIG_ROCKCHIP_DRM_DEBUG) ++ /** ++ * @vop_dump_status the status of vop dump control ++ * @vop_dump_list_head the list head of vop dump list ++ * @vop_dump_list_init_flag init once ++ * @vop_dump_times control the dump times ++ * @frme_count the frame of dump buf ++ */ ++ enum vop_dump_status vop_dump_status; ++ struct list_head vop_dump_list_head; ++ bool vop_dump_list_init_flag; ++ int vop_dump_times; ++ int frame_count; ++#endif ++}; ++ ++struct rockchip_dsc_sink_cap { ++ /** ++ * @slice_width: the number of pixel columns that comprise the slice width ++ * @slice_height: the number of pixel rows that comprise the slice height ++ * @block_pred: Does block prediction ++ * @native_420: Does sink support DSC with 4:2:0 compression ++ * @bpc_supported: compressed bpc supported by sink : 10, 12 or 16 bpc ++ * @version_major: DSC major version ++ * @version_minor: DSC minor version ++ * @target_bits_per_pixel_x16: bits num after compress and multiply 16 ++ */ ++ u16 slice_width; ++ u16 slice_height; ++ bool block_pred; ++ bool native_420; ++ u8 bpc_supported; ++ u8 version_major; ++ u8 version_minor; ++ u16 target_bits_per_pixel_x16; ++}; ++ + struct rockchip_crtc_state { + struct drm_crtc_state base; ++ int vp_id; + int output_type; + int output_mode; + int output_bpc; + int output_flags; + bool enable_afbc; ++ ++ //[CC:] vop2 related change ++ /** ++ * @splice_mode: enabled when display a hdisplay > 4096 on rk3588 ++ */ ++ bool splice_mode; ++ ++ /** ++ * @hold_mode: enabled when it's: ++ * (1) mcu hold mode ++ * (2) mipi dsi cmd mode ++ * (3) edp psr mode ++ */ ++ bool hold_mode; ++ ++ struct drm_tv_connector_state *tv_state; ++ int left_margin; ++ int right_margin; ++ int top_margin; ++ int bottom_margin; ++ int vdisplay; ++ int afbdc_win_format; ++ int afbdc_win_width; ++ int afbdc_win_height; ++ int afbdc_win_ptr; ++ int afbdc_win_id; ++ int afbdc_en; ++ int afbdc_win_vir_width; ++ int afbdc_win_xoffset; ++ int afbdc_win_yoffset; ++ int dsp_layer_sel; ++ u32 output_if; + u32 bus_format; + u32 bus_flags; ++ int yuv_overlay; ++ int post_r2y_en; ++ int post_y2r_en; ++ int post_csc_mode; ++ int bcsh_en; + int color_space; ++ int eotf; ++ u32 background; ++ u32 line_flag; ++ u8 mode_update; ++ u8 dsc_id; ++ ++ //[CC:] vop2 related changes ++ u8 dsc_enable; ++ unsigned long dsc_clk; ++ ++ u8 dsc_slice_num; ++ u8 dsc_pixel_num; ++ ++ u64 dsc_txp_clk_rate; ++ u64 dsc_pxl_clk_rate; ++ u64 dsc_cds_clk_rate; ++ ++ struct drm_dsc_picture_parameter_set pps; ++ struct rockchip_dsc_sink_cap dsc_sink_cap; ++ struct rockchip_hdr_state hdr; + }; ++ + #define to_rockchip_crtc_state(s) \ + container_of(s, struct rockchip_crtc_state, base) + ++struct rockchip_drm_vcnt { ++ struct drm_pending_vblank_event *event; ++ __u32 sequence; ++ int pipe; ++}; ++ ++struct rockchip_logo { ++ dma_addr_t dma_addr; ++ struct drm_mm_node logo_reserved_node; ++ void *kvaddr; ++ phys_addr_t start; ++ phys_addr_t size; ++ int count; ++}; ++ ++struct loader_cubic_lut { ++ bool enable; ++ u32 offset; ++}; ++ ++struct rockchip_drm_dsc_cap { ++ bool v_1p2; ++ bool native_420; ++ bool all_bpp; ++ u8 bpc_supported; ++ u8 max_slices; ++ u8 max_lanes; ++ u8 max_frl_rate_per_lane; ++ u8 total_chunk_kbytes; ++ int clk_per_slice; ++}; ++ ++struct ver_26_v0 { ++ u8 yuv422_12bit; ++ u8 support_2160p_60; ++ u8 global_dimming; ++ u8 dm_major_ver; ++ u8 dm_minor_ver; ++ u16 t_min_pq; ++ u16 t_max_pq; ++ u16 rx; ++ u16 ry; ++ u16 gx; ++ u16 gy; ++ u16 bx; ++ u16 by; ++ u16 wx; ++ u16 wy; ++} __packed; ++ ++struct ver_15_v1 { ++ u8 yuv422_12bit; ++ u8 support_2160p_60; ++ u8 global_dimming; ++ u8 dm_version; ++ u8 colorimetry; ++ u8 t_max_lum; ++ u8 t_min_lum; ++ u8 rx; ++ u8 ry; ++ u8 gx; ++ u8 gy; ++ u8 bx; ++ u8 by; ++} __packed; ++ ++struct ver_12_v1 { ++ u8 yuv422_12bit; ++ u8 support_2160p_60; ++ u8 global_dimming; ++ u8 dm_version; ++ u8 colorimetry; ++ u8 low_latency; ++ u8 t_max_lum; ++ u8 t_min_lum; ++ u8 unique_rx; ++ u8 unique_ry; ++ u8 unique_gx; ++ u8 unique_gy; ++ u8 unique_bx; ++ u8 unique_by; ++} __packed; ++ ++struct ver_12_v2 { ++ u8 yuv422_12bit; ++ u8 backlt_ctrl; ++ u8 global_dimming; ++ u8 dm_version; ++ u8 backlt_min_luma; ++ u8 interface; ++ u8 yuv444_10b_12b; ++ u8 t_min_pq_v2; ++ u8 t_max_pq_v2; ++ u8 unique_rx; ++ u8 unique_ry; ++ u8 unique_gx; ++ u8 unique_gy; ++ u8 unique_bx; ++ u8 unique_by; ++} __packed; ++ ++struct next_hdr_sink_data { ++ u8 version; ++ struct ver_26_v0 ver_26_v0; ++ struct ver_15_v1 ver_15_v1; ++ struct ver_12_v1 ver_12_v1; ++ struct ver_12_v2 ver_12_v2; ++} __packed; ++ ++//[CC:] drop struct dmcfreq_vop_info ++struct dmcfreq_vop_info; ++ ++/* ++ * Rockchip drm private crtc funcs. ++ * @loader_protect: protect loader logo crtc's power ++ * @enable_vblank: enable crtc vblank irq. ++ * @disable_vblank: disable crtc vblank irq. ++ * @bandwidth: report present crtc bandwidth consume. ++ * @cancel_pending_vblank: cancel pending vblank. ++ * @debugfs_init: init crtc debugfs. ++ * @debugfs_dump: debugfs to dump crtc and plane state. ++ * @regs_dump: dump vop current register config. ++ * @mode_valid: verify that the current mode is supported. ++ * @crtc_close: close vop. ++ * @crtc_send_mcu_cmd: send mcu panel init cmd. ++ * @te_handler: soft te hand for cmd mode panel. ++ * @wait_vact_end: wait the last active line. ++ */ ++struct rockchip_crtc_funcs { ++ int (*loader_protect)(struct drm_crtc *crtc, bool on); ++ int (*enable_vblank)(struct drm_crtc *crtc); ++ void (*disable_vblank)(struct drm_crtc *crtc); ++ size_t (*bandwidth)(struct drm_crtc *crtc, ++ struct drm_crtc_state *crtc_state, ++ struct dmcfreq_vop_info *vop_bw_info); ++ void (*cancel_pending_vblank)(struct drm_crtc *crtc, ++ struct drm_file *file_priv); ++ int (*debugfs_init)(struct drm_minor *minor, struct drm_crtc *crtc); ++ int (*debugfs_dump)(struct drm_crtc *crtc, struct seq_file *s); ++ void (*regs_dump)(struct drm_crtc *crtc, struct seq_file *s); ++ enum drm_mode_status (*mode_valid)(struct drm_crtc *crtc, ++ const struct drm_display_mode *mode, ++ int output_type); ++ void (*crtc_close)(struct drm_crtc *crtc); ++ void (*crtc_send_mcu_cmd)(struct drm_crtc *crtc, u32 type, u32 value); ++ void (*te_handler)(struct drm_crtc *crtc); ++ int (*wait_vact_end)(struct drm_crtc *crtc, unsigned int mstimeout); ++ void (*crtc_standby)(struct drm_crtc *crtc, bool standby); ++}; ++ ++struct rockchip_dclk_pll { ++ struct clk *pll; ++ unsigned int use_count; ++}; ++ + /* + * Rockchip drm private structure. + * +@@ -46,10 +391,56 @@ struct rockchip_crtc_state { + * @mm_lock: protect drm_mm on multi-threads. + */ + struct rockchip_drm_private { ++ struct rockchip_logo *logo; ++ struct drm_fb_helper *fbdev_helper; ++ struct drm_gem_object *fbdev_bo; + struct iommu_domain *domain; ++ struct gen_pool *secure_buffer_pool; + struct device *iommu_dev; + struct mutex mm_lock; + struct drm_mm mm; ++ struct list_head psr_list; ++ struct mutex psr_list_lock; ++ struct mutex commit_lock; ++ ++ /* private crtc prop */ ++ struct drm_property *soc_id_prop; ++ struct drm_property *port_id_prop; ++ struct drm_property *aclk_prop; ++ struct drm_property *bg_prop; ++ struct drm_property *line_flag_prop; ++ ++ /* private plane prop */ ++ struct drm_property *eotf_prop; ++ struct drm_property *color_space_prop; ++ struct drm_property *async_commit_prop; ++ struct drm_property *share_id_prop; ++ ++ /* private connector prop */ ++ struct drm_property *connector_id_prop; ++ ++ const struct rockchip_crtc_funcs *crtc_funcs[ROCKCHIP_MAX_CRTC]; ++ ++ struct rockchip_dclk_pll default_pll; ++ struct rockchip_dclk_pll hdmi_pll; ++ ++ /* ++ * protect some shared overlay resource ++ * OVL_LAYER_SEL/OVL_PORT_SEL ++ */ ++ struct mutex ovl_lock; ++ ++ struct rockchip_drm_vcnt vcnt[ROCKCHIP_MAX_CRTC]; ++ /** ++ * @loader_protect ++ * ignore restore_fbdev_mode_atomic when in logo on state ++ */ ++ bool loader_protect; ++ ++ dma_addr_t cubic_lut_dma_addr; ++ void *cubic_lut_kvaddr; ++ struct drm_mm_node *clut_reserved_node; ++ struct loader_cubic_lut cubic_lut[ROCKCHIP_MAX_CRTC]; + }; + + struct rockchip_encoder { +@@ -66,16 +457,52 @@ void rockchip_drm_dma_init_device(struct drm_device *drm_dev, + int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout); + int rockchip_drm_encoder_set_crtc_endpoint_id(struct rockchip_encoder *rencoder, + struct device_node *np, int port, int reg); ++ ++int rockchip_register_crtc_funcs(struct drm_crtc *crtc, ++ const struct rockchip_crtc_funcs *crtc_funcs); ++void rockchip_unregister_crtc_funcs(struct drm_crtc *crtc); ++void rockchip_drm_crtc_standby(struct drm_crtc *crtc, bool standby); ++ ++void rockchip_drm_register_sub_dev(struct rockchip_drm_sub_dev *sub_dev); ++void rockchip_drm_unregister_sub_dev(struct rockchip_drm_sub_dev *sub_dev); ++struct rockchip_drm_sub_dev *rockchip_drm_get_sub_dev(struct device_node *node); ++int rockchip_drm_add_modes_noedid(struct drm_connector *connector); ++void rockchip_drm_te_handle(struct drm_crtc *crtc); ++void drm_mode_convert_to_split_mode(struct drm_display_mode *mode); ++void drm_mode_convert_to_origin_mode(struct drm_display_mode *mode); ++#if IS_REACHABLE(CONFIG_DRM_ROCKCHIP) ++int rockchip_drm_get_sub_dev_type(void); ++#else ++static inline int rockchip_drm_get_sub_dev_type(void) ++{ ++ return DRM_MODE_CONNECTOR_Unknown; ++} ++#endif ++ + int rockchip_drm_endpoint_is_subdriver(struct device_node *ep); ++uint32_t rockchip_drm_get_bpp(const struct drm_format_info *info); ++int rockchip_drm_get_yuv422_format(struct drm_connector *connector, ++ struct edid *edid); ++int rockchip_drm_parse_cea_ext(struct rockchip_drm_dsc_cap *dsc_cap, ++ u8 *max_frl_rate_per_lane, u8 *max_lanes, ++ const struct edid *edid); ++int rockchip_drm_parse_next_hdr(struct next_hdr_sink_data *sink_data, ++ const struct edid *edid); ++ + extern struct platform_driver cdn_dp_driver; + extern struct platform_driver dw_hdmi_rockchip_pltfm_driver; + extern struct platform_driver dw_mipi_dsi_rockchip_driver; ++extern struct platform_driver dw_mipi_dsi2_rockchip_driver; + extern struct platform_driver inno_hdmi_driver; + extern struct platform_driver rockchip_dp_driver; + extern struct platform_driver rockchip_lvds_driver; + extern struct platform_driver vop_platform_driver; +-extern struct platform_driver rk3066_hdmi_driver; + extern struct platform_driver vop2_platform_driver; ++extern struct platform_driver rk3066_hdmi_driver; ++extern struct platform_driver rockchip_rgb_driver; ++extern struct platform_driver dw_dp_driver; ++extern struct platform_driver vconn_platform_driver; ++extern struct platform_driver vvop_platform_driver; + + static inline struct rockchip_encoder *to_rockchip_encoder(struct drm_encoder *encoder) + { +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +index 4b2daefeb8c1..6ebea5c36560 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +@@ -15,6 +15,16 @@ + #define VOP_MAJOR(version) ((version) >> 8) + #define VOP_MINOR(version) ((version) & 0xff) + ++//[CC:] vop2 related changes ++#define VOP_VERSION_RK3568 VOP_VERSION(0x40, 0x15) ++#define VOP_VERSION_RK3588 VOP_VERSION(0x40, 0x17) ++ ++#define ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE BIT(0) ++#define ROCKCHIP_OUTPUT_DUAL_CHANNEL_ODD_EVEN_MODE BIT(1) ++#define ROCKCHIP_OUTPUT_DATA_SWAP BIT(2) ++/* MIPI DSI DataStream(cmd) mode on rk3588 */ ++#define ROCKCHIP_OUTPUT_MIPI_DS_MODE BIT(3) ++ + #define NUM_YUV2YUV_COEFFICIENTS 12 + + /* AFBC supports a number of configurable modes. Relevant to us is block size +@@ -280,11 +290,16 @@ struct vop_data { + /* + * display output interface supported by rockchip lcdc + */ +-#define ROCKCHIP_OUT_MODE_P888 0 +-#define ROCKCHIP_OUT_MODE_P666 1 +-#define ROCKCHIP_OUT_MODE_P565 2 ++#define ROCKCHIP_OUT_MODE_P888 0 ++#define ROCKCHIP_OUT_MODE_BT1120 0 ++#define ROCKCHIP_OUT_MODE_P666 1 ++#define ROCKCHIP_OUT_MODE_P565 2 ++#define ROCKCHIP_OUT_MODE_BT656 5 ++#define ROCKCHIP_OUT_MODE_S888 8 ++#define ROCKCHIP_OUT_MODE_S888_DUMMY 12 ++#define ROCKCHIP_OUT_MODE_YUV420 14 + /* for use special outface */ +-#define ROCKCHIP_OUT_MODE_AAAA 15 ++#define ROCKCHIP_OUT_MODE_AAAA 15 + + /* output flags */ + #define ROCKCHIP_OUTPUT_DSI_DUAL BIT(0) +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +index 6862fb146ace..509caa514dca 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + #include + + #include +@@ -159,6 +160,8 @@ struct vop2_video_port { + struct drm_crtc crtc; + struct vop2 *vop2; + struct clk *dclk; ++ struct reset_control *dclk_rst; ++ struct clk *dclk_parent; + unsigned int id; + const struct vop2_video_port_data *data; + +@@ -191,6 +194,8 @@ struct vop2 { + struct regmap *map; + + struct regmap *grf; ++ struct regmap *vop_grf; ++ struct regmap *vo1_grf; + + /* physical map length of vop2 register */ + u32 len; +@@ -209,6 +214,9 @@ struct vop2 { + unsigned int enable_count; + struct clk *hclk; + struct clk *aclk; ++ struct clk *pclk; ++ struct reset_control *ahb_rst; ++ struct reset_control *axi_rst; + + /* optional internal rgb encoder */ + struct rockchip_rgb *rgb; +@@ -217,6 +225,13 @@ struct vop2 { + struct vop2_win win[]; + }; + ++#define vop2_output_if_is_hdmi(x) (x == ROCKCHIP_VOP2_EP_HDMI0 || x == ROCKCHIP_VOP2_EP_HDMI1) ++#define vop2_output_if_is_dp(x) (x == ROCKCHIP_VOP2_EP_DP0 || x == ROCKCHIP_VOP2_EP_DP1) ++#define vop2_output_if_is_edp(x) (x == ROCKCHIP_VOP2_EP_EDP0 || x == ROCKCHIP_VOP2_EP_EDP1) ++#define vop2_output_if_is_mipi(x) (x == ROCKCHIP_VOP2_EP_MIPI0 || x == ROCKCHIP_VOP2_EP_MIPI1) ++#define vop2_output_if_is_lvds(x) (x == ROCKCHIP_VOP2_EP_LVDS0 || x == ROCKCHIP_VOP2_EP_LVDS1) ++#define vop2_output_if_is_dpi(x) (x == ROCKCHIP_VOP2_EP_RGB0) ++ + static struct vop2_video_port *to_vop2_video_port(struct drm_crtc *crtc) + { + return container_of(crtc, struct vop2_video_port, crtc); +@@ -269,9 +284,16 @@ static bool vop2_cluster_window(const struct vop2_win *win) + static void vop2_cfg_done(struct vop2_video_port *vp) + { + struct vop2 *vop2 = vp->vop2; ++ unsigned int bits = BIT(vp->id) | RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN; ++ ++ if (vop2->data->soc_id == 3588) { ++ bits |= BIT(vp->id) << 16; ++ // [CC:] handle splice_mode ++ // if (vcstate->splice_mode) ++ // bits |= BIT(vp_data->splice_vp_id) | (BIT(vp_data->splice_vp_id) << 16); ++ } + +- regmap_set_bits(vop2->map, RK3568_REG_CFG_DONE, +- BIT(vp->id) | RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN); ++ regmap_set_bits(vop2->map, RK3568_REG_CFG_DONE, bits); + } + + static void vop2_win_disable(struct vop2_win *win) +@@ -846,16 +868,36 @@ static int vop2_core_clks_prepare_enable(struct vop2 *vop2) + ret = clk_prepare_enable(vop2->aclk); + if (ret < 0) { + drm_err(vop2->drm, "failed to enable aclk - %d\n", ret); +- goto err; ++ goto err_aclk; ++ } ++ ++ ret = clk_prepare_enable(vop2->pclk); ++ if (ret < 0) { ++ drm_err(vop2->drm, "failed to enable pclk - %d\n", ret); ++ goto err_pclk; + } + + return 0; +-err: ++ ++err_pclk: ++ clk_disable_unprepare(vop2->aclk); ++err_aclk: + clk_disable_unprepare(vop2->hclk); + + return ret; + } + ++static void vop2_power_domain_all_on(struct vop2 *vop2) ++{ ++ u32 pd; ++ ++ pd = vop2_readl(vop2, RK3588_SYS_PD_CTRL); ++ pd |= VOP2_PD_CLUSTER0 | VOP2_PD_CLUSTER1 | VOP2_PD_CLUSTER2 | ++ VOP2_PD_CLUSTER3 | VOP2_PD_ESMART; ++ ++ vop2_writel(vop2, RK3588_SYS_PD_CTRL, pd); ++} ++ + static void vop2_enable(struct vop2 *vop2) + { + int ret; +@@ -883,6 +925,9 @@ static void vop2_enable(struct vop2 *vop2) + if (vop2->data->soc_id == 3566) + vop2_writel(vop2, RK3568_OTP_WIN_EN, 1); + ++ if (vop2->data->soc_id == 3588) ++ vop2_power_domain_all_on(vop2); ++ + vop2_writel(vop2, RK3568_REG_CFG_DONE, RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN); + + /* +@@ -910,6 +955,7 @@ static void vop2_disable(struct vop2 *vop2) + + regcache_mark_dirty(vop2->map); + ++ clk_disable_unprepare(vop2->pclk); + clk_disable_unprepare(vop2->aclk); + clk_disable_unprepare(vop2->hclk); + } +@@ -1004,6 +1050,7 @@ static int vop2_plane_atomic_check(struct drm_plane *plane, + if (!pstate->visible) + return 0; + ++ // [CC:] Drop format var since it's not used anywhere, use ret var instead + format = vop2_convert_format(fb->format->format); + if (format < 0) + return format; +@@ -1027,6 +1074,35 @@ static int vop2_plane_atomic_check(struct drm_plane *plane, + return -EINVAL; + } + ++ // [CC:] do we need the checks below? ++ bool afbc_en = rockchip_afbc(plane, fb->modifier); ++ struct vop2_win *win = to_vop2_win(plane); ++ ++ /* ++ * This is special feature at rk356x, the cluster layer only can support ++ * afbc format and can't support linear format; ++ */ ++ if (vp->vop2->data->soc_id == 3568) { ++ if (vop2_cluster_window(win) && !afbc_en) { ++ DRM_ERROR("Unsupported linear format at %s\n", win->data->name); ++ return -EINVAL; ++ } ++ } ++ ++ if (vp->vop2->data->soc_id > 3568) { ++ if (vop2_cluster_window(win) && !afbc_en && fb->format->is_yuv) { ++ DRM_ERROR("Unsupported linear yuv format at %s\n", win->data->name); ++ return -EINVAL; ++ } ++ ++ if (vop2_cluster_window(win) && !afbc_en && ++ (win->data->supported_rotations & pstate->rotation)) { ++ DRM_ERROR("Unsupported linear rotation(%d) format at %s\n", ++ pstate->rotation, win->data->name); ++ return -EINVAL; ++ } ++ } ++ + /* + * Src.x1 can be odd when do clip, but yuv plane start point + * need align with 2 pixel. +@@ -1107,6 +1183,7 @@ static void vop2_plane_setup_color_key(struct drm_plane *plane, u32 color_key) + vop2_win_write(win, VOP2_WIN_COLOR_KEY, (r << 20) | (g << 10) | b); + } + ++// [CC:] fix BUG: sleeping function called from invalid context + static void vop2_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) + { +@@ -1272,7 +1349,12 @@ static void vop2_plane_atomic_update(struct drm_plane *plane, + vop2_win_write(win, VOP2_WIN_AFBC_ENABLE, 1); + vop2_win_write(win, VOP2_WIN_AFBC_FORMAT, afbc_format); + vop2_win_write(win, VOP2_WIN_AFBC_UV_SWAP, uv_swap); +- vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0); ++ ++ if (vop2->data->soc_id == 3566 || vop2->data->soc_id == 3568) ++ vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0); ++ else ++ vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 1); ++ + vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0); + if (pstate->rotation & (DRM_MODE_ROTATE_270 | DRM_MODE_ROTATE_90)) { + vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 0); +@@ -1462,7 +1544,7 @@ static void vop2_post_config(struct drm_crtc *crtc) + } + + static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id, +- u32 polflags) ++ u32 polflags, u32 invpolflags) + { + struct vop2 *vop2 = vp->vop2; + u32 die, dip; +@@ -1477,6 +1559,7 @@ static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id, + FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_RGB_MUX, vp->id); + dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL; + dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags); ++ // [CC:] use .grf_dclk_inv = VOP_REG(RK3588_GRF_SOC_CON1, 0x1, 14) for 3588 variant + if (polflags & POLFLAG_DCLK_INV) + regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16) | BIT(3)); + else +@@ -1487,8 +1570,18 @@ static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id, + die |= RK3568_SYS_DSP_INFACE_EN_HDMI | + FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_HDMI_MUX, vp->id); + dip &= ~RK3568_DSP_IF_POL__HDMI_PIN_POL; +- dip |= FIELD_PREP(RK3568_DSP_IF_POL__HDMI_PIN_POL, polflags); ++ dip |= FIELD_PREP(RK3568_DSP_IF_POL__HDMI_PIN_POL, invpolflags); ++ ++ /* grf_hdmi0_en */ ++ if (vop2->vop_grf) ++ regmap_write(vop2->vop_grf, RK3588_GRF_VOP_CON2, BIT(1 + 16) | BIT(1)); ++ /* hdmi0_pin_pol */ ++ if (vop2->vo1_grf) ++ regmap_write(vop2->vo1_grf, RK3588_GRF_VO1_CON0, ++ RK3588_GRF_VO1_CON0__HDMI0_PIN_POL << 16 | ++ FIELD_PREP(RK3588_GRF_VO1_CON0__HDMI0_PIN_POL, invpolflags)); + break; ++ // [CC:] TODO: ROCKCHIP_VOP2_EP_HDMI1 + case ROCKCHIP_VOP2_EP_EDP0: + die &= ~RK3568_SYS_DSP_INFACE_EN_EDP_MUX; + die |= RK3568_SYS_DSP_INFACE_EN_EDP | +@@ -1535,6 +1628,243 @@ static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id, + vop2_writel(vop2, RK3568_DSP_IF_POL, dip); + } + ++/* ++ * calc the dclk on rk3588 ++ * the available div of dclk is 1, 2, 4 ++ */ ++static unsigned long vop2_calc_dclk(unsigned long child_clk, unsigned long max_dclk) ++{ ++ if (child_clk * 4 <= max_dclk) ++ return child_clk * 4; ++ else if (child_clk * 2 <= max_dclk) ++ return child_clk * 2; ++ else if (child_clk <= max_dclk) ++ return child_clk; ++ else ++ return 0; ++} ++ ++/* ++ * 4 pixclk/cycle on rk3588 ++ * RGB/eDP/HDMI: if_pixclk >= dclk_core ++ * DP: dp_pixclk = dclk_out <= dclk_core ++ * DSI: mipi_pixclk <= dclk_out <= dclk_core ++ */ ++static unsigned long vop2_calc_cru_cfg(struct vop2_video_port *vp, int id, ++ int *dclk_core_div, int *dclk_out_div, ++ int *if_pixclk_div, int *if_dclk_div) ++{ ++ struct vop2 *vop2 = vp->vop2; ++ struct drm_crtc *crtc = &vp->crtc; ++ struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; ++ struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); ++ int output_mode = vcstate->output_mode; ++ unsigned long v_pixclk = adjusted_mode->crtc_clock * 1000LL; /* video timing pixclk */ ++ unsigned long dclk_core_rate = v_pixclk >> 2; ++ unsigned long dclk_rate = v_pixclk; ++ unsigned long dclk_out_rate; ++ unsigned long if_dclk_rate; ++ unsigned long if_pixclk_rate; ++ int K = 1; ++ ++ if (vop2_output_if_is_hdmi(id)) { ++ /* ++ * K = 2: dclk_core = if_pixclk_rate > if_dclk_rate ++ * K = 1: dclk_core = hdmie_edp_dclk > if_pixclk_rate ++ */ ++ if (output_mode == ROCKCHIP_OUT_MODE_YUV420) { ++ dclk_rate = dclk_rate >> 1; ++ K = 2; ++ } ++ ++ if_pixclk_rate = (dclk_core_rate << 1) / K; ++ if_dclk_rate = dclk_core_rate / K; ++ ++ *if_pixclk_div = dclk_rate / if_pixclk_rate; ++ *if_dclk_div = dclk_rate / if_dclk_rate; ++ *dclk_core_div = dclk_rate / dclk_core_rate; ++ } else if (vop2_output_if_is_edp(id)) { ++ /* edp_pixclk = edp_dclk > dclk_core */ ++ if_pixclk_rate = v_pixclk / K; ++ if_dclk_rate = v_pixclk / K; ++ dclk_rate = if_pixclk_rate * K; ++ *dclk_core_div = dclk_rate / dclk_core_rate; ++ *if_pixclk_div = dclk_rate / if_pixclk_rate; ++ *if_dclk_div = *if_pixclk_div; ++ } else if (vop2_output_if_is_dp(id)) { ++ if (output_mode == ROCKCHIP_OUT_MODE_YUV420) ++ dclk_out_rate = v_pixclk >> 3; ++ else ++ dclk_out_rate = v_pixclk >> 2; ++ ++ dclk_rate = vop2_calc_dclk(dclk_out_rate, 600000); ++ if (!dclk_rate) { ++ drm_err(vop2->drm, "DP dclk_out_rate out of range(max_dclk: 600 KHZ, dclk_out_rate: %ld KHZ)\n", ++ dclk_out_rate); ++ return -EINVAL; ++ } ++ *dclk_out_div = dclk_rate / dclk_out_rate; ++ *dclk_core_div = dclk_rate / dclk_core_rate; ++ } else if (vop2_output_if_is_mipi(id)) { ++ if_pixclk_rate = dclk_core_rate / K; ++ /* dclk_core = dclk_out * K = if_pixclk * K = v_pixclk / 4 */ ++ dclk_out_rate = if_pixclk_rate; ++ /* dclk_rate = N * dclk_core_rate N = (1,2,4 ), we get a little factor here */ ++ dclk_rate = vop2_calc_dclk(dclk_out_rate, 600000); ++ if (!dclk_rate) { ++ drm_err(vop2->drm, "MIPI dclk out of range(max_dclk: 600 KHZ, dclk_out_rate: %ld KHZ)\n", ++ dclk_out_rate); ++ return -EINVAL; ++ } ++ *dclk_out_div = dclk_rate / dclk_out_rate; ++ *dclk_core_div = dclk_rate / dclk_core_rate; ++ *if_pixclk_div = 1; /*mipi pixclk == dclk_out*/ ++ } else if (vop2_output_if_is_dpi(id)) { ++ dclk_rate = v_pixclk; ++ *dclk_core_div = dclk_rate / dclk_core_rate; ++ } ++ ++ *if_pixclk_div = ilog2(*if_pixclk_div); ++ *if_dclk_div = ilog2(*if_dclk_div); ++ *dclk_core_div = ilog2(*dclk_core_div); ++ *dclk_out_div = ilog2(*dclk_out_div); ++ ++ drm_dbg(vop2->drm, "dclk:%ld,if_pixclk_div;%d,if_dclk_div:%d\n", dclk_rate, *if_pixclk_div, *if_dclk_div); ++ ++ return dclk_rate; ++} ++ ++/* ++ * MIPI port mux on rk3588: ++ * 0: Video Port2 ++ * 1: Video Port3 ++ * 3: Video Port 1(MIPI1 only) ++ */ ++static u32 rk3588_get_mipi_port_mux(int vp_id) ++{ ++ if (vp_id == 1) ++ return 3; ++ else if (vp_id == 3) ++ return 1; ++ else ++ return 0; ++} ++ ++static u32 rk3588_get_hdmi_pol(u32 flags) ++{ ++ u32 val; ++ ++ val = (flags & DRM_MODE_FLAG_NHSYNC) ? BIT(HSYNC_POSITIVE) : 0; ++ val |= (flags & DRM_MODE_FLAG_NVSYNC) ? BIT(VSYNC_POSITIVE) : 0; ++ ++ return val; ++} ++ ++static void rk3588_set_intf_mux(struct vop2_video_port *vp, int id, u32 polflags) ++{ ++ struct vop2 *vop2 = vp->vop2; ++ int dclk_core_div, dclk_out_div, if_pixclk_div, if_dclk_div; ++ u32 die, dip, div, vp_clk_div, val; ++ ++ vop2_calc_cru_cfg(vp, id, &dclk_core_div, &dclk_out_div, &if_pixclk_div, &if_dclk_div); ++ ++ vp_clk_div = FIELD_PREP(RK3588_VP_CLK_CTRL__DCLK_CORE_DIV, dclk_core_div); ++ vp_clk_div |= FIELD_PREP(RK3588_VP_CLK_CTRL__DCLK_OUT_DIV, dclk_out_div); ++ ++ die = vop2_readl(vop2, RK3568_DSP_IF_EN); ++ dip = vop2_readl(vop2, RK3568_DSP_IF_POL); ++ div = vop2_readl(vop2, RK3568_DSP_IF_CTRL); ++ ++ switch (id) { ++ case ROCKCHIP_VOP2_EP_HDMI0: ++ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV, if_dclk_div); ++ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV, if_pixclk_div); ++ die &= ~RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX; ++ die |= RK3588_SYS_DSP_INFACE_EN_HDMI0 | ++ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX, vp->id); ++ val = rk3588_get_hdmi_pol(polflags); ++ regmap_write(vop2->vop_grf, RK3588_GRF_VOP_CON2, HIWORD_UPDATE(1, 1, 1)); ++ regmap_write(vop2->vo1_grf, RK3588_GRF_VO1_CON0, HIWORD_UPDATE(val, 6, 5)); ++ break; ++ case ROCKCHIP_VOP2_EP_HDMI1: ++ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI1_DCLK_DIV, if_dclk_div); ++ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI1_PCLK_DIV, if_pixclk_div); ++ die &= ~RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX; ++ die |= RK3588_SYS_DSP_INFACE_EN_HDMI1 | ++ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX, vp->id); ++ val = rk3588_get_hdmi_pol(polflags); ++ regmap_write(vop2->vop_grf, RK3588_GRF_VOP_CON2, HIWORD_UPDATE(1, 4, 4)); ++ regmap_write(vop2->vo1_grf, RK3588_GRF_VO1_CON0, HIWORD_UPDATE(val, 8, 7)); ++ break; ++ case ROCKCHIP_VOP2_EP_EDP0: ++ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV, if_dclk_div); ++ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV, if_pixclk_div); ++ die &= ~RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX; ++ die |= RK3588_SYS_DSP_INFACE_EN_EDP0 | ++ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX, vp->id); ++ regmap_write(vop2->vop_grf, RK3588_GRF_VOP_CON2, HIWORD_UPDATE(1, 0, 0)); ++ break; ++ case ROCKCHIP_VOP2_EP_EDP1: ++ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV, if_dclk_div); ++ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV, if_pixclk_div); ++ die &= ~RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX; ++ die |= RK3588_SYS_DSP_INFACE_EN_EDP1 | ++ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX, vp->id); ++ regmap_write(vop2->vop_grf, RK3588_GRF_VOP_CON2, HIWORD_UPDATE(1, 3, 3)); ++ break; ++ case ROCKCHIP_VOP2_EP_MIPI0: ++ div |= FIELD_PREP(RK3588_DSP_IF_MIPI0_PCLK_DIV, if_pixclk_div); ++ die &= ~RK3588_SYS_DSP_INFACE_EN_MIPI0_MUX; ++ val = rk3588_get_mipi_port_mux(vp->id); ++ die |= RK3588_SYS_DSP_INFACE_EN_MIPI0 | ++ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_MIPI0_MUX, !!val); ++ break; ++ case ROCKCHIP_VOP2_EP_MIPI1: ++ div |= FIELD_PREP(RK3588_DSP_IF_MIPI1_PCLK_DIV, if_pixclk_div); ++ die &= ~RK3588_SYS_DSP_INFACE_EN_MIPI1_MUX; ++ val = rk3588_get_mipi_port_mux(vp->id); ++ die |= RK3588_SYS_DSP_INFACE_EN_MIPI1 | ++ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_MIPI1_MUX, val); ++ break; ++ case ROCKCHIP_VOP2_EP_DP0: ++ die &= ~RK3588_SYS_DSP_INFACE_EN_DP0_MUX; ++ die |= RK3588_SYS_DSP_INFACE_EN_DP0 | ++ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_DP0_MUX, vp->id); ++ dip &= ~RK3588_DSP_IF_POL__DP0_PIN_POL; ++ dip |= FIELD_PREP(RK3588_DSP_IF_POL__DP0_PIN_POL, polflags); ++ break; ++ case ROCKCHIP_VOP2_EP_DP1: ++ die &= ~RK3588_SYS_DSP_INFACE_EN_MIPI1_MUX; ++ die |= RK3588_SYS_DSP_INFACE_EN_MIPI1 | ++ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_MIPI1_MUX, vp->id); ++ dip &= ~RK3588_DSP_IF_POL__DP1_PIN_POL; ++ dip |= FIELD_PREP(RK3588_DSP_IF_POL__DP1_PIN_POL, polflags); ++ break; ++ default: ++ drm_err(vop2->drm, "Invalid interface id %d on vp%d\n", id, vp->id); ++ return; ++ } ++ ++ dip |= RK3568_DSP_IF_POL__CFG_DONE_IMD; ++ ++ vop2_vp_write(vp, RK3588_VP_CLK_CTRL, vp_clk_div); ++ vop2_writel(vop2, RK3568_DSP_IF_EN, die); ++ vop2_writel(vop2, RK3568_DSP_IF_CTRL, div); ++ vop2_writel(vop2, RK3568_DSP_IF_POL, dip); ++} ++ ++static void vop2_set_intf_mux(struct vop2_video_port *vp, int ep_id, u32 polflags) ++{ ++ struct vop2 *vop2 = vp->vop2; ++ ++ if (vop2->data->soc_id == 3566 || vop2->data->soc_id == 3568) { ++ // [CC:] drop 2nd polflags arg ++ rk3568_set_intf_mux(vp, ep_id, polflags, polflags); ++ } else if(vop2->data->soc_id == 3588) { ++ rk3588_set_intf_mux(vp, ep_id, polflags); ++ } ++} ++ + static int us_to_vertical_line(struct drm_display_mode *mode, int us) + { + return us * mode->clock / mode->htotal / 1000; +@@ -1564,7 +1894,7 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, + u8 out_mode; + u32 dsp_ctrl = 0; + int act_end; +- u32 val, polflags; ++ u32 val, polflags, invpolflags; + int ret; + struct drm_encoder *encoder; + +@@ -1597,10 +1927,22 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, + if (mode->flags & DRM_MODE_FLAG_PVSYNC) + polflags |= BIT(VSYNC_POSITIVE); + ++ /* RK3588 uses inverted HDMI V/HSYNC polarity */ ++ if (vop2->data->soc_id == 3588) { ++ invpolflags = 0; ++ if (mode->flags & DRM_MODE_FLAG_NHSYNC) ++ invpolflags |= BIT(HSYNC_POSITIVE); ++ if (mode->flags & DRM_MODE_FLAG_NVSYNC) ++ invpolflags |= BIT(VSYNC_POSITIVE); ++ } else { ++ invpolflags = polflags; ++ } ++ + drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) { + struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder); + +- rk3568_set_intf_mux(vp, rkencoder->crtc_endpoint_id, polflags); ++ // rk3568_set_intf_mux(vp, rkencoder->crtc_endpoint_id, polflags, invpolflags); ++ vop2_set_intf_mux(vp, rkencoder->crtc_endpoint_id, polflags); + } + + if (vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA && +@@ -1668,6 +2010,15 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, + + drm_crtc_vblank_on(crtc); + ++ // [CC:] needed? ++ ret = reset_control_assert(vp->dclk_rst); ++ if (ret < 0) ++ drm_warn(vop2->drm, "failed to assert reset: %d\n", ret); ++ udelay(10); ++ ret = reset_control_deassert(vp->dclk_rst); ++ if (ret < 0) ++ drm_warn(vop2->drm, "failed to deassert reset: %d\n", ret); ++ + vop2_unlock(vop2); + } + +@@ -2299,7 +2650,7 @@ static int vop2_create_crtcs(struct vop2 *vop2) + for (i = 0; i < vop2_data->nr_vps; i++) { + const struct vop2_video_port_data *vp_data; + struct device_node *np; +- char dclk_name[9]; ++ char clk_name[16]; + + vp_data = &vop2_data->vp[i]; + vp = &vop2->vps[i]; +@@ -2307,13 +2658,26 @@ static int vop2_create_crtcs(struct vop2 *vop2) + vp->id = vp_data->id; + vp->data = vp_data; + +- snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", vp->id); +- vp->dclk = devm_clk_get(vop2->dev, dclk_name); ++ snprintf(clk_name, sizeof(clk_name), "dclk_vp%d", vp->id); ++ vp->dclk = devm_clk_get(vop2->dev, clk_name); + if (IS_ERR(vp->dclk)) { +- drm_err(vop2->drm, "failed to get %s\n", dclk_name); ++ drm_err(vop2->drm, "failed to get %s\n", clk_name); + return PTR_ERR(vp->dclk); + } + ++ vp->dclk_rst = devm_reset_control_get_optional(vop2->dev, clk_name); ++ if (IS_ERR(vp->dclk_rst)) { ++ drm_err(vop2->drm, "failed to get dclk reset\n"); ++ return PTR_ERR(vp->dclk_rst); ++ } ++ ++ snprintf(clk_name, sizeof(clk_name), "dclk_src_vp%d", vp->id); ++ vp->dclk_parent = devm_clk_get_optional(vop2->dev, clk_name); ++ if (IS_ERR(vp->dclk_parent)) { ++ drm_err(vop2->drm, "failed to get %s\n", clk_name); ++ return PTR_ERR(vp->dclk_parent); ++ } ++ + np = of_graph_get_remote_node(dev->of_node, i, -1); + if (!np) { + drm_dbg(vop2->drm, "%s: No remote for vp%d\n", __func__, i); +@@ -2726,7 +3090,12 @@ static int vop2_bind(struct device *dev, struct device *master, void *data) + return PTR_ERR(vop2->lut_regs); + } + ++ // [CC:] grf -> sys_grf in downstream + vop2->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf"); ++ // [CC:] vop_grf -> grf in downstream ++ // [CC:] make use of new grf's ++ vop2->vop_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,vop-grf"); ++ vop2->vo1_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,vo1-grf"); + + vop2->hclk = devm_clk_get(vop2->dev, "hclk"); + if (IS_ERR(vop2->hclk)) { +@@ -2740,6 +3109,25 @@ static int vop2_bind(struct device *dev, struct device *master, void *data) + return PTR_ERR(vop2->aclk); + } + ++ vop2->pclk = devm_clk_get_optional(vop2->dev, "pclk"); ++ if (IS_ERR(vop2->pclk)) { ++ DRM_DEV_ERROR(vop2->dev, "failed to get pclk source\n"); ++ return PTR_ERR(vop2->pclk); ++ } ++ ++ // [CC:] drop ahb_rst & axi_rst ++ vop2->ahb_rst = devm_reset_control_get_optional(vop2->dev, "ahb"); ++ if (IS_ERR(vop2->ahb_rst)) { ++ DRM_DEV_ERROR(vop2->dev, "failed to get ahb reset\n"); ++ return PTR_ERR(vop2->ahb_rst); ++ } ++ ++ vop2->axi_rst = devm_reset_control_get_optional(vop2->dev, "axi"); ++ if (IS_ERR(vop2->axi_rst)) { ++ DRM_DEV_ERROR(vop2->dev, "failed to get axi reset\n"); ++ return PTR_ERR(vop2->axi_rst); ++ } ++ + vop2->irq = platform_get_irq(pdev, 0); + if (vop2->irq < 0) { + drm_err(vop2->drm, "cannot find irq for vop2\n"); +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h +index 56fd31e05238..cae71df81a6d 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h +@@ -12,10 +12,18 @@ + #include + #include + +-#define VOP_FEATURE_OUTPUT_10BIT BIT(0) ++/* a feature to splice two windows and two vps to support resolution > 4096 */ ++#define VOP_FEATURE_SPLICE BIT(5) ++//[CC:] dupplicate of #define VOP_FEATURE_OUTPUT_RGB10 BIT(0) in vop.h ++#define VOP_FEATURE_OUTPUT_10BIT BIT(0) + ++//[CC:] downstream uses #define WIN_FEATURE_AFBDC BIT(3) + #define WIN_FEATURE_AFBDC BIT(0) + #define WIN_FEATURE_CLUSTER BIT(1) ++/* Left win in splice mode */ ++#define WIN_FEATURE_SPLICE_LEFT BIT(6) ++ ++#define HIWORD_UPDATE(v, h, l) ((GENMASK(h, l) << 16) | ((v) << (l))) + + /* + * the delay number of a window in different mode. +@@ -39,6 +47,18 @@ enum vop2_scale_down_mode { + VOP2_SCALE_DOWN_AVG, + }; + ++/* ++ * vop2 internal power domain id, ++ * should be all none zero, 0 will be treat as invalid; ++ */ ++#define VOP2_PD_CLUSTER0 BIT(0) ++#define VOP2_PD_CLUSTER1 BIT(1) ++#define VOP2_PD_CLUSTER2 BIT(2) ++#define VOP2_PD_CLUSTER3 BIT(3) ++#define VOP2_PD_DSC_8K BIT(5) ++#define VOP2_PD_DSC_4K BIT(6) ++#define VOP2_PD_ESMART BIT(7) ++ + enum vop2_win_regs { + VOP2_WIN_ENABLE, + VOP2_WIN_FORMAT, +@@ -107,6 +127,7 @@ enum vop2_win_regs { + struct vop2_win_data { + const char *name; + unsigned int phys_id; ++ uint8_t splice_win_id; + + u32 base; + enum drm_plane_type type; +@@ -129,9 +150,11 @@ struct vop2_win_data { + + struct vop2_video_port_data { + unsigned int id; ++ uint8_t splice_vp_id; + u32 feature; + u16 gamma_lut_len; + u16 cubic_lut_len; ++ unsigned long dclk_max; + struct vop_rect max_output; + const u8 pre_scan_max_dly[4]; + unsigned int offset; +@@ -145,7 +168,13 @@ struct vop2_data { + struct vop_rect max_output; + + unsigned int win_size; ++ // [CC:] convert to an enum as it is used in conditional statements + unsigned int soc_id; ++ ++ uint8_t nr_dscs; ++ uint8_t nr_conns; ++ const struct vop2_dsc_data *dsc; ++ const struct vop2_connector_if_data *conn; + }; + + /* interrupt define */ +@@ -450,6 +479,45 @@ enum dst_factor_mode { + + #define POLFLAG_DCLK_INV BIT(3) + ++/* RK3588 registers */ ++#define RK3588_GRF_SOC_CON1 0x0304 ++#define RK3588_GRF_VOP_CON2 0x08 ++#define RK3588_GRF_VO1_CON0 0x00 ++ ++#define RK3588_GRF_VO1_CON0__HDMI0_PIN_POL GENMASK(6, 5) ++ ++#define RK3588_SYS_PD_CTRL 0x034 ++#define RK3588_VP_CLK_CTRL 0x0c ++ ++#define RK3588_VP_CLK_CTRL__DCLK_OUT_DIV GENMASK(3, 2) ++#define RK3588_VP_CLK_CTRL__DCLK_CORE_DIV GENMASK(1, 0) ++ ++#define RK3588_SYS_DSP_INFACE_EN_MIPI1_MUX GENMASK(22, 21) ++#define RK3588_SYS_DSP_INFACE_EN_MIPI0_MUX GENMASK(20, 20) ++#define RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX GENMASK(19, 18) ++#define RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX GENMASK(17, 16) ++#define RK3588_SYS_DSP_INFACE_EN_DP1_MUX GENMASK(15, 14) ++#define RK3588_SYS_DSP_INFACE_EN_DP0_MUX GENMASK(13, 12) ++#define RK3588_SYS_DSP_INFACE_EN_DPI GENMASK(9, 8) ++#define RK3588_SYS_DSP_INFACE_EN_MIPI1 BIT(7) ++#define RK3588_SYS_DSP_INFACE_EN_MIPI0 BIT(6) ++#define RK3588_SYS_DSP_INFACE_EN_HDMI1 BIT(5) ++#define RK3588_SYS_DSP_INFACE_EN_EDP1 BIT(4) ++#define RK3588_SYS_DSP_INFACE_EN_HDMI0 BIT(3) ++#define RK3588_SYS_DSP_INFACE_EN_EDP0 BIT(2) ++#define RK3588_SYS_DSP_INFACE_EN_DP1 BIT(1) ++#define RK3588_SYS_DSP_INFACE_EN_DP0 BIT(0) ++ ++#define RK3588_DSP_IF_MIPI1_PCLK_DIV GENMASK(27, 26) ++#define RK3588_DSP_IF_MIPI0_PCLK_DIV GENMASK(25, 24) ++#define RK3588_DSP_IF_EDP_HDMI1_PCLK_DIV GENMASK(22, 22) ++#define RK3588_DSP_IF_EDP_HDMI1_DCLK_DIV GENMASK(21, 20) ++#define RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV GENMASK(18, 18) ++#define RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV GENMASK(17, 16) ++ ++#define RK3588_DSP_IF_POL__DP1_PIN_POL GENMASK(14, 12) ++#define RK3588_DSP_IF_POL__DP0_PIN_POL GENMASK(10, 8) ++ + enum vop2_layer_phy_id { + ROCKCHIP_VOP2_CLUSTER0 = 0, + ROCKCHIP_VOP2_CLUSTER1, +diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c +index 22288ad7f326..2bc1baf5cbd5 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c ++++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c +@@ -131,6 +131,49 @@ static const struct vop2_video_port_data rk3568_vop_video_ports[] = { + }, + }; + ++static const struct vop2_video_port_data rk3588_vop_video_ports[] = { ++ { ++ .id = 0, ++ // .splice_vp_id = 1, ++ // .lut_dma_rid = 1, ++ .feature = VOP_FEATURE_OUTPUT_10BIT, ++ .gamma_lut_len = 1024, ++ .cubic_lut_len = 9 * 9 * 9, ++ // .dclk_max = 600000000, ++ .max_output = { 7680, 4320 }, ++ .pre_scan_max_dly = { 76, 65, 65, 54 }, ++ .offset = 0xc00, ++ }, { ++ .id = 1, ++ // .lut_dma_rid = 14, ++ .feature = VOP_FEATURE_OUTPUT_10BIT, ++ .gamma_lut_len = 1024, ++ .cubic_lut_len = 9 * 9 * 9, ++ // .dclk_max = 600000000, ++ .max_output = { 4096, 2304 }, ++ .pre_scan_max_dly = { 76, 65, 65, 54 }, ++ .offset = 0xd00, ++ }, { ++ .id = 2, ++ // .lut_dma_rid = 14, ++ .feature = VOP_FEATURE_OUTPUT_10BIT, ++ .gamma_lut_len = 1024, ++ .cubic_lut_len = 17 * 17 * 17, ++ // .dclk_max = 600000000, ++ .max_output = { 4096, 2304 }, ++ .pre_scan_max_dly = { 52, 52, 52, 52 }, ++ .offset = 0xe00, ++ }, { ++ .id = 3, ++ // .lut_dma_rid = 14, ++ .gamma_lut_len = 1024, ++ // .dclk_max = 200000000, ++ .max_output = { 2048, 1536 }, ++ .pre_scan_max_dly = { 52, 52, 52, 52 }, ++ .offset = 0xf00, ++ }, ++}; ++ + /* + * rk3568 vop with 2 cluster, 2 esmart win, 2 smart win. + * Every cluster can work as 4K win or split into two win. +@@ -234,6 +277,174 @@ static const struct vop2_win_data rk3568_vop_win_data[] = { + }, + }; + ++/* ++ * rk3588 vop with 4 cluster, 4 esmart win. ++ * Every cluster can work as 4K win or split into two win. ++ * All win in cluster support AFBCD. ++ * ++ * Every esmart win and smart win support 4 Multi-region. ++ * ++ * Scale filter mode: ++ * ++ * * Cluster: bicubic for horizontal scale up, others use bilinear ++ * * ESmart: ++ * * nearest-neighbor/bilinear/bicubic for scale up ++ * * nearest-neighbor/bilinear/average for scale down ++ * ++ * AXI Read ID assignment: ++ * Two AXI bus: ++ * AXI0 is a read/write bus with a higher performance. ++ * AXI1 is a read only bus. ++ * ++ * Every window on a AXI bus must assigned two unique ++ * read id(yrgb_id/uv_id, valid id are 0x1~0xe). ++ * ++ * AXI0: ++ * Cluster0/1, Esmart0/1, WriteBack ++ * ++ * AXI 1: ++ * Cluster2/3, Esmart2/3 ++ */ ++static const struct vop2_win_data rk3588_vop_win_data[] = { ++ { ++ .name = "Cluster0-win0", ++ .phys_id = ROCKCHIP_VOP2_CLUSTER0, ++ //[CC:] .splice_win_id = ROCKCHIP_VOP2_CLUSTER1, ++ .base = 0x1000, ++ .formats = formats_cluster, ++ .nformats = ARRAY_SIZE(formats_cluster), ++ .format_modifiers = format_modifiers_afbc, ++ .layer_sel_id = 0, ++ .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 | ++ DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, ++ .type = DRM_PLANE_TYPE_OVERLAY, ++ // .pd_id = VOP2_PD_CLUSTER0, ++ // .axi_id = 0, ++ // .axi_yrgb_id = 2, ++ // .axi_uv_id = 3, ++ .max_upscale_factor = 4, ++ .max_downscale_factor = 4, ++ .dly = { 4, 26, 29 }, ++ //[CC:] WIN_FEATURE_SPLICE_LEFT ++ .feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER, ++ }, { ++ .name = "Cluster1-win0", ++ .phys_id = ROCKCHIP_VOP2_CLUSTER1, ++ .base = 0x1200, ++ .formats = formats_cluster, ++ .nformats = ARRAY_SIZE(formats_cluster), ++ .format_modifiers = format_modifiers_afbc, ++ .layer_sel_id = 1, ++ .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 | ++ DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, ++ .type = DRM_PLANE_TYPE_OVERLAY, ++ // .pd_id = VOP2_PD_CLUSTER1, ++ // .axi_id = 0, ++ // .axi_yrgb_id = 6, ++ // .axi_uv_id = 7, ++ .max_upscale_factor = 4, ++ .max_downscale_factor = 4, ++ .dly = { 4, 26, 29 }, ++ .feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER, ++ }, { ++ .name = "Cluster2-win0", ++ .phys_id = ROCKCHIP_VOP2_CLUSTER2, ++ // [CC:] .splice_win_id = ROCKCHIP_VOP2_CLUSTER3, ++ .base = 0x1400, ++ .formats = formats_cluster, ++ .nformats = ARRAY_SIZE(formats_cluster), ++ .format_modifiers = format_modifiers_afbc, ++ .layer_sel_id = 4, ++ .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 | ++ DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, ++ .type = DRM_PLANE_TYPE_OVERLAY, ++ // .pd_id = VOP2_PD_CLUSTER2, ++ // .axi_id = 1, ++ // .axi_yrgb_id = 2, ++ // .axi_uv_id = 3, ++ .max_upscale_factor = 4, ++ .max_downscale_factor = 4, ++ .dly = { 4, 26, 29 }, ++ //[CC:] WIN_FEATURE_SPLICE_LEFT ++ .feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER, ++ }, { ++ .name = "Cluster3-win0", ++ .phys_id = ROCKCHIP_VOP2_CLUSTER3, ++ .base = 0x1600, ++ .formats = formats_cluster, ++ .nformats = ARRAY_SIZE(formats_cluster), ++ .format_modifiers = format_modifiers_afbc, ++ .layer_sel_id = 5, ++ .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 | ++ DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, ++ .type = DRM_PLANE_TYPE_OVERLAY, ++ // .pd_id = VOP2_PD_CLUSTER3, ++ // .axi_id = 1, ++ // .axi_yrgb_id = 6, ++ // .axi_uv_id = 7, ++ .max_upscale_factor = 4, ++ .max_downscale_factor = 4, ++ .dly = { 4, 26, 29 }, ++ .feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER, ++ }, { ++ .name = "Esmart0-win0", ++ .phys_id = ROCKCHIP_VOP2_ESMART0, ++ // [CC:] .splice_win_id = ROCKCHIP_VOP2_ESMART1, ++ .formats = formats_rk356x_esmart, ++ .nformats = ARRAY_SIZE(formats_rk356x_esmart), ++ .format_modifiers = format_modifiers, ++ .base = 0x1800, ++ .layer_sel_id = 2, ++ .supported_rotations = DRM_MODE_REFLECT_Y, ++ .type = DRM_PLANE_TYPE_PRIMARY, ++ // [CC:] .pd_id missing in downstream code ++ // .axi_id = 0, ++ // .axi_yrgb_id = 0x0a, ++ // .axi_uv_id = 0x0b, ++ .max_upscale_factor = 8, ++ .max_downscale_factor = 8, ++ .dly = { 23, 45, 48 }, ++ // .feature = WIN_FEATURE_SPLICE_LEFT | WIN_FEATURE_MULTI_AREA, ++ }, { ++ .name = "Esmart2-win0", ++ .phys_id = ROCKCHIP_VOP2_ESMART2, ++ // [CC:] .splice_win_id = ROCKCHIP_VOP2_ESMART3, ++ .formats = formats_rk356x_esmart, ++ .nformats = ARRAY_SIZE(formats_rk356x_esmart), ++ .format_modifiers = format_modifiers, ++ .base = 0x1c00, ++ .layer_sel_id = 6, ++ .supported_rotations = DRM_MODE_REFLECT_Y, ++ .type = DRM_PLANE_TYPE_PRIMARY, ++ // .pd_id = VOP2_PD_ESMART, ++ // .axi_id = 1, ++ // .axi_yrgb_id = 0x0a, ++ // .axi_uv_id = 0x0b, ++ .max_upscale_factor = 8, ++ .max_downscale_factor = 8, ++ .dly = { 23, 45, 48 }, ++ // .feature = WIN_FEATURE_SPLICE_LEFT | WIN_FEATURE_MULTI_AREA, ++ }, { ++ .name = "Esmart1-win0", ++ .phys_id = ROCKCHIP_VOP2_ESMART1, ++ .formats = formats_rk356x_esmart, ++ .nformats = ARRAY_SIZE(formats_rk356x_esmart), ++ .format_modifiers = format_modifiers, ++ .base = 0x1a00, ++ .layer_sel_id = 3, ++ .supported_rotations = DRM_MODE_REFLECT_Y, ++ .type = DRM_PLANE_TYPE_PRIMARY, ++ // .pd_id = VOP2_PD_ESMART, ++ // .axi_id = 0, ++ // .axi_yrgb_id = 0x0c, ++ // .axi_uv_id = 0x0d, ++ .max_upscale_factor = 8, ++ .max_downscale_factor = 8, ++ .dly = { 23, 45, 48 }, ++ // .feature = WIN_FEATURE_MULTI_AREA, ++ }, ++}; ++ + static const struct vop2_data rk3566_vop = { + .nr_vps = 3, + .max_input = { 4096, 2304 }, +@@ -246,14 +457,27 @@ static const struct vop2_data rk3566_vop = { + + static const struct vop2_data rk3568_vop = { + .nr_vps = 3, +- .max_input = { 4096, 2304 }, +- .max_output = { 4096, 2304 }, ++ .max_input = { 4096, 4320 }, ++ .max_output = { 4096, 4320 }, + .vp = rk3568_vop_video_ports, + .win = rk3568_vop_win_data, + .win_size = ARRAY_SIZE(rk3568_vop_win_data), + .soc_id = 3568, + }; + ++static const struct vop2_data rk3588_vop = { ++ // .feature = VOP_FEATURE_SPLICE, ++ .nr_vps = 4, ++ .max_input = { 4096, 2304 }, ++ .max_output = { 4096, 2304 }, ++ .vp = rk3588_vop_video_ports, ++ .win = rk3588_vop_win_data, ++ .win_size = ARRAY_SIZE(rk3588_vop_win_data), ++ // .conn = rk3588_conn_if_data, ++ // .nr_conns = ARRAY_SIZE(rk3588_conn_if_data), ++ .soc_id = 3588, ++}; ++ + static const struct of_device_id vop2_dt_match[] = { + { + .compatible = "rockchip,rk3566-vop", +@@ -261,6 +485,9 @@ static const struct of_device_id vop2_dt_match[] = { + }, { + .compatible = "rockchip,rk3568-vop", + .data = &rk3568_vop, ++ }, { ++ .compatible = "rockchip,rk3588-vop", ++ .data = &rk3588_vop, + }, { + }, + }; +diff --git a/include/dt-bindings/soc/rockchip,vop2.h b/include/dt-bindings/soc/rockchip,vop2.h +index 6e66a802b96a..668f199df9f0 100644 +--- a/include/dt-bindings/soc/rockchip,vop2.h ++++ b/include/dt-bindings/soc/rockchip,vop2.h +@@ -10,5 +10,9 @@ + #define ROCKCHIP_VOP2_EP_LVDS0 5 + #define ROCKCHIP_VOP2_EP_MIPI1 6 + #define ROCKCHIP_VOP2_EP_LVDS1 7 ++#define ROCKCHIP_VOP2_EP_HDMI1 8 ++#define ROCKCHIP_VOP2_EP_EDP1 9 ++#define ROCKCHIP_VOP2_EP_DP0 10 ++#define ROCKCHIP_VOP2_EP_DP1 11 + + #endif /* __DT_BINDINGS_ROCKCHIP_VOP2_H */ +diff --git a/include/uapi/drm/rockchip_drm.h b/include/uapi/drm/rockchip_drm.h +new file mode 100644 +index 000000000000..246192fa2922 +--- /dev/null ++++ b/include/uapi/drm/rockchip_drm.h +@@ -0,0 +1,134 @@ ++/* ++ * ++ * Copyright (c) Fuzhou Rockchip Electronics Co.Ltd ++ * Authors: ++ * Mark Yao ++ * ++ * base on exynos_drm.h ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#ifndef _UAPI_ROCKCHIP_DRM_H ++#define _UAPI_ROCKCHIP_DRM_H ++ ++#include ++#include ++ ++/* ++ * Send vcnt event instead of blocking, ++ * like _DRM_VBLANK_EVENT ++ */ ++#define _DRM_ROCKCHIP_VCNT_EVENT 0x80000000 ++#define DRM_EVENT_ROCKCHIP_CRTC_VCNT 0xf ++ ++/* memory type definitions. */ ++enum drm_rockchip_gem_mem_type { ++ /* Physically Continuous memory. */ ++ ROCKCHIP_BO_CONTIG = 1 << 0, ++ /* cachable mapping. */ ++ ROCKCHIP_BO_CACHABLE = 1 << 1, ++ /* write-combine mapping. */ ++ ROCKCHIP_BO_WC = 1 << 2, ++ ROCKCHIP_BO_SECURE = 1 << 3, ++ /* keep kmap for cma buffer or alloc kmap for other type memory */ ++ ROCKCHIP_BO_ALLOC_KMAP = 1 << 4, ++ ROCKCHIP_BO_MASK = ROCKCHIP_BO_CONTIG | ROCKCHIP_BO_CACHABLE | ++ ROCKCHIP_BO_WC | ROCKCHIP_BO_SECURE | ROCKCHIP_BO_ALLOC_KMAP, ++}; ++ ++/** ++ * User-desired buffer creation information structure. ++ * ++ * @size: user-desired memory allocation size. ++ * @flags: user request for setting memory type or cache attributes. ++ * @handle: returned a handle to created gem object. ++ * - this handle will be set by gem module of kernel side. ++ */ ++struct drm_rockchip_gem_create { ++ uint64_t size; ++ uint32_t flags; ++ uint32_t handle; ++}; ++ ++struct drm_rockchip_gem_phys { ++ uint32_t handle; ++ uint32_t phy_addr; ++}; ++ ++/** ++ * A structure for getting buffer offset. ++ * ++ * @handle: a pointer to gem object created. ++ * @pad: just padding to be 64-bit aligned. ++ * @offset: relatived offset value of the memory region allocated. ++ * - this value should be set by user. ++ */ ++struct drm_rockchip_gem_map_off { ++ uint32_t handle; ++ uint32_t pad; ++ uint64_t offset; ++}; ++ ++/* acquire type definitions. */ ++enum drm_rockchip_gem_cpu_acquire_type { ++ DRM_ROCKCHIP_GEM_CPU_ACQUIRE_SHARED = 0x0, ++ DRM_ROCKCHIP_GEM_CPU_ACQUIRE_EXCLUSIVE = 0x1, ++}; ++ ++enum rockchip_crtc_feture { ++ ROCKCHIP_DRM_CRTC_FEATURE_ALPHA_SCALE, ++ ROCKCHIP_DRM_CRTC_FEATURE_HDR10, ++ ROCKCHIP_DRM_CRTC_FEATURE_NEXT_HDR, ++}; ++ ++enum rockchip_plane_feture { ++ ROCKCHIP_DRM_PLANE_FEATURE_SCALE, ++ ROCKCHIP_DRM_PLANE_FEATURE_ALPHA, ++ ROCKCHIP_DRM_PLANE_FEATURE_HDR2SDR, ++ ROCKCHIP_DRM_PLANE_FEATURE_SDR2HDR, ++ ROCKCHIP_DRM_PLANE_FEATURE_AFBDC, ++ ROCKCHIP_DRM_PLANE_FEATURE_PDAF_POS, ++ ROCKCHIP_DRM_PLANE_FEATURE_MAX, ++}; ++ ++enum rockchip_cabc_mode { ++ ROCKCHIP_DRM_CABC_MODE_DISABLE, ++ ROCKCHIP_DRM_CABC_MODE_NORMAL, ++ ROCKCHIP_DRM_CABC_MODE_LOWPOWER, ++ ROCKCHIP_DRM_CABC_MODE_USERSPACE, ++}; ++ ++struct drm_rockchip_vcnt_event { ++ struct drm_pending_event base; ++}; ++ ++#define DRM_ROCKCHIP_GEM_CREATE 0x00 ++#define DRM_ROCKCHIP_GEM_MAP_OFFSET 0x01 ++#define DRM_ROCKCHIP_GEM_CPU_ACQUIRE 0x02 ++#define DRM_ROCKCHIP_GEM_CPU_RELEASE 0x03 ++#define DRM_ROCKCHIP_GEM_GET_PHYS 0x04 ++#define DRM_ROCKCHIP_GET_VCNT_EVENT 0x05 ++ ++#define DRM_IOCTL_ROCKCHIP_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + \ ++ DRM_ROCKCHIP_GEM_CREATE, struct drm_rockchip_gem_create) ++ ++#define DRM_IOCTL_ROCKCHIP_GEM_MAP_OFFSET DRM_IOWR(DRM_COMMAND_BASE + \ ++ DRM_ROCKCHIP_GEM_MAP_OFFSET, struct drm_rockchip_gem_map_off) ++ ++#define DRM_IOCTL_ROCKCHIP_GEM_CPU_ACQUIRE DRM_IOWR(DRM_COMMAND_BASE + \ ++ DRM_ROCKCHIP_GEM_CPU_ACQUIRE, struct drm_rockchip_gem_cpu_acquire) ++ ++#define DRM_IOCTL_ROCKCHIP_GEM_CPU_RELEASE DRM_IOWR(DRM_COMMAND_BASE + \ ++ DRM_ROCKCHIP_GEM_CPU_RELEASE, struct drm_rockchip_gem_cpu_release) ++ ++#define DRM_IOCTL_ROCKCHIP_GEM_GET_PHYS DRM_IOWR(DRM_COMMAND_BASE + \ ++ DRM_ROCKCHIP_GEM_GET_PHYS, struct drm_rockchip_gem_phys) ++ ++#define DRM_IOCTL_ROCKCHIP_GET_VCNT_EVENT DRM_IOWR(DRM_COMMAND_BASE + \ ++ DRM_ROCKCHIP_GET_VCNT_EVENT, union drm_wait_vblank) ++ ++#endif /* _UAPI_ROCKCHIP_DRM_H */ +-- +2.42.1 + + +From 94b6bcb0b87d896e764615f9c1601ac270300ce8 Mon Sep 17 00:00:00 2001 +From: Cristian Ciocaltea +Date: Wed, 1 Nov 2023 16:42:58 +0200 +Subject: [PATCH 5/9] arm64: dts: rockchip: Add DT nodes for HDMI support in + RK3588 + +Signed-off-by: Cristian Ciocaltea +--- + .../boot/dts/rockchip/rk3588-rock-5b.dts | 20 ++ + arch/arm64/boot/dts/rockchip/rk3588s.dtsi | 220 ++++++++++++++++++ + 2 files changed, 240 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts +index 9ee415e6f498..39df9bfbbc5b 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts +@@ -920,3 +920,23 @@ &usb_host1_xhci { + &usb_host2_xhci { + status = "okay"; + }; ++ ++&hdmi0 { ++ status = "okay"; ++}; ++ ++&hdmi0_in_vp0 { ++ status = "okay"; ++}; ++ ++&hdptxphy_hdmi0 { ++ status = "okay"; ++}; ++ ++&vop_mmu { ++ status = "okay"; ++}; ++ ++&vop { ++ status = "okay"; ++}; +diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi +index e7ebeda1c799..4481a2e578f8 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -580,6 +581,11 @@ spll: clock-0 { + #clock-cells = <0>; + }; + ++ display_subsystem: display-subsystem { ++ compatible = "rockchip,display-subsystem"; ++ ports = <&vop_out>; ++ }; ++ + thermal_zones: thermal-zones { + soc_thermal: soc-thermal { + polling-delay-passive = <20>; /* milliseconds */ +@@ -1019,12 +1025,23 @@ u2phy2_host: host-port { + }; + }; + ++ vop_grf: syscon@fd5a4000 { ++ compatible = "rockchip,rk3588-vop-grf", "syscon"; ++ reg = <0x0 0xfd5a4000 0x0 0x2000>; ++ }; ++ + vo0_grf: syscon@fd5a6000 { + compatible = "rockchip,rk3588-vo-grf", "syscon"; + reg = <0x0 0xfd5a6000 0x0 0x2000>; + clocks = <&cru PCLK_VO0GRF>; + }; + ++ vo1_grf: syscon@fd5a8000 { ++ compatible = "rockchip,rk3588-vo-grf", "syscon"; ++ reg = <0x0 0xfd5a8000 0x0 0x100>; ++ clocks = <&cru PCLK_VO1GRF>; ++ }; ++ + usb_grf: syscon@fd5ac000 { + compatible = "rockchip,rk3588-usb-grf", "syscon"; + reg = <0x0 0xfd5ac000 0x0 0x4000>; +@@ -1055,6 +1072,11 @@ u2phy3_host: host-port { + }; + }; + ++ hdptxphy0_grf: syscon@fd5e0000 { ++ compatible = "rockchip,rk3588-hdptxphy-grf", "syscon"; ++ reg = <0x0 0xfd5e0000 0x0 0x100>; ++ }; ++ + ioc: syscon@fd5f0000 { + compatible = "rockchip,rk3588-ioc", "syscon"; + reg = <0x0 0xfd5f0000 0x0 0x10000>; +@@ -1478,6 +1500,112 @@ power-domain@RK3588_PD_SDMMC { + }; + }; + ++ vop: vop@fdd90000 { ++ compatible = "rockchip,rk3588-vop"; ++ reg = <0x0 0xfdd90000 0x0 0x4200>, <0x0 0xfdd95000 0x0 0x1000>; ++ reg-names = "vop", "gamma_lut"; ++ interrupts = ; ++ clocks = <&cru ACLK_VOP>, ++ <&cru HCLK_VOP>, ++ <&cru DCLK_VOP0>, ++ <&cru DCLK_VOP1>, ++ <&cru DCLK_VOP2>, ++ <&cru DCLK_VOP3>, ++ <&cru PCLK_VOP_ROOT>, ++ <&cru DCLK_VOP0_SRC>, ++ <&cru DCLK_VOP1_SRC>, ++ <&cru DCLK_VOP2_SRC>; ++ clock-names = "aclk", ++ "hclk", ++ "dclk_vp0", ++ "dclk_vp1", ++ "dclk_vp2", ++ "dclk_vp3", ++ "pclk", ++ "dclk_src_vp0", ++ "dclk_src_vp1", ++ "dclk_src_vp2"; ++ resets = <&cru SRST_A_VOP>, ++ <&cru SRST_H_VOP>, ++ <&cru SRST_D_VOP0>, ++ <&cru SRST_D_VOP1>, ++ <&cru SRST_D_VOP2>, ++ <&cru SRST_D_VOP3>; ++ reset-names = "axi", ++ "ahb", ++ "dclk_vp0", ++ "dclk_vp1", ++ "dclk_vp2", ++ "dclk_vp3"; ++ iommus = <&vop_mmu>; ++ power-domains = <&power RK3588_PD_VOP>; ++ rockchip,grf = <&sys_grf>; ++ rockchip,vop-grf = <&vop_grf>; ++ rockchip,vo1-grf = <&vo1_grf>; ++ rockchip,pmu = <&pmu>; ++ ++ status = "disabled"; ++ ++ // [CC: move endpoints to rock5b dts, see rock-3a] ++ vop_out: ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ vp0: port@0 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0>; ++ ++ vp0_out_hdmi0: endpoint@ROCKCHIP_VOP2_EP_HDMI0 { ++ reg = ; ++ remote-endpoint = <&hdmi0_in_vp0>; ++ }; ++ }; ++ ++ vp1: port@1 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <1>; ++ ++ vp1_out_hdmi0: endpoint@ROCKCHIP_VOP2_EP_HDMI0 { ++ reg = ; ++ remote-endpoint = <&hdmi0_in_vp1>; ++ }; ++ }; ++ ++ vp2: port@2 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <2>; ++ ++ assigned-clocks = <&cru DCLK_VOP2_SRC>; ++ assigned-clock-parents = <&cru PLL_V0PLL>; ++ ++ vp2_out_hdmi0: endpoint@ROCKCHIP_VOP2_EP_HDMI0 { ++ reg = ; ++ remote-endpoint = <&hdmi0_in_vp2>; ++ }; ++ }; ++ ++ vp3: port@3 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <3>; ++ }; ++ }; ++ }; ++ ++ vop_mmu: iommu@fdd97e00 { ++ compatible = "rockchip,rk3568-iommu"; ++ reg = <0x0 0xfdd97e00 0x0 0x100>, <0x0 0xfdd97f00 0x0 0x100>; ++ interrupts = ; ++ interrupt-names = "vop_mmu"; ++ clocks = <&cru ACLK_VOP>, <&cru HCLK_VOP>; ++ clock-names = "aclk", "iface"; ++ #iommu-cells = <0>; ++ status = "disabled"; ++ }; ++ + i2s4_8ch: i2s@fddc0000 { + compatible = "rockchip,rk3588-i2s-tdm"; + reg = <0x0 0xfddc0000 0x0 0x1000>; +@@ -1529,6 +1657,77 @@ i2s9_8ch: i2s@fddfc000 { + status = "disabled"; + }; + ++ hdmi0: hdmi@fde80000 { ++ compatible = "rockchip,rk3588-dw-hdmi"; ++ reg = <0x0 0xfde80000 0x0 0x20000>; ++ interrupts = , ++ , ++ , ++ , ++ ; ++ clocks = <&cru PCLK_HDMITX0>, ++ <&cru CLK_HDMIHDP0>, ++ <&cru CLK_HDMITX0_EARC>, ++ <&cru CLK_HDMITX0_REF>, ++ <&cru MCLK_I2S5_8CH_TX>, ++ <&cru DCLK_VOP0>, ++ <&cru DCLK_VOP1>, ++ <&cru DCLK_VOP2>, ++ <&cru DCLK_VOP3>, ++ <&cru HCLK_VO1>; ++ clock-names = "pclk", ++ "hpd", ++ "earc", ++ "hdmitx_ref", ++ "aud", ++ "dclk_vp0", ++ "dclk_vp1", ++ "dclk_vp2", ++ "dclk_vp3", ++ "hclk_vo1"; ++ resets = <&cru SRST_HDMITX0_REF>, <&cru SRST_HDMIHDP0>; ++ reset-names = "ref", "hdp"; ++ power-domains = <&power RK3588_PD_VO1>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&hdmim0_tx0_cec &hdmim0_tx0_hpd &hdmim0_tx0_scl &hdmim0_tx0_sda>; ++ reg-io-width = <4>; ++ rockchip,grf = <&sys_grf>; ++ rockchip,vo1_grf = <&vo1_grf>; ++ phys = <&hdptxphy_hdmi0>; ++ phy-names = "hdmi"; ++ #sound-dai-cells = <0>; ++ status = "disabled"; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hdmi0_in: port@0 { ++ reg = <0>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hdmi0_in_vp0: endpoint@0 { ++ reg = <0>; ++ remote-endpoint = <&vp0_out_hdmi0>; ++ status = "disabled"; ++ }; ++ ++ hdmi0_in_vp1: endpoint@1 { ++ reg = <1>; ++ remote-endpoint = <&vp1_out_hdmi0>; ++ status = "disabled"; ++ }; ++ ++ hdmi0_in_vp2: endpoint@2 { ++ reg = <2>; ++ remote-endpoint = <&vp2_out_hdmi0>; ++ status = "disabled"; ++ }; ++ }; ++ }; ++ }; ++ + qos_gpu_m0: qos@fdf35000 { + compatible = "rockchip,rk3588-qos", "syscon"; + reg = <0x0 0xfdf35000 0x0 0x20>; +@@ -2760,6 +2959,27 @@ dmac2: dma-controller@fed10000 { + #dma-cells = <1>; + }; + ++ hdptxphy_hdmi0: hdmiphy@fed60000 { ++ compatible = "rockchip,rk3588-hdptx-phy-hdmi"; ++ reg = <0x0 0xfed60000 0x0 0x2000>; ++ clocks = <&cru CLK_USB2PHY_HDPTXRXPHY_REF>, <&cru PCLK_HDPTX0>; ++ clock-names = "ref", "apb"; ++ resets = <&cru SRST_HDPTX0>, <&cru SRST_P_HDPTX0>, ++ <&cru SRST_HDPTX0_INIT>, <&cru SRST_HDPTX0_CMN>, ++ <&cru SRST_HDPTX0_LANE>, <&cru SRST_HDPTX0_ROPLL>, ++ <&cru SRST_HDPTX0_LCPLL>; ++ reset-names = "phy", "apb", "init", "cmn", "lane", "ropll", ++ "lcpll"; ++ rockchip,grf = <&hdptxphy0_grf>; ++ #phy-cells = <0>; ++ status = "disabled"; ++ ++ hdptxphy_hdmi_clk0: clk-port { ++ #clock-cells = <0>; ++ status = "disabled"; ++ }; ++ }; ++ + usbdp_phy0: phy@fed80000 { + compatible = "rockchip,rk3588-usbdp-phy"; + reg = <0x0 0xfed80000 0x0 0x10000>; +-- +2.42.1 + + +From 5760547fa8739f1185b4b2523fb801fd678cfbde Mon Sep 17 00:00:00 2001 +From: Cristian Ciocaltea +Date: Fri, 3 Nov 2023 19:58:02 +0200 +Subject: [PATCH 6/9] drm/rockchip: vop2: Fix display modes handling on rk3588 + +Improve HDMI0 clocking in order to properly support the failing display +modes mentioned in the fixed commit. + +Fixes: cd2e7fe23a00 ("drm/rockchip: vop2: Add basic support for rk3588") +Signed-off-by: Cristian Ciocaltea +--- + drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 670 +++++++++++++++++-- + 1 file changed, 612 insertions(+), 58 deletions(-) + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +index 509caa514dca..a073d1d5c453 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +@@ -5,6 +5,8 @@ + */ + #include + #include ++#include ++#include + #include + #include + #include +@@ -215,16 +217,35 @@ struct vop2 { + struct clk *hclk; + struct clk *aclk; + struct clk *pclk; ++ // [CC:] handle all display modes ++ struct clk *hdmi0_phy_pll; + struct reset_control *ahb_rst; + struct reset_control *axi_rst; + + /* optional internal rgb encoder */ + struct rockchip_rgb *rgb; + ++ /* list_head of internal clk */ ++ struct list_head clk_list_head; ++ + /* must be put at the end of the struct */ + struct vop2_win win[]; + }; + ++struct vop2_clk { ++ struct vop2 *vop2; ++ struct list_head list; ++ unsigned long rate; ++ struct clk_hw hw; ++ struct clk_divider div; ++ int div_val; ++ u8 parent_index; ++}; ++ ++#define to_vop2_clk(_hw) container_of(_hw, struct vop2_clk, hw) ++ ++#define VOP2_MAX_DCLK_RATE 600000 /* kHz */ ++ + #define vop2_output_if_is_hdmi(x) (x == ROCKCHIP_VOP2_EP_HDMI0 || x == ROCKCHIP_VOP2_EP_HDMI1) + #define vop2_output_if_is_dp(x) (x == ROCKCHIP_VOP2_EP_DP0 || x == ROCKCHIP_VOP2_EP_DP1) + #define vop2_output_if_is_edp(x) (x == ROCKCHIP_VOP2_EP_EDP0 || x == ROCKCHIP_VOP2_EP_EDP1) +@@ -960,59 +981,6 @@ static void vop2_disable(struct vop2 *vop2) + clk_disable_unprepare(vop2->hclk); + } + +-static void vop2_crtc_atomic_disable(struct drm_crtc *crtc, +- struct drm_atomic_state *state) +-{ +- struct vop2_video_port *vp = to_vop2_video_port(crtc); +- struct vop2 *vop2 = vp->vop2; +- struct drm_crtc_state *old_crtc_state; +- int ret; +- +- vop2_lock(vop2); +- +- old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc); +- drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false); +- +- drm_crtc_vblank_off(crtc); +- +- /* +- * Vop standby will take effect at end of current frame, +- * if dsp hold valid irq happen, it means standby complete. +- * +- * we must wait standby complete when we want to disable aclk, +- * if not, memory bus maybe dead. +- */ +- reinit_completion(&vp->dsp_hold_completion); +- +- vop2_crtc_enable_irq(vp, VP_INT_DSP_HOLD_VALID); +- +- vop2_vp_write(vp, RK3568_VP_DSP_CTRL, RK3568_VP_DSP_CTRL__STANDBY); +- +- ret = wait_for_completion_timeout(&vp->dsp_hold_completion, +- msecs_to_jiffies(50)); +- if (!ret) +- drm_info(vop2->drm, "wait for vp%d dsp_hold timeout\n", vp->id); +- +- vop2_crtc_disable_irq(vp, VP_INT_DSP_HOLD_VALID); +- +- clk_disable_unprepare(vp->dclk); +- +- vop2->enable_count--; +- +- if (!vop2->enable_count) +- vop2_disable(vop2); +- +- vop2_unlock(vop2); +- +- if (crtc->state->event && !crtc->state->active) { +- spin_lock_irq(&crtc->dev->event_lock); +- drm_crtc_send_vblank_event(crtc, crtc->state->event); +- spin_unlock_irq(&crtc->dev->event_lock); +- +- crtc->state->event = NULL; +- } +-} +- + static int vop2_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *astate) + { +@@ -1458,9 +1426,30 @@ static bool vop2_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) + { ++ struct vop2_video_port *vp = to_vop2_video_port(crtc); ++ struct drm_connector *connector; ++ struct drm_connector_list_iter conn_iter; ++ struct drm_crtc_state *new_crtc_state = container_of(mode, struct drm_crtc_state, mode); + drm_mode_set_crtcinfo(adj_mode, CRTC_INTERLACE_HALVE_V | + CRTC_STEREO_DOUBLE); + ++ if (mode->flags & DRM_MODE_FLAG_DBLCLK) ++ adj_mode->crtc_clock *= 2; ++ ++ drm_connector_list_iter_begin(crtc->dev, &conn_iter); ++ drm_for_each_connector_iter(connector, &conn_iter) { ++ if ((new_crtc_state->connector_mask & drm_connector_mask(connector)) && ++ ((connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) || ++ (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA))) { ++ drm_connector_list_iter_end(&conn_iter); ++ return true; ++ } ++ } ++ drm_connector_list_iter_end(&conn_iter); ++ ++ if (adj_mode->crtc_clock <= VOP2_MAX_DCLK_RATE) ++ adj_mode->crtc_clock = DIV_ROUND_UP(clk_round_rate(vp->dclk, ++ adj_mode->crtc_clock * 1000), 1000); + return true; + } + +@@ -1644,6 +1633,31 @@ static unsigned long vop2_calc_dclk(unsigned long child_clk, unsigned long max_d + return 0; + } + ++static struct vop2_clk *vop2_clk_get(struct vop2 *vop2, const char *name); ++ ++static int vop2_cru_set_rate(struct vop2_clk *if_pixclk, struct vop2_clk *if_dclk) ++{ ++ int ret = 0; ++ ++ if (if_pixclk) { ++ ret = clk_set_rate(if_pixclk->hw.clk, if_pixclk->rate); ++ if (ret < 0) { ++ DRM_DEV_ERROR(if_pixclk->vop2->dev, "set %s to %ld failed: %d\n", ++ clk_hw_get_name(&if_pixclk->hw), if_pixclk->rate, ret); ++ return ret; ++ } ++ } ++ ++ if (if_dclk) { ++ ret = clk_set_rate(if_dclk->hw.clk, if_dclk->rate); ++ if (ret < 0) ++ DRM_DEV_ERROR(if_dclk->vop2->dev, "set %s to %ld failed %d\n", ++ clk_hw_get_name(&if_dclk->hw), if_dclk->rate, ret); ++ } ++ ++ return ret; ++} ++ + /* + * 4 pixclk/cycle on rk3588 + * RGB/eDP/HDMI: if_pixclk >= dclk_core +@@ -1668,6 +1682,71 @@ static unsigned long vop2_calc_cru_cfg(struct vop2_video_port *vp, int id, + int K = 1; + + if (vop2_output_if_is_hdmi(id)) { ++ if (vop2->data->soc_id == 3588 && id == ROCKCHIP_VOP2_EP_HDMI0) { ++ const char *clk_src_name = "hdmi_edp0_clk_src"; ++ const char *clk_parent_name = "dclk"; ++ const char *pixclk_name = "hdmi_edp0_pixclk"; ++ const char *dclk_name = "hdmi_edp0_dclk"; ++ struct vop2_clk *if_clk_src, *if_clk_parent, *if_pixclk, *if_dclk, *dclk, *dclk_core, *dclk_out; ++ char clk_name[32]; ++ int ret; ++ ++ if_clk_src = vop2_clk_get(vop2, clk_src_name); ++ snprintf(clk_name, sizeof(clk_name), "%s%d", clk_parent_name, vp->id); ++ if_clk_parent = vop2_clk_get(vop2, clk_name); ++ if_pixclk = vop2_clk_get(vop2, pixclk_name); ++ if_dclk = vop2_clk_get(vop2, dclk_name); ++ if (!if_pixclk || !if_clk_parent) { ++ DRM_DEV_ERROR(vop2->dev, "failed to get connector interface clk\n"); ++ return -ENODEV; ++ } ++ ++ ret = clk_set_parent(if_clk_src->hw.clk, if_clk_parent->hw.clk); ++ if (ret < 0) { ++ DRM_DEV_ERROR(vop2->dev, "failed to set parent(%s) for %s: %d\n", ++ __clk_get_name(if_clk_parent->hw.clk), ++ __clk_get_name(if_clk_src->hw.clk), ret); ++ return ret; ++ } ++ ++ if (output_mode == ROCKCHIP_OUT_MODE_YUV420) ++ K = 2; ++ ++ if_pixclk->rate = (dclk_core_rate << 1) / K; ++ if_dclk->rate = dclk_core_rate / K; ++ ++ snprintf(clk_name, sizeof(clk_name), "dclk_core%d", vp->id); ++ dclk_core = vop2_clk_get(vop2, clk_name); ++ ++ snprintf(clk_name, sizeof(clk_name), "dclk_out%d", vp->id); ++ dclk_out = vop2_clk_get(vop2, clk_name); ++ ++ snprintf(clk_name, sizeof(clk_name), "dclk%d", vp->id); ++ dclk = vop2_clk_get(vop2, clk_name); ++ if (v_pixclk <= (VOP2_MAX_DCLK_RATE * 1000)) { ++ if (output_mode == ROCKCHIP_OUT_MODE_YUV420) ++ v_pixclk = v_pixclk >> 1; ++ } else { ++ v_pixclk = v_pixclk >> 2; ++ } ++ clk_set_rate(dclk->hw.clk, v_pixclk); ++ ++ if (dclk_core_rate > if_pixclk->rate) { ++ clk_set_rate(dclk_core->hw.clk, dclk_core_rate); ++ ret = vop2_cru_set_rate(if_pixclk, if_dclk); ++ } else { ++ ret = vop2_cru_set_rate(if_pixclk, if_dclk); ++ clk_set_rate(dclk_core->hw.clk, dclk_core_rate); ++ } ++ ++ *dclk_core_div = dclk_core->div_val; ++ *dclk_out_div = dclk_out->div_val; ++ *if_pixclk_div = if_pixclk->div_val; ++ *if_dclk_div = if_dclk->div_val; ++ ++ return dclk->rate; ++ } ++ + /* + * K = 2: dclk_core = if_pixclk_rate > if_dclk_rate + * K = 1: dclk_core = hdmie_edp_dclk > if_pixclk_rate +@@ -1870,6 +1949,22 @@ static int us_to_vertical_line(struct drm_display_mode *mode, int us) + return us * mode->clock / mode->htotal / 1000; + } + ++// [CC:] rework virtual clock ++static struct vop2_clk *vop2_clk_get(struct vop2 *vop2, const char *name) ++{ ++ struct vop2_clk *clk, *n; ++ ++ if (!name) ++ return NULL; ++ ++ list_for_each_entry_safe(clk, n, &vop2->clk_list_head, list) { ++ if (!strcmp(clk_hw_get_name(&clk->hw), name)) ++ return clk; ++ } ++ ++ return NULL; ++} ++ + static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state) + { +@@ -1897,6 +1992,8 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, + u32 val, polflags, invpolflags; + int ret; + struct drm_encoder *encoder; ++ char clk_name[32]; ++ struct vop2_clk *dclk; + + drm_dbg(vop2->drm, "Update mode to %dx%d%s%d, type: %d for vp%d\n", + hdisplay, vdisplay, mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "p", +@@ -1940,8 +2037,6 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, + + drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) { + struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder); +- +- // rk3568_set_intf_mux(vp, rkencoder->crtc_endpoint_id, polflags, invpolflags); + vop2_set_intf_mux(vp, rkencoder->crtc_endpoint_id, polflags); + } + +@@ -1993,13 +2088,44 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, + + vop2_vp_write(vp, RK3568_VP_DSP_VTOTAL_VS_END, vtotal << 16 | vsync_len); + +- if (mode->flags & DRM_MODE_FLAG_DBLCLK) { +- dsp_ctrl |= RK3568_VP_DSP_CTRL__CORE_DCLK_DIV; +- clock *= 2; ++ if (vop2->data->soc_id == 3568) { ++ if (mode->flags & DRM_MODE_FLAG_DBLCLK) { ++ dsp_ctrl |= RK3568_VP_DSP_CTRL__CORE_DCLK_DIV; ++ // [CC:] done via mode_fixup ++ // clock *= 2; ++ } + } + + vop2_vp_write(vp, RK3568_VP_MIPI_CTRL, 0); + ++ snprintf(clk_name, sizeof(clk_name), "dclk%d", vp->id); ++ dclk = vop2_clk_get(vop2, clk_name); ++ if (dclk) { ++ /* ++ * use HDMI_PHY_PLL as dclk source under 4K@60 if it is available, ++ * otherwise use system cru as dclk source. ++ */ ++ drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) { ++ struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder); ++ ++ // [CC:] Using PHY PLL to handle all display modes ++ if (rkencoder->crtc_endpoint_id == ROCKCHIP_VOP2_EP_HDMI0) { ++ clk_get_rate(vop2->hdmi0_phy_pll); ++ ++ if (mode->crtc_clock > VOP2_MAX_DCLK_RATE) ++ ret = clk_set_parent(vp->dclk, vp->dclk_parent); ++ else ++ ret = clk_set_parent(vp->dclk, vop2->hdmi0_phy_pll); ++ ++ if (ret < 0) ++ DRM_WARN("failed to set clock parent for %s\n", ++ __clk_get_name(vp->dclk)); ++ ++ clock = dclk->rate; ++ } ++ } ++ } ++ + clk_set_rate(vp->dclk, clock); + + vop2_post_config(crtc); +@@ -2022,6 +2148,59 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, + vop2_unlock(vop2); + } + ++static void vop2_crtc_atomic_disable(struct drm_crtc *crtc, ++ struct drm_atomic_state *state) ++{ ++ struct vop2_video_port *vp = to_vop2_video_port(crtc); ++ struct vop2 *vop2 = vp->vop2; ++ struct drm_crtc_state *old_crtc_state; ++ int ret; ++ ++ vop2_lock(vop2); ++ ++ old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc); ++ drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false); ++ ++ drm_crtc_vblank_off(crtc); ++ ++ /* ++ * Vop standby will take effect at end of current frame, ++ * if dsp hold valid irq happen, it means standby complete. ++ * ++ * we must wait standby complete when we want to disable aclk, ++ * if not, memory bus maybe dead. ++ */ ++ reinit_completion(&vp->dsp_hold_completion); ++ ++ vop2_crtc_enable_irq(vp, VP_INT_DSP_HOLD_VALID); ++ ++ vop2_vp_write(vp, RK3568_VP_DSP_CTRL, RK3568_VP_DSP_CTRL__STANDBY); ++ ++ ret = wait_for_completion_timeout(&vp->dsp_hold_completion, ++ msecs_to_jiffies(50)); ++ if (!ret) ++ drm_info(vop2->drm, "wait for vp%d dsp_hold timeout\n", vp->id); ++ ++ vop2_crtc_disable_irq(vp, VP_INT_DSP_HOLD_VALID); ++ ++ clk_disable_unprepare(vp->dclk); ++ ++ vop2->enable_count--; ++ ++ if (!vop2->enable_count) ++ vop2_disable(vop2); ++ ++ vop2_unlock(vop2); ++ ++ if (crtc->state->event && !crtc->state->active) { ++ spin_lock_irq(&crtc->dev->event_lock); ++ drm_crtc_send_vblank_event(crtc, crtc->state->event); ++ spin_unlock_irq(&crtc->dev->event_lock); ++ ++ crtc->state->event = NULL; ++ } ++} ++ + static int vop2_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_atomic_state *state) + { +@@ -2455,7 +2634,43 @@ static void vop2_crtc_atomic_flush(struct drm_crtc *crtc, + spin_unlock_irq(&crtc->dev->event_lock); + } + ++static enum drm_mode_status ++vop2_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode) ++{ ++ struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); ++ struct vop2_video_port *vp = to_vop2_video_port(crtc); ++ struct vop2 *vop2 = vp->vop2; ++ const struct vop2_data *vop2_data = vop2->data; ++ const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id]; ++ int request_clock = mode->clock; ++ int clock; ++ ++ if (mode->hdisplay > vp_data->max_output.width) ++ return MODE_BAD_HVALUE; ++ ++ if (mode->flags & DRM_MODE_FLAG_DBLCLK) ++ request_clock *= 2; ++ ++ if (request_clock <= VOP2_MAX_DCLK_RATE) { ++ clock = request_clock; ++ } else { ++ request_clock = request_clock >> 2; ++ clock = clk_round_rate(vp->dclk, request_clock * 1000) / 1000; ++ } ++ ++ /* ++ * Hdmi or DisplayPort request a Accurate clock. ++ */ ++ if (vcstate->output_type == DRM_MODE_CONNECTOR_HDMIA || ++ vcstate->output_type == DRM_MODE_CONNECTOR_DisplayPort) ++ if (clock != request_clock) ++ return MODE_CLOCK_RANGE; ++ ++ return MODE_OK; ++} ++ + static const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = { ++ .mode_valid = vop2_crtc_mode_valid, + .mode_fixup = vop2_crtc_mode_fixup, + .atomic_check = vop2_crtc_atomic_check, + .atomic_begin = vop2_crtc_atomic_begin, +@@ -3038,6 +3253,336 @@ static const struct regmap_config vop2_regmap_config = { + .cache_type = REGCACHE_MAPLE, + }; + ++/* ++ * BEGIN virtual clock ++ */ ++#define PLL_RATE_MIN 30000000 ++ ++#define cru_dbg(format, ...) do { \ ++ if (cru_debug) \ ++ pr_info("%s: " format, __func__, ## __VA_ARGS__); \ ++ } while (0) ++ ++#define PNAME(x) static const char *const x[] ++ ++enum vop_clk_branch_type { ++ branch_mux, ++ branch_divider, ++ branch_factor, ++ branch_virtual, ++}; ++ ++#define VIR(cname) \ ++ { \ ++ .branch_type = branch_virtual, \ ++ .name = cname, \ ++ } ++ ++ ++#define MUX(cname, pnames, f) \ ++ { \ ++ .branch_type = branch_mux, \ ++ .name = cname, \ ++ .parent_names = pnames, \ ++ .num_parents = ARRAY_SIZE(pnames), \ ++ .flags = f, \ ++ } ++ ++#define FACTOR(cname, pname, f) \ ++ { \ ++ .branch_type = branch_factor, \ ++ .name = cname, \ ++ .parent_names = (const char *[]){ pname }, \ ++ .num_parents = 1, \ ++ .flags = f, \ ++ } ++ ++#define DIV(cname, pname, f, w) \ ++ { \ ++ .branch_type = branch_divider, \ ++ .name = cname, \ ++ .parent_names = (const char *[]){ pname }, \ ++ .num_parents = 1, \ ++ .flags = f, \ ++ .div_width = w, \ ++ } ++ ++struct vop2_clk_branch { ++ enum vop_clk_branch_type branch_type; ++ const char *name; ++ const char *const *parent_names; ++ u8 num_parents; ++ unsigned long flags; ++ u8 div_shift; ++ u8 div_width; ++ u8 div_flags; ++}; ++ ++PNAME(mux_port0_dclk_src_p) = { "dclk0", "dclk1" }; ++PNAME(mux_port2_dclk_src_p) = { "dclk2", "dclk1" }; ++PNAME(mux_dp_pixclk_p) = { "dclk_out0", "dclk_out1", "dclk_out2" }; ++PNAME(mux_hdmi_edp_clk_src_p) = { "dclk0", "dclk1", "dclk2" }; ++PNAME(mux_mipi_clk_src_p) = { "dclk_out1", "dclk_out2", "dclk_out3" }; ++PNAME(mux_dsc_8k_clk_src_p) = { "dclk0", "dclk1", "dclk2", "dclk3" }; ++PNAME(mux_dsc_4k_clk_src_p) = { "dclk0", "dclk1", "dclk2", "dclk3" }; ++ ++/* ++ * We only use this clk driver calculate the div ++ * of dclk_core/dclk_out/if_pixclk/if_dclk and ++ * the rate of the dclk from the soc. ++ * ++ * We don't touch the cru in the vop here, as ++ * these registers has special read andy write ++ * limits. ++ */ ++static struct vop2_clk_branch rk3588_vop_clk_branches[] = { ++ VIR("dclk0"), ++ VIR("dclk1"), ++ VIR("dclk2"), ++ VIR("dclk3"), ++ ++ MUX("port0_dclk_src", mux_port0_dclk_src_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT), ++ DIV("dclk_core0", "port0_dclk_src", CLK_SET_RATE_PARENT, 2), ++ DIV("dclk_out0", "port0_dclk_src", CLK_SET_RATE_PARENT, 2), ++ ++ FACTOR("port1_dclk_src", "dclk1", CLK_SET_RATE_PARENT), ++ DIV("dclk_core1", "port1_dclk_src", CLK_SET_RATE_PARENT, 2), ++ DIV("dclk_out1", "port1_dclk_src", CLK_SET_RATE_PARENT, 2), ++ ++ MUX("port2_dclk_src", mux_port2_dclk_src_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT), ++ DIV("dclk_core2", "port2_dclk_src", CLK_SET_RATE_PARENT, 2), ++ DIV("dclk_out2", "port2_dclk_src", CLK_SET_RATE_PARENT, 2), ++ ++ FACTOR("port3_dclk_src", "dclk3", CLK_SET_RATE_PARENT), ++ DIV("dclk_core3", "port3_dclk_src", CLK_SET_RATE_PARENT, 2), ++ DIV("dclk_out3", "port3_dclk_src", CLK_SET_RATE_PARENT, 2), ++ ++ MUX("dp0_pixclk", mux_dp_pixclk_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT), ++ MUX("dp1_pixclk", mux_dp_pixclk_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT), ++ ++ MUX("hdmi_edp0_clk_src", mux_hdmi_edp_clk_src_p, ++ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT), ++ DIV("hdmi_edp0_dclk", "hdmi_edp0_clk_src", 0, 2), ++ DIV("hdmi_edp0_pixclk", "hdmi_edp0_clk_src", CLK_SET_RATE_PARENT, 1), ++ ++ MUX("hdmi_edp1_clk_src", mux_hdmi_edp_clk_src_p, ++ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT), ++ DIV("hdmi_edp1_dclk", "hdmi_edp1_clk_src", 0, 2), ++ DIV("hdmi_edp1_pixclk", "hdmi_edp1_clk_src", CLK_SET_RATE_PARENT, 1), ++ ++ MUX("mipi0_clk_src", mux_mipi_clk_src_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT), ++ DIV("mipi0_pixclk", "mipi0_clk_src", CLK_SET_RATE_PARENT, 2), ++ ++ MUX("mipi1_clk_src", mux_mipi_clk_src_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT), ++ DIV("mipi1_pixclk", "mipi1_clk_src", CLK_SET_RATE_PARENT, 2), ++ ++ FACTOR("rgb_pixclk", "port3_dclk_src", CLK_SET_RATE_PARENT), ++ ++ MUX("dsc_8k_txp_clk_src", mux_dsc_8k_clk_src_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT), ++ DIV("dsc_8k_txp_clk", "dsc_8k_txp_clk_src", 0, 2), ++ DIV("dsc_8k_pxl_clk", "dsc_8k_txp_clk_src", 0, 2), ++ DIV("dsc_8k_cds_clk", "dsc_8k_txp_clk_src", 0, 2), ++ ++ MUX("dsc_4k_txp_clk_src", mux_dsc_4k_clk_src_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT), ++ DIV("dsc_4k_txp_clk", "dsc_4k_txp_clk_src", 0, 2), ++ DIV("dsc_4k_pxl_clk", "dsc_4k_txp_clk_src", 0, 2), ++ DIV("dsc_4k_cds_clk", "dsc_4k_txp_clk_src", 0, 2), ++}; ++ ++static unsigned long clk_virtual_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ struct vop2_clk *vop2_clk = to_vop2_clk(hw); ++ ++ return (unsigned long)vop2_clk->rate; ++} ++ ++static long clk_virtual_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *prate) ++{ ++ struct vop2_clk *vop2_clk = to_vop2_clk(hw); ++ ++ vop2_clk->rate = rate; ++ ++ return rate; ++} ++ ++static int clk_virtual_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ return 0; ++} ++ ++const struct clk_ops clk_virtual_ops = { ++ .round_rate = clk_virtual_round_rate, ++ .set_rate = clk_virtual_set_rate, ++ .recalc_rate = clk_virtual_recalc_rate, ++}; ++ ++static u8 vop2_mux_get_parent(struct clk_hw *hw) ++{ ++ struct vop2_clk *vop2_clk = to_vop2_clk(hw); ++ ++ // cru_dbg("%s index: %d\n", clk_hw_get_name(hw), vop2_clk->parent_index); ++ return vop2_clk->parent_index; ++} ++ ++static int vop2_mux_set_parent(struct clk_hw *hw, u8 index) ++{ ++ struct vop2_clk *vop2_clk = to_vop2_clk(hw); ++ ++ vop2_clk->parent_index = index; ++ ++ // cru_dbg("%s index: %d\n", clk_hw_get_name(hw), index); ++ return 0; ++} ++ ++static int vop2_clk_mux_determine_rate(struct clk_hw *hw, ++ struct clk_rate_request *req) ++{ ++ // cru_dbg("%s %ld(min: %ld max: %ld)\n", ++ // clk_hw_get_name(hw), req->rate, req->min_rate, req->max_rate); ++ return __clk_mux_determine_rate(hw, req); ++} ++ ++static const struct clk_ops vop2_mux_clk_ops = { ++ .get_parent = vop2_mux_get_parent, ++ .set_parent = vop2_mux_set_parent, ++ .determine_rate = vop2_clk_mux_determine_rate, ++}; ++ ++#define div_mask(width) ((1 << (width)) - 1) ++ ++static int vop2_div_get_val(unsigned long rate, unsigned long parent_rate) ++{ ++ unsigned int div, value; ++ ++ div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); ++ ++ value = ilog2(div); ++ ++ return value; ++} ++ ++static unsigned long vop2_clk_div_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ struct vop2_clk *vop2_clk = to_vop2_clk(hw); ++ unsigned long rate; ++ unsigned int div; ++ ++ div = 1 << vop2_clk->div_val; ++ rate = parent_rate / div; ++ ++ // cru_dbg("%s rate: %ld(prate: %ld)\n", clk_hw_get_name(hw), rate, parent_rate); ++ return rate; ++} ++ ++static long vop2_clk_div_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *prate) ++{ ++ struct vop2_clk *vop2_clk = to_vop2_clk(hw); ++ ++ if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) { ++ if (*prate < rate) ++ *prate = rate; ++ if ((*prate >> vop2_clk->div.width) > rate) ++ *prate = rate; ++ ++ if ((*prate % rate)) ++ *prate = rate; ++ ++ /* SOC PLL can't output a too low pll freq */ ++ if (*prate < PLL_RATE_MIN) ++ *prate = rate << vop2_clk->div.width; ++ } ++ ++ // cru_dbg("%s rate: %ld(prate: %ld)\n", clk_hw_get_name(hw), rate, *prate); ++ return rate; ++} ++ ++static int vop2_clk_div_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) ++{ ++ struct vop2_clk *vop2_clk = to_vop2_clk(hw); ++ int div_val; ++ ++ div_val = vop2_div_get_val(rate, parent_rate); ++ vop2_clk->div_val = div_val; ++ ++ // cru_dbg("%s prate: %ld rate: %ld div_val: %d\n", ++ // clk_hw_get_name(hw), parent_rate, rate, div_val); ++ return 0; ++} ++ ++static const struct clk_ops vop2_div_clk_ops = { ++ .recalc_rate = vop2_clk_div_recalc_rate, ++ .round_rate = vop2_clk_div_round_rate, ++ .set_rate = vop2_clk_div_set_rate, ++}; ++ ++static struct clk *vop2_clk_register(struct vop2 *vop2, struct vop2_clk_branch *branch) ++{ ++ struct clk_init_data init = {}; ++ struct vop2_clk *vop2_clk; ++ struct clk *clk; ++ ++ vop2_clk = devm_kzalloc(vop2->dev, sizeof(*vop2_clk), GFP_KERNEL); ++ if (!vop2_clk) ++ return ERR_PTR(-ENOMEM); ++ ++ vop2_clk->vop2 = vop2; ++ vop2_clk->hw.init = &init; ++ vop2_clk->div.shift = branch->div_shift; ++ vop2_clk->div.width = branch->div_width; ++ ++ init.name = branch->name; ++ init.flags = branch->flags; ++ init.num_parents = branch->num_parents; ++ init.parent_names = branch->parent_names; ++ if (branch->branch_type == branch_divider) { ++ init.ops = &vop2_div_clk_ops; ++ } else if (branch->branch_type == branch_virtual) { ++ init.ops = &clk_virtual_ops; ++ init.num_parents = 0; ++ init.parent_names = NULL; ++ } else { ++ init.ops = &vop2_mux_clk_ops; ++ } ++ ++ clk = devm_clk_register(vop2->dev, &vop2_clk->hw); ++ if (!IS_ERR(clk)) ++ list_add_tail(&vop2_clk->list, &vop2->clk_list_head); ++ else ++ DRM_DEV_ERROR(vop2->dev, "Register %s failed\n", branch->name); ++ ++ return clk; ++} ++ ++static int vop2_clk_init(struct vop2 *vop2) ++{ ++ struct vop2_clk_branch *branch = rk3588_vop_clk_branches; ++ unsigned int nr_clk = ARRAY_SIZE(rk3588_vop_clk_branches); ++ unsigned int idx; ++ struct vop2_clk *clk, *n; ++ ++ INIT_LIST_HEAD(&vop2->clk_list_head); ++ ++ if (vop2->data->soc_id < 3588) ++ return 0; ++ ++ list_for_each_entry_safe(clk, n, &vop2->clk_list_head, list) { ++ list_del(&clk->list); ++ } ++ ++ for (idx = 0; idx < nr_clk; idx++, branch++) ++ vop2_clk_register(vop2, branch); ++ ++ return 0; ++} ++/* ++ * END virtual clock ++ */ ++ + static int vop2_bind(struct device *dev, struct device *master, void *data) + { + struct platform_device *pdev = to_platform_device(dev); +@@ -3115,6 +3660,12 @@ static int vop2_bind(struct device *dev, struct device *master, void *data) + return PTR_ERR(vop2->pclk); + } + ++ vop2->hdmi0_phy_pll = devm_clk_get_optional(vop2->drm->dev, "hdmi0_phy_pll"); ++ if (IS_ERR(vop2->hdmi0_phy_pll)) { ++ DRM_DEV_ERROR(vop2->dev, "failed to get hdmi0_phy_pll source\n"); ++ return PTR_ERR(vop2->hdmi0_phy_pll); ++ } ++ + // [CC:] drop ahb_rst & axi_rst + vop2->ahb_rst = devm_reset_control_get_optional(vop2->dev, "ahb"); + if (IS_ERR(vop2->ahb_rst)) { +@@ -3144,6 +3695,9 @@ static int vop2_bind(struct device *dev, struct device *master, void *data) + if (ret) + return ret; + ++ // [CC:] rework virtual clock ++ vop2_clk_init(vop2); ++ + ret = vop2_find_rgb_encoder(vop2); + if (ret >= 0) { + vop2->rgb = rockchip_rgb_init(dev, &vop2->vps[ret].crtc, +-- +2.42.1 + + +From 3b1edb31f3ac2f55d62968f2fd7d9b5d430cd3ec Mon Sep 17 00:00:00 2001 +From: Cristian Ciocaltea +Date: Fri, 3 Nov 2023 20:05:05 +0200 +Subject: [PATCH 7/9] arm64: dts: rockchip: Enable HDMI0 PHY PLL on RK3588 + +This is necessary for proper handling of various display modes. + +Signed-off-by: Cristian Ciocaltea +--- + arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts +index 39df9bfbbc5b..dacf6a4d8625 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts +@@ -193,6 +193,11 @@ &cpu_l3 { + mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + ++&display_subsystem { ++ clocks = <&hdptxphy_hdmi_clk0>; ++ clock-names = "hdmi0_phy_pll"; ++}; ++ + &i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0m2_xfer>; +@@ -933,6 +938,10 @@ &hdptxphy_hdmi0 { + status = "okay"; + }; + ++&hdptxphy_hdmi_clk0 { ++ status = "okay"; ++}; ++ + &vop_mmu { + status = "okay"; + }; +-- +2.42.1 + + +From 3e33bc702048b524d4faaa7d8f151bfe4a48fd2b Mon Sep 17 00:00:00 2001 +From: Sebastian Reichel +Date: Mon, 6 Nov 2023 17:34:54 +0100 +Subject: [PATCH 8/9] arm64: dts: rockchip: add HDMI support to rk3588-evb1 + +Enable all bits required for HDMI output on Rockchip RK3588 EVB1. + +Signed-off-by: Sebastian Reichel +--- + .../boot/dts/rockchip/rk3588-evb1-v10.dts | 29 +++++++++++++++++++ + 1 file changed, 29 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts +index c0ea050371f9..2ef5c98c4168 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts +@@ -300,6 +300,11 @@ &cpu_l3 { + mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + ++&display_subsystem { ++ clocks = <&hdptxphy_hdmi_clk0>; ++ clock-names = "hdmi0_phy_pll"; ++}; ++ + &gmac0 { + clock_in_out = "output"; + phy-handle = <&rgmii_phy>; +@@ -1263,3 +1268,27 @@ dwc3_0_role_switch: endpoint@0 { + &usb_host1_xhci { + status = "okay"; + }; ++ ++&hdmi0 { ++ status = "okay"; ++}; ++ ++&hdmi0_in_vp0 { ++ status = "okay"; ++}; ++ ++&hdptxphy_hdmi0 { ++ status = "okay"; ++}; ++ ++&hdptxphy_hdmi_clk0 { ++ status = "okay"; ++}; ++ ++&vop_mmu { ++ status = "okay"; ++}; ++ ++&vop { ++ status = "okay"; ++}; +-- +2.42.1 + + +From f41b3e9a9d7f22bef0735b4fe0007321ce6b6d6b Mon Sep 17 00:00:00 2001 +From: Cristian Ciocaltea +Date: Fri, 10 Nov 2023 00:40:54 +0200 +Subject: [PATCH 9/9] arm64: defconfig: Enable Rockchip Samsung HDMI/DP Combo + PHY + +Enable support for the Rockchip HDMI/DP Combo PHY based on a Samsung IP +block. This is used by the RK3588 SoC family. + +Signed-off-by: Cristian Ciocaltea +--- + arch/arm64/configs/defconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig +index b1109c48fe27..906f2f21ac05 100644 +--- a/arch/arm64/configs/defconfig ++++ b/arch/arm64/configs/defconfig +@@ -1451,6 +1451,7 @@ CONFIG_PHY_ROCKCHIP_INNO_USB2=y + CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY=m + CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY=y + CONFIG_PHY_ROCKCHIP_PCIE=m ++CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX_HDMI=m + CONFIG_PHY_ROCKCHIP_SNPS_PCIE3=y + CONFIG_PHY_ROCKCHIP_TYPEC=y + CONFIG_PHY_SAMSUNG_UFS=y +-- +2.42.1 + diff --git a/patch/kernel/rockchip-rk3588-edge/0024-RK3588-Add-Crypto-Support.patch b/patch/kernel/rockchip-rk3588-edge/0024-RK3588-Add-Crypto-Support.patch new file mode 100644 index 0000000000..6e164596e3 --- /dev/null +++ b/patch/kernel/rockchip-rk3588-edge/0024-RK3588-Add-Crypto-Support.patch @@ -0,0 +1,2365 @@ +From 9877897c64b2b1666188878f1b27ec17341032af Mon Sep 17 00:00:00 2001 +From: Corentin Labbe +Date: Tue, 7 Nov 2023 15:55:27 +0000 +Subject: [PATCH 1/6] dt-bindings: crypto: add support for + rockchip,crypto-rk3588 + +Add device tree binding documentation for the Rockchip cryptographic +offloader V2. + +Signed-off-by: Corentin Labbe +--- + .../crypto/rockchip,rk3588-crypto.yaml | 65 +++++++++++++++++++ + 1 file changed, 65 insertions(+) + create mode 100644 Documentation/devicetree/bindings/crypto/rockchip,rk3588-crypto.yaml + +diff --git a/Documentation/devicetree/bindings/crypto/rockchip,rk3588-crypto.yaml b/Documentation/devicetree/bindings/crypto/rockchip,rk3588-crypto.yaml +new file mode 100644 +index 000000000000..c01963413260 +--- /dev/null ++++ b/Documentation/devicetree/bindings/crypto/rockchip,rk3588-crypto.yaml +@@ -0,0 +1,65 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/crypto/rockchip,rk3588-crypto.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Rockchip cryptographic offloader V2 ++ ++maintainers: ++ - Corentin Labbe ++ ++properties: ++ compatible: ++ enum: ++ - rockchip,rk3568-crypto ++ - rockchip,rk3588-crypto ++ ++ reg: ++ maxItems: 1 ++ ++ interrupts: ++ maxItems: 1 ++ ++ clocks: ++ minItems: 3 ++ ++ clock-names: ++ items: ++ - const: core ++ - const: a ++ - const: h ++ ++ resets: ++ minItems: 1 ++ ++ reset-names: ++ items: ++ - const: core ++ ++required: ++ - compatible ++ - reg ++ - interrupts ++ - clocks ++ - clock-names ++ - resets ++ - reset-names ++ ++additionalProperties: false ++ ++examples: ++ - | ++ #include ++ #include ++ #include ++ crypto@fe370000 { ++ compatible = "rockchip,rk3588-crypto"; ++ reg = <0xfe370000 0x4000>; ++ interrupts = ; ++ clocks = <&scmi_clk SCMI_CRYPTO_CORE>, <&scmi_clk SCMI_ACLK_SECURE_NS>, ++ <&scmi_clk SCMI_HCLK_SECURE_NS>; ++ clock-names = "core", "a", "h"; ++ resets = <&scmi_reset SRST_CRYPTO_CORE>; ++ reset-names = "core"; ++ }; +-- +2.42.1 + + +From a44a7d8ca1eb437c8a1b28b09b564a56023a33ad Mon Sep 17 00:00:00 2001 +From: Corentin Labbe +Date: Tue, 7 Nov 2023 15:55:28 +0000 +Subject: [PATCH 2/6] MAINTAINERS: add new dt-binding doc to the right entry + +Rockchip crypto driver have a new file to be added. + +Signed-off-by: Corentin Labbe +--- + MAINTAINERS | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/MAINTAINERS b/MAINTAINERS +index 97f51d5ec1cf..0ae6a1709894 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -18701,6 +18701,7 @@ M: Corentin Labbe + L: linux-crypto@vger.kernel.org + S: Maintained + F: Documentation/devicetree/bindings/crypto/rockchip,rk3288-crypto.yaml ++F: Documentation/devicetree/bindings/crypto/rockchip,rk3588-crypto.yaml + F: drivers/crypto/rockchip/ + + ROCKCHIP I2S TDM DRIVER +-- +2.42.1 + + +From a5d149d6b0d4846582acf150493af5052d2b4c8c Mon Sep 17 00:00:00 2001 +From: Corentin Labbe +Date: Tue, 7 Nov 2023 15:55:29 +0000 +Subject: [PATCH 3/6] ARM64: dts: rk3588: add crypto node + +The rk3588 has a crypto IP handled by the rk3588 crypto driver so adds a +node for it. + +Signed-off-by: Corentin Labbe +--- + arch/arm64/boot/dts/rockchip/rk3588s.dtsi | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi +index 4481a2e578f8..a89577fb801e 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi +@@ -2238,6 +2238,18 @@ sdhci: mmc@fe2e0000 { + status = "disabled"; + }; + ++ crypto: crypto@fe370000 { ++ compatible = "rockchip,rk3588-crypto"; ++ reg = <0x0 0xfe370000 0x0 0x2000>; ++ interrupts = ; ++ clocks = <&scmi_clk SCMI_CRYPTO_CORE>, <&scmi_clk SCMI_ACLK_SECURE_NS>, ++ <&scmi_clk SCMI_HCLK_SECURE_NS>; ++ clock-names = "core", "aclk", "hclk"; ++ resets = <&scmi_reset SRST_CRYPTO_CORE>; ++ reset-names = "core"; ++ status = "okay"; ++ }; ++ + i2s0_8ch: i2s@fe470000 { + compatible = "rockchip,rk3588-i2s-tdm"; + reg = <0x0 0xfe470000 0x0 0x1000>; +-- +2.42.1 + + +From cde73c4a00f5e87c7ac4184a1480d4c5723f1ed7 Mon Sep 17 00:00:00 2001 +From: Corentin Labbe +Date: Tue, 7 Nov 2023 15:55:30 +0000 +Subject: [PATCH 4/6] ARM64: dts: rk356x: add crypto node + +Both RK3566 and RK3568 have a crypto IP handled by the rk3588 crypto driver so adds a +node for it. + +Tested-by: Ricardo Pardini +Signed-off-by: Corentin Labbe +--- + arch/arm64/boot/dts/rockchip/rk356x.dtsi | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk356x.dtsi b/arch/arm64/boot/dts/rockchip/rk356x.dtsi +index 0964761e3ce9..c94a1b535c32 100644 +--- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi +@@ -1070,6 +1070,18 @@ sdhci: mmc@fe310000 { + status = "disabled"; + }; + ++ crypto: crypto@fe380000 { ++ compatible = "rockchip,rk3568-crypto"; ++ reg = <0x0 0xfe380000 0x0 0x2000>; ++ interrupts = ; ++ clocks = <&cru ACLK_CRYPTO_NS>, <&cru HCLK_CRYPTO_NS>, ++ <&cru CLK_CRYPTO_NS_CORE>; ++ clock-names = "aclk", "hclk", "core"; ++ resets = <&cru SRST_CRYPTO_NS_CORE>; ++ reset-names = "core"; ++ status = "okay"; ++ }; ++ + i2s0_8ch: i2s@fe400000 { + compatible = "rockchip,rk3568-i2s-tdm"; + reg = <0x0 0xfe400000 0x0 0x1000>; +-- +2.42.1 + + +From 4ef835dc26a22a8ccfac65eaf53eb1e51ad55970 Mon Sep 17 00:00:00 2001 +From: Corentin Labbe +Date: Tue, 7 Nov 2023 15:55:31 +0000 +Subject: [PATCH 5/6] reset: rockchip: secure reset must be used by SCMI + +While working on the rk3588 crypto driver, I loose lot of time +understanding why resetting the IP failed. +This is due to RK3588_SECURECRU_RESET_OFFSET being in the secure world, +so impossible to operate on it from the kernel. +All resets in this block must be handled via SCMI call. + +Signed-off-by: Corentin Labbe +--- + drivers/clk/rockchip/rst-rk3588.c | 42 ------------ + .../dt-bindings/reset/rockchip,rk3588-cru.h | 68 +++++++++---------- + 2 files changed, 34 insertions(+), 76 deletions(-) + +diff --git a/drivers/clk/rockchip/rst-rk3588.c b/drivers/clk/rockchip/rst-rk3588.c +index e855bb8d5413..6556d9d3c7ab 100644 +--- a/drivers/clk/rockchip/rst-rk3588.c ++++ b/drivers/clk/rockchip/rst-rk3588.c +@@ -16,9 +16,6 @@ + /* 0xFD7C8000 + 0x0A00 */ + #define RK3588_PHPTOPCRU_RESET_OFFSET(id, reg, bit) [id] = (0x8000*4 + reg * 16 + bit) + +-/* 0xFD7D0000 + 0x0A00 */ +-#define RK3588_SECURECRU_RESET_OFFSET(id, reg, bit) [id] = (0x10000*4 + reg * 16 + bit) +- + /* 0xFD7F0000 + 0x0A00 */ + #define RK3588_PMU1CRU_RESET_OFFSET(id, reg, bit) [id] = (0x30000*4 + reg * 16 + bit) + +@@ -806,45 +803,6 @@ static const int rk3588_register_offset[] = { + RK3588_PMU1CRU_RESET_OFFSET(SRST_P_PMU0IOC, 5, 4), + RK3588_PMU1CRU_RESET_OFFSET(SRST_P_GPIO0, 5, 5), + RK3588_PMU1CRU_RESET_OFFSET(SRST_GPIO0, 5, 6), +- +- /* SECURECRU_SOFTRST_CON00 */ +- RK3588_SECURECRU_RESET_OFFSET(SRST_A_SECURE_NS_BIU, 0, 10), +- RK3588_SECURECRU_RESET_OFFSET(SRST_H_SECURE_NS_BIU, 0, 11), +- RK3588_SECURECRU_RESET_OFFSET(SRST_A_SECURE_S_BIU, 0, 12), +- RK3588_SECURECRU_RESET_OFFSET(SRST_H_SECURE_S_BIU, 0, 13), +- RK3588_SECURECRU_RESET_OFFSET(SRST_P_SECURE_S_BIU, 0, 14), +- RK3588_SECURECRU_RESET_OFFSET(SRST_CRYPTO_CORE, 0, 15), +- +- /* SECURECRU_SOFTRST_CON01 */ +- RK3588_SECURECRU_RESET_OFFSET(SRST_CRYPTO_PKA, 1, 0), +- RK3588_SECURECRU_RESET_OFFSET(SRST_CRYPTO_RNG, 1, 1), +- RK3588_SECURECRU_RESET_OFFSET(SRST_A_CRYPTO, 1, 2), +- RK3588_SECURECRU_RESET_OFFSET(SRST_H_CRYPTO, 1, 3), +- RK3588_SECURECRU_RESET_OFFSET(SRST_KEYLADDER_CORE, 1, 9), +- RK3588_SECURECRU_RESET_OFFSET(SRST_KEYLADDER_RNG, 1, 10), +- RK3588_SECURECRU_RESET_OFFSET(SRST_A_KEYLADDER, 1, 11), +- RK3588_SECURECRU_RESET_OFFSET(SRST_H_KEYLADDER, 1, 12), +- RK3588_SECURECRU_RESET_OFFSET(SRST_P_OTPC_S, 1, 13), +- RK3588_SECURECRU_RESET_OFFSET(SRST_OTPC_S, 1, 14), +- RK3588_SECURECRU_RESET_OFFSET(SRST_WDT_S, 1, 15), +- +- /* SECURECRU_SOFTRST_CON02 */ +- RK3588_SECURECRU_RESET_OFFSET(SRST_T_WDT_S, 2, 0), +- RK3588_SECURECRU_RESET_OFFSET(SRST_H_BOOTROM, 2, 1), +- RK3588_SECURECRU_RESET_OFFSET(SRST_A_DCF, 2, 2), +- RK3588_SECURECRU_RESET_OFFSET(SRST_P_DCF, 2, 3), +- RK3588_SECURECRU_RESET_OFFSET(SRST_H_BOOTROM_NS, 2, 5), +- RK3588_SECURECRU_RESET_OFFSET(SRST_P_KEYLADDER, 2, 14), +- RK3588_SECURECRU_RESET_OFFSET(SRST_H_TRNG_S, 2, 15), +- +- /* SECURECRU_SOFTRST_CON03 */ +- RK3588_SECURECRU_RESET_OFFSET(SRST_H_TRNG_NS, 3, 0), +- RK3588_SECURECRU_RESET_OFFSET(SRST_D_SDMMC_BUFFER, 3, 1), +- RK3588_SECURECRU_RESET_OFFSET(SRST_H_SDMMC, 3, 2), +- RK3588_SECURECRU_RESET_OFFSET(SRST_H_SDMMC_BUFFER, 3, 3), +- RK3588_SECURECRU_RESET_OFFSET(SRST_SDMMC, 3, 4), +- RK3588_SECURECRU_RESET_OFFSET(SRST_P_TRNG_CHK, 3, 5), +- RK3588_SECURECRU_RESET_OFFSET(SRST_TRNG_S, 3, 6), + }; + + void rk3588_rst_init(struct device_node *np, void __iomem *reg_base) +diff --git a/include/dt-bindings/reset/rockchip,rk3588-cru.h b/include/dt-bindings/reset/rockchip,rk3588-cru.h +index d4264db2a07f..c0d08ae78cd5 100644 +--- a/include/dt-bindings/reset/rockchip,rk3588-cru.h ++++ b/include/dt-bindings/reset/rockchip,rk3588-cru.h +@@ -716,39 +716,39 @@ + #define SRST_P_GPIO0 627 + #define SRST_GPIO0 628 + +-#define SRST_A_SECURE_NS_BIU 629 +-#define SRST_H_SECURE_NS_BIU 630 +-#define SRST_A_SECURE_S_BIU 631 +-#define SRST_H_SECURE_S_BIU 632 +-#define SRST_P_SECURE_S_BIU 633 +-#define SRST_CRYPTO_CORE 634 +- +-#define SRST_CRYPTO_PKA 635 +-#define SRST_CRYPTO_RNG 636 +-#define SRST_A_CRYPTO 637 +-#define SRST_H_CRYPTO 638 +-#define SRST_KEYLADDER_CORE 639 +-#define SRST_KEYLADDER_RNG 640 +-#define SRST_A_KEYLADDER 641 +-#define SRST_H_KEYLADDER 642 +-#define SRST_P_OTPC_S 643 +-#define SRST_OTPC_S 644 +-#define SRST_WDT_S 645 +- +-#define SRST_T_WDT_S 646 +-#define SRST_H_BOOTROM 647 +-#define SRST_A_DCF 648 +-#define SRST_P_DCF 649 +-#define SRST_H_BOOTROM_NS 650 +-#define SRST_P_KEYLADDER 651 +-#define SRST_H_TRNG_S 652 +- +-#define SRST_H_TRNG_NS 653 +-#define SRST_D_SDMMC_BUFFER 654 +-#define SRST_H_SDMMC 655 +-#define SRST_H_SDMMC_BUFFER 656 +-#define SRST_SDMMC 657 +-#define SRST_P_TRNG_CHK 658 +-#define SRST_TRNG_S 659 ++#define SRST_A_SECURE_NS_BIU 10 ++#define SRST_H_SECURE_NS_BIU 11 ++#define SRST_A_SECURE_S_BIU 12 ++#define SRST_H_SECURE_S_BIU 13 ++#define SRST_P_SECURE_S_BIU 14 ++#define SRST_CRYPTO_CORE 15 ++ ++#define SRST_CRYPTO_PKA 16 ++#define SRST_CRYPTO_RNG 17 ++#define SRST_A_CRYPTO 18 ++#define SRST_H_CRYPTO 19 ++#define SRST_KEYLADDER_CORE 25 ++#define SRST_KEYLADDER_RNG 26 ++#define SRST_A_KEYLADDER 27 ++#define SRST_H_KEYLADDER 28 ++#define SRST_P_OTPC_S 29 ++#define SRST_OTPC_S 30 ++#define SRST_WDT_S 31 ++ ++#define SRST_T_WDT_S 32 ++#define SRST_H_BOOTROM 33 ++#define SRST_A_DCF 34 ++#define SRST_P_DCF 35 ++#define SRST_H_BOOTROM_NS 37 ++#define SRST_P_KEYLADDER 46 ++#define SRST_H_TRNG_S 47 ++ ++#define SRST_H_TRNG_NS 48 ++#define SRST_D_SDMMC_BUFFER 49 ++#define SRST_H_SDMMC 50 ++#define SRST_H_SDMMC_BUFFER 51 ++#define SRST_SDMMC 52 ++#define SRST_P_TRNG_CHK 53 ++#define SRST_TRNG_S 54 + + #endif +-- +2.42.1 + + +From 00acde5de0c9014184c573c015bbc2d7655003e6 Mon Sep 17 00:00:00 2001 +From: Corentin Labbe +Date: Tue, 7 Nov 2023 15:55:32 +0000 +Subject: [PATCH 6/6] crypto: rockchip: add rk3588 driver + +RK3588 have a new crypto IP, this patch adds basic support for it. +Only hashes and cipher are handled for the moment. + +Signed-off-by: Corentin Labbe +--- + drivers/crypto/Kconfig | 29 + + drivers/crypto/rockchip/Makefile | 5 + + drivers/crypto/rockchip/rk2_crypto.c | 739 ++++++++++++++++++ + drivers/crypto/rockchip/rk2_crypto.h | 246 ++++++ + drivers/crypto/rockchip/rk2_crypto_ahash.c | 344 ++++++++ + drivers/crypto/rockchip/rk2_crypto_skcipher.c | 576 ++++++++++++++ + 6 files changed, 1939 insertions(+) + create mode 100644 drivers/crypto/rockchip/rk2_crypto.c + create mode 100644 drivers/crypto/rockchip/rk2_crypto.h + create mode 100644 drivers/crypto/rockchip/rk2_crypto_ahash.c + create mode 100644 drivers/crypto/rockchip/rk2_crypto_skcipher.c + +diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig +index 79c3bb9c99c3..b6a2027b1f9a 100644 +--- a/drivers/crypto/Kconfig ++++ b/drivers/crypto/Kconfig +@@ -660,6 +660,35 @@ config CRYPTO_DEV_ROCKCHIP_DEBUG + the number of requests per algorithm and other internal stats. + + ++config CRYPTO_DEV_ROCKCHIP2 ++ tristate "Rockchip's cryptographic offloader V2" ++ depends on OF && ARCH_ROCKCHIP ++ depends on PM ++ select CRYPTO_ECB ++ select CRYPTO_CBC ++ select CRYPTO_AES ++ select CRYPTO_MD5 ++ select CRYPTO_SHA1 ++ select CRYPTO_SHA256 ++ select CRYPTO_SHA512 ++ select CRYPTO_SM3_GENERIC ++ select CRYPTO_HASH ++ select CRYPTO_SKCIPHER ++ select CRYPTO_ENGINE ++ ++ help ++ This driver interfaces with the hardware crypto offloader present ++ on RK3566, RK3568 and RK3588. ++ ++config CRYPTO_DEV_ROCKCHIP2_DEBUG ++ bool "Enable Rockchip V2 crypto stats" ++ depends on CRYPTO_DEV_ROCKCHIP2 ++ depends on DEBUG_FS ++ help ++ Say y to enable Rockchip crypto debug stats. ++ This will create /sys/kernel/debug/rk3588_crypto/stats for displaying ++ the number of requests per algorithm and other internal stats. ++ + config CRYPTO_DEV_ZYNQMP_AES + tristate "Support for Xilinx ZynqMP AES hw accelerator" + depends on ZYNQMP_FIRMWARE || COMPILE_TEST +diff --git a/drivers/crypto/rockchip/Makefile b/drivers/crypto/rockchip/Makefile +index 785277aca71e..452a12ff6538 100644 +--- a/drivers/crypto/rockchip/Makefile ++++ b/drivers/crypto/rockchip/Makefile +@@ -3,3 +3,8 @@ obj-$(CONFIG_CRYPTO_DEV_ROCKCHIP) += rk_crypto.o + rk_crypto-objs := rk3288_crypto.o \ + rk3288_crypto_skcipher.o \ + rk3288_crypto_ahash.o ++ ++obj-$(CONFIG_CRYPTO_DEV_ROCKCHIP2) += rk_crypto2.o ++rk_crypto2-objs := rk2_crypto.o \ ++ rk2_crypto_skcipher.o \ ++ rk2_crypto_ahash.o +diff --git a/drivers/crypto/rockchip/rk2_crypto.c b/drivers/crypto/rockchip/rk2_crypto.c +new file mode 100644 +index 000000000000..79ed697d8ec5 +--- /dev/null ++++ b/drivers/crypto/rockchip/rk2_crypto.c +@@ -0,0 +1,739 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * hardware cryptographic offloader for RK3568/RK3588 SoC ++ * ++ * Copyright (c) 2022-2023, Corentin Labbe ++ */ ++ ++#include "rk2_crypto.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static struct rockchip_ip rocklist = { ++ .dev_list = LIST_HEAD_INIT(rocklist.dev_list), ++ .lock = __SPIN_LOCK_UNLOCKED(rocklist.lock), ++}; ++ ++struct rk2_crypto_dev *get_rk2_crypto(void) ++{ ++ struct rk2_crypto_dev *first; ++ ++ spin_lock(&rocklist.lock); ++ first = list_first_entry_or_null(&rocklist.dev_list, ++ struct rk2_crypto_dev, list); ++ list_rotate_left(&rocklist.dev_list); ++ spin_unlock(&rocklist.lock); ++ return first; ++} ++ ++static const struct rk2_variant rk3568_variant = { ++ .num_clks = 3, ++}; ++ ++static const struct rk2_variant rk3588_variant = { ++ .num_clks = 3, ++}; ++ ++static int rk2_crypto_get_clks(struct rk2_crypto_dev *dev) ++{ ++ int i, j, err; ++ unsigned long cr; ++ ++ dev->num_clks = devm_clk_bulk_get_all(dev->dev, &dev->clks); ++ if (dev->num_clks < dev->variant->num_clks) { ++ dev_err(dev->dev, "Missing clocks, got %d instead of %d\n", ++ dev->num_clks, dev->variant->num_clks); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < dev->num_clks; i++) { ++ cr = clk_get_rate(dev->clks[i].clk); ++ for (j = 0; j < ARRAY_SIZE(dev->variant->rkclks); j++) { ++ if (dev->variant->rkclks[j].max == 0) ++ continue; ++ if (strcmp(dev->variant->rkclks[j].name, dev->clks[i].id)) ++ continue; ++ if (cr > dev->variant->rkclks[j].max) { ++ err = clk_set_rate(dev->clks[i].clk, ++ dev->variant->rkclks[j].max); ++ if (err) ++ dev_err(dev->dev, "Fail downclocking %s from %lu to %lu\n", ++ dev->variant->rkclks[j].name, cr, ++ dev->variant->rkclks[j].max); ++ else ++ dev_info(dev->dev, "Downclocking %s from %lu to %lu\n", ++ dev->variant->rkclks[j].name, cr, ++ dev->variant->rkclks[j].max); ++ } ++ } ++ } ++ return 0; ++} ++ ++static int rk2_crypto_enable_clk(struct rk2_crypto_dev *dev) ++{ ++ int err; ++ ++ err = clk_bulk_prepare_enable(dev->num_clks, dev->clks); ++ if (err) ++ dev_err(dev->dev, "Could not enable clock clks\n"); ++ ++ return err; ++} ++ ++static void rk2_crypto_disable_clk(struct rk2_crypto_dev *dev) ++{ ++ clk_bulk_disable_unprepare(dev->num_clks, dev->clks); ++} ++ ++/* ++ * Power management strategy: The device is suspended until a request ++ * is handled. For avoiding suspend/resume yoyo, the autosuspend is set to 2s. ++ */ ++static int rk2_crypto_pm_suspend(struct device *dev) ++{ ++ struct rk2_crypto_dev *rkdev = dev_get_drvdata(dev); ++ ++ rk2_crypto_disable_clk(rkdev); ++ reset_control_assert(rkdev->rst); ++ ++ return 0; ++} ++ ++static int rk2_crypto_pm_resume(struct device *dev) ++{ ++ struct rk2_crypto_dev *rkdev = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = rk2_crypto_enable_clk(rkdev); ++ if (ret) ++ return ret; ++ ++ reset_control_deassert(rkdev->rst); ++ return 0; ++} ++ ++static const struct dev_pm_ops rk2_crypto_pm_ops = { ++ SET_RUNTIME_PM_OPS(rk2_crypto_pm_suspend, rk2_crypto_pm_resume, NULL) ++}; ++ ++static int rk2_crypto_pm_init(struct rk2_crypto_dev *rkdev) ++{ ++ int err; ++ ++ pm_runtime_use_autosuspend(rkdev->dev); ++ pm_runtime_set_autosuspend_delay(rkdev->dev, 2000); ++ ++ err = pm_runtime_set_suspended(rkdev->dev); ++ if (err) ++ return err; ++ pm_runtime_enable(rkdev->dev); ++ return err; ++} ++ ++static void rk2_crypto_pm_exit(struct rk2_crypto_dev *rkdev) ++{ ++ pm_runtime_disable(rkdev->dev); ++} ++ ++static irqreturn_t rk2_crypto_irq_handle(int irq, void *dev_id) ++{ ++ struct rk2_crypto_dev *rkc = platform_get_drvdata(dev_id); ++ u32 v; ++ ++ v = readl(rkc->reg + RK2_CRYPTO_DMA_INT_ST); ++ writel(v, rkc->reg + RK2_CRYPTO_DMA_INT_ST); ++ ++ rkc->status = 1; ++ if (v & 0xF8) { ++ dev_warn(rkc->dev, "DMA Error\n"); ++ rkc->status = 0; ++ } ++ complete(&rkc->complete); ++ ++ return IRQ_HANDLED; ++} ++ ++static struct rk2_crypto_template rk2_crypto_algs[] = { ++ { ++ .type = CRYPTO_ALG_TYPE_SKCIPHER, ++ .rk2_mode = RK2_CRYPTO_AES_ECB, ++ .alg.skcipher.base = { ++ .base.cra_name = "ecb(aes)", ++ .base.cra_driver_name = "ecb-aes-rk2", ++ .base.cra_priority = 300, ++ .base.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK, ++ .base.cra_blocksize = AES_BLOCK_SIZE, ++ .base.cra_ctxsize = sizeof(struct rk2_cipher_ctx), ++ .base.cra_alignmask = 0x0f, ++ .base.cra_module = THIS_MODULE, ++ ++ .init = rk2_cipher_tfm_init, ++ .exit = rk2_cipher_tfm_exit, ++ .min_keysize = AES_MIN_KEY_SIZE, ++ .max_keysize = AES_MAX_KEY_SIZE, ++ .setkey = rk2_aes_setkey, ++ .encrypt = rk2_skcipher_encrypt, ++ .decrypt = rk2_skcipher_decrypt, ++ }, ++ .alg.skcipher.op = { ++ .do_one_request = rk2_cipher_run, ++ }, ++ }, ++ { ++ .type = CRYPTO_ALG_TYPE_SKCIPHER, ++ .rk2_mode = RK2_CRYPTO_AES_CBC, ++ .alg.skcipher.base = { ++ .base.cra_name = "cbc(aes)", ++ .base.cra_driver_name = "cbc-aes-rk2", ++ .base.cra_priority = 300, ++ .base.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK, ++ .base.cra_blocksize = AES_BLOCK_SIZE, ++ .base.cra_ctxsize = sizeof(struct rk2_cipher_ctx), ++ .base.cra_alignmask = 0x0f, ++ .base.cra_module = THIS_MODULE, ++ ++ .init = rk2_cipher_tfm_init, ++ .exit = rk2_cipher_tfm_exit, ++ .min_keysize = AES_MIN_KEY_SIZE, ++ .max_keysize = AES_MAX_KEY_SIZE, ++ .ivsize = AES_BLOCK_SIZE, ++ .setkey = rk2_aes_setkey, ++ .encrypt = rk2_skcipher_encrypt, ++ .decrypt = rk2_skcipher_decrypt, ++ }, ++ .alg.skcipher.op = { ++ .do_one_request = rk2_cipher_run, ++ }, ++ }, ++ { ++ .type = CRYPTO_ALG_TYPE_SKCIPHER, ++ .rk2_mode = RK2_CRYPTO_AES_XTS, ++ .is_xts = true, ++ .alg.skcipher.base = { ++ .base.cra_name = "xts(aes)", ++ .base.cra_driver_name = "xts-aes-rk2", ++ .base.cra_priority = 300, ++ .base.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK, ++ .base.cra_blocksize = AES_BLOCK_SIZE, ++ .base.cra_ctxsize = sizeof(struct rk2_cipher_ctx), ++ .base.cra_alignmask = 0x0f, ++ .base.cra_module = THIS_MODULE, ++ ++ .init = rk2_cipher_tfm_init, ++ .exit = rk2_cipher_tfm_exit, ++ .min_keysize = AES_MIN_KEY_SIZE * 2, ++ .max_keysize = AES_MAX_KEY_SIZE * 2, ++ .ivsize = AES_BLOCK_SIZE, ++ .setkey = rk2_aes_xts_setkey, ++ .encrypt = rk2_skcipher_encrypt, ++ .decrypt = rk2_skcipher_decrypt, ++ }, ++ .alg.skcipher.op = { ++ .do_one_request = rk2_cipher_run, ++ }, ++ }, ++ { ++ .type = CRYPTO_ALG_TYPE_AHASH, ++ .rk2_mode = RK2_CRYPTO_MD5, ++ .alg.hash.base = { ++ .init = rk2_ahash_init, ++ .update = rk2_ahash_update, ++ .final = rk2_ahash_final, ++ .finup = rk2_ahash_finup, ++ .export = rk2_ahash_export, ++ .import = rk2_ahash_import, ++ .digest = rk2_ahash_digest, ++ .init_tfm = rk2_hash_init_tfm, ++ .exit_tfm = rk2_hash_exit_tfm, ++ .halg = { ++ .digestsize = MD5_DIGEST_SIZE, ++ .statesize = sizeof(struct md5_state), ++ .base = { ++ .cra_name = "md5", ++ .cra_driver_name = "rk2-md5", ++ .cra_priority = 300, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_NEED_FALLBACK, ++ .cra_blocksize = MD5_HMAC_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct rk2_ahash_ctx), ++ .cra_module = THIS_MODULE, ++ } ++ } ++ }, ++ .alg.hash.op = { ++ .do_one_request = rk2_hash_run, ++ }, ++ ++ }, ++ { ++ .type = CRYPTO_ALG_TYPE_AHASH, ++ .rk2_mode = RK2_CRYPTO_SHA1, ++ .alg.hash.base = { ++ .init = rk2_ahash_init, ++ .update = rk2_ahash_update, ++ .final = rk2_ahash_final, ++ .finup = rk2_ahash_finup, ++ .export = rk2_ahash_export, ++ .import = rk2_ahash_import, ++ .digest = rk2_ahash_digest, ++ .init_tfm = rk2_hash_init_tfm, ++ .exit_tfm = rk2_hash_exit_tfm, ++ .halg = { ++ .digestsize = SHA1_DIGEST_SIZE, ++ .statesize = sizeof(struct sha1_state), ++ .base = { ++ .cra_name = "sha1", ++ .cra_driver_name = "rk2-sha1", ++ .cra_priority = 300, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_NEED_FALLBACK, ++ .cra_blocksize = SHA1_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct rk2_ahash_ctx), ++ .cra_module = THIS_MODULE, ++ } ++ } ++ }, ++ .alg.hash.op = { ++ .do_one_request = rk2_hash_run, ++ }, ++ }, ++ { ++ .type = CRYPTO_ALG_TYPE_AHASH, ++ .rk2_mode = RK2_CRYPTO_SHA256, ++ .alg.hash.base = { ++ .init = rk2_ahash_init, ++ .update = rk2_ahash_update, ++ .final = rk2_ahash_final, ++ .finup = rk2_ahash_finup, ++ .export = rk2_ahash_export, ++ .import = rk2_ahash_import, ++ .digest = rk2_ahash_digest, ++ .init_tfm = rk2_hash_init_tfm, ++ .exit_tfm = rk2_hash_exit_tfm, ++ .halg = { ++ .digestsize = SHA256_DIGEST_SIZE, ++ .statesize = sizeof(struct sha256_state), ++ .base = { ++ .cra_name = "sha256", ++ .cra_driver_name = "rk2-sha256", ++ .cra_priority = 300, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_NEED_FALLBACK, ++ .cra_blocksize = SHA256_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct rk2_ahash_ctx), ++ .cra_module = THIS_MODULE, ++ } ++ } ++ }, ++ .alg.hash.op = { ++ .do_one_request = rk2_hash_run, ++ }, ++ }, ++ { ++ .type = CRYPTO_ALG_TYPE_AHASH, ++ .rk2_mode = RK2_CRYPTO_SHA384, ++ .alg.hash.base = { ++ .init = rk2_ahash_init, ++ .update = rk2_ahash_update, ++ .final = rk2_ahash_final, ++ .finup = rk2_ahash_finup, ++ .export = rk2_ahash_export, ++ .import = rk2_ahash_import, ++ .digest = rk2_ahash_digest, ++ .init_tfm = rk2_hash_init_tfm, ++ .exit_tfm = rk2_hash_exit_tfm, ++ .halg = { ++ .digestsize = SHA384_DIGEST_SIZE, ++ .statesize = sizeof(struct sha512_state), ++ .base = { ++ .cra_name = "sha384", ++ .cra_driver_name = "rk2-sha384", ++ .cra_priority = 300, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_NEED_FALLBACK, ++ .cra_blocksize = SHA384_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct rk2_ahash_ctx), ++ .cra_module = THIS_MODULE, ++ } ++ } ++ }, ++ .alg.hash.op = { ++ .do_one_request = rk2_hash_run, ++ }, ++ }, ++ { ++ .type = CRYPTO_ALG_TYPE_AHASH, ++ .rk2_mode = RK2_CRYPTO_SHA512, ++ .alg.hash.base = { ++ .init = rk2_ahash_init, ++ .update = rk2_ahash_update, ++ .final = rk2_ahash_final, ++ .finup = rk2_ahash_finup, ++ .export = rk2_ahash_export, ++ .import = rk2_ahash_import, ++ .digest = rk2_ahash_digest, ++ .init_tfm = rk2_hash_init_tfm, ++ .exit_tfm = rk2_hash_exit_tfm, ++ .halg = { ++ .digestsize = SHA512_DIGEST_SIZE, ++ .statesize = sizeof(struct sha512_state), ++ .base = { ++ .cra_name = "sha512", ++ .cra_driver_name = "rk2-sha512", ++ .cra_priority = 300, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_NEED_FALLBACK, ++ .cra_blocksize = SHA512_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct rk2_ahash_ctx), ++ .cra_module = THIS_MODULE, ++ } ++ } ++ }, ++ .alg.hash.op = { ++ .do_one_request = rk2_hash_run, ++ }, ++ }, ++ { ++ .type = CRYPTO_ALG_TYPE_AHASH, ++ .rk2_mode = RK2_CRYPTO_SM3, ++ .alg.hash.base = { ++ .init = rk2_ahash_init, ++ .update = rk2_ahash_update, ++ .final = rk2_ahash_final, ++ .finup = rk2_ahash_finup, ++ .export = rk2_ahash_export, ++ .import = rk2_ahash_import, ++ .digest = rk2_ahash_digest, ++ .init_tfm = rk2_hash_init_tfm, ++ .exit_tfm = rk2_hash_exit_tfm, ++ .halg = { ++ .digestsize = SM3_DIGEST_SIZE, ++ .statesize = sizeof(struct sm3_state), ++ .base = { ++ .cra_name = "sm3", ++ .cra_driver_name = "rk2-sm3", ++ .cra_priority = 300, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_NEED_FALLBACK, ++ .cra_blocksize = SM3_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct rk2_ahash_ctx), ++ .cra_module = THIS_MODULE, ++ } ++ } ++ }, ++ .alg.hash.op = { ++ .do_one_request = rk2_hash_run, ++ }, ++ }, ++}; ++ ++#ifdef CONFIG_CRYPTO_DEV_ROCKCHIP2_DEBUG ++static int rk2_crypto_debugfs_stats_show(struct seq_file *seq, void *v) ++{ ++ struct rk2_crypto_dev *rkc; ++ unsigned int i; ++ ++ spin_lock(&rocklist.lock); ++ list_for_each_entry(rkc, &rocklist.dev_list, list) { ++ seq_printf(seq, "%s %s requests: %lu\n", ++ dev_driver_string(rkc->dev), dev_name(rkc->dev), ++ rkc->nreq); ++ } ++ spin_unlock(&rocklist.lock); ++ ++ for (i = 0; i < ARRAY_SIZE(rk2_crypto_algs); i++) { ++ if (!rk2_crypto_algs[i].dev) ++ continue; ++ switch (rk2_crypto_algs[i].type) { ++ case CRYPTO_ALG_TYPE_SKCIPHER: ++ seq_printf(seq, "%s %s reqs=%lu fallback=%lu\n", ++ rk2_crypto_algs[i].alg.skcipher.base.base.cra_driver_name, ++ rk2_crypto_algs[i].alg.skcipher.base.base.cra_name, ++ rk2_crypto_algs[i].stat_req, rk2_crypto_algs[i].stat_fb); ++ seq_printf(seq, "\tfallback due to length: %lu\n", ++ rk2_crypto_algs[i].stat_fb_len); ++ seq_printf(seq, "\tfallback due to alignment: %lu\n", ++ rk2_crypto_algs[i].stat_fb_align); ++ seq_printf(seq, "\tfallback due to SGs: %lu\n", ++ rk2_crypto_algs[i].stat_fb_sgdiff); ++ break; ++ case CRYPTO_ALG_TYPE_AHASH: ++ seq_printf(seq, "%s %s reqs=%lu fallback=%lu\n", ++ rk2_crypto_algs[i].alg.hash.base.halg.base.cra_driver_name, ++ rk2_crypto_algs[i].alg.hash.base.halg.base.cra_name, ++ rk2_crypto_algs[i].stat_req, rk2_crypto_algs[i].stat_fb); ++ break; ++ } ++ } ++ return 0; ++} ++ ++static int rk2_crypto_debugfs_info_show(struct seq_file *seq, void *d) ++{ ++ struct rk2_crypto_dev *rkc; ++ u32 v; ++ ++ spin_lock(&rocklist.lock); ++ list_for_each_entry(rkc, &rocklist.dev_list, list) { ++ v = readl(rkc->reg + RK2_CRYPTO_CLK_CTL); ++ seq_printf(seq, "CRYPTO_CLK_CTL %x\n", v); ++ v = readl(rkc->reg + RK2_CRYPTO_RST_CTL); ++ seq_printf(seq, "CRYPTO_RST_CTL %x\n", v); ++ ++ v = readl(rkc->reg + CRYPTO_AES_VERSION); ++ seq_printf(seq, "CRYPTO_AES_VERSION %x\n", v); ++ if (v & BIT(17)) ++ seq_puts(seq, "AES 192\n"); ++ ++ v = readl(rkc->reg + CRYPTO_DES_VERSION); ++ seq_printf(seq, "CRYPTO_DES_VERSION %x\n", v); ++ v = readl(rkc->reg + CRYPTO_SM4_VERSION); ++ seq_printf(seq, "CRYPTO_SM4_VERSION %x\n", v); ++ v = readl(rkc->reg + CRYPTO_HASH_VERSION); ++ seq_printf(seq, "CRYPTO_HASH_VERSION %x\n", v); ++ v = readl(rkc->reg + CRYPTO_HMAC_VERSION); ++ seq_printf(seq, "CRYPTO_HMAC_VERSION %x\n", v); ++ v = readl(rkc->reg + CRYPTO_RNG_VERSION); ++ seq_printf(seq, "CRYPTO_RNG_VERSION %x\n", v); ++ v = readl(rkc->reg + CRYPTO_PKA_VERSION); ++ seq_printf(seq, "CRYPTO_PKA_VERSION %x\n", v); ++ v = readl(rkc->reg + CRYPTO_CRYPTO_VERSION); ++ seq_printf(seq, "CRYPTO_CRYPTO_VERSION %x\n", v); ++ } ++ spin_unlock(&rocklist.lock); ++ ++ return 0; ++} ++ ++DEFINE_SHOW_ATTRIBUTE(rk2_crypto_debugfs_stats); ++DEFINE_SHOW_ATTRIBUTE(rk2_crypto_debugfs_info); ++ ++#endif ++ ++static void register_debugfs(struct rk2_crypto_dev *crypto_dev) ++{ ++#ifdef CONFIG_CRYPTO_DEV_ROCKCHIP2_DEBUG ++ /* Ignore error of debugfs */ ++ rocklist.dbgfs_dir = debugfs_create_dir("rk2_crypto", NULL); ++ rocklist.dbgfs_stats = debugfs_create_file("stats", 0440, ++ rocklist.dbgfs_dir, ++ &rocklist, ++ &rk2_crypto_debugfs_stats_fops); ++ rocklist.dbgfs_stats = debugfs_create_file("info", 0440, ++ rocklist.dbgfs_dir, ++ &rocklist, ++ &rk2_crypto_debugfs_info_fops); ++#endif ++} ++ ++static int rk2_crypto_register(struct rk2_crypto_dev *rkc) ++{ ++ unsigned int i, k; ++ int err = 0; ++ ++ for (i = 0; i < ARRAY_SIZE(rk2_crypto_algs); i++) { ++ rk2_crypto_algs[i].dev = rkc; ++ switch (rk2_crypto_algs[i].type) { ++ case CRYPTO_ALG_TYPE_SKCIPHER: ++ dev_info(rkc->dev, "Register %s as %s\n", ++ rk2_crypto_algs[i].alg.skcipher.base.base.cra_name, ++ rk2_crypto_algs[i].alg.skcipher.base.base.cra_driver_name); ++ err = crypto_engine_register_skcipher(&rk2_crypto_algs[i].alg.skcipher); ++ break; ++ case CRYPTO_ALG_TYPE_AHASH: ++ dev_info(rkc->dev, "Register %s as %s %d\n", ++ rk2_crypto_algs[i].alg.hash.base.halg.base.cra_name, ++ rk2_crypto_algs[i].alg.hash.base.halg.base.cra_driver_name, i); ++ err = crypto_engine_register_ahash(&rk2_crypto_algs[i].alg.hash); ++ break; ++ default: ++ dev_err(rkc->dev, "unknown algorithm\n"); ++ } ++ if (err) ++ goto err_cipher_algs; ++ } ++ return 0; ++ ++err_cipher_algs: ++ for (k = 0; k < i; k++) { ++ if (rk2_crypto_algs[k].type == CRYPTO_ALG_TYPE_SKCIPHER) ++ crypto_engine_unregister_skcipher(&rk2_crypto_algs[k].alg.skcipher); ++ else ++ crypto_engine_unregister_ahash(&rk2_crypto_algs[k].alg.hash); ++ } ++ return err; ++} ++ ++static void rk2_crypto_unregister(void) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(rk2_crypto_algs); i++) { ++ if (rk2_crypto_algs[i].type == CRYPTO_ALG_TYPE_SKCIPHER) ++ crypto_engine_unregister_skcipher(&rk2_crypto_algs[i].alg.skcipher); ++ else ++ crypto_engine_unregister_ahash(&rk2_crypto_algs[i].alg.hash); ++ } ++} ++ ++static const struct of_device_id crypto_of_id_table[] = { ++ { .compatible = "rockchip,rk3568-crypto", ++ .data = &rk3568_variant, ++ }, ++ { .compatible = "rockchip,rk3588-crypto", ++ .data = &rk3588_variant, ++ }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, crypto_of_id_table); ++ ++static int rk2_crypto_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct rk2_crypto_dev *rkc, *first; ++ int err = 0; ++ ++ rkc = devm_kzalloc(&pdev->dev, sizeof(*rkc), GFP_KERNEL); ++ if (!rkc) { ++ err = -ENOMEM; ++ goto err_crypto; ++ } ++ ++ rkc->dev = &pdev->dev; ++ platform_set_drvdata(pdev, rkc); ++ ++ rkc->variant = of_device_get_match_data(&pdev->dev); ++ if (!rkc->variant) { ++ dev_err(&pdev->dev, "Missing variant\n"); ++ return -EINVAL; ++ } ++ ++ rkc->rst = devm_reset_control_array_get_exclusive(dev); ++ if (IS_ERR(rkc->rst)) { ++ err = PTR_ERR(rkc->rst); ++ dev_err(&pdev->dev, "Fail to get resets err=%d\n", err); ++ goto err_crypto; ++ } ++ ++ rkc->tl = dma_alloc_coherent(rkc->dev, ++ sizeof(struct rk2_crypto_lli) * MAX_LLI, ++ &rkc->t_phy, GFP_KERNEL); ++ if (!rkc->tl) { ++ dev_err(rkc->dev, "Cannot get DMA memory for task\n"); ++ err = -ENOMEM; ++ goto err_crypto; ++ } ++ ++ reset_control_assert(rkc->rst); ++ usleep_range(10, 20); ++ reset_control_deassert(rkc->rst); ++ ++ rkc->reg = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(rkc->reg)) { ++ err = PTR_ERR(rkc->reg); ++ dev_err(&pdev->dev, "Fail to get resources\n"); ++ goto err_crypto; ++ } ++ ++ err = rk2_crypto_get_clks(rkc); ++ if (err) ++ goto err_crypto; ++ ++ rkc->irq = platform_get_irq(pdev, 0); ++ if (rkc->irq < 0) { ++ dev_err(&pdev->dev, "control Interrupt is not available.\n"); ++ err = rkc->irq; ++ goto err_crypto; ++ } ++ ++ err = devm_request_irq(&pdev->dev, rkc->irq, ++ rk2_crypto_irq_handle, IRQF_SHARED, ++ "rk-crypto", pdev); ++ ++ if (err) { ++ dev_err(&pdev->dev, "irq request failed.\n"); ++ goto err_crypto; ++ } ++ ++ rkc->engine = crypto_engine_alloc_init(&pdev->dev, true); ++ crypto_engine_start(rkc->engine); ++ init_completion(&rkc->complete); ++ ++ err = rk2_crypto_pm_init(rkc); ++ if (err) ++ goto err_pm; ++ ++ err = pm_runtime_resume_and_get(&pdev->dev); ++ ++ spin_lock(&rocklist.lock); ++ first = list_first_entry_or_null(&rocklist.dev_list, ++ struct rk2_crypto_dev, list); ++ list_add_tail(&rkc->list, &rocklist.dev_list); ++ spin_unlock(&rocklist.lock); ++ ++ if (!first) { ++ dev_info(dev, "Registers crypto algos\n"); ++ err = rk2_crypto_register(rkc); ++ if (err) { ++ dev_err(dev, "Fail to register crypto algorithms"); ++ goto err_register_alg; ++ } ++ ++ register_debugfs(rkc); ++ } ++ ++ return 0; ++ ++err_register_alg: ++ rk2_crypto_pm_exit(rkc); ++err_pm: ++ crypto_engine_exit(rkc->engine); ++err_crypto: ++ dev_err(dev, "Crypto Accelerator not successfully registered\n"); ++ return err; ++} ++ ++static int rk2_crypto_remove(struct platform_device *pdev) ++{ ++ struct rk2_crypto_dev *rkc = platform_get_drvdata(pdev); ++ struct rk2_crypto_dev *first; ++ ++ spin_lock_bh(&rocklist.lock); ++ list_del(&rkc->list); ++ first = list_first_entry_or_null(&rocklist.dev_list, ++ struct rk2_crypto_dev, list); ++ spin_unlock_bh(&rocklist.lock); ++ ++ if (!first) { ++#ifdef CONFIG_CRYPTO_DEV_ROCKCHIP2_DEBUG ++ debugfs_remove_recursive(rocklist.dbgfs_dir); ++#endif ++ rk2_crypto_unregister(); ++ } ++ rk2_crypto_pm_exit(rkc); ++ crypto_engine_exit(rkc->engine); ++ return 0; ++} ++ ++static struct platform_driver crypto_driver = { ++ .probe = rk2_crypto_probe, ++ .remove = rk2_crypto_remove, ++ .driver = { ++ .name = "rk2-crypto", ++ .pm = &rk2_crypto_pm_ops, ++ .of_match_table = crypto_of_id_table, ++ }, ++}; ++ ++module_platform_driver(crypto_driver); ++ ++MODULE_DESCRIPTION("Rockchip Crypto Engine cryptographic offloader"); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Corentin Labbe "); +diff --git a/drivers/crypto/rockchip/rk2_crypto.h b/drivers/crypto/rockchip/rk2_crypto.h +new file mode 100644 +index 000000000000..59cd8be59f70 +--- /dev/null ++++ b/drivers/crypto/rockchip/rk2_crypto.h +@@ -0,0 +1,246 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define RK2_CRYPTO_CLK_CTL 0x0000 ++#define RK2_CRYPTO_RST_CTL 0x0004 ++ ++#define RK2_CRYPTO_DMA_INT_EN 0x0008 ++/* values for RK2_CRYPTO_DMA_INT_EN */ ++#define RK2_CRYPTO_DMA_INT_LISTDONE BIT(0) ++ ++#define RK2_CRYPTO_DMA_INT_ST 0x000C ++/* values in RK2_CRYPTO_DMA_INT_ST are the same than in RK2_CRYPTO_DMA_INT_EN */ ++ ++#define RK2_CRYPTO_DMA_CTL 0x0010 ++#define RK2_CRYPTO_DMA_CTL_START BIT(0) ++ ++#define RK2_CRYPTO_DMA_LLI_ADDR 0x0014 ++#define RK2_CRYPTO_DMA_ST 0x0018 ++#define RK2_CRYPTO_DMA_STATE 0x001C ++#define RK2_CRYPTO_DMA_LLI_RADDR 0x0020 ++#define RK2_CRYPTO_DMA_SRC_RADDR 0x0024 ++#define RK2_CRYPTO_DMA_DST_WADDR 0x0028 ++#define RK2_CRYPTO_DMA_ITEM_ID 0x002C ++ ++#define RK2_CRYPTO_FIFO_CTL 0x0040 ++ ++#define RK2_CRYPTO_BC_CTL 0x0044 ++#define RK2_CRYPTO_AES (0 << 8) ++#define RK2_CRYPTO_MODE_ECB (0 << 4) ++#define RK2_CRYPTO_MODE_CBC (1 << 4) ++#define RK2_CRYPTO_XTS (6 << 4) ++ ++#define RK2_CRYPTO_HASH_CTL 0x0048 ++#define RK2_CRYPTO_HW_PAD BIT(2) ++#define RK2_CRYPTO_SHA1 (0 << 4) ++#define RK2_CRYPTO_MD5 (1 << 4) ++#define RK2_CRYPTO_SHA224 (3 << 4) ++#define RK2_CRYPTO_SHA256 (2 << 4) ++#define RK2_CRYPTO_SHA384 (9 << 4) ++#define RK2_CRYPTO_SHA512 (8 << 4) ++#define RK2_CRYPTO_SM3 (4 << 4) ++ ++#define RK2_CRYPTO_AES_ECB (RK2_CRYPTO_AES | RK2_CRYPTO_MODE_ECB) ++#define RK2_CRYPTO_AES_CBC (RK2_CRYPTO_AES | RK2_CRYPTO_MODE_CBC) ++#define RK2_CRYPTO_AES_XTS (RK2_CRYPTO_AES | RK2_CRYPTO_XTS) ++#define RK2_CRYPTO_AES_CTR_MODE 3 ++#define RK2_CRYPTO_AES_128BIT_key (0 << 2) ++#define RK2_CRYPTO_AES_192BIT_key (1 << 2) ++#define RK2_CRYPTO_AES_256BIT_key (2 << 2) ++ ++#define RK2_CRYPTO_DEC BIT(1) ++#define RK2_CRYPTO_ENABLE BIT(0) ++ ++#define RK2_CRYPTO_CIPHER_ST 0x004C ++#define RK2_CRYPTO_CIPHER_STATE 0x0050 ++ ++#define RK2_CRYPTO_CH0_IV_0 0x0100 ++ ++#define RK2_CRYPTO_KEY0 0x0180 ++#define RK2_CRYPTO_KEY1 0x0184 ++#define RK2_CRYPTO_KEY2 0x0188 ++#define RK2_CRYPTO_KEY3 0x018C ++#define RK2_CRYPTO_KEY4 0x0190 ++#define RK2_CRYPTO_KEY5 0x0194 ++#define RK2_CRYPTO_KEY6 0x0198 ++#define RK2_CRYPTO_KEY7 0x019C ++#define RK2_CRYPTO_CH4_KEY0 0x01c0 ++ ++#define RK2_CRYPTO_CH0_PC_LEN_0 0x0280 ++ ++#define RK2_CRYPTO_CH0_IV_LEN 0x0300 ++ ++#define RK2_CRYPTO_HASH_DOUT_0 0x03A0 ++#define RK2_CRYPTO_HASH_VALID 0x03E4 ++ ++#define RK2_CRYPTO_TRNG_CTL 0x0400 ++#define RK2_CRYPTO_TRNG_START BIT(0) ++#define RK2_CRYPTO_TRNG_ENABLE BIT(1) ++#define RK2_CRYPTO_TRNG_256 (0x3 << 4) ++#define RK2_CRYPTO_TRNG_SAMPLE_CNT 0x0404 ++#define RK2_CRYPTO_TRNG_DOUT 0x0410 ++ ++#define CRYPTO_AES_VERSION 0x0680 ++#define CRYPTO_DES_VERSION 0x0684 ++#define CRYPTO_SM4_VERSION 0x0688 ++#define CRYPTO_HASH_VERSION 0x068C ++#define CRYPTO_HMAC_VERSION 0x0690 ++#define CRYPTO_RNG_VERSION 0x0694 ++#define CRYPTO_PKA_VERSION 0x0698 ++#define CRYPTO_CRYPTO_VERSION 0x06F0 ++ ++#define RK2_LLI_DMA_CTRL_SRC_INT BIT(10) ++#define RK2_LLI_DMA_CTRL_DST_INT BIT(9) ++#define RK2_LLI_DMA_CTRL_LIST_INT BIT(8) ++#define RK2_LLI_DMA_CTRL_LAST BIT(0) ++ ++#define RK2_LLI_STRING_LAST BIT(2) ++#define RK2_LLI_STRING_FIRST BIT(1) ++#define RK2_LLI_CIPHER_START BIT(0) ++ ++#define RK2_MAX_CLKS 4 ++ ++#define MAX_LLI 20 ++ ++struct rk2_crypto_lli { ++ __le32 src_addr; ++ __le32 src_len; ++ __le32 dst_addr; ++ __le32 dst_len; ++ __le32 user; ++ __le32 iv; ++ __le32 dma_ctrl; ++ __le32 next; ++}; ++ ++/* ++ * struct rockchip_ip - struct for managing a list of RK crypto instance ++ * @dev_list: Used for doing a list of rk2_crypto_dev ++ * @lock: Control access to dev_list ++ * @dbgfs_dir: Debugfs dentry for statistic directory ++ * @dbgfs_stats: Debugfs dentry for statistic counters ++ */ ++struct rockchip_ip { ++ struct list_head dev_list; ++ spinlock_t lock; /* Control access to dev_list */ ++ struct dentry *dbgfs_dir; ++ struct dentry *dbgfs_stats; ++}; ++ ++struct rk2_clks { ++ const char *name; ++ unsigned long max; ++}; ++ ++struct rk2_variant { ++ int num_clks; ++ struct rk2_clks rkclks[RK2_MAX_CLKS]; ++}; ++ ++struct rk2_crypto_dev { ++ struct list_head list; ++ struct device *dev; ++ struct clk_bulk_data *clks; ++ int num_clks; ++ struct reset_control *rst; ++ void __iomem *reg; ++ int irq; ++ const struct rk2_variant *variant; ++ unsigned long nreq; ++ struct crypto_engine *engine; ++ struct completion complete; ++ int status; ++ struct rk2_crypto_lli *tl; ++ dma_addr_t t_phy; ++}; ++ ++/* the private variable of hash */ ++struct rk2_ahash_ctx { ++ /* for fallback */ ++ struct crypto_ahash *fallback_tfm; ++}; ++ ++/* the private variable of hash for fallback */ ++struct rk2_ahash_rctx { ++ struct rk2_crypto_dev *dev; ++ struct ahash_request fallback_req; ++ u32 mode; ++ int nrsgs; ++}; ++ ++/* the private variable of cipher */ ++struct rk2_cipher_ctx { ++ unsigned int keylen; ++ u8 key[AES_MAX_KEY_SIZE * 2]; ++ u8 iv[AES_BLOCK_SIZE]; ++ struct crypto_skcipher *fallback_tfm; ++}; ++ ++struct rk2_cipher_rctx { ++ struct rk2_crypto_dev *dev; ++ u8 backup_iv[AES_BLOCK_SIZE]; ++ u32 mode; ++ struct skcipher_request fallback_req; // keep at the end ++}; ++ ++struct rk2_crypto_template { ++ u32 type; ++ u32 rk2_mode; ++ bool is_xts; ++ struct rk2_crypto_dev *dev; ++ union { ++ struct skcipher_engine_alg skcipher; ++ struct ahash_engine_alg hash; ++ } alg; ++ unsigned long stat_req; ++ unsigned long stat_fb; ++ unsigned long stat_fb_len; ++ unsigned long stat_fb_sglen; ++ unsigned long stat_fb_align; ++ unsigned long stat_fb_sgdiff; ++}; ++ ++struct rk2_crypto_dev *get_rk2_crypto(void); ++int rk2_cipher_run(struct crypto_engine *engine, void *async_req); ++int rk2_hash_run(struct crypto_engine *engine, void *breq); ++ ++int rk2_cipher_tfm_init(struct crypto_skcipher *tfm); ++void rk2_cipher_tfm_exit(struct crypto_skcipher *tfm); ++int rk2_aes_setkey(struct crypto_skcipher *cipher, const u8 *key, ++ unsigned int keylen); ++int rk2_aes_xts_setkey(struct crypto_skcipher *cipher, const u8 *key, ++ unsigned int keylen); ++int rk2_skcipher_encrypt(struct skcipher_request *req); ++int rk2_skcipher_decrypt(struct skcipher_request *req); ++int rk2_aes_ecb_encrypt(struct skcipher_request *req); ++int rk2_aes_ecb_decrypt(struct skcipher_request *req); ++int rk2_aes_cbc_encrypt(struct skcipher_request *req); ++int rk2_aes_cbc_decrypt(struct skcipher_request *req); ++ ++int rk2_ahash_init(struct ahash_request *req); ++int rk2_ahash_update(struct ahash_request *req); ++int rk2_ahash_final(struct ahash_request *req); ++int rk2_ahash_finup(struct ahash_request *req); ++int rk2_ahash_import(struct ahash_request *req, const void *in); ++int rk2_ahash_export(struct ahash_request *req, void *out); ++int rk2_ahash_digest(struct ahash_request *req); ++int rk2_hash_init_tfm(struct crypto_ahash *tfm); ++void rk2_hash_exit_tfm(struct crypto_ahash *tfm); +diff --git a/drivers/crypto/rockchip/rk2_crypto_ahash.c b/drivers/crypto/rockchip/rk2_crypto_ahash.c +new file mode 100644 +index 000000000000..75b8d9893447 +--- /dev/null ++++ b/drivers/crypto/rockchip/rk2_crypto_ahash.c +@@ -0,0 +1,344 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Crypto offloader support for Rockchip RK3568/RK3588 ++ * ++ * Copyright (c) 2022-2023 Corentin Labbe ++ */ ++#include ++#include ++#include "rk2_crypto.h" ++ ++static bool rk2_ahash_need_fallback(struct ahash_request *areq) ++{ ++ struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); ++ struct ahash_alg *alg = crypto_ahash_alg(tfm); ++ struct rk2_crypto_template *algt = container_of(alg, struct rk2_crypto_template, alg.hash.base); ++ struct scatterlist *sg; ++ ++ sg = areq->src; ++ while (sg) { ++ if (!IS_ALIGNED(sg->offset, sizeof(u32))) { ++ algt->stat_fb_align++; ++ return true; ++ } ++ if (sg->length % 4) { ++ algt->stat_fb_sglen++; ++ return true; ++ } ++ sg = sg_next(sg); ++ } ++ return false; ++} ++ ++static int rk2_ahash_digest_fb(struct ahash_request *areq) ++{ ++ struct rk2_ahash_rctx *rctx = ahash_request_ctx(areq); ++ struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); ++ struct rk2_ahash_ctx *tfmctx = crypto_ahash_ctx(tfm); ++ struct ahash_alg *alg = crypto_ahash_alg(tfm); ++ struct rk2_crypto_template *algt = container_of(alg, struct rk2_crypto_template, alg.hash.base); ++ ++ algt->stat_fb++; ++ ++ ahash_request_set_tfm(&rctx->fallback_req, tfmctx->fallback_tfm); ++ rctx->fallback_req.base.flags = areq->base.flags & ++ CRYPTO_TFM_REQ_MAY_SLEEP; ++ ++ rctx->fallback_req.nbytes = areq->nbytes; ++ rctx->fallback_req.src = areq->src; ++ rctx->fallback_req.result = areq->result; ++ ++ return crypto_ahash_digest(&rctx->fallback_req); ++} ++ ++static int zero_message_process(struct ahash_request *req) ++{ ++ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); ++ struct ahash_alg *alg = crypto_ahash_alg(tfm); ++ struct rk2_crypto_template *algt = container_of(alg, struct rk2_crypto_template, alg.hash.base); ++ int digestsize = crypto_ahash_digestsize(tfm); ++ ++ switch (algt->rk2_mode) { ++ case RK2_CRYPTO_SHA1: ++ memcpy(req->result, sha1_zero_message_hash, digestsize); ++ break; ++ case RK2_CRYPTO_SHA256: ++ memcpy(req->result, sha256_zero_message_hash, digestsize); ++ break; ++ case RK2_CRYPTO_SHA384: ++ memcpy(req->result, sha384_zero_message_hash, digestsize); ++ break; ++ case RK2_CRYPTO_SHA512: ++ memcpy(req->result, sha512_zero_message_hash, digestsize); ++ break; ++ case RK2_CRYPTO_MD5: ++ memcpy(req->result, md5_zero_message_hash, digestsize); ++ break; ++ case RK2_CRYPTO_SM3: ++ memcpy(req->result, sm3_zero_message_hash, digestsize); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++int rk2_ahash_init(struct ahash_request *req) ++{ ++ struct rk2_ahash_rctx *rctx = ahash_request_ctx(req); ++ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); ++ struct rk2_ahash_ctx *ctx = crypto_ahash_ctx(tfm); ++ ++ ahash_request_set_tfm(&rctx->fallback_req, ctx->fallback_tfm); ++ rctx->fallback_req.base.flags = req->base.flags & ++ CRYPTO_TFM_REQ_MAY_SLEEP; ++ ++ return crypto_ahash_init(&rctx->fallback_req); ++} ++ ++int rk2_ahash_update(struct ahash_request *req) ++{ ++ struct rk2_ahash_rctx *rctx = ahash_request_ctx(req); ++ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); ++ struct rk2_ahash_ctx *ctx = crypto_ahash_ctx(tfm); ++ ++ ahash_request_set_tfm(&rctx->fallback_req, ctx->fallback_tfm); ++ rctx->fallback_req.base.flags = req->base.flags & ++ CRYPTO_TFM_REQ_MAY_SLEEP; ++ rctx->fallback_req.nbytes = req->nbytes; ++ rctx->fallback_req.src = req->src; ++ ++ return crypto_ahash_update(&rctx->fallback_req); ++} ++ ++int rk2_ahash_final(struct ahash_request *req) ++{ ++ struct rk2_ahash_rctx *rctx = ahash_request_ctx(req); ++ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); ++ struct rk2_ahash_ctx *ctx = crypto_ahash_ctx(tfm); ++ ++ ahash_request_set_tfm(&rctx->fallback_req, ctx->fallback_tfm); ++ rctx->fallback_req.base.flags = req->base.flags & ++ CRYPTO_TFM_REQ_MAY_SLEEP; ++ rctx->fallback_req.result = req->result; ++ ++ return crypto_ahash_final(&rctx->fallback_req); ++} ++ ++int rk2_ahash_finup(struct ahash_request *req) ++{ ++ struct rk2_ahash_rctx *rctx = ahash_request_ctx(req); ++ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); ++ struct rk2_ahash_ctx *ctx = crypto_ahash_ctx(tfm); ++ ++ ahash_request_set_tfm(&rctx->fallback_req, ctx->fallback_tfm); ++ rctx->fallback_req.base.flags = req->base.flags & ++ CRYPTO_TFM_REQ_MAY_SLEEP; ++ ++ rctx->fallback_req.nbytes = req->nbytes; ++ rctx->fallback_req.src = req->src; ++ rctx->fallback_req.result = req->result; ++ ++ return crypto_ahash_finup(&rctx->fallback_req); ++} ++ ++int rk2_ahash_import(struct ahash_request *req, const void *in) ++{ ++ struct rk2_ahash_rctx *rctx = ahash_request_ctx(req); ++ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); ++ struct rk2_ahash_ctx *ctx = crypto_ahash_ctx(tfm); ++ ++ ahash_request_set_tfm(&rctx->fallback_req, ctx->fallback_tfm); ++ rctx->fallback_req.base.flags = req->base.flags & ++ CRYPTO_TFM_REQ_MAY_SLEEP; ++ ++ return crypto_ahash_import(&rctx->fallback_req, in); ++} ++ ++int rk2_ahash_export(struct ahash_request *req, void *out) ++{ ++ struct rk2_ahash_rctx *rctx = ahash_request_ctx(req); ++ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); ++ struct rk2_ahash_ctx *ctx = crypto_ahash_ctx(tfm); ++ ++ ahash_request_set_tfm(&rctx->fallback_req, ctx->fallback_tfm); ++ rctx->fallback_req.base.flags = req->base.flags & ++ CRYPTO_TFM_REQ_MAY_SLEEP; ++ ++ return crypto_ahash_export(&rctx->fallback_req, out); ++} ++ ++int rk2_ahash_digest(struct ahash_request *req) ++{ ++ struct rk2_ahash_rctx *rctx = ahash_request_ctx(req); ++ struct rk2_crypto_dev *dev; ++ struct crypto_engine *engine; ++ ++ if (rk2_ahash_need_fallback(req)) ++ return rk2_ahash_digest_fb(req); ++ ++ if (!req->nbytes) ++ return zero_message_process(req); ++ ++ dev = get_rk2_crypto(); ++ ++ rctx->dev = dev; ++ engine = dev->engine; ++ ++ return crypto_transfer_hash_request_to_engine(engine, req); ++} ++ ++static int rk2_hash_prepare(struct crypto_engine *engine, void *breq) ++{ ++ struct ahash_request *areq = container_of(breq, struct ahash_request, base); ++ struct rk2_ahash_rctx *rctx = ahash_request_ctx(areq); ++ struct rk2_crypto_dev *rkc = rctx->dev; ++ int ret; ++ ++ ret = dma_map_sg(rkc->dev, areq->src, sg_nents(areq->src), DMA_TO_DEVICE); ++ if (ret <= 0) ++ return -EINVAL; ++ ++ rctx->nrsgs = ret; ++ ++ return 0; ++} ++ ++static void rk2_hash_unprepare(struct crypto_engine *engine, void *breq) ++{ ++ struct ahash_request *areq = container_of(breq, struct ahash_request, base); ++ struct rk2_ahash_rctx *rctx = ahash_request_ctx(areq); ++ struct rk2_crypto_dev *rkc = rctx->dev; ++ ++ dma_unmap_sg(rkc->dev, areq->src, rctx->nrsgs, DMA_TO_DEVICE); ++} ++ ++int rk2_hash_run(struct crypto_engine *engine, void *breq) ++{ ++ struct ahash_request *areq = container_of(breq, struct ahash_request, base); ++ struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); ++ struct rk2_ahash_rctx *rctx = ahash_request_ctx(areq); ++ struct ahash_alg *alg = crypto_ahash_alg(tfm); ++ struct rk2_crypto_template *algt = container_of(alg, struct rk2_crypto_template, alg.hash.base); ++ struct scatterlist *sgs = areq->src; ++ struct rk2_crypto_dev *rkc = rctx->dev; ++ struct rk2_crypto_lli *dd = &rkc->tl[0]; ++ int ddi = 0; ++ int err = 0; ++ unsigned int len = areq->nbytes; ++ unsigned int todo; ++ u32 v; ++ int i; ++ ++ err = rk2_hash_prepare(engine, breq); ++ ++ err = pm_runtime_resume_and_get(rkc->dev); ++ if (err) ++ return err; ++ ++ dev_dbg(rkc->dev, "%s %s len=%d\n", __func__, ++ crypto_tfm_alg_name(areq->base.tfm), areq->nbytes); ++ ++ algt->stat_req++; ++ rkc->nreq++; ++ ++ rctx->mode = algt->rk2_mode; ++ rctx->mode |= 0xffff0000; ++ rctx->mode |= RK2_CRYPTO_ENABLE | RK2_CRYPTO_HW_PAD; ++ writel(rctx->mode, rkc->reg + RK2_CRYPTO_HASH_CTL); ++ ++ while (sgs && len > 0) { ++ dd = &rkc->tl[ddi]; ++ ++ todo = min(sg_dma_len(sgs), len); ++ dd->src_addr = sg_dma_address(sgs); ++ dd->src_len = todo; ++ dd->dst_addr = 0; ++ dd->dst_len = 0; ++ dd->dma_ctrl = ddi << 24; ++ dd->iv = 0; ++ dd->next = rkc->t_phy + sizeof(struct rk2_crypto_lli) * (ddi + 1); ++ ++ if (ddi == 0) ++ dd->user = RK2_LLI_CIPHER_START | RK2_LLI_STRING_FIRST; ++ else ++ dd->user = 0; ++ ++ len -= todo; ++ dd->dma_ctrl |= RK2_LLI_DMA_CTRL_SRC_INT; ++ if (len == 0) { ++ dd->user |= RK2_LLI_STRING_LAST; ++ dd->dma_ctrl |= RK2_LLI_DMA_CTRL_LAST; ++ } ++ dev_dbg(rkc->dev, "HASH SG %d sglen=%d user=%x dma=%x mode=%x len=%d todo=%d phy=%llx\n", ++ ddi, sgs->length, dd->user, dd->dma_ctrl, rctx->mode, len, todo, rkc->t_phy); ++ ++ sgs = sg_next(sgs); ++ ddi++; ++ } ++ dd->next = 1; ++ writel(RK2_CRYPTO_DMA_INT_LISTDONE | 0x7F, rkc->reg + RK2_CRYPTO_DMA_INT_EN); ++ ++ writel(rkc->t_phy, rkc->reg + RK2_CRYPTO_DMA_LLI_ADDR); ++ ++ reinit_completion(&rkc->complete); ++ rkc->status = 0; ++ ++ writel(RK2_CRYPTO_DMA_CTL_START | RK2_CRYPTO_DMA_CTL_START << 16, rkc->reg + RK2_CRYPTO_DMA_CTL); ++ ++ wait_for_completion_interruptible_timeout(&rkc->complete, ++ msecs_to_jiffies(2000)); ++ if (!rkc->status) { ++ dev_err(rkc->dev, "DMA timeout\n"); ++ err = -EFAULT; ++ goto theend; ++ } ++ ++ readl_poll_timeout_atomic(rkc->reg + RK2_CRYPTO_HASH_VALID, v, v == 1, ++ 10, 1000); ++ ++ for (i = 0; i < crypto_ahash_digestsize(tfm) / 4; i++) { ++ v = readl(rkc->reg + RK2_CRYPTO_HASH_DOUT_0 + i * 4); ++ put_unaligned_le32(be32_to_cpu(v), areq->result + i * 4); ++ } ++ ++theend: ++ pm_runtime_put_autosuspend(rkc->dev); ++ ++ rk2_hash_unprepare(engine, breq); ++ ++ local_bh_disable(); ++ crypto_finalize_hash_request(engine, breq, err); ++ local_bh_enable(); ++ ++ return 0; ++} ++ ++int rk2_hash_init_tfm(struct crypto_ahash *tfm) ++{ ++ struct rk2_ahash_ctx *tctx = crypto_ahash_ctx(tfm); ++ const char *alg_name = crypto_ahash_alg_name(tfm); ++ struct ahash_alg *alg = crypto_ahash_alg(tfm); ++ struct rk2_crypto_template *algt = container_of(alg, struct rk2_crypto_template, alg.hash.base); ++ ++ /* for fallback */ ++ tctx->fallback_tfm = crypto_alloc_ahash(alg_name, 0, ++ CRYPTO_ALG_NEED_FALLBACK); ++ if (IS_ERR(tctx->fallback_tfm)) { ++ dev_err(algt->dev->dev, "Could not load fallback driver.\n"); ++ return PTR_ERR(tctx->fallback_tfm); ++ } ++ ++ crypto_ahash_set_reqsize(tfm, ++ sizeof(struct rk2_ahash_rctx) + ++ crypto_ahash_reqsize(tctx->fallback_tfm)); ++ return 0; ++} ++ ++void rk2_hash_exit_tfm(struct crypto_ahash *tfm) ++{ ++ struct rk2_ahash_ctx *tctx = crypto_ahash_ctx(tfm); ++ ++ crypto_free_ahash(tctx->fallback_tfm); ++} +diff --git a/drivers/crypto/rockchip/rk2_crypto_skcipher.c b/drivers/crypto/rockchip/rk2_crypto_skcipher.c +new file mode 100644 +index 000000000000..3e8e44d84b47 +--- /dev/null ++++ b/drivers/crypto/rockchip/rk2_crypto_skcipher.c +@@ -0,0 +1,576 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * hardware cryptographic offloader for RK3568/RK3588 SoC ++ * ++ * Copyright (c) 2022-2023 Corentin Labbe ++ */ ++#include ++#include "rk2_crypto.h" ++ ++static void rk2_print(struct rk2_crypto_dev *rkc) ++{ ++ u32 v; ++ ++ v = readl(rkc->reg + RK2_CRYPTO_DMA_ST); ++ dev_info(rkc->dev, "DMA_ST %x\n", v); ++ switch (v) { ++ case 0: ++ dev_info(rkc->dev, "DMA_ST: DMA IDLE\n"); ++ break; ++ case 1: ++ dev_info(rkc->dev, "DMA_ST: DMA BUSY\n"); ++ break; ++ default: ++ dev_err(rkc->dev, "DMA_ST: invalid value\n"); ++ } ++ ++ v = readl(rkc->reg + RK2_CRYPTO_DMA_STATE); ++ dev_info(rkc->dev, "DMA_STATE %x\n", v); ++ ++ switch (v & 0x3) { ++ case 0: ++ dev_info(rkc->dev, "DMA_STATE: DMA DST IDLE\n"); ++ break; ++ case 1: ++ dev_info(rkc->dev, "DMA_STATE: DMA DST LOAD\n"); ++ break; ++ case 2: ++ dev_info(rkc->dev, "DMA_STATE: DMA DST WORK\n"); ++ break; ++ default: ++ dev_err(rkc->dev, "DMA DST invalid\n"); ++ break; ++ } ++ switch (v & 0xC) { ++ case 0: ++ dev_info(rkc->dev, "DMA_STATE: DMA SRC IDLE\n"); ++ break; ++ case 1: ++ dev_info(rkc->dev, "DMA_STATE: DMA SRC LOAD\n"); ++ break; ++ case 2: ++ dev_info(rkc->dev, "DMA_STATE: DMA SRC WORK\n"); ++ break; ++ default: ++ dev_err(rkc->dev, "DMA_STATE: DMA SRC invalid\n"); ++ break; ++ } ++ switch (v & 0x30) { ++ case 0: ++ dev_info(rkc->dev, "DMA_STATE: DMA LLI IDLE\n"); ++ break; ++ case 1: ++ dev_info(rkc->dev, "DMA_STATE: DMA LLI LOAD\n"); ++ break; ++ case 2: ++ dev_info(rkc->dev, "DMA LLI WORK\n"); ++ break; ++ default: ++ dev_err(rkc->dev, "DMA LLI invalid\n"); ++ break; ++ } ++ ++ v = readl(rkc->reg + RK2_CRYPTO_DMA_LLI_RADDR); ++ dev_info(rkc->dev, "DMA_LLI_RADDR %x\n", v); ++ v = readl(rkc->reg + RK2_CRYPTO_DMA_SRC_RADDR); ++ dev_info(rkc->dev, "DMA_SRC_RADDR %x\n", v); ++ v = readl(rkc->reg + RK2_CRYPTO_DMA_DST_WADDR); ++ dev_info(rkc->dev, "DMA_LLI_WADDR %x\n", v); ++ v = readl(rkc->reg + RK2_CRYPTO_DMA_ITEM_ID); ++ dev_info(rkc->dev, "DMA_LLI_ITEMID %x\n", v); ++ ++ v = readl(rkc->reg + RK2_CRYPTO_CIPHER_ST); ++ dev_info(rkc->dev, "CIPHER_ST %x\n", v); ++ if (v & BIT(0)) ++ dev_info(rkc->dev, "CIPHER_ST: BLOCK CIPHER BUSY\n"); ++ else ++ dev_info(rkc->dev, "CIPHER_ST: BLOCK CIPHER IDLE\n"); ++ if (v & BIT(2)) ++ dev_info(rkc->dev, "CIPHER_ST: HASH BUSY\n"); ++ else ++ dev_info(rkc->dev, "CIPHER_ST: HASH IDLE\n"); ++ if (v & BIT(2)) ++ dev_info(rkc->dev, "CIPHER_ST: OTP KEY VALID\n"); ++ else ++ dev_info(rkc->dev, "CIPHER_ST: OTP KEY INVALID\n"); ++ ++ v = readl(rkc->reg + RK2_CRYPTO_CIPHER_STATE); ++ dev_info(rkc->dev, "CIPHER_STATE %x\n", v); ++ switch (v & 0x3) { ++ case 0: ++ dev_info(rkc->dev, "serial: IDLE state\n"); ++ break; ++ case 1: ++ dev_info(rkc->dev, "serial: PRE state\n"); ++ break; ++ case 2: ++ dev_info(rkc->dev, "serial: BULK state\n"); ++ break; ++ default: ++ dev_info(rkc->dev, "serial: reserved state\n"); ++ break; ++ } ++ switch (v & 0xC) { ++ case 0: ++ dev_info(rkc->dev, "mac_state: IDLE state\n"); ++ break; ++ case 1: ++ dev_info(rkc->dev, "mac_state: PRE state\n"); ++ break; ++ case 2: ++ dev_info(rkc->dev, "mac_state: BULK state\n"); ++ break; ++ default: ++ dev_info(rkc->dev, "mac_state: reserved state\n"); ++ break; ++ } ++ switch (v & 0x30) { ++ case 0: ++ dev_info(rkc->dev, "parallel_state: IDLE state\n"); ++ break; ++ case 1: ++ dev_info(rkc->dev, "parallel_state: PRE state\n"); ++ break; ++ case 2: ++ dev_info(rkc->dev, "parallel_state: BULK state\n"); ++ break; ++ default: ++ dev_info(rkc->dev, "parallel_state: reserved state\n"); ++ break; ++ } ++ switch (v & 0xC0) { ++ case 0: ++ dev_info(rkc->dev, "ccm_state: IDLE state\n"); ++ break; ++ case 1: ++ dev_info(rkc->dev, "ccm_state: PRE state\n"); ++ break; ++ case 2: ++ dev_info(rkc->dev, "ccm_state: NA state\n"); ++ break; ++ default: ++ dev_info(rkc->dev, "ccm_state: reserved state\n"); ++ break; ++ } ++ switch (v & 0xF00) { ++ case 0: ++ dev_info(rkc->dev, "gcm_state: IDLE state\n"); ++ break; ++ case 1: ++ dev_info(rkc->dev, "gcm_state: PRE state\n"); ++ break; ++ case 2: ++ dev_info(rkc->dev, "gcm_state: NA state\n"); ++ break; ++ case 3: ++ dev_info(rkc->dev, "gcm_state: PC state\n"); ++ break; ++ } ++ switch (v & 0xC00) { ++ case 0x1: ++ dev_info(rkc->dev, "hash_state: IDLE state\n"); ++ break; ++ case 0x2: ++ dev_info(rkc->dev, "hash_state: IPAD state\n"); ++ break; ++ case 0x4: ++ dev_info(rkc->dev, "hash_state: TEXT state\n"); ++ break; ++ case 0x8: ++ dev_info(rkc->dev, "hash_state: OPAD state\n"); ++ break; ++ case 0x10: ++ dev_info(rkc->dev, "hash_state: OPAD EXT state\n"); ++ break; ++ default: ++ dev_info(rkc->dev, "hash_state: invalid state\n"); ++ break; ++ } ++ ++ v = readl(rkc->reg + RK2_CRYPTO_DMA_INT_ST); ++ dev_info(rkc->dev, "RK2_CRYPTO_DMA_INT_ST %x\n", v); ++} ++ ++static int rk2_cipher_need_fallback(struct skcipher_request *req) ++{ ++ struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); ++ struct skcipher_alg *alg = crypto_skcipher_alg(tfm); ++ struct rk2_crypto_template *algt = container_of(alg, struct rk2_crypto_template, alg.skcipher.base); ++ struct scatterlist *sgs, *sgd; ++ unsigned int stodo, dtodo, len; ++ unsigned int bs = crypto_skcipher_blocksize(tfm); ++ ++ if (!req->cryptlen) ++ return true; ++ ++ if (algt->is_xts) { ++ if (sg_nents_for_len(req->src, req->cryptlen) > 1) ++ return true; ++ if (sg_nents_for_len(req->dst, req->cryptlen) > 1) ++ return true; ++ } ++ ++ len = req->cryptlen; ++ sgs = req->src; ++ sgd = req->dst; ++ while (sgs && sgd) { ++ if (!IS_ALIGNED(sgs->offset, sizeof(u32))) { ++ algt->stat_fb_align++; ++ return true; ++ } ++ if (!IS_ALIGNED(sgd->offset, sizeof(u32))) { ++ algt->stat_fb_align++; ++ return true; ++ } ++ stodo = min(len, sgs->length); ++ if (stodo % bs) { ++ algt->stat_fb_len++; ++ return true; ++ } ++ dtodo = min(len, sgd->length); ++ if (dtodo % bs) { ++ algt->stat_fb_len++; ++ return true; ++ } ++ if (stodo != dtodo) { ++ algt->stat_fb_sgdiff++; ++ return true; ++ } ++ len -= stodo; ++ sgs = sg_next(sgs); ++ sgd = sg_next(sgd); ++ } ++ return false; ++} ++ ++static int rk2_cipher_fallback(struct skcipher_request *areq) ++{ ++ struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(areq); ++ struct rk2_cipher_ctx *op = crypto_skcipher_ctx(tfm); ++ struct rk2_cipher_rctx *rctx = skcipher_request_ctx(areq); ++ struct skcipher_alg *alg = crypto_skcipher_alg(tfm); ++ struct rk2_crypto_template *algt = container_of(alg, struct rk2_crypto_template, alg.skcipher.base); ++ int err; ++ ++ algt->stat_fb++; ++ ++ skcipher_request_set_tfm(&rctx->fallback_req, op->fallback_tfm); ++ skcipher_request_set_callback(&rctx->fallback_req, areq->base.flags, ++ areq->base.complete, areq->base.data); ++ skcipher_request_set_crypt(&rctx->fallback_req, areq->src, areq->dst, ++ areq->cryptlen, areq->iv); ++ if (rctx->mode & RK2_CRYPTO_DEC) ++ err = crypto_skcipher_decrypt(&rctx->fallback_req); ++ else ++ err = crypto_skcipher_encrypt(&rctx->fallback_req); ++ return err; ++} ++ ++static int rk2_cipher_handle_req(struct skcipher_request *req) ++{ ++ struct rk2_cipher_rctx *rctx = skcipher_request_ctx(req); ++ struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); ++ struct rk2_cipher_ctx *ctx = crypto_skcipher_ctx(tfm); ++ struct rk2_crypto_dev *rkc; ++ struct crypto_engine *engine; ++ ++ if (ctx->keylen == AES_KEYSIZE_192 * 2) ++ return rk2_cipher_fallback(req); ++ ++ if (rk2_cipher_need_fallback(req)) ++ return rk2_cipher_fallback(req); ++ ++ rkc = get_rk2_crypto(); ++ ++ engine = rkc->engine; ++ rctx->dev = rkc; ++ ++ return crypto_transfer_skcipher_request_to_engine(engine, req); ++} ++ ++int rk2_aes_xts_setkey(struct crypto_skcipher *cipher, const u8 *key, ++ unsigned int keylen) ++{ ++ struct crypto_tfm *tfm = crypto_skcipher_tfm(cipher); ++ struct rk2_cipher_ctx *ctx = crypto_tfm_ctx(tfm); ++ int err; ++ ++ err = xts_verify_key(cipher, key, keylen); ++ if (err) ++ return err; ++ ++ ctx->keylen = keylen; ++ memcpy(ctx->key, key, keylen); ++ ++ return crypto_skcipher_setkey(ctx->fallback_tfm, key, keylen); ++} ++ ++int rk2_aes_setkey(struct crypto_skcipher *cipher, const u8 *key, ++ unsigned int keylen) ++{ ++ struct crypto_tfm *tfm = crypto_skcipher_tfm(cipher); ++ struct rk2_cipher_ctx *ctx = crypto_tfm_ctx(tfm); ++ ++ if (keylen != AES_KEYSIZE_128 && keylen != AES_KEYSIZE_192 && ++ keylen != AES_KEYSIZE_256) ++ return -EINVAL; ++ ctx->keylen = keylen; ++ memcpy(ctx->key, key, keylen); ++ ++ return crypto_skcipher_setkey(ctx->fallback_tfm, key, keylen); ++} ++ ++int rk2_skcipher_encrypt(struct skcipher_request *req) ++{ ++ struct rk2_cipher_rctx *rctx = skcipher_request_ctx(req); ++ struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); ++ struct skcipher_alg *alg = crypto_skcipher_alg(tfm); ++ struct rk2_crypto_template *algt = container_of(alg, struct rk2_crypto_template, alg.skcipher.base); ++ ++ rctx->mode = algt->rk2_mode; ++ return rk2_cipher_handle_req(req); ++} ++ ++int rk2_skcipher_decrypt(struct skcipher_request *req) ++{ ++ struct rk2_cipher_rctx *rctx = skcipher_request_ctx(req); ++ struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); ++ struct skcipher_alg *alg = crypto_skcipher_alg(tfm); ++ struct rk2_crypto_template *algt = container_of(alg, struct rk2_crypto_template, alg.skcipher.base); ++ ++ rctx->mode = algt->rk2_mode | RK2_CRYPTO_DEC; ++ return rk2_cipher_handle_req(req); ++} ++ ++int rk2_cipher_run(struct crypto_engine *engine, void *async_req) ++{ ++ struct skcipher_request *areq = container_of(async_req, struct skcipher_request, base); ++ struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(areq); ++ struct rk2_cipher_rctx *rctx = skcipher_request_ctx(areq); ++ struct rk2_cipher_ctx *ctx = crypto_skcipher_ctx(tfm); ++ struct scatterlist *sgs, *sgd; ++ int err = 0; ++ int ivsize = crypto_skcipher_ivsize(tfm); ++ unsigned int len = areq->cryptlen; ++ unsigned int todo; ++ struct skcipher_alg *alg = crypto_skcipher_alg(tfm); ++ struct rk2_crypto_template *algt = container_of(alg, struct rk2_crypto_template, alg.skcipher.base); ++ struct rk2_crypto_dev *rkc = rctx->dev; ++ struct rk2_crypto_lli *dd = &rkc->tl[0]; ++ u32 m, v; ++ u32 *rkey = (u32 *)ctx->key; ++ u32 *riv = (u32 *)areq->iv; ++ int i; ++ unsigned int offset; ++ ++ algt->stat_req++; ++ rkc->nreq++; ++ ++ m = rctx->mode | RK2_CRYPTO_ENABLE; ++ if (algt->is_xts) { ++ switch (ctx->keylen) { ++ case AES_KEYSIZE_128 * 2: ++ m |= RK2_CRYPTO_AES_128BIT_key; ++ break; ++ case AES_KEYSIZE_256 * 2: ++ m |= RK2_CRYPTO_AES_256BIT_key; ++ break; ++ default: ++ dev_err(rkc->dev, "Invalid key length %u\n", ctx->keylen); ++ return -EINVAL; ++ } ++ } else { ++ switch (ctx->keylen) { ++ case AES_KEYSIZE_128: ++ m |= RK2_CRYPTO_AES_128BIT_key; ++ break; ++ case AES_KEYSIZE_192: ++ m |= RK2_CRYPTO_AES_192BIT_key; ++ break; ++ case AES_KEYSIZE_256: ++ m |= RK2_CRYPTO_AES_256BIT_key; ++ break; ++ default: ++ dev_err(rkc->dev, "Invalid key length %u\n", ctx->keylen); ++ return -EINVAL; ++ } ++ } ++ ++ err = pm_runtime_resume_and_get(rkc->dev); ++ if (err) ++ return err; ++ ++ /* the upper bits are a write enable mask, so we need to write 1 to all ++ * upper 16 bits to allow write to the 16 lower bits ++ */ ++ m |= 0xffff0000; ++ ++ dev_dbg(rkc->dev, "%s %s len=%u keylen=%u mode=%x\n", __func__, ++ crypto_tfm_alg_name(areq->base.tfm), ++ areq->cryptlen, ctx->keylen, m); ++ sgs = areq->src; ++ sgd = areq->dst; ++ ++ while (sgs && sgd && len) { ++ ivsize = crypto_skcipher_ivsize(tfm); ++ if (areq->iv && crypto_skcipher_ivsize(tfm) > 0) { ++ if (rctx->mode & RK2_CRYPTO_DEC) { ++ offset = sgs->length - ivsize; ++ scatterwalk_map_and_copy(rctx->backup_iv, sgs, ++ offset, ivsize, 0); ++ } ++ } ++ ++ dev_dbg(rkc->dev, "SG len=%u mode=%x ivsize=%u\n", sgs->length, m, ivsize); ++ ++ if (sgs == sgd) { ++ err = dma_map_sg(rkc->dev, sgs, 1, DMA_BIDIRECTIONAL); ++ if (err != 1) { ++ dev_err(rkc->dev, "Invalid sg number %d\n", err); ++ err = -EINVAL; ++ goto theend; ++ } ++ } else { ++ err = dma_map_sg(rkc->dev, sgs, 1, DMA_TO_DEVICE); ++ if (err != 1) { ++ dev_err(rkc->dev, "Invalid sg number %d\n", err); ++ err = -EINVAL; ++ goto theend; ++ } ++ err = dma_map_sg(rkc->dev, sgd, 1, DMA_FROM_DEVICE); ++ if (err != 1) { ++ dev_err(rkc->dev, "Invalid sg number %d\n", err); ++ err = -EINVAL; ++ dma_unmap_sg(rkc->dev, sgs, 1, DMA_TO_DEVICE); ++ goto theend; ++ } ++ } ++ err = 0; ++ writel(m, rkc->reg + RK2_CRYPTO_BC_CTL); ++ ++ if (algt->is_xts) { ++ for (i = 0; i < ctx->keylen / 8; i++) { ++ v = cpu_to_be32(rkey[i]); ++ writel(v, rkc->reg + RK2_CRYPTO_KEY0 + i * 4); ++ } ++ for (i = 0; i < (ctx->keylen / 8); i++) { ++ v = cpu_to_be32(rkey[i + ctx->keylen / 8]); ++ writel(v, rkc->reg + RK2_CRYPTO_CH4_KEY0 + i * 4); ++ } ++ } else { ++ for (i = 0; i < ctx->keylen / 4; i++) { ++ v = cpu_to_be32(rkey[i]); ++ writel(v, rkc->reg + RK2_CRYPTO_KEY0 + i * 4); ++ } ++ } ++ ++ if (ivsize) { ++ for (i = 0; i < ivsize / 4; i++) ++ writel(cpu_to_be32(riv[i]), ++ rkc->reg + RK2_CRYPTO_CH0_IV_0 + i * 4); ++ writel(ivsize, rkc->reg + RK2_CRYPTO_CH0_IV_LEN); ++ } ++ if (!sgs->length) { ++ sgs = sg_next(sgs); ++ sgd = sg_next(sgd); ++ continue; ++ } ++ ++ /* The hw support multiple descriptor, so why this driver use ++ * only one descriptor ? ++ * Using one descriptor per SG seems the way to do and it works ++ * but only when doing encryption. ++ * With decryption it always fail on second descriptor. ++ * Probably the HW dont know how to use IV. ++ */ ++ todo = min(sg_dma_len(sgs), len); ++ len -= todo; ++ dd->src_addr = sg_dma_address(sgs); ++ dd->src_len = todo; ++ dd->dst_addr = sg_dma_address(sgd); ++ dd->dst_len = todo; ++ dd->iv = 0; ++ dd->next = 1; ++ ++ dd->user = RK2_LLI_CIPHER_START | RK2_LLI_STRING_FIRST | RK2_LLI_STRING_LAST; ++ dd->dma_ctrl |= RK2_LLI_DMA_CTRL_DST_INT | RK2_LLI_DMA_CTRL_LAST; ++ ++ writel(RK2_CRYPTO_DMA_INT_LISTDONE | 0x7F, rkc->reg + RK2_CRYPTO_DMA_INT_EN); ++ ++ /*writel(0x00030000, rkc->reg + RK2_CRYPTO_FIFO_CTL);*/ ++ writel(rkc->t_phy, rkc->reg + RK2_CRYPTO_DMA_LLI_ADDR); ++ ++ reinit_completion(&rkc->complete); ++ rkc->status = 0; ++ ++ writel(RK2_CRYPTO_DMA_CTL_START | 1 << 16, rkc->reg + RK2_CRYPTO_DMA_CTL); ++ ++ wait_for_completion_interruptible_timeout(&rkc->complete, ++ msecs_to_jiffies(10000)); ++ if (sgs == sgd) { ++ dma_unmap_sg(rkc->dev, sgs, 1, DMA_BIDIRECTIONAL); ++ } else { ++ dma_unmap_sg(rkc->dev, sgs, 1, DMA_TO_DEVICE); ++ dma_unmap_sg(rkc->dev, sgd, 1, DMA_FROM_DEVICE); ++ } ++ ++ if (!rkc->status) { ++ dev_err(rkc->dev, "DMA timeout\n"); ++ rk2_print(rkc); ++ err = -EFAULT; ++ goto theend; ++ } ++ if (areq->iv && ivsize > 0) { ++ offset = sgd->length - ivsize; ++ if (rctx->mode & RK2_CRYPTO_DEC) { ++ memcpy(areq->iv, rctx->backup_iv, ivsize); ++ memzero_explicit(rctx->backup_iv, ivsize); ++ } else { ++ scatterwalk_map_and_copy(areq->iv, sgd, offset, ++ ivsize, 0); ++ } ++ } ++ sgs = sg_next(sgs); ++ sgd = sg_next(sgd); ++ } ++theend: ++ writel(0xffff0000, rkc->reg + RK2_CRYPTO_BC_CTL); ++ pm_runtime_put_autosuspend(rkc->dev); ++ ++ local_bh_disable(); ++ crypto_finalize_skcipher_request(engine, areq, err); ++ local_bh_enable(); ++ return 0; ++} ++ ++int rk2_cipher_tfm_init(struct crypto_skcipher *tfm) ++{ ++ struct rk2_cipher_ctx *ctx = crypto_skcipher_ctx(tfm); ++ const char *name = crypto_tfm_alg_name(&tfm->base); ++ struct skcipher_alg *alg = crypto_skcipher_alg(tfm); ++ struct rk2_crypto_template *algt = container_of(alg, struct rk2_crypto_template, alg.skcipher.base); ++ ++ ctx->fallback_tfm = crypto_alloc_skcipher(name, 0, CRYPTO_ALG_NEED_FALLBACK); ++ if (IS_ERR(ctx->fallback_tfm)) { ++ dev_err(algt->dev->dev, "ERROR: Cannot allocate fallback for %s %ld\n", ++ name, PTR_ERR(ctx->fallback_tfm)); ++ return PTR_ERR(ctx->fallback_tfm); ++ } ++ ++ dev_info(algt->dev->dev, "Fallback for %s is %s\n", ++ crypto_tfm_alg_driver_name(&tfm->base), ++ crypto_tfm_alg_driver_name(crypto_skcipher_tfm(ctx->fallback_tfm))); ++ ++ tfm->reqsize = sizeof(struct rk2_cipher_rctx) + ++ crypto_skcipher_reqsize(ctx->fallback_tfm); ++ ++ return 0; ++} ++ ++void rk2_cipher_tfm_exit(struct crypto_skcipher *tfm) ++{ ++ struct rk2_cipher_ctx *ctx = crypto_skcipher_ctx(tfm); ++ ++ memzero_explicit(ctx->key, ctx->keylen); ++ crypto_free_skcipher(ctx->fallback_tfm); ++} +-- +2.42.1 + diff --git a/patch/kernel/rockchip-rk3588-edge/0025-RK3588-Add-HW-RNG-Support.patch b/patch/kernel/rockchip-rk3588-edge/0025-RK3588-Add-HW-RNG-Support.patch new file mode 100644 index 0000000000..084d70e691 --- /dev/null +++ b/patch/kernel/rockchip-rk3588-edge/0025-RK3588-Add-HW-RNG-Support.patch @@ -0,0 +1,665 @@ +From 2162fb12380c68ea4fa5300ef7475d6852196b89 Mon Sep 17 00:00:00 2001 +From: Muhammed Efe Cetin +Date: Thu, 16 Nov 2023 17:49:42 +0300 +Subject: [PATCH 1/2] hwrng: rockchip: Add support for Rockchip HW RNG + +--- + drivers/char/hw_random/Kconfig | 13 + + drivers/char/hw_random/Makefile | 1 + + drivers/char/hw_random/rockchip-rng.c | 574 ++++++++++++++++++++++++++ + 3 files changed, 588 insertions(+) + create mode 100644 drivers/char/hw_random/rockchip-rng.c + +diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig +index 442c40efb200..11063cac5b36 100644 +--- a/drivers/char/hw_random/Kconfig ++++ b/drivers/char/hw_random/Kconfig +@@ -538,6 +538,19 @@ config HW_RANDOM_XIPHERA + To compile this driver as a module, choose M here: the + module will be called xiphera-trng. + ++config HW_RANDOM_ROCKCHIP ++ tristate "Rockchip Random Number Generator support" ++ depends on ARCH_ROCKCHIP ++ default HW_RANDOM ++ help ++ This driver provides kernel-side support for the Random Number ++ Generator hardware found on Rockchip cpus. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called rockchip-rng. ++ ++ If unsure, say Y. ++ + config HW_RANDOM_ARM_SMCCC_TRNG + tristate "Arm SMCCC TRNG firmware interface support" + depends on HAVE_ARM_SMCCC_DISCOVERY +diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile +index 32549a1186dc..fd3bbf6e08e1 100644 +--- a/drivers/char/hw_random/Makefile ++++ b/drivers/char/hw_random/Makefile +@@ -35,6 +35,7 @@ obj-$(CONFIG_HW_RANDOM_IPROC_RNG200) += iproc-rng200.o + obj-$(CONFIG_HW_RANDOM_ST) += st-rng.o + obj-$(CONFIG_HW_RANDOM_XGENE) += xgene-rng.o + obj-$(CONFIG_HW_RANDOM_STM32) += stm32-rng.o ++obj-$(CONFIG_HW_RANDOM_ROCKCHIP) += rockchip-rng.o + obj-$(CONFIG_HW_RANDOM_PIC32) += pic32-rng.o + obj-$(CONFIG_HW_RANDOM_MESON) += meson-rng.o + obj-$(CONFIG_HW_RANDOM_CAVIUM) += cavium-rng.o cavium-rng-vf.o +diff --git a/drivers/char/hw_random/rockchip-rng.c b/drivers/char/hw_random/rockchip-rng.c +new file mode 100644 +index 000000000000..f7a3a7f52cca +--- /dev/null ++++ b/drivers/char/hw_random/rockchip-rng.c +@@ -0,0 +1,574 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * rockchip-rng.c Random Number Generator driver for the Rockchip ++ * ++ * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd. ++ * Author: Lin Jinhan ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define _SBF(s, v) ((v) << (s)) ++#define HIWORD_UPDATE(val, mask, shift) \ ++ ((val) << (shift) | (mask) << ((shift) + 16)) ++ ++#define ROCKCHIP_AUTOSUSPEND_DELAY 100 ++#define ROCKCHIP_POLL_PERIOD_US 100 ++#define ROCKCHIP_POLL_TIMEOUT_US 50000 ++#define RK_MAX_RNG_BYTE (32) ++ ++/* start of CRYPTO V1 register define */ ++#define CRYPTO_V1_CTRL 0x0008 ++#define CRYPTO_V1_RNG_START BIT(8) ++#define CRYPTO_V1_RNG_FLUSH BIT(9) ++ ++#define CRYPTO_V1_TRNG_CTRL 0x0200 ++#define CRYPTO_V1_OSC_ENABLE BIT(16) ++#define CRYPTO_V1_TRNG_SAMPLE_PERIOD(x) (x) ++ ++#define CRYPTO_V1_TRNG_DOUT_0 0x0204 ++/* end of CRYPTO V1 register define */ ++ ++/* start of CRYPTO V2 register define */ ++#define CRYPTO_V2_RNG_DEFAULT_OFFSET 0x0400 ++#define CRYPTO_V2_RNG_CTL 0x0 ++#define CRYPTO_V2_RNG_64_BIT_LEN _SBF(4, 0x00) ++#define CRYPTO_V2_RNG_128_BIT_LEN _SBF(4, 0x01) ++#define CRYPTO_V2_RNG_192_BIT_LEN _SBF(4, 0x02) ++#define CRYPTO_V2_RNG_256_BIT_LEN _SBF(4, 0x03) ++#define CRYPTO_V2_RNG_FATESY_SOC_RING _SBF(2, 0x00) ++#define CRYPTO_V2_RNG_SLOWER_SOC_RING_0 _SBF(2, 0x01) ++#define CRYPTO_V2_RNG_SLOWER_SOC_RING_1 _SBF(2, 0x02) ++#define CRYPTO_V2_RNG_SLOWEST_SOC_RING _SBF(2, 0x03) ++#define CRYPTO_V2_RNG_ENABLE BIT(1) ++#define CRYPTO_V2_RNG_START BIT(0) ++#define CRYPTO_V2_RNG_SAMPLE_CNT 0x0004 ++#define CRYPTO_V2_RNG_DOUT_0 0x0010 ++/* end of CRYPTO V2 register define */ ++ ++/* start of TRNG_V1 register define */ ++/* TRNG is no longer subordinate to the Crypto module */ ++#define TRNG_V1_CTRL 0x0000 ++#define TRNG_V1_CTRL_NOP _SBF(0, 0x00) ++#define TRNG_V1_CTRL_RAND _SBF(0, 0x01) ++#define TRNG_V1_CTRL_SEED _SBF(0, 0x02) ++ ++#define TRNG_V1_STAT 0x0004 ++#define TRNG_V1_STAT_SEEDED BIT(9) ++#define TRNG_V1_STAT_GENERATING BIT(30) ++#define TRNG_V1_STAT_RESEEDING BIT(31) ++ ++#define TRNG_V1_MODE 0x0008 ++#define TRNG_V1_MODE_128_BIT _SBF(3, 0x00) ++#define TRNG_V1_MODE_256_BIT _SBF(3, 0x01) ++ ++#define TRNG_V1_IE 0x0010 ++#define TRNG_V1_IE_GLBL_EN BIT(31) ++#define TRNG_V1_IE_SEED_DONE_EN BIT(1) ++#define TRNG_V1_IE_RAND_RDY_EN BIT(0) ++ ++#define TRNG_V1_ISTAT 0x0014 ++#define TRNG_V1_ISTAT_RAND_RDY BIT(0) ++ ++/* RAND0 ~ RAND7 */ ++#define TRNG_V1_RAND0 0x0020 ++#define TRNG_V1_RAND7 0x003C ++ ++#define TRNG_V1_AUTO_RQSTS 0x0060 ++ ++#define TRNG_V1_VERSION 0x00F0 ++#define TRNG_v1_VERSION_CODE 0x46bc ++/* end of TRNG_V1 register define */ ++ ++/* start of RKRNG register define */ ++#define RKRNG_CTRL 0x0010 ++#define RKRNG_CTRL_INST_REQ BIT(0) ++#define RKRNG_CTRL_RESEED_REQ BIT(1) ++#define RKRNG_CTRL_TEST_REQ BIT(2) ++#define RKRNG_CTRL_SW_DRNG_REQ BIT(3) ++#define RKRNG_CTRL_SW_TRNG_REQ BIT(4) ++ ++#define RKRNG_STATE 0x0014 ++#define RKRNG_STATE_INST_ACK BIT(0) ++#define RKRNG_STATE_RESEED_ACK BIT(1) ++#define RKRNG_STATE_TEST_ACK BIT(2) ++#define RKRNG_STATE_SW_DRNG_ACK BIT(3) ++#define RKRNG_STATE_SW_TRNG_ACK BIT(4) ++ ++/* DRNG_DATA_0 ~ DNG_DATA_7 */ ++#define RKRNG_DRNG_DATA_0 0x0070 ++#define RKRNG_DRNG_DATA_7 0x008C ++ ++/* end of RKRNG register define */ ++ ++struct rk_rng_soc_data { ++ u32 default_offset; ++ ++ int (*rk_rng_init)(struct hwrng *rng); ++ int (*rk_rng_read)(struct hwrng *rng, void *buf, size_t max, bool wait); ++}; ++ ++struct rk_rng { ++ struct device *dev; ++ struct hwrng rng; ++ void __iomem *mem; ++ struct rk_rng_soc_data *soc_data; ++ int clk_num; ++ struct clk_bulk_data *clk_bulks; ++}; ++ ++static void rk_rng_writel(struct rk_rng *rng, u32 val, u32 offset) ++{ ++ __raw_writel(val, rng->mem + offset); ++} ++ ++static u32 rk_rng_readl(struct rk_rng *rng, u32 offset) ++{ ++ return __raw_readl(rng->mem + offset); ++} ++ ++static int rk_rng_init(struct hwrng *rng) ++{ ++ int ret; ++ struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); ++ ++ dev_dbg(rk_rng->dev, "clk_bulk_prepare_enable.\n"); ++ ++ ret = clk_bulk_prepare_enable(rk_rng->clk_num, rk_rng->clk_bulks); ++ if (ret < 0) { ++ dev_err(rk_rng->dev, "failed to enable clks %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void rk_rng_cleanup(struct hwrng *rng) ++{ ++ struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); ++ ++ dev_dbg(rk_rng->dev, "clk_bulk_disable_unprepare.\n"); ++ clk_bulk_disable_unprepare(rk_rng->clk_num, rk_rng->clk_bulks); ++} ++ ++static int rk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) ++{ ++ int ret; ++ int read_len = 0; ++ struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); ++ ++ if (!rk_rng->soc_data->rk_rng_read) ++ return -EFAULT; ++ ++ ret = pm_runtime_get_sync(rk_rng->dev); ++ if (ret < 0) { ++ pm_runtime_put_noidle(rk_rng->dev); ++ return ret; ++ } ++ ++ ret = 0; ++ while (max > ret) { ++ read_len = rk_rng->soc_data->rk_rng_read(rng, buf + ret, ++ max - ret, wait); ++ if (read_len < 0) { ++ ret = read_len; ++ break; ++ } ++ ret += read_len; ++ } ++ ++ pm_runtime_mark_last_busy(rk_rng->dev); ++ pm_runtime_put_sync_autosuspend(rk_rng->dev); ++ ++ return ret; ++} ++ ++static void rk_rng_read_regs(struct rk_rng *rng, u32 offset, void *buf, ++ size_t size) ++{ ++ u32 i; ++ ++ for (i = 0; i < size; i += 4) ++ *(u32 *)(buf + i) = be32_to_cpu(rk_rng_readl(rng, offset + i)); ++} ++ ++static int crypto_v1_read(struct hwrng *rng, void *buf, size_t max, bool wait) ++{ ++ int ret = 0; ++ u32 reg_ctrl = 0; ++ struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); ++ ++ /* enable osc_ring to get entropy, sample period is set as 100 */ ++ reg_ctrl = CRYPTO_V1_OSC_ENABLE | CRYPTO_V1_TRNG_SAMPLE_PERIOD(100); ++ rk_rng_writel(rk_rng, reg_ctrl, CRYPTO_V1_TRNG_CTRL); ++ ++ reg_ctrl = HIWORD_UPDATE(CRYPTO_V1_RNG_START, CRYPTO_V1_RNG_START, 0); ++ ++ rk_rng_writel(rk_rng, reg_ctrl, CRYPTO_V1_CTRL); ++ ++ ret = read_poll_timeout(rk_rng_readl, reg_ctrl, ++ !(reg_ctrl & CRYPTO_V1_RNG_START), ++ ROCKCHIP_POLL_PERIOD_US, ++ ROCKCHIP_POLL_TIMEOUT_US, false, ++ rk_rng, CRYPTO_V1_CTRL); ++ ++ if (ret < 0) ++ goto out; ++ ++ ret = min_t(size_t, max, RK_MAX_RNG_BYTE); ++ ++ rk_rng_read_regs(rk_rng, CRYPTO_V1_TRNG_DOUT_0, buf, ret); ++ ++out: ++ /* close TRNG */ ++ rk_rng_writel(rk_rng, HIWORD_UPDATE(0, CRYPTO_V1_RNG_START, 0), ++ CRYPTO_V1_CTRL); ++ ++ return ret; ++} ++ ++static int crypto_v2_read(struct hwrng *rng, void *buf, size_t max, bool wait) ++{ ++ int ret = 0; ++ u32 reg_ctrl = 0; ++ struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); ++ ++ /* enable osc_ring to get entropy, sample period is set as 100 */ ++ rk_rng_writel(rk_rng, 100, CRYPTO_V2_RNG_SAMPLE_CNT); ++ ++ reg_ctrl |= CRYPTO_V2_RNG_256_BIT_LEN; ++ reg_ctrl |= CRYPTO_V2_RNG_SLOWER_SOC_RING_0; ++ reg_ctrl |= CRYPTO_V2_RNG_ENABLE; ++ reg_ctrl |= CRYPTO_V2_RNG_START; ++ ++ rk_rng_writel(rk_rng, HIWORD_UPDATE(reg_ctrl, 0xffff, 0), ++ CRYPTO_V2_RNG_CTL); ++ ++ ret = read_poll_timeout(rk_rng_readl, reg_ctrl, ++ !(reg_ctrl & CRYPTO_V2_RNG_START), ++ ROCKCHIP_POLL_PERIOD_US, ++ ROCKCHIP_POLL_TIMEOUT_US, false, ++ rk_rng, CRYPTO_V2_RNG_CTL); ++ if (ret < 0) ++ goto out; ++ ++ ret = min_t(size_t, max, RK_MAX_RNG_BYTE); ++ ++ rk_rng_read_regs(rk_rng, CRYPTO_V2_RNG_DOUT_0, buf, ret); ++ ++out: ++ /* close TRNG */ ++ rk_rng_writel(rk_rng, HIWORD_UPDATE(0, 0xffff, 0), CRYPTO_V2_RNG_CTL); ++ ++ return ret; ++} ++ ++static int trng_v1_init(struct hwrng *rng) ++{ ++ int ret; ++ uint32_t auto_reseed_cnt = 1000; ++ uint32_t reg_ctrl, status, version; ++ struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); ++ ++ version = rk_rng_readl(rk_rng, TRNG_V1_VERSION); ++ if (version != TRNG_v1_VERSION_CODE) { ++ dev_err(rk_rng->dev, ++ "wrong trng version, expected = %08x, actual = %08x\n", ++ TRNG_V1_VERSION, version); ++ ret = -EFAULT; ++ goto exit; ++ } ++ ++ status = rk_rng_readl(rk_rng, TRNG_V1_STAT); ++ ++ /* TRNG should wait RAND_RDY triggered if it is busy or not seeded */ ++ if (!(status & TRNG_V1_STAT_SEEDED) || ++ (status & TRNG_V1_STAT_GENERATING) || ++ (status & TRNG_V1_STAT_RESEEDING)) { ++ uint32_t mask = TRNG_V1_STAT_SEEDED | ++ TRNG_V1_STAT_GENERATING | ++ TRNG_V1_STAT_RESEEDING; ++ ++ udelay(10); ++ ++ /* wait for GENERATING and RESEEDING flag to clear */ ++ read_poll_timeout(rk_rng_readl, reg_ctrl, ++ (reg_ctrl & mask) == TRNG_V1_STAT_SEEDED, ++ ROCKCHIP_POLL_PERIOD_US, ++ ROCKCHIP_POLL_TIMEOUT_US, false, ++ rk_rng, TRNG_V1_STAT); ++ } ++ ++ /* clear ISTAT flag because trng may auto reseeding when power on */ ++ reg_ctrl = rk_rng_readl(rk_rng, TRNG_V1_ISTAT); ++ rk_rng_writel(rk_rng, reg_ctrl, TRNG_V1_ISTAT); ++ ++ /* auto reseed after (auto_reseed_cnt * 16) byte rand generate */ ++ rk_rng_writel(rk_rng, auto_reseed_cnt, TRNG_V1_AUTO_RQSTS); ++ ++ ret = 0; ++exit: ++ ++ return ret; ++} ++ ++static int trng_v1_read(struct hwrng *rng, void *buf, size_t max, bool wait) ++{ ++ int ret = 0; ++ u32 reg_ctrl = 0; ++ struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); ++ ++ /* clear ISTAT anyway */ ++ reg_ctrl = rk_rng_readl(rk_rng, TRNG_V1_ISTAT); ++ rk_rng_writel(rk_rng, reg_ctrl, TRNG_V1_ISTAT); ++ ++ /* generate 256bit random */ ++ rk_rng_writel(rk_rng, TRNG_V1_MODE_256_BIT, TRNG_V1_MODE); ++ rk_rng_writel(rk_rng, TRNG_V1_CTRL_RAND, TRNG_V1_CTRL); ++ ++ /* ++ * Generate2 56 bit random data will cost 1024 clock cycles. ++ * Estimated at 150M RNG module frequency, it takes 6.7 microseconds. ++ */ ++ udelay(10); ++ reg_ctrl = rk_rng_readl(rk_rng, TRNG_V1_ISTAT); ++ if (!(reg_ctrl & TRNG_V1_ISTAT_RAND_RDY)) { ++ /* wait RAND_RDY triggered */ ++ ret = read_poll_timeout(rk_rng_readl, reg_ctrl, ++ (reg_ctrl & TRNG_V1_ISTAT_RAND_RDY), ++ ROCKCHIP_POLL_PERIOD_US, ++ ROCKCHIP_POLL_TIMEOUT_US, false, ++ rk_rng, TRNG_V1_ISTAT); ++ if (ret < 0) ++ goto out; ++ } ++ ++ ret = min_t(size_t, max, RK_MAX_RNG_BYTE); ++ ++ rk_rng_read_regs(rk_rng, TRNG_V1_RAND0, buf, ret); ++ ++ /* clear all status flag */ ++ rk_rng_writel(rk_rng, reg_ctrl, TRNG_V1_ISTAT); ++out: ++ /* close TRNG */ ++ rk_rng_writel(rk_rng, TRNG_V1_CTRL_NOP, TRNG_V1_CTRL); ++ ++ return ret; ++} ++ ++static int rkrng_init(struct hwrng *rng) ++{ ++ struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); ++ u32 reg = 0; ++ ++ rk_rng_writel(rk_rng, HIWORD_UPDATE(0, 0xffff, 0), RKRNG_CTRL); ++ ++ reg = rk_rng_readl(rk_rng, RKRNG_STATE); ++ rk_rng_writel(rk_rng, reg, RKRNG_STATE); ++ ++ return 0; ++} ++ ++static int rkrng_read(struct hwrng *rng, void *buf, size_t max, bool wait) ++{ ++ struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); ++ u32 reg_ctrl = 0; ++ int ret; ++ ++ reg_ctrl = RKRNG_CTRL_SW_DRNG_REQ; ++ ++ rk_rng_writel(rk_rng, HIWORD_UPDATE(reg_ctrl, 0xffff, 0), RKRNG_CTRL); ++ ++ ret = readl_poll_timeout(rk_rng->mem + RKRNG_STATE, reg_ctrl, ++ (reg_ctrl & RKRNG_STATE_SW_DRNG_ACK), ++ ROCKCHIP_POLL_PERIOD_US, ++ ROCKCHIP_POLL_TIMEOUT_US); ++ ++ if (ret) ++ goto exit; ++ ++ rk_rng_writel(rk_rng, reg_ctrl, RKRNG_STATE); ++ ++ ret = min_t(size_t, max, RK_MAX_RNG_BYTE); ++ ++ rk_rng_read_regs(rk_rng, RKRNG_DRNG_DATA_0, buf, ret); ++ ++exit: ++ /* close TRNG */ ++ rk_rng_writel(rk_rng, HIWORD_UPDATE(0, 0xffff, 0), RKRNG_CTRL); ++ ++ return ret; ++} ++ ++static const struct rk_rng_soc_data crypto_v1_soc_data = { ++ .default_offset = 0, ++ ++ .rk_rng_read = crypto_v1_read, ++}; ++ ++static const struct rk_rng_soc_data crypto_v2_soc_data = { ++ .default_offset = CRYPTO_V2_RNG_DEFAULT_OFFSET, ++ ++ .rk_rng_read = crypto_v2_read, ++}; ++ ++static const struct rk_rng_soc_data trng_v1_soc_data = { ++ .default_offset = 0, ++ ++ .rk_rng_init = trng_v1_init, ++ .rk_rng_read = trng_v1_read, ++}; ++ ++static const struct rk_rng_soc_data rkrng_soc_data = { ++ .default_offset = 0, ++ ++ .rk_rng_init = rkrng_init, ++ .rk_rng_read = rkrng_read, ++}; ++ ++static const struct of_device_id rk_rng_dt_match[] = { ++ { ++ .compatible = "rockchip,cryptov1-rng", ++ .data = (void *)&crypto_v1_soc_data, ++ }, ++ { ++ .compatible = "rockchip,cryptov2-rng", ++ .data = (void *)&crypto_v2_soc_data, ++ }, ++ { ++ .compatible = "rockchip,trngv1", ++ .data = (void *)&trng_v1_soc_data, ++ }, ++ { ++ .compatible = "rockchip,rkrng", ++ .data = (void *)&rkrng_soc_data, ++ }, ++ { }, ++}; ++ ++MODULE_DEVICE_TABLE(of, rk_rng_dt_match); ++ ++static int rk_rng_probe(struct platform_device *pdev) ++{ ++ int ret; ++ struct rk_rng *rk_rng; ++ struct device_node *np = pdev->dev.of_node; ++ const struct of_device_id *match; ++ resource_size_t map_size; ++ ++ dev_dbg(&pdev->dev, "probing...\n"); ++ rk_rng = devm_kzalloc(&pdev->dev, sizeof(struct rk_rng), GFP_KERNEL); ++ if (!rk_rng) ++ return -ENOMEM; ++ ++ match = of_match_node(rk_rng_dt_match, np); ++ rk_rng->soc_data = (struct rk_rng_soc_data *)match->data; ++ ++ rk_rng->dev = &pdev->dev; ++ rk_rng->rng.name = "rockchip"; ++#ifndef CONFIG_PM ++ rk_rng->rng.init = rk_rng_init; ++ rk_rng->rng.cleanup = rk_rng_cleanup, ++#endif ++ rk_rng->rng.read = rk_rng_read; ++ rk_rng->rng.quality = 999; ++ ++ rk_rng->mem = devm_of_iomap(&pdev->dev, pdev->dev.of_node, 0, &map_size); ++ if (IS_ERR(rk_rng->mem)) ++ return PTR_ERR(rk_rng->mem); ++ ++ /* compatible with crypto v2 module */ ++ /* ++ * With old dtsi configurations, the RNG base was equal to the crypto ++ * base, so both drivers could not be enabled at the same time. ++ * RNG base = CRYPTO base + RNG offset ++ * (Since RK356X, RNG module is no longer belongs to CRYPTO module) ++ * ++ * With new dtsi configurations, CRYPTO regs is divided into two parts ++ * |---cipher---|---rng---|---pka---|, and RNG base is real RNG base. ++ * RNG driver and CRYPTO driver could be enabled at the same time. ++ */ ++ if (map_size > rk_rng->soc_data->default_offset) ++ rk_rng->mem += rk_rng->soc_data->default_offset; ++ ++ rk_rng->clk_num = devm_clk_bulk_get_all(&pdev->dev, &rk_rng->clk_bulks); ++ if (rk_rng->clk_num < 0) { ++ dev_err(&pdev->dev, "failed to get clks property\n"); ++ return -ENODEV; ++ } ++ ++ platform_set_drvdata(pdev, rk_rng); ++ ++ pm_runtime_set_autosuspend_delay(&pdev->dev, ++ ROCKCHIP_AUTOSUSPEND_DELAY); ++ pm_runtime_use_autosuspend(&pdev->dev); ++ pm_runtime_enable(&pdev->dev); ++ ++ ret = devm_hwrng_register(&pdev->dev, &rk_rng->rng); ++ if (ret) { ++ pm_runtime_dont_use_autosuspend(&pdev->dev); ++ pm_runtime_disable(&pdev->dev); ++ } ++ ++ /* for some platform need hardware operation when probe */ ++ if (rk_rng->soc_data->rk_rng_init) { ++ pm_runtime_get_sync(rk_rng->dev); ++ ++ ret = rk_rng->soc_data->rk_rng_init(&rk_rng->rng); ++ ++ pm_runtime_mark_last_busy(rk_rng->dev); ++ pm_runtime_put_sync_autosuspend(rk_rng->dev); ++ } ++ ++ return ret; ++} ++ ++#ifdef CONFIG_PM ++static int rk_rng_runtime_suspend(struct device *dev) ++{ ++ struct rk_rng *rk_rng = dev_get_drvdata(dev); ++ ++ rk_rng_cleanup(&rk_rng->rng); ++ ++ return 0; ++} ++ ++static int rk_rng_runtime_resume(struct device *dev) ++{ ++ struct rk_rng *rk_rng = dev_get_drvdata(dev); ++ ++ return rk_rng_init(&rk_rng->rng); ++} ++ ++static const struct dev_pm_ops rk_rng_pm_ops = { ++ SET_RUNTIME_PM_OPS(rk_rng_runtime_suspend, ++ rk_rng_runtime_resume, NULL) ++ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, ++ pm_runtime_force_resume) ++}; ++ ++#endif ++ ++static struct platform_driver rk_rng_driver = { ++ .driver = { ++ .name = "rockchip-rng", ++#ifdef CONFIG_PM ++ .pm = &rk_rng_pm_ops, ++#endif ++ .of_match_table = rk_rng_dt_match, ++ }, ++ .probe = rk_rng_probe, ++}; ++ ++module_platform_driver(rk_rng_driver); ++ ++MODULE_DESCRIPTION("ROCKCHIP H/W Random Number Generator driver"); ++MODULE_AUTHOR("Lin Jinhan "); ++MODULE_LICENSE("GPL v2"); +-- +2.42.1 + + +From 80c1c5fd75a0057d19c739c1fae76c63fd27c220 Mon Sep 17 00:00:00 2001 +From: Muhammed Efe Cetin +Date: Thu, 16 Nov 2023 17:52:35 +0300 +Subject: [PATCH 2/2] arm64: dts: Add HW RNG support to RK3588S + +--- + arch/arm64/boot/dts/rockchip/rk3588s.dtsi | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi +index a89577fb801e..ed6197dd9267 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi +@@ -2250,6 +2250,16 @@ crypto: crypto@fe370000 { + status = "okay"; + }; + ++ rng: rng@fe378000 { ++ compatible = "rockchip,trngv1"; ++ reg = <0x0 0xfe378000 0x0 0x200>; ++ interrupts = ; ++ clocks = <&scmi_clk SCMI_HCLK_SECURE_NS>; ++ clock-names = "hclk_trng"; ++ resets = <&scmi_reset SRST_H_TRNG_NS>; ++ reset-names = "reset"; ++ }; ++ + i2s0_8ch: i2s@fe470000 { + compatible = "rockchip,rk3588-i2s-tdm"; + reg = <0x0 0xfe470000 0x0 0x1000>; +-- +2.42.1 + diff --git a/patch/kernel/rockchip-rk3588-edge/0025-arm64-dts-rockchip-rk3588-add-sfc-node.patch b/patch/kernel/rockchip-rk3588-edge/0025-arm64-dts-rockchip-rk3588-add-sfc-node.patch deleted file mode 100644 index 7436880009..0000000000 --- a/patch/kernel/rockchip-rk3588-edge/0025-arm64-dts-rockchip-rk3588-add-sfc-node.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 951f73458d984d485d73f64a00d8dd041187222a Mon Sep 17 00:00:00 2001 -From: Muhammed Efe Cetin -Date: Sat, 29 Jul 2023 21:41:14 +0300 -Subject: [PATCH 1/1] arm64: dts: rockchip: rk3588: add sfc node - ---- - arch/arm64/boot/dts/rockchip/rk3588s.dtsi | 13 +++++++++++++ - 1 file changed, 13 insertions(+) - -diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi -index 5dde719c9cd7..7902c638a5be 100644 ---- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi -+++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi -@@ -2251,6 +2251,19 @@ wdt: watchdog@feaf0000 { - interrupts = ; - }; - -+ sfc: spi@fe2b0000 { -+ compatible = "rockchip,sfc"; -+ reg = <0x0 0xfe2b0000 0x0 0x4000>; -+ interrupts = ; -+ clocks = <&cru SCLK_SFC>, <&cru HCLK_SFC>; -+ clock-names = "clk_sfc", "hclk_sfc"; -+ assigned-clocks = <&cru SCLK_SFC>; -+ assigned-clock-rates = <100000000>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ status = "disabled"; -+ }; -+ - spi0: spi@feb00000 { - compatible = "rockchip,rk3588-spi", "rockchip,rk3066-spi"; - reg = <0x0 0xfeb00000 0x0 0x1000>; --- -2.41.0 - diff --git a/patch/kernel/rockchip-rk3588-edge/0026-Add-missing-nodes-to-Orange-Pi-5.patch b/patch/kernel/rockchip-rk3588-edge/0026-Add-missing-nodes-to-Orange-Pi-5.patch new file mode 100644 index 0000000000..d74bea1a41 --- /dev/null +++ b/patch/kernel/rockchip-rk3588-edge/0026-Add-missing-nodes-to-Orange-Pi-5.patch @@ -0,0 +1,322 @@ +From bdf64687d6ecd6fddbceb840da4accf636739633 Mon Sep 17 00:00:00 2001 +From: Muhammed Efe Cetin +Date: Thu, 16 Nov 2023 18:09:07 +0300 +Subject: [PATCH 1/2] arm64: dts: Add missing nodes to Orange Pi 5 + +--- + .../boot/dts/rockchip/rk3588s-orangepi-5.dts | 214 +++++++++++++++++- + 1 file changed, 213 insertions(+), 1 deletion(-) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-orangepi-5.dts b/arch/arm64/boot/dts/rockchip/rk3588s-orangepi-5.dts +index 8f399c4317bd..8c834ced3686 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3588s-orangepi-5.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3588s-orangepi-5.dts +@@ -6,6 +6,7 @@ + #include + #include + #include ++#include + #include "rk3588s.dtsi" + + / { +@@ -47,6 +48,46 @@ led-1 { + }; + }; + ++ analog-sound { ++ compatible = "simple-audio-card"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&hp_detect>; ++ simple-audio-card,name = "Orange Pi 5 Audio"; ++ simple-audio-card,bitclock-master = <&masterdai>; ++ simple-audio-card,format = "i2s"; ++ simple-audio-card,frame-master = <&masterdai>; ++ simple-audio-card,hp-det-gpio = <&gpio1 RK_PD5 GPIO_ACTIVE_LOW>; ++ simple-audio-card,mclk-fs = <256>; ++ simple-audio-card,pin-switches = "Headphones", "Speaker"; ++ simple-audio-card,routing = ++ "Speaker Amplifier INL", "LOUT2", ++ "Speaker Amplifier INR", "ROUT2", ++ "Speaker", "Speaker Amplifier OUTL", ++ "Speaker", "Speaker Amplifier OUTR", ++ "Headphones Amplifier INL", "LOUT1", ++ "Headphones Amplifier INR", "ROUT1", ++ "Headphones", "Headphones Amplifier OUTL", ++ "Headphones", "Headphones Amplifier OUTR", ++ "LINPUT1", "Onboard Microphone", ++ "RINPUT1", "Onboard Microphone", ++ "LINPUT2", "Microphone Jack", ++ "RINPUT2", "Microphone Jack"; ++ simple-audio-card,widgets = ++ "Microphone", "Microphone Jack", ++ "Microphone", "Onboard Microphone", ++ "Headphone", "Headphones", ++ "Speaker", "Speaker"; ++ ++ simple-audio-card,cpu { ++ sound-dai = <&i2s1_8ch>; ++ }; ++ ++ masterdai: simple-audio-card,codec { ++ sound-dai = <&es8388>; ++ system-clock-frequency = <12288000>; ++ }; ++ }; ++ + vbus_typec: vbus-typec-regulator { + compatible = "regulator-fixed"; + enable-active-high; +@@ -102,34 +143,47 @@ &combphy2_psu { + + &cpu_b0 { + cpu-supply = <&vdd_cpu_big0_s0>; ++ mem-supply = <&vdd_cpu_big0_s0>; + }; + + &cpu_b1 { + cpu-supply = <&vdd_cpu_big0_s0>; ++ mem-supply = <&vdd_cpu_big0_s0>; + }; + + &cpu_b2 { + cpu-supply = <&vdd_cpu_big1_s0>; ++ mem-supply = <&vdd_cpu_big1_s0>; + }; + + &cpu_b3 { + cpu-supply = <&vdd_cpu_big1_s0>; ++ mem-supply = <&vdd_cpu_big1_s0>; + }; + + &cpu_l0 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &cpu_l1 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &cpu_l2 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &cpu_l3 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; ++}; ++ ++&display_subsystem { ++ clocks = <&hdptxphy_hdmi_clk0>; ++ clock-names = "hdmi0_phy_pll"; + }; + + &gmac1 { +@@ -223,6 +277,75 @@ hym8563: rtc@51 { + interrupts = ; + wakeup-source; + }; ++ ++ es8388: audio-codec@10 { ++ compatible = "everest,es8388"; ++ reg = <0x10>; ++ clocks = <&cru I2S1_8CH_MCLKOUT>; ++ assigned-clocks = <&cru I2S1_8CH_MCLKOUT>; ++ assigned-clock-rates = <12288000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2s1m0_mclk>; ++ #sound-dai-cells = <0>; ++ }; ++ ++ usbc0: usb-typec@22 { ++ compatible = "fcs,fusb302"; ++ reg = <0x22>; ++ interrupt-parent = <&gpio0>; ++ interrupts = ; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&usbc0_int>; ++ vbus-supply = <&vbus_typec>; ++ ++ usb_con: connector { ++ compatible = "usb-c-connector"; ++ label = "USB-C"; ++ data-role = "dual"; ++ power-role = "dual"; ++ try-power-role = "source"; ++ op-sink-microwatt = <1000000>; ++ sink-pdos = ; ++ source-pdos = ; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ reg = <0>; ++ usbc0_hs: endpoint { ++ remote-endpoint = <&usb_host0_xhci_drd_sw>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ usbc0_ss: endpoint { ++ remote-endpoint = <&usbdp_phy0_typec_ss>; ++ }; ++ }; ++ ++ port@2 { ++ reg = <2>; ++ usbc0_sbu: endpoint { ++ remote-endpoint = <&usbdp_phy0_typec_sbu>; ++ }; ++ }; ++ }; ++ }; ++ }; ++}; ++ ++&i2s1_8ch { ++ rockchip,i2s-tx-route = <3 2 1 0>; ++ rockchip,i2s-rx-route = <1 3 2 0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2s1m0_sclk ++ &i2s1m0_lrck ++ &i2s1m0_sdi1 ++ &i2s1m0_sdo3>; ++ status = "okay"; + }; + + &mdio1 { +@@ -263,6 +386,12 @@ typec5v_pwren: typec5v-pwren { + rockchip,pins = <3 RK_PC0 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; ++ ++ sound { ++ hp_detect: hp-detect { ++ rockchip,pins = <1 RK_PD5 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ }; + }; + + &saradc { +@@ -363,7 +492,7 @@ regulator-state-mem { + }; + }; + +- vdd_cpu_lit_s0: dcdc-reg2 { ++ vdd_cpu_lit_s0: vdd_cpu_lit_mem_s0: dcdc-reg2 { + regulator-name = "vdd_cpu_lit_s0"; + regulator-always-on; + regulator-boot-on; +@@ -624,6 +753,14 @@ &tsadc { + status = "okay"; + }; + ++&u2phy0 { ++ status = "okay"; ++}; ++ ++&u2phy0_otg { ++ status = "okay"; ++}; ++ + &u2phy2 { + status = "okay"; + }; +@@ -649,10 +786,56 @@ &usb_host0_ehci { + status = "okay"; + }; + ++&usbdp_phy0 { ++ orientation-switch; ++ mode-switch; ++ svid = <0xff01>; ++ sbu1-dc-gpios = <&gpio4 RK_PA5 GPIO_ACTIVE_HIGH>; ++ sbu2-dc-gpios = <&gpio4 RK_PA7 GPIO_ACTIVE_HIGH>; ++ ++ port { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ usbdp_phy0_typec_ss: endpoint@0 { ++ reg = <0>; ++ remote-endpoint = <&usbc0_ss>; ++ }; ++ ++ usbdp_phy0_typec_sbu: endpoint@1 { ++ reg = <1>; ++ remote-endpoint = <&usbc0_sbu>; ++ }; ++ }; ++}; ++ ++&usbdp_phy0_dp { ++ status = "okay"; ++}; ++ ++&usbdp_phy0_u3 { ++ status = "okay"; ++}; ++ + &usb_host0_ohci { + status = "okay"; + }; + ++&usb_host0_xhci { ++ usb-role-switch; ++ dr_mode = "otg"; ++ status = "okay"; ++ ++ port { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ usb_host0_xhci_drd_sw: endpoint { ++ remote-endpoint = <&usbc0_hs>; ++ }; ++ }; ++}; ++ + &usb_host1_ehci { + status = "okay"; + }; +@@ -660,3 +843,32 @@ &usb_host1_ehci { + &usb_host1_ohci { + status = "okay"; + }; ++ ++&usb_host2_xhci { ++ dr_mode = "host"; ++ status = "okay"; ++}; ++ ++&hdmi0 { ++ status = "okay"; ++}; ++ ++&hdmi0_in_vp0 { ++ status = "okay"; ++}; ++ ++&hdptxphy_hdmi0 { ++ status = "okay"; ++}; ++ ++&hdptxphy_hdmi_clk0 { ++ status = "okay"; ++}; ++ ++&vop_mmu { ++ status = "okay"; ++}; ++ ++&vop { ++ status = "okay"; ++}; +-- +2.42.1 + diff --git a/patch/kernel/rockchip-rk3588-edge/0027-arm64-dts-Add-missing-nodes-to-Orange-Pi-5-Plus.patch b/patch/kernel/rockchip-rk3588-edge/0027-arm64-dts-Add-missing-nodes-to-Orange-Pi-5-Plus.patch new file mode 100644 index 0000000000..795f3470b8 --- /dev/null +++ b/patch/kernel/rockchip-rk3588-edge/0027-arm64-dts-Add-missing-nodes-to-Orange-Pi-5-Plus.patch @@ -0,0 +1,282 @@ +From 87ce7cdd0d7b56f1826173204add338e25aa8037 Mon Sep 17 00:00:00 2001 +From: Muhammed Efe Cetin +Date: Thu, 16 Nov 2023 18:15:09 +0300 +Subject: [PATCH 2/2] arm64: dts: Add missing nodes to Orange Pi 5 Plus + +--- + .../dts/rockchip/rk3588-orangepi-5-plus.dts | 178 +++++++++++++++++- + 1 file changed, 177 insertions(+), 1 deletion(-) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3588-orangepi-5-plus.dts b/arch/arm64/boot/dts/rockchip/rk3588-orangepi-5-plus.dts +index 298c183d6f4f..8368b3117351 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3588-orangepi-5-plus.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3588-orangepi-5-plus.dts +@@ -200,6 +200,18 @@ vcc5v0_sys: vcc5v0-sys-regulator { + regulator-max-microvolt = <5000000>; + }; + ++ vbus5v0_typec: vbus5v0-typec-regulator { ++ compatible = "regulator-fixed"; ++ enable-active-high; ++ gpio = <&gpio4 RK_PB0 GPIO_ACTIVE_HIGH>; ++ regulator-name = "vbus5v0_typec"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&typec5v_pwren>; ++ vin-supply = <&vcc5v0_sys>; ++ }; ++ + vcc5v0_usb20: vcc5v0-usb20-regulator { + compatible = "regulator-fixed"; + enable-active-high; +@@ -227,34 +239,47 @@ &combphy2_psu { + + &cpu_b0 { + cpu-supply = <&vdd_cpu_big0_s0>; ++ mem-supply = <&vdd_cpu_big0_s0>; + }; + + &cpu_b1 { + cpu-supply = <&vdd_cpu_big0_s0>; ++ mem-supply = <&vdd_cpu_big0_s0>; + }; + + &cpu_b2 { + cpu-supply = <&vdd_cpu_big1_s0>; ++ mem-supply = <&vdd_cpu_big1_s0>; + }; + + &cpu_b3 { + cpu-supply = <&vdd_cpu_big1_s0>; ++ mem-supply = <&vdd_cpu_big1_s0>; + }; + + &cpu_l0 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &cpu_l1 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &cpu_l2 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; + }; + + &cpu_l3 { + cpu-supply = <&vdd_cpu_lit_s0>; ++ mem-supply = <&vdd_cpu_lit_mem_s0>; ++}; ++ ++&display_subsystem { ++ clocks = <&hdptxphy_hdmi_clk0>; ++ clock-names = "hdmi0_phy_pll"; + }; + + &i2c0 { +@@ -312,6 +337,53 @@ hym8563: rtc@51 { + pinctrl-0 = <&hym8563_int>; + wakeup-source; + }; ++ ++ usbc0: usb-typec@22 { ++ compatible = "fcs,fusb302"; ++ reg = <0x22>; ++ interrupt-parent = <&gpio0>; ++ interrupts = ; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&usbc0_int>; ++ vbus-supply = <&vbus5v0_typec>; ++ ++ usb_con: connector { ++ compatible = "usb-c-connector"; ++ label = "USB-C"; ++ data-role = "dual"; ++ power-role = "dual"; ++ try-power-role = "source"; ++ op-sink-microwatt = <1000000>; ++ sink-pdos = ; ++ source-pdos = ; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ reg = <0>; ++ usbc0_hs: endpoint { ++ remote-endpoint = <&usb_host0_xhci_drd_sw>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ usbc0_ss: endpoint { ++ remote-endpoint = <&usbdp_phy0_typec_ss>; ++ }; ++ }; ++ ++ port@2 { ++ reg = <2>; ++ usbc0_sbu: endpoint { ++ remote-endpoint = <&usbdp_phy0_typec_sbu>; ++ }; ++ }; ++ }; ++ }; ++ }; + }; + + &i2c7 { +@@ -409,6 +481,14 @@ hp_detect: hp-detect { + }; + + usb { ++ typec5v_pwren: typec5v-pwren { ++ rockchip,pins = <4 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ ++ usbc0_int: usbc0-int { ++ rockchip,pins = <0 RK_PD3 RK_FUNC_GPIO &pcfg_pull_up>; ++ }; ++ + vcc5v0_usb20_en: vcc5v0-usb20-en { + rockchip,pins = <3 RK_PB7 RK_FUNC_GPIO &pcfg_pull_none>; + }; +@@ -536,7 +616,7 @@ regulator-state-mem { + }; + }; + +- vdd_cpu_lit_s0: dcdc-reg2 { ++ vdd_cpu_lit_s0: vdd_cpu_lit_mem_s0: dcdc-reg2 { + regulator-name = "vdd_cpu_lit_s0"; + regulator-always-on; + regulator-boot-on; +@@ -803,6 +883,22 @@ &tsadc { + status = "okay"; + }; + ++&u2phy0 { ++ status = "okay"; ++}; ++ ++&u2phy0_otg { ++ status = "okay"; ++}; ++ ++&u2phy1 { ++ status = "okay"; ++}; ++ ++&u2phy1_otg { ++ status = "okay"; ++}; ++ + &u2phy2 { + status = "okay"; + }; +@@ -831,6 +927,44 @@ &uart9 { + status = "okay"; + }; + ++&usbdp_phy0 { ++ orientation-switch; ++ mode-switch; ++ sbu1-dc-gpios = <&gpio4 RK_PA6 GPIO_ACTIVE_HIGH>; ++ sbu2-dc-gpios = <&gpio4 RK_PA7 GPIO_ACTIVE_HIGH>; ++ status = "okay"; ++ svid = <0xff01>; ++ ++ port { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ usbdp_phy0_typec_ss: endpoint@0 { ++ reg = <0>; ++ remote-endpoint = <&usbc0_ss>; ++ }; ++ ++ usbdp_phy0_typec_sbu: endpoint@1 { ++ reg = <1>; ++ remote-endpoint = <&usbc0_sbu>; ++ }; ++ }; ++}; ++ ++&usbdp_phy0_u3 { ++ status = "okay"; ++}; ++ ++&usbdp_phy1 { ++ rockchip,dp-lane-mux = <2 3>; ++ status = "okay"; ++}; ++ ++&usbdp_phy1_u3 { ++ status = "okay"; ++}; ++ ++ + &usb_host0_ehci { + status = "okay"; + }; +@@ -839,6 +973,20 @@ &usb_host0_ohci { + status = "okay"; + }; + ++&usb_host0_xhci { ++ dr_mode = "otg"; ++ usb-role-switch; ++ status = "okay"; ++ ++ port { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ usb_host0_xhci_drd_sw: endpoint { ++ remote-endpoint = <&usbc0_hs>; ++ }; ++ }; ++}; ++ + &usb_host1_ehci { + status = "okay"; + }; +@@ -846,3 +994,31 @@ &usb_host1_ehci { + &usb_host1_ohci { + status = "okay"; + }; ++ ++&usb_host1_xhci { ++ status = "okay"; ++}; ++ ++&hdmi0 { ++ status = "okay"; ++}; ++ ++&hdmi0_in_vp0 { ++ status = "okay"; ++}; ++ ++&hdptxphy_hdmi0 { ++ status = "okay"; ++}; ++ ++&hdptxphy_hdmi_clk0 { ++ status = "okay"; ++}; ++ ++&vop_mmu { ++ status = "okay"; ++}; ++ ++&vop { ++ status = "okay"; ++}; +-- +2.42.1 + diff --git a/patch/kernel/rockchip-rk3588-edge/dt/rk3588-orangepi-5-plus.dts b/patch/kernel/rockchip-rk3588-edge/dt/rk3588-orangepi-5-plus.dts deleted file mode 100644 index 59a77d3d78..0000000000 --- a/patch/kernel/rockchip-rk3588-edge/dt/rk3588-orangepi-5-plus.dts +++ /dev/null @@ -1,1227 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0+ OR MIT) - -/dts-v1/; - -#include -#include -#include -#include -#include "rk3588.dtsi" - -/ { - model = "Orange Pi 5 Plus"; - compatible = "xunlong,orangepi-5-plus", "rockchip,rk3588"; - - aliases { - mmc0 = &sdmmc; - mmc1 = &sdhci; - serial2 = &uart2; - }; - - chosen { - stdout-path = "serial2:1500000n8"; - }; - - gpio_leds: leds { - compatible = "gpio-leds"; - pinctrl-names = "default"; - pinctrl-0 =<&leds_rgb>; - status = "okay"; - - blue_led@1 { - gpios = <&gpio3 RK_PA6 GPIO_ACTIVE_HIGH>; - label = "blue_led"; - linux,default-trigger = "heartbeat"; - linux,default-trigger-delay-ms = <0>; - }; - - green_led@2 { - gpios = <&gpio3 RK_PB1 GPIO_ACTIVE_HIGH>; - label = "green_led"; - linux,default-trigger = "heartbeat"; - linux,default-trigger-delay-ms = <0>; - }; - }; - - fan: pwm-fan { - compatible = "pwm-fan"; - #cooling-cells = <2>; - pwms = <&pwm3 0 50000 0>; - cooling-levels = <0 50 100 150 200 255>; - rockchip,temp-trips = < - 50000 1 - 55000 2 - 60000 3 - 65000 4 - 70000 5 - >; - - status = "okay"; - }; - - adc_keys: adc-keys { - compatible = "adc-keys"; - io-channels = <&saradc 1>; - io-channel-names = "buttons"; - keyup-threshold-microvolt = <1800000>; - poll-interval = <100>; - - vol-up-key { - label = "volume up"; - linux,code = ; - press-threshold-microvolt = <17000>; - }; - - vol-down-key { - label = "volume down"; - linux,code = ; - press-threshold-microvolt = <417000>; - }; - - menu-key { - label = "menu"; - linux,code = ; - press-threshold-microvolt = <890000>; - }; - - back-key { - label = "back"; - linux,code = ; - press-threshold-microvolt = <1235000>; - }; - }; - - analog-sound { - compatible = "simple-audio-card"; - pinctrl-names = "default"; - pinctrl-0 = <&hp_detect>; - simple-audio-card,name = "ES8388 Audio"; - simple-audio-card,format = "i2s"; - simple-audio-card,mclk-fs = <256>; - simple-audio-card,bitclock-master = <&masterdai>; - simple-audio-card,frame-master = <&masterdai>; - - simple-audio-card,widgets = - "Microphone", "Mic Jack", - "Microphone", "Onboard Mic", - "Headphone", "Headphones", - "Speaker", "Speaker"; - simple-audio-card,routing = - "Speaker Amplifier INL", "LOUT2", - "Speaker Amplifier INR", "ROUT2", - "Speaker", "Speaker Amplifier OUTL", - "Speaker", "Speaker Amplifier OUTR", - "Headphone Amplifier INL", "LOUT1", - "Headphone Amplifier INR", "ROUT1", - "Headphones", "Headphone Amplifier OUTL", - "Headphones", "Headphone Amplifier OUTR", - "LINPUT1", "Onboard Mic", - "LINPUT2", "Onboard Mic", - "RINPUT1", "Mic Jack", - "RINPUT2", "Mic Jack"; - - simple-audio-card,hp-det-gpio = <&gpio1 RK_PD3 GPIO_ACTIVE_HIGH>; - simple-audio-card,spk-con-gpio = <&gpio3 RK_PC0 GPIO_ACTIVE_HIGH>; - simple-audio-card,hp-con-gpio = <&gpio3 RK_PA7 GPIO_ACTIVE_HIGH>; - - simple-audio-card,io-channels = <&saradc 3>; - simple-audio-card,io-channel-names = "adc-detect"; - simple-audio-card,keyup-threshold-microvolt = <1800000>; - simple-audio-card,poll-interval = <100>; - - simple-audio-card,cpu { - sound-dai = <&i2s0_8ch>; - }; - - masterdai: simple-audio-card,codec { - sound-dai = <&es8388>; - }; - - play-pause-key { - label = "playpause"; - linux,code = ; - press-threshold-microvolt = <2000>; - }; - }; - - backlight: backlight { - compatible = "pwm-backlight"; - brightness-levels = < - 0 20 20 21 21 22 22 23 - 23 24 24 25 25 26 26 27 - 27 28 28 29 29 30 30 31 - 31 32 32 33 33 34 34 35 - 35 36 36 37 37 38 38 39 - 40 41 42 43 44 45 46 47 - 48 49 50 51 52 53 54 55 - 56 57 58 59 60 61 62 63 - 64 65 66 67 68 69 70 71 - 72 73 74 75 76 77 78 79 - 80 81 82 83 84 85 86 87 - 88 89 90 91 92 93 94 95 - 96 97 98 99 100 101 102 103 - 104 105 106 107 108 109 110 111 - 112 113 114 115 116 117 118 119 - 120 121 122 123 124 125 126 127 - 128 129 130 131 132 133 134 135 - 136 137 138 139 140 141 142 143 - 144 145 146 147 148 149 150 151 - 152 153 154 155 156 157 158 159 - 160 161 162 163 164 165 166 167 - 168 169 170 171 172 173 174 175 - 176 177 178 179 180 181 182 183 - 184 185 186 187 188 189 190 191 - 192 193 194 195 196 197 198 199 - 200 201 202 203 204 205 206 207 - 208 209 210 211 212 213 214 215 - 216 217 218 219 220 221 222 223 - 224 225 226 227 228 229 230 231 - 232 233 234 235 236 237 238 239 - 240 241 242 243 244 245 246 247 - 248 249 250 251 252 253 254 255 - >; - default-brightness-level = <200>; - pwms = <&pwm2 0 25000 0>; - status = "okay"; - }; - - wireless_bluetooth: wireless-bluetooth { - compatible = "bluetooth-platdata"; - clocks = <&hym8563>; - clock-names = "ext_clock"; - uart_rts_gpios = <&gpio4 RK_PC4 GPIO_ACTIVE_LOW>; - pinctrl-names = "default", "rts_gpio"; - pinctrl-0 = <&uart9m0_rtsn>, <&bt_reset_gpio>, <&bt_wake_gpio>, <&bt_irq_gpio>; - pinctrl-1 = <&uart9_gpios>; - BT,reset_gpio = <&gpio0 RK_PC5 GPIO_ACTIVE_HIGH>; - BT,wake_gpio = <&gpio4 RK_PC6 GPIO_ACTIVE_HIGH>; - BT,wake_host_irq = <&gpio0 RK_PA0 GPIO_ACTIVE_HIGH>; - status = "okay"; - }; - - wireless_wlan: wireless-wlan { - compatible = "wlan-platdata"; - wifi_chip_type = "ap6275p"; - pinctrl-names = "default"; - pinctrl-0 = <&wifi_host_wake_irq>; - WIFI,host_wake_irq = <&gpio0 RK_PB0 GPIO_ACTIVE_HIGH>; - status = "okay"; - }; - - wifi_disable: wifi-diable-gpio-regulator { - compatible = "regulator-fixed"; - regulator-name = "wifi_disable"; - enable-active-high; - gpio = <&gpio0 RK_PC4 GPIO_ACTIVE_HIGH>; - regulator-boot-on; - regulator-always-on; - }; - - vcc12v_dcin: vcc12v-dcin-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc12v_dcin"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <12000000>; - regulator-max-microvolt = <12000000>; - }; - - vcc5v0_sys: vcc5v0-sys-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc5v0_sys"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - vin-supply = <&vcc12v_dcin>; - }; - - vcc_1v1_nldo_s3: vcc-1v1-nldo-s3-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc_1v1_nldo_s3"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <1100000>; - regulator-max-microvolt = <1100000>; - vin-supply = <&vcc5v0_sys>; - }; - - vcc_3v3_sd_s0: vcc-3v3-sd-s0 { - compatible = "regulator-fixed"; - regulator-name = "vcc_3v3_sd_s0"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpios = <&gpio3 RK_PD5 GPIO_ACTIVE_LOW>; - pinctrl-names = "default"; - pinctrl-0 = <&sdmmc_pwr>; - enable-active-low; - }; - - vcc5v0_usbdcin: vcc5v0-usbdcin-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc5v0_usbdcin"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - vin-supply = <&vcc12v_dcin>; - }; - - vcc5v0_usb: vcc5v0-usb-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc5v0_usb"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - vin-supply = <&vcc5v0_usbdcin>; - }; - - vbus5v0_typec: vbus5v0-typec-regulator { - compatible = "regulator-fixed"; - regulator-name = "vbus5v0_typec"; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - enable-active-high; - gpio = <&gpio4 RK_PB0 GPIO_ACTIVE_HIGH>; - vin-supply = <&vcc5v0_usb>; - pinctrl-names = "default"; - pinctrl-0 = <&typec5v_pwren>; - }; - - pcie20_avdd0v85: pcie20-avdd0v85-regulator { - compatible = "regulator-fixed"; - regulator-name = "pcie20_avdd0v85"; - regulator-boot-on; - regulator-always-on; - regulator-min-microvolt = <850000>; - regulator-max-microvolt = <850000>; - vin-supply = <&vdd_0v85_s0>; - }; - - pcie20_avdd1v8: pcie20-avdd1v8-regulator { - compatible = "regulator-fixed"; - regulator-name = "pcie20_avdd1v8"; - regulator-boot-on; - regulator-always-on; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - vin-supply = <&avcc_1v8_s0>; - }; - - pcie30_avdd0v75: pcie30-avdd0v75-regulator { - compatible = "regulator-fixed"; - regulator-name = "pcie30_avdd0v75"; - regulator-boot-on; - regulator-always-on; - regulator-min-microvolt = <750000>; - regulator-max-microvolt = <750000>; - vin-supply = <&avdd_0v75_s0>; - }; - - pcie30_avdd1v8: pcie30-avdd1v8-regulator { - compatible = "regulator-fixed"; - regulator-name = "pcie30_avdd1v8"; - regulator-boot-on; - regulator-always-on; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - vin-supply = <&avcc_1v8_s0>; - }; - - vcc3v3_pcie30: vcc3v3-pcie30-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc3v3_pcie30"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - enable-active-high; - gpios = <&gpio2 RK_PB6 GPIO_ACTIVE_HIGH>; - startup-delay-us = <5000>; - vin-supply = <&vcc5v0_sys>; - }; - - vcc3v3_pcie2x1l0: vcc3v3-pcie2x1l0-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc3v3_pcie2x1l0"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - enable-active-high; - gpios = <&gpio2 RK_PC5 GPIO_ACTIVE_HIGH>; - startup-delay-us = <50000>; - vin-supply = <&vcc12v_dcin>; - }; - - vcc3v3_pcie_eth: vcc3v3-pcie-eth-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc3v3_pcie_eth"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-boot-on; - regulator-always-on; - enable-active-low; - gpios = <&gpio3 RK_PB4 GPIO_ACTIVE_LOW>; - startup-delay-us = <50000>; - vin-supply = <&vcc12v_dcin>; - }; - - vcc5v0_host: vcc5v0-host-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc5v0_host"; - regulator-boot-on; - regulator-always-on; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - enable-active-high; - gpio = <&gpio3 RK_PB7 GPIO_ACTIVE_HIGH>; - vin-supply = <&vcc5v0_usb>; - pinctrl-names = "default"; - pinctrl-0 = <&vcc5v0_host_en>; - }; -}; - -&combphy0_ps { - status = "okay"; -}; - -&combphy1_ps { - status = "okay"; -}; - -&combphy2_psu { - status = "okay"; -}; - -&cpu_b0 { - cpu-supply = <&vdd_cpu_big0_s0>; - mem-supply = <&vdd_cpu_big0_mem_s0>; -}; - -&cpu_b1 { - cpu-supply = <&vdd_cpu_big0_s0>; - mem-supply = <&vdd_cpu_big0_mem_s0>; -}; - -&cpu_b2 { - cpu-supply = <&vdd_cpu_big1_s0>; - mem-supply = <&vdd_cpu_big1_mem_s0>; -}; - -&cpu_b3 { - cpu-supply = <&vdd_cpu_big1_s0>; - mem-supply = <&vdd_cpu_big1_mem_s0>; -}; - -&cpu_l0 { - cpu-supply = <&vdd_cpu_lit_s0>; - mem-supply = <&vdd_cpu_lit_mem_s0>; -}; - -&cpu_l1 { - cpu-supply = <&vdd_cpu_lit_s0>; - mem-supply = <&vdd_cpu_lit_mem_s0>; -}; - -&cpu_l2 { - cpu-supply = <&vdd_cpu_lit_s0>; - mem-supply = <&vdd_cpu_lit_mem_s0>; -}; - -&cpu_l3 { - cpu-supply = <&vdd_cpu_lit_s0>; - mem-supply = <&vdd_cpu_lit_mem_s0>; -}; - -&i2c0 { - pinctrl-names = "default"; - pinctrl-0 = <&i2c0m2_xfer>; - status = "okay"; - - vdd_cpu_big0_s0: vdd_cpu_big0_mem_s0: regulator@42 { - compatible = "rockchip,rk8602"; - reg = <0x42>; - fcs,suspend-voltage-selector = <1>; - regulator-name = "vdd_cpu_big0_s0"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <550000>; - regulator-max-microvolt = <1050000>; - regulator-ramp-delay = <2300>; - vin-supply = <&vcc5v0_sys>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vdd_cpu_big1_s0: vdd_cpu_big1_mem_s0: regulator@43 { - compatible = "rockchip,rk8603", "rockchip,rk8602"; - reg = <0x43>; - fcs,suspend-voltage-selector = <1>; - regulator-name = "vdd_cpu_big1_s0"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <550000>; - regulator-max-microvolt = <1050000>; - regulator-ramp-delay = <2300>; - vin-supply = <&vcc5v0_sys>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; -}; - -&i2c1 { - pinctrl-names = "default"; - pinctrl-0 = <&i2c1m2_xfer>; - status = "okay"; - - vdd_npu_s0: vdd_npu_mem_s0: rk8602@42 { - compatible = "rockchip,rk8602"; - reg = <0x42>; - vin-supply = <&vcc5v0_sys>; - regulator-compatible = "rk860x-reg"; - regulator-name = "vdd_npu_s0"; - regulator-min-microvolt = <550000>; - regulator-max-microvolt = <950000>; - regulator-ramp-delay = <2300>; - rockchip,suspend-voltage-selector = <1>; - regulator-boot-on; - regulator-always-on; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; -}; - -&i2c6 { - pinctrl-names = "default"; - pinctrl-0 = <&i2c6m0_xfer>; - status = "okay"; - - hym8563: rtc@51 { - compatible = "haoyu,hym8563"; - reg = <0x51>; - #clock-cells = <0>; - clock-output-names = "hym8563"; - pinctrl-names = "default"; - pinctrl-0 = <&hym8563_int>; - interrupt-parent = <&gpio0>; - interrupts = ; - }; - - usbc0: usb-typec@22 { - compatible = "fcs,fusb302"; - reg = <0x22>; - interrupt-parent = <&gpio0>; - interrupts = ; - pinctrl-names = "default"; - pinctrl-0 = <&usbc0_int>; - vbus-supply = <&vbus5v0_typec>; - status = "okay"; - - usb_con: connector { - compatible = "usb-c-connector"; - label = "USB-C"; - data-role = "dual"; - power-role = "dual"; - try-power-role = "sink"; - op-sink-microwatt = <1000000>; - sink-pdos = - ; - source-pdos = - ; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - usbc0_hs: endpoint { - remote-endpoint = <&usb_host0_xhci_drd_sw>; - }; - }; - - port@1 { - reg = <1>; - usbc0_ss: endpoint { - remote-endpoint = <&usbdp_phy0_typec_ss>; - }; - }; - - port@2 { - reg = <2>; - usbc0_sbu: endpoint { - remote-endpoint = <&usbdp_phy0_typec_sbu>; - }; - }; - }; - }; - }; -}; - -&i2c7 { - status = "okay"; - - es8388: audio-codec@11 { - compatible = "everest,es8388"; - reg = <0x11>; - clocks = <&cru I2S0_8CH_MCLKOUT>; - clock-names = "mclk"; - assigned-clocks = <&cru I2S0_8CH_MCLKOUT>; - assigned-clock-rates = <12288000>; - pinctrl-names = "default"; - pinctrl-0 = <&i2s0_mclk>; - #sound-dai-cells = <0>; - }; -}; - -&i2s0_8ch { - pinctrl-0 = <&i2s0_lrck - &i2s0_sclk - &i2s0_sdi0 - &i2s0_sdo0>; - status = "okay"; -}; - -&pinctrl { - headphone { - hp_detect: hp-detect { - rockchip,pins = <1 RK_PD3 RK_FUNC_GPIO &pcfg_pull_none>; - }; - }; - - hym8563 { - hym8563_int: hym8563-int { - rockchip,pins = <0 RK_PC6 RK_FUNC_GPIO &pcfg_pull_up>; - }; - }; - - usb { - vcc5v0_host_en: vcc5v0-host-en { - rockchip,pins = <3 RK_PB7 RK_FUNC_GPIO &pcfg_pull_none>; - }; - }; - - usb-typec { - usbc0_int: usbc0-int { - rockchip,pins = <0 RK_PD3 RK_FUNC_GPIO &pcfg_pull_up>; - }; - - typec5v_pwren: typec5v-pwren { - rockchip,pins = <4 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>; - }; - }; - - wireless-bluetooth { - uart9_gpios: uart9-gpios { - rockchip,pins = <4 RK_PC4 RK_FUNC_GPIO &pcfg_pull_none>; - }; - - bt_reset_gpio: bt-reset-gpio { - rockchip,pins = <0 RK_PC5 RK_FUNC_GPIO &pcfg_pull_none>; - }; - - bt_wake_gpio: bt-wake-gpio { - rockchip,pins = <4 RK_PC6 RK_FUNC_GPIO &pcfg_pull_none>; - }; - - bt_irq_gpio: bt-irq-gpio { - rockchip,pins = <0 RK_PA0 RK_FUNC_GPIO &pcfg_pull_down>; - }; - }; - - wireless-wlan { - wifi_host_wake_irq: wifi-host-wake-irq { - rockchip,pins = <0 RK_PB0 RK_FUNC_GPIO &pcfg_pull_down>; - }; - }; - - sdmmc { - sdmmc_pwr: sdmmc_pwr { - rockchip,pins = <3 RK_PD5 RK_FUNC_GPIO &pcfg_pull_down>; - }; - }; - - gpio-func { - leds_rgb: leds-rgb { - rockchip,pins = <3 RK_PA6 RK_FUNC_GPIO &pcfg_pull_up>, - <3 RK_PB1 RK_FUNC_GPIO &pcfg_pull_up>; - }; - }; -}; - -&pwm2 { - pinctrl-names = "active"; - pinctrl-0 = <&pwm2m2_pins>; - status = "okay"; -}; - -&pwm3 { - pinctrl-names = "active"; - pinctrl-0 = <&pwm3m1_pins>; - status = "okay"; -}; - -&saradc { - vref-supply = <&avcc_1v8_s0>; - status = "okay"; -}; - -&sdhci { - bus-width = <8>; - no-sdio; - no-sd; - non-removable; - max-frequency = <200000000>; - mmc-hs400-1_8v; - mmc-hs400-enhanced-strobe; - status = "okay"; -}; - -&sdmmc { - max-frequency = <150000000>; - no-sdio; - no-mmc; - bus-width = <4>; - cap-mmc-highspeed; - cap-sd-highspeed; - disable-wp; - sd-uhs-sdr104; - vmmc-supply = <&vcc_3v3_sd_s0>; - vqmmc-supply = <&vccio_sd_s0>; - status = "okay"; -}; - -&spi0 { - assigned-clocks = <&cru CLK_SPI0>; - assigned-clock-rates = <200000000>; - num-cs = <2>; - status = "disabled"; -}; - -&spi2 { - status = "okay"; - assigned-clocks = <&cru CLK_SPI2>; - assigned-clock-rates = <200000000>; - pinctrl-names = "default"; - pinctrl-0 = <&spi2m2_cs0 &spi2m2_pins>; - num-cs = <1>; - - pmic@0 { - compatible = "rockchip,rk806"; - spi-max-frequency = <1000000>; - reg = <0x0>; - - interrupt-parent = <&gpio0>; - interrupts = <7 IRQ_TYPE_LEVEL_LOW>; - - pinctrl-names = "default"; - pinctrl-0 = <&pmic_pins>, <&rk806_dvs1_null>, - <&rk806_dvs2_null>, <&rk806_dvs3_null>; - - vcc1-supply = <&vcc5v0_sys>; - vcc2-supply = <&vcc5v0_sys>; - vcc3-supply = <&vcc5v0_sys>; - vcc4-supply = <&vcc5v0_sys>; - vcc5-supply = <&vcc5v0_sys>; - vcc6-supply = <&vcc5v0_sys>; - vcc7-supply = <&vcc5v0_sys>; - vcc8-supply = <&vcc5v0_sys>; - vcc9-supply = <&vcc5v0_sys>; - vcc10-supply = <&vcc5v0_sys>; - vcc11-supply = <&vcc_2v0_pldo_s3>; - vcc12-supply = <&vcc5v0_sys>; - vcc13-supply = <&vcc_1v1_nldo_s3>; - vcc14-supply = <&vcc_1v1_nldo_s3>; - vcca-supply = <&vcc5v0_sys>; - - gpio-controller; - #gpio-cells = <2>; - - rk806_dvs1_null: dvs1-null-pins { - pins = "gpio_pwrctrl2"; - function = "pin_fun0"; - }; - - rk806_dvs2_null: dvs2-null-pins { - pins = "gpio_pwrctrl2"; - function = "pin_fudr_moden0"; - }; - - rk806_dvs3_null: dvs3-null-pins { - pins = "gpio_pwrctrl3"; - function = "pin_fun0"; - }; - - regulators { - vdd_gpu_s0: vdd_gpu_mem_s0: dcdc-reg1 { - regulator-boot-on; - regulator-min-microvolt = <550000>; - regulator-max-microvolt = <950000>; - regulator-ramp-delay = <12500>; - regulator-name = "vdd_gpu_s0"; - regulator-enable-ramp-delay = <400>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vdd_cpu_lit_s0: vdd_cpu_lit_mem_s0: dcdc-reg2 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <550000>; - regulator-max-microvolt = <950000>; - regulator-ramp-delay = <12500>; - regulator-name = "vdd_cpu_lit_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vdd_log_s0: dcdc-reg3 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <675000>; - regulator-max-microvolt = <750000>; - regulator-ramp-delay = <12500>; - regulator-name = "vdd_log_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - regulator-suspend-microvolt = <750000>; - }; - }; - - vdd_vdenc_s0: vdd_vdenc_mem_s0: dcdc-reg4 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <550000>; - regulator-max-microvolt = <950000>; - regulator-init-microvolt = <750000>; - regulator-ramp-delay = <12500>; - regulator-name = "vdd_vdenc_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vdd_ddr_s0: dcdc-reg5 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <675000>; - regulator-max-microvolt = <900000>; - regulator-ramp-delay = <12500>; - regulator-name = "vdd_ddr_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - regulator-suspend-microvolt = <850000>; - }; - }; - - vdd2_ddr_s3: dcdc-reg6 { - regulator-always-on; - regulator-boot-on; - regulator-name = "vdd2_ddr_s3"; - - regulator-state-mem { - regulator-on-in-suspend; - }; - }; - - vcc_2v0_pldo_s3: dcdc-reg7 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <2000000>; - regulator-max-microvolt = <2000000>; - regulator-ramp-delay = <12500>; - regulator-name = "vdd_2v0_pldo_s3"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <2000000>; - }; - }; - - vcc_3v3_s3: dcdc-reg8 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-name = "vcc_3v3_s3"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <3300000>; - }; - }; - - vddq_ddr_s0: dcdc-reg9 { - regulator-always-on; - regulator-boot-on; - regulator-name = "vddq_ddr_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vcc_1v8_s3: dcdc-reg10 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-name = "vcc_1v8_s3"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <1800000>; - }; - }; - - avcc_1v8_s0: pldo-reg1 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-name = "avcc_1v8_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vcc_1v8_s0: pldo-reg2 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-name = "vcc_1v8_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - regulator-suspend-microvolt = <1800000>; - }; - }; - - avdd_1v2_s0: pldo-reg3 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - regulator-name = "avdd_1v2_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vcc_3v3_s0: pldo-reg4 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-ramp-delay = <12500>; - regulator-name = "vcc_3v3_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vccio_sd_s0: pldo-reg5 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <3300000>; - regulator-ramp-delay = <12500>; - regulator-name = "vccio_sd_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - pldo6_s3: pldo-reg6 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-name = "pldo6_s3"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <1800000>; - }; - }; - - vdd_0v75_s3: nldo-reg1 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <750000>; - regulator-max-microvolt = <750000>; - regulator-name = "vdd_0v75_s3"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <750000>; - }; - }; - - vdd_ddr_pll_s0: nldo-reg2 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <850000>; - regulator-max-microvolt = <850000>; - regulator-name = "vdd_ddr_pll_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - regulator-suspend-microvolt = <850000>; - }; - }; - - avdd_0v75_s0: nldo-reg3 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <750000>; - regulator-max-microvolt = <750000>; - regulator-name = "avdd_0v75_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vdd_0v85_s0: nldo-reg4 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <850000>; - regulator-max-microvolt = <850000>; - regulator-name = "vdd_0v85_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vdd_0v75_s0: nldo-reg5 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <750000>; - regulator-max-microvolt = <750000>; - regulator-name = "vdd_0v75_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - }; - }; -}; - -&spi4 { - assigned-clocks = <&cru CLK_SPI4>; - assigned-clock-rates = <200000000>; - num-cs = <2>; - status = "disabled"; -}; - -&uart2 { - pinctrl-0 = <&uart2m0_xfer>; - status = "okay"; -}; - -&uart9 { - pinctrl-names = "default"; - pinctrl-0 = <&uart9m0_xfer &uart9m0_ctsn>; - status = "okay"; -}; - -&tsadc { - status = "okay"; -}; - -&sfc { - max-freq = <100000000>; - #address-cells = <1>; - #size-cells = <0>; - pinctrl-names = "default"; - pinctrl-0 = <&fspim1_pins>; - status = "okay"; - - spi_flash: spi-flash@0 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "jedec,spi-nor"; - reg = <0x0>; - spi-max-frequency = <100000000>; - spi-tx-bus-width = <1>; - spi-rx-bus-width = <4>; - status = "okay"; - - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - loader@0 { - label = "loader"; - reg = <0x0 0x1000000>; - }; - }; - }; -}; - -&pcie2x1l0 { - reset-gpios = <&gpio4 RK_PA5 GPIO_ACTIVE_HIGH>; - vpcie3v3-supply = <&vcc3v3_pcie2x1l0>; - rockchip,skip-scan-in-resume; - status = "okay"; -}; - -&pcie2x1l1 { - reset-gpios = <&gpio3 RK_PB3 GPIO_ACTIVE_HIGH>; - status = "okay"; -}; - -&pcie2x1l2 { - reset-gpios = <&gpio4 RK_PA2 GPIO_ACTIVE_HIGH>; - status = "okay"; -}; - -&pcie30phy { - //rockchip,pcie30-phymode = ; - status = "okay"; -}; - -&pcie3x4 { - reset-gpios = <&gpio4 RK_PB6 GPIO_ACTIVE_HIGH>; - vpcie3v3-supply = <&vcc3v3_pcie30>; - status = "okay"; -}; - -&u2phy0 { - status = "okay"; -}; - -&u2phy0_otg { - rockchip,typec-vbus-det; - status = "okay"; -}; - -&u2phy1 { - status = "okay"; -}; - -&u2phy1_otg { - phy-supply = <&vcc5v0_host>; - status = "okay"; -}; - -&u2phy2 { - status = "okay"; -}; - -&u2phy2_host { - phy-supply = <&vcc5v0_host>; - status = "okay"; -}; - -&u2phy3 { - status = "okay"; -}; - -&u2phy3_host { - phy-supply = <&vcc5v0_host>; - status = "okay"; -}; - -&usbdp_phy0 { - orientation-switch; - mode-switch; - //svid = <0xff01>; - sbu1-dc-gpios = <&gpio4 RK_PA6 GPIO_ACTIVE_HIGH>; - sbu2-dc-gpios = <&gpio4 RK_PA7 GPIO_ACTIVE_HIGH>; - - port { - #address-cells = <1>; - #size-cells = <0>; - - usbdp_phy0_typec_ss: endpoint@0 { - reg = <0>; - remote-endpoint = <&usbc0_ss>; - }; - - usbdp_phy0_typec_sbu: endpoint@1 { - reg = <1>; - remote-endpoint = <&usbc0_sbu>; - }; - }; -}; - -&usbdp_phy0_dp { - status = "okay"; -}; - -&usbdp_phy0_u3 { - status = "okay"; -}; - -&usbdp_phy1 { - rockchip,dp-lane-mux = <2 3>; - status = "okay"; -}; - -&usbdp_phy1_dp { - status = "okay"; -}; - -&usbdp_phy1_u3 { - status = "okay"; -}; - -&usb_host0_ehci { - status = "okay"; -}; - -&usb_host0_ohci { - status = "okay"; -}; - -&usb_host0_xhci { - usb-role-switch; - dr_mode = "otg"; - status = "okay"; - - port { - #address-cells = <1>; - #size-cells = <0>; - - usb_host0_xhci_drd_sw: endpoint { - remote-endpoint = <&usbc0_hs>; - }; - }; -}; - -&usb_host1_ehci { - status = "okay"; -}; - -&usb_host1_ohci { - status = "okay"; -}; - -&usb_host1_xhci { - status = "okay"; -}; - -&wdt { - status = "okay"; -}; diff --git a/patch/kernel/rockchip-rk3588-edge/dt/rk3588s-nanopi-r6s.dts b/patch/kernel/rockchip-rk3588-edge/dt/rk3588s-nanopi-r6s.dts index 2f09b9024c..5e04849dea 100644 --- a/patch/kernel/rockchip-rk3588-edge/dt/rk3588s-nanopi-r6s.dts +++ b/patch/kernel/rockchip-rk3588-edge/dt/rk3588s-nanopi-r6s.dts @@ -224,6 +224,11 @@ mem-supply = <&vdd_cpu_lit_mem_s0>; }; +&display_subsystem { + clocks = <&hdptxphy_hdmi_clk0>; + clock-names = "hdmi0_phy_pll"; +}; + &i2c0 { pinctrl-names = "default"; pinctrl-0 = <&i2c0m2_xfer>; @@ -830,6 +835,30 @@ status = "okay"; }; +&hdmi0 { + status = "okay"; +}; + +&hdmi0_in_vp0 { + status = "okay"; +}; + +&hdptxphy_hdmi0 { + status = "okay"; +}; + +&hdptxphy_hdmi_clk0 { + status = "okay"; +}; + +&vop_mmu { + status = "okay"; +}; + +&vop { + status = "okay"; +}; + &wdt { status = "okay"; }; diff --git a/patch/kernel/rockchip-rk3588-edge/dt/rk3588s-orangepi-5.dts b/patch/kernel/rockchip-rk3588-edge/dt/rk3588s-orangepi-5.dts deleted file mode 100644 index 9734fe024a..0000000000 --- a/patch/kernel/rockchip-rk3588-edge/dt/rk3588s-orangepi-5.dts +++ /dev/null @@ -1,1276 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0+ OR MIT) - -/dts-v1/; - -#include -#include -#include -#include -#include "rk3588s.dtsi" - -/ { - model = "Orange Pi 5"; - compatible = "xunlong,orangepi-5", "rockchip,rk3588s"; - - aliases { - mmc0 = &sdmmc; - //mmc1 = &sdhci; - serial2 = &uart2; - }; - - chosen { - stdout-path = "serial2:1500000n8"; - }; - - gpio_leds: gpio-leds { - compatible = "gpio-leds"; - pinctrl-names = "default"; - pinctrl-0 =<&leds_gpio>; - status = "okay"; - - led@1 { - gpios = <&gpio1 RK_PA2 GPIO_ACTIVE_HIGH>; - label = "status_led"; - linux,default-trigger = "heartbeat"; - linux,default-trigger-delay-ms = <0>; - }; - }; - - analog-sound { - compatible = "simple-audio-card"; - pinctrl-names = "default"; - pinctrl-0 = <&hp_detect>; - simple-audio-card,name = "ES8388 Audio"; - simple-audio-card,format = "i2s"; - simple-audio-card,mclk-fs = <256>; - simple-audio-card,bitclock-master = <&masterdai>; - simple-audio-card,frame-master = <&masterdai>; - - simple-audio-card,widgets = - "Microphone", "Mic Jack", - "Microphone", "Onboard Mic", - "Headphone", "Headphones", - "Speaker", "Speaker"; - simple-audio-card,routing = - "Speaker Amplifier INL", "LOUT2", - "Speaker Amplifier INR", "ROUT2", - "Speaker", "Speaker Amplifier OUTL", - "Speaker", "Speaker Amplifier OUTR", - "Headphone Amplifier INL", "LOUT1", - "Headphone Amplifier INR", "ROUT1", - "Headphones", "Headphone Amplifier OUTL", - "Headphones", "Headphone Amplifier OUTR", - "LINPUT1", "Onboard Mic", - "LINPUT2", "Onboard Mic", - "RINPUT1", "Mic Jack", - "RINPUT2", "Mic Jack"; - - simple-audio-card,hp-det-gpio = <&gpio1 RK_PD5 GPIO_ACTIVE_HIGH>; - io-channels = <&saradc 3>; - io-channel-names = "adc-detect"; - - simple-audio-card,cpu { - sound-dai = <&i2s1_8ch>; - }; - - masterdai: simple-audio-card,codec { - sound-dai = <&es8388>; - }; - - play-pause-key { - label = "playpause"; - linux,code = ; - press-threshold-microvolt = <2000>; - }; - }; - - adc_keys: adc-keys { - compatible = "adc-keys"; - io-channels = <&saradc 1>; - io-channel-names = "buttons"; - keyup-threshold-microvolt = <1800000>; - poll-interval = <100>; - - vol-up-key { - label = "volume up"; - linux,code = ; - press-threshold-microvolt = <17000>; - }; - - vol-down-key { - label = "volume down"; - linux,code = ; - press-threshold-microvolt = <417000>; - }; - - menu-key { - label = "menu"; - linux,code = ; - press-threshold-microvolt = <890000>; - }; - - back-key { - label = "back"; - linux,code = ; - press-threshold-microvolt = <1235000>; - }; - }; - - backlight: backlight { - compatible = "pwm-backlight"; - brightness-levels = < - 0 20 20 21 21 22 22 23 - 23 24 24 25 25 26 26 27 - 27 28 28 29 29 30 30 31 - 31 32 32 33 33 34 34 35 - 35 36 36 37 37 38 38 39 - 40 41 42 43 44 45 46 47 - 48 49 50 51 52 53 54 55 - 56 57 58 59 60 61 62 63 - 64 65 66 67 68 69 70 71 - 72 73 74 75 76 77 78 79 - 80 81 82 83 84 85 86 87 - 88 89 90 91 92 93 94 95 - 96 97 98 99 100 101 102 103 - 104 105 106 107 108 109 110 111 - 112 113 114 115 116 117 118 119 - 120 121 122 123 124 125 126 127 - 128 129 130 131 132 133 134 135 - 136 137 138 139 140 141 142 143 - 144 145 146 147 148 149 150 151 - 152 153 154 155 156 157 158 159 - 160 161 162 163 164 165 166 167 - 168 169 170 171 172 173 174 175 - 176 177 178 179 180 181 182 183 - 184 185 186 187 188 189 190 191 - 192 193 194 195 196 197 198 199 - 200 201 202 203 204 205 206 207 - 208 209 210 211 212 213 214 215 - 216 217 218 219 220 221 222 223 - 224 225 226 227 228 229 230 231 - 232 233 234 235 236 237 238 239 - 240 241 242 243 244 245 246 247 - 248 249 250 251 252 253 254 255 - >; - default-brightness-level = <200>; - }; - - backlight_1: backlight_1 { - compatible = "pwm-backlight"; - brightness-levels = < - 0 20 20 21 21 22 22 23 - 23 24 24 25 25 26 26 27 - 27 28 28 29 29 30 30 31 - 31 32 32 33 33 34 34 35 - 35 36 36 37 37 38 38 39 - 40 41 42 43 44 45 46 47 - 48 49 50 51 52 53 54 55 - 56 57 58 59 60 61 62 63 - 64 65 66 67 68 69 70 71 - 72 73 74 75 76 77 78 79 - 80 81 82 83 84 85 86 87 - 88 89 90 91 92 93 94 95 - 96 97 98 99 100 101 102 103 - 104 105 106 107 108 109 110 111 - 112 113 114 115 116 117 118 119 - 120 121 122 123 124 125 126 127 - 128 129 130 131 132 133 134 135 - 136 137 138 139 140 141 142 143 - 144 145 146 147 148 149 150 151 - 152 153 154 155 156 157 158 159 - 160 161 162 163 164 165 166 167 - 168 169 170 171 172 173 174 175 - 176 177 178 179 180 181 182 183 - 184 185 186 187 188 189 190 191 - 192 193 194 195 196 197 198 199 - 200 201 202 203 204 205 206 207 - 208 209 210 211 212 213 214 215 - 216 217 218 219 220 221 222 223 - 224 225 226 227 228 229 230 231 - 232 233 234 235 236 237 238 239 - 240 241 242 243 244 245 246 247 - 248 249 250 251 252 253 254 255 - >; - default-brightness-level = <200>; - }; - - wireless_bluetooth: wireless-bluetooth { - compatible = "bluetooth-platdata"; - clocks = <&hym8563>; - clock-names = "ext_clock"; - uart_rts_gpios = <&gpio3 RK_PD2 GPIO_ACTIVE_LOW>; - pinctrl-names = "default", "rts_gpio"; - pinctrl-0 = <&uart9m2_rtsn>, <&bt_gpio>; - pinctrl-1 = <&uart9_gpios>; - BT,reset_gpio = <&gpio3 RK_PA6 GPIO_ACTIVE_HIGH>; - BT,wake_gpio = <&gpio0 RK_PC6 GPIO_ACTIVE_HIGH>; - status = "disabled"; - }; - - wireless_wlan: wireless-wlan { - compatible = "wlan-platdata"; - wifi_chip_type = "ap6275p"; - pinctrl-names = "default"; - pinctrl-0 = <&wifi_host_wake_irq>, <&wifi_poweren_gpio>; - WIFI,host_wake_irq = <&gpio0 RK_PA0 GPIO_ACTIVE_HIGH>; - WIFI,poweren_gpio = <&gpio0 RK_PD0 GPIO_ACTIVE_HIGH>; - status = "disabled"; - }; - - vcc12v_dcin: vcc12v-dcin-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc12v_dcin"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <12000000>; - regulator-max-microvolt = <12000000>; - }; - - vcc5v0_sys: vcc5v0-sys-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc5v0_sys"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - vin-supply = <&vcc12v_dcin>; - }; - - vcc_1v1_nldo_s3: vcc-1v1-nldo-s3-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc_1v1_nldo_s3"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <1100000>; - regulator-max-microvolt = <1100000>; - vin-supply = <&vcc5v0_sys>; - }; - - vcc_3v3_sd_s0: vcc-3v3-sd-s0-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc_3v3_sd_s0"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpios = <&gpio4 RK_PB5 GPIO_ACTIVE_LOW>; - enable-active-low; - vin-supply = <&vcc_3v3_s3>; - regulator-always-on; - regulator-boot-on; - regulator-state-mem { - regulator-on-in-suspend; - }; - }; - - vcc5v0_usbdcin: vcc5v0-usbdcin-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc5v0_usbdcin"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - vin-supply = <&vcc12v_dcin>; - }; - - vcc5v0_usb: vcc5v0-usb-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc5v0_usb"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - vin-supply = <&vcc5v0_usbdcin>; - }; - - combophy_avdd0v85: combophy-avdd0v85-regulator { - compatible = "regulator-fixed"; - regulator-name = "combophy_avdd0v85"; - regulator-boot-on; - regulator-always-on; - regulator-min-microvolt = <850000>; - regulator-max-microvolt = <850000>; - vin-supply = <&vdd_0v85_s0>; - }; - - combophy_avdd1v8: combophy-avdd1v8-regulator { - compatible = "regulator-fixed"; - regulator-name = "combophy_avdd1v8"; - regulator-boot-on; - regulator-always-on; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - vin-supply = <&avcc_1v8_s0>; - }; - - vcc3v3_pcie2x1l2: vcc3v3-pcie2x1l2-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc3v3_pcie2x1l2"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - enable-active-high; - regulator-boot-on; - regulator-always-on; - gpios = <&gpio0 RK_PC5 GPIO_ACTIVE_HIGH>; - startup-delay-us = <50000>; - vin-supply = <&vcc5v0_sys>; - }; - - vbus5v0_typec: vbus5v0-typec-regulator { - compatible = "regulator-fixed"; - regulator-name = "vbus5v0_typec"; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - enable-active-high; - gpio = <&gpio3 RK_PC0 GPIO_ACTIVE_HIGH>; - vin-supply = <&vcc5v0_usb>; - pinctrl-names = "default"; - pinctrl-0 = <&typec5v_pwren>; - }; -}; - -&combphy0_ps { - status = "okay"; -}; - -&combphy2_psu { - status = "okay"; -}; - -&gmac1 { - /* Use rgmii-rxid mode to disable rx delay inside Soc */ - phy-mode = "rgmii-rxid"; - clock_in_out = "output"; - - snps,reset-gpio = <&gpio3 RK_PB2 GPIO_ACTIVE_LOW>; - snps,reset-active-low; - /* Reset time is 20ms, 100ms for rtl8211f */ - snps,reset-delays-us = <0 20000 100000>; - - pinctrl-names = "default"; - pinctrl-0 = <&gmac1_miim - &gmac1_tx_bus2 - &gmac1_rx_bus2 - &gmac1_rgmii_clk - &gmac1_rgmii_bus>; - - tx_delay = <0x42>; - rx_delay = <0x3f>; - - phy-handle = <&rgmii_phy1>; - status = "okay"; -}; - -&mdio1 { - rgmii_phy1: ethernet-phy@1 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <0x1>; - }; -}; - -&cpu_b0 { - cpu-supply = <&vdd_cpu_big0_s0>; - mem-supply = <&vdd_cpu_big0_mem_s0>; -}; - -&cpu_b1 { - cpu-supply = <&vdd_cpu_big0_s0>; - mem-supply = <&vdd_cpu_big0_mem_s0>; -}; - -&cpu_b2 { - cpu-supply = <&vdd_cpu_big1_s0>; - mem-supply = <&vdd_cpu_big1_mem_s0>; -}; - -&cpu_b3 { - cpu-supply = <&vdd_cpu_big1_s0>; - mem-supply = <&vdd_cpu_big1_mem_s0>; -}; - -&cpu_l0 { - cpu-supply = <&vdd_cpu_lit_s0>; - mem-supply = <&vdd_cpu_lit_mem_s0>; -}; - -&cpu_l1 { - cpu-supply = <&vdd_cpu_lit_s0>; - mem-supply = <&vdd_cpu_lit_mem_s0>; -}; - -&cpu_l2 { - cpu-supply = <&vdd_cpu_lit_s0>; - mem-supply = <&vdd_cpu_lit_mem_s0>; -}; - -&cpu_l3 { - cpu-supply = <&vdd_cpu_lit_s0>; - mem-supply = <&vdd_cpu_lit_mem_s0>; -}; - -&i2c0 { - pinctrl-names = "default"; - pinctrl-0 = <&i2c0m2_xfer>; - status = "okay"; - - vdd_cpu_big0_s0: vdd_cpu_big0_mem_s0: regulator@42 { - compatible = "rockchip,rk8602"; - reg = <0x42>; - fcs,suspend-voltage-selector = <1>; - regulator-name = "vdd_cpu_big0_s0"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <550000>; - regulator-max-microvolt = <1050000>; - regulator-ramp-delay = <2300>; - vin-supply = <&vcc5v0_sys>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vdd_cpu_big1_s0: vdd_cpu_big1_mem_s0: regulator@43 { - compatible = "rockchip,rk8603", "rockchip,rk8602"; - reg = <0x43>; - fcs,suspend-voltage-selector = <1>; - regulator-name = "vdd_cpu_big1_s0"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <550000>; - regulator-max-microvolt = <1050000>; - regulator-ramp-delay = <2300>; - vin-supply = <&vcc5v0_sys>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; -}; - -&i2c1 { - pinctrl-names = "default"; - pinctrl-0 = <&i2c1m2_xfer>; - status = "disabled"; -}; - -&i2c2 { - status = "okay"; - - vdd_npu_s0: vdd_npu_mem_s0: rk8602@42 { - compatible = "rockchip,rk8602"; - reg = <0x42>; - vin-supply = <&vcc5v0_sys>; - regulator-compatible = "rk860x-reg"; - regulator-name = "vdd_npu_s0"; - regulator-min-microvolt = <550000>; - regulator-max-microvolt = <950000>; - regulator-ramp-delay = <2300>; - rockchip,suspend-voltage-selector = <1>; - regulator-boot-on; - regulator-always-on; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; -}; - -&i2c3 { - pinctrl-names = "default"; - pinctrl-0 = <&i2c3m0_xfer>; - status = "disabled"; -}; - -/* - pin3: GPIO1_B7 - pin5: GPIO1_B6 -*/ -&i2c5 { - pinctrl-names = "default"; - pinctrl-0 = <&i2c5m3_xfer>; - status = "disabled"; -}; - -&i2c6 { - pinctrl-names = "default"; - pinctrl-0 = <&i2c6m3_xfer>; - status = "okay"; - - hym8563: rtc@51 { - compatible = "haoyu,hym8563"; - reg = <0x51>; - #clock-cells = <0>; - clock-output-names = "hym8563"; - pinctrl-names = "default"; - pinctrl-0 = <&hym8563_int>; - interrupt-parent = <&gpio0>; - interrupts = ; - wakeup-source; - }; - - es8388: audio-codec@10 { - compatible = "everest,es8388"; - reg = <0x10>; - assigned-clock-rates = <12288000>; - assigned-clocks = <&cru I2S1_8CH_MCLKOUT>; - clock-names = "mclk"; - clocks = <&cru I2S1_8CH_MCLKOUT>; - #sound-dai-cells = <0>; - pinctrl-names = "default"; - pinctrl-0 = <&i2s1m0_mclk>; - - }; - - usbc0: usb-typec@22 { - compatible = "fcs,fusb302"; - reg = <0x22>; - interrupt-parent = <&gpio0>; - interrupts = ; - pinctrl-names = "default"; - pinctrl-0 = <&usbc0_int>; - vbus-supply = <&vbus5v0_typec>; - status = "okay"; - - usb_con: connector { - compatible = "usb-c-connector"; - label = "USB-C"; - data-role = "dual"; - power-role = "dual"; - try-power-role = "sink"; - op-sink-microwatt = <1000000>; - sink-pdos = - ; - source-pdos = - ; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - usbc0_hs: endpoint { - remote-endpoint = <&usb_host0_xhci_drd_sw>; - }; - }; - - port@1 { - reg = <1>; - usbc0_ss: endpoint { - remote-endpoint = <&usbdp_phy0_typec_ss>; - }; - }; - - port@2 { - reg = <2>; - usbc0_sbu: endpoint { - remote-endpoint = <&usbdp_phy0_typec_sbu>; - }; - }; - }; - }; - }; -}; - -&i2s1_8ch { - rockchip,i2s-tx-route = <3 2 1 0>; - rockchip,i2s-rx-route = <1 3 2 0>; - pinctrl-names = "default"; - pinctrl-0 = <&i2s1m0_sclk - &i2s1m0_lrck - &i2s1m0_sdi1 - &i2s1m0_sdo3>; - status = "okay"; -}; - -&i2s5_8ch { - status = "okay"; -}; - -&pinctrl { - hym8563 { - hym8563_int: hym8563-int { - rockchip,pins = <0 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>; - }; - }; - - sound { - hp_detect: hp-detect { - rockchip,pins = <1 RK_PD5 RK_FUNC_GPIO &pcfg_pull_none>; - }; - }; - - gpio-func { - leds_gpio: leds-gpio { - rockchip,pins = <0 RK_PA2 RK_FUNC_GPIO &pcfg_pull_none>; - }; - }; - - sata { - sata_reset:sata-reset{ - rockchip,pins = <3 RK_PD1 RK_FUNC_GPIO &pcfg_pull_up>; - }; - }; - - usb-typec { - usbc0_int: usbc0-int { - rockchip,pins = <0 RK_PD3 RK_FUNC_GPIO &pcfg_pull_up>; - }; - - typec5v_pwren: typec5v-pwren { - rockchip,pins = <3 RK_PC0 RK_FUNC_GPIO &pcfg_pull_none>; - }; - }; - - wireless-bluetooth { - uart9_gpios: uart9-gpios { - rockchip,pins = <3 RK_PD2 RK_FUNC_GPIO &pcfg_pull_none>; - }; - - bt_gpio: bt-gpio { - rockchip,pins = - <3 RK_PA6 RK_FUNC_GPIO &pcfg_pull_none>, - <0 RK_PC6 RK_FUNC_GPIO &pcfg_pull_up>; - }; - }; - - wireless-wlan { - wifi_host_wake_irq: wifi-host-wake-irq { - rockchip,pins = <0 RK_PA0 RK_FUNC_GPIO &pcfg_pull_down>; - }; - - wifi_poweren_gpio: wifi-poweren-gpio { - rockchip,pins = <0 RK_PD0 RK_FUNC_GPIO &pcfg_pull_up>; - }; - }; -}; - -&pwm0 { - pinctrl-names = "active"; - pinctrl-0 = <&pwm0m1_pins>; - status = "disabled"; -}; - -/* - pin26: GPIO1_A3 -*/ -&pwm1 { - pinctrl-names = "active"; - pinctrl-0 = <&pwm1m1_pins>; - status = "disabled"; -}; - -&pwm2 { - pinctrl-names = "active"; - pinctrl-0 = <&pwm2m0_pins>; - status = "okay"; -}; - -&pwm3 { - pinctrl-names = "active"; - pinctrl-0 = <&pwm3m2_pins>; - status = "disabled"; -}; - -&pwm6 { - pinctrl-names = "active"; - pinctrl-0 = <&pwm6m0_pins>; - status = "okay"; -}; - -&pwm13 { - pinctrl-names = "active"; - pinctrl-0 = <&pwm13m2_pins>; - status = "disabled"; -}; - -/* - pin11: GPIO4_B2 - pin13: GPIO4_B3 -*/ -&pwm14 { - pinctrl-names = "active"; - pinctrl-0 = <&pwm14m1_pins>; - status = "disabled"; -}; - -/* - pin7: GPIO1_C6 -*/ -&pwm15 { - pinctrl-names = "active"; - pinctrl-0 = <&pwm15m2_pins>; - status = "disabled"; -}; - -&backlight_1 { - pwms = <&pwm6 0 25000 0>; - status = "okay"; -}; - -&backlight { - pwms = <&pwm2 0 25000 0>; - status = "okay"; -}; - -&saradc { - vref-supply = <&avcc_1v8_s0>; - status = "okay"; -}; - -&sdhci { - bus-width = <8>; - no-sdio; - no-sd; - non-removable; - max-frequency = <200000000>; - mmc-hs400-1_8v; - mmc-hs400-enhanced-strobe; - status = "disabled"; -}; - -&sdmmc { - max-frequency = <150000000>; - no-sdio; - no-mmc; - bus-width = <4>; - cap-mmc-highspeed; - cap-sd-highspeed; - disable-wp; - sd-uhs-sdr104; - vmmc-supply = <&vcc_3v3_sd_s0>; - vqmmc-supply = <&vccio_sd_s0>; - status = "okay"; -}; - -&spi2 { - status = "okay"; - assigned-clocks = <&cru CLK_SPI2>; - assigned-clock-rates = <200000000>; - pinctrl-names = "default"; - pinctrl-0 = <&spi2m2_cs0 &spi2m2_pins>; - num-cs = <1>; - - pmic@0 { - compatible = "rockchip,rk806"; - spi-max-frequency = <1000000>; - reg = <0x0>; - - interrupt-parent = <&gpio0>; - interrupts = <7 IRQ_TYPE_LEVEL_LOW>; - - pinctrl-names = "default"; - pinctrl-0 = <&pmic_pins>, <&rk806_dvs1_null>, - <&rk806_dvs2_null>, <&rk806_dvs3_null>; - - vcc1-supply = <&vcc5v0_sys>; - vcc2-supply = <&vcc5v0_sys>; - vcc3-supply = <&vcc5v0_sys>; - vcc4-supply = <&vcc5v0_sys>; - vcc5-supply = <&vcc5v0_sys>; - vcc6-supply = <&vcc5v0_sys>; - vcc7-supply = <&vcc5v0_sys>; - vcc8-supply = <&vcc5v0_sys>; - vcc9-supply = <&vcc5v0_sys>; - vcc10-supply = <&vcc5v0_sys>; - vcc11-supply = <&vcc_2v0_pldo_s3>; - vcc12-supply = <&vcc5v0_sys>; - vcc13-supply = <&vcc_1v1_nldo_s3>; - vcc14-supply = <&vcc_1v1_nldo_s3>; - vcca-supply = <&vcc5v0_sys>; - - gpio-controller; - #gpio-cells = <2>; - - rk806_dvs1_null: dvs1-null-pins { - pins = "gpio_pwrctrl2"; - function = "pin_fun0"; - }; - - rk806_dvs2_null: dvs2-null-pins { - pins = "gpio_pwrctrl2"; - function = "pin_fudr_moden0"; - }; - - rk806_dvs3_null: dvs3-null-pins { - pins = "gpio_pwrctrl3"; - function = "pin_fun0"; - }; - - regulators { - vdd_gpu_s0: vdd_gpu_mem_s0: dcdc-reg1 { - regulator-boot-on; - regulator-min-microvolt = <550000>; - regulator-max-microvolt = <950000>; - regulator-ramp-delay = <12500>; - regulator-name = "vdd_gpu_s0"; - regulator-enable-ramp-delay = <400>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vdd_cpu_lit_s0: vdd_cpu_lit_mem_s0: dcdc-reg2 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <550000>; - regulator-max-microvolt = <950000>; - regulator-ramp-delay = <12500>; - regulator-name = "vdd_cpu_lit_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vdd_log_s0: dcdc-reg3 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <675000>; - regulator-max-microvolt = <750000>; - regulator-ramp-delay = <12500>; - regulator-name = "vdd_log_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - regulator-suspend-microvolt = <750000>; - }; - }; - - vdd_vdenc_s0: vdd_vdenc_mem_s0: dcdc-reg4 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <550000>; - regulator-max-microvolt = <950000>; - regulator-init-microvolt = <750000>; - regulator-ramp-delay = <12500>; - regulator-name = "vdd_vdenc_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vdd_ddr_s0: dcdc-reg5 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <675000>; - regulator-max-microvolt = <900000>; - regulator-ramp-delay = <12500>; - regulator-name = "vdd_ddr_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - regulator-suspend-microvolt = <850000>; - }; - }; - - vdd2_ddr_s3: dcdc-reg6 { - regulator-always-on; - regulator-boot-on; - regulator-name = "vdd2_ddr_s3"; - - regulator-state-mem { - regulator-on-in-suspend; - }; - }; - - vcc_2v0_pldo_s3: dcdc-reg7 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <2000000>; - regulator-max-microvolt = <2000000>; - regulator-ramp-delay = <12500>; - regulator-name = "vdd_2v0_pldo_s3"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <2000000>; - }; - }; - - vcc_3v3_s3: dcdc-reg8 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-name = "vcc_3v3_s3"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <3300000>; - }; - }; - - vddq_ddr_s0: dcdc-reg9 { - regulator-always-on; - regulator-boot-on; - regulator-name = "vddq_ddr_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vcc_1v8_s3: dcdc-reg10 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-name = "vcc_1v8_s3"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <1800000>; - }; - }; - - avcc_1v8_s0: pldo-reg1 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-name = "avcc_1v8_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vcc_1v8_s0: pldo-reg2 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-name = "vcc_1v8_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - regulator-suspend-microvolt = <1800000>; - }; - }; - - avdd_1v2_s0: pldo-reg3 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - regulator-name = "avdd_1v2_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vcc_3v3_s0: pldo-reg4 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-ramp-delay = <12500>; - regulator-name = "vcc_3v3_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vccio_sd_s0: pldo-reg5 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <3300000>; - regulator-ramp-delay = <12500>; - regulator-name = "vccio_sd_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - pldo6_s3: pldo-reg6 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-name = "pldo6_s3"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <1800000>; - }; - }; - - vdd_0v75_s3: nldo-reg1 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <750000>; - regulator-max-microvolt = <750000>; - regulator-name = "vdd_0v75_s3"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <750000>; - }; - }; - - vdd_ddr_pll_s0: nldo-reg2 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <850000>; - regulator-max-microvolt = <850000>; - regulator-name = "vdd_ddr_pll_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - regulator-suspend-microvolt = <850000>; - }; - }; - - avdd_0v75_s0: nldo-reg3 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <750000>; - regulator-max-microvolt = <750000>; - regulator-name = "avdd_0v75_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vdd_0v85_s0: nldo-reg4 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <850000>; - regulator-max-microvolt = <850000>; - regulator-name = "vdd_0v85_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vdd_0v75_s0: nldo-reg5 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <750000>; - regulator-max-microvolt = <750000>; - regulator-name = "vdd_0v75_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - }; - }; -}; - -/* - pin19: GPIO1_C1 - pin21: GPIO1_C0 - pin23: GPIO1_C2 - pin24: GPIO1_C4 -*/ -&spi4 { - pinctrl-names = "default"; - pinctrl-0 = <&spi4m0_cs1 &spi4m0_pins>; - assigned-clocks = <&cru CLK_SPI4>; - assigned-clock-rates = <200000000>; - num-cs = <2>; - status = "disabled"; - - spi_dev@1 { - compatible = "rockchip,spidev"; - reg = <1>; - spi-max-frequency = <50000000>; - }; -}; - -/* - pin8: GPIO4_A3 - pin10: GPIO4_A4 -*/ -&uart0 { - pinctrl-names = "default"; - pinctrl-0 = <&uart0m2_xfer>; - status = "disabled"; -}; - -&uart1 { - pinctrl-names = "default"; - pinctrl-0 = <&uart1m1_xfer>; - status = "disabled"; -}; - -&uart2 { - pinctrl-0 = <&uart2m0_xfer>; - status = "okay"; -}; - -&uart3 { - pinctrl-names = "default"; - pinctrl-0 = <&uart3m0_xfer>; - status = "disabled"; -}; - -/* - pin16: GPIO1_D3 - pin18: GPIO1_D2 -*/ -&uart4 { - pinctrl-names = "default"; - pinctrl-0 = <&uart4m0_xfer>; - status = "disabled"; -}; - -&uart9 { - pinctrl-names = "default"; - pinctrl-0 = <&uart9m2_xfer &uart9m2_ctsn>; - status = "okay"; -}; - -&tsadc { - status = "okay"; -}; - -&sata0 { - pinctrl-names = "default"; - pinctrl-0 = <&sata_reset>; - status = "disabled"; -}; - -&sfc { - status = "okay"; - max-freq = <100000000>; - #address-cells = <1>; - #size-cells = <0>; - pinctrl-names = "default"; - pinctrl-0 = <&fspim0_pins>; - - spi_flash: spi-flash@0 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "jedec,spi-nor"; - reg = <0x0>; - spi-max-frequency = <100000000>; - spi-tx-bus-width = <1>; - spi-rx-bus-width = <4>; - status = "okay"; - - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - loader@0 { - label = "loader"; - reg = <0x0 0x1000000>; - }; - }; - }; -}; - - -&pcie2x1l2 { - reset-gpios = <&gpio3 RK_PD1 GPIO_ACTIVE_HIGH>; - vpcie3v3-supply = <&vcc3v3_pcie2x1l2>; - rockchip,skip-scan-in-resume; - status = "okay"; -}; - -&u2phy0 { - status = "okay"; -}; - -&u2phy0_otg { - rockchip,typec-vbus-det; - status = "okay"; -}; - -&u2phy2 { - status = "okay"; -}; - -&u2phy2_host { - status = "okay"; -}; - -&u2phy3 { - status = "okay"; -}; - -&u2phy3_host { - status = "okay"; -}; - -&usbdp_phy0 { - orientation-switch; - mode-switch; - svid = <0xff01>; - sbu1-dc-gpios = <&gpio4 RK_PA5 GPIO_ACTIVE_HIGH>; - sbu2-dc-gpios = <&gpio4 RK_PA7 GPIO_ACTIVE_HIGH>; - - port { - #address-cells = <1>; - #size-cells = <0>; - - usbdp_phy0_typec_ss: endpoint@0 { - reg = <0>; - remote-endpoint = <&usbc0_ss>; - }; - - usbdp_phy0_typec_sbu: endpoint@1 { - reg = <1>; - remote-endpoint = <&usbc0_sbu>; - }; - }; -}; - -&usbdp_phy0_dp { - status = "okay"; -}; - -&usbdp_phy0_u3 { - status = "okay"; -}; - -&usb_host0_ehci { - status = "okay"; -}; - -&usb_host0_ohci { - status = "okay"; -}; - -&usb_host0_xhci { - usb-role-switch; - dr_mode = "otg"; - status = "okay"; - - port { - #address-cells = <1>; - #size-cells = <0>; - - usb_host0_xhci_drd_sw: endpoint { - remote-endpoint = <&usbc0_hs>; - }; - }; -}; - -&usb_host1_ehci { - status = "okay"; -}; - -&usb_host1_ohci { - status = "okay"; -}; - -&usb_host2_xhci { - dr_mode = "host"; - status = "okay"; -}; - -&wdt { - status = "okay"; -};