Merge tag 'v5.15.57' into rpi-5.15.y
[platform/kernel/linux-rpi.git] / sound / soc / bcm / pifi-40.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * ALSA ASoC Machine Driver for PiFi-40
4  *
5  * Author:      David Knell <david.knell@gmail.com)
6  *              based on code by Daniel Matuschek <info@crazy-audio.com>
7  *              based on code by Florian Meier <florian.meier@koalo.de>
8  * Copyright (C) 2020
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * version 2 as published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * General Public License for more details.
18  */
19
20 #include <linux/module.h>
21 #include <linux/platform_device.h>
22 #include <linux/gpio/consumer.h>
23 #include <sound/core.h>
24 #include <sound/pcm.h>
25 #include <sound/pcm_params.h>
26 #include <sound/soc.h>
27 #include <linux/firmware.h>
28 #include <linux/delay.h>
29 #include <sound/tlv.h>
30
31 static struct gpio_desc *pdn_gpio;
32 static int vol = 0x30;
33
34 // Volume control
35 static int pifi_40_vol_get(struct snd_kcontrol *kcontrol,
36                            struct snd_ctl_elem_value *ucontrol)
37 {
38         ucontrol->value.integer.value[0] = vol;
39         ucontrol->value.integer.value[1] = vol;
40         return 0;
41 }
42
43 static int pifi_40_vol_set(struct snd_kcontrol *kcontrol,
44                            struct snd_ctl_elem_value *ucontrol)
45 {
46         struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
47         struct snd_soc_pcm_runtime *rtd;
48         unsigned int v = ucontrol->value.integer.value[0];
49         struct snd_soc_component *dac[2];
50
51         rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
52         dac[0] = asoc_rtd_to_codec(rtd, 0)->component;
53         dac[1] = asoc_rtd_to_codec(rtd, 1)->component;
54
55         snd_soc_component_write(dac[0], 0x07, 255 - v);
56         snd_soc_component_write(dac[1], 0x07, 255 - v);
57
58         vol = v;
59         return 1;
60 }
61
62 static const DECLARE_TLV_DB_SCALE(digital_tlv_master, -10350, 50, 1);
63 static const struct snd_kcontrol_new pifi_40_controls[] = {
64         SOC_DOUBLE_R_EXT_TLV("Master Volume", 0x00, 0x01,
65                              0x00, // Min
66                              0xff, // Max
67                              0x01, // Invert
68                              pifi_40_vol_get, pifi_40_vol_set,
69                              digital_tlv_master)
70 };
71
72 static const char * const codec_ctl_pfx[] = { "Left", "Right" };
73
74 static const char * const codec_ctl_name[] = { "Master Volume",
75                                         "Speaker Volume",
76                                         "Speaker Switch" };
77
78 static int snd_pifi_40_init(struct snd_soc_pcm_runtime *rtd)
79 {
80         struct snd_soc_card *card = rtd->card;
81         struct snd_soc_component *dac[2];
82         struct snd_kcontrol *kctl;
83         int i, j;
84
85         dac[0] = asoc_rtd_to_codec(rtd, 0)->component;
86         dac[1] = asoc_rtd_to_codec(rtd, 1)->component;
87
88
89         // Set up cards - pulse power down first
90         gpiod_set_value_cansleep(pdn_gpio, 1);
91         usleep_range(1000, 10000);
92         gpiod_set_value_cansleep(pdn_gpio, 0);
93         usleep_range(20000, 30000);
94
95         // Oscillator trim
96         snd_soc_component_write(dac[0], 0x1b, 0);
97         snd_soc_component_write(dac[1], 0x1b, 0);
98         usleep_range(60000, 80000);
99
100         // Common setup
101         for (i = 0; i < 2; i++) {
102                 // MCLK at 64fs, sample rate 44.1 or 48kHz
103                 snd_soc_component_write(dac[i], 0x00, 0x60);
104
105                 // Set up for PBTL
106                 snd_soc_component_write(dac[i], 0x19, 0x3A);
107                 snd_soc_component_write(dac[i], 0x25, 0x01103245);
108
109                 // Master vol to -10db
110                 snd_soc_component_write(dac[i], 0x07, 0x44);
111         }
112         // Inputs set to L and R respectively
113         snd_soc_component_write(dac[0], 0x20, 0x00017772);
114         snd_soc_component_write(dac[1], 0x20, 0x00107772);
115
116         // Remove codec controls
117         for (i = 0; i < 2; i++) {
118                 for (j = 0; j < 3; j++) {
119                         char cname[256];
120
121                         sprintf(cname, "%s %s", codec_ctl_pfx[i],
122                                 codec_ctl_name[j]);
123                         kctl = snd_soc_card_get_kcontrol(card, cname);
124                         if (!kctl) {
125                                 pr_info("Control %s not found\n",
126                                        cname);
127                         } else {
128                                 kctl->vd[0].access =
129                                         SNDRV_CTL_ELEM_ACCESS_READWRITE;
130                                 snd_ctl_remove(card->snd_card, kctl);
131                         }
132                 }
133         }
134
135         return 0;
136 }
137
138 static int snd_pifi_40_hw_params(struct snd_pcm_substream *substream,
139                                  struct snd_pcm_hw_params *params)
140 {
141         struct snd_soc_pcm_runtime *rtd = substream->private_data;
142         struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
143         unsigned int sample_bits;
144
145         sample_bits = snd_pcm_format_physical_width(params_format(params));
146         return snd_soc_dai_set_bclk_ratio(cpu_dai, 64);
147 }
148
149 static struct snd_soc_ops snd_pifi_40_ops = { .hw_params =
150                                                       snd_pifi_40_hw_params };
151
152 static struct snd_soc_dai_link_component pifi_40_codecs[] = {
153         {
154                 .dai_name = "tas571x-hifi",
155         },
156         {
157                 .dai_name = "tas571x-hifi",
158         },
159 };
160
161 SND_SOC_DAILINK_DEFS(
162         pifi_40_dai, DAILINK_COMP_ARRAY(COMP_EMPTY()),
163         DAILINK_COMP_ARRAY(COMP_CODEC("tas571x.1-001a", "tas571x-hifi"),
164                            COMP_CODEC("tas571x.1-001b", "tas571x-hifi")),
165         DAILINK_COMP_ARRAY(COMP_EMPTY()));
166
167 static struct snd_soc_dai_link snd_pifi_40_dai[] = {
168         {
169                 .name = "PiFi40",
170                 .stream_name = "PiFi40",
171                 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
172                            SND_SOC_DAIFMT_CBS_CFS,
173                 .ops = &snd_pifi_40_ops,
174                 .init = snd_pifi_40_init,
175                 SND_SOC_DAILINK_REG(pifi_40_dai),
176         },
177 };
178
179 // Machine driver
180 static struct snd_soc_card snd_pifi_40 = {
181         .name = "PiFi40",
182         .owner = THIS_MODULE,
183         .dai_link = snd_pifi_40_dai,
184         .num_links = ARRAY_SIZE(snd_pifi_40_dai),
185         .controls = pifi_40_controls,
186         .num_controls = ARRAY_SIZE(pifi_40_controls)
187 };
188
189 static void snd_pifi_40_pdn(struct snd_soc_card *card, int on)
190 {
191         if (pdn_gpio)
192                 gpiod_set_value_cansleep(pdn_gpio, on ? 0 : 1);
193 }
194
195 static int snd_pifi_40_probe(struct platform_device *pdev)
196 {
197         struct snd_soc_card *card = &snd_pifi_40;
198         int ret = 0, i = 0;
199
200         card->dev = &pdev->dev;
201         platform_set_drvdata(pdev, &snd_pifi_40);
202
203         if (pdev->dev.of_node) {
204                 struct device_node *i2s_node;
205                 struct snd_soc_dai_link *dai;
206
207                 dai = &snd_pifi_40_dai[0];
208                 i2s_node = of_parse_phandle(pdev->dev.of_node, "i2s-controller",
209                                             0);
210                 if (i2s_node) {
211                         for (i = 0; i < card->num_links; i++) {
212                                 dai->cpus->dai_name = NULL;
213                                 dai->cpus->of_node = i2s_node;
214                                 dai->platforms->name = NULL;
215                                 dai->platforms->of_node = i2s_node;
216                         }
217                 }
218
219                 pifi_40_codecs[0].of_node =
220                         of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
221                 pifi_40_codecs[1].of_node =
222                         of_parse_phandle(pdev->dev.of_node, "audio-codec", 1);
223                 if (!pifi_40_codecs[0].of_node || !pifi_40_codecs[1].of_node) {
224                         dev_err(&pdev->dev,
225                                 "Property 'audio-codec' missing or invalid\n");
226                         return -EINVAL;
227                 }
228
229                 pdn_gpio = devm_gpiod_get_optional(&pdev->dev, "pdn",
230                                                    GPIOD_OUT_LOW);
231                 if (IS_ERR(pdn_gpio)) {
232                         ret = PTR_ERR(pdn_gpio);
233                         dev_err(&pdev->dev, "failed to get pdn gpio: %d\n",
234                                 ret);
235                         return ret;
236                 }
237
238                 ret = snd_soc_register_card(&snd_pifi_40);
239                 if (ret < 0) {
240                         dev_err(&pdev->dev,
241                                 "snd_soc_register_card() failed: %d\n", ret);
242                         return ret;
243                 }
244
245                 return 0;
246         }
247
248         return -EINVAL;
249 }
250
251 static int snd_pifi_40_remove(struct platform_device *pdev)
252 {
253         struct snd_soc_card *card = platform_get_drvdata(pdev);
254
255         kfree(&card->drvdata);
256         snd_pifi_40_pdn(&snd_pifi_40, 0);
257         return snd_soc_unregister_card(&snd_pifi_40);
258 }
259
260 static const struct of_device_id snd_pifi_40_of_match[] = {
261         {
262                 .compatible = "pifi,pifi-40",
263         },
264         { /* sentinel */ },
265 };
266
267 MODULE_DEVICE_TABLE(of, snd_pifi_40_of_match);
268
269 static struct platform_driver snd_pifi_40_driver = {
270         .driver = {
271                 .name = "snd-pifi-40",
272                 .owner = THIS_MODULE,
273                 .of_match_table = snd_pifi_40_of_match,
274         },
275         .probe = snd_pifi_40_probe,
276         .remove = snd_pifi_40_remove,
277 };
278
279 module_platform_driver(snd_pifi_40_driver);
280
281 MODULE_AUTHOR("David Knell <david.knell@gmail.com>");
282 MODULE_DESCRIPTION("ALSA ASoC Machine Driver for PiFi-40");
283 MODULE_LICENSE("GPL v2");