diff options
Diffstat (limited to 'meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0063-ASoC-PCM3168A-add-TDM-modes-merge-ADC-and-DAC.patch')
-rw-r--r-- | meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0063-ASoC-PCM3168A-add-TDM-modes-merge-ADC-and-DAC.patch | 479 |
1 files changed, 479 insertions, 0 deletions
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0063-ASoC-PCM3168A-add-TDM-modes-merge-ADC-and-DAC.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0063-ASoC-PCM3168A-add-TDM-modes-merge-ADC-and-DAC.patch new file mode 100644 index 0000000..46d5d84 --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0063-ASoC-PCM3168A-add-TDM-modes-merge-ADC-and-DAC.patch @@ -0,0 +1,479 @@ +From 963d9e84748bc43a8dbcd28019bd8ff0942b5934 Mon Sep 17 00:00:00 2001 +From: Andrey Gusakov <andrey.gusakov@cogentembedded.com> +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 <andrey.gusakov@cogentembedded.com> +--- + 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 + |