linux/sound/soc/meson/aiu-encoder-i2s.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2//
   3// Copyright (c) 2020 BayLibre, SAS.
   4// Author: Jerome Brunet <jbrunet@baylibre.com>
   5
   6#include <linux/bitfield.h>
   7#include <linux/clk.h>
   8#include <sound/pcm_params.h>
   9#include <sound/soc.h>
  10#include <sound/soc-dai.h>
  11
  12#include "aiu.h"
  13
  14#define AIU_I2S_SOURCE_DESC_MODE_8CH    BIT(0)
  15#define AIU_I2S_SOURCE_DESC_MODE_24BIT  BIT(5)
  16#define AIU_I2S_SOURCE_DESC_MODE_32BIT  BIT(9)
  17#define AIU_I2S_SOURCE_DESC_MODE_SPLIT  BIT(11)
  18#define AIU_RST_SOFT_I2S_FAST           BIT(0)
  19
  20#define AIU_I2S_DAC_CFG_MSB_FIRST       BIT(2)
  21#define AIU_I2S_MISC_HOLD_EN            BIT(2)
  22#define AIU_CLK_CTRL_I2S_DIV_EN         BIT(0)
  23#define AIU_CLK_CTRL_I2S_DIV            GENMASK(3, 2)
  24#define AIU_CLK_CTRL_AOCLK_INVERT       BIT(6)
  25#define AIU_CLK_CTRL_LRCLK_INVERT       BIT(7)
  26#define AIU_CLK_CTRL_LRCLK_SKEW         GENMASK(9, 8)
  27#define AIU_CLK_CTRL_MORE_HDMI_AMCLK    BIT(6)
  28#define AIU_CLK_CTRL_MORE_I2S_DIV       GENMASK(5, 0)
  29#define AIU_CODEC_DAC_LRCLK_CTRL_DIV    GENMASK(11, 0)
  30
  31static void aiu_encoder_i2s_divider_enable(struct snd_soc_component *component,
  32                                           bool enable)
  33{
  34        snd_soc_component_update_bits(component, AIU_CLK_CTRL,
  35                                      AIU_CLK_CTRL_I2S_DIV_EN,
  36                                      enable ? AIU_CLK_CTRL_I2S_DIV_EN : 0);
  37}
  38
  39static void aiu_encoder_i2s_hold(struct snd_soc_component *component,
  40                                 bool enable)
  41{
  42        snd_soc_component_update_bits(component, AIU_I2S_MISC,
  43                                      AIU_I2S_MISC_HOLD_EN,
  44                                      enable ? AIU_I2S_MISC_HOLD_EN : 0);
  45}
  46
  47static int aiu_encoder_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
  48                                   struct snd_soc_dai *dai)
  49{
  50        struct snd_soc_component *component = dai->component;
  51
  52        switch (cmd) {
  53        case SNDRV_PCM_TRIGGER_START:
  54        case SNDRV_PCM_TRIGGER_RESUME:
  55        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
  56                aiu_encoder_i2s_hold(component, false);
  57                return 0;
  58
  59        case SNDRV_PCM_TRIGGER_STOP:
  60        case SNDRV_PCM_TRIGGER_SUSPEND:
  61        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
  62                aiu_encoder_i2s_hold(component, true);
  63                return 0;
  64
  65        default:
  66                return -EINVAL;
  67        }
  68}
  69
  70static int aiu_encoder_i2s_setup_desc(struct snd_soc_component *component,
  71                                      struct snd_pcm_hw_params *params)
  72{
  73        /* Always operate in split (classic interleaved) mode */
  74        unsigned int desc = AIU_I2S_SOURCE_DESC_MODE_SPLIT;
  75
  76        /* Reset required to update the pipeline */
  77        snd_soc_component_write(component, AIU_RST_SOFT, AIU_RST_SOFT_I2S_FAST);
  78        snd_soc_component_read(component, AIU_I2S_SYNC);
  79
  80        switch (params_physical_width(params)) {
  81        case 16: /* Nothing to do */
  82                break;
  83
  84        case 32:
  85                desc |= (AIU_I2S_SOURCE_DESC_MODE_24BIT |
  86                         AIU_I2S_SOURCE_DESC_MODE_32BIT);
  87                break;
  88
  89        default:
  90                return -EINVAL;
  91        }
  92
  93        switch (params_channels(params)) {
  94        case 2: /* Nothing to do */
  95                break;
  96        case 8:
  97                desc |= AIU_I2S_SOURCE_DESC_MODE_8CH;
  98                break;
  99        default:
 100                return -EINVAL;
 101        }
 102
 103        snd_soc_component_update_bits(component, AIU_I2S_SOURCE_DESC,
 104                                      AIU_I2S_SOURCE_DESC_MODE_8CH |
 105                                      AIU_I2S_SOURCE_DESC_MODE_24BIT |
 106                                      AIU_I2S_SOURCE_DESC_MODE_32BIT |
 107                                      AIU_I2S_SOURCE_DESC_MODE_SPLIT,
 108                                      desc);
 109
 110        return 0;
 111}
 112
 113static int aiu_encoder_i2s_set_legacy_div(struct snd_soc_component *component,
 114                                          struct snd_pcm_hw_params *params,
 115                                          unsigned int bs)
 116{
 117        switch (bs) {
 118        case 1:
 119        case 2:
 120        case 4:
 121        case 8:
 122                /* These are the only valid legacy dividers */
 123                break;
 124
 125        default:
 126                dev_err(component->dev, "Unsupported i2s divider: %u\n", bs);
 127                return -EINVAL;
 128        }
 129
 130        snd_soc_component_update_bits(component, AIU_CLK_CTRL,
 131                                      AIU_CLK_CTRL_I2S_DIV,
 132                                      FIELD_PREP(AIU_CLK_CTRL_I2S_DIV,
 133                                                 __ffs(bs)));
 134
 135        snd_soc_component_update_bits(component, AIU_CLK_CTRL_MORE,
 136                                      AIU_CLK_CTRL_MORE_I2S_DIV,
 137                                      FIELD_PREP(AIU_CLK_CTRL_MORE_I2S_DIV,
 138                                                 0));
 139
 140        return 0;
 141}
 142
 143static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component,
 144                                        struct snd_pcm_hw_params *params,
 145                                        unsigned int bs)
 146{
 147        /*
 148         * NOTE: this HW is odd.
 149         * In most configuration, the i2s divider is 'mclk / blck'.
 150         * However, in 16 bits - 8ch mode, this factor needs to be
 151         * increased by 50% to get the correct output rate.
 152         * No idea why !
 153         */
 154        if (params_width(params) == 16 && params_channels(params) == 8) {
 155                if (bs % 2) {
 156                        dev_err(component->dev,
 157                                "Cannot increase i2s divider by 50%%\n");
 158                        return -EINVAL;
 159                }
 160                bs += bs / 2;
 161        }
 162
 163        /* Use CLK_MORE for mclk to bclk divider */
 164        snd_soc_component_update_bits(component, AIU_CLK_CTRL,
 165                                      AIU_CLK_CTRL_I2S_DIV,
 166                                      FIELD_PREP(AIU_CLK_CTRL_I2S_DIV, 0));
 167
 168        snd_soc_component_update_bits(component, AIU_CLK_CTRL_MORE,
 169                                      AIU_CLK_CTRL_MORE_I2S_DIV,
 170                                      FIELD_PREP(AIU_CLK_CTRL_MORE_I2S_DIV,
 171                                                 bs - 1));
 172
 173        return 0;
 174}
 175
 176static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component,
 177                                      struct snd_pcm_hw_params *params)
 178{
 179        struct aiu *aiu = snd_soc_component_get_drvdata(component);
 180        unsigned int srate = params_rate(params);
 181        unsigned int fs, bs;
 182        int ret;
 183
 184        /* Get the oversampling factor */
 185        fs = DIV_ROUND_CLOSEST(clk_get_rate(aiu->i2s.clks[MCLK].clk), srate);
 186
 187        if (fs % 64)
 188                return -EINVAL;
 189
 190        /* Send data MSB first */
 191        snd_soc_component_update_bits(component, AIU_I2S_DAC_CFG,
 192                                      AIU_I2S_DAC_CFG_MSB_FIRST,
 193                                      AIU_I2S_DAC_CFG_MSB_FIRST);
 194
 195        /* Set bclk to lrlck ratio */
 196        snd_soc_component_update_bits(component, AIU_CODEC_DAC_LRCLK_CTRL,
 197                                      AIU_CODEC_DAC_LRCLK_CTRL_DIV,
 198                                      FIELD_PREP(AIU_CODEC_DAC_LRCLK_CTRL_DIV,
 199                                                 64 - 1));
 200
 201        bs = fs / 64;
 202
 203        if (aiu->platform->has_clk_ctrl_more_i2s_div)
 204                ret = aiu_encoder_i2s_set_more_div(component, params, bs);
 205        else
 206                ret = aiu_encoder_i2s_set_legacy_div(component, params, bs);
 207
 208        if (ret)
 209                return ret;
 210
 211        /* Make sure amclk is used for HDMI i2s as well */
 212        snd_soc_component_update_bits(component, AIU_CLK_CTRL_MORE,
 213                                      AIU_CLK_CTRL_MORE_HDMI_AMCLK,
 214                                      AIU_CLK_CTRL_MORE_HDMI_AMCLK);
 215
 216        return 0;
 217}
 218
 219static int aiu_encoder_i2s_hw_params(struct snd_pcm_substream *substream,
 220                                     struct snd_pcm_hw_params *params,
 221                                     struct snd_soc_dai *dai)
 222{
 223        struct snd_soc_component *component = dai->component;
 224        int ret;
 225
 226        /* Disable the clock while changing the settings */
 227        aiu_encoder_i2s_divider_enable(component, false);
 228
 229        ret = aiu_encoder_i2s_setup_desc(component, params);
 230        if (ret) {
 231                dev_err(dai->dev, "setting i2s desc failed\n");
 232                return ret;
 233        }
 234
 235        ret = aiu_encoder_i2s_set_clocks(component, params);
 236        if (ret) {
 237                dev_err(dai->dev, "setting i2s clocks failed\n");
 238                return ret;
 239        }
 240
 241        aiu_encoder_i2s_divider_enable(component, true);
 242
 243        return 0;
 244}
 245
 246static int aiu_encoder_i2s_hw_free(struct snd_pcm_substream *substream,
 247                                   struct snd_soc_dai *dai)
 248{
 249        struct snd_soc_component *component = dai->component;
 250
 251        aiu_encoder_i2s_divider_enable(component, false);
 252
 253        return 0;
 254}
 255
 256static int aiu_encoder_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 257{
 258        struct snd_soc_component *component = dai->component;
 259        unsigned int inv = fmt & SND_SOC_DAIFMT_INV_MASK;
 260        unsigned int val = 0;
 261        unsigned int skew;
 262
 263        /* Only CPU Master / Codec Slave supported ATM */
 264        if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
 265                return -EINVAL;
 266
 267        if (inv == SND_SOC_DAIFMT_NB_IF ||
 268            inv == SND_SOC_DAIFMT_IB_IF)
 269                val |= AIU_CLK_CTRL_LRCLK_INVERT;
 270
 271        if (inv == SND_SOC_DAIFMT_IB_NF ||
 272            inv == SND_SOC_DAIFMT_IB_IF)
 273                val |= AIU_CLK_CTRL_AOCLK_INVERT;
 274
 275        /* Signal skew */
 276        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 277        case SND_SOC_DAIFMT_I2S:
 278                /* Invert sample clock for i2s */
 279                val ^= AIU_CLK_CTRL_LRCLK_INVERT;
 280                skew = 1;
 281                break;
 282        case SND_SOC_DAIFMT_LEFT_J:
 283                skew = 0;
 284                break;
 285        default:
 286                return -EINVAL;
 287        }
 288
 289        val |= FIELD_PREP(AIU_CLK_CTRL_LRCLK_SKEW, skew);
 290        snd_soc_component_update_bits(component, AIU_CLK_CTRL,
 291                                      AIU_CLK_CTRL_LRCLK_INVERT |
 292                                      AIU_CLK_CTRL_AOCLK_INVERT |
 293                                      AIU_CLK_CTRL_LRCLK_SKEW,
 294                                      val);
 295
 296        return 0;
 297}
 298
 299static int aiu_encoder_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
 300                                      unsigned int freq, int dir)
 301{
 302        struct aiu *aiu = snd_soc_component_get_drvdata(dai->component);
 303        int ret;
 304
 305        if (WARN_ON(clk_id != 0))
 306                return -EINVAL;
 307
 308        if (dir == SND_SOC_CLOCK_IN)
 309                return 0;
 310
 311        ret = clk_set_rate(aiu->i2s.clks[MCLK].clk, freq);
 312        if (ret)
 313                dev_err(dai->dev, "Failed to set sysclk to %uHz", freq);
 314
 315        return ret;
 316}
 317
 318static const unsigned int hw_channels[] = {2, 8};
 319static const struct snd_pcm_hw_constraint_list hw_channel_constraints = {
 320        .list = hw_channels,
 321        .count = ARRAY_SIZE(hw_channels),
 322        .mask = 0,
 323};
 324
 325static int aiu_encoder_i2s_startup(struct snd_pcm_substream *substream,
 326                                   struct snd_soc_dai *dai)
 327{
 328        struct aiu *aiu = snd_soc_component_get_drvdata(dai->component);
 329        int ret;
 330
 331        /* Make sure the encoder gets either 2 or 8 channels */
 332        ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
 333                                         SNDRV_PCM_HW_PARAM_CHANNELS,
 334                                         &hw_channel_constraints);
 335        if (ret) {
 336                dev_err(dai->dev, "adding channels constraints failed\n");
 337                return ret;
 338        }
 339
 340        ret = clk_bulk_prepare_enable(aiu->i2s.clk_num, aiu->i2s.clks);
 341        if (ret)
 342                dev_err(dai->dev, "failed to enable i2s clocks\n");
 343
 344        return ret;
 345}
 346
 347static void aiu_encoder_i2s_shutdown(struct snd_pcm_substream *substream,
 348                                     struct snd_soc_dai *dai)
 349{
 350        struct aiu *aiu = snd_soc_component_get_drvdata(dai->component);
 351
 352        clk_bulk_disable_unprepare(aiu->i2s.clk_num, aiu->i2s.clks);
 353}
 354
 355const struct snd_soc_dai_ops aiu_encoder_i2s_dai_ops = {
 356        .trigger        = aiu_encoder_i2s_trigger,
 357        .hw_params      = aiu_encoder_i2s_hw_params,
 358        .hw_free        = aiu_encoder_i2s_hw_free,
 359        .set_fmt        = aiu_encoder_i2s_set_fmt,
 360        .set_sysclk     = aiu_encoder_i2s_set_sysclk,
 361        .startup        = aiu_encoder_i2s_startup,
 362        .shutdown       = aiu_encoder_i2s_shutdown,
 363};
 364
 365