Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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