Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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