blob: 1bcdbe3d74b8b3aac192aadfedb9d80f29179d4d [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;
Surendar Karka0a915f82018-07-09 20:30:03 +053050 const char *clk_name;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053051};
52
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -080053static inline struct audio_ext_clk_priv *to_audio_clk(struct clk_hw *hw)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053054{
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -080055 return container_of(hw, struct audio_ext_clk_priv, audio_clk.fact.hw);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053056}
57
58static int audio_ext_clk_prepare(struct clk_hw *hw)
59{
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -080060 struct audio_ext_clk_priv *clk_priv = to_audio_clk(hw);
61 struct pinctrl_info *pnctrl_info = &clk_priv->audio_clk.pnctrl_info;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053062 int ret;
63
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -080064 if (clk_priv->clk_src == AUDIO_EXT_CLK_LPASS) {
65 clk_priv->clk_cfg.enable = 1;
66 ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk_priv->clk_cfg);
67 if (ret < 0) {
68 pr_err("%s afe_set_digital_codec_core_clock failed\n",
69 __func__);
70 return ret;
71 }
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053072 }
73
74 if (pnctrl_info->pinctrl) {
75 ret = pinctrl_select_state(pnctrl_info->pinctrl,
76 pnctrl_info->active);
77 if (ret) {
78 pr_err("%s: active state select failed with %d\n",
79 __func__, ret);
80 return -EIO;
81 }
82 }
83
84 if (pnctrl_info->base)
85 iowrite32(1, pnctrl_info->base);
86 return 0;
87}
88
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -080089static void audio_ext_clk_unprepare(struct clk_hw *hw)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053090{
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -080091 struct audio_ext_clk_priv *clk_priv = to_audio_clk(hw);
92 struct pinctrl_info *pnctrl_info = &clk_priv->audio_clk.pnctrl_info;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053093 int ret;
94
95 if (pnctrl_info->pinctrl) {
96 ret = pinctrl_select_state(pnctrl_info->pinctrl,
97 pnctrl_info->sleep);
98 if (ret) {
99 pr_err("%s: active state select failed with %d\n",
100 __func__, ret);
101 return;
102 }
103 }
104
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800105 if (clk_priv->clk_src == AUDIO_EXT_CLK_LPASS) {
106 clk_priv->clk_cfg.enable = 0;
107 ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk_priv->clk_cfg);
108 if (ret < 0)
109 pr_err("%s: afe_set_lpass_clk_cfg failed, ret = %d\n",
110 __func__, ret);
111 }
112
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530113 if (pnctrl_info->base)
114 iowrite32(0, pnctrl_info->base);
115}
116
Surendar Karka0a915f82018-07-09 20:30:03 +0530117static u8 audio_ext_clk_get_parent(struct clk_hw *hw)
118{
119 struct audio_ext_clk_priv *clk_priv = to_audio_clk(hw);
120 int num_parents = clk_hw_get_num_parents(hw);
121 const char * const *parent_names = hw->init->parent_names;
122 u8 i = 0, ret = hw->init->num_parents + 1;
123
124 if ((clk_priv->clk_src == AUDIO_EXT_CLK_PMI) && clk_priv->clk_name) {
125 for (i = 0; i < num_parents; i++) {
126 if (!strcmp(parent_names[i], clk_priv->clk_name))
127 ret = i;
128 }
129 pr_debug("%s: parent index = %u\n", __func__, ret);
130 return ret;
131 } else
132 return 0;
133}
134
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800135static const struct clk_ops audio_ext_clk_ops = {
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530136 .prepare = audio_ext_clk_prepare,
137 .unprepare = audio_ext_clk_unprepare,
Surendar Karka0a915f82018-07-09 20:30:03 +0530138 .get_parent = audio_ext_clk_get_parent,
139};
140
141static const char * const audio_ext_pmi_div_clk[] = {
142 "qpnp_clkdiv_1",
143 "pms405_div_clk1",
144 "pm6150_div_clk1",
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530145};
146
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800147static struct audio_ext_clk audio_clk_array[] = {
148 {
149 .pnctrl_info = {NULL},
150 .fact = {
151 .mult = 1,
152 .div = 1,
153 .hw.init = &(struct clk_init_data){
154 .name = "audio_ext_pmi_clk",
Surendar Karka0a915f82018-07-09 20:30:03 +0530155 .parent_names = audio_ext_pmi_div_clk,
156 .num_parents =
157 ARRAY_SIZE(audio_ext_pmi_div_clk),
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800158 .ops = &audio_ext_clk_ops,
159 },
160 },
161 },
162 {
163 .pnctrl_info = {NULL},
164 .fact = {
165 .mult = 1,
166 .div = 1,
167 .hw.init = &(struct clk_init_data){
168 .name = "audio_ext_pmi_lnbb_clk",
169 .parent_names = (const char *[])
170 { "ln_bb_clk2" },
171 .num_parents = 1,
172 .ops = &clk_dummy_ops,
173 },
174 },
175 },
176 {
177 .pnctrl_info = {NULL},
178 .fact = {
179 .mult = 1,
180 .div = 1,
181 .hw.init = &(struct clk_init_data){
182 .name = "audio_lpass_mclk",
183 .ops = &audio_ext_clk_ops,
184 },
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530185 },
186 },
187};
188
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800189static int audio_get_pinctrl(struct platform_device *pdev)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530190{
191 struct device *dev = &pdev->dev;
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800192 struct audio_ext_clk_priv *clk_priv = platform_get_drvdata(pdev);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530193 struct pinctrl_info *pnctrl_info;
194 struct pinctrl *pinctrl;
195 int ret;
196 u32 reg;
197
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800198 pnctrl_info = &clk_priv->audio_clk.pnctrl_info;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530199 if (pnctrl_info->pinctrl) {
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800200 dev_err(dev, "%s: already requested before\n",
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530201 __func__);
202 return -EINVAL;
203 }
204
205 pinctrl = devm_pinctrl_get(dev);
206 if (IS_ERR_OR_NULL(pinctrl)) {
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800207 dev_err(dev, "%s: Unable to get pinctrl handle\n",
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530208 __func__);
209 return -EINVAL;
210 }
211 pnctrl_info->pinctrl = pinctrl;
212 /* get all state handles from Device Tree */
213 pnctrl_info->sleep = pinctrl_lookup_state(pinctrl, "sleep");
214 if (IS_ERR(pnctrl_info->sleep)) {
215 dev_err(dev, "%s: could not get sleep pinstate\n",
216 __func__);
217 goto err;
218 }
219 pnctrl_info->active = pinctrl_lookup_state(pinctrl, "active");
220 if (IS_ERR(pnctrl_info->active)) {
221 dev_err(dev, "%s: could not get active pinstate\n",
222 __func__);
223 goto err;
224 }
225 /* Reset the TLMM pins to a default state */
226 ret = pinctrl_select_state(pnctrl_info->pinctrl,
227 pnctrl_info->sleep);
228 if (ret) {
229 dev_err(dev, "%s: Disable TLMM pins failed with %d\n",
230 __func__, ret);
231 goto err;
232 }
233
234 ret = of_property_read_u32(dev->of_node, "qcom,mclk-clk-reg", &reg);
235 if (ret < 0) {
236 dev_dbg(dev, "%s: miss mclk reg\n", __func__);
237 } else {
238 pnctrl_info->base = ioremap(reg, sizeof(u32));
239 if (pnctrl_info->base == NULL) {
240 dev_err(dev, "%s ioremap failed\n", __func__);
241 goto err;
242 }
243 }
244
245 return 0;
246
247err:
248 devm_pinctrl_put(pnctrl_info->pinctrl);
249 return -EINVAL;
250}
251
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800252static int audio_put_pinctrl(struct platform_device *pdev)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530253{
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800254 struct audio_ext_clk_priv *clk_priv = platform_get_drvdata(pdev);
255 struct pinctrl_info *pnctrl_info = NULL;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530256
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800257 pnctrl_info = &clk_priv->audio_clk.pnctrl_info;
258 if (pnctrl_info && pnctrl_info->pinctrl) {
259 devm_pinctrl_put(pnctrl_info->pinctrl);
260 pnctrl_info->pinctrl = NULL;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530261 }
262
263 return 0;
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800264}
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530265
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800266static int audio_get_clk_data(struct platform_device *pdev)
267{
268 int ret;
269 struct clk *audio_clk;
270 struct clk_hw *clkhw;
271 struct clk_onecell_data *clk_data;
272 struct audio_ext_clk_priv *clk_priv = platform_get_drvdata(pdev);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530273
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800274 clk_data = devm_kzalloc(&pdev->dev, sizeof(*clk_data), GFP_KERNEL);
275 if (!clk_data)
276 return -ENOMEM;
277
278 clk_data->clk_num = 1;
279 clk_data->clks = devm_kzalloc(&pdev->dev,
280 sizeof(struct clk *),
281 GFP_KERNEL);
282 if (!clk_data->clks)
283 return -ENOMEM;
284
285 clkhw = &clk_priv->audio_clk.fact.hw;
286 audio_clk = devm_clk_register(&pdev->dev, clkhw);
287 if (IS_ERR(audio_clk)) {
288 dev_err(&pdev->dev,
289 "%s: clock register failed for clk_src = %d\\n",
290 __func__, clk_priv->clk_src);
291 ret = PTR_ERR(audio_clk);
292 return ret;
293 }
294 clk_data->clks[0] = audio_clk;
295
296 ret = of_clk_add_provider(pdev->dev.of_node,
297 of_clk_src_onecell_get, clk_data);
298 if (ret)
299 dev_err(&pdev->dev, "%s: clock add failed for clk_src = %d\n",
300 __func__, clk_priv->clk_src);
301
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530302 return ret;
303}
304
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800305static int audio_ref_clk_probe(struct platform_device *pdev)
306{
307 int ret;
308 struct audio_ext_clk_priv *clk_priv;
Laxminath Kasam43c1a132018-06-15 13:18:07 +0530309 u32 clk_freq = 0, clk_id = 0, clk_src = 0, use_pinctrl = 0;
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800310
311 clk_priv = devm_kzalloc(&pdev->dev, sizeof(*clk_priv), GFP_KERNEL);
312 if (!clk_priv)
313 return -ENOMEM;
314
315 ret = of_property_read_u32(pdev->dev.of_node,
316 "qcom,codec-ext-clk-src",
317 &clk_src);
318 if (ret) {
319 dev_err(&pdev->dev, "%s: could not get clk source, ret = %d\n",
320 __func__, ret);
321 return ret;
322 }
323
324 if (clk_src >= AUDIO_EXT_CLK_MAX) {
325 dev_err(&pdev->dev, "%s: Invalid clk source = %d\n",
326 __func__, clk_src);
327 return -EINVAL;
328 }
Surendar Karka0a915f82018-07-09 20:30:03 +0530329 clk_priv->clk_name = NULL;
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800330 clk_priv->clk_src = clk_src;
331 memcpy(&clk_priv->audio_clk, &audio_clk_array[clk_src],
332 sizeof(struct audio_ext_clk));
333
334 /* Init lpass clk default values */
335 clk_priv->clk_cfg.clk_set_minor_version =
336 Q6AFE_LPASS_CLK_CONFIG_API_VERSION;
337 clk_priv->clk_cfg.clk_id = Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR;
338 clk_priv->clk_cfg.clk_freq_in_hz = Q6AFE_LPASS_OSR_CLK_9_P600_MHZ;
339 clk_priv->clk_cfg.clk_attri = Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO;
340
341 ret = of_property_read_u32(pdev->dev.of_node,
342 "qcom,codec-lpass-ext-clk-freq",
343 &clk_freq);
344 if (!ret)
345 clk_priv->clk_cfg.clk_freq_in_hz = clk_freq;
346
347 ret = of_property_read_u32(pdev->dev.of_node,
348 "qcom,codec-lpass-clk-id",
349 &clk_id);
350 if (!ret)
351 clk_priv->clk_cfg.clk_id = clk_id;
352
353 dev_dbg(&pdev->dev, "%s: ext-clk freq: %d, lpass clk_id: %d, clk_src: %d\n",
354 __func__, clk_priv->clk_cfg.clk_freq_in_hz,
355 clk_priv->clk_cfg.clk_id, clk_priv->clk_src);
356 platform_set_drvdata(pdev, clk_priv);
357
Surendar Karka0a915f82018-07-09 20:30:03 +0530358 ret = of_property_read_string(pdev->dev.of_node, "pmic-clock-names",
359 &clk_priv->clk_name);
360 if (ret)
361 dev_dbg(&pdev->dev, "%s: could not find pmic clock names\n",
362 __func__);
Laxminath Kasam43c1a132018-06-15 13:18:07 +0530363 /*
364 * property qcom,use-pinctrl to be defined in DTSI to val 1
365 * for clock nodes using pinctrl
366 */
367 of_property_read_u32(pdev->dev.of_node, "qcom,use-pinctrl",
368 &use_pinctrl);
369 dev_dbg(&pdev->dev, "%s: use-pinctrl : %d\n",
370 __func__, use_pinctrl);
371
372 if (use_pinctrl) {
373 ret = audio_get_pinctrl(pdev);
374 if (ret) {
375 dev_err(&pdev->dev, "%s: Parsing PMI pinctrl failed\n",
376 __func__);
377 return ret;
378 }
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800379 }
380
381 ret = audio_get_clk_data(pdev);
382 if (ret) {
383 dev_err(&pdev->dev, "%s: clk_init is failed\n",
384 __func__);
385 audio_put_pinctrl(pdev);
386 return ret;
387 }
388
389 return 0;
390}
391
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530392static int audio_ref_clk_remove(struct platform_device *pdev)
393{
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800394 audio_put_pinctrl(pdev);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530395
396 return 0;
397}
398
399static const struct of_device_id audio_ref_clk_match[] = {
400 {.compatible = "qcom,audio-ref-clk"},
401 {}
402};
403MODULE_DEVICE_TABLE(of, audio_ref_clk_match);
404
405static struct platform_driver audio_ref_clk_driver = {
406 .driver = {
407 .name = "audio-ref-clk",
408 .owner = THIS_MODULE,
409 .of_match_table = audio_ref_clk_match,
410 },
411 .probe = audio_ref_clk_probe,
412 .remove = audio_ref_clk_remove,
413};
414
415int audio_ref_clk_platform_init(void)
416{
417 return platform_driver_register(&audio_ref_clk_driver);
418}
419
420void audio_ref_clk_platform_exit(void)
421{
422 platform_driver_unregister(&audio_ref_clk_driver);
423}
424
425MODULE_DESCRIPTION("Audio Ref Up Clock module platform driver");
426MODULE_LICENSE("GPL v2");