linux/sound/soc/intel/boards/bytcht_da7213.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 *  bytcht-da7213.c - ASoc Machine driver for Intel Baytrail and
   4 *             Cherrytrail-based platforms, with Dialog DA7213 codec
   5 *
   6 *  Copyright (C) 2017 Intel Corporation
   7 *  Author: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
   8 *
   9 *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  10 *
  11 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  12 */
  13
  14#include <linux/module.h>
  15#include <linux/acpi.h>
  16#include <linux/platform_device.h>
  17#include <linux/slab.h>
  18#include <sound/pcm.h>
  19#include <sound/pcm_params.h>
  20#include <sound/soc.h>
  21#include <sound/soc-acpi.h>
  22#include "../../codecs/da7213.h"
  23#include "../atom/sst-atom-controls.h"
  24
  25static const struct snd_kcontrol_new controls[] = {
  26        SOC_DAPM_PIN_SWITCH("Headphone Jack"),
  27        SOC_DAPM_PIN_SWITCH("Headset Mic"),
  28        SOC_DAPM_PIN_SWITCH("Mic"),
  29        SOC_DAPM_PIN_SWITCH("Aux In"),
  30};
  31
  32static const struct snd_soc_dapm_widget dapm_widgets[] = {
  33        SND_SOC_DAPM_HP("Headphone Jack", NULL),
  34        SND_SOC_DAPM_MIC("Headset Mic", NULL),
  35        SND_SOC_DAPM_MIC("Mic", NULL),
  36        SND_SOC_DAPM_LINE("Aux In", NULL),
  37};
  38
  39static const struct snd_soc_dapm_route audio_map[] = {
  40        {"Headphone Jack", NULL, "HPL"},
  41        {"Headphone Jack", NULL, "HPR"},
  42
  43        {"AUXL", NULL, "Aux In"},
  44        {"AUXR", NULL, "Aux In"},
  45
  46        /* Assume Mic1 is linked to Headset and Mic2 to on-board mic */
  47        {"MIC1", NULL, "Headset Mic"},
  48        {"MIC2", NULL, "Mic"},
  49
  50        /* SOC-codec link */
  51        {"ssp2 Tx", NULL, "codec_out0"},
  52        {"ssp2 Tx", NULL, "codec_out1"},
  53        {"codec_in0", NULL, "ssp2 Rx"},
  54        {"codec_in1", NULL, "ssp2 Rx"},
  55
  56        {"Playback", NULL, "ssp2 Tx"},
  57        {"ssp2 Rx", NULL, "Capture"},
  58};
  59
  60static int codec_fixup(struct snd_soc_pcm_runtime *rtd,
  61                       struct snd_pcm_hw_params *params)
  62{
  63        int ret;
  64        struct snd_interval *rate = hw_param_interval(params,
  65                        SNDRV_PCM_HW_PARAM_RATE);
  66        struct snd_interval *channels = hw_param_interval(params,
  67                                                SNDRV_PCM_HW_PARAM_CHANNELS);
  68
  69        /* The DSP will convert the FE rate to 48k, stereo, 24bits */
  70        rate->min = rate->max = 48000;
  71        channels->min = channels->max = 2;
  72
  73        /* set SSP2 to 24-bit */
  74        params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
  75
  76        /*
  77         * Default mode for SSP configuration is TDM 4 slot, override config
  78         * with explicit setting to I2S 2ch 24-bit. The word length is set with
  79         * dai_set_tdm_slot() since there is no other API exposed
  80         */
  81        ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0),
  82                                  SND_SOC_DAIFMT_I2S     |
  83                                  SND_SOC_DAIFMT_NB_NF   |
  84                                  SND_SOC_DAIFMT_CBS_CFS);
  85        if (ret < 0) {
  86                dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
  87                return ret;
  88        }
  89
  90        ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, 24);
  91        if (ret < 0) {
  92                dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
  93                return ret;
  94        }
  95
  96        return 0;
  97}
  98
  99static int aif1_startup(struct snd_pcm_substream *substream)
 100{
 101        return snd_pcm_hw_constraint_single(substream->runtime,
 102                        SNDRV_PCM_HW_PARAM_RATE, 48000);
 103}
 104
 105static int aif1_hw_params(struct snd_pcm_substream *substream,
 106                          struct snd_pcm_hw_params *params)
 107{
 108        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 109        struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
 110        int ret;
 111
 112        ret = snd_soc_dai_set_sysclk(codec_dai, DA7213_CLKSRC_MCLK,
 113                                     19200000, SND_SOC_CLOCK_IN);
 114        if (ret < 0)
 115                dev_err(codec_dai->dev, "can't set codec sysclk configuration\n");
 116
 117        ret = snd_soc_dai_set_pll(codec_dai, 0,
 118                        DA7213_SYSCLK_PLL_SRM, 0, DA7213_PLL_FREQ_OUT_98304000);
 119        if (ret < 0) {
 120                dev_err(codec_dai->dev, "failed to start PLL: %d\n", ret);
 121                return -EIO;
 122        }
 123
 124        return ret;
 125}
 126
 127static int aif1_hw_free(struct snd_pcm_substream *substream)
 128{
 129        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 130        struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
 131        int ret;
 132
 133        ret = snd_soc_dai_set_pll(codec_dai, 0,
 134                                  DA7213_SYSCLK_MCLK, 0, 0);
 135        if (ret < 0) {
 136                dev_err(codec_dai->dev, "failed to stop PLL: %d\n", ret);
 137                return -EIO;
 138        }
 139
 140        return ret;
 141}
 142
 143static const struct snd_soc_ops aif1_ops = {
 144        .startup = aif1_startup,
 145};
 146
 147static const struct snd_soc_ops ssp2_ops = {
 148        .hw_params = aif1_hw_params,
 149        .hw_free = aif1_hw_free,
 150
 151};
 152
 153SND_SOC_DAILINK_DEF(dummy,
 154        DAILINK_COMP_ARRAY(COMP_DUMMY()));
 155
 156SND_SOC_DAILINK_DEF(media,
 157        DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai")));
 158
 159SND_SOC_DAILINK_DEF(deepbuffer,
 160        DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai")));
 161
 162SND_SOC_DAILINK_DEF(ssp2_port,
 163        DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port")));
 164SND_SOC_DAILINK_DEF(ssp2_codec,
 165        DAILINK_COMP_ARRAY(COMP_CODEC("i2c-DLGS7213:00",
 166                                      "da7213-hifi")));
 167
 168SND_SOC_DAILINK_DEF(platform,
 169        DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform")));
 170
 171static struct snd_soc_dai_link dailink[] = {
 172        [MERR_DPCM_AUDIO] = {
 173                .name = "Audio Port",
 174                .stream_name = "Audio",
 175                .nonatomic = true,
 176                .dynamic = 1,
 177                .dpcm_playback = 1,
 178                .dpcm_capture = 1,
 179                .ops = &aif1_ops,
 180                SND_SOC_DAILINK_REG(media, dummy, platform),
 181        },
 182        [MERR_DPCM_DEEP_BUFFER] = {
 183                .name = "Deep-Buffer Audio Port",
 184                .stream_name = "Deep-Buffer Audio",
 185                .nonatomic = true,
 186                .dynamic = 1,
 187                .dpcm_playback = 1,
 188                .ops = &aif1_ops,
 189                SND_SOC_DAILINK_REG(deepbuffer, dummy, platform),
 190        },
 191        /* CODEC<->CODEC link */
 192        /* back ends */
 193        {
 194                .name = "SSP2-Codec",
 195                .id = 0,
 196                .no_pcm = 1,
 197                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
 198                                                | SND_SOC_DAIFMT_CBS_CFS,
 199                .be_hw_params_fixup = codec_fixup,
 200                .nonatomic = true,
 201                .dpcm_playback = 1,
 202                .dpcm_capture = 1,
 203                .ops = &ssp2_ops,
 204                SND_SOC_DAILINK_REG(ssp2_port, ssp2_codec, platform),
 205        },
 206};
 207
 208/* SoC card */
 209static struct snd_soc_card bytcht_da7213_card = {
 210        .name = "bytcht-da7213",
 211        .owner = THIS_MODULE,
 212        .dai_link = dailink,
 213        .num_links = ARRAY_SIZE(dailink),
 214        .controls = controls,
 215        .num_controls = ARRAY_SIZE(controls),
 216        .dapm_widgets = dapm_widgets,
 217        .num_dapm_widgets = ARRAY_SIZE(dapm_widgets),
 218        .dapm_routes = audio_map,
 219        .num_dapm_routes = ARRAY_SIZE(audio_map),
 220};
 221
 222static char codec_name[SND_ACPI_I2C_ID_LEN];
 223
 224static int bytcht_da7213_probe(struct platform_device *pdev)
 225{
 226        struct snd_soc_card *card;
 227        struct snd_soc_acpi_mach *mach;
 228        const char *platform_name;
 229        struct acpi_device *adev;
 230        int dai_index = 0;
 231        int ret_val = 0;
 232        int i;
 233
 234        mach = pdev->dev.platform_data;
 235        card = &bytcht_da7213_card;
 236        card->dev = &pdev->dev;
 237
 238        /* fix index of codec dai */
 239        for (i = 0; i < ARRAY_SIZE(dailink); i++) {
 240                if (!strcmp(dailink[i].codecs->name, "i2c-DLGS7213:00")) {
 241                        dai_index = i;
 242                        break;
 243                }
 244        }
 245
 246        /* fixup codec name based on HID */
 247        adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1);
 248        if (adev) {
 249                snprintf(codec_name, sizeof(codec_name),
 250                         "i2c-%s", acpi_dev_name(adev));
 251                put_device(&adev->dev);
 252                dailink[dai_index].codecs->name = codec_name;
 253        }
 254
 255        /* override plaform name, if required */
 256        platform_name = mach->mach_params.platform;
 257
 258        ret_val = snd_soc_fixup_dai_links_platform_name(card, platform_name);
 259        if (ret_val)
 260                return ret_val;
 261
 262        ret_val = devm_snd_soc_register_card(&pdev->dev, card);
 263        if (ret_val) {
 264                dev_err(&pdev->dev,
 265                        "snd_soc_register_card failed %d\n", ret_val);
 266                return ret_val;
 267        }
 268        platform_set_drvdata(pdev, card);
 269        return ret_val;
 270}
 271
 272static struct platform_driver bytcht_da7213_driver = {
 273        .driver = {
 274                .name = "bytcht_da7213",
 275        },
 276        .probe = bytcht_da7213_probe,
 277};
 278module_platform_driver(bytcht_da7213_driver);
 279
 280MODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail+DA7213 Machine driver");
 281MODULE_AUTHOR("Pierre-Louis Bossart");
 282MODULE_LICENSE("GPL v2");
 283MODULE_ALIAS("platform:bytcht_da7213");
 284