~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/rockchip/rk3288_hdmi_analog.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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);
263 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php