blob: 3952dd5770e8f550688581b8592d1aa4eb43b6e4 [file] [log] [blame]
Joonwoo Parka8890262012-10-15 12:04:27 -07001/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
2 *
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#include <linux/module.h>
13#include <linux/init.h>
14#include <linux/firmware.h>
15#include <linux/slab.h>
16#include <linux/platform_device.h>
17#include <linux/device.h>
18#include <linux/printk.h>
19#include <linux/ratelimit.h>
20#include <linux/debugfs.h>
21#include <linux/mfd/wcd9xxx/core.h>
22#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
23#include <linux/mfd/wcd9xxx/wcd9320_registers.h>
24#include <linux/mfd/wcd9xxx/pdata.h>
25#include <sound/pcm.h>
26#include <sound/pcm_params.h>
27#include <sound/soc.h>
28#include <sound/soc-dapm.h>
29#include <sound/tlv.h>
30#include <linux/bitops.h>
31#include <linux/delay.h>
32#include <linux/pm_runtime.h>
33#include <linux/kernel.h>
34#include <linux/gpio.h>
35#include "wcd9xxx-resmgr.h"
36
37static char wcd9xxx_event_string[][64] = {
38 "WCD9XXX_EVENT_INVALID",
39
40 "WCD9XXX_EVENT_PRE_RCO_ON",
41 "WCD9XXX_EVENT_POST_RCO_ON",
42 "WCD9XXX_EVENT_PRE_RCO_OFF",
43 "WCD9XXX_EVENT_POST_RCO_OFF",
44
45 "WCD9XXX_EVENT_PRE_MCLK_ON",
46 "WCD9XXX_EVENT_POST_MCLK_ON",
47 "WCD9XXX_EVENT_PRE_MCLK_OFF",
48 "WCD9XXX_EVENT_POST_MCLK_OFF",
49
50 "WCD9XXX_EVENT_PRE_BG_OFF",
51 "WCD9XXX_EVENT_POST_BG_OFF",
52 "WCD9XXX_EVENT_PRE_BG_AUDIO_ON",
53 "WCD9XXX_EVENT_POST_BG_AUDIO_ON",
54 "WCD9XXX_EVENT_PRE_BG_MBHC_ON",
55 "WCD9XXX_EVENT_POST_BG_MBHC_ON",
56
57 "WCD9XXX_EVENT_PRE_MICBIAS_1_OFF",
58 "WCD9XXX_EVENT_POST_MICBIAS_1_OFF",
59 "WCD9XXX_EVENT_PRE_MICBIAS_2_OFF",
60 "WCD9XXX_EVENT_POST_MICBIAS_2_OFF",
61 "WCD9XXX_EVENT_PRE_MICBIAS_3_OFF",
62 "WCD9XXX_EVENT_POST_MICBIAS_3_OFF",
63 "WCD9XXX_EVENT_PRE_MICBIAS_4_OFF",
64 "WCD9XXX_EVENT_POST_MICBIAS_4_OFF",
65 "WCD9XXX_EVENT_PRE_MICBIAS_1_ON",
66 "WCD9XXX_EVENT_POST_MICBIAS_1_ON",
67 "WCD9XXX_EVENT_PRE_MICBIAS_2_ON",
68 "WCD9XXX_EVENT_POST_MICBIAS_2_ON",
69 "WCD9XXX_EVENT_PRE_MICBIAS_3_ON",
70 "WCD9XXX_EVENT_POST_MICBIAS_3_ON",
71 "WCD9XXX_EVENT_PRE_MICBIAS_4_ON",
72 "WCD9XXX_EVENT_POST_MICBIAS_4_ON",
73
74 "WCD9XXX_EVENT_PRE_CFILT_1_OFF",
75 "WCD9XXX_EVENT_POST_CFILT_1_OFF",
76 "WCD9XXX_EVENT_PRE_CFILT_2_OFF",
77 "WCD9XXX_EVENT_POST_CFILT_2_OFF",
78 "WCD9XXX_EVENT_PRE_CFILT_3_OFF",
79 "WCD9XXX_EVENT_POST_CFILT_3_OFF",
80 "WCD9XXX_EVENT_PRE_CFILT_1_ON",
81 "WCD9XXX_EVENT_POST_CFILT_1_ON",
82 "WCD9XXX_EVENT_PRE_CFILT_2_ON",
83 "WCD9XXX_EVENT_POST_CFILT_2_ON",
84 "WCD9XXX_EVENT_PRE_CFILT_3_ON",
85 "WCD9XXX_EVENT_POST_CFILT_3_ON",
86
87 "WCD9XXX_EVENT_PRE_HPHL_PA_ON",
88 "WCD9XXX_EVENT_POST_HPHL_PA_OFF",
89 "WCD9XXX_EVENT_PRE_HPHR_PA_ON",
90 "WCD9XXX_EVENT_POST_HPHR_PA_OFF",
91
92 "WCD9XXX_EVENT_POST_RESUME",
93
94 "WCD9XXX_EVENT_LAST",
95};
96
97static enum wcd9xxx_clock_type wcd9xxx_save_clock(struct wcd9xxx_resmgr
98 *resmgr);
99static void wcd9xxx_restore_clock(struct wcd9xxx_resmgr *resmgr,
100 enum wcd9xxx_clock_type type);
101
102const char *wcd9xxx_get_event_string(enum wcd9xxx_notify_event type)
103{
104 return wcd9xxx_event_string[type];
105}
106
107void wcd9xxx_resmgr_notifier_call(struct wcd9xxx_resmgr *resmgr,
108 const enum wcd9xxx_notify_event e)
109{
110 pr_debug("%s: notifier call event %d\n", __func__, e);
111 blocking_notifier_call_chain(&resmgr->notifier, e, resmgr);
112}
113
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -0700114static void wcd9xxx_disable_bg(struct wcd9xxx_resmgr *resmgr)
Joonwoo Parka8890262012-10-15 12:04:27 -0700115{
116 /* Notify bg mode change */
117 wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_OFF);
118 /* Disable bg */
119 snd_soc_write(resmgr->codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x00);
120 usleep_range(100, 100);
121 /* Notify bg mode change */
122 wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_OFF);
123}
124
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -0700125/*
126 * BG enablement should always enable in slow mode.
127 * The fast mode doesn't need to be enabled as fast mode BG is to be driven
128 * by MBHC override.
129 */
130static void wcd9xxx_enable_bg(struct wcd9xxx_resmgr *resmgr)
Joonwoo Parka8890262012-10-15 12:04:27 -0700131{
132 struct snd_soc_codec *codec = resmgr->codec;
133
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -0700134 /* Enable BG in slow mode and precharge */
Joonwoo Parka8890262012-10-15 12:04:27 -0700135 snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x80);
136 snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x04, 0x04);
137 snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x01, 0x01);
138 usleep_range(1000, 1000);
139 snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x00);
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -0700140}
141
142static void wcd9xxx_enable_bg_audio(struct wcd9xxx_resmgr *resmgr)
143{
144 /* Notify bandgap mode change */
145 wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_AUDIO_ON);
146 wcd9xxx_enable_bg(resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -0700147 /* Notify bandgap mode change */
148 wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_AUDIO_ON);
149}
150
151static void wcd9xxx_enable_bg_mbhc(struct wcd9xxx_resmgr *resmgr)
152{
153 struct snd_soc_codec *codec = resmgr->codec;
154
155 /* Notify bandgap mode change */
156 wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_MBHC_ON);
157
158 /*
Joonwoo Parka8890262012-10-15 12:04:27 -0700159 * mclk should be off or clk buff source souldn't be VBG
160 * Let's turn off mclk always
161 */
162 WARN_ON(snd_soc_read(codec, WCD9XXX_A_CLK_BUFF_EN2) & (1 << 2));
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -0700163 wcd9xxx_enable_bg(resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -0700164 /* Notify bandgap mode change */
165 wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_MBHC_ON);
166}
167
Joonwoo Parka8890262012-10-15 12:04:27 -0700168static void wcd9xxx_disable_clock_block(struct wcd9xxx_resmgr *resmgr)
169{
170 struct snd_soc_codec *codec = resmgr->codec;
171
172 pr_debug("%s: enter\n", __func__);
173 WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
174
175 /* Notify */
176 if (resmgr->clk_type == WCD9XXX_CLK_RCO)
177 wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_RCO_OFF);
178 else
179 wcd9xxx_resmgr_notifier_call(resmgr,
180 WCD9XXX_EVENT_PRE_MCLK_OFF);
181 /* Disable clock */
182 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x04, 0x00);
183 usleep_range(50, 50);
184 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02, 0x02);
185 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x00);
186 usleep_range(50, 50);
187 /* Notify */
188 if (resmgr->clk_type == WCD9XXX_CLK_RCO)
189 wcd9xxx_resmgr_notifier_call(resmgr,
190 WCD9XXX_EVENT_POST_RCO_OFF);
191 else
192 wcd9xxx_resmgr_notifier_call(resmgr,
193 WCD9XXX_EVENT_POST_MCLK_OFF);
194 pr_debug("%s: leave\n", __func__);
195}
196
197/*
198 * wcd9xxx_resmgr_get_bandgap : Vote for bandgap ref
199 * choice : WCD9XXX_BANDGAP_AUDIO_MODE, WCD9XXX_BANDGAP_MBHC_MODE
200 */
201void wcd9xxx_resmgr_get_bandgap(struct wcd9xxx_resmgr *resmgr,
202 const enum wcd9xxx_bandgap_type choice)
203{
204 enum wcd9xxx_clock_type clock_save;
205
206 pr_debug("%s: enter, wants %d\n", __func__, choice);
207
208 WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
209 switch (choice) {
210 case WCD9XXX_BANDGAP_AUDIO_MODE:
211 resmgr->bg_audio_users++;
212 if (resmgr->bg_audio_users == 1 && resmgr->bg_mbhc_users) {
213 /*
214 * Current bg is MBHC mode, about to switch to
215 * audio mode.
216 */
217 WARN_ON(resmgr->bandgap_type !=
218 WCD9XXX_BANDGAP_MBHC_MODE);
219
220 /* BG mode can be changed only with clock off */
221 clock_save = wcd9xxx_save_clock(resmgr);
222 /* Swtich BG mode */
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -0700223 wcd9xxx_disable_bg(resmgr);
224 wcd9xxx_enable_bg_audio(resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -0700225 /* restore clock */
226 wcd9xxx_restore_clock(resmgr, clock_save);
227 } else if (resmgr->bg_audio_users == 1) {
228 /* currently off, just enable it */
229 WARN_ON(resmgr->bandgap_type != WCD9XXX_BANDGAP_OFF);
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -0700230 wcd9xxx_enable_bg_audio(resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -0700231 }
232 resmgr->bandgap_type = WCD9XXX_BANDGAP_AUDIO_MODE;
233 break;
234 case WCD9XXX_BANDGAP_MBHC_MODE:
235 resmgr->bg_mbhc_users++;
236 if (resmgr->bandgap_type == WCD9XXX_BANDGAP_MBHC_MODE ||
237 resmgr->bandgap_type == WCD9XXX_BANDGAP_AUDIO_MODE)
238 /* do nothing */
239 break;
240
241 /* bg mode can be changed only with clock off */
242 clock_save = wcd9xxx_save_clock(resmgr);
243 /* enable bg with MBHC mode */
244 wcd9xxx_enable_bg_mbhc(resmgr);
245 /* restore clock */
246 wcd9xxx_restore_clock(resmgr, clock_save);
247 /* save current mode */
248 resmgr->bandgap_type = WCD9XXX_BANDGAP_MBHC_MODE;
249 break;
250 default:
251 pr_err("%s: Error, Invalid bandgap settings\n", __func__);
252 break;
253 }
254
255 pr_debug("%s: bg users audio %d, mbhc %d\n", __func__,
256 resmgr->bg_audio_users, resmgr->bg_mbhc_users);
257}
258
259/*
260 * wcd9xxx_resmgr_put_bandgap : Unvote bandgap ref that has been voted
261 * choice : WCD9XXX_BANDGAP_AUDIO_MODE, WCD9XXX_BANDGAP_MBHC_MODE
262 */
263void wcd9xxx_resmgr_put_bandgap(struct wcd9xxx_resmgr *resmgr,
264 enum wcd9xxx_bandgap_type choice)
265{
266 enum wcd9xxx_clock_type clock_save;
267
268 pr_debug("%s: enter choice %d\n", __func__, choice);
269
270 WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
271 switch (choice) {
272 case WCD9XXX_BANDGAP_AUDIO_MODE:
273 if (--resmgr->bg_audio_users == 0) {
274 if (resmgr->bg_mbhc_users) {
275 /* bg mode can be changed only with clock off */
276 clock_save = wcd9xxx_save_clock(resmgr);
277 /* switch to MBHC mode */
278 wcd9xxx_enable_bg_mbhc(resmgr);
279 /* restore clock */
280 wcd9xxx_restore_clock(resmgr, clock_save);
281 resmgr->bandgap_type =
282 WCD9XXX_BANDGAP_MBHC_MODE;
283 } else {
284 /* turn off */
285 wcd9xxx_disable_bg(resmgr);
286 resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
287 }
288 }
289 break;
290 case WCD9XXX_BANDGAP_MBHC_MODE:
291 WARN(resmgr->bandgap_type == WCD9XXX_BANDGAP_OFF,
292 "Unexpected bandgap type %d\n", resmgr->bandgap_type);
293 if (--resmgr->bg_mbhc_users == 0 &&
294 resmgr->bandgap_type == WCD9XXX_BANDGAP_MBHC_MODE) {
295 wcd9xxx_disable_bg(resmgr);
296 resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
297 }
298 break;
299 default:
300 pr_err("%s: Error, Invalid bandgap settings\n", __func__);
301 break;
302 }
303
304 pr_debug("%s: bg users audio %d, mbhc %d\n", __func__,
305 resmgr->bg_audio_users, resmgr->bg_mbhc_users);
306}
307
308void wcd9xxx_resmgr_enable_rx_bias(struct wcd9xxx_resmgr *resmgr, u32 enable)
309{
310 struct snd_soc_codec *codec = resmgr->codec;
311
312 if (enable) {
313 resmgr->rx_bias_count++;
314 if (resmgr->rx_bias_count == 1)
315 snd_soc_update_bits(codec, WCD9XXX_A_RX_COM_BIAS,
316 0x80, 0x80);
317 } else {
318 resmgr->rx_bias_count--;
319 if (!resmgr->rx_bias_count)
320 snd_soc_update_bits(codec, WCD9XXX_A_RX_COM_BIAS,
321 0x80, 0x00);
322 }
323}
324
325int wcd9xxx_resmgr_enable_config_mode(struct snd_soc_codec *codec, int enable)
326{
327 pr_debug("%s: enable = %d\n", __func__, enable);
328 if (enable) {
329 snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x10, 0);
330 /* bandgap mode to fast */
331 snd_soc_write(codec, WCD9XXX_A_BIAS_OSC_BG_CTL, 0x17);
332 usleep_range(5, 5);
333 snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x80, 0x80);
334 snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_TEST, 0x80, 0x80);
335 usleep_range(10, 10);
336 snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_TEST, 0x80, 0);
337 usleep_range(10000, 10000);
338 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x08, 0x08);
339 } else {
340 snd_soc_update_bits(codec, WCD9XXX_A_BIAS_OSC_BG_CTL, 0x1, 0);
341 snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x80, 0);
342 /* clk source to ext clk and clk buff ref to VBG */
343 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x0C, 0x04);
344 }
345
346 return 0;
347}
348
349static void wcd9xxx_enable_clock_block(struct wcd9xxx_resmgr *resmgr,
350 int config_mode)
351{
352 struct snd_soc_codec *codec = resmgr->codec;
353
354 pr_debug("%s: config_mode = %d\n", __func__, config_mode);
355 /* transit to RCO requires mclk off */
356 WARN_ON(snd_soc_read(codec, WCD9XXX_A_CLK_BUFF_EN2) & (1 << 2));
357 if (config_mode) {
358 /* Notify */
359 wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_RCO_ON);
360 /* enable RCO and switch to it */
361 wcd9xxx_resmgr_enable_config_mode(codec, 1);
362 snd_soc_write(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02);
363 usleep_range(1000, 1000);
364 } else {
365 /* Notify */
366 wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_MCLK_ON);
367 /* switch to MCLK */
368 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x08, 0x00);
369 /* if RCO is enabled, switch from it */
370 if (snd_soc_read(codec, WCD9XXX_A_RC_OSC_FREQ) & 0x80) {
371 snd_soc_write(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02);
372 wcd9xxx_resmgr_enable_config_mode(codec, 0);
373 }
374 }
375
376 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x01, 0x01);
377 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02, 0x00);
378
379 /* on MCLK */
380 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x04, 0x04);
381 snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL, 0x01, 0x01);
382 usleep_range(50, 50);
383
384 /* Notify */
385 if (config_mode)
386 wcd9xxx_resmgr_notifier_call(resmgr,
387 WCD9XXX_EVENT_POST_RCO_ON);
388 else
389 wcd9xxx_resmgr_notifier_call(resmgr,
390 WCD9XXX_EVENT_POST_MCLK_ON);
391}
392
393/*
394 * disable clock and return previous clock state
395 */
396static enum wcd9xxx_clock_type wcd9xxx_save_clock(struct wcd9xxx_resmgr *resmgr)
397{
398 WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
399 if (resmgr->clk_type != WCD9XXX_CLK_OFF)
400 wcd9xxx_disable_clock_block(resmgr);
401 return resmgr->clk_type != WCD9XXX_CLK_OFF;
402}
403
404static void wcd9xxx_restore_clock(struct wcd9xxx_resmgr *resmgr,
405 enum wcd9xxx_clock_type type)
406{
407 if (type != WCD9XXX_CLK_OFF)
408 wcd9xxx_enable_clock_block(resmgr, type == WCD9XXX_CLK_RCO);
409}
410
411void wcd9xxx_resmgr_get_clk_block(struct wcd9xxx_resmgr *resmgr,
412 enum wcd9xxx_clock_type type)
413{
414 pr_debug("%s: current %d, requested %d, rco_users %d, mclk_users %d\n",
415 __func__, resmgr->clk_type, type,
416 resmgr->clk_rco_users, resmgr->clk_mclk_users);
417 WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
418 switch (type) {
419 case WCD9XXX_CLK_RCO:
420 if (++resmgr->clk_rco_users == 1 &&
421 resmgr->clk_type == WCD9XXX_CLK_OFF) {
422 /* enable RCO and switch to it */
423 wcd9xxx_enable_clock_block(resmgr, 1);
424 resmgr->clk_type = WCD9XXX_CLK_RCO;
425 }
426 break;
427 case WCD9XXX_CLK_MCLK:
428 if (++resmgr->clk_mclk_users == 1 &&
429 resmgr->clk_type == WCD9XXX_CLK_OFF) {
430 /* switch to MCLK */
431 wcd9xxx_enable_clock_block(resmgr, 0);
432 resmgr->clk_type = WCD9XXX_CLK_MCLK;
433 } else if (resmgr->clk_mclk_users == 1 &&
434 resmgr->clk_type == WCD9XXX_CLK_RCO) {
435 /* if RCO is enabled, switch from it */
436 WARN_ON(!(snd_soc_read(resmgr->codec,
437 WCD9XXX_A_RC_OSC_FREQ) & 0x80));
438 /* disable clock block */
439 wcd9xxx_disable_clock_block(resmgr);
440 /* switch to RCO */
441 wcd9xxx_enable_clock_block(resmgr, 0);
442 resmgr->clk_type = WCD9XXX_CLK_MCLK;
443 }
444 break;
445 default:
446 pr_err("%s: Error, Invalid clock get request %d\n", __func__,
447 type);
448 break;
449 }
450 pr_debug("%s: leave\n", __func__);
451}
452
453void wcd9xxx_resmgr_put_clk_block(struct wcd9xxx_resmgr *resmgr,
454 enum wcd9xxx_clock_type type)
455{
456 pr_debug("%s: current %d, put %d\n", __func__, resmgr->clk_type, type);
457
458 WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
459 switch (type) {
460 case WCD9XXX_CLK_RCO:
461 if (--resmgr->clk_rco_users == 0 &&
462 resmgr->clk_type == WCD9XXX_CLK_RCO) {
463 wcd9xxx_disable_clock_block(resmgr);
464 resmgr->clk_type = WCD9XXX_CLK_OFF;
465 }
466 break;
467 case WCD9XXX_CLK_MCLK:
468 if (--resmgr->clk_mclk_users == 0 &&
469 resmgr->clk_rco_users == 0) {
470 wcd9xxx_disable_clock_block(resmgr);
471 resmgr->clk_type = WCD9XXX_CLK_OFF;
472 } else if (resmgr->clk_mclk_users == 0 &&
473 resmgr->clk_rco_users) {
474 /* disable clock */
475 wcd9xxx_disable_clock_block(resmgr);
476 /* switch to RCO */
477 wcd9xxx_enable_clock_block(resmgr, 1);
478 resmgr->clk_type = WCD9XXX_CLK_RCO;
479 }
480 break;
481 default:
482 pr_err("%s: Error, Invalid clock get request %d\n", __func__,
483 type);
484 break;
485 }
486 WARN_ON(resmgr->clk_rco_users < 0);
487 WARN_ON(resmgr->clk_mclk_users < 0);
488
489 pr_debug("%s: new rco_users %d, mclk_users %d\n", __func__,
490 resmgr->clk_rco_users, resmgr->clk_mclk_users);
491}
492
493static void wcd9xxx_resmgr_update_cfilt_usage(struct wcd9xxx_resmgr *resmgr,
494 enum wcd9xxx_cfilt_sel cfilt_sel,
495 bool inc)
496{
497 u16 micb_cfilt_reg;
498 enum wcd9xxx_notify_event e_pre_on, e_post_off;
499 struct snd_soc_codec *codec = resmgr->codec;
500
501 switch (cfilt_sel) {
502 case WCD9XXX_CFILT1_SEL:
503 micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_1_CTL;
504 e_pre_on = WCD9XXX_EVENT_PRE_CFILT_1_ON;
505 e_post_off = WCD9XXX_EVENT_POST_CFILT_1_OFF;
506 break;
507 case WCD9XXX_CFILT2_SEL:
508 micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_2_CTL;
509 e_pre_on = WCD9XXX_EVENT_PRE_CFILT_2_ON;
510 e_post_off = WCD9XXX_EVENT_POST_CFILT_2_OFF;
511 break;
512 case WCD9XXX_CFILT3_SEL:
513 micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_3_CTL;
514 e_pre_on = WCD9XXX_EVENT_PRE_CFILT_3_ON;
515 e_post_off = WCD9XXX_EVENT_POST_CFILT_3_OFF;
516 break;
517 default:
518 WARN(1, "Invalid CFILT selection %d\n", cfilt_sel);
519 return; /* should not happen */
520 }
521
522 if (inc) {
523 if ((resmgr->cfilt_users[cfilt_sel]++) == 0) {
524 /* Notify */
525 wcd9xxx_resmgr_notifier_call(resmgr, e_pre_on);
526 /* Enable CFILT */
527 snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0x80);
528 }
529 } else {
530 /*
531 * Check if count not zero, decrease
532 * then check if zero, go ahead disable cfilter
533 */
534 WARN(resmgr->cfilt_users[cfilt_sel] == 0,
535 "Invalid CFILT use count 0\n");
536 if ((--resmgr->cfilt_users[cfilt_sel]) == 0) {
537 /* Disable CFILT */
538 snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0);
539 /* Notify MBHC so MBHC can switch CFILT to fast mode */
540 wcd9xxx_resmgr_notifier_call(resmgr, e_post_off);
541 }
542 }
543}
544
545void wcd9xxx_resmgr_cfilt_get(struct wcd9xxx_resmgr *resmgr,
546 enum wcd9xxx_cfilt_sel cfilt_sel)
547{
548 return wcd9xxx_resmgr_update_cfilt_usage(resmgr, cfilt_sel, true);
549}
550
551void wcd9xxx_resmgr_cfilt_put(struct wcd9xxx_resmgr *resmgr,
552 enum wcd9xxx_cfilt_sel cfilt_sel)
553{
554 return wcd9xxx_resmgr_update_cfilt_usage(resmgr, cfilt_sel, false);
555}
556
557int wcd9xxx_resmgr_get_k_val(struct wcd9xxx_resmgr *resmgr,
558 unsigned int cfilt_mv)
559{
560 int rc = -EINVAL;
561 unsigned int ldoh_v = resmgr->pdata->micbias.ldoh_v;
562 unsigned min_mv, max_mv;
563
564 switch (ldoh_v) {
565 case WCD9XXX_LDOH_1P95_V:
566 min_mv = 160;
567 max_mv = 1800;
568 break;
569 case WCD9XXX_LDOH_2P35_V:
570 min_mv = 200;
571 max_mv = 2200;
572 break;
573 case WCD9XXX_LDOH_2P75_V:
574 min_mv = 240;
575 max_mv = 2600;
576 break;
577 case WCD9XXX_LDOH_3P0_V:
578 min_mv = 260;
579 max_mv = 2875;
580 break;
581 default:
582 goto done;
583 }
584
585 if (cfilt_mv < min_mv || cfilt_mv > max_mv)
586 goto done;
587
588 for (rc = 4; rc <= 44; rc++) {
589 min_mv = max_mv * (rc) / 44;
590 if (min_mv >= cfilt_mv) {
591 rc -= 4;
592 break;
593 }
594 }
595done:
596 return rc;
597}
598
599int wcd9xxx_resmgr_register_notifier(struct wcd9xxx_resmgr *resmgr,
600 struct notifier_block *nblock)
601{
602 return blocking_notifier_chain_register(&resmgr->notifier, nblock);
603}
604
605int wcd9xxx_resmgr_unregister_notifier(struct wcd9xxx_resmgr *resmgr,
606 struct notifier_block *nblock)
607{
608 return blocking_notifier_chain_unregister(&resmgr->notifier, nblock);
609}
610
611int wcd9xxx_resmgr_init(struct wcd9xxx_resmgr *resmgr,
612 struct snd_soc_codec *codec,
613 struct wcd9xxx *wcd9xxx,
614 struct wcd9xxx_pdata *pdata,
615 struct wcd9xxx_reg_address *reg_addr)
616{
617 WARN(ARRAY_SIZE(wcd9xxx_event_string) != WCD9XXX_EVENT_LAST + 1,
618 "Event string table isn't up to date!, %d != %d\n",
619 ARRAY_SIZE(wcd9xxx_event_string), WCD9XXX_EVENT_LAST + 1);
620
621 resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
622 resmgr->codec = codec;
623 /* This gives access of core handle to lock/unlock suspend */
624 resmgr->core = wcd9xxx;
625 resmgr->pdata = pdata;
626 resmgr->reg_addr = reg_addr;
627
628 BLOCKING_INIT_NOTIFIER_HEAD(&resmgr->notifier);
629
630 mutex_init(&resmgr->codec_resource_lock);
631
632 return 0;
633}
634
635void wcd9xxx_resmgr_deinit(struct wcd9xxx_resmgr *resmgr)
636{
637 mutex_destroy(&resmgr->codec_resource_lock);
638}
639
640void wcd9xxx_resmgr_bcl_lock(struct wcd9xxx_resmgr *resmgr)
641{
642 mutex_lock(&resmgr->codec_resource_lock);
643}
644
645void wcd9xxx_resmgr_bcl_unlock(struct wcd9xxx_resmgr *resmgr)
646{
647 mutex_unlock(&resmgr->codec_resource_lock);
648}
649
650MODULE_DESCRIPTION("wcd9xxx resmgr module");
651MODULE_LICENSE("GPL v2");