linux/sound/soc/intel/boards/sof_pcm512x.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2// Copyright(c) 2018-2020 Intel Corporation.
   3
   4/*
   5 * Intel SOF Machine Driver for Intel platforms with TI PCM512x codec,
   6 * e.g. Up or Up2 with Hifiberry DAC+ HAT
   7 */
   8#include <linux/clk.h>
   9#include <linux/dmi.h>
  10#include <linux/i2c.h>
  11#include <linux/input.h>
  12#include <linux/module.h>
  13#include <linux/platform_device.h>
  14#include <linux/types.h>
  15#include <sound/core.h>
  16#include <sound/jack.h>
  17#include <sound/pcm.h>
  18#include <sound/pcm_params.h>
  19#include <sound/soc.h>
  20#include <sound/soc-acpi.h>
  21#include "../../codecs/pcm512x.h"
  22#include "../common/soc-intel-quirks.h"
  23#include "hda_dsp_common.h"
  24
  25#define NAME_SIZE 32
  26
  27#define SOF_PCM512X_SSP_CODEC(quirk)            ((quirk) & GENMASK(3, 0))
  28#define SOF_PCM512X_SSP_CODEC_MASK                      (GENMASK(3, 0))
  29
  30#define IDISP_CODEC_MASK        0x4
  31
  32/* Default: SSP5 */
  33static unsigned long sof_pcm512x_quirk = SOF_PCM512X_SSP_CODEC(5);
  34
  35static bool is_legacy_cpu;
  36
  37struct sof_hdmi_pcm {
  38        struct list_head head;
  39        struct snd_soc_dai *codec_dai;
  40        int device;
  41};
  42
  43struct sof_card_private {
  44        struct list_head hdmi_pcm_list;
  45        bool idisp_codec;
  46};
  47
  48static int sof_pcm512x_quirk_cb(const struct dmi_system_id *id)
  49{
  50        sof_pcm512x_quirk = (unsigned long)id->driver_data;
  51        return 1;
  52}
  53
  54static const struct dmi_system_id sof_pcm512x_quirk_table[] = {
  55        {
  56                .callback = sof_pcm512x_quirk_cb,
  57                .matches = {
  58                        DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
  59                        DMI_MATCH(DMI_PRODUCT_NAME, "UP-CHT01"),
  60                },
  61                .driver_data = (void *)(SOF_PCM512X_SSP_CODEC(2)),
  62        },
  63        {}
  64};
  65
  66static int sof_hdmi_init(struct snd_soc_pcm_runtime *rtd)
  67{
  68        struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
  69        struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0);
  70        struct sof_hdmi_pcm *pcm;
  71
  72        pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
  73        if (!pcm)
  74                return -ENOMEM;
  75
  76        /* dai_link id is 1:1 mapped to the PCM device */
  77        pcm->device = rtd->dai_link->id;
  78        pcm->codec_dai = dai;
  79
  80        list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
  81
  82        return 0;
  83}
  84
  85static int sof_pcm512x_codec_init(struct snd_soc_pcm_runtime *rtd)
  86{
  87        struct snd_soc_component *codec = asoc_rtd_to_codec(rtd, 0)->component;
  88
  89        snd_soc_component_update_bits(codec, PCM512x_GPIO_EN, 0x08, 0x08);
  90        snd_soc_component_update_bits(codec, PCM512x_GPIO_OUTPUT_4, 0x0f, 0x02);
  91        snd_soc_component_update_bits(codec, PCM512x_GPIO_CONTROL_1,
  92                                      0x08, 0x08);
  93
  94        return 0;
  95}
  96
  97static int aif1_startup(struct snd_pcm_substream *substream)
  98{
  99        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
 100        struct snd_soc_component *codec = asoc_rtd_to_codec(rtd, 0)->component;
 101
 102        snd_soc_component_update_bits(codec, PCM512x_GPIO_CONTROL_1,
 103                                      0x08, 0x08);
 104
 105        return 0;
 106}
 107
 108static void aif1_shutdown(struct snd_pcm_substream *substream)
 109{
 110        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
 111        struct snd_soc_component *codec = asoc_rtd_to_codec(rtd, 0)->component;
 112
 113        snd_soc_component_update_bits(codec, PCM512x_GPIO_CONTROL_1,
 114                                      0x08, 0x00);
 115}
 116
 117static const struct snd_soc_ops sof_pcm512x_ops = {
 118        .startup = aif1_startup,
 119        .shutdown = aif1_shutdown,
 120};
 121
 122static struct snd_soc_dai_link_component platform_component[] = {
 123        {
 124                /* name might be overridden during probe */
 125                .name = "0000:00:1f.3"
 126        }
 127};
 128
 129static int sof_card_late_probe(struct snd_soc_card *card)
 130{
 131        struct sof_card_private *ctx = snd_soc_card_get_drvdata(card);
 132        struct sof_hdmi_pcm *pcm;
 133
 134        /* HDMI is not supported by SOF on Baytrail/CherryTrail */
 135        if (is_legacy_cpu)
 136                return 0;
 137
 138        if (list_empty(&ctx->hdmi_pcm_list))
 139                return -EINVAL;
 140
 141        if (!ctx->idisp_codec)
 142                return 0;
 143
 144        pcm = list_first_entry(&ctx->hdmi_pcm_list, struct sof_hdmi_pcm, head);
 145
 146        return hda_dsp_hdmi_build_controls(card, pcm->codec_dai->component);
 147}
 148
 149static const struct snd_kcontrol_new sof_controls[] = {
 150        SOC_DAPM_PIN_SWITCH("Ext Spk"),
 151};
 152
 153static const struct snd_soc_dapm_widget sof_widgets[] = {
 154        SND_SOC_DAPM_SPK("Ext Spk", NULL),
 155};
 156
 157static const struct snd_soc_dapm_widget dmic_widgets[] = {
 158        SND_SOC_DAPM_MIC("SoC DMIC", NULL),
 159};
 160
 161static const struct snd_soc_dapm_route sof_map[] = {
 162        /* Speaker */
 163        {"Ext Spk", NULL, "OUTR"},
 164        {"Ext Spk", NULL, "OUTL"},
 165};
 166
 167static const struct snd_soc_dapm_route dmic_map[] = {
 168        /* digital mics */
 169        {"DMic", NULL, "SoC DMIC"},
 170};
 171
 172static int dmic_init(struct snd_soc_pcm_runtime *rtd)
 173{
 174        struct snd_soc_card *card = rtd->card;
 175        int ret;
 176
 177        ret = snd_soc_dapm_new_controls(&card->dapm, dmic_widgets,
 178                                        ARRAY_SIZE(dmic_widgets));
 179        if (ret) {
 180                dev_err(card->dev, "DMic widget addition failed: %d\n", ret);
 181                /* Don't need to add routes if widget addition failed */
 182                return ret;
 183        }
 184
 185        ret = snd_soc_dapm_add_routes(&card->dapm, dmic_map,
 186                                      ARRAY_SIZE(dmic_map));
 187
 188        if (ret)
 189                dev_err(card->dev, "DMic map addition failed: %d\n", ret);
 190
 191        return ret;
 192}
 193
 194/* sof audio machine driver for pcm512x codec */
 195static struct snd_soc_card sof_audio_card_pcm512x = {
 196        .name = "pcm512x",
 197        .owner = THIS_MODULE,
 198        .controls = sof_controls,
 199        .num_controls = ARRAY_SIZE(sof_controls),
 200        .dapm_widgets = sof_widgets,
 201        .num_dapm_widgets = ARRAY_SIZE(sof_widgets),
 202        .dapm_routes = sof_map,
 203        .num_dapm_routes = ARRAY_SIZE(sof_map),
 204        .fully_routed = true,
 205        .late_probe = sof_card_late_probe,
 206};
 207
 208SND_SOC_DAILINK_DEF(pcm512x_component,
 209        DAILINK_COMP_ARRAY(COMP_CODEC("i2c-104C5122:00", "pcm512x-hifi")));
 210SND_SOC_DAILINK_DEF(dmic_component,
 211        DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi")));
 212
 213static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
 214                                                          int ssp_codec,
 215                                                          int dmic_be_num,
 216                                                          int hdmi_num,
 217                                                          bool idisp_codec)
 218{
 219        struct snd_soc_dai_link_component *idisp_components;
 220        struct snd_soc_dai_link_component *cpus;
 221        struct snd_soc_dai_link *links;
 222        int i, id = 0;
 223
 224        links = devm_kcalloc(dev, sof_audio_card_pcm512x.num_links,
 225                        sizeof(struct snd_soc_dai_link), GFP_KERNEL);
 226        cpus = devm_kcalloc(dev, sof_audio_card_pcm512x.num_links,
 227                        sizeof(struct snd_soc_dai_link_component), GFP_KERNEL);
 228        if (!links || !cpus)
 229                goto devm_err;
 230
 231        /* codec SSP */
 232        links[id].name = devm_kasprintf(dev, GFP_KERNEL,
 233                                        "SSP%d-Codec", ssp_codec);
 234        if (!links[id].name)
 235                goto devm_err;
 236
 237        links[id].id = id;
 238        links[id].codecs = pcm512x_component;
 239        links[id].num_codecs = ARRAY_SIZE(pcm512x_component);
 240        links[id].platforms = platform_component;
 241        links[id].num_platforms = ARRAY_SIZE(platform_component);
 242        links[id].init = sof_pcm512x_codec_init;
 243        links[id].ops = &sof_pcm512x_ops;
 244        links[id].dpcm_playback = 1;
 245        /*
 246         * capture only supported with specific versions of the Hifiberry DAC+
 247         * links[id].dpcm_capture = 1;
 248         */
 249        links[id].no_pcm = 1;
 250        links[id].cpus = &cpus[id];
 251        links[id].num_cpus = 1;
 252        if (is_legacy_cpu) {
 253                links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
 254                                                          "ssp%d-port",
 255                                                          ssp_codec);
 256                if (!links[id].cpus->dai_name)
 257                        goto devm_err;
 258        } else {
 259                links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
 260                                                          "SSP%d Pin",
 261                                                          ssp_codec);
 262                if (!links[id].cpus->dai_name)
 263                        goto devm_err;
 264        }
 265        id++;
 266
 267        /* dmic */
 268        if (dmic_be_num > 0) {
 269                /* at least we have dmic01 */
 270                links[id].name = "dmic01";
 271                links[id].cpus = &cpus[id];
 272                links[id].cpus->dai_name = "DMIC01 Pin";
 273                links[id].init = dmic_init;
 274                if (dmic_be_num > 1) {
 275                        /* set up 2 BE links at most */
 276                        links[id + 1].name = "dmic16k";
 277                        links[id + 1].cpus = &cpus[id + 1];
 278                        links[id + 1].cpus->dai_name = "DMIC16k Pin";
 279                        dmic_be_num = 2;
 280                }
 281        }
 282
 283        for (i = 0; i < dmic_be_num; i++) {
 284                links[id].id = id;
 285                links[id].num_cpus = 1;
 286                links[id].codecs = dmic_component;
 287                links[id].num_codecs = ARRAY_SIZE(dmic_component);
 288                links[id].platforms = platform_component;
 289                links[id].num_platforms = ARRAY_SIZE(platform_component);
 290                links[id].ignore_suspend = 1;
 291                links[id].dpcm_capture = 1;
 292                links[id].no_pcm = 1;
 293                id++;
 294        }
 295
 296        /* HDMI */
 297        if (hdmi_num > 0) {
 298                idisp_components = devm_kcalloc(dev, hdmi_num,
 299                                sizeof(struct snd_soc_dai_link_component),
 300                                GFP_KERNEL);
 301                if (!idisp_components)
 302                        goto devm_err;
 303        }
 304        for (i = 1; i <= hdmi_num; i++) {
 305                links[id].name = devm_kasprintf(dev, GFP_KERNEL,
 306                                                "iDisp%d", i);
 307                if (!links[id].name)
 308                        goto devm_err;
 309
 310                links[id].id = id;
 311                links[id].cpus = &cpus[id];
 312                links[id].num_cpus = 1;
 313                links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
 314                                                          "iDisp%d Pin", i);
 315                if (!links[id].cpus->dai_name)
 316                        goto devm_err;
 317
 318                /*
 319                 * topology cannot be loaded if codec is missing, so
 320                 * use the dummy codec if needed
 321                 */
 322                if (idisp_codec) {
 323                        idisp_components[i - 1].name = "ehdaudio0D2";
 324                        idisp_components[i - 1].dai_name =
 325                                devm_kasprintf(dev, GFP_KERNEL,
 326                                               "intel-hdmi-hifi%d", i);
 327                } else {
 328                        idisp_components[i - 1].name = "snd-soc-dummy";
 329                        idisp_components[i - 1].dai_name = "snd-soc-dummy-dai";
 330                }
 331                if (!idisp_components[i - 1].dai_name)
 332                        goto devm_err;
 333
 334                links[id].codecs = &idisp_components[i - 1];
 335                links[id].num_codecs = 1;
 336                links[id].platforms = platform_component;
 337                links[id].num_platforms = ARRAY_SIZE(platform_component);
 338                links[id].init = sof_hdmi_init;
 339                links[id].dpcm_playback = 1;
 340                links[id].no_pcm = 1;
 341                id++;
 342        }
 343
 344        return links;
 345devm_err:
 346        return NULL;
 347}
 348
 349static int sof_audio_probe(struct platform_device *pdev)
 350{
 351        struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;
 352        struct snd_soc_dai_link *dai_links;
 353        struct sof_card_private *ctx;
 354        int dmic_be_num, hdmi_num;
 355        int ret, ssp_codec;
 356
 357        ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
 358        if (!ctx)
 359                return -ENOMEM;
 360
 361        hdmi_num = 0;
 362        if (soc_intel_is_byt() || soc_intel_is_cht()) {
 363                is_legacy_cpu = true;
 364                dmic_be_num = 0;
 365                /* default quirk for legacy cpu */
 366                sof_pcm512x_quirk = SOF_PCM512X_SSP_CODEC(2);
 367        } else {
 368                dmic_be_num = 2;
 369                if (mach->mach_params.common_hdmi_codec_drv &&
 370                    (mach->mach_params.codec_mask & IDISP_CODEC_MASK))
 371                        ctx->idisp_codec = true;
 372
 373                /* links are always present in topology */
 374                hdmi_num = 3;
 375        }
 376
 377        dmi_check_system(sof_pcm512x_quirk_table);
 378
 379        dev_dbg(&pdev->dev, "sof_pcm512x_quirk = %lx\n", sof_pcm512x_quirk);
 380
 381        ssp_codec = sof_pcm512x_quirk & SOF_PCM512X_SSP_CODEC_MASK;
 382
 383        /* compute number of dai links */
 384        sof_audio_card_pcm512x.num_links = 1 + dmic_be_num + hdmi_num;
 385
 386        dai_links = sof_card_dai_links_create(&pdev->dev, ssp_codec,
 387                                              dmic_be_num, hdmi_num,
 388                                              ctx->idisp_codec);
 389        if (!dai_links)
 390                return -ENOMEM;
 391
 392        sof_audio_card_pcm512x.dai_link = dai_links;
 393
 394        INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
 395
 396        sof_audio_card_pcm512x.dev = &pdev->dev;
 397
 398        /* set platform name for each dailink */
 399        ret = snd_soc_fixup_dai_links_platform_name(&sof_audio_card_pcm512x,
 400                                                    mach->mach_params.platform);
 401        if (ret)
 402                return ret;
 403
 404        snd_soc_card_set_drvdata(&sof_audio_card_pcm512x, ctx);
 405
 406        return devm_snd_soc_register_card(&pdev->dev,
 407                                          &sof_audio_card_pcm512x);
 408}
 409
 410static int sof_pcm512x_remove(struct platform_device *pdev)
 411{
 412        struct snd_soc_card *card = platform_get_drvdata(pdev);
 413        struct snd_soc_component *component = NULL;
 414
 415        for_each_card_components(card, component) {
 416                if (!strcmp(component->name, pcm512x_component[0].name)) {
 417                        snd_soc_component_set_jack(component, NULL, NULL);
 418                        break;
 419                }
 420        }
 421
 422        return 0;
 423}
 424
 425static struct platform_driver sof_audio = {
 426        .probe = sof_audio_probe,
 427        .remove = sof_pcm512x_remove,
 428        .driver = {
 429                .name = "sof_pcm512x",
 430                .pm = &snd_soc_pm_ops,
 431        },
 432};
 433module_platform_driver(sof_audio)
 434
 435MODULE_DESCRIPTION("ASoC Intel(R) SOF + PCM512x Machine driver");
 436MODULE_AUTHOR("Pierre-Louis Bossart");
 437MODULE_LICENSE("GPL v2");
 438MODULE_ALIAS("platform:sof_pcm512x");
 439MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);
 440