Commit d6981c3b authored by Mark Brown's avatar Mark Brown

Merge series "ASoC: sun8i-codec: support for AIF2 and AIF3" from Samuel...

Merge series "ASoC: sun8i-codec: support for AIF2 and AIF3" from Samuel Holland <samuel@sholland.org>:

This series adds support the other two AIFs present in the sun8i codec,
which can be used for codec2codec DAI links.

This series first fills out the DAI driver, removing assumptions that
were made for AIF1 (16 bits, 2 channels, certain clock inversions). Some
new logic is required to handle 3 DAIs and the ADC/DAC sharing the same
clock. Finally, it adds the new DAIs, and hooks them up with DAPM
widgets and routes per the hardware topology.

To minimize the number of patches in this series, related device tree
patches (increasing #sound-dai-cells, adding new DAI links) will be sent
separately.

Changes from v1:
  - Patches 1-8 from v1 (DAPM changes) were merged
  - Prefixed AIF constants with "SUN8I_CODEC_" [1, 7, 10, 16, 17]
  - Renamed variables in sun8i_codec_set_fmt for clarity [3]
  - Update sysclk->sysclk_rate if later calls to hw_params change the
    sample rate (thanks Chen-Yu for reminding me of this) [11]
  - Select COMMON_CLK for clk_set_rate_exclusive [12]
  - Add comments and hopefully clarify the clock protection logic [12]
  - Make the error message more concise and put it on one line [12]
  - Drop the "reg" variable holding SUN8I_AIF_CLK_CTRL(dai->id) [15]
  - Rename "div_reg" to "clk_reg" and adjust comments for clarity [17]
  - Improve the AIF2/AIF3 rate mismatch error message [17]
  - Minor grammar/wording fixes in commit messages [2, 4, 7, 8, 16, 17]
  - Added Maxime's Acked-by: [1-2, 4-9, 11, 13-14, 16]

Samuel Holland (17):
  ASoC: sun8i-codec: Prepare to extend the DAI driver
  ASoC: sun8i-codec: Program DAI format before clock inversion
  ASoC: sun8i-codec: Enable all supported clock inversions
  ASoC: sun8i-codec: Use the provided word size
  ASoC: sun8i-codec: Round up the LRCK divisor
  ASoC: sun8i-codec: Correct the BCLK divisor calculation
  ASoC: sun8i-codec: Support the TDM slot binding
  ASoC: sun8i-codec: Enforce symmetric DAI parameters
  ASoC: sun8i-codec: Enable all supported sample rates
  ASoC: sun8i-codec: Automatically set the system sample rate
  ASoC: sun8i-codec: Constrain to compatible sample rates
  ASoC: sun8i-codec: Protect the clock rate while streams are open
  ASoC: sun8i-codec: Require an exact BCLK divisor match
  ASoC: sun8i-codec: Enable all supported PCM formats
  ASoC: sun8i-codec: Generalize AIF clock control
  ASoC: sun8i-codec: Add the AIF2 DAI, widgets, and routes
  ASoC: sun8i-codec: Add the AIF3 DAI, widgets, and routes

 sound/soc/sunxi/Kconfig       |   1 +
 sound/soc/sunxi/sun8i-codec.c | 834 ++++++++++++++++++++++++++++------
 2 files changed, 699 insertions(+), 136 deletions(-)

--
2.26.2
parents 81437cc3 5a7f34ab
......@@ -14,6 +14,7 @@ config SND_SUN8I_CODEC
tristate "Allwinner SUN8I audio codec"
depends on OF
depends on MACH_SUN8I || (ARM64 && ARCH_SUNXI) || COMPILE_TEST
select COMMON_CLK
select REGMAP_MMIO
help
This option enables the digital part of the internal audio codec for
......
......@@ -33,24 +33,26 @@
#define SUN8I_SYSCLK_CTL_SYSCLK_SRC_AIF2CLK (0x1 << 0)
#define SUN8I_MOD_CLK_ENA 0x010
#define SUN8I_MOD_CLK_ENA_AIF1 15
#define SUN8I_MOD_CLK_ENA_AIF2 14
#define SUN8I_MOD_CLK_ENA_AIF3 13
#define SUN8I_MOD_CLK_ENA_ADC 3
#define SUN8I_MOD_CLK_ENA_DAC 2
#define SUN8I_MOD_RST_CTL 0x014
#define SUN8I_MOD_RST_CTL_AIF1 15
#define SUN8I_MOD_RST_CTL_AIF2 14
#define SUN8I_MOD_RST_CTL_AIF3 13
#define SUN8I_MOD_RST_CTL_ADC 3
#define SUN8I_MOD_RST_CTL_DAC 2
#define SUN8I_SYS_SR_CTRL 0x018
#define SUN8I_SYS_SR_CTRL_AIF1_FS 12
#define SUN8I_SYS_SR_CTRL_AIF2_FS 8
#define SUN8I_AIF1CLK_CTRL 0x040
#define SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD 15
#define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV 14
#define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV 13
#define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV 9
#define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV 6
#define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ 4
#define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16 (1 << 4)
#define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT 2
#define SUN8I_AIF_CLK_CTRL(n) (0x040 * (1 + (n)))
#define SUN8I_AIF_CLK_CTRL_MSTR_MOD 15
#define SUN8I_AIF_CLK_CTRL_CLK_INV 13
#define SUN8I_AIF_CLK_CTRL_BCLK_DIV 9
#define SUN8I_AIF_CLK_CTRL_LRCK_DIV 6
#define SUN8I_AIF_CLK_CTRL_WORD_SIZ 4
#define SUN8I_AIF_CLK_CTRL_DATA_FMT 2
#define SUN8I_AIF1_ADCDAT_CTRL 0x044
#define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA 15
#define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA 14
......@@ -70,6 +72,32 @@
#define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR 10
#define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR 9
#define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL 8
#define SUN8I_AIF2_ADCDAT_CTRL 0x084
#define SUN8I_AIF2_ADCDAT_CTRL_AIF2_ADCL_ENA 15
#define SUN8I_AIF2_ADCDAT_CTRL_AIF2_ADCR_ENA 14
#define SUN8I_AIF2_ADCDAT_CTRL_AIF2_ADCL_SRC 10
#define SUN8I_AIF2_ADCDAT_CTRL_AIF2_ADCR_SRC 8
#define SUN8I_AIF2_DACDAT_CTRL 0x088
#define SUN8I_AIF2_DACDAT_CTRL_AIF2_DACL_ENA 15
#define SUN8I_AIF2_DACDAT_CTRL_AIF2_DACR_ENA 14
#define SUN8I_AIF2_DACDAT_CTRL_AIF2_DACL_SRC 10
#define SUN8I_AIF2_DACDAT_CTRL_AIF2_DACR_SRC 8
#define SUN8I_AIF2_MXR_SRC 0x08c
#define SUN8I_AIF2_MXR_SRC_ADCL_MXR_SRC_AIF1DA0L 15
#define SUN8I_AIF2_MXR_SRC_ADCL_MXR_SRC_AIF1DA1L 14
#define SUN8I_AIF2_MXR_SRC_ADCL_MXR_SRC_AIF2DACR 13
#define SUN8I_AIF2_MXR_SRC_ADCL_MXR_SRC_ADCL 12
#define SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_AIF1DA0R 11
#define SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_AIF1DA1R 10
#define SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_AIF2DACL 9
#define SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_ADCR 8
#define SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_AIF1 (0x0 << 0)
#define SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_AIF2 (0x1 << 0)
#define SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_AIF1CLK (0x2 << 0)
#define SUN8I_AIF3_PATH_CTRL 0x0cc
#define SUN8I_AIF3_PATH_CTRL_AIF3_ADC_SRC 10
#define SUN8I_AIF3_PATH_CTRL_AIF2_DAC_SRC 8
#define SUN8I_AIF3_PATH_CTRL_AIF3_PINS_TRI 7
#define SUN8I_ADC_DIG_CTRL 0x100
#define SUN8I_ADC_DIG_CTRL_ENAD 15
#define SUN8I_ADC_DIG_CTRL_ADOUT_DTS 2
......@@ -90,10 +118,44 @@
#define SUN8I_SYSCLK_CTL_AIF2CLK_SRC_MASK GENMASK(5, 4)
#define SUN8I_SYS_SR_CTRL_AIF1_FS_MASK GENMASK(15, 12)
#define SUN8I_SYS_SR_CTRL_AIF2_FS_MASK GENMASK(11, 8)
#define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK GENMASK(12, 9)
#define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK GENMASK(8, 6)
#define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK GENMASK(5, 4)
#define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT_MASK GENMASK(3, 2)
#define SUN8I_AIF_CLK_CTRL_CLK_INV_MASK GENMASK(14, 13)
#define SUN8I_AIF_CLK_CTRL_BCLK_DIV_MASK GENMASK(12, 9)
#define SUN8I_AIF_CLK_CTRL_LRCK_DIV_MASK GENMASK(8, 6)
#define SUN8I_AIF_CLK_CTRL_WORD_SIZ_MASK GENMASK(5, 4)
#define SUN8I_AIF_CLK_CTRL_DATA_FMT_MASK GENMASK(3, 2)
#define SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_MASK GENMASK(1, 0)
#define SUN8I_CODEC_PASSTHROUGH_SAMPLE_RATE 48000
#define SUN8I_CODEC_PCM_FORMATS (SNDRV_PCM_FMTBIT_S8 |\
SNDRV_PCM_FMTBIT_S16_LE |\
SNDRV_PCM_FMTBIT_S20_LE |\
SNDRV_PCM_FMTBIT_S24_LE |\
SNDRV_PCM_FMTBIT_S20_3LE|\
SNDRV_PCM_FMTBIT_S24_3LE)
#define SUN8I_CODEC_PCM_RATES (SNDRV_PCM_RATE_8000_48000|\
SNDRV_PCM_RATE_88200 |\
SNDRV_PCM_RATE_96000 |\
SNDRV_PCM_RATE_176400 |\
SNDRV_PCM_RATE_192000 |\
SNDRV_PCM_RATE_KNOT)
enum {
SUN8I_CODEC_AIF1,
SUN8I_CODEC_AIF2,
SUN8I_CODEC_AIF3,
SUN8I_CODEC_NAIFS
};
struct sun8i_codec_aif {
unsigned int lrck_div_order;
unsigned int sample_rate;
unsigned int slots;
unsigned int slot_width;
unsigned int active_streams : 2;
unsigned int open_streams : 2;
};
struct sun8i_codec_quirks {
bool legacy_widgets : 1;
......@@ -104,8 +166,13 @@ struct sun8i_codec {
struct regmap *regmap;
struct clk *clk_module;
const struct sun8i_codec_quirks *quirks;
struct sun8i_codec_aif aifs[SUN8I_CODEC_NAIFS];
unsigned int sysclk_rate;
int sysclk_refcnt;
};
static struct snd_soc_dai_driver sun8i_codec_dais[];
static int sun8i_codec_runtime_resume(struct device *dev)
{
struct sun8i_codec *scodec = dev_get_drvdata(dev);
......@@ -132,32 +199,34 @@ static int sun8i_codec_runtime_suspend(struct device *dev)
return 0;
}
static int sun8i_codec_get_hw_rate(struct snd_pcm_hw_params *params)
static int sun8i_codec_get_hw_rate(unsigned int sample_rate)
{
unsigned int rate = params_rate(params);
switch (rate) {
case 8000:
switch (sample_rate) {
case 7350:
case 8000:
return 0x0;
case 11025:
return 0x1;
case 12000:
return 0x2;
case 14700:
case 16000:
return 0x3;
case 22050:
return 0x4;
case 24000:
return 0x5;
case 29400:
case 32000:
return 0x6;
case 44100:
return 0x7;
case 48000:
return 0x8;
case 88200:
case 96000:
return 0x9;
case 176400:
case 192000:
return 0xa;
default:
......@@ -165,10 +234,37 @@ static int sun8i_codec_get_hw_rate(struct snd_pcm_hw_params *params)
}
}
static int sun8i_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int sun8i_codec_update_sample_rate(struct sun8i_codec *scodec)
{
unsigned int max_rate = 0;
int hw_rate, i;
for (i = SUN8I_CODEC_AIF1; i < SUN8I_CODEC_NAIFS; ++i) {
struct sun8i_codec_aif *aif = &scodec->aifs[i];
if (aif->active_streams)
max_rate = max(max_rate, aif->sample_rate);
}
/* Set the sample rate for ADC->DAC passthrough when no AIF is active. */
if (!max_rate)
max_rate = SUN8I_CODEC_PASSTHROUGH_SAMPLE_RATE;
hw_rate = sun8i_codec_get_hw_rate(max_rate);
if (hw_rate < 0)
return hw_rate;
regmap_update_bits(scodec->regmap, SUN8I_SYS_SR_CTRL,
SUN8I_SYS_SR_CTRL_AIF1_FS_MASK,
hw_rate << SUN8I_SYS_SR_CTRL_AIF1_FS);
return 0;
}
static int sun8i_codec_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai);
u32 value;
u32 dsp_format, format, invert, value;
/* clock masters */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
......@@ -181,25 +277,81 @@ static int sun8i_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
default:
return -EINVAL;
}
regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
BIT(SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD),
value << SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD);
if (dai->id == SUN8I_CODEC_AIF3) {
/* AIF3 only supports master mode. */
if (value)
return -EINVAL;
/* Use the AIF2 BCLK and LRCK for AIF3. */
regmap_update_bits(scodec->regmap, SUN8I_AIF_CLK_CTRL(dai->id),
SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_MASK,
SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_AIF2);
} else {
regmap_update_bits(scodec->regmap, SUN8I_AIF_CLK_CTRL(dai->id),
BIT(SUN8I_AIF_CLK_CTRL_MSTR_MOD),
value << SUN8I_AIF_CLK_CTRL_MSTR_MOD);
}
/* DAI format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
format = 0x0;
break;
case SND_SOC_DAIFMT_LEFT_J:
format = 0x1;
break;
case SND_SOC_DAIFMT_RIGHT_J:
format = 0x2;
break;
case SND_SOC_DAIFMT_DSP_A:
format = 0x3;
dsp_format = 0x0; /* Set LRCK_INV to 0 */
break;
case SND_SOC_DAIFMT_DSP_B:
format = 0x3;
dsp_format = 0x1; /* Set LRCK_INV to 1 */
break;
default:
return -EINVAL;
}
if (dai->id == SUN8I_CODEC_AIF3) {
/* AIF3 only supports DSP mode. */
if (format != 3)
return -EINVAL;
} else {
regmap_update_bits(scodec->regmap, SUN8I_AIF_CLK_CTRL(dai->id),
SUN8I_AIF_CLK_CTRL_DATA_FMT_MASK,
format << SUN8I_AIF_CLK_CTRL_DATA_FMT);
}
/* clock inversion */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF: /* Normal */
value = 0x0;
invert = 0x0;
break;
case SND_SOC_DAIFMT_IB_IF: /* Inversion */
value = 0x1;
case SND_SOC_DAIFMT_NB_IF: /* Inverted LRCK */
invert = 0x1;
break;
case SND_SOC_DAIFMT_IB_NF: /* Inverted BCLK */
invert = 0x2;
break;
case SND_SOC_DAIFMT_IB_IF: /* Both inverted */
invert = 0x3;
break;
default:
return -EINVAL;
}
regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
BIT(SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV),
value << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV);
if (format == 0x3) {
/* Inverted LRCK is not available in DSP mode. */
if (invert & BIT(0))
return -EINVAL;
/* Instead, the bit selects between DSP A/B formats. */
invert |= dsp_format;
} else {
/*
* It appears that the DAI and the codec in the A33 SoC don't
* share the same polarity for the LRCK signal when they mean
......@@ -210,36 +362,77 @@ static int sun8i_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
* that the codec probably gets it backward, and we have to
* invert the value here.
*/
value ^= scodec->quirks->lrck_inversion;
regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
BIT(SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV),
value << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV);
invert ^= scodec->quirks->lrck_inversion;
}
/* DAI format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
value = 0x0;
break;
case SND_SOC_DAIFMT_LEFT_J:
value = 0x1;
break;
case SND_SOC_DAIFMT_RIGHT_J:
value = 0x2;
break;
case SND_SOC_DAIFMT_DSP_A:
case SND_SOC_DAIFMT_DSP_B:
value = 0x3;
break;
default:
regmap_update_bits(scodec->regmap, SUN8I_AIF_CLK_CTRL(dai->id),
SUN8I_AIF_CLK_CTRL_CLK_INV_MASK,
invert << SUN8I_AIF_CLK_CTRL_CLK_INV);
return 0;
}
static int sun8i_codec_set_tdm_slot(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask,
int slots, int slot_width)
{
struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai);
struct sun8i_codec_aif *aif = &scodec->aifs[dai->id];
if (slot_width && !is_power_of_2(slot_width))
return -EINVAL;
}
regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT_MASK,
value << SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT);
aif->slots = slots;
aif->slot_width = slot_width;
return 0;
}
static const unsigned int sun8i_codec_rates[] = {
7350, 8000, 11025, 12000, 14700, 16000, 22050, 24000,
29400, 32000, 44100, 48000, 88200, 96000, 176400, 192000,
};
static const struct snd_pcm_hw_constraint_list sun8i_codec_all_rates = {
.list = sun8i_codec_rates,
.count = ARRAY_SIZE(sun8i_codec_rates),
};
static const struct snd_pcm_hw_constraint_list sun8i_codec_22M_rates = {
.list = sun8i_codec_rates,
.count = ARRAY_SIZE(sun8i_codec_rates),
.mask = 0x5555,
};
static const struct snd_pcm_hw_constraint_list sun8i_codec_24M_rates = {
.list = sun8i_codec_rates,
.count = ARRAY_SIZE(sun8i_codec_rates),
.mask = 0xaaaa,
};
static int sun8i_codec_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai);
const struct snd_pcm_hw_constraint_list *list;
/* hw_constraints is not relevant for codec2codec DAIs. */
if (dai->id != SUN8I_CODEC_AIF1)
return 0;
if (!scodec->sysclk_refcnt)
list = &sun8i_codec_all_rates;
else if (scodec->sysclk_rate == 22579200)
list = &sun8i_codec_22M_rates;
else if (scodec->sysclk_rate == 24576000)
list = &sun8i_codec_24M_rates;
else
return -EINVAL;
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, list);
}
struct sun8i_codec_clk_div {
u8 div;
u8 val;
......@@ -262,37 +455,37 @@ static const struct sun8i_codec_clk_div sun8i_codec_bclk_div[] = {
{ .div = 192, .val = 13 },
};
static u8 sun8i_codec_get_bclk_div(struct sun8i_codec *scodec,
unsigned int rate,
unsigned int word_size)
static int sun8i_codec_get_bclk_div(unsigned int sysclk_rate,
unsigned int lrck_div_order,
unsigned int sample_rate)
{
unsigned long clk_rate = clk_get_rate(scodec->clk_module);
unsigned int div = clk_rate / rate / word_size / 2;
unsigned int best_val = 0, best_diff = ~0;
unsigned int div = sysclk_rate / sample_rate >> lrck_div_order;
int i;
for (i = 0; i < ARRAY_SIZE(sun8i_codec_bclk_div); i++) {
const struct sun8i_codec_clk_div *bdiv = &sun8i_codec_bclk_div[i];
unsigned int diff = abs(bdiv->div - div);
if (diff < best_diff) {
best_diff = diff;
best_val = bdiv->val;
}
if (bdiv->div == div)
return bdiv->val;
}
return best_val;
return -EINVAL;
}
static int sun8i_codec_get_lrck_div(unsigned int channels,
unsigned int word_size)
static int sun8i_codec_get_lrck_div_order(unsigned int slots,
unsigned int slot_width)
{
unsigned int div = word_size * channels;
unsigned int div = slots * slot_width;
if (div < 16 || div > 256)
return -EINVAL;
return ilog2(div) - 4;
return order_base_2(div);
}
static unsigned int sun8i_codec_get_sysclk_rate(unsigned int sample_rate)
{
return sample_rate % 4000 ? 22579200 : 24576000;
}
static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
......@@ -300,42 +493,225 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai);
int sample_rate, lrck_div;
u8 bclk_div;
struct sun8i_codec_aif *aif = &scodec->aifs[dai->id];
unsigned int sample_rate = params_rate(params);
unsigned int slots = aif->slots ?: params_channels(params);
unsigned int slot_width = aif->slot_width ?: params_width(params);
unsigned int sysclk_rate = sun8i_codec_get_sysclk_rate(sample_rate);
int bclk_div, lrck_div_order, ret, word_size;
u32 clk_reg;
/* word size */
switch (params_width(params)) {
case 8:
word_size = 0x0;
break;
case 16:
word_size = 0x1;
break;
case 20:
word_size = 0x2;
break;
case 24:
word_size = 0x3;
break;
default:
return -EINVAL;
}
regmap_update_bits(scodec->regmap, SUN8I_AIF_CLK_CTRL(dai->id),
SUN8I_AIF_CLK_CTRL_WORD_SIZ_MASK,
word_size << SUN8I_AIF_CLK_CTRL_WORD_SIZ);
/* LRCK divider (BCLK/LRCK ratio) */
lrck_div_order = sun8i_codec_get_lrck_div_order(slots, slot_width);
if (lrck_div_order < 0)
return lrck_div_order;
if (dai->id == SUN8I_CODEC_AIF2 || dai->id == SUN8I_CODEC_AIF3) {
/* AIF2 and AIF3 share AIF2's BCLK and LRCK generation circuitry. */
int partner = (SUN8I_CODEC_AIF2 + SUN8I_CODEC_AIF3) - dai->id;
const struct sun8i_codec_aif *partner_aif = &scodec->aifs[partner];
const char *partner_name = sun8i_codec_dais[partner].name;
if (partner_aif->open_streams &&
(lrck_div_order != partner_aif->lrck_div_order ||
sample_rate != partner_aif->sample_rate)) {
dev_err(dai->dev,
"%s sample and bit rates must match %s when both are used\n",
dai->name, partner_name);
return -EBUSY;
}
clk_reg = SUN8I_AIF_CLK_CTRL(SUN8I_CODEC_AIF2);
} else {
clk_reg = SUN8I_AIF_CLK_CTRL(dai->id);
}
regmap_update_bits(scodec->regmap, clk_reg,
SUN8I_AIF_CLK_CTRL_LRCK_DIV_MASK,
(lrck_div_order - 4) << SUN8I_AIF_CLK_CTRL_LRCK_DIV);
/* BCLK divider (SYSCLK/BCLK ratio) */
bclk_div = sun8i_codec_get_bclk_div(sysclk_rate, lrck_div_order, sample_rate);
if (bclk_div < 0)
return bclk_div;
regmap_update_bits(scodec->regmap, clk_reg,
SUN8I_AIF_CLK_CTRL_BCLK_DIV_MASK,
bclk_div << SUN8I_AIF_CLK_CTRL_BCLK_DIV);
/*
* The CPU DAI handles only a sample of 16 bits. Configure the
* codec to handle this type of sample resolution.
* SYSCLK rate
*
* Clock rate protection is reference counted; but hw_params may be
* called many times per substream, without matching calls to hw_free.
* Protect the clock rate once per AIF, on the first hw_params call
* for the first substream. clk_set_rate() will allow clock rate
* changes on subsequent calls if only one AIF has open streams.
*/
regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK,
SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16);
ret = (aif->open_streams ? clk_set_rate : clk_set_rate_exclusive)(scodec->clk_module,
sysclk_rate);
if (ret == -EBUSY)
dev_err(dai->dev,
"%s sample rate (%u Hz) conflicts with other audio streams\n",
dai->name, sample_rate);
if (ret < 0)
return ret;
bclk_div = sun8i_codec_get_bclk_div(scodec, params_rate(params), 16);
regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK,
bclk_div << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV);
if (!aif->open_streams)
scodec->sysclk_refcnt++;
scodec->sysclk_rate = sysclk_rate;
lrck_div = sun8i_codec_get_lrck_div(params_channels(params),
params_physical_width(params));
if (lrck_div < 0)
return lrck_div;
aif->lrck_div_order = lrck_div_order;
aif->sample_rate = sample_rate;
aif->open_streams |= BIT(substream->stream);
regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK,
lrck_div << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV);
return sun8i_codec_update_sample_rate(scodec);
}
static int sun8i_codec_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai);
struct sun8i_codec_aif *aif = &scodec->aifs[dai->id];
sample_rate = sun8i_codec_get_hw_rate(params);
if (sample_rate < 0)
return sample_rate;
/* Drop references when the last substream for the AIF is freed. */
if (aif->open_streams != BIT(substream->stream))
goto done;
regmap_update_bits(scodec->regmap, SUN8I_SYS_SR_CTRL,
SUN8I_SYS_SR_CTRL_AIF1_FS_MASK,
sample_rate << SUN8I_SYS_SR_CTRL_AIF1_FS);
clk_rate_exclusive_put(scodec->clk_module);
scodec->sysclk_refcnt--;
aif->lrck_div_order = 0;
aif->sample_rate = 0;
done:
aif->open_streams &= ~BIT(substream->stream);
return 0;
}
static const struct snd_soc_dai_ops sun8i_codec_dai_ops = {
.set_fmt = sun8i_codec_set_fmt,
.set_tdm_slot = sun8i_codec_set_tdm_slot,
.startup = sun8i_codec_startup,
.hw_params = sun8i_codec_hw_params,
.hw_free = sun8i_codec_hw_free,
};
static struct snd_soc_dai_driver sun8i_codec_dais[] = {
{
.name = "sun8i-codec-aif1",
.id = SUN8I_CODEC_AIF1,
.ops = &sun8i_codec_dai_ops,
/* capture capabilities */
.capture = {
.stream_name = "AIF1 Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SUN8I_CODEC_PCM_RATES,
.formats = SUN8I_CODEC_PCM_FORMATS,
.sig_bits = 24,
},
/* playback capabilities */
.playback = {
.stream_name = "AIF1 Playback",
.channels_min = 1,
.channels_max = 2,
.rates = SUN8I_CODEC_PCM_RATES,
.formats = SUN8I_CODEC_PCM_FORMATS,
},
.symmetric_rates = true,
.symmetric_channels = true,
.symmetric_samplebits = true,
},
{
.name = "sun8i-codec-aif2",
.id = SUN8I_CODEC_AIF2,
.ops = &sun8i_codec_dai_ops,
/* capture capabilities */
.capture = {
.stream_name = "AIF2 Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SUN8I_CODEC_PCM_RATES,
.formats = SUN8I_CODEC_PCM_FORMATS,
.sig_bits = 24,
},
/* playback capabilities */
.playback = {
.stream_name = "AIF2 Playback",
.channels_min = 1,
.channels_max = 2,
.rates = SUN8I_CODEC_PCM_RATES,
.formats = SUN8I_CODEC_PCM_FORMATS,
},
.symmetric_rates = true,
.symmetric_channels = true,
.symmetric_samplebits = true,
},
{
.name = "sun8i-codec-aif3",
.id = SUN8I_CODEC_AIF3,
.ops = &sun8i_codec_dai_ops,
/* capture capabilities */
.capture = {
.stream_name = "AIF3 Capture",
.channels_min = 1,
.channels_max = 1,
.rates = SUN8I_CODEC_PCM_RATES,
.formats = SUN8I_CODEC_PCM_FORMATS,
.sig_bits = 24,
},
/* playback capabilities */
.playback = {
.stream_name = "AIF3 Playback",
.channels_min = 1,
.channels_max = 1,
.rates = SUN8I_CODEC_PCM_RATES,
.formats = SUN8I_CODEC_PCM_FORMATS,
},
.symmetric_rates = true,
.symmetric_channels = true,
.symmetric_samplebits = true,
},
};
static int sun8i_codec_aif_event(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 sun8i_codec *scodec = snd_soc_component_get_drvdata(component);
struct sun8i_codec_aif *aif = &scodec->aifs[w->sname[3] - '1'];
int stream = w->id == snd_soc_dapm_aif_out;
if (SND_SOC_DAPM_EVENT_ON(event))
aif->active_streams |= BIT(stream);
else
aif->active_streams &= ~BIT(stream);
return sun8i_codec_update_sample_rate(scodec);
}
static const char *const sun8i_aif_stereo_mux_enum_values[] = {
"Stereo", "Reverse Stereo", "Sum Mono", "Mix Mono"
};
......@@ -350,6 +726,29 @@ static const struct snd_kcontrol_new sun8i_aif1_ad0_stereo_mux_control =
SOC_DAPM_ENUM("AIF1 AD0 Stereo Capture Route",
sun8i_aif1_ad0_stereo_mux_enum);
static SOC_ENUM_DOUBLE_DECL(sun8i_aif2_adc_stereo_mux_enum,
SUN8I_AIF2_ADCDAT_CTRL,
SUN8I_AIF2_ADCDAT_CTRL_AIF2_ADCL_SRC,
SUN8I_AIF2_ADCDAT_CTRL_AIF2_ADCR_SRC,
sun8i_aif_stereo_mux_enum_values);
static const struct snd_kcontrol_new sun8i_aif2_adc_stereo_mux_control =
SOC_DAPM_ENUM("AIF2 ADC Stereo Capture Route",
sun8i_aif2_adc_stereo_mux_enum);
static const char *const sun8i_aif3_adc_mux_enum_values[] = {
"None", "AIF2 ADCL", "AIF2 ADCR"
};
static SOC_ENUM_SINGLE_DECL(sun8i_aif3_adc_mux_enum,
SUN8I_AIF3_PATH_CTRL,
SUN8I_AIF3_PATH_CTRL_AIF3_ADC_SRC,
sun8i_aif3_adc_mux_enum_values);
static const struct snd_kcontrol_new sun8i_aif3_adc_mux_control =
SOC_DAPM_ENUM("AIF3 ADC Source Capture Route",
sun8i_aif3_adc_mux_enum);
static const struct snd_kcontrol_new sun8i_aif1_ad0_mixer_controls[] = {
SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital ADC Capture Switch",
SUN8I_AIF1_MXR_SRC,
......@@ -369,6 +768,38 @@ static const struct snd_kcontrol_new sun8i_aif1_ad0_mixer_controls[] = {
SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL, 1, 0),
};
static const struct snd_kcontrol_new sun8i_aif2_adc_mixer_controls[] = {
SOC_DAPM_DOUBLE("AIF2 ADC Mixer AIF1 DA0 Capture Switch",
SUN8I_AIF2_MXR_SRC,
SUN8I_AIF2_MXR_SRC_ADCL_MXR_SRC_AIF1DA0L,
SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_AIF1DA0R, 1, 0),
SOC_DAPM_DOUBLE("AIF2 ADC Mixer AIF1 DA1 Capture Switch",
SUN8I_AIF2_MXR_SRC,
SUN8I_AIF2_MXR_SRC_ADCL_MXR_SRC_AIF1DA1L,
SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_AIF1DA1R, 1, 0),
SOC_DAPM_DOUBLE("AIF2 ADC Mixer AIF2 DAC Rev Capture Switch",
SUN8I_AIF2_MXR_SRC,
SUN8I_AIF2_MXR_SRC_ADCL_MXR_SRC_AIF2DACR,
SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_AIF2DACL, 1, 0),
SOC_DAPM_DOUBLE("AIF2 ADC Mixer ADC Capture Switch",
SUN8I_AIF2_MXR_SRC,
SUN8I_AIF2_MXR_SRC_ADCL_MXR_SRC_ADCL,
SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_ADCR, 1, 0),
};
static const char *const sun8i_aif2_dac_mux_enum_values[] = {
"AIF2", "AIF3+2", "AIF2+3"
};
static SOC_ENUM_SINGLE_DECL(sun8i_aif2_dac_mux_enum,
SUN8I_AIF3_PATH_CTRL,
SUN8I_AIF3_PATH_CTRL_AIF2_DAC_SRC,
sun8i_aif2_dac_mux_enum_values);
static const struct snd_kcontrol_new sun8i_aif2_dac_mux_control =
SOC_DAPM_ENUM("AIF2 DAC Source Playback Route",
sun8i_aif2_dac_mux_enum);
static SOC_ENUM_DOUBLE_DECL(sun8i_aif1_da0_stereo_mux_enum,
SUN8I_AIF1_DACDAT_CTRL,
SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_SRC,
......@@ -379,6 +810,16 @@ static const struct snd_kcontrol_new sun8i_aif1_da0_stereo_mux_control =
SOC_DAPM_ENUM("AIF1 DA0 Stereo Playback Route",
sun8i_aif1_da0_stereo_mux_enum);
static SOC_ENUM_DOUBLE_DECL(sun8i_aif2_dac_stereo_mux_enum,
SUN8I_AIF2_DACDAT_CTRL,
SUN8I_AIF2_DACDAT_CTRL_AIF2_DACL_SRC,
SUN8I_AIF2_DACDAT_CTRL_AIF2_DACR_SRC,
sun8i_aif_stereo_mux_enum_values);
static const struct snd_kcontrol_new sun8i_aif2_dac_stereo_mux_control =
SOC_DAPM_ENUM("AIF2 DAC Stereo Playback Route",
sun8i_aif2_dac_stereo_mux_enum);
static const struct snd_kcontrol_new sun8i_dac_mixer_controls[] = {
SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital DAC Playback Switch",
SUN8I_DAC_MXR_SRC,
......@@ -403,6 +844,9 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("AIF1CLK",
SUN8I_SYSCLK_CTL,
SUN8I_SYSCLK_CTL_AIF1CLK_ENA, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("AIF2CLK",
SUN8I_SYSCLK_CTL,
SUN8I_SYSCLK_CTL_AIF2CLK_ENA, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("SYSCLK",
SUN8I_SYSCLK_CTL,
SUN8I_SYSCLK_CTL_SYSCLK_ENA, 0, NULL, 0),
......@@ -411,6 +855,12 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("CLK AIF1",
SUN8I_MOD_CLK_ENA,
SUN8I_MOD_CLK_ENA_AIF1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("CLK AIF2",
SUN8I_MOD_CLK_ENA,
SUN8I_MOD_CLK_ENA_AIF2, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("CLK AIF3",
SUN8I_MOD_CLK_ENA,
SUN8I_MOD_CLK_ENA_AIF3, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("CLK ADC",
SUN8I_MOD_CLK_ENA,
SUN8I_MOD_CLK_ENA_ADC, 0, NULL, 0),
......@@ -422,6 +872,12 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("RST AIF1",
SUN8I_MOD_RST_CTL,
SUN8I_MOD_RST_CTL_AIF1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("RST AIF2",
SUN8I_MOD_RST_CTL,
SUN8I_MOD_RST_CTL_AIF2, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("RST AIF3",
SUN8I_MOD_RST_CTL,
SUN8I_MOD_RST_CTL_AIF3, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("RST ADC",
SUN8I_MOD_RST_CTL,
SUN8I_MOD_RST_CTL_ADC, 0, NULL, 0),
......@@ -438,39 +894,96 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
SUN8I_DAC_DIG_CTRL_ENDA, 0, NULL, 0),
/* AIF "ADC" Outputs */
SND_SOC_DAPM_AIF_OUT("AIF1 AD0L", "Capture", 0,
SND_SOC_DAPM_AIF_OUT_E("AIF1 AD0L", "AIF1 Capture", 0,
SUN8I_AIF1_ADCDAT_CTRL,
SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA, 0),
SND_SOC_DAPM_AIF_OUT("AIF1 AD0R", "Capture", 1,
SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA, 0,
sun8i_codec_aif_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_AIF_OUT("AIF1 AD0R", "AIF1 Capture", 1,
SUN8I_AIF1_ADCDAT_CTRL,
SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA, 0),
SND_SOC_DAPM_AIF_OUT_E("AIF2 ADCL", "AIF2 Capture", 0,
SUN8I_AIF2_ADCDAT_CTRL,
SUN8I_AIF2_ADCDAT_CTRL_AIF2_ADCL_ENA, 0,
sun8i_codec_aif_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_AIF_OUT("AIF2 ADCR", "AIF2 Capture", 1,
SUN8I_AIF2_ADCDAT_CTRL,
SUN8I_AIF2_ADCDAT_CTRL_AIF2_ADCR_ENA, 0),
SND_SOC_DAPM_AIF_OUT_E("AIF3 ADC", "AIF3 Capture", 0,
SND_SOC_NOPM, 0, 0,
sun8i_codec_aif_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
/* AIF "ADC" Mono/Stereo Muxes */
SND_SOC_DAPM_MUX("AIF1 AD0L Stereo Mux", SND_SOC_NOPM, 0, 0,
&sun8i_aif1_ad0_stereo_mux_control),
SND_SOC_DAPM_MUX("AIF1 AD0R Stereo Mux", SND_SOC_NOPM, 0, 0,
&sun8i_aif1_ad0_stereo_mux_control),
SND_SOC_DAPM_MUX("AIF2 ADCL Stereo Mux", SND_SOC_NOPM, 0, 0,
&sun8i_aif2_adc_stereo_mux_control),
SND_SOC_DAPM_MUX("AIF2 ADCR Stereo Mux", SND_SOC_NOPM, 0, 0,
&sun8i_aif2_adc_stereo_mux_control),
/* AIF "ADC" Output Muxes */
SND_SOC_DAPM_MUX("AIF3 ADC Source Capture Route", SND_SOC_NOPM, 0, 0,
&sun8i_aif3_adc_mux_control),
/* AIF "ADC" Mixers */
SOC_MIXER_ARRAY("AIF1 AD0L Mixer", SND_SOC_NOPM, 0, 0,
sun8i_aif1_ad0_mixer_controls),
SOC_MIXER_ARRAY("AIF1 AD0R Mixer", SND_SOC_NOPM, 0, 0,
sun8i_aif1_ad0_mixer_controls),
SOC_MIXER_ARRAY("AIF2 ADCL Mixer", SND_SOC_NOPM, 0, 0,
sun8i_aif2_adc_mixer_controls),
SOC_MIXER_ARRAY("AIF2 ADCR Mixer", SND_SOC_NOPM, 0, 0,
sun8i_aif2_adc_mixer_controls),
/* AIF "DAC" Input Muxes */
SND_SOC_DAPM_MUX("AIF2 DACL Source", SND_SOC_NOPM, 0, 0,
&sun8i_aif2_dac_mux_control),
SND_SOC_DAPM_MUX("AIF2 DACR Source", SND_SOC_NOPM, 0, 0,
&sun8i_aif2_dac_mux_control),
/* AIF "DAC" Mono/Stereo Muxes */
SND_SOC_DAPM_MUX("AIF1 DA0L Stereo Mux", SND_SOC_NOPM, 0, 0,
&sun8i_aif1_da0_stereo_mux_control),
SND_SOC_DAPM_MUX("AIF1 DA0R Stereo Mux", SND_SOC_NOPM, 0, 0,
&sun8i_aif1_da0_stereo_mux_control),
SND_SOC_DAPM_MUX("AIF2 DACL Stereo Mux", SND_SOC_NOPM, 0, 0,
&sun8i_aif2_dac_stereo_mux_control),
SND_SOC_DAPM_MUX("AIF2 DACR Stereo Mux", SND_SOC_NOPM, 0, 0,
&sun8i_aif2_dac_stereo_mux_control),
/* AIF "DAC" Inputs */
SND_SOC_DAPM_AIF_IN("AIF1 DA0L", "Playback", 0,
SND_SOC_DAPM_AIF_IN_E("AIF1 DA0L", "AIF1 Playback", 0,
SUN8I_AIF1_DACDAT_CTRL,
SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA, 0),
SND_SOC_DAPM_AIF_IN("AIF1 DA0R", "Playback", 1,
SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA, 0,
sun8i_codec_aif_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_AIF_IN("AIF1 DA0R", "AIF1 Playback", 1,
SUN8I_AIF1_DACDAT_CTRL,
SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0),
SND_SOC_DAPM_AIF_IN_E("AIF2 DACL", "AIF2 Playback", 0,
SUN8I_AIF2_DACDAT_CTRL,
SUN8I_AIF2_DACDAT_CTRL_AIF2_DACL_ENA, 0,
sun8i_codec_aif_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_AIF_IN("AIF2 DACR", "AIF2 Playback", 1,
SUN8I_AIF2_DACDAT_CTRL,
SUN8I_AIF2_DACDAT_CTRL_AIF2_DACR_ENA, 0),
SND_SOC_DAPM_AIF_IN_E("AIF3 DAC", "AIF3 Playback", 0,
SND_SOC_NOPM, 0, 0,
sun8i_codec_aif_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
/* ADC Inputs (connected to analog codec DAPM context) */
SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0),
......@@ -500,6 +1013,20 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = {
{ "AIF1 DA0L", NULL, "RST AIF1" },
{ "AIF1 DA0R", NULL, "RST AIF1" },
{ "CLK AIF2", NULL, "AIF2CLK" },
{ "CLK AIF2", NULL, "SYSCLK" },
{ "RST AIF2", NULL, "CLK AIF2" },
{ "AIF2 ADCL", NULL, "RST AIF2" },
{ "AIF2 ADCR", NULL, "RST AIF2" },
{ "AIF2 DACL", NULL, "RST AIF2" },
{ "AIF2 DACR", NULL, "RST AIF2" },
{ "CLK AIF3", NULL, "AIF1CLK" },
{ "CLK AIF3", NULL, "SYSCLK" },
{ "RST AIF3", NULL, "CLK AIF3" },
{ "AIF3 ADC", NULL, "RST AIF3" },
{ "AIF3 DAC", NULL, "RST AIF3" },
{ "CLK ADC", NULL, "SYSCLK" },
{ "RST ADC", NULL, "CLK ADC" },
{ "ADC", NULL, "RST ADC" },
......@@ -516,6 +1043,11 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = {
{ "AIF1 AD0L", NULL, "AIF1 AD0L Stereo Mux" },
{ "AIF1 AD0R", NULL, "AIF1 AD0R Stereo Mux" },
{ "AIF2 ADCL", NULL, "AIF2 ADCL Stereo Mux" },
{ "AIF2 ADCR", NULL, "AIF2 ADCR Stereo Mux" },
{ "AIF3 ADC", NULL, "AIF3 ADC Source Capture Route" },
/* AIF "ADC" Mono/Stereo Mux Routes */
{ "AIF1 AD0L Stereo Mux", "Stereo", "AIF1 AD0L Mixer" },
{ "AIF1 AD0L Stereo Mux", "Reverse Stereo", "AIF1 AD0R Mixer" },
......@@ -531,12 +1063,51 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = {
{ "AIF1 AD0R Stereo Mux", "Mix Mono", "AIF1 AD0L Mixer" },
{ "AIF1 AD0R Stereo Mux", "Mix Mono", "AIF1 AD0R Mixer" },
{ "AIF2 ADCL Stereo Mux", "Stereo", "AIF2 ADCL Mixer" },
{ "AIF2 ADCL Stereo Mux", "Reverse Stereo", "AIF2 ADCR Mixer" },
{ "AIF2 ADCL Stereo Mux", "Sum Mono", "AIF2 ADCL Mixer" },
{ "AIF2 ADCL Stereo Mux", "Sum Mono", "AIF2 ADCR Mixer" },
{ "AIF2 ADCL Stereo Mux", "Mix Mono", "AIF2 ADCL Mixer" },
{ "AIF2 ADCL Stereo Mux", "Mix Mono", "AIF2 ADCR Mixer" },
{ "AIF2 ADCR Stereo Mux", "Stereo", "AIF2 ADCR Mixer" },
{ "AIF2 ADCR Stereo Mux", "Reverse Stereo", "AIF2 ADCL Mixer" },
{ "AIF2 ADCR Stereo Mux", "Sum Mono", "AIF2 ADCL Mixer" },
{ "AIF2 ADCR Stereo Mux", "Sum Mono", "AIF2 ADCR Mixer" },
{ "AIF2 ADCR Stereo Mux", "Mix Mono", "AIF2 ADCL Mixer" },
{ "AIF2 ADCR Stereo Mux", "Mix Mono", "AIF2 ADCR Mixer" },
/* AIF "ADC" Output Mux Routes */
{ "AIF3 ADC Source Capture Route", "AIF2 ADCL", "AIF2 ADCL Mixer" },
{ "AIF3 ADC Source Capture Route", "AIF2 ADCR", "AIF2 ADCR Mixer" },
/* AIF "ADC" Mixer Routes */
{ "AIF1 AD0L Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0L Stereo Mux" },
{ "AIF1 AD0L Mixer", "AIF2 Digital ADC Capture Switch", "AIF2 DACL Source" },
{ "AIF1 AD0L Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCL" },
{ "AIF1 AD0L Mixer", "AIF2 Inv Digital ADC Capture Switch", "AIF2 DACR Source" },
{ "AIF1 AD0R Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0R Stereo Mux" },
{ "AIF1 AD0R Mixer", "AIF2 Digital ADC Capture Switch", "AIF2 DACR Source" },
{ "AIF1 AD0R Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCR" },
{ "AIF1 AD0R Mixer", "AIF2 Inv Digital ADC Capture Switch", "AIF2 DACL Source" },
{ "AIF2 ADCL Mixer", "AIF2 ADC Mixer AIF1 DA0 Capture Switch", "AIF1 DA0L Stereo Mux" },
{ "AIF2 ADCL Mixer", "AIF2 ADC Mixer AIF2 DAC Rev Capture Switch", "AIF2 DACR Source" },
{ "AIF2 ADCL Mixer", "AIF2 ADC Mixer ADC Capture Switch", "ADCL" },
{ "AIF2 ADCR Mixer", "AIF2 ADC Mixer AIF1 DA0 Capture Switch", "AIF1 DA0R Stereo Mux" },
{ "AIF2 ADCR Mixer", "AIF2 ADC Mixer AIF2 DAC Rev Capture Switch", "AIF2 DACL Source" },
{ "AIF2 ADCR Mixer", "AIF2 ADC Mixer ADC Capture Switch", "ADCR" },
/* AIF "DAC" Input Mux Routes */
{ "AIF2 DACL Source", "AIF2", "AIF2 DACL Stereo Mux" },
{ "AIF2 DACL Source", "AIF3+2", "AIF3 DAC" },
{ "AIF2 DACL Source", "AIF2+3", "AIF2 DACL Stereo Mux" },
{ "AIF2 DACR Source", "AIF2", "AIF2 DACR Stereo Mux" },
{ "AIF2 DACR Source", "AIF3+2", "AIF2 DACR Stereo Mux" },
{ "AIF2 DACR Source", "AIF2+3", "AIF3 DAC" },
/* AIF "DAC" Mono/Stereo Mux Routes */
{ "AIF1 DA0L Stereo Mux", "Stereo", "AIF1 DA0L" },
......@@ -553,15 +1124,31 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = {
{ "AIF1 DA0R Stereo Mux", "Mix Mono", "AIF1 DA0L" },
{ "AIF1 DA0R Stereo Mux", "Mix Mono", "AIF1 DA0R" },
{ "AIF2 DACL Stereo Mux", "Stereo", "AIF2 DACL" },
{ "AIF2 DACL Stereo Mux", "Reverse Stereo", "AIF2 DACR" },
{ "AIF2 DACL Stereo Mux", "Sum Mono", "AIF2 DACL" },
{ "AIF2 DACL Stereo Mux", "Sum Mono", "AIF2 DACR" },
{ "AIF2 DACL Stereo Mux", "Mix Mono", "AIF2 DACL" },
{ "AIF2 DACL Stereo Mux", "Mix Mono", "AIF2 DACR" },
{ "AIF2 DACR Stereo Mux", "Stereo", "AIF2 DACR" },
{ "AIF2 DACR Stereo Mux", "Reverse Stereo", "AIF2 DACL" },
{ "AIF2 DACR Stereo Mux", "Sum Mono", "AIF2 DACL" },
{ "AIF2 DACR Stereo Mux", "Sum Mono", "AIF2 DACR" },
{ "AIF2 DACR Stereo Mux", "Mix Mono", "AIF2 DACL" },
{ "AIF2 DACR Stereo Mux", "Mix Mono", "AIF2 DACR" },
/* DAC Output Routes */
{ "DACL", NULL, "DACL Mixer" },
{ "DACR", NULL, "DACR Mixer" },
/* DAC Mixer Routes */
{ "DACL Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0L Stereo Mux" },
{ "DACL Mixer", "AIF2 Digital DAC Playback Switch", "AIF2 DACL Source" },
{ "DACL Mixer", "ADC Digital DAC Playback Switch", "ADCL" },
{ "DACR Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0R Stereo Mux" },
{ "DACR Mixer", "AIF2 Digital DAC Playback Switch", "AIF2 DACR Source" },
{ "DACR Mixer", "ADC Digital DAC Playback Switch", "ADCR" },
};
......@@ -621,37 +1208,12 @@ static int sun8i_codec_component_probe(struct snd_soc_component *component)
BIT(SUN8I_SYSCLK_CTL_SYSCLK_SRC),
SUN8I_SYSCLK_CTL_SYSCLK_SRC_AIF1CLK);
/* Program the default sample rate. */
sun8i_codec_update_sample_rate(scodec);
return 0;
}
static const struct snd_soc_dai_ops sun8i_codec_dai_ops = {
.hw_params = sun8i_codec_hw_params,
.set_fmt = sun8i_set_fmt,
};
static struct snd_soc_dai_driver sun8i_codec_dai = {
.name = "sun8i",
/* playback capabilities */
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
/* capture capabilities */
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.sig_bits = 24,
},
/* pcm operations */
.ops = &sun8i_codec_dai_ops,
};
static const struct snd_soc_component_driver sun8i_soc_component = {
.dapm_widgets = sun8i_codec_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(sun8i_codec_dapm_widgets),
......@@ -659,7 +1221,6 @@ static const struct snd_soc_component_driver sun8i_soc_component = {
.num_dapm_routes = ARRAY_SIZE(sun8i_codec_dapm_routes),
.probe = sun8i_codec_component_probe,
.idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
.non_legacy_dai_naming = 1,
};
......@@ -714,7 +1275,8 @@ static int sun8i_codec_probe(struct platform_device *pdev)
}
ret = devm_snd_soc_register_component(&pdev->dev, &sun8i_soc_component,
&sun8i_codec_dai, 1);
sun8i_codec_dais,
ARRAY_SIZE(sun8i_codec_dais));
if (ret) {
dev_err(&pdev->dev, "Failed to register codec\n");
goto err_suspend;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment