ASoC: Intel: sof_cs42l42: add support for jsl_cs4242_mx98360a
[linux-2.6-microblaze.git] / sound / soc / intel / boards / sof_cs42l42.c
index 42aadf8..ce78c18 100644 (file)
@@ -16,6 +16,7 @@
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
+#include <sound/sof.h>
 #include <sound/soc-acpi.h>
 #include <dt-bindings/sound/cs42l42.h>
 #include "../../codecs/hdac_hdmi.h"
 #define SOF_CS42L42_NUM_HDMIDEV_MASK           (GENMASK(9, 7))
 #define SOF_CS42L42_NUM_HDMIDEV(quirk) \
        (((quirk) << SOF_CS42L42_NUM_HDMIDEV_SHIFT) & SOF_CS42L42_NUM_HDMIDEV_MASK)
-#define SOF_MAX98357A_SPEAKER_AMP_PRESENT      BIT(10)
+#define SOF_CS42L42_DAILINK_SHIFT              10
+#define SOF_CS42L42_DAILINK_MASK               (GENMASK(24, 10))
+#define SOF_CS42L42_DAILINK(link1, link2, link3, link4, link5) \
+       ((((link1) | ((link2) << 3) | ((link3) << 6) | ((link4) << 9) | ((link5) << 12)) << SOF_CS42L42_DAILINK_SHIFT) & SOF_CS42L42_DAILINK_MASK)
+#define SOF_MAX98357A_SPEAKER_AMP_PRESENT      BIT(25)
+#define SOF_MAX98360A_SPEAKER_AMP_PRESENT      BIT(26)
+
+enum {
+       LINK_NONE = 0,
+       LINK_HP = 1,
+       LINK_SPK = 2,
+       LINK_DMIC = 3,
+       LINK_HDMI = 4,
+};
 
 /* Default: SSP2 */
 static unsigned long sof_cs42l42_quirk = SOF_CS42L42_SSP_CODEC(2);
@@ -122,7 +136,12 @@ static int sof_cs42l42_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
        int clk_freq, ret;
 
-       clk_freq = 3072000; /* BCLK freq */
+       clk_freq = sof_dai_get_bclk(rtd); /* BCLK freq */
+
+       if (clk_freq <= 0) {
+               dev_err(rtd->dev, "get bclk freq failed: %d\n", clk_freq);
+               return -EINVAL;
+       }
 
        /* Configure sysclk for codec */
        ret = snd_soc_dai_set_sysclk(codec_dai, 0,
@@ -259,133 +278,168 @@ static struct snd_soc_dai_link_component dmic_component[] = {
        }
 };
 
-static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
-                                                         int ssp_codec,
-                                                         int ssp_amp,
-                                                         int dmic_be_num,
-                                                         int hdmi_num)
+static int create_spk_amp_dai_links(struct device *dev,
+                                   struct snd_soc_dai_link *links,
+                                   struct snd_soc_dai_link_component *cpus,
+                                   int *id, int ssp_amp)
 {
-       struct snd_soc_dai_link_component *idisp_components;
-       struct snd_soc_dai_link_component *cpus;
-       struct snd_soc_dai_link *links;
-       int i, id = 0;
-
-       links = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link) *
-                            sof_audio_card_cs42l42.num_links, GFP_KERNEL);
-       cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component) *
-                            sof_audio_card_cs42l42.num_links, GFP_KERNEL);
-       if (!links || !cpus)
-               goto devm_err;
+       int ret = 0;
 
        /* speaker amp */
-       if (sof_cs42l42_quirk & SOF_SPEAKER_AMP_PRESENT) {
-               links[id].name = devm_kasprintf(dev, GFP_KERNEL,
-                                               "SSP%d-Codec", ssp_amp);
-               if (!links[id].name)
-                       goto devm_err;
+       if (!(sof_cs42l42_quirk & SOF_SPEAKER_AMP_PRESENT))
+               return 0;
 
-               links[id].id = id;
+       links[*id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec",
+                                        ssp_amp);
+       if (!links[*id].name) {
+               ret = -ENOMEM;
+               goto devm_err;
+       }
 
-               if (sof_cs42l42_quirk & SOF_MAX98357A_SPEAKER_AMP_PRESENT) {
-                       max_98357a_dai_link(&links[id]);
-               } else {
-                       dev_err(dev, "no amp defined\n");
-                       goto devm_err;
-               }
+       links[*id].id = *id;
 
-               links[id].platforms = platform_component;
-               links[id].num_platforms = ARRAY_SIZE(platform_component);
-               links[id].dpcm_playback = 1;
-               links[id].no_pcm = 1;
-               links[id].cpus = &cpus[id];
-               links[id].num_cpus = 1;
-
-               links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
-                                                         "SSP%d Pin",
-                                                         ssp_amp);
-               if (!links[id].cpus->dai_name)
-                       goto devm_err;
+       if (sof_cs42l42_quirk & SOF_MAX98357A_SPEAKER_AMP_PRESENT) {
+               max_98357a_dai_link(&links[*id]);
+       } else if (sof_cs42l42_quirk & SOF_MAX98360A_SPEAKER_AMP_PRESENT) {
+               max_98360a_dai_link(&links[*id]);
+       } else {
+               dev_err(dev, "no amp defined\n");
+               ret = -EINVAL;
+               goto devm_err;
+       }
 
-               id++;
+       links[*id].platforms = platform_component;
+       links[*id].num_platforms = ARRAY_SIZE(platform_component);
+       links[*id].dpcm_playback = 1;
+       links[*id].no_pcm = 1;
+       links[*id].cpus = &cpus[*id];
+       links[*id].num_cpus = 1;
+
+       links[*id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+                                                  "SSP%d Pin", ssp_amp);
+       if (!links[*id].cpus->dai_name) {
+               ret = -ENOMEM;
+               goto devm_err;
        }
 
+       (*id)++;
+
+devm_err:
+       return ret;
+}
+
+static int create_hp_codec_dai_links(struct device *dev,
+                                    struct snd_soc_dai_link *links,
+                                    struct snd_soc_dai_link_component *cpus,
+                                    int *id, int ssp_codec)
+{
        /* codec SSP */
-       links[id].name = devm_kasprintf(dev, GFP_KERNEL,
-                                       "SSP%d-Codec", ssp_codec);
-       if (!links[id].name)
+       links[*id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec",
+                                        ssp_codec);
+       if (!links[*id].name)
                goto devm_err;
 
-       links[id].id = id;
-       links[id].codecs = cs42l42_component;
-       links[id].num_codecs = ARRAY_SIZE(cs42l42_component);
-       links[id].platforms = platform_component;
-       links[id].num_platforms = ARRAY_SIZE(platform_component);
-       links[id].init = sof_cs42l42_init;
-       links[id].exit = sof_cs42l42_exit;
-       links[id].ops = &sof_cs42l42_ops;
-       links[id].dpcm_playback = 1;
-       links[id].dpcm_capture = 1;
-       links[id].no_pcm = 1;
-       links[id].cpus = &cpus[id];
-       links[id].num_cpus = 1;
-
-       links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
-                                                 "SSP%d Pin",
-                                                 ssp_codec);
-       if (!links[id].cpus->dai_name)
+       links[*id].id = *id;
+       links[*id].codecs = cs42l42_component;
+       links[*id].num_codecs = ARRAY_SIZE(cs42l42_component);
+       links[*id].platforms = platform_component;
+       links[*id].num_platforms = ARRAY_SIZE(platform_component);
+       links[*id].init = sof_cs42l42_init;
+       links[*id].exit = sof_cs42l42_exit;
+       links[*id].ops = &sof_cs42l42_ops;
+       links[*id].dpcm_playback = 1;
+       links[*id].dpcm_capture = 1;
+       links[*id].no_pcm = 1;
+       links[*id].cpus = &cpus[*id];
+       links[*id].num_cpus = 1;
+
+       links[*id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+                                                  "SSP%d Pin",
+                                                  ssp_codec);
+       if (!links[*id].cpus->dai_name)
                goto devm_err;
 
-       id++;
+       (*id)++;
+
+       return 0;
+
+devm_err:
+       return -ENOMEM;
+}
+
+static int create_dmic_dai_links(struct device *dev,
+                                struct snd_soc_dai_link *links,
+                                struct snd_soc_dai_link_component *cpus,
+                                int *id, int dmic_be_num)
+{
+       int i;
 
        /* dmic */
-       if (dmic_be_num > 0) {
-               /* at least we have dmic01 */
-               links[id].name = "dmic01";
-               links[id].cpus = &cpus[id];
-               links[id].cpus->dai_name = "DMIC01 Pin";
-               links[id].init = dmic_init;
-               if (dmic_be_num > 1) {
-                       /* set up 2 BE links at most */
-                       links[id + 1].name = "dmic16k";
-                       links[id + 1].cpus = &cpus[id + 1];
-                       links[id + 1].cpus->dai_name = "DMIC16k Pin";
-                       dmic_be_num = 2;
-               }
+       if (dmic_be_num <= 0)
+               return 0;
+
+       /* at least we have dmic01 */
+       links[*id].name = "dmic01";
+       links[*id].cpus = &cpus[*id];
+       links[*id].cpus->dai_name = "DMIC01 Pin";
+       links[*id].init = dmic_init;
+       if (dmic_be_num > 1) {
+               /* set up 2 BE links at most */
+               links[*id + 1].name = "dmic16k";
+               links[*id + 1].cpus = &cpus[*id + 1];
+               links[*id + 1].cpus->dai_name = "DMIC16k Pin";
+               dmic_be_num = 2;
        }
 
        for (i = 0; i < dmic_be_num; i++) {
-               links[id].id = id;
-               links[id].num_cpus = 1;
-               links[id].codecs = dmic_component;
-               links[id].num_codecs = ARRAY_SIZE(dmic_component);
-               links[id].platforms = platform_component;
-               links[id].num_platforms = ARRAY_SIZE(platform_component);
-               links[id].ignore_suspend = 1;
-               links[id].dpcm_capture = 1;
-               links[id].no_pcm = 1;
-               id++;
+               links[*id].id = *id;
+               links[*id].num_cpus = 1;
+               links[*id].codecs = dmic_component;
+               links[*id].num_codecs = ARRAY_SIZE(dmic_component);
+               links[*id].platforms = platform_component;
+               links[*id].num_platforms = ARRAY_SIZE(platform_component);
+               links[*id].ignore_suspend = 1;
+               links[*id].dpcm_capture = 1;
+               links[*id].no_pcm = 1;
+
+               (*id)++;
        }
 
+       return 0;
+}
+
+static int create_hdmi_dai_links(struct device *dev,
+                                struct snd_soc_dai_link *links,
+                                struct snd_soc_dai_link_component *cpus,
+                                int *id, int hdmi_num)
+{
+       struct snd_soc_dai_link_component *idisp_components;
+       int i;
+
        /* HDMI */
-       if (hdmi_num > 0) {
-               idisp_components = devm_kzalloc(dev,
-                                               sizeof(struct snd_soc_dai_link_component) *
-                                               hdmi_num, GFP_KERNEL);
-               if (!idisp_components)
-                       goto devm_err;
-       }
+       if (hdmi_num <= 0)
+               return 0;
+
+       idisp_components = devm_kzalloc(dev,
+                                       sizeof(struct snd_soc_dai_link_component) *
+                                       hdmi_num, GFP_KERNEL);
+       if (!idisp_components)
+               goto devm_err;
+
        for (i = 1; i <= hdmi_num; i++) {
-               links[id].name = devm_kasprintf(dev, GFP_KERNEL,
-                                               "iDisp%d", i);
-               if (!links[id].name)
+               links[*id].name = devm_kasprintf(dev, GFP_KERNEL,
+                                                "iDisp%d", i);
+               if (!links[*id].name)
                        goto devm_err;
 
-               links[id].id = id;
-               links[id].cpus = &cpus[id];
-               links[id].num_cpus = 1;
-               links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
-                                                         "iDisp%d Pin", i);
-               if (!links[id].cpus->dai_name)
+               links[*id].id = *id;
+               links[*id].cpus = &cpus[*id];
+               links[*id].num_cpus = 1;
+               links[*id].cpus->dai_name = devm_kasprintf(dev,
+                                                          GFP_KERNEL,
+                                                          "iDisp%d Pin",
+                                                          i);
+               if (!links[*id].cpus->dai_name)
                        goto devm_err;
 
                idisp_components[i - 1].name = "ehdaudio0D2";
@@ -396,14 +450,86 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
                if (!idisp_components[i - 1].dai_name)
                        goto devm_err;
 
-               links[id].codecs = &idisp_components[i - 1];
-               links[id].num_codecs = 1;
-               links[id].platforms = platform_component;
-               links[id].num_platforms = ARRAY_SIZE(platform_component);
-               links[id].init = sof_hdmi_init;
-               links[id].dpcm_playback = 1;
-               links[id].no_pcm = 1;
-               id++;
+               links[*id].codecs = &idisp_components[i - 1];
+               links[*id].num_codecs = 1;
+               links[*id].platforms = platform_component;
+               links[*id].num_platforms = ARRAY_SIZE(platform_component);
+               links[*id].init = sof_hdmi_init;
+               links[*id].dpcm_playback = 1;
+               links[*id].no_pcm = 1;
+
+               (*id)++;
+       }
+
+       return 0;
+
+devm_err:
+       return -ENOMEM;
+}
+
+static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
+                                                         int ssp_codec,
+                                                         int ssp_amp,
+                                                         int dmic_be_num,
+                                                         int hdmi_num)
+{
+       struct snd_soc_dai_link_component *cpus;
+       struct snd_soc_dai_link *links;
+       int ret, id = 0, link_seq;
+
+       links = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link) *
+                            sof_audio_card_cs42l42.num_links, GFP_KERNEL);
+       cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component) *
+                            sof_audio_card_cs42l42.num_links, GFP_KERNEL);
+       if (!links || !cpus)
+               goto devm_err;
+
+       link_seq = (sof_cs42l42_quirk & SOF_CS42L42_DAILINK_MASK) >> SOF_CS42L42_DAILINK_SHIFT;
+
+       while (link_seq) {
+               int link_type = link_seq & 0x07;
+
+               switch (link_type) {
+               case LINK_HP:
+                       ret = create_hp_codec_dai_links(dev, links, cpus, &id, ssp_codec);
+                       if (ret < 0) {
+                               dev_err(dev, "fail to create hp codec dai links, ret %d\n",
+                                       ret);
+                               goto devm_err;
+                       }
+                       break;
+               case LINK_SPK:
+                       ret = create_spk_amp_dai_links(dev, links, cpus, &id, ssp_amp);
+                       if (ret < 0) {
+                               dev_err(dev, "fail to create spk amp dai links, ret %d\n",
+                                       ret);
+                               goto devm_err;
+                       }
+                       break;
+               case LINK_DMIC:
+                       ret = create_dmic_dai_links(dev, links, cpus, &id, dmic_be_num);
+                       if (ret < 0) {
+                               dev_err(dev, "fail to create dmic dai links, ret %d\n",
+                                       ret);
+                               goto devm_err;
+                       }
+                       break;
+               case LINK_HDMI:
+                       ret = create_hdmi_dai_links(dev, links, cpus, &id, hdmi_num);
+                       if (ret < 0) {
+                               dev_err(dev, "fail to create hdmi dai links, ret %d\n",
+                                       ret);
+                               goto devm_err;
+                       }
+                       break;
+               case LINK_NONE:
+                       /* caught here if it's not used as terminator in macro */
+               default:
+                       dev_err(dev, "invalid link type %d\n", link_type);
+                       goto devm_err;
+               }
+
+               link_seq >>= 3;
        }
 
        return links;
@@ -484,7 +610,16 @@ static const struct platform_device_id board_ids[] = {
                .driver_data = (kernel_ulong_t)(SOF_CS42L42_SSP_CODEC(2) |
                                        SOF_SPEAKER_AMP_PRESENT |
                                        SOF_MAX98357A_SPEAKER_AMP_PRESENT |
-                                       SOF_CS42L42_SSP_AMP(1)),
+                                       SOF_CS42L42_SSP_AMP(1)) |
+                                       SOF_CS42L42_DAILINK(LINK_SPK, LINK_HP, LINK_DMIC, LINK_HDMI, LINK_NONE),
+       },
+       {
+               .name = "jsl_cs4242_mx98360a",
+               .driver_data = (kernel_ulong_t)(SOF_CS42L42_SSP_CODEC(0) |
+                                       SOF_SPEAKER_AMP_PRESENT |
+                                       SOF_MAX98360A_SPEAKER_AMP_PRESENT |
+                                       SOF_CS42L42_SSP_AMP(1)) |
+                                       SOF_CS42L42_DAILINK(LINK_HP, LINK_DMIC, LINK_HDMI, LINK_SPK, LINK_NONE),
        },
        { }
 };