linux/sound/soc/intel/boards/skl_rt286.c
<<
>>
Prefs
   1/*
   2 * Intel Skylake I2S Machine Driver
   3 *
   4 * Copyright (C) 2014-2015, Intel Corporation. All rights reserved.
   5 *
   6 * Modified from:
   7 *   Intel Broadwell Wildcatpoint SST Audio
   8 *
   9 *   Copyright (C) 2013, Intel Corporation. All rights reserved.
  10 *
  11 * This program is free software; you can redistribute it and/or
  12 * modify it under the terms of the GNU General Public License version
  13 * 2 as published by the Free Software Foundation.
  14 *
  15 * This program is distributed in the hope that it will be useful,
  16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18 * GNU General Public License for more details.
  19 */
  20
  21#include <linux/module.h>
  22#include <linux/platform_device.h>
  23#include <sound/core.h>
  24#include <sound/pcm.h>
  25#include <sound/soc.h>
  26#include <sound/jack.h>
  27#include <sound/pcm_params.h>
  28#include "../../codecs/rt286.h"
  29#include "../../codecs/hdac_hdmi.h"
  30
  31static struct snd_soc_jack skylake_headset;
  32static struct snd_soc_jack skylake_hdmi[3];
  33
  34struct skl_hdmi_pcm {
  35        struct list_head head;
  36        struct snd_soc_dai *codec_dai;
  37        int device;
  38};
  39
  40struct skl_rt286_private {
  41        struct list_head hdmi_pcm_list;
  42};
  43
  44enum {
  45        SKL_DPCM_AUDIO_PB = 0,
  46        SKL_DPCM_AUDIO_DB_PB,
  47        SKL_DPCM_AUDIO_CP,
  48        SKL_DPCM_AUDIO_REF_CP,
  49        SKL_DPCM_AUDIO_DMIC_CP,
  50        SKL_DPCM_AUDIO_HDMI1_PB,
  51        SKL_DPCM_AUDIO_HDMI2_PB,
  52        SKL_DPCM_AUDIO_HDMI3_PB,
  53};
  54
  55/* Headset jack detection DAPM pins */
  56static struct snd_soc_jack_pin skylake_headset_pins[] = {
  57        {
  58                .pin = "Mic Jack",
  59                .mask = SND_JACK_MICROPHONE,
  60        },
  61        {
  62                .pin = "Headphone Jack",
  63                .mask = SND_JACK_HEADPHONE,
  64        },
  65};
  66
  67static const struct snd_kcontrol_new skylake_controls[] = {
  68        SOC_DAPM_PIN_SWITCH("Speaker"),
  69        SOC_DAPM_PIN_SWITCH("Headphone Jack"),
  70        SOC_DAPM_PIN_SWITCH("Mic Jack"),
  71};
  72
  73static const struct snd_soc_dapm_widget skylake_widgets[] = {
  74        SND_SOC_DAPM_HP("Headphone Jack", NULL),
  75        SND_SOC_DAPM_SPK("Speaker", NULL),
  76        SND_SOC_DAPM_MIC("Mic Jack", NULL),
  77        SND_SOC_DAPM_MIC("DMIC2", NULL),
  78        SND_SOC_DAPM_MIC("SoC DMIC", NULL),
  79        SND_SOC_DAPM_SPK("HDMI1", NULL),
  80        SND_SOC_DAPM_SPK("HDMI2", NULL),
  81        SND_SOC_DAPM_SPK("HDMI3", NULL),
  82};
  83
  84static const struct snd_soc_dapm_route skylake_rt286_map[] = {
  85        /* speaker */
  86        {"Speaker", NULL, "SPOR"},
  87        {"Speaker", NULL, "SPOL"},
  88
  89        /* HP jack connectors - unknown if we have jack deteck */
  90        {"Headphone Jack", NULL, "HPO Pin"},
  91
  92        /* other jacks */
  93        {"MIC1", NULL, "Mic Jack"},
  94
  95        /* digital mics */
  96        {"DMIC1 Pin", NULL, "DMIC2"},
  97        {"DMic", NULL, "SoC DMIC"},
  98
  99        /* CODEC BE connections */
 100        { "AIF1 Playback", NULL, "ssp0 Tx"},
 101        { "ssp0 Tx", NULL, "codec0_out"},
 102        { "ssp0 Tx", NULL, "codec1_out"},
 103
 104        { "codec0_in", NULL, "ssp0 Rx" },
 105        { "codec1_in", NULL, "ssp0 Rx" },
 106        { "ssp0 Rx", NULL, "AIF1 Capture" },
 107
 108        { "dmic01_hifi", NULL, "DMIC01 Rx" },
 109        { "DMIC01 Rx", NULL, "DMIC AIF" },
 110
 111        { "hifi3", NULL, "iDisp3 Tx"},
 112        { "iDisp3 Tx", NULL, "iDisp3_out"},
 113        { "hifi2", NULL, "iDisp2 Tx"},
 114        { "iDisp2 Tx", NULL, "iDisp2_out"},
 115        { "hifi1", NULL, "iDisp1 Tx"},
 116        { "iDisp1 Tx", NULL, "iDisp1_out"},
 117
 118};
 119
 120static int skylake_rt286_fe_init(struct snd_soc_pcm_runtime *rtd)
 121{
 122        struct snd_soc_dapm_context *dapm;
 123        struct snd_soc_component *component = rtd->cpu_dai->component;
 124
 125        dapm = snd_soc_component_get_dapm(component);
 126        snd_soc_dapm_ignore_suspend(dapm, "Reference Capture");
 127
 128        return 0;
 129}
 130
 131static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
 132{
 133        struct snd_soc_component *component = rtd->codec_dai->component;
 134        int ret;
 135
 136        ret = snd_soc_card_jack_new(rtd->card, "Headset",
 137                SND_JACK_HEADSET | SND_JACK_BTN_0,
 138                &skylake_headset,
 139                skylake_headset_pins, ARRAY_SIZE(skylake_headset_pins));
 140
 141        if (ret)
 142                return ret;
 143
 144        rt286_mic_detect(component, &skylake_headset);
 145
 146        snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
 147
 148        return 0;
 149}
 150
 151static int skylake_hdmi_init(struct snd_soc_pcm_runtime *rtd)
 152{
 153        struct skl_rt286_private *ctx = snd_soc_card_get_drvdata(rtd->card);
 154        struct snd_soc_dai *dai = rtd->codec_dai;
 155        struct skl_hdmi_pcm *pcm;
 156
 157        pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
 158        if (!pcm)
 159                return -ENOMEM;
 160
 161        pcm->device = SKL_DPCM_AUDIO_HDMI1_PB + dai->id;
 162        pcm->codec_dai = dai;
 163
 164        list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
 165
 166        return 0;
 167}
 168
 169static const unsigned int rates[] = {
 170        48000,
 171};
 172
 173static const struct snd_pcm_hw_constraint_list constraints_rates = {
 174        .count = ARRAY_SIZE(rates),
 175        .list  = rates,
 176        .mask = 0,
 177};
 178
 179static const unsigned int channels[] = {
 180        2,
 181};
 182
 183static const struct snd_pcm_hw_constraint_list constraints_channels = {
 184        .count = ARRAY_SIZE(channels),
 185        .list = channels,
 186        .mask = 0,
 187};
 188
 189static int skl_fe_startup(struct snd_pcm_substream *substream)
 190{
 191        struct snd_pcm_runtime *runtime = substream->runtime;
 192
 193        /*
 194         * on this platform for PCM device we support,
 195         *      48Khz
 196         *      stereo
 197         *      16 bit audio
 198         */
 199
 200        runtime->hw.channels_max = 2;
 201        snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
 202                                           &constraints_channels);
 203
 204        runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
 205        snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16);
 206
 207        snd_pcm_hw_constraint_list(runtime, 0,
 208                                SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
 209
 210        return 0;
 211}
 212
 213static const struct snd_soc_ops skylake_rt286_fe_ops = {
 214        .startup = skl_fe_startup,
 215};
 216
 217static int skylake_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
 218                        struct snd_pcm_hw_params *params)
 219{
 220        struct snd_interval *rate = hw_param_interval(params,
 221                        SNDRV_PCM_HW_PARAM_RATE);
 222        struct snd_interval *channels = hw_param_interval(params,
 223                                                SNDRV_PCM_HW_PARAM_CHANNELS);
 224        struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
 225
 226        /* The output is 48KHz, stereo, 16bits */
 227        rate->min = rate->max = 48000;
 228        channels->min = channels->max = 2;
 229
 230        /* set SSP0 to 24 bit */
 231        snd_mask_none(fmt);
 232        snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE);
 233        return 0;
 234}
 235
 236static int skylake_rt286_hw_params(struct snd_pcm_substream *substream,
 237        struct snd_pcm_hw_params *params)
 238{
 239        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 240        struct snd_soc_dai *codec_dai = rtd->codec_dai;
 241        int ret;
 242
 243        ret = snd_soc_dai_set_sysclk(codec_dai, RT286_SCLK_S_PLL, 24000000,
 244                SND_SOC_CLOCK_IN);
 245        if (ret < 0)
 246                dev_err(rtd->dev, "set codec sysclk failed: %d\n", ret);
 247
 248        return ret;
 249}
 250
 251static const struct snd_soc_ops skylake_rt286_ops = {
 252        .hw_params = skylake_rt286_hw_params,
 253};
 254
 255static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd,
 256                                struct snd_pcm_hw_params *params)
 257{
 258        struct snd_interval *channels = hw_param_interval(params,
 259                                                SNDRV_PCM_HW_PARAM_CHANNELS);
 260        if (params_channels(params) == 2)
 261                channels->min = channels->max = 2;
 262        else
 263                channels->min = channels->max = 4;
 264
 265        return 0;
 266}
 267
 268static const unsigned int channels_dmic[] = {
 269        2, 4,
 270};
 271
 272static const struct snd_pcm_hw_constraint_list constraints_dmic_channels = {
 273        .count = ARRAY_SIZE(channels_dmic),
 274        .list = channels_dmic,
 275        .mask = 0,
 276};
 277
 278static int skylake_dmic_startup(struct snd_pcm_substream *substream)
 279{
 280        struct snd_pcm_runtime *runtime = substream->runtime;
 281
 282        runtime->hw.channels_max = 4;
 283        snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
 284                                           &constraints_dmic_channels);
 285
 286        return snd_pcm_hw_constraint_list(substream->runtime, 0,
 287                        SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
 288}
 289
 290static const struct snd_soc_ops skylake_dmic_ops = {
 291        .startup = skylake_dmic_startup,
 292};
 293
 294/* skylake digital audio interface glue - connects codec <--> CPU */
 295static struct snd_soc_dai_link skylake_rt286_dais[] = {
 296        /* Front End DAI links */
 297        [SKL_DPCM_AUDIO_PB] = {
 298                .name = "Skl Audio Port",
 299                .stream_name = "Audio",
 300                .cpu_dai_name = "System Pin",
 301                .platform_name = "0000:00:1f.3",
 302                .nonatomic = 1,
 303                .dynamic = 1,
 304                .codec_name = "snd-soc-dummy",
 305                .codec_dai_name = "snd-soc-dummy-dai",
 306                .init = skylake_rt286_fe_init,
 307                .trigger = {
 308                        SND_SOC_DPCM_TRIGGER_POST,
 309                        SND_SOC_DPCM_TRIGGER_POST
 310                },
 311                .dpcm_playback = 1,
 312                .ops = &skylake_rt286_fe_ops,
 313        },
 314        [SKL_DPCM_AUDIO_DB_PB] = {
 315                .name = "Skl Deepbuffer Port",
 316                .stream_name = "Deep Buffer Audio",
 317                .cpu_dai_name = "Deepbuffer Pin",
 318                .platform_name = "0000:00:1f.3",
 319                .nonatomic = 1,
 320                .dynamic = 1,
 321                .codec_name = "snd-soc-dummy",
 322                .codec_dai_name = "snd-soc-dummy-dai",
 323                .trigger = {
 324                        SND_SOC_DPCM_TRIGGER_POST,
 325                        SND_SOC_DPCM_TRIGGER_POST
 326                },
 327                .dpcm_playback = 1,
 328                .ops = &skylake_rt286_fe_ops,
 329
 330        },
 331        [SKL_DPCM_AUDIO_CP] = {
 332                .name = "Skl Audio Capture Port",
 333                .stream_name = "Audio Record",
 334                .cpu_dai_name = "System Pin",
 335                .platform_name = "0000:00:1f.3",
 336                .nonatomic = 1,
 337                .dynamic = 1,
 338                .codec_name = "snd-soc-dummy",
 339                .codec_dai_name = "snd-soc-dummy-dai",
 340                .trigger = {
 341                        SND_SOC_DPCM_TRIGGER_POST,
 342                        SND_SOC_DPCM_TRIGGER_POST
 343                },
 344                .dpcm_capture = 1,
 345                .ops = &skylake_rt286_fe_ops,
 346        },
 347        [SKL_DPCM_AUDIO_REF_CP] = {
 348                .name = "Skl Audio Reference cap",
 349                .stream_name = "refcap",
 350                .cpu_dai_name = "Reference Pin",
 351                .codec_name = "snd-soc-dummy",
 352                .codec_dai_name = "snd-soc-dummy-dai",
 353                .platform_name = "0000:00:1f.3",
 354                .init = NULL,
 355                .dpcm_capture = 1,
 356                .nonatomic = 1,
 357                .dynamic = 1,
 358        },
 359        [SKL_DPCM_AUDIO_DMIC_CP] = {
 360                .name = "Skl Audio DMIC cap",
 361                .stream_name = "dmiccap",
 362                .cpu_dai_name = "DMIC Pin",
 363                .codec_name = "snd-soc-dummy",
 364                .codec_dai_name = "snd-soc-dummy-dai",
 365                .platform_name = "0000:00:1f.3",
 366                .init = NULL,
 367                .dpcm_capture = 1,
 368                .nonatomic = 1,
 369                .dynamic = 1,
 370                .ops = &skylake_dmic_ops,
 371        },
 372        [SKL_DPCM_AUDIO_HDMI1_PB] = {
 373                .name = "Skl HDMI Port1",
 374                .stream_name = "Hdmi1",
 375                .cpu_dai_name = "HDMI1 Pin",
 376                .codec_name = "snd-soc-dummy",
 377                .codec_dai_name = "snd-soc-dummy-dai",
 378                .platform_name = "0000:00:1f.3",
 379                .dpcm_playback = 1,
 380                .init = NULL,
 381                .nonatomic = 1,
 382                .dynamic = 1,
 383        },
 384        [SKL_DPCM_AUDIO_HDMI2_PB] = {
 385                .name = "Skl HDMI Port2",
 386                .stream_name = "Hdmi2",
 387                .cpu_dai_name = "HDMI2 Pin",
 388                .codec_name = "snd-soc-dummy",
 389                .codec_dai_name = "snd-soc-dummy-dai",
 390                .platform_name = "0000:00:1f.3",
 391                .dpcm_playback = 1,
 392                .init = NULL,
 393                .nonatomic = 1,
 394                .dynamic = 1,
 395        },
 396        [SKL_DPCM_AUDIO_HDMI3_PB] = {
 397                .name = "Skl HDMI Port3",
 398                .stream_name = "Hdmi3",
 399                .cpu_dai_name = "HDMI3 Pin",
 400                .codec_name = "snd-soc-dummy",
 401                .codec_dai_name = "snd-soc-dummy-dai",
 402                .platform_name = "0000:00:1f.3",
 403                .dpcm_playback = 1,
 404                .init = NULL,
 405                .nonatomic = 1,
 406                .dynamic = 1,
 407        },
 408
 409        /* Back End DAI links */
 410        {
 411                /* SSP0 - Codec */
 412                .name = "SSP0-Codec",
 413                .id = 0,
 414                .cpu_dai_name = "SSP0 Pin",
 415                .platform_name = "0000:00:1f.3",
 416                .no_pcm = 1,
 417                .codec_name = "i2c-INT343A:00",
 418                .codec_dai_name = "rt286-aif1",
 419                .init = skylake_rt286_codec_init,
 420                .dai_fmt = SND_SOC_DAIFMT_I2S |
 421                        SND_SOC_DAIFMT_NB_NF |
 422                        SND_SOC_DAIFMT_CBS_CFS,
 423                .ignore_pmdown_time = 1,
 424                .be_hw_params_fixup = skylake_ssp0_fixup,
 425                .ops = &skylake_rt286_ops,
 426                .dpcm_playback = 1,
 427                .dpcm_capture = 1,
 428        },
 429        {
 430                .name = "dmic01",
 431                .id = 1,
 432                .cpu_dai_name = "DMIC01 Pin",
 433                .codec_name = "dmic-codec",
 434                .codec_dai_name = "dmic-hifi",
 435                .platform_name = "0000:00:1f.3",
 436                .be_hw_params_fixup = skylake_dmic_fixup,
 437                .ignore_suspend = 1,
 438                .dpcm_capture = 1,
 439                .no_pcm = 1,
 440        },
 441        {
 442                .name = "iDisp1",
 443                .id = 2,
 444                .cpu_dai_name = "iDisp1 Pin",
 445                .codec_name = "ehdaudio0D2",
 446                .codec_dai_name = "intel-hdmi-hifi1",
 447                .platform_name = "0000:00:1f.3",
 448                .init = skylake_hdmi_init,
 449                .dpcm_playback = 1,
 450                .no_pcm = 1,
 451        },
 452        {
 453                .name = "iDisp2",
 454                .id = 3,
 455                .cpu_dai_name = "iDisp2 Pin",
 456                .codec_name = "ehdaudio0D2",
 457                .codec_dai_name = "intel-hdmi-hifi2",
 458                .platform_name = "0000:00:1f.3",
 459                .init = skylake_hdmi_init,
 460                .dpcm_playback = 1,
 461                .no_pcm = 1,
 462        },
 463        {
 464                .name = "iDisp3",
 465                .id = 4,
 466                .cpu_dai_name = "iDisp3 Pin",
 467                .codec_name = "ehdaudio0D2",
 468                .codec_dai_name = "intel-hdmi-hifi3",
 469                .platform_name = "0000:00:1f.3",
 470                .init = skylake_hdmi_init,
 471                .dpcm_playback = 1,
 472                .no_pcm = 1,
 473        },
 474};
 475
 476#define NAME_SIZE       32
 477static int skylake_card_late_probe(struct snd_soc_card *card)
 478{
 479        struct skl_rt286_private *ctx = snd_soc_card_get_drvdata(card);
 480        struct skl_hdmi_pcm *pcm;
 481        struct snd_soc_component *component = NULL;
 482        int err, i = 0;
 483        char jack_name[NAME_SIZE];
 484
 485        list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
 486                component = pcm->codec_dai->component;
 487                snprintf(jack_name, sizeof(jack_name),
 488                        "HDMI/DP, pcm=%d Jack", pcm->device);
 489                err = snd_soc_card_jack_new(card, jack_name,
 490                                        SND_JACK_AVOUT, &skylake_hdmi[i],
 491                                        NULL, 0);
 492
 493                if (err)
 494                        return err;
 495
 496                err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device,
 497                                                &skylake_hdmi[i]);
 498                if (err < 0)
 499                        return err;
 500
 501                i++;
 502        }
 503
 504        if (!component)
 505                return -EINVAL;
 506
 507        return hdac_hdmi_jack_port_init(component, &card->dapm);
 508}
 509
 510/* skylake audio machine driver for SPT + RT286S */
 511static struct snd_soc_card skylake_rt286 = {
 512        .name = "skylake-rt286",
 513        .owner = THIS_MODULE,
 514        .dai_link = skylake_rt286_dais,
 515        .num_links = ARRAY_SIZE(skylake_rt286_dais),
 516        .controls = skylake_controls,
 517        .num_controls = ARRAY_SIZE(skylake_controls),
 518        .dapm_widgets = skylake_widgets,
 519        .num_dapm_widgets = ARRAY_SIZE(skylake_widgets),
 520        .dapm_routes = skylake_rt286_map,
 521        .num_dapm_routes = ARRAY_SIZE(skylake_rt286_map),
 522        .fully_routed = true,
 523        .late_probe = skylake_card_late_probe,
 524};
 525
 526static int skylake_audio_probe(struct platform_device *pdev)
 527{
 528        struct skl_rt286_private *ctx;
 529
 530        ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
 531        if (!ctx)
 532                return -ENOMEM;
 533
 534        INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
 535
 536        skylake_rt286.dev = &pdev->dev;
 537        snd_soc_card_set_drvdata(&skylake_rt286, ctx);
 538
 539        return devm_snd_soc_register_card(&pdev->dev, &skylake_rt286);
 540}
 541
 542static const struct platform_device_id skl_board_ids[] = {
 543        { .name = "skl_alc286s_i2s" },
 544        { .name = "kbl_alc286s_i2s" },
 545        { }
 546};
 547
 548static struct platform_driver skylake_audio = {
 549        .probe = skylake_audio_probe,
 550        .driver = {
 551                .name = "skl_alc286s_i2s",
 552                .pm = &snd_soc_pm_ops,
 553        },
 554        .id_table = skl_board_ids,
 555
 556};
 557
 558module_platform_driver(skylake_audio)
 559
 560/* Module information */
 561MODULE_AUTHOR("Omair Mohammed Abdullah <omair.m.abdullah@intel.com>");
 562MODULE_DESCRIPTION("Intel SST Audio for Skylake");
 563MODULE_LICENSE("GPL v2");
 564MODULE_ALIAS("platform:skl_alc286s_i2s");
 565MODULE_ALIAS("platform:kbl_alc286s_i2s");
 566