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 44struct 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 52static 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 80static 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 96static struct snd_soc_ops tegra_rt5677_ops = { 97 .hw_params = tegra_rt5677_asoc_hw_params, 98}; 99 100static struct snd_soc_jack tegra_rt5677_hp_jack; 101 102static struct snd_soc_jack_pin tegra_rt5677_hp_jack_pins = { 103 .pin = "Headphone", 104 .mask = SND_JACK_HEADPHONE, 105}; 106static 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 112static struct snd_soc_jack tegra_rt5677_mic_jack; 113 114static struct snd_soc_jack_pin tegra_rt5677_mic_jack_pins = { 115 .pin = "Headset Mic", 116 .mask = SND_JACK_MICROPHONE, 117}; 118 119static 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 126static 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 134static 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 142static 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 172static int tegra_rt5677_card_remove(struct snd_soc_card *card) 173{ 174 struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(card); 175 176 if (gpio_is_valid(machine->gpio_hp_det)) { 177 snd_soc_jack_free_gpios(&tegra_rt5677_hp_jack, 1, 178 &tegra_rt5677_hp_jack_gpio); 179 } 180 181 if (gpio_is_valid(machine->gpio_mic_present)) { 182 snd_soc_jack_free_gpios(&tegra_rt5677_mic_jack, 1, 183 &tegra_rt5677_mic_jack_gpio); 184 } 185 186 return 0; 187} 188 189static struct snd_soc_dai_link tegra_rt5677_dai = { 190 .name = "RT5677", 191 .stream_name = "RT5677 PCM", 192 .codec_dai_name = "rt5677-aif1", 193 .init = tegra_rt5677_asoc_init, 194 .ops = &tegra_rt5677_ops, 195 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 196 SND_SOC_DAIFMT_CBS_CFS, 197}; 198 199static struct snd_soc_card snd_soc_tegra_rt5677 = { 200 .name = "tegra-rt5677", 201 .owner = THIS_MODULE, 202 .remove = tegra_rt5677_card_remove, 203 .dai_link = &tegra_rt5677_dai, 204 .num_links = 1, 205 .controls = tegra_rt5677_controls, 206 .num_controls = ARRAY_SIZE(tegra_rt5677_controls), 207 .dapm_widgets = tegra_rt5677_dapm_widgets, 208 .num_dapm_widgets = ARRAY_SIZE(tegra_rt5677_dapm_widgets), 209 .fully_routed = true, 210}; 211 212static int tegra_rt5677_probe(struct platform_device *pdev) 213{ 214 struct device_node *np = pdev->dev.of_node; 215 struct snd_soc_card *card = &snd_soc_tegra_rt5677; 216 struct tegra_rt5677 *machine; 217 int ret; 218 219 machine = devm_kzalloc(&pdev->dev, 220 sizeof(struct tegra_rt5677), GFP_KERNEL); 221 if (!machine) 222 return -ENOMEM; 223 224 card->dev = &pdev->dev; 225 platform_set_drvdata(pdev, card); 226 snd_soc_card_set_drvdata(card, machine); 227 228 machine->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0); 229 if (machine->gpio_hp_det == -EPROBE_DEFER) 230 return -EPROBE_DEFER; 231 232 machine->gpio_mic_present = of_get_named_gpio(np, 233 "nvidia,mic-present-gpios", 0); 234 if (machine->gpio_mic_present == -EPROBE_DEFER) 235 return -EPROBE_DEFER; 236 237 machine->gpio_hp_en = of_get_named_gpio(np, "nvidia,hp-en-gpios", 0); 238 if (machine->gpio_hp_en == -EPROBE_DEFER) 239 return -EPROBE_DEFER; 240 if (gpio_is_valid(machine->gpio_hp_en)) { 241 ret = devm_gpio_request_one(&pdev->dev, machine->gpio_hp_en, 242 GPIOF_OUT_INIT_LOW, "hp_en"); 243 if (ret) { 244 dev_err(card->dev, "cannot get hp_en gpio\n"); 245 return ret; 246 } 247 } 248 249 machine->gpio_dmic_clk_en = of_get_named_gpio(np, 250 "nvidia,dmic-clk-en-gpios", 0); 251 if (machine->gpio_dmic_clk_en == -EPROBE_DEFER) 252 return -EPROBE_DEFER; 253 if (gpio_is_valid(machine->gpio_dmic_clk_en)) { 254 ret = devm_gpio_request_one(&pdev->dev, 255 machine->gpio_dmic_clk_en, 256 GPIOF_OUT_INIT_HIGH, "dmic_clk_en"); 257 if (ret) { 258 dev_err(card->dev, "cannot get dmic_clk_en gpio\n"); 259 return ret; 260 } 261 } 262 263 ret = snd_soc_of_parse_card_name(card, "nvidia,model"); 264 if (ret) 265 goto err; 266 267 ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing"); 268 if (ret) 269 goto err; 270 271 tegra_rt5677_dai.codec_of_node = of_parse_phandle(np, 272 "nvidia,audio-codec", 0); 273 if (!tegra_rt5677_dai.codec_of_node) { 274 dev_err(&pdev->dev, 275 "Property 'nvidia,audio-codec' missing or invalid\n"); 276 ret = -EINVAL; 277 goto err; 278 } 279 280 tegra_rt5677_dai.cpu_of_node = of_parse_phandle(np, 281 "nvidia,i2s-controller", 0); 282 if (!tegra_rt5677_dai.cpu_of_node) { 283 dev_err(&pdev->dev, 284 "Property 'nvidia,i2s-controller' missing or invalid\n"); 285 ret = -EINVAL; 286 goto err; 287 } 288 tegra_rt5677_dai.platform_of_node = tegra_rt5677_dai.cpu_of_node; 289 290 ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev); 291 if (ret) 292 goto err; 293 294 ret = snd_soc_register_card(card); 295 if (ret) { 296 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", 297 ret); 298 goto err_fini_utils; 299 } 300 301 return 0; 302 303err_fini_utils: 304 tegra_asoc_utils_fini(&machine->util_data); 305err: 306 return ret; 307} 308 309static int tegra_rt5677_remove(struct platform_device *pdev) 310{ 311 struct snd_soc_card *card = platform_get_drvdata(pdev); 312 struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(card); 313 314 snd_soc_unregister_card(card); 315 316 tegra_asoc_utils_fini(&machine->util_data); 317 318 return 0; 319} 320 321static const struct of_device_id tegra_rt5677_of_match[] = { 322 { .compatible = "nvidia,tegra-audio-rt5677", }, 323 {}, 324}; 325 326static struct platform_driver tegra_rt5677_driver = { 327 .driver = { 328 .name = DRV_NAME, 329 .pm = &snd_soc_pm_ops, 330 .of_match_table = tegra_rt5677_of_match, 331 }, 332 .probe = tegra_rt5677_probe, 333 .remove = tegra_rt5677_remove, 334}; 335module_platform_driver(tegra_rt5677_driver); 336 337MODULE_AUTHOR("Anatol Pomozov <anatol@google.com>"); 338MODULE_DESCRIPTION("Tegra+RT5677 machine ASoC driver"); 339MODULE_LICENSE("GPL v2"); 340MODULE_ALIAS("platform:" DRV_NAME); 341MODULE_DEVICE_TABLE(of, tegra_rt5677_of_match); 342