blob: 0ab650eb044e2189f20ef7f061a5dc9fc6659ce5 [file] [log] [blame]
Joonwoo Park11133b82013-01-03 17:26:38 -08001/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Joonwoo Parka8890262012-10-15 12:04:27 -07002 *
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 */
Joonwoo Park11133b82013-01-03 17:26:38 -0800119 snd_soc_update_bits(resmgr->codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL,
120 0x03, 0x00);
Joonwoo Parka8890262012-10-15 12:04:27 -0700121 usleep_range(100, 100);
122 /* Notify bg mode change */
123 wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_OFF);
124}
125
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -0700126/*
127 * BG enablement should always enable in slow mode.
128 * The fast mode doesn't need to be enabled as fast mode BG is to be driven
129 * by MBHC override.
130 */
131static void wcd9xxx_enable_bg(struct wcd9xxx_resmgr *resmgr)
Joonwoo Parka8890262012-10-15 12:04:27 -0700132{
133 struct snd_soc_codec *codec = resmgr->codec;
134
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -0700135 /* Enable BG in slow mode and precharge */
Joonwoo Parka8890262012-10-15 12:04:27 -0700136 snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x80);
137 snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x04, 0x04);
138 snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x01, 0x01);
139 usleep_range(1000, 1000);
140 snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x00);
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -0700141}
142
143static void wcd9xxx_enable_bg_audio(struct wcd9xxx_resmgr *resmgr)
144{
145 /* Notify bandgap mode change */
146 wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_AUDIO_ON);
147 wcd9xxx_enable_bg(resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -0700148 /* Notify bandgap mode change */
149 wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_AUDIO_ON);
150}
151
152static void wcd9xxx_enable_bg_mbhc(struct wcd9xxx_resmgr *resmgr)
153{
154 struct snd_soc_codec *codec = resmgr->codec;
155
156 /* Notify bandgap mode change */
157 wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_MBHC_ON);
158
159 /*
Joonwoo Parka8890262012-10-15 12:04:27 -0700160 * mclk should be off or clk buff source souldn't be VBG
161 * Let's turn off mclk always
162 */
163 WARN_ON(snd_soc_read(codec, WCD9XXX_A_CLK_BUFF_EN2) & (1 << 2));
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -0700164 wcd9xxx_enable_bg(resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -0700165 /* Notify bandgap mode change */
166 wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_MBHC_ON);
167}
168
Joonwoo Parka8890262012-10-15 12:04:27 -0700169static void wcd9xxx_disable_clock_block(struct wcd9xxx_resmgr *resmgr)
170{
171 struct snd_soc_codec *codec = resmgr->codec;
172
173 pr_debug("%s: enter\n", __func__);
174 WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
175
176 /* Notify */
177 if (resmgr->clk_type == WCD9XXX_CLK_RCO)
178 wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_RCO_OFF);
179 else
180 wcd9xxx_resmgr_notifier_call(resmgr,
181 WCD9XXX_EVENT_PRE_MCLK_OFF);
182 /* Disable clock */
183 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x04, 0x00);
184 usleep_range(50, 50);
185 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02, 0x02);
186 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x00);
187 usleep_range(50, 50);
188 /* Notify */
189 if (resmgr->clk_type == WCD9XXX_CLK_RCO)
190 wcd9xxx_resmgr_notifier_call(resmgr,
191 WCD9XXX_EVENT_POST_RCO_OFF);
192 else
193 wcd9xxx_resmgr_notifier_call(resmgr,
194 WCD9XXX_EVENT_POST_MCLK_OFF);
195 pr_debug("%s: leave\n", __func__);
196}
197
Ravishankar Sarawadi839fcf32012-11-14 12:13:00 -0800198void wcd9xxx_resmgr_post_ssr(struct wcd9xxx_resmgr *resmgr)
199{
200 int old_bg_audio_users, old_bg_mbhc_users;
201
202 WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
203
204 old_bg_audio_users = resmgr->bg_audio_users;
205 resmgr->bg_audio_users = 0;
206 old_bg_mbhc_users = resmgr->bg_mbhc_users;
207 resmgr->bg_mbhc_users = 0;
208
209 while (old_bg_audio_users && --old_bg_audio_users)
210 wcd9xxx_resmgr_get_bandgap(resmgr, WCD9XXX_BANDGAP_AUDIO_MODE);
211
212 while (old_bg_mbhc_users && --old_bg_mbhc_users)
213 wcd9xxx_resmgr_get_bandgap(resmgr, WCD9XXX_BANDGAP_MBHC_MODE);
214}
215
Joonwoo Parka8890262012-10-15 12:04:27 -0700216/*
217 * wcd9xxx_resmgr_get_bandgap : Vote for bandgap ref
218 * choice : WCD9XXX_BANDGAP_AUDIO_MODE, WCD9XXX_BANDGAP_MBHC_MODE
219 */
220void wcd9xxx_resmgr_get_bandgap(struct wcd9xxx_resmgr *resmgr,
221 const enum wcd9xxx_bandgap_type choice)
222{
223 enum wcd9xxx_clock_type clock_save;
224
225 pr_debug("%s: enter, wants %d\n", __func__, choice);
226
227 WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
228 switch (choice) {
229 case WCD9XXX_BANDGAP_AUDIO_MODE:
230 resmgr->bg_audio_users++;
231 if (resmgr->bg_audio_users == 1 && resmgr->bg_mbhc_users) {
232 /*
233 * Current bg is MBHC mode, about to switch to
234 * audio mode.
235 */
236 WARN_ON(resmgr->bandgap_type !=
237 WCD9XXX_BANDGAP_MBHC_MODE);
238
239 /* BG mode can be changed only with clock off */
240 clock_save = wcd9xxx_save_clock(resmgr);
241 /* Swtich BG mode */
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -0700242 wcd9xxx_disable_bg(resmgr);
243 wcd9xxx_enable_bg_audio(resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -0700244 /* restore clock */
245 wcd9xxx_restore_clock(resmgr, clock_save);
246 } else if (resmgr->bg_audio_users == 1) {
247 /* currently off, just enable it */
248 WARN_ON(resmgr->bandgap_type != WCD9XXX_BANDGAP_OFF);
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -0700249 wcd9xxx_enable_bg_audio(resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -0700250 }
251 resmgr->bandgap_type = WCD9XXX_BANDGAP_AUDIO_MODE;
252 break;
253 case WCD9XXX_BANDGAP_MBHC_MODE:
254 resmgr->bg_mbhc_users++;
255 if (resmgr->bandgap_type == WCD9XXX_BANDGAP_MBHC_MODE ||
256 resmgr->bandgap_type == WCD9XXX_BANDGAP_AUDIO_MODE)
257 /* do nothing */
258 break;
259
260 /* bg mode can be changed only with clock off */
261 clock_save = wcd9xxx_save_clock(resmgr);
262 /* enable bg with MBHC mode */
263 wcd9xxx_enable_bg_mbhc(resmgr);
264 /* restore clock */
265 wcd9xxx_restore_clock(resmgr, clock_save);
266 /* save current mode */
267 resmgr->bandgap_type = WCD9XXX_BANDGAP_MBHC_MODE;
268 break;
269 default:
270 pr_err("%s: Error, Invalid bandgap settings\n", __func__);
271 break;
272 }
273
274 pr_debug("%s: bg users audio %d, mbhc %d\n", __func__,
275 resmgr->bg_audio_users, resmgr->bg_mbhc_users);
276}
277
278/*
279 * wcd9xxx_resmgr_put_bandgap : Unvote bandgap ref that has been voted
280 * choice : WCD9XXX_BANDGAP_AUDIO_MODE, WCD9XXX_BANDGAP_MBHC_MODE
281 */
282void wcd9xxx_resmgr_put_bandgap(struct wcd9xxx_resmgr *resmgr,
283 enum wcd9xxx_bandgap_type choice)
284{
285 enum wcd9xxx_clock_type clock_save;
286
287 pr_debug("%s: enter choice %d\n", __func__, choice);
288
289 WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
290 switch (choice) {
291 case WCD9XXX_BANDGAP_AUDIO_MODE:
292 if (--resmgr->bg_audio_users == 0) {
293 if (resmgr->bg_mbhc_users) {
294 /* bg mode can be changed only with clock off */
295 clock_save = wcd9xxx_save_clock(resmgr);
296 /* switch to MBHC mode */
297 wcd9xxx_enable_bg_mbhc(resmgr);
298 /* restore clock */
299 wcd9xxx_restore_clock(resmgr, clock_save);
300 resmgr->bandgap_type =
301 WCD9XXX_BANDGAP_MBHC_MODE;
302 } else {
303 /* turn off */
304 wcd9xxx_disable_bg(resmgr);
305 resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
306 }
307 }
308 break;
309 case WCD9XXX_BANDGAP_MBHC_MODE:
310 WARN(resmgr->bandgap_type == WCD9XXX_BANDGAP_OFF,
311 "Unexpected bandgap type %d\n", resmgr->bandgap_type);
312 if (--resmgr->bg_mbhc_users == 0 &&
313 resmgr->bandgap_type == WCD9XXX_BANDGAP_MBHC_MODE) {
314 wcd9xxx_disable_bg(resmgr);
315 resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
316 }
317 break;
318 default:
319 pr_err("%s: Error, Invalid bandgap settings\n", __func__);
320 break;
321 }
322
323 pr_debug("%s: bg users audio %d, mbhc %d\n", __func__,
324 resmgr->bg_audio_users, resmgr->bg_mbhc_users);
325}
326
327void wcd9xxx_resmgr_enable_rx_bias(struct wcd9xxx_resmgr *resmgr, u32 enable)
328{
329 struct snd_soc_codec *codec = resmgr->codec;
330
331 if (enable) {
332 resmgr->rx_bias_count++;
333 if (resmgr->rx_bias_count == 1)
334 snd_soc_update_bits(codec, WCD9XXX_A_RX_COM_BIAS,
335 0x80, 0x80);
336 } else {
337 resmgr->rx_bias_count--;
338 if (!resmgr->rx_bias_count)
339 snd_soc_update_bits(codec, WCD9XXX_A_RX_COM_BIAS,
340 0x80, 0x00);
341 }
342}
343
344int wcd9xxx_resmgr_enable_config_mode(struct snd_soc_codec *codec, int enable)
345{
346 pr_debug("%s: enable = %d\n", __func__, enable);
347 if (enable) {
348 snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x10, 0);
349 /* bandgap mode to fast */
350 snd_soc_write(codec, WCD9XXX_A_BIAS_OSC_BG_CTL, 0x17);
351 usleep_range(5, 5);
352 snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x80, 0x80);
353 snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_TEST, 0x80, 0x80);
354 usleep_range(10, 10);
355 snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_TEST, 0x80, 0);
356 usleep_range(10000, 10000);
357 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x08, 0x08);
358 } else {
359 snd_soc_update_bits(codec, WCD9XXX_A_BIAS_OSC_BG_CTL, 0x1, 0);
360 snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x80, 0);
361 /* clk source to ext clk and clk buff ref to VBG */
362 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x0C, 0x04);
363 }
364
365 return 0;
366}
367
368static void wcd9xxx_enable_clock_block(struct wcd9xxx_resmgr *resmgr,
369 int config_mode)
370{
371 struct snd_soc_codec *codec = resmgr->codec;
372
373 pr_debug("%s: config_mode = %d\n", __func__, config_mode);
374 /* transit to RCO requires mclk off */
375 WARN_ON(snd_soc_read(codec, WCD9XXX_A_CLK_BUFF_EN2) & (1 << 2));
376 if (config_mode) {
377 /* Notify */
378 wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_RCO_ON);
379 /* enable RCO and switch to it */
380 wcd9xxx_resmgr_enable_config_mode(codec, 1);
381 snd_soc_write(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02);
382 usleep_range(1000, 1000);
383 } else {
384 /* Notify */
385 wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_MCLK_ON);
386 /* switch to MCLK */
387 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x08, 0x00);
388 /* if RCO is enabled, switch from it */
389 if (snd_soc_read(codec, WCD9XXX_A_RC_OSC_FREQ) & 0x80) {
390 snd_soc_write(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02);
391 wcd9xxx_resmgr_enable_config_mode(codec, 0);
392 }
393 }
394
395 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x01, 0x01);
396 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02, 0x00);
397
398 /* on MCLK */
399 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x04, 0x04);
400 snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL, 0x01, 0x01);
401 usleep_range(50, 50);
402
403 /* Notify */
404 if (config_mode)
405 wcd9xxx_resmgr_notifier_call(resmgr,
406 WCD9XXX_EVENT_POST_RCO_ON);
407 else
408 wcd9xxx_resmgr_notifier_call(resmgr,
409 WCD9XXX_EVENT_POST_MCLK_ON);
410}
411
412/*
413 * disable clock and return previous clock state
414 */
415static enum wcd9xxx_clock_type wcd9xxx_save_clock(struct wcd9xxx_resmgr *resmgr)
416{
417 WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
418 if (resmgr->clk_type != WCD9XXX_CLK_OFF)
419 wcd9xxx_disable_clock_block(resmgr);
420 return resmgr->clk_type != WCD9XXX_CLK_OFF;
421}
422
423static void wcd9xxx_restore_clock(struct wcd9xxx_resmgr *resmgr,
424 enum wcd9xxx_clock_type type)
425{
426 if (type != WCD9XXX_CLK_OFF)
427 wcd9xxx_enable_clock_block(resmgr, type == WCD9XXX_CLK_RCO);
428}
429
430void wcd9xxx_resmgr_get_clk_block(struct wcd9xxx_resmgr *resmgr,
431 enum wcd9xxx_clock_type type)
432{
433 pr_debug("%s: current %d, requested %d, rco_users %d, mclk_users %d\n",
434 __func__, resmgr->clk_type, type,
435 resmgr->clk_rco_users, resmgr->clk_mclk_users);
436 WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
437 switch (type) {
438 case WCD9XXX_CLK_RCO:
439 if (++resmgr->clk_rco_users == 1 &&
440 resmgr->clk_type == WCD9XXX_CLK_OFF) {
441 /* enable RCO and switch to it */
442 wcd9xxx_enable_clock_block(resmgr, 1);
443 resmgr->clk_type = WCD9XXX_CLK_RCO;
444 }
445 break;
446 case WCD9XXX_CLK_MCLK:
447 if (++resmgr->clk_mclk_users == 1 &&
448 resmgr->clk_type == WCD9XXX_CLK_OFF) {
449 /* switch to MCLK */
450 wcd9xxx_enable_clock_block(resmgr, 0);
451 resmgr->clk_type = WCD9XXX_CLK_MCLK;
452 } else if (resmgr->clk_mclk_users == 1 &&
453 resmgr->clk_type == WCD9XXX_CLK_RCO) {
454 /* if RCO is enabled, switch from it */
455 WARN_ON(!(snd_soc_read(resmgr->codec,
456 WCD9XXX_A_RC_OSC_FREQ) & 0x80));
457 /* disable clock block */
458 wcd9xxx_disable_clock_block(resmgr);
459 /* switch to RCO */
460 wcd9xxx_enable_clock_block(resmgr, 0);
461 resmgr->clk_type = WCD9XXX_CLK_MCLK;
462 }
463 break;
464 default:
465 pr_err("%s: Error, Invalid clock get request %d\n", __func__,
466 type);
467 break;
468 }
469 pr_debug("%s: leave\n", __func__);
470}
471
472void wcd9xxx_resmgr_put_clk_block(struct wcd9xxx_resmgr *resmgr,
473 enum wcd9xxx_clock_type type)
474{
475 pr_debug("%s: current %d, put %d\n", __func__, resmgr->clk_type, type);
476
477 WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
478 switch (type) {
479 case WCD9XXX_CLK_RCO:
480 if (--resmgr->clk_rco_users == 0 &&
481 resmgr->clk_type == WCD9XXX_CLK_RCO) {
482 wcd9xxx_disable_clock_block(resmgr);
483 resmgr->clk_type = WCD9XXX_CLK_OFF;
484 }
485 break;
486 case WCD9XXX_CLK_MCLK:
487 if (--resmgr->clk_mclk_users == 0 &&
488 resmgr->clk_rco_users == 0) {
489 wcd9xxx_disable_clock_block(resmgr);
490 resmgr->clk_type = WCD9XXX_CLK_OFF;
491 } else if (resmgr->clk_mclk_users == 0 &&
492 resmgr->clk_rco_users) {
493 /* disable clock */
494 wcd9xxx_disable_clock_block(resmgr);
495 /* switch to RCO */
496 wcd9xxx_enable_clock_block(resmgr, 1);
497 resmgr->clk_type = WCD9XXX_CLK_RCO;
498 }
499 break;
500 default:
501 pr_err("%s: Error, Invalid clock get request %d\n", __func__,
502 type);
503 break;
504 }
505 WARN_ON(resmgr->clk_rco_users < 0);
506 WARN_ON(resmgr->clk_mclk_users < 0);
507
508 pr_debug("%s: new rco_users %d, mclk_users %d\n", __func__,
509 resmgr->clk_rco_users, resmgr->clk_mclk_users);
510}
511
512static void wcd9xxx_resmgr_update_cfilt_usage(struct wcd9xxx_resmgr *resmgr,
513 enum wcd9xxx_cfilt_sel cfilt_sel,
514 bool inc)
515{
516 u16 micb_cfilt_reg;
517 enum wcd9xxx_notify_event e_pre_on, e_post_off;
518 struct snd_soc_codec *codec = resmgr->codec;
519
520 switch (cfilt_sel) {
521 case WCD9XXX_CFILT1_SEL:
522 micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_1_CTL;
523 e_pre_on = WCD9XXX_EVENT_PRE_CFILT_1_ON;
524 e_post_off = WCD9XXX_EVENT_POST_CFILT_1_OFF;
525 break;
526 case WCD9XXX_CFILT2_SEL:
527 micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_2_CTL;
528 e_pre_on = WCD9XXX_EVENT_PRE_CFILT_2_ON;
529 e_post_off = WCD9XXX_EVENT_POST_CFILT_2_OFF;
530 break;
531 case WCD9XXX_CFILT3_SEL:
532 micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_3_CTL;
533 e_pre_on = WCD9XXX_EVENT_PRE_CFILT_3_ON;
534 e_post_off = WCD9XXX_EVENT_POST_CFILT_3_OFF;
535 break;
536 default:
537 WARN(1, "Invalid CFILT selection %d\n", cfilt_sel);
538 return; /* should not happen */
539 }
540
541 if (inc) {
542 if ((resmgr->cfilt_users[cfilt_sel]++) == 0) {
543 /* Notify */
544 wcd9xxx_resmgr_notifier_call(resmgr, e_pre_on);
545 /* Enable CFILT */
546 snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0x80);
547 }
548 } else {
549 /*
550 * Check if count not zero, decrease
551 * then check if zero, go ahead disable cfilter
552 */
553 WARN(resmgr->cfilt_users[cfilt_sel] == 0,
554 "Invalid CFILT use count 0\n");
555 if ((--resmgr->cfilt_users[cfilt_sel]) == 0) {
556 /* Disable CFILT */
557 snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0);
558 /* Notify MBHC so MBHC can switch CFILT to fast mode */
559 wcd9xxx_resmgr_notifier_call(resmgr, e_post_off);
560 }
561 }
562}
563
564void wcd9xxx_resmgr_cfilt_get(struct wcd9xxx_resmgr *resmgr,
565 enum wcd9xxx_cfilt_sel cfilt_sel)
566{
567 return wcd9xxx_resmgr_update_cfilt_usage(resmgr, cfilt_sel, true);
568}
569
570void wcd9xxx_resmgr_cfilt_put(struct wcd9xxx_resmgr *resmgr,
571 enum wcd9xxx_cfilt_sel cfilt_sel)
572{
573 return wcd9xxx_resmgr_update_cfilt_usage(resmgr, cfilt_sel, false);
574}
575
576int wcd9xxx_resmgr_get_k_val(struct wcd9xxx_resmgr *resmgr,
577 unsigned int cfilt_mv)
578{
579 int rc = -EINVAL;
580 unsigned int ldoh_v = resmgr->pdata->micbias.ldoh_v;
581 unsigned min_mv, max_mv;
582
583 switch (ldoh_v) {
584 case WCD9XXX_LDOH_1P95_V:
585 min_mv = 160;
586 max_mv = 1800;
587 break;
588 case WCD9XXX_LDOH_2P35_V:
589 min_mv = 200;
590 max_mv = 2200;
591 break;
592 case WCD9XXX_LDOH_2P75_V:
593 min_mv = 240;
594 max_mv = 2600;
595 break;
596 case WCD9XXX_LDOH_3P0_V:
597 min_mv = 260;
598 max_mv = 2875;
599 break;
600 default:
601 goto done;
602 }
603
604 if (cfilt_mv < min_mv || cfilt_mv > max_mv)
605 goto done;
606
607 for (rc = 4; rc <= 44; rc++) {
608 min_mv = max_mv * (rc) / 44;
609 if (min_mv >= cfilt_mv) {
610 rc -= 4;
611 break;
612 }
613 }
614done:
615 return rc;
616}
617
618int wcd9xxx_resmgr_register_notifier(struct wcd9xxx_resmgr *resmgr,
619 struct notifier_block *nblock)
620{
621 return blocking_notifier_chain_register(&resmgr->notifier, nblock);
622}
623
624int wcd9xxx_resmgr_unregister_notifier(struct wcd9xxx_resmgr *resmgr,
625 struct notifier_block *nblock)
626{
627 return blocking_notifier_chain_unregister(&resmgr->notifier, nblock);
628}
629
630int wcd9xxx_resmgr_init(struct wcd9xxx_resmgr *resmgr,
631 struct snd_soc_codec *codec,
632 struct wcd9xxx *wcd9xxx,
633 struct wcd9xxx_pdata *pdata,
634 struct wcd9xxx_reg_address *reg_addr)
635{
636 WARN(ARRAY_SIZE(wcd9xxx_event_string) != WCD9XXX_EVENT_LAST + 1,
637 "Event string table isn't up to date!, %d != %d\n",
638 ARRAY_SIZE(wcd9xxx_event_string), WCD9XXX_EVENT_LAST + 1);
639
640 resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
641 resmgr->codec = codec;
642 /* This gives access of core handle to lock/unlock suspend */
643 resmgr->core = wcd9xxx;
644 resmgr->pdata = pdata;
645 resmgr->reg_addr = reg_addr;
646
647 BLOCKING_INIT_NOTIFIER_HEAD(&resmgr->notifier);
648
649 mutex_init(&resmgr->codec_resource_lock);
650
651 return 0;
652}
653
654void wcd9xxx_resmgr_deinit(struct wcd9xxx_resmgr *resmgr)
655{
656 mutex_destroy(&resmgr->codec_resource_lock);
657}
658
659void wcd9xxx_resmgr_bcl_lock(struct wcd9xxx_resmgr *resmgr)
660{
661 mutex_lock(&resmgr->codec_resource_lock);
662}
663
664void wcd9xxx_resmgr_bcl_unlock(struct wcd9xxx_resmgr *resmgr)
665{
666 mutex_unlock(&resmgr->codec_resource_lock);
667}
668
669MODULE_DESCRIPTION("wcd9xxx resmgr module");
670MODULE_LICENSE("GPL v2");