linux/sound/soc/intel/boards/bytcht_es8316.c
<<
>>
Prefs
   1/*
   2 *  bytcht_es8316.c - ASoc Machine driver for Intel Baytrail/Cherrytrail
   3 *                    platforms with Everest ES8316 SoC
   4 *
   5 *  Copyright (C) 2017 Endless Mobile, Inc.
   6 *  Authors: David Yang <yangxiaohua@everest-semi.com>,
   7 *           Daniel Drake <drake@endlessm.com>
   8 *
   9 *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  10 *
  11 *  This program is free software; you can redistribute it and/or modify
  12 *  it under the terms of the GNU General Public License as published by
  13 *  the Free Software Foundation; version 2 of the License.
  14 *
  15 *  This program is distributed in the hope that it will be useful, but
  16 *  WITHOUT ANY WARRANTY; without even the implied warranty of
  17 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  18 *  General Public License for more details.
  19 *
  20 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  21 */
  22#include <linux/init.h>
  23#include <linux/module.h>
  24#include <linux/platform_device.h>
  25#include <linux/device.h>
  26#include <linux/slab.h>
  27#include <asm/platform_sst_audio.h>
  28#include <linux/clk.h>
  29#include <sound/pcm.h>
  30#include <sound/pcm_params.h>
  31#include <sound/soc.h>
  32#include <sound/soc-acpi.h>
  33#include "../atom/sst-atom-controls.h"
  34#include "../common/sst-dsp.h"
  35
  36struct byt_cht_es8316_private {
  37        struct clk *mclk;
  38};
  39
  40static const struct snd_soc_dapm_widget byt_cht_es8316_widgets[] = {
  41        SND_SOC_DAPM_HP("Headphone", NULL),
  42
  43        /*
  44         * The codec supports two analog microphone inputs. I have only
  45         * tested MIC1. A DMIC route could also potentially be added
  46         * if such functionality is found on another platform.
  47         */
  48        SND_SOC_DAPM_MIC("Microphone 1", NULL),
  49        SND_SOC_DAPM_MIC("Microphone 2", NULL),
  50};
  51
  52static const struct snd_soc_dapm_route byt_cht_es8316_audio_map[] = {
  53        {"MIC1", NULL, "Microphone 1"},
  54        {"MIC2", NULL, "Microphone 2"},
  55
  56        {"Headphone", NULL, "HPOL"},
  57        {"Headphone", NULL, "HPOR"},
  58
  59        {"Playback", NULL, "ssp2 Tx"},
  60        {"ssp2 Tx", NULL, "codec_out0"},
  61        {"ssp2 Tx", NULL, "codec_out1"},
  62        {"codec_in0", NULL, "ssp2 Rx" },
  63        {"codec_in1", NULL, "ssp2 Rx" },
  64        {"ssp2 Rx", NULL, "Capture"},
  65};
  66
  67static const struct snd_kcontrol_new byt_cht_es8316_controls[] = {
  68        SOC_DAPM_PIN_SWITCH("Headphone"),
  69        SOC_DAPM_PIN_SWITCH("Microphone 1"),
  70        SOC_DAPM_PIN_SWITCH("Microphone 2"),
  71};
  72
  73static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime)
  74{
  75        struct snd_soc_card *card = runtime->card;
  76        struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card);
  77        int ret;
  78
  79        card->dapm.idle_bias_off = true;
  80
  81        /*
  82         * The firmware might enable the clock at boot (this information
  83         * may or may not be reflected in the enable clock register).
  84         * To change the rate we must disable the clock first to cover these
  85         * cases. Due to common clock framework restrictions that do not allow
  86         * to disable a clock that has not been enabled, we need to enable
  87         * the clock first.
  88         */
  89        ret = clk_prepare_enable(priv->mclk);
  90        if (!ret)
  91                clk_disable_unprepare(priv->mclk);
  92
  93        ret = clk_set_rate(priv->mclk, 19200000);
  94        if (ret)
  95                dev_err(card->dev, "unable to set MCLK rate\n");
  96
  97        ret = clk_prepare_enable(priv->mclk);
  98        if (ret)
  99                dev_err(card->dev, "unable to enable MCLK\n");
 100
 101        ret = snd_soc_dai_set_sysclk(runtime->codec_dai, 0, 19200000,
 102                                     SND_SOC_CLOCK_IN);
 103        if (ret < 0) {
 104                dev_err(card->dev, "can't set codec clock %d\n", ret);
 105                return ret;
 106        }
 107
 108        return 0;
 109}
 110
 111static const struct snd_soc_pcm_stream byt_cht_es8316_dai_params = {
 112        .formats = SNDRV_PCM_FMTBIT_S24_LE,
 113        .rate_min = 48000,
 114        .rate_max = 48000,
 115        .channels_min = 2,
 116        .channels_max = 2,
 117};
 118
 119static int byt_cht_es8316_codec_fixup(struct snd_soc_pcm_runtime *rtd,
 120                            struct snd_pcm_hw_params *params)
 121{
 122        struct snd_interval *rate = hw_param_interval(params,
 123                        SNDRV_PCM_HW_PARAM_RATE);
 124        struct snd_interval *channels = hw_param_interval(params,
 125                                                SNDRV_PCM_HW_PARAM_CHANNELS);
 126        int ret;
 127
 128        /* The DSP will covert the FE rate to 48k, stereo */
 129        rate->min = rate->max = 48000;
 130        channels->min = channels->max = 2;
 131
 132        /* set SSP2 to 24-bit */
 133        params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
 134
 135        /*
 136         * Default mode for SSP configuration is TDM 4 slot, override config
 137         * with explicit setting to I2S 2ch 24-bit. The word length is set with
 138         * dai_set_tdm_slot() since there is no other API exposed
 139         */
 140        ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
 141                                SND_SOC_DAIFMT_I2S     |
 142                                SND_SOC_DAIFMT_NB_NF   |
 143                                SND_SOC_DAIFMT_CBS_CFS
 144                );
 145        if (ret < 0) {
 146                dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
 147                return ret;
 148        }
 149
 150        ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24);
 151        if (ret < 0) {
 152                dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
 153                return ret;
 154        }
 155
 156        return 0;
 157}
 158
 159static int byt_cht_es8316_aif1_startup(struct snd_pcm_substream *substream)
 160{
 161        return snd_pcm_hw_constraint_single(substream->runtime,
 162                        SNDRV_PCM_HW_PARAM_RATE, 48000);
 163}
 164
 165static const struct snd_soc_ops byt_cht_es8316_aif1_ops = {
 166        .startup = byt_cht_es8316_aif1_startup,
 167};
 168
 169static struct snd_soc_dai_link byt_cht_es8316_dais[] = {
 170        [MERR_DPCM_AUDIO] = {
 171                .name = "Audio Port",
 172                .stream_name = "Audio",
 173                .cpu_dai_name = "media-cpu-dai",
 174                .codec_dai_name = "snd-soc-dummy-dai",
 175                .codec_name = "snd-soc-dummy",
 176                .platform_name = "sst-mfld-platform",
 177                .nonatomic = true,
 178                .dynamic = 1,
 179                .dpcm_playback = 1,
 180                .dpcm_capture = 1,
 181                .ops = &byt_cht_es8316_aif1_ops,
 182        },
 183
 184        [MERR_DPCM_DEEP_BUFFER] = {
 185                .name = "Deep-Buffer Audio Port",
 186                .stream_name = "Deep-Buffer Audio",
 187                .cpu_dai_name = "deepbuffer-cpu-dai",
 188                .codec_dai_name = "snd-soc-dummy-dai",
 189                .codec_name = "snd-soc-dummy",
 190                .platform_name = "sst-mfld-platform",
 191                .nonatomic = true,
 192                .dynamic = 1,
 193                .dpcm_playback = 1,
 194                .ops = &byt_cht_es8316_aif1_ops,
 195        },
 196
 197                /* back ends */
 198        {
 199                /* Only SSP2 has been tested here, so BYT-CR platforms that
 200                 * require SSP0 will not work.
 201                 */
 202                .name = "SSP2-Codec",
 203                .id = 0,
 204                .cpu_dai_name = "ssp2-port",
 205                .platform_name = "sst-mfld-platform",
 206                .no_pcm = 1,
 207                .codec_dai_name = "ES8316 HiFi",
 208                .codec_name = "i2c-ESSX8316:00",
 209                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
 210                                                | SND_SOC_DAIFMT_CBS_CFS,
 211                .be_hw_params_fixup = byt_cht_es8316_codec_fixup,
 212                .nonatomic = true,
 213                .dpcm_playback = 1,
 214                .dpcm_capture = 1,
 215                .init = byt_cht_es8316_init,
 216        },
 217};
 218
 219
 220/* SoC card */
 221static struct snd_soc_card byt_cht_es8316_card = {
 222        .name = "bytcht-es8316",
 223        .owner = THIS_MODULE,
 224        .dai_link = byt_cht_es8316_dais,
 225        .num_links = ARRAY_SIZE(byt_cht_es8316_dais),
 226        .dapm_widgets = byt_cht_es8316_widgets,
 227        .num_dapm_widgets = ARRAY_SIZE(byt_cht_es8316_widgets),
 228        .dapm_routes = byt_cht_es8316_audio_map,
 229        .num_dapm_routes = ARRAY_SIZE(byt_cht_es8316_audio_map),
 230        .controls = byt_cht_es8316_controls,
 231        .num_controls = ARRAY_SIZE(byt_cht_es8316_controls),
 232        .fully_routed = true,
 233};
 234
 235static char codec_name[SND_ACPI_I2C_ID_LEN];
 236
 237static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev)
 238{
 239        struct byt_cht_es8316_private *priv;
 240        struct snd_soc_acpi_mach *mach;
 241        const char *i2c_name = NULL;
 242        int dai_index = 0;
 243        int i;
 244        int ret = 0;
 245
 246        priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC);
 247        if (!priv)
 248                return -ENOMEM;
 249
 250        mach = (&pdev->dev)->platform_data;
 251        /* fix index of codec dai */
 252        for (i = 0; i < ARRAY_SIZE(byt_cht_es8316_dais); i++) {
 253                if (!strcmp(byt_cht_es8316_dais[i].codec_name,
 254                            "i2c-ESSX8316:00")) {
 255                        dai_index = i;
 256                        break;
 257                }
 258        }
 259
 260        /* fixup codec name based on HID */
 261        i2c_name = acpi_dev_get_first_match_name(mach->id, NULL, -1);
 262        if (i2c_name) {
 263                snprintf(codec_name, sizeof(codec_name),
 264                        "%s%s", "i2c-", i2c_name);
 265                byt_cht_es8316_dais[dai_index].codec_name = codec_name;
 266        }
 267
 268        /* register the soc card */
 269        byt_cht_es8316_card.dev = &pdev->dev;
 270        snd_soc_card_set_drvdata(&byt_cht_es8316_card, priv);
 271
 272        priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
 273        if (IS_ERR(priv->mclk)) {
 274                ret = PTR_ERR(priv->mclk);
 275                dev_err(&pdev->dev,
 276                        "Failed to get MCLK from pmc_plt_clk_3: %d\n",
 277                        ret);
 278                return ret;
 279        }
 280
 281        ret = devm_snd_soc_register_card(&pdev->dev, &byt_cht_es8316_card);
 282        if (ret) {
 283                dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret);
 284                return ret;
 285        }
 286        platform_set_drvdata(pdev, &byt_cht_es8316_card);
 287        return ret;
 288}
 289
 290static struct platform_driver snd_byt_cht_es8316_mc_driver = {
 291        .driver = {
 292                .name = "bytcht_es8316",
 293        },
 294        .probe = snd_byt_cht_es8316_mc_probe,
 295};
 296
 297module_platform_driver(snd_byt_cht_es8316_mc_driver);
 298MODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail Machine driver");
 299MODULE_AUTHOR("David Yang <yangxiaohua@everest-semi.com>");
 300MODULE_LICENSE("GPL v2");
 301MODULE_ALIAS("platform:bytcht_es8316");
 302