blob: 1faa8541a872eb387115e92319b8ee92b924b5d8 [file]
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
/* WSA885X codec driver */
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/tlv.h>
/* Control Registers - Audio Processing */
#define WSA885X_SMP_AMP_CTRL_STEREO_STEREO_SMP_AMP_CTRL_I2S 0x0000
#define WSA885X_SMP_AMP_CTRL_STEREO_CMT_GRP_MASK 0x0004
#define WSA885X_SMP_AMP_CTRL_STEREO_IT21_CLUSERINDEX 0x0140
#define WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID 0x0208
#define WSA885X_SMP_AMP_CTRL_STEREO_CS21_SAMPLERATEINDEX 0x0240
#define WSA885X_SMP_AMP_CTRL_STEREO_PPU21_POSTURENUMBER 0x0340
#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0 0x4405
#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1 0x4406
#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB 0x4409
#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_MSB 0x6409
#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB 0x440a
#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_MSB 0x640a
#define WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS 0x0a04
#define WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS 0x0a40
#define WSA885X_SMP_AMP_CTRL_STEREO_OT23_USAGE 0x0b10
#define WSA885X_SMP_AMP_CTRL_STEREO_CS24_SAMPLERATEINDEX 0x0e40
/* Analog Top Registers - Power and Clock Control */
#define WSA885X_ANA_TOP_PON_CKSK_CTL_0 0x800d
#define WSA885X_ANA_TOP_BG_TVP_UVLO1_PROG 0x8024
#define WSA885X_ANA_TOP_BG_TVP_UVLO2_PROG 0x8025
#define WSA885X_ANA_TOP_BG_TVP_OVRD_CTL 0x8034
/* Analog PLL Registers */
#define WSA885X_ANA_PLL_DIV_CTL_0 0x8090
#define WSA885X_ANA_PLL_DIV_CTL_1 0x8091
#define WSA885X_ANA_TOP_PLL_VCO_CTL 0x8092
#define WSA885X_ANA_TOP_PLL_LOOPFILT_0 0x8093
#define WSA885X_ANA_TOP_PLL_OVRD_CTL 0x8098
#define WSA885X_ANA_TOP_PLL_STATUS_0 0x809a
#define WSA885X_ANA_TOP_PLL_STATUS_1 0x809b
/* Analog Boost Control Registers */
#define WSA885X_ANA_TOP_BOOST_STB_CTRL2 0x805b
#define WSA885X_ANA_TOP_BOOST_STB_CTRL3 0x805c
#define WSA885X_ANA_TOP_BOOST_BYP_CTRL2 0x805e
#define WSA885X_ANA_TOP_BOOST_BYP_CTRL3 0x805f
#define WSA885X_ANA_TOP_BOOST_MISC 0x8063
#define WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL2 0x8065
#define WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL4 0x8067
/* Analog IV Sense ADC Registers */
#define WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL2 0x80ca
#define WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3 0x80cb
#define WSA885X_ANA_TOP_IVSENSE_ADC_REF_CTL 0x80cc
#define WSA885X_ANA_TOP_IVSENSE_ADC_CDAC_CAL_CTL2 0x80d0
/* Analog Speaker Power Stage Registers */
#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_CTRL3 0x8108
#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_TUNE3 0x810b
#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_CTRL3 0x810e
#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_TUNE3 0x8111
#define WSA885X_ANA_TOP_SPK_TOP_SPARE3 0x813c
#define WSA885X_SPK_TOP_LF_CH1_CTRL11 0x811c
#define WSA885X_SPK_TOP_LF_CH1_TUNE1 0x811d
#define WSA885X_SPK_TOP_LF_CH2_TUNE1 0x8129
#define WSA885X_SPK_TOP_LF_CH1_CTRL9 0x811a
#define WSA885X_SPK_TOP_LF_CH2_CTRL9 0x8126
#define WSA885X_SPK_TOP_LF_CH2_CTRL11 0x8128
#define WSA885X_SPK_TOP_COMMON_CTRL2 0x8102
#define WSA885X_SPK_TOP_COMMON_TUNE1 0x8103
#define WSA885X_IVSENSE_VSNS_ISNS_CTL_CH1 0x80ba
#define WSA885X_DIG_CTRL0_TOP_CLK_CFG 0x8418
#define WSA885X_DIG_CTRL0_SDCA_COMMIT 0x8419
#define WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE 0x841a
#define WSA885X_DIG_CTRL0_SYS_CLK_SEL 0x841b
#define WSA885X_DIG_CTRL0_CDC_CLK_CTL 0x841c
#define WSA885X_DIG_CTRL0_PA_FSM_CTL 0x8420
#define WSA885X_DIG_CTRL0_POWER_FSM_CTL0 0x8423
#define WSA885X_DIG_CTRL0_POWER_FSM_CTL1 0x8424
#define WSA885X_DIG_CTRL0_PA0_FSM_CTL1 0x842b
#define WSA885X_DIG_CTRL0_PA1_FSM_CTL1 0x8435
#define WSA885X_DIG_CTRL0_VBAT_THRM_FLT_CTL 0x8458
#define WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL 0x8470
#define WSA885X_DIG_CTRL0_GAIN_RAMP0_CTL1 0x84b4
#define WSA885X_DIG_CTRL0_GAIN_RAMP1_CTL1 0x84b7
#define WSA885X_DIG_CTRL0_PCM_DATA_WD0_CTL1 0x84A0
#define WSA885X_DIG_CTRL0_PCM_DATA_WD1_CTL1 0x84A4
/* Digital Control 1 Registers - I2S/TDM Interface */
#define WSA885X_DIG_CTRL1_I2S_CTL0 0x85A0
#define WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX 0x85A2
#define WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX 0x85A3
#define WSA885X_DIG_CTRL1_I2S_TDM_CTL0 0x85A7
#define WSA885X_DIG_CTRL1_I2S_TDM_CTL1 0x85A9
#define WSA885X_DIG_CTRL1_I2S_TDM_CH_RX 0x85AA
#define WSA885X_DIG_CTRL1_I2S_TDM_CH_TX 0x85AB
#define WSA885X_DIG_CTRL1_I2S_RESET_CTL 0x85AE
/* CDC RX Path Registers - Audio Data Path */
#define WSA885X_CDC_RX0_RX_PATH_CFG0 0x8601
#define WSA885X_CDC_RX0_RX_PATH_CFG1 0x8602
#define WSA885X_CDC_RX0_RX_PATH_CTL 0x8606
#define WSA885X_RX0_RX_PATH_DSMDEM_CTL 0x8613
#define WSA885X_CDC_RX1_RX_PATH_CFG0 0x8621
#define WSA885X_CDC_RX1_RX_PATH_CFG1 0x8622
#define WSA885X_CDC_RX1_RX_PATH_CTL 0x8626
#define WSA885X_RX1_RX_PATH_DSMDEM_CTL 0x8633
/* CDC Compander Registers - Dynamic Range Control */
#define WSA885X_CDC_COMPANDER0_CTL0 0x8640
#define WSA885X_CDC_COMPANDER0_CTL7 0x8647
#define WSA885X_CDC_COMPANDER1_CTL0 0x8660
#define WSA885X_CDC_COMPANDER1_CTL7 0x8667
/* CDC Speaker Protection Registers - IV Sense */
#define WSA885X_CDC_VSENSE0_SPKR_PROT_PATH_CTL 0x86A1
#define WSA885X_CDC_VSENSE1_SPKR_PROT_PATH_CTL 0x86B1
#define WSA885X_CDC_ISENSE0_SPKR_PROT_PATH_CTL 0x86A9
#define WSA885X_CDC_ISENSE1_SPKR_PROT_PATH_CTL 0x86B9
/* CDC Class-H Registers - Headroom Control */
#define WSA885X_CDC_CLSH_V1P8_BP_CTL1 0x86CD
#define WSA885X_CDC_CLSH_V1P8_BP_CTL0 0x86CC
#define WSA885X_CDC_CLSH_CLSH_SIG_DP_CTL0 0x86C7
#define WSA885X_CDC_CLSH_CLSH_V_HD_PA 0x86C3
#define WSA885X_CDC_CLSH_V1P8_BP_CTL2 0x86CE
/* Driver Constants */
#define WSA885X_CLK_RATE_FIXED 73728000
#define WSA885X_NUM_REGS 0x03
/* Interrupt Registers */
#define WSA885X_INTR_STATUS0 0x8584
#define WSA885X_INTR_MASK0 0x8581
#define WSA885X_INTR_CLEAR0 0x8587
/* Power and PA FSM Control Registers */
#define WSA885X_PA0_FSM_CTL0 0x842A
#define WSA885X_PA1_FSM_CTL0 0x8434
/* Digital Control GPIO and Interrupt Registers */
#define WSA885X_DIG_CTRL1_PIN_CT 0x8510
#define WSA885X_DIG_CTRL1_SPMI_PAD_GPIO2_CTL 0x8518
#define WSA885X_DIG_CTRL1_INTR_MODE 0x8580
#define WSA885X_I2S_CTL0_PCM_RATE_MASK GENMASK(4, 1)
#define WSA885X_I2S_CTL0_ENABLE_MASK BIT(0)
#define WSA885X_I2S_CTL0_PCM_RATE(v) \
FIELD_PREP(WSA885X_I2S_CTL0_PCM_RATE_MASK, (v))
#define WSA885X_I2S_CTL0_PCM_RATE_8KHZ 0x0
#define WSA885X_I2S_CTL0_PCM_RATE_16KHZ 0x1
#define WSA885X_I2S_CTL0_PCM_RATE_32KHZ 0x2
#define WSA885X_I2S_CTL0_PCM_RATE_48_OR_44KHZ 0x3
#define WSA885X_I2S_CTL0_PCM_RATE_96_OR_88KHZ 0x4
#define WSA885X_I2S_CTL0_PCM_RATE_192_OR_176KHZ 0x5
#define WSA885X_I2S_CTL0_PCM_RATE_384_OR_352KHZ 0x6
#define WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK GENMASK(2, 0)
#define WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK GENMASK(6, 4)
#define WSA885X_I2S_CFG0_TDM_TX_SLOT0(v) \
FIELD_PREP_CONST(WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK, (v))
#define WSA885X_I2S_CFG0_TDM_TX_SLOT1(v) \
FIELD_PREP_CONST(WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK, (v))
#define WSA885X_I2S_CFG1_TDM_TX_SLOT2_MASK GENMASK(2, 0)
#define WSA885X_I2S_CFG1_TDM_TX_SLOT3_MASK GENMASK(6, 4)
#define WSA885X_I2S_CFG1_TDM_TX_SLOT2(v) \
FIELD_PREP_CONST(WSA885X_I2S_CFG1_TDM_TX_SLOT2_MASK, (v))
#define WSA885X_I2S_CFG1_TDM_TX_SLOT3(v) \
FIELD_PREP_CONST(WSA885X_I2S_CFG1_TDM_TX_SLOT3_MASK, (v))
#define WSA885X_I2S_TDM_CTL0_I2S_TDM_EN_MASK BIT(0)
#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK GENMASK(3, 2)
#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_2 \
FIELD_PREP_CONST(WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK, 0)
#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_4 \
FIELD_PREP_CONST(WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK, 1)
#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_8 \
FIELD_PREP_CONST(WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK, 3)
#define WSA885X_I2S_TDM_CH_TX_CH0_EN BIT(0)
#define WSA885X_I2S_TDM_CH_TX_CH1_EN BIT(1)
#define WSA885X_I2S_TDM_CH_TX_CH2_EN BIT(2)
#define WSA885X_I2S_TDM_CH_TX_CH3_EN BIT(3)
#define WSA885X_I2S_TDM_CH_RX_CH0_EN BIT(0)
#define WSA885X_I2S_TDM_CH_RX_CH3_EN BIT(3)
#define WSA885X_I2S_RESET_CTL_RESET_MASK BIT(0)
#define WSA885X_PCM_DATA_WD_CTL1_PCM_DATA_WD_EN_MASK BIT(2)
#define WSA885X_POWER_FSM_CTL0_CLEAR_ERROR_MASK BIT(3)
#define WSA885X_PA_FSM_CTL0_CLEAR_ERROR_MASK BIT(2)
#define WSA885X_I2S_TX_SLOT_ISENSE0 0x1
#define WSA885X_I2S_TX_SLOT_ISENSE1 0x2
#define WSA885X_I2S_TX_SLOT_CUR_SENSE0 0x5
#define WSA885X_I2S_TX_SLOT_CUR_SENSE1 0x6
/* RX Sample Rate Index Values - Audio Playback Path */
#define WSA885X_RX_RATE_8000HZ 0x00
#define WSA885X_RX_RATE_16000HZ 0x01
#define WSA885X_RX_RATE_32000HZ 0x02
#define WSA885X_RX_RATE_44100HZ 0x03
#define WSA885X_RX_RATE_48000HZ 0x04
#define WSA885X_RX_RATE_96000HZ 0x05
#define WSA885X_RX_RATE_192000HZ 0x06
#define WSA885X_RX_RATE_384000HZ 0x07
/* VI Sample Rate Index Values - Voltage/Current Sensing Path */
#define WSA885X_VI_RATE_8000HZ 0x00
#define WSA885X_VI_RATE_16000HZ 0x01
#define WSA885X_VI_RATE_44100HZ 0x02
#define WSA885X_VI_RATE_48000HZ 0x03
#define WSA885X_VI_RATE_96000HZ 0x04
#define WSA885X_VI_RATE_22050HZ 0x05
#define WSA885X_VI_RATE_24000HZ 0x06
#define WSA885X_VI_RATE_192000HZ 0x07
#define WSA885X_VI_RATE_384000HZ 0x08
/* Channel Configuration Masks */
#define WSA885X_CHANNEL_STEREO 0x03
#define WSA885X_CHANNEL_MONO_LEFT 0x01
#define WSA885X_CHANNEL_MONO_RIGHT 0x02
#define WSA885X_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | \
SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
#define WSA885X_PLL_LOCK_BIT BIT(0)
#define WSA885X_FU21_VOL_STEPS 124
#define WSA885X_USAGE_MODE_MAX 8
static const DECLARE_TLV_DB_SCALE(wsa885x_fu21_digital_gain, -8400, 100, 0);
static bool wsa885x_is_valid_rx_slot_mask(u32 mask)
{
return mask == WSA885X_CHANNEL_MONO_LEFT ||
mask == WSA885X_CHANNEL_MONO_RIGHT ||
mask == WSA885X_CHANNEL_STEREO;
}
static const char *const wsa885x_supply_name[] = {
"vdd-1p8",
"vdd-io",
};
enum {
WSA885X_BATT_1S = 1,
WSA885X_BATT_2S,
};
enum {
WSA885X_IRQ_INT_SAF2WAR = 0,
WSA885X_IRQ_INT_WAR2SAF,
WSA885X_IRQ_INT_DISABLE,
WSA885X_IRQ_INT_PA0_OCP,
WSA885X_IRQ_INT_PA1_OCP,
WSA885X_IRQ_INT_CLIP0,
WSA885X_IRQ_INT_CLIP1,
WSA885X_IRQ_INT_CLK_WD,
WSA885X_IRQ_INT_INTR_GPIO1_PIN,
WSA885X_IRQ_INT_INTR_GPIO2_PIN,
WSA885X_IRQ_INT_UVLO,
WSA885X_IRQ_INT_BOP,
WSA885X_IRQ_INT_PA0_FSM_ERR,
WSA885X_IRQ_INT_PA1_FSM_ERR,
WSA885X_IRQ_INT_MAIN_FSM_ERR,
WSA885X_IRQ_INT_PCM_DATA0_WD,
WSA885X_IRQ_INT_PCM_DATA1_WD,
WSA885X_IRQ_INT_PCM_DATA0_DC,
WSA885X_IRQ_INT_PCM_DATA1_DC,
WSA885X_IRQ_INT_PLL_UNLOCKED,
WSA885X_IRQ_INT_PROT_MODE_CHANGE,
WSA885X_IRQ_INT_PB_CLOCK_VALID,
WSA885X_IRQ_INT_SENSE_CLOCK_VALID,
WSA885X_IRQ_MAX,
};
struct wsa885x_priv {
struct i2c_client *client;
struct regmap *regmap;
struct device *dev;
struct snd_soc_component *component;
struct gpio_desc *sd_n;
struct reset_control *sd_reset;
u32 usage_mode;
u32 rx_slot_mask;
u32 batt_conf;
int stereo_vol_db;
struct mutex state_lock; /* protects mutable control state */
};
struct wsa885x_reg_update {
unsigned int reg;
unsigned int mask;
unsigned int val;
};
static const struct regmap_range_cfg wsa885x_regmap_ranges[] = {
{
.range_min = 0,
.range_max = 0x88ff,
.selector_reg = 0x0,
.selector_mask = 0xFF,
.selector_shift = 0,
.window_start = 0,
.window_len = 0x100,
},
};
static const struct reg_default wsa885x_codec_reg_defaults[] = {
{WSA885X_SMP_AMP_CTRL_STEREO_STEREO_SMP_AMP_CTRL_I2S, 0x00},
{WSA885X_SMP_AMP_CTRL_STEREO_IT21_CLUSERINDEX, 0x01},
{WSA885X_SMP_AMP_CTRL_STEREO_CMT_GRP_MASK, 0x00},
{WSA885X_SMP_AMP_CTRL_STEREO_OT23_USAGE, 0x00},
{WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID, 0x00},
{WSA885X_SMP_AMP_CTRL_STEREO_CS21_SAMPLERATEINDEX, 0x04},
{WSA885X_SMP_AMP_CTRL_STEREO_PPU21_POSTURENUMBER, 0x01},
{WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0, 0x01},
{WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1, 0x01},
{WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_MSB, 0xac},
{WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB, 0x00},
{WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_MSB, 0xac},
{WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB, 0x00},
{WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS, 0x03},
{WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS, 0x03},
{WSA885X_SMP_AMP_CTRL_STEREO_CS24_SAMPLERATEINDEX, 0x03},
{WSA885X_ANA_TOP_PON_CKSK_CTL_0, 0x00},
{WSA885X_ANA_TOP_BG_TVP_UVLO1_PROG, 0x19},
{WSA885X_ANA_TOP_BG_TVP_UVLO2_PROG, 0x22},
{WSA885X_ANA_PLL_DIV_CTL_0, 0x0c},
{WSA885X_ANA_PLL_DIV_CTL_1, 0x50},
{WSA885X_ANA_TOP_PLL_VCO_CTL, 0x00},
{WSA885X_ANA_TOP_PLL_LOOPFILT_0, 0xb4},
{WSA885X_ANA_TOP_PLL_OVRD_CTL, 0x00},
{WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x00},
{WSA885X_ANA_TOP_BOOST_STB_CTRL2, 0x03},
{WSA885X_ANA_TOP_BOOST_STB_CTRL3, 0x3c},
{WSA885X_ANA_TOP_BOOST_BYP_CTRL2, 0xc5},
{WSA885X_ANA_TOP_BOOST_BYP_CTRL3, 0x13},
{WSA885X_ANA_TOP_BOOST_MISC, 0x79},
{WSA885X_ANA_TOP_SPK_TOP_SPARE3, 0x00},
{WSA885X_SPK_TOP_COMMON_CTRL2, 0x08},
{WSA885X_SPK_TOP_LF_CH1_CTRL11, 0x09},
{WSA885X_SPK_TOP_LF_CH1_TUNE1, 0x00},
{WSA885X_SPK_TOP_LF_CH2_TUNE1, 0x00},
{WSA885X_SPK_TOP_LF_CH1_CTRL9, 0x00},
{WSA885X_SPK_TOP_LF_CH2_CTRL9, 0x00},
{WSA885X_SPK_TOP_LF_CH2_CTRL11, 0x09},
{WSA885X_SPK_TOP_COMMON_TUNE1, 0x03},
{WSA885X_IVSENSE_VSNS_ISNS_CTL_CH1, 0x00},
{WSA885X_DIG_CTRL0_CDC_CLK_CTL, 0x0e},
{WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL2, 0x40},
{WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL4, 0xff},
{WSA885X_ANA_TOP_PLL_STATUS_0, 0x00},
{WSA885X_ANA_TOP_PLL_STATUS_1, 0x00},
{WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL2, 0x84},
{WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3, 0x02},
{WSA885X_ANA_TOP_IVSENSE_ADC_REF_CTL, 0x00},
{WSA885X_ANA_TOP_IVSENSE_ADC_CDAC_CAL_CTL2, 0xe0},
{WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_CTRL3, 0xa4},
{WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_TUNE3, 0xc9},
{WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_CTRL3, 0xa4},
{WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_TUNE3, 0xc9},
{WSA885X_DIG_CTRL0_TOP_CLK_CFG, 0x00},
{WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x00},
{WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE, 0x00},
{WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x00},
{WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00},
{WSA885X_DIG_CTRL0_POWER_FSM_CTL0, 0x05},
{WSA885X_DIG_CTRL0_POWER_FSM_CTL1, 0x00},
{WSA885X_DIG_CTRL0_PA0_FSM_CTL1, 0x45},
{WSA885X_DIG_CTRL0_PA1_FSM_CTL1, 0x45},
{WSA885X_DIG_CTRL0_VBAT_THRM_FLT_CTL, 0x7f},
{WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x00},
{WSA885X_DIG_CTRL0_GAIN_RAMP0_CTL1, 0x01},
{WSA885X_DIG_CTRL0_GAIN_RAMP1_CTL1, 0x01},
{WSA885X_DIG_CTRL1_I2S_CTL0, 0x06},
{WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, 0x00},
{WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX, 0x00},
{WSA885X_DIG_CTRL1_I2S_TDM_CTL0, 0x00},
{WSA885X_DIG_CTRL1_I2S_TDM_CTL1, 0x05},
{WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, 0x00},
{WSA885X_DIG_CTRL1_I2S_RESET_CTL, 0x00},
{WSA885X_DIG_CTRL1_I2S_TDM_CH_RX, WSA885X_I2S_TDM_CH_RX_CH3_EN},
{WSA885X_CDC_RX0_RX_PATH_CFG0, 0x89},
{WSA885X_CDC_RX0_RX_PATH_CFG1, 0x64},
{WSA885X_CDC_RX0_RX_PATH_CTL, 0x24},
{WSA885X_RX0_RX_PATH_DSMDEM_CTL, 0x01},
{WSA885X_CDC_RX1_RX_PATH_CFG0, 0x89},
{WSA885X_CDC_RX1_RX_PATH_CFG1, 0x64},
{WSA885X_CDC_RX1_RX_PATH_CTL, 0x04},
{WSA885X_RX1_RX_PATH_DSMDEM_CTL, 0x01},
{WSA885X_CDC_COMPANDER0_CTL0, 0x01},
{WSA885X_CDC_COMPANDER0_CTL7, 0x2a},
{WSA885X_CDC_COMPANDER1_CTL0, 0x01},
{WSA885X_CDC_COMPANDER1_CTL7, 0x2a},
{WSA885X_CDC_VSENSE0_SPKR_PROT_PATH_CTL, 0x14},
{WSA885X_CDC_VSENSE1_SPKR_PROT_PATH_CTL, 0x14},
{WSA885X_CDC_ISENSE0_SPKR_PROT_PATH_CTL, 0x14},
{WSA885X_CDC_ISENSE1_SPKR_PROT_PATH_CTL, 0x14},
{WSA885X_CDC_CLSH_V1P8_BP_CTL1, 0x50},
{WSA885X_CDC_CLSH_V1P8_BP_CTL0, 0x6c},
{WSA885X_CDC_CLSH_CLSH_SIG_DP_CTL0, 0x0d},
{WSA885X_CDC_CLSH_CLSH_V_HD_PA, 0x03},
{WSA885X_CDC_CLSH_V1P8_BP_CTL2, 0x05},
};
static void wsa885x_multi_update_bits(struct regmap *regmap,
const struct wsa885x_reg_update *updates,
size_t num_updates)
{
size_t i;
for (i = 0; i < num_updates; i++)
regmap_update_bits(regmap, updates[i].reg,
updates[i].mask, updates[i].val);
}
static void wsa885x_toggle_irq_bit(struct wsa885x_priv *wsa885x,
unsigned int reg, unsigned int mask)
{
regmap_update_bits(wsa885x->regmap, reg, mask, 0);
regmap_update_bits(wsa885x->regmap, reg, mask, mask);
}
static void wsa885x_pulse_irq_bit(struct wsa885x_priv *wsa885x,
unsigned int reg, unsigned int mask)
{
regmap_update_bits(wsa885x->regmap, reg, mask, 0);
regmap_update_bits(wsa885x->regmap, reg, mask, mask);
regmap_update_bits(wsa885x->regmap, reg, mask, 0);
}
static int wsa885x_tdm_ctl0_slot_num_val(int slots, unsigned int *slot_num_val)
{
if (!slot_num_val)
return -EINVAL;
switch (slots) {
case 2:
*slot_num_val = WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_2;
return 0;
case 4:
*slot_num_val = WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_4;
return 0;
case 8:
*slot_num_val = WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_8;
return 0;
default:
return -EINVAL;
}
}
static int wsa885x_reg_update_sequence(struct regmap *regmap, int slots)
{
static const struct reg_sequence regs[] = {
{ WSA885X_DIG_CTRL1_I2S_TDM_CTL1, 0x15 },
{ WSA885X_DIG_CTRL1_I2S_TDM_CTL1, 0x11 },
};
unsigned int slot_num_val;
int ret;
if (!regmap)
return -EINVAL;
ret = wsa885x_tdm_ctl0_slot_num_val(slots, &slot_num_val);
if (ret)
return ret;
regmap_multi_reg_write(regmap, regs, ARRAY_SIZE(regs));
regmap_update_bits(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CTL0,
WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK,
slot_num_val);
regmap_update_bits(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CTL0,
WSA885X_I2S_TDM_CTL0_I2S_TDM_EN_MASK,
WSA885X_I2S_TDM_CTL0_I2S_TDM_EN_MASK);
regmap_write(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX,
WSA885X_I2S_TDM_CH_TX_CH0_EN);
regmap_update_bits(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX,
WSA885X_I2S_TDM_CH_TX_CH1_EN,
WSA885X_I2S_TDM_CH_TX_CH1_EN);
return 0;
}
static int wsa885x_wait_for_pll_lock(struct wsa885x_priv *wsa885x)
{
unsigned int status = 0;
int cnt = 0, ret = 0;
do {
usleep_range(1000, 1100);
ret = regmap_read(wsa885x->regmap, WSA885X_ANA_TOP_PLL_STATUS_0, &status);
if (ret) {
dev_err(wsa885x->dev, "PLL status read failed: %d\n", ret);
return ret;
}
if (status & WSA885X_PLL_LOCK_BIT)
return 0;
} while (++cnt < 20);
dev_warn(wsa885x->dev, "PLL lock timeout after 20ms, status=0x%x\n", status);
return -ETIMEDOUT;
}
static int wsa885x_2s_conf(struct wsa885x_priv *wsa885x)
{
static const struct reg_sequence regs[] = {
{ WSA885X_SPK_TOP_COMMON_TUNE1, 0x26 },
{ WSA885X_SPK_TOP_LF_CH1_CTRL11, 0x0d },
{ WSA885X_SPK_TOP_LF_CH2_CTRL11, 0x0d },
{ WSA885X_CDC_CLSH_V1P8_BP_CTL1, 0x71 },
{ WSA885X_CDC_CLSH_V1P8_BP_CTL0, 0xAA },
};
return regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs));
}
static const struct reg_sequence wsa885x_reg_init[] = {
{ WSA885X_CDC_RX0_RX_PATH_CTL, 0x24 },
{ WSA885X_CDC_RX1_RX_PATH_CTL, 0x24 },
{ WSA885X_RX0_RX_PATH_DSMDEM_CTL, 0x01 },
{ WSA885X_RX1_RX_PATH_DSMDEM_CTL, 0x01 },
{ WSA885X_CDC_COMPANDER0_CTL0, 0x01 },
{ WSA885X_CDC_COMPANDER1_CTL0, 0x01 },
{ WSA885X_CDC_VSENSE0_SPKR_PROT_PATH_CTL, 0x14 },
{ WSA885X_CDC_VSENSE1_SPKR_PROT_PATH_CTL, 0x14 },
{ WSA885X_CDC_ISENSE0_SPKR_PROT_PATH_CTL, 0x14 },
{ WSA885X_CDC_ISENSE1_SPKR_PROT_PATH_CTL, 0x14 },
{ WSA885X_DIG_CTRL0_CDC_CLK_CTL, 0x0f },
{ WSA885X_DIG_CTRL0_CDC_CLK_CTL, 0x4f },
{ WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x02 },
{ WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x00 },
{ WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x01 },
{ WSA885X_SMP_AMP_CTRL_STEREO_CMT_GRP_MASK, 0x01 },
{ WSA885X_CDC_RX0_RX_PATH_CFG1, 0x60 },
{ WSA885X_CDC_RX1_RX_PATH_CFG1, 0x60 },
{ WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_CTRL3, 0xa5 },
{ WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_CTRL3, 0xa5 },
{ WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL2, 0x85 },
{ WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3, 0x0c },
{ WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3, 0x0e },
{ WSA885X_ANA_TOP_IVSENSE_ADC_REF_CTL, 0x0c },
{ WSA885X_DIG_CTRL0_GAIN_RAMP0_CTL1, 0x01 },
{ WSA885X_DIG_CTRL0_GAIN_RAMP1_CTL1, 0x01 },
{ WSA885X_CDC_RX0_RX_PATH_CFG0, 0x88 },
{ WSA885X_CDC_RX0_RX_PATH_CFG0, 0x89 },
{ WSA885X_CDC_RX1_RX_PATH_CFG0, 0x88 },
{ WSA885X_CDC_RX1_RX_PATH_CFG0, 0x89 },
{ WSA885X_ANA_TOP_BOOST_STB_CTRL2, 0x82 },
{ WSA885X_ANA_TOP_BOOST_STB_CTRL3, 0x34 },
{ WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL2, 0x41 },
{ WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL4, 0x7f },
{ WSA885X_CDC_CLSH_V1P8_BP_CTL1, 0x50 },
{ WSA885X_CDC_CLSH_V1P8_BP_CTL0, 0x6c },
{ WSA885X_CDC_CLSH_CLSH_SIG_DP_CTL0, 0x0d },
{ WSA885X_CDC_CLSH_CLSH_V_HD_PA, 0x03 },
{ WSA885X_DIG_CTRL0_POWER_FSM_CTL0, 0x05 },
{ WSA885X_ANA_TOP_PON_CKSK_CTL_0, 0x20 },
{ WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_TUNE3, 0x45 },
{ WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_TUNE3, 0x45 },
{ WSA885X_CDC_CLSH_V1P8_BP_CTL2, 0x05 },
{ WSA885X_ANA_TOP_BG_TVP_UVLO1_PROG, 0x35 },
{ WSA885X_ANA_TOP_BG_TVP_UVLO2_PROG, 0x21 },
{ WSA885X_ANA_TOP_BOOST_BYP_CTRL2, 0xc7 },
{ WSA885X_ANA_TOP_BOOST_BYP_CTRL3, 0x11 },
{ WSA885X_ANA_TOP_IVSENSE_ADC_CDAC_CAL_CTL2, 0x80 },
{ WSA885X_ANA_TOP_SPK_TOP_SPARE3, 0x08 },
{ WSA885X_DIG_CTRL0_PA0_FSM_CTL1, 0x47 },
{ WSA885X_DIG_CTRL0_PA1_FSM_CTL1, 0x47 },
{ WSA885X_CDC_COMPANDER0_CTL7, 0x34 },
{ WSA885X_CDC_COMPANDER1_CTL7, 0x34 },
{ WSA885X_DIG_CTRL0_VBAT_THRM_FLT_CTL, 0x79 },
};
static int wsa885x_hw_init(struct wsa885x_priv *wsa885x)
{
static const struct reg_sequence regs[] = {
{ WSA885X_DIG_CTRL1_SPMI_PAD_GPIO2_CTL, 0x2e },
{ WSA885X_DIG_CTRL1_INTR_MODE, 0x01 },
{ WSA885X_DIG_CTRL1_PIN_CT, 0x04 },
};
int ret;
ret = regmap_multi_reg_write(wsa885x->regmap, wsa885x_reg_init,
ARRAY_SIZE(wsa885x_reg_init));
if (ret)
return ret;
if (wsa885x->batt_conf == WSA885X_BATT_2S) {
ret = wsa885x_2s_conf(wsa885x);
if (ret)
return ret;
}
return regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs));
}
static int wsa885x_unmask_interrupts(struct wsa885x_priv *wsa885x)
{
static const struct reg_sequence regs[] = {
{ WSA885X_INTR_MASK0, 0x00 },
{ WSA885X_INTR_MASK0 + 1, 0x00 },
{ WSA885X_INTR_MASK0 + 2, 0xf8 },
};
return regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs));
}
static int wsa885x_wait_for_pde_state(struct wsa885x_priv *wsa885x, int ps)
{
unsigned int act_ps = 0, clock_valid = 0;
int rc = 0, cnt = 0;
if (ps < 0 || ps > 3)
return -EINVAL;
do {
usleep_range(1000, 1500);
rc = regmap_read(wsa885x->regmap,
WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS,
&act_ps);
if (rc) {
dev_err(wsa885x->dev, "PDE state read failed: %d\n", rc);
return rc;
}
if (act_ps == ps)
return 0;
} while (++cnt < 5);
if (regmap_read(wsa885x->regmap,
WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID,
&clock_valid))
dev_err(wsa885x->dev,
"PDE power state %d request failed, actual_ps %d, clock_valid read failed\n",
ps, act_ps);
else
dev_err(wsa885x->dev,
"PDE power state %d request failed, actual_ps %d, clock_valid:%d\n",
ps, act_ps, clock_valid);
return -ETIMEDOUT;
}
static void wsa885x_program_stereo_volume(struct wsa885x_priv *wsa885x,
int stereo_vol_db, bool commit)
{
regmap_write(wsa885x->regmap,
WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_MSB,
(u8)(s8)stereo_vol_db);
regmap_write(wsa885x->regmap,
WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB, 0x00);
regmap_write(wsa885x->regmap,
WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_MSB,
(u8)(s8)stereo_vol_db);
regmap_write(wsa885x->regmap,
WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB, 0x00);
if (commit)
regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01);
}
static int wsa885x_codec_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct wsa885x_priv *wsa885x;
u8 pcm_rate, cs21_sample_rate_idx, cs24_sample_rate_idx;
wsa885x = snd_soc_component_get_drvdata(dai->component);
switch (params_rate(params)) {
case 8000:
pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_8KHZ;
cs21_sample_rate_idx = WSA885X_RX_RATE_8000HZ;
cs24_sample_rate_idx = WSA885X_VI_RATE_8000HZ;
break;
case 16000:
pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_16KHZ;
cs21_sample_rate_idx = WSA885X_RX_RATE_16000HZ;
cs24_sample_rate_idx = WSA885X_VI_RATE_16000HZ;
break;
case 32000:
pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_32KHZ;
cs21_sample_rate_idx = WSA885X_RX_RATE_32000HZ;
cs24_sample_rate_idx = WSA885X_VI_RATE_48000HZ;
break;
case 44100:
pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_48_OR_44KHZ;
cs21_sample_rate_idx = WSA885X_RX_RATE_44100HZ;
cs24_sample_rate_idx = WSA885X_VI_RATE_44100HZ;
break;
case 48000:
pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_48_OR_44KHZ;
cs21_sample_rate_idx = WSA885X_RX_RATE_48000HZ;
cs24_sample_rate_idx = WSA885X_VI_RATE_48000HZ;
break;
case 88200:
case 96000:
pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_96_OR_88KHZ;
cs21_sample_rate_idx = WSA885X_RX_RATE_96000HZ;
cs24_sample_rate_idx = WSA885X_VI_RATE_96000HZ;
break;
case 176400:
case 192000:
pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_192_OR_176KHZ;
cs21_sample_rate_idx = WSA885X_RX_RATE_192000HZ;
cs24_sample_rate_idx = WSA885X_VI_RATE_192000HZ;
break;
case 352800:
case 384000:
pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_384_OR_352KHZ;
cs21_sample_rate_idx = WSA885X_RX_RATE_384000HZ;
cs24_sample_rate_idx = WSA885X_VI_RATE_384000HZ;
break;
default:
dev_err(wsa885x->dev, "sampling rate %d is not supported\n", params_rate(params));
return -EINVAL;
}
regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_CTL0,
WSA885X_I2S_CTL0_PCM_RATE_MASK |
WSA885X_I2S_CTL0_ENABLE_MASK,
WSA885X_I2S_CTL0_PCM_RATE(pcm_rate) |
WSA885X_I2S_CTL0_ENABLE_MASK);
regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_RESET_CTL, 0x00);
regmap_write(wsa885x->regmap, WSA885X_SMP_AMP_CTRL_STEREO_CS21_SAMPLERATEINDEX,
cs21_sample_rate_idx);
regmap_write(wsa885x->regmap, WSA885X_SMP_AMP_CTRL_STEREO_CS24_SAMPLERATEINDEX,
cs24_sample_rate_idx);
mutex_lock(&wsa885x->state_lock);
wsa885x_program_stereo_volume(wsa885x, wsa885x->stereo_vol_db, false);
mutex_unlock(&wsa885x->state_lock);
regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01);
return 0;
}
static int wsa885x_codec_set_tdm_slot(struct snd_soc_dai *dai,
unsigned int tx_slot_mask,
unsigned int rx_slot_mask, int slots,
int slot_width)
{
static const struct wsa885x_reg_update stereo_updates[] = {
{ WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK,
WSA885X_I2S_CFG0_TDM_TX_SLOT0(WSA885X_I2S_TX_SLOT_ISENSE0) },
{ WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK,
WSA885X_I2S_CFG0_TDM_TX_SLOT1(WSA885X_I2S_TX_SLOT_ISENSE1) },
{ WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX, WSA885X_I2S_CFG1_TDM_TX_SLOT2_MASK,
WSA885X_I2S_CFG1_TDM_TX_SLOT2(WSA885X_I2S_TX_SLOT_CUR_SENSE0) },
{ WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX, WSA885X_I2S_CFG1_TDM_TX_SLOT3_MASK,
WSA885X_I2S_CFG1_TDM_TX_SLOT3(WSA885X_I2S_TX_SLOT_CUR_SENSE1) },
};
static const struct wsa885x_reg_update mono_left_updates[] = {
{ WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK,
WSA885X_I2S_CFG0_TDM_TX_SLOT0(WSA885X_I2S_TX_SLOT_ISENSE0) },
{ WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK,
WSA885X_I2S_CFG0_TDM_TX_SLOT1(WSA885X_I2S_TX_SLOT_CUR_SENSE0) },
};
static const struct wsa885x_reg_update mono_right_updates[] = {
{ WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK,
WSA885X_I2S_CFG0_TDM_TX_SLOT0(WSA885X_I2S_TX_SLOT_ISENSE1) },
{ WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK,
WSA885X_I2S_CFG0_TDM_TX_SLOT1(WSA885X_I2S_TX_SLOT_CUR_SENSE1) },
};
struct wsa885x_priv *wsa885x;
unsigned int slot_num_val;
u32 mask;
int ret;
wsa885x = snd_soc_component_get_drvdata(dai->component);
ret = wsa885x_tdm_ctl0_slot_num_val(slots, &slot_num_val);
if (ret) {
dev_err(wsa885x->dev, "%s: unsupported slot count %d\n",
__func__, slots);
return ret;
}
if (rx_slot_mask && !wsa885x_is_valid_rx_slot_mask(rx_slot_mask)) {
dev_err(wsa885x->dev,
"%s: unsupported rx_slot_mask 0x%x\n",
__func__, rx_slot_mask);
return -EINVAL;
}
mutex_lock(&wsa885x->state_lock);
if (rx_slot_mask)
wsa885x->rx_slot_mask = rx_slot_mask;
else if (!wsa885x_is_valid_rx_slot_mask(wsa885x->rx_slot_mask))
wsa885x->rx_slot_mask = WSA885X_CHANNEL_STEREO;
mask = wsa885x->rx_slot_mask;
regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_RESET_CTL,
WSA885X_I2S_RESET_CTL_RESET_MASK,
WSA885X_I2S_RESET_CTL_RESET_MASK);
if (mask == WSA885X_CHANNEL_STEREO) {
wsa885x_multi_update_bits(wsa885x->regmap, stereo_updates,
ARRAY_SIZE(stereo_updates));
ret = wsa885x_reg_update_sequence(wsa885x->regmap, slots);
if (ret)
goto exit_unlock;
regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX,
WSA885X_I2S_TDM_CH_TX_CH2_EN,
WSA885X_I2S_TDM_CH_TX_CH2_EN);
regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX,
WSA885X_I2S_TDM_CH_TX_CH3_EN,
WSA885X_I2S_TDM_CH_TX_CH3_EN);
} else if (mask == WSA885X_CHANNEL_MONO_LEFT) {
wsa885x_multi_update_bits(wsa885x->regmap, mono_left_updates,
ARRAY_SIZE(mono_left_updates));
ret = wsa885x_reg_update_sequence(wsa885x->regmap, slots);
if (ret)
goto exit_unlock;
} else if (mask == WSA885X_CHANNEL_MONO_RIGHT) {
wsa885x_multi_update_bits(wsa885x->regmap, mono_right_updates,
ARRAY_SIZE(mono_right_updates));
ret = wsa885x_reg_update_sequence(wsa885x->regmap, slots);
if (ret)
goto exit_unlock;
}
regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_CTL0,
WSA885X_I2S_CTL0_ENABLE_MASK,
WSA885X_I2S_CTL0_ENABLE_MASK);
regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_RESET_CTL,
WSA885X_I2S_RESET_CTL_RESET_MASK, 0);
ret = 0;
exit_unlock:
mutex_unlock(&wsa885x->state_lock);
return ret;
}
static int wsa885x_codec_set_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
static const struct reg_sequence pll_prep[] = {
{ WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x03 },
{ WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x04 },
{ WSA885X_ANA_TOP_PLL_LOOPFILT_0, 0xB4 },
{ WSA885X_ANA_TOP_PLL_VCO_CTL, 0x00 },
{ WSA885X_ANA_TOP_PLL_OVRD_CTL, 0x00 },
};
static const struct reg_sequence pll_cleanup[] = {
{ WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE, 0x00 },
{ WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x00 },
{ WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x00 },
};
struct wsa885x_priv *wsa885x;
u32 pll_div;
int ret = 0;
wsa885x = snd_soc_component_get_drvdata(dai->component);
if (!freq)
return -EINVAL;
if (WSA885X_CLK_RATE_FIXED % freq)
return -EINVAL;
pll_div = WSA885X_CLK_RATE_FIXED / freq;
if (pll_div > 0xff)
return -EINVAL;
regmap_multi_reg_write(wsa885x->regmap, pll_prep, ARRAY_SIZE(pll_prep));
regmap_write(wsa885x->regmap, WSA885X_ANA_PLL_DIV_CTL_0, pll_div);
regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE, 0x02);
ret = wsa885x_wait_for_pll_lock(wsa885x);
if (ret) {
dev_err(wsa885x->dev, "PLL lock failed, aborting sysclk configuration\n");
regmap_multi_reg_write(wsa885x->regmap, pll_cleanup,
ARRAY_SIZE(pll_cleanup));
return ret;
}
regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x00);
regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_POWER_FSM_CTL1, 0x01);
regmap_write(wsa885x->regmap, WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x00);
return 0;
}
static int wsa885x_codec_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
{
static const struct reg_sequence mute_regs[] = {
{ WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00 },
{ WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS, 0x03 },
};
static const struct reg_sequence mute_commit_regs[] = {
{ WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0, 0x01 },
{ WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1, 0x01 },
{ WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01 },
};
static const struct reg_sequence unmute_prep_head_regs[] = {
{ WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00 },
};
static const struct reg_sequence unmute_prep_tail_regs[] = {
{ WSA885X_SMP_AMP_CTRL_STEREO_IT21_CLUSERINDEX, 0x01 },
{ WSA885X_SMP_AMP_CTRL_STEREO_PPU21_POSTURENUMBER, 0x01 },
};
static const struct reg_sequence unmute_volume_regs[] = {
{ WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB, 0x00 },
{ WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB, 0x00 },
};
static const struct reg_sequence unmute_commit_regs[] = {
{ WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01 },
{ WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS, 0x00 },
};
static const struct reg_sequence unmute_finish_regs[] = {
{ WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0, 0x00 },
{ WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1, 0x00 },
{ WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01 },
};
struct wsa885x_priv *wsa885x;
int ret = 0, ps0 = 0, ps3 = 3;
wsa885x = snd_soc_component_get_drvdata(dai->component);
if (stream != SNDRV_PCM_STREAM_PLAYBACK)
return 0;
mutex_lock(&wsa885x->state_lock);
if (wsa885x->usage_mode > WSA885X_USAGE_MODE_MAX) {
ret = -EINVAL;
goto exit_unlock;
}
if (!wsa885x_is_valid_rx_slot_mask(wsa885x->rx_slot_mask))
wsa885x->rx_slot_mask = WSA885X_CHANNEL_STEREO;
if (mute) {
regmap_multi_reg_write(wsa885x->regmap, mute_regs,
ARRAY_SIZE(mute_regs));
ret = wsa885x_wait_for_pde_state(wsa885x, ps3);
if (ret) {
dev_err(wsa885x->dev,
"PS3 transition failed: %d\n", ret);
} else {
regmap_multi_reg_write(wsa885x->regmap, mute_commit_regs,
ARRAY_SIZE(mute_commit_regs));
}
} else {
regmap_multi_reg_write(wsa885x->regmap, unmute_prep_head_regs,
ARRAY_SIZE(unmute_prep_head_regs));
regmap_write(wsa885x->regmap, WSA885X_SMP_AMP_CTRL_STEREO_OT23_USAGE,
wsa885x->usage_mode);
regmap_multi_reg_write(wsa885x->regmap, unmute_prep_tail_regs,
ARRAY_SIZE(unmute_prep_tail_regs));
wsa885x_program_stereo_volume(wsa885x, wsa885x->stereo_vol_db, false);
regmap_multi_reg_write(wsa885x->regmap, unmute_volume_regs,
ARRAY_SIZE(unmute_volume_regs));
regmap_multi_reg_write(wsa885x->regmap, unmute_commit_regs,
ARRAY_SIZE(unmute_commit_regs));
ret = wsa885x_wait_for_pde_state(wsa885x, ps0);
if (ret)
goto exit_unlock;
if (wsa885x->rx_slot_mask == WSA885X_CHANNEL_STEREO) {
regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_RX,
WSA885X_I2S_TDM_CH_RX_CH0_EN |
WSA885X_I2S_TDM_CH_RX_CH3_EN);
regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x03);
} else if (wsa885x->rx_slot_mask == WSA885X_CHANNEL_MONO_LEFT) {
regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_RX,
WSA885X_I2S_TDM_CH_RX_CH3_EN);
regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x01);
} else if (wsa885x->rx_slot_mask == WSA885X_CHANNEL_MONO_RIGHT) {
regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_RX,
WSA885X_I2S_TDM_CH_RX_CH0_EN);
regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x02);
}
regmap_multi_reg_write(wsa885x->regmap, unmute_finish_regs,
ARRAY_SIZE(unmute_finish_regs));
}
exit_unlock:
mutex_unlock(&wsa885x->state_lock);
return ret;
}
static int wsa885x_codec_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
static const struct reg_sequence regs[] = {
{ WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00 },
};
struct wsa885x_priv *wsa885x;
wsa885x = snd_soc_component_get_drvdata(dai->component);
if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
return 0;
mutex_lock(&wsa885x->state_lock);
regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs));
mutex_unlock(&wsa885x->state_lock);
return 0;
}
static const struct snd_soc_dai_ops wsa885x_dai_ops = {
.hw_params = wsa885x_codec_hw_params,
.set_tdm_slot = wsa885x_codec_set_tdm_slot,
.set_sysclk = wsa885x_codec_set_sysclk,
.mute_stream = wsa885x_codec_mute_stream,
.hw_free = wsa885x_codec_hw_free,
};
static struct snd_soc_dai_driver wsa885x_dai[] = {
{
.name = "wsa885x_dai_drv",
.playback = {
.stream_name = "WSA885X TDM Playback",
.channels_min = 1,
.channels_max = 2,
.rates = WSA885X_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
.ops = &wsa885x_dai_ops,
},
};
static void wsa885x_reset_assert(void *data)
{
struct wsa885x_priv *wsa885x = data;
if (wsa885x->sd_reset)
reset_control_assert(wsa885x->sd_reset);
else
gpiod_direction_output(wsa885x->sd_n, 1);
}
static void wsa885x_reset_deassert(struct wsa885x_priv *wsa885x)
{
if (wsa885x->sd_reset)
reset_control_deassert(wsa885x->sd_reset);
else
gpiod_direction_output(wsa885x->sd_n, 0);
}
static int wsa885x_get_reset(struct device *dev, struct wsa885x_priv *wsa885x)
{
wsa885x->sd_reset = devm_reset_control_get_optional_shared(dev, NULL);
if (IS_ERR(wsa885x->sd_reset))
return dev_err_probe(dev, PTR_ERR(wsa885x->sd_reset),
"Failed to get reset\n");
else if (wsa885x->sd_reset)
return 0;
wsa885x->sd_n = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH);
if (IS_ERR(wsa885x->sd_n))
return dev_err_probe(dev, PTR_ERR(wsa885x->sd_n),
"Shutdown Control GPIO not found\n");
return 0;
}
static bool wsa885x_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case WSA885X_ANA_TOP_PLL_STATUS_0:
case WSA885X_ANA_TOP_PLL_STATUS_1:
case WSA885X_DIG_CTRL0_SDCA_COMMIT:
case WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS:
case WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID:
case WSA885X_INTR_STATUS0:
case WSA885X_INTR_STATUS0 + 1:
case WSA885X_INTR_STATUS0 + 2:
case WSA885X_INTR_CLEAR0:
case WSA885X_INTR_CLEAR0 + 1:
case WSA885X_INTR_CLEAR0 + 2:
return true;
default:
return false;
}
}
static bool wsa885x_readable_register(struct device *dev, unsigned int reg)
{
if (reg == WSA885X_INTR_CLEAR0 ||
reg == WSA885X_INTR_CLEAR0 + 1 ||
reg == WSA885X_INTR_CLEAR0 + 2)
return false;
return reg <= 0x88ff;
}
static bool wsa885x_writeable_register(struct device *dev, unsigned int reg)
{
if (reg > 0x88ff)
return false;
switch (reg) {
case WSA885X_ANA_TOP_PLL_STATUS_0:
case WSA885X_ANA_TOP_PLL_STATUS_1:
case WSA885X_INTR_STATUS0:
case WSA885X_INTR_STATUS0 + 1:
case WSA885X_INTR_STATUS0 + 2:
case WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS:
case WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID:
return false;
default:
return true;
}
}
static const struct regmap_config wsa885x_regmap_cfg = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x88FF,
.ranges = wsa885x_regmap_ranges,
.num_ranges = ARRAY_SIZE(wsa885x_regmap_ranges),
.reg_defaults = wsa885x_codec_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wsa885x_codec_reg_defaults),
.volatile_reg = wsa885x_volatile_register,
.writeable_reg = wsa885x_writeable_register,
.readable_reg = wsa885x_readable_register,
.cache_type = REGCACHE_MAPLE,
.use_single_read = true,
.use_single_write = true,
};
static int wsa885x_component_probe(struct snd_soc_component *component)
{
struct wsa885x_priv *wsa885x =
snd_soc_component_get_drvdata(component);
int ret;
wsa885x->component = component;
snd_soc_component_init_regmap(component, wsa885x->regmap);
ret = wsa885x_hw_init(wsa885x);
if (ret)
return ret;
return wsa885x_unmask_interrupts(wsa885x);
}
static int wsa885x_stereo_gain_offset_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component);
int val;
mutex_lock(&wsa885x->state_lock);
val = wsa885x->stereo_vol_db + 84;
mutex_unlock(&wsa885x->state_lock);
if (val < 0 || val > WSA885X_FU21_VOL_STEPS)
return -ERANGE;
ucontrol->value.integer.value[0] = val;
return 0;
}
static int wsa885x_stereo_gain_offset_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component);
long val;
int stereo_vol_db;
val = ucontrol->value.integer.value[0];
if (val < 0 || val > WSA885X_FU21_VOL_STEPS) {
dev_err(component->dev, "%s: Invalid range, Val: %ld\n", __func__, val);
return -EINVAL;
}
stereo_vol_db = (int)val - 84;
mutex_lock(&wsa885x->state_lock);
if (wsa885x->stereo_vol_db == stereo_vol_db) {
mutex_unlock(&wsa885x->state_lock);
return 0;
}
wsa885x_program_stereo_volume(wsa885x, stereo_vol_db, true);
wsa885x->stereo_vol_db = stereo_vol_db;
mutex_unlock(&wsa885x->state_lock);
return 1;
}
static int wsa885x_usage_modes_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component);
mutex_lock(&wsa885x->state_lock);
if (wsa885x->usage_mode > WSA885X_USAGE_MODE_MAX) {
mutex_unlock(&wsa885x->state_lock);
return -ERANGE;
}
ucontrol->value.integer.value[0] = wsa885x->usage_mode;
mutex_unlock(&wsa885x->state_lock);
return 0;
}
static int wsa885x_usage_modes_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component);
u32 val = ucontrol->value.integer.value[0];
if (val > WSA885X_USAGE_MODE_MAX)
return -EINVAL;
mutex_lock(&wsa885x->state_lock);
if (wsa885x->usage_mode == val) {
mutex_unlock(&wsa885x->state_lock);
return 0;
}
wsa885x->usage_mode = val;
mutex_unlock(&wsa885x->state_lock);
return 1;
}
static int wsa885x_rx_slot_mask_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component);
u32 mask;
mutex_lock(&wsa885x->state_lock);
mask = wsa885x->rx_slot_mask;
mutex_unlock(&wsa885x->state_lock);
if (!wsa885x_is_valid_rx_slot_mask(mask))
return -ERANGE;
ucontrol->value.integer.value[0] = mask;
return 0;
}
static int wsa885x_rx_slot_mask_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component);
u32 mask = ucontrol->value.integer.value[0];
if (!wsa885x_is_valid_rx_slot_mask(mask))
return -EINVAL;
mutex_lock(&wsa885x->state_lock);
if (wsa885x->rx_slot_mask == mask) {
mutex_unlock(&wsa885x->state_lock);
return 0;
}
wsa885x->rx_slot_mask = mask;
mutex_unlock(&wsa885x->state_lock);
return 1;
}
static const struct snd_kcontrol_new wsa885x_snd_controls[] = {
SOC_SINGLE_EXT("Usage Mode", SND_SOC_NOPM, 0, WSA885X_USAGE_MODE_MAX, 0,
wsa885x_usage_modes_get,
wsa885x_usage_modes_put),
SOC_SINGLE_EXT_TLV("Speaker Volume", SND_SOC_NOPM,
0, WSA885X_FU21_VOL_STEPS, 0,
wsa885x_stereo_gain_offset_get,
wsa885x_stereo_gain_offset_put,
wsa885x_fu21_digital_gain),
SOC_SINGLE_EXT("Rx Slot Mask", SND_SOC_NOPM, 0, 3, 0,
wsa885x_rx_slot_mask_get,
wsa885x_rx_slot_mask_put),
};
static const struct snd_soc_component_driver wsa885x_component = {
.name = "wsa885x",
.probe = wsa885x_component_probe,
.controls = wsa885x_snd_controls,
.num_controls = ARRAY_SIZE(wsa885x_snd_controls),
};
static irqreturn_t wsa885x_handle_irq(int irq_idx, void *data)
{
struct wsa885x_priv *wsa885x = data;
if (irq_idx < 0 || irq_idx >= WSA885X_IRQ_MAX)
return IRQ_NONE;
switch (irq_idx) {
case WSA885X_IRQ_INT_SAF2WAR:
case WSA885X_IRQ_INT_WAR2SAF:
case WSA885X_IRQ_INT_DISABLE:
case WSA885X_IRQ_INT_INTR_GPIO1_PIN:
case WSA885X_IRQ_INT_INTR_GPIO2_PIN:
case WSA885X_IRQ_INT_PA0_OCP:
case WSA885X_IRQ_INT_PA1_OCP:
case WSA885X_IRQ_INT_CLIP0:
case WSA885X_IRQ_INT_CLIP1:
case WSA885X_IRQ_INT_CLK_WD:
case WSA885X_IRQ_INT_BOP:
case WSA885X_IRQ_INT_UVLO:
case WSA885X_IRQ_INT_PCM_DATA0_DC:
case WSA885X_IRQ_INT_PCM_DATA1_DC:
case WSA885X_IRQ_INT_PLL_UNLOCKED:
case WSA885X_IRQ_INT_PROT_MODE_CHANGE:
case WSA885X_IRQ_INT_PB_CLOCK_VALID:
case WSA885X_IRQ_INT_SENSE_CLOCK_VALID:
break;
case WSA885X_IRQ_INT_PCM_DATA0_WD:
case WSA885X_IRQ_INT_PCM_DATA1_WD:
if (irq_idx == WSA885X_IRQ_INT_PCM_DATA0_WD)
wsa885x_toggle_irq_bit(wsa885x, WSA885X_DIG_CTRL0_PCM_DATA_WD0_CTL1,
WSA885X_PCM_DATA_WD_CTL1_PCM_DATA_WD_EN_MASK);
else
wsa885x_toggle_irq_bit(wsa885x, WSA885X_DIG_CTRL0_PCM_DATA_WD1_CTL1,
WSA885X_PCM_DATA_WD_CTL1_PCM_DATA_WD_EN_MASK);
break;
case WSA885X_IRQ_INT_PA0_FSM_ERR:
case WSA885X_IRQ_INT_PA1_FSM_ERR:
case WSA885X_IRQ_INT_MAIN_FSM_ERR:
if (irq_idx == WSA885X_IRQ_INT_MAIN_FSM_ERR) {
wsa885x_pulse_irq_bit(wsa885x, WSA885X_DIG_CTRL0_POWER_FSM_CTL0,
WSA885X_POWER_FSM_CTL0_CLEAR_ERROR_MASK);
} else if (irq_idx == WSA885X_IRQ_INT_PA0_FSM_ERR) {
wsa885x_pulse_irq_bit(wsa885x, WSA885X_PA0_FSM_CTL0,
WSA885X_PA_FSM_CTL0_CLEAR_ERROR_MASK);
} else if (irq_idx == WSA885X_IRQ_INT_PA1_FSM_ERR) {
wsa885x_pulse_irq_bit(wsa885x, WSA885X_PA1_FSM_CTL0,
WSA885X_PA_FSM_CTL0_CLEAR_ERROR_MASK);
}
break;
default:
break;
}
return IRQ_HANDLED;
}
static irqreturn_t wsa885x_interrupt_handler(int irq, void *data)
{
static const unsigned int status_reg[WSA885X_NUM_REGS] = {
WSA885X_INTR_STATUS0,
WSA885X_INTR_STATUS0 + 1,
WSA885X_INTR_STATUS0 + 2,
};
static const unsigned int clear_reg[WSA885X_NUM_REGS] = {
WSA885X_INTR_CLEAR0,
WSA885X_INTR_CLEAR0 + 1,
WSA885X_INTR_CLEAR0 + 2,
};
unsigned int status[WSA885X_NUM_REGS] = { 0 };
struct wsa885x_priv *wsa885x = data;
irqreturn_t handled = IRQ_NONE;
irqreturn_t irq_ret;
int i, bit, ret, irq_num;
for (i = 0; i < WSA885X_NUM_REGS; i++) {
ret = regmap_read(wsa885x->regmap, status_reg[i], &status[i]);
if (ret) {
dev_err(wsa885x->dev,
"Failed to read status_reg[%d] (0x%x): %d\n",
i, status_reg[i], ret);
status[i] = 0;
continue;
}
}
for (i = 0; i < WSA885X_NUM_REGS; i++) {
for (bit = 0; bit < 8; bit++) {
if (status[i] & BIT(bit)) {
irq_num = i * 8 + bit;
regmap_write(wsa885x->regmap, clear_reg[i], BIT(bit));
regmap_write(wsa885x->regmap, clear_reg[i], 0);
if (irq_num >= WSA885X_IRQ_MAX) {
dev_warn_ratelimited(wsa885x->dev,
"Unexpected IRQ bit %d (reg %d)\n",
bit, i);
handled = IRQ_HANDLED;
continue;
}
irq_ret = wsa885x_handle_irq(irq_num, wsa885x);
if (irq_ret == IRQ_HANDLED)
handled = IRQ_HANDLED;
}
}
}
return handled;
}
static int wsa885x_register_irq(struct wsa885x_priv *wsa885x)
{
if (!wsa885x->client->irq)
return dev_err_probe(wsa885x->dev, -EINVAL,
"IRQ is not configured\n");
return devm_request_threaded_irq(wsa885x->dev, wsa885x->client->irq, NULL,
wsa885x_interrupt_handler,
IRQF_ONESHOT,
dev_name(wsa885x->dev), wsa885x);
}
static int wsa885x_probe(struct i2c_client *client)
{
struct wsa885x_priv *wsa885x;
const struct snd_soc_component_driver *component_driver = &wsa885x_component;
const char *battery_config;
unsigned int i;
int ret;
struct device *dev = &client->dev;
wsa885x = devm_kzalloc(dev, sizeof(*wsa885x), GFP_KERNEL);
if (!wsa885x)
return -ENOMEM;
wsa885x->client = client;
wsa885x->dev = dev;
wsa885x->stereo_vol_db = -84;
wsa885x->rx_slot_mask = WSA885X_CHANNEL_STEREO;
mutex_init(&wsa885x->state_lock);
wsa885x->regmap = devm_regmap_init_i2c(client, &wsa885x_regmap_cfg);
if (IS_ERR(wsa885x->regmap))
return PTR_ERR(wsa885x->regmap);
ret = device_property_read_string(dev, "qcom,battery-config",
&battery_config);
if (ret) {
wsa885x->batt_conf = WSA885X_BATT_1S;
} else if (!strcmp(battery_config, "1s")) {
wsa885x->batt_conf = WSA885X_BATT_1S;
} else if (!strcmp(battery_config, "2s")) {
wsa885x->batt_conf = WSA885X_BATT_2S;
} else {
return dev_err_probe(dev, -EINVAL,
"Invalid battery config %s (expected 1s or 2s)\n",
battery_config);
}
for (i = 0; i < ARRAY_SIZE(wsa885x_supply_name); i++) {
ret = devm_regulator_get_enable(dev, wsa885x_supply_name[i]);
if (ret)
return dev_err_probe(dev, ret,
"Failed to enable regulator %s\n",
wsa885x_supply_name[i]);
}
ret = wsa885x_get_reset(dev, wsa885x);
if (ret)
return ret;
wsa885x_reset_deassert(wsa885x);
usleep_range(5000, 5500);
ret = devm_add_action_or_reset(dev, wsa885x_reset_assert, wsa885x);
if (ret)
return dev_err_probe(dev, ret, "devm_add_action_or_reset failed\n");
i2c_set_clientdata(client, wsa885x);
ret = wsa885x_register_irq(wsa885x);
if (ret)
return dev_err_probe(dev, ret, "wsa885x irq registration failed\n");
ret = devm_snd_soc_register_component(dev, component_driver,
wsa885x_dai,
ARRAY_SIZE(wsa885x_dai));
if (ret)
return dev_err_probe(dev, ret, "Codec component registration failed\n");
return 0;
}
static const struct of_device_id wsa885x_dt_match[] = {
{
.compatible = "qcom,wsa8855",
},
{}
};
MODULE_DEVICE_TABLE(of, wsa885x_dt_match);
static const struct i2c_device_id wsa885x_id[] = {
{
.name = "wsa885x",
.driver_data = 0,
},
{}
};
MODULE_DEVICE_TABLE(i2c, wsa885x_id);
static struct i2c_driver wsa885x_driver = {
.driver = {
.name = "wsa885x",
.of_match_table = wsa885x_dt_match,
},
.probe = wsa885x_probe,
.id_table = wsa885x_id,
};
module_i2c_driver(wsa885x_driver);
MODULE_DESCRIPTION("ASoC WSA885X Stereo Smart PA Codec Driver");
MODULE_LICENSE("GPL");