Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 //
0003 // Copyright(c) 2022 Intel Corporation. All rights reserved.
0004 
0005 /*
0006  * sof_ssp_amp.c - ASoc Machine driver for Intel platforms
0007  * with RT1308/CS35L41 codec.
0008  */
0009 
0010 #include <linux/acpi.h>
0011 #include <linux/delay.h>
0012 #include <linux/dmi.h>
0013 #include <linux/module.h>
0014 #include <linux/platform_device.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/sof.h>
0020 #include "../../codecs/hdac_hdmi.h"
0021 #include "hda_dsp_common.h"
0022 #include "sof_realtek_common.h"
0023 #include "sof_cirrus_common.h"
0024 
0025 #define NAME_SIZE 32
0026 
0027 /* SSP port ID for speaker amplifier */
0028 #define SOF_AMPLIFIER_SSP(quirk)        ((quirk) & GENMASK(3, 0))
0029 #define SOF_AMPLIFIER_SSP_MASK          (GENMASK(3, 0))
0030 
0031 /* HDMI capture*/
0032 #define SOF_SSP_HDMI_CAPTURE_PRESENT        BIT(4)
0033 #define SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT        5
0034 #define SOF_NO_OF_HDMI_CAPTURE_SSP_MASK     (GENMASK(6, 5))
0035 #define SOF_NO_OF_HDMI_CAPTURE_SSP(quirk)   \
0036     (((quirk) << SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT) & SOF_NO_OF_HDMI_CAPTURE_SSP_MASK)
0037 
0038 #define SOF_HDMI_CAPTURE_1_SSP_SHIFT        7
0039 #define SOF_HDMI_CAPTURE_1_SSP_MASK     (GENMASK(9, 7))
0040 #define SOF_HDMI_CAPTURE_1_SSP(quirk)   \
0041     (((quirk) << SOF_HDMI_CAPTURE_1_SSP_SHIFT) & SOF_HDMI_CAPTURE_1_SSP_MASK)
0042 
0043 #define SOF_HDMI_CAPTURE_2_SSP_SHIFT        10
0044 #define SOF_HDMI_CAPTURE_2_SSP_MASK     (GENMASK(12, 10))
0045 #define SOF_HDMI_CAPTURE_2_SSP(quirk)   \
0046     (((quirk) << SOF_HDMI_CAPTURE_2_SSP_SHIFT) & SOF_HDMI_CAPTURE_2_SSP_MASK)
0047 
0048 /* HDMI playback */
0049 #define SOF_HDMI_PLAYBACK_PRESENT       BIT(13)
0050 #define SOF_NO_OF_HDMI_PLAYBACK_SHIFT       14
0051 #define SOF_NO_OF_HDMI_PLAYBACK_MASK        (GENMASK(16, 14))
0052 #define SOF_NO_OF_HDMI_PLAYBACK(quirk)  \
0053     (((quirk) << SOF_NO_OF_HDMI_PLAYBACK_SHIFT) & SOF_NO_OF_HDMI_PLAYBACK_MASK)
0054 
0055 /* BT audio offload */
0056 #define SOF_SSP_BT_OFFLOAD_PRESENT      BIT(17)
0057 #define SOF_BT_OFFLOAD_SSP_SHIFT        18
0058 #define SOF_BT_OFFLOAD_SSP_MASK         (GENMASK(20, 18))
0059 #define SOF_BT_OFFLOAD_SSP(quirk)   \
0060     (((quirk) << SOF_BT_OFFLOAD_SSP_SHIFT) & SOF_BT_OFFLOAD_SSP_MASK)
0061 
0062 /* Speaker amplifiers */
0063 #define SOF_RT1308_SPEAKER_AMP_PRESENT      BIT(21)
0064 #define SOF_CS35L41_SPEAKER_AMP_PRESENT     BIT(22)
0065 
0066 /* Default: SSP2  */
0067 static unsigned long sof_ssp_amp_quirk = SOF_AMPLIFIER_SSP(2);
0068 
0069 struct sof_hdmi_pcm {
0070     struct list_head head;
0071     struct snd_soc_jack sof_hdmi;
0072     struct snd_soc_dai *codec_dai;
0073     int device;
0074 };
0075 
0076 struct sof_card_private {
0077     struct list_head hdmi_pcm_list;
0078     bool common_hdmi_codec_drv;
0079     bool idisp_codec;
0080 };
0081 
0082 static const struct dmi_system_id chromebook_platforms[] = {
0083     {
0084         .ident = "Google Chromebooks",
0085         .matches = {
0086             DMI_MATCH(DMI_SYS_VENDOR, "Google"),
0087         }
0088     },
0089     {},
0090 };
0091 
0092 static const struct snd_soc_dapm_widget sof_ssp_amp_dapm_widgets[] = {
0093     SND_SOC_DAPM_MIC("SoC DMIC", NULL),
0094 };
0095 
0096 static const struct snd_soc_dapm_route sof_ssp_amp_dapm_routes[] = {
0097     /* digital mics */
0098     {"DMic", NULL, "SoC DMIC"},
0099 };
0100 
0101 static int sof_card_late_probe(struct snd_soc_card *card)
0102 {
0103     struct sof_card_private *ctx = snd_soc_card_get_drvdata(card);
0104     struct snd_soc_component *component = NULL;
0105     char jack_name[NAME_SIZE];
0106     struct sof_hdmi_pcm *pcm;
0107     int err;
0108     int i;
0109 
0110     if (!(sof_ssp_amp_quirk & SOF_HDMI_PLAYBACK_PRESENT))
0111         return 0;
0112 
0113     /* HDMI is not supported by SOF on Baytrail/CherryTrail */
0114     if (!ctx->idisp_codec)
0115         return 0;
0116 
0117     if (list_empty(&ctx->hdmi_pcm_list))
0118         return -EINVAL;
0119 
0120     if (ctx->common_hdmi_codec_drv) {
0121         pcm = list_first_entry(&ctx->hdmi_pcm_list, struct sof_hdmi_pcm,
0122                        head);
0123         component = pcm->codec_dai->component;
0124         return hda_dsp_hdmi_build_controls(card, component);
0125     }
0126 
0127     i = 0;
0128     list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
0129         component = pcm->codec_dai->component;
0130         snprintf(jack_name, sizeof(jack_name),
0131              "HDMI/DP, pcm=%d Jack", pcm->device);
0132         err = snd_soc_card_jack_new(card, jack_name,
0133                         SND_JACK_AVOUT, &pcm->sof_hdmi);
0134 
0135         if (err)
0136             return err;
0137 
0138         err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device,
0139                       &pcm->sof_hdmi);
0140         if (err < 0)
0141             return err;
0142 
0143         i++;
0144     }
0145 
0146     return hdac_hdmi_jack_port_init(component, &card->dapm);
0147 }
0148 
0149 static struct snd_soc_card sof_ssp_amp_card = {
0150     .name         = "ssp_amp",
0151     .owner        = THIS_MODULE,
0152     .dapm_widgets = sof_ssp_amp_dapm_widgets,
0153     .num_dapm_widgets = ARRAY_SIZE(sof_ssp_amp_dapm_widgets),
0154     .dapm_routes = sof_ssp_amp_dapm_routes,
0155     .num_dapm_routes = ARRAY_SIZE(sof_ssp_amp_dapm_routes),
0156     .fully_routed = true,
0157     .late_probe = sof_card_late_probe,
0158 };
0159 
0160 static struct snd_soc_dai_link_component platform_component[] = {
0161     {
0162         /* name might be overridden during probe */
0163         .name = "0000:00:1f.3"
0164     }
0165 };
0166 
0167 static struct snd_soc_dai_link_component dmic_component[] = {
0168     {
0169         .name = "dmic-codec",
0170         .dai_name = "dmic-hifi",
0171     }
0172 };
0173 
0174 static struct snd_soc_dai_link_component dummy_component[] = {
0175     {
0176         .name = "snd-soc-dummy",
0177         .dai_name = "snd-soc-dummy-dai",
0178     }
0179 };
0180 
0181 static int sof_hdmi_init(struct snd_soc_pcm_runtime *rtd)
0182 {
0183     struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
0184     struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0);
0185     struct sof_hdmi_pcm *pcm;
0186 
0187     pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
0188     if (!pcm)
0189         return -ENOMEM;
0190 
0191     /* dai_link id is 1:1 mapped to the PCM device */
0192     pcm->device = rtd->dai_link->id;
0193     pcm->codec_dai = dai;
0194 
0195     list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
0196 
0197     return 0;
0198 }
0199 
0200 #define IDISP_CODEC_MASK    0x4
0201 
0202 static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
0203                               int ssp_codec,
0204                               int dmic_be_num,
0205                               int hdmi_num,
0206                               bool idisp_codec)
0207 {
0208     struct snd_soc_dai_link_component *idisp_components;
0209     struct snd_soc_dai_link_component *cpus;
0210     struct snd_soc_dai_link *links;
0211     int i, id = 0;
0212 
0213     links = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link) *
0214                     sof_ssp_amp_card.num_links, GFP_KERNEL);
0215     cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component) *
0216                     sof_ssp_amp_card.num_links, GFP_KERNEL);
0217     if (!links || !cpus)
0218         return NULL;
0219 
0220     /* HDMI-In SSP */
0221     if (sof_ssp_amp_quirk & SOF_SSP_HDMI_CAPTURE_PRESENT) {
0222         int num_of_hdmi_ssp = (sof_ssp_amp_quirk & SOF_NO_OF_HDMI_CAPTURE_SSP_MASK) >>
0223                 SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT;
0224 
0225         for (i = 1; i <= num_of_hdmi_ssp; i++) {
0226             int port = (i == 1 ? (sof_ssp_amp_quirk & SOF_HDMI_CAPTURE_1_SSP_MASK) >>
0227                         SOF_HDMI_CAPTURE_1_SSP_SHIFT :
0228                         (sof_ssp_amp_quirk & SOF_HDMI_CAPTURE_2_SSP_MASK) >>
0229                         SOF_HDMI_CAPTURE_2_SSP_SHIFT);
0230 
0231             links[id].cpus = &cpus[id];
0232             links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
0233                                   "SSP%d Pin", port);
0234             if (!links[id].cpus->dai_name)
0235                 return NULL;
0236             links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-HDMI", port);
0237             if (!links[id].name)
0238                 return NULL;
0239             links[id].id = id;
0240             links[id].codecs = dummy_component;
0241             links[id].num_codecs = ARRAY_SIZE(dummy_component);
0242             links[id].platforms = platform_component;
0243             links[id].num_platforms = ARRAY_SIZE(platform_component);
0244             links[id].dpcm_capture = 1;
0245             links[id].no_pcm = 1;
0246             links[id].num_cpus = 1;
0247             id++;
0248         }
0249     }
0250 
0251     /* codec SSP */
0252     links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_codec);
0253     if (!links[id].name)
0254         return NULL;
0255 
0256     links[id].id = id;
0257     if (sof_ssp_amp_quirk & SOF_RT1308_SPEAKER_AMP_PRESENT) {
0258         sof_rt1308_dai_link(&links[id]);
0259     } else if (sof_ssp_amp_quirk & SOF_CS35L41_SPEAKER_AMP_PRESENT) {
0260         cs35l41_set_dai_link(&links[id]);
0261 
0262         /* feedback from amplifier */
0263         links[id].dpcm_capture = 1;
0264     }
0265     links[id].platforms = platform_component;
0266     links[id].num_platforms = ARRAY_SIZE(platform_component);
0267     links[id].dpcm_playback = 1;
0268     links[id].no_pcm = 1;
0269     links[id].cpus = &cpus[id];
0270     links[id].num_cpus = 1;
0271     links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_codec);
0272     if (!links[id].cpus->dai_name)
0273         return NULL;
0274 
0275     id++;
0276 
0277     /* dmic */
0278     if (dmic_be_num > 0) {
0279         /* at least we have dmic01 */
0280         links[id].name = "dmic01";
0281         links[id].cpus = &cpus[id];
0282         links[id].cpus->dai_name = "DMIC01 Pin";
0283         if (dmic_be_num > 1) {
0284             /* set up 2 BE links at most */
0285             links[id + 1].name = "dmic16k";
0286             links[id + 1].cpus = &cpus[id + 1];
0287             links[id + 1].cpus->dai_name = "DMIC16k Pin";
0288             dmic_be_num = 2;
0289         }
0290     }
0291 
0292     for (i = 0; i < dmic_be_num; i++) {
0293         links[id].id = id;
0294         links[id].num_cpus = 1;
0295         links[id].codecs = dmic_component;
0296         links[id].num_codecs = ARRAY_SIZE(dmic_component);
0297         links[id].platforms = platform_component;
0298         links[id].num_platforms = ARRAY_SIZE(platform_component);
0299         links[id].ignore_suspend = 1;
0300         links[id].dpcm_capture = 1;
0301         links[id].no_pcm = 1;
0302         id++;
0303     }
0304 
0305     /* HDMI playback */
0306     if (sof_ssp_amp_quirk & SOF_HDMI_PLAYBACK_PRESENT) {
0307         /* HDMI */
0308         if (hdmi_num > 0) {
0309             idisp_components = devm_kzalloc(dev,
0310                        sizeof(struct snd_soc_dai_link_component) *
0311                        hdmi_num, GFP_KERNEL);
0312             if (!idisp_components)
0313                 goto devm_err;
0314         }
0315         for (i = 1; i <= hdmi_num; i++) {
0316             links[id].name = devm_kasprintf(dev, GFP_KERNEL,
0317                             "iDisp%d", i);
0318             if (!links[id].name)
0319                 goto devm_err;
0320 
0321             links[id].id = id;
0322             links[id].cpus = &cpus[id];
0323             links[id].num_cpus = 1;
0324             links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
0325                                   "iDisp%d Pin", i);
0326             if (!links[id].cpus->dai_name)
0327                 goto devm_err;
0328 
0329             if (idisp_codec) {
0330                 idisp_components[i - 1].name = "ehdaudio0D2";
0331                 idisp_components[i - 1].dai_name = devm_kasprintf(dev,
0332                                           GFP_KERNEL,
0333                                           "intel-hdmi-hifi%d",
0334                                           i);
0335                 if (!idisp_components[i - 1].dai_name)
0336                     goto devm_err;
0337             } else {
0338                 idisp_components[i - 1].name = "snd-soc-dummy";
0339                 idisp_components[i - 1].dai_name = "snd-soc-dummy-dai";
0340             }
0341 
0342             links[id].codecs = &idisp_components[i - 1];
0343             links[id].num_codecs = 1;
0344             links[id].platforms = platform_component;
0345             links[id].num_platforms = ARRAY_SIZE(platform_component);
0346             links[id].init = sof_hdmi_init;
0347             links[id].dpcm_playback = 1;
0348             links[id].no_pcm = 1;
0349             id++;
0350         }
0351     }
0352 
0353     /* BT audio offload */
0354     if (sof_ssp_amp_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) {
0355         int port = (sof_ssp_amp_quirk & SOF_BT_OFFLOAD_SSP_MASK) >>
0356                 SOF_BT_OFFLOAD_SSP_SHIFT;
0357 
0358         links[id].id = id;
0359         links[id].cpus = &cpus[id];
0360         links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
0361                               "SSP%d Pin", port);
0362         if (!links[id].cpus->dai_name)
0363             goto devm_err;
0364         links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", port);
0365         if (!links[id].name)
0366             goto devm_err;
0367         links[id].codecs = dummy_component;
0368         links[id].num_codecs = ARRAY_SIZE(dummy_component);
0369         links[id].platforms = platform_component;
0370         links[id].num_platforms = ARRAY_SIZE(platform_component);
0371         links[id].dpcm_playback = 1;
0372         links[id].dpcm_capture = 1;
0373         links[id].no_pcm = 1;
0374         links[id].num_cpus = 1;
0375         id++;
0376     }
0377 
0378     return links;
0379 devm_err:
0380     return NULL;
0381 }
0382 
0383 static int sof_ssp_amp_probe(struct platform_device *pdev)
0384 {
0385     struct snd_soc_dai_link *dai_links;
0386     struct snd_soc_acpi_mach *mach;
0387     struct sof_card_private *ctx;
0388     int dmic_be_num = 0, hdmi_num = 0;
0389     int ret, ssp_codec;
0390 
0391     ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
0392     if (!ctx)
0393         return -ENOMEM;
0394 
0395     if (pdev->id_entry && pdev->id_entry->driver_data)
0396         sof_ssp_amp_quirk = (unsigned long)pdev->id_entry->driver_data;
0397 
0398     mach = pdev->dev.platform_data;
0399 
0400     if (dmi_check_system(chromebook_platforms) || mach->mach_params.dmic_num > 0)
0401         dmic_be_num = 2;
0402 
0403     ssp_codec = sof_ssp_amp_quirk & SOF_AMPLIFIER_SSP_MASK;
0404 
0405     /* set number of dai links */
0406     sof_ssp_amp_card.num_links = 1 + dmic_be_num;
0407 
0408     if (sof_ssp_amp_quirk & SOF_SSP_HDMI_CAPTURE_PRESENT)
0409         sof_ssp_amp_card.num_links += (sof_ssp_amp_quirk & SOF_NO_OF_HDMI_CAPTURE_SSP_MASK) >>
0410                 SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT;
0411 
0412     if (sof_ssp_amp_quirk & SOF_HDMI_PLAYBACK_PRESENT) {
0413         hdmi_num = (sof_ssp_amp_quirk & SOF_NO_OF_HDMI_PLAYBACK_MASK) >>
0414                 SOF_NO_OF_HDMI_PLAYBACK_SHIFT;
0415         /* default number of HDMI DAI's */
0416         if (!hdmi_num)
0417             hdmi_num = 3;
0418 
0419         if (mach->mach_params.codec_mask & IDISP_CODEC_MASK)
0420             ctx->idisp_codec = true;
0421 
0422         sof_ssp_amp_card.num_links += hdmi_num;
0423     }
0424 
0425     if (sof_ssp_amp_quirk & SOF_SSP_BT_OFFLOAD_PRESENT)
0426         sof_ssp_amp_card.num_links++;
0427 
0428     dai_links = sof_card_dai_links_create(&pdev->dev, ssp_codec, dmic_be_num, hdmi_num, ctx->idisp_codec);
0429     if (!dai_links)
0430         return -ENOMEM;
0431 
0432     sof_ssp_amp_card.dai_link = dai_links;
0433 
0434     /* update codec_conf */
0435     if (sof_ssp_amp_quirk & SOF_CS35L41_SPEAKER_AMP_PRESENT) {
0436         cs35l41_set_codec_conf(&sof_ssp_amp_card);
0437     }
0438 
0439     INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
0440 
0441     sof_ssp_amp_card.dev = &pdev->dev;
0442 
0443     /* set platform name for each dailink */
0444     ret = snd_soc_fixup_dai_links_platform_name(&sof_ssp_amp_card,
0445                             mach->mach_params.platform);
0446     if (ret)
0447         return ret;
0448 
0449     ctx->common_hdmi_codec_drv = mach->mach_params.common_hdmi_codec_drv;
0450 
0451     snd_soc_card_set_drvdata(&sof_ssp_amp_card, ctx);
0452 
0453     return devm_snd_soc_register_card(&pdev->dev, &sof_ssp_amp_card);
0454 }
0455 
0456 static const struct platform_device_id board_ids[] = {
0457     {
0458         .name = "sof_ssp_amp",
0459     },
0460     {
0461         .name = "tgl_rt1308_hdmi_ssp",
0462         .driver_data = (kernel_ulong_t)(SOF_AMPLIFIER_SSP(2) |
0463                     SOF_NO_OF_HDMI_CAPTURE_SSP(2) |
0464                     SOF_HDMI_CAPTURE_1_SSP(1) |
0465                     SOF_HDMI_CAPTURE_2_SSP(5) |
0466                     SOF_SSP_HDMI_CAPTURE_PRESENT |
0467                     SOF_RT1308_SPEAKER_AMP_PRESENT),
0468     },
0469     {
0470         .name = "adl_cs35l41",
0471         .driver_data = (kernel_ulong_t)(SOF_AMPLIFIER_SSP(1) |
0472                     SOF_NO_OF_HDMI_PLAYBACK(4) |
0473                     SOF_HDMI_PLAYBACK_PRESENT |
0474                     SOF_BT_OFFLOAD_SSP(2) |
0475                     SOF_SSP_BT_OFFLOAD_PRESENT |
0476                     SOF_CS35L41_SPEAKER_AMP_PRESENT),
0477     },
0478     { }
0479 };
0480 MODULE_DEVICE_TABLE(platform, board_ids);
0481 
0482 static struct platform_driver sof_ssp_amp_driver = {
0483     .probe          = sof_ssp_amp_probe,
0484     .driver = {
0485         .name   = "sof_ssp_amp",
0486         .pm = &snd_soc_pm_ops,
0487     },
0488     .id_table = board_ids,
0489 };
0490 module_platform_driver(sof_ssp_amp_driver);
0491 
0492 MODULE_DESCRIPTION("ASoC Intel(R) SOF Amplifier Machine driver");
0493 MODULE_AUTHOR("balamurugan.c <balamurugan.c@intel.com>");
0494 MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>");
0495 MODULE_LICENSE("GPL");
0496 MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);
0497 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_REALTEK_COMMON);
0498 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_CIRRUS_COMMON);