Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 // Copyright(c) 2018-2020 Intel Corporation.
0003 
0004 /*
0005  * Intel SOF Machine Driver for Intel platforms with TI PCM512x codec,
0006  * e.g. Up or Up2 with Hifiberry DAC+ HAT
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 /* Default: SSP5 */
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     /* dai_link id is 1:1 mapped to the PCM device */
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         /* name might be overridden during probe */
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     /* HDMI is not supported by SOF on Baytrail/CherryTrail */
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     /* Speaker */
0168     {"Ext Spk", NULL, "OUTR"},
0169     {"Ext Spk", NULL, "OUTL"},
0170 };
0171 
0172 static const struct snd_soc_dapm_route dmic_map[] = {
0173     /* digital mics */
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         /* Don't need to add routes if widget addition failed */
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 /* sof audio machine driver for pcm512x codec */
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     /* codec SSP */
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      * capture only supported with specific versions of the Hifiberry DAC+
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     /* dmic */
0274     if (dmic_be_num > 0) {
0275         /* at least we have dmic01 */
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             /* set up 2 BE links at most */
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     /* HDMI */
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          * topology cannot be loaded if codec is missing, so
0326          * use the dummy codec if needed
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         /* default quirk for legacy cpu */
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         /* links are always present in topology */
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     /* compute number of dai links */
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     /* set platform name for each dailink */
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);