Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * tegra_wm8903.c - Tegra machine ASoC driver for boards using WM8903 codec.
0004  *
0005  * Author: Stephen Warren <swarren@nvidia.com>
0006  * Copyright (C) 2010-2012 - NVIDIA, Inc.
0007  *
0008  * Based on code copyright/by:
0009  *
0010  * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd.
0011  *
0012  * Copyright 2007 Wolfson Microelectronics PLC.
0013  * Author: Graeme Gregory
0014  *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
0015  */
0016 
0017 #include <linux/gpio/consumer.h>
0018 #include <linux/of.h>
0019 #include <linux/module.h>
0020 #include <linux/platform_device.h>
0021 
0022 #include <sound/core.h>
0023 #include <sound/jack.h>
0024 #include <sound/soc.h>
0025 
0026 #include "../codecs/wm8903.h"
0027 
0028 #include "tegra_asoc_machine.h"
0029 
0030 static struct snd_soc_jack_pin tegra_wm8903_mic_jack_pins[] = {
0031     { .pin = "Mic Jack", .mask = SND_JACK_MICROPHONE },
0032 };
0033 
0034 static unsigned int tegra_wm8903_mclk_rate(unsigned int srate)
0035 {
0036     unsigned int mclk;
0037 
0038     switch (srate) {
0039     case 64000:
0040     case 88200:
0041     case 96000:
0042         mclk = 128 * srate;
0043         break;
0044     default:
0045         mclk = 256 * srate;
0046         break;
0047     }
0048     /* FIXME: Codec only requires >= 3MHz if OSR==0 */
0049     while (mclk < 6000000)
0050         mclk *= 2;
0051 
0052     return mclk;
0053 }
0054 
0055 static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd)
0056 {
0057     struct tegra_machine *machine = snd_soc_card_get_drvdata(rtd->card);
0058     struct snd_soc_card *card = rtd->card;
0059     int err;
0060 
0061     /*
0062      * Older version of machine driver was ignoring GPIO polarity,
0063      * forcing it to active-low.  This means that all older device-trees
0064      * which set the polarity to active-high are wrong and we need to fix
0065      * them up.
0066      */
0067     if (machine->asoc->hp_jack_gpio_active_low) {
0068         bool active_low = gpiod_is_active_low(machine->gpiod_hp_det);
0069 
0070         machine->hp_jack_gpio->invert = !active_low;
0071     }
0072 
0073     err = tegra_asoc_machine_init(rtd);
0074     if (err)
0075         return err;
0076 
0077     if (!machine->gpiod_mic_det && machine->asoc->add_mic_jack) {
0078         struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
0079         struct snd_soc_component *component = codec_dai->component;
0080         int shrt = 0;
0081 
0082         err = snd_soc_card_jack_new_pins(rtd->card, "Mic Jack",
0083                          SND_JACK_MICROPHONE,
0084                          machine->mic_jack,
0085                          tegra_wm8903_mic_jack_pins,
0086                          ARRAY_SIZE(tegra_wm8903_mic_jack_pins));
0087         if (err) {
0088             dev_err(rtd->dev, "Mic Jack creation failed: %d\n", err);
0089             return err;
0090         }
0091 
0092         if (of_property_read_bool(card->dev->of_node, "nvidia,headset"))
0093             shrt = SND_JACK_MICROPHONE;
0094 
0095         wm8903_mic_detect(component, machine->mic_jack,
0096                   SND_JACK_MICROPHONE, shrt);
0097     }
0098 
0099     snd_soc_dapm_force_enable_pin(&card->dapm, "MICBIAS");
0100 
0101     return 0;
0102 }
0103 
0104 static int tegra_wm8903_remove(struct snd_soc_card *card)
0105 {
0106     struct snd_soc_dai_link *link = &card->dai_link[0];
0107     struct snd_soc_pcm_runtime *rtd = snd_soc_get_pcm_runtime(card, link);
0108     struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
0109     struct snd_soc_component *component = codec_dai->component;
0110 
0111     wm8903_mic_detect(component, NULL, 0, 0);
0112 
0113     return 0;
0114 }
0115 
0116 SND_SOC_DAILINK_DEFS(hifi,
0117     DAILINK_COMP_ARRAY(COMP_EMPTY()),
0118     DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8903-hifi")),
0119     DAILINK_COMP_ARRAY(COMP_EMPTY()));
0120 
0121 static struct snd_soc_dai_link tegra_wm8903_dai = {
0122     .name = "WM8903",
0123     .stream_name = "WM8903 PCM",
0124     .init = tegra_wm8903_init,
0125     .dai_fmt = SND_SOC_DAIFMT_I2S |
0126            SND_SOC_DAIFMT_NB_NF |
0127            SND_SOC_DAIFMT_CBS_CFS,
0128     SND_SOC_DAILINK_REG(hifi),
0129 };
0130 
0131 static struct snd_soc_card snd_soc_tegra_wm8903 = {
0132     .components = "codec:wm8903",
0133     .owner = THIS_MODULE,
0134     .dai_link = &tegra_wm8903_dai,
0135     .num_links = 1,
0136     .remove = tegra_wm8903_remove,
0137     .fully_routed = true,
0138 };
0139 
0140 /* older device-trees used wrong polarity for the headphones-detection GPIO */
0141 static const struct tegra_asoc_data tegra_wm8903_data_legacy = {
0142     .mclk_rate = tegra_wm8903_mclk_rate,
0143     .card = &snd_soc_tegra_wm8903,
0144     .hp_jack_gpio_active_low = true,
0145     .add_common_dapm_widgets = true,
0146     .add_common_controls = true,
0147     .add_common_snd_ops = true,
0148     .add_mic_jack = true,
0149     .add_hp_jack = true,
0150 };
0151 
0152 static const struct tegra_asoc_data tegra_wm8903_data = {
0153     .mclk_rate = tegra_wm8903_mclk_rate,
0154     .card = &snd_soc_tegra_wm8903,
0155     .add_common_dapm_widgets = true,
0156     .add_common_controls = true,
0157     .add_common_snd_ops = true,
0158     .add_mic_jack = true,
0159     .add_hp_jack = true,
0160 };
0161 
0162 static const struct of_device_id tegra_wm8903_of_match[] = {
0163     { .compatible = "ad,tegra-audio-plutux", .data = &tegra_wm8903_data_legacy },
0164     { .compatible = "ad,tegra-audio-wm8903-medcom-wide", .data = &tegra_wm8903_data_legacy },
0165     { .compatible = "ad,tegra-audio-wm8903-tec", .data = &tegra_wm8903_data_legacy },
0166     { .compatible = "nvidia,tegra-audio-wm8903-cardhu", .data = &tegra_wm8903_data_legacy },
0167     { .compatible = "nvidia,tegra-audio-wm8903-harmony", .data = &tegra_wm8903_data_legacy },
0168     { .compatible = "nvidia,tegra-audio-wm8903-picasso", .data = &tegra_wm8903_data_legacy },
0169     { .compatible = "nvidia,tegra-audio-wm8903-seaboard", .data = &tegra_wm8903_data_legacy },
0170     { .compatible = "nvidia,tegra-audio-wm8903-ventana", .data = &tegra_wm8903_data_legacy },
0171     { .compatible = "nvidia,tegra-audio-wm8903", .data = &tegra_wm8903_data },
0172     {},
0173 };
0174 MODULE_DEVICE_TABLE(of, tegra_wm8903_of_match);
0175 
0176 static struct platform_driver tegra_wm8903_driver = {
0177     .driver = {
0178         .name = "tegra-wm8903",
0179         .of_match_table = tegra_wm8903_of_match,
0180         .pm = &snd_soc_pm_ops,
0181     },
0182     .probe = tegra_asoc_machine_probe,
0183 };
0184 module_platform_driver(tegra_wm8903_driver);
0185 
0186 MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
0187 MODULE_DESCRIPTION("Tegra+WM8903 machine ASoC driver");
0188 MODULE_LICENSE("GPL");