Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 //
0003 // Copyright (c) 2020 BayLibre, SAS.
0004 // Author: Jerome Brunet <jbrunet@baylibre.com>
0005 
0006 #include <linux/bitfield.h>
0007 #include <linux/clk.h>
0008 #include <sound/pcm_params.h>
0009 #include <sound/pcm_iec958.h>
0010 #include <sound/soc.h>
0011 #include <sound/soc-dai.h>
0012 
0013 #include "aiu.h"
0014 
0015 #define AIU_958_MISC_NON_PCM        BIT(0)
0016 #define AIU_958_MISC_MODE_16BITS    BIT(1)
0017 #define AIU_958_MISC_16BITS_ALIGN   GENMASK(6, 5)
0018 #define AIU_958_MISC_MODE_32BITS    BIT(7)
0019 #define AIU_958_MISC_U_FROM_STREAM  BIT(12)
0020 #define AIU_958_MISC_FORCE_LR       BIT(13)
0021 #define AIU_958_CTRL_HOLD_EN        BIT(0)
0022 #define AIU_CLK_CTRL_958_DIV_EN     BIT(1)
0023 #define AIU_CLK_CTRL_958_DIV        GENMASK(5, 4)
0024 #define AIU_CLK_CTRL_958_DIV_MORE   BIT(12)
0025 
0026 #define AIU_CS_WORD_LEN         4
0027 #define AIU_958_INTERNAL_DIV        2
0028 
0029 static void
0030 aiu_encoder_spdif_divider_enable(struct snd_soc_component *component,
0031                  bool enable)
0032 {
0033     snd_soc_component_update_bits(component, AIU_CLK_CTRL,
0034                       AIU_CLK_CTRL_958_DIV_EN,
0035                       enable ? AIU_CLK_CTRL_958_DIV_EN : 0);
0036 }
0037 
0038 static void aiu_encoder_spdif_hold(struct snd_soc_component *component,
0039                    bool enable)
0040 {
0041     snd_soc_component_update_bits(component, AIU_958_CTRL,
0042                       AIU_958_CTRL_HOLD_EN,
0043                       enable ? AIU_958_CTRL_HOLD_EN : 0);
0044 }
0045 
0046 static int
0047 aiu_encoder_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
0048               struct snd_soc_dai *dai)
0049 {
0050     struct snd_soc_component *component = dai->component;
0051 
0052     switch (cmd) {
0053     case SNDRV_PCM_TRIGGER_START:
0054     case SNDRV_PCM_TRIGGER_RESUME:
0055     case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
0056         aiu_encoder_spdif_hold(component, false);
0057         return 0;
0058 
0059     case SNDRV_PCM_TRIGGER_STOP:
0060     case SNDRV_PCM_TRIGGER_SUSPEND:
0061     case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
0062         aiu_encoder_spdif_hold(component, true);
0063         return 0;
0064 
0065     default:
0066         return -EINVAL;
0067     }
0068 }
0069 
0070 static int aiu_encoder_spdif_setup_cs_word(struct snd_soc_component *component,
0071                        struct snd_pcm_hw_params *params)
0072 {
0073     u8 cs[AIU_CS_WORD_LEN];
0074     unsigned int val;
0075     int ret;
0076 
0077     ret = snd_pcm_create_iec958_consumer_hw_params(params, cs,
0078                                AIU_CS_WORD_LEN);
0079     if (ret < 0)
0080         return ret;
0081 
0082     /* Write the 1st half word */
0083     val = cs[1] | cs[0] << 8;
0084     snd_soc_component_write(component, AIU_958_CHSTAT_L0, val);
0085     snd_soc_component_write(component, AIU_958_CHSTAT_R0, val);
0086 
0087     /* Write the 2nd half word */
0088     val = cs[3] | cs[2] << 8;
0089     snd_soc_component_write(component, AIU_958_CHSTAT_L1, val);
0090     snd_soc_component_write(component, AIU_958_CHSTAT_R1, val);
0091 
0092     return 0;
0093 }
0094 
0095 static int aiu_encoder_spdif_hw_params(struct snd_pcm_substream *substream,
0096                        struct snd_pcm_hw_params *params,
0097                        struct snd_soc_dai *dai)
0098 {
0099     struct snd_soc_component *component = dai->component;
0100     struct aiu *aiu = snd_soc_component_get_drvdata(component);
0101     unsigned int val = 0, mrate;
0102     int ret;
0103 
0104     /* Disable the clock while changing the settings */
0105     aiu_encoder_spdif_divider_enable(component, false);
0106 
0107     switch (params_physical_width(params)) {
0108     case 16:
0109         val |= AIU_958_MISC_MODE_16BITS;
0110         val |= FIELD_PREP(AIU_958_MISC_16BITS_ALIGN, 2);
0111         break;
0112     case 32:
0113         val |= AIU_958_MISC_MODE_32BITS;
0114         break;
0115     default:
0116         dev_err(dai->dev, "Unsupported physical width\n");
0117         return -EINVAL;
0118     }
0119 
0120     snd_soc_component_update_bits(component, AIU_958_MISC,
0121                       AIU_958_MISC_NON_PCM |
0122                       AIU_958_MISC_MODE_16BITS |
0123                       AIU_958_MISC_16BITS_ALIGN |
0124                       AIU_958_MISC_MODE_32BITS |
0125                       AIU_958_MISC_FORCE_LR |
0126                       AIU_958_MISC_U_FROM_STREAM,
0127                       val);
0128 
0129     /* Set the stream channel status word */
0130     ret = aiu_encoder_spdif_setup_cs_word(component, params);
0131     if (ret) {
0132         dev_err(dai->dev, "failed to set channel status word\n");
0133         return ret;
0134     }
0135 
0136     snd_soc_component_update_bits(component, AIU_CLK_CTRL,
0137                       AIU_CLK_CTRL_958_DIV |
0138                       AIU_CLK_CTRL_958_DIV_MORE,
0139                       FIELD_PREP(AIU_CLK_CTRL_958_DIV,
0140                          __ffs(AIU_958_INTERNAL_DIV)));
0141 
0142     /* 2 * 32bits per subframe * 2 channels = 128 */
0143     mrate = params_rate(params) * 128 * AIU_958_INTERNAL_DIV;
0144     ret = clk_set_rate(aiu->spdif.clks[MCLK].clk, mrate);
0145     if (ret) {
0146         dev_err(dai->dev, "failed to set mclk rate\n");
0147         return ret;
0148     }
0149 
0150     aiu_encoder_spdif_divider_enable(component, true);
0151 
0152     return 0;
0153 }
0154 
0155 static int aiu_encoder_spdif_hw_free(struct snd_pcm_substream *substream,
0156                      struct snd_soc_dai *dai)
0157 {
0158     struct snd_soc_component *component = dai->component;
0159 
0160     aiu_encoder_spdif_divider_enable(component, false);
0161 
0162     return 0;
0163 }
0164 
0165 static int aiu_encoder_spdif_startup(struct snd_pcm_substream *substream,
0166                      struct snd_soc_dai *dai)
0167 {
0168     struct aiu *aiu = snd_soc_component_get_drvdata(dai->component);
0169     int ret;
0170 
0171     /*
0172      * NOTE: Make sure the spdif block is on its own divider.
0173      *
0174      * The spdif can be clocked by the i2s master clock or its own
0175      * clock. We should (in theory) change the source depending on the
0176      * origin of the data.
0177      *
0178      * However, considering the clocking scheme used on these platforms,
0179      * the master clocks will pick the same PLL source when they are
0180      * playing from the same FIFO. The clock should be in sync so, it
0181      * should not be necessary to reparent the spdif master clock.
0182      */
0183     ret = clk_set_parent(aiu->spdif.clks[MCLK].clk,
0184                  aiu->spdif_mclk);
0185     if (ret)
0186         return ret;
0187 
0188     ret = clk_bulk_prepare_enable(aiu->spdif.clk_num, aiu->spdif.clks);
0189     if (ret)
0190         dev_err(dai->dev, "failed to enable spdif clocks\n");
0191 
0192     return ret;
0193 }
0194 
0195 static void aiu_encoder_spdif_shutdown(struct snd_pcm_substream *substream,
0196                        struct snd_soc_dai *dai)
0197 {
0198     struct aiu *aiu = snd_soc_component_get_drvdata(dai->component);
0199 
0200     clk_bulk_disable_unprepare(aiu->spdif.clk_num, aiu->spdif.clks);
0201 }
0202 
0203 const struct snd_soc_dai_ops aiu_encoder_spdif_dai_ops = {
0204     .trigger    = aiu_encoder_spdif_trigger,
0205     .hw_params  = aiu_encoder_spdif_hw_params,
0206     .hw_free    = aiu_encoder_spdif_hw_free,
0207     .startup    = aiu_encoder_spdif_startup,
0208     .shutdown   = aiu_encoder_spdif_shutdown,
0209 };