linux/sound/soc/tegra/tegra_wm8903.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * tegra_wm8903.c - Tegra machine ASoC driver for boards using WM8903 codec.
   4 *
   5 * Author: Stephen Warren <swarren@nvidia.com>
   6 * Copyright (C) 2010-2012 - NVIDIA, Inc.
   7 *
   8 * Based on code copyright/by:
   9 *
  10 * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd.
  11 *
  12 * Copyright 2007 Wolfson Microelectronics PLC.
  13 * Author: Graeme Gregory
  14 *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
  15 */
  16
  17#include <linux/gpio/consumer.h>
  18#include <linux/of.h>
  19#include <linux/module.h>
  20#include <linux/platform_device.h>
  21
  22#include <sound/core.h>
  23#include <sound/jack.h>
  24#include <sound/soc.h>
  25
  26#include "../codecs/wm8903.h"
  27
  28#include "tegra_asoc_machine.h"
  29
  30static struct snd_soc_jack_pin tegra_wm8903_mic_jack_pins[] = {
  31        { .pin = "Mic Jack", .mask = SND_JACK_MICROPHONE },
  32};
  33
  34static unsigned int tegra_wm8903_mclk_rate(unsigned int srate)
  35{
  36        unsigned int mclk;
  37
  38        switch (srate) {
  39        case 64000:
  40        case 88200:
  41        case 96000:
  42                mclk = 128 * srate;
  43                break;
  44        default:
  45                mclk = 256 * srate;
  46                break;
  47        }
  48        /* FIXME: Codec only requires >= 3MHz if OSR==0 */
  49        while (mclk < 6000000)
  50                mclk *= 2;
  51
  52        return mclk;
  53}
  54
  55static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd)
  56{
  57        struct tegra_machine *machine = snd_soc_card_get_drvdata(rtd->card);
  58        struct snd_soc_card *card = rtd->card;
  59        int err;
  60
  61        /*
  62         * Older version of machine driver was ignoring GPIO polarity,
  63         * forcing it to active-low.  This means that all older device-trees
  64         * which set the polarity to active-high are wrong and we need to fix
  65         * them up.
  66         */
  67        if (machine->asoc->hp_jack_gpio_active_low) {
  68                bool active_low = gpiod_is_active_low(machine->gpiod_hp_det);
  69
  70                machine->hp_jack_gpio->invert = !active_low;
  71        }
  72
  73        err = tegra_asoc_machine_init(rtd);
  74        if (err)
  75                return err;
  76
  77        if (!machine->gpiod_mic_det && machine->asoc->add_mic_jack) {
  78                struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
  79                struct snd_soc_component *component = codec_dai->component;
  80                int shrt = 0;
  81
  82                err = snd_soc_card_jack_new(rtd->card, "Mic Jack",
  83                                            SND_JACK_MICROPHONE,
  84                                            machine->mic_jack,
  85                                            tegra_wm8903_mic_jack_pins,
  86                                            ARRAY_SIZE(tegra_wm8903_mic_jack_pins));
  87                if (err) {
  88                        dev_err(rtd->dev, "Mic Jack creation failed: %d\n", err);
  89                        return err;
  90                }
  91
  92                if (of_property_read_bool(card->dev->of_node, "nvidia,headset"))
  93                        shrt = SND_JACK_MICROPHONE;
  94
  95                wm8903_mic_detect(component, machine->mic_jack,
  96                                  SND_JACK_MICROPHONE, shrt);
  97        }
  98
  99        snd_soc_dapm_force_enable_pin(&card->dapm, "MICBIAS");
 100
 101        return 0;
 102}
 103
 104static int tegra_wm8903_remove(struct snd_soc_card *card)
 105{
 106        struct snd_soc_dai_link *link = &card->dai_link[0];
 107        struct snd_soc_pcm_runtime *rtd = snd_soc_get_pcm_runtime(card, link);
 108        struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
 109        struct snd_soc_component *component = codec_dai->component;
 110
 111        wm8903_mic_detect(component, NULL, 0, 0);
 112
 113        return 0;
 114}
 115
 116SND_SOC_DAILINK_DEFS(hifi,
 117        DAILINK_COMP_ARRAY(COMP_EMPTY()),
 118        DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8903-hifi")),
 119        DAILINK_COMP_ARRAY(COMP_EMPTY()));
 120
 121static struct snd_soc_dai_link tegra_wm8903_dai = {
 122        .name = "WM8903",
 123        .stream_name = "WM8903 PCM",
 124        .init = tegra_wm8903_init,
 125        .dai_fmt = SND_SOC_DAIFMT_I2S |
 126                   SND_SOC_DAIFMT_NB_NF |
 127                   SND_SOC_DAIFMT_CBS_CFS,
 128        SND_SOC_DAILINK_REG(hifi),
 129};
 130
 131static struct snd_soc_card snd_soc_tegra_wm8903 = {
 132        .components = "codec:wm8903",
 133        .owner = THIS_MODULE,
 134        .dai_link = &tegra_wm8903_dai,
 135        .num_links = 1,
 136        .remove = tegra_wm8903_remove,
 137        .fully_routed = true,
 138};
 139
 140/* older device-trees used wrong polarity for the headphones-detection GPIO */
 141static const struct tegra_asoc_data tegra_wm8903_data_legacy = {
 142        .mclk_rate = tegra_wm8903_mclk_rate,
 143        .card = &snd_soc_tegra_wm8903,
 144        .hp_jack_gpio_active_low = true,
 145        .add_common_dapm_widgets = true,
 146        .add_common_controls = true,
 147        .add_common_snd_ops = true,
 148        .add_mic_jack = true,
 149        .add_hp_jack = true,
 150};
 151
 152static const struct tegra_asoc_data tegra_wm8903_data = {
 153        .mclk_rate = tegra_wm8903_mclk_rate,
 154        .card = &snd_soc_tegra_wm8903,
 155        .add_common_dapm_widgets = true,
 156        .add_common_controls = true,
 157        .add_common_snd_ops = true,
 158        .add_mic_jack = true,
 159        .add_hp_jack = true,
 160};
 161
 162static const struct of_device_id tegra_wm8903_of_match[] = {
 163        { .compatible = "ad,tegra-audio-plutux", .data = &tegra_wm8903_data_legacy },
 164        { .compatible = "ad,tegra-audio-wm8903-medcom-wide", .data = &tegra_wm8903_data_legacy },
 165        { .compatible = "ad,tegra-audio-wm8903-tec", .data = &tegra_wm8903_data_legacy },
 166        { .compatible = "nvidia,tegra-audio-wm8903-cardhu", .data = &tegra_wm8903_data_legacy },
 167        { .compatible = "nvidia,tegra-audio-wm8903-harmony", .data = &tegra_wm8903_data_legacy },
 168        { .compatible = "nvidia,tegra-audio-wm8903-picasso", .data = &tegra_wm8903_data_legacy },
 169        { .compatible = "nvidia,tegra-audio-wm8903-seaboard", .data = &tegra_wm8903_data_legacy },
 170        { .compatible = "nvidia,tegra-audio-wm8903-ventana", .data = &tegra_wm8903_data_legacy },
 171        { .compatible = "nvidia,tegra-audio-wm8903", .data = &tegra_wm8903_data },
 172        {},
 173};
 174MODULE_DEVICE_TABLE(of, tegra_wm8903_of_match);
 175
 176static struct platform_driver tegra_wm8903_driver = {
 177        .driver = {
 178                .name = "tegra-wm8903",
 179                .of_match_table = tegra_wm8903_of_match,
 180                .pm = &snd_soc_pm_ops,
 181        },
 182        .probe = tegra_asoc_machine_probe,
 183};
 184module_platform_driver(tegra_wm8903_driver);
 185
 186MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
 187MODULE_DESCRIPTION("Tegra+WM8903 machine ASoC driver");
 188MODULE_LICENSE("GPL");
 189