// SPDX-License-Identifier: GPL-2.0-only
/*
* SSM2518 amplifier audio driver
*
* Copyright 2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*/
#include <linux/err.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/gpio/consumer.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include "ssm2518.h"
#define SSM2518_REG_POWER1 0 x00
#define SSM2518_REG_CLOCK 0 x01
#define SSM2518_REG_SAI_CTRL1 0 x02
#define SSM2518_REG_SAI_CTRL2 0 x03
#define SSM2518_REG_CHAN_MAP 0 x04
#define SSM2518_REG_LEFT_VOL 0 x05
#define SSM2518_REG_RIGHT_VOL 0 x06
#define SSM2518_REG_MUTE_CTRL 0 x07
#define SSM2518_REG_FAULT_CTRL 0 x08
#define SSM2518_REG_POWER2 0 x09
#define SSM2518_REG_DRC_1 0 x0a
#define SSM2518_REG_DRC_2 0 x0b
#define SSM2518_REG_DRC_3 0 x0c
#define SSM2518_REG_DRC_4 0 x0d
#define SSM2518_REG_DRC_5 0 x0e
#define SSM2518_REG_DRC_6 0 x0f
#define SSM2518_REG_DRC_7 0 x10
#define SSM2518_REG_DRC_8 0 x11
#define SSM2518_REG_DRC_9 0 x12
#define SSM2518_POWER1_RESET BIT(7 )
#define SSM2518_POWER1_NO_BCLK BIT(5 )
#define SSM2518_POWER1_MCS_MASK (0 xf << 1 )
#define SSM2518_POWER1_MCS_64FS (0 x0 << 1 )
#define SSM2518_POWER1_MCS_128FS (0 x1 << 1 )
#define SSM2518_POWER1_MCS_256FS (0 x2 << 1 )
#define SSM2518_POWER1_MCS_384FS (0 x3 << 1 )
#define SSM2518_POWER1_MCS_512FS (0 x4 << 1 )
#define SSM2518_POWER1_MCS_768FS (0 x5 << 1 )
#define SSM2518_POWER1_MCS_100FS (0 x6 << 1 )
#define SSM2518_POWER1_MCS_200FS (0 x7 << 1 )
#define SSM2518_POWER1_MCS_400FS (0 x8 << 1 )
#define SSM2518_POWER1_SPWDN BIT(0 )
#define SSM2518_CLOCK_ASR BIT(0 )
#define SSM2518_SAI_CTRL1_FMT_MASK (0 x3 << 5 )
#define SSM2518_SAI_CTRL1_FMT_I2S (0 x0 << 5 )
#define SSM2518_SAI_CTRL1_FMT_LJ (0 x1 << 5 )
#define SSM2518_SAI_CTRL1_FMT_RJ_24BIT (0 x2 << 5 )
#define SSM2518_SAI_CTRL1_FMT_RJ_16BIT (0 x3 << 5 )
#define SSM2518_SAI_CTRL1_SAI_MASK (0 x7 << 2 )
#define SSM2518_SAI_CTRL1_SAI_I2S (0 x0 << 2 )
#define SSM2518_SAI_CTRL1_SAI_TDM_2 (0 x1 << 2 )
#define SSM2518_SAI_CTRL1_SAI_TDM_4 (0 x2 << 2 )
#define SSM2518_SAI_CTRL1_SAI_TDM_8 (0 x3 << 2 )
#define SSM2518_SAI_CTRL1_SAI_TDM_16 (0 x4 << 2 )
#define SSM2518_SAI_CTRL1_SAI_MONO (0 x5 << 2 )
#define SSM2518_SAI_CTRL1_FS_MASK (0 x3)
#define SSM2518_SAI_CTRL1_FS_8000_12000 (0 x0)
#define SSM2518_SAI_CTRL1_FS_16000_24000 (0 x1)
#define SSM2518_SAI_CTRL1_FS_32000_48000 (0 x2)
#define SSM2518_SAI_CTRL1_FS_64000_96000 (0 x3)
#define SSM2518_SAI_CTRL2_BCLK_INTERAL BIT(7 )
#define SSM2518_SAI_CTRL2_LRCLK_PULSE BIT(6 )
#define SSM2518_SAI_CTRL2_LRCLK_INVERT BIT(5 )
#define SSM2518_SAI_CTRL2_MSB BIT(4 )
#define SSM2518_SAI_CTRL2_SLOT_WIDTH_MASK (0 x3 << 2 )
#define SSM2518_SAI_CTRL2_SLOT_WIDTH_32 (0 x0 << 2 )
#define SSM2518_SAI_CTRL2_SLOT_WIDTH_24 (0 x1 << 2 )
#define SSM2518_SAI_CTRL2_SLOT_WIDTH_16 (0 x2 << 2 )
#define SSM2518_SAI_CTRL2_BCLK_INVERT BIT(1 )
#define SSM2518_CHAN_MAP_RIGHT_SLOT_OFFSET 4
#define SSM2518_CHAN_MAP_RIGHT_SLOT_MASK 0 xf0
#define SSM2518_CHAN_MAP_LEFT_SLOT_OFFSET 0
#define SSM2518_CHAN_MAP_LEFT_SLOT_MASK 0 x0f
#define SSM2518_MUTE_CTRL_ANA_GAIN BIT(5 )
#define SSM2518_MUTE_CTRL_MUTE_MASTER BIT(0 )
#define SSM2518_POWER2_APWDN BIT(0 )
#define SSM2518_DAC_MUTE BIT(6 )
#define SSM2518_DAC_FS_MASK 0 x07
#define SSM2518_DAC_FS_8000 0 x00
#define SSM2518_DAC_FS_16000 0 x01
#define SSM2518_DAC_FS_32000 0 x02
#define SSM2518_DAC_FS_64000 0 x03
#define SSM2518_DAC_FS_128000 0 x04
struct ssm2518 {
struct regmap *regmap;
bool right_j;
unsigned int sysclk;
const struct snd_pcm_hw_constraint_list *constraints;
struct gpio_desc *enable_gpio;
};
static const struct reg_default ssm2518_reg_defaults[] = {
{ 0 x00, 0 x05 },
{ 0 x01, 0 x00 },
{ 0 x02, 0 x02 },
{ 0 x03, 0 x00 },
{ 0 x04, 0 x10 },
{ 0 x05, 0 x40 },
{ 0 x06, 0 x40 },
{ 0 x07, 0 x81 },
{ 0 x08, 0 x0c },
{ 0 x09, 0 x99 },
{ 0 x0a, 0 x7c },
{ 0 x0b, 0 x5b },
{ 0 x0c, 0 x57 },
{ 0 x0d, 0 x89 },
{ 0 x0e, 0 x8c },
{ 0 x0f, 0 x77 },
{ 0 x10, 0 x26 },
{ 0 x11, 0 x1c },
{ 0 x12, 0 x97 },
};
static const DECLARE_TLV_DB_MINMAX_MUTE(ssm2518_vol_tlv, -7125 , 2400 );
static const DECLARE_TLV_DB_SCALE(ssm2518_compressor_tlv, -3400 , 200 , 0 );
static const DECLARE_TLV_DB_SCALE(ssm2518_expander_tlv, -8100 , 300 , 0 );
static const DECLARE_TLV_DB_SCALE(ssm2518_noise_gate_tlv, -9600 , 300 , 0 );
static const DECLARE_TLV_DB_SCALE(ssm2518_post_drc_tlv, -2400 , 300 , 0 );
static const DECLARE_TLV_DB_RANGE(ssm2518_limiter_tlv,
0 , 7 , TLV_DB_SCALE_ITEM(-2200 , 200 , 0 ),
7 , 15 , TLV_DB_SCALE_ITEM(-800 , 100 , 0 ),
);
static const char * const ssm2518_drc_peak_detector_attack_time_text[] = {
"0 ms" , "0.1 ms" , "0.19 ms" , "0.37 ms" , "0.75 ms" , "1.5 ms" , "3 ms" ,
"6 ms" , "12 ms" , "24 ms" , "48 ms" , "96 ms" , "192 ms" , "384 ms" ,
"768 ms" , "1536 ms" ,
};
static const char * const ssm2518_drc_peak_detector_release_time_text[] = {
"0 ms" , "1.5 ms" , "3 ms" , "6 ms" , "12 ms" , "24 ms" , "48 ms" , "96 ms" ,
"192 ms" , "384 ms" , "768 ms" , "1536 ms" , "3072 ms" , "6144 ms" ,
"12288 ms" , "24576 ms"
};
static const char * const ssm2518_drc_hold_time_text[] = {
"0 ms" , "0.67 ms" , "1.33 ms" , "2.67 ms" , "5.33 ms" , "10.66 ms" ,
"21.32 ms" , "42.64 ms" , "85.28 ms" , "170.56 ms" , "341.12 ms" ,
"682.24 ms" , "1364 ms" ,
};
static SOC_ENUM_SINGLE_DECL(ssm2518_drc_peak_detector_attack_time_enum,
SSM2518_REG_DRC_2, 4 , ssm2518_drc_peak_detector_attack_time_text);
static SOC_ENUM_SINGLE_DECL(ssm2518_drc_peak_detector_release_time_enum,
SSM2518_REG_DRC_2, 0 , ssm2518_drc_peak_detector_release_time_text);
static SOC_ENUM_SINGLE_DECL(ssm2518_drc_attack_time_enum,
SSM2518_REG_DRC_6, 4 , ssm2518_drc_peak_detector_attack_time_text);
static SOC_ENUM_SINGLE_DECL(ssm2518_drc_decay_time_enum,
SSM2518_REG_DRC_6, 0 , ssm2518_drc_peak_detector_release_time_text);
static SOC_ENUM_SINGLE_DECL(ssm2518_drc_hold_time_enum,
SSM2518_REG_DRC_7, 4 , ssm2518_drc_hold_time_text);
static SOC_ENUM_SINGLE_DECL(ssm2518_drc_noise_gate_hold_time_enum,
SSM2518_REG_DRC_7, 0 , ssm2518_drc_hold_time_text);
static SOC_ENUM_SINGLE_DECL(ssm2518_drc_rms_averaging_time_enum,
SSM2518_REG_DRC_9, 0 , ssm2518_drc_peak_detector_release_time_text);
static const struct snd_kcontrol_new ssm2518_snd_controls[] = {
SOC_SINGLE("Playback De-emphasis Switch" , SSM2518_REG_MUTE_CTRL,
4 , 1 , 0 ),
SOC_DOUBLE_R_TLV("Master Playback Volume" , SSM2518_REG_LEFT_VOL,
SSM2518_REG_RIGHT_VOL, 0 , 0 xff, 1 , ssm2518_vol_tlv),
SOC_DOUBLE("Master Playback Switch" , SSM2518_REG_MUTE_CTRL, 2 , 1 , 1 , 1 ),
SOC_SINGLE("Amp Low Power Mode Switch" , SSM2518_REG_POWER2, 4 , 1 , 0 ),
SOC_SINGLE("DAC Low Power Mode Switch" , SSM2518_REG_POWER2, 3 , 1 , 0 ),
SOC_SINGLE("DRC Limiter Switch" , SSM2518_REG_DRC_1, 5 , 1 , 0 ),
SOC_SINGLE("DRC Compressor Switch" , SSM2518_REG_DRC_1, 4 , 1 , 0 ),
SOC_SINGLE("DRC Expander Switch" , SSM2518_REG_DRC_1, 3 , 1 , 0 ),
SOC_SINGLE("DRC Noise Gate Switch" , SSM2518_REG_DRC_1, 2 , 1 , 0 ),
SOC_DOUBLE("DRC Switch" , SSM2518_REG_DRC_1, 0 , 1 , 1 , 0 ),
SOC_SINGLE_TLV("DRC Limiter Threshold Volume" ,
SSM2518_REG_DRC_3, 4 , 15 , 1 , ssm2518_limiter_tlv),
SOC_SINGLE_TLV("DRC Compressor Lower Threshold Volume" ,
SSM2518_REG_DRC_3, 0 , 15 , 1 , ssm2518_compressor_tlv),
SOC_SINGLE_TLV("DRC Expander Upper Threshold Volume" , SSM2518_REG_DRC_4,
4 , 15 , 1 , ssm2518_expander_tlv),
SOC_SINGLE_TLV("DRC Noise Gate Threshold Volume" ,
SSM2518_REG_DRC_4, 0 , 15 , 1 , ssm2518_noise_gate_tlv),
SOC_SINGLE_TLV("DRC Upper Output Threshold Volume" ,
SSM2518_REG_DRC_5, 4 , 15 , 1 , ssm2518_limiter_tlv),
SOC_SINGLE_TLV("DRC Lower Output Threshold Volume" ,
SSM2518_REG_DRC_5, 0 , 15 , 1 , ssm2518_noise_gate_tlv),
SOC_SINGLE_TLV("DRC Post Volume" , SSM2518_REG_DRC_8,
2 , 15 , 1 , ssm2518_post_drc_tlv),
SOC_ENUM("DRC Peak Detector Attack Time" ,
ssm2518_drc_peak_detector_attack_time_enum),
SOC_ENUM("DRC Peak Detector Release Time" ,
ssm2518_drc_peak_detector_release_time_enum),
SOC_ENUM("DRC Attack Time" , ssm2518_drc_attack_time_enum),
SOC_ENUM("DRC Decay Time" , ssm2518_drc_decay_time_enum),
SOC_ENUM("DRC Hold Time" , ssm2518_drc_hold_time_enum),
SOC_ENUM("DRC Noise Gate Hold Time" ,
ssm2518_drc_noise_gate_hold_time_enum),
SOC_ENUM("DRC RMS Averaging Time" , ssm2518_drc_rms_averaging_time_enum),
};
static const struct snd_soc_dapm_widget ssm2518_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DACL" , "HiFi Playback" , SSM2518_REG_POWER2, 1 , 1 ),
SND_SOC_DAPM_DAC("DACR" , "HiFi Playback" , SSM2518_REG_POWER2, 2 , 1 ),
SND_SOC_DAPM_OUTPUT("OUTL" ),
SND_SOC_DAPM_OUTPUT("OUTR" ),
};
static const struct snd_soc_dapm_route ssm2518_routes[] = {
{ "OUTL" , NULL, "DACL" },
{ "OUTR" , NULL, "DACR" },
};
struct ssm2518_mcs_lut {
unsigned int rate;
const unsigned int *sysclks;
};
static const unsigned int ssm2518_sysclks_2048000[] = {
2048000 , 4096000 , 8192000 , 12288000 , 16384000 , 24576000 ,
3200000 , 6400000 , 12800000 , 0
};
static const unsigned int ssm2518_sysclks_2822000[] = {
2822000 , 5644800 , 11289600 , 16934400 , 22579200 , 33868800 ,
4410000 , 8820000 , 17640000 , 0
};
static const unsigned int ssm2518_sysclks_3072000[] = {
3072000 , 6144000 , 12288000 , 16384000 , 24576000 , 38864000 ,
4800000 , 9600000 , 19200000 , 0
};
static const struct ssm2518_mcs_lut ssm2518_mcs_lut[] = {
{ 8000 , ssm2518_sysclks_2048000, },
{ 11025 , ssm2518_sysclks_2822000, },
{ 12000 , ssm2518_sysclks_3072000, },
{ 16000 , ssm2518_sysclks_2048000, },
{ 24000 , ssm2518_sysclks_3072000, },
{ 22050 , ssm2518_sysclks_2822000, },
{ 32000 , ssm2518_sysclks_2048000, },
{ 44100 , ssm2518_sysclks_2822000, },
{ 48000 , ssm2518_sysclks_3072000, },
{ 96000 , ssm2518_sysclks_3072000, },
};
static const unsigned int ssm2518_rates_2048000[] = {
8000 , 16000 , 32000 ,
};
static const struct snd_pcm_hw_constraint_list ssm2518_constraints_2048000 = {
.list = ssm2518_rates_2048000,
.count = ARRAY_SIZE(ssm2518_rates_2048000),
};
static const unsigned int ssm2518_rates_2822000[] = {
11025 , 22050 , 44100 ,
};
static const struct snd_pcm_hw_constraint_list ssm2518_constraints_2822000 = {
.list = ssm2518_rates_2822000,
.count = ARRAY_SIZE(ssm2518_rates_2822000),
};
static const unsigned int ssm2518_rates_3072000[] = {
12000 , 24000 , 48000 , 96000 ,
};
static const struct snd_pcm_hw_constraint_list ssm2518_constraints_3072000 = {
.list = ssm2518_rates_3072000,
.count = ARRAY_SIZE(ssm2518_rates_3072000),
};
static const unsigned int ssm2518_rates_12288000[] = {
8000 , 12000 , 16000 , 24000 , 32000 , 48000 , 96000 ,
};
static const struct snd_pcm_hw_constraint_list ssm2518_constraints_12288000 = {
.list = ssm2518_rates_12288000,
.count = ARRAY_SIZE(ssm2518_rates_12288000),
};
static int ssm2518_lookup_mcs(struct ssm2518 *ssm2518,
unsigned int rate)
{
const unsigned int *sysclks = NULL;
int i;
for (i = 0 ; i < ARRAY_SIZE(ssm2518_mcs_lut); i++) {
if (ssm2518_mcs_lut[i].rate == rate) {
sysclks = ssm2518_mcs_lut[i].sysclks;
break ;
}
}
if (!sysclks)
return -EINVAL;
for (i = 0 ; sysclks[i]; i++) {
if (sysclks[i] == ssm2518->sysclk)
return i;
}
return -EINVAL;
}
static int ssm2518_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 ssm2518 *ssm2518 = snd_soc_component_get_drvdata(component);
unsigned int rate = params_rate(params);
unsigned int ctrl1, ctrl1_mask;
int mcs;
int ret;
mcs = ssm2518_lookup_mcs(ssm2518, rate);
if (mcs < 0 )
return mcs;
ctrl1_mask = SSM2518_SAI_CTRL1_FS_MASK;
if (rate >= 8000 && rate <= 12000 )
ctrl1 = SSM2518_SAI_CTRL1_FS_8000_12000;
else if (rate >= 16000 && rate <= 24000 )
ctrl1 = SSM2518_SAI_CTRL1_FS_16000_24000;
else if (rate >= 32000 && rate <= 48000 )
ctrl1 = SSM2518_SAI_CTRL1_FS_32000_48000;
else if (rate >= 64000 && rate <= 96000 )
ctrl1 = SSM2518_SAI_CTRL1_FS_64000_96000;
else
return -EINVAL;
if (ssm2518->right_j) {
switch (params_width(params)) {
case 16 :
ctrl1 |= SSM2518_SAI_CTRL1_FMT_RJ_16BIT;
break ;
case 24 :
ctrl1 |= SSM2518_SAI_CTRL1_FMT_RJ_24BIT;
break ;
default :
return -EINVAL;
}
ctrl1_mask |= SSM2518_SAI_CTRL1_FMT_MASK;
}
/* Disable auto samplerate detection */
ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_CLOCK,
SSM2518_CLOCK_ASR, SSM2518_CLOCK_ASR);
if (ret < 0 )
return ret;
ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_SAI_CTRL1,
ctrl1_mask, ctrl1);
if (ret < 0 )
return ret;
return regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1,
SSM2518_POWER1_MCS_MASK, mcs << 1 );
}
static int ssm2518_mute(struct snd_soc_dai *dai, int mute, int direction)
{
struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(dai->component);
unsigned int val;
if (mute)
val = SSM2518_MUTE_CTRL_MUTE_MASTER;
else
val = 0 ;
return regmap_update_bits(ssm2518->regmap, SSM2518_REG_MUTE_CTRL,
SSM2518_MUTE_CTRL_MUTE_MASTER, val);
}
static int ssm2518_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(dai->component);
unsigned int ctrl1 = 0 , ctrl2 = 0 ;
bool invert_fclk;
int ret;
switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
case SND_SOC_DAIFMT_CBC_CFC:
break ;
default :
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
invert_fclk = false ;
break ;
case SND_SOC_DAIFMT_IB_NF:
ctrl2 |= SSM2518_SAI_CTRL2_BCLK_INVERT;
invert_fclk = false ;
break ;
case SND_SOC_DAIFMT_NB_IF:
invert_fclk = true ;
break ;
case SND_SOC_DAIFMT_IB_IF:
ctrl2 |= SSM2518_SAI_CTRL2_BCLK_INVERT;
invert_fclk = true ;
break ;
default :
return -EINVAL;
}
ssm2518->right_j = false ;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
ctrl1 |= SSM2518_SAI_CTRL1_FMT_I2S;
break ;
case SND_SOC_DAIFMT_LEFT_J:
ctrl1 |= SSM2518_SAI_CTRL1_FMT_LJ;
invert_fclk = !invert_fclk;
break ;
case SND_SOC_DAIFMT_RIGHT_J:
ctrl1 |= SSM2518_SAI_CTRL1_FMT_RJ_24BIT;
ssm2518->right_j = true ;
invert_fclk = !invert_fclk;
break ;
case SND_SOC_DAIFMT_DSP_A:
ctrl2 |= SSM2518_SAI_CTRL2_LRCLK_PULSE;
ctrl1 |= SSM2518_SAI_CTRL1_FMT_I2S;
invert_fclk = false ;
break ;
case SND_SOC_DAIFMT_DSP_B:
ctrl2 |= SSM2518_SAI_CTRL2_LRCLK_PULSE;
ctrl1 |= SSM2518_SAI_CTRL1_FMT_LJ;
invert_fclk = false ;
break ;
default :
return -EINVAL;
}
if (invert_fclk)
ctrl2 |= SSM2518_SAI_CTRL2_LRCLK_INVERT;
ret = regmap_write(ssm2518->regmap, SSM2518_REG_SAI_CTRL1, ctrl1);
if (ret)
return ret;
return regmap_write(ssm2518->regmap, SSM2518_REG_SAI_CTRL2, ctrl2);
}
static int ssm2518_set_power(struct ssm2518 *ssm2518, bool enable)
{
int ret = 0 ;
if (!enable) {
ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1,
SSM2518_POWER1_SPWDN, SSM2518_POWER1_SPWDN);
regcache_mark_dirty(ssm2518->regmap);
}
if (ssm2518->enable_gpio)
gpiod_set_value_cansleep(ssm2518->enable_gpio, enable);
regcache_cache_only(ssm2518->regmap, !enable);
if (enable) {
ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1,
SSM2518_POWER1_SPWDN | SSM2518_POWER1_RESET, 0 x00);
regcache_sync(ssm2518->regmap);
}
return ret;
}
static int ssm2518_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(component);
int ret = 0 ;
switch (level) {
case SND_SOC_BIAS_ON:
break ;
case SND_SOC_BIAS_PREPARE:
break ;
case SND_SOC_BIAS_STANDBY:
if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
ret = ssm2518_set_power(ssm2518, true );
break ;
case SND_SOC_BIAS_OFF:
ret = ssm2518_set_power(ssm2518, false );
break ;
}
return ret;
}
static int ssm2518_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int rx_mask, int slots, int width)
{
struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(dai->component);
unsigned int ctrl1, ctrl2;
int left_slot, right_slot;
int ret;
if (slots == 0 )
return regmap_update_bits(ssm2518->regmap,
SSM2518_REG_SAI_CTRL1, SSM2518_SAI_CTRL1_SAI_MASK,
SSM2518_SAI_CTRL1_SAI_I2S);
if (tx_mask == 0 || rx_mask != 0 )
return -EINVAL;
if (slots == 1 ) {
if (tx_mask != 1 )
return -EINVAL;
left_slot = 0 ;
right_slot = 0 ;
} else {
/* We assume the left channel < right channel */
left_slot = __ffs(tx_mask);
tx_mask &= ~(1 << left_slot);
if (tx_mask == 0 ) {
right_slot = left_slot;
} else {
right_slot = __ffs(tx_mask);
tx_mask &= ~(1 << right_slot);
}
}
if (tx_mask != 0 || left_slot >= slots || right_slot >= slots)
return -EINVAL;
switch (width) {
case 16 :
ctrl2 = SSM2518_SAI_CTRL2_SLOT_WIDTH_16;
break ;
case 24 :
ctrl2 = SSM2518_SAI_CTRL2_SLOT_WIDTH_24;
break ;
case 32 :
ctrl2 = SSM2518_SAI_CTRL2_SLOT_WIDTH_32;
break ;
default :
return -EINVAL;
}
switch (slots) {
case 1 :
ctrl1 = SSM2518_SAI_CTRL1_SAI_MONO;
break ;
case 2 :
ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_2;
break ;
case 4 :
ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_4;
break ;
case 8 :
ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_8;
break ;
case 16 :
ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_16;
break ;
default :
return -EINVAL;
}
ret = regmap_write(ssm2518->regmap, SSM2518_REG_CHAN_MAP,
(left_slot << SSM2518_CHAN_MAP_LEFT_SLOT_OFFSET) |
(right_slot << SSM2518_CHAN_MAP_RIGHT_SLOT_OFFSET));
if (ret)
return ret;
ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_SAI_CTRL1,
SSM2518_SAI_CTRL1_SAI_MASK, ctrl1);
if (ret)
return ret;
return regmap_update_bits(ssm2518->regmap, SSM2518_REG_SAI_CTRL2,
SSM2518_SAI_CTRL2_SLOT_WIDTH_MASK, ctrl2);
}
static int ssm2518_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(dai->component);
if (ssm2518->constraints)
snd_pcm_hw_constraint_list(substream->runtime, 0 ,
SNDRV_PCM_HW_PARAM_RATE, ssm2518->constraints);
return 0 ;
}
#define SSM2518_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32)
static const struct snd_soc_dai_ops ssm2518_dai_ops = {
.startup = ssm2518_startup,
.hw_params = ssm2518_hw_params,
.mute_stream = ssm2518_mute,
.set_fmt = ssm2518_set_dai_fmt,
.set_tdm_slot = ssm2518_set_tdm_slot,
.no_capture_mute = 1 ,
};
static struct snd_soc_dai_driver ssm2518_dai = {
.name = "ssm2518-hifi" ,
.playback = {
.stream_name = "Playback" ,
.channels_min = 2 ,
.channels_max = 2 ,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SSM2518_FORMATS,
},
.ops = &ssm2518_dai_ops,
};
static int ssm2518_set_sysclk(struct snd_soc_component *component, int clk_id,
int source, unsigned int freq, int dir)
{
struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(component);
unsigned int val;
if (clk_id != SSM2518_SYSCLK)
return -EINVAL;
switch (source) {
case SSM2518_SYSCLK_SRC_MCLK:
val = 0 ;
break ;
case SSM2518_SYSCLK_SRC_BCLK:
/* In this case the bitclock is used as the system clock, and
* the bitclock signal needs to be connected to the MCLK pin and
* the BCLK pin is left unconnected */
val = SSM2518_POWER1_NO_BCLK;
break ;
default :
return -EINVAL;
}
switch (freq) {
case 0 :
ssm2518->constraints = NULL;
break ;
case 2048000 :
case 4096000 :
case 8192000 :
case 3200000 :
case 6400000 :
case 12800000 :
ssm2518->constraints = &ssm2518_constraints_2048000;
break ;
case 2822000 :
case 5644800 :
case 11289600 :
case 16934400 :
case 22579200 :
case 33868800 :
case 4410000 :
case 8820000 :
case 17640000 :
ssm2518->constraints = &ssm2518_constraints_2822000;
break ;
case 3072000 :
case 6144000 :
case 38864000 :
case 4800000 :
case 9600000 :
case 19200000 :
ssm2518->constraints = &ssm2518_constraints_3072000;
break ;
case 12288000 :
case 16384000 :
case 24576000 :
ssm2518->constraints = &ssm2518_constraints_12288000;
break ;
default :
return -EINVAL;
}
ssm2518->sysclk = freq;
return regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1,
SSM2518_POWER1_NO_BCLK, val);
}
static const struct snd_soc_component_driver ssm2518_component_driver = {
.set_bias_level = ssm2518_set_bias_level,
.set_sysclk = ssm2518_set_sysclk,
.controls = ssm2518_snd_controls,
.num_controls = ARRAY_SIZE(ssm2518_snd_controls),
.dapm_widgets = ssm2518_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ssm2518_dapm_widgets),
.dapm_routes = ssm2518_routes,
.num_dapm_routes = ARRAY_SIZE(ssm2518_routes),
.use_pmdown_time = 1 ,
.endianness = 1 ,
};
static const struct regmap_config ssm2518_regmap_config = {
.val_bits = 8 ,
.reg_bits = 8 ,
.max_register = SSM2518_REG_DRC_9,
.cache_type = REGCACHE_RBTREE,
.reg_defaults = ssm2518_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(ssm2518_reg_defaults),
};
static int ssm2518_i2c_probe(struct i2c_client *i2c)
{
struct ssm2518 *ssm2518;
int ret;
ssm2518 = devm_kzalloc(&i2c->dev, sizeof (*ssm2518), GFP_KERNEL);
if (ssm2518 == NULL)
return -ENOMEM;
/* Start with enabling the chip */
ssm2518->enable_gpio = devm_gpiod_get_optional(&i2c->dev, NULL,
GPIOD_OUT_HIGH);
ret = PTR_ERR_OR_ZERO(ssm2518->enable_gpio);
if (ret)
return ret;
gpiod_set_consumer_name(ssm2518->enable_gpio, "SSM2518 nSD" );
i2c_set_clientdata(i2c, ssm2518);
ssm2518->regmap = devm_regmap_init_i2c(i2c, &ssm2518_regmap_config);
if (IS_ERR(ssm2518->regmap))
return PTR_ERR(ssm2518->regmap);
/*
* The reset bit is obviously volatile, but we need to be able to cache
* the other bits in the register, so we can't just mark the whole
* register as volatile. Since this is the only place where we'll ever
* touch the reset bit just bypass the cache for this operation.
*/
regcache_cache_bypass(ssm2518->regmap, true );
ret = regmap_write(ssm2518->regmap, SSM2518_REG_POWER1,
SSM2518_POWER1_RESET);
regcache_cache_bypass(ssm2518->regmap, false );
if (ret)
return ret;
ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER2,
SSM2518_POWER2_APWDN, 0 x00);
if (ret)
return ret;
ret = ssm2518_set_power(ssm2518, false );
if (ret)
return ret;
return devm_snd_soc_register_component(&i2c->dev,
&ssm2518_component_driver,
&ssm2518_dai, 1 );
}
#ifdef CONFIG_OF
static const struct of_device_id ssm2518_dt_ids[] = {
{ .compatible = "adi,ssm2518" , },
{ }
};
MODULE_DEVICE_TABLE(of, ssm2518_dt_ids);
#endif
static const struct i2c_device_id ssm2518_i2c_ids[] = {
{ "ssm2518" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ssm2518_i2c_ids);
static struct i2c_driver ssm2518_driver = {
.driver = {
.name = "ssm2518" ,
.of_match_table = of_match_ptr(ssm2518_dt_ids),
},
.probe = ssm2518_i2c_probe,
.id_table = ssm2518_i2c_ids,
};
module_i2c_driver(ssm2518_driver);
MODULE_DESCRIPTION("ASoC SSM2518 driver" );
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>" );
MODULE_LICENSE("GPL" );
Messung V0.5 in Prozent C=96 H=93 G=94
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-06-08)
¤
*© Formatika GbR, Deutschland