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 "../common/sst-dsp.h"
  18#include "../haswell/sst-haswell-ipc.h"
  19
  20#include "../../codecs/rt286.h"
  21
  22static struct snd_soc_jack broadwell_headset;
  23/* Headset jack detection DAPM pins */
  24static struct snd_soc_jack_pin broadwell_headset_pins[] = {
  25        {
  26                .pin = "Mic Jack",
  27                .mask = SND_JACK_MICROPHONE,
  28        },
  29        {
  30                .pin = "Headphone Jack",
  31                .mask = SND_JACK_HEADPHONE,
  32        },
  33};
  34
  35static const struct snd_kcontrol_new broadwell_controls[] = {
  36        SOC_DAPM_PIN_SWITCH("Speaker"),
  37        SOC_DAPM_PIN_SWITCH("Headphone Jack"),
  38};
  39
  40static const struct snd_soc_dapm_widget broadwell_widgets[] = {
  41        SND_SOC_DAPM_HP("Headphone Jack", NULL),
  42        SND_SOC_DAPM_SPK("Speaker", NULL),
  43        SND_SOC_DAPM_MIC("Mic Jack", NULL),
  44        SND_SOC_DAPM_MIC("DMIC1", NULL),
  45        SND_SOC_DAPM_MIC("DMIC2", NULL),
  46        SND_SOC_DAPM_LINE("Line Jack", NULL),
  47};
  48
  49static const struct snd_soc_dapm_route broadwell_rt286_map[] = {
  50
  51        /* speaker */
  52        {"Speaker", NULL, "SPOR"},
  53        {"Speaker", NULL, "SPOL"},
  54
  55        /* HP jack connectors - unknown if we have jack deteck */
  56        {"Headphone Jack", NULL, "HPO Pin"},
  57
  58        /* other jacks */
  59        {"MIC1", NULL, "Mic Jack"},
  60        {"LINE1", NULL, "Line Jack"},
  61
  62        /* digital mics */
  63        {"DMIC1 Pin", NULL, "DMIC1"},
  64        {"DMIC2 Pin", NULL, "DMIC2"},
  65
  66        /* CODEC BE connections */
  67        {"SSP0 CODEC IN", NULL, "AIF1 Capture"},
  68        {"AIF1 Playback", NULL, "SSP0 CODEC OUT"},
  69};
  70
  71static int broadwell_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
  72{
  73        struct snd_soc_component *component = rtd->codec_dai->component;
  74        int ret = 0;
  75        ret = snd_soc_card_jack_new(rtd->card, "Headset",
  76                SND_JACK_HEADSET | SND_JACK_BTN_0, &broadwell_headset,
  77                broadwell_headset_pins, ARRAY_SIZE(broadwell_headset_pins));
  78        if (ret)
  79                return ret;
  80
  81        rt286_mic_detect(component, &broadwell_headset);
  82        return 0;
  83}
  84
  85
  86static int broadwell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
  87                        struct snd_pcm_hw_params *params)
  88{
  89        struct snd_interval *rate = hw_param_interval(params,
  90                        SNDRV_PCM_HW_PARAM_RATE);
  91        struct snd_interval *channels = hw_param_interval(params,
  92                                                SNDRV_PCM_HW_PARAM_CHANNELS);
  93
  94        /* The ADSP will covert the FE rate to 48k, stereo */
  95        rate->min = rate->max = 48000;
  96        channels->min = channels->max = 2;
  97
  98        /* set SSP0 to 16 bit */
  99        params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
 100        return 0;
 101}
 102
 103static int broadwell_rt286_hw_params(struct snd_pcm_substream *substream,
 104        struct snd_pcm_hw_params *params)
 105{
 106        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 107        struct snd_soc_dai *codec_dai = rtd->codec_dai;
 108        int ret;
 109
 110        ret = snd_soc_dai_set_sysclk(codec_dai, RT286_SCLK_S_PLL, 24000000,
 111                SND_SOC_CLOCK_IN);
 112
 113        if (ret < 0) {
 114                dev_err(rtd->dev, "can't set codec sysclk configuration\n");
 115                return ret;
 116        }
 117
 118        return ret;
 119}
 120
 121static const struct snd_soc_ops broadwell_rt286_ops = {
 122        .hw_params = broadwell_rt286_hw_params,
 123};
 124
 125#if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL)
 126static int broadwell_rtd_init(struct snd_soc_pcm_runtime *rtd)
 127{
 128        struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
 129        struct sst_pdata *pdata = dev_get_platdata(component->dev);
 130        struct sst_hsw *broadwell = pdata->dsp;
 131        int ret;
 132
 133        /* Set ADSP SSP port settings */
 134        ret = sst_hsw_device_set_config(broadwell, SST_HSW_DEVICE_SSP_0,
 135                SST_HSW_DEVICE_MCLK_FREQ_24_MHZ,
 136                SST_HSW_DEVICE_CLOCK_MASTER, 9);
 137        if (ret < 0) {
 138                dev_err(rtd->dev, "error: failed to set device config\n");
 139                return ret;
 140        }
 141
 142        return 0;
 143}
 144#endif
 145
 146SND_SOC_DAILINK_DEF(system,
 147        DAILINK_COMP_ARRAY(COMP_CPU("System Pin")));
 148
 149SND_SOC_DAILINK_DEF(offload0,
 150        DAILINK_COMP_ARRAY(COMP_CPU("Offload0 Pin")));
 151
 152SND_SOC_DAILINK_DEF(offload1,
 153        DAILINK_COMP_ARRAY(COMP_CPU("Offload1 Pin")));
 154
 155SND_SOC_DAILINK_DEF(loopback,
 156        DAILINK_COMP_ARRAY(COMP_CPU("Loopback Pin")));
 157
 158SND_SOC_DAILINK_DEF(dummy,
 159        DAILINK_COMP_ARRAY(COMP_DUMMY()));
 160
 161SND_SOC_DAILINK_DEF(platform,
 162        DAILINK_COMP_ARRAY(COMP_PLATFORM("haswell-pcm-audio")));
 163
 164SND_SOC_DAILINK_DEF(codec,
 165        DAILINK_COMP_ARRAY(COMP_CODEC("i2c-INT343A:00", "rt286-aif1")));
 166
 167/* broadwell digital audio interface glue - connects codec <--> CPU */
 168static struct snd_soc_dai_link broadwell_rt286_dais[] = {
 169        /* Front End DAI links */
 170        {
 171                .name = "System PCM",
 172                .stream_name = "System Playback/Capture",
 173                .dynamic = 1,
 174#if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL)
 175                .init = broadwell_rtd_init,
 176#endif
 177                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
 178                .dpcm_playback = 1,
 179                .dpcm_capture = 1,
 180                SND_SOC_DAILINK_REG(system, dummy, platform),
 181        },
 182        {
 183                .name = "Offload0",
 184                .stream_name = "Offload0 Playback",
 185                .dynamic = 1,
 186                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
 187                .dpcm_playback = 1,
 188                SND_SOC_DAILINK_REG(offload0, dummy, platform),
 189        },
 190        {
 191                .name = "Offload1",
 192                .stream_name = "Offload1 Playback",
 193                .dynamic = 1,
 194                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
 195                .dpcm_playback = 1,
 196                SND_SOC_DAILINK_REG(offload1, dummy, platform),
 197        },
 198        {
 199                .name = "Loopback PCM",
 200                .stream_name = "Loopback",
 201                .dynamic = 1,
 202                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
 203                .dpcm_capture = 1,
 204                SND_SOC_DAILINK_REG(loopback, dummy, platform),
 205        },
 206        /* Back End DAI links */
 207        {
 208                /* SSP0 - Codec */
 209                .name = "Codec",
 210                .id = 0,
 211                .no_pcm = 1,
 212                .init = broadwell_rt286_codec_init,
 213                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 214                        SND_SOC_DAIFMT_CBS_CFS,
 215                .ignore_suspend = 1,
 216                .ignore_pmdown_time = 1,
 217                .be_hw_params_fixup = broadwell_ssp0_fixup,
 218                .ops = &broadwell_rt286_ops,
 219                .dpcm_playback = 1,
 220                .dpcm_capture = 1,
 221                SND_SOC_DAILINK_REG(dummy, codec, dummy),
 222        },
 223};
 224
 225static int broadwell_suspend(struct snd_soc_card *card){
 226        struct snd_soc_component *component;
 227
 228        for_each_card_components(card, component) {
 229                if (!strcmp(component->name, "i2c-INT343A:00")) {
 230
 231                        dev_dbg(component->dev, "disabling jack detect before going to suspend.\n");
 232                        rt286_mic_detect(component, NULL);
 233                        break;
 234                }
 235        }
 236        return 0;
 237}
 238
 239static int broadwell_resume(struct snd_soc_card *card){
 240        struct snd_soc_component *component;
 241
 242        for_each_card_components(card, component) {
 243                if (!strcmp(component->name, "i2c-INT343A:00")) {
 244
 245                        dev_dbg(component->dev, "enabling jack detect for resume.\n");
 246                        rt286_mic_detect(component, &broadwell_headset);
 247                        break;
 248                }
 249        }
 250        return 0;
 251}
 252
 253/* broadwell audio machine driver for WPT + RT286S */
 254static struct snd_soc_card broadwell_rt286 = {
 255        .name = "broadwell-rt286",
 256        .owner = THIS_MODULE,
 257        .dai_link = broadwell_rt286_dais,
 258        .num_links = ARRAY_SIZE(broadwell_rt286_dais),
 259        .controls = broadwell_controls,
 260        .num_controls = ARRAY_SIZE(broadwell_controls),
 261        .dapm_widgets = broadwell_widgets,
 262        .num_dapm_widgets = ARRAY_SIZE(broadwell_widgets),
 263        .dapm_routes = broadwell_rt286_map,
 264        .num_dapm_routes = ARRAY_SIZE(broadwell_rt286_map),
 265        .fully_routed = true,
 266        .suspend_pre = broadwell_suspend,
 267        .resume_post = broadwell_resume,
 268};
 269
 270static int broadwell_audio_probe(struct platform_device *pdev)
 271{
 272        struct snd_soc_acpi_mach *mach;
 273        const char *platform_name = NULL;
 274        int ret;
 275
 276        broadwell_rt286.dev = &pdev->dev;
 277
 278        /* override plaform name, if required */
 279        mach = (&pdev->dev)->platform_data;
 280        if (mach) /* extra check since legacy does not pass parameters */
 281                platform_name = mach->mach_params.platform;
 282
 283        ret = snd_soc_fixup_dai_links_platform_name(&broadwell_rt286,
 284                                                    platform_name);
 285        if (ret)
 286                return ret;
 287
 288        return devm_snd_soc_register_card(&pdev->dev, &broadwell_rt286);
 289}
 290
 291static struct platform_driver broadwell_audio = {
 292        .probe = broadwell_audio_probe,
 293        .driver = {
 294                .name = "broadwell-audio",
 295        },
 296};
 297
 298module_platform_driver(broadwell_audio)
 299
 300/* Module information */
 301MODULE_AUTHOR("Liam Girdwood, Xingchao Wang");
 302MODULE_DESCRIPTION("Intel SST Audio for WPT/Broadwell");
 303MODULE_LICENSE("GPL v2");
 304MODULE_ALIAS("platform:broadwell-audio");
 305