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