broadwell.c (8868B)
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_pins(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_CBC_CFC, 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 platform 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");