From 5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 21 Feb 2023 18:24:12 -0800 Subject: Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next Pull networking updates from Jakub Kicinski: "Core: - Add dedicated kmem_cache for typical/small skb->head, avoid having to access struct page at kfree time, and improve memory use. - Introduce sysctl to set default RPS configuration for new netdevs. - Define Netlink protocol specification format which can be used to describe messages used by each family and auto-generate parsers. Add tools for generating kernel data structures and uAPI headers. - Expose all net/core sysctls inside netns. - Remove 4s sleep in netpoll if carrier is instantly detected on boot. - Add configurable limit of MDB entries per port, and port-vlan. - Continue populating drop reasons throughout the stack. - Retire a handful of legacy Qdiscs and classifiers. Protocols: - Support IPv4 big TCP (TSO frames larger than 64kB). - Add IP_LOCAL_PORT_RANGE socket option, to control local port range on socket by socket basis. - Track and report in procfs number of MPTCP sockets used. - Support mixing IPv4 and IPv6 flows in the in-kernel MPTCP path manager. - IPv6: don't check net.ipv6.route.max_size and rely on garbage collection to free memory (similarly to IPv4). - Support Penultimate Segment Pop (PSP) flavor in SRv6 (RFC8986). - ICMP: add per-rate limit counters. - Add support for user scanning requests in ieee802154. - Remove static WEP support. - Support minimal Wi-Fi 7 Extremely High Throughput (EHT) rate reporting. - WiFi 7 EHT channel puncturing support (client & AP). BPF: - Add a rbtree data structure following the "next-gen data structure" precedent set by recently added linked list, that is, by using kfunc + kptr instead of adding a new BPF map type. - Expose XDP hints via kfuncs with initial support for RX hash and timestamp metadata. - Add BPF_F_NO_TUNNEL_KEY extension to bpf_skb_set_tunnel_key to better support decap on GRE tunnel devices not operating in collect metadata. - Improve x86 JIT's codegen for PROBE_MEM runtime error checks. - Remove the need for trace_printk_lock for bpf_trace_printk and bpf_trace_vprintk helpers. - Extend libbpf's bpf_tracing.h support for tracing arguments of kprobes/uprobes and syscall as a special case. - Significantly reduce the search time for module symbols by livepatch and BPF. - Enable cpumasks to be used as kptrs, which is useful for tracing programs tracking which tasks end up running on which CPUs in different time intervals. - Add support for BPF trampoline on s390x and riscv64. - Add capability to export the XDP features supported by the NIC. - Add __bpf_kfunc tag for marking kernel functions as kfuncs. - Add cgroup.memory=nobpf kernel parameter option to disable BPF memory accounting for container environments. Netfilter: - Remove the CLUSTERIP target. It has been marked as obsolete for years, and we still have WARN splats wrt races of the out-of-band /proc interface installed by this target. - Add 'destroy' commands to nf_tables. They are identical to the existing 'delete' commands, but do not return an error if the referenced object (set, chain, rule...) did not exist. Driver API: - Improve cpumask_local_spread() locality to help NICs set the right IRQ affinity on AMD platforms. - Separate C22 and C45 MDIO bus transactions more clearly. - Introduce new DCB table to control DSCP rewrite on egress. - Support configuration of Physical Layer Collision Avoidance (PLCA) Reconciliation Sublayer (RS) (802.3cg-2019). Modern version of shared medium Ethernet. - Support for MAC Merge layer (IEEE 802.3-2018 clause 99). Allowing preemption of low priority frames by high priority frames. - Add support for controlling MACSec offload using netlink SET. - Rework devlink instance refcounts to allow registration and de-registration under the instance lock. Split the code into multiple files, drop some of the unnecessarily granular locks and factor out common parts of netlink operation handling. - Add TX frame aggregation parameters (for USB drivers). - Add a new attr TCA_EXT_WARN_MSG to report TC (offload) warning messages with notifications for debug. - Allow offloading of UDP NEW connections via act_ct. - Add support for per action HW stats in TC. - Support hardware miss to TC action (continue processing in SW from a specific point in the action chain). - Warn if old Wireless Extension user space interface is used with modern cfg80211/mac80211 drivers. Do not support Wireless Extensions for Wi-Fi 7 devices at all. Everyone should switch to using nl80211 interface instead. - Improve the CAN bit timing configuration. Use extack to return error messages directly to user space, update the SJW handling, including the definition of a new default value that will benefit CAN-FD controllers, by increasing their oscillator tolerance. New hardware / drivers: - Ethernet: - nVidia BlueField-3 support (control traffic driver) - Ethernet support for imx93 SoCs - Motorcomm yt8531 gigabit Ethernet PHY - onsemi NCN26000 10BASE-T1S PHY (with support for PLCA) - Microchip LAN8841 PHY (incl. cable diagnostics and PTP) - Amlogic gxl MDIO mux - WiFi: - RealTek RTL8188EU (rtl8xxxu) - Qualcomm Wi-Fi 7 devices (ath12k) - CAN: - Renesas R-Car V4H Drivers: - Bluetooth: - Set Per Platform Antenna Gain (PPAG) for Intel controllers. - Ethernet NICs: - Intel (1G, igc): - support TSN / Qbv / packet scheduling features of i226 model - Intel (100G, ice): - use GNSS subsystem instead of TTY - multi-buffer XDP support - extend support for GPIO pins to E823 devices - nVidia/Mellanox: - update the shared buffer configuration on PFC commands - implement PTP adjphase function for HW offset control - TC support for Geneve and GRE with VF tunnel offload - more efficient crypto key management method - multi-port eswitch support - Netronome/Corigine: - add DCB IEEE support - support IPsec offloading for NFP3800 - Freescale/NXP (enetc): - support XDP_REDIRECT for XDP non-linear buffers - improve reconfig, avoid link flap and waiting for idle - support MAC Merge layer - Other NICs: - sfc/ef100: add basic devlink support for ef100 - ionic: rx_push mode operation (writing descriptors via MMIO) - bnxt: use the auxiliary bus abstraction for RDMA - r8169: disable ASPM and reset bus in case of tx timeout - cpsw: support QSGMII mode for J721e CPSW9G - cpts: support pulse-per-second output - ngbe: add an mdio bus driver - usbnet: optimize usbnet_bh() by avoiding unnecessary queuing - r8152: handle devices with FW with NCM support - amd-xgbe: support 10Mbps, 2.5GbE speeds and rx-adaptation - virtio-net: support multi buffer XDP - virtio/vsock: replace virtio_vsock_pkt with sk_buff - tsnep: XDP support - Ethernet high-speed switches: - nVidia/Mellanox (mlxsw): - add support for latency TLV (in FW control messages) - Microchip (sparx5): - separate explicit and implicit traffic forwarding rules, make the implicit rules always active - add support for egress DSCP rewrite - IS0 VCAP support (Ingress Classification) - IS2 VCAP filters (protos, L3 addrs, L4 ports, flags, ToS etc.) - ES2 VCAP support (Egress Access Control) - support for Per-Stream Filtering and Policing (802.1Q, 8.6.5.1) - Ethernet embedded switches: - Marvell (mv88e6xxx): - add MAB (port auth) offload support - enable PTP receive for mv88e6390 - NXP (ocelot): - support MAC Merge layer - support for the the vsc7512 internal copper phys - Microchip: - lan9303: convert to PHYLINK - lan966x: support TC flower filter statistics - lan937x: PTP support for KSZ9563/KSZ8563 and LAN937x - lan937x: support Credit Based Shaper configuration - ksz9477: support Energy Efficient Ethernet - other: - qca8k: convert to regmap read/write API, use bulk operations - rswitch: Improve TX timestamp accuracy - Intel WiFi (iwlwifi): - EHT (Wi-Fi 7) rate reporting - STEP equalizer support: transfer some STEP (connection to radio on platforms with integrated wifi) related parameters from the BIOS to the firmware. - Qualcomm 802.11ax WiFi (ath11k): - IPQ5018 support - Fine Timing Measurement (FTM) responder role support - channel 177 support - MediaTek WiFi (mt76): - per-PHY LED support - mt7996: EHT (Wi-Fi 7) support - Wireless Ethernet Dispatch (WED) reset support - switch to using page pool allocator - RealTek WiFi (rtw89): - support new version of Bluetooth co-existance - Mobile: - rmnet: support TX aggregation" * tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1872 commits) page_pool: add a comment explaining the fragment counter usage net: ethtool: fix __ethtool_dev_mm_supported() implementation ethtool: pse-pd: Fix double word in comments xsk: add linux/vmalloc.h to xsk.c sefltests: netdevsim: wait for devlink instance after netns removal selftest: fib_tests: Always cleanup before exit net/mlx5e: Align IPsec ASO result memory to be as required by hardware net/mlx5e: TC, Set CT miss to the specific ct action instance net/mlx5e: Rename CHAIN_TO_REG to MAPPED_OBJ_TO_REG net/mlx5: Refactor tc miss handling to a single function net/mlx5: Kconfig: Make tc offload depend on tc skb extension net/sched: flower: Support hardware miss to tc action net/sched: flower: Move filter handle initialization earlier net/sched: cls_api: Support hardware miss to tc action net/sched: Rename user cookie and act cookie sfc: fix builds without CONFIG_RTC_LIB sfc: clean up some inconsistent indentings net/mlx4_en: Introduce flexible array to silence overflow warning net: lan966x: Fix possible deadlock inside PTP net/ulp: Remove redundant ->clone() test in inet_clone_ulp(). ... --- sound/soc/codecs/adau1372.c | 1065 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1065 insertions(+) create mode 100644 sound/soc/codecs/adau1372.c (limited to 'sound/soc/codecs/adau1372.c') diff --git a/sound/soc/codecs/adau1372.c b/sound/soc/codecs/adau1372.c new file mode 100644 index 000000000..d9bde7eb0 --- /dev/null +++ b/sound/soc/codecs/adau1372.c @@ -0,0 +1,1065 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices ADAU1372 Audio Codec driver + * + * Copyright 2016 Analog Devices Inc. + * Author: Lars-Peter Clausen + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "adau1372.h" +#include "adau-utils.h" + +struct adau1372 { + struct regmap *regmap; + void (*switch_mode)(struct device *dev); + bool use_pll; + bool enabled; + bool clock_provider; + + struct snd_pcm_hw_constraint_list rate_constraints; + unsigned int slot_width; + + struct clk *mclk; + struct gpio_desc *pd_gpio; + struct device *dev; +}; + +#define ADAU1372_REG_CLK_CTRL 0x00 +#define ADAU1372_REG_PLL(x) (0x01 + (x)) +#define ADAU1372_REG_DAC_SOURCE 0x11 +#define ADAU1372_REG_SOUT_SOURCE_0_1 0x13 +#define ADAU1372_REG_SOUT_SOURCE_2_3 0x14 +#define ADAU1372_REG_SOUT_SOURCE_4_5 0x15 +#define ADAU1372_REG_SOUT_SOURCE_6_7 0x16 +#define ADAU1372_REG_ADC_SDATA_CH 0x17 +#define ADAU1372_REG_ASRCO_SOURCE_0_1 0x18 +#define ADAU1372_REG_ASRCO_SOURCE_2_3 0x19 +#define ADAU1372_REG_ASRC_MODE 0x1a +#define ADAU1372_REG_ADC_CTRL0 0x1b +#define ADAU1372_REG_ADC_CTRL1 0x1c +#define ADAU1372_REG_ADC_CTRL2 0x1d +#define ADAU1372_REG_ADC_CTRL3 0x1e +#define ADAU1372_REG_ADC_VOL(x) (0x1f + (x)) +#define ADAU1372_REG_PGA_CTRL(x) (0x23 + (x)) +#define ADAU1372_REG_PGA_BOOST 0x28 +#define ADAU1372_REG_MICBIAS 0x2d +#define ADAU1372_REG_DAC_CTRL 0x2e +#define ADAU1372_REG_DAC_VOL(x) (0x2f + (x)) +#define ADAU1372_REG_OP_STAGE_MUTE 0x31 +#define ADAU1372_REG_SAI0 0x32 +#define ADAU1372_REG_SAI1 0x33 +#define ADAU1372_REG_SOUT_CTRL 0x34 +#define ADAU1372_REG_MODE_MP(x) (0x38 + (x)) +#define ADAU1372_REG_OP_STAGE_CTRL 0x43 +#define ADAU1372_REG_DECIM_PWR 0x44 +#define ADAU1372_REG_INTERP_PWR 0x45 +#define ADAU1372_REG_BIAS_CTRL0 0x46 +#define ADAU1372_REG_BIAS_CTRL1 0x47 + +#define ADAU1372_CLK_CTRL_PLL_EN BIT(7) +#define ADAU1372_CLK_CTRL_XTAL_DIS BIT(4) +#define ADAU1372_CLK_CTRL_CLKSRC BIT(3) +#define ADAU1372_CLK_CTRL_CC_MDIV BIT(1) +#define ADAU1372_CLK_CTRL_MCLK_EN BIT(0) + +#define ADAU1372_SAI0_DELAY1 (0x0 << 6) +#define ADAU1372_SAI0_DELAY0 (0x1 << 6) +#define ADAU1372_SAI0_DELAY_MASK (0x3 << 6) +#define ADAU1372_SAI0_SAI_I2S (0x0 << 4) +#define ADAU1372_SAI0_SAI_TDM2 (0x1 << 4) +#define ADAU1372_SAI0_SAI_TDM4 (0x2 << 4) +#define ADAU1372_SAI0_SAI_TDM8 (0x3 << 4) +#define ADAU1372_SAI0_SAI_MASK (0x3 << 4) +#define ADAU1372_SAI0_FS_48 0x0 +#define ADAU1372_SAI0_FS_8 0x1 +#define ADAU1372_SAI0_FS_12 0x2 +#define ADAU1372_SAI0_FS_16 0x3 +#define ADAU1372_SAI0_FS_24 0x4 +#define ADAU1372_SAI0_FS_32 0x5 +#define ADAU1372_SAI0_FS_96 0x6 +#define ADAU1372_SAI0_FS_192 0x7 +#define ADAU1372_SAI0_FS_MASK 0xf + +#define ADAU1372_SAI1_TDM_TS BIT(7) +#define ADAU1372_SAI1_BCLK_TDMC BIT(6) +#define ADAU1372_SAI1_LR_MODE BIT(5) +#define ADAU1372_SAI1_LR_POL BIT(4) +#define ADAU1372_SAI1_BCLKRATE BIT(2) +#define ADAU1372_SAI1_BCLKEDGE BIT(1) +#define ADAU1372_SAI1_MS BIT(0) + +static const unsigned int adau1372_rates[] = { + [ADAU1372_SAI0_FS_8] = 8000, + [ADAU1372_SAI0_FS_12] = 12000, + [ADAU1372_SAI0_FS_16] = 16000, + [ADAU1372_SAI0_FS_24] = 24000, + [ADAU1372_SAI0_FS_32] = 32000, + [ADAU1372_SAI0_FS_48] = 48000, + [ADAU1372_SAI0_FS_96] = 96000, + [ADAU1372_SAI0_FS_192] = 192000, +}; + +/* 8k, 12k, 24k, 48k */ +#define ADAU1372_RATE_MASK_TDM8 0x17 +/* + 16k, 96k */ +#define ADAU1372_RATE_MASK_TDM4_MASTER (ADAU1372_RATE_MASK_TDM8 | 0x48 | 0x20) +/* +32k */ +#define ADAU1372_RATE_MASK_TDM4 (ADAU1372_RATE_MASK_TDM4_MASTER | 0x20) +/* + 192k */ +#define ADAU1372_RATE_MASK_TDM2 (ADAU1372_RATE_MASK_TDM4 | 0x80) + +static const DECLARE_TLV_DB_MINMAX(adau1372_digital_tlv, -9563, 0); +static const DECLARE_TLV_DB_SCALE(adau1372_pga_tlv, -1200, 75, 0); +static const DECLARE_TLV_DB_SCALE(adau1372_pga_boost_tlv, 0, 1000, 0); + +static const char * const adau1372_bias_text[] = { + "Normal operation", "Extreme power saving", "Enhanced performance", + "Power saving", +}; + +static const unsigned int adau1372_bias_adc_values[] = { + 0, 2, 3, +}; + +static const char * const adau1372_bias_adc_text[] = { + "Normal operation", "Enhanced performance", "Power saving", +}; + +static const char * const adau1372_bias_dac_text[] = { + "Normal operation", "Power saving", "Superior performance", + "Enhanced performance", +}; + +static SOC_ENUM_SINGLE_DECL(adau1372_bias_hp_enum, + ADAU1372_REG_BIAS_CTRL0, 6, adau1372_bias_text); +static SOC_ENUM_SINGLE_DECL(adau1372_bias_afe0_1_enum, + ADAU1372_REG_BIAS_CTRL0, 4, adau1372_bias_text); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_bias_adc2_3_enum, + ADAU1372_REG_BIAS_CTRL0, 2, 0x3, adau1372_bias_adc_text, + adau1372_bias_adc_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_bias_adc0_1_enum, + ADAU1372_REG_BIAS_CTRL0, 0, 0x3, adau1372_bias_adc_text, + adau1372_bias_adc_values); +static SOC_ENUM_SINGLE_DECL(adau1372_bias_afe2_3_enum, + ADAU1372_REG_BIAS_CTRL1, 4, adau1372_bias_text); +static SOC_ENUM_SINGLE_DECL(adau1372_bias_mic_enum, + ADAU1372_REG_BIAS_CTRL1, 2, adau1372_bias_text); +static SOC_ENUM_SINGLE_DECL(adau1372_bias_dac_enum, + ADAU1372_REG_BIAS_CTRL1, 0, adau1372_bias_dac_text); + +static const char * const adau1372_hpf_text[] = { + "Off", + "1 Hz", + "4 Hz", + "8 Hz", +}; + +static SOC_ENUM_SINGLE_DECL(adau1372_hpf0_1_enum, ADAU1372_REG_ADC_CTRL2, 5, + adau1372_hpf_text); +static SOC_ENUM_SINGLE_DECL(adau1372_hpf2_3_enum, ADAU1372_REG_ADC_CTRL3, 5, + adau1372_hpf_text); +static const struct snd_kcontrol_new adau1372_controls[] = { + SOC_SINGLE_TLV("ADC 0 Capture Volume", ADAU1372_REG_ADC_VOL(0), + 0, 0xff, 1, adau1372_digital_tlv), + SOC_SINGLE_TLV("ADC 1 Capture Volume", ADAU1372_REG_ADC_VOL(1), + 0, 0xff, 1, adau1372_digital_tlv), + SOC_SINGLE_TLV("ADC 2 Capture Volume", ADAU1372_REG_ADC_VOL(2), + 0, 0xff, 1, adau1372_digital_tlv), + SOC_SINGLE_TLV("ADC 3 Capture Volume", ADAU1372_REG_ADC_VOL(3), + 0, 0xff, 1, adau1372_digital_tlv), + SOC_SINGLE("ADC 0 Capture Switch", ADAU1372_REG_ADC_CTRL0, 3, 1, 1), + SOC_SINGLE("ADC 1 Capture Switch", ADAU1372_REG_ADC_CTRL0, 4, 1, 1), + SOC_SINGLE("ADC 2 Capture Switch", ADAU1372_REG_ADC_CTRL1, 3, 1, 1), + SOC_SINGLE("ADC 3 Capture Switch", ADAU1372_REG_ADC_CTRL1, 4, 1, 1), + + SOC_ENUM("ADC 0+1 High-Pass-Filter", adau1372_hpf0_1_enum), + SOC_ENUM("ADC 2+3 High-Pass-Filter", adau1372_hpf2_3_enum), + + SOC_SINGLE_TLV("PGA 0 Capture Volume", ADAU1372_REG_PGA_CTRL(0), + 0, 0x3f, 0, adau1372_pga_tlv), + SOC_SINGLE_TLV("PGA 1 Capture Volume", ADAU1372_REG_PGA_CTRL(1), + 0, 0x3f, 0, adau1372_pga_tlv), + SOC_SINGLE_TLV("PGA 2 Capture Volume", ADAU1372_REG_PGA_CTRL(2), + 0, 0x3f, 0, adau1372_pga_tlv), + SOC_SINGLE_TLV("PGA 3 Capture Volume", ADAU1372_REG_PGA_CTRL(3), + 0, 0x3f, 0, adau1372_pga_tlv), + SOC_SINGLE_TLV("PGA 0 Boost Capture Volume", ADAU1372_REG_PGA_BOOST, + 0, 1, 0, adau1372_pga_boost_tlv), + SOC_SINGLE_TLV("PGA 1 Boost Capture Volume", ADAU1372_REG_PGA_BOOST, + 1, 1, 0, adau1372_pga_boost_tlv), + SOC_SINGLE_TLV("PGA 2 Boost Capture Volume", ADAU1372_REG_PGA_BOOST, + 2, 1, 0, adau1372_pga_boost_tlv), + SOC_SINGLE_TLV("PGA 3 Boost Capture Volume", ADAU1372_REG_PGA_BOOST, + 3, 1, 0, adau1372_pga_boost_tlv), + SOC_SINGLE("PGA 0 Capture Switch", ADAU1372_REG_PGA_CTRL(0), 7, 1, 0), + SOC_SINGLE("PGA 1 Capture Switch", ADAU1372_REG_PGA_CTRL(1), 7, 1, 0), + SOC_SINGLE("PGA 2 Capture Switch", ADAU1372_REG_PGA_CTRL(2), 7, 1, 0), + SOC_SINGLE("PGA 3 Capture Switch", ADAU1372_REG_PGA_CTRL(3), 7, 1, 0), + + SOC_SINGLE_TLV("DAC 0 Playback Volume", ADAU1372_REG_DAC_VOL(0), + 0, 0xff, 1, adau1372_digital_tlv), + SOC_SINGLE_TLV("DAC 1 Playback Volume", ADAU1372_REG_DAC_VOL(1), + 0, 0xff, 1, adau1372_digital_tlv), + SOC_SINGLE("DAC 0 Playback Switch", ADAU1372_REG_DAC_CTRL, 3, 1, 1), + SOC_SINGLE("DAC 1 Playback Switch", ADAU1372_REG_DAC_CTRL, 4, 1, 1), + + SOC_ENUM("Headphone Bias", adau1372_bias_hp_enum), + SOC_ENUM("Microphone Bias", adau1372_bias_mic_enum), + SOC_ENUM("AFE 0+1 Bias", adau1372_bias_afe0_1_enum), + SOC_ENUM("AFE 2+3 Bias", adau1372_bias_afe2_3_enum), + SOC_ENUM("ADC 0+1 Bias", adau1372_bias_adc0_1_enum), + SOC_ENUM("ADC 2+3 Bias", adau1372_bias_adc2_3_enum), + SOC_ENUM("DAC 0+1 Bias", adau1372_bias_dac_enum), +}; + +static const char * const adau1372_decimator_mux_text[] = { + "ADC", + "DMIC", +}; + +static SOC_ENUM_SINGLE_DECL(adau1372_decimator0_1_mux_enum, ADAU1372_REG_ADC_CTRL2, + 2, adau1372_decimator_mux_text); + +static const struct snd_kcontrol_new adau1372_decimator0_1_mux_control = + SOC_DAPM_ENUM("Decimator 0+1 Capture Mux", adau1372_decimator0_1_mux_enum); + +static SOC_ENUM_SINGLE_DECL(adau1372_decimator2_3_mux_enum, ADAU1372_REG_ADC_CTRL3, + 2, adau1372_decimator_mux_text); + +static const struct snd_kcontrol_new adau1372_decimator2_3_mux_control = + SOC_DAPM_ENUM("Decimator 2+3 Capture Mux", adau1372_decimator2_3_mux_enum); + +static const unsigned int adau1372_asrco_mux_values[] = { + 4, 5, 6, 7, +}; + +static const char * const adau1372_asrco_mux_text[] = { + "Decimator0", + "Decimator1", + "Decimator2", + "Decimator3", +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_asrco0_mux_enum, ADAU1372_REG_ASRCO_SOURCE_0_1, + 0, 0xf, adau1372_asrco_mux_text, adau1372_asrco_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_asrco1_mux_enum, ADAU1372_REG_ASRCO_SOURCE_0_1, + 4, 0xf, adau1372_asrco_mux_text, adau1372_asrco_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_asrco2_mux_enum, ADAU1372_REG_ASRCO_SOURCE_2_3, + 0, 0xf, adau1372_asrco_mux_text, adau1372_asrco_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_asrco3_mux_enum, ADAU1372_REG_ASRCO_SOURCE_2_3, + 4, 0xf, adau1372_asrco_mux_text, adau1372_asrco_mux_values); + +static const struct snd_kcontrol_new adau1372_asrco0_mux_control = + SOC_DAPM_ENUM("Output ASRC0 Capture Mux", adau1372_asrco0_mux_enum); +static const struct snd_kcontrol_new adau1372_asrco1_mux_control = + SOC_DAPM_ENUM("Output ASRC1 Capture Mux", adau1372_asrco1_mux_enum); +static const struct snd_kcontrol_new adau1372_asrco2_mux_control = + SOC_DAPM_ENUM("Output ASRC2 Capture Mux", adau1372_asrco2_mux_enum); +static const struct snd_kcontrol_new adau1372_asrco3_mux_control = + SOC_DAPM_ENUM("Output ASRC3 Capture Mux", adau1372_asrco3_mux_enum); + +static const unsigned int adau1372_sout_mux_values[] = { + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 +}; + +static const char * const adau1372_sout_mux_text[] = { + "Output ASRC0", + "Output ASRC1", + "Output ASRC2", + "Output ASRC3", + "Serial Input 0", + "Serial Input 1", + "Serial Input 2", + "Serial Input 3", + "Serial Input 4", + "Serial Input 5", + "Serial Input 6", + "Serial Input 7", +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout0_mux_enum, ADAU1372_REG_SOUT_SOURCE_0_1, + 0, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout1_mux_enum, ADAU1372_REG_SOUT_SOURCE_0_1, + 4, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout2_mux_enum, ADAU1372_REG_SOUT_SOURCE_2_3, + 0, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout3_mux_enum, ADAU1372_REG_SOUT_SOURCE_2_3, + 4, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout4_mux_enum, ADAU1372_REG_SOUT_SOURCE_4_5, + 0, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout5_mux_enum, ADAU1372_REG_SOUT_SOURCE_4_5, + 4, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout6_mux_enum, ADAU1372_REG_SOUT_SOURCE_6_7, + 0, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout7_mux_enum, ADAU1372_REG_SOUT_SOURCE_6_7, + 4, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values); + +static const struct snd_kcontrol_new adau1372_sout0_mux_control = + SOC_DAPM_ENUM("Serial Output 0 Capture Mux", adau1372_sout0_mux_enum); +static const struct snd_kcontrol_new adau1372_sout1_mux_control = + SOC_DAPM_ENUM("Serial Output 1 Capture Mux", adau1372_sout1_mux_enum); +static const struct snd_kcontrol_new adau1372_sout2_mux_control = + SOC_DAPM_ENUM("Serial Output 2 Capture Mux", adau1372_sout2_mux_enum); +static const struct snd_kcontrol_new adau1372_sout3_mux_control = + SOC_DAPM_ENUM("Serial Output 3 Capture Mux", adau1372_sout3_mux_enum); +static const struct snd_kcontrol_new adau1372_sout4_mux_control = + SOC_DAPM_ENUM("Serial Output 4 Capture Mux", adau1372_sout4_mux_enum); +static const struct snd_kcontrol_new adau1372_sout5_mux_control = + SOC_DAPM_ENUM("Serial Output 5 Capture Mux", adau1372_sout5_mux_enum); +static const struct snd_kcontrol_new adau1372_sout6_mux_control = + SOC_DAPM_ENUM("Serial Output 6 Capture Mux", adau1372_sout6_mux_enum); +static const struct snd_kcontrol_new adau1372_sout7_mux_control = + SOC_DAPM_ENUM("Serial Output 7 Capture Mux", adau1372_sout7_mux_enum); + +static const char * const adau1372_asrci_mux_text[] = { + "Serial Input 0+1", + "Serial Input 2+3", + "Serial Input 4+5", + "Serial Input 6+7", +}; + +static SOC_ENUM_SINGLE_DECL(adau1372_asrci_mux_enum, + ADAU1372_REG_ASRC_MODE, 2, adau1372_asrci_mux_text); + +static const struct snd_kcontrol_new adau1372_asrci_mux_control = + SOC_DAPM_ENUM("Input ASRC Playback Mux", adau1372_asrci_mux_enum); + +static const unsigned int adau1372_dac_mux_values[] = { + 12, 13 +}; + +static const char * const adau1372_dac_mux_text[] = { + "Input ASRC0", + "Input ASRC1", +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_dac0_mux_enum, ADAU1372_REG_DAC_SOURCE, + 0, 0xf, adau1372_dac_mux_text, adau1372_dac_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_dac1_mux_enum, ADAU1372_REG_DAC_SOURCE, + 4, 0xf, adau1372_dac_mux_text, adau1372_dac_mux_values); + +static const struct snd_kcontrol_new adau1372_dac0_mux_control = + SOC_DAPM_ENUM("DAC 0 Playback Mux", adau1372_dac0_mux_enum); +static const struct snd_kcontrol_new adau1372_dac1_mux_control = + SOC_DAPM_ENUM("DAC 1 Playback Mux", adau1372_dac1_mux_enum); + +static const struct snd_soc_dapm_widget adau1372_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("AIN0"), + SND_SOC_DAPM_INPUT("AIN1"), + SND_SOC_DAPM_INPUT("AIN2"), + SND_SOC_DAPM_INPUT("AIN3"), + SND_SOC_DAPM_INPUT("DMIC0_1"), + SND_SOC_DAPM_INPUT("DMIC2_3"), + + SND_SOC_DAPM_SUPPLY("MICBIAS0", ADAU1372_REG_MICBIAS, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS1", ADAU1372_REG_MICBIAS, 5, 0, NULL, 0), + + SND_SOC_DAPM_PGA("PGA0", ADAU1372_REG_PGA_CTRL(0), 6, 1, NULL, 0), + SND_SOC_DAPM_PGA("PGA1", ADAU1372_REG_PGA_CTRL(1), 6, 1, NULL, 0), + SND_SOC_DAPM_PGA("PGA2", ADAU1372_REG_PGA_CTRL(2), 6, 1, NULL, 0), + SND_SOC_DAPM_PGA("PGA3", ADAU1372_REG_PGA_CTRL(3), 6, 1, NULL, 0), + SND_SOC_DAPM_ADC("ADC0", NULL, ADAU1372_REG_ADC_CTRL2, 0, 0), + SND_SOC_DAPM_ADC("ADC1", NULL, ADAU1372_REG_ADC_CTRL2, 1, 0), + SND_SOC_DAPM_ADC("ADC2", NULL, ADAU1372_REG_ADC_CTRL3, 0, 0), + SND_SOC_DAPM_ADC("ADC3", NULL, ADAU1372_REG_ADC_CTRL3, 1, 0), + + SND_SOC_DAPM_SUPPLY("ADC0 Filter", ADAU1372_REG_DECIM_PWR, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1 Filter", ADAU1372_REG_DECIM_PWR, 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC2 Filter", ADAU1372_REG_DECIM_PWR, 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC3 Filter", ADAU1372_REG_DECIM_PWR, 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Output ASRC0 Decimator", ADAU1372_REG_DECIM_PWR, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Output ASRC1 Decimator", ADAU1372_REG_DECIM_PWR, 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Output ASRC2 Decimator", ADAU1372_REG_DECIM_PWR, 6, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Output ASRC3 Decimator", ADAU1372_REG_DECIM_PWR, 7, 0, NULL, 0), + + SND_SOC_DAPM_MUX("Decimator0 Mux", SND_SOC_NOPM, 0, 0, &adau1372_decimator0_1_mux_control), + SND_SOC_DAPM_MUX("Decimator1 Mux", SND_SOC_NOPM, 0, 0, &adau1372_decimator0_1_mux_control), + SND_SOC_DAPM_MUX("Decimator2 Mux", SND_SOC_NOPM, 0, 0, &adau1372_decimator2_3_mux_control), + SND_SOC_DAPM_MUX("Decimator3 Mux", SND_SOC_NOPM, 0, 0, &adau1372_decimator2_3_mux_control), + + SND_SOC_DAPM_MUX("Output ASRC0 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrco0_mux_control), + SND_SOC_DAPM_MUX("Output ASRC1 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrco1_mux_control), + SND_SOC_DAPM_MUX("Output ASRC2 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrco2_mux_control), + SND_SOC_DAPM_MUX("Output ASRC3 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrco3_mux_control), + SND_SOC_DAPM_MUX("Serial Output 0 Capture Mux", SND_SOC_NOPM, 0, 0, + &adau1372_sout0_mux_control), + SND_SOC_DAPM_MUX("Serial Output 1 Capture Mux", SND_SOC_NOPM, 0, 0, + &adau1372_sout1_mux_control), + SND_SOC_DAPM_MUX("Serial Output 2 Capture Mux", SND_SOC_NOPM, 0, 0, + &adau1372_sout2_mux_control), + SND_SOC_DAPM_MUX("Serial Output 3 Capture Mux", SND_SOC_NOPM, 0, 0, + &adau1372_sout3_mux_control), + SND_SOC_DAPM_MUX("Serial Output 4 Capture Mux", SND_SOC_NOPM, 0, 0, + &adau1372_sout4_mux_control), + SND_SOC_DAPM_MUX("Serial Output 5 Capture Mux", SND_SOC_NOPM, 0, 0, + &adau1372_sout5_mux_control), + SND_SOC_DAPM_MUX("Serial Output 6 Capture Mux", SND_SOC_NOPM, 0, 0, + &adau1372_sout6_mux_control), + SND_SOC_DAPM_MUX("Serial Output 7 Capture Mux", SND_SOC_NOPM, 0, 0, + &adau1372_sout7_mux_control), + + SND_SOC_DAPM_AIF_IN("Serial Input 0", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Serial Input 1", NULL, 1, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Serial Input 2", NULL, 2, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Serial Input 3", NULL, 3, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Serial Input 4", NULL, 4, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Serial Input 5", NULL, 5, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Serial Input 6", NULL, 6, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Serial Input 7", NULL, 7, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_OUT("Serial Output 0", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("Serial Output 1", NULL, 1, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("Serial Output 2", NULL, 2, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("Serial Output 3", NULL, 3, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("Serial Output 4", NULL, 4, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("Serial Output 5", NULL, 5, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("Serial Output 6", NULL, 6, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("Serial Output 7", NULL, 7, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY("Output ASRC Supply", ADAU1372_REG_ASRC_MODE, 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Input ASRC Supply", ADAU1372_REG_ASRC_MODE, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DAC1 Modulator", ADAU1372_REG_INTERP_PWR, 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC0 Modulator", ADAU1372_REG_INTERP_PWR, 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Input ASRC1 Interpolator", ADAU1372_REG_INTERP_PWR, 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Input ASRC0 Interpolator", ADAU1372_REG_INTERP_PWR, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("Input ASRC0 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrci_mux_control), + SND_SOC_DAPM_MUX("Input ASRC1 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrci_mux_control), + + SND_SOC_DAPM_MUX("DAC 0 Mux", SND_SOC_NOPM, 0, 0, &adau1372_dac0_mux_control), + SND_SOC_DAPM_MUX("DAC 1 Mux", SND_SOC_NOPM, 0, 0, &adau1372_dac1_mux_control), + + SND_SOC_DAPM_DAC("DAC0", NULL, ADAU1372_REG_DAC_CTRL, 0, 0), + SND_SOC_DAPM_DAC("DAC1", NULL, ADAU1372_REG_DAC_CTRL, 1, 0), + + SND_SOC_DAPM_OUT_DRV("OP_STAGE_LP", ADAU1372_REG_OP_STAGE_CTRL, 0, 1, NULL, 0), + SND_SOC_DAPM_OUT_DRV("OP_STAGE_LN", ADAU1372_REG_OP_STAGE_CTRL, 1, 1, NULL, 0), + SND_SOC_DAPM_OUT_DRV("OP_STAGE_RP", ADAU1372_REG_OP_STAGE_CTRL, 2, 1, NULL, 0), + SND_SOC_DAPM_OUT_DRV("OP_STAGE_RN", ADAU1372_REG_OP_STAGE_CTRL, 3, 1, NULL, 0), + + SND_SOC_DAPM_OUTPUT("HPOUTL"), + SND_SOC_DAPM_OUTPUT("HPOUTR"), +}; + +#define ADAU1372_SOUT_ROUTES(x) \ + { "Serial Output " #x " Capture Mux", "Output ASRC0", "Output ASRC0 Mux" }, \ + { "Serial Output " #x " Capture Mux", "Output ASRC1", "Output ASRC1 Mux" }, \ + { "Serial Output " #x " Capture Mux", "Output ASRC2", "Output ASRC2 Mux" }, \ + { "Serial Output " #x " Capture Mux", "Output ASRC3", "Output ASRC3 Mux" }, \ + { "Serial Output " #x " Capture Mux", "Serial Input 0", "Serial Input 0" }, \ + { "Serial Output " #x " Capture Mux", "Serial Input 1", "Serial Input 1" }, \ + { "Serial Output " #x " Capture Mux", "Serial Input 2", "Serial Input 2" }, \ + { "Serial Output " #x " Capture Mux", "Serial Input 3", "Serial Input 3" }, \ + { "Serial Output " #x " Capture Mux", "Serial Input 4", "Serial Input 4" }, \ + { "Serial Output " #x " Capture Mux", "Serial Input 5", "Serial Input 5" }, \ + { "Serial Output " #x " Capture Mux", "Serial Input 6", "Serial Input 6" }, \ + { "Serial Output " #x " Capture Mux", "Serial Input 7", "Serial Input 7" }, \ + { "Serial Output " #x, NULL, "Serial Output " #x " Capture Mux" }, \ + { "Capture", NULL, "Serial Output " #x } + +#define ADAU1372_ASRCO_ROUTES(x) \ + { "Output ASRC" #x " Mux", "Decimator0", "Decimator0 Mux" }, \ + { "Output ASRC" #x " Mux", "Decimator1", "Decimator1 Mux" }, \ + { "Output ASRC" #x " Mux", "Decimator2", "Decimator2 Mux" }, \ + { "Output ASRC" #x " Mux", "Decimator3", "Decimator3 Mux" } + +static const struct snd_soc_dapm_route adau1372_dapm_routes[] = { + { "PGA0", NULL, "AIN0" }, + { "PGA1", NULL, "AIN1" }, + { "PGA2", NULL, "AIN2" }, + { "PGA3", NULL, "AIN3" }, + + { "ADC0", NULL, "PGA0" }, + { "ADC1", NULL, "PGA1" }, + { "ADC2", NULL, "PGA2" }, + { "ADC3", NULL, "PGA3" }, + + { "Decimator0 Mux", "ADC", "ADC0" }, + { "Decimator1 Mux", "ADC", "ADC1" }, + { "Decimator2 Mux", "ADC", "ADC2" }, + { "Decimator3 Mux", "ADC", "ADC3" }, + + { "Decimator0 Mux", "DMIC", "DMIC0_1" }, + { "Decimator1 Mux", "DMIC", "DMIC0_1" }, + { "Decimator2 Mux", "DMIC", "DMIC2_3" }, + { "Decimator3 Mux", "DMIC", "DMIC2_3" }, + + { "Decimator0 Mux", NULL, "ADC0 Filter" }, + { "Decimator1 Mux", NULL, "ADC1 Filter" }, + { "Decimator2 Mux", NULL, "ADC2 Filter" }, + { "Decimator3 Mux", NULL, "ADC3 Filter" }, + + { "Output ASRC0 Mux", NULL, "Output ASRC Supply" }, + { "Output ASRC1 Mux", NULL, "Output ASRC Supply" }, + { "Output ASRC2 Mux", NULL, "Output ASRC Supply" }, + { "Output ASRC3 Mux", NULL, "Output ASRC Supply" }, + { "Output ASRC0 Mux", NULL, "Output ASRC0 Decimator" }, + { "Output ASRC1 Mux", NULL, "Output ASRC1 Decimator" }, + { "Output ASRC2 Mux", NULL, "Output ASRC2 Decimator" }, + { "Output ASRC3 Mux", NULL, "Output ASRC3 Decimator" }, + + ADAU1372_ASRCO_ROUTES(0), + ADAU1372_ASRCO_ROUTES(1), + ADAU1372_ASRCO_ROUTES(2), + ADAU1372_ASRCO_ROUTES(3), + + ADAU1372_SOUT_ROUTES(0), + ADAU1372_SOUT_ROUTES(1), + ADAU1372_SOUT_ROUTES(2), + ADAU1372_SOUT_ROUTES(3), + ADAU1372_SOUT_ROUTES(4), + ADAU1372_SOUT_ROUTES(5), + ADAU1372_SOUT_ROUTES(6), + ADAU1372_SOUT_ROUTES(7), + + { "Serial Input 0", NULL, "Playback" }, + { "Serial Input 1", NULL, "Playback" }, + { "Serial Input 2", NULL, "Playback" }, + { "Serial Input 3", NULL, "Playback" }, + { "Serial Input 4", NULL, "Playback" }, + { "Serial Input 5", NULL, "Playback" }, + { "Serial Input 6", NULL, "Playback" }, + { "Serial Input 7", NULL, "Playback" }, + + { "Input ASRC0 Mux", "Serial Input 0+1", "Serial Input 0" }, + { "Input ASRC1 Mux", "Serial Input 0+1", "Serial Input 1" }, + { "Input ASRC0 Mux", "Serial Input 2+3", "Serial Input 2" }, + { "Input ASRC1 Mux", "Serial Input 2+3", "Serial Input 3" }, + { "Input ASRC0 Mux", "Serial Input 4+5", "Serial Input 4" }, + { "Input ASRC1 Mux", "Serial Input 4+5", "Serial Input 5" }, + { "Input ASRC0 Mux", "Serial Input 6+7", "Serial Input 6" }, + { "Input ASRC1 Mux", "Serial Input 6+7", "Serial Input 7" }, + { "Input ASRC0 Mux", NULL, "Input ASRC Supply" }, + { "Input ASRC1 Mux", NULL, "Input ASRC Supply" }, + { "Input ASRC0 Mux", NULL, "Input ASRC0 Interpolator" }, + { "Input ASRC1 Mux", NULL, "Input ASRC1 Interpolator" }, + + { "DAC 0 Mux", "Input ASRC0", "Input ASRC0 Mux" }, + { "DAC 0 Mux", "Input ASRC1", "Input ASRC1 Mux" }, + { "DAC 1 Mux", "Input ASRC0", "Input ASRC0 Mux" }, + { "DAC 1 Mux", "Input ASRC1", "Input ASRC1 Mux" }, + + { "DAC0", NULL, "DAC 0 Mux" }, + { "DAC1", NULL, "DAC 1 Mux" }, + { "DAC0", NULL, "DAC0 Modulator" }, + { "DAC1", NULL, "DAC1 Modulator" }, + + { "OP_STAGE_LP", NULL, "DAC0" }, + { "OP_STAGE_LN", NULL, "DAC0" }, + { "OP_STAGE_RP", NULL, "DAC1" }, + { "OP_STAGE_RN", NULL, "DAC1" }, + + { "HPOUTL", NULL, "OP_STAGE_LP" }, + { "HPOUTL", NULL, "OP_STAGE_LN" }, + { "HPOUTR", NULL, "OP_STAGE_RP" }, + { "HPOUTR", NULL, "OP_STAGE_RN" }, +}; + +static int adau1372_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai); + unsigned int sai0 = 0, sai1 = 0; + bool invert_lrclk = false; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + adau1372->clock_provider = true; + sai1 |= ADAU1372_SAI1_MS; + break; + case SND_SOC_DAIFMT_CBC_CFC: + adau1372->clock_provider = false; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + invert_lrclk = false; + break; + case SND_SOC_DAIFMT_NB_IF: + invert_lrclk = true; + break; + case SND_SOC_DAIFMT_IB_NF: + invert_lrclk = false; + sai1 |= ADAU1372_SAI1_BCLKEDGE; + break; + case SND_SOC_DAIFMT_IB_IF: + invert_lrclk = true; + sai1 |= ADAU1372_SAI1_BCLKEDGE; + break; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + sai0 |= ADAU1372_SAI0_DELAY1; + break; + case SND_SOC_DAIFMT_LEFT_J: + sai0 |= ADAU1372_SAI0_DELAY0; + invert_lrclk = !invert_lrclk; + break; + case SND_SOC_DAIFMT_DSP_A: + sai0 |= ADAU1372_SAI0_DELAY1; + sai1 |= ADAU1372_SAI1_LR_MODE; + break; + case SND_SOC_DAIFMT_DSP_B: + sai0 |= ADAU1372_SAI0_DELAY0; + sai1 |= ADAU1372_SAI1_LR_MODE; + break; + } + + if (invert_lrclk) + sai1 |= ADAU1372_SAI1_LR_POL; + + regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI0, ADAU1372_SAI0_DELAY_MASK, sai0); + regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI1, + ADAU1372_SAI1_MS | ADAU1372_SAI1_BCLKEDGE | + ADAU1372_SAI1_LR_MODE | ADAU1372_SAI1_LR_POL, sai1); + + return 0; +} + +static int adau1372_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai); + unsigned int rate = params_rate(params); + unsigned int slot_width; + unsigned int sai0, sai1; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(adau1372_rates); i++) { + if (rate == adau1372_rates[i]) + break; + } + + if (i == ARRAY_SIZE(adau1372_rates)) + return -EINVAL; + + sai0 = i; + + slot_width = adau1372->slot_width; + if (slot_width == 0) + slot_width = params_width(params); + + switch (slot_width) { + case 16: + sai1 = ADAU1372_SAI1_BCLKRATE; + break; + case 24: + case 32: + sai1 = 0; + break; + default: + return -EINVAL; + } + + regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI0, ADAU1372_SAI0_FS_MASK, sai0); + regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI1, ADAU1372_SAI1_BCLKRATE, sai1); + + return 0; +} + +static int adau1372_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int width) +{ + struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai); + unsigned int sai0, sai1; + + /* I2S mode */ + if (slots == 0) { + /* The other settings dont matter in I2S mode */ + regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI0, + ADAU1372_SAI0_SAI_MASK, ADAU1372_SAI0_SAI_I2S); + adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM2; + adau1372->slot_width = 0; + return 0; + } + + /* We have 8 channels anything outside that is not supported */ + if ((tx_mask & ~0xff) != 0 || (rx_mask & ~0xff) != 0) + return -EINVAL; + + switch (width) { + case 16: + sai1 = ADAU1372_SAI1_BCLK_TDMC; + break; + case 24: + case 32: + sai1 = 0; + break; + default: + return -EINVAL; + } + + switch (slots) { + case 2: + sai0 = ADAU1372_SAI0_SAI_TDM2; + adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM2; + break; + case 4: + sai0 = ADAU1372_SAI0_SAI_TDM4; + if (adau1372->clock_provider) + adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM4_MASTER; + else + adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM4; + break; + case 8: + sai0 = ADAU1372_SAI0_SAI_TDM8; + adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM8; + break; + default: + return -EINVAL; + } + + adau1372->slot_width = width; + + regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI0, ADAU1372_SAI0_SAI_MASK, sai0); + regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI1, ADAU1372_SAI1_BCLK_TDMC, sai1); + + /* Mask is inverted in hardware */ + regmap_write(adau1372->regmap, ADAU1372_REG_SOUT_CTRL, ~tx_mask); + + return 0; +} + +static int adau1372_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai); + unsigned int sai1; + + if (tristate) + sai1 = ADAU1372_SAI1_TDM_TS; + else + sai1 = 0; + + return regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI1, ADAU1372_SAI1_TDM_TS, sai1); +} + +static int adau1372_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai); + + snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &adau1372->rate_constraints); + + return 0; +} + +static void adau1372_enable_pll(struct adau1372 *adau1372) +{ + unsigned int val, timeout = 0; + int ret; + + regmap_update_bits(adau1372->regmap, ADAU1372_REG_CLK_CTRL, + ADAU1372_CLK_CTRL_PLL_EN, ADAU1372_CLK_CTRL_PLL_EN); + do { + /* Takes about 1ms to lock */ + usleep_range(1000, 2000); + ret = regmap_read(adau1372->regmap, ADAU1372_REG_PLL(5), &val); + if (ret) + break; + timeout++; + } while (!(val & 1) && timeout < 3); + + if (ret < 0 || !(val & 1)) + dev_err(adau1372->dev, "Failed to lock PLL\n"); +} + +static void adau1372_set_power(struct adau1372 *adau1372, bool enable) +{ + if (adau1372->enabled == enable) + return; + + if (enable) { + unsigned int clk_ctrl = ADAU1372_CLK_CTRL_MCLK_EN; + + clk_prepare_enable(adau1372->mclk); + if (adau1372->pd_gpio) + gpiod_set_value(adau1372->pd_gpio, 0); + + if (adau1372->switch_mode) + adau1372->switch_mode(adau1372->dev); + + regcache_cache_only(adau1372->regmap, false); + + /* + * Clocks needs to be enabled before any other register can be + * accessed. + */ + if (adau1372->use_pll) { + adau1372_enable_pll(adau1372); + clk_ctrl |= ADAU1372_CLK_CTRL_CLKSRC; + } + + regmap_update_bits(adau1372->regmap, ADAU1372_REG_CLK_CTRL, + ADAU1372_CLK_CTRL_MCLK_EN | ADAU1372_CLK_CTRL_CLKSRC, clk_ctrl); + regcache_sync(adau1372->regmap); + } else { + if (adau1372->pd_gpio) { + /* + * This will turn everything off and reset the register + * map. No need to do any register writes to manually + * turn things off. + */ + gpiod_set_value(adau1372->pd_gpio, 1); + regcache_mark_dirty(adau1372->regmap); + } else { + regmap_update_bits(adau1372->regmap, ADAU1372_REG_CLK_CTRL, + ADAU1372_CLK_CTRL_MCLK_EN | ADAU1372_CLK_CTRL_PLL_EN, 0); + } + clk_disable_unprepare(adau1372->mclk); + regcache_cache_only(adau1372->regmap, true); + } + + adau1372->enabled = enable; +} + +static int adau1372_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct adau1372 *adau1372 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + adau1372_set_power(adau1372, true); + break; + case SND_SOC_BIAS_OFF: + adau1372_set_power(adau1372, false); + break; + } + + return 0; +} + +static const struct snd_soc_component_driver adau1372_driver = { + .set_bias_level = adau1372_set_bias_level, + .controls = adau1372_controls, + .num_controls = ARRAY_SIZE(adau1372_controls), + .dapm_widgets = adau1372_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adau1372_dapm_widgets), + .dapm_routes = adau1372_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(adau1372_dapm_routes), + .endianness = 1, +}; + +static const struct snd_soc_dai_ops adau1372_dai_ops = { + .set_fmt = adau1372_set_dai_fmt, + .set_tdm_slot = adau1372_set_tdm_slot, + .set_tristate = adau1372_set_tristate, + .hw_params = adau1372_hw_params, + .startup = adau1372_startup, +}; + +#define ADAU1372_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver adau1372_dai_driver = { + .name = "adau1372", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = ADAU1372_FORMATS, + .sig_bits = 24, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = ADAU1372_FORMATS, + .sig_bits = 24, + }, + .ops = &adau1372_dai_ops, + .symmetric_rate = 1, +}; + +static int adau1372_setup_pll(struct adau1372 *adau1372, unsigned int rate) +{ + u8 regs[5]; + unsigned int i; + int ret; + + ret = adau_calc_pll_cfg(rate, 49152000, regs); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(regs); i++) + regmap_write(adau1372->regmap, ADAU1372_REG_PLL(i), regs[i]); + + return 0; +} + +int adau1372_probe(struct device *dev, struct regmap *regmap, + void (*switch_mode)(struct device *dev)) +{ + struct adau1372 *adau1372; + unsigned int clk_ctrl; + unsigned long rate; + int ret; + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + adau1372 = devm_kzalloc(dev, sizeof(*adau1372), GFP_KERNEL); + if (!adau1372) + return -ENOMEM; + + adau1372->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(adau1372->mclk)) + return PTR_ERR(adau1372->mclk); + + adau1372->pd_gpio = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); + if (IS_ERR(adau1372->pd_gpio)) + return PTR_ERR(adau1372->pd_gpio); + + adau1372->regmap = regmap; + adau1372->switch_mode = switch_mode; + adau1372->dev = dev; + adau1372->rate_constraints.list = adau1372_rates; + adau1372->rate_constraints.count = ARRAY_SIZE(adau1372_rates); + adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM2; + + dev_set_drvdata(dev, adau1372); + + /* + * The datasheet says that the internal MCLK always needs to run at + * 12.288MHz. Automatically choose a valid configuration from the + * external clock. + */ + rate = clk_get_rate(adau1372->mclk); + + switch (rate) { + case 12288000: + clk_ctrl = ADAU1372_CLK_CTRL_CC_MDIV; + break; + case 24576000: + clk_ctrl = 0; + break; + default: + clk_ctrl = 0; + ret = adau1372_setup_pll(adau1372, rate); + if (ret < 0) + return ret; + adau1372->use_pll = true; + break; + } + + /* + * Most of the registers are inaccessible unless the internal clock is + * enabled. + */ + regcache_cache_only(regmap, true); + + regmap_update_bits(regmap, ADAU1372_REG_CLK_CTRL, ADAU1372_CLK_CTRL_CC_MDIV, clk_ctrl); + + /* + * No pinctrl support yet, put the multi-purpose pins in the most + * sensible mode for general purpose CODEC operation. + */ + regmap_write(regmap, ADAU1372_REG_MODE_MP(1), 0x00); /* SDATA OUT */ + regmap_write(regmap, ADAU1372_REG_MODE_MP(6), 0x12); /* CLOCKOUT */ + + regmap_write(regmap, ADAU1372_REG_OP_STAGE_MUTE, 0x0); + + regmap_write(regmap, 0x7, 0x01); /* CLOCK OUT */ + + return devm_snd_soc_register_component(dev, &adau1372_driver, &adau1372_dai_driver, 1); +} +EXPORT_SYMBOL(adau1372_probe); + +static const struct reg_default adau1372_reg_defaults[] = { + { ADAU1372_REG_CLK_CTRL, 0x00 }, + { ADAU1372_REG_PLL(0), 0x00 }, + { ADAU1372_REG_PLL(1), 0x00 }, + { ADAU1372_REG_PLL(2), 0x00 }, + { ADAU1372_REG_PLL(3), 0x00 }, + { ADAU1372_REG_PLL(4), 0x00 }, + { ADAU1372_REG_PLL(5), 0x00 }, + { ADAU1372_REG_DAC_SOURCE, 0x10 }, + { ADAU1372_REG_SOUT_SOURCE_0_1, 0x54 }, + { ADAU1372_REG_SOUT_SOURCE_2_3, 0x76 }, + { ADAU1372_REG_SOUT_SOURCE_4_5, 0x54 }, + { ADAU1372_REG_SOUT_SOURCE_6_7, 0x76 }, + { ADAU1372_REG_ADC_SDATA_CH, 0x04 }, + { ADAU1372_REG_ASRCO_SOURCE_0_1, 0x10 }, + { ADAU1372_REG_ASRCO_SOURCE_2_3, 0x32 }, + { ADAU1372_REG_ASRC_MODE, 0x00 }, + { ADAU1372_REG_ADC_CTRL0, 0x19 }, + { ADAU1372_REG_ADC_CTRL1, 0x19 }, + { ADAU1372_REG_ADC_CTRL2, 0x00 }, + { ADAU1372_REG_ADC_CTRL3, 0x00 }, + { ADAU1372_REG_ADC_VOL(0), 0x00 }, + { ADAU1372_REG_ADC_VOL(1), 0x00 }, + { ADAU1372_REG_ADC_VOL(2), 0x00 }, + { ADAU1372_REG_ADC_VOL(3), 0x00 }, + { ADAU1372_REG_PGA_CTRL(0), 0x40 }, + { ADAU1372_REG_PGA_CTRL(1), 0x40 }, + { ADAU1372_REG_PGA_CTRL(2), 0x40 }, + { ADAU1372_REG_PGA_CTRL(3), 0x40 }, + { ADAU1372_REG_PGA_BOOST, 0x00 }, + { ADAU1372_REG_MICBIAS, 0x00 }, + { ADAU1372_REG_DAC_CTRL, 0x18 }, + { ADAU1372_REG_DAC_VOL(0), 0x00 }, + { ADAU1372_REG_DAC_VOL(1), 0x00 }, + { ADAU1372_REG_OP_STAGE_MUTE, 0x0f }, + { ADAU1372_REG_SAI0, 0x00 }, + { ADAU1372_REG_SAI1, 0x00 }, + { ADAU1372_REG_SOUT_CTRL, 0x00 }, + { ADAU1372_REG_MODE_MP(0), 0x00 }, + { ADAU1372_REG_MODE_MP(1), 0x10 }, + { ADAU1372_REG_MODE_MP(4), 0x00 }, + { ADAU1372_REG_MODE_MP(5), 0x00 }, + { ADAU1372_REG_MODE_MP(6), 0x11 }, + { ADAU1372_REG_OP_STAGE_CTRL, 0x0f }, + { ADAU1372_REG_DECIM_PWR, 0x00 }, + { ADAU1372_REG_INTERP_PWR, 0x00 }, + { ADAU1372_REG_BIAS_CTRL0, 0x00 }, + { ADAU1372_REG_BIAS_CTRL1, 0x00 }, +}; + +static bool adau1372_volatile_register(struct device *dev, unsigned int reg) +{ + if (reg == ADAU1372_REG_PLL(5)) + return true; + + return false; +} + +const struct regmap_config adau1372_regmap_config = { + .val_bits = 8, + .reg_bits = 16, + .max_register = 0x4d, + + .reg_defaults = adau1372_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(adau1372_reg_defaults), + .volatile_reg = adau1372_volatile_register, + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL_GPL(adau1372_regmap_config); + +MODULE_DESCRIPTION("ASoC ADAU1372 CODEC driver"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3