linux/sound/soc/codecs/sti-sas.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) STMicroelectronics SA 2015
   4 * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com>
   5 *          for STMicroelectronics.
   6 */
   7
   8#include <linux/io.h>
   9#include <linux/module.h>
  10#include <linux/regmap.h>
  11#include <linux/reset.h>
  12#include <linux/mfd/syscon.h>
  13
  14#include <sound/soc.h>
  15#include <sound/soc-dapm.h>
  16
  17/* DAC definitions */
  18
  19/* stih407 DAC registers */
  20/* sysconf 5041: Audio-Gue-Control */
  21#define STIH407_AUDIO_GLUE_CTRL 0x000000A4
  22/* sysconf 5042: Audio-DAC-Control */
  23#define STIH407_AUDIO_DAC_CTRL 0x000000A8
  24
  25/* DAC definitions */
  26#define STIH407_DAC_SOFTMUTE            0x0
  27#define STIH407_DAC_STANDBY_ANA         0x1
  28#define STIH407_DAC_STANDBY             0x2
  29
  30#define STIH407_DAC_SOFTMUTE_MASK       BIT(STIH407_DAC_SOFTMUTE)
  31#define STIH407_DAC_STANDBY_ANA_MASK    BIT(STIH407_DAC_STANDBY_ANA)
  32#define STIH407_DAC_STANDBY_MASK        BIT(STIH407_DAC_STANDBY)
  33
  34/* SPDIF definitions */
  35#define SPDIF_BIPHASE_ENABLE            0x6
  36#define SPDIF_BIPHASE_IDLE              0x7
  37
  38#define SPDIF_BIPHASE_ENABLE_MASK       BIT(SPDIF_BIPHASE_ENABLE)
  39#define SPDIF_BIPHASE_IDLE_MASK         BIT(SPDIF_BIPHASE_IDLE)
  40
  41enum {
  42        STI_SAS_DAI_SPDIF_OUT,
  43        STI_SAS_DAI_ANALOG_OUT,
  44};
  45
  46static const struct reg_default stih407_sas_reg_defaults[] = {
  47        { STIH407_AUDIO_DAC_CTRL, 0x000000000 },
  48        { STIH407_AUDIO_GLUE_CTRL, 0x00000040 },
  49};
  50
  51struct sti_dac_audio {
  52        struct regmap *regmap;
  53        struct regmap *virt_regmap;
  54        struct regmap_field  **field;
  55        struct reset_control *rst;
  56        int mclk;
  57};
  58
  59struct sti_spdif_audio {
  60        struct regmap *regmap;
  61        struct regmap_field  **field;
  62        int mclk;
  63};
  64
  65/* device data structure */
  66struct sti_sas_dev_data {
  67        const struct regmap_config *regmap;
  68        const struct snd_soc_dai_ops *dac_ops;  /* DAC function callbacks */
  69        const struct snd_soc_dapm_widget *dapm_widgets; /* dapms declaration */
  70        const int num_dapm_widgets; /* dapms declaration */
  71        const struct snd_soc_dapm_route *dapm_routes; /* route declaration */
  72        const int num_dapm_routes; /* route declaration */
  73};
  74
  75/* driver data structure */
  76struct sti_sas_data {
  77        struct device *dev;
  78        const struct sti_sas_dev_data *dev_data;
  79        struct sti_dac_audio dac;
  80        struct sti_spdif_audio spdif;
  81};
  82
  83/* Read a register from the sysconf reg bank */
  84static int sti_sas_read_reg(void *context, unsigned int reg,
  85                            unsigned int *value)
  86{
  87        struct sti_sas_data *drvdata = context;
  88        int status;
  89        u32 val;
  90
  91        status = regmap_read(drvdata->dac.regmap, reg, &val);
  92        *value = (unsigned int)val;
  93
  94        return status;
  95}
  96
  97/* Read a register from the sysconf reg bank */
  98static int sti_sas_write_reg(void *context, unsigned int reg,
  99                             unsigned int value)
 100{
 101        struct sti_sas_data *drvdata = context;
 102        int status;
 103
 104        status = regmap_write(drvdata->dac.regmap, reg, value);
 105
 106        return status;
 107}
 108
 109static int  sti_sas_init_sas_registers(struct snd_soc_component *component,
 110                                       struct sti_sas_data *data)
 111{
 112        int ret;
 113        /*
 114         * DAC and SPDIF are activated by default
 115         * put them in IDLE to save power
 116         */
 117
 118        /* Initialise bi-phase formatter to disabled */
 119        ret = snd_soc_component_update_bits(component, STIH407_AUDIO_GLUE_CTRL,
 120                                  SPDIF_BIPHASE_ENABLE_MASK, 0);
 121
 122        if (!ret)
 123                /* Initialise bi-phase formatter idle value to 0 */
 124                ret = snd_soc_component_update_bits(component, STIH407_AUDIO_GLUE_CTRL,
 125                                          SPDIF_BIPHASE_IDLE_MASK, 0);
 126        if (ret < 0) {
 127                dev_err(component->dev, "Failed to update SPDIF registers\n");
 128                return ret;
 129        }
 130
 131        /* Init DAC configuration */
 132        /* init configuration */
 133        ret =  snd_soc_component_update_bits(component, STIH407_AUDIO_DAC_CTRL,
 134                                   STIH407_DAC_STANDBY_MASK,
 135                                   STIH407_DAC_STANDBY_MASK);
 136
 137        if (!ret)
 138                ret = snd_soc_component_update_bits(component, STIH407_AUDIO_DAC_CTRL,
 139                                          STIH407_DAC_STANDBY_ANA_MASK,
 140                                          STIH407_DAC_STANDBY_ANA_MASK);
 141        if (!ret)
 142                ret = snd_soc_component_update_bits(component, STIH407_AUDIO_DAC_CTRL,
 143                                          STIH407_DAC_SOFTMUTE_MASK,
 144                                          STIH407_DAC_SOFTMUTE_MASK);
 145
 146        if (ret < 0) {
 147                dev_err(component->dev, "Failed to update DAC registers\n");
 148                return ret;
 149        }
 150
 151        return ret;
 152}
 153
 154/*
 155 * DAC
 156 */
 157static int sti_sas_dac_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 158{
 159        /* Sanity check only */
 160        if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
 161                dev_err(dai->component->dev,
 162                        "%s: ERROR: Unsupporter master mask 0x%x\n",
 163                        __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK);
 164                return -EINVAL;
 165        }
 166
 167        return 0;
 168}
 169
 170static const struct snd_soc_dapm_widget stih407_sas_dapm_widgets[] = {
 171        SND_SOC_DAPM_OUT_DRV("DAC standby ana", STIH407_AUDIO_DAC_CTRL,
 172                             STIH407_DAC_STANDBY_ANA, 1, NULL, 0),
 173        SND_SOC_DAPM_DAC("DAC standby",  "dac_p", STIH407_AUDIO_DAC_CTRL,
 174                         STIH407_DAC_STANDBY, 1),
 175        SND_SOC_DAPM_OUTPUT("DAC Output"),
 176};
 177
 178static const struct snd_soc_dapm_route stih407_sas_route[] = {
 179        {"DAC Output", NULL, "DAC standby ana"},
 180        {"DAC standby ana", NULL, "DAC standby"},
 181};
 182
 183
 184static int stih407_sas_dac_mute(struct snd_soc_dai *dai, int mute, int stream)
 185{
 186        struct snd_soc_component *component = dai->component;
 187
 188        if (mute) {
 189                return snd_soc_component_update_bits(component, STIH407_AUDIO_DAC_CTRL,
 190                                            STIH407_DAC_SOFTMUTE_MASK,
 191                                            STIH407_DAC_SOFTMUTE_MASK);
 192        } else {
 193                return snd_soc_component_update_bits(component, STIH407_AUDIO_DAC_CTRL,
 194                                            STIH407_DAC_SOFTMUTE_MASK,
 195                                            0);
 196        }
 197}
 198
 199/*
 200 * SPDIF
 201 */
 202static int sti_sas_spdif_set_fmt(struct snd_soc_dai *dai,
 203                                 unsigned int fmt)
 204{
 205        if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
 206                dev_err(dai->component->dev,
 207                        "%s: ERROR: Unsupporter master mask 0x%x\n",
 208                        __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK);
 209                return -EINVAL;
 210        }
 211
 212        return 0;
 213}
 214
 215/*
 216 * sti_sas_spdif_trigger:
 217 * Trigger function is used to ensure that BiPhase Formater is disabled
 218 * before CPU dai is stopped.
 219 * This is mandatory to avoid that BPF is stalled
 220 */
 221static int sti_sas_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
 222                                 struct snd_soc_dai *dai)
 223{
 224        struct snd_soc_component *component = dai->component;
 225
 226        switch (cmd) {
 227        case SNDRV_PCM_TRIGGER_START:
 228        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 229                return snd_soc_component_update_bits(component, STIH407_AUDIO_GLUE_CTRL,
 230                                            SPDIF_BIPHASE_ENABLE_MASK,
 231                                            SPDIF_BIPHASE_ENABLE_MASK);
 232        case SNDRV_PCM_TRIGGER_RESUME:
 233        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 234        case SNDRV_PCM_TRIGGER_STOP:
 235        case SNDRV_PCM_TRIGGER_SUSPEND:
 236                return snd_soc_component_update_bits(component, STIH407_AUDIO_GLUE_CTRL,
 237                                            SPDIF_BIPHASE_ENABLE_MASK,
 238                                            0);
 239        default:
 240                return -EINVAL;
 241        }
 242}
 243
 244static bool sti_sas_volatile_register(struct device *dev, unsigned int reg)
 245{
 246        if (reg == STIH407_AUDIO_GLUE_CTRL)
 247                return true;
 248
 249        return false;
 250}
 251
 252/*
 253 * CODEC DAIS
 254 */
 255
 256/*
 257 * sti_sas_set_sysclk:
 258 * get MCLK input frequency to check that MCLK-FS ratio is coherent
 259 */
 260static int sti_sas_set_sysclk(struct snd_soc_dai *dai, int clk_id,
 261                              unsigned int freq, int dir)
 262{
 263        struct snd_soc_component *component = dai->component;
 264        struct sti_sas_data *drvdata = dev_get_drvdata(component->dev);
 265
 266        if (dir == SND_SOC_CLOCK_OUT)
 267                return 0;
 268
 269        if (clk_id != 0)
 270                return -EINVAL;
 271
 272        switch (dai->id) {
 273        case STI_SAS_DAI_SPDIF_OUT:
 274                drvdata->spdif.mclk = freq;
 275                break;
 276
 277        case STI_SAS_DAI_ANALOG_OUT:
 278                drvdata->dac.mclk = freq;
 279                break;
 280        }
 281
 282        return 0;
 283}
 284
 285static int sti_sas_prepare(struct snd_pcm_substream *substream,
 286                           struct snd_soc_dai *dai)
 287{
 288        struct snd_soc_component *component = dai->component;
 289        struct sti_sas_data *drvdata = dev_get_drvdata(component->dev);
 290        struct snd_pcm_runtime *runtime = substream->runtime;
 291
 292        switch (dai->id) {
 293        case STI_SAS_DAI_SPDIF_OUT:
 294                if ((drvdata->spdif.mclk / runtime->rate) != 128) {
 295                        dev_err(component->dev, "unexpected mclk-fs ratio\n");
 296                        return -EINVAL;
 297                }
 298                break;
 299        case STI_SAS_DAI_ANALOG_OUT:
 300                if ((drvdata->dac.mclk / runtime->rate) != 256) {
 301                        dev_err(component->dev, "unexpected mclk-fs ratio\n");
 302                        return -EINVAL;
 303                }
 304                break;
 305        }
 306
 307        return 0;
 308}
 309
 310static const struct snd_soc_dai_ops stih407_dac_ops = {
 311        .set_fmt = sti_sas_dac_set_fmt,
 312        .mute_stream = stih407_sas_dac_mute,
 313        .prepare = sti_sas_prepare,
 314        .set_sysclk = sti_sas_set_sysclk,
 315};
 316
 317static const struct regmap_config stih407_sas_regmap = {
 318        .reg_bits = 32,
 319        .val_bits = 32,
 320        .fast_io = true,
 321        .max_register = STIH407_AUDIO_DAC_CTRL,
 322        .reg_defaults = stih407_sas_reg_defaults,
 323        .num_reg_defaults = ARRAY_SIZE(stih407_sas_reg_defaults),
 324        .volatile_reg = sti_sas_volatile_register,
 325        .cache_type = REGCACHE_RBTREE,
 326        .reg_read = sti_sas_read_reg,
 327        .reg_write = sti_sas_write_reg,
 328};
 329
 330static const struct sti_sas_dev_data stih407_data = {
 331        .regmap = &stih407_sas_regmap,
 332        .dac_ops = &stih407_dac_ops,
 333        .dapm_widgets = stih407_sas_dapm_widgets,
 334        .num_dapm_widgets = ARRAY_SIZE(stih407_sas_dapm_widgets),
 335        .dapm_routes =  stih407_sas_route,
 336        .num_dapm_routes = ARRAY_SIZE(stih407_sas_route),
 337};
 338
 339static struct snd_soc_dai_driver sti_sas_dai[] = {
 340        {
 341                .name = "sas-dai-spdif-out",
 342                .id = STI_SAS_DAI_SPDIF_OUT,
 343                .playback = {
 344                        .stream_name = "spdif_p",
 345                        .channels_min = 2,
 346                        .channels_max = 2,
 347                        .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
 348                                 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 |
 349                                 SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
 350                                 SNDRV_PCM_RATE_192000,
 351                        .formats = SNDRV_PCM_FMTBIT_S16_LE |
 352                                   SNDRV_PCM_FMTBIT_S32_LE,
 353                },
 354                .ops = (struct snd_soc_dai_ops[]) {
 355                        {
 356                                .set_fmt = sti_sas_spdif_set_fmt,
 357                                .trigger = sti_sas_spdif_trigger,
 358                                .set_sysclk = sti_sas_set_sysclk,
 359                                .prepare = sti_sas_prepare,
 360                        }
 361                },
 362        },
 363        {
 364                .name = "sas-dai-dac",
 365                .id = STI_SAS_DAI_ANALOG_OUT,
 366                .playback = {
 367                        .stream_name = "dac_p",
 368                        .channels_min = 2,
 369                        .channels_max = 2,
 370                        .rates = SNDRV_PCM_RATE_8000_48000,
 371                        .formats = SNDRV_PCM_FMTBIT_S16_LE |
 372                                   SNDRV_PCM_FMTBIT_S32_LE,
 373                },
 374        },
 375};
 376
 377#ifdef CONFIG_PM_SLEEP
 378static int sti_sas_resume(struct snd_soc_component *component)
 379{
 380        struct sti_sas_data *drvdata = dev_get_drvdata(component->dev);
 381
 382        return sti_sas_init_sas_registers(component, drvdata);
 383}
 384#else
 385#define sti_sas_resume NULL
 386#endif
 387
 388static int sti_sas_component_probe(struct snd_soc_component *component)
 389{
 390        struct sti_sas_data *drvdata = dev_get_drvdata(component->dev);
 391        int ret;
 392
 393        ret = sti_sas_init_sas_registers(component, drvdata);
 394
 395        return ret;
 396}
 397
 398static struct snd_soc_component_driver sti_sas_driver = {
 399        .probe                  = sti_sas_component_probe,
 400        .resume                 = sti_sas_resume,
 401        .idle_bias_on           = 1,
 402        .use_pmdown_time        = 1,
 403        .endianness             = 1,
 404        .non_legacy_dai_naming  = 1,
 405};
 406
 407static const struct of_device_id sti_sas_dev_match[] = {
 408        {
 409                .compatible = "st,stih407-sas-codec",
 410                .data = &stih407_data,
 411        },
 412        {},
 413};
 414
 415static int sti_sas_driver_probe(struct platform_device *pdev)
 416{
 417        struct device_node *pnode = pdev->dev.of_node;
 418        struct sti_sas_data *drvdata;
 419        const struct of_device_id *of_id;
 420
 421        /* Allocate device structure */
 422        drvdata = devm_kzalloc(&pdev->dev, sizeof(struct sti_sas_data),
 423                               GFP_KERNEL);
 424        if (!drvdata)
 425                return -ENOMEM;
 426
 427        /* Populate data structure depending on compatibility */
 428        of_id = of_match_node(sti_sas_dev_match, pnode);
 429        if (!of_id->data) {
 430                dev_err(&pdev->dev, "data associated to device is missing\n");
 431                return -EINVAL;
 432        }
 433
 434        drvdata->dev_data = (struct sti_sas_dev_data *)of_id->data;
 435
 436        /* Initialise device structure */
 437        drvdata->dev = &pdev->dev;
 438
 439        /* Request the DAC & SPDIF registers memory region */
 440        drvdata->dac.virt_regmap = devm_regmap_init(&pdev->dev, NULL, drvdata,
 441                                                    drvdata->dev_data->regmap);
 442        if (IS_ERR(drvdata->dac.virt_regmap)) {
 443                dev_err(&pdev->dev, "audio registers not enabled\n");
 444                return PTR_ERR(drvdata->dac.virt_regmap);
 445        }
 446
 447        /* Request the syscon region */
 448        drvdata->dac.regmap =
 449                syscon_regmap_lookup_by_phandle(pnode, "st,syscfg");
 450        if (IS_ERR(drvdata->dac.regmap)) {
 451                dev_err(&pdev->dev, "syscon registers not available\n");
 452                return PTR_ERR(drvdata->dac.regmap);
 453        }
 454        drvdata->spdif.regmap = drvdata->dac.regmap;
 455
 456        sti_sas_dai[STI_SAS_DAI_ANALOG_OUT].ops = drvdata->dev_data->dac_ops;
 457
 458        /* Set dapms*/
 459        sti_sas_driver.dapm_widgets = drvdata->dev_data->dapm_widgets;
 460        sti_sas_driver.num_dapm_widgets = drvdata->dev_data->num_dapm_widgets;
 461
 462        sti_sas_driver.dapm_routes = drvdata->dev_data->dapm_routes;
 463        sti_sas_driver.num_dapm_routes = drvdata->dev_data->num_dapm_routes;
 464
 465        /* Store context */
 466        dev_set_drvdata(&pdev->dev, drvdata);
 467
 468        return devm_snd_soc_register_component(&pdev->dev, &sti_sas_driver,
 469                                        sti_sas_dai,
 470                                        ARRAY_SIZE(sti_sas_dai));
 471}
 472
 473static struct platform_driver sti_sas_platform_driver = {
 474        .driver = {
 475                .name = "sti-sas-codec",
 476                .of_match_table = sti_sas_dev_match,
 477        },
 478        .probe = sti_sas_driver_probe,
 479};
 480
 481module_platform_driver(sti_sas_platform_driver);
 482
 483MODULE_DESCRIPTION("audio codec for STMicroelectronics sti platforms");
 484MODULE_AUTHOR("Arnaud.pouliquen@st.com");
 485MODULE_LICENSE("GPL v2");
 486