Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 //
0003 // ASoC machine driver for Snow boards
0004 
0005 #include <linux/clk.h>
0006 #include <linux/module.h>
0007 #include <linux/platform_device.h>
0008 #include <linux/of.h>
0009 #include <linux/of_device.h>
0010 #include <sound/pcm_params.h>
0011 #include <sound/soc.h>
0012 
0013 #include "i2s.h"
0014 
0015 #define FIN_PLL_RATE        24000000
0016 
0017 SND_SOC_DAILINK_DEFS(links,
0018     DAILINK_COMP_ARRAY(COMP_EMPTY()),
0019     DAILINK_COMP_ARRAY(COMP_EMPTY()),
0020     DAILINK_COMP_ARRAY(COMP_EMPTY()));
0021 
0022 struct snow_priv {
0023     struct snd_soc_dai_link dai_link;
0024     struct clk *clk_i2s_bus;
0025 };
0026 
0027 static int snow_card_hw_params(struct snd_pcm_substream *substream,
0028                       struct snd_pcm_hw_params *params)
0029 {
0030     static const unsigned int pll_rate[] = {
0031         73728000U, 67737602U, 49152000U, 45158401U, 32768001U
0032     };
0033     struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0034     struct snow_priv *priv = snd_soc_card_get_drvdata(rtd->card);
0035     int bfs, psr, rfs, bitwidth;
0036     unsigned long int rclk;
0037     long int freq = -EINVAL;
0038     int ret, i;
0039 
0040     bitwidth = snd_pcm_format_width(params_format(params));
0041     if (bitwidth < 0) {
0042         dev_err(rtd->card->dev, "Invalid bit-width: %d\n", bitwidth);
0043         return bitwidth;
0044     }
0045 
0046     if (bitwidth != 16 && bitwidth != 24) {
0047         dev_err(rtd->card->dev, "Unsupported bit-width: %d\n", bitwidth);
0048         return -EINVAL;
0049     }
0050 
0051     bfs = 2 * bitwidth;
0052 
0053     switch (params_rate(params)) {
0054     case 16000:
0055     case 22050:
0056     case 24000:
0057     case 32000:
0058     case 44100:
0059     case 48000:
0060     case 88200:
0061     case 96000:
0062         rfs = 8 * bfs;
0063         break;
0064     case 64000:
0065         rfs = 384;
0066         break;
0067     case 8000:
0068     case 11025:
0069     case 12000:
0070         rfs = 16 * bfs;
0071         break;
0072     default:
0073         return -EINVAL;
0074     }
0075 
0076     rclk = params_rate(params) * rfs;
0077 
0078     for (psr = 8; psr > 0; psr /= 2) {
0079         for (i = 0; i < ARRAY_SIZE(pll_rate); i++) {
0080             if ((pll_rate[i] - rclk * psr) <= 2) {
0081                 freq = pll_rate[i];
0082                 break;
0083             }
0084         }
0085     }
0086     if (freq < 0) {
0087         dev_err(rtd->card->dev, "Unsupported RCLK rate: %lu\n", rclk);
0088         return -EINVAL;
0089     }
0090 
0091     ret = clk_set_rate(priv->clk_i2s_bus, freq);
0092     if (ret < 0) {
0093         dev_err(rtd->card->dev, "I2S bus clock rate set failed\n");
0094         return ret;
0095     }
0096 
0097     return 0;
0098 }
0099 
0100 static const struct snd_soc_ops snow_card_ops = {
0101     .hw_params = snow_card_hw_params,
0102 };
0103 
0104 static int snow_late_probe(struct snd_soc_card *card)
0105 {
0106     struct snd_soc_pcm_runtime *rtd;
0107     struct snd_soc_dai *codec_dai;
0108 
0109     rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
0110 
0111     /* In the multi-codec case codec_dais 0 is MAX98095 and 1 is HDMI. */
0112     codec_dai = asoc_rtd_to_codec(rtd, 0);
0113 
0114     /* Set the MCLK rate for the codec */
0115     return snd_soc_dai_set_sysclk(codec_dai, 0,
0116                 FIN_PLL_RATE, SND_SOC_CLOCK_IN);
0117 }
0118 
0119 static struct snd_soc_card snow_snd = {
0120     .name = "Snow-I2S",
0121     .owner = THIS_MODULE,
0122     .late_probe = snow_late_probe,
0123 };
0124 
0125 static int snow_probe(struct platform_device *pdev)
0126 {
0127     struct device *dev = &pdev->dev;
0128     struct snd_soc_card *card = &snow_snd;
0129     struct device_node *cpu, *codec;
0130     struct snd_soc_dai_link *link;
0131     struct snow_priv *priv;
0132     int ret;
0133 
0134     priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
0135     if (!priv)
0136         return -ENOMEM;
0137 
0138     link = &priv->dai_link;
0139 
0140     link->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
0141             SND_SOC_DAIFMT_CBS_CFS;
0142 
0143     link->name = "Primary";
0144     link->stream_name = link->name;
0145 
0146     link->cpus = links_cpus;
0147     link->num_cpus = ARRAY_SIZE(links_cpus);
0148     link->codecs = links_codecs;
0149     link->num_codecs = ARRAY_SIZE(links_codecs);
0150     link->platforms = links_platforms;
0151     link->num_platforms = ARRAY_SIZE(links_platforms);
0152 
0153     card->dai_link = link;
0154     card->num_links = 1;
0155     card->dev = dev;
0156 
0157     /* Try new DT bindings with HDMI support first. */
0158     cpu = of_get_child_by_name(dev->of_node, "cpu");
0159 
0160     if (cpu) {
0161         link->ops = &snow_card_ops;
0162 
0163         link->cpus->of_node = of_parse_phandle(cpu, "sound-dai", 0);
0164         of_node_put(cpu);
0165 
0166         if (!link->cpus->of_node) {
0167             dev_err(dev, "Failed parsing cpu/sound-dai property\n");
0168             return -EINVAL;
0169         }
0170 
0171         codec = of_get_child_by_name(dev->of_node, "codec");
0172         ret = snd_soc_of_get_dai_link_codecs(dev, codec, link);
0173         of_node_put(codec);
0174 
0175         if (ret < 0) {
0176             of_node_put(link->cpus->of_node);
0177             dev_err(dev, "Failed parsing codec node\n");
0178             return ret;
0179         }
0180 
0181         priv->clk_i2s_bus = of_clk_get_by_name(link->cpus->of_node,
0182                                "i2s_opclk0");
0183         if (IS_ERR(priv->clk_i2s_bus)) {
0184             snd_soc_of_put_dai_link_codecs(link);
0185             of_node_put(link->cpus->of_node);
0186             return PTR_ERR(priv->clk_i2s_bus);
0187         }
0188     } else {
0189         link->codecs->dai_name = "HiFi";
0190 
0191         link->cpus->of_node = of_parse_phandle(dev->of_node,
0192                         "samsung,i2s-controller", 0);
0193         if (!link->cpus->of_node) {
0194             dev_err(dev, "i2s-controller property parse error\n");
0195             return -EINVAL;
0196         }
0197 
0198         link->codecs->of_node = of_parse_phandle(dev->of_node,
0199                         "samsung,audio-codec", 0);
0200         if (!link->codecs->of_node) {
0201             of_node_put(link->cpus->of_node);
0202             dev_err(dev, "audio-codec property parse error\n");
0203             return -EINVAL;
0204         }
0205     }
0206 
0207     link->platforms->of_node = link->cpus->of_node;
0208 
0209     /* Update card-name if provided through DT, else use default name */
0210     snd_soc_of_parse_card_name(card, "samsung,model");
0211 
0212     snd_soc_card_set_drvdata(card, priv);
0213 
0214     ret = devm_snd_soc_register_card(dev, card);
0215     if (ret)
0216         return dev_err_probe(&pdev->dev, ret,
0217                      "snd_soc_register_card failed\n");
0218 
0219     return 0;
0220 }
0221 
0222 static int snow_remove(struct platform_device *pdev)
0223 {
0224     struct snow_priv *priv = platform_get_drvdata(pdev);
0225     struct snd_soc_dai_link *link = &priv->dai_link;
0226 
0227     of_node_put(link->cpus->of_node);
0228     of_node_put(link->codecs->of_node);
0229     snd_soc_of_put_dai_link_codecs(link);
0230 
0231     clk_put(priv->clk_i2s_bus);
0232 
0233     return 0;
0234 }
0235 
0236 static const struct of_device_id snow_of_match[] = {
0237     { .compatible = "google,snow-audio-max98090", },
0238     { .compatible = "google,snow-audio-max98091", },
0239     { .compatible = "google,snow-audio-max98095", },
0240     {},
0241 };
0242 MODULE_DEVICE_TABLE(of, snow_of_match);
0243 
0244 static struct platform_driver snow_driver = {
0245     .driver = {
0246         .name = "snow-audio",
0247         .pm = &snd_soc_pm_ops,
0248         .of_match_table = snow_of_match,
0249     },
0250     .probe = snow_probe,
0251     .remove = snow_remove,
0252 };
0253 
0254 module_platform_driver(snow_driver);
0255 
0256 MODULE_DESCRIPTION("ALSA SoC Audio machine driver for Snow");
0257 MODULE_LICENSE("GPL");