255 lines · c
1// SPDX-License-Identifier: GPL-2.02//3// ASoC machine driver for Snow boards4 5#include <linux/clk.h>6#include <linux/module.h>7#include <linux/platform_device.h>8#include <linux/of.h>9#include <sound/pcm_params.h>10#include <sound/soc.h>11 12#include "i2s.h"13 14#define FIN_PLL_RATE 2400000015 16SND_SOC_DAILINK_DEFS(links,17 DAILINK_COMP_ARRAY(COMP_EMPTY()),18 DAILINK_COMP_ARRAY(COMP_EMPTY()),19 DAILINK_COMP_ARRAY(COMP_EMPTY()));20 21struct snow_priv {22 struct snd_soc_dai_link dai_link;23 struct clk *clk_i2s_bus;24};25 26static int snow_card_hw_params(struct snd_pcm_substream *substream,27 struct snd_pcm_hw_params *params)28{29 static const unsigned int pll_rate[] = {30 73728000U, 67737602U, 49152000U, 45158401U, 32768001U31 };32 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);33 struct snow_priv *priv = snd_soc_card_get_drvdata(rtd->card);34 int bfs, psr, rfs, bitwidth;35 unsigned long int rclk;36 long int freq = -EINVAL;37 int ret, i;38 39 bitwidth = snd_pcm_format_width(params_format(params));40 if (bitwidth < 0) {41 dev_err(rtd->card->dev, "Invalid bit-width: %d\n", bitwidth);42 return bitwidth;43 }44 45 if (bitwidth != 16 && bitwidth != 24) {46 dev_err(rtd->card->dev, "Unsupported bit-width: %d\n", bitwidth);47 return -EINVAL;48 }49 50 bfs = 2 * bitwidth;51 52 switch (params_rate(params)) {53 case 16000:54 case 22050:55 case 24000:56 case 32000:57 case 44100:58 case 48000:59 case 88200:60 case 96000:61 rfs = 8 * bfs;62 break;63 case 64000:64 rfs = 384;65 break;66 case 8000:67 case 11025:68 case 12000:69 rfs = 16 * bfs;70 break;71 default:72 return -EINVAL;73 }74 75 rclk = params_rate(params) * rfs;76 77 for (psr = 8; psr > 0; psr /= 2) {78 for (i = 0; i < ARRAY_SIZE(pll_rate); i++) {79 if ((pll_rate[i] - rclk * psr) <= 2) {80 freq = pll_rate[i];81 break;82 }83 }84 }85 if (freq < 0) {86 dev_err(rtd->card->dev, "Unsupported RCLK rate: %lu\n", rclk);87 return -EINVAL;88 }89 90 ret = clk_set_rate(priv->clk_i2s_bus, freq);91 if (ret < 0) {92 dev_err(rtd->card->dev, "I2S bus clock rate set failed\n");93 return ret;94 }95 96 return 0;97}98 99static const struct snd_soc_ops snow_card_ops = {100 .hw_params = snow_card_hw_params,101};102 103static int snow_late_probe(struct snd_soc_card *card)104{105 struct snd_soc_pcm_runtime *rtd;106 struct snd_soc_dai *codec_dai;107 108 rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);109 110 /* In the multi-codec case codec_dais 0 is MAX98095 and 1 is HDMI. */111 codec_dai = snd_soc_rtd_to_codec(rtd, 0);112 113 /* Set the MCLK rate for the codec */114 return snd_soc_dai_set_sysclk(codec_dai, 0,115 FIN_PLL_RATE, SND_SOC_CLOCK_IN);116}117 118static struct snd_soc_card snow_snd = {119 .name = "Snow-I2S",120 .owner = THIS_MODULE,121 .late_probe = snow_late_probe,122};123 124static int snow_probe(struct platform_device *pdev)125{126 struct device *dev = &pdev->dev;127 struct snd_soc_card *card = &snow_snd;128 struct device_node *cpu, *codec;129 struct snd_soc_dai_link *link;130 struct snow_priv *priv;131 int ret;132 133 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);134 if (!priv)135 return -ENOMEM;136 137 link = &priv->dai_link;138 139 link->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |140 SND_SOC_DAIFMT_CBS_CFS;141 142 link->name = "Primary";143 link->stream_name = link->name;144 145 link->cpus = links_cpus;146 link->num_cpus = ARRAY_SIZE(links_cpus);147 link->codecs = links_codecs;148 link->num_codecs = ARRAY_SIZE(links_codecs);149 link->platforms = links_platforms;150 link->num_platforms = ARRAY_SIZE(links_platforms);151 152 card->dai_link = link;153 card->num_links = 1;154 card->dev = dev;155 156 /* Try new DT bindings with HDMI support first. */157 cpu = of_get_child_by_name(dev->of_node, "cpu");158 159 if (cpu) {160 link->ops = &snow_card_ops;161 162 link->cpus->of_node = of_parse_phandle(cpu, "sound-dai", 0);163 of_node_put(cpu);164 165 if (!link->cpus->of_node) {166 dev_err(dev, "Failed parsing cpu/sound-dai property\n");167 return -EINVAL;168 }169 170 codec = of_get_child_by_name(dev->of_node, "codec");171 ret = snd_soc_of_get_dai_link_codecs(dev, codec, link);172 of_node_put(codec);173 174 if (ret < 0) {175 of_node_put(link->cpus->of_node);176 dev_err(dev, "Failed parsing codec node\n");177 return ret;178 }179 180 priv->clk_i2s_bus = of_clk_get_by_name(link->cpus->of_node,181 "i2s_opclk0");182 if (IS_ERR(priv->clk_i2s_bus)) {183 snd_soc_of_put_dai_link_codecs(link);184 of_node_put(link->cpus->of_node);185 return PTR_ERR(priv->clk_i2s_bus);186 }187 } else {188 link->codecs->dai_name = "HiFi";189 190 link->cpus->of_node = of_parse_phandle(dev->of_node,191 "samsung,i2s-controller", 0);192 if (!link->cpus->of_node) {193 dev_err(dev, "i2s-controller property parse error\n");194 return -EINVAL;195 }196 197 link->codecs->of_node = of_parse_phandle(dev->of_node,198 "samsung,audio-codec", 0);199 if (!link->codecs->of_node) {200 of_node_put(link->cpus->of_node);201 dev_err(dev, "audio-codec property parse error\n");202 return -EINVAL;203 }204 }205 206 link->platforms->of_node = link->cpus->of_node;207 208 /* Update card-name if provided through DT, else use default name */209 snd_soc_of_parse_card_name(card, "samsung,model");210 211 snd_soc_card_set_drvdata(card, priv);212 213 ret = devm_snd_soc_register_card(dev, card);214 if (ret)215 return dev_err_probe(&pdev->dev, ret,216 "snd_soc_register_card failed\n");217 218 return 0;219}220 221static void snow_remove(struct platform_device *pdev)222{223 struct snow_priv *priv = platform_get_drvdata(pdev);224 struct snd_soc_dai_link *link = &priv->dai_link;225 226 of_node_put(link->cpus->of_node);227 of_node_put(link->codecs->of_node);228 snd_soc_of_put_dai_link_codecs(link);229 230 clk_put(priv->clk_i2s_bus);231}232 233static const struct of_device_id snow_of_match[] = {234 { .compatible = "google,snow-audio-max98090", },235 { .compatible = "google,snow-audio-max98091", },236 { .compatible = "google,snow-audio-max98095", },237 {},238};239MODULE_DEVICE_TABLE(of, snow_of_match);240 241static struct platform_driver snow_driver = {242 .driver = {243 .name = "snow-audio",244 .pm = &snd_soc_pm_ops,245 .of_match_table = snow_of_match,246 },247 .probe = snow_probe,248 .remove = snow_remove,249};250 251module_platform_driver(snow_driver);252 253MODULE_DESCRIPTION("ALSA SoC Audio machine driver for Snow");254MODULE_LICENSE("GPL");255