1ad47191aSFabio Estevam // SPDX-License-Identifier: GPL-2.0 2ad47191aSFabio Estevam // 3ad47191aSFabio Estevam // Freescale ALSA SoC Machine driver utility 4ad47191aSFabio Estevam // 5ad47191aSFabio Estevam // Author: Timur Tabi <timur@freescale.com> 6ad47191aSFabio Estevam // 7ad47191aSFabio Estevam // Copyright 2010 Freescale Semiconductor, Inc. 860aae8daSShawn Guo 97bad8125SShengjiu Wang #include <linux/clk.h> 107bad8125SShengjiu Wang #include <linux/clk-provider.h> 1160aae8daSShawn Guo #include <linux/module.h> 1260aae8daSShawn Guo #include <linux/of_address.h> 1360aae8daSShawn Guo #include <sound/soc.h> 1460aae8daSShawn Guo 1560aae8daSShawn Guo #include "fsl_utils.h" 1660aae8daSShawn Guo 1760aae8daSShawn Guo /** 1860aae8daSShawn Guo * fsl_asoc_get_dma_channel - determine the dma channel for a SSI node 1960aae8daSShawn Guo * 2060aae8daSShawn Guo * @ssi_np: pointer to the SSI device tree node 2160aae8daSShawn Guo * @name: name of the phandle pointing to the dma channel 2260aae8daSShawn Guo * @dai: ASoC DAI link pointer to be filled with platform_name 2360aae8daSShawn Guo * @dma_channel_id: dma channel id to be returned 2460aae8daSShawn Guo * @dma_id: dma id to be returned 2560aae8daSShawn Guo * 2660aae8daSShawn Guo * This function determines the dma and channel id for given SSI node. It 2760aae8daSShawn Guo * also discovers the platform_name for the ASoC DAI link. 2860aae8daSShawn Guo */ 2960aae8daSShawn Guo int fsl_asoc_get_dma_channel(struct device_node *ssi_np, 3060aae8daSShawn Guo const char *name, 3160aae8daSShawn Guo struct snd_soc_dai_link *dai, 3260aae8daSShawn Guo unsigned int *dma_channel_id, 3360aae8daSShawn Guo unsigned int *dma_id) 3460aae8daSShawn Guo { 3560aae8daSShawn Guo struct resource res; 3660aae8daSShawn Guo struct device_node *dma_channel_np, *dma_np; 37cb3981b6SFabio Estevam const __be32 *iprop; 3860aae8daSShawn Guo int ret; 3960aae8daSShawn Guo 4060aae8daSShawn Guo dma_channel_np = of_parse_phandle(ssi_np, name, 0); 4160aae8daSShawn Guo if (!dma_channel_np) 4260aae8daSShawn Guo return -EINVAL; 4360aae8daSShawn Guo 4460aae8daSShawn Guo if (!of_device_is_compatible(dma_channel_np, "fsl,ssi-dma-channel")) { 4560aae8daSShawn Guo of_node_put(dma_channel_np); 4660aae8daSShawn Guo return -EINVAL; 4760aae8daSShawn Guo } 4860aae8daSShawn Guo 4960aae8daSShawn Guo /* Determine the dev_name for the device_node. This code mimics the 5060aae8daSShawn Guo * behavior of of_device_make_bus_id(). We need this because ASoC uses 5160aae8daSShawn Guo * the dev_name() of the device to match the platform (DMA) device with 5260aae8daSShawn Guo * the CPU (SSI) device. It's all ugly and hackish, but it works (for 5360aae8daSShawn Guo * now). 5460aae8daSShawn Guo * 5560aae8daSShawn Guo * dai->platform name should already point to an allocated buffer. 5660aae8daSShawn Guo */ 5760aae8daSShawn Guo ret = of_address_to_resource(dma_channel_np, 0, &res); 5860aae8daSShawn Guo if (ret) { 5960aae8daSShawn Guo of_node_put(dma_channel_np); 6060aae8daSShawn Guo return ret; 6160aae8daSShawn Guo } 626aa7b409SKuninori Morimoto snprintf((char *)dai->platforms->name, DAI_NAME_SIZE, "%llx.%pOFn", 635d585e1eSRob Herring (unsigned long long) res.start, dma_channel_np); 6460aae8daSShawn Guo 6560aae8daSShawn Guo iprop = of_get_property(dma_channel_np, "cell-index", NULL); 6660aae8daSShawn Guo if (!iprop) { 6760aae8daSShawn Guo of_node_put(dma_channel_np); 6860aae8daSShawn Guo return -EINVAL; 6960aae8daSShawn Guo } 7060aae8daSShawn Guo *dma_channel_id = be32_to_cpup(iprop); 7160aae8daSShawn Guo 7260aae8daSShawn Guo dma_np = of_get_parent(dma_channel_np); 7360aae8daSShawn Guo iprop = of_get_property(dma_np, "cell-index", NULL); 7460aae8daSShawn Guo if (!iprop) { 7560aae8daSShawn Guo of_node_put(dma_np); 76c7052471SWen Yang of_node_put(dma_channel_np); 7760aae8daSShawn Guo return -EINVAL; 7860aae8daSShawn Guo } 7960aae8daSShawn Guo *dma_id = be32_to_cpup(iprop); 8060aae8daSShawn Guo 8160aae8daSShawn Guo of_node_put(dma_np); 8260aae8daSShawn Guo of_node_put(dma_channel_np); 8360aae8daSShawn Guo 8460aae8daSShawn Guo return 0; 8560aae8daSShawn Guo } 8660aae8daSShawn Guo EXPORT_SYMBOL(fsl_asoc_get_dma_channel); 8760aae8daSShawn Guo 887bad8125SShengjiu Wang /** 897bad8125SShengjiu Wang * fsl_asoc_get_pll_clocks - get two PLL clock source 907bad8125SShengjiu Wang * 917bad8125SShengjiu Wang * @dev: device pointer 927bad8125SShengjiu Wang * @pll8k_clk: PLL clock pointer for 8kHz 937bad8125SShengjiu Wang * @pll11k_clk: PLL clock pointer for 11kHz 947bad8125SShengjiu Wang * 957bad8125SShengjiu Wang * This function get two PLL clock source 967bad8125SShengjiu Wang */ 977bad8125SShengjiu Wang void fsl_asoc_get_pll_clocks(struct device *dev, struct clk **pll8k_clk, 987bad8125SShengjiu Wang struct clk **pll11k_clk) 997bad8125SShengjiu Wang { 1007bad8125SShengjiu Wang *pll8k_clk = devm_clk_get(dev, "pll8k"); 1017bad8125SShengjiu Wang if (IS_ERR(*pll8k_clk)) 1027bad8125SShengjiu Wang *pll8k_clk = NULL; 1037bad8125SShengjiu Wang 1047bad8125SShengjiu Wang *pll11k_clk = devm_clk_get(dev, "pll11k"); 1057bad8125SShengjiu Wang if (IS_ERR(*pll11k_clk)) 1067bad8125SShengjiu Wang *pll11k_clk = NULL; 1077bad8125SShengjiu Wang } 1087bad8125SShengjiu Wang EXPORT_SYMBOL(fsl_asoc_get_pll_clocks); 1097bad8125SShengjiu Wang 1107bad8125SShengjiu Wang /** 1117bad8125SShengjiu Wang * fsl_asoc_reparent_pll_clocks - set clock parent if necessary 1127bad8125SShengjiu Wang * 1137bad8125SShengjiu Wang * @dev: device pointer 1147bad8125SShengjiu Wang * @clk: root clock pointer 1157bad8125SShengjiu Wang * @pll8k_clk: PLL clock pointer for 8kHz 1167bad8125SShengjiu Wang * @pll11k_clk: PLL clock pointer for 11kHz 1177bad8125SShengjiu Wang * @ratio: target requency for root clock 1187bad8125SShengjiu Wang * 1197bad8125SShengjiu Wang * This function set root clock parent according to the target ratio 1207bad8125SShengjiu Wang */ 1217bad8125SShengjiu Wang void fsl_asoc_reparent_pll_clocks(struct device *dev, struct clk *clk, 1227bad8125SShengjiu Wang struct clk *pll8k_clk, 1237bad8125SShengjiu Wang struct clk *pll11k_clk, u64 ratio) 1247bad8125SShengjiu Wang { 125050237e6SShengjiu Wang struct clk *p, *pll = NULL, *npll = NULL; 1267bad8125SShengjiu Wang bool reparent = false; 1277bad8125SShengjiu Wang int ret = 0; 1287bad8125SShengjiu Wang 1297bad8125SShengjiu Wang if (!clk || !pll8k_clk || !pll11k_clk) 1307bad8125SShengjiu Wang return; 1317bad8125SShengjiu Wang 1327bad8125SShengjiu Wang p = clk; 1337bad8125SShengjiu Wang while (p && pll8k_clk && pll11k_clk) { 1347bad8125SShengjiu Wang struct clk *pp = clk_get_parent(p); 1357bad8125SShengjiu Wang 1367bad8125SShengjiu Wang if (clk_is_match(pp, pll8k_clk) || 1377bad8125SShengjiu Wang clk_is_match(pp, pll11k_clk)) { 1387bad8125SShengjiu Wang pll = pp; 1397bad8125SShengjiu Wang break; 1407bad8125SShengjiu Wang } 1417bad8125SShengjiu Wang p = pp; 1427bad8125SShengjiu Wang } 1437bad8125SShengjiu Wang 1447bad8125SShengjiu Wang npll = (do_div(ratio, 8000) ? pll11k_clk : pll8k_clk); 1457bad8125SShengjiu Wang reparent = (pll && !clk_is_match(pll, npll)); 1467bad8125SShengjiu Wang 1477bad8125SShengjiu Wang if (reparent) { 1487bad8125SShengjiu Wang ret = clk_set_parent(p, npll); 1497bad8125SShengjiu Wang if (ret < 0) 150*eaa27e7fSShengjiu Wang dev_warn(dev, "failed to set parent:%d\n", ret); 1517bad8125SShengjiu Wang } 1527bad8125SShengjiu Wang } 1537bad8125SShengjiu Wang EXPORT_SYMBOL(fsl_asoc_reparent_pll_clocks); 1547bad8125SShengjiu Wang 15560aae8daSShawn Guo MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); 15660aae8daSShawn Guo MODULE_DESCRIPTION("Freescale ASoC utility code"); 15760aae8daSShawn Guo MODULE_LICENSE("GPL v2"); 158