blob: 9f2e4022bccda7b7ead155b4a677888fa6b52b53 [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
Sudheer Papothi9b51e3e2018-07-21 05:20:10 +0530147static int audio_ext_clk_dummy_prepare(struct clk_hw *hw)
148{
149 return 0;
150}
151
152static void audio_ext_clk_dummy_unprepare(struct clk_hw *hw)
153{
154
155}
156
157static const struct clk_ops audio_ext_clk_dummy_ops = {
158 .prepare = audio_ext_clk_dummy_prepare,
159 .unprepare = audio_ext_clk_dummy_unprepare,
160};
161
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800162static struct audio_ext_clk audio_clk_array[] = {
163 {
164 .pnctrl_info = {NULL},
165 .fact = {
166 .mult = 1,
167 .div = 1,
168 .hw.init = &(struct clk_init_data){
169 .name = "audio_ext_pmi_clk",
Surendar Karka0a915f82018-07-09 20:30:03 +0530170 .parent_names = audio_ext_pmi_div_clk,
171 .num_parents =
172 ARRAY_SIZE(audio_ext_pmi_div_clk),
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800173 .ops = &audio_ext_clk_ops,
174 },
175 },
176 },
177 {
178 .pnctrl_info = {NULL},
179 .fact = {
180 .mult = 1,
181 .div = 1,
182 .hw.init = &(struct clk_init_data){
183 .name = "audio_ext_pmi_lnbb_clk",
184 .parent_names = (const char *[])
185 { "ln_bb_clk2" },
186 .num_parents = 1,
Sudheer Papothi9b51e3e2018-07-21 05:20:10 +0530187 .ops = &audio_ext_clk_dummy_ops,
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800188 },
189 },
190 },
191 {
192 .pnctrl_info = {NULL},
193 .fact = {
194 .mult = 1,
195 .div = 1,
196 .hw.init = &(struct clk_init_data){
197 .name = "audio_lpass_mclk",
198 .ops = &audio_ext_clk_ops,
199 },
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530200 },
201 },
202};
203
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800204static int audio_get_pinctrl(struct platform_device *pdev)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530205{
206 struct device *dev = &pdev->dev;
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800207 struct audio_ext_clk_priv *clk_priv = platform_get_drvdata(pdev);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530208 struct pinctrl_info *pnctrl_info;
209 struct pinctrl *pinctrl;
210 int ret;
211 u32 reg;
212
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800213 pnctrl_info = &clk_priv->audio_clk.pnctrl_info;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530214 if (pnctrl_info->pinctrl) {
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800215 dev_err(dev, "%s: already requested before\n",
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530216 __func__);
217 return -EINVAL;
218 }
219
220 pinctrl = devm_pinctrl_get(dev);
221 if (IS_ERR_OR_NULL(pinctrl)) {
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800222 dev_err(dev, "%s: Unable to get pinctrl handle\n",
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530223 __func__);
224 return -EINVAL;
225 }
226 pnctrl_info->pinctrl = pinctrl;
227 /* get all state handles from Device Tree */
228 pnctrl_info->sleep = pinctrl_lookup_state(pinctrl, "sleep");
229 if (IS_ERR(pnctrl_info->sleep)) {
230 dev_err(dev, "%s: could not get sleep pinstate\n",
231 __func__);
232 goto err;
233 }
234 pnctrl_info->active = pinctrl_lookup_state(pinctrl, "active");
235 if (IS_ERR(pnctrl_info->active)) {
236 dev_err(dev, "%s: could not get active pinstate\n",
237 __func__);
238 goto err;
239 }
240 /* Reset the TLMM pins to a default state */
241 ret = pinctrl_select_state(pnctrl_info->pinctrl,
242 pnctrl_info->sleep);
243 if (ret) {
244 dev_err(dev, "%s: Disable TLMM pins failed with %d\n",
245 __func__, ret);
246 goto err;
247 }
248
249 ret = of_property_read_u32(dev->of_node, "qcom,mclk-clk-reg", &reg);
250 if (ret < 0) {
251 dev_dbg(dev, "%s: miss mclk reg\n", __func__);
252 } else {
253 pnctrl_info->base = ioremap(reg, sizeof(u32));
254 if (pnctrl_info->base == NULL) {
255 dev_err(dev, "%s ioremap failed\n", __func__);
256 goto err;
257 }
258 }
259
260 return 0;
261
262err:
263 devm_pinctrl_put(pnctrl_info->pinctrl);
264 return -EINVAL;
265}
266
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800267static int audio_put_pinctrl(struct platform_device *pdev)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530268{
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800269 struct audio_ext_clk_priv *clk_priv = platform_get_drvdata(pdev);
270 struct pinctrl_info *pnctrl_info = NULL;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530271
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800272 pnctrl_info = &clk_priv->audio_clk.pnctrl_info;
273 if (pnctrl_info && pnctrl_info->pinctrl) {
274 devm_pinctrl_put(pnctrl_info->pinctrl);
275 pnctrl_info->pinctrl = NULL;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530276 }
277
278 return 0;
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800279}
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530280
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800281static int audio_get_clk_data(struct platform_device *pdev)
282{
283 int ret;
284 struct clk *audio_clk;
285 struct clk_hw *clkhw;
286 struct clk_onecell_data *clk_data;
287 struct audio_ext_clk_priv *clk_priv = platform_get_drvdata(pdev);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530288
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800289 clk_data = devm_kzalloc(&pdev->dev, sizeof(*clk_data), GFP_KERNEL);
290 if (!clk_data)
291 return -ENOMEM;
292
293 clk_data->clk_num = 1;
294 clk_data->clks = devm_kzalloc(&pdev->dev,
295 sizeof(struct clk *),
296 GFP_KERNEL);
297 if (!clk_data->clks)
298 return -ENOMEM;
299
300 clkhw = &clk_priv->audio_clk.fact.hw;
301 audio_clk = devm_clk_register(&pdev->dev, clkhw);
302 if (IS_ERR(audio_clk)) {
303 dev_err(&pdev->dev,
304 "%s: clock register failed for clk_src = %d\\n",
305 __func__, clk_priv->clk_src);
306 ret = PTR_ERR(audio_clk);
307 return ret;
308 }
309 clk_data->clks[0] = audio_clk;
310
311 ret = of_clk_add_provider(pdev->dev.of_node,
312 of_clk_src_onecell_get, clk_data);
313 if (ret)
314 dev_err(&pdev->dev, "%s: clock add failed for clk_src = %d\n",
315 __func__, clk_priv->clk_src);
316
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530317 return ret;
318}
319
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800320static int audio_ref_clk_probe(struct platform_device *pdev)
321{
322 int ret;
323 struct audio_ext_clk_priv *clk_priv;
Laxminath Kasam43c1a132018-06-15 13:18:07 +0530324 u32 clk_freq = 0, clk_id = 0, clk_src = 0, use_pinctrl = 0;
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800325
326 clk_priv = devm_kzalloc(&pdev->dev, sizeof(*clk_priv), GFP_KERNEL);
327 if (!clk_priv)
328 return -ENOMEM;
329
330 ret = of_property_read_u32(pdev->dev.of_node,
331 "qcom,codec-ext-clk-src",
332 &clk_src);
333 if (ret) {
334 dev_err(&pdev->dev, "%s: could not get clk source, ret = %d\n",
335 __func__, ret);
336 return ret;
337 }
338
339 if (clk_src >= AUDIO_EXT_CLK_MAX) {
340 dev_err(&pdev->dev, "%s: Invalid clk source = %d\n",
341 __func__, clk_src);
342 return -EINVAL;
343 }
Surendar Karka0a915f82018-07-09 20:30:03 +0530344 clk_priv->clk_name = NULL;
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800345 clk_priv->clk_src = clk_src;
346 memcpy(&clk_priv->audio_clk, &audio_clk_array[clk_src],
347 sizeof(struct audio_ext_clk));
348
349 /* Init lpass clk default values */
350 clk_priv->clk_cfg.clk_set_minor_version =
351 Q6AFE_LPASS_CLK_CONFIG_API_VERSION;
352 clk_priv->clk_cfg.clk_id = Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR;
353 clk_priv->clk_cfg.clk_freq_in_hz = Q6AFE_LPASS_OSR_CLK_9_P600_MHZ;
354 clk_priv->clk_cfg.clk_attri = Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO;
355
356 ret = of_property_read_u32(pdev->dev.of_node,
357 "qcom,codec-lpass-ext-clk-freq",
358 &clk_freq);
359 if (!ret)
360 clk_priv->clk_cfg.clk_freq_in_hz = clk_freq;
361
362 ret = of_property_read_u32(pdev->dev.of_node,
363 "qcom,codec-lpass-clk-id",
364 &clk_id);
365 if (!ret)
366 clk_priv->clk_cfg.clk_id = clk_id;
367
368 dev_dbg(&pdev->dev, "%s: ext-clk freq: %d, lpass clk_id: %d, clk_src: %d\n",
369 __func__, clk_priv->clk_cfg.clk_freq_in_hz,
370 clk_priv->clk_cfg.clk_id, clk_priv->clk_src);
371 platform_set_drvdata(pdev, clk_priv);
372
Surendar Karka0a915f82018-07-09 20:30:03 +0530373 ret = of_property_read_string(pdev->dev.of_node, "pmic-clock-names",
374 &clk_priv->clk_name);
375 if (ret)
376 dev_dbg(&pdev->dev, "%s: could not find pmic clock names\n",
377 __func__);
Laxminath Kasam43c1a132018-06-15 13:18:07 +0530378 /*
379 * property qcom,use-pinctrl to be defined in DTSI to val 1
380 * for clock nodes using pinctrl
381 */
382 of_property_read_u32(pdev->dev.of_node, "qcom,use-pinctrl",
383 &use_pinctrl);
384 dev_dbg(&pdev->dev, "%s: use-pinctrl : %d\n",
385 __func__, use_pinctrl);
386
387 if (use_pinctrl) {
388 ret = audio_get_pinctrl(pdev);
389 if (ret) {
390 dev_err(&pdev->dev, "%s: Parsing PMI pinctrl failed\n",
391 __func__);
392 return ret;
393 }
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800394 }
395
396 ret = audio_get_clk_data(pdev);
397 if (ret) {
398 dev_err(&pdev->dev, "%s: clk_init is failed\n",
399 __func__);
400 audio_put_pinctrl(pdev);
401 return ret;
402 }
403
404 return 0;
405}
406
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530407static int audio_ref_clk_remove(struct platform_device *pdev)
408{
Vidyakumar Athotaecc4eda2017-12-13 16:24:10 -0800409 audio_put_pinctrl(pdev);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530410
411 return 0;
412}
413
414static const struct of_device_id audio_ref_clk_match[] = {
415 {.compatible = "qcom,audio-ref-clk"},
416 {}
417};
418MODULE_DEVICE_TABLE(of, audio_ref_clk_match);
419
420static struct platform_driver audio_ref_clk_driver = {
421 .driver = {
422 .name = "audio-ref-clk",
423 .owner = THIS_MODULE,
424 .of_match_table = audio_ref_clk_match,
425 },
426 .probe = audio_ref_clk_probe,
427 .remove = audio_ref_clk_remove,
428};
429
430int audio_ref_clk_platform_init(void)
431{
432 return platform_driver_register(&audio_ref_clk_driver);
433}
434
435void audio_ref_clk_platform_exit(void)
436{
437 platform_driver_unregister(&audio_ref_clk_driver);
438}
439
440MODULE_DESCRIPTION("Audio Ref Up Clock module platform driver");
441MODULE_LICENSE("GPL v2");