1 /*
2  * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 and
6  * only version 2 as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * lpass-cpu.c -- ALSA SoC CPU DAI driver for QTi LPASS
14  */
15 
16 #include <linux/clk.h>
17 #include <linux/kernel.h>
18 #include <linux/module.h>
19 #include <linux/of.h>
20 #include <linux/of_device.h>
21 #include <linux/platform_device.h>
22 #include <sound/pcm.h>
23 #include <sound/pcm_params.h>
24 #include <linux/regmap.h>
25 #include <sound/soc.h>
26 #include <sound/soc-dai.h>
27 #include "lpass-lpaif-reg.h"
28 #include "lpass.h"
29 
lpass_cpu_daiops_set_sysclk(struct snd_soc_dai * dai,int clk_id,unsigned int freq,int dir)30 static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id,
31 		unsigned int freq, int dir)
32 {
33 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
34 	int ret;
35 
36 	ret = clk_set_rate(drvdata->mi2s_osr_clk[dai->driver->id], freq);
37 	if (ret)
38 		dev_err(dai->dev, "error setting mi2s osrclk to %u: %d\n",
39 			freq, ret);
40 
41 	return ret;
42 }
43 
lpass_cpu_daiops_startup(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)44 static int lpass_cpu_daiops_startup(struct snd_pcm_substream *substream,
45 		struct snd_soc_dai *dai)
46 {
47 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
48 	int ret;
49 
50 	ret = clk_prepare_enable(drvdata->mi2s_osr_clk[dai->driver->id]);
51 	if (ret) {
52 		dev_err(dai->dev, "error in enabling mi2s osr clk: %d\n", ret);
53 		return ret;
54 	}
55 
56 	ret = clk_prepare_enable(drvdata->mi2s_bit_clk[dai->driver->id]);
57 	if (ret) {
58 		dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret);
59 		clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]);
60 		return ret;
61 	}
62 
63 	return 0;
64 }
65 
lpass_cpu_daiops_shutdown(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)66 static void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream,
67 		struct snd_soc_dai *dai)
68 {
69 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
70 
71 	clk_disable_unprepare(drvdata->mi2s_bit_clk[dai->driver->id]);
72 
73 	clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]);
74 }
75 
lpass_cpu_daiops_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)76 static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
77 		struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
78 {
79 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
80 	snd_pcm_format_t format = params_format(params);
81 	unsigned int channels = params_channels(params);
82 	unsigned int rate = params_rate(params);
83 	unsigned int regval;
84 	int bitwidth, ret;
85 
86 	bitwidth = snd_pcm_format_width(format);
87 	if (bitwidth < 0) {
88 		dev_err(dai->dev, "invalid bit width given: %d\n", bitwidth);
89 		return bitwidth;
90 	}
91 
92 	regval = LPAIF_I2SCTL_LOOPBACK_DISABLE |
93 			LPAIF_I2SCTL_WSSRC_INTERNAL;
94 
95 	switch (bitwidth) {
96 	case 16:
97 		regval |= LPAIF_I2SCTL_BITWIDTH_16;
98 		break;
99 	case 24:
100 		regval |= LPAIF_I2SCTL_BITWIDTH_24;
101 		break;
102 	case 32:
103 		regval |= LPAIF_I2SCTL_BITWIDTH_32;
104 		break;
105 	default:
106 		dev_err(dai->dev, "invalid bitwidth given: %d\n", bitwidth);
107 		return -EINVAL;
108 	}
109 
110 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
111 		switch (channels) {
112 		case 1:
113 			regval |= LPAIF_I2SCTL_SPKMODE_SD0;
114 			regval |= LPAIF_I2SCTL_SPKMONO_MONO;
115 			break;
116 		case 2:
117 			regval |= LPAIF_I2SCTL_SPKMODE_SD0;
118 			regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
119 			break;
120 		case 4:
121 			regval |= LPAIF_I2SCTL_SPKMODE_QUAD01;
122 			regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
123 			break;
124 		case 6:
125 			regval |= LPAIF_I2SCTL_SPKMODE_6CH;
126 			regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
127 			break;
128 		case 8:
129 			regval |= LPAIF_I2SCTL_SPKMODE_8CH;
130 			regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
131 			break;
132 		default:
133 			dev_err(dai->dev, "invalid channels given: %u\n",
134 				channels);
135 			return -EINVAL;
136 		}
137 	} else {
138 		switch (channels) {
139 		case 1:
140 			regval |= LPAIF_I2SCTL_MICMODE_SD0;
141 			regval |= LPAIF_I2SCTL_MICMONO_MONO;
142 			break;
143 		case 2:
144 			regval |= LPAIF_I2SCTL_MICMODE_SD0;
145 			regval |= LPAIF_I2SCTL_MICMONO_STEREO;
146 			break;
147 		case 4:
148 			regval |= LPAIF_I2SCTL_MICMODE_QUAD01;
149 			regval |= LPAIF_I2SCTL_MICMONO_STEREO;
150 			break;
151 		case 6:
152 			regval |= LPAIF_I2SCTL_MICMODE_6CH;
153 			regval |= LPAIF_I2SCTL_MICMONO_STEREO;
154 			break;
155 		case 8:
156 			regval |= LPAIF_I2SCTL_MICMODE_8CH;
157 			regval |= LPAIF_I2SCTL_MICMONO_STEREO;
158 			break;
159 		default:
160 			dev_err(dai->dev, "invalid channels given: %u\n",
161 				channels);
162 			return -EINVAL;
163 		}
164 	}
165 
166 	ret = regmap_write(drvdata->lpaif_map,
167 			   LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
168 			   regval);
169 	if (ret) {
170 		dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret);
171 		return ret;
172 	}
173 
174 	ret = clk_set_rate(drvdata->mi2s_bit_clk[dai->driver->id],
175 			   rate * bitwidth * 2);
176 	if (ret) {
177 		dev_err(dai->dev, "error setting mi2s bitclk to %u: %d\n",
178 			rate * bitwidth * 2, ret);
179 		return ret;
180 	}
181 
182 	return 0;
183 }
184 
lpass_cpu_daiops_hw_free(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)185 static int lpass_cpu_daiops_hw_free(struct snd_pcm_substream *substream,
186 		struct snd_soc_dai *dai)
187 {
188 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
189 	int ret;
190 
191 	ret = regmap_write(drvdata->lpaif_map,
192 			   LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
193 			   0);
194 	if (ret)
195 		dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret);
196 
197 	return ret;
198 }
199 
lpass_cpu_daiops_prepare(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)200 static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream,
201 		struct snd_soc_dai *dai)
202 {
203 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
204 	int ret;
205 	unsigned int val, mask;
206 
207 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
208 		val = LPAIF_I2SCTL_SPKEN_ENABLE;
209 		mask = LPAIF_I2SCTL_SPKEN_MASK;
210 	} else  {
211 		val = LPAIF_I2SCTL_MICEN_ENABLE;
212 		mask = LPAIF_I2SCTL_MICEN_MASK;
213 	}
214 
215 	ret = regmap_update_bits(drvdata->lpaif_map,
216 			LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
217 			mask, val);
218 	if (ret)
219 		dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret);
220 
221 	return ret;
222 }
223 
lpass_cpu_daiops_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * dai)224 static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
225 		int cmd, struct snd_soc_dai *dai)
226 {
227 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
228 	int ret = -EINVAL;
229 	unsigned int val, mask;
230 
231 	switch (cmd) {
232 	case SNDRV_PCM_TRIGGER_START:
233 	case SNDRV_PCM_TRIGGER_RESUME:
234 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
235 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
236 			val = LPAIF_I2SCTL_SPKEN_ENABLE;
237 			mask = LPAIF_I2SCTL_SPKEN_MASK;
238 		} else  {
239 			val = LPAIF_I2SCTL_MICEN_ENABLE;
240 			mask = LPAIF_I2SCTL_MICEN_MASK;
241 		}
242 
243 		ret = regmap_update_bits(drvdata->lpaif_map,
244 				LPAIF_I2SCTL_REG(drvdata->variant,
245 						dai->driver->id),
246 				mask, val);
247 		if (ret)
248 			dev_err(dai->dev, "error writing to i2sctl reg: %d\n",
249 				ret);
250 		break;
251 	case SNDRV_PCM_TRIGGER_STOP:
252 	case SNDRV_PCM_TRIGGER_SUSPEND:
253 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
254 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
255 			val = LPAIF_I2SCTL_SPKEN_DISABLE;
256 			mask = LPAIF_I2SCTL_SPKEN_MASK;
257 		} else  {
258 			val = LPAIF_I2SCTL_MICEN_DISABLE;
259 			mask = LPAIF_I2SCTL_MICEN_MASK;
260 		}
261 
262 		ret = regmap_update_bits(drvdata->lpaif_map,
263 				LPAIF_I2SCTL_REG(drvdata->variant,
264 						dai->driver->id),
265 				mask, val);
266 		if (ret)
267 			dev_err(dai->dev, "error writing to i2sctl reg: %d\n",
268 				ret);
269 		break;
270 	}
271 
272 	return ret;
273 }
274 
275 const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops = {
276 	.set_sysclk	= lpass_cpu_daiops_set_sysclk,
277 	.startup	= lpass_cpu_daiops_startup,
278 	.shutdown	= lpass_cpu_daiops_shutdown,
279 	.hw_params	= lpass_cpu_daiops_hw_params,
280 	.hw_free	= lpass_cpu_daiops_hw_free,
281 	.prepare	= lpass_cpu_daiops_prepare,
282 	.trigger	= lpass_cpu_daiops_trigger,
283 };
284 EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops);
285 
asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai * dai)286 int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai)
287 {
288 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
289 	int ret;
290 
291 	/* ensure audio hardware is disabled */
292 	ret = regmap_write(drvdata->lpaif_map,
293 			LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), 0);
294 	if (ret)
295 		dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret);
296 
297 	return ret;
298 }
299 EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_probe);
300 
301 static const struct snd_soc_component_driver lpass_cpu_comp_driver = {
302 	.name = "lpass-cpu",
303 };
304 
lpass_cpu_regmap_writeable(struct device * dev,unsigned int reg)305 static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg)
306 {
307 	struct lpass_data *drvdata = dev_get_drvdata(dev);
308 	struct lpass_variant *v = drvdata->variant;
309 	int i;
310 
311 	for (i = 0; i < v->i2s_ports; ++i)
312 		if (reg == LPAIF_I2SCTL_REG(v, i))
313 			return true;
314 
315 	for (i = 0; i < v->irq_ports; ++i) {
316 		if (reg == LPAIF_IRQEN_REG(v, i))
317 			return true;
318 		if (reg == LPAIF_IRQCLEAR_REG(v, i))
319 			return true;
320 	}
321 
322 	for (i = 0; i < v->rdma_channels; ++i) {
323 		if (reg == LPAIF_RDMACTL_REG(v, i))
324 			return true;
325 		if (reg == LPAIF_RDMABASE_REG(v, i))
326 			return true;
327 		if (reg == LPAIF_RDMABUFF_REG(v, i))
328 			return true;
329 		if (reg == LPAIF_RDMAPER_REG(v, i))
330 			return true;
331 	}
332 
333 	for (i = 0; i < v->wrdma_channels; ++i) {
334 		if (reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start))
335 			return true;
336 		if (reg == LPAIF_WRDMABASE_REG(v, i + v->wrdma_channel_start))
337 			return true;
338 		if (reg == LPAIF_WRDMABUFF_REG(v, i + v->wrdma_channel_start))
339 			return true;
340 		if (reg == LPAIF_WRDMAPER_REG(v, i + v->wrdma_channel_start))
341 			return true;
342 	}
343 
344 	return false;
345 }
346 
lpass_cpu_regmap_readable(struct device * dev,unsigned int reg)347 static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg)
348 {
349 	struct lpass_data *drvdata = dev_get_drvdata(dev);
350 	struct lpass_variant *v = drvdata->variant;
351 	int i;
352 
353 	for (i = 0; i < v->i2s_ports; ++i)
354 		if (reg == LPAIF_I2SCTL_REG(v, i))
355 			return true;
356 
357 	for (i = 0; i < v->irq_ports; ++i) {
358 		if (reg == LPAIF_IRQEN_REG(v, i))
359 			return true;
360 		if (reg == LPAIF_IRQSTAT_REG(v, i))
361 			return true;
362 	}
363 
364 	for (i = 0; i < v->rdma_channels; ++i) {
365 		if (reg == LPAIF_RDMACTL_REG(v, i))
366 			return true;
367 		if (reg == LPAIF_RDMABASE_REG(v, i))
368 			return true;
369 		if (reg == LPAIF_RDMABUFF_REG(v, i))
370 			return true;
371 		if (reg == LPAIF_RDMACURR_REG(v, i))
372 			return true;
373 		if (reg == LPAIF_RDMAPER_REG(v, i))
374 			return true;
375 	}
376 
377 	for (i = 0; i < v->wrdma_channels; ++i) {
378 		if (reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start))
379 			return true;
380 		if (reg == LPAIF_WRDMABASE_REG(v, i + v->wrdma_channel_start))
381 			return true;
382 		if (reg == LPAIF_WRDMABUFF_REG(v, i + v->wrdma_channel_start))
383 			return true;
384 		if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start))
385 			return true;
386 		if (reg == LPAIF_WRDMAPER_REG(v, i + v->wrdma_channel_start))
387 			return true;
388 	}
389 
390 	return false;
391 }
392 
lpass_cpu_regmap_volatile(struct device * dev,unsigned int reg)393 static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg)
394 {
395 	struct lpass_data *drvdata = dev_get_drvdata(dev);
396 	struct lpass_variant *v = drvdata->variant;
397 	int i;
398 
399 	for (i = 0; i < v->irq_ports; ++i)
400 		if (reg == LPAIF_IRQSTAT_REG(v, i))
401 			return true;
402 
403 	for (i = 0; i < v->rdma_channels; ++i)
404 		if (reg == LPAIF_RDMACURR_REG(v, i))
405 			return true;
406 
407 	for (i = 0; i < v->wrdma_channels; ++i)
408 		if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start))
409 			return true;
410 
411 	return false;
412 }
413 
414 static struct regmap_config lpass_cpu_regmap_config = {
415 	.reg_bits = 32,
416 	.reg_stride = 4,
417 	.val_bits = 32,
418 	.writeable_reg = lpass_cpu_regmap_writeable,
419 	.readable_reg = lpass_cpu_regmap_readable,
420 	.volatile_reg = lpass_cpu_regmap_volatile,
421 	.cache_type = REGCACHE_FLAT,
422 };
423 
asoc_qcom_lpass_cpu_platform_probe(struct platform_device * pdev)424 int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
425 {
426 	struct lpass_data *drvdata;
427 	struct device_node *dsp_of_node;
428 	struct resource *res;
429 	struct lpass_variant *variant;
430 	struct device *dev = &pdev->dev;
431 	const struct of_device_id *match;
432 	int ret, i, dai_id;
433 
434 	dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0);
435 	if (dsp_of_node) {
436 		dev_err(&pdev->dev, "DSP exists and holds audio resources\n");
437 		return -EBUSY;
438 	}
439 
440 	drvdata = devm_kzalloc(&pdev->dev, sizeof(struct lpass_data),
441 			GFP_KERNEL);
442 	if (!drvdata)
443 		return -ENOMEM;
444 	platform_set_drvdata(pdev, drvdata);
445 
446 	match = of_match_device(dev->driver->of_match_table, dev);
447 	if (!match || !match->data)
448 		return -EINVAL;
449 
450 	drvdata->variant = (struct lpass_variant *)match->data;
451 	variant = drvdata->variant;
452 
453 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-lpaif");
454 
455 	drvdata->lpaif = devm_ioremap_resource(&pdev->dev, res);
456 	if (IS_ERR((void const __force *)drvdata->lpaif)) {
457 		dev_err(&pdev->dev, "error mapping reg resource: %ld\n",
458 				PTR_ERR((void const __force *)drvdata->lpaif));
459 		return PTR_ERR((void const __force *)drvdata->lpaif);
460 	}
461 
462 	lpass_cpu_regmap_config.max_register = LPAIF_WRDMAPER_REG(variant,
463 						variant->wrdma_channels +
464 						variant->wrdma_channel_start);
465 
466 	drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif,
467 			&lpass_cpu_regmap_config);
468 	if (IS_ERR(drvdata->lpaif_map)) {
469 		dev_err(&pdev->dev, "error initializing regmap: %ld\n",
470 			PTR_ERR(drvdata->lpaif_map));
471 		return PTR_ERR(drvdata->lpaif_map);
472 	}
473 
474 	if (variant->init)
475 		variant->init(pdev);
476 
477 	for (i = 0; i < variant->num_dai; i++) {
478 		dai_id = variant->dai_driver[i].id;
479 		drvdata->mi2s_osr_clk[dai_id] = devm_clk_get(&pdev->dev,
480 					     variant->dai_osr_clk_names[i]);
481 		if (IS_ERR(drvdata->mi2s_osr_clk[dai_id])) {
482 			dev_warn(&pdev->dev,
483 				"%s() error getting optional %s: %ld\n",
484 				__func__,
485 				variant->dai_osr_clk_names[i],
486 				PTR_ERR(drvdata->mi2s_osr_clk[dai_id]));
487 
488 			drvdata->mi2s_osr_clk[dai_id] = NULL;
489 		}
490 
491 		drvdata->mi2s_bit_clk[dai_id] = devm_clk_get(&pdev->dev,
492 						variant->dai_bit_clk_names[i]);
493 		if (IS_ERR(drvdata->mi2s_bit_clk[dai_id])) {
494 			dev_err(&pdev->dev,
495 				"error getting %s: %ld\n",
496 				variant->dai_bit_clk_names[i],
497 				PTR_ERR(drvdata->mi2s_bit_clk[dai_id]));
498 			return PTR_ERR(drvdata->mi2s_bit_clk[dai_id]);
499 		}
500 	}
501 
502 	drvdata->ahbix_clk = devm_clk_get(&pdev->dev, "ahbix-clk");
503 	if (IS_ERR(drvdata->ahbix_clk)) {
504 		dev_err(&pdev->dev, "error getting ahbix-clk: %ld\n",
505 			PTR_ERR(drvdata->ahbix_clk));
506 		return PTR_ERR(drvdata->ahbix_clk);
507 	}
508 
509 	ret = clk_set_rate(drvdata->ahbix_clk, LPASS_AHBIX_CLOCK_FREQUENCY);
510 	if (ret) {
511 		dev_err(&pdev->dev, "error setting rate on ahbix_clk: %d\n",
512 			ret);
513 		return ret;
514 	}
515 	dev_dbg(&pdev->dev, "set ahbix_clk rate to %lu\n",
516 		clk_get_rate(drvdata->ahbix_clk));
517 
518 	ret = clk_prepare_enable(drvdata->ahbix_clk);
519 	if (ret) {
520 		dev_err(&pdev->dev, "error enabling ahbix_clk: %d\n", ret);
521 		return ret;
522 	}
523 
524 	ret = devm_snd_soc_register_component(&pdev->dev,
525 					      &lpass_cpu_comp_driver,
526 					      variant->dai_driver,
527 					      variant->num_dai);
528 	if (ret) {
529 		dev_err(&pdev->dev, "error registering cpu driver: %d\n", ret);
530 		goto err_clk;
531 	}
532 
533 	ret = asoc_qcom_lpass_platform_register(pdev);
534 	if (ret) {
535 		dev_err(&pdev->dev, "error registering platform driver: %d\n",
536 			ret);
537 		goto err_clk;
538 	}
539 
540 	return 0;
541 
542 err_clk:
543 	clk_disable_unprepare(drvdata->ahbix_clk);
544 	return ret;
545 }
546 EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_probe);
547 
asoc_qcom_lpass_cpu_platform_remove(struct platform_device * pdev)548 int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev)
549 {
550 	struct lpass_data *drvdata = platform_get_drvdata(pdev);
551 
552 	if (drvdata->variant->exit)
553 		drvdata->variant->exit(pdev);
554 
555 	clk_disable_unprepare(drvdata->ahbix_clk);
556 
557 	return 0;
558 }
559 EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_remove);
560 
561 MODULE_DESCRIPTION("QTi LPASS CPU Driver");
562 MODULE_LICENSE("GPL v2");
563