From 7fd1b352f4f731ffe36107b057e9f65877a5b2af Mon Sep 17 00:00:00 2001 From: Jan200101 Date: Sun, 28 Jul 2024 00:28:19 +0200 Subject: kernel 6.9.10 --- SOURCES/0001-amd-pstate.patch | 22 +- SOURCES/kernel-aarch64-16k-debug-fedora.config | 4 +- SOURCES/kernel-aarch64-16k-fedora.config | 4 +- SOURCES/kernel-aarch64-64k-debug-rhel.config | 4 +- SOURCES/kernel-aarch64-64k-rhel.config | 4 +- SOURCES/kernel-aarch64-debug-fedora.config | 4 +- SOURCES/kernel-aarch64-debug-rhel.config | 4 +- SOURCES/kernel-aarch64-fedora.config | 4 +- SOURCES/kernel-aarch64-rhel.config | 4 +- SOURCES/kernel-aarch64-rt-debug-rhel.config | 4 +- SOURCES/kernel-aarch64-rt-rhel.config | 4 +- SOURCES/kernel-ppc64le-debug-fedora.config | 4 +- SOURCES/kernel-ppc64le-debug-rhel.config | 4 +- SOURCES/kernel-ppc64le-fedora.config | 4 +- SOURCES/kernel-ppc64le-rhel.config | 4 +- SOURCES/kernel-s390x-debug-fedora.config | 4 +- SOURCES/kernel-s390x-debug-rhel.config | 4 +- SOURCES/kernel-s390x-fedora.config | 4 +- SOURCES/kernel-s390x-rhel.config | 4 +- SOURCES/kernel-s390x-zfcpdump-rhel.config | 4 +- SOURCES/kernel-x86_64-debug-fedora.config | 5 +- SOURCES/kernel-x86_64-debug-rhel.config | 5 +- SOURCES/kernel-x86_64-fedora.config | 5 +- SOURCES/kernel-x86_64-rhel.config | 5 +- SOURCES/kernel-x86_64-rt-debug-rhel.config | 5 +- SOURCES/kernel-x86_64-rt-rhel.config | 5 +- SOURCES/kernel.changelog | 4 + SOURCES/patch-6.9-redhat.patch | 2 +- SOURCES/v0-speaker-multifix.patch | 23729 +++++++++++++++++++++++ 29 files changed, 23796 insertions(+), 67 deletions(-) create mode 100644 SOURCES/v0-speaker-multifix.patch (limited to 'SOURCES') diff --git a/SOURCES/0001-amd-pstate.patch b/SOURCES/0001-amd-pstate.patch index 72d5cc6..8b81bc4 100644 --- a/SOURCES/0001-amd-pstate.patch +++ b/SOURCES/0001-amd-pstate.patch @@ -1,4 +1,4 @@ -From b2368823f6d79235b4e706263855373ff2fed1e5 Mon Sep 17 00:00:00 2001 +From 4ca68a16f48ef388742a25c6d6643bf996e8d1d5 Mon Sep 17 00:00:00 2001 From: Peter Jung Date: Fri, 5 Jul 2024 10:31:29 +0200 Subject: [PATCH 02/10] amd-pstate @@ -13,8 +13,8 @@ Signed-off-by: Peter Jung drivers/cpufreq/acpi-cpufreq.c | 3 +- drivers/cpufreq/amd-pstate.c | 356 ++++++++++++++------ drivers/cpufreq/amd-pstate.h | 16 +- - drivers/cpufreq/cpufreq.c | 14 +- - 9 files changed, 292 insertions(+), 120 deletions(-) + drivers/cpufreq/cpufreq.c | 11 +- + 9 files changed, 290 insertions(+), 119 deletions(-) diff --git a/Documentation/admin-guide/pm/amd-pstate.rst b/Documentation/admin-guide/pm/amd-pstate.rst index 1e0d101b020a..d0324d44f548 100644 @@ -102,7 +102,7 @@ index 438c9e75a04d..97c2d4f15d76 100644 help This kernel module is used for testing. It's safe to say M here. diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c -index 37f1cdf46d29..094f1f91a9b3 100644 +index 4ac3a35dcd98..f4f8587c4ea0 100644 --- a/drivers/cpufreq/acpi-cpufreq.c +++ b/drivers/cpufreq/acpi-cpufreq.c @@ -50,8 +50,6 @@ enum { @@ -846,7 +846,7 @@ index bc341f35908d..cc8bb2bc325a 100644 #endif /* _LINUX_AMD_PSTATE_H */ diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c -index fd9c3ed21f49..bbbeb8b90313 100644 +index d7630d9cdb2f..bbbeb8b90313 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -614,10 +614,9 @@ static ssize_t show_boost(struct kobject *kobj, @@ -876,16 +876,6 @@ index fd9c3ed21f49..bbbeb8b90313 100644 return -EINVAL; if (!cpufreq_driver->boost_enabled) -@@ -1431,7 +1430,8 @@ static int cpufreq_online(unsigned int cpu) - } - - /* Let the per-policy boost flag mirror the cpufreq_driver boost during init */ -- policy->boost_enabled = cpufreq_boost_enabled() && policy_has_boost_freq(policy); -+ if (cpufreq_boost_enabled() && policy_has_boost_freq(policy)) -+ policy->boost_enabled = true; - - /* - * The initialization has succeeded and the policy is online. -- -2.45.2 +2.46.0.rc1 diff --git a/SOURCES/kernel-aarch64-16k-debug-fedora.config b/SOURCES/kernel-aarch64-16k-debug-fedora.config index 8b9bf99..a7dfbe1 100644 --- a/SOURCES/kernel-aarch64-16k-debug-fedora.config +++ b/SOURCES/kernel-aarch64-16k-debug-fedora.config @@ -10098,10 +10098,10 @@ CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m CONFIG_IPC_CLASSES=y CONFIG_LEDS_TPS68470=m CONFIG_SENSORS_SURFACE_TEMP=m -CONFIG_DRM_AMD_COLOR_STEAMDECK=y +CONFIG_AMD_PRIVATE_COLOR=y CONFIG_DRM_APPLETBDRM=m CONFIG_HID_APPLETB_BL=m CONFIG_HID_APPLETB_KBD=m CONFIG_HID_APPLE_MAGIC_BACKLIGHT=m CONFIG_APPLE_BCE=m -CONFIG_AMD_PRIVATE_COLOR=y +CONFIG_SND_SOC_AW87XXX=m diff --git a/SOURCES/kernel-aarch64-16k-fedora.config b/SOURCES/kernel-aarch64-16k-fedora.config index c6ccf78..a2d2a94 100644 --- a/SOURCES/kernel-aarch64-16k-fedora.config +++ b/SOURCES/kernel-aarch64-16k-fedora.config @@ -10069,10 +10069,10 @@ CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m CONFIG_IPC_CLASSES=y CONFIG_LEDS_TPS68470=m CONFIG_SENSORS_SURFACE_TEMP=m -CONFIG_DRM_AMD_COLOR_STEAMDECK=y +CONFIG_AMD_PRIVATE_COLOR=y CONFIG_DRM_APPLETBDRM=m CONFIG_HID_APPLETB_BL=m CONFIG_HID_APPLETB_KBD=m CONFIG_HID_APPLE_MAGIC_BACKLIGHT=m CONFIG_APPLE_BCE=m -CONFIG_AMD_PRIVATE_COLOR=y +CONFIG_SND_SOC_AW87XXX=m diff --git a/SOURCES/kernel-aarch64-64k-debug-rhel.config b/SOURCES/kernel-aarch64-64k-debug-rhel.config index b1ea174..0992b16 100644 --- a/SOURCES/kernel-aarch64-64k-debug-rhel.config +++ b/SOURCES/kernel-aarch64-64k-debug-rhel.config @@ -8134,10 +8134,10 @@ CONFIG_IPC_CLASSES=y CONFIG_LEDS_TPS68470=m CONFIG_SENSORS_SURFACE_FAN=m CONFIG_SENSORS_SURFACE_TEMP=m -CONFIG_DRM_AMD_COLOR_STEAMDECK=y +CONFIG_AMD_PRIVATE_COLOR=y CONFIG_DRM_APPLETBDRM=m CONFIG_HID_APPLETB_BL=m CONFIG_HID_APPLETB_KBD=m CONFIG_HID_APPLE_MAGIC_BACKLIGHT=m CONFIG_APPLE_BCE=m -CONFIG_AMD_PRIVATE_COLOR=y +CONFIG_SND_SOC_AW87XXX=m diff --git a/SOURCES/kernel-aarch64-64k-rhel.config b/SOURCES/kernel-aarch64-64k-rhel.config index 30709e2..2158cd3 100644 --- a/SOURCES/kernel-aarch64-64k-rhel.config +++ b/SOURCES/kernel-aarch64-64k-rhel.config @@ -8109,10 +8109,10 @@ CONFIG_IPC_CLASSES=y CONFIG_LEDS_TPS68470=m CONFIG_SENSORS_SURFACE_FAN=m CONFIG_SENSORS_SURFACE_TEMP=m -CONFIG_DRM_AMD_COLOR_STEAMDECK=y +CONFIG_AMD_PRIVATE_COLOR=y CONFIG_DRM_APPLETBDRM=m CONFIG_HID_APPLETB_BL=m CONFIG_HID_APPLETB_KBD=m CONFIG_HID_APPLE_MAGIC_BACKLIGHT=m CONFIG_APPLE_BCE=m -CONFIG_AMD_PRIVATE_COLOR=y +CONFIG_SND_SOC_AW87XXX=m diff --git a/SOURCES/kernel-aarch64-debug-fedora.config b/SOURCES/kernel-aarch64-debug-fedora.config index 85a7e95..980c3ad 100644 --- a/SOURCES/kernel-aarch64-debug-fedora.config +++ b/SOURCES/kernel-aarch64-debug-fedora.config @@ -10097,10 +10097,10 @@ CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m CONFIG_IPC_CLASSES=y CONFIG_LEDS_TPS68470=m CONFIG_SENSORS_SURFACE_TEMP=m -CONFIG_DRM_AMD_COLOR_STEAMDECK=y +CONFIG_AMD_PRIVATE_COLOR=y CONFIG_DRM_APPLETBDRM=m CONFIG_HID_APPLETB_BL=m CONFIG_HID_APPLETB_KBD=m CONFIG_HID_APPLE_MAGIC_BACKLIGHT=m CONFIG_APPLE_BCE=m -CONFIG_AMD_PRIVATE_COLOR=y +CONFIG_SND_SOC_AW87XXX=m diff --git a/SOURCES/kernel-aarch64-debug-rhel.config b/SOURCES/kernel-aarch64-debug-rhel.config index 58aa680..0af3ed0 100644 --- a/SOURCES/kernel-aarch64-debug-rhel.config +++ b/SOURCES/kernel-aarch64-debug-rhel.config @@ -8130,10 +8130,10 @@ CONFIG_IPC_CLASSES=y CONFIG_LEDS_TPS68470=m CONFIG_SENSORS_SURFACE_FAN=m CONFIG_SENSORS_SURFACE_TEMP=m -CONFIG_DRM_AMD_COLOR_STEAMDECK=y +CONFIG_AMD_PRIVATE_COLOR=y CONFIG_DRM_APPLETBDRM=m CONFIG_HID_APPLETB_BL=m CONFIG_HID_APPLETB_KBD=m CONFIG_HID_APPLE_MAGIC_BACKLIGHT=m CONFIG_APPLE_BCE=m -CONFIG_AMD_PRIVATE_COLOR=y +CONFIG_SND_SOC_AW87XXX=m diff --git a/SOURCES/kernel-aarch64-fedora.config b/SOURCES/kernel-aarch64-fedora.config index 45d7c64..d11fb32 100644 --- a/SOURCES/kernel-aarch64-fedora.config +++ b/SOURCES/kernel-aarch64-fedora.config @@ -10068,10 +10068,10 @@ CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m CONFIG_IPC_CLASSES=y CONFIG_LEDS_TPS68470=m CONFIG_SENSORS_SURFACE_TEMP=m -CONFIG_DRM_AMD_COLOR_STEAMDECK=y +CONFIG_AMD_PRIVATE_COLOR=y CONFIG_DRM_APPLETBDRM=m CONFIG_HID_APPLETB_BL=m CONFIG_HID_APPLETB_KBD=m CONFIG_HID_APPLE_MAGIC_BACKLIGHT=m CONFIG_APPLE_BCE=m -CONFIG_AMD_PRIVATE_COLOR=y +CONFIG_SND_SOC_AW87XXX=m diff --git a/SOURCES/kernel-aarch64-rhel.config b/SOURCES/kernel-aarch64-rhel.config index 8dfe7db..778777a 100644 --- a/SOURCES/kernel-aarch64-rhel.config +++ b/SOURCES/kernel-aarch64-rhel.config @@ -8105,10 +8105,10 @@ CONFIG_IPC_CLASSES=y CONFIG_LEDS_TPS68470=m CONFIG_SENSORS_SURFACE_FAN=m CONFIG_SENSORS_SURFACE_TEMP=m -CONFIG_DRM_AMD_COLOR_STEAMDECK=y +CONFIG_AMD_PRIVATE_COLOR=y CONFIG_DRM_APPLETBDRM=m CONFIG_HID_APPLETB_BL=m CONFIG_HID_APPLETB_KBD=m CONFIG_HID_APPLE_MAGIC_BACKLIGHT=m CONFIG_APPLE_BCE=m -CONFIG_AMD_PRIVATE_COLOR=y +CONFIG_SND_SOC_AW87XXX=m diff --git a/SOURCES/kernel-aarch64-rt-debug-rhel.config b/SOURCES/kernel-aarch64-rt-debug-rhel.config index 87cb2c1..05ae5de 100644 --- a/SOURCES/kernel-aarch64-rt-debug-rhel.config +++ b/SOURCES/kernel-aarch64-rt-debug-rhel.config @@ -8190,10 +8190,10 @@ CONFIG_IPC_CLASSES=y CONFIG_LEDS_TPS68470=m CONFIG_SENSORS_SURFACE_FAN=m CONFIG_SENSORS_SURFACE_TEMP=m -CONFIG_DRM_AMD_COLOR_STEAMDECK=y +CONFIG_AMD_PRIVATE_COLOR=y CONFIG_DRM_APPLETBDRM=m CONFIG_HID_APPLETB_BL=m CONFIG_HID_APPLETB_KBD=m CONFIG_HID_APPLE_MAGIC_BACKLIGHT=m CONFIG_APPLE_BCE=m -CONFIG_AMD_PRIVATE_COLOR=y +CONFIG_SND_SOC_AW87XXX=m diff --git a/SOURCES/kernel-aarch64-rt-rhel.config b/SOURCES/kernel-aarch64-rt-rhel.config index 2b1ac51..d0011aa 100644 --- a/SOURCES/kernel-aarch64-rt-rhel.config +++ b/SOURCES/kernel-aarch64-rt-rhel.config @@ -8165,10 +8165,10 @@ CONFIG_IPC_CLASSES=y CONFIG_LEDS_TPS68470=m CONFIG_SENSORS_SURFACE_FAN=m CONFIG_SENSORS_SURFACE_TEMP=m -CONFIG_DRM_AMD_COLOR_STEAMDECK=y +CONFIG_AMD_PRIVATE_COLOR=y CONFIG_DRM_APPLETBDRM=m CONFIG_HID_APPLETB_BL=m CONFIG_HID_APPLETB_KBD=m CONFIG_HID_APPLE_MAGIC_BACKLIGHT=m CONFIG_APPLE_BCE=m -CONFIG_AMD_PRIVATE_COLOR=y +CONFIG_SND_SOC_AW87XXX=m diff --git a/SOURCES/kernel-ppc64le-debug-fedora.config b/SOURCES/kernel-ppc64le-debug-fedora.config index bacd0fe..6bbf924 100644 --- a/SOURCES/kernel-ppc64le-debug-fedora.config +++ b/SOURCES/kernel-ppc64le-debug-fedora.config @@ -8349,10 +8349,10 @@ CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m CONFIG_IPC_CLASSES=y CONFIG_LEDS_TPS68470=m CONFIG_SENSORS_SURFACE_TEMP=m -CONFIG_DRM_AMD_COLOR_STEAMDECK=y +CONFIG_AMD_PRIVATE_COLOR=y CONFIG_DRM_APPLETBDRM=m CONFIG_HID_APPLETB_BL=m CONFIG_HID_APPLETB_KBD=m CONFIG_HID_APPLE_MAGIC_BACKLIGHT=m CONFIG_APPLE_BCE=m -CONFIG_AMD_PRIVATE_COLOR=y +CONFIG_SND_SOC_AW87XXX=m diff --git a/SOURCES/kernel-ppc64le-debug-rhel.config b/SOURCES/kernel-ppc64le-debug-rhel.config index ba2f4e6..117c2ba 100644 --- a/SOURCES/kernel-ppc64le-debug-rhel.config +++ b/SOURCES/kernel-ppc64le-debug-rhel.config @@ -7598,10 +7598,10 @@ CONFIG_IPC_CLASSES=y CONFIG_LEDS_TPS68470=m CONFIG_SENSORS_SURFACE_FAN=m CONFIG_SENSORS_SURFACE_TEMP=m -CONFIG_DRM_AMD_COLOR_STEAMDECK=y +CONFIG_AMD_PRIVATE_COLOR=y CONFIG_DRM_APPLETBDRM=m CONFIG_HID_APPLETB_BL=m CONFIG_HID_APPLETB_KBD=m CONFIG_HID_APPLE_MAGIC_BACKLIGHT=m CONFIG_APPLE_BCE=m -CONFIG_AMD_PRIVATE_COLOR=y +CONFIG_SND_SOC_AW87XXX=m diff --git a/SOURCES/kernel-ppc64le-fedora.config b/SOURCES/kernel-ppc64le-fedora.config index 330cf3c..9a2fea5 100644 --- a/SOURCES/kernel-ppc64le-fedora.config +++ b/SOURCES/kernel-ppc64le-fedora.config @@ -8318,10 +8318,10 @@ CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m CONFIG_IPC_CLASSES=y CONFIG_LEDS_TPS68470=m CONFIG_SENSORS_SURFACE_TEMP=m -CONFIG_DRM_AMD_COLOR_STEAMDECK=y +CONFIG_AMD_PRIVATE_COLOR=y CONFIG_DRM_APPLETBDRM=m CONFIG_HID_APPLETB_BL=m CONFIG_HID_APPLETB_KBD=m CONFIG_HID_APPLE_MAGIC_BACKLIGHT=m CONFIG_APPLE_BCE=m -CONFIG_AMD_PRIVATE_COLOR=y +CONFIG_SND_SOC_AW87XXX=m diff --git a/SOURCES/kernel-ppc64le-rhel.config b/SOURCES/kernel-ppc64le-rhel.config index 5b74759..ce6a598 100644 --- a/SOURCES/kernel-ppc64le-rhel.config +++ b/SOURCES/kernel-ppc64le-rhel.config @@ -7575,10 +7575,10 @@ CONFIG_IPC_CLASSES=y CONFIG_LEDS_TPS68470=m CONFIG_SENSORS_SURFACE_FAN=m CONFIG_SENSORS_SURFACE_TEMP=m -CONFIG_DRM_AMD_COLOR_STEAMDECK=y +CONFIG_AMD_PRIVATE_COLOR=y CONFIG_DRM_APPLETBDRM=m CONFIG_HID_APPLETB_BL=m CONFIG_HID_APPLETB_KBD=m CONFIG_HID_APPLE_MAGIC_BACKLIGHT=m CONFIG_APPLE_BCE=m -CONFIG_AMD_PRIVATE_COLOR=y +CONFIG_SND_SOC_AW87XXX=m diff --git a/SOURCES/kernel-s390x-debug-fedora.config b/SOURCES/kernel-s390x-debug-fedora.config index c3b15ab..f0f1575 100644 --- a/SOURCES/kernel-s390x-debug-fedora.config +++ b/SOURCES/kernel-s390x-debug-fedora.config @@ -8284,10 +8284,10 @@ CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m CONFIG_IPC_CLASSES=y CONFIG_LEDS_TPS68470=m CONFIG_SENSORS_SURFACE_TEMP=m -CONFIG_DRM_AMD_COLOR_STEAMDECK=y +CONFIG_AMD_PRIVATE_COLOR=y CONFIG_DRM_APPLETBDRM=m CONFIG_HID_APPLETB_BL=m CONFIG_HID_APPLETB_KBD=m CONFIG_HID_APPLE_MAGIC_BACKLIGHT=m CONFIG_APPLE_BCE=m -CONFIG_AMD_PRIVATE_COLOR=y +CONFIG_SND_SOC_AW87XXX=m diff --git a/SOURCES/kernel-s390x-debug-rhel.config b/SOURCES/kernel-s390x-debug-rhel.config index d9e180e..d6972d0 100644 --- a/SOURCES/kernel-s390x-debug-rhel.config +++ b/SOURCES/kernel-s390x-debug-rhel.config @@ -7580,10 +7580,10 @@ CONFIG_IPC_CLASSES=y CONFIG_LEDS_TPS68470=m CONFIG_SENSORS_SURFACE_FAN=m CONFIG_SENSORS_SURFACE_TEMP=m -CONFIG_DRM_AMD_COLOR_STEAMDECK=y +CONFIG_AMD_PRIVATE_COLOR=y CONFIG_DRM_APPLETBDRM=m CONFIG_HID_APPLETB_BL=m CONFIG_HID_APPLETB_KBD=m CONFIG_HID_APPLE_MAGIC_BACKLIGHT=m CONFIG_APPLE_BCE=m -CONFIG_AMD_PRIVATE_COLOR=y +CONFIG_SND_SOC_AW87XXX=m diff --git a/SOURCES/kernel-s390x-fedora.config b/SOURCES/kernel-s390x-fedora.config index 1d7548b..fe1f8e9 100644 --- a/SOURCES/kernel-s390x-fedora.config +++ b/SOURCES/kernel-s390x-fedora.config @@ -8253,10 +8253,10 @@ CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m CONFIG_IPC_CLASSES=y CONFIG_LEDS_TPS68470=m CONFIG_SENSORS_SURFACE_TEMP=m -CONFIG_DRM_AMD_COLOR_STEAMDECK=y +CONFIG_AMD_PRIVATE_COLOR=y CONFIG_DRM_APPLETBDRM=m CONFIG_HID_APPLETB_BL=m CONFIG_HID_APPLETB_KBD=m CONFIG_HID_APPLE_MAGIC_BACKLIGHT=m CONFIG_APPLE_BCE=m -CONFIG_AMD_PRIVATE_COLOR=y +CONFIG_SND_SOC_AW87XXX=m diff --git a/SOURCES/kernel-s390x-rhel.config b/SOURCES/kernel-s390x-rhel.config index ce38556..8c35759 100644 --- a/SOURCES/kernel-s390x-rhel.config +++ b/SOURCES/kernel-s390x-rhel.config @@ -7557,10 +7557,10 @@ CONFIG_IPC_CLASSES=y CONFIG_LEDS_TPS68470=m CONFIG_SENSORS_SURFACE_FAN=m CONFIG_SENSORS_SURFACE_TEMP=m -CONFIG_DRM_AMD_COLOR_STEAMDECK=y +CONFIG_AMD_PRIVATE_COLOR=y CONFIG_DRM_APPLETBDRM=m CONFIG_HID_APPLETB_BL=m CONFIG_HID_APPLETB_KBD=m CONFIG_HID_APPLE_MAGIC_BACKLIGHT=m CONFIG_APPLE_BCE=m -CONFIG_AMD_PRIVATE_COLOR=y +CONFIG_SND_SOC_AW87XXX=m diff --git a/SOURCES/kernel-s390x-zfcpdump-rhel.config b/SOURCES/kernel-s390x-zfcpdump-rhel.config index f987196..a71fad6 100644 --- a/SOURCES/kernel-s390x-zfcpdump-rhel.config +++ b/SOURCES/kernel-s390x-zfcpdump-rhel.config @@ -7580,10 +7580,10 @@ CONFIG_IPC_CLASSES=y CONFIG_LEDS_TPS68470=m CONFIG_SENSORS_SURFACE_FAN=m CONFIG_SENSORS_SURFACE_TEMP=m -CONFIG_DRM_AMD_COLOR_STEAMDECK=y +CONFIG_AMD_PRIVATE_COLOR=y CONFIG_DRM_APPLETBDRM=m CONFIG_HID_APPLETB_BL=m CONFIG_HID_APPLETB_KBD=m CONFIG_HID_APPLE_MAGIC_BACKLIGHT=m CONFIG_APPLE_BCE=m -CONFIG_AMD_PRIVATE_COLOR=y +CONFIG_SND_SOC_AW87XXX=m diff --git a/SOURCES/kernel-x86_64-debug-fedora.config b/SOURCES/kernel-x86_64-debug-fedora.config index 9486803..83ad2e0 100644 --- a/SOURCES/kernel-x86_64-debug-fedora.config +++ b/SOURCES/kernel-x86_64-debug-fedora.config @@ -9000,7 +9000,7 @@ CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m CONFIG_IPC_CLASSES=y CONFIG_LEDS_TPS68470=m CONFIG_SENSORS_SURFACE_TEMP=m -CONFIG_DRM_AMD_COLOR_STEAMDECK=y +CONFIG_AMD_PRIVATE_COLOR=y CONFIG_DRM_APPLETBDRM=m CONFIG_HID_APPLETB_BL=m CONFIG_HID_APPLETB_KBD=m @@ -9041,10 +9041,11 @@ CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 # CONFIG_USB_DUMMY_HCD is not set # CONFIG_USB_CONFIGFS is not set # CONFIG_PHY_SAMSUNG_USB2 is not set +CONFIG_DRM_AMD_COLOR_STEAMDECK=y CONFIG_SND_SOC_SOF=m CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE=y CONFIG_SND_SOC_SOF_IPC3=y CONFIG_SND_SOC_SOF_INTEL_IPC4=y CONFIG_SND_SOC_SOF_AMD_COMMON=m CONFIG_SND_SOC_TOPOLOGY=y -CONFIG_AMD_PRIVATE_COLOR=y +CONFIG_SND_SOC_AW87XXX=m diff --git a/SOURCES/kernel-x86_64-debug-rhel.config b/SOURCES/kernel-x86_64-debug-rhel.config index 8b78a23..a6b6433 100644 --- a/SOURCES/kernel-x86_64-debug-rhel.config +++ b/SOURCES/kernel-x86_64-debug-rhel.config @@ -7975,7 +7975,7 @@ CONFIG_IPC_CLASSES=y CONFIG_LEDS_TPS68470=m CONFIG_SENSORS_SURFACE_FAN=m CONFIG_SENSORS_SURFACE_TEMP=m -CONFIG_DRM_AMD_COLOR_STEAMDECK=y +CONFIG_AMD_PRIVATE_COLOR=y CONFIG_DRM_APPLETBDRM=m CONFIG_HID_APPLETB_BL=m CONFIG_HID_APPLETB_KBD=m @@ -8022,6 +8022,7 @@ CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 # CONFIG_USB_DUMMY_HCD is not set # CONFIG_USB_CONFIGFS is not set # CONFIG_PHY_SAMSUNG_USB2 is not set +CONFIG_DRM_AMD_COLOR_STEAMDECK=y CONFIG_SND_SOC_AMD_SOF_MACH=m CONFIG_SND_SOC_AMD_MACH_COMMON=m CONFIG_SND_SOC_SOF=m @@ -8035,4 +8036,4 @@ CONFIG_SND_SOC_SOF_AMD_ACP63=m # CONFIG_SND_AMD_ASOC_REMBRANDT is not set # CONFIG_SND_SOC_AMD_LEGACY_MACH is not set CONFIG_SND_SOC_TOPOLOGY=y -CONFIG_AMD_PRIVATE_COLOR=y +CONFIG_SND_SOC_AW87XXX=m diff --git a/SOURCES/kernel-x86_64-fedora.config b/SOURCES/kernel-x86_64-fedora.config index a4240f3..b5c1bd1 100644 --- a/SOURCES/kernel-x86_64-fedora.config +++ b/SOURCES/kernel-x86_64-fedora.config @@ -8970,7 +8970,7 @@ CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m CONFIG_IPC_CLASSES=y CONFIG_LEDS_TPS68470=m CONFIG_SENSORS_SURFACE_TEMP=m -CONFIG_DRM_AMD_COLOR_STEAMDECK=y +CONFIG_AMD_PRIVATE_COLOR=y CONFIG_DRM_APPLETBDRM=m CONFIG_HID_APPLETB_BL=m CONFIG_HID_APPLETB_KBD=m @@ -9011,10 +9011,11 @@ CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 # CONFIG_USB_DUMMY_HCD is not set # CONFIG_USB_CONFIGFS is not set # CONFIG_PHY_SAMSUNG_USB2 is not set +CONFIG_DRM_AMD_COLOR_STEAMDECK=y CONFIG_SND_SOC_SOF=m CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE=y CONFIG_SND_SOC_SOF_IPC3=y CONFIG_SND_SOC_SOF_INTEL_IPC4=y CONFIG_SND_SOC_SOF_AMD_COMMON=m CONFIG_SND_SOC_TOPOLOGY=y -CONFIG_AMD_PRIVATE_COLOR=y +CONFIG_SND_SOC_AW87XXX=m diff --git a/SOURCES/kernel-x86_64-rhel.config b/SOURCES/kernel-x86_64-rhel.config index c7b6ac2..12b1550 100644 --- a/SOURCES/kernel-x86_64-rhel.config +++ b/SOURCES/kernel-x86_64-rhel.config @@ -7951,7 +7951,7 @@ CONFIG_IPC_CLASSES=y CONFIG_LEDS_TPS68470=m CONFIG_SENSORS_SURFACE_FAN=m CONFIG_SENSORS_SURFACE_TEMP=m -CONFIG_DRM_AMD_COLOR_STEAMDECK=y +CONFIG_AMD_PRIVATE_COLOR=y CONFIG_DRM_APPLETBDRM=m CONFIG_HID_APPLETB_BL=m CONFIG_HID_APPLETB_KBD=m @@ -7998,6 +7998,7 @@ CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 # CONFIG_USB_DUMMY_HCD is not set # CONFIG_USB_CONFIGFS is not set # CONFIG_PHY_SAMSUNG_USB2 is not set +CONFIG_DRM_AMD_COLOR_STEAMDECK=y CONFIG_SND_SOC_AMD_SOF_MACH=m CONFIG_SND_SOC_AMD_MACH_COMMON=m CONFIG_SND_SOC_SOF=m @@ -8011,4 +8012,4 @@ CONFIG_SND_SOC_SOF_AMD_ACP63=m # CONFIG_SND_AMD_ASOC_REMBRANDT is not set # CONFIG_SND_SOC_AMD_LEGACY_MACH is not set CONFIG_SND_SOC_TOPOLOGY=y -CONFIG_AMD_PRIVATE_COLOR=y +CONFIG_SND_SOC_AW87XXX=m diff --git a/SOURCES/kernel-x86_64-rt-debug-rhel.config b/SOURCES/kernel-x86_64-rt-debug-rhel.config index f9a79f1..c06e648 100644 --- a/SOURCES/kernel-x86_64-rt-debug-rhel.config +++ b/SOURCES/kernel-x86_64-rt-debug-rhel.config @@ -8036,7 +8036,7 @@ CONFIG_IPC_CLASSES=y CONFIG_LEDS_TPS68470=m CONFIG_SENSORS_SURFACE_FAN=m CONFIG_SENSORS_SURFACE_TEMP=m -CONFIG_DRM_AMD_COLOR_STEAMDECK=y +CONFIG_AMD_PRIVATE_COLOR=y CONFIG_DRM_APPLETBDRM=m CONFIG_HID_APPLETB_BL=m CONFIG_HID_APPLETB_KBD=m @@ -8083,6 +8083,7 @@ CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 # CONFIG_USB_DUMMY_HCD is not set # CONFIG_USB_CONFIGFS is not set # CONFIG_PHY_SAMSUNG_USB2 is not set +CONFIG_DRM_AMD_COLOR_STEAMDECK=y CONFIG_SND_SOC_AMD_SOF_MACH=m CONFIG_SND_SOC_AMD_MACH_COMMON=m CONFIG_SND_SOC_SOF=m @@ -8096,4 +8097,4 @@ CONFIG_SND_SOC_SOF_AMD_ACP63=m # CONFIG_SND_AMD_ASOC_REMBRANDT is not set # CONFIG_SND_SOC_AMD_LEGACY_MACH is not set CONFIG_SND_SOC_TOPOLOGY=y -CONFIG_AMD_PRIVATE_COLOR=y +CONFIG_SND_SOC_AW87XXX=m diff --git a/SOURCES/kernel-x86_64-rt-rhel.config b/SOURCES/kernel-x86_64-rt-rhel.config index 5e66ede..68335ca 100644 --- a/SOURCES/kernel-x86_64-rt-rhel.config +++ b/SOURCES/kernel-x86_64-rt-rhel.config @@ -8012,7 +8012,7 @@ CONFIG_IPC_CLASSES=y CONFIG_LEDS_TPS68470=m CONFIG_SENSORS_SURFACE_FAN=m CONFIG_SENSORS_SURFACE_TEMP=m -CONFIG_DRM_AMD_COLOR_STEAMDECK=y +CONFIG_AMD_PRIVATE_COLOR=y CONFIG_DRM_APPLETBDRM=m CONFIG_HID_APPLETB_BL=m CONFIG_HID_APPLETB_KBD=m @@ -8059,6 +8059,7 @@ CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 # CONFIG_USB_DUMMY_HCD is not set # CONFIG_USB_CONFIGFS is not set # CONFIG_PHY_SAMSUNG_USB2 is not set +CONFIG_DRM_AMD_COLOR_STEAMDECK=y CONFIG_SND_SOC_AMD_SOF_MACH=m CONFIG_SND_SOC_AMD_MACH_COMMON=m CONFIG_SND_SOC_SOF=m @@ -8072,4 +8073,4 @@ CONFIG_SND_SOC_SOF_AMD_ACP63=m # CONFIG_SND_AMD_ASOC_REMBRANDT is not set # CONFIG_SND_SOC_AMD_LEGACY_MACH is not set CONFIG_SND_SOC_TOPOLOGY=y -CONFIG_AMD_PRIVATE_COLOR=y +CONFIG_SND_SOC_AW87XXX=m diff --git a/SOURCES/kernel.changelog b/SOURCES/kernel.changelog index ef2af95..a5ab474 100644 --- a/SOURCES/kernel.changelog +++ b/SOURCES/kernel.changelog @@ -1,3 +1,7 @@ +* Thu Jul 18 2024 Augusto Caringi [6.9.10-0] +- Linux v6.9.10 +Resolves: + * Thu Jul 11 2024 Augusto Caringi [6.9.9-0] - Linux v6.9.9 Resolves: diff --git a/SOURCES/patch-6.9-redhat.patch b/SOURCES/patch-6.9-redhat.patch index f9444f1..4c879f4 100644 --- a/SOURCES/patch-6.9-redhat.patch +++ b/SOURCES/patch-6.9-redhat.patch @@ -72,7 +72,7 @@ index 000000000000..733a26bd887a + +endmenu diff --git a/Makefile b/Makefile -index cbe3a580ff48..45ff8af1947c 100644 +index 5471f554f95e..ad70d16c06c2 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,18 @@ $(if $(filter __%, $(MAKECMDGOALS)), \ diff --git a/SOURCES/v0-speaker-multifix.patch b/SOURCES/v0-speaker-multifix.patch new file mode 100644 index 0000000..d7af208 --- /dev/null +++ b/SOURCES/v0-speaker-multifix.patch @@ -0,0 +1,23729 @@ +From dbe14cbae955a033bff0c83b7341cc722b476bda Mon Sep 17 00:00:00 2001 +From: Matthew Anderson +Date: Thu, 25 Apr 2024 09:39:40 -0500 +Subject: [PATCH 1/8] Codec: Add aw87xxx codec with partial acpi implementation + Contribution by CVMagic (https://github.com/CVMagic) + +aw87xxx: Use strscpy instead of strlcpy + +awinic: i2c_driver cleanup and fixes +Signed-off-by: Antheas Kapenekakis +--- + sound/soc/codecs/Kconfig | 2 + + sound/soc/codecs/Makefile | 1 + + sound/soc/codecs/aw87xxx/Kconfig | 5 + + sound/soc/codecs/aw87xxx/Makefile | 4 + + sound/soc/codecs/aw87xxx/aw87xxx.c | 1457 +++++ + sound/soc/codecs/aw87xxx/aw87xxx.h | 121 + + sound/soc/codecs/aw87xxx/aw87xxx_acf_bin.c | 1558 +++++ + sound/soc/codecs/aw87xxx/aw87xxx_acf_bin.h | 191 + + sound/soc/codecs/aw87xxx/aw87xxx_bin_parse.c | 515 ++ + sound/soc/codecs/aw87xxx/aw87xxx_bin_parse.h | 73 + + sound/soc/codecs/aw87xxx/aw87xxx_device.c | 977 +++ + sound/soc/codecs/aw87xxx/aw87xxx_device.h | 149 + + sound/soc/codecs/aw87xxx/aw87xxx_dsp.c | 355 ++ + sound/soc/codecs/aw87xxx/aw87xxx_dsp.h | 65 + + sound/soc/codecs/aw87xxx/aw87xxx_log.h | 33 + + sound/soc/codecs/aw87xxx/aw87xxx_monitor.c | 1208 ++++ + sound/soc/codecs/aw87xxx/aw87xxx_monitor.h | 96 + + sound/soc/codecs/aw87xxx/aw87xxx_pid_18_reg.h | 2315 ++++++++ + sound/soc/codecs/aw87xxx/aw87xxx_pid_39_reg.h | 67 + + .../codecs/aw87xxx/aw87xxx_pid_59_3x9_reg.h | 93 + + .../codecs/aw87xxx/aw87xxx_pid_59_5x9_reg.h | 94 + + sound/soc/codecs/aw87xxx/aw87xxx_pid_5a_reg.h | 4124 +++++++++++++ + sound/soc/codecs/aw87xxx/aw87xxx_pid_60_reg.h | 5246 +++++++++++++++++ + sound/soc/codecs/aw87xxx/aw87xxx_pid_76_reg.h | 1205 ++++ + sound/soc/codecs/aw87xxx/aw87xxx_pid_9b_reg.h | 81 + + 25 files changed, 20035 insertions(+) + create mode 100644 sound/soc/codecs/aw87xxx/Kconfig + create mode 100644 sound/soc/codecs/aw87xxx/Makefile + create mode 100644 sound/soc/codecs/aw87xxx/aw87xxx.c + create mode 100644 sound/soc/codecs/aw87xxx/aw87xxx.h + create mode 100644 sound/soc/codecs/aw87xxx/aw87xxx_acf_bin.c + create mode 100644 sound/soc/codecs/aw87xxx/aw87xxx_acf_bin.h + create mode 100644 sound/soc/codecs/aw87xxx/aw87xxx_bin_parse.c + create mode 100644 sound/soc/codecs/aw87xxx/aw87xxx_bin_parse.h + create mode 100644 sound/soc/codecs/aw87xxx/aw87xxx_device.c + create mode 100644 sound/soc/codecs/aw87xxx/aw87xxx_device.h + create mode 100644 sound/soc/codecs/aw87xxx/aw87xxx_dsp.c + create mode 100644 sound/soc/codecs/aw87xxx/aw87xxx_dsp.h + create mode 100644 sound/soc/codecs/aw87xxx/aw87xxx_log.h + create mode 100644 sound/soc/codecs/aw87xxx/aw87xxx_monitor.c + create mode 100644 sound/soc/codecs/aw87xxx/aw87xxx_monitor.h + create mode 100644 sound/soc/codecs/aw87xxx/aw87xxx_pid_18_reg.h + create mode 100644 sound/soc/codecs/aw87xxx/aw87xxx_pid_39_reg.h + create mode 100644 sound/soc/codecs/aw87xxx/aw87xxx_pid_59_3x9_reg.h + create mode 100644 sound/soc/codecs/aw87xxx/aw87xxx_pid_59_5x9_reg.h + create mode 100644 sound/soc/codecs/aw87xxx/aw87xxx_pid_5a_reg.h + create mode 100644 sound/soc/codecs/aw87xxx/aw87xxx_pid_60_reg.h + create mode 100644 sound/soc/codecs/aw87xxx/aw87xxx_pid_76_reg.h + create mode 100644 sound/soc/codecs/aw87xxx/aw87xxx_pid_9b_reg.h + +diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig +index f78ea2f86..34d6b290f 100644 +--- a/sound/soc/codecs/Kconfig ++++ b/sound/soc/codecs/Kconfig +@@ -2497,4 +2497,6 @@ config SND_SOC_LPASS_TX_MACRO + select SND_SOC_LPASS_MACRO_COMMON + tristate "Qualcomm TX Macro in LPASS(Low Power Audio SubSystem)" + ++source "sound/soc/codecs/aw87xxx/Kconfig" ++ + endmenu +diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile +index 7c075539d..23f6d3b75 100644 +--- a/sound/soc/codecs/Makefile ++++ b/sound/soc/codecs/Makefile +@@ -781,6 +781,7 @@ obj-$(CONFIG_SND_SOC_WSA884X) += snd-soc-wsa884x.o + obj-$(CONFIG_SND_SOC_ZL38060) += snd-soc-zl38060.o + + # Amp ++obj-$(CONFIG_SND_SOC_AW87XXX) += aw87xxx/ + obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o + obj-$(CONFIG_SND_SOC_MAX98504) += snd-soc-max98504.o + obj-$(CONFIG_SND_SOC_SIMPLE_AMPLIFIER) += snd-soc-simple-amplifier.o +diff --git a/sound/soc/codecs/aw87xxx/Kconfig b/sound/soc/codecs/aw87xxx/Kconfig +new file mode 100644 +index 000000000..bd0f208e2 +--- /dev/null ++++ b/sound/soc/codecs/aw87xxx/Kconfig +@@ -0,0 +1,5 @@ ++config SND_SOC_AW87XXX ++ tristate "SoC Audio for awinic AW87XXX Smart K PA" ++ depends on I2C ++ help ++ This option enables support for AW87XXX Smart K PA. +diff --git a/sound/soc/codecs/aw87xxx/Makefile b/sound/soc/codecs/aw87xxx/Makefile +new file mode 100644 +index 000000000..d32f319a5 +--- /dev/null ++++ b/sound/soc/codecs/aw87xxx/Makefile +@@ -0,0 +1,4 @@ ++#for AWINIC AW87XXX Smart K PA ++snd-soc-aw87xxx-objs := aw87xxx.o aw87xxx_device.o aw87xxx_monitor.o aw87xxx_bin_parse.o aw87xxx_dsp.o aw87xxx_acf_bin.o ++obj-$(CONFIG_SND_SOC_AW87XXX) += snd-soc-aw87xxx.o ++ +diff --git a/sound/soc/codecs/aw87xxx/aw87xxx.c b/sound/soc/codecs/aw87xxx/aw87xxx.c +new file mode 100644 +index 000000000..eddb01695 +--- /dev/null ++++ b/sound/soc/codecs/aw87xxx/aw87xxx.c +@@ -0,0 +1,1457 @@ ++/* ++ * aw87xxx.c aw87xxx pa module ++ * ++ * Copyright (c) 2021 AWINIC Technology CO., LTD ++ * ++ * Author: Barry ++ * ++ * 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. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "aw87xxx.h" ++#include "aw87xxx_device.h" ++#include "aw87xxx_log.h" ++#include "aw87xxx_monitor.h" ++#include "aw87xxx_acf_bin.h" ++#include "aw87xxx_bin_parse.h" ++#include "aw87xxx_dsp.h" ++ ++/***************************************************************** ++* aw87xxx marco ++******************************************************************/ ++#define AW87XXX_I2C_NAME "aw87xxx_pa" ++#define AW87XXX_DRIVER_VERSION "v2.7.0" ++#define AW87XXX_FW_BIN_NAME "aw87xxx_acf.bin" ++#define AW87XXX_PROF_MUSIC "Music" ++/************************************************************************* ++ * aw87xxx variable ++ ************************************************************************/ ++static LIST_HEAD(g_aw87xxx_list); ++static DEFINE_MUTEX(g_aw87xxx_mutex_lock); ++unsigned int g_aw87xxx_dev_cnt = 0; ++ ++static const char *const aw87xxx_monitor_switch[] = {"Disable", "Enable"}; ++static const char *const aw87xxx_spin_switch[] = {"spin_0", "spin_90", ++ "spin_180", "spin_270"}; ++#ifdef AW_KERNEL_VER_OVER_4_19_1 ++static struct aw_componet_codec_ops aw_componet_codec_ops = { ++ .add_codec_controls = snd_soc_add_component_controls, ++ .unregister_codec = snd_soc_unregister_component, ++}; ++#else ++static struct aw_componet_codec_ops aw_componet_codec_ops = { ++ .add_codec_controls = snd_soc_add_codec_controls, ++ .unregister_codec = snd_soc_unregister_codec, ++}; ++#endif ++ ++ ++/************************************************************************ ++ * ++ * aw87xxx device update profile ++ * ++ ************************************************************************/ ++static int aw87xxx_power_down(struct aw87xxx *aw87xxx, char *profile) ++{ ++ int ret = 0; ++ struct aw_prof_desc *prof_desc = NULL; ++ struct aw_prof_info *prof_info = &aw87xxx->acf_info.prof_info; ++ struct aw_data_container *data_container = NULL; ++ struct aw_device *aw_dev = &aw87xxx->aw_dev; ++ ++ AW_DEV_LOGD(aw87xxx->dev, "enter"); ++ ++ if (!prof_info->status) { ++ AW_DEV_LOGE(aw87xxx->dev, "profile_cfg not load"); ++ return -EINVAL; ++ } ++ ++ prof_desc = aw87xxx_acf_get_prof_desc_form_name(aw87xxx->dev, &aw87xxx->acf_info, profile); ++ if (prof_desc == NULL) ++ goto no_bin_pwr_off; ++ ++ if (!prof_desc->prof_st) ++ goto no_bin_pwr_off; ++ ++ ++ data_container = &prof_desc->data_container; ++ AW_DEV_LOGD(aw87xxx->dev, "get profile[%s] data len [%d]", ++ profile, data_container->len); ++ ++ if (aw_dev->hwen_status == AW_DEV_HWEN_OFF) { ++ AW_DEV_LOGI(aw87xxx->dev, "profile[%s] has already load ", profile); ++ } else { ++ if (aw_dev->ops.pwr_off_func) { ++ ret = aw_dev->ops.pwr_off_func(aw_dev, data_container); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", profile); ++ goto pwr_off_failed; ++ } ++ } else { ++ ret = aw87xxx_dev_default_pwr_off(aw_dev, data_container); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", profile); ++ goto pwr_off_failed; ++ } ++ } ++ } ++ ++ aw87xxx->current_profile = prof_desc->prof_name; ++ return 0; ++ ++pwr_off_failed: ++no_bin_pwr_off: ++ aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, false); ++ aw87xxx->current_profile = aw87xxx->prof_off_name; ++ return ret; ++} ++ ++static int aw87xxx_power_on(struct aw87xxx *aw87xxx, char *profile) ++{ ++ int ret = -EINVAL; ++ struct aw_prof_desc *prof_desc = NULL; ++ struct aw_prof_info *prof_info = &aw87xxx->acf_info.prof_info; ++ struct aw_data_container *data_container = NULL; ++ struct aw_device *aw_dev = &aw87xxx->aw_dev; ++ ++ AW_DEV_LOGD(aw87xxx->dev, "enter"); ++ ++ if (!prof_info->status) { ++ AW_DEV_LOGE(aw87xxx->dev, "profile_cfg not load"); ++ return -EINVAL; ++ } ++ ++ if (0 == strncmp(profile, aw87xxx->prof_off_name, AW_PROFILE_STR_MAX)) ++ return aw87xxx_power_down(aw87xxx, profile); ++ ++ prof_desc = aw87xxx_acf_get_prof_desc_form_name(aw87xxx->dev, &aw87xxx->acf_info, profile); ++ if (prof_desc == NULL) { ++ AW_DEV_LOGE(aw87xxx->dev, "not found [%s] parameter", profile); ++ return -EINVAL; ++ } ++ ++ if (!prof_desc->prof_st) { ++ AW_DEV_LOGE(aw87xxx->dev, "not found data container"); ++ return -EINVAL; ++ } ++ ++ data_container = &prof_desc->data_container; ++ AW_DEV_LOGD(aw87xxx->dev, "get profile[%s] data len [%d]", ++ profile, data_container->len); ++ ++ if (aw_dev->ops.pwr_on_func) { ++ ret = aw_dev->ops.pwr_on_func(aw_dev, data_container); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", ++ profile); ++ return aw87xxx_power_down(aw87xxx, aw87xxx->prof_off_name); ++ } ++ } else { ++ ret = aw87xxx_dev_default_pwr_on(aw_dev, data_container); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", ++ profile); ++ return aw87xxx_power_down(aw87xxx, aw87xxx->prof_off_name); ++ } ++ } ++ ++ aw87xxx->current_profile = prof_desc->prof_name; ++ AW_DEV_LOGD(aw87xxx->dev, "load profile[%s] succeed", profile); ++ ++ return 0; ++} ++ ++ ++ ++int aw87xxx_update_profile(struct aw87xxx *aw87xxx, char *profile) ++{ ++ int ret = -1; ++ ++ AW_DEV_LOGD(aw87xxx->dev, "load profile[%s] enter", profile); ++ mutex_lock(&aw87xxx->reg_lock); ++ aw87xxx_monitor_stop(&aw87xxx->monitor); ++ if (0 == strncmp(profile, aw87xxx->prof_off_name, AW_PROFILE_STR_MAX)) { ++ ret = aw87xxx_power_down(aw87xxx, profile); ++ } else { ++ ret = aw87xxx_power_on(aw87xxx, profile); ++ if (!ret) ++ aw87xxx_monitor_start(&aw87xxx->monitor); ++ } ++ mutex_unlock(&aw87xxx->reg_lock); ++ ++ return ret; ++} ++ ++int aw87xxx_update_profile_esd(struct aw87xxx *aw87xxx, char *profile) ++{ ++ int ret = -1; ++ ++ if (0 == strncmp(profile, aw87xxx->prof_off_name, AW_PROFILE_STR_MAX)) ++ ret = aw87xxx_power_down(aw87xxx, profile); ++ else ++ ret = aw87xxx_power_on(aw87xxx, profile); ++ ++ return ret; ++} ++ ++char *aw87xxx_show_current_profile(int dev_index) ++{ ++ struct list_head *pos = NULL; ++ struct aw87xxx *aw87xxx = NULL; ++ ++ list_for_each(pos, &g_aw87xxx_list) { ++ aw87xxx = list_entry(pos, struct aw87xxx, list); ++ if (aw87xxx->dev_index == dev_index) { ++ AW_DEV_LOGI(aw87xxx->dev, "current profile is [%s]", ++ aw87xxx->current_profile); ++ return aw87xxx->current_profile; ++ } ++ } ++ ++ AW_LOGE("not found struct aw87xxx, dev_index = [%d]", dev_index); ++ return NULL; ++} ++EXPORT_SYMBOL(aw87xxx_show_current_profile); ++ ++int aw87xxx_set_profile(int dev_index, char *profile) ++{ ++ struct list_head *pos = NULL; ++ struct aw87xxx *aw87xxx = NULL; ++ ++ list_for_each(pos, &g_aw87xxx_list) { ++ aw87xxx = list_entry(pos, struct aw87xxx, list); ++ if (profile && aw87xxx->dev_index == dev_index) { ++ AW_DEV_LOGD(aw87xxx->dev, "set dev_index = %d, profile = %s", ++ dev_index, profile); ++ return aw87xxx_update_profile(aw87xxx, profile); ++ } ++ } ++ ++ AW_LOGE("not found struct aw87xxx, dev_index = [%d]", dev_index); ++ return -EINVAL; ++} ++EXPORT_SYMBOL(aw87xxx_set_profile); ++ ++int aw87xxx_set_profile_by_id(int dev_index, int profile_id) ++{ ++ char *profile = NULL; ++ ++ profile = aw87xxx_ctos_get_prof_name(profile_id); ++ if (profile == NULL) { ++ AW_LOGE("aw87xxx, dev_index[%d] profile[%d] not support!", ++ dev_index, profile_id); ++ return -EINVAL; ++ } ++ ++ AW_LOGI("aw87xxx, dev_index[%d] set profile[%s] by id[%d]", ++ dev_index, profile, profile_id); ++ return aw87xxx_set_profile(dev_index, profile); ++} ++EXPORT_SYMBOL(aw87xxx_set_profile_by_id); ++ ++/**************************************************************************** ++ * ++ * aw87xxx Kcontrols ++ * ++ ****************************************************************************/ ++static int aw87xxx_profile_switch_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ int count = 0; ++ char *name = NULL; ++ char *profile_name = NULL; ++ struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; ++ ++ if (aw87xxx == NULL) { ++ AW_LOGE("get struct aw87xxx failed"); ++ return -EINVAL; ++ } ++ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; ++ uinfo->count = 1; ++ ++ /*make sure have prof */ ++ count = aw87xxx_acf_get_profile_count(aw87xxx->dev, &aw87xxx->acf_info); ++ if (count <= 0) { ++ uinfo->value.enumerated.items = 0; ++ AW_DEV_LOGE(aw87xxx->dev, "get count[%d] failed", count); ++ return 0; ++ } ++ ++ uinfo->value.enumerated.items = count; ++ if (uinfo->value.enumerated.item >= count) ++ uinfo->value.enumerated.item = count - 1; ++ ++ name = uinfo->value.enumerated.name; ++ count = uinfo->value.enumerated.item; ++ profile_name = aw87xxx_acf_get_prof_name_form_index(aw87xxx->dev, ++ &aw87xxx->acf_info, count); ++ if (profile_name == NULL) { ++ strscpy(uinfo->value.enumerated.name, "NULL", ++ strlen("NULL") + 1); ++ return 0; ++ } ++ ++ strscpy(name, profile_name, sizeof(uinfo->value.enumerated.name)); ++ ++ return 0; ++} ++ ++static int aw87xxx_profile_switch_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ int ret = -1; ++ char *profile_name = NULL; ++ int index = ucontrol->value.integer.value[0]; ++ struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; ++ struct acf_bin_info *acf_info = NULL; ++ ++ if (aw87xxx == NULL) { ++ AW_LOGE("get struct aw87xxx failed"); ++ return -EINVAL; ++ } ++ ++ acf_info = &aw87xxx->acf_info; ++ ++ profile_name = aw87xxx_acf_get_prof_name_form_index(aw87xxx->dev, acf_info, index); ++ if (!profile_name) { ++ AW_DEV_LOGE(aw87xxx->dev, "not found profile name,index=[%d]", ++ index); ++ return -EINVAL; ++ } ++ ++ AW_DEV_LOGI(aw87xxx->dev, "set profile [%s]", profile_name); ++ ++ ret = aw87xxx_update_profile(aw87xxx, profile_name); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "set dev_index[%d] profile failed, profile = %s", ++ aw87xxx->dev_index, profile_name); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int aw87xxx_profile_switch_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ int index = 0; ++ char *profile; ++ struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; ++ ++ if (aw87xxx == NULL) { ++ AW_LOGE("get struct aw87xxx failed"); ++ return -EINVAL; ++ } ++ ++ if (!aw87xxx->current_profile) { ++ AW_DEV_LOGE(aw87xxx->dev, "profile not init"); ++ return -EINVAL; ++ } ++ ++ profile = aw87xxx->current_profile; ++ AW_DEV_LOGI(aw87xxx->dev, "current profile:[%s]", ++ aw87xxx->current_profile); ++ ++ ++ index = aw87xxx_acf_get_prof_index_form_name(aw87xxx->dev, ++ &aw87xxx->acf_info, aw87xxx->current_profile); ++ if (index < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "get profile index failed"); ++ return index; ++ } ++ ++ ucontrol->value.integer.value[0] = index; ++ ++ return 0; ++} ++ ++static int aw87xxx_vmax_get_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = 1; ++ uinfo->value.integer.min = INT_MIN; ++ uinfo->value.integer.max = AW_VMAX_MAX; ++ ++ return 0; ++} ++ ++static int aw87xxx_vmax_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ int ret = -1; ++ int vmax_val = 0; ++ struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; ++ ++ if (aw87xxx == NULL) { ++ AW_LOGE("get struct aw87xxx failed"); ++ return -EINVAL; ++ } ++ ++ ret = aw87xxx_monitor_no_dsp_get_vmax(&aw87xxx->monitor, &vmax_val); ++ if (ret < 0) ++ return ret; ++ ++ ucontrol->value.integer.value[0] = vmax_val; ++ AW_DEV_LOGI(aw87xxx->dev, "get vmax = [0x%x]", vmax_val); ++ ++ return 0; ++} ++ ++static int aw87xxx_monitor_switch_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ int count; ++ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; ++ uinfo->count = 1; ++ count = ARRAY_SIZE(aw87xxx_monitor_switch); ++ ++ uinfo->value.enumerated.items = count; ++ ++ if (uinfo->value.enumerated.item >= count) ++ uinfo->value.enumerated.item = count - 1; ++ ++ strscpy(uinfo->value.enumerated.name, ++ aw87xxx_monitor_switch[uinfo->value.enumerated.item], ++ strlen(aw87xxx_monitor_switch[uinfo->value.enumerated.item]) + 1); ++ ++ return 0; ++} ++ ++static int aw87xxx_monitor_switch_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ uint32_t ctrl_value = ucontrol->value.integer.value[0]; ++ struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; ++ struct aw_monitor *aw_monitor = &aw87xxx->monitor; ++ int ret = -1; ++ ++ ret = aw87xxx_dev_monitor_switch_set(aw_monitor, ctrl_value); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int aw87xxx_monitor_switch_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; ++ struct aw_monitor *aw_monitor = &aw87xxx->monitor; ++ ++ ucontrol->value.integer.value[0] = aw_monitor->monitor_hdr.monitor_switch; ++ ++ AW_DEV_LOGI(aw87xxx->dev, "monitor switch is %ld", ucontrol->value.integer.value[0]); ++ return 0; ++} ++ ++static int aw87xxx_spin_switch_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ int count; ++ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; ++ uinfo->count = 1; ++ count = ARRAY_SIZE(aw87xxx_spin_switch); ++ ++ uinfo->value.enumerated.items = count; ++ ++ if (uinfo->value.enumerated.item >= count) ++ uinfo->value.enumerated.item = count - 1; ++ ++ strscpy(uinfo->value.enumerated.name, ++ aw87xxx_spin_switch[uinfo->value.enumerated.item], ++ strlen(aw87xxx_spin_switch[uinfo->value.enumerated.item]) + 1); ++ ++ return 0; ++} ++ ++static int aw87xxx_spin_switch_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ uint32_t ctrl_value = 0; ++ int ret = 0; ++ struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; ++ ctrl_value = ucontrol->value.integer.value[0]; ++ ++ ret = aw87xxx_dsp_set_spin(ctrl_value); ++ if (ret) { ++ AW_DEV_LOGE(aw87xxx->dev, "write spin failed"); ++ return ret; ++ } ++ AW_DEV_LOGD(aw87xxx->dev, "write spin done ctrl_value=%d", ctrl_value); ++ return 0; ++} ++ ++static int aw87xxx_spin_switch_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; ++ ++ ucontrol->value.integer.value[0] = aw87xxx_dsp_get_spin(); ++ AW_DEV_LOGD(aw87xxx->dev, "current spin is %ld", ucontrol->value.integer.value[0]); ++ ++ return 0; ++} ++ ++ ++static int aw87xxx_kcontrol_dynamic_create(struct aw87xxx *aw87xxx, ++ void *codec) ++{ ++ struct snd_kcontrol_new *aw87xxx_kcontrol = NULL; ++ aw_snd_soc_codec_t *soc_codec = (aw_snd_soc_codec_t *)codec; ++ char *kctl_name[AW87XXX_PRIVATE_KCONTROL_NUM]; ++ int kcontrol_num = AW87XXX_PRIVATE_KCONTROL_NUM; ++ int ret = -1; ++ ++ AW_DEV_LOGD(aw87xxx->dev, "enter"); ++ aw87xxx->codec = soc_codec; ++ ++ aw87xxx_kcontrol = devm_kzalloc(aw87xxx->dev, ++ sizeof(struct snd_kcontrol_new) * kcontrol_num, ++ GFP_KERNEL); ++ if (aw87xxx_kcontrol == NULL) { ++ AW_DEV_LOGE(aw87xxx->dev, "aw87xxx_kcontrol devm_kzalloc failed"); ++ return -ENOMEM; ++ } ++ ++ kctl_name[0] = devm_kzalloc(aw87xxx->dev, AW_NAME_BUF_MAX, ++ GFP_KERNEL); ++ if (kctl_name[0] == NULL) ++ return -ENOMEM; ++ ++ snprintf(kctl_name[0], AW_NAME_BUF_MAX, "aw87xxx_profile_switch_%d", ++ aw87xxx->dev_index); ++ ++ aw87xxx_kcontrol[0].name = kctl_name[0]; ++ aw87xxx_kcontrol[0].iface = SNDRV_CTL_ELEM_IFACE_MIXER; ++ aw87xxx_kcontrol[0].info = aw87xxx_profile_switch_info; ++ aw87xxx_kcontrol[0].get = aw87xxx_profile_switch_get; ++ aw87xxx_kcontrol[0].put = aw87xxx_profile_switch_put; ++ aw87xxx_kcontrol[0].private_value = (unsigned long)aw87xxx; ++ ++ kctl_name[1] = devm_kzalloc(aw87xxx->codec->dev, AW_NAME_BUF_MAX, ++ GFP_KERNEL); ++ if (kctl_name[1] == NULL) ++ return -ENOMEM; ++ ++ snprintf(kctl_name[1], AW_NAME_BUF_MAX, "aw87xxx_vmax_get_%d", ++ aw87xxx->dev_index); ++ ++ aw87xxx_kcontrol[1].name = kctl_name[1]; ++ aw87xxx_kcontrol[1].iface = SNDRV_CTL_ELEM_IFACE_MIXER; ++ aw87xxx_kcontrol[1].access = SNDRV_CTL_ELEM_ACCESS_READ; ++ aw87xxx_kcontrol[1].info = aw87xxx_vmax_get_info; ++ aw87xxx_kcontrol[1].get = aw87xxx_vmax_get; ++ aw87xxx_kcontrol[1].private_value = (unsigned long)aw87xxx; ++ ++ kctl_name[2] = devm_kzalloc(aw87xxx->codec->dev, AW_NAME_BUF_MAX, ++ GFP_KERNEL); ++ if (kctl_name[2] == NULL) ++ return -ENOMEM; ++ ++ snprintf(kctl_name[2], AW_NAME_BUF_MAX, "aw87xxx_monitor_switch_%d", ++ aw87xxx->dev_index); ++ ++ aw87xxx_kcontrol[2].name = kctl_name[2]; ++ aw87xxx_kcontrol[2].iface = SNDRV_CTL_ELEM_IFACE_MIXER; ++ aw87xxx_kcontrol[2].info = aw87xxx_monitor_switch_info; ++ aw87xxx_kcontrol[2].get = aw87xxx_monitor_switch_get; ++ aw87xxx_kcontrol[2].put = aw87xxx_monitor_switch_put; ++ aw87xxx_kcontrol[2].private_value = (unsigned long)aw87xxx; ++ ++ ret = aw_componet_codec_ops.add_codec_controls(aw87xxx->codec, ++ aw87xxx_kcontrol, kcontrol_num); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "add codec controls failed, ret = %d", ++ ret); ++ return ret; ++ } ++ ++ AW_DEV_LOGI(aw87xxx->dev, "add codec controls[%s,%s,%s]", ++ aw87xxx_kcontrol[0].name, ++ aw87xxx_kcontrol[1].name, ++ aw87xxx_kcontrol[2].name); ++ ++ return 0; ++} ++ ++static int aw87xxx_public_kcontrol_create(struct aw87xxx *aw87xxx, ++ void *codec) ++{ ++ struct snd_kcontrol_new *aw87xxx_kcontrol = NULL; ++ aw_snd_soc_codec_t *soc_codec = (aw_snd_soc_codec_t *)codec; ++ char *kctl_name[AW87XXX_PUBLIC_KCONTROL_NUM]; ++ int kcontrol_num = AW87XXX_PUBLIC_KCONTROL_NUM; ++ int ret = -1; ++ ++ AW_DEV_LOGD(aw87xxx->dev, "enter"); ++ aw87xxx->codec = soc_codec; ++ ++ aw87xxx_kcontrol = devm_kzalloc(aw87xxx->dev, ++ sizeof(struct snd_kcontrol_new) * kcontrol_num, ++ GFP_KERNEL); ++ if (aw87xxx_kcontrol == NULL) { ++ AW_DEV_LOGE(aw87xxx->dev, "aw87xxx_kcontrol devm_kzalloc failed"); ++ return -ENOMEM; ++ } ++ ++ kctl_name[0] = devm_kzalloc(aw87xxx->dev, AW_NAME_BUF_MAX, ++ GFP_KERNEL); ++ if (kctl_name[0] == NULL) ++ return -ENOMEM; ++ ++ snprintf(kctl_name[0], AW_NAME_BUF_MAX, "aw87xxx_spin_switch"); ++ ++ aw87xxx_kcontrol[0].name = kctl_name[0]; ++ aw87xxx_kcontrol[0].iface = SNDRV_CTL_ELEM_IFACE_MIXER; ++ aw87xxx_kcontrol[0].info = aw87xxx_spin_switch_info; ++ aw87xxx_kcontrol[0].get = aw87xxx_spin_switch_get; ++ aw87xxx_kcontrol[0].put = aw87xxx_spin_switch_put; ++ aw87xxx_kcontrol[0].private_value = (unsigned long)aw87xxx; ++ ++ ret = aw_componet_codec_ops.add_codec_controls(aw87xxx->codec, ++ aw87xxx_kcontrol, kcontrol_num); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "add codec controls failed, ret = %d", ++ ret); ++ return ret; ++ } ++ ++ AW_DEV_LOGI(aw87xxx->dev, "add public codec controls[%s]", ++ aw87xxx_kcontrol[0].name); ++ ++ return 0; ++} ++ ++/**************************************************************************** ++ * ++ *aw87xxx kcontrol create ++ * ++ ****************************************************************************/ ++int aw87xxx_add_codec_controls(void *codec) ++{ ++ struct list_head *pos = NULL; ++ struct aw87xxx *aw87xxx = NULL; ++ int ret = -1; ++ ++ list_for_each(pos, &g_aw87xxx_list) { ++ aw87xxx = list_entry(pos, struct aw87xxx, list); ++ ret = aw87xxx_kcontrol_dynamic_create(aw87xxx, codec); ++ if (ret < 0) ++ return ret; ++ ++ if (aw87xxx->dev_index == 0) { ++ ret = aw87xxx_public_kcontrol_create(aw87xxx, codec); ++ if (ret < 0) ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(aw87xxx_add_codec_controls); ++ ++ ++/**************************************************************************** ++ * ++ * aw87xxx firmware cfg load ++ * ++ ***************************************************************************/ ++static void aw87xxx_fw_cfg_free(struct aw87xxx *aw87xxx) ++{ ++ AW_DEV_LOGD(aw87xxx->dev, "enter"); ++ aw87xxx_acf_profile_free(aw87xxx->dev, &aw87xxx->acf_info); ++ aw87xxx_monitor_cfg_free(&aw87xxx->monitor); ++} ++ ++static int aw87xxx_init_default_prof(struct aw87xxx *aw87xxx) ++{ ++ char *profile = NULL; ++ ++ profile = aw87xxx_acf_get_prof_off_name(aw87xxx->dev, &aw87xxx->acf_info); ++ if (profile == NULL) { ++ AW_DEV_LOGE(aw87xxx->dev, "get profile off name failed"); ++ return -EINVAL; ++ } ++ ++ snprintf(aw87xxx->prof_off_name, AW_PROFILE_STR_MAX, "%s", profile); ++ aw87xxx->current_profile = profile; ++ AW_DEV_LOGI(aw87xxx->dev, "init profile name [%s]", ++ aw87xxx->current_profile); ++ ++ return 0; ++} ++ ++static void aw87xxx_fw_load_retry(struct aw87xxx *aw87xxx) ++{ ++ struct acf_bin_info *acf_info = &aw87xxx->acf_info; ++ int ram_timer_val = 2000; ++ ++ AW_DEV_LOGD(aw87xxx->dev, "failed to read [%s]", ++ aw87xxx->fw_name); ++ ++ if (acf_info->load_count < AW_LOAD_FW_RETRIES) { ++ AW_DEV_LOGD(aw87xxx->dev, ++ "restart hrtimer to load firmware"); ++ schedule_delayed_work(&aw87xxx->fw_load_work, ++ msecs_to_jiffies(ram_timer_val)); ++ } else { ++ acf_info->load_count = 0; ++ AW_DEV_LOGE(aw87xxx->dev, ++ "can not load firmware,please check name or file exists"); ++ return; ++ } ++ acf_info->load_count++; ++} ++ ++static void aw87xxx_fw_load(const struct firmware *fw, void *context) ++{ ++ int ret = -1; ++ struct aw87xxx *aw87xxx = context; ++ struct acf_bin_info *acf_info = &aw87xxx->acf_info; ++ ++ AW_DEV_LOGD(aw87xxx->dev, "enter"); ++ ++ if (!fw) { ++ aw87xxx_fw_load_retry(aw87xxx); ++ return; ++ } ++ ++ AW_DEV_LOGD(aw87xxx->dev, "loaded %s - size: %ld", ++ aw87xxx->fw_name, (u_long)(fw ? fw->size : 0)); ++ ++ mutex_lock(&aw87xxx->reg_lock); ++ acf_info->fw_data = vmalloc(fw->size); ++ if (!acf_info->fw_data) { ++ AW_DEV_LOGE(aw87xxx->dev, "fw_data kzalloc memory failed"); ++ goto exit_vmalloc_failed; ++ } ++ memset(acf_info->fw_data, 0, fw->size); ++ memcpy(acf_info->fw_data, fw->data, fw->size); ++ acf_info->fw_size = fw->size; ++ ++ ret = aw87xxx_acf_parse(aw87xxx->dev, &aw87xxx->acf_info); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "fw_data parse failed"); ++ goto exit_acf_parse_failed; ++ } ++ ++ ret = aw87xxx_init_default_prof(aw87xxx); ++ if (ret < 0) { ++ aw87xxx_fw_cfg_free(aw87xxx); ++ goto exit_acf_parse_failed; ++ } ++ ++ AW_DEV_LOGI(aw87xxx->dev, "acf parse succeed"); ++ mutex_unlock(&aw87xxx->reg_lock); ++ release_firmware(fw); ++ // Updating profile to "Music" because the firmware is set to "off" during init ++ aw87xxx_update_profile(aw87xxx, AW87XXX_PROF_MUSIC); ++ ++ return; ++ ++exit_acf_parse_failed: ++exit_vmalloc_failed: ++ release_firmware(fw); ++ mutex_unlock(&aw87xxx->reg_lock); ++} ++ ++static void aw87xxx_fw_load_work_routine(struct work_struct *work) ++{ ++ struct aw87xxx *aw87xxx = container_of(work, ++ struct aw87xxx, fw_load_work.work); ++ struct aw_prof_info *prof_info = &aw87xxx->acf_info.prof_info; ++ ++ AW_DEV_LOGD(aw87xxx->dev, "enter"); ++ ++ if (prof_info->status == AW_ACF_WAIT) { ++ request_firmware_nowait(THIS_MODULE, ++// FW_ACTION_HOTPLUG, ++ FW_ACTION_UEVENT, ++ aw87xxx->fw_name, ++ aw87xxx->dev, ++ GFP_KERNEL, aw87xxx, ++ aw87xxx_fw_load); ++ } ++} ++ ++static void aw87xxx_fw_load_init(struct aw87xxx *aw87xxx) ++{ ++#ifdef AW_CFG_UPDATE_DELAY ++ int cfg_timer_val = AW_CFG_UPDATE_DELAY_TIMER; ++#else ++ int cfg_timer_val = 0; ++#endif ++ AW_DEV_LOGI(aw87xxx->dev, "enter"); ++ snprintf(aw87xxx->fw_name, AW87XXX_FW_NAME_MAX, "%s", AW87XXX_FW_BIN_NAME); ++ aw87xxx_acf_init(&aw87xxx->aw_dev, &aw87xxx->acf_info, aw87xxx->dev_index); ++ ++ INIT_DELAYED_WORK(&aw87xxx->fw_load_work, aw87xxx_fw_load_work_routine); ++ schedule_delayed_work(&aw87xxx->fw_load_work, ++ msecs_to_jiffies(cfg_timer_val)); ++} ++ ++/**************************************************************************** ++ * ++ *aw87xxx attribute node ++ * ++ ****************************************************************************/ ++static ssize_t aw87xxx_attr_get_reg(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ssize_t len = 0; ++ int ret = 0; ++ unsigned int i = 0; ++ unsigned char reg_val = 0; ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ struct aw_device *aw_dev = &aw87xxx->aw_dev; ++ ++ mutex_lock(&aw87xxx->reg_lock); ++ for (i = 0; i < aw_dev->reg_max_addr; i++) { ++ if (!(aw_dev->reg_access[i] & AW_DEV_REG_RD_ACCESS)) ++ continue; ++ ret = aw87xxx_dev_i2c_read_byte(&aw87xxx->aw_dev, i, ®_val); ++ if (ret < 0) { ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ "read reg [0x%x] failed\n", i); ++ AW_DEV_LOGE(aw87xxx->dev, "read reg [0x%x] failed", i); ++ } else { ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ "reg:0x%02X=0x%02X\n", i, reg_val); ++ AW_DEV_LOGD(aw87xxx->dev, "reg:0x%02X=0x%02X", ++ i, reg_val); ++ } ++ } ++ mutex_unlock(&aw87xxx->reg_lock); ++ ++ return len; ++} ++ ++static ssize_t aw87xxx_attr_set_reg(struct device *dev, ++ struct device_attribute *attr, const char *buf, ++ size_t len) ++{ ++ unsigned int databuf[2] = { 0 }; ++ int ret = 0; ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ ++ mutex_lock(&aw87xxx->reg_lock); ++ if (sscanf(buf, "0x%x 0x%x", &databuf[0], &databuf[1]) == 2) { ++ if (databuf[0] >= aw87xxx->aw_dev.reg_max_addr) { ++ AW_DEV_LOGE(aw87xxx->dev, "set reg[0x%x] error,is out of reg_addr_max[0x%x]", ++ databuf[0], aw87xxx->aw_dev.reg_max_addr); ++ mutex_unlock(&aw87xxx->reg_lock); ++ return -EINVAL; ++ } ++ ++ ret = aw87xxx_dev_i2c_write_byte(&aw87xxx->aw_dev, ++ databuf[0], databuf[1]); ++ if (ret < 0) ++ AW_DEV_LOGE(aw87xxx->dev, "set [0x%x]=0x%x failed", ++ databuf[0], databuf[1]); ++ else ++ AW_DEV_LOGD(aw87xxx->dev, "set [0x%x]=0x%x succeed", ++ databuf[0], databuf[1]); ++ } else { ++ AW_DEV_LOGE(aw87xxx->dev, "i2c write cmd input error"); ++ } ++ mutex_unlock(&aw87xxx->reg_lock); ++ ++ return len; ++} ++ ++static ssize_t aw87xxx_attr_get_profile(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ssize_t len = 0; ++ unsigned int i = 0; ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ struct aw_prof_info *prof_info = &aw87xxx->acf_info.prof_info; ++ ++ if (!prof_info->status) { ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ "profile_cfg not load\n"); ++ return len; ++ } ++ ++ AW_DEV_LOGI(aw87xxx->dev, "current profile:[%s]", aw87xxx->current_profile); ++ ++ for (i = 0; i < prof_info->count; i++) { ++ if (!strncmp(aw87xxx->current_profile, prof_info->prof_name_list[i], ++ AW_PROFILE_STR_MAX)) ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ ">%s\n", prof_info->prof_name_list[i]); ++ else ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ " %s\n", prof_info->prof_name_list[i]); ++ } ++ ++ return len; ++} ++ ++static ssize_t aw87xxx_attr_set_profile(struct device *dev, ++ struct device_attribute *attr, const char *buf, ++ size_t len) ++{ ++ char profile[AW_PROFILE_STR_MAX] = {0}; ++ int ret = 0; ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ ++ if (strlen(buf) > AW_PROFILE_STR_MAX) { ++ AW_DEV_LOGE(aw87xxx->dev, "input profile_str_len is out of max[%d]", ++ AW_PROFILE_STR_MAX); ++ return -EINVAL; ++ } ++ ++ if (sscanf(buf, "%s", profile) == 1) { ++ AW_DEV_LOGD(aw87xxx->dev, "set profile [%s]", profile); ++ ret = aw87xxx_update_profile(aw87xxx, profile); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "set profile[%s] failed", ++ profile); ++ return ret; ++ } ++ } ++ ++ return len; ++} ++ ++static ssize_t aw87xxx_attr_get_hwen(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ssize_t len = 0; ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ int hwen = aw87xxx->aw_dev.hwen_status; ++ ++ if (hwen >= AW_DEV_HWEN_INVALID) ++ len += snprintf(buf + len, PAGE_SIZE - len, "hwen_status: invalid\n"); ++ else if (hwen == AW_DEV_HWEN_ON) ++ len += snprintf(buf + len, PAGE_SIZE - len, "hwen_status: on\n"); ++ else if (hwen == AW_DEV_HWEN_OFF) ++ len += snprintf(buf + len, PAGE_SIZE - len, "hwen_status: off\n"); ++ ++ return len; ++} ++ ++static ssize_t aw87xxx_attr_set_hwen(struct device *dev, ++ struct device_attribute *attr, const char *buf, ++ size_t len) ++{ ++ int ret = -1; ++ unsigned int state; ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ ++ ret = kstrtouint(buf, 0, &state); ++ if (ret) { ++ AW_DEV_LOGE(aw87xxx->dev, "fail to channelge str to int"); ++ return ret; ++ } ++ ++ mutex_lock(&aw87xxx->reg_lock); ++ if (state == AW_DEV_HWEN_OFF) ++ aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, false); /*OFF*/ ++ else if (state == AW_DEV_HWEN_ON) ++ aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, true); /*ON*/ ++ else ++ AW_DEV_LOGE(aw87xxx->dev, "input [%d] error, hwen_on=[%d],hwen_off=[%d]", ++ state, AW_DEV_HWEN_ON, AW_DEV_HWEN_OFF); ++ mutex_unlock(&aw87xxx->reg_lock); ++ return len; ++} ++ ++int aw87xxx_awrw_write(struct aw87xxx *aw87xxx, ++ const char *buf, size_t count) ++{ ++ int i = 0, ret = -1; ++ char *data_buf = NULL; ++ int buf_len = 0; ++ int temp_data = 0; ++ int data_str_size = 0; ++ char *reg_data; ++ struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; ++ ++ AW_DEV_LOGD(aw87xxx->dev, "enter"); ++ /* one addr or one data string Composition of Contains two bytes of symbol(0X)*/ ++ /* and two byte of hexadecimal data*/ ++ data_str_size = 2 + 2 * AWRW_DATA_BYTES; ++ ++ /* The buf includes the first address of the register to be written and all data */ ++ buf_len = AWRW_ADDR_BYTES + packet->reg_num * AWRW_DATA_BYTES; ++ AW_DEV_LOGI(aw87xxx->dev, "buf_len = %d,reg_num = %d", buf_len, packet->reg_num); ++ data_buf = vmalloc(buf_len); ++ if (data_buf == NULL) { ++ AW_DEV_LOGE(aw87xxx->dev, "alloc memory failed"); ++ return -ENOMEM; ++ } ++ memset(data_buf, 0, buf_len); ++ ++ data_buf[0] = packet->reg_addr; ++ reg_data = data_buf + 1; ++ ++ AW_DEV_LOGD(aw87xxx->dev, "reg_addr: 0x%02x", data_buf[0]); ++ ++ /*ag:0x00 0x01 0x01 0x01 0x01 0x00\x0a*/ ++ for (i = 0; i < packet->reg_num; i++) { ++ ret = sscanf(buf + AWRW_HDR_LEN + 1 + i * (data_str_size + 1), ++ "0x%x", &temp_data); ++ if (ret != 1) { ++ AW_DEV_LOGE(aw87xxx->dev, "sscanf failed,ret=%d", ret); ++ vfree(data_buf); ++ data_buf = NULL; ++ return ret; ++ } ++ reg_data[i] = temp_data; ++ AW_DEV_LOGD(aw87xxx->dev, "[%d] : 0x%02x", i, reg_data[i]); ++ } ++ ++ mutex_lock(&aw87xxx->reg_lock); ++ ret = i2c_master_send(aw87xxx->aw_dev.i2c, data_buf, buf_len); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "write failed"); ++ vfree(data_buf); ++ data_buf = NULL; ++ return -EFAULT; ++ } ++ mutex_unlock(&aw87xxx->reg_lock); ++ ++ vfree(data_buf); ++ data_buf = NULL; ++ ++ AW_DEV_LOGD(aw87xxx->dev, "down"); ++ return 0; ++} ++ ++static int aw87xxx_awrw_data_check(struct aw87xxx *aw87xxx, ++ int *data, size_t count) ++{ ++ struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; ++ int req_data_len = 0; ++ int act_data_len = 0; ++ int data_str_size = 0; ++ ++ if ((data[AWRW_HDR_ADDR_BYTES] != AWRW_ADDR_BYTES) || ++ (data[AWRW_HDR_DATA_BYTES] != AWRW_DATA_BYTES)) { ++ AW_DEV_LOGE(aw87xxx->dev, "addr_bytes [%d] or data_bytes [%d] unsupport", ++ data[AWRW_HDR_ADDR_BYTES], data[AWRW_HDR_DATA_BYTES]); ++ return -EINVAL; ++ } ++ ++ /* one data string Composition of Contains two bytes of symbol(0x)*/ ++ /* and two byte of hexadecimal data*/ ++ data_str_size = 2 + 2 * AWRW_DATA_BYTES; ++ act_data_len = count - AWRW_HDR_LEN - 1; ++ ++ /* There is a comma(,) or space between each piece of data */ ++ if (data[AWRW_HDR_WR_FLAG] == AWRW_FLAG_WRITE) { ++ /*ag:0x00 0x01 0x01 0x01 0x01 0x00\x0a*/ ++ req_data_len = (data_str_size + 1) * packet->reg_num; ++ if (req_data_len > act_data_len) { ++ AW_DEV_LOGE(aw87xxx->dev, "data_len checkfailed,requeset data_len [%d],actaul data_len [%d]", ++ req_data_len, act_data_len); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++/* flag addr_bytes data_bytes reg_num reg_addr*/ ++static int aw87xxx_awrw_parse_buf(struct aw87xxx *aw87xxx, ++ const char *buf, size_t count, int *wr_status) ++{ ++ int data[AWRW_HDR_MAX] = {0}; ++ struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; ++ int ret = -1; ++ ++ if (sscanf(buf, "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x", ++ &data[AWRW_HDR_WR_FLAG], &data[AWRW_HDR_ADDR_BYTES], ++ &data[AWRW_HDR_DATA_BYTES], &data[AWRW_HDR_REG_NUM], ++ &data[AWRW_HDR_REG_ADDR]) == 5) { ++ ++ packet->reg_addr = data[AWRW_HDR_REG_ADDR]; ++ packet->reg_num = data[AWRW_HDR_REG_NUM]; ++ *wr_status = data[AWRW_HDR_WR_FLAG]; ++ ret = aw87xxx_awrw_data_check(aw87xxx, data, count); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++static ssize_t aw87xxx_attr_awrw_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; ++ int wr_status = 0; ++ int ret = -1; ++ ++ if (count < AWRW_HDR_LEN) { ++ AW_DEV_LOGE(aw87xxx->dev, "data count too smaller, please check write format"); ++ AW_DEV_LOGE(aw87xxx->dev, "string %s,count=%ld", ++ buf, (u_long)count); ++ return -EINVAL; ++ } ++ ++ AW_DEV_LOGI(aw87xxx->dev, "string:[%s],count=%ld", buf, (u_long)count); ++ ret = aw87xxx_awrw_parse_buf(aw87xxx, buf, count, &wr_status); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "can not parse string"); ++ return ret; ++ } ++ ++ if (wr_status == AWRW_FLAG_WRITE) { ++ ret = aw87xxx_awrw_write(aw87xxx, buf, count); ++ if (ret < 0) ++ return ret; ++ } else if (wr_status == AWRW_FLAG_READ) { ++ packet->status = AWRW_I2C_ST_READ; ++ AW_DEV_LOGI(aw87xxx->dev, "read_cmd:reg_addr[0x%02x], reg_num[%d]", ++ packet->reg_addr, packet->reg_num); ++ } else { ++ AW_DEV_LOGE(aw87xxx->dev, "please check str format, unsupport read_write_status: %d", ++ wr_status); ++ return -EINVAL; ++ } ++ ++ return count; ++} ++ ++static ssize_t aw87xxx_attr_awrw_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; ++ int data_len = 0; ++ size_t len = 0; ++ int ret = -1, i = 0; ++ char *reg_data = NULL; ++ ++ if (packet->status != AWRW_I2C_ST_READ) { ++ AW_DEV_LOGE(aw87xxx->dev, "please write read cmd first"); ++ return -EINVAL; ++ } ++ ++ data_len = AWRW_DATA_BYTES * packet->reg_num; ++ reg_data = (char *)vmalloc(data_len); ++ if (reg_data == NULL) { ++ AW_DEV_LOGE(aw87xxx->dev, "memory alloc failed"); ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ mutex_lock(&aw87xxx->reg_lock); ++ ret = aw87xxx_dev_i2c_read_msg(&aw87xxx->aw_dev, packet->reg_addr, ++ (char *)reg_data, data_len); ++ if (ret < 0) { ++ ret = -EFAULT; ++ mutex_unlock(&aw87xxx->reg_lock); ++ goto exit; ++ } ++ mutex_unlock(&aw87xxx->reg_lock); ++ ++ AW_DEV_LOGI(aw87xxx->dev, "reg_addr 0x%02x, reg_num %d", ++ packet->reg_addr, packet->reg_num); ++ ++ for (i = 0; i < data_len; i++) { ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ "0x%02x,", reg_data[i]); ++ AW_DEV_LOGI(aw87xxx->dev, "0x%02x", reg_data[i]); ++ } ++ ++ ret = len; ++ ++exit: ++ if (reg_data) { ++ vfree(reg_data); ++ reg_data = NULL; ++ } ++ packet->status = AWRW_I2C_ST_NONE; ++ return ret; ++} ++ ++static ssize_t aw87xxx_drv_ver_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ssize_t len = 0; ++ ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ "driver_ver: %s \n", AW87XXX_DRIVER_VERSION); ++ ++ return len; ++} ++ ++static DEVICE_ATTR(reg, S_IWUSR | S_IRUGO, ++ aw87xxx_attr_get_reg, aw87xxx_attr_set_reg); ++static DEVICE_ATTR(profile, S_IWUSR | S_IRUGO, ++ aw87xxx_attr_get_profile, aw87xxx_attr_set_profile); ++static DEVICE_ATTR(hwen, S_IWUSR | S_IRUGO, ++ aw87xxx_attr_get_hwen, aw87xxx_attr_set_hwen); ++static DEVICE_ATTR(awrw, S_IWUSR | S_IRUGO, ++ aw87xxx_attr_awrw_show, aw87xxx_attr_awrw_store); ++static DEVICE_ATTR(drv_ver, S_IRUGO, aw87xxx_drv_ver_show, NULL); ++ ++static struct attribute *aw87xxx_attributes[] = { ++ &dev_attr_reg.attr, ++ &dev_attr_profile.attr, ++ &dev_attr_hwen.attr, ++ &dev_attr_awrw.attr, ++ &dev_attr_drv_ver.attr, ++ NULL ++}; ++ ++static struct attribute_group aw87xxx_attribute_group = { ++ .attrs = aw87xxx_attributes ++}; ++ ++/**************************************************************************** ++ * ++ *aw87xxx device probe ++ * ++ ****************************************************************************/ ++static const struct acpi_gpio_params reset_gpio = { 0, 0, false }; ++static const struct acpi_gpio_mapping reset_acpi_gpios[] = { ++ { "reset-gpios", &reset_gpio, 1 }, ++ { } ++}; ++ ++static struct aw87xxx *aw87xxx_malloc_init(struct i2c_client *client) ++{ ++ struct aw87xxx *aw87xxx = NULL; ++ ++ aw87xxx = devm_kzalloc(&client->dev, sizeof(struct aw87xxx), ++ GFP_KERNEL); ++ if (aw87xxx == NULL) { ++ AW_DEV_LOGE(&client->dev, "failed to devm_kzalloc aw87xxx"); ++ return NULL; ++ } ++ memset(aw87xxx, 0, sizeof(struct aw87xxx)); ++ ++ aw87xxx->dev = &client->dev; ++ aw87xxx->aw_dev.dev = &client->dev; ++ aw87xxx->aw_dev.i2c_bus = client->adapter->nr; ++ aw87xxx->aw_dev.i2c_addr = client->addr; ++ aw87xxx->aw_dev.i2c = client; ++ aw87xxx->aw_dev.hwen_status = false; ++ aw87xxx->aw_dev.reg_access = NULL; ++ aw87xxx->aw_dev.hwen_status = AW_DEV_HWEN_INVALID; ++ aw87xxx->off_bin_status = AW87XXX_NO_OFF_BIN; ++ aw87xxx->codec = NULL; ++ aw87xxx->current_profile = aw87xxx->prof_off_name; ++ ++ mutex_init(&aw87xxx->reg_lock); ++ ++ AW_DEV_LOGI(&client->dev, "struct aw87xxx devm_kzalloc and init down"); ++ return aw87xxx; ++} ++ ++static int aw87xxx_i2c_probe(struct i2c_client *client) ++{ ++ struct device_node *dev_node = client->dev.of_node; ++ struct aw87xxx *aw87xxx = NULL; ++ struct gpio_desc *gpiod = NULL; ++ int ret = -1; ++ ++ ++// To do, add this function ++//acpi_dev_add_driver_gpios() ++ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { ++ AW_DEV_LOGE(&client->dev, "check_functionality failed"); ++ ret = -ENODEV; ++ goto exit_check_functionality_failed; ++ } ++ ++ /* aw87xxx i2c_dev struct init */ ++ aw87xxx = aw87xxx_malloc_init(client); ++ if (aw87xxx == NULL) ++ goto exit_malloc_init_failed; ++ ++ i2c_set_clientdata(client, aw87xxx); ++ ++ aw87xxx_device_parse_port_id_dt(&aw87xxx->aw_dev); ++ aw87xxx_device_parse_topo_id_dt(&aw87xxx->aw_dev); ++ ++ /* aw87xxx Get ACPI GPIO */ ++/* ++ ret = devm_acpi_dev_add_driver_gpios(aw87xxx->dev, reset_acpi_gpios); ++ if(ret){ ++ AW_DEV_LOGE(aw87xxx->dev, "Unable to add GPIO mapping table"); ++ goto exit_device_init_failed; ++ } ++ ++ gpiod = gpiod_get_optional(aw87xxx->dev, "reset", GPIOD_OUT_LOW); ++ if (IS_ERR(gpiod)){ ++ AW_DEV_LOGE(aw87xxx->dev, "Get gpiod failed"); ++ goto exit_device_init_failed; ++ } ++ ++ aw87xxx->aw_dev.rst_gpio = desc_to_gpio(gpiod); ++ aw87xxx->aw_dev.hwen_status = AW_DEV_HWEN_OFF; ++ AW_DEV_LOGI(aw87xxx->dev, "reset gpio[%d] parse succeed", aw87xxx->aw_dev.rst_gpio); ++ if (gpio_is_valid(aw87xxx->aw_dev.rst_gpio)) { ++ ret = devm_gpio_request_one(aw87xxx->dev, aw87xxx->aw_dev.rst_gpio, GPIOF_OUT_INIT_LOW, "aw87xxx_reset"); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "reset request failed"); ++ goto exit_device_init_failed; ++ } ++ } ++*/ ++ ++ /*Disabling RESET GPIO*/ ++ AW_DEV_LOGI(aw87xxx->dev, "no reset gpio provided, hardware reset unavailable"); ++ aw87xxx->aw_dev.rst_gpio = AW_NO_RESET_GPIO; ++ aw87xxx->aw_dev.hwen_status = AW_DEV_HWEN_INVALID; ++ ++ ++ /*hw power on PA*/ ++ aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, true); ++ ++ /* aw87xxx devices private attributes init */ ++ ret = aw87xxx_dev_init(&aw87xxx->aw_dev); ++ if (ret < 0) ++ goto exit_device_init_failed; ++ ++ /*product register reset */ ++ aw87xxx_dev_soft_reset(&aw87xxx->aw_dev); ++ ++ /*hw power off */ ++ aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, false); ++ ++ /* create debug attrbute nodes */ ++ ret = sysfs_create_group(&aw87xxx->dev->kobj, &aw87xxx_attribute_group); ++ if (ret < 0) ++ AW_DEV_LOGE(aw87xxx->dev, "failed to create sysfs nodes, will not allowed to use"); ++ ++ /* cfg_load init */ ++ aw87xxx_fw_load_init(aw87xxx); ++ ++ /*monitor init*/ ++ aw87xxx_monitor_init(aw87xxx->dev, &aw87xxx->monitor, dev_node); ++ ++ /*add device to total list */ ++ mutex_lock(&g_aw87xxx_mutex_lock); ++ g_aw87xxx_dev_cnt++; ++ list_add(&aw87xxx->list, &g_aw87xxx_list); ++ aw87xxx->dev_index = g_aw87xxx_dev_cnt; ++ ++ mutex_unlock(&g_aw87xxx_mutex_lock); ++ AW_DEV_LOGI(aw87xxx->dev, "succeed, dev_index=[%d], g_aw87xxx_dev_cnt= [%d]", ++ aw87xxx->dev_index, g_aw87xxx_dev_cnt); ++ ++ return 0; ++ ++exit_device_init_failed: ++ AW_DEV_LOGE(aw87xxx->dev, "pa init failed"); ++ ++ devm_kfree(&client->dev, aw87xxx); ++ aw87xxx = NULL; ++exit_malloc_init_failed: ++exit_check_functionality_failed: ++ return ret; ++} ++ ++static void aw87xxx_i2c_remove(struct i2c_client *client) ++{ ++ struct aw87xxx *aw87xxx = i2c_get_clientdata(client); ++ ++ aw87xxx_monitor_exit(&aw87xxx->monitor); ++ ++ /*rm attr node*/ ++ sysfs_remove_group(&aw87xxx->dev->kobj, &aw87xxx_attribute_group); ++ ++ aw87xxx_fw_cfg_free(aw87xxx); ++ ++ mutex_lock(&g_aw87xxx_mutex_lock); ++ g_aw87xxx_dev_cnt--; ++ list_del(&aw87xxx->list); ++ mutex_unlock(&g_aw87xxx_mutex_lock); ++ ++ devm_kfree(&client->dev, aw87xxx); ++ aw87xxx = NULL; ++ ++// return 0; ++} ++ ++static void aw87xxx_i2c_shutdown(struct i2c_client *client) ++{ ++ struct aw87xxx *aw87xxx = i2c_get_clientdata(client); ++ ++ AW_DEV_LOGI(&client->dev, "enter"); ++ ++ /*soft and hw power off*/ ++ aw87xxx_update_profile(aw87xxx, aw87xxx->prof_off_name); ++} ++ ++static const struct acpi_device_id aw87xxx_acpi_match[] = { ++ { "AWDZ8830", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(acpi, aw87xxx_acpi_match); ++ ++// This is not necessary if the acpi match probes correctly. This is needed for userspace `new_device() functionality ++static const struct i2c_device_id aw87xxx_i2c_id[] = { ++ {AW87XXX_I2C_NAME, 0}, ++ {}, ++}; ++ ++static struct i2c_driver aw87xxx_i2c_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = AW87XXX_I2C_NAME, ++ .acpi_match_table = aw87xxx_acpi_match, ++ }, ++ .probe = aw87xxx_i2c_probe, ++ .remove = aw87xxx_i2c_remove, ++ .shutdown = aw87xxx_i2c_shutdown, ++ .id_table = aw87xxx_i2c_id, ++}; ++ ++module_i2c_driver(aw87xxx_i2c_driver) ++ ++MODULE_AUTHOR(""); ++MODULE_DESCRIPTION("awinic aw87xxx pa driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/sound/soc/codecs/aw87xxx/aw87xxx.h b/sound/soc/codecs/aw87xxx/aw87xxx.h +new file mode 100644 +index 000000000..45d3cea77 +--- /dev/null ++++ b/sound/soc/codecs/aw87xxx/aw87xxx.h +@@ -0,0 +1,121 @@ ++#ifndef __AW87XXX_H__ ++#define __AW87XXX_H__ ++#include ++#include ++#include ++#include ++ ++#include "aw87xxx_device.h" ++#include "aw87xxx_monitor.h" ++#include "aw87xxx_acf_bin.h" ++ ++#define AW_CFG_UPDATE_DELAY ++#define AW_CFG_UPDATE_DELAY_TIMER (3000) ++ ++#define AW87XXX_NO_OFF_BIN (0) ++#define AW87XXX_OFF_BIN_OK (1) ++ ++#define AW87XXX_PRIVATE_KCONTROL_NUM (3) ++#define AW87XXX_PUBLIC_KCONTROL_NUM (1) ++ ++#define AW_I2C_RETRIES (5) ++#define AW_I2C_RETRY_DELAY (2) ++#define AW_I2C_READ_MSG_NUM (2) ++ ++#define AW87XXX_FW_NAME_MAX (64) ++#define AW_NAME_BUF_MAX (64) ++#define AW_LOAD_FW_RETRIES (3) ++ ++#define AW_DEV_REG_RD_ACCESS (1 << 0) ++#define AW_DEV_REG_WR_ACCESS (1 << 1) ++ ++#define AWRW_ADDR_BYTES (1) ++#define AWRW_DATA_BYTES (1) ++#define AWRW_HDR_LEN (24) ++ ++/*********************************************************** ++ * ++ * aw87xxx codec control compatible with kernel 4.19 ++ * ++ ***********************************************************/ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 1) ++#define AW_KERNEL_VER_OVER_4_19_1 ++#endif ++ ++#ifdef AW_KERNEL_VER_OVER_4_19_1 ++typedef struct snd_soc_component aw_snd_soc_codec_t; ++#else ++typedef struct snd_soc_codec aw_snd_soc_codec_t; ++#endif ++ ++struct aw_componet_codec_ops { ++ int (*add_codec_controls)(aw_snd_soc_codec_t *codec, ++ const struct snd_kcontrol_new *controls, unsigned int num_controls); ++ void (*unregister_codec)(struct device *dev); ++}; ++ ++ ++/******************************************** ++ * ++ * aw87xxx devices attributes ++ * ++ *******************************************/ ++enum { ++ AWRW_FLAG_WRITE = 0, ++ AWRW_FLAG_READ, ++}; ++ ++enum { ++ AWRW_I2C_ST_NONE = 0, ++ AWRW_I2C_ST_READ, ++ AWRW_I2C_ST_WRITE, ++}; ++ ++enum { ++ AWRW_HDR_WR_FLAG = 0, ++ AWRW_HDR_ADDR_BYTES, ++ AWRW_HDR_DATA_BYTES, ++ AWRW_HDR_REG_NUM, ++ AWRW_HDR_REG_ADDR, ++ AWRW_HDR_MAX, ++}; ++ ++struct aw_i2c_packet { ++ char status; ++ unsigned int reg_num; ++ unsigned int reg_addr; ++ char *reg_data; ++}; ++ ++ ++/******************************************** ++ * ++ * aw87xxx device struct ++ * ++ *******************************************/ ++struct aw87xxx { ++ char fw_name[AW87XXX_FW_NAME_MAX]; ++ int32_t dev_index; ++ char *current_profile; ++ char prof_off_name[AW_PROFILE_STR_MAX]; ++ uint32_t off_bin_status; ++ struct device *dev; ++ ++ struct mutex reg_lock; ++ struct aw_device aw_dev; ++ struct aw_i2c_packet i2c_packet; ++ ++ struct delayed_work fw_load_work; ++ struct acf_bin_info acf_info; ++ ++ aw_snd_soc_codec_t *codec; ++ ++ struct list_head list; ++ ++ struct aw_monitor monitor; ++}; ++ ++int aw87xxx_update_profile(struct aw87xxx *aw87xxx, char *profile); ++int aw87xxx_update_profile_esd(struct aw87xxx *aw87xxx, char *profile); ++ ++#endif +diff --git a/sound/soc/codecs/aw87xxx/aw87xxx_acf_bin.c b/sound/soc/codecs/aw87xxx/aw87xxx_acf_bin.c +new file mode 100644 +index 000000000..00c7aedb7 +--- /dev/null ++++ b/sound/soc/codecs/aw87xxx/aw87xxx_acf_bin.c +@@ -0,0 +1,1558 @@ ++/* ++ * aw87xxx_acf_bin.c ++ * ++ * Copyright (c) 2021 AWINIC Technology CO., LTD ++ * ++ * Author: Barry ++ * ++ * 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. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "aw87xxx.h" ++#include "aw87xxx_acf_bin.h" ++#include "aw87xxx_monitor.h" ++#include "aw87xxx_log.h" ++#include "aw87xxx_bin_parse.h" ++ ++/************************************************************************* ++ * ++ *Table corresponding to customized profile ids to profile names ++ * ++ *************************************************************************/ ++enum aw_customers_profile_id { ++ AW_CTOS_PROFILE_OFF = 0, ++ AW_CTOS_PROFILE_MUSIC, ++ AW_CTOS_PROFILE_VOICE, ++ AW_CTOS_PROFILE_VOIP, ++ AW_CTOS_PROFILE_RINGTONE, ++ AW_CTOS_PROFILE_RINGTONE_HS, ++ AW_CTOS_PROFILE_LOWPOWER, ++ AW_CTOS_PROFILE_BYPASS, ++ AW_CTOS_PROFILE_MMI, ++ AW_CTOS_PROFILE_FM, ++ AW_CTOS_PROFILE_NOTIFICATION, ++ AW_CTOS_PROFILE_RECEIVER, ++ AW_CTOS_PROFILE_MAX, ++}; ++ ++static char *g_ctos_profile_name[AW_PROFILE_MAX] = { ++ [AW_CTOS_PROFILE_OFF] = "Off", ++ [AW_CTOS_PROFILE_MUSIC] = "Music", ++ [AW_CTOS_PROFILE_VOICE] = "Voice", ++ [AW_CTOS_PROFILE_VOIP] = "Voip", ++ [AW_CTOS_PROFILE_RINGTONE] = "Ringtone", ++ [AW_CTOS_PROFILE_RINGTONE_HS] = "Ringtone_hs", ++ [AW_CTOS_PROFILE_LOWPOWER] = "Lowpower", ++ [AW_CTOS_PROFILE_BYPASS] = "Bypass", ++ [AW_CTOS_PROFILE_MMI] = "Mmi", ++ [AW_CTOS_PROFILE_FM] = "Fm", ++ [AW_CTOS_PROFILE_NOTIFICATION] = "Notification", ++ [AW_CTOS_PROFILE_RECEIVER] = "Receiver", ++}; ++ ++ ++char *aw87xxx_ctos_get_prof_name(int profile_id) ++{ ++ if (profile_id < 0 || profile_id >= AW_CTOS_PROFILE_MAX) ++ return NULL; ++ else ++ return g_ctos_profile_name[profile_id]; ++} ++ ++ ++static char *g_profile_name[] = {"Music", "Voice", "Voip", ++ "Ringtone", "Ringtone_hs", "Lowpower", "Bypass", "Mmi", ++ "Fm", "Notification", "Receiver", "Off"}; ++ ++static char *g_power_off_name[] = {"Off", "OFF", "off", "oFF", "power_down"}; ++ ++static char *aw_get_prof_name(int profile) ++{ ++ if (profile < 0 || profile >= AW_PROFILE_MAX) ++ return "NULL"; ++ else ++ return g_profile_name[profile]; ++} ++ ++/************************************************************************* ++ * ++ *acf check ++ * ++ *************************************************************************/ ++static int aw_crc8_check(const unsigned char *data, unsigned int data_size) ++ ++{ ++ unsigned char crc_value = 0x00; ++ unsigned char *pdata; ++ int i; ++ unsigned char pdatabuf = 0; ++ ++ pdata = (unsigned char *)data; ++ ++ while (data_size--) { ++ pdatabuf = *pdata++; ++ for (i = 0; i < 8; i++) { ++ if ((crc_value ^ (pdatabuf)) & 0x01) { ++ crc_value ^= 0x18; ++ crc_value >>= 1; ++ crc_value |= 0x80; ++ } else { ++ crc_value >>= 1; ++ } ++ pdatabuf >>= 1; ++ } ++ } ++ ++ return (int)crc_value; ++} ++ ++static int aw_check_file_id(struct device *dev, ++ char *fw_data, int32_t file_id) ++{ ++ int32_t *acf_file_id = NULL; ++ ++ acf_file_id = (int32_t *)fw_data; ++ if (*acf_file_id != file_id) { ++ AW_DEV_LOGE(dev, "file id [%x] check failed", *acf_file_id); ++ return -ENFILE; ++ } ++ ++ return 0; ++} ++ ++static int aw_check_header_size(struct device *dev, ++ char *fw_data, size_t fw_size) ++{ ++ if (fw_size < sizeof(struct aw_acf_hdr)) { ++ AW_DEV_LOGE(dev, "acf size check failed,size less-than aw_acf_hdr"); ++ return -ENOEXEC; ++ } ++ ++ return 0; ++} ++ ++/*************************************************************************** ++ * V0.0.0.1 version acf check ++ **************************************************************************/ ++static int aw_check_ddt_size_v_0_0_0_1(struct device *dev, char *fw_data) ++{ ++ struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)fw_data; ++ struct aw_acf_dde *acf_dde = NULL; ++ ++ acf_dde = (struct aw_acf_dde *)(fw_data + acf_hdr->ddt_offset); ++ ++ /* check ddt_size in acf_header is aqual to ddt_num multiply by dde_size */ ++ if (acf_hdr->ddt_size != acf_hdr->dde_num * sizeof(struct aw_acf_dde)) { ++ AW_DEV_LOGE(dev, "acf ddt size check failed"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int aw_check_data_size_v_0_0_0_1(struct device *dev, ++ char *fw_data, size_t fw_size) ++{ ++ int i = 0; ++ size_t data_size = 0; ++ struct aw_acf_hdr *acf_hdr = NULL; ++ struct aw_acf_dde *acf_dde = NULL; ++ ++ acf_hdr = (struct aw_acf_hdr *)fw_data; ++ acf_dde = (struct aw_acf_dde *)(fw_data + acf_hdr->ddt_offset); ++ ++ for (i = 0; i < acf_hdr->dde_num; ++i) { ++ if (acf_dde[i].data_size % 2) { ++ AW_DEV_LOGE(dev, "acf dde[%d].data_size[%d],dev_name[%s],data_type[%d], data_size check failed", ++ i, acf_dde[i].data_size, acf_dde[i].dev_name, ++ acf_dde[i].data_type); ++ return -EINVAL; ++ } ++ data_size += acf_dde[i].data_size; ++ } ++ ++ /* Verify that the file size is equal to the header size plus */ ++ /* the table size and data size */ ++ if (fw_size != data_size + sizeof(struct aw_acf_hdr) + acf_hdr->ddt_size) { ++ AW_DEV_LOGE(dev, "acf size check failed"); ++ AW_DEV_LOGE(dev, "fw_size=%ld,hdr_size and ddt size and data size =%ld", ++ (u_long)fw_size, (u_long)(data_size + sizeof(struct aw_acf_hdr) + ++ acf_hdr->ddt_size)); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int aw_check_data_crc_v_0_0_0_1(struct device *dev, char *fw_data) ++{ ++ int i = 0; ++ size_t crc_val = 0; ++ char *data = NULL; ++ struct aw_acf_hdr *acf_hdr = NULL; ++ struct aw_acf_dde *acf_dde = NULL; ++ ++ acf_hdr = (struct aw_acf_hdr *)fw_data; ++ acf_dde = (struct aw_acf_dde *)(fw_data + acf_hdr->ddt_offset); ++ ++ for (i = 0; i < acf_hdr->dde_num; ++i) { ++ data = fw_data + acf_dde[i].data_offset; ++ crc_val = aw_crc8_check(data, acf_dde[i].data_size); ++ if (crc_val != acf_dde[i].data_crc) { ++ AW_DEV_LOGE(dev, "acf dde_crc check failed"); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++static int aw_check_profile_id_v_0_0_0_1(struct device *dev, char *fw_data) ++{ ++ int i = 0; ++ struct aw_acf_hdr *acf_hdr = NULL; ++ struct aw_acf_dde *acf_dde = NULL; ++ ++ acf_hdr = (struct aw_acf_hdr *)fw_data; ++ acf_dde = (struct aw_acf_dde *)(fw_data + acf_hdr->ddt_offset); ++ ++ for (i = 0; i < acf_hdr->dde_num; ++i) { ++ if (acf_dde[i].data_type == AW_MONITOR) ++ continue; ++ if (acf_dde[i].dev_profile > AW_PROFILE_MAX) { ++ AW_DEV_LOGE(dev, "parse profile_id[%d] failed", acf_dde[i].dev_profile); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++static int aw_check_data_v_0_0_0_1(struct device *dev, ++ char *fw_data, size_t size) ++{ ++ int ret = -1; ++ ++ /* check file type id is awinic acf file */ ++ ret = aw_check_file_id(dev, fw_data, AW_ACF_FILE_ID); ++ if (ret < 0) ++ return ret; ++ ++ /* check ddt_size in header is equal to all ddt aize */ ++ ret = aw_check_ddt_size_v_0_0_0_1(dev, fw_data); ++ if (ret < 0) ++ return ret; ++ ++ /* Verify that the file size is equal to the header size plus */ ++ /* the table size and data size */ ++ ret = aw_check_data_size_v_0_0_0_1(dev, fw_data, size); ++ if (ret < 0) ++ return ret; ++ ++ /* check crc in is equal to dde data crc */ ++ ret = aw_check_data_crc_v_0_0_0_1(dev, fw_data); ++ if (ret < 0) ++ return ret; ++ ++ /* check profile id is in profile_id_max */ ++ ret = aw_check_profile_id_v_0_0_0_1(dev, fw_data); ++ if (ret < 0) ++ return ret; ++ ++ AW_DEV_LOGI(dev, "acf fimware check succeed"); ++ ++ return 0; ++} ++ ++/*************************************************************************** ++ * V1.0.0.0 version acf chack ++ **************************************************************************/ ++static int aw_check_ddt_size_v_1_0_0_0(struct device *dev, char *fw_data) ++{ ++ struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)fw_data; ++ struct aw_acf_dde_v_1_0_0_0 *acf_dde = NULL; ++ ++ acf_dde = (struct aw_acf_dde_v_1_0_0_0 *)(fw_data + acf_hdr->ddt_offset); ++ ++ /* check ddt_size in acf_header is aqual to ddt_num multiply by dde_size */ ++ if (acf_hdr->ddt_size != acf_hdr->dde_num * sizeof(struct aw_acf_dde_v_1_0_0_0)) { ++ AW_DEV_LOGE(dev, "acf ddt size check failed"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int aw_check_data_size_v_1_0_0_0(struct device *dev, ++ char *fw_data, size_t fw_size) ++{ ++ int i = 0; ++ size_t data_size = 0; ++ struct aw_acf_hdr *acf_hdr = NULL; ++ struct aw_acf_dde_v_1_0_0_0 *acf_dde = NULL; ++ ++ acf_hdr = (struct aw_acf_hdr *)fw_data; ++ acf_dde = (struct aw_acf_dde_v_1_0_0_0 *)(fw_data + acf_hdr->ddt_offset); ++ ++ for (i = 0; i < acf_hdr->dde_num; ++i) { ++ if (acf_dde[i].data_size % 2) { ++ AW_DEV_LOGE(dev, "acf dde[%d].data_size[%d],dev_name[%s],data_type[%d], data_size check failed", ++ i, acf_dde[i].data_size, acf_dde[i].dev_name, ++ acf_dde[i].data_type); ++ return -EINVAL; ++ } ++ data_size += acf_dde[i].data_size; ++ } ++ ++ /* Verify that the file size is equal to the header size plus */ ++ /* the table size and data size */ ++ if (fw_size != data_size + sizeof(struct aw_acf_hdr) + acf_hdr->ddt_size) { ++ AW_DEV_LOGE(dev, "acf size check failed"); ++ AW_DEV_LOGE(dev, "fw_size=%ld,hdr_size and ddt size and data size =%ld", ++ (u_long)fw_size, (u_long)(data_size + sizeof(struct aw_acf_hdr) + ++ acf_hdr->ddt_size)); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int aw_check_data_crc_v_1_0_0_0(struct device *dev, char *fw_data) ++{ ++ int i = 0; ++ size_t crc_val = 0; ++ char *data = NULL; ++ struct aw_acf_hdr *acf_hdr = NULL; ++ struct aw_acf_dde_v_1_0_0_0 *acf_dde = NULL; ++ ++ acf_hdr = (struct aw_acf_hdr *)fw_data; ++ acf_dde = (struct aw_acf_dde_v_1_0_0_0 *)(fw_data + acf_hdr->ddt_offset); ++ ++ for (i = 0; i < acf_hdr->dde_num; ++i) { ++ data = fw_data + acf_dde[i].data_offset; ++ crc_val = aw_crc8_check(data, acf_dde[i].data_size); ++ if (crc_val != acf_dde[i].data_crc) { ++ AW_DEV_LOGE(dev, "acf dde_crc check failed"); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++static int aw_check_data_v_1_0_0_0(struct device *dev, ++ char *fw_data, size_t size) ++{ ++ int ret = -1; ++ ++ /* check file type id is awinic acf file */ ++ ret = aw_check_file_id(dev, fw_data, AW_ACF_FILE_ID); ++ if (ret < 0) ++ return ret; ++ ++ /* check ddt_size in header is equal to all ddt aize */ ++ ret = aw_check_ddt_size_v_1_0_0_0(dev, fw_data); ++ if (ret < 0) ++ return ret; ++ ++ /* Verify that the file size is equal to the header size plus */ ++ /* the table size and data size */ ++ ret = aw_check_data_size_v_1_0_0_0(dev, fw_data, size); ++ if (ret < 0) ++ return ret; ++ ++ /* check crc in is equal to dde data crc */ ++ ret = aw_check_data_crc_v_1_0_0_0(dev, fw_data); ++ if (ret < 0) ++ return ret; ++ ++ AW_DEV_LOGI(dev, "acf fimware check succeed"); ++ ++ return 0; ++} ++ ++/*************************************************************************** ++ * acf chack API ++ **************************************************************************/ ++static int aw_check_acf_firmware(struct device *dev, ++ char *fw_data, size_t size) ++{ ++ int ret = -1; ++ struct aw_acf_hdr *acf_hdr = NULL; ++ ++ if (fw_data == NULL) { ++ AW_DEV_LOGE(dev, "fw_data is NULL,fw_data check failed"); ++ return -ENODATA; ++ } ++ ++ /* check file size is less-than header size */ ++ ret = aw_check_header_size(dev, fw_data, size); ++ if (ret < 0) ++ return ret; ++ ++ acf_hdr = (struct aw_acf_hdr *)fw_data; ++ AW_DEV_LOGI(dev, "project name: [%s]", acf_hdr->project); ++ AW_DEV_LOGI(dev, "custom name: [%s]", acf_hdr->custom); ++ AW_DEV_LOGI(dev, "version name: [%s]", acf_hdr->version); ++ AW_DEV_LOGI(dev, "author_id: [%d]", acf_hdr->author_id); ++ ++ switch (acf_hdr->hdr_version) { ++ case AW_ACF_HDR_VER_0_0_0_1: ++ return aw_check_data_v_0_0_0_1(dev, fw_data, size); ++ case AW_ACF_HDR_VER_1_0_0_0: ++ return aw_check_data_v_1_0_0_0(dev, fw_data, size); ++ default: ++ AW_DEV_LOGE(dev, "unsupported hdr_version [0x%x]", ++ acf_hdr->hdr_version); ++ return -EINVAL; ++ } ++ ++ return ret; ++} ++ ++ ++ ++/************************************************************************* ++ * ++ *acf parse ++ * ++ *************************************************************************/ ++static int aw_parse_raw_reg(struct device *dev, uint8_t *data, ++ uint32_t data_len, struct aw_prof_desc *prof_desc) ++{ ++ AW_DEV_LOGD(dev, "data_size:%d enter", data_len); ++ ++ prof_desc->data_container.data = data; ++ prof_desc->data_container.len = data_len; ++ ++ prof_desc->prof_st = AW_PROFILE_OK; ++ ++ return 0; ++} ++ ++static int aw_parse_reg_with_hdr(struct device *dev, uint8_t *data, ++ uint32_t data_len, struct aw_prof_desc *prof_desc) ++{ ++ struct aw_bin *aw_bin = NULL; ++ int ret = -1; ++ ++ AW_DEV_LOGD(dev, "data_size:%d enter", data_len); ++ ++ aw_bin = kzalloc(data_len + sizeof(struct aw_bin), GFP_KERNEL); ++ if (aw_bin == NULL) { ++ AW_DEV_LOGE(dev, "devm_kzalloc aw_bin failed"); ++ return -ENOMEM; ++ } ++ ++ aw_bin->info.len = data_len; ++ memcpy(aw_bin->info.data, data, data_len); ++ ++ ret = aw87xxx_parsing_bin_file(aw_bin); ++ if (ret < 0) { ++ AW_DEV_LOGE(dev, "parse bin failed"); ++ goto parse_bin_failed; ++ } ++ ++ if ((aw_bin->all_bin_parse_num != 1) || ++ (aw_bin->header_info[0].bin_data_type != DATA_TYPE_REGISTER)) { ++ AW_DEV_LOGE(dev, "bin num or type error"); ++ goto parse_bin_failed; ++ } ++ ++ prof_desc->data_container.data = ++ data + aw_bin->header_info[0].valid_data_addr; ++ prof_desc->data_container.len = aw_bin->header_info[0].valid_data_len; ++ prof_desc->prof_st = AW_PROFILE_OK; ++ ++ kfree(aw_bin); ++ aw_bin = NULL; ++ ++ return 0; ++ ++parse_bin_failed: ++ kfree(aw_bin); ++ aw_bin = NULL; ++ return ret; ++} ++ ++static int aw_parse_monitor_config(struct device *dev, ++ char *monitor_data, uint32_t data_len) ++{ ++ int ret = -1; ++ ++ if (monitor_data == NULL || data_len == 0) { ++ AW_DEV_LOGE(dev, "no data to parse"); ++ return -EBFONT; ++ } ++ ++ ret = aw87xxx_monitor_bin_parse(dev, monitor_data, data_len); ++ if (ret < 0) { ++ AW_DEV_LOGE(dev, "monitor_config parse failed"); ++ return ret; ++ } ++ ++ AW_DEV_LOGI(dev, "monitor_bin parse succeed"); ++ ++ return 0; ++} ++ ++static int aw_check_prof_str_is_off(char *profile_name) ++{ ++ int i = 0; ++ ++ for (i = 0; i < AW_POWER_OFF_NAME_SUPPORT_COUNT; i++) { ++ if (strnstr(profile_name, g_power_off_name[i], ++ strlen(profile_name) + 1)) ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++/*************************************************************************** ++ * V0.0.0.1 version acf paese ++ **************************************************************************/ ++static int aw_check_product_name_v_0_0_0_1(struct device *dev, ++ struct acf_bin_info *acf_info, ++ struct aw_acf_dde *prof_hdr) ++{ ++ int i = 0; ++ ++ for (i = 0; i < acf_info->product_cnt; i++) { ++ if (0 == strcmp(acf_info->product_tab[i], prof_hdr->dev_name)) { ++ AW_DEV_LOGD(dev, "bin_dev_name:%s", ++ prof_hdr->dev_name); ++ return 0; ++ } ++ } ++ ++ return -ENXIO; ++} ++ ++static int aw_check_data_type_is_monitor_v_0_0_0_1(struct device *dev, ++ struct aw_acf_dde *prof_hdr) ++{ ++ if (prof_hdr->data_type == AW_MONITOR) { ++ AW_DEV_LOGD(dev, "bin data is monitor"); ++ return 0; ++ } ++ ++ return -ENXIO; ++} ++ ++static int aw_parse_data_by_sec_type_v_0_0_0_1(struct device *dev, ++ struct acf_bin_info *acf_info, ++ struct aw_acf_dde *prof_hdr, ++ struct aw_prof_desc *profile_prof_desc) ++{ ++ int ret = -1; ++ char *cfg_data = acf_info->fw_data + prof_hdr->data_offset; ++ ++ switch (prof_hdr->data_type) { ++ case AW_BIN_TYPE_REG: ++ snprintf(profile_prof_desc->dev_name, sizeof(prof_hdr->dev_name), ++ "%s", prof_hdr->dev_name); ++ profile_prof_desc->prof_name = aw_get_prof_name(prof_hdr->dev_profile); ++ AW_DEV_LOGD(dev, "parse reg type data enter,profile=%s", ++ aw_get_prof_name(prof_hdr->dev_profile)); ++ ret = aw_parse_raw_reg(dev, cfg_data, prof_hdr->data_size, ++ profile_prof_desc); ++ break; ++ case AW_BIN_TYPE_HDR_REG: ++ snprintf(profile_prof_desc->dev_name, sizeof(prof_hdr->dev_name), ++ "%s", prof_hdr->dev_name); ++ profile_prof_desc->prof_name = aw_get_prof_name(prof_hdr->dev_profile); ++ AW_DEV_LOGD(dev, "parse hdr_reg type data enter,profile=%s", ++ aw_get_prof_name(prof_hdr->dev_profile)); ++ ret = aw_parse_reg_with_hdr(dev, cfg_data, ++ prof_hdr->data_size, ++ profile_prof_desc); ++ break; ++ } ++ ++ return ret; ++} ++ ++static int aw_parse_dev_type_v_0_0_0_1(struct device *dev, ++ struct acf_bin_info *acf_info, struct aw_all_prof_info *all_prof_info) ++{ ++ int i = 0; ++ int ret = -1; ++ int sec_num = 0; ++ char *cfg_data = NULL; ++ struct aw_prof_desc *prof_desc = NULL; ++ struct aw_acf_dde *acf_dde = ++ (struct aw_acf_dde *)(acf_info->fw_data + acf_info->acf_hdr.ddt_offset); ++ ++ AW_DEV_LOGD(dev, "enter"); ++ ++ for (i = 0; i < acf_info->acf_hdr.dde_num; i++) { ++ if ((acf_info->aw_dev->i2c_bus == acf_dde[i].dev_bus) && ++ (acf_info->aw_dev->i2c_addr == acf_dde[i].dev_addr) && ++ (acf_dde[i].type == AW_DDE_DEV_TYPE_ID)) { ++ ++ ret = aw_check_product_name_v_0_0_0_1(dev, acf_info, &acf_dde[i]); ++ if (ret < 0) ++ continue; ++ ++ ret = aw_check_data_type_is_monitor_v_0_0_0_1(dev, &acf_dde[i]); ++ if (ret == 0) { ++ cfg_data = acf_info->fw_data + acf_dde[i].data_offset; ++ ret = aw_parse_monitor_config(dev, cfg_data, acf_dde[i].data_size); ++ if (ret < 0) ++ return ret; ++ continue; ++ } ++ ++ prof_desc = &all_prof_info->prof_desc[acf_dde[i].dev_profile]; ++ ret = aw_parse_data_by_sec_type_v_0_0_0_1(dev, acf_info, &acf_dde[i], ++ prof_desc); ++ if (ret < 0) { ++ AW_DEV_LOGE(dev, "parse dev type data failed"); ++ return ret; ++ } ++ sec_num++; ++ } ++ } ++ ++ if (sec_num == 0) { ++ AW_DEV_LOGD(dev, "get dev type num is %d, please use default", ++ sec_num); ++ return AW_DEV_TYPE_NONE; ++ } ++ ++ return AW_DEV_TYPE_OK; ++} ++ ++static int aw_parse_default_type_v_0_0_0_1(struct device *dev, ++ struct acf_bin_info *acf_info, struct aw_all_prof_info *all_prof_info) ++{ ++ int i = 0; ++ int ret = -1; ++ int sec_num = 0; ++ char *cfg_data = NULL; ++ struct aw_prof_desc *prof_desc = NULL; ++ struct aw_acf_dde *acf_dde = ++ (struct aw_acf_dde *)(acf_info->fw_data + acf_info->acf_hdr.ddt_offset); ++ ++ AW_DEV_LOGD(dev, "enter"); ++ ++ for (i = 0; i < acf_info->acf_hdr.dde_num; i++) { ++ if ((acf_info->dev_index == acf_dde[i].dev_index) && ++ (acf_dde[i].type == AW_DDE_DEV_DEFAULT_TYPE_ID)) { ++ ++ ret = aw_check_product_name_v_0_0_0_1(dev, acf_info, &acf_dde[i]); ++ if (ret < 0) ++ continue; ++ ++ ret = aw_check_data_type_is_monitor_v_0_0_0_1(dev, &acf_dde[i]); ++ if (ret == 0) { ++ cfg_data = acf_info->fw_data + acf_dde[i].data_offset; ++ ret = aw_parse_monitor_config(dev, cfg_data, acf_dde[i].data_size); ++ if (ret < 0) ++ return ret; ++ continue; ++ } ++ ++ prof_desc = &all_prof_info->prof_desc[acf_dde[i].dev_profile]; ++ ret = aw_parse_data_by_sec_type_v_0_0_0_1(dev, acf_info, &acf_dde[i], ++ prof_desc); ++ if (ret < 0) { ++ AW_DEV_LOGE(dev, "parse default type data failed"); ++ return ret; ++ } ++ sec_num++; ++ } ++ } ++ ++ if (sec_num == 0) { ++ AW_DEV_LOGE(dev, "get dev default type failed, get num[%d]", ++ sec_num); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int aw_get_prof_count_v_0_0_0_1(struct device *dev, ++ struct acf_bin_info *acf_info, ++ struct aw_all_prof_info *all_prof_info) ++{ ++ int i = 0; ++ int prof_count = 0; ++ struct aw_prof_desc *prof_desc = all_prof_info->prof_desc; ++ ++ for (i = 0; i < AW_PROFILE_MAX; i++) { ++ if (prof_desc[i].prof_st == AW_PROFILE_OK) { ++ prof_count++; ++ } else if (i == AW_PROFILE_OFF) { ++ prof_count++; ++ AW_DEV_LOGI(dev, "not found profile [Off], set default"); ++ } ++ } ++ ++ AW_DEV_LOGI(dev, "get profile count=[%d]", prof_count); ++ return prof_count; ++} ++ ++static int aw_set_prof_off_info_v_0_0_0_1(struct device *dev, ++ struct acf_bin_info *acf_info, ++ struct aw_all_prof_info *all_prof_info, ++ int index) ++{ ++ struct aw_prof_desc *prof_desc = all_prof_info->prof_desc; ++ struct aw_prof_info *prof_info = &acf_info->prof_info; ++ ++ if (index >= prof_info->count) { ++ AW_DEV_LOGE(dev, "index[%d] is out of table,profile count[%d]", ++ index, prof_info->count); ++ return -EINVAL; ++ } ++ ++ if (prof_desc[AW_PROFILE_OFF].prof_st == AW_PROFILE_OK) { ++ prof_info->prof_desc[index] = prof_desc[AW_PROFILE_OFF]; ++ AW_DEV_LOGI(dev, "product=[%s]----profile=[%s]", ++ prof_info->prof_desc[index].dev_name, ++ aw_get_prof_name(AW_PROFILE_OFF)); ++ } else { ++ memset(&prof_info->prof_desc[index].data_container, 0, ++ sizeof(struct aw_data_container)); ++ prof_info->prof_desc[index].prof_st = AW_PROFILE_WAIT; ++ prof_info->prof_desc[index].prof_name = aw_get_prof_name(AW_PROFILE_OFF); ++ AW_DEV_LOGI(dev, "set default power_off with no data to profile"); ++ } ++ ++ return 0; ++} ++ ++ ++static int aw_get_vaild_prof_v_0_0_0_1(struct device *dev, ++ struct acf_bin_info *acf_info, ++ struct aw_all_prof_info *all_prof_info) ++{ ++ int i = 0; ++ int ret = 0; ++ int index = 0; ++ struct aw_prof_desc *prof_desc = all_prof_info->prof_desc; ++ struct aw_prof_info *prof_info = &acf_info->prof_info; ++ ++ prof_info->count = 0; ++ ret = aw_get_prof_count_v_0_0_0_1(dev, acf_info, all_prof_info); ++ if (ret < 0) ++ return ret; ++ prof_info->count = ret; ++ prof_info->prof_desc = devm_kzalloc(dev, ++ prof_info->count * sizeof(struct aw_prof_desc), ++ GFP_KERNEL); ++ if (prof_info->prof_desc == NULL) { ++ AW_DEV_LOGE(dev, "prof_desc kzalloc failed"); ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < AW_PROFILE_MAX; i++) { ++ if (i != AW_PROFILE_OFF && prof_desc[i].prof_st == AW_PROFILE_OK) { ++ if (index >= prof_info->count) { ++ AW_DEV_LOGE(dev, "get profile index[%d] overflow count[%d]", ++ index, prof_info->count); ++ return -ENOMEM; ++ } ++ prof_info->prof_desc[index] = prof_desc[i]; ++ AW_DEV_LOGI(dev, "product=[%s]----profile=[%s]", ++ prof_info->prof_desc[index].dev_name, ++ aw_get_prof_name(i)); ++ index++; ++ } ++ } ++ ++ ret = aw_set_prof_off_info_v_0_0_0_1(dev, acf_info, all_prof_info, index); ++ if (ret < 0) ++ return ret; ++ ++ AW_DEV_LOGD(dev, "get vaild profile succeed"); ++ return 0; ++} ++ ++static int aw_set_prof_name_list_v_0_0_0_1(struct device *dev, ++ struct acf_bin_info *acf_info) ++{ ++ int i = 0; ++ int count = acf_info->prof_info.count; ++ struct aw_prof_info *prof_info = &acf_info->prof_info; ++ ++ prof_info->prof_name_list = (char (*)[AW_PROFILE_STR_MAX])devm_kzalloc(dev, ++ count * (AW_PROFILE_STR_MAX), GFP_KERNEL); ++ if (prof_info->prof_name_list == NULL) { ++ AW_DEV_LOGE(dev, "prof_name_list devm_kzalloc failed"); ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < count; ++i) { ++ snprintf(prof_info->prof_name_list[i], AW_PROFILE_STR_MAX, "%s", ++ prof_info->prof_desc[i].prof_name); ++ AW_DEV_LOGI(dev, "index=[%d], profile_name=[%s]", ++ i, prof_info->prof_name_list[i]); ++ } ++ ++ return 0; ++} ++ ++static int aw_parse_acf_v_0_0_0_1(struct device *dev, ++ struct acf_bin_info *acf_info) ++ ++{ ++ int ret = 0; ++ struct aw_all_prof_info all_prof_info; ++ ++ AW_DEV_LOGD(dev, "enter"); ++ acf_info->prof_info.status = AW_ACF_WAIT; ++ ++ memset(&all_prof_info, 0, sizeof(struct aw_all_prof_info)); ++ ++ ret = aw_parse_dev_type_v_0_0_0_1(dev, acf_info, &all_prof_info); ++ if (ret < 0) { ++ return ret; ++ } else if (ret == AW_DEV_TYPE_NONE) { ++ AW_DEV_LOGD(dev, "get dev type num is 0, parse default dev type"); ++ ret = aw_parse_default_type_v_0_0_0_1(dev, acf_info, &all_prof_info); ++ if (ret < 0) ++ return ret; ++ } ++ ++ ret = aw_get_vaild_prof_v_0_0_0_1(dev, acf_info, &all_prof_info); ++ if (ret < 0) { ++ aw87xxx_acf_profile_free(dev, acf_info); ++ AW_DEV_LOGE(dev, "hdr_cersion[0x%x] parse failed", ++ acf_info->acf_hdr.hdr_version); ++ return ret; ++ } ++ ++ ret = aw_set_prof_name_list_v_0_0_0_1(dev, acf_info); ++ if (ret < 0) { ++ aw87xxx_acf_profile_free(dev, acf_info); ++ AW_DEV_LOGE(dev, "creat prof_id_and_name_list failed"); ++ return ret; ++ } ++ ++ acf_info->prof_info.status = AW_ACF_UPDATE; ++ AW_DEV_LOGI(dev, "acf parse success"); ++ return 0; ++} ++ ++/*************************************************************************** ++ * V1.0.0.0 version acf paese ++ **************************************************************************/ ++static int aw_check_product_name_v_1_0_0_0(struct device *dev, ++ struct acf_bin_info *acf_info, ++ struct aw_acf_dde_v_1_0_0_0 *prof_hdr) ++{ ++ int i = 0; ++ ++ for (i = 0; i < acf_info->product_cnt; i++) { ++ if (0 == strcmp(acf_info->product_tab[i], prof_hdr->dev_name)) { ++ AW_DEV_LOGI(dev, "bin_dev_name:%s", prof_hdr->dev_name); ++ return 0; ++ } ++ } ++ ++ return -ENXIO; ++} ++ ++static int aw_get_dde_type_info_v_1_0_0_0(struct device *dev, ++ struct acf_bin_info *acf_info) ++{ ++ int i; ++ int dev_num = 0; ++ int default_num = 0; ++ struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)acf_info->fw_data; ++ struct aw_prof_info *prof_info = &acf_info->prof_info; ++ struct aw_acf_dde_v_1_0_0_0 *acf_dde = ++ (struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_hdr->ddt_offset); ++ ++ prof_info->prof_type = AW_DEV_NONE_TYPE_ID; ++ for (i = 0; i < acf_hdr->dde_num; i++) { ++ if (acf_dde[i].type == AW_DDE_DEV_TYPE_ID) ++ dev_num++; ++ if (acf_dde[i].type == AW_DDE_DEV_DEFAULT_TYPE_ID) ++ default_num++; ++ } ++ ++ if (!(dev_num || default_num)) { ++ AW_DEV_LOGE(dev, "can't find scene"); ++ return -EINVAL; ++ } ++ ++ if (dev_num != 0) ++ prof_info->prof_type = AW_DDE_DEV_TYPE_ID; ++ else if (default_num != 0) ++ prof_info->prof_type = AW_DDE_DEV_DEFAULT_TYPE_ID; ++ ++ return 0; ++} ++ ++ ++static int aw_parse_get_dev_type_prof_count_v_1_0_0_0(struct device *dev, ++ struct acf_bin_info *acf_info) ++{ ++ struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)acf_info->fw_data; ++ struct aw_acf_dde_v_1_0_0_0 *acf_dde = ++ (struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_hdr->ddt_offset); ++ int i = 0; ++ int ret = 0; ++ int found_off_prof_flag = 0; ++ int count = acf_info->prof_info.count; ++ ++ for (i = 0; i < acf_hdr->dde_num; ++i) { ++ if (((acf_dde[i].data_type == AW_BIN_TYPE_REG) || ++ (acf_dde[i].data_type == AW_BIN_TYPE_HDR_REG)) && ++ ((acf_info->aw_dev->i2c_bus == acf_dde[i].dev_bus) && ++ (acf_info->aw_dev->i2c_addr == acf_dde[i].dev_addr)) && ++ (acf_info->aw_dev->chipid == acf_dde[i].chip_id)) { ++ ++ ret = aw_check_product_name_v_1_0_0_0(dev, acf_info, &acf_dde[i]); ++ if (ret < 0) ++ continue; ++ ++ ret = aw_check_prof_str_is_off(acf_dde[i].dev_profile_str); ++ if (ret == 0) { ++ found_off_prof_flag = AW_PROFILE_OK; ++ } ++ count++; ++ } ++ } ++ ++ if (count == 0) { ++ AW_DEV_LOGE(dev, "can't find profile"); ++ return -EINVAL; ++ } ++ ++ if (!found_off_prof_flag) { ++ count++; ++ AW_DEV_LOGD(dev, "set no config power off profile in count"); ++ } ++ ++ acf_info->prof_info.count = count; ++ AW_DEV_LOGI(dev, "profile dev_type profile count is %d", acf_info->prof_info.count); ++ return 0; ++} ++ ++static int aw_parse_get_default_type_prof_count_v_1_0_0_0(struct device *dev, ++ struct acf_bin_info *acf_info) ++{ ++ struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)acf_info->fw_data; ++ struct aw_acf_dde_v_1_0_0_0 *acf_dde = ++ (struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_hdr->ddt_offset); ++ int i = 0; ++ int ret = 0; ++ int found_off_prof_flag = 0; ++ int count = acf_info->prof_info.count; ++ ++ for (i = 0; i < acf_hdr->dde_num; ++i) { ++ if (((acf_dde[i].data_type == AW_BIN_TYPE_REG) || ++ (acf_dde[i].data_type == AW_BIN_TYPE_HDR_REG)) && ++ (acf_info->dev_index == acf_dde[i].dev_index) && ++ (acf_info->aw_dev->chipid == acf_dde[i].chip_id)) { ++ ++ ret = aw_check_product_name_v_1_0_0_0(dev, acf_info, &acf_dde[i]); ++ if (ret < 0) ++ continue; ++ ++ ret = aw_check_prof_str_is_off(acf_dde[i].dev_profile_str); ++ if (ret == 0) { ++ found_off_prof_flag = AW_PROFILE_OK; ++ } ++ count++; ++ } ++ } ++ ++ if (count == 0) { ++ AW_DEV_LOGE(dev, "can't find profile"); ++ return -EINVAL; ++ } ++ ++ if (!found_off_prof_flag) { ++ count++; ++ AW_DEV_LOGD(dev, "set no config power off profile in count"); ++ } ++ ++ acf_info->prof_info.count = count; ++ AW_DEV_LOGI(dev, "profile default_type profile count is %d", acf_info->prof_info.count); ++ return 0; ++} ++ ++static int aw_parse_get_profile_count_v_1_0_0_0(struct device *dev, ++ struct acf_bin_info *acf_info) ++{ ++ int ret = 0; ++ ++ ret = aw_get_dde_type_info_v_1_0_0_0(dev, acf_info); ++ if (ret < 0) ++ return ret; ++ ++ if (acf_info->prof_info.prof_type == AW_DDE_DEV_TYPE_ID) { ++ ret = aw_parse_get_dev_type_prof_count_v_1_0_0_0(dev, acf_info); ++ if (ret < 0) { ++ AW_DEV_LOGE(dev, "parse dev_type profile count failed"); ++ return ret; ++ } ++ } else if (acf_info->prof_info.prof_type == AW_DDE_DEV_DEFAULT_TYPE_ID) { ++ ret = aw_parse_get_default_type_prof_count_v_1_0_0_0(dev, acf_info); ++ if (ret < 0) { ++ AW_DEV_LOGE(dev, "parse default_type profile count failed"); ++ return ret; ++ } ++ } else { ++ AW_DEV_LOGE(dev, "unsupport prof_type[0x%x]", ++ acf_info->prof_info.prof_type); ++ return -EINVAL; ++ } ++ ++ AW_DEV_LOGI(dev, "profile count is %d", acf_info->prof_info.count); ++ return 0; ++} ++ ++static int aw_parse_dev_type_prof_name_v_1_0_0_0(struct device *dev, ++ struct acf_bin_info *acf_info) ++{ ++ struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)acf_info->fw_data; ++ struct aw_acf_dde_v_1_0_0_0 *acf_dde = ++ (struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_hdr->ddt_offset); ++ struct aw_prof_info *prof_info = &acf_info->prof_info; ++ int i, ret, list_index = 0; ++ ++ for (i = 0; i < acf_hdr->dde_num; ++i) { ++ if (((acf_dde[i].data_type == AW_BIN_TYPE_REG) || ++ (acf_dde[i].data_type == AW_BIN_TYPE_HDR_REG)) && ++ (acf_info->aw_dev->i2c_bus == acf_dde[i].dev_bus) && ++ (acf_info->aw_dev->i2c_addr == acf_dde[i].dev_addr) && ++ (acf_info->aw_dev->chipid == acf_dde[i].chip_id)) { ++ if (list_index > prof_info->count) { ++ AW_DEV_LOGE(dev, "%s:Alrealdy set list_index [%d], redundant profile [%s]exist\n", ++ __func__, list_index, ++ acf_dde[i].dev_profile_str); ++ return -EINVAL; ++ } ++ ++ ret = aw_check_product_name_v_1_0_0_0(dev, acf_info, &acf_dde[i]); ++ if (ret < 0) ++ continue; ++ ++ snprintf(prof_info->prof_name_list[list_index], AW_PROFILE_STR_MAX, "%s", ++ acf_dde[i].dev_profile_str); ++ AW_DEV_LOGI(dev, "profile_name=[%s]", ++ prof_info->prof_name_list[list_index]); ++ list_index++; ++ } ++ } ++ ++ return 0; ++} ++ ++static int aw_parse_default_type_prof_name_v_1_0_0_0(struct device *dev, ++ struct acf_bin_info *acf_info) ++{ ++ struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)acf_info->fw_data; ++ struct aw_acf_dde_v_1_0_0_0 *acf_dde = ++ (struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_hdr->ddt_offset); ++ struct aw_prof_info *prof_info = &acf_info->prof_info; ++ int i, ret, list_index = 0; ++ ++ for (i = 0; i < acf_hdr->dde_num; ++i) { ++ if (((acf_dde[i].data_type == AW_BIN_TYPE_REG) || ++ (acf_dde[i].data_type == AW_BIN_TYPE_HDR_REG)) && ++ (acf_info->dev_index == acf_dde[i].dev_index) && ++ (acf_info->aw_dev->chipid == acf_dde[i].chip_id)) { ++ if (list_index > prof_info->count) { ++ AW_DEV_LOGE(dev, "%s:Alrealdy set list_index [%d], redundant profile [%s]exist\n", ++ __func__, list_index, ++ acf_dde[i].dev_profile_str); ++ return -EINVAL; ++ } ++ ++ ret = aw_check_product_name_v_1_0_0_0(dev, acf_info, &acf_dde[i]); ++ if (ret < 0) ++ continue; ++ ++ snprintf(prof_info->prof_name_list[list_index], AW_PROFILE_STR_MAX, "%s", ++ acf_dde[i].dev_profile_str); ++ AW_DEV_LOGI(dev, "profile_name=[%s]", ++ prof_info->prof_name_list[list_index]); ++ list_index++; ++ } ++ } ++ ++ return 0; ++} ++ ++static int aw_parse_prof_name_v_1_0_0_0(struct device *dev, ++ struct acf_bin_info *acf_info) ++{ ++ int ret = 0; ++ int count = acf_info->prof_info.count; ++ struct aw_prof_info *prof_info = &acf_info->prof_info; ++ ++ prof_info->prof_name_list = (char (*)[AW_PROFILE_STR_MAX])devm_kzalloc(dev, ++ count * (AW_PROFILE_STR_MAX), GFP_KERNEL); ++ if (prof_info->prof_name_list == NULL) { ++ AW_DEV_LOGE(dev, "prof_name_list devm_kzalloc failed"); ++ return -ENOMEM; ++ } ++ ++ if (acf_info->prof_info.prof_type == AW_DDE_DEV_TYPE_ID) { ++ ret = aw_parse_dev_type_prof_name_v_1_0_0_0(dev, acf_info); ++ if (ret < 0) { ++ AW_DEV_LOGE(dev, "parse dev_type profile count failed"); ++ return ret; ++ } ++ } else if (acf_info->prof_info.prof_type == AW_DDE_DEV_DEFAULT_TYPE_ID) { ++ ret = aw_parse_default_type_prof_name_v_1_0_0_0(dev, acf_info); ++ if (ret < 0) { ++ AW_DEV_LOGE(dev, "parse default_type profile count failed"); ++ return ret; ++ } ++ } else { ++ AW_DEV_LOGE(dev, "unsupport prof_type[0x%x]", ++ acf_info->prof_info.prof_type); ++ return -EINVAL; ++ } ++ ++ AW_DEV_LOGI(dev, "profile name parse succeed"); ++ return 0; ++} ++ ++ ++static int aw_search_prof_index_from_list_v_1_0_0_0(struct device *dev, ++ struct acf_bin_info *acf_info, ++ struct aw_prof_desc **prof_desc, ++ struct aw_acf_dde_v_1_0_0_0 *prof_hdr) ++{ ++ int i = 0; ++ int count = acf_info->prof_info.count; ++ char (*prof_name_list)[AW_PROFILE_STR_MAX] = acf_info->prof_info.prof_name_list; ++ ++ for (i = 0; i < count; i++) { ++ if (!strncmp(prof_name_list[i], prof_hdr->dev_profile_str, AW_PROFILE_STR_MAX)) { ++ *prof_desc = &(acf_info->prof_info.prof_desc[i]); ++ return 0; ++ } ++ } ++ ++ if (i == count) ++ AW_DEV_LOGE(dev, "not find prof_id and prof_name in list"); ++ ++ return -EINVAL; ++} ++ ++static int aw_parse_data_by_sec_type_v_1_0_0_0(struct device *dev, ++ struct acf_bin_info *acf_info, ++ struct aw_acf_dde_v_1_0_0_0 *prof_hdr) ++{ ++ int ret = -1; ++ char *cfg_data = acf_info->fw_data + prof_hdr->data_offset; ++ struct aw_prof_desc *prof_desc = NULL; ++ ++ ret = aw_search_prof_index_from_list_v_1_0_0_0(dev, acf_info, &prof_desc, prof_hdr); ++ if (ret < 0) ++ return ret; ++ ++ switch (prof_hdr->data_type) { ++ case AW_BIN_TYPE_REG: ++ snprintf(prof_desc->dev_name, sizeof(prof_hdr->dev_name), ++ "%s", prof_hdr->dev_name); ++ AW_DEV_LOGI(dev, "parse reg type data enter,product=[%s],prof_id=[%d],prof_name=[%s]", ++ prof_hdr->dev_name, prof_hdr->dev_profile, ++ prof_hdr->dev_profile_str); ++ prof_desc->prof_name = prof_hdr->dev_profile_str; ++ ret = aw_parse_raw_reg(dev, cfg_data, prof_hdr->data_size, ++ prof_desc); ++ break; ++ case AW_BIN_TYPE_HDR_REG: ++ snprintf(prof_desc->dev_name, sizeof(prof_hdr->dev_name), ++ "%s", prof_hdr->dev_name); ++ AW_DEV_LOGI(dev, "parse hdr_reg type data enter,product=[%s],prof_id=[%d],prof_name=[%s]", ++ prof_hdr->dev_name, prof_hdr->dev_profile, ++ prof_hdr->dev_profile_str); ++ prof_desc->prof_name = prof_hdr->dev_profile_str; ++ ret = aw_parse_reg_with_hdr(dev, cfg_data, ++ prof_hdr->data_size, prof_desc); ++ break; ++ } ++ ++ return ret; ++} ++ ++static int aw_parse_dev_type_v_1_0_0_0(struct device *dev, ++ struct acf_bin_info *acf_info) ++{ ++ int i = 0; ++ int ret; ++ int parse_prof_count = 0; ++ char *cfg_data = NULL; ++ struct aw_acf_dde_v_1_0_0_0 *acf_dde = ++ (struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_info->acf_hdr.ddt_offset); ++ ++ AW_DEV_LOGD(dev, "enter"); ++ ++ for (i = 0; i < acf_info->acf_hdr.dde_num; i++) { ++ if ((acf_dde[i].type == AW_DDE_DEV_TYPE_ID) && ++ (acf_info->aw_dev->i2c_bus == acf_dde[i].dev_bus) && ++ (acf_info->aw_dev->i2c_addr == acf_dde[i].dev_addr) && ++ (acf_info->aw_dev->chipid == acf_dde[i].chip_id)) { ++ ret = aw_check_product_name_v_1_0_0_0(dev, acf_info, &acf_dde[i]); ++ if (ret < 0) ++ continue; ++ ++ if (acf_dde[i].data_type == AW_MONITOR) { ++ cfg_data = acf_info->fw_data + acf_dde[i].data_offset; ++ AW_DEV_LOGD(dev, "parse monitor type data enter"); ++ ret = aw_parse_monitor_config(dev, cfg_data, ++ acf_dde[i].data_size); ++ } else { ++ ret = aw_parse_data_by_sec_type_v_1_0_0_0(dev, acf_info, ++ &acf_dde[i]); ++ if (ret < 0) ++ AW_DEV_LOGE(dev, "parse dev type data failed"); ++ else ++ parse_prof_count++; ++ } ++ } ++ } ++ ++ if (parse_prof_count == 0) { ++ AW_DEV_LOGE(dev, "get dev type num is %d, parse failed", parse_prof_count); ++ return -EINVAL; ++ } ++ ++ return AW_DEV_TYPE_OK; ++} ++ ++static int aw_parse_default_type_v_1_0_0_0(struct device *dev, ++ struct acf_bin_info *acf_info) ++{ ++ int i = 0; ++ int ret; ++ int parse_prof_count = 0; ++ char *cfg_data = NULL; ++ struct aw_acf_dde_v_1_0_0_0 *acf_dde = ++ (struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_info->acf_hdr.ddt_offset); ++ ++ AW_DEV_LOGD(dev, "enter"); ++ ++ for (i = 0; i < acf_info->acf_hdr.dde_num; i++) { ++ if ((acf_dde[i].type == AW_DDE_DEV_DEFAULT_TYPE_ID) && ++ (acf_info->dev_index == acf_dde[i].dev_index) && ++ (acf_info->aw_dev->chipid == acf_dde[i].chip_id)) { ++ ret = aw_check_product_name_v_1_0_0_0(dev, acf_info, &acf_dde[i]); ++ if (ret < 0) ++ continue; ++ ++ if (acf_dde[i].data_type == AW_MONITOR) { ++ cfg_data = acf_info->fw_data + acf_dde[i].data_offset; ++ AW_DEV_LOGD(dev, "parse monitor type data enter"); ++ ret = aw_parse_monitor_config(dev, cfg_data, ++ acf_dde[i].data_size); ++ } else { ++ ret = aw_parse_data_by_sec_type_v_1_0_0_0(dev, acf_info, ++ &acf_dde[i]); ++ if (ret < 0) ++ AW_DEV_LOGE(dev, "parse default type data failed"); ++ else ++ parse_prof_count++; ++ } ++ } ++ } ++ ++ if (parse_prof_count == 0) { ++ AW_DEV_LOGE(dev, "get default type num is %d,parse failed", parse_prof_count); ++ return -EINVAL; ++ } ++ ++ return AW_DEV_TYPE_OK; ++} ++ ++static int aw_parse_by_hdr_v_1_0_0_0(struct device *dev, ++ struct acf_bin_info *acf_info) ++{ ++ int ret; ++ ++ if (acf_info->prof_info.prof_type == AW_DDE_DEV_TYPE_ID) { ++ ret = aw_parse_dev_type_v_1_0_0_0(dev, acf_info); ++ if (ret < 0) ++ return ret; ++ } else if (acf_info->prof_info.prof_type == AW_DDE_DEV_DEFAULT_TYPE_ID) { ++ ret = aw_parse_default_type_v_1_0_0_0(dev, acf_info); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int aw_set_prof_off_info_v_1_0_0_0(struct device *dev, ++ struct acf_bin_info *acf_info) ++{ ++ struct aw_prof_info *prof_info = &acf_info->prof_info; ++ int i = 0; ++ int ret = 0; ++ ++ for (i = 0; i < prof_info->count; ++i) { ++ if (!(prof_info->prof_desc[i].prof_st)) { ++ snprintf(prof_info->prof_name_list[i], AW_PROFILE_STR_MAX, "%s", ++ g_power_off_name[0]); ++ prof_info->prof_desc[i].prof_name = prof_info->prof_name_list[i]; ++ prof_info->prof_desc[i].prof_st = AW_PROFILE_WAIT; ++ memset(&prof_info->prof_desc[i].data_container, 0, ++ sizeof(struct aw_data_container)); ++ return 0; ++ } ++ ++ ret = aw_check_prof_str_is_off(prof_info->prof_name_list[i]); ++ if (ret == 0) { ++ AW_DEV_LOGD(dev, "found profile off,data_len=[%d]", ++ prof_info->prof_desc[i].data_container.len); ++ return 0; ++ } ++ } ++ ++ AW_DEV_LOGE(dev, "index[%d] is out of table,profile count[%d]", ++ i, prof_info->count); ++ return -EINVAL; ++} ++ ++static int aw_parse_acf_v_1_0_0_0(struct device *dev, ++ struct acf_bin_info *acf_info) ++ ++{ ++ struct aw_prof_info *prof_info = &acf_info->prof_info; ++ int ret; ++ ++ ret = aw_parse_get_profile_count_v_1_0_0_0(dev, acf_info); ++ if (ret < 0) { ++ AW_DEV_LOGE(dev, "get profile count failed"); ++ return ret; ++ } ++ ++ ret = aw_parse_prof_name_v_1_0_0_0(dev, acf_info); ++ if (ret < 0) { ++ AW_DEV_LOGE(dev, "get profile count failed"); ++ return ret; ++ } ++ ++ acf_info->prof_info.prof_desc = devm_kzalloc(dev, ++ prof_info->count * sizeof(struct aw_prof_desc), GFP_KERNEL); ++ if (acf_info->prof_info.prof_desc == NULL) { ++ AW_DEV_LOGE(dev, "prof_desc devm_kzalloc failed"); ++ return -ENOMEM; ++ } ++ ++ ret = aw_parse_by_hdr_v_1_0_0_0(dev, acf_info); ++ if (ret < 0) { ++ AW_DEV_LOGE(dev, "parse data failed"); ++ return ret; ++ } ++ ++ ret = aw_set_prof_off_info_v_1_0_0_0(dev, acf_info); ++ if (ret < 0) { ++ AW_DEV_LOGE(dev, "set profile off info failed"); ++ return ret; ++ } ++ ++ prof_info->status = AW_ACF_UPDATE; ++ AW_DEV_LOGI(dev, "acf paese succeed"); ++ return 0; ++} ++ ++ ++/************************************************************************* ++ * ++ *acf parse API ++ * ++ *************************************************************************/ ++void aw87xxx_acf_profile_free(struct device *dev, struct acf_bin_info *acf_info) ++{ ++ struct aw_prof_info *prof_info = &acf_info->prof_info; ++ ++ prof_info->count = 0; ++ prof_info->status = AW_ACF_WAIT; ++ memset(&acf_info->acf_hdr, 0, sizeof(struct aw_acf_hdr)); ++ ++ if (prof_info->prof_desc) { ++ devm_kfree(dev, prof_info->prof_desc); ++ prof_info->prof_desc = NULL; ++ } ++ ++ if (prof_info->prof_name_list) { ++ devm_kfree(dev, prof_info->prof_name_list); ++ prof_info->prof_name_list = NULL; ++ } ++ ++ if (acf_info->fw_data) { ++ vfree(acf_info->fw_data); ++ acf_info->fw_data = NULL; ++ } ++} ++ ++int aw87xxx_acf_parse(struct device *dev, struct acf_bin_info *acf_info) ++{ ++ int ret = 0; ++ ++ AW_DEV_LOGD(dev, "enter"); ++ acf_info->prof_info.status = AW_ACF_WAIT; ++ ret = aw_check_acf_firmware(dev, acf_info->fw_data, ++ acf_info->fw_size); ++ if (ret < 0) { ++ AW_DEV_LOGE(dev, "load firmware check failed"); ++ return -EINVAL; ++ } ++ ++ memcpy(&acf_info->acf_hdr, acf_info->fw_data, ++ sizeof(struct aw_acf_hdr)); ++ ++ switch (acf_info->acf_hdr.hdr_version) { ++ case AW_ACF_HDR_VER_0_0_0_1: ++ return aw_parse_acf_v_0_0_0_1(dev, acf_info); ++ case AW_ACF_HDR_VER_1_0_0_0: ++ return aw_parse_acf_v_1_0_0_0(dev, acf_info); ++ default: ++ AW_DEV_LOGE(dev, "unsupported hdr_version [0x%x]", ++ acf_info->acf_hdr.hdr_version); ++ return -EINVAL; ++ } ++ ++ return ret; ++} ++ ++struct aw_prof_desc *aw87xxx_acf_get_prof_desc_form_name(struct device *dev, ++ struct acf_bin_info *acf_info, char *profile_name) ++{ ++ int i = 0; ++ struct aw_prof_desc *prof_desc = NULL; ++ struct aw_prof_info *prof_info = &acf_info->prof_info; ++ ++ AW_DEV_LOGD(dev, "enter"); ++ ++ if (!acf_info->prof_info.status) { ++ AW_DEV_LOGE(dev, "profile_cfg not load"); ++ return NULL; ++ } ++ ++ for (i = 0; i < prof_info->count; i++) { ++ if (!strncmp(profile_name, prof_info->prof_desc[i].prof_name, ++ AW_PROFILE_STR_MAX)) { ++ prof_desc = &prof_info->prof_desc[i]; ++ break; ++ } ++ } ++ ++ if (i == prof_info->count) { ++ AW_DEV_LOGE(dev, "profile not found"); ++ return NULL; ++ } ++ ++ AW_DEV_LOGI(dev, "get prof desc down"); ++ return prof_desc; ++} ++ ++int aw87xxx_acf_get_prof_index_form_name(struct device *dev, ++ struct acf_bin_info *acf_info, char *profile_name) ++{ ++ int i = 0; ++ struct aw_prof_info *prof_info = &acf_info->prof_info; ++ ++ if (!acf_info->prof_info.status) { ++ AW_DEV_LOGE(dev, "profile_cfg not load"); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < prof_info->count; i++) { ++ if (!strncmp(profile_name, prof_info->prof_name_list[i], ++ AW_PROFILE_STR_MAX)) { ++ return i; ++ } ++ } ++ ++ AW_DEV_LOGE(dev, "profile_index not found"); ++ return -EINVAL; ++} ++ ++char *aw87xxx_acf_get_prof_name_form_index(struct device *dev, ++ struct acf_bin_info *acf_info, int index) ++{ ++ struct aw_prof_info *prof_info = &acf_info->prof_info; ++ ++ if (!acf_info->prof_info.status) { ++ AW_DEV_LOGE(dev, "profile_cfg not load"); ++ return NULL; ++ } ++ ++ if (index >= prof_info->count || index < 0) { ++ AW_DEV_LOGE(dev, "profile_index out of table"); ++ return NULL; ++ } ++ ++ return prof_info->prof_desc[index].prof_name; ++} ++ ++ ++int aw87xxx_acf_get_profile_count(struct device *dev, ++ struct acf_bin_info *acf_info) ++{ ++ struct aw_prof_info *prof_info = &acf_info->prof_info; ++ ++ if (!acf_info->prof_info.status) { ++ AW_DEV_LOGE(dev, "profile_cfg not load"); ++ return -EINVAL; ++ } ++ ++ if (prof_info->count > 0) { ++ return prof_info->count; ++ } ++ ++ return -EINVAL; ++} ++ ++char *aw87xxx_acf_get_prof_off_name(struct device *dev, ++ struct acf_bin_info *acf_info) ++{ ++ int i = 0; ++ int ret = 0; ++ struct aw_prof_info *prof_info = &acf_info->prof_info; ++ ++ if (!acf_info->prof_info.status) { ++ AW_DEV_LOGE(dev, "profile_cfg not load"); ++ return NULL; ++ } ++ ++ for (i = 0; i < prof_info->count; i++) { ++ ret = aw_check_prof_str_is_off(prof_info->prof_name_list[i]); ++ if (ret == 0) ++ return prof_info->prof_name_list[i]; ++ } ++ ++ return NULL; ++} ++ ++void aw87xxx_acf_init(struct aw_device *aw_dev, struct acf_bin_info *acf_info, int index) ++{ ++ ++ acf_info->load_count = 0; ++ acf_info->prof_info.status = AW_ACF_WAIT; ++ acf_info->dev_index = index; ++ acf_info->aw_dev = aw_dev; ++ acf_info->product_cnt = aw_dev->product_cnt; ++ acf_info->product_tab = aw_dev->product_tab; ++ acf_info->prof_info.prof_desc = NULL; ++ acf_info->fw_data = NULL; ++ acf_info->fw_size = 0; ++} ++ +diff --git a/sound/soc/codecs/aw87xxx/aw87xxx_acf_bin.h b/sound/soc/codecs/aw87xxx/aw87xxx_acf_bin.h +new file mode 100644 +index 000000000..ebe0c77f5 +--- /dev/null ++++ b/sound/soc/codecs/aw87xxx/aw87xxx_acf_bin.h +@@ -0,0 +1,191 @@ ++#ifndef __AW87XXX_ACF_BIN_H__ ++#define __AW87XXX_ACF_BIN_H__ ++ ++#include "aw87xxx_device.h" ++ ++#define AW_PROJECT_NAME_MAX (24) ++#define AW_CUSTOMER_NAME_MAX (16) ++#define AW_CFG_VERSION_MAX (4) ++#define AW_TBL_VERSION_MAX (4) ++#define AW_DDE_DEVICE_TYPE (0) ++#define AW_DDE_SKT_TYPE (1) ++#define AW_DDE_DEFAULT_TYPE (2) ++ ++#define AW_REG_ADDR_BYTE (1) ++#define AW_REG_DATA_BYTE (1) ++ ++#define AW_ACF_FILE_ID (0xa15f908) ++#define AW_PROFILE_STR_MAX (32) ++#define AW_POWER_OFF_NAME_SUPPORT_COUNT (5) ++ ++enum aw_cfg_hdr_version { ++ AW_ACF_HDR_VER_0_0_0_1 = 0x00000001, ++ AW_ACF_HDR_VER_1_0_0_0 = 0x01000000, ++}; ++ ++enum aw_acf_dde_type_id { ++ AW_DEV_NONE_TYPE_ID = 0xFFFFFFFF, ++ AW_DDE_DEV_TYPE_ID = 0x00000000, ++ AW_DDE_SKT_TYPE_ID = 0x00000001, ++ AW_DDE_DEV_DEFAULT_TYPE_ID = 0x00000002, ++ AW_DDE_TYPE_MAX, ++}; ++ ++enum aw_raw_data_type_id { ++ AW_BIN_TYPE_REG = 0x00000000, ++ AW_BIN_TYPE_DSP, ++ AW_BIN_TYPE_DSP_CFG, ++ AW_BIN_TYPE_DSP_FW, ++ AW_BIN_TYPE_HDR_REG, ++ AW_BIN_TYPE_HDR_DSP_CFG, ++ AW_BIN_TYPE_HDR_DSP_FW, ++ AW_BIN_TYPE_MUTLBIN, ++ AW_SKT_UI_PROJECT, ++ AW_DSP_CFG, ++ AW_MONITOR, ++ AW_BIN_TYPE_MAX, ++}; ++ ++enum { ++ AW_DEV_TYPE_OK = 0, ++ AW_DEV_TYPE_NONE = 1, ++}; ++ ++enum aw_profile_status { ++ AW_PROFILE_WAIT = 0, ++ AW_PROFILE_OK, ++}; ++ ++enum aw_acf_load_status { ++ AW_ACF_WAIT = 0, ++ AW_ACF_UPDATE, ++}; ++ ++enum aw_bin_dev_profile_id { ++ AW_PROFILE_MUSIC = 0x0000, ++ AW_PROFILE_VOICE, ++ AW_PROFILE_VOIP, ++ AW_PROFILE_RINGTONE, ++ AW_PROFILE_RINGTONE_HS, ++ AW_PROFILE_LOWPOWER, ++ AW_PROFILE_BYPASS, ++ AW_PROFILE_MMI, ++ AW_PROFILE_FM, ++ AW_PROFILE_NOTIFICATION, ++ AW_PROFILE_RECEIVER, ++ AW_PROFILE_OFF, ++ AW_PROFILE_MAX, ++}; ++ ++struct aw_acf_hdr { ++ int32_t a_id; /* acf file ID 0xa15f908 */ ++ char project[AW_PROJECT_NAME_MAX]; /* project name */ ++ char custom[AW_CUSTOMER_NAME_MAX]; /* custom name :huawei xiaomi vivo oppo */ ++ uint8_t version[AW_CFG_VERSION_MAX]; /* author update version */ ++ int32_t author_id; /* author id */ ++ int32_t ddt_size; /* sub section table entry size */ ++ int32_t dde_num; /* sub section table entry num */ ++ int32_t ddt_offset; /* sub section table offset in file */ ++ int32_t hdr_version; /* sub section table version */ ++ int32_t reserve[3]; /* Reserved Bits */ ++}; ++ ++struct aw_acf_dde { ++ int32_t type; /* dde type id */ ++ char dev_name[AW_CUSTOMER_NAME_MAX]; /* customer dev name */ ++ int16_t dev_index; /* dev id */ ++ int16_t dev_bus; /* dev bus id */ ++ int16_t dev_addr; /* dev addr id */ ++ int16_t dev_profile; /* dev profile id */ ++ int32_t data_type; /* data type id */ ++ int32_t data_size; /* dde data size in block */ ++ int32_t data_offset; /* dde data offset in block */ ++ int32_t data_crc; /* dde data crc checkout */ ++ int32_t reserve[5]; /* Reserved Bits */ ++}; ++ ++struct aw_acf_dde_v_1_0_0_0 { ++ uint32_t type; /* DDE type id */ ++ char dev_name[AW_CUSTOMER_NAME_MAX]; /* customer dev name */ ++ uint16_t dev_index; /* dev id */ ++ uint16_t dev_bus; /* dev bus id */ ++ uint16_t dev_addr; /* dev addr id */ ++ uint16_t dev_profile; /* dev profile id*/ ++ uint32_t data_type; /* data type id */ ++ uint32_t data_size; /* dde data size in block */ ++ uint32_t data_offset; /* dde data offset in block */ ++ uint32_t data_crc; /* dde data crc checkout */ ++ char dev_profile_str[AW_PROFILE_STR_MAX]; /* dde custom profile name */ ++ uint32_t chip_id; /* dde custom product chip id */ ++ uint32_t reserve[4]; ++}; ++ ++struct aw_data_with_header { ++ uint32_t check_sum; ++ uint32_t header_ver; ++ uint32_t bin_data_type; ++ uint32_t bin_data_ver; ++ uint32_t bin_data_size; ++ uint32_t ui_ver; ++ char product[8]; ++ uint32_t addr_byte_len; ++ uint32_t data_byte_len; ++ uint32_t device_addr; ++ uint32_t reserve[4]; ++}; ++ ++struct aw_data_container { ++ uint32_t len; ++ uint8_t *data; ++}; ++ ++struct aw_prof_desc { ++ uint32_t prof_st; ++ char *prof_name; ++ char dev_name[AW_CUSTOMER_NAME_MAX]; ++ struct aw_data_container data_container; ++}; ++ ++struct aw_all_prof_info { ++ struct aw_prof_desc prof_desc[AW_PROFILE_MAX]; ++}; ++ ++struct aw_prof_info { ++ int count; ++ int status; ++ int prof_type; ++ char (*prof_name_list)[AW_PROFILE_STR_MAX]; ++ struct aw_prof_desc *prof_desc; ++}; ++ ++struct acf_bin_info { ++ int load_count; ++ int fw_size; ++ int16_t dev_index; ++ char *fw_data; ++ int product_cnt; ++ const char **product_tab; ++ struct aw_device *aw_dev; ++ ++ struct aw_acf_hdr acf_hdr; ++ struct aw_prof_info prof_info; ++}; ++ ++char *aw87xxx_ctos_get_prof_name(int profile_id); ++void aw87xxx_acf_profile_free(struct device *dev, ++ struct acf_bin_info *acf_info); ++int aw87xxx_acf_parse(struct device *dev, struct acf_bin_info *acf_info); ++struct aw_prof_desc *aw87xxx_acf_get_prof_desc_form_name(struct device *dev, ++ struct acf_bin_info *acf_info, char *profile_name); ++int aw87xxx_acf_get_prof_index_form_name(struct device *dev, ++ struct acf_bin_info *acf_info, char *profile_name); ++char *aw87xxx_acf_get_prof_name_form_index(struct device *dev, ++ struct acf_bin_info *acf_info, int index); ++int aw87xxx_acf_get_profile_count(struct device *dev, ++ struct acf_bin_info *acf_info); ++char *aw87xxx_acf_get_prof_off_name(struct device *dev, ++ struct acf_bin_info *acf_info); ++void aw87xxx_acf_init(struct aw_device *aw_dev, struct acf_bin_info *acf_info, int index); ++ ++ ++#endif +diff --git a/sound/soc/codecs/aw87xxx/aw87xxx_bin_parse.c b/sound/soc/codecs/aw87xxx/aw87xxx_bin_parse.c +new file mode 100644 +index 000000000..7eab9efde +--- /dev/null ++++ b/sound/soc/codecs/aw87xxx/aw87xxx_bin_parse.c +@@ -0,0 +1,515 @@ ++/* ++* aw87xxx_bin_parse.c ++* ++* Copyright (c) 2020 AWINIC Technology CO., LTD ++* ++* 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. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "aw87xxx_bin_parse.h" ++ ++#define AWINIC_CODE_VERSION "V0.0.7-V1.0.4" /* "code version"-"excel version" */ ++ ++#define DEBUG_LOG_LEVEL ++#ifdef DEBUG_LOG_LEVEL ++#define DBG(fmt, arg...) do {\ ++printk("AWINIC_BIN %s,line= %d,"fmt, __func__, __LINE__, ##arg);\ ++} while (0) ++#define DBG_ERR(fmt, arg...) do {\ ++printk("AWINIC_BIN_ERR %s,line= %d,"fmt, __func__, __LINE__, ##arg);\ ++} while (0) ++#else ++#define DBG(fmt, arg...) do {} while (0) ++#define DBG_ERR(fmt, arg...) do {} while (0) ++#endif ++ ++#define printing_data_code ++ ++typedef unsigned short int aw_uint16; ++typedef unsigned long int aw_uint32; ++ ++#define BigLittleSwap16(A) ((((aw_uint16)(A) & 0xff00) >> 8) | \ ++ (((aw_uint16)(A) & 0x00ff) << 8)) ++ ++#define BigLittleSwap32(A) ((((aw_uint32)(A) & 0xff000000) >> 24) | \ ++ (((aw_uint32)(A) & 0x00ff0000) >> 8) | \ ++ (((aw_uint32)(A) & 0x0000ff00) << 8) | \ ++ (((aw_uint32)(A) & 0x000000ff) << 24)) ++ ++ ++static int aw_parse_bin_header_1_0_0(struct aw_bin *bin); ++ ++/** ++* ++* Interface function ++* ++* return value: ++* value = 0 :success; ++* value = -1 :check bin header version ++* value = -2 :check bin data type ++* value = -3 :check sum or check bin data len error ++* value = -4 :check data version ++* value = -5 :check register num ++* value = -6 :check dsp reg num ++* value = -7 :check soc app num ++* value = -8 :bin is NULL point ++* ++**/ ++ ++/******************************************************** ++* ++* check sum data ++* ++********************************************************/ ++static int aw_check_sum(struct aw_bin *bin, int bin_num) ++{ ++ unsigned int i = 0; ++ unsigned int sum_data = 0; ++ unsigned int check_sum = 0; ++ unsigned char *p_check_sum = NULL; ++ ++ DBG("enter\n"); ++ ++ p_check_sum = ++ &(bin->info.data[(bin->header_info[bin_num].valid_data_addr - ++ bin->header_info[bin_num].header_len)]); ++ DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); ++ check_sum = GET_32_DATA(*(p_check_sum + 3), ++ *(p_check_sum + 2), ++ *(p_check_sum + 1), *(p_check_sum)); ++ ++ for (i = 4; ++ i < ++ bin->header_info[bin_num].bin_data_len + ++ bin->header_info[bin_num].header_len; i++) { ++ sum_data += *(p_check_sum + i); ++ } ++ DBG("aw_bin_parse bin_num=%d, check_sum = 0x%x, sum_data = 0x%x\n", ++ bin_num, check_sum, sum_data); ++ if (sum_data != check_sum) { ++ p_check_sum = NULL; ++ DBG_ERR("aw_bin_parse check sum or check bin data len error\n"); ++ DBG_ERR("aw_bin_parse bin_num=%d, check_sum = 0x%x, sum_data = 0x%x\n", bin_num, check_sum, sum_data); ++ return -3; ++ } ++ p_check_sum = NULL; ++ ++ return 0; ++} ++ ++static int aw_check_data_version(struct aw_bin *bin, int bin_num) ++{ ++ int i = 0; ++ DBG("enter\n"); ++ ++ for (i = DATA_VERSION_V1; i < DATA_VERSION_MAX; i++) { ++ if (bin->header_info[bin_num].bin_data_ver == i) { ++ return 0; ++ } ++ } ++ DBG_ERR("aw_bin_parse Unrecognized this bin data version\n"); ++ return -4; ++} ++ ++static int aw_check_register_num_v1(struct aw_bin *bin, int bin_num) ++{ ++ unsigned int check_register_num = 0; ++ unsigned int parse_register_num = 0; ++ unsigned char *p_check_sum = NULL; ++ ++ DBG("enter\n"); ++ ++ p_check_sum = ++ &(bin->info.data[(bin->header_info[bin_num].valid_data_addr)]); ++ DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); ++ parse_register_num = GET_32_DATA(*(p_check_sum + 3), ++ *(p_check_sum + 2), ++ *(p_check_sum + 1), *(p_check_sum)); ++ check_register_num = (bin->header_info[bin_num].bin_data_len - 4) / ++ (bin->header_info[bin_num].reg_byte_len + ++ bin->header_info[bin_num].data_byte_len); ++ DBG ++ ("aw_bin_parse bin_num=%d, parse_register_num = 0x%x, check_register_num = 0x%x\n", ++ bin_num, parse_register_num, check_register_num); ++ if (parse_register_num != check_register_num) { ++ p_check_sum = NULL; ++ DBG_ERR("aw_bin_parse register num is error\n"); ++ DBG_ERR("aw_bin_parse bin_num=%d, parse_register_num = 0x%x, check_register_num = 0x%x\n", bin_num, parse_register_num, check_register_num); ++ return -5; ++ } ++ bin->header_info[bin_num].reg_num = parse_register_num; ++ bin->header_info[bin_num].valid_data_len = ++ bin->header_info[bin_num].bin_data_len - 4; ++ p_check_sum = NULL; ++ bin->header_info[bin_num].valid_data_addr = ++ bin->header_info[bin_num].valid_data_addr + 4; ++ return 0; ++} ++ ++static int aw_check_dsp_reg_num_v1(struct aw_bin *bin, int bin_num) ++{ ++ unsigned int check_dsp_reg_num = 0; ++ unsigned int parse_dsp_reg_num = 0; ++ unsigned char *p_check_sum = NULL; ++ ++ DBG("enter\n"); ++ ++ p_check_sum = ++ &(bin->info.data[(bin->header_info[bin_num].valid_data_addr)]); ++ DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); ++ parse_dsp_reg_num = GET_32_DATA(*(p_check_sum + 7), ++ *(p_check_sum + 6), ++ *(p_check_sum + 5), *(p_check_sum + 4)); ++ bin->header_info[bin_num].reg_data_byte_len = ++ GET_32_DATA(*(p_check_sum + 11), *(p_check_sum + 10), ++ *(p_check_sum + 9), *(p_check_sum + 8)); ++ check_dsp_reg_num = ++ (bin->header_info[bin_num].bin_data_len - ++ 12) / bin->header_info[bin_num].reg_data_byte_len; ++ DBG ++ ("aw_bin_parse bin_num=%d, parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x\n", ++ bin_num, parse_dsp_reg_num, check_dsp_reg_num); ++ if (parse_dsp_reg_num != check_dsp_reg_num) { ++ p_check_sum = NULL; ++ DBG_ERR("aw_bin_parse dsp reg num is error\n"); ++ DBG_ERR("aw_bin_parse bin_num=%d, parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x\n", bin_num, parse_dsp_reg_num, check_dsp_reg_num); ++ return -6; ++ } ++ bin->header_info[bin_num].download_addr = ++ GET_32_DATA(*(p_check_sum + 3), *(p_check_sum + 2), ++ *(p_check_sum + 1), *(p_check_sum)); ++ bin->header_info[bin_num].reg_num = parse_dsp_reg_num; ++ bin->header_info[bin_num].valid_data_len = ++ bin->header_info[bin_num].bin_data_len - 12; ++ p_check_sum = NULL; ++ bin->header_info[bin_num].valid_data_addr = ++ bin->header_info[bin_num].valid_data_addr + 12; ++ return 0; ++} ++ ++static int aw_check_soc_app_num_v1(struct aw_bin *bin, int bin_num) ++{ ++ unsigned int check_soc_app_num = 0; ++ unsigned int parse_soc_app_num = 0; ++ unsigned char *p_check_sum = NULL; ++ ++ DBG("enter\n"); ++ ++ p_check_sum = ++ &(bin->info.data[(bin->header_info[bin_num].valid_data_addr)]); ++ DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); ++ bin->header_info[bin_num].app_version = GET_32_DATA(*(p_check_sum + 3), ++ *(p_check_sum + 2), ++ *(p_check_sum + 1), ++ *(p_check_sum)); ++ parse_soc_app_num = GET_32_DATA(*(p_check_sum + 11), ++ *(p_check_sum + 10), ++ *(p_check_sum + 9), *(p_check_sum + 8)); ++ check_soc_app_num = bin->header_info[bin_num].bin_data_len - 12; ++ DBG ++ ("aw_bin_parse bin_num=%d, parse_soc_app_num = 0x%x, check_soc_app_num = 0x%x\n", ++ bin_num, parse_soc_app_num, check_soc_app_num); ++ if (parse_soc_app_num != check_soc_app_num) { ++ p_check_sum = NULL; ++ DBG_ERR("aw_bin_parse soc app num is error\n"); ++ DBG_ERR("aw_bin_parse bin_num=%d, parse_soc_app_num = 0x%x, check_soc_app_num = 0x%x\n", bin_num, parse_soc_app_num, check_soc_app_num); ++ return -7; ++ } ++ bin->header_info[bin_num].reg_num = parse_soc_app_num; ++ bin->header_info[bin_num].download_addr = ++ GET_32_DATA(*(p_check_sum + 7), *(p_check_sum + 6), ++ *(p_check_sum + 5), *(p_check_sum + 4)); ++ bin->header_info[bin_num].valid_data_len = ++ bin->header_info[bin_num].bin_data_len - 12; ++ p_check_sum = NULL; ++ bin->header_info[bin_num].valid_data_addr = ++ bin->header_info[bin_num].valid_data_addr + 12; ++ return 0; ++} ++ ++/************************ ++*** ++***bin header 1_0_0 ++*** ++************************/ ++static void aw_get_single_bin_header_1_0_0(struct aw_bin *bin) ++{ ++ int i; ++ DBG("enter %s\n", __func__); ++ bin->header_info[bin->all_bin_parse_num].header_len = 60; ++ bin->header_info[bin->all_bin_parse_num].check_sum = ++ GET_32_DATA(*(bin->p_addr + 3), *(bin->p_addr + 2), ++ *(bin->p_addr + 1), *(bin->p_addr)); ++ bin->header_info[bin->all_bin_parse_num].header_ver = ++ GET_32_DATA(*(bin->p_addr + 7), *(bin->p_addr + 6), ++ *(bin->p_addr + 5), *(bin->p_addr + 4)); ++ bin->header_info[bin->all_bin_parse_num].bin_data_type = ++ GET_32_DATA(*(bin->p_addr + 11), *(bin->p_addr + 10), ++ *(bin->p_addr + 9), *(bin->p_addr + 8)); ++ bin->header_info[bin->all_bin_parse_num].bin_data_ver = ++ GET_32_DATA(*(bin->p_addr + 15), *(bin->p_addr + 14), ++ *(bin->p_addr + 13), *(bin->p_addr + 12)); ++ bin->header_info[bin->all_bin_parse_num].bin_data_len = ++ GET_32_DATA(*(bin->p_addr + 19), *(bin->p_addr + 18), ++ *(bin->p_addr + 17), *(bin->p_addr + 16)); ++ bin->header_info[bin->all_bin_parse_num].ui_ver = ++ GET_32_DATA(*(bin->p_addr + 23), *(bin->p_addr + 22), ++ *(bin->p_addr + 21), *(bin->p_addr + 20)); ++ bin->header_info[bin->all_bin_parse_num].reg_byte_len = ++ GET_32_DATA(*(bin->p_addr + 35), *(bin->p_addr + 34), ++ *(bin->p_addr + 33), *(bin->p_addr + 32)); ++ bin->header_info[bin->all_bin_parse_num].data_byte_len = ++ GET_32_DATA(*(bin->p_addr + 39), *(bin->p_addr + 38), ++ *(bin->p_addr + 37), *(bin->p_addr + 36)); ++ bin->header_info[bin->all_bin_parse_num].device_addr = ++ GET_32_DATA(*(bin->p_addr + 43), *(bin->p_addr + 42), ++ *(bin->p_addr + 41), *(bin->p_addr + 40)); ++ for (i = 0; i < 8; i++) { ++ bin->header_info[bin->all_bin_parse_num].chip_type[i] = ++ *(bin->p_addr + 24 + i); ++ } ++ bin->header_info[bin->all_bin_parse_num].reg_num = 0x00000000; ++ bin->header_info[bin->all_bin_parse_num].reg_data_byte_len = 0x00000000; ++ bin->header_info[bin->all_bin_parse_num].download_addr = 0x00000000; ++ bin->header_info[bin->all_bin_parse_num].app_version = 0x00000000; ++ bin->header_info[bin->all_bin_parse_num].valid_data_len = 0x00000000; ++ bin->all_bin_parse_num += 1; ++} ++ ++static int aw_parse_each_of_multi_bins_1_0_0(unsigned int bin_num, int bin_serial_num, ++ struct aw_bin *bin) ++{ ++ int ret = 0; ++ unsigned int bin_start_addr = 0; ++ unsigned int valid_data_len = 0; ++ DBG("aw_bin_parse enter multi bin branch -- %s\n", __func__); ++ if (!bin_serial_num) { ++ bin_start_addr = GET_32_DATA(*(bin->p_addr + 67), ++ *(bin->p_addr + 66), ++ *(bin->p_addr + 65), ++ *(bin->p_addr + 64)); ++ bin->p_addr += (60 + bin_start_addr); ++ bin->header_info[bin->all_bin_parse_num].valid_data_addr = ++ bin->header_info[bin->all_bin_parse_num - ++ 1].valid_data_addr + 4 + 8 * bin_num + 60; ++ } else { ++ valid_data_len = ++ bin->header_info[bin->all_bin_parse_num - 1].bin_data_len; ++ bin->p_addr += (60 + valid_data_len); ++ bin->header_info[bin->all_bin_parse_num].valid_data_addr = ++ bin->header_info[bin->all_bin_parse_num - ++ 1].valid_data_addr + ++ bin->header_info[bin->all_bin_parse_num - 1].bin_data_len + ++ 60; ++ } ++ ++ ret = aw_parse_bin_header_1_0_0(bin); ++ return ret; ++} ++ ++/* Get the number of bins in multi bins, and set a for loop, loop processing each bin data */ ++static int aw_get_multi_bin_header_1_0_0(struct aw_bin *bin) ++{ ++ int i = 0; ++ int ret = 0; ++ unsigned int bin_num = 0; ++ DBG("aw_bin_parse enter multi bin branch -- %s\n", __func__); ++ bin_num = GET_32_DATA(*(bin->p_addr + 63), ++ *(bin->p_addr + 62), ++ *(bin->p_addr + 61), *(bin->p_addr + 60)); ++ if (bin->multi_bin_parse_num == 1) { ++ bin->header_info[bin->all_bin_parse_num].valid_data_addr = 60; ++ } ++ aw_get_single_bin_header_1_0_0(bin); ++ ++ for (i = 0; i < bin_num; i++) { ++ DBG("aw_bin_parse enter multi bin for is %d\n", i); ++ ret = aw_parse_each_of_multi_bins_1_0_0(bin_num, i, bin); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ return 0; ++} ++ ++/******************************************************** ++* ++* If the bin framework header version is 1.0.0, ++ determine the data type of bin, and then perform different processing ++ according to the data type ++ If it is a single bin data type, write the data directly into the structure array ++ If it is a multi-bin data type, first obtain the number of bins, ++ and then recursively call the bin frame header processing function ++ according to the bin number to process the frame header information of each bin separately ++* ++********************************************************/ ++static int aw_parse_bin_header_1_0_0(struct aw_bin *bin) ++{ ++ int ret = 0; ++ unsigned int bin_data_type; ++ DBG("enter %s\n", __func__); ++ bin_data_type = GET_32_DATA(*(bin->p_addr + 11), ++ *(bin->p_addr + 10), ++ *(bin->p_addr + 9), *(bin->p_addr + 8)); ++ DBG("aw_bin_parse bin_data_type 0x%x\n", bin_data_type); ++ switch (bin_data_type) { ++ case DATA_TYPE_REGISTER: ++ case DATA_TYPE_DSP_REG: ++ case DATA_TYPE_SOC_APP: ++ /* Divided into two processing methods, ++ one is single bin processing, ++ and the other is single bin processing in multi bin */ ++ DBG("aw_bin_parse enter single bin branch\n"); ++ bin->single_bin_parse_num += 1; ++ DBG("%s bin->single_bin_parse_num is %d\n", __func__, ++ bin->single_bin_parse_num); ++ if (!bin->multi_bin_parse_num) { ++ bin->header_info[bin-> ++ all_bin_parse_num].valid_data_addr = ++ 60; ++ } ++ aw_get_single_bin_header_1_0_0(bin); ++ break; ++ case DATA_TYPE_MULTI_BINS: ++ /* Get the number of times to enter multi bins */ ++ DBG("aw_bin_parse enter multi bin branch\n"); ++ bin->multi_bin_parse_num += 1; ++ DBG("%s bin->multi_bin_parse_num is %d\n", __func__, ++ bin->multi_bin_parse_num); ++ ret = aw_get_multi_bin_header_1_0_0(bin); ++ if (ret < 0) { ++ return ret; ++ } ++ break; ++ default: ++ DBG_ERR("aw_bin_parse Unrecognized this bin data type\n"); ++ return -2; ++ } ++ return 0; ++} ++ ++/* get the bin's header version */ ++static int aw_check_bin_header_version(struct aw_bin *bin) ++{ ++ int ret = 0; ++ unsigned int header_version = 0; ++ ++ header_version = GET_32_DATA(*(bin->p_addr + 7), ++ *(bin->p_addr + 6), ++ *(bin->p_addr + 5), *(bin->p_addr + 4)); ++ ++ DBG("aw_bin_parse header_version 0x%x\n", header_version); ++ ++ /* Write data to the corresponding structure array ++ according to different formats of the bin frame header version */ ++ switch (header_version) { ++ case HEADER_VERSION_1_0_0: ++ ret = aw_parse_bin_header_1_0_0(bin); ++ return ret; ++ default: ++ DBG_ERR("aw_bin_parse Unrecognized this bin header version \n"); ++ return -1; ++ } ++} ++ ++int aw87xxx_parsing_bin_file(struct aw_bin *bin) ++{ ++ int i = 0; ++ int ret = 0; ++ ++ DBG("aw_bin_parse code version:%s\n", AWINIC_CODE_VERSION); ++ if (!bin) { ++ DBG_ERR("aw_bin_parse bin is NULL\n"); ++ return -8; ++ } ++ bin->p_addr = bin->info.data; ++ bin->all_bin_parse_num = 0; ++ bin->multi_bin_parse_num = 0; ++ bin->single_bin_parse_num = 0; ++ ++ /* filling bins header info */ ++ ret = aw_check_bin_header_version(bin); ++ if (ret < 0) { ++ DBG_ERR("aw_bin_parse check bin header version error\n"); ++ return ret; ++ } ++ bin->p_addr = NULL; ++ ++ /* check bin header info */ ++ for (i = 0; i < bin->all_bin_parse_num; i++) { ++ /* check sum */ ++ ret = aw_check_sum(bin, i); ++ if (ret < 0) { ++ DBG_ERR("aw_bin_parse check sum data error\n"); ++ return ret; ++ } ++ /* check bin data version */ ++ ret = aw_check_data_version(bin, i); ++ if (ret < 0) { ++ DBG_ERR("aw_bin_parse check data version error\n"); ++ return ret; ++ } ++ /* check valid data */ ++ if (bin->header_info[i].bin_data_ver == DATA_VERSION_V1) { ++ /* check register num */ ++ if (bin->header_info[i].bin_data_type == ++ DATA_TYPE_REGISTER) { ++ ret = aw_check_register_num_v1(bin, i); ++ if (ret < 0) { ++ DBG_ERR ++ ("aw_bin_parse check register num error\n"); ++ return ret; ++ } ++ /* check dsp reg num */ ++ } else if (bin->header_info[i].bin_data_type == ++ DATA_TYPE_DSP_REG) { ++ ret = aw_check_dsp_reg_num_v1(bin, i); ++ if (ret < 0) { ++ DBG_ERR ++ ("aw_bin_parse check dsp reg num error\n"); ++ return ret; ++ } ++ /* check soc app num */ ++ } else if (bin->header_info[i].bin_data_type == ++ DATA_TYPE_SOC_APP) { ++ ret = aw_check_soc_app_num_v1(bin, i); ++ if (ret < 0) { ++ DBG_ERR ++ ("aw_bin_parse check soc app num error\n"); ++ return ret; ++ } ++ } else { ++ bin->header_info[i].valid_data_len = ++ bin->header_info[i].bin_data_len; ++ } ++ } ++ } ++ DBG("aw_bin_parse parsing success\n"); ++ ++ return 0; ++} +diff --git a/sound/soc/codecs/aw87xxx/aw87xxx_bin_parse.h b/sound/soc/codecs/aw87xxx/aw87xxx_bin_parse.h +new file mode 100644 +index 000000000..c6e6eaa8a +--- /dev/null ++++ b/sound/soc/codecs/aw87xxx/aw87xxx_bin_parse.h +@@ -0,0 +1,73 @@ ++#ifndef __AW87XXX_BIN_PARSE_H__ ++#define __AW87XXX_BIN_PARSE_H__ ++ ++#define NULL ((void *)0) ++#define GET_32_DATA(w, x, y, z) ((unsigned int)(((w) << 24) | ((x) << 16) | ((y) << 8) | (z))) ++#define BIN_NUM_MAX 100 ++#define HEADER_LEN 60 ++/********************************************************* ++ * ++ * header information ++ * ++ ********************************************************/ ++enum bin_header_version_enum { ++ HEADER_VERSION_1_0_0 = 0x01000000, ++}; ++ ++enum data_type_enum { ++ DATA_TYPE_REGISTER = 0x00000000, ++ DATA_TYPE_DSP_REG = 0x00000010, ++ DATA_TYPE_DSP_CFG = 0x00000011, ++ DATA_TYPE_SOC_REG = 0x00000020, ++ DATA_TYPE_SOC_APP = 0x00000021, ++ DATA_TYPE_MULTI_BINS = 0x00002000, ++ DATA_TYPE_MONITOR_ANALOG = 0x00020000, ++}; ++ ++enum data_version_enum { ++ DATA_VERSION_V1 = 0X00000001, /*default little edian */ ++ DATA_VERSION_MAX, ++}; ++ ++struct bin_header_info { ++ unsigned int header_len; /* Frame header length */ ++ unsigned int check_sum; /* Frame header information-Checksum */ ++ unsigned int header_ver; /* Frame header information-Frame header version */ ++ unsigned int bin_data_type; /* Frame header information-Data type */ ++ unsigned int bin_data_ver; /* Frame header information-Data version */ ++ unsigned int bin_data_len; /* Frame header information-Data length */ ++ unsigned int ui_ver; /* Frame header information-ui version */ ++ unsigned char chip_type[8]; /* Frame header information-chip type */ ++ unsigned int reg_byte_len; /* Frame header information-reg byte len */ ++ unsigned int data_byte_len; /* Frame header information-data byte len */ ++ unsigned int device_addr; /* Frame header information-device addr */ ++ unsigned int valid_data_len; /* Length of valid data obtained after parsing */ ++ unsigned int valid_data_addr; /* The offset address of the valid data obtained after parsing relative to info */ ++ ++ unsigned int reg_num; /* The number of registers obtained after parsing */ ++ unsigned int reg_data_byte_len; /* The byte length of the register obtained after parsing */ ++ unsigned int download_addr; /* The starting address or download address obtained after parsing */ ++ unsigned int app_version; /* The software version number obtained after parsing */ ++}; ++ ++/************************************************************ ++* ++* function define ++* ++************************************************************/ ++struct bin_container { ++ unsigned int len; /* The size of the bin file obtained from the firmware */ ++ unsigned char data[]; /* Store the bin file obtained from the firmware */ ++}; ++ ++struct aw_bin { ++ unsigned char *p_addr; /* Offset pointer (backward offset pointer to obtain frame header information and important information) */ ++ unsigned int all_bin_parse_num; /* The number of all bin files */ ++ unsigned int multi_bin_parse_num; /* The number of single bin files */ ++ unsigned int single_bin_parse_num; /* The number of multiple bin files */ ++ struct bin_header_info header_info[BIN_NUM_MAX]; /* Frame header information and other important data obtained after parsing */ ++ struct bin_container info; /* Obtained bin file data that needs to be parsed */ ++}; ++ ++extern int aw87xxx_parsing_bin_file(struct aw_bin *bin); ++#endif +diff --git a/sound/soc/codecs/aw87xxx/aw87xxx_device.c b/sound/soc/codecs/aw87xxx/aw87xxx_device.c +new file mode 100644 +index 000000000..087770857 +--- /dev/null ++++ b/sound/soc/codecs/aw87xxx/aw87xxx_device.c +@@ -0,0 +1,977 @@ ++/* ++ * aw87xxx_device.c aw87xxx pa module ++ * ++ * Copyright (c) 2021 AWINIC Technology CO., LTD ++ * ++ * Author: Barry ++ * ++ * 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. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "aw87xxx.h" ++#include "aw87xxx_device.h" ++#include "aw87xxx_log.h" ++#include "aw87xxx_pid_9b_reg.h" ++#include "aw87xxx_pid_18_reg.h" ++#include "aw87xxx_pid_39_reg.h" ++#include "aw87xxx_pid_59_3x9_reg.h" ++#include "aw87xxx_pid_59_5x9_reg.h" ++#include "aw87xxx_pid_5a_reg.h" ++#include "aw87xxx_pid_76_reg.h" ++#include "aw87xxx_pid_60_reg.h" ++ ++/************************************************************************* ++ * aw87xxx variable ++ ************************************************************************/ ++const char *g_aw_pid_9b_product[] = { ++ "aw87319", ++}; ++const char *g_aw_pid_18_product[] = { ++ "aw87418", ++}; ++ ++const char *g_aw_pid_39_product[] = { ++ "aw87329", ++ "aw87339", ++ "aw87349", ++}; ++ ++const char *g_aw_pid_59_3x9_product[] = { ++ "aw87359", ++ "aw87389", ++}; ++ ++const char *g_aw_pid_59_5x9_product[] = { ++ "aw87509", ++ "aw87519", ++ "aw87529", ++ "aw87539", ++}; ++ ++const char *g_aw_pid_5a_product[] = { ++ "aw87549", ++ "aw87559", ++ "aw87569", ++ "aw87579", ++ "aw81509", ++}; ++ ++const char *g_aw_pid_76_product[] = { ++ "aw87390", ++ "aw87320", ++ "aw87401", ++ "aw87360", ++}; ++ ++const char *g_aw_pid_60_product[] = { ++ "aw87560", ++ "aw87561", ++ "aw87562", ++ "aw87501", ++ "aw87550", ++}; ++ ++static int aw87xxx_dev_get_chipid(struct aw_device *aw_dev); ++ ++/*************************************************************************** ++ * ++ * reading and writing of I2C bus ++ * ++ ***************************************************************************/ ++int aw87xxx_dev_i2c_write_byte(struct aw_device *aw_dev, ++ uint8_t reg_addr, uint8_t reg_data) ++{ ++ int ret = -1; ++ unsigned char cnt = 0; ++ ++ while (cnt < AW_I2C_RETRIES) { ++ ret = i2c_smbus_write_byte_data(aw_dev->i2c, reg_addr, reg_data); ++ if (ret < 0) ++ AW_DEV_LOGE(aw_dev->dev, "i2c_write cnt=%d error=%d", ++ cnt, ret); ++ else ++ break; ++ ++ cnt++; ++ msleep(AW_I2C_RETRY_DELAY); ++ } ++ ++ return ret; ++} ++ ++int aw87xxx_dev_i2c_read_byte(struct aw_device *aw_dev, ++ uint8_t reg_addr, uint8_t *reg_data) ++{ ++ int ret = -1; ++ unsigned char cnt = 0; ++ ++ while (cnt < AW_I2C_RETRIES) { ++ ret = i2c_smbus_read_byte_data(aw_dev->i2c, reg_addr); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw_dev->dev, "i2c_read cnt=%d error=%d", ++ cnt, ret); ++ } else { ++ *reg_data = ret; ++ break; ++ } ++ cnt++; ++ msleep(AW_I2C_RETRY_DELAY); ++ } ++ ++ return ret; ++} ++ ++int aw87xxx_dev_i2c_read_msg(struct aw_device *aw_dev, ++ uint8_t reg_addr, uint8_t *data_buf, uint32_t data_len) ++{ ++ int ret = -1; ++ ++ struct i2c_msg msg[] = { ++ [0] = { ++ .addr = aw_dev->i2c_addr, ++ .flags = 0, ++ .len = sizeof(uint8_t), ++ .buf = ®_addr, ++ }, ++ [1] = { ++ .addr = aw_dev->i2c_addr, ++ .flags = I2C_M_RD, ++ .len = data_len, ++ .buf = data_buf, ++ }, ++ }; ++ ++ ret = i2c_transfer(aw_dev->i2c->adapter, msg, ARRAY_SIZE(msg)); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw_dev->dev, "transfer failed"); ++ return ret; ++ } else if (ret != AW_I2C_READ_MSG_NUM) { ++ AW_DEV_LOGE(aw_dev->dev, "transfer failed(size error)"); ++ return -ENXIO; ++ } ++ ++ return 0; ++} ++ ++int aw87xxx_dev_i2c_write_bits(struct aw_device *aw_dev, ++ uint8_t reg_addr, uint8_t mask, uint8_t reg_data) ++{ ++ int ret = -1; ++ unsigned char reg_val = 0; ++ ++ ret = aw87xxx_dev_i2c_read_byte(aw_dev, reg_addr, ®_val); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw_dev->dev, "i2c read error, ret=%d", ret); ++ return ret; ++ } ++ reg_val &= mask; ++ reg_val |= (reg_data & (~mask)); ++ ret = aw87xxx_dev_i2c_write_byte(aw_dev, reg_addr, reg_val); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw_dev->dev, "i2c write error, ret=%d", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/************************************************************************ ++ * ++ * aw87xxx device update profile data to registers ++ * ++ ************************************************************************/ ++static int aw87xxx_dev_reg_update(struct aw_device *aw_dev, ++ struct aw_data_container *profile_data) ++{ ++ int i = 0; ++ int ret = -1; ++ ++ if (profile_data == NULL) ++ return -EINVAL; ++ ++ if (aw_dev->hwen_status == AW_DEV_HWEN_OFF) { ++ AW_DEV_LOGE(aw_dev->dev, "dev is pwr_off,can not update reg"); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < profile_data->len; i = i + 2) { ++ AW_DEV_LOGI(aw_dev->dev, "reg=0x%02x, val = 0x%02x", ++ profile_data->data[i], profile_data->data[i + 1]); ++ ++ ret = aw87xxx_dev_i2c_write_byte(aw_dev, profile_data->data[i], ++ profile_data->data[i + 1]); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void aw87xxx_dev_reg_mute_bits_set(struct aw_device *aw_dev, ++ uint8_t *reg_val, bool enable) ++{ ++ if (enable) { ++ *reg_val &= aw_dev->mute_desc.mask; ++ *reg_val |= aw_dev->mute_desc.enable; ++ } else { ++ *reg_val &= aw_dev->mute_desc.mask; ++ *reg_val |= aw_dev->mute_desc.disable; ++ } ++} ++ ++static int aw87xxx_dev_reg_update_mute(struct aw_device *aw_dev, ++ struct aw_data_container *profile_data) ++{ ++ int i = 0; ++ int ret = -1; ++ uint8_t reg_val = 0; ++ ++ if (profile_data == NULL) ++ return -EINVAL; ++ ++ if (aw_dev->hwen_status == AW_DEV_HWEN_OFF) { ++ AW_DEV_LOGE(aw_dev->dev, "hwen is off,can not update reg"); ++ return -EINVAL; ++ } ++ ++ if (aw_dev->mute_desc.mask == AW_DEV_REG_INVALID_MASK) { ++ AW_DEV_LOGE(aw_dev->dev, "mute ctrl mask invalid"); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < profile_data->len; i = i + 2) { ++ AW_DEV_LOGI(aw_dev->dev, "reg=0x%02x, val = 0x%02x", ++ profile_data->data[i], profile_data->data[i + 1]); ++ ++ reg_val = profile_data->data[i + 1]; ++ if (profile_data->data[i] == aw_dev->mute_desc.addr) { ++ aw87xxx_dev_reg_mute_bits_set(aw_dev, ®_val, true); ++ AW_DEV_LOGD(aw_dev->dev, "change mute_mask, val = 0x%02x", ++ reg_val); ++ } ++ ++ ret = aw87xxx_dev_i2c_write_byte(aw_dev, profile_data->data[i], reg_val); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/************************************************************************ ++ * ++ * aw87xxx device hadware and soft contols ++ * ++ ************************************************************************/ ++static bool aw87xxx_dev_gpio_is_valid(struct aw_device *aw_dev) ++{ ++ if (gpio_is_valid(aw_dev->rst_gpio)) ++ return true; ++ else ++ return false; ++} ++ ++void aw87xxx_dev_hw_pwr_ctrl(struct aw_device *aw_dev, bool enable) ++{ ++ if (aw_dev->hwen_status == AW_DEV_HWEN_INVALID) { ++ AW_DEV_LOGD(aw_dev->dev, "product not have reset-pin,hardware pwd control invalid"); ++ return; ++ } ++ if (enable) { ++ if (aw87xxx_dev_gpio_is_valid(aw_dev)) { ++ gpio_set_value_cansleep(aw_dev->rst_gpio, AW_GPIO_LOW_LEVEL); ++ mdelay(2); ++ gpio_set_value_cansleep(aw_dev->rst_gpio, AW_GPIO_HIGHT_LEVEL); ++ mdelay(2); ++ aw_dev->hwen_status = AW_DEV_HWEN_ON; ++ AW_DEV_LOGI(aw_dev->dev, "hw power on"); ++ } else { ++ AW_DEV_LOGI(aw_dev->dev, "hw already power on"); ++ } ++ } else { ++ if (aw87xxx_dev_gpio_is_valid(aw_dev)) { ++ gpio_set_value_cansleep(aw_dev->rst_gpio, AW_GPIO_LOW_LEVEL); ++ mdelay(2); ++ aw_dev->hwen_status = AW_DEV_HWEN_OFF; ++ AW_DEV_LOGI(aw_dev->dev, "hw power off"); ++ } else { ++ AW_DEV_LOGI(aw_dev->dev, "hw already power off"); ++ } ++ } ++} ++ ++static int aw87xxx_dev_mute_ctrl(struct aw_device *aw_dev, bool enable) ++{ ++ int ret = 0; ++ ++ if (enable) { ++ ret = aw87xxx_dev_i2c_write_bits(aw_dev, aw_dev->mute_desc.addr, ++ aw_dev->mute_desc.mask, aw_dev->mute_desc.enable); ++ if (ret < 0) ++ return ret; ++ AW_DEV_LOGI(aw_dev->dev, "set mute down"); ++ } else { ++ ret = aw87xxx_dev_i2c_write_bits(aw_dev, aw_dev->mute_desc.addr, ++ aw_dev->mute_desc.mask, aw_dev->mute_desc.disable); ++ if (ret < 0) ++ return ret; ++ AW_DEV_LOGI(aw_dev->dev, "close mute down"); ++ } ++ ++ return 0; ++} ++ ++void aw87xxx_dev_soft_reset(struct aw_device *aw_dev) ++{ ++ int i = 0; ++ int ret = -1; ++ struct aw_soft_rst_desc *soft_rst = &aw_dev->soft_rst_desc; ++ ++ AW_DEV_LOGD(aw_dev->dev, "enter"); ++ ++ if (aw_dev->hwen_status == AW_DEV_HWEN_OFF) { ++ AW_DEV_LOGE(aw_dev->dev, "hw is off,can not softrst"); ++ return; ++ } ++ ++ if (aw_dev->soft_rst_enable == AW_DEV_SOFT_RST_DISENABLE) { ++ AW_DEV_LOGD(aw_dev->dev, "softrst is disenable"); ++ return; ++ } ++ ++ if (soft_rst->access == NULL || soft_rst->len == 0) { ++ AW_DEV_LOGE(aw_dev->dev, "softrst_info not init"); ++ return; ++ } ++ ++ if (soft_rst->len % 2) { ++ AW_DEV_LOGE(aw_dev->dev, "softrst data_len[%d] is odd number,data not available", ++ aw_dev->soft_rst_desc.len); ++ return; ++ } ++ ++ for (i = 0; i < soft_rst->len; i += 2) { ++ AW_DEV_LOGD(aw_dev->dev, "softrst_reg=0x%02x, val = 0x%02x", ++ soft_rst->access[i], soft_rst->access[i + 1]); ++ ++ ret = aw87xxx_dev_i2c_write_byte(aw_dev, soft_rst->access[i], ++ soft_rst->access[i + 1]); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw_dev->dev, "write failed,ret = %d,cnt=%d", ++ ret, i); ++ return; ++ } ++ } ++ AW_DEV_LOGD(aw_dev->dev, "down"); ++} ++ ++ ++int aw87xxx_dev_default_pwr_off(struct aw_device *aw_dev, ++ struct aw_data_container *profile_data) ++{ ++ int ret = 0; ++ ++ AW_DEV_LOGD(aw_dev->dev, "enter"); ++ if (aw_dev->hwen_status == AW_DEV_HWEN_OFF) { ++ AW_DEV_LOGE(aw_dev->dev, "hwen is already off"); ++ return 0; ++ } ++ ++ if (aw_dev->soft_off_enable && profile_data) { ++ ret = aw87xxx_dev_reg_update(aw_dev, profile_data); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw_dev->dev, "update profile[Off] fw config failed"); ++ goto reg_off_update_failed; ++ } ++ } ++ ++ aw87xxx_dev_hw_pwr_ctrl(aw_dev, false); ++ AW_DEV_LOGD(aw_dev->dev, "down"); ++ return 0; ++ ++reg_off_update_failed: ++ aw87xxx_dev_hw_pwr_ctrl(aw_dev, false); ++ return ret; ++} ++ ++ ++/************************************************************************ ++ * ++ * aw87xxx device power on process function ++ * ++ ************************************************************************/ ++ ++int aw87xxx_dev_default_pwr_on(struct aw_device *aw_dev, ++ struct aw_data_container *profile_data) ++{ ++ int ret = 0; ++ ++ /*hw power on*/ ++ aw87xxx_dev_hw_pwr_ctrl(aw_dev, true); ++ ++ ret = aw87xxx_dev_reg_update(aw_dev, profile_data); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++/**************************************************************************** ++ * ++ * aw87xxx chip esd status check ++ * ++ ****************************************************************************/ ++int aw87xxx_dev_esd_reg_status_check(struct aw_device *aw_dev) ++{ ++ int ret; ++ unsigned char reg_val = 0; ++ struct aw_esd_check_desc *esd_desc = &aw_dev->esd_desc; ++ ++ AW_DEV_LOGD(aw_dev->dev, "enter"); ++ ++ if (!esd_desc->first_update_reg_addr) { ++ AW_DEV_LOGE(aw_dev->dev, "esd check info if not init,please check"); ++ return -EINVAL; ++ } ++ ++ ret = aw87xxx_dev_i2c_read_byte(aw_dev, esd_desc->first_update_reg_addr, ++ ®_val); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw_dev->dev, "read reg 0x%02x failed", ++ esd_desc->first_update_reg_addr); ++ return ret; ++ } ++ ++ AW_DEV_LOGD(aw_dev->dev, "0x%02x:default val=0x%02x real val=0x%02x", ++ esd_desc->first_update_reg_addr, ++ esd_desc->first_update_reg_val, reg_val); ++ ++ if (reg_val == esd_desc->first_update_reg_val) { ++ AW_DEV_LOGE(aw_dev->dev, "reg status check failed"); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++int aw87xxx_dev_check_reg_is_rec_mode(struct aw_device *aw_dev) ++{ ++ int ret; ++ unsigned char reg_val = 0; ++ struct aw_rec_mode_desc *rec_desc = &aw_dev->rec_desc; ++ ++ if (!rec_desc->addr) { ++ AW_DEV_LOGE(aw_dev->dev, "rec check info if not init,please check"); ++ return -EINVAL; ++ } ++ ++ ret = aw87xxx_dev_i2c_read_byte(aw_dev, rec_desc->addr, ®_val); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw_dev->dev, "read reg 0x%02x failed", ++ rec_desc->addr); ++ return ret; ++ } ++ ++ if (rec_desc->enable) { ++ if (reg_val & ~(rec_desc->mask)) { ++ AW_DEV_LOGI(aw_dev->dev, "reg status is receiver mode"); ++ aw_dev->is_rec_mode = AW_IS_REC_MODE; ++ } else { ++ aw_dev->is_rec_mode = AW_NOT_REC_MODE; ++ } ++ } else { ++ if (!(reg_val & ~(rec_desc->mask))) { ++ AW_DEV_LOGI(aw_dev->dev, "reg status is receiver mode"); ++ aw_dev->is_rec_mode = AW_IS_REC_MODE; ++ } else { ++ aw_dev->is_rec_mode = AW_NOT_REC_MODE; ++ } ++ } ++ return 0; ++} ++ ++ ++/**************************************************************************** ++ * ++ * aw87xxx product attributes init info ++ * ++ ****************************************************************************/ ++ ++/********************** aw87xxx_pid_9A attributes ***************************/ ++ ++static int aw_dev_pid_9b_reg_update(struct aw_device *aw_dev, ++ struct aw_data_container *profile_data) ++{ ++ int i = 0; ++ int ret = -1; ++ uint8_t reg_val = 0; ++ ++ if (profile_data == NULL) ++ return -EINVAL; ++ ++ if (aw_dev->hwen_status == AW_DEV_HWEN_OFF) { ++ AW_DEV_LOGE(aw_dev->dev, "dev is pwr_off,can not update reg"); ++ return -EINVAL; ++ } ++ ++ if (profile_data->len != AW_PID_9B_BIN_REG_CFG_COUNT) { ++ AW_DEV_LOGE(aw_dev->dev, "reg_config count of bin is error,can not update reg"); ++ return -EINVAL; ++ } ++ ret = aw87xxx_dev_i2c_write_byte(aw_dev, AW87XXX_PID_9B_ENCRYPTION_REG, ++ AW87XXX_PID_9B_ENCRYPTION_BOOST_OUTPUT_SET); ++ if (ret < 0) ++ return ret; ++ ++ for (i = 1; i < AW_PID_9B_BIN_REG_CFG_COUNT; i++) { ++ AW_DEV_LOGI(aw_dev->dev, "reg=0x%02x, val = 0x%02x", ++ i, profile_data->data[i]); ++ reg_val = profile_data->data[i]; ++ if (i == AW87XXX_PID_9B_SYSCTRL_REG) { ++ aw87xxx_dev_reg_mute_bits_set(aw_dev, ®_val, true); ++ AW_DEV_LOGD(aw_dev->dev, "change mute_mask, val = 0x%02x", ++ reg_val); ++ } ++ ++ ret = aw87xxx_dev_i2c_write_byte(aw_dev, i, reg_val); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int aw_dev_pid_9b_pwr_on(struct aw_device *aw_dev, struct aw_data_container *data) ++{ ++ int ret = 0; ++ ++ /*hw power on*/ ++ aw87xxx_dev_hw_pwr_ctrl(aw_dev, true); ++ ++ /* open the mute */ ++ ret = aw87xxx_dev_mute_ctrl(aw_dev, true); ++ if (ret < 0) ++ return ret; ++ ++ /* Update scene parameters in mute mode */ ++ ret = aw_dev_pid_9b_reg_update(aw_dev, data); ++ if (ret < 0) ++ return ret; ++ ++ /* close the mute */ ++ ret = aw87xxx_dev_mute_ctrl(aw_dev, false); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static void aw_dev_pid_9b_init(struct aw_device *aw_dev) ++{ ++ /* Product register permission info */ ++ aw_dev->reg_max_addr = AW87XXX_PID_9B_REG_MAX; ++ aw_dev->reg_access = aw87xxx_pid_9b_reg_access; ++ ++ aw_dev->mute_desc.addr = AW87XXX_PID_9B_SYSCTRL_REG; ++ aw_dev->mute_desc.mask = AW87XXX_PID_9B_REG_EN_SW_MASK; ++ aw_dev->mute_desc.enable = AW87XXX_PID_9B_REG_EN_SW_DISABLE_VALUE; ++ aw_dev->mute_desc.disable = AW87XXX_PID_9B_REG_EN_SW_ENABLE_VALUE; ++ aw_dev->ops.pwr_on_func = aw_dev_pid_9b_pwr_on; ++ ++ /* software reset control info */ ++ aw_dev->soft_rst_desc.len = sizeof(aw87xxx_pid_9b_softrst_access); ++ aw_dev->soft_rst_desc.access = aw87xxx_pid_9b_softrst_access; ++ aw_dev->soft_rst_enable = AW_DEV_SOFT_RST_ENABLE; ++ ++ /* Whether to allow register operation to power off */ ++ aw_dev->soft_off_enable = AW_DEV_SOFT_OFF_DISENABLE; ++ ++ aw_dev->product_tab = g_aw_pid_9b_product; ++ aw_dev->product_cnt = AW87XXX_PID_9B_PRODUCT_MAX; ++ ++ aw_dev->rec_desc.addr = AW87XXX_PID_9B_SYSCTRL_REG; ++ aw_dev->rec_desc.disable = AW87XXX_PID_9B_SPK_MODE_ENABLE; ++ aw_dev->rec_desc.enable = AW87XXX_PID_9B_SPK_MODE_DISABLE; ++ aw_dev->rec_desc.mask = AW87XXX_PID_9B_SPK_MODE_MASK; ++ ++ /* esd reg info */ ++ aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_9B_SYSCTRL_REG; ++ aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_9B_SYSCTRL_DEFAULT; ++} ++ ++static int aw_dev_pid_9a_init(struct aw_device *aw_dev) ++{ ++ int ret = 0; ++ ++ ret = aw87xxx_dev_i2c_write_byte(aw_dev, AW87XXX_PID_9B_ENCRYPTION_REG, ++ AW87XXX_PID_9B_ENCRYPTION_BOOST_OUTPUT_SET); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw_dev->dev, "write 0x64=0x2C error"); ++ return -EINVAL; ++ } ++ ++ ret = aw87xxx_dev_get_chipid(aw_dev); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw_dev->dev, "read chipid is failed,ret=%d", ret); ++ return ret; ++ } ++ ++ if (aw_dev->chipid == AW_DEV_CHIPID_9B) { ++ AW_DEV_LOGI(aw_dev->dev, "product is pid_9B class"); ++ aw_dev_pid_9b_init(aw_dev); ++ } else { ++ AW_DEV_LOGE(aw_dev->dev, "product is not pid_9B class,not support"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/********************** aw87xxx_pid_9b attributes end ***********************/ ++ ++/********************** aw87xxx_pid_18 attributes ***************************/ ++static int aw_dev_pid_18_pwr_on(struct aw_device *aw_dev, struct aw_data_container *data) ++{ ++ int ret = 0; ++ ++ /*hw power on*/ ++ aw87xxx_dev_hw_pwr_ctrl(aw_dev, true); ++ ++ /* open the mute */ ++ ret = aw87xxx_dev_mute_ctrl(aw_dev, true); ++ if (ret < 0) ++ return ret; ++ ++ /* Update scene parameters in mute mode */ ++ ret = aw87xxx_dev_reg_update_mute(aw_dev, data); ++ if (ret < 0) ++ return ret; ++ ++ /* close the mute */ ++ ret = aw87xxx_dev_mute_ctrl(aw_dev, false); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static void aw_dev_chipid_18_init(struct aw_device *aw_dev) ++{ ++ /* Product register permission info */ ++ aw_dev->reg_max_addr = AW87XXX_PID_18_REG_MAX; ++ aw_dev->reg_access = aw87xxx_pid_18_reg_access; ++ ++ aw_dev->mute_desc.addr = AW87XXX_PID_18_SYSCTRL_REG; ++ aw_dev->mute_desc.mask = AW87XXX_PID_18_REG_EN_SW_MASK; ++ aw_dev->mute_desc.enable = AW87XXX_PID_18_REG_EN_SW_DISABLE_VALUE; ++ aw_dev->mute_desc.disable = AW87XXX_PID_18_REG_EN_SW_ENABLE_VALUE; ++ aw_dev->ops.pwr_on_func = aw_dev_pid_18_pwr_on; ++ ++ /* software reset control info */ ++ aw_dev->soft_rst_desc.len = sizeof(aw87xxx_pid_18_softrst_access); ++ aw_dev->soft_rst_desc.access = aw87xxx_pid_18_softrst_access; ++ aw_dev->soft_rst_enable = AW_DEV_SOFT_RST_ENABLE; ++ ++ /* Whether to allow register operation to power off */ ++ aw_dev->soft_off_enable = AW_DEV_SOFT_OFF_ENABLE; ++ ++ aw_dev->product_tab = g_aw_pid_18_product; ++ aw_dev->product_cnt = AW87XXX_PID_18_PRODUCT_MAX; ++ ++ aw_dev->rec_desc.addr = AW87XXX_PID_18_SYSCTRL_REG; ++ aw_dev->rec_desc.disable = AW87XXX_PID_18_REG_REC_MODE_DISABLE; ++ aw_dev->rec_desc.enable = AW87XXX_PID_18_REG_REC_MODE_ENABLE; ++ aw_dev->rec_desc.mask = AW87XXX_PID_18_REG_REC_MODE_MASK; ++ ++ /* esd reg info */ ++ aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_18_CLASSD_REG; ++ aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_18_CLASSD_DEFAULT; ++} ++/********************** aw87xxx_pid_18 attributes end ***********************/ ++ ++/********************** aw87xxx_pid_39 attributes ***************************/ ++static void aw_dev_chipid_39_init(struct aw_device *aw_dev) ++{ ++ /* Product register permission info */ ++ aw_dev->reg_max_addr = AW87XXX_PID_39_REG_MAX; ++ aw_dev->reg_access = aw87xxx_pid_39_reg_access; ++ ++ /* software reset control info */ ++ aw_dev->soft_rst_desc.len = sizeof(aw87xxx_pid_39_softrst_access); ++ aw_dev->soft_rst_desc.access = aw87xxx_pid_39_softrst_access; ++ aw_dev->soft_rst_enable = AW_DEV_SOFT_RST_ENABLE; ++ ++ /* Whether to allow register operation to power off */ ++ aw_dev->soft_off_enable = AW_DEV_SOFT_OFF_ENABLE; ++ ++ aw_dev->product_tab = g_aw_pid_39_product; ++ aw_dev->product_cnt = AW87XXX_PID_39_PRODUCT_MAX; ++ ++ aw_dev->rec_desc.addr = AW87XXX_PID_39_REG_MODECTRL; ++ aw_dev->rec_desc.disable = AW87XXX_PID_39_REC_MODE_DISABLE; ++ aw_dev->rec_desc.enable = AW87XXX_PID_39_REC_MODE_ENABLE; ++ aw_dev->rec_desc.mask = AW87XXX_PID_39_REC_MODE_MASK; ++ ++ /* esd reg info */ ++ aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_39_REG_MODECTRL; ++ aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_39_MODECTRL_DEFAULT; ++} ++/********************* aw87xxx_pid_39 attributes end *************************/ ++ ++ ++/********************* aw87xxx_pid_59_5x9 attributes *************************/ ++static void aw_dev_chipid_59_5x9_init(struct aw_device *aw_dev) ++{ ++ /* Product register permission info */ ++ aw_dev->reg_max_addr = AW87XXX_PID_59_5X9_REG_MAX; ++ aw_dev->reg_access = aw87xxx_pid_59_5x9_reg_access; ++ ++ /* software reset control info */ ++ aw_dev->soft_rst_desc.len = sizeof(aw87xxx_pid_59_5x9_softrst_access); ++ aw_dev->soft_rst_desc.access = aw87xxx_pid_59_5x9_softrst_access; ++ aw_dev->soft_rst_enable = AW_DEV_SOFT_RST_ENABLE; ++ ++ /* Whether to allow register operation to power off */ ++ aw_dev->soft_off_enable = AW_DEV_SOFT_OFF_ENABLE; ++ ++ aw_dev->product_tab = g_aw_pid_59_5x9_product; ++ aw_dev->product_cnt = AW87XXX_PID_59_5X9_PRODUCT_MAX; ++ ++ aw_dev->rec_desc.addr = AW87XXX_PID_59_5X9_REG_SYSCTRL; ++ aw_dev->rec_desc.disable = AW87XXX_PID_59_5X9_REC_MODE_DISABLE; ++ aw_dev->rec_desc.enable = AW87XXX_PID_59_5X9_REC_MODE_ENABLE; ++ aw_dev->rec_desc.mask = AW87XXX_PID_59_5X9_REC_MODE_MASK; ++ ++ /* esd reg info */ ++ aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_59_5X9_REG_ENCR; ++ aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_59_5X9_ENCRY_DEFAULT; ++} ++/******************* aw87xxx_pid_59_5x9 attributes end ***********************/ ++ ++/********************* aw87xxx_pid_59_3x9 attributes *************************/ ++static void aw_dev_chipid_59_3x9_init(struct aw_device *aw_dev) ++{ ++ /* Product register permission info */ ++ aw_dev->reg_max_addr = AW87XXX_PID_59_3X9_REG_MAX; ++ aw_dev->reg_access = aw87xxx_pid_59_3x9_reg_access; ++ ++ /* software reset control info */ ++ aw_dev->soft_rst_desc.len = sizeof(aw87xxx_pid_59_3x9_softrst_access); ++ aw_dev->soft_rst_desc.access = aw87xxx_pid_59_3x9_softrst_access; ++ aw_dev->soft_rst_enable = AW_DEV_SOFT_RST_ENABLE; ++ ++ /* Whether to allow register operation to power off */ ++ aw_dev->soft_off_enable = AW_DEV_SOFT_OFF_ENABLE; ++ ++ aw_dev->product_tab = g_aw_pid_59_3x9_product; ++ aw_dev->product_cnt = AW87XXX_PID_59_3X9_PRODUCT_MAX; ++ ++ aw_dev->rec_desc.addr = AW87XXX_PID_59_3X9_REG_MDCRTL; ++ aw_dev->rec_desc.disable = AW87XXX_PID_59_3X9_SPK_MODE_ENABLE; ++ aw_dev->rec_desc.enable = AW87XXX_PID_59_3X9_SPK_MODE_DISABLE; ++ aw_dev->rec_desc.mask = AW87XXX_PID_59_3X9_SPK_MODE_MASK; ++ ++ /* esd reg info */ ++ aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_59_3X9_REG_ENCR; ++ aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_59_3X9_ENCR_DEFAULT; ++} ++/******************* aw87xxx_pid_59_3x9 attributes end ***********************/ ++ ++/********************** aw87xxx_pid_5a attributes ****************************/ ++static void aw_dev_chipid_5a_init(struct aw_device *aw_dev) ++{ ++ /* Product register permission info */ ++ aw_dev->reg_max_addr = AW87XXX_PID_5A_REG_MAX; ++ aw_dev->reg_access = aw87xxx_pid_5a_reg_access; ++ ++ /* software reset control info */ ++ aw_dev->soft_rst_desc.len = sizeof(aw87xxx_pid_5a_softrst_access); ++ aw_dev->soft_rst_desc.access = aw87xxx_pid_5a_softrst_access; ++ aw_dev->soft_rst_enable = AW_DEV_SOFT_RST_ENABLE; ++ ++ /* Whether to allow register operation to power off */ ++ aw_dev->soft_off_enable = AW_DEV_SOFT_OFF_ENABLE; ++ ++ aw_dev->product_tab = g_aw_pid_5a_product; ++ aw_dev->product_cnt = AW87XXX_PID_5A_PRODUCT_MAX; ++ ++ aw_dev->rec_desc.addr = AW87XXX_PID_5A_REG_SYSCTRL_REG; ++ aw_dev->rec_desc.disable = AW87XXX_PID_5A_REG_RCV_MODE_DISABLE; ++ aw_dev->rec_desc.enable = AW87XXX_PID_5A_REG_RCV_MODE_ENABLE; ++ aw_dev->rec_desc.mask = AW87XXX_PID_5A_REG_RCV_MODE_MASK; ++ ++ /* esd reg info */ ++ aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_5A_REG_DFT3R_REG; ++ aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_5A_DFT3R_DEFAULT; ++} ++/********************** aw87xxx_pid_5a attributes end ************************/ ++ ++/********************** aw87xxx_pid_76 attributes ****************************/ ++static void aw_dev_chipid_76_init(struct aw_device *aw_dev) ++{ ++ /* Product register permission info */ ++ aw_dev->reg_max_addr = AW87XXX_PID_76_REG_MAX; ++ aw_dev->reg_access = aw87xxx_pid_76_reg_access; ++ ++ /* software reset control info */ ++ aw_dev->soft_rst_desc.len = sizeof(aw87xxx_pid_76_softrst_access); ++ aw_dev->soft_rst_desc.access = aw87xxx_pid_76_softrst_access; ++ aw_dev->soft_rst_enable = AW_DEV_SOFT_RST_ENABLE; ++ ++ /* software power off control info */ ++ aw_dev->soft_off_enable = AW_DEV_SOFT_OFF_ENABLE; ++ ++ aw_dev->product_tab = g_aw_pid_76_product; ++ aw_dev->product_cnt = AW87XXX_PID_76_PROFUCT_MAX; ++ ++ aw_dev->rec_desc.addr = AW87XXX_PID_76_MDCTRL_REG; ++ aw_dev->rec_desc.disable = AW87XXX_PID_76_EN_SPK_ENABLE; ++ aw_dev->rec_desc.enable = AW87XXX_PID_76_EN_SPK_DISABLE; ++ aw_dev->rec_desc.mask = AW87XXX_PID_76_EN_SPK_MASK; ++ ++ /* esd reg info */ ++ aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_76_DFT_ADP1_REG; ++ aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_76_DFT_ADP1_CHECK; ++} ++/********************** aw87xxx_pid_76 attributes end ************************/ ++ ++/********************** aw87xxx_pid_60 attributes ****************************/ ++static void aw_dev_chipid_60_init(struct aw_device *aw_dev) ++{ ++ /* Product register permission info */ ++ aw_dev->reg_max_addr = AW87XXX_PID_60_REG_MAX; ++ aw_dev->reg_access = aw87xxx_pid_60_reg_access; ++ ++ /* software reset control info */ ++ aw_dev->soft_rst_desc.len = sizeof(aw87xxx_pid_60_softrst_access); ++ aw_dev->soft_rst_desc.access = aw87xxx_pid_60_softrst_access; ++ aw_dev->soft_rst_enable = AW_DEV_SOFT_RST_ENABLE; ++ ++ /* software power off control info */ ++ aw_dev->soft_off_enable = AW_DEV_SOFT_OFF_ENABLE; ++ ++ aw_dev->product_tab = g_aw_pid_60_product; ++ aw_dev->product_cnt = AW87XXX_PID_60_PROFUCT_MAX; ++ ++ aw_dev->rec_desc.addr = AW87XXX_PID_60_SYSCTRL_REG; ++ aw_dev->rec_desc.disable = AW87XXX_PID_60_RCV_MODE_DISABLE; ++ aw_dev->rec_desc.enable = AW87XXX_PID_60_RCV_MODE_ENABLE; ++ aw_dev->rec_desc.mask = AW87XXX_PID_60_RCV_MODE_MASK; ++ ++ /* esd reg info */ ++ aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_60_NG3_REG; ++ aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_60_ESD_REG_VAL; ++} ++/********************** aw87xxx_pid_60 attributes end ************************/ ++ ++static int aw_dev_chip_init(struct aw_device *aw_dev) ++{ ++ int ret = 0; ++ ++ /*get info by chipid*/ ++ switch (aw_dev->chipid) { ++ case AW_DEV_CHIPID_9A: ++ ret = aw_dev_pid_9a_init(aw_dev); ++ if (ret < 0) ++ AW_DEV_LOGE(aw_dev->dev, "product is pid_9B init failed"); ++ break; ++ case AW_DEV_CHIPID_9B: ++ aw_dev_pid_9b_init(aw_dev); ++ AW_DEV_LOGI(aw_dev->dev, "product is pid_9B class"); ++ break; ++ case AW_DEV_CHIPID_18: ++ aw_dev_chipid_18_init(aw_dev); ++ AW_DEV_LOGI(aw_dev->dev, "product is pid_18 class"); ++ break; ++ case AW_DEV_CHIPID_39: ++ aw_dev_chipid_39_init(aw_dev); ++ AW_DEV_LOGI(aw_dev->dev, "product is pid_39 class"); ++ break; ++ case AW_DEV_CHIPID_59: ++ if (aw87xxx_dev_gpio_is_valid(aw_dev)) { ++ aw_dev_chipid_59_5x9_init(aw_dev); ++ AW_DEV_LOGI(aw_dev->dev, "product is pid_59_5x9 class"); ++ } else { ++ aw_dev_chipid_59_3x9_init(aw_dev); ++ AW_DEV_LOGI(aw_dev->dev, "product is pid_59_3x9 class"); ++ } ++ break; ++ case AW_DEV_CHIPID_5A: ++ aw_dev_chipid_5a_init(aw_dev); ++ AW_DEV_LOGI(aw_dev->dev, "product is pid_5A class"); ++ break; ++ case AW_DEV_CHIPID_76: ++ aw_dev_chipid_76_init(aw_dev); ++ AW_DEV_LOGI(aw_dev->dev, "product is pid_76 class"); ++ break; ++ case AW_DEV_CHIPID_60: ++ aw_dev_chipid_60_init(aw_dev); ++ AW_DEV_LOGI(aw_dev->dev, "product is pid_60 class"); ++ break; ++ default: ++ AW_DEV_LOGE(aw_dev->dev, "unsupported device revision [0x%x]", ++ aw_dev->chipid); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int aw87xxx_dev_get_chipid(struct aw_device *aw_dev) ++{ ++ int ret = -1; ++ unsigned int cnt = 0; ++ unsigned char reg_val = 0; ++ ++ for (cnt = 0; cnt < AW_READ_CHIPID_RETRIES; cnt++) { ++ ret = aw87xxx_dev_i2c_read_byte(aw_dev, AW_DEV_REG_CHIPID, ®_val); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw_dev->dev, "[%d] read chip is failed, ret=%d", ++ cnt, ret); ++ continue; ++ } ++ break; ++ } ++ ++ ++ if (cnt == AW_READ_CHIPID_RETRIES) { ++ AW_DEV_LOGE(aw_dev->dev, "read chip is failed,cnt=%d", cnt); ++ return -EINVAL; ++ } ++ ++ AW_DEV_LOGI(aw_dev->dev, "read chipid[0x%x] succeed", reg_val); ++ aw_dev->chipid = reg_val; ++ ++ return 0; ++} ++ ++int aw87xxx_dev_init(struct aw_device *aw_dev) ++{ ++ int ret = -1; ++ ++ ret = aw87xxx_dev_get_chipid(aw_dev); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw_dev->dev, "read chipid is failed,ret=%d", ret); ++ return ret; ++ } ++ ++ ret = aw_dev_chip_init(aw_dev); ++ ++ return ret; ++} ++ ++ +diff --git a/sound/soc/codecs/aw87xxx/aw87xxx_device.h b/sound/soc/codecs/aw87xxx/aw87xxx_device.h +new file mode 100644 +index 000000000..b063218e3 +--- /dev/null ++++ b/sound/soc/codecs/aw87xxx/aw87xxx_device.h +@@ -0,0 +1,149 @@ ++#ifndef __AW87XXX_DEVICE_H__ ++#define __AW87XXX_DEVICE_H__ ++#include ++#include ++#include ++#include ++#include "aw87xxx_acf_bin.h" ++ ++#define AW87XXX_PID_9B_PRODUCT_MAX (1) ++#define AW87XXX_PID_18_PRODUCT_MAX (1) ++#define AW87XXX_PID_39_PRODUCT_MAX (3) ++#define AW87XXX_PID_59_3X9_PRODUCT_MAX (2) ++#define AW87XXX_PID_59_5X9_PRODUCT_MAX (4) ++#define AW87XXX_PID_5A_PRODUCT_MAX (5) ++#define AW87XXX_PID_76_PROFUCT_MAX (4) ++#define AW87XXX_PID_60_PROFUCT_MAX (5) ++#define AW_PRODUCT_NAME_LEN (8) ++ ++#define AW_GPIO_HIGHT_LEVEL (1) ++#define AW_GPIO_LOW_LEVEL (0) ++ ++#define AW_I2C_RETRIES (5) ++#define AW_I2C_RETRY_DELAY (2) ++#define AW_I2C_READ_MSG_NUM (2) ++ ++#define AW_READ_CHIPID_RETRIES (5) ++#define AW_READ_CHIPID_RETRY_DELAY (2) ++#define AW_DEV_REG_CHIPID (0x00) ++ ++#define AW_DEV_REG_INVALID_MASK (0xff) ++ ++#define AW_NO_RESET_GPIO (-1) ++ ++#define AW_PID_9B_BIN_REG_CFG_COUNT (10) ++ ++/******************************************** ++ * ++ * aw87xxx devices attributes ++ * ++ *******************************************/ ++struct aw_device; ++ ++struct aw_device_ops { ++ int (*pwr_on_func)(struct aw_device *aw_dev, struct aw_data_container *data); ++ int (*pwr_off_func)(struct aw_device *aw_dev, struct aw_data_container *data); ++}; ++ ++enum aw_dev_chipid { ++ AW_DEV_CHIPID_18 = 0x18, ++ AW_DEV_CHIPID_39 = 0x39, ++ AW_DEV_CHIPID_59 = 0x59, ++ AW_DEV_CHIPID_69 = 0x69, ++ AW_DEV_CHIPID_5A = 0x5A, ++ AW_DEV_CHIPID_9A = 0x9A, ++ AW_DEV_CHIPID_9B = 0x9B, ++ AW_DEV_CHIPID_76 = 0x76, ++ AW_DEV_CHIPID_60 = 0x60, ++}; ++ ++enum aw_dev_hw_status { ++ AW_DEV_HWEN_OFF = 0, ++ AW_DEV_HWEN_ON, ++ AW_DEV_HWEN_INVALID, ++ AW_DEV_HWEN_STATUS_MAX, ++}; ++ ++enum aw_dev_soft_off_enable { ++ AW_DEV_SOFT_OFF_DISENABLE = 0, ++ AW_DEV_SOFT_OFF_ENABLE = 1, ++}; ++ ++enum aw_dev_soft_rst_enable { ++ AW_DEV_SOFT_RST_DISENABLE = 0, ++ AW_DEV_SOFT_RST_ENABLE = 1, ++}; ++ ++enum aw_reg_receiver_mode { ++ AW_NOT_REC_MODE = 0, ++ AW_IS_REC_MODE = 1, ++}; ++ ++struct aw_mute_desc { ++ uint8_t addr; ++ uint8_t enable; ++ uint8_t disable; ++ uint16_t mask; ++}; ++ ++struct aw_soft_rst_desc { ++ int len; ++ unsigned char *access; ++}; ++ ++struct aw_esd_check_desc { ++ uint8_t first_update_reg_addr; ++ uint8_t first_update_reg_val; ++}; ++ ++struct aw_rec_mode_desc { ++ uint8_t addr; ++ uint8_t enable; ++ uint8_t disable; ++ uint8_t mask; ++}; ++ ++struct aw_device { ++ uint8_t i2c_addr; ++ uint8_t chipid; ++ uint8_t soft_rst_enable; ++ uint8_t soft_off_enable; ++ uint8_t is_rec_mode; ++ int hwen_status; ++ int i2c_bus; ++ int rst_gpio; ++ int reg_max_addr; ++ int product_cnt; ++ const char **product_tab; ++ const unsigned char *reg_access; ++ ++ struct device *dev; ++ struct i2c_client *i2c; ++ struct aw_mute_desc mute_desc; ++ struct aw_soft_rst_desc soft_rst_desc; ++ struct aw_esd_check_desc esd_desc; ++ struct aw_rec_mode_desc rec_desc; ++ ++ struct aw_device_ops ops; ++}; ++ ++ ++int aw87xxx_dev_i2c_write_byte(struct aw_device *aw_dev, ++ uint8_t reg_addr, uint8_t reg_data); ++int aw87xxx_dev_i2c_read_byte(struct aw_device *aw_dev, ++ uint8_t reg_addr, uint8_t *reg_data); ++int aw87xxx_dev_i2c_read_msg(struct aw_device *aw_dev, ++ uint8_t reg_addr, uint8_t *data_buf, uint32_t data_len); ++int aw87xxx_dev_i2c_write_bits(struct aw_device *aw_dev, ++ uint8_t reg_addr, uint8_t mask, uint8_t reg_data); ++void aw87xxx_dev_soft_reset(struct aw_device *aw_dev); ++void aw87xxx_dev_hw_pwr_ctrl(struct aw_device *aw_dev, bool enable); ++int aw87xxx_dev_default_pwr_on(struct aw_device *aw_dev, ++ struct aw_data_container *profile_data); ++int aw87xxx_dev_default_pwr_off(struct aw_device *aw_dev, ++ struct aw_data_container *profile_data); ++int aw87xxx_dev_esd_reg_status_check(struct aw_device *aw_dev); ++int aw87xxx_dev_check_reg_is_rec_mode(struct aw_device *aw_dev); ++int aw87xxx_dev_init(struct aw_device *aw_dev); ++ ++#endif +diff --git a/sound/soc/codecs/aw87xxx/aw87xxx_dsp.c b/sound/soc/codecs/aw87xxx/aw87xxx_dsp.c +new file mode 100644 +index 000000000..bd9896cd1 +--- /dev/null ++++ b/sound/soc/codecs/aw87xxx/aw87xxx_dsp.c +@@ -0,0 +1,355 @@ ++/* ++ * aw87xxx_dsp.c ++ * ++ * Copyright (c) 2021 AWINIC Technology CO., LTD ++ * ++ * Author: Barry ++ * ++ * 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. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "aw87xxx_log.h" ++#include "aw87xxx_dsp.h" ++ ++static DEFINE_MUTEX(g_dsp_lock); ++static unsigned int g_spin_value = 0; ++ ++static int g_rx_topo_id = AW_RX_DEFAULT_TOPO_ID; ++static int g_rx_port_id = AW_RX_DEFAULT_PORT_ID; ++ ++#ifdef AW_MTK_OPEN_DSP_PLATFORM ++extern int mtk_spk_send_ipi_buf_to_dsp(void *data_buffer, ++ uint32_t data_size); ++extern int mtk_spk_recv_ipi_buf_from_dsp(int8_t *buffer, ++ int16_t size, uint32_t *buf_len); ++/* ++static int mtk_spk_send_ipi_buf_to_dsp(void *data_buffer, ++ uint32_t data_size) ++{ ++ AW_LOGI("enter"); ++ return 0; ++} ++ ++static int mtk_spk_recv_ipi_buf_from_dsp(int8_t *buffer, ++ int16_t size, uint32_t *buf_len) ++{ ++ AW_LOGI("enter"); ++ return 0; ++} ++*/ ++#elif defined AW_QCOM_OPEN_DSP_PLATFORM ++extern int afe_get_topology(int port_id); ++extern int aw_send_afe_cal_apr(uint32_t param_id, ++ void *buf, int cmd_size, bool write); ++/* ++static int afe_get_topology(int port_id) ++{ ++ return -EPERM; ++} ++ ++static int aw_send_afe_cal_apr(uint32_t param_id, ++ void *buf, int cmd_size, bool write) ++{ ++ AW_LOGI("enter, no define AWINIC_ADSP_ENABLE", __func__); ++ return 0; ++} ++*/ ++#endif ++ ++#ifdef AW_QCOM_OPEN_DSP_PLATFORM ++extern void aw_set_port_id(int rx_port_id); ++#else ++static void aw_set_port_id(int rx_port_id) ++{ ++ return; ++} ++#endif ++ ++uint8_t aw87xxx_dsp_isEnable(void) ++{ ++#if (defined AW_QCOM_OPEN_DSP_PLATFORM) || (defined AW_MTK_OPEN_DSP_PLATFORM) ++ return true; ++#else ++ return false; ++#endif ++} ++ ++/*****************mtk dsp communication function start**********************/ ++#ifdef AW_MTK_OPEN_DSP_PLATFORM ++static int aw_mtk_write_data_to_dsp(int32_t param_id, ++ void *data, int size) ++{ ++ int32_t *dsp_data = NULL; ++ mtk_dsp_hdr_t *hdr = NULL; ++ int ret; ++ ++ dsp_data = kzalloc(sizeof(mtk_dsp_hdr_t) + size, GFP_KERNEL); ++ if (!dsp_data) { ++ AW_LOGE("kzalloc dsp_msg error"); ++ return -ENOMEM; ++ } ++ ++ hdr = (mtk_dsp_hdr_t *)dsp_data; ++ hdr->type = DSP_MSG_TYPE_DATA; ++ hdr->opcode_id = param_id; ++ hdr->version = AW_DSP_MSG_HDR_VER; ++ ++ memcpy(((char *)dsp_data) + sizeof(mtk_dsp_hdr_t), ++ data, size); ++ ++ ret = mtk_spk_send_ipi_buf_to_dsp(dsp_data, ++ sizeof(mtk_dsp_hdr_t) + size); ++ if (ret < 0) { ++ AW_LOGE("write data failed"); ++ kfree(dsp_data); ++ dsp_data = NULL; ++ return ret; ++ } ++ ++ kfree(dsp_data); ++ dsp_data = NULL; ++ return 0; ++} ++ ++static int aw_mtk_read_data_from_dsp(int32_t param_id, void *data, ++ int data_size) ++{ ++ int ret; ++ mtk_dsp_hdr_t hdr; ++ ++ mutex_lock(&g_dsp_lock); ++ hdr.type = DSP_MSG_TYPE_CMD; ++ hdr.opcode_id = param_id; ++ hdr.version = AW_DSP_MSG_HDR_VER; ++ ++ ret = mtk_spk_send_ipi_buf_to_dsp(&hdr, sizeof(mtk_dsp_hdr_t)); ++ if (ret < 0) ++ goto failed; ++ ++ ret = mtk_spk_recv_ipi_buf_from_dsp(data, data_size, &data_size); ++ if (ret < 0) ++ goto failed; ++ ++ mutex_unlock(&g_dsp_lock); ++ return 0; ++ ++failed: ++ mutex_unlock(&g_dsp_lock); ++ return ret; ++} ++ ++#endif ++/********************mtk dsp communication function end***********************/ ++ ++/******************qcom dsp communication function start**********************/ ++#ifdef AW_QCOM_OPEN_DSP_PLATFORM ++static void aw_check_dsp_ready(void) ++{ ++ int ret; ++ ++ ret = afe_get_topology(g_rx_port_id); ++ AW_LOGD("topo_id 0x%x", ret); ++ ++ if (ret != g_rx_topo_id) ++ AW_LOGE("topo id 0x%x", ret); ++ ++} ++ ++static int aw_qcom_write_data_to_dsp(int32_t param_id, ++ void *data, int data_size) ++{ ++ int ret = 0; ++ ++ AW_LOGI("enter"); ++ mutex_lock(&g_dsp_lock); ++ aw_check_dsp_ready(); ++ ret = aw_send_afe_cal_apr(param_id, data, ++ data_size, true); ++ mutex_unlock(&g_dsp_lock); ++ return ret; ++} ++ ++static int aw_qcom_read_data_from_dsp(int32_t param_id, ++ void *data, int data_size) ++{ ++ int ret = 0; ++ ++ AW_LOGI("enter"); ++ mutex_lock(&g_dsp_lock); ++ aw_check_dsp_ready(); ++ ret = aw_send_afe_cal_apr(param_id, data, ++ data_size, false); ++ mutex_unlock(&g_dsp_lock); ++ return ret; ++} ++ ++#endif ++/*****************qcom dsp communication function end*********************/ ++ ++/*****************read/write msg communication function*********************/ ++static int aw_write_data_to_dsp(int32_t param_id, void *data, int data_size) ++{ ++#if defined AW_QCOM_OPEN_DSP_PLATFORM ++ return aw_qcom_write_data_to_dsp(param_id, data, data_size); ++#elif defined AW_MTK_OPEN_DSP_PLATFORM ++ return aw_mtk_write_data_to_dsp(param_id, data, data_size); ++#else ++ return -EINVAL; ++#endif ++} ++ ++static int aw_read_data_from_dsp(int32_t param_id, void *data, int data_size) ++{ ++#if defined AW_QCOM_OPEN_DSP_PLATFORM ++ return aw_qcom_read_data_from_dsp(param_id, data, data_size); ++#elif defined AW_MTK_OPEN_DSP_PLATFORM ++ return aw_mtk_read_data_from_dsp(param_id, data, data_size); ++#else ++ return -EINVAL; ++#endif ++} ++ ++/***************read/write msg communication function end*******************/ ++ ++int aw87xxx_dsp_get_rx_module_enable(int *enable) ++{ ++ if (!enable) { ++ AW_LOGE("enable is NULL"); ++ return -EINVAL; ++ } ++ ++ return aw_read_data_from_dsp(AWDSP_RX_SET_ENABLE, ++ (void *)enable, sizeof(uint32_t)); ++} ++ ++int aw87xxx_dsp_set_rx_module_enable(int enable) ++{ ++ switch (enable) { ++ case AW_RX_MODULE_DISENABLE: ++ AW_LOGD("set enable=%d", enable); ++ break; ++ case AW_RX_MODULE_ENABLE: ++ AW_LOGD("set enable=%d", enable); ++ break; ++ default: ++ AW_LOGE("unsupport enable=%d", enable); ++ return -EINVAL; ++ } ++ ++ return aw_write_data_to_dsp(AWDSP_RX_SET_ENABLE, ++ &enable, sizeof(uint32_t)); ++} ++ ++ ++int aw87xxx_dsp_get_vmax(uint32_t *vmax, int dev_index) ++{ ++ int32_t param_id = 0; ++ ++ switch (dev_index % AW_DSP_CHANNEL_MAX) { ++ case AW_DSP_CHANNEL_0: ++ param_id = AWDSP_RX_VMAX_0; ++ break; ++ case AW_DSP_CHANNEL_1: ++ param_id = AWDSP_RX_VMAX_1; ++ break; ++ default: ++ AW_LOGE("algo only support double PA channel:%d unsupport", ++ dev_index); ++ return -EINVAL; ++ } ++ ++ return aw_read_data_from_dsp(param_id, ++ (void *)vmax, sizeof(uint32_t)); ++} ++ ++int aw87xxx_dsp_set_vmax(uint32_t vmax, int dev_index) ++{ ++ int32_t param_id = 0; ++ ++ switch (dev_index % AW_DSP_CHANNEL_MAX) { ++ case AW_DSP_CHANNEL_0: ++ param_id = AWDSP_RX_VMAX_0; ++ break; ++ case AW_DSP_CHANNEL_1: ++ param_id = AWDSP_RX_VMAX_1; ++ break; ++ default: ++ AW_LOGE("algo only support double PA channel:%d unsupport", ++ dev_index); ++ return -EINVAL; ++ } ++ ++ return aw_write_data_to_dsp(param_id, &vmax, sizeof(uint32_t)); ++} ++ ++int aw87xxx_dsp_set_spin(uint32_t ctrl_value) ++{ ++ int ret = 0; ++ ++ if (ctrl_value >= AW_SPIN_MAX) { ++ AW_LOGE("spin [%d] unsupported ", ctrl_value); ++ return -EINVAL; ++ } ++ ret = aw_write_data_to_dsp(AW_MSG_ID_SPIN, &ctrl_value, ++ sizeof(uint32_t)); ++ if (ret) { ++ AW_LOGE("spin [%d] set failed ", ctrl_value); ++ return ret; ++ } ++ ++ g_spin_value = ctrl_value; ++ return 0; ++} ++ ++int aw87xxx_dsp_get_spin(void) ++{ ++ return g_spin_value; ++} ++ ++int aw87xxx_spin_set_record_val(void) ++{ ++ AW_LOGD("record write spin enter"); ++ ++ return aw87xxx_dsp_set_spin(g_spin_value); ++} ++EXPORT_SYMBOL(aw87xxx_spin_set_record_val); ++ ++void aw87xxx_device_parse_topo_id_dt(struct aw_device *aw_dev) ++{ ++ int ret; ++ ++ ret = of_property_read_u32(aw_dev->dev->of_node, "aw-rx-topo-id", &g_rx_topo_id); ++ if (ret < 0) { ++ g_rx_topo_id = AW_RX_DEFAULT_TOPO_ID; ++ AW_DEV_LOGI(aw_dev->dev, "read aw-rx-topo-id failed,use default"); ++ } ++ ++ AW_DEV_LOGI(aw_dev->dev, "rx-topo-id: 0x%x", g_rx_topo_id); ++} ++ ++void aw87xxx_device_parse_port_id_dt(struct aw_device *aw_dev) ++{ ++ int ret; ++ ++ ret = of_property_read_u32(aw_dev->dev->of_node, "aw-rx-port-id", &g_rx_port_id); ++ if (ret < 0) { ++ g_rx_port_id = AW_RX_DEFAULT_PORT_ID; ++ AW_DEV_LOGI(aw_dev->dev, "read aw-rx-port-id failed,use default"); ++ } ++ ++ aw_set_port_id(g_rx_port_id); ++ AW_DEV_LOGI(aw_dev->dev, "rx-port-id: 0x%x", g_rx_port_id); ++} ++ +diff --git a/sound/soc/codecs/aw87xxx/aw87xxx_dsp.h b/sound/soc/codecs/aw87xxx/aw87xxx_dsp.h +new file mode 100644 +index 000000000..2668170f1 +--- /dev/null ++++ b/sound/soc/codecs/aw87xxx/aw87xxx_dsp.h +@@ -0,0 +1,65 @@ ++#ifndef __AW87XXX_DSP_H__ ++#define __AW87XXX_DSP_H__ ++ ++#include "aw87xxx_device.h" ++ ++/*#define AW_MTK_OPEN_DSP_PLATFORM*/ ++/*#define AW_QCOM_OPEN_DSP_PLATFORM*/ ++ ++/*Note: The pord_ID is configured according to different platforms*/ ++#define AW_DSP_SLEEP_TIME (10) ++ ++#define AW_DSP_MSG_HDR_VER (1) ++ ++#define AW_RX_DEFAULT_TOPO_ID (0x1000FF01) ++#define AW_RX_DEFAULT_PORT_ID (0x4000) ++ ++#define AWDSP_RX_SET_ENABLE (0x10013D11) ++#define AWDSP_RX_PARAMS (0x10013D12) ++#define AWDSP_RX_VMAX_0 (0X10013D17) ++#define AWDSP_RX_VMAX_1 (0X10013D18) ++#define AW_MSG_ID_SPIN (0x10013D2E) ++ ++enum { ++ AW_SPIN_0 = 0, ++ AW_SPIN_90, ++ AW_SPIN_180, ++ AW_SPIN_270, ++ AW_SPIN_MAX, ++}; ++ ++typedef struct mtk_dsp_msg_header { ++ int32_t type; ++ int32_t opcode_id; ++ int32_t version; ++ int32_t reserver[3]; ++} mtk_dsp_hdr_t; ++ ++enum aw_rx_module_enable { ++ AW_RX_MODULE_DISENABLE = 0, ++ AW_RX_MODULE_ENABLE, ++}; ++ ++enum aw_dsp_msg_type { ++ DSP_MSG_TYPE_DATA = 0, ++ DSP_MSG_TYPE_CMD = 1, ++}; ++ ++enum aw_dsp_channel { ++ AW_DSP_CHANNEL_0 = 0, ++ AW_DSP_CHANNEL_1, ++ AW_DSP_CHANNEL_MAX, ++}; ++ ++uint8_t aw87xxx_dsp_isEnable(void); ++int aw87xxx_dsp_get_rx_module_enable(int *enable); ++int aw87xxx_dsp_set_rx_module_enable(int enable); ++int aw87xxx_dsp_get_vmax(uint32_t *vmax, int channel); ++int aw87xxx_dsp_set_vmax(uint32_t vmax, int channel); ++int aw87xxx_dsp_set_spin(uint32_t ctrl_value); ++int aw87xxx_dsp_get_spin(void); ++int aw87xxx_spin_set_record_val(void); ++void aw87xxx_device_parse_port_id_dt(struct aw_device *aw_dev); ++void aw87xxx_device_parse_topo_id_dt(struct aw_device *aw_dev); ++ ++#endif +diff --git a/sound/soc/codecs/aw87xxx/aw87xxx_log.h b/sound/soc/codecs/aw87xxx/aw87xxx_log.h +new file mode 100644 +index 000000000..67263484d +--- /dev/null ++++ b/sound/soc/codecs/aw87xxx/aw87xxx_log.h +@@ -0,0 +1,33 @@ ++#ifndef __AW87XXX_LOG_H__ ++#define __AW87XXX_LOG_H__ ++ ++#include ++ ++ ++/******************************************** ++ * ++ * print information control ++ * ++ *******************************************/ ++#define AW_LOGI(fmt, ...)\ ++ pr_info("[Awinic] %s:" fmt "\n", __func__, ##__VA_ARGS__) ++ ++#define AW_LOGD(fmt, ...)\ ++ pr_debug("[Awinic] %s:" fmt "\n", __func__, ##__VA_ARGS__) ++ ++#define AW_LOGE(fmt, ...)\ ++ pr_err("[Awinic] %s:" fmt "\n", __func__, ##__VA_ARGS__) ++ ++ ++#define AW_DEV_LOGI(dev, fmt, ...)\ ++ pr_info("[Awinic] [%s]%s: " fmt "\n", dev_name(dev), __func__, ##__VA_ARGS__) ++ ++#define AW_DEV_LOGD(dev, fmt, ...)\ ++ pr_debug("[Awinic] [%s]%s: " fmt "\n", dev_name(dev), __func__, ##__VA_ARGS__) ++ ++#define AW_DEV_LOGE(dev, fmt, ...)\ ++ pr_err("[Awinic] [%s]%s: " fmt "\n", dev_name(dev), __func__, ##__VA_ARGS__) ++ ++ ++ ++#endif +diff --git a/sound/soc/codecs/aw87xxx/aw87xxx_monitor.c b/sound/soc/codecs/aw87xxx/aw87xxx_monitor.c +new file mode 100644 +index 000000000..92305f9c4 +--- /dev/null ++++ b/sound/soc/codecs/aw87xxx/aw87xxx_monitor.c +@@ -0,0 +1,1208 @@ ++/* ++ * aw87xxx_monitor.c ++ * ++ * Copyright (c) 2021 AWINIC Technology CO., LTD ++ * ++ * Author: Barry ++ * ++ * 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. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "aw87xxx.h" ++#include "aw87xxx_log.h" ++#include "aw87xxx_monitor.h" ++#include "aw87xxx_dsp.h" ++#include "aw87xxx_bin_parse.h" ++#include "aw87xxx_device.h" ++ ++#define AW_MONITOT_BIN_PARSE_VERSION "V0.1.0" ++ ++#define AW_GET_32_DATA(w, x, y, z) \ ++ ((uint32_t)((((uint8_t)w) << 24) | (((uint8_t)x) << 16) | \ ++ (((uint8_t)y) << 8) | ((uint8_t)z))) ++ ++/**************************************************************************** ++ * ++ * aw87xxx monitor bin check ++ * ++ ****************************************************************************/ ++static int aw_monitor_check_header_v_1_0_0(struct device *dev, ++ char *data, uint32_t data_len) ++{ ++ int i = 0; ++ struct aw_bin_header *header = (struct aw_bin_header *)data; ++ ++ if (header->bin_data_type != DATA_TYPE_MONITOR_ANALOG) { ++ AW_DEV_LOGE(dev, "monitor data_type check error!"); ++ return -EINVAL; ++ } ++ ++ if (header->bin_data_size != AW_MONITOR_HDR_DATA_SIZE) { ++ AW_DEV_LOGE(dev, "monitor data_size error!"); ++ return -EINVAL; ++ } ++ ++ if (header->data_byte_len != AW_MONITOR_HDR_DATA_BYTE_LEN) { ++ AW_DEV_LOGE(dev, "monitor data_byte_len error!"); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < AW_MONITOR_DATA_VER_MAX; i++) { ++ if (header->bin_data_ver == i) { ++ AW_LOGD("monitor bin_data_ver[0x%x]", i); ++ break; ++ } ++ } ++ if (i == AW_MONITOR_DATA_VER_MAX) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int aw_monitor_check_data_v1_size(struct device *dev, ++ char *data, int32_t data_len) ++{ ++ int32_t bin_header_len = sizeof(struct aw_bin_header); ++ int32_t monitor_header_len = sizeof(struct aw_monitor_header); ++ int32_t monitor_data_len = sizeof(struct vmax_step_config); ++ int32_t len = 0; ++ struct aw_monitor_header *monitor_header = NULL; ++ ++ AW_DEV_LOGD(dev, "enter"); ++ ++ if (data_len < bin_header_len + monitor_header_len) { ++ AW_DEV_LOGE(dev, "bin len is less than aw_bin_header and monitoor_header,check failed"); ++ return -EINVAL; ++ } ++ ++ monitor_header = (struct aw_monitor_header *)(data + bin_header_len); ++ len = data_len - bin_header_len - monitor_header_len; ++ if (len < monitor_header->step_count * monitor_data_len) { ++ AW_DEV_LOGE(dev, "bin data len is not enough,check failed"); ++ return -EINVAL; ++ } ++ ++ AW_DEV_LOGD(dev, "succeed"); ++ ++ return 0; ++} ++ ++static int aw_monitor_check_data_size(struct device *dev, ++ char *data, int32_t data_len) ++{ ++ int ret = -1; ++ struct aw_bin_header *header = (struct aw_bin_header *)data; ++ ++ switch (header->bin_data_ver) { ++ case AW_MONITOR_DATA_VER: ++ ret = aw_monitor_check_data_v1_size(dev, data, data_len); ++ if (ret < 0) ++ return ret; ++ break; ++ default: ++ AW_DEV_LOGE(dev, "bin data_ver[0x%x] non support", ++ header->bin_data_ver); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++ ++static int aw_monitor_check_bin_header(struct device *dev, ++ char *data, int32_t data_len) ++{ ++ int ret = -1; ++ struct aw_bin_header *header = NULL; ++ ++ if (data_len < sizeof(struct aw_bin_header)) { ++ AW_DEV_LOGE(dev, "bin len is less than aw_bin_header,check failed"); ++ return -EINVAL; ++ } ++ header = (struct aw_bin_header *)data; ++ ++ switch (header->header_ver) { ++ case HEADER_VERSION_1_0_0: ++ ret = aw_monitor_check_header_v_1_0_0(dev, data, data_len); ++ if (ret < 0) { ++ AW_DEV_LOGE(dev, "monitor bin haeder info check error!"); ++ return ret; ++ } ++ break; ++ default: ++ AW_DEV_LOGE(dev, "bin version[0x%x] non support", ++ header->header_ver); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int aw_monitor_bin_check_sum(struct device *dev, ++ char *data, int32_t data_len) ++{ ++ int i, data_sum = 0; ++ uint32_t *check_sum = (uint32_t *)data; ++ ++ for (i = 4; i < data_len; i++) ++ data_sum += data[i]; ++ ++ if (*check_sum != data_sum) { ++ AW_DEV_LOGE(dev, "check_sum[%d] is not equal to data_sum[%d]", ++ *check_sum, data_sum); ++ return -ENOMEM; ++ } ++ ++ AW_DEV_LOGD(dev, "succeed"); ++ ++ return 0; ++} ++ ++static int aw_monitor_bin_check(struct device *dev, ++ char *monitor_data, uint32_t data_len) ++{ ++ int ret = -1; ++ ++ if (monitor_data == NULL || data_len == 0) { ++ AW_DEV_LOGE(dev, "none data to parse"); ++ return -EINVAL; ++ } ++ ++ ret = aw_monitor_bin_check_sum(dev, monitor_data, data_len); ++ if (ret < 0) { ++ AW_DEV_LOGE(dev, "bin data check sum failed"); ++ return ret; ++ } ++ ++ ret = aw_monitor_check_bin_header(dev, monitor_data, data_len); ++ if (ret < 0) { ++ AW_DEV_LOGE(dev, "bin data len check failed"); ++ return ret; ++ } ++ ++ ret = aw_monitor_check_data_size(dev, monitor_data, data_len); ++ if (ret < 0) { ++ AW_DEV_LOGE(dev, "bin header info check failed"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/***************************************************************************** ++ * ++ * aw87xxx monitor header bin parse ++ * ++ *****************************************************************************/ ++static void aw_monitor_write_to_table_v1(struct device *dev, ++ struct vmax_step_config *vmax_step, ++ char *vmax_data, uint32_t step_count) ++{ ++ int i = 0; ++ int index = 0; ++ int vmax_step_size = (int)sizeof(struct vmax_step_config); ++ ++ for (i = 0; i < step_count; i++) { ++ index = vmax_step_size * i; ++ vmax_step[i].vbat_min = ++ AW_GET_32_DATA(vmax_data[index + 3], ++ vmax_data[index + 2], ++ vmax_data[index + 1], ++ vmax_data[index + 0]); ++ vmax_step[i].vbat_max = ++ AW_GET_32_DATA(vmax_data[index + 7], ++ vmax_data[index + 6], ++ vmax_data[index + 5], ++ vmax_data[index + 4]); ++ vmax_step[i].vmax_vol = ++ AW_GET_32_DATA(vmax_data[index + 11], ++ vmax_data[index + 10], ++ vmax_data[index + 9], ++ vmax_data[index + 8]); ++ } ++ ++ for (i = 0; i < step_count; i++) ++ AW_DEV_LOGI(dev, "vbat_min:%d, vbat_max%d, vmax_vol:0x%x", ++ vmax_step[i].vbat_min, ++ vmax_step[i].vbat_max, ++ vmax_step[i].vmax_vol); ++} ++ ++static int aw_monitor_parse_vol_data_v1(struct device *dev, ++ struct aw_monitor *monitor, char *monitor_data) ++{ ++ uint32_t step_count = 0; ++ char *vmax_data = NULL; ++ struct vmax_step_config *vmax_step = NULL; ++ ++ AW_DEV_LOGD(dev, "enter"); ++ ++ step_count = monitor->monitor_hdr.step_count; ++ if (step_count) { ++ vmax_step = devm_kzalloc(dev, sizeof(struct vmax_step_config) * step_count, ++ GFP_KERNEL); ++ if (vmax_step == NULL) { ++ AW_DEV_LOGE(dev, "vmax_cfg vmalloc failed"); ++ return -ENOMEM; ++ } ++ memset(vmax_step, 0, ++ sizeof(struct vmax_step_config) * step_count); ++ } ++ ++ vmax_data = monitor_data + sizeof(struct aw_bin_header) + ++ sizeof(struct aw_monitor_header); ++ aw_monitor_write_to_table_v1(dev, vmax_step, vmax_data, step_count); ++ monitor->vmax_cfg = vmax_step; ++ ++ AW_DEV_LOGI(dev, "vmax_data parse succeed"); ++ ++ return 0; ++} ++ ++static int aw_monitor_parse_data_v1(struct device *dev, ++ struct aw_monitor *monitor, char *monitor_data) ++{ ++ int ret = -1; ++ int header_len = 0; ++ struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; ++ ++ header_len = sizeof(struct aw_bin_header); ++ memcpy(monitor_hdr, monitor_data + header_len, ++ sizeof(struct aw_monitor_header)); ++ ++ AW_DEV_LOGI(dev, "monitor_switch:%d, monitor_time:%d (ms), monitor_count:%d, step_count:%d", ++ monitor_hdr->monitor_switch, monitor_hdr->monitor_time, ++ monitor_hdr->monitor_count, monitor_hdr->step_count); ++ ++ ret = aw_monitor_parse_vol_data_v1(dev, monitor, monitor_data); ++ if (ret < 0) { ++ AW_DEV_LOGE(dev, "vmax_data parse failed"); ++ return ret; ++ } ++ ++ monitor->bin_status = AW_MONITOR_CFG_OK; ++ ++ return 0; ++} ++ ++ ++static int aw_monitor_parse_v_1_0_0(struct device *dev, ++ struct aw_monitor *monitor, char *monitor_data) ++{ ++ int ret = -1; ++ struct aw_bin_header *header = (struct aw_bin_header *)monitor_data; ++ ++ switch (header->bin_data_ver) { ++ case AW_MONITOR_DATA_VER: ++ ret = aw_monitor_parse_data_v1(dev, monitor, monitor_data); ++ if (ret < 0) ++ return ret; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++void aw87xxx_monitor_cfg_free(struct aw_monitor *monitor) ++{ ++ struct aw87xxx *aw87xxx = ++ container_of(monitor, struct aw87xxx, monitor); ++ ++ monitor->bin_status = AW_MONITOR_CFG_WAIT; ++ memset(&monitor->monitor_hdr, 0, ++ sizeof(struct aw_monitor_header)); ++ if (monitor->vmax_cfg) { ++ devm_kfree(aw87xxx->dev, monitor->vmax_cfg); ++ monitor->vmax_cfg = NULL; ++ } ++} ++ ++int aw87xxx_monitor_bin_parse(struct device *dev, ++ char *monitor_data, uint32_t data_len) ++{ ++ int ret = -1; ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ struct aw_monitor *monitor = NULL; ++ struct aw_bin_header *bin_header = NULL; ++ ++ if (aw87xxx == NULL) { ++ AW_DEV_LOGE(dev, "get struct aw87xxx failed"); ++ return -EINVAL; ++ } ++ ++ monitor = &aw87xxx->monitor; ++ monitor->bin_status = AW_MONITOR_CFG_WAIT; ++ ++ AW_DEV_LOGI(dev, "monitor bin parse version: %s", ++ AW_MONITOT_BIN_PARSE_VERSION); ++ ++ ret = aw_monitor_bin_check(dev, monitor_data, data_len); ++ if (ret < 0) { ++ AW_DEV_LOGE(dev, "monitor bin check failed"); ++ return ret; ++ } ++ ++ bin_header = (struct aw_bin_header *)monitor_data; ++ switch (bin_header->bin_data_ver) { ++ case DATA_VERSION_V1: ++ ret = aw_monitor_parse_v_1_0_0(dev, monitor, ++ monitor_data); ++ if (ret < 0) { ++ aw87xxx_monitor_cfg_free(monitor); ++ return ret; ++ } ++ break; ++ default: ++ AW_DEV_LOGE(dev, "Unrecognized this bin data version[0x%x]", ++ bin_header->bin_data_ver); ++ } ++ ++ return 0; ++} ++ ++/*************************************************************************** ++ * ++ * aw87xxx monitor get adjustment vmax of power ++ * ++ ***************************************************************************/ ++static int aw_monitor_get_battery_capacity(struct device *dev, ++ struct aw_monitor *monitor, int *vbat_capacity) ++{ ++ char name[] = "battery"; ++ int ret = -1; ++ union power_supply_propval prop = { 0 }; ++ struct power_supply *psy = NULL; ++ ++ psy = power_supply_get_by_name(name); ++ if (psy == NULL) { ++ AW_DEV_LOGE(dev, "no struct power supply name:%s", name); ++ return -EINVAL; ++ } ++ ++ ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_CAPACITY, &prop); ++ if (ret < 0) { ++ AW_DEV_LOGE(dev, "get vbat capacity failed"); ++ return -EINVAL; ++ } ++ *vbat_capacity = prop.intval; ++ AW_DEV_LOGI(dev, "The percentage is %d", ++ *vbat_capacity); ++ ++ return 0; ++} ++ ++static int aw_search_vmax_from_table(struct device *dev, ++ struct aw_monitor *monitor, ++ const int vbat_vol, int *vmax_vol) ++{ ++ int i = 0; ++ int vmax_set = 0; ++ uint32_t vmax_flag = 0; ++ struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; ++ struct vmax_step_config *vmax_cfg = monitor->vmax_cfg; ++ ++ if (monitor->bin_status == AW_MONITOR_CFG_WAIT) { ++ AW_DEV_LOGE(dev, "vmax_cfg not loaded or parse failed"); ++ return -ENODATA; ++ } ++ ++ for (i = 0; i < monitor_hdr->step_count; i++) { ++ if (vbat_vol == AW_VBAT_MAX) { ++ vmax_set = AW_VMAX_MAX; ++ vmax_flag = 1; ++ AW_DEV_LOGD(dev, "vbat=%d, setting vmax=0x%x", ++ vbat_vol, vmax_set); ++ break; ++ } ++ ++ if (vbat_vol >= vmax_cfg[i].vbat_min && ++ vbat_vol < vmax_cfg[i].vbat_max) { ++ vmax_set = vmax_cfg[i].vmax_vol; ++ vmax_flag = 1; ++ AW_DEV_LOGD(dev, "read setting vmax=0x%x, step[%d]: vbat_min=%d,vbat_max=%d", ++ vmax_set, i, ++ vmax_cfg[i].vbat_min, ++ vmax_cfg[i].vbat_max); ++ break; ++ } ++ } ++ ++ if (!vmax_flag) { ++ AW_DEV_LOGE(dev, "vmax_cfg not found"); ++ return -ENODATA; ++ } ++ ++ *vmax_vol = vmax_set; ++ return 0; ++} ++ ++ ++/*************************************************************************** ++ * ++ *monitor_esd_func ++ * ++ ***************************************************************************/ ++static int aw_chip_status_recover(struct aw87xxx *aw87xxx) ++{ ++ int ret = -1; ++ struct aw_monitor *monitor = &aw87xxx->monitor; ++ char *profile = aw87xxx->current_profile; ++ ++ AW_DEV_LOGD(aw87xxx->dev, "enter"); ++ ++ ret = aw87xxx_update_profile_esd(aw87xxx, profile); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", ++ profile); ++ return ret; ++ } ++ ++ AW_DEV_LOGI(aw87xxx->dev, "current prof[%s], dev_index[%d] ", ++ profile, aw87xxx->dev_index); ++ ++ monitor->pre_vmax = AW_VMAX_INIT_VAL; ++ monitor->first_entry = AW_FIRST_ENTRY; ++ monitor->timer_cnt = 0; ++ monitor->vbat_sum = 0; ++ ++ return 0; ++} ++ ++static int aw_monitor_chip_esd_check_work(struct aw87xxx *aw87xxx) ++{ ++ int ret = 0; ++ int i = 0; ++ ++ for (i = 0; i < REG_STATUS_CHECK_MAX; i++) { ++ AW_DEV_LOGD(aw87xxx->dev, "reg_status_check[%d]", i); ++ ++ ret = aw87xxx_dev_esd_reg_status_check(&aw87xxx->aw_dev); ++ if (ret < 0) { ++ aw_chip_status_recover(aw87xxx); ++ } else { ++ AW_DEV_LOGD(aw87xxx->dev, "chip status check succeed"); ++ break; ++ } ++ msleep(AW_ESD_CHECK_DELAY); ++ } ++ ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "chip status recover failed,chip off"); ++ aw87xxx_update_profile_esd(aw87xxx, aw87xxx->prof_off_name); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++ ++/*************************************************************************** ++ * ++ * aw87xxx monitor work with dsp ++ * ++ ***************************************************************************/ ++static int aw_monitor_update_vmax_to_dsp(struct device *dev, ++ struct aw_monitor *monitor, int vmax_set) ++{ ++ int ret = -1; ++ uint32_t enable = 0; ++ ++ if (monitor->pre_vmax != vmax_set) { ++ ret = aw87xxx_dsp_get_rx_module_enable(&enable); ++ if (!enable || ret < 0) { ++ AW_DEV_LOGE(dev, "get rx failed or rx disable, ret=%d, enable=%d", ++ ret, enable); ++ return -EPERM; ++ } ++ ++ ret = aw87xxx_dsp_set_vmax(vmax_set, monitor->dev_index); ++ if (ret) { ++ AW_DEV_LOGE(dev, "set dsp msg fail, ret=%d", ret); ++ return ret; ++ } ++ ++ AW_DEV_LOGI(dev, "set dsp vmax=0x%x sucess", vmax_set); ++ monitor->pre_vmax = vmax_set; ++ } else { ++ AW_DEV_LOGI(dev, "vmax=0x%x no change", vmax_set); ++ } ++ ++ return 0; ++} ++ ++static void aw_monitor_with_dsp_vmax_work(struct device *dev, ++ struct aw_monitor *monitor) ++{ ++ int ret = -1; ++ int vmax_set = 0; ++ int vbat_capacity = 0; ++ int ave_capacity = 0; ++ struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; ++ ++ AW_DEV_LOGD(dev, "enter with dsp monitor"); ++ ++ ret = aw_monitor_get_battery_capacity(dev, monitor, &vbat_capacity); ++ if (ret < 0) ++ return; ++ ++ if (monitor->timer_cnt < monitor_hdr->monitor_count) { ++ monitor->timer_cnt++; ++ monitor->vbat_sum += vbat_capacity; ++ AW_DEV_LOGI(dev, "timer_cnt = %d", ++ monitor->timer_cnt); ++ } ++ if ((monitor->timer_cnt >= monitor_hdr->monitor_count) || ++ (monitor->first_entry == AW_FIRST_ENTRY)) { ++ if (monitor->first_entry == AW_FIRST_ENTRY) ++ monitor->first_entry = AW_NOT_FIRST_ENTRY; ++ ave_capacity = monitor->vbat_sum / monitor->timer_cnt; ++ ++ if (monitor->custom_capacity) ++ ave_capacity = monitor->custom_capacity; ++ ++ AW_DEV_LOGI(dev, "get average capacity = %d", ave_capacity); ++ ++ ret = aw_search_vmax_from_table(dev, monitor, ++ ave_capacity, &vmax_set); ++ if (ret < 0) ++ AW_DEV_LOGE(dev, "not find vmax_vol"); ++ else ++ aw_monitor_update_vmax_to_dsp(dev, monitor, vmax_set); ++ ++ monitor->timer_cnt = 0; ++ monitor->vbat_sum = 0; ++ } ++} ++ ++static void aw_monitor_work_func(struct work_struct *work) ++{ ++ int ret = 0; ++ struct aw87xxx *aw87xxx = container_of(work, ++ struct aw87xxx, monitor.with_dsp_work.work); ++ struct device *dev = aw87xxx->dev; ++ struct aw_monitor *monitor = &aw87xxx->monitor; ++ struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; ++ ++ AW_DEV_LOGD(dev, "enter"); ++ ++ if (monitor->esd_enable) { ++ ret = aw_monitor_chip_esd_check_work(aw87xxx); ++ if (ret < 0) ++ return; ++ } ++ ++ if (monitor_hdr->monitor_switch && !(aw87xxx->aw_dev.is_rec_mode) && ++ monitor->open_dsp_en && monitor->bin_status == AW_ACF_UPDATE) { ++ AW_DEV_LOGD(dev, "start low power protection"); ++ aw_monitor_with_dsp_vmax_work(dev, monitor); ++ } ++ ++ if (monitor->esd_enable || (monitor_hdr->monitor_switch && ++ !(aw87xxx->aw_dev.is_rec_mode) && monitor->open_dsp_en && ++ monitor->bin_status == AW_ACF_UPDATE)) { ++ schedule_delayed_work(&monitor->with_dsp_work, ++ msecs_to_jiffies(monitor_hdr->monitor_time)); ++ } ++} ++ ++void aw87xxx_monitor_stop(struct aw_monitor *monitor) ++{ ++ struct aw87xxx *aw87xxx = ++ container_of(monitor, struct aw87xxx, monitor); ++ ++ AW_DEV_LOGD(aw87xxx->dev, "enter"); ++ cancel_delayed_work_sync(&monitor->with_dsp_work); ++} ++ ++void aw87xxx_monitor_start(struct aw_monitor *monitor) ++{ ++ struct aw87xxx *aw87xxx = ++ container_of(monitor, struct aw87xxx, monitor); ++ int ret = 0; ++ ++ ret = aw87xxx_dev_check_reg_is_rec_mode(&aw87xxx->aw_dev); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "get reg current mode failed"); ++ return; ++ } ++ ++ if (monitor->esd_enable || (monitor->monitor_hdr.monitor_switch && ++ !(aw87xxx->aw_dev.is_rec_mode) && monitor->open_dsp_en ++ && monitor->bin_status == AW_ACF_UPDATE)) { ++ ++ AW_DEV_LOGD(aw87xxx->dev, "enter"); ++ monitor->pre_vmax = AW_VMAX_INIT_VAL; ++ monitor->first_entry = AW_FIRST_ENTRY; ++ monitor->timer_cnt = 0; ++ monitor->vbat_sum = 0; ++ ++ schedule_delayed_work(&monitor->with_dsp_work, ++ msecs_to_jiffies(monitor->monitor_hdr.monitor_time)); ++ } ++} ++/*************************************************************************** ++ * ++ * aw87xxx no dsp monitor func ++ * ++ ***************************************************************************/ ++int aw87xxx_monitor_no_dsp_get_vmax(struct aw_monitor *monitor, int32_t *vmax) ++{ ++ int vbat_capacity = 0; ++ int ret = -1; ++ int vmax_vol = 0; ++ struct aw87xxx *aw87xxx = ++ container_of(monitor, struct aw87xxx, monitor); ++ struct device *dev = aw87xxx->dev; ++ ++ ret = aw_monitor_get_battery_capacity(dev, monitor, &vbat_capacity); ++ if (ret < 0) ++ return ret; ++ ++ if (monitor->custom_capacity) ++ vbat_capacity = monitor->custom_capacity; ++ AW_DEV_LOGI(dev, "get_battery_capacity is[%d]", vbat_capacity); ++ ++ ret = aw_search_vmax_from_table(dev, monitor, ++ vbat_capacity, &vmax_vol); ++ if (ret < 0) { ++ AW_DEV_LOGE(dev, "not find vmax_vol"); ++ return ret; ++ } ++ ++ *vmax = vmax_vol; ++ return 0; ++} ++ ++ ++/*************************************************************************** ++ * ++ * aw87xxx monitor sysfs nodes ++ * ++ ***************************************************************************/ ++static ssize_t aw_attr_get_esd_enable(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ssize_t len = 0; ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ struct aw_monitor *monitor = &aw87xxx->monitor; ++ ++ if (monitor->esd_enable) { ++ AW_DEV_LOGI(aw87xxx->dev, "esd-enable=true"); ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ "esd-enable=true\n"); ++ } else { ++ AW_DEV_LOGI(aw87xxx->dev, "esd-enable=false"); ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ "esd-enable=false\n"); ++ } ++ ++ return len; ++} ++ ++static ssize_t aw_attr_set_esd_enable(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t len) ++{ ++ char esd_enable[AW_ESD_ENABLE_STRLEN] = {0}; ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ struct aw_monitor *monitor = &aw87xxx->monitor; ++ ++ if (strlen(buf) > AW_ESD_ENABLE_STRLEN) { ++ AW_DEV_LOGE(aw87xxx->dev, "input esd_enable_str_len is out of max[%d]", ++ AW_ESD_ENABLE_STRLEN); ++ return -EINVAL; ++ } ++ ++ if (sscanf(buf, "%s", esd_enable) == 1) { ++ AW_DEV_LOGD(aw87xxx->dev, "input esd-enable=[%s]", esd_enable); ++ if (!strcmp(esd_enable, "true")) ++ monitor->esd_enable = AW_ESD_ENABLE; ++ else ++ monitor->esd_enable = AW_ESD_DISABLE; ++ AW_DEV_LOGI(dev, "set esd-enable=[%s]", ++ monitor->esd_enable ? "true" : "false"); ++ } else { ++ AW_DEV_LOGE(aw87xxx->dev, "input esd-enable error"); ++ return -EINVAL; ++ } ++ ++ return len; ++} ++ ++static ssize_t aw_attr_get_vbat(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ssize_t len = 0; ++ int ret = -1; ++ int vbat_capacity = 0; ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ struct aw_monitor *monitor = &aw87xxx->monitor; ++ ++ if (monitor->custom_capacity == 0) { ++ ret = aw_monitor_get_battery_capacity(dev, monitor, ++ &vbat_capacity); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "get battery_capacity failed"); ++ return ret; ++ } ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ "vbat capacity=%d\n", vbat_capacity); ++ } else { ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ "vbat capacity=%d\n", ++ monitor->custom_capacity); ++ } ++ ++ return len; ++} ++ ++static ssize_t aw_attr_set_vbat(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t len) ++{ ++ int ret = -1; ++ int capacity = 0; ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ struct aw_monitor *monitor = &aw87xxx->monitor; ++ ++ ret = kstrtouint(buf, 0, &capacity); ++ if (ret < 0) ++ return ret; ++ AW_DEV_LOGI(aw87xxx->dev, "set capacity = %d", capacity); ++ if (capacity >= AW_VBAT_CAPACITY_MIN && ++ capacity <= AW_VBAT_CAPACITY_MAX){ ++ monitor->custom_capacity = capacity; ++ } else { ++ AW_DEV_LOGE(aw87xxx->dev, "vbat_set=invalid,please input value [%d-%d]", ++ AW_VBAT_CAPACITY_MIN, AW_VBAT_CAPACITY_MAX); ++ return -EINVAL; ++ } ++ ++ return len; ++} ++ ++static ssize_t aw_attr_get_vmax(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ssize_t len = 0; ++ int ret = -1; ++ int vbat_capacity = 0; ++ int vmax_get = 0; ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ struct aw_monitor *monitor = &aw87xxx->monitor; ++ ++ if (monitor->open_dsp_en) { ++ ret = aw87xxx_dsp_get_vmax(&vmax_get, aw87xxx->dev_index); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, ++ "get dsp vmax fail, ret=%d", ret); ++ return ret; ++ } ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ "get_vmax=%d\n", vmax_get); ++ } else { ++ ret = aw_monitor_get_battery_capacity(dev, monitor, ++ &vbat_capacity); ++ if (ret < 0) ++ return ret; ++ AW_DEV_LOGI(aw87xxx->dev, "get_battery_capacity is [%d]", ++ vbat_capacity); ++ ++ if (monitor->custom_capacity) { ++ vbat_capacity = monitor->custom_capacity; ++ AW_DEV_LOGI(aw87xxx->dev, "get custom_capacity is [%d]", ++ vbat_capacity); ++ } ++ ++ ret = aw_search_vmax_from_table(aw87xxx->dev, monitor, ++ vbat_capacity, &vmax_get); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "not find vmax_vol"); ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ "not_find_vmax_vol\n"); ++ return len; ++ } ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ "0x%x\n", vmax_get); ++ AW_DEV_LOGI(aw87xxx->dev, "0x%x", vmax_get); ++ } ++ ++ return len; ++} ++ ++static ssize_t aw_attr_set_vmax(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ uint32_t vmax_set = 0; ++ int ret = -1; ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ struct aw_monitor *monitor = &aw87xxx->monitor; ++ ++ ret = kstrtouint(buf, 0, &vmax_set); ++ if (ret < 0) ++ return ret; ++ ++ AW_DEV_LOGI(aw87xxx->dev, "vmax_set=0x%x", vmax_set); ++ ++ if (monitor->open_dsp_en) { ++ ret = aw87xxx_dsp_set_vmax(vmax_set, aw87xxx->dev_index); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "send dsp_msg error, ret = %d", ++ ret); ++ return ret; ++ } ++ msleep(2); ++ } else { ++ AW_DEV_LOGE(aw87xxx->dev, "no_dsp system,vmax_set invalid"); ++ return -EINVAL; ++ } ++ ++ return count; ++} ++ ++static ssize_t aw_attr_get_monitor_switch(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ssize_t len = 0; ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ struct aw_monitor *monitor = &aw87xxx->monitor; ++ struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; ++ ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ "aw87xxx monitor switch: %u\n", ++ monitor_hdr->monitor_switch); ++ return len; ++} ++ ++ ++int aw87xxx_dev_monitor_switch_set(struct aw_monitor *monitor, uint32_t enable) ++{ ++ struct aw87xxx *aw87xxx = ++ container_of(monitor, struct aw87xxx, monitor); ++ struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; ++ ++ AW_DEV_LOGI(aw87xxx->dev, "monitor switch set =%d", enable); ++ ++ if (!monitor->bin_status) { ++ AW_DEV_LOGE(aw87xxx->dev, "bin parse faile or not loaded,set invalid"); ++ return -EINVAL; ++ } ++ ++ if (monitor_hdr->monitor_switch == enable) ++ return 0; ++ ++ if (enable > 0) { ++ monitor_hdr->monitor_switch = 1; ++ if (monitor->open_dsp_en) { ++ monitor->pre_vmax = AW_VMAX_INIT_VAL; ++ monitor->first_entry = AW_FIRST_ENTRY; ++ monitor->timer_cnt = 0; ++ monitor->vbat_sum = 0; ++ } ++ } else { ++ monitor_hdr->monitor_switch = 0; ++ } ++ ++ return 0; ++} ++ ++static ssize_t aw_attr_set_monitor_switch(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ uint32_t enable = 0; ++ int ret = -1; ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ struct aw_monitor *monitor = &aw87xxx->monitor; ++ ++ ret = kstrtouint(buf, 0, &enable); ++ if (ret < 0) ++ return ret; ++ ++ ret = aw87xxx_dev_monitor_switch_set(monitor, enable); ++ if (ret) ++ return ret; ++ ++ return count; ++} ++ ++static ssize_t aw_attr_get_monitor_time(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ssize_t len = 0; ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ struct aw_monitor *monitor = &aw87xxx->monitor; ++ struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; ++ ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ "aw_monitor_timer = %u(ms)\n", ++ monitor_hdr->monitor_time); ++ return len; ++} ++ ++static ssize_t aw_attr_set_monitor_time(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ unsigned int timer_val = 0; ++ int ret = -1; ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ struct aw_monitor *monitor = &aw87xxx->monitor; ++ struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; ++ ++ ret = kstrtouint(buf, 0, &timer_val); ++ if (ret < 0) ++ return ret; ++ ++ AW_DEV_LOGI(aw87xxx->dev, "input monitor timer=%d(ms)", timer_val); ++ ++ if (!monitor->bin_status) { ++ AW_DEV_LOGE(aw87xxx->dev, "bin parse faile or not loaded,set invalid"); ++ return -EINVAL; ++ } ++ ++ if (timer_val != monitor_hdr->monitor_time) ++ monitor_hdr->monitor_time = timer_val; ++ else ++ AW_DEV_LOGI(aw87xxx->dev, "no_change monitor_time"); ++ ++ return count; ++} ++ ++static ssize_t aw_attr_get_monitor_count(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ssize_t len = 0; ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ struct aw_monitor *monitor = &aw87xxx->monitor; ++ struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; ++ ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ "aw_monitor_count = %u\n", ++ monitor_hdr->monitor_count); ++ return len; ++} ++ ++static ssize_t aw_attr_set_monitor_count(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ unsigned int monitor_count = 0; ++ int ret = -1; ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ struct aw_monitor *monitor = &aw87xxx->monitor; ++ struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; ++ ++ ret = kstrtouint(buf, 0, &monitor_count); ++ if (ret < 0) ++ return ret; ++ AW_DEV_LOGI(aw87xxx->dev, "input monitor count=%d", monitor_count); ++ ++ if (!monitor->bin_status) { ++ AW_DEV_LOGE(aw87xxx->dev, "bin parse faile or not loaded,set invalid"); ++ return -EINVAL; ++ } ++ ++ if (monitor_count != monitor_hdr->monitor_count) ++ monitor_hdr->monitor_count = monitor_count; ++ else ++ AW_DEV_LOGI(aw87xxx->dev, "no_change monitor_count"); ++ ++ return count; ++} ++ ++ ++static ssize_t aw_attr_get_rx(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ struct aw_monitor *monitor = &aw87xxx->monitor; ++ ssize_t len = 0; ++ int ret = -1; ++ uint32_t enable = 0; ++ ++ if (monitor->open_dsp_en) { ++ ret = aw87xxx_dsp_get_rx_module_enable(&enable); ++ if (ret) { ++ AW_DEV_LOGE(aw87xxx->dev, "dsp_msg error, ret=%d", ret); ++ return ret; ++ } ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ "aw87xxx rx: %u\n", enable); ++ } else { ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ "command is invalid\n"); ++ } ++ ++ return len; ++} ++ ++static ssize_t aw_attr_set_rx(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ struct aw_monitor *monitor = &aw87xxx->monitor; ++ int ret = -1; ++ uint32_t enable; ++ ++ ret = kstrtouint(buf, 0, &enable); ++ if (ret < 0) ++ return ret; ++ ++ if (monitor->open_dsp_en) { ++ AW_DEV_LOGI(aw87xxx->dev, "set rx enable=%d", enable); ++ ++ ret = aw87xxx_dsp_set_rx_module_enable(enable); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "dsp_msg error, ret=%d", ++ ret); ++ return ret; ++ } ++ } else { ++ AW_DEV_LOGE(aw87xxx->dev, "command is invalid"); ++ return -EINVAL; ++ } ++ ++ return count; ++} ++ ++ ++static DEVICE_ATTR(esd_enable, S_IWUSR | S_IRUGO, ++ aw_attr_get_esd_enable, aw_attr_set_esd_enable); ++static DEVICE_ATTR(vbat, S_IWUSR | S_IRUGO, ++ aw_attr_get_vbat, aw_attr_set_vbat); ++static DEVICE_ATTR(vmax, S_IWUSR | S_IRUGO, ++ aw_attr_get_vmax, aw_attr_set_vmax); ++ ++static DEVICE_ATTR(monitor_switch, S_IWUSR | S_IRUGO, ++ aw_attr_get_monitor_switch, aw_attr_set_monitor_switch); ++static DEVICE_ATTR(monitor_time, S_IWUSR | S_IRUGO, ++ aw_attr_get_monitor_time, aw_attr_set_monitor_time); ++static DEVICE_ATTR(monitor_count, S_IWUSR | S_IRUGO, ++ aw_attr_get_monitor_count, aw_attr_set_monitor_count); ++static DEVICE_ATTR(rx, S_IWUSR | S_IRUGO, ++ aw_attr_get_rx, aw_attr_set_rx); ++ ++static struct attribute *aw_monitor_vol_adjust[] = { ++ &dev_attr_esd_enable.attr, ++ &dev_attr_vbat.attr, ++ &dev_attr_vmax.attr, ++ NULL ++}; ++ ++static struct attribute_group aw_monitor_vol_adjust_group = { ++ .attrs = aw_monitor_vol_adjust, ++}; ++ ++static struct attribute *aw_monitor_control[] = { ++ &dev_attr_monitor_switch.attr, ++ &dev_attr_monitor_time.attr, ++ &dev_attr_monitor_count.attr, ++ &dev_attr_rx.attr, ++ NULL ++}; ++ ++static struct attribute_group aw_monitor_control_group = { ++ .attrs = aw_monitor_control, ++}; ++ ++/*************************************************************************** ++ * ++ * aw87xxx monitor init ++ * ++ ***************************************************************************/ ++static void aw_monitor_dtsi_parse(struct device *dev, ++ struct aw_monitor *monitor, ++ struct device_node *dev_node) ++{ ++ int ret = -1; ++ const char *esd_enable; ++ ++ ret = of_property_read_string(dev_node, "esd-enable", &esd_enable); ++ if (ret < 0) { ++ AW_DEV_LOGI(dev, "esd_enable parse failed, user default[disable]"); ++ monitor->esd_enable = AW_ESD_DISABLE; ++ } else { ++ if (!strcmp(esd_enable, "true")) ++ monitor->esd_enable = AW_ESD_ENABLE; ++ else ++ monitor->esd_enable = AW_ESD_DISABLE; ++ ++ AW_DEV_LOGI(dev, "parse esd-enable=[%s]", ++ monitor->esd_enable ? "true" : "false"); ++ } ++} ++ ++void aw87xxx_monitor_init(struct device *dev, struct aw_monitor *monitor, ++ struct device_node *dev_node) ++{ ++ int ret = -1; ++ struct aw87xxx *aw87xxx = ++ container_of(monitor, struct aw87xxx, monitor); ++ ++ monitor->dev_index = aw87xxx->dev_index; ++ monitor->monitor_hdr.monitor_time = AW_DEFAULT_MONITOR_TIME; ++ ++ aw_monitor_dtsi_parse(dev, monitor, dev_node); ++ ++ /* get platform open dsp type */ ++ monitor->open_dsp_en = aw87xxx_dsp_isEnable(); ++ ++ ret = sysfs_create_group(&dev->kobj, &aw_monitor_vol_adjust_group); ++ if (ret < 0) ++ AW_DEV_LOGE(dev, "failed to create monitor vol_adjust sysfs nodes"); ++ ++ INIT_DELAYED_WORK(&monitor->with_dsp_work, aw_monitor_work_func); ++ ++ if (monitor->open_dsp_en) { ++ ret = sysfs_create_group(&dev->kobj, &aw_monitor_control_group); ++ if (ret < 0) ++ AW_DEV_LOGE(dev, "failed to create monitor dsp control sysfs nodes"); ++ } ++ ++ if (!ret) ++ AW_DEV_LOGI(dev, "monitor init succeed"); ++} ++ ++void aw87xxx_monitor_exit(struct aw_monitor *monitor) ++{ ++ struct aw87xxx *aw87xxx = ++ container_of(monitor, struct aw87xxx, monitor); ++ /*rm attr node*/ ++ sysfs_remove_group(&aw87xxx->dev->kobj, ++ &aw_monitor_vol_adjust_group); ++ ++ aw87xxx_monitor_stop(monitor); ++ ++ if (monitor->open_dsp_en) { ++ sysfs_remove_group(&aw87xxx->dev->kobj, ++ &aw_monitor_control_group); ++ } ++} ++ +diff --git a/sound/soc/codecs/aw87xxx/aw87xxx_monitor.h b/sound/soc/codecs/aw87xxx/aw87xxx_monitor.h +new file mode 100644 +index 000000000..4b0282b41 +--- /dev/null ++++ b/sound/soc/codecs/aw87xxx/aw87xxx_monitor.h +@@ -0,0 +1,96 @@ ++#ifndef __AW87XXX_MONITOR_H__ ++#define __AW87XXX_MONITOR_H__ ++ ++#define AW_WAIT_DSP_OPEN_TIME (3000) ++#define AW_VBAT_CAPACITY_MIN (0) ++#define AW_VBAT_CAPACITY_MAX (100) ++#define AW_VMAX_INIT_VAL (0xFFFFFFFF) ++#define AW_VBAT_MAX (100) ++#define AW_VMAX_MAX (0) ++#define AW_DEFAULT_MONITOR_TIME (3000) ++#define AW_WAIT_TIME (3000) ++#define REG_STATUS_CHECK_MAX (10) ++#define AW_ESD_CHECK_DELAY (1) ++ ++#define AW_ESD_ENABLE (true) ++#define AW_ESD_DISABLE (false) ++#define AW_ESD_ENABLE_STRLEN (16) ++ ++enum aw_monitor_init { ++ AW_MONITOR_CFG_WAIT = 0, ++ AW_MONITOR_CFG_OK = 1, ++}; ++ ++enum aw_monitor_hdr_info { ++ AW_MONITOR_HDR_DATA_SIZE = 0x00000004, ++ AW_MONITOR_HDR_DATA_BYTE_LEN = 0x00000004, ++}; ++ ++enum aw_monitor_data_ver { ++ AW_MONITOR_DATA_VER = 0x00000001, ++ AW_MONITOR_DATA_VER_MAX, ++}; ++ ++enum aw_monitor_first_enter { ++ AW_FIRST_ENTRY = 0, ++ AW_NOT_FIRST_ENTRY = 1, ++}; ++ ++struct aw_bin_header { ++ uint32_t check_sum; ++ uint32_t header_ver; ++ uint32_t bin_data_type; ++ uint32_t bin_data_ver; ++ uint32_t bin_data_size; ++ uint32_t ui_ver; ++ char product[8]; ++ uint32_t addr_byte_len; ++ uint32_t data_byte_len; ++ uint32_t device_addr; ++ uint32_t reserve[4]; ++}; ++ ++struct aw_monitor_header { ++ uint32_t monitor_switch; ++ uint32_t monitor_time; ++ uint32_t monitor_count; ++ uint32_t step_count; ++ uint32_t reserve[4]; ++}; ++ ++struct vmax_step_config { ++ uint32_t vbat_min; ++ uint32_t vbat_max; ++ int vmax_vol; ++}; ++ ++struct aw_monitor { ++ bool open_dsp_en; ++ bool esd_enable; ++ int32_t dev_index; ++ uint8_t first_entry; ++ uint8_t timer_cnt; ++ uint32_t vbat_sum; ++ int32_t custom_capacity; ++ uint32_t pre_vmax; ++ ++ int bin_status; ++ struct aw_monitor_header monitor_hdr; ++ struct vmax_step_config *vmax_cfg; ++ ++ struct delayed_work with_dsp_work; ++}; ++ ++void aw87xxx_monitor_cfg_free(struct aw_monitor *monitor); ++int aw87xxx_monitor_bin_parse(struct device *dev, ++ char *monitor_data, uint32_t data_len); ++void aw87xxx_monitor_stop(struct aw_monitor *monitor); ++void aw87xxx_monitor_start(struct aw_monitor *monitor); ++int aw87xxx_monitor_no_dsp_get_vmax(struct aw_monitor *monitor, ++ int32_t *vmax); ++void aw87xxx_monitor_init(struct device *dev, struct aw_monitor *monitor, ++ struct device_node *dev_node); ++void aw87xxx_monitor_exit(struct aw_monitor *monitor); ++int aw87xxx_dev_monitor_switch_set(struct aw_monitor *monitor, uint32_t enable); ++ ++#endif +diff --git a/sound/soc/codecs/aw87xxx/aw87xxx_pid_18_reg.h b/sound/soc/codecs/aw87xxx/aw87xxx_pid_18_reg.h +new file mode 100644 +index 000000000..7b95d2252 +--- /dev/null ++++ b/sound/soc/codecs/aw87xxx/aw87xxx_pid_18_reg.h +@@ -0,0 +1,2315 @@ ++#ifndef __AW87XXX_PID_18_REG_H__ ++#define __AW87XXX_PID_18_REG_H__ ++ ++/* registers list */ ++#define AW87XXX_PID_18_CHIPID_REG (0x00) ++#define AW87XXX_PID_18_SYSST_REG (0x01) ++#define AW87XXX_PID_18_SYSINT_REG (0x02) ++#define AW87XXX_PID_18_SYSCTRL_REG (0x03) ++#define AW87XXX_PID_18_CPOC_REG (0x04) ++#define AW87XXX_PID_18_CLASSD_REG (0x05) ++#define AW87XXX_PID_18_MADPVTH_REG (0x06) ++#define AW87XXX_PID_18_A3PARAM_REG (0x07) ++#define AW87XXX_PID_18_A3A2PO_REG (0x08) ++#define AW87XXX_PID_18_A2PARAM_REG (0x09) ++#define AW87XXX_PID_18_A1PARAM_REG (0x0A) ++#define AW87XXX_PID_18_POPCLK_REG (0x0B) ++#define AW87XXX_PID_18_GTDRCPSS_REG (0x0C) ++#define AW87XXX_PID_18_MULTI_REG (0x0D) ++#define AW87XXX_PID_18_DFT1_REG (0x61) ++#define AW87XXX_PID_18_DFT2_REG (0x62) ++#define AW87XXX_PID_18_DFT3_REG (0x63) ++#define AW87XXX_PID_18_DFT4_REG (0x64) ++#define AW87XXX_PID_18_DFT5_REG (0x65) ++#define AW87XXX_PID_18_DFT6_REG (0x66) ++ ++#define AW87XXX_PID_18_CLASSD_DEFAULT (0x10) ++ ++/******************************************** ++ * soft control info ++ * If you need to update this file, add this information manually ++ *******************************************/ ++unsigned char aw87xxx_pid_18_softrst_access[2] = {0x00, 0xaa}; ++ ++/******************************************** ++ * Register Access ++ *******************************************/ ++#define AW87XXX_PID_18_REG_MAX (0x67) ++ ++#define REG_NONE_ACCESS (0) ++#define REG_RD_ACCESS (1 << 0) ++#define REG_WR_ACCESS (1 << 1) ++ ++const unsigned char aw87xxx_pid_18_reg_access[AW87XXX_PID_18_REG_MAX] = { ++ [AW87XXX_PID_18_CHIPID_REG] = (REG_RD_ACCESS), ++ [AW87XXX_PID_18_SYSST_REG] = (REG_RD_ACCESS), ++ [AW87XXX_PID_18_SYSINT_REG] = (REG_RD_ACCESS), ++ [AW87XXX_PID_18_SYSCTRL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), ++ [AW87XXX_PID_18_CPOC_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), ++ [AW87XXX_PID_18_CLASSD_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), ++ [AW87XXX_PID_18_MADPVTH_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), ++ [AW87XXX_PID_18_A3PARAM_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), ++ [AW87XXX_PID_18_A3A2PO_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), ++ [AW87XXX_PID_18_A2PARAM_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), ++ [AW87XXX_PID_18_A1PARAM_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), ++ [AW87XXX_PID_18_POPCLK_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), ++ [AW87XXX_PID_18_GTDRCPSS_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), ++ [AW87XXX_PID_18_MULTI_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), ++ [AW87XXX_PID_18_DFT1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), ++ [AW87XXX_PID_18_DFT2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), ++ [AW87XXX_PID_18_DFT3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), ++ [AW87XXX_PID_18_DFT4_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), ++ [AW87XXX_PID_18_DFT5_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), ++ [AW87XXX_PID_18_DFT6_REG] = (REG_RD_ACCESS), ++}; ++ ++/* detail information of registers begin */ ++/* CHIPID (0x00) detail */ ++/* IDCODE bit 7:0 (CHIPID 0x00) */ ++#define AW87XXX_PID_18_IDCODE_START_BIT (0) ++#define AW87XXX_PID_18_IDCODE_BITS_LEN (8) ++#define AW87XXX_PID_18_IDCODE_MASK \ ++ (~(((1< +Date: Thu, 16 May 2024 04:57:32 +0000 +Subject: [PATCH 2/8] Updated AW87xxx driver to be more verbose for debugging + purposes, but also fixed Reset Pin GPIO initialization issue with Ayn Loki + Mini + +Signed-off-by: Antheas Kapenekakis +--- + sound/soc/codecs/aw87xxx/aw87xxx.c | 2923 +++++++++++---------- + sound/soc/codecs/aw87xxx/aw87xxx_device.c | 8 +- + 2 files changed, 1470 insertions(+), 1461 deletions(-) + +diff --git a/sound/soc/codecs/aw87xxx/aw87xxx.c b/sound/soc/codecs/aw87xxx/aw87xxx.c +index eddb01695..7f44d9b9d 100644 +--- a/sound/soc/codecs/aw87xxx/aw87xxx.c ++++ b/sound/soc/codecs/aw87xxx/aw87xxx.c +@@ -1,1457 +1,1466 @@ +-/* +- * aw87xxx.c aw87xxx pa module +- * +- * Copyright (c) 2021 AWINIC Technology CO., LTD +- * +- * Author: Barry +- * +- * 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. +- * +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include "aw87xxx.h" +-#include "aw87xxx_device.h" +-#include "aw87xxx_log.h" +-#include "aw87xxx_monitor.h" +-#include "aw87xxx_acf_bin.h" +-#include "aw87xxx_bin_parse.h" +-#include "aw87xxx_dsp.h" +- +-/***************************************************************** +-* aw87xxx marco +-******************************************************************/ +-#define AW87XXX_I2C_NAME "aw87xxx_pa" +-#define AW87XXX_DRIVER_VERSION "v2.7.0" +-#define AW87XXX_FW_BIN_NAME "aw87xxx_acf.bin" +-#define AW87XXX_PROF_MUSIC "Music" +-/************************************************************************* +- * aw87xxx variable +- ************************************************************************/ +-static LIST_HEAD(g_aw87xxx_list); +-static DEFINE_MUTEX(g_aw87xxx_mutex_lock); +-unsigned int g_aw87xxx_dev_cnt = 0; +- +-static const char *const aw87xxx_monitor_switch[] = {"Disable", "Enable"}; +-static const char *const aw87xxx_spin_switch[] = {"spin_0", "spin_90", +- "spin_180", "spin_270"}; +-#ifdef AW_KERNEL_VER_OVER_4_19_1 +-static struct aw_componet_codec_ops aw_componet_codec_ops = { +- .add_codec_controls = snd_soc_add_component_controls, +- .unregister_codec = snd_soc_unregister_component, +-}; +-#else +-static struct aw_componet_codec_ops aw_componet_codec_ops = { +- .add_codec_controls = snd_soc_add_codec_controls, +- .unregister_codec = snd_soc_unregister_codec, +-}; +-#endif +- +- +-/************************************************************************ +- * +- * aw87xxx device update profile +- * +- ************************************************************************/ +-static int aw87xxx_power_down(struct aw87xxx *aw87xxx, char *profile) +-{ +- int ret = 0; +- struct aw_prof_desc *prof_desc = NULL; +- struct aw_prof_info *prof_info = &aw87xxx->acf_info.prof_info; +- struct aw_data_container *data_container = NULL; +- struct aw_device *aw_dev = &aw87xxx->aw_dev; +- +- AW_DEV_LOGD(aw87xxx->dev, "enter"); +- +- if (!prof_info->status) { +- AW_DEV_LOGE(aw87xxx->dev, "profile_cfg not load"); +- return -EINVAL; +- } +- +- prof_desc = aw87xxx_acf_get_prof_desc_form_name(aw87xxx->dev, &aw87xxx->acf_info, profile); +- if (prof_desc == NULL) +- goto no_bin_pwr_off; +- +- if (!prof_desc->prof_st) +- goto no_bin_pwr_off; +- +- +- data_container = &prof_desc->data_container; +- AW_DEV_LOGD(aw87xxx->dev, "get profile[%s] data len [%d]", +- profile, data_container->len); +- +- if (aw_dev->hwen_status == AW_DEV_HWEN_OFF) { +- AW_DEV_LOGI(aw87xxx->dev, "profile[%s] has already load ", profile); +- } else { +- if (aw_dev->ops.pwr_off_func) { +- ret = aw_dev->ops.pwr_off_func(aw_dev, data_container); +- if (ret < 0) { +- AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", profile); +- goto pwr_off_failed; +- } +- } else { +- ret = aw87xxx_dev_default_pwr_off(aw_dev, data_container); +- if (ret < 0) { +- AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", profile); +- goto pwr_off_failed; +- } +- } +- } +- +- aw87xxx->current_profile = prof_desc->prof_name; +- return 0; +- +-pwr_off_failed: +-no_bin_pwr_off: +- aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, false); +- aw87xxx->current_profile = aw87xxx->prof_off_name; +- return ret; +-} +- +-static int aw87xxx_power_on(struct aw87xxx *aw87xxx, char *profile) +-{ +- int ret = -EINVAL; +- struct aw_prof_desc *prof_desc = NULL; +- struct aw_prof_info *prof_info = &aw87xxx->acf_info.prof_info; +- struct aw_data_container *data_container = NULL; +- struct aw_device *aw_dev = &aw87xxx->aw_dev; +- +- AW_DEV_LOGD(aw87xxx->dev, "enter"); +- +- if (!prof_info->status) { +- AW_DEV_LOGE(aw87xxx->dev, "profile_cfg not load"); +- return -EINVAL; +- } +- +- if (0 == strncmp(profile, aw87xxx->prof_off_name, AW_PROFILE_STR_MAX)) +- return aw87xxx_power_down(aw87xxx, profile); +- +- prof_desc = aw87xxx_acf_get_prof_desc_form_name(aw87xxx->dev, &aw87xxx->acf_info, profile); +- if (prof_desc == NULL) { +- AW_DEV_LOGE(aw87xxx->dev, "not found [%s] parameter", profile); +- return -EINVAL; +- } +- +- if (!prof_desc->prof_st) { +- AW_DEV_LOGE(aw87xxx->dev, "not found data container"); +- return -EINVAL; +- } +- +- data_container = &prof_desc->data_container; +- AW_DEV_LOGD(aw87xxx->dev, "get profile[%s] data len [%d]", +- profile, data_container->len); +- +- if (aw_dev->ops.pwr_on_func) { +- ret = aw_dev->ops.pwr_on_func(aw_dev, data_container); +- if (ret < 0) { +- AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", +- profile); +- return aw87xxx_power_down(aw87xxx, aw87xxx->prof_off_name); +- } +- } else { +- ret = aw87xxx_dev_default_pwr_on(aw_dev, data_container); +- if (ret < 0) { +- AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", +- profile); +- return aw87xxx_power_down(aw87xxx, aw87xxx->prof_off_name); +- } +- } +- +- aw87xxx->current_profile = prof_desc->prof_name; +- AW_DEV_LOGD(aw87xxx->dev, "load profile[%s] succeed", profile); +- +- return 0; +-} +- +- +- +-int aw87xxx_update_profile(struct aw87xxx *aw87xxx, char *profile) +-{ +- int ret = -1; +- +- AW_DEV_LOGD(aw87xxx->dev, "load profile[%s] enter", profile); +- mutex_lock(&aw87xxx->reg_lock); +- aw87xxx_monitor_stop(&aw87xxx->monitor); +- if (0 == strncmp(profile, aw87xxx->prof_off_name, AW_PROFILE_STR_MAX)) { +- ret = aw87xxx_power_down(aw87xxx, profile); +- } else { +- ret = aw87xxx_power_on(aw87xxx, profile); +- if (!ret) +- aw87xxx_monitor_start(&aw87xxx->monitor); +- } +- mutex_unlock(&aw87xxx->reg_lock); +- +- return ret; +-} +- +-int aw87xxx_update_profile_esd(struct aw87xxx *aw87xxx, char *profile) +-{ +- int ret = -1; +- +- if (0 == strncmp(profile, aw87xxx->prof_off_name, AW_PROFILE_STR_MAX)) +- ret = aw87xxx_power_down(aw87xxx, profile); +- else +- ret = aw87xxx_power_on(aw87xxx, profile); +- +- return ret; +-} +- +-char *aw87xxx_show_current_profile(int dev_index) +-{ +- struct list_head *pos = NULL; +- struct aw87xxx *aw87xxx = NULL; +- +- list_for_each(pos, &g_aw87xxx_list) { +- aw87xxx = list_entry(pos, struct aw87xxx, list); +- if (aw87xxx->dev_index == dev_index) { +- AW_DEV_LOGI(aw87xxx->dev, "current profile is [%s]", +- aw87xxx->current_profile); +- return aw87xxx->current_profile; +- } +- } +- +- AW_LOGE("not found struct aw87xxx, dev_index = [%d]", dev_index); +- return NULL; +-} +-EXPORT_SYMBOL(aw87xxx_show_current_profile); +- +-int aw87xxx_set_profile(int dev_index, char *profile) +-{ +- struct list_head *pos = NULL; +- struct aw87xxx *aw87xxx = NULL; +- +- list_for_each(pos, &g_aw87xxx_list) { +- aw87xxx = list_entry(pos, struct aw87xxx, list); +- if (profile && aw87xxx->dev_index == dev_index) { +- AW_DEV_LOGD(aw87xxx->dev, "set dev_index = %d, profile = %s", +- dev_index, profile); +- return aw87xxx_update_profile(aw87xxx, profile); +- } +- } +- +- AW_LOGE("not found struct aw87xxx, dev_index = [%d]", dev_index); +- return -EINVAL; +-} +-EXPORT_SYMBOL(aw87xxx_set_profile); +- +-int aw87xxx_set_profile_by_id(int dev_index, int profile_id) +-{ +- char *profile = NULL; +- +- profile = aw87xxx_ctos_get_prof_name(profile_id); +- if (profile == NULL) { +- AW_LOGE("aw87xxx, dev_index[%d] profile[%d] not support!", +- dev_index, profile_id); +- return -EINVAL; +- } +- +- AW_LOGI("aw87xxx, dev_index[%d] set profile[%s] by id[%d]", +- dev_index, profile, profile_id); +- return aw87xxx_set_profile(dev_index, profile); +-} +-EXPORT_SYMBOL(aw87xxx_set_profile_by_id); +- +-/**************************************************************************** +- * +- * aw87xxx Kcontrols +- * +- ****************************************************************************/ +-static int aw87xxx_profile_switch_info(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_info *uinfo) +-{ +- int count = 0; +- char *name = NULL; +- char *profile_name = NULL; +- struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; +- +- if (aw87xxx == NULL) { +- AW_LOGE("get struct aw87xxx failed"); +- return -EINVAL; +- } +- +- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; +- uinfo->count = 1; +- +- /*make sure have prof */ +- count = aw87xxx_acf_get_profile_count(aw87xxx->dev, &aw87xxx->acf_info); +- if (count <= 0) { +- uinfo->value.enumerated.items = 0; +- AW_DEV_LOGE(aw87xxx->dev, "get count[%d] failed", count); +- return 0; +- } +- +- uinfo->value.enumerated.items = count; +- if (uinfo->value.enumerated.item >= count) +- uinfo->value.enumerated.item = count - 1; +- +- name = uinfo->value.enumerated.name; +- count = uinfo->value.enumerated.item; +- profile_name = aw87xxx_acf_get_prof_name_form_index(aw87xxx->dev, +- &aw87xxx->acf_info, count); +- if (profile_name == NULL) { +- strscpy(uinfo->value.enumerated.name, "NULL", +- strlen("NULL") + 1); +- return 0; +- } +- +- strscpy(name, profile_name, sizeof(uinfo->value.enumerated.name)); +- +- return 0; +-} +- +-static int aw87xxx_profile_switch_put(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) +-{ +- int ret = -1; +- char *profile_name = NULL; +- int index = ucontrol->value.integer.value[0]; +- struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; +- struct acf_bin_info *acf_info = NULL; +- +- if (aw87xxx == NULL) { +- AW_LOGE("get struct aw87xxx failed"); +- return -EINVAL; +- } +- +- acf_info = &aw87xxx->acf_info; +- +- profile_name = aw87xxx_acf_get_prof_name_form_index(aw87xxx->dev, acf_info, index); +- if (!profile_name) { +- AW_DEV_LOGE(aw87xxx->dev, "not found profile name,index=[%d]", +- index); +- return -EINVAL; +- } +- +- AW_DEV_LOGI(aw87xxx->dev, "set profile [%s]", profile_name); +- +- ret = aw87xxx_update_profile(aw87xxx, profile_name); +- if (ret < 0) { +- AW_DEV_LOGE(aw87xxx->dev, "set dev_index[%d] profile failed, profile = %s", +- aw87xxx->dev_index, profile_name); +- return ret; +- } +- +- return 0; +-} +- +-static int aw87xxx_profile_switch_get(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) +-{ +- int index = 0; +- char *profile; +- struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; +- +- if (aw87xxx == NULL) { +- AW_LOGE("get struct aw87xxx failed"); +- return -EINVAL; +- } +- +- if (!aw87xxx->current_profile) { +- AW_DEV_LOGE(aw87xxx->dev, "profile not init"); +- return -EINVAL; +- } +- +- profile = aw87xxx->current_profile; +- AW_DEV_LOGI(aw87xxx->dev, "current profile:[%s]", +- aw87xxx->current_profile); +- +- +- index = aw87xxx_acf_get_prof_index_form_name(aw87xxx->dev, +- &aw87xxx->acf_info, aw87xxx->current_profile); +- if (index < 0) { +- AW_DEV_LOGE(aw87xxx->dev, "get profile index failed"); +- return index; +- } +- +- ucontrol->value.integer.value[0] = index; +- +- return 0; +-} +- +-static int aw87xxx_vmax_get_info(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_info *uinfo) +-{ +- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; +- uinfo->count = 1; +- uinfo->value.integer.min = INT_MIN; +- uinfo->value.integer.max = AW_VMAX_MAX; +- +- return 0; +-} +- +-static int aw87xxx_vmax_get(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) +-{ +- int ret = -1; +- int vmax_val = 0; +- struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; +- +- if (aw87xxx == NULL) { +- AW_LOGE("get struct aw87xxx failed"); +- return -EINVAL; +- } +- +- ret = aw87xxx_monitor_no_dsp_get_vmax(&aw87xxx->monitor, &vmax_val); +- if (ret < 0) +- return ret; +- +- ucontrol->value.integer.value[0] = vmax_val; +- AW_DEV_LOGI(aw87xxx->dev, "get vmax = [0x%x]", vmax_val); +- +- return 0; +-} +- +-static int aw87xxx_monitor_switch_info(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_info *uinfo) +-{ +- int count; +- +- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; +- uinfo->count = 1; +- count = ARRAY_SIZE(aw87xxx_monitor_switch); +- +- uinfo->value.enumerated.items = count; +- +- if (uinfo->value.enumerated.item >= count) +- uinfo->value.enumerated.item = count - 1; +- +- strscpy(uinfo->value.enumerated.name, +- aw87xxx_monitor_switch[uinfo->value.enumerated.item], +- strlen(aw87xxx_monitor_switch[uinfo->value.enumerated.item]) + 1); +- +- return 0; +-} +- +-static int aw87xxx_monitor_switch_put(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) +-{ +- uint32_t ctrl_value = ucontrol->value.integer.value[0]; +- struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; +- struct aw_monitor *aw_monitor = &aw87xxx->monitor; +- int ret = -1; +- +- ret = aw87xxx_dev_monitor_switch_set(aw_monitor, ctrl_value); +- if (ret) +- return ret; +- +- return 0; +-} +- +-static int aw87xxx_monitor_switch_get(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; +- struct aw_monitor *aw_monitor = &aw87xxx->monitor; +- +- ucontrol->value.integer.value[0] = aw_monitor->monitor_hdr.monitor_switch; +- +- AW_DEV_LOGI(aw87xxx->dev, "monitor switch is %ld", ucontrol->value.integer.value[0]); +- return 0; +-} +- +-static int aw87xxx_spin_switch_info(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_info *uinfo) +-{ +- int count; +- +- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; +- uinfo->count = 1; +- count = ARRAY_SIZE(aw87xxx_spin_switch); +- +- uinfo->value.enumerated.items = count; +- +- if (uinfo->value.enumerated.item >= count) +- uinfo->value.enumerated.item = count - 1; +- +- strscpy(uinfo->value.enumerated.name, +- aw87xxx_spin_switch[uinfo->value.enumerated.item], +- strlen(aw87xxx_spin_switch[uinfo->value.enumerated.item]) + 1); +- +- return 0; +-} +- +-static int aw87xxx_spin_switch_put(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) +-{ +- uint32_t ctrl_value = 0; +- int ret = 0; +- struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; +- ctrl_value = ucontrol->value.integer.value[0]; +- +- ret = aw87xxx_dsp_set_spin(ctrl_value); +- if (ret) { +- AW_DEV_LOGE(aw87xxx->dev, "write spin failed"); +- return ret; +- } +- AW_DEV_LOGD(aw87xxx->dev, "write spin done ctrl_value=%d", ctrl_value); +- return 0; +-} +- +-static int aw87xxx_spin_switch_get(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; +- +- ucontrol->value.integer.value[0] = aw87xxx_dsp_get_spin(); +- AW_DEV_LOGD(aw87xxx->dev, "current spin is %ld", ucontrol->value.integer.value[0]); +- +- return 0; +-} +- +- +-static int aw87xxx_kcontrol_dynamic_create(struct aw87xxx *aw87xxx, +- void *codec) +-{ +- struct snd_kcontrol_new *aw87xxx_kcontrol = NULL; +- aw_snd_soc_codec_t *soc_codec = (aw_snd_soc_codec_t *)codec; +- char *kctl_name[AW87XXX_PRIVATE_KCONTROL_NUM]; +- int kcontrol_num = AW87XXX_PRIVATE_KCONTROL_NUM; +- int ret = -1; +- +- AW_DEV_LOGD(aw87xxx->dev, "enter"); +- aw87xxx->codec = soc_codec; +- +- aw87xxx_kcontrol = devm_kzalloc(aw87xxx->dev, +- sizeof(struct snd_kcontrol_new) * kcontrol_num, +- GFP_KERNEL); +- if (aw87xxx_kcontrol == NULL) { +- AW_DEV_LOGE(aw87xxx->dev, "aw87xxx_kcontrol devm_kzalloc failed"); +- return -ENOMEM; +- } +- +- kctl_name[0] = devm_kzalloc(aw87xxx->dev, AW_NAME_BUF_MAX, +- GFP_KERNEL); +- if (kctl_name[0] == NULL) +- return -ENOMEM; +- +- snprintf(kctl_name[0], AW_NAME_BUF_MAX, "aw87xxx_profile_switch_%d", +- aw87xxx->dev_index); +- +- aw87xxx_kcontrol[0].name = kctl_name[0]; +- aw87xxx_kcontrol[0].iface = SNDRV_CTL_ELEM_IFACE_MIXER; +- aw87xxx_kcontrol[0].info = aw87xxx_profile_switch_info; +- aw87xxx_kcontrol[0].get = aw87xxx_profile_switch_get; +- aw87xxx_kcontrol[0].put = aw87xxx_profile_switch_put; +- aw87xxx_kcontrol[0].private_value = (unsigned long)aw87xxx; +- +- kctl_name[1] = devm_kzalloc(aw87xxx->codec->dev, AW_NAME_BUF_MAX, +- GFP_KERNEL); +- if (kctl_name[1] == NULL) +- return -ENOMEM; +- +- snprintf(kctl_name[1], AW_NAME_BUF_MAX, "aw87xxx_vmax_get_%d", +- aw87xxx->dev_index); +- +- aw87xxx_kcontrol[1].name = kctl_name[1]; +- aw87xxx_kcontrol[1].iface = SNDRV_CTL_ELEM_IFACE_MIXER; +- aw87xxx_kcontrol[1].access = SNDRV_CTL_ELEM_ACCESS_READ; +- aw87xxx_kcontrol[1].info = aw87xxx_vmax_get_info; +- aw87xxx_kcontrol[1].get = aw87xxx_vmax_get; +- aw87xxx_kcontrol[1].private_value = (unsigned long)aw87xxx; +- +- kctl_name[2] = devm_kzalloc(aw87xxx->codec->dev, AW_NAME_BUF_MAX, +- GFP_KERNEL); +- if (kctl_name[2] == NULL) +- return -ENOMEM; +- +- snprintf(kctl_name[2], AW_NAME_BUF_MAX, "aw87xxx_monitor_switch_%d", +- aw87xxx->dev_index); +- +- aw87xxx_kcontrol[2].name = kctl_name[2]; +- aw87xxx_kcontrol[2].iface = SNDRV_CTL_ELEM_IFACE_MIXER; +- aw87xxx_kcontrol[2].info = aw87xxx_monitor_switch_info; +- aw87xxx_kcontrol[2].get = aw87xxx_monitor_switch_get; +- aw87xxx_kcontrol[2].put = aw87xxx_monitor_switch_put; +- aw87xxx_kcontrol[2].private_value = (unsigned long)aw87xxx; +- +- ret = aw_componet_codec_ops.add_codec_controls(aw87xxx->codec, +- aw87xxx_kcontrol, kcontrol_num); +- if (ret < 0) { +- AW_DEV_LOGE(aw87xxx->dev, "add codec controls failed, ret = %d", +- ret); +- return ret; +- } +- +- AW_DEV_LOGI(aw87xxx->dev, "add codec controls[%s,%s,%s]", +- aw87xxx_kcontrol[0].name, +- aw87xxx_kcontrol[1].name, +- aw87xxx_kcontrol[2].name); +- +- return 0; +-} +- +-static int aw87xxx_public_kcontrol_create(struct aw87xxx *aw87xxx, +- void *codec) +-{ +- struct snd_kcontrol_new *aw87xxx_kcontrol = NULL; +- aw_snd_soc_codec_t *soc_codec = (aw_snd_soc_codec_t *)codec; +- char *kctl_name[AW87XXX_PUBLIC_KCONTROL_NUM]; +- int kcontrol_num = AW87XXX_PUBLIC_KCONTROL_NUM; +- int ret = -1; +- +- AW_DEV_LOGD(aw87xxx->dev, "enter"); +- aw87xxx->codec = soc_codec; +- +- aw87xxx_kcontrol = devm_kzalloc(aw87xxx->dev, +- sizeof(struct snd_kcontrol_new) * kcontrol_num, +- GFP_KERNEL); +- if (aw87xxx_kcontrol == NULL) { +- AW_DEV_LOGE(aw87xxx->dev, "aw87xxx_kcontrol devm_kzalloc failed"); +- return -ENOMEM; +- } +- +- kctl_name[0] = devm_kzalloc(aw87xxx->dev, AW_NAME_BUF_MAX, +- GFP_KERNEL); +- if (kctl_name[0] == NULL) +- return -ENOMEM; +- +- snprintf(kctl_name[0], AW_NAME_BUF_MAX, "aw87xxx_spin_switch"); +- +- aw87xxx_kcontrol[0].name = kctl_name[0]; +- aw87xxx_kcontrol[0].iface = SNDRV_CTL_ELEM_IFACE_MIXER; +- aw87xxx_kcontrol[0].info = aw87xxx_spin_switch_info; +- aw87xxx_kcontrol[0].get = aw87xxx_spin_switch_get; +- aw87xxx_kcontrol[0].put = aw87xxx_spin_switch_put; +- aw87xxx_kcontrol[0].private_value = (unsigned long)aw87xxx; +- +- ret = aw_componet_codec_ops.add_codec_controls(aw87xxx->codec, +- aw87xxx_kcontrol, kcontrol_num); +- if (ret < 0) { +- AW_DEV_LOGE(aw87xxx->dev, "add codec controls failed, ret = %d", +- ret); +- return ret; +- } +- +- AW_DEV_LOGI(aw87xxx->dev, "add public codec controls[%s]", +- aw87xxx_kcontrol[0].name); +- +- return 0; +-} +- +-/**************************************************************************** +- * +- *aw87xxx kcontrol create +- * +- ****************************************************************************/ +-int aw87xxx_add_codec_controls(void *codec) +-{ +- struct list_head *pos = NULL; +- struct aw87xxx *aw87xxx = NULL; +- int ret = -1; +- +- list_for_each(pos, &g_aw87xxx_list) { +- aw87xxx = list_entry(pos, struct aw87xxx, list); +- ret = aw87xxx_kcontrol_dynamic_create(aw87xxx, codec); +- if (ret < 0) +- return ret; +- +- if (aw87xxx->dev_index == 0) { +- ret = aw87xxx_public_kcontrol_create(aw87xxx, codec); +- if (ret < 0) +- return ret; +- } +- } +- +- return 0; +-} +-EXPORT_SYMBOL(aw87xxx_add_codec_controls); +- +- +-/**************************************************************************** +- * +- * aw87xxx firmware cfg load +- * +- ***************************************************************************/ +-static void aw87xxx_fw_cfg_free(struct aw87xxx *aw87xxx) +-{ +- AW_DEV_LOGD(aw87xxx->dev, "enter"); +- aw87xxx_acf_profile_free(aw87xxx->dev, &aw87xxx->acf_info); +- aw87xxx_monitor_cfg_free(&aw87xxx->monitor); +-} +- +-static int aw87xxx_init_default_prof(struct aw87xxx *aw87xxx) +-{ +- char *profile = NULL; +- +- profile = aw87xxx_acf_get_prof_off_name(aw87xxx->dev, &aw87xxx->acf_info); +- if (profile == NULL) { +- AW_DEV_LOGE(aw87xxx->dev, "get profile off name failed"); +- return -EINVAL; +- } +- +- snprintf(aw87xxx->prof_off_name, AW_PROFILE_STR_MAX, "%s", profile); +- aw87xxx->current_profile = profile; +- AW_DEV_LOGI(aw87xxx->dev, "init profile name [%s]", +- aw87xxx->current_profile); +- +- return 0; +-} +- +-static void aw87xxx_fw_load_retry(struct aw87xxx *aw87xxx) +-{ +- struct acf_bin_info *acf_info = &aw87xxx->acf_info; +- int ram_timer_val = 2000; +- +- AW_DEV_LOGD(aw87xxx->dev, "failed to read [%s]", +- aw87xxx->fw_name); +- +- if (acf_info->load_count < AW_LOAD_FW_RETRIES) { +- AW_DEV_LOGD(aw87xxx->dev, +- "restart hrtimer to load firmware"); +- schedule_delayed_work(&aw87xxx->fw_load_work, +- msecs_to_jiffies(ram_timer_val)); +- } else { +- acf_info->load_count = 0; +- AW_DEV_LOGE(aw87xxx->dev, +- "can not load firmware,please check name or file exists"); +- return; +- } +- acf_info->load_count++; +-} +- +-static void aw87xxx_fw_load(const struct firmware *fw, void *context) +-{ +- int ret = -1; +- struct aw87xxx *aw87xxx = context; +- struct acf_bin_info *acf_info = &aw87xxx->acf_info; +- +- AW_DEV_LOGD(aw87xxx->dev, "enter"); +- +- if (!fw) { +- aw87xxx_fw_load_retry(aw87xxx); +- return; +- } +- +- AW_DEV_LOGD(aw87xxx->dev, "loaded %s - size: %ld", +- aw87xxx->fw_name, (u_long)(fw ? fw->size : 0)); +- +- mutex_lock(&aw87xxx->reg_lock); +- acf_info->fw_data = vmalloc(fw->size); +- if (!acf_info->fw_data) { +- AW_DEV_LOGE(aw87xxx->dev, "fw_data kzalloc memory failed"); +- goto exit_vmalloc_failed; +- } +- memset(acf_info->fw_data, 0, fw->size); +- memcpy(acf_info->fw_data, fw->data, fw->size); +- acf_info->fw_size = fw->size; +- +- ret = aw87xxx_acf_parse(aw87xxx->dev, &aw87xxx->acf_info); +- if (ret < 0) { +- AW_DEV_LOGE(aw87xxx->dev, "fw_data parse failed"); +- goto exit_acf_parse_failed; +- } +- +- ret = aw87xxx_init_default_prof(aw87xxx); +- if (ret < 0) { +- aw87xxx_fw_cfg_free(aw87xxx); +- goto exit_acf_parse_failed; +- } +- +- AW_DEV_LOGI(aw87xxx->dev, "acf parse succeed"); +- mutex_unlock(&aw87xxx->reg_lock); +- release_firmware(fw); +- // Updating profile to "Music" because the firmware is set to "off" during init +- aw87xxx_update_profile(aw87xxx, AW87XXX_PROF_MUSIC); +- +- return; +- +-exit_acf_parse_failed: +-exit_vmalloc_failed: +- release_firmware(fw); +- mutex_unlock(&aw87xxx->reg_lock); +-} +- +-static void aw87xxx_fw_load_work_routine(struct work_struct *work) +-{ +- struct aw87xxx *aw87xxx = container_of(work, +- struct aw87xxx, fw_load_work.work); +- struct aw_prof_info *prof_info = &aw87xxx->acf_info.prof_info; +- +- AW_DEV_LOGD(aw87xxx->dev, "enter"); +- +- if (prof_info->status == AW_ACF_WAIT) { +- request_firmware_nowait(THIS_MODULE, +-// FW_ACTION_HOTPLUG, +- FW_ACTION_UEVENT, +- aw87xxx->fw_name, +- aw87xxx->dev, +- GFP_KERNEL, aw87xxx, +- aw87xxx_fw_load); +- } +-} +- +-static void aw87xxx_fw_load_init(struct aw87xxx *aw87xxx) +-{ +-#ifdef AW_CFG_UPDATE_DELAY +- int cfg_timer_val = AW_CFG_UPDATE_DELAY_TIMER; +-#else +- int cfg_timer_val = 0; +-#endif +- AW_DEV_LOGI(aw87xxx->dev, "enter"); +- snprintf(aw87xxx->fw_name, AW87XXX_FW_NAME_MAX, "%s", AW87XXX_FW_BIN_NAME); +- aw87xxx_acf_init(&aw87xxx->aw_dev, &aw87xxx->acf_info, aw87xxx->dev_index); +- +- INIT_DELAYED_WORK(&aw87xxx->fw_load_work, aw87xxx_fw_load_work_routine); +- schedule_delayed_work(&aw87xxx->fw_load_work, +- msecs_to_jiffies(cfg_timer_val)); +-} +- +-/**************************************************************************** +- * +- *aw87xxx attribute node +- * +- ****************************************************************************/ +-static ssize_t aw87xxx_attr_get_reg(struct device *dev, +- struct device_attribute *attr, char *buf) +-{ +- ssize_t len = 0; +- int ret = 0; +- unsigned int i = 0; +- unsigned char reg_val = 0; +- struct aw87xxx *aw87xxx = dev_get_drvdata(dev); +- struct aw_device *aw_dev = &aw87xxx->aw_dev; +- +- mutex_lock(&aw87xxx->reg_lock); +- for (i = 0; i < aw_dev->reg_max_addr; i++) { +- if (!(aw_dev->reg_access[i] & AW_DEV_REG_RD_ACCESS)) +- continue; +- ret = aw87xxx_dev_i2c_read_byte(&aw87xxx->aw_dev, i, ®_val); +- if (ret < 0) { +- len += snprintf(buf + len, PAGE_SIZE - len, +- "read reg [0x%x] failed\n", i); +- AW_DEV_LOGE(aw87xxx->dev, "read reg [0x%x] failed", i); +- } else { +- len += snprintf(buf + len, PAGE_SIZE - len, +- "reg:0x%02X=0x%02X\n", i, reg_val); +- AW_DEV_LOGD(aw87xxx->dev, "reg:0x%02X=0x%02X", +- i, reg_val); +- } +- } +- mutex_unlock(&aw87xxx->reg_lock); +- +- return len; +-} +- +-static ssize_t aw87xxx_attr_set_reg(struct device *dev, +- struct device_attribute *attr, const char *buf, +- size_t len) +-{ +- unsigned int databuf[2] = { 0 }; +- int ret = 0; +- struct aw87xxx *aw87xxx = dev_get_drvdata(dev); +- +- mutex_lock(&aw87xxx->reg_lock); +- if (sscanf(buf, "0x%x 0x%x", &databuf[0], &databuf[1]) == 2) { +- if (databuf[0] >= aw87xxx->aw_dev.reg_max_addr) { +- AW_DEV_LOGE(aw87xxx->dev, "set reg[0x%x] error,is out of reg_addr_max[0x%x]", +- databuf[0], aw87xxx->aw_dev.reg_max_addr); +- mutex_unlock(&aw87xxx->reg_lock); +- return -EINVAL; +- } +- +- ret = aw87xxx_dev_i2c_write_byte(&aw87xxx->aw_dev, +- databuf[0], databuf[1]); +- if (ret < 0) +- AW_DEV_LOGE(aw87xxx->dev, "set [0x%x]=0x%x failed", +- databuf[0], databuf[1]); +- else +- AW_DEV_LOGD(aw87xxx->dev, "set [0x%x]=0x%x succeed", +- databuf[0], databuf[1]); +- } else { +- AW_DEV_LOGE(aw87xxx->dev, "i2c write cmd input error"); +- } +- mutex_unlock(&aw87xxx->reg_lock); +- +- return len; +-} +- +-static ssize_t aw87xxx_attr_get_profile(struct device *dev, +- struct device_attribute *attr, char *buf) +-{ +- ssize_t len = 0; +- unsigned int i = 0; +- struct aw87xxx *aw87xxx = dev_get_drvdata(dev); +- struct aw_prof_info *prof_info = &aw87xxx->acf_info.prof_info; +- +- if (!prof_info->status) { +- len += snprintf(buf + len, PAGE_SIZE - len, +- "profile_cfg not load\n"); +- return len; +- } +- +- AW_DEV_LOGI(aw87xxx->dev, "current profile:[%s]", aw87xxx->current_profile); +- +- for (i = 0; i < prof_info->count; i++) { +- if (!strncmp(aw87xxx->current_profile, prof_info->prof_name_list[i], +- AW_PROFILE_STR_MAX)) +- len += snprintf(buf + len, PAGE_SIZE - len, +- ">%s\n", prof_info->prof_name_list[i]); +- else +- len += snprintf(buf + len, PAGE_SIZE - len, +- " %s\n", prof_info->prof_name_list[i]); +- } +- +- return len; +-} +- +-static ssize_t aw87xxx_attr_set_profile(struct device *dev, +- struct device_attribute *attr, const char *buf, +- size_t len) +-{ +- char profile[AW_PROFILE_STR_MAX] = {0}; +- int ret = 0; +- struct aw87xxx *aw87xxx = dev_get_drvdata(dev); +- +- if (strlen(buf) > AW_PROFILE_STR_MAX) { +- AW_DEV_LOGE(aw87xxx->dev, "input profile_str_len is out of max[%d]", +- AW_PROFILE_STR_MAX); +- return -EINVAL; +- } +- +- if (sscanf(buf, "%s", profile) == 1) { +- AW_DEV_LOGD(aw87xxx->dev, "set profile [%s]", profile); +- ret = aw87xxx_update_profile(aw87xxx, profile); +- if (ret < 0) { +- AW_DEV_LOGE(aw87xxx->dev, "set profile[%s] failed", +- profile); +- return ret; +- } +- } +- +- return len; +-} +- +-static ssize_t aw87xxx_attr_get_hwen(struct device *dev, +- struct device_attribute *attr, char *buf) +-{ +- ssize_t len = 0; +- struct aw87xxx *aw87xxx = dev_get_drvdata(dev); +- int hwen = aw87xxx->aw_dev.hwen_status; +- +- if (hwen >= AW_DEV_HWEN_INVALID) +- len += snprintf(buf + len, PAGE_SIZE - len, "hwen_status: invalid\n"); +- else if (hwen == AW_DEV_HWEN_ON) +- len += snprintf(buf + len, PAGE_SIZE - len, "hwen_status: on\n"); +- else if (hwen == AW_DEV_HWEN_OFF) +- len += snprintf(buf + len, PAGE_SIZE - len, "hwen_status: off\n"); +- +- return len; +-} +- +-static ssize_t aw87xxx_attr_set_hwen(struct device *dev, +- struct device_attribute *attr, const char *buf, +- size_t len) +-{ +- int ret = -1; +- unsigned int state; +- struct aw87xxx *aw87xxx = dev_get_drvdata(dev); +- +- ret = kstrtouint(buf, 0, &state); +- if (ret) { +- AW_DEV_LOGE(aw87xxx->dev, "fail to channelge str to int"); +- return ret; +- } +- +- mutex_lock(&aw87xxx->reg_lock); +- if (state == AW_DEV_HWEN_OFF) +- aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, false); /*OFF*/ +- else if (state == AW_DEV_HWEN_ON) +- aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, true); /*ON*/ +- else +- AW_DEV_LOGE(aw87xxx->dev, "input [%d] error, hwen_on=[%d],hwen_off=[%d]", +- state, AW_DEV_HWEN_ON, AW_DEV_HWEN_OFF); +- mutex_unlock(&aw87xxx->reg_lock); +- return len; +-} +- +-int aw87xxx_awrw_write(struct aw87xxx *aw87xxx, +- const char *buf, size_t count) +-{ +- int i = 0, ret = -1; +- char *data_buf = NULL; +- int buf_len = 0; +- int temp_data = 0; +- int data_str_size = 0; +- char *reg_data; +- struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; +- +- AW_DEV_LOGD(aw87xxx->dev, "enter"); +- /* one addr or one data string Composition of Contains two bytes of symbol(0X)*/ +- /* and two byte of hexadecimal data*/ +- data_str_size = 2 + 2 * AWRW_DATA_BYTES; +- +- /* The buf includes the first address of the register to be written and all data */ +- buf_len = AWRW_ADDR_BYTES + packet->reg_num * AWRW_DATA_BYTES; +- AW_DEV_LOGI(aw87xxx->dev, "buf_len = %d,reg_num = %d", buf_len, packet->reg_num); +- data_buf = vmalloc(buf_len); +- if (data_buf == NULL) { +- AW_DEV_LOGE(aw87xxx->dev, "alloc memory failed"); +- return -ENOMEM; +- } +- memset(data_buf, 0, buf_len); +- +- data_buf[0] = packet->reg_addr; +- reg_data = data_buf + 1; +- +- AW_DEV_LOGD(aw87xxx->dev, "reg_addr: 0x%02x", data_buf[0]); +- +- /*ag:0x00 0x01 0x01 0x01 0x01 0x00\x0a*/ +- for (i = 0; i < packet->reg_num; i++) { +- ret = sscanf(buf + AWRW_HDR_LEN + 1 + i * (data_str_size + 1), +- "0x%x", &temp_data); +- if (ret != 1) { +- AW_DEV_LOGE(aw87xxx->dev, "sscanf failed,ret=%d", ret); +- vfree(data_buf); +- data_buf = NULL; +- return ret; +- } +- reg_data[i] = temp_data; +- AW_DEV_LOGD(aw87xxx->dev, "[%d] : 0x%02x", i, reg_data[i]); +- } +- +- mutex_lock(&aw87xxx->reg_lock); +- ret = i2c_master_send(aw87xxx->aw_dev.i2c, data_buf, buf_len); +- if (ret < 0) { +- AW_DEV_LOGE(aw87xxx->dev, "write failed"); +- vfree(data_buf); +- data_buf = NULL; +- return -EFAULT; +- } +- mutex_unlock(&aw87xxx->reg_lock); +- +- vfree(data_buf); +- data_buf = NULL; +- +- AW_DEV_LOGD(aw87xxx->dev, "down"); +- return 0; +-} +- +-static int aw87xxx_awrw_data_check(struct aw87xxx *aw87xxx, +- int *data, size_t count) +-{ +- struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; +- int req_data_len = 0; +- int act_data_len = 0; +- int data_str_size = 0; +- +- if ((data[AWRW_HDR_ADDR_BYTES] != AWRW_ADDR_BYTES) || +- (data[AWRW_HDR_DATA_BYTES] != AWRW_DATA_BYTES)) { +- AW_DEV_LOGE(aw87xxx->dev, "addr_bytes [%d] or data_bytes [%d] unsupport", +- data[AWRW_HDR_ADDR_BYTES], data[AWRW_HDR_DATA_BYTES]); +- return -EINVAL; +- } +- +- /* one data string Composition of Contains two bytes of symbol(0x)*/ +- /* and two byte of hexadecimal data*/ +- data_str_size = 2 + 2 * AWRW_DATA_BYTES; +- act_data_len = count - AWRW_HDR_LEN - 1; +- +- /* There is a comma(,) or space between each piece of data */ +- if (data[AWRW_HDR_WR_FLAG] == AWRW_FLAG_WRITE) { +- /*ag:0x00 0x01 0x01 0x01 0x01 0x00\x0a*/ +- req_data_len = (data_str_size + 1) * packet->reg_num; +- if (req_data_len > act_data_len) { +- AW_DEV_LOGE(aw87xxx->dev, "data_len checkfailed,requeset data_len [%d],actaul data_len [%d]", +- req_data_len, act_data_len); +- return -EINVAL; +- } +- } +- +- return 0; +-} +- +-/* flag addr_bytes data_bytes reg_num reg_addr*/ +-static int aw87xxx_awrw_parse_buf(struct aw87xxx *aw87xxx, +- const char *buf, size_t count, int *wr_status) +-{ +- int data[AWRW_HDR_MAX] = {0}; +- struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; +- int ret = -1; +- +- if (sscanf(buf, "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x", +- &data[AWRW_HDR_WR_FLAG], &data[AWRW_HDR_ADDR_BYTES], +- &data[AWRW_HDR_DATA_BYTES], &data[AWRW_HDR_REG_NUM], +- &data[AWRW_HDR_REG_ADDR]) == 5) { +- +- packet->reg_addr = data[AWRW_HDR_REG_ADDR]; +- packet->reg_num = data[AWRW_HDR_REG_NUM]; +- *wr_status = data[AWRW_HDR_WR_FLAG]; +- ret = aw87xxx_awrw_data_check(aw87xxx, data, count); +- if (ret < 0) +- return ret; +- +- return 0; +- } +- +- return -EINVAL; +-} +- +-static ssize_t aw87xxx_attr_awrw_store(struct device *dev, +- struct device_attribute *attr, const char *buf, size_t count) +-{ +- struct aw87xxx *aw87xxx = dev_get_drvdata(dev); +- struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; +- int wr_status = 0; +- int ret = -1; +- +- if (count < AWRW_HDR_LEN) { +- AW_DEV_LOGE(aw87xxx->dev, "data count too smaller, please check write format"); +- AW_DEV_LOGE(aw87xxx->dev, "string %s,count=%ld", +- buf, (u_long)count); +- return -EINVAL; +- } +- +- AW_DEV_LOGI(aw87xxx->dev, "string:[%s],count=%ld", buf, (u_long)count); +- ret = aw87xxx_awrw_parse_buf(aw87xxx, buf, count, &wr_status); +- if (ret < 0) { +- AW_DEV_LOGE(aw87xxx->dev, "can not parse string"); +- return ret; +- } +- +- if (wr_status == AWRW_FLAG_WRITE) { +- ret = aw87xxx_awrw_write(aw87xxx, buf, count); +- if (ret < 0) +- return ret; +- } else if (wr_status == AWRW_FLAG_READ) { +- packet->status = AWRW_I2C_ST_READ; +- AW_DEV_LOGI(aw87xxx->dev, "read_cmd:reg_addr[0x%02x], reg_num[%d]", +- packet->reg_addr, packet->reg_num); +- } else { +- AW_DEV_LOGE(aw87xxx->dev, "please check str format, unsupport read_write_status: %d", +- wr_status); +- return -EINVAL; +- } +- +- return count; +-} +- +-static ssize_t aw87xxx_attr_awrw_show(struct device *dev, +- struct device_attribute *attr, char *buf) +-{ +- struct aw87xxx *aw87xxx = dev_get_drvdata(dev); +- struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; +- int data_len = 0; +- size_t len = 0; +- int ret = -1, i = 0; +- char *reg_data = NULL; +- +- if (packet->status != AWRW_I2C_ST_READ) { +- AW_DEV_LOGE(aw87xxx->dev, "please write read cmd first"); +- return -EINVAL; +- } +- +- data_len = AWRW_DATA_BYTES * packet->reg_num; +- reg_data = (char *)vmalloc(data_len); +- if (reg_data == NULL) { +- AW_DEV_LOGE(aw87xxx->dev, "memory alloc failed"); +- ret = -EINVAL; +- goto exit; +- } +- +- mutex_lock(&aw87xxx->reg_lock); +- ret = aw87xxx_dev_i2c_read_msg(&aw87xxx->aw_dev, packet->reg_addr, +- (char *)reg_data, data_len); +- if (ret < 0) { +- ret = -EFAULT; +- mutex_unlock(&aw87xxx->reg_lock); +- goto exit; +- } +- mutex_unlock(&aw87xxx->reg_lock); +- +- AW_DEV_LOGI(aw87xxx->dev, "reg_addr 0x%02x, reg_num %d", +- packet->reg_addr, packet->reg_num); +- +- for (i = 0; i < data_len; i++) { +- len += snprintf(buf + len, PAGE_SIZE - len, +- "0x%02x,", reg_data[i]); +- AW_DEV_LOGI(aw87xxx->dev, "0x%02x", reg_data[i]); +- } +- +- ret = len; +- +-exit: +- if (reg_data) { +- vfree(reg_data); +- reg_data = NULL; +- } +- packet->status = AWRW_I2C_ST_NONE; +- return ret; +-} +- +-static ssize_t aw87xxx_drv_ver_show(struct device *dev, +- struct device_attribute *attr, char *buf) +-{ +- ssize_t len = 0; +- +- len += snprintf(buf + len, PAGE_SIZE - len, +- "driver_ver: %s \n", AW87XXX_DRIVER_VERSION); +- +- return len; +-} +- +-static DEVICE_ATTR(reg, S_IWUSR | S_IRUGO, +- aw87xxx_attr_get_reg, aw87xxx_attr_set_reg); +-static DEVICE_ATTR(profile, S_IWUSR | S_IRUGO, +- aw87xxx_attr_get_profile, aw87xxx_attr_set_profile); +-static DEVICE_ATTR(hwen, S_IWUSR | S_IRUGO, +- aw87xxx_attr_get_hwen, aw87xxx_attr_set_hwen); +-static DEVICE_ATTR(awrw, S_IWUSR | S_IRUGO, +- aw87xxx_attr_awrw_show, aw87xxx_attr_awrw_store); +-static DEVICE_ATTR(drv_ver, S_IRUGO, aw87xxx_drv_ver_show, NULL); +- +-static struct attribute *aw87xxx_attributes[] = { +- &dev_attr_reg.attr, +- &dev_attr_profile.attr, +- &dev_attr_hwen.attr, +- &dev_attr_awrw.attr, +- &dev_attr_drv_ver.attr, +- NULL +-}; +- +-static struct attribute_group aw87xxx_attribute_group = { +- .attrs = aw87xxx_attributes +-}; +- +-/**************************************************************************** +- * +- *aw87xxx device probe +- * +- ****************************************************************************/ +-static const struct acpi_gpio_params reset_gpio = { 0, 0, false }; +-static const struct acpi_gpio_mapping reset_acpi_gpios[] = { +- { "reset-gpios", &reset_gpio, 1 }, +- { } +-}; +- +-static struct aw87xxx *aw87xxx_malloc_init(struct i2c_client *client) +-{ +- struct aw87xxx *aw87xxx = NULL; +- +- aw87xxx = devm_kzalloc(&client->dev, sizeof(struct aw87xxx), +- GFP_KERNEL); +- if (aw87xxx == NULL) { +- AW_DEV_LOGE(&client->dev, "failed to devm_kzalloc aw87xxx"); +- return NULL; +- } +- memset(aw87xxx, 0, sizeof(struct aw87xxx)); +- +- aw87xxx->dev = &client->dev; +- aw87xxx->aw_dev.dev = &client->dev; +- aw87xxx->aw_dev.i2c_bus = client->adapter->nr; +- aw87xxx->aw_dev.i2c_addr = client->addr; +- aw87xxx->aw_dev.i2c = client; +- aw87xxx->aw_dev.hwen_status = false; +- aw87xxx->aw_dev.reg_access = NULL; +- aw87xxx->aw_dev.hwen_status = AW_DEV_HWEN_INVALID; +- aw87xxx->off_bin_status = AW87XXX_NO_OFF_BIN; +- aw87xxx->codec = NULL; +- aw87xxx->current_profile = aw87xxx->prof_off_name; +- +- mutex_init(&aw87xxx->reg_lock); +- +- AW_DEV_LOGI(&client->dev, "struct aw87xxx devm_kzalloc and init down"); +- return aw87xxx; +-} +- +-static int aw87xxx_i2c_probe(struct i2c_client *client) +-{ +- struct device_node *dev_node = client->dev.of_node; +- struct aw87xxx *aw87xxx = NULL; +- struct gpio_desc *gpiod = NULL; +- int ret = -1; +- +- +-// To do, add this function +-//acpi_dev_add_driver_gpios() +- +- if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { +- AW_DEV_LOGE(&client->dev, "check_functionality failed"); +- ret = -ENODEV; +- goto exit_check_functionality_failed; +- } +- +- /* aw87xxx i2c_dev struct init */ +- aw87xxx = aw87xxx_malloc_init(client); +- if (aw87xxx == NULL) +- goto exit_malloc_init_failed; +- +- i2c_set_clientdata(client, aw87xxx); +- +- aw87xxx_device_parse_port_id_dt(&aw87xxx->aw_dev); +- aw87xxx_device_parse_topo_id_dt(&aw87xxx->aw_dev); +- +- /* aw87xxx Get ACPI GPIO */ +-/* +- ret = devm_acpi_dev_add_driver_gpios(aw87xxx->dev, reset_acpi_gpios); +- if(ret){ +- AW_DEV_LOGE(aw87xxx->dev, "Unable to add GPIO mapping table"); +- goto exit_device_init_failed; +- } +- +- gpiod = gpiod_get_optional(aw87xxx->dev, "reset", GPIOD_OUT_LOW); +- if (IS_ERR(gpiod)){ +- AW_DEV_LOGE(aw87xxx->dev, "Get gpiod failed"); +- goto exit_device_init_failed; +- } +- +- aw87xxx->aw_dev.rst_gpio = desc_to_gpio(gpiod); +- aw87xxx->aw_dev.hwen_status = AW_DEV_HWEN_OFF; +- AW_DEV_LOGI(aw87xxx->dev, "reset gpio[%d] parse succeed", aw87xxx->aw_dev.rst_gpio); +- if (gpio_is_valid(aw87xxx->aw_dev.rst_gpio)) { +- ret = devm_gpio_request_one(aw87xxx->dev, aw87xxx->aw_dev.rst_gpio, GPIOF_OUT_INIT_LOW, "aw87xxx_reset"); +- if (ret < 0) { +- AW_DEV_LOGE(aw87xxx->dev, "reset request failed"); +- goto exit_device_init_failed; +- } +- } +-*/ +- +- /*Disabling RESET GPIO*/ +- AW_DEV_LOGI(aw87xxx->dev, "no reset gpio provided, hardware reset unavailable"); +- aw87xxx->aw_dev.rst_gpio = AW_NO_RESET_GPIO; +- aw87xxx->aw_dev.hwen_status = AW_DEV_HWEN_INVALID; +- +- +- /*hw power on PA*/ +- aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, true); +- +- /* aw87xxx devices private attributes init */ +- ret = aw87xxx_dev_init(&aw87xxx->aw_dev); +- if (ret < 0) +- goto exit_device_init_failed; +- +- /*product register reset */ +- aw87xxx_dev_soft_reset(&aw87xxx->aw_dev); +- +- /*hw power off */ +- aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, false); +- +- /* create debug attrbute nodes */ +- ret = sysfs_create_group(&aw87xxx->dev->kobj, &aw87xxx_attribute_group); +- if (ret < 0) +- AW_DEV_LOGE(aw87xxx->dev, "failed to create sysfs nodes, will not allowed to use"); +- +- /* cfg_load init */ +- aw87xxx_fw_load_init(aw87xxx); +- +- /*monitor init*/ +- aw87xxx_monitor_init(aw87xxx->dev, &aw87xxx->monitor, dev_node); +- +- /*add device to total list */ +- mutex_lock(&g_aw87xxx_mutex_lock); +- g_aw87xxx_dev_cnt++; +- list_add(&aw87xxx->list, &g_aw87xxx_list); +- aw87xxx->dev_index = g_aw87xxx_dev_cnt; +- +- mutex_unlock(&g_aw87xxx_mutex_lock); +- AW_DEV_LOGI(aw87xxx->dev, "succeed, dev_index=[%d], g_aw87xxx_dev_cnt= [%d]", +- aw87xxx->dev_index, g_aw87xxx_dev_cnt); +- +- return 0; +- +-exit_device_init_failed: +- AW_DEV_LOGE(aw87xxx->dev, "pa init failed"); +- +- devm_kfree(&client->dev, aw87xxx); +- aw87xxx = NULL; +-exit_malloc_init_failed: +-exit_check_functionality_failed: +- return ret; +-} +- +-static void aw87xxx_i2c_remove(struct i2c_client *client) +-{ +- struct aw87xxx *aw87xxx = i2c_get_clientdata(client); +- +- aw87xxx_monitor_exit(&aw87xxx->monitor); +- +- /*rm attr node*/ +- sysfs_remove_group(&aw87xxx->dev->kobj, &aw87xxx_attribute_group); +- +- aw87xxx_fw_cfg_free(aw87xxx); +- +- mutex_lock(&g_aw87xxx_mutex_lock); +- g_aw87xxx_dev_cnt--; +- list_del(&aw87xxx->list); +- mutex_unlock(&g_aw87xxx_mutex_lock); +- +- devm_kfree(&client->dev, aw87xxx); +- aw87xxx = NULL; +- +-// return 0; +-} +- +-static void aw87xxx_i2c_shutdown(struct i2c_client *client) +-{ +- struct aw87xxx *aw87xxx = i2c_get_clientdata(client); +- +- AW_DEV_LOGI(&client->dev, "enter"); +- +- /*soft and hw power off*/ +- aw87xxx_update_profile(aw87xxx, aw87xxx->prof_off_name); +-} +- +-static const struct acpi_device_id aw87xxx_acpi_match[] = { +- { "AWDZ8830", 0 }, +- { } +-}; +-MODULE_DEVICE_TABLE(acpi, aw87xxx_acpi_match); +- +-// This is not necessary if the acpi match probes correctly. This is needed for userspace `new_device() functionality +-static const struct i2c_device_id aw87xxx_i2c_id[] = { +- {AW87XXX_I2C_NAME, 0}, +- {}, +-}; +- +-static struct i2c_driver aw87xxx_i2c_driver = { +- .driver = { +- .owner = THIS_MODULE, +- .name = AW87XXX_I2C_NAME, +- .acpi_match_table = aw87xxx_acpi_match, +- }, +- .probe = aw87xxx_i2c_probe, +- .remove = aw87xxx_i2c_remove, +- .shutdown = aw87xxx_i2c_shutdown, +- .id_table = aw87xxx_i2c_id, +-}; +- +-module_i2c_driver(aw87xxx_i2c_driver) +- +-MODULE_AUTHOR(""); +-MODULE_DESCRIPTION("awinic aw87xxx pa driver"); +-MODULE_LICENSE("GPL v2"); ++/* ++ * aw87xxx.c aw87xxx pa module ++ * ++ * Copyright (c) 2021 AWINIC Technology CO., LTD ++ * ++ * Author: Barry ++ * ++ * 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. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "aw87xxx.h" ++#include "aw87xxx_device.h" ++#include "aw87xxx_log.h" ++#include "aw87xxx_monitor.h" ++#include "aw87xxx_acf_bin.h" ++#include "aw87xxx_bin_parse.h" ++#include "aw87xxx_dsp.h" ++ ++/***************************************************************** ++* aw87xxx marco ++******************************************************************/ ++#define AW87XXX_I2C_NAME "aw87xxx_pa" ++#define AW87XXX_DRIVER_VERSION "v2.7.0" ++#define AW87XXX_FW_BIN_NAME "aw87xxx_acf.bin" ++#define AW87XXX_PROF_MUSIC "Music" ++/************************************************************************* ++ * aw87xxx variable ++ ************************************************************************/ ++static LIST_HEAD(g_aw87xxx_list); ++static DEFINE_MUTEX(g_aw87xxx_mutex_lock); ++unsigned int g_aw87xxx_dev_cnt = 0; ++ ++static const char *const aw87xxx_monitor_switch[] = {"Disable", "Enable"}; ++static const char *const aw87xxx_spin_switch[] = {"spin_0", "spin_90", ++ "spin_180", "spin_270"}; ++#ifdef AW_KERNEL_VER_OVER_4_19_1 ++static struct aw_componet_codec_ops aw_componet_codec_ops = { ++ .add_codec_controls = snd_soc_add_component_controls, ++ .unregister_codec = snd_soc_unregister_component, ++}; ++#else ++static struct aw_componet_codec_ops aw_componet_codec_ops = { ++ .add_codec_controls = snd_soc_add_codec_controls, ++ .unregister_codec = snd_soc_unregister_codec, ++}; ++#endif ++ ++ ++/************************************************************************ ++ * ++ * aw87xxx device update profile ++ * ++ ************************************************************************/ ++static int aw87xxx_power_down(struct aw87xxx *aw87xxx, char *profile) ++{ ++ int ret = 0; ++ struct aw_prof_desc *prof_desc = NULL; ++ struct aw_prof_info *prof_info = &aw87xxx->acf_info.prof_info; ++ struct aw_data_container *data_container = NULL; ++ struct aw_device *aw_dev = &aw87xxx->aw_dev; ++ ++ AW_DEV_LOGD(aw87xxx->dev, "enter"); ++ ++ if (!prof_info->status) { ++ AW_DEV_LOGE(aw87xxx->dev, "profile_cfg not load"); ++ return -EINVAL; ++ } ++ ++ prof_desc = aw87xxx_acf_get_prof_desc_form_name(aw87xxx->dev, &aw87xxx->acf_info, profile); ++ if (prof_desc == NULL) ++ goto no_bin_pwr_off; ++ ++ if (!prof_desc->prof_st) ++ goto no_bin_pwr_off; ++ ++ ++ data_container = &prof_desc->data_container; ++ AW_DEV_LOGD(aw87xxx->dev, "get profile[%s] data len [%d]", ++ profile, data_container->len); ++ ++ if (aw_dev->hwen_status == AW_DEV_HWEN_OFF) { ++ AW_DEV_LOGI(aw87xxx->dev, "profile[%s] has already load ", profile); ++ } else { ++ if (aw_dev->ops.pwr_off_func) { ++ ret = aw_dev->ops.pwr_off_func(aw_dev, data_container); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", profile); ++ goto pwr_off_failed; ++ } ++ } else { ++ ret = aw87xxx_dev_default_pwr_off(aw_dev, data_container); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", profile); ++ goto pwr_off_failed; ++ } ++ } ++ } ++ ++ aw87xxx->current_profile = prof_desc->prof_name; ++ return 0; ++ ++pwr_off_failed: ++no_bin_pwr_off: ++ aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, false); ++ aw87xxx->current_profile = aw87xxx->prof_off_name; ++ return ret; ++} ++ ++static int aw87xxx_power_on(struct aw87xxx *aw87xxx, char *profile) ++{ ++ int ret = -EINVAL; ++ struct aw_prof_desc *prof_desc = NULL; ++ struct aw_prof_info *prof_info = &aw87xxx->acf_info.prof_info; ++ struct aw_data_container *data_container = NULL; ++ struct aw_device *aw_dev = &aw87xxx->aw_dev; ++ ++ AW_DEV_LOGD(aw87xxx->dev, "enter"); ++ ++ if (!prof_info->status) { ++ AW_DEV_LOGE(aw87xxx->dev, "profile_cfg not load"); ++ return -EINVAL; ++ } ++ ++ if (0 == strncmp(profile, aw87xxx->prof_off_name, AW_PROFILE_STR_MAX)) ++ return aw87xxx_power_down(aw87xxx, profile); ++ ++ prof_desc = aw87xxx_acf_get_prof_desc_form_name(aw87xxx->dev, &aw87xxx->acf_info, profile); ++ if (prof_desc == NULL) { ++ AW_DEV_LOGE(aw87xxx->dev, "not found [%s] parameter", profile); ++ return -EINVAL; ++ } ++ ++ if (!prof_desc->prof_st) { ++ AW_DEV_LOGE(aw87xxx->dev, "not found data container"); ++ return -EINVAL; ++ } ++ ++ data_container = &prof_desc->data_container; ++ AW_DEV_LOGD(aw87xxx->dev, "get profile[%s] data len [%d]", ++ profile, data_container->len); ++ ++ if (aw_dev->ops.pwr_on_func) { ++ ret = aw_dev->ops.pwr_on_func(aw_dev, data_container); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", ++ profile); ++ return aw87xxx_power_down(aw87xxx, aw87xxx->prof_off_name); ++ } ++ } else { ++ ret = aw87xxx_dev_default_pwr_on(aw_dev, data_container); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", ++ profile); ++ return aw87xxx_power_down(aw87xxx, aw87xxx->prof_off_name); ++ } ++ } ++ ++ aw87xxx->current_profile = prof_desc->prof_name; ++ AW_DEV_LOGD(aw87xxx->dev, "load profile[%s] succeed", profile); ++ ++ return 0; ++} ++ ++ ++ ++int aw87xxx_update_profile(struct aw87xxx *aw87xxx, char *profile) ++{ ++ int ret = -1; ++ ++ AW_DEV_LOGD(aw87xxx->dev, "load profile[%s] enter", profile); ++ mutex_lock(&aw87xxx->reg_lock); ++ aw87xxx_monitor_stop(&aw87xxx->monitor); ++ if (0 == strncmp(profile, aw87xxx->prof_off_name, AW_PROFILE_STR_MAX)) { ++ ret = aw87xxx_power_down(aw87xxx, profile); ++ } else { ++ ret = aw87xxx_power_on(aw87xxx, profile); ++ if (!ret) ++ aw87xxx_monitor_start(&aw87xxx->monitor); ++ } ++ mutex_unlock(&aw87xxx->reg_lock); ++ ++ return ret; ++} ++ ++int aw87xxx_update_profile_esd(struct aw87xxx *aw87xxx, char *profile) ++{ ++ int ret = -1; ++ ++ if (0 == strncmp(profile, aw87xxx->prof_off_name, AW_PROFILE_STR_MAX)) ++ ret = aw87xxx_power_down(aw87xxx, profile); ++ else ++ ret = aw87xxx_power_on(aw87xxx, profile); ++ ++ return ret; ++} ++ ++char *aw87xxx_show_current_profile(int dev_index) ++{ ++ struct list_head *pos = NULL; ++ struct aw87xxx *aw87xxx = NULL; ++ ++ list_for_each(pos, &g_aw87xxx_list) { ++ aw87xxx = list_entry(pos, struct aw87xxx, list); ++ if (aw87xxx->dev_index == dev_index) { ++ AW_DEV_LOGI(aw87xxx->dev, "current profile is [%s]", ++ aw87xxx->current_profile); ++ return aw87xxx->current_profile; ++ } ++ } ++ ++ AW_LOGE("not found struct aw87xxx, dev_index = [%d]", dev_index); ++ return NULL; ++} ++EXPORT_SYMBOL(aw87xxx_show_current_profile); ++ ++int aw87xxx_set_profile(int dev_index, char *profile) ++{ ++ struct list_head *pos = NULL; ++ struct aw87xxx *aw87xxx = NULL; ++ ++ list_for_each(pos, &g_aw87xxx_list) { ++ aw87xxx = list_entry(pos, struct aw87xxx, list); ++ if (profile && aw87xxx->dev_index == dev_index) { ++ AW_DEV_LOGD(aw87xxx->dev, "set dev_index = %d, profile = %s", ++ dev_index, profile); ++ return aw87xxx_update_profile(aw87xxx, profile); ++ } ++ } ++ ++ AW_LOGE("not found struct aw87xxx, dev_index = [%d]", dev_index); ++ return -EINVAL; ++} ++EXPORT_SYMBOL(aw87xxx_set_profile); ++ ++int aw87xxx_set_profile_by_id(int dev_index, int profile_id) ++{ ++ char *profile = NULL; ++ ++ profile = aw87xxx_ctos_get_prof_name(profile_id); ++ if (profile == NULL) { ++ AW_LOGE("aw87xxx, dev_index[%d] profile[%d] not support!", ++ dev_index, profile_id); ++ return -EINVAL; ++ } ++ ++ AW_LOGI("aw87xxx, dev_index[%d] set profile[%s] by id[%d]", ++ dev_index, profile, profile_id); ++ return aw87xxx_set_profile(dev_index, profile); ++} ++EXPORT_SYMBOL(aw87xxx_set_profile_by_id); ++ ++/**************************************************************************** ++ * ++ * aw87xxx Kcontrols ++ * ++ ****************************************************************************/ ++static int aw87xxx_profile_switch_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ int count = 0; ++ char *name = NULL; ++ char *profile_name = NULL; ++ struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; ++ ++ if (aw87xxx == NULL) { ++ AW_LOGE("get struct aw87xxx failed"); ++ return -EINVAL; ++ } ++ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; ++ uinfo->count = 1; ++ ++ /*make sure have prof */ ++ count = aw87xxx_acf_get_profile_count(aw87xxx->dev, &aw87xxx->acf_info); ++ if (count <= 0) { ++ uinfo->value.enumerated.items = 0; ++ AW_DEV_LOGE(aw87xxx->dev, "get count[%d] failed", count); ++ return 0; ++ } ++ ++ uinfo->value.enumerated.items = count; ++ if (uinfo->value.enumerated.item >= count) ++ uinfo->value.enumerated.item = count - 1; ++ ++ name = uinfo->value.enumerated.name; ++ count = uinfo->value.enumerated.item; ++ profile_name = aw87xxx_acf_get_prof_name_form_index(aw87xxx->dev, ++ &aw87xxx->acf_info, count); ++ if (profile_name == NULL) { ++ strscpy(uinfo->value.enumerated.name, "NULL", ++ strlen("NULL") + 1); ++ return 0; ++ } ++ ++ strscpy(name, profile_name, sizeof(uinfo->value.enumerated.name)); ++ ++ return 0; ++} ++ ++static int aw87xxx_profile_switch_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ int ret = -1; ++ char *profile_name = NULL; ++ int index = ucontrol->value.integer.value[0]; ++ struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; ++ struct acf_bin_info *acf_info = NULL; ++ ++ if (aw87xxx == NULL) { ++ AW_LOGE("get struct aw87xxx failed"); ++ return -EINVAL; ++ } ++ ++ acf_info = &aw87xxx->acf_info; ++ ++ profile_name = aw87xxx_acf_get_prof_name_form_index(aw87xxx->dev, acf_info, index); ++ if (!profile_name) { ++ AW_DEV_LOGE(aw87xxx->dev, "not found profile name,index=[%d]", ++ index); ++ return -EINVAL; ++ } ++ ++ AW_DEV_LOGI(aw87xxx->dev, "set profile [%s]", profile_name); ++ ++ ret = aw87xxx_update_profile(aw87xxx, profile_name); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "set dev_index[%d] profile failed, profile = %s", ++ aw87xxx->dev_index, profile_name); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int aw87xxx_profile_switch_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ int index = 0; ++ char *profile; ++ struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; ++ ++ if (aw87xxx == NULL) { ++ AW_LOGE("get struct aw87xxx failed"); ++ return -EINVAL; ++ } ++ ++ if (!aw87xxx->current_profile) { ++ AW_DEV_LOGE(aw87xxx->dev, "profile not init"); ++ return -EINVAL; ++ } ++ ++ profile = aw87xxx->current_profile; ++ AW_DEV_LOGI(aw87xxx->dev, "current profile:[%s]", ++ aw87xxx->current_profile); ++ ++ ++ index = aw87xxx_acf_get_prof_index_form_name(aw87xxx->dev, ++ &aw87xxx->acf_info, aw87xxx->current_profile); ++ if (index < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "get profile index failed"); ++ return index; ++ } ++ ++ ucontrol->value.integer.value[0] = index; ++ ++ return 0; ++} ++ ++static int aw87xxx_vmax_get_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = 1; ++ uinfo->value.integer.min = INT_MIN; ++ uinfo->value.integer.max = AW_VMAX_MAX; ++ ++ return 0; ++} ++ ++static int aw87xxx_vmax_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ int ret = -1; ++ int vmax_val = 0; ++ struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; ++ ++ if (aw87xxx == NULL) { ++ AW_LOGE("get struct aw87xxx failed"); ++ return -EINVAL; ++ } ++ ++ ret = aw87xxx_monitor_no_dsp_get_vmax(&aw87xxx->monitor, &vmax_val); ++ if (ret < 0) ++ return ret; ++ ++ ucontrol->value.integer.value[0] = vmax_val; ++ AW_DEV_LOGI(aw87xxx->dev, "get vmax = [0x%x]", vmax_val); ++ ++ return 0; ++} ++ ++static int aw87xxx_monitor_switch_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ int count; ++ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; ++ uinfo->count = 1; ++ count = ARRAY_SIZE(aw87xxx_monitor_switch); ++ ++ uinfo->value.enumerated.items = count; ++ ++ if (uinfo->value.enumerated.item >= count) ++ uinfo->value.enumerated.item = count - 1; ++ ++ strscpy(uinfo->value.enumerated.name, ++ aw87xxx_monitor_switch[uinfo->value.enumerated.item], ++ strlen(aw87xxx_monitor_switch[uinfo->value.enumerated.item]) + 1); ++ ++ return 0; ++} ++ ++static int aw87xxx_monitor_switch_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ uint32_t ctrl_value = ucontrol->value.integer.value[0]; ++ struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; ++ struct aw_monitor *aw_monitor = &aw87xxx->monitor; ++ int ret = -1; ++ ++ ret = aw87xxx_dev_monitor_switch_set(aw_monitor, ctrl_value); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int aw87xxx_monitor_switch_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; ++ struct aw_monitor *aw_monitor = &aw87xxx->monitor; ++ ++ ucontrol->value.integer.value[0] = aw_monitor->monitor_hdr.monitor_switch; ++ ++ AW_DEV_LOGI(aw87xxx->dev, "monitor switch is %ld", ucontrol->value.integer.value[0]); ++ return 0; ++} ++ ++static int aw87xxx_spin_switch_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ int count; ++ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; ++ uinfo->count = 1; ++ count = ARRAY_SIZE(aw87xxx_spin_switch); ++ ++ uinfo->value.enumerated.items = count; ++ ++ if (uinfo->value.enumerated.item >= count) ++ uinfo->value.enumerated.item = count - 1; ++ ++ strscpy(uinfo->value.enumerated.name, ++ aw87xxx_spin_switch[uinfo->value.enumerated.item], ++ strlen(aw87xxx_spin_switch[uinfo->value.enumerated.item]) + 1); ++ ++ return 0; ++} ++ ++static int aw87xxx_spin_switch_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ uint32_t ctrl_value = 0; ++ int ret = 0; ++ struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; ++ ctrl_value = ucontrol->value.integer.value[0]; ++ ++ ret = aw87xxx_dsp_set_spin(ctrl_value); ++ if (ret) { ++ AW_DEV_LOGE(aw87xxx->dev, "write spin failed"); ++ return ret; ++ } ++ AW_DEV_LOGD(aw87xxx->dev, "write spin done ctrl_value=%d", ctrl_value); ++ return 0; ++} ++ ++static int aw87xxx_spin_switch_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; ++ ++ ucontrol->value.integer.value[0] = aw87xxx_dsp_get_spin(); ++ AW_DEV_LOGD(aw87xxx->dev, "current spin is %ld", ucontrol->value.integer.value[0]); ++ ++ return 0; ++} ++ ++ ++static int aw87xxx_kcontrol_dynamic_create(struct aw87xxx *aw87xxx, ++ void *codec) ++{ ++ struct snd_kcontrol_new *aw87xxx_kcontrol = NULL; ++ aw_snd_soc_codec_t *soc_codec = (aw_snd_soc_codec_t *)codec; ++ char *kctl_name[AW87XXX_PRIVATE_KCONTROL_NUM]; ++ int kcontrol_num = AW87XXX_PRIVATE_KCONTROL_NUM; ++ int ret = -1; ++ ++ AW_DEV_LOGD(aw87xxx->dev, "enter"); ++ aw87xxx->codec = soc_codec; ++ ++ aw87xxx_kcontrol = devm_kzalloc(aw87xxx->dev, ++ sizeof(struct snd_kcontrol_new) * kcontrol_num, ++ GFP_KERNEL); ++ if (aw87xxx_kcontrol == NULL) { ++ AW_DEV_LOGE(aw87xxx->dev, "aw87xxx_kcontrol devm_kzalloc failed"); ++ return -ENOMEM; ++ } ++ ++ kctl_name[0] = devm_kzalloc(aw87xxx->dev, AW_NAME_BUF_MAX, ++ GFP_KERNEL); ++ if (kctl_name[0] == NULL) ++ return -ENOMEM; ++ ++ snprintf(kctl_name[0], AW_NAME_BUF_MAX, "aw87xxx_profile_switch_%d", ++ aw87xxx->dev_index); ++ ++ aw87xxx_kcontrol[0].name = kctl_name[0]; ++ aw87xxx_kcontrol[0].iface = SNDRV_CTL_ELEM_IFACE_MIXER; ++ aw87xxx_kcontrol[0].info = aw87xxx_profile_switch_info; ++ aw87xxx_kcontrol[0].get = aw87xxx_profile_switch_get; ++ aw87xxx_kcontrol[0].put = aw87xxx_profile_switch_put; ++ aw87xxx_kcontrol[0].private_value = (unsigned long)aw87xxx; ++ ++ kctl_name[1] = devm_kzalloc(aw87xxx->codec->dev, AW_NAME_BUF_MAX, ++ GFP_KERNEL); ++ if (kctl_name[1] == NULL) ++ return -ENOMEM; ++ ++ snprintf(kctl_name[1], AW_NAME_BUF_MAX, "aw87xxx_vmax_get_%d", ++ aw87xxx->dev_index); ++ ++ aw87xxx_kcontrol[1].name = kctl_name[1]; ++ aw87xxx_kcontrol[1].iface = SNDRV_CTL_ELEM_IFACE_MIXER; ++ aw87xxx_kcontrol[1].access = SNDRV_CTL_ELEM_ACCESS_READ; ++ aw87xxx_kcontrol[1].info = aw87xxx_vmax_get_info; ++ aw87xxx_kcontrol[1].get = aw87xxx_vmax_get; ++ aw87xxx_kcontrol[1].private_value = (unsigned long)aw87xxx; ++ ++ kctl_name[2] = devm_kzalloc(aw87xxx->codec->dev, AW_NAME_BUF_MAX, ++ GFP_KERNEL); ++ if (kctl_name[2] == NULL) ++ return -ENOMEM; ++ ++ snprintf(kctl_name[2], AW_NAME_BUF_MAX, "aw87xxx_monitor_switch_%d", ++ aw87xxx->dev_index); ++ ++ aw87xxx_kcontrol[2].name = kctl_name[2]; ++ aw87xxx_kcontrol[2].iface = SNDRV_CTL_ELEM_IFACE_MIXER; ++ aw87xxx_kcontrol[2].info = aw87xxx_monitor_switch_info; ++ aw87xxx_kcontrol[2].get = aw87xxx_monitor_switch_get; ++ aw87xxx_kcontrol[2].put = aw87xxx_monitor_switch_put; ++ aw87xxx_kcontrol[2].private_value = (unsigned long)aw87xxx; ++ ++ ret = aw_componet_codec_ops.add_codec_controls(aw87xxx->codec, ++ aw87xxx_kcontrol, kcontrol_num); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "add codec controls failed, ret = %d", ++ ret); ++ return ret; ++ } ++ ++ AW_DEV_LOGI(aw87xxx->dev, "add codec controls[%s,%s,%s]", ++ aw87xxx_kcontrol[0].name, ++ aw87xxx_kcontrol[1].name, ++ aw87xxx_kcontrol[2].name); ++ ++ return 0; ++} ++ ++static int aw87xxx_public_kcontrol_create(struct aw87xxx *aw87xxx, ++ void *codec) ++{ ++ struct snd_kcontrol_new *aw87xxx_kcontrol = NULL; ++ aw_snd_soc_codec_t *soc_codec = (aw_snd_soc_codec_t *)codec; ++ char *kctl_name[AW87XXX_PUBLIC_KCONTROL_NUM]; ++ int kcontrol_num = AW87XXX_PUBLIC_KCONTROL_NUM; ++ int ret = -1; ++ ++ AW_DEV_LOGD(aw87xxx->dev, "enter"); ++ aw87xxx->codec = soc_codec; ++ ++ aw87xxx_kcontrol = devm_kzalloc(aw87xxx->dev, ++ sizeof(struct snd_kcontrol_new) * kcontrol_num, ++ GFP_KERNEL); ++ if (aw87xxx_kcontrol == NULL) { ++ AW_DEV_LOGE(aw87xxx->dev, "aw87xxx_kcontrol devm_kzalloc failed"); ++ return -ENOMEM; ++ } ++ ++ kctl_name[0] = devm_kzalloc(aw87xxx->dev, AW_NAME_BUF_MAX, ++ GFP_KERNEL); ++ if (kctl_name[0] == NULL) ++ return -ENOMEM; ++ ++ snprintf(kctl_name[0], AW_NAME_BUF_MAX, "aw87xxx_spin_switch"); ++ ++ aw87xxx_kcontrol[0].name = kctl_name[0]; ++ aw87xxx_kcontrol[0].iface = SNDRV_CTL_ELEM_IFACE_MIXER; ++ aw87xxx_kcontrol[0].info = aw87xxx_spin_switch_info; ++ aw87xxx_kcontrol[0].get = aw87xxx_spin_switch_get; ++ aw87xxx_kcontrol[0].put = aw87xxx_spin_switch_put; ++ aw87xxx_kcontrol[0].private_value = (unsigned long)aw87xxx; ++ ++ ret = aw_componet_codec_ops.add_codec_controls(aw87xxx->codec, ++ aw87xxx_kcontrol, kcontrol_num); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "add codec controls failed, ret = %d", ++ ret); ++ return ret; ++ } ++ ++ AW_DEV_LOGI(aw87xxx->dev, "add public codec controls[%s]", ++ aw87xxx_kcontrol[0].name); ++ ++ return 0; ++} ++ ++/**************************************************************************** ++ * ++ *aw87xxx kcontrol create ++ * ++ ****************************************************************************/ ++int aw87xxx_add_codec_controls(void *codec) ++{ ++ struct list_head *pos = NULL; ++ struct aw87xxx *aw87xxx = NULL; ++ int ret = -1; ++ ++ list_for_each(pos, &g_aw87xxx_list) { ++ aw87xxx = list_entry(pos, struct aw87xxx, list); ++ ret = aw87xxx_kcontrol_dynamic_create(aw87xxx, codec); ++ if (ret < 0) ++ return ret; ++ ++ if (aw87xxx->dev_index == 0) { ++ ret = aw87xxx_public_kcontrol_create(aw87xxx, codec); ++ if (ret < 0) ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(aw87xxx_add_codec_controls); ++ ++ ++/**************************************************************************** ++ * ++ * aw87xxx firmware cfg load ++ * ++ ***************************************************************************/ ++static void aw87xxx_fw_cfg_free(struct aw87xxx *aw87xxx) ++{ ++ AW_DEV_LOGD(aw87xxx->dev, "enter"); ++ aw87xxx_acf_profile_free(aw87xxx->dev, &aw87xxx->acf_info); ++ aw87xxx_monitor_cfg_free(&aw87xxx->monitor); ++} ++ ++static int aw87xxx_init_default_prof(struct aw87xxx *aw87xxx) ++{ ++ char *profile = NULL; ++ ++ profile = aw87xxx_acf_get_prof_off_name(aw87xxx->dev, &aw87xxx->acf_info); ++ if (profile == NULL) { ++ AW_DEV_LOGE(aw87xxx->dev, "get profile off name failed"); ++ return -EINVAL; ++ } ++ ++ snprintf(aw87xxx->prof_off_name, AW_PROFILE_STR_MAX, "%s", profile); ++ aw87xxx->current_profile = profile; ++ AW_DEV_LOGI(aw87xxx->dev, "init profile name [%s]", ++ aw87xxx->current_profile); ++ ++ return 0; ++} ++ ++static void aw87xxx_fw_load_retry(struct aw87xxx *aw87xxx) ++{ ++ struct acf_bin_info *acf_info = &aw87xxx->acf_info; ++ int ram_timer_val = 2000; ++ ++ AW_DEV_LOGD(aw87xxx->dev, "failed to read [%s]", ++ aw87xxx->fw_name); ++ ++ if (acf_info->load_count < AW_LOAD_FW_RETRIES) { ++ AW_DEV_LOGD(aw87xxx->dev, ++ "restart hrtimer to load firmware"); ++ schedule_delayed_work(&aw87xxx->fw_load_work, ++ msecs_to_jiffies(ram_timer_val)); ++ } else { ++ acf_info->load_count = 0; ++ AW_DEV_LOGE(aw87xxx->dev, ++ "can not load firmware,please check name or file exists"); ++ return; ++ } ++ acf_info->load_count++; ++} ++ ++static void aw87xxx_fw_load(const struct firmware *fw, void *context) ++{ ++ int ret = -1; ++ struct aw87xxx *aw87xxx = context; ++ struct acf_bin_info *acf_info = &aw87xxx->acf_info; ++ ++ AW_DEV_LOGD(aw87xxx->dev, "enter"); ++ ++ if (!fw) { ++ aw87xxx_fw_load_retry(aw87xxx); ++ return; ++ } ++ ++ AW_DEV_LOGD(aw87xxx->dev, "loaded %s - size: %ld", ++ aw87xxx->fw_name, (u_long)(fw ? fw->size : 0)); ++ ++ mutex_lock(&aw87xxx->reg_lock); ++ acf_info->fw_data = vmalloc(fw->size); ++ if (!acf_info->fw_data) { ++ AW_DEV_LOGE(aw87xxx->dev, "fw_data kzalloc memory failed"); ++ goto exit_vmalloc_failed; ++ } ++ memset(acf_info->fw_data, 0, fw->size); ++ memcpy(acf_info->fw_data, fw->data, fw->size); ++ acf_info->fw_size = fw->size; ++ ++ ret = aw87xxx_acf_parse(aw87xxx->dev, &aw87xxx->acf_info); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "fw_data parse failed"); ++ goto exit_acf_parse_failed; ++ } ++ ++ ret = aw87xxx_init_default_prof(aw87xxx); ++ if (ret < 0) { ++ aw87xxx_fw_cfg_free(aw87xxx); ++ goto exit_acf_parse_failed; ++ } ++ ++ AW_DEV_LOGI(aw87xxx->dev, "acf parse succeed"); ++ mutex_unlock(&aw87xxx->reg_lock); ++ release_firmware(fw); ++ // Updating profile to "Music" because the firmware is set to "off" during init ++ aw87xxx_update_profile(aw87xxx, AW87XXX_PROF_MUSIC); ++ ++ return; ++ ++exit_acf_parse_failed: ++exit_vmalloc_failed: ++ release_firmware(fw); ++ mutex_unlock(&aw87xxx->reg_lock); ++} ++ ++static void aw87xxx_fw_load_work_routine(struct work_struct *work) ++{ ++ struct aw87xxx *aw87xxx = container_of(work, ++ struct aw87xxx, fw_load_work.work); ++ struct aw_prof_info *prof_info = &aw87xxx->acf_info.prof_info; ++ ++ AW_DEV_LOGD(aw87xxx->dev, "enter"); ++ ++ if (prof_info->status == AW_ACF_WAIT) { ++ request_firmware_nowait(THIS_MODULE, ++// FW_ACTION_HOTPLUG, ++ FW_ACTION_UEVENT, ++ aw87xxx->fw_name, ++ aw87xxx->dev, ++ GFP_KERNEL, aw87xxx, ++ aw87xxx_fw_load); ++ } ++} ++ ++static void aw87xxx_fw_load_init(struct aw87xxx *aw87xxx) ++{ ++#ifdef AW_CFG_UPDATE_DELAY ++ int cfg_timer_val = AW_CFG_UPDATE_DELAY_TIMER; ++#else ++ int cfg_timer_val = 0; ++#endif ++ AW_DEV_LOGI(aw87xxx->dev, "enter"); ++ snprintf(aw87xxx->fw_name, AW87XXX_FW_NAME_MAX, "%s", AW87XXX_FW_BIN_NAME); ++ aw87xxx_acf_init(&aw87xxx->aw_dev, &aw87xxx->acf_info, aw87xxx->dev_index); ++ ++ INIT_DELAYED_WORK(&aw87xxx->fw_load_work, aw87xxx_fw_load_work_routine); ++ schedule_delayed_work(&aw87xxx->fw_load_work, ++ msecs_to_jiffies(cfg_timer_val)); ++} ++ ++/**************************************************************************** ++ * ++ *aw87xxx attribute node ++ * ++ ****************************************************************************/ ++static ssize_t aw87xxx_attr_get_reg(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ssize_t len = 0; ++ int ret = 0; ++ unsigned int i = 0; ++ unsigned char reg_val = 0; ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ struct aw_device *aw_dev = &aw87xxx->aw_dev; ++ ++ mutex_lock(&aw87xxx->reg_lock); ++ for (i = 0; i < aw_dev->reg_max_addr; i++) { ++ if (!(aw_dev->reg_access[i] & AW_DEV_REG_RD_ACCESS)) ++ continue; ++ ret = aw87xxx_dev_i2c_read_byte(&aw87xxx->aw_dev, i, ®_val); ++ if (ret < 0) { ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ "read reg [0x%x] failed\n", i); ++ AW_DEV_LOGE(aw87xxx->dev, "read reg [0x%x] failed", i); ++ } else { ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ "reg:0x%02X=0x%02X\n", i, reg_val); ++ AW_DEV_LOGD(aw87xxx->dev, "reg:0x%02X=0x%02X", ++ i, reg_val); ++ } ++ } ++ mutex_unlock(&aw87xxx->reg_lock); ++ ++ return len; ++} ++ ++static ssize_t aw87xxx_attr_set_reg(struct device *dev, ++ struct device_attribute *attr, const char *buf, ++ size_t len) ++{ ++ unsigned int databuf[2] = { 0 }; ++ int ret = 0; ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ ++ mutex_lock(&aw87xxx->reg_lock); ++ if (sscanf(buf, "0x%x 0x%x", &databuf[0], &databuf[1]) == 2) { ++ if (databuf[0] >= aw87xxx->aw_dev.reg_max_addr) { ++ AW_DEV_LOGE(aw87xxx->dev, "set reg[0x%x] error,is out of reg_addr_max[0x%x]", ++ databuf[0], aw87xxx->aw_dev.reg_max_addr); ++ mutex_unlock(&aw87xxx->reg_lock); ++ return -EINVAL; ++ } ++ ++ ret = aw87xxx_dev_i2c_write_byte(&aw87xxx->aw_dev, ++ databuf[0], databuf[1]); ++ if (ret < 0) ++ AW_DEV_LOGE(aw87xxx->dev, "set [0x%x]=0x%x failed", ++ databuf[0], databuf[1]); ++ else ++ AW_DEV_LOGD(aw87xxx->dev, "set [0x%x]=0x%x succeed", ++ databuf[0], databuf[1]); ++ } else { ++ AW_DEV_LOGE(aw87xxx->dev, "i2c write cmd input error"); ++ } ++ mutex_unlock(&aw87xxx->reg_lock); ++ ++ return len; ++} ++ ++static ssize_t aw87xxx_attr_get_profile(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ssize_t len = 0; ++ unsigned int i = 0; ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ struct aw_prof_info *prof_info = &aw87xxx->acf_info.prof_info; ++ ++ if (!prof_info->status) { ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ "profile_cfg not load\n"); ++ return len; ++ } ++ ++ AW_DEV_LOGI(aw87xxx->dev, "current profile:[%s]", aw87xxx->current_profile); ++ ++ for (i = 0; i < prof_info->count; i++) { ++ if (!strncmp(aw87xxx->current_profile, prof_info->prof_name_list[i], ++ AW_PROFILE_STR_MAX)) ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ ">%s\n", prof_info->prof_name_list[i]); ++ else ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ " %s\n", prof_info->prof_name_list[i]); ++ } ++ ++ return len; ++} ++ ++static ssize_t aw87xxx_attr_set_profile(struct device *dev, ++ struct device_attribute *attr, const char *buf, ++ size_t len) ++{ ++ char profile[AW_PROFILE_STR_MAX] = {0}; ++ int ret = 0; ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ ++ if (strlen(buf) > AW_PROFILE_STR_MAX) { ++ AW_DEV_LOGE(aw87xxx->dev, "input profile_str_len is out of max[%d]", ++ AW_PROFILE_STR_MAX); ++ return -EINVAL; ++ } ++ ++ if (sscanf(buf, "%s", profile) == 1) { ++ AW_DEV_LOGD(aw87xxx->dev, "set profile [%s]", profile); ++ ret = aw87xxx_update_profile(aw87xxx, profile); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "set profile[%s] failed", ++ profile); ++ return ret; ++ } ++ } ++ ++ return len; ++} ++ ++static ssize_t aw87xxx_attr_get_hwen(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ssize_t len = 0; ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ int hwen = aw87xxx->aw_dev.hwen_status; ++ ++ if (hwen >= AW_DEV_HWEN_INVALID) ++ len += snprintf(buf + len, PAGE_SIZE - len, "hwen_status: invalid\n"); ++ else if (hwen == AW_DEV_HWEN_ON) ++ len += snprintf(buf + len, PAGE_SIZE - len, "hwen_status: on\n"); ++ else if (hwen == AW_DEV_HWEN_OFF) ++ len += snprintf(buf + len, PAGE_SIZE - len, "hwen_status: off\n"); ++ ++ return len; ++} ++ ++static ssize_t aw87xxx_attr_set_hwen(struct device *dev, ++ struct device_attribute *attr, const char *buf, ++ size_t len) ++{ ++ int ret = -1; ++ unsigned int state; ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ ++ ret = kstrtouint(buf, 0, &state); ++ if (ret) { ++ AW_DEV_LOGE(aw87xxx->dev, "fail to channelge str to int"); ++ return ret; ++ } ++ ++ mutex_lock(&aw87xxx->reg_lock); ++ if (state == AW_DEV_HWEN_OFF) ++ aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, false); /*OFF*/ ++ else if (state == AW_DEV_HWEN_ON) ++ aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, true); /*ON*/ ++ else ++ AW_DEV_LOGE(aw87xxx->dev, "input [%d] error, hwen_on=[%d],hwen_off=[%d]", ++ state, AW_DEV_HWEN_ON, AW_DEV_HWEN_OFF); ++ mutex_unlock(&aw87xxx->reg_lock); ++ return len; ++} ++ ++int aw87xxx_awrw_write(struct aw87xxx *aw87xxx, ++ const char *buf, size_t count) ++{ ++ int i = 0, ret = -1; ++ char *data_buf = NULL; ++ int buf_len = 0; ++ int temp_data = 0; ++ int data_str_size = 0; ++ char *reg_data; ++ struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; ++ ++ AW_DEV_LOGD(aw87xxx->dev, "enter"); ++ /* one addr or one data string Composition of Contains two bytes of symbol(0X)*/ ++ /* and two byte of hexadecimal data*/ ++ data_str_size = 2 + 2 * AWRW_DATA_BYTES; ++ ++ /* The buf includes the first address of the register to be written and all data */ ++ buf_len = AWRW_ADDR_BYTES + packet->reg_num * AWRW_DATA_BYTES; ++ AW_DEV_LOGI(aw87xxx->dev, "buf_len = %d,reg_num = %d", buf_len, packet->reg_num); ++ data_buf = vmalloc(buf_len); ++ if (data_buf == NULL) { ++ AW_DEV_LOGE(aw87xxx->dev, "alloc memory failed"); ++ return -ENOMEM; ++ } ++ memset(data_buf, 0, buf_len); ++ ++ data_buf[0] = packet->reg_addr; ++ reg_data = data_buf + 1; ++ ++ AW_DEV_LOGD(aw87xxx->dev, "reg_addr: 0x%02x", data_buf[0]); ++ ++ /*ag:0x00 0x01 0x01 0x01 0x01 0x00\x0a*/ ++ for (i = 0; i < packet->reg_num; i++) { ++ ret = sscanf(buf + AWRW_HDR_LEN + 1 + i * (data_str_size + 1), ++ "0x%x", &temp_data); ++ if (ret != 1) { ++ AW_DEV_LOGE(aw87xxx->dev, "sscanf failed,ret=%d", ret); ++ vfree(data_buf); ++ data_buf = NULL; ++ return ret; ++ } ++ reg_data[i] = temp_data; ++ AW_DEV_LOGD(aw87xxx->dev, "[%d] : 0x%02x", i, reg_data[i]); ++ } ++ ++ mutex_lock(&aw87xxx->reg_lock); ++ ret = i2c_master_send(aw87xxx->aw_dev.i2c, data_buf, buf_len); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "write failed"); ++ vfree(data_buf); ++ data_buf = NULL; ++ return -EFAULT; ++ } ++ mutex_unlock(&aw87xxx->reg_lock); ++ ++ vfree(data_buf); ++ data_buf = NULL; ++ ++ AW_DEV_LOGD(aw87xxx->dev, "down"); ++ return 0; ++} ++ ++static int aw87xxx_awrw_data_check(struct aw87xxx *aw87xxx, ++ int *data, size_t count) ++{ ++ struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; ++ int req_data_len = 0; ++ int act_data_len = 0; ++ int data_str_size = 0; ++ ++ if ((data[AWRW_HDR_ADDR_BYTES] != AWRW_ADDR_BYTES) || ++ (data[AWRW_HDR_DATA_BYTES] != AWRW_DATA_BYTES)) { ++ AW_DEV_LOGE(aw87xxx->dev, "addr_bytes [%d] or data_bytes [%d] unsupport", ++ data[AWRW_HDR_ADDR_BYTES], data[AWRW_HDR_DATA_BYTES]); ++ return -EINVAL; ++ } ++ ++ /* one data string Composition of Contains two bytes of symbol(0x)*/ ++ /* and two byte of hexadecimal data*/ ++ data_str_size = 2 + 2 * AWRW_DATA_BYTES; ++ act_data_len = count - AWRW_HDR_LEN - 1; ++ ++ /* There is a comma(,) or space between each piece of data */ ++ if (data[AWRW_HDR_WR_FLAG] == AWRW_FLAG_WRITE) { ++ /*ag:0x00 0x01 0x01 0x01 0x01 0x00\x0a*/ ++ req_data_len = (data_str_size + 1) * packet->reg_num; ++ if (req_data_len > act_data_len) { ++ AW_DEV_LOGE(aw87xxx->dev, "data_len checkfailed,requeset data_len [%d],actaul data_len [%d]", ++ req_data_len, act_data_len); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++/* flag addr_bytes data_bytes reg_num reg_addr*/ ++static int aw87xxx_awrw_parse_buf(struct aw87xxx *aw87xxx, ++ const char *buf, size_t count, int *wr_status) ++{ ++ int data[AWRW_HDR_MAX] = {0}; ++ struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; ++ int ret = -1; ++ ++ if (sscanf(buf, "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x", ++ &data[AWRW_HDR_WR_FLAG], &data[AWRW_HDR_ADDR_BYTES], ++ &data[AWRW_HDR_DATA_BYTES], &data[AWRW_HDR_REG_NUM], ++ &data[AWRW_HDR_REG_ADDR]) == 5) { ++ ++ packet->reg_addr = data[AWRW_HDR_REG_ADDR]; ++ packet->reg_num = data[AWRW_HDR_REG_NUM]; ++ *wr_status = data[AWRW_HDR_WR_FLAG]; ++ ret = aw87xxx_awrw_data_check(aw87xxx, data, count); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++static ssize_t aw87xxx_attr_awrw_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; ++ int wr_status = 0; ++ int ret = -1; ++ ++ if (count < AWRW_HDR_LEN) { ++ AW_DEV_LOGE(aw87xxx->dev, "data count too smaller, please check write format"); ++ AW_DEV_LOGE(aw87xxx->dev, "string %s,count=%ld", ++ buf, (u_long)count); ++ return -EINVAL; ++ } ++ ++ AW_DEV_LOGI(aw87xxx->dev, "string:[%s],count=%ld", buf, (u_long)count); ++ ret = aw87xxx_awrw_parse_buf(aw87xxx, buf, count, &wr_status); ++ if (ret < 0) { ++ AW_DEV_LOGE(aw87xxx->dev, "can not parse string"); ++ return ret; ++ } ++ ++ if (wr_status == AWRW_FLAG_WRITE) { ++ ret = aw87xxx_awrw_write(aw87xxx, buf, count); ++ if (ret < 0) ++ return ret; ++ } else if (wr_status == AWRW_FLAG_READ) { ++ packet->status = AWRW_I2C_ST_READ; ++ AW_DEV_LOGI(aw87xxx->dev, "read_cmd:reg_addr[0x%02x], reg_num[%d]", ++ packet->reg_addr, packet->reg_num); ++ } else { ++ AW_DEV_LOGE(aw87xxx->dev, "please check str format, unsupport read_write_status: %d", ++ wr_status); ++ return -EINVAL; ++ } ++ ++ return count; ++} ++ ++static ssize_t aw87xxx_attr_awrw_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; ++ int data_len = 0; ++ size_t len = 0; ++ int ret = -1, i = 0; ++ char *reg_data = NULL; ++ ++ if (packet->status != AWRW_I2C_ST_READ) { ++ AW_DEV_LOGE(aw87xxx->dev, "please write read cmd first"); ++ return -EINVAL; ++ } ++ ++ data_len = AWRW_DATA_BYTES * packet->reg_num; ++ reg_data = (char *)vmalloc(data_len); ++ if (reg_data == NULL) { ++ AW_DEV_LOGE(aw87xxx->dev, "memory alloc failed"); ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ mutex_lock(&aw87xxx->reg_lock); ++ ret = aw87xxx_dev_i2c_read_msg(&aw87xxx->aw_dev, packet->reg_addr, ++ (char *)reg_data, data_len); ++ if (ret < 0) { ++ ret = -EFAULT; ++ mutex_unlock(&aw87xxx->reg_lock); ++ goto exit; ++ } ++ mutex_unlock(&aw87xxx->reg_lock); ++ ++ AW_DEV_LOGI(aw87xxx->dev, "reg_addr 0x%02x, reg_num %d", ++ packet->reg_addr, packet->reg_num); ++ ++ for (i = 0; i < data_len; i++) { ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ "0x%02x,", reg_data[i]); ++ AW_DEV_LOGI(aw87xxx->dev, "0x%02x", reg_data[i]); ++ } ++ ++ ret = len; ++ ++exit: ++ if (reg_data) { ++ vfree(reg_data); ++ reg_data = NULL; ++ } ++ packet->status = AWRW_I2C_ST_NONE; ++ return ret; ++} ++ ++static ssize_t aw87xxx_drv_ver_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ssize_t len = 0; ++ ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ "driver_ver: %s \n", AW87XXX_DRIVER_VERSION); ++ ++ return len; ++} ++ ++static DEVICE_ATTR(reg, S_IWUSR | S_IRUGO, ++ aw87xxx_attr_get_reg, aw87xxx_attr_set_reg); ++static DEVICE_ATTR(profile, S_IWUSR | S_IRUGO, ++ aw87xxx_attr_get_profile, aw87xxx_attr_set_profile); ++static DEVICE_ATTR(hwen, S_IWUSR | S_IRUGO, ++ aw87xxx_attr_get_hwen, aw87xxx_attr_set_hwen); ++static DEVICE_ATTR(awrw, S_IWUSR | S_IRUGO, ++ aw87xxx_attr_awrw_show, aw87xxx_attr_awrw_store); ++static DEVICE_ATTR(drv_ver, S_IRUGO, aw87xxx_drv_ver_show, NULL); ++ ++static struct attribute *aw87xxx_attributes[] = { ++ &dev_attr_reg.attr, ++ &dev_attr_profile.attr, ++ &dev_attr_hwen.attr, ++ &dev_attr_awrw.attr, ++ &dev_attr_drv_ver.attr, ++ NULL ++}; ++ ++static struct attribute_group aw87xxx_attribute_group = { ++ .attrs = aw87xxx_attributes ++}; ++ ++/**************************************************************************** ++ * ++ *aw87xxx device probe ++ * ++ ****************************************************************************/ ++static const struct acpi_gpio_params reset_gpio = { 0, 0, false }; ++static const struct acpi_gpio_mapping reset_acpi_gpios[] = { ++ { "reset-gpios", &reset_gpio, 1 }, ++ { } ++}; ++ ++static struct aw87xxx *aw87xxx_malloc_init(struct i2c_client *client) ++{ ++ struct aw87xxx *aw87xxx = NULL; ++ ++ aw87xxx = devm_kzalloc(&client->dev, sizeof(struct aw87xxx), ++ GFP_KERNEL); ++ if (aw87xxx == NULL) { ++ AW_DEV_LOGE(&client->dev, "failed to devm_kzalloc aw87xxx"); ++ return NULL; ++ } ++ memset(aw87xxx, 0, sizeof(struct aw87xxx)); ++ ++ aw87xxx->dev = &client->dev; ++ aw87xxx->aw_dev.dev = &client->dev; ++ aw87xxx->aw_dev.i2c_bus = client->adapter->nr; ++ aw87xxx->aw_dev.i2c_addr = client->addr; ++ aw87xxx->aw_dev.i2c = client; ++ aw87xxx->aw_dev.hwen_status = false; ++ aw87xxx->aw_dev.reg_access = NULL; ++ aw87xxx->aw_dev.hwen_status = AW_DEV_HWEN_INVALID; ++ aw87xxx->off_bin_status = AW87XXX_NO_OFF_BIN; ++ aw87xxx->codec = NULL; ++ aw87xxx->current_profile = aw87xxx->prof_off_name; ++ ++ mutex_init(&aw87xxx->reg_lock); ++ ++ AW_DEV_LOGI(&client->dev, "Driver struct alloc and mutex init done, devinfo: i2c_bus=%u, i2c_addr=%x", client->adapter->nr, client->addr); ++ return aw87xxx; ++} ++ ++static int aw87xxx_i2c_probe(struct i2c_client *client) ++{ ++ struct device_node *dev_node = client->dev.of_node; ++ struct aw87xxx *aw87xxx = NULL; ++ struct gpio_desc *gpiod = NULL; ++ int ret = -1; ++ ++ ++// To do, add this function ++//acpi_dev_add_driver_gpios() ++ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { ++ AW_DEV_LOGE(&client->dev, "check_functionality failed"); ++ ret = -ENODEV; ++ goto exit_check_functionality_failed; ++ } ++ ++ /* aw87xxx i2c_dev struct init */ ++ aw87xxx = aw87xxx_malloc_init(client); ++ if (aw87xxx == NULL) ++ goto exit_malloc_init_failed; ++ ++ i2c_set_clientdata(client, aw87xxx); ++ ++ aw87xxx_device_parse_port_id_dt(&aw87xxx->aw_dev); ++ aw87xxx_device_parse_topo_id_dt(&aw87xxx->aw_dev); ++ ++ /* aw87xxx Get ACPI GPIO */ ++/* ++ ret = devm_acpi_dev_add_driver_gpios(aw87xxx->dev, reset_acpi_gpios); ++ if(ret){ ++ AW_DEV_LOGE(aw87xxx->dev, "Unable to add GPIO mapping table"); ++ goto exit_device_init_failed; ++ } ++ ++ gpiod = gpiod_get_optional(aw87xxx->dev, "reset", GPIOD_OUT_LOW); ++*/ ++ ++ if (g_aw87xxx_dev_cnt == 0){ ++ gpiod = gpiod_get(aw87xxx->dev, NULL, GPIOD_OUT_LOW); ++ if (gpiod == NULL){ ++ AW_DEV_LOGE(aw87xxx->dev, "Gpiod returned NULL failing gracefully."); ++ goto exit_device_init_failed; ++ } ++ ++ if (IS_ERR(gpiod)){ ++ AW_DEV_LOGE(aw87xxx->dev, "Get gpiod failed."); ++ goto exit_device_init_failed; ++ } ++ ++ aw87xxx->aw_dev.rst_gpio = desc_to_gpio(gpiod); ++ aw87xxx->aw_dev.hwen_status = AW_DEV_HWEN_OFF; ++ AW_DEV_LOGI(aw87xxx->dev, "reset gpio[%x] parse succeed", aw87xxx->aw_dev.rst_gpio); ++ ++ if (gpio_is_valid(aw87xxx->aw_dev.rst_gpio)) { ++ ret = devm_gpio_request_one(aw87xxx->dev, aw87xxx->aw_dev.rst_gpio, GPIOF_OUT_INIT_LOW, "aw87xxx_reset"); ++ if ((ret < 0) && (ret != -EBUSY)) { ++ AW_DEV_LOGE(aw87xxx->dev, "reset request failed, returned [%d]", ret); ++ goto exit_device_init_failed; ++ } ++ }else{ ++ /*Disabling RESET GPIO*/ ++ AW_DEV_LOGI(aw87xxx->dev, "no reset gpio provided, hardware reset unavailable"); ++ aw87xxx->aw_dev.rst_gpio = AW_NO_RESET_GPIO; ++ aw87xxx->aw_dev.hwen_status = AW_DEV_HWEN_INVALID; ++ } ++ ++ } ++ /*hw power on PA*/ ++ aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, true); ++ ++ /* aw87xxx devices private attributes init */ ++ ret = aw87xxx_dev_init(&aw87xxx->aw_dev); ++ if (ret < 0) ++ goto exit_device_init_failed; ++ ++ /*product register reset */ ++ aw87xxx_dev_soft_reset(&aw87xxx->aw_dev); ++ ++ /*hw power off */ ++ aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, false); ++ ++ /* create debug attrbute nodes */ ++ ret = sysfs_create_group(&aw87xxx->dev->kobj, &aw87xxx_attribute_group); ++ if (ret < 0) ++ AW_DEV_LOGE(aw87xxx->dev, "failed to create sysfs nodes, will not allowed to use"); ++ ++ /* cfg_load init */ ++ aw87xxx_fw_load_init(aw87xxx); ++ ++ /*monitor init*/ ++ aw87xxx_monitor_init(aw87xxx->dev, &aw87xxx->monitor, dev_node); ++ ++ /*add device to total list */ ++ mutex_lock(&g_aw87xxx_mutex_lock); ++ g_aw87xxx_dev_cnt++; ++ list_add(&aw87xxx->list, &g_aw87xxx_list); ++ aw87xxx->dev_index = g_aw87xxx_dev_cnt; ++ ++ mutex_unlock(&g_aw87xxx_mutex_lock); ++ AW_DEV_LOGI(aw87xxx->dev, "succeed, dev_index=[%d], g_aw87xxx_dev_cnt= [%d]", ++ aw87xxx->dev_index, g_aw87xxx_dev_cnt); ++ ++ return 0; ++ ++exit_device_init_failed: ++ AW_DEV_LOGE(aw87xxx->dev, "pa init failed"); ++ ++ devm_kfree(&client->dev, aw87xxx); ++ aw87xxx = NULL; ++exit_malloc_init_failed: ++exit_check_functionality_failed: ++ return ret; ++} ++ ++static void aw87xxx_i2c_remove(struct i2c_client *client) ++{ ++ struct aw87xxx *aw87xxx = i2c_get_clientdata(client); ++ ++ aw87xxx_monitor_exit(&aw87xxx->monitor); ++ ++ /*rm attr node*/ ++ sysfs_remove_group(&aw87xxx->dev->kobj, &aw87xxx_attribute_group); ++ ++ aw87xxx_fw_cfg_free(aw87xxx); ++ ++ mutex_lock(&g_aw87xxx_mutex_lock); ++ g_aw87xxx_dev_cnt--; ++ list_del(&aw87xxx->list); ++ mutex_unlock(&g_aw87xxx_mutex_lock); ++ ++ devm_kfree(&client->dev, aw87xxx); ++ aw87xxx = NULL; ++ ++// return 0; ++} ++ ++static void aw87xxx_i2c_shutdown(struct i2c_client *client) ++{ ++ struct aw87xxx *aw87xxx = i2c_get_clientdata(client); ++ ++ AW_DEV_LOGI(&client->dev, "enter"); ++ ++ /*soft and hw power off*/ ++ aw87xxx_update_profile(aw87xxx, aw87xxx->prof_off_name); ++} ++ ++static const struct acpi_device_id aw87xxx_acpi_match[] = { ++ { "AWDZ8830", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(acpi, aw87xxx_acpi_match); ++ ++// This is not necessary if the acpi match probes correctly. This is needed for userspace `new_device() functionality ++static const struct i2c_device_id aw87xxx_i2c_id[] = { ++ {AW87XXX_I2C_NAME, 0}, ++ {}, ++}; ++ ++static struct i2c_driver aw87xxx_i2c_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = AW87XXX_I2C_NAME, ++ .acpi_match_table = aw87xxx_acpi_match, ++ }, ++ .probe = aw87xxx_i2c_probe, ++ .remove = aw87xxx_i2c_remove, ++ .shutdown = aw87xxx_i2c_shutdown, ++ .id_table = aw87xxx_i2c_id, ++}; ++ ++module_i2c_driver(aw87xxx_i2c_driver) ++ ++MODULE_AUTHOR(""); ++MODULE_DESCRIPTION("awinic aw87xxx pa driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/sound/soc/codecs/aw87xxx/aw87xxx_device.c b/sound/soc/codecs/aw87xxx/aw87xxx_device.c +index 087770857..5874c598a 100644 +--- a/sound/soc/codecs/aw87xxx/aw87xxx_device.c ++++ b/sound/soc/codecs/aw87xxx/aw87xxx_device.c +@@ -102,8 +102,8 @@ int aw87xxx_dev_i2c_write_byte(struct aw_device *aw_dev, + while (cnt < AW_I2C_RETRIES) { + ret = i2c_smbus_write_byte_data(aw_dev->i2c, reg_addr, reg_data); + if (ret < 0) +- AW_DEV_LOGE(aw_dev->dev, "i2c_write cnt=%d error=%d", +- cnt, ret); ++ AW_DEV_LOGE(aw_dev->dev, "i2c_write cnt=%d error=%d i2c_bus=%u i2c_addr=%X chipid=%X", ++ cnt, ret, aw_dev->i2c_bus, aw_dev->i2c_addr, aw_dev->chipid); + else + break; + +@@ -123,8 +123,8 @@ int aw87xxx_dev_i2c_read_byte(struct aw_device *aw_dev, + while (cnt < AW_I2C_RETRIES) { + ret = i2c_smbus_read_byte_data(aw_dev->i2c, reg_addr); + if (ret < 0) { +- AW_DEV_LOGE(aw_dev->dev, "i2c_read cnt=%d error=%d", +- cnt, ret); ++ AW_DEV_LOGE(aw_dev->dev, "i2c_read cnt=%d error=%d i2c_bus=%u i2c_addr=%X chipid=%X", ++ cnt, ret, aw_dev->i2c_bus, aw_dev->i2c_addr, aw_dev->chipid); + } else { + *reg_data = ret; + break; +-- +2.45.2 + + +From c929d4f9c7be7599522d39d207c77006d96dc70c Mon Sep 17 00:00:00 2001 +From: CVMagic <546352+CVMagic@users.noreply.github.com> +Date: Sun, 19 May 2024 21:37:37 +0200 +Subject: [PATCH 3/8] Updated AW87xxx driver to automatically enumerate a + second I2C chip if specified in ACPI + +Signed-off-by: Antheas Kapenekakis +--- + sound/soc/codecs/aw87xxx/aw87xxx.c | 80 ++++++++++++++++++++++-------- + 1 file changed, 58 insertions(+), 22 deletions(-) + +diff --git a/sound/soc/codecs/aw87xxx/aw87xxx.c b/sound/soc/codecs/aw87xxx/aw87xxx.c +index 7f44d9b9d..8b3e74a7b 100644 +--- a/sound/soc/codecs/aw87xxx/aw87xxx.c ++++ b/sound/soc/codecs/aw87xxx/aw87xxx.c +@@ -82,6 +82,22 @@ static struct aw_componet_codec_ops aw_componet_codec_ops = { + }; + #endif + ++enum smi_bus_type { ++ SMI_I2C, ++ SMI_SPI, ++ SMI_AUTO_DETECT, ++}; ++ ++struct smi_instance { ++ const char *type; ++ unsigned int flags; ++ int irq_idx; ++}; ++ ++struct smi_node { ++ enum smi_bus_type bus_type; ++ struct smi_instance instances[]; ++}; + + /************************************************************************ + * +@@ -1291,13 +1307,20 @@ static struct aw87xxx *aw87xxx_malloc_init(struct i2c_client *client) + static int aw87xxx_i2c_probe(struct i2c_client *client) + { + struct device_node *dev_node = client->dev.of_node; ++ const struct smi_node *node; ++ struct acpi_device *adev = ACPI_COMPANION(&client->dev); + struct aw87xxx *aw87xxx = NULL; + struct gpio_desc *gpiod = NULL; ++ struct i2c_board_info board_info = {}; ++ char i2c_name[32]; + int ret = -1; ++ int acpi_dev_count = 0; + +- +-// To do, add this function +-//acpi_dev_add_driver_gpios() ++ /* aw87xxx Get APCI I2C device count */ ++ if(g_aw87xxx_dev_cnt == 0){ ++ acpi_dev_count = i2c_acpi_client_count(adev); ++ AW_DEV_LOGI(&client->dev, "I2C_ACPI_CLIENT_COUNT returned [%d]", acpi_dev_count); ++ } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + AW_DEV_LOGE(&client->dev, "check_functionality failed"); +@@ -1316,18 +1339,15 @@ static int aw87xxx_i2c_probe(struct i2c_client *client) + aw87xxx_device_parse_topo_id_dt(&aw87xxx->aw_dev); + + /* aw87xxx Get ACPI GPIO */ +-/* +- ret = devm_acpi_dev_add_driver_gpios(aw87xxx->dev, reset_acpi_gpios); +- if(ret){ +- AW_DEV_LOGE(aw87xxx->dev, "Unable to add GPIO mapping table"); +- goto exit_device_init_failed; +- } +- +- gpiod = gpiod_get_optional(aw87xxx->dev, "reset", GPIOD_OUT_LOW); +-*/ + + if (g_aw87xxx_dev_cnt == 0){ +- gpiod = gpiod_get(aw87xxx->dev, NULL, GPIOD_OUT_LOW); ++ ret = devm_acpi_dev_add_driver_gpios(aw87xxx->dev, reset_acpi_gpios); ++ if(ret){ ++ AW_DEV_LOGE(aw87xxx->dev, "Unable to add GPIO mapping table"); ++ goto exit_device_init_failed; ++ } ++ ++ gpiod = devm_gpiod_get(aw87xxx->dev, "reset", GPIOD_OUT_LOW); + if (gpiod == NULL){ + AW_DEV_LOGE(aw87xxx->dev, "Gpiod returned NULL failing gracefully."); + goto exit_device_init_failed; +@@ -1342,13 +1362,7 @@ static int aw87xxx_i2c_probe(struct i2c_client *client) + aw87xxx->aw_dev.hwen_status = AW_DEV_HWEN_OFF; + AW_DEV_LOGI(aw87xxx->dev, "reset gpio[%x] parse succeed", aw87xxx->aw_dev.rst_gpio); + +- if (gpio_is_valid(aw87xxx->aw_dev.rst_gpio)) { +- ret = devm_gpio_request_one(aw87xxx->dev, aw87xxx->aw_dev.rst_gpio, GPIOF_OUT_INIT_LOW, "aw87xxx_reset"); +- if ((ret < 0) && (ret != -EBUSY)) { +- AW_DEV_LOGE(aw87xxx->dev, "reset request failed, returned [%d]", ret); +- goto exit_device_init_failed; +- } +- }else{ ++ if (!gpio_is_valid(aw87xxx->aw_dev.rst_gpio)) { + /*Disabling RESET GPIO*/ + AW_DEV_LOGI(aw87xxx->dev, "no reset gpio provided, hardware reset unavailable"); + aw87xxx->aw_dev.rst_gpio = AW_NO_RESET_GPIO; +@@ -1356,8 +1370,11 @@ static int aw87xxx_i2c_probe(struct i2c_client *client) + } + + } ++ + /*hw power on PA*/ +- aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, true); ++ if(g_aw87xxx_dev_cnt == 0) { ++ aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, true); ++ } + + /* aw87xxx devices private attributes init */ + ret = aw87xxx_dev_init(&aw87xxx->aw_dev); +@@ -1368,7 +1385,9 @@ static int aw87xxx_i2c_probe(struct i2c_client *client) + aw87xxx_dev_soft_reset(&aw87xxx->aw_dev); + + /*hw power off */ +- aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, false); ++ if(g_aw87xxx_dev_cnt == 0) { ++ aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, false); ++ } + + /* create debug attrbute nodes */ + ret = sysfs_create_group(&aw87xxx->dev->kobj, &aw87xxx_attribute_group); +@@ -1391,6 +1410,23 @@ static int aw87xxx_i2c_probe(struct i2c_client *client) + AW_DEV_LOGI(aw87xxx->dev, "succeed, dev_index=[%d], g_aw87xxx_dev_cnt= [%d]", + aw87xxx->dev_index, g_aw87xxx_dev_cnt); + ++ AW_DEV_LOGI(aw87xxx->dev, "acpi_c=[%d] dev_c=[%d]", acpi_dev_count, g_aw87xxx_dev_cnt); ++ ++ /* Attempt to add other I2C AMPs */ ++ if ((acpi_dev_count > 1) && (g_aw87xxx_dev_cnt == 1)){ ++ /* power on the chip */ ++ aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, true); ++ ++ node = device_get_match_data(aw87xxx->dev); ++ memset(&board_info, 0, sizeof(board_info)); ++ strscpy(board_info.type, client->name, I2C_NAME_SIZE); ++ snprintf(i2c_name, sizeof(i2c_name), "%s.%d", client->name, 1); ++ board_info.dev_name = i2c_name; ++ ++ aw87xxx_i2c_probe(i2c_acpi_new_device_by_fwnode(acpi_fwnode_handle(adev), 1, &board_info)); ++ ++ } ++ + return 0; + + exit_device_init_failed: +-- +2.45.2 + + +From e67153f0c9b2c66e66334c59136ecc244139e555 Mon Sep 17 00:00:00 2001 +From: CVMagic <546352+CVMagic@users.noreply.github.com> +Date: Wed, 22 May 2024 02:42:40 +0000 +Subject: [PATCH 4/8] Updated AW87xxx driver to implement Suspend and Resume. + +Signed-off-by: Antheas Kapenekakis +--- + sound/soc/codecs/aw87xxx/aw87xxx.c | 39 ++++++++++++++++++++++++++++++ + 1 file changed, 39 insertions(+) + +diff --git a/sound/soc/codecs/aw87xxx/aw87xxx.c b/sound/soc/codecs/aw87xxx/aw87xxx.c +index 8b3e74a7b..134a9a750 100644 +--- a/sound/soc/codecs/aw87xxx/aw87xxx.c ++++ b/sound/soc/codecs/aw87xxx/aw87xxx.c +@@ -1471,6 +1471,44 @@ static void aw87xxx_i2c_shutdown(struct i2c_client *client) + aw87xxx_update_profile(aw87xxx, aw87xxx->prof_off_name); + } + ++ ++static int aw87xxx_runtime_suspend(struct device *dev) ++{ ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ ++ AW_DEV_LOGI(aw87xxx->dev, "Suspending..."); ++ ++ // soft and hw power off ++ aw87xxx_update_profile(aw87xxx, aw87xxx->prof_off_name); ++ ++ return 0; ++} ++ ++static int aw87xxx_runtime_resume(struct device *dev) ++{ ++ struct list_head *pos = NULL; ++ struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ++ ++ // Power on PA ++ if (aw87xxx->dev_index == 1) ++ aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, true); ++ ++ // Set profile to Music ++ list_for_each_prev(pos, &g_aw87xxx_list) { ++ aw87xxx = list_entry(pos, struct aw87xxx, list); ++ AW_DEV_LOGI(aw87xxx->dev, "Resuming..."); ++ ++ mutex_lock(&aw87xxx->reg_lock); ++ aw87xxx_power_on(aw87xxx, AW87XXX_PROF_MUSIC); ++ mutex_unlock(&aw87xxx->reg_lock); ++ ++ } ++ ++ return 0; ++} ++ ++static SIMPLE_DEV_PM_OPS(aw87xxx_pm_ops, aw87xxx_runtime_suspend, aw87xxx_runtime_resume); ++ + static const struct acpi_device_id aw87xxx_acpi_match[] = { + { "AWDZ8830", 0 }, + { } +@@ -1488,6 +1526,7 @@ static struct i2c_driver aw87xxx_i2c_driver = { + .owner = THIS_MODULE, + .name = AW87XXX_I2C_NAME, + .acpi_match_table = aw87xxx_acpi_match, ++ .pm = &aw87xxx_pm_ops, + }, + .probe = aw87xxx_i2c_probe, + .remove = aw87xxx_i2c_remove, +-- +2.45.2 + + +From c6eb7c48fd8bb0c281c43c6e2c89ec32ab6dc7c4 Mon Sep 17 00:00:00 2001 +From: bouhaa +Date: Fri, 22 Sep 2023 21:53:06 +0200 +Subject: [PATCH 5/8] Ayaneo geek headset patch + +Signed-off-by: Antheas Kapenekakis +--- + sound/pci/hda/patch_realtek.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c +index c0530d4aa..62ffe1448 100644 +--- a/sound/pci/hda/patch_realtek.c ++++ b/sound/pci/hda/patch_realtek.c +@@ -6555,6 +6555,20 @@ static void alc294_gx502_toggle_output(struct hda_codec *codec, + alc_write_coef_idx(codec, 0x10, 0x0a20); + } + ++static void alc269_fixup_headphone_volume(struct hda_codec *codec, ++ const struct hda_fixup *fix, int action) ++{ ++ /* Pin 0x21: Some devices share 0x14 for headphones and speakers. ++ * This will fix ensure these devices have volume controls. */ ++ if (!is_jack_detectable(codec, 0x21)) ++ return; ++ ++ if (action == HDA_FIXUP_ACT_PRE_PROBE) { ++ static const hda_nid_t conn1[] = { 0x02 }; ++ snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1); ++ } ++} ++ + static void alc294_fixup_gx502_hp(struct hda_codec *codec, + const struct hda_fixup *fix, int action) + { +@@ -7271,6 +7285,7 @@ enum { + ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, + ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET, + ALC269_FIXUP_HEADSET_MODE, ++ ALC269_FIXUP_HEADSET_AYA_GEEK, + ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC, + ALC269_FIXUP_ASPIRE_HEADSET_MIC, + ALC269_FIXUP_ASUS_X101_FUNC, +@@ -8769,6 +8784,10 @@ static const struct hda_fixup alc269_fixups[] = { + .chained = true, + .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE + }, ++ [ALC269_FIXUP_HEADSET_AYA_GEEK] = { ++ .type = HDA_FIXUP_FUNC, ++ .v.func = alc269_fixup_headphone_volume, ++ }, + [ALC299_FIXUP_PREDATOR_SPK] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { +@@ -10601,6 +10601,7 @@ + SND_PCI_QUIRK(0x2782, 0x0214, "VAIO VJFE-CL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO), + SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME), ++ SND_PCI_QUIRK(0x1f66, 0x0101, "GEEK", ALC269_FIXUP_HEADSET_AYA_GEEK), + SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC), + SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED), + SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", ALC256_FIXUP_INTEL_NUC10), +-- +2.45.2 + + +From 2d9cf63b5f1b72a8dbdc226fda6e060b69b7d1c9 Mon Sep 17 00:00:00 2001 +From: bouhaa +Date: Fri, 22 Sep 2023 22:08:35 +0200 +Subject: [PATCH 6/8] ayaneo 2 headphone fix + +Signed-off-by: Antheas Kapenekakis +--- + sound/pci/hda/patch_realtek.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c +index 62ffe1448..f4aaaf6ba 100644 +--- a/sound/pci/hda/patch_realtek.c ++++ b/sound/pci/hda/patch_realtek.c +@@ -7285,6 +7285,7 @@ enum { + ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, + ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET, + ALC269_FIXUP_HEADSET_MODE, ++ ALC269_FIXUP_HEADSET_AYA_2, + ALC269_FIXUP_HEADSET_AYA_GEEK, + ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC, + ALC269_FIXUP_ASPIRE_HEADSET_MIC, +@@ -8784,6 +8785,10 @@ static const struct hda_fixup alc269_fixups[] = { + .chained = true, + .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE + }, ++ [ALC269_FIXUP_HEADSET_AYA_2] = { ++ .type = HDA_FIXUP_FUNC, ++ .v.func = alc269_fixup_headphone_volume, ++ }, + [ALC269_FIXUP_HEADSET_AYA_GEEK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_headphone_volume, +@@ -10601,6 +10601,7 @@ + SND_PCI_QUIRK(0x2782, 0x0214, "VAIO VJFE-CL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO), + SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME), ++ SND_PCI_QUIRK(0x1f66, 0x0101, "AYANEO 2", ALC269_FIXUP_HEADSET_AYA_2), + SND_PCI_QUIRK(0x1f66, 0x0101, "GEEK", ALC269_FIXUP_HEADSET_AYA_GEEK), + SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC), + SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED), +-- +2.45.2 + + +From e59906fda334a028c8b3997db4408995fdd61725 Mon Sep 17 00:00:00 2001 +From: fewtarius +Date: Thu, 11 Jul 2024 18:31:08 +0000 +Subject: [PATCH 7/8] Kernel 6.9.9, fix Air 1S audio - thanks in part to + @linh1987! + +Signed-off-by: Antheas Kapenekakis +--- + sound/pci/hda/patch_realtek.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c +index f4aaaf6ba..f2be4b102 100644 +--- a/sound/pci/hda/patch_realtek.c ++++ b/sound/pci/hda/patch_realtek.c +@@ -7300,6 +7300,7 @@ enum { + ALC269VB_FIXUP_ASUS_ZENBOOK, + ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A, + ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE, ++ ALC269VB_FIXUP_AYANEO_SPKR_PIN_FIX, + ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED, + ALC269VB_FIXUP_ORDISSIMO_EVE2, + ALC283_FIXUP_CHROME_BOOK, +@@ -8012,6 +8013,22 @@ static const struct hda_fixup alc269_fixups[] = { + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MIC + }, ++ [ALC269VB_FIXUP_AYANEO_SPKR_PIN_FIX] = { ++ .type = HDA_FIXUP_PINS, ++ .v.pins = (const struct hda_pintbl[]) { ++ { 0x12, 0x90a60130 }, ++ { 0x14, 0x90170110 }, ++ { 0x17, 0x40000000 }, ++ { 0x18, 0x03a19020 }, ++ { 0x19, 0x411111f0 }, ++ { 0x1a, 0x90170150 }, ++ { 0x1b, 0x411111f0 }, ++ { 0x1d, 0x40e69945 }, ++ { 0x1e, 0x411111f0 }, ++ { 0x21, 0x90170150 }, ++ { } ++ }, ++ }, + [ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_limit_int_mic_boost, +@@ -10603,6 +10603,7 @@ + SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME), + SND_PCI_QUIRK(0x1f66, 0x0101, "AYANEO 2", ALC269_FIXUP_HEADSET_AYA_2), + SND_PCI_QUIRK(0x1f66, 0x0101, "GEEK", ALC269_FIXUP_HEADSET_AYA_GEEK), ++ SND_PCI_QUIRK(0x1f66, 0x0103, "AYANEO AIR 1S", ALC269VB_FIXUP_AYANEO_SPKR_PIN_FIX), + SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC), + SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED), + SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", ALC256_FIXUP_INTEL_NUC10), +@@ -10743,6 +10761,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { + {.id = ALC269VB_FIXUP_ASUS_ZENBOOK, .name = "asus-zenbook"}, + {.id = ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A, .name = "asus-zenbook-ux31a"}, + {.id = ALC269VB_FIXUP_ORDISSIMO_EVE2, .name = "ordissimo"}, ++ {.id = ALC269VB_FIXUP_AYANEO_SPKR_PIN_FIX, .name = "ayaneo-speaker-pin-fix"}, + {.id = ALC282_FIXUP_ASUS_TX300, .name = "asus-tx300"}, + {.id = ALC283_FIXUP_INT_MIC, .name = "alc283-int-mic"}, + {.id = ALC290_FIXUP_MONO_SPEAKERS_HSJACK, .name = "mono-speakers"}, +-- +2.45.2 + + +From c1ca32ecd4172c05ddc83a1927d73d3a23253353 Mon Sep 17 00:00:00 2001 +From: CVMagic <546352+CVMagic@users.noreply.github.com> +Date: Sat, 13 Jul 2024 12:18:58 -0400 +Subject: [PATCH 8/8] Use DMI matching for conflicting SSID 0x1f660101 between + Ayaneo and AYN + +Signed-off-by: Antheas Kapenekakis +--- + sound/pci/hda/patch_realtek.c | 38 ++++++++++++++++++++++++++++------- + 1 file changed, 31 insertions(+), 7 deletions(-) + +diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c +index f2be4b102..928202b1a 100644 +--- a/sound/pci/hda/patch_realtek.c ++++ b/sound/pci/hda/patch_realtek.c +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -7285,8 +7286,8 @@ enum { + ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, + ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET, + ALC269_FIXUP_HEADSET_MODE, +- ALC269_FIXUP_HEADSET_AYA_2, +- ALC269_FIXUP_HEADSET_AYA_GEEK, ++ ALC269_FIXUP_DMI_MATCH, ++ ALC269_FIXUP_AYA_HEADSET_VOLUME, + ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC, + ALC269_FIXUP_ASPIRE_HEADSET_MIC, + ALC269_FIXUP_ASUS_X101_FUNC, +@@ -7534,6 +7535,30 @@ enum { + ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318, + }; + ++/* A special fixup for AYN and AYANEO handhelds as both ++* have the same PCI SSID as well as the same codec, but ++* require different quirks, falling back to DMI matching. ++*/ ++static void alc269_fixup_match_via_dmi(struct hda_codec *codec, ++ const struct hda_fixup *fix, int action) ++{ ++ int alc269_fix_id; ++ const char *board_name = dmi_get_system_info(DMI_BOARD_NAME); ++ ++ if (dmi_name_in_vendors("AYANEO") || dmi_name_in_vendors("AYADEVICE") || dmi_name_in_vendors("AYA DEVICE")) { ++ if (board_name && (strcmp(board_name, "AYANEO 2") || strcmp(board_name, "AYANEO 2S") || strcmp(board_name, "GEEK") || strcmp(board_name, "GEEK 1S"))) { ++ alc269_fix_id = ALC269_FIXUP_AYA_HEADSET_VOLUME; ++ } else { ++ return; ++ } ++ } else if (dmi_name_in_vendors("ayn") && strcmp(board_name, "Loki MiniPro")) { ++ alc269_fix_id = ALC269VB_FIXUP_AYANEO_SPKR_PIN_FIX; ++ } else { ++ return; ++ } ++ __snd_hda_apply_fixup(codec, alc269_fix_id, action, 0); ++} ++ + /* A special fixup for Lenovo C940 and Yoga Duet 7; + * both have the very same PCI SSID, and we need to apply different fixups + * depending on the codec ID +@@ -8802,11 +8827,11 @@ static const struct hda_fixup alc269_fixups[] = { + .chained = true, + .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE + }, +- [ALC269_FIXUP_HEADSET_AYA_2] = { ++ [ALC269_FIXUP_DMI_MATCH] = { + .type = HDA_FIXUP_FUNC, +- .v.func = alc269_fixup_headphone_volume, ++ .v.func = alc269_fixup_match_via_dmi, + }, +- [ALC269_FIXUP_HEADSET_AYA_GEEK] = { ++ [ALC269_FIXUP_AYA_HEADSET_VOLUME] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_headphone_volume, + }, +@@ -10601,8 +10601,7 @@ + SND_PCI_QUIRK(0x2782, 0x0214, "VAIO VJFE-CL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO), + SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME), +- SND_PCI_QUIRK(0x1f66, 0x0101, "AYANEO 2", ALC269_FIXUP_HEADSET_AYA_2), +- SND_PCI_QUIRK(0x1f66, 0x0101, "GEEK", ALC269_FIXUP_HEADSET_AYA_GEEK), ++ SND_PCI_QUIRK(0x1f66, 0x0101, "Multiple Vendors", ALC269_FIXUP_DMI_MATCH), + SND_PCI_QUIRK(0x1f66, 0x0103, "AYANEO AIR 1S", ALC269VB_FIXUP_AYANEO_SPKR_PIN_FIX), + SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC), + SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED), +-- +2.45.2 + -- cgit v1.2.3