Загрузка данных
/*
* Set PCM DAI bit size and sample rate.
* input: params_rate, params_fmt
*/
static int sgtl5000_pcm_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 sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component);
int channels = params_channels(params);
int i2s_ctl = 0;
int stereo;
int ret;
/* sysclk should already set */
if (!sgtl5000->sysclk) {
dev_err(component->dev, "%s: set sysclk first!\n", __func__);
return -EFAULT;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
stereo = SGTL5000_DAC_STEREO;
else
stereo = SGTL5000_ADC_STEREO;
/* set mono to save power */
snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER, stereo,
channels == 1 ? 0 : stereo);
/* set codec clock base on lrclk */
ret = sgtl5000_set_clock(component, params_rate(params));
if (ret)
return ret;
/* set i2s data format */
switch (params_width(params)) {
case 16:
if (sgtl5000->fmt == SND_SOC_DAIFMT_RIGHT_J)
return -EINVAL;
i2s_ctl |= SGTL5000_I2S_DLEN_16 << SGTL5000_I2S_DLEN_SHIFT;
i2s_ctl |= SGTL5000_I2S_SCLKFREQ_32FS <<
SGTL5000_I2S_SCLKFREQ_SHIFT;
break;
case 20:
i2s_ctl |= SGTL5000_I2S_DLEN_20 << SGTL5000_I2S_DLEN_SHIFT;
i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS <<
SGTL5000_I2S_SCLKFREQ_SHIFT;
break;
case 24:
i2s_ctl |= SGTL5000_I2S_DLEN_24 << SGTL5000_I2S_DLEN_SHIFT;
i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS <<
SGTL5000_I2S_SCLKFREQ_SHIFT;
break;
case 32:
if (sgtl5000->fmt == SND_SOC_DAIFMT_RIGHT_J)
return -EINVAL;
i2s_ctl |= SGTL5000_I2S_DLEN_32 << SGTL5000_I2S_DLEN_SHIFT;
i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS <<
SGTL5000_I2S_SCLKFREQ_SHIFT;
break;
default:
return -EINVAL;
}
snd_soc_component_update_bits(component, SGTL5000_CHIP_I2S_CTRL,
SGTL5000_I2S_DLEN_MASK | SGTL5000_I2S_SCLKFREQ_MASK,
i2s_ctl);
return 0;
}
/*
* set dac bias
* common state changes:
* startup:
* off --> standby --> prepare --> on
* standby --> prepare --> on
*
* stop:
* on --> prepare --> standby
*/
static int sgtl5000_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
struct sgtl5000_priv *sgtl = snd_soc_component_get_drvdata(component);
int ret;
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
case SND_SOC_BIAS_STANDBY:
regcache_cache_only(sgtl->regmap, false);
ret = regcache_sync(sgtl->regmap);
if (ret) {
regcache_cache_only(sgtl->regmap, true);
return ret;
}
snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER,
SGTL5000_REFTOP_POWERUP,
SGTL5000_REFTOP_POWERUP);
break;
case SND_SOC_BIAS_OFF:
regcache_cache_only(sgtl->regmap, true);
snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER,
SGTL5000_REFTOP_POWERUP, 0);
break;
}
return 0;
}
#define SGTL5000_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE |\
SNDRV_PCM_FMTBIT_S32_LE)
static const struct snd_soc_dai_ops sgtl5000_ops = {
.hw_params = sgtl5000_pcm_hw_params,
.mute_stream = sgtl5000_mute_stream,
.set_fmt = sgtl5000_set_dai_fmt,
.set_sysclk = sgtl5000_set_dai_sysclk,
.no_capture_mute = 1,
};
static struct snd_soc_dai_driver sgtl5000_dai = {
.name = "sgtl5000",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
/*
* only support 8~48K + 96K,
* TODO modify hw_param to support more
*/
.rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000,
.formats = SGTL5000_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000,
.formats = SGTL5000_FORMATS,
},
.ops = &sgtl5000_ops,
.symmetric_rate = 1,
};
static bool sgtl5000_volatile(struct device *dev, unsigned int reg)
{
switch (reg) {
case SGTL5000_CHIP_ID:
case SGTL5000_CHIP_ADCDAC_CTRL:
case SGTL5000_CHIP_ANA_STATUS:
return true;
}
return false;
}
static bool sgtl5000_readable(struct device *dev, unsigned int reg)
{
switch (reg) {
case SGTL5000_CHIP_ID:
case SGTL5000_CHIP_DIG_POWER:
case SGTL5000_CHIP_CLK_CTRL:
case SGTL5000_CHIP_I2S_CTRL:
case SGTL5000_CHIP_SSS_CTRL:
case SGTL5000_CHIP_ADCDAC_CTRL:
case SGTL5000_CHIP_DAC_VOL:
case SGTL5000_CHIP_PAD_STRENGTH:
case SGTL5000_CHIP_ANA_ADC_CTRL:
case SGTL5000_CHIP_ANA_HP_CTRL:
case SGTL5000_CHIP_ANA_CTRL:
case SGTL5000_CHIP_LINREG_CTRL:
case SGTL5000_CHIP_REF_CTRL:
case SGTL5000_CHIP_MIC_CTRL:
case SGTL5000_CHIP_LINE_OUT_CTRL:
case SGTL5000_CHIP_LINE_OUT_VOL:
case SGTL5000_CHIP_ANA_POWER:
case SGTL5000_CHIP_PLL_CTRL:
case SGTL5000_CHIP_CLK_TOP_CTRL:
case SGTL5000_CHIP_ANA_STATUS:
case SGTL5000_CHIP_SHORT_CTRL:
case SGTL5000_CHIP_ANA_TEST2:
case SGTL5000_DAP_CTRL:
case SGTL5000_DAP_PEQ:
case SGTL5000_DAP_BASS_ENHANCE:
case SGTL5000_DAP_BASS_ENHANCE_CTRL:
case SGTL5000_DAP_AUDIO_EQ:
case SGTL5000_DAP_SURROUND:
case SGTL5000_DAP_FLT_COEF_ACCESS:
case SGTL5000_DAP_COEF_WR_B0_MSB:
case SGTL5000_DAP_COEF_WR_B0_LSB:
case SGTL5000_DAP_EQ_BASS_BAND0:
case SGTL5000_DAP_EQ_BASS_BAND1:
case SGTL5000_DAP_EQ_BASS_BAND2:
case SGTL5000_DAP_EQ_BASS_BAND3:
case SGTL5000_DAP_EQ_BASS_BAND4:
case SGTL5000_DAP_MAIN_CHAN:
case SGTL5000_DAP_MIX_CHAN:
case SGTL5000_DAP_AVC_CTRL:
case SGTL5000_DAP_AVC_THRESHOLD:
case SGTL5000_DAP_AVC_ATTACK:
case SGTL5000_DAP_AVC_DECAY:
case SGTL5000_DAP_COEF_WR_B1_MSB:
case SGTL5000_DAP_COEF_WR_B1_LSB:
case SGTL5000_DAP_COEF_WR_B2_MSB:
case SGTL5000_DAP_COEF_WR_B2_LSB:
case SGTL5000_DAP_COEF_WR_A1_MSB:
case SGTL5000_DAP_COEF_WR_A1_LSB:
case SGTL5000_DAP_COEF_WR_A2_MSB:
case SGTL5000_DAP_COEF_WR_A2_LSB:
return true;
default:
return false;
}
}