diff options
author | 2023-02-21 18:24:12 -0800 | |
---|---|---|
committer | 2023-02-21 18:24:12 -0800 | |
commit | 5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 (patch) | |
tree | cc5c2d0a898769fd59549594fedb3ee6f84e59a0 /sound/soc/codecs/tfa989x.c | |
download | linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.tar.gz linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.zip |
Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-nextgrafted
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().
...
Diffstat (limited to 'sound/soc/codecs/tfa989x.c')
-rw-r--r-- | sound/soc/codecs/tfa989x.c | 425 |
1 files changed, 425 insertions, 0 deletions
diff --git a/sound/soc/codecs/tfa989x.c b/sound/soc/codecs/tfa989x.c new file mode 100644 index 000000000..b853507e6 --- /dev/null +++ b/sound/soc/codecs/tfa989x.c @@ -0,0 +1,425 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021 Stephan Gerhold + * + * Register definitions/sequences taken from various tfa98xx kernel drivers: + * Copyright (C) 2014-2020 NXP Semiconductors, All Rights Reserved. + * Copyright (C) 2013 Sony Mobile Communications Inc. + */ + +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <sound/soc.h> + +#define TFA989X_STATUSREG 0x00 +#define TFA989X_BATTERYVOLTAGE 0x01 +#define TFA989X_TEMPERATURE 0x02 +#define TFA989X_REVISIONNUMBER 0x03 +#define TFA989X_REVISIONNUMBER_REV_MSK GENMASK(7, 0) /* device revision */ +#define TFA989X_I2SREG 0x04 +#define TFA989X_I2SREG_RCV 2 /* receiver mode */ +#define TFA989X_I2SREG_CHSA 6 /* amplifier input select */ +#define TFA989X_I2SREG_CHSA_MSK GENMASK(7, 6) +#define TFA989X_I2SREG_I2SSR 12 /* sample rate */ +#define TFA989X_I2SREG_I2SSR_MSK GENMASK(15, 12) +#define TFA989X_BAT_PROT 0x05 +#define TFA989X_AUDIO_CTR 0x06 +#define TFA989X_DCDCBOOST 0x07 +#define TFA989X_SPKR_CALIBRATION 0x08 +#define TFA989X_SYS_CTRL 0x09 +#define TFA989X_SYS_CTRL_PWDN 0 /* power down */ +#define TFA989X_SYS_CTRL_I2CR 1 /* I2C reset */ +#define TFA989X_SYS_CTRL_CFE 2 /* enable CoolFlux DSP */ +#define TFA989X_SYS_CTRL_AMPE 3 /* enable amplifier */ +#define TFA989X_SYS_CTRL_DCA 4 /* enable boost */ +#define TFA989X_SYS_CTRL_SBSL 5 /* DSP configured */ +#define TFA989X_SYS_CTRL_AMPC 6 /* amplifier enabled by DSP */ +#define TFA989X_I2S_SEL_REG 0x0a +#define TFA989X_I2S_SEL_REG_SPKR_MSK GENMASK(10, 9) /* speaker impedance */ +#define TFA989X_I2S_SEL_REG_DCFG_MSK GENMASK(14, 11) /* DCDC compensation */ +#define TFA989X_HIDE_UNHIDE_KEY 0x40 +#define TFA989X_PWM_CONTROL 0x41 +#define TFA989X_CURRENTSENSE1 0x46 +#define TFA989X_CURRENTSENSE2 0x47 +#define TFA989X_CURRENTSENSE3 0x48 +#define TFA989X_CURRENTSENSE4 0x49 + +#define TFA9890_REVISION 0x80 +#define TFA9895_REVISION 0x12 +#define TFA9897_REVISION 0x97 + +struct tfa989x_rev { + unsigned int rev; + int (*init)(struct regmap *regmap); +}; + +struct tfa989x { + const struct tfa989x_rev *rev; + struct regulator *vddd_supply; + struct gpio_desc *rcv_gpiod; +}; + +static bool tfa989x_writeable_reg(struct device *dev, unsigned int reg) +{ + return reg > TFA989X_REVISIONNUMBER; +} + +static bool tfa989x_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg < TFA989X_REVISIONNUMBER; +} + +static const struct regmap_config tfa989x_regmap = { + .reg_bits = 8, + .val_bits = 16, + + .writeable_reg = tfa989x_writeable_reg, + .volatile_reg = tfa989x_volatile_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static const char * const chsa_text[] = { "Left", "Right", /* "DSP" */ }; +static SOC_ENUM_SINGLE_DECL(chsa_enum, TFA989X_I2SREG, TFA989X_I2SREG_CHSA, chsa_text); +static const struct snd_kcontrol_new chsa_mux = SOC_DAPM_ENUM("Amp Input", chsa_enum); + +static const struct snd_soc_dapm_widget tfa989x_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("OUT"), + SND_SOC_DAPM_SUPPLY("POWER", TFA989X_SYS_CTRL, TFA989X_SYS_CTRL_PWDN, 1, NULL, 0), + SND_SOC_DAPM_OUT_DRV("AMPE", TFA989X_SYS_CTRL, TFA989X_SYS_CTRL_AMPE, 0, NULL, 0), + + SND_SOC_DAPM_MUX("Amp Input", SND_SOC_NOPM, 0, 0, &chsa_mux), + SND_SOC_DAPM_AIF_IN("AIFINL", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIFINR", "HiFi Playback", 1, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route tfa989x_dapm_routes[] = { + {"OUT", NULL, "AMPE"}, + {"AMPE", NULL, "POWER"}, + {"AMPE", NULL, "Amp Input"}, + {"Amp Input", "Left", "AIFINL"}, + {"Amp Input", "Right", "AIFINR"}, +}; + +static int tfa989x_put_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct tfa989x *tfa989x = snd_soc_component_get_drvdata(component); + + gpiod_set_value_cansleep(tfa989x->rcv_gpiod, ucontrol->value.enumerated.item[0]); + + return snd_soc_put_enum_double(kcontrol, ucontrol); +} + +static const char * const mode_text[] = { "Speaker", "Receiver" }; +static SOC_ENUM_SINGLE_DECL(mode_enum, TFA989X_I2SREG, TFA989X_I2SREG_RCV, mode_text); +static const struct snd_kcontrol_new tfa989x_mode_controls[] = { + SOC_ENUM_EXT("Mode", mode_enum, snd_soc_get_enum_double, tfa989x_put_mode), +}; + +static int tfa989x_probe(struct snd_soc_component *component) +{ + struct tfa989x *tfa989x = snd_soc_component_get_drvdata(component); + + if (tfa989x->rev->rev == TFA9897_REVISION) + return snd_soc_add_component_controls(component, tfa989x_mode_controls, + ARRAY_SIZE(tfa989x_mode_controls)); + + return 0; +} + +static const struct snd_soc_component_driver tfa989x_component = { + .probe = tfa989x_probe, + .dapm_widgets = tfa989x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tfa989x_dapm_widgets), + .dapm_routes = tfa989x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(tfa989x_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const unsigned int tfa989x_rates[] = { + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 +}; + +static int tfa989x_find_sample_rate(unsigned int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(tfa989x_rates); ++i) + if (tfa989x_rates[i] == rate) + return i; + + return -EINVAL; +} + +static int tfa989x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + int sr; + + sr = tfa989x_find_sample_rate(params_rate(params)); + if (sr < 0) + return sr; + + return snd_soc_component_update_bits(component, TFA989X_I2SREG, + TFA989X_I2SREG_I2SSR_MSK, + sr << TFA989X_I2SREG_I2SSR); +} + +static const struct snd_soc_dai_ops tfa989x_dai_ops = { + .hw_params = tfa989x_hw_params, +}; + +static struct snd_soc_dai_driver tfa989x_dai = { + .name = "tfa989x-hifi", + .playback = { + .stream_name = "HiFi Playback", + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tfa989x_dai_ops, +}; + +static int tfa9890_init(struct regmap *regmap) +{ + int ret; + + /* temporarily allow access to hidden registers */ + ret = regmap_write(regmap, TFA989X_HIDE_UNHIDE_KEY, 0x5a6b); + if (ret) + return ret; + + /* update PLL registers */ + ret = regmap_set_bits(regmap, 0x59, 0x3); + if (ret) + return ret; + + /* hide registers again */ + ret = regmap_write(regmap, TFA989X_HIDE_UNHIDE_KEY, 0x0000); + if (ret) + return ret; + + return regmap_write(regmap, TFA989X_CURRENTSENSE2, 0x7BE1); +} + +static const struct tfa989x_rev tfa9890_rev = { + .rev = TFA9890_REVISION, + .init = tfa9890_init, +}; + +static const struct reg_sequence tfa9895_reg_init[] = { + /* some other registers must be set for optimal amplifier behaviour */ + { TFA989X_BAT_PROT, 0x13ab }, + { TFA989X_AUDIO_CTR, 0x001f }, + + /* peak voltage protection is always on, but may be written */ + { TFA989X_SPKR_CALIBRATION, 0x3c4e }, + + /* TFA989X_SYSCTRL_DCA = 0 */ + { TFA989X_SYS_CTRL, 0x024d }, + { TFA989X_PWM_CONTROL, 0x0308 }, + { TFA989X_CURRENTSENSE4, 0x0e82 }, +}; + +static int tfa9895_init(struct regmap *regmap) +{ + return regmap_multi_reg_write(regmap, tfa9895_reg_init, + ARRAY_SIZE(tfa9895_reg_init)); +} + +static const struct tfa989x_rev tfa9895_rev = { + .rev = TFA9895_REVISION, + .init = tfa9895_init, +}; + +static int tfa9897_init(struct regmap *regmap) +{ + int ret; + + /* Reduce slewrate by clearing iddqtestbst to avoid booster damage */ + ret = regmap_write(regmap, TFA989X_CURRENTSENSE3, 0x0300); + if (ret) + return ret; + + /* Enable clipping */ + ret = regmap_clear_bits(regmap, TFA989X_CURRENTSENSE4, 0x1); + if (ret) + return ret; + + /* Set required TDM configuration */ + return regmap_write(regmap, 0x14, 0x0); +} + +static const struct tfa989x_rev tfa9897_rev = { + .rev = TFA9897_REVISION, + .init = tfa9897_init, +}; + +/* + * Note: At the moment this driver bypasses the "CoolFlux DSP" built into the + * TFA989X amplifiers. Unfortunately, there seems to be absolutely + * no documentation for it - the public "short datasheets" do not provide + * any information about the DSP or available registers. + * + * Usually the TFA989X amplifiers are configured through proprietary userspace + * libraries. There are also some (rather complex) kernel drivers but even those + * rely on obscure firmware blobs for configuration (so-called "containers"). + * They seem to contain different "profiles" with tuned speaker settings, sample + * rates and volume steps (which would be better exposed as separate ALSA mixers). + * + * Bypassing the DSP disables volume control (and perhaps some speaker + * optimization?), but at least allows using the speaker without obscure + * kernel drivers and firmware. + * + * Ideally NXP (or now Goodix) should release proper documentation for these + * amplifiers so that support for the "CoolFlux DSP" can be implemented properly. + */ +static int tfa989x_dsp_bypass(struct regmap *regmap) +{ + int ret; + + /* Clear CHSA to bypass DSP and take input from I2S 1 left channel */ + ret = regmap_clear_bits(regmap, TFA989X_I2SREG, TFA989X_I2SREG_CHSA_MSK); + if (ret) + return ret; + + /* Set DCDC compensation to off and speaker impedance to 8 ohm */ + ret = regmap_update_bits(regmap, TFA989X_I2S_SEL_REG, + TFA989X_I2S_SEL_REG_DCFG_MSK | + TFA989X_I2S_SEL_REG_SPKR_MSK, + TFA989X_I2S_SEL_REG_SPKR_MSK); + if (ret) + return ret; + + /* Set DCDC to follower mode and disable CoolFlux DSP */ + return regmap_clear_bits(regmap, TFA989X_SYS_CTRL, + BIT(TFA989X_SYS_CTRL_DCA) | + BIT(TFA989X_SYS_CTRL_CFE) | + BIT(TFA989X_SYS_CTRL_AMPC)); +} + +static void tfa989x_regulator_disable(void *data) +{ + struct tfa989x *tfa989x = data; + + regulator_disable(tfa989x->vddd_supply); +} + +static int tfa989x_i2c_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + const struct tfa989x_rev *rev; + struct tfa989x *tfa989x; + struct regmap *regmap; + unsigned int val; + int ret; + + rev = device_get_match_data(dev); + if (!rev) { + dev_err(dev, "unknown device revision\n"); + return -ENODEV; + } + + tfa989x = devm_kzalloc(dev, sizeof(*tfa989x), GFP_KERNEL); + if (!tfa989x) + return -ENOMEM; + + tfa989x->rev = rev; + i2c_set_clientdata(i2c, tfa989x); + + tfa989x->vddd_supply = devm_regulator_get(dev, "vddd"); + if (IS_ERR(tfa989x->vddd_supply)) + return dev_err_probe(dev, PTR_ERR(tfa989x->vddd_supply), + "Failed to get vddd regulator\n"); + + if (tfa989x->rev->rev == TFA9897_REVISION) { + tfa989x->rcv_gpiod = devm_gpiod_get_optional(dev, "rcv", GPIOD_OUT_LOW); + if (IS_ERR(tfa989x->rcv_gpiod)) + return PTR_ERR(tfa989x->rcv_gpiod); + } + + regmap = devm_regmap_init_i2c(i2c, &tfa989x_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + ret = regulator_enable(tfa989x->vddd_supply); + if (ret) { + dev_err(dev, "Failed to enable vddd regulator: %d\n", ret); + return ret; + } + + ret = devm_add_action_or_reset(dev, tfa989x_regulator_disable, tfa989x); + if (ret) + return ret; + + /* Bypass regcache for reset and init sequence */ + regcache_cache_bypass(regmap, true); + + /* Dummy read to generate i2c clocks, required on some devices */ + regmap_read(regmap, TFA989X_REVISIONNUMBER, &val); + + ret = regmap_read(regmap, TFA989X_REVISIONNUMBER, &val); + if (ret) { + dev_err(dev, "failed to read revision number: %d\n", ret); + return ret; + } + + val &= TFA989X_REVISIONNUMBER_REV_MSK; + if (val != rev->rev) { + dev_err(dev, "invalid revision number, expected %#x, got %#x\n", + rev->rev, val); + return -ENODEV; + } + + ret = regmap_write(regmap, TFA989X_SYS_CTRL, BIT(TFA989X_SYS_CTRL_I2CR)); + if (ret) { + dev_err(dev, "failed to reset I2C registers: %d\n", ret); + return ret; + } + + ret = rev->init(regmap); + if (ret) { + dev_err(dev, "failed to initialize registers: %d\n", ret); + return ret; + } + + ret = tfa989x_dsp_bypass(regmap); + if (ret) { + dev_err(dev, "failed to enable DSP bypass: %d\n", ret); + return ret; + } + regcache_cache_bypass(regmap, false); + + return devm_snd_soc_register_component(dev, &tfa989x_component, + &tfa989x_dai, 1); +} + +static const struct of_device_id tfa989x_of_match[] = { + { .compatible = "nxp,tfa9890", .data = &tfa9890_rev }, + { .compatible = "nxp,tfa9895", .data = &tfa9895_rev }, + { .compatible = "nxp,tfa9897", .data = &tfa9897_rev }, + { } +}; +MODULE_DEVICE_TABLE(of, tfa989x_of_match); + +static struct i2c_driver tfa989x_i2c_driver = { + .driver = { + .name = "tfa989x", + .of_match_table = tfa989x_of_match, + }, + .probe_new = tfa989x_i2c_probe, +}; +module_i2c_driver(tfa989x_i2c_driver); + +MODULE_DESCRIPTION("ASoC NXP/Goodix TFA989X (TFA1) driver"); +MODULE_AUTHOR("Stephan Gerhold <stephan@gerhold.net>"); +MODULE_LICENSE("GPL"); |