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