From 963d9e84748bc43a8dbcd28019bd8ff0942b5934 Mon Sep 17 00:00:00 2001 From: Andrey Gusakov Date: Wed, 13 Apr 2016 15:32:38 +0300 Subject: [PATCH] ASoC: PCM3168A: add TDM modes, merge ADC and DAC Also disable 16 bit format and enable at start Signed-off-by: Andrey Gusakov --- sound/soc/codecs/pcm3168a.c | 320 ++++++++++++++++++++++++++++---------------- sound/soc/codecs/pcm3168a.h | 2 +- 2 files changed, 205 insertions(+), 117 deletions(-) diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c index 992a77edcd5d..8b9e4ff6b354 100644 --- a/sound/soc/codecs/pcm3168a.c +++ b/sound/soc/codecs/pcm3168a.c @@ -22,7 +22,7 @@ #include "pcm3168a.h" -#define PCM3168A_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ +#define PCM3168A_FORMATS (/*SNDRV_PCM_FMTBIT_S16_LE | */\ SNDRV_PCM_FMTBIT_S24_3LE | \ SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S32_LE) @@ -33,7 +33,11 @@ #define PCM3168A_FMT_RIGHT_J_16 0x3 #define PCM3168A_FMT_DSP_A 0x4 #define PCM3168A_FMT_DSP_B 0x5 -#define PCM3168A_FMT_DSP_MASK 0x4 +#define PCM3168A_FMT_I2S_TDM 0x6 +#define PCM3168A_FMT_LEFT_J_TDM 0x7 +/* High speed */ +#define PCM3168A_FMT_I2S_TDMHS 0x8 +#define PCM3168A_FMT_LEFT_J_TDMHS 0x9 #define PCM3168A_NUM_SUPPLIES 6 static const char *const pcm3168a_supply_names[PCM3168A_NUM_SUPPLIES] = { @@ -45,12 +49,18 @@ static const char *const pcm3168a_supply_names[PCM3168A_NUM_SUPPLIES] = { "VCCDA2" }; +#define TDM_MODE_NONE 0 +#define TDM_MODE_NORM 1 +#define TDM_MODE_HS 2 + struct pcm3168a_priv { struct regulator_bulk_data supplies[PCM3168A_NUM_SUPPLIES]; struct regmap *regmap; struct clk *scki; - bool adc_master_mode; - bool dac_master_mode; + bool master_mode; + unsigned int tdm; + unsigned int slots; + unsigned int slot_width; unsigned long sysclk; unsigned int adc_fmt; unsigned int dac_fmt; @@ -313,32 +323,43 @@ static int pcm3168a_set_dai_sysclk(struct snd_soc_dai *dai, return 0; } -static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai, +int format_table[3][6] = { + [TDM_MODE_NONE] = { + [0] = -1, + [SND_SOC_DAIFMT_I2S] = PCM3168A_FMT_I2S, + [SND_SOC_DAIFMT_LEFT_J] = PCM3168A_FMT_LEFT_J, + [SND_SOC_DAIFMT_RIGHT_J] = PCM3168A_FMT_RIGHT_J, + [SND_SOC_DAIFMT_DSP_A] = PCM3168A_FMT_DSP_A, + [SND_SOC_DAIFMT_DSP_B] = PCM3168A_FMT_DSP_B}, + [TDM_MODE_NORM] = { + [0] = -1, + [SND_SOC_DAIFMT_I2S] = PCM3168A_FMT_I2S_TDM, + [SND_SOC_DAIFMT_LEFT_J] = PCM3168A_FMT_LEFT_J_TDM, + [SND_SOC_DAIFMT_RIGHT_J] = -1, + [SND_SOC_DAIFMT_DSP_A] = -1, + [SND_SOC_DAIFMT_DSP_B] = -1}, + [TDM_MODE_HS] = { + [0] = -1, + [SND_SOC_DAIFMT_I2S] = PCM3168A_FMT_I2S_TDMHS, + [SND_SOC_DAIFMT_LEFT_J] = PCM3168A_FMT_LEFT_J_TDMHS, + [SND_SOC_DAIFMT_RIGHT_J] = -1, + [SND_SOC_DAIFMT_DSP_A] = -1, + [SND_SOC_DAIFMT_DSP_B] = -1}, +}; + +static int __pcm3168a_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format, bool dac) { struct snd_soc_codec *codec = dai->codec; struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec); - u32 fmt, reg, mask, shift; + u32 reg, mask, shift; + int fmt; bool master_mode; - switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_LEFT_J: - fmt = PCM3168A_FMT_LEFT_J; - break; - case SND_SOC_DAIFMT_I2S: - fmt = PCM3168A_FMT_I2S; - break; - case SND_SOC_DAIFMT_RIGHT_J: - fmt = PCM3168A_FMT_RIGHT_J; - break; - case SND_SOC_DAIFMT_DSP_A: - fmt = PCM3168A_FMT_DSP_A; - break; - case SND_SOC_DAIFMT_DSP_B: - fmt = PCM3168A_FMT_DSP_B; - break; - default: - dev_err(codec->dev, "unsupported dai format\n"); + fmt = format_table[pcm3168a->tdm][format & SND_SOC_DAIFMT_FORMAT_MASK]; + + if (fmt < 0) { + dev_err(codec->dev, "unsupported dai format of TDM mode\n"); return -EINVAL; } @@ -354,6 +375,16 @@ static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai, return -EINVAL; } + if ((pcm3168a->tdm == TDM_MODE_HS) && (master_mode)) { + dev_err(codec->dev, "TDM high speed supported only in slave mode\n"); + return -EINVAL; + } + + if ((pcm3168a->tdm == TDM_MODE_HS) && (!dac)) { + dev_err(codec->dev, "TDM high speed not supported for ADC\n"); + return -EINVAL; + } + switch (format & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: break; @@ -365,31 +396,32 @@ static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai, reg = PCM3168A_DAC_PWR_MST_FMT; mask = PCM3168A_DAC_FMT_MASK; shift = PCM3168A_DAC_FMT_SHIFT; - pcm3168a->dac_master_mode = master_mode; pcm3168a->dac_fmt = fmt; } else { reg = PCM3168A_ADC_MST_FMT; mask = PCM3168A_ADC_FMTAD_MASK; shift = PCM3168A_ADC_FMTAD_SHIFT; - pcm3168a->adc_master_mode = master_mode; pcm3168a->adc_fmt = fmt; } + pcm3168a->master_mode = master_mode; + regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift); return 0; } -static int pcm3168a_set_dai_fmt_dac(struct snd_soc_dai *dai, +static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format) { - return pcm3168a_set_dai_fmt(dai, format, true); -} + int ret; -static int pcm3168a_set_dai_fmt_adc(struct snd_soc_dai *dai, - unsigned int format) -{ - return pcm3168a_set_dai_fmt(dai, format, false); + /* dac */ + ret = __pcm3168a_set_dai_fmt(dai, format, false); + if (ret) + return ret; + /* adc */ + return __pcm3168a_set_dai_fmt(dai, format, true); } static int pcm3168a_hw_params(struct snd_pcm_substream *substream, @@ -398,127 +430,170 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_codec *codec = dai->codec; struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec); - bool tx, master_mode; + int bits; + bool tx; u32 val, mask, shift, reg; - unsigned int rate, fmt, ratio, max_ratio; + u32 sample_rate = 0; /* auto */ + unsigned int rate, channels, fmt, ratio, max_ratio; int i, min_frame_size; snd_pcm_format_t format; rate = params_rate(params); format = params_format(params); - - ratio = pcm3168a->sysclk / rate; + channels = params_channels(params); + bits = params->msbits; tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + if (tx) { max_ratio = PCM3168A_NUM_SCKI_RATIOS_DAC; - reg = PCM3168A_DAC_PWR_MST_FMT; - mask = PCM3168A_DAC_MSDA_MASK; - shift = PCM3168A_DAC_MSDA_SHIFT; - master_mode = pcm3168a->dac_master_mode; fmt = pcm3168a->dac_fmt; } else { max_ratio = PCM3168A_NUM_SCKI_RATIOS_ADC; - reg = PCM3168A_ADC_MST_FMT; - mask = PCM3168A_ADC_MSAD_MASK; - shift = PCM3168A_ADC_MSAD_SHIFT; - master_mode = pcm3168a->adc_master_mode; fmt = pcm3168a->adc_fmt; } - for (i = 0; i < max_ratio; i++) { - if (pcm3168a_scki_ratios[i] == ratio) - break; - } - - if (i == max_ratio) { - dev_err(codec->dev, "unsupported sysclk ratio\n"); - return -EINVAL; - } + if (pcm3168a->master_mode) { + ratio = pcm3168a->sysclk / rate; + for (i = 0; i < max_ratio; i++) + if (pcm3168a_scki_ratios[i] == ratio) + break; - min_frame_size = params_width(params) * 2; - switch (min_frame_size) { - case 32: - if (master_mode || (fmt != PCM3168A_FMT_RIGHT_J)) { - dev_err(codec->dev, "32-bit frames are supported only for slave mode using right justified\n"); + if (i == max_ratio) { + dev_err(codec->dev, "unsupported sysclk ratio: %d\n", ratio); return -EINVAL; } - fmt = PCM3168A_FMT_RIGHT_J_16; - break; - case 48: - if (master_mode || (fmt & PCM3168A_FMT_DSP_MASK)) { - dev_err(codec->dev, "48-bit frames not supported in master mode, or slave mode using DSP\n"); - return -EINVAL; - } - break; - case 64: - break; - default: - dev_err(codec->dev, "unsupported frame size: %d\n", min_frame_size); - return -EINVAL; + val = i + 1; + } else { + /* slave mode */ + val = 0; } - if (master_mode) - val = ((i + 1) << shift); - else + if (pcm3168a->tdm == TDM_MODE_NONE) { + /* one stereo frame size */ + min_frame_size = bits * 2; + switch (min_frame_size) { + case 32: + if (pcm3168a->master_mode || + (fmt != PCM3168A_FMT_RIGHT_J)) { + dev_err(codec->dev, "32-bit frames are supported only for slave mode using right justified\n"); + return -EINVAL; + } + fmt = PCM3168A_FMT_RIGHT_J_16; + break; + case 48: + if (pcm3168a->master_mode || + (fmt == PCM3168A_FMT_DSP_A) || + (fmt == PCM3168A_FMT_DSP_B)) { + dev_err(codec->dev, "48-bit frames not supported in master mode, or slave mode using DSP\n"); + return -EINVAL; + } + break; + case 64: + break; + default: + dev_err(codec->dev, "unsupported frame size: %d\n", min_frame_size); + return -EINVAL; + } + } + if ((pcm3168a->tdm == TDM_MODE_NORM) || + (pcm3168a->tdm == TDM_MODE_HS)) { + /* all channels over one or two line */ + min_frame_size = bits * channels; + + /* single rate */ + sample_rate = 1; + + /* + * 256fs for single line DIN0/DOUT0 + * 128fs for two lines DIN01/DOU01 + */ + if ((min_frame_size != 256) && + (min_frame_size != 128)) { + dev_err(codec->dev, "256/128-bit frames only supported in TDM formats\n"); + return -EINVAL; + } + } + + /* Setup ADC in master mode, couse it drives ADC */ + if ((pcm3168a->master_mode) || (tx)) { + fmt = pcm3168a->dac_fmt; + reg = PCM3168A_DAC_PWR_MST_FMT; + mask = PCM3168A_DAC_MSDA_MASK | PCM3168A_DAC_FMT_MASK; + shift = PCM3168A_DAC_MSDA_SHIFT; + /* start DAC */ + regmap_update_bits(pcm3168a->regmap, reg, mask, (val << shift) | fmt); + } + /* Do we need also ADC? */ + if (!tx) { + fmt = pcm3168a->adc_fmt; + reg = PCM3168A_ADC_MST_FMT; + mask = PCM3168A_ADC_MSAD_MASK | PCM3168A_ADC_FMTAD_MASK; + shift = PCM3168A_ADC_MSAD_SHIFT; + /* ADC slave mode only, driven by DAC or CPU DAI */ val = 0; + regmap_update_bits(pcm3168a->regmap, reg, mask, (val << shift) | fmt); + } - regmap_update_bits(pcm3168a->regmap, reg, mask, val); + regmap_update_bits(pcm3168a->regmap, PCM3168A_RST_SMODE, + PCM3168A_DAC_SRDA_MASK, + sample_rate << PCM3168A_DAC_SRDA_SHIFT); - if (tx) { - mask = PCM3168A_DAC_FMT_MASK; - shift = PCM3168A_DAC_FMT_SHIFT; - } else { - mask = PCM3168A_ADC_FMTAD_MASK; - shift = PCM3168A_ADC_FMTAD_SHIFT; - } + return 0; +} - regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift); +static int pcm3168a_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask, + int slots, + int slot_width) +{ + struct snd_soc_codec *codec = dai->codec; + struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec); + + if ((slots != 8) && (slots != 4)) + return -EINVAL; + + if ((slot_width != 32) && (slot_width != 24)) + return -EINVAL; + + pcm3168a->slots = slots; + pcm3168a->slot_width = slot_width; return 0; } -static const struct snd_soc_dai_ops pcm3168a_dac_dai_ops = { - .set_fmt = pcm3168a_set_dai_fmt_dac, +static const struct snd_soc_dai_ops pcm3168a_dai_ops = { + .set_fmt = pcm3168a_set_dai_fmt, .set_sysclk = pcm3168a_set_dai_sysclk, + .set_tdm_slot = pcm3168a_set_tdm_slot, .hw_params = pcm3168a_hw_params, .digital_mute = pcm3168a_digital_mute }; -static const struct snd_soc_dai_ops pcm3168a_adc_dai_ops = { - .set_fmt = pcm3168a_set_dai_fmt_adc, - .set_sysclk = pcm3168a_set_dai_sysclk, - .hw_params = pcm3168a_hw_params -}; - -static struct snd_soc_dai_driver pcm3168a_dais[] = { - { - .name = "pcm3168a-dac", - .playback = { - .stream_name = "Playback", - .channels_min = 1, - .channels_max = 8, - .rates = SNDRV_PCM_RATE_8000_192000, - .formats = PCM3168A_FORMATS - }, - .ops = &pcm3168a_dac_dai_ops +static struct snd_soc_dai_driver pcm3168a_dai = { + .name = "pcm3168a", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = PCM3168A_FORMATS }, - { - .name = "pcm3168a-adc", - .capture = { - .stream_name = "Capture", - .channels_min = 1, - .channels_max = 6, - .rates = SNDRV_PCM_RATE_8000_96000, - .formats = PCM3168A_FORMATS - }, - .ops = &pcm3168a_adc_dai_ops + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = PCM3168A_FORMATS }, + .ops = &pcm3168a_dai_ops, + .symmetric_rates = 1, }; static const struct reg_default pcm3168a_reg_default[] = { { PCM3168A_RST_SMODE, PCM3168A_MRST_MASK | PCM3168A_SRST_MASK }, - { PCM3168A_DAC_PWR_MST_FMT, 0x00 }, + { PCM3168A_DAC_PWR_MST_FMT, 0x80 }, { PCM3168A_DAC_OP_FLT, 0x00 }, { PCM3168A_DAC_INV, 0x00 }, { PCM3168A_DAC_MUTE, 0x00 }, @@ -665,12 +740,25 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap) goto err_regulator; } + /* get TDM mode */ + if (dev->of_node) { + if (of_get_property(dev->of_node, "tdm", NULL)) + pcm3168a->tdm = TDM_MODE_NORM; + else if (of_get_property(dev->of_node, "tdmhs", NULL)) + pcm3168a->tdm = TDM_MODE_HS; + } + pm_runtime_set_active(dev); pm_runtime_enable(dev); pm_runtime_idle(dev); - ret = snd_soc_register_codec(dev, &pcm3168a_driver, pcm3168a_dais, - ARRAY_SIZE(pcm3168a_dais)); + if (pcm3168a->tdm != TDM_MODE_NONE) { + pcm3168a_dai.playback.channels_min = 8; + pcm3168a_dai.capture.channels_min = 8; + } + + ret = snd_soc_register_codec(dev, &pcm3168a_driver, + &pcm3168a_dai, 1); if (ret) { dev_err(dev, "failed to register codec: %d\n", ret); goto err_regulator; diff --git a/sound/soc/codecs/pcm3168a.h b/sound/soc/codecs/pcm3168a.h index 56c8332d82fb..658507f86c97 100644 --- a/sound/soc/codecs/pcm3168a.h +++ b/sound/soc/codecs/pcm3168a.h @@ -69,7 +69,7 @@ extern void pcm3168a_remove(struct device *dev); #define PCM3168A_ADC_MSAD_SHIFT 4 #define PCM3168A_ADC_MSAD_MASK 0x70 #define PCM3168A_ADC_FMTAD_SHIFT 0 -#define PCM3168A_ADC_FMTAD_MASK 0x7 +#define PCM3168A_ADC_FMTAD_MASK 0xf #define PCM3168A_ADC_PWR_HPFB 0x52 #define PCM3168A_ADC_PSVAD_SHIFT 4 -- 2.13.0