linux/sound/soc/amd/acp-da7219-max98357a.c
<<
>>
Prefs
   1/*
   2 * Machine driver for AMD ACP Audio engine using DA7219 & MAX98357 codec
   3 *
   4 * Copyright 2017 Advanced Micro Devices, Inc.
   5 *
   6 * Permission is hereby granted, free of charge, to any person obtaining a
   7 * copy of this software and associated documentation files (the "Software"),
   8 * to deal in the Software without restriction, including without limitation
   9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  10 * and/or sell copies of the Software, and to permit persons to whom the
  11 * Software is furnished to do so, subject to the following conditions:
  12 *
  13 * The above copyright notice and this permission notice shall be included in
  14 * all copies or substantial portions of the Software.
  15 *
  16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
  20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  22 * OTHER DEALINGS IN THE SOFTWARE.
  23 *
  24 */
  25
  26#include <sound/core.h>
  27#include <sound/soc.h>
  28#include <sound/pcm.h>
  29#include <sound/pcm_params.h>
  30#include <sound/soc-dapm.h>
  31#include <sound/jack.h>
  32#include <linux/clk.h>
  33#include <linux/gpio.h>
  34#include <linux/module.h>
  35#include <linux/regulator/machine.h>
  36#include <linux/regulator/driver.h>
  37#include <linux/i2c.h>
  38#include <linux/input.h>
  39#include <linux/acpi.h>
  40
  41#include "acp.h"
  42#include "../codecs/da7219.h"
  43#include "../codecs/da7219-aad.h"
  44
  45#define CZ_PLAT_CLK 48000000
  46#define DUAL_CHANNEL            2
  47
  48static struct snd_soc_jack cz_jack;
  49static struct clk *da7219_dai_wclk;
  50static struct clk *da7219_dai_bclk;
  51extern bool bt_uart_enable;
  52
  53static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd)
  54{
  55        int ret;
  56        struct snd_soc_card *card = rtd->card;
  57        struct snd_soc_dai *codec_dai = rtd->codec_dai;
  58        struct snd_soc_component *component = codec_dai->component;
  59
  60        dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
  61
  62        ret = snd_soc_dai_set_sysclk(codec_dai, DA7219_CLKSRC_MCLK,
  63                                     CZ_PLAT_CLK, SND_SOC_CLOCK_IN);
  64        if (ret < 0) {
  65                dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
  66                return ret;
  67        }
  68
  69        ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_PLL,
  70                                  CZ_PLAT_CLK, DA7219_PLL_FREQ_OUT_98304);
  71        if (ret < 0) {
  72                dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
  73                return ret;
  74        }
  75
  76        da7219_dai_wclk = clk_get(component->dev, "da7219-dai-wclk");
  77        da7219_dai_bclk = clk_get(component->dev, "da7219-dai-bclk");
  78
  79        ret = snd_soc_card_jack_new(card, "Headset Jack",
  80                                SND_JACK_HEADSET | SND_JACK_LINEOUT |
  81                                SND_JACK_BTN_0 | SND_JACK_BTN_1 |
  82                                SND_JACK_BTN_2 | SND_JACK_BTN_3,
  83                                &cz_jack, NULL, 0);
  84        if (ret) {
  85                dev_err(card->dev, "HP jack creation failed %d\n", ret);
  86                return ret;
  87        }
  88
  89        snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
  90        snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
  91        snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
  92        snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
  93
  94        da7219_aad_jack_det(component, &cz_jack);
  95
  96        return 0;
  97}
  98
  99static int da7219_clk_enable(struct snd_pcm_substream *substream,
 100                             int wclk_rate, int bclk_rate)
 101{
 102        int ret = 0;
 103        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 104
 105        clk_set_rate(da7219_dai_wclk, wclk_rate);
 106        clk_set_rate(da7219_dai_bclk, bclk_rate);
 107        ret = clk_prepare_enable(da7219_dai_bclk);
 108        if (ret < 0) {
 109                dev_err(rtd->dev, "can't enable master clock %d\n", ret);
 110                return ret;
 111        }
 112
 113        return ret;
 114}
 115
 116static void da7219_clk_disable(void)
 117{
 118        clk_disable_unprepare(da7219_dai_bclk);
 119}
 120
 121static const unsigned int channels[] = {
 122        DUAL_CHANNEL,
 123};
 124
 125static const unsigned int rates[] = {
 126        48000,
 127};
 128
 129static const struct snd_pcm_hw_constraint_list constraints_rates = {
 130        .count = ARRAY_SIZE(rates),
 131        .list  = rates,
 132        .mask = 0,
 133};
 134
 135static const struct snd_pcm_hw_constraint_list constraints_channels = {
 136        .count = ARRAY_SIZE(channels),
 137        .list = channels,
 138        .mask = 0,
 139};
 140
 141static int cz_da7219_play_startup(struct snd_pcm_substream *substream)
 142{
 143        struct snd_pcm_runtime *runtime = substream->runtime;
 144        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 145        struct snd_soc_card *card = rtd->card;
 146        struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
 147
 148        /*
 149         * On this platform for PCM device we support stereo
 150         */
 151
 152        runtime->hw.channels_max = DUAL_CHANNEL;
 153        snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
 154                                   &constraints_channels);
 155        snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
 156                                   &constraints_rates);
 157
 158        machine->play_i2s_instance = I2S_SP_INSTANCE;
 159        return 0;
 160}
 161
 162static int cz_da7219_cap_startup(struct snd_pcm_substream *substream)
 163{
 164        struct snd_pcm_runtime *runtime = substream->runtime;
 165        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 166        struct snd_soc_card *card = rtd->card;
 167        struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
 168
 169        /*
 170         * On this platform for PCM device we support stereo
 171         */
 172
 173        runtime->hw.channels_max = DUAL_CHANNEL;
 174        snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
 175                                   &constraints_channels);
 176        snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
 177                                   &constraints_rates);
 178
 179        machine->cap_i2s_instance = I2S_SP_INSTANCE;
 180        machine->capture_channel = CAP_CHANNEL1;
 181        return 0;
 182}
 183
 184static int cz_max_startup(struct snd_pcm_substream *substream)
 185{
 186        struct snd_pcm_runtime *runtime = substream->runtime;
 187        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 188        struct snd_soc_card *card = rtd->card;
 189        struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
 190
 191        /*
 192         * On this platform for PCM device we support stereo
 193         */
 194
 195        runtime->hw.channels_max = DUAL_CHANNEL;
 196        snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
 197                                   &constraints_channels);
 198        snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
 199                                   &constraints_rates);
 200
 201        machine->play_i2s_instance = I2S_BT_INSTANCE;
 202        return 0;
 203}
 204
 205static int cz_dmic0_startup(struct snd_pcm_substream *substream)
 206{
 207        struct snd_pcm_runtime *runtime = substream->runtime;
 208        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 209        struct snd_soc_card *card = rtd->card;
 210        struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
 211
 212        /*
 213         * On this platform for PCM device we support stereo
 214         */
 215
 216        runtime->hw.channels_max = DUAL_CHANNEL;
 217        snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
 218                                   &constraints_channels);
 219        snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
 220                                   &constraints_rates);
 221
 222        machine->cap_i2s_instance = I2S_BT_INSTANCE;
 223        return 0;
 224}
 225
 226static int cz_dmic1_startup(struct snd_pcm_substream *substream)
 227{
 228        struct snd_pcm_runtime *runtime = substream->runtime;
 229        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 230        struct snd_soc_card *card = rtd->card;
 231        struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
 232
 233        /*
 234         * On this platform for PCM device we support stereo
 235         */
 236
 237        runtime->hw.channels_max = DUAL_CHANNEL;
 238        snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
 239                                   &constraints_channels);
 240        snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
 241                                   &constraints_rates);
 242
 243        machine->cap_i2s_instance = I2S_SP_INSTANCE;
 244        machine->capture_channel = CAP_CHANNEL0;
 245        return 0;
 246}
 247
 248static int cz_da7219_params(struct snd_pcm_substream *substream,
 249                                      struct snd_pcm_hw_params *params)
 250{
 251        int wclk, bclk;
 252
 253        wclk = params_rate(params);
 254        bclk = wclk * params_channels(params) *
 255                snd_pcm_format_width(params_format(params));
 256        /* ADAU7002 spec: "The ADAU7002 requires a BCLK rate
 257         * that is minimum of 64x the LRCLK sample rate."
 258         * DA7219 is the only clk source so for all codecs
 259         * we have to limit bclk to 64X lrclk.
 260         */
 261        if (bclk < (wclk * 64))
 262                bclk = wclk * 64;
 263        return da7219_clk_enable(substream, wclk, bclk);
 264}
 265
 266static void cz_da7219_shutdown(struct snd_pcm_substream *substream)
 267{
 268        da7219_clk_disable();
 269}
 270
 271static const struct snd_soc_ops cz_da7219_play_ops = {
 272        .startup = cz_da7219_play_startup,
 273        .shutdown = cz_da7219_shutdown,
 274        .hw_params = cz_da7219_params,
 275};
 276
 277static const struct snd_soc_ops cz_da7219_cap_ops = {
 278        .startup = cz_da7219_cap_startup,
 279        .shutdown = cz_da7219_shutdown,
 280        .hw_params = cz_da7219_params,
 281};
 282
 283static const struct snd_soc_ops cz_max_play_ops = {
 284        .startup = cz_max_startup,
 285        .shutdown = cz_da7219_shutdown,
 286        .hw_params = cz_da7219_params,
 287};
 288
 289static const struct snd_soc_ops cz_dmic0_cap_ops = {
 290        .startup = cz_dmic0_startup,
 291        .shutdown = cz_da7219_shutdown,
 292        .hw_params = cz_da7219_params,
 293};
 294
 295static const struct snd_soc_ops cz_dmic1_cap_ops = {
 296        .startup = cz_dmic1_startup,
 297        .shutdown = cz_da7219_shutdown,
 298        .hw_params = cz_da7219_params,
 299};
 300
 301SND_SOC_DAILINK_DEF(designware1,
 302        DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.1.auto")));
 303SND_SOC_DAILINK_DEF(designware2,
 304        DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.2.auto")));
 305SND_SOC_DAILINK_DEF(designware3,
 306        DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.3.auto")));
 307
 308SND_SOC_DAILINK_DEF(dlgs,
 309        DAILINK_COMP_ARRAY(COMP_CODEC("i2c-DLGS7219:00", "da7219-hifi")));
 310SND_SOC_DAILINK_DEF(mx,
 311        DAILINK_COMP_ARRAY(COMP_CODEC("MX98357A:00", "HiFi")));
 312SND_SOC_DAILINK_DEF(adau,
 313        DAILINK_COMP_ARRAY(COMP_CODEC("ADAU7002:00", "adau7002-hifi")));
 314
 315SND_SOC_DAILINK_DEF(platform,
 316        DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_audio_dma.0.auto")));
 317
 318static struct snd_soc_dai_link cz_dai_7219_98357[] = {
 319        {
 320                .name = "amd-da7219-play",
 321                .stream_name = "Playback",
 322                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
 323                                | SND_SOC_DAIFMT_CBM_CFM,
 324                .init = cz_da7219_init,
 325                .dpcm_playback = 1,
 326                .ops = &cz_da7219_play_ops,
 327                SND_SOC_DAILINK_REG(designware1, dlgs, platform),
 328        },
 329        {
 330                .name = "amd-da7219-cap",
 331                .stream_name = "Capture",
 332                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
 333                                | SND_SOC_DAIFMT_CBM_CFM,
 334                .dpcm_capture = 1,
 335                .ops = &cz_da7219_cap_ops,
 336                SND_SOC_DAILINK_REG(designware2, dlgs, platform),
 337        },
 338        {
 339                .name = "amd-max98357-play",
 340                .stream_name = "HiFi Playback",
 341                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
 342                                | SND_SOC_DAIFMT_CBM_CFM,
 343                .dpcm_playback = 1,
 344                .ops = &cz_max_play_ops,
 345                SND_SOC_DAILINK_REG(designware3, mx, platform),
 346        },
 347        {
 348                /* C panel DMIC */
 349                .name = "dmic0",
 350                .stream_name = "DMIC0 Capture",
 351                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
 352                                | SND_SOC_DAIFMT_CBM_CFM,
 353                .dpcm_capture = 1,
 354                .ops = &cz_dmic0_cap_ops,
 355                SND_SOC_DAILINK_REG(designware3, adau, platform),
 356        },
 357        {
 358                /* A/B panel DMIC */
 359                .name = "dmic1",
 360                .stream_name = "DMIC1 Capture",
 361                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
 362                                | SND_SOC_DAIFMT_CBM_CFM,
 363                .dpcm_capture = 1,
 364                .ops = &cz_dmic1_cap_ops,
 365                SND_SOC_DAILINK_REG(designware2, adau, platform),
 366        },
 367};
 368
 369static const struct snd_soc_dapm_widget cz_widgets[] = {
 370        SND_SOC_DAPM_HP("Headphones", NULL),
 371        SND_SOC_DAPM_SPK("Speakers", NULL),
 372        SND_SOC_DAPM_MIC("Headset Mic", NULL),
 373        SND_SOC_DAPM_MIC("Int Mic", NULL),
 374};
 375
 376static const struct snd_soc_dapm_route cz_audio_route[] = {
 377        {"Headphones", NULL, "HPL"},
 378        {"Headphones", NULL, "HPR"},
 379        {"MIC", NULL, "Headset Mic"},
 380        {"Speakers", NULL, "Speaker"},
 381        {"PDM_DAT", NULL, "Int Mic"},
 382};
 383
 384static const struct snd_kcontrol_new cz_mc_controls[] = {
 385        SOC_DAPM_PIN_SWITCH("Headphones"),
 386        SOC_DAPM_PIN_SWITCH("Speakers"),
 387        SOC_DAPM_PIN_SWITCH("Headset Mic"),
 388        SOC_DAPM_PIN_SWITCH("Int Mic"),
 389};
 390
 391static struct snd_soc_card cz_card = {
 392        .name = "acpd7219m98357",
 393        .owner = THIS_MODULE,
 394        .dai_link = cz_dai_7219_98357,
 395        .num_links = ARRAY_SIZE(cz_dai_7219_98357),
 396        .dapm_widgets = cz_widgets,
 397        .num_dapm_widgets = ARRAY_SIZE(cz_widgets),
 398        .dapm_routes = cz_audio_route,
 399        .num_dapm_routes = ARRAY_SIZE(cz_audio_route),
 400        .controls = cz_mc_controls,
 401        .num_controls = ARRAY_SIZE(cz_mc_controls),
 402};
 403
 404static struct regulator_consumer_supply acp_da7219_supplies[] = {
 405        REGULATOR_SUPPLY("VDD", "i2c-DLGS7219:00"),
 406        REGULATOR_SUPPLY("VDDMIC", "i2c-DLGS7219:00"),
 407        REGULATOR_SUPPLY("VDDIO", "i2c-DLGS7219:00"),
 408        REGULATOR_SUPPLY("IOVDD", "ADAU7002:00"),
 409};
 410
 411static struct regulator_init_data acp_da7219_data = {
 412        .constraints = {
 413                .always_on = 1,
 414        },
 415        .num_consumer_supplies = ARRAY_SIZE(acp_da7219_supplies),
 416        .consumer_supplies = acp_da7219_supplies,
 417};
 418
 419static struct regulator_config acp_da7219_cfg = {
 420        .init_data = &acp_da7219_data,
 421};
 422
 423static struct regulator_ops acp_da7219_ops = {
 424};
 425
 426static const struct regulator_desc acp_da7219_desc = {
 427        .name = "reg-fixed-1.8V",
 428        .type = REGULATOR_VOLTAGE,
 429        .owner = THIS_MODULE,
 430        .ops = &acp_da7219_ops,
 431        .fixed_uV = 1800000, /* 1.8V */
 432        .n_voltages = 1,
 433};
 434
 435static int cz_probe(struct platform_device *pdev)
 436{
 437        int ret;
 438        struct snd_soc_card *card;
 439        struct acp_platform_info *machine;
 440        struct regulator_dev *rdev;
 441
 442        acp_da7219_cfg.dev = &pdev->dev;
 443        rdev = devm_regulator_register(&pdev->dev, &acp_da7219_desc,
 444                                       &acp_da7219_cfg);
 445        if (IS_ERR(rdev)) {
 446                dev_err(&pdev->dev, "Failed to register regulator: %d\n",
 447                        (int)PTR_ERR(rdev));
 448                return -EINVAL;
 449        }
 450
 451        machine = devm_kzalloc(&pdev->dev, sizeof(struct acp_platform_info),
 452                               GFP_KERNEL);
 453        if (!machine)
 454                return -ENOMEM;
 455        card = &cz_card;
 456        cz_card.dev = &pdev->dev;
 457        platform_set_drvdata(pdev, card);
 458        snd_soc_card_set_drvdata(card, machine);
 459        ret = devm_snd_soc_register_card(&pdev->dev, &cz_card);
 460        if (ret) {
 461                dev_err(&pdev->dev,
 462                                "devm_snd_soc_register_card(%s) failed: %d\n",
 463                                cz_card.name, ret);
 464                return ret;
 465        }
 466        bt_uart_enable = !device_property_read_bool(&pdev->dev,
 467                                                    "bt-pad-enable");
 468        return 0;
 469}
 470
 471static const struct acpi_device_id cz_audio_acpi_match[] = {
 472        { "AMD7219", 0 },
 473        {},
 474};
 475MODULE_DEVICE_TABLE(acpi, cz_audio_acpi_match);
 476
 477static struct platform_driver cz_pcm_driver = {
 478        .driver = {
 479                .name = "cz-da7219-max98357a",
 480                .acpi_match_table = ACPI_PTR(cz_audio_acpi_match),
 481                .pm = &snd_soc_pm_ops,
 482        },
 483        .probe = cz_probe,
 484};
 485
 486module_platform_driver(cz_pcm_driver);
 487
 488MODULE_AUTHOR("akshu.agrawal@amd.com");
 489MODULE_DESCRIPTION("DA7219 & MAX98357A audio support");
 490MODULE_LICENSE("GPL v2");
 491