Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Rockchip machine ASoC driver for RK3288 boards that have an HDMI and analog
0004  * audio output
0005  *
0006  * Copyright (c) 2016, Collabora Ltd.
0007  *
0008  * Authors: Sjoerd Simons <sjoerd.simons@collabora.com>,
0009  *      Romain Perier <romain.perier@collabora.com>
0010  */
0011 
0012 #include <linux/module.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/slab.h>
0015 #include <linux/gpio.h>
0016 #include <linux/of_gpio.h>
0017 #include <sound/core.h>
0018 #include <sound/jack.h>
0019 #include <sound/pcm.h>
0020 #include <sound/pcm_params.h>
0021 #include <sound/soc.h>
0022 #include <sound/soc-dapm.h>
0023 
0024 #include "rockchip_i2s.h"
0025 
0026 #define DRV_NAME "rk3288-snd-hdmi-analog"
0027 
0028 struct rk_drvdata {
0029     int gpio_hp_en;
0030     int gpio_hp_det;
0031 };
0032 
0033 static int rk_hp_power(struct snd_soc_dapm_widget *w,
0034                struct snd_kcontrol *k, int event)
0035 {
0036     struct rk_drvdata *machine = snd_soc_card_get_drvdata(w->dapm->card);
0037 
0038     if (!gpio_is_valid(machine->gpio_hp_en))
0039         return 0;
0040 
0041     gpio_set_value_cansleep(machine->gpio_hp_en,
0042                 SND_SOC_DAPM_EVENT_ON(event));
0043 
0044     return 0;
0045 }
0046 
0047 static struct snd_soc_jack headphone_jack;
0048 static struct snd_soc_jack_pin headphone_jack_pins[] = {
0049     {
0050         .pin = "Analog",
0051         .mask = SND_JACK_HEADPHONE
0052     },
0053 };
0054 
0055 static const struct snd_soc_dapm_widget rk_dapm_widgets[] = {
0056     SND_SOC_DAPM_HP("Analog", rk_hp_power),
0057     SND_SOC_DAPM_LINE("HDMI", NULL),
0058 };
0059 
0060 static const struct snd_kcontrol_new rk_mc_controls[] = {
0061     SOC_DAPM_PIN_SWITCH("Analog"),
0062     SOC_DAPM_PIN_SWITCH("HDMI"),
0063 };
0064 
0065 static int rk_hw_params(struct snd_pcm_substream *substream,
0066             struct snd_pcm_hw_params *params)
0067 {
0068     int ret = 0;
0069     struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0070     struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
0071     struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
0072     int mclk;
0073 
0074     switch (params_rate(params)) {
0075     case 8000:
0076     case 16000:
0077     case 24000:
0078     case 32000:
0079     case 48000:
0080     case 64000:
0081     case 96000:
0082         mclk = 12288000;
0083         break;
0084     case 192000:
0085         mclk = 24576000;
0086         break;
0087     case 11025:
0088     case 22050:
0089     case 44100:
0090     case 88200:
0091         mclk = 11289600;
0092         break;
0093     default:
0094         return -EINVAL;
0095     }
0096 
0097     ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
0098                      SND_SOC_CLOCK_OUT);
0099 
0100     if (ret && ret != -ENOTSUPP) {
0101         dev_err(codec_dai->dev, "Can't set cpu clock %d\n", ret);
0102         return ret;
0103     }
0104 
0105     ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
0106                      SND_SOC_CLOCK_IN);
0107     if (ret && ret != -ENOTSUPP) {
0108         dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret);
0109         return ret;
0110     }
0111 
0112     return 0;
0113 }
0114 
0115 static struct snd_soc_jack_gpio rk_hp_jack_gpio = {
0116     .name = "Headphone detection",
0117     .report = SND_JACK_HEADPHONE,
0118     .debounce_time = 150
0119 };
0120 
0121 static int rk_init(struct snd_soc_pcm_runtime *runtime)
0122 {
0123     struct rk_drvdata *machine = snd_soc_card_get_drvdata(runtime->card);
0124 
0125     /* Enable Headset Jack detection */
0126     if (gpio_is_valid(machine->gpio_hp_det)) {
0127         snd_soc_card_jack_new_pins(runtime->card, "Headphone Jack",
0128                        SND_JACK_HEADPHONE, &headphone_jack,
0129                        headphone_jack_pins,
0130                        ARRAY_SIZE(headphone_jack_pins));
0131         rk_hp_jack_gpio.gpio = machine->gpio_hp_det;
0132         snd_soc_jack_add_gpios(&headphone_jack, 1, &rk_hp_jack_gpio);
0133     }
0134 
0135     return 0;
0136 }
0137 
0138 static const struct snd_soc_ops rk_ops = {
0139     .hw_params = rk_hw_params,
0140 };
0141 
0142 SND_SOC_DAILINK_DEFS(audio,
0143     DAILINK_COMP_ARRAY(COMP_EMPTY()),
0144     DAILINK_COMP_ARRAY(COMP_CODEC(NULL, NULL),
0145                COMP_CODEC("hdmi-audio-codec.2.auto", "i2s-hifi")),
0146     DAILINK_COMP_ARRAY(COMP_EMPTY()));
0147 
0148 static struct snd_soc_dai_link rk_dailink = {
0149     .name = "Codecs",
0150     .stream_name = "Audio",
0151     .init = rk_init,
0152     .ops = &rk_ops,
0153     /* Set codecs as slave */
0154     .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
0155         SND_SOC_DAIFMT_CBS_CFS,
0156     SND_SOC_DAILINK_REG(audio),
0157 };
0158 
0159 static struct snd_soc_card snd_soc_card_rk = {
0160     .name = "ROCKCHIP-I2S",
0161     .dai_link = &rk_dailink,
0162     .num_links = 1,
0163     .num_aux_devs = 0,
0164     .dapm_widgets = rk_dapm_widgets,
0165     .num_dapm_widgets = ARRAY_SIZE(rk_dapm_widgets),
0166     .controls = rk_mc_controls,
0167     .num_controls = ARRAY_SIZE(rk_mc_controls),
0168 };
0169 
0170 static int snd_rk_mc_probe(struct platform_device *pdev)
0171 {
0172     int ret;
0173     struct snd_soc_card *card = &snd_soc_card_rk;
0174     struct device_node *np = pdev->dev.of_node;
0175     struct rk_drvdata *machine;
0176     struct of_phandle_args args;
0177 
0178     machine = devm_kzalloc(&pdev->dev, sizeof(struct rk_drvdata),
0179                    GFP_KERNEL);
0180     if (!machine)
0181         return -ENOMEM;
0182 
0183     card->dev = &pdev->dev;
0184 
0185     machine->gpio_hp_det = of_get_named_gpio(np,
0186         "rockchip,hp-det-gpios", 0);
0187     if (!gpio_is_valid(machine->gpio_hp_det) && machine->gpio_hp_det != -ENODEV)
0188         return machine->gpio_hp_det;
0189 
0190     machine->gpio_hp_en = of_get_named_gpio(np,
0191         "rockchip,hp-en-gpios", 0);
0192     if (!gpio_is_valid(machine->gpio_hp_en) && machine->gpio_hp_en != -ENODEV)
0193         return machine->gpio_hp_en;
0194 
0195     if (gpio_is_valid(machine->gpio_hp_en)) {
0196         ret = devm_gpio_request_one(&pdev->dev, machine->gpio_hp_en,
0197                         GPIOF_OUT_INIT_LOW, "hp_en");
0198         if (ret) {
0199             dev_err(card->dev, "cannot get hp_en gpio\n");
0200             return ret;
0201         }
0202     }
0203 
0204     ret = snd_soc_of_parse_card_name(card, "rockchip,model");
0205     if (ret) {
0206         dev_err(card->dev, "SoC parse card name failed %d\n", ret);
0207         return ret;
0208     }
0209 
0210     rk_dailink.codecs[0].of_node = of_parse_phandle(np,
0211                             "rockchip,audio-codec",
0212                             0);
0213     if (!rk_dailink.codecs[0].of_node) {
0214         dev_err(&pdev->dev,
0215             "Property 'rockchip,audio-codec' missing or invalid\n");
0216         return -EINVAL;
0217     }
0218     ret = of_parse_phandle_with_fixed_args(np, "rockchip,audio-codec",
0219                            0, 0, &args);
0220     if (ret) {
0221         dev_err(&pdev->dev,
0222             "Unable to parse property 'rockchip,audio-codec'\n");
0223         return ret;
0224     }
0225 
0226     ret = snd_soc_get_dai_name(&args, &rk_dailink.codecs[0].dai_name);
0227     if (ret) {
0228         dev_err(&pdev->dev, "Unable to get codec_dai_name\n");
0229         return ret;
0230     }
0231 
0232     rk_dailink.cpus->of_node = of_parse_phandle(np, "rockchip,i2s-controller",
0233                           0);
0234     if (!rk_dailink.cpus->of_node) {
0235         dev_err(&pdev->dev,
0236             "Property 'rockchip,i2s-controller' missing or invalid\n");
0237         return -EINVAL;
0238     }
0239 
0240     rk_dailink.platforms->of_node = rk_dailink.cpus->of_node;
0241 
0242     ret = snd_soc_of_parse_audio_routing(card, "rockchip,routing");
0243     if (ret) {
0244         dev_err(&pdev->dev,
0245             "Unable to parse 'rockchip,routing' property\n");
0246         return ret;
0247     }
0248 
0249     snd_soc_card_set_drvdata(card, machine);
0250 
0251     ret = devm_snd_soc_register_card(&pdev->dev, card);
0252     if (ret)
0253         return dev_err_probe(&pdev->dev, ret,
0254                      "Soc register card failed\n");
0255 
0256     return 0;
0257 }
0258 
0259 static const struct of_device_id rockchip_sound_of_match[] = {
0260     { .compatible = "rockchip,rk3288-hdmi-analog", },
0261     {},
0262 };
0263 
0264 MODULE_DEVICE_TABLE(of, rockchip_sound_of_match);
0265 
0266 static struct platform_driver rockchip_sound_driver = {
0267     .probe = snd_rk_mc_probe,
0268     .driver = {
0269         .name = DRV_NAME,
0270         .pm = &snd_soc_pm_ops,
0271         .of_match_table = rockchip_sound_of_match,
0272     },
0273 };
0274 
0275 module_platform_driver(rockchip_sound_driver);
0276 
0277 MODULE_AUTHOR("Sjoerd Simons <sjoerd.simons@collabora.com>");
0278 MODULE_DESCRIPTION("Rockchip RK3288 machine ASoC driver");
0279 MODULE_LICENSE("GPL v2");
0280 MODULE_ALIAS("platform:" DRV_NAME);