// SPDX-License-Identifier: GPL-2.0-only
// cs4234.c -- ALSA SoC CS4234 driver
//
// Copyright (C) 2020 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
//
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/jiffies.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include <linux/workqueue.h>
#include "cs4234.h"
struct cs4234 {
struct device *dev;
struct regmap *regmap;
struct gpio_desc *reset_gpio;
struct regulator_bulk_data core_supplies[2];
int num_core_supplies;
struct completion vq_ramp_complete;
struct delayed_work vq_ramp_delay;
struct clk *mclk;
unsigned long mclk_rate;
unsigned long lrclk_rate;
unsigned int format;
struct snd_ratnum rate_dividers[2];
struct snd_pcm_hw_constraint_ratnums rate_constraint;
};
/* -89.92dB to +6.02dB with step of 0.38dB */
static const DECLARE_TLV_DB_SCALE(dac_tlv, -8992, 38, 0);
static const char * const cs4234_dac14_delay_text[] = {
"0us", "100us", "150us", "200us", "225us", "250us", "275us", "300us",
"325us", "350us", "375us", "400us", "425us", "450us", "475us", "500us",
};
static SOC_ENUM_SINGLE_DECL(cs4234_dac14_group_delay, CS4234_TPS_CTRL,
CS4234_GRP_DELAY_SHIFT, cs4234_dac14_delay_text);
static const char * const cs4234_noise_gate_text[] = {
"72dB", "78dB", "84dB", "90dB", "96dB", "102dB", "138dB", "Disabled",
};
static SOC_ENUM_SINGLE_DECL(cs4234_ll_noise_gate, CS4234_LOW_LAT_CTRL1,
CS4234_LL_NG_SHIFT, cs4234_noise_gate_text);
static SOC_ENUM_SINGLE_DECL(cs4234_dac14_noise_gate, CS4234_DAC_CTRL1,
CS4234_DAC14_NG_SHIFT, cs4234_noise_gate_text);
static SOC_ENUM_SINGLE_DECL(cs4234_dac5_noise_gate, CS4234_DAC_CTRL2,
CS4234_DAC5_NG_SHIFT, cs4234_noise_gate_text);
static const char * const cs4234_dac5_config_fltr_sel_text[] = {
"Interpolation Filter", "Sample and Hold"
};
static SOC_ENUM_SINGLE_DECL(cs4234_dac5_config_fltr_sel, CS4234_DAC_CTRL1,
CS4234_DAC5_CFG_FLTR_SHIFT,
cs4234_dac5_config_fltr_sel_text);
static const char * const cs4234_mute_delay_text[] = {
"1x", "4x", "16x", "64x",
};
static SOC_ENUM_SINGLE_DECL(cs4234_mute_delay, CS4234_VOLUME_MODE,
CS4234_MUTE_DELAY_SHIFT, cs4234_mute_delay_text);
static const char * const cs4234_minmax_delay_text[] = {
"1x", "2x", "4x", "8x", "16x", "32x", "64x", "128x",
};
static SOC_ENUM_SINGLE_DECL(cs4234_min_delay, CS4234_VOLUME_MODE,
CS4234_MIN_DELAY_SHIFT, cs4234_minmax_delay_text);
static SOC_ENUM_SINGLE_DECL(cs4234_max_delay, CS4234_VOLUME_MODE,
CS4234_MAX_DELAY_SHIFT, cs4234_minmax_delay_text);
static int cs4234_dac14_grp_delay_put(struct snd_kcontrol *kctrl,
struct snd_ctl_elem_value *uctrl)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kctrl);
struct cs4234 *cs4234 = snd_soc_component_get_drvdata(component);
struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
unsigned int val = 0;
int ret = 0;
snd_soc_dapm_mutex_lock(dapm);
regmap_read(cs4234->regmap, CS4234_ADC_CTRL2, &val);
if ((val & 0x0F) != 0x0F) { // are all the ADCs powerdown
ret = -EBUSY;
dev_err(component->dev, "Can't change group delay while ADC are ON\n");
goto exit;
}
regmap_read(cs4234->regmap, CS4234_DAC_CTRL4, &val);
if ((val & 0x1F) != 0x1F) { // are all the DACs powerdown
ret = -EBUSY;
dev_err(component->dev, "Can't change group delay while DAC are ON\n");
goto exit;
}
ret = snd_soc_put_enum_double(kctrl, uctrl);
exit:
snd_soc_dapm_mutex_unlock(dapm);
return ret;
}
static void cs4234_vq_ramp_done(struct work_struct *work)
{
struct delayed_work *dw = to_delayed_work(work);
struct cs4234 *cs4234 = container_of(dw, struct cs4234, vq_ramp_delay);
complete_all(&cs4234->vq_ramp_complete);
}
static int cs4234_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
struct cs4234 *cs4234 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_PREPARE:
switch (snd_soc_component_get_bias_level(component)) {
case SND_SOC_BIAS_STANDBY:
wait_for_completion(&cs4234->vq_ramp_complete);
break;
default:
break;
}
break;
default:
break;
}
return 0;
}
static