0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/clk.h>
0009 #include <linux/dmi.h>
0010 #include <linux/i2c.h>
0011 #include <linux/input.h>
0012 #include <linux/module.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/types.h>
0015 #include <sound/core.h>
0016 #include <sound/jack.h>
0017 #include <sound/pcm.h>
0018 #include <sound/pcm_params.h>
0019 #include <sound/soc.h>
0020 #include <sound/soc-acpi.h>
0021 #include "../../codecs/pcm512x.h"
0022 #include "../common/soc-intel-quirks.h"
0023 #include "hda_dsp_common.h"
0024
0025 #define NAME_SIZE 32
0026
0027 #define SOF_PCM512X_SSP_CODEC(quirk) ((quirk) & GENMASK(3, 0))
0028 #define SOF_PCM512X_SSP_CODEC_MASK (GENMASK(3, 0))
0029 #define SOF_PCM512X_ENABLE_SSP_CAPTURE BIT(4)
0030 #define SOF_PCM512X_ENABLE_DMIC BIT(5)
0031
0032 #define IDISP_CODEC_MASK 0x4
0033
0034
0035 static unsigned long sof_pcm512x_quirk =
0036 SOF_PCM512X_SSP_CODEC(5) |
0037 SOF_PCM512X_ENABLE_SSP_CAPTURE |
0038 SOF_PCM512X_ENABLE_DMIC;
0039
0040 static bool is_legacy_cpu;
0041
0042 struct sof_hdmi_pcm {
0043 struct list_head head;
0044 struct snd_soc_dai *codec_dai;
0045 int device;
0046 };
0047
0048 struct sof_card_private {
0049 struct list_head hdmi_pcm_list;
0050 bool idisp_codec;
0051 };
0052
0053 static int sof_pcm512x_quirk_cb(const struct dmi_system_id *id)
0054 {
0055 sof_pcm512x_quirk = (unsigned long)id->driver_data;
0056 return 1;
0057 }
0058
0059 static const struct dmi_system_id sof_pcm512x_quirk_table[] = {
0060 {
0061 .callback = sof_pcm512x_quirk_cb,
0062 .matches = {
0063 DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
0064 DMI_MATCH(DMI_PRODUCT_NAME, "UP-CHT01"),
0065 },
0066 .driver_data = (void *)(SOF_PCM512X_SSP_CODEC(2)),
0067 },
0068 {}
0069 };
0070
0071 static int sof_hdmi_init(struct snd_soc_pcm_runtime *rtd)
0072 {
0073 struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
0074 struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0);
0075 struct sof_hdmi_pcm *pcm;
0076
0077 pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
0078 if (!pcm)
0079 return -ENOMEM;
0080
0081
0082 pcm->device = rtd->dai_link->id;
0083 pcm->codec_dai = dai;
0084
0085 list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
0086
0087 return 0;
0088 }
0089
0090 static int sof_pcm512x_codec_init(struct snd_soc_pcm_runtime *rtd)
0091 {
0092 struct snd_soc_component *codec = asoc_rtd_to_codec(rtd, 0)->component;
0093
0094 snd_soc_component_update_bits(codec, PCM512x_GPIO_EN, 0x08, 0x08);
0095 snd_soc_component_update_bits(codec, PCM512x_GPIO_OUTPUT_4, 0x0f, 0x02);
0096 snd_soc_component_update_bits(codec, PCM512x_GPIO_CONTROL_1,
0097 0x08, 0x08);
0098
0099 return 0;
0100 }
0101
0102 static int aif1_startup(struct snd_pcm_substream *substream)
0103 {
0104 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0105 struct snd_soc_component *codec = asoc_rtd_to_codec(rtd, 0)->component;
0106
0107 snd_soc_component_update_bits(codec, PCM512x_GPIO_CONTROL_1,
0108 0x08, 0x08);
0109
0110 return 0;
0111 }
0112
0113 static void aif1_shutdown(struct snd_pcm_substream *substream)
0114 {
0115 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0116 struct snd_soc_component *codec = asoc_rtd_to_codec(rtd, 0)->component;
0117
0118 snd_soc_component_update_bits(codec, PCM512x_GPIO_CONTROL_1,
0119 0x08, 0x00);
0120 }
0121
0122 static const struct snd_soc_ops sof_pcm512x_ops = {
0123 .startup = aif1_startup,
0124 .shutdown = aif1_shutdown,
0125 };
0126
0127 static struct snd_soc_dai_link_component platform_component[] = {
0128 {
0129
0130 .name = "0000:00:1f.3"
0131 }
0132 };
0133
0134 static int sof_card_late_probe(struct snd_soc_card *card)
0135 {
0136 struct sof_card_private *ctx = snd_soc_card_get_drvdata(card);
0137 struct sof_hdmi_pcm *pcm;
0138
0139
0140 if (is_legacy_cpu)
0141 return 0;
0142
0143 if (list_empty(&ctx->hdmi_pcm_list))
0144 return -EINVAL;
0145
0146 if (!ctx->idisp_codec)
0147 return 0;
0148
0149 pcm = list_first_entry(&ctx->hdmi_pcm_list, struct sof_hdmi_pcm, head);
0150
0151 return hda_dsp_hdmi_build_controls(card, pcm->codec_dai->component);
0152 }
0153
0154 static const struct snd_kcontrol_new sof_controls[] = {
0155 SOC_DAPM_PIN_SWITCH("Ext Spk"),
0156 };
0157
0158 static const struct snd_soc_dapm_widget sof_widgets[] = {
0159 SND_SOC_DAPM_SPK("Ext Spk", NULL),
0160 };
0161
0162 static const struct snd_soc_dapm_widget dmic_widgets[] = {
0163 SND_SOC_DAPM_MIC("SoC DMIC", NULL),
0164 };
0165
0166 static const struct snd_soc_dapm_route sof_map[] = {
0167
0168 {"Ext Spk", NULL, "OUTR"},
0169 {"Ext Spk", NULL, "OUTL"},
0170 };
0171
0172 static const struct snd_soc_dapm_route dmic_map[] = {
0173
0174 {"DMic", NULL, "SoC DMIC"},
0175 };
0176
0177 static int dmic_init(struct snd_soc_pcm_runtime *rtd)
0178 {
0179 struct snd_soc_card *card = rtd->card;
0180 int ret;
0181
0182 ret = snd_soc_dapm_new_controls(&card->dapm, dmic_widgets,
0183 ARRAY_SIZE(dmic_widgets));
0184 if (ret) {
0185 dev_err(card->dev, "DMic widget addition failed: %d\n", ret);
0186
0187 return ret;
0188 }
0189
0190 ret = snd_soc_dapm_add_routes(&card->dapm, dmic_map,
0191 ARRAY_SIZE(dmic_map));
0192
0193 if (ret)
0194 dev_err(card->dev, "DMic map addition failed: %d\n", ret);
0195
0196 return ret;
0197 }
0198
0199
0200 static struct snd_soc_card sof_audio_card_pcm512x = {
0201 .name = "pcm512x",
0202 .owner = THIS_MODULE,
0203 .controls = sof_controls,
0204 .num_controls = ARRAY_SIZE(sof_controls),
0205 .dapm_widgets = sof_widgets,
0206 .num_dapm_widgets = ARRAY_SIZE(sof_widgets),
0207 .dapm_routes = sof_map,
0208 .num_dapm_routes = ARRAY_SIZE(sof_map),
0209 .fully_routed = true,
0210 .late_probe = sof_card_late_probe,
0211 };
0212
0213 SND_SOC_DAILINK_DEF(pcm512x_component,
0214 DAILINK_COMP_ARRAY(COMP_CODEC("i2c-104C5122:00", "pcm512x-hifi")));
0215 SND_SOC_DAILINK_DEF(dmic_component,
0216 DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi")));
0217
0218 static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
0219 int ssp_codec,
0220 int dmic_be_num,
0221 int hdmi_num,
0222 bool idisp_codec)
0223 {
0224 struct snd_soc_dai_link_component *idisp_components;
0225 struct snd_soc_dai_link_component *cpus;
0226 struct snd_soc_dai_link *links;
0227 int i, id = 0;
0228
0229 links = devm_kcalloc(dev, sof_audio_card_pcm512x.num_links,
0230 sizeof(struct snd_soc_dai_link), GFP_KERNEL);
0231 cpus = devm_kcalloc(dev, sof_audio_card_pcm512x.num_links,
0232 sizeof(struct snd_soc_dai_link_component), GFP_KERNEL);
0233 if (!links || !cpus)
0234 goto devm_err;
0235
0236
0237 links[id].name = devm_kasprintf(dev, GFP_KERNEL,
0238 "SSP%d-Codec", ssp_codec);
0239 if (!links[id].name)
0240 goto devm_err;
0241
0242 links[id].id = id;
0243 links[id].codecs = pcm512x_component;
0244 links[id].num_codecs = ARRAY_SIZE(pcm512x_component);
0245 links[id].platforms = platform_component;
0246 links[id].num_platforms = ARRAY_SIZE(platform_component);
0247 links[id].init = sof_pcm512x_codec_init;
0248 links[id].ops = &sof_pcm512x_ops;
0249 links[id].dpcm_playback = 1;
0250
0251
0252
0253 if (sof_pcm512x_quirk & SOF_PCM512X_ENABLE_SSP_CAPTURE)
0254 links[id].dpcm_capture = 1;
0255 links[id].no_pcm = 1;
0256 links[id].cpus = &cpus[id];
0257 links[id].num_cpus = 1;
0258 if (is_legacy_cpu) {
0259 links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
0260 "ssp%d-port",
0261 ssp_codec);
0262 if (!links[id].cpus->dai_name)
0263 goto devm_err;
0264 } else {
0265 links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
0266 "SSP%d Pin",
0267 ssp_codec);
0268 if (!links[id].cpus->dai_name)
0269 goto devm_err;
0270 }
0271 id++;
0272
0273
0274 if (dmic_be_num > 0) {
0275
0276 links[id].name = "dmic01";
0277 links[id].cpus = &cpus[id];
0278 links[id].cpus->dai_name = "DMIC01 Pin";
0279 links[id].init = dmic_init;
0280 if (dmic_be_num > 1) {
0281
0282 links[id + 1].name = "dmic16k";
0283 links[id + 1].cpus = &cpus[id + 1];
0284 links[id + 1].cpus->dai_name = "DMIC16k Pin";
0285 dmic_be_num = 2;
0286 }
0287 }
0288
0289 for (i = 0; i < dmic_be_num; i++) {
0290 links[id].id = id;
0291 links[id].num_cpus = 1;
0292 links[id].codecs = dmic_component;
0293 links[id].num_codecs = ARRAY_SIZE(dmic_component);
0294 links[id].platforms = platform_component;
0295 links[id].num_platforms = ARRAY_SIZE(platform_component);
0296 links[id].ignore_suspend = 1;
0297 links[id].dpcm_capture = 1;
0298 links[id].no_pcm = 1;
0299 id++;
0300 }
0301
0302
0303 if (hdmi_num > 0) {
0304 idisp_components = devm_kcalloc(dev, hdmi_num,
0305 sizeof(struct snd_soc_dai_link_component),
0306 GFP_KERNEL);
0307 if (!idisp_components)
0308 goto devm_err;
0309 }
0310 for (i = 1; i <= hdmi_num; i++) {
0311 links[id].name = devm_kasprintf(dev, GFP_KERNEL,
0312 "iDisp%d", i);
0313 if (!links[id].name)
0314 goto devm_err;
0315
0316 links[id].id = id;
0317 links[id].cpus = &cpus[id];
0318 links[id].num_cpus = 1;
0319 links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
0320 "iDisp%d Pin", i);
0321 if (!links[id].cpus->dai_name)
0322 goto devm_err;
0323
0324
0325
0326
0327
0328 if (idisp_codec) {
0329 idisp_components[i - 1].name = "ehdaudio0D2";
0330 idisp_components[i - 1].dai_name =
0331 devm_kasprintf(dev, GFP_KERNEL,
0332 "intel-hdmi-hifi%d", i);
0333 } else {
0334 idisp_components[i - 1].name = "snd-soc-dummy";
0335 idisp_components[i - 1].dai_name = "snd-soc-dummy-dai";
0336 }
0337 if (!idisp_components[i - 1].dai_name)
0338 goto devm_err;
0339
0340 links[id].codecs = &idisp_components[i - 1];
0341 links[id].num_codecs = 1;
0342 links[id].platforms = platform_component;
0343 links[id].num_platforms = ARRAY_SIZE(platform_component);
0344 links[id].init = sof_hdmi_init;
0345 links[id].dpcm_playback = 1;
0346 links[id].no_pcm = 1;
0347 id++;
0348 }
0349
0350 return links;
0351 devm_err:
0352 return NULL;
0353 }
0354
0355 static int sof_audio_probe(struct platform_device *pdev)
0356 {
0357 struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;
0358 struct snd_soc_dai_link *dai_links;
0359 struct sof_card_private *ctx;
0360 int dmic_be_num, hdmi_num;
0361 int ret, ssp_codec;
0362
0363 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
0364 if (!ctx)
0365 return -ENOMEM;
0366
0367 hdmi_num = 0;
0368 if (soc_intel_is_byt() || soc_intel_is_cht()) {
0369 is_legacy_cpu = true;
0370 dmic_be_num = 0;
0371
0372 sof_pcm512x_quirk = SOF_PCM512X_SSP_CODEC(2);
0373 } else {
0374 dmic_be_num = 2;
0375 if (mach->mach_params.common_hdmi_codec_drv &&
0376 (mach->mach_params.codec_mask & IDISP_CODEC_MASK))
0377 ctx->idisp_codec = true;
0378
0379
0380 hdmi_num = 3;
0381 }
0382
0383 dmi_check_system(sof_pcm512x_quirk_table);
0384
0385 dev_dbg(&pdev->dev, "sof_pcm512x_quirk = %lx\n", sof_pcm512x_quirk);
0386
0387 ssp_codec = sof_pcm512x_quirk & SOF_PCM512X_SSP_CODEC_MASK;
0388
0389 if (!(sof_pcm512x_quirk & SOF_PCM512X_ENABLE_DMIC))
0390 dmic_be_num = 0;
0391
0392
0393 sof_audio_card_pcm512x.num_links = 1 + dmic_be_num + hdmi_num;
0394
0395 dai_links = sof_card_dai_links_create(&pdev->dev, ssp_codec,
0396 dmic_be_num, hdmi_num,
0397 ctx->idisp_codec);
0398 if (!dai_links)
0399 return -ENOMEM;
0400
0401 sof_audio_card_pcm512x.dai_link = dai_links;
0402
0403 INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
0404
0405 sof_audio_card_pcm512x.dev = &pdev->dev;
0406
0407
0408 ret = snd_soc_fixup_dai_links_platform_name(&sof_audio_card_pcm512x,
0409 mach->mach_params.platform);
0410 if (ret)
0411 return ret;
0412
0413 snd_soc_card_set_drvdata(&sof_audio_card_pcm512x, ctx);
0414
0415 return devm_snd_soc_register_card(&pdev->dev,
0416 &sof_audio_card_pcm512x);
0417 }
0418
0419 static int sof_pcm512x_remove(struct platform_device *pdev)
0420 {
0421 struct snd_soc_card *card = platform_get_drvdata(pdev);
0422 struct snd_soc_component *component;
0423
0424 for_each_card_components(card, component) {
0425 if (!strcmp(component->name, pcm512x_component[0].name)) {
0426 snd_soc_component_set_jack(component, NULL, NULL);
0427 break;
0428 }
0429 }
0430
0431 return 0;
0432 }
0433
0434 static struct platform_driver sof_audio = {
0435 .probe = sof_audio_probe,
0436 .remove = sof_pcm512x_remove,
0437 .driver = {
0438 .name = "sof_pcm512x",
0439 .pm = &snd_soc_pm_ops,
0440 },
0441 };
0442 module_platform_driver(sof_audio)
0443
0444 MODULE_DESCRIPTION("ASoC Intel(R) SOF + PCM512x Machine driver");
0445 MODULE_AUTHOR("Pierre-Louis Bossart");
0446 MODULE_LICENSE("GPL v2");
0447 MODULE_ALIAS("platform:sof_pcm512x");
0448 MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);