brintos

brintos / linux-shallow public Read only

0
0
Text · 5.9 KiB · 4bbe7bc Raw
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