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
  65static struct snd_soc_dai_link mxs_sgtl5000_dai[] = {
  66        {
  67                .name           = "HiFi Tx",
  68                .stream_name    = "HiFi Playback",
  69                .codec_dai_name = "sgtl5000",
  70                .dai_fmt        = MXS_SGTL5000_DAI_FMT,
  71                .ops            = &mxs_sgtl5000_hifi_ops,
  72                .playback_only  = true,
  73        }, {
  74                .name           = "HiFi Rx",
  75                .stream_name    = "HiFi Capture",
  76                .codec_dai_name = "sgtl5000",
  77                .dai_fmt        = MXS_SGTL5000_DAI_FMT,
  78                .ops            = &mxs_sgtl5000_hifi_ops,
  79                .capture_only   = true,
  80        },
  81};
  82
  83static const struct snd_soc_dapm_widget mxs_sgtl5000_dapm_widgets[] = {
  84        SND_SOC_DAPM_MIC("Mic Jack", NULL),
  85        SND_SOC_DAPM_LINE("Line In Jack", NULL),
  86        SND_SOC_DAPM_HP("Headphone Jack", NULL),
  87        SND_SOC_DAPM_SPK("Line Out Jack", NULL),
  88        SND_SOC_DAPM_SPK("Ext Spk", NULL),
  89};
  90
  91static struct snd_soc_card mxs_sgtl5000 = {
  92        .name           = "mxs_sgtl5000",
  93        .owner          = THIS_MODULE,
  94        .dai_link       = mxs_sgtl5000_dai,
  95        .num_links      = ARRAY_SIZE(mxs_sgtl5000_dai),
  96};
  97
  98static int mxs_sgtl5000_probe(struct platform_device *pdev)
  99{
 100        struct snd_soc_card *card = &mxs_sgtl5000;
 101        int ret, i;
 102        struct device_node *np = pdev->dev.of_node;
 103        struct device_node *saif_np[2], *codec_np;
 104
 105        saif_np[0] = of_parse_phandle(np, "saif-controllers", 0);
 106        saif_np[1] = of_parse_phandle(np, "saif-controllers", 1);
 107        codec_np = of_parse_phandle(np, "audio-codec", 0);
 108        if (!saif_np[0] || !saif_np[1] || !codec_np) {
 109                dev_err(&pdev->dev, "phandle missing or invalid\n");
 110                return -EINVAL;
 111        }
 112
 113        for (i = 0; i < 2; i++) {
 114                mxs_sgtl5000_dai[i].codec_name = NULL;
 115                mxs_sgtl5000_dai[i].codec_of_node = codec_np;
 116                mxs_sgtl5000_dai[i].cpu_dai_name = NULL;
 117                mxs_sgtl5000_dai[i].cpu_of_node = saif_np[i];
 118                mxs_sgtl5000_dai[i].platform_name = NULL;
 119                mxs_sgtl5000_dai[i].platform_of_node = saif_np[i];
 120        }
 121
 122        of_node_put(codec_np);
 123        of_node_put(saif_np[0]);
 124        of_node_put(saif_np[1]);
 125
 126        /*
 127         * Set an init clock(11.28Mhz) for sgtl5000 initialization(i2c r/w).
 128         * The Sgtl5000 sysclk is derived from saif0 mclk and it's range
 129         * should be >= 8MHz and <= 27M.
 130         */
 131        ret = mxs_saif_get_mclk(0, 44100 * 256, 44100);
 132        if (ret) {
 133                dev_err(&pdev->dev, "failed to get mclk\n");
 134                return ret;
 135        }
 136
 137        card->dev = &pdev->dev;
 138
 139        if (of_find_property(np, "audio-routing", NULL)) {
 140                card->dapm_widgets = mxs_sgtl5000_dapm_widgets;
 141                card->num_dapm_widgets = ARRAY_SIZE(mxs_sgtl5000_dapm_widgets);
 142
 143                ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
 144                if (ret) {
 145                        dev_err(&pdev->dev, "failed to parse audio-routing (%d)\n",
 146                                ret);
 147                        return ret;
 148                }
 149        }
 150
 151        ret = devm_snd_soc_register_card(&pdev->dev, card);
 152        if (ret) {
 153                if (ret != -EPROBE_DEFER)
 154                        dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
 155                                ret);
 156                return ret;
 157        }
 158
 159        return 0;
 160}
 161
 162static int mxs_sgtl5000_remove(struct platform_device *pdev)
 163{
 164        mxs_saif_put_mclk(0);
 165
 166        return 0;
 167}
 168
 169static const struct of_device_id mxs_sgtl5000_dt_ids[] = {
 170        { .compatible = "fsl,mxs-audio-sgtl5000", },
 171        { /* sentinel */ }
 172};
 173MODULE_DEVICE_TABLE(of, mxs_sgtl5000_dt_ids);
 174
 175static struct platform_driver mxs_sgtl5000_audio_driver = {
 176        .driver = {
 177                .name = "mxs-sgtl5000",
 178                .of_match_table = mxs_sgtl5000_dt_ids,
 179        },
 180        .probe = mxs_sgtl5000_probe,
 181        .remove = mxs_sgtl5000_remove,
 182};
 183
 184module_platform_driver(mxs_sgtl5000_audio_driver);
 185
 186MODULE_AUTHOR("Freescale Semiconductor, Inc.");
 187MODULE_DESCRIPTION("MXS ALSA SoC Machine driver");
 188MODULE_LICENSE("GPL");
 189MODULE_ALIAS("platform:mxs-sgtl5000");
 190