GNU Linux-libre 6.8.9-gnu
[releases.git] / sound / soc / rockchip / rk3288_hdmi_analog.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Rockchip machine ASoC driver for RK3288 boards that have an HDMI and analog
4  * audio output
5  *
6  * Copyright (c) 2016, Collabora Ltd.
7  *
8  * Authors: Sjoerd Simons <sjoerd.simons@collabora.com>,
9  *          Romain Perier <romain.perier@collabora.com>
10  */
11
12 #include <linux/module.h>
13 #include <linux/platform_device.h>
14 #include <linux/slab.h>
15 #include <linux/gpio/consumer.h>
16 #include <sound/core.h>
17 #include <sound/jack.h>
18 #include <sound/pcm.h>
19 #include <sound/pcm_params.h>
20 #include <sound/soc.h>
21 #include <sound/soc-dapm.h>
22
23 #include "rockchip_i2s.h"
24
25 #define DRV_NAME "rk3288-snd-hdmi-analog"
26
27 struct rk_drvdata {
28         struct gpio_desc *gpio_hp_en;
29 };
30
31 static int rk_hp_power(struct snd_soc_dapm_widget *w,
32                        struct snd_kcontrol *k, int event)
33 {
34         struct rk_drvdata *machine = snd_soc_card_get_drvdata(w->dapm->card);
35
36         gpiod_set_value_cansleep(machine->gpio_hp_en,
37                                  SND_SOC_DAPM_EVENT_ON(event));
38
39         return 0;
40 }
41
42 static struct snd_soc_jack headphone_jack;
43 static struct snd_soc_jack_pin headphone_jack_pins[] = {
44         {
45                 .pin = "Analog",
46                 .mask = SND_JACK_HEADPHONE
47         },
48 };
49
50 static const struct snd_soc_dapm_widget rk_dapm_widgets[] = {
51         SND_SOC_DAPM_HP("Analog", rk_hp_power),
52         SND_SOC_DAPM_LINE("HDMI", NULL),
53 };
54
55 static const struct snd_kcontrol_new rk_mc_controls[] = {
56         SOC_DAPM_PIN_SWITCH("Analog"),
57         SOC_DAPM_PIN_SWITCH("HDMI"),
58 };
59
60 static int rk_hw_params(struct snd_pcm_substream *substream,
61                         struct snd_pcm_hw_params *params)
62 {
63         int ret = 0;
64         struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
65         struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
66         struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
67         int mclk;
68
69         switch (params_rate(params)) {
70         case 8000:
71         case 16000:
72         case 24000:
73         case 32000:
74         case 48000:
75         case 64000:
76         case 96000:
77                 mclk = 12288000;
78                 break;
79         case 192000:
80                 mclk = 24576000;
81                 break;
82         case 11025:
83         case 22050:
84         case 44100:
85         case 88200:
86                 mclk = 11289600;
87                 break;
88         default:
89                 return -EINVAL;
90         }
91
92         ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
93                                      SND_SOC_CLOCK_OUT);
94
95         if (ret && ret != -ENOTSUPP) {
96                 dev_err(codec_dai->dev, "Can't set cpu clock %d\n", ret);
97                 return ret;
98         }
99
100         ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
101                                      SND_SOC_CLOCK_IN);
102         if (ret && ret != -ENOTSUPP) {
103                 dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret);
104                 return ret;
105         }
106
107         return 0;
108 }
109
110 static struct snd_soc_jack_gpio rk_hp_jack_gpio = {
111         .name = "rockchip,hp-det",
112         .report = SND_JACK_HEADPHONE,
113         .debounce_time = 150
114 };
115
116 static int rk_init(struct snd_soc_pcm_runtime *runtime)
117 {
118         struct snd_soc_card *card = runtime->card;
119         struct device *dev = card->dev;
120
121         /* Enable optional Headset Jack detection */
122         if (of_property_present(dev->of_node, "rockchip,hp-det-gpios")) {
123                 rk_hp_jack_gpio.gpiod_dev = dev;
124                 snd_soc_card_jack_new_pins(runtime->card, "Headphone Jack",
125                                            SND_JACK_HEADPHONE, &headphone_jack,
126                                            headphone_jack_pins,
127                                            ARRAY_SIZE(headphone_jack_pins));
128                 snd_soc_jack_add_gpios(&headphone_jack, 1, &rk_hp_jack_gpio);
129         }
130
131         return 0;
132 }
133
134 static const struct snd_soc_ops rk_ops = {
135         .hw_params = rk_hw_params,
136 };
137
138 SND_SOC_DAILINK_DEFS(audio,
139         DAILINK_COMP_ARRAY(COMP_EMPTY()),
140         DAILINK_COMP_ARRAY(COMP_CODEC(NULL, NULL),
141                            COMP_CODEC("hdmi-audio-codec.2.auto", "i2s-hifi")),
142         DAILINK_COMP_ARRAY(COMP_EMPTY()));
143
144 static struct snd_soc_dai_link rk_dailink = {
145         .name = "Codecs",
146         .stream_name = "Audio",
147         .init = rk_init,
148         .ops = &rk_ops,
149         /* Set codecs as slave */
150         .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
151                 SND_SOC_DAIFMT_CBS_CFS,
152         SND_SOC_DAILINK_REG(audio),
153 };
154
155 static struct snd_soc_card snd_soc_card_rk = {
156         .name = "ROCKCHIP-I2S",
157         .dai_link = &rk_dailink,
158         .num_links = 1,
159         .num_aux_devs = 0,
160         .dapm_widgets = rk_dapm_widgets,
161         .num_dapm_widgets = ARRAY_SIZE(rk_dapm_widgets),
162         .controls = rk_mc_controls,
163         .num_controls = ARRAY_SIZE(rk_mc_controls),
164 };
165
166 static int snd_rk_mc_probe(struct platform_device *pdev)
167 {
168         int ret;
169         struct snd_soc_card *card = &snd_soc_card_rk;
170         struct device_node *np = pdev->dev.of_node;
171         struct rk_drvdata *machine;
172         struct of_phandle_args args;
173
174         machine = devm_kzalloc(&pdev->dev, sizeof(struct rk_drvdata),
175                                GFP_KERNEL);
176         if (!machine)
177                 return -ENOMEM;
178
179         card->dev = &pdev->dev;
180
181         machine->gpio_hp_en = devm_gpiod_get_optional(&pdev->dev, "rockchip,hp-en", GPIOD_OUT_LOW);
182         if (IS_ERR(machine->gpio_hp_en))
183                 return PTR_ERR(machine->gpio_hp_en);
184         gpiod_set_consumer_name(machine->gpio_hp_en, "hp_en");
185
186         ret = snd_soc_of_parse_card_name(card, "rockchip,model");
187         if (ret) {
188                 dev_err(card->dev, "SoC parse card name failed %d\n", ret);
189                 return ret;
190         }
191
192         rk_dailink.codecs[0].of_node = of_parse_phandle(np,
193                                                         "rockchip,audio-codec",
194                                                         0);
195         if (!rk_dailink.codecs[0].of_node) {
196                 dev_err(&pdev->dev,
197                         "Property 'rockchip,audio-codec' missing or invalid\n");
198                 return -EINVAL;
199         }
200         ret = of_parse_phandle_with_fixed_args(np, "rockchip,audio-codec",
201                                                0, 0, &args);
202         if (ret) {
203                 dev_err(&pdev->dev,
204                         "Unable to parse property 'rockchip,audio-codec'\n");
205                 return ret;
206         }
207
208         ret = snd_soc_get_dai_name(&args, &rk_dailink.codecs[0].dai_name);
209         if (ret) {
210                 dev_err(&pdev->dev, "Unable to get codec_dai_name\n");
211                 return ret;
212         }
213
214         rk_dailink.cpus->of_node = of_parse_phandle(np, "rockchip,i2s-controller",
215                                                   0);
216         if (!rk_dailink.cpus->of_node) {
217                 dev_err(&pdev->dev,
218                         "Property 'rockchip,i2s-controller' missing or invalid\n");
219                 return -EINVAL;
220         }
221
222         rk_dailink.platforms->of_node = rk_dailink.cpus->of_node;
223
224         ret = snd_soc_of_parse_audio_routing(card, "rockchip,routing");
225         if (ret) {
226                 dev_err(&pdev->dev,
227                         "Unable to parse 'rockchip,routing' property\n");
228                 return ret;
229         }
230
231         snd_soc_card_set_drvdata(card, machine);
232
233         ret = devm_snd_soc_register_card(&pdev->dev, card);
234         if (ret)
235                 return dev_err_probe(&pdev->dev, ret,
236                                      "Soc register card failed\n");
237
238         return 0;
239 }
240
241 static const struct of_device_id rockchip_sound_of_match[] = {
242         { .compatible = "rockchip,rk3288-hdmi-analog", },
243         {},
244 };
245
246 MODULE_DEVICE_TABLE(of, rockchip_sound_of_match);
247
248 static struct platform_driver rockchip_sound_driver = {
249         .probe = snd_rk_mc_probe,
250         .driver = {
251                 .name = DRV_NAME,
252                 .pm = &snd_soc_pm_ops,
253                 .of_match_table = rockchip_sound_of_match,
254         },
255 };
256
257 module_platform_driver(rockchip_sound_driver);
258
259 MODULE_AUTHOR("Sjoerd Simons <sjoerd.simons@collabora.com>");
260 MODULE_DESCRIPTION("Rockchip RK3288 machine ASoC driver");
261 MODULE_LICENSE("GPL v2");
262 MODULE_ALIAS("platform:" DRV_NAME);