linux/sound/soc/intel/boards/cht_bsw_rt5645.c
<<
>>
Prefs
   1/*
   2 *  cht-bsw-rt5645.c - ASoc Machine driver for Intel Cherryview-based platforms
   3 *                     Cherrytrail and Braswell, with RT5645 codec.
   4 *
   5 *  Copyright (C) 2015 Intel Corp
   6 *  Author: Fang, Yang A <yang.a.fang@intel.com>
   7 *              N,Harshapriya <harshapriya.n@intel.com>
   8 *  This file is modified from cht_bsw_rt5672.c
   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
  23#include <linux/module.h>
  24#include <linux/acpi.h>
  25#include <linux/platform_device.h>
  26#include <linux/slab.h>
  27#include <sound/pcm.h>
  28#include <sound/pcm_params.h>
  29#include <sound/soc.h>
  30#include <sound/jack.h>
  31#include "../../codecs/rt5645.h"
  32#include "../atom/sst-atom-controls.h"
  33#include "../common/sst-acpi.h"
  34
  35#define CHT_PLAT_CLK_3_HZ       19200000
  36#define CHT_CODEC_DAI   "rt5645-aif1"
  37
  38struct cht_acpi_card {
  39        char *codec_id;
  40        int codec_type;
  41        struct snd_soc_card *soc_card;
  42};
  43
  44struct cht_mc_private {
  45        struct snd_soc_jack jack;
  46        struct cht_acpi_card *acpi_card;
  47};
  48
  49static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
  50{
  51        struct snd_soc_pcm_runtime *rtd;
  52
  53        list_for_each_entry(rtd, &card->rtd_list, list) {
  54                if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI,
  55                             strlen(CHT_CODEC_DAI)))
  56                        return rtd->codec_dai;
  57        }
  58        return NULL;
  59}
  60
  61static int platform_clock_control(struct snd_soc_dapm_widget *w,
  62                struct snd_kcontrol *k, int  event)
  63{
  64        struct snd_soc_dapm_context *dapm = w->dapm;
  65        struct snd_soc_card *card = dapm->card;
  66        struct snd_soc_dai *codec_dai;
  67        int ret;
  68
  69        codec_dai = cht_get_codec_dai(card);
  70        if (!codec_dai) {
  71                dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n");
  72                return -EIO;
  73        }
  74
  75        if (!SND_SOC_DAPM_EVENT_OFF(event))
  76                return 0;
  77
  78        /* Set codec sysclk source to its internal clock because codec PLL will
  79         * be off when idle and MCLK will also be off by ACPI when codec is
  80         * runtime suspended. Codec needs clock for jack detection and button
  81         * press.
  82         */
  83        ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_RCCLK,
  84                        0, SND_SOC_CLOCK_IN);
  85        if (ret < 0) {
  86                dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
  87                return ret;
  88        }
  89
  90        return 0;
  91}
  92
  93static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
  94        SND_SOC_DAPM_HP("Headphone", NULL),
  95        SND_SOC_DAPM_MIC("Headset Mic", NULL),
  96        SND_SOC_DAPM_MIC("Int Mic", NULL),
  97        SND_SOC_DAPM_SPK("Ext Spk", NULL),
  98        SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
  99                        platform_clock_control, SND_SOC_DAPM_POST_PMD),
 100};
 101
 102static const struct snd_soc_dapm_route cht_rt5645_audio_map[] = {
 103        {"IN1P", NULL, "Headset Mic"},
 104        {"IN1N", NULL, "Headset Mic"},
 105        {"DMIC L1", NULL, "Int Mic"},
 106        {"DMIC R1", NULL, "Int Mic"},
 107        {"Headphone", NULL, "HPOL"},
 108        {"Headphone", NULL, "HPOR"},
 109        {"Ext Spk", NULL, "SPOL"},
 110        {"Ext Spk", NULL, "SPOR"},
 111        {"AIF1 Playback", NULL, "ssp2 Tx"},
 112        {"ssp2 Tx", NULL, "codec_out0"},
 113        {"ssp2 Tx", NULL, "codec_out1"},
 114        {"codec_in0", NULL, "ssp2 Rx" },
 115        {"codec_in1", NULL, "ssp2 Rx" },
 116        {"ssp2 Rx", NULL, "AIF1 Capture"},
 117        {"Headphone", NULL, "Platform Clock"},
 118        {"Headset Mic", NULL, "Platform Clock"},
 119        {"Int Mic", NULL, "Platform Clock"},
 120        {"Ext Spk", NULL, "Platform Clock"},
 121};
 122
 123static const struct snd_soc_dapm_route cht_rt5650_audio_map[] = {
 124        {"IN1P", NULL, "Headset Mic"},
 125        {"IN1N", NULL, "Headset Mic"},
 126        {"DMIC L2", NULL, "Int Mic"},
 127        {"DMIC R2", NULL, "Int Mic"},
 128        {"Headphone", NULL, "HPOL"},
 129        {"Headphone", NULL, "HPOR"},
 130        {"Ext Spk", NULL, "SPOL"},
 131        {"Ext Spk", NULL, "SPOR"},
 132        {"AIF1 Playback", NULL, "ssp2 Tx"},
 133        {"ssp2 Tx", NULL, "codec_out0"},
 134        {"ssp2 Tx", NULL, "codec_out1"},
 135        {"codec_in0", NULL, "ssp2 Rx" },
 136        {"codec_in1", NULL, "ssp2 Rx" },
 137        {"ssp2 Rx", NULL, "AIF1 Capture"},
 138        {"Headphone", NULL, "Platform Clock"},
 139        {"Headset Mic", NULL, "Platform Clock"},
 140        {"Int Mic", NULL, "Platform Clock"},
 141        {"Ext Spk", NULL, "Platform Clock"},
 142};
 143
 144static const struct snd_kcontrol_new cht_mc_controls[] = {
 145        SOC_DAPM_PIN_SWITCH("Headphone"),
 146        SOC_DAPM_PIN_SWITCH("Headset Mic"),
 147        SOC_DAPM_PIN_SWITCH("Int Mic"),
 148        SOC_DAPM_PIN_SWITCH("Ext Spk"),
 149};
 150
 151static struct snd_soc_jack_pin cht_bsw_jack_pins[] = {
 152        {
 153                .pin    = "Headphone",
 154                .mask   = SND_JACK_HEADPHONE,
 155        },
 156        {
 157                .pin    = "Headset Mic",
 158                .mask   = SND_JACK_MICROPHONE,
 159        },
 160};
 161
 162static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
 163                             struct snd_pcm_hw_params *params)
 164{
 165        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 166        struct snd_soc_dai *codec_dai = rtd->codec_dai;
 167        int ret;
 168
 169        /* set codec PLL source to the 19.2MHz platform clock (MCLK) */
 170        ret = snd_soc_dai_set_pll(codec_dai, 0, RT5645_PLL1_S_MCLK,
 171                                  CHT_PLAT_CLK_3_HZ, params_rate(params) * 512);
 172        if (ret < 0) {
 173                dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
 174                return ret;
 175        }
 176
 177        ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_PLL1,
 178                                params_rate(params) * 512, SND_SOC_CLOCK_IN);
 179        if (ret < 0) {
 180                dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
 181                return ret;
 182        }
 183
 184        return 0;
 185}
 186
 187static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
 188{
 189        int ret;
 190        int jack_type;
 191        struct snd_soc_codec *codec = runtime->codec;
 192        struct snd_soc_dai *codec_dai = runtime->codec_dai;
 193        struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card);
 194
 195        /* Select clk_i2s1_asrc as ASRC clock source */
 196        rt5645_sel_asrc_clk_src(codec,
 197                                RT5645_DA_STEREO_FILTER |
 198                                RT5645_DA_MONO_L_FILTER |
 199                                RT5645_DA_MONO_R_FILTER |
 200                                RT5645_AD_STEREO_FILTER,
 201                                RT5645_CLK_SEL_I2S1_ASRC);
 202
 203        /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
 204        ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24);
 205        if (ret < 0) {
 206                dev_err(runtime->dev, "can't set codec TDM slot %d\n", ret);
 207                return ret;
 208        }
 209
 210        if (ctx->acpi_card->codec_type == CODEC_TYPE_RT5650)
 211                jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
 212                                        SND_JACK_BTN_0 | SND_JACK_BTN_1 |
 213                                        SND_JACK_BTN_2 | SND_JACK_BTN_3;
 214        else
 215                jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE;
 216
 217        ret = snd_soc_card_jack_new(runtime->card, "Headset",
 218                                    jack_type, &ctx->jack,
 219                                    cht_bsw_jack_pins, ARRAY_SIZE(cht_bsw_jack_pins));
 220        if (ret) {
 221                dev_err(runtime->dev, "Headset jack creation failed %d\n", ret);
 222                return ret;
 223        }
 224
 225        rt5645_set_jack_detect(codec, &ctx->jack, &ctx->jack, &ctx->jack);
 226
 227        return ret;
 228}
 229
 230static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
 231                            struct snd_pcm_hw_params *params)
 232{
 233        struct snd_interval *rate = hw_param_interval(params,
 234                        SNDRV_PCM_HW_PARAM_RATE);
 235        struct snd_interval *channels = hw_param_interval(params,
 236                                                SNDRV_PCM_HW_PARAM_CHANNELS);
 237
 238        /* The DSP will covert the FE rate to 48k, stereo, 24bits */
 239        rate->min = rate->max = 48000;
 240        channels->min = channels->max = 2;
 241
 242        /* set SSP2 to 24-bit */
 243        params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
 244        return 0;
 245}
 246
 247static int cht_aif1_startup(struct snd_pcm_substream *substream)
 248{
 249        return snd_pcm_hw_constraint_single(substream->runtime,
 250                        SNDRV_PCM_HW_PARAM_RATE, 48000);
 251}
 252
 253static struct snd_soc_ops cht_aif1_ops = {
 254        .startup = cht_aif1_startup,
 255};
 256
 257static struct snd_soc_ops cht_be_ssp2_ops = {
 258        .hw_params = cht_aif1_hw_params,
 259};
 260
 261static struct snd_soc_dai_link cht_dailink[] = {
 262        [MERR_DPCM_AUDIO] = {
 263                .name = "Audio Port",
 264                .stream_name = "Audio",
 265                .cpu_dai_name = "media-cpu-dai",
 266                .codec_dai_name = "snd-soc-dummy-dai",
 267                .codec_name = "snd-soc-dummy",
 268                .platform_name = "sst-mfld-platform",
 269                .nonatomic = true,
 270                .dynamic = 1,
 271                .dpcm_playback = 1,
 272                .dpcm_capture = 1,
 273                .ops = &cht_aif1_ops,
 274        },
 275        [MERR_DPCM_DEEP_BUFFER] = {
 276                .name = "Deep-Buffer Audio Port",
 277                .stream_name = "Deep-Buffer Audio",
 278                .cpu_dai_name = "deepbuffer-cpu-dai",
 279                .codec_dai_name = "snd-soc-dummy-dai",
 280                .codec_name = "snd-soc-dummy",
 281                .platform_name = "sst-mfld-platform",
 282                .nonatomic = true,
 283                .dynamic = 1,
 284                .dpcm_playback = 1,
 285                .ops = &cht_aif1_ops,
 286        },
 287        [MERR_DPCM_COMPR] = {
 288                .name = "Compressed Port",
 289                .stream_name = "Compress",
 290                .cpu_dai_name = "compress-cpu-dai",
 291                .codec_dai_name = "snd-soc-dummy-dai",
 292                .codec_name = "snd-soc-dummy",
 293                .platform_name = "sst-mfld-platform",
 294        },
 295        /* CODEC<->CODEC link */
 296        /* back ends */
 297        {
 298                .name = "SSP2-Codec",
 299                .id = 1,
 300                .cpu_dai_name = "ssp2-port",
 301                .platform_name = "sst-mfld-platform",
 302                .no_pcm = 1,
 303                .codec_dai_name = "rt5645-aif1",
 304                .codec_name = "i2c-10EC5645:00",
 305                .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF
 306                                        | SND_SOC_DAIFMT_CBS_CFS,
 307                .init = cht_codec_init,
 308                .be_hw_params_fixup = cht_codec_fixup,
 309                .nonatomic = true,
 310                .dpcm_playback = 1,
 311                .dpcm_capture = 1,
 312                .ops = &cht_be_ssp2_ops,
 313        },
 314};
 315
 316/* SoC card */
 317static struct snd_soc_card snd_soc_card_chtrt5645 = {
 318        .name = "chtrt5645",
 319        .owner = THIS_MODULE,
 320        .dai_link = cht_dailink,
 321        .num_links = ARRAY_SIZE(cht_dailink),
 322        .dapm_widgets = cht_dapm_widgets,
 323        .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
 324        .dapm_routes = cht_rt5645_audio_map,
 325        .num_dapm_routes = ARRAY_SIZE(cht_rt5645_audio_map),
 326        .controls = cht_mc_controls,
 327        .num_controls = ARRAY_SIZE(cht_mc_controls),
 328};
 329
 330static struct snd_soc_card snd_soc_card_chtrt5650 = {
 331        .name = "chtrt5650",
 332        .owner = THIS_MODULE,
 333        .dai_link = cht_dailink,
 334        .num_links = ARRAY_SIZE(cht_dailink),
 335        .dapm_widgets = cht_dapm_widgets,
 336        .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
 337        .dapm_routes = cht_rt5650_audio_map,
 338        .num_dapm_routes = ARRAY_SIZE(cht_rt5650_audio_map),
 339        .controls = cht_mc_controls,
 340        .num_controls = ARRAY_SIZE(cht_mc_controls),
 341};
 342
 343static struct cht_acpi_card snd_soc_cards[] = {
 344        {"10EC5640", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},
 345        {"10EC5645", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},
 346        {"10EC5650", CODEC_TYPE_RT5650, &snd_soc_card_chtrt5650},
 347};
 348
 349static char cht_rt5640_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
 350
 351static int snd_cht_mc_probe(struct platform_device *pdev)
 352{
 353        int ret_val = 0;
 354        int i;
 355        struct cht_mc_private *drv;
 356        struct snd_soc_card *card = snd_soc_cards[0].soc_card;
 357        char codec_name[16];
 358        struct sst_acpi_mach *mach;
 359        const char *i2c_name = NULL;
 360        int dai_index = 0;
 361
 362        drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
 363        if (!drv)
 364                return -ENOMEM;
 365
 366        for (i = 0; i < ARRAY_SIZE(snd_soc_cards); i++) {
 367                if (acpi_dev_found(snd_soc_cards[i].codec_id)) {
 368                        dev_dbg(&pdev->dev,
 369                                "found codec %s\n", snd_soc_cards[i].codec_id);
 370                        card = snd_soc_cards[i].soc_card;
 371                        drv->acpi_card = &snd_soc_cards[i];
 372                        break;
 373                }
 374        }
 375        card->dev = &pdev->dev;
 376        mach = card->dev->platform_data;
 377        sprintf(codec_name, "i2c-%s:00", drv->acpi_card->codec_id);
 378
 379        /* set correct codec name */
 380        for (i = 0; i < ARRAY_SIZE(cht_dailink); i++)
 381                if (!strcmp(card->dai_link[i].codec_name, "i2c-10EC5645:00")) {
 382                        card->dai_link[i].codec_name = kstrdup(codec_name, GFP_KERNEL);
 383                        dai_index = i;
 384                }
 385
 386        /* fixup codec name based on HID */
 387        i2c_name = sst_acpi_find_name_from_hid(mach->id);
 388        if (i2c_name != NULL) {
 389                snprintf(cht_rt5640_codec_name, sizeof(cht_rt5640_codec_name),
 390                        "%s%s", "i2c-", i2c_name);
 391                cht_dailink[dai_index].codec_name = cht_rt5640_codec_name;
 392        }
 393
 394        snd_soc_card_set_drvdata(card, drv);
 395        ret_val = devm_snd_soc_register_card(&pdev->dev, card);
 396        if (ret_val) {
 397                dev_err(&pdev->dev,
 398                        "snd_soc_register_card failed %d\n", ret_val);
 399                return ret_val;
 400        }
 401        platform_set_drvdata(pdev, card);
 402        return ret_val;
 403}
 404
 405static struct platform_driver snd_cht_mc_driver = {
 406        .driver = {
 407                .name = "cht-bsw-rt5645",
 408        },
 409        .probe = snd_cht_mc_probe,
 410};
 411
 412module_platform_driver(snd_cht_mc_driver)
 413
 414MODULE_DESCRIPTION("ASoC Intel(R) Braswell Machine driver");
 415MODULE_AUTHOR("Fang, Yang A,N,Harshapriya");
 416MODULE_LICENSE("GPL v2");
 417MODULE_ALIAS("platform:cht-bsw-rt5645");
 418