~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/tegra/tegra_rt5677.c

Version: ~ [ linux-5.9-rc6 ] ~ [ linux-5.8.10 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.66 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.146 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.198 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.236 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.236 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.85 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2 * tegra_rt5677.c - Tegra machine ASoC driver for boards using RT5677 codec.
  3  *
  4  * Copyright (c) 2014, The Chromium OS Authors.  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/rt5677.h"
 39 
 40 #include "tegra_asoc_utils.h"
 41 
 42 #define DRV_NAME "tegra-snd-rt5677"
 43 
 44 struct tegra_rt5677 {
 45         struct tegra_asoc_utils_data util_data;
 46         int gpio_hp_det;
 47         int gpio_hp_en;
 48         int gpio_mic_present;
 49         int gpio_dmic_clk_en;
 50 };
 51 
 52 static int tegra_rt5677_asoc_hw_params(struct snd_pcm_substream *substream,
 53                                         struct snd_pcm_hw_params *params)
 54 {
 55         struct snd_soc_pcm_runtime *rtd = substream->private_data;
 56         struct snd_soc_dai *codec_dai = rtd->codec_dai;
 57         struct snd_soc_card *card = rtd->card;
 58         struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(card);
 59         int srate, mclk, err;
 60 
 61         srate = params_rate(params);
 62         mclk = 256 * srate;
 63 
 64         err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
 65         if (err < 0) {
 66                 dev_err(card->dev, "Can't configure clocks\n");
 67                 return err;
 68         }
 69 
 70         err = snd_soc_dai_set_sysclk(codec_dai, RT5677_SCLK_S_MCLK, mclk,
 71                                         SND_SOC_CLOCK_IN);
 72         if (err < 0) {
 73                 dev_err(card->dev, "codec_dai clock not set\n");
 74                 return err;
 75         }
 76 
 77         return 0;
 78 }
 79 
 80 static int tegra_rt5677_event_hp(struct snd_soc_dapm_widget *w,
 81                         struct snd_kcontrol *k, int event)
 82 {
 83         struct snd_soc_dapm_context *dapm = w->dapm;
 84         struct snd_soc_card *card = dapm->card;
 85         struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(card);
 86 
 87         if (!gpio_is_valid(machine->gpio_hp_en))
 88                 return 0;
 89 
 90         gpio_set_value_cansleep(machine->gpio_hp_en,
 91                 SND_SOC_DAPM_EVENT_ON(event));
 92 
 93         return 0;
 94 }
 95 
 96 static const struct snd_soc_ops tegra_rt5677_ops = {
 97         .hw_params = tegra_rt5677_asoc_hw_params,
 98 };
 99 
100 static struct snd_soc_jack tegra_rt5677_hp_jack;
101 
102 static struct snd_soc_jack_pin tegra_rt5677_hp_jack_pins = {
103         .pin = "Headphone",
104         .mask = SND_JACK_HEADPHONE,
105 };
106 static struct snd_soc_jack_gpio tegra_rt5677_hp_jack_gpio = {
107         .name = "Headphone detection",
108         .report = SND_JACK_HEADPHONE,
109         .debounce_time = 150,
110 };
111 
112 static struct snd_soc_jack tegra_rt5677_mic_jack;
113 
114 static struct snd_soc_jack_pin tegra_rt5677_mic_jack_pins = {
115         .pin = "Headset Mic",
116         .mask = SND_JACK_MICROPHONE,
117 };
118 
119 static struct snd_soc_jack_gpio tegra_rt5677_mic_jack_gpio = {
120         .name = "Headset Mic detection",
121         .report = SND_JACK_MICROPHONE,
122         .debounce_time = 150,
123         .invert = 1
124 };
125 
126 static const struct snd_soc_dapm_widget tegra_rt5677_dapm_widgets[] = {
127         SND_SOC_DAPM_SPK("Speaker", NULL),
128         SND_SOC_DAPM_HP("Headphone", tegra_rt5677_event_hp),
129         SND_SOC_DAPM_MIC("Headset Mic", NULL),
130         SND_SOC_DAPM_MIC("Internal Mic 1", NULL),
131         SND_SOC_DAPM_MIC("Internal Mic 2", NULL),
132 };
133 
134 static const struct snd_kcontrol_new tegra_rt5677_controls[] = {
135         SOC_DAPM_PIN_SWITCH("Speaker"),
136         SOC_DAPM_PIN_SWITCH("Headphone"),
137         SOC_DAPM_PIN_SWITCH("Headset Mic"),
138         SOC_DAPM_PIN_SWITCH("Internal Mic 1"),
139         SOC_DAPM_PIN_SWITCH("Internal Mic 2"),
140 };
141 
142 static int tegra_rt5677_asoc_init(struct snd_soc_pcm_runtime *rtd)
143 {
144         struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(rtd->card);
145 
146         snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE,
147                               &tegra_rt5677_hp_jack,
148                               &tegra_rt5677_hp_jack_pins, 1);
149 
150         if (gpio_is_valid(machine->gpio_hp_det)) {
151                 tegra_rt5677_hp_jack_gpio.gpio = machine->gpio_hp_det;
152                 snd_soc_jack_add_gpios(&tegra_rt5677_hp_jack, 1,
153                                 &tegra_rt5677_hp_jack_gpio);
154         }
155 
156 
157         snd_soc_card_jack_new(rtd->card, "Mic Jack", SND_JACK_MICROPHONE,
158                               &tegra_rt5677_mic_jack,
159                               &tegra_rt5677_mic_jack_pins, 1);
160 
161         if (gpio_is_valid(machine->gpio_mic_present)) {
162                 tegra_rt5677_mic_jack_gpio.gpio = machine->gpio_mic_present;
163                 snd_soc_jack_add_gpios(&tegra_rt5677_mic_jack, 1,
164                                 &tegra_rt5677_mic_jack_gpio);
165         }
166 
167         snd_soc_dapm_force_enable_pin(&rtd->card->dapm, "MICBIAS1");
168 
169         return 0;
170 }
171 
172 static struct snd_soc_dai_link tegra_rt5677_dai = {
173         .name = "RT5677",
174         .stream_name = "RT5677 PCM",
175         .codec_dai_name = "rt5677-aif1",
176         .init = tegra_rt5677_asoc_init,
177         .ops = &tegra_rt5677_ops,
178         .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
179                         SND_SOC_DAIFMT_CBS_CFS,
180 };
181 
182 static struct snd_soc_card snd_soc_tegra_rt5677 = {
183         .name = "tegra-rt5677",
184         .owner = THIS_MODULE,
185         .dai_link = &tegra_rt5677_dai,
186         .num_links = 1,
187         .controls = tegra_rt5677_controls,
188         .num_controls = ARRAY_SIZE(tegra_rt5677_controls),
189         .dapm_widgets = tegra_rt5677_dapm_widgets,
190         .num_dapm_widgets = ARRAY_SIZE(tegra_rt5677_dapm_widgets),
191         .fully_routed = true,
192 };
193 
194 static int tegra_rt5677_probe(struct platform_device *pdev)
195 {
196         struct device_node *np = pdev->dev.of_node;
197         struct snd_soc_card *card = &snd_soc_tegra_rt5677;
198         struct tegra_rt5677 *machine;
199         int ret;
200 
201         machine = devm_kzalloc(&pdev->dev,
202                         sizeof(struct tegra_rt5677), GFP_KERNEL);
203         if (!machine)
204                 return -ENOMEM;
205 
206         card->dev = &pdev->dev;
207         snd_soc_card_set_drvdata(card, machine);
208 
209         machine->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0);
210         if (machine->gpio_hp_det == -EPROBE_DEFER)
211                 return -EPROBE_DEFER;
212 
213         machine->gpio_mic_present = of_get_named_gpio(np,
214                         "nvidia,mic-present-gpios", 0);
215         if (machine->gpio_mic_present == -EPROBE_DEFER)
216                 return -EPROBE_DEFER;
217 
218         machine->gpio_hp_en = of_get_named_gpio(np, "nvidia,hp-en-gpios", 0);
219         if (machine->gpio_hp_en == -EPROBE_DEFER)
220                 return -EPROBE_DEFER;
221         if (gpio_is_valid(machine->gpio_hp_en)) {
222                 ret = devm_gpio_request_one(&pdev->dev, machine->gpio_hp_en,
223                                 GPIOF_OUT_INIT_LOW, "hp_en");
224                 if (ret) {
225                         dev_err(card->dev, "cannot get hp_en gpio\n");
226                         return ret;
227                 }
228         }
229 
230         machine->gpio_dmic_clk_en = of_get_named_gpio(np,
231                 "nvidia,dmic-clk-en-gpios", 0);
232         if (machine->gpio_dmic_clk_en == -EPROBE_DEFER)
233                 return -EPROBE_DEFER;
234         if (gpio_is_valid(machine->gpio_dmic_clk_en)) {
235                 ret = devm_gpio_request_one(&pdev->dev,
236                                 machine->gpio_dmic_clk_en,
237                                 GPIOF_OUT_INIT_HIGH, "dmic_clk_en");
238                 if (ret) {
239                         dev_err(card->dev, "cannot get dmic_clk_en gpio\n");
240                         return ret;
241                 }
242         }
243 
244         ret = snd_soc_of_parse_card_name(card, "nvidia,model");
245         if (ret)
246                 goto err;
247 
248         ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing");
249         if (ret)
250                 goto err;
251 
252         tegra_rt5677_dai.codec_of_node = of_parse_phandle(np,
253                         "nvidia,audio-codec", 0);
254         if (!tegra_rt5677_dai.codec_of_node) {
255                 dev_err(&pdev->dev,
256                         "Property 'nvidia,audio-codec' missing or invalid\n");
257                 ret = -EINVAL;
258                 goto err;
259         }
260 
261         tegra_rt5677_dai.cpu_of_node = of_parse_phandle(np,
262                         "nvidia,i2s-controller", 0);
263         if (!tegra_rt5677_dai.cpu_of_node) {
264                 dev_err(&pdev->dev,
265                         "Property 'nvidia,i2s-controller' missing or invalid\n");
266                 ret = -EINVAL;
267                 goto err;
268         }
269         tegra_rt5677_dai.platform_of_node = tegra_rt5677_dai.cpu_of_node;
270 
271         ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
272         if (ret)
273                 goto err;
274 
275         ret = snd_soc_register_card(card);
276         if (ret) {
277                 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
278                         ret);
279                 goto err_fini_utils;
280         }
281 
282         return 0;
283 
284 err_fini_utils:
285         tegra_asoc_utils_fini(&machine->util_data);
286 err:
287         return ret;
288 }
289 
290 static int tegra_rt5677_remove(struct platform_device *pdev)
291 {
292         struct snd_soc_card *card = platform_get_drvdata(pdev);
293         struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(card);
294 
295         snd_soc_unregister_card(card);
296 
297         tegra_asoc_utils_fini(&machine->util_data);
298 
299         return 0;
300 }
301 
302 static const struct of_device_id tegra_rt5677_of_match[] = {
303         { .compatible = "nvidia,tegra-audio-rt5677", },
304         {},
305 };
306 
307 static struct platform_driver tegra_rt5677_driver = {
308         .driver = {
309                 .name = DRV_NAME,
310                 .pm = &snd_soc_pm_ops,
311                 .of_match_table = tegra_rt5677_of_match,
312         },
313         .probe = tegra_rt5677_probe,
314         .remove = tegra_rt5677_remove,
315 };
316 module_platform_driver(tegra_rt5677_driver);
317 
318 MODULE_AUTHOR("Anatol Pomozov <anatol@google.com>");
319 MODULE_DESCRIPTION("Tegra+RT5677 machine ASoC driver");
320 MODULE_LICENSE("GPL v2");
321 MODULE_ALIAS("platform:" DRV_NAME);
322 MODULE_DEVICE_TABLE(of, tegra_rt5677_of_match);
323 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp