linux/sound/soc/intel/boards/broadwell.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Intel Broadwell Wildcatpoint SST Audio
   4 *
   5 * Copyright (C) 2013, Intel Corporation. All rights reserved.
   6 */
   7
   8#include <linux/module.h>
   9#include <linux/platform_device.h>
  10#include <sound/core.h>
  11#include <sound/pcm.h>
  12#include <sound/soc.h>
  13#include <sound/jack.h>
  14#include <sound/pcm_params.h>
  15#include <sound/soc-acpi.h>
  16
  17#include "../../codecs/rt286.h"
  18
  19static struct snd_soc_jack broadwell_headset;
  20/* Headset jack detection DAPM pins */
  21static struct snd_soc_jack_pin broadwell_headset_pins[] = {
  22        {
  23                .pin = "Mic Jack",
  24                .mask = SND_JACK_MICROPHONE,
  25        },
  26        {
  27                .pin = "Headphone Jack",
  28                .mask = SND_JACK_HEADPHONE,
  29        },
  30};
  31
  32static const struct snd_kcontrol_new broadwell_controls[] = {
  33        SOC_DAPM_PIN_SWITCH("Speaker"),
  34        SOC_DAPM_PIN_SWITCH("Headphone Jack"),
  35};
  36
  37static const struct snd_soc_dapm_widget broadwell_widgets[] = {
  38        SND_SOC_DAPM_HP("Headphone Jack", NULL),
  39        SND_SOC_DAPM_SPK("Speaker", NULL),
  40        SND_SOC_DAPM_MIC("Mic Jack", NULL),
  41        SND_SOC_DAPM_MIC("DMIC1", NULL),
  42        SND_SOC_DAPM_MIC("DMIC2", NULL),
  43        SND_SOC_DAPM_LINE("Line Jack", NULL),
  44};
  45
  46static const struct snd_soc_dapm_route broadwell_rt286_map[] = {
  47
  48        /* speaker */
  49        {"Speaker", NULL, "SPOR"},
  50        {"Speaker", NULL, "SPOL"},
  51
  52        /* HP jack connectors - unknown if we have jack deteck */
  53        {"Headphone Jack", NULL, "HPO Pin"},
  54
  55        /* other jacks */
  56        {"MIC1", NULL, "Mic Jack"},
  57        {"LINE1", NULL, "Line Jack"},
  58
  59        /* digital mics */
  60        {"DMIC1 Pin", NULL, "DMIC1"},
  61        {"DMIC2 Pin", NULL, "DMIC2"},
  62
  63        /* CODEC BE connections */
  64        {"SSP0 CODEC IN", NULL, "AIF1 Capture"},
  65        {"AIF1 Playback", NULL, "SSP0 CODEC OUT"},
  66};
  67
  68static int broadwell_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
  69{
  70        struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
  71        int ret = 0;
  72        ret = snd_soc_card_jack_new(rtd->card, "Headset",
  73                SND_JACK_HEADSET | SND_JACK_BTN_0, &broadwell_headset,
  74                broadwell_headset_pins, ARRAY_SIZE(broadwell_headset_pins));
  75        if (ret)
  76                return ret;
  77
  78        rt286_mic_detect(component, &broadwell_headset);
  79        return 0;
  80}
  81
  82
  83static int broadwell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
  84                        struct snd_pcm_hw_params *params)
  85{
  86        struct snd_interval *rate = hw_param_interval(params,
  87                                                      SNDRV_PCM_HW_PARAM_RATE);
  88        struct snd_interval *chan = hw_param_interval(params,
  89                                                      SNDRV_PCM_HW_PARAM_CHANNELS);
  90
  91        /* The ADSP will covert the FE rate to 48k, stereo */
  92        rate->min = rate->max = 48000;
  93        chan->min = chan->max = 2;
  94
  95        /* set SSP0 to 16 bit */
  96        params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
  97        return 0;
  98}
  99
 100static int broadwell_rt286_hw_params(struct snd_pcm_substream *substream,
 101        struct snd_pcm_hw_params *params)
 102{
 103        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
 104        struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
 105        int ret;
 106
 107        ret = snd_soc_dai_set_sysclk(codec_dai, RT286_SCLK_S_PLL, 24000000,
 108                SND_SOC_CLOCK_IN);
 109
 110        if (ret < 0) {
 111                dev_err(rtd->dev, "can't set codec sysclk configuration\n");
 112                return ret;
 113        }
 114
 115        return ret;
 116}
 117
 118static const struct snd_soc_ops broadwell_rt286_ops = {
 119        .hw_params = broadwell_rt286_hw_params,
 120};
 121
 122static const unsigned int channels[] = {
 123        2,
 124};
 125
 126static const struct snd_pcm_hw_constraint_list constraints_channels = {
 127        .count = ARRAY_SIZE(channels),
 128        .list = channels,
 129        .mask = 0,
 130};
 131
 132static int broadwell_fe_startup(struct snd_pcm_substream *substream)
 133{
 134        struct snd_pcm_runtime *runtime = substream->runtime;
 135
 136        /* Board supports stereo configuration only */
 137        runtime->hw.channels_max = 2;
 138        return snd_pcm_hw_constraint_list(runtime, 0,
 139                                          SNDRV_PCM_HW_PARAM_CHANNELS,
 140                                          &constraints_channels);
 141}
 142
 143static const struct snd_soc_ops broadwell_fe_ops = {
 144        .startup = broadwell_fe_startup,
 145};
 146
 147SND_SOC_DAILINK_DEF(system,
 148        DAILINK_COMP_ARRAY(COMP_CPU("System Pin")));
 149
 150SND_SOC_DAILINK_DEF(offload0,
 151        DAILINK_COMP_ARRAY(COMP_CPU("Offload0 Pin")));
 152
 153SND_SOC_DAILINK_DEF(offload1,
 154        DAILINK_COMP_ARRAY(COMP_CPU("Offload1 Pin")));
 155
 156SND_SOC_DAILINK_DEF(loopback,
 157        DAILINK_COMP_ARRAY(COMP_CPU("Loopback Pin")));
 158
 159SND_SOC_DAILINK_DEF(dummy,
 160        DAILINK_COMP_ARRAY(COMP_DUMMY()));
 161
 162SND_SOC_DAILINK_DEF(platform,
 163        DAILINK_COMP_ARRAY(COMP_PLATFORM("haswell-pcm-audio")));
 164
 165SND_SOC_DAILINK_DEF(codec,
 166        DAILINK_COMP_ARRAY(COMP_CODEC("i2c-INT343A:00", "rt286-aif1")));
 167
 168SND_SOC_DAILINK_DEF(ssp0_port,
 169            DAILINK_COMP_ARRAY(COMP_CPU("ssp0-port")));
 170
 171/* broadwell digital audio interface glue - connects codec <--> CPU */
 172static struct snd_soc_dai_link broadwell_rt286_dais[] = {
 173        /* Front End DAI links */
 174        {
 175                .name = "System PCM",
 176                .stream_name = "System Playback/Capture",
 177                .nonatomic = 1,
 178                .dynamic = 1,
 179                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
 180                .ops = &broadwell_fe_ops,
 181                .dpcm_playback = 1,
 182                .dpcm_capture = 1,
 183                SND_SOC_DAILINK_REG(system, dummy, platform),
 184        },
 185        {
 186                .name = "Offload0",
 187                .stream_name = "Offload0 Playback",
 188                .nonatomic = 1,
 189                .dynamic = 1,
 190                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
 191                .dpcm_playback = 1,
 192                SND_SOC_DAILINK_REG(offload0, dummy, platform),
 193        },
 194        {
 195                .name = "Offload1",
 196                .stream_name = "Offload1 Playback",
 197                .nonatomic = 1,
 198                .dynamic = 1,
 199                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
 200                .dpcm_playback = 1,
 201                SND_SOC_DAILINK_REG(offload1, dummy, platform),
 202        },
 203        {
 204                .name = "Loopback PCM",
 205                .stream_name = "Loopback",
 206                .nonatomic = 1,
 207                .dynamic = 1,
 208                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
 209                .dpcm_capture = 1,
 210                SND_SOC_DAILINK_REG(loopback, dummy, platform),
 211        },
 212        /* Back End DAI links */
 213        {
 214                /* SSP0 - Codec */
 215                .name = "Codec",
 216                .id = 0,
 217                .no_pcm = 1,
 218                .init = broadwell_rt286_codec_init,
 219                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 220                        SND_SOC_DAIFMT_CBS_CFS,
 221                .ignore_pmdown_time = 1,
 222                .be_hw_params_fixup = broadwell_ssp0_fixup,
 223                .ops = &broadwell_rt286_ops,
 224                .dpcm_playback = 1,
 225                .dpcm_capture = 1,
 226                SND_SOC_DAILINK_REG(ssp0_port, codec, platform),
 227        },
 228};
 229
 230static int broadwell_disable_jack(struct snd_soc_card *card)
 231{
 232        struct snd_soc_component *component;
 233
 234        for_each_card_components(card, component) {
 235                if (!strcmp(component->name, "i2c-INT343A:00")) {
 236
 237                        dev_dbg(component->dev, "disabling jack detect before going to suspend.\n");
 238                        rt286_mic_detect(component, NULL);
 239                        break;
 240                }
 241        }
 242
 243        return 0;
 244}
 245
 246static int broadwell_suspend(struct snd_soc_card *card)
 247{
 248        return broadwell_disable_jack(card);
 249}
 250
 251static int broadwell_resume(struct snd_soc_card *card){
 252        struct snd_soc_component *component;
 253
 254        for_each_card_components(card, component) {
 255                if (!strcmp(component->name, "i2c-INT343A:00")) {
 256
 257                        dev_dbg(component->dev, "enabling jack detect for resume.\n");
 258                        rt286_mic_detect(component, &broadwell_headset);
 259                        break;
 260                }
 261        }
 262        return 0;
 263}
 264
 265/* use space before codec name to simplify card ID, and simplify driver name */
 266#define SOF_CARD_NAME "bdw rt286" /* card name will be 'sof-bdw rt286' */
 267#define SOF_DRIVER_NAME "SOF"
 268
 269#define CARD_NAME "broadwell-rt286"
 270#define DRIVER_NAME NULL /* card name will be used for driver name */
 271
 272/* broadwell audio machine driver for WPT + RT286S */
 273static struct snd_soc_card broadwell_rt286 = {
 274        .owner = THIS_MODULE,
 275        .dai_link = broadwell_rt286_dais,
 276        .num_links = ARRAY_SIZE(broadwell_rt286_dais),
 277        .controls = broadwell_controls,
 278        .num_controls = ARRAY_SIZE(broadwell_controls),
 279        .dapm_widgets = broadwell_widgets,
 280        .num_dapm_widgets = ARRAY_SIZE(broadwell_widgets),
 281        .dapm_routes = broadwell_rt286_map,
 282        .num_dapm_routes = ARRAY_SIZE(broadwell_rt286_map),
 283        .fully_routed = true,
 284        .suspend_pre = broadwell_suspend,
 285        .resume_post = broadwell_resume,
 286};
 287
 288static int broadwell_audio_probe(struct platform_device *pdev)
 289{
 290        struct snd_soc_acpi_mach *mach;
 291        int ret;
 292
 293        broadwell_rt286.dev = &pdev->dev;
 294
 295        /* override plaform name, if required */
 296        mach = pdev->dev.platform_data;
 297        ret = snd_soc_fixup_dai_links_platform_name(&broadwell_rt286,
 298                                                    mach->mach_params.platform);
 299        if (ret)
 300                return ret;
 301
 302        /* set card and driver name */
 303        if (snd_soc_acpi_sof_parent(&pdev->dev)) {
 304                broadwell_rt286.name = SOF_CARD_NAME;
 305                broadwell_rt286.driver_name = SOF_DRIVER_NAME;
 306        } else {
 307                broadwell_rt286.name = CARD_NAME;
 308                broadwell_rt286.driver_name = DRIVER_NAME;
 309        }
 310
 311        return devm_snd_soc_register_card(&pdev->dev, &broadwell_rt286);
 312}
 313
 314static int broadwell_audio_remove(struct platform_device *pdev)
 315{
 316        struct snd_soc_card *card = platform_get_drvdata(pdev);
 317
 318        return broadwell_disable_jack(card);
 319}
 320
 321static struct platform_driver broadwell_audio = {
 322        .probe = broadwell_audio_probe,
 323        .remove = broadwell_audio_remove,
 324        .driver = {
 325                .name = "broadwell-audio",
 326                .pm = &snd_soc_pm_ops
 327        },
 328};
 329
 330module_platform_driver(broadwell_audio)
 331
 332/* Module information */
 333MODULE_AUTHOR("Liam Girdwood, Xingchao Wang");
 334MODULE_DESCRIPTION("Intel SST Audio for WPT/Broadwell");
 335MODULE_LICENSE("GPL v2");
 336MODULE_ALIAS("platform:broadwell-audio");
 337