linux/sound/soc/mxs/mxs-sgtl5000.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright 2011 Freescale Semiconductor, Inc.
   4 */
   5
   6#include <linux/module.h>
   7#include <linux/device.h>
   8#include <linux/of.h>
   9#include <linux/of_device.h>
  10#include <sound/core.h>
  11#include <sound/pcm.h>
  12#include <sound/soc.h>
  13#include <sound/jack.h>
  14#include <sound/soc-dapm.h>
  15
  16#include "../codecs/sgtl5000.h"
  17#include "mxs-saif.h"
  18
  19static int mxs_sgtl5000_hw_params(struct snd_pcm_substream *substream,
  20        struct snd_pcm_hw_params *params)
  21{
  22        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  23        struct snd_soc_dai *codec_dai = rtd->codec_dai;
  24        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
  25        unsigned int rate = params_rate(params);
  26        u32 mclk;
  27        int ret;
  28
  29        /* sgtl5000 does not support 512*rate when in 96000 fs */
  30        switch (rate) {
  31        case 96000:
  32                mclk = 256 * rate;
  33                break;
  34        default:
  35                mclk = 512 * rate;
  36                break;
  37        }
  38
  39        /* Set SGTL5000's SYSCLK (provided by SAIF MCLK) */
  40        ret = snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, mclk, 0);
  41        if (ret) {
  42                dev_err(codec_dai->dev, "Failed to set sysclk to %u.%03uMHz\n",
  43                        mclk / 1000000, mclk / 1000 % 1000);
  44                return ret;
  45        }
  46
  47        /* The SAIF MCLK should be the same as SGTL5000_SYSCLK */
  48        ret = snd_soc_dai_set_sysclk(cpu_dai, MXS_SAIF_MCLK, mclk, 0);
  49        if (ret) {
  50                dev_err(cpu_dai->dev, "Failed to set sysclk to %u.%03uMHz\n",
  51                        mclk / 1000000, mclk / 1000 % 1000);
  52                return ret;
  53        }
  54
  55        return 0;
  56}
  57
  58static const struct snd_soc_ops mxs_sgtl5000_hifi_ops = {
  59        .hw_params = mxs_sgtl5000_hw_params,
  60};
  61
  62#define MXS_SGTL5000_DAI_FMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \
  63        SND_SOC_DAIFMT_CBS_CFS)
  64
  65
  66SND_SOC_DAILINK_DEFS(hifi_tx,
  67        DAILINK_COMP_ARRAY(COMP_EMPTY()),
  68        DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "sgtl5000")),
  69        DAILINK_COMP_ARRAY(COMP_EMPTY()));
  70
  71SND_SOC_DAILINK_DEFS(hifi_rx,
  72        DAILINK_COMP_ARRAY(COMP_EMPTY()),
  73        DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "sgtl5000")),
  74        DAILINK_COMP_ARRAY(COMP_EMPTY()));
  75
  76static struct snd_soc_dai_link mxs_sgtl5000_dai[] = {
  77        {
  78                .name           = "HiFi Tx",
  79                .stream_name    = "HiFi Playback",
  80                .dai_fmt        = MXS_SGTL5000_DAI_FMT,
  81                .ops            = &mxs_sgtl5000_hifi_ops,
  82                .playback_only  = true,
  83                SND_SOC_DAILINK_REG(hifi_tx),
  84        }, {
  85                .name           = "HiFi Rx",
  86                .stream_name    = "HiFi Capture",
  87                .dai_fmt        = MXS_SGTL5000_DAI_FMT,
  88                .ops            = &mxs_sgtl5000_hifi_ops,
  89                .capture_only   = true,
  90                SND_SOC_DAILINK_REG(hifi_rx),
  91        },
  92};
  93
  94static const struct snd_soc_dapm_widget mxs_sgtl5000_dapm_widgets[] = {
  95        SND_SOC_DAPM_MIC("Mic Jack", NULL),
  96        SND_SOC_DAPM_LINE("Line In Jack", NULL),
  97        SND_SOC_DAPM_HP("Headphone Jack", NULL),
  98        SND_SOC_DAPM_SPK("Line Out Jack", NULL),
  99        SND_SOC_DAPM_SPK("Ext Spk", NULL),
 100};
 101
 102static struct snd_soc_card mxs_sgtl5000 = {
 103        .name           = "mxs_sgtl5000",
 104        .owner          = THIS_MODULE,
 105        .dai_link       = mxs_sgtl5000_dai,
 106        .num_links      = ARRAY_SIZE(mxs_sgtl5000_dai),
 107};
 108
 109static int mxs_sgtl5000_probe(struct platform_device *pdev)
 110{
 111        struct snd_soc_card *card = &mxs_sgtl5000;
 112        int ret, i;
 113        struct device_node *np = pdev->dev.of_node;
 114        struct device_node *saif_np[2], *codec_np;
 115
 116        saif_np[0] = of_parse_phandle(np, "saif-controllers", 0);
 117        saif_np[1] = of_parse_phandle(np, "saif-controllers", 1);
 118        codec_np = of_parse_phandle(np, "audio-codec", 0);
 119        if (!saif_np[0] || !saif_np[1] || !codec_np) {
 120                dev_err(&pdev->dev, "phandle missing or invalid\n");
 121                return -EINVAL;
 122        }
 123
 124        for (i = 0; i < 2; i++) {
 125                mxs_sgtl5000_dai[i].codecs->name = NULL;
 126                mxs_sgtl5000_dai[i].codecs->of_node = codec_np;
 127                mxs_sgtl5000_dai[i].cpus->dai_name = NULL;
 128                mxs_sgtl5000_dai[i].cpus->of_node = saif_np[i];
 129                mxs_sgtl5000_dai[i].platforms->name = NULL;
 130                mxs_sgtl5000_dai[i].platforms->of_node = saif_np[i];
 131        }
 132
 133        of_node_put(codec_np);
 134        of_node_put(saif_np[0]);
 135        of_node_put(saif_np[1]);
 136
 137        /*
 138         * Set an init clock(11.28Mhz) for sgtl5000 initialization(i2c r/w).
 139         * The Sgtl5000 sysclk is derived from saif0 mclk and it's range
 140         * should be >= 8MHz and <= 27M.
 141         */
 142        ret = mxs_saif_get_mclk(0, 44100 * 256, 44100);
 143        if (ret) {
 144                dev_err(&pdev->dev, "failed to get mclk\n");
 145                return ret;
 146        }
 147
 148        card->dev = &pdev->dev;
 149
 150        if (of_find_property(np, "audio-routing", NULL)) {
 151                card->dapm_widgets = mxs_sgtl5000_dapm_widgets;
 152                card->num_dapm_widgets = ARRAY_SIZE(mxs_sgtl5000_dapm_widgets);
 153
 154                ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
 155                if (ret) {
 156                        dev_err(&pdev->dev, "failed to parse audio-routing (%d)\n",
 157                                ret);
 158                        return ret;
 159                }
 160        }
 161
 162        ret = devm_snd_soc_register_card(&pdev->dev, card);
 163        if (ret) {
 164                if (ret != -EPROBE_DEFER)
 165                        dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
 166                                ret);
 167                return ret;
 168        }
 169
 170        return 0;
 171}
 172
 173static int mxs_sgtl5000_remove(struct platform_device *pdev)
 174{
 175        mxs_saif_put_mclk(0);
 176
 177        return 0;
 178}
 179
 180static const struct of_device_id mxs_sgtl5000_dt_ids[] = {
 181        { .compatible = "fsl,mxs-audio-sgtl5000", },
 182        { /* sentinel */ }
 183};
 184MODULE_DEVICE_TABLE(of, mxs_sgtl5000_dt_ids);
 185
 186static struct platform_driver mxs_sgtl5000_audio_driver = {
 187        .driver = {
 188                .name = "mxs-sgtl5000",
 189                .of_match_table = mxs_sgtl5000_dt_ids,
 190        },
 191        .probe = mxs_sgtl5000_probe,
 192        .remove = mxs_sgtl5000_remove,
 193};
 194
 195module_platform_driver(mxs_sgtl5000_audio_driver);
 196
 197MODULE_AUTHOR("Freescale Semiconductor, Inc.");
 198MODULE_DESCRIPTION("MXS ALSA SoC Machine driver");
 199MODULE_LICENSE("GPL");
 200MODULE_ALIAS("platform:mxs-sgtl5000");
 201