Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/fsl/fsl_utils.c
26427 views
1
// SPDX-License-Identifier: GPL-2.0
2
//
3
// Freescale ALSA SoC Machine driver utility
4
//
5
// Author: Timur Tabi <[email protected]>
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 node
19
*
20
* @ssi_np: pointer to the SSI device tree node
21
* @name: name of the phandle pointing to the dma channel
22
* @dai: ASoC DAI link pointer to be filled with platform_name
23
* @dma_channel_id: dma channel id to be returned
24
* @dma_id: dma id to be returned
25
*
26
* This function determines the dma and channel id for given SSI node. It
27
* also discovers the platform_name for the ASoC DAI link.
28
*/
29
int 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 the
50
* behavior of of_device_make_bus_id(). We need this because ASoC uses
51
* the dev_name() of the device to match the platform (DMA) device with
52
* the CPU (SSI) device. It's all ugly and hackish, but it works (for
53
* 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
}
86
EXPORT_SYMBOL(fsl_asoc_get_dma_channel);
87
88
/**
89
* fsl_asoc_get_pll_clocks - get two PLL clock source
90
*
91
* @dev: device pointer
92
* @pll8k_clk: PLL clock pointer for 8kHz
93
* @pll11k_clk: PLL clock pointer for 11kHz
94
*
95
* This function get two PLL clock source
96
*/
97
void 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
}
108
EXPORT_SYMBOL(fsl_asoc_get_pll_clocks);
109
110
/**
111
* fsl_asoc_reparent_pll_clocks - set clock parent if necessary
112
*
113
* @dev: device pointer
114
* @clk: root clock pointer
115
* @pll8k_clk: PLL clock pointer for 8kHz
116
* @pll11k_clk: PLL clock pointer for 11kHz
117
* @ratio: target requency for root clock
118
*
119
* This function set root clock parent according to the target ratio
120
*/
121
void 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
}
153
EXPORT_SYMBOL(fsl_asoc_reparent_pll_clocks);
154
155
/**
156
* fsl_asoc_constrain_rates - constrain rates according to clocks
157
*
158
* @target_constr: target constraint
159
* @original_constr: original constraint
160
* @pll8k_clk: PLL clock pointer for 8kHz
161
* @pll11k_clk: PLL clock pointer for 11kHz
162
* @ext_clk: External clock pointer
163
* @target_rates: target rates array
164
*
165
* This function constrain rates according to clocks
166
*/
167
void fsl_asoc_constrain_rates(struct snd_pcm_hw_constraint_list *target_constr,
168
const struct snd_pcm_hw_constraint_list *original_constr,
169
struct clk *pll8k_clk, struct clk *pll11k_clk,
170
struct clk *ext_clk, int *target_rates)
171
{
172
int i, j, k = 0;
173
u64 clk_rate[3];
174
175
*target_constr = *original_constr;
176
if (pll8k_clk || pll11k_clk || ext_clk) {
177
target_constr->list = target_rates;
178
target_constr->count = 0;
179
for (i = 0; i < original_constr->count; i++) {
180
clk_rate[0] = clk_get_rate(pll8k_clk);
181
clk_rate[1] = clk_get_rate(pll11k_clk);
182
clk_rate[2] = clk_get_rate(ext_clk);
183
for (j = 0; j < 3; j++) {
184
if (clk_rate[j] != 0 &&
185
do_div(clk_rate[j], original_constr->list[i]) == 0) {
186
target_rates[k++] = original_constr->list[i];
187
target_constr->count++;
188
break;
189
}
190
}
191
}
192
193
/* protection for if there is no proper rate found*/
194
if (!target_constr->count)
195
*target_constr = *original_constr;
196
}
197
}
198
EXPORT_SYMBOL(fsl_asoc_constrain_rates);
199
200
MODULE_AUTHOR("Timur Tabi <[email protected]>");
201
MODULE_DESCRIPTION("Freescale ASoC utility code");
202
MODULE_LICENSE("GPL v2");
203
204