158 lines · c
1// SPDX-License-Identifier: GPL-2.02//3// Freescale ALSA SoC Machine driver utility4//5// Author: Timur Tabi <timur@freescale.com>6//7// Copyright 2010 Freescale Semiconductor, Inc.8 9#include <linux/clk.h>10#include <linux/clk-provider.h>11#include <linux/module.h>12#include <linux/of_address.h>13#include <sound/soc.h>14 15#include "fsl_utils.h"16 17/**18 * fsl_asoc_get_dma_channel - determine the dma channel for a SSI node19 *20 * @ssi_np: pointer to the SSI device tree node21 * @name: name of the phandle pointing to the dma channel22 * @dai: ASoC DAI link pointer to be filled with platform_name23 * @dma_channel_id: dma channel id to be returned24 * @dma_id: dma id to be returned25 *26 * This function determines the dma and channel id for given SSI node. It27 * also discovers the platform_name for the ASoC DAI link.28 */29int fsl_asoc_get_dma_channel(struct device_node *ssi_np,30 const char *name,31 struct snd_soc_dai_link *dai,32 unsigned int *dma_channel_id,33 unsigned int *dma_id)34{35 struct resource res;36 struct device_node *dma_channel_np, *dma_np;37 const __be32 *iprop;38 int ret;39 40 dma_channel_np = of_parse_phandle(ssi_np, name, 0);41 if (!dma_channel_np)42 return -EINVAL;43 44 if (!of_device_is_compatible(dma_channel_np, "fsl,ssi-dma-channel")) {45 of_node_put(dma_channel_np);46 return -EINVAL;47 }48 49 /* Determine the dev_name for the device_node. This code mimics the50 * behavior of of_device_make_bus_id(). We need this because ASoC uses51 * the dev_name() of the device to match the platform (DMA) device with52 * the CPU (SSI) device. It's all ugly and hackish, but it works (for53 * now).54 *55 * dai->platform name should already point to an allocated buffer.56 */57 ret = of_address_to_resource(dma_channel_np, 0, &res);58 if (ret) {59 of_node_put(dma_channel_np);60 return ret;61 }62 snprintf((char *)dai->platforms->name, DAI_NAME_SIZE, "%llx.%pOFn",63 (unsigned long long) res.start, dma_channel_np);64 65 iprop = of_get_property(dma_channel_np, "cell-index", NULL);66 if (!iprop) {67 of_node_put(dma_channel_np);68 return -EINVAL;69 }70 *dma_channel_id = be32_to_cpup(iprop);71 72 dma_np = of_get_parent(dma_channel_np);73 iprop = of_get_property(dma_np, "cell-index", NULL);74 if (!iprop) {75 of_node_put(dma_np);76 of_node_put(dma_channel_np);77 return -EINVAL;78 }79 *dma_id = be32_to_cpup(iprop);80 81 of_node_put(dma_np);82 of_node_put(dma_channel_np);83 84 return 0;85}86EXPORT_SYMBOL(fsl_asoc_get_dma_channel);87 88/**89 * fsl_asoc_get_pll_clocks - get two PLL clock source90 *91 * @dev: device pointer92 * @pll8k_clk: PLL clock pointer for 8kHz93 * @pll11k_clk: PLL clock pointer for 11kHz94 *95 * This function get two PLL clock source96 */97void fsl_asoc_get_pll_clocks(struct device *dev, struct clk **pll8k_clk,98 struct clk **pll11k_clk)99{100 *pll8k_clk = devm_clk_get(dev, "pll8k");101 if (IS_ERR(*pll8k_clk))102 *pll8k_clk = NULL;103 104 *pll11k_clk = devm_clk_get(dev, "pll11k");105 if (IS_ERR(*pll11k_clk))106 *pll11k_clk = NULL;107}108EXPORT_SYMBOL(fsl_asoc_get_pll_clocks);109 110/**111 * fsl_asoc_reparent_pll_clocks - set clock parent if necessary112 *113 * @dev: device pointer114 * @clk: root clock pointer115 * @pll8k_clk: PLL clock pointer for 8kHz116 * @pll11k_clk: PLL clock pointer for 11kHz117 * @ratio: target requency for root clock118 *119 * This function set root clock parent according to the target ratio120 */121void fsl_asoc_reparent_pll_clocks(struct device *dev, struct clk *clk,122 struct clk *pll8k_clk,123 struct clk *pll11k_clk, u64 ratio)124{125 struct clk *p, *pll = NULL, *npll = NULL;126 bool reparent = false;127 int ret;128 129 if (!clk || !pll8k_clk || !pll11k_clk)130 return;131 132 p = clk;133 while (p && pll8k_clk && pll11k_clk) {134 struct clk *pp = clk_get_parent(p);135 136 if (clk_is_match(pp, pll8k_clk) ||137 clk_is_match(pp, pll11k_clk)) {138 pll = pp;139 break;140 }141 p = pp;142 }143 144 npll = (do_div(ratio, 8000) ? pll11k_clk : pll8k_clk);145 reparent = (pll && !clk_is_match(pll, npll));146 147 if (reparent) {148 ret = clk_set_parent(p, npll);149 if (ret < 0)150 dev_warn(dev, "failed to set parent:%d\n", ret);151 }152}153EXPORT_SYMBOL(fsl_asoc_reparent_pll_clocks);154 155MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");156MODULE_DESCRIPTION("Freescale ASoC utility code");157MODULE_LICENSE("GPL v2");158