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 v3.12 258 lines 6.9 kB view raw
1/* 2* tegra_rt5640.c - Tegra machine ASoC driver for boards using WM8903 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}; 48 49static int tegra_rt5640_asoc_hw_params(struct snd_pcm_substream *substream, 50 struct snd_pcm_hw_params *params) 51{ 52 struct snd_soc_pcm_runtime *rtd = substream->private_data; 53 struct snd_soc_dai *codec_dai = rtd->codec_dai; 54 struct snd_soc_codec *codec = codec_dai->codec; 55 struct snd_soc_card *card = codec->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 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 snd_soc_dai *codec_dai = rtd->codec_dai; 112 struct snd_soc_codec *codec = codec_dai->codec; 113 struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(codec->card); 114 115 snd_soc_jack_new(codec, "Headphones", SND_JACK_HEADPHONE, 116 &tegra_rt5640_hp_jack); 117 snd_soc_jack_add_pins(&tegra_rt5640_hp_jack, 118 ARRAY_SIZE(tegra_rt5640_hp_jack_pins), 119 tegra_rt5640_hp_jack_pins); 120 121 if (gpio_is_valid(machine->gpio_hp_det)) { 122 tegra_rt5640_hp_jack_gpio.gpio = machine->gpio_hp_det; 123 snd_soc_jack_add_gpios(&tegra_rt5640_hp_jack, 124 1, 125 &tegra_rt5640_hp_jack_gpio); 126 } 127 128 return 0; 129} 130 131static struct snd_soc_dai_link tegra_rt5640_dai = { 132 .name = "RT5640", 133 .stream_name = "RT5640 PCM", 134 .codec_dai_name = "rt5640-aif1", 135 .init = tegra_rt5640_asoc_init, 136 .ops = &tegra_rt5640_ops, 137 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 138 SND_SOC_DAIFMT_CBS_CFS, 139}; 140 141static struct snd_soc_card snd_soc_tegra_rt5640 = { 142 .name = "tegra-rt5640", 143 .owner = THIS_MODULE, 144 .dai_link = &tegra_rt5640_dai, 145 .num_links = 1, 146 .controls = tegra_rt5640_controls, 147 .num_controls = ARRAY_SIZE(tegra_rt5640_controls), 148 .dapm_widgets = tegra_rt5640_dapm_widgets, 149 .num_dapm_widgets = ARRAY_SIZE(tegra_rt5640_dapm_widgets), 150 .fully_routed = true, 151}; 152 153static int tegra_rt5640_probe(struct platform_device *pdev) 154{ 155 struct device_node *np = pdev->dev.of_node; 156 struct snd_soc_card *card = &snd_soc_tegra_rt5640; 157 struct tegra_rt5640 *machine; 158 int ret; 159 160 machine = devm_kzalloc(&pdev->dev, 161 sizeof(struct tegra_rt5640), GFP_KERNEL); 162 if (!machine) { 163 dev_err(&pdev->dev, "Can't allocate tegra_rt5640\n"); 164 return -ENOMEM; 165 } 166 167 card->dev = &pdev->dev; 168 platform_set_drvdata(pdev, card); 169 snd_soc_card_set_drvdata(card, machine); 170 171 machine->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0); 172 if (machine->gpio_hp_det == -EPROBE_DEFER) 173 return -EPROBE_DEFER; 174 175 ret = snd_soc_of_parse_card_name(card, "nvidia,model"); 176 if (ret) 177 goto err; 178 179 ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing"); 180 if (ret) 181 goto err; 182 183 tegra_rt5640_dai.codec_of_node = of_parse_phandle(np, 184 "nvidia,audio-codec", 0); 185 if (!tegra_rt5640_dai.codec_of_node) { 186 dev_err(&pdev->dev, 187 "Property 'nvidia,audio-codec' missing or invalid\n"); 188 ret = -EINVAL; 189 goto err; 190 } 191 192 tegra_rt5640_dai.cpu_of_node = of_parse_phandle(np, 193 "nvidia,i2s-controller", 0); 194 if (!tegra_rt5640_dai.cpu_of_node) { 195 dev_err(&pdev->dev, 196 "Property 'nvidia,i2s-controller' missing or invalid\n"); 197 ret = -EINVAL; 198 goto err; 199 } 200 201 tegra_rt5640_dai.platform_of_node = tegra_rt5640_dai.cpu_of_node; 202 203 ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev); 204 if (ret) 205 goto err; 206 207 ret = snd_soc_register_card(card); 208 if (ret) { 209 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", 210 ret); 211 goto err_fini_utils; 212 } 213 214 return 0; 215 216err_fini_utils: 217 tegra_asoc_utils_fini(&machine->util_data); 218err: 219 return ret; 220} 221 222static int tegra_rt5640_remove(struct platform_device *pdev) 223{ 224 struct snd_soc_card *card = platform_get_drvdata(pdev); 225 struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card); 226 227 snd_soc_jack_free_gpios(&tegra_rt5640_hp_jack, 1, 228 &tegra_rt5640_hp_jack_gpio); 229 230 snd_soc_unregister_card(card); 231 232 tegra_asoc_utils_fini(&machine->util_data); 233 234 return 0; 235} 236 237static const struct of_device_id tegra_rt5640_of_match[] = { 238 { .compatible = "nvidia,tegra-audio-rt5640", }, 239 {}, 240}; 241 242static struct platform_driver tegra_rt5640_driver = { 243 .driver = { 244 .name = DRV_NAME, 245 .owner = THIS_MODULE, 246 .pm = &snd_soc_pm_ops, 247 .of_match_table = tegra_rt5640_of_match, 248 }, 249 .probe = tegra_rt5640_probe, 250 .remove = tegra_rt5640_remove, 251}; 252module_platform_driver(tegra_rt5640_driver); 253 254MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); 255MODULE_DESCRIPTION("Tegra+RT5640 machine ASoC driver"); 256MODULE_LICENSE("GPL v2"); 257MODULE_ALIAS("platform:" DRV_NAME); 258MODULE_DEVICE_TABLE(of, tegra_rt5640_of_match);