blob: 17edd4a5c223198798ecaf56c91daf719f2968e8 [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;
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -0800201 int old_clk_rco_users, old_clk_mclk_users;
Ravishankar Sarawadi839fcf32012-11-14 12:13:00 -0800202
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -0800203 pr_debug("%s: enter\n", __func__);
Ravishankar Sarawadi839fcf32012-11-14 12:13:00 -0800204 WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
205
206 old_bg_audio_users = resmgr->bg_audio_users;
Ravishankar Sarawadi839fcf32012-11-14 12:13:00 -0800207 old_bg_mbhc_users = resmgr->bg_mbhc_users;
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -0800208 old_clk_rco_users = resmgr->clk_rco_users;
209 old_clk_mclk_users = resmgr->clk_mclk_users;
210 resmgr->bg_audio_users = 0;
Ravishankar Sarawadi839fcf32012-11-14 12:13:00 -0800211 resmgr->bg_mbhc_users = 0;
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -0800212 resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
213 resmgr->clk_rco_users = 0;
214 resmgr->clk_mclk_users = 0;
215 resmgr->clk_type = WCD9XXX_CLK_OFF;
Ravishankar Sarawadi839fcf32012-11-14 12:13:00 -0800216
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -0800217 if (old_bg_audio_users) {
218 while (old_bg_audio_users--)
219 wcd9xxx_resmgr_get_bandgap(resmgr,
220 WCD9XXX_BANDGAP_AUDIO_MODE);
221 }
Ravishankar Sarawadi839fcf32012-11-14 12:13:00 -0800222
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -0800223 if (old_bg_mbhc_users) {
224 while (old_bg_mbhc_users--)
225 wcd9xxx_resmgr_get_bandgap(resmgr,
226 WCD9XXX_BANDGAP_MBHC_MODE);
227 }
228
229 if (old_clk_mclk_users) {
230 while (old_clk_mclk_users--)
231 wcd9xxx_resmgr_get_clk_block(resmgr, WCD9XXX_CLK_MCLK);
232 }
233
234 if (old_clk_rco_users) {
235 while (old_clk_rco_users--)
236 wcd9xxx_resmgr_get_clk_block(resmgr, WCD9XXX_CLK_RCO);
237 }
238 pr_debug("%s: leave\n", __func__);
Ravishankar Sarawadi839fcf32012-11-14 12:13:00 -0800239}
240
Joonwoo Parka8890262012-10-15 12:04:27 -0700241/*
242 * wcd9xxx_resmgr_get_bandgap : Vote for bandgap ref
243 * choice : WCD9XXX_BANDGAP_AUDIO_MODE, WCD9XXX_BANDGAP_MBHC_MODE
244 */
245void wcd9xxx_resmgr_get_bandgap(struct wcd9xxx_resmgr *resmgr,
246 const enum wcd9xxx_bandgap_type choice)
247{
248 enum wcd9xxx_clock_type clock_save;
249
250 pr_debug("%s: enter, wants %d\n", __func__, choice);
251
252 WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
253 switch (choice) {
254 case WCD9XXX_BANDGAP_AUDIO_MODE:
255 resmgr->bg_audio_users++;
256 if (resmgr->bg_audio_users == 1 && resmgr->bg_mbhc_users) {
257 /*
258 * Current bg is MBHC mode, about to switch to
259 * audio mode.
260 */
261 WARN_ON(resmgr->bandgap_type !=
262 WCD9XXX_BANDGAP_MBHC_MODE);
263
264 /* BG mode can be changed only with clock off */
265 clock_save = wcd9xxx_save_clock(resmgr);
266 /* Swtich BG mode */
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -0700267 wcd9xxx_disable_bg(resmgr);
268 wcd9xxx_enable_bg_audio(resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -0700269 /* restore clock */
270 wcd9xxx_restore_clock(resmgr, clock_save);
271 } else if (resmgr->bg_audio_users == 1) {
272 /* currently off, just enable it */
273 WARN_ON(resmgr->bandgap_type != WCD9XXX_BANDGAP_OFF);
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -0700274 wcd9xxx_enable_bg_audio(resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -0700275 }
276 resmgr->bandgap_type = WCD9XXX_BANDGAP_AUDIO_MODE;
277 break;
278 case WCD9XXX_BANDGAP_MBHC_MODE:
279 resmgr->bg_mbhc_users++;
280 if (resmgr->bandgap_type == WCD9XXX_BANDGAP_MBHC_MODE ||
281 resmgr->bandgap_type == WCD9XXX_BANDGAP_AUDIO_MODE)
282 /* do nothing */
283 break;
284
285 /* bg mode can be changed only with clock off */
286 clock_save = wcd9xxx_save_clock(resmgr);
287 /* enable bg with MBHC mode */
288 wcd9xxx_enable_bg_mbhc(resmgr);
289 /* restore clock */
290 wcd9xxx_restore_clock(resmgr, clock_save);
291 /* save current mode */
292 resmgr->bandgap_type = WCD9XXX_BANDGAP_MBHC_MODE;
293 break;
294 default:
295 pr_err("%s: Error, Invalid bandgap settings\n", __func__);
296 break;
297 }
298
299 pr_debug("%s: bg users audio %d, mbhc %d\n", __func__,
300 resmgr->bg_audio_users, resmgr->bg_mbhc_users);
301}
302
303/*
304 * wcd9xxx_resmgr_put_bandgap : Unvote bandgap ref that has been voted
305 * choice : WCD9XXX_BANDGAP_AUDIO_MODE, WCD9XXX_BANDGAP_MBHC_MODE
306 */
307void wcd9xxx_resmgr_put_bandgap(struct wcd9xxx_resmgr *resmgr,
308 enum wcd9xxx_bandgap_type choice)
309{
310 enum wcd9xxx_clock_type clock_save;
311
312 pr_debug("%s: enter choice %d\n", __func__, choice);
313
314 WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
315 switch (choice) {
316 case WCD9XXX_BANDGAP_AUDIO_MODE:
317 if (--resmgr->bg_audio_users == 0) {
318 if (resmgr->bg_mbhc_users) {
319 /* bg mode can be changed only with clock off */
320 clock_save = wcd9xxx_save_clock(resmgr);
321 /* switch to MBHC mode */
322 wcd9xxx_enable_bg_mbhc(resmgr);
323 /* restore clock */
324 wcd9xxx_restore_clock(resmgr, clock_save);
325 resmgr->bandgap_type =
326 WCD9XXX_BANDGAP_MBHC_MODE;
327 } else {
328 /* turn off */
329 wcd9xxx_disable_bg(resmgr);
330 resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
331 }
332 }
333 break;
334 case WCD9XXX_BANDGAP_MBHC_MODE:
335 WARN(resmgr->bandgap_type == WCD9XXX_BANDGAP_OFF,
336 "Unexpected bandgap type %d\n", resmgr->bandgap_type);
337 if (--resmgr->bg_mbhc_users == 0 &&
338 resmgr->bandgap_type == WCD9XXX_BANDGAP_MBHC_MODE) {
339 wcd9xxx_disable_bg(resmgr);
340 resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
341 }
342 break;
343 default:
344 pr_err("%s: Error, Invalid bandgap settings\n", __func__);
345 break;
346 }
347
348 pr_debug("%s: bg users audio %d, mbhc %d\n", __func__,
349 resmgr->bg_audio_users, resmgr->bg_mbhc_users);
350}
351
352void wcd9xxx_resmgr_enable_rx_bias(struct wcd9xxx_resmgr *resmgr, u32 enable)
353{
354 struct snd_soc_codec *codec = resmgr->codec;
355
356 if (enable) {
357 resmgr->rx_bias_count++;
358 if (resmgr->rx_bias_count == 1)
359 snd_soc_update_bits(codec, WCD9XXX_A_RX_COM_BIAS,
360 0x80, 0x80);
361 } else {
362 resmgr->rx_bias_count--;
363 if (!resmgr->rx_bias_count)
364 snd_soc_update_bits(codec, WCD9XXX_A_RX_COM_BIAS,
365 0x80, 0x00);
366 }
367}
368
369int wcd9xxx_resmgr_enable_config_mode(struct snd_soc_codec *codec, int enable)
370{
371 pr_debug("%s: enable = %d\n", __func__, enable);
372 if (enable) {
373 snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x10, 0);
374 /* bandgap mode to fast */
375 snd_soc_write(codec, WCD9XXX_A_BIAS_OSC_BG_CTL, 0x17);
376 usleep_range(5, 5);
377 snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x80, 0x80);
378 snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_TEST, 0x80, 0x80);
379 usleep_range(10, 10);
380 snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_TEST, 0x80, 0);
381 usleep_range(10000, 10000);
382 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x08, 0x08);
383 } else {
384 snd_soc_update_bits(codec, WCD9XXX_A_BIAS_OSC_BG_CTL, 0x1, 0);
385 snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x80, 0);
386 /* clk source to ext clk and clk buff ref to VBG */
387 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x0C, 0x04);
388 }
389
390 return 0;
391}
392
393static void wcd9xxx_enable_clock_block(struct wcd9xxx_resmgr *resmgr,
394 int config_mode)
395{
396 struct snd_soc_codec *codec = resmgr->codec;
397
398 pr_debug("%s: config_mode = %d\n", __func__, config_mode);
399 /* transit to RCO requires mclk off */
400 WARN_ON(snd_soc_read(codec, WCD9XXX_A_CLK_BUFF_EN2) & (1 << 2));
401 if (config_mode) {
402 /* Notify */
403 wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_RCO_ON);
404 /* enable RCO and switch to it */
405 wcd9xxx_resmgr_enable_config_mode(codec, 1);
406 snd_soc_write(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02);
407 usleep_range(1000, 1000);
408 } else {
409 /* Notify */
410 wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_MCLK_ON);
411 /* switch to MCLK */
412 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x08, 0x00);
413 /* if RCO is enabled, switch from it */
414 if (snd_soc_read(codec, WCD9XXX_A_RC_OSC_FREQ) & 0x80) {
415 snd_soc_write(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02);
416 wcd9xxx_resmgr_enable_config_mode(codec, 0);
417 }
418 }
419
420 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x01, 0x01);
421 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02, 0x00);
422
423 /* on MCLK */
424 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x04, 0x04);
425 snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL, 0x01, 0x01);
426 usleep_range(50, 50);
427
428 /* Notify */
429 if (config_mode)
430 wcd9xxx_resmgr_notifier_call(resmgr,
431 WCD9XXX_EVENT_POST_RCO_ON);
432 else
433 wcd9xxx_resmgr_notifier_call(resmgr,
434 WCD9XXX_EVENT_POST_MCLK_ON);
435}
436
437/*
438 * disable clock and return previous clock state
439 */
440static enum wcd9xxx_clock_type wcd9xxx_save_clock(struct wcd9xxx_resmgr *resmgr)
441{
442 WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
443 if (resmgr->clk_type != WCD9XXX_CLK_OFF)
444 wcd9xxx_disable_clock_block(resmgr);
445 return resmgr->clk_type != WCD9XXX_CLK_OFF;
446}
447
448static void wcd9xxx_restore_clock(struct wcd9xxx_resmgr *resmgr,
449 enum wcd9xxx_clock_type type)
450{
451 if (type != WCD9XXX_CLK_OFF)
452 wcd9xxx_enable_clock_block(resmgr, type == WCD9XXX_CLK_RCO);
453}
454
455void wcd9xxx_resmgr_get_clk_block(struct wcd9xxx_resmgr *resmgr,
456 enum wcd9xxx_clock_type type)
457{
458 pr_debug("%s: current %d, requested %d, rco_users %d, mclk_users %d\n",
459 __func__, resmgr->clk_type, type,
460 resmgr->clk_rco_users, resmgr->clk_mclk_users);
461 WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
462 switch (type) {
463 case WCD9XXX_CLK_RCO:
464 if (++resmgr->clk_rco_users == 1 &&
465 resmgr->clk_type == WCD9XXX_CLK_OFF) {
466 /* enable RCO and switch to it */
467 wcd9xxx_enable_clock_block(resmgr, 1);
468 resmgr->clk_type = WCD9XXX_CLK_RCO;
469 }
470 break;
471 case WCD9XXX_CLK_MCLK:
472 if (++resmgr->clk_mclk_users == 1 &&
473 resmgr->clk_type == WCD9XXX_CLK_OFF) {
474 /* switch to MCLK */
475 wcd9xxx_enable_clock_block(resmgr, 0);
476 resmgr->clk_type = WCD9XXX_CLK_MCLK;
477 } else if (resmgr->clk_mclk_users == 1 &&
478 resmgr->clk_type == WCD9XXX_CLK_RCO) {
479 /* if RCO is enabled, switch from it */
480 WARN_ON(!(snd_soc_read(resmgr->codec,
481 WCD9XXX_A_RC_OSC_FREQ) & 0x80));
482 /* disable clock block */
483 wcd9xxx_disable_clock_block(resmgr);
484 /* switch to RCO */
485 wcd9xxx_enable_clock_block(resmgr, 0);
486 resmgr->clk_type = WCD9XXX_CLK_MCLK;
487 }
488 break;
489 default:
490 pr_err("%s: Error, Invalid clock get request %d\n", __func__,
491 type);
492 break;
493 }
494 pr_debug("%s: leave\n", __func__);
495}
496
497void wcd9xxx_resmgr_put_clk_block(struct wcd9xxx_resmgr *resmgr,
498 enum wcd9xxx_clock_type type)
499{
500 pr_debug("%s: current %d, put %d\n", __func__, resmgr->clk_type, type);
501
502 WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
503 switch (type) {
504 case WCD9XXX_CLK_RCO:
505 if (--resmgr->clk_rco_users == 0 &&
506 resmgr->clk_type == WCD9XXX_CLK_RCO) {
507 wcd9xxx_disable_clock_block(resmgr);
508 resmgr->clk_type = WCD9XXX_CLK_OFF;
509 }
510 break;
511 case WCD9XXX_CLK_MCLK:
512 if (--resmgr->clk_mclk_users == 0 &&
513 resmgr->clk_rco_users == 0) {
514 wcd9xxx_disable_clock_block(resmgr);
515 resmgr->clk_type = WCD9XXX_CLK_OFF;
516 } else if (resmgr->clk_mclk_users == 0 &&
517 resmgr->clk_rco_users) {
518 /* disable clock */
519 wcd9xxx_disable_clock_block(resmgr);
520 /* switch to RCO */
521 wcd9xxx_enable_clock_block(resmgr, 1);
522 resmgr->clk_type = WCD9XXX_CLK_RCO;
523 }
524 break;
525 default:
526 pr_err("%s: Error, Invalid clock get request %d\n", __func__,
527 type);
528 break;
529 }
530 WARN_ON(resmgr->clk_rco_users < 0);
531 WARN_ON(resmgr->clk_mclk_users < 0);
532
533 pr_debug("%s: new rco_users %d, mclk_users %d\n", __func__,
534 resmgr->clk_rco_users, resmgr->clk_mclk_users);
535}
536
537static void wcd9xxx_resmgr_update_cfilt_usage(struct wcd9xxx_resmgr *resmgr,
538 enum wcd9xxx_cfilt_sel cfilt_sel,
539 bool inc)
540{
541 u16 micb_cfilt_reg;
542 enum wcd9xxx_notify_event e_pre_on, e_post_off;
543 struct snd_soc_codec *codec = resmgr->codec;
544
545 switch (cfilt_sel) {
546 case WCD9XXX_CFILT1_SEL:
547 micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_1_CTL;
548 e_pre_on = WCD9XXX_EVENT_PRE_CFILT_1_ON;
549 e_post_off = WCD9XXX_EVENT_POST_CFILT_1_OFF;
550 break;
551 case WCD9XXX_CFILT2_SEL:
552 micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_2_CTL;
553 e_pre_on = WCD9XXX_EVENT_PRE_CFILT_2_ON;
554 e_post_off = WCD9XXX_EVENT_POST_CFILT_2_OFF;
555 break;
556 case WCD9XXX_CFILT3_SEL:
557 micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_3_CTL;
558 e_pre_on = WCD9XXX_EVENT_PRE_CFILT_3_ON;
559 e_post_off = WCD9XXX_EVENT_POST_CFILT_3_OFF;
560 break;
561 default:
562 WARN(1, "Invalid CFILT selection %d\n", cfilt_sel);
563 return; /* should not happen */
564 }
565
566 if (inc) {
567 if ((resmgr->cfilt_users[cfilt_sel]++) == 0) {
568 /* Notify */
569 wcd9xxx_resmgr_notifier_call(resmgr, e_pre_on);
570 /* Enable CFILT */
571 snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0x80);
572 }
573 } else {
574 /*
575 * Check if count not zero, decrease
576 * then check if zero, go ahead disable cfilter
577 */
578 WARN(resmgr->cfilt_users[cfilt_sel] == 0,
579 "Invalid CFILT use count 0\n");
580 if ((--resmgr->cfilt_users[cfilt_sel]) == 0) {
581 /* Disable CFILT */
582 snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0);
583 /* Notify MBHC so MBHC can switch CFILT to fast mode */
584 wcd9xxx_resmgr_notifier_call(resmgr, e_post_off);
585 }
586 }
587}
588
589void wcd9xxx_resmgr_cfilt_get(struct wcd9xxx_resmgr *resmgr,
590 enum wcd9xxx_cfilt_sel cfilt_sel)
591{
592 return wcd9xxx_resmgr_update_cfilt_usage(resmgr, cfilt_sel, true);
593}
594
595void wcd9xxx_resmgr_cfilt_put(struct wcd9xxx_resmgr *resmgr,
596 enum wcd9xxx_cfilt_sel cfilt_sel)
597{
598 return wcd9xxx_resmgr_update_cfilt_usage(resmgr, cfilt_sel, false);
599}
600
601int wcd9xxx_resmgr_get_k_val(struct wcd9xxx_resmgr *resmgr,
602 unsigned int cfilt_mv)
603{
604 int rc = -EINVAL;
605 unsigned int ldoh_v = resmgr->pdata->micbias.ldoh_v;
606 unsigned min_mv, max_mv;
607
608 switch (ldoh_v) {
609 case WCD9XXX_LDOH_1P95_V:
610 min_mv = 160;
611 max_mv = 1800;
612 break;
613 case WCD9XXX_LDOH_2P35_V:
614 min_mv = 200;
615 max_mv = 2200;
616 break;
617 case WCD9XXX_LDOH_2P75_V:
618 min_mv = 240;
619 max_mv = 2600;
620 break;
621 case WCD9XXX_LDOH_3P0_V:
622 min_mv = 260;
623 max_mv = 2875;
624 break;
625 default:
626 goto done;
627 }
628
629 if (cfilt_mv < min_mv || cfilt_mv > max_mv)
630 goto done;
631
632 for (rc = 4; rc <= 44; rc++) {
633 min_mv = max_mv * (rc) / 44;
634 if (min_mv >= cfilt_mv) {
635 rc -= 4;
636 break;
637 }
638 }
639done:
640 return rc;
641}
642
643int wcd9xxx_resmgr_register_notifier(struct wcd9xxx_resmgr *resmgr,
644 struct notifier_block *nblock)
645{
646 return blocking_notifier_chain_register(&resmgr->notifier, nblock);
647}
648
649int wcd9xxx_resmgr_unregister_notifier(struct wcd9xxx_resmgr *resmgr,
650 struct notifier_block *nblock)
651{
652 return blocking_notifier_chain_unregister(&resmgr->notifier, nblock);
653}
654
655int wcd9xxx_resmgr_init(struct wcd9xxx_resmgr *resmgr,
656 struct snd_soc_codec *codec,
657 struct wcd9xxx *wcd9xxx,
658 struct wcd9xxx_pdata *pdata,
659 struct wcd9xxx_reg_address *reg_addr)
660{
661 WARN(ARRAY_SIZE(wcd9xxx_event_string) != WCD9XXX_EVENT_LAST + 1,
662 "Event string table isn't up to date!, %d != %d\n",
663 ARRAY_SIZE(wcd9xxx_event_string), WCD9XXX_EVENT_LAST + 1);
664
665 resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
666 resmgr->codec = codec;
667 /* This gives access of core handle to lock/unlock suspend */
668 resmgr->core = wcd9xxx;
669 resmgr->pdata = pdata;
670 resmgr->reg_addr = reg_addr;
671
672 BLOCKING_INIT_NOTIFIER_HEAD(&resmgr->notifier);
673
674 mutex_init(&resmgr->codec_resource_lock);
675
676 return 0;
677}
678
679void wcd9xxx_resmgr_deinit(struct wcd9xxx_resmgr *resmgr)
680{
681 mutex_destroy(&resmgr->codec_resource_lock);
682}
683
684void wcd9xxx_resmgr_bcl_lock(struct wcd9xxx_resmgr *resmgr)
685{
686 mutex_lock(&resmgr->codec_resource_lock);
687}
688
689void wcd9xxx_resmgr_bcl_unlock(struct wcd9xxx_resmgr *resmgr)
690{
691 mutex_unlock(&resmgr->codec_resource_lock);
692}
693
694MODULE_DESCRIPTION("wcd9xxx resmgr module");
695MODULE_LICENSE("GPL v2");