// SPDX-License-Identifier: GPL-2.0-only
//
// Cirrus Logic CS48L32 audio DSP.
//
// Copyright (C) 2016-2018, 2020, 2022, 2025 Cirrus Logic, Inc. and
//               Cirrus Logic International Semiconductor Ltd.

#include <dt-bindings/sound/cs48l32.h>
#include <linux/array_size.h>
#include <linux/build_bug.h>
#include <linux/clk.h>
#include <linux/container_of.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/gcd.h>
#include <linux/gpio/consumer.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/string_choices.h>
#include <sound/cs48l32.h>
#include <sound/cs48l32_registers.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-component.h>
#include <sound/soc-dai.h>
#include <sound/soc-dapm.h>
#include <sound/tlv.h>

#include "cs48l32.h"

static const char * const cs48l32_core_supplies[] = { "vdd-a", "vdd-io" };

static const struct cs_dsp_region cs48l32_dsp1_regions[] = {
	{ .type = WMFW_HALO_PM_PACKED, .base = 0x3800000 },
	{ .type = WMFW_HALO_XM_PACKED, .base = 0x2000000 },
	{ .type = WMFW_ADSP2_XM, .base = 0x2800000 },
	{ .type = WMFW_HALO_YM_PACKED, .base = 0x2C00000 },
	{ .type = WMFW_ADSP2_YM, .base = 0x3400000 },
};

static const struct cs48l32_dsp_power_reg_block cs48l32_dsp1_sram_ext_regs[] = {
	{ CS48L32_DSP1_XM_SRAM_IBUS_SETUP_1, CS48L32_DSP1_XM_SRAM_IBUS_SETUP_24 },
	{ CS48L32_DSP1_YM_SRAM_IBUS_SETUP_1, CS48L32_DSP1_YM_SRAM_IBUS_SETUP_8 },
	{ CS48L32_DSP1_PM_SRAM_IBUS_SETUP_1, CS48L32_DSP1_PM_SRAM_IBUS_SETUP_7 },
};

static const unsigned int cs48l32_dsp1_sram_pwd_regs[] = {
	CS48L32_DSP1_XM_SRAM_IBUS_SETUP_0,
	CS48L32_DSP1_YM_SRAM_IBUS_SETUP_0,
	CS48L32_DSP1_PM_SRAM_IBUS_SETUP_0,
};

static const struct cs48l32_dsp_power_regs cs48l32_dsp_sram_regs = {
	.ext = cs48l32_dsp1_sram_ext_regs,
	.n_ext = ARRAY_SIZE(cs48l32_dsp1_sram_ext_regs),
	.pwd = cs48l32_dsp1_sram_pwd_regs,
	.n_pwd = ARRAY_SIZE(cs48l32_dsp1_sram_pwd_regs),
};

static const char * const cs48l32_mixer_texts[] = {
	"None",
	"Tone Generator 1",
	"Tone Generator 2",
	"Noise Generator",
	"IN1L",
	"IN1R",
	"IN2L",
	"IN2R",
	"ASP1RX1",
	"ASP1RX2",
	"ASP1RX3",
	"ASP1RX4",
	"ASP1RX5",
	"ASP1RX6",
	"ASP1RX7",
	"ASP1RX8",
	"ASP2RX1",
	"ASP2RX2",
	"ASP2RX3",
	"ASP2RX4",
	"ISRC1INT1",
	"ISRC1INT2",
	"ISRC1INT3",
	"ISRC1INT4",
	"ISRC1DEC1",
	"ISRC1DEC2",
	"ISRC1DEC3",
	"ISRC1DEC4",
	"ISRC2INT1",
	"ISRC2INT2",
	"ISRC2DEC1",
	"ISRC2DEC2",
	"ISRC3INT1",
	"ISRC3INT2",
	"ISRC3DEC1",
	"ISRC3DEC2",
	"EQ1",
	"EQ2",
	"EQ3",
	"EQ4",
	"DRC1L",
	"DRC1R",
	"DRC2L",
	"DRC2R",
	"LHPF1",
	"LHPF2",
	"LHPF3",
	"LHPF4",
	"Ultrasonic 1",
	"Ultrasonic 2",
	"DSP1.1",
	"DSP1.2",
	"DSP1.3",
	"DSP1.4",
	"DSP1.5",
	"DSP1.6",
	"DSP1.7",
	"DSP1.8",
};

static unsigned int cs48l32_mixer_values[] = {
	0x000, /* Silence (mute) */
	0x004, /* Tone generator 1 */
	0x005, /* Tone generator 2 */
	0x00C, /* Noise Generator */
	0x010, /* IN1L signal path */
	0x011, /* IN1R signal path */
	0x012, /* IN2L signal path */
	0x013, /* IN2R signal path */
	0x020, /* ASP1 RX1 */
	0x021, /* ASP1 RX2 */
	0x022, /* ASP1 RX3 */
	0x023, /* ASP1 RX4 */
	0x024, /* ASP1 RX5 */
	0x025, /* ASP1 RX6 */
	0x026, /* ASP1 RX7 */
	0x027, /* ASP1 RX8 */
	0x030, /* ASP2 RX1 */
	0x031, /* ASP2 RX2 */
	0x032, /* ASP2 RX3 */
	0x033, /* ASP2 RX4 */
	0x098, /* ISRC1 INT1 */
	0x099, /* ISRC1 INT2 */
	0x09a, /* ISRC1 INT3 */
	0x09b, /* ISRC1 INT4 */
	0x09C, /* ISRC1 DEC1 */
	0x09D, /* ISRC1 DEC2 */
	0x09e, /* ISRC1 DEC3 */
	0x09f, /* ISRC1 DEC4 */
	0x0A0, /* ISRC2 INT1 */
	0x0A1, /* ISRC2 INT2 */
	0x0A4, /* ISRC2 DEC1 */
	0x0A5, /* ISRC2 DEC2 */
	0x0A8, /* ISRC3 INT1 */
	0x0A9, /* ISRC3 INT2 */
	0x0AC, /* ISRC3 DEC1 */
	0x0AD, /* ISRC3 DEC2 */
	0x0B8, /* EQ1 */
	0x0B9, /* EQ2 */
	0x0BA, /* EQ3 */
	0x0BB, /* EQ4 */
	0x0C0, /* DRC1 Left */
	0x0C1, /* DRC1 Right */
	0x0C2, /* DRC2 Left */
	0x0C3, /* DRC2 Right */
	0x0C8, /* LHPF1 */
	0x0C9, /* LHPF2 */
	0x0CA, /* LHPF3 */
	0x0CB, /* LHPF4 */
	0x0D8, /* Ultrasonic 1 */
	0x0D9, /* Ultrasonic 2 */
	0x100, /* DSP1 channel 1 */
	0x101, /* DSP1 channel 2 */
	0x102, /* DSP1 channel 3 */
	0x103, /* DSP1 channel 4 */
	0x104, /* DSP1 channel 5 */
	0x105, /* DSP1 channel 6 */
	0x106, /* DSP1 channel 7 */
	0x107, /* DSP1 channel 8 */
};
static_assert(ARRAY_SIZE(cs48l32_mixer_texts) == ARRAY_SIZE(cs48l32_mixer_values));
#define CS48L32_NUM_MIXER_INPUTS	ARRAY_SIZE(cs48l32_mixer_values)

static const DECLARE_TLV_DB_SCALE(cs48l32_ana_tlv, 0, 100, 0);
static const DECLARE_TLV_DB_SCALE(cs48l32_eq_tlv, -1200, 100, 0);
static const DECLARE_TLV_DB_SCALE(cs48l32_digital_tlv, -6400, 50, 0);
static const DECLARE_TLV_DB_SCALE(cs48l32_noise_tlv, -10800, 600, 0);
static const DECLARE_TLV_DB_SCALE(cs48l32_mixer_tlv, -3200, 100, 0);
static const DECLARE_TLV_DB_SCALE(cs48l32_us_tlv, 0, 600, 0);

static void cs48l32_spin_sysclk(struct cs48l32_codec *cs48l32_codec)
{
	struct cs48l32 *cs48l32 = &cs48l32_codec->core;
	unsigned int val;
	int ret, i;

	/* Skip this if the chip is down */
	if (pm_runtime_suspended(cs48l32->dev))
		return;

	/*
	 * Just read a register a few times to ensure the internal
	 * oscillator sends out some clocks.
	 */
	for (i = 0; i < 4; i++) {
		ret = regmap_read(cs48l32->regmap, CS48L32_DEVID, &val);
		if (ret)
			dev_err(cs48l32_codec->core.dev, "%s Failed to read register: %d (%d)\n",
				__func__, ret, i);
	}

	udelay(300);
}

static const char * const cs48l32_rate_text[] = {
	"Sample Rate 1", "Sample Rate 2", "Sample Rate 3", "Sample Rate 4",
};

static const unsigned int cs48l32_rate_val[] = {
	0x0, 0x1, 0x2, 0x3,
};
static_assert(ARRAY_SIZE(cs48l32_rate_val) == ARRAY_SIZE(cs48l32_rate_text));

static int cs48l32_rate_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
	int ret;

	/* Prevent any mixer mux changes while we do this */
	mutex_lock(&cs48l32_codec->rate_lock);

	/* The write must be guarded by a number of SYSCLK cycles */
	cs48l32_spin_sysclk(cs48l32_codec);
	ret = snd_soc_put_enum_double(kcontrol, ucontrol);
	cs48l32_spin_sysclk(cs48l32_codec);

	mutex_unlock(&cs48l32_codec->rate_lock);

	return ret;
}

static const char * const cs48l32_sample_rate_text[] = {
	"12kHz",
	"24kHz",
	"48kHz",
	"96kHz",
	"192kHz",
	"384kHz",
	"768kHz",
	"11.025kHz",
	"22.05kHz",
	"44.1kHz",
	"88.2kHz",
	"176.4kHz",
	"352.8kHz",
	"705.6kHz",
	"8kHz",
	"16kHz",
	"32kHz",
};

static const unsigned int cs48l32_sample_rate_val[] = {
	0x01, /* 12kHz */
	0x02, /* 24kHz */
	0x03, /* 48kHz */
	0x04, /* 96kHz */
	0x05, /* 192kHz */
	0x06, /* 384kHz */
	0x07, /* 768kHz */
	0x09, /* 11.025kHz */
	0x0a, /* 22.05kHz */
	0x0b, /* 44.1kHz */
	0x0c, /* 88.2kHz */
	0x0d, /* 176.4kHz */
	0x0e, /* 352.8kHz */
	0x0f, /* 705.6kHz */
	0x11, /* 8kHz */
	0x12, /* 16kHz */
	0x13, /* 32kHz */
};
static_assert(ARRAY_SIZE(cs48l32_sample_rate_val) == ARRAY_SIZE(cs48l32_sample_rate_text));
#define CS48L32_SAMPLE_RATE_ENUM_SIZE	ARRAY_SIZE(cs48l32_sample_rate_val)

static const struct soc_enum cs48l32_sample_rate[] = {
	SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE1,
			      CS48L32_SAMPLE_RATE_1_SHIFT,
			      CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT,
			      CS48L32_SAMPLE_RATE_ENUM_SIZE,
			      cs48l32_sample_rate_text,
			      cs48l32_sample_rate_val),
	SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE2,
			      CS48L32_SAMPLE_RATE_1_SHIFT,
			      CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT,
			      CS48L32_SAMPLE_RATE_ENUM_SIZE,
			      cs48l32_sample_rate_text,
			      cs48l32_sample_rate_val),
	SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE3,
			      CS48L32_SAMPLE_RATE_1_SHIFT,
			      CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT,
			      CS48L32_SAMPLE_RATE_ENUM_SIZE,
			      cs48l32_sample_rate_text,
			      cs48l32_sample_rate_val),
	SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE4,
			      CS48L32_SAMPLE_RATE_1_SHIFT,
			      CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT,
			      CS48L32_SAMPLE_RATE_ENUM_SIZE,
			      cs48l32_sample_rate_text,
			      cs48l32_sample_rate_val),
};

static int cs48l32_inmux_put(struct snd_kcontrol *kcontrol,
			     struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
	struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
	struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
	unsigned int mux, src_val, in_type;
	int ret;

	mux = ucontrol->value.enumerated.item[0];
	if (mux > 1)
		return -EINVAL;

	switch (e->reg) {
	case CS48L32_IN1L_CONTROL1:
		in_type = cs48l32_codec->in_type[0][mux];
		break;
	case CS48L32_IN1R_CONTROL1:
		in_type = cs48l32_codec->in_type[1][mux];
		break;
	default:
		return -EINVAL;
	}

	src_val = mux << e->shift_l;

	if (in_type == CS48L32_IN_TYPE_SE)
		src_val |= 1 << CS48L32_INx_SRC_SHIFT;

	ret = snd_soc_component_update_bits(component,
					    e->reg,
					    CS48L32_INx_SRC_MASK,
					    src_val);
	if (ret > 0)
		snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL);

	return ret;
}

static const char * const cs48l32_inmux_texts[] = {
	"Analog 1", "Analog 2",
};

static SOC_ENUM_SINGLE_DECL(cs48l32_in1muxl_enum,
			    CS48L32_IN1L_CONTROL1,
			    CS48L32_INx_SRC_SHIFT + 1,
			    cs48l32_inmux_texts);

static SOC_ENUM_SINGLE_DECL(cs48l32_in1muxr_enum,
			    CS48L32_IN1R_CONTROL1,
			    CS48L32_INx_SRC_SHIFT + 1,
			    cs48l32_inmux_texts);

static const struct snd_kcontrol_new cs48l32_inmux[] = {
	SOC_DAPM_ENUM_EXT("IN1L Mux", cs48l32_in1muxl_enum,
			  snd_soc_dapm_get_enum_double, cs48l32_inmux_put),
	SOC_DAPM_ENUM_EXT("IN1R Mux", cs48l32_in1muxr_enum,
			  snd_soc_dapm_get_enum_double, cs48l32_inmux_put),
};

static const char * const cs48l32_dmode_texts[] = {
	"Analog", "Digital",
};

static int cs48l32_dmode_put(struct snd_kcontrol *kcontrol,
			     struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
	struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
	struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
	unsigned int mode;
	int ret, result;

	mode = ucontrol->value.enumerated.item[0];
	switch (mode) {
	case 0:
		ret = snd_soc_component_update_bits(component,
						    CS48L32_ADC1L_ANA_CONTROL1,
						    CS48L32_ADC1x_INT_ENA_FRC_MASK,
						    CS48L32_ADC1x_INT_ENA_FRC_MASK);
		if (ret < 0) {
			dev_err(component->dev,
				"Failed to set ADC1L_INT_ENA_FRC: %d\n", ret);
			return ret;
		}

		ret = snd_soc_component_update_bits(component,
						    CS48L32_ADC1R_ANA_CONTROL1,
						    CS48L32_ADC1x_INT_ENA_FRC_MASK,
						    CS48L32_ADC1x_INT_ENA_FRC_MASK);
		if (ret < 0) {
			dev_err(component->dev,
				"Failed to set ADC1R_INT_ENA_FRC: %d\n", ret);
			return ret;
		}

		result = snd_soc_component_update_bits(component,
						       e->reg,
						       BIT(CS48L32_IN1_MODE_SHIFT),
						       0);
		if (result < 0) {
			dev_err(component->dev, "Failed to set input mode: %d\n", result);
			return result;
		}

		usleep_range(200, 300);

		ret = snd_soc_component_update_bits(component,
						    CS48L32_ADC1L_ANA_CONTROL1,
						    CS48L32_ADC1x_INT_ENA_FRC_MASK,
						    0);
		if (ret < 0) {
			dev_err(component->dev,
				"Failed to clear ADC1L_INT_ENA_FRC: %d\n", ret);
			return ret;
		}

		ret = snd_soc_component_update_bits(component,
						    CS48L32_ADC1R_ANA_CONTROL1,
						    CS48L32_ADC1x_INT_ENA_FRC_MASK,
						    0);
		if (ret < 0) {
			dev_err(component->dev,
				"Failed to clear ADC1R_INT_ENA_FRC: %d\n", ret);
			return ret;
		}

		if (result > 0)
			snd_soc_dapm_mux_update_power(dapm, kcontrol, mode, e, NULL);

		return result;
	case 1:
		return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
	default:
		return -EINVAL;
	}
}

static SOC_ENUM_SINGLE_DECL(cs48l32_in1dmode_enum,
			    CS48L32_INPUT1_CONTROL1,
			    CS48L32_IN1_MODE_SHIFT,
			    cs48l32_dmode_texts);

static const struct snd_kcontrol_new cs48l32_dmode_mux[] = {
	SOC_DAPM_ENUM_EXT("IN1 Mode", cs48l32_in1dmode_enum,
			  snd_soc_dapm_get_enum_double, cs48l32_dmode_put),
};

static const char * const cs48l32_in_texts[] = {
	"IN1L", "IN1R", "IN2L", "IN2R",
};
static_assert(ARRAY_SIZE(cs48l32_in_texts) == CS48L32_MAX_INPUT);

static const char * const cs48l32_us_freq_texts[] = {
	"16-24kHz", "20-28kHz",
};

static const unsigned int cs48l32_us_freq_val[] = {
	0x2, 0x3,
};

static const struct soc_enum cs48l32_us_freq[] = {
	SOC_VALUE_ENUM_SINGLE(CS48L32_US1_CONTROL,
			      CS48L32_US1_FREQ_SHIFT,
			      CS48L32_US1_FREQ_MASK >> CS48L32_US1_FREQ_SHIFT,
			      ARRAY_SIZE(cs48l32_us_freq_val),
			      cs48l32_us_freq_texts,
			      cs48l32_us_freq_val),
	SOC_VALUE_ENUM_SINGLE(CS48L32_US2_CONTROL,
			      CS48L32_US1_FREQ_SHIFT,
			      CS48L32_US1_FREQ_MASK >> CS48L32_US1_FREQ_SHIFT,
			      ARRAY_SIZE(cs48l32_us_freq_val),
			      cs48l32_us_freq_texts,
			      cs48l32_us_freq_val),
};

static const unsigned int cs48l32_us_in_val[] = {
	0x0, 0x1, 0x2, 0x3,
};

static const struct soc_enum cs48l32_us_inmux_enum[] = {
	SOC_VALUE_ENUM_SINGLE(CS48L32_US1_CONTROL,
			      CS48L32_US1_SRC_SHIFT,
			      CS48L32_US1_SRC_MASK >> CS48L32_US1_SRC_SHIFT,
			      ARRAY_SIZE(cs48l32_us_in_val),
			      cs48l32_in_texts,
			      cs48l32_us_in_val),
	SOC_VALUE_ENUM_SINGLE(CS48L32_US2_CONTROL,
			      CS48L32_US1_SRC_SHIFT,
			      CS48L32_US1_SRC_MASK >> CS48L32_US1_SRC_SHIFT,
			      ARRAY_SIZE(cs48l32_us_in_val),
			      cs48l32_in_texts,
			      cs48l32_us_in_val),
};

static const struct snd_kcontrol_new cs48l32_us_inmux[] = {
	SOC_DAPM_ENUM("Ultrasonic 1 Input", cs48l32_us_inmux_enum[0]),
	SOC_DAPM_ENUM("Ultrasonic 2 Input", cs48l32_us_inmux_enum[1]),
};

static const char * const cs48l32_us_det_thr_texts[] = {
	"-6dB", "-9dB", "-12dB", "-15dB", "-18dB", "-21dB", "-24dB", "-27dB",
};

static const struct soc_enum cs48l32_us_det_thr[] = {
	SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL,
			CS48L32_US1_DET_THR_SHIFT,
			ARRAY_SIZE(cs48l32_us_det_thr_texts),
			cs48l32_us_det_thr_texts),
	SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL,
			CS48L32_US1_DET_THR_SHIFT,
			ARRAY_SIZE(cs48l32_us_det_thr_texts),
			cs48l32_us_det_thr_texts),
};

static const char * const cs48l32_us_det_num_texts[] = {
	"1 Sample",
	"2 Samples",
	"4 Samples",
	"8 Samples",
	"16 Samples",
	"32 Samples",
	"64 Samples",
	"128 Samples",
	"256 Samples",
	"512 Samples",
	"1024 Samples",
	"2048 Samples",
	"4096 Samples",
	"8192 Samples",
	"16384 Samples",
	"32768 Samples",
};

static const struct soc_enum cs48l32_us_det_num[] = {
	SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL,
			CS48L32_US1_DET_NUM_SHIFT,
			ARRAY_SIZE(cs48l32_us_det_num_texts),
			cs48l32_us_det_num_texts),
	SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL,
			CS48L32_US1_DET_NUM_SHIFT,
			ARRAY_SIZE(cs48l32_us_det_num_texts),
			cs48l32_us_det_num_texts),
};

static const char * const cs48l32_us_det_hold_texts[] = {
	"0 Samples",
	"31 Samples",
	"63 Samples",
	"127 Samples",
	"255 Samples",
	"511 Samples",
	"1023 Samples",
	"2047 Samples",
	"4095 Samples",
	"8191 Samples",
	"16383 Samples",
	"32767 Samples",
	"65535 Samples",
	"131071 Samples",
	"262143 Samples",
	"524287 Samples",
};

static const struct soc_enum cs48l32_us_det_hold[] = {
	SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL,
			CS48L32_US1_DET_HOLD_SHIFT,
			ARRAY_SIZE(cs48l32_us_det_hold_texts),
			cs48l32_us_det_hold_texts),
	SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL,
			CS48L32_US1_DET_HOLD_SHIFT,
			ARRAY_SIZE(cs48l32_us_det_hold_texts),
			cs48l32_us_det_hold_texts),
};

static const struct soc_enum cs48l32_us_output_rate[] = {
	SOC_VALUE_ENUM_SINGLE(CS48L32_US1_CONTROL,
			      CS48L32_US1_RATE_SHIFT,
			      CS48L32_US1_RATE_MASK >> CS48L32_US1_RATE_SHIFT,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text,
			      cs48l32_rate_val),
	SOC_VALUE_ENUM_SINGLE(CS48L32_US2_CONTROL,
			      CS48L32_US1_RATE_SHIFT,
			      CS48L32_US1_RATE_MASK >> CS48L32_US1_RATE_SHIFT,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text,
			      cs48l32_rate_val),
};

static const char * const cs48l32_us_det_lpf_cut_texts[] = {
	"1722Hz", "833Hz", "408Hz", "203Hz",
};

static const struct soc_enum cs48l32_us_det_lpf_cut[] = {
	SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL,
			CS48L32_US1_DET_LPF_CUT_SHIFT,
			ARRAY_SIZE(cs48l32_us_det_lpf_cut_texts),
			cs48l32_us_det_lpf_cut_texts),
	SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL,
			CS48L32_US1_DET_LPF_CUT_SHIFT,
			ARRAY_SIZE(cs48l32_us_det_lpf_cut_texts),
			cs48l32_us_det_lpf_cut_texts),
};

static const char * const cs48l32_us_det_dcy_texts[] = {
	"0 ms", "0.79 ms", "1.58 ms", "3.16 ms", "6.33 ms", "12.67 ms", "25.34 ms", "50.69 ms",
};

static const struct soc_enum cs48l32_us_det_dcy[] = {
	SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL,
			CS48L32_US1_DET_DCY_SHIFT,
			ARRAY_SIZE(cs48l32_us_det_dcy_texts),
			cs48l32_us_det_dcy_texts),
	SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL,
			CS48L32_US1_DET_DCY_SHIFT,
			ARRAY_SIZE(cs48l32_us_det_dcy_texts),
			cs48l32_us_det_dcy_texts),
};

static const struct snd_kcontrol_new cs48l32_us_switch[] = {
	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
};

static const char * const cs48l32_vol_ramp_text[] = {
	"0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB", "16ms/6dB", "32ms/6dB",
};

static SOC_ENUM_SINGLE_DECL(cs48l32_in_vd_ramp,
			    CS48L32_INPUT_VOL_CONTROL,
			    CS48L32_IN_VD_RAMP_SHIFT,
			    cs48l32_vol_ramp_text);

static SOC_ENUM_SINGLE_DECL(cs48l32_in_vi_ramp,
			    CS48L32_INPUT_VOL_CONTROL,
			    CS48L32_IN_VI_RAMP_SHIFT,
			    cs48l32_vol_ramp_text);

static const char * const cs48l32_in_hpf_cut_text[] = {
	"2.5Hz", "5Hz", "10Hz", "20Hz", "40Hz"
};

static SOC_ENUM_SINGLE_DECL(cs48l32_in_hpf_cut_enum,
			    CS48L32_INPUT_HPF_CONTROL,
			    CS48L32_IN_HPF_CUT_SHIFT,
			    cs48l32_in_hpf_cut_text);

static const char * const cs48l32_in_dmic_osr_text[] = {
	"384kHz", "768kHz", "1.536MHz", "2.048MHz", "2.4576MHz", "3.072MHz", "6.144MHz",
};

static const struct soc_enum cs48l32_in_dmic_osr[] = {
	SOC_ENUM_SINGLE(CS48L32_INPUT1_CONTROL1,
			CS48L32_IN1_OSR_SHIFT,
			ARRAY_SIZE(cs48l32_in_dmic_osr_text),
			cs48l32_in_dmic_osr_text),
	SOC_ENUM_SINGLE(CS48L32_INPUT2_CONTROL1,
			CS48L32_IN1_OSR_SHIFT,
			ARRAY_SIZE(cs48l32_in_dmic_osr_text),
			cs48l32_in_dmic_osr_text),
};

static bool cs48l32_is_input_enabled(struct snd_soc_component *component,
				     unsigned int reg)
{
	unsigned int input_active;

	input_active = snd_soc_component_read(component, CS48L32_INPUT_CONTROL);
	switch (reg) {
	case CS48L32_IN1L_CONTROL1:
		return input_active & BIT(CS48L32_IN1L_EN_SHIFT);
	case CS48L32_IN1R_CONTROL1:
		return input_active & BIT(CS48L32_IN1R_EN_SHIFT);
	case CS48L32_IN2L_CONTROL1:
		return input_active & BIT(CS48L32_IN2L_EN_SHIFT);
	case CS48L32_IN2R_CONTROL1:
		return input_active & BIT(CS48L32_IN2R_EN_SHIFT);
	default:
		return false;
	}
}

static int cs48l32_in_rate_put(struct snd_kcontrol *kcontrol,
			       struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
	struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
	int ret;

	snd_soc_dapm_mutex_lock(dapm);

	/* Cannot change rate on an active input */
	if (cs48l32_is_input_enabled(component, e->reg)) {
		ret = -EBUSY;
		goto exit;
	}

	ret = snd_soc_put_enum_double(kcontrol, ucontrol);
exit:
	snd_soc_dapm_mutex_unlock(dapm);

	return ret;
}

static const struct soc_enum cs48l32_input_rate[] = {
	SOC_VALUE_ENUM_SINGLE(CS48L32_IN1L_CONTROL1,
			      CS48L32_INx_RATE_SHIFT,
			      CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text,
			      cs48l32_rate_val),
	SOC_VALUE_ENUM_SINGLE(CS48L32_IN1R_CONTROL1,
			      CS48L32_INx_RATE_SHIFT,
			      CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text,
			      cs48l32_rate_val),
	SOC_VALUE_ENUM_SINGLE(CS48L32_IN2L_CONTROL1,
			      CS48L32_INx_RATE_SHIFT,
			      CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text,
			      cs48l32_rate_val),
	SOC_VALUE_ENUM_SINGLE(CS48L32_IN2R_CONTROL1,
			      CS48L32_INx_RATE_SHIFT,
			      CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text,
			      cs48l32_rate_val),
};

static int cs48l32_low_power_mode_put(struct snd_kcontrol *kcontrol,
				      struct snd_ctl_elem_value *ucontrol)
{
	struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value;
	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
	struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
	int ret;

	snd_soc_dapm_mutex_lock(dapm);

	/* Cannot change rate on an active input */
	if (cs48l32_is_input_enabled(component, mc->reg)) {
		ret = -EBUSY;
		goto exit;
	}

	ret = snd_soc_put_volsw(kcontrol, ucontrol);

exit:
	snd_soc_dapm_mutex_unlock(dapm);
	return ret;
}

static const struct soc_enum noise_gen_rate =
	SOC_VALUE_ENUM_SINGLE(CS48L32_COMFORT_NOISE_GENERATOR,
			      CS48L32_NOISE_GEN_RATE_SHIFT,
			      CS48L32_NOISE_GEN_RATE_MASK >> CS48L32_NOISE_GEN_RATE_SHIFT,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text,
			      cs48l32_rate_val);

static const char * const cs48l32_auxpdm_freq_texts[] = {
	"3.072MHz", "2.048MHz", "1.536MHz", "768kHz",
};

static SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm1_freq,
			    CS48L32_AUXPDM1_CONTROL1,
			    CS48L32_AUXPDM1_FREQ_SHIFT,
			    cs48l32_auxpdm_freq_texts);

static SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm2_freq,
			    CS48L32_AUXPDM2_CONTROL1,
			    CS48L32_AUXPDM1_FREQ_SHIFT,
			    cs48l32_auxpdm_freq_texts);

static const char * const cs48l32_auxpdm_src_texts[] = {
	"Analog", "IN1 Digital", "IN2 Digital",
};

static SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm1_in,
			    CS48L32_AUXPDM_CTRL2,
			    CS48L32_AUXPDMDAT1_SRC_SHIFT,
			    cs48l32_auxpdm_src_texts);

static SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm2_in,
			    CS48L32_AUXPDM_CTRL2,
			    CS48L32_AUXPDMDAT2_SRC_SHIFT,
			    cs48l32_auxpdm_src_texts);

static const struct snd_kcontrol_new cs48l32_auxpdm_inmux[] = {
	SOC_DAPM_ENUM("AUXPDM1 Input", cs48l32_auxpdm1_in),
	SOC_DAPM_ENUM("AUXPDM2 Input", cs48l32_auxpdm2_in),
};

static const unsigned int cs48l32_auxpdm_analog_in_val[] = {
	0x0, 0x1,
};

static const struct soc_enum cs48l32_auxpdm_analog_inmux_enum[] = {
	SOC_VALUE_ENUM_SINGLE(CS48L32_AUXPDM1_CONTROL1,
			      CS48L32_AUXPDM1_SRC_SHIFT,
			      CS48L32_AUXPDM1_SRC_MASK >> CS48L32_AUXPDM1_SRC_SHIFT,
			      ARRAY_SIZE(cs48l32_auxpdm_analog_in_val),
			      cs48l32_in_texts,
			      cs48l32_auxpdm_analog_in_val),
	SOC_VALUE_ENUM_SINGLE(CS48L32_AUXPDM2_CONTROL1,
			      CS48L32_AUXPDM1_SRC_SHIFT,
			      CS48L32_AUXPDM1_SRC_MASK >> CS48L32_AUXPDM1_SRC_SHIFT,
			      ARRAY_SIZE(cs48l32_auxpdm_analog_in_val),
			      cs48l32_in_texts,
			      cs48l32_auxpdm_analog_in_val),
};

static const struct snd_kcontrol_new cs48l32_auxpdm_analog_inmux[] = {
	SOC_DAPM_ENUM("AUXPDM1 Analog Input", cs48l32_auxpdm_analog_inmux_enum[0]),
	SOC_DAPM_ENUM("AUXPDM2 Analog Input", cs48l32_auxpdm_analog_inmux_enum[1]),
};

static const struct snd_kcontrol_new cs48l32_auxpdm_switch[] = {
	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
};

static const struct soc_enum cs48l32_isrc_fsh[] = {
	SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC1_CONTROL1,
			      CS48L32_ISRC1_FSH_SHIFT,
			      CS48L32_ISRC1_FSH_MASK >> CS48L32_ISRC1_FSH_SHIFT,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text,
			      cs48l32_rate_val),
	SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC2_CONTROL1,
			      CS48L32_ISRC1_FSH_SHIFT,
			      CS48L32_ISRC1_FSH_MASK >> CS48L32_ISRC1_FSH_SHIFT,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text,
			      cs48l32_rate_val),
	SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC3_CONTROL1,
			      CS48L32_ISRC1_FSH_SHIFT,
			      CS48L32_ISRC1_FSH_MASK >> CS48L32_ISRC1_FSH_SHIFT,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text,
			      cs48l32_rate_val),
};

static const struct soc_enum cs48l32_isrc_fsl[] = {
	SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC1_CONTROL1,
			      CS48L32_ISRC1_FSL_SHIFT,
			      CS48L32_ISRC1_FSL_MASK >> CS48L32_ISRC1_FSL_SHIFT,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text,
			      cs48l32_rate_val),
	SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC2_CONTROL1,
			      CS48L32_ISRC1_FSL_SHIFT,
			      CS48L32_ISRC1_FSL_MASK >> CS48L32_ISRC1_FSL_SHIFT,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text,
			      cs48l32_rate_val),
	SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC3_CONTROL1,
			      CS48L32_ISRC1_FSL_SHIFT,
			      CS48L32_ISRC1_FSL_MASK >> CS48L32_ISRC1_FSL_SHIFT,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text,
			      cs48l32_rate_val),
};

static const struct soc_enum cs48l32_fx_rate =
	SOC_VALUE_ENUM_SINGLE(CS48L32_FX_SAMPLE_RATE,
			      CS48L32_FX_RATE_SHIFT,
			      CS48L32_FX_RATE_MASK >> CS48L32_FX_RATE_SHIFT,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text,
			      cs48l32_rate_val);

static const char * const cs48l32_lhpf_mode_text[] = {
	"Low-pass", "High-pass"
};

static const struct soc_enum cs48l32_lhpf_mode[] = {
	SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 0,
			ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text),
	SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 1,
			ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text),
	SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 2,
			ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text),
	SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 3,
			ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text),
};

static int cs48l32_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
				  struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
	__be32 *data = (__be32 *)ucontrol->value.bytes.data;
	s16 val = (s16)be32_to_cpu(*data);

	if (abs(val) > CS48L32_LHPF_MAX_COEFF) {
		dev_err(cs48l32_codec->core.dev, "Rejecting unstable LHPF coefficients\n");
		return -EINVAL;
	}

	return snd_soc_bytes_put(kcontrol, ucontrol);
}

static const char * const cs48l32_eq_mode_text[] = {
	"Low-pass", "High-pass",
};

static const struct soc_enum cs48l32_eq_mode[] = {
	SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 0,
			ARRAY_SIZE(cs48l32_eq_mode_text),
			cs48l32_eq_mode_text),
	SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 1,
			ARRAY_SIZE(cs48l32_eq_mode_text),
			cs48l32_eq_mode_text),
	SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 2,
			ARRAY_SIZE(cs48l32_eq_mode_text),
			cs48l32_eq_mode_text),
	SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 3,
			ARRAY_SIZE(cs48l32_eq_mode_text),
			cs48l32_eq_mode_text),
};

static int cs48l32_eq_mode_get(struct snd_kcontrol *kcontrol,
			       struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
	struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
	unsigned int item;

	item = snd_soc_enum_val_to_item(e, cs48l32_codec->eq_mode[e->shift_l]);
	ucontrol->value.enumerated.item[0] = item;

	return 0;
}

static int cs48l32_eq_mode_put(struct snd_kcontrol *kcontrol,
			       struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
	struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
	struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
	unsigned int *item = ucontrol->value.enumerated.item;
	unsigned int val;
	bool changed = false;

	if (item[0] >= e->items)
		return -EINVAL;

	val = snd_soc_enum_item_to_val(e, item[0]);

	snd_soc_dapm_mutex_lock(dapm);
	if (cs48l32_codec->eq_mode[e->shift_l] != val) {
		cs48l32_codec->eq_mode[e->shift_l] = val;
		changed = true;
	}
	snd_soc_dapm_mutex_unlock(dapm);

	return changed;
}

static int cs48l32_eq_coeff_info(struct snd_kcontrol *kcontrol,
				 struct snd_ctl_elem_info *uinfo)
{
	struct cs48l32_eq_control *ctl = (void *) kcontrol->private_value;

	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
	uinfo->count = 1;
	uinfo->value.integer.min = 0;
	uinfo->value.integer.max = ctl->max;

	return 0;
}

static int cs48l32_eq_coeff_get(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
	struct cs48l32_eq_control *params = (void *)kcontrol->private_value;
	__be16 *coeffs;
	unsigned int coeff_idx;
	int block_idx;

	block_idx = ((int) params->block_base - (int) CS48L32_EQ1_BAND1_COEFF1);
	block_idx /= (CS48L32_EQ2_BAND1_COEFF1 - CS48L32_EQ1_BAND1_COEFF1);

	coeffs = &cs48l32_codec->eq_coefficients[block_idx][0];
	coeff_idx = (params->reg - params->block_base) / 2;

	/* High __be16 is in [coeff_idx] and low __be16 in [coeff_idx + 1] */
	if (params->shift == 0)
		coeff_idx++;

	ucontrol->value.integer.value[0] = be16_to_cpu(coeffs[coeff_idx]);

	return 0;
}

static int cs48l32_eq_coeff_put(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
	struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
	struct cs48l32_eq_control *params = (void *)kcontrol->private_value;
	__be16 *coeffs;
	unsigned int coeff_idx;
	int block_idx;

	block_idx = ((int) params->block_base - (int) CS48L32_EQ1_BAND1_COEFF1);
	block_idx /= (CS48L32_EQ2_BAND1_COEFF1 - CS48L32_EQ1_BAND1_COEFF1);

	coeffs = &cs48l32_codec->eq_coefficients[block_idx][0];
	coeff_idx = (params->reg - params->block_base) / 2;

	/* Put high __be16 in [coeff_idx] and low __be16 in [coeff_idx + 1] */
	if (params->shift == 0)
		coeff_idx++;

	snd_soc_dapm_mutex_lock(dapm);
	coeffs[coeff_idx] = cpu_to_be16(ucontrol->value.integer.value[0]);
	snd_soc_dapm_mutex_unlock(dapm);

	return 0;
}

static const struct snd_kcontrol_new cs48l32_drc_activity_output_mux[] = {
	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
};

static const struct snd_kcontrol_new cs48l32_dsp_trigger_output_mux[] = {
	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
};

static int cs48l32_dsp_rate_get(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
	struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
	unsigned int cached_rate;
	const unsigned int rate_num = e->mask;
	int item;

	if (rate_num >= ARRAY_SIZE(cs48l32_codec->dsp_dma_rates))
		return -EINVAL;

	cached_rate = cs48l32_codec->dsp_dma_rates[rate_num];
	item = snd_soc_enum_val_to_item(e, cached_rate);
	ucontrol->value.enumerated.item[0] = item;

	return 0;
}

static int cs48l32_dsp_rate_put(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
	struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
	struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
	const unsigned int rate_num = e->mask;
	const unsigned int item = ucontrol->value.enumerated.item[0];
	unsigned int val;
	bool changed = false;

	if (item >= e->items)
		return -EINVAL;

	if (rate_num >= ARRAY_SIZE(cs48l32_codec->dsp_dma_rates))
		return -EINVAL;

	val = snd_soc_enum_item_to_val(e, item);

	snd_soc_dapm_mutex_lock(dapm);
	if (cs48l32_codec->dsp_dma_rates[rate_num] != val) {
		cs48l32_codec->dsp_dma_rates[rate_num] = val;
		changed = true;
	}
	snd_soc_dapm_mutex_unlock(dapm);

	return changed;
}

static const struct soc_enum cs48l32_dsp_rate_enum[] = {
	/* RX rates */
	SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
			      0,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text, cs48l32_rate_val),
	SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
			      1,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text, cs48l32_rate_val),
	SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
			      2,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text, cs48l32_rate_val),
	SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
			      3,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text, cs48l32_rate_val),
	SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
			      4,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text,  cs48l32_rate_val),
	SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
			      5,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text, cs48l32_rate_val),
	SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
			      6,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text, cs48l32_rate_val),
	SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
			      7,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text, cs48l32_rate_val),
	/* TX rates */
	SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
			      8,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text, cs48l32_rate_val),
	SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
			      9,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text, cs48l32_rate_val),
	SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
			      10,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text, cs48l32_rate_val),
	SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
			      11,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text, cs48l32_rate_val),
	SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
			      12,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text, cs48l32_rate_val),
	SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
			      13,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text, cs48l32_rate_val),
	SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
			      14,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text, cs48l32_rate_val),
	SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
			      15,
			      ARRAY_SIZE(cs48l32_rate_text),
			      cs48l32_rate_text, cs48l32_rate_val),
};

static int cs48l32_dsp_pre_run(struct wm_adsp *dsp)
{
	struct cs48l32_codec *cs48l32_codec = container_of(dsp, struct cs48l32_codec, dsp);
	unsigned int reg;
	const u8 *rate = cs48l32_codec->dsp_dma_rates;
	int i;

	reg = dsp->cs_dsp.base + CS48L32_HALO_SAMPLE_RATE_RX1;
	for (i = 0; i < CS48L32_DSP_N_RX_CHANNELS; ++i) {
		regmap_update_bits(dsp->cs_dsp.regmap, reg, CS48L32_HALO_DSP_RATE_MASK, *rate);
		reg += 8;
		rate++;
	}

	reg = dsp->cs_dsp.base + CS48L32_HALO_SAMPLE_RATE_TX1;
	for (i = 0; i < CS48L32_DSP_N_TX_CHANNELS; ++i) {
		regmap_update_bits(dsp->cs_dsp.regmap, reg, CS48L32_HALO_DSP_RATE_MASK, *rate);
		reg += 8;
		rate++;
	}

	usleep_range(300, 600);

	return 0;
}

static void cs48l32_dsp_memory_disable(struct cs48l32_codec *cs48l32_codec,
				       const struct cs48l32_dsp_power_regs *regs)
{
	struct regmap *regmap = cs48l32_codec->core.regmap;
	int i, j, ret;

	for (i = 0; i < regs->n_pwd; ++i) {
		ret = regmap_write(regmap, regs->pwd[i], 0);
		if (ret)
			goto err;
	}

	for (i = 0; i < regs->n_ext; ++i) {
		for (j = regs->ext[i].start; j <= regs->ext[i].end; j += 4) {
			ret = regmap_write(regmap, j, 0);
			if (ret)
				goto err;
		}
	}

	return;

err:
	dev_warn(cs48l32_codec->core.dev, "Failed to write SRAM enables (%d)\n", ret);
}

static int cs48l32_dsp_memory_enable(struct cs48l32_codec *cs48l32_codec,
				     const struct cs48l32_dsp_power_regs *regs)
{
	struct regmap *regmap = cs48l32_codec->core.regmap;
	int i, j, ret;

	/* disable power-off */
	for (i = 0; i < regs->n_ext; ++i) {
		for (j = regs->ext[i].start; j <= regs->ext[i].end; j += 4) {
			ret = regmap_write(regmap, j, 0x3);
			if (ret)
				goto err;
		}
	}

	/* power-up the banks in sequence */
	for (i = 0; i < regs->n_pwd; ++i) {
		ret = regmap_write(regmap, regs->pwd[i], 0x1);
		if (ret)
			goto err;

		udelay(1); /* allow bank to power-up */

		ret = regmap_write(regmap, regs->pwd[i], 0x3);
		if (ret)
			goto err;

		udelay(1); /* allow bank to power-up */
	}

	return 0;

err:
	dev_err(cs48l32_codec->core.dev, "Failed to write SRAM enables (%d)\n", ret);
	cs48l32_dsp_memory_disable(cs48l32_codec, regs);

	return ret;
}

static int cs48l32_dsp_freq_update(struct snd_soc_dapm_widget *w, unsigned int freq_reg,
				   unsigned int freqsel_reg)
{
	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
	struct regmap *regmap = cs48l32_codec->core.regmap;
	struct wm_adsp *dsp = &cs48l32_codec->dsp;
	int ret;
	unsigned int freq, freq_sel, freq_sts;

	if (!freq_reg)
		return -EINVAL;

	ret = regmap_read(regmap, freq_reg, &freq);
	if (ret) {
		dev_err(component->dev, "Failed to read #%x: %d\n", freq_reg, ret);
		return ret;
	}

	if (freqsel_reg) {
		freq_sts = (freq & CS48L32_SYSCLK_FREQ_STS_MASK) >> CS48L32_SYSCLK_FREQ_STS_SHIFT;

		ret = regmap_read(regmap, freqsel_reg, &freq_sel);
		if (ret) {
			dev_err(component->dev, "Failed to read #%x: %d\n", freqsel_reg, ret);
			return ret;
		}
		freq_sel = (freq_sel & CS48L32_SYSCLK_FREQ_MASK) >> CS48L32_SYSCLK_FREQ_SHIFT;

		if (freq_sts != freq_sel) {
			dev_err(component->dev, "SYSCLK FREQ (#%x) != FREQ STS (#%x)\n",
				freq_sel, freq_sts);
			return -ETIMEDOUT;
		}
	}

	freq &= CS48L32_DSP_CLK_FREQ_MASK;
	freq >>= CS48L32_DSP_CLK_FREQ_SHIFT;

	ret = regmap_write(dsp->cs_dsp.regmap,
			   dsp->cs_dsp.base + CS48L32_DSP_CLOCK_FREQ_OFFS, freq);
	if (ret) {
		dev_err(component->dev, "Failed to set HALO clock freq: %d\n", ret);
		return ret;
	}

	return 0;
}

static int cs48l32_dsp_freq_ev(struct snd_soc_dapm_widget *w,
			       struct snd_kcontrol *kcontrol, int event)
{
	switch (event) {
	case SND_SOC_DAPM_POST_PMU:
		return cs48l32_dsp_freq_update(w, CS48L32_SYSTEM_CLOCK2, CS48L32_SYSTEM_CLOCK1);
	default:
		return 0;
	}
}

static irqreturn_t cs48l32_irq(int irq, void *data)
{
	static const unsigned int eint1_regs[] = {
		CS48L32_IRQ1_EINT_9, CS48L32_IRQ1_MASK_9,
		CS48L32_IRQ1_EINT_7, CS48L32_IRQ1_MASK_7
	};
	u32 reg_vals[4];
	struct cs48l32_codec *cs48l32_codec = data;
	struct regmap *regmap = cs48l32_codec->core.regmap;
	irqreturn_t result = IRQ_NONE;
	unsigned int eint_pending;
	int i, ret;

	static_assert(ARRAY_SIZE(eint1_regs) == ARRAY_SIZE(reg_vals));

	ret = pm_runtime_resume_and_get(cs48l32_codec->core.dev);
	if (ret) {
		dev_warn(cs48l32_codec->core.dev, "irq could not get pm runtime: %d\n", ret);
		return IRQ_NONE;
	}

	ret = regmap_read(regmap, CS48L32_IRQ1_STATUS, &eint_pending);
	if (ret) {
		dev_warn(cs48l32_codec->core.dev, "Read IRQ1_STATUS failed: %d\n", ret);
		return IRQ_NONE;
	}
	if ((eint_pending & CS48L32_IRQ1_STS_MASK) == 0)
		goto out;

	ret = regmap_multi_reg_read(regmap, eint1_regs, reg_vals, ARRAY_SIZE(reg_vals));
	if (ret) {
		dev_warn(cs48l32_codec->core.dev, "Read IRQ regs failed: %d\n", ret);
		return IRQ_NONE;
	}

	for (i = 0; i < ARRAY_SIZE(reg_vals); i += 2) {
		reg_vals[i] &= ~reg_vals[i + 1];
		regmap_write(regmap, eint1_regs[i], reg_vals[i]);
	}

	if (reg_vals[0] & CS48L32_DSP1_IRQ0_EINT1_MASK)
		wm_adsp_compr_handle_irq(&cs48l32_codec->dsp);

	if (reg_vals[2] & CS48L32_DSP1_MPU_ERR_EINT1_MASK) {
		dev_warn(cs48l32_codec->core.dev, "MPU err IRQ\n");
		wm_halo_bus_error(irq, &cs48l32_codec->dsp);
	}

	if (reg_vals[2] & CS48L32_DSP1_WDT_EXPIRE_EINT1_MASK) {
		dev_warn(cs48l32_codec->core.dev, "WDT expire IRQ\n");
		wm_halo_wdt_expire(irq, &cs48l32_codec->dsp);
	}

	result = IRQ_HANDLED;

out:
	pm_runtime_put_autosuspend(cs48l32_codec->core.dev);

	return result;
}

static int cs48l32_get_dspclk_setting(struct cs48l32_codec *cs48l32_codec, unsigned int freq,
				      int src, unsigned int *val)
{
	freq /= 15625; /* convert to 1/64ths of 1MHz */
	*val |= freq << CS48L32_DSP_CLK_FREQ_SHIFT;

	return 0;
}

static int cs48l32_get_sysclk_setting(unsigned int freq)
{
	switch (freq) {
	case 0:
	case 5644800:
	case 6144000:
		return CS48L32_SYSCLK_RATE_6MHZ;
	case 11289600:
	case 12288000:
		return CS48L32_SYSCLK_RATE_12MHZ << CS48L32_SYSCLK_FREQ_SHIFT;
	case 22579200:
	case 24576000:
		return CS48L32_SYSCLK_RATE_24MHZ << CS48L32_SYSCLK_FREQ_SHIFT;
	case 45158400:
	case 49152000:
		return CS48L32_SYSCLK_RATE_49MHZ << CS48L32_SYSCLK_FREQ_SHIFT;
	case 90316800:
	case 98304000:
		return CS48L32_SYSCLK_RATE_98MHZ << CS48L32_SYSCLK_FREQ_SHIFT;
	default:
		return -EINVAL;
	}
}

static int cs48l32_set_pdm_fllclk(struct snd_soc_component *component, int source)
{
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
	struct regmap *regmap = cs48l32_codec->core.regmap;
	unsigned int val;

	switch (source) {
	case CS48L32_PDMCLK_SRC_IN1_PDMCLK:
	case CS48L32_PDMCLK_SRC_IN2_PDMCLK:
	case CS48L32_PDMCLK_SRC_IN3_PDMCLK:
	case CS48L32_PDMCLK_SRC_IN4_PDMCLK:
	case CS48L32_PDMCLK_SRC_AUXPDM1_CLK:
	case CS48L32_PDMCLK_SRC_AUXPDM2_CLK:
		val = source << CS48L32_PDM_FLLCLK_SRC_SHIFT;
		break;
	default:
		dev_err(cs48l32_codec->core.dev, "Invalid PDM FLLCLK src %d\n", source);
		return -EINVAL;
	}

	return regmap_update_bits(regmap, CS48L32_INPUT_CONTROL2,
				  CS48L32_PDM_FLLCLK_SRC_MASK, val);
}

static int cs48l32_set_sysclk(struct snd_soc_component *component, int clk_id, int source,
			      unsigned int freq, int dir)
{
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
	struct regmap *regmap = cs48l32_codec->core.regmap;
	char *name;
	unsigned int reg;
	unsigned int mask = CS48L32_SYSCLK_SRC_MASK;
	unsigned int val = source << CS48L32_SYSCLK_SRC_SHIFT;
	int clk_freq_sel, *clk;

	switch (clk_id) {
	case CS48L32_CLK_SYSCLK_1:
		name = "SYSCLK";
		reg = CS48L32_SYSTEM_CLOCK1;
		clk = &cs48l32_codec->sysclk;
		clk_freq_sel = cs48l32_get_sysclk_setting(freq);
		mask |= CS48L32_SYSCLK_FREQ_MASK | CS48L32_SYSCLK_FRAC_MASK;
		break;
	case CS48L32_CLK_DSPCLK:
		name = "DSPCLK";
		reg = CS48L32_DSP_CLOCK1;
		clk = &cs48l32_codec->dspclk;
		clk_freq_sel = cs48l32_get_dspclk_setting(cs48l32_codec, freq, source, &val);
		mask |= CS48L32_DSP_CLK_FREQ_MASK;
		break;
	case CS48L32_CLK_PDM_FLLCLK:
		return cs48l32_set_pdm_fllclk(component, source);
	default:
		return -EINVAL;
	}

	if (clk_freq_sel < 0) {
		dev_err(cs48l32_codec->core.dev, "Failed to get %s setting for %dHZ\n", name, freq);
		return clk_freq_sel;
	}

	*clk = freq;

	if (freq == 0) {
		dev_dbg(cs48l32_codec->core.dev, "%s cleared\n", name);
		return 0;
	}

	val |= clk_freq_sel;

	if (freq % 6144000)
		val |= CS48L32_SYSCLK_FRAC_MASK;

	dev_dbg(cs48l32_codec->core.dev, "%s set to %uHz", name, freq);

	return regmap_update_bits(regmap, reg, mask, val);
}

static int cs48l32_is_enabled_fll(struct cs48l32_fll *fll, int base)
{
	struct regmap *regmap = fll->codec->core.regmap;
	unsigned int reg;
	int ret;

	ret = regmap_read(regmap, base + CS48L32_FLL_CONTROL1_OFFS, &reg);
	if (ret != 0) {
		cs48l32_fll_err(fll, "Failed to read current state: %d\n", ret);
		return ret;
	}

	return reg & CS48L32_FLL_EN_MASK;
}

static int cs48l32_wait_for_fll(struct cs48l32_fll *fll, bool requested)
{
	struct regmap *regmap = fll->codec->core.regmap;
	unsigned int val = 0;
	int i;

	cs48l32_fll_dbg(fll, "Waiting for FLL...\n");

	for (i = 0; i < 30; i++) {
		regmap_read(regmap, fll->sts_addr, &val);
		if (!!(val & fll->sts_mask) == requested)
			return 0;

		switch (i) {
		case 0 ... 5:
			usleep_range(75, 125);
			break;
		case 6 ... 20:
			usleep_range(750, 1250);
			break;
		default:
			fsleep(20000);
			break;
		}
	}

	cs48l32_fll_warn(fll, "Timed out waiting for %s\n", requested ? "lock" : "unlock");

	return -ETIMEDOUT;
}

static int cs48l32_fllhj_disable(struct cs48l32_fll *fll)
{
	struct cs48l32 *cs48l32 = &fll->codec->core;
	bool change;

	cs48l32_fll_dbg(fll, "Disabling FLL\n");

	/*
	 * Disable lockdet, but don't set ctrl_upd update bit. This allows the
	 * lock status bit to clear as normal, but should the FLL be enabled
	 * again due to a control clock being required, the lock won't re-assert
	 * as the FLL config registers are automatically applied when the FLL
	 * enables.
	 */
	regmap_set_bits(cs48l32->regmap,
			fll->base + CS48L32_FLL_CONTROL1_OFFS,
			CS48L32_FLL_HOLD_MASK);
	regmap_clear_bits(cs48l32->regmap,
			  fll->base + CS48L32_FLL_CONTROL2_OFFS,
			  CS48L32_FLL_LOCKDET_MASK);
	regmap_set_bits(cs48l32->regmap,
			fll->base + CS48L32_FLL_CONTROL5_OFFS,
			CS48L32_FLL_FRC_INTEG_UPD_MASK);
	regmap_update_bits_check(cs48l32->regmap,
				 fll->base + CS48L32_FLL_CONTROL1_OFFS,
				 CS48L32_FLL_EN_MASK,
				 0,
				 &change);

	cs48l32_wait_for_fll(fll, false);

	/*
	 * ctrl_up gates the writes to all the fll's registers, setting it to 0
	 * here ensures that after a runtime suspend/resume cycle when one
	 * enables the fll then ctrl_up is the last bit that is configured
	 * by the fll enable code rather than the cache sync operation which
	 * would have updated it much earlier before writing out all fll
	 * registers
	 */
	regmap_clear_bits(cs48l32->regmap,
			  fll->base + CS48L32_FLL_CONTROL1_OFFS,
			  CS48L32_FLL_CTRL_UPD_MASK);

	if (change)
		pm_runtime_put_autosuspend(cs48l32->dev);

	return 0;
}

static int cs48l32_fllhj_apply(struct cs48l32_fll *fll, int fin)
{
	struct regmap *regmap = fll->codec->core.regmap;
	int refdiv, fref, fout, lockdet_thr, fbdiv, fllgcd;
	bool frac = false;
	unsigned int fll_n, min_n, max_n, ratio, theta, lambda, hp;
	unsigned int gains, num;

	cs48l32_fll_dbg(fll, "fin=%d, fout=%d\n", fin, fll->fout);

	for (refdiv = 0; refdiv < 4; refdiv++) {
		if ((fin / (1 << refdiv)) <= CS48L32_FLLHJ_MAX_THRESH)
			break;
	}

	fref = fin / (1 << refdiv);
	fout = fll->fout;
	frac = fout % fref;

	/*
	 * Use simple heuristic approach to find a configuration that
	 * should work for most input clocks.
	 */
	if (fref < CS48L32_FLLHJ_LOW_THRESH) {
		lockdet_thr = 2;
		gains = CS48L32_FLLHJ_LOW_GAINS;

		if (frac)
			fbdiv = 256;
		else
			fbdiv = 4;
	} else if (fref < CS48L32_FLLHJ_MID_THRESH) {
		lockdet_thr = 8;
		gains = CS48L32_FLLHJ_MID_GAINS;
		fbdiv = (frac) ? 16 : 2;
	} else {
		lockdet_thr = 8;
		gains = CS48L32_FLLHJ_HIGH_GAINS;
		fbdiv = 1;
	}
	/* Use high performance mode for fractional configurations. */
	if (frac) {
		hp = 3;
		min_n = CS48L32_FLLHJ_FRAC_MIN_N;
		max_n = CS48L32_FLLHJ_FRAC_MAX_N;
	} else {
		if (fref < CS48L32_FLLHJ_LP_INT_MODE_THRESH)
			hp = 0;
		else
			hp = 1;

		min_n = CS48L32_FLLHJ_INT_MIN_N;
		max_n = CS48L32_FLLHJ_INT_MAX_N;
	}

	ratio = fout / fref;

	cs48l32_fll_dbg(fll, "refdiv=%d, fref=%d, frac:%d\n", refdiv, fref, frac);

	while (ratio / fbdiv < min_n) {
		fbdiv /= 2;
		if (fbdiv < min_n) {
			cs48l32_fll_err(fll, "FBDIV (%u) < minimum N (%u)\n", fbdiv, min_n);
			return -EINVAL;
		}
	}
	while (frac && (ratio / fbdiv > max_n)) {
		fbdiv *= 2;
		if (fbdiv >= 1024) {
			cs48l32_fll_err(fll, "FBDIV (%u) >= 1024\n", fbdiv);
			return -EINVAL;
		}
	}

	cs48l32_fll_dbg(fll, "lockdet=%d, hp=#%x, fbdiv:%d\n", lockdet_thr, hp, fbdiv);

	/* Calculate N.K values */
	fllgcd = gcd(fout, fbdiv * fref);
	num = fout / fllgcd;
	lambda = (fref * fbdiv) / fllgcd;
	fll_n = num / lambda;
	theta = num % lambda;

	cs48l32_fll_dbg(fll, "fll_n=%d, gcd=%d, theta=%d, lambda=%d\n",
			fll_n, fllgcd, theta, lambda);

	/* Some sanity checks before any registers are written. */
	if (fll_n < min_n || fll_n > max_n) {
		cs48l32_fll_err(fll, "N not in valid %s mode range %d-%d: %d\n",
				frac ? "fractional" : "integer", min_n, max_n, fll_n);
		return -EINVAL;
	}
	if (fbdiv < 1 || (frac && fbdiv >= 1024) || (!frac && fbdiv >= 256)) {
		cs48l32_fll_err(fll, "Invalid fbdiv for %s mode (%u)\n",
				frac ? "fractional" : "integer", fbdiv);
		return -EINVAL;
	}

	/* clear the ctrl_upd bit to guarantee we write to it later. */
	regmap_update_bits(regmap,
			   fll->base + CS48L32_FLL_CONTROL2_OFFS,
			   CS48L32_FLL_LOCKDET_THR_MASK |
			   CS48L32_FLL_PHASEDET_MASK |
			   CS48L32_FLL_REFCLK_DIV_MASK |
			   CS48L32_FLL_N_MASK |
			   CS48L32_FLL_CTRL_UPD_MASK,
			   (lockdet_thr << CS48L32_FLL_LOCKDET_THR_SHIFT) |
			   (1 << CS48L32_FLL_PHASEDET_SHIFT) |
			   (refdiv << CS48L32_FLL_REFCLK_DIV_SHIFT) |
			   (fll_n << CS48L32_FLL_N_SHIFT));

	regmap_update_bits(regmap,
			   fll->base + CS48L32_FLL_CONTROL3_OFFS,
			   CS48L32_FLL_LAMBDA_MASK |
			   CS48L32_FLL_THETA_MASK,
			   (lambda << CS48L32_FLL_LAMBDA_SHIFT) |
			   (theta << CS48L32_FLL_THETA_SHIFT));

	regmap_update_bits(regmap,
			   fll->base + CS48L32_FLL_CONTROL4_OFFS,
			   (0xffff << CS48L32_FLL_FD_GAIN_COARSE_SHIFT) |
			   CS48L32_FLL_HP_MASK |
			   CS48L32_FLL_FB_DIV_MASK,
			   (gains << CS48L32_FLL_FD_GAIN_COARSE_SHIFT) |
			   (hp << CS48L32_FLL_HP_SHIFT) |
			   (fbdiv << CS48L32_FLL_FB_DIV_SHIFT));

	return 0;
}

static int cs48l32_fllhj_enable(struct cs48l32_fll *fll)
{
	struct cs48l32 *cs48l32 = &fll->codec->core;
	int already_enabled = cs48l32_is_enabled_fll(fll, fll->base);
	int ret;

	if (already_enabled < 0)
		return already_enabled;

	if (!already_enabled)
		pm_runtime_get_sync(cs48l32->dev);

	cs48l32_fll_dbg(fll, "Enabling FLL, initially %s\n",
			str_enabled_disabled(already_enabled));

	/* FLLn_HOLD must be set before configuring any registers */
	regmap_set_bits(cs48l32->regmap,
			fll->base + CS48L32_FLL_CONTROL1_OFFS,
			CS48L32_FLL_HOLD_MASK);

	/* Apply refclk */
	ret = cs48l32_fllhj_apply(fll, fll->ref_freq);
	if (ret) {
		cs48l32_fll_err(fll, "Failed to set FLL: %d\n", ret);
		goto out;
	}
	regmap_update_bits(cs48l32->regmap,
			   fll->base + CS48L32_FLL_CONTROL2_OFFS,
			   CS48L32_FLL_REFCLK_SRC_MASK,
			   fll->ref_src << CS48L32_FLL_REFCLK_SRC_SHIFT);

	regmap_set_bits(cs48l32->regmap,
			fll->base + CS48L32_FLL_CONTROL1_OFFS,
			CS48L32_FLL_EN_MASK);

out:
	regmap_set_bits(cs48l32->regmap,
			fll->base + CS48L32_FLL_CONTROL2_OFFS,
			CS48L32_FLL_LOCKDET_MASK);

	regmap_set_bits(cs48l32->regmap,
			fll->base + CS48L32_FLL_CONTROL1_OFFS,
			CS48L32_FLL_CTRL_UPD_MASK);

	/* Release the hold so that flln locks to external frequency */
	regmap_clear_bits(cs48l32->regmap,
			  fll->base + CS48L32_FLL_CONTROL1_OFFS,
			  CS48L32_FLL_HOLD_MASK);

	if (!already_enabled)
		cs48l32_wait_for_fll(fll, true);

	return 0;
}

static int cs48l32_fllhj_validate(struct cs48l32_fll *fll,
				  unsigned int ref_in,
				  unsigned int fout)
{
	if (fout && !ref_in) {
		cs48l32_fll_err(fll, "fllout set without valid input clk\n");
		return -EINVAL;
	}

	if (fll->fout && fout != fll->fout) {
		cs48l32_fll_err(fll, "Can't change output on active FLL\n");
		return -EINVAL;
	}

	if (ref_in / CS48L32_FLL_MAX_REFDIV > CS48L32_FLLHJ_MAX_THRESH) {
		cs48l32_fll_err(fll, "Can't scale %dMHz to <=13MHz\n", ref_in);
		return -EINVAL;
	}

	if (fout > CS48L32_FLL_MAX_FOUT) {
		cs48l32_fll_err(fll, "Fout=%dMHz exceeds maximum %dMHz\n",
				fout, CS48L32_FLL_MAX_FOUT);
		return -EINVAL;
	}

	return 0;
}

static int cs48l32_fllhj_set_refclk(struct cs48l32_fll *fll, int source,
				    unsigned int fin, unsigned int fout)
{
	int ret = 0;

	if (fll->ref_src == source && fll->ref_freq == fin && fll->fout == fout)
		return 0;

	if (fin && fout && cs48l32_fllhj_validate(fll, fin, fout))
		return -EINVAL;

	fll->ref_src = source;
	fll->ref_freq = fin;
	fll->fout = fout;

	if (fout)
		ret = cs48l32_fllhj_enable(fll);
	else
		cs48l32_fllhj_disable(fll);

	return ret;
}

static int cs48l32_init_fll(struct cs48l32_fll *fll)
{
	fll->ref_src = CS48L32_FLL_SRC_NONE;

	return 0;
}

static int cs48l32_set_fll(struct snd_soc_component *component, int fll_id,
			   int source, unsigned int fref, unsigned int fout)
{
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);

	switch (fll_id) {
	case CS48L32_FLL1_REFCLK:
		break;
	default:
		return -EINVAL;
	}

	return cs48l32_fllhj_set_refclk(&cs48l32_codec->fll, source, fref, fout);
}

static int cs48l32_asp_dai_probe(struct snd_soc_dai *dai)
{
	struct snd_soc_component *component = dai->component;
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
	struct regmap *regmap = cs48l32_codec->core.regmap;
	unsigned int pin_reg, last_pin_reg, hiz_reg;

	switch (dai->id) {
	case 1:
		pin_reg = CS48L32_GPIO3_CTRL1;
		hiz_reg = CS48L32_ASP1_CONTROL3;
		break;
	case 2:
		pin_reg = CS48L32_GPIO7_CTRL1;
		hiz_reg = CS48L32_ASP2_CONTROL3;
		break;
	default:
		return -EINVAL;
	}

	for (last_pin_reg = pin_reg + 12; pin_reg <= last_pin_reg; ++pin_reg)
		regmap_clear_bits(regmap, pin_reg, CS48L32_GPIOX_CTRL1_FN_MASK);

	/* DOUT high-impendance when not transmitting */
	regmap_set_bits(regmap, hiz_reg, CS48L32_ASP_DOUT_HIZ_MASK);

	return 0;
}

static int cs48l32_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
	struct snd_soc_component *component = dai->component;
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
	struct regmap *regmap = cs48l32_codec->core.regmap;
	unsigned int val = 0U;
	unsigned int base = dai->driver->base;
	unsigned int mask = CS48L32_ASP_FMT_MASK | CS48L32_ASP_BCLK_INV_MASK |
			    CS48L32_ASP_BCLK_MSTR_MASK |
			    CS48L32_ASP_FSYNC_INV_MASK |
			    CS48L32_ASP_FSYNC_MSTR_MASK;

	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
	case SND_SOC_DAIFMT_DSP_A:
		val |= (CS48L32_ASP_FMT_DSP_MODE_A << CS48L32_ASP_FMT_SHIFT);
		break;
	case SND_SOC_DAIFMT_DSP_B:
		if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_BP_FP) {
			cs48l32_asp_err(dai, "DSP_B cannot be clock consumer\n");
			return -EINVAL;
		}
		val |= (CS48L32_ASP_FMT_DSP_MODE_B << CS48L32_ASP_FMT_SHIFT);
		break;
	case SND_SOC_DAIFMT_I2S:
		val |= (CS48L32_ASP_FMT_I2S_MODE << CS48L32_ASP_FMT_SHIFT);
		break;
	case SND_SOC_DAIFMT_LEFT_J:
		if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_BP_FP) {
			cs48l32_asp_err(dai, "LEFT_J cannot be clock consumer\n");
			return -EINVAL;
		}
		val |= (CS48L32_ASP_FMT_LEFT_JUSTIFIED_MODE << CS48L32_ASP_FMT_SHIFT);
		break;
	default:
		cs48l32_asp_err(dai, "Unsupported DAI format %d\n",
				fmt & SND_SOC_DAIFMT_FORMAT_MASK);
		return -EINVAL;
	}

	switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
	case SND_SOC_DAIFMT_BC_FC:
		break;
	case SND_SOC_DAIFMT_BC_FP:
		val |= CS48L32_ASP_FSYNC_MSTR_MASK;
		break;
	case SND_SOC_DAIFMT_BP_FC:
		val |= CS48L32_ASP_BCLK_MSTR_MASK;
		break;
	case SND_SOC_DAIFMT_BP_FP:
		val |= CS48L32_ASP_BCLK_MSTR_MASK;
		val |= CS48L32_ASP_FSYNC_MSTR_MASK;
		break;
	default:
		cs48l32_asp_err(dai, "Unsupported clock direction %d\n",
				fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK);
		return -EINVAL;
	}

	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
	case SND_SOC_DAIFMT_NB_NF:
		break;
	case SND_SOC_DAIFMT_IB_IF:
		val |= CS48L32_ASP_BCLK_INV_MASK;
		val |= CS48L32_ASP_FSYNC_INV_MASK;
		break;
	case SND_SOC_DAIFMT_IB_NF:
		val |= CS48L32_ASP_BCLK_INV_MASK;
		break;
	case SND_SOC_DAIFMT_NB_IF:
		val |= CS48L32_ASP_FSYNC_INV_MASK;
		break;
	default:
		return -EINVAL;
	}

	regmap_update_bits(regmap, base + CS48L32_ASP_CONTROL2, mask, val);

	return 0;
}

static const struct {
	u32 freq;
	u32 id;
} cs48l32_sclk_rates[] = {
	{ 128000,   12 },
	{ 176400,   13 },
	{ 192000,   14 },
	{ 256000,   15 },
	{ 352800,   16 },
	{ 384000,   17 },
	{ 512000,   18 },
	{ 705600,   19 },
	{ 768000,   21 },
	{ 1024000,  23 },
	{ 1411200,  25 },
	{ 1536000,  27 },
	{ 2048000,  29 },
	{ 2822400,  31 },
	{ 3072000,  33 },
	{ 4096000,  36 },
	{ 5644800,  38 },
	{ 6144000,  40 },
	{ 8192000,  47 },
	{ 11289600, 49 },
	{ 12288000, 51 },
	{ 22579200, 57 },
	{ 24576000, 59 },
};

#define CS48L32_48K_RATE_MASK	0x0e00fe
#define CS48L32_44K1_RATE_MASK	0x00fe00
#define CS48L32_RATE_MASK	(CS48L32_48K_RATE_MASK | CS48L32_44K1_RATE_MASK)

static const unsigned int cs48l32_sr_vals[] = {
	0,
	12000,  /* CS48L32_48K_RATE_MASK */
	24000,  /* CS48L32_48K_RATE_MASK */
	48000,  /* CS48L32_48K_RATE_MASK */
	96000,  /* CS48L32_48K_RATE_MASK */
	192000, /* CS48L32_48K_RATE_MASK */
	384000, /* CS48L32_48K_RATE_MASK */
	768000, /* CS48L32_48K_RATE_MASK */
	0,
	11025,  /* CS48L32_44K1_RATE_MASK */
	22050,  /* CS48L32_44K1_RATE_MASK */
	44100,  /* CS48L32_44K1_RATE_MASK */
	88200,  /* CS48L32_44K1_RATE_MASK */
	176400, /* CS48L32_44K1_RATE_MASK */
	352800, /* CS48L32_44K1_RATE_MASK */
	705600, /* CS48L32_44K1_RATE_MASK */
	0,
	8000,   /* CS48L32_48K_RATE_MASK */
	16000,  /* CS48L32_48K_RATE_MASK */
	32000,  /* CS48L32_48K_RATE_MASK */
};

static const struct snd_pcm_hw_constraint_list cs48l32_constraint = {
	.count	= ARRAY_SIZE(cs48l32_sr_vals),
	.list	= cs48l32_sr_vals,
};

static int cs48l32_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
	struct snd_soc_component *component = dai->component;
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
	struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[dai->id - 1];
	unsigned int base_rate;

	if (!substream->runtime)
		return 0;

	switch (dai_priv->clk) {
	case CS48L32_CLK_SYSCLK_1:
	case CS48L32_CLK_SYSCLK_2:
	case CS48L32_CLK_SYSCLK_3:
	case CS48L32_CLK_SYSCLK_4:
		base_rate = cs48l32_codec->sysclk;
		break;
	default:
		return 0;
	}

	if (base_rate == 0)
		dai_priv->constraint.mask = CS48L32_RATE_MASK;
	else if (base_rate % 4000)
		dai_priv->constraint.mask = CS48L32_44K1_RATE_MASK;
	else
		dai_priv->constraint.mask = CS48L32_48K_RATE_MASK;

	return snd_pcm_hw_constraint_list(substream->runtime, 0,
					  SNDRV_PCM_HW_PARAM_RATE,
					  &dai_priv->constraint);
}

static int cs48l32_hw_params_rate(struct snd_pcm_substream *substream,
				  struct snd_pcm_hw_params *params,
				  struct snd_soc_dai *dai)
{
	struct snd_soc_component *component = dai->component;
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
	struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[dai->id - 1];
	unsigned int sr_val, sr_reg, rate;

	rate = params_rate(params);
	for (sr_val = 0; sr_val < ARRAY_SIZE(cs48l32_sr_vals); sr_val++)
		if (cs48l32_sr_vals[sr_val] == rate)
			break;

	if (sr_val == ARRAY_SIZE(cs48l32_sr_vals)) {
		cs48l32_asp_err(dai, "Unsupported sample rate %dHz\n", rate);
		return -EINVAL;
	}

	switch (dai_priv->clk) {
	case CS48L32_CLK_SYSCLK_1:
		sr_reg = CS48L32_SAMPLE_RATE1;
		break;
	case CS48L32_CLK_SYSCLK_2:
		sr_reg = CS48L32_SAMPLE_RATE2;
		break;
	case CS48L32_CLK_SYSCLK_3:
		sr_reg = CS48L32_SAMPLE_RATE3;
		break;
	case CS48L32_CLK_SYSCLK_4:
		sr_reg = CS48L32_SAMPLE_RATE4;
		break;
	default:
		return -EINVAL;
	}

	snd_soc_component_update_bits(component, sr_reg, CS48L32_SAMPLE_RATE_1_MASK, sr_val);

	return 0;
}

static bool cs48l32_asp_cfg_changed(struct snd_soc_component *component,
				    unsigned int base, unsigned int sclk,
				    unsigned int slotws, unsigned int dataw)
{
	unsigned int val;

	val = snd_soc_component_read(component, base + CS48L32_ASP_CONTROL1);
	if (sclk != (val & CS48L32_ASP_BCLK_FREQ_MASK))
		return true;

	val = snd_soc_component_read(component, base + CS48L32_ASP_CONTROL2);
	if (slotws != (val & (CS48L32_ASP_RX_WIDTH_MASK | CS48L32_ASP_TX_WIDTH_MASK)))
		return true;

	val = snd_soc_component_read(component, base + CS48L32_ASP_DATA_CONTROL1);
	if (dataw != (val & (CS48L32_ASP_TX_WL_MASK)))
		return true;

	val = snd_soc_component_read(component, base + CS48L32_ASP_DATA_CONTROL5);
	if (dataw != (val & (CS48L32_ASP_RX_WL_MASK)))
		return true;

	return false;
}

static int cs48l32_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;
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
	struct regmap *regmap = cs48l32_codec->core.regmap;
	int base = dai->driver->base;
	int dai_id = dai->id - 1;
	unsigned int rate = params_rate(params);
	unsigned int dataw = snd_pcm_format_width(params_format(params));
	unsigned int asp_state = 0;
	int sclk, sclk_target;
	unsigned int slotw, n_slots, n_slots_multiple, val;
	int i, ret;

	cs48l32_asp_dbg(dai, "hwparams in: ch:%u dataw:%u rate:%u\n",
			params_channels(params), dataw, rate);
	/*
	 * The following calculations hold only under the assumption that
	 * symmetric_[rates|channels|samplebits] are set to 1
	 */
	if (cs48l32_codec->tdm_slots[dai_id]) {
		n_slots = cs48l32_codec->tdm_slots[dai_id];
		slotw = cs48l32_codec->tdm_width[dai_id];
	} else {
		n_slots = params_channels(params);
		slotw = dataw;
	}

	val = snd_soc_component_read(component, base + CS48L32_ASP_CONTROL2);
	val = (val & CS48L32_ASP_FMT_MASK) >> CS48L32_ASP_FMT_SHIFT;
	if (val == CS48L32_ASP_FMT_I2S_MODE)
		n_slots_multiple = 2;
	else
		n_slots_multiple = 1;

	sclk_target = snd_soc_tdm_params_to_bclk(params, slotw, n_slots, n_slots_multiple);
	if (sclk_target < 0) {
		cs48l32_asp_err(dai, "Invalid parameters\n");
		return sclk_target;
	}

	for (i = 0; i < ARRAY_SIZE(cs48l32_sclk_rates); i++) {
		if ((cs48l32_sclk_rates[i].freq >= sclk_target) &&
		    (cs48l32_sclk_rates[i].freq % rate == 0)) {
			sclk = cs48l32_sclk_rates[i].id;
			break;
		}
	}
	if (i == ARRAY_SIZE(cs48l32_sclk_rates)) {
		cs48l32_asp_err(dai, "Unsupported sample rate %dHz\n", rate);
		return -EINVAL;
	}

	cs48l32_asp_dbg(dai, "hwparams out: n_slots:%u dataw:%u slotw:%u bclk:%u bclkid:%u\n",
			n_slots, dataw, slotw, sclk_target, sclk);

	slotw = (slotw << CS48L32_ASP_TX_WIDTH_SHIFT) |
		(slotw << CS48L32_ASP_RX_WIDTH_SHIFT);

	if (!cs48l32_asp_cfg_changed(component, base, sclk, slotw, dataw))
		return cs48l32_hw_params_rate(substream, params, dai);

	/* ASP must be disabled while changing configuration */
	asp_state = snd_soc_component_read(component, base + CS48L32_ASP_ENABLES1);
	regmap_clear_bits(regmap, base + CS48L32_ASP_ENABLES1, 0xff00ff);

	ret = cs48l32_hw_params_rate(substream, params, dai);
	if (ret != 0)
		goto restore_asp;

	regmap_update_bits_async(regmap,
				 base + CS48L32_ASP_CONTROL1,
				 CS48L32_ASP_BCLK_FREQ_MASK,
				 sclk);
	regmap_update_bits_async(regmap,
				 base + CS48L32_ASP_CONTROL2,
				 CS48L32_ASP_RX_WIDTH_MASK | CS48L32_ASP_TX_WIDTH_MASK,
				 slotw);
	regmap_update_bits_async(regmap,
				 base + CS48L32_ASP_DATA_CONTROL1,
				 CS48L32_ASP_TX_WL_MASK,
				 dataw);
	regmap_update_bits(regmap,
			   base + CS48L32_ASP_DATA_CONTROL5,
			   CS48L32_ASP_RX_WL_MASK,
			   dataw);

restore_asp:
	/* Restore ASP TX/RX enable state */
	regmap_update_bits(regmap,
			   base + CS48L32_ASP_ENABLES1,
			   0xff00ff,
			   asp_state);
	return ret;
}

static const char *cs48l32_dai_clk_str(int clk_id)
{
	switch (clk_id) {
	case CS48L32_CLK_SYSCLK_1:
	case CS48L32_CLK_SYSCLK_2:
	case CS48L32_CLK_SYSCLK_3:
	case CS48L32_CLK_SYSCLK_4:
		return "SYSCLK";
	default:
		return "Unknown clock";
	}
}

static int cs48l32_dai_set_sysclk(struct snd_soc_dai *dai,
				  int clk_id, unsigned int freq, int dir)
{
	struct snd_soc_component *component = dai->component;
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
	struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[dai->id - 1];
	unsigned int base = dai->driver->base;
	unsigned int current_asp_rate, target_asp_rate;
	bool change_rate_domain = false;
	int ret;

	if (clk_id == dai_priv->clk)
		return 0;

	if (snd_soc_dai_active(dai)) {
		cs48l32_asp_err(dai, "Can't change clock on active DAI\n");
		return -EBUSY;
	}

	switch (clk_id) {
	case CS48L32_CLK_SYSCLK_1:
		target_asp_rate = 0U << CS48L32_ASP_RATE_SHIFT;
		break;
	case CS48L32_CLK_SYSCLK_2:
		target_asp_rate = 1U << CS48L32_ASP_RATE_SHIFT;
		break;
	case CS48L32_CLK_SYSCLK_3:
		target_asp_rate = 2U << CS48L32_ASP_RATE_SHIFT;
		break;
	case CS48L32_CLK_SYSCLK_4:
		target_asp_rate = 3U << CS48L32_ASP_RATE_SHIFT;
		break;
	default:
		return -EINVAL;
	}

	dai_priv->clk = clk_id;
	cs48l32_asp_dbg(dai, "Setting to %s\n", cs48l32_dai_clk_str(clk_id));

	if (base) {
		ret = regmap_read(cs48l32_codec->core.regmap,
				  base + CS48L32_ASP_CONTROL1,
				  &current_asp_rate);
		if (ret != 0) {
			cs48l32_asp_err(dai, "Failed to check rate: %d\n", ret);
			return ret;
		}

		if ((current_asp_rate & CS48L32_ASP_RATE_MASK) !=
		    (target_asp_rate & CS48L32_ASP_RATE_MASK)) {
			change_rate_domain = true;

			mutex_lock(&cs48l32_codec->rate_lock);
			/* Guard the rate change with SYSCLK cycles */
			cs48l32_spin_sysclk(cs48l32_codec);
		}

		snd_soc_component_update_bits(component, base + CS48L32_ASP_CONTROL1,
					      CS48L32_ASP_RATE_MASK, target_asp_rate);

		if (change_rate_domain) {
			cs48l32_spin_sysclk(cs48l32_codec);
			mutex_unlock(&cs48l32_codec->rate_lock);
		}
	}

	return 0;
}

static void cs48l32_set_channels_to_mask(struct snd_soc_dai *dai,
					 unsigned int base,
					 int channels, unsigned int mask)
{
	struct snd_soc_component *component = dai->component;
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
	struct regmap *regmap = cs48l32_codec->core.regmap;
	int slot, i, j = 0, shift;
	unsigned int frame_ctls[2] = {0, 0};

	for (i = 0; i < channels; ++i) {
		slot = ffs(mask) - 1;
		if (slot < 0)
			return;

		if (i - (j * 4) >= 4) {
			++j;
			if (j >= 2)
				break;
		}

		shift = (8 * (i - j * 4));

		frame_ctls[j] |= slot << shift;

		mask &= ~(1 << slot); /* ? mask ^= 1 << slot ? */
	}

	regmap_write(regmap, base, frame_ctls[0]);
	regmap_write(regmap, base + 0x4, frame_ctls[1]);

	if (mask)
		cs48l32_asp_warn(dai, "Too many channels in TDM mask\n");
}

static int cs48l32_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
				unsigned int rx_mask, int slots, int slot_width)
{
	struct snd_soc_component *component = dai->component;
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
	int base = dai->driver->base;
	int rx_max_chan = dai->driver->playback.channels_max;
	int tx_max_chan = dai->driver->capture.channels_max;

	/* Only support TDM for the physical ASPs */
	if (dai->id > CS48L32_MAX_ASP)
		return -EINVAL;

	if (slots == 0) {
		tx_mask = (1 << tx_max_chan) - 1;
		rx_mask = (1 << rx_max_chan) - 1;
	}

	cs48l32_set_channels_to_mask(dai, base + CS48L32_ASP_FRAME_CONTROL1,
				   tx_max_chan, tx_mask);
	cs48l32_set_channels_to_mask(dai, base + CS48L32_ASP_FRAME_CONTROL5,
				   rx_max_chan, rx_mask);

	cs48l32_codec->tdm_width[dai->id - 1] = slot_width;
	cs48l32_codec->tdm_slots[dai->id - 1] = slots;

	return 0;
}

static const struct snd_soc_dai_ops cs48l32_dai_ops = {
	.probe = &cs48l32_asp_dai_probe,
	.startup = &cs48l32_startup,
	.set_fmt = &cs48l32_set_fmt,
	.set_tdm_slot = &cs48l32_set_tdm_slot,
	.hw_params = &cs48l32_hw_params,
	.set_sysclk = &cs48l32_dai_set_sysclk,
};

static int cs48l32_sysclk_ev(struct snd_soc_dapm_widget *w,
			     struct snd_kcontrol *kcontrol, int event)
{
	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);

	cs48l32_spin_sysclk(cs48l32_codec);

	return 0;
}

static int cs48l32_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event)
{
	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
	unsigned int reg;

	if (w->shift % 2)
		reg = CS48L32_IN1L_CONTROL2;
	else
		reg = CS48L32_IN1R_CONTROL2;

	reg += (w->shift / 2) * (CS48L32_IN2L_CONTROL2 - CS48L32_IN1L_CONTROL2);

	switch (event) {
	case SND_SOC_DAPM_PRE_PMU:
		switch (w->shift) {
		case CS48L32_IN1L_EN_SHIFT:
			snd_soc_component_update_bits(component,
						      CS48L32_ADC1L_ANA_CONTROL1,
						      CS48L32_ADC1x_INT_ENA_FRC_MASK,
						      CS48L32_ADC1x_INT_ENA_FRC_MASK);
			break;
		case CS48L32_IN1R_EN_SHIFT:
			snd_soc_component_update_bits(component,
						      CS48L32_ADC1R_ANA_CONTROL1,
						      CS48L32_ADC1x_INT_ENA_FRC_MASK,
						      CS48L32_ADC1x_INT_ENA_FRC_MASK);
			break;
		default:
			break;
		}
		cs48l32_codec->in_up_pending++;
		break;
	case SND_SOC_DAPM_POST_PMU:
		usleep_range(200, 300);

		switch (w->shift) {
		case CS48L32_IN1L_EN_SHIFT:
			snd_soc_component_update_bits(component,
						      CS48L32_ADC1L_ANA_CONTROL1,
						      CS48L32_ADC1x_INT_ENA_FRC_MASK,
						      0);
			break;
		case CS48L32_IN1R_EN_SHIFT:
			snd_soc_component_update_bits(component,
						      CS48L32_ADC1R_ANA_CONTROL1,
						      CS48L32_ADC1x_INT_ENA_FRC_MASK,
						      0);
			break;

		default:
			break;
		}
		cs48l32_codec->in_up_pending--;
		snd_soc_component_update_bits(component, reg, CS48L32_INx_MUTE_MASK, 0);

		/* Uncached write-only register, no need for update_bits */
		if (!cs48l32_codec->in_up_pending) {
			snd_soc_component_write(component, cs48l32_codec->in_vu_reg,
						CS48L32_IN_VU_MASK);
		}
		break;
	case SND_SOC_DAPM_PRE_PMD:
		snd_soc_component_update_bits(component, reg,
					      CS48L32_INx_MUTE_MASK, CS48L32_INx_MUTE_MASK);
		snd_soc_component_write(component, cs48l32_codec->in_vu_reg,
					CS48L32_IN_VU_MASK);
		break;
	default:
		break;
	}

	return 0;
}

static int cs48l32_in_put_volsw(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
	int ret;

	ret = snd_soc_put_volsw(kcontrol, ucontrol);
	if (ret < 0)
		return ret;

	/*
	 * Uncached write-only register, no need for update_bits.
	 * Will fail if codec is off but that will be handled by cs48l32_in_ev
	 */
	snd_soc_component_write(component, cs48l32_codec->in_vu_reg, CS48L32_IN_VU);

	return ret;
}

static bool cs48l32_eq_filter_unstable(bool mode, __be16 in_a, __be16 in_b)
{
	s16 a = be16_to_cpu(in_a);
	s16 b = be16_to_cpu(in_b);

	if (!mode)
		return abs(a) > CS48L32_EQ_MAX_COEFF;

	if (abs(b) > CS48L32_EQ_MAX_COEFF)
		return true;

	if (abs((a << 16) / (CS48L32_EQ_MAX_COEFF + 1 - b)) >= ((CS48L32_EQ_MAX_COEFF + 1) << 4))
		return true;

	return false;
}

static int cs48l32_eq_ev(struct snd_soc_dapm_widget *w,
			 struct snd_kcontrol *kcontrol, int event)
{
	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
	struct regmap *regmap = cs48l32_codec->core.regmap;
	unsigned int mode = cs48l32_codec->eq_mode[w->shift];
	unsigned int reg;
	__be16 *data = &cs48l32_codec->eq_coefficients[w->shift][0];
	int ret = 0;

	reg = CS48L32_EQ1_BAND1_COEFF1;
	reg += w->shift * (CS48L32_EQ2_BAND1_COEFF1 - CS48L32_EQ1_BAND1_COEFF1);

	switch (event) {
	case SND_SOC_DAPM_PRE_PMU:
		if (cs48l32_eq_filter_unstable(!!mode, data[1], data[0]) ||
		    cs48l32_eq_filter_unstable(true, data[7], data[6]) ||
		    cs48l32_eq_filter_unstable(true, data[13], data[12]) ||
		    cs48l32_eq_filter_unstable(true, data[19], data[18]) ||
		    cs48l32_eq_filter_unstable(false, data[25], data[24])) {
			dev_err(cs48l32_codec->core.dev, "Rejecting unstable EQ coefficients.\n");
			ret = -EINVAL;
		} else {
			ret = regmap_raw_write(regmap, reg, data, CS48L32_EQ_BLOCK_SZ);
			if (ret < 0) {
				dev_err(cs48l32_codec->core.dev,
					"Error writing EQ coefficients: %d\n", ret);
				goto out;
			}

			ret = snd_soc_component_update_bits(component,
							    CS48L32_EQ_CONTROL2,
							    w->mask,
							    mode << w->shift);
			if (ret < 0) {
				dev_err(cs48l32_codec->core.dev,
					"Error writing EQ mode: %d\n", ret);
			}
		}
		break;
	default:
		break;
	}

out:
	return ret;
}

static const struct snd_kcontrol_new cs48l32_snd_controls[] = {
SOC_ENUM("IN1 OSR", cs48l32_in_dmic_osr[0]),
SOC_ENUM("IN2 OSR", cs48l32_in_dmic_osr[1]),

SOC_SINGLE_RANGE_TLV("IN1L Volume", CS48L32_IN1L_CONTROL2,
		     CS48L32_INx_PGA_VOL_SHIFT, 0x40, 0x5f, 0, cs48l32_ana_tlv),
SOC_SINGLE_RANGE_TLV("IN1R Volume", CS48L32_IN1R_CONTROL2,
		     CS48L32_INx_PGA_VOL_SHIFT, 0x40, 0x5f, 0, cs48l32_ana_tlv),

SOC_ENUM("IN HPF Cutoff Frequency", cs48l32_in_hpf_cut_enum),

SOC_SINGLE_EXT("IN1L LP Switch", CS48L32_IN1L_CONTROL1, CS48L32_INx_LP_MODE_SHIFT,
	       1, 0, snd_soc_get_volsw, cs48l32_low_power_mode_put),
SOC_SINGLE_EXT("IN1R LP Switch", CS48L32_IN1R_CONTROL1, CS48L32_INx_LP_MODE_SHIFT,
	       1, 0, snd_soc_get_volsw, cs48l32_low_power_mode_put),

SOC_SINGLE("IN1L HPF Switch", CS48L32_IN1L_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0),
SOC_SINGLE("IN1R HPF Switch", CS48L32_IN1R_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0),
SOC_SINGLE("IN2L HPF Switch", CS48L32_IN2L_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0),
SOC_SINGLE("IN2R HPF Switch", CS48L32_IN2R_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0),

SOC_SINGLE_EXT_TLV("IN1L Digital Volume", CS48L32_IN1L_CONTROL2,
		   CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw,
		   cs48l32_in_put_volsw, cs48l32_digital_tlv),
SOC_SINGLE_EXT_TLV("IN1R Digital Volume", CS48L32_IN1R_CONTROL2,
		   CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw,
		   cs48l32_in_put_volsw, cs48l32_digital_tlv),
SOC_SINGLE_EXT_TLV("IN2L Digital Volume", CS48L32_IN2L_CONTROL2,
		   CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw,
		   cs48l32_in_put_volsw, cs48l32_digital_tlv),
SOC_SINGLE_EXT_TLV("IN2R Digital Volume", CS48L32_IN2R_CONTROL2,
		   CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw,
		   cs48l32_in_put_volsw, cs48l32_digital_tlv),

SOC_ENUM("Input Ramp Up", cs48l32_in_vi_ramp),
SOC_ENUM("Input Ramp Down", cs48l32_in_vd_ramp),

CS48L32_RATE_ENUM("Ultrasonic 1 Rate", cs48l32_us_output_rate[0]),
CS48L32_RATE_ENUM("Ultrasonic 2 Rate", cs48l32_us_output_rate[1]),

SOC_ENUM("Ultrasonic 1 Freq", cs48l32_us_freq[0]),
SOC_ENUM("Ultrasonic 2 Freq", cs48l32_us_freq[1]),

SOC_SINGLE_TLV("Ultrasonic 1 Volume", CS48L32_US1_CONTROL, CS48L32_US1_GAIN_SHIFT,
	       3, 0, cs48l32_us_tlv),
SOC_SINGLE_TLV("Ultrasonic 2 Volume", CS48L32_US2_CONTROL, CS48L32_US1_GAIN_SHIFT,
	       3, 0, cs48l32_us_tlv),

SOC_ENUM("Ultrasonic 1 Detect Threshold", cs48l32_us_det_thr[0]),
SOC_ENUM("Ultrasonic 2 Detect Threshold", cs48l32_us_det_thr[1]),

SOC_ENUM("Ultrasonic 1 Detect Pulse Length", cs48l32_us_det_num[0]),
SOC_ENUM("Ultrasonic 2 Detect Pulse Length", cs48l32_us_det_num[1]),

SOC_ENUM("Ultrasonic 1 Detect Hold", cs48l32_us_det_hold[0]),
SOC_ENUM("Ultrasonic 2 Detect Hold", cs48l32_us_det_hold[1]),

SOC_ENUM("Ultrasonic 1 Detect Decay", cs48l32_us_det_dcy[0]),
SOC_ENUM("Ultrasonic 2 Detect Decay", cs48l32_us_det_dcy[1]),

SOC_SINGLE("Ultrasonic 1 Detect LPF Switch",
	   CS48L32_US1_DET_CONTROL, CS48L32_US1_DET_LPF_SHIFT, 1, 0),
SOC_SINGLE("Ultrasonic 2 Detect LPF Switch",
	   CS48L32_US2_DET_CONTROL, CS48L32_US1_DET_LPF_SHIFT, 1, 0),

SOC_ENUM("Ultrasonic 1 Detect LPF Cut-off", cs48l32_us_det_lpf_cut[0]),
SOC_ENUM("Ultrasonic 2 Detect LPF Cut-off", cs48l32_us_det_lpf_cut[1]),

CS48L32_MIXER_CONTROLS("EQ1", CS48L32_EQ1_INPUT1),
CS48L32_MIXER_CONTROLS("EQ2", CS48L32_EQ2_INPUT1),
CS48L32_MIXER_CONTROLS("EQ3", CS48L32_EQ3_INPUT1),
CS48L32_MIXER_CONTROLS("EQ4", CS48L32_EQ4_INPUT1),

SOC_ENUM_EXT("EQ1 Mode", cs48l32_eq_mode[0], cs48l32_eq_mode_get, cs48l32_eq_mode_put),

CS48L32_EQ_COEFF_CONTROLS(EQ1),

SOC_SINGLE_TLV("EQ1 B1 Volume", CS48L32_EQ1_GAIN1, 0, 24, 0, cs48l32_eq_tlv),
SOC_SINGLE_TLV("EQ1 B2 Volume", CS48L32_EQ1_GAIN1, 8, 24, 0, cs48l32_eq_tlv),
SOC_SINGLE_TLV("EQ1 B3 Volume", CS48L32_EQ1_GAIN1, 16, 24, 0, cs48l32_eq_tlv),
SOC_SINGLE_TLV("EQ1 B4 Volume", CS48L32_EQ1_GAIN1, 24, 24, 0, cs48l32_eq_tlv),
SOC_SINGLE_TLV("EQ1 B5 Volume", CS48L32_EQ1_GAIN2, 0,  24, 0, cs48l32_eq_tlv),

SOC_ENUM_EXT("EQ2 Mode", cs48l32_eq_mode[1], cs48l32_eq_mode_get, cs48l32_eq_mode_put),
CS48L32_EQ_COEFF_CONTROLS(EQ2),
SOC_SINGLE_TLV("EQ2 B1 Volume", CS48L32_EQ2_GAIN1, 0, 24, 0, cs48l32_eq_tlv),
SOC_SINGLE_TLV("EQ2 B2 Volume", CS48L32_EQ2_GAIN1, 8, 24, 0, cs48l32_eq_tlv),
SOC_SINGLE_TLV("EQ2 B3 Volume", CS48L32_EQ2_GAIN1, 16, 24, 0, cs48l32_eq_tlv),
SOC_SINGLE_TLV("EQ2 B4 Volume", CS48L32_EQ2_GAIN1, 24, 24, 0, cs48l32_eq_tlv),
SOC_SINGLE_TLV("EQ2 B5 Volume", CS48L32_EQ2_GAIN2, 0,  24, 0, cs48l32_eq_tlv),

SOC_ENUM_EXT("EQ3 Mode", cs48l32_eq_mode[2], cs48l32_eq_mode_get, cs48l32_eq_mode_put),
CS48L32_EQ_COEFF_CONTROLS(EQ3),
SOC_SINGLE_TLV("EQ3 B1 Volume", CS48L32_EQ3_GAIN1, 0, 24, 0, cs48l32_eq_tlv),
SOC_SINGLE_TLV("EQ3 B2 Volume", CS48L32_EQ3_GAIN1, 8, 24, 0, cs48l32_eq_tlv),
SOC_SINGLE_TLV("EQ3 B3 Volume", CS48L32_EQ3_GAIN1, 16, 24, 0, cs48l32_eq_tlv),
SOC_SINGLE_TLV("EQ3 B4 Volume", CS48L32_EQ3_GAIN1, 24, 24, 0, cs48l32_eq_tlv),
SOC_SINGLE_TLV("EQ3 B5 Volume", CS48L32_EQ3_GAIN2, 0,  24, 0, cs48l32_eq_tlv),

SOC_ENUM_EXT("EQ4 Mode", cs48l32_eq_mode[3], cs48l32_eq_mode_get, cs48l32_eq_mode_put),
CS48L32_EQ_COEFF_CONTROLS(EQ4),
SOC_SINGLE_TLV("EQ4 B1 Volume", CS48L32_EQ4_GAIN1, 0, 24, 0, cs48l32_eq_tlv),
SOC_SINGLE_TLV("EQ4 B2 Volume", CS48L32_EQ4_GAIN1, 8, 24, 0, cs48l32_eq_tlv),
SOC_SINGLE_TLV("EQ4 B3 Volume", CS48L32_EQ4_GAIN1, 16, 24, 0, cs48l32_eq_tlv),
SOC_SINGLE_TLV("EQ4 B4 Volume", CS48L32_EQ4_GAIN1, 24, 24, 0, cs48l32_eq_tlv),
SOC_SINGLE_TLV("EQ4 B5 Volume", CS48L32_EQ4_GAIN2, 0,  24, 0, cs48l32_eq_tlv),

CS48L32_MIXER_CONTROLS("DRC1L", CS48L32_DRC1L_INPUT1),
CS48L32_MIXER_CONTROLS("DRC1R", CS48L32_DRC1R_INPUT1),
CS48L32_MIXER_CONTROLS("DRC2L", CS48L32_DRC2L_INPUT1),
CS48L32_MIXER_CONTROLS("DRC2R", CS48L32_DRC2R_INPUT1),

SND_SOC_BYTES_MASK("DRC1 Coefficients", CS48L32_DRC1_CONTROL1, 4,
		   BIT(CS48L32_DRC1R_EN_SHIFT) | BIT(CS48L32_DRC1L_EN_SHIFT)),
SND_SOC_BYTES_MASK("DRC2 Coefficients", CS48L32_DRC2_CONTROL1, 4,
		   BIT(CS48L32_DRC1R_EN_SHIFT) | BIT(CS48L32_DRC1L_EN_SHIFT)),

CS48L32_MIXER_CONTROLS("LHPF1", CS48L32_LHPF1_INPUT1),
CS48L32_MIXER_CONTROLS("LHPF2", CS48L32_LHPF2_INPUT1),
CS48L32_MIXER_CONTROLS("LHPF3", CS48L32_LHPF3_INPUT1),
CS48L32_MIXER_CONTROLS("LHPF4", CS48L32_LHPF4_INPUT1),

CS48L32_LHPF_CONTROL("LHPF1 Coefficients", CS48L32_LHPF1_COEFF),
CS48L32_LHPF_CONTROL("LHPF2 Coefficients", CS48L32_LHPF2_COEFF),
CS48L32_LHPF_CONTROL("LHPF3 Coefficients", CS48L32_LHPF3_COEFF),
CS48L32_LHPF_CONTROL("LHPF4 Coefficients", CS48L32_LHPF4_COEFF),

SOC_ENUM("LHPF1 Mode", cs48l32_lhpf_mode[0]),
SOC_ENUM("LHPF2 Mode", cs48l32_lhpf_mode[1]),
SOC_ENUM("LHPF3 Mode", cs48l32_lhpf_mode[2]),
SOC_ENUM("LHPF4 Mode", cs48l32_lhpf_mode[3]),

CS48L32_RATE_CONTROL("Sample Rate 1", 1),
CS48L32_RATE_CONTROL("Sample Rate 2", 2),
CS48L32_RATE_CONTROL("Sample Rate 3", 3),
CS48L32_RATE_CONTROL("Sample Rate 4", 4),

CS48L32_RATE_ENUM("FX Rate", cs48l32_fx_rate),

CS48L32_RATE_ENUM("ISRC1 FSL", cs48l32_isrc_fsl[0]),
CS48L32_RATE_ENUM("ISRC2 FSL", cs48l32_isrc_fsl[1]),
CS48L32_RATE_ENUM("ISRC3 FSL", cs48l32_isrc_fsl[2]),
CS48L32_RATE_ENUM("ISRC1 FSH", cs48l32_isrc_fsh[0]),
CS48L32_RATE_ENUM("ISRC2 FSH", cs48l32_isrc_fsh[1]),
CS48L32_RATE_ENUM("ISRC3 FSH", cs48l32_isrc_fsh[2]),

SOC_ENUM("AUXPDM1 Rate", cs48l32_auxpdm1_freq),
SOC_ENUM("AUXPDM2 Rate", cs48l32_auxpdm2_freq),

SOC_ENUM_EXT("IN1L Rate", cs48l32_input_rate[0], snd_soc_get_enum_double, cs48l32_in_rate_put),
SOC_ENUM_EXT("IN1R Rate", cs48l32_input_rate[1], snd_soc_get_enum_double, cs48l32_in_rate_put),
SOC_ENUM_EXT("IN2L Rate", cs48l32_input_rate[2], snd_soc_get_enum_double, cs48l32_in_rate_put),
SOC_ENUM_EXT("IN2R Rate", cs48l32_input_rate[3], snd_soc_get_enum_double, cs48l32_in_rate_put),

CS48L32_RATE_ENUM("Noise Generator Rate", noise_gen_rate),

SOC_SINGLE_TLV("Noise Generator Volume", CS48L32_COMFORT_NOISE_GENERATOR,
	       CS48L32_NOISE_GEN_GAIN_SHIFT, 0x12, 0, cs48l32_noise_tlv),

CS48L32_MIXER_CONTROLS("ASP1TX1", CS48L32_ASP1TX1_INPUT1),
CS48L32_MIXER_CONTROLS("ASP1TX2", CS48L32_ASP1TX2_INPUT1),
CS48L32_MIXER_CONTROLS("ASP1TX3", CS48L32_ASP1TX3_INPUT1),
CS48L32_MIXER_CONTROLS("ASP1TX4", CS48L32_ASP1TX4_INPUT1),
CS48L32_MIXER_CONTROLS("ASP1TX5", CS48L32_ASP1TX5_INPUT1),
CS48L32_MIXER_CONTROLS("ASP1TX6", CS48L32_ASP1TX6_INPUT1),
CS48L32_MIXER_CONTROLS("ASP1TX7", CS48L32_ASP1TX7_INPUT1),
CS48L32_MIXER_CONTROLS("ASP1TX8", CS48L32_ASP1TX8_INPUT1),

CS48L32_MIXER_CONTROLS("ASP2TX1", CS48L32_ASP2TX1_INPUT1),
CS48L32_MIXER_CONTROLS("ASP2TX2", CS48L32_ASP2TX2_INPUT1),
CS48L32_MIXER_CONTROLS("ASP2TX3", CS48L32_ASP2TX3_INPUT1),
CS48L32_MIXER_CONTROLS("ASP2TX4", CS48L32_ASP2TX4_INPUT1),

WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),

CS48L32_MIXER_CONTROLS("DSP1RX1", CS48L32_DSP1RX1_INPUT1),
CS48L32_MIXER_CONTROLS("DSP1RX2", CS48L32_DSP1RX2_INPUT1),
CS48L32_MIXER_CONTROLS("DSP1RX3", CS48L32_DSP1RX3_INPUT1),
CS48L32_MIXER_CONTROLS("DSP1RX4", CS48L32_DSP1RX4_INPUT1),
CS48L32_MIXER_CONTROLS("DSP1RX5", CS48L32_DSP1RX5_INPUT1),
CS48L32_MIXER_CONTROLS("DSP1RX6", CS48L32_DSP1RX6_INPUT1),
CS48L32_MIXER_CONTROLS("DSP1RX7", CS48L32_DSP1RX7_INPUT1),
CS48L32_MIXER_CONTROLS("DSP1RX8", CS48L32_DSP1RX8_INPUT1),

WM_ADSP_FW_CONTROL("DSP1", 0),

CS48L32_DSP_RATE_CONTROL("DSP1RX1", 0),
CS48L32_DSP_RATE_CONTROL("DSP1RX2", 1),
CS48L32_DSP_RATE_CONTROL("DSP1RX3", 2),
CS48L32_DSP_RATE_CONTROL("DSP1RX4", 3),
CS48L32_DSP_RATE_CONTROL("DSP1RX5", 4),
CS48L32_DSP_RATE_CONTROL("DSP1RX6", 5),
CS48L32_DSP_RATE_CONTROL("DSP1RX7", 6),
CS48L32_DSP_RATE_CONTROL("DSP1RX8", 7),
CS48L32_DSP_RATE_CONTROL("DSP1TX1", 8),
CS48L32_DSP_RATE_CONTROL("DSP1TX2", 9),
CS48L32_DSP_RATE_CONTROL("DSP1TX3", 10),
CS48L32_DSP_RATE_CONTROL("DSP1TX4", 11),
CS48L32_DSP_RATE_CONTROL("DSP1TX5", 12),
CS48L32_DSP_RATE_CONTROL("DSP1TX6", 13),
CS48L32_DSP_RATE_CONTROL("DSP1TX7", 14),
CS48L32_DSP_RATE_CONTROL("DSP1TX8", 15),
};

CS48L32_MIXER_ENUMS(EQ1, CS48L32_EQ1_INPUT1);
CS48L32_MIXER_ENUMS(EQ2, CS48L32_EQ2_INPUT1);
CS48L32_MIXER_ENUMS(EQ3, CS48L32_EQ3_INPUT1);
CS48L32_MIXER_ENUMS(EQ4, CS48L32_EQ4_INPUT1);

CS48L32_MIXER_ENUMS(DRC1L, CS48L32_DRC1L_INPUT1);
CS48L32_MIXER_ENUMS(DRC1R, CS48L32_DRC1R_INPUT1);
CS48L32_MIXER_ENUMS(DRC2L, CS48L32_DRC2L_INPUT1);
CS48L32_MIXER_ENUMS(DRC2R, CS48L32_DRC2R_INPUT1);

CS48L32_MIXER_ENUMS(LHPF1, CS48L32_LHPF1_INPUT1);
CS48L32_MIXER_ENUMS(LHPF2, CS48L32_LHPF2_INPUT1);
CS48L32_MIXER_ENUMS(LHPF3, CS48L32_LHPF3_INPUT1);
CS48L32_MIXER_ENUMS(LHPF4, CS48L32_LHPF4_INPUT1);

CS48L32_MIXER_ENUMS(ASP1TX1, CS48L32_ASP1TX1_INPUT1);
CS48L32_MIXER_ENUMS(ASP1TX2, CS48L32_ASP1TX2_INPUT1);
CS48L32_MIXER_ENUMS(ASP1TX3, CS48L32_ASP1TX3_INPUT1);
CS48L32_MIXER_ENUMS(ASP1TX4, CS48L32_ASP1TX4_INPUT1);
CS48L32_MIXER_ENUMS(ASP1TX5, CS48L32_ASP1TX5_INPUT1);
CS48L32_MIXER_ENUMS(ASP1TX6, CS48L32_ASP1TX6_INPUT1);
CS48L32_MIXER_ENUMS(ASP1TX7, CS48L32_ASP1TX7_INPUT1);
CS48L32_MIXER_ENUMS(ASP1TX8, CS48L32_ASP1TX8_INPUT1);

CS48L32_MIXER_ENUMS(ASP2TX1, CS48L32_ASP2TX1_INPUT1);
CS48L32_MIXER_ENUMS(ASP2TX2, CS48L32_ASP2TX2_INPUT1);
CS48L32_MIXER_ENUMS(ASP2TX3, CS48L32_ASP2TX3_INPUT1);
CS48L32_MIXER_ENUMS(ASP2TX4, CS48L32_ASP2TX4_INPUT1);

CS48L32_MUX_ENUMS(ISRC1INT1, CS48L32_ISRC1INT1_INPUT1);
CS48L32_MUX_ENUMS(ISRC1INT2, CS48L32_ISRC1INT2_INPUT1);
CS48L32_MUX_ENUMS(ISRC1INT3, CS48L32_ISRC1INT3_INPUT1);
CS48L32_MUX_ENUMS(ISRC1INT4, CS48L32_ISRC1INT4_INPUT1);

CS48L32_MUX_ENUMS(ISRC1DEC1, CS48L32_ISRC1DEC1_INPUT1);
CS48L32_MUX_ENUMS(ISRC1DEC2, CS48L32_ISRC1DEC2_INPUT1);
CS48L32_MUX_ENUMS(ISRC1DEC3, CS48L32_ISRC1DEC3_INPUT1);
CS48L32_MUX_ENUMS(ISRC1DEC4, CS48L32_ISRC1DEC4_INPUT1);

CS48L32_MUX_ENUMS(ISRC2INT1, CS48L32_ISRC2INT1_INPUT1);
CS48L32_MUX_ENUMS(ISRC2INT2, CS48L32_ISRC2INT2_INPUT1);

CS48L32_MUX_ENUMS(ISRC2DEC1, CS48L32_ISRC2DEC1_INPUT1);
CS48L32_MUX_ENUMS(ISRC2DEC2, CS48L32_ISRC2DEC2_INPUT1);

CS48L32_MUX_ENUMS(ISRC3INT1, CS48L32_ISRC3INT1_INPUT1);
CS48L32_MUX_ENUMS(ISRC3INT2, CS48L32_ISRC3INT2_INPUT1);

CS48L32_MUX_ENUMS(ISRC3DEC1, CS48L32_ISRC3DEC1_INPUT1);
CS48L32_MUX_ENUMS(ISRC3DEC2, CS48L32_ISRC3DEC2_INPUT1);

CS48L32_MIXER_ENUMS(DSP1RX1, CS48L32_DSP1RX1_INPUT1);
CS48L32_MIXER_ENUMS(DSP1RX2, CS48L32_DSP1RX2_INPUT1);
CS48L32_MIXER_ENUMS(DSP1RX3, CS48L32_DSP1RX3_INPUT1);
CS48L32_MIXER_ENUMS(DSP1RX4, CS48L32_DSP1RX4_INPUT1);
CS48L32_MIXER_ENUMS(DSP1RX5, CS48L32_DSP1RX5_INPUT1);
CS48L32_MIXER_ENUMS(DSP1RX6, CS48L32_DSP1RX6_INPUT1);
CS48L32_MIXER_ENUMS(DSP1RX7, CS48L32_DSP1RX7_INPUT1);
CS48L32_MIXER_ENUMS(DSP1RX8, CS48L32_DSP1RX8_INPUT1);

static int cs48l32_dsp_mem_ev(struct snd_soc_dapm_widget *w,
			      struct snd_kcontrol *kcontrol, int event)
{
	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);

	switch (event) {
	case SND_SOC_DAPM_POST_PMU:
		return cs48l32_dsp_memory_enable(cs48l32_codec, &cs48l32_dsp_sram_regs);
	case SND_SOC_DAPM_PRE_PMD:
		cs48l32_dsp_memory_disable(cs48l32_codec, &cs48l32_dsp_sram_regs);
		return 0;
	default:
		return 0;
	}
}

static const struct snd_soc_dapm_widget cs48l32_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("SYSCLK", CS48L32_SYSTEM_CLOCK1, CS48L32_SYSCLK_EN_SHIFT, 0,
		    cs48l32_sysclk_ev, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),

SND_SOC_DAPM_REGULATOR_SUPPLY("vdd-cp", 20, 0),

SND_SOC_DAPM_SUPPLY("VOUT_MIC", CS48L32_CHARGE_PUMP1, CS48L32_CP2_EN_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("VOUT_MIC_REGULATED", CS48L32_CHARGE_PUMP1, CS48L32_CP2_BYPASS_SHIFT,
		    1, NULL, 0),
SND_SOC_DAPM_SUPPLY("MICBIAS1", CS48L32_MICBIAS_CTRL1, CS48L32_MICB1_EN_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("MICBIAS1A", CS48L32_MICBIAS_CTRL5, CS48L32_MICB1A_EN_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("MICBIAS1B", CS48L32_MICBIAS_CTRL5, CS48L32_MICB1B_EN_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("MICBIAS1C", CS48L32_MICBIAS_CTRL5, CS48L32_MICB1C_EN_SHIFT, 0, NULL, 0),

SND_SOC_DAPM_SUPPLY("DSP1MEM", SND_SOC_NOPM, 0, 0, cs48l32_dsp_mem_ev,
		    SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),

CS48L32_DSP_FREQ_WIDGET_EV("DSP1", 0, cs48l32_dsp_freq_ev),

SND_SOC_DAPM_SIGGEN("TONE"),
SND_SOC_DAPM_SIGGEN("NOISE"),

SND_SOC_DAPM_INPUT("IN1LN_1"),
SND_SOC_DAPM_INPUT("IN1LN_2"),
SND_SOC_DAPM_INPUT("IN1LP_1"),
SND_SOC_DAPM_INPUT("IN1LP_2"),
SND_SOC_DAPM_INPUT("IN1RN_1"),
SND_SOC_DAPM_INPUT("IN1RN_2"),
SND_SOC_DAPM_INPUT("IN1RP_1"),
SND_SOC_DAPM_INPUT("IN1RP_2"),
SND_SOC_DAPM_INPUT("IN1_PDMCLK"),
SND_SOC_DAPM_INPUT("IN1_PDMDATA"),

SND_SOC_DAPM_INPUT("IN2_PDMCLK"),
SND_SOC_DAPM_INPUT("IN2_PDMDATA"),

SND_SOC_DAPM_MUX("Ultrasonic 1 Input", SND_SOC_NOPM, 0, 0, &cs48l32_us_inmux[0]),
SND_SOC_DAPM_MUX("Ultrasonic 2 Input", SND_SOC_NOPM, 0, 0, &cs48l32_us_inmux[1]),

SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),

SND_SOC_DAPM_OUTPUT("DSP Trigger Out"),

SND_SOC_DAPM_MUX("IN1L Mux", SND_SOC_NOPM, 0, 0, &cs48l32_inmux[0]),
SND_SOC_DAPM_MUX("IN1R Mux", SND_SOC_NOPM, 0, 0, &cs48l32_inmux[1]),

SND_SOC_DAPM_MUX("IN1L Mode", SND_SOC_NOPM, 0, 0, &cs48l32_dmode_mux[0]),
SND_SOC_DAPM_MUX("IN1R Mode", SND_SOC_NOPM, 0, 0, &cs48l32_dmode_mux[0]),

SND_SOC_DAPM_AIF_OUT("ASP1TX1", NULL, 0, CS48L32_ASP1_ENABLES1, 0, 0),
SND_SOC_DAPM_AIF_OUT("ASP1TX2", NULL, 1, CS48L32_ASP1_ENABLES1, 1, 0),
SND_SOC_DAPM_AIF_OUT("ASP1TX3", NULL, 2, CS48L32_ASP1_ENABLES1, 2, 0),
SND_SOC_DAPM_AIF_OUT("ASP1TX4", NULL, 3, CS48L32_ASP1_ENABLES1, 3, 0),
SND_SOC_DAPM_AIF_OUT("ASP1TX5", NULL, 4, CS48L32_ASP1_ENABLES1, 4, 0),
SND_SOC_DAPM_AIF_OUT("ASP1TX6", NULL, 5, CS48L32_ASP1_ENABLES1, 5, 0),
SND_SOC_DAPM_AIF_OUT("ASP1TX7", NULL, 6, CS48L32_ASP1_ENABLES1, 6, 0),
SND_SOC_DAPM_AIF_OUT("ASP1TX8", NULL, 7, CS48L32_ASP1_ENABLES1, 7, 0),

SND_SOC_DAPM_AIF_OUT("ASP2TX1", NULL, 0, CS48L32_ASP2_ENABLES1, 0, 0),
SND_SOC_DAPM_AIF_OUT("ASP2TX2", NULL, 1, CS48L32_ASP2_ENABLES1, 1, 0),
SND_SOC_DAPM_AIF_OUT("ASP2TX3", NULL, 2, CS48L32_ASP2_ENABLES1, 2, 0),
SND_SOC_DAPM_AIF_OUT("ASP2TX4", NULL, 3, CS48L32_ASP2_ENABLES1, 3, 0),

SND_SOC_DAPM_SWITCH("AUXPDM1 Output", CS48L32_AUXPDM_CONTROL1, 0, 0, &cs48l32_auxpdm_switch[0]),
SND_SOC_DAPM_SWITCH("AUXPDM2 Output", CS48L32_AUXPDM_CONTROL1, 1, 0, &cs48l32_auxpdm_switch[1]),

SND_SOC_DAPM_MUX("AUXPDM1 Input", SND_SOC_NOPM, 0, 0, &cs48l32_auxpdm_inmux[0]),
SND_SOC_DAPM_MUX("AUXPDM2 Input", SND_SOC_NOPM, 0, 0, &cs48l32_auxpdm_inmux[1]),

SND_SOC_DAPM_MUX("AUXPDM1 Analog Input", SND_SOC_NOPM, 0, 0,
		 &cs48l32_auxpdm_analog_inmux[0]),
SND_SOC_DAPM_MUX("AUXPDM2 Analog Input", SND_SOC_NOPM, 0, 0,
		 &cs48l32_auxpdm_analog_inmux[1]),

SND_SOC_DAPM_SWITCH("Ultrasonic 1 Detect", CS48L32_US_CONTROL,
		    CS48L32_US1_DET_EN_SHIFT, 0, &cs48l32_us_switch[0]),
SND_SOC_DAPM_SWITCH("Ultrasonic 2 Detect", CS48L32_US_CONTROL,
		    CS48L32_US1_DET_EN_SHIFT, 0, &cs48l32_us_switch[1]),

/*
 * mux_in widgets : arranged in the order of sources
 * specified in CS48L32_MIXER_INPUT_ROUTES
 */
SND_SOC_DAPM_PGA("Tone Generator 1", CS48L32_TONE_GENERATOR1, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("Tone Generator 2", CS48L32_TONE_GENERATOR1, 1, 0, NULL, 0),

SND_SOC_DAPM_PGA("Noise Generator", CS48L32_COMFORT_NOISE_GENERATOR,
		 CS48L32_NOISE_GEN_EN_SHIFT, 0, NULL, 0),

SND_SOC_DAPM_PGA_E("IN1L PGA", CS48L32_INPUT_CONTROL, CS48L32_IN1L_EN_SHIFT,
		   0, NULL, 0, cs48l32_in_ev,
		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("IN1R PGA", CS48L32_INPUT_CONTROL, CS48L32_IN1R_EN_SHIFT,
		   0, NULL, 0, cs48l32_in_ev,
		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("IN2L PGA", CS48L32_INPUT_CONTROL, CS48L32_IN2L_EN_SHIFT,
		   0, NULL, 0, cs48l32_in_ev,
		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("IN2R PGA", CS48L32_INPUT_CONTROL, CS48L32_IN2R_EN_SHIFT,
		   0, NULL, 0, cs48l32_in_ev,
		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),

SND_SOC_DAPM_AIF_IN("ASP1RX1", NULL, 0, CS48L32_ASP1_ENABLES1, 16, 0),
SND_SOC_DAPM_AIF_IN("ASP1RX2", NULL, 1, CS48L32_ASP1_ENABLES1, 17, 0),
SND_SOC_DAPM_AIF_IN("ASP1RX3", NULL, 2, CS48L32_ASP1_ENABLES1, 18, 0),
SND_SOC_DAPM_AIF_IN("ASP1RX4", NULL, 3, CS48L32_ASP1_ENABLES1, 19, 0),
SND_SOC_DAPM_AIF_IN("ASP1RX5", NULL, 4, CS48L32_ASP1_ENABLES1, 20, 0),
SND_SOC_DAPM_AIF_IN("ASP1RX6", NULL, 5, CS48L32_ASP1_ENABLES1, 21, 0),
SND_SOC_DAPM_AIF_IN("ASP1RX7", NULL, 6, CS48L32_ASP1_ENABLES1, 22, 0),
SND_SOC_DAPM_AIF_IN("ASP1RX8", NULL, 7, CS48L32_ASP1_ENABLES1, 23, 0),

SND_SOC_DAPM_AIF_IN("ASP2RX1", NULL, 0, CS48L32_ASP2_ENABLES1, 16, 0),
SND_SOC_DAPM_AIF_IN("ASP2RX2", NULL, 1, CS48L32_ASP2_ENABLES1, 17, 0),
SND_SOC_DAPM_AIF_IN("ASP2RX3", NULL, 2, CS48L32_ASP2_ENABLES1, 18, 0),
SND_SOC_DAPM_AIF_IN("ASP2RX4", NULL, 3, CS48L32_ASP2_ENABLES1, 19, 0),

SND_SOC_DAPM_PGA("ISRC1DEC1", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC1_EN_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_PGA("ISRC1DEC2", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC2_EN_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_PGA("ISRC1DEC3", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC3_EN_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_PGA("ISRC1DEC4", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC4_EN_SHIFT, 0, NULL, 0),

SND_SOC_DAPM_PGA("ISRC1INT1", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT1_EN_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_PGA("ISRC1INT2", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT2_EN_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_PGA("ISRC1INT3", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT3_EN_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_PGA("ISRC1INT4", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT4_EN_SHIFT, 0, NULL, 0),

SND_SOC_DAPM_PGA("ISRC2DEC1", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_DEC1_EN_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_PGA("ISRC2DEC2", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_DEC2_EN_SHIFT, 0, NULL, 0),

SND_SOC_DAPM_PGA("ISRC2INT1", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_INT1_EN_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_PGA("ISRC2INT2", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_INT2_EN_SHIFT, 0, NULL, 0),

SND_SOC_DAPM_PGA("ISRC3DEC1", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_DEC1_EN_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_PGA("ISRC3DEC2", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_DEC2_EN_SHIFT, 0, NULL, 0),

SND_SOC_DAPM_PGA("ISRC3INT1", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_INT1_EN_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_PGA("ISRC3INT2", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_INT2_EN_SHIFT, 0, NULL, 0),

SND_SOC_DAPM_PGA_E("EQ1", CS48L32_EQ_CONTROL1, 0, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU),
SND_SOC_DAPM_PGA_E("EQ2", CS48L32_EQ_CONTROL1, 1, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU),
SND_SOC_DAPM_PGA_E("EQ3", CS48L32_EQ_CONTROL1, 2, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU),
SND_SOC_DAPM_PGA_E("EQ4", CS48L32_EQ_CONTROL1, 3, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU),

SND_SOC_DAPM_PGA("DRC1L", CS48L32_DRC1_CONTROL1, CS48L32_DRC1L_EN_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_PGA("DRC1R", CS48L32_DRC1_CONTROL1, CS48L32_DRC1R_EN_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_PGA("DRC2L", CS48L32_DRC2_CONTROL1, CS48L32_DRC1L_EN_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_PGA("DRC2R", CS48L32_DRC2_CONTROL1, CS48L32_DRC1R_EN_SHIFT, 0, NULL, 0),

SND_SOC_DAPM_PGA("LHPF1", CS48L32_LHPF_CONTROL1, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("LHPF2", CS48L32_LHPF_CONTROL1, 1, 0, NULL, 0),
SND_SOC_DAPM_PGA("LHPF3", CS48L32_LHPF_CONTROL1, 2, 0, NULL, 0),
SND_SOC_DAPM_PGA("LHPF4", CS48L32_LHPF_CONTROL1, 3, 0, NULL, 0),

SND_SOC_DAPM_PGA("Ultrasonic 1", CS48L32_US_CONTROL, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("Ultrasonic 2", CS48L32_US_CONTROL, 1, 0, NULL, 0),

WM_ADSP2("DSP1", 0, wm_adsp_early_event),

/* end of ordered widget list */

CS48L32_MIXER_WIDGETS(EQ1, "EQ1"),
CS48L32_MIXER_WIDGETS(EQ2, "EQ2"),
CS48L32_MIXER_WIDGETS(EQ3, "EQ3"),
CS48L32_MIXER_WIDGETS(EQ4, "EQ4"),

CS48L32_MIXER_WIDGETS(DRC1L, "DRC1L"),
CS48L32_MIXER_WIDGETS(DRC1R, "DRC1R"),
CS48L32_MIXER_WIDGETS(DRC2L, "DRC2L"),
CS48L32_MIXER_WIDGETS(DRC2R, "DRC2R"),

SND_SOC_DAPM_SWITCH("DRC1 Activity Output", SND_SOC_NOPM, 0, 0,
		    &cs48l32_drc_activity_output_mux[0]),
SND_SOC_DAPM_SWITCH("DRC2 Activity Output", SND_SOC_NOPM, 0, 0,
		    &cs48l32_drc_activity_output_mux[1]),

CS48L32_MIXER_WIDGETS(LHPF1, "LHPF1"),
CS48L32_MIXER_WIDGETS(LHPF2, "LHPF2"),
CS48L32_MIXER_WIDGETS(LHPF3, "LHPF3"),
CS48L32_MIXER_WIDGETS(LHPF4, "LHPF4"),

CS48L32_MIXER_WIDGETS(ASP1TX1, "ASP1TX1"),
CS48L32_MIXER_WIDGETS(ASP1TX2, "ASP1TX2"),
CS48L32_MIXER_WIDGETS(ASP1TX3, "ASP1TX3"),
CS48L32_MIXER_WIDGETS(ASP1TX4, "ASP1TX4"),
CS48L32_MIXER_WIDGETS(ASP1TX5, "ASP1TX5"),
CS48L32_MIXER_WIDGETS(ASP1TX6, "ASP1TX6"),
CS48L32_MIXER_WIDGETS(ASP1TX7, "ASP1TX7"),
CS48L32_MIXER_WIDGETS(ASP1TX8, "ASP1TX8"),

CS48L32_MIXER_WIDGETS(ASP2TX1, "ASP2TX1"),
CS48L32_MIXER_WIDGETS(ASP2TX2, "ASP2TX2"),
CS48L32_MIXER_WIDGETS(ASP2TX3, "ASP2TX3"),
CS48L32_MIXER_WIDGETS(ASP2TX4, "ASP2TX4"),

CS48L32_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
CS48L32_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
CS48L32_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"),
CS48L32_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"),

CS48L32_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
CS48L32_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
CS48L32_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"),
CS48L32_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"),

CS48L32_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
CS48L32_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),

CS48L32_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
CS48L32_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),

CS48L32_MUX_WIDGETS(ISRC3DEC1, "ISRC3DEC1"),
CS48L32_MUX_WIDGETS(ISRC3DEC2, "ISRC3DEC2"),

CS48L32_MUX_WIDGETS(ISRC3INT1, "ISRC3INT1"),
CS48L32_MUX_WIDGETS(ISRC3INT2, "ISRC3INT2"),

CS48L32_MIXER_WIDGETS(DSP1RX1, "DSP1RX1"),
CS48L32_MIXER_WIDGETS(DSP1RX2, "DSP1RX2"),
CS48L32_MIXER_WIDGETS(DSP1RX3, "DSP1RX3"),
CS48L32_MIXER_WIDGETS(DSP1RX4, "DSP1RX4"),
CS48L32_MIXER_WIDGETS(DSP1RX5, "DSP1RX5"),
CS48L32_MIXER_WIDGETS(DSP1RX6, "DSP1RX6"),
CS48L32_MIXER_WIDGETS(DSP1RX7, "DSP1RX7"),
CS48L32_MIXER_WIDGETS(DSP1RX8, "DSP1RX8"),

SND_SOC_DAPM_SWITCH("DSP1 Trigger Output", SND_SOC_NOPM, 0, 0,
		    &cs48l32_dsp_trigger_output_mux[0]),

SND_SOC_DAPM_OUTPUT("AUXPDM1_CLK"),
SND_SOC_DAPM_OUTPUT("AUXPDM1_DOUT"),
SND_SOC_DAPM_OUTPUT("AUXPDM2_CLK"),
SND_SOC_DAPM_OUTPUT("AUXPDM2_DOUT"),

SND_SOC_DAPM_OUTPUT("MICSUPP"),

SND_SOC_DAPM_OUTPUT("Ultrasonic Dummy Output"),
};

static const struct snd_soc_dapm_route cs48l32_dapm_routes[] = {
	{ "IN1LN_1", NULL, "SYSCLK" },
	{ "IN1LN_2", NULL, "SYSCLK" },
	{ "IN1LP_1", NULL, "SYSCLK" },
	{ "IN1LP_2", NULL, "SYSCLK" },
	{ "IN1RN_1", NULL, "SYSCLK" },
	{ "IN1RN_2", NULL, "SYSCLK" },
	{ "IN1RP_1", NULL, "SYSCLK" },
	{ "IN1RP_2", NULL, "SYSCLK" },

	{ "IN1_PDMCLK", NULL, "SYSCLK" },
	{ "IN1_PDMDATA", NULL, "SYSCLK" },
	{ "IN2_PDMCLK", NULL, "SYSCLK" },
	{ "IN2_PDMDATA", NULL, "SYSCLK" },

	{ "DSP1 Preloader", NULL, "DSP1MEM" },
	{ "DSP1", NULL, "DSP1FREQ" },

	{ "Audio Trace DSP", NULL, "DSP1" },
	{ "Voice Ctrl DSP", NULL, "DSP1" },

	{ "VOUT_MIC_REGULATED", NULL, "VOUT_MIC" },
	{ "MICBIAS1", NULL, "VOUT_MIC_REGULATED" },
	{ "MICBIAS1A", NULL, "MICBIAS1" },
	{ "MICBIAS1B", NULL, "MICBIAS1" },
	{ "MICBIAS1C", NULL, "MICBIAS1" },

	{ "Tone Generator 1", NULL, "SYSCLK" },
	{ "Tone Generator 2", NULL, "SYSCLK" },
	{ "Noise Generator", NULL, "SYSCLK" },

	{ "Tone Generator 1", NULL, "TONE" },
	{ "Tone Generator 2", NULL, "TONE" },
	{ "Noise Generator", NULL, "NOISE" },

	{ "ASP1 Capture", NULL, "ASP1TX1" },
	{ "ASP1 Capture", NULL, "ASP1TX2" },
	{ "ASP1 Capture", NULL, "ASP1TX3" },
	{ "ASP1 Capture", NULL, "ASP1TX4" },
	{ "ASP1 Capture", NULL, "ASP1TX5" },
	{ "ASP1 Capture", NULL, "ASP1TX6" },
	{ "ASP1 Capture", NULL, "ASP1TX7" },
	{ "ASP1 Capture", NULL, "ASP1TX8" },

	{ "ASP1RX1", NULL, "ASP1 Playback" },
	{ "ASP1RX2", NULL, "ASP1 Playback" },
	{ "ASP1RX3", NULL, "ASP1 Playback" },
	{ "ASP1RX4", NULL, "ASP1 Playback" },
	{ "ASP1RX5", NULL, "ASP1 Playback" },
	{ "ASP1RX6", NULL, "ASP1 Playback" },
	{ "ASP1RX7", NULL, "ASP1 Playback" },
	{ "ASP1RX8", NULL, "ASP1 Playback" },

	{ "ASP2 Capture", NULL, "ASP2TX1" },
	{ "ASP2 Capture", NULL, "ASP2TX2" },
	{ "ASP2 Capture", NULL, "ASP2TX3" },
	{ "ASP2 Capture", NULL, "ASP2TX4" },

	{ "ASP2RX1", NULL, "ASP2 Playback" },
	{ "ASP2RX2", NULL, "ASP2 Playback" },
	{ "ASP2RX3", NULL, "ASP2 Playback" },
	{ "ASP2RX4", NULL, "ASP2 Playback" },

	{ "ASP1 Playback", NULL, "SYSCLK" },
	{ "ASP2 Playback", NULL, "SYSCLK" },

	{ "ASP1 Capture", NULL, "SYSCLK" },
	{ "ASP2 Capture", NULL, "SYSCLK" },

	{ "IN1L Mux", "Analog 1", "IN1LN_1" },
	{ "IN1L Mux", "Analog 2", "IN1LN_2" },
	{ "IN1L Mux", "Analog 1", "IN1LP_1" },
	{ "IN1L Mux", "Analog 2", "IN1LP_2" },
	{ "IN1R Mux", "Analog 1", "IN1RN_1" },
	{ "IN1R Mux", "Analog 2", "IN1RN_2" },
	{ "IN1R Mux", "Analog 1", "IN1RP_1" },
	{ "IN1R Mux", "Analog 2", "IN1RP_2" },

	{ "IN1L PGA", NULL, "IN1L Mode" },
	{ "IN1R PGA", NULL, "IN1R Mode" },

	{ "IN1L Mode", "Analog", "IN1L Mux" },
	{ "IN1R Mode", "Analog", "IN1R Mux" },

	{ "IN1L Mode", "Digital", "IN1_PDMCLK" },
	{ "IN1L Mode", "Digital", "IN1_PDMDATA" },
	{ "IN1R Mode", "Digital", "IN1_PDMCLK" },
	{ "IN1R Mode", "Digital", "IN1_PDMDATA" },

	{ "IN1L PGA", NULL, "VOUT_MIC" },
	{ "IN1R PGA", NULL, "VOUT_MIC" },

	{ "IN2L PGA", NULL, "VOUT_MIC" },
	{ "IN2R PGA", NULL, "VOUT_MIC" },

	{ "IN2L PGA", NULL, "IN2_PDMCLK" },
	{ "IN2R PGA", NULL, "IN2_PDMCLK" },
	{ "IN2L PGA", NULL, "IN2_PDMDATA" },
	{ "IN2R PGA", NULL, "IN2_PDMDATA" },

	{ "Ultrasonic 1", NULL, "Ultrasonic 1 Input" },
	{ "Ultrasonic 2", NULL, "Ultrasonic 2 Input" },

	{ "Ultrasonic 1 Input", "IN1L", "IN1L PGA" },
	{ "Ultrasonic 1 Input", "IN1R", "IN1R PGA" },
	{ "Ultrasonic 1 Input", "IN2L", "IN2L PGA" },
	{ "Ultrasonic 1 Input", "IN2R", "IN2R PGA" },

	{ "Ultrasonic 2 Input", "IN1L", "IN1L PGA" },
	{ "Ultrasonic 2 Input", "IN1R", "IN1R PGA" },
	{ "Ultrasonic 2 Input", "IN2L", "IN2L PGA" },
	{ "Ultrasonic 2 Input", "IN2R", "IN2R PGA" },

	{ "Ultrasonic 1 Detect", "Switch", "Ultrasonic 1 Input" },
	{ "Ultrasonic 2 Detect", "Switch", "Ultrasonic 2 Input" },

	{ "Ultrasonic Dummy Output", NULL, "Ultrasonic 1 Detect" },
	{ "Ultrasonic Dummy Output", NULL, "Ultrasonic 2 Detect" },

	CS48L32_MIXER_ROUTES("ASP1TX1", "ASP1TX1"),
	CS48L32_MIXER_ROUTES("ASP1TX2", "ASP1TX2"),
	CS48L32_MIXER_ROUTES("ASP1TX3", "ASP1TX3"),
	CS48L32_MIXER_ROUTES("ASP1TX4", "ASP1TX4"),
	CS48L32_MIXER_ROUTES("ASP1TX5", "ASP1TX5"),
	CS48L32_MIXER_ROUTES("ASP1TX6", "ASP1TX6"),
	CS48L32_MIXER_ROUTES("ASP1TX7", "ASP1TX7"),
	CS48L32_MIXER_ROUTES("ASP1TX8", "ASP1TX8"),

	CS48L32_MIXER_ROUTES("ASP2TX1", "ASP2TX1"),
	CS48L32_MIXER_ROUTES("ASP2TX2", "ASP2TX2"),
	CS48L32_MIXER_ROUTES("ASP2TX3", "ASP2TX3"),
	CS48L32_MIXER_ROUTES("ASP2TX4", "ASP2TX4"),

	CS48L32_MIXER_ROUTES("EQ1", "EQ1"),
	CS48L32_MIXER_ROUTES("EQ2", "EQ2"),
	CS48L32_MIXER_ROUTES("EQ3", "EQ3"),
	CS48L32_MIXER_ROUTES("EQ4", "EQ4"),

	CS48L32_MIXER_ROUTES("DRC1L", "DRC1L"),
	CS48L32_MIXER_ROUTES("DRC1R", "DRC1R"),
	CS48L32_MIXER_ROUTES("DRC2L", "DRC2L"),
	CS48L32_MIXER_ROUTES("DRC2R", "DRC2R"),

	CS48L32_MIXER_ROUTES("LHPF1", "LHPF1"),
	CS48L32_MIXER_ROUTES("LHPF2", "LHPF2"),
	CS48L32_MIXER_ROUTES("LHPF3", "LHPF3"),
	CS48L32_MIXER_ROUTES("LHPF4", "LHPF4"),

	CS48L32_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
	CS48L32_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
	CS48L32_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"),
	CS48L32_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"),

	CS48L32_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
	CS48L32_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
	CS48L32_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"),
	CS48L32_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"),

	CS48L32_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
	CS48L32_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),

	CS48L32_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
	CS48L32_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),

	CS48L32_MUX_ROUTES("ISRC3INT1", "ISRC3INT1"),
	CS48L32_MUX_ROUTES("ISRC3INT2", "ISRC3INT2"),

	CS48L32_MUX_ROUTES("ISRC3DEC1", "ISRC3DEC1"),
	CS48L32_MUX_ROUTES("ISRC3DEC2", "ISRC3DEC2"),

	CS48L32_DSP_ROUTES_1_8_SYSCLK("DSP1"),

	{ "DSP Trigger Out", NULL, "DSP1 Trigger Output" },

	{ "DSP1 Trigger Output", "Switch", "DSP1" },

	{ "AUXPDM1 Analog Input", "IN1L", "IN1L PGA" },
	{ "AUXPDM1 Analog Input", "IN1R", "IN1R PGA" },

	{ "AUXPDM2 Analog Input", "IN1L", "IN1L PGA" },
	{ "AUXPDM2 Analog Input", "IN1R", "IN1R PGA" },

	{ "AUXPDM1 Input", "Analog", "AUXPDM1 Analog Input" },
	{ "AUXPDM1 Input", "IN1 Digital", "IN1L PGA" },
	{ "AUXPDM1 Input", "IN1 Digital", "IN1R PGA" },
	{ "AUXPDM1 Input", "IN2 Digital", "IN2L PGA" },
	{ "AUXPDM1 Input", "IN2 Digital", "IN2R PGA" },

	{ "AUXPDM2 Input", "Analog", "AUXPDM2 Analog Input" },
	{ "AUXPDM2 Input", "IN1 Digital", "IN1L PGA" },
	{ "AUXPDM2 Input", "IN1 Digital", "IN1R PGA" },
	{ "AUXPDM2 Input", "IN2 Digital", "IN2L PGA" },
	{ "AUXPDM2 Input", "IN2 Digital", "IN2R PGA" },

	{ "AUXPDM1 Output", "Switch", "AUXPDM1 Input" },
	{ "AUXPDM1_CLK", NULL, "AUXPDM1 Output" },
	{ "AUXPDM1_DOUT", NULL, "AUXPDM1 Output" },

	{ "AUXPDM2 Output", "Switch", "AUXPDM2 Input" },
	{ "AUXPDM2_CLK", NULL, "AUXPDM2 Output" },
	{ "AUXPDM2_DOUT", NULL, "AUXPDM2 Output" },

	{ "MICSUPP", NULL, "SYSCLK" },

	{ "DRC1 Signal Activity", NULL, "DRC1 Activity Output" },
	{ "DRC2 Signal Activity", NULL, "DRC2 Activity Output" },
	{ "DRC1 Activity Output", "Switch", "DRC1L" },
	{ "DRC1 Activity Output", "Switch", "DRC1R" },
	{ "DRC2 Activity Output", "Switch", "DRC2L" },
	{ "DRC2 Activity Output", "Switch", "DRC2R" },
};

static int cs48l32_compr_open(struct snd_soc_component *component,
			      struct snd_compr_stream *stream)
{
	struct snd_soc_pcm_runtime *rtd = stream->private_data;
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);

	if (strcmp(snd_soc_rtd_to_codec(rtd, 0)->name, "cs48l32-dsp-trace") &&
	    strcmp(snd_soc_rtd_to_codec(rtd, 0)->name, "cs48l32-dsp-voicectrl")) {
		dev_err(cs48l32_codec->core.dev, "No suitable compressed stream for DAI '%s'\n",
			snd_soc_rtd_to_codec(rtd, 0)->name);
		return -EINVAL;
	}

	return wm_adsp_compr_open(&cs48l32_codec->dsp, stream);
}

static const struct snd_compress_ops cs48l32_compress_ops = {
	.open = &cs48l32_compr_open,
	.free = &wm_adsp_compr_free,
	.set_params = &wm_adsp_compr_set_params,
	.get_caps = &wm_adsp_compr_get_caps,
	.trigger = &wm_adsp_compr_trigger,
	.pointer = &wm_adsp_compr_pointer,
	.copy = &wm_adsp_compr_copy,
};

static const struct snd_soc_dai_ops cs48l32_compress_dai_ops = {
	.compress_new = snd_soc_new_compress,
};

static struct snd_soc_dai_driver cs48l32_dai[] = {
	{
		.name = "cs48l32-asp1",
		.id = 1,
		.base = CS48L32_ASP1_ENABLES1,
		.playback = {
			.stream_name = "ASP1 Playback",
			.channels_min = 1,
			.channels_max = 8,
			.rates = CS48L32_RATES,
			.formats = CS48L32_FORMATS,
		},
		.capture = {
			.stream_name = "ASP1 Capture",
			.channels_min = 1,
			.channels_max = 8,
			.rates = CS48L32_RATES,
			.formats = CS48L32_FORMATS,
		 },
		.ops = &cs48l32_dai_ops,
		.symmetric_rate = 1,
		.symmetric_sample_bits = 1,
	},
	{
		.name = "cs48l32-asp2",
		.id = 2,
		.base = CS48L32_ASP2_ENABLES1,
		.playback = {
			.stream_name = "ASP2 Playback",
			.channels_min = 1,
			.channels_max = 4,
			.rates = CS48L32_RATES,
			.formats = CS48L32_FORMATS,
		},
		.capture = {
			.stream_name = "ASP2 Capture",
			.channels_min = 1,
			.channels_max = 4,
			.rates = CS48L32_RATES,
			.formats = CS48L32_FORMATS,
		 },
		.ops = &cs48l32_dai_ops,
		.symmetric_rate = 1,
		.symmetric_sample_bits = 1,
	},
	{
		.name = "cs48l32-cpu-trace",
		.id = 3,
		.capture = {
			.stream_name = "Audio Trace CPU",
			.channels_min = 1,
			.channels_max = 8,
			.rates = CS48L32_RATES,
			.formats = CS48L32_FORMATS,
		},
		.ops = &cs48l32_compress_dai_ops,
	},
	{
		.name = "cs48l32-dsp-trace",
		.id = 4,
		.capture = {
			.stream_name = "Audio Trace DSP",
			.channels_min = 1,
			.channels_max = 8,
			.rates = CS48L32_RATES,
			.formats = CS48L32_FORMATS,
		},
	},
	{
		.name = "cs48l32-cpu-voicectrl",
		.id = 5,
		.capture = {
			.stream_name = "Voice Ctrl CPU",
			.channels_min = 1,
			.channels_max = 8,
			.rates = CS48L32_RATES,
			.formats = CS48L32_FORMATS,
		},
		.ops = &cs48l32_compress_dai_ops,
	},
	{
		.name = "cs48l32-dsp-voicectrl",
		.id = 6,
		.capture = {
			.stream_name = "Voice Ctrl DSP",
			.channels_min = 1,
			.channels_max = 8,
			.rates = CS48L32_RATES,
			.formats = CS48L32_FORMATS,
		},
	},
};

static int cs48l32_init_inputs(struct snd_soc_component *component)
{
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
	struct regmap *regmap = cs48l32_codec->core.regmap;
	unsigned int ana_mode_l, ana_mode_r, dig_mode;
	int i;

	/*
	 * Initialize input modes from the A settings. For muxed inputs the
	 * B settings will be applied if the mux is changed
	 */
	switch (cs48l32_codec->in_type[0][0]) {
	default:
	case CS48L32_IN_TYPE_DIFF:
		ana_mode_l = 0;
		break;
	case CS48L32_IN_TYPE_SE:
		ana_mode_l = 1 << CS48L32_INx_SRC_SHIFT;
		break;
	}

	switch (cs48l32_codec->in_type[1][0]) {
	default:
	case CS48L32_IN_TYPE_DIFF:
		ana_mode_r = 0;
		break;
	case CS48L32_IN_TYPE_SE:
		ana_mode_r = 1 << CS48L32_INx_SRC_SHIFT;
		break;
	}

	dev_dbg(cs48l32_codec->core.dev, "IN1_1 Analogue mode=#%x,#%x\n",
		ana_mode_l, ana_mode_r);

	regmap_update_bits(regmap,
			   CS48L32_IN1L_CONTROL1,
			   CS48L32_INx_SRC_MASK,
			   ana_mode_l);

	regmap_update_bits(regmap,
			   CS48L32_IN1R_CONTROL1,
			   CS48L32_INx_SRC_MASK,
			   ana_mode_r);

	for (i = 0; i < ARRAY_SIZE(cs48l32_codec->pdm_sup); i++) {
		dig_mode = cs48l32_codec->pdm_sup[i] << CS48L32_IN1_PDM_SUP_SHIFT;

		dev_dbg(cs48l32_codec->core.dev, "IN%d PDM_SUP=#%x\n", i + 1, dig_mode);

		regmap_update_bits(regmap,
				   CS48L32_INPUT1_CONTROL1 + (i * 0x40),
				   CS48L32_IN1_PDM_SUP_MASK, dig_mode);
	}

	return 0;
}

static int cs48l32_init_dai(struct cs48l32_codec *cs48l32_codec, int id)
{
	struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[id];

	dai_priv->clk = CS48L32_CLK_SYSCLK_1;
	dai_priv->constraint = cs48l32_constraint;

	return 0;
}

static int cs48l32_init_eq(struct cs48l32_codec *cs48l32_codec)
{
	struct regmap *regmap = cs48l32_codec->core.regmap;
	unsigned int reg = CS48L32_EQ1_BAND1_COEFF1, mode;
	__be16 *data;
	int i, ret;

	ret = regmap_read(regmap, CS48L32_EQ_CONTROL2, &mode);
	if (ret < 0) {
		dev_err(cs48l32_codec->core.dev, "Error reading EQ mode: %d\n", ret);
		goto out;
	}

	for (i = 0; i < 4; ++i) {
		cs48l32_codec->eq_mode[i] = (mode >> i) & 0x1;

		data = &cs48l32_codec->eq_coefficients[i][0];
		ret = regmap_raw_read(regmap, reg + (i * 68), data,
				      CS48L32_EQ_BLOCK_SZ);
		if (ret < 0) {
			dev_err(cs48l32_codec->core.dev,
				"Error reading EQ coefficients: %d\n", ret);
			goto out;
		}
	}

out:
	return ret;
}

static int cs48l32_component_probe(struct snd_soc_component *component)
{
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
	int i, ret;

	snd_soc_component_init_regmap(component, cs48l32_codec->core.regmap);

	ret = cs48l32_init_inputs(component);
	if (ret)
		return ret;

	for (i = 0; i < ARRAY_SIZE(cs48l32_dai); i++)
		cs48l32_init_dai(cs48l32_codec, i);

	ret = cs48l32_init_eq(cs48l32_codec);
	if (ret)
		return ret;

	wm_adsp2_component_probe(&cs48l32_codec->dsp, component);

	/* Unmask DSP IRQs */
	regmap_clear_bits(cs48l32_codec->core.regmap, CS48L32_IRQ1_MASK_7,
			  CS48L32_DSP1_MPU_ERR_EINT1_MASK | CS48L32_DSP1_WDT_EXPIRE_EINT1_MASK);
	regmap_clear_bits(cs48l32_codec->core.regmap, CS48L32_IRQ1_MASK_9,
			  CS48L32_DSP1_IRQ0_EINT1_MASK);

	return 0;
}

static void cs48l32_component_remove(struct snd_soc_component *component)
{
	struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);

	/* Mask DSP IRQs */
	regmap_set_bits(cs48l32_codec->core.regmap, CS48L32_IRQ1_MASK_7,
			CS48L32_DSP1_MPU_ERR_EINT1_MASK | CS48L32_DSP1_WDT_EXPIRE_EINT1_MASK);
	regmap_set_bits(cs48l32_codec->core.regmap, CS48L32_IRQ1_MASK_9,
			CS48L32_DSP1_IRQ0_EINT1_MASK);

	wm_adsp2_component_remove(&cs48l32_codec->dsp, component);
}

static const struct snd_soc_component_driver cs48l32_soc_component_drv = {
	.probe			= &cs48l32_component_probe,
	.remove			= &cs48l32_component_remove,
	.set_sysclk		= &cs48l32_set_sysclk,
	.set_pll		= &cs48l32_set_fll,
	.name			= "cs48l32-codec",
	.compress_ops		= &cs48l32_compress_ops,
	.controls		= cs48l32_snd_controls,
	.num_controls		= ARRAY_SIZE(cs48l32_snd_controls),
	.dapm_widgets		= cs48l32_dapm_widgets,
	.num_dapm_widgets	= ARRAY_SIZE(cs48l32_dapm_widgets),
	.dapm_routes		= cs48l32_dapm_routes,
	.num_dapm_routes	= ARRAY_SIZE(cs48l32_dapm_routes),
	.use_pmdown_time	= 1,
	.endianness		= 1,
};

static int cs48l32_prop_read_u32_array(struct cs48l32_codec *cs48l32_codec,
				       const char *propname,
				       u32 *dest,
				       int n_max)
{
	struct cs48l32 *cs48l32 = &cs48l32_codec->core;
	int ret;

	ret = device_property_read_u32_array(cs48l32->dev, propname, dest, n_max);
	if (ret == -EINVAL)
		return -ENOENT;

	if (ret < 0)
		return dev_err_probe(cs48l32->dev, ret, "%s malformed\n", propname);

	return 0;
}

static void cs48l32_prop_get_in_type(struct cs48l32_codec *cs48l32_codec)
{
	const char *propname = "cirrus,in-type";
	u32 tmp[CS48L32_MAX_ANALOG_INPUT * CS48L32_MAX_IN_MUX_WAYS];
	int i, in_idx, mux_way_idx, ret;

	static_assert(ARRAY_SIZE(tmp) ==
		      ARRAY_SIZE(cs48l32_codec->in_type) * ARRAY_SIZE(cs48l32_codec->in_type[0]));

	ret = cs48l32_prop_read_u32_array(cs48l32_codec, propname, tmp, ARRAY_SIZE(tmp));
	if (ret < 0)
		return;

	in_idx = 0;
	mux_way_idx = 0;
	for (i = 0; i < ARRAY_SIZE(tmp); ++i) {
		switch (tmp[i]) {
		case CS48L32_IN_TYPE_DIFF:
		case CS48L32_IN_TYPE_SE:
			cs48l32_codec->in_type[in_idx][mux_way_idx] = tmp[i];
			break;
		default:
			dev_warn(cs48l32_codec->core.dev, "Illegal %s value %d ignored\n",
				 propname, tmp[i]);
			break;
		}

		/*
		 * Property array is [mux_way][in_channel]. Swap to
		 * [in_channel][mux_way] for convenience.
		 */
		if (++in_idx == ARRAY_SIZE(cs48l32_codec->in_type)) {
			in_idx = 0;
			++mux_way_idx;
		}
	}
}

static void cs48l32_prop_get_pdm_sup(struct cs48l32_codec *cs48l32_codec)
{
	const char *propname = "cirrus,pdm-sup";
	u32 tmp[CS48L32_MAX_ANALOG_INPUT];
	int i;

	static_assert(ARRAY_SIZE(tmp) == ARRAY_SIZE(cs48l32_codec->pdm_sup));

	cs48l32_prop_read_u32_array(cs48l32_codec, propname, tmp, ARRAY_SIZE(tmp));

	for (i = 0; i < ARRAY_SIZE(cs48l32_codec->pdm_sup); i++) {
		switch (tmp[i]) {
		case CS48L32_PDM_SUP_VOUT_MIC:
		case CS48L32_PDM_SUP_MICBIAS1:
			cs48l32_codec->pdm_sup[i] = tmp[i];
			break;
		default:
			dev_warn(cs48l32_codec->core.dev, "Illegal %s value %d ignored\n",
				 propname, cs48l32_codec->pdm_sup[i]);
			break;
		}
	}
}

static void cs48l32_handle_properties(struct cs48l32_codec *cs48l32_codec)
{
	cs48l32_prop_get_in_type(cs48l32_codec);
	cs48l32_prop_get_pdm_sup(cs48l32_codec);
}

static int cs48l32_request_interrupt(struct cs48l32_codec *cs48l32_codec)
{
	int irq = cs48l32_codec->core.irq;
	int ret;

	if (irq < 1)
		return 0;

	/*
	 * Don't use devm because this must be freed before destroying the
	 * rest of the driver
	 */
	ret = request_threaded_irq(irq, NULL, cs48l32_irq,
				   IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW,
				   "cs48l32", cs48l32_codec);
	if (ret)
		return dev_err_probe(cs48l32_codec->core.dev, ret, "Failed to get IRQ\n");

	return 0;
}

static int cs48l32_create_codec_component(struct cs48l32_codec *cs48l32_codec)
{
	struct wm_adsp *dsp;
	int ret;

	ASSERT_STRUCT_OFFSET(struct cs48l32_codec, dsp, 0);
	static_assert(ARRAY_SIZE(cs48l32_dai) == ARRAY_SIZE(cs48l32_codec->dai));

	cs48l32_handle_properties(cs48l32_codec);

	dsp = &cs48l32_codec->dsp;
	dsp->part = "cs48l32";
	dsp->cs_dsp.num = 1;
	dsp->cs_dsp.type = WMFW_HALO;
	dsp->cs_dsp.rev = 0;
	dsp->cs_dsp.dev = cs48l32_codec->core.dev;
	dsp->cs_dsp.regmap = cs48l32_codec->core.regmap;
	dsp->cs_dsp.base = CS48L32_DSP1_CLOCK_FREQ;
	dsp->cs_dsp.base_sysinfo = CS48L32_DSP1_SYS_INFO_ID;
	dsp->cs_dsp.mem = cs48l32_dsp1_regions;
	dsp->cs_dsp.num_mems = ARRAY_SIZE(cs48l32_dsp1_regions);
	dsp->pre_run = cs48l32_dsp_pre_run;

	ret = wm_halo_init(dsp);
	if (ret != 0)
		return ret;

	cs48l32_codec->fll.codec = cs48l32_codec;
	cs48l32_codec->fll.id = 1;
	cs48l32_codec->fll.base = CS48L32_FLL1_CONTROL1;
	cs48l32_codec->fll.sts_addr = CS48L32_IRQ1_STS_6;
	cs48l32_codec->fll.sts_mask = CS48L32_FLL1_LOCK_STS1_MASK;
	cs48l32_init_fll(&cs48l32_codec->fll);

	ret = cs48l32_request_interrupt(cs48l32_codec);
	if (ret)
		goto err_dsp;

	ret = devm_snd_soc_register_component(cs48l32_codec->core.dev,
					      &cs48l32_soc_component_drv,
					      cs48l32_dai,
					      ARRAY_SIZE(cs48l32_dai));
	if (ret < 0) {
		dev_err_probe(cs48l32_codec->core.dev, ret, "Failed to register component\n");
		goto err_dsp;
	}

	return 0;

err_dsp:
	wm_adsp2_remove(&cs48l32_codec->dsp);

	return ret;
}

static int cs48l32_wait_for_boot(struct cs48l32 *cs48l32)
{
	unsigned int val;
	int ret;

	ret = regmap_read_poll_timeout(cs48l32->regmap, CS48L32_IRQ1_EINT_2, val,
				       ((val < 0xffffffff) && (val & CS48L32_BOOT_DONE_EINT1_MASK)),
				       1000, CS48L32_BOOT_TIMEOUT_US);
	if (ret) {
		dev_err(cs48l32->dev, "BOOT_DONE timed out\n");
		return -ETIMEDOUT;
	}

	ret = regmap_read(cs48l32->regmap, CS48L32_MCU_CTRL1, &val);
	if (ret) {
		dev_err(cs48l32->dev, "Failed to read MCU_CTRL1: %d\n", ret);
		return ret;
	}

	if (val & BIT(CS48L32_MCU_STS_SHIFT)) {
		dev_err(cs48l32->dev, "MCU boot failed\n");
		return -EIO;
	}

	pm_runtime_mark_last_busy(cs48l32->dev);

	return 0;
}

static int cs48l32_soft_reset(struct cs48l32 *cs48l32)
{
	int ret;

	ret = regmap_write(cs48l32->regmap, CS48L32_SFT_RESET, CS48L32_SFT_RESET_MAGIC);
	if (ret != 0) {
		dev_err(cs48l32->dev, "Failed to write soft reset: %d\n", ret);
		return ret;
	}

	usleep_range(CS48L32_SOFT_RESET_US, CS48L32_SOFT_RESET_US + 1000);

	return 0;
}

static void cs48l32_enable_hard_reset(struct cs48l32 *cs48l32)
{
	if (cs48l32->reset_gpio)
		gpiod_set_raw_value_cansleep(cs48l32->reset_gpio, 0);
}

static void cs48l32_disable_hard_reset(struct cs48l32 *cs48l32)
{
	if (cs48l32->reset_gpio) {
		gpiod_set_raw_value_cansleep(cs48l32->reset_gpio, 1);
		usleep_range(CS48L32_HARD_RESET_MIN_US, CS48L32_HARD_RESET_MIN_US + 1000);
	}
}

static int cs48l32_runtime_resume(struct device *dev)
{
	struct cs48l32_codec *cs48l32_codec = dev_get_drvdata(dev);
	struct cs48l32 *cs48l32 = &cs48l32_codec->core;
	unsigned int val;
	int ret;

	ret = regulator_enable(cs48l32->vdd_d);
	if (ret) {
		dev_err(cs48l32->dev, "Failed to enable VDD_D: %d\n", ret);
		return ret;
	}

	usleep_range(CS48L32_SOFT_RESET_US, CS48L32_SOFT_RESET_US + 1000);

	regcache_cache_only(cs48l32->regmap, false);

	ret = cs48l32_wait_for_boot(cs48l32);
	if (ret)
		goto err;

	/* Check whether registers reset during suspend */
	regmap_read(cs48l32->regmap, CS48L32_CTRL_IF_DEBUG3, &val);
	if (!val)
		regcache_mark_dirty(cs48l32->regmap);
	else
		dev_dbg(cs48l32->dev, "Did not reset during suspend\n");

	ret = regcache_sync(cs48l32->regmap);
	if (ret) {
		dev_err(cs48l32->dev, "Failed to restore register cache\n");
		goto err;
	}

	return 0;

err:
	regcache_cache_only(cs48l32->regmap, true);
	regulator_disable(cs48l32->vdd_d);

	return ret;
}

static int cs48l32_runtime_suspend(struct device *dev)
{
	struct cs48l32_codec *cs48l32_codec = dev_get_drvdata(dev);
	struct cs48l32 *cs48l32 = &cs48l32_codec->core;

	/* Flag to detect if the registers reset during suspend */
	regmap_write(cs48l32->regmap, CS48L32_CTRL_IF_DEBUG3, 1);

	regcache_cache_only(cs48l32->regmap, true);
	regulator_disable(cs48l32->vdd_d);

	return 0;
}

static const struct dev_pm_ops cs48l32_pm_ops = {
	RUNTIME_PM_OPS(cs48l32_runtime_suspend, cs48l32_runtime_resume, NULL)
};

static int cs48l32_configure_clk32k(struct cs48l32 *cs48l32)
{
	int ret = 0;

	ret = clk_prepare_enable(cs48l32->mclk1);
	if (ret)
		return dev_err_probe(cs48l32->dev, ret, "Failed to enable 32k clock\n");

	ret = regmap_update_bits(cs48l32->regmap, CS48L32_CLOCK32K,
				 CS48L32_CLK_32K_EN_MASK | CS48L32_CLK_32K_SRC_MASK,
				 CS48L32_CLK_32K_EN_MASK | CS48L32_32K_MCLK1);
	if (ret) {
		clk_disable_unprepare(cs48l32->mclk1);
		return dev_err_probe(cs48l32->dev, ret, "Failed to init 32k clock\n");
	}

	return 0;
}

static int cs48l32_get_clocks(struct cs48l32 *cs48l32)
{
	cs48l32->mclk1 = devm_clk_get_optional(cs48l32->dev, "mclk1");
	if (IS_ERR(cs48l32->mclk1))
		return dev_err_probe(cs48l32->dev, PTR_ERR(cs48l32->mclk1),
				     "Failed to get mclk1\n");

	return 0;
}

static int cs48l32_get_reset_gpio(struct cs48l32 *cs48l32)
{
	struct gpio_desc *reset;

	reset = devm_gpiod_get_optional(cs48l32->dev, "reset", GPIOD_OUT_LOW);
	if (IS_ERR(reset))
		return dev_err_probe(cs48l32->dev, PTR_ERR(reset), "Failed to request /RESET\n");

	/* ACPI can override the GPIOD_OUT_LOW so ensure it starts low */
	gpiod_set_raw_value_cansleep(reset, 0);

	cs48l32->reset_gpio = reset;

	return 0;
}

static int cs48l32_spi_probe(struct spi_device *spi)
{
	struct device *dev = &spi->dev;
	struct cs48l32_codec *cs48l32_codec;
	struct cs48l32 *cs48l32;
	unsigned int hwid, rev, otp_rev;
	int i, ret;

	cs48l32_codec = devm_kzalloc(&spi->dev, sizeof(*cs48l32_codec), GFP_KERNEL);
	if (!cs48l32_codec)
		return -ENOMEM;

	cs48l32 = &cs48l32_codec->core;
	cs48l32->dev = dev;
	cs48l32->irq = spi->irq;
	mutex_init(&cs48l32_codec->rate_lock);
	cs48l32_codec->in_vu_reg = CS48L32_INPUT_CONTROL3;

	dev_set_drvdata(cs48l32->dev, cs48l32_codec);

	ret = cs48l32_create_regmap(spi, cs48l32);
	if (ret)
		return dev_err_probe(&spi->dev, ret, "Failed to allocate regmap\n");

	regcache_cache_only(cs48l32->regmap, true);

	ret = cs48l32_get_reset_gpio(cs48l32);
	if (ret)
		return ret;

	ret = cs48l32_get_clocks(cs48l32);
	if (ret)
		return ret;

	static_assert(ARRAY_SIZE(cs48l32_core_supplies) == ARRAY_SIZE(cs48l32->core_supplies));
	for (i = 0; i < ARRAY_SIZE(cs48l32->core_supplies); i++)
		cs48l32->core_supplies[i].supply = cs48l32_core_supplies[i];

	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs48l32->core_supplies),
				      cs48l32->core_supplies);
	if (ret)
		return dev_err_probe(dev, ret, "Failed to request core supplies\n");

	cs48l32->vdd_d = devm_regulator_get(cs48l32->dev, "vdd-d");
	if (IS_ERR(cs48l32->vdd_d))
		return dev_err_probe(dev, PTR_ERR(cs48l32->vdd_d), "Failed to request vdd-d\n");

	ret = regulator_bulk_enable(ARRAY_SIZE(cs48l32->core_supplies), cs48l32->core_supplies);
	if (ret)
		return dev_err_probe(dev, ret, "Failed to enable core supplies\n");

	ret = regulator_enable(cs48l32->vdd_d);
	if (ret) {
		dev_err(dev, "Failed to enable vdd-d: %d\n", ret);
		goto err_enable;
	}

	cs48l32_disable_hard_reset(cs48l32);

	regcache_cache_only(cs48l32->regmap, false);

	/* If we don't have a reset GPIO use a soft reset */
	if (!cs48l32->reset_gpio) {
		ret = cs48l32_soft_reset(cs48l32);
		if (ret)
			goto err_reset;
	}

	ret = cs48l32_wait_for_boot(cs48l32);
	if (ret) {
		dev_err(cs48l32->dev, "Device failed initial boot: %d\n", ret);
		goto err_reset;
	}

	ret = regmap_read(cs48l32->regmap, CS48L32_DEVID, &hwid);
	if (ret) {
		dev_err(dev, "Failed to read ID register: %d\n", ret);
		goto err_reset;
	}
	hwid &= CS48L32_DEVID_MASK;

	switch (hwid) {
	case CS48L32_SILICON_ID:
		break;
	default:
		ret = -ENODEV;
		dev_err_probe(cs48l32->dev, ret, "Unknown device ID: %#x\n", hwid);
		goto err_reset;
	}

	ret = regmap_read(cs48l32->regmap, CS48L32_REVID, &rev);
	if (ret) {
		dev_err(dev, "Failed to read revision register: %d\n", ret);
		goto err_reset;
	}
	rev &= CS48L32_AREVID_MASK | CS48L32_MTLREVID_MASK;

	ret = regmap_read(cs48l32->regmap, CS48L32_OTPID, &otp_rev);
	if (ret) {
		dev_err(dev, "Failed to read OTP revision register: %d\n", ret);
		goto err_reset;
	}
	otp_rev &= CS48L32_OTPID_MASK;

	dev_info(dev, "CS48L%x revision %X%u OTP%u\n", hwid & 0xff,
		 rev >> CS48L32_AREVID_SHIFT, rev & CS48L32_MTLREVID_MASK, otp_rev);

	/* Apply hardware patch */
	ret = cs48l32_apply_patch(cs48l32);
	if (ret) {
		dev_err(cs48l32->dev, "Failed to apply patch %d\n", ret);
		goto err_reset;
	}

	/* BOOT_DONE interrupt is unmasked by default, so mask it */
	ret = regmap_set_bits(cs48l32->regmap, CS48L32_IRQ1_MASK_2, CS48L32_BOOT_DONE_EINT1_MASK);

	ret = cs48l32_configure_clk32k(cs48l32);
	if (ret)
		goto err_reset;

	pm_runtime_set_active(cs48l32->dev);
	pm_runtime_set_autosuspend_delay(cs48l32->dev, 100);
	pm_runtime_use_autosuspend(cs48l32->dev);
	pm_runtime_enable(cs48l32->dev);

	ret = cs48l32_create_codec_component(cs48l32_codec);
	if (ret)
		goto err_clk32k;

	return 0;

err_clk32k:
	clk_disable_unprepare(cs48l32->mclk1);
err_reset:
	cs48l32_enable_hard_reset(cs48l32);
	regulator_disable(cs48l32->vdd_d);
err_enable:
	regulator_bulk_disable(ARRAY_SIZE(cs48l32->core_supplies), cs48l32->core_supplies);

	return ret;
}

static void cs48l32_spi_remove(struct spi_device *spi)
{
	struct cs48l32_codec *cs48l32_codec = spi_get_drvdata(spi);
	struct cs48l32 *cs48l32 = &cs48l32_codec->core;

	/* Remove IRQ handler before destroying anything else */
	if (cs48l32->irq >= 1)
		free_irq(cs48l32->irq, cs48l32_codec);

	pm_runtime_disable(cs48l32->dev);
	regulator_disable(cs48l32->vdd_d);
	clk_disable_unprepare(cs48l32->mclk1);
	cs48l32_enable_hard_reset(cs48l32);
	regulator_bulk_disable(ARRAY_SIZE(cs48l32->core_supplies), cs48l32->core_supplies);

	mutex_destroy(&cs48l32_codec->rate_lock);
}

static const struct of_device_id cs48l32_of_match[] = {
	{ .compatible = "cirrus,cs48l32", },
	{},
};

static const struct spi_device_id cs48l32_spi_ids[] = {
	{ "cs48l32", },
	{ },
};
MODULE_DEVICE_TABLE(spi, cs48l32_spi_ids);

static struct spi_driver cs48l32_spi_driver = {
	.driver = {
		.name		= "cs48l32",
		.pm		= pm_ptr(&cs48l32_pm_ops),
		.of_match_table	= cs48l32_of_match,
	},
	.probe		= &cs48l32_spi_probe,
	.remove		= &cs48l32_spi_remove,
	.id_table	= cs48l32_spi_ids,
};
module_spi_driver(cs48l32_spi_driver);

MODULE_DESCRIPTION("CS48L32 ASoC codec driver");
MODULE_AUTHOR("Stuart Henderson <stuarth@opensource.cirrus.com>");
MODULE_AUTHOR("Piotr Stankiewicz <piotrs@opensource.cirrus.com>");
MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
MODULE_LICENSE("GPL");
