linux/sound/soc/intel/cht_bsw_rt5672.c
<<
>>
Prefs
   1/*
   2 *  cht_bsw_rt5672.c - ASoc Machine driver for Intel Cherryview-based platforms
   3 *                     Cherrytrail and Braswell, with RT5672 codec.
   4 *
   5 *  Copyright (C) 2014 Intel Corp
   6 *  Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
   7 *          Mengdong Lin <mengdong.lin@intel.com>
   8 *
   9 *  This program is free software; you can redistribute it and/or modify
  10 *  it under the terms of the GNU General Public License as published by
  11 *  the Free Software Foundation; version 2 of the License.
  12 *
  13 *  This program is distributed in the hope that it will be useful, but
  14 *  WITHOUT ANY WARRANTY; without even the implied warranty of
  15 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16 *  General Public License for more details.
  17 */
  18
  19#include <linux/module.h>
  20#include <linux/platform_device.h>
  21#include <linux/slab.h>
  22#include <sound/pcm.h>
  23#include <sound/pcm_params.h>
  24#include <sound/soc.h>
  25#include "../codecs/rt5670.h"
  26#include "sst-atom-controls.h"
  27
  28/* The platform clock #3 outputs 19.2Mhz clock to codec as I2S MCLK */
  29#define CHT_PLAT_CLK_3_HZ       19200000
  30#define CHT_CODEC_DAI   "rt5670-aif1"
  31
  32static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
  33{
  34        int i;
  35
  36        for (i = 0; i < card->num_rtd; i++) {
  37                struct snd_soc_pcm_runtime *rtd;
  38
  39                rtd = card->rtd + i;
  40                if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI,
  41                             strlen(CHT_CODEC_DAI)))
  42                        return rtd->codec_dai;
  43        }
  44        return NULL;
  45}
  46
  47static int platform_clock_control(struct snd_soc_dapm_widget *w,
  48                struct snd_kcontrol *k, int  event)
  49{
  50        struct snd_soc_dapm_context *dapm = w->dapm;
  51        struct snd_soc_card *card = dapm->card;
  52        struct snd_soc_dai *codec_dai;
  53
  54        codec_dai = cht_get_codec_dai(card);
  55        if (!codec_dai) {
  56                dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n");
  57                return -EIO;
  58        }
  59
  60        if (!SND_SOC_DAPM_EVENT_OFF(event))
  61                return 0;
  62
  63        /* Set codec sysclk source to its internal clock because codec PLL will
  64         * be off when idle and MCLK will also be off by ACPI when codec is
  65         * runtime suspended. Codec needs clock for jack detection and button
  66         * press.
  67         */
  68        snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_RCCLK,
  69                               0, SND_SOC_CLOCK_IN);
  70
  71        return 0;
  72}
  73
  74static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
  75        SND_SOC_DAPM_HP("Headphone", NULL),
  76        SND_SOC_DAPM_MIC("Headset Mic", NULL),
  77        SND_SOC_DAPM_MIC("Int Mic", NULL),
  78        SND_SOC_DAPM_SPK("Ext Spk", NULL),
  79        SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
  80                        platform_clock_control, SND_SOC_DAPM_POST_PMD),
  81};
  82
  83static const struct snd_soc_dapm_route cht_audio_map[] = {
  84        {"IN1P", NULL, "Headset Mic"},
  85        {"IN1N", NULL, "Headset Mic"},
  86        {"DMIC L1", NULL, "Int Mic"},
  87        {"DMIC R1", NULL, "Int Mic"},
  88        {"Headphone", NULL, "HPOL"},
  89        {"Headphone", NULL, "HPOR"},
  90        {"Ext Spk", NULL, "SPOLP"},
  91        {"Ext Spk", NULL, "SPOLN"},
  92        {"Ext Spk", NULL, "SPORP"},
  93        {"Ext Spk", NULL, "SPORN"},
  94        {"AIF1 Playback", NULL, "ssp2 Tx"},
  95        {"ssp2 Tx", NULL, "codec_out0"},
  96        {"ssp2 Tx", NULL, "codec_out1"},
  97        {"codec_in0", NULL, "ssp2 Rx"},
  98        {"codec_in1", NULL, "ssp2 Rx"},
  99        {"ssp2 Rx", NULL, "AIF1 Capture"},
 100        {"Headphone", NULL, "Platform Clock"},
 101        {"Headset Mic", NULL, "Platform Clock"},
 102        {"Int Mic", NULL, "Platform Clock"},
 103        {"Ext Spk", NULL, "Platform Clock"},
 104};
 105
 106static const struct snd_kcontrol_new cht_mc_controls[] = {
 107        SOC_DAPM_PIN_SWITCH("Headphone"),
 108        SOC_DAPM_PIN_SWITCH("Headset Mic"),
 109        SOC_DAPM_PIN_SWITCH("Int Mic"),
 110        SOC_DAPM_PIN_SWITCH("Ext Spk"),
 111};
 112
 113static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
 114                                        struct snd_pcm_hw_params *params)
 115{
 116        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 117        struct snd_soc_dai *codec_dai = rtd->codec_dai;
 118        int ret;
 119
 120        /* set codec PLL source to the 19.2MHz platform clock (MCLK) */
 121        ret = snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_MCLK,
 122                                  CHT_PLAT_CLK_3_HZ, params_rate(params) * 512);
 123        if (ret < 0) {
 124                dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
 125                return ret;
 126        }
 127
 128        /* set codec sysclk source to PLL */
 129        ret = snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_PLL1,
 130                                     params_rate(params) * 512,
 131                                     SND_SOC_CLOCK_IN);
 132        if (ret < 0) {
 133                dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
 134                return ret;
 135        }
 136        return 0;
 137}
 138
 139static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
 140{
 141        int ret;
 142        struct snd_soc_dai *codec_dai = runtime->codec_dai;
 143
 144        /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
 145        ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24);
 146        if (ret < 0) {
 147                dev_err(runtime->dev, "can't set codec TDM slot %d\n", ret);
 148                return ret;
 149        }
 150
 151        return 0;
 152}
 153
 154static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
 155                            struct snd_pcm_hw_params *params)
 156{
 157        struct snd_interval *rate = hw_param_interval(params,
 158                        SNDRV_PCM_HW_PARAM_RATE);
 159        struct snd_interval *channels = hw_param_interval(params,
 160                                                SNDRV_PCM_HW_PARAM_CHANNELS);
 161
 162        /* The DSP will covert the FE rate to 48k, stereo, 24bits */
 163        rate->min = rate->max = 48000;
 164        channels->min = channels->max = 2;
 165
 166        /* set SSP2 to 24-bit */
 167        snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
 168                                    SNDRV_PCM_HW_PARAM_FIRST_MASK],
 169                                    SNDRV_PCM_FORMAT_S24_LE);
 170        return 0;
 171}
 172
 173static unsigned int rates_48000[] = {
 174        48000,
 175};
 176
 177static struct snd_pcm_hw_constraint_list constraints_48000 = {
 178        .count = ARRAY_SIZE(rates_48000),
 179        .list  = rates_48000,
 180};
 181
 182static int cht_aif1_startup(struct snd_pcm_substream *substream)
 183{
 184        return snd_pcm_hw_constraint_list(substream->runtime, 0,
 185                        SNDRV_PCM_HW_PARAM_RATE,
 186                        &constraints_48000);
 187}
 188
 189static struct snd_soc_ops cht_aif1_ops = {
 190        .startup = cht_aif1_startup,
 191};
 192
 193static struct snd_soc_ops cht_be_ssp2_ops = {
 194        .hw_params = cht_aif1_hw_params,
 195};
 196
 197static struct snd_soc_dai_link cht_dailink[] = {
 198        /* Front End DAI links */
 199        [MERR_DPCM_AUDIO] = {
 200                .name = "Audio Port",
 201                .stream_name = "Audio",
 202                .cpu_dai_name = "media-cpu-dai",
 203                .codec_dai_name = "snd-soc-dummy-dai",
 204                .codec_name = "snd-soc-dummy",
 205                .platform_name = "sst-mfld-platform",
 206                .ignore_suspend = 1,
 207                .dynamic = 1,
 208                .dpcm_playback = 1,
 209                .dpcm_capture = 1,
 210                .ops = &cht_aif1_ops,
 211        },
 212        [MERR_DPCM_COMPR] = {
 213                .name = "Compressed Port",
 214                .stream_name = "Compress",
 215                .cpu_dai_name = "compress-cpu-dai",
 216                .codec_dai_name = "snd-soc-dummy-dai",
 217                .codec_name = "snd-soc-dummy",
 218                .platform_name = "sst-mfld-platform",
 219        },
 220
 221        /* Back End DAI links */
 222        {
 223                /* SSP2 - Codec */
 224                .name = "SSP2-Codec",
 225                .be_id = 1,
 226                .cpu_dai_name = "ssp2-port",
 227                .platform_name = "sst-mfld-platform",
 228                .no_pcm = 1,
 229                .codec_dai_name = "rt5670-aif1",
 230                .codec_name = "i2c-10EC5670:00",
 231                .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF
 232                                        | SND_SOC_DAIFMT_CBS_CFS,
 233                .init = cht_codec_init,
 234                .be_hw_params_fixup = cht_codec_fixup,
 235                .ignore_suspend = 1,
 236                .dpcm_playback = 1,
 237                .dpcm_capture = 1,
 238                .ops = &cht_be_ssp2_ops,
 239        },
 240};
 241
 242/* SoC card */
 243static struct snd_soc_card snd_soc_card_cht = {
 244        .name = "cherrytrailcraudio",
 245        .dai_link = cht_dailink,
 246        .num_links = ARRAY_SIZE(cht_dailink),
 247        .dapm_widgets = cht_dapm_widgets,
 248        .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
 249        .dapm_routes = cht_audio_map,
 250        .num_dapm_routes = ARRAY_SIZE(cht_audio_map),
 251        .controls = cht_mc_controls,
 252        .num_controls = ARRAY_SIZE(cht_mc_controls),
 253};
 254
 255static int snd_cht_mc_probe(struct platform_device *pdev)
 256{
 257        int ret_val = 0;
 258
 259        /* register the soc card */
 260        snd_soc_card_cht.dev = &pdev->dev;
 261        ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht);
 262        if (ret_val) {
 263                dev_err(&pdev->dev,
 264                        "snd_soc_register_card failed %d\n", ret_val);
 265                return ret_val;
 266        }
 267        platform_set_drvdata(pdev, &snd_soc_card_cht);
 268        return ret_val;
 269}
 270
 271static struct platform_driver snd_cht_mc_driver = {
 272        .driver = {
 273                .owner = THIS_MODULE,
 274                .name = "cht-bsw-rt5672",
 275                .pm = &snd_soc_pm_ops,
 276        },
 277        .probe = snd_cht_mc_probe,
 278};
 279
 280module_platform_driver(snd_cht_mc_driver);
 281
 282MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver");
 283MODULE_AUTHOR("Subhransu S. Prusty, Mengdong Lin");
 284MODULE_LICENSE("GPL v2");
 285MODULE_ALIAS("platform:cht-bsw-rt5672");
 286