xref: /linux/sound/soc/tegra/tegra_wm8962.c (revision a8e7ef3cec99ba2487110e01d77a8a278593b3e9)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * tegra_wm8962.c - Tegra machine ASoC driver for boards using WM8962 codec.
4  *
5  * Copyright (C) 2021-2024 Jonas Schwöbel <jonasschwoebel@yahoo.de>
6  *			   Svyatoslav Ryhel <clamor95@gmail.com>
7  *
8  * Based on tegra_wm8903 code copyright/by:
9  *
10  * Author: Stephen Warren <swarren@nvidia.com>
11  * Copyright (C) 2010-2012 - NVIDIA, Inc.
12  *
13  * Based on code copyright/by:
14  *
15  * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd.
16  *
17  * Copyright 2007 Wolfson Microelectronics PLC.
18  * Author: Graeme Gregory
19  *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
20  */
21 
22 #include <linux/gpio/consumer.h>
23 #include <linux/of.h>
24 #include <linux/module.h>
25 #include <linux/platform_device.h>
26 
27 #include <sound/core.h>
28 #include <sound/jack.h>
29 #include <sound/soc.h>
30 
31 #include "../codecs/wm8962.h"
32 
33 #include "tegra_asoc_machine.h"
34 
35 static struct snd_soc_jack_pin tegra_wm8962_mic_jack_pins[] = {
36 	{ .pin = "Mic Jack", .mask = SND_JACK_MICROPHONE },
37 };
38 
39 static unsigned int tegra_wm8962_mclk_rate(unsigned int srate)
40 {
41 	unsigned int mclk;
42 
43 	switch (srate) {
44 	case 8000:
45 	case 16000:
46 	case 24000:
47 	case 32000:
48 	case 48000:
49 	case 64000:
50 	case 96000:
51 		mclk = 12288000;
52 		break;
53 	case 11025:
54 	case 22050:
55 	case 44100:
56 	case 88200:
57 		mclk = 11289600;
58 		break;
59 	default:
60 		mclk = 12000000;
61 		break;
62 	}
63 
64 	return mclk;
65 }
66 
67 static int tegra_wm8962_init(struct snd_soc_pcm_runtime *rtd)
68 {
69 	struct tegra_machine *machine = snd_soc_card_get_drvdata(rtd->card);
70 	struct snd_soc_card *card = rtd->card;
71 	struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
72 	int err;
73 
74 	err = tegra_asoc_machine_init(rtd);
75 	if (err)
76 		return err;
77 
78 	if (!machine->gpiod_mic_det && machine->asoc->add_mic_jack) {
79 		struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
80 		struct snd_soc_component *component = codec_dai->component;
81 
82 		err = snd_soc_card_jack_new_pins(rtd->card, "Mic Jack",
83 						 SND_JACK_MICROPHONE,
84 						 machine->mic_jack,
85 						 tegra_wm8962_mic_jack_pins,
86 						 ARRAY_SIZE(tegra_wm8962_mic_jack_pins));
87 		if (err) {
88 			dev_err(rtd->dev, "Mic Jack creation failed: %d\n", err);
89 			return err;
90 		}
91 
92 		wm8962_mic_detect(component, machine->mic_jack);
93 	}
94 
95 	snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
96 
97 	return 0;
98 }
99 
100 static int tegra_wm8962_remove(struct snd_soc_card *card)
101 {
102 	struct snd_soc_dai_link *link = &card->dai_link[0];
103 	struct snd_soc_pcm_runtime *rtd = snd_soc_get_pcm_runtime(card, link);
104 	struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
105 	struct snd_soc_component *component = codec_dai->component;
106 
107 	wm8962_mic_detect(component, NULL);
108 
109 	return 0;
110 }
111 
112 SND_SOC_DAILINK_DEFS(wm8962_hifi,
113 	DAILINK_COMP_ARRAY(COMP_EMPTY()),
114 	DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8962")),
115 	DAILINK_COMP_ARRAY(COMP_EMPTY()));
116 
117 static struct snd_soc_dai_link tegra_wm8962_dai = {
118 	.name = "WM8962",
119 	.stream_name = "WM8962 PCM",
120 	.init = tegra_wm8962_init,
121 	.dai_fmt = SND_SOC_DAIFMT_I2S |
122 		   SND_SOC_DAIFMT_NB_NF |
123 		   SND_SOC_DAIFMT_CBC_CFC,
124 	SND_SOC_DAILINK_REG(wm8962_hifi),
125 };
126 
127 static struct snd_soc_card snd_soc_tegra_wm8962 = {
128 	.components = "codec:wm8962",
129 	.owner = THIS_MODULE,
130 	.dai_link = &tegra_wm8962_dai,
131 	.num_links = 1,
132 	.remove = tegra_wm8962_remove,
133 	.fully_routed = true,
134 };
135 
136 static const struct tegra_asoc_data tegra_wm8962_data = {
137 	.mclk_rate = tegra_wm8962_mclk_rate,
138 	.card = &snd_soc_tegra_wm8962,
139 	.add_common_dapm_widgets = true,
140 	.add_common_controls = true,
141 	.add_common_snd_ops = true,
142 	.add_mic_jack = true,
143 	.add_hp_jack = true,
144 };
145 
146 static const struct of_device_id tegra_wm8962_of_match[] = {
147 	{ .compatible = "nvidia,tegra-audio-wm8962", .data = &tegra_wm8962_data },
148 	{},
149 };
150 MODULE_DEVICE_TABLE(of, tegra_wm8962_of_match);
151 
152 static struct platform_driver tegra_wm8962_driver = {
153 	.driver = {
154 		.name = "tegra-wm8962",
155 		.of_match_table = tegra_wm8962_of_match,
156 		.pm = &snd_soc_pm_ops,
157 	},
158 	.probe = tegra_asoc_machine_probe,
159 };
160 module_platform_driver(tegra_wm8962_driver);
161 
162 MODULE_AUTHOR("Jonas Schwöbel <jonasschwoebel@yahoo.de>");
163 MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
164 MODULE_DESCRIPTION("Tegra+WM8962 machine ASoC driver");
165 MODULE_LICENSE("GPL");
166