Merge tag 'v5.15.57' into rpi-5.15.y
[platform/kernel/linux-rpi.git] / sound / soc / bcm / hifiberry_dacplushd.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * ASoC Driver for HiFiBerry DAC+ HD
4  *
5  * Author:      Joerg Schambacher, i2Audio GmbH for HiFiBerry
6  *              Copyright 2020
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * version 2 as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  */
17
18 #include <linux/module.h>
19 #include <linux/platform_device.h>
20 #include <linux/kernel.h>
21 #include <linux/delay.h>
22 #include <linux/module.h>
23 #include <linux/of.h>
24 #include <linux/delay.h>
25 #include <linux/gpio.h>
26 #include <linux/gpio/consumer.h>
27 #include <sound/core.h>
28 #include <sound/pcm.h>
29 #include <sound/pcm_params.h>
30 #include <sound/soc.h>
31 #include <linux/i2c.h>
32 #include <linux/clk.h>
33
34 #include "../codecs/pcm179x.h"
35
36 #define DEFAULT_RATE            44100
37
38 struct brd_drv_data {
39         struct regmap *regmap;
40         struct clk *sclk;
41 };
42
43 static struct brd_drv_data drvdata;
44 static struct gpio_desc *reset_gpio;
45 static const unsigned int hb_dacplushd_rates[] = {
46         192000, 96000, 48000, 176400, 88200, 44100,
47 };
48
49 static struct snd_pcm_hw_constraint_list hb_dacplushd_constraints = {
50         .list = hb_dacplushd_rates,
51         .count = ARRAY_SIZE(hb_dacplushd_rates),
52 };
53
54 static int snd_rpi_hb_dacplushd_startup(struct snd_pcm_substream *substream)
55 {
56         /* constraints for standard sample rates */
57         snd_pcm_hw_constraint_list(substream->runtime, 0,
58                                 SNDRV_PCM_HW_PARAM_RATE,
59                                 &hb_dacplushd_constraints);
60         return 0;
61 }
62
63 static void snd_rpi_hifiberry_dacplushd_set_sclk(
64                 struct snd_soc_component *component,
65                 int sample_rate)
66 {
67         if (!IS_ERR(drvdata.sclk))
68                 clk_set_rate(drvdata.sclk, sample_rate);
69 }
70
71 static int snd_rpi_hifiberry_dacplushd_init(struct snd_soc_pcm_runtime *rtd)
72 {
73         struct snd_soc_dai_link *dai = rtd->dai_link;
74         struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
75
76         dai->name = "HiFiBerry DAC+ HD";
77         dai->stream_name = "HiFiBerry DAC+ HD HiFi";
78         dai->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
79                 | SND_SOC_DAIFMT_CBM_CFM;
80
81         /* allow only fixed 32 clock counts per channel */
82         snd_soc_dai_set_bclk_ratio(cpu_dai, 32*2);
83
84         return 0;
85 }
86
87 static int snd_rpi_hifiberry_dacplushd_hw_params(
88         struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
89 {
90         int ret = 0;
91         struct snd_soc_pcm_runtime *rtd = substream->private_data;
92
93         struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
94
95         snd_rpi_hifiberry_dacplushd_set_sclk(component, params_rate(params));
96         return ret;
97 }
98
99 /* machine stream operations */
100 static struct snd_soc_ops snd_rpi_hifiberry_dacplushd_ops = {
101         .startup = snd_rpi_hb_dacplushd_startup,
102         .hw_params = snd_rpi_hifiberry_dacplushd_hw_params,
103 };
104
105 SND_SOC_DAILINK_DEFS(hifi,
106         DAILINK_COMP_ARRAY(COMP_CPU("bcm2708-i2s.0")),
107         DAILINK_COMP_ARRAY(COMP_CODEC("pcm179x.1-004c", "pcm179x-hifi")),
108         DAILINK_COMP_ARRAY(COMP_PLATFORM("bcm2708-i2s.0")));
109
110
111 static struct snd_soc_dai_link snd_rpi_hifiberry_dacplushd_dai[] = {
112 {
113         .name           = "HiFiBerry DAC+ HD",
114         .stream_name    = "HiFiBerry DAC+ HD HiFi",
115         .dai_fmt        = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
116                                 SND_SOC_DAIFMT_CBS_CFS,
117         .ops            = &snd_rpi_hifiberry_dacplushd_ops,
118         .init           = snd_rpi_hifiberry_dacplushd_init,
119         SND_SOC_DAILINK_REG(hifi),
120 },
121 };
122
123 /* audio machine driver */
124 static struct snd_soc_card snd_rpi_hifiberry_dacplushd = {
125         .name         = "snd_rpi_hifiberry_dacplushd",
126         .driver_name  = "HifiberryDacplusHD",
127         .owner        = THIS_MODULE,
128         .dai_link     = snd_rpi_hifiberry_dacplushd_dai,
129         .num_links    = ARRAY_SIZE(snd_rpi_hifiberry_dacplushd_dai),
130 };
131
132 static int snd_rpi_hifiberry_dacplushd_probe(struct platform_device *pdev)
133 {
134         int ret = 0;
135         static int dac_reset_done;
136         struct device *dev = &pdev->dev;
137         struct device_node *dev_node = dev->of_node;
138
139         snd_rpi_hifiberry_dacplushd.dev = &pdev->dev;
140
141         /* get GPIO and release DAC from RESET */
142         if (!dac_reset_done) {
143                 reset_gpio = gpiod_get(&pdev->dev, "reset", GPIOD_OUT_LOW);
144                 if (IS_ERR(reset_gpio)) {
145                         dev_err(&pdev->dev, "gpiod_get() failed\n");
146                         return -EINVAL;
147                 }
148                 dac_reset_done = 1;
149         }
150         if (!IS_ERR(reset_gpio))
151                 gpiod_set_value(reset_gpio, 0);
152         msleep(1);
153         if (!IS_ERR(reset_gpio))
154                 gpiod_set_value(reset_gpio, 1);
155         msleep(1);
156         if (!IS_ERR(reset_gpio))
157                 gpiod_set_value(reset_gpio, 0);
158
159         if (pdev->dev.of_node) {
160                 struct device_node *i2s_node;
161                 struct snd_soc_dai_link *dai;
162
163                 dai = &snd_rpi_hifiberry_dacplushd_dai[0];
164                 i2s_node = of_parse_phandle(pdev->dev.of_node,
165                         "i2s-controller", 0);
166
167                 if (i2s_node) {
168                         dai->cpus->of_node = i2s_node;
169                         dai->platforms->of_node = i2s_node;
170                         dai->cpus->dai_name = NULL;
171                         dai->platforms->name = NULL;
172                 } else {
173                         return -EPROBE_DEFER;
174                 }
175
176         }
177
178         ret = devm_snd_soc_register_card(&pdev->dev,
179                         &snd_rpi_hifiberry_dacplushd);
180         if (ret && ret != -EPROBE_DEFER) {
181                 dev_err(&pdev->dev,
182                         "snd_soc_register_card() failed: %d\n", ret);
183                 return ret;
184         }
185         if (ret == -EPROBE_DEFER)
186                 return ret;
187
188         dev_set_drvdata(dev, &drvdata);
189         if (dev_node == NULL) {
190                 dev_err(&pdev->dev, "Device tree node not found\n");
191                 return -ENODEV;
192         }
193
194         drvdata.sclk = devm_clk_get(dev, NULL);
195         if (IS_ERR(drvdata.sclk)) {
196                 drvdata.sclk = ERR_PTR(-ENOENT);
197                 return -ENODEV;
198         }
199
200         clk_set_rate(drvdata.sclk, DEFAULT_RATE);
201
202         return ret;
203 }
204
205 static int snd_rpi_hifiberry_dacplushd_remove(struct platform_device *pdev)
206 {
207         if (IS_ERR(reset_gpio))
208                 return -EINVAL;
209
210         /* put DAC into RESET and release GPIO */
211         gpiod_set_value(reset_gpio, 0);
212         gpiod_put(reset_gpio);
213
214         return 0;
215 }
216
217 static const struct of_device_id snd_rpi_hifiberry_dacplushd_of_match[] = {
218         { .compatible = "hifiberry,hifiberry-dacplushd", },
219         {},
220 };
221
222 MODULE_DEVICE_TABLE(of, snd_rpi_hifiberry_dacplushd_of_match);
223
224 static struct platform_driver snd_rpi_hifiberry_dacplushd_driver = {
225         .driver = {
226                 .name   = "snd-rpi-hifiberry-dacplushd",
227                 .owner  = THIS_MODULE,
228                 .of_match_table = snd_rpi_hifiberry_dacplushd_of_match,
229         },
230         .probe          = snd_rpi_hifiberry_dacplushd_probe,
231         .remove         = snd_rpi_hifiberry_dacplushd_remove,
232 };
233
234 module_platform_driver(snd_rpi_hifiberry_dacplushd_driver);
235
236 MODULE_AUTHOR("Joerg Schambacher <joerg@i2audio.com>");
237 MODULE_DESCRIPTION("ASoC Driver for HiFiBerry DAC+ HD");
238 MODULE_LICENSE("GPL v2");