blob: d20aab2c83b27482f6f0bee32bbcfc57d8cec236 [file] [log] [blame]
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -08001/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05302 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/kernel.h>
14#include <linux/init.h>
15#include <linux/err.h>
16#include <linux/module.h>
17#include <linux/of.h>
18#include <linux/clk.h>
19#include <linux/clk-provider.h>
20#include "../../../drivers/clk/qcom/common.h"
21#include <linux/platform_device.h>
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053022#include <dt-bindings/clock/qcom,audio-ext-clk.h>
Laxminath Kasam605b42f2017-08-01 22:02:15 +053023#include <dsp/q6afe-v2.h>
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053024#include "audio-ext-clk-up.h"
25
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -080026enum {
27 AUDIO_EXT_CLK_PMI,
28 AUDIO_EXT_CLK_LNBB2,
29 AUDIO_EXT_CLK_LPASS,
30 AUDIO_EXT_CLK_MAX,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053031};
32
33struct pinctrl_info {
34 struct pinctrl *pinctrl;
35 struct pinctrl_state *sleep;
36 struct pinctrl_state *active;
37 char __iomem *base;
38};
39
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -080040struct audio_ext_clk {
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053041 struct pinctrl_info pnctrl_info;
42 struct clk_fixed_factor fact;
43};
44
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -080045struct audio_ext_clk_priv {
46 struct device *dev;
47 int clk_src;
48 struct afe_clk_set clk_cfg;
49 struct audio_ext_clk audio_clk;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053050};
51
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -080052static inline struct audio_ext_clk_priv *to_audio_clk(struct clk_hw *hw)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053053{
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -080054 return container_of(hw, struct audio_ext_clk_priv, audio_clk.fact.hw);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053055}
56
57static int audio_ext_clk_prepare(struct clk_hw *hw)
58{
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -080059 struct audio_ext_clk_priv *clk_priv = to_audio_clk(hw);
60 struct pinctrl_info *pnctrl_info = &clk_priv->audio_clk.pnctrl_info;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053061 int ret;
62
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -080063 if (clk_priv->clk_src == AUDIO_EXT_CLK_LPASS) {
64 clk_priv->clk_cfg.enable = 1;
65 ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk_priv->clk_cfg);
66 if (ret < 0) {
67 pr_err("%s afe_set_digital_codec_core_clock failed\n",
68 __func__);
69 return ret;
70 }
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053071 }
72
73 if (pnctrl_info->pinctrl) {
74 ret = pinctrl_select_state(pnctrl_info->pinctrl,
75 pnctrl_info->active);
76 if (ret) {
77 pr_err("%s: active state select failed with %d\n",
78 __func__, ret);
79 return -EIO;
80 }
81 }
82
83 if (pnctrl_info->base)
84 iowrite32(1, pnctrl_info->base);
85 return 0;
86}
87
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -080088static void audio_ext_clk_unprepare(struct clk_hw *hw)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053089{
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -080090 struct audio_ext_clk_priv *clk_priv = to_audio_clk(hw);
91 struct pinctrl_info *pnctrl_info = &clk_priv->audio_clk.pnctrl_info;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053092 int ret;
93
94 if (pnctrl_info->pinctrl) {
95 ret = pinctrl_select_state(pnctrl_info->pinctrl,
96 pnctrl_info->sleep);
97 if (ret) {
98 pr_err("%s: active state select failed with %d\n",
99 __func__, ret);
100 return;
101 }
102 }
103
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800104 if (clk_priv->clk_src == AUDIO_EXT_CLK_LPASS) {
105 clk_priv->clk_cfg.enable = 0;
106 ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk_priv->clk_cfg);
107 if (ret < 0)
108 pr_err("%s: afe_set_lpass_clk_cfg failed, ret = %d\n",
109 __func__, ret);
110 }
111
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530112 if (pnctrl_info->base)
113 iowrite32(0, pnctrl_info->base);
114}
115
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800116static const struct clk_ops audio_ext_clk_ops = {
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530117 .prepare = audio_ext_clk_prepare,
118 .unprepare = audio_ext_clk_unprepare,
119};
120
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800121static struct audio_ext_clk audio_clk_array[] = {
122 {
123 .pnctrl_info = {NULL},
124 .fact = {
125 .mult = 1,
126 .div = 1,
127 .hw.init = &(struct clk_init_data){
128 .name = "audio_ext_pmi_clk",
129 .parent_names = (const char *[])
130 { "qpnp_clkdiv_1" },
131 .num_parents = 1,
132 .ops = &audio_ext_clk_ops,
133 },
134 },
135 },
136 {
137 .pnctrl_info = {NULL},
138 .fact = {
139 .mult = 1,
140 .div = 1,
141 .hw.init = &(struct clk_init_data){
142 .name = "audio_ext_pmi_lnbb_clk",
143 .parent_names = (const char *[])
144 { "ln_bb_clk2" },
145 .num_parents = 1,
146 .ops = &clk_dummy_ops,
147 },
148 },
149 },
150 {
151 .pnctrl_info = {NULL},
152 .fact = {
153 .mult = 1,
154 .div = 1,
155 .hw.init = &(struct clk_init_data){
156 .name = "audio_lpass_mclk",
157 .ops = &audio_ext_clk_ops,
158 },
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530159 },
160 },
161};
162
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800163static int audio_get_pinctrl(struct platform_device *pdev)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530164{
165 struct device *dev = &pdev->dev;
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800166 struct audio_ext_clk_priv *clk_priv = platform_get_drvdata(pdev);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530167 struct pinctrl_info *pnctrl_info;
168 struct pinctrl *pinctrl;
169 int ret;
170 u32 reg;
171
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800172 if (clk_priv->clk_src == AUDIO_EXT_CLK_LNBB2) {
173 dev_dbg(dev, "%s no pinctrl for clk_src = %d\n",
174 __func__, clk_priv->clk_src);
175 return 0;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530176 }
177
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800178 pnctrl_info = &clk_priv->audio_clk.pnctrl_info;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530179 if (pnctrl_info->pinctrl) {
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800180 dev_err(dev, "%s: already requested before\n",
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530181 __func__);
182 return -EINVAL;
183 }
184
185 pinctrl = devm_pinctrl_get(dev);
186 if (IS_ERR_OR_NULL(pinctrl)) {
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800187 dev_err(dev, "%s: Unable to get pinctrl handle\n",
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530188 __func__);
189 return -EINVAL;
190 }
191 pnctrl_info->pinctrl = pinctrl;
192 /* get all state handles from Device Tree */
193 pnctrl_info->sleep = pinctrl_lookup_state(pinctrl, "sleep");
194 if (IS_ERR(pnctrl_info->sleep)) {
195 dev_err(dev, "%s: could not get sleep pinstate\n",
196 __func__);
197 goto err;
198 }
199 pnctrl_info->active = pinctrl_lookup_state(pinctrl, "active");
200 if (IS_ERR(pnctrl_info->active)) {
201 dev_err(dev, "%s: could not get active pinstate\n",
202 __func__);
203 goto err;
204 }
205 /* Reset the TLMM pins to a default state */
206 ret = pinctrl_select_state(pnctrl_info->pinctrl,
207 pnctrl_info->sleep);
208 if (ret) {
209 dev_err(dev, "%s: Disable TLMM pins failed with %d\n",
210 __func__, ret);
211 goto err;
212 }
213
214 ret = of_property_read_u32(dev->of_node, "qcom,mclk-clk-reg", &reg);
215 if (ret < 0) {
216 dev_dbg(dev, "%s: miss mclk reg\n", __func__);
217 } else {
218 pnctrl_info->base = ioremap(reg, sizeof(u32));
219 if (pnctrl_info->base == NULL) {
220 dev_err(dev, "%s ioremap failed\n", __func__);
221 goto err;
222 }
223 }
224
225 return 0;
226
227err:
228 devm_pinctrl_put(pnctrl_info->pinctrl);
229 return -EINVAL;
230}
231
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800232static int audio_put_pinctrl(struct platform_device *pdev)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530233{
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800234 struct audio_ext_clk_priv *clk_priv = platform_get_drvdata(pdev);
235 struct pinctrl_info *pnctrl_info = NULL;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530236
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800237 pnctrl_info = &clk_priv->audio_clk.pnctrl_info;
238 if (pnctrl_info && pnctrl_info->pinctrl) {
239 devm_pinctrl_put(pnctrl_info->pinctrl);
240 pnctrl_info->pinctrl = NULL;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530241 }
242
243 return 0;
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800244}
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530245
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800246static int audio_get_clk_data(struct platform_device *pdev)
247{
248 int ret;
249 struct clk *audio_clk;
250 struct clk_hw *clkhw;
251 struct clk_onecell_data *clk_data;
252 struct audio_ext_clk_priv *clk_priv = platform_get_drvdata(pdev);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530253
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800254 clk_data = devm_kzalloc(&pdev->dev, sizeof(*clk_data), GFP_KERNEL);
255 if (!clk_data)
256 return -ENOMEM;
257
258 clk_data->clk_num = 1;
259 clk_data->clks = devm_kzalloc(&pdev->dev,
260 sizeof(struct clk *),
261 GFP_KERNEL);
262 if (!clk_data->clks)
263 return -ENOMEM;
264
265 clkhw = &clk_priv->audio_clk.fact.hw;
266 audio_clk = devm_clk_register(&pdev->dev, clkhw);
267 if (IS_ERR(audio_clk)) {
268 dev_err(&pdev->dev,
269 "%s: clock register failed for clk_src = %d\\n",
270 __func__, clk_priv->clk_src);
271 ret = PTR_ERR(audio_clk);
272 return ret;
273 }
274 clk_data->clks[0] = audio_clk;
275
276 ret = of_clk_add_provider(pdev->dev.of_node,
277 of_clk_src_onecell_get, clk_data);
278 if (ret)
279 dev_err(&pdev->dev, "%s: clock add failed for clk_src = %d\n",
280 __func__, clk_priv->clk_src);
281
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530282 return ret;
283}
284
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800285static int audio_ref_clk_probe(struct platform_device *pdev)
286{
287 int ret;
288 struct audio_ext_clk_priv *clk_priv;
289 u32 clk_freq = 0, clk_id = 0, clk_src = 0;
290
291
292 clk_priv = devm_kzalloc(&pdev->dev, sizeof(*clk_priv), GFP_KERNEL);
293 if (!clk_priv)
294 return -ENOMEM;
295
296 ret = of_property_read_u32(pdev->dev.of_node,
297 "qcom,codec-ext-clk-src",
298 &clk_src);
299 if (ret) {
300 dev_err(&pdev->dev, "%s: could not get clk source, ret = %d\n",
301 __func__, ret);
302 return ret;
303 }
304
305 if (clk_src >= AUDIO_EXT_CLK_MAX) {
306 dev_err(&pdev->dev, "%s: Invalid clk source = %d\n",
307 __func__, clk_src);
308 return -EINVAL;
309 }
310
311 clk_priv->clk_src = clk_src;
312 memcpy(&clk_priv->audio_clk, &audio_clk_array[clk_src],
313 sizeof(struct audio_ext_clk));
314
315 /* Init lpass clk default values */
316 clk_priv->clk_cfg.clk_set_minor_version =
317 Q6AFE_LPASS_CLK_CONFIG_API_VERSION;
318 clk_priv->clk_cfg.clk_id = Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR;
319 clk_priv->clk_cfg.clk_freq_in_hz = Q6AFE_LPASS_OSR_CLK_9_P600_MHZ;
320 clk_priv->clk_cfg.clk_attri = Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO;
321
322 ret = of_property_read_u32(pdev->dev.of_node,
323 "qcom,codec-lpass-ext-clk-freq",
324 &clk_freq);
325 if (!ret)
326 clk_priv->clk_cfg.clk_freq_in_hz = clk_freq;
327
328 ret = of_property_read_u32(pdev->dev.of_node,
329 "qcom,codec-lpass-clk-id",
330 &clk_id);
331 if (!ret)
332 clk_priv->clk_cfg.clk_id = clk_id;
333
334 dev_dbg(&pdev->dev, "%s: ext-clk freq: %d, lpass clk_id: %d, clk_src: %d\n",
335 __func__, clk_priv->clk_cfg.clk_freq_in_hz,
336 clk_priv->clk_cfg.clk_id, clk_priv->clk_src);
337 platform_set_drvdata(pdev, clk_priv);
338
339 ret = audio_get_pinctrl(pdev);
340 if (ret) {
341 dev_err(&pdev->dev, "%s: Parsing PMI pinctrl failed\n",
342 __func__);
343 return ret;
344 }
345
346 ret = audio_get_clk_data(pdev);
347 if (ret) {
348 dev_err(&pdev->dev, "%s: clk_init is failed\n",
349 __func__);
350 audio_put_pinctrl(pdev);
351 return ret;
352 }
353
354 return 0;
355}
356
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530357static int audio_ref_clk_remove(struct platform_device *pdev)
358{
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800359 audio_put_pinctrl(pdev);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530360
361 return 0;
362}
363
364static const struct of_device_id audio_ref_clk_match[] = {
365 {.compatible = "qcom,audio-ref-clk"},
366 {}
367};
368MODULE_DEVICE_TABLE(of, audio_ref_clk_match);
369
370static struct platform_driver audio_ref_clk_driver = {
371 .driver = {
372 .name = "audio-ref-clk",
373 .owner = THIS_MODULE,
374 .of_match_table = audio_ref_clk_match,
375 },
376 .probe = audio_ref_clk_probe,
377 .remove = audio_ref_clk_remove,
378};
379
380int audio_ref_clk_platform_init(void)
381{
382 return platform_driver_register(&audio_ref_clk_driver);
383}
384
385void audio_ref_clk_platform_exit(void)
386{
387 platform_driver_unregister(&audio_ref_clk_driver);
388}
389
390MODULE_DESCRIPTION("Audio Ref Up Clock module platform driver");
391MODULE_LICENSE("GPL v2");