From 97e8425444a66d386755a097dc15669d60ba3552 Mon Sep 17 00:00:00 2001 From: Martin Sekera Date: Tue, 21 Sep 2021 09:44:31 +0200 Subject: [PATCH] re-add kernel 5.13, mask 5.14 --- .../decade-sources-5.13.19.ebuild | 25 + .../decade-sources-5.14.6.ebuild | 30 +- .../patches-5.13/add-board-nanopi-m4v2.patch | 96 + .../add-csgpio-to-rockchip-spi.patch | 0 .../add-fusb30x-driver.patch | 0 .../add-maker-friendlyarm.patch | 0 ...board-nanopi-m4v2-dts-add-sound-card.patch | 0 ...board-nanopi-m4v2-dts-ethernet-tweak.patch | 0 ...nanopi-m4v2-dts-fix-stability-issues.patch | 0 ...roc-rk3399-pc-fix-fusb302-compatible.patch | 0 ...dwmac-rk-Add-MAC-driver-support-for-.patch | 88 + ...ockchip-add-tsadc-support-for-rk3308.patch | 78 + ...d-support-for-rockchip-i2s-tdm-contr.patch | 0 ...IP-Sync-rockchip_i2s_tdm-to-BSP-tree.patch | 2063 +++++ ...ecs-Add-RK3308-internal-codec-driver.patch | 0 ...s-0019-Sync-rk3308_codec-to-BSP-tree.patch | 0 ...308_codec-replace-codec-to-component.patch | 0 .../general-emmc-hs400es-init-tweak.patch | 0 .../general-fix-es8316-kernel-panic.patch | 0 ..._DMA_block_memory_allocation_to_2048.patch | 0 .../general-rt5651-add-mclk.patch | 0 .../rk3399-add-sclk-i2sout-src-clock.patch | 0 ...k3399-enable-dwc3-xhci-usb-trb-quirk.patch | 0 ...kchip-support-ep-gpio-undefined-case.patch | 0 .../rk3399-sd-drive-level-8ma.patch | 0 .../add-board-nanopi-m4v2.patch | 0 .../add-csgpio-to-rockchip-spi.patch | 83 + .../patches-5.14/add-fusb30x-driver.patch | 4047 ++++++++++ .../patches-5.14/add-maker-friendlyarm.patch | 211 + ...board-nanopi-m4v2-dts-add-sound-card.patch | 106 + ...board-nanopi-m4v2-dts-ethernet-tweak.patch | 15 + ...nanopi-m4v2-dts-fix-stability-issues.patch | 29 + .../board-nanopi-r2s-r8152-mac-from-dt.patch | 0 ...roc-rk3399-pc-fix-fusb302-compatible.patch | 22 + ...ockchip-add-tsadc-support-for-rk3308.patch | 0 ...d-support-for-rockchip-i2s-tdm-contr.patch | 1083 +++ ...IP-Sync-rockchip_i2s_tdm-to-BSP-tree.patch | 0 ...ecs-Add-RK3308-internal-codec-driver.patch | 2645 +++++++ ...s-0019-Sync-rk3308_codec-to-BSP-tree.patch | 6740 +++++++++++++++++ ...308_codec-replace-codec-to-component.patch | 459 ++ .../general-emmc-hs400es-init-tweak.patch | 22 + .../general-fix-es8316-kernel-panic.patch | 11 + ...fix-mmc-signal-voltage-before-reboot.patch | 0 ..._DMA_block_memory_allocation_to_2048.patch | 21 + ...08-configurable-switch-voltage-steps.patch | 0 .../general-rt5651-add-mclk.patch | 60 + .../rk3399-add-sclk-i2sout-src-clock.patch | 25 + ...k3399-enable-dwc3-xhci-usb-trb-quirk.patch | 20 + ...kchip-support-ep-gpio-undefined-case.patch | 28 + .../rk3399-sd-drive-level-8ma.patch | 49 + 50 files changed, 18028 insertions(+), 28 deletions(-) create mode 100644 sys-kernel/decade-sources/decade-sources-5.13.19.ebuild create mode 100644 sys-kernel/decade-sources/files/patches-5.13/add-board-nanopi-m4v2.patch rename sys-kernel/decade-sources/files/{ => patches-5.13}/add-csgpio-to-rockchip-spi.patch (100%) rename sys-kernel/decade-sources/files/{ => patches-5.13}/add-fusb30x-driver.patch (100%) rename sys-kernel/decade-sources/files/{ => patches-5.13}/add-maker-friendlyarm.patch (100%) rename sys-kernel/decade-sources/files/{ => patches-5.13}/board-nanopi-m4v2-dts-add-sound-card.patch (100%) rename sys-kernel/decade-sources/files/{ => patches-5.13}/board-nanopi-m4v2-dts-ethernet-tweak.patch (100%) rename sys-kernel/decade-sources/files/{ => patches-5.13}/board-nanopi-m4v2-dts-fix-stability-issues.patch (100%) rename sys-kernel/decade-sources/files/{ => patches-5.13}/board-roc-rk3399-pc-fix-fusb302-compatible.patch (100%) create mode 100644 sys-kernel/decade-sources/files/patches-5.13/board-rockpis-0004-ethernet-stmmac-dwmac-rk-Add-MAC-driver-support-for-.patch create mode 100644 sys-kernel/decade-sources/files/patches-5.13/board-rockpis-0008-thermal-rockchip-add-tsadc-support-for-rk3308.patch rename sys-kernel/decade-sources/files/{ => patches-5.13}/board-rockpis-0016-ASoC-rockchip-add-support-for-rockchip-i2s-tdm-contr.patch (100%) create mode 100644 sys-kernel/decade-sources/files/patches-5.13/board-rockpis-0017-WIP-Sync-rockchip_i2s_tdm-to-BSP-tree.patch rename sys-kernel/decade-sources/files/{ => patches-5.13}/board-rockpis-0018-ASoC-codecs-Add-RK3308-internal-codec-driver.patch (100%) rename sys-kernel/decade-sources/files/{ => patches-5.13}/board-rockpis-0019-Sync-rk3308_codec-to-BSP-tree.patch (100%) rename sys-kernel/decade-sources/files/{ => patches-5.13}/board-rockpis-0022-ASoC-rk3308_codec-replace-codec-to-component.patch (100%) rename sys-kernel/decade-sources/files/{ => patches-5.13}/general-emmc-hs400es-init-tweak.patch (100%) rename sys-kernel/decade-sources/files/{ => patches-5.13}/general-fix-es8316-kernel-panic.patch (100%) rename sys-kernel/decade-sources/files/{ => patches-5.13}/general-increasing_DMA_block_memory_allocation_to_2048.patch (100%) rename sys-kernel/decade-sources/files/{ => patches-5.13}/general-rt5651-add-mclk.patch (100%) rename sys-kernel/decade-sources/files/{ => patches-5.13}/rk3399-add-sclk-i2sout-src-clock.patch (100%) rename sys-kernel/decade-sources/files/{ => patches-5.13}/rk3399-enable-dwc3-xhci-usb-trb-quirk.patch (100%) rename sys-kernel/decade-sources/files/{ => patches-5.13}/rk3399-pci-rockchip-support-ep-gpio-undefined-case.patch (100%) rename sys-kernel/decade-sources/files/{ => patches-5.13}/rk3399-sd-drive-level-8ma.patch (100%) rename sys-kernel/decade-sources/files/{ => patches-5.14}/add-board-nanopi-m4v2.patch (100%) create mode 100644 sys-kernel/decade-sources/files/patches-5.14/add-csgpio-to-rockchip-spi.patch create mode 100644 sys-kernel/decade-sources/files/patches-5.14/add-fusb30x-driver.patch create mode 100644 sys-kernel/decade-sources/files/patches-5.14/add-maker-friendlyarm.patch create mode 100644 sys-kernel/decade-sources/files/patches-5.14/board-nanopi-m4v2-dts-add-sound-card.patch create mode 100644 sys-kernel/decade-sources/files/patches-5.14/board-nanopi-m4v2-dts-ethernet-tweak.patch create mode 100644 sys-kernel/decade-sources/files/patches-5.14/board-nanopi-m4v2-dts-fix-stability-issues.patch rename sys-kernel/decade-sources/files/{ => patches-5.14}/board-nanopi-r2s-r8152-mac-from-dt.patch (100%) create mode 100644 sys-kernel/decade-sources/files/patches-5.14/board-roc-rk3399-pc-fix-fusb302-compatible.patch rename sys-kernel/decade-sources/files/{ => patches-5.14}/board-rockpis-0008-thermal-rockchip-add-tsadc-support-for-rk3308.patch (100%) create mode 100644 sys-kernel/decade-sources/files/patches-5.14/board-rockpis-0016-ASoC-rockchip-add-support-for-rockchip-i2s-tdm-contr.patch rename sys-kernel/decade-sources/files/{ => patches-5.14}/board-rockpis-0017-WIP-Sync-rockchip_i2s_tdm-to-BSP-tree.patch (100%) create mode 100644 sys-kernel/decade-sources/files/patches-5.14/board-rockpis-0018-ASoC-codecs-Add-RK3308-internal-codec-driver.patch create mode 100644 sys-kernel/decade-sources/files/patches-5.14/board-rockpis-0019-Sync-rk3308_codec-to-BSP-tree.patch create mode 100644 sys-kernel/decade-sources/files/patches-5.14/board-rockpis-0022-ASoC-rk3308_codec-replace-codec-to-component.patch create mode 100644 sys-kernel/decade-sources/files/patches-5.14/general-emmc-hs400es-init-tweak.patch create mode 100644 sys-kernel/decade-sources/files/patches-5.14/general-fix-es8316-kernel-panic.patch rename sys-kernel/decade-sources/files/{ => patches-5.14}/general-fix-mmc-signal-voltage-before-reboot.patch (100%) create mode 100644 sys-kernel/decade-sources/files/patches-5.14/general-increasing_DMA_block_memory_allocation_to_2048.patch rename sys-kernel/decade-sources/files/{ => patches-5.14}/general-rk808-configurable-switch-voltage-steps.patch (100%) create mode 100644 sys-kernel/decade-sources/files/patches-5.14/general-rt5651-add-mclk.patch create mode 100644 sys-kernel/decade-sources/files/patches-5.14/rk3399-add-sclk-i2sout-src-clock.patch create mode 100644 sys-kernel/decade-sources/files/patches-5.14/rk3399-enable-dwc3-xhci-usb-trb-quirk.patch create mode 100644 sys-kernel/decade-sources/files/patches-5.14/rk3399-pci-rockchip-support-ep-gpio-undefined-case.patch create mode 100644 sys-kernel/decade-sources/files/patches-5.14/rk3399-sd-drive-level-8ma.patch diff --git a/sys-kernel/decade-sources/decade-sources-5.13.19.ebuild b/sys-kernel/decade-sources/decade-sources-5.13.19.ebuild new file mode 100644 index 0000000..9f3853e --- /dev/null +++ b/sys-kernel/decade-sources/decade-sources-5.13.19.ebuild @@ -0,0 +1,25 @@ +# Copyright 1999-2021 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI="7" +ETYPE="sources" + +inherit kernel-2 +detect_version +detect_arch + +KEYWORDS="~arm64" +HOMEPAGE="https://dev.gentoo.org/~mpagano/genpatches" + +DESCRIPTION="Full sources for the Linux ${KV_MAJOR}.${KV_MINOR} kernel tree." +SRC_URI="${KERNEL_URI}" + +DECADE_PATCHES=( ${FILESDIR}/patches-${KV_MAJOR}.${KV_MINOR}/*.patch ) + +src_prepare() { + for P in ${DECADE_PATCHES[@]}; do + eapply "${P}" + done + + kernel-2_src_prepare +} diff --git a/sys-kernel/decade-sources/decade-sources-5.14.6.ebuild b/sys-kernel/decade-sources/decade-sources-5.14.6.ebuild index 6dac9e6..95b26de 100644 --- a/sys-kernel/decade-sources/decade-sources-5.14.6.ebuild +++ b/sys-kernel/decade-sources/decade-sources-5.14.6.ebuild @@ -8,39 +8,13 @@ inherit kernel-2 detect_version detect_arch -KEYWORDS="~arm64" +KEYWORDS="" HOMEPAGE="https://dev.gentoo.org/~mpagano/genpatches" DESCRIPTION="Full sources for the Linux ${KV_MAJOR}.${KV_MINOR} kernel tree." SRC_URI="${KERNEL_URI}" -DECADE_PATCHES=( - "${FILESDIR}/add-board-nanopi-m4v2.patch" - "${FILESDIR}/add-csgpio-to-rockchip-spi.patch" - "${FILESDIR}/add-fusb30x-driver.patch" - "${FILESDIR}/add-maker-friendlyarm.patch" - "${FILESDIR}/board-nanopi-m4v2-dts-add-sound-card.patch" - "${FILESDIR}/board-nanopi-m4v2-dts-ethernet-tweak.patch" - "${FILESDIR}/board-nanopi-m4v2-dts-fix-stability-issues.patch" - "${FILESDIR}/board-nanopi-r2s-r8152-mac-from-dt.patch" - "${FILESDIR}/board-roc-rk3399-pc-fix-fusb302-compatible.patch" - "${FILESDIR}/board-rockpis-0008-thermal-rockchip-add-tsadc-support-for-rk3308.patch" - "${FILESDIR}/board-rockpis-0016-ASoC-rockchip-add-support-for-rockchip-i2s-tdm-contr.patch" - "${FILESDIR}/board-rockpis-0017-WIP-Sync-rockchip_i2s_tdm-to-BSP-tree.patch" - "${FILESDIR}/board-rockpis-0018-ASoC-codecs-Add-RK3308-internal-codec-driver.patch" - "${FILESDIR}/board-rockpis-0019-Sync-rk3308_codec-to-BSP-tree.patch" - "${FILESDIR}/board-rockpis-0022-ASoC-rk3308_codec-replace-codec-to-component.patch" - "${FILESDIR}/general-emmc-hs400es-init-tweak.patch" - "${FILESDIR}/general-fix-es8316-kernel-panic.patch" - "${FILESDIR}/general-fix-mmc-signal-voltage-before-reboot.patch" - "${FILESDIR}/general-increasing_DMA_block_memory_allocation_to_2048.patch" - "${FILESDIR}/general-rk808-configurable-switch-voltage-steps.patch" - "${FILESDIR}/general-rt5651-add-mclk.patch" - "${FILESDIR}/rk3399-add-sclk-i2sout-src-clock.patch" - "${FILESDIR}/rk3399-enable-dwc3-xhci-usb-trb-quirk.patch" - "${FILESDIR}/rk3399-pci-rockchip-support-ep-gpio-undefined-case.patch" - "${FILESDIR}/rk3399-sd-drive-level-8ma.patch" -) +DECADE_PATCHES=( ${FILESDIR}/patches-${KV_MAJOR}.${KV_MINOR}/*.patch ) src_prepare() { for P in ${DECADE_PATCHES[@]}; do diff --git a/sys-kernel/decade-sources/files/patches-5.13/add-board-nanopi-m4v2.patch b/sys-kernel/decade-sources/files/patches-5.13/add-board-nanopi-m4v2.patch new file mode 100644 index 0000000..f95481d --- /dev/null +++ b/sys-kernel/decade-sources/files/patches-5.13/add-board-nanopi-m4v2.patch @@ -0,0 +1,96 @@ +diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile +index c3e00c0e2db7..c41203318e3a 100644 +--- a/arch/arm64/boot/dts/rockchip/Makefile ++++ b/arch/arm64/boot/dts/rockchip/Makefile +@@ -34,6 +34,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-kobol-helios64.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-leez-p710.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-nanopc-t4.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-nanopi-m4.dtb ++dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-nanopi-m4v2.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-nanopi-m4b.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-nanopi-neo4.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-nanopi-r4s.dtb +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-nanopi-m4v2.dts b/arch/arm64/boot/dts/rockchip/rk3399-nanopi-m4v2.dts +new file mode 100644 +index 000000000..60358ab8c +--- /dev/null ++++ b/arch/arm64/boot/dts/rockchip/rk3399-nanopi-m4v2.dts +@@ -0,0 +1,78 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * FriendlyElec NanoPi M4V2 board device tree source ++ * ++ * Copyright (c) 2018 FriendlyElec Computer Tech. Co., Ltd. ++ * (http://www.friendlyarm.com) ++ * ++ * Copyright (c) 2018 Collabora Ltd. ++ * Copyright (c) 2019 Arm Ltd. ++ */ ++ ++/dts-v1/; ++#include "rk3399-nanopi4.dtsi" ++ ++/ { ++ model = "FriendlyElec NanoPi M4 Ver2.0"; ++ compatible = "friendlyarm,nanopi-m4", "rockchip,rk3399"; ++ ++ vdd_5v: vdd-5v { ++ compatible = "regulator-fixed"; ++ regulator-name = "vdd_5v"; ++ regulator-always-on; ++ regulator-boot-on; ++ }; ++ ++ vcc5v0_core: vcc5v0-core { ++ compatible = "regulator-fixed"; ++ regulator-name = "vcc5v0_core"; ++ regulator-always-on; ++ regulator-boot-on; ++ vin-supply = <&vdd_5v>; ++ }; ++ ++ vcc5v0_usb1: vcc5v0-usb1 { ++ compatible = "regulator-fixed"; ++ regulator-name = "vcc5v0_usb1"; ++ regulator-always-on; ++ regulator-boot-on; ++ vin-supply = <&vcc5v0_sys>; ++ }; ++ ++ vcc5v0_usb2: vcc5v0-usb2 { ++ compatible = "regulator-fixed"; ++ regulator-name = "vcc5v0_usb2"; ++ regulator-always-on; ++ regulator-boot-on; ++ vin-supply = <&vcc5v0_sys>; ++ }; ++ ++ vdd_log: vdd-log { ++ compatible = "pwm-regulator"; ++ pwms = <&pwm2 0 25000 1>; ++ regulator-name = "vdd_log"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <1400000>; ++ regulator-init-microvolt = <900000>; ++ vin-supply = <&vcc5v0_core>; ++ }; ++}; ++ ++&vcc3v3_sys { ++ vin-supply = <&vcc5v0_core>; ++}; ++ ++&u2phy0_host { ++ phy-supply = <&vcc5v0_usb1>; ++}; ++ ++&u2phy1_host { ++ phy-supply = <&vcc5v0_usb2>; ++}; ++ ++&vbus_typec { ++ regulator-always-on; ++ vin-supply = <&vdd_5v>; ++}; diff --git a/sys-kernel/decade-sources/files/add-csgpio-to-rockchip-spi.patch b/sys-kernel/decade-sources/files/patches-5.13/add-csgpio-to-rockchip-spi.patch similarity index 100% rename from sys-kernel/decade-sources/files/add-csgpio-to-rockchip-spi.patch rename to sys-kernel/decade-sources/files/patches-5.13/add-csgpio-to-rockchip-spi.patch diff --git a/sys-kernel/decade-sources/files/add-fusb30x-driver.patch b/sys-kernel/decade-sources/files/patches-5.13/add-fusb30x-driver.patch similarity index 100% rename from sys-kernel/decade-sources/files/add-fusb30x-driver.patch rename to sys-kernel/decade-sources/files/patches-5.13/add-fusb30x-driver.patch diff --git a/sys-kernel/decade-sources/files/add-maker-friendlyarm.patch b/sys-kernel/decade-sources/files/patches-5.13/add-maker-friendlyarm.patch similarity index 100% rename from sys-kernel/decade-sources/files/add-maker-friendlyarm.patch rename to sys-kernel/decade-sources/files/patches-5.13/add-maker-friendlyarm.patch diff --git a/sys-kernel/decade-sources/files/board-nanopi-m4v2-dts-add-sound-card.patch b/sys-kernel/decade-sources/files/patches-5.13/board-nanopi-m4v2-dts-add-sound-card.patch similarity index 100% rename from sys-kernel/decade-sources/files/board-nanopi-m4v2-dts-add-sound-card.patch rename to sys-kernel/decade-sources/files/patches-5.13/board-nanopi-m4v2-dts-add-sound-card.patch diff --git a/sys-kernel/decade-sources/files/board-nanopi-m4v2-dts-ethernet-tweak.patch b/sys-kernel/decade-sources/files/patches-5.13/board-nanopi-m4v2-dts-ethernet-tweak.patch similarity index 100% rename from sys-kernel/decade-sources/files/board-nanopi-m4v2-dts-ethernet-tweak.patch rename to sys-kernel/decade-sources/files/patches-5.13/board-nanopi-m4v2-dts-ethernet-tweak.patch diff --git a/sys-kernel/decade-sources/files/board-nanopi-m4v2-dts-fix-stability-issues.patch b/sys-kernel/decade-sources/files/patches-5.13/board-nanopi-m4v2-dts-fix-stability-issues.patch similarity index 100% rename from sys-kernel/decade-sources/files/board-nanopi-m4v2-dts-fix-stability-issues.patch rename to sys-kernel/decade-sources/files/patches-5.13/board-nanopi-m4v2-dts-fix-stability-issues.patch diff --git a/sys-kernel/decade-sources/files/board-roc-rk3399-pc-fix-fusb302-compatible.patch b/sys-kernel/decade-sources/files/patches-5.13/board-roc-rk3399-pc-fix-fusb302-compatible.patch similarity index 100% rename from sys-kernel/decade-sources/files/board-roc-rk3399-pc-fix-fusb302-compatible.patch rename to sys-kernel/decade-sources/files/patches-5.13/board-roc-rk3399-pc-fix-fusb302-compatible.patch diff --git a/sys-kernel/decade-sources/files/patches-5.13/board-rockpis-0004-ethernet-stmmac-dwmac-rk-Add-MAC-driver-support-for-.patch b/sys-kernel/decade-sources/files/patches-5.13/board-rockpis-0004-ethernet-stmmac-dwmac-rk-Add-MAC-driver-support-for-.patch new file mode 100644 index 0000000..8653970 --- /dev/null +++ b/sys-kernel/decade-sources/files/patches-5.13/board-rockpis-0004-ethernet-stmmac-dwmac-rk-Add-MAC-driver-support-for-.patch @@ -0,0 +1,88 @@ +From 3aab567792a7fa949e8aca7a92a31f15d435efbe Mon Sep 17 00:00:00 2001 +From: ashthespy +Date: Thu, 16 Jan 2020 21:11:56 +0100 +Subject: [PATCH 04/23] ethernet: stmmac: dwmac-rk: Add MAC driver support for + rk3308 + +Probably in the wrong place, as it's only 10/100 but :meh: + +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c +index dc50ba13a746..cfeca75ed284 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c +@@ -482,6 +482,64 @@ static const struct rk_gmac_ops rk3288_ops = { + .set_rmii_speed = rk3288_set_rmii_speed, + }; + ++#define RK3308_GRF_MAC_CON0 0x04a0 ++ ++/* Rk3308_GRF_MAC_CON1 */ ++#define RK3308_MAC_PHY_INTF_SEL_RMII (GRF_CLR_BIT(2) | GRF_CLR_BIT(3) | \ ++ GRF_BIT(4)) ++#define RK3308_MAC_SPEED_10M GRF_CLR_BIT(0) ++#define Rk3308_MAC_SPEED_100M GRF_BIT(0) ++ ++static void rk3308_set_to_rmii(struct rk_priv_data *bsp_priv) ++{ ++ struct device *dev = &bsp_priv->pdev->dev; ++ ++ if (IS_ERR(bsp_priv->grf)) { ++ dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); ++ return; ++ } ++ ++ regmap_write(bsp_priv->grf, RK3308_GRF_MAC_CON0, ++ RK3308_MAC_PHY_INTF_SEL_RMII); ++} ++ ++static void rk3308_set_rmii_speed(struct rk_priv_data *bsp_priv, int speed) ++{ ++ struct device *dev = &bsp_priv->pdev->dev; ++ int ret; ++ ++ if (IS_ERR(bsp_priv->clk_mac_speed)) { ++ dev_err(dev, "%s: Missing clk_mac_speed clock\n", __func__); ++ return; ++ } ++ ++ if (speed == 10) { ++ regmap_write(bsp_priv->grf, RK3308_GRF_MAC_CON0, ++ RK3308_MAC_SPEED_10M); ++ ++ ret = clk_set_rate(bsp_priv->clk_mac_speed, 2500000); ++ if (ret) ++ dev_err(dev, "%s: set clk_mac_speed rate 2500000 failed: %d\n", ++ __func__, ret); ++ } else if (speed == 100) { ++ regmap_write(bsp_priv->grf, RK3308_GRF_MAC_CON0, ++ Rk3308_MAC_SPEED_100M); ++ ++ ret = clk_set_rate(bsp_priv->clk_mac_speed, 25000000); ++ if (ret) ++ dev_err(dev, "%s: set clk_mac_speed rate 25000000 failed: %d\n", ++ __func__, ret); ++ ++ } else { ++ dev_err(dev, "unknown speed value for RMII! speed=%d", speed); ++ } ++} ++ ++static const struct rk_gmac_ops rk3308_ops = { ++ .set_to_rmii = rk3308_set_to_rmii, ++ .set_rmii_speed = rk3308_set_rmii_speed, ++}; ++ + #define RK3328_GRF_MAC_CON0 0x0900 + #define RK3328_GRF_MAC_CON1 0x0904 + #define RK3328_GRF_MAC_CON2 0x0908 +@@ -1477,6 +1535,7 @@ static const struct of_device_id rk_gmac_dwmac_match[] = { + { .compatible = "rockchip,rk3128-gmac", .data = &rk3128_ops }, + { .compatible = "rockchip,rk3228-gmac", .data = &rk3228_ops }, + { .compatible = "rockchip,rk3288-gmac", .data = &rk3288_ops }, ++ { .compatible = "rockchip,rk3308-gmac", .data = &rk3308_ops }, + { .compatible = "rockchip,rk3328-gmac", .data = &rk3328_ops }, + { .compatible = "rockchip,rk3366-gmac", .data = &rk3366_ops }, + { .compatible = "rockchip,rk3368-gmac", .data = &rk3368_ops }, +-- +2.25.1 + diff --git a/sys-kernel/decade-sources/files/patches-5.13/board-rockpis-0008-thermal-rockchip-add-tsadc-support-for-rk3308.patch b/sys-kernel/decade-sources/files/patches-5.13/board-rockpis-0008-thermal-rockchip-add-tsadc-support-for-rk3308.patch new file mode 100644 index 0000000..b041586 --- /dev/null +++ b/sys-kernel/decade-sources/files/patches-5.13/board-rockpis-0008-thermal-rockchip-add-tsadc-support-for-rk3308.patch @@ -0,0 +1,78 @@ +From 498c9f200325f0397fd03163a98e053430b80aa4 Mon Sep 17 00:00:00 2001 +From: ashthespy +Date: Fri, 17 Jan 2020 15:58:20 +0100 +Subject: [PATCH 08/23] thermal: rockchip: add tsadc support for rk3308 + +From a231e9c68e5f5e6cf5a82a40828cfd1df4ad1f3e Mon Sep 17 00:00:00 2001 +From: Rocky Hao +Date: Fri, 9 Mar 2018 17:36:39 +0800 +Subject: [PATCH] thermal: rockchip: add tsadc support for rk3308 + +Change-Id: Ibf1782ca471c8ad4b14d6fd64eeb123181903adc +Signed-off-by: Rocky Hao +--- + .../bindings/thermal/rockchip-thermal.txt | 1 + + drivers/thermal/rockchip_thermal.c | 28 +++++++++++++++++++ + 2 files changed, 29 insertions(+) + +diff --git a/Documentation/devicetree/bindings/thermal/rockchip-thermal.txt b/Documentation/devicetree/bindings/thermal/rockchip-thermal.txt +index c6aac9bcacf1..3a0a9556680e 100644 +--- a/Documentation/devicetree/bindings/thermal/rockchip-thermal.txt ++++ b/Documentation/devicetree/bindings/thermal/rockchip-thermal.txt +@@ -6,6 +6,7 @@ Required properties: + "rockchip,rv1108-tsadc": found on RV1108 SoCs + "rockchip,rk3228-tsadc": found on RK3228 SoCs + "rockchip,rk3288-tsadc": found on RK3288 SoCs ++ "rockchip,rk3308-tsadc": found on RK3308 SoCs + "rockchip,rk3328-tsadc": found on RK3328 SoCs + "rockchip,rk3368-tsadc": found on RK3368 SoCs + "rockchip,rk3399-tsadc": found on RK3399 SoCs +diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c +index 343c2f5c5a25..d4d66724535a 100644 +--- a/drivers/thermal/rockchip_thermal.c ++++ b/drivers/thermal/rockchip_thermal.c +@@ -821,6 +821,30 @@ static void rk_tsadcv2_tshut_mode(int chn, void __iomem *regs, + writel_relaxed(val, regs + TSADCV2_INT_EN); + } + ++static const struct rockchip_tsadc_chip rk3308_tsadc_data = { ++ .chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */ ++ .chn_id[SENSOR_GPU] = 1, /* gpu sensor is channel 1 */ ++ .chn_num = 2, /* 2 channels for tsadc */ ++ ++ .tshut_mode = TSHUT_MODE_CRU, /* default TSHUT via CRU */ ++ .tshut_temp = 95000, ++ ++ .initialize = rk_tsadcv4_initialize, ++ .irq_ack = rk_tsadcv3_irq_ack, ++ .control = rk_tsadcv3_control, ++ .get_temp = rk_tsadcv2_get_temp, ++ .set_alarm_temp = rk_tsadcv2_alarm_temp, ++ .set_tshut_temp = rk_tsadcv2_tshut_temp, ++ .set_tshut_mode = rk_tsadcv2_tshut_mode, ++ ++ .table = { ++ .id = rk3328_code_table, ++ .length = ARRAY_SIZE(rk3328_code_table), ++ .data_mask = TSADCV2_DATA_MASK, ++ .mode = ADC_INCREMENT, ++ }, ++}; ++ + static const struct rockchip_tsadc_chip px30_tsadc_data = { + .chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */ + .chn_id[SENSOR_GPU] = 1, /* gpu sensor is channel 1 */ +@@ -1032,6 +1056,10 @@ static const struct of_device_id of_rockchip_thermal_match[] = { + .compatible = "rockchip,rk3288-tsadc", + .data = (void *)&rk3288_tsadc_data, + }, ++ { ++ .compatible = "rockchip,rk3308-tsadc", ++ .data = (void *)&rk3308_tsadc_data, ++ }, + { + .compatible = "rockchip,rk3328-tsadc", + .data = (void *)&rk3328_tsadc_data, +-- +2.25.1 + diff --git a/sys-kernel/decade-sources/files/board-rockpis-0016-ASoC-rockchip-add-support-for-rockchip-i2s-tdm-contr.patch b/sys-kernel/decade-sources/files/patches-5.13/board-rockpis-0016-ASoC-rockchip-add-support-for-rockchip-i2s-tdm-contr.patch similarity index 100% rename from sys-kernel/decade-sources/files/board-rockpis-0016-ASoC-rockchip-add-support-for-rockchip-i2s-tdm-contr.patch rename to sys-kernel/decade-sources/files/patches-5.13/board-rockpis-0016-ASoC-rockchip-add-support-for-rockchip-i2s-tdm-contr.patch diff --git a/sys-kernel/decade-sources/files/patches-5.13/board-rockpis-0017-WIP-Sync-rockchip_i2s_tdm-to-BSP-tree.patch b/sys-kernel/decade-sources/files/patches-5.13/board-rockpis-0017-WIP-Sync-rockchip_i2s_tdm-to-BSP-tree.patch new file mode 100644 index 0000000..a44b51d --- /dev/null +++ b/sys-kernel/decade-sources/files/patches-5.13/board-rockpis-0017-WIP-Sync-rockchip_i2s_tdm-to-BSP-tree.patch @@ -0,0 +1,2063 @@ +From 7d997d4fe8ee4756f63ea6188f091759bca01b20 Mon Sep 17 00:00:00 2001 +From: ashthespy +Date: Mon, 3 Feb 2020 16:31:07 +0100 +Subject: [PATCH 17/23] [WIP] Sync `rockchip_i2s_tdm` to BSP tree + +--- + .../bindings/sound/rockchip,i2s-tdm.txt | 50 + + sound/soc/rockchip/rockchip_i2s.c | 167 ++- + sound/soc/rockchip/rockchip_i2s_tdm.c | 1136 ++++++++++++++++- + sound/soc/rockchip/rockchip_i2s_tdm.h | 138 +- + 4 files changed, 1379 insertions(+), 112 deletions(-) + +diff --git a/Documentation/devicetree/bindings/sound/rockchip,i2s-tdm.txt b/Documentation/devicetree/bindings/sound/rockchip,i2s-tdm.txt +index e26e72e3315f..db130be87d47 100644 +--- a/Documentation/devicetree/bindings/sound/rockchip,i2s-tdm.txt ++++ b/Documentation/devicetree/bindings/sound/rockchip,i2s-tdm.txt +@@ -3,6 +3,8 @@ + Required properties: + + - compatible: should be one of the following ++ - "rockchip,px30-i2s-tdm": for px30 ++ - "rockchip,rk1808-i2s-tdm": for rk1808 + - "rockchip,rk3308-i2s-tdm": for rk3308 + - reg: physical base address of the controller and length of memory mapped + region. +@@ -13,6 +15,50 @@ Required properties: + - clocks: a list of phandle + clock-specifer pairs, one for each entry in clock-names. + - clock-names: clock names. + - rockchip,bclk-fs: configure the bclk fs. ++- resets: a list of phandle + reset-specifer paris, one for each entry in reset-names. ++- reset-names: reset names, should include "tx-m", "rx-m". ++- rockchip,cru: cru phandle. ++- rockchip,grf: the phandle of the syscon node for GRF register. ++- rockchip,mclk-calibrate: enable mclk source calibration. ++- rockchip,clk-trcm: tx and rx lrck/bclk common use. ++ - 0: both tx_lrck/bclk and rx_lrck/bclk are used ++ - 1: only tx_lrck/bclk is used ++ - 2: only rx_lrck/bclk is used ++- rockchip,no-dmaengine: This is a boolean property. If present, driver will do not ++ register pcm dmaengine, only just register dai. if the dai is part of multi-dais, ++ the property should be present. Please refer to rockchip,multidais.txt about ++ multi-dais usage. ++ ++Optional properties: ++- rockchip,i2s-rx-route: This is a variable length array, that shows the mapping ++ route of i2s rx sdis to I2S data bus. By default, they are one-to-one mapping: ++ * sdi_0 <-- data_0 ++ * sdi_1 <-- data_1 ++ * sdi_2 <-- data_2 ++ * sdi_3 <-- data_3 ++ If you would like to change the order of I2S RX data, the route mapping may ++ like this: ++ * sdi_3 <-- data_0 ++ * sdi_1 <-- data_1 ++ * sdi_2 <-- data_2 ++ * sdi_0 <-- data_3 ++ You need to add the property for i2s node on dts: ++ - rockchip,i2s-rx-route = <3 1 2 0>; ++ ++- rockchip,i2s-tx-route: This is a variable length array, that shows the mapping ++ route of i2s tx sdos to I2S data bus. By default, they are one-to-one mapping: ++ * sdo_0 --> data_0 ++ * sdo_1 --> data_1 ++ * sdo_2 --> data_2 ++ * sdo_3 --> data_3 ++ If you would like to change the order of I2S TX data, the route mapping may ++ like this: ++ * sdo_2 --> data_0 ++ * sdo_1 --> data_1 ++ * sdo_0 --> data_2 ++ * sdo_3 --> data_3 ++ You need to add the property for i2s node on dts: ++ - rockchip,i2s-tx-route = <2 1 0 3>; + + Example for rk3308 I2S/TDM controller: + +@@ -24,6 +70,10 @@ i2s_8ch_0: i2s@ff300000 { + clock-names = "mclk_tx", "mclk_rx", "hclk"; + dmas = <&dmac1 0>, <&dmac1 1>; + dma-names = "tx", "rx"; ++ resets = <&cru SRST_I2S0_8CH_TX_M>, <&cru SRST_I2S0_8CH_RX_M>; ++ reset-names = "tx-m", "rx-m"; ++ rockchip,cru = <&cru>; ++ rockchip,clk-trcm = <1>; + pinctrl-names = "default"; + pinctrl-0 = <&i2s_8ch_0_sclktx + &i2s_8ch_0_sclkrx +diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c +index 61c984f10d8e..e6125ebfe5a9 100644 +--- a/sound/soc/rockchip/rockchip_i2s.c ++++ b/sound/soc/rockchip/rockchip_i2s.c +@@ -1,4 +1,3 @@ +-// SPDX-License-Identifier: GPL-2.0-only + /* sound/soc/rockchip/rockchip_i2s.c + * + * ALSA SoC Audio Layer - Rockchip I2S Controller driver +@@ -15,11 +14,12 @@ + #include + #include + #include ++#include ++#include + #include + #include + + #include "rockchip_i2s.h" +-#include "rockchip_pcm.h" + + #define DRV_NAME "rockchip-i2s" + +@@ -39,6 +39,8 @@ struct rk_i2s_dev { + + struct regmap *regmap; + struct regmap *grf; ++ struct reset_control *reset_m; ++ struct reset_control *reset_h; + + /* + * Used to indicate the tx/rx status. +@@ -49,8 +51,13 @@ struct rk_i2s_dev { + bool rx_start; + bool is_master_mode; + const struct rk_i2s_pins *pins; ++ unsigned int bclk_fs; ++ unsigned int clk_trcm; + }; + ++/* txctrl/rxctrl lock */ ++static DEFINE_SPINLOCK(lock); ++ + static int i2s_runtime_suspend(struct device *dev) + { + struct rk_i2s_dev *i2s = dev_get_drvdata(dev); +@@ -87,11 +94,27 @@ static inline struct rk_i2s_dev *to_info(struct snd_soc_dai *dai) + return snd_soc_dai_get_drvdata(dai); + } + ++static void rockchip_i2s_reset(struct rk_i2s_dev *i2s) ++{ ++ if (!IS_ERR(i2s->reset_m)) ++ reset_control_assert(i2s->reset_m); ++ if (!IS_ERR(i2s->reset_h)) ++ reset_control_assert(i2s->reset_h); ++ udelay(1); ++ if (!IS_ERR(i2s->reset_m)) ++ reset_control_deassert(i2s->reset_m); ++ if (!IS_ERR(i2s->reset_h)) ++ reset_control_deassert(i2s->reset_h); ++ regcache_mark_dirty(i2s->regmap); ++ regcache_sync(i2s->regmap); ++} ++ + static void rockchip_snd_txctrl(struct rk_i2s_dev *i2s, int on) + { + unsigned int val = 0; + int retry = 10; + ++ spin_lock(&lock); + if (on) { + regmap_update_bits(i2s->regmap, I2S_DMACR, + I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_ENABLE); +@@ -126,12 +149,14 @@ static void rockchip_snd_txctrl(struct rk_i2s_dev *i2s, int on) + regmap_read(i2s->regmap, I2S_CLR, &val); + retry--; + if (!retry) { +- dev_warn(i2s->dev, "fail to clear\n"); ++ dev_warn(i2s->dev, "reset\n"); ++ rockchip_i2s_reset(i2s); + break; + } + } + } + } ++ spin_unlock(&lock); + } + + static void rockchip_snd_rxctrl(struct rk_i2s_dev *i2s, int on) +@@ -139,6 +164,7 @@ static void rockchip_snd_rxctrl(struct rk_i2s_dev *i2s, int on) + unsigned int val = 0; + int retry = 10; + ++ spin_lock(&lock); + if (on) { + regmap_update_bits(i2s->regmap, I2S_DMACR, + I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_ENABLE); +@@ -173,12 +199,14 @@ static void rockchip_snd_rxctrl(struct rk_i2s_dev *i2s, int on) + regmap_read(i2s->regmap, I2S_CLR, &val); + retry--; + if (!retry) { +- dev_warn(i2s->dev, "fail to clear\n"); ++ dev_warn(i2s->dev, "reset\n"); ++ rockchip_i2s_reset(i2s); + break; + } + } + } + } ++ spin_unlock(&lock); + } + + static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai, +@@ -186,7 +214,9 @@ static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai, + { + struct rk_i2s_dev *i2s = to_info(cpu_dai); + unsigned int mask = 0, val = 0; ++ int ret = 0; + ++ pm_runtime_get_sync(cpu_dai->dev); + mask = I2S_CKR_MSS_MASK; + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: +@@ -199,7 +229,8 @@ static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai, + i2s->is_master_mode = false; + break; + default: +- return -EINVAL; ++ ret = -EINVAL; ++ goto err_pm_put; + } + + regmap_update_bits(i2s->regmap, I2S_CKR, mask, val); +@@ -213,7 +244,8 @@ static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai, + val = I2S_CKR_CKP_POS; + break; + default: +- return -EINVAL; ++ ret = -EINVAL; ++ goto err_pm_put; + } + + regmap_update_bits(i2s->regmap, I2S_CKR, mask, val); +@@ -229,14 +261,15 @@ static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai, + case SND_SOC_DAIFMT_I2S: + val = I2S_TXCR_IBM_NORMAL; + break; +- case SND_SOC_DAIFMT_DSP_A: /* PCM no delay mode */ +- val = I2S_TXCR_TFS_PCM; +- break; +- case SND_SOC_DAIFMT_DSP_B: /* PCM delay 1 mode */ ++ case SND_SOC_DAIFMT_DSP_A: /* PCM delay 1 bit mode */ + val = I2S_TXCR_TFS_PCM | I2S_TXCR_PBM_MODE(1); + break; ++ case SND_SOC_DAIFMT_DSP_B: /* PCM no delay mode */ ++ val = I2S_TXCR_TFS_PCM; ++ break; + default: +- return -EINVAL; ++ ret = -EINVAL; ++ goto err_pm_put; + } + + regmap_update_bits(i2s->regmap, I2S_TXCR, mask, val); +@@ -252,19 +285,23 @@ static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai, + case SND_SOC_DAIFMT_I2S: + val = I2S_RXCR_IBM_NORMAL; + break; +- case SND_SOC_DAIFMT_DSP_A: /* PCM no delay mode */ +- val = I2S_RXCR_TFS_PCM; +- break; +- case SND_SOC_DAIFMT_DSP_B: /* PCM delay 1 mode */ ++ case SND_SOC_DAIFMT_DSP_A: /* PCM delay 1 bit mode */ + val = I2S_RXCR_TFS_PCM | I2S_RXCR_PBM_MODE(1); + break; ++ case SND_SOC_DAIFMT_DSP_B: /* PCM no delay mode */ ++ val = I2S_RXCR_TFS_PCM; ++ break; + default: +- return -EINVAL; ++ ret = -EINVAL; ++ goto err_pm_put; + } + + regmap_update_bits(i2s->regmap, I2S_RXCR, mask, val); + +- return 0; ++err_pm_put: ++ pm_runtime_put(cpu_dai->dev); ++ ++ return ret; + } + + static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream, +@@ -272,17 +309,16 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + { + struct rk_i2s_dev *i2s = to_info(dai); +- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + unsigned int val = 0; + unsigned int mclk_rate, bclk_rate, div_bclk, div_lrck; + + if (i2s->is_master_mode) { + mclk_rate = clk_get_rate(i2s->mclk); +- bclk_rate = 2 * 32 * params_rate(params); +- if (bclk_rate == 0 || mclk_rate % bclk_rate) ++ bclk_rate = i2s->bclk_fs * params_rate(params); ++ if (!bclk_rate) + return -EINVAL; + +- div_bclk = mclk_rate / bclk_rate; ++ div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate); + div_lrck = bclk_rate / params_rate(params); + regmap_update_bits(i2s->regmap, I2S_CKR, + I2S_CKR_MDIV_MASK, +@@ -372,13 +408,6 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream, + regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK, + I2S_DMACR_RDL(16)); + +- val = I2S_CKR_TRCM_TXRX; +- if (dai->driver->symmetric_rate && rtd->dai_link->symmetric_rate) +- val = I2S_CKR_TRCM_TXONLY; +- +- regmap_update_bits(i2s->regmap, I2S_CKR, +- I2S_CKR_TRCM_MASK, +- val); + return 0; + } + +@@ -419,9 +448,6 @@ static int rockchip_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, + struct rk_i2s_dev *i2s = to_info(cpu_dai); + int ret; + +- if (freq == 0) +- return 0; +- + ret = clk_set_rate(i2s->mclk, freq); + if (ret) + dev_err(i2s->dev, "Fail to set mclk %d\n", ret); +@@ -471,7 +497,6 @@ static struct snd_soc_dai_driver rockchip_i2s_dai = { + SNDRV_PCM_FMTBIT_S32_LE), + }, + .ops = &rockchip_i2s_dai_ops, +- .symmetric_rate = 1, + }; + + static const struct snd_soc_component_driver rockchip_i2s_component = { +@@ -567,9 +592,16 @@ static const struct rk_i2s_pins rk3399_i2s_pins = { + }; + + static const struct of_device_id rockchip_i2s_match[] __maybe_unused = { ++ { .compatible = "rockchip,px30-i2s", }, ++ { .compatible = "rockchip,rk1808-i2s", }, ++ { .compatible = "rockchip,rk3036-i2s", }, + { .compatible = "rockchip,rk3066-i2s", }, ++ { .compatible = "rockchip,rk3128-i2s", }, + { .compatible = "rockchip,rk3188-i2s", }, + { .compatible = "rockchip,rk3288-i2s", }, ++ { .compatible = "rockchip,rk3308-i2s", }, ++ { .compatible = "rockchip,rk3328-i2s", }, ++ { .compatible = "rockchip,rk3368-i2s", }, + { .compatible = "rockchip,rk3399-i2s", .data = &rk3399_i2s_pins }, + {}, + }; +@@ -586,8 +618,10 @@ static int rockchip_i2s_probe(struct platform_device *pdev) + int val; + + i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); +- if (!i2s) ++ if (!i2s) { ++ dev_err(&pdev->dev, "Can't allocate rk_i2s_dev\n"); + return -ENOMEM; ++ } + + i2s->dev = &pdev->dev; + +@@ -600,6 +634,9 @@ static int rockchip_i2s_probe(struct platform_device *pdev) + i2s->pins = of_id->data; + } + ++ i2s->reset_m = devm_reset_control_get(&pdev->dev, "reset-m"); ++ i2s->reset_h = devm_reset_control_get(&pdev->dev, "reset-h"); ++ + /* try to prepare related clocks */ + i2s->hclk = devm_clk_get(&pdev->dev, "i2s_hclk"); + if (IS_ERR(i2s->hclk)) { +@@ -633,11 +670,11 @@ static int rockchip_i2s_probe(struct platform_device *pdev) + + i2s->playback_dma_data.addr = res->start + I2S_TXDR; + i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; +- i2s->playback_dma_data.maxburst = 4; ++ i2s->playback_dma_data.maxburst = 8; + + i2s->capture_dma_data.addr = res->start + I2S_RXDR; + i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; +- i2s->capture_dma_data.maxburst = 4; ++ i2s->capture_dma_data.maxburst = 8; + + dev_set_drvdata(&pdev->dev, i2s); + +@@ -648,13 +685,12 @@ static int rockchip_i2s_probe(struct platform_device *pdev) + goto err_pm_disable; + } + +- soc_dai = devm_kmemdup(&pdev->dev, &rockchip_i2s_dai, ++ soc_dai = devm_kzalloc(&pdev->dev, + sizeof(*soc_dai), GFP_KERNEL); +- if (!soc_dai) { +- ret = -ENOMEM; +- goto err_pm_disable; +- } ++ if (!soc_dai) ++ return -ENOMEM; + ++ memcpy(soc_dai, &rockchip_i2s_dai, sizeof(*soc_dai)); + if (!of_property_read_u32(node, "rockchip,playback-channels", &val)) { + if (val >= 2 && val <= 8) + soc_dai->playback.channels_max = val; +@@ -665,6 +701,24 @@ static int rockchip_i2s_probe(struct platform_device *pdev) + soc_dai->capture.channels_max = val; + } + ++ i2s->bclk_fs = 64; ++ if (!of_property_read_u32(node, "rockchip,bclk-fs", &val)) { ++ if ((val >= 32) && (val % 2 == 0)) ++ i2s->bclk_fs = val; ++ } ++ ++ i2s->clk_trcm = I2S_CKR_TRCM_TXRX; ++ if (!of_property_read_u32(node, "rockchip,clk-trcm", &val)) { ++ if (val >= 0 && val <= 2) { ++ i2s->clk_trcm = val << I2S_CKR_TRCM_SHIFT; ++ if (i2s->clk_trcm) ++ soc_dai->symmetric_rate = 1; ++ } ++ } ++ ++ regmap_update_bits(i2s->regmap, I2S_CKR, ++ I2S_CKR_TRCM_MASK, i2s->clk_trcm); ++ + ret = devm_snd_soc_register_component(&pdev->dev, + &rockchip_i2s_component, + soc_dai, 1); +@@ -674,10 +728,12 @@ static int rockchip_i2s_probe(struct platform_device *pdev) + goto err_suspend; + } + +- ret = rockchip_pcm_platform_register(&pdev->dev); ++ if (of_property_read_bool(node, "rockchip,no-dmaengine")) ++ return ret; ++ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) { + dev_err(&pdev->dev, "Could not register PCM\n"); +- goto err_suspend; ++ return ret; + } + + return 0; +@@ -699,14 +755,41 @@ static int rockchip_i2s_remove(struct platform_device *pdev) + if (!pm_runtime_status_suspended(&pdev->dev)) + i2s_runtime_suspend(&pdev->dev); + ++ clk_disable_unprepare(i2s->mclk); + clk_disable_unprepare(i2s->hclk); + + return 0; + } + ++#ifdef CONFIG_PM_SLEEP ++static int rockchip_i2s_suspend(struct device *dev) ++{ ++ struct rk_i2s_dev *i2s = dev_get_drvdata(dev); ++ ++ regcache_mark_dirty(i2s->regmap); ++ ++ return 0; ++} ++ ++static int rockchip_i2s_resume(struct device *dev) ++{ ++ struct rk_i2s_dev *i2s = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = pm_runtime_get_sync(dev); ++ if (ret < 0) ++ return ret; ++ ret = regcache_sync(i2s->regmap); ++ pm_runtime_put(dev); ++ ++ return ret; ++} ++#endif ++ + static const struct dev_pm_ops rockchip_i2s_pm_ops = { + SET_RUNTIME_PM_OPS(i2s_runtime_suspend, i2s_runtime_resume, + NULL) ++ SET_SYSTEM_SLEEP_PM_OPS(rockchip_i2s_suspend, rockchip_i2s_resume) + }; + + static struct platform_driver rockchip_i2s_driver = { +diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c +index 39c1b98f9593..499b991c3c1a 100644 +--- a/sound/soc/rockchip/rockchip_i2s_tdm.c ++++ b/sound/soc/rockchip/rockchip_i2s_tdm.c +@@ -15,9 +15,13 @@ + #include + #include + #include ++#include + #include ++#include + #include + #include ++#include ++#include + #include + #include + +@@ -25,19 +29,91 @@ + + #define DRV_NAME "rockchip-i2s-tdm" + ++#define DEFAULT_MCLK_FS 256 ++#define CH_GRP_MAX 4 /* The max channel 8 / 2 */ ++#define MULTIPLEX_CH_MAX 10 ++ ++struct txrx_config { ++ u32 addr; ++ u32 reg; ++ u32 txonly; ++ u32 rxonly; ++}; ++ ++struct rk_i2s_soc_data { ++ u32 softrst_offset; ++ u32 grf_reg_offset; ++ u32 grf_shift; ++ int config_count; ++ const struct txrx_config *configs; ++ int (*init)(struct device *dev, u32 addr); ++}; ++ + struct rk_i2s_tdm_dev { + struct device *dev; + struct clk *hclk; + struct clk *mclk_tx; + struct clk *mclk_rx; ++ /* The mclk_tx_src is parent of mclk_tx */ ++ struct clk *mclk_tx_src; ++ /* The mclk_rx_src is parent of mclk_rx */ ++ struct clk *mclk_rx_src; ++ /* ++ * The mclk_root0 and mclk_root1 are root parent and supplies for ++ * the different FS. ++ * ++ * e.g: ++ * mclk_root0 is VPLL0, used for FS=48000Hz ++ * mclk_root0 is VPLL1, used for FS=44100Hz ++ */ ++ struct clk *mclk_root0; ++ struct clk *mclk_root1; + struct regmap *regmap; ++ struct regmap *grf; + struct snd_dmaengine_dai_dma_data capture_dma_data; + struct snd_dmaengine_dai_dma_data playback_dma_data; +- ++ struct reset_control *tx_reset; ++ struct reset_control *rx_reset; ++ struct rk_i2s_soc_data *soc_data; ++ void __iomem *cru_base; + bool is_master_mode; ++ bool io_multiplex; ++ bool mclk_calibrate; ++ bool tdm_mode; ++ unsigned int mclk_rx_freq; ++ unsigned int mclk_tx_freq; + unsigned int bclk_fs; ++ unsigned int clk_trcm; ++ unsigned int i2s_sdis[CH_GRP_MAX]; ++ unsigned int i2s_sdos[CH_GRP_MAX]; ++ int tx_reset_id; ++ int rx_reset_id; ++ atomic_t refcount; ++ spinlock_t lock; /* xfer lock */ + }; + ++static int to_ch_num(unsigned int val) ++{ ++ int chs; ++ ++ switch (val) { ++ case I2S_CHN_4: ++ chs = 4; ++ break; ++ case I2S_CHN_6: ++ chs = 6; ++ break; ++ case I2S_CHN_8: ++ chs = 8; ++ break; ++ default: ++ chs = 2; ++ break; ++ } ++ ++ return chs; ++} ++ + static int i2s_tdm_runtime_suspend(struct device *dev) + { + struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); +@@ -80,6 +156,205 @@ static inline struct rk_i2s_tdm_dev *to_info(struct snd_soc_dai *dai) + return snd_soc_dai_get_drvdata(dai); + } + ++#if defined(CONFIG_ARM) && !defined(writeq) ++static inline void __raw_writeq(u64 val, volatile void __iomem *addr) ++{ ++ asm volatile("strd %0, %H0, [%1]" : : "r" (val), "r" (addr)); ++} ++#define writeq(v,c) ({ __iowmb(); __raw_writeq((__force u64) cpu_to_le64(v), c); }) ++#endif ++ ++static void rockchip_snd_xfer_reset_assert(struct rk_i2s_tdm_dev *i2s_tdm, ++ int tx_bank, int tx_offset, ++ int rx_bank, int rx_offset) ++{ ++ void __iomem *cru_reset, *addr; ++ unsigned long flags; ++ u64 val; ++ ++ cru_reset = i2s_tdm->cru_base + i2s_tdm->soc_data->softrst_offset; ++ ++ switch (abs(tx_bank - rx_bank)) { ++ case 0: ++ writel(BIT(tx_offset) | BIT(rx_offset) | ++ (BIT(tx_offset) << 16) | (BIT(rx_offset) << 16), ++ cru_reset + (tx_bank * 4)); ++ break; ++ case 1: ++ if (tx_bank < rx_bank) { ++ val = BIT(rx_offset) | (BIT(rx_offset) << 16); ++ val <<= 32; ++ val |= BIT(tx_offset) | (BIT(tx_offset) << 16); ++ addr = cru_reset + (tx_bank * 4); ++ } else { ++ val = BIT(tx_offset) | (BIT(tx_offset) << 16); ++ val <<= 32; ++ val |= BIT(rx_offset) | (BIT(rx_offset) << 16); ++ addr = cru_reset + (rx_bank * 4); ++ } ++ ++ if (IS_ALIGNED((uintptr_t)addr, 8)) { ++ writeq(val, addr); ++ break; ++ } ++ /* fall through */ ++ default: ++ local_irq_save(flags); ++ writel(BIT(tx_offset) | (BIT(tx_offset) << 16), ++ cru_reset + (tx_bank * 4)); ++ writel(BIT(rx_offset) | (BIT(rx_offset) << 16), ++ cru_reset + (rx_bank * 4)); ++ local_irq_restore(flags); ++ break; ++ } ++} ++ ++static void rockchip_snd_xfer_reset_deassert(struct rk_i2s_tdm_dev *i2s_tdm, ++ int tx_bank, int tx_offset, ++ int rx_bank, int rx_offset) ++{ ++ void __iomem *cru_reset, *addr; ++ unsigned long flags; ++ u64 val; ++ ++ cru_reset = i2s_tdm->cru_base + i2s_tdm->soc_data->softrst_offset; ++ ++ switch (abs(tx_bank - rx_bank)) { ++ case 0: ++ writel((BIT(tx_offset) << 16) | (BIT(rx_offset) << 16), ++ cru_reset + (tx_bank * 4)); ++ break; ++ case 1: ++ if (tx_bank < rx_bank) { ++ val = (BIT(rx_offset) << 16); ++ val <<= 32; ++ val |= (BIT(tx_offset) << 16); ++ addr = cru_reset + (tx_bank * 4); ++ } else { ++ val = (BIT(tx_offset) << 16); ++ val <<= 32; ++ val |= (BIT(rx_offset) << 16); ++ addr = cru_reset + (rx_bank * 4); ++ } ++ ++ if (IS_ALIGNED((uintptr_t)addr, 8)) { ++ writeq(val, addr); ++ break; ++ } ++ /* fall through */ ++ default: ++ local_irq_save(flags); ++ writel((BIT(tx_offset) << 16), ++ cru_reset + (tx_bank * 4)); ++ writel((BIT(rx_offset) << 16), ++ cru_reset + (rx_bank * 4)); ++ local_irq_restore(flags); ++ break; ++ } ++} ++ ++/* ++ * to make sure tx/rx reset at the same time when clk_trcm > 0 ++ * if not, will lead lrck is abnormal. ++ */ ++static void rockchip_snd_xfer_sync_reset(struct rk_i2s_tdm_dev *i2s_tdm) ++{ ++ int tx_id, rx_id; ++ int tx_bank, rx_bank, tx_offset, rx_offset; ++ ++ if (!i2s_tdm->cru_base || !i2s_tdm->soc_data) ++ return; ++ ++ tx_id = i2s_tdm->tx_reset_id; ++ rx_id = i2s_tdm->rx_reset_id; ++ if (tx_id < 0 || rx_id < 0) { ++ dev_err(i2s_tdm->dev, "invalid reset id\n"); ++ return; ++ } ++ ++ tx_bank = tx_id / 16; ++ tx_offset = tx_id % 16; ++ rx_bank = rx_id / 16; ++ rx_offset = rx_id % 16; ++ dev_dbg(i2s_tdm->dev, ++ "tx_bank: %d, rx_bank: %d,tx_offset: %d, rx_offset: %d\n", ++ tx_bank, rx_bank, tx_offset, rx_offset); ++ ++ rockchip_snd_xfer_reset_assert(i2s_tdm, tx_bank, tx_offset, ++ rx_bank, rx_offset); ++ ++ udelay(150); ++ ++ rockchip_snd_xfer_reset_deassert(i2s_tdm, tx_bank, tx_offset, ++ rx_bank, rx_offset); ++} ++ ++/* only used when clk_trcm > 0 */ ++static void rockchip_snd_txrxctrl(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai, int on) ++{ ++ struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai); ++ unsigned int val = 0; ++ int retry = 10; ++ ++ spin_lock(&i2s_tdm->lock); ++ if (on) { ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ++ regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, ++ I2S_DMACR_TDE_ENABLE, ++ I2S_DMACR_TDE_ENABLE); ++ else ++ regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, ++ I2S_DMACR_RDE_ENABLE, ++ I2S_DMACR_RDE_ENABLE); ++ ++ if (atomic_inc_return(&i2s_tdm->refcount) == 1) { ++ rockchip_snd_xfer_sync_reset(i2s_tdm); ++ regmap_update_bits(i2s_tdm->regmap, I2S_XFER, ++ I2S_XFER_TXS_START | ++ I2S_XFER_RXS_START, ++ I2S_XFER_TXS_START | ++ I2S_XFER_RXS_START); ++ } ++ } else { ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ++ regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, ++ I2S_DMACR_TDE_ENABLE, ++ I2S_DMACR_TDE_DISABLE); ++ else ++ regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, ++ I2S_DMACR_RDE_ENABLE, ++ I2S_DMACR_RDE_DISABLE); ++ ++ if (atomic_dec_and_test(&i2s_tdm->refcount)) { ++ regmap_update_bits(i2s_tdm->regmap, I2S_XFER, ++ I2S_XFER_TXS_START | ++ I2S_XFER_RXS_START, ++ I2S_XFER_TXS_STOP | ++ I2S_XFER_RXS_STOP); ++ ++ udelay(150); ++ regmap_update_bits(i2s_tdm->regmap, I2S_CLR, ++ I2S_CLR_TXC | I2S_CLR_RXC, ++ I2S_CLR_TXC | I2S_CLR_RXC); ++ ++ regmap_read(i2s_tdm->regmap, I2S_CLR, &val); ++ ++ /* Should wait for clear operation to finish */ ++ while (val) { ++ regmap_read(i2s_tdm->regmap, I2S_CLR, &val); ++ retry--; ++ if (!retry) { ++ dev_info(i2s_tdm->dev, "reset txrx\n"); ++ rockchip_snd_xfer_sync_reset(i2s_tdm); ++ break; ++ } ++ } ++ } ++ } ++ spin_unlock(&i2s_tdm->lock); ++} ++ + static void rockchip_snd_txctrl(struct rk_i2s_tdm_dev *i2s_tdm, int on) + { + unsigned int val = 0; +@@ -112,7 +387,10 @@ static void rockchip_snd_txctrl(struct rk_i2s_tdm_dev *i2s_tdm, int on) + regmap_read(i2s_tdm->regmap, I2S_CLR, &val); + retry--; + if (!retry) { +- dev_warn(i2s_tdm->dev, "fail to clear\n"); ++ dev_warn(i2s_tdm->dev, "reset tx\n"); ++ reset_control_assert(i2s_tdm->tx_reset); ++ udelay(1); ++ reset_control_deassert(i2s_tdm->tx_reset); + break; + } + } +@@ -151,7 +429,10 @@ static void rockchip_snd_rxctrl(struct rk_i2s_tdm_dev *i2s_tdm, int on) + regmap_read(i2s_tdm->regmap, I2S_CLR, &val); + retry--; + if (!retry) { +- dev_warn(i2s_tdm->dev, "fail to clear\n"); ++ dev_warn(i2s_tdm->dev, "reset rx\n"); ++ reset_control_assert(i2s_tdm->rx_reset); ++ udelay(1); ++ reset_control_deassert(i2s_tdm->rx_reset); + break; + } + } +@@ -162,8 +443,9 @@ static int rockchip_i2s_tdm_set_fmt(struct snd_soc_dai *cpu_dai, + unsigned int fmt) + { + struct rk_i2s_tdm_dev *i2s_tdm = to_info(cpu_dai); +- unsigned int mask = 0, val = 0; ++ unsigned int mask = 0, val = 0, tdm_val = 0; + int ret = 0; ++ bool is_tdm = i2s_tdm->tdm_mode; + + pm_runtime_get_sync(cpu_dai->dev); + mask = I2S_CKR_MSS_MASK; +@@ -247,36 +529,283 @@ static int rockchip_i2s_tdm_set_fmt(struct snd_soc_dai *cpu_dai, + + regmap_update_bits(i2s_tdm->regmap, I2S_RXCR, mask, val); + ++ if (is_tdm) { ++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { ++ case SND_SOC_DAIFMT_RIGHT_J: ++ val = I2S_TXCR_TFS_TDM_I2S; ++ tdm_val = TDM_SHIFT_CTRL(2); ++ break; ++ case SND_SOC_DAIFMT_LEFT_J: ++ val = I2S_TXCR_TFS_TDM_I2S; ++ tdm_val = TDM_SHIFT_CTRL(1); ++ break; ++ case SND_SOC_DAIFMT_I2S: ++ val = I2S_TXCR_TFS_TDM_I2S; ++ tdm_val = TDM_SHIFT_CTRL(0); ++ break; ++ case SND_SOC_DAIFMT_DSP_A: ++ val = I2S_TXCR_TFS_TDM_PCM; ++ tdm_val = TDM_SHIFT_CTRL(0); ++ break; ++ case SND_SOC_DAIFMT_DSP_B: ++ val = I2S_TXCR_TFS_TDM_PCM; ++ tdm_val = TDM_SHIFT_CTRL(2); ++ break; ++ default: ++ ret = -EINVAL; ++ goto err_pm_put; ++ } ++ ++ tdm_val |= TDM_FSYNC_WIDTH_SEL1(1); ++ tdm_val |= TDM_FSYNC_WIDTH_ONE_FRAME; ++ ++ mask = I2S_TXCR_TFS_MASK; ++ regmap_update_bits(i2s_tdm->regmap, I2S_TXCR, mask, val); ++ regmap_update_bits(i2s_tdm->regmap, I2S_RXCR, mask, val); ++ ++ mask = TDM_FSYNC_WIDTH_SEL1_MSK | TDM_FSYNC_WIDTH_SEL0_MSK | ++ TDM_SHIFT_CTRL_MSK; ++ regmap_update_bits(i2s_tdm->regmap, I2S_TDM_TXCR, ++ mask, tdm_val); ++ regmap_update_bits(i2s_tdm->regmap, I2S_TDM_RXCR, ++ mask, tdm_val); ++ } ++ + err_pm_put: + pm_runtime_put(cpu_dai->dev); + + return ret; + } + ++static void rockchip_i2s_tdm_xfer_pause(struct snd_pcm_substream *substream, ++ struct rk_i2s_tdm_dev *i2s_tdm) ++{ ++ int stream; ++ unsigned int val = 0; ++ int retry = 10; ++ ++ stream = SNDRV_PCM_STREAM_LAST - substream->stream; ++ if (stream == SNDRV_PCM_STREAM_PLAYBACK) ++ regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, ++ I2S_DMACR_TDE_ENABLE, ++ I2S_DMACR_TDE_DISABLE); ++ else ++ regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, ++ I2S_DMACR_RDE_ENABLE, ++ I2S_DMACR_RDE_DISABLE); ++ ++ regmap_update_bits(i2s_tdm->regmap, I2S_XFER, ++ I2S_XFER_TXS_START | ++ I2S_XFER_RXS_START, ++ I2S_XFER_TXS_STOP | ++ I2S_XFER_RXS_STOP); ++ ++ udelay(150); ++ regmap_update_bits(i2s_tdm->regmap, I2S_CLR, ++ I2S_CLR_TXC | I2S_CLR_RXC, ++ I2S_CLR_TXC | I2S_CLR_RXC); ++ ++ regmap_read(i2s_tdm->regmap, I2S_CLR, &val); ++ ++ /* Should wait for clear operation to finish */ ++ while (val) { ++ regmap_read(i2s_tdm->regmap, I2S_CLR, &val); ++ retry--; ++ if (!retry) { ++ dev_info(i2s_tdm->dev, "reset txrx\n"); ++ rockchip_snd_xfer_sync_reset(i2s_tdm); ++ break; ++ } ++ } ++} ++ ++static void rockchip_i2s_tdm_xfer_resume(struct snd_pcm_substream *substream, ++ struct rk_i2s_tdm_dev *i2s_tdm) ++{ ++ int stream; ++ ++ stream = SNDRV_PCM_STREAM_LAST - substream->stream; ++ if (stream == SNDRV_PCM_STREAM_PLAYBACK) ++ regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, ++ I2S_DMACR_TDE_ENABLE, ++ I2S_DMACR_TDE_ENABLE); ++ else ++ regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, ++ I2S_DMACR_RDE_ENABLE, ++ I2S_DMACR_RDE_ENABLE); ++ ++ regmap_update_bits(i2s_tdm->regmap, I2S_XFER, ++ I2S_XFER_TXS_START | ++ I2S_XFER_RXS_START, ++ I2S_XFER_TXS_START | ++ I2S_XFER_RXS_START); ++} ++ ++static int rockchip_i2s_tdm_calibrate_mclk(struct rk_i2s_tdm_dev *i2s_tdm, ++ struct snd_pcm_substream *substream, ++ unsigned int lrck_freq) ++{ ++ struct clk *mclk_root; ++ struct clk *mclk_parent; ++ /* It's 256 times higher than a high sample rate */ ++ unsigned int mclk_parent_freq; ++ int ret; ++ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ++ mclk_parent = i2s_tdm->mclk_tx_src; ++ else ++ mclk_parent = i2s_tdm->mclk_rx_src; ++ ++ switch (lrck_freq) { ++ case 8000: ++ case 16000: ++ case 24000: ++ case 32000: ++ case 48000: ++ case 64000: ++ case 96000: ++ case 192000: ++ mclk_root = i2s_tdm->mclk_root0; ++ mclk_parent_freq = DEFAULT_MCLK_FS * 192000; ++ break; ++ case 11025: ++ case 22050: ++ case 44100: ++ case 88200: ++ case 176400: ++ mclk_root = i2s_tdm->mclk_root1; ++ mclk_parent_freq = DEFAULT_MCLK_FS * 176400; ++ break; ++ default: ++ dev_err(i2s_tdm->dev, "Invalid LRCK freq: %u Hz\n", ++ lrck_freq); ++ return -EINVAL; ++ } ++ ++ ret = clk_set_parent(mclk_parent, mclk_root); ++ if (ret < 0) { ++ dev_err(i2s_tdm->dev, "parent: %s set root: %s failed: %d\n", ++ __clk_get_name(mclk_parent), ++ __clk_get_name(mclk_root), ++ ret); ++ goto out; ++ } ++ ++ ret = clk_set_rate(mclk_parent, mclk_parent_freq); ++ if (ret < 0) { ++ dev_err(i2s_tdm->dev, "parent: %s set freq: %d failed: %d\n", ++ __clk_get_name(mclk_parent), ++ mclk_parent_freq, ++ ret); ++ goto out; ++ } ++ ++out: ++ return ret; ++} ++ ++static int rockchip_i2s_tdm_set_mclk(struct rk_i2s_tdm_dev *i2s_tdm, ++ struct snd_pcm_substream *substream, ++ struct clk **mclk) ++{ ++ unsigned int mclk_freq; ++ int ret; ++ ++ if (i2s_tdm->clk_trcm) { ++ if (i2s_tdm->mclk_tx_freq != i2s_tdm->mclk_rx_freq) { ++ dev_err(i2s_tdm->dev, ++ "clk_trcm, tx: %d and rx: %d should be same\n", ++ i2s_tdm->mclk_tx_freq, ++ i2s_tdm->mclk_rx_freq); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ ret = clk_set_rate(i2s_tdm->mclk_tx, i2s_tdm->mclk_tx_freq); ++ if (ret < 0) { ++ dev_err(i2s_tdm->dev, ++ "Set mclk_tx: %s freq: %d failed: %d\n", ++ __clk_get_name(i2s_tdm->mclk_tx), ++ i2s_tdm->mclk_tx_freq, ret); ++ goto err; ++ } ++ ++ ret = clk_set_rate(i2s_tdm->mclk_rx, i2s_tdm->mclk_rx_freq); ++ if (ret < 0) { ++ dev_err(i2s_tdm->dev, ++ "Set mclk_rx: %s freq: %d failed: %d\n", ++ __clk_get_name(i2s_tdm->mclk_rx), ++ i2s_tdm->mclk_rx_freq, ret); ++ goto err; ++ } ++ ++ /* Using mclk_rx is ok. */ ++ *mclk = i2s_tdm->mclk_tx; ++ } else { ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ *mclk = i2s_tdm->mclk_tx; ++ mclk_freq = i2s_tdm->mclk_tx_freq; ++ } else { ++ *mclk = i2s_tdm->mclk_rx; ++ mclk_freq = i2s_tdm->mclk_rx_freq; ++ } ++ ++ ret = clk_set_rate(*mclk, mclk_freq); ++ if (ret < 0) { ++ dev_err(i2s_tdm->dev, "Set mclk_%s: %s freq: %d failed: %d\n", ++ substream->stream ? "rx" : "tx", ++ __clk_get_name(*mclk), mclk_freq, ret); ++ goto err; ++ } ++ } ++ ++ return 0; ++ ++err: ++ return ret; ++} ++ + static int rockchip_i2s_tdm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) + { + struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai); +- struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct clk *mclk; ++ int ret = 0; + unsigned int val = 0; + unsigned int mclk_rate, bclk_rate, div_bclk, div_lrck; + +- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) +- mclk = i2s_tdm->mclk_tx; +- else +- mclk = i2s_tdm->mclk_rx; ++ if (i2s_tdm->mclk_calibrate) ++ rockchip_i2s_tdm_calibrate_mclk(i2s_tdm, substream, ++ params_rate(params)); ++ ++ ret = rockchip_i2s_tdm_set_mclk(i2s_tdm, substream, &mclk); ++ if (ret) ++ return ret; ++ ++ if (i2s_tdm->clk_trcm) { ++ spin_lock(&i2s_tdm->lock); ++ if (atomic_read(&i2s_tdm->refcount)) ++ rockchip_i2s_tdm_xfer_pause(substream, i2s_tdm); ++ } + + if (i2s_tdm->is_master_mode) { + mclk_rate = clk_get_rate(mclk); + bclk_rate = i2s_tdm->bclk_fs * params_rate(params); +- if (!bclk_rate) +- return -EINVAL; +- ++ if (!bclk_rate) { ++ ret = -EINVAL; ++ goto err; ++ } + div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate); + div_lrck = bclk_rate / params_rate(params); +- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ if (i2s_tdm->clk_trcm) { ++ regmap_update_bits(i2s_tdm->regmap, I2S_CLKDIV, ++ I2S_CLKDIV_TXM_MASK | I2S_CLKDIV_RXM_MASK, ++ I2S_CLKDIV_TXM(div_bclk) | I2S_CLKDIV_RXM(div_bclk)); ++ regmap_update_bits(i2s_tdm->regmap, I2S_CKR, ++ I2S_CKR_TSD_MASK | I2S_CKR_RSD_MASK, ++ I2S_CKR_TSD(div_lrck) | I2S_CKR_RSD(div_lrck)); ++ } else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(i2s_tdm->regmap, I2S_CLKDIV, + I2S_CLKDIV_TXM_MASK, + I2S_CLKDIV_TXM(div_bclk)); +@@ -310,7 +839,8 @@ static int rockchip_i2s_tdm_hw_params(struct snd_pcm_substream *substream, + val |= I2S_TXCR_VDW(32); + break; + default: +- return -EINVAL; ++ ret = -EINVAL; ++ goto err; + } + + switch (params_channels(params)) { +@@ -329,7 +859,8 @@ static int rockchip_i2s_tdm_hw_params(struct snd_pcm_substream *substream, + default: + dev_err(i2s_tdm->dev, "invalid channel: %d\n", + params_channels(params)); +- return -EINVAL; ++ ret = -EINVAL; ++ goto err; + } + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) +@@ -341,19 +872,103 @@ static int rockchip_i2s_tdm_hw_params(struct snd_pcm_substream *substream, + I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK, + val); + ++ if (i2s_tdm->io_multiplex) { ++ int usable_chs = MULTIPLEX_CH_MAX; ++ ++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { ++ struct snd_pcm_str *playback_str = ++ &substream->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]; ++ ++ if (playback_str->substream_opened) { ++ regmap_read(i2s_tdm->regmap, I2S_TXCR, &val); ++ val &= I2S_TXCR_CSR_MASK; ++ usable_chs = MULTIPLEX_CH_MAX - to_ch_num(val); ++ } ++ ++ regmap_read(i2s_tdm->regmap, I2S_RXCR, &val); ++ val &= I2S_RXCR_CSR_MASK; ++ ++ if (to_ch_num(val) > usable_chs) { ++ dev_err(i2s_tdm->dev, ++ "Capture chs(%d) > usable chs(%d)\n", ++ to_ch_num(val), usable_chs); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ switch (val) { ++ case I2S_CHN_4: ++ val = I2S_IO_6CH_OUT_4CH_IN; ++ break; ++ case I2S_CHN_6: ++ val = I2S_IO_4CH_OUT_6CH_IN; ++ break; ++ case I2S_CHN_8: ++ val = I2S_IO_2CH_OUT_8CH_IN; ++ break; ++ default: ++ val = I2S_IO_8CH_OUT_2CH_IN; ++ break; ++ } ++ } else { ++ struct snd_pcm_str *capture_str = ++ &substream->pcm->streams[SNDRV_PCM_STREAM_CAPTURE]; ++ ++ if (capture_str->substream_opened) { ++ regmap_read(i2s_tdm->regmap, I2S_RXCR, &val); ++ val &= I2S_RXCR_CSR_MASK; ++ usable_chs = MULTIPLEX_CH_MAX - to_ch_num(val); ++ } ++ ++ regmap_read(i2s_tdm->regmap, I2S_TXCR, &val); ++ val &= I2S_TXCR_CSR_MASK; ++ ++ if (to_ch_num(val) > usable_chs) { ++ dev_err(i2s_tdm->dev, ++ "Playback chs(%d) > usable chs(%d)\n", ++ to_ch_num(val), usable_chs); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ switch (val) { ++ case I2S_CHN_4: ++ val = I2S_IO_4CH_OUT_6CH_IN; ++ break; ++ case I2S_CHN_6: ++ val = I2S_IO_6CH_OUT_4CH_IN; ++ break; ++ case I2S_CHN_8: ++ val = I2S_IO_8CH_OUT_2CH_IN; ++ break; ++ default: ++ val = I2S_IO_2CH_OUT_8CH_IN; ++ break; ++ } ++ } ++ ++ val <<= i2s_tdm->soc_data->grf_shift; ++ val |= (I2S_IO_DIRECTION_MASK << i2s_tdm->soc_data->grf_shift) << 16; ++ regmap_write(i2s_tdm->grf, i2s_tdm->soc_data->grf_reg_offset, val); ++ } ++ + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK, + I2S_DMACR_TDL(16)); + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK, + I2S_DMACR_RDL(16)); + +- val = I2S_CKR_TRCM_TXRX; +- if (dai->driver->symmetric_rates && rtd->dai_link->symmetric_rates) +- val = I2S_CKR_TRCM_TXONLY; ++ if (i2s_tdm->clk_trcm) { ++ if (atomic_read(&i2s_tdm->refcount)) ++ rockchip_i2s_tdm_xfer_resume(substream, i2s_tdm); ++ spin_unlock(&i2s_tdm->lock); ++ } + +- regmap_update_bits(i2s_tdm->regmap, I2S_CKR, +- I2S_CKR_TRCM_MASK, +- val); + return 0; ++ ++err: ++ if (i2s_tdm->clk_trcm) ++ spin_unlock(&i2s_tdm->lock); ++ return ret; + } + + static int rockchip_i2s_tdm_trigger(struct snd_pcm_substream *substream, +@@ -366,7 +981,9 @@ static int rockchip_i2s_tdm_trigger(struct snd_pcm_substream *substream, + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: +- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ++ if (i2s_tdm->clk_trcm) ++ rockchip_snd_txrxctrl(substream, dai, 1); ++ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + rockchip_snd_rxctrl(i2s_tdm, 1); + else + rockchip_snd_txctrl(i2s_tdm, 1); +@@ -374,7 +991,9 @@ static int rockchip_i2s_tdm_trigger(struct snd_pcm_substream *substream, + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: +- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ++ if (i2s_tdm->clk_trcm) ++ rockchip_snd_txrxctrl(substream, dai, 0); ++ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + rockchip_snd_rxctrl(i2s_tdm, 0); + else + rockchip_snd_txctrl(i2s_tdm, 0); +@@ -387,19 +1006,26 @@ static int rockchip_i2s_tdm_trigger(struct snd_pcm_substream *substream, + return ret; + } + +-static int rockchip_i2s_tdm_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, ++static int rockchip_i2s_tdm_set_sysclk(struct snd_soc_dai *cpu_dai, int stream, + unsigned int freq, int dir) + { + struct rk_i2s_tdm_dev *i2s_tdm = to_info(cpu_dai); +- int ret; + +- ret = clk_set_rate(i2s_tdm->mclk_tx, freq); +- if (ret) +- dev_err(i2s_tdm->dev, "Fail to set mclk_tx %d\n", ret); ++ /* Put set mclk rate into rockchip_i2s_tdm_set_mclk() */ ++ if (i2s_tdm->clk_trcm) { ++ i2s_tdm->mclk_tx_freq = freq; ++ i2s_tdm->mclk_rx_freq = freq; ++ } else { ++ if (stream == SNDRV_PCM_STREAM_PLAYBACK) ++ i2s_tdm->mclk_tx_freq = freq; ++ else ++ i2s_tdm->mclk_rx_freq = freq; ++ } + +- if (!IS_ERR(i2s_tdm->mclk_rx)) +- ret = clk_set_rate(i2s_tdm->mclk_rx, freq); +- return ret; ++ dev_dbg(i2s_tdm->dev, "The target mclk_%s freq is: %d\n", ++ stream ? "rx" : "tx", freq); ++ ++ return 0; + } + + static int rockchip_i2s_tdm_dai_probe(struct snd_soc_dai *dai) +@@ -412,41 +1038,34 @@ static int rockchip_i2s_tdm_dai_probe(struct snd_soc_dai *dai) + return 0; + } + ++static int rockchip_dai_tdm_slot(struct snd_soc_dai *dai, ++ unsigned int tx_mask, unsigned int rx_mask, ++ int slots, int slot_width) ++{ ++ struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai); ++ unsigned int mask, val; ++ ++ i2s_tdm->tdm_mode = true; ++ i2s_tdm->bclk_fs = slots * slot_width; ++ mask = TDM_SLOT_BIT_WIDTH_MSK | TDM_FRAME_WIDTH_MSK; ++ val = TDM_SLOT_BIT_WIDTH(slot_width) | ++ TDM_FRAME_WIDTH(slots * slot_width); ++ regmap_update_bits(i2s_tdm->regmap, I2S_TDM_TXCR, ++ mask, val); ++ regmap_update_bits(i2s_tdm->regmap, I2S_TDM_RXCR, ++ mask, val); ++ ++ return 0; ++} ++ + static const struct snd_soc_dai_ops rockchip_i2s_tdm_dai_ops = { + .hw_params = rockchip_i2s_tdm_hw_params, + .set_sysclk = rockchip_i2s_tdm_set_sysclk, + .set_fmt = rockchip_i2s_tdm_set_fmt, ++ .set_tdm_slot = rockchip_dai_tdm_slot, + .trigger = rockchip_i2s_tdm_trigger, + }; + +-static struct snd_soc_dai_driver rockchip_i2s_tdm_dai = { +- .probe = rockchip_i2s_tdm_dai_probe, +- .playback = { +- .stream_name = "Playback", +- .channels_min = 2, +- .channels_max = 8, +- .rates = SNDRV_PCM_RATE_8000_192000, +- .formats = (SNDRV_PCM_FMTBIT_S8 | +- SNDRV_PCM_FMTBIT_S16_LE | +- SNDRV_PCM_FMTBIT_S20_3LE | +- SNDRV_PCM_FMTBIT_S24_LE | +- SNDRV_PCM_FMTBIT_S32_LE), +- }, +- .capture = { +- .stream_name = "Capture", +- .channels_min = 2, +- .channels_max = 8, +- .rates = SNDRV_PCM_RATE_8000_192000, +- .formats = (SNDRV_PCM_FMTBIT_S8 | +- SNDRV_PCM_FMTBIT_S16_LE | +- SNDRV_PCM_FMTBIT_S20_3LE | +- SNDRV_PCM_FMTBIT_S24_LE | +- SNDRV_PCM_FMTBIT_S32_LE), +- }, +- .ops = &rockchip_i2s_tdm_dai_ops, +- .symmetric_rates = 1, +-}; +- + static const struct snd_soc_component_driver rockchip_i2s_tdm_component = { + .name = DRV_NAME, + }; +@@ -481,9 +1100,11 @@ static bool rockchip_i2s_tdm_rd_reg(struct device *dev, unsigned int reg) + case I2S_INTCR: + case I2S_XFER: + case I2S_CLR: ++ case I2S_TXDR: + case I2S_RXDR: +- case I2S_FIFOLR: ++ case I2S_TXFIFOLR: + case I2S_INTSR: ++ case I2S_RXFIFOLR: + case I2S_TDM_TXCR: + case I2S_TDM_RXCR: + case I2S_CLKDIV: +@@ -496,8 +1117,12 @@ static bool rockchip_i2s_tdm_rd_reg(struct device *dev, unsigned int reg) + static bool rockchip_i2s_tdm_volatile_reg(struct device *dev, unsigned int reg) + { + switch (reg) { ++ case I2S_TXFIFOLR: + case I2S_INTSR: + case I2S_CLR: ++ case I2S_TXDR: ++ case I2S_RXDR: ++ case I2S_RXFIFOLR: + return true; + default: + return false; +@@ -507,6 +1132,8 @@ static bool rockchip_i2s_tdm_volatile_reg(struct device *dev, unsigned int reg) + static bool rockchip_i2s_tdm_precious_reg(struct device *dev, unsigned int reg) + { + switch (reg) { ++ case I2S_RXDR: ++ return true; + default: + return false; + } +@@ -537,26 +1164,363 @@ static const struct regmap_config rockchip_i2s_tdm_regmap_config = { + .cache_type = REGCACHE_FLAT, + }; + ++static int common_soc_init(struct device *dev, u32 addr) ++{ ++ struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); ++ const struct txrx_config *configs = i2s_tdm->soc_data->configs; ++ u32 reg = 0, val = 0, trcm = i2s_tdm->clk_trcm; ++ int i; ++ ++ switch (trcm) { ++ case I2S_CKR_TRCM_TXONLY: ++ /* fall through */ ++ case I2S_CKR_TRCM_RXONLY: ++ break; ++ default: ++ return 0; ++ } ++ ++ for (i = 0; i < i2s_tdm->soc_data->config_count; i++) { ++ if (addr != configs[i].addr) ++ continue; ++ reg = configs[i].reg; ++ if (trcm == I2S_CKR_TRCM_TXONLY) ++ val = configs[i].txonly; ++ else ++ val = configs[i].rxonly; ++ } ++ ++ if (reg) ++ regmap_write(i2s_tdm->grf, reg, val); ++ ++ return 0; ++} ++ ++static const struct txrx_config px30_txrx_config[] = { ++ { 0xff060000, 0x184, PX30_I2S0_CLK_TXONLY, PX30_I2S0_CLK_RXONLY }, ++}; ++ ++static const struct txrx_config rk1808_txrx_config[] = { ++ { 0xff7e0000, 0x190, RK1808_I2S0_CLK_TXONLY, RK1808_I2S0_CLK_RXONLY }, ++}; ++ ++static const struct txrx_config rk3308_txrx_config[] = { ++ { 0xff300000, 0x308, RK3308_I2S0_CLK_TXONLY, RK3308_I2S0_CLK_RXONLY }, ++ { 0xff310000, 0x308, RK3308_I2S1_CLK_TXONLY, RK3308_I2S1_CLK_RXONLY }, ++}; ++ ++static struct rk_i2s_soc_data px30_i2s_soc_data = { ++ .softrst_offset = 0x0300, ++ .configs = px30_txrx_config, ++ .config_count = ARRAY_SIZE(px30_txrx_config), ++ .init = common_soc_init, ++}; ++ ++static struct rk_i2s_soc_data rk1808_i2s_soc_data = { ++ .softrst_offset = 0x0300, ++ .configs = rk1808_txrx_config, ++ .config_count = ARRAY_SIZE(rk1808_txrx_config), ++ .init = common_soc_init, ++}; ++ ++static struct rk_i2s_soc_data rk3308_i2s_soc_data = { ++ .softrst_offset = 0x0400, ++ .grf_reg_offset = 0x0308, ++ .grf_shift = 5, ++ .configs = rk3308_txrx_config, ++ .config_count = ARRAY_SIZE(rk3308_txrx_config), ++ .init = common_soc_init, ++}; ++ + static const struct of_device_id rockchip_i2s_tdm_match[] = { +- { .compatible = "rockchip,rk3308-i2s-tdm", }, ++ { .compatible = "rockchip,px30-i2s-tdm", .data = &px30_i2s_soc_data }, ++ { .compatible = "rockchip,rk1808-i2s-tdm", .data = &rk1808_i2s_soc_data }, ++ { .compatible = "rockchip,rk3308-i2s-tdm", .data = &rk3308_i2s_soc_data }, + {}, + }; + ++static int of_i2s_resetid_get(struct device_node *node, ++ const char *id) ++{ ++ struct of_phandle_args args; ++ int index = 0; ++ int ret; ++ ++ if (id) ++ index = of_property_match_string(node, ++ "reset-names", id); ++ ret = of_parse_phandle_with_args(node, "resets", "#reset-cells", ++ index, &args); ++ if (ret) ++ return ret; ++ ++ return args.args[0]; ++} ++ ++static int rockchip_i2s_tdm_dai_prepare(struct platform_device *pdev, ++ struct snd_soc_dai_driver **soc_dai) ++{ ++ struct snd_soc_dai_driver rockchip_i2s_tdm_dai = { ++ .probe = rockchip_i2s_tdm_dai_probe, ++ .playback = { ++ .stream_name = "Playback", ++ .channels_min = 2, ++ .channels_max = 8, ++ .rates = SNDRV_PCM_RATE_8000_192000, ++ .formats = (SNDRV_PCM_FMTBIT_S8 | ++ SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_S20_3LE | ++ SNDRV_PCM_FMTBIT_S24_LE | ++ SNDRV_PCM_FMTBIT_S32_LE), ++ }, ++ .capture = { ++ .stream_name = "Capture", ++ .channels_min = 2, ++ .channels_max = 8, ++ .rates = SNDRV_PCM_RATE_8000_192000, ++ .formats = (SNDRV_PCM_FMTBIT_S8 | ++ SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_S20_3LE | ++ SNDRV_PCM_FMTBIT_S24_LE | ++ SNDRV_PCM_FMTBIT_S32_LE), ++ }, ++ .ops = &rockchip_i2s_tdm_dai_ops, ++ }; ++ ++ *soc_dai = devm_kmemdup(&pdev->dev, &rockchip_i2s_tdm_dai, ++ sizeof(rockchip_i2s_tdm_dai), GFP_KERNEL); ++ if (!(*soc_dai)) { ++ dev_err(&pdev->dev, "Failed to duplicate i2s_tdm_dai\n"); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static int rockchip_i2s_tdm_path_check(struct rk_i2s_tdm_dev *i2s_tdm, ++ int num, ++ bool is_rx_path) ++{ ++ unsigned int *i2s_data; ++ int i, j, ret = 0; ++ ++ if (is_rx_path) ++ i2s_data = i2s_tdm->i2s_sdis; ++ else ++ i2s_data = i2s_tdm->i2s_sdos; ++ ++ for (i = 0; i < num; i++) { ++ if (i2s_data[i] > CH_GRP_MAX - 1) { ++ dev_err(i2s_tdm->dev, ++ "%s path i2s_data[%d]: %d is overflow, max is: %d\n", ++ is_rx_path ? "RX" : "TX", ++ i, i2s_data[i], CH_GRP_MAX); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ for (j = 0; j < num; j++) { ++ if (i == j) ++ continue; ++ ++ if (i2s_data[i] == i2s_data[j]) { ++ dev_err(i2s_tdm->dev, ++ "%s path invalid routed i2s_data: [%d]%d == [%d]%d\n", ++ is_rx_path ? "RX" : "TX", ++ i, i2s_data[i], ++ j, i2s_data[j]); ++ ret = -EINVAL; ++ goto err; ++ } ++ } ++ } ++ ++err: ++ return ret; ++} ++ ++static void rockchip_i2s_tdm_tx_path_config(struct rk_i2s_tdm_dev *i2s_tdm, ++ int num) ++{ ++ int idx; ++ ++ for (idx = 0; idx < num; idx++) { ++ regmap_update_bits(i2s_tdm->regmap, I2S_TXCR, ++ I2S_TXCR_PATH_MASK(idx), ++ I2S_TXCR_PATH(idx, i2s_tdm->i2s_sdos[idx])); ++ } ++} ++ ++static void rockchip_i2s_tdm_rx_path_config(struct rk_i2s_tdm_dev *i2s_tdm, ++ int num) ++{ ++ int idx; ++ ++ for (idx = 0; idx < num; idx++) { ++ regmap_update_bits(i2s_tdm->regmap, I2S_RXCR, ++ I2S_RXCR_PATH_MASK(idx), ++ I2S_RXCR_PATH(idx, i2s_tdm->i2s_sdis[idx])); ++ } ++} ++ ++static void rockchip_i2s_tdm_path_config(struct rk_i2s_tdm_dev *i2s_tdm, ++ int num, bool is_rx_path) ++{ ++ if (is_rx_path) ++ rockchip_i2s_tdm_rx_path_config(i2s_tdm, num); ++ else ++ rockchip_i2s_tdm_tx_path_config(i2s_tdm, num); ++} ++ ++static int rockchip_i2s_tdm_path_prepare(struct rk_i2s_tdm_dev *i2s_tdm, ++ struct device_node *np, ++ bool is_rx_path) ++{ ++ char *i2s_tx_path_prop = "rockchip,i2s-tx-route"; ++ char *i2s_rx_path_prop = "rockchip,i2s-rx-route"; ++ char *i2s_path_prop; ++ unsigned int *i2s_data; ++ int num, ret = 0; ++ ++ if (is_rx_path) { ++ i2s_path_prop = i2s_rx_path_prop; ++ i2s_data = i2s_tdm->i2s_sdis; ++ } else { ++ i2s_path_prop = i2s_tx_path_prop; ++ i2s_data = i2s_tdm->i2s_sdos; ++ } ++ ++ num = of_count_phandle_with_args(np, i2s_path_prop, NULL); ++ if (num < 0) { ++ if (num != -ENOENT) { ++ dev_err(i2s_tdm->dev, ++ "Failed to read '%s' num: %d\n", ++ i2s_path_prop, num); ++ ret = num; ++ } ++ goto out; ++ } else if (num != CH_GRP_MAX) { ++ dev_err(i2s_tdm->dev, ++ "The num: %d should be: %d\n", num, CH_GRP_MAX); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ ret = of_property_read_u32_array(np, i2s_path_prop, ++ i2s_data, num); ++ if (ret < 0) { ++ dev_err(i2s_tdm->dev, ++ "Failed to read '%s': %d\n", ++ i2s_path_prop, ret); ++ goto out; ++ } ++ ++ ret = rockchip_i2s_tdm_path_check(i2s_tdm, num, is_rx_path); ++ if (ret < 0) { ++ dev_err(i2s_tdm->dev, ++ "Failed to check i2s data bus: %d\n", ret); ++ goto out; ++ } ++ ++ rockchip_i2s_tdm_path_config(i2s_tdm, num, is_rx_path); ++ ++out: ++ return ret; ++} ++ ++static int rockchip_i2s_tdm_tx_path_prepare(struct rk_i2s_tdm_dev *i2s_tdm, ++ struct device_node *np) ++{ ++ return rockchip_i2s_tdm_path_prepare(i2s_tdm, np, 0); ++} ++ ++static int rockchip_i2s_tdm_rx_path_prepare(struct rk_i2s_tdm_dev *i2s_tdm, ++ struct device_node *np) ++{ ++ return rockchip_i2s_tdm_path_prepare(i2s_tdm, np, 1); ++} ++ + static int rockchip_i2s_tdm_probe(struct platform_device *pdev) + { + struct device_node *node = pdev->dev.of_node; ++ struct device_node *cru_node; ++ const struct of_device_id *of_id; + struct rk_i2s_tdm_dev *i2s_tdm; ++ struct snd_soc_dai_driver *soc_dai; + struct resource *res; + void __iomem *regs; + int ret; + int val; + ++ ret = rockchip_i2s_tdm_dai_prepare(pdev, &soc_dai); ++ if (ret < 0) ++ return ret; ++ + i2s_tdm = devm_kzalloc(&pdev->dev, sizeof(*i2s_tdm), GFP_KERNEL); + if (!i2s_tdm) + return -ENOMEM; + + i2s_tdm->dev = &pdev->dev; + ++ of_id = of_match_device(rockchip_i2s_tdm_match, &pdev->dev); ++ if (!of_id || !of_id->data) ++ return -EINVAL; ++ ++ spin_lock_init(&i2s_tdm->lock); ++ i2s_tdm->soc_data = (struct rk_i2s_soc_data *)of_id->data; ++ ++ i2s_tdm->bclk_fs = 64; ++ if (!of_property_read_u32(node, "rockchip,bclk-fs", &val)) { ++ if ((val >= 32) && (val % 2 == 0)) ++ i2s_tdm->bclk_fs = val; ++ } ++ ++ i2s_tdm->clk_trcm = I2S_CKR_TRCM_TXRX; ++ if (!of_property_read_u32(node, "rockchip,clk-trcm", &val)) { ++ if (val >= 0 && val <= 2) { ++ i2s_tdm->clk_trcm = val << I2S_CKR_TRCM_SHIFT; ++ if (i2s_tdm->clk_trcm) ++ soc_dai->symmetric_rate = 1; ++ } ++ } ++ ++ if (of_property_read_bool(node, "rockchip,playback-only")) ++ soc_dai->capture.channels_min = 0; ++ else if (of_property_read_bool(node, "rockchip,capture-only")) ++ soc_dai->playback.channels_min = 0; ++ ++ i2s_tdm->grf = syscon_regmap_lookup_by_phandle(node, "rockchip,grf"); ++ if (IS_ERR(i2s_tdm->grf)) ++ return PTR_ERR(i2s_tdm->grf); ++ ++ if (i2s_tdm->clk_trcm) { ++ cru_node = of_parse_phandle(node, "rockchip,cru", 0); ++ i2s_tdm->cru_base = of_iomap(cru_node, 0); ++ if (!i2s_tdm->cru_base) ++ return -ENOENT; ++ ++ i2s_tdm->tx_reset_id = of_i2s_resetid_get(node, "tx-m"); ++ if (i2s_tdm->tx_reset_id < 0) ++ return -EINVAL; ++ i2s_tdm->rx_reset_id = of_i2s_resetid_get(node, "rx-m"); ++ if (i2s_tdm->rx_reset_id < 0) ++ return -EINVAL; ++ } ++ ++ i2s_tdm->tx_reset = devm_reset_control_get(&pdev->dev, "tx-m"); ++ if (IS_ERR(i2s_tdm->tx_reset)) { ++ ret = PTR_ERR(i2s_tdm->tx_reset); ++ if (ret != -ENOENT) ++ return ret; ++ } ++ ++ i2s_tdm->rx_reset = devm_reset_control_get(&pdev->dev, "rx-m"); ++ if (IS_ERR(i2s_tdm->rx_reset)) { ++ ret = PTR_ERR(i2s_tdm->rx_reset); ++ if (ret != -ENOENT) ++ return ret; ++ } ++ + i2s_tdm->hclk = devm_clk_get(&pdev->dev, "hclk"); + if (IS_ERR(i2s_tdm->hclk)) + return PTR_ERR(i2s_tdm->hclk); +@@ -573,6 +1537,29 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) + if (IS_ERR(i2s_tdm->mclk_rx)) + return PTR_ERR(i2s_tdm->mclk_rx); + ++ i2s_tdm->io_multiplex = ++ of_property_read_bool(node, "rockchip,io-multiplex"); ++ ++ i2s_tdm->mclk_calibrate = ++ of_property_read_bool(node, "rockchip,mclk-calibrate"); ++ if (i2s_tdm->mclk_calibrate) { ++ i2s_tdm->mclk_tx_src = devm_clk_get(&pdev->dev, "mclk_tx_src"); ++ if (IS_ERR(i2s_tdm->mclk_tx_src)) ++ return PTR_ERR(i2s_tdm->mclk_tx_src); ++ ++ i2s_tdm->mclk_rx_src = devm_clk_get(&pdev->dev, "mclk_rx_src"); ++ if (IS_ERR(i2s_tdm->mclk_rx_src)) ++ return PTR_ERR(i2s_tdm->mclk_rx_src); ++ ++ i2s_tdm->mclk_root0 = devm_clk_get(&pdev->dev, "mclk_root0"); ++ if (IS_ERR(i2s_tdm->mclk_root0)) ++ return PTR_ERR(i2s_tdm->mclk_root0); ++ ++ i2s_tdm->mclk_root1 = devm_clk_get(&pdev->dev, "mclk_root1"); ++ if (IS_ERR(i2s_tdm->mclk_root1)) ++ return PTR_ERR(i2s_tdm->mclk_root1); ++ } ++ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) +@@ -591,6 +1578,19 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) + i2s_tdm->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + i2s_tdm->capture_dma_data.maxburst = 8; + ++ ret = rockchip_i2s_tdm_tx_path_prepare(i2s_tdm, node); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "I2S TX path prepare failed: %d\n", ret); ++ return ret; ++ } ++ ++ ret = rockchip_i2s_tdm_rx_path_prepare(i2s_tdm, node); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "I2S RX path prepare failed: %d\n", ret); ++ return ret; ++ } ++ ++ atomic_set(&i2s_tdm->refcount, 0); + dev_set_drvdata(&pdev->dev, i2s_tdm); + + pm_runtime_enable(&pdev->dev); +@@ -600,21 +1600,23 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) + goto err_pm_disable; + } + +- i2s_tdm->bclk_fs = 64; +- if (!of_property_read_u32(node, "rockchip,bclk-fs", &val)) { +- if ((val >= 32) && (val % 2 == 0)) +- i2s_tdm->bclk_fs = val; +- } ++ regmap_update_bits(i2s_tdm->regmap, I2S_CKR, ++ I2S_CKR_TRCM_MASK, i2s_tdm->clk_trcm); ++ ++ if (i2s_tdm->soc_data && i2s_tdm->soc_data->init) ++ i2s_tdm->soc_data->init(&pdev->dev, res->start); + + ret = devm_snd_soc_register_component(&pdev->dev, + &rockchip_i2s_tdm_component, +- &rockchip_i2s_tdm_dai, 1); ++ soc_dai, 1); + + if (ret) { + dev_err(&pdev->dev, "Could not register DAI\n"); + goto err_suspend; + } + ++ if (of_property_read_bool(node, "rockchip,no-dmaengine")) ++ return ret; + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) { + dev_err(&pdev->dev, "Could not register PCM\n"); +diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.h b/sound/soc/rockchip/rockchip_i2s_tdm.h +index 1a28523cfd82..3a69fa276f8f 100644 +--- a/sound/soc/rockchip/rockchip_i2s_tdm.h ++++ b/sound/soc/rockchip/rockchip_i2s_tdm.h +@@ -18,6 +18,9 @@ + * TXCR + * transmit operation control register + */ ++#define I2S_TXCR_PATH_SHIFT(x) (23 + (x) * 2) ++#define I2S_TXCR_PATH_MASK(x) (0x3 << I2S_TXCR_PATH_SHIFT(x)) ++#define I2S_TXCR_PATH(x, v) ((v) << I2S_TXCR_PATH_SHIFT(x)) + #define I2S_TXCR_RCNT_SHIFT 17 + #define I2S_TXCR_RCNT_MASK (0x3f << I2S_TXCR_RCNT_SHIFT) + #define I2S_TXCR_CSR_SHIFT 15 +@@ -41,7 +44,9 @@ + #define I2S_TXCR_TFS_SHIFT 5 + #define I2S_TXCR_TFS_I2S (0 << I2S_TXCR_TFS_SHIFT) + #define I2S_TXCR_TFS_PCM (1 << I2S_TXCR_TFS_SHIFT) +-#define I2S_TXCR_TFS_MASK (1 << I2S_TXCR_TFS_SHIFT) ++#define I2S_TXCR_TFS_TDM_PCM (2 << I2S_TXCR_TFS_SHIFT) ++#define I2S_TXCR_TFS_TDM_I2S (3 << I2S_TXCR_TFS_SHIFT) ++#define I2S_TXCR_TFS_MASK (3 << I2S_TXCR_TFS_SHIFT) + #define I2S_TXCR_VDW_SHIFT 0 + #define I2S_TXCR_VDW(x) ((x - 1) << I2S_TXCR_VDW_SHIFT) + #define I2S_TXCR_VDW_MASK (0x1f << I2S_TXCR_VDW_SHIFT) +@@ -50,6 +55,9 @@ + * RXCR + * receive operation control register + */ ++#define I2S_RXCR_PATH_SHIFT(x) (17 + (x) * 2) ++#define I2S_RXCR_PATH_MASK(x) (0x3 << I2S_RXCR_PATH_SHIFT(x)) ++#define I2S_RXCR_PATH(x, v) ((v) << I2S_RXCR_PATH_SHIFT(x)) + #define I2S_RXCR_CSR_SHIFT 15 + #define I2S_RXCR_CSR(x) (x << I2S_RXCR_CSR_SHIFT) + #define I2S_RXCR_CSR_MASK (3 << I2S_RXCR_CSR_SHIFT) +@@ -71,7 +79,9 @@ + #define I2S_RXCR_TFS_SHIFT 5 + #define I2S_RXCR_TFS_I2S (0 << I2S_RXCR_TFS_SHIFT) + #define I2S_RXCR_TFS_PCM (1 << I2S_RXCR_TFS_SHIFT) +-#define I2S_RXCR_TFS_MASK (1 << I2S_RXCR_TFS_SHIFT) ++#define I2S_RXCR_TFS_TDM_PCM (2 << I2S_RXCR_TFS_SHIFT) ++#define I2S_RXCR_TFS_TDM_I2S (3 << I2S_RXCR_TFS_SHIFT) ++#define I2S_RXCR_TFS_MASK (3 << I2S_RXCR_TFS_SHIFT) + #define I2S_RXCR_VDW_SHIFT 0 + #define I2S_RXCR_VDW(x) ((x - 1) << I2S_RXCR_VDW_SHIFT) + #define I2S_RXCR_VDW_MASK (0x1f << I2S_RXCR_VDW_SHIFT) +@@ -213,6 +223,22 @@ + */ + #define I2S_RXDR_MASK (0xff) + ++/* ++ * TDM_CTRL ++ * TDM ctrl register ++ */ ++#define TDM_FSYNC_WIDTH_SEL1_MSK GENMASK(20, 18) ++#define TDM_FSYNC_WIDTH_SEL1(x) ((x - 1) << 18) ++#define TDM_FSYNC_WIDTH_SEL0_MSK BIT(17) ++#define TDM_FSYNC_WIDTH_HALF_FRAME 0 ++#define TDM_FSYNC_WIDTH_ONE_FRAME BIT(17) ++#define TDM_SHIFT_CTRL_MSK GENMASK(16, 14) ++#define TDM_SHIFT_CTRL(x) ((x) << 14) ++#define TDM_SLOT_BIT_WIDTH_MSK GENMASK(13, 9) ++#define TDM_SLOT_BIT_WIDTH(x) ((x - 1) << 9) ++#define TDM_FRAME_WIDTH_MSK GENMASK(8, 0) ++#define TDM_FRAME_WIDTH(x) ((x - 1) << 0) ++ + /* + * CLKDIV + * Mclk div register +@@ -237,11 +263,18 @@ enum { + #define I2S_CHN_6 (2 << I2S_CSR_SHIFT) + #define I2S_CHN_8 (3 << I2S_CSR_SHIFT) + ++/* io direction cfg register */ ++#define I2S_IO_DIRECTION_MASK (7) ++#define I2S_IO_8CH_OUT_2CH_IN (7) ++#define I2S_IO_6CH_OUT_4CH_IN (3) ++#define I2S_IO_4CH_OUT_6CH_IN (1) ++#define I2S_IO_2CH_OUT_8CH_IN (0) ++ + /* I2S REGS */ + #define I2S_TXCR (0x0000) + #define I2S_RXCR (0x0004) + #define I2S_CKR (0x0008) +-#define I2S_FIFOLR (0x000c) ++#define I2S_TXFIFOLR (0x000c) + #define I2S_DMACR (0x0010) + #define I2S_INTCR (0x0014) + #define I2S_INTSR (0x0018) +@@ -249,8 +282,107 @@ enum { + #define I2S_CLR (0x0020) + #define I2S_TXDR (0x0024) + #define I2S_RXDR (0x0028) ++#define I2S_RXFIFOLR (0x002c) + #define I2S_TDM_TXCR (0x0030) + #define I2S_TDM_RXCR (0x0034) + #define I2S_CLKDIV (0x0038) + ++/* PX30 GRF CONFIGS*/ ++#define PX30_I2S0_CLK_IN_SRC_MASK GENMASK(13, 12) ++#define PX30_I2S0_CLK_IN_SRC_FROM_TX (0x1 << 12) ++#define PX30_I2S0_CLK_IN_SRC_FROM_RX (0x2 << 12) ++#define PX30_I2S0_MCLK_OUT_SRC_MSK BIT(5) ++#define PX30_I2S0_MCLK_OUT_SRC_FROM_TX BIT(5) ++#define PX30_I2S0_MCLK_OUT_SRC_FROM_RX 0 ++ ++#define PX30_I2S0_CLK_MSK \ ++ (PX30_I2S0_MCLK_OUT_SRC_MSK | \ ++ PX30_I2S0_CLK_IN_SRC_MASK) ++ ++#define PX30_I2S0_CLK_TXONLY \ ++ (PX30_I2S0_MCLK_OUT_SRC_FROM_TX | \ ++ PX30_I2S0_CLK_IN_SRC_FROM_TX | \ ++ (PX30_I2S0_CLK_MSK << 16)) ++ ++#define PX30_I2S0_CLK_RXONLY \ ++ (PX30_I2S0_MCLK_OUT_SRC_FROM_RX | \ ++ PX30_I2S0_CLK_IN_SRC_FROM_RX | \ ++ (PX30_I2S0_CLK_MSK << 16)) ++ ++/* RK1808 GRF CONFIGS*/ ++#define RK1808_I2S0_MCLK_OUT_SRC_MSK BIT(2) ++#define RK1808_I2S0_MCLK_OUT_SRC_FROM_RX BIT(2) ++#define RK1808_I2S0_MCLK_OUT_SRC_FROM_TX 0 ++#define RK1808_I2S0_CLK_IN_SRC_MASK GENMASK(1, 0) ++#define RK1808_I2S0_CLK_IN_SRC_FROM_TX (0x1 << 0) ++#define RK1808_I2S0_CLK_IN_SRC_FROM_RX (0x2 << 0) ++ ++#define RK1808_I2S0_CLK_MSK \ ++ (RK1808_I2S0_MCLK_OUT_SRC_MSK | \ ++ RK1808_I2S0_CLK_IN_SRC_MASK) ++ ++#define RK1808_I2S0_CLK_TXONLY \ ++ (RK1808_I2S0_MCLK_OUT_SRC_FROM_TX | \ ++ RK1808_I2S0_CLK_IN_SRC_FROM_TX | \ ++ (RK1808_I2S0_CLK_MSK << 16)) ++ ++#define RK1808_I2S0_CLK_RXONLY \ ++ (RK1808_I2S0_MCLK_OUT_SRC_FROM_RX | \ ++ RK1808_I2S0_CLK_IN_SRC_FROM_RX | \ ++ (RK1808_I2S0_CLK_MSK << 16)) ++ ++/* RK3308 GRF CONFIGS*/ ++#define RK3308_I2S0_8CH_MCLK_OUT_SRC_MSK BIT(10) ++#define RK3308_I2S0_8CH_MCLK_OUT_SRC_FROM_RX BIT(10) ++#define RK3308_I2S0_8CH_MCLK_OUT_SRC_FROM_TX 0 ++#define RK3308_I2S0_8CH_CLK_IN_RX_SRC_MSK BIT(9) ++#define RK3308_I2S0_8CH_CLK_IN_RX_SRC_FROM_TX BIT(9) ++#define RK3308_I2S0_8CH_CLK_IN_RX_SRC_FROM_RX 0 ++#define RK3308_I2S0_8CH_CLK_IN_TX_SRC_MSK BIT(8) ++#define RK3308_I2S0_8CH_CLK_IN_TX_SRC_FROM_RX BIT(8) ++#define RK3308_I2S0_8CH_CLK_IN_TX_SRC_FROM_TX 0 ++#define RK3308_I2S1_8CH_MCLK_OUT_SRC_MSK BIT(2) ++#define RK3308_I2S1_8CH_MCLK_OUT_SRC_FROM_RX BIT(2) ++#define RK3308_I2S1_8CH_MCLK_OUT_SRC_FROM_TX 0 ++#define RK3308_I2S1_8CH_CLK_IN_RX_SRC_MSK BIT(1) ++#define RK3308_I2S1_8CH_CLK_IN_RX_SRC_FROM_TX BIT(1) ++#define RK3308_I2S1_8CH_CLK_IN_RX_SRC_FROM_RX 0 ++#define RK3308_I2S1_8CH_CLK_IN_TX_SRC_MSK BIT(0) ++#define RK3308_I2S1_8CH_CLK_IN_TX_SRC_FROM_RX BIT(0) ++#define RK3308_I2S1_8CH_CLK_IN_TX_SRC_FROM_TX 0 ++ ++#define RK3308_I2S0_CLK_MSK \ ++ (RK3308_I2S0_8CH_MCLK_OUT_SRC_MSK | \ ++ RK3308_I2S0_8CH_CLK_IN_RX_SRC_MSK | \ ++ RK3308_I2S0_8CH_CLK_IN_TX_SRC_MSK) ++ ++#define RK3308_I2S0_CLK_TXONLY \ ++ (RK3308_I2S0_8CH_MCLK_OUT_SRC_FROM_TX | \ ++ RK3308_I2S0_8CH_CLK_IN_RX_SRC_FROM_TX | \ ++ RK3308_I2S0_8CH_CLK_IN_TX_SRC_FROM_TX | \ ++ (RK3308_I2S0_CLK_MSK << 16)) ++ ++#define RK3308_I2S0_CLK_RXONLY \ ++ (RK3308_I2S0_8CH_MCLK_OUT_SRC_FROM_RX | \ ++ RK3308_I2S0_8CH_CLK_IN_RX_SRC_FROM_RX | \ ++ RK3308_I2S0_8CH_CLK_IN_TX_SRC_FROM_RX | \ ++ (RK3308_I2S0_CLK_MSK << 16)) ++ ++#define RK3308_I2S1_CLK_MSK \ ++ (RK3308_I2S1_8CH_MCLK_OUT_SRC_MSK | \ ++ RK3308_I2S1_8CH_CLK_IN_RX_SRC_MSK | \ ++ RK3308_I2S1_8CH_CLK_IN_TX_SRC_MSK) ++ ++#define RK3308_I2S1_CLK_TXONLY \ ++ (RK3308_I2S1_8CH_MCLK_OUT_SRC_FROM_TX | \ ++ RK3308_I2S1_8CH_CLK_IN_RX_SRC_FROM_TX | \ ++ RK3308_I2S1_8CH_CLK_IN_TX_SRC_FROM_TX | \ ++ (RK3308_I2S1_CLK_MSK << 16)) ++ ++#define RK3308_I2S1_CLK_RXONLY \ ++ (RK3308_I2S1_8CH_MCLK_OUT_SRC_FROM_RX | \ ++ RK3308_I2S1_8CH_CLK_IN_RX_SRC_FROM_RX | \ ++ RK3308_I2S1_8CH_CLK_IN_TX_SRC_FROM_RX | \ ++ (RK3308_I2S1_CLK_MSK << 16)) ++ + #endif /* _ROCKCHIP_I2S_TDM_H */ +-- +2.25.1 + diff --git a/sys-kernel/decade-sources/files/board-rockpis-0018-ASoC-codecs-Add-RK3308-internal-codec-driver.patch b/sys-kernel/decade-sources/files/patches-5.13/board-rockpis-0018-ASoC-codecs-Add-RK3308-internal-codec-driver.patch similarity index 100% rename from sys-kernel/decade-sources/files/board-rockpis-0018-ASoC-codecs-Add-RK3308-internal-codec-driver.patch rename to sys-kernel/decade-sources/files/patches-5.13/board-rockpis-0018-ASoC-codecs-Add-RK3308-internal-codec-driver.patch diff --git a/sys-kernel/decade-sources/files/board-rockpis-0019-Sync-rk3308_codec-to-BSP-tree.patch b/sys-kernel/decade-sources/files/patches-5.13/board-rockpis-0019-Sync-rk3308_codec-to-BSP-tree.patch similarity index 100% rename from sys-kernel/decade-sources/files/board-rockpis-0019-Sync-rk3308_codec-to-BSP-tree.patch rename to sys-kernel/decade-sources/files/patches-5.13/board-rockpis-0019-Sync-rk3308_codec-to-BSP-tree.patch diff --git a/sys-kernel/decade-sources/files/board-rockpis-0022-ASoC-rk3308_codec-replace-codec-to-component.patch b/sys-kernel/decade-sources/files/patches-5.13/board-rockpis-0022-ASoC-rk3308_codec-replace-codec-to-component.patch similarity index 100% rename from sys-kernel/decade-sources/files/board-rockpis-0022-ASoC-rk3308_codec-replace-codec-to-component.patch rename to sys-kernel/decade-sources/files/patches-5.13/board-rockpis-0022-ASoC-rk3308_codec-replace-codec-to-component.patch diff --git a/sys-kernel/decade-sources/files/general-emmc-hs400es-init-tweak.patch b/sys-kernel/decade-sources/files/patches-5.13/general-emmc-hs400es-init-tweak.patch similarity index 100% rename from sys-kernel/decade-sources/files/general-emmc-hs400es-init-tweak.patch rename to sys-kernel/decade-sources/files/patches-5.13/general-emmc-hs400es-init-tweak.patch diff --git a/sys-kernel/decade-sources/files/general-fix-es8316-kernel-panic.patch b/sys-kernel/decade-sources/files/patches-5.13/general-fix-es8316-kernel-panic.patch similarity index 100% rename from sys-kernel/decade-sources/files/general-fix-es8316-kernel-panic.patch rename to sys-kernel/decade-sources/files/patches-5.13/general-fix-es8316-kernel-panic.patch diff --git a/sys-kernel/decade-sources/files/general-increasing_DMA_block_memory_allocation_to_2048.patch b/sys-kernel/decade-sources/files/patches-5.13/general-increasing_DMA_block_memory_allocation_to_2048.patch similarity index 100% rename from sys-kernel/decade-sources/files/general-increasing_DMA_block_memory_allocation_to_2048.patch rename to sys-kernel/decade-sources/files/patches-5.13/general-increasing_DMA_block_memory_allocation_to_2048.patch diff --git a/sys-kernel/decade-sources/files/general-rt5651-add-mclk.patch b/sys-kernel/decade-sources/files/patches-5.13/general-rt5651-add-mclk.patch similarity index 100% rename from sys-kernel/decade-sources/files/general-rt5651-add-mclk.patch rename to sys-kernel/decade-sources/files/patches-5.13/general-rt5651-add-mclk.patch diff --git a/sys-kernel/decade-sources/files/rk3399-add-sclk-i2sout-src-clock.patch b/sys-kernel/decade-sources/files/patches-5.13/rk3399-add-sclk-i2sout-src-clock.patch similarity index 100% rename from sys-kernel/decade-sources/files/rk3399-add-sclk-i2sout-src-clock.patch rename to sys-kernel/decade-sources/files/patches-5.13/rk3399-add-sclk-i2sout-src-clock.patch diff --git a/sys-kernel/decade-sources/files/rk3399-enable-dwc3-xhci-usb-trb-quirk.patch b/sys-kernel/decade-sources/files/patches-5.13/rk3399-enable-dwc3-xhci-usb-trb-quirk.patch similarity index 100% rename from sys-kernel/decade-sources/files/rk3399-enable-dwc3-xhci-usb-trb-quirk.patch rename to sys-kernel/decade-sources/files/patches-5.13/rk3399-enable-dwc3-xhci-usb-trb-quirk.patch diff --git a/sys-kernel/decade-sources/files/rk3399-pci-rockchip-support-ep-gpio-undefined-case.patch b/sys-kernel/decade-sources/files/patches-5.13/rk3399-pci-rockchip-support-ep-gpio-undefined-case.patch similarity index 100% rename from sys-kernel/decade-sources/files/rk3399-pci-rockchip-support-ep-gpio-undefined-case.patch rename to sys-kernel/decade-sources/files/patches-5.13/rk3399-pci-rockchip-support-ep-gpio-undefined-case.patch diff --git a/sys-kernel/decade-sources/files/rk3399-sd-drive-level-8ma.patch b/sys-kernel/decade-sources/files/patches-5.13/rk3399-sd-drive-level-8ma.patch similarity index 100% rename from sys-kernel/decade-sources/files/rk3399-sd-drive-level-8ma.patch rename to sys-kernel/decade-sources/files/patches-5.13/rk3399-sd-drive-level-8ma.patch diff --git a/sys-kernel/decade-sources/files/add-board-nanopi-m4v2.patch b/sys-kernel/decade-sources/files/patches-5.14/add-board-nanopi-m4v2.patch similarity index 100% rename from sys-kernel/decade-sources/files/add-board-nanopi-m4v2.patch rename to sys-kernel/decade-sources/files/patches-5.14/add-board-nanopi-m4v2.patch diff --git a/sys-kernel/decade-sources/files/patches-5.14/add-csgpio-to-rockchip-spi.patch b/sys-kernel/decade-sources/files/patches-5.14/add-csgpio-to-rockchip-spi.patch new file mode 100644 index 0000000..b65a5b1 --- /dev/null +++ b/sys-kernel/decade-sources/files/patches-5.14/add-csgpio-to-rockchip-spi.patch @@ -0,0 +1,83 @@ +diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c +index 2cc6d99..19e05ad 100644 +--- a/drivers/spi/spi-rockchip.c ++++ b/drivers/spi/spi-rockchip.c +@@ -6,6 +6,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -185,6 +186,10 @@ struct rockchip_spi { + bool cs_asserted[ROCKCHIP_SPI_MAX_CS_NUM]; + }; + ++struct rockchip_spi_data { ++ bool cs_gpio_requested; ++}; ++ + static inline void spi_enable_chip(struct rockchip_spi *rs, bool enable) + { + writel_relaxed((enable ? 1U : 0U), rs->regs + ROCKCHIP_SPI_SSIENR); +@@ -455,6 +460,50 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs, + return 1; + } + ++static int rockchip_spi_setup(struct spi_device *spi) ++{ ++ int ret = 0; ++ unsigned long flags = (spi->mode & SPI_CS_HIGH) ? ++ GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH; ++ struct rockchip_spi_data *data = spi_get_ctldata(spi); ++ ++ if (!gpio_is_valid(spi->cs_gpio)) ++ return 0; ++ ++ if (!data) { ++ data = kzalloc(sizeof(*data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ spi_set_ctldata(spi, data); ++ } ++ ++ if (!data->cs_gpio_requested) { ++ ret = gpio_request_one(spi->cs_gpio, flags, ++ dev_name(&spi->dev)); ++ if (!ret) ++ data->cs_gpio_requested = 1; ++ } else ++ ret = gpio_direction_output(spi->cs_gpio, flags); ++ ++ if (ret < 0) ++ dev_err(&spi->dev, "Failed to setup cs gpio(%d): %d\n", ++ spi->cs_gpio, ret); ++ ++ return ret; ++} ++ ++static void rockchip_spi_cleanup(struct spi_device *spi) ++{ ++ struct rockchip_spi_data *data = spi_get_ctldata(spi); ++ ++ if (data) { ++ if (data->cs_gpio_requested) ++ gpio_free(spi->cs_gpio); ++ kfree(data); ++ spi_set_ctldata(spi, NULL); ++ } ++} ++ + static int rockchip_spi_config(struct rockchip_spi *rs, + struct spi_device *spi, struct spi_transfer *xfer, + bool use_dma) +@@ -683,6 +732,8 @@ static int rockchip_spi_probe(struct platform_device *pdev) + ctlr->max_speed_hz = min(rs->freq / BAUDR_SCKDV_MIN, MAX_SCLK_OUT); + + ctlr->set_cs = rockchip_spi_set_cs; ++ ctlr->setup = rockchip_spi_setup; ++ ctlr->cleanup = rockchip_spi_cleanup; + ctlr->transfer_one = rockchip_spi_transfer_one; + ctlr->max_transfer_size = rockchip_spi_max_transfer_size; + ctlr->handle_err = rockchip_spi_handle_err; diff --git a/sys-kernel/decade-sources/files/patches-5.14/add-fusb30x-driver.patch b/sys-kernel/decade-sources/files/patches-5.14/add-fusb30x-driver.patch new file mode 100644 index 0000000..67233f0 --- /dev/null +++ b/sys-kernel/decade-sources/files/patches-5.14/add-fusb30x-driver.patch @@ -0,0 +1,4047 @@ +diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig +index 1abf76be2..7ad8b090c 100644 +--- a/drivers/staging/Kconfig ++++ b/drivers/staging/Kconfig +@@ -92,6 +92,8 @@ source "drivers/staging/fbtft/Kconfig" + + source "drivers/staging/fsl-dpaa2/Kconfig" + ++source "drivers/staging/fusb30x/Kconfig" ++ + source "drivers/staging/most/Kconfig" + + source "drivers/staging/ks7010/Kconfig" +diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile +index ab0cbe881..2e308d901 100644 +--- a/drivers/staging/Makefile ++++ b/drivers/staging/Makefile +@@ -36,6 +36,7 @@ obj-$(CONFIG_UNISYSSPAR) += unisys/ + obj-$(CONFIG_UNISYSSPAR) += unisys/ + obj-$(CONFIG_COMMON_CLK_XLNX_CLKWZRD) += clocking-wizard/ + obj-$(CONFIG_FB_TFT) += fbtft/ ++obj-$(CONFIG_FUSB_30X) += fusb30x/ + obj-$(CONFIG_MOST) += most/ + obj-$(CONFIG_KS7010) += ks7010/ + obj-$(CONFIG_GREYBUS) += greybus/ +diff --git a/drivers/staging/fusb30x/Kconfig b/drivers/staging/fusb30x/Kconfig +new file mode 100644 +index 000000000..5bb75270f +--- /dev/null ++++ b/drivers/staging/fusb30x/Kconfig +@@ -0,0 +1,10 @@ ++config FUSB_30X ++ tristate "Fairchild FUSB30X Type-C chip driver" ++ depends on I2C ++ help ++ This is a driver for the Fairchild FUSB302 Type-C chip. It supports ++ USB Type-C PD functionality controlled using I2C. ++ ++ This driver supports extcon reporting not yet implemented in the ++ mainline FUSB302 driver. ++ +diff --git a/drivers/staging/fusb30x/Makefile b/drivers/staging/fusb30x/Makefile +new file mode 100644 +index 000000000..1c8e35df3 +--- /dev/null ++++ b/drivers/staging/fusb30x/Makefile +@@ -0,0 +1,2 @@ ++# SPDX-License-Identifier: GPL-2.0 ++obj-$(CONFIG_FUSB_30X) += fusb30x.o +diff --git a/drivers/staging/fusb30x/fusb30x.c b/drivers/staging/fusb30x/fusb30x.c +new file mode 100644 +index 000000000..56d22648c +--- /dev/null ++++ b/drivers/staging/fusb30x/fusb30x.c +@@ -0,0 +1,3434 @@ ++/* ++ * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd ++ * Author: Zain Wang ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * Some ideas are from chrome ec and fairchild GPL fusb302 driver. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "fusb30x.h" ++ ++#define FUSB302_MAX_REG (FUSB_REG_FIFO + 50) ++#define FUSB_MS_TO_NS(x) ((s64)x * 1000 * 1000) ++ ++#define TYPEC_CC_VOLT_OPEN 0 ++#define TYPEC_CC_VOLT_RA 1 ++#define TYPEC_CC_VOLT_RD 2 ++#define TYPEC_CC_VOLT_RP 3 ++ ++#define EVENT_CC BIT(0) ++#define EVENT_RX BIT(1) ++#define EVENT_TX BIT(2) ++#define EVENT_REC_RESET BIT(3) ++#define EVENT_WORK_CONTINUE BIT(5) ++#define EVENT_TIMER_MUX BIT(6) ++#define EVENT_TIMER_STATE BIT(7) ++#define EVENT_DELAY_CC BIT(8) ++#define FLAG_EVENT (EVENT_RX | EVENT_TIMER_MUX | \ ++ EVENT_TIMER_STATE) ++ ++#define PACKET_IS_CONTROL_MSG(header, type) \ ++ (PD_HEADER_CNT(header) == 0 && \ ++ PD_HEADER_TYPE(header) == type) ++ ++#define PACKET_IS_DATA_MSG(header, type) \ ++ (PD_HEADER_CNT(header) != 0 && \ ++ PD_HEADER_TYPE(header) == type) ++ ++/* ++ * DisplayPort modes capabilities ++ * ------------------------------- ++ * <31:24> : Reserved (always 0). ++ * <23:16> : UFP_D pin assignment supported ++ * <15:8> : DFP_D pin assignment supported ++ * <7> : USB 2.0 signaling (0b=yes, 1b=no) ++ * <6> : Plug | Receptacle (0b == plug, 1b == receptacle) ++ * <5:2> : xxx1: Supports DPv1.3, xx1x Supports USB Gen 2 signaling ++ * Other bits are reserved. ++ * <1:0> : signal direction ( 00b=rsv, 01b=sink, 10b=src 11b=both ) ++ */ ++#define PD_DP_PIN_CAPS(x) ((((x) >> 6) & 0x1) ? (((x) >> 16) & 0x3f) \ ++ : (((x) >> 8) & 0x3f)) ++#define PD_DP_SIGNAL_GEN2(x) (((x) >> 3) & 0x1) ++ ++#define MODE_DP_PIN_A BIT(0) ++#define MODE_DP_PIN_B BIT(1) ++#define MODE_DP_PIN_C BIT(2) ++#define MODE_DP_PIN_D BIT(3) ++#define MODE_DP_PIN_E BIT(4) ++#define MODE_DP_PIN_F BIT(5) ++ ++/* Pin configs B/D/F support multi-function */ ++#define MODE_DP_PIN_MF_MASK (MODE_DP_PIN_B | MODE_DP_PIN_D | MODE_DP_PIN_F) ++/* Pin configs A/B support BR2 signaling levels */ ++#define MODE_DP_PIN_BR2_MASK (MODE_DP_PIN_A | MODE_DP_PIN_B) ++/* Pin configs C/D/E/F support DP signaling levels */ ++#define MODE_DP_PIN_DP_MASK (MODE_DP_PIN_C | MODE_DP_PIN_D | \ ++ MODE_DP_PIN_E | MODE_DP_PIN_F) ++ ++/* ++ * DisplayPort Status VDO ++ * ---------------------- ++ * <31:9> : Reserved (always 0). ++ * <8> : IRQ_HPD : 1 == irq arrived since last message otherwise 0. ++ * <7> : HPD state : 0 = HPD_LOW, 1 == HPD_HIGH ++ * <6> : Exit DP Alt mode: 0 == maintain, 1 == exit ++ * <5> : USB config : 0 == maintain current, 1 == switch to USB from DP ++ * <4> : Multi-function preference : 0 == no pref, 1 == MF preferred. ++ * <3> : enabled : is DPout on/off. ++ * <2> : power low : 0 == normal or LPM disabled, 1 == DP disabled for LPM ++ * <1:0> : connect status : 00b == no (DFP|UFP)_D is connected or disabled. ++ * 01b == DFP_D connected, 10b == UFP_D connected, 11b == both. ++ */ ++#define PD_VDO_DPSTS_HPD_IRQ(x) (((x) >> 8) & 0x1) ++#define PD_VDO_DPSTS_HPD_LVL(x) (((x) >> 7) & 0x1) ++#define PD_VDO_DPSTS_MF_PREF(x) (((x) >> 4) & 0x1) ++ ++static u8 fusb30x_port_used; ++static struct fusb30x_chip *fusb30x_port_info[256]; ++ ++static bool is_write_reg(struct device *dev, unsigned int reg) ++{ ++ if (reg >= FUSB_REG_FIFO) ++ return true; ++ else ++ return ((reg < (FUSB_REG_CONTROL4 + 1)) && (reg > 0x01)) ? ++ true : false; ++} ++ ++static bool is_volatile_reg(struct device *dev, unsigned int reg) ++{ ++ if (reg > FUSB_REG_CONTROL4) ++ return true; ++ ++ switch (reg) { ++ case FUSB_REG_CONTROL0: ++ case FUSB_REG_CONTROL1: ++ case FUSB_REG_CONTROL3: ++ case FUSB_REG_RESET: ++ return true; ++ } ++ return false; ++} ++ ++struct regmap_config fusb302_regmap_config = { ++ .reg_bits = 8, ++ .val_bits = 8, ++ .writeable_reg = is_write_reg, ++ .volatile_reg = is_volatile_reg, ++ .max_register = FUSB302_MAX_REG, ++ .cache_type = REGCACHE_RBTREE, ++}; ++ ++static void dump_notify_info(struct fusb30x_chip *chip) ++{ ++ dev_dbg(chip->dev, "port %d\n", chip->port_num); ++ dev_dbg(chip->dev, "orientation %d\n", chip->notify.orientation); ++ dev_dbg(chip->dev, "power_role %d\n", chip->notify.power_role); ++ dev_dbg(chip->dev, "data_role %d\n", chip->notify.data_role); ++ dev_dbg(chip->dev, "cc %d\n", chip->notify.is_cc_connected); ++ dev_dbg(chip->dev, "pd %d\n", chip->notify.is_pd_connected); ++ dev_dbg(chip->dev, "enter_mode %d\n", chip->notify.is_enter_mode); ++ dev_dbg(chip->dev, "pin support %d\n", ++ chip->notify.pin_assignment_support); ++ dev_dbg(chip->dev, "pin def %d\n", chip->notify.pin_assignment_def); ++ dev_dbg(chip->dev, "attention %d\n", chip->notify.attention); ++} ++ ++static const unsigned int fusb302_cable[] = { ++ EXTCON_USB, ++ EXTCON_USB_HOST, ++ EXTCON_CHG_USB_SDP, ++ EXTCON_CHG_USB_CDP, ++ EXTCON_CHG_USB_DCP, ++ EXTCON_CHG_USB_SLOW, ++ EXTCON_CHG_USB_FAST, ++ EXTCON_DISP_DP, ++ EXTCON_NONE, ++}; ++ ++static void fusb_set_pos_power(struct fusb30x_chip *chip, int max_vol, ++ int max_cur) ++{ ++ int i; ++ int pos_find; ++ int tmp; ++ ++ pos_find = 0; ++ for (i = PD_HEADER_CNT(chip->rec_head) - 1; i >= 0; i--) { ++ switch (CAP_POWER_TYPE(chip->rec_load[i])) { ++ case 0: ++ /* Fixed Supply */ ++ if ((CAP_FPDO_VOLTAGE(chip->rec_load[i]) * 50) <= ++ max_vol && ++ (CAP_FPDO_CURRENT(chip->rec_load[i]) * 10) <= ++ max_cur) { ++ chip->pos_power = i + 1; ++ tmp = CAP_FPDO_VOLTAGE(chip->rec_load[i]); ++ chip->pd_output_vol = tmp * 50; ++ tmp = CAP_FPDO_CURRENT(chip->rec_load[i]); ++ chip->pd_output_cur = tmp * 10; ++ pos_find = 1; ++ } ++ break; ++ case 1: ++ /* Battery */ ++ if ((CAP_VPDO_VOLTAGE(chip->rec_load[i]) * 50) <= ++ max_vol && ++ (CAP_VPDO_CURRENT(chip->rec_load[i]) * 10) <= ++ max_cur) { ++ chip->pos_power = i + 1; ++ tmp = CAP_VPDO_VOLTAGE(chip->rec_load[i]); ++ chip->pd_output_vol = tmp * 50; ++ tmp = CAP_VPDO_CURRENT(chip->rec_load[i]); ++ chip->pd_output_cur = tmp * 10; ++ pos_find = 1; ++ } ++ break; ++ default: ++ /* not meet battery caps */ ++ break; ++ } ++ if (pos_find) ++ break; ++ } ++} ++ ++static int fusb302_set_pos_power_by_charge_ic(struct fusb30x_chip *chip) ++{ ++ struct power_supply *psy = NULL; ++ union power_supply_propval val; ++ enum power_supply_property psp; ++ int max_vol, max_cur; ++ ++ max_vol = 0; ++ max_cur = 0; ++ psy = power_supply_get_by_phandle(chip->dev->of_node, "charge-dev"); ++ if (!psy || IS_ERR(psy)) ++ return -1; ++ ++ psp = POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX; ++ if (power_supply_get_property(psy, psp, &val) == 0) ++ max_vol = val.intval / 1000; ++ ++ psp = POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT; ++ if (power_supply_get_property(psy, psp, &val) == 0) ++ max_cur = val.intval / 1000; ++ ++ if (max_vol > 0 && max_cur > 0) ++ fusb_set_pos_power(chip, max_vol, max_cur); ++ ++ return 0; ++} ++ ++void fusb_irq_disable(struct fusb30x_chip *chip) ++{ ++ unsigned long irqflags = 0; ++ ++ spin_lock_irqsave(&chip->irq_lock, irqflags); ++ if (chip->enable_irq) { ++ disable_irq_nosync(chip->gpio_int_irq); ++ chip->enable_irq = 0; ++ } else { ++ dev_warn(chip->dev, "irq have already disabled\n"); ++ } ++ spin_unlock_irqrestore(&chip->irq_lock, irqflags); ++} ++ ++void fusb_irq_enable(struct fusb30x_chip *chip) ++{ ++ unsigned long irqflags = 0; ++ ++ spin_lock_irqsave(&chip->irq_lock, irqflags); ++ if (!chip->enable_irq) { ++ enable_irq(chip->gpio_int_irq); ++ chip->enable_irq = 1; ++ } ++ spin_unlock_irqrestore(&chip->irq_lock, irqflags); ++} ++ ++static void platform_fusb_notify(struct fusb30x_chip *chip) ++{ ++ bool plugged = false, flip = false, dfp = false, ufp = false, ++ dp = false, usb_ss = false, hpd = false; ++ union extcon_property_value property; ++ ++ if (chip->notify.is_cc_connected) ++ chip->notify.orientation = ++ (chip->cc_polarity == TYPEC_POLARITY_CC1) ? ++ CC1 : CC2; ++ ++ /* avoid notify repeated */ ++ if (memcmp(&chip->notify, &chip->notify_cmp, ++ sizeof(struct notify_info))) { ++ dump_notify_info(chip); ++ chip->notify.attention = false; ++ memcpy(&chip->notify_cmp, &chip->notify, ++ sizeof(struct notify_info)); ++ ++ plugged = chip->notify.is_cc_connected || ++ chip->notify.is_pd_connected; ++ if (chip->notify.orientation != NONE) ++ flip = (chip->notify.orientation == CC1) ? false : true; ++ dp = chip->notify.is_enter_mode; ++ ++ if (dp) { ++ dfp = true; ++ usb_ss = (chip->notify.pin_assignment_def & ++ MODE_DP_PIN_MF_MASK) ? true : false; ++ hpd = GET_DP_STATUS_HPD(chip->notify.dp_status); ++ } else if (chip->notify.data_role) { ++ dfp = true; ++ usb_ss = true; ++ } else if (plugged) { ++ ufp = true; ++ usb_ss = true; ++ } ++ ++ property.intval = flip; ++ extcon_set_property(chip->extcon, EXTCON_USB, ++ EXTCON_PROP_USB_TYPEC_POLARITY, property); ++ extcon_set_property(chip->extcon, EXTCON_USB_HOST, ++ EXTCON_PROP_USB_TYPEC_POLARITY, property); ++ extcon_set_property(chip->extcon, EXTCON_DISP_DP, ++ EXTCON_PROP_USB_TYPEC_POLARITY, property); ++ ++ property.intval = usb_ss; ++ extcon_set_property(chip->extcon, EXTCON_USB, ++ EXTCON_PROP_USB_SS, property); ++ extcon_set_property(chip->extcon, EXTCON_USB_HOST, ++ EXTCON_PROP_USB_SS, property); ++ extcon_set_property(chip->extcon, EXTCON_DISP_DP, ++ EXTCON_PROP_USB_SS, property); ++ extcon_set_state(chip->extcon, EXTCON_USB, ufp); ++ extcon_set_state(chip->extcon, EXTCON_USB_HOST, dfp); ++ extcon_set_state(chip->extcon, EXTCON_DISP_DP, dp && hpd); ++ extcon_sync(chip->extcon, EXTCON_USB); ++ extcon_sync(chip->extcon, EXTCON_USB_HOST); ++ extcon_sync(chip->extcon, EXTCON_DISP_DP); ++ if (chip->notify.power_role == POWER_ROLE_SINK && ++ chip->notify.is_pd_connected && ++ chip->pd_output_vol > 0 && chip->pd_output_cur > 0) { ++ extcon_set_state(chip->extcon, EXTCON_CHG_USB_FAST, true); ++ property.intval = ++ (chip->pd_output_cur << 15 | ++ chip->pd_output_vol); ++ extcon_set_property(chip->extcon, EXTCON_CHG_USB_FAST, ++ EXTCON_PROP_USB_TYPEC_POLARITY, ++ property); ++ extcon_sync(chip->extcon, EXTCON_CHG_USB_FAST); ++ } ++ } ++} ++ ++static bool platform_get_device_irq_state(struct fusb30x_chip *chip) ++{ ++ return !gpiod_get_value(chip->gpio_int); ++} ++ ++static void fusb_timer_start(struct hrtimer *timer, int ms) ++{ ++ ktime_t ktime; ++ ++ ktime = ktime_set(0, FUSB_MS_TO_NS(ms)); ++ hrtimer_start(timer, ktime, HRTIMER_MODE_REL); ++} ++ ++static void platform_set_vbus_lvl_enable(struct fusb30x_chip *chip, int vbus_5v, ++ int vbus_other) ++{ ++ bool gpio_vbus_value = false; ++ ++ gpio_vbus_value = gpiod_get_value(chip->gpio_vbus_5v); ++ if (chip->gpio_vbus_5v) { ++ gpiod_set_raw_value(chip->gpio_vbus_5v, vbus_5v); ++ } ++ ++ if (chip->gpio_vbus_other) ++ gpiod_set_raw_value(chip->gpio_vbus_5v, vbus_other); ++ ++ if (chip->gpio_discharge && !vbus_5v && gpio_vbus_value) { ++ gpiod_set_value(chip->gpio_discharge, 1); ++ msleep(20); ++ gpiod_set_value(chip->gpio_discharge, 0); ++ } ++} ++ ++static void set_state(struct fusb30x_chip *chip, enum connection_state state) ++{ ++ dev_dbg(chip->dev, "port %d, state %d\n", chip->port_num, state); ++ if (!state) ++ dev_info(chip->dev, "PD disabled\n"); ++ chip->conn_state = state; ++ chip->sub_state = 0; ++ chip->val_tmp = 0; ++ chip->work_continue |= EVENT_WORK_CONTINUE; ++} ++ ++static int tcpm_get_message(struct fusb30x_chip *chip) ++{ ++ u8 buf[32]; ++ int len; ++ ++ do { ++ regmap_raw_read(chip->regmap, FUSB_REG_FIFO, buf, 3); ++ chip->rec_head = (buf[1] & 0xff) | ((buf[2] << 8) & 0xff00); ++ ++ len = PD_HEADER_CNT(chip->rec_head) << 2; ++ regmap_raw_read(chip->regmap, FUSB_REG_FIFO, buf, len + 4); ++ /* ignore good_crc message */ ++ } while (PACKET_IS_CONTROL_MSG(chip->rec_head, CMT_GOODCRC)); ++ ++ memcpy(chip->rec_load, buf, len); ++ ++ return 0; ++} ++ ++static void fusb302_flush_rx_fifo(struct fusb30x_chip *chip) ++{ ++ regmap_write(chip->regmap, FUSB_REG_CONTROL1, CONTROL1_RX_FLUSH); ++} ++ ++static int tcpm_get_cc(struct fusb30x_chip *chip, int *CC1, int *CC2) ++{ ++ u32 val; ++ int *CC_MEASURE; ++ u32 store; ++ ++ *CC1 = TYPEC_CC_VOLT_OPEN; ++ *CC2 = TYPEC_CC_VOLT_OPEN; ++ ++ if (chip->cc_state & CC_STATE_TOGSS_CC1) ++ CC_MEASURE = CC1; ++ else ++ CC_MEASURE = CC2; ++ ++ if (chip->cc_state & CC_STATE_TOGSS_IS_UFP) { ++ regmap_read(chip->regmap, FUSB_REG_SWITCHES0, &store); ++ /* measure cc1 first */ ++ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0, ++ SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2 | ++ SWITCHES0_PU_EN1 | SWITCHES0_PU_EN2 | ++ SWITCHES0_PDWN1 | SWITCHES0_PDWN2, ++ SWITCHES0_PDWN1 | SWITCHES0_PDWN2 | ++ SWITCHES0_MEAS_CC1); ++ usleep_range(250, 300); ++ ++ regmap_read(chip->regmap, FUSB_REG_STATUS0, &val); ++ val &= STATUS0_BC_LVL; ++ *CC1 = val ? TYPEC_CC_VOLT_RP : TYPEC_CC_VOLT_OPEN; ++ ++ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0, ++ SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2 | ++ SWITCHES0_PU_EN1 | SWITCHES0_PU_EN2 | ++ SWITCHES0_PDWN1 | SWITCHES0_PDWN2, ++ SWITCHES0_PDWN1 | SWITCHES0_PDWN2 | ++ SWITCHES0_MEAS_CC2); ++ usleep_range(250, 300); ++ ++ regmap_read(chip->regmap, FUSB_REG_STATUS0, &val); ++ val &= STATUS0_BC_LVL; ++ *CC2 = val ? TYPEC_CC_VOLT_RP : TYPEC_CC_VOLT_OPEN; ++ ++ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0, ++ SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2, ++ store); ++ } else { ++ regmap_read(chip->regmap, FUSB_REG_SWITCHES0, &store); ++ val = store; ++ val &= ~(SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2 | ++ SWITCHES0_PU_EN1 | SWITCHES0_PU_EN2); ++ if (chip->cc_state & CC_STATE_TOGSS_CC1) { ++ val |= SWITCHES0_MEAS_CC1 | SWITCHES0_PU_EN1; ++ } else { ++ val |= SWITCHES0_MEAS_CC2 | SWITCHES0_PU_EN2; ++ } ++ regmap_write(chip->regmap, FUSB_REG_SWITCHES0, val); ++ ++ regmap_write(chip->regmap, FUSB_REG_MEASURE, chip->cc_meas_high); ++ usleep_range(250, 300); ++ ++ regmap_read(chip->regmap, FUSB_REG_STATUS0, &val); ++ if (val & STATUS0_COMP) { ++ int retry = 3; ++ int comp_times = 0; ++ ++ while (retry--) { ++ regmap_write(chip->regmap, FUSB_REG_MEASURE, chip->cc_meas_high); ++ usleep_range(250, 300); ++ regmap_read(chip->regmap, FUSB_REG_STATUS0, &val); ++ if (val & STATUS0_COMP) { ++ comp_times++; ++ if (comp_times == 3) { ++ *CC_MEASURE = TYPEC_CC_VOLT_OPEN; ++ regmap_write(chip->regmap, FUSB_REG_SWITCHES0, store); ++ } ++ } ++ } ++ } else { ++ regmap_write(chip->regmap, FUSB_REG_MEASURE, chip->cc_meas_low); ++ regmap_read(chip->regmap, FUSB_REG_MEASURE, &val); ++ usleep_range(250, 300); ++ ++ regmap_read(chip->regmap, FUSB_REG_STATUS0, &val); ++ ++ if (val & STATUS0_COMP) ++ *CC_MEASURE = TYPEC_CC_VOLT_RD; ++ else ++ *CC_MEASURE = TYPEC_CC_VOLT_RA; ++ } ++ regmap_write(chip->regmap, FUSB_REG_SWITCHES0, store); ++ regmap_write(chip->regmap, FUSB_REG_MEASURE, ++ chip->cc_meas_high); ++ } ++ ++ return 0; ++} ++ ++static void tcpm_set_cc_pull_mode(struct fusb30x_chip *chip, enum CC_MODE mode) ++{ ++ u8 val; ++ ++ switch (mode) { ++ case CC_PULL_UP: ++ if (chip->cc_polarity == TYPEC_POLARITY_CC1) ++ val = SWITCHES0_PU_EN1; ++ else ++ val = SWITCHES0_PU_EN2; ++ break; ++ case CC_PULL_DOWN: ++ val = SWITCHES0_PDWN1 | SWITCHES0_PDWN2; ++ break; ++ default: ++ val = 0; ++ break; ++ } ++ ++ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0, ++ SWITCHES0_PU_EN1 | SWITCHES0_PU_EN2 | ++ SWITCHES0_PDWN1 | SWITCHES0_PDWN2, ++ val); ++ ++ if (chip->cc_meas_high && mode == CC_PULL_UP) ++ regmap_write(chip->regmap, FUSB_REG_MEASURE, ++ chip->cc_meas_high); ++} ++ ++static int tcpm_set_cc(struct fusb30x_chip *chip, enum role_mode mode) ++{ ++ switch (mode) { ++ case ROLE_MODE_DFP: ++ tcpm_set_cc_pull_mode(chip, CC_PULL_UP); ++ regmap_update_bits(chip->regmap, FUSB_REG_CONTROL2, ++ CONTROL2_MODE | CONTROL2_TOG_RD_ONLY, ++ CONTROL2_MODE_DFP | CONTROL2_TOG_RD_ONLY); ++ break; ++ case ROLE_MODE_UFP: ++ tcpm_set_cc_pull_mode(chip, CC_PULL_UP); ++ regmap_update_bits(chip->regmap, FUSB_REG_CONTROL2, ++ CONTROL2_MODE | CONTROL2_TOG_RD_ONLY, ++ CONTROL2_MODE_UFP); ++ break; ++ case ROLE_MODE_DRP: ++ tcpm_set_cc_pull_mode(chip, CC_PULL_NONE); ++ regmap_update_bits(chip->regmap, FUSB_REG_CONTROL2, ++ CONTROL2_MODE | CONTROL2_TOG_RD_ONLY, ++ CONTROL2_MODE_DRP | CONTROL2_TOG_RD_ONLY); ++ break; ++ default: ++ dev_err(chip->dev, "%s: Unsupport cc mode %d\n", ++ __func__, mode); ++ return -EINVAL; ++ break; ++ } ++ ++ regmap_update_bits(chip->regmap, FUSB_REG_CONTROL2, CONTROL2_TOGGLE, ++ CONTROL2_TOGGLE); ++ ++ return 0; ++} ++ ++static int tcpm_set_rx_enable(struct fusb30x_chip *chip, int enable) ++{ ++ u8 val = 0; ++ ++ if (enable) { ++ if (chip->cc_polarity == TYPEC_POLARITY_CC1) ++ val |= SWITCHES0_MEAS_CC1; ++ else ++ val |= SWITCHES0_MEAS_CC2; ++ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0, ++ SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2, ++ val); ++ fusb302_flush_rx_fifo(chip); ++ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES1, ++ SWITCHES1_AUTO_CRC, SWITCHES1_AUTO_CRC); ++ } else { ++ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0, ++ SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2, ++ 0); ++ regmap_update_bits(chip->regmap, ++ FUSB_REG_SWITCHES1, SWITCHES1_AUTO_CRC, 0); ++ } ++ ++ return 0; ++} ++ ++static int tcpm_set_msg_header(struct fusb30x_chip *chip) ++{ ++ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES1, ++ SWITCHES1_POWERROLE | SWITCHES1_DATAROLE, ++ (chip->notify.power_role << 7) | ++ (chip->notify.data_role << 4)); ++ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES1, ++ SWITCHES1_SPECREV, 2 << 5); ++ return 0; ++} ++ ++static int tcpm_set_polarity(struct fusb30x_chip *chip, ++ enum typec_cc_polarity polarity) ++{ ++ u8 val = 0; ++ ++ if (chip->vconn_enabled) { ++ if (polarity) ++ val |= SWITCHES0_VCONN_CC1; ++ else ++ val |= SWITCHES0_VCONN_CC2; ++ } ++ ++ if (chip->cc_state & CC_STATE_TOGSS_IS_UFP) { ++ if (polarity == TYPEC_POLARITY_CC1) ++ val |= SWITCHES0_MEAS_CC1; ++ else ++ val |= SWITCHES0_MEAS_CC2; ++ } else { ++ if (polarity == TYPEC_POLARITY_CC1) ++ val |= SWITCHES0_MEAS_CC1 | SWITCHES0_PU_EN1; ++ else ++ val |= SWITCHES0_MEAS_CC2 | SWITCHES0_PU_EN2; ++ } ++ ++ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0, ++ SWITCHES0_VCONN_CC1 | SWITCHES0_VCONN_CC2 | ++ SWITCHES0_MEAS_CC1 | SWITCHES0_MEAS_CC2 | ++ SWITCHES0_PU_EN1 | SWITCHES0_PU_EN2, ++ val); ++ ++ val = 0; ++ if (polarity == TYPEC_POLARITY_CC1) ++ val |= SWITCHES1_TXCC1; ++ else ++ val |= SWITCHES1_TXCC2; ++ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES1, ++ SWITCHES1_TXCC1 | SWITCHES1_TXCC2, ++ val); ++ ++ chip->cc_polarity = polarity; ++ ++ return 0; ++} ++ ++static int tcpm_set_vconn(struct fusb30x_chip *chip, int enable) ++{ ++ u8 val = 0; ++ ++ if (enable) { ++ if (chip->cc_polarity == TYPEC_POLARITY_CC1) ++ val = SWITCHES0_VCONN_CC2; ++ else ++ val = SWITCHES0_VCONN_CC1; ++ } ++ regmap_update_bits(chip->regmap, FUSB_REG_SWITCHES0, ++ SWITCHES0_VCONN_CC1 | SWITCHES0_VCONN_CC2, ++ val); ++ chip->vconn_enabled = (bool)enable; ++ return 0; ++} ++ ++static void fusb302_pd_reset(struct fusb30x_chip *chip) ++{ ++ regmap_write(chip->regmap, FUSB_REG_RESET, RESET_PD_RESET); ++ regmap_reinit_cache(chip->regmap, &fusb302_regmap_config); ++} ++ ++static void tcpm_select_rp_value(struct fusb30x_chip *chip, u32 rp) ++{ ++ u32 control0_reg; ++ ++ regmap_read(chip->regmap, FUSB_REG_CONTROL0, &control0_reg); ++ ++ control0_reg &= ~CONTROL0_HOST_CUR; ++ /* ++ * according to the host current, the compare value is different ++ * Fusb302 datasheet Table 3 ++ */ ++ switch (rp) { ++ /* ++ * host pull up current is 80ua , high voltage is 1.596v, ++ * low is 0.21v ++ */ ++ case TYPEC_RP_USB: ++ chip->cc_meas_high = 0x26; ++ chip->cc_meas_low = 0x5; ++ control0_reg |= CONTROL0_HOST_CUR_USB; ++ break; ++ /* ++ * host pull up current is 330ua , high voltage is 2.604v, ++ * low is 0.798v ++ */ ++ case TYPEC_RP_3A0: ++ chip->cc_meas_high = 0x3e; ++ chip->cc_meas_low = 0x13; ++ control0_reg |= CONTROL0_HOST_CUR_3A0; ++ break; ++ /* ++ * host pull up current is 180ua , high voltage is 1.596v, ++ * low is 0.42v ++ */ ++ case TYPEC_RP_1A5: ++ default: ++ chip->cc_meas_high = 0x26; ++ chip->cc_meas_low = 0xa; ++ control0_reg |= CONTROL0_HOST_CUR_1A5; ++ break; ++ } ++ ++ regmap_write(chip->regmap, FUSB_REG_CONTROL0, control0_reg); ++} ++ ++static int tcpm_check_vbus(struct fusb30x_chip *chip) ++{ ++ u32 val; ++ ++ /* Read status register */ ++ regmap_read(chip->regmap, FUSB_REG_STATUS0, &val); ++ ++ return (val & STATUS0_VBUSOK) ? 1 : 0; ++} ++ ++static void tcpm_init(struct fusb30x_chip *chip) ++{ ++ u8 val; ++ u32 tmp; ++ ++ regmap_read(chip->regmap, FUSB_REG_DEVICEID, &tmp); ++ chip->chip_id = (u8)tmp; ++ platform_set_vbus_lvl_enable(chip, 0, 0); ++ chip->notify.is_cc_connected = false; ++ chip->cc_state = 0; ++ ++ /* restore default settings */ ++ regmap_update_bits(chip->regmap, FUSB_REG_RESET, RESET_SW_RESET, ++ RESET_SW_RESET); ++ fusb302_pd_reset(chip); ++ /* set auto_retry and number of retries */ ++ regmap_update_bits(chip->regmap, FUSB_REG_CONTROL3, ++ CONTROL3_AUTO_RETRY | CONTROL3_N_RETRIES, ++ CONTROL3_AUTO_RETRY | CONTROL3_N_RETRIES), ++ ++ /* set interrupts */ ++ val = 0xff; ++ val &= ~(MASK_M_COLLISION | MASK_M_ALERT | MASK_M_VBUSOK); ++ regmap_write(chip->regmap, FUSB_REG_MASK, val); ++ ++ val = 0xff; ++ val &= ~(MASKA_M_RETRYFAIL | MASKA_M_HARDSENT | MASKA_M_TXSENT | ++ MASKA_M_HARDRST | MASKA_M_TOGDONE); ++ regmap_write(chip->regmap, FUSB_REG_MASKA, val); ++ ++ val = ~MASKB_M_GCRCSEND; ++ regmap_write(chip->regmap, FUSB_REG_MASKB, val); ++ ++ tcpm_select_rp_value(chip, TYPEC_RP_1A5); ++ /* Interrupts Enable */ ++ regmap_update_bits(chip->regmap, FUSB_REG_CONTROL0, CONTROL0_INT_MASK, ++ ~CONTROL0_INT_MASK); ++ ++ tcpm_set_vconn(chip, 0); ++ ++ regmap_write(chip->regmap, FUSB_REG_POWER, 0xf); ++} ++ ++static void pd_execute_hard_reset(struct fusb30x_chip *chip) ++{ ++ chip->msg_id = 0; ++ chip->vdm_state = VDM_STATE_DISCOVERY_ID; ++ if (chip->notify.power_role) ++ set_state(chip, policy_src_transition_default); ++ else ++ set_state(chip, policy_snk_transition_default); ++} ++ ++static void tcpc_alert(struct fusb30x_chip *chip, u32 *evt) ++{ ++ int interrupt, interrupta, interruptb; ++ u32 val; ++ static int retry; ++ ++ regmap_read(chip->regmap, FUSB_REG_INTERRUPT, &interrupt); ++ regmap_read(chip->regmap, FUSB_REG_INTERRUPTA, &interrupta); ++ regmap_read(chip->regmap, FUSB_REG_INTERRUPTB, &interruptb); ++ ++ if ((interrupt & INTERRUPT_COMP_CHNG) && ++ (!(chip->cc_state & CC_STATE_TOGSS_IS_UFP))) { ++ regmap_read(chip->regmap, FUSB_REG_STATUS0, &val); ++ if (val & STATUS0_COMP) ++ *evt |= EVENT_CC; ++ } ++ ++ if (interrupt & INTERRUPT_VBUSOK) { ++ if (chip->notify.is_cc_connected) ++ *evt |= EVENT_CC; ++ } ++ ++ if (interrupta & INTERRUPTA_TOGDONE) { ++ *evt |= EVENT_CC; ++ regmap_read(chip->regmap, FUSB_REG_STATUS1A, &val); ++ chip->cc_state = ((u8)val >> 3) & 0x07; ++ ++ regmap_update_bits(chip->regmap, FUSB_REG_CONTROL2, ++ CONTROL2_TOGGLE, ++ 0); ++ } ++ ++ if (interrupta & INTERRUPTA_TXSENT) { ++ *evt |= EVENT_TX; ++ chip->tx_state = tx_success; ++ } ++ ++ if (interruptb & INTERRUPTB_GCRCSENT) ++ *evt |= EVENT_RX; ++ ++ if (interrupta & INTERRUPTA_HARDRST) { ++ fusb302_pd_reset(chip); ++ pd_execute_hard_reset(chip); ++ *evt |= EVENT_REC_RESET; ++ } ++ ++ if (interrupta & INTERRUPTA_RETRYFAIL) { ++ *evt |= EVENT_TX; ++ chip->tx_state = tx_failed; ++ } ++ ++ if (interrupta & INTERRUPTA_HARDSENT) { ++ /* ++ * The fusb PD should be reset once to sync adapter PD ++ * signal after fusb sent hard reset cmd.This is not PD ++ * device if reset failed. ++ */ ++ if (!retry) { ++ retry = 1; ++ fusb302_pd_reset(chip); ++ pd_execute_hard_reset(chip); ++ } else { ++ retry = 0; ++ chip->tx_state = tx_success; ++ chip->timer_state = T_DISABLED; ++ *evt |= EVENT_TX; ++ } ++ } ++} ++ ++static void mux_alert(struct fusb30x_chip *chip, u32 *evt) ++{ ++ if (!chip->timer_mux) { ++ *evt |= EVENT_TIMER_MUX; ++ chip->timer_mux = T_DISABLED; ++ } ++ ++ if (!chip->timer_state) { ++ *evt |= EVENT_TIMER_STATE; ++ chip->timer_state = T_DISABLED; ++ } ++ ++ if (chip->work_continue) { ++ *evt |= chip->work_continue; ++ chip->work_continue = 0; ++ } ++} ++ ++static void set_state_unattached(struct fusb30x_chip *chip) ++{ ++ dev_info(chip->dev, "connection has disconnected\n"); ++ tcpm_init(chip); ++ tcpm_set_rx_enable(chip, 0); ++ set_state(chip, unattached); ++ tcpm_set_cc(chip, chip->role); ++ ++ /* claer notify_info */ ++ memset(&chip->notify, 0, sizeof(struct notify_info)); ++ platform_fusb_notify(chip); ++ ++ if (chip->gpio_discharge) ++ gpiod_set_value(chip->gpio_discharge, 1); ++ msleep(100); ++ if (chip->gpio_discharge) ++ gpiod_set_value(chip->gpio_discharge, 0); ++ ++ regmap_update_bits(chip->regmap, FUSB_REG_MASK, ++ MASK_M_COMP_CHNG, MASK_M_COMP_CHNG); ++ chip->try_role_complete = false; ++} ++ ++static void set_mesg(struct fusb30x_chip *chip, int cmd, int is_DMT) ++{ ++ int i; ++ struct PD_CAP_INFO *pd_cap_info = &chip->pd_cap_info; ++ ++ chip->send_head = ((chip->msg_id & 0x7) << 9) | ++ ((chip->notify.power_role & 0x1) << 8) | ++ (1 << 6) | ++ ((chip->notify.data_role & 0x1) << 5); ++ ++ if (is_DMT) { ++ switch (cmd) { ++ case DMT_SOURCECAPABILITIES: ++ chip->send_head |= ((chip->n_caps_used & 0x3) << 12) | (cmd & 0xf); ++ ++ for (i = 0; i < chip->n_caps_used; i++) { ++ chip->send_load[i] = (pd_cap_info->supply_type << 30) | ++ (pd_cap_info->dual_role_power << 29) | ++ (pd_cap_info->usb_suspend_support << 28) | ++ (pd_cap_info->externally_powered << 27) | ++ (pd_cap_info->usb_communications_cap << 26) | ++ (pd_cap_info->data_role_swap << 25) | ++ (pd_cap_info->peak_current << 20) | ++ (chip->source_power_supply[i] << 10) | ++ (chip->source_max_current[i]); ++ } ++ break; ++ case DMT_REQUEST: ++ chip->send_head |= ((1 << 12) | (cmd & 0xf)); ++ /* send request with FVRDO */ ++ chip->send_load[0] = (chip->pos_power << 28) | ++ (0 << 27) | ++ (1 << 26) | ++ (0 << 25) | ++ (0 << 24); ++ ++ switch (CAP_POWER_TYPE(chip->rec_load[chip->pos_power - 1])) { ++ case 0: ++ /* Fixed Supply */ ++ chip->send_load[0] |= ((CAP_FPDO_VOLTAGE(chip->rec_load[chip->pos_power - 1]) << 10) & 0x3ff); ++ chip->send_load[0] |= (CAP_FPDO_CURRENT(chip->rec_load[chip->pos_power - 1]) & 0x3ff); ++ break; ++ case 1: ++ /* Battery */ ++ chip->send_load[0] |= ((CAP_VPDO_VOLTAGE(chip->rec_load[chip->pos_power - 1]) << 10) & 0x3ff); ++ chip->send_load[0] |= (CAP_VPDO_CURRENT(chip->rec_load[chip->pos_power - 1]) & 0x3ff); ++ break; ++ default: ++ /* not meet battery caps */ ++ break; ++ } ++ break; ++ case DMT_SINKCAPABILITIES: ++ break; ++ case DMT_VENDERDEFINED: ++ break; ++ default: ++ break; ++ } ++ } else { ++ chip->send_head |= (cmd & 0xf); ++ } ++} ++ ++/* ++ * This algorithm defaults to choosing higher pin config over lower ones in ++ * order to prefer multi-function if desired. ++ * ++ * NAME | SIGNALING | OUTPUT TYPE | MULTI-FUNCTION | PIN CONFIG ++ * ------------------------------------------------------------- ++ * A | USB G2 | ? | no | 00_0001 ++ * B | USB G2 | ? | yes | 00_0010 ++ * C | DP | CONVERTED | no | 00_0100 ++ * D | PD | CONVERTED | yes | 00_1000 ++ * E | DP | DP | no | 01_0000 ++ * F | PD | DP | yes | 10_0000 ++ * ++ * if UFP has NOT asserted multi-function preferred code masks away B/D/F ++ * leaving only A/C/E. For single-output dongles that should leave only one ++ * possible pin config depending on whether its a converter DP->(VGA|HDMI) or DP ++ * output. If UFP is a USB-C receptacle it may assert C/D/E/F. The DFP USB-C ++ * receptacle must always choose C/D in those cases. ++ */ ++static int pd_dfp_dp_get_pin_assignment(struct fusb30x_chip *chip, ++ uint32_t caps, uint32_t status) ++{ ++ uint32_t pin_caps; ++ ++ /* revisit with DFP that can be a sink */ ++ pin_caps = PD_DP_PIN_CAPS(caps); ++ ++ /* if don't want multi-function then ignore those pin configs */ ++ if (!PD_VDO_DPSTS_MF_PREF(status)) ++ pin_caps &= ~MODE_DP_PIN_MF_MASK; ++ ++ /* revisit if DFP drives USB Gen 2 signals */ ++ if (PD_DP_SIGNAL_GEN2(caps)) ++ pin_caps &= ~MODE_DP_PIN_DP_MASK; ++ else ++ pin_caps &= ~MODE_DP_PIN_BR2_MASK; ++ ++ /* if C/D present they have precedence over E/F for USB-C->USB-C */ ++ if (pin_caps & (MODE_DP_PIN_C | MODE_DP_PIN_D)) ++ pin_caps &= ~(MODE_DP_PIN_E | MODE_DP_PIN_F); ++ ++ /* returns undefined for zero */ ++ if (!pin_caps) ++ return 0; ++ ++ /* choosing higher pin config over lower ones */ ++ return 1 << (31 - __builtin_clz(pin_caps)); ++} ++ ++static void set_vdm_mesg(struct fusb30x_chip *chip, int cmd, int type, int mode) ++{ ++ chip->send_head = (chip->msg_id & 0x7) << 9; ++ chip->send_head |= (chip->notify.power_role & 0x1) << 8; ++ ++ chip->send_head = ((chip->msg_id & 0x7) << 9) | ++ ((chip->notify.power_role & 0x1) << 8) | ++ (1 << 6) | ++ ((chip->notify.data_role & 0x1) << 5) | ++ (DMT_VENDERDEFINED & 0xf); ++ ++ chip->send_load[0] = (1 << 15) | ++ (0 << 13) | ++ (type << 6) | ++ (cmd); ++ ++ switch (cmd) { ++ case VDM_DISCOVERY_ID: ++ case VDM_DISCOVERY_SVIDS: ++ case VDM_ATTENTION: ++ chip->send_load[0] |= (0xff00 << 16); ++ chip->send_head |= (1 << 12); ++ break; ++ case VDM_DISCOVERY_MODES: ++ chip->send_load[0] |= ++ (chip->vdm_svid[chip->val_tmp >> 1] << 16); ++ chip->send_head |= (1 << 12); ++ break; ++ case VDM_ENTER_MODE: ++ chip->send_head |= (1 << 12); ++ chip->send_load[0] |= (mode << 8) | (0xff01 << 16); ++ break; ++ case VDM_EXIT_MODE: ++ chip->send_head |= (1 << 12); ++ chip->send_load[0] |= (0x0f << 8) | (0xff01 << 16); ++ break; ++ case VDM_DP_STATUS_UPDATE: ++ chip->send_head |= (2 << 12); ++ chip->send_load[0] |= (1 << 8) | (0xff01 << 16); ++ chip->send_load[1] = 5; ++ break; ++ case VDM_DP_CONFIG: ++ chip->send_head |= (2 << 12); ++ chip->send_load[0] |= (1 << 8) | (0xff01 << 16); ++ ++ chip->notify.pin_assignment_def = ++ pd_dfp_dp_get_pin_assignment(chip, chip->notify.dp_caps, ++ chip->notify.dp_status); ++ ++ chip->send_load[1] = (chip->notify.pin_assignment_def << 8) | ++ (1 << 2) | 2; ++ dev_dbg(chip->dev, "DisplayPort Configurations: 0x%08x\n", ++ chip->send_load[1]); ++ break; ++ default: ++ break; ++ } ++} ++ ++static enum tx_state policy_send_hardrst(struct fusb30x_chip *chip, u32 evt) ++{ ++ switch (chip->tx_state) { ++ case 0: ++ regmap_update_bits(chip->regmap, FUSB_REG_CONTROL3, ++ CONTROL3_SEND_HARDRESET, ++ CONTROL3_SEND_HARDRESET); ++ chip->tx_state = tx_busy; ++ chip->timer_state = T_BMC_TIMEOUT; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++ break; ++ default: ++ if (evt & EVENT_TIMER_STATE) ++ chip->tx_state = tx_success; ++ break; ++ } ++ return chip->tx_state; ++} ++ ++static enum tx_state policy_send_data(struct fusb30x_chip *chip) ++{ ++ u8 senddata[40]; ++ int pos = 0; ++ u8 len; ++ ++ switch (chip->tx_state) { ++ case 0: ++ senddata[pos++] = FUSB_TKN_SYNC1; ++ senddata[pos++] = FUSB_TKN_SYNC1; ++ senddata[pos++] = FUSB_TKN_SYNC1; ++ senddata[pos++] = FUSB_TKN_SYNC2; ++ ++ len = PD_HEADER_CNT(chip->send_head) << 2; ++ senddata[pos++] = FUSB_TKN_PACKSYM | ((len + 2) & 0x1f); ++ ++ senddata[pos++] = chip->send_head & 0xff; ++ senddata[pos++] = (chip->send_head >> 8) & 0xff; ++ ++ memcpy(&senddata[pos], chip->send_load, len); ++ pos += len; ++ ++ senddata[pos++] = FUSB_TKN_JAMCRC; ++ senddata[pos++] = FUSB_TKN_EOP; ++ senddata[pos++] = FUSB_TKN_TXOFF; ++ senddata[pos++] = FUSB_TKN_TXON; ++ ++ regmap_raw_write(chip->regmap, FUSB_REG_FIFO, senddata, pos); ++ chip->tx_state = tx_busy; ++ break; ++ ++ default: ++ /* wait Tx result */ ++ break; ++ } ++ ++ return chip->tx_state; ++} ++ ++static void process_vdm_msg(struct fusb30x_chip *chip) ++{ ++ u32 vdm_header = chip->rec_load[0]; ++ int i; ++ u32 tmp; ++ ++ /* can't procee unstructed vdm msg */ ++ if (!GET_VDMHEAD_STRUCT_TYPE(vdm_header)) { ++ dev_warn(chip->dev, "unknown unstructed vdm message\n"); ++ return; ++ } ++ ++ switch (GET_VDMHEAD_CMD_TYPE(vdm_header)) { ++ case VDM_TYPE_INIT: ++ switch (GET_VDMHEAD_CMD(vdm_header)) { ++ case VDM_ATTENTION: ++ chip->notify.dp_status = GET_DP_STATUS(chip->rec_load[1]); ++ dev_info(chip->dev, "attention, dp_status %x\n", ++ chip->rec_load[1]); ++ chip->notify.attention = true; ++ platform_fusb_notify(chip); ++ break; ++ default: ++ dev_warn(chip->dev, "rec unknown init vdm msg\n"); ++ break; ++ } ++ break; ++ case VDM_TYPE_ACK: ++ switch (GET_VDMHEAD_CMD(vdm_header)) { ++ case VDM_DISCOVERY_ID: ++ chip->vdm_id = chip->rec_load[1]; ++ break; ++ case VDM_DISCOVERY_SVIDS: ++ for (i = 0; i < 6; i++) { ++ tmp = (chip->rec_load[i + 1] >> 16) & ++ 0x0000ffff; ++ if (tmp) { ++ chip->vdm_svid[i * 2] = tmp; ++ chip->vdm_svid_num++; ++ } else { ++ break; ++ } ++ ++ tmp = (chip->rec_load[i + 1] & 0x0000ffff); ++ if (tmp) { ++ chip->vdm_svid[i * 2 + 1] = tmp; ++ chip->vdm_svid_num++; ++ } else { ++ break; ++ } ++ } ++ break; ++ case VDM_DISCOVERY_MODES: ++ /* indicate there are some vdo modes */ ++ if (PD_HEADER_CNT(chip->rec_head) > 1) { ++ /* ++ * store mode config, ++ * enter first mode default ++ */ ++ tmp = chip->rec_load[1]; ++ ++ if ((!((tmp >> 8) & 0x3f)) && ++ (!((tmp >> 16) & 0x3f))) { ++ chip->val_tmp |= 1; ++ break; ++ } ++ chip->notify.dp_caps = chip->rec_load[1]; ++ chip->notify.pin_assignment_def = 0; ++ chip->notify.pin_assignment_support = ++ PD_DP_PIN_CAPS(tmp); ++ chip->val_tmp |= 1; ++ dev_dbg(chip->dev, ++ "DisplayPort Capabilities: 0x%08x\n", ++ chip->rec_load[1]); ++ } ++ break; ++ case VDM_ENTER_MODE: ++ chip->val_tmp = 1; ++ break; ++ case VDM_DP_STATUS_UPDATE: ++ chip->notify.dp_status = GET_DP_STATUS(chip->rec_load[1]); ++ dev_dbg(chip->dev, "DisplayPort Status: 0x%08x\n", ++ chip->rec_load[1]); ++ chip->val_tmp = 1; ++ break; ++ case VDM_DP_CONFIG: ++ chip->val_tmp = 1; ++ dev_info(chip->dev, ++ "DP config successful, pin_assignment 0x%x\n", ++ chip->notify.pin_assignment_def); ++ chip->notify.is_enter_mode = true; ++ break; ++ default: ++ break; ++ } ++ break; ++ case VDM_TYPE_NACK: ++ dev_warn(chip->dev, "REC NACK for 0x%x\n", ++ GET_VDMHEAD_CMD(vdm_header)); ++ /* disable vdm */ ++ chip->vdm_state = VDM_STATE_ERR; ++ break; ++ } ++} ++ ++static int vdm_send_discoveryid(struct fusb30x_chip *chip, u32 evt) ++{ ++ int tmp; ++ ++ switch (chip->vdm_send_state) { ++ case 0: ++ set_vdm_mesg(chip, VDM_DISCOVERY_ID, VDM_TYPE_INIT, 0); ++ chip->vdm_id = 0; ++ chip->tx_state = 0; ++ chip->vdm_send_state++; ++ case 1: ++ tmp = policy_send_data(chip); ++ if (tmp == tx_success) { ++ chip->vdm_send_state++; ++ chip->timer_state = T_SENDER_RESPONSE; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++ } else if (tmp == tx_failed) { ++ dev_warn(chip->dev, "VDM_DISCOVERY_ID send failed\n"); ++ /* disable auto_vdm_machine */ ++ chip->vdm_state = VDM_STATE_ERR; ++ return -EPIPE; ++ } ++ ++ if (chip->vdm_send_state != 2) ++ break; ++ default: ++ if (chip->vdm_id) { ++ chip->vdm_send_state = 0; ++ return 0; ++ } else if (evt & EVENT_TIMER_STATE) { ++ dev_warn(chip->dev, "VDM_DISCOVERY_ID time out\n"); ++ chip->vdm_state = VDM_STATE_ERR; ++ chip->work_continue |= EVENT_WORK_CONTINUE; ++ return -ETIMEDOUT; ++ } ++ break; ++ } ++ return -EINPROGRESS; ++} ++ ++static int vdm_send_discoverysvid(struct fusb30x_chip *chip, u32 evt) ++{ ++ int tmp; ++ ++ switch (chip->vdm_send_state) { ++ case 0: ++ set_vdm_mesg(chip, VDM_DISCOVERY_SVIDS, VDM_TYPE_INIT, 0); ++ memset(chip->vdm_svid, 0, sizeof(chip->vdm_svid)); ++ chip->vdm_svid_num = 0; ++ chip->tx_state = 0; ++ chip->vdm_send_state++; ++ case 1: ++ tmp = policy_send_data(chip); ++ if (tmp == tx_success) { ++ chip->vdm_send_state++; ++ chip->timer_state = T_SENDER_RESPONSE; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++ } else if (tmp == tx_failed) { ++ dev_warn(chip->dev, "VDM_DISCOVERY_SVIDS send failed\n"); ++ /* disable auto_vdm_machine */ ++ chip->vdm_state = VDM_STATE_ERR; ++ return -EPIPE; ++ } ++ ++ if (chip->vdm_send_state != 2) ++ break; ++ default: ++ if (chip->vdm_svid_num) { ++ chip->vdm_send_state = 0; ++ return 0; ++ } else if (evt & EVENT_TIMER_STATE) { ++ dev_warn(chip->dev, "VDM_DISCOVERY_SVIDS time out\n"); ++ chip->vdm_state = VDM_STATE_ERR; ++ chip->work_continue |= EVENT_WORK_CONTINUE; ++ return -ETIMEDOUT; ++ } ++ break; ++ } ++ return -EINPROGRESS; ++} ++ ++static int vdm_send_discoverymodes(struct fusb30x_chip *chip, u32 evt) ++{ ++ int tmp; ++ ++ if ((chip->val_tmp >> 1) != chip->vdm_svid_num) { ++ switch (chip->vdm_send_state) { ++ case 0: ++ set_vdm_mesg(chip, VDM_DISCOVERY_MODES, ++ VDM_TYPE_INIT, 0); ++ chip->tx_state = 0; ++ chip->vdm_send_state++; ++ case 1: ++ tmp = policy_send_data(chip); ++ if (tmp == tx_success) { ++ chip->vdm_send_state++; ++ chip->timer_state = T_SENDER_RESPONSE; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++ } else if (tmp == tx_failed) { ++ dev_warn(chip->dev, ++ "VDM_DISCOVERY_MODES send failed\n"); ++ chip->vdm_state = VDM_STATE_ERR; ++ return -EPIPE; ++ } ++ ++ if (chip->vdm_send_state != 2) ++ break; ++ default: ++ if (chip->val_tmp & 1) { ++ chip->val_tmp &= 0xfe; ++ chip->val_tmp += 2; ++ chip->vdm_send_state = 0; ++ chip->work_continue |= EVENT_WORK_CONTINUE; ++ } else if (evt & EVENT_TIMER_STATE) { ++ dev_warn(chip->dev, ++ "VDM_DISCOVERY_MODES time out\n"); ++ chip->vdm_state = VDM_STATE_ERR; ++ chip->work_continue |= EVENT_WORK_CONTINUE; ++ return -ETIMEDOUT; ++ } ++ break; ++ } ++ } else { ++ chip->val_tmp = 0; ++ return 0; ++ } ++ ++ return -EINPROGRESS; ++} ++ ++static int vdm_send_entermode(struct fusb30x_chip *chip, u32 evt) ++{ ++ int tmp; ++ ++ switch (chip->vdm_send_state) { ++ case 0: ++ set_vdm_mesg(chip, VDM_ENTER_MODE, VDM_TYPE_INIT, 1); ++ chip->tx_state = 0; ++ chip->vdm_send_state++; ++ chip->notify.is_enter_mode = false; ++ case 1: ++ tmp = policy_send_data(chip); ++ if (tmp == tx_success) { ++ chip->vdm_send_state++; ++ chip->timer_state = T_SENDER_RESPONSE; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++ } else if (tmp == tx_failed) { ++ dev_warn(chip->dev, "VDM_ENTER_MODE send failed\n"); ++ /* disable auto_vdm_machine */ ++ chip->vdm_state = VDM_STATE_ERR; ++ return -EPIPE; ++ } ++ ++ if (chip->vdm_send_state != 2) ++ break; ++ default: ++ if (chip->val_tmp) { ++ chip->val_tmp = 0; ++ chip->vdm_send_state = 0; ++ return 0; ++ } else if (evt & EVENT_TIMER_STATE) { ++ dev_warn(chip->dev, "VDM_ENTER_MODE time out\n"); ++ chip->vdm_state = VDM_STATE_ERR; ++ chip->work_continue |= EVENT_WORK_CONTINUE; ++ return -ETIMEDOUT; ++ } ++ break; ++ } ++ return -EINPROGRESS; ++} ++ ++static int vdm_send_getdpstatus(struct fusb30x_chip *chip, u32 evt) ++{ ++ int tmp; ++ ++ switch (chip->vdm_send_state) { ++ case 0: ++ set_vdm_mesg(chip, VDM_DP_STATUS_UPDATE, VDM_TYPE_INIT, 1); ++ chip->tx_state = 0; ++ chip->vdm_send_state++; ++ case 1: ++ tmp = policy_send_data(chip); ++ if (tmp == tx_success) { ++ chip->vdm_send_state++; ++ chip->timer_state = T_SENDER_RESPONSE; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++ } else if (tmp == tx_failed) { ++ dev_warn(chip->dev, ++ "VDM_DP_STATUS_UPDATE send failed\n"); ++ /* disable auto_vdm_machine */ ++ chip->vdm_state = VDM_STATE_ERR; ++ return -EPIPE; ++ } ++ ++ if (chip->vdm_send_state != 2) ++ break; ++ default: ++ if (chip->val_tmp) { ++ chip->val_tmp = 0; ++ chip->vdm_send_state = 0; ++ return 0; ++ } else if (evt & EVENT_TIMER_STATE) { ++ dev_warn(chip->dev, "VDM_DP_STATUS_UPDATE time out\n"); ++ chip->vdm_state = VDM_STATE_ERR; ++ chip->work_continue |= EVENT_WORK_CONTINUE; ++ return -ETIMEDOUT; ++ } ++ break; ++ } ++ return -EINPROGRESS; ++} ++ ++static int vdm_send_dpconfig(struct fusb30x_chip *chip, u32 evt) ++{ ++ int tmp; ++ ++ switch (chip->vdm_send_state) { ++ case 0: ++ set_vdm_mesg(chip, VDM_DP_CONFIG, VDM_TYPE_INIT, 0); ++ chip->tx_state = 0; ++ chip->vdm_send_state++; ++ case 1: ++ tmp = policy_send_data(chip); ++ if (tmp == tx_success) { ++ chip->vdm_send_state++; ++ chip->timer_state = T_SENDER_RESPONSE; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++ } else if (tmp == tx_failed) { ++ dev_warn(chip->dev, "vdm_send_dpconfig send failed\n"); ++ /* disable auto_vdm_machine */ ++ chip->vdm_state = VDM_STATE_ERR; ++ return -EPIPE; ++ } ++ ++ if (chip->vdm_send_state != 2) ++ break; ++ default: ++ if (chip->val_tmp) { ++ chip->val_tmp = 0; ++ chip->vdm_send_state = 0; ++ return 0; ++ } else if (evt & EVENT_TIMER_STATE) { ++ dev_warn(chip->dev, "vdm_send_dpconfig time out\n"); ++ chip->vdm_state = VDM_STATE_ERR; ++ chip->work_continue |= EVENT_WORK_CONTINUE; ++ return -ETIMEDOUT; ++ } ++ break; ++ } ++ return -EINPROGRESS; ++} ++ ++/* without break if success */ ++#define AUTO_VDM_HANDLE(func, chip, evt, conditions) \ ++do { \ ++ conditions = func(chip, evt); \ ++ if (!conditions) { \ ++ chip->vdm_state++; \ ++ chip->work_continue |= EVENT_WORK_CONTINUE; \ ++ } else { \ ++ if (conditions != -EINPROGRESS) \ ++ chip->vdm_state = VDM_STATE_ERR; \ ++ } \ ++} while (0) ++ ++static void auto_vdm_machine(struct fusb30x_chip *chip, u32 evt) ++{ ++ int conditions; ++ ++ switch (chip->vdm_state) { ++ case VDM_STATE_DISCOVERY_ID: ++ AUTO_VDM_HANDLE(vdm_send_discoveryid, chip, evt, conditions); ++ break; ++ case VDM_STATE_DISCOVERY_SVID: ++ AUTO_VDM_HANDLE(vdm_send_discoverysvid, chip, evt, conditions); ++ break; ++ case VDM_STATE_DISCOVERY_MODES: ++ AUTO_VDM_HANDLE(vdm_send_discoverymodes, chip, evt, conditions); ++ break; ++ case VDM_STATE_ENTER_MODE: ++ AUTO_VDM_HANDLE(vdm_send_entermode, chip, evt, conditions); ++ break; ++ case VDM_STATE_UPDATE_STATUS: ++ AUTO_VDM_HANDLE(vdm_send_getdpstatus, chip, evt, conditions); ++ break; ++ case VDM_STATE_DP_CONFIG: ++ AUTO_VDM_HANDLE(vdm_send_dpconfig, chip, evt, conditions); ++ break; ++ case VDM_STATE_NOTIFY: ++ platform_fusb_notify(chip); ++ chip->vdm_state = VDM_STATE_READY; ++ break; ++ default: ++ break; ++ } ++} ++ ++static void fusb_state_disabled(struct fusb30x_chip *chip, u32 evt) ++{ ++ /* Do nothing */ ++} ++ ++static void fusb_state_unattached(struct fusb30x_chip *chip, u32 evt) ++{ ++ chip->notify.is_cc_connected = false; ++ chip->is_pd_support = false; ++ ++ if ((evt & EVENT_CC) && chip->cc_state) { ++ if (chip->cc_state & CC_STATE_TOGSS_IS_UFP) ++ set_state(chip, attach_wait_sink); ++ else ++ set_state(chip, attach_wait_source); ++ ++ chip->vbus_begin = tcpm_check_vbus(chip); ++ ++ tcpm_set_polarity(chip, (chip->cc_state & CC_STATE_TOGSS_CC1) ? ++ TYPEC_POLARITY_CC1 : ++ TYPEC_POLARITY_CC2); ++ tcpm_get_cc(chip, &chip->cc1, &chip->cc2); ++ chip->debounce_cnt = 0; ++ chip->timer_mux = 2; ++ fusb_timer_start(&chip->timer_mux_machine, chip->timer_mux); ++ } ++} ++ ++static void fusb_state_try_attach_set(struct fusb30x_chip *chip, ++ enum role_mode mode) ++{ ++ if (mode == ROLE_MODE_NONE || mode == ROLE_MODE_DRP || ++ mode == ROLE_MODE_ASS) ++ return; ++ ++ tcpm_init(chip); ++ tcpm_set_cc(chip, (mode == ROLE_MODE_DFP) ? ++ ROLE_MODE_DFP : ROLE_MODE_UFP); ++ chip->timer_mux = T_PD_TRY_DRP; ++ fusb_timer_start(&chip->timer_mux_machine, chip->timer_mux); ++ set_state(chip, (mode == ROLE_MODE_DFP) ? ++ attach_try_src : attach_try_snk); ++} ++ ++static void fusb_state_attach_wait_sink(struct fusb30x_chip *chip, u32 evt) ++{ ++ int cc1, cc2; ++ ++ if (evt & EVENT_TIMER_MUX) { ++ if (tcpm_check_vbus(chip)) { ++ chip->timer_mux = T_DISABLED; ++ if (chip->role == ROLE_MODE_DRP && ++ chip->try_role == ROLE_MODE_DFP && ++ !chip->try_role_complete) { ++ fusb_state_try_attach_set(chip, ROLE_MODE_DFP); ++ return; ++ } else if (chip->try_role_complete) { ++ chip->timer_mux = T_PD_SOURCE_ON; ++ fusb_timer_start(&chip->timer_mux_machine, ++ chip->timer_mux); ++ set_state(chip, attached_sink); ++ return; ++ } ++ } ++ ++ tcpm_get_cc(chip, &cc1, &cc2); ++ ++ if ((chip->cc1 == cc1) && (chip->cc2 == cc2)) { ++ chip->debounce_cnt++; ++ } else { ++ chip->cc1 = cc1; ++ chip->cc2 = cc2; ++ chip->debounce_cnt = 0; ++ } ++ ++ if (chip->debounce_cnt > N_DEBOUNCE_CNT) { ++ chip->timer_mux = T_DISABLED; ++ if ((chip->cc1 == TYPEC_CC_VOLT_RP && ++ chip->cc2 == TYPEC_CC_VOLT_OPEN) || ++ (chip->cc2 == TYPEC_CC_VOLT_RP && ++ chip->cc1 == TYPEC_CC_VOLT_OPEN)) { ++ chip->timer_mux = T_PD_SOURCE_ON; ++ fusb_timer_start(&chip->timer_mux_machine, ++ chip->timer_mux); ++ set_state(chip, attached_sink); ++ } else { ++ set_state_unattached(chip); ++ } ++ return; ++ } ++ ++ chip->timer_mux = 2; ++ fusb_timer_start(&chip->timer_mux_machine, ++ chip->timer_mux); ++ } ++} ++ ++static void fusb_state_attach_wait_source(struct fusb30x_chip *chip, u32 evt) ++{ ++ int cc1, cc2; ++ ++ if (evt & EVENT_TIMER_MUX) { ++ tcpm_get_cc(chip, &cc1, &cc2); ++ ++ if ((chip->cc1 == cc1) && (chip->cc2 == cc2)) { ++ chip->debounce_cnt++; ++ } else { ++ chip->cc1 = cc1; ++ chip->cc2 = cc2; ++ chip->debounce_cnt = 0; ++ } ++ ++ if (chip->debounce_cnt > N_DEBOUNCE_CNT) { ++ if (((!chip->cc1) || (!chip->cc2)) && ++ ((chip->cc1 == TYPEC_CC_VOLT_RD) || ++ (chip->cc2 == TYPEC_CC_VOLT_RD))) { ++ if (chip->role == ROLE_MODE_DRP && ++ chip->try_role == ROLE_MODE_UFP && ++ !chip->try_role_complete) ++ fusb_state_try_attach_set(chip, ++ ROLE_MODE_UFP); ++ else ++ set_state(chip, attached_source); ++ } else { ++ set_state_unattached(chip); ++ } ++ return; ++ } ++ ++ chip->timer_mux = 2; ++ fusb_timer_start(&chip->timer_mux_machine, ++ chip->timer_mux); ++ } ++} ++ ++static void fusb_state_attached_source(struct fusb30x_chip *chip, u32 evt) ++{ ++ platform_set_vbus_lvl_enable(chip, 1, 0); ++ tcpm_set_polarity(chip, (chip->cc_state & CC_STATE_TOGSS_CC1) ? ++ TYPEC_POLARITY_CC1 : TYPEC_POLARITY_CC2); ++ tcpm_set_vconn(chip, 1); ++ ++ chip->notify.is_cc_connected = true; ++ ++ chip->notify.power_role = POWER_ROLE_SOURCE; ++ chip->notify.data_role = DATA_ROLE_DFP; ++ chip->hardrst_count = 0; ++ set_state(chip, policy_src_startup); ++ regmap_update_bits(chip->regmap, FUSB_REG_MASK, MASK_M_COMP_CHNG, 0); ++ dev_info(chip->dev, "CC connected in %s as DFP\n", ++ chip->cc_polarity ? "CC1" : "CC2"); ++} ++ ++static void fusb_state_attached_sink(struct fusb30x_chip *chip, u32 evt) ++{ ++ if (tcpm_check_vbus(chip)) { ++ chip->timer_mux = T_DISABLED; ++ chip->timer_state = T_DISABLED; ++ if (!chip->try_role_complete && ++ chip->try_role == ROLE_MODE_DFP && ++ chip->role == ROLE_MODE_DRP) { ++ fusb_state_try_attach_set(chip, ROLE_MODE_DFP); ++ return; ++ } ++ ++ chip->try_role_complete = true; ++ chip->notify.is_cc_connected = true; ++ chip->notify.power_role = POWER_ROLE_SINK; ++ chip->notify.data_role = DATA_ROLE_UFP; ++ chip->hardrst_count = 0; ++ set_state(chip, policy_snk_startup); ++ dev_info(chip->dev, "CC connected in %s as UFP\n", ++ chip->cc_polarity ? "CC1" : "CC2"); ++ return; ++ } else if (evt & EVENT_TIMER_MUX) { ++ set_state_unattached(chip); ++ return; ++ } ++ ++ chip->timer_state = 2; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++} ++ ++static void fusb_state_try_attach(struct fusb30x_chip *chip, u32 evt, ++ enum role_mode mode) ++{ ++ if ((evt & EVENT_CC) && chip->cc_state) { ++ chip->try_role_complete = true; ++ if (chip->cc_state & CC_STATE_TOGSS_IS_UFP) ++ set_state(chip, (mode == ROLE_MODE_UFP) ? ++ attach_wait_sink : error_recovery); ++ else ++ set_state(chip, (mode == ROLE_MODE_DFP) ? ++ attach_wait_source : error_recovery); ++ ++ tcpm_set_polarity(chip, (chip->cc_state & CC_STATE_TOGSS_CC1) ? ++ TYPEC_POLARITY_CC1 : ++ TYPEC_POLARITY_CC2); ++ tcpm_get_cc(chip, &chip->cc1, &chip->cc2); ++ chip->debounce_cnt = 0; ++ chip->timer_mux = 2; ++ fusb_timer_start(&chip->timer_mux_machine, chip->timer_mux); ++ } else if (evt & EVENT_TIMER_MUX) { ++ if (!chip->try_role_complete) { ++ chip->try_role_complete = true; ++ fusb_state_try_attach_set(chip, ++ (mode == ROLE_MODE_DFP) ? ++ ROLE_MODE_UFP : ++ ROLE_MODE_DFP); ++ } else { ++ set_state(chip, error_recovery); ++ } ++ } ++} ++ ++static void fusb_soft_reset_parameter(struct fusb30x_chip *chip) ++{ ++ chip->caps_counter = 0; ++ chip->msg_id = 0; ++ chip->vdm_state = VDM_STATE_DISCOVERY_ID; ++ chip->vdm_substate = 0; ++ chip->vdm_send_state = 0; ++ chip->val_tmp = 0; ++ chip->pos_power = 0; ++} ++ ++static void fusb_state_src_startup(struct fusb30x_chip *chip, u32 evt) ++{ ++ chip->notify.is_pd_connected = false; ++ fusb_soft_reset_parameter(chip); ++ ++ memset(chip->partner_cap, 0, sizeof(chip->partner_cap)); ++ ++ tcpm_set_msg_header(chip); ++ tcpm_set_polarity(chip, chip->cc_polarity); ++ tcpm_set_rx_enable(chip, 1); ++ ++ set_state(chip, policy_src_send_caps); ++ platform_fusb_notify(chip); ++} ++ ++static void fusb_state_src_discovery(struct fusb30x_chip *chip, u32 evt) ++{ ++ switch (chip->sub_state) { ++ case 0: ++ chip->caps_counter++; ++ ++ if (chip->caps_counter < N_CAPS_COUNT) { ++ chip->timer_state = T_TYPEC_SEND_SOURCECAP; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++ chip->sub_state = 1; ++ } else { ++ set_state(chip, disabled); ++ } ++ break; ++ default: ++ if (evt & EVENT_TIMER_STATE) { ++ set_state(chip, policy_src_send_caps); ++ } else if (evt & EVENT_TIMER_MUX) { ++ if (!chip->is_pd_support) ++ set_state(chip, disabled); ++ else if (chip->hardrst_count > N_HARDRESET_COUNT) ++ set_state(chip, error_recovery); ++ else ++ set_state(chip, policy_src_send_hardrst); ++ } ++ break; ++ } ++} ++ ++static void fusb_state_src_send_caps(struct fusb30x_chip *chip, u32 evt) ++{ ++ u32 tmp; ++ ++ switch (chip->sub_state) { ++ case 0: ++ set_mesg(chip, DMT_SOURCECAPABILITIES, DATAMESSAGE); ++ chip->sub_state = 1; ++ chip->tx_state = tx_idle; ++ /* without break */ ++ case 1: ++ tmp = policy_send_data(chip); ++ ++ if (tmp == tx_success) { ++ chip->hardrst_count = 0; ++ chip->caps_counter = 0; ++ chip->timer_state = T_SENDER_RESPONSE; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++ chip->timer_mux = T_DISABLED; ++ chip->sub_state++; ++ chip->is_pd_support = true; ++ } else if (tmp == tx_failed) { ++ set_state(chip, policy_src_discovery); ++ break; ++ } ++ ++ if (!(evt & FLAG_EVENT)) ++ break; ++ default: ++ if (evt & EVENT_RX) { ++ if (PACKET_IS_DATA_MSG(chip->rec_head, DMT_REQUEST)) { ++ set_state(chip, policy_src_negotiate_cap); ++ } else { ++ set_state(chip, policy_src_send_softrst); ++ } ++ } else if (evt & EVENT_TIMER_STATE) { ++ if (chip->hardrst_count <= N_HARDRESET_COUNT) ++ set_state(chip, policy_src_send_hardrst); ++ else ++ set_state(chip, disabled); ++ } else if (evt & EVENT_TIMER_MUX) { ++ if (!chip->is_pd_support) ++ set_state(chip, disabled); ++ else if (chip->hardrst_count > N_HARDRESET_COUNT) ++ set_state(chip, error_recovery); ++ else ++ set_state(chip, policy_src_send_hardrst); ++ } ++ break; ++ } ++} ++ ++static void fusb_state_src_negotiate_cap(struct fusb30x_chip *chip, u32 evt) ++{ ++ u32 tmp; ++ ++ /* base on evb1 */ ++ tmp = (chip->rec_load[0] >> 28) & 0x07; ++ if (tmp > chip->n_caps_used) ++ set_state(chip, policy_src_cap_response); ++ else ++ set_state(chip, policy_src_transition_supply); ++} ++ ++static void fusb_state_src_transition_supply(struct fusb30x_chip *chip, ++ u32 evt) ++{ ++ u32 tmp; ++ ++ switch (chip->sub_state) { ++ case 0: ++ set_mesg(chip, CMT_ACCEPT, CONTROLMESSAGE); ++ chip->tx_state = tx_idle; ++ chip->sub_state++; ++ /* without break */ ++ case 1: ++ tmp = policy_send_data(chip); ++ if (tmp == tx_success) { ++ chip->timer_state = T_SRC_TRANSITION; ++ chip->sub_state++; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++ } else if (tmp == tx_failed) { ++ set_state(chip, policy_src_send_softrst); ++ } ++ break; ++ case 2: ++ if (evt & EVENT_TIMER_STATE) { ++ chip->notify.is_pd_connected = true; ++ platform_set_vbus_lvl_enable(chip, 1, 0); ++ set_mesg(chip, CMT_PS_RDY, CONTROLMESSAGE); ++ chip->tx_state = tx_idle; ++ chip->sub_state++; ++ chip->work_continue |= EVENT_WORK_CONTINUE; ++ } ++ break; ++ default: ++ tmp = policy_send_data(chip); ++ if (tmp == tx_success) { ++ dev_info(chip->dev, ++ "PD connected as DFP, supporting 5V\n"); ++ set_state(chip, policy_src_ready); ++ } else if (tmp == tx_failed) { ++ set_state(chip, policy_src_send_softrst); ++ } ++ break; ++ } ++} ++ ++static void fusb_state_src_cap_response(struct fusb30x_chip *chip, u32 evt) ++{ ++ u32 tmp; ++ ++ switch (chip->sub_state) { ++ case 0: ++ set_mesg(chip, CMT_REJECT, CONTROLMESSAGE); ++ chip->tx_state = tx_idle; ++ chip->sub_state++; ++ /* without break */ ++ default: ++ tmp = policy_send_data(chip); ++ if (tmp == tx_success) { ++ if (chip->notify.is_pd_connected) { ++ dev_info(chip->dev, ++ "PD connected as DFP, supporting 5V\n"); ++ set_state(chip, policy_src_ready); ++ } else { ++ set_state(chip, policy_src_send_hardrst); ++ } ++ } else if (tmp == tx_failed) { ++ set_state(chip, policy_src_send_softrst); ++ } ++ break; ++ } ++} ++ ++static void fusb_state_src_transition_default(struct fusb30x_chip *chip, ++ u32 evt) ++{ ++ switch (chip->sub_state) { ++ case 0: ++ chip->notify.is_pd_connected = false; ++ platform_set_vbus_lvl_enable(chip, 0, 0); ++ if (chip->notify.data_role) ++ regmap_update_bits(chip->regmap, ++ FUSB_REG_SWITCHES1, ++ SWITCHES1_DATAROLE, ++ SWITCHES1_DATAROLE); ++ else ++ regmap_update_bits(chip->regmap, ++ FUSB_REG_SWITCHES1, ++ SWITCHES1_DATAROLE, ++ 0); ++ ++ chip->timer_state = T_SRC_RECOVER; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++ chip->sub_state++; ++ break; ++ default: ++ if (evt & EVENT_TIMER_STATE) { ++ platform_set_vbus_lvl_enable(chip, 1, 0); ++ chip->timer_mux = T_NO_RESPONSE; ++ fusb_timer_start(&chip->timer_mux_machine, ++ chip->timer_mux); ++ set_state(chip, policy_src_startup); ++ dev_dbg(chip->dev, "reset over-> src startup\n"); ++ } ++ break; ++ } ++} ++ ++static void fusb_state_vcs_ufp_evaluate_swap(struct fusb30x_chip *chip, u32 evt) ++{ ++ if (chip->vconn_supported) ++ set_state(chip, policy_vcs_ufp_accept); ++ else ++ set_state(chip, policy_vcs_ufp_reject); ++} ++ ++static void fusb_state_swap_msg_process(struct fusb30x_chip *chip, u32 evt) ++{ ++ if (evt & EVENT_RX) { ++ if (PACKET_IS_CONTROL_MSG(chip->rec_head, CMT_PR_SWAP)) { ++ set_state(chip, policy_src_prs_evaluate); ++ } else if (PACKET_IS_CONTROL_MSG(chip->rec_head, ++ CMT_VCONN_SWAP)) { ++ if (chip->notify.data_role) ++ set_state(chip, chip->conn_state); ++ else ++ set_state(chip, policy_vcs_ufp_evaluate_swap); ++ } else if (PACKET_IS_CONTROL_MSG(chip->rec_head, ++ CMT_DR_SWAP)) { ++ if (chip->notify.data_role) ++ set_state(chip, policy_drs_dfp_evaluate); ++ else ++ set_state(chip, policy_drs_ufp_evaluate); ++ } ++ } ++} ++ ++#define VDM_IS_ACTIVE(chip) \ ++ (chip->notify.data_role && chip->vdm_state < VDM_STATE_READY) ++ ++static void fusb_state_src_ready(struct fusb30x_chip *chip, u32 evt) ++{ ++ if (evt & EVENT_RX) { ++ if (PACKET_IS_DATA_MSG(chip->rec_head, DMT_VENDERDEFINED)) { ++ process_vdm_msg(chip); ++ chip->work_continue |= EVENT_WORK_CONTINUE; ++ chip->timer_state = T_DISABLED; ++ } else if (!VDM_IS_ACTIVE(chip)) { ++ fusb_state_swap_msg_process(chip, evt); ++ } ++ } ++ ++ if (!chip->partner_cap[0]) ++ set_state(chip, policy_src_get_sink_caps); ++ else if (VDM_IS_ACTIVE(chip)) ++ auto_vdm_machine(chip, evt); ++} ++ ++static void fusb_state_prs_evaluate(struct fusb30x_chip *chip, u32 evt) ++{ ++ if (chip->role == ROLE_MODE_DRP) ++ set_state(chip, policy_src_prs_accept); ++ else ++ set_state(chip, policy_src_prs_reject); ++} ++ ++static void fusb_state_send_simple_msg(struct fusb30x_chip *chip, u32 evt, ++ int cmd, int is_DMT, ++ enum connection_state state_success, ++ enum connection_state state_failed) ++{ ++ u32 tmp; ++ ++ switch (chip->sub_state) { ++ case 0: ++ set_mesg(chip, cmd, is_DMT); ++ chip->tx_state = tx_idle; ++ chip->sub_state++; ++ /* fallthrough */ ++ case 1: ++ tmp = policy_send_data(chip); ++ if (tmp == tx_success) ++ set_state(chip, state_success); ++ else if (tmp == tx_failed) ++ set_state(chip, state_failed); ++ } ++} ++ ++static void fusb_state_prs_reject(struct fusb30x_chip *chip, u32 evt) ++{ ++ fusb_state_send_simple_msg(chip, evt, CMT_REJECT, CONTROLMESSAGE, ++ (chip->notify.power_role) ? ++ policy_src_ready : policy_snk_ready, ++ (chip->notify.power_role) ? ++ policy_src_send_softrst : ++ policy_snk_send_softrst); ++} ++ ++static void fusb_state_prs_accept(struct fusb30x_chip *chip, u32 evt) ++{ ++ fusb_state_send_simple_msg(chip, evt, CMT_ACCEPT, CONTROLMESSAGE, ++ (chip->notify.power_role) ? ++ policy_src_prs_transition_to_off : ++ policy_snk_prs_transition_to_off, ++ (chip->notify.power_role) ? ++ policy_src_send_softrst : ++ policy_snk_send_softrst); ++} ++ ++static void fusb_state_vcs_ufp_accept(struct fusb30x_chip *chip, u32 evt) ++{ ++ fusb_state_send_simple_msg(chip, evt, CMT_ACCEPT, CONTROLMESSAGE, ++ (chip->vconn_enabled) ? ++ policy_vcs_ufp_wait_for_dfp_vconn : ++ policy_vcs_ufp_turn_on_vconn, ++ (chip->notify.power_role) ? ++ policy_src_send_softrst : ++ policy_snk_send_softrst); ++} ++ ++static void fusb_state_vcs_set_vconn(struct fusb30x_chip *chip, ++ u32 evt, bool on) ++{ ++ if (on) { ++ tcpm_set_vconn(chip, 1); ++ set_state(chip, chip->notify.data_role ? ++ policy_vcs_dfp_send_ps_rdy : ++ policy_vcs_ufp_send_ps_rdy); ++ } else { ++ tcpm_set_vconn(chip, 0); ++ if (chip->notify.power_role) ++ set_state(chip, policy_src_ready); ++ else ++ set_state(chip, policy_snk_ready); ++ } ++} ++ ++static void fusb_state_vcs_send_ps_rdy(struct fusb30x_chip *chip, u32 evt) ++{ ++ fusb_state_send_simple_msg(chip, evt, CMT_PS_RDY, CONTROLMESSAGE, ++ (chip->notify.power_role) ? ++ policy_src_ready : policy_snk_ready, ++ (chip->notify.power_role) ? ++ policy_src_send_softrst : ++ policy_snk_send_softrst); ++} ++ ++static void fusb_state_vcs_wait_for_vconn(struct fusb30x_chip *chip, ++ u32 evt) ++{ ++ switch (chip->sub_state) { ++ case 0: ++ chip->timer_state = T_PD_VCONN_SRC_ON; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++ chip->sub_state++; ++ /* fallthrough */ ++ case 1: ++ if (evt & EVENT_RX) { ++ if (PACKET_IS_CONTROL_MSG(chip->rec_head, CMT_PS_RDY)) ++ set_state(chip, chip->notify.data_role ? ++ policy_vcs_dfp_turn_off_vconn : ++ policy_vcs_ufp_turn_off_vconn); ++ } else if (evt & EVENT_TIMER_STATE) { ++ if (chip->notify.power_role) ++ set_state(chip, policy_src_send_hardrst); ++ else ++ set_state(chip, policy_snk_send_hardrst); ++ } ++ } ++} ++ ++static void fusb_state_src_prs_transition_to_off(struct fusb30x_chip *chip, ++ u32 evt) ++{ ++ switch (chip->sub_state) { ++ case 0: ++ chip->timer_state = T_SRC_TRANSITION; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++ chip->sub_state++; ++ break; ++ case 1: ++ if (evt & EVENT_TIMER_STATE) { ++ platform_set_vbus_lvl_enable(chip, 0, 0); ++ chip->notify.power_role = POWER_ROLE_SINK; ++ tcpm_set_msg_header(chip); ++ if (chip->role == ROLE_MODE_DRP) ++ set_state(chip, policy_src_prs_assert_rd); ++ else ++ set_state(chip, policy_src_prs_source_off); ++ } ++ } ++} ++ ++static void fusb_state_src_prs_assert_rd(struct fusb30x_chip *chip, u32 evt) ++{ ++ tcpm_set_cc_pull_mode(chip, CC_PULL_DOWN); ++ set_state(chip, policy_src_prs_source_off); ++} ++ ++static void fusb_state_src_prs_source_off(struct fusb30x_chip *chip, u32 evt) ++{ ++ u32 tmp; ++ ++ switch (chip->sub_state) { ++ case 0: ++ set_mesg(chip, CMT_PS_RDY, CONTROLMESSAGE); ++ chip->tx_state = tx_idle; ++ chip->sub_state++; ++ /* fallthrough */ ++ case 1: ++ tmp = policy_send_data(chip); ++ if (tmp == tx_success) { ++ chip->timer_state = T_PD_SOURCE_ON; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++ chip->sub_state++; ++ } else if (tmp == tx_failed) { ++ chip->notify.power_role = POWER_ROLE_SOURCE; ++ tcpm_set_msg_header(chip); ++ set_state(chip, policy_src_send_hardrst); ++ } ++ if (chip->sub_state != 3) ++ break; ++ case 2: ++ if (evt & EVENT_RX) { ++ if (PACKET_IS_CONTROL_MSG(chip->rec_head, ++ CMT_PS_RDY)) { ++ chip->timer_state = T_DISABLED; ++ /* snk startup */ ++ chip->notify.is_pd_connected = false; ++ chip->cc_state |= CC_STATE_TOGSS_IS_UFP; ++ tcpm_set_polarity(chip, chip->cc_polarity); ++ tcpm_set_rx_enable(chip, 1); ++ set_state(chip, policy_snk_discovery); ++ } else { ++ dev_dbg(chip->dev, ++ "rec careless msg: head %x\n", ++ chip->rec_head); ++ } ++ } else if (evt & EVENT_TIMER_STATE) { ++ chip->notify.power_role = POWER_ROLE_SOURCE; ++ tcpm_set_msg_header(chip); ++ set_state(chip, policy_src_send_hardrst); ++ } ++ } ++} ++ ++static void fusb_state_drs_evaluate(struct fusb30x_chip *chip, u32 evt) ++{ ++ if (chip->pd_cap_info.data_role_swap) ++ /* ++ * TODO: ++ * NOW REJECT swap when the port is DFP ++ * since we should work together with USB part ++ */ ++ set_state(chip, chip->notify.data_role ? ++ policy_drs_dfp_reject : policy_drs_ufp_accept); ++ else ++ set_state(chip, chip->notify.data_role ? ++ policy_drs_dfp_reject : policy_drs_ufp_reject); ++} ++ ++static void fusb_state_drs_send_accept(struct fusb30x_chip *chip, u32 evt) ++{ ++ fusb_state_send_simple_msg(chip, evt, CMT_ACCEPT, CONTROLMESSAGE, ++ chip->notify.power_role ? ++ policy_drs_dfp_change : ++ policy_drs_ufp_change, ++ error_recovery); ++} ++ ++static void fusb_state_drs_role_change(struct fusb30x_chip *chip, u32 evt) ++{ ++ chip->notify.data_role = chip->notify.data_role ? ++ DATA_ROLE_UFP : DATA_ROLE_DFP; ++ tcpm_set_msg_header(chip); ++ set_state(chip, chip->notify.power_role ? policy_src_ready : ++ policy_snk_ready); ++} ++ ++static void fusb_state_src_get_sink_cap(struct fusb30x_chip *chip, u32 evt) ++{ ++ u32 tmp; ++ ++ switch (chip->sub_state) { ++ case 0: ++ set_mesg(chip, CMT_GETSINKCAP, CONTROLMESSAGE); ++ chip->tx_state = tx_idle; ++ chip->sub_state++; ++ /* without break */ ++ case 1: ++ tmp = policy_send_data(chip); ++ if (tmp == tx_success) { ++ chip->timer_state = T_SENDER_RESPONSE; ++ chip->sub_state++; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++ } else if (tmp == tx_failed) { ++ set_state(chip, policy_src_send_softrst); ++ } ++ ++ if (!(evt & FLAG_EVENT)) ++ break; ++ default: ++ if (evt & EVENT_RX) { ++ if (PACKET_IS_DATA_MSG(chip->rec_head, ++ DMT_SINKCAPABILITIES)) { ++ for (tmp = 0; ++ tmp < PD_HEADER_CNT(chip->rec_head); ++ tmp++) { ++ chip->partner_cap[tmp] = ++ chip->rec_load[tmp]; ++ } ++ set_state(chip, policy_src_ready); ++ } else { ++ chip->partner_cap[0] = 0xffffffff; ++ set_state(chip, policy_src_ready); ++ } ++ } else if (evt & EVENT_TIMER_STATE) { ++ dev_warn(chip->dev, "Get sink cap time out\n"); ++ chip->partner_cap[0] = 0xffffffff; ++ set_state(chip, policy_src_ready); ++ } ++ } ++} ++ ++static void fusb_state_src_send_hardreset(struct fusb30x_chip *chip, u32 evt) ++{ ++ u32 tmp; ++ ++ switch (chip->sub_state) { ++ case 0: ++ chip->tx_state = tx_idle; ++ chip->sub_state++; ++ /* without break */ ++ default: ++ tmp = policy_send_hardrst(chip, evt); ++ if (tmp == tx_success) { ++ chip->hardrst_count++; ++ set_state(chip, policy_src_transition_default); ++ } else if (tmp == tx_failed) { ++ /* can't reach here */ ++ set_state(chip, error_recovery); ++ } ++ break; ++ } ++} ++ ++static void fusb_state_src_softreset(struct fusb30x_chip *chip) ++{ ++ u32 tmp; ++ ++ switch (chip->sub_state) { ++ case 0: ++ set_mesg(chip, CMT_ACCEPT, CONTROLMESSAGE); ++ chip->tx_state = tx_idle; ++ chip->sub_state++; ++ /* without break */ ++ default: ++ tmp = policy_send_data(chip); ++ if (tmp == tx_success) { ++ fusb_soft_reset_parameter(chip); ++ set_state(chip, policy_src_send_caps); ++ } else if (tmp == tx_failed) { ++ set_state(chip, policy_src_send_hardrst); ++ } ++ break; ++ } ++} ++ ++static void fusb_state_src_send_softreset(struct fusb30x_chip *chip, u32 evt) ++{ ++ u32 tmp; ++ ++ switch (chip->sub_state) { ++ case 0: ++ set_mesg(chip, CMT_SOFTRESET, CONTROLMESSAGE); ++ chip->tx_state = tx_idle; ++ chip->sub_state++; ++ /* without break */ ++ case 1: ++ tmp = policy_send_data(chip); ++ if (tmp == tx_success) { ++ chip->timer_state = T_SENDER_RESPONSE; ++ chip->sub_state++; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++ } else if (tmp == tx_failed) { ++ set_state(chip, policy_src_send_hardrst); ++ } ++ ++ if (!(evt & FLAG_EVENT)) ++ break; ++ default: ++ if (evt & EVENT_RX) { ++ if (PACKET_IS_CONTROL_MSG(chip->rec_head, CMT_ACCEPT)) { ++ fusb_soft_reset_parameter(chip); ++ set_state(chip, policy_src_send_caps); ++ } ++ } else if (evt & EVENT_TIMER_STATE) { ++ set_state(chip, policy_src_send_hardrst); ++ } ++ break; ++ } ++} ++ ++static void fusb_state_snk_startup(struct fusb30x_chip *chip, u32 evt) ++{ ++ chip->notify.is_pd_connected = false; ++ fusb_soft_reset_parameter(chip); ++ ++ memset(chip->partner_cap, 0, sizeof(chip->partner_cap)); ++ ++ tcpm_set_msg_header(chip); ++ tcpm_set_polarity(chip, chip->cc_polarity); ++ tcpm_set_rx_enable(chip, 1); ++ set_state(chip, policy_snk_discovery); ++ platform_fusb_notify(chip); ++} ++ ++static void fusb_state_snk_discovery(struct fusb30x_chip *chip, u32 evt) ++{ ++ set_state(chip, policy_snk_wait_caps); ++ chip->timer_state = T_TYPEC_SINK_WAIT_CAP; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++} ++ ++static void fusb_state_snk_wait_caps(struct fusb30x_chip *chip, u32 evt) ++{ ++ if (evt & EVENT_RX) { ++ if (PACKET_IS_DATA_MSG(chip->rec_head, ++ DMT_SOURCECAPABILITIES)) { ++ chip->is_pd_support = true; ++ chip->timer_mux = T_DISABLED; ++ set_state(chip, policy_snk_evaluate_caps); ++ } ++ } else if (evt & EVENT_TIMER_STATE) { ++ if (chip->hardrst_count <= N_HARDRESET_COUNT) { ++ if (chip->vbus_begin) { ++ chip->vbus_begin = false; ++ set_state(chip, policy_snk_send_softrst); ++ } else { ++ set_state(chip, policy_snk_send_hardrst); ++ } ++ } else { ++ if (chip->is_pd_support) ++ set_state(chip, error_recovery); ++ else ++ set_state(chip, disabled); ++ } ++ } else if ((evt & EVENT_TIMER_MUX) && ++ (chip->hardrst_count > N_HARDRESET_COUNT)) { ++ if (chip->is_pd_support) ++ set_state(chip, error_recovery); ++ else ++ set_state(chip, disabled); ++ } ++} ++ ++static void fusb_state_snk_evaluate_caps(struct fusb30x_chip *chip, u32 evt) ++{ ++ u32 tmp; ++ ++ chip->hardrst_count = 0; ++ chip->pos_power = 0; ++ ++ for (tmp = 0; tmp < PD_HEADER_CNT(chip->rec_head); tmp++) { ++ switch (CAP_POWER_TYPE(chip->rec_load[tmp])) { ++ case 0: ++ /* Fixed Supply */ ++ if (CAP_FPDO_VOLTAGE(chip->rec_load[tmp]) <= 100) ++ chip->pos_power = tmp + 1; ++ break; ++ case 1: ++ /* Battery */ ++ if (CAP_VPDO_VOLTAGE(chip->rec_load[tmp]) <= 100) ++ chip->pos_power = tmp + 1; ++ break; ++ default: ++ /* not meet battery caps */ ++ break; ++ } ++ } ++ fusb302_set_pos_power_by_charge_ic(chip); ++ ++ if ((!chip->pos_power) || (chip->pos_power > 7)) { ++ chip->pos_power = 0; ++ set_state(chip, policy_snk_wait_caps); ++ } else { ++ set_state(chip, policy_snk_select_cap); ++ } ++} ++ ++static void fusb_state_snk_select_cap(struct fusb30x_chip *chip, u32 evt) ++{ ++ u32 tmp; ++ ++ switch (chip->sub_state) { ++ case 0: ++ set_mesg(chip, DMT_REQUEST, DATAMESSAGE); ++ chip->sub_state = 1; ++ chip->tx_state = tx_idle; ++ /* without break */ ++ case 1: ++ tmp = policy_send_data(chip); ++ ++ if (tmp == tx_success) { ++ chip->timer_state = T_SENDER_RESPONSE; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++ chip->sub_state++; ++ } else if (tmp == tx_failed) { ++ set_state(chip, policy_snk_discovery); ++ break; ++ } ++ ++ if (!(evt & FLAG_EVENT)) ++ break; ++ default: ++ if (evt & EVENT_RX) { ++ if (!PD_HEADER_CNT(chip->rec_head)) { ++ switch (PD_HEADER_TYPE(chip->rec_head)) { ++ case CMT_ACCEPT: ++ set_state(chip, ++ policy_snk_transition_sink); ++ chip->timer_state = T_PS_TRANSITION; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++ break; ++ case CMT_WAIT: ++ case CMT_REJECT: ++ if (chip->notify.is_pd_connected) { ++ dev_info(chip->dev, ++ "PD connected as UFP, fetching 5V\n"); ++ set_state(chip, ++ policy_snk_ready); ++ } else { ++ set_state(chip, ++ policy_snk_wait_caps); ++ /* ++ * make sure don't send ++ * hard reset to prevent ++ * infinite loop ++ */ ++ chip->hardrst_count = ++ N_HARDRESET_COUNT + 1; ++ } ++ break; ++ default: ++ break; ++ } ++ } ++ } else if (evt & EVENT_TIMER_STATE) { ++ set_state(chip, policy_snk_send_hardrst); ++ } ++ break; ++ } ++} ++ ++static void fusb_state_snk_transition_sink(struct fusb30x_chip *chip, u32 evt) ++{ ++ if (evt & EVENT_RX) { ++ if (PACKET_IS_CONTROL_MSG(chip->rec_head, CMT_PS_RDY)) { ++ chip->notify.is_pd_connected = true; ++ dev_info(chip->dev, ++ "PD connected as UFP, fetching 5V\n"); ++ set_state(chip, policy_snk_ready); ++ } else if (PACKET_IS_DATA_MSG(chip->rec_head, ++ DMT_SOURCECAPABILITIES)) { ++ set_state(chip, policy_snk_evaluate_caps); ++ } ++ } else if (evt & EVENT_TIMER_STATE) { ++ set_state(chip, policy_snk_send_hardrst); ++ } ++} ++ ++static void fusb_state_snk_transition_default(struct fusb30x_chip *chip, ++ u32 evt) ++{ ++ switch (chip->sub_state) { ++ case 0: ++ chip->notify.is_pd_connected = false; ++ chip->timer_mux = T_NO_RESPONSE; ++ fusb_timer_start(&chip->timer_mux_machine, ++ chip->timer_mux); ++ chip->timer_state = T_PS_HARD_RESET_MAX + T_SAFE_0V; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++ if (chip->notify.data_role) ++ tcpm_set_msg_header(chip); ++ ++ chip->sub_state++; ++ /* fallthrough */ ++ case 1: ++ if (!tcpm_check_vbus(chip)) { ++ chip->sub_state++; ++ chip->timer_state = T_SRC_RECOVER_MAX + T_SRC_TURN_ON; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++ } else if (evt & EVENT_TIMER_STATE) { ++ set_state(chip, policy_snk_startup); ++ } ++ break; ++ default: ++ if (tcpm_check_vbus(chip)) { ++ chip->timer_state = T_DISABLED; ++ set_state(chip, policy_snk_startup); ++ } else if (evt & EVENT_TIMER_STATE) { ++ set_state(chip, policy_snk_startup); ++ } ++ break; ++ } ++} ++ ++static void fusb_state_snk_ready(struct fusb30x_chip *chip, u32 evt) ++{ ++ if (evt & EVENT_RX) { ++ if (PACKET_IS_DATA_MSG(chip->rec_head, DMT_VENDERDEFINED)) { ++ process_vdm_msg(chip); ++ chip->work_continue |= EVENT_WORK_CONTINUE; ++ chip->timer_state = T_DISABLED; ++ } else if (!VDM_IS_ACTIVE(chip)) { ++ fusb_state_swap_msg_process(chip, evt); ++ } ++ } ++ ++ if (VDM_IS_ACTIVE(chip)) ++ auto_vdm_machine(chip, evt); ++ ++ fusb_state_swap_msg_process(chip, evt); ++ platform_fusb_notify(chip); ++} ++ ++static void fusb_state_snk_send_hardreset(struct fusb30x_chip *chip, u32 evt) ++{ ++ u32 tmp; ++ ++ switch (chip->sub_state) { ++ case 0: ++ chip->tx_state = tx_idle; ++ chip->sub_state++; ++ default: ++ tmp = policy_send_hardrst(chip, evt); ++ if (tmp == tx_success) { ++ chip->hardrst_count++; ++ set_state(chip, policy_snk_transition_default); ++ } else if (tmp == tx_failed) { ++ set_state(chip, error_recovery); ++ } ++ break; ++ } ++} ++ ++static void fusb_state_send_swap(struct fusb30x_chip *chip, u32 evt, int cmd) ++{ ++ u32 tmp; ++ ++ switch (chip->sub_state) { ++ case 0: ++ set_mesg(chip, cmd, CONTROLMESSAGE); ++ chip->sub_state = 1; ++ chip->tx_state = tx_idle; ++ /* fallthrough */ ++ case 1: ++ tmp = policy_send_data(chip); ++ ++ if (tmp == tx_success) { ++ chip->timer_state = T_SENDER_RESPONSE; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++ chip->sub_state++; ++ } else if (tmp == tx_failed) { ++ if (cmd == CMT_DR_SWAP) { ++ set_state(chip, error_recovery); ++ return; ++ } ++ ++ if (chip->notify.power_role) ++ set_state(chip, policy_src_send_softrst); ++ else ++ set_state(chip, policy_snk_send_softrst); ++ } ++ break; ++ case 2: ++ if (evt & EVENT_RX) { ++ if (PACKET_IS_CONTROL_MSG(chip->rec_head, ++ CMT_ACCEPT)) { ++ chip->timer_state = T_DISABLED; ++ if (cmd == CMT_VCONN_SWAP) { ++ set_state(chip, chip->vconn_enabled ? ++ policy_vcs_dfp_wait_for_ufp_vconn : ++ policy_vcs_dfp_turn_on_vconn); ++ } else if (cmd == CMT_PR_SWAP) { ++ if (chip->notify.power_role) ++ set_state(chip, policy_src_prs_transition_to_off); ++ else ++ set_state(chip, policy_snk_prs_transition_to_off); ++ chip->notify.power_role = POWER_ROLE_SOURCE; ++ tcpm_set_msg_header(chip); ++ } else if (cmd == CMT_DR_SWAP) { ++ set_state(chip, chip->notify.data_role ? ++ policy_drs_dfp_change : ++ policy_drs_ufp_change); ++ } ++ } else if (PACKET_IS_CONTROL_MSG(chip->rec_head, ++ CMT_REJECT) || ++ PACKET_IS_CONTROL_MSG(chip->rec_head, ++ CMT_WAIT)) { ++ chip->timer_state = T_DISABLED; ++ if (chip->notify.power_role) ++ set_state(chip, policy_src_ready); ++ else ++ set_state(chip, policy_snk_ready); ++ } ++ } else if (evt & EVENT_TIMER_STATE) { ++ if (chip->notify.power_role) ++ set_state(chip, policy_src_ready); ++ else ++ set_state(chip, policy_snk_ready); ++ } ++ } ++} ++ ++static void fusb_state_snk_prs_transition_to_off(struct fusb30x_chip *chip, ++ u32 evt) ++{ ++ switch (chip->sub_state) { ++ case 0: ++ chip->timer_state = T_PD_SOURCE_OFF; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++ chip->sub_state++; ++ /* fallthrough */ ++ case 1: ++ if (evt & EVENT_RX) { ++ if (PACKET_IS_CONTROL_MSG(chip->rec_head, ++ CMT_PS_RDY)) { ++ if (chip->role == ROLE_MODE_DRP) ++ set_state(chip, ++ policy_snk_prs_assert_rp); ++ else ++ set_state(chip, ++ policy_snk_prs_source_on); ++ } else { ++ dev_dbg(chip->dev, ++ "rec careless msg: head %x\n", ++ chip->rec_head); ++ } ++ } else if (evt & EVENT_TIMER_STATE) { ++ chip->notify.power_role = POWER_ROLE_SINK; ++ tcpm_set_msg_header(chip); ++ set_state(chip, policy_snk_send_hardrst); ++ } ++ break; ++ } ++} ++ ++static void fusb_state_snk_prs_assert_rp(struct fusb30x_chip *chip, u32 evt) ++{ ++ tcpm_set_cc_pull_mode(chip, CC_PULL_UP); ++ set_state(chip, policy_snk_prs_source_on); ++} ++ ++static void fusb_state_snk_prs_source_on(struct fusb30x_chip *chip, u32 evt) ++{ ++ u32 tmp; ++ ++ switch (chip->sub_state) { ++ case 0: ++ /* supply power in 50ms */ ++ platform_set_vbus_lvl_enable(chip, 1, 0); ++ chip->sub_state++; ++ chip->work_continue |= EVENT_WORK_CONTINUE; ++ break; ++ case 1: ++ set_mesg(chip, CMT_PS_RDY, CONTROLMESSAGE); ++ chip->tx_state = tx_idle; ++ chip->sub_state++; ++ /* fallthrough */ ++ case 2: ++ tmp = policy_send_data(chip); ++ if (tmp == tx_success) { ++ /* PD spe 6.5.10.2 */ ++ chip->timer_state = T_PD_SWAP_SOURCE_START; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++ chip->sub_state++; ++ } else if (tmp == tx_failed) { ++ chip->notify.power_role = POWER_ROLE_SINK; ++ tcpm_set_msg_header(chip); ++ set_state(chip, policy_snk_send_hardrst); ++ } ++ break; ++ case 3: ++ if (evt & EVENT_TIMER_STATE) { ++ chip->cc_state &= ~CC_STATE_TOGSS_IS_UFP; ++ regmap_update_bits(chip->regmap, FUSB_REG_MASK, ++ MASK_M_COMP_CHNG, 0); ++ set_state(chip, policy_src_send_caps); ++ } ++ break; ++ } ++} ++ ++static void fusb_state_snk_softreset(struct fusb30x_chip *chip) ++{ ++ u32 tmp; ++ ++ switch (chip->sub_state) { ++ case 0: ++ set_mesg(chip, CMT_ACCEPT, CONTROLMESSAGE); ++ chip->tx_state = tx_idle; ++ chip->sub_state++; ++ /* without break */ ++ default: ++ tmp = policy_send_data(chip); ++ if (tmp == tx_success) { ++ fusb_soft_reset_parameter(chip); ++ chip->timer_state = T_TYPEC_SINK_WAIT_CAP; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++ set_state(chip, policy_snk_wait_caps); ++ } else if (tmp == tx_failed) { ++ set_state(chip, policy_snk_send_hardrst); ++ } ++ break; ++ } ++} ++ ++static void fusb_state_snk_send_softreset(struct fusb30x_chip *chip, u32 evt) ++{ ++ u32 tmp; ++ ++ switch (chip->sub_state) { ++ case 0: ++ set_mesg(chip, CMT_SOFTRESET, CONTROLMESSAGE); ++ chip->tx_state = tx_idle; ++ chip->sub_state++; ++ case 1: ++ tmp = policy_send_data(chip); ++ if (tmp == tx_success) { ++ chip->timer_state = T_SENDER_RESPONSE; ++ chip->sub_state++; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++ } else if (tmp == tx_failed) { ++ /* can't reach here */ ++ set_state(chip, policy_snk_send_hardrst); ++ } ++ ++ if (!(evt & FLAG_EVENT)) ++ break; ++ default: ++ if (evt & EVENT_RX) { ++ if ((!PD_HEADER_CNT(chip->rec_head)) && ++ (PD_HEADER_TYPE(chip->rec_head) == CMT_ACCEPT)) { ++ fusb_soft_reset_parameter(chip); ++ chip->timer_state = T_TYPEC_SINK_WAIT_CAP; ++ fusb_timer_start(&chip->timer_state_machine, ++ chip->timer_state); ++ set_state(chip, policy_snk_wait_caps); ++ } ++ } else if (evt & EVENT_TIMER_STATE) { ++ set_state(chip, policy_snk_send_hardrst); ++ } ++ break; ++ } ++} ++ ++static void fusb_try_detach(struct fusb30x_chip *chip) ++{ ++ int cc1, cc2; ++ ++ if ((chip->cc_state & CC_STATE_TOGSS_IS_UFP) && ++ (chip->conn_state != ++ policy_snk_transition_default) && ++ (chip->conn_state != ++ policy_src_prs_source_off) && ++ (chip->conn_state != policy_snk_prs_send_swap) && ++ (chip->conn_state != policy_snk_prs_assert_rp) && ++ (chip->conn_state != policy_snk_prs_source_on) && ++ (chip->conn_state != policy_snk_prs_transition_to_off)) { ++ if (!tcpm_check_vbus(chip)) ++ set_state_unattached(chip); ++ } else if ((chip->conn_state != ++ policy_src_transition_default) && ++ (chip->conn_state != ++ policy_src_prs_source_off) && ++ (chip->conn_state != policy_snk_prs_source_on)) { ++ tcpm_get_cc(chip, &cc1, &cc2); ++ if (chip->cc_state & CC_STATE_TOGSS_CC2) ++ cc1 = cc2; ++ if (cc1 == TYPEC_CC_VOLT_OPEN) ++ set_state_unattached(chip); ++ } else { ++ /* ++ * Detached may occurred at swap operations. So, DON'T ignore ++ * the EVENT_CC during swapping at all, check the connection ++ * after it. ++ */ ++ chip->work_continue |= EVENT_DELAY_CC; ++ } ++} ++ ++static void state_machine_typec(struct fusb30x_chip *chip) ++{ ++ u32 evt = 0; ++ ++ tcpc_alert(chip, &evt); ++ mux_alert(chip, &evt); ++ if (!evt) ++ goto BACK; ++ ++ if (chip->notify.is_cc_connected) ++ if (evt & (EVENT_CC | EVENT_DELAY_CC)) ++ fusb_try_detach(chip); ++ ++ if (evt & EVENT_RX) { ++ tcpm_get_message(chip); ++ if (PACKET_IS_CONTROL_MSG(chip->rec_head, CMT_SOFTRESET)) { ++ if (chip->notify.power_role) ++ set_state(chip, policy_src_softrst); ++ else ++ set_state(chip, policy_snk_softrst); ++ } ++ } ++ ++ if (evt & EVENT_TX) { ++ if (chip->tx_state == tx_success) ++ chip->msg_id++; ++ } ++ switch (chip->conn_state) { ++ case disabled: ++ fusb_state_disabled(chip, evt); ++ break; ++ case error_recovery: ++ set_state_unattached(chip); ++ break; ++ case unattached: ++ fusb_state_unattached(chip, evt); ++ break; ++ case attach_wait_sink: ++ fusb_state_attach_wait_sink(chip, evt); ++ break; ++ case attach_wait_source: ++ fusb_state_attach_wait_source(chip, evt); ++ break; ++ case attached_source: ++ fusb_state_attached_source(chip, evt); ++ break; ++ case attached_sink: ++ fusb_state_attached_sink(chip, evt); ++ break; ++ case attach_try_src: ++ fusb_state_try_attach(chip, evt, ROLE_MODE_DFP); ++ break; ++ case attach_try_snk: ++ fusb_state_try_attach(chip, evt, ROLE_MODE_UFP); ++ break; ++ ++ /* POWER DELIVERY */ ++ case policy_src_startup: ++ fusb_state_src_startup(chip, evt); ++ break; ++ case policy_src_discovery: ++ fusb_state_src_discovery(chip, evt); ++ break; ++ case policy_src_send_caps: ++ fusb_state_src_send_caps(chip, evt); ++ if (chip->conn_state != policy_src_negotiate_cap) ++ break; ++ case policy_src_negotiate_cap: ++ fusb_state_src_negotiate_cap(chip, evt); ++ ++ case policy_src_transition_supply: ++ fusb_state_src_transition_supply(chip, evt); ++ break; ++ case policy_src_cap_response: ++ fusb_state_src_cap_response(chip, evt); ++ break; ++ case policy_src_transition_default: ++ fusb_state_src_transition_default(chip, evt); ++ break; ++ case policy_src_ready: ++ fusb_state_src_ready(chip, evt); ++ break; ++ case policy_src_get_sink_caps: ++ fusb_state_src_get_sink_cap(chip, evt); ++ break; ++ case policy_src_send_hardrst: ++ fusb_state_src_send_hardreset(chip, evt); ++ break; ++ case policy_src_send_softrst: ++ fusb_state_src_send_softreset(chip, evt); ++ break; ++ case policy_src_softrst: ++ fusb_state_src_softreset(chip); ++ break; ++ ++ /* UFP */ ++ case policy_snk_startup: ++ fusb_state_snk_startup(chip, evt); ++ break; ++ case policy_snk_discovery: ++ fusb_state_snk_discovery(chip, evt); ++ break; ++ case policy_snk_wait_caps: ++ fusb_state_snk_wait_caps(chip, evt); ++ break; ++ case policy_snk_evaluate_caps: ++ fusb_state_snk_evaluate_caps(chip, evt); ++ /* without break */ ++ case policy_snk_select_cap: ++ fusb_state_snk_select_cap(chip, evt); ++ break; ++ case policy_snk_transition_sink: ++ fusb_state_snk_transition_sink(chip, evt); ++ break; ++ case policy_snk_transition_default: ++ fusb_state_snk_transition_default(chip, evt); ++ break; ++ case policy_snk_ready: ++ fusb_state_snk_ready(chip, evt); ++ break; ++ case policy_snk_send_hardrst: ++ fusb_state_snk_send_hardreset(chip, evt); ++ break; ++ case policy_snk_send_softrst: ++ fusb_state_snk_send_softreset(chip, evt); ++ break; ++ case policy_snk_softrst: ++ fusb_state_snk_softreset(chip); ++ break; ++ ++ /* ++ * PD Spec 1.0: PR SWAP: chap 8.3.3.6.3.1/2 ++ * VC SWAP: chap 8.3.3.7.1/2 ++ */ ++ case policy_src_prs_evaluate: ++ case policy_snk_prs_evaluate: ++ fusb_state_prs_evaluate(chip, evt); ++ break; ++ case policy_snk_prs_accept: ++ case policy_src_prs_accept: ++ fusb_state_prs_accept(chip, evt); ++ break; ++ case policy_snk_prs_reject: ++ case policy_src_prs_reject: ++ case policy_vcs_ufp_reject: ++ case policy_drs_dfp_reject: ++ case policy_drs_ufp_reject: ++ fusb_state_prs_reject(chip, evt); ++ break; ++ case policy_src_prs_transition_to_off: ++ fusb_state_src_prs_transition_to_off(chip, evt); ++ break; ++ case policy_src_prs_assert_rd: ++ fusb_state_src_prs_assert_rd(chip, evt); ++ break; ++ case policy_src_prs_source_off: ++ fusb_state_src_prs_source_off(chip, evt); ++ break; ++ case policy_snk_prs_send_swap: ++ case policy_src_prs_send_swap: ++ fusb_state_send_swap(chip, evt, CMT_PR_SWAP); ++ break; ++ case policy_snk_prs_transition_to_off: ++ fusb_state_snk_prs_transition_to_off(chip, evt); ++ break; ++ case policy_snk_prs_assert_rp: ++ fusb_state_snk_prs_assert_rp(chip, evt); ++ break; ++ case policy_snk_prs_source_on: ++ fusb_state_snk_prs_source_on(chip, evt); ++ break; ++ case policy_vcs_ufp_evaluate_swap: ++ fusb_state_vcs_ufp_evaluate_swap(chip, evt); ++ break; ++ case policy_vcs_ufp_accept: ++ fusb_state_vcs_ufp_accept(chip, evt); ++ break; ++ case policy_vcs_ufp_wait_for_dfp_vconn: ++ case policy_vcs_dfp_wait_for_ufp_vconn: ++ fusb_state_vcs_wait_for_vconn(chip, evt); ++ break; ++ case policy_vcs_ufp_turn_off_vconn: ++ case policy_vcs_dfp_turn_off_vconn: ++ fusb_state_vcs_set_vconn(chip, evt, false); ++ break; ++ case policy_vcs_ufp_turn_on_vconn: ++ case policy_vcs_dfp_turn_on_vconn: ++ fusb_state_vcs_set_vconn(chip, evt, true); ++ break; ++ case policy_vcs_ufp_send_ps_rdy: ++ case policy_vcs_dfp_send_ps_rdy: ++ fusb_state_vcs_send_ps_rdy(chip, evt); ++ break; ++ case policy_vcs_dfp_send_swap: ++ fusb_state_send_swap(chip, evt, CMT_VCONN_SWAP); ++ break; ++ case policy_drs_ufp_evaluate: ++ case policy_drs_dfp_evaluate: ++ fusb_state_drs_evaluate(chip, evt); ++ break; ++ case policy_drs_dfp_accept: ++ case policy_drs_ufp_accept: ++ fusb_state_drs_send_accept(chip, evt); ++ break; ++ case policy_drs_dfp_change: ++ case policy_drs_ufp_change: ++ fusb_state_drs_role_change(chip, evt); ++ break; ++ case policy_drs_ufp_send_swap: ++ case policy_drs_dfp_send_swap: ++ fusb_state_send_swap(chip, evt, CMT_DR_SWAP); ++ break; ++ ++ default: ++ break; ++ } ++ ++BACK: ++ if (chip->work_continue) { ++ queue_work(chip->fusb30x_wq, &chip->work); ++ return; ++ } ++ ++ if (!platform_get_device_irq_state(chip)) ++ fusb_irq_enable(chip); ++ else ++ queue_work(chip->fusb30x_wq, &chip->work); ++} ++ ++static irqreturn_t cc_interrupt_handler(int irq, void *dev_id) ++{ ++ struct fusb30x_chip *chip = dev_id; ++ ++ queue_work(chip->fusb30x_wq, &chip->work); ++ fusb_irq_disable(chip); ++ return IRQ_HANDLED; ++} ++ ++static int fusb_initialize_gpio(struct fusb30x_chip *chip) ++{ ++ chip->gpio_int = devm_gpiod_get_optional(chip->dev, "int-n", GPIOD_IN); ++ if (IS_ERR(chip->gpio_int)) ++ return PTR_ERR(chip->gpio_int); ++ ++ /* some board support vbus with other ways */ ++ chip->gpio_vbus_5v = devm_gpiod_get_optional(chip->dev, "vbus-5v", ++ GPIOD_OUT_LOW); ++ if (IS_ERR(chip->gpio_vbus_5v)) ++ dev_warn(chip->dev, ++ "Could not get named GPIO for VBus5V!\n"); ++ else ++ gpiod_set_raw_value(chip->gpio_vbus_5v, 0); ++ ++ chip->gpio_vbus_other = devm_gpiod_get_optional(chip->dev, ++ "vbus-other", ++ GPIOD_OUT_LOW); ++ if (IS_ERR(chip->gpio_vbus_other)) ++ dev_warn(chip->dev, ++ "Could not get named GPIO for VBusOther!\n"); ++ else ++ gpiod_set_raw_value(chip->gpio_vbus_other, 0); ++ ++ chip->gpio_discharge = devm_gpiod_get_optional(chip->dev, "discharge", ++ GPIOD_OUT_LOW); ++ if (IS_ERR(chip->gpio_discharge)) { ++ dev_warn(chip->dev, ++ "Could not get named GPIO for discharge!\n"); ++ chip->gpio_discharge = NULL; ++ } ++ ++ return 0; ++} ++ ++static enum hrtimer_restart fusb_timer_handler(struct hrtimer *timer) ++{ ++ int i; ++ ++ for (i = 0; i < fusb30x_port_used; i++) { ++ if (timer == &fusb30x_port_info[i]->timer_state_machine) { ++ if (fusb30x_port_info[i]->timer_state != T_DISABLED) ++ fusb30x_port_info[i]->timer_state = 0; ++ break; ++ } ++ ++ if (timer == &fusb30x_port_info[i]->timer_mux_machine) { ++ if (fusb30x_port_info[i]->timer_mux != T_DISABLED) ++ fusb30x_port_info[i]->timer_mux = 0; ++ break; ++ } ++ } ++ ++ if (i != fusb30x_port_used) ++ queue_work(fusb30x_port_info[i]->fusb30x_wq, ++ &fusb30x_port_info[i]->work); ++ ++ return HRTIMER_NORESTART; ++} ++ ++static void fusb_initialize_timer(struct fusb30x_chip *chip) ++{ ++ hrtimer_init(&chip->timer_state_machine, CLOCK_MONOTONIC, ++ HRTIMER_MODE_REL); ++ chip->timer_state_machine.function = fusb_timer_handler; ++ ++ hrtimer_init(&chip->timer_mux_machine, CLOCK_MONOTONIC, ++ HRTIMER_MODE_REL); ++ chip->timer_mux_machine.function = fusb_timer_handler; ++ ++ chip->timer_state = T_DISABLED; ++ chip->timer_mux = T_DISABLED; ++} ++ ++static void fusb302_work_func(struct work_struct *work) ++{ ++ struct fusb30x_chip *chip; ++ ++ chip = container_of(work, struct fusb30x_chip, work); ++ state_machine_typec(chip); ++} ++ ++static int fusb30x_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct fusb30x_chip *chip; ++ struct PD_CAP_INFO *pd_cap_info; ++ int ret; ++ char *string[2]; ++ ++ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); ++ if (!chip) ++ return -ENOMEM; ++ ++ if (fusb30x_port_used == 0xff) ++ return -1; ++ ++ chip->port_num = fusb30x_port_used++; ++ fusb30x_port_info[chip->port_num] = chip; ++ ++ chip->dev = &client->dev; ++ chip->regmap = devm_regmap_init_i2c(client, &fusb302_regmap_config); ++ if (IS_ERR(chip->regmap)) { ++ dev_err(&client->dev, "Failed to allocate regmap!\n"); ++ return PTR_ERR(chip->regmap); ++ } ++ ++ ret = fusb_initialize_gpio(chip); ++ if (ret) ++ return ret; ++ ++ fusb_initialize_timer(chip); ++ ++ chip->fusb30x_wq = create_workqueue("fusb302_wq"); ++ INIT_WORK(&chip->work, fusb302_work_func); ++ ++ chip->role = ROLE_MODE_NONE; ++ chip->try_role = ROLE_MODE_NONE; ++ if (!of_property_read_string(chip->dev->of_node, "fusb302,role", ++ (const char **)&string[0])) { ++ if (!strcmp(string[0], "ROLE_MODE_DRP")) ++ chip->role = ROLE_MODE_DRP; ++ else if (!strcmp(string[0], "ROLE_MODE_DFP")) ++ chip->role = ROLE_MODE_DFP; ++ else if (!strcmp(string[0], "ROLE_MODE_UFP")) ++ chip->role = ROLE_MODE_UFP; ++ } ++ ++ if (chip->role == ROLE_MODE_NONE) { ++ dev_warn(chip->dev, ++ "Can't get property of role, set role to default DRP\n"); ++ chip->role = ROLE_MODE_DRP; ++ string[0] = "ROLE_MODE_DRP"; ++ } ++ ++ if (!of_property_read_string(chip->dev->of_node, "fusb302,try_role", ++ (const char **)&string[1])) { ++ if (!strcmp(string[1], "ROLE_MODE_DFP")) ++ chip->try_role = ROLE_MODE_DFP; ++ else if (!strcmp(string[1], "ROLE_MODE_UFP")) ++ chip->try_role = ROLE_MODE_UFP; ++ } ++ ++ if (chip->try_role == ROLE_MODE_NONE) ++ string[1] = "ROLE_MODE_NONE"; ++ ++ chip->vconn_supported = true; ++ tcpm_init(chip); ++ tcpm_set_rx_enable(chip, 0); ++ chip->conn_state = unattached; ++ tcpm_set_cc(chip, chip->role); ++ ++ chip->n_caps_used = 1; ++ chip->source_power_supply[0] = 0x64; ++ chip->source_max_current[0] = 0x96; ++ ++ pd_cap_info = &chip->pd_cap_info; ++ pd_cap_info->dual_role_power = 1; ++ pd_cap_info->data_role_swap = 1; ++ ++ pd_cap_info->externally_powered = 1; ++ pd_cap_info->usb_suspend_support = 0; ++ pd_cap_info->usb_communications_cap = 0; ++ pd_cap_info->supply_type = 0; ++ pd_cap_info->peak_current = 0; ++ ++ chip->extcon = devm_extcon_dev_allocate(&client->dev, fusb302_cable); ++ if (IS_ERR(chip->extcon)) { ++ dev_err(&client->dev, "allocat extcon failed\n"); ++ return PTR_ERR(chip->extcon); ++ } ++ ++ ret = devm_extcon_dev_register(&client->dev, chip->extcon); ++ if (ret) { ++ dev_err(&client->dev, "failed to register extcon: %d\n", ++ ret); ++ return ret; ++ } ++ ++ ret = extcon_set_property_capability(chip->extcon, EXTCON_USB, ++ EXTCON_PROP_USB_TYPEC_POLARITY); ++ if (ret) { ++ dev_err(&client->dev, ++ "failed to set USB property capability: %d\n", ++ ret); ++ return ret; ++ } ++ ++ ret = extcon_set_property_capability(chip->extcon, EXTCON_USB_HOST, ++ EXTCON_PROP_USB_TYPEC_POLARITY); ++ if (ret) { ++ dev_err(&client->dev, ++ "failed to set USB_HOST property capability: %d\n", ++ ret); ++ return ret; ++ } ++ ++ ret = extcon_set_property_capability(chip->extcon, EXTCON_DISP_DP, ++ EXTCON_PROP_USB_TYPEC_POLARITY); ++ if (ret) { ++ dev_err(&client->dev, ++ "failed to set DISP_DP property capability: %d\n", ++ ret); ++ return ret; ++ } ++ ++ ret = extcon_set_property_capability(chip->extcon, EXTCON_USB, ++ EXTCON_PROP_USB_SS); ++ if (ret) { ++ dev_err(&client->dev, ++ "failed to set USB USB_SS property capability: %d\n", ++ ret); ++ return ret; ++ } ++ ++ ret = extcon_set_property_capability(chip->extcon, EXTCON_USB_HOST, ++ EXTCON_PROP_USB_SS); ++ if (ret) { ++ dev_err(&client->dev, ++ "failed to set USB_HOST USB_SS property capability: %d\n", ++ ret); ++ return ret; ++ } ++ ++ ret = extcon_set_property_capability(chip->extcon, EXTCON_DISP_DP, ++ EXTCON_PROP_USB_SS); ++ if (ret) { ++ dev_err(&client->dev, ++ "failed to set DISP_DP USB_SS property capability: %d\n", ++ ret); ++ return ret; ++ } ++ ++ ret = extcon_set_property_capability(chip->extcon, EXTCON_CHG_USB_FAST, ++ EXTCON_PROP_USB_TYPEC_POLARITY); ++ if (ret) { ++ dev_err(&client->dev, ++ "failed to set USB_PD property capability: %d\n", ret); ++ return ret; ++ } ++ ++ i2c_set_clientdata(client, chip); ++ ++ spin_lock_init(&chip->irq_lock); ++ chip->enable_irq = 1; ++ ++ chip->gpio_int_irq = gpiod_to_irq(chip->gpio_int); ++ if (chip->gpio_int_irq < 0) { ++ dev_err(&client->dev, ++ "Unable to request IRQ for INT_N GPIO! %d\n", ++ ret); ++ ret = chip->gpio_int_irq; ++ goto IRQ_ERR; ++ } ++ ++ ret = devm_request_threaded_irq(&client->dev, ++ chip->gpio_int_irq, ++ NULL, ++ cc_interrupt_handler, ++ IRQF_ONESHOT | IRQF_TRIGGER_LOW, ++ client->name, ++ chip); ++ if (ret) { ++ dev_err(&client->dev, "irq request failed\n"); ++ goto IRQ_ERR; ++ } ++ ++ dev_info(chip->dev, ++ "port %d probe success with role %s, try_role %s\n", ++ chip->port_num, string[0], string[1]); ++ ++ return 0; ++ ++IRQ_ERR: ++ destroy_workqueue(chip->fusb30x_wq); ++ return ret; ++} ++ ++static int fusb30x_remove(struct i2c_client *client) ++{ ++ struct fusb30x_chip *chip = i2c_get_clientdata(client); ++ ++ destroy_workqueue(chip->fusb30x_wq); ++ return 0; ++} ++ ++static void fusb30x_shutdown(struct i2c_client *client) ++{ ++ struct fusb30x_chip *chip = i2c_get_clientdata(client); ++ ++ if (chip->gpio_vbus_5v) ++ gpiod_set_value(chip->gpio_vbus_5v, 0); ++ if (chip->gpio_discharge) { ++ gpiod_set_value(chip->gpio_discharge, 1); ++ msleep(100); ++ gpiod_set_value(chip->gpio_discharge, 0); ++ } ++} ++ ++static const struct of_device_id fusb30x_dt_match[] = { ++ { .compatible = FUSB30X_I2C_DEVICETREE_NAME }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, fusb30x_dt_match); ++ ++static const struct i2c_device_id fusb30x_i2c_device_id[] = { ++ { FUSB30X_I2C_DRIVER_NAME, 0 }, ++ {} ++}; ++MODULE_DEVICE_TABLE(i2c, fusb30x_i2c_device_id); ++ ++static struct i2c_driver fusb30x_driver = { ++ .driver = { ++ .name = FUSB30X_I2C_DRIVER_NAME, ++ .of_match_table = of_match_ptr(fusb30x_dt_match), ++ }, ++ .probe = fusb30x_probe, ++ .remove = fusb30x_remove, ++ .shutdown = fusb30x_shutdown, ++ .id_table = fusb30x_i2c_device_id, ++}; ++ ++module_i2c_driver(fusb30x_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("zain wang "); ++MODULE_DESCRIPTION("fusb302 typec pd driver"); +diff --git a/drivers/staging/fusb30x/fusb30x.h b/drivers/staging/fusb30x/fusb30x.h +new file mode 100644 +index 000000000..4f5ca64f7 +--- /dev/null ++++ b/drivers/staging/fusb30x/fusb30x.h +@@ -0,0 +1,552 @@ ++/* ++ * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd ++ * Author: Zain Wang ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * Some ideas are from chrome ec and fairchild GPL fusb302 driver. ++ */ ++ ++#ifndef FUSB302_H ++#define FUSB302_H ++ ++#include ++#include ++ ++const char *FUSB_DT_INTERRUPT_INTN = "fsc_interrupt_int_n"; ++#define FUSB_DT_GPIO_INTN "fairchild,int_n" ++#define FUSB_DT_GPIO_VBUS_5V "fairchild,vbus5v" ++#define FUSB_DT_GPIO_VBUS_OTHER "fairchild,vbusOther" ++ ++#define FUSB30X_I2C_DRIVER_NAME "fusb302" ++#define FUSB30X_I2C_DEVICETREE_NAME "fairchild,fusb302" ++ ++/* FUSB300 Register Addresses */ ++#define FUSB_REG_DEVICEID 0x01 ++#define FUSB_REG_SWITCHES0 0x02 ++#define FUSB_REG_SWITCHES1 0x03 ++#define FUSB_REG_MEASURE 0x04 ++#define FUSB_REG_SLICE 0x05 ++#define FUSB_REG_CONTROL0 0x06 ++#define FUSB_REG_CONTROL1 0x07 ++#define FUSB_REG_CONTROL2 0x08 ++#define FUSB_REG_CONTROL3 0x09 ++#define FUSB_REG_MASK 0x0A ++#define FUSB_REG_POWER 0x0B ++#define FUSB_REG_RESET 0x0C ++#define FUSB_REG_OCPREG 0x0D ++#define FUSB_REG_MASKA 0x0E ++#define FUSB_REG_MASKB 0x0F ++#define FUSB_REG_CONTROL4 0x10 ++#define FUSB_REG_STATUS0A 0x3C ++#define FUSB_REG_STATUS1A 0x3D ++#define FUSB_REG_INTERRUPTA 0x3E ++#define FUSB_REG_INTERRUPTB 0x3F ++#define FUSB_REG_STATUS0 0x40 ++#define FUSB_REG_STATUS1 0x41 ++#define FUSB_REG_INTERRUPT 0x42 ++#define FUSB_REG_FIFO 0x43 ++ ++enum connection_state { ++ disabled = 0, ++ error_recovery, ++ unattached, ++ attach_wait_sink, ++ attach_wait_source, ++ attached_source, ++ attached_sink, ++ ++ policy_src_startup, ++ policy_src_send_caps, ++ policy_src_discovery, ++ policy_src_negotiate_cap, ++ policy_src_cap_response, ++ policy_src_transition_supply, ++ policy_src_transition_default, ++ ++ policy_src_ready, ++ policy_src_get_sink_caps, ++ ++ policy_src_send_softrst, ++ policy_src_softrst, ++ policy_src_send_hardrst, ++ ++ policy_snk_startup, ++ policy_snk_discovery, ++ policy_snk_wait_caps, ++ policy_snk_evaluate_caps, ++ policy_snk_select_cap, ++ policy_snk_transition_sink, ++ policy_snk_ready, ++ ++ policy_snk_send_softrst, ++ policy_snk_softrst, ++ policy_snk_send_hardrst, ++ ++ policy_snk_transition_default, ++ ++ /* PR SWAP */ ++ policy_src_prs_evaluate, ++ policy_src_prs_accept, ++ policy_src_prs_transition_to_off, ++ policy_src_prs_source_off, ++ policy_src_prs_assert_rd, ++ policy_src_prs_reject, ++ policy_src_prs_send_swap, ++ ++ policy_snk_prs_evaluate, ++ policy_snk_prs_accept, ++ policy_snk_prs_transition_to_off, ++ policy_snk_prs_source_on, ++ policy_snk_prs_assert_rp, ++ policy_snk_prs_reject, ++ policy_snk_prs_send_swap, ++ ++ /* VC SWAP */ ++ policy_vcs_dfp_send_swap, ++ policy_vcs_dfp_wait_for_ufp_vconn, ++ policy_vcs_dfp_turn_off_vconn, ++ policy_vcs_dfp_turn_on_vconn, ++ policy_vcs_dfp_send_ps_rdy, ++ ++ policy_vcs_ufp_evaluate_swap, ++ policy_vcs_ufp_reject, ++ policy_vcs_ufp_accept, ++ policy_vcs_ufp_wait_for_dfp_vconn, ++ policy_vcs_ufp_turn_off_vconn, ++ policy_vcs_ufp_turn_on_vconn, ++ policy_vcs_ufp_send_ps_rdy, ++ ++ policy_drs_ufp_evaluate, ++ policy_drs_ufp_accept, ++ policy_drs_ufp_reject, ++ policy_drs_ufp_change, ++ policy_drs_ufp_send_swap, ++ ++ policy_drs_dfp_evaluate, ++ policy_drs_dfp_accept, ++ policy_drs_dfp_reject, ++ policy_drs_dfp_change, ++ policy_drs_dfp_send_swap, ++ ++ attach_try_src, ++ attach_try_snk, ++}; ++ ++enum vdm_state { ++ VDM_STATE_DISCOVERY_ID, ++ VDM_STATE_DISCOVERY_SVID, ++ VDM_STATE_DISCOVERY_MODES, ++ VDM_STATE_ENTER_MODE, ++ VDM_STATE_UPDATE_STATUS, ++ VDM_STATE_DP_CONFIG, ++ VDM_STATE_NOTIFY, ++ VDM_STATE_READY, ++ VDM_STATE_ERR, ++}; ++ ++enum tcpm_rp_value { ++ TYPEC_RP_USB = 0, ++ TYPEC_RP_1A5 = 1, ++ TYPEC_RP_3A0 = 2, ++ TYPEC_RP_RESERVED = 3, ++}; ++ ++enum role_mode { ++ ROLE_MODE_NONE, ++ ROLE_MODE_DRP, ++ ROLE_MODE_UFP, ++ ROLE_MODE_DFP, ++ ROLE_MODE_ASS, ++}; ++ ++#define SBF(s, v) ((s) << (v)) ++#define SWITCHES0_PDWN1 SBF(1, 0) ++#define SWITCHES0_PDWN2 SBF(1, 1) ++#define SWITCHES0_MEAS_CC1 SBF(1, 2) ++#define SWITCHES0_MEAS_CC2 SBF(1, 3) ++#define SWITCHES0_VCONN_CC1 SBF(1, 4) ++#define SWITCHES0_VCONN_CC2 SBF(1, 5) ++#define SWITCHES0_PU_EN1 SBF(1, 6) ++#define SWITCHES0_PU_EN2 SBF(1, 7) ++ ++#define SWITCHES1_TXCC1 SBF(1, 0) ++#define SWITCHES1_TXCC2 SBF(1, 1) ++#define SWITCHES1_AUTO_CRC SBF(1, 2) ++#define SWITCHES1_DATAROLE SBF(1, 4) ++#define SWITCHES1_SPECREV SBF(3, 5) ++#define SWITCHES1_POWERROLE SBF(1, 7) ++ ++#define MEASURE_MDAC SBF(0x3f, 0) ++#define MEASURE_VBUS SBF(1, 6) ++ ++#define SLICE_SDAC SBF(0x3f, 0) ++#define SLICE_SDAC_HYS SBF(3, 6) ++ ++#define CONTROL0_TX_START SBF(1, 0) ++#define CONTROL0_AUTO_PRE SBF(1, 1) ++#define CONTROL0_HOST_CUR SBF(3, 2) ++#define CONTROL0_HOST_CUR_USB SBF(1, 2) ++#define CONTROL0_HOST_CUR_1A5 SBF(2, 2) ++#define CONTROL0_HOST_CUR_3A0 SBF(3, 2) ++#define CONTROL0_INT_MASK SBF(1, 5) ++#define CONTROL0_TX_FLUSH SBF(1, 6) ++ ++#define CONTROL1_ENSOP1 SBF(1, 0) ++#define CONTROL1_ENSOP2 SBF(1, 1) ++#define CONTROL1_RX_FLUSH SBF(1, 2) ++#define CONTROL1_BIST_MODE2 SBF(1, 4) ++#define CONTROL1_ENSOP1DB SBF(1, 5) ++#define CONTROL1_ENSOP2DB SBF(1, 6) ++ ++#define CONTROL2_TOGGLE SBF(1, 0) ++#define CONTROL2_MODE SBF(3, 1) ++#define CONTROL2_MODE_NONE 0 ++#define CONTROL2_MODE_DFP SBF(3, 1) ++#define CONTROL2_MODE_UFP SBF(2, 1) ++#define CONTROL2_MODE_DRP SBF(1, 1) ++#define CONTROL2_WAKE_EN SBF(1, 3) ++#define CONTROL2_TOG_RD_ONLY SBF(1, 5) ++#define CONTROL2_TOG_SAVE_PWR1 SBF(1, 6) ++#define CONTROL2_TOG_SAVE_PWR2 SBF(1, 7) ++ ++#define CONTROL3_AUTO_RETRY SBF(1, 0) ++#define CONTROL3_N_RETRIES SBF(3, 1) ++#define CONTROL3_AUTO_SOFTRESET SBF(1, 3) ++#define CONTROL3_AUTO_HARDRESET SBF(1, 4) ++#define CONTROL3_SEND_HARDRESET SBF(1, 6) ++ ++#define MASK_M_BC_LVL SBF(1, 0) ++#define MASK_M_COLLISION SBF(1, 1) ++#define MASK_M_WAKE SBF(1, 2) ++#define MASK_M_ALERT SBF(1, 3) ++#define MASK_M_CRC_CHK SBF(1, 4) ++#define MASK_M_COMP_CHNG SBF(1, 5) ++#define MASK_M_ACTIVITY SBF(1, 6) ++#define MASK_M_VBUSOK SBF(1, 7) ++ ++#define POWER_PWR SBF(0xf, 0) ++ ++#define RESET_SW_RESET SBF(1, 0) ++#define RESET_PD_RESET SBF(1, 1) ++ ++#define MASKA_M_HARDRST SBF(1, 0) ++#define MASKA_M_SOFTRST SBF(1, 1) ++#define MASKA_M_TXSENT SBF(1, 2) ++#define MASKA_M_HARDSENT SBF(1, 3) ++#define MASKA_M_RETRYFAIL SBF(1, 4) ++#define MASKA_M_SOFTFAIL SBF(1, 5) ++#define MASKA_M_TOGDONE SBF(1, 6) ++#define MASKA_M_OCP_TEMP SBF(1, 7) ++ ++#define MASKB_M_GCRCSEND SBF(1, 0) ++ ++#define CONTROL4_TOG_USRC_EXIT SBF(1, 0) ++ ++#define MDAC_1P6V 0x26 ++ ++#define STATUS0A_HARDRST SBF(1, 0) ++#define STATUS0A_SOFTRST SBF(1, 1) ++#define STATUS0A_POWER23 SBF(3, 2) ++#define STATUS0A_RETRYFAIL SBF(1, 4) ++#define STATUS0A_SOFTFAIL SBF(1, 5) ++#define STATUS0A_TOGDONE SBF(1, 6) ++#define STATUS0A_M_OCP_TEMP SBF(1, 7) ++ ++#define STATUS1A_RXSOP SBF(1, 0) ++#define STATUS1A_RXSOP1DB SBF(1, 1) ++#define STATUS1A_RXSOP2DB SBF(1, 2) ++#define STATUS1A_TOGSS SBF(7, 3) ++#define CC_STATE_TOGSS_CC1 SBF(1, 0) ++#define CC_STATE_TOGSS_CC2 SBF(1, 1) ++#define CC_STATE_TOGSS_IS_UFP SBF(1, 2) ++ ++#define INTERRUPTA_HARDRST SBF(1, 0) ++#define INTERRUPTA_SOFTRST SBF(1, 1) ++#define INTERRUPTA_TXSENT SBF(1, 2) ++#define INTERRUPTA_HARDSENT SBF(1, 3) ++#define INTERRUPTA_RETRYFAIL SBF(1, 4) ++#define INTERRUPTA_SOFTFAIL SBF(1, 5) ++#define INTERRUPTA_TOGDONE SBF(1, 6) ++#define INTERRUPTA_OCP_TEMP SBF(1, 7) ++ ++#define INTERRUPTB_GCRCSENT SBF(1, 0) ++ ++#define STATUS0_BC_LVL SBF(3, 0) ++#define STATUS0_WAKE SBF(1, 2) ++#define STATUS0_ALERT SBF(1, 3) ++#define STATUS0_CRC_CHK SBF(1, 4) ++#define STATUS0_COMP SBF(1, 5) ++#define STATUS0_ACTIVITY SBF(1, 6) ++#define STATUS0_VBUSOK SBF(1, 7) ++ ++#define STATUS1_OCP SBF(1, 0) ++#define STATUS1_OVRTEMP SBF(1, 1) ++#define STATUS1_TX_FULL SBF(1, 2) ++#define STATUS1_TX_EMPTY SBF(1, 3) ++#define STATUS1_RX_FULL SBF(1, 4) ++#define STATUS1_RX_EMPTY SBF(1, 5) ++#define STATUS1_RXSOP1 SBF(1, 6) ++#define STATUS1_RXSOP2 SBF(1, 7) ++ ++#define INTERRUPT_BC_LVL SBF(1, 0) ++#define INTERRUPT_COLLISION SBF(1, 1) ++#define INTERRUPT_WAKE SBF(1, 2) ++#define INTERRUPT_ALERT SBF(1, 3) ++#define INTERRUPT_CRC_CHK SBF(1, 4) ++#define INTERRUPT_COMP_CHNG SBF(1, 5) ++#define INTERRUPT_ACTIVITY SBF(1, 6) ++#define INTERRUPT_VBUSOK SBF(1, 7) ++ ++#define FUSB_TKN_TXON 0xa1 ++#define FUSB_TKN_SYNC1 0x12 ++#define FUSB_TKN_SYNC2 0x13 ++#define FUSB_TKN_SYNC3 0x1b ++#define FUSB_TKN_RST1 0x15 ++#define FUSB_TKN_RST2 0x16 ++#define FUSB_TKN_PACKSYM 0x80 ++#define FUSB_TKN_JAMCRC 0xff ++#define FUSB_TKN_EOP 0x14 ++#define FUSB_TKN_TXOFF 0xfe ++ ++/* USB PD Control Message Types */ ++#define CONTROLMESSAGE 0 ++#define CMT_GOODCRC 1 ++#define CMT_GOTOMIN 2 ++#define CMT_ACCEPT 3 ++#define CMT_REJECT 4 ++#define CMT_PING 5 ++#define CMT_PS_RDY 6 ++#define CMT_GETSOURCECAP 7 ++#define CMT_GETSINKCAP 8 ++#define CMT_DR_SWAP 9 ++#define CMT_PR_SWAP 10 ++#define CMT_VCONN_SWAP 11 ++#define CMT_WAIT 12 ++#define CMT_SOFTRESET 13 ++ ++/* USB PD Data Message Types */ ++#define DATAMESSAGE 1 ++#define DMT_SOURCECAPABILITIES 1 ++#define DMT_REQUEST 2 ++#define DMT_BIST 3 ++#define DMT_SINKCAPABILITIES 4 ++#define DMT_VENDERDEFINED 15 ++ ++/* VDM Command Types */ ++#define VDM_DISCOVERY_ID 0X01 ++#define VDM_DISCOVERY_SVIDS 0X02 ++#define VDM_DISCOVERY_MODES 0X03 ++#define VDM_ENTER_MODE 0X04 ++#define VDM_EXIT_MODE 0X05 ++#define VDM_ATTENTION 0X06 ++#define VDM_DP_STATUS_UPDATE 0X10 ++#define VDM_DP_CONFIG 0X11 ++ ++#define VDM_TYPE_INIT 0 ++#define VDM_TYPE_ACK 1 ++#define VDM_TYPE_NACK 2 ++#define VDM_TYPE_BUSY 3 ++ ++/* 200ms at least, 1 cycle about 6ms */ ++#define N_DEBOUNCE_CNT 33 ++#define N_CAPS_COUNT 50 ++#define N_HARDRESET_COUNT 0 ++ ++#define T_NO_RESPONSE 5000 ++#define T_SRC_RECOVER 830 ++#define T_TYPEC_SEND_SOURCECAP 100 ++#define T_SENDER_RESPONSE 30 ++#define T_SRC_TRANSITION 30 ++#define T_TYPEC_SINK_WAIT_CAP 500 ++#define T_PS_TRANSITION 500 ++#define T_BMC_TIMEOUT 5 ++#define T_PS_HARD_RESET_MAX 35 ++#define T_SAFE_0V 650 ++#define T_SRC_TURN_ON 275 ++#define T_SRC_RECOVER_MAX 1000 ++#define T_PD_SOURCE_OFF 920 ++#define T_PD_SOURCE_ON 480 ++#define T_PD_SWAP_SOURCE_START 20 ++#define T_PD_VCONN_SRC_ON 100 ++#define T_PD_TRY_DRP 75 ++ ++#define T_NO_TRIGGER 500 ++#define T_DISABLED 0xffff ++ ++#define PD_HEADER_CNT(header) (((header) >> 12) & 7) ++#define PD_HEADER_TYPE(header) ((header) & 0xF) ++#define PD_HEADER_ID(header) (((header) >> 9) & 7) ++ ++#define VDM_HEADER_TYPE(header) (((header) >> 6) & 3) ++#define VDMHEAD_CMD_TYPE_MASK (3 << 6) ++#define VDMHEAD_CMD_MASK (0x1f << 0) ++#define VDMHEAD_STRUCT_TYPE_MASK BIT(15) ++ ++#define GET_VDMHEAD_CMD_TYPE(head) ((head & VDMHEAD_CMD_TYPE_MASK) >> 6) ++#define GET_VDMHEAD_CMD(head) (head & VDMHEAD_CMD_MASK) ++#define GET_VDMHEAD_STRUCT_TYPE(head) ((head & VDMHEAD_STRUCT_TYPE_MASK) >> 15) ++ ++#define DP_STATUS_MASK 0x000000ff ++#define DP_STATUS_HPD_STATE BIT(7) ++ ++#define GET_DP_STATUS(status) (status & DP_STATUS_MASK) ++#define GET_DP_STATUS_HPD(status) ((status & DP_STATUS_HPD_STATE) >> 7) ++ ++#define VDM_IDHEAD_USBVID_MASK (0xffff << 0) ++#define VDM_IDHEAD_MODALSUPPORT_MASK BIT(26) ++#define VDM_IDHEAD_PRODUCTTYPE (7 << 27) ++#define VDM_IDHEAD_USBDEVICE BIT(30) ++#define VDM_IDHEAD_USBHOST BIT(30) ++ ++#define CAP_POWER_TYPE(PDO) ((PDO >> 30) & 3) ++#define CAP_FPDO_VOLTAGE(PDO) ((PDO >> 10) & 0x3ff) ++#define CAP_VPDO_VOLTAGE(PDO) ((PDO >> 20) & 0x3ff) ++#define CAP_FPDO_CURRENT(PDO) ((PDO >> 0) & 0x3ff) ++#define CAP_VPDO_CURRENT(PDO) ((PDO >> 0) & 0x3ff) ++ ++enum CC_ORIENTATION { ++ NONE, ++ CC1, ++ CC2, ++}; ++ ++enum typec_cc_polarity { ++ TYPEC_POLARITY_CC1, ++ TYPEC_POLARITY_CC2, ++}; ++ ++enum CC_MODE { ++ CC_PULL_UP, ++ CC_PULL_DOWN, ++ CC_PULL_NONE, ++}; ++ ++enum typec_power_role { ++ POWER_ROLE_SINK = 0, ++ POWER_ROLE_SOURCE, ++}; ++ ++enum typec_data_role { ++ DATA_ROLE_UFP = 0, ++ DATA_ROLE_DFP, ++}; ++ ++struct notify_info { ++ enum CC_ORIENTATION orientation; ++ /* 0 UFP : 1 DFP */ ++ enum typec_power_role power_role; ++ enum typec_data_role data_role; ++ ++ bool is_cc_connected; ++ bool is_pd_connected; ++ ++ bool is_enter_mode; ++ int pin_assignment_support; ++ int pin_assignment_def; ++ bool attention; ++ u32 dp_status; ++ u32 dp_caps; ++}; ++ ++enum tx_state { ++ tx_idle, ++ tx_busy, ++ tx_failed, ++ tx_success ++}; ++ ++struct PD_CAP_INFO { ++ u32 peak_current; ++ u32 specification_revision; ++ u32 externally_powered; ++ u32 usb_suspend_support; ++ u32 usb_communications_cap; ++ u32 dual_role_power; ++ u32 data_role_swap; ++ u32 supply_type; ++}; ++ ++struct fusb30x_chip { ++ struct i2c_client *client; ++ struct device *dev; ++ struct regmap *regmap; ++ struct work_struct work; ++ struct workqueue_struct *fusb30x_wq; ++ struct hrtimer timer_state_machine; ++ struct hrtimer timer_mux_machine; ++ struct PD_CAP_INFO pd_cap_info; ++ struct notify_info notify; ++ struct notify_info notify_cmp; ++ struct extcon_dev *extcon; ++ enum connection_state conn_state; ++ struct gpio_desc *gpio_vbus_5v; ++ struct gpio_desc *gpio_vbus_other; ++ struct gpio_desc *gpio_int; ++ struct gpio_desc *gpio_discharge; ++ int timer_state; ++ int timer_mux; ++ int port_num; ++ u32 work_continue; ++ spinlock_t irq_lock; ++ int gpio_int_irq; ++ int enable_irq; ++ ++ /* ++ * --------------------------------- ++ * | role 0x03 << 2, | cc_use 0x03 | ++ * | src 1 << 2, | cc1 1 | ++ * | snk 2 << 2, | cc2 2 | ++ * --------------------------------- ++ */ ++ u8 cc_state; ++ int cc1; ++ int cc2; ++ enum typec_cc_polarity cc_polarity; ++ u8 val_tmp; ++ u8 debounce_cnt; ++ int sub_state; ++ int caps_counter; ++ u32 send_load[7]; ++ u32 rec_load[7]; ++ u16 send_head; ++ u16 rec_head; ++ int msg_id; ++ enum tx_state tx_state; ++ int hardrst_count; ++ u32 source_power_supply[7]; ++ /* 50mv unit */ ++ u32 source_max_current[7]; ++ /* 10ma uint*/ ++ int pos_power; ++ /* ++ * if PartnerCap[0] == 0xffffffff ++ * show Partner Device do not support supply ++ */ ++ u32 partner_cap[7]; ++ int n_caps_used; ++ int vdm_state; ++ int vdm_substate; ++ int vdm_send_state; ++ u16 vdm_svid[12]; ++ int vdm_svid_num; ++ u32 vdm_id; ++ u8 chip_id; ++ bool vconn_enabled; ++ bool is_pd_support; ++ int pd_output_vol; ++ int pd_output_cur; ++ int cc_meas_high; ++ int cc_meas_low; ++ bool vbus_begin; ++ ++ enum role_mode role; ++ bool vconn_supported; ++ bool try_role_complete; ++ enum role_mode try_role; ++}; ++ ++#endif /* FUSB302_H */ ++ diff --git a/sys-kernel/decade-sources/files/patches-5.14/add-maker-friendlyarm.patch b/sys-kernel/decade-sources/files/patches-5.14/add-maker-friendlyarm.patch new file mode 100644 index 0000000..f48596b --- /dev/null +++ b/sys-kernel/decade-sources/files/patches-5.14/add-maker-friendlyarm.patch @@ -0,0 +1,211 @@ +From 2fc2cbaaaf0dcebdeffa6e87bbc9ad843a5470dd Mon Sep 17 00:00:00 2001 +From: hmz007 +Date: Sat, 11 Jan 2020 19:35:03 +0800 +Subject: [PATCH] soc: friendlyelec: Add board info driver + +Change-Id: I122adb4f99c816b5c177f16392fb2df9c10a47be +Signed-off-by: hmz007 +--- + drivers/soc/Kconfig | 1 + + drivers/soc/Makefile | 1 + + drivers/soc/friendlyelec/Kconfig | 11 ++ + drivers/soc/friendlyelec/Makefile | 1 + + drivers/soc/friendlyelec/board.c | 143 ++++++++++++++++++ + 8 files changed, 161 insertions(+) + create mode 100644 drivers/soc/friendlyelec/Kconfig + create mode 100644 drivers/soc/friendlyelec/Makefile + create mode 100644 drivers/soc/friendlyelec/board.c + +diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig +index 833e04a7835c..9ddbd976395d 100644 +--- a/drivers/soc/Kconfig ++++ b/drivers/soc/Kconfig +@@ -21,5 +21,6 @@ source "drivers/soc/ux500/Kconfig" + source "drivers/soc/ux500/Kconfig" + source "drivers/soc/versatile/Kconfig" + source "drivers/soc/xilinx/Kconfig" ++source "drivers/soc/friendlyelec/Kconfig" + + endmenu +diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile +index f678e4d9e..cfc815a7e 100644 +--- a/drivers/soc/Makefile ++++ b/drivers/soc/Makefile +@@ -29,3 +29,4 @@ obj-y += ti/ + obj-$(CONFIG_ARCH_U8500) += ux500/ + obj-$(CONFIG_PLAT_VERSATILE) += versatile/ + obj-y += xilinx/ ++obj-$(CONFIG_VENDOR_FRIENDLYELEC) += friendlyelec/ +diff --git a/drivers/soc/friendlyelec/Kconfig b/drivers/soc/friendlyelec/Kconfig +new file mode 100644 +index 000000000000..9e21c663e6c8 +--- /dev/null ++++ b/drivers/soc/friendlyelec/Kconfig +@@ -0,0 +1,11 @@ ++# ++# Machine drivers ++# ++ ++if ARCH_ROCKCHIP ++ ++config VENDOR_FRIENDLYELEC ++ bool "FriendlyElec board based on RK33XX SoCs" ++ default n ++ ++endif +diff --git a/drivers/soc/friendlyelec/Makefile b/drivers/soc/friendlyelec/Makefile +new file mode 100644 +index 000000000000..870542f05177 +--- /dev/null ++++ b/drivers/soc/friendlyelec/Makefile +@@ -0,0 +1 @@ ++obj-$(CONFIG_VENDOR_FRIENDLYELEC) += board.o +diff --git a/drivers/soc/friendlyelec/board.c b/drivers/soc/friendlyelec/board.c +new file mode 100644 +index 000000000000..886a8e1f7dc0 +--- /dev/null ++++ b/drivers/soc/friendlyelec/board.c +@@ -0,0 +1,143 @@ ++/* ++ * Copyright (C) Guangzhou FriendlyELEC Computer Tech. Co., Ltd. ++ * (http://www.friendlyarm.com) ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, you can access it online at ++ * http://www.gnu.org/licenses/gpl-2.0.html. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define BOARD_MANF "FriendlyELEC Computer Tech. Co., Ltd." ++ ++static const char *board_mach; ++static const char *board_name; ++static u32 board_rev; ++static u32 board_serial_high, board_serial_low; ++ ++static ssize_t board_sys_info_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ char *s = buf; ++ ++ s += sprintf(s, "Hardware\t: %s\n", board_mach); ++ s += sprintf(s, "Revision\t: %04x\n", board_rev); ++ s += sprintf(s, "Serial\t\t: %08x%08x\n", ++ board_serial_high, board_serial_low); ++ s += sprintf(s, "\nModel\t\t: %s\n", board_name); ++ s += sprintf(s, "Manufacturer\t: %s\n", BOARD_MANF); ++ ++ return (s - buf); ++} ++ ++static struct device_attribute board_attr_info = ++ __ATTR(info, S_IRUGO, board_sys_info_show, NULL); ++ ++static int rockchip_cpuinfo_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct nvmem_cell *cell; ++ unsigned char *efuse_buf, buf[16]; ++ size_t len; ++ int i; ++ ++ cell = nvmem_cell_get(dev, "id"); ++ if (IS_ERR(cell)) { ++ dev_err(dev, "failed to get id cell: %ld\n", PTR_ERR(cell)); ++ return PTR_ERR(cell); ++ } ++ ++ efuse_buf = nvmem_cell_read(cell, &len); ++ nvmem_cell_put(cell); ++ ++ if (len != 16) { ++ kfree(efuse_buf); ++ dev_err(dev, "invalid id len: %zu\n", len); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < 8; i++) { ++ buf[i] = efuse_buf[1 + (i << 1)]; ++ buf[i + 8] = efuse_buf[i << 1]; ++ } ++ ++ kfree(efuse_buf); ++ ++ board_serial_low = crc32(0, buf, 8); ++ board_serial_high = crc32(board_serial_low, buf + 8, 8); ++ ++ dev_info(dev, "Serial\t\t: %08x%08x\n", ++ board_serial_high, board_serial_low); ++ ++ return 0; ++} ++ ++static int board_sys_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct device_node *root; ++ ++ root = of_find_node_by_path("/"); ++ ++ of_property_read_u32(np, "hwrev", &board_rev); ++ ++ if (of_property_read_string(np, "machine", &board_mach)) ++ of_property_read_string(root, "compatible", &board_mach); ++ ++ if (of_property_read_string(np, "model", &board_name)) ++ of_property_read_string(root, "model", &board_name); ++ ++ of_node_put(root); ++ ++ rockchip_cpuinfo_probe(pdev); ++ ++ device_create_file(&pdev->dev, &board_attr_info); ++ ++ return 0; ++} ++ ++static const struct of_device_id board_sys_of_match[] = { ++ { .compatible = "friendlyelec,board" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, board_sys_of_match); ++ ++static struct platform_driver board_sys_driver = { ++ .probe = board_sys_probe, ++ .driver = { ++ .name = "friendlyelec-board", ++ .of_match_table = board_sys_of_match, ++ }, ++}; ++ ++static int __init board_sys_init(void) ++{ ++ return platform_driver_register(&board_sys_driver); ++} ++late_initcall(board_sys_init); ++ ++MODULE_AUTHOR("support@friendlyarm.com"); ++MODULE_DESCRIPTION("FriendlyElec NanoPi Series Machine Driver"); ++MODULE_LICENSE("GPL v2"); diff --git a/sys-kernel/decade-sources/files/patches-5.14/board-nanopi-m4v2-dts-add-sound-card.patch b/sys-kernel/decade-sources/files/patches-5.14/board-nanopi-m4v2-dts-add-sound-card.patch new file mode 100644 index 0000000..6f5c6c6 --- /dev/null +++ b/sys-kernel/decade-sources/files/patches-5.14/board-nanopi-m4v2-dts-add-sound-card.patch @@ -0,0 +1,106 @@ +diff -u a/arch/arm64/boot/dts/rockchip/rk3399-nanopi4.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-nanopi4.dtsi +--- a/arch/arm64/boot/dts/rockchip/rk3399-nanopi4.dtsi 2019-10-17 23:47:33.000000000 +0300 ++++ b/arch/arm64/boot/dts/rockchip/rk3399-nanopi4.dtsi 2019-10-27 22:34:55.988303874 +0300 +@@ -105,6 +105,27 @@ + }; + }; + ++ rt5651-sound { ++ compatible = "simple-audio-card"; ++ simple-audio-card,name = "realtek,rt5651-codec"; ++ simple-audio-card,format = "i2s"; ++ simple-audio-card,mclk-fs = <256>; ++ simple-audio-card,widgets = ++ "Microphone", "Mic Jack", ++ "Headphone", "Headphone Jack"; ++ simple-audio-card,routing = ++ "Mic Jack", "micbias1", ++ "IN1P", "Mic Jack", ++ "Headphone Jack", "HPOL", ++ "Headphone Jack", "HPOR"; ++ simple-audio-card,cpu { ++ sound-dai = <&i2s1>; ++ }; ++ simple-audio-card,codec { ++ sound-dai = <&rt5651>; ++ }; ++ }; ++ + sdio_pwrseq: sdio-pwrseq { + compatible = "mmc-pwrseq-simple"; + clocks = <&rk808 1>; +@@ -184,6 +205,10 @@ + status = "okay"; + }; + ++&hdmi_sound { ++ status = "okay"; ++}; ++ + &i2c0 { + clock-frequency = <400000>; + i2c-scl-rising-time-ns = <160>; +@@ -432,6 +457,16 @@ + i2c-scl-rising-time-ns = <150>; + i2c-scl-falling-time-ns = <30>; + status = "okay"; ++ ++ rt5651: rt5651@1a { ++ compatible = "realtek,rt5651"; ++ reg = <0x1a>; ++ clocks = <&cru SCLK_I2S_8CH_OUT>; ++ clock-names = "mclk"; ++ hp-det-gpio = <&gpio4 RK_PC4 GPIO_ACTIVE_LOW>; ++ // spk-con-gpio = <&gpio0 RK_PB3 GPIO_ACTIVE_HIGH>; ++ #sound-dai-cells = <0>; ++ }; + }; + + &i2c2 { +@@ -459,6 +494,16 @@ + status = "okay"; + }; + ++&i2s1 { ++ rockchip,playback-channels = <8>; ++ rockchip,capture-channels = <8>; ++ status = "okay"; ++}; ++ ++&i2s2 { ++ status = "okay"; ++}; ++ + &io_domains { + bt656-supply = <&vcc_1v8>; + audio-supply = <&vcca1v8_codec>; +@@ -724,3 +769,9 @@ + &vopl_mmu { + status = "okay"; + }; ++ ++&spdif { ++ i2c-scl-rising-time-ns = <450>; ++ i2c-scl-falling-time-ns = <15>; ++ status = "okay"; ++}; +diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig +index b43657e6e..fb75c425e 100644 +--- a/sound/soc/rockchip/Kconfig ++++ b/sound/soc/rockchip/Kconfig +@@ -53,6 +53,15 @@ config SND_SOC_ROCKCHIP_RT5645 + Say Y or M here if you want to add support for SoC audio on Rockchip + boards using the RT5645/RT5650 codec, such as Veyron. + ++config SND_SOC_ROCKCHIP_RT5651 ++ tristate "ASoC support for Rockchip boards using a RT5651 codec" ++ depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP ++ select SND_SOC_ROCKCHIP_I2S ++ select SND_SOC_RT5651 ++ help ++ Say Y or M here if you want to add support for SoC audio on Rockchip ++ boards using the RT5651 codec, such as FriendlyARM's Nano{Pi,PC} family. ++ + config SND_SOC_RK3288_HDMI_ANALOG + tristate "ASoC support multiple codecs for Rockchip RK3288 boards" + depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP diff --git a/sys-kernel/decade-sources/files/patches-5.14/board-nanopi-m4v2-dts-ethernet-tweak.patch b/sys-kernel/decade-sources/files/patches-5.14/board-nanopi-m4v2-dts-ethernet-tweak.patch new file mode 100644 index 0000000..bb2cef9 --- /dev/null +++ b/sys-kernel/decade-sources/files/patches-5.14/board-nanopi-m4v2-dts-ethernet-tweak.patch @@ -0,0 +1,15 @@ +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-nanopi-m4v2.dts b/arch/arm64/boot/dts/rockchip/rk3399-nanopi-m4v2.dts +index 60358ab8c..057045ca3 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-nanopi-m4v2.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3399-nanopi-m4v2.dts +@@ -48,6 +48,10 @@ + }; + }; + ++&gmac { ++ rx_delay = <0x16>; ++}; ++ + &vcc3v3_sys { + vin-supply = <&vcc5v0_core>; + }; diff --git a/sys-kernel/decade-sources/files/patches-5.14/board-nanopi-m4v2-dts-fix-stability-issues.patch b/sys-kernel/decade-sources/files/patches-5.14/board-nanopi-m4v2-dts-fix-stability-issues.patch new file mode 100644 index 0000000..d666555 --- /dev/null +++ b/sys-kernel/decade-sources/files/patches-5.14/board-nanopi-m4v2-dts-fix-stability-issues.patch @@ -0,0 +1,29 @@ +By default rk808's buck regulators switch voltage in multiple 100mV jumps. +This seems to be too much for NanoPi M4V2 and makes it unstable. + +Shortening the steps to 50mV (4 multiple of base buck step which is 12.5mV) +makes the NanoPi M4V2 stable. +Tested with multiple hours of running memtester without a single failure. + +Signed-off-by: Piotr Szczepanik + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-nanopi-m4v2.dts b/arch/arm64/boot/dts/rockchip/rk3399-nanopi-m4v2.dts +index 2dcaf497c..094440ce3 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-nanopi-m4v2.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3399-nanopi-m4v2.dts +@@ -64,6 +64,10 @@ &gmac { + rx_delay = <0x16>; + }; + ++&rk808 { ++ max-buck-steps-per-change = <4>; ++}; ++ + &vcc3v3_sys { + vin-supply = <&vcc5v0_core>; + }; +@@ -80,3 +84,4 @@ &vbus_typec { + regulator-always-on; + vin-supply = <&vdd_5v>; + }; ++ diff --git a/sys-kernel/decade-sources/files/board-nanopi-r2s-r8152-mac-from-dt.patch b/sys-kernel/decade-sources/files/patches-5.14/board-nanopi-r2s-r8152-mac-from-dt.patch similarity index 100% rename from sys-kernel/decade-sources/files/board-nanopi-r2s-r8152-mac-from-dt.patch rename to sys-kernel/decade-sources/files/patches-5.14/board-nanopi-r2s-r8152-mac-from-dt.patch diff --git a/sys-kernel/decade-sources/files/patches-5.14/board-roc-rk3399-pc-fix-fusb302-compatible.patch b/sys-kernel/decade-sources/files/patches-5.14/board-roc-rk3399-pc-fix-fusb302-compatible.patch new file mode 100644 index 0000000..5f41dd4 --- /dev/null +++ b/sys-kernel/decade-sources/files/patches-5.14/board-roc-rk3399-pc-fix-fusb302-compatible.patch @@ -0,0 +1,22 @@ +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-roc-pc.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-roc-pc.dtsi +index 9f225e9c3..057c938be 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-roc-pc.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3399-roc-pc.dtsi +@@ -506,7 +506,7 @@ + status = "okay"; + + fusb1: usb-typec@22 { +- compatible = "fcs,fusb302"; ++ compatible = "fairchild,fusb302"; + reg = <0x22>; + interrupt-parent = <&gpio1>; + interrupts = <1 IRQ_TYPE_LEVEL_LOW>; +@@ -523,7 +523,7 @@ + status = "okay"; + + fusb0: usb-typec@22 { +- compatible = "fcs,fusb302"; ++ compatible = "fairchild,fusb302"; + reg = <0x22>; + interrupt-parent = <&gpio1>; + interrupts = <2 IRQ_TYPE_LEVEL_LOW>; diff --git a/sys-kernel/decade-sources/files/board-rockpis-0008-thermal-rockchip-add-tsadc-support-for-rk3308.patch b/sys-kernel/decade-sources/files/patches-5.14/board-rockpis-0008-thermal-rockchip-add-tsadc-support-for-rk3308.patch similarity index 100% rename from sys-kernel/decade-sources/files/board-rockpis-0008-thermal-rockchip-add-tsadc-support-for-rk3308.patch rename to sys-kernel/decade-sources/files/patches-5.14/board-rockpis-0008-thermal-rockchip-add-tsadc-support-for-rk3308.patch diff --git a/sys-kernel/decade-sources/files/patches-5.14/board-rockpis-0016-ASoC-rockchip-add-support-for-rockchip-i2s-tdm-contr.patch b/sys-kernel/decade-sources/files/patches-5.14/board-rockpis-0016-ASoC-rockchip-add-support-for-rockchip-i2s-tdm-contr.patch new file mode 100644 index 0000000..8509312 --- /dev/null +++ b/sys-kernel/decade-sources/files/patches-5.14/board-rockpis-0016-ASoC-rockchip-add-support-for-rockchip-i2s-tdm-contr.patch @@ -0,0 +1,1083 @@ +From e07dd04b6af66325e3dee7a88aa8c6be5a8cb472 Mon Sep 17 00:00:00 2001 +From: ashthespy +Date: Mon, 3 Feb 2020 15:26:23 +0100 +Subject: [PATCH 16/23] ASoC: rockchip: add support for rockchip i2s/tdm + controller + +From 018d1c86c16617eaae1b9fd0d07cc2c70c11006f Mon Sep 17 00:00:00 2001 +From: Sugar Zhang +Date: Sat, 24 Mar 2018 17:01:49 +0800 +Subject: [PATCH] ASoC: rockchip: add support for rockchip i2s/tdm controller + +This patch is add for rockchip i2s/tdm controller. + +Change-Id: I428e311402220ff14441c48e13fa51356ced46e8 +Signed-off-by: Sugar Zhang +--- + .../bindings/sound/rockchip,i2s-tdm.txt | 42 ++ + sound/soc/rockchip/Kconfig | 9 + + sound/soc/rockchip/Makefile | 2 + + sound/soc/rockchip/rockchip_i2s_tdm.c | 700 ++++++++++++++++++ + sound/soc/rockchip/rockchip_i2s_tdm.h | 256 +++++++ + 5 files changed, 1009 insertions(+) + create mode 100644 Documentation/devicetree/bindings/sound/rockchip,i2s-tdm.txt + create mode 100644 sound/soc/rockchip/rockchip_i2s_tdm.c + create mode 100644 sound/soc/rockchip/rockchip_i2s_tdm.h + +diff --git a/Documentation/devicetree/bindings/sound/rockchip,i2s-tdm.txt b/Documentation/devicetree/bindings/sound/rockchip,i2s-tdm.txt +new file mode 100644 +index 000000000000..e26e72e3315f +--- /dev/null ++++ b/Documentation/devicetree/bindings/sound/rockchip,i2s-tdm.txt +@@ -0,0 +1,42 @@ ++* Rockchip I2S/TDM controller ++ ++Required properties: ++ ++- compatible: should be one of the following ++ - "rockchip,rk3308-i2s-tdm": for rk3308 ++- reg: physical base address of the controller and length of memory mapped ++ region. ++- interrupts: should contain the I2S interrupt. ++- dmas: DMA specifiers for tx and rx dma. See the DMA client binding, ++ Documentation/devicetree/bindings/dma/dma.txt ++- dma-names: should include "tx" and "rx". ++- clocks: a list of phandle + clock-specifer pairs, one for each entry in clock-names. ++- clock-names: clock names. ++- rockchip,bclk-fs: configure the bclk fs. ++ ++Example for rk3308 I2S/TDM controller: ++ ++i2s_8ch_0: i2s@ff300000 { ++ compatible = "rockchip,rk3308-i2s-tdm"; ++ reg = <0x0 0xff300000 0x0 0x1000>; ++ interrupts = ; ++ clocks = <&cru SCLK_I2S0_8CH_TX>, <&cru SCLK_I2S0_8CH_RX>, <&cru HCLK_I2S0_8CH>; ++ clock-names = "mclk_tx", "mclk_rx", "hclk"; ++ dmas = <&dmac1 0>, <&dmac1 1>; ++ dma-names = "tx", "rx"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2s_8ch_0_sclktx ++ &i2s_8ch_0_sclkrx ++ &i2s_8ch_0_lrcktx ++ &i2s_8ch_0_lrckrx ++ &i2s_8ch_0_sdi0 ++ &i2s_8ch_0_sdi1 ++ &i2s_8ch_0_sdi2 ++ &i2s_8ch_0_sdi3 ++ &i2s_8ch_0_sdo0 ++ &i2s_8ch_0_sdo1 ++ &i2s_8ch_0_sdo2 ++ &i2s_8ch_0_sdo3 ++ &i2s_8ch_0_mclk>; ++ status = "disabled"; ++}; +diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig +index d610b553ea3b..25e26d85c2d1 100644 +--- a/sound/soc/rockchip/Kconfig ++++ b/sound/soc/rockchip/Kconfig +@@ -16,6 +16,15 @@ config SND_SOC_ROCKCHIP_I2S + Rockchip I2S device. The device supports upto maximum of + 8 channels each for play and record. + ++config SND_SOC_ROCKCHIP_I2S_TDM ++ tristate "Rockchip I2S/TDM Device Driver" ++ depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP ++ select SND_SOC_GENERIC_DMAENGINE_PCM ++ help ++ Say Y or M if you want to add support for I2S/TDM driver for ++ Rockchip I2S/TDM device. The device supports up to maximum of ++ 8 channels each for play and record. ++ + config SND_SOC_ROCKCHIP_PDM + tristate "Rockchip PDM Controller Driver" + depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP +diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile +index 65e814d46006..b10f5e7b136d 100644 +--- a/sound/soc/rockchip/Makefile ++++ b/sound/soc/rockchip/Makefile +@@ -1,11 +1,13 @@ + # SPDX-License-Identifier: GPL-2.0 + # ROCKCHIP Platform Support + snd-soc-rockchip-i2s-objs := rockchip_i2s.o ++snd-soc-rockchip-i2s-tdm-objs := rockchip_i2s_tdm.o + snd-soc-rockchip-pcm-objs := rockchip_pcm.o + snd-soc-rockchip-pdm-objs := rockchip_pdm.o + snd-soc-rockchip-spdif-objs := rockchip_spdif.o + + obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o snd-soc-rockchip-pcm.o ++obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S_TDM) += snd-soc-rockchip-i2s-tdm.o + obj-$(CONFIG_SND_SOC_ROCKCHIP_PDM) += snd-soc-rockchip-pdm.o + obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o + +diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c +new file mode 100644 +index 000000000000..39c1b98f9593 +--- /dev/null ++++ b/sound/soc/rockchip/rockchip_i2s_tdm.c +@@ -0,0 +1,700 @@ ++/* sound/soc/rockchip/rockchip_i2s_tdm.c ++ * ++ * ALSA SoC Audio Layer - Rockchip I2S/TDM Controller driver ++ * ++ * Copyright (c) 2018 Rockchip Electronics Co. Ltd. ++ * Author: Sugar Zhang ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "rockchip_i2s_tdm.h" ++ ++#define DRV_NAME "rockchip-i2s-tdm" ++ ++struct rk_i2s_tdm_dev { ++ struct device *dev; ++ struct clk *hclk; ++ struct clk *mclk_tx; ++ struct clk *mclk_rx; ++ struct regmap *regmap; ++ struct snd_dmaengine_dai_dma_data capture_dma_data; ++ struct snd_dmaengine_dai_dma_data playback_dma_data; ++ ++ bool is_master_mode; ++ unsigned int bclk_fs; ++}; ++ ++static int i2s_tdm_runtime_suspend(struct device *dev) ++{ ++ struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); ++ ++ regcache_cache_only(i2s_tdm->regmap, true); ++ if (!IS_ERR(i2s_tdm->mclk_tx)) ++ clk_disable_unprepare(i2s_tdm->mclk_tx); ++ if (!IS_ERR(i2s_tdm->mclk_rx)) ++ clk_disable_unprepare(i2s_tdm->mclk_rx); ++ ++ return 0; ++} ++ ++static int i2s_tdm_runtime_resume(struct device *dev) ++{ ++ struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); ++ int ret; ++ ++ if (!IS_ERR(i2s_tdm->mclk_tx)) ++ clk_prepare_enable(i2s_tdm->mclk_tx); ++ if (!IS_ERR(i2s_tdm->mclk_rx)) ++ clk_prepare_enable(i2s_tdm->mclk_rx); ++ ++ regcache_cache_only(i2s_tdm->regmap, false); ++ regcache_mark_dirty(i2s_tdm->regmap); ++ ++ ret = regcache_sync(i2s_tdm->regmap); ++ if (ret) { ++ if (!IS_ERR(i2s_tdm->mclk_tx)) ++ clk_disable_unprepare(i2s_tdm->mclk_tx); ++ if (!IS_ERR(i2s_tdm->mclk_rx)) ++ clk_disable_unprepare(i2s_tdm->mclk_rx); ++ } ++ ++ return ret; ++} ++ ++static inline struct rk_i2s_tdm_dev *to_info(struct snd_soc_dai *dai) ++{ ++ return snd_soc_dai_get_drvdata(dai); ++} ++ ++static void rockchip_snd_txctrl(struct rk_i2s_tdm_dev *i2s_tdm, int on) ++{ ++ unsigned int val = 0; ++ int retry = 10; ++ ++ if (on) { ++ regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, ++ I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_ENABLE); ++ ++ regmap_update_bits(i2s_tdm->regmap, I2S_XFER, ++ I2S_XFER_TXS_START, ++ I2S_XFER_TXS_START); ++ } else { ++ regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, ++ I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_DISABLE); ++ ++ regmap_update_bits(i2s_tdm->regmap, I2S_XFER, ++ I2S_XFER_TXS_START, ++ I2S_XFER_TXS_STOP); ++ ++ udelay(150); ++ regmap_update_bits(i2s_tdm->regmap, I2S_CLR, ++ I2S_CLR_TXC, ++ I2S_CLR_TXC); ++ ++ regmap_read(i2s_tdm->regmap, I2S_CLR, &val); ++ ++ /* Should wait for clear operation to finish */ ++ while (val) { ++ regmap_read(i2s_tdm->regmap, I2S_CLR, &val); ++ retry--; ++ if (!retry) { ++ dev_warn(i2s_tdm->dev, "fail to clear\n"); ++ break; ++ } ++ } ++ } ++} ++ ++static void rockchip_snd_rxctrl(struct rk_i2s_tdm_dev *i2s_tdm, int on) ++{ ++ unsigned int val = 0; ++ int retry = 10; ++ ++ if (on) { ++ regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, ++ I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_ENABLE); ++ ++ regmap_update_bits(i2s_tdm->regmap, I2S_XFER, ++ I2S_XFER_RXS_START, ++ I2S_XFER_RXS_START); ++ } else { ++ regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, ++ I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_DISABLE); ++ ++ regmap_update_bits(i2s_tdm->regmap, I2S_XFER, ++ I2S_XFER_RXS_START, ++ I2S_XFER_RXS_STOP); ++ ++ udelay(150); ++ regmap_update_bits(i2s_tdm->regmap, I2S_CLR, ++ I2S_CLR_RXC, ++ I2S_CLR_RXC); ++ ++ regmap_read(i2s_tdm->regmap, I2S_CLR, &val); ++ ++ /* Should wait for clear operation to finish */ ++ while (val) { ++ regmap_read(i2s_tdm->regmap, I2S_CLR, &val); ++ retry--; ++ if (!retry) { ++ dev_warn(i2s_tdm->dev, "fail to clear\n"); ++ break; ++ } ++ } ++ } ++} ++ ++static int rockchip_i2s_tdm_set_fmt(struct snd_soc_dai *cpu_dai, ++ unsigned int fmt) ++{ ++ struct rk_i2s_tdm_dev *i2s_tdm = to_info(cpu_dai); ++ unsigned int mask = 0, val = 0; ++ int ret = 0; ++ ++ pm_runtime_get_sync(cpu_dai->dev); ++ mask = I2S_CKR_MSS_MASK; ++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { ++ case SND_SOC_DAIFMT_CBS_CFS: ++ /* Set source clock in Master mode */ ++ val = I2S_CKR_MSS_MASTER; ++ i2s_tdm->is_master_mode = true; ++ break; ++ case SND_SOC_DAIFMT_CBM_CFM: ++ val = I2S_CKR_MSS_SLAVE; ++ i2s_tdm->is_master_mode = false; ++ break; ++ default: ++ ret = -EINVAL; ++ goto err_pm_put; ++ } ++ ++ regmap_update_bits(i2s_tdm->regmap, I2S_CKR, mask, val); ++ ++ mask = I2S_CKR_CKP_MASK; ++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { ++ case SND_SOC_DAIFMT_NB_NF: ++ val = I2S_CKR_CKP_NEG; ++ break; ++ case SND_SOC_DAIFMT_IB_NF: ++ val = I2S_CKR_CKP_POS; ++ break; ++ default: ++ ret = -EINVAL; ++ goto err_pm_put; ++ } ++ ++ regmap_update_bits(i2s_tdm->regmap, I2S_CKR, mask, val); ++ ++ mask = I2S_TXCR_IBM_MASK | I2S_TXCR_TFS_MASK | I2S_TXCR_PBM_MASK; ++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { ++ case SND_SOC_DAIFMT_RIGHT_J: ++ val = I2S_TXCR_IBM_RSJM; ++ break; ++ case SND_SOC_DAIFMT_LEFT_J: ++ val = I2S_TXCR_IBM_LSJM; ++ break; ++ case SND_SOC_DAIFMT_I2S: ++ val = I2S_TXCR_IBM_NORMAL; ++ break; ++ case SND_SOC_DAIFMT_DSP_A: /* PCM no delay mode */ ++ val = I2S_TXCR_TFS_PCM; ++ break; ++ case SND_SOC_DAIFMT_DSP_B: /* PCM delay 1 mode */ ++ val = I2S_TXCR_TFS_PCM | I2S_TXCR_PBM_MODE(1); ++ break; ++ default: ++ ret = -EINVAL; ++ goto err_pm_put; ++ } ++ ++ regmap_update_bits(i2s_tdm->regmap, I2S_TXCR, mask, val); ++ ++ mask = I2S_RXCR_IBM_MASK | I2S_RXCR_TFS_MASK | I2S_RXCR_PBM_MASK; ++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { ++ case SND_SOC_DAIFMT_RIGHT_J: ++ val = I2S_RXCR_IBM_RSJM; ++ break; ++ case SND_SOC_DAIFMT_LEFT_J: ++ val = I2S_RXCR_IBM_LSJM; ++ break; ++ case SND_SOC_DAIFMT_I2S: ++ val = I2S_RXCR_IBM_NORMAL; ++ break; ++ case SND_SOC_DAIFMT_DSP_A: /* PCM no delay mode */ ++ val = I2S_RXCR_TFS_PCM; ++ break; ++ case SND_SOC_DAIFMT_DSP_B: /* PCM delay 1 mode */ ++ val = I2S_RXCR_TFS_PCM | I2S_RXCR_PBM_MODE(1); ++ break; ++ default: ++ ret = -EINVAL; ++ goto err_pm_put; ++ } ++ ++ regmap_update_bits(i2s_tdm->regmap, I2S_RXCR, mask, val); ++ ++err_pm_put: ++ pm_runtime_put(cpu_dai->dev); ++ ++ return ret; ++} ++ ++static int rockchip_i2s_tdm_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) ++{ ++ struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai); ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct clk *mclk; ++ unsigned int val = 0; ++ unsigned int mclk_rate, bclk_rate, div_bclk, div_lrck; ++ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ++ mclk = i2s_tdm->mclk_tx; ++ else ++ mclk = i2s_tdm->mclk_rx; ++ ++ if (i2s_tdm->is_master_mode) { ++ mclk_rate = clk_get_rate(mclk); ++ bclk_rate = i2s_tdm->bclk_fs * params_rate(params); ++ if (!bclk_rate) ++ return -EINVAL; ++ ++ div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate); ++ div_lrck = bclk_rate / params_rate(params); ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ regmap_update_bits(i2s_tdm->regmap, I2S_CLKDIV, ++ I2S_CLKDIV_TXM_MASK, ++ I2S_CLKDIV_TXM(div_bclk)); ++ regmap_update_bits(i2s_tdm->regmap, I2S_CKR, ++ I2S_CKR_TSD_MASK, ++ I2S_CKR_TSD(div_lrck)); ++ } else { ++ regmap_update_bits(i2s_tdm->regmap, I2S_CLKDIV, ++ I2S_CLKDIV_RXM_MASK, ++ I2S_CLKDIV_RXM(div_bclk)); ++ regmap_update_bits(i2s_tdm->regmap, I2S_CKR, ++ I2S_CKR_RSD_MASK, ++ I2S_CKR_RSD(div_lrck)); ++ } ++ } ++ ++ switch (params_format(params)) { ++ case SNDRV_PCM_FORMAT_S8: ++ val |= I2S_TXCR_VDW(8); ++ break; ++ case SNDRV_PCM_FORMAT_S16_LE: ++ val |= I2S_TXCR_VDW(16); ++ break; ++ case SNDRV_PCM_FORMAT_S20_3LE: ++ val |= I2S_TXCR_VDW(20); ++ break; ++ case SNDRV_PCM_FORMAT_S24_LE: ++ val |= I2S_TXCR_VDW(24); ++ break; ++ case SNDRV_PCM_FORMAT_S32_LE: ++ val |= I2S_TXCR_VDW(32); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (params_channels(params)) { ++ case 8: ++ val |= I2S_CHN_8; ++ break; ++ case 6: ++ val |= I2S_CHN_6; ++ break; ++ case 4: ++ val |= I2S_CHN_4; ++ break; ++ case 2: ++ val |= I2S_CHN_2; ++ break; ++ default: ++ dev_err(i2s_tdm->dev, "invalid channel: %d\n", ++ params_channels(params)); ++ return -EINVAL; ++ } ++ ++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ++ regmap_update_bits(i2s_tdm->regmap, I2S_RXCR, ++ I2S_RXCR_VDW_MASK | I2S_RXCR_CSR_MASK, ++ val); ++ else ++ regmap_update_bits(i2s_tdm->regmap, I2S_TXCR, ++ I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK, ++ val); ++ ++ regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK, ++ I2S_DMACR_TDL(16)); ++ regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK, ++ I2S_DMACR_RDL(16)); ++ ++ val = I2S_CKR_TRCM_TXRX; ++ if (dai->driver->symmetric_rates && rtd->dai_link->symmetric_rates) ++ val = I2S_CKR_TRCM_TXONLY; ++ ++ regmap_update_bits(i2s_tdm->regmap, I2S_CKR, ++ I2S_CKR_TRCM_MASK, ++ val); ++ return 0; ++} ++ ++static int rockchip_i2s_tdm_trigger(struct snd_pcm_substream *substream, ++ int cmd, struct snd_soc_dai *dai) ++{ ++ struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai); ++ int ret = 0; ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ case SNDRV_PCM_TRIGGER_RESUME: ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ++ rockchip_snd_rxctrl(i2s_tdm, 1); ++ else ++ rockchip_snd_txctrl(i2s_tdm, 1); ++ break; ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ case SNDRV_PCM_TRIGGER_STOP: ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ++ rockchip_snd_rxctrl(i2s_tdm, 0); ++ else ++ rockchip_snd_txctrl(i2s_tdm, 0); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++static int rockchip_i2s_tdm_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, ++ unsigned int freq, int dir) ++{ ++ struct rk_i2s_tdm_dev *i2s_tdm = to_info(cpu_dai); ++ int ret; ++ ++ ret = clk_set_rate(i2s_tdm->mclk_tx, freq); ++ if (ret) ++ dev_err(i2s_tdm->dev, "Fail to set mclk_tx %d\n", ret); ++ ++ if (!IS_ERR(i2s_tdm->mclk_rx)) ++ ret = clk_set_rate(i2s_tdm->mclk_rx, freq); ++ return ret; ++} ++ ++static int rockchip_i2s_tdm_dai_probe(struct snd_soc_dai *dai) ++{ ++ struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai); ++ ++ dai->capture_dma_data = &i2s_tdm->capture_dma_data; ++ dai->playback_dma_data = &i2s_tdm->playback_dma_data; ++ ++ return 0; ++} ++ ++static const struct snd_soc_dai_ops rockchip_i2s_tdm_dai_ops = { ++ .hw_params = rockchip_i2s_tdm_hw_params, ++ .set_sysclk = rockchip_i2s_tdm_set_sysclk, ++ .set_fmt = rockchip_i2s_tdm_set_fmt, ++ .trigger = rockchip_i2s_tdm_trigger, ++}; ++ ++static struct snd_soc_dai_driver rockchip_i2s_tdm_dai = { ++ .probe = rockchip_i2s_tdm_dai_probe, ++ .playback = { ++ .stream_name = "Playback", ++ .channels_min = 2, ++ .channels_max = 8, ++ .rates = SNDRV_PCM_RATE_8000_192000, ++ .formats = (SNDRV_PCM_FMTBIT_S8 | ++ SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_S20_3LE | ++ SNDRV_PCM_FMTBIT_S24_LE | ++ SNDRV_PCM_FMTBIT_S32_LE), ++ }, ++ .capture = { ++ .stream_name = "Capture", ++ .channels_min = 2, ++ .channels_max = 8, ++ .rates = SNDRV_PCM_RATE_8000_192000, ++ .formats = (SNDRV_PCM_FMTBIT_S8 | ++ SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_S20_3LE | ++ SNDRV_PCM_FMTBIT_S24_LE | ++ SNDRV_PCM_FMTBIT_S32_LE), ++ }, ++ .ops = &rockchip_i2s_tdm_dai_ops, ++ .symmetric_rates = 1, ++}; ++ ++static const struct snd_soc_component_driver rockchip_i2s_tdm_component = { ++ .name = DRV_NAME, ++}; ++ ++static bool rockchip_i2s_tdm_wr_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case I2S_TXCR: ++ case I2S_RXCR: ++ case I2S_CKR: ++ case I2S_DMACR: ++ case I2S_INTCR: ++ case I2S_XFER: ++ case I2S_CLR: ++ case I2S_TXDR: ++ case I2S_TDM_TXCR: ++ case I2S_TDM_RXCR: ++ case I2S_CLKDIV: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static bool rockchip_i2s_tdm_rd_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case I2S_TXCR: ++ case I2S_RXCR: ++ case I2S_CKR: ++ case I2S_DMACR: ++ case I2S_INTCR: ++ case I2S_XFER: ++ case I2S_CLR: ++ case I2S_RXDR: ++ case I2S_FIFOLR: ++ case I2S_INTSR: ++ case I2S_TDM_TXCR: ++ case I2S_TDM_RXCR: ++ case I2S_CLKDIV: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static bool rockchip_i2s_tdm_volatile_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case I2S_INTSR: ++ case I2S_CLR: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static bool rockchip_i2s_tdm_precious_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ default: ++ return false; ++ } ++} ++ ++static const struct reg_default rockchip_i2s_tdm_reg_defaults[] = { ++ {0x00, 0x7200000f}, ++ {0x04, 0x01c8000f}, ++ {0x08, 0x00001f1f}, ++ {0x10, 0x001f0000}, ++ {0x14, 0x01f00000}, ++ {0x30, 0x00003eff}, ++ {0x34, 0x00003eff}, ++ {0x38, 0x00000707}, ++}; ++ ++static const struct regmap_config rockchip_i2s_tdm_regmap_config = { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .max_register = I2S_CLKDIV, ++ .reg_defaults = rockchip_i2s_tdm_reg_defaults, ++ .num_reg_defaults = ARRAY_SIZE(rockchip_i2s_tdm_reg_defaults), ++ .writeable_reg = rockchip_i2s_tdm_wr_reg, ++ .readable_reg = rockchip_i2s_tdm_rd_reg, ++ .volatile_reg = rockchip_i2s_tdm_volatile_reg, ++ .precious_reg = rockchip_i2s_tdm_precious_reg, ++ .cache_type = REGCACHE_FLAT, ++}; ++ ++static const struct of_device_id rockchip_i2s_tdm_match[] = { ++ { .compatible = "rockchip,rk3308-i2s-tdm", }, ++ {}, ++}; ++ ++static int rockchip_i2s_tdm_probe(struct platform_device *pdev) ++{ ++ struct device_node *node = pdev->dev.of_node; ++ struct rk_i2s_tdm_dev *i2s_tdm; ++ struct resource *res; ++ void __iomem *regs; ++ int ret; ++ int val; ++ ++ i2s_tdm = devm_kzalloc(&pdev->dev, sizeof(*i2s_tdm), GFP_KERNEL); ++ if (!i2s_tdm) ++ return -ENOMEM; ++ ++ i2s_tdm->dev = &pdev->dev; ++ ++ i2s_tdm->hclk = devm_clk_get(&pdev->dev, "hclk"); ++ if (IS_ERR(i2s_tdm->hclk)) ++ return PTR_ERR(i2s_tdm->hclk); ++ ++ ret = clk_prepare_enable(i2s_tdm->hclk); ++ if (ret) ++ return ret; ++ ++ i2s_tdm->mclk_tx = devm_clk_get(&pdev->dev, "mclk_tx"); ++ if (IS_ERR(i2s_tdm->mclk_tx)) ++ return PTR_ERR(i2s_tdm->mclk_tx); ++ ++ i2s_tdm->mclk_rx = devm_clk_get(&pdev->dev, "mclk_rx"); ++ if (IS_ERR(i2s_tdm->mclk_rx)) ++ return PTR_ERR(i2s_tdm->mclk_rx); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ regs = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(regs)) ++ return PTR_ERR(regs); ++ ++ i2s_tdm->regmap = devm_regmap_init_mmio(&pdev->dev, regs, ++ &rockchip_i2s_tdm_regmap_config); ++ if (IS_ERR(i2s_tdm->regmap)) ++ return PTR_ERR(i2s_tdm->regmap); ++ ++ i2s_tdm->playback_dma_data.addr = res->start + I2S_TXDR; ++ i2s_tdm->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ i2s_tdm->playback_dma_data.maxburst = 8; ++ ++ i2s_tdm->capture_dma_data.addr = res->start + I2S_RXDR; ++ i2s_tdm->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ i2s_tdm->capture_dma_data.maxburst = 8; ++ ++ dev_set_drvdata(&pdev->dev, i2s_tdm); ++ ++ pm_runtime_enable(&pdev->dev); ++ if (!pm_runtime_enabled(&pdev->dev)) { ++ ret = i2s_tdm_runtime_resume(&pdev->dev); ++ if (ret) ++ goto err_pm_disable; ++ } ++ ++ i2s_tdm->bclk_fs = 64; ++ if (!of_property_read_u32(node, "rockchip,bclk-fs", &val)) { ++ if ((val >= 32) && (val % 2 == 0)) ++ i2s_tdm->bclk_fs = val; ++ } ++ ++ ret = devm_snd_soc_register_component(&pdev->dev, ++ &rockchip_i2s_tdm_component, ++ &rockchip_i2s_tdm_dai, 1); ++ ++ if (ret) { ++ dev_err(&pdev->dev, "Could not register DAI\n"); ++ goto err_suspend; ++ } ++ ++ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); ++ if (ret) { ++ dev_err(&pdev->dev, "Could not register PCM\n"); ++ return ret; ++ } ++ ++ return 0; ++ ++err_suspend: ++ if (!pm_runtime_status_suspended(&pdev->dev)) ++ i2s_tdm_runtime_suspend(&pdev->dev); ++err_pm_disable: ++ pm_runtime_disable(&pdev->dev); ++ ++ return ret; ++} ++ ++static int rockchip_i2s_tdm_remove(struct platform_device *pdev) ++{ ++ struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(&pdev->dev); ++ ++ pm_runtime_disable(&pdev->dev); ++ if (!pm_runtime_status_suspended(&pdev->dev)) ++ i2s_tdm_runtime_suspend(&pdev->dev); ++ ++ if (!IS_ERR(i2s_tdm->mclk_tx)) ++ clk_prepare_enable(i2s_tdm->mclk_tx); ++ if (!IS_ERR(i2s_tdm->mclk_rx)) ++ clk_prepare_enable(i2s_tdm->mclk_rx); ++ if (!IS_ERR(i2s_tdm->hclk)) ++ clk_disable_unprepare(i2s_tdm->hclk); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int rockchip_i2s_tdm_suspend(struct device *dev) ++{ ++ struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); ++ ++ regcache_mark_dirty(i2s_tdm->regmap); ++ ++ return 0; ++} ++ ++static int rockchip_i2s_tdm_resume(struct device *dev) ++{ ++ struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = pm_runtime_get_sync(dev); ++ if (ret < 0) ++ return ret; ++ ret = regcache_sync(i2s_tdm->regmap); ++ pm_runtime_put(dev); ++ ++ return ret; ++} ++#endif ++ ++static const struct dev_pm_ops rockchip_i2s_tdm_pm_ops = { ++ SET_RUNTIME_PM_OPS(i2s_tdm_runtime_suspend, i2s_tdm_runtime_resume, ++ NULL) ++ SET_SYSTEM_SLEEP_PM_OPS(rockchip_i2s_tdm_suspend, ++ rockchip_i2s_tdm_resume) ++}; ++ ++static struct platform_driver rockchip_i2s_tdm_driver = { ++ .probe = rockchip_i2s_tdm_probe, ++ .remove = rockchip_i2s_tdm_remove, ++ .driver = { ++ .name = DRV_NAME, ++ .of_match_table = of_match_ptr(rockchip_i2s_tdm_match), ++ .pm = &rockchip_i2s_tdm_pm_ops, ++ }, ++}; ++module_platform_driver(rockchip_i2s_tdm_driver); ++ ++MODULE_DESCRIPTION("ROCKCHIP I2S/TDM ASoC Interface"); ++MODULE_AUTHOR("Sugar Zhang "); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:" DRV_NAME); ++MODULE_DEVICE_TABLE(of, rockchip_i2s_tdm_match); +diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.h b/sound/soc/rockchip/rockchip_i2s_tdm.h +new file mode 100644 +index 000000000000..1a28523cfd82 +--- /dev/null ++++ b/sound/soc/rockchip/rockchip_i2s_tdm.h +@@ -0,0 +1,256 @@ ++/* ++ * sound/soc/rockchip/rockchip_i2s_tdm.h ++ * ++ * ALSA SoC Audio Layer - Rockchip I2S_TDM Controller driver ++ * ++ * Copyright (c) 2018 Rockchip Electronics Co. Ltd. ++ * Author: Sugar Zhang ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef _ROCKCHIP_I2S_TDM_H ++#define _ROCKCHIP_I2S_TDM_H ++ ++/* ++ * TXCR ++ * transmit operation control register ++ */ ++#define I2S_TXCR_RCNT_SHIFT 17 ++#define I2S_TXCR_RCNT_MASK (0x3f << I2S_TXCR_RCNT_SHIFT) ++#define I2S_TXCR_CSR_SHIFT 15 ++#define I2S_TXCR_CSR(x) (x << I2S_TXCR_CSR_SHIFT) ++#define I2S_TXCR_CSR_MASK (3 << I2S_TXCR_CSR_SHIFT) ++#define I2S_TXCR_HWT BIT(14) ++#define I2S_TXCR_SJM_SHIFT 12 ++#define I2S_TXCR_SJM_R (0 << I2S_TXCR_SJM_SHIFT) ++#define I2S_TXCR_SJM_L (1 << I2S_TXCR_SJM_SHIFT) ++#define I2S_TXCR_FBM_SHIFT 11 ++#define I2S_TXCR_FBM_MSB (0 << I2S_TXCR_FBM_SHIFT) ++#define I2S_TXCR_FBM_LSB (1 << I2S_TXCR_FBM_SHIFT) ++#define I2S_TXCR_IBM_SHIFT 9 ++#define I2S_TXCR_IBM_NORMAL (0 << I2S_TXCR_IBM_SHIFT) ++#define I2S_TXCR_IBM_LSJM (1 << I2S_TXCR_IBM_SHIFT) ++#define I2S_TXCR_IBM_RSJM (2 << I2S_TXCR_IBM_SHIFT) ++#define I2S_TXCR_IBM_MASK (3 << I2S_TXCR_IBM_SHIFT) ++#define I2S_TXCR_PBM_SHIFT 7 ++#define I2S_TXCR_PBM_MODE(x) (x << I2S_TXCR_PBM_SHIFT) ++#define I2S_TXCR_PBM_MASK (3 << I2S_TXCR_PBM_SHIFT) ++#define I2S_TXCR_TFS_SHIFT 5 ++#define I2S_TXCR_TFS_I2S (0 << I2S_TXCR_TFS_SHIFT) ++#define I2S_TXCR_TFS_PCM (1 << I2S_TXCR_TFS_SHIFT) ++#define I2S_TXCR_TFS_MASK (1 << I2S_TXCR_TFS_SHIFT) ++#define I2S_TXCR_VDW_SHIFT 0 ++#define I2S_TXCR_VDW(x) ((x - 1) << I2S_TXCR_VDW_SHIFT) ++#define I2S_TXCR_VDW_MASK (0x1f << I2S_TXCR_VDW_SHIFT) ++ ++/* ++ * RXCR ++ * receive operation control register ++ */ ++#define I2S_RXCR_CSR_SHIFT 15 ++#define I2S_RXCR_CSR(x) (x << I2S_RXCR_CSR_SHIFT) ++#define I2S_RXCR_CSR_MASK (3 << I2S_RXCR_CSR_SHIFT) ++#define I2S_RXCR_HWT BIT(14) ++#define I2S_RXCR_SJM_SHIFT 12 ++#define I2S_RXCR_SJM_R (0 << I2S_RXCR_SJM_SHIFT) ++#define I2S_RXCR_SJM_L (1 << I2S_RXCR_SJM_SHIFT) ++#define I2S_RXCR_FBM_SHIFT 11 ++#define I2S_RXCR_FBM_MSB (0 << I2S_RXCR_FBM_SHIFT) ++#define I2S_RXCR_FBM_LSB (1 << I2S_RXCR_FBM_SHIFT) ++#define I2S_RXCR_IBM_SHIFT 9 ++#define I2S_RXCR_IBM_NORMAL (0 << I2S_RXCR_IBM_SHIFT) ++#define I2S_RXCR_IBM_LSJM (1 << I2S_RXCR_IBM_SHIFT) ++#define I2S_RXCR_IBM_RSJM (2 << I2S_RXCR_IBM_SHIFT) ++#define I2S_RXCR_IBM_MASK (3 << I2S_RXCR_IBM_SHIFT) ++#define I2S_RXCR_PBM_SHIFT 7 ++#define I2S_RXCR_PBM_MODE(x) (x << I2S_RXCR_PBM_SHIFT) ++#define I2S_RXCR_PBM_MASK (3 << I2S_RXCR_PBM_SHIFT) ++#define I2S_RXCR_TFS_SHIFT 5 ++#define I2S_RXCR_TFS_I2S (0 << I2S_RXCR_TFS_SHIFT) ++#define I2S_RXCR_TFS_PCM (1 << I2S_RXCR_TFS_SHIFT) ++#define I2S_RXCR_TFS_MASK (1 << I2S_RXCR_TFS_SHIFT) ++#define I2S_RXCR_VDW_SHIFT 0 ++#define I2S_RXCR_VDW(x) ((x - 1) << I2S_RXCR_VDW_SHIFT) ++#define I2S_RXCR_VDW_MASK (0x1f << I2S_RXCR_VDW_SHIFT) ++ ++/* ++ * CKR ++ * clock generation register ++ */ ++#define I2S_CKR_TRCM_SHIFT 28 ++#define I2S_CKR_TRCM(x) (x << I2S_CKR_TRCM_SHIFT) ++#define I2S_CKR_TRCM_TXRX (0 << I2S_CKR_TRCM_SHIFT) ++#define I2S_CKR_TRCM_TXONLY (1 << I2S_CKR_TRCM_SHIFT) ++#define I2S_CKR_TRCM_RXONLY (2 << I2S_CKR_TRCM_SHIFT) ++#define I2S_CKR_TRCM_MASK (3 << I2S_CKR_TRCM_SHIFT) ++#define I2S_CKR_MSS_SHIFT 27 ++#define I2S_CKR_MSS_MASTER (0 << I2S_CKR_MSS_SHIFT) ++#define I2S_CKR_MSS_SLAVE (1 << I2S_CKR_MSS_SHIFT) ++#define I2S_CKR_MSS_MASK (1 << I2S_CKR_MSS_SHIFT) ++#define I2S_CKR_CKP_SHIFT 26 ++#define I2S_CKR_CKP_NEG (0 << I2S_CKR_CKP_SHIFT) ++#define I2S_CKR_CKP_POS (1 << I2S_CKR_CKP_SHIFT) ++#define I2S_CKR_CKP_MASK (1 << I2S_CKR_CKP_SHIFT) ++#define I2S_CKR_RLP_SHIFT 25 ++#define I2S_CKR_RLP_NORMAL (0 << I2S_CKR_RLP_SHIFT) ++#define I2S_CKR_RLP_OPPSITE (1 << I2S_CKR_RLP_SHIFT) ++#define I2S_CKR_TLP_SHIFT 24 ++#define I2S_CKR_TLP_NORMAL (0 << I2S_CKR_TLP_SHIFT) ++#define I2S_CKR_TLP_OPPSITE (1 << I2S_CKR_TLP_SHIFT) ++#define I2S_CKR_MDIV_SHIFT 16 ++#define I2S_CKR_MDIV(x) ((x - 1) << I2S_CKR_MDIV_SHIFT) ++#define I2S_CKR_MDIV_MASK (0xff << I2S_CKR_MDIV_SHIFT) ++#define I2S_CKR_RSD_SHIFT 8 ++#define I2S_CKR_RSD(x) ((x - 1) << I2S_CKR_RSD_SHIFT) ++#define I2S_CKR_RSD_MASK (0xff << I2S_CKR_RSD_SHIFT) ++#define I2S_CKR_TSD_SHIFT 0 ++#define I2S_CKR_TSD(x) ((x - 1) << I2S_CKR_TSD_SHIFT) ++#define I2S_CKR_TSD_MASK (0xff << I2S_CKR_TSD_SHIFT) ++ ++/* ++ * FIFOLR ++ * FIFO level register ++ */ ++#define I2S_FIFOLR_RFL_SHIFT 24 ++#define I2S_FIFOLR_RFL_MASK (0x3f << I2S_FIFOLR_RFL_SHIFT) ++#define I2S_FIFOLR_TFL3_SHIFT 18 ++#define I2S_FIFOLR_TFL3_MASK (0x3f << I2S_FIFOLR_TFL3_SHIFT) ++#define I2S_FIFOLR_TFL2_SHIFT 12 ++#define I2S_FIFOLR_TFL2_MASK (0x3f << I2S_FIFOLR_TFL2_SHIFT) ++#define I2S_FIFOLR_TFL1_SHIFT 6 ++#define I2S_FIFOLR_TFL1_MASK (0x3f << I2S_FIFOLR_TFL1_SHIFT) ++#define I2S_FIFOLR_TFL0_SHIFT 0 ++#define I2S_FIFOLR_TFL0_MASK (0x3f << I2S_FIFOLR_TFL0_SHIFT) ++ ++/* ++ * DMACR ++ * DMA control register ++ */ ++#define I2S_DMACR_RDE_SHIFT 24 ++#define I2S_DMACR_RDE_DISABLE (0 << I2S_DMACR_RDE_SHIFT) ++#define I2S_DMACR_RDE_ENABLE (1 << I2S_DMACR_RDE_SHIFT) ++#define I2S_DMACR_RDL_SHIFT 16 ++#define I2S_DMACR_RDL(x) ((x - 1) << I2S_DMACR_RDL_SHIFT) ++#define I2S_DMACR_RDL_MASK (0x1f << I2S_DMACR_RDL_SHIFT) ++#define I2S_DMACR_TDE_SHIFT 8 ++#define I2S_DMACR_TDE_DISABLE (0 << I2S_DMACR_TDE_SHIFT) ++#define I2S_DMACR_TDE_ENABLE (1 << I2S_DMACR_TDE_SHIFT) ++#define I2S_DMACR_TDL_SHIFT 0 ++#define I2S_DMACR_TDL(x) ((x) << I2S_DMACR_TDL_SHIFT) ++#define I2S_DMACR_TDL_MASK (0x1f << I2S_DMACR_TDL_SHIFT) ++ ++/* ++ * INTCR ++ * interrupt control register ++ */ ++#define I2S_INTCR_RFT_SHIFT 20 ++#define I2S_INTCR_RFT(x) ((x - 1) << I2S_INTCR_RFT_SHIFT) ++#define I2S_INTCR_RXOIC BIT(18) ++#define I2S_INTCR_RXOIE_SHIFT 17 ++#define I2S_INTCR_RXOIE_DISABLE (0 << I2S_INTCR_RXOIE_SHIFT) ++#define I2S_INTCR_RXOIE_ENABLE (1 << I2S_INTCR_RXOIE_SHIFT) ++#define I2S_INTCR_RXFIE_SHIFT 16 ++#define I2S_INTCR_RXFIE_DISABLE (0 << I2S_INTCR_RXFIE_SHIFT) ++#define I2S_INTCR_RXFIE_ENABLE (1 << I2S_INTCR_RXFIE_SHIFT) ++#define I2S_INTCR_TFT_SHIFT 4 ++#define I2S_INTCR_TFT(x) ((x - 1) << I2S_INTCR_TFT_SHIFT) ++#define I2S_INTCR_TFT_MASK (0x1f << I2S_INTCR_TFT_SHIFT) ++#define I2S_INTCR_TXUIC BIT(2) ++#define I2S_INTCR_TXUIE_SHIFT 1 ++#define I2S_INTCR_TXUIE_DISABLE (0 << I2S_INTCR_TXUIE_SHIFT) ++#define I2S_INTCR_TXUIE_ENABLE (1 << I2S_INTCR_TXUIE_SHIFT) ++ ++/* ++ * INTSR ++ * interrupt status register ++ */ ++#define I2S_INTSR_TXEIE_SHIFT 0 ++#define I2S_INTSR_TXEIE_DISABLE (0 << I2S_INTSR_TXEIE_SHIFT) ++#define I2S_INTSR_TXEIE_ENABLE (1 << I2S_INTSR_TXEIE_SHIFT) ++#define I2S_INTSR_RXOI_SHIFT 17 ++#define I2S_INTSR_RXOI_INA (0 << I2S_INTSR_RXOI_SHIFT) ++#define I2S_INTSR_RXOI_ACT (1 << I2S_INTSR_RXOI_SHIFT) ++#define I2S_INTSR_RXFI_SHIFT 16 ++#define I2S_INTSR_RXFI_INA (0 << I2S_INTSR_RXFI_SHIFT) ++#define I2S_INTSR_RXFI_ACT (1 << I2S_INTSR_RXFI_SHIFT) ++#define I2S_INTSR_TXUI_SHIFT 1 ++#define I2S_INTSR_TXUI_INA (0 << I2S_INTSR_TXUI_SHIFT) ++#define I2S_INTSR_TXUI_ACT (1 << I2S_INTSR_TXUI_SHIFT) ++#define I2S_INTSR_TXEI_SHIFT 0 ++#define I2S_INTSR_TXEI_INA (0 << I2S_INTSR_TXEI_SHIFT) ++#define I2S_INTSR_TXEI_ACT (1 << I2S_INTSR_TXEI_SHIFT) ++ ++/* ++ * XFER ++ * Transfer start register ++ */ ++#define I2S_XFER_RXS_SHIFT 1 ++#define I2S_XFER_RXS_STOP (0 << I2S_XFER_RXS_SHIFT) ++#define I2S_XFER_RXS_START (1 << I2S_XFER_RXS_SHIFT) ++#define I2S_XFER_TXS_SHIFT 0 ++#define I2S_XFER_TXS_STOP (0 << I2S_XFER_TXS_SHIFT) ++#define I2S_XFER_TXS_START (1 << I2S_XFER_TXS_SHIFT) ++ ++/* ++ * CLR ++ * clear SCLK domain logic register ++ */ ++#define I2S_CLR_RXC BIT(1) ++#define I2S_CLR_TXC BIT(0) ++ ++/* ++ * TXDR ++ * Transimt FIFO data register, write only. ++ */ ++#define I2S_TXDR_MASK (0xff) ++ ++/* ++ * RXDR ++ * Receive FIFO data register, write only. ++ */ ++#define I2S_RXDR_MASK (0xff) ++ ++/* ++ * CLKDIV ++ * Mclk div register ++ */ ++#define I2S_CLKDIV_TXM_SHIFT 0 ++#define I2S_CLKDIV_TXM(x) ((x - 1) << I2S_CLKDIV_TXM_SHIFT) ++#define I2S_CLKDIV_TXM_MASK (0xff << I2S_CLKDIV_TXM_SHIFT) ++#define I2S_CLKDIV_RXM_SHIFT 8 ++#define I2S_CLKDIV_RXM(x) ((x - 1) << I2S_CLKDIV_RXM_SHIFT) ++#define I2S_CLKDIV_RXM_MASK (0xff << I2S_CLKDIV_RXM_SHIFT) ++ ++/* Clock divider id */ ++enum { ++ ROCKCHIP_DIV_MCLK = 0, ++ ROCKCHIP_DIV_BCLK, ++}; ++ ++/* channel select */ ++#define I2S_CSR_SHIFT 15 ++#define I2S_CHN_2 (0 << I2S_CSR_SHIFT) ++#define I2S_CHN_4 (1 << I2S_CSR_SHIFT) ++#define I2S_CHN_6 (2 << I2S_CSR_SHIFT) ++#define I2S_CHN_8 (3 << I2S_CSR_SHIFT) ++ ++/* I2S REGS */ ++#define I2S_TXCR (0x0000) ++#define I2S_RXCR (0x0004) ++#define I2S_CKR (0x0008) ++#define I2S_FIFOLR (0x000c) ++#define I2S_DMACR (0x0010) ++#define I2S_INTCR (0x0014) ++#define I2S_INTSR (0x0018) ++#define I2S_XFER (0x001c) ++#define I2S_CLR (0x0020) ++#define I2S_TXDR (0x0024) ++#define I2S_RXDR (0x0028) ++#define I2S_TDM_TXCR (0x0030) ++#define I2S_TDM_RXCR (0x0034) ++#define I2S_CLKDIV (0x0038) ++ ++#endif /* _ROCKCHIP_I2S_TDM_H */ +-- +2.25.1 + diff --git a/sys-kernel/decade-sources/files/board-rockpis-0017-WIP-Sync-rockchip_i2s_tdm-to-BSP-tree.patch b/sys-kernel/decade-sources/files/patches-5.14/board-rockpis-0017-WIP-Sync-rockchip_i2s_tdm-to-BSP-tree.patch similarity index 100% rename from sys-kernel/decade-sources/files/board-rockpis-0017-WIP-Sync-rockchip_i2s_tdm-to-BSP-tree.patch rename to sys-kernel/decade-sources/files/patches-5.14/board-rockpis-0017-WIP-Sync-rockchip_i2s_tdm-to-BSP-tree.patch diff --git a/sys-kernel/decade-sources/files/patches-5.14/board-rockpis-0018-ASoC-codecs-Add-RK3308-internal-codec-driver.patch b/sys-kernel/decade-sources/files/patches-5.14/board-rockpis-0018-ASoC-codecs-Add-RK3308-internal-codec-driver.patch new file mode 100644 index 0000000..1e280ec --- /dev/null +++ b/sys-kernel/decade-sources/files/patches-5.14/board-rockpis-0018-ASoC-codecs-Add-RK3308-internal-codec-driver.patch @@ -0,0 +1,2645 @@ +From 76f98b4d0be07417ab9bc4a5669d185299f54cdf Mon Sep 17 00:00:00 2001 +From: ashthespy +Date: Mon, 3 Feb 2020 17:09:38 +0100 +Subject: [PATCH 18/23] ASoC: codecs: Add RK3308 internal codec driver + +From b9d097610177b7117a09ea74ed00476a2105f169 Mon Sep 17 00:00:00 2001 +From: Xing Zheng +Date: Sun, 11 Mar 2018 11:37:28 +0800 +Subject: [PATCH] ASoC: codecs: Add RK3308 internal codec driver + +This adds support for the RK3308 audio codec. + +Change-Id: Ieccdebaa27f4a46f6de9406046a6e02e20398013 +Signed-off-by: Xing Zheng +--- + sound/soc/codecs/Kconfig | 5 + + sound/soc/codecs/Makefile | 2 + + sound/soc/codecs/rk3308_codec.c | 1604 +++++++++++++++++++++++++++++++ + sound/soc/codecs/rk3308_codec.h | 960 ++++++++++++++++++ + 4 files changed, 2571 insertions(+) + create mode 100644 sound/soc/codecs/rk3308_codec.c + create mode 100644 sound/soc/codecs/rk3308_codec.h + +diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig +index 030f500f3bc8..1f4675a59014 100644 +--- a/sound/soc/codecs/Kconfig ++++ b/sound/soc/codecs/Kconfig +@@ -144,6 +144,7 @@ config SND_SOC_ALL_CODECS + imply SND_SOC_PCM5102A + imply SND_SOC_PCM512x_I2C + imply SND_SOC_PCM512x_SPI ++ imply SND_SOC_RK3308 + imply SND_SOC_RK3328 + imply SND_SOC_RT274 + imply SND_SOC_RT286 +@@ -931,6 +932,10 @@ config SND_SOC_PCM512x_SPI + select SND_SOC_PCM512x + select REGMAP_SPI + ++config SND_SOC_RK3308 ++ select REGMAP_MMIO ++ tristate "Rockchip RK3308 CODEC" ++ + config SND_SOC_RK3328 + tristate "Rockchip RK3328 audio CODEC" + select REGMAP_MMIO +diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile +index ddfd07071925..9a35df4862dc 100644 +--- a/sound/soc/codecs/Makefile ++++ b/sound/soc/codecs/Makefile +@@ -148,6 +148,7 @@ snd-soc-pcm5102a-objs := pcm5102a.o + snd-soc-pcm512x-objs := pcm512x.o + snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o + snd-soc-pcm512x-spi-objs := pcm512x-spi.o ++snd-soc-rk3308-objs := rk3308_codec.o + snd-soc-rk3328-objs := rk3328_codec.o + snd-soc-rl6231-objs := rl6231.o + snd-soc-rl6347a-objs := rl6347a.o +@@ -437,6 +438,7 @@ obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o + obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o + obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o + obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o ++obj-$(CONFIG_SND_SOC_RK3308) += snd-soc-rk3308.o + obj-$(CONFIG_SND_SOC_RK3328) += snd-soc-rk3328.o + obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o + obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o +diff --git a/sound/soc/codecs/rk3308_codec.c b/sound/soc/codecs/rk3308_codec.c +new file mode 100644 +index 000000000000..106f09738dd0 +--- /dev/null ++++ b/sound/soc/codecs/rk3308_codec.c +@@ -0,0 +1,1604 @@ ++/* ++ * rk3308_codec.c -- RK3308 ALSA Soc Audio Driver ++ * ++ * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "rk3308_codec.h" ++ ++struct rk3308_codec_priv { ++ const struct device *plat_dev; ++ struct device dev; ++ struct reset_control *reset; ++ struct regmap *regmap; ++ struct clk *pclk; ++ struct gpio_desc *spk_ctl_gpio; ++ int adc_ch; /* To select ADCs for channel */ ++ int adc_ch0_using_linein; ++}; ++ ++static const DECLARE_TLV_DB_SCALE(rk3308_codec_alc_agc_ch_gain_tlv, ++ -1800, 150, 2850); ++static const DECLARE_TLV_DB_SCALE(rk3308_codec_alc_agc_ch_max_gain_tlv, ++ -1350, 600, 2850); ++static const DECLARE_TLV_DB_SCALE(rk3308_codec_alc_agc_ch_min_gain_tlv, ++ -1800, 600, 2400); ++static const DECLARE_TLV_DB_SCALE(rk3308_codec_adc_mic_gain_tlv, ++ 0, 600, 3000); ++static const DECLARE_TLV_DB_SCALE(rk3308_codec_adc_alc_gain_tlv, ++ -1800, 150, 2850); ++static const DECLARE_TLV_DB_SCALE(rk3308_codec_dac_gain_tlv, ++ 0, 150, 600); ++static const DECLARE_TLV_DB_SCALE(rk3308_codec_dac_hpout_gain_tlv, ++ -3900, 150, 600); ++static const DECLARE_TLV_DB_SCALE(rk3308_codec_dac_hpmix_gain_tlv, ++ -600, 600, 0); ++ ++static const struct snd_kcontrol_new rk3308_codec_dapm_controls[] = { ++ /* ALC AGC Channel*/ ++ SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 0 Volume", ++ RK3308_ALC_L_DIG_CON03(0), ++ RK3308_ALC_R_DIG_CON03(0), ++ RK3308_AGC_PGA_GAIN_SFT, ++ RK3308_AGC_PGA_GAIN_NDB_18, ++ RK3308_AGC_PGA_GAIN_PDB_28_5, ++ 0, rk3308_codec_alc_agc_ch_gain_tlv), ++ SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 1 Volume", ++ RK3308_ALC_L_DIG_CON03(1), ++ RK3308_ALC_R_DIG_CON03(1), ++ RK3308_AGC_PGA_GAIN_SFT, ++ RK3308_AGC_PGA_GAIN_NDB_18, ++ RK3308_AGC_PGA_GAIN_PDB_28_5, ++ 0, rk3308_codec_alc_agc_ch_gain_tlv), ++ SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 2 Volume", ++ RK3308_ALC_L_DIG_CON03(2), ++ RK3308_ALC_R_DIG_CON03(2), ++ RK3308_AGC_PGA_GAIN_SFT, ++ RK3308_AGC_PGA_GAIN_NDB_18, ++ RK3308_AGC_PGA_GAIN_PDB_28_5, ++ 0, rk3308_codec_alc_agc_ch_gain_tlv), ++ SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 3 Volume", ++ RK3308_ALC_L_DIG_CON03(3), ++ RK3308_ALC_R_DIG_CON03(3), ++ RK3308_AGC_PGA_GAIN_SFT, ++ RK3308_AGC_PGA_GAIN_NDB_18, ++ RK3308_AGC_PGA_GAIN_PDB_28_5, ++ 0, rk3308_codec_alc_agc_ch_gain_tlv), ++ ++ /* ALC AGC MAX */ ++ SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 0 Max Volume", ++ RK3308_ALC_L_DIG_CON09(0), ++ RK3308_ALC_R_DIG_CON09(0), ++ RK3308_AGC_MAX_GAIN_PGA_SFT, ++ RK3308_AGC_MAX_GAIN_PGA_NDB_13_5, ++ RK3308_AGC_MAX_GAIN_PGA_PDB_28_5, ++ 0, rk3308_codec_alc_agc_ch_max_gain_tlv), ++ SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 1 Max Volume", ++ RK3308_ALC_L_DIG_CON09(1), ++ RK3308_ALC_R_DIG_CON09(1), ++ RK3308_AGC_MAX_GAIN_PGA_SFT, ++ RK3308_AGC_MAX_GAIN_PGA_NDB_13_5, ++ RK3308_AGC_MAX_GAIN_PGA_PDB_28_5, ++ 0, rk3308_codec_alc_agc_ch_max_gain_tlv), ++ SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 2 Max Volume", ++ RK3308_ALC_L_DIG_CON09(2), ++ RK3308_ALC_R_DIG_CON09(2), ++ RK3308_AGC_MAX_GAIN_PGA_SFT, ++ RK3308_AGC_MAX_GAIN_PGA_NDB_13_5, ++ RK3308_AGC_MAX_GAIN_PGA_PDB_28_5, ++ 0, rk3308_codec_alc_agc_ch_max_gain_tlv), ++ SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 3 Max Volume", ++ RK3308_ALC_L_DIG_CON09(3), ++ RK3308_ALC_R_DIG_CON09(3), ++ RK3308_AGC_MAX_GAIN_PGA_SFT, ++ RK3308_AGC_MAX_GAIN_PGA_NDB_13_5, ++ RK3308_AGC_MAX_GAIN_PGA_PDB_28_5, ++ 0, rk3308_codec_alc_agc_ch_max_gain_tlv), ++ ++ /* ALC AGC MIN */ ++ SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 0 Min Volume", ++ RK3308_ALC_L_DIG_CON09(0), ++ RK3308_ALC_R_DIG_CON09(0), ++ RK3308_AGC_MIN_GAIN_PGA_SFT, ++ RK3308_AGC_MIN_GAIN_PGA_NDB_18, ++ RK3308_AGC_MIN_GAIN_PGA_PDB_24, ++ 0, rk3308_codec_alc_agc_ch_min_gain_tlv), ++ SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 1 Min Volume", ++ RK3308_ALC_L_DIG_CON09(1), ++ RK3308_ALC_R_DIG_CON09(1), ++ RK3308_AGC_MIN_GAIN_PGA_SFT, ++ RK3308_AGC_MIN_GAIN_PGA_NDB_18, ++ RK3308_AGC_MIN_GAIN_PGA_PDB_24, ++ 0, rk3308_codec_alc_agc_ch_min_gain_tlv), ++ SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 2 Min Volume", ++ RK3308_ALC_L_DIG_CON09(2), ++ RK3308_ALC_R_DIG_CON09(2), ++ RK3308_AGC_MIN_GAIN_PGA_SFT, ++ RK3308_AGC_MIN_GAIN_PGA_NDB_18, ++ RK3308_AGC_MIN_GAIN_PGA_PDB_24, ++ 0, rk3308_codec_alc_agc_ch_min_gain_tlv), ++ SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 3 Min Volume", ++ RK3308_ALC_L_DIG_CON09(3), ++ RK3308_ALC_R_DIG_CON09(3), ++ RK3308_AGC_MIN_GAIN_PGA_SFT, ++ RK3308_AGC_MIN_GAIN_PGA_NDB_18, ++ RK3308_AGC_MIN_GAIN_PGA_PDB_24, ++ 0, rk3308_codec_alc_agc_ch_min_gain_tlv), ++ ++ /* ADC MIC */ ++ SOC_SINGLE_RANGE_TLV("ADC MIC Channel 0 Left Volume", ++ RK3308_ADC_ANA_CON01(0), ++ RK3308_ADC_CH1_MIC_GAIN_SFT, ++ RK3308_ADC_CH1_MIC_GAIN_0DB, ++ RK3308_ADC_CH1_MIC_GAIN_30DB, ++ 0, rk3308_codec_adc_mic_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("ADC MIC Channel 0 Right Volume", ++ RK3308_ADC_ANA_CON01(0), ++ RK3308_ADC_CH2_MIC_GAIN_SFT, ++ RK3308_ADC_CH2_MIC_GAIN_0DB, ++ RK3308_ADC_CH2_MIC_GAIN_30DB, ++ 0, rk3308_codec_adc_mic_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("ADC MIC Channel 1 Left Volume", ++ RK3308_ADC_ANA_CON01(1), ++ RK3308_ADC_CH1_MIC_GAIN_SFT, ++ RK3308_ADC_CH1_MIC_GAIN_0DB, ++ RK3308_ADC_CH1_MIC_GAIN_30DB, ++ 0, rk3308_codec_adc_mic_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("ADC MIC Channel 1 Right Volume", ++ RK3308_ADC_ANA_CON01(1), ++ RK3308_ADC_CH2_MIC_GAIN_SFT, ++ RK3308_ADC_CH2_MIC_GAIN_0DB, ++ RK3308_ADC_CH2_MIC_GAIN_30DB, ++ 0, rk3308_codec_adc_mic_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("ADC MIC Channel 2 Left Volume", ++ RK3308_ADC_ANA_CON01(2), ++ RK3308_ADC_CH1_MIC_GAIN_SFT, ++ RK3308_ADC_CH1_MIC_GAIN_0DB, ++ RK3308_ADC_CH1_MIC_GAIN_30DB, ++ 0, rk3308_codec_adc_mic_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("ADC MIC Channel 2 Right Volume", ++ RK3308_ADC_ANA_CON01(2), ++ RK3308_ADC_CH2_MIC_GAIN_SFT, ++ RK3308_ADC_CH2_MIC_GAIN_0DB, ++ RK3308_ADC_CH2_MIC_GAIN_30DB, ++ 0, rk3308_codec_adc_mic_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("ADC MIC Channel 3 Left Volume", ++ RK3308_ADC_ANA_CON01(3), ++ RK3308_ADC_CH1_MIC_GAIN_SFT, ++ RK3308_ADC_CH1_MIC_GAIN_0DB, ++ RK3308_ADC_CH1_MIC_GAIN_30DB, ++ 0, rk3308_codec_adc_mic_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("ADC MIC Channel 3 Right Volume", ++ RK3308_ADC_ANA_CON01(3), ++ RK3308_ADC_CH2_MIC_GAIN_SFT, ++ RK3308_ADC_CH2_MIC_GAIN_0DB, ++ RK3308_ADC_CH2_MIC_GAIN_30DB, ++ 0, rk3308_codec_adc_mic_gain_tlv), ++ ++ /* ADC ALC */ ++ SOC_SINGLE_RANGE_TLV("ADC ALC Channel 0 Left Volume", ++ RK3308_ADC_ANA_CON03(0), ++ RK3308_ADC_CH1_ALC_GAIN_SFT, ++ RK3308_ADC_CH1_ALC_GAIN_NDB_18, ++ RK3308_ADC_CH1_ALC_GAIN_PDB_28_5, ++ 0, rk3308_codec_adc_alc_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("ADC ALC Channel 0 Right Volume", ++ RK3308_ADC_ANA_CON04(0), ++ RK3308_ADC_CH2_ALC_GAIN_SFT, ++ RK3308_ADC_CH2_ALC_GAIN_NDB_18, ++ RK3308_ADC_CH2_ALC_GAIN_PDB_28_5, ++ 0, rk3308_codec_adc_alc_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("ADC ALC Channel 1 Left Volume", ++ RK3308_ADC_ANA_CON03(1), ++ RK3308_ADC_CH1_ALC_GAIN_SFT, ++ RK3308_ADC_CH1_ALC_GAIN_NDB_18, ++ RK3308_ADC_CH1_ALC_GAIN_PDB_28_5, ++ 0, rk3308_codec_adc_alc_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("ADC ALC Channel 1 Right Volume", ++ RK3308_ADC_ANA_CON04(1), ++ RK3308_ADC_CH2_ALC_GAIN_SFT, ++ RK3308_ADC_CH2_ALC_GAIN_NDB_18, ++ RK3308_ADC_CH2_ALC_GAIN_PDB_28_5, ++ 0, rk3308_codec_adc_alc_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("ADC ALC Channel 2 Left Volume", ++ RK3308_ADC_ANA_CON03(2), ++ RK3308_ADC_CH1_ALC_GAIN_SFT, ++ RK3308_ADC_CH1_ALC_GAIN_NDB_18, ++ RK3308_ADC_CH1_ALC_GAIN_PDB_28_5, ++ 0, rk3308_codec_adc_alc_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("ADC ALC Channel 2 Right Volume", ++ RK3308_ADC_ANA_CON04(2), ++ RK3308_ADC_CH2_ALC_GAIN_SFT, ++ RK3308_ADC_CH2_ALC_GAIN_NDB_18, ++ RK3308_ADC_CH2_ALC_GAIN_PDB_28_5, ++ 0, rk3308_codec_adc_alc_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("ADC ALC Channel 3 Left Volume", ++ RK3308_ADC_ANA_CON03(3), ++ RK3308_ADC_CH1_ALC_GAIN_SFT, ++ RK3308_ADC_CH1_ALC_GAIN_NDB_18, ++ RK3308_ADC_CH1_ALC_GAIN_PDB_28_5, ++ 0, rk3308_codec_adc_alc_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("ADC ALC Channel 3 Right Volume", ++ RK3308_ADC_ANA_CON04(3), ++ RK3308_ADC_CH2_ALC_GAIN_SFT, ++ RK3308_ADC_CH2_ALC_GAIN_NDB_18, ++ RK3308_ADC_CH2_ALC_GAIN_PDB_28_5, ++ 0, rk3308_codec_adc_alc_gain_tlv), ++ ++ /* DAC */ ++ SOC_SINGLE_RANGE_TLV("DAC Left Volume", ++ RK3308_DAC_ANA_CON04, ++ RK3308_DAC_L_GAIN_SFT, ++ RK3308_DAC_L_GAIN_0DB, ++ RK3308_DAC_L_GAIN_PDB_6, ++ 0, rk3308_codec_dac_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("DAC Right Volume", ++ RK3308_DAC_ANA_CON04, ++ RK3308_DAC_R_GAIN_SFT, ++ RK3308_DAC_R_GAIN_0DB, ++ RK3308_DAC_R_GAIN_PDB_6, ++ 0, rk3308_codec_dac_gain_tlv), ++ ++ /* DAC HPOUT */ ++ SOC_SINGLE_RANGE_TLV("DAC HPOUT Left Volume", ++ RK3308_DAC_ANA_CON05, ++ RK3308_DAC_L_HPOUT_GAIN_SFT, ++ RK3308_DAC_L_HPOUT_GAIN_NDB_39, ++ RK3308_DAC_L_HPOUT_GAIN_PDB_6, ++ 0, rk3308_codec_dac_hpout_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("DAC HPOUT Right Volume", ++ RK3308_DAC_ANA_CON06, ++ RK3308_DAC_R_HPOUT_GAIN_SFT, ++ RK3308_DAC_R_HPOUT_GAIN_NDB_39, ++ RK3308_DAC_R_HPOUT_GAIN_PDB_6, ++ 0, rk3308_codec_dac_hpout_gain_tlv), ++ ++ /* DAC HPMIX */ ++ SOC_SINGLE_RANGE_TLV("DAC HPMIX Left Volume", ++ RK3308_DAC_ANA_CON05, ++ RK3308_DAC_L_HPMIX_GAIN_SFT, ++ RK3308_DAC_L_HPMIX_GAIN_NDB_6, ++ RK3308_DAC_L_HPMIX_GAIN_0DB, ++ 0, rk3308_codec_dac_hpmix_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("DAC HPMIX Right Volume", ++ RK3308_DAC_ANA_CON05, ++ RK3308_DAC_R_HPMIX_GAIN_SFT, ++ RK3308_DAC_R_HPMIX_GAIN_NDB_6, ++ RK3308_DAC_R_HPMIX_GAIN_0DB, ++ 0, rk3308_codec_dac_hpmix_gain_tlv), ++}; ++ ++static void rk3308_speaker_ctl(struct rk3308_codec_priv *rk3308, int on) ++{ ++ gpiod_direction_output(rk3308->spk_ctl_gpio, on); ++} ++ ++static int rk3308_codec_reset(struct snd_soc_codec *codec) ++{ ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ ++ reset_control_assert(rk3308->reset); ++ usleep_range(200, 300); /* estimated value */ ++ reset_control_deassert(rk3308->reset); ++ ++ regmap_write(rk3308->regmap, RK3308_GLB_CON, 0x00); ++ usleep_range(200, 300); /* estimated value */ ++ regmap_write(rk3308->regmap, RK3308_GLB_CON, ++ RK3308_SYS_WORK | ++ RK3308_DAC_DIG_WORK | ++ RK3308_ADC_DIG_WORK); ++ ++ return 0; ++} ++ ++static int rk3308_set_bias_level(struct snd_soc_codec *codec, ++ enum snd_soc_bias_level level) ++{ ++ switch (level) { ++ case SND_SOC_BIAS_ON: ++ break; ++ ++ case SND_SOC_BIAS_PREPARE: ++ break; ++ ++ case SND_SOC_BIAS_STANDBY: ++ case SND_SOC_BIAS_OFF: ++ break; ++ } ++ ++ snd_soc_codec_force_bias_level(codec, level); ++ ++ return 0; ++} ++ ++static int rk3308_set_dai_fmt(struct snd_soc_dai *codec_dai, ++ unsigned int fmt) ++{ ++ struct snd_soc_codec *codec = codec_dai->codec; ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ unsigned int adc_aif1 = 0, adc_aif2 = 0, dac_aif1 = 0, dac_aif2 = 0; ++ int ch = rk3308->adc_ch; ++ ++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { ++ case SND_SOC_DAIFMT_CBS_CFS: ++ adc_aif2 |= RK3308_ADC_IO_MODE_SLAVE; ++ adc_aif2 |= RK3308_ADC_MODE_SLAVE; ++ dac_aif2 |= RK3308_DAC_IO_MODE_SLAVE; ++ dac_aif2 |= RK3308_DAC_MODE_SLAVE; ++ break; ++ case SND_SOC_DAIFMT_CBM_CFM: ++ adc_aif2 |= RK3308_ADC_IO_MODE_MASTER; ++ adc_aif2 |= RK3308_ADC_MODE_MASTER; ++ dac_aif2 |= RK3308_DAC_IO_MODE_MASTER; ++ dac_aif2 |= RK3308_DAC_MODE_MASTER; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { ++ case SND_SOC_DAIFMT_DSP_A: ++ adc_aif1 |= RK3308_ADC_I2S_MODE_PCM; ++ dac_aif1 |= RK3308_DAC_I2S_MODE_PCM; ++ break; ++ case SND_SOC_DAIFMT_I2S: ++ adc_aif1 |= RK3308_ADC_I2S_MODE_I2S; ++ dac_aif1 |= RK3308_DAC_I2S_MODE_I2S; ++ break; ++ case SND_SOC_DAIFMT_RIGHT_J: ++ adc_aif1 |= RK3308_ADC_I2S_MODE_RJ; ++ dac_aif1 |= RK3308_DAC_I2S_MODE_RJ; ++ break; ++ case SND_SOC_DAIFMT_LEFT_J: ++ adc_aif1 |= RK3308_ADC_I2S_MODE_RJ; ++ dac_aif1 |= RK3308_DAC_I2S_MODE_LJ; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { ++ case SND_SOC_DAIFMT_NB_NF: ++ adc_aif1 |= RK3308_ADC_I2S_LRC_POL_NORMAL; ++ adc_aif2 |= RK3308_ADC_I2S_BIT_CLK_POL_NORMAL; ++ dac_aif1 |= RK3308_DAC_I2S_LRC_POL_NORMAL; ++ dac_aif2 |= RK3308_DAC_I2S_BIT_CLK_POL_NORMAL; ++ break; ++ case SND_SOC_DAIFMT_IB_IF: ++ adc_aif1 |= RK3308_ADC_I2S_LRC_POL_REVERSAL; ++ adc_aif2 |= RK3308_ADC_I2S_BIT_CLK_POL_REVERSAL; ++ dac_aif1 |= RK3308_DAC_I2S_LRC_POL_REVERSAL; ++ dac_aif2 |= RK3308_DAC_I2S_BIT_CLK_POL_REVERSAL; ++ break; ++ case SND_SOC_DAIFMT_IB_NF: ++ adc_aif1 |= RK3308_ADC_I2S_LRC_POL_NORMAL; ++ adc_aif2 |= RK3308_ADC_I2S_BIT_CLK_POL_REVERSAL; ++ dac_aif1 |= RK3308_DAC_I2S_LRC_POL_NORMAL; ++ dac_aif2 |= RK3308_DAC_I2S_BIT_CLK_POL_REVERSAL; ++ break; ++ case SND_SOC_DAIFMT_NB_IF: ++ adc_aif1 |= RK3308_ADC_I2S_LRC_POL_REVERSAL; ++ adc_aif2 |= RK3308_ADC_I2S_BIT_CLK_POL_NORMAL; ++ dac_aif1 |= RK3308_DAC_I2S_LRC_POL_REVERSAL; ++ dac_aif2 |= RK3308_DAC_I2S_BIT_CLK_POL_NORMAL; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON01(ch), ++ RK3308_ADC_I2S_LRC_POL_MSK | ++ RK3308_ADC_I2S_MODE_MSK, ++ adc_aif1); ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON02(ch), ++ RK3308_ADC_IO_MODE_MSK | ++ RK3308_ADC_MODE_MSK | ++ RK3308_ADC_I2S_BIT_CLK_POL_MSK, ++ adc_aif2); ++ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON01, ++ RK3308_DAC_I2S_LRC_POL_MSK | ++ RK3308_DAC_I2S_MODE_MSK, ++ dac_aif1); ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON02, ++ RK3308_DAC_IO_MODE_MSK | ++ RK3308_DAC_MODE_MSK | ++ RK3308_DAC_I2S_BIT_CLK_POL_MSK, ++ dac_aif2); ++ ++ return 0; ++} ++ ++static int rk3308_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_codec *codec = dai->codec; ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ unsigned int adc_aif1 = 0, adc_aif2 = 0, dac_aif1 = 0, dac_aif2 = 0; ++ int ch = rk3308->adc_ch; ++ ++ switch (params_format(params)) { ++ case SNDRV_PCM_FORMAT_S16_LE: ++ adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_16BITS; ++ dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_16BITS; ++ break; ++ case SNDRV_PCM_FORMAT_S20_3LE: ++ adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_20BITS; ++ dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_20BITS; ++ break; ++ case SNDRV_PCM_FORMAT_S24_LE: ++ adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_24BITS; ++ dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_24BITS; ++ break; ++ case SNDRV_PCM_FORMAT_S32_LE: ++ adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_32BITS; ++ dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_32BITS; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (params_channels(params)) { ++ case 1: ++ adc_aif1 |= RK3308_ADC_I2S_MONO; ++ break; ++ case 2: ++ adc_aif1 |= RK3308_ADC_I2S_STEREO; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ adc_aif1 |= RK3308_ADC_I2S_LR_NORMAL; ++ adc_aif2 |= RK3308_ADC_I2S_WORK; ++ dac_aif1 |= RK3308_DAC_I2S_LR_NORMAL; ++ dac_aif2 |= RK3308_DAC_I2S_WORK; ++ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON01(ch), ++ RK3308_ADC_I2S_VALID_LEN_MSK | ++ RK3308_ADC_I2S_LR_MSK | ++ RK3308_ADC_I2S_TYPE_MSK, ++ adc_aif1); ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON02(ch), ++ RK3308_ADC_I2S_MSK, ++ adc_aif2); ++ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON01, ++ RK3308_DAC_I2S_VALID_LEN_MSK | ++ RK3308_DAC_I2S_LR_MSK, ++ dac_aif1); ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON02, ++ RK3308_DAC_I2S_MSK, ++ dac_aif2); ++ ++ return 0; ++} ++ ++static int rk3308_digital_mute(struct snd_soc_dai *dai, int mute) ++{ ++ return 0; ++} ++ ++static int rk3308_codec_dac_enable(struct rk3308_codec_priv *rk3308) ++{ ++ /* Step 01 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON00, ++ RK3308_DAC_CURRENT_MSK, ++ RK3308_DAC_CURRENT_EN); ++ ++ /* Step 02 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, ++ RK3308_DAC_BUF_REF_L_MSK | ++ RK3308_DAC_BUF_REF_R_MSK, ++ RK3308_DAC_BUF_REF_L_EN | ++ RK3308_DAC_BUF_REF_R_EN); ++ ++ /* Step 03 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, ++ RK3308_DAC_POP_SOUND_L_MSK | ++ RK3308_DAC_POP_SOUND_R_MSK, ++ RK3308_DAC_POP_SOUND_L_WORK | ++ RK3308_DAC_POP_SOUND_R_WORK); ++ ++ /* Step 04 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, ++ RK3308_DAC_L_HPMIX_EN | RK3308_DAC_R_HPMIX_EN, ++ RK3308_DAC_L_HPMIX_EN | RK3308_DAC_R_HPMIX_EN); ++ ++ /* Step 05 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, ++ RK3308_DAC_L_HPMIX_WORK | RK3308_DAC_R_HPMIX_WORK, ++ RK3308_DAC_L_HPMIX_WORK | RK3308_DAC_R_HPMIX_WORK); ++ ++ /* Step 06 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, ++ RK3308_DAC_L_LINEOUT_EN | RK3308_DAC_R_LINEOUT_EN, ++ RK3308_DAC_L_LINEOUT_EN | RK3308_DAC_R_LINEOUT_EN); ++ ++ /* Step 07 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, ++ RK3308_DAC_L_HPOUT_EN | RK3308_DAC_R_HPOUT_EN, ++ RK3308_DAC_L_HPOUT_EN | RK3308_DAC_R_HPOUT_EN); ++ ++ /* Step 08 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, ++ RK3308_DAC_L_HPOUT_WORK | RK3308_DAC_R_HPOUT_WORK, ++ RK3308_DAC_L_HPOUT_WORK | RK3308_DAC_R_HPOUT_WORK); ++ ++ /* Step 09 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, ++ RK3308_DAC_L_REF_EN | RK3308_DAC_R_REF_EN, ++ RK3308_DAC_L_REF_EN | RK3308_DAC_R_REF_EN); ++ ++ /* Step 10 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, ++ RK3308_DAC_L_CLK_EN | RK3308_DAC_R_CLK_EN, ++ RK3308_DAC_L_CLK_EN | RK3308_DAC_R_CLK_EN); ++ ++ /* Step 11 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, ++ RK3308_DAC_L_DAC_EN | RK3308_DAC_R_DAC_EN, ++ RK3308_DAC_L_DAC_EN | RK3308_DAC_R_DAC_EN); ++ ++ /* Step 12 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, ++ RK3308_DAC_L_DAC_WORK | RK3308_DAC_R_DAC_WORK, ++ RK3308_DAC_L_DAC_WORK | RK3308_DAC_R_DAC_WORK); ++ ++ /* Step 13 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON12, ++ RK3308_DAC_L_HPMIX_SEL_MSK | ++ RK3308_DAC_R_HPMIX_SEL_MSK, ++ RK3308_DAC_L_HPMIX_I2S | ++ RK3308_DAC_R_HPMIX_I2S); ++ ++ /* Step 14 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, ++ RK3308_DAC_L_HPMIX_UNMUTE | ++ RK3308_DAC_R_HPMIX_UNMUTE, ++ RK3308_DAC_L_HPMIX_UNMUTE | ++ RK3308_DAC_R_HPMIX_UNMUTE); ++ ++ /* Step 15 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON12, ++ RK3308_DAC_L_HPMIX_GAIN_MSK | ++ RK3308_DAC_R_HPMIX_GAIN_MSK, ++ RK3308_DAC_L_HPMIX_GAIN_0DB | ++ RK3308_DAC_R_HPMIX_GAIN_0DB); ++ ++ /* Step 16 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, ++ RK3308_DAC_L_HPOUT_UNMUTE | ++ RK3308_DAC_R_HPOUT_UNMUTE, ++ RK3308_DAC_L_HPOUT_UNMUTE | ++ RK3308_DAC_R_HPOUT_UNMUTE); ++ ++ /* Step 17 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, ++ RK3308_DAC_L_LINEOUT_UNMUTE | ++ RK3308_DAC_R_LINEOUT_UNMUTE, ++ RK3308_DAC_L_LINEOUT_UNMUTE | ++ RK3308_DAC_R_LINEOUT_UNMUTE); ++ ++ /* Step 18 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON05, ++ RK3308_DAC_L_HPOUT_GAIN_MSK, ++ RK3308_DAC_L_HPOUT_GAIN_0DB); ++ ++ /* Step 18 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON06, ++ RK3308_DAC_R_HPOUT_GAIN_MSK, ++ RK3308_DAC_R_HPOUT_GAIN_0DB); ++ ++ /* Step 19 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, ++ RK3308_DAC_L_GAIN_MSK | RK3308_DAC_R_GAIN_MSK, ++ RK3308_DAC_L_GAIN_0DB | RK3308_DAC_R_GAIN_0DB); ++ ++ return 0; ++} ++ ++static int rk3308_codec_dac_disable(struct rk3308_codec_priv *rk3308) ++{ ++ /* Step 01 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, ++ RK3308_DAC_L_GAIN_MSK | RK3308_DAC_R_GAIN_MSK, ++ RK3308_DAC_L_GAIN_0DB | RK3308_DAC_R_GAIN_0DB); ++ ++ /* ++ * Step 02 ++ * ++ * Note1. In the step2, adjusting the register step by step to the ++ * appropriate value and taking 20ms as time step ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON05, ++ RK3308_DAC_L_HPOUT_GAIN_MSK, ++ RK3308_DAC_L_HPOUT_GAIN_NDB_39); ++ ++ /* Step 02 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON06, ++ RK3308_DAC_R_HPOUT_GAIN_MSK, ++ RK3308_DAC_R_HPOUT_GAIN_NDB_39); ++ ++ /* Step 03 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, ++ RK3308_DAC_L_HPMIX_UNMUTE | ++ RK3308_DAC_R_HPMIX_UNMUTE, ++ RK3308_DAC_L_HPMIX_MUTE | ++ RK3308_DAC_R_HPMIX_MUTE); ++ ++ /* Step 04 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON12, ++ RK3308_DAC_L_HPMIX_SEL_MSK | ++ RK3308_DAC_R_HPMIX_SEL_MSK, ++ RK3308_DAC_L_HPMIX_NONE | ++ RK3308_DAC_R_HPMIX_NONE); ++ ++ /* Step 05 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, ++ RK3308_DAC_L_HPOUT_UNMUTE | ++ RK3308_DAC_R_HPOUT_UNMUTE, ++ RK3308_DAC_L_HPOUT_MUTE | ++ RK3308_DAC_R_HPOUT_MUTE); ++ ++ /* Step 06 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, ++ RK3308_DAC_L_DAC_WORK | RK3308_DAC_R_DAC_WORK, ++ RK3308_DAC_L_DAC_INIT | RK3308_DAC_R_DAC_INIT); ++ ++ /* Step 07 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, ++ RK3308_DAC_L_HPOUT_EN | RK3308_DAC_R_HPOUT_EN, ++ RK3308_DAC_L_HPOUT_DIS | RK3308_DAC_R_HPOUT_DIS); ++ ++ /* Step 08 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, ++ RK3308_DAC_L_LINEOUT_UNMUTE | ++ RK3308_DAC_R_LINEOUT_UNMUTE, ++ RK3308_DAC_L_LINEOUT_MUTE | ++ RK3308_DAC_R_LINEOUT_MUTE); ++ ++ /* Step 09 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, ++ RK3308_DAC_L_LINEOUT_EN | RK3308_DAC_R_LINEOUT_EN, ++ RK3308_DAC_L_LINEOUT_DIS | RK3308_DAC_R_LINEOUT_DIS); ++ ++ /* Step 10 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, ++ RK3308_DAC_L_HPMIX_EN | RK3308_DAC_R_HPMIX_EN, ++ RK3308_DAC_L_HPMIX_DIS | RK3308_DAC_R_HPMIX_DIS); ++ ++ /* Step 11 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, ++ RK3308_DAC_L_DAC_EN | RK3308_DAC_R_DAC_EN, ++ RK3308_DAC_L_DAC_DIS | RK3308_DAC_R_DAC_DIS); ++ ++ /* Step 12 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, ++ RK3308_DAC_L_CLK_EN | RK3308_DAC_R_CLK_EN, ++ RK3308_DAC_L_CLK_DIS | RK3308_DAC_R_CLK_DIS); ++ ++ /* Step 13 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, ++ RK3308_DAC_L_REF_EN | RK3308_DAC_R_REF_EN, ++ RK3308_DAC_L_REF_DIS | RK3308_DAC_R_REF_DIS); ++ ++ /* Step 14 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, ++ RK3308_DAC_POP_SOUND_L_MSK | ++ RK3308_DAC_POP_SOUND_R_MSK, ++ RK3308_DAC_POP_SOUND_L_INIT | ++ RK3308_DAC_POP_SOUND_R_INIT); ++ ++ /* Step 15 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, ++ RK3308_DAC_BUF_REF_L_EN | RK3308_DAC_BUF_REF_R_EN, ++ RK3308_DAC_BUF_REF_L_DIS | RK3308_DAC_BUF_REF_R_DIS); ++ ++ /* Step 16 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON00, ++ RK3308_DAC_CURRENT_EN, ++ RK3308_DAC_CURRENT_DIS); ++ ++ /* Step 17 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, ++ RK3308_DAC_L_HPOUT_WORK | RK3308_DAC_R_HPOUT_WORK, ++ RK3308_DAC_L_HPOUT_INIT | RK3308_DAC_R_HPOUT_INIT); ++ ++ /* Step 18 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, ++ RK3308_DAC_L_HPMIX_WORK | RK3308_DAC_R_HPMIX_WORK, ++ RK3308_DAC_L_HPMIX_INIT | RK3308_DAC_R_HPMIX_INIT); ++ ++ /* Step 19 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON12, ++ RK3308_DAC_L_HPMIX_GAIN_MSK | ++ RK3308_DAC_R_HPMIX_GAIN_MSK, ++ RK3308_DAC_L_HPMIX_GAIN_NDB_6 | ++ RK3308_DAC_R_HPMIX_GAIN_NDB_6); ++ ++ /* ++ * Note2. If the ACODEC_DAC_ANA_CON12[7] or ACODEC_DAC_ANA_CON12[3] ++ * is set to 0x1, add the steps from the section Disable DAC ++ * Configuration Standard Usage Flow after complete the step 19 ++ */ ++ ++ return 0; ++} ++ ++static int rk3308_codec_power_on(struct snd_soc_codec *codec) ++{ ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ ++ /* 1. Supply the power of digital part and reset the Audio Codec */ ++ /* Do nothing */ ++ ++ /* ++ * 2. Configure ACODEC_DAC_ANA_CON1[1:0] and ACODEC_DAC_ANA_CON1[5:4] ++ * to 0x1, to setup dc voltage of the DAC channel output ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, ++ RK3308_DAC_POP_SOUND_L_MSK, RK3308_DAC_POP_SOUND_L_INIT); ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, ++ RK3308_DAC_POP_SOUND_R_MSK, RK3308_DAC_POP_SOUND_R_INIT); ++ ++ /* ++ * 3. Configure the register ACODEC_ADC_ANA_CON10[6:0] to 0x1 ++ * ++ * Note: Only the reg (ADC_ANA_CON10+0x0)[6:0] represent the control ++ * signal to select current to pre-charge/dis_charge ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), ++ RK3308_ADC_CURRENT_CHARGE_MSK, RK3308_ADC_SEL_I_64(1)); ++ ++ /* 4. Supply the power of the analog part(AVDD,AVDDRV) */ ++ ++ /* ++ * 5. Configure the register ACODEC_ADC_ANA_CON10[7] to 0x1 to setup ++ * reference voltage ++ * ++ * Note: Only the reg (ADC_ANA_CON10+0x0)[7] represent the enable ++ * signal of reference voltage module ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), ++ RK3308_ADC_REF_EN, RK3308_ADC_REF_EN); ++ ++ /* ++ * 6. Change the register ACODEC_ADC_ANA_CON10[6:0] from the 0x1 to ++ * 0x7f step by step or configure the ACODEC_ADC_ANA_CON10[6:0] to ++ * 0x7f directly. The suggestion slot time of the step is 20ms. ++ */ ++ mdelay(20); ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), ++ RK3308_ADC_CURRENT_CHARGE_MSK, ++ RK3308_ADC_DONT_SEL_ALL); ++ ++ /* 7. Wait until the voltage of VCM keeps stable at the AVDD/2 */ ++ usleep_range(200, 300); /* estimated value */ ++ ++ /* ++ * 8. Configure the register ACODEC_ADC_ANA_CON10[6:0] to the ++ * appropriate value(expect 0x0) for reducing power. ++ */ ++ ++ /* TODO: choose an appropriate charge value */ ++ ++ return 0; ++} ++ ++static int rk3308_codec_power_off(struct snd_soc_codec *codec) ++{ ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ ++ /* ++ * 1. Keep the power on and disable the DAC and ADC path according to ++ * the section power on configuration standard usage flow. ++ */ ++ ++ /* 2. Configure the register ACODEC_ADC_ANA_CON10[6:0] to 0x1 */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), ++ RK3308_ADC_CURRENT_CHARGE_MSK, RK3308_ADC_SEL_I_64(1)); ++ ++ /* 3. Configure the register ACODEC_ADC_ANA_CON10[7] to 0x0 */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), ++ RK3308_ADC_REF_EN, RK3308_ADC_REF_DIS); ++ ++ /* ++ * 4.Change the register ACODEC_ADC_ANA_CON10[6:0] from the 0x1 to 0x7f ++ * step by step or configure the ACODEC_ADC_ANA_CON10[6:0] to 0x7f ++ * directly. The suggestion slot time of the step is 20ms ++ */ ++ mdelay(20); ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), ++ RK3308_ADC_CURRENT_CHARGE_MSK, ++ RK3308_ADC_DONT_SEL_ALL); ++ ++ /* 5. Wait until the voltage of VCM keeps stable at the AGND */ ++ usleep_range(200, 300); /* estimated value */ ++ ++ /* 6. Power off the analog power supply */ ++ /* 7. Power off the digital power supply */ ++ ++ /* Do something via hardware */ ++ ++ return 0; ++} ++ ++static int check_micbias(int micbias) ++{ ++ switch (micbias) { ++ case RK3308_ADC_MICBIAS_VOLT_0_85: ++ case RK3308_ADC_MICBIAS_VOLT_0_8: ++ case RK3308_ADC_MICBIAS_VOLT_0_75: ++ case RK3308_ADC_MICBIAS_VOLT_0_7: ++ case RK3308_ADC_MICBIAS_VOLT_0_65: ++ case RK3308_ADC_MICBIAS_VOLT_0_6: ++ case RK3308_ADC_MICBIAS_VOLT_0_55: ++ case RK3308_ADC_MICBIAS_VOLT_0_5: ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++static int rk3308_codec_micbias_enable(struct rk3308_codec_priv *rk3308, ++ int micbias) ++{ ++ int ch = rk3308->adc_ch; ++ int ret; ++ ++ if (ch != 1 && ch != 2) { ++ dev_err(rk3308->plat_dev, ++ "%s: currnet ch: %d, only ch1/2 control MICBIAS1/2\n", ++ __func__, ch); ++ return -EINVAL; ++ } ++ ++ /* 1. Power up the ACODEC and keep the AVDDH stable */ ++ ++ /* 2. Configure ACODEC_ADC_ANA_CON7[2:0] to the certain value */ ++ ret = check_micbias(micbias); ++ if (ret < 0) { ++ dev_err(rk3308->plat_dev, "This is an invalid micbias: %d\n", ++ micbias); ++ return ret; ++ } ++ ++ /* ++ * Note: Only the reg (ADC_ANA_CON7+0x0)[2:0] represent the level range ++ * control signal of MICBIAS voltage ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(0), ++ RK3308_ADC_LEVEL_RANGE_MICBIAS_MSK, ++ micbias); ++ ++ /* 3. Wait until the VCMH keep stable */ ++ usleep_range(200, 300); /* estimated value */ ++ ++ /* 4. Configure ACODEC_ADC_ANA_CON8[4] to 0x1 */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON08(ch), ++ RK3308_ADC_MICBIAS_CURRENT_MSK, ++ RK3308_ADC_MICBIAS_CURRENT_EN); ++ ++ /* ++ * 5. Configure the (ADC_ANA_CON7+0x40)[3] or (ADC_ANA_CON7+0x80)[3] ++ * to 0x1. ++ * (ADC_ANA_CON7+0x40)[3] used to control the MICBIAS1, and ++ * (ADC_ANA_CON7+0x80)[3] used to control the MICBIAS2 ++ */ ++ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(ch), ++ RK3308_ADC_MIC_BIAS_BUF_EN, ++ RK3308_ADC_MIC_BIAS_BUF_EN); ++ ++ return 0; ++} ++ ++static int rk3308_codec_micbias_disable(struct rk3308_codec_priv *rk3308) ++{ ++ int ch = rk3308->adc_ch; ++ ++ if (ch != 1 && ch != 2) { ++ dev_err(rk3308->plat_dev, ++ "%s: currnet ch: %d, only ch1/2 control MICBIAS1/2\n", ++ __func__, ch); ++ return -EINVAL; ++ } ++ ++ /* 1. Enable the MICBIAS and keep the Audio Codec stable */ ++ /* Do nothing */ ++ ++ /* ++ * 2. Configure the (ADC_ANA_CON7+0x40)[3] or ++ * (ADC_ANA_CON7+0x80)[3] to 0x0 ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(ch), ++ RK3308_ADC_MIC_BIAS_BUF_EN, ++ RK3308_ADC_MIC_BIAS_BUF_DIS); ++ ++ /* 3. Configure ACODEC_ADC_ANA_CON8[4] to 0x0 */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON08(ch), ++ RK3308_ADC_MICBIAS_CURRENT_MSK, ++ RK3308_ADC_MICBIAS_CURRENT_DIS); ++ ++ return 0; ++} ++ ++static int rk3308_codec_alc_enable(struct rk3308_codec_priv *rk3308) ++{ ++ int ch = rk3308->adc_ch; ++ ++ /* ++ * 1. Set he max level and min level of the ALC need to control. ++ * ++ * These values are estimated ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_ALC_L_DIG_CON05(ch), ++ RK3308_AGC_LO_8BITS_AGC_MIN_MSK, ++ 0x16); ++ regmap_update_bits(rk3308->regmap, RK3308_ALC_L_DIG_CON05(ch), ++ RK3308_AGC_HI_8BITS_AGC_MIN_MSK, ++ 0x40); ++ ++ regmap_update_bits(rk3308->regmap, RK3308_ALC_R_DIG_CON05(ch), ++ RK3308_AGC_LO_8BITS_AGC_MAX_MSK, ++ 0x26); ++ regmap_update_bits(rk3308->regmap, RK3308_ALC_R_DIG_CON05(ch), ++ RK3308_AGC_HI_8BITS_AGC_MAX_MSK, ++ 0x40); ++ ++ /* ++ * 2. Set ACODEC_ALC_DIG_CON4[2:0] according to the sample rate ++ * ++ * By default is 44.1KHz for sample. ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_ALC_L_DIG_CON04(ch), ++ RK3308_AGC_APPROX_RATE_MSK, ++ RK3308_AGC_APPROX_RATE_44_1K); ++ regmap_update_bits(rk3308->regmap, RK3308_ALC_R_DIG_CON04(ch), ++ RK3308_AGC_APPROX_RATE_MSK, ++ RK3308_AGC_APPROX_RATE_44_1K); ++ ++ /* 3. Set ACODEC_ALC_DIG_CON9[6] to 0x1, to enable the ALC module */ ++ regmap_update_bits(rk3308->regmap, RK3308_ALC_L_DIG_CON09(ch), ++ RK3308_AGC_FUNC_SEL_MSK, ++ RK3308_AGC_FUNC_SEL_EN); ++ regmap_update_bits(rk3308->regmap, RK3308_ALC_R_DIG_CON09(ch), ++ RK3308_AGC_FUNC_SEL_MSK, ++ RK3308_AGC_FUNC_SEL_EN); ++ ++ /* ++ * 4. Set ACODEC_ADC_ANA_CON11[1:0], (ACODEC_ADC_ANA_CON11+0x40)[1:0], ++ * (ACODEC_ADC_ANA_CON11+0x80)[1:0] and (ACODEC_ADC_ANA_CON11+0xc0)[1:0] ++ * to 0x3, to enable the ALC module to control the gain of PGA. ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON11(ch), ++ RK3308_ADC_ALCL_CON_GAIN_PGAL_MSK | ++ RK3308_ADC_ALCR_CON_GAIN_PGAR_MSK, ++ RK3308_ADC_ALCL_CON_GAIN_PGAL_EN | ++ RK3308_ADC_ALCR_CON_GAIN_PGAR_EN); ++ ++ /* ++ * 5.Observe the current ALC output gain by reading ++ * ACODEC_ALC_DIG_CON12[4:0] ++ * ++ * The default GAIN is 0x0c ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_ALC_L_DIG_CON12(ch), ++ RK3308_AGC_GAIN_MSK, ++ 0x0c); ++ regmap_update_bits(rk3308->regmap, RK3308_ALC_R_DIG_CON12(ch), ++ RK3308_AGC_GAIN_MSK, ++ 0x0c); ++ ++ return 0; ++} ++ ++static int rk3308_codec_alc_disable(struct rk3308_codec_priv *rk3308) ++{ ++ int ch = rk3308->adc_ch; ++ ++ /* ++ * 1. Set ACODEC_ALC_DIG_CON9[6] to 0x0, to disable the ALC module, ++ * then the ALC output gain will keep to the last value ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_ALC_L_DIG_CON09(ch), ++ RK3308_AGC_FUNC_SEL_MSK, ++ RK3308_AGC_FUNC_SEL_DIS); ++ regmap_update_bits(rk3308->regmap, RK3308_ALC_R_DIG_CON09(ch), ++ RK3308_AGC_FUNC_SEL_MSK, ++ RK3308_AGC_FUNC_SEL_DIS); ++ ++ /* ++ * 2. Set ACODEC_ADC_ANA_CON11[1:0], (ACODEC_ADC_ANA_CON11+0x40)[1:0], ++ * (ACODEC_ADC_ANA_CON11+0x80)[1:0] and (ACODEC_ADC_ANA_CON11+0xc0)[1:0] ++ * to 0x0, to disable the ALC module to control the gain of PGA. ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON11(ch), ++ RK3308_ADC_ALCL_CON_GAIN_PGAL_MSK | ++ RK3308_ADC_ALCR_CON_GAIN_PGAR_MSK, ++ RK3308_ADC_ALCL_CON_GAIN_PGAL_DIS | ++ RK3308_ADC_ALCR_CON_GAIN_PGAR_DIS); ++ ++ return 0; ++} ++ ++static int rk3308_codec_adc_ana_enable(struct rk3308_codec_priv *rk3308) ++{ ++ unsigned int adc_aif1 = 0, adc_aif2 = 0; ++ unsigned int agc_func_en; ++ int ch = rk3308->adc_ch; ++ ++ /* ++ * 1. Set the ACODEC_ADC_ANA_CON7[7:6] and ACODEC_ADC_ANA_CON7[5:4], ++ * to select the line-in or microphone as input of ADC ++ * ++ * Note1. Please ignore the step1 for enabling ADC3, ADC4, ADC5, ++ * ADC6, ADC7, and ADC8 ++ */ ++ if (ch == 0) { ++ if (rk3308->adc_ch0_using_linein) { ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(ch), ++ RK3308_ADC_CH1_IN_SEL_MSK, ++ RK3308_ADC_CH1_IN_LINEIN); ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(ch), ++ RK3308_ADC_CH2_IN_SEL_MSK, ++ RK3308_ADC_CH2_IN_LINEIN); ++ } else { ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(ch), ++ RK3308_ADC_CH1_IN_SEL_MSK, ++ RK3308_ADC_CH1_IN_MIC); ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(ch), ++ RK3308_ADC_CH2_IN_SEL_MSK, ++ RK3308_ADC_CH2_IN_MIC); ++ } ++ } ++ ++ /* ++ * 2. Set ACODEC_ADC_ANA_CON0[7:0] to 0xff, to end the mute station ++ * of ADC, to enable the MIC module, to enable the reference voltage ++ * buffer, and to end the initialization of MIC ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(ch), ++ RK3308_ADC_CH1_CH2_MIC_ALL_MSK, ++ RK3308_ADC_CH1_CH2_MIC_ALL); ++ ++ /* ++ * 3. Set ACODEC_ADC_ANA_CON6[0] to 0x1, to enable the current source ++ * of audio ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON06(ch), ++ RK3308_ADC_CURRENT_MSK, ++ RK3308_ADC_CURRENT_EN); ++ ++ /* ++ * 4. Set ACODEC_ADC_ANA_CON2[7:0] to 0x77, to enable the ALC module, ++ * to enable the zero-crossing detection function, and to end the ++ * initialization of ALC ++ * ++ * Note2. Please set ACODEC_ADC_ANA_CON2[7:0] to 0x33 in step4 ++ * if the AGC function is closed ++ */ ++ ++ adc_aif1 = RK3308_ADC_CH1_ALC_EN | RK3308_ADC_CH1_ALC_WORK; ++ regmap_read(rk3308->regmap, RK3308_ALC_L_DIG_CON09(ch), &agc_func_en); ++ if (agc_func_en & RK3308_AGC_FUNC_SEL_EN) ++ adc_aif1 |= RK3308_ADC_CH1_ZEROCROSS_DET_EN; ++ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(ch), ++ RK3308_ADC_CH1_ALC_ZC_MSK, ++ adc_aif1); ++ ++ adc_aif2 = RK3308_ADC_CH2_ALC_EN | RK3308_ADC_CH2_ALC_WORK; ++ regmap_read(rk3308->regmap, RK3308_ALC_L_DIG_CON09(ch), &agc_func_en); ++ if (agc_func_en & RK3308_AGC_FUNC_SEL_EN) ++ adc_aif2 |= RK3308_ADC_CH2_ZEROCROSS_DET_EN; ++ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(ch), ++ RK3308_ADC_CH2_ALC_ZC_MSK, ++ adc_aif2); ++ ++ /* ++ * 5. Set ACODEC_ADC_ANA_CON5[7:0] to 0x77, to enable the clock and ++ * ADC module, and to end the initialization of ADC ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(ch), ++ RK3308_ADC_CH1_ADC_CLK_MSK, ++ RK3308_ADC_CH1_CLK_EN | ++ RK3308_ADC_CH1_ADC_EN | ++ RK3308_ADC_CH1_ADC_WORK); ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(ch), ++ RK3308_ADC_CH2_ADC_CLK_MSK, ++ RK3308_ADC_CH2_CLK_EN | ++ RK3308_ADC_CH2_ADC_EN | ++ RK3308_ADC_CH2_ADC_WORK); ++ ++ /* ++ * 6. Set ACODEC_ADC_ANA_CON1[5:4] and ACODEC_ADC_ANA_CON1[1:0], ++ * to select the gain of the MIC ++ * ++ * By default is 0db. ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(ch), ++ RK3308_ADC_CH1_MIC_GAIN_MSK, ++ RK3308_ADC_CH1_MIC_GAIN_0DB); ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(ch), ++ RK3308_ADC_CH2_MIC_GAIN_MSK, ++ RK3308_ADC_CH2_MIC_GAIN_0DB); ++ ++ /* ++ * 7.Set ACODEC_ADC_ANA_CON3[4:0] and ACODEC_ADC_ANA_CON4[3:0] to ++ * select the gain of ALC ++ * ++ * By default is 0db. ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON03(ch), ++ RK3308_ADC_CH1_ALC_GAIN_MSK, ++ RK3308_ADC_CH1_ALC_GAIN_0DB); ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON04(ch), ++ RK3308_ADC_CH2_ALC_GAIN_MSK, ++ RK3308_ADC_CH2_ALC_GAIN_0DB); ++ ++ /* 8.Begin recording */ ++ ++ return 0; ++} ++ ++static int rk3308_codec_adc_ana_disable(struct rk3308_codec_priv *rk3308) ++{ ++ int ch = rk3308->adc_ch; ++ ++ /* ++ * 1. Set ACODEC_ADC_ANA_CON2[7:0] to 0x0, to disable the ALC module, ++ * to disable the zero-crossing detection function, and to begin the ++ * initialization of ALC ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(ch), ++ RK3308_ADC_CH1_ALC_ZC_MSK, ++ 0); ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(ch), ++ RK3308_ADC_CH2_ALC_ZC_MSK, ++ 0); ++ ++ /* ++ * 2. Set ACODEC_ADC_ANA_CON5[7:0] to 0x0, to disable the clock and ++ * ADC module, and to begin the initialization of ADC ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(ch), ++ RK3308_ADC_CH1_ADC_CLK_MSK, ++ 0); ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(ch), ++ RK3308_ADC_CH2_ADC_CLK_MSK, ++ 0); ++ ++ /* ++ * 3. Set ACODEC_ADC_ANA_CON0[7:0] to 0x88, to disable the MIC module, ++ * to disable the reference voltage buffer, and to begin the ++ * initialization of MIC ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(ch), ++ RK3308_ADC_CH1_CH2_MIC_ALL_MSK, ++ RK3308_ADC_CH1_MIC_UNMUTE | ++ RK3308_ADC_CH2_MIC_UNMUTE); ++ ++ /* ++ * 4. Set ACODEC_ADC_ANA_CON6[0] to 0x0, to disable the current ++ * source of audio ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON06(ch), ++ RK3308_ADC_CURRENT_MSK, ++ RK3308_ADC_CURRENT_DIS); ++ ++ return 0; ++} ++ ++static int rk3308_codec_open_capture(struct snd_soc_codec *codec) ++{ ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ ++ rk3308_codec_alc_enable(rk3308); ++ rk3308_codec_adc_ana_enable(rk3308); ++ ++ return 0; ++} ++ ++static int rk3308_codec_close_capture(struct snd_soc_codec *codec) ++{ ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ ++ rk3308_codec_alc_disable(rk3308); ++ rk3308_codec_adc_ana_disable(rk3308); ++ ++ return 0; ++} ++ ++static int rk3308_codec_open_playback(struct snd_soc_codec *codec) ++{ ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ ++ rk3308_codec_dac_enable(rk3308); ++ rk3308_speaker_ctl(rk3308, 1); ++ ++ return 0; ++} ++ ++static int rk3308_codec_close_playback(struct snd_soc_codec *codec) ++{ ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ ++ rk3308_speaker_ctl(rk3308, 0); ++ rk3308_codec_dac_disable(rk3308); ++ ++ return 0; ++} ++ ++static int rk3308_pcm_startup(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_codec *codec = dai->codec; ++ int ret = 0; ++ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ++ ret = rk3308_codec_open_playback(codec); ++ else ++ ret = rk3308_codec_open_capture(codec); ++ ++ return ret; ++} ++ ++static void rk3308_pcm_shutdown(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_codec *codec = dai->codec; ++ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ++ rk3308_codec_close_playback(codec); ++ else ++ rk3308_codec_close_capture(codec); ++} ++ ++static struct snd_soc_dai_ops rk3308_dai_ops = { ++ .hw_params = rk3308_hw_params, ++ .set_fmt = rk3308_set_dai_fmt, ++ .digital_mute = rk3308_digital_mute, ++ .startup = rk3308_pcm_startup, ++ .shutdown = rk3308_pcm_shutdown, ++}; ++ ++static struct snd_soc_dai_driver rk3308_dai[] = { ++ { ++ .name = "rk3308-hifi", ++ .id = RK3308_HIFI, ++ .playback = { ++ .stream_name = "HiFi Playback", ++ .channels_min = 2, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_8000_96000, ++ .formats = (SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_S20_3LE | ++ SNDRV_PCM_FMTBIT_S24_LE | ++ SNDRV_PCM_FMTBIT_S32_LE), ++ }, ++ .capture = { ++ .stream_name = "HiFi Capture", ++ .channels_min = 1, ++ .channels_max = 8, ++ .rates = SNDRV_PCM_RATE_8000_96000, ++ .formats = (SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_S20_3LE | ++ SNDRV_PCM_FMTBIT_S24_LE | ++ SNDRV_PCM_FMTBIT_S32_LE), ++ }, ++ .ops = &rk3308_dai_ops, ++ }, ++}; ++ ++static int rk3308_suspend(struct snd_soc_codec *codec) ++{ ++ rk3308_set_bias_level(codec, SND_SOC_BIAS_OFF); ++ ++ return 0; ++} ++ ++static int rk3308_resume(struct snd_soc_codec *codec) ++{ ++ rk3308_set_bias_level(codec, SND_SOC_BIAS_STANDBY); ++ ++ return 0; ++} ++ ++static int rk3308_probe(struct snd_soc_codec *codec) ++{ ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ ++ rk3308_codec_reset(codec); ++ rk3308_codec_power_on(codec); ++ ++ rk3308_codec_micbias_enable(rk3308, RK3308_ADC_MICBIAS_VOLT_0_7); ++ ++ return 0; ++} ++ ++static int rk3308_remove(struct snd_soc_codec *codec) ++{ ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ ++ rk3308_speaker_ctl(rk3308, 0); ++ rk3308_codec_micbias_disable(rk3308); ++ rk3308_codec_power_off(codec); ++ ++ return 0; ++} ++ ++static struct snd_soc_codec_driver soc_codec_dev_rk3308 = { ++ .probe = rk3308_probe, ++ .remove = rk3308_remove, ++ .suspend = rk3308_suspend, ++ .resume = rk3308_resume, ++ .set_bias_level = rk3308_set_bias_level, ++ .controls = rk3308_codec_dapm_controls, ++ .num_controls = ARRAY_SIZE(rk3308_codec_dapm_controls), ++}; ++ ++static const struct reg_default rk3308_codec_reg_defaults[] = { ++ { RK3308_GLB_CON, 0x07 }, ++}; ++ ++static bool rk3308_codec_write_read_reg(struct device *dev, unsigned int reg) ++{ ++ /* All registers can be read / write */ ++ return true; ++} ++ ++static bool rk3308_codec_volatile_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case RK3308_GLB_CON: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static const struct regmap_config rk3308_codec_regmap_config = { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .max_register = RK3308_DAC_ANA_CON13, ++ .writeable_reg = rk3308_codec_write_read_reg, ++ .readable_reg = rk3308_codec_write_read_reg, ++ .volatile_reg = rk3308_codec_volatile_reg, ++ .reg_defaults = rk3308_codec_reg_defaults, ++ .num_reg_defaults = ARRAY_SIZE(rk3308_codec_reg_defaults), ++ .cache_type = REGCACHE_FLAT, ++}; ++ ++static ssize_t adc_ch_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct rk3308_codec_priv *rk3308 = ++ container_of(dev, struct rk3308_codec_priv, dev); ++ ++ return sprintf(buf, "adc_ch: %d\n", rk3308->adc_ch); ++} ++ ++static ssize_t adc_ch_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct rk3308_codec_priv *rk3308 = ++ container_of(dev, struct rk3308_codec_priv, dev); ++ unsigned long ch; ++ int ret = kstrtoul(buf, 10, &ch); ++ ++ if (ret < 0 || ch > 4) { ++ dev_err(dev, "Invalid ch: %ld, ret: %d\n", ch, ret); ++ return -EINVAL; ++ } ++ ++ rk3308->adc_ch = ch; ++ ++ dev_info(dev, "Store ch: %d\n", rk3308->adc_ch); ++ ++ return count; ++} ++ ++static const struct device_attribute adc_ch_attrs[] = { ++ __ATTR(adc_ch, 0644, adc_ch_show, adc_ch_store), ++}; ++ ++static void rk3308_codec_device_release(struct device *dev) ++{ ++ /* Do nothing */ ++} ++ ++static int rk3308_codec_sysfs_init(struct platform_device *pdev, ++ struct rk3308_codec_priv *rk3308) ++{ ++ struct device *dev = &rk3308->dev; ++ int i; ++ ++ dev->release = rk3308_codec_device_release; ++ dev->parent = &pdev->dev; ++ set_dev_node(dev, dev_to_node(&pdev->dev)); ++ dev_set_name(dev, "rk3308-acodec-dev"); ++ ++ if (device_register(dev)) { ++ dev_err(&pdev->dev, ++ "Register 'rk3308-acodec-dev' failed\n"); ++ dev->parent = NULL; ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(adc_ch_attrs); i++) { ++ if (device_create_file(dev, &adc_ch_attrs[i])) { ++ dev_err(&pdev->dev, ++ "Create 'rk3308-acodec-dev' attr failed\n"); ++ device_unregister(dev); ++ return -ENOMEM; ++ } ++ } ++ ++ return 0; ++} ++ ++static int rk3308_platform_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct rk3308_codec_priv *rk3308; ++ struct resource *res; ++ void __iomem *base; ++ int ret = 0; ++ struct regmap *grf; ++ ++ grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); ++ if (IS_ERR(grf)) { ++ dev_err(&pdev->dev, ++ "Missing 'rockchip,grf' property\n"); ++ return PTR_ERR(grf); ++ } ++ ++ rk3308 = devm_kzalloc(&pdev->dev, sizeof(*rk3308), GFP_KERNEL); ++ if (!rk3308) ++ return -ENOMEM; ++ ++ ret = rk3308_codec_sysfs_init(pdev, rk3308); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "Sysfs init failed\n"); ++ return ret; ++ } ++ ++ rk3308->plat_dev = &pdev->dev; ++ ++ rk3308->reset = devm_reset_control_get(&pdev->dev, "acodec-reset"); ++ if (IS_ERR(rk3308->reset)) { ++ ret = PTR_ERR(rk3308->reset); ++ if (ret != -ENOENT) ++ return ret; ++ ++ dev_dbg(&pdev->dev, "No reset control found\n"); ++ rk3308->reset = NULL; ++ } ++ ++ /* GPIO0_A5 control speaker on RK3308 EVB */ ++ rk3308->spk_ctl_gpio = devm_gpiod_get_optional(&pdev->dev, "spk_ctl", ++ GPIOD_OUT_HIGH); ++ if (IS_ERR(rk3308->spk_ctl_gpio)) { ++ ret = PTR_ERR(rk3308->spk_ctl_gpio); ++ dev_err(&pdev->dev, "Unable to claim gpio spk_ctl\n"); ++ return ret; ++ } ++ ++ rk3308->pclk = devm_clk_get(&pdev->dev, "acodec"); ++ if (IS_ERR(rk3308->pclk)) { ++ dev_err(&pdev->dev, "Can't get acodec pclk\n"); ++ return PTR_ERR(rk3308->pclk); ++ } ++ ++ ret = clk_prepare_enable(rk3308->pclk); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "Failed to enable acodec pclk: %d\n", ret); ++ return ret; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(base)) { ++ ret = PTR_ERR(base); ++ dev_err(&pdev->dev, "Failed to ioremap resource\n"); ++ goto failed; ++ } ++ ++ rk3308->regmap = devm_regmap_init_mmio(&pdev->dev, base, ++ &rk3308_codec_regmap_config); ++ if (IS_ERR(rk3308->regmap)) { ++ ret = PTR_ERR(rk3308->regmap); ++ dev_err(&pdev->dev, "Failed to regmap mmio\n"); ++ goto failed; ++ } ++ ++ platform_set_drvdata(pdev, rk3308); ++ ++ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_rk3308, ++ rk3308_dai, ARRAY_SIZE(rk3308_dai)); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "Failed to register codec: %d\n", ret); ++ goto failed; ++ } ++ ++ return ret; ++ ++failed: ++ clk_disable_unprepare(rk3308->pclk); ++ ++ return ret; ++} ++ ++static int rk3308_platform_remove(struct platform_device *pdev) ++{ ++ struct rk3308_codec_priv *rk3308 = ++ (struct rk3308_codec_priv *)platform_get_drvdata(pdev); ++ ++ clk_disable_unprepare(rk3308->pclk); ++ snd_soc_unregister_codec(&pdev->dev); ++ ++ return 0; ++} ++ ++static const struct of_device_id rk3308codec_of_match[] = { ++ { .compatible = "rockchip,rk3308-codec", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, rk3308codec_of_match); ++ ++static struct platform_driver rk3308_codec_driver = { ++ .driver = { ++ .name = "rk3308-acodec", ++ .of_match_table = of_match_ptr(rk3308codec_of_match), ++ }, ++ .probe = rk3308_platform_probe, ++ .remove = rk3308_platform_remove, ++}; ++module_platform_driver(rk3308_codec_driver); ++ ++MODULE_AUTHOR("Xing Zheng "); ++MODULE_DESCRIPTION("ASoC RK3308 Codec Driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/sound/soc/codecs/rk3308_codec.h b/sound/soc/codecs/rk3308_codec.h +new file mode 100644 +index 000000000000..6cfa69157785 +--- /dev/null ++++ b/sound/soc/codecs/rk3308_codec.h +@@ -0,0 +1,960 @@ ++/* ++ * rk3308_codec.h -- RK3308 ALSA Soc Audio Driver ++ * ++ * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ */ ++ ++#ifndef __RK3308_CODEC_H__ ++#define __RK3308_CODEC_H__ ++ ++#define ACODEC_RESET_CTL 0x00 /* REG 0x00 */ ++ ++/* ADC DIGITAL REGISTERS */ ++#define ACODEC_ADC_I2S_CTL0 0x04 /* REG 0x01 */ ++#define ACODEC_ADC_I2S_CTL1 0x08 /* REG 0x02 */ ++#define ACODEC_ADC_BIST_MODE_SEL 0x0c /* REG 0x03 */ ++/* Resevred REG 0x04 ~ 0x06 */ ++#define ACODEC_ADC_DATA_PATH 0x1c /* REG 0x07 */ ++/* Resevred REG 0x08 ~ 0x0f */ ++ ++/* REG 0x10 ~ 0x1c are used to configure AGC of Left channel (ALC1) */ ++#define ACODEC_ADC_PGA_AGC_L_CTL0 0x40 /* REG 0x10 */ ++#define ACODEC_ADC_PGA_AGC_L_CTL1 0x44 /* REG 0x11 */ ++#define ACODEC_ADC_PGA_AGC_L_CTL2 0x48 /* REG 0x12 */ ++#define ACODEC_ADC_PGA_AGC_L_CTL3 0x4c /* REG 0x13 */ ++#define ACODEC_ADC_PGA_AGC_L_CTL4 0x50 /* REG 0x14 */ ++#define ACODEC_ADC_PGA_AGC_L_LO_MAX 0x54 /* REG 0x15 */ ++#define ACODEC_ADC_PGA_AGC_L_HI_MAX 0x58 /* REG 0x16 */ ++#define ACODEC_ADC_PGA_AGC_L_LO_MIN 0x5c /* REG 0x17 */ ++#define ACODEC_ADC_PGA_AGC_L_HI_MIN 0x60 /* REG 0x18 */ ++#define ACODEC_ADC_PGA_AGC_L_CTL5 0x64 /* REG 0x19 */ ++/* Resevred REG 0x1a ~ 0x1b */ ++#define ACODEC_ADC_AGC_L_RO_GAIN 0x70 /* REG 0x1c */ ++ ++/* REG 0x20 ~ 0x2c are used to configure AGC of Right channel (ALC2) */ ++#define ACODEC_ADC_PGA_AGC_R_CTL0 0x80 /* REG 0x20 */ ++#define ACODEC_ADC_PGA_AGC_R_CTL1 0x84 /* REG 0x21 */ ++#define ACODEC_ADC_PGA_AGC_R_CTL2 0x88 /* REG 0x22 */ ++#define ACODEC_ADC_PGA_AGC_R_CTL3 0x8c /* REG 0x23 */ ++#define ACODEC_ADC_PGA_AGC_R_CTL4 0x90 /* REG 0x24 */ ++#define ACODEC_ADC_PGA_AGC_R_LO_MAX 0x94 /* REG 0x25 */ ++#define ACODEC_ADC_PGA_AGC_R_HI_MAX 0x98 /* REG 0x26 */ ++#define ACODEC_ADC_PGA_AGC_R_LO_MIN 0x9c /* REG 0x27 */ ++#define ACODEC_ADC_PGA_AGC_R_HI_MIN 0xa0 /* REG 0x28 */ ++#define ACODEC_ADC_PGA_AGC_R_CTL5 0xa4 /* REG 0x29 */ ++/* Resevred REG 0x2a ~ 0x2b */ ++#define ACODEC_ADC_AGC_R_RO_GAIN 0xb0 /* REG 0x2c */ ++ ++/* DAC DIGITAL REGISTERS */ ++#define ACODEC_DAC_I2S_CTL0 0x04 /* REG 0x01 */ ++#define ACODEC_DAC_I2S_CTL1 0x08 /* REG 0x02 */ ++#define ACODEC_DAC_BIST_MODE_SEL 0x0c /* REG 0x03 */ ++/* Resevred REG 0x04 */ ++#define ACODEC_DAC_DATA_SEL 0x14 /* REG 0x05 */ ++/* Resevred REG 0x06 ~ 0x09 */ ++#define ACODEC_DAC_DATA_HI 0x28 /* REG 0x0a */ ++#define ACODEC_DAC_DATA_LO 0x2c /* REG 0x0b */ ++/* Resevred REG 0x0c ~ 0x0f */ ++ ++/* ADC ANALOG REGISTERS */ ++#define ACODEC_ADC_ANA_MIC_CTL 0x00 /* REG 0x00 */ ++#define ACODEC_ADC_ANA_MIC_GAIN 0x04 /* REG 0x01 */ ++#define ACODEC_ADC_ANA_ALC_CTL 0x08 /* REG 0x02 */ ++#define ACODEC_ADC_ANA_ALC_GAIN1 0x0c /* REG 0x03 */ ++#define ACODEC_ADC_ANA_ALC_GAIN2 0x10 /* REG 0x04 */ ++#define ACODEC_ADC_ANA_CTL0 0x14 /* REG 0x05 */ ++#define ACODEC_ADC_ANA_CTL1 0x18 /* REG 0x06 */ ++#define ACODEC_ADC_ANA_CTL2 0x1c /* REG 0x07 */ ++#define ACODEC_ADC_ANA_CTL3 0x20 /* REG 0x08 */ ++/* Resevred REG 0x09 */ ++#define ACODEC_ADC_ANA_CTL4 0x28 /* REG 0x0a */ ++#define ACODEC_ADC_ANA_ALC_PGA 0x2c /* REG 0x0b */ ++/* Resevred REG 0x0c ~ 0x0f */ ++ ++/* DAC ANALOG REGISTERS */ ++#define ACODEC_DAC_ANA_CTL0 0x00 /* REG 0x00 */ ++#define ACODEC_DAC_ANA_POP_VOLT 0x04 /* REG 0x01 */ ++#define ACODEC_DAC_ANA_CTL1 0x08 /* REG 0x02 */ ++#define ACODEC_DAC_ANA_HPOUT 0x0c /* REG 0x03 */ ++#define ACODEC_DAC_ANA_LINEOUT 0x10 /* REG 0x04 */ ++#define ACODEC_DAC_ANA_L_HPOUT_GAIN 0x14 /* REG 0x05 */ ++#define ACODEC_DAC_ANA_R_HPOUT_GAIN 0x18 /* REG 0x06 */ ++/* Resevred REG 0x07 ~ 0x0b */ ++#define ACODEC_DAC_ANA_HPMIX_CTL0 0x30 /* REG 0x0c */ ++#define ACODEC_DAC_ANA_HPMIX_CTL1 0x34 /* REG 0x0d */ ++/* Resevred REG 0x0e ~ 0x0f */ ++ ++/* ++ * These registers are referenced by codec driver ++ */ ++ ++#define RK3308_GLB_CON ACODEC_RESET_CTL ++ ++/* ADC DIGITAL REGISTERS */ ++ ++/* ++ * The ADC chanel are 0 ~ 3, that control: ++ * ++ * CH0: left_0(ADC1) and right_0(ADC2) ++ * CH1: left_1(ADC3) and right_1(ADC4) ++ * CH2: left_2(ADC5) and right_2(ADC6) ++ * CH3: left_3(ADC7) and right_3(ADC8) ++ */ ++#define RK3308_ADC_DIG_OFFSET(ch) ((ch & 0x3) * 0xc0 + 0x0) ++ ++#define RK3308_ADC_DIG_CON01(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_I2S_CTL0) ++#define RK3308_ADC_DIG_CON02(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_I2S_CTL1) ++#define RK3308_ADC_DIG_CON03(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_BIST_MODE_SEL) ++#define RK3308_ADC_DIG_CON07(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_DATA_PATH) ++ ++#define RK3308_ALC_L_DIG_CON00(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_PGA_AGC_L_CTL0) ++#define RK3308_ALC_L_DIG_CON01(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_PGA_AGC_L_CTL1) ++#define RK3308_ALC_L_DIG_CON02(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_PGA_AGC_L_CTL2) ++#define RK3308_ALC_L_DIG_CON03(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_PGA_AGC_L_CTL3) ++#define RK3308_ALC_L_DIG_CON04(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_PGA_AGC_L_CTL4) ++#define RK3308_ALC_L_DIG_CON05(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_PGA_AGC_L_LO_MAX) ++#define RK3308_ALC_L_DIG_CON06(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_PGA_AGC_L_HI_MAX) ++#define RK3308_ALC_L_DIG_CON07(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_PGA_AGC_L_LO_MIN) ++#define RK3308_ALC_L_DIG_CON08(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_PGA_AGC_L_HI_MIN) ++#define RK3308_ALC_L_DIG_CON09(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_PGA_AGC_L_CTL5) ++#define RK3308_ALC_L_DIG_CON12(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_AGC_L_RO_GAIN) ++ ++#define RK3308_ALC_R_DIG_CON00(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_PGA_AGC_R_CTL0) ++#define RK3308_ALC_R_DIG_CON01(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_PGA_AGC_R_CTL1) ++#define RK3308_ALC_R_DIG_CON02(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_PGA_AGC_R_CTL2) ++#define RK3308_ALC_R_DIG_CON03(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_PGA_AGC_R_CTL3) ++#define RK3308_ALC_R_DIG_CON04(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_PGA_AGC_R_CTL4) ++#define RK3308_ALC_R_DIG_CON05(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_PGA_AGC_R_LO_MAX) ++#define RK3308_ALC_R_DIG_CON06(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_PGA_AGC_R_HI_MAX) ++#define RK3308_ALC_R_DIG_CON07(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_PGA_AGC_R_LO_MIN) ++#define RK3308_ALC_R_DIG_CON08(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_PGA_AGC_R_HI_MIN) ++#define RK3308_ALC_R_DIG_CON09(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_PGA_AGC_R_CTL5) ++#define RK3308_ALC_R_DIG_CON12(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_AGC_R_RO_GAIN) ++ ++/* DAC DIGITAL REGISTERS */ ++#define RK3308_DAC_DIG_OFFSET 0x300 ++ ++#define RK3308_DAC_DIG_CON01 (RK3308_DAC_DIG_OFFSET + ACODEC_DAC_I2S_CTL0) ++#define RK3308_DAC_DIG_CON02 (RK3308_DAC_DIG_OFFSET + ACODEC_DAC_I2S_CTL1) ++#define RK3308_DAC_DIG_CON03 (RK3308_DAC_DIG_OFFSET + ACODEC_DAC_BIST_MODE_SEL) ++#define RK3308_DAC_DIG_CON05 (RK3308_DAC_DIG_OFFSET + ACODEC_DAC_DATA_SEL) ++#define RK3308_DAC_DIG_CON10 (RK3308_DAC_DIG_OFFSET + ACODEC_DAC_DATA_HI) ++#define RK3308_DAC_DIG_CON11 (RK3308_DAC_DIG_OFFSET + ACODEC_DAC_DATA_LO) ++ ++/* ADC ANALOG REGISTERS */ ++/* ++ * The ADC chanel are 0 ~ 3, that control: ++ * ++ * CH0: left_0(ADC1) and right_0(ADC2) ++ * CH1: left_1(ADC3) and right_1(ADC4) ++ * CH2: left_2(ADC5) and right_2(ADC6) ++ * CH3: left_3(ADC7) and right_3(ADC8) ++ */ ++#define RK3308_ADC_ANA_OFFSET(ch) ((ch & 0x3) * 0x40 + 0x340) ++ ++#define RK3308_ADC_ANA_CON00(ch) (RK3308_ADC_ANA_OFFSET(ch) + ACODEC_ADC_ANA_MIC_CTL) ++#define RK3308_ADC_ANA_CON01(ch) (RK3308_ADC_ANA_OFFSET(ch) + ACODEC_ADC_ANA_MIC_GAIN) ++#define RK3308_ADC_ANA_CON02(ch) (RK3308_ADC_ANA_OFFSET(ch) + ACODEC_ADC_ANA_ALC_CTL) ++#define RK3308_ADC_ANA_CON03(ch) (RK3308_ADC_ANA_OFFSET(ch) + ACODEC_ADC_ANA_ALC_GAIN1) ++#define RK3308_ADC_ANA_CON04(ch) (RK3308_ADC_ANA_OFFSET(ch) + ACODEC_ADC_ANA_ALC_GAIN2) ++#define RK3308_ADC_ANA_CON05(ch) (RK3308_ADC_ANA_OFFSET(ch) + ACODEC_ADC_ANA_CTL0) ++#define RK3308_ADC_ANA_CON06(ch) (RK3308_ADC_ANA_OFFSET(ch) + ACODEC_ADC_ANA_CTL1) ++#define RK3308_ADC_ANA_CON07(ch) (RK3308_ADC_ANA_OFFSET(ch) + ACODEC_ADC_ANA_CTL2) ++#define RK3308_ADC_ANA_CON08(ch) (RK3308_ADC_ANA_OFFSET(ch) + ACODEC_ADC_ANA_CTL3) ++#define RK3308_ADC_ANA_CON10(ch) (RK3308_ADC_ANA_OFFSET(ch) + ACODEC_ADC_ANA_CTL4) ++#define RK3308_ADC_ANA_CON11(ch) (RK3308_ADC_ANA_OFFSET(ch) + ACODEC_ADC_ANA_ALC_PGA) ++ ++/* DAC ANALOG REGISTERS */ ++#define RK3308_DAC_ANA_OFFSET 0x440 ++ ++#define RK3308_DAC_ANA_CON00 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_CTL0) ++#define RK3308_DAC_ANA_CON01 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_POP_VOLT) ++#define RK3308_DAC_ANA_CON02 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_CTL1) ++#define RK3308_DAC_ANA_CON03 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_HPOUT) ++#define RK3308_DAC_ANA_CON04 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_LINEOUT) ++#define RK3308_DAC_ANA_CON05 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_L_HPOUT_GAIN) ++#define RK3308_DAC_ANA_CON06 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_R_HPOUT_GAIN) ++#define RK3308_DAC_ANA_CON12 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_HPMIX_CTL0) ++#define RK3308_DAC_ANA_CON13 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_HPMIX_CTL1) ++ ++/* ++ * These are the bits for registers ++ */ ++ ++/* RK3308_GLB_CON - REG: 0x0000 */ ++#define RK3308_ADC_BIST_WORK (1 << 7) ++#define RK3308_ADC_BIST_RESET (0 << 7) ++#define RK3308_DAC_BIST_WORK (1 << 6) ++#define RK3308_DAC_BIST_RESET (0 << 6) ++#define RK3308_CODEC_RST_MSK (0x7 << 0) ++#define RK3308_ADC_DIG_WORK (1 << 2) ++#define RK3308_ADC_DIG_RESET (0 << 2) ++#define RK3308_DAC_DIG_WORK (1 << 1) ++#define RK3308_DAC_DIG_RESET (0 << 1) ++#define RK3308_SYS_WORK (1 << 0) ++#define RK3308_SYS_RESET (0 << 0) ++ ++/* RK3308_ADC_DIG_CON01 - REG: 0x0004 */ ++#define RK3308_ADC_I2S_LRC_POL_MSK (1 << 0) ++#define RK3308_ADC_I2S_LRC_POL_REVERSAL (1 << 0) ++#define RK3308_ADC_I2S_LRC_POL_NORMAL (0 << 0) ++#define RK3308_ADC_I2S_VALID_LEN_SFT 5 ++#define RK3308_ADC_I2S_VALID_LEN_MSK (0x3 << RK3308_ADC_I2S_VALID_LEN_SFT) ++#define RK3308_ADC_I2S_VALID_LEN_32BITS (0x3 << RK3308_ADC_I2S_VALID_LEN_SFT) ++#define RK3308_ADC_I2S_VALID_LEN_24BITS (0x2 << RK3308_ADC_I2S_VALID_LEN_SFT) ++#define RK3308_ADC_I2S_VALID_LEN_20BITS (0x1 << RK3308_ADC_I2S_VALID_LEN_SFT) ++#define RK3308_ADC_I2S_VALID_LEN_16BITS (0x0 << RK3308_ADC_I2S_VALID_LEN_SFT) ++#define RK3308_ADC_I2S_MODE_SFT 3 ++#define RK3308_ADC_I2S_MODE_MSK (0x3 << RK3308_ADC_I2S_MODE_SFT) ++#define RK3308_ADC_I2S_MODE_PCM (0x3 << RK3308_ADC_I2S_MODE_SFT) ++#define RK3308_ADC_I2S_MODE_I2S (0x2 << RK3308_ADC_I2S_MODE_SFT) ++#define RK3308_ADC_I2S_MODE_LJ (0x1 << RK3308_ADC_I2S_MODE_SFT) ++#define RK3308_ADC_I2S_MODE_RJ (0x0 << RK3308_ADC_I2S_MODE_SFT) ++#define RK3308_ADC_I2S_LR_MSK (1 << 1) ++#define RK3308_ADC_I2S_LR_SWAP (1 << 1) ++#define RK3308_ADC_I2S_LR_NORMAL (0 << 1) ++#define RK3308_ADC_I2S_TYPE_MSK (1 << 0) ++#define RK3308_ADC_I2S_MONO (1 << 0) ++#define RK3308_ADC_I2S_STEREO (0 << 0) ++ ++/* RK3308_ADC_DIG_CON02 - REG: 0x0008 */ ++#define RK3308_ADC_IO_MODE_MSK (1 << 5) ++#define RK3308_ADC_IO_MODE_MASTER (1 << 5) ++#define RK3308_ADC_IO_MODE_SLAVE (0 << 5) ++#define RK3308_ADC_MODE_MSK (1 << 4) ++#define RK3308_ADC_MODE_MASTER (1 << 4) ++#define RK3308_ADC_MODE_SLAVE (0 << 4) ++#define RK3308_ADC_I2S_FRAME_LEN_SFT 2 ++#define RK3308_ADC_I2S_FRAME_LEN_MSK (0x3 << RK3308_ADC_I2S_FRAME_LEN_SFT) ++#define RK3308_ADC_I2S_FRAME_32BITS (0x3 << RK3308_ADC_I2S_FRAME_LEN_SFT) ++#define RK3308_ADC_I2S_FRAME_24BITS (0x2 << RK3308_ADC_I2S_FRAME_LEN_SFT) ++#define RK3308_ADC_I2S_FRAME_20BITS (0x1 << RK3308_ADC_I2S_FRAME_LEN_SFT) ++#define RK3308_ADC_I2S_FRAME_16BITS (0x0 << RK3308_ADC_I2S_FRAME_LEN_SFT) ++#define RK3308_ADC_I2S_MSK (0x1 << 1) ++#define RK3308_ADC_I2S_WORK (0x1 << 1) ++#define RK3308_ADC_I2S_RESET (0x0 << 1) ++#define RK3308_ADC_I2S_BIT_CLK_POL_MSK (0x1 << 0) ++#define RK3308_ADC_I2S_BIT_CLK_POL_REVERSAL (0x1 << 0) ++#define RK3308_ADC_I2S_BIT_CLK_POL_NORMAL (0x0 << 0) ++ ++/* RK3308_ADC_DIG_CON03 - REG: 0x000c */ ++#define RK3308_ADC_L_CH_BIST_SFT 2 ++#define RK3308_ADC_L_CH_BIST_MSK (0x3 << RK3308_ADC_L_CH_BIST_SFT) ++#define RK3308_ADC_L_CH_BIST_LEFT (0x3 << RK3308_ADC_L_CH_BIST_SFT) /* normal mode */ ++#define RK3308_ADC_L_CH_BIST_SINE (0x2 << RK3308_ADC_L_CH_BIST_SFT) ++#define RK3308_ADC_L_CH_BIST_CUBE (0x1 << RK3308_ADC_L_CH_BIST_SFT) ++#define RK3308_ADC_L_CH_BIST_RIGHT (0x0 << RK3308_ADC_L_CH_BIST_SFT) /* normal mode */ ++#define RK3308_ADC_R_CH_BIST_SFT 0 ++#define RK3308_ADC_R_CH_BIST_MSK (0x3 << RK3308_ADC_R_CH_BIST_SFT) ++#define RK3308_ADC_R_CH_BIST_LEFT (0x3 << RK3308_ADC_R_CH_BIST_SFT) /* normal mode */ ++#define RK3308_ADC_R_CH_BIST_SINE (0x2 << RK3308_ADC_R_CH_BIST_SFT) ++#define RK3308_ADC_R_CH_BIST_CUBE (0x1 << RK3308_ADC_R_CH_BIST_SFT) ++#define RK3308_ADC_R_CH_BIST_RIGHT (0x0 << RK3308_ADC_R_CH_BIST_SFT) /* normal mode */ ++ ++/* RK3308_ADC_DIG_CON07 - REG: 0x001c */ ++#define RK3308_ADCL_DATA_SFT 4 ++#define RK3308_ADCL_DATA(x) (x << RK3308_ADCL_DATA_SFT) ++#define RK3308_ADCR_DATA_SFT 2 ++#define RK3308_ADCR_DATA(x) (x << RK3308_ADCR_DATA_SFT) ++#define RK3308_ADCL_DATA_SEL_ADCL (0x1 << 1) ++#define RK3308_ADCL_DATA_SEL_NORMAL (0x0 << 1) ++#define RK3308_ADCR_DATA_SEL_ADCR (0x1 << 0) ++#define RK3308_ADCR_DATA_SEL_NORMAL (0x0 << 0) ++ ++/* ++ * RK3308_ALC_L_DIG_CON00 - REG: 0x0040 + ch * 0xc0 ++ * RK3308_ALC_R_DIG_CON00 - REG: 0x0080 + ch * 0xc0 ++ */ ++#define RK3308_GAIN_ATTACK_JACK (0x1 << 6) ++#define RK3308_GAIN_ATTACK_NORMAL (0x0 << 6) ++#define RK3308_CTRL_GEN_SFT 4 ++#define RK3308_CTRL_GEN_MSK (0x3 << RK3308_ALC_CTRL_GEN_SFT) ++#define RK3308_CTRL_GEN_JACK3 (0x3 << RK3308_ALC_CTRL_GEN_SFT) ++#define RK3308_CTRL_GEN_JACK2 (0x2 << RK3308_ALC_CTRL_GEN_SFT) ++#define RK3308_CTRL_GEN_JACK1 (0x1 << RK3308_ALC_CTRL_GEN_SFT) ++#define RK3308_CTRL_GEN_NORMAL (0x0 << RK3308_ALC_CTRL_GEN_SFT) ++#define RK3308_AGC_HOLD_TIME_SFT 0 ++#define RK3308_AGC_HOLD_TIME_MSK (0xf << RK3308_AGC_HOLD_TIME_SFT) ++#define RK3308_AGC_HOLD_TIME_1S (0xa << RK3308_AGC_HOLD_TIME_SFT) ++#define RK3308_AGC_HOLD_TIME_512MS (0x9 << RK3308_AGC_HOLD_TIME_SFT) ++#define RK3308_AGC_HOLD_TIME_256MS (0x8 << RK3308_AGC_HOLD_TIME_SFT) ++#define RK3308_AGC_HOLD_TIME_128MS (0x7 << RK3308_AGC_HOLD_TIME_SFT) ++#define RK3308_AGC_HOLD_TIME_64MS (0x6 << RK3308_AGC_HOLD_TIME_SFT) ++#define RK3308_AGC_HOLD_TIME_32MS (0x5 << RK3308_AGC_HOLD_TIME_SFT) ++#define RK3308_AGC_HOLD_TIME_16MS (0x4 << RK3308_AGC_HOLD_TIME_SFT) ++#define RK3308_AGC_HOLD_TIME_8MS (0x3 << RK3308_AGC_HOLD_TIME_SFT) ++#define RK3308_AGC_HOLD_TIME_4MS (0x2 << RK3308_AGC_HOLD_TIME_SFT) ++#define RK3308_AGC_HOLD_TIME_2MS (0x1 << RK3308_AGC_HOLD_TIME_SFT) ++#define RK3308_AGC_HOLD_TIME_0MS (0x0 << RK3308_AGC_HOLD_TIME_SFT) ++ ++/* ++ * RK3308_ALC_L_DIG_CON01 - REG: 0x0044 + ch * 0xc0 ++ * RK3308_ALC_R_DIG_CON01 - REG: 0x0084 + ch * 0xc0 ++ */ ++#define RK3308_AGC_DECAY_TIME_SFT 4 ++/* Normal mode (reg_agc_mode = 0) */ ++#define RK3308_AGC_DECAY_NORMAL_MSK (0xf << RK3308_AGC_DECAY_TIME_SFT) ++#define RK3308_AGC_DECAY_NORMAL_512MS (0xa << RK3308_AGC_DECAY_TIME_SFT) ++#define RK3308_AGC_DECAY_NORMAL_256MS (0x9 << RK3308_AGC_DECAY_TIME_SFT) ++#define RK3308_AGC_DECAY_NORMAL_128MS (0x8 << RK3308_AGC_DECAY_TIME_SFT) ++#define RK3308_AGC_DECAY_NORMAL_64MS (0x7 << RK3308_AGC_DECAY_TIME_SFT) ++#define RK3308_AGC_DECAY_NORMAL_32MS (0x6 << RK3308_AGC_DECAY_TIME_SFT) ++#define RK3308_AGC_DECAY_NORMAL_16MS (0x5 << RK3308_AGC_DECAY_TIME_SFT) ++#define RK3308_AGC_DECAY_NORMAL_8MS (0x4 << RK3308_AGC_DECAY_TIME_SFT) ++#define RK3308_AGC_DECAY_NORMAL_4MS (0x3 << RK3308_AGC_DECAY_TIME_SFT) ++#define RK3308_AGC_DECAY_NORMAL_2MS (0x2 << RK3308_AGC_DECAY_TIME_SFT) ++#define RK3308_AGC_DECAY_NORMAL_1MS (0x1 << RK3308_AGC_DECAY_TIME_SFT) ++#define RK3308_AGC_DECAY_NORMAL_0MS (0x0 << RK3308_AGC_DECAY_TIME_SFT) ++/* Limiter mode (reg_agc_mode = 1) */ ++#define RK3308_AGC_DECAY_LIMITER_MSK (0xf << RK3308_AGC_DECAY_TIME_SFT) ++#define RK3308_AGC_DECAY_LIMITER_128MS (0xa << RK3308_AGC_DECAY_TIME_SFT) ++#define RK3308_AGC_DECAY_LIMITER_64MS (0x9 << RK3308_AGC_DECAY_TIME_SFT) ++#define RK3308_AGC_DECAY_LIMITER_32MS (0x8 << RK3308_AGC_DECAY_TIME_SFT) ++#define RK3308_AGC_DECAY_LIMITER_16MS (0x7 << RK3308_AGC_DECAY_TIME_SFT) ++#define RK3308_AGC_DECAY_LIMITER_8MS (0x6 << RK3308_AGC_DECAY_TIME_SFT) ++#define RK3308_AGC_DECAY_LIMITER_4MS (0x5 << RK3308_AGC_DECAY_TIME_SFT) ++#define RK3308_AGC_DECAY_LIMITER_2MS (0x4 << RK3308_AGC_DECAY_TIME_SFT) ++#define RK3308_AGC_DECAY_LIMITER_1MS (0x3 << RK3308_AGC_DECAY_TIME_SFT) ++#define RK3308_AGC_DECAY_LIMITER_500US (0x2 << RK3308_AGC_DECAY_TIME_SFT) ++#define RK3308_AGC_DECAY_LIMITER_250US (0x1 << RK3308_AGC_DECAY_TIME_SFT) ++#define RK3308_AGC_DECAY_LIMITER_125US (0x0 << RK3308_AGC_DECAY_TIME_SFT) ++ ++#define RK3308_AGC_ATTACK_TIME_SFT 0 ++/* Normal mode (reg_agc_mode = 0) */ ++#define RK3308_AGC_ATTACK_NORMAL_MSK (0xf << RK3308_AGC_ATTACK_TIME_SFT) ++#define RK3308_AGC_ATTACK_NORMAL_128MS (0xa << RK3308_AGC_ATTACK_TIME_SFT) ++#define RK3308_AGC_ATTACK_NORMAL_64MS (0x9 << RK3308_AGC_ATTACK_TIME_SFT) ++#define RK3308_AGC_ATTACK_NORMAL_32MS (0x8 << RK3308_AGC_ATTACK_TIME_SFT) ++#define RK3308_AGC_ATTACK_NORMAL_16MS (0x7 << RK3308_AGC_ATTACK_TIME_SFT) ++#define RK3308_AGC_ATTACK_NORMAL_8MS (0x6 << RK3308_AGC_ATTACK_TIME_SFT) ++#define RK3308_AGC_ATTACK_NORMAL_4MS (0x5 << RK3308_AGC_ATTACK_TIME_SFT) ++#define RK3308_AGC_ATTACK_NORMAL_2MS (0x4 << RK3308_AGC_ATTACK_TIME_SFT) ++#define RK3308_AGC_ATTACK_NORMAL_1MS (0x3 << RK3308_AGC_ATTACK_TIME_SFT) ++#define RK3308_AGC_ATTACK_NORMAL_500US (0x2 << RK3308_AGC_ATTACK_TIME_SFT) ++#define RK3308_AGC_ATTACK_NORMAL_250US (0x1 << RK3308_AGC_ATTACK_TIME_SFT) ++#define RK3308_AGC_ATTACK_NORMAL_125US (0x0 << RK3308_AGC_ATTACK_TIME_SFT) ++/* Limiter mode (reg_agc_mode = 1) */ ++#define RK3308_AGC_ATTACK_LIMITER_MSK (0xf << RK3308_AGC_ATTACK_TIME_SFT) ++#define RK3308_AGC_ATTACK_LIMITER_32MS (0xa << RK3308_AGC_ATTACK_TIME_SFT) ++#define RK3308_AGC_ATTACK_LIMITER_16MS (0x9 << RK3308_AGC_ATTACK_TIME_SFT) ++#define RK3308_AGC_ATTACK_LIMITER_8MS (0x8 << RK3308_AGC_ATTACK_TIME_SFT) ++#define RK3308_AGC_ATTACK_LIMITER_4MS (0x7 << RK3308_AGC_ATTACK_TIME_SFT) ++#define RK3308_AGC_ATTACK_LIMITER_2MS (0x6 << RK3308_AGC_ATTACK_TIME_SFT) ++#define RK3308_AGC_ATTACK_LIMITER_1MS (0x5 << RK3308_AGC_ATTACK_TIME_SFT) ++#define RK3308_AGC_ATTACK_LIMITER_500US (0x4 << RK3308_AGC_ATTACK_TIME_SFT) ++#define RK3308_AGC_ATTACK_LIMITER_250US (0x3 << RK3308_AGC_ATTACK_TIME_SFT) ++#define RK3308_AGC_ATTACK_LIMITER_125US (0x2 << RK3308_AGC_ATTACK_TIME_SFT) ++#define RK3308_AGC_ATTACK_LIMITER_64US (0x1 << RK3308_AGC_ATTACK_TIME_SFT) ++#define RK3308_AGC_ATTACK_LIMITER_32US (0x0 << RK3308_AGC_ATTACK_TIME_SFT) ++ ++/* ++ * RK3308_ALC_L_DIG_CON02 - REG: 0x0048 + ch * 0xc0 ++ * RK3308_ALC_R_DIG_CON02 - REG: 0x0088 + ch * 0xc0 ++ */ ++#define RK3308_AGC_MODE_LIMITER (0x1 << 7) ++#define RK3308_AGC_MODE_NORMAL (0x0 << 7) ++#define RK3308_AGC_ZERO_CRO_EN (0x1 << 6) ++#define RK3308_AGC_ZERO_CRO_DIS (0x0 << 6) ++#define RK3308_AGC_AMP_RECOVER_GAIN (0x1 << 5) ++#define RK3308_AGC_AMP_RECOVER_LVOL (0x0 << 5) ++#define RK3308_AGC_FAST_DEC_EN (0x1 << 4) ++#define RK3308_AGC_FAST_DEC_DIS (0x0 << 4) ++#define RK3308_AGC_NOISE_GATE_EN (0x1 << 3) ++#define RK3308_AGC_NOISE_GATE_DIS (0x0 << 3) ++#define RK3308_AGC_NOISE_GATE_THRESH_SFT 0 ++#define RK3308_AGC_NOISE_GATE_THRESH_MSK (0x7 << RK3308_AGC_NOISE_GATE_THRESH_SFT) ++#define RK3308_AGC_NOISE_GATE_THRESH_N81DB (0x7 << RK3308_AGC_NOISE_GATE_THRESH_SFT) ++#define RK3308_AGC_NOISE_GATE_THRESH_N75DB (0x6 << RK3308_AGC_NOISE_GATE_THRESH_SFT) ++#define RK3308_AGC_NOISE_GATE_THRESH_N69DB (0x5 << RK3308_AGC_NOISE_GATE_THRESH_SFT) ++#define RK3308_AGC_NOISE_GATE_THRESH_N63DB (0x4 << RK3308_AGC_NOISE_GATE_THRESH_SFT) ++#define RK3308_AGC_NOISE_GATE_THRESH_N57DB (0x3 << RK3308_AGC_NOISE_GATE_THRESH_SFT) ++#define RK3308_AGC_NOISE_GATE_THRESH_N51DB (0x2 << RK3308_AGC_NOISE_GATE_THRESH_SFT) ++#define RK3308_AGC_NOISE_GATE_THRESH_N45DB (0x1 << RK3308_AGC_NOISE_GATE_THRESH_SFT) ++#define RK3308_AGC_NOISE_GATE_THRESH_N39DB (0x0 << RK3308_AGC_NOISE_GATE_THRESH_SFT) ++ ++/* ++ * RK3308_ALC_L_DIG_CON03 - REG: 0x004c + ch * 0xc0 ++ * RK3308_ALC_R_DIG_CON03 - REG: 0x008c + ch * 0xc0 ++ */ ++#define RK3308_AGC_PGA_ZERO_CRO_EN (0x1 << 5) ++#define RK3308_AGC_PGA_ZERO_CRO_DIS (0x0 << 5) ++#define RK3308_AGC_PGA_GAIN_SFT 0 ++#define RK3308_AGC_PGA_GAIN_MSK (0x1f << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_PDB_28_5 (0x1f << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_PDB_27 (0x1e << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_PDB_25_5 (0x1d << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_PDB_24 (0x1c << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_PDB_22_5 (0x1b << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_PDB_21 (0x1a << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_PDB_19_5 (0x19 << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_PDB_18 (0x18 << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_PDB_16_5 (0x17 << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_PDB_15 (0x16 << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_PDB_13_5 (0x15 << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_PDB_12 (0x14 << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_PDB_10_5 (0x13 << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_PDB_9 (0x12 << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_PDB_7_5 (0x11 << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_PDB_6 (0x10 << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_PDB_4_5 (0x0f << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_PDB_3 (0x0e << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_PDB_1_5 (0x0d << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_0DB (0x0c << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_NDB_1_5 (0x0b << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_NDB_3 (0x0a << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_NDB_4_5 (0x09 << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_NDB_6 (0x08 << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_NDB_7_5 (0x07 << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_NDB_9 (0x06 << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_NDB_10_5 (0x05 << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_NDB_12 (0x04 << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_NDB_13_5 (0x03 << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_NDB_15 (0x02 << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_NDB_16_5 (0x01 << RK3308_AGC_PGA_GAIN_SFT) ++#define RK3308_AGC_PGA_GAIN_NDB_18 (0x00 << RK3308_AGC_PGA_GAIN_SFT) ++ ++/* ++ * RK3308_ALC_L_DIG_CON04 - REG: 0x0050 + ch * 0xc0 ++ * RK3308_ALC_R_DIG_CON04 - REG: 0x0090 + ch * 0xc0 ++ */ ++#define RK3308_AGC_SLOW_CLK_EN (0x1 << 3) ++#define RK3308_AGC_SLOW_CLK_DIS (0x0 << 3) ++#define RK3308_AGC_APPROX_RATE_SFT 0 ++#define RK3308_AGC_APPROX_RATE_MSK (0x7 << RK3308_AGC_APPROX_RATE_SFT) ++#define RK3308_AGC_APPROX_RATE_8K (0x7 << RK3308_AGC_APPROX_RATE_SFT) ++#define RK3308_AGC_APPROX_RATE_12K (0x6 << RK3308_AGC_APPROX_RATE_SFT) ++#define RK3308_AGC_APPROX_RATE_16K (0x5 << RK3308_AGC_APPROX_RATE_SFT) ++#define RK3308_AGC_APPROX_RATE_24K (0x4 << RK3308_AGC_APPROX_RATE_SFT) ++#define RK3308_AGC_APPROX_RATE_32K (0x3 << RK3308_AGC_APPROX_RATE_SFT) ++#define RK3308_AGC_APPROX_RATE_44_1K (0x2 << RK3308_AGC_APPROX_RATE_SFT) ++#define RK3308_AGC_APPROX_RATE_48K (0x1 << RK3308_AGC_APPROX_RATE_SFT) ++#define RK3308_AGC_APPROX_RATE_96K (0x0 << RK3308_AGC_APPROX_RATE_SFT) ++ ++/* ++ * RK3308_ALC_L_DIG_CON05 - REG: 0x0054 + ch * 0xc0 ++ * RK3308_ALC_R_DIG_CON05 - REG: 0x0094 + ch * 0xc0 ++ */ ++#define RK3308_AGC_LO_8BITS_AGC_MAX_MSK 0xff ++ ++/* ++ * RK3308_ALC_L_DIG_CON06 - REG: 0x0058 + ch * 0xc0 ++ * RK3308_ALC_R_DIG_CON06 - REG: 0x0098 + ch * 0xc0 ++ */ ++#define RK3308_AGC_HI_8BITS_AGC_MAX_MSK 0xff ++ ++/* ++ * RK3308_ALC_L_DIG_CON07 - REG: 0x005c + ch * 0xc0 ++ * RK3308_ALC_R_DIG_CON07 - REG: 0x009c + ch * 0xc0 ++ */ ++#define RK3308_AGC_LO_8BITS_AGC_MIN_MSK 0xff ++ ++/* ++ * RK3308_ALC_L_DIG_CON08 - REG: 0x0060 + ch * 0xc0 ++ * RK3308_ALC_R_DIG_CON08 - REG: 0x00a0 + ch * 0xc0 ++ */ ++#define RK3308_AGC_HI_8BITS_AGC_MIN_MSK 0xff ++ ++/* ++ * RK3308_ALC_L_DIG_CON09 - REG: 0x0064 + ch * 0xc0 ++ * RK3308_ALC_R_DIG_CON09 - REG: 0x00a4 + ch * 0xc0 ++ */ ++#define RK3308_AGC_FUNC_SEL_MSK (0x1 << 6) ++#define RK3308_AGC_FUNC_SEL_EN (0x1 << 6) ++#define RK3308_AGC_FUNC_SEL_DIS (0x0 << 6) ++#define RK3308_AGC_MAX_GAIN_PGA_SFT 3 ++#define RK3308_AGC_MAX_GAIN_PGA_MSK (0x7 << RK3308_AGC_MAX_GAIN_PGA_SFT) ++#define RK3308_AGC_MAX_GAIN_PGA_PDB_28_5 (0x7 << RK3308_AGC_MAX_GAIN_PGA_SFT) ++#define RK3308_AGC_MAX_GAIN_PGA_PDB_22_5 (0x6 << RK3308_AGC_MAX_GAIN_PGA_SFT) ++#define RK3308_AGC_MAX_GAIN_PGA_PDB_16_5 (0x5 << RK3308_AGC_MAX_GAIN_PGA_SFT) ++#define RK3308_AGC_MAX_GAIN_PGA_PDB_10_5 (0x4 << RK3308_AGC_MAX_GAIN_PGA_SFT) ++#define RK3308_AGC_MAX_GAIN_PGA_PDB_4_5 (0x3 << RK3308_AGC_MAX_GAIN_PGA_SFT) ++#define RK3308_AGC_MAX_GAIN_PGA_NDB_1_5 (0x2 << RK3308_AGC_MAX_GAIN_PGA_SFT) ++#define RK3308_AGC_MAX_GAIN_PGA_NDB_7_5 (0x1 << RK3308_AGC_MAX_GAIN_PGA_SFT) ++#define RK3308_AGC_MAX_GAIN_PGA_NDB_13_5 (0x0 << RK3308_AGC_MAX_GAIN_PGA_SFT) ++#define RK3308_AGC_MIN_GAIN_PGA_SFT 0 ++#define RK3308_AGC_MIN_GAIN_PGA_MSK (0x7 << RK3308_AGC_MIN_GAIN_PGA_SFT) ++#define RK3308_AGC_MIN_GAIN_PGA_PDB_24 (0x7 << RK3308_AGC_MIN_GAIN_PGA_SFT) ++#define RK3308_AGC_MIN_GAIN_PGA_PDB_18 (0x6 << RK3308_AGC_MIN_GAIN_PGA_SFT) ++#define RK3308_AGC_MIN_GAIN_PGA_PDB_12 (0x5 << RK3308_AGC_MIN_GAIN_PGA_SFT) ++#define RK3308_AGC_MIN_GAIN_PGA_PDB_6 (0x4 << RK3308_AGC_MIN_GAIN_PGA_SFT) ++#define RK3308_AGC_MIN_GAIN_PGA_0DB (0x3 << RK3308_AGC_MIN_GAIN_PGA_SFT) ++#define RK3308_AGC_MIN_GAIN_PGA_NDB_6 (0x2 << RK3308_AGC_MIN_GAIN_PGA_SFT) ++#define RK3308_AGC_MIN_GAIN_PGA_NDB_12 (0x1 << RK3308_AGC_MIN_GAIN_PGA_SFT) ++#define RK3308_AGC_MIN_GAIN_PGA_NDB_18 (0x0 << RK3308_AGC_MIN_GAIN_PGA_SFT) ++ ++/* ++ * RK3308_ALC_L_DIG_CON12 - REG: 0x0068 + ch * 0xc0 ++ * RK3308_ALC_R_DIG_CON12 - REG: 0x00a8 + ch * 0xc0 ++ */ ++#define RK3308_AGC_GAIN_MSK 0x1f ++ ++/* RK3308_DAC_DIG_CON01 - REG: 0x0304 */ ++#define RK3308_DAC_I2S_LRC_POL_MSK (0x1 << 7) ++#define RK3308_DAC_I2S_LRC_POL_REVERSAL (0x1 << 7) ++#define RK3308_DAC_I2S_LRC_POL_NORMAL (0x0 << 7) ++#define RK3308_DAC_I2S_VALID_LEN_SFT 5 ++#define RK3308_DAC_I2S_VALID_LEN_MSK (0x3 << RK3308_DAC_I2S_VALID_LEN_SFT) ++#define RK3308_DAC_I2S_VALID_LEN_32BITS (0x3 << RK3308_DAC_I2S_VALID_LEN_SFT) ++#define RK3308_DAC_I2S_VALID_LEN_24BITS (0x2 << RK3308_DAC_I2S_VALID_LEN_SFT) ++#define RK3308_DAC_I2S_VALID_LEN_20BITS (0x1 << RK3308_DAC_I2S_VALID_LEN_SFT) ++#define RK3308_DAC_I2S_VALID_LEN_16BITS (0x0 << RK3308_DAC_I2S_VALID_LEN_SFT) ++#define RK3308_DAC_I2S_MODE_SFT 3 ++#define RK3308_DAC_I2S_MODE_MSK (0x3 << RK3308_DAC_I2S_MODE_SFT) ++#define RK3308_DAC_I2S_MODE_PCM (0x3 << RK3308_DAC_I2S_MODE_SFT) ++#define RK3308_DAC_I2S_MODE_I2S (0x2 << RK3308_DAC_I2S_MODE_SFT) ++#define RK3308_DAC_I2S_MODE_LJ (0x1 << RK3308_DAC_I2S_MODE_SFT) ++#define RK3308_DAC_I2S_MODE_RJ (0x0 << RK3308_DAC_I2S_MODE_SFT) ++#define RK3308_DAC_I2S_LR_MSK (0x1 << 2) ++#define RK3308_DAC_I2S_LR_SWAP (0x1 << 2) ++#define RK3308_DAC_I2S_LR_NORMAL (0x0 << 2) ++ ++/* RK3308_DAC_DIG_CON02 - REG: 0x0308 */ ++#define RK3308_DAC_IO_MODE_MSK (0x1 << 5) ++#define RK3308_DAC_IO_MODE_MASTER (0x1 << 5) ++#define RK3308_DAC_IO_MODE_SLAVE (0x0 << 5) ++#define RK3308_DAC_MODE_MSK (0x1 << 4) ++#define RK3308_DAC_MODE_MASTER (0x1 << 4) ++#define RK3308_DAC_MODE_SLAVE (0x0 << 4) ++#define RK3308_DAC_I2S_FRAME_LEN_SFT 2 ++#define RK3308_DAC_I2S_FRAME_LEN_MSK (0x3 << RK3308_DAC_I2S_FRAME_LEN_SFT) ++#define RK3308_DAC_I2S_FRAME_32BITS (0x3 << RK3308_DAC_I2S_FRAME_LEN_SFT) ++#define RK3308_DAC_I2S_FRAME_24BITS (0x2 << RK3308_DAC_I2S_FRAME_LEN_SFT) ++#define RK3308_DAC_I2S_FRAME_20BITS (0x1 << RK3308_DAC_I2S_FRAME_LEN_SFT) ++#define RK3308_DAC_I2S_FRAME_16BITS (0x0 << RK3308_DAC_I2S_FRAME_LEN_SFT) ++#define RK3308_DAC_I2S_MSK (0x1 << 1) ++#define RK3308_DAC_I2S_WORK (0x1 << 1) ++#define RK3308_DAC_I2S_RESET (0x0 << 1) ++#define RK3308_DAC_I2S_BIT_CLK_POL_MSK (0x1 << 0) ++#define RK3308_DAC_I2S_BIT_CLK_POL_REVERSAL (0x1 << 0) ++#define RK3308_DAC_I2S_BIT_CLK_POL_NORMAL (0x0 << 0) ++ ++/* RK3308_DAC_DIG_CON03 - REG: 0x030C */ ++#define RK3308_DAC_L_CH_BIST_SFT 2 ++#define RK3308_DAC_L_CH_BIST_MSK (0x3 << RK3308_DAC_L_CH_BIST_SFT) ++#define RK3308_DAC_L_CH_BIST_LEFT (0x3 << RK3308_DAC_L_CH_BIST_SFT) /* normal mode */ ++#define RK3308_DAC_L_CH_BIST_CUBE (0x2 << RK3308_DAC_L_CH_BIST_SFT) ++#define RK3308_DAC_L_CH_BIST_SINE (0x1 << RK3308_DAC_L_CH_BIST_SFT) ++#define RK3308_DAC_L_CH_BIST_RIGHT (0x0 << RK3308_DAC_L_CH_BIST_SFT) /* normal mode */ ++#define RK3308_DAC_R_CH_BIST_SFT 0 ++#define RK3308_DAC_R_CH_BIST_MSK (0x3 << RK3308_DAC_R_CH_BIST_SFT) ++#define RK3308_DAC_R_CH_BIST_LEFT (0x3 << RK3308_DAC_R_CH_BIST_SFT) /* normal mode */ ++#define RK3308_DAC_R_CH_BIST_CUBE (0x2 << RK3308_DAC_R_CH_BIST_SFT) ++#define RK3308_DAC_R_CH_BIST_SINE (0x1 << RK3308_DAC_R_CH_BIST_SFT) ++#define RK3308_DAC_R_CH_BIST_RIGHT (0x0 << RK3308_DAC_R_CH_BIST_SFT) /* normal mode */ ++ ++/* RK3308_DAC_DIG_CON05 - REG: 0x0314 */ ++#define RK3308_DAC_L_REG_CTL_INDATA (0x1 << 2) ++#define RK3308_DAC_L_NORMAL_DATA (0x0 << 2) ++#define RK3308_DAC_R_REG_CTL_INDATA (0x1 << 1) ++#define RK3308_DAC_R_NORMAL_DATA (0x0 << 1) ++ ++/* RK3308_DAC_DIG_CON10 - REG: 0x0328 */ ++#define RK3308_DAC_DATA_HI4(x) (x & 0xf) /* Need to RK3308_DAC_x_REG_CTL_INDATA */ ++ ++/* RK3308_DAC_DIG_CON11 - REG: 0x032c */ ++#define RK3308_DAC_DATA_LO8(x) (x & 0xff) /* Need to RK3308_DAC_x_REG_CTL_INDATA */ ++ ++/* RK3308_ADC_ANA_CON00 - REG: 0x0340 */ ++#define RK3308_ADC_CH1_CH2_MIC_ALL_MSK (0xff << 0) ++#define RK3308_ADC_CH1_CH2_MIC_ALL 0xff ++#define RK3308_ADC_CH2_MIC_UNMUTE (0x1 << 7) ++#define RK3308_ADC_CH2_MIC_MUTE (0x0 << 7) ++#define RK3308_ADC_CH2_MIC_WORK (0x1 << 6) ++#define RK3308_ADC_CH2_MIC_INIT (0x0 << 6) ++#define RK3308_ADC_CH2_MIC_EN (0x1 << 5) ++#define RK3308_ADC_CH2_MIC_DIS (0x0 << 5) ++#define RK3308_ADC_CH2_BUF_REF_EN (0x1 << 4) ++#define RK3308_ADC_CH2_BUF_REF_DIS (0x0 << 4) ++#define RK3308_ADC_CH1_MIC_UNMUTE (0x1 << 3) ++#define RK3308_ADC_CH1_MIC_MUTE (0x0 << 3) ++#define RK3308_ADC_CH1_MIC_WORK (0x1 << 2) ++#define RK3308_ADC_CH1_MIC_INIT (0x0 << 2) ++#define RK3308_ADC_CH1_MIC_EN (0x1 << 1) ++#define RK3308_ADC_CH1_MIC_DIS (0x0 << 1) ++#define RK3308_ADC_CH1_BUF_REF_EN (0x1 << 0) ++#define RK3308_ADC_CH1_BUF_REF_DIS (0x0 << 0) ++ ++/* RK3308_ADC_ANA_CON01 - REG: 0x0344 */ ++#define RK3308_ADC_CH2_MIC_GAIN_SFT 4 ++#define RK3308_ADC_CH2_MIC_GAIN_MSK (0x3 << RK3308_ADC_CH2_MIC_GAIN_SFT) ++#define RK3308_ADC_CH2_MIC_GAIN_30DB (0x3 << RK3308_ADC_CH2_MIC_GAIN_SFT) ++#define RK3308_ADC_CH2_MIC_GAIN_20DB (0x2 << RK3308_ADC_CH2_MIC_GAIN_SFT) ++#define RK3308_ADC_CH2_MIC_GAIN_6DB (0x1 << RK3308_ADC_CH2_MIC_GAIN_SFT) ++#define RK3308_ADC_CH2_MIC_GAIN_0DB (0x0 << RK3308_ADC_CH2_MIC_GAIN_SFT) ++#define RK3308_ADC_CH1_MIC_GAIN_SFT 0 ++#define RK3308_ADC_CH1_MIC_GAIN_MSK (0x3 << RK3308_ADC_CH1_MIC_GAIN_SFT) ++#define RK3308_ADC_CH1_MIC_GAIN_30DB (0x3 << RK3308_ADC_CH1_MIC_GAIN_SFT) ++#define RK3308_ADC_CH1_MIC_GAIN_20DB (0x2 << RK3308_ADC_CH1_MIC_GAIN_SFT) ++#define RK3308_ADC_CH1_MIC_GAIN_6DB (0x1 << RK3308_ADC_CH1_MIC_GAIN_SFT) ++#define RK3308_ADC_CH1_MIC_GAIN_0DB (0x0 << RK3308_ADC_CH1_MIC_GAIN_SFT) ++ ++/* RK3308_ADC_ANA_CON02 - REG: 0x0348 */ ++#define RK3308_ADC_CH2_ALC_ZC_MSK (0x7 << 4) ++#define RK3308_ADC_CH2_ZEROCROSS_DET_EN (0x1 << 6) ++#define RK3308_ADC_CH2_ZEROCROSS_DET_DIS (0x0 << 6) ++#define RK3308_ADC_CH2_ALC_WORK (0x1 << 5) ++#define RK3308_ADC_CH2_ALC_INIT (0x0 << 5) ++#define RK3308_ADC_CH2_ALC_EN (0x1 << 4) ++#define RK3308_ADC_CH2_ALC_DIS (0x0 << 4) ++ ++#define RK3308_ADC_CH1_ALC_ZC_MSK (0x7 << 0) ++#define RK3308_ADC_CH1_ZEROCROSS_DET_EN (0x1 << 2) ++#define RK3308_ADC_CH1_ZEROCROSS_DET_DIS (0x0 << 2) ++#define RK3308_ADC_CH1_ALC_WORK (0x1 << 1) ++#define RK3308_ADC_CH1_ALC_INIT (0x0 << 1) ++#define RK3308_ADC_CH1_ALC_EN (0x1 << 0) ++#define RK3308_ADC_CH1_ALC_DIS (0x0 << 0) ++ ++/* RK3308_ADC_ANA_CON03 - REG: 0x034c */ ++#define RK3308_ADC_CH1_ALC_GAIN_SFT 0 ++#define RK3308_ADC_CH1_ALC_GAIN_MSK (0x1f << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_PDB_28_5 (0x1f << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_PDB_27 (0x1e << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_PDB_25_5 (0x1d << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_PDB_24 (0x1c << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_PDB_22_5 (0x1b << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_PDB_21 (0x1a << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_PDB_19_5 (0x19 << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_PDB_18 (0x18 << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_PDB_16_5 (0x17 << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_PDB_15 (0x16 << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_PDB_13_5 (0x15 << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_PDB_12 (0x14 << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_PDB_10_5 (0x13 << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_PDB_9 (0x12 << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_PDB_7_5 (0x11 << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_PDB_6 (0x10 << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_PDB_4_5 (0x0f << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_PDB_3 (0x0e << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_PDB_1_5 (0x0d << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_0DB (0x0c << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_NDB_1_5 (0x0b << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_NDB_3 (0x0a << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_NDB_4_5 (0x09 << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_NDB_6 (0x08 << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_NDB_7_5 (0x07 << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_NDB_9 (0x06 << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_NDB_10_5 (0x05 << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_NDB_12 (0x04 << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_NDB_13_5 (0x03 << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_NDB_15 (0x02 << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_NDB_16_5 (0x01 << RK3308_ADC_CH1_ALC_GAIN_SFT) ++#define RK3308_ADC_CH1_ALC_GAIN_NDB_18 (0x00 << RK3308_ADC_CH1_ALC_GAIN_SFT) ++ ++/* RK3308_ADC_ANA_CON04 - REG: 0x0350 */ ++#define RK3308_ADC_CH2_ALC_GAIN_SFT 0 ++#define RK3308_ADC_CH2_ALC_GAIN_MSK (0x1f << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_PDB_28_5 (0x1f << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_PDB_27 (0x1e << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_PDB_25_5 (0x1d << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_PDB_24 (0x1c << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_PDB_22_5 (0x1b << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_PDB_21 (0x1a << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_PDB_19_5 (0x19 << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_PDB_18 (0x18 << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_PDB_16_5 (0x17 << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_PDB_15 (0x16 << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_PDB_13_5 (0x15 << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_PDB_12 (0x14 << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_PDB_10_5 (0x13 << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_PDB_9 (0x12 << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_PDB_7_5 (0x11 << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_PDB_6 (0x10 << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_PDB_4_5 (0x0f << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_PDB_3 (0x0e << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_PDB_1_5 (0x0d << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_0DB (0x0c << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_NDB_1_5 (0x0b << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_NDB_3 (0x0a << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_NDB_4_5 (0x09 << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_NDB_6 (0x08 << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_NDB_7_5 (0x07 << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_NDB_9 (0x06 << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_NDB_10_5 (0x05 << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_NDB_12 (0x04 << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_NDB_13_5 (0x03 << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_NDB_15 (0x02 << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_NDB_16_5 (0x01 << RK3308_ADC_CH2_ALC_GAIN_SFT) ++#define RK3308_ADC_CH2_ALC_GAIN_NDB_18 (0x00 << RK3308_ADC_CH2_ALC_GAIN_SFT) ++ ++/* RK3308_ADC_ANA_CON05 - REG: 0x0354 */ ++#define RK3308_ADC_CH2_ADC_CLK_MSK (0x7 << 4) ++#define RK3308_ADC_CH2_ADC_WORK (0x1 << 6) ++#define RK3308_ADC_CH2_ADC_INIT (0x0 << 6) ++#define RK3308_ADC_CH2_ADC_EN (0x1 << 5) ++#define RK3308_ADC_CH2_ADC_DIS (0x0 << 5) ++#define RK3308_ADC_CH2_CLK_EN (0x1 << 4) ++#define RK3308_ADC_CH2_CLK_DIS (0x0 << 4) ++ ++#define RK3308_ADC_CH1_ADC_CLK_MSK (0x7 << 0) ++#define RK3308_ADC_CH1_ADC_WORK (0x1 << 2) ++#define RK3308_ADC_CH1_ADC_INIT (0x0 << 2) ++#define RK3308_ADC_CH1_ADC_EN (0x1 << 1) ++#define RK3308_ADC_CH1_ADC_DIS (0x0 << 1) ++#define RK3308_ADC_CH1_CLK_EN (0x1 << 0) ++#define RK3308_ADC_CH1_CLK_DIS (0x0 << 0) ++ ++/* RK3308_ADC_ANA_CON06 - REG: 0x0358 */ ++#define RK3308_ADC_CURRENT_MSK (0x1 << 0) ++#define RK3308_ADC_CURRENT_EN (0x1 << 0) ++#define RK3308_ADC_CURRENT_DIS (0x0 << 0) ++ ++/* RK3308_ADC_ANA_CON07 - REG: 0x035c */ ++/* Note: The register configuration is only valid for ADC2 */ ++#define RK3308_ADC_CH2_IN_SEL_SFT 6 ++#define RK3308_ADC_CH2_IN_SEL_MSK (0x3 << RK3308_ADC_CH2_IN_SEL_SFT) ++#define RK3308_ADC_CH2_IN_LINEIN_MIC (0x3 << RK3308_ADC_CH2_IN_SEL_SFT) ++#define RK3308_ADC_CH2_IN_LINEIN (0x2 << RK3308_ADC_CH2_IN_SEL_SFT) ++#define RK3308_ADC_CH2_IN_MIC (0x1 << RK3308_ADC_CH2_IN_SEL_SFT) ++#define RK3308_ADC_CH2_IN_NONE (0x0 << RK3308_ADC_CH2_IN_SEL_SFT) ++/* Note: The register configuration is only valid for ADC1 */ ++#define RK3308_ADC_CH1_IN_SEL_SFT 4 ++#define RK3308_ADC_CH1_IN_SEL_MSK (0x3 << RK3308_ADC_CH1_IN_SEL_SFT) ++#define RK3308_ADC_CH1_IN_LINEIN_MIC (0x3 << RK3308_ADC_CH1_IN_SEL_SFT) ++#define RK3308_ADC_CH1_IN_LINEIN (0x2 << RK3308_ADC_CH1_IN_SEL_SFT) ++#define RK3308_ADC_CH1_IN_MIC (0x1 << RK3308_ADC_CH1_IN_SEL_SFT) ++#define RK3308_ADC_CH1_IN_NONE (0x0 << RK3308_ADC_CH1_IN_SEL_SFT) ++ ++#define RK3308_ADC_MIC_BIAS_BUF_EN (0x1 << 3) ++#define RK3308_ADC_MIC_BIAS_BUF_DIS (0x0 << 3) ++#define RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT 0 ++#define RK3308_ADC_LEVEL_RANGE_MICBIAS_MSK (0x7 << RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT) ++#define RK3308_ADC_MICBIAS_VOLT_0_85 (0x7 << RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT) ++#define RK3308_ADC_MICBIAS_VOLT_0_8 (0x6 << RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT) ++#define RK3308_ADC_MICBIAS_VOLT_0_75 (0x5 << RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT) ++#define RK3308_ADC_MICBIAS_VOLT_0_7 (0x4 << RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT) ++#define RK3308_ADC_MICBIAS_VOLT_0_65 (0x3 << RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT) ++#define RK3308_ADC_MICBIAS_VOLT_0_6 (0x2 << RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT) ++#define RK3308_ADC_MICBIAS_VOLT_0_55 (0x1 << RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT) ++#define RK3308_ADC_MICBIAS_VOLT_0_5 (0x0 << RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT) ++ ++/* RK3308_ADC_ANA_CON08 - REG: 0x0360 */ ++#define RK3308_ADC_MICBIAS_CURRENT_MSK (0x1 << 4) ++#define RK3308_ADC_MICBIAS_CURRENT_EN (0x1 << 4) ++#define RK3308_ADC_MICBIAS_CURRENT_DIS (0x0 << 4) ++ ++/* RK3308_ADC_ANA_CON10 - REG: 0x0368 */ ++#define RK3308_ADC_REF_EN (0x1 << 7) ++#define RK3308_ADC_REF_DIS (0x0 << 7) ++#define RK3308_ADC_CURRENT_CHARGE_SFT 0 ++#define RK3308_ADC_CURRENT_CHARGE_MSK (0x7f << RK3308_ADC_CURRENT_CHARGE_SFT) ++#define RK3308_ADC_DONT_SEL_ALL (0x7f << RK3308_ADC_CURRENT_CHARGE_SFT) ++/* ++ * 0: Choose the current I ++ * 1: Don't choose the current I ++ */ ++#define RK3308_ADC_SEL_I_1(x) ((x & 0x1) << 6) ++#define RK3308_ADC_SEL_I_2(x) ((x & 0x1) << 5) ++#define RK3308_ADC_SEL_I_4(x) ((x & 0x1) << 4) ++#define RK3308_ADC_SEL_I_8(x) ((x & 0x1) << 3) ++#define RK3308_ADC_SEL_I_16(x) ((x & 0x1) << 2) ++#define RK3308_ADC_SEL_I_32(x) ((x & 0x1) << 1) ++#define RK3308_ADC_SEL_I_64(x) ((x & 0x1) << 0) ++ ++/* RK3308_ADC_ANA_CON11 - REG: 0x036c */ ++#define RK3308_ADC_ALCR_CON_GAIN_PGAR_MSK (0x1 << 1) ++#define RK3308_ADC_ALCR_CON_GAIN_PGAR_EN (0x1 << 1) ++#define RK3308_ADC_ALCR_CON_GAIN_PGAR_DIS (0x0 << 1) ++#define RK3308_ADC_ALCL_CON_GAIN_PGAL_MSK (0x1 << 0) ++#define RK3308_ADC_ALCL_CON_GAIN_PGAL_EN (0x1 << 0) ++#define RK3308_ADC_ALCL_CON_GAIN_PGAL_DIS (0x0 << 0) ++ ++/* RK3308_DAC_ANA_CON00 - REG: 0x0440 */ ++#define RK3308_DAC_HEADPHONE_DET_EN (0x1 << 1) ++#define RK3308_DAC_HEADPHONE_DET_DIS (0x0 << 1) ++#define RK3308_DAC_CURRENT_MSK (0x1 << 0) ++#define RK3308_DAC_CURRENT_EN (0x1 << 0) ++#define RK3308_DAC_CURRENT_DIS (0x0 << 0) ++ ++/* RK3308_DAC_ANA_CON01 - REG: 0x0444 */ ++#define RK3308_DAC_BUF_REF_R_MSK (0x1 << 6) ++#define RK3308_DAC_BUF_REF_R_EN (0x1 << 6) ++#define RK3308_DAC_BUF_REF_R_DIS (0x0 << 6) ++#define RK3308_DAC_POP_SOUND_R_SFT 4 ++#define RK3308_DAC_POP_SOUND_R_MSK (0x3 << RK3308_DAC_POP_SOUND_R_SFT) ++#define RK3308_DAC_POP_SOUND_R_WORK (0x2 << RK3308_DAC_POP_SOUND_R_SFT) ++#define RK3308_DAC_POP_SOUND_R_INIT (0x1 << RK3308_DAC_POP_SOUND_R_SFT) ++#define RK3308_DAC_BUF_REF_L_MSK (0x1 << 2) ++#define RK3308_DAC_BUF_REF_L_EN (0x1 << 2) ++#define RK3308_DAC_BUF_REF_L_DIS (0x0 << 2) ++#define RK3308_DAC_POP_SOUND_L_SFT 0 ++#define RK3308_DAC_POP_SOUND_L_MSK (0x3 << RK3308_DAC_POP_SOUND_L_SFT) ++#define RK3308_DAC_POP_SOUND_L_WORK (0x2 << RK3308_DAC_POP_SOUND_L_SFT) ++#define RK3308_DAC_POP_SOUND_L_INIT (0x1 << RK3308_DAC_POP_SOUND_L_SFT) ++ ++/* RK3308_DAC_ANA_CON02 - REG: 0x0448 */ ++#define RK3308_DAC_R_DAC_WORK (0x1 << 7) ++#define RK3308_DAC_R_DAC_INIT (0x0 << 7) ++#define RK3308_DAC_R_DAC_EN (0x1 << 6) ++#define RK3308_DAC_R_DAC_DIS (0x0 << 6) ++#define RK3308_DAC_R_CLK_EN (0x1 << 5) ++#define RK3308_DAC_R_CLK_DIS (0x0 << 5) ++#define RK3308_DAC_R_REF_EN (0x1 << 4) ++#define RK3308_DAC_R_REF_DIS (0x0 << 4) ++#define RK3308_DAC_L_DAC_WORK (0x1 << 3) ++#define RK3308_DAC_L_DAC_INIT (0x0 << 3) ++#define RK3308_DAC_L_DAC_EN (0x1 << 2) ++#define RK3308_DAC_L_DAC_DIS (0x0 << 2) ++#define RK3308_DAC_L_CLK_EN (0x1 << 1) ++#define RK3308_DAC_L_CLK_DIS (0x0 << 1) ++#define RK3308_DAC_L_REF_EN (0x1 << 0) ++#define RK3308_DAC_L_REF_DIS (0x0 << 0) ++ ++/* RK3308_DAC_ANA_CON03 - REG: 0x044c */ ++#define RK3308_DAC_R_HPOUT_WORK (0x1 << 6) ++#define RK3308_DAC_R_HPOUT_INIT (0x0 << 6) ++#define RK3308_DAC_R_HPOUT_EN (0x1 << 5) ++#define RK3308_DAC_R_HPOUT_DIS (0x0 << 5) ++#define RK3308_DAC_R_HPOUT_UNMUTE (0x1 << 4) ++#define RK3308_DAC_R_HPOUT_MUTE (0x0 << 4) ++#define RK3308_DAC_L_HPOUT_WORK (0x1 << 2) ++#define RK3308_DAC_L_HPOUT_INIT (0x0 << 2) ++#define RK3308_DAC_L_HPOUT_EN (0x1 << 1) ++#define RK3308_DAC_L_HPOUT_DIS (0x0 << 1) ++#define RK3308_DAC_L_HPOUT_UNMUTE (0x1 << 0) ++#define RK3308_DAC_L_HPOUT_MUTE (0x0 << 0) ++ ++/* RK3308_DAC_ANA_CON04 - REG: 0x0450 */ ++#define RK3308_DAC_R_GAIN_SFT 6 ++#define RK3308_DAC_R_GAIN_MSK (0x3 << RK3308_DAC_R_GAIN_SFT) ++#define RK3308_DAC_R_GAIN_0DB (0x3 << RK3308_DAC_R_GAIN_SFT) ++#define RK3308_DAC_R_GAIN_PDB_1_5 (0x2 << RK3308_DAC_R_GAIN_SFT) ++#define RK3308_DAC_R_GAIN_PDB_3 (0x1 << RK3308_DAC_R_GAIN_SFT) ++#define RK3308_DAC_R_GAIN_PDB_6 (0x0 << RK3308_DAC_R_GAIN_SFT) ++#define RK3308_DAC_R_LINEOUT_UNMUTE (0x1 << 5) ++#define RK3308_DAC_R_LINEOUT_MUTE (0x0 << 5) ++#define RK3308_DAC_R_LINEOUT_EN (0x1 << 4) ++#define RK3308_DAC_R_LINEOUT_DIS (0x0 << 4) ++#define RK3308_DAC_L_GAIN_SFT 2 ++#define RK3308_DAC_L_GAIN_MSK (0x3 << RK3308_DAC_L_GAIN_SFT) ++#define RK3308_DAC_L_GAIN_0DB (0x3 << RK3308_DAC_L_GAIN_SFT) ++#define RK3308_DAC_L_GAIN_PDB_1_5 (0x2 << RK3308_DAC_L_GAIN_SFT) ++#define RK3308_DAC_L_GAIN_PDB_3 (0x1 << RK3308_DAC_L_GAIN_SFT) ++#define RK3308_DAC_L_GAIN_PDB_6 (0x0 << RK3308_DAC_L_GAIN_SFT) ++#define RK3308_DAC_L_LINEOUT_UNMUTE (0x1 << 1) ++#define RK3308_DAC_L_LINEOUT_MUTE (0x0 << 1) ++#define RK3308_DAC_L_LINEOUT_EN (0x1 << 0) ++#define RK3308_DAC_L_LINEOUT_DIS (0x0 << 0) ++ ++/* RK3308_DAC_ANA_CON05 - REG: 0x0454, step is 1.5db */ ++#define RK3308_DAC_L_HPOUT_GAIN_SFT 0 ++#define RK3308_DAC_L_HPOUT_GAIN_MSK (0x1f << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_PDB_6 (0x1e << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_PDB_4_5 (0x1d << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_PDB_3 (0x1c << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_PDB_1_5 (0x1b << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_0DB (0x1a << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_NDB_1_5 (0x19 << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_NDB_3 (0x18 << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_NDB_4_5 (0x17 << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_NDB_6 (0x16 << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_NDB_7_5 (0x15 << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_NDB_9 (0x14 << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_NDB_10_5 (0x13 << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_NDB_12 (0x12 << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_NDB_13_5 (0x11 << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_NDB_15 (0x10 << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_NDB_16_5 (0x0f << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_NDB_18 (0x0e << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_NDB_19_5 (0x0d << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_NDB_21 (0x0c << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_NDB_22_5 (0x0b << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_NDB_24 (0x0a << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_NDB_25_5 (0x09 << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_NDB_27 (0x08 << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_NDB_28_5 (0x07 << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_NDB_30 (0x06 << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_NDB_31_5 (0x05 << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_NDB_33 (0x04 << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_NDB_34_5 (0x03 << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_NDB_36 (0x02 << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_NDB_37_5 (0x01 << RK3308_DAC_L_HPOUT_GAIN_SFT) ++#define RK3308_DAC_L_HPOUT_GAIN_NDB_39 (0x00 << RK3308_DAC_L_HPOUT_GAIN_SFT) ++ ++/* RK3308_DAC_ANA_CON06 - REG: 0x0458, step is 1.5db */ ++#define RK3308_DAC_R_HPOUT_GAIN_SFT 0 ++#define RK3308_DAC_R_HPOUT_GAIN_MSK (0x1f << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_PDB_6 (0x1e << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_PDB_4_5 (0x1d << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_PDB_3 (0x1c << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_PDB_1_5 (0x1b << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_0DB (0x1a << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_NDB_1_5 (0x19 << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_NDB_3 (0x18 << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_NDB_4_5 (0x17 << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_NDB_6 (0x16 << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_NDB_7_5 (0x15 << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_NDB_9 (0x14 << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_NDB_10_5 (0x13 << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_NDB_12 (0x12 << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_NDB_13_5 (0x11 << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_NDB_15 (0x10 << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_NDB_16_5 (0x0f << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_NDB_18 (0x0e << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_NDB_19_5 (0x0d << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_NDB_21 (0x0c << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_NDB_22_5 (0x0b << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_NDB_24 (0x0a << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_NDB_25_5 (0x09 << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_NDB_27 (0x08 << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_NDB_28_5 (0x07 << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_NDB_30 (0x06 << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_NDB_31_5 (0x05 << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_NDB_33 (0x04 << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_NDB_34_5 (0x03 << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_NDB_36 (0x02 << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_NDB_37_5 (0x01 << RK3308_DAC_R_HPOUT_GAIN_SFT) ++#define RK3308_DAC_R_HPOUT_GAIN_NDB_39 (0x00 << RK3308_DAC_R_HPOUT_GAIN_SFT) ++ ++/* RK3308_DAC_ANA_CON12 - REG: 0x0470 */ ++#define RK3308_DAC_R_HPMIX_SEL_SFT 6 ++#define RK3308_DAC_R_HPMIX_SEL_MSK (0x3 << RK3308_DAC_R_HPMIX_SEL_SFT) ++#define RK3308_DAC_R_HPMIX_LINEIN_I2S (0x3 << RK3308_DAC_R_HPMIX_SEL_SFT) ++#define RK3308_DAC_R_HPMIX_LINEIN (0x2 << RK3308_DAC_R_HPMIX_SEL_SFT) ++#define RK3308_DAC_R_HPMIX_I2S (0x1 << RK3308_DAC_R_HPMIX_SEL_SFT) ++#define RK3308_DAC_R_HPMIX_NONE (0x0 << RK3308_DAC_R_HPMIX_SEL_SFT) ++#define RK3308_DAC_R_HPMIX_GAIN_SFT 4 ++#define RK3308_DAC_R_HPMIX_GAIN_MSK (0x3 << RK3308_DAC_R_HPMIX_GAIN_SFT) ++#define RK3308_DAC_R_HPMIX_GAIN_0DB (0x2 << RK3308_DAC_R_HPMIX_GAIN_SFT) ++#define RK3308_DAC_R_HPMIX_GAIN_NDB_6 (0x1 << RK3308_DAC_R_HPMIX_GAIN_SFT) ++#define RK3308_DAC_L_HPMIX_SEL_SFT 2 ++#define RK3308_DAC_L_HPMIX_SEL_MSK (0x3 << RK3308_DAC_L_HPMIX_SEL_SFT) ++#define RK3308_DAC_L_HPMIX_LINEIN_I2S (0x3 << RK3308_DAC_L_HPMIX_SEL_SFT) ++#define RK3308_DAC_L_HPMIX_LINEIN (0x2 << RK3308_DAC_L_HPMIX_SEL_SFT) ++#define RK3308_DAC_L_HPMIX_I2S (0x1 << RK3308_DAC_L_HPMIX_SEL_SFT) ++#define RK3308_DAC_L_HPMIX_NONE (0x0 << RK3308_DAC_L_HPMIX_SEL_SFT) ++#define RK3308_DAC_L_HPMIX_GAIN_SFT 0 ++#define RK3308_DAC_L_HPMIX_GAIN_MSK (0x3 << RK3308_DAC_L_HPMIX_GAIN_SFT) ++#define RK3308_DAC_L_HPMIX_GAIN_0DB (0x2 << RK3308_DAC_L_HPMIX_GAIN_SFT) ++#define RK3308_DAC_L_HPMIX_GAIN_NDB_6 (0x1 << RK3308_DAC_L_HPMIX_GAIN_SFT) ++ ++/* RK3308_DAC_ANA_CON13 - REG: 0x0474 */ ++#define RK3308_DAC_R_HPMIX_UNMUTE (0x1 << 6) ++#define RK3308_DAC_R_HPMIX_MUTE (0x0 << 6) ++#define RK3308_DAC_R_HPMIX_WORK (0x1 << 5) ++#define RK3308_DAC_R_HPMIX_INIT (0x0 << 5) ++#define RK3308_DAC_R_HPMIX_EN (0x1 << 4) ++#define RK3308_DAC_R_HPMIX_DIS (0x0 << 4) ++#define RK3308_DAC_L_HPMIX_UNMUTE (0x1 << 2) ++#define RK3308_DAC_L_HPMIX_MUTE (0x0 << 2) ++#define RK3308_DAC_L_HPMIX_WORK (0x1 << 1) ++#define RK3308_DAC_L_HPMIX_INIT (0x0 << 1) ++#define RK3308_DAC_L_HPMIX_EN (0x1 << 0) ++#define RK3308_DAC_L_HPMIX_DIS (0x0 << 0) ++ ++#define RK3308_HIFI 0x0 ++ ++#endif /* __RK3308_CODEC_H__ */ +-- +2.25.1 + diff --git a/sys-kernel/decade-sources/files/patches-5.14/board-rockpis-0019-Sync-rk3308_codec-to-BSP-tree.patch b/sys-kernel/decade-sources/files/patches-5.14/board-rockpis-0019-Sync-rk3308_codec-to-BSP-tree.patch new file mode 100644 index 0000000..0cb1086 --- /dev/null +++ b/sys-kernel/decade-sources/files/patches-5.14/board-rockpis-0019-Sync-rk3308_codec-to-BSP-tree.patch @@ -0,0 +1,6740 @@ +From 26d61ff64d9a61425d017846db61e9a06de07286 Mon Sep 17 00:00:00 2001 +From: ashthespy +Date: Mon, 3 Feb 2020 17:13:59 +0100 +Subject: [PATCH 19/23] Sync `rk3308_codec` to BSP tree + +--- + .../bindings/sound/rockchip,rk3308-codec.txt | 78 + + sound/soc/codecs/rk3308_codec.c | 5687 ++++++++++++++--- + sound/soc/codecs/rk3308_codec.h | 217 +- + sound/soc/codecs/rk3308_codec_provider.h | 28 + + 4 files changed, 4894 insertions(+), 1116 deletions(-) + create mode 100644 Documentation/devicetree/bindings/sound/rockchip,rk3308-codec.txt + create mode 100644 sound/soc/codecs/rk3308_codec_provider.h + +diff --git a/Documentation/devicetree/bindings/sound/rockchip,rk3308-codec.txt b/Documentation/devicetree/bindings/sound/rockchip,rk3308-codec.txt +new file mode 100644 +index 000000000000..e20bbd73e37e +--- /dev/null ++++ b/Documentation/devicetree/bindings/sound/rockchip,rk3308-codec.txt +@@ -0,0 +1,78 @@ ++* Rockchip RK3308 Internal Codec ++ ++Required properties: ++ ++- compatible: "rockchip,rk3308-codec" ++- reg: The physical base address of the controller and length of memory ++ mapped region. ++- rockchip,grf: The phandle of the syscon node for GRF register. ++- clocks: A list of phandle + clock-specifer pairs, one for each entry in ++ clock-names. ++- clock-names: It should be "acodec". ++- resets : Must contain an entry for each entry in reset-names. ++- reset-names : Must include the following entries: "acodec-reset". ++ ++Optional properties: ++- rockchip,enable-all-adcs: This is a boolean type property, that shows whether ++ force enable all of ADCs. The following shows the relationship between grps ++ and ADC: ++ * grp 0 -- select ADC1 / ADC2 ++ * grp 1 -- select ADC3 / ADC4 ++ * grp 2 -- select ADC5 / ADC6 ++ * grp 3 -- select ADC7 / ADC8 ++ If the property is not used, the enabled ADC groups refer to needed channels ++ via configure hw_params. ++ ++- rockchip,adc-grps-route: This is a variable length array, that shows the ++ mapping route of ACODEC sdo to I2S sdi. By default, they are one-to-one ++ mapping: ++ * sdi_0 <-- sdo_0 ++ * sdi_1 <-- sdo_1 ++ * sdi_2 <-- sdo_2 ++ * sdi_3 <-- sdo_3 ++ If you would like to change the route mapping like this: ++ * sdi_0 <-- sdo_3 ++ * sdi_1 <-- sdo_0 ++ * sdi_2 <-- sdo_2 ++ * sdi_3 <-- sdo_1 ++ You need to add the property on dts: ++ - rockchip,adc-grps-route = <3 0 2 1>; ++ ++- rockchip,delay-loopback-handle-ms: This property points out that the delay for ++ handling ADC after enable PAs during loopback. ++- rockchip,delay-start-play-ms: This property points out the delay ms of start ++ playback according to different amplifier performance. ++- rockchip,en-always-grps: This property will keep the needed ADCs enabled ++ always after enabling once. ++- rockchip,loopback-grp: It points out the ADC group which is the loopback used. ++- rockchip,no-deep-low-power: The codec will not enter deep low power mode ++ during suspend. ++- rockchip,no-hp-det: If there is no headphone on boards, we don't need to ++ enable headphone detection. ++- rockchip,micbias1: Using internal micbias1 supply which are from codec. ++- rockchip,micbias2: Using internal micbias2 supply which are from codec. ++- rockchip,hp-jack-reversed;: To detect headphone via the reversed jack. ++- hp-ctl-gpios: The gpio of head phone controller. ++- pa-drv-gpios: The gpio of poweramplifier controller ++- rockchip,delay-pa-drv-ms: This property points out that the delay for ++ power on amplifier ++- spk-ctl-gpios: The gpio of speak controller. ++- micbias-en-gpios: The GPIO to enable external micbias. ++- vmicbias-supply: The phandle to the regulator to handle external micbias. ++ ++Example for rk3308 internal codec: ++ ++acodec: acodec@ff560000 { ++ compatible = "rockchip,rk3308-codec"; ++ reg = <0x0 0xff560000 0x0 0x10000>; ++ rockchip,grf = <&grf>; ++ clocks = <&cru PCLK_ACODEC>; ++ clock-names = "acodec"; ++ resets = <&cru SRST_ACODEC_P>; ++ reset-names = "acodec-reset"; ++ rockchip,loopback-grp = <0>; ++ hp-ctl-gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>; ++ pa-drv-gpios = <&gpio0 RK_PC0 GPIO_ACTIVE_HIGH>; ++ spk-ctl-gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>; ++ status = "okay"; ++}; +diff --git a/sound/soc/codecs/rk3308_codec.c b/sound/soc/codecs/rk3308_codec.c +index 106f09738dd0..815e22fc346c 100644 +--- a/sound/soc/codecs/rk3308_codec.c ++++ b/sound/soc/codecs/rk3308_codec.c +@@ -29,1420 +29,4699 @@ + #include + #include + #include ++#include + #include + #include + #include + #include + #include + #include ++#include + #include + #include ++#include + #include + #include + + #include "rk3308_codec.h" ++#include "rk3308_codec_provider.h" ++ ++#if defined(CONFIG_DEBUG_FS) ++#include ++#include ++#include ++#endif ++ ++#define CODEC_DRV_NAME "rk3308-acodec" ++ ++#define ADC_GRP_SKIP_MAGIC 0x1001 ++#define ADC_LR_GROUP_MAX 4 ++#define ADC_STABLE_MS 200 ++#define DEBUG_POP_ALWAYS 0 ++#define HPDET_POLL_MS 2000 ++#define NOT_USED 255 ++#define LOOPBACK_HANDLE_MS 100 ++#define PA_DRV_MS 5 ++ ++#define GRF_SOC_CON1 0x304 ++#define GRF_CHIP_ID 0x800 ++#define GRF_I2S2_8CH_SDI_SFT 0 ++#define GRF_I2S3_4CH_SDI_SFT 8 ++#define GRF_I2S1_2CH_SDI_SFT 12 ++ ++#define GRF_I2S2_8CH_SDI_R_MSK(i, v) ((v >> (i * 2 + GRF_I2S2_8CH_SDI_SFT)) & 0x3) ++#define GRF_I2S2_8CH_SDI_W_MSK(i) (0x3 << (i * 2 + GRF_I2S2_8CH_SDI_SFT + 16)) ++#define GRF_I2S2_8CH_SDI(i, v) (((v & 0x3) << (i * 2 + GRF_I2S2_8CH_SDI_SFT)) |\ ++ GRF_I2S2_8CH_SDI_W_MSK(i)) ++ ++#define GRF_I2S3_4CH_SDI_W_MSK(i) (0x3 << (i * 2 + GRF_I2S3_4CH_SDI_SFT + 16)) ++#define GRF_I2S3_4CH_SDI(i, v) (((v & 0x3) << (i * 2 + GRF_I2S3_4CH_SDI_SFT)) |\ ++ GRF_I2S3_4CH_SDI_W_MSK(i)) ++ ++#define GRF_I2S1_2CH_SDI_W_MSK (0x3 << (GRF_I2S1_2CH_SDI_SFT + 16)) ++#define GRF_I2S1_2CH_SDI(v) (((v & 0x3) << GRF_I2S1_2CH_SDI_SFT) |\ ++ GRF_I2S1_2CH_SDI_W_MSK) ++ ++#define DETECT_GRF_ACODEC_HPDET_COUNTER 0x0030 ++#define DETECT_GRF_ACODEC_HPDET_CON 0x0034 ++#define DETECT_GRF_ACODEC_HPDET_STATUS 0x0038 ++#define DETECT_GRF_ACODEC_HPDET_STATUS_CLR 0x003c ++ ++/* 200ms based on pclk is 100MHz */ ++#define DEFAULT_HPDET_COUNT 20000000 ++#define HPDET_NEG_IRQ_SFT 1 ++#define HPDET_POS_IRQ_SFT 0 ++#define HPDET_BOTH_NEG_POS ((1 << HPDET_NEG_IRQ_SFT) |\ ++ (1 << HPDET_POS_IRQ_SFT)) ++ ++#define ACODEC_VERSION_A 0xa ++#define ACODEC_VERSION_B 0xb ++ ++enum { ++ ACODEC_TO_I2S2_8CH = 0, ++ ACODEC_TO_I2S3_4CH, ++ ACODEC_TO_I2S1_2CH, ++}; ++ ++enum { ++ ADC_GRP0_MICIN = 0, ++ ADC_GRP0_LINEIN ++}; ++ ++enum { ++ ADC_TYPE_NORMAL = 0, ++ ADC_TYPE_LOOPBACK, ++ ADC_TYPE_DBG, ++ ADC_TYPE_ALL, ++}; ++ ++enum { ++ DAC_LINEOUT = 0, ++ DAC_HPOUT = 1, ++ DAC_LINEOUT_HPOUT = 11, ++}; ++ ++enum { ++ EXT_MICBIAS_NONE = 0, ++ EXT_MICBIAS_FUNC1, /* enable external micbias via GPIO */ ++ EXT_MICBIAS_FUNC2, /* enable external micbias via regulator */ ++}; ++ ++enum { ++ PATH_IDLE = 0, ++ PATH_BUSY, ++}; ++ ++enum { ++ PM_NORMAL = 0, ++ PM_LLP_DOWN, /* light low power down */ ++ PM_LLP_UP, ++ PM_DLP_DOWN, /* deep low power down */ ++ PM_DLP_UP, ++ PM_DLP_DOWN2, ++ PM_DLP_UP2, ++}; + + struct rk3308_codec_priv { + const struct device *plat_dev; + struct device dev; + struct reset_control *reset; + struct regmap *regmap; ++ struct regmap *grf; ++ struct regmap *detect_grf; + struct clk *pclk; ++ struct clk *mclk_rx; ++ struct clk *mclk_tx; ++ struct gpio_desc *micbias_en_gpio; ++ struct gpio_desc *hp_ctl_gpio; + struct gpio_desc *spk_ctl_gpio; +- int adc_ch; /* To select ADCs for channel */ +- int adc_ch0_using_linein; ++ struct gpio_desc *pa_drv_gpio; ++ struct snd_soc_codec *codec; ++ struct snd_soc_jack *hpdet_jack; ++ struct regulator *vcc_micbias; ++ u32 codec_ver; ++ ++ /* ++ * To select ADCs for groups: ++ * ++ * grp 0 -- select ADC1 / ADC2 ++ * grp 1 -- select ADC3 / ADC4 ++ * grp 2 -- select ADC5 / ADC6 ++ * grp 3 -- select ADC7 / ADC8 ++ */ ++ u32 used_adc_grps; ++ /* The ADC group which is used for loop back */ ++ u32 loopback_grp; ++ u32 cur_dbg_grp; ++ u32 en_always_grps[ADC_LR_GROUP_MAX]; ++ u32 en_always_grps_num; ++ u32 skip_grps[ADC_LR_GROUP_MAX]; ++ u32 i2s_sdis[ADC_LR_GROUP_MAX]; ++ u32 to_i2s_grps; ++ u32 delay_loopback_handle_ms; ++ u32 delay_start_play_ms; ++ u32 delay_pa_drv_ms; ++ u32 micbias_num; ++ u32 micbias_volt; ++ int which_i2s; ++ int irq; ++ int adc_grp0_using_linein; ++ int adc_zerocross; ++ /* 0: line out, 1: hp out, 11: lineout and hpout */ ++ int dac_output; ++ int dac_path_state; ++ ++ int ext_micbias; ++ int pm_state; ++ ++ /* AGC L/R Off/on */ ++ unsigned int agc_l[ADC_LR_GROUP_MAX]; ++ unsigned int agc_r[ADC_LR_GROUP_MAX]; ++ ++ /* AGC L/R Approximate Sample Rate */ ++ unsigned int agc_asr_l[ADC_LR_GROUP_MAX]; ++ unsigned int agc_asr_r[ADC_LR_GROUP_MAX]; ++ ++ /* ADC MIC Mute/Work */ ++ unsigned int mic_mute_l[ADC_LR_GROUP_MAX]; ++ unsigned int mic_mute_r[ADC_LR_GROUP_MAX]; ++ ++ /* For the high pass filter */ ++ unsigned int hpf_cutoff[ADC_LR_GROUP_MAX]; ++ ++ /* Only hpout do fade-in and fade-out */ ++ unsigned int hpout_l_dgain; ++ unsigned int hpout_r_dgain; ++ ++ bool adc_grps_endisable[ADC_LR_GROUP_MAX]; ++ bool dac_endisable; ++ bool enable_all_adcs; ++ bool enable_micbias; ++ bool micbias1; ++ bool micbias2; ++ bool hp_jack_reversed; ++ bool hp_plugged; ++ bool loopback_dacs_enabled; ++ bool no_deep_low_power; ++ bool no_hp_det; ++ struct delayed_work hpdet_work; ++ struct delayed_work loopback_work; ++ ++#if defined(CONFIG_DEBUG_FS) ++ struct dentry *dbg_codec; ++#endif + }; + +-static const DECLARE_TLV_DB_SCALE(rk3308_codec_alc_agc_ch_gain_tlv, ++static const DECLARE_TLV_DB_SCALE(rk3308_codec_alc_agc_grp_gain_tlv, + -1800, 150, 2850); +-static const DECLARE_TLV_DB_SCALE(rk3308_codec_alc_agc_ch_max_gain_tlv, ++static const DECLARE_TLV_DB_SCALE(rk3308_codec_alc_agc_grp_max_gain_tlv, + -1350, 600, 2850); +-static const DECLARE_TLV_DB_SCALE(rk3308_codec_alc_agc_ch_min_gain_tlv, ++static const DECLARE_TLV_DB_SCALE(rk3308_codec_alc_agc_grp_min_gain_tlv, + -1800, 600, 2400); +-static const DECLARE_TLV_DB_SCALE(rk3308_codec_adc_mic_gain_tlv, +- 0, 600, 3000); + static const DECLARE_TLV_DB_SCALE(rk3308_codec_adc_alc_gain_tlv, + -1800, 150, 2850); +-static const DECLARE_TLV_DB_SCALE(rk3308_codec_dac_gain_tlv, +- 0, 150, 600); ++static const DECLARE_TLV_DB_SCALE(rk3308_codec_dac_lineout_gain_tlv, ++ -600, 150, 0); + static const DECLARE_TLV_DB_SCALE(rk3308_codec_dac_hpout_gain_tlv, + -3900, 150, 600); + static const DECLARE_TLV_DB_SCALE(rk3308_codec_dac_hpmix_gain_tlv, + -600, 600, 0); + ++static const DECLARE_TLV_DB_RANGE(rk3308_codec_adc_mic_gain_tlv_a, ++ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), ++ 3, 3, TLV_DB_SCALE_ITEM(2000, 0, 0), ++); ++ ++static const DECLARE_TLV_DB_RANGE(rk3308_codec_adc_mic_gain_tlv_b, ++ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), ++ 1, 1, TLV_DB_SCALE_ITEM(660, 0, 0), ++ 2, 2, TLV_DB_SCALE_ITEM(1300, 0, 0), ++ 3, 3, TLV_DB_SCALE_ITEM(2000, 0, 0), ++); ++ ++static bool handle_loopback(struct rk3308_codec_priv *rk3308); ++ ++static int check_micbias(int micbias); ++ ++static int rk3308_codec_micbias_enable(struct rk3308_codec_priv *rk3308, ++ int micbias); ++static int rk3308_codec_micbias_disable(struct rk3308_codec_priv *rk3308); ++ ++static int rk3308_codec_hpout_l_get_tlv(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol); ++static int rk3308_codec_hpout_l_put_tlv(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol); ++static int rk3308_codec_hpout_r_get_tlv(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol); ++static int rk3308_codec_hpout_r_put_tlv(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol); ++static int rk3308_codec_hpf_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol); ++static int rk3308_codec_hpf_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol); ++static int rk3308_codec_agc_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol); ++static int rk3308_codec_agc_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol); ++static int rk3308_codec_agc_asr_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol); ++static int rk3308_codec_agc_asr_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol); ++static int rk3308_codec_mic_mute_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol); ++static int rk3308_codec_mic_mute_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol); ++static int rk3308_codec_mic_gain_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol); ++static int rk3308_codec_mic_gain_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol); ++static int rk3308_codec_micbias_volts_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol); ++static int rk3308_codec_micbias_volts_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol); ++static int rk3308_codec_main_micbias_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol); ++static int rk3308_codec_main_micbias_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol); ++ ++static const char *offon_text[2] = { ++ [0] = "Off", ++ [1] = "On", ++}; ++ ++static const char *mute_text[2] = { ++ [0] = "Work", ++ [1] = "Mute", ++}; ++ ++/* ADC MICBIAS Volt */ ++#define MICBIAS_VOLT_NUM 8 ++ ++#define MICBIAS_VREFx0_5 0 ++#define MICBIAS_VREFx0_55 1 ++#define MICBIAS_VREFx0_6 2 ++#define MICBIAS_VREFx0_65 3 ++#define MICBIAS_VREFx0_7 4 ++#define MICBIAS_VREFx0_75 5 ++#define MICBIAS_VREFx0_8 6 ++#define MICBIAS_VREFx0_85 7 ++ ++static const char *micbias_volts_enum_array[MICBIAS_VOLT_NUM] = { ++ [MICBIAS_VREFx0_5] = "VREFx0_5", ++ [MICBIAS_VREFx0_55] = "VREFx0_55", ++ [MICBIAS_VREFx0_6] = "VREFx0_6", ++ [MICBIAS_VREFx0_65] = "VREFx0_65", ++ [MICBIAS_VREFx0_7] = "VREFx0_7", ++ [MICBIAS_VREFx0_75] = "VREFx0_75", ++ [MICBIAS_VREFx0_8] = "VREFx0_8", ++ [MICBIAS_VREFx0_85] = "VREFx0_85", ++}; ++ ++static const struct soc_enum rk3308_micbias_volts_enum_array[] = { ++ SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(micbias_volts_enum_array), micbias_volts_enum_array), ++}; ++ ++/* ADC MICBIAS1 and MICBIAS2 Main Switch */ ++static const struct soc_enum rk3308_main_micbias_enum_array[] = { ++ SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(offon_text), offon_text), ++}; ++ ++static const struct soc_enum rk3308_hpf_enum_array[] = { ++ SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(offon_text), offon_text), ++ SOC_ENUM_SINGLE(1, 0, ARRAY_SIZE(offon_text), offon_text), ++ SOC_ENUM_SINGLE(2, 0, ARRAY_SIZE(offon_text), offon_text), ++ SOC_ENUM_SINGLE(3, 0, ARRAY_SIZE(offon_text), offon_text), ++}; ++ ++/* ALC AGC Switch */ ++static const struct soc_enum rk3308_agc_enum_array[] = { ++ SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(offon_text), offon_text), ++ SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(offon_text), offon_text), ++ SOC_ENUM_SINGLE(1, 0, ARRAY_SIZE(offon_text), offon_text), ++ SOC_ENUM_SINGLE(1, 1, ARRAY_SIZE(offon_text), offon_text), ++ SOC_ENUM_SINGLE(2, 0, ARRAY_SIZE(offon_text), offon_text), ++ SOC_ENUM_SINGLE(2, 1, ARRAY_SIZE(offon_text), offon_text), ++ SOC_ENUM_SINGLE(3, 0, ARRAY_SIZE(offon_text), offon_text), ++ SOC_ENUM_SINGLE(3, 1, ARRAY_SIZE(offon_text), offon_text), ++}; ++ ++/* ADC MIC Mute/Work Switch */ ++static const struct soc_enum rk3308_mic_mute_enum_array[] = { ++ SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(mute_text), mute_text), ++ SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(mute_text), mute_text), ++ SOC_ENUM_SINGLE(1, 0, ARRAY_SIZE(mute_text), mute_text), ++ SOC_ENUM_SINGLE(1, 1, ARRAY_SIZE(mute_text), mute_text), ++ SOC_ENUM_SINGLE(2, 0, ARRAY_SIZE(mute_text), mute_text), ++ SOC_ENUM_SINGLE(2, 1, ARRAY_SIZE(mute_text), mute_text), ++ SOC_ENUM_SINGLE(3, 0, ARRAY_SIZE(mute_text), mute_text), ++ SOC_ENUM_SINGLE(3, 1, ARRAY_SIZE(mute_text), mute_text), ++}; ++ ++/* ALC AGC Approximate Sample Rate */ ++#define AGC_ASR_NUM 8 ++ ++#define AGC_ASR_96KHZ 0 ++#define AGC_ASR_48KHZ 1 ++#define AGC_ASR_44_1KHZ 2 ++#define AGC_ASR_32KHZ 3 ++#define AGC_ASR_24KHZ 4 ++#define AGC_ASR_16KHZ 5 ++#define AGC_ASR_12KHZ 6 ++#define AGC_ASR_8KHZ 7 ++ ++static const char *agc_asr_text[AGC_ASR_NUM] = { ++ [AGC_ASR_96KHZ] = "96KHz", ++ [AGC_ASR_48KHZ] = "48KHz", ++ [AGC_ASR_44_1KHZ] = "44.1KHz", ++ [AGC_ASR_32KHZ] = "32KHz", ++ [AGC_ASR_24KHZ] = "24KHz", ++ [AGC_ASR_16KHZ] = "16KHz", ++ [AGC_ASR_12KHZ] = "12KHz", ++ [AGC_ASR_8KHZ] = "8KHz", ++}; ++ ++static const struct soc_enum rk3308_agc_asr_enum_array[] = { ++ SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(agc_asr_text), agc_asr_text), ++ SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(agc_asr_text), agc_asr_text), ++ SOC_ENUM_SINGLE(1, 0, ARRAY_SIZE(agc_asr_text), agc_asr_text), ++ SOC_ENUM_SINGLE(1, 1, ARRAY_SIZE(agc_asr_text), agc_asr_text), ++ SOC_ENUM_SINGLE(2, 0, ARRAY_SIZE(agc_asr_text), agc_asr_text), ++ SOC_ENUM_SINGLE(2, 1, ARRAY_SIZE(agc_asr_text), agc_asr_text), ++ SOC_ENUM_SINGLE(3, 0, ARRAY_SIZE(agc_asr_text), agc_asr_text), ++ SOC_ENUM_SINGLE(3, 1, ARRAY_SIZE(agc_asr_text), agc_asr_text), ++}; ++ ++static const struct snd_kcontrol_new mic_gains_a[] = { ++ /* ADC MIC */ ++ SOC_SINGLE_EXT_TLV("ADC MIC Group 0 Left Volume", ++ RK3308_ADC_ANA_CON01(0), ++ RK3308_ADC_CH1_MIC_GAIN_SFT, ++ RK3308_ADC_CH1_MIC_GAIN_MAX, ++ 0, ++ rk3308_codec_mic_gain_get, ++ rk3308_codec_mic_gain_put, ++ rk3308_codec_adc_mic_gain_tlv_a), ++ SOC_SINGLE_EXT_TLV("ADC MIC Group 0 Right Volume", ++ RK3308_ADC_ANA_CON01(0), ++ RK3308_ADC_CH2_MIC_GAIN_SFT, ++ RK3308_ADC_CH2_MIC_GAIN_MAX, ++ 0, ++ rk3308_codec_mic_gain_get, ++ rk3308_codec_mic_gain_put, ++ rk3308_codec_adc_mic_gain_tlv_a), ++ SOC_SINGLE_EXT_TLV("ADC MIC Group 1 Left Volume", ++ RK3308_ADC_ANA_CON01(1), ++ RK3308_ADC_CH1_MIC_GAIN_SFT, ++ RK3308_ADC_CH1_MIC_GAIN_MAX, ++ 0, ++ rk3308_codec_mic_gain_get, ++ rk3308_codec_mic_gain_put, ++ rk3308_codec_adc_mic_gain_tlv_a), ++ SOC_SINGLE_EXT_TLV("ADC MIC Group 1 Right Volume", ++ RK3308_ADC_ANA_CON01(1), ++ RK3308_ADC_CH2_MIC_GAIN_SFT, ++ RK3308_ADC_CH2_MIC_GAIN_MAX, ++ 0, ++ rk3308_codec_mic_gain_get, ++ rk3308_codec_mic_gain_put, ++ rk3308_codec_adc_mic_gain_tlv_a), ++ SOC_SINGLE_EXT_TLV("ADC MIC Group 2 Left Volume", ++ RK3308_ADC_ANA_CON01(2), ++ RK3308_ADC_CH1_MIC_GAIN_SFT, ++ RK3308_ADC_CH1_MIC_GAIN_MAX, ++ 0, ++ rk3308_codec_mic_gain_get, ++ rk3308_codec_mic_gain_put, ++ rk3308_codec_adc_mic_gain_tlv_a), ++ SOC_SINGLE_EXT_TLV("ADC MIC Group 2 Right Volume", ++ RK3308_ADC_ANA_CON01(2), ++ RK3308_ADC_CH2_MIC_GAIN_SFT, ++ RK3308_ADC_CH2_MIC_GAIN_MAX, ++ 0, ++ rk3308_codec_mic_gain_get, ++ rk3308_codec_mic_gain_put, ++ rk3308_codec_adc_mic_gain_tlv_a), ++ SOC_SINGLE_EXT_TLV("ADC MIC Group 3 Left Volume", ++ RK3308_ADC_ANA_CON01(3), ++ RK3308_ADC_CH1_MIC_GAIN_SFT, ++ RK3308_ADC_CH1_MIC_GAIN_MAX, ++ 0, ++ rk3308_codec_mic_gain_get, ++ rk3308_codec_mic_gain_put, ++ rk3308_codec_adc_mic_gain_tlv_a), ++ SOC_SINGLE_EXT_TLV("ADC MIC Group 3 Right Volume", ++ RK3308_ADC_ANA_CON01(3), ++ RK3308_ADC_CH2_MIC_GAIN_SFT, ++ RK3308_ADC_CH2_MIC_GAIN_MAX, ++ 0, ++ rk3308_codec_mic_gain_get, ++ rk3308_codec_mic_gain_put, ++ rk3308_codec_adc_mic_gain_tlv_a), ++}; ++ ++static const struct snd_kcontrol_new mic_gains_b[] = { ++ /* ADC MIC */ ++ SOC_SINGLE_EXT_TLV("ADC MIC Group 0 Left Volume", ++ RK3308_ADC_ANA_CON01(0), ++ RK3308_ADC_CH1_MIC_GAIN_SFT, ++ RK3308_ADC_CH1_MIC_GAIN_MAX, ++ 0, ++ rk3308_codec_mic_gain_get, ++ rk3308_codec_mic_gain_put, ++ rk3308_codec_adc_mic_gain_tlv_b), ++ SOC_SINGLE_EXT_TLV("ADC MIC Group 0 Right Volume", ++ RK3308_ADC_ANA_CON01(0), ++ RK3308_ADC_CH2_MIC_GAIN_SFT, ++ RK3308_ADC_CH2_MIC_GAIN_MAX, ++ 0, ++ rk3308_codec_mic_gain_get, ++ rk3308_codec_mic_gain_put, ++ rk3308_codec_adc_mic_gain_tlv_b), ++ SOC_SINGLE_EXT_TLV("ADC MIC Group 1 Left Volume", ++ RK3308_ADC_ANA_CON01(1), ++ RK3308_ADC_CH1_MIC_GAIN_SFT, ++ RK3308_ADC_CH1_MIC_GAIN_MAX, ++ 0, ++ rk3308_codec_mic_gain_get, ++ rk3308_codec_mic_gain_put, ++ rk3308_codec_adc_mic_gain_tlv_b), ++ SOC_SINGLE_EXT_TLV("ADC MIC Group 1 Right Volume", ++ RK3308_ADC_ANA_CON01(1), ++ RK3308_ADC_CH2_MIC_GAIN_SFT, ++ RK3308_ADC_CH2_MIC_GAIN_MAX, ++ 0, ++ rk3308_codec_mic_gain_get, ++ rk3308_codec_mic_gain_put, ++ rk3308_codec_adc_mic_gain_tlv_b), ++ SOC_SINGLE_EXT_TLV("ADC MIC Group 2 Left Volume", ++ RK3308_ADC_ANA_CON01(2), ++ RK3308_ADC_CH1_MIC_GAIN_SFT, ++ RK3308_ADC_CH1_MIC_GAIN_MAX, ++ 0, ++ rk3308_codec_mic_gain_get, ++ rk3308_codec_mic_gain_put, ++ rk3308_codec_adc_mic_gain_tlv_b), ++ SOC_SINGLE_EXT_TLV("ADC MIC Group 2 Right Volume", ++ RK3308_ADC_ANA_CON01(2), ++ RK3308_ADC_CH2_MIC_GAIN_SFT, ++ RK3308_ADC_CH2_MIC_GAIN_MAX, ++ 0, ++ rk3308_codec_mic_gain_get, ++ rk3308_codec_mic_gain_put, ++ rk3308_codec_adc_mic_gain_tlv_b), ++ SOC_SINGLE_EXT_TLV("ADC MIC Group 3 Left Volume", ++ RK3308_ADC_ANA_CON01(3), ++ RK3308_ADC_CH1_MIC_GAIN_SFT, ++ RK3308_ADC_CH1_MIC_GAIN_MAX, ++ 0, ++ rk3308_codec_mic_gain_get, ++ rk3308_codec_mic_gain_put, ++ rk3308_codec_adc_mic_gain_tlv_b), ++ SOC_SINGLE_EXT_TLV("ADC MIC Group 3 Right Volume", ++ RK3308_ADC_ANA_CON01(3), ++ RK3308_ADC_CH2_MIC_GAIN_SFT, ++ RK3308_ADC_CH2_MIC_GAIN_MAX, ++ 0, ++ rk3308_codec_mic_gain_get, ++ rk3308_codec_mic_gain_put, ++ rk3308_codec_adc_mic_gain_tlv_b), ++}; ++ + static const struct snd_kcontrol_new rk3308_codec_dapm_controls[] = { +- /* ALC AGC Channel*/ +- SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 0 Volume", ++ /* ALC AGC Group */ ++ SOC_SINGLE_RANGE_TLV("ALC AGC Group 0 Left Volume", + RK3308_ALC_L_DIG_CON03(0), ++ RK3308_AGC_PGA_GAIN_SFT, ++ RK3308_AGC_PGA_GAIN_MIN, ++ RK3308_AGC_PGA_GAIN_MAX, ++ 0, rk3308_codec_alc_agc_grp_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("ALC AGC Group 0 Right Volume", + RK3308_ALC_R_DIG_CON03(0), + RK3308_AGC_PGA_GAIN_SFT, +- RK3308_AGC_PGA_GAIN_NDB_18, +- RK3308_AGC_PGA_GAIN_PDB_28_5, +- 0, rk3308_codec_alc_agc_ch_gain_tlv), +- SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 1 Volume", ++ RK3308_AGC_PGA_GAIN_MIN, ++ RK3308_AGC_PGA_GAIN_MAX, ++ 0, rk3308_codec_alc_agc_grp_gain_tlv), ++ ++ SOC_SINGLE_RANGE_TLV("ALC AGC Group 1 Left Volume", + RK3308_ALC_L_DIG_CON03(1), ++ RK3308_AGC_PGA_GAIN_SFT, ++ RK3308_AGC_PGA_GAIN_MIN, ++ RK3308_AGC_PGA_GAIN_MAX, ++ 0, rk3308_codec_alc_agc_grp_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("ALC AGC Group 1 Right Volume", + RK3308_ALC_R_DIG_CON03(1), + RK3308_AGC_PGA_GAIN_SFT, +- RK3308_AGC_PGA_GAIN_NDB_18, +- RK3308_AGC_PGA_GAIN_PDB_28_5, +- 0, rk3308_codec_alc_agc_ch_gain_tlv), +- SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 2 Volume", ++ RK3308_AGC_PGA_GAIN_MIN, ++ RK3308_AGC_PGA_GAIN_MAX, ++ 0, rk3308_codec_alc_agc_grp_gain_tlv), ++ ++ SOC_SINGLE_RANGE_TLV("ALC AGC Group 2 Left Volume", + RK3308_ALC_L_DIG_CON03(2), ++ RK3308_AGC_PGA_GAIN_SFT, ++ RK3308_AGC_PGA_GAIN_MIN, ++ RK3308_AGC_PGA_GAIN_MAX, ++ 0, rk3308_codec_alc_agc_grp_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("ALC AGC Group 2 Right Volume", + RK3308_ALC_R_DIG_CON03(2), + RK3308_AGC_PGA_GAIN_SFT, +- RK3308_AGC_PGA_GAIN_NDB_18, +- RK3308_AGC_PGA_GAIN_PDB_28_5, +- 0, rk3308_codec_alc_agc_ch_gain_tlv), +- SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 3 Volume", ++ RK3308_AGC_PGA_GAIN_MIN, ++ RK3308_AGC_PGA_GAIN_MAX, ++ 0, rk3308_codec_alc_agc_grp_gain_tlv), ++ ++ SOC_SINGLE_RANGE_TLV("ALC AGC Group 3 Left Volume", + RK3308_ALC_L_DIG_CON03(3), ++ RK3308_AGC_PGA_GAIN_SFT, ++ RK3308_AGC_PGA_GAIN_MIN, ++ RK3308_AGC_PGA_GAIN_MAX, ++ 0, rk3308_codec_alc_agc_grp_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("ALC AGC Group 3 Right Volume", + RK3308_ALC_R_DIG_CON03(3), + RK3308_AGC_PGA_GAIN_SFT, +- RK3308_AGC_PGA_GAIN_NDB_18, +- RK3308_AGC_PGA_GAIN_PDB_28_5, +- 0, rk3308_codec_alc_agc_ch_gain_tlv), ++ RK3308_AGC_PGA_GAIN_MIN, ++ RK3308_AGC_PGA_GAIN_MAX, ++ 0, rk3308_codec_alc_agc_grp_gain_tlv), + + /* ALC AGC MAX */ +- SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 0 Max Volume", ++ SOC_SINGLE_RANGE_TLV("ALC AGC Group 0 Left Max Volume", + RK3308_ALC_L_DIG_CON09(0), ++ RK3308_AGC_MAX_GAIN_PGA_SFT, ++ RK3308_AGC_MAX_GAIN_PGA_MIN, ++ RK3308_AGC_MAX_GAIN_PGA_MAX, ++ 0, rk3308_codec_alc_agc_grp_max_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("ALC AGC Group 0 Right Max Volume", + RK3308_ALC_R_DIG_CON09(0), + RK3308_AGC_MAX_GAIN_PGA_SFT, +- RK3308_AGC_MAX_GAIN_PGA_NDB_13_5, +- RK3308_AGC_MAX_GAIN_PGA_PDB_28_5, +- 0, rk3308_codec_alc_agc_ch_max_gain_tlv), +- SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 1 Max Volume", ++ RK3308_AGC_MAX_GAIN_PGA_MIN, ++ RK3308_AGC_MAX_GAIN_PGA_MAX, ++ 0, rk3308_codec_alc_agc_grp_max_gain_tlv), ++ ++ SOC_SINGLE_RANGE_TLV("ALC AGC Group 1 Left Max Volume", + RK3308_ALC_L_DIG_CON09(1), ++ RK3308_AGC_MAX_GAIN_PGA_SFT, ++ RK3308_AGC_MAX_GAIN_PGA_MIN, ++ RK3308_AGC_MAX_GAIN_PGA_MAX, ++ 0, rk3308_codec_alc_agc_grp_max_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("ALC AGC Group 1 Right Max Volume", + RK3308_ALC_R_DIG_CON09(1), + RK3308_AGC_MAX_GAIN_PGA_SFT, +- RK3308_AGC_MAX_GAIN_PGA_NDB_13_5, +- RK3308_AGC_MAX_GAIN_PGA_PDB_28_5, +- 0, rk3308_codec_alc_agc_ch_max_gain_tlv), +- SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 2 Max Volume", ++ RK3308_AGC_MAX_GAIN_PGA_MIN, ++ RK3308_AGC_MAX_GAIN_PGA_MAX, ++ 0, rk3308_codec_alc_agc_grp_max_gain_tlv), ++ ++ SOC_SINGLE_RANGE_TLV("ALC AGC Group 2 Left Max Volume", + RK3308_ALC_L_DIG_CON09(2), ++ RK3308_AGC_MAX_GAIN_PGA_SFT, ++ RK3308_AGC_MAX_GAIN_PGA_MIN, ++ RK3308_AGC_MAX_GAIN_PGA_MAX, ++ 0, rk3308_codec_alc_agc_grp_max_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("ALC AGC Group 2 Right Max Volume", + RK3308_ALC_R_DIG_CON09(2), + RK3308_AGC_MAX_GAIN_PGA_SFT, +- RK3308_AGC_MAX_GAIN_PGA_NDB_13_5, +- RK3308_AGC_MAX_GAIN_PGA_PDB_28_5, +- 0, rk3308_codec_alc_agc_ch_max_gain_tlv), +- SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 3 Max Volume", ++ RK3308_AGC_MAX_GAIN_PGA_MIN, ++ RK3308_AGC_MAX_GAIN_PGA_MAX, ++ 0, rk3308_codec_alc_agc_grp_max_gain_tlv), ++ ++ SOC_SINGLE_RANGE_TLV("ALC AGC Group 3 Left Max Volume", + RK3308_ALC_L_DIG_CON09(3), ++ RK3308_AGC_MAX_GAIN_PGA_SFT, ++ RK3308_AGC_MAX_GAIN_PGA_MIN, ++ RK3308_AGC_MAX_GAIN_PGA_MAX, ++ 0, rk3308_codec_alc_agc_grp_max_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("ALC AGC Group 3 Right Max Volume", + RK3308_ALC_R_DIG_CON09(3), + RK3308_AGC_MAX_GAIN_PGA_SFT, +- RK3308_AGC_MAX_GAIN_PGA_NDB_13_5, +- RK3308_AGC_MAX_GAIN_PGA_PDB_28_5, +- 0, rk3308_codec_alc_agc_ch_max_gain_tlv), ++ RK3308_AGC_MAX_GAIN_PGA_MIN, ++ RK3308_AGC_MAX_GAIN_PGA_MAX, ++ 0, rk3308_codec_alc_agc_grp_max_gain_tlv), + + /* ALC AGC MIN */ +- SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 0 Min Volume", ++ SOC_SINGLE_RANGE_TLV("ALC AGC Group 0 Left Min Volume", + RK3308_ALC_L_DIG_CON09(0), ++ RK3308_AGC_MIN_GAIN_PGA_SFT, ++ RK3308_AGC_MIN_GAIN_PGA_MIN, ++ RK3308_AGC_MIN_GAIN_PGA_MAX, ++ 0, rk3308_codec_alc_agc_grp_min_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("ALC AGC Group 0 Right Min Volume", + RK3308_ALC_R_DIG_CON09(0), + RK3308_AGC_MIN_GAIN_PGA_SFT, +- RK3308_AGC_MIN_GAIN_PGA_NDB_18, +- RK3308_AGC_MIN_GAIN_PGA_PDB_24, +- 0, rk3308_codec_alc_agc_ch_min_gain_tlv), +- SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 1 Min Volume", ++ RK3308_AGC_MIN_GAIN_PGA_MIN, ++ RK3308_AGC_MIN_GAIN_PGA_MAX, ++ 0, rk3308_codec_alc_agc_grp_min_gain_tlv), ++ ++ SOC_SINGLE_RANGE_TLV("ALC AGC Group 1 Left Min Volume", + RK3308_ALC_L_DIG_CON09(1), ++ RK3308_AGC_MIN_GAIN_PGA_SFT, ++ RK3308_AGC_MIN_GAIN_PGA_MIN, ++ RK3308_AGC_MIN_GAIN_PGA_MAX, ++ 0, rk3308_codec_alc_agc_grp_min_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("ALC AGC Group 1 Right Min Volume", + RK3308_ALC_R_DIG_CON09(1), + RK3308_AGC_MIN_GAIN_PGA_SFT, +- RK3308_AGC_MIN_GAIN_PGA_NDB_18, +- RK3308_AGC_MIN_GAIN_PGA_PDB_24, +- 0, rk3308_codec_alc_agc_ch_min_gain_tlv), +- SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 2 Min Volume", ++ RK3308_AGC_MIN_GAIN_PGA_MIN, ++ RK3308_AGC_MIN_GAIN_PGA_MAX, ++ 0, rk3308_codec_alc_agc_grp_min_gain_tlv), ++ ++ SOC_SINGLE_RANGE_TLV("ALC AGC Group 2 Left Min Volume", + RK3308_ALC_L_DIG_CON09(2), ++ RK3308_AGC_MIN_GAIN_PGA_SFT, ++ RK3308_AGC_MIN_GAIN_PGA_MIN, ++ RK3308_AGC_MIN_GAIN_PGA_MAX, ++ 0, rk3308_codec_alc_agc_grp_min_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("ALC AGC Group 2 Right Min Volume", + RK3308_ALC_R_DIG_CON09(2), + RK3308_AGC_MIN_GAIN_PGA_SFT, +- RK3308_AGC_MIN_GAIN_PGA_NDB_18, +- RK3308_AGC_MIN_GAIN_PGA_PDB_24, +- 0, rk3308_codec_alc_agc_ch_min_gain_tlv), +- SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 3 Min Volume", ++ RK3308_AGC_MIN_GAIN_PGA_MIN, ++ RK3308_AGC_MIN_GAIN_PGA_MAX, ++ 0, rk3308_codec_alc_agc_grp_min_gain_tlv), ++ ++ SOC_SINGLE_RANGE_TLV("ALC AGC Group 3 Left Min Volume", + RK3308_ALC_L_DIG_CON09(3), ++ RK3308_AGC_MIN_GAIN_PGA_SFT, ++ RK3308_AGC_MIN_GAIN_PGA_MIN, ++ RK3308_AGC_MIN_GAIN_PGA_MAX, ++ 0, rk3308_codec_alc_agc_grp_min_gain_tlv), ++ SOC_SINGLE_RANGE_TLV("ALC AGC Group 3 Right Min Volume", + RK3308_ALC_R_DIG_CON09(3), + RK3308_AGC_MIN_GAIN_PGA_SFT, +- RK3308_AGC_MIN_GAIN_PGA_NDB_18, +- RK3308_AGC_MIN_GAIN_PGA_PDB_24, +- 0, rk3308_codec_alc_agc_ch_min_gain_tlv), +- +- /* ADC MIC */ +- SOC_SINGLE_RANGE_TLV("ADC MIC Channel 0 Left Volume", +- RK3308_ADC_ANA_CON01(0), +- RK3308_ADC_CH1_MIC_GAIN_SFT, +- RK3308_ADC_CH1_MIC_GAIN_0DB, +- RK3308_ADC_CH1_MIC_GAIN_30DB, +- 0, rk3308_codec_adc_mic_gain_tlv), +- SOC_SINGLE_RANGE_TLV("ADC MIC Channel 0 Right Volume", +- RK3308_ADC_ANA_CON01(0), +- RK3308_ADC_CH2_MIC_GAIN_SFT, +- RK3308_ADC_CH2_MIC_GAIN_0DB, +- RK3308_ADC_CH2_MIC_GAIN_30DB, +- 0, rk3308_codec_adc_mic_gain_tlv), +- SOC_SINGLE_RANGE_TLV("ADC MIC Channel 1 Left Volume", +- RK3308_ADC_ANA_CON01(1), +- RK3308_ADC_CH1_MIC_GAIN_SFT, +- RK3308_ADC_CH1_MIC_GAIN_0DB, +- RK3308_ADC_CH1_MIC_GAIN_30DB, +- 0, rk3308_codec_adc_mic_gain_tlv), +- SOC_SINGLE_RANGE_TLV("ADC MIC Channel 1 Right Volume", +- RK3308_ADC_ANA_CON01(1), +- RK3308_ADC_CH2_MIC_GAIN_SFT, +- RK3308_ADC_CH2_MIC_GAIN_0DB, +- RK3308_ADC_CH2_MIC_GAIN_30DB, +- 0, rk3308_codec_adc_mic_gain_tlv), +- SOC_SINGLE_RANGE_TLV("ADC MIC Channel 2 Left Volume", +- RK3308_ADC_ANA_CON01(2), +- RK3308_ADC_CH1_MIC_GAIN_SFT, +- RK3308_ADC_CH1_MIC_GAIN_0DB, +- RK3308_ADC_CH1_MIC_GAIN_30DB, +- 0, rk3308_codec_adc_mic_gain_tlv), +- SOC_SINGLE_RANGE_TLV("ADC MIC Channel 2 Right Volume", +- RK3308_ADC_ANA_CON01(2), +- RK3308_ADC_CH2_MIC_GAIN_SFT, +- RK3308_ADC_CH2_MIC_GAIN_0DB, +- RK3308_ADC_CH2_MIC_GAIN_30DB, +- 0, rk3308_codec_adc_mic_gain_tlv), +- SOC_SINGLE_RANGE_TLV("ADC MIC Channel 3 Left Volume", +- RK3308_ADC_ANA_CON01(3), +- RK3308_ADC_CH1_MIC_GAIN_SFT, +- RK3308_ADC_CH1_MIC_GAIN_0DB, +- RK3308_ADC_CH1_MIC_GAIN_30DB, +- 0, rk3308_codec_adc_mic_gain_tlv), +- SOC_SINGLE_RANGE_TLV("ADC MIC Channel 3 Right Volume", +- RK3308_ADC_ANA_CON01(3), +- RK3308_ADC_CH2_MIC_GAIN_SFT, +- RK3308_ADC_CH2_MIC_GAIN_0DB, +- RK3308_ADC_CH2_MIC_GAIN_30DB, +- 0, rk3308_codec_adc_mic_gain_tlv), ++ RK3308_AGC_MIN_GAIN_PGA_MIN, ++ RK3308_AGC_MIN_GAIN_PGA_MAX, ++ 0, rk3308_codec_alc_agc_grp_min_gain_tlv), ++ ++ /* ALC AGC Switch */ ++ SOC_ENUM_EXT("ALC AGC Group 0 Left Switch", rk3308_agc_enum_array[0], ++ rk3308_codec_agc_get, rk3308_codec_agc_put), ++ SOC_ENUM_EXT("ALC AGC Group 0 Right Switch", rk3308_agc_enum_array[1], ++ rk3308_codec_agc_get, rk3308_codec_agc_put), ++ SOC_ENUM_EXT("ALC AGC Group 1 Left Switch", rk3308_agc_enum_array[2], ++ rk3308_codec_agc_get, rk3308_codec_agc_put), ++ SOC_ENUM_EXT("ALC AGC Group 1 Right Switch", rk3308_agc_enum_array[3], ++ rk3308_codec_agc_get, rk3308_codec_agc_put), ++ SOC_ENUM_EXT("ALC AGC Group 2 Left Switch", rk3308_agc_enum_array[4], ++ rk3308_codec_agc_get, rk3308_codec_agc_put), ++ SOC_ENUM_EXT("ALC AGC Group 2 Right Switch", rk3308_agc_enum_array[5], ++ rk3308_codec_agc_get, rk3308_codec_agc_put), ++ SOC_ENUM_EXT("ALC AGC Group 3 Left Switch", rk3308_agc_enum_array[6], ++ rk3308_codec_agc_get, rk3308_codec_agc_put), ++ SOC_ENUM_EXT("ALC AGC Group 3 Right Switch", rk3308_agc_enum_array[7], ++ rk3308_codec_agc_get, rk3308_codec_agc_put), ++ ++ /* ALC AGC Approximate Sample Rate */ ++ SOC_ENUM_EXT("AGC Group 0 Left Approximate Sample Rate", rk3308_agc_asr_enum_array[0], ++ rk3308_codec_agc_asr_get, rk3308_codec_agc_asr_put), ++ SOC_ENUM_EXT("AGC Group 0 Right Approximate Sample Rate", rk3308_agc_asr_enum_array[1], ++ rk3308_codec_agc_asr_get, rk3308_codec_agc_asr_put), ++ SOC_ENUM_EXT("AGC Group 1 Left Approximate Sample Rate", rk3308_agc_asr_enum_array[2], ++ rk3308_codec_agc_asr_get, rk3308_codec_agc_asr_put), ++ SOC_ENUM_EXT("AGC Group 1 Right Approximate Sample Rate", rk3308_agc_asr_enum_array[3], ++ rk3308_codec_agc_asr_get, rk3308_codec_agc_asr_put), ++ SOC_ENUM_EXT("AGC Group 2 Left Approximate Sample Rate", rk3308_agc_asr_enum_array[4], ++ rk3308_codec_agc_asr_get, rk3308_codec_agc_asr_put), ++ SOC_ENUM_EXT("AGC Group 2 Right Approximate Sample Rate", rk3308_agc_asr_enum_array[5], ++ rk3308_codec_agc_asr_get, rk3308_codec_agc_asr_put), ++ SOC_ENUM_EXT("AGC Group 3 Left Approximate Sample Rate", rk3308_agc_asr_enum_array[6], ++ rk3308_codec_agc_asr_get, rk3308_codec_agc_asr_put), ++ SOC_ENUM_EXT("AGC Group 3 Right Approximate Sample Rate", rk3308_agc_asr_enum_array[7], ++ rk3308_codec_agc_asr_get, rk3308_codec_agc_asr_put), ++ ++ /* ADC MICBIAS Voltage */ ++ SOC_ENUM_EXT("ADC MICBIAS Voltage", rk3308_micbias_volts_enum_array[0], ++ rk3308_codec_micbias_volts_get, rk3308_codec_micbias_volts_put), ++ ++ /* ADC Main MICBIAS Switch */ ++ SOC_ENUM_EXT("ADC Main MICBIAS", rk3308_main_micbias_enum_array[0], ++ rk3308_codec_main_micbias_get, rk3308_codec_main_micbias_put), ++ ++ /* ADC MICBIAS1 and MICBIAS2 Switch */ ++ SOC_SINGLE("ADC MICBIAS1", RK3308_ADC_ANA_CON07(1), ++ RK3308_ADC_MIC_BIAS_BUF_SFT, 1, 0), ++ SOC_SINGLE("ADC MICBIAS2", RK3308_ADC_ANA_CON07(2), ++ RK3308_ADC_MIC_BIAS_BUF_SFT, 1, 0), ++ ++ /* ADC MIC Mute/Work Switch */ ++ SOC_ENUM_EXT("ADC MIC Group 0 Left Switch", rk3308_mic_mute_enum_array[0], ++ rk3308_codec_mic_mute_get, rk3308_codec_mic_mute_put), ++ SOC_ENUM_EXT("ADC MIC Group 0 Right Switch", rk3308_mic_mute_enum_array[1], ++ rk3308_codec_mic_mute_get, rk3308_codec_mic_mute_put), ++ SOC_ENUM_EXT("ADC MIC Group 1 Left Switch", rk3308_mic_mute_enum_array[2], ++ rk3308_codec_mic_mute_get, rk3308_codec_mic_mute_put), ++ SOC_ENUM_EXT("ADC MIC Group 1 Right Switch", rk3308_mic_mute_enum_array[3], ++ rk3308_codec_mic_mute_get, rk3308_codec_mic_mute_put), ++ SOC_ENUM_EXT("ADC MIC Group 2 Left Switch", rk3308_mic_mute_enum_array[4], ++ rk3308_codec_mic_mute_get, rk3308_codec_mic_mute_put), ++ SOC_ENUM_EXT("ADC MIC Group 2 Right Switch", rk3308_mic_mute_enum_array[5], ++ rk3308_codec_mic_mute_get, rk3308_codec_mic_mute_put), ++ SOC_ENUM_EXT("ADC MIC Group 3 Left Switch", rk3308_mic_mute_enum_array[6], ++ rk3308_codec_mic_mute_get, rk3308_codec_mic_mute_put), ++ SOC_ENUM_EXT("ADC MIC Group 3 Right Switch", rk3308_mic_mute_enum_array[7], ++ rk3308_codec_mic_mute_get, rk3308_codec_mic_mute_put), + + /* ADC ALC */ +- SOC_SINGLE_RANGE_TLV("ADC ALC Channel 0 Left Volume", ++ SOC_SINGLE_RANGE_TLV("ADC ALC Group 0 Left Volume", + RK3308_ADC_ANA_CON03(0), + RK3308_ADC_CH1_ALC_GAIN_SFT, +- RK3308_ADC_CH1_ALC_GAIN_NDB_18, +- RK3308_ADC_CH1_ALC_GAIN_PDB_28_5, ++ RK3308_ADC_CH1_ALC_GAIN_MIN, ++ RK3308_ADC_CH1_ALC_GAIN_MAX, + 0, rk3308_codec_adc_alc_gain_tlv), +- SOC_SINGLE_RANGE_TLV("ADC ALC Channel 0 Right Volume", ++ SOC_SINGLE_RANGE_TLV("ADC ALC Group 0 Right Volume", + RK3308_ADC_ANA_CON04(0), + RK3308_ADC_CH2_ALC_GAIN_SFT, +- RK3308_ADC_CH2_ALC_GAIN_NDB_18, +- RK3308_ADC_CH2_ALC_GAIN_PDB_28_5, ++ RK3308_ADC_CH2_ALC_GAIN_MIN, ++ RK3308_ADC_CH2_ALC_GAIN_MAX, + 0, rk3308_codec_adc_alc_gain_tlv), +- SOC_SINGLE_RANGE_TLV("ADC ALC Channel 1 Left Volume", ++ SOC_SINGLE_RANGE_TLV("ADC ALC Group 1 Left Volume", + RK3308_ADC_ANA_CON03(1), + RK3308_ADC_CH1_ALC_GAIN_SFT, +- RK3308_ADC_CH1_ALC_GAIN_NDB_18, +- RK3308_ADC_CH1_ALC_GAIN_PDB_28_5, ++ RK3308_ADC_CH1_ALC_GAIN_MIN, ++ RK3308_ADC_CH1_ALC_GAIN_MAX, + 0, rk3308_codec_adc_alc_gain_tlv), +- SOC_SINGLE_RANGE_TLV("ADC ALC Channel 1 Right Volume", ++ SOC_SINGLE_RANGE_TLV("ADC ALC Group 1 Right Volume", + RK3308_ADC_ANA_CON04(1), + RK3308_ADC_CH2_ALC_GAIN_SFT, +- RK3308_ADC_CH2_ALC_GAIN_NDB_18, +- RK3308_ADC_CH2_ALC_GAIN_PDB_28_5, ++ RK3308_ADC_CH2_ALC_GAIN_MIN, ++ RK3308_ADC_CH2_ALC_GAIN_MAX, + 0, rk3308_codec_adc_alc_gain_tlv), +- SOC_SINGLE_RANGE_TLV("ADC ALC Channel 2 Left Volume", ++ SOC_SINGLE_RANGE_TLV("ADC ALC Group 2 Left Volume", + RK3308_ADC_ANA_CON03(2), + RK3308_ADC_CH1_ALC_GAIN_SFT, +- RK3308_ADC_CH1_ALC_GAIN_NDB_18, +- RK3308_ADC_CH1_ALC_GAIN_PDB_28_5, ++ RK3308_ADC_CH1_ALC_GAIN_MIN, ++ RK3308_ADC_CH1_ALC_GAIN_MAX, + 0, rk3308_codec_adc_alc_gain_tlv), +- SOC_SINGLE_RANGE_TLV("ADC ALC Channel 2 Right Volume", ++ SOC_SINGLE_RANGE_TLV("ADC ALC Group 2 Right Volume", + RK3308_ADC_ANA_CON04(2), + RK3308_ADC_CH2_ALC_GAIN_SFT, +- RK3308_ADC_CH2_ALC_GAIN_NDB_18, +- RK3308_ADC_CH2_ALC_GAIN_PDB_28_5, ++ RK3308_ADC_CH2_ALC_GAIN_MIN, ++ RK3308_ADC_CH2_ALC_GAIN_MAX, + 0, rk3308_codec_adc_alc_gain_tlv), +- SOC_SINGLE_RANGE_TLV("ADC ALC Channel 3 Left Volume", ++ SOC_SINGLE_RANGE_TLV("ADC ALC Group 3 Left Volume", + RK3308_ADC_ANA_CON03(3), + RK3308_ADC_CH1_ALC_GAIN_SFT, +- RK3308_ADC_CH1_ALC_GAIN_NDB_18, +- RK3308_ADC_CH1_ALC_GAIN_PDB_28_5, ++ RK3308_ADC_CH1_ALC_GAIN_MIN, ++ RK3308_ADC_CH1_ALC_GAIN_MAX, + 0, rk3308_codec_adc_alc_gain_tlv), +- SOC_SINGLE_RANGE_TLV("ADC ALC Channel 3 Right Volume", ++ SOC_SINGLE_RANGE_TLV("ADC ALC Group 3 Right Volume", + RK3308_ADC_ANA_CON04(3), + RK3308_ADC_CH2_ALC_GAIN_SFT, +- RK3308_ADC_CH2_ALC_GAIN_NDB_18, +- RK3308_ADC_CH2_ALC_GAIN_PDB_28_5, ++ RK3308_ADC_CH2_ALC_GAIN_MIN, ++ RK3308_ADC_CH2_ALC_GAIN_MAX, + 0, rk3308_codec_adc_alc_gain_tlv), + +- /* DAC */ +- SOC_SINGLE_RANGE_TLV("DAC Left Volume", +- RK3308_DAC_ANA_CON04, +- RK3308_DAC_L_GAIN_SFT, +- RK3308_DAC_L_GAIN_0DB, +- RK3308_DAC_L_GAIN_PDB_6, +- 0, rk3308_codec_dac_gain_tlv), +- SOC_SINGLE_RANGE_TLV("DAC Right Volume", +- RK3308_DAC_ANA_CON04, +- RK3308_DAC_R_GAIN_SFT, +- RK3308_DAC_R_GAIN_0DB, +- RK3308_DAC_R_GAIN_PDB_6, +- 0, rk3308_codec_dac_gain_tlv), ++ /* ADC High Pass Filter */ ++ SOC_ENUM_EXT("ADC Group 0 HPF Cut-off", rk3308_hpf_enum_array[0], ++ rk3308_codec_hpf_get, rk3308_codec_hpf_put), ++ SOC_ENUM_EXT("ADC Group 1 HPF Cut-off", rk3308_hpf_enum_array[1], ++ rk3308_codec_hpf_get, rk3308_codec_hpf_put), ++ SOC_ENUM_EXT("ADC Group 2 HPF Cut-off", rk3308_hpf_enum_array[2], ++ rk3308_codec_hpf_get, rk3308_codec_hpf_put), ++ SOC_ENUM_EXT("ADC Group 3 HPF Cut-off", rk3308_hpf_enum_array[3], ++ rk3308_codec_hpf_get, rk3308_codec_hpf_put), ++ ++ /* DAC LINEOUT */ ++ SOC_SINGLE_TLV("DAC LINEOUT Left Volume", ++ RK3308_DAC_ANA_CON04, ++ RK3308_DAC_L_LINEOUT_GAIN_SFT, ++ RK3308_DAC_L_LINEOUT_GAIN_MAX, ++ 0, rk3308_codec_dac_lineout_gain_tlv), ++ SOC_SINGLE_TLV("DAC LINEOUT Right Volume", ++ RK3308_DAC_ANA_CON04, ++ RK3308_DAC_R_LINEOUT_GAIN_SFT, ++ RK3308_DAC_R_LINEOUT_GAIN_MAX, ++ 0, rk3308_codec_dac_lineout_gain_tlv), + + /* DAC HPOUT */ +- SOC_SINGLE_RANGE_TLV("DAC HPOUT Left Volume", +- RK3308_DAC_ANA_CON05, +- RK3308_DAC_L_HPOUT_GAIN_SFT, +- RK3308_DAC_L_HPOUT_GAIN_NDB_39, +- RK3308_DAC_L_HPOUT_GAIN_PDB_6, +- 0, rk3308_codec_dac_hpout_gain_tlv), +- SOC_SINGLE_RANGE_TLV("DAC HPOUT Right Volume", +- RK3308_DAC_ANA_CON06, +- RK3308_DAC_R_HPOUT_GAIN_SFT, +- RK3308_DAC_R_HPOUT_GAIN_NDB_39, +- RK3308_DAC_R_HPOUT_GAIN_PDB_6, +- 0, rk3308_codec_dac_hpout_gain_tlv), ++ SOC_SINGLE_EXT_TLV("DAC HPOUT Left Volume", ++ RK3308_DAC_ANA_CON05, ++ RK3308_DAC_L_HPOUT_GAIN_SFT, ++ RK3308_DAC_L_HPOUT_GAIN_MAX, ++ 0, ++ rk3308_codec_hpout_l_get_tlv, ++ rk3308_codec_hpout_l_put_tlv, ++ rk3308_codec_dac_hpout_gain_tlv), ++ SOC_SINGLE_EXT_TLV("DAC HPOUT Right Volume", ++ RK3308_DAC_ANA_CON06, ++ RK3308_DAC_R_HPOUT_GAIN_SFT, ++ RK3308_DAC_R_HPOUT_GAIN_MAX, ++ 0, ++ rk3308_codec_hpout_r_get_tlv, ++ rk3308_codec_hpout_r_put_tlv, ++ rk3308_codec_dac_hpout_gain_tlv), + + /* DAC HPMIX */ + SOC_SINGLE_RANGE_TLV("DAC HPMIX Left Volume", +- RK3308_DAC_ANA_CON05, ++ RK3308_DAC_ANA_CON12, + RK3308_DAC_L_HPMIX_GAIN_SFT, +- RK3308_DAC_L_HPMIX_GAIN_NDB_6, +- RK3308_DAC_L_HPMIX_GAIN_0DB, ++ RK3308_DAC_L_HPMIX_GAIN_MIN, ++ RK3308_DAC_L_HPMIX_GAIN_MAX, + 0, rk3308_codec_dac_hpmix_gain_tlv), + SOC_SINGLE_RANGE_TLV("DAC HPMIX Right Volume", +- RK3308_DAC_ANA_CON05, ++ RK3308_DAC_ANA_CON12, + RK3308_DAC_R_HPMIX_GAIN_SFT, +- RK3308_DAC_R_HPMIX_GAIN_NDB_6, +- RK3308_DAC_R_HPMIX_GAIN_0DB, ++ RK3308_DAC_R_HPMIX_GAIN_MIN, ++ RK3308_DAC_R_HPMIX_GAIN_MAX, + 0, rk3308_codec_dac_hpmix_gain_tlv), + }; + +-static void rk3308_speaker_ctl(struct rk3308_codec_priv *rk3308, int on) ++static int rk3308_codec_agc_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) + { +- gpiod_direction_output(rk3308->spk_ctl_gpio, on); ++ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; ++ ++ if (e->reg < 0 || e->reg > ADC_LR_GROUP_MAX - 1) { ++ dev_err(rk3308->plat_dev, ++ "%s: Invalid ADC grp: %d\n", __func__, e->reg); ++ return -EINVAL; ++ } ++ ++ if (e->shift_l) ++ ucontrol->value.integer.value[0] = rk3308->agc_r[e->reg]; ++ else ++ ucontrol->value.integer.value[0] = rk3308->agc_l[e->reg]; ++ ++ return 0; + } + +-static int rk3308_codec_reset(struct snd_soc_codec *codec) ++static int rk3308_codec_agc_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) + { ++ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; ++ unsigned int value = ucontrol->value.integer.value[0]; ++ int grp = e->reg; + +- reset_control_assert(rk3308->reset); +- usleep_range(200, 300); /* estimated value */ +- reset_control_deassert(rk3308->reset); ++ if (e->reg < 0 || e->reg > ADC_LR_GROUP_MAX - 1) { ++ dev_err(rk3308->plat_dev, ++ "%s: Invalid ADC grp: %d\n", __func__, e->reg); ++ return -EINVAL; ++ } + +- regmap_write(rk3308->regmap, RK3308_GLB_CON, 0x00); +- usleep_range(200, 300); /* estimated value */ +- regmap_write(rk3308->regmap, RK3308_GLB_CON, +- RK3308_SYS_WORK | +- RK3308_DAC_DIG_WORK | +- RK3308_ADC_DIG_WORK); ++ if (value) { ++ /* ALC AGC On */ ++ if (e->shift_l) { ++ /* ALC AGC Right On */ ++ regmap_update_bits(rk3308->regmap, RK3308_ALC_R_DIG_CON09(grp), ++ RK3308_AGC_FUNC_SEL_MSK, ++ RK3308_AGC_FUNC_SEL_EN); ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON11(grp), ++ RK3308_ADC_ALCR_CON_GAIN_PGAR_MSK, ++ RK3308_ADC_ALCR_CON_GAIN_PGAR_EN); ++ ++ rk3308->agc_r[e->reg] = 1; ++ } else { ++ /* ALC AGC Left On */ ++ regmap_update_bits(rk3308->regmap, RK3308_ALC_L_DIG_CON09(grp), ++ RK3308_AGC_FUNC_SEL_MSK, ++ RK3308_AGC_FUNC_SEL_EN); ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON11(grp), ++ RK3308_ADC_ALCL_CON_GAIN_PGAL_MSK, ++ RK3308_ADC_ALCL_CON_GAIN_PGAL_EN); ++ ++ rk3308->agc_l[e->reg] = 1; ++ } ++ } else { ++ /* ALC AGC Off */ ++ if (e->shift_l) { ++ /* ALC AGC Right Off */ ++ regmap_update_bits(rk3308->regmap, RK3308_ALC_R_DIG_CON09(grp), ++ RK3308_AGC_FUNC_SEL_MSK, ++ RK3308_AGC_FUNC_SEL_DIS); ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON11(grp), ++ RK3308_ADC_ALCR_CON_GAIN_PGAR_MSK, ++ RK3308_ADC_ALCR_CON_GAIN_PGAR_DIS); ++ ++ rk3308->agc_r[e->reg] = 0; ++ } else { ++ /* ALC AGC Left Off */ ++ regmap_update_bits(rk3308->regmap, RK3308_ALC_L_DIG_CON09(grp), ++ RK3308_AGC_FUNC_SEL_MSK, ++ RK3308_AGC_FUNC_SEL_DIS); ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON11(grp), ++ RK3308_ADC_ALCL_CON_GAIN_PGAL_MSK, ++ RK3308_ADC_ALCL_CON_GAIN_PGAL_DIS); ++ ++ rk3308->agc_l[e->reg] = 0; ++ } ++ } + + return 0; + } + +-static int rk3308_set_bias_level(struct snd_soc_codec *codec, +- enum snd_soc_bias_level level) ++static int rk3308_codec_agc_asr_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) + { +- switch (level) { +- case SND_SOC_BIAS_ON: +- break; +- +- case SND_SOC_BIAS_PREPARE: +- break; ++ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; ++ unsigned int value; ++ int grp = e->reg; + +- case SND_SOC_BIAS_STANDBY: +- case SND_SOC_BIAS_OFF: +- break; ++ if (e->reg < 0 || e->reg > ADC_LR_GROUP_MAX - 1) { ++ dev_err(rk3308->plat_dev, ++ "%s: Invalid ADC grp: %d\n", __func__, e->reg); ++ return -EINVAL; + } + +- snd_soc_codec_force_bias_level(codec, level); ++ if (e->shift_l) { ++ regmap_read(rk3308->regmap, RK3308_ALC_R_DIG_CON04(grp), &value); ++ rk3308->agc_asr_r[e->reg] = value >> RK3308_AGC_APPROX_RATE_SFT; ++ ucontrol->value.integer.value[0] = rk3308->agc_asr_r[e->reg]; ++ } else { ++ regmap_read(rk3308->regmap, RK3308_ALC_L_DIG_CON04(grp), &value); ++ rk3308->agc_asr_l[e->reg] = value >> RK3308_AGC_APPROX_RATE_SFT; ++ ucontrol->value.integer.value[0] = rk3308->agc_asr_l[e->reg]; ++ } + + return 0; + } + +-static int rk3308_set_dai_fmt(struct snd_soc_dai *codec_dai, +- unsigned int fmt) ++static int rk3308_codec_agc_asr_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) + { +- struct snd_soc_codec *codec = codec_dai->codec; ++ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); +- unsigned int adc_aif1 = 0, adc_aif2 = 0, dac_aif1 = 0, dac_aif2 = 0; +- int ch = rk3308->adc_ch; ++ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; ++ unsigned int value; ++ int grp = e->reg; + +- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { +- case SND_SOC_DAIFMT_CBS_CFS: +- adc_aif2 |= RK3308_ADC_IO_MODE_SLAVE; +- adc_aif2 |= RK3308_ADC_MODE_SLAVE; +- dac_aif2 |= RK3308_DAC_IO_MODE_SLAVE; +- dac_aif2 |= RK3308_DAC_MODE_SLAVE; +- break; +- case SND_SOC_DAIFMT_CBM_CFM: +- adc_aif2 |= RK3308_ADC_IO_MODE_MASTER; +- adc_aif2 |= RK3308_ADC_MODE_MASTER; +- dac_aif2 |= RK3308_DAC_IO_MODE_MASTER; +- dac_aif2 |= RK3308_DAC_MODE_MASTER; +- break; +- default: ++ if (e->reg < 0 || e->reg > ADC_LR_GROUP_MAX - 1) { ++ dev_err(rk3308->plat_dev, ++ "%s: Invalid ADC grp: %d\n", __func__, e->reg); + return -EINVAL; + } + +- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { +- case SND_SOC_DAIFMT_DSP_A: +- adc_aif1 |= RK3308_ADC_I2S_MODE_PCM; +- dac_aif1 |= RK3308_DAC_I2S_MODE_PCM; +- break; +- case SND_SOC_DAIFMT_I2S: +- adc_aif1 |= RK3308_ADC_I2S_MODE_I2S; +- dac_aif1 |= RK3308_DAC_I2S_MODE_I2S; +- break; +- case SND_SOC_DAIFMT_RIGHT_J: +- adc_aif1 |= RK3308_ADC_I2S_MODE_RJ; +- dac_aif1 |= RK3308_DAC_I2S_MODE_RJ; +- break; +- case SND_SOC_DAIFMT_LEFT_J: +- adc_aif1 |= RK3308_ADC_I2S_MODE_RJ; +- dac_aif1 |= RK3308_DAC_I2S_MODE_LJ; +- break; +- default: +- return -EINVAL; ++ value = ucontrol->value.integer.value[0] << RK3308_AGC_APPROX_RATE_SFT; ++ ++ if (e->shift_l) { ++ /* ALC AGC Right Approximate Sample Rate */ ++ regmap_update_bits(rk3308->regmap, RK3308_ALC_R_DIG_CON04(grp), ++ RK3308_AGC_APPROX_RATE_MSK, ++ value); ++ rk3308->agc_asr_r[e->reg] = ucontrol->value.integer.value[0]; ++ } else { ++ /* ALC AGC Left Approximate Sample Rate */ ++ regmap_update_bits(rk3308->regmap, RK3308_ALC_L_DIG_CON04(grp), ++ RK3308_AGC_APPROX_RATE_MSK, ++ value); ++ rk3308->agc_asr_l[e->reg] = ucontrol->value.integer.value[0]; + } + +- switch (fmt & SND_SOC_DAIFMT_INV_MASK) { +- case SND_SOC_DAIFMT_NB_NF: +- adc_aif1 |= RK3308_ADC_I2S_LRC_POL_NORMAL; +- adc_aif2 |= RK3308_ADC_I2S_BIT_CLK_POL_NORMAL; +- dac_aif1 |= RK3308_DAC_I2S_LRC_POL_NORMAL; +- dac_aif2 |= RK3308_DAC_I2S_BIT_CLK_POL_NORMAL; +- break; +- case SND_SOC_DAIFMT_IB_IF: +- adc_aif1 |= RK3308_ADC_I2S_LRC_POL_REVERSAL; +- adc_aif2 |= RK3308_ADC_I2S_BIT_CLK_POL_REVERSAL; +- dac_aif1 |= RK3308_DAC_I2S_LRC_POL_REVERSAL; +- dac_aif2 |= RK3308_DAC_I2S_BIT_CLK_POL_REVERSAL; +- break; +- case SND_SOC_DAIFMT_IB_NF: +- adc_aif1 |= RK3308_ADC_I2S_LRC_POL_NORMAL; +- adc_aif2 |= RK3308_ADC_I2S_BIT_CLK_POL_REVERSAL; +- dac_aif1 |= RK3308_DAC_I2S_LRC_POL_NORMAL; +- dac_aif2 |= RK3308_DAC_I2S_BIT_CLK_POL_REVERSAL; +- break; +- case SND_SOC_DAIFMT_NB_IF: +- adc_aif1 |= RK3308_ADC_I2S_LRC_POL_REVERSAL; +- adc_aif2 |= RK3308_ADC_I2S_BIT_CLK_POL_NORMAL; +- dac_aif1 |= RK3308_DAC_I2S_LRC_POL_REVERSAL; +- dac_aif2 |= RK3308_DAC_I2S_BIT_CLK_POL_NORMAL; +- break; +- default: ++ return 0; ++} ++ ++static int rk3308_codec_mic_mute_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; ++ unsigned int value; ++ int grp = e->reg; ++ ++ if (e->reg < 0 || e->reg > ADC_LR_GROUP_MAX - 1) { ++ dev_err(rk3308->plat_dev, ++ "%s: Invalid ADC grp: %d\n", __func__, e->reg); + return -EINVAL; + } + +- regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON01(ch), +- RK3308_ADC_I2S_LRC_POL_MSK | +- RK3308_ADC_I2S_MODE_MSK, +- adc_aif1); +- regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON02(ch), +- RK3308_ADC_IO_MODE_MSK | +- RK3308_ADC_MODE_MSK | +- RK3308_ADC_I2S_BIT_CLK_POL_MSK, +- adc_aif2); +- +- regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON01, +- RK3308_DAC_I2S_LRC_POL_MSK | +- RK3308_DAC_I2S_MODE_MSK, +- dac_aif1); +- regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON02, +- RK3308_DAC_IO_MODE_MSK | +- RK3308_DAC_MODE_MSK | +- RK3308_DAC_I2S_BIT_CLK_POL_MSK, +- dac_aif2); ++ if (e->shift_l) { ++ /* ADC MIC Right Mute/Work Infos */ ++ regmap_read(rk3308->regmap, RK3308_ADC_DIG_CON03(grp), &value); ++ rk3308->mic_mute_r[e->reg] = (value & RK3308_ADC_R_CH_BIST_SINE) >> ++ RK3308_ADC_R_CH_BIST_SFT; ++ ucontrol->value.integer.value[0] = rk3308->mic_mute_r[e->reg]; ++ } else { ++ /* ADC MIC Left Mute/Work Infos */ ++ regmap_read(rk3308->regmap, RK3308_ADC_DIG_CON03(grp), &value); ++ rk3308->mic_mute_l[e->reg] = (value & RK3308_ADC_L_CH_BIST_SINE) >> ++ RK3308_ADC_L_CH_BIST_SFT; ++ ucontrol->value.integer.value[0] = rk3308->mic_mute_l[e->reg]; ++ } + + return 0; + } + +-static int rk3308_hw_params(struct snd_pcm_substream *substream, +- struct snd_pcm_hw_params *params, +- struct snd_soc_dai *dai) ++static int rk3308_codec_mic_mute_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) + { +- struct snd_soc_codec *codec = dai->codec; ++ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); +- unsigned int adc_aif1 = 0, adc_aif2 = 0, dac_aif1 = 0, dac_aif2 = 0; +- int ch = rk3308->adc_ch; ++ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; ++ unsigned int value; ++ int grp = e->reg; + +- switch (params_format(params)) { +- case SNDRV_PCM_FORMAT_S16_LE: +- adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_16BITS; +- dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_16BITS; +- break; +- case SNDRV_PCM_FORMAT_S20_3LE: +- adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_20BITS; +- dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_20BITS; +- break; +- case SNDRV_PCM_FORMAT_S24_LE: +- adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_24BITS; +- dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_24BITS; +- break; +- case SNDRV_PCM_FORMAT_S32_LE: +- adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_32BITS; +- dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_32BITS; +- break; +- default: ++ if (e->reg < 0 || e->reg > ADC_LR_GROUP_MAX - 1) { ++ dev_err(rk3308->plat_dev, ++ "%s: Invalid ADC grp: %d\n", __func__, e->reg); + return -EINVAL; + } + +- switch (params_channels(params)) { +- case 1: +- adc_aif1 |= RK3308_ADC_I2S_MONO; +- break; +- case 2: +- adc_aif1 |= RK3308_ADC_I2S_STEREO; +- break; +- default: +- return -EINVAL; ++ if (e->shift_l) { ++ /* ADC MIC Right Mute/Work Configuration */ ++ value = ucontrol->value.integer.value[0] << RK3308_ADC_R_CH_BIST_SFT; ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON03(grp), ++ RK3308_ADC_R_CH_BIST_SINE, ++ value); ++ rk3308->mic_mute_r[e->reg] = ucontrol->value.integer.value[0]; ++ } else { ++ /* ADC MIC Left Mute/Work Configuration */ ++ value = ucontrol->value.integer.value[0] << RK3308_ADC_L_CH_BIST_SFT; ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON03(grp), ++ RK3308_ADC_L_CH_BIST_SINE, ++ value); ++ rk3308->mic_mute_l[e->reg] = ucontrol->value.integer.value[0]; + } + +- adc_aif1 |= RK3308_ADC_I2S_LR_NORMAL; +- adc_aif2 |= RK3308_ADC_I2S_WORK; +- dac_aif1 |= RK3308_DAC_I2S_LR_NORMAL; +- dac_aif2 |= RK3308_DAC_I2S_WORK; ++ return 0; ++} + +- regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON01(ch), +- RK3308_ADC_I2S_VALID_LEN_MSK | +- RK3308_ADC_I2S_LR_MSK | +- RK3308_ADC_I2S_TYPE_MSK, +- adc_aif1); +- regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON02(ch), +- RK3308_ADC_I2S_MSK, +- adc_aif2); ++static int rk3308_codec_micbias_volts_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + +- regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON01, +- RK3308_DAC_I2S_VALID_LEN_MSK | +- RK3308_DAC_I2S_LR_MSK, +- dac_aif1); +- regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON02, +- RK3308_DAC_I2S_MSK, +- dac_aif2); ++ ucontrol->value.integer.value[0] = rk3308->micbias_volt; + + return 0; + } + +-static int rk3308_digital_mute(struct snd_soc_dai *dai, int mute) ++static int rk3308_codec_micbias_volts_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) + { ++ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ unsigned int volt = ucontrol->value.integer.value[0]; ++ int ret; ++ ++ ret = check_micbias(volt); ++ if (ret < 0) { ++ dev_err(rk3308->plat_dev, "The invalid micbias volt: %d\n", ++ volt); ++ return ret; ++ } ++ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(0), ++ RK3308_ADC_LEVEL_RANGE_MICBIAS_MSK, ++ volt); ++ ++ rk3308->micbias_volt = volt; ++ + return 0; + } + +-static int rk3308_codec_dac_enable(struct rk3308_codec_priv *rk3308) ++static int rk3308_codec_main_micbias_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) + { +- /* Step 01 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON00, +- RK3308_DAC_CURRENT_MSK, +- RK3308_DAC_CURRENT_EN); +- +- /* Step 02 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, +- RK3308_DAC_BUF_REF_L_MSK | +- RK3308_DAC_BUF_REF_R_MSK, +- RK3308_DAC_BUF_REF_L_EN | +- RK3308_DAC_BUF_REF_R_EN); ++ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + +- /* Step 03 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, +- RK3308_DAC_POP_SOUND_L_MSK | +- RK3308_DAC_POP_SOUND_R_MSK, +- RK3308_DAC_POP_SOUND_L_WORK | +- RK3308_DAC_POP_SOUND_R_WORK); ++ ucontrol->value.integer.value[0] = rk3308->enable_micbias; + +- /* Step 04 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, +- RK3308_DAC_L_HPMIX_EN | RK3308_DAC_R_HPMIX_EN, +- RK3308_DAC_L_HPMIX_EN | RK3308_DAC_R_HPMIX_EN); ++ return 0; ++} + +- /* Step 05 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, +- RK3308_DAC_L_HPMIX_WORK | RK3308_DAC_R_HPMIX_WORK, +- RK3308_DAC_L_HPMIX_WORK | RK3308_DAC_R_HPMIX_WORK); ++static int rk3308_codec_main_micbias_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ unsigned int on = ucontrol->value.integer.value[0]; ++ ++ if (on) { ++ if (!rk3308->enable_micbias) ++ rk3308_codec_micbias_enable(rk3308, rk3308->micbias_volt); ++ } else { ++ if (rk3308->enable_micbias) ++ rk3308_codec_micbias_disable(rk3308); ++ } + +- /* Step 06 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, +- RK3308_DAC_L_LINEOUT_EN | RK3308_DAC_R_LINEOUT_EN, +- RK3308_DAC_L_LINEOUT_EN | RK3308_DAC_R_LINEOUT_EN); ++ return 0; ++} + +- /* Step 07 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, +- RK3308_DAC_L_HPOUT_EN | RK3308_DAC_R_HPOUT_EN, +- RK3308_DAC_L_HPOUT_EN | RK3308_DAC_R_HPOUT_EN); ++static int rk3308_codec_mic_gain_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ return snd_soc_get_volsw_range(kcontrol, ucontrol); ++} + +- /* Step 08 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, +- RK3308_DAC_L_HPOUT_WORK | RK3308_DAC_R_HPOUT_WORK, +- RK3308_DAC_L_HPOUT_WORK | RK3308_DAC_R_HPOUT_WORK); ++static int rk3308_codec_mic_gain_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ unsigned int gain = ucontrol->value.integer.value[0]; + +- /* Step 09 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, +- RK3308_DAC_L_REF_EN | RK3308_DAC_R_REF_EN, +- RK3308_DAC_L_REF_EN | RK3308_DAC_R_REF_EN); ++ if (gain > RK3308_ADC_CH1_MIC_GAIN_MAX) { ++ dev_err(rk3308->plat_dev, "%s: invalid mic gain: %d\n", ++ __func__, gain); ++ return -EINVAL; ++ } + +- /* Step 10 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, +- RK3308_DAC_L_CLK_EN | RK3308_DAC_R_CLK_EN, +- RK3308_DAC_L_CLK_EN | RK3308_DAC_R_CLK_EN); ++ if (rk3308->codec_ver == ACODEC_VERSION_A) { ++ /* ++ * From the TRM, there are only suupport 0dB(gain==0) and ++ * 20dB(gain==3) on the codec version A. ++ */ ++ if (!(gain == 0 || gain == RK3308_ADC_CH1_MIC_GAIN_MAX)) { ++ dev_err(rk3308->plat_dev, ++ "version A doesn't supported: %d, expect: 0,%d\n", ++ gain, RK3308_ADC_CH1_MIC_GAIN_MAX); ++ return 0; ++ } ++ } + +- /* Step 11 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, +- RK3308_DAC_L_DAC_EN | RK3308_DAC_R_DAC_EN, +- RK3308_DAC_L_DAC_EN | RK3308_DAC_R_DAC_EN); ++ return snd_soc_put_volsw_range(kcontrol, ucontrol); ++} + +- /* Step 12 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, +- RK3308_DAC_L_DAC_WORK | RK3308_DAC_R_DAC_WORK, +- RK3308_DAC_L_DAC_WORK | RK3308_DAC_R_DAC_WORK); ++static int rk3308_codec_hpf_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; ++ unsigned int value; + +- /* Step 13 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON12, +- RK3308_DAC_L_HPMIX_SEL_MSK | +- RK3308_DAC_R_HPMIX_SEL_MSK, +- RK3308_DAC_L_HPMIX_I2S | +- RK3308_DAC_R_HPMIX_I2S); ++ if (e->reg < 0 || e->reg > ADC_LR_GROUP_MAX - 1) { ++ dev_err(rk3308->plat_dev, ++ "%s: Invalid ADC grp: %d\n", __func__, e->reg); ++ return -EINVAL; ++ } + +- /* Step 14 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, +- RK3308_DAC_L_HPMIX_UNMUTE | +- RK3308_DAC_R_HPMIX_UNMUTE, +- RK3308_DAC_L_HPMIX_UNMUTE | +- RK3308_DAC_R_HPMIX_UNMUTE); ++ regmap_read(rk3308->regmap, RK3308_ADC_DIG_CON04(e->reg), &value); ++ if (value & RK3308_ADC_HPF_PATH_MSK) ++ rk3308->hpf_cutoff[e->reg] = 0; ++ else ++ rk3308->hpf_cutoff[e->reg] = 1; + +- /* Step 15 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON12, +- RK3308_DAC_L_HPMIX_GAIN_MSK | +- RK3308_DAC_R_HPMIX_GAIN_MSK, +- RK3308_DAC_L_HPMIX_GAIN_0DB | +- RK3308_DAC_R_HPMIX_GAIN_0DB); ++ ucontrol->value.integer.value[0] = rk3308->hpf_cutoff[e->reg]; + +- /* Step 16 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, +- RK3308_DAC_L_HPOUT_UNMUTE | +- RK3308_DAC_R_HPOUT_UNMUTE, +- RK3308_DAC_L_HPOUT_UNMUTE | +- RK3308_DAC_R_HPOUT_UNMUTE); ++ return 0; ++} + +- /* Step 17 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, +- RK3308_DAC_L_LINEOUT_UNMUTE | +- RK3308_DAC_R_LINEOUT_UNMUTE, +- RK3308_DAC_L_LINEOUT_UNMUTE | +- RK3308_DAC_R_LINEOUT_UNMUTE); ++static int rk3308_codec_hpf_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; ++ unsigned int value = ucontrol->value.integer.value[0]; + +- /* Step 18 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON05, +- RK3308_DAC_L_HPOUT_GAIN_MSK, +- RK3308_DAC_L_HPOUT_GAIN_0DB); ++ if (e->reg < 0 || e->reg > ADC_LR_GROUP_MAX - 1) { ++ dev_err(rk3308->plat_dev, ++ "%s: Invalid ADC grp: %d\n", __func__, e->reg); ++ return -EINVAL; ++ } + +- /* Step 18 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON06, +- RK3308_DAC_R_HPOUT_GAIN_MSK, +- RK3308_DAC_R_HPOUT_GAIN_0DB); ++ if (value) { ++ /* Enable high pass filter for ADCs */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON04(e->reg), ++ RK3308_ADC_HPF_PATH_MSK, ++ RK3308_ADC_HPF_PATH_EN); ++ } else { ++ /* Disable high pass filter for ADCs. */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON04(e->reg), ++ RK3308_ADC_HPF_PATH_MSK, ++ RK3308_ADC_HPF_PATH_DIS); ++ } + +- /* Step 19 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, +- RK3308_DAC_L_GAIN_MSK | RK3308_DAC_R_GAIN_MSK, +- RK3308_DAC_L_GAIN_0DB | RK3308_DAC_R_GAIN_0DB); ++ rk3308->hpf_cutoff[e->reg] = value; + + return 0; + } + +-static int rk3308_codec_dac_disable(struct rk3308_codec_priv *rk3308) ++static int rk3308_codec_hpout_l_get_tlv(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) + { +- /* Step 01 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, +- RK3308_DAC_L_GAIN_MSK | RK3308_DAC_R_GAIN_MSK, +- RK3308_DAC_L_GAIN_0DB | RK3308_DAC_R_GAIN_0DB); +- +- /* +- * Step 02 +- * +- * Note1. In the step2, adjusting the register step by step to the +- * appropriate value and taking 20ms as time step +- */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON05, +- RK3308_DAC_L_HPOUT_GAIN_MSK, +- RK3308_DAC_L_HPOUT_GAIN_NDB_39); +- +- /* Step 02 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON06, +- RK3308_DAC_R_HPOUT_GAIN_MSK, +- RK3308_DAC_R_HPOUT_GAIN_NDB_39); ++ return snd_soc_get_volsw_range(kcontrol, ucontrol); ++} + +- /* Step 03 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, +- RK3308_DAC_L_HPMIX_UNMUTE | +- RK3308_DAC_R_HPMIX_UNMUTE, +- RK3308_DAC_L_HPMIX_MUTE | +- RK3308_DAC_R_HPMIX_MUTE); ++static int rk3308_codec_hpout_l_put_tlv(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ unsigned int dgain = ucontrol->value.integer.value[0]; + +- /* Step 04 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON12, +- RK3308_DAC_L_HPMIX_SEL_MSK | +- RK3308_DAC_R_HPMIX_SEL_MSK, +- RK3308_DAC_L_HPMIX_NONE | +- RK3308_DAC_R_HPMIX_NONE); ++ if (dgain > RK3308_DAC_L_HPOUT_GAIN_MAX) { ++ dev_err(rk3308->plat_dev, "%s: invalid l_dgain: %d\n", ++ __func__, dgain); ++ return -EINVAL; ++ } + +- /* Step 05 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, +- RK3308_DAC_L_HPOUT_UNMUTE | +- RK3308_DAC_R_HPOUT_UNMUTE, +- RK3308_DAC_L_HPOUT_MUTE | +- RK3308_DAC_R_HPOUT_MUTE); ++ rk3308->hpout_l_dgain = dgain; + +- /* Step 06 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, +- RK3308_DAC_L_DAC_WORK | RK3308_DAC_R_DAC_WORK, +- RK3308_DAC_L_DAC_INIT | RK3308_DAC_R_DAC_INIT); ++ return snd_soc_put_volsw_range(kcontrol, ucontrol); ++} + +- /* Step 07 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, +- RK3308_DAC_L_HPOUT_EN | RK3308_DAC_R_HPOUT_EN, +- RK3308_DAC_L_HPOUT_DIS | RK3308_DAC_R_HPOUT_DIS); ++static int rk3308_codec_hpout_r_get_tlv(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ return snd_soc_get_volsw_range(kcontrol, ucontrol); ++} + +- /* Step 08 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, +- RK3308_DAC_L_LINEOUT_UNMUTE | +- RK3308_DAC_R_LINEOUT_UNMUTE, +- RK3308_DAC_L_LINEOUT_MUTE | +- RK3308_DAC_R_LINEOUT_MUTE); ++static int rk3308_codec_hpout_r_put_tlv(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ unsigned int dgain = ucontrol->value.integer.value[0]; + +- /* Step 09 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, +- RK3308_DAC_L_LINEOUT_EN | RK3308_DAC_R_LINEOUT_EN, +- RK3308_DAC_L_LINEOUT_DIS | RK3308_DAC_R_LINEOUT_DIS); ++ if (dgain > RK3308_DAC_R_HPOUT_GAIN_MAX) { ++ dev_err(rk3308->plat_dev, "%s: invalid r_dgain: %d\n", ++ __func__, dgain); ++ return -EINVAL; ++ } + +- /* Step 10 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, +- RK3308_DAC_L_HPMIX_EN | RK3308_DAC_R_HPMIX_EN, +- RK3308_DAC_L_HPMIX_DIS | RK3308_DAC_R_HPMIX_DIS); ++ rk3308->hpout_r_dgain = dgain; + +- /* Step 11 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, +- RK3308_DAC_L_DAC_EN | RK3308_DAC_R_DAC_EN, +- RK3308_DAC_L_DAC_DIS | RK3308_DAC_R_DAC_DIS); ++ return snd_soc_put_volsw_range(kcontrol, ucontrol); ++} + +- /* Step 12 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, +- RK3308_DAC_L_CLK_EN | RK3308_DAC_R_CLK_EN, +- RK3308_DAC_L_CLK_DIS | RK3308_DAC_R_CLK_DIS); ++static u32 to_mapped_grp(struct rk3308_codec_priv *rk3308, int idx) ++{ ++ return rk3308->i2s_sdis[idx]; ++} + +- /* Step 13 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, +- RK3308_DAC_L_REF_EN | RK3308_DAC_R_REF_EN, +- RK3308_DAC_L_REF_DIS | RK3308_DAC_R_REF_DIS); ++static bool adc_for_each_grp(struct rk3308_codec_priv *rk3308, ++ int type, int idx, u32 *grp) ++{ ++ if (type == ADC_TYPE_NORMAL) { ++ u32 mapped_grp = to_mapped_grp(rk3308, idx); ++ int max_grps; ++ ++ if (rk3308->enable_all_adcs) ++ max_grps = ADC_LR_GROUP_MAX; ++ else ++ max_grps = rk3308->used_adc_grps; ++ ++ if (idx >= max_grps) ++ return false; ++ ++ if ((!rk3308->loopback_dacs_enabled) && ++ handle_loopback(rk3308) && ++ rk3308->loopback_grp == mapped_grp) { ++ /* ++ * Ths loopback DACs are closed, and specify the ++ * loopback ADCs. ++ */ ++ *grp = ADC_GRP_SKIP_MAGIC; ++ } else if (rk3308->en_always_grps_num && ++ rk3308->skip_grps[mapped_grp]) { ++ /* To set the skip flag if the ADC GRP is enabled. */ ++ *grp = ADC_GRP_SKIP_MAGIC; ++ } else { ++ *grp = mapped_grp; ++ } + +- /* Step 14 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, +- RK3308_DAC_POP_SOUND_L_MSK | +- RK3308_DAC_POP_SOUND_R_MSK, +- RK3308_DAC_POP_SOUND_L_INIT | +- RK3308_DAC_POP_SOUND_R_INIT); ++ dev_dbg(rk3308->plat_dev, ++ "ADC_TYPE_NORMAL, idx: %d, mapped_grp: %d, get grp: %d,\n", ++ idx, mapped_grp, *grp); ++ } else if (type == ADC_TYPE_ALL) { ++ if (idx >= ADC_LR_GROUP_MAX) ++ return false; ++ ++ *grp = idx; ++ dev_dbg(rk3308->plat_dev, ++ "ADC_TYPE_ALL, idx: %d, get grp: %d\n", ++ idx, *grp); ++ } else if (type == ADC_TYPE_DBG) { ++ if (idx >= ADC_LR_GROUP_MAX) ++ return false; ++ ++ if (idx == (int)rk3308->cur_dbg_grp) ++ *grp = idx; ++ else ++ *grp = ADC_GRP_SKIP_MAGIC; ++ ++ dev_dbg(rk3308->plat_dev, ++ "ADC_TYPE_DBG, idx: %d, get grp: %d\n", ++ idx, *grp); ++ } else { ++ if (idx >= 1) ++ return false; ++ ++ *grp = rk3308->loopback_grp; ++ dev_dbg(rk3308->plat_dev, ++ "ADC_TYPE_LOOPBACK, idx: %d, get grp: %d\n", ++ idx, *grp); ++ } + +- /* Step 15 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, +- RK3308_DAC_BUF_REF_L_EN | RK3308_DAC_BUF_REF_R_EN, +- RK3308_DAC_BUF_REF_L_DIS | RK3308_DAC_BUF_REF_R_DIS); ++ return true; ++} + +- /* Step 16 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON00, +- RK3308_DAC_CURRENT_EN, +- RK3308_DAC_CURRENT_DIS); ++static int rk3308_codec_get_dac_path_state(struct rk3308_codec_priv *rk3308) ++{ ++ return rk3308->dac_path_state; ++} + +- /* Step 17 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, +- RK3308_DAC_L_HPOUT_WORK | RK3308_DAC_R_HPOUT_WORK, +- RK3308_DAC_L_HPOUT_INIT | RK3308_DAC_R_HPOUT_INIT); ++static void rk3308_codec_set_dac_path_state(struct rk3308_codec_priv *rk3308, ++ int state) ++{ ++ rk3308->dac_path_state = state; ++} + +- /* Step 18 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, +- RK3308_DAC_L_HPMIX_WORK | RK3308_DAC_R_HPMIX_WORK, +- RK3308_DAC_L_HPMIX_INIT | RK3308_DAC_R_HPMIX_INIT); ++static void rk3308_headphone_ctl(struct rk3308_codec_priv *rk3308, int on) ++{ ++ if (rk3308->hp_ctl_gpio) ++ gpiod_direction_output(rk3308->hp_ctl_gpio, on); ++} + +- /* Step 19 */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON12, +- RK3308_DAC_L_HPMIX_GAIN_MSK | +- RK3308_DAC_R_HPMIX_GAIN_MSK, +- RK3308_DAC_L_HPMIX_GAIN_NDB_6 | +- RK3308_DAC_R_HPMIX_GAIN_NDB_6); ++static void rk3308_speaker_ctl(struct rk3308_codec_priv *rk3308, int on) ++{ ++ if (on) { ++ if (rk3308->pa_drv_gpio) { ++ gpiod_direction_output(rk3308->pa_drv_gpio, on); ++ msleep(rk3308->delay_pa_drv_ms); ++ } + +- /* +- * Note2. If the ACODEC_DAC_ANA_CON12[7] or ACODEC_DAC_ANA_CON12[3] +- * is set to 0x1, add the steps from the section Disable DAC +- * Configuration Standard Usage Flow after complete the step 19 +- */ ++ if (rk3308->spk_ctl_gpio) ++ gpiod_direction_output(rk3308->spk_ctl_gpio, on); ++ } else { ++ if (rk3308->spk_ctl_gpio) ++ gpiod_direction_output(rk3308->spk_ctl_gpio, on); + +- return 0; ++ if (rk3308->pa_drv_gpio) { ++ msleep(rk3308->delay_pa_drv_ms); ++ gpiod_direction_output(rk3308->pa_drv_gpio, on); ++ } ++ } + } + +-static int rk3308_codec_power_on(struct snd_soc_codec *codec) ++static int rk3308_codec_reset(struct snd_soc_codec *codec) + { + struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + +- /* 1. Supply the power of digital part and reset the Audio Codec */ +- /* Do nothing */ ++ reset_control_assert(rk3308->reset); ++ usleep_range(2000, 2500); /* estimated value */ ++ reset_control_deassert(rk3308->reset); + +- /* +- * 2. Configure ACODEC_DAC_ANA_CON1[1:0] and ACODEC_DAC_ANA_CON1[5:4] +- * to 0x1, to setup dc voltage of the DAC channel output +- */ +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, +- RK3308_DAC_POP_SOUND_L_MSK, RK3308_DAC_POP_SOUND_L_INIT); +- regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, +- RK3308_DAC_POP_SOUND_R_MSK, RK3308_DAC_POP_SOUND_R_INIT); ++ regmap_write(rk3308->regmap, RK3308_GLB_CON, 0x00); ++ usleep_range(200, 300); /* estimated value */ ++ regmap_write(rk3308->regmap, RK3308_GLB_CON, ++ RK3308_SYS_WORK | ++ RK3308_DAC_DIG_WORK | ++ RK3308_ADC_DIG_WORK); + +- /* +- * 3. Configure the register ACODEC_ADC_ANA_CON10[6:0] to 0x1 +- * +- * Note: Only the reg (ADC_ANA_CON10+0x0)[6:0] represent the control +- * signal to select current to pre-charge/dis_charge +- */ +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), +- RK3308_ADC_CURRENT_CHARGE_MSK, RK3308_ADC_SEL_I_64(1)); ++ return 0; ++} + +- /* 4. Supply the power of the analog part(AVDD,AVDDRV) */ ++static int rk3308_codec_adc_dig_reset(struct rk3308_codec_priv *rk3308) ++{ ++ regmap_update_bits(rk3308->regmap, RK3308_GLB_CON, ++ RK3308_ADC_DIG_WORK, ++ RK3308_ADC_DIG_RESET); ++ udelay(50); ++ regmap_update_bits(rk3308->regmap, RK3308_GLB_CON, ++ RK3308_ADC_DIG_WORK, ++ RK3308_ADC_DIG_WORK); + +- /* +- * 5. Configure the register ACODEC_ADC_ANA_CON10[7] to 0x1 to setup +- * reference voltage +- * +- * Note: Only the reg (ADC_ANA_CON10+0x0)[7] represent the enable +- * signal of reference voltage module +- */ +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), +- RK3308_ADC_REF_EN, RK3308_ADC_REF_EN); ++ return 0; ++} + +- /* +- * 6. Change the register ACODEC_ADC_ANA_CON10[6:0] from the 0x1 to +- * 0x7f step by step or configure the ACODEC_ADC_ANA_CON10[6:0] to +- * 0x7f directly. The suggestion slot time of the step is 20ms. +- */ +- mdelay(20); +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), +- RK3308_ADC_CURRENT_CHARGE_MSK, +- RK3308_ADC_DONT_SEL_ALL); ++static int rk3308_codec_dac_dig_reset(struct rk3308_codec_priv *rk3308) ++{ ++ regmap_update_bits(rk3308->regmap, RK3308_GLB_CON, ++ RK3308_DAC_DIG_WORK, ++ RK3308_DAC_DIG_RESET); ++ udelay(50); ++ regmap_update_bits(rk3308->regmap, RK3308_GLB_CON, ++ RK3308_DAC_DIG_WORK, ++ RK3308_DAC_DIG_WORK); + +- /* 7. Wait until the voltage of VCM keeps stable at the AVDD/2 */ +- usleep_range(200, 300); /* estimated value */ ++ return 0; ++} + +- /* +- * 8. Configure the register ACODEC_ADC_ANA_CON10[6:0] to the +- * appropriate value(expect 0x0) for reducing power. +- */ ++static int rk3308_set_bias_level(struct snd_soc_codec *codec, ++ enum snd_soc_bias_level level) ++{ ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + +- /* TODO: choose an appropriate charge value */ ++ switch (level) { ++ case SND_SOC_BIAS_ON: ++ break; ++ case SND_SOC_BIAS_PREPARE: ++ break; ++ case SND_SOC_BIAS_STANDBY: ++ regcache_cache_only(rk3308->regmap, false); ++ regcache_sync(rk3308->regmap); ++ break; ++ case SND_SOC_BIAS_OFF: ++ break; ++ } + + return 0; + } + +-static int rk3308_codec_power_off(struct snd_soc_codec *codec) ++static int rk3308_set_dai_fmt(struct snd_soc_dai *codec_dai, ++ unsigned int fmt) + { ++ struct snd_soc_codec *codec = codec_dai->codec; + struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ unsigned int adc_aif1 = 0, adc_aif2 = 0, dac_aif1 = 0, dac_aif2 = 0; ++ int idx, grp, is_master; ++ int type = ADC_TYPE_ALL; ++ ++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { ++ case SND_SOC_DAIFMT_CBS_CFS: ++ adc_aif2 |= RK3308_ADC_IO_MODE_SLAVE; ++ adc_aif2 |= RK3308_ADC_MODE_SLAVE; ++ dac_aif2 |= RK3308_DAC_IO_MODE_SLAVE; ++ dac_aif2 |= RK3308_DAC_MODE_SLAVE; ++ is_master = 0; ++ break; ++ case SND_SOC_DAIFMT_CBM_CFM: ++ adc_aif2 |= RK3308_ADC_IO_MODE_MASTER; ++ adc_aif2 |= RK3308_ADC_MODE_MASTER; ++ dac_aif2 |= RK3308_DAC_IO_MODE_MASTER; ++ dac_aif2 |= RK3308_DAC_MODE_MASTER; ++ is_master = 1; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { ++ case SND_SOC_DAIFMT_DSP_A: ++ adc_aif1 |= RK3308_ADC_I2S_MODE_PCM; ++ dac_aif1 |= RK3308_DAC_I2S_MODE_PCM; ++ break; ++ case SND_SOC_DAIFMT_I2S: ++ adc_aif1 |= RK3308_ADC_I2S_MODE_I2S; ++ dac_aif1 |= RK3308_DAC_I2S_MODE_I2S; ++ break; ++ case SND_SOC_DAIFMT_RIGHT_J: ++ adc_aif1 |= RK3308_ADC_I2S_MODE_RJ; ++ dac_aif1 |= RK3308_DAC_I2S_MODE_RJ; ++ break; ++ case SND_SOC_DAIFMT_LEFT_J: ++ adc_aif1 |= RK3308_ADC_I2S_MODE_LJ; ++ dac_aif1 |= RK3308_DAC_I2S_MODE_LJ; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { ++ case SND_SOC_DAIFMT_NB_NF: ++ adc_aif1 |= RK3308_ADC_I2S_LRC_POL_NORMAL; ++ adc_aif2 |= RK3308_ADC_I2S_BIT_CLK_POL_NORMAL; ++ dac_aif1 |= RK3308_DAC_I2S_LRC_POL_NORMAL; ++ dac_aif2 |= RK3308_DAC_I2S_BIT_CLK_POL_NORMAL; ++ break; ++ case SND_SOC_DAIFMT_IB_IF: ++ adc_aif1 |= RK3308_ADC_I2S_LRC_POL_REVERSAL; ++ adc_aif2 |= RK3308_ADC_I2S_BIT_CLK_POL_REVERSAL; ++ dac_aif1 |= RK3308_DAC_I2S_LRC_POL_REVERSAL; ++ dac_aif2 |= RK3308_DAC_I2S_BIT_CLK_POL_REVERSAL; ++ break; ++ case SND_SOC_DAIFMT_IB_NF: ++ adc_aif1 |= RK3308_ADC_I2S_LRC_POL_NORMAL; ++ adc_aif2 |= RK3308_ADC_I2S_BIT_CLK_POL_REVERSAL; ++ dac_aif1 |= RK3308_DAC_I2S_LRC_POL_NORMAL; ++ dac_aif2 |= RK3308_DAC_I2S_BIT_CLK_POL_REVERSAL; ++ break; ++ case SND_SOC_DAIFMT_NB_IF: ++ adc_aif1 |= RK3308_ADC_I2S_LRC_POL_REVERSAL; ++ adc_aif2 |= RK3308_ADC_I2S_BIT_CLK_POL_NORMAL; ++ dac_aif1 |= RK3308_DAC_I2S_LRC_POL_REVERSAL; ++ dac_aif2 |= RK3308_DAC_I2S_BIT_CLK_POL_NORMAL; ++ break; ++ default: ++ return -EINVAL; ++ } + + /* +- * 1. Keep the power on and disable the DAC and ADC path according to +- * the section power on configuration standard usage flow. ++ * Hold ADC Digital registers start at master mode ++ * ++ * There are 8 ADCs and use the same SCLK and LRCK internal for master ++ * mode, We need to make sure that they are in effect at the same time, ++ * otherwise they will cause the abnormal clocks. + */ ++ if (is_master) ++ regmap_update_bits(rk3308->regmap, RK3308_GLB_CON, ++ RK3308_ADC_DIG_WORK, ++ RK3308_ADC_DIG_RESET); ++ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON01(grp), ++ RK3308_ADC_I2S_LRC_POL_MSK | ++ RK3308_ADC_I2S_MODE_MSK, ++ adc_aif1); ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON02(grp), ++ RK3308_ADC_IO_MODE_MSK | ++ RK3308_ADC_MODE_MSK | ++ RK3308_ADC_I2S_BIT_CLK_POL_MSK, ++ adc_aif2); ++ } + +- /* 2. Configure the register ACODEC_ADC_ANA_CON10[6:0] to 0x1 */ +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), +- RK3308_ADC_CURRENT_CHARGE_MSK, RK3308_ADC_SEL_I_64(1)); ++ /* Hold ADC Digital registers end at master mode */ ++ if (is_master) ++ regmap_update_bits(rk3308->regmap, RK3308_GLB_CON, ++ RK3308_ADC_DIG_WORK, ++ RK3308_ADC_DIG_WORK); + +- /* 3. Configure the register ACODEC_ADC_ANA_CON10[7] to 0x0 */ +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), +- RK3308_ADC_REF_EN, RK3308_ADC_REF_DIS); ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON01, ++ RK3308_DAC_I2S_LRC_POL_MSK | ++ RK3308_DAC_I2S_MODE_MSK, ++ dac_aif1); ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON02, ++ RK3308_DAC_IO_MODE_MSK | ++ RK3308_DAC_MODE_MSK | ++ RK3308_DAC_I2S_BIT_CLK_POL_MSK, ++ dac_aif2); ++ ++ return 0; ++} ++ ++static int rk3308_codec_dac_dig_config(struct rk3308_codec_priv *rk3308, ++ struct snd_pcm_hw_params *params) ++{ ++ unsigned int dac_aif1 = 0, dac_aif2 = 0; ++ ++ /* Clear the status of DAC DIG Digital reigisters */ ++ rk3308_codec_dac_dig_reset(rk3308); ++ ++ switch (params_format(params)) { ++ case SNDRV_PCM_FORMAT_S16_LE: ++ dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_16BITS; ++ break; ++ case SNDRV_PCM_FORMAT_S20_3LE: ++ dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_20BITS; ++ break; ++ case SNDRV_PCM_FORMAT_S24_LE: ++ dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_24BITS; ++ break; ++ case SNDRV_PCM_FORMAT_S32_LE: ++ dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_32BITS; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ dac_aif1 |= RK3308_DAC_I2S_LR_NORMAL; ++ dac_aif2 |= RK3308_DAC_I2S_WORK; ++ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON01, ++ RK3308_DAC_I2S_VALID_LEN_MSK | ++ RK3308_DAC_I2S_LR_MSK, ++ dac_aif1); ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON02, ++ RK3308_DAC_I2S_MSK, ++ dac_aif2); ++ ++ return 0; ++} ++ ++static int rk3308_codec_adc_dig_config(struct rk3308_codec_priv *rk3308, ++ struct snd_pcm_hw_params *params) ++{ ++ unsigned int adc_aif1 = 0, adc_aif2 = 0; ++ int type = ADC_TYPE_NORMAL; ++ int idx, grp; ++ ++ /* Clear the status of ADC DIG Digital reigisters */ ++ rk3308_codec_adc_dig_reset(rk3308); ++ ++ switch (params_format(params)) { ++ case SNDRV_PCM_FORMAT_S16_LE: ++ adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_16BITS; ++ break; ++ case SNDRV_PCM_FORMAT_S20_3LE: ++ adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_20BITS; ++ break; ++ case SNDRV_PCM_FORMAT_S24_LE: ++ adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_24BITS; ++ break; ++ case SNDRV_PCM_FORMAT_S32_LE: ++ adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_32BITS; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (params_channels(params)) { ++ case 1: ++ adc_aif1 |= RK3308_ADC_I2S_MONO; ++ break; ++ case 2: ++ case 4: ++ case 6: ++ case 8: ++ adc_aif1 |= RK3308_ADC_I2S_STEREO; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ adc_aif1 |= RK3308_ADC_I2S_LR_NORMAL; ++ adc_aif2 |= RK3308_ADC_I2S_WORK; ++ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON01(grp), ++ RK3308_ADC_I2S_VALID_LEN_MSK | ++ RK3308_ADC_I2S_LR_MSK | ++ RK3308_ADC_I2S_TYPE_MSK, ++ adc_aif1); ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON02(grp), ++ RK3308_ADC_I2S_MSK, ++ adc_aif2); ++ } ++ ++ return 0; ++} ++ ++static int rk3308_codec_update_adc_grps(struct rk3308_codec_priv *rk3308, ++ struct snd_pcm_hw_params *params) ++{ ++ switch (params_channels(params)) { ++ case 1: ++ rk3308->used_adc_grps = 1; ++ break; ++ case 2: ++ case 4: ++ case 6: ++ case 8: ++ rk3308->used_adc_grps = params_channels(params) / 2; ++ break; ++ default: ++ dev_err(rk3308->plat_dev, "Invalid channels: %d\n", ++ params_channels(params)); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int rk3308_mute_stream(struct snd_soc_dai *dai, int mute, int stream) ++{ ++ struct snd_soc_codec *codec = dai->codec; ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ ++ if (stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ int dgain; ++ ++ if (mute) { ++ for (dgain = 0x2; dgain <= 0x7; dgain++) { ++ /* ++ * Keep the max -> min digital CIC interpolation ++ * filter gain step by step. ++ * ++ * loud: 0x2; whisper: 0x7 ++ */ ++ regmap_update_bits(rk3308->regmap, ++ RK3308_DAC_DIG_CON04, ++ RK3308_DAC_CIC_IF_GAIN_MSK, ++ dgain); ++ usleep_range(200, 300); /* estimated value */ ++ } ++ ++#if !DEBUG_POP_ALWAYS ++ rk3308_headphone_ctl(rk3308, 0); ++ rk3308_speaker_ctl(rk3308, 0); ++#endif ++ } else { ++#if !DEBUG_POP_ALWAYS ++ if (rk3308->dac_output == DAC_LINEOUT) ++ rk3308_speaker_ctl(rk3308, 1); ++ else if (rk3308->dac_output == DAC_HPOUT) ++ rk3308_headphone_ctl(rk3308, 1); ++ ++ if (rk3308->delay_start_play_ms) ++ msleep(rk3308->delay_start_play_ms); ++#endif ++ for (dgain = 0x7; dgain >= 0x2; dgain--) { ++ /* ++ * Keep the min -> max digital CIC interpolation ++ * filter gain step by step ++ * ++ * loud: 0x2; whisper: 0x7 ++ */ ++ regmap_update_bits(rk3308->regmap, ++ RK3308_DAC_DIG_CON04, ++ RK3308_DAC_CIC_IF_GAIN_MSK, ++ dgain); ++ usleep_range(200, 300); /* estimated value */ ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++static int rk3308_codec_digital_fadein(struct rk3308_codec_priv *rk3308) ++{ ++ unsigned int dgain, dgain_ref; ++ ++ if (rk3308->hpout_l_dgain != rk3308->hpout_r_dgain) { ++ pr_warn("HPOUT l_dgain: 0x%x != r_dgain: 0x%x\n", ++ rk3308->hpout_l_dgain, rk3308->hpout_r_dgain); ++ dgain_ref = min(rk3308->hpout_l_dgain, rk3308->hpout_r_dgain); ++ } else { ++ dgain_ref = rk3308->hpout_l_dgain; ++ } + + /* +- * 4.Change the register ACODEC_ADC_ANA_CON10[6:0] from the 0x1 to 0x7f +- * step by step or configure the ACODEC_ADC_ANA_CON10[6:0] to 0x7f +- * directly. The suggestion slot time of the step is 20ms ++ * We'd better change the gain of the left and right channels ++ * at the same time to avoid different listening + */ +- mdelay(20); +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), +- RK3308_ADC_CURRENT_CHARGE_MSK, +- RK3308_ADC_DONT_SEL_ALL); ++ for (dgain = RK3308_DAC_L_HPOUT_GAIN_NDB_39; ++ dgain <= dgain_ref; dgain++) { ++ /* Step 02 decrease dgains for de-pop */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON05, ++ RK3308_DAC_L_HPOUT_GAIN_MSK, ++ dgain); ++ ++ /* Step 02 decrease dgains for de-pop */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON06, ++ RK3308_DAC_R_HPOUT_GAIN_MSK, ++ dgain); ++ } ++ ++ return 0; ++} ++ ++static int rk3308_codec_digital_fadeout(struct rk3308_codec_priv *rk3308) ++{ ++ unsigned int l_dgain, r_dgain; ++ ++ /* ++ * Note. In the step2, adjusting the register step by step to ++ * the appropriate value and taking 20ms as time step ++ */ ++ regmap_read(rk3308->regmap, RK3308_DAC_ANA_CON05, &l_dgain); ++ l_dgain &= RK3308_DAC_L_HPOUT_GAIN_MSK; ++ ++ regmap_read(rk3308->regmap, RK3308_DAC_ANA_CON06, &r_dgain); ++ r_dgain &= RK3308_DAC_R_HPOUT_GAIN_MSK; ++ ++ if (l_dgain != r_dgain) { ++ pr_warn("HPOUT l_dgain: 0x%x != r_dgain: 0x%x\n", ++ l_dgain, r_dgain); ++ l_dgain = min(l_dgain, r_dgain); ++ } ++ ++ /* ++ * We'd better change the gain of the left and right channels ++ * at the same time to avoid different listening ++ */ ++ while (l_dgain >= RK3308_DAC_L_HPOUT_GAIN_NDB_39) { ++ /* Step 02 decrease dgains for de-pop */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON05, ++ RK3308_DAC_L_HPOUT_GAIN_MSK, ++ l_dgain); ++ ++ /* Step 02 decrease dgains for de-pop */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON06, ++ RK3308_DAC_R_HPOUT_GAIN_MSK, ++ l_dgain); ++ ++ usleep_range(200, 300); /* estimated value */ ++ ++ if (l_dgain == RK3308_DAC_L_HPOUT_GAIN_NDB_39) ++ break; ++ ++ l_dgain--; ++ } ++ ++ return 0; ++} ++ ++static int rk3308_codec_dac_lineout_enable(struct rk3308_codec_priv *rk3308) ++{ ++ if (rk3308->codec_ver == ACODEC_VERSION_B) { ++ /* Step 04 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON15, ++ RK3308_DAC_LINEOUT_POP_SOUND_L_MSK | ++ RK3308_DAC_LINEOUT_POP_SOUND_R_MSK, ++ RK3308_DAC_L_SEL_DC_FROM_INTERNAL | ++ RK3308_DAC_R_SEL_DC_FROM_INTERNAL); ++ } ++ ++ /* Step 07 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, ++ RK3308_DAC_L_LINEOUT_EN | ++ RK3308_DAC_R_LINEOUT_EN, ++ RK3308_DAC_L_LINEOUT_EN | ++ RK3308_DAC_R_LINEOUT_EN); ++ ++ udelay(20); ++ ++ if (rk3308->codec_ver == ACODEC_VERSION_B) { ++ /* Step 10 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON15, ++ RK3308_DAC_LINEOUT_POP_SOUND_L_MSK | ++ RK3308_DAC_LINEOUT_POP_SOUND_R_MSK, ++ RK3308_DAC_L_SEL_LINEOUT_FROM_INTERNAL | ++ RK3308_DAC_R_SEL_LINEOUT_FROM_INTERNAL); ++ ++ udelay(20); ++ } ++ ++ /* Step 19 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, ++ RK3308_DAC_L_LINEOUT_UNMUTE | ++ RK3308_DAC_R_LINEOUT_UNMUTE, ++ RK3308_DAC_L_LINEOUT_UNMUTE | ++ RK3308_DAC_R_LINEOUT_UNMUTE); ++ udelay(20); ++ ++ return 0; ++} ++ ++static int rk3308_codec_dac_lineout_disable(struct rk3308_codec_priv *rk3308) ++{ ++ /* Step 08 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, ++ RK3308_DAC_L_LINEOUT_UNMUTE | ++ RK3308_DAC_R_LINEOUT_UNMUTE, ++ RK3308_DAC_L_LINEOUT_MUTE | ++ RK3308_DAC_R_LINEOUT_MUTE); ++ ++ /* Step 09 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, ++ RK3308_DAC_L_LINEOUT_EN | ++ RK3308_DAC_R_LINEOUT_EN, ++ RK3308_DAC_L_LINEOUT_DIS | ++ RK3308_DAC_R_LINEOUT_DIS); ++ ++ return 0; ++} ++ ++static int rk3308_codec_dac_hpout_enable(struct rk3308_codec_priv *rk3308) ++{ ++ /* Step 03 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, ++ RK3308_DAC_HPOUT_POP_SOUND_L_MSK | ++ RK3308_DAC_HPOUT_POP_SOUND_R_MSK, ++ RK3308_DAC_HPOUT_POP_SOUND_L_WORK | ++ RK3308_DAC_HPOUT_POP_SOUND_R_WORK); ++ ++ udelay(20); ++ ++ /* Step 07 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, ++ RK3308_DAC_L_HPOUT_EN | ++ RK3308_DAC_R_HPOUT_EN, ++ RK3308_DAC_L_HPOUT_EN | ++ RK3308_DAC_R_HPOUT_EN); ++ ++ udelay(20); ++ ++ /* Step 08 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, ++ RK3308_DAC_L_HPOUT_WORK | ++ RK3308_DAC_R_HPOUT_WORK, ++ RK3308_DAC_L_HPOUT_WORK | ++ RK3308_DAC_R_HPOUT_WORK); ++ ++ udelay(20); ++ ++ /* Step 16 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, ++ RK3308_DAC_L_HPOUT_UNMUTE | ++ RK3308_DAC_R_HPOUT_UNMUTE, ++ RK3308_DAC_L_HPOUT_UNMUTE | ++ RK3308_DAC_R_HPOUT_UNMUTE); ++ ++ udelay(20); ++ ++ return 0; ++} ++ ++static int rk3308_codec_dac_hpout_disable(struct rk3308_codec_priv *rk3308) ++{ ++ /* Step 03 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, ++ RK3308_DAC_HPOUT_POP_SOUND_L_MSK | ++ RK3308_DAC_HPOUT_POP_SOUND_R_MSK, ++ RK3308_DAC_HPOUT_POP_SOUND_L_INIT | ++ RK3308_DAC_HPOUT_POP_SOUND_R_INIT); ++ ++ /* Step 07 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, ++ RK3308_DAC_L_HPOUT_EN | ++ RK3308_DAC_R_HPOUT_EN, ++ RK3308_DAC_L_HPOUT_DIS | ++ RK3308_DAC_R_HPOUT_DIS); ++ ++ /* Step 08 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, ++ RK3308_DAC_L_HPOUT_WORK | ++ RK3308_DAC_R_HPOUT_WORK, ++ RK3308_DAC_L_HPOUT_INIT | ++ RK3308_DAC_R_HPOUT_INIT); ++ ++ /* Step 16 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, ++ RK3308_DAC_L_HPOUT_UNMUTE | ++ RK3308_DAC_R_HPOUT_UNMUTE, ++ RK3308_DAC_L_HPOUT_MUTE | ++ RK3308_DAC_R_HPOUT_MUTE); ++ ++ return 0; ++} ++ ++static int rk3308_codec_dac_switch(struct rk3308_codec_priv *rk3308, ++ int dac_output) ++{ int ret = 0; ++ ++ if (rk3308->dac_output == dac_output) { ++ dev_info(rk3308->plat_dev, ++ "Don't need to change dac_output: %d\n", dac_output); ++ goto out; ++ } ++ ++ switch (dac_output) { ++ case DAC_LINEOUT: ++ case DAC_HPOUT: ++ case DAC_LINEOUT_HPOUT: ++ break; ++ default: ++ dev_err(rk3308->plat_dev, "Unknown value: %d\n", dac_output); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if (rk3308_codec_get_dac_path_state(rk3308) == PATH_BUSY) { ++ /* ++ * We can only switch the audio path to LINEOUT or HPOUT on ++ * codec during playbacking, otherwise, just update the ++ * dac_output flag. ++ */ ++ switch (dac_output) { ++ case DAC_LINEOUT: ++ rk3308_headphone_ctl(rk3308, 0); ++ rk3308_speaker_ctl(rk3308, 1); ++ rk3308_codec_dac_hpout_disable(rk3308); ++ rk3308_codec_dac_lineout_enable(rk3308); ++ break; ++ case DAC_HPOUT: ++ rk3308_speaker_ctl(rk3308, 0); ++ rk3308_headphone_ctl(rk3308, 1); ++ rk3308_codec_dac_lineout_disable(rk3308); ++ rk3308_codec_dac_hpout_enable(rk3308); ++ break; ++ case DAC_LINEOUT_HPOUT: ++ rk3308_speaker_ctl(rk3308, 1); ++ rk3308_headphone_ctl(rk3308, 1); ++ rk3308_codec_dac_lineout_enable(rk3308); ++ rk3308_codec_dac_hpout_enable(rk3308); ++ break; ++ default: ++ break; ++ } ++ } ++ ++ rk3308->dac_output = dac_output; ++out: ++ dev_dbg(rk3308->plat_dev, "switch dac_output to: %d\n", ++ rk3308->dac_output); ++ ++ return ret; ++} ++ ++static int rk3308_codec_dac_enable(struct rk3308_codec_priv *rk3308) ++{ ++ /* ++ * Note1. If the ACODEC_DAC_ANA_CON12[6] or ACODEC_DAC_ANA_CON12[2] ++ * is set to 0x1, ignoring the step9~12. ++ */ ++ ++ /* ++ * Note2. If the ACODEC_ DAC_ANA_CON12[7] or ACODEC_DAC_ANA_CON12[3] ++ * is set to 0x1, the ADC0 or ADC1 should be enabled firstly, and ++ * please refer to Enable ADC Configuration Standard Usage Flow(expect ++ * step7~step9,step14). ++ */ ++ ++ /* ++ * Note3. If no opening the line out, ignoring the step6, step17 and ++ * step19. ++ */ ++ ++ /* ++ * Note4. If no opening the headphone out, ignoring the step3,step7~8, ++ * step16 and step18. ++ */ ++ ++ /* ++ * Note5. In the step18, adjust the register step by step to the ++ * appropriate value and taking 10ms as one time step ++ */ ++ ++ /* ++ * 1. Set the ACODEC_DAC_ANA_CON0[0] to 0x1, to enable the current ++ * source of DAC ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON00, ++ RK3308_DAC_CURRENT_MSK, ++ RK3308_DAC_CURRENT_EN); ++ ++ udelay(20); ++ ++ /* ++ * 2. Set the ACODEC_DAC_ANA_CON1[6] and ACODEC_DAC_ANA_CON1[2] to 0x1, ++ * to enable the reference voltage buffer ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, ++ RK3308_DAC_BUF_REF_L_MSK | ++ RK3308_DAC_BUF_REF_R_MSK, ++ RK3308_DAC_BUF_REF_L_EN | ++ RK3308_DAC_BUF_REF_R_EN); ++ ++ /* Waiting the stable reference voltage */ ++ mdelay(1); ++ ++ if (rk3308->dac_output == DAC_HPOUT || ++ rk3308->dac_output == DAC_LINEOUT_HPOUT) { ++ /* Step 03 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, ++ RK3308_DAC_HPOUT_POP_SOUND_L_MSK | ++ RK3308_DAC_HPOUT_POP_SOUND_R_MSK, ++ RK3308_DAC_HPOUT_POP_SOUND_L_WORK | ++ RK3308_DAC_HPOUT_POP_SOUND_R_WORK); ++ ++ udelay(20); ++ } ++ ++ if (rk3308->codec_ver == ACODEC_VERSION_B && ++ (rk3308->dac_output == DAC_LINEOUT || ++ rk3308->dac_output == DAC_LINEOUT_HPOUT)) { ++ /* Step 04 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON15, ++ RK3308_DAC_LINEOUT_POP_SOUND_L_MSK | ++ RK3308_DAC_LINEOUT_POP_SOUND_R_MSK, ++ RK3308_DAC_L_SEL_DC_FROM_INTERNAL | ++ RK3308_DAC_R_SEL_DC_FROM_INTERNAL); ++ ++ udelay(20); ++ } ++ ++ /* Step 05 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, ++ RK3308_DAC_L_HPMIX_EN | ++ RK3308_DAC_R_HPMIX_EN, ++ RK3308_DAC_L_HPMIX_EN | ++ RK3308_DAC_R_HPMIX_EN); ++ ++ /* Waiting the stable HPMIX */ ++ mdelay(1); ++ ++ /* Step 06. Reset HPMIX and recover HPMIX gains */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, ++ RK3308_DAC_L_HPMIX_WORK | ++ RK3308_DAC_R_HPMIX_WORK, ++ RK3308_DAC_L_HPMIX_INIT | ++ RK3308_DAC_R_HPMIX_INIT); ++ udelay(50); ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, ++ RK3308_DAC_L_HPMIX_WORK | ++ RK3308_DAC_R_HPMIX_WORK, ++ RK3308_DAC_L_HPMIX_WORK | ++ RK3308_DAC_R_HPMIX_WORK); ++ ++ udelay(20); ++ ++ if (rk3308->dac_output == DAC_LINEOUT || ++ rk3308->dac_output == DAC_LINEOUT_HPOUT) { ++ /* Step 07 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, ++ RK3308_DAC_L_LINEOUT_EN | ++ RK3308_DAC_R_LINEOUT_EN, ++ RK3308_DAC_L_LINEOUT_EN | ++ RK3308_DAC_R_LINEOUT_EN); ++ ++ udelay(20); ++ } ++ ++ if (rk3308->dac_output == DAC_HPOUT || ++ rk3308->dac_output == DAC_LINEOUT_HPOUT) { ++ /* Step 08 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, ++ RK3308_DAC_L_HPOUT_EN | ++ RK3308_DAC_R_HPOUT_EN, ++ RK3308_DAC_L_HPOUT_EN | ++ RK3308_DAC_R_HPOUT_EN); ++ ++ udelay(20); ++ ++ /* Step 09 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, ++ RK3308_DAC_L_HPOUT_WORK | ++ RK3308_DAC_R_HPOUT_WORK, ++ RK3308_DAC_L_HPOUT_WORK | ++ RK3308_DAC_R_HPOUT_WORK); ++ ++ udelay(20); ++ } ++ ++ if (rk3308->codec_ver == ACODEC_VERSION_B) { ++ /* Step 10 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON15, ++ RK3308_DAC_LINEOUT_POP_SOUND_L_MSK | ++ RK3308_DAC_LINEOUT_POP_SOUND_R_MSK, ++ RK3308_DAC_L_SEL_LINEOUT_FROM_INTERNAL | ++ RK3308_DAC_R_SEL_LINEOUT_FROM_INTERNAL); ++ ++ udelay(20); ++ } ++ ++ /* Step 11 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, ++ RK3308_DAC_L_REF_EN | ++ RK3308_DAC_R_REF_EN, ++ RK3308_DAC_L_REF_EN | ++ RK3308_DAC_R_REF_EN); ++ ++ udelay(20); ++ ++ /* Step 12 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, ++ RK3308_DAC_L_CLK_EN | ++ RK3308_DAC_R_CLK_EN, ++ RK3308_DAC_L_CLK_EN | ++ RK3308_DAC_R_CLK_EN); ++ ++ udelay(20); ++ ++ /* Step 13 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, ++ RK3308_DAC_L_DAC_EN | ++ RK3308_DAC_R_DAC_EN, ++ RK3308_DAC_L_DAC_EN | ++ RK3308_DAC_R_DAC_EN); ++ ++ udelay(20); ++ ++ /* Step 14 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, ++ RK3308_DAC_L_DAC_WORK | ++ RK3308_DAC_R_DAC_WORK, ++ RK3308_DAC_L_DAC_WORK | ++ RK3308_DAC_R_DAC_WORK); ++ ++ udelay(20); ++ ++ /* Step 15 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON12, ++ RK3308_DAC_L_HPMIX_SEL_MSK | ++ RK3308_DAC_R_HPMIX_SEL_MSK, ++ RK3308_DAC_L_HPMIX_I2S | ++ RK3308_DAC_R_HPMIX_I2S); ++ ++ udelay(20); ++ ++ /* Step 16 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, ++ RK3308_DAC_L_HPMIX_UNMUTE | ++ RK3308_DAC_R_HPMIX_UNMUTE, ++ RK3308_DAC_L_HPMIX_UNMUTE | ++ RK3308_DAC_R_HPMIX_UNMUTE); ++ ++ udelay(20); ++ ++ /* Step 17: Put configuration HPMIX Gain to DAPM */ ++ ++ if (rk3308->dac_output == DAC_HPOUT || ++ rk3308->dac_output == DAC_LINEOUT_HPOUT) { ++ /* Step 18 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, ++ RK3308_DAC_L_HPOUT_UNMUTE | ++ RK3308_DAC_R_HPOUT_UNMUTE, ++ RK3308_DAC_L_HPOUT_UNMUTE | ++ RK3308_DAC_R_HPOUT_UNMUTE); ++ ++ udelay(20); ++ } ++ ++ if (rk3308->dac_output == DAC_LINEOUT || ++ rk3308->dac_output == DAC_LINEOUT_HPOUT) { ++ /* Step 19 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, ++ RK3308_DAC_L_LINEOUT_UNMUTE | ++ RK3308_DAC_R_LINEOUT_UNMUTE, ++ RK3308_DAC_L_LINEOUT_UNMUTE | ++ RK3308_DAC_R_LINEOUT_UNMUTE); ++ udelay(20); ++ } ++ ++ /* Step 20, put configuration HPOUT gain to DAPM control */ ++ /* Step 21, put configuration LINEOUT gain to DAPM control */ ++ ++ if (rk3308->dac_output == DAC_HPOUT || ++ rk3308->dac_output == DAC_LINEOUT_HPOUT) { ++ /* Just for HPOUT */ ++ rk3308_codec_digital_fadein(rk3308); ++ } ++ ++ rk3308->dac_endisable = true; ++ ++ /* TODO: TRY TO TEST DRIVE STRENGTH */ ++ ++ return 0; ++} ++ ++static int rk3308_codec_dac_disable(struct rk3308_codec_priv *rk3308) ++{ ++ /* ++ * Step 00 skipped. Keep the DAC channel work and input the mute signal. ++ */ ++ ++ /* Step 01 skipped. May set the min gain for LINEOUT. */ ++ ++ /* Step 02 skipped. May set the min gain for HPOUT. */ ++ ++ if (rk3308->dac_output == DAC_HPOUT || ++ rk3308->dac_output == DAC_LINEOUT_HPOUT) { ++ /* Just for HPOUT */ ++ rk3308_codec_digital_fadeout(rk3308); ++ } ++ ++ /* Step 03 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, ++ RK3308_DAC_L_HPMIX_UNMUTE | ++ RK3308_DAC_R_HPMIX_UNMUTE, ++ RK3308_DAC_L_HPMIX_UNMUTE | ++ RK3308_DAC_R_HPMIX_UNMUTE); ++ ++ /* Step 04 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON12, ++ RK3308_DAC_L_HPMIX_SEL_MSK | ++ RK3308_DAC_R_HPMIX_SEL_MSK, ++ RK3308_DAC_L_HPMIX_NONE | ++ RK3308_DAC_R_HPMIX_NONE); ++ /* Step 05 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, ++ RK3308_DAC_L_HPOUT_UNMUTE | ++ RK3308_DAC_R_HPOUT_UNMUTE, ++ RK3308_DAC_L_HPOUT_MUTE | ++ RK3308_DAC_R_HPOUT_MUTE); ++ ++ /* Step 06 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, ++ RK3308_DAC_L_DAC_WORK | ++ RK3308_DAC_R_DAC_WORK, ++ RK3308_DAC_L_DAC_INIT | ++ RK3308_DAC_R_DAC_INIT); ++ ++ /* Step 07 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, ++ RK3308_DAC_L_HPOUT_EN | ++ RK3308_DAC_R_HPOUT_EN, ++ RK3308_DAC_L_HPOUT_DIS | ++ RK3308_DAC_R_HPOUT_DIS); ++ ++ /* Step 08 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, ++ RK3308_DAC_L_LINEOUT_UNMUTE | ++ RK3308_DAC_R_LINEOUT_UNMUTE, ++ RK3308_DAC_L_LINEOUT_MUTE | ++ RK3308_DAC_R_LINEOUT_MUTE); ++ ++ /* Step 09 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, ++ RK3308_DAC_L_LINEOUT_EN | ++ RK3308_DAC_R_LINEOUT_EN, ++ RK3308_DAC_L_LINEOUT_DIS | ++ RK3308_DAC_R_LINEOUT_DIS); ++ ++ /* Step 10 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, ++ RK3308_DAC_L_HPMIX_EN | ++ RK3308_DAC_R_HPMIX_EN, ++ RK3308_DAC_L_HPMIX_DIS | ++ RK3308_DAC_R_HPMIX_DIS); ++ ++ /* Step 11 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, ++ RK3308_DAC_L_DAC_EN | ++ RK3308_DAC_R_DAC_EN, ++ RK3308_DAC_L_DAC_DIS | ++ RK3308_DAC_R_DAC_DIS); ++ ++ /* Step 12 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, ++ RK3308_DAC_L_CLK_EN | ++ RK3308_DAC_R_CLK_EN, ++ RK3308_DAC_L_CLK_DIS | ++ RK3308_DAC_R_CLK_DIS); ++ ++ /* Step 13 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, ++ RK3308_DAC_L_REF_EN | ++ RK3308_DAC_R_REF_EN, ++ RK3308_DAC_L_REF_DIS | ++ RK3308_DAC_R_REF_DIS); ++ ++ /* Step 14 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, ++ RK3308_DAC_HPOUT_POP_SOUND_L_MSK | ++ RK3308_DAC_HPOUT_POP_SOUND_R_MSK, ++ RK3308_DAC_HPOUT_POP_SOUND_L_INIT | ++ RK3308_DAC_HPOUT_POP_SOUND_R_INIT); ++ ++ /* Step 15 */ ++ if (rk3308->codec_ver == ACODEC_VERSION_B && ++ (rk3308->dac_output == DAC_LINEOUT || ++ rk3308->dac_output == DAC_LINEOUT_HPOUT)) { ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON15, ++ RK3308_DAC_LINEOUT_POP_SOUND_L_MSK | ++ RK3308_DAC_LINEOUT_POP_SOUND_R_MSK, ++ RK3308_DAC_L_SEL_DC_FROM_VCM | ++ RK3308_DAC_R_SEL_DC_FROM_VCM); ++ } ++ ++ /* Step 16 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, ++ RK3308_DAC_BUF_REF_L_EN | ++ RK3308_DAC_BUF_REF_R_EN, ++ RK3308_DAC_BUF_REF_L_DIS | ++ RK3308_DAC_BUF_REF_R_DIS); ++ ++ /* Step 17 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON00, ++ RK3308_DAC_CURRENT_EN, ++ RK3308_DAC_CURRENT_DIS); ++ ++ /* Step 18 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, ++ RK3308_DAC_L_HPOUT_WORK | ++ RK3308_DAC_R_HPOUT_WORK, ++ RK3308_DAC_L_HPOUT_INIT | ++ RK3308_DAC_R_HPOUT_INIT); ++ ++ /* Step 19 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, ++ RK3308_DAC_L_HPMIX_WORK | ++ RK3308_DAC_R_HPMIX_WORK, ++ RK3308_DAC_L_HPMIX_WORK | ++ RK3308_DAC_R_HPMIX_WORK); ++ ++ /* Step 20 skipped, may set the min gain for HPOUT. */ ++ ++ /* ++ * Note2. If the ACODEC_DAC_ANA_CON12[7] or ACODEC_DAC_ANA_CON12[3] ++ * is set to 0x1, add the steps from the section Disable ADC ++ * Configuration Standard Usage Flow after complete the step 19 ++ * ++ * IF USING LINE-IN ++ * rk3308_codec_adc_ana_disable(rk3308, type); ++ */ ++ ++ rk3308->dac_endisable = false; ++ ++ return 0; ++} ++ ++static int rk3308_codec_power_on(struct rk3308_codec_priv *rk3308) ++{ ++ unsigned int v; ++ ++ /* 0. Supply the power of digital part and reset the Audio Codec */ ++ /* Do nothing */ ++ ++ /* ++ * 1. Configure ACODEC_DAC_ANA_CON1[1:0] and ACODEC_DAC_ANA_CON1[5:4] ++ * to 0x1, to setup dc voltage of the DAC channel output. ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, ++ RK3308_DAC_HPOUT_POP_SOUND_L_MSK, ++ RK3308_DAC_HPOUT_POP_SOUND_L_INIT); ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, ++ RK3308_DAC_HPOUT_POP_SOUND_R_MSK, ++ RK3308_DAC_HPOUT_POP_SOUND_R_INIT); ++ ++ if (rk3308->codec_ver == ACODEC_VERSION_B) { ++ /* ++ * 2. Configure ACODEC_DAC_ANA_CON15[1:0] and ++ * ACODEC_DAC_ANA_CON15[5:4] to 0x1, to setup dc voltage of ++ * the DAC channel output. ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON15, ++ RK3308_DAC_LINEOUT_POP_SOUND_L_MSK, ++ RK3308_DAC_L_SEL_DC_FROM_VCM); ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON15, ++ RK3308_DAC_LINEOUT_POP_SOUND_R_MSK, ++ RK3308_DAC_R_SEL_DC_FROM_VCM); ++ } ++ ++ /* ++ * 3. Configure the register ACODEC_ADC_ANA_CON10[3:0] to 7’b000_0001. ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), ++ RK3308_ADC_CURRENT_CHARGE_MSK, ++ RK3308_ADC_SEL_I(0x1)); ++ ++ if (rk3308->codec_ver == ACODEC_VERSION_B) { ++ /* ++ * 4. Configure the register ACODEC_ADC_ANA_CON14[3:0] to ++ * 4’b0001. ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON14, ++ RK3308_DAC_CURRENT_CHARGE_MSK, ++ RK3308_DAC_SEL_I(0x1)); ++ } ++ ++ /* 5. Supply the power of the analog part(AVDD,AVDDRV) */ ++ ++ /* ++ * 6. Configure the register ACODEC_ADC_ANA_CON10[7] to 0x1 to setup ++ * reference voltage ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), ++ RK3308_ADC_REF_EN, RK3308_ADC_REF_EN); ++ ++ if (rk3308->codec_ver == ACODEC_VERSION_B) { ++ /* ++ * 7. Configure the register ACODEC_ADC_ANA_CON14[4] to 0x1 to ++ * setup reference voltage ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON14, ++ RK3308_DAC_VCM_LINEOUT_EN, ++ RK3308_DAC_VCM_LINEOUT_EN); ++ } ++ ++ /* ++ * 8. Change the register ACODEC_ADC_ANA_CON10[6:0] from the 0x1 to ++ * 0x7f step by step or configure the ACODEC_ADC_ANA_CON10[6:0] to ++ * 0x7f directly. Here the slot time of the step is 200us. ++ */ ++ for (v = 0x1; v <= 0x7f; v++) { ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), ++ RK3308_ADC_CURRENT_CHARGE_MSK, ++ v); ++ udelay(200); ++ } ++ ++ if (rk3308->codec_ver == ACODEC_VERSION_B) { ++ /* ++ * 9. Change the register ACODEC_ADC_ANA_CON14[3:0] from the 0x1 ++ * to 0xf step by step or configure the ++ * ACODEC_ADC_ANA_CON14[3:0] to 0xf directly. Here the slot ++ * time of the step is 200us. ++ */ ++ for (v = 0x1; v <= 0xf; v++) { ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON14, ++ RK3308_DAC_CURRENT_CHARGE_MSK, ++ v); ++ udelay(200); ++ } ++ } ++ ++ /* 10. Wait until the voltage of VCM keeps stable at the AVDD/2 */ ++ msleep(20); /* estimated value */ ++ ++ /* ++ * 11. Configure the register ACODEC_ADC_ANA_CON10[6:0] to the ++ * appropriate value(expect 0x0) for reducing power. ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), ++ RK3308_ADC_CURRENT_CHARGE_MSK, 0x7c); ++ ++ if (rk3308->codec_ver == ACODEC_VERSION_B) { ++ /* ++ * 12. Configure the register ACODEC_DAC_ANA_CON14[6:0] to the ++ * appropriate value(expect 0x0) for reducing power. ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON14, ++ RK3308_DAC_CURRENT_CHARGE_MSK, 0xf); ++ } ++ ++ return 0; ++} ++ ++static int rk3308_codec_power_off(struct rk3308_codec_priv *rk3308) ++{ ++ unsigned int v; ++ ++ /* ++ * 0. Keep the power on and disable the DAC and ADC path according to ++ * the section power on configuration standard usage flow. ++ */ ++ ++ /* ++ * 1. Configure the register ACODEC_ADC_ANA_CON10[6:0] to 7’b000_0001. ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), ++ RK3308_ADC_CURRENT_CHARGE_MSK, ++ RK3308_ADC_SEL_I(0x1)); ++ ++ if (rk3308->codec_ver == ACODEC_VERSION_B) { ++ /* ++ * 2. Configure the register ACODEC_DAC_ANA_CON14[3:0] to ++ * 4’b0001. ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON14, ++ RK3308_DAC_CURRENT_CHARGE_MSK, ++ RK3308_DAC_SEL_I(0x1)); ++ } ++ ++ /* 3. Configure the register ACODEC_ADC_ANA_CON10[7] to 0x0 */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), ++ RK3308_ADC_REF_EN, ++ RK3308_ADC_REF_DIS); ++ ++ if (rk3308->codec_ver == ACODEC_VERSION_B) { ++ /* 4. Configure the register ACODEC_DAC_ANA_CON14[7] to 0x0 */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON14, ++ RK3308_DAC_VCM_LINEOUT_EN, ++ RK3308_DAC_VCM_LINEOUT_DIS); ++ } ++ ++ /* ++ * 5. Change the register ACODEC_ADC_ANA_CON10[6:0] from the 0x1 to 0x7f ++ * step by step or configure the ACODEC_ADC_ANA_CON10[6:0] to 0x7f ++ * directly. Here the slot time of the step is 200us. ++ */ ++ for (v = 0x1; v <= 0x7f; v++) { ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), ++ RK3308_ADC_CURRENT_CHARGE_MSK, ++ v); ++ udelay(200); ++ } ++ ++ if (rk3308->codec_ver == ACODEC_VERSION_B) { ++ /* ++ * 6. Change the register ACODEC_DAC_ANA_CON14[3:0] from the 0x1 ++ * to 0xf step by step or configure the ++ * ACODEC_DAC_ANA_CON14[3:0] to 0xf directly. Here the slot ++ * time of the step is 200us. ++ */ ++ for (v = 0x1; v <= 0x7f; v++) { ++ regmap_update_bits(rk3308->regmap, ++ RK3308_ADC_ANA_CON10(0), ++ RK3308_ADC_CURRENT_CHARGE_MSK, ++ v); ++ udelay(200); ++ } ++ } ++ ++ /* 7. Wait until the voltage of VCM keeps stable at the AGND */ ++ msleep(20); /* estimated value */ ++ ++ /* 8. Power off the analog power supply */ ++ /* 9. Power off the digital power supply */ ++ ++ /* Do something via hardware */ ++ ++ return 0; ++} ++ ++static int rk3308_codec_headset_detect_enable(struct rk3308_codec_priv *rk3308) ++{ ++ /* ++ * Set ACODEC_DAC_ANA_CON0[1] to 0x1, to enable the headset insert ++ * detection ++ * ++ * Note. When the voltage of PAD HPDET> 8*AVDD/9, the output value of ++ * the pin_hpdet will be set to 0x1 and assert a interrupt ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON00, ++ RK3308_DAC_HEADPHONE_DET_MSK, ++ RK3308_DAC_HEADPHONE_DET_EN); ++ ++ return 0; ++} ++ ++static int rk3308_codec_headset_detect_disable(struct rk3308_codec_priv *rk3308) ++{ ++ /* ++ * Set ACODEC_DAC_ANA_CON0[1] to 0x0, to disable the headset insert ++ * detection ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON00, ++ RK3308_DAC_HEADPHONE_DET_MSK, ++ RK3308_DAC_HEADPHONE_DET_DIS); ++ ++ return 0; ++} ++ ++static int rk3308_codec_check_i2s_sdis(struct rk3308_codec_priv *rk3308, ++ int num) ++{ ++ int i, j, ret = 0; ++ ++ switch (num) { ++ case 1: ++ rk3308->which_i2s = ACODEC_TO_I2S1_2CH; ++ break; ++ case 2: ++ rk3308->which_i2s = ACODEC_TO_I2S3_4CH; ++ break; ++ case 4: ++ rk3308->which_i2s = ACODEC_TO_I2S2_8CH; ++ break; ++ default: ++ dev_err(rk3308->plat_dev, "Invalid i2s sdis num: %d\n", num); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ for (i = 0; i < num; i++) { ++ if (rk3308->i2s_sdis[i] > ADC_LR_GROUP_MAX - 1) { ++ dev_err(rk3308->plat_dev, ++ "i2s_sdis[%d]: %d is overflow\n", ++ i, rk3308->i2s_sdis[i]); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ for (j = 0; j < num; j++) { ++ if (i == j) ++ continue; ++ ++ if (rk3308->i2s_sdis[i] == rk3308->i2s_sdis[j]) { ++ dev_err(rk3308->plat_dev, ++ "Invalid i2s_sdis: [%d]%d == [%d]%d\n", ++ i, rk3308->i2s_sdis[i], ++ j, rk3308->i2s_sdis[j]); ++ ret = -EINVAL; ++ goto err; ++ } ++ } ++ } ++ ++err: ++ return ret; ++} ++ ++static int rk3308_codec_adc_grps_route_config(struct rk3308_codec_priv *rk3308) ++{ ++ int idx = 0; ++ ++ if (rk3308->which_i2s == ACODEC_TO_I2S2_8CH) { ++ for (idx = 0; idx < rk3308->to_i2s_grps; idx++) { ++ regmap_write(rk3308->grf, GRF_SOC_CON1, ++ GRF_I2S2_8CH_SDI(idx, rk3308->i2s_sdis[idx])); ++ } ++ } else if (rk3308->which_i2s == ACODEC_TO_I2S3_4CH) { ++ for (idx = 0; idx < rk3308->to_i2s_grps; idx++) { ++ regmap_write(rk3308->grf, GRF_SOC_CON1, ++ GRF_I2S3_4CH_SDI(idx, rk3308->i2s_sdis[idx])); ++ } ++ } else if (rk3308->which_i2s == ACODEC_TO_I2S1_2CH) { ++ regmap_write(rk3308->grf, GRF_SOC_CON1, ++ GRF_I2S1_2CH_SDI(rk3308->i2s_sdis[idx])); ++ } ++ ++ return 0; ++} ++ ++/* Put default one-to-one mapping */ ++static int rk3308_codec_adc_grps_route_default(struct rk3308_codec_priv *rk3308) ++{ ++ unsigned int idx; ++ ++ /* ++ * The GRF values may be kept the previous status after hot reboot, ++ * if the property 'rockchip,adc-grps-route' is not set, we need to ++ * recover default the order of sdi/sdo for i2s2_8ch/i2s3_8ch/i2s1_2ch. ++ */ ++ regmap_write(rk3308->grf, GRF_SOC_CON1, ++ GRF_I2S1_2CH_SDI(0)); ++ ++ for (idx = 0; idx < 2; idx++) { ++ regmap_write(rk3308->grf, GRF_SOC_CON1, ++ GRF_I2S3_4CH_SDI(idx, idx)); ++ } ++ ++ /* Using i2s2_8ch by default. */ ++ rk3308->which_i2s = ACODEC_TO_I2S2_8CH; ++ rk3308->to_i2s_grps = ADC_LR_GROUP_MAX; ++ ++ for (idx = 0; idx < ADC_LR_GROUP_MAX; idx++) { ++ rk3308->i2s_sdis[idx] = idx; ++ regmap_write(rk3308->grf, GRF_SOC_CON1, ++ GRF_I2S2_8CH_SDI(idx, idx)); ++ } ++ ++ return 0; ++} ++ ++static int rk3308_codec_adc_grps_route(struct rk3308_codec_priv *rk3308, ++ struct device_node *np) ++{ ++ int num, ret; ++ ++ num = of_count_phandle_with_args(np, "rockchip,adc-grps-route", NULL); ++ if (num < 0) { ++ if (num == -ENOENT) { ++ /* Not use 'rockchip,adc-grps-route' property here */ ++ rk3308_codec_adc_grps_route_default(rk3308); ++ ret = 0; ++ } else { ++ dev_err(rk3308->plat_dev, ++ "Failed to read 'rockchip,adc-grps-route' num: %d\n", ++ num); ++ ret = num; ++ } ++ return ret; ++ } ++ ++ ret = of_property_read_u32_array(np, "rockchip,adc-grps-route", ++ rk3308->i2s_sdis, num); ++ if (ret < 0) { ++ dev_err(rk3308->plat_dev, ++ "Failed to read 'rockchip,adc-grps-route': %d\n", ++ ret); ++ return ret; ++ } ++ ++ ret = rk3308_codec_check_i2s_sdis(rk3308, num); ++ if (ret < 0) { ++ dev_err(rk3308->plat_dev, ++ "Failed to check i2s_sdis: %d\n", ret); ++ return ret; ++ } ++ ++ rk3308->to_i2s_grps = num; ++ ++ rk3308_codec_adc_grps_route_config(rk3308); ++ ++ return 0; ++} ++ ++static int check_micbias(int micbias) ++{ ++ switch (micbias) { ++ case RK3308_ADC_MICBIAS_VOLT_0_85: ++ case RK3308_ADC_MICBIAS_VOLT_0_8: ++ case RK3308_ADC_MICBIAS_VOLT_0_75: ++ case RK3308_ADC_MICBIAS_VOLT_0_7: ++ case RK3308_ADC_MICBIAS_VOLT_0_65: ++ case RK3308_ADC_MICBIAS_VOLT_0_6: ++ case RK3308_ADC_MICBIAS_VOLT_0_55: ++ case RK3308_ADC_MICBIAS_VOLT_0_5: ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++static bool handle_loopback(struct rk3308_codec_priv *rk3308) ++{ ++ /* The version B doesn't need to handle loopback. */ ++ if (rk3308->codec_ver == ACODEC_VERSION_B) ++ return false; ++ ++ switch (rk3308->loopback_grp) { ++ case 0: ++ case 1: ++ case 2: ++ case 3: ++ return true; ++ } ++ ++ return false; ++} ++ ++static bool has_en_always_grps(struct rk3308_codec_priv *rk3308) ++{ ++ int idx; ++ ++ if (rk3308->en_always_grps_num) { ++ for (idx = 0; idx < ADC_LR_GROUP_MAX; idx++) { ++ if (rk3308->en_always_grps[idx] >= 0 && ++ rk3308->en_always_grps[idx] <= ADC_LR_GROUP_MAX - 1) ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++static int rk3308_codec_micbias_enable(struct rk3308_codec_priv *rk3308, ++ int micbias) ++{ ++ int ret; ++ ++ if (rk3308->ext_micbias != EXT_MICBIAS_NONE) ++ return 0; ++ ++ /* 0. Power up the ACODEC and keep the AVDDH stable */ ++ ++ /* Step 1. Configure ACODEC_ADC_ANA_CON7[2:0] to the certain value */ ++ ret = check_micbias(micbias); ++ if (ret < 0) { ++ dev_err(rk3308->plat_dev, "This is an invalid micbias: %d\n", ++ micbias); ++ return ret; ++ } ++ ++ /* ++ * Note: Only the reg (ADC_ANA_CON7+0x0)[2:0] represent the level range ++ * control signal of MICBIAS voltage ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(0), ++ RK3308_ADC_LEVEL_RANGE_MICBIAS_MSK, ++ micbias); ++ ++ /* Step 2. Wait until the VCMH keep stable */ ++ msleep(20); /* estimated value */ ++ ++ /* ++ * Step 3. Configure ACODEC_ADC_ANA_CON8[4] to 0x1 ++ * ++ * Note: Only the reg (ADC_ANA_CON8+0x0)[4] represent the enable ++ * signal of current source for MICBIAS ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON08(0), ++ RK3308_ADC_MICBIAS_CURRENT_MSK, ++ RK3308_ADC_MICBIAS_CURRENT_EN); ++ ++ /* ++ * Step 4. Configure the (ADC_ANA_CON7+0x40)[3] or ++ * (ADC_ANA_CON7+0x80)[3] to 0x1. ++ * ++ * (ADC_ANA_CON7+0x40)[3] used to control the MICBIAS1, and ++ * (ADC_ANA_CON7+0x80)[3] used to control the MICBIAS2 ++ */ ++ if (rk3308->micbias1) ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(1), ++ RK3308_ADC_MIC_BIAS_BUF_EN, ++ RK3308_ADC_MIC_BIAS_BUF_EN); ++ ++ if (rk3308->micbias2) ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(2), ++ RK3308_ADC_MIC_BIAS_BUF_EN, ++ RK3308_ADC_MIC_BIAS_BUF_EN); ++ ++ /* waiting micbias stabled*/ ++ mdelay(50); ++ ++ rk3308->enable_micbias = true; ++ ++ return 0; ++} ++ ++static int rk3308_codec_micbias_disable(struct rk3308_codec_priv *rk3308) ++{ ++ if (rk3308->ext_micbias != EXT_MICBIAS_NONE) ++ return 0; ++ ++ /* Step 0. Enable the MICBIAS and keep the Audio Codec stable */ ++ /* Do nothing */ ++ ++ /* ++ * Step 1. Configure the (ADC_ANA_CON7+0x40)[3] or ++ * (ADC_ANA_CON7+0x80)[3] to 0x0 ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(1), ++ RK3308_ADC_MIC_BIAS_BUF_EN, ++ RK3308_ADC_MIC_BIAS_BUF_DIS); ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(2), ++ RK3308_ADC_MIC_BIAS_BUF_EN, ++ RK3308_ADC_MIC_BIAS_BUF_DIS); ++ ++ /* ++ * Step 2. Configure ACODEC_ADC_ANA_CON8[4] to 0x0 ++ * ++ * Note: Only the reg (ADC_ANA_CON8+0x0)[4] represent the enable ++ * signal of current source for MICBIAS ++ */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON08(0), ++ RK3308_ADC_MICBIAS_CURRENT_MSK, ++ RK3308_ADC_MICBIAS_CURRENT_DIS); ++ ++ rk3308->enable_micbias = false; ++ ++ return 0; ++} ++ ++static int rk3308_codec_adc_reinit_mics(struct rk3308_codec_priv *rk3308, ++ int type) ++{ ++ int idx, grp; ++ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ /* vendor step 1 */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(grp), ++ RK3308_ADC_CH1_ADC_WORK | ++ RK3308_ADC_CH2_ADC_WORK, ++ RK3308_ADC_CH1_ADC_INIT | ++ RK3308_ADC_CH2_ADC_INIT); ++ } ++ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ /* vendor step 2 */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(grp), ++ RK3308_ADC_CH1_ALC_WORK | ++ RK3308_ADC_CH2_ALC_WORK, ++ RK3308_ADC_CH1_ALC_INIT | ++ RK3308_ADC_CH2_ALC_INIT); ++ } ++ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ /* vendor step 3 */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(grp), ++ RK3308_ADC_CH1_MIC_WORK | ++ RK3308_ADC_CH2_MIC_WORK, ++ RK3308_ADC_CH1_MIC_INIT | ++ RK3308_ADC_CH2_MIC_INIT); ++ } ++ ++ usleep_range(200, 250); /* estimated value */ ++ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ /* vendor step 1 */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(grp), ++ RK3308_ADC_CH1_ADC_WORK | ++ RK3308_ADC_CH2_ADC_WORK, ++ RK3308_ADC_CH1_ADC_WORK | ++ RK3308_ADC_CH2_ADC_WORK); ++ } ++ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ /* vendor step 2 */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(grp), ++ RK3308_ADC_CH1_ALC_WORK | ++ RK3308_ADC_CH2_ALC_WORK, ++ RK3308_ADC_CH1_ALC_WORK | ++ RK3308_ADC_CH2_ALC_WORK); ++ } ++ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ /* vendor step 3 */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(grp), ++ RK3308_ADC_CH1_MIC_WORK | ++ RK3308_ADC_CH2_MIC_WORK, ++ RK3308_ADC_CH1_MIC_WORK | ++ RK3308_ADC_CH2_MIC_WORK); ++ } ++ ++ return 0; ++} ++ ++static int rk3308_codec_adc_ana_enable(struct rk3308_codec_priv *rk3308, ++ int type) ++{ ++ unsigned int agc_func_en; ++ int idx, grp; ++ ++ /* ++ * 1. Set the ACODEC_ADC_ANA_CON7[7:6] and ACODEC_ADC_ANA_CON7[5:4], ++ * to select the line-in or microphone as input of ADC ++ * ++ * Note1. Please ignore the step1 for enabling ADC3, ADC4, ADC5, ++ * ADC6, ADC7, and ADC8 ++ */ ++ if (rk3308->adc_grp0_using_linein) { ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(0), ++ RK3308_ADC_CH1_IN_SEL_MSK | ++ RK3308_ADC_CH2_IN_SEL_MSK, ++ RK3308_ADC_CH1_IN_LINEIN | ++ RK3308_ADC_CH2_IN_LINEIN); ++ ++ /* Keep other ADCs as MIC-IN */ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ /* The groups without line-in are >= 1 */ ++ if (grp < 1 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ regmap_update_bits(rk3308->regmap, ++ RK3308_ADC_ANA_CON07(grp), ++ RK3308_ADC_CH1_IN_SEL_MSK | ++ RK3308_ADC_CH2_IN_SEL_MSK, ++ RK3308_ADC_CH1_IN_MIC | ++ RK3308_ADC_CH2_IN_MIC); ++ } ++ } else { ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ regmap_update_bits(rk3308->regmap, ++ RK3308_ADC_ANA_CON07(grp), ++ RK3308_ADC_CH1_IN_SEL_MSK | ++ RK3308_ADC_CH2_IN_SEL_MSK, ++ RK3308_ADC_CH1_IN_MIC | ++ RK3308_ADC_CH2_IN_MIC); ++ } ++ } ++ ++ /* ++ * 2. Set ACODEC_ADC_ANA_CON0[7] and [3] to 0x1, to end the mute station ++ * of ADC, to enable the MIC module, to enable the reference voltage ++ * buffer, and to end the initialization of MIC ++ */ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(grp), ++ RK3308_ADC_CH1_MIC_UNMUTE | ++ RK3308_ADC_CH2_MIC_UNMUTE, ++ RK3308_ADC_CH1_MIC_UNMUTE | ++ RK3308_ADC_CH2_MIC_UNMUTE); ++ } ++ ++ /* ++ * 3. Set ACODEC_ADC_ANA_CON6[0] to 0x1, to enable the current source ++ * of audio ++ */ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON06(grp), ++ RK3308_ADC_CURRENT_MSK, ++ RK3308_ADC_CURRENT_EN); ++ } ++ ++ /* ++ * This is mainly used for BIST mode that wait ADCs are stable. ++ * ++ * By tested results, the type delay is >40us, but we need to leave ++ * enough delay margin. ++ */ ++ usleep_range(400, 500); ++ ++ /* vendor step 4*/ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(grp), ++ RK3308_ADC_CH1_BUF_REF_EN | ++ RK3308_ADC_CH2_BUF_REF_EN, ++ RK3308_ADC_CH1_BUF_REF_EN | ++ RK3308_ADC_CH2_BUF_REF_EN); ++ } ++ ++ /* vendor step 5 */ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(grp), ++ RK3308_ADC_CH1_MIC_EN | ++ RK3308_ADC_CH2_MIC_EN, ++ RK3308_ADC_CH1_MIC_EN | ++ RK3308_ADC_CH2_MIC_EN); ++ } ++ ++ /* vendor step 6 */ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(grp), ++ RK3308_ADC_CH1_ALC_EN | ++ RK3308_ADC_CH2_ALC_EN, ++ RK3308_ADC_CH1_ALC_EN | ++ RK3308_ADC_CH2_ALC_EN); ++ } ++ ++ /* vendor step 7 */ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(grp), ++ RK3308_ADC_CH1_CLK_EN | ++ RK3308_ADC_CH2_CLK_EN, ++ RK3308_ADC_CH1_CLK_EN | ++ RK3308_ADC_CH2_CLK_EN); ++ } ++ ++ /* vendor step 8 */ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(grp), ++ RK3308_ADC_CH1_ADC_EN | ++ RK3308_ADC_CH2_ADC_EN, ++ RK3308_ADC_CH1_ADC_EN | ++ RK3308_ADC_CH2_ADC_EN); ++ } ++ ++ /* vendor step 9 */ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(grp), ++ RK3308_ADC_CH1_ADC_WORK | ++ RK3308_ADC_CH2_ADC_WORK, ++ RK3308_ADC_CH1_ADC_WORK | ++ RK3308_ADC_CH2_ADC_WORK); ++ } ++ ++ /* vendor step 10 */ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(grp), ++ RK3308_ADC_CH1_ALC_WORK | ++ RK3308_ADC_CH2_ALC_WORK, ++ RK3308_ADC_CH1_ALC_WORK | ++ RK3308_ADC_CH2_ALC_WORK); ++ } ++ ++ /* vendor step 11 */ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(grp), ++ RK3308_ADC_CH1_MIC_WORK | ++ RK3308_ADC_CH2_MIC_WORK, ++ RK3308_ADC_CH1_MIC_WORK | ++ RK3308_ADC_CH2_MIC_WORK); ++ } ++ ++ /* vendor step 12 */ ++ ++ /* vendor step 13 */ ++ ++ /* vendor step 14 */ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ regmap_read(rk3308->regmap, RK3308_ALC_L_DIG_CON09(grp), ++ &agc_func_en); ++ if (rk3308->adc_zerocross || ++ agc_func_en & RK3308_AGC_FUNC_SEL_EN) { ++ regmap_update_bits(rk3308->regmap, ++ RK3308_ADC_ANA_CON02(grp), ++ RK3308_ADC_CH1_ZEROCROSS_DET_EN, ++ RK3308_ADC_CH1_ZEROCROSS_DET_EN); ++ } ++ regmap_read(rk3308->regmap, RK3308_ALC_R_DIG_CON09(grp), ++ &agc_func_en); ++ if (rk3308->adc_zerocross || ++ agc_func_en & RK3308_AGC_FUNC_SEL_EN) { ++ regmap_update_bits(rk3308->regmap, ++ RK3308_ADC_ANA_CON02(grp), ++ RK3308_ADC_CH2_ZEROCROSS_DET_EN, ++ RK3308_ADC_CH2_ZEROCROSS_DET_EN); ++ } ++ } ++ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ rk3308->adc_grps_endisable[grp] = true; ++ } ++ ++ return 0; ++} ++ ++static int rk3308_codec_adc_ana_disable(struct rk3308_codec_priv *rk3308, ++ int type) ++{ ++ int idx, grp; ++ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ /* vendor step 1 */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(grp), ++ RK3308_ADC_CH1_ZEROCROSS_DET_EN | ++ RK3308_ADC_CH2_ZEROCROSS_DET_EN, ++ RK3308_ADC_CH1_ZEROCROSS_DET_DIS | ++ RK3308_ADC_CH2_ZEROCROSS_DET_DIS); ++ } ++ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ /* vendor step 2 */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(grp), ++ RK3308_ADC_CH1_ADC_EN | ++ RK3308_ADC_CH2_ADC_EN, ++ RK3308_ADC_CH1_ADC_DIS | ++ RK3308_ADC_CH2_ADC_DIS); ++ } ++ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ /* vendor step 3 */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(grp), ++ RK3308_ADC_CH1_CLK_EN | ++ RK3308_ADC_CH2_CLK_EN, ++ RK3308_ADC_CH1_CLK_DIS | ++ RK3308_ADC_CH2_CLK_DIS); ++ } ++ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ /* vendor step 4 */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(grp), ++ RK3308_ADC_CH1_ALC_EN | ++ RK3308_ADC_CH2_ALC_EN, ++ RK3308_ADC_CH1_ALC_DIS | ++ RK3308_ADC_CH2_ALC_DIS); ++ } ++ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ /* vendor step 5 */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(grp), ++ RK3308_ADC_CH1_MIC_EN | ++ RK3308_ADC_CH2_MIC_EN, ++ RK3308_ADC_CH1_MIC_DIS | ++ RK3308_ADC_CH2_MIC_DIS); ++ } ++ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ /* vendor step 6 */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(grp), ++ RK3308_ADC_CH1_BUF_REF_EN | ++ RK3308_ADC_CH2_BUF_REF_EN, ++ RK3308_ADC_CH1_BUF_REF_DIS | ++ RK3308_ADC_CH2_BUF_REF_DIS); ++ } ++ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ /* vendor step 7 */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON06(grp), ++ RK3308_ADC_CURRENT_MSK, ++ RK3308_ADC_CURRENT_DIS); ++ } ++ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ /* vendor step 8 */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(grp), ++ RK3308_ADC_CH1_ADC_WORK | ++ RK3308_ADC_CH2_ADC_WORK, ++ RK3308_ADC_CH1_ADC_INIT | ++ RK3308_ADC_CH2_ADC_INIT); ++ } ++ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ /* vendor step 9 */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(grp), ++ RK3308_ADC_CH1_ALC_WORK | ++ RK3308_ADC_CH2_ALC_WORK, ++ RK3308_ADC_CH1_ALC_INIT | ++ RK3308_ADC_CH2_ALC_INIT); ++ } ++ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ /* vendor step 10 */ ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(grp), ++ RK3308_ADC_CH1_MIC_WORK | ++ RK3308_ADC_CH2_MIC_WORK, ++ RK3308_ADC_CH1_MIC_INIT | ++ RK3308_ADC_CH2_MIC_INIT); ++ } ++ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ rk3308->adc_grps_endisable[grp] = false; ++ } ++ ++ return 0; ++} ++ ++static int rk3308_codec_open_capture(struct rk3308_codec_priv *rk3308) ++{ ++ int idx, grp = 0; ++ int type = ADC_TYPE_NORMAL; ++ ++ rk3308_codec_adc_ana_enable(rk3308, type); ++ rk3308_codec_adc_reinit_mics(rk3308, type); ++ ++ if (rk3308->adc_grp0_using_linein) { ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON03(0), ++ RK3308_ADC_L_CH_BIST_MSK, ++ RK3308_ADC_L_CH_NORMAL_RIGHT); ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON03(0), ++ RK3308_ADC_R_CH_BIST_MSK, ++ RK3308_ADC_R_CH_NORMAL_LEFT); ++ } else { ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (handle_loopback(rk3308) && ++ idx == rk3308->loopback_grp && ++ grp == ADC_GRP_SKIP_MAGIC) { ++ /* ++ * Switch to dummy BIST mode (BIST keep reset ++ * now) to keep the zero input data in I2S bus. ++ * ++ * It may cause the glitch if we hold the ADC ++ * digtital i2s module in codec. ++ * ++ * Then, the grp which is set from loopback_grp. ++ */ ++ regmap_update_bits(rk3308->regmap, ++ RK3308_ADC_DIG_CON03(rk3308->loopback_grp), ++ RK3308_ADC_L_CH_BIST_MSK, ++ RK3308_ADC_L_CH_BIST_SINE); ++ regmap_update_bits(rk3308->regmap, ++ RK3308_ADC_DIG_CON03(rk3308->loopback_grp), ++ RK3308_ADC_R_CH_BIST_MSK, ++ RK3308_ADC_R_CH_BIST_SINE); ++ } else { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ regmap_update_bits(rk3308->regmap, ++ RK3308_ADC_DIG_CON03(grp), ++ RK3308_ADC_L_CH_BIST_MSK, ++ RK3308_ADC_L_CH_NORMAL_LEFT); ++ regmap_update_bits(rk3308->regmap, ++ RK3308_ADC_DIG_CON03(grp), ++ RK3308_ADC_R_CH_BIST_MSK, ++ RK3308_ADC_R_CH_NORMAL_RIGHT); ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++static void rk3308_codec_adc_mclk_disable(struct rk3308_codec_priv *rk3308) ++{ ++ regmap_update_bits(rk3308->regmap, RK3308_GLB_CON, ++ RK3308_ADC_MCLK_MSK, ++ RK3308_ADC_MCLK_DIS); ++} ++ ++static void rk3308_codec_adc_mclk_enable(struct rk3308_codec_priv *rk3308) ++{ ++ regmap_update_bits(rk3308->regmap, RK3308_GLB_CON, ++ RK3308_ADC_MCLK_MSK, ++ RK3308_ADC_MCLK_EN); ++ udelay(20); ++} ++ ++static void rk3308_codec_dac_mclk_disable(struct rk3308_codec_priv *rk3308) ++{ ++ regmap_update_bits(rk3308->regmap, RK3308_GLB_CON, ++ RK3308_DAC_MCLK_MSK, ++ RK3308_DAC_MCLK_DIS); ++} ++ ++static void rk3308_codec_dac_mclk_enable(struct rk3308_codec_priv *rk3308) ++{ ++ regmap_update_bits(rk3308->regmap, RK3308_GLB_CON, ++ RK3308_DAC_MCLK_MSK, ++ RK3308_DAC_MCLK_EN); ++ udelay(20); ++} ++ ++static int rk3308_codec_open_dbg_capture(struct rk3308_codec_priv *rk3308) ++{ ++ rk3308_codec_adc_ana_enable(rk3308, ADC_TYPE_DBG); ++ ++ return 0; ++} ++ ++static int rk3308_codec_close_dbg_capture(struct rk3308_codec_priv *rk3308) ++{ ++ rk3308_codec_adc_ana_disable(rk3308, ADC_TYPE_DBG); ++ ++ return 0; ++} ++ ++static int rk3308_codec_close_all_capture(struct rk3308_codec_priv *rk3308) ++{ ++ rk3308_codec_adc_ana_disable(rk3308, ADC_TYPE_ALL); ++ ++ return 0; ++} ++ ++static int rk3308_codec_close_capture(struct rk3308_codec_priv *rk3308) ++{ ++ rk3308_codec_adc_ana_disable(rk3308, ADC_TYPE_NORMAL); ++ ++ return 0; ++} ++ ++static int rk3308_codec_open_playback(struct rk3308_codec_priv *rk3308) ++{ ++ rk3308_codec_dac_enable(rk3308); ++ ++ return 0; ++} ++ ++static int rk3308_codec_close_playback(struct rk3308_codec_priv *rk3308) ++{ ++ rk3308_codec_dac_disable(rk3308); ++ ++ return 0; ++} ++ ++static int rk3308_codec_llp_down(struct rk3308_codec_priv *rk3308) ++{ ++ rk3308_codec_adc_mclk_disable(rk3308); ++ rk3308_codec_dac_mclk_disable(rk3308); ++ ++ return 0; ++} ++ ++static int rk3308_codec_llp_up(struct rk3308_codec_priv *rk3308) ++{ ++ rk3308_codec_adc_mclk_enable(rk3308); ++ rk3308_codec_dac_mclk_enable(rk3308); ++ ++ return 0; ++} ++ ++static int rk3308_codec_dlp_down(struct rk3308_codec_priv *rk3308) ++{ ++ rk3308_codec_micbias_disable(rk3308); ++ rk3308_codec_power_off(rk3308); ++ ++ return 0; ++} ++ ++static int rk3308_codec_dlp_up(struct rk3308_codec_priv *rk3308) ++{ ++ rk3308_codec_power_on(rk3308); ++ rk3308_codec_micbias_enable(rk3308, rk3308->micbias_volt); ++ ++ return 0; ++} ++ ++/* Just used for debug and trace power state */ ++static void rk3308_codec_set_pm_state(struct rk3308_codec_priv *rk3308, ++ int pm_state) ++{ ++ int ret; ++ ++ switch (pm_state) { ++ case PM_LLP_DOWN: ++ rk3308_codec_llp_down(rk3308); ++ break; ++ case PM_LLP_UP: ++ rk3308_codec_llp_up(rk3308); ++ break; ++ case PM_DLP_DOWN: ++ rk3308_codec_dlp_down(rk3308); ++ break; ++ case PM_DLP_UP: ++ rk3308_codec_dlp_up(rk3308); ++ break; ++ case PM_DLP_DOWN2: ++ clk_disable_unprepare(rk3308->mclk_rx); ++ clk_disable_unprepare(rk3308->mclk_tx); ++ clk_disable_unprepare(rk3308->pclk); ++ break; ++ case PM_DLP_UP2: ++ ret = clk_prepare_enable(rk3308->pclk); ++ if (ret < 0) { ++ dev_err(rk3308->plat_dev, ++ "Failed to enable acodec pclk: %d\n", ret); ++ goto err; ++ } ++ ++ ret = clk_prepare_enable(rk3308->mclk_rx); ++ if (ret < 0) { ++ dev_err(rk3308->plat_dev, ++ "Failed to enable i2s mclk_rx: %d\n", ret); ++ goto err; ++ } ++ ++ ret = clk_prepare_enable(rk3308->mclk_tx); ++ if (ret < 0) { ++ dev_err(rk3308->plat_dev, ++ "Failed to enable i2s mclk_tx: %d\n", ret); ++ goto err; ++ } ++ break; ++ default: ++ dev_err(rk3308->plat_dev, "Invalid pm_state: %d\n", pm_state); ++ goto err; ++ } ++ ++ rk3308->pm_state = pm_state; ++ ++err: ++ return; ++} ++ ++static void rk3308_codec_update_adcs_status(struct rk3308_codec_priv *rk3308, ++ int state) ++{ ++ int idx, grp; ++ ++ /* Update skip_grps flags if the ADCs need to be enabled always. */ ++ if (state == PATH_BUSY) { ++ for (idx = 0; idx < rk3308->used_adc_grps; idx++) { ++ u32 mapped_grp = to_mapped_grp(rk3308, idx); ++ ++ for (grp = 0; grp < rk3308->en_always_grps_num; grp++) { ++ u32 en_always_grp = rk3308->en_always_grps[grp]; ++ ++ if (mapped_grp == en_always_grp) ++ rk3308->skip_grps[en_always_grp] = 1; ++ } ++ } ++ } ++} ++ ++static int rk3308_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_codec *codec = dai->codec; ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct snd_pcm_str *playback_str = ++ &substream->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]; ++ int type = ADC_TYPE_LOOPBACK; ++ int idx, grp; ++ int ret; ++ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ /* DAC only supports 2 channels */ ++ rk3308_codec_dac_mclk_enable(rk3308); ++ rk3308_codec_open_playback(rk3308); ++ rk3308_codec_dac_dig_config(rk3308, params); ++ rk3308_codec_set_dac_path_state(rk3308, PATH_BUSY); ++ } else { ++ if (rk3308->micbias_num && ++ !rk3308->enable_micbias) ++ rk3308_codec_micbias_enable(rk3308, rk3308->micbias_volt); ++ ++ rk3308_codec_adc_mclk_enable(rk3308); ++ ret = rk3308_codec_update_adc_grps(rk3308, params); ++ if (ret < 0) ++ return ret; ++ ++ if (handle_loopback(rk3308)) { ++ if (rk3308->micbias_num && ++ (params_channels(params) == 2) && ++ to_mapped_grp(rk3308, 0) == rk3308->loopback_grp) ++ rk3308_codec_micbias_disable(rk3308); ++ ++ /* Check the DACs are opened */ ++ if (playback_str->substream_opened) { ++ rk3308->loopback_dacs_enabled = true; ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ regmap_update_bits(rk3308->regmap, ++ RK3308_ADC_DIG_CON03(grp), ++ RK3308_ADC_L_CH_BIST_MSK, ++ RK3308_ADC_L_CH_NORMAL_LEFT); ++ regmap_update_bits(rk3308->regmap, ++ RK3308_ADC_DIG_CON03(grp), ++ RK3308_ADC_R_CH_BIST_MSK, ++ RK3308_ADC_R_CH_NORMAL_RIGHT); ++ } ++ } else { ++ rk3308->loopback_dacs_enabled = false; ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ regmap_update_bits(rk3308->regmap, ++ RK3308_ADC_DIG_CON03(grp), ++ RK3308_ADC_L_CH_BIST_MSK, ++ RK3308_ADC_L_CH_BIST_SINE); ++ regmap_update_bits(rk3308->regmap, ++ RK3308_ADC_DIG_CON03(grp), ++ RK3308_ADC_R_CH_BIST_MSK, ++ RK3308_ADC_R_CH_BIST_SINE); ++ } ++ } ++ } ++ ++ rk3308_codec_open_capture(rk3308); ++ rk3308_codec_adc_dig_config(rk3308, params); ++ rk3308_codec_update_adcs_status(rk3308, PATH_BUSY); ++ } ++ ++ return 0; ++} ++ ++static int rk3308_pcm_trigger(struct snd_pcm_substream *substream, ++ int cmd, struct snd_soc_dai *dai) ++{ ++ struct snd_soc_codec *codec = dai->codec; ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ int type = ADC_TYPE_LOOPBACK; ++ int idx, grp; ++ ++ if (handle_loopback(rk3308) && ++ rk3308->dac_output == DAC_LINEOUT && ++ substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ if (cmd == SNDRV_PCM_TRIGGER_START) { ++ struct snd_pcm_str *capture_str = ++ &substream->pcm->streams[SNDRV_PCM_STREAM_CAPTURE]; ++ ++ if (capture_str->substream_opened) ++ queue_delayed_work(system_power_efficient_wq, ++ &rk3308->loopback_work, ++ msecs_to_jiffies(rk3308->delay_loopback_handle_ms)); ++ } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { ++ /* ++ * Switch to dummy bist mode to kick the glitch during disable ++ * ADCs and keep zero input data ++ */ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ regmap_update_bits(rk3308->regmap, ++ RK3308_ADC_DIG_CON03(grp), ++ RK3308_ADC_L_CH_BIST_MSK, ++ RK3308_ADC_L_CH_BIST_SINE); ++ regmap_update_bits(rk3308->regmap, ++ RK3308_ADC_DIG_CON03(grp), ++ RK3308_ADC_R_CH_BIST_MSK, ++ RK3308_ADC_R_CH_BIST_SINE); ++ } ++ rk3308_codec_adc_ana_disable(rk3308, ADC_TYPE_LOOPBACK); ++ } ++ } ++ ++ return 0; ++} ++ ++static void rk3308_pcm_shutdown(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_codec *codec = dai->codec; ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ rk3308_codec_close_playback(rk3308); ++ rk3308_codec_dac_mclk_disable(rk3308); ++ regcache_cache_only(rk3308->regmap, false); ++ regcache_sync(rk3308->regmap); ++ rk3308_codec_set_dac_path_state(rk3308, PATH_IDLE); ++ } else { ++ rk3308_codec_close_capture(rk3308); ++ if (!has_en_always_grps(rk3308)) { ++ rk3308_codec_adc_mclk_disable(rk3308); ++ rk3308_codec_update_adcs_status(rk3308, PATH_IDLE); ++ if (rk3308->micbias_num && ++ rk3308->enable_micbias) ++ rk3308_codec_micbias_disable(rk3308); ++ } ++ ++ regcache_cache_only(rk3308->regmap, false); ++ regcache_sync(rk3308->regmap); ++ } ++} ++ ++static struct snd_soc_dai_ops rk3308_dai_ops = { ++ .hw_params = rk3308_hw_params, ++ .set_fmt = rk3308_set_dai_fmt, ++ .mute_stream = rk3308_mute_stream, ++ .trigger = rk3308_pcm_trigger, ++ .shutdown = rk3308_pcm_shutdown, ++}; ++ ++static struct snd_soc_dai_driver rk3308_dai[] = { ++ { ++ .name = "rk3308-hifi", ++ .id = RK3308_HIFI, ++ .playback = { ++ .stream_name = "HiFi Playback", ++ .channels_min = 2, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_8000_192000, ++ .formats = (SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_S20_3LE | ++ SNDRV_PCM_FMTBIT_S24_LE | ++ SNDRV_PCM_FMTBIT_S32_LE), ++ }, ++ .capture = { ++ .stream_name = "HiFi Capture", ++ .channels_min = 1, ++ .channels_max = 8, ++ .rates = SNDRV_PCM_RATE_8000_192000, ++ .formats = (SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_S20_3LE | ++ SNDRV_PCM_FMTBIT_S24_LE | ++ SNDRV_PCM_FMTBIT_S32_LE), ++ }, ++ .ops = &rk3308_dai_ops, ++ }, ++}; ++ ++static int rk3308_suspend(struct snd_soc_codec *codec) ++{ ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ ++ if (rk3308->no_deep_low_power) ++ goto out; ++ ++ rk3308_codec_dlp_down(rk3308); ++ clk_disable_unprepare(rk3308->mclk_rx); ++ clk_disable_unprepare(rk3308->mclk_tx); ++ clk_disable_unprepare(rk3308->pclk); ++ ++out: ++ rk3308_set_bias_level(codec, SND_SOC_BIAS_OFF); ++ return 0; ++} ++ ++static int rk3308_resume(struct snd_soc_codec *codec) ++{ ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ int ret = 0; ++ ++ if (rk3308->no_deep_low_power) ++ goto out; ++ ++ ret = clk_prepare_enable(rk3308->pclk); ++ if (ret < 0) { ++ dev_err(rk3308->plat_dev, ++ "Failed to enable acodec pclk: %d\n", ret); ++ goto out; ++ } ++ ++ ret = clk_prepare_enable(rk3308->mclk_rx); ++ if (ret < 0) { ++ dev_err(rk3308->plat_dev, ++ "Failed to enable i2s mclk_rx: %d\n", ret); ++ goto out; ++ } ++ ++ ret = clk_prepare_enable(rk3308->mclk_tx); ++ if (ret < 0) { ++ dev_err(rk3308->plat_dev, ++ "Failed to enable i2s mclk_tx: %d\n", ret); ++ goto out; ++ } ++ ++ rk3308_codec_dlp_up(rk3308); ++out: ++ rk3308_set_bias_level(codec, SND_SOC_BIAS_STANDBY); ++ return ret; ++} ++ ++static int rk3308_codec_default_gains(struct rk3308_codec_priv *rk3308) ++{ ++ int grp; ++ ++ /* Prepare ADC gains */ ++ /* vendor step 12, set MIC PGA default gains */ ++ for (grp = 0; grp < ADC_LR_GROUP_MAX; grp++) { ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON01(grp), ++ RK3308_ADC_CH1_MIC_GAIN_MSK | ++ RK3308_ADC_CH2_MIC_GAIN_MSK, ++ RK3308_ADC_CH1_MIC_GAIN_0DB | ++ RK3308_ADC_CH2_MIC_GAIN_0DB); ++ } ++ ++ /* vendor step 13, set ALC default gains */ ++ for (grp = 0; grp < ADC_LR_GROUP_MAX; grp++) { ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON03(grp), ++ RK3308_ADC_CH1_ALC_GAIN_MSK, ++ RK3308_ADC_CH1_ALC_GAIN_0DB); ++ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON04(grp), ++ RK3308_ADC_CH2_ALC_GAIN_MSK, ++ RK3308_ADC_CH2_ALC_GAIN_0DB); ++ } ++ ++ /* Prepare DAC gains */ ++ /* Step 15, set HPMIX default gains */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON12, ++ RK3308_DAC_L_HPMIX_GAIN_MSK | ++ RK3308_DAC_R_HPMIX_GAIN_MSK, ++ RK3308_DAC_L_HPMIX_GAIN_NDB_6 | ++ RK3308_DAC_R_HPMIX_GAIN_NDB_6); ++ ++ /* Step 18, set HPOUT default gains */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON05, ++ RK3308_DAC_L_HPOUT_GAIN_MSK, ++ RK3308_DAC_L_HPOUT_GAIN_NDB_39); ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON06, ++ RK3308_DAC_R_HPOUT_GAIN_MSK, ++ RK3308_DAC_R_HPOUT_GAIN_NDB_39); ++ ++ /* Using the same gain to HPOUT LR channels */ ++ rk3308->hpout_l_dgain = RK3308_DAC_L_HPOUT_GAIN_NDB_39; ++ ++ /* Step 19, set LINEOUT default gains */ ++ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, ++ RK3308_DAC_L_LINEOUT_GAIN_MSK | ++ RK3308_DAC_R_LINEOUT_GAIN_MSK, ++ RK3308_DAC_L_LINEOUT_GAIN_NDB_6 | ++ RK3308_DAC_R_LINEOUT_GAIN_NDB_6); ++ ++ return 0; ++} ++ ++static int rk3308_codec_setup_en_always_adcs(struct rk3308_codec_priv *rk3308, ++ struct device_node *np) ++{ ++ int num, ret; ++ ++ num = of_count_phandle_with_args(np, "rockchip,en-always-grps", NULL); ++ if (num < 0) { ++ if (num == -ENOENT) { ++ /* ++ * If there is note use 'rockchip,en-always-grps' ++ * property, return 0 is also right. ++ */ ++ ret = 0; ++ } else { ++ dev_err(rk3308->plat_dev, ++ "Failed to read 'rockchip,adc-grps-route' num: %d\n", ++ num); ++ ret = num; ++ } ++ ++ rk3308->en_always_grps_num = 0; ++ return ret; ++ } ++ ++ rk3308->en_always_grps_num = num; ++ ++ ret = of_property_read_u32_array(np, "rockchip,en-always-grps", ++ rk3308->en_always_grps, num); ++ if (ret < 0) { ++ dev_err(rk3308->plat_dev, ++ "Failed to read 'rockchip,en-always-grps': %d\n", ++ ret); ++ return ret; ++ } ++ ++ /* Clear all of skip_grps flags. */ ++ for (num = 0; num < ADC_LR_GROUP_MAX; num++) ++ rk3308->skip_grps[num] = 0; ++ ++ /* The loopback grp should not be enabled always. */ ++ for (num = 0; num < rk3308->en_always_grps_num; num++) { ++ if (rk3308->en_always_grps[num] == rk3308->loopback_grp) { ++ dev_err(rk3308->plat_dev, ++ "loopback_grp: %d should not be enabled always!\n", ++ rk3308->loopback_grp); ++ ret = -EINVAL; ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static int rk3308_codec_dapm_mic_gains(struct rk3308_codec_priv *rk3308) ++{ ++ int ret; ++ ++ if (rk3308->codec_ver == ACODEC_VERSION_B) { ++ ret = snd_soc_add_codec_controls(rk3308->codec, ++ mic_gains_b, ++ ARRAY_SIZE(mic_gains_b)); ++ if (ret) { ++ dev_err(rk3308->plat_dev, ++ "%s: add mic_gains_b failed: %d\n", ++ __func__, ret); ++ return ret; ++ } ++ } else { ++ ret = snd_soc_add_codec_controls(rk3308->codec, ++ mic_gains_a, ++ ARRAY_SIZE(mic_gains_a)); ++ if (ret) { ++ dev_err(rk3308->plat_dev, ++ "%s: add mic_gains_a failed: %d\n", ++ __func__, ret); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static int rk3308_codec_check_micbias(struct rk3308_codec_priv *rk3308, ++ struct device_node *np) ++{ ++ struct device *dev = (struct device *)rk3308->plat_dev; ++ int num = 0, ret; ++ ++ /* Check internal micbias */ ++ rk3308->micbias1 = ++ of_property_read_bool(np, "rockchip,micbias1"); ++ if (rk3308->micbias1) ++ num++; ++ ++ rk3308->micbias2 = ++ of_property_read_bool(np, "rockchip,micbias2"); ++ if (rk3308->micbias2) ++ num++; ++ ++ rk3308->micbias_volt = RK3308_ADC_MICBIAS_VOLT_0_85; /* by default */ ++ rk3308->micbias_num = num; ++ ++ /* Check external micbias */ ++ rk3308->ext_micbias = EXT_MICBIAS_NONE; ++ ++ rk3308->micbias_en_gpio = devm_gpiod_get_optional(dev, ++ "micbias-en", ++ GPIOD_IN); ++ if (!rk3308->micbias_en_gpio) { ++ dev_info(dev, "Don't need micbias-en gpio\n"); ++ } else if (IS_ERR(rk3308->micbias_en_gpio)) { ++ ret = PTR_ERR(rk3308->micbias_en_gpio); ++ dev_err(dev, "Unable to claim gpio micbias-en\n"); ++ return ret; ++ } else if (gpiod_get_value(rk3308->micbias_en_gpio)) { ++ rk3308->ext_micbias = EXT_MICBIAS_FUNC1; ++ } ++ ++ rk3308->vcc_micbias = devm_regulator_get_optional(dev, ++ "vmicbias"); ++ if (IS_ERR(rk3308->vcc_micbias)) { ++ if (PTR_ERR(rk3308->vcc_micbias) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ dev_info(dev, "no vmicbias regulator found\n"); ++ } else { ++ ret = regulator_enable(rk3308->vcc_micbias); ++ if (ret) { ++ dev_err(dev, "Can't enable vmicbias: %d\n", ret); ++ return ret; ++ } ++ rk3308->ext_micbias = EXT_MICBIAS_FUNC2; ++ } ++ ++ dev_info(dev, "Check ext_micbias: %d\n", rk3308->ext_micbias); ++ ++ return 0; ++} ++ ++static int rk3308_codec_dapm_controls_prepare(struct rk3308_codec_priv *rk3308) ++{ ++ int grp; ++ ++ for (grp = 0; grp < ADC_LR_GROUP_MAX; grp++) { ++ rk3308->hpf_cutoff[grp] = 0; ++ rk3308->agc_l[grp] = 0; ++ rk3308->agc_r[grp] = 0; ++ rk3308->agc_asr_l[grp] = AGC_ASR_96KHZ; ++ rk3308->agc_asr_r[grp] = AGC_ASR_96KHZ; ++ } ++ ++ rk3308_codec_dapm_mic_gains(rk3308); ++ ++ return 0; ++} ++ ++static int rk3308_codec_prepare(struct rk3308_codec_priv *rk3308) ++{ ++ /* Clear registers for ADC and DAC */ ++ rk3308_codec_close_playback(rk3308); ++ rk3308_codec_close_all_capture(rk3308); ++ rk3308_codec_default_gains(rk3308); ++ rk3308_codec_llp_down(rk3308); ++ rk3308_codec_dapm_controls_prepare(rk3308); ++ ++ return 0; ++} ++ ++static int rk3308_probe(struct snd_soc_codec *codec) ++{ ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ int ext_micbias; ++ ++ rk3308->codec = codec; ++ rk3308_codec_set_dac_path_state(rk3308, PATH_IDLE); ++ ++ rk3308_codec_reset(codec); ++ rk3308_codec_power_on(rk3308); ++ ++ /* From vendor recommend, disable micbias at first. */ ++ ext_micbias = rk3308->ext_micbias; ++ rk3308->ext_micbias = EXT_MICBIAS_NONE; ++ rk3308_codec_micbias_disable(rk3308); ++ rk3308->ext_micbias = ext_micbias; ++ ++ rk3308_codec_prepare(rk3308); ++ if (!rk3308->no_hp_det) ++ rk3308_codec_headset_detect_enable(rk3308); ++ ++ regcache_cache_only(rk3308->regmap, false); ++ regcache_sync(rk3308->regmap); ++ ++ return 0; ++} + +- /* 5. Wait until the voltage of VCM keeps stable at the AGND */ +- usleep_range(200, 300); /* estimated value */ ++static int rk3308_remove(struct snd_soc_codec *codec) ++{ ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ ++ rk3308_headphone_ctl(rk3308, 0); ++ rk3308_speaker_ctl(rk3308, 0); ++ if (!rk3308->no_hp_det) ++ rk3308_codec_headset_detect_disable(rk3308); ++ rk3308_codec_micbias_disable(rk3308); ++ rk3308_codec_power_off(rk3308); + +- /* 6. Power off the analog power supply */ +- /* 7. Power off the digital power supply */ ++ rk3308_codec_set_dac_path_state(rk3308, PATH_IDLE); + +- /* Do something via hardware */ ++ regcache_cache_only(rk3308->regmap, false); ++ regcache_sync(rk3308->regmap); + + return 0; + } + +-static int check_micbias(int micbias) ++static struct snd_soc_codec_driver soc_codec_dev_rk3308 = { ++ .probe = rk3308_probe, ++ .remove = rk3308_remove, ++ .suspend = rk3308_suspend, ++ .resume = rk3308_resume, ++ .set_bias_level = rk3308_set_bias_level, ++ .controls = rk3308_codec_dapm_controls, ++ .num_controls = ARRAY_SIZE(rk3308_codec_dapm_controls), ++}; ++ ++static const struct reg_default rk3308_codec_reg_defaults[] = { ++ { RK3308_GLB_CON, 0x07 }, ++}; ++ ++static bool rk3308_codec_write_read_reg(struct device *dev, unsigned int reg) + { +- switch (micbias) { +- case RK3308_ADC_MICBIAS_VOLT_0_85: +- case RK3308_ADC_MICBIAS_VOLT_0_8: +- case RK3308_ADC_MICBIAS_VOLT_0_75: +- case RK3308_ADC_MICBIAS_VOLT_0_7: +- case RK3308_ADC_MICBIAS_VOLT_0_65: +- case RK3308_ADC_MICBIAS_VOLT_0_6: +- case RK3308_ADC_MICBIAS_VOLT_0_55: +- case RK3308_ADC_MICBIAS_VOLT_0_5: +- return 0; +- } ++ /* All registers can be read / write */ ++ return true; ++} + +- return -EINVAL; ++static bool rk3308_codec_volatile_reg(struct device *dev, unsigned int reg) ++{ ++ return true; + } + +-static int rk3308_codec_micbias_enable(struct rk3308_codec_priv *rk3308, +- int micbias) ++static void rk3308_codec_hpdetect_work(struct work_struct *work) + { +- int ch = rk3308->adc_ch; +- int ret; ++ struct rk3308_codec_priv *rk3308 = ++ container_of(work, struct rk3308_codec_priv, hpdet_work.work); ++ unsigned int val; ++ int need_poll = 0, need_irq = 0; ++ int need_report = 0, report_type = 0; ++ int dac_output = DAC_LINEOUT; ++ ++ if (rk3308->codec_ver == ACODEC_VERSION_B) { ++ /* Check headphone plugged/unplugged directly. */ ++ regmap_read(rk3308->detect_grf, ++ DETECT_GRF_ACODEC_HPDET_STATUS, &val); ++ regmap_write(rk3308->detect_grf, ++ DETECT_GRF_ACODEC_HPDET_STATUS_CLR, val); ++ ++ if (rk3308->hp_jack_reversed) { ++ switch (val) { ++ case 0x0: ++ case 0x2: ++ dac_output = DAC_HPOUT; ++ report_type = SND_JACK_HEADPHONE; ++ break; ++ default: ++ break; ++ } ++ } else { ++ switch (val) { ++ case 0x1: ++ dac_output = DAC_HPOUT; ++ report_type = SND_JACK_HEADPHONE; ++ break; ++ default: ++ /* Includes val == 2 or others. */ ++ break; ++ } ++ } + +- if (ch != 1 && ch != 2) { +- dev_err(rk3308->plat_dev, +- "%s: currnet ch: %d, only ch1/2 control MICBIAS1/2\n", +- __func__, ch); +- return -EINVAL; +- } ++ rk3308_codec_dac_switch(rk3308, dac_output); ++ if (rk3308->hpdet_jack) ++ snd_soc_jack_report(rk3308->hpdet_jack, ++ report_type, ++ SND_JACK_HEADPHONE); + +- /* 1. Power up the ACODEC and keep the AVDDH stable */ ++ enable_irq(rk3308->irq); + +- /* 2. Configure ACODEC_ADC_ANA_CON7[2:0] to the certain value */ +- ret = check_micbias(micbias); +- if (ret < 0) { +- dev_err(rk3308->plat_dev, "This is an invalid micbias: %d\n", +- micbias); +- return ret; ++ return; + } + +- /* +- * Note: Only the reg (ADC_ANA_CON7+0x0)[2:0] represent the level range +- * control signal of MICBIAS voltage +- */ +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(0), +- RK3308_ADC_LEVEL_RANGE_MICBIAS_MSK, +- micbias); ++ /* Check headphone unplugged via poll. */ ++ regmap_read(rk3308->regmap, RK3308_DAC_DIG_CON14, &val); + +- /* 3. Wait until the VCMH keep stable */ +- usleep_range(200, 300); /* estimated value */ ++ if (rk3308->hp_jack_reversed) { ++ if (!val) { ++ rk3308->hp_plugged = true; ++ report_type = SND_JACK_HEADPHONE; + +- /* 4. Configure ACODEC_ADC_ANA_CON8[4] to 0x1 */ +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON08(ch), +- RK3308_ADC_MICBIAS_CURRENT_MSK, +- RK3308_ADC_MICBIAS_CURRENT_EN); ++ need_report = 1; ++ need_irq = 1; ++ } else { ++ if (rk3308->hp_plugged) { ++ rk3308->hp_plugged = false; ++ need_report = 1; ++ } ++ need_poll = 1; ++ } ++ } else { ++ if (!val) { ++ rk3308->hp_plugged = false; + +- /* +- * 5. Configure the (ADC_ANA_CON7+0x40)[3] or (ADC_ANA_CON7+0x80)[3] +- * to 0x1. +- * (ADC_ANA_CON7+0x40)[3] used to control the MICBIAS1, and +- * (ADC_ANA_CON7+0x80)[3] used to control the MICBIAS2 +- */ ++ need_report = 1; ++ need_irq = 1; ++ } else { ++ if (!rk3308->hp_plugged) { ++ rk3308->hp_plugged = true; ++ report_type = SND_JACK_HEADPHONE; ++ need_report = 1; ++ } ++ need_poll = 1; ++ } ++ } + +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(ch), +- RK3308_ADC_MIC_BIAS_BUF_EN, +- RK3308_ADC_MIC_BIAS_BUF_EN); ++ if (need_poll) ++ queue_delayed_work(system_power_efficient_wq, ++ &rk3308->hpdet_work, ++ msecs_to_jiffies(HPDET_POLL_MS)); + +- return 0; +-} ++ if (need_report) { ++ if (report_type) ++ dac_output = DAC_HPOUT; + +-static int rk3308_codec_micbias_disable(struct rk3308_codec_priv *rk3308) +-{ +- int ch = rk3308->adc_ch; ++ rk3308_codec_dac_switch(rk3308, dac_output); + +- if (ch != 1 && ch != 2) { +- dev_err(rk3308->plat_dev, +- "%s: currnet ch: %d, only ch1/2 control MICBIAS1/2\n", +- __func__, ch); +- return -EINVAL; ++ if (rk3308->hpdet_jack) ++ snd_soc_jack_report(rk3308->hpdet_jack, ++ report_type, ++ SND_JACK_HEADPHONE); + } + +- /* 1. Enable the MICBIAS and keep the Audio Codec stable */ +- /* Do nothing */ +- +- /* +- * 2. Configure the (ADC_ANA_CON7+0x40)[3] or +- * (ADC_ANA_CON7+0x80)[3] to 0x0 +- */ +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(ch), +- RK3308_ADC_MIC_BIAS_BUF_EN, +- RK3308_ADC_MIC_BIAS_BUF_DIS); +- +- /* 3. Configure ACODEC_ADC_ANA_CON8[4] to 0x0 */ +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON08(ch), +- RK3308_ADC_MICBIAS_CURRENT_MSK, +- RK3308_ADC_MICBIAS_CURRENT_DIS); ++ if (need_irq) ++ enable_irq(rk3308->irq); ++} + +- return 0; ++static void rk3308_codec_loopback_work(struct work_struct *work) ++{ ++ struct rk3308_codec_priv *rk3308 = ++ container_of(work, struct rk3308_codec_priv, loopback_work.work); ++ int type = ADC_TYPE_LOOPBACK; ++ int idx, grp; ++ ++ /* Prepare loopback ADCs */ ++ rk3308_codec_adc_ana_enable(rk3308, type); ++ ++ /* Waiting ADCs are stable */ ++ msleep(ADC_STABLE_MS); ++ ++ /* Recover normal mode after enable ADCs */ ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { ++ if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) ++ continue; ++ ++ regmap_update_bits(rk3308->regmap, ++ RK3308_ADC_DIG_CON03(grp), ++ RK3308_ADC_L_CH_BIST_MSK, ++ RK3308_ADC_L_CH_NORMAL_LEFT); ++ regmap_update_bits(rk3308->regmap, ++ RK3308_ADC_DIG_CON03(grp), ++ RK3308_ADC_R_CH_BIST_MSK, ++ RK3308_ADC_R_CH_NORMAL_RIGHT); ++ } + } + +-static int rk3308_codec_alc_enable(struct rk3308_codec_priv *rk3308) ++static irqreturn_t rk3308_codec_hpdet_isr(int irq, void *data) + { +- int ch = rk3308->adc_ch; ++ struct rk3308_codec_priv *rk3308 = data; + + /* +- * 1. Set he max level and min level of the ALC need to control. +- * +- * These values are estimated ++ * For the high level irq trigger, disable irq and avoid a lot of ++ * repeated irq handlers entry. + */ +- regmap_update_bits(rk3308->regmap, RK3308_ALC_L_DIG_CON05(ch), +- RK3308_AGC_LO_8BITS_AGC_MIN_MSK, +- 0x16); +- regmap_update_bits(rk3308->regmap, RK3308_ALC_L_DIG_CON05(ch), +- RK3308_AGC_HI_8BITS_AGC_MIN_MSK, +- 0x40); +- +- regmap_update_bits(rk3308->regmap, RK3308_ALC_R_DIG_CON05(ch), +- RK3308_AGC_LO_8BITS_AGC_MAX_MSK, +- 0x26); +- regmap_update_bits(rk3308->regmap, RK3308_ALC_R_DIG_CON05(ch), +- RK3308_AGC_HI_8BITS_AGC_MAX_MSK, +- 0x40); ++ disable_irq_nosync(rk3308->irq); ++ queue_delayed_work(system_power_efficient_wq, ++ &rk3308->hpdet_work, msecs_to_jiffies(10)); + +- /* +- * 2. Set ACODEC_ALC_DIG_CON4[2:0] according to the sample rate +- * +- * By default is 44.1KHz for sample. +- */ +- regmap_update_bits(rk3308->regmap, RK3308_ALC_L_DIG_CON04(ch), +- RK3308_AGC_APPROX_RATE_MSK, +- RK3308_AGC_APPROX_RATE_44_1K); +- regmap_update_bits(rk3308->regmap, RK3308_ALC_R_DIG_CON04(ch), +- RK3308_AGC_APPROX_RATE_MSK, +- RK3308_AGC_APPROX_RATE_44_1K); +- +- /* 3. Set ACODEC_ALC_DIG_CON9[6] to 0x1, to enable the ALC module */ +- regmap_update_bits(rk3308->regmap, RK3308_ALC_L_DIG_CON09(ch), +- RK3308_AGC_FUNC_SEL_MSK, +- RK3308_AGC_FUNC_SEL_EN); +- regmap_update_bits(rk3308->regmap, RK3308_ALC_R_DIG_CON09(ch), +- RK3308_AGC_FUNC_SEL_MSK, +- RK3308_AGC_FUNC_SEL_EN); ++ return IRQ_HANDLED; ++} + +- /* +- * 4. Set ACODEC_ADC_ANA_CON11[1:0], (ACODEC_ADC_ANA_CON11+0x40)[1:0], +- * (ACODEC_ADC_ANA_CON11+0x80)[1:0] and (ACODEC_ADC_ANA_CON11+0xc0)[1:0] +- * to 0x3, to enable the ALC module to control the gain of PGA. +- */ +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON11(ch), +- RK3308_ADC_ALCL_CON_GAIN_PGAL_MSK | +- RK3308_ADC_ALCR_CON_GAIN_PGAR_MSK, +- RK3308_ADC_ALCL_CON_GAIN_PGAL_EN | +- RK3308_ADC_ALCR_CON_GAIN_PGAR_EN); ++void (*rk3308_codec_set_jack_detect_cb)(struct snd_soc_codec *codec, ++ struct snd_soc_jack *hpdet_jack); ++EXPORT_SYMBOL_GPL(rk3308_codec_set_jack_detect_cb); + +- /* +- * 5.Observe the current ALC output gain by reading +- * ACODEC_ALC_DIG_CON12[4:0] +- * +- * The default GAIN is 0x0c +- */ +- regmap_update_bits(rk3308->regmap, RK3308_ALC_L_DIG_CON12(ch), +- RK3308_AGC_GAIN_MSK, +- 0x0c); +- regmap_update_bits(rk3308->regmap, RK3308_ALC_R_DIG_CON12(ch), +- RK3308_AGC_GAIN_MSK, +- 0x0c); ++static void rk3308_codec_set_jack_detect(struct snd_soc_codec *codec, ++ struct snd_soc_jack *hpdet_jack) ++{ ++ struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + +- return 0; +-} ++ rk3308->hpdet_jack = hpdet_jack; + +-static int rk3308_codec_alc_disable(struct rk3308_codec_priv *rk3308) +-{ +- int ch = rk3308->adc_ch; ++ /* To detect jack once during startup */ ++ disable_irq_nosync(rk3308->irq); ++ queue_delayed_work(system_power_efficient_wq, ++ &rk3308->hpdet_work, msecs_to_jiffies(10)); + +- /* +- * 1. Set ACODEC_ALC_DIG_CON9[6] to 0x0, to disable the ALC module, +- * then the ALC output gain will keep to the last value +- */ +- regmap_update_bits(rk3308->regmap, RK3308_ALC_L_DIG_CON09(ch), +- RK3308_AGC_FUNC_SEL_MSK, +- RK3308_AGC_FUNC_SEL_DIS); +- regmap_update_bits(rk3308->regmap, RK3308_ALC_R_DIG_CON09(ch), +- RK3308_AGC_FUNC_SEL_MSK, +- RK3308_AGC_FUNC_SEL_DIS); ++ dev_info(rk3308->plat_dev, "%s: Request detect hp jack once\n", ++ __func__); ++} + +- /* +- * 2. Set ACODEC_ADC_ANA_CON11[1:0], (ACODEC_ADC_ANA_CON11+0x40)[1:0], +- * (ACODEC_ADC_ANA_CON11+0x80)[1:0] and (ACODEC_ADC_ANA_CON11+0xc0)[1:0] +- * to 0x0, to disable the ALC module to control the gain of PGA. +- */ +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON11(ch), +- RK3308_ADC_ALCL_CON_GAIN_PGAL_MSK | +- RK3308_ADC_ALCR_CON_GAIN_PGAR_MSK, +- RK3308_ADC_ALCL_CON_GAIN_PGAL_DIS | +- RK3308_ADC_ALCR_CON_GAIN_PGAR_DIS); ++static const struct regmap_config rk3308_codec_regmap_config = { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .max_register = RK3308_DAC_ANA_CON15, ++ .writeable_reg = rk3308_codec_write_read_reg, ++ .readable_reg = rk3308_codec_write_read_reg, ++ .volatile_reg = rk3308_codec_volatile_reg, ++ .reg_defaults = rk3308_codec_reg_defaults, ++ .num_reg_defaults = ARRAY_SIZE(rk3308_codec_reg_defaults), ++ .cache_type = REGCACHE_FLAT, ++}; + +- return 0; ++static ssize_t pm_state_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct rk3308_codec_priv *rk3308 = ++ container_of(dev, struct rk3308_codec_priv, dev); ++ ++ return sprintf(buf, "pm_state: %d\n", rk3308->pm_state); + } + +-static int rk3308_codec_adc_ana_enable(struct rk3308_codec_priv *rk3308) ++static ssize_t pm_state_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) + { +- unsigned int adc_aif1 = 0, adc_aif2 = 0; +- unsigned int agc_func_en; +- int ch = rk3308->adc_ch; ++ struct rk3308_codec_priv *rk3308 = ++ container_of(dev, struct rk3308_codec_priv, dev); ++ unsigned long pm_state; ++ int ret = kstrtoul(buf, 10, &pm_state); + +- /* +- * 1. Set the ACODEC_ADC_ANA_CON7[7:6] and ACODEC_ADC_ANA_CON7[5:4], +- * to select the line-in or microphone as input of ADC +- * +- * Note1. Please ignore the step1 for enabling ADC3, ADC4, ADC5, +- * ADC6, ADC7, and ADC8 +- */ +- if (ch == 0) { +- if (rk3308->adc_ch0_using_linein) { +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(ch), +- RK3308_ADC_CH1_IN_SEL_MSK, +- RK3308_ADC_CH1_IN_LINEIN); +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(ch), +- RK3308_ADC_CH2_IN_SEL_MSK, +- RK3308_ADC_CH2_IN_LINEIN); +- } else { +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(ch), +- RK3308_ADC_CH1_IN_SEL_MSK, +- RK3308_ADC_CH1_IN_MIC); +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(ch), +- RK3308_ADC_CH2_IN_SEL_MSK, +- RK3308_ADC_CH2_IN_MIC); +- } ++ if (ret < 0) { ++ dev_err(dev, "Invalid pm_state: %ld, ret: %d\n", ++ pm_state, ret); ++ return -EINVAL; + } + +- /* +- * 2. Set ACODEC_ADC_ANA_CON0[7:0] to 0xff, to end the mute station +- * of ADC, to enable the MIC module, to enable the reference voltage +- * buffer, and to end the initialization of MIC +- */ +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(ch), +- RK3308_ADC_CH1_CH2_MIC_ALL_MSK, +- RK3308_ADC_CH1_CH2_MIC_ALL); ++ rk3308_codec_set_pm_state(rk3308, pm_state); + +- /* +- * 3. Set ACODEC_ADC_ANA_CON6[0] to 0x1, to enable the current source +- * of audio +- */ +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON06(ch), +- RK3308_ADC_CURRENT_MSK, +- RK3308_ADC_CURRENT_EN); ++ dev_info(dev, "Store pm_state: %d\n", rk3308->pm_state); + +- /* +- * 4. Set ACODEC_ADC_ANA_CON2[7:0] to 0x77, to enable the ALC module, +- * to enable the zero-crossing detection function, and to end the +- * initialization of ALC +- * +- * Note2. Please set ACODEC_ADC_ANA_CON2[7:0] to 0x33 in step4 +- * if the AGC function is closed +- */ ++ return count; ++} + +- adc_aif1 = RK3308_ADC_CH1_ALC_EN | RK3308_ADC_CH1_ALC_WORK; +- regmap_read(rk3308->regmap, RK3308_ALC_L_DIG_CON09(ch), &agc_func_en); +- if (agc_func_en & RK3308_AGC_FUNC_SEL_EN) +- adc_aif1 |= RK3308_ADC_CH1_ZEROCROSS_DET_EN; ++static ssize_t adc_grps_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct rk3308_codec_priv *rk3308 = ++ container_of(dev, struct rk3308_codec_priv, dev); ++ u32 grp; ++ int type = ADC_TYPE_NORMAL, count = 0; ++ int idx; ++ ++ count += sprintf(buf + count, "current used adc_grps:\n"); ++ count += sprintf(buf + count, "- normal:"); ++ for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) ++ count += sprintf(buf + count, " %d", grp); ++ count += sprintf(buf + count, "\n"); ++ count += sprintf(buf + count, "- loopback: %d\n", ++ rk3308->loopback_grp); + +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(ch), +- RK3308_ADC_CH1_ALC_ZC_MSK, +- adc_aif1); ++ return count; ++} + +- adc_aif2 = RK3308_ADC_CH2_ALC_EN | RK3308_ADC_CH2_ALC_WORK; +- regmap_read(rk3308->regmap, RK3308_ALC_L_DIG_CON09(ch), &agc_func_en); +- if (agc_func_en & RK3308_AGC_FUNC_SEL_EN) +- adc_aif2 |= RK3308_ADC_CH2_ZEROCROSS_DET_EN; ++static ssize_t adc_grps_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct rk3308_codec_priv *rk3308 = ++ container_of(dev, struct rk3308_codec_priv, dev); ++ char adc_type; ++ int grps, ret; ++ ++ ret = sscanf(buf, "%c,%d", &adc_type, &grps); ++ if (ret != 2) { ++ dev_err(rk3308->plat_dev, "%s sscanf failed: %d\n", ++ __func__, ret); ++ return -EFAULT; ++ } + +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(ch), +- RK3308_ADC_CH2_ALC_ZC_MSK, +- adc_aif2); ++ if (adc_type == 'n') ++ rk3308->used_adc_grps = grps; ++ else if (adc_type == 'l') ++ rk3308->loopback_grp = grps; + +- /* +- * 5. Set ACODEC_ADC_ANA_CON5[7:0] to 0x77, to enable the clock and +- * ADC module, and to end the initialization of ADC +- */ +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(ch), +- RK3308_ADC_CH1_ADC_CLK_MSK, +- RK3308_ADC_CH1_CLK_EN | +- RK3308_ADC_CH1_ADC_EN | +- RK3308_ADC_CH1_ADC_WORK); +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(ch), +- RK3308_ADC_CH2_ADC_CLK_MSK, +- RK3308_ADC_CH2_CLK_EN | +- RK3308_ADC_CH2_ADC_EN | +- RK3308_ADC_CH2_ADC_WORK); ++ return count; ++} + +- /* +- * 6. Set ACODEC_ADC_ANA_CON1[5:4] and ACODEC_ADC_ANA_CON1[1:0], +- * to select the gain of the MIC +- * +- * By default is 0db. +- */ +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(ch), +- RK3308_ADC_CH1_MIC_GAIN_MSK, +- RK3308_ADC_CH1_MIC_GAIN_0DB); +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(ch), +- RK3308_ADC_CH2_MIC_GAIN_MSK, +- RK3308_ADC_CH2_MIC_GAIN_0DB); ++static ssize_t adc_grps_route_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct rk3308_codec_priv *rk3308 = ++ container_of(dev, struct rk3308_codec_priv, dev); ++ char which_i2s[32] = {0}; ++ int count = 0; ++ u32 grp; + +- /* +- * 7.Set ACODEC_ADC_ANA_CON3[4:0] and ACODEC_ADC_ANA_CON4[3:0] to +- * select the gain of ALC +- * +- * By default is 0db. +- */ +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON03(ch), +- RK3308_ADC_CH1_ALC_GAIN_MSK, +- RK3308_ADC_CH1_ALC_GAIN_0DB); +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON04(ch), +- RK3308_ADC_CH2_ALC_GAIN_MSK, +- RK3308_ADC_CH2_ALC_GAIN_0DB); ++ switch (rk3308->which_i2s) { ++ case ACODEC_TO_I2S1_2CH: ++ strcpy(which_i2s, "i2s1_2ch"); ++ break; ++ case ACODEC_TO_I2S3_4CH: ++ strcpy(which_i2s, "i2s3_4ch"); ++ break; ++ default: ++ strcpy(which_i2s, "i2s2_8ch"); ++ break; ++ } + +- /* 8.Begin recording */ ++ count += sprintf(buf + count, "%s from acodec route mapping:\n", ++ which_i2s); ++ for (grp = 0; grp < rk3308->to_i2s_grps; grp++) { ++ count += sprintf(buf + count, "* sdi_%d <-- sdo_%d\n", ++ grp, rk3308->i2s_sdis[grp]); ++ } + +- return 0; ++ return count; + } + +-static int rk3308_codec_adc_ana_disable(struct rk3308_codec_priv *rk3308) ++static ssize_t adc_grps_route_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) + { +- int ch = rk3308->adc_ch; ++ struct rk3308_codec_priv *rk3308 = ++ container_of(dev, struct rk3308_codec_priv, dev); ++ int which_i2s, idx, i2s_sdis[ADC_LR_GROUP_MAX]; ++ int ret; + +- /* +- * 1. Set ACODEC_ADC_ANA_CON2[7:0] to 0x0, to disable the ALC module, +- * to disable the zero-crossing detection function, and to begin the +- * initialization of ALC +- */ +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(ch), +- RK3308_ADC_CH1_ALC_ZC_MSK, +- 0); +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(ch), +- RK3308_ADC_CH2_ALC_ZC_MSK, +- 0); ++ ret = sscanf(buf, "%d,%d,%d,%d,%d", &which_i2s, ++ &i2s_sdis[0], &i2s_sdis[1], &i2s_sdis[2], &i2s_sdis[3]); ++ if (ret != 5) { ++ dev_err(rk3308->plat_dev, "%s sscanf failed: %d\n", ++ __func__, ret); ++ goto err; ++ } + +- /* +- * 2. Set ACODEC_ADC_ANA_CON5[7:0] to 0x0, to disable the clock and +- * ADC module, and to begin the initialization of ADC +- */ +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(ch), +- RK3308_ADC_CH1_ADC_CLK_MSK, +- 0); +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(ch), +- RK3308_ADC_CH2_ADC_CLK_MSK, +- 0); ++ if (which_i2s < ACODEC_TO_I2S2_8CH || ++ which_i2s > ACODEC_TO_I2S1_2CH) { ++ dev_err(rk3308->plat_dev, "Invalid i2s type: %d\n", which_i2s); ++ goto err; ++ } + +- /* +- * 3. Set ACODEC_ADC_ANA_CON0[7:0] to 0x88, to disable the MIC module, +- * to disable the reference voltage buffer, and to begin the +- * initialization of MIC +- */ +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(ch), +- RK3308_ADC_CH1_CH2_MIC_ALL_MSK, +- RK3308_ADC_CH1_MIC_UNMUTE | +- RK3308_ADC_CH2_MIC_UNMUTE); ++ rk3308->which_i2s = which_i2s; + +- /* +- * 4. Set ACODEC_ADC_ANA_CON6[0] to 0x0, to disable the current +- * source of audio +- */ +- regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON06(ch), +- RK3308_ADC_CURRENT_MSK, +- RK3308_ADC_CURRENT_DIS); ++ switch (rk3308->which_i2s) { ++ case ACODEC_TO_I2S1_2CH: ++ rk3308->to_i2s_grps = 1; ++ break; ++ case ACODEC_TO_I2S3_4CH: ++ rk3308->to_i2s_grps = 2; ++ break; ++ default: ++ rk3308->to_i2s_grps = 4; ++ break; ++ } + +- return 0; ++ for (idx = 0; idx < rk3308->to_i2s_grps; idx++) ++ rk3308->i2s_sdis[idx] = i2s_sdis[idx]; ++ ++ rk3308_codec_adc_grps_route_config(rk3308); ++ ++err: ++ return count; + } + +-static int rk3308_codec_open_capture(struct snd_soc_codec *codec) ++static ssize_t adc_grp0_in_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) + { +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); +- +- rk3308_codec_alc_enable(rk3308); +- rk3308_codec_adc_ana_enable(rk3308); ++ struct rk3308_codec_priv *rk3308 = ++ container_of(dev, struct rk3308_codec_priv, dev); + +- return 0; ++ return sprintf(buf, "adc ch0 using: %s\n", ++ rk3308->adc_grp0_using_linein ? "line in" : "mic in"); + } + +-static int rk3308_codec_close_capture(struct snd_soc_codec *codec) ++static ssize_t adc_grp0_in_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) + { +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct rk3308_codec_priv *rk3308 = ++ container_of(dev, struct rk3308_codec_priv, dev); ++ unsigned long using_linein; ++ int ret = kstrtoul(buf, 10, &using_linein); ++ ++ if (ret < 0 || using_linein > 1) { ++ dev_err(dev, "Invalid input status: %ld, ret: %d\n", ++ using_linein, ret); ++ return -EINVAL; ++ } + +- rk3308_codec_alc_disable(rk3308); +- rk3308_codec_adc_ana_disable(rk3308); ++ rk3308->adc_grp0_using_linein = using_linein; + +- return 0; ++ dev_info(dev, "store using_linein: %d\n", ++ rk3308->adc_grp0_using_linein); ++ ++ return count; + } + +-static int rk3308_codec_open_playback(struct snd_soc_codec *codec) ++static ssize_t adc_zerocross_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) + { +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); +- +- rk3308_codec_dac_enable(rk3308); +- rk3308_speaker_ctl(rk3308, 1); ++ struct rk3308_codec_priv *rk3308 = ++ container_of(dev, struct rk3308_codec_priv, dev); + +- return 0; ++ return sprintf(buf, "adc zerocross: %s\n", ++ rk3308->adc_zerocross ? "enabled" : "disabled"); + } + +-static int rk3308_codec_close_playback(struct snd_soc_codec *codec) ++static ssize_t adc_zerocross_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) + { +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct rk3308_codec_priv *rk3308 = ++ container_of(dev, struct rk3308_codec_priv, dev); ++ unsigned long zerocross; ++ int ret = kstrtoul(buf, 10, &zerocross); + +- rk3308_speaker_ctl(rk3308, 0); +- rk3308_codec_dac_disable(rk3308); ++ if (ret < 0 || zerocross > 1) { ++ dev_err(dev, "Invalid zerocross: %ld, ret: %d\n", ++ zerocross, ret); ++ return -EINVAL; ++ } + +- return 0; ++ rk3308->adc_zerocross = zerocross; ++ ++ dev_info(dev, "store adc zerocross: %d\n", rk3308->adc_zerocross); ++ ++ return count; + } + +-static int rk3308_pcm_startup(struct snd_pcm_substream *substream, +- struct snd_soc_dai *dai) ++static ssize_t adc_grps_endisable_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) + { +- struct snd_soc_codec *codec = dai->codec; +- int ret = 0; ++ struct rk3308_codec_priv *rk3308 = ++ container_of(dev, struct rk3308_codec_priv, dev); ++ int count = 0, i; + +- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) +- ret = rk3308_codec_open_playback(codec); +- else +- ret = rk3308_codec_open_capture(codec); ++ count += sprintf(buf + count, "enabled adc grps:"); ++ for (i = 0; i < ADC_LR_GROUP_MAX; i++) ++ count += sprintf(buf + count, "%d ", ++ rk3308->adc_grps_endisable[i]); + +- return ret; ++ count += sprintf(buf + count, "\n"); ++ return count; + } + +-static void rk3308_pcm_shutdown(struct snd_pcm_substream *substream, +- struct snd_soc_dai *dai) ++static ssize_t adc_grps_endisable_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) + { +- struct snd_soc_codec *codec = dai->codec; ++ struct rk3308_codec_priv *rk3308 = ++ container_of(dev, struct rk3308_codec_priv, dev); ++ int grp, endisable, ret; + +- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) +- rk3308_codec_close_playback(codec); +- else +- rk3308_codec_close_capture(codec); +-} ++ ret = sscanf(buf, "%d,%d", &grp, &endisable); ++ if (ret != 2) { ++ dev_err(rk3308->plat_dev, "%s sscanf failed: %d\n", ++ __func__, ret); ++ return -EFAULT; ++ } + +-static struct snd_soc_dai_ops rk3308_dai_ops = { +- .hw_params = rk3308_hw_params, +- .set_fmt = rk3308_set_dai_fmt, +- .digital_mute = rk3308_digital_mute, +- .startup = rk3308_pcm_startup, +- .shutdown = rk3308_pcm_shutdown, +-}; ++ rk3308->cur_dbg_grp = grp; + +-static struct snd_soc_dai_driver rk3308_dai[] = { +- { +- .name = "rk3308-hifi", +- .id = RK3308_HIFI, +- .playback = { +- .stream_name = "HiFi Playback", +- .channels_min = 2, +- .channels_max = 2, +- .rates = SNDRV_PCM_RATE_8000_96000, +- .formats = (SNDRV_PCM_FMTBIT_S16_LE | +- SNDRV_PCM_FMTBIT_S20_3LE | +- SNDRV_PCM_FMTBIT_S24_LE | +- SNDRV_PCM_FMTBIT_S32_LE), +- }, +- .capture = { +- .stream_name = "HiFi Capture", +- .channels_min = 1, +- .channels_max = 8, +- .rates = SNDRV_PCM_RATE_8000_96000, +- .formats = (SNDRV_PCM_FMTBIT_S16_LE | +- SNDRV_PCM_FMTBIT_S20_3LE | +- SNDRV_PCM_FMTBIT_S24_LE | +- SNDRV_PCM_FMTBIT_S32_LE), +- }, +- .ops = &rk3308_dai_ops, +- }, +-}; ++ if (endisable) ++ rk3308_codec_open_dbg_capture(rk3308); ++ else ++ rk3308_codec_close_dbg_capture(rk3308); + +-static int rk3308_suspend(struct snd_soc_codec *codec) +-{ +- rk3308_set_bias_level(codec, SND_SOC_BIAS_OFF); ++ dev_info(dev, "ADC grp %d endisable: %d\n", grp, endisable); + +- return 0; ++ return count; + } + +-static int rk3308_resume(struct snd_soc_codec *codec) ++static ssize_t dac_endisable_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) + { +- rk3308_set_bias_level(codec, SND_SOC_BIAS_STANDBY); ++ struct rk3308_codec_priv *rk3308 = ++ container_of(dev, struct rk3308_codec_priv, dev); + +- return 0; ++ return sprintf(buf, "%d\n", rk3308->dac_endisable); + } + +-static int rk3308_probe(struct snd_soc_codec *codec) ++static ssize_t dac_endisable_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) + { +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct rk3308_codec_priv *rk3308 = ++ container_of(dev, struct rk3308_codec_priv, dev); ++ unsigned long endisable; ++ int ret = kstrtoul(buf, 10, &endisable); + +- rk3308_codec_reset(codec); +- rk3308_codec_power_on(codec); ++ if (ret < 0) { ++ dev_err(dev, "Invalid endisable: %ld, ret: %d\n", ++ endisable, ret); ++ return -EINVAL; ++ } ++ ++ if (endisable) ++ rk3308_codec_open_playback(rk3308); ++ else ++ rk3308_codec_close_playback(rk3308); + +- rk3308_codec_micbias_enable(rk3308, RK3308_ADC_MICBIAS_VOLT_0_7); ++ dev_info(dev, "DAC endisable: %ld\n", endisable); + +- return 0; ++ return count; + } + +-static int rk3308_remove(struct snd_soc_codec *codec) ++static ssize_t dac_output_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) + { +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct rk3308_codec_priv *rk3308 = ++ container_of(dev, struct rk3308_codec_priv, dev); ++ ssize_t ret = 0; + +- rk3308_speaker_ctl(rk3308, 0); +- rk3308_codec_micbias_disable(rk3308); +- rk3308_codec_power_off(codec); ++ switch (rk3308->dac_output) { ++ case DAC_LINEOUT: ++ ret = sprintf(buf, "dac path: %s\n", "line out"); ++ break; ++ case DAC_HPOUT: ++ ret = sprintf(buf, "dac path: %s\n", "hp out"); ++ break; ++ case DAC_LINEOUT_HPOUT: ++ ret = sprintf(buf, "dac path: %s\n", ++ "both line out and hp out"); ++ break; ++ default: ++ pr_err("Invalid dac path: %d ?\n", rk3308->dac_output); ++ break; ++ } + +- return 0; ++ return ret; + } + +-static struct snd_soc_codec_driver soc_codec_dev_rk3308 = { +- .probe = rk3308_probe, +- .remove = rk3308_remove, +- .suspend = rk3308_suspend, +- .resume = rk3308_resume, +- .set_bias_level = rk3308_set_bias_level, +- .controls = rk3308_codec_dapm_controls, +- .num_controls = ARRAY_SIZE(rk3308_codec_dapm_controls), +-}; +- +-static const struct reg_default rk3308_codec_reg_defaults[] = { +- { RK3308_GLB_CON, 0x07 }, +-}; +- +-static bool rk3308_codec_write_read_reg(struct device *dev, unsigned int reg) ++static ssize_t dac_output_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) + { +- /* All registers can be read / write */ +- return true; +-} ++ struct rk3308_codec_priv *rk3308 = ++ container_of(dev, struct rk3308_codec_priv, dev); ++ unsigned long dac_output; ++ int ret = kstrtoul(buf, 10, &dac_output); + +-static bool rk3308_codec_volatile_reg(struct device *dev, unsigned int reg) +-{ +- switch (reg) { +- case RK3308_GLB_CON: +- return true; +- default: +- return false; ++ if (ret < 0) { ++ dev_err(dev, "Invalid input status: %ld, ret: %d\n", ++ dac_output, ret); ++ return -EINVAL; + } +-} + +-static const struct regmap_config rk3308_codec_regmap_config = { +- .reg_bits = 32, +- .reg_stride = 4, +- .val_bits = 32, +- .max_register = RK3308_DAC_ANA_CON13, +- .writeable_reg = rk3308_codec_write_read_reg, +- .readable_reg = rk3308_codec_write_read_reg, +- .volatile_reg = rk3308_codec_volatile_reg, +- .reg_defaults = rk3308_codec_reg_defaults, +- .num_reg_defaults = ARRAY_SIZE(rk3308_codec_reg_defaults), +- .cache_type = REGCACHE_FLAT, +-}; ++ rk3308_codec_dac_switch(rk3308, dac_output); + +-static ssize_t adc_ch_show(struct device *dev, +- struct device_attribute *attr, +- char *buf) ++ dev_info(dev, "Store dac_output: %d\n", rk3308->dac_output); ++ ++ return count; ++} ++ ++static ssize_t enable_all_adcs_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) + { + struct rk3308_codec_priv *rk3308 = + container_of(dev, struct rk3308_codec_priv, dev); + +- return sprintf(buf, "adc_ch: %d\n", rk3308->adc_ch); ++ return sprintf(buf, "%d\n", rk3308->enable_all_adcs); + } + +-static ssize_t adc_ch_store(struct device *dev, +- struct device_attribute *attr, +- const char *buf, size_t count) ++static ssize_t enable_all_adcs_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) + { + struct rk3308_codec_priv *rk3308 = + container_of(dev, struct rk3308_codec_priv, dev); +- unsigned long ch; +- int ret = kstrtoul(buf, 10, &ch); ++ unsigned long enable; ++ int ret = kstrtoul(buf, 10, &enable); + +- if (ret < 0 || ch > 4) { +- dev_err(dev, "Invalid ch: %ld, ret: %d\n", ch, ret); ++ if (ret < 0) { ++ dev_err(dev, "Invalid enable value: %ld, ret: %d\n", ++ enable, ret); + return -EINVAL; + } + +- rk3308->adc_ch = ch; +- +- dev_info(dev, "Store ch: %d\n", rk3308->adc_ch); ++ rk3308->enable_all_adcs = enable; + + return count; + } + +-static const struct device_attribute adc_ch_attrs[] = { +- __ATTR(adc_ch, 0644, adc_ch_show, adc_ch_store), ++static const struct device_attribute acodec_attrs[] = { ++ __ATTR_RW(adc_grps), ++ __ATTR_RW(adc_grps_endisable), ++ __ATTR_RW(adc_grps_route), ++ __ATTR_RW(adc_grp0_in), ++ __ATTR_RW(adc_zerocross), ++ __ATTR_RW(dac_endisable), ++ __ATTR_RW(dac_output), ++ __ATTR_RW(enable_all_adcs), ++ __ATTR_RW(pm_state), + }; + + static void rk3308_codec_device_release(struct device *dev) +@@ -1468,8 +4747,8 @@ static int rk3308_codec_sysfs_init(struct platform_device *pdev, + return -ENOMEM; + } + +- for (i = 0; i < ARRAY_SIZE(adc_ch_attrs); i++) { +- if (device_create_file(dev, &adc_ch_attrs[i])) { ++ for (i = 0; i < ARRAY_SIZE(acodec_attrs); i++) { ++ if (device_create_file(dev, &acodec_attrs[i])) { + dev_err(&pdev->dev, + "Create 'rk3308-acodec-dev' attr failed\n"); + device_unregister(dev); +@@ -1480,32 +4759,136 @@ static int rk3308_codec_sysfs_init(struct platform_device *pdev, + return 0; + } + ++#if defined(CONFIG_DEBUG_FS) ++static int rk3308_codec_debugfs_reg_show(struct seq_file *s, void *v) ++{ ++ struct rk3308_codec_priv *rk3308 = s->private; ++ unsigned int i; ++ unsigned int val; ++ ++ for (i = RK3308_GLB_CON; i <= RK3308_DAC_ANA_CON13; i += 4) { ++ regmap_read(rk3308->regmap, i, &val); ++ if (!(i % 16)) ++ seq_printf(s, "\nR:%04x: ", i); ++ seq_printf(s, "%08x ", val); ++ } ++ ++ seq_puts(s, "\n"); ++ ++ return 0; ++} ++ ++static ssize_t rk3308_codec_debugfs_reg_operate(struct file *file, ++ const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct rk3308_codec_priv *rk3308 = ++ ((struct seq_file *)file->private_data)->private; ++ unsigned int reg, val; ++ char op; ++ char kbuf[32]; ++ int ret; ++ ++ if (count >= sizeof(kbuf)) ++ return -EINVAL; ++ ++ if (copy_from_user(kbuf, buf, count)) ++ return -EFAULT; ++ kbuf[count] = '\0'; ++ ++ ret = sscanf(kbuf, "%c,%x,%x", &op, ®, &val); ++ if (ret != 3) { ++ pr_err("sscanf failed: %d\n", ret); ++ return -EFAULT; ++ } ++ ++ if (op == 'w') { ++ pr_info("Write reg: 0x%04x with val: 0x%08x\n", reg, val); ++ regmap_write(rk3308->regmap, reg, val); ++ regcache_cache_only(rk3308->regmap, false); ++ regcache_sync(rk3308->regmap); ++ pr_info("Read back reg: 0x%04x with val: 0x%08x\n", reg, val); ++ } else if (op == 'r') { ++ regmap_read(rk3308->regmap, reg, &val); ++ pr_info("Read reg: 0x%04x with val: 0x%08x\n", reg, val); ++ } else { ++ pr_err("This is an invalid operation: %c\n", op); ++ } ++ ++ return count; ++} ++ ++static int rk3308_codec_debugfs_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, ++ rk3308_codec_debugfs_reg_show, inode->i_private); ++} ++ ++static const struct file_operations rk3308_codec_reg_debugfs_fops = { ++ .owner = THIS_MODULE, ++ .open = rk3308_codec_debugfs_open, ++ .read = seq_read, ++ .write = rk3308_codec_debugfs_reg_operate, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++#endif /* CONFIG_DEBUG_FS */ ++ ++static int rk3308_codec_get_version(struct rk3308_codec_priv *rk3308) ++{ ++ unsigned int chip_id; ++ ++ regmap_read(rk3308->grf, GRF_CHIP_ID, &chip_id); ++ switch (chip_id) { ++ case 3306: ++ rk3308->codec_ver = ACODEC_VERSION_A; ++ break; ++ case 0x3308: ++ rk3308->codec_ver = ACODEC_VERSION_B; ++ break; ++ default: ++ pr_err("Unknown chip_id: %d / 0x%x\n", chip_id, chip_id); ++ return -EFAULT; ++ } ++ ++ pr_info("The acodec version is: %x\n", rk3308->codec_ver); ++ return 0; ++} ++ + static int rk3308_platform_probe(struct platform_device *pdev) + { + struct device_node *np = pdev->dev.of_node; + struct rk3308_codec_priv *rk3308; + struct resource *res; + void __iomem *base; +- int ret = 0; +- struct regmap *grf; +- +- grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); +- if (IS_ERR(grf)) { +- dev_err(&pdev->dev, +- "Missing 'rockchip,grf' property\n"); +- return PTR_ERR(grf); +- } ++ int ret; + + rk3308 = devm_kzalloc(&pdev->dev, sizeof(*rk3308), GFP_KERNEL); + if (!rk3308) + return -ENOMEM; + ++ rk3308->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); ++ if (IS_ERR(rk3308->grf)) { ++ dev_err(&pdev->dev, ++ "Missing 'rockchip,grf' property\n"); ++ return PTR_ERR(rk3308->grf); ++ } ++ + ret = rk3308_codec_sysfs_init(pdev, rk3308); + if (ret < 0) { + dev_err(&pdev->dev, "Sysfs init failed\n"); + return ret; + } + ++#if defined(CONFIG_DEBUG_FS) ++ rk3308->dbg_codec = debugfs_create_dir(CODEC_DRV_NAME, NULL); ++ if (IS_ERR(rk3308->dbg_codec)) ++ dev_err(&pdev->dev, ++ "Failed to create debugfs dir for rk3308!\n"); ++ else ++ debugfs_create_file("reg", 0644, rk3308->dbg_codec, ++ rk3308, &rk3308_codec_reg_debugfs_fops); ++#endif + rk3308->plat_dev = &pdev->dev; + + rk3308->reset = devm_reset_control_get(&pdev->dev, "acodec-reset"); +@@ -1518,27 +4901,146 @@ static int rk3308_platform_probe(struct platform_device *pdev) + rk3308->reset = NULL; + } + +- /* GPIO0_A5 control speaker on RK3308 EVB */ +- rk3308->spk_ctl_gpio = devm_gpiod_get_optional(&pdev->dev, "spk_ctl", +- GPIOD_OUT_HIGH); +- if (IS_ERR(rk3308->spk_ctl_gpio)) { ++ rk3308->hp_ctl_gpio = devm_gpiod_get_optional(&pdev->dev, "hp-ctl", ++ GPIOD_OUT_LOW); ++ if (!rk3308->hp_ctl_gpio) { ++ dev_info(&pdev->dev, "Don't need hp-ctl gpio\n"); ++ } else if (IS_ERR(rk3308->hp_ctl_gpio)) { ++ ret = PTR_ERR(rk3308->hp_ctl_gpio); ++ dev_err(&pdev->dev, "Unable to claim gpio hp-ctl\n"); ++ return ret; ++ } ++ ++ rk3308->spk_ctl_gpio = devm_gpiod_get_optional(&pdev->dev, "spk-ctl", ++ GPIOD_OUT_LOW); ++ ++ if (!rk3308->spk_ctl_gpio) { ++ dev_info(&pdev->dev, "Don't need spk-ctl gpio\n"); ++ } else if (IS_ERR(rk3308->spk_ctl_gpio)) { + ret = PTR_ERR(rk3308->spk_ctl_gpio); +- dev_err(&pdev->dev, "Unable to claim gpio spk_ctl\n"); ++ dev_err(&pdev->dev, "Unable to claim gpio spk-ctl\n"); ++ return ret; ++ } ++ ++ rk3308->pa_drv_gpio = devm_gpiod_get_optional(&pdev->dev, "pa-drv", ++ GPIOD_OUT_LOW); ++ ++ if (!rk3308->pa_drv_gpio) { ++ dev_info(&pdev->dev, "Don't need pa-drv gpio\n"); ++ } else if (IS_ERR(rk3308->pa_drv_gpio)) { ++ ret = PTR_ERR(rk3308->pa_drv_gpio); ++ dev_err(&pdev->dev, "Unable to claim gpio pa-drv\n"); + return ret; + } + ++ if (rk3308->pa_drv_gpio) { ++ rk3308->delay_pa_drv_ms = PA_DRV_MS; ++ ret = of_property_read_u32(np, "rockchip,delay-pa-drv-ms", ++ &rk3308->delay_pa_drv_ms); ++ } ++ ++#if DEBUG_POP_ALWAYS ++ dev_info(&pdev->dev, "Enable all ctl gpios always for debugging pop\n"); ++ rk3308_headphone_ctl(rk3308, 1); ++ rk3308_speaker_ctl(rk3308, 1); ++#else ++ dev_info(&pdev->dev, "De-pop as much as possible\n"); ++ rk3308_headphone_ctl(rk3308, 0); ++ rk3308_speaker_ctl(rk3308, 0); ++#endif ++ + rk3308->pclk = devm_clk_get(&pdev->dev, "acodec"); + if (IS_ERR(rk3308->pclk)) { + dev_err(&pdev->dev, "Can't get acodec pclk\n"); + return PTR_ERR(rk3308->pclk); + } + ++ rk3308->mclk_rx = devm_clk_get(&pdev->dev, "mclk_rx"); ++ if (IS_ERR(rk3308->mclk_rx)) { ++ dev_err(&pdev->dev, "Can't get acodec mclk_rx\n"); ++ return PTR_ERR(rk3308->mclk_rx); ++ } ++ ++ rk3308->mclk_tx = devm_clk_get(&pdev->dev, "mclk_tx"); ++ if (IS_ERR(rk3308->mclk_tx)) { ++ dev_err(&pdev->dev, "Can't get acodec mclk_tx\n"); ++ return PTR_ERR(rk3308->mclk_tx); ++ } ++ + ret = clk_prepare_enable(rk3308->pclk); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to enable acodec pclk: %d\n", ret); + return ret; + } + ++ ret = clk_prepare_enable(rk3308->mclk_rx); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "Failed to enable i2s mclk_rx: %d\n", ret); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(rk3308->mclk_tx); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "Failed to enable i2s mclk_tx: %d\n", ret); ++ return ret; ++ } ++ ++ rk3308_codec_check_micbias(rk3308, np); ++ ++ rk3308->enable_all_adcs = ++ of_property_read_bool(np, "rockchip,enable-all-adcs"); ++ ++ rk3308->hp_jack_reversed = ++ of_property_read_bool(np, "rockchip,hp-jack-reversed"); ++ ++ rk3308->no_deep_low_power = ++ of_property_read_bool(np, "rockchip,no-deep-low-power"); ++ ++ rk3308->no_hp_det = ++ of_property_read_bool(np, "rockchip,no-hp-det"); ++ ++ rk3308->delay_loopback_handle_ms = LOOPBACK_HANDLE_MS; ++ ret = of_property_read_u32(np, "rockchip,delay-loopback-handle-ms", ++ &rk3308->delay_loopback_handle_ms); ++ ++ rk3308->delay_start_play_ms = 0; ++ ret = of_property_read_u32(np, "rockchip,delay-start-play-ms", ++ &rk3308->delay_start_play_ms); ++ ++ rk3308->loopback_grp = NOT_USED; ++ ret = of_property_read_u32(np, "rockchip,loopback-grp", ++ &rk3308->loopback_grp); ++ /* ++ * If there is no loopback on some board, the -EINVAL indicates that ++ * we don't need add the node, and it is not an error. ++ */ ++ if (ret < 0 && ret != -EINVAL) { ++ dev_err(&pdev->dev, "Failed to read loopback property: %d\n", ++ ret); ++ return ret; ++ } ++ ++ ret = rk3308_codec_adc_grps_route(rk3308, np); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "Failed to route ADC groups: %d\n", ++ ret); ++ return ret; ++ } ++ ++ ret = rk3308_codec_setup_en_always_adcs(rk3308, np); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "Failed to setup enabled always ADCs: %d\n", ++ ret); ++ return ret; ++ } ++ ++ ret = rk3308_codec_get_version(rk3308); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "Failed to get acodec version: %d\n", ++ ret); ++ return ret; ++ } ++ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) { +@@ -1555,10 +5057,65 @@ static int rk3308_platform_probe(struct platform_device *pdev) + goto failed; + } + ++ if (!rk3308->no_hp_det) { ++ int index = 0; ++ ++ if (rk3308->codec_ver == ACODEC_VERSION_B) ++ index = 1; ++ ++ rk3308->irq = platform_get_irq(pdev, index); ++ if (rk3308->irq < 0) { ++ dev_err(&pdev->dev, "Can not get codec irq\n"); ++ goto failed; ++ } ++ ++ INIT_DELAYED_WORK(&rk3308->hpdet_work, rk3308_codec_hpdetect_work); ++ ++ ret = devm_request_irq(&pdev->dev, rk3308->irq, ++ rk3308_codec_hpdet_isr, ++ 0, ++ "acodec-hpdet", ++ rk3308); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "Failed to request IRQ: %d\n", ret); ++ goto failed; ++ } ++ ++ if (rk3308->codec_ver == ACODEC_VERSION_B) { ++ rk3308->detect_grf = ++ syscon_regmap_lookup_by_phandle(np, "rockchip,detect-grf"); ++ if (IS_ERR(rk3308->detect_grf)) { ++ dev_err(&pdev->dev, ++ "Missing 'rockchip,detect-grf' property\n"); ++ return PTR_ERR(rk3308->detect_grf); ++ } ++ ++ /* Configure filter count and enable hpdet irq. */ ++ regmap_write(rk3308->detect_grf, ++ DETECT_GRF_ACODEC_HPDET_COUNTER, ++ DEFAULT_HPDET_COUNT); ++ regmap_write(rk3308->detect_grf, ++ DETECT_GRF_ACODEC_HPDET_CON, ++ (HPDET_BOTH_NEG_POS << 16) | ++ HPDET_BOTH_NEG_POS); ++ } ++ ++ rk3308_codec_set_jack_detect_cb = rk3308_codec_set_jack_detect; ++ } ++ ++ if (rk3308->codec_ver == ACODEC_VERSION_A) ++ INIT_DELAYED_WORK(&rk3308->loopback_work, ++ rk3308_codec_loopback_work); ++ ++ rk3308->adc_grp0_using_linein = ADC_GRP0_MICIN; ++ rk3308->dac_output = DAC_LINEOUT; ++ rk3308->adc_zerocross = 1; ++ rk3308->pm_state = PM_NORMAL; ++ + platform_set_drvdata(pdev, rk3308); + + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_rk3308, +- rk3308_dai, ARRAY_SIZE(rk3308_dai)); ++ rk3308_dai, ARRAY_SIZE(rk3308_dai)); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register codec: %d\n", ret); + goto failed; +@@ -1567,7 +5124,10 @@ static int rk3308_platform_probe(struct platform_device *pdev) + return ret; + + failed: ++ clk_disable_unprepare(rk3308->mclk_rx); ++ clk_disable_unprepare(rk3308->mclk_tx); + clk_disable_unprepare(rk3308->pclk); ++ device_unregister(&rk3308->dev); + + return ret; + } +@@ -1577,8 +5137,11 @@ static int rk3308_platform_remove(struct platform_device *pdev) + struct rk3308_codec_priv *rk3308 = + (struct rk3308_codec_priv *)platform_get_drvdata(pdev); + ++ clk_disable_unprepare(rk3308->mclk_rx); ++ clk_disable_unprepare(rk3308->mclk_tx); + clk_disable_unprepare(rk3308->pclk); + snd_soc_unregister_codec(&pdev->dev); ++ device_unregister(&rk3308->dev); + + return 0; + } +@@ -1591,7 +5154,7 @@ MODULE_DEVICE_TABLE(of, rk3308codec_of_match); + + static struct platform_driver rk3308_codec_driver = { + .driver = { +- .name = "rk3308-acodec", ++ .name = CODEC_DRV_NAME, + .of_match_table = of_match_ptr(rk3308codec_of_match), + }, + .probe = rk3308_platform_probe, +diff --git a/sound/soc/codecs/rk3308_codec.h b/sound/soc/codecs/rk3308_codec.h +index 6cfa69157785..93e089dae081 100644 +--- a/sound/soc/codecs/rk3308_codec.h ++++ b/sound/soc/codecs/rk3308_codec.h +@@ -26,7 +26,8 @@ + #define ACODEC_ADC_I2S_CTL0 0x04 /* REG 0x01 */ + #define ACODEC_ADC_I2S_CTL1 0x08 /* REG 0x02 */ + #define ACODEC_ADC_BIST_MODE_SEL 0x0c /* REG 0x03 */ +-/* Resevred REG 0x04 ~ 0x06 */ ++#define ACODEC_ADC_HPF_PATH 0x10 /* REG 0x04 */ ++/* Resevred REG 0x05 ~ 0x06 */ + #define ACODEC_ADC_DATA_PATH 0x1c /* REG 0x07 */ + /* Resevred REG 0x08 ~ 0x0f */ + +@@ -62,12 +63,15 @@ + #define ACODEC_DAC_I2S_CTL0 0x04 /* REG 0x01 */ + #define ACODEC_DAC_I2S_CTL1 0x08 /* REG 0x02 */ + #define ACODEC_DAC_BIST_MODE_SEL 0x0c /* REG 0x03 */ +-/* Resevred REG 0x04 */ ++#define ACODEC_DAC_DIGITAL_GAIN 0x10 /* REG 0x04 */ + #define ACODEC_DAC_DATA_SEL 0x14 /* REG 0x05 */ + /* Resevred REG 0x06 ~ 0x09 */ + #define ACODEC_DAC_DATA_HI 0x28 /* REG 0x0a */ + #define ACODEC_DAC_DATA_LO 0x2c /* REG 0x0b */ +-/* Resevred REG 0x0c ~ 0x0f */ ++/* Resevred REG 0x0c */ ++#define ACODEC_DAC_HPDET_DELAYTIME 0x34 /* REG 0x0d */ ++#define ACODEC_DAC_HPDET_STATUS 0x38 /* REG 0x0e, Read-only */ ++/* Resevred REG 0x0f */ + + /* ADC ANALOG REGISTERS */ + #define ACODEC_ADC_ANA_MIC_CTL 0x00 /* REG 0x00 */ +@@ -92,10 +96,13 @@ + #define ACODEC_DAC_ANA_LINEOUT 0x10 /* REG 0x04 */ + #define ACODEC_DAC_ANA_L_HPOUT_GAIN 0x14 /* REG 0x05 */ + #define ACODEC_DAC_ANA_R_HPOUT_GAIN 0x18 /* REG 0x06 */ ++#define ACODEC_DAC_ANA_DRV_HPOUT 0x1c /* REG 0x07 */ ++#define ACODEC_DAC_ANA_DRV_LINEOUT 0x20 /* REG 0x08 */ + /* Resevred REG 0x07 ~ 0x0b */ + #define ACODEC_DAC_ANA_HPMIX_CTL0 0x30 /* REG 0x0c */ + #define ACODEC_DAC_ANA_HPMIX_CTL1 0x34 /* REG 0x0d */ +-/* Resevred REG 0x0e ~ 0x0f */ ++#define ACODEC_DAC_ANA_LINEOUT_CTL0 0x38 /* REG 0x0e */ ++#define ACODEC_DAC_ANA_LINEOUT_CTL1 0x3c /* REG 0x0f */ + + /* + * These registers are referenced by codec driver +@@ -106,7 +113,7 @@ + /* ADC DIGITAL REGISTERS */ + + /* +- * The ADC chanel are 0 ~ 3, that control: ++ * The ADC group are 0 ~ 3, that control: + * + * CH0: left_0(ADC1) and right_0(ADC2) + * CH1: left_1(ADC3) and right_1(ADC4) +@@ -118,6 +125,7 @@ + #define RK3308_ADC_DIG_CON01(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_I2S_CTL0) + #define RK3308_ADC_DIG_CON02(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_I2S_CTL1) + #define RK3308_ADC_DIG_CON03(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_BIST_MODE_SEL) ++#define RK3308_ADC_DIG_CON04(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_HPF_PATH) + #define RK3308_ADC_DIG_CON07(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_DATA_PATH) + + #define RK3308_ALC_L_DIG_CON00(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_PGA_AGC_L_CTL0) +@@ -150,13 +158,16 @@ + #define RK3308_DAC_DIG_CON01 (RK3308_DAC_DIG_OFFSET + ACODEC_DAC_I2S_CTL0) + #define RK3308_DAC_DIG_CON02 (RK3308_DAC_DIG_OFFSET + ACODEC_DAC_I2S_CTL1) + #define RK3308_DAC_DIG_CON03 (RK3308_DAC_DIG_OFFSET + ACODEC_DAC_BIST_MODE_SEL) ++#define RK3308_DAC_DIG_CON04 (RK3308_DAC_DIG_OFFSET + ACODEC_DAC_DIGITAL_GAIN) + #define RK3308_DAC_DIG_CON05 (RK3308_DAC_DIG_OFFSET + ACODEC_DAC_DATA_SEL) + #define RK3308_DAC_DIG_CON10 (RK3308_DAC_DIG_OFFSET + ACODEC_DAC_DATA_HI) + #define RK3308_DAC_DIG_CON11 (RK3308_DAC_DIG_OFFSET + ACODEC_DAC_DATA_LO) ++#define RK3308_DAC_DIG_CON13 (RK3308_DAC_DIG_OFFSET + ACODEC_DAC_HPDET_DELAYTIME) ++#define RK3308_DAC_DIG_CON14 (RK3308_DAC_DIG_OFFSET + ACODEC_DAC_HPDET_STATUS) + + /* ADC ANALOG REGISTERS */ + /* +- * The ADC chanel are 0 ~ 3, that control: ++ * The ADC group are 0 ~ 3, that control: + * + * CH0: left_0(ADC1) and right_0(ADC2) + * CH1: left_1(ADC3) and right_1(ADC4) +@@ -179,7 +190,6 @@ + + /* DAC ANALOG REGISTERS */ + #define RK3308_DAC_ANA_OFFSET 0x440 +- + #define RK3308_DAC_ANA_CON00 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_CTL0) + #define RK3308_DAC_ANA_CON01 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_POP_VOLT) + #define RK3308_DAC_ANA_CON02 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_CTL1) +@@ -187,8 +197,12 @@ + #define RK3308_DAC_ANA_CON04 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_LINEOUT) + #define RK3308_DAC_ANA_CON05 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_L_HPOUT_GAIN) + #define RK3308_DAC_ANA_CON06 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_R_HPOUT_GAIN) ++#define RK3308_DAC_ANA_CON07 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_DRV_HPOUT) ++#define RK3308_DAC_ANA_CON08 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_DRV_LINEOUT) + #define RK3308_DAC_ANA_CON12 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_HPMIX_CTL0) + #define RK3308_DAC_ANA_CON13 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_HPMIX_CTL1) ++#define RK3308_DAC_ANA_CON14 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_LINEOUT_CTL0) ++#define RK3308_DAC_ANA_CON15 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_LINEOUT_CTL1) + + /* + * These are the bits for registers +@@ -199,6 +213,12 @@ + #define RK3308_ADC_BIST_RESET (0 << 7) + #define RK3308_DAC_BIST_WORK (1 << 6) + #define RK3308_DAC_BIST_RESET (0 << 6) ++#define RK3308_ADC_MCLK_MSK (1 << 5) ++#define RK3308_ADC_MCLK_DIS (1 << 5) ++#define RK3308_ADC_MCLK_EN (0 << 5) ++#define RK3308_DAC_MCLK_MSK (1 << 4) ++#define RK3308_DAC_MCLK_DIS (1 << 4) ++#define RK3308_DAC_MCLK_EN (0 << 4) + #define RK3308_CODEC_RST_MSK (0x7 << 0) + #define RK3308_ADC_DIG_WORK (1 << 2) + #define RK3308_ADC_DIG_RESET (0 << 2) +@@ -253,16 +273,27 @@ + /* RK3308_ADC_DIG_CON03 - REG: 0x000c */ + #define RK3308_ADC_L_CH_BIST_SFT 2 + #define RK3308_ADC_L_CH_BIST_MSK (0x3 << RK3308_ADC_L_CH_BIST_SFT) +-#define RK3308_ADC_L_CH_BIST_LEFT (0x3 << RK3308_ADC_L_CH_BIST_SFT) /* normal mode */ +-#define RK3308_ADC_L_CH_BIST_SINE (0x2 << RK3308_ADC_L_CH_BIST_SFT) +-#define RK3308_ADC_L_CH_BIST_CUBE (0x1 << RK3308_ADC_L_CH_BIST_SFT) +-#define RK3308_ADC_L_CH_BIST_RIGHT (0x0 << RK3308_ADC_L_CH_BIST_SFT) /* normal mode */ ++#define RK3308_ADC_L_CH_NORMAL_RIGHT (0x3 << RK3308_ADC_L_CH_BIST_SFT) /* normal mode */ ++#define RK3308_ADC_L_CH_BIST_CUBE (0x2 << RK3308_ADC_L_CH_BIST_SFT) ++#define RK3308_ADC_L_CH_BIST_SINE (0x1 << RK3308_ADC_L_CH_BIST_SFT) ++#define RK3308_ADC_L_CH_NORMAL_LEFT (0x0 << RK3308_ADC_L_CH_BIST_SFT) /* normal mode */ + #define RK3308_ADC_R_CH_BIST_SFT 0 + #define RK3308_ADC_R_CH_BIST_MSK (0x3 << RK3308_ADC_R_CH_BIST_SFT) +-#define RK3308_ADC_R_CH_BIST_LEFT (0x3 << RK3308_ADC_R_CH_BIST_SFT) /* normal mode */ +-#define RK3308_ADC_R_CH_BIST_SINE (0x2 << RK3308_ADC_R_CH_BIST_SFT) +-#define RK3308_ADC_R_CH_BIST_CUBE (0x1 << RK3308_ADC_R_CH_BIST_SFT) +-#define RK3308_ADC_R_CH_BIST_RIGHT (0x0 << RK3308_ADC_R_CH_BIST_SFT) /* normal mode */ ++#define RK3308_ADC_R_CH_NORMAL_LEFT (0x3 << RK3308_ADC_R_CH_BIST_SFT) /* normal mode */ ++#define RK3308_ADC_R_CH_BIST_CUBE (0x2 << RK3308_ADC_R_CH_BIST_SFT) ++#define RK3308_ADC_R_CH_BIST_SINE (0x1 << RK3308_ADC_R_CH_BIST_SFT) ++#define RK3308_ADC_R_CH_NORMAL_RIGHT (0x0 << RK3308_ADC_R_CH_BIST_SFT) /* normal mode */ ++ ++/* RK3308_ADC_DIG_CON04 - REG: 0x0010 */ ++#define RK3308_ADC_HPF_PATH_SFT 2 ++#define RK3308_ADC_HPF_PATH_MSK (1 << RK3308_ADC_HPF_PATH_SFT) ++#define RK3308_ADC_HPF_PATH_DIS (1 << RK3308_ADC_HPF_PATH_SFT) ++#define RK3308_ADC_HPF_PATH_EN (0 << RK3308_ADC_HPF_PATH_SFT) ++#define RK3308_ADC_HPF_CUTOFF_SFT 0 ++#define RK3308_ADC_HPF_CUTOFF_MSK (0x3 << RK3308_ADC_HPF_CUTOFF_SFT) ++#define RK3308_ADC_HPF_CUTOFF_612HZ (0x2 << RK3308_ADC_HPF_CUTOFF_SFT) ++#define RK3308_ADC_HPF_CUTOFF_245HZ (0x1 << RK3308_ADC_HPF_CUTOFF_SFT) ++#define RK3308_ADC_HPF_CUTOFF_20HZ (0x0 << RK3308_ADC_HPF_CUTOFF_SFT) + + /* RK3308_ADC_DIG_CON07 - REG: 0x001c */ + #define RK3308_ADCL_DATA_SFT 4 +@@ -391,6 +422,8 @@ + */ + #define RK3308_AGC_PGA_ZERO_CRO_EN (0x1 << 5) + #define RK3308_AGC_PGA_ZERO_CRO_DIS (0x0 << 5) ++#define RK3308_AGC_PGA_GAIN_MAX 0x1f ++#define RK3308_AGC_PGA_GAIN_MIN 0 + #define RK3308_AGC_PGA_GAIN_SFT 0 + #define RK3308_AGC_PGA_GAIN_MSK (0x1f << RK3308_AGC_PGA_GAIN_SFT) + #define RK3308_AGC_PGA_GAIN_PDB_28_5 (0x1f << RK3308_AGC_PGA_GAIN_SFT) +@@ -474,6 +507,8 @@ + #define RK3308_AGC_FUNC_SEL_MSK (0x1 << 6) + #define RK3308_AGC_FUNC_SEL_EN (0x1 << 6) + #define RK3308_AGC_FUNC_SEL_DIS (0x0 << 6) ++#define RK3308_AGC_MAX_GAIN_PGA_MAX 0x7 ++#define RK3308_AGC_MAX_GAIN_PGA_MIN 0 + #define RK3308_AGC_MAX_GAIN_PGA_SFT 3 + #define RK3308_AGC_MAX_GAIN_PGA_MSK (0x7 << RK3308_AGC_MAX_GAIN_PGA_SFT) + #define RK3308_AGC_MAX_GAIN_PGA_PDB_28_5 (0x7 << RK3308_AGC_MAX_GAIN_PGA_SFT) +@@ -484,6 +519,8 @@ + #define RK3308_AGC_MAX_GAIN_PGA_NDB_1_5 (0x2 << RK3308_AGC_MAX_GAIN_PGA_SFT) + #define RK3308_AGC_MAX_GAIN_PGA_NDB_7_5 (0x1 << RK3308_AGC_MAX_GAIN_PGA_SFT) + #define RK3308_AGC_MAX_GAIN_PGA_NDB_13_5 (0x0 << RK3308_AGC_MAX_GAIN_PGA_SFT) ++#define RK3308_AGC_MIN_GAIN_PGA_MAX 0x7 ++#define RK3308_AGC_MIN_GAIN_PGA_MIN 0 + #define RK3308_AGC_MIN_GAIN_PGA_SFT 0 + #define RK3308_AGC_MIN_GAIN_PGA_MSK (0x7 << RK3308_AGC_MIN_GAIN_PGA_SFT) + #define RK3308_AGC_MIN_GAIN_PGA_PDB_24 (0x7 << RK3308_AGC_MIN_GAIN_PGA_SFT) +@@ -555,6 +592,18 @@ + #define RK3308_DAC_R_CH_BIST_SINE (0x1 << RK3308_DAC_R_CH_BIST_SFT) + #define RK3308_DAC_R_CH_BIST_RIGHT (0x0 << RK3308_DAC_R_CH_BIST_SFT) /* normal mode */ + ++/* RK3308_DAC_DIG_CON04 - REG: 0x0310 */ ++#define RK3308_DAC_MODULATOR_GAIN_SFT 4 ++#define RK3308_DAC_MODULATOR_GAIN_MSK (0x7 << RK3308_DAC_MODULATOR_GAIN_SFT) ++#define RK3308_DAC_MODULATOR_GAIN_4_8DB (0x5 << RK3308_DAC_MODULATOR_GAIN_SFT) ++#define RK3308_DAC_MODULATOR_GAIN_4_2DB (0x4 << RK3308_DAC_MODULATOR_GAIN_SFT) ++#define RK3308_DAC_MODULATOR_GAIN_3_5DB (0x3 << RK3308_DAC_MODULATOR_GAIN_SFT) ++#define RK3308_DAC_MODULATOR_GAIN_2_8DB (0x2 << RK3308_DAC_MODULATOR_GAIN_SFT) ++#define RK3308_DAC_MODULATOR_GAIN_2DB (0x1 << RK3308_DAC_MODULATOR_GAIN_SFT) ++#define RK3308_DAC_MODULATOR_GAIN_0DB (0x0 << RK3308_DAC_MODULATOR_GAIN_SFT) ++#define RK3308_DAC_CIC_IF_GAIN_SFT 0 ++#define RK3308_DAC_CIC_IF_GAIN_MSK (0x7 << RK3308_DAC_CIC_IF_GAIN_SFT) ++ + /* RK3308_DAC_DIG_CON05 - REG: 0x0314 */ + #define RK3308_DAC_L_REG_CTL_INDATA (0x1 << 2) + #define RK3308_DAC_L_NORMAL_DATA (0x0 << 2) +@@ -587,18 +636,30 @@ + #define RK3308_ADC_CH1_BUF_REF_EN (0x1 << 0) + #define RK3308_ADC_CH1_BUF_REF_DIS (0x0 << 0) + +-/* RK3308_ADC_ANA_CON01 - REG: 0x0344 */ ++/* RK3308_ADC_ANA_CON01 - REG: 0x0344 ++ * ++ * The PGA of MIC-INs: ++ * 0x0 - MIC1~MIC8 0dB ++ * 0x1 - MIC1~MIC8 6.6dB ++ * 0x2 - MIC1~MIC8 13dB ++ * 0x3 - MIC1~MIC8 20dB ++ */ ++#define RK3308_ADC_CH2_MIC_GAIN_MAX 0x3 ++#define RK3308_ADC_CH2_MIC_GAIN_MIN 0 + #define RK3308_ADC_CH2_MIC_GAIN_SFT 4 + #define RK3308_ADC_CH2_MIC_GAIN_MSK (0x3 << RK3308_ADC_CH2_MIC_GAIN_SFT) +-#define RK3308_ADC_CH2_MIC_GAIN_30DB (0x3 << RK3308_ADC_CH2_MIC_GAIN_SFT) +-#define RK3308_ADC_CH2_MIC_GAIN_20DB (0x2 << RK3308_ADC_CH2_MIC_GAIN_SFT) +-#define RK3308_ADC_CH2_MIC_GAIN_6DB (0x1 << RK3308_ADC_CH2_MIC_GAIN_SFT) ++#define RK3308_ADC_CH2_MIC_GAIN_20DB (0x3 << RK3308_ADC_CH2_MIC_GAIN_SFT) ++#define RK3308_ADC_CH2_MIC_GAIN_13DB (0x2 << RK3308_ADC_CH2_MIC_GAIN_SFT) /* TRM: only used for version B */ ++#define RK3308_ADC_CH2_MIC_GAIN_6_6DB (0x1 << RK3308_ADC_CH2_MIC_GAIN_SFT) /* TRM: only used for version B */ + #define RK3308_ADC_CH2_MIC_GAIN_0DB (0x0 << RK3308_ADC_CH2_MIC_GAIN_SFT) ++ ++#define RK3308_ADC_CH1_MIC_GAIN_MAX 0x3 ++#define RK3308_ADC_CH1_MIC_GAIN_MIN 0 + #define RK3308_ADC_CH1_MIC_GAIN_SFT 0 + #define RK3308_ADC_CH1_MIC_GAIN_MSK (0x3 << RK3308_ADC_CH1_MIC_GAIN_SFT) +-#define RK3308_ADC_CH1_MIC_GAIN_30DB (0x3 << RK3308_ADC_CH1_MIC_GAIN_SFT) +-#define RK3308_ADC_CH1_MIC_GAIN_20DB (0x2 << RK3308_ADC_CH1_MIC_GAIN_SFT) +-#define RK3308_ADC_CH1_MIC_GAIN_6DB (0x1 << RK3308_ADC_CH1_MIC_GAIN_SFT) ++#define RK3308_ADC_CH1_MIC_GAIN_20DB (0x3 << RK3308_ADC_CH1_MIC_GAIN_SFT) ++#define RK3308_ADC_CH1_MIC_GAIN_13DB (0x2 << RK3308_ADC_CH1_MIC_GAIN_SFT) /* TRM: only used for version B */ ++#define RK3308_ADC_CH1_MIC_GAIN_6_6DB (0x1 << RK3308_ADC_CH1_MIC_GAIN_SFT) /* TRM: only used for version B */ + #define RK3308_ADC_CH1_MIC_GAIN_0DB (0x0 << RK3308_ADC_CH1_MIC_GAIN_SFT) + + /* RK3308_ADC_ANA_CON02 - REG: 0x0348 */ +@@ -619,6 +680,8 @@ + #define RK3308_ADC_CH1_ALC_DIS (0x0 << 0) + + /* RK3308_ADC_ANA_CON03 - REG: 0x034c */ ++#define RK3308_ADC_CH1_ALC_GAIN_MAX 0x1f ++#define RK3308_ADC_CH1_ALC_GAIN_MIN 0 + #define RK3308_ADC_CH1_ALC_GAIN_SFT 0 + #define RK3308_ADC_CH1_ALC_GAIN_MSK (0x1f << RK3308_ADC_CH1_ALC_GAIN_SFT) + #define RK3308_ADC_CH1_ALC_GAIN_PDB_28_5 (0x1f << RK3308_ADC_CH1_ALC_GAIN_SFT) +@@ -655,6 +718,8 @@ + #define RK3308_ADC_CH1_ALC_GAIN_NDB_18 (0x00 << RK3308_ADC_CH1_ALC_GAIN_SFT) + + /* RK3308_ADC_ANA_CON04 - REG: 0x0350 */ ++#define RK3308_ADC_CH2_ALC_GAIN_MAX 0x1f ++#define RK3308_ADC_CH2_ALC_GAIN_MIN 0 + #define RK3308_ADC_CH2_ALC_GAIN_SFT 0 + #define RK3308_ADC_CH2_ALC_GAIN_MSK (0x1f << RK3308_ADC_CH2_ALC_GAIN_SFT) + #define RK3308_ADC_CH2_ALC_GAIN_PDB_28_5 (0x1f << RK3308_ADC_CH2_ALC_GAIN_SFT) +@@ -728,10 +793,16 @@ + #define RK3308_ADC_CH1_IN_MIC (0x1 << RK3308_ADC_CH1_IN_SEL_SFT) + #define RK3308_ADC_CH1_IN_NONE (0x0 << RK3308_ADC_CH1_IN_SEL_SFT) + +-#define RK3308_ADC_MIC_BIAS_BUF_EN (0x1 << 3) +-#define RK3308_ADC_MIC_BIAS_BUF_DIS (0x0 << 3) ++#define RK3308_ADC_MIC_BIAS_BUF_SFT 3 ++#define RK3308_ADC_MIC_BIAS_BUF_EN (0x1 << RK3308_ADC_MIC_BIAS_BUF_SFT) ++#define RK3308_ADC_MIC_BIAS_BUF_DIS (0x0 << RK3308_ADC_MIC_BIAS_BUF_SFT) + #define RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT 0 + #define RK3308_ADC_LEVEL_RANGE_MICBIAS_MSK (0x7 << RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT) ++/* ++ * The follow MICBIAS_VOLTs are based on the external reference voltage(Vref). ++ * For example, the Vref == 3.3V, the MICBIAS_VOLT_0_85 is equal: ++ * 3.3V * 0.85 = 2.805V. ++ */ + #define RK3308_ADC_MICBIAS_VOLT_0_85 (0x7 << RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT) + #define RK3308_ADC_MICBIAS_VOLT_0_8 (0x6 << RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT) + #define RK3308_ADC_MICBIAS_VOLT_0_75 (0x5 << RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT) +@@ -751,18 +822,11 @@ + #define RK3308_ADC_REF_DIS (0x0 << 7) + #define RK3308_ADC_CURRENT_CHARGE_SFT 0 + #define RK3308_ADC_CURRENT_CHARGE_MSK (0x7f << RK3308_ADC_CURRENT_CHARGE_SFT) +-#define RK3308_ADC_DONT_SEL_ALL (0x7f << RK3308_ADC_CURRENT_CHARGE_SFT) + /* +- * 0: Choose the current I +- * 1: Don't choose the current I ++ * 1: Choose the current I ++ * 0: Don't choose the current I + */ +-#define RK3308_ADC_SEL_I_1(x) ((x & 0x1) << 6) +-#define RK3308_ADC_SEL_I_2(x) ((x & 0x1) << 5) +-#define RK3308_ADC_SEL_I_4(x) ((x & 0x1) << 4) +-#define RK3308_ADC_SEL_I_8(x) ((x & 0x1) << 3) +-#define RK3308_ADC_SEL_I_16(x) ((x & 0x1) << 2) +-#define RK3308_ADC_SEL_I_32(x) ((x & 0x1) << 1) +-#define RK3308_ADC_SEL_I_64(x) ((x & 0x1) << 0) ++#define RK3308_ADC_SEL_I(x) (x & 0x7f) + + /* RK3308_ADC_ANA_CON11 - REG: 0x036c */ + #define RK3308_ADC_ALCR_CON_GAIN_PGAR_MSK (0x1 << 1) +@@ -773,6 +837,7 @@ + #define RK3308_ADC_ALCL_CON_GAIN_PGAL_DIS (0x0 << 0) + + /* RK3308_DAC_ANA_CON00 - REG: 0x0440 */ ++#define RK3308_DAC_HEADPHONE_DET_MSK (0x1 << 1) + #define RK3308_DAC_HEADPHONE_DET_EN (0x1 << 1) + #define RK3308_DAC_HEADPHONE_DET_DIS (0x0 << 1) + #define RK3308_DAC_CURRENT_MSK (0x1 << 0) +@@ -783,17 +848,17 @@ + #define RK3308_DAC_BUF_REF_R_MSK (0x1 << 6) + #define RK3308_DAC_BUF_REF_R_EN (0x1 << 6) + #define RK3308_DAC_BUF_REF_R_DIS (0x0 << 6) +-#define RK3308_DAC_POP_SOUND_R_SFT 4 +-#define RK3308_DAC_POP_SOUND_R_MSK (0x3 << RK3308_DAC_POP_SOUND_R_SFT) +-#define RK3308_DAC_POP_SOUND_R_WORK (0x2 << RK3308_DAC_POP_SOUND_R_SFT) +-#define RK3308_DAC_POP_SOUND_R_INIT (0x1 << RK3308_DAC_POP_SOUND_R_SFT) ++#define RK3308_DAC_HPOUT_POP_SOUND_R_SFT 4 ++#define RK3308_DAC_HPOUT_POP_SOUND_R_MSK (0x3 << RK3308_DAC_HPOUT_POP_SOUND_R_SFT) ++#define RK3308_DAC_HPOUT_POP_SOUND_R_WORK (0x2 << RK3308_DAC_HPOUT_POP_SOUND_R_SFT) ++#define RK3308_DAC_HPOUT_POP_SOUND_R_INIT (0x1 << RK3308_DAC_HPOUT_POP_SOUND_R_SFT) + #define RK3308_DAC_BUF_REF_L_MSK (0x1 << 2) + #define RK3308_DAC_BUF_REF_L_EN (0x1 << 2) + #define RK3308_DAC_BUF_REF_L_DIS (0x0 << 2) +-#define RK3308_DAC_POP_SOUND_L_SFT 0 +-#define RK3308_DAC_POP_SOUND_L_MSK (0x3 << RK3308_DAC_POP_SOUND_L_SFT) +-#define RK3308_DAC_POP_SOUND_L_WORK (0x2 << RK3308_DAC_POP_SOUND_L_SFT) +-#define RK3308_DAC_POP_SOUND_L_INIT (0x1 << RK3308_DAC_POP_SOUND_L_SFT) ++#define RK3308_DAC_HPOUT_POP_SOUND_L_SFT 0 ++#define RK3308_DAC_HPOUT_POP_SOUND_L_MSK (0x3 << RK3308_DAC_HPOUT_POP_SOUND_L_SFT) ++#define RK3308_DAC_HPOUT_POP_SOUND_L_WORK (0x2 << RK3308_DAC_HPOUT_POP_SOUND_L_SFT) ++#define RK3308_DAC_HPOUT_POP_SOUND_L_INIT (0x1 << RK3308_DAC_HPOUT_POP_SOUND_L_SFT) + + /* RK3308_DAC_ANA_CON02 - REG: 0x0448 */ + #define RK3308_DAC_R_DAC_WORK (0x1 << 7) +@@ -828,28 +893,31 @@ + #define RK3308_DAC_L_HPOUT_MUTE (0x0 << 0) + + /* RK3308_DAC_ANA_CON04 - REG: 0x0450 */ +-#define RK3308_DAC_R_GAIN_SFT 6 +-#define RK3308_DAC_R_GAIN_MSK (0x3 << RK3308_DAC_R_GAIN_SFT) +-#define RK3308_DAC_R_GAIN_0DB (0x3 << RK3308_DAC_R_GAIN_SFT) +-#define RK3308_DAC_R_GAIN_PDB_1_5 (0x2 << RK3308_DAC_R_GAIN_SFT) +-#define RK3308_DAC_R_GAIN_PDB_3 (0x1 << RK3308_DAC_R_GAIN_SFT) +-#define RK3308_DAC_R_GAIN_PDB_6 (0x0 << RK3308_DAC_R_GAIN_SFT) ++#define RK3308_DAC_R_LINEOUT_GAIN_MAX 0x3 ++#define RK3308_DAC_R_LINEOUT_GAIN_SFT 6 ++#define RK3308_DAC_R_LINEOUT_GAIN_MSK (0x3 << RK3308_DAC_R_LINEOUT_GAIN_SFT) ++#define RK3308_DAC_R_LINEOUT_GAIN_0DB (0x3 << RK3308_DAC_R_LINEOUT_GAIN_SFT) ++#define RK3308_DAC_R_LINEOUT_GAIN_NDB_1_5 (0x2 << RK3308_DAC_R_LINEOUT_GAIN_SFT) ++#define RK3308_DAC_R_LINEOUT_GAIN_NDB_3 (0x1 << RK3308_DAC_R_LINEOUT_GAIN_SFT) ++#define RK3308_DAC_R_LINEOUT_GAIN_NDB_6 (0x0 << RK3308_DAC_R_LINEOUT_GAIN_SFT) + #define RK3308_DAC_R_LINEOUT_UNMUTE (0x1 << 5) + #define RK3308_DAC_R_LINEOUT_MUTE (0x0 << 5) + #define RK3308_DAC_R_LINEOUT_EN (0x1 << 4) + #define RK3308_DAC_R_LINEOUT_DIS (0x0 << 4) +-#define RK3308_DAC_L_GAIN_SFT 2 +-#define RK3308_DAC_L_GAIN_MSK (0x3 << RK3308_DAC_L_GAIN_SFT) +-#define RK3308_DAC_L_GAIN_0DB (0x3 << RK3308_DAC_L_GAIN_SFT) +-#define RK3308_DAC_L_GAIN_PDB_1_5 (0x2 << RK3308_DAC_L_GAIN_SFT) +-#define RK3308_DAC_L_GAIN_PDB_3 (0x1 << RK3308_DAC_L_GAIN_SFT) +-#define RK3308_DAC_L_GAIN_PDB_6 (0x0 << RK3308_DAC_L_GAIN_SFT) ++#define RK3308_DAC_L_LINEOUT_GAIN_MAX 0x3 ++#define RK3308_DAC_L_LINEOUT_GAIN_SFT 2 ++#define RK3308_DAC_L_LINEOUT_GAIN_MSK (0x3 << RK3308_DAC_L_LINEOUT_GAIN_SFT) ++#define RK3308_DAC_L_LINEOUT_GAIN_0DB (0x3 << RK3308_DAC_L_LINEOUT_GAIN_SFT) ++#define RK3308_DAC_L_LINEOUT_GAIN_NDB_1_5 (0x2 << RK3308_DAC_L_LINEOUT_GAIN_SFT) ++#define RK3308_DAC_L_LINEOUT_GAIN_NDB_3 (0x1 << RK3308_DAC_L_LINEOUT_GAIN_SFT) ++#define RK3308_DAC_L_LINEOUT_GAIN_NDB_6 (0x0 << RK3308_DAC_L_LINEOUT_GAIN_SFT) + #define RK3308_DAC_L_LINEOUT_UNMUTE (0x1 << 1) + #define RK3308_DAC_L_LINEOUT_MUTE (0x0 << 1) + #define RK3308_DAC_L_LINEOUT_EN (0x1 << 0) + #define RK3308_DAC_L_LINEOUT_DIS (0x0 << 0) + + /* RK3308_DAC_ANA_CON05 - REG: 0x0454, step is 1.5db */ ++#define RK3308_DAC_L_HPOUT_GAIN_MAX 0x1e + #define RK3308_DAC_L_HPOUT_GAIN_SFT 0 + #define RK3308_DAC_L_HPOUT_GAIN_MSK (0x1f << RK3308_DAC_L_HPOUT_GAIN_SFT) + #define RK3308_DAC_L_HPOUT_GAIN_PDB_6 (0x1e << RK3308_DAC_L_HPOUT_GAIN_SFT) +@@ -885,6 +953,7 @@ + #define RK3308_DAC_L_HPOUT_GAIN_NDB_39 (0x00 << RK3308_DAC_L_HPOUT_GAIN_SFT) + + /* RK3308_DAC_ANA_CON06 - REG: 0x0458, step is 1.5db */ ++#define RK3308_DAC_R_HPOUT_GAIN_MAX 0x1e + #define RK3308_DAC_R_HPOUT_GAIN_SFT 0 + #define RK3308_DAC_R_HPOUT_GAIN_MSK (0x1f << RK3308_DAC_R_HPOUT_GAIN_SFT) + #define RK3308_DAC_R_HPOUT_GAIN_PDB_6 (0x1e << RK3308_DAC_R_HPOUT_GAIN_SFT) +@@ -919,6 +988,18 @@ + #define RK3308_DAC_R_HPOUT_GAIN_NDB_37_5 (0x01 << RK3308_DAC_R_HPOUT_GAIN_SFT) + #define RK3308_DAC_R_HPOUT_GAIN_NDB_39 (0x00 << RK3308_DAC_R_HPOUT_GAIN_SFT) + ++/* RK3308_DAC_ANA_CON07 - REG: 0x045c */ ++#define RK3308_DAC_R_HPOUT_DRV_SFT 4 ++#define RK3308_DAC_R_HPOUT_DRV_MSK (0xf << RK3308_DAC_R_HPOUT_DRV_SFT) ++#define RK3308_DAC_L_HPOUT_DRV_SFT 0 ++#define RK3308_DAC_L_HPOUT_DRV_MSK (0xf << RK3308_DAC_L_HPOUT_DRV_SFT) ++ ++/* RK3308_DAC_ANA_CON08 - REG: 0x0460 */ ++#define RK3308_DAC_R_LINEOUT_DRV_SFT 4 ++#define RK3308_DAC_R_LINEOUT_DRV_MSK (0xf << RK3308_DAC_R_LINEOUT_DRV_SFT) ++#define RK3308_DAC_L_LINEOUT_DRV_SFT 0 ++#define RK3308_DAC_L_LINEOUT_DRV_MSK (0xf << RK3308_DAC_L_LINEOUT_DRV_SFT) ++ + /* RK3308_DAC_ANA_CON12 - REG: 0x0470 */ + #define RK3308_DAC_R_HPMIX_SEL_SFT 6 + #define RK3308_DAC_R_HPMIX_SEL_MSK (0x3 << RK3308_DAC_R_HPMIX_SEL_SFT) +@@ -926,6 +1007,8 @@ + #define RK3308_DAC_R_HPMIX_LINEIN (0x2 << RK3308_DAC_R_HPMIX_SEL_SFT) + #define RK3308_DAC_R_HPMIX_I2S (0x1 << RK3308_DAC_R_HPMIX_SEL_SFT) + #define RK3308_DAC_R_HPMIX_NONE (0x0 << RK3308_DAC_R_HPMIX_SEL_SFT) ++#define RK3308_DAC_R_HPMIX_GAIN_MIN 0x1 ++#define RK3308_DAC_R_HPMIX_GAIN_MAX 0x2 + #define RK3308_DAC_R_HPMIX_GAIN_SFT 4 + #define RK3308_DAC_R_HPMIX_GAIN_MSK (0x3 << RK3308_DAC_R_HPMIX_GAIN_SFT) + #define RK3308_DAC_R_HPMIX_GAIN_0DB (0x2 << RK3308_DAC_R_HPMIX_GAIN_SFT) +@@ -936,6 +1019,8 @@ + #define RK3308_DAC_L_HPMIX_LINEIN (0x2 << RK3308_DAC_L_HPMIX_SEL_SFT) + #define RK3308_DAC_L_HPMIX_I2S (0x1 << RK3308_DAC_L_HPMIX_SEL_SFT) + #define RK3308_DAC_L_HPMIX_NONE (0x0 << RK3308_DAC_L_HPMIX_SEL_SFT) ++#define RK3308_DAC_L_HPMIX_GAIN_MIN 0x1 ++#define RK3308_DAC_L_HPMIX_GAIN_MAX 0x2 + #define RK3308_DAC_L_HPMIX_GAIN_SFT 0 + #define RK3308_DAC_L_HPMIX_GAIN_MSK (0x3 << RK3308_DAC_L_HPMIX_GAIN_SFT) + #define RK3308_DAC_L_HPMIX_GAIN_0DB (0x2 << RK3308_DAC_L_HPMIX_GAIN_SFT) +@@ -955,6 +1040,30 @@ + #define RK3308_DAC_L_HPMIX_EN (0x1 << 0) + #define RK3308_DAC_L_HPMIX_DIS (0x0 << 0) + ++/* RK3308_DAC_ANA_CON14 - REG: 0x0478 */ ++#define RK3308_DAC_VCM_LINEOUT_EN (0x1 << 4) ++#define RK3308_DAC_VCM_LINEOUT_DIS (0x0 << 4) ++#define RK3308_DAC_CURRENT_CHARGE_SFT 0 ++#define RK3308_DAC_CURRENT_CHARGE_MSK (0xf << RK3308_DAC_CURRENT_CHARGE_SFT) ++ ++/* ++ * 1: Choose the current I ++ * 0: Don't choose the current I ++ */ ++#define RK3308_DAC_SEL_I(x) (x & 0xf) ++ ++/* RK3308_DAC_ANA_CON15 - REG: 0x047C */ ++#define RK3308_DAC_LINEOUT_POP_SOUND_R_SFT 4 ++#define RK3308_DAC_LINEOUT_POP_SOUND_R_MSK (0x3 << RK3308_DAC_LINEOUT_POP_SOUND_R_SFT) ++#define RK3308_DAC_R_SEL_DC_FROM_INTERNAL (0x2 << RK3308_DAC_LINEOUT_POP_SOUND_R_SFT) ++#define RK3308_DAC_R_SEL_DC_FROM_VCM (0x1 << RK3308_DAC_LINEOUT_POP_SOUND_R_SFT) ++#define RK3308_DAC_R_SEL_LINEOUT_FROM_INTERNAL (0x0 << RK3308_DAC_LINEOUT_POP_SOUND_R_SFT) ++#define RK3308_DAC_LINEOUT_POP_SOUND_L_SFT 0 ++#define RK3308_DAC_LINEOUT_POP_SOUND_L_MSK (0x3 << RK3308_DAC_LINEOUT_POP_SOUND_L_SFT) ++#define RK3308_DAC_L_SEL_DC_FROM_INTERNAL (0x2 << RK3308_DAC_LINEOUT_POP_SOUND_L_SFT) ++#define RK3308_DAC_L_SEL_DC_FROM_VCM (0x1 << RK3308_DAC_LINEOUT_POP_SOUND_L_SFT) ++#define RK3308_DAC_L_SEL_LINEOUT_FROM_INTERNAL (0x0 << RK3308_DAC_LINEOUT_POP_SOUND_L_SFT) ++ + #define RK3308_HIFI 0x0 + + #endif /* __RK3308_CODEC_H__ */ +diff --git a/sound/soc/codecs/rk3308_codec_provider.h b/sound/soc/codecs/rk3308_codec_provider.h +new file mode 100644 +index 000000000000..68042b1328dc +--- /dev/null ++++ b/sound/soc/codecs/rk3308_codec_provider.h +@@ -0,0 +1,28 @@ ++/* ++ * rk3308_codec_provider.h -- RK3308 ALSA Soc Audio Driver ++ * ++ * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ */ ++ ++#ifndef __RK3308_CODEC_PROVIDER_H__ ++#define __RK3308_CODEC_PROVIDER_H__ ++ ++#ifdef CONFIG_SND_SOC_RK3308 ++extern void (*rk3308_codec_set_jack_detect_cb)(struct snd_soc_codec *codec, ++ struct snd_soc_jack *hpdet_jack); ++#endif ++ ++#endif /* __RK3308_CODEC_PROVIDER_H__ */ +-- +2.25.1 + diff --git a/sys-kernel/decade-sources/files/patches-5.14/board-rockpis-0022-ASoC-rk3308_codec-replace-codec-to-component.patch b/sys-kernel/decade-sources/files/patches-5.14/board-rockpis-0022-ASoC-rk3308_codec-replace-codec-to-component.patch new file mode 100644 index 0000000..0223222 --- /dev/null +++ b/sys-kernel/decade-sources/files/patches-5.14/board-rockpis-0022-ASoC-rk3308_codec-replace-codec-to-component.patch @@ -0,0 +1,459 @@ +From b882c2185ab561ec88c2540623cfa49e2cb56956 Mon Sep 17 00:00:00 2001 +From: ashthespy +Date: Mon, 3 Feb 2020 19:35:42 +0100 +Subject: [PATCH 22/23] ASoC: rk3308_codec: replace codec to component + +--- + sound/soc/codecs/rk3308_codec.c | 159 ++++++++++++----------- + sound/soc/codecs/rk3308_codec_provider.h | 2 +- + 2 files changed, 84 insertions(+), 77 deletions(-) + +diff --git a/sound/soc/codecs/rk3308_codec.c b/sound/soc/codecs/rk3308_codec.c +index 815e22fc346c..16bfb215586e 100644 +--- a/sound/soc/codecs/rk3308_codec.c ++++ b/sound/soc/codecs/rk3308_codec.c +@@ -31,7 +31,7 @@ + #include + #include + #include +-#include ++// #include + #include + #include + #include +@@ -156,7 +156,7 @@ struct rk3308_codec_priv { + struct gpio_desc *hp_ctl_gpio; + struct gpio_desc *spk_ctl_gpio; + struct gpio_desc *pa_drv_gpio; +- struct snd_soc_codec *codec; ++ struct snd_soc_component *component; + struct snd_soc_jack *hpdet_jack; + struct regulator *vcc_micbias; + u32 codec_ver; +@@ -883,8 +883,8 @@ static const struct snd_kcontrol_new rk3308_codec_dapm_controls[] = { + static int rk3308_codec_agc_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) + { +- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); ++ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + if (e->reg < 0 || e->reg > ADC_LR_GROUP_MAX - 1) { +@@ -904,8 +904,8 @@ static int rk3308_codec_agc_get(struct snd_kcontrol *kcontrol, + static int rk3308_codec_agc_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) + { +- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); ++ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int value = ucontrol->value.integer.value[0]; + int grp = e->reg; +@@ -970,8 +970,8 @@ static int rk3308_codec_agc_put(struct snd_kcontrol *kcontrol, + static int rk3308_codec_agc_asr_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) + { +- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); ++ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int value; + int grp = e->reg; +@@ -998,8 +998,8 @@ static int rk3308_codec_agc_asr_get(struct snd_kcontrol *kcontrol, + static int rk3308_codec_agc_asr_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) + { +- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); ++ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int value; + int grp = e->reg; +@@ -1032,8 +1032,8 @@ static int rk3308_codec_agc_asr_put(struct snd_kcontrol *kcontrol, + static int rk3308_codec_mic_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) + { +- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); ++ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int value; + int grp = e->reg; +@@ -1064,8 +1064,8 @@ static int rk3308_codec_mic_mute_get(struct snd_kcontrol *kcontrol, + static int rk3308_codec_mic_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) + { +- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); ++ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int value; + int grp = e->reg; +@@ -1098,8 +1098,8 @@ static int rk3308_codec_mic_mute_put(struct snd_kcontrol *kcontrol, + static int rk3308_codec_micbias_volts_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) + { +- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); ++ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rk3308->micbias_volt; + +@@ -1109,8 +1109,8 @@ static int rk3308_codec_micbias_volts_get(struct snd_kcontrol *kcontrol, + static int rk3308_codec_micbias_volts_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) + { +- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); ++ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + unsigned int volt = ucontrol->value.integer.value[0]; + int ret; + +@@ -1133,8 +1133,8 @@ static int rk3308_codec_micbias_volts_put(struct snd_kcontrol *kcontrol, + static int rk3308_codec_main_micbias_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) + { +- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); ++ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rk3308->enable_micbias; + +@@ -1144,8 +1144,8 @@ static int rk3308_codec_main_micbias_get(struct snd_kcontrol *kcontrol, + static int rk3308_codec_main_micbias_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) + { +- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); ++ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + unsigned int on = ucontrol->value.integer.value[0]; + + if (on) { +@@ -1168,8 +1168,8 @@ static int rk3308_codec_mic_gain_get(struct snd_kcontrol *kcontrol, + static int rk3308_codec_mic_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) + { +- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); ++ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + unsigned int gain = ucontrol->value.integer.value[0]; + + if (gain > RK3308_ADC_CH1_MIC_GAIN_MAX) { +@@ -1197,8 +1197,8 @@ static int rk3308_codec_mic_gain_put(struct snd_kcontrol *kcontrol, + static int rk3308_codec_hpf_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) + { +- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); ++ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int value; + +@@ -1222,8 +1222,8 @@ static int rk3308_codec_hpf_get(struct snd_kcontrol *kcontrol, + static int rk3308_codec_hpf_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) + { +- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); ++ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int value = ucontrol->value.integer.value[0]; + +@@ -1259,8 +1259,8 @@ static int rk3308_codec_hpout_l_get_tlv(struct snd_kcontrol *kcontrol, + static int rk3308_codec_hpout_l_put_tlv(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) + { +- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); ++ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + unsigned int dgain = ucontrol->value.integer.value[0]; + + if (dgain > RK3308_DAC_L_HPOUT_GAIN_MAX) { +@@ -1283,8 +1283,8 @@ static int rk3308_codec_hpout_r_get_tlv(struct snd_kcontrol *kcontrol, + static int rk3308_codec_hpout_r_put_tlv(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) + { +- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); ++ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + unsigned int dgain = ucontrol->value.integer.value[0]; + + if (dgain > RK3308_DAC_R_HPOUT_GAIN_MAX) { +@@ -1408,9 +1408,9 @@ static void rk3308_speaker_ctl(struct rk3308_codec_priv *rk3308, int on) + } + } + +-static int rk3308_codec_reset(struct snd_soc_codec *codec) ++static int rk3308_codec_reset(struct snd_soc_component *component) + { +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + + reset_control_assert(rk3308->reset); + usleep_range(2000, 2500); /* estimated value */ +@@ -1452,10 +1452,10 @@ static int rk3308_codec_dac_dig_reset(struct rk3308_codec_priv *rk3308) + return 0; + } + +-static int rk3308_set_bias_level(struct snd_soc_codec *codec, ++static int rk3308_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) + { +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: +@@ -1473,11 +1473,11 @@ static int rk3308_set_bias_level(struct snd_soc_codec *codec, + return 0; + } + +-static int rk3308_set_dai_fmt(struct snd_soc_dai *codec_dai, ++static int rk3308_set_dai_fmt(struct snd_soc_dai *dai, + unsigned int fmt) + { +- struct snd_soc_codec *codec = codec_dai->codec; +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct snd_soc_component *component = dai->component; ++ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + unsigned int adc_aif1 = 0, adc_aif2 = 0, dac_aif1 = 0, dac_aif2 = 0; + int idx, grp, is_master; + int type = ADC_TYPE_ALL; +@@ -1721,8 +1721,8 @@ static int rk3308_codec_update_adc_grps(struct rk3308_codec_priv *rk3308, + + static int rk3308_mute_stream(struct snd_soc_dai *dai, int mute, int stream) + { +- struct snd_soc_codec *codec = dai->codec; +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct snd_soc_component *component = dai->component; ++ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + int dgain; +@@ -3630,8 +3630,8 @@ static int rk3308_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) + { +- struct snd_soc_codec *codec = dai->codec; +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct snd_soc_component *component = dai->component; ++ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + struct snd_pcm_str *playback_str = + &substream->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]; + int type = ADC_TYPE_LOOPBACK; +@@ -3705,8 +3705,8 @@ static int rk3308_hw_params(struct snd_pcm_substream *substream, + static int rk3308_pcm_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) + { +- struct snd_soc_codec *codec = dai->codec; +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct snd_soc_component *component = dai->component; ++ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + int type = ADC_TYPE_LOOPBACK; + int idx, grp; + +@@ -3749,8 +3749,8 @@ static int rk3308_pcm_trigger(struct snd_pcm_substream *substream, + static void rk3308_pcm_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + { +- struct snd_soc_codec *codec = dai->codec; +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct snd_soc_component *component = dai->component; ++ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + rk3308_codec_close_playback(rk3308); +@@ -3809,9 +3809,9 @@ static struct snd_soc_dai_driver rk3308_dai[] = { + }, + }; + +-static int rk3308_suspend(struct snd_soc_codec *codec) ++static int rk3308_suspend(struct snd_soc_component *component) + { +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + + if (rk3308->no_deep_low_power) + goto out; +@@ -3822,13 +3822,13 @@ static int rk3308_suspend(struct snd_soc_codec *codec) + clk_disable_unprepare(rk3308->pclk); + + out: +- rk3308_set_bias_level(codec, SND_SOC_BIAS_OFF); ++ rk3308_set_bias_level(component, SND_SOC_BIAS_OFF); + return 0; + } + +-static int rk3308_resume(struct snd_soc_codec *codec) ++static int rk3308_resume(struct snd_soc_component *component) + { +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + int ret = 0; + + if (rk3308->no_deep_low_power) +@@ -3857,7 +3857,7 @@ static int rk3308_resume(struct snd_soc_codec *codec) + + rk3308_codec_dlp_up(rk3308); + out: +- rk3308_set_bias_level(codec, SND_SOC_BIAS_STANDBY); ++ rk3308_set_bias_level(component, SND_SOC_BIAS_STANDBY); + return ret; + } + +@@ -3972,7 +3972,7 @@ static int rk3308_codec_dapm_mic_gains(struct rk3308_codec_priv *rk3308) + int ret; + + if (rk3308->codec_ver == ACODEC_VERSION_B) { +- ret = snd_soc_add_codec_controls(rk3308->codec, ++ ret = snd_soc_add_component_controls(rk3308->component, + mic_gains_b, + ARRAY_SIZE(mic_gains_b)); + if (ret) { +@@ -3982,7 +3982,7 @@ static int rk3308_codec_dapm_mic_gains(struct rk3308_codec_priv *rk3308) + return ret; + } + } else { +- ret = snd_soc_add_codec_controls(rk3308->codec, ++ ret = snd_soc_add_component_controls(rk3308->component, + mic_gains_a, + ARRAY_SIZE(mic_gains_a)); + if (ret) { +@@ -4081,15 +4081,15 @@ static int rk3308_codec_prepare(struct rk3308_codec_priv *rk3308) + return 0; + } + +-static int rk3308_probe(struct snd_soc_codec *codec) ++static int rk3308_probe(struct snd_soc_component *component) + { +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + int ext_micbias; + +- rk3308->codec = codec; ++ rk3308->component = component; + rk3308_codec_set_dac_path_state(rk3308, PATH_IDLE); + +- rk3308_codec_reset(codec); ++ rk3308_codec_reset(component); + rk3308_codec_power_on(rk3308); + + /* From vendor recommend, disable micbias at first. */ +@@ -4108,9 +4108,9 @@ static int rk3308_probe(struct snd_soc_codec *codec) + return 0; + } + +-static int rk3308_remove(struct snd_soc_codec *codec) ++static void rk3308_remove(struct snd_soc_component *component) + { +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + + rk3308_headphone_ctl(rk3308, 0); + rk3308_speaker_ctl(rk3308, 0); +@@ -4124,17 +4124,25 @@ static int rk3308_remove(struct snd_soc_codec *codec) + regcache_cache_only(rk3308->regmap, false); + regcache_sync(rk3308->regmap); + +- return 0; + } + +-static struct snd_soc_codec_driver soc_codec_dev_rk3308 = { +- .probe = rk3308_probe, +- .remove = rk3308_remove, +- .suspend = rk3308_suspend, +- .resume = rk3308_resume, +- .set_bias_level = rk3308_set_bias_level, +- .controls = rk3308_codec_dapm_controls, +- .num_controls = ARRAY_SIZE(rk3308_codec_dapm_controls), ++static const struct snd_soc_component_driver soc_codec_dev_rk3308_component = { ++ .probe = rk3308_probe, ++ .remove = rk3308_remove, ++ .resume = rk3308_resume, ++ .suspend = rk3308_suspend, ++ .set_bias_level = rk3308_set_bias_level, ++ .controls = rk3308_codec_dapm_controls, ++ .num_controls = ARRAY_SIZE(rk3308_codec_dapm_controls), ++ // .dapm_widgets = rk3308_dapm_widgets, ++ // .num_dapm_widgets = ARRAY_SIZE(rk3308_dapm_widgets), ++ // .dapm_routes = rk3308_dapm_routes, ++ // .num_dapm_routes = ARRAY_SIZE(rk3308_dapm_routes), ++ // .suspend_bias_off = 1, ++ // .idle_bias_on = 1, ++ // .use_pmdown_time = 1, ++ .endianness = 1, ++ .non_legacy_dai_naming = 1, + }; + + static const struct reg_default rk3308_codec_reg_defaults[] = { +@@ -4299,14 +4307,14 @@ static irqreturn_t rk3308_codec_hpdet_isr(int irq, void *data) + return IRQ_HANDLED; + } + +-void (*rk3308_codec_set_jack_detect_cb)(struct snd_soc_codec *codec, ++void (*rk3308_codec_set_jack_detect_cb)(struct snd_soc_component *component, + struct snd_soc_jack *hpdet_jack); + EXPORT_SYMBOL_GPL(rk3308_codec_set_jack_detect_cb); + +-static void rk3308_codec_set_jack_detect(struct snd_soc_codec *codec, ++static void rk3308_codec_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hpdet_jack) + { +- struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); ++ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + + rk3308->hpdet_jack = hpdet_jack; + +@@ -5114,10 +5122,10 @@ static int rk3308_platform_probe(struct platform_device *pdev) + + platform_set_drvdata(pdev, rk3308); + +- ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_rk3308, ++ ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_dev_rk3308_component, + rk3308_dai, ARRAY_SIZE(rk3308_dai)); + if (ret < 0) { +- dev_err(&pdev->dev, "Failed to register codec: %d\n", ret); ++ dev_err(&pdev->dev, "Failed to register component: %d\n", ret); + goto failed; + } + +@@ -5140,7 +5148,6 @@ static int rk3308_platform_remove(struct platform_device *pdev) + clk_disable_unprepare(rk3308->mclk_rx); + clk_disable_unprepare(rk3308->mclk_tx); + clk_disable_unprepare(rk3308->pclk); +- snd_soc_unregister_codec(&pdev->dev); + device_unregister(&rk3308->dev); + + return 0; +diff --git a/sound/soc/codecs/rk3308_codec_provider.h b/sound/soc/codecs/rk3308_codec_provider.h +index 68042b1328dc..34c1ef86a507 100644 +--- a/sound/soc/codecs/rk3308_codec_provider.h ++++ b/sound/soc/codecs/rk3308_codec_provider.h +@@ -21,7 +21,7 @@ + #define __RK3308_CODEC_PROVIDER_H__ + + #ifdef CONFIG_SND_SOC_RK3308 +-extern void (*rk3308_codec_set_jack_detect_cb)(struct snd_soc_codec *codec, ++extern void (*rk3308_codec_set_jack_detect_cb)(struct snd_soc_component *component, + struct snd_soc_jack *hpdet_jack); + #endif + +-- +2.25.1 + diff --git a/sys-kernel/decade-sources/files/patches-5.14/general-emmc-hs400es-init-tweak.patch b/sys-kernel/decade-sources/files/patches-5.14/general-emmc-hs400es-init-tweak.patch new file mode 100644 index 0000000..03144ff --- /dev/null +++ b/sys-kernel/decade-sources/files/patches-5.14/general-emmc-hs400es-init-tweak.patch @@ -0,0 +1,22 @@ +This patch is required to boot some Rock Pi 4 and NanoPC T4 units +with kernel 5.3+ and is on par with how it is done in Rockchip's BSP. + +diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c +index de8fbc396..95858e554 100644 +--- a/drivers/mmc/core/mmc.c ++++ b/drivers/mmc/core/mmc.c +@@ -1371,12 +1371,12 @@ static int mmc_select_hs400es(struct mmc_card *card) + } + + mmc_set_timing(host, MMC_TIMING_MMC_HS); ++ mmc_set_clock(host, card->ext_csd.hs_max_dtr); ++ + err = mmc_switch_status(card, true); + if (err) + goto out_err; + +- mmc_set_clock(host, card->ext_csd.hs_max_dtr); +- + /* Switch card to DDR with strobe bit */ + val = EXT_CSD_DDR_BUS_WIDTH_8 | EXT_CSD_BUS_WIDTH_STROBE; + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, diff --git a/sys-kernel/decade-sources/files/patches-5.14/general-fix-es8316-kernel-panic.patch b/sys-kernel/decade-sources/files/patches-5.14/general-fix-es8316-kernel-panic.patch new file mode 100644 index 0000000..3a27c0c --- /dev/null +++ b/sys-kernel/decade-sources/files/patches-5.14/general-fix-es8316-kernel-panic.patch @@ -0,0 +1,11 @@ +--- a/sound/soc/codecs/es8316.c 2020-04-09 19:13:08.268473737 +0000 ++++ b/sound/soc/codecs/es8316.c 2020-04-09 19:14:00.535995842 +0000 +@@ -687,7 +687,7 @@ + snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE, + ES8316_GPIO_ENABLE_INTERRUPT, 0); + +- if (es8316->jack->status & SND_JACK_MICROPHONE) { ++ if (es8316->jack && (es8316->jack->status & SND_JACK_MICROPHONE)) { + es8316_disable_micbias_for_mic_gnd_short_detect(component); + snd_soc_jack_report(es8316->jack, 0, SND_JACK_BTN_0); + } diff --git a/sys-kernel/decade-sources/files/general-fix-mmc-signal-voltage-before-reboot.patch b/sys-kernel/decade-sources/files/patches-5.14/general-fix-mmc-signal-voltage-before-reboot.patch similarity index 100% rename from sys-kernel/decade-sources/files/general-fix-mmc-signal-voltage-before-reboot.patch rename to sys-kernel/decade-sources/files/patches-5.14/general-fix-mmc-signal-voltage-before-reboot.patch diff --git a/sys-kernel/decade-sources/files/patches-5.14/general-increasing_DMA_block_memory_allocation_to_2048.patch b/sys-kernel/decade-sources/files/patches-5.14/general-increasing_DMA_block_memory_allocation_to_2048.patch new file mode 100644 index 0000000..fc6c248 --- /dev/null +++ b/sys-kernel/decade-sources/files/patches-5.14/general-increasing_DMA_block_memory_allocation_to_2048.patch @@ -0,0 +1,21 @@ +diff --git a/kernel/dma/pool.c b/kernel/dma/pool.c +index 6bc74a2d5..e3827da51 100644 +--- a/kernel/dma/pool.c ++++ b/kernel/dma/pool.c +@@ -164,13 +164,11 @@ static int __init dma_atomic_pool_init(void) + int ret = 0; + + /* +- * If coherent_pool was not used on the command line, default the pool +- * sizes to 128KB per 1GB of memory, min 128KB, max MAX_ORDER-1. ++ * Always use 2MiB as default pool size. ++ * See: https://forum.armbian.com/topic/4811-uas-mainline-kernel-coherent-pool-memory-size/ + */ + if (!atomic_pool_size) { +- unsigned long pages = totalram_pages() / (SZ_1G / SZ_128K); +- pages = min_t(unsigned long, pages, MAX_ORDER_NR_PAGES); +- atomic_pool_size = max_t(size_t, pages << PAGE_SHIFT, SZ_128K); ++ atomic_pool_size = SZ_2M; + } + INIT_WORK(&atomic_pool_work, atomic_pool_work_fn); + diff --git a/sys-kernel/decade-sources/files/general-rk808-configurable-switch-voltage-steps.patch b/sys-kernel/decade-sources/files/patches-5.14/general-rk808-configurable-switch-voltage-steps.patch similarity index 100% rename from sys-kernel/decade-sources/files/general-rk808-configurable-switch-voltage-steps.patch rename to sys-kernel/decade-sources/files/patches-5.14/general-rk808-configurable-switch-voltage-steps.patch diff --git a/sys-kernel/decade-sources/files/patches-5.14/general-rt5651-add-mclk.patch b/sys-kernel/decade-sources/files/patches-5.14/general-rt5651-add-mclk.patch new file mode 100644 index 0000000..26c6320 --- /dev/null +++ b/sys-kernel/decade-sources/files/patches-5.14/general-rt5651-add-mclk.patch @@ -0,0 +1,60 @@ +diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c +index c506c9305..41a08b320 100644 +--- a/sound/soc/codecs/rt5651.c ++++ b/sound/soc/codecs/rt5651.c +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + + #include "rl6231.h" + #include "rt5651.h" +@@ -1511,6 +1512,7 @@ static int rt5651_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, + static int rt5651_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) + { ++ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (SND_SOC_BIAS_STANDBY == snd_soc_component_get_bias_level(component)) { +@@ -1518,6 +1520,13 @@ static int rt5651_set_bias_level(struct snd_soc_component *component, + snd_soc_component_update_bits(component, RT5651_D_MISC, + 0xc00, 0xc00); + } ++ if (!IS_ERR(rt5651->mclk)){ ++ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) { ++ clk_disable_unprepare(rt5651->mclk); ++ } else { ++ clk_prepare_enable(rt5651->mclk); ++ } ++ } + break; + case SND_SOC_BIAS_STANDBY: + if (SND_SOC_BIAS_OFF == snd_soc_component_get_bias_level(component)) { +@@ -2059,6 +2068,13 @@ static int rt5651_probe(struct snd_soc_component *component) + { + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + ++ /* Check if MCLK provided */ ++ rt5651->mclk = devm_clk_get(component->dev, "mclk"); ++ if (PTR_ERR(rt5651->mclk) == -EPROBE_DEFER){ ++ dev_err(component->dev, "unable to get mclk\n"); ++ return -EPROBE_DEFER; ++ } ++ + rt5651->component = component; + + snd_soc_component_update_bits(component, RT5651_PWR_ANLG1, +diff --git a/sound/soc/codecs/rt5651.h b/sound/soc/codecs/rt5651.h +index 20c33a3ec..17524fa9f 100644 +--- a/sound/soc/codecs/rt5651.h ++++ b/sound/soc/codecs/rt5651.h +@@ -2097,6 +2097,7 @@ struct rt5651_priv { + + int dmic_en; + bool hp_mute; ++ struct clk *mclk; + }; + + #endif /* __RT5651_H__ */ diff --git a/sys-kernel/decade-sources/files/patches-5.14/rk3399-add-sclk-i2sout-src-clock.patch b/sys-kernel/decade-sources/files/patches-5.14/rk3399-add-sclk-i2sout-src-clock.patch new file mode 100644 index 0000000..5573ece --- /dev/null +++ b/sys-kernel/decade-sources/files/patches-5.14/rk3399-add-sclk-i2sout-src-clock.patch @@ -0,0 +1,25 @@ +diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c +index ce1d2446f..38447441b 100644 +--- a/drivers/clk/rockchip/clk-rk3399.c ++++ b/drivers/clk/rockchip/clk-rk3399.c +@@ -620,7 +620,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { + GATE(SCLK_I2S2_8CH, "clk_i2s2", "clk_i2s2_mux", CLK_SET_RATE_PARENT, + RK3399_CLKGATE_CON(8), 11, GFLAGS), + +- MUX(0, "clk_i2sout_src", mux_i2sch_p, CLK_SET_RATE_PARENT, ++ MUX(SCLK_I2SOUT_SRC, "clk_i2sout_src", mux_i2sch_p, CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(31), 0, 2, MFLAGS), + COMPOSITE_NODIV(SCLK_I2S_8CH_OUT, "clk_i2sout", mux_i2sout_p, CLK_SET_RATE_PARENT, + RK3399_CLKSEL_CON(31), 2, 1, MFLAGS, +diff --git a/include/dt-bindings/clock/rk3399-cru.h b/include/dt-bindings/clock/rk3399-cru.h +index 44e0a319f..b7b07dfda 100644 +--- a/include/dt-bindings/clock/rk3399-cru.h ++++ b/include/dt-bindings/clock/rk3399-cru.h +@@ -19,6 +19,7 @@ + #define ARMCLKB 9 + + /* sclk gates (special clocks) */ ++#define SCLK_I2SOUT_SRC 64 + #define SCLK_I2C1 65 + #define SCLK_I2C2 66 + #define SCLK_I2C3 67 diff --git a/sys-kernel/decade-sources/files/patches-5.14/rk3399-enable-dwc3-xhci-usb-trb-quirk.patch b/sys-kernel/decade-sources/files/patches-5.14/rk3399-enable-dwc3-xhci-usb-trb-quirk.patch new file mode 100644 index 0000000..8ca2079 --- /dev/null +++ b/sys-kernel/decade-sources/files/patches-5.14/rk3399-enable-dwc3-xhci-usb-trb-quirk.patch @@ -0,0 +1,20 @@ +diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi +index bcd31e9d6..91f1aa809 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi +@@ -396,6 +396,7 @@ usbdrd_dwc3_0: usb@fe800000 { + snps,dis_u2_susphy_quirk; + snps,dis-del-phy-power-chg-quirk; + snps,dis-tx-ipgap-linecheck-quirk; ++ snps,xhci-trb-ent-quirk; + power-domains = <&power RK3399_PD_USB3>; + status = "disabled"; + }; +@@ -461,6 +462,7 @@ usbdrd_dwc3_1: usb@fe900000 { + snps,dis_u2_susphy_quirk; + snps,dis-del-phy-power-chg-quirk; + snps,dis-tx-ipgap-linecheck-quirk; ++ snps,xhci-trb-ent-quirk; + power-domains = <&power RK3399_PD_USB3>; + status = "disabled"; + }; diff --git a/sys-kernel/decade-sources/files/patches-5.14/rk3399-pci-rockchip-support-ep-gpio-undefined-case.patch b/sys-kernel/decade-sources/files/patches-5.14/rk3399-pci-rockchip-support-ep-gpio-undefined-case.patch new file mode 100644 index 0000000..662969f --- /dev/null +++ b/sys-kernel/decade-sources/files/patches-5.14/rk3399-pci-rockchip-support-ep-gpio-undefined-case.patch @@ -0,0 +1,28 @@ +From 9de42a7ce7b821596a151cfaa0aca79d53c2170f Mon Sep 17 00:00:00 2001 +From: Igor Pecovnik +Date: Sun, 7 Mar 2021 15:24:02 +0100 +Subject: [PATCH] oo + +Signed-off-by: Aditya Prayoga +Changed by: Igor Pecovnik +--- + drivers/pci/controller/pcie-rockchip.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/drivers/pci/controller/pcie-rockchip.c b/drivers/pci/controller/pcie-rockchip.c +index 990a00e08..193d26562 100644 +--- a/drivers/pci/controller/pcie-rockchip.c ++++ b/drivers/pci/controller/pcie-rockchip.c +@@ -118,8 +118,7 @@ int rockchip_pcie_parse_dt(struct rockchip_pcie *rockchip) + } + + if (rockchip->is_rc) { +- rockchip->ep_gpio = devm_gpiod_get_optional(dev, "ep", +- GPIOD_OUT_HIGH); ++ rockchip->ep_gpio = devm_gpiod_get_optional(dev, "ep", GPIOD_OUT_HIGH); + if (IS_ERR(rockchip->ep_gpio)) + return dev_err_probe(dev, PTR_ERR(rockchip->ep_gpio), + "failed to get ep GPIO\n"); +-- +Created with Armbian build tools https://github.com/armbian/build + diff --git a/sys-kernel/decade-sources/files/patches-5.14/rk3399-sd-drive-level-8ma.patch b/sys-kernel/decade-sources/files/patches-5.14/rk3399-sd-drive-level-8ma.patch new file mode 100644 index 0000000..cac129a --- /dev/null +++ b/sys-kernel/decade-sources/files/patches-5.14/rk3399-sd-drive-level-8ma.patch @@ -0,0 +1,49 @@ +diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi +index 6eb9dda..d6fc676 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi +@@ -2285,35 +2285,35 @@ + sdmmc { + sdmmc_bus1: sdmmc-bus1 { + rockchip,pins = +- <4 RK_PB0 1 &pcfg_pull_up>; ++ <4 RK_PB0 1 &pcfg_pull_up_8ma>; + }; + + sdmmc_bus4: sdmmc-bus4 { + rockchip,pins = +- <4 RK_PB0 1 &pcfg_pull_up>, +- <4 RK_PB1 1 &pcfg_pull_up>, +- <4 RK_PB2 1 &pcfg_pull_up>, +- <4 RK_PB3 1 &pcfg_pull_up>; ++ <4 RK_PB0 1 &pcfg_pull_up_8ma>, ++ <4 RK_PB1 1 &pcfg_pull_up_8ma>, ++ <4 RK_PB2 1 &pcfg_pull_up_8ma>, ++ <4 RK_PB3 1 &pcfg_pull_up_8ma>; + }; + + sdmmc_clk: sdmmc-clk { + rockchip,pins = +- <4 RK_PB4 1 &pcfg_pull_none>; ++ <4 RK_PB4 1 &pcfg_pull_none_12ma>; + }; + + sdmmc_cmd: sdmmc-cmd { + rockchip,pins = +- <4 RK_PB5 1 &pcfg_pull_up>; ++ <4 RK_PB5 1 &pcfg_pull_up_8ma>; + }; + + sdmmc_cd: sdmmc-cd { + rockchip,pins = +- <0 RK_PA7 1 &pcfg_pull_up>; ++ <0 RK_PA7 1 &pcfg_pull_up>; + }; + + sdmmc_wp: sdmmc-wp { + rockchip,pins = +- <0 RK_PB0 1 &pcfg_pull_up>; ++ <0 RK_PB0 1 &pcfg_pull_up_8ma>; + }; + }; +