linux/sound/soc/tegra/tegra_rt5640.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3* tegra_rt5640.c - Tegra machine ASoC driver for boards using RT5640 codec.
   4 *
   5 * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
   6 *
   7 * Based on code copyright/by:
   8 *
   9 * Copyright (C) 2010-2012 - NVIDIA, Inc.
  10 * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.lauchpad.net>
  11 * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd.
  12 * Copyright 2007 Wolfson Microelectronics PLC.
  13 */
  14
  15#include <linux/module.h>
  16#include <linux/platform_device.h>
  17#include <linux/slab.h>
  18#include <linux/gpio.h>
  19#include <linux/of_gpio.h>
  20
  21#include <sound/core.h>
  22#include <sound/jack.h>
  23#include <sound/pcm.h>
  24#include <sound/pcm_params.h>
  25#include <sound/soc.h>
  26
  27#include "../codecs/rt5640.h"
  28
  29#include "tegra_asoc_utils.h"
  30
  31#define DRV_NAME "tegra-snd-rt5640"
  32
  33struct tegra_rt5640 {
  34        struct tegra_asoc_utils_data util_data;
  35        int gpio_hp_det;
  36        enum of_gpio_flags gpio_hp_det_flags;
  37};
  38
  39static int tegra_rt5640_asoc_hw_params(struct snd_pcm_substream *substream,
  40                                        struct snd_pcm_hw_params *params)
  41{
  42        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  43        struct snd_soc_dai *codec_dai = rtd->codec_dai;
  44        struct snd_soc_card *card = rtd->card;
  45        struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card);
  46        int srate, mclk;
  47        int err;
  48
  49        srate = params_rate(params);
  50        mclk = 256 * srate;
  51
  52        err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
  53        if (err < 0) {
  54                dev_err(card->dev, "Can't configure clocks\n");
  55                return err;
  56        }
  57
  58        err = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_MCLK, mclk,
  59                                        SND_SOC_CLOCK_IN);
  60        if (err < 0) {
  61                dev_err(card->dev, "codec_dai clock not set\n");
  62                return err;
  63        }
  64
  65        return 0;
  66}
  67
  68static const struct snd_soc_ops tegra_rt5640_ops = {
  69        .hw_params = tegra_rt5640_asoc_hw_params,
  70};
  71
  72static struct snd_soc_jack tegra_rt5640_hp_jack;
  73
  74static struct snd_soc_jack_pin tegra_rt5640_hp_jack_pins[] = {
  75        {
  76                .pin = "Headphones",
  77                .mask = SND_JACK_HEADPHONE,
  78        },
  79};
  80
  81static struct snd_soc_jack_gpio tegra_rt5640_hp_jack_gpio = {
  82        .name = "Headphone detection",
  83        .report = SND_JACK_HEADPHONE,
  84        .debounce_time = 150,
  85        .invert = 1,
  86};
  87
  88static const struct snd_soc_dapm_widget tegra_rt5640_dapm_widgets[] = {
  89        SND_SOC_DAPM_HP("Headphones", NULL),
  90        SND_SOC_DAPM_SPK("Speakers", NULL),
  91        SND_SOC_DAPM_MIC("Mic Jack", NULL),
  92};
  93
  94static const struct snd_kcontrol_new tegra_rt5640_controls[] = {
  95        SOC_DAPM_PIN_SWITCH("Speakers"),
  96};
  97
  98static int tegra_rt5640_asoc_init(struct snd_soc_pcm_runtime *rtd)
  99{
 100        struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(rtd->card);
 101
 102        snd_soc_card_jack_new(rtd->card, "Headphones", SND_JACK_HEADPHONE,
 103                              &tegra_rt5640_hp_jack, tegra_rt5640_hp_jack_pins,
 104                              ARRAY_SIZE(tegra_rt5640_hp_jack_pins));
 105
 106        if (gpio_is_valid(machine->gpio_hp_det)) {
 107                tegra_rt5640_hp_jack_gpio.gpio = machine->gpio_hp_det;
 108                tegra_rt5640_hp_jack_gpio.invert =
 109                        !!(machine->gpio_hp_det_flags & OF_GPIO_ACTIVE_LOW);
 110                snd_soc_jack_add_gpios(&tegra_rt5640_hp_jack,
 111                                                1,
 112                                                &tegra_rt5640_hp_jack_gpio);
 113        }
 114
 115        return 0;
 116}
 117
 118SND_SOC_DAILINK_DEFS(aif1,
 119        DAILINK_COMP_ARRAY(COMP_EMPTY()),
 120        DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5640-aif1")),
 121        DAILINK_COMP_ARRAY(COMP_EMPTY()));
 122
 123static struct snd_soc_dai_link tegra_rt5640_dai = {
 124        .name = "RT5640",
 125        .stream_name = "RT5640 PCM",
 126        .init = tegra_rt5640_asoc_init,
 127        .ops = &tegra_rt5640_ops,
 128        .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 129                        SND_SOC_DAIFMT_CBS_CFS,
 130        SND_SOC_DAILINK_REG(aif1),
 131};
 132
 133static struct snd_soc_card snd_soc_tegra_rt5640 = {
 134        .name = "tegra-rt5640",
 135        .owner = THIS_MODULE,
 136        .dai_link = &tegra_rt5640_dai,
 137        .num_links = 1,
 138        .controls = tegra_rt5640_controls,
 139        .num_controls = ARRAY_SIZE(tegra_rt5640_controls),
 140        .dapm_widgets = tegra_rt5640_dapm_widgets,
 141        .num_dapm_widgets = ARRAY_SIZE(tegra_rt5640_dapm_widgets),
 142        .fully_routed = true,
 143};
 144
 145static int tegra_rt5640_probe(struct platform_device *pdev)
 146{
 147        struct device_node *np = pdev->dev.of_node;
 148        struct snd_soc_card *card = &snd_soc_tegra_rt5640;
 149        struct tegra_rt5640 *machine;
 150        int ret;
 151
 152        machine = devm_kzalloc(&pdev->dev,
 153                        sizeof(struct tegra_rt5640), GFP_KERNEL);
 154        if (!machine)
 155                return -ENOMEM;
 156
 157        card->dev = &pdev->dev;
 158        snd_soc_card_set_drvdata(card, machine);
 159
 160        machine->gpio_hp_det = of_get_named_gpio_flags(
 161                np, "nvidia,hp-det-gpios", 0, &machine->gpio_hp_det_flags);
 162        if (machine->gpio_hp_det == -EPROBE_DEFER)
 163                return -EPROBE_DEFER;
 164
 165        ret = snd_soc_of_parse_card_name(card, "nvidia,model");
 166        if (ret)
 167                goto err;
 168
 169        ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing");
 170        if (ret)
 171                goto err;
 172
 173        tegra_rt5640_dai.codecs->of_node = of_parse_phandle(np,
 174                        "nvidia,audio-codec", 0);
 175        if (!tegra_rt5640_dai.codecs->of_node) {
 176                dev_err(&pdev->dev,
 177                        "Property 'nvidia,audio-codec' missing or invalid\n");
 178                ret = -EINVAL;
 179                goto err;
 180        }
 181
 182        tegra_rt5640_dai.cpus->of_node = of_parse_phandle(np,
 183                        "nvidia,i2s-controller", 0);
 184        if (!tegra_rt5640_dai.cpus->of_node) {
 185                dev_err(&pdev->dev,
 186                        "Property 'nvidia,i2s-controller' missing or invalid\n");
 187                ret = -EINVAL;
 188                goto err;
 189        }
 190
 191        tegra_rt5640_dai.platforms->of_node = tegra_rt5640_dai.cpus->of_node;
 192
 193        ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
 194        if (ret)
 195                goto err;
 196
 197        ret = snd_soc_register_card(card);
 198        if (ret) {
 199                dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
 200                        ret);
 201                goto err_fini_utils;
 202        }
 203
 204        return 0;
 205
 206err_fini_utils:
 207        tegra_asoc_utils_fini(&machine->util_data);
 208err:
 209        return ret;
 210}
 211
 212static int tegra_rt5640_remove(struct platform_device *pdev)
 213{
 214        struct snd_soc_card *card = platform_get_drvdata(pdev);
 215        struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card);
 216
 217        snd_soc_unregister_card(card);
 218
 219        tegra_asoc_utils_fini(&machine->util_data);
 220
 221        return 0;
 222}
 223
 224static const struct of_device_id tegra_rt5640_of_match[] = {
 225        { .compatible = "nvidia,tegra-audio-rt5640", },
 226        {},
 227};
 228
 229static struct platform_driver tegra_rt5640_driver = {
 230        .driver = {
 231                .name = DRV_NAME,
 232                .pm = &snd_soc_pm_ops,
 233                .of_match_table = tegra_rt5640_of_match,
 234        },
 235        .probe = tegra_rt5640_probe,
 236        .remove = tegra_rt5640_remove,
 237};
 238module_platform_driver(tegra_rt5640_driver);
 239
 240MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
 241MODULE_DESCRIPTION("Tegra+RT5640 machine ASoC driver");
 242MODULE_LICENSE("GPL v2");
 243MODULE_ALIAS("platform:" DRV_NAME);
 244MODULE_DEVICE_TABLE(of, tegra_rt5640_of_match);
 245