Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 // Copyright (c) 2020 Intel Corporation
0003 
0004 /*
0005  * ehl_rt5660 - ASOC Machine driver for Elkhart Lake platforms
0006  * with rt5660 codec
0007  */
0008 
0009 #include <linux/acpi.h>
0010 #include <sound/core.h>
0011 #include <linux/device.h>
0012 #include <linux/errno.h>
0013 #include <linux/gfp.h>
0014 #include <sound/jack.h>
0015 #include <linux/kernel.h>
0016 #include <linux/list.h>
0017 #include <linux/module.h>
0018 #include <sound/pcm.h>
0019 #include <sound/pcm_params.h>
0020 #include <sound/soc.h>
0021 #include <sound/soc-acpi.h>
0022 
0023 #include "hda_dsp_common.h"
0024 #include "../../codecs/rt5660.h"
0025 
0026 #define DUAL_CHANNEL 2
0027 #define HDMI_LINK_START 3
0028 #define HDMI_LINE_END 6
0029 #define NAME_SIZE   32
0030 #define IDISP_CODEC_MASK    0x4
0031 
0032 struct sof_card_private {
0033     struct list_head hdmi_pcm_list;
0034     bool idisp_codec;
0035 };
0036 
0037 static const struct snd_kcontrol_new rt5660_controls[] = {
0038     SOC_DAPM_PIN_SWITCH("Speaker"),
0039     /* There are two MICBIAS in rt5660, each for one MIC */
0040     SOC_DAPM_PIN_SWITCH("Headset Mic"),
0041     SOC_DAPM_PIN_SWITCH("Headset Mic2"),
0042     SOC_DAPM_PIN_SWITCH("Line Out"),
0043 };
0044 
0045 static const struct snd_soc_dapm_widget rt5660_widgets[] = {
0046     SND_SOC_DAPM_SPK("Speaker", NULL),
0047     SND_SOC_DAPM_MIC("Headset Mic", NULL),
0048     SND_SOC_DAPM_MIC("Headset Mic2", NULL),
0049     SND_SOC_DAPM_MIC("SoC DMIC", NULL),
0050     SND_SOC_DAPM_LINE("Line Out", NULL),
0051 };
0052 
0053 static const struct snd_soc_dapm_route rt5660_map[] = {
0054     {"Speaker", NULL, "SPO"},
0055 
0056     {"Headset Mic", NULL, "MICBIAS1"},
0057     {"Headset Mic2", NULL, "MICBIAS2"},
0058 
0059     {"IN1P", NULL, "Headset Mic"},
0060     {"IN2P", NULL, "Headset Mic2"},
0061 
0062     {"Line Out", NULL, "LOUTL"},
0063     {"Line Out", NULL, "LOUTR"},
0064 
0065     {"DMic", NULL, "SoC DMIC"},
0066 };
0067 
0068 struct sof_hdmi_pcm {
0069     struct list_head head;
0070     struct snd_soc_dai *codec_dai;
0071     int device;
0072 };
0073 
0074 static int hdmi_init(struct snd_soc_pcm_runtime *rtd)
0075 {
0076     struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
0077     struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0);
0078     struct sof_hdmi_pcm *pcm;
0079 
0080     pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
0081     if (!pcm)
0082         return -ENOMEM;
0083 
0084     /* dai_link id is 1:1 mapped to the PCM device */
0085     pcm->device = rtd->dai_link->id;
0086     pcm->codec_dai = dai;
0087 
0088     list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
0089 
0090     return 0;
0091 }
0092 
0093 static int card_late_probe(struct snd_soc_card *card)
0094 {
0095     struct sof_card_private *ctx = snd_soc_card_get_drvdata(card);
0096     struct sof_hdmi_pcm *pcm;
0097 
0098     if (list_empty(&ctx->hdmi_pcm_list))
0099         return -ENOENT;
0100 
0101     if (!ctx->idisp_codec)
0102         return 0;
0103 
0104     pcm = list_first_entry(&ctx->hdmi_pcm_list, struct sof_hdmi_pcm, head);
0105 
0106     return hda_dsp_hdmi_build_controls(card, pcm->codec_dai->component);
0107 }
0108 
0109 static int rt5660_hw_params(struct snd_pcm_substream *substream,
0110                 struct snd_pcm_hw_params *params)
0111 {
0112     struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0113     struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
0114     int ret;
0115 
0116     ret = snd_soc_dai_set_sysclk(codec_dai,
0117                      RT5660_SCLK_S_PLL1,
0118                      params_rate(params) * 512,
0119                      SND_SOC_CLOCK_IN);
0120     if (ret < 0) {
0121         dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret);
0122         return ret;
0123     }
0124 
0125     ret = snd_soc_dai_set_pll(codec_dai, 0,
0126                   RT5660_PLL1_S_BCLK,
0127                   params_rate(params) * 50,
0128                   params_rate(params) * 512);
0129     if (ret < 0)
0130         dev_err(codec_dai->dev, "can't set codec pll: %d\n", ret);
0131 
0132     return ret;
0133 }
0134 
0135 static struct snd_soc_ops rt5660_ops = {
0136     .hw_params = rt5660_hw_params,
0137 };
0138 
0139 SND_SOC_DAILINK_DEF(ssp0_pin,
0140     DAILINK_COMP_ARRAY(COMP_CPU("SSP0 Pin")));
0141 
0142 SND_SOC_DAILINK_DEF(rt5660_codec,
0143     DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5660:00", "rt5660-aif1")));
0144 
0145 SND_SOC_DAILINK_DEF(platform,
0146     DAILINK_COMP_ARRAY(COMP_PLATFORM("0000:00:1f.3")));
0147 
0148 SND_SOC_DAILINK_DEF(dmic_pin,
0149     DAILINK_COMP_ARRAY(COMP_CPU("DMIC01 Pin")));
0150 SND_SOC_DAILINK_DEF(dmic_codec,
0151     DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi")));
0152 SND_SOC_DAILINK_DEF(dmic16k,
0153     DAILINK_COMP_ARRAY(COMP_CPU("DMIC16k Pin")));
0154 
0155 SND_SOC_DAILINK_DEF(idisp1_pin,
0156     DAILINK_COMP_ARRAY(COMP_CPU("iDisp1 Pin")));
0157 SND_SOC_DAILINK_DEF(idisp1_codec,
0158     DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi1")));
0159 
0160 SND_SOC_DAILINK_DEF(idisp2_pin,
0161     DAILINK_COMP_ARRAY(COMP_CPU("iDisp2 Pin")));
0162 SND_SOC_DAILINK_DEF(idisp2_codec,
0163     DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi2")));
0164 
0165 SND_SOC_DAILINK_DEF(idisp3_pin,
0166     DAILINK_COMP_ARRAY(COMP_CPU("iDisp3 Pin")));
0167 SND_SOC_DAILINK_DEF(idisp3_codec,
0168     DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi3")));
0169 
0170 SND_SOC_DAILINK_DEF(idisp4_pin,
0171     DAILINK_COMP_ARRAY(COMP_CPU("iDisp4 Pin")));
0172 SND_SOC_DAILINK_DEF(idisp4_codec,
0173     DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi4")));
0174 
0175 static struct snd_soc_dai_link ehl_rt5660_dailink[] = {
0176     /* back ends */
0177     {
0178         .name = "SSP0-Codec",
0179         .id = 0,
0180         .no_pcm = 1,
0181         .dpcm_playback = 1,
0182         .dpcm_capture = 1,
0183         .ops = &rt5660_ops,
0184         SND_SOC_DAILINK_REG(ssp0_pin, rt5660_codec, platform),
0185     },
0186     {
0187         .name = "dmic48k",
0188         .id = 1,
0189         .ignore_suspend = 1,
0190         .dpcm_capture = 1,
0191         .no_pcm = 1,
0192         SND_SOC_DAILINK_REG(dmic_pin, dmic_codec, platform),
0193     },
0194     {
0195         .name = "dmic16k",
0196         .id = 2,
0197         .ignore_suspend = 1,
0198         .dpcm_capture = 1,
0199         .no_pcm = 1,
0200         SND_SOC_DAILINK_REG(dmic16k, dmic_codec, platform),
0201     },
0202     {
0203         .name = "iDisp1",
0204         .id = 5,
0205         .init = hdmi_init,
0206         .dpcm_playback = 1,
0207         .no_pcm = 1,
0208         SND_SOC_DAILINK_REG(idisp1_pin, idisp1_codec, platform),
0209     },
0210     {
0211         .name = "iDisp2",
0212         .id = 6,
0213         .init = hdmi_init,
0214         .dpcm_playback = 1,
0215         .no_pcm = 1,
0216         SND_SOC_DAILINK_REG(idisp2_pin, idisp2_codec, platform),
0217     },
0218     {
0219         .name = "iDisp3",
0220         .id = 7,
0221         .init = hdmi_init,
0222         .dpcm_playback = 1,
0223         .no_pcm = 1,
0224         SND_SOC_DAILINK_REG(idisp3_pin, idisp3_codec, platform),
0225     },
0226     {
0227         .name = "iDisp4",
0228         .id = 8,
0229         .init = hdmi_init,
0230         .dpcm_playback = 1,
0231         .no_pcm = 1,
0232         SND_SOC_DAILINK_REG(idisp4_pin, idisp4_codec, platform),
0233     },
0234 };
0235 
0236 /* SoC card */
0237 static struct snd_soc_card snd_soc_card_ehl_rt5660 = {
0238     .name = "ehl-rt5660",
0239     .owner = THIS_MODULE,
0240     .dai_link = ehl_rt5660_dailink,
0241     .num_links = ARRAY_SIZE(ehl_rt5660_dailink),
0242     .dapm_widgets = rt5660_widgets,
0243     .num_dapm_widgets = ARRAY_SIZE(rt5660_widgets),
0244     .dapm_routes = rt5660_map,
0245     .num_dapm_routes = ARRAY_SIZE(rt5660_map),
0246     .controls = rt5660_controls,
0247     .num_controls = ARRAY_SIZE(rt5660_controls),
0248     .fully_routed = true,
0249     .late_probe = card_late_probe,
0250 };
0251 
0252 /* If hdmi codec is not supported, switch to use dummy codec */
0253 static void hdmi_link_init(struct snd_soc_card *card,
0254                struct sof_card_private *ctx,
0255                struct snd_soc_acpi_mach *mach)
0256 {
0257     struct snd_soc_dai_link *link;
0258     int i;
0259 
0260     if (mach->mach_params.common_hdmi_codec_drv &&
0261         (mach->mach_params.codec_mask & IDISP_CODEC_MASK)) {
0262         ctx->idisp_codec = true;
0263         return;
0264     }
0265 
0266     /*
0267      * if HDMI is not enabled in kernel config, or
0268      * hdmi codec is not supported
0269      */
0270     for (i = HDMI_LINK_START; i <= HDMI_LINE_END; i++) {
0271         link = &card->dai_link[i];
0272         link->codecs[0].name = "snd-soc-dummy";
0273         link->codecs[0].dai_name = "snd-soc-dummy-dai";
0274     }
0275 }
0276 
0277 static int snd_ehl_rt5660_probe(struct platform_device *pdev)
0278 {
0279     struct snd_soc_acpi_mach *mach;
0280     struct snd_soc_card *card = &snd_soc_card_ehl_rt5660;
0281     struct sof_card_private *ctx;
0282     int ret;
0283 
0284     card->dev = &pdev->dev;
0285 
0286     ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
0287     if (!ctx)
0288         return -ENOMEM;
0289     INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
0290     snd_soc_card_set_drvdata(card, ctx);
0291 
0292     mach = pdev->dev.platform_data;
0293     ret = snd_soc_fixup_dai_links_platform_name(card,
0294                             mach->mach_params.platform);
0295     if (ret)
0296         return ret;
0297 
0298     hdmi_link_init(card, ctx, mach);
0299 
0300     return devm_snd_soc_register_card(&pdev->dev, card);
0301 }
0302 
0303 static const struct platform_device_id ehl_board_ids[] = {
0304     { .name = "ehl_rt5660" },
0305     { }
0306 };
0307 MODULE_DEVICE_TABLE(platform, ehl_board_ids);
0308 
0309 static struct platform_driver snd_ehl_rt5660_driver = {
0310     .driver = {
0311         .name = "ehl_rt5660",
0312         .pm = &snd_soc_pm_ops,
0313     },
0314     .probe = snd_ehl_rt5660_probe,
0315     .id_table = ehl_board_ids,
0316 };
0317 
0318 module_platform_driver(snd_ehl_rt5660_driver);
0319 
0320 MODULE_DESCRIPTION("ASoC Intel(R) Elkhartlake + rt5660 Machine driver");
0321 MODULE_AUTHOR("libin.yang@intel.com");
0322 MODULE_LICENSE("GPL v2");
0323 MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);