blob: f12f4ca65a54f8dd2a4ae02d6f25c7df22e3feb7 [file] [log] [blame]
Banajit Goswami2d3eebc2017-02-01 12:52:50 -08001/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
Banajit Goswamide8271c2017-01-18 00:28:59 -08002 *
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>
22#include <linux/gpio.h>
23#include <linux/of_gpio.h>
Banajit Goswami2d3eebc2017-02-01 12:52:50 -080024#include <dt-bindings/clock/qcom,audio-ext-clk.h>
Banajit Goswamide8271c2017-01-18 00:28:59 -080025#include <sound/q6afe-v2.h>
26
27enum audio_clk_mux {
28 AP_CLK2,
29 LPASS_MCLK,
30 LPASS_MCLK2,
31};
32
33struct pinctrl_info {
34 struct pinctrl *pinctrl;
35 struct pinctrl_state *sleep;
36 struct pinctrl_state *active;
Meng Wang417e5712017-03-07 09:44:05 +080037 char __iomem *base;
Banajit Goswamide8271c2017-01-18 00:28:59 -080038};
39
40struct audio_ext_ap_clk {
41 bool enabled;
42 int gpio;
43 struct clk_fixed_factor fact;
44};
45
46struct audio_ext_pmi_clk {
47 int gpio;
48 struct clk_fixed_factor fact;
49};
50
51struct audio_ext_ap_clk2 {
52 bool enabled;
53 struct pinctrl_info pnctrl_info;
54 struct clk_fixed_factor fact;
55};
56
57struct audio_ext_lpass_mclk {
58 struct pinctrl_info pnctrl_info;
59 struct clk_fixed_factor fact;
60};
61
62static struct afe_clk_set clk2_config = {
63 Q6AFE_LPASS_CLK_CONFIG_API_VERSION,
64 Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR,
65 Q6AFE_LPASS_IBIT_CLK_11_P2896_MHZ,
66 Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
67 Q6AFE_LPASS_CLK_ROOT_DEFAULT,
68 0,
69};
70
71static struct afe_clk_set lpass_default = {
72 Q6AFE_LPASS_CLK_CONFIG_API_VERSION,
73 Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR,
74 Q6AFE_LPASS_IBIT_CLK_11_P2896_MHZ,
75 Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
76 Q6AFE_LPASS_CLK_ROOT_DEFAULT,
77 0,
78};
79
80static struct afe_clk_set lpass_mclk = {
81 Q6AFE_LPASS_CLK_CONFIG_API_VERSION,
82 Q6AFE_LPASS_CLK_ID_MCLK_1,
83 Q6AFE_LPASS_OSR_CLK_11_P2896_MHZ,
84 Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
85 Q6AFE_LPASS_CLK_ROOT_DEFAULT,
86 0,
87};
88
89static inline struct audio_ext_ap_clk *to_audio_ap_clk(struct clk_hw *hw)
90{
91 return container_of(hw, struct audio_ext_ap_clk, fact.hw);
92}
93
94static int audio_ext_clk_prepare(struct clk_hw *hw)
95{
96 struct audio_ext_ap_clk *audio_clk = to_audio_ap_clk(hw);
97
98 pr_debug("%s: gpio: %d\n", __func__, audio_clk->gpio);
99 if (gpio_is_valid(audio_clk->gpio))
100 return gpio_direction_output(audio_clk->gpio, 1);
101 return 0;
102}
103
104static void audio_ext_clk_unprepare(struct clk_hw *hw)
105{
106 struct audio_ext_ap_clk *audio_clk = to_audio_ap_clk(hw);
107
108 pr_debug("%s: gpio: %d\n", __func__, audio_clk->gpio);
109 if (gpio_is_valid(audio_clk->gpio))
110 gpio_direction_output(audio_clk->gpio, 0);
111}
112
113static inline struct audio_ext_ap_clk2 *to_audio_ap_clk2(struct clk_hw *hw)
114{
115 return container_of(hw, struct audio_ext_ap_clk2, fact.hw);
116}
117
118static int audio_ext_clk2_prepare(struct clk_hw *hw)
119{
120 struct audio_ext_ap_clk2 *audio_clk2 = to_audio_ap_clk2(hw);
121 struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info;
122 int ret;
123
124
125 if (!pnctrl_info->pinctrl || !pnctrl_info->active)
126 return 0;
127
128 ret = pinctrl_select_state(pnctrl_info->pinctrl,
129 pnctrl_info->active);
130 if (ret) {
131 pr_err("%s: active state select failed with %d\n",
132 __func__, ret);
133 return -EIO;
134 }
135
136 clk2_config.enable = 1;
137 ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk2_config);
138 if (ret < 0) {
139 pr_err("%s: failed to set clock, ret = %d\n", __func__, ret);
140 return -EINVAL;
141 }
142
143 return 0;
144}
145
146static void audio_ext_clk2_unprepare(struct clk_hw *hw)
147{
148 struct audio_ext_ap_clk2 *audio_clk2 = to_audio_ap_clk2(hw);
149 struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info;
150 int ret;
151
152 if (!pnctrl_info->pinctrl || !pnctrl_info->sleep)
153 return;
154
155 ret = pinctrl_select_state(pnctrl_info->pinctrl,
156 pnctrl_info->sleep);
157 if (ret)
158 pr_err("%s: sleep state select failed with %d\n",
159 __func__, ret);
160
161 clk2_config.enable = 0;
162 ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk2_config);
163 if (ret < 0)
164 pr_err("%s: failed to reset clock, ret = %d\n", __func__, ret);
165}
166
167static inline struct audio_ext_lpass_mclk *to_audio_lpass_mclk(
168 struct clk_hw *hw)
169{
170 return container_of(hw, struct audio_ext_lpass_mclk, fact.hw);
171}
172
173static int audio_ext_lpass_mclk_prepare(struct clk_hw *hw)
174{
175 struct audio_ext_lpass_mclk *audio_lpass_mclk = to_audio_lpass_mclk(hw);
176 struct pinctrl_info *pnctrl_info = &audio_lpass_mclk->pnctrl_info;
177 int ret;
178
179 if (pnctrl_info->pinctrl) {
180 ret = pinctrl_select_state(pnctrl_info->pinctrl,
181 pnctrl_info->active);
182 if (ret) {
183 pr_err("%s: active state select failed with %d\n",
184 __func__, ret);
185 return -EIO;
186 }
187 }
188
189 lpass_mclk.enable = 1;
190 ret = afe_set_lpass_clock_v2(AFE_PORT_ID_PRIMARY_MI2S_RX,
191 &lpass_mclk);
192 if (ret < 0) {
193 pr_err("%s afe_set_digital_codec_core_clock failed\n",
194 __func__);
195 return ret;
Meng Wang417e5712017-03-07 09:44:05 +0800196 }
Banajit Goswamide8271c2017-01-18 00:28:59 -0800197
Meng Wang417e5712017-03-07 09:44:05 +0800198 if (pnctrl_info->base)
199 iowrite32(1, pnctrl_info->base);
Banajit Goswamide8271c2017-01-18 00:28:59 -0800200 return 0;
201}
202
203static void audio_ext_lpass_mclk_unprepare(struct clk_hw *hw)
204{
205 struct audio_ext_lpass_mclk *audio_lpass_mclk = to_audio_lpass_mclk(hw);
206 struct pinctrl_info *pnctrl_info = &audio_lpass_mclk->pnctrl_info;
207 int ret;
208
209 if (pnctrl_info->pinctrl) {
210 ret = pinctrl_select_state(pnctrl_info->pinctrl,
211 pnctrl_info->sleep);
212 if (ret) {
213 pr_err("%s: active state select failed with %d\n",
214 __func__, ret);
215 return;
216 }
217 }
218
219 lpass_mclk.enable = 0;
220 ret = afe_set_lpass_clock_v2(AFE_PORT_ID_PRIMARY_MI2S_RX,
221 &lpass_mclk);
222 if (ret < 0)
223 pr_err("%s: afe_set_digital_codec_core_clock failed, ret = %d\n",
224 __func__, ret);
Meng Wang417e5712017-03-07 09:44:05 +0800225 if (pnctrl_info->base)
226 iowrite32(0, pnctrl_info->base);
Banajit Goswamide8271c2017-01-18 00:28:59 -0800227}
228
229static int audio_ext_lpass_mclk2_prepare(struct clk_hw *hw)
230{
231 struct audio_ext_lpass_mclk *audio_lpass_mclk2 =
232 to_audio_lpass_mclk(hw);
233 struct pinctrl_info *pnctrl_info = &audio_lpass_mclk2->pnctrl_info;
234 int ret;
235
236 if (pnctrl_info->pinctrl) {
237 ret = pinctrl_select_state(pnctrl_info->pinctrl,
238 pnctrl_info->active);
239 if (ret) {
240 pr_err("%s: active state select failed with %d\n",
241 __func__, ret);
242 return -EIO;
243 }
244 }
245
246 lpass_default.enable = 1;
247 ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &lpass_default);
248 if (ret < 0) {
249 pr_err("%s: failed to set clock, ret = %d\n", __func__, ret);
250 return -EINVAL;
251 }
252
253 return 0;
254}
255
256static void audio_ext_lpass_mclk2_unprepare(struct clk_hw *hw)
257{
258 struct audio_ext_lpass_mclk *audio_lpass_mclk2 =
259 to_audio_lpass_mclk(hw);
260 struct pinctrl_info *pnctrl_info = &audio_lpass_mclk2->pnctrl_info;
261 int ret;
262
263 if (pnctrl_info->pinctrl) {
264 ret = pinctrl_select_state(pnctrl_info->pinctrl,
265 pnctrl_info->sleep);
266 if (ret)
267 pr_err("%s: sleep state select failed with %d\n",
268 __func__, ret);
269 }
270
271 lpass_default.enable = 0;
272 ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &lpass_default);
273 if (ret < 0)
274 pr_err("%s: failed to reset clock, ret = %d\n", __func__, ret);
275}
276
277static const struct clk_ops audio_ext_ap_clk_ops = {
278 .prepare = audio_ext_clk_prepare,
279 .unprepare = audio_ext_clk_unprepare,
280};
281
282static const struct clk_ops audio_ext_ap_clk2_ops = {
283 .prepare = audio_ext_clk2_prepare,
284 .unprepare = audio_ext_clk2_unprepare,
285};
286
287static const struct clk_ops audio_ext_lpass_mclk_ops = {
288 .prepare = audio_ext_lpass_mclk_prepare,
289 .unprepare = audio_ext_lpass_mclk_unprepare,
290};
291
292static const struct clk_ops audio_ext_lpass_mclk2_ops = {
293 .prepare = audio_ext_lpass_mclk2_prepare,
294 .unprepare = audio_ext_lpass_mclk2_unprepare,
295};
296
297static struct audio_ext_pmi_clk audio_pmi_clk = {
298 .gpio = -EINVAL,
299 .fact = {
300 .mult = 1,
301 .div = 1,
302 .hw.init = &(struct clk_init_data){
303 .name = "audio_ext_pmi_clk",
Yeleswarapu Nagaradhesh3f051562017-01-17 16:05:34 +0530304 .parent_names = (const char *[]){ "div_clk1" },
305 .num_parents = 1,
Banajit Goswamide8271c2017-01-18 00:28:59 -0800306 .ops = &clk_dummy_ops,
307 },
308 },
309};
310
311static struct audio_ext_pmi_clk audio_pmi_lnbb_clk = {
312 .gpio = -EINVAL,
313 .fact = {
314 .mult = 1,
315 .div = 1,
316 .hw.init = &(struct clk_init_data){
317 .name = "audio_ext_pmi_lnbb_clk",
Yeleswarapu Nagaradhesh3f051562017-01-17 16:05:34 +0530318 .parent_names = (const char *[]){ "ln_bb_clk2" },
319 .num_parents = 1,
Banajit Goswamide8271c2017-01-18 00:28:59 -0800320 .ops = &clk_dummy_ops,
321 },
322 },
323};
324
325static struct audio_ext_ap_clk audio_ap_clk = {
326 .gpio = -EINVAL,
327 .fact = {
328 .mult = 1,
329 .div = 1,
330 .hw.init = &(struct clk_init_data){
331 .name = "audio_ap_clk",
332 .ops = &audio_ext_ap_clk_ops,
333 },
334 },
335};
336
337static struct audio_ext_ap_clk2 audio_ap_clk2 = {
338 .enabled = false,
339 .pnctrl_info = {NULL},
340 .fact = {
341 .mult = 1,
342 .div = 1,
343 .hw.init = &(struct clk_init_data){
344 .name = "audio_ap_clk2",
345 .ops = &audio_ext_ap_clk2_ops,
346 },
347 },
348};
349
350static struct audio_ext_lpass_mclk audio_lpass_mclk = {
351 .pnctrl_info = {NULL},
352 .fact = {
353 .mult = 1,
354 .div = 1,
355 .hw.init = &(struct clk_init_data){
356 .name = "audio_lpass_mclk",
357 .ops = &audio_ext_lpass_mclk_ops,
358 },
359 },
360};
361
362static struct audio_ext_lpass_mclk audio_lpass_mclk2 = {
363 .pnctrl_info = {NULL},
364 .fact = {
365 .mult = 1,
366 .div = 1,
367 .hw.init = &(struct clk_init_data){
368 .name = "audio_lpass_mclk2",
369 .ops = &audio_ext_lpass_mclk2_ops,
370 },
371 },
372};
373
374static struct clk_hw *audio_msm_hws[] = {
375 &audio_pmi_clk.fact.hw,
Banajit Goswamide8271c2017-01-18 00:28:59 -0800376 &audio_ap_clk.fact.hw,
377 &audio_ap_clk2.fact.hw,
378 &audio_lpass_mclk.fact.hw,
379 &audio_lpass_mclk2.fact.hw,
380};
381
Yeleswarapu Nagaradhesh3f051562017-01-17 16:05:34 +0530382static struct clk_hw *audio_msm_hws1[] = {
383 &audio_pmi_lnbb_clk.fact.hw,
384};
385
Banajit Goswamide8271c2017-01-18 00:28:59 -0800386static int audio_get_pinctrl(struct platform_device *pdev,
387 enum audio_clk_mux mux)
388{
Meng Wang417e5712017-03-07 09:44:05 +0800389 struct device *dev = &pdev->dev;
Banajit Goswamide8271c2017-01-18 00:28:59 -0800390 struct pinctrl_info *pnctrl_info;
391 struct pinctrl *pinctrl;
392 int ret;
Meng Wang417e5712017-03-07 09:44:05 +0800393 u32 reg;
Banajit Goswamide8271c2017-01-18 00:28:59 -0800394
395 switch (mux) {
396 case AP_CLK2:
397 pnctrl_info = &audio_ap_clk2.pnctrl_info;
398 break;
399 case LPASS_MCLK:
400 pnctrl_info = &audio_lpass_mclk.pnctrl_info;
401 break;
402 case LPASS_MCLK2:
403 pnctrl_info = &audio_lpass_mclk2.pnctrl_info;
404 break;
405 default:
Meng Wang417e5712017-03-07 09:44:05 +0800406 dev_err(dev, "%s Not a valid MUX ID: %d\n",
Banajit Goswamide8271c2017-01-18 00:28:59 -0800407 __func__, mux);
408 return -EINVAL;
409 }
Banajit Goswamide8271c2017-01-18 00:28:59 -0800410
411 if (pnctrl_info->pinctrl) {
Meng Wang417e5712017-03-07 09:44:05 +0800412 dev_dbg(dev, "%s: already requested before\n",
Banajit Goswamide8271c2017-01-18 00:28:59 -0800413 __func__);
414 return -EINVAL;
415 }
416
Meng Wang417e5712017-03-07 09:44:05 +0800417 pinctrl = devm_pinctrl_get(dev);
Banajit Goswamide8271c2017-01-18 00:28:59 -0800418 if (IS_ERR_OR_NULL(pinctrl)) {
Meng Wang417e5712017-03-07 09:44:05 +0800419 dev_dbg(dev, "%s: Unable to get pinctrl handle\n",
Banajit Goswamide8271c2017-01-18 00:28:59 -0800420 __func__);
421 return -EINVAL;
422 }
423 pnctrl_info->pinctrl = pinctrl;
424 /* get all state handles from Device Tree */
425 pnctrl_info->sleep = pinctrl_lookup_state(pinctrl, "sleep");
426 if (IS_ERR(pnctrl_info->sleep)) {
Meng Wang417e5712017-03-07 09:44:05 +0800427 dev_err(dev, "%s: could not get sleep pinstate\n",
Banajit Goswamide8271c2017-01-18 00:28:59 -0800428 __func__);
429 goto err;
430 }
431 pnctrl_info->active = pinctrl_lookup_state(pinctrl, "active");
432 if (IS_ERR(pnctrl_info->active)) {
Meng Wang417e5712017-03-07 09:44:05 +0800433 dev_err(dev, "%s: could not get active pinstate\n",
Banajit Goswamide8271c2017-01-18 00:28:59 -0800434 __func__);
435 goto err;
436 }
437 /* Reset the TLMM pins to a default state */
438 ret = pinctrl_select_state(pnctrl_info->pinctrl,
439 pnctrl_info->sleep);
440 if (ret) {
Meng Wang417e5712017-03-07 09:44:05 +0800441 dev_err(dev, "%s: Disable TLMM pins failed with %d\n",
Banajit Goswamide8271c2017-01-18 00:28:59 -0800442 __func__, ret);
443 goto err;
444 }
Meng Wang417e5712017-03-07 09:44:05 +0800445
446 ret = of_property_read_u32(dev->of_node, "qcom,mclk-clk-reg", &reg);
447 if (ret < 0) {
448 dev_dbg(dev, "%s: miss mclk reg\n", __func__);
449 } else {
450 pnctrl_info->base = ioremap(reg, sizeof(u32));
451 if (pnctrl_info->base == NULL) {
452 dev_err(dev, "%s ioremap failed\n", __func__);
453 goto err;
454 }
455 }
456
Banajit Goswamide8271c2017-01-18 00:28:59 -0800457 return 0;
458
459err:
460 devm_pinctrl_put(pnctrl_info->pinctrl);
461 return -EINVAL;
462}
463
464static int audio_ref_clk_probe(struct platform_device *pdev)
465{
466 int clk_gpio;
467 int ret;
468 u32 mclk_freq;
469 struct clk *audio_clk;
470 struct device *dev = &pdev->dev;
471 int i;
472 struct clk_onecell_data *clk_data;
473
474 ret = of_property_read_u32(pdev->dev.of_node,
475 "qcom,codec-mclk-clk-freq",
476 &mclk_freq);
477 if (!ret) {
478 lpass_mclk.clk_freq_in_hz = mclk_freq;
479
480 ret = audio_get_pinctrl(pdev, LPASS_MCLK);
481 if (ret)
482 dev_err(&pdev->dev, "%s: Parsing pinctrl %s failed\n",
483 __func__, "LPASS_MCLK");
484 ret = audio_get_pinctrl(pdev, LPASS_MCLK2);
485 if (ret)
486 dev_dbg(&pdev->dev, "%s: Parsing pinctrl %s failed\n",
487 __func__, "LPASS_MCLK2");
488 }
489
490 clk_gpio = of_get_named_gpio(pdev->dev.of_node,
491 "qcom,audio-ref-clk-gpio", 0);
492 if (clk_gpio > 0) {
493 ret = gpio_request(clk_gpio, "EXT_CLK");
494 if (ret) {
495 dev_err(&pdev->dev,
496 "Request ext clk gpio failed %d, err:%d\n",
497 clk_gpio, ret);
498 goto err;
499 }
500 if (of_property_read_bool(pdev->dev.of_node,
501 "qcom,node_has_rpm_clock")) {
502 audio_pmi_clk.gpio = clk_gpio;
503 } else
504 audio_ap_clk.gpio = clk_gpio;
505
506 }
507
508 ret = audio_get_pinctrl(pdev, AP_CLK2);
509 if (ret)
510 dev_dbg(&pdev->dev, "%s: Parsing pinctrl failed\n",
511 __func__);
512
513 clk_data = devm_kzalloc(&pdev->dev, sizeof(*clk_data), GFP_KERNEL);
514 if (!clk_data)
515 goto err_gpio;
516
Yeleswarapu Nagaradhesh3f051562017-01-17 16:05:34 +0530517
518 clk_gpio = of_get_named_gpio(pdev->dev.of_node,
519 "qcom,audio-ref-clk-gpio", 0);
520 if (clk_gpio > 0) {
Banajit Goswamie5daf7b2017-02-25 04:00:43 -0800521 clk_data->clk_num = ARRAY_SIZE(audio_msm_hws);
522 clk_data->clks = devm_kzalloc(&pdev->dev,
523 clk_data->clk_num *
524 sizeof(struct clk *),
525 GFP_KERNEL);
526 if (!clk_data->clks)
527 goto err_clk;
528
Yeleswarapu Nagaradhesh3f051562017-01-17 16:05:34 +0530529 for (i = 0; i < ARRAY_SIZE(audio_msm_hws); i++) {
530 audio_clk = devm_clk_register(dev, audio_msm_hws[i]);
531 if (IS_ERR(audio_clk)) {
532 dev_err(&pdev->dev,
533 "%s: ref clock: %d register failed\n",
534 __func__, i);
535 return PTR_ERR(audio_clk);
536 }
537 clk_data->clks[i] = audio_clk;
Banajit Goswamide8271c2017-01-18 00:28:59 -0800538 }
Yeleswarapu Nagaradhesh3f051562017-01-17 16:05:34 +0530539 } else {
Banajit Goswamie5daf7b2017-02-25 04:00:43 -0800540 clk_data->clk_num = ARRAY_SIZE(audio_msm_hws1);
541 clk_data->clks = devm_kzalloc(&pdev->dev,
542 clk_data->clk_num *
543 sizeof(struct clk *),
544 GFP_KERNEL);
545 if (!clk_data->clks)
546 goto err_clk;
547
Yeleswarapu Nagaradhesh3f051562017-01-17 16:05:34 +0530548 for (i = 0; i < ARRAY_SIZE(audio_msm_hws1); i++) {
549 audio_clk = devm_clk_register(dev, audio_msm_hws1[i]);
550 if (IS_ERR(audio_clk)) {
551 dev_err(&pdev->dev,
552 "%s: ref clock: %d register failed\n",
553 __func__, i);
554 return PTR_ERR(audio_clk);
555 }
556 clk_data->clks[i] = audio_clk;
557 }
Banajit Goswamide8271c2017-01-18 00:28:59 -0800558 }
559
560 ret = of_clk_add_provider(pdev->dev.of_node,
561 of_clk_src_onecell_get, clk_data);
562 if (ret) {
563 dev_err(&pdev->dev, "%s: audio ref clock register failed\n",
564 __func__);
565 goto err_gpio;
566 }
567
568 return 0;
569
570err_clk:
571 if (clk_data)
572 devm_kfree(&pdev->dev, clk_data->clks);
573 devm_kfree(&pdev->dev, clk_data);
574err_gpio:
575 gpio_free(clk_gpio);
576
577err:
578 return ret;
579}
580
581static int audio_ref_clk_remove(struct platform_device *pdev)
582{
583 struct pinctrl_info *pnctrl_info = &audio_ap_clk2.pnctrl_info;
584
585 if (audio_pmi_clk.gpio > 0)
586 gpio_free(audio_pmi_clk.gpio);
587 else if (audio_ap_clk.gpio > 0)
588 gpio_free(audio_ap_clk.gpio);
589
590 if (pnctrl_info->pinctrl) {
591 devm_pinctrl_put(pnctrl_info->pinctrl);
592 pnctrl_info->pinctrl = NULL;
593 }
594
595 return 0;
596}
597
598static const struct of_device_id audio_ref_clk_match[] = {
599 {.compatible = "qcom,audio-ref-clk"},
600 {}
601};
602MODULE_DEVICE_TABLE(of, audio_ref_clk_match);
603
604static struct platform_driver audio_ref_clk_driver = {
605 .driver = {
606 .name = "audio-ref-clk",
607 .owner = THIS_MODULE,
608 .of_match_table = audio_ref_clk_match,
609 },
610 .probe = audio_ref_clk_probe,
611 .remove = audio_ref_clk_remove,
612};
613
614static int __init audio_ref_clk_platform_init(void)
615{
616 return platform_driver_register(&audio_ref_clk_driver);
617}
618module_init(audio_ref_clk_platform_init);
619
620static void __exit audio_ref_clk_platform_exit(void)
621{
622 platform_driver_unregister(&audio_ref_clk_driver);
623}
624module_exit(audio_ref_clk_platform_exit);
625
626MODULE_DESCRIPTION("Audio Ref Up Clock module platform driver");
627MODULE_LICENSE("GPL v2");