Skip to content

Commit 2ec30f6

Browse files
John-Hsubroonie
authored andcommitted
ASoC: nau8825: non-clock jack detection for power saving at standby
The driver changes jack type detection interruption to non-clock archi- tecture for less 1mW power saving. The architecture is called manual mode jack detection. It has no hardware debounce, no jack type detection, but only detecting jack insertion. After jack insertion, the driver will switch to auto mode jack detection with internal clock which can detect microphone, jack type and do hardware debounce. The manual architecture has these main changes including codec initiation, interruption, clock control, and power management. When codec initiation or system resume, the clock is closed as jack insertion detection at man- ual mode, and bypass debounce circuit. These configurations move to resume setup function when setup bias level after resume. When jack insertion detection happens, the manual mode turns off and make configuration about jack type detection interruption at auto mode in auto irq setup function which can detect microphone and jack type. The inter- ruption will switch to manual mode again with clock free until jack ejec- tion happens. The system clock configuration adds clock disable option which can disable internal VCO clock. Before the system clock change, there is an restric- tion added to make sure clock disabled and not config any clock when no headset connected. In power management, we involve the solution about races and jack detec- tion in resume from Ben Zhang in the following patch and list his comment. [PATCH] ASoC: nau8825: Fix jack detection across suspend "Jack plug status is rechecked at resume to handle plug/unplug in S3 when the chip has no power." "Suspend/resume callbacks are moved from the i2c dev_pm_ops to snd_soc_codec_driver. soc_resume_deferred is a delayed work which may trigger nau8825_set_bias_level. The bias change races against dev_pm_ops, causing jack detection issues. soc_resume_deferred ensures bias change and snd_soc_codec_driver suspend/resume are sequenced correctly." Change SAR widget to supply type which can prevent the codec keeping at SND_SOC_BIAS_ON during suspend. The codec suspend function can just invoke normally. Before the system suspends, the driver turns off all interruptions. Keep the interruption quiet before resume setup completes. The ADC channel will be disabled which is needed for interruptions at audo mode. Signed-off-by: John Hsu <[email protected]> Signed-off-by: Mark Brown <[email protected]>
1 parent 18d8306 commit 2ec30f6

File tree

2 files changed

+188
-70
lines changed

2 files changed

+188
-70
lines changed

sound/soc/codecs/nau8825.c

Lines changed: 180 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,16 @@
3030

3131
#include "nau8825.h"
3232

33+
34+
#define NUVOTON_CODEC_DAI "nau8825-hifi"
35+
3336
#define NAU_FREF_MAX 13500000
3437
#define NAU_FVCO_MAX 124000000
3538
#define NAU_FVCO_MIN 90000000
3639

40+
static int nau8825_configure_sysclk(struct nau8825 *nau8825,
41+
int clk_id, unsigned int freq);
42+
3743
struct nau8825_fll {
3844
int mclk_src;
3945
int ratio;
@@ -670,9 +676,6 @@ int nau8825_enable_jack_detect(struct snd_soc_codec *codec,
670676
NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L,
671677
NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L);
672678

673-
regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
674-
NAU8825_IRQ_HEADSET_COMPLETE_EN | NAU8825_IRQ_EJECT_EN, 0);
675-
676679
return 0;
677680
}
678681
EXPORT_SYMBOL_GPL(nau8825_enable_jack_detect);
@@ -688,16 +691,6 @@ static bool nau8825_is_jack_inserted(struct regmap *regmap)
688691

689692
static void nau8825_restart_jack_detection(struct regmap *regmap)
690693
{
691-
/* Chip needs one FSCLK cycle in order to generate interrupts,
692-
* as we cannot guarantee one will be provided by the system. Turning
693-
* master mode on then off enables us to generate that FSCLK cycle
694-
* with a minimum of contention on the clock bus.
695-
*/
696-
regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2,
697-
NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_MASTER);
698-
regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2,
699-
NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_SLAVE);
700-
701694
/* this will restart the entire jack detection process including MIC/GND
702695
* switching and create interrupts. We have to go from 0 to 1 and back
703696
* to 0 to restart.
@@ -708,6 +701,22 @@ static void nau8825_restart_jack_detection(struct regmap *regmap)
708701
NAU8825_JACK_DET_RESTART, 0);
709702
}
710703

704+
static void nau8825_int_status_clear_all(struct regmap *regmap)
705+
{
706+
int active_irq, clear_irq, i;
707+
708+
/* Reset the intrruption status from rightmost bit if the corres-
709+
* ponding irq event occurs.
710+
*/
711+
regmap_read(regmap, NAU8825_REG_IRQ_STATUS, &active_irq);
712+
for (i = 0; i < NAU8825_REG_DATA_LEN; i++) {
713+
clear_irq = (0x1 << i);
714+
if (active_irq & clear_irq)
715+
regmap_write(regmap,
716+
NAU8825_REG_INT_CLR_KEY_STATUS, clear_irq);
717+
}
718+
}
719+
711720
static void nau8825_eject_jack(struct nau8825 *nau8825)
712721
{
713722
struct snd_soc_dapm_context *dapm = nau8825->dapm;
@@ -722,6 +731,69 @@ static void nau8825_eject_jack(struct nau8825 *nau8825)
722731
regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 0xf, 0xf);
723732

724733
snd_soc_dapm_sync(dapm);
734+
735+
/* Clear all interruption status */
736+
nau8825_int_status_clear_all(regmap);
737+
738+
/* Enable the insertion interruption, disable the ejection inter-
739+
* ruption, and then bypass de-bounce circuit.
740+
*/
741+
regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_DIS_CTRL,
742+
NAU8825_IRQ_EJECT_DIS | NAU8825_IRQ_INSERT_DIS,
743+
NAU8825_IRQ_EJECT_DIS);
744+
regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
745+
NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_EJECT_EN |
746+
NAU8825_IRQ_HEADSET_COMPLETE_EN | NAU8825_IRQ_INSERT_EN,
747+
NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_EJECT_EN |
748+
NAU8825_IRQ_HEADSET_COMPLETE_EN);
749+
regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
750+
NAU8825_JACK_DET_DB_BYPASS, NAU8825_JACK_DET_DB_BYPASS);
751+
752+
/* Disable ADC needed for interruptions at audo mode */
753+
regmap_update_bits(regmap, NAU8825_REG_ENA_CTRL,
754+
NAU8825_ENABLE_ADC, 0);
755+
756+
/* Close clock for jack type detection at manual mode */
757+
nau8825_configure_sysclk(nau8825, NAU8825_CLK_DIS, 0);
758+
}
759+
760+
/* Enable audo mode interruptions with internal clock. */
761+
static void nau8825_setup_auto_irq(struct nau8825 *nau8825)
762+
{
763+
struct regmap *regmap = nau8825->regmap;
764+
765+
/* Enable headset jack type detection complete interruption and
766+
* jack ejection interruption.
767+
*/
768+
regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
769+
NAU8825_IRQ_HEADSET_COMPLETE_EN | NAU8825_IRQ_EJECT_EN, 0);
770+
771+
/* Enable internal VCO needed for interruptions */
772+
nau8825_configure_sysclk(nau8825, NAU8825_CLK_INTERNAL, 0);
773+
774+
/* Enable ADC needed for interruptions */
775+
regmap_update_bits(regmap, NAU8825_REG_ENA_CTRL,
776+
NAU8825_ENABLE_ADC, NAU8825_ENABLE_ADC);
777+
778+
/* Chip needs one FSCLK cycle in order to generate interruptions,
779+
* as we cannot guarantee one will be provided by the system. Turning
780+
* master mode on then off enables us to generate that FSCLK cycle
781+
* with a minimum of contention on the clock bus.
782+
*/
783+
regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2,
784+
NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_MASTER);
785+
regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2,
786+
NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_SLAVE);
787+
788+
/* Not bypass de-bounce circuit */
789+
regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
790+
NAU8825_JACK_DET_DB_BYPASS, 0);
791+
792+
/* Unmask all interruptions */
793+
regmap_write(regmap, NAU8825_REG_INTERRUPT_DIS_CTRL, 0);
794+
795+
/* Restart the jack detection process at auto mode */
796+
nau8825_restart_jack_detection(regmap);
725797
}
726798

727799
static int nau8825_button_decode(int value)
@@ -858,6 +930,26 @@ static irqreturn_t nau8825_interrupt(int irq, void *data)
858930

859931
event_mask |= SND_JACK_HEADSET;
860932
clear_irq = NAU8825_HEADSET_COMPLETION_IRQ;
933+
} else if ((active_irq & NAU8825_JACK_INSERTION_IRQ_MASK) ==
934+
NAU8825_JACK_INSERTION_DETECTED) {
935+
/* One more step to check GPIO status directly. Thus, the
936+
* driver can confirm the real insertion interruption because
937+
* the intrruption at manual mode has bypassed debounce
938+
* circuit which can get rid of unstable status.
939+
*/
940+
if (nau8825_is_jack_inserted(regmap)) {
941+
/* Turn off insertion interruption at manual mode */
942+
regmap_update_bits(regmap,
943+
NAU8825_REG_INTERRUPT_DIS_CTRL,
944+
NAU8825_IRQ_INSERT_DIS,
945+
NAU8825_IRQ_INSERT_DIS);
946+
regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
947+
NAU8825_IRQ_INSERT_EN, NAU8825_IRQ_INSERT_EN);
948+
/* Enable interruption for jack type detection at audo
949+
* mode which can detect microphone and jack type.
950+
*/
951+
nau8825_setup_auto_irq(nau8825);
952+
}
861953
}
862954

863955
if (!clear_irq)
@@ -1007,8 +1099,8 @@ static void nau8825_init_regs(struct nau8825 *nau8825)
10071099
}
10081100

10091101
static const struct regmap_config nau8825_regmap_config = {
1010-
.val_bits = 16,
1011-
.reg_bits = 16,
1102+
.val_bits = NAU8825_REG_DATA_LEN,
1103+
.reg_bits = NAU8825_REG_ADDR_LEN,
10121104

10131105
.max_register = NAU8825_REG_MAX,
10141106
.readable_reg = nau8825_readable_reg,
@@ -1027,12 +1119,6 @@ static int nau8825_codec_probe(struct snd_soc_codec *codec)
10271119

10281120
nau8825->dapm = dapm;
10291121

1030-
/* Unmask interruptions. Handler uses dapm object so we can enable
1031-
* interruptions only after dapm is fully initialized.
1032-
*/
1033-
regmap_write(nau8825->regmap, NAU8825_REG_INTERRUPT_DIS_CTRL, 0);
1034-
nau8825_restart_jack_detection(nau8825->regmap);
1035-
10361122
return 0;
10371123
}
10381124

@@ -1197,17 +1283,32 @@ static int nau8825_mclk_prepare(struct nau8825 *nau8825, unsigned int freq)
11971283
return 0;
11981284
}
11991285

1286+
static void nau8825_configure_mclk_as_sysclk(struct regmap *regmap)
1287+
{
1288+
regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
1289+
NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_MCLK);
1290+
regmap_update_bits(regmap, NAU8825_REG_FLL6,
1291+
NAU8825_DCO_EN, 0);
1292+
}
1293+
12001294
static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id,
12011295
unsigned int freq)
12021296
{
12031297
struct regmap *regmap = nau8825->regmap;
12041298
int ret;
12051299

12061300
switch (clk_id) {
1301+
case NAU8825_CLK_DIS:
1302+
/* Clock provided externally and disable internal VCO clock */
1303+
nau8825_configure_mclk_as_sysclk(regmap);
1304+
if (nau8825->mclk_freq) {
1305+
clk_disable_unprepare(nau8825->mclk);
1306+
nau8825->mclk_freq = 0;
1307+
}
1308+
1309+
break;
12071310
case NAU8825_CLK_MCLK:
1208-
regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
1209-
NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_MCLK);
1210-
regmap_update_bits(regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN, 0);
1311+
nau8825_configure_mclk_as_sysclk(regmap);
12111312
/* MCLK not changed by clock tree */
12121313
regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
12131314
NAU8825_CLK_MCLK_SRC_MASK, 0);
@@ -1217,17 +1318,25 @@ static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id,
12171318

12181319
break;
12191320
case NAU8825_CLK_INTERNAL:
1220-
regmap_update_bits(regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN,
1221-
NAU8825_DCO_EN);
1222-
regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
1223-
NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO);
1224-
/* Decrease the VCO frequency for power saving */
1225-
regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
1226-
NAU8825_CLK_MCLK_SRC_MASK, 0xf);
1227-
regmap_update_bits(regmap, NAU8825_REG_FLL1,
1228-
NAU8825_FLL_RATIO_MASK, 0x10);
1229-
regmap_update_bits(regmap, NAU8825_REG_FLL6,
1230-
NAU8825_SDM_EN, NAU8825_SDM_EN);
1321+
if (nau8825_is_jack_inserted(nau8825->regmap)) {
1322+
regmap_update_bits(regmap, NAU8825_REG_FLL6,
1323+
NAU8825_DCO_EN, NAU8825_DCO_EN);
1324+
regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
1325+
NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO);
1326+
/* Decrease the VCO frequency for power saving */
1327+
regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
1328+
NAU8825_CLK_MCLK_SRC_MASK, 0xf);
1329+
regmap_update_bits(regmap, NAU8825_REG_FLL1,
1330+
NAU8825_FLL_RATIO_MASK, 0x10);
1331+
regmap_update_bits(regmap, NAU8825_REG_FLL6,
1332+
NAU8825_SDM_EN, NAU8825_SDM_EN);
1333+
} else {
1334+
/* The clock turns off intentionally for power saving
1335+
* when no headset connected.
1336+
*/
1337+
nau8825_configure_mclk_as_sysclk(regmap);
1338+
dev_warn(nau8825->dev, "Disable clock for power saving when no headset connected\n");
1339+
}
12311340
if (nau8825->mclk_freq) {
12321341
clk_disable_unprepare(nau8825->mclk);
12331342
nau8825->mclk_freq = 0;
@@ -1278,6 +1387,31 @@ static int nau8825_set_sysclk(struct snd_soc_codec *codec, int clk_id,
12781387
return nau8825_configure_sysclk(nau8825, clk_id, freq);
12791388
}
12801389

1390+
static int nau8825_resume_setup(struct nau8825 *nau8825)
1391+
{
1392+
struct regmap *regmap = nau8825->regmap;
1393+
1394+
/* Close clock when jack type detection at manual mode */
1395+
nau8825_configure_sysclk(nau8825, NAU8825_CLK_DIS, 0);
1396+
1397+
/* Clear all interruption status */
1398+
nau8825_int_status_clear_all(regmap);
1399+
1400+
/* Enable both insertion and ejection interruptions, and then
1401+
* bypass de-bounce circuit.
1402+
*/
1403+
regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
1404+
NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_HEADSET_COMPLETE_EN |
1405+
NAU8825_IRQ_EJECT_EN | NAU8825_IRQ_INSERT_EN,
1406+
NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_HEADSET_COMPLETE_EN);
1407+
regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
1408+
NAU8825_JACK_DET_DB_BYPASS, NAU8825_JACK_DET_DB_BYPASS);
1409+
regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_DIS_CTRL,
1410+
NAU8825_IRQ_INSERT_DIS | NAU8825_IRQ_EJECT_DIS, 0);
1411+
1412+
return 0;
1413+
}
1414+
12811415
static int nau8825_set_bias_level(struct snd_soc_codec *codec,
12821416
enum snd_soc_bias_level level)
12831417
{
@@ -1300,10 +1434,20 @@ static int nau8825_set_bias_level(struct snd_soc_codec *codec,
13001434
return ret;
13011435
}
13021436
}
1437+
/* Setup codec configuration after resume */
1438+
nau8825_resume_setup(nau8825);
13031439
}
13041440
break;
13051441

13061442
case SND_SOC_BIAS_OFF:
1443+
/* Turn off all interruptions before system shutdown. Keep the
1444+
* interruption quiet before resume setup completes.
1445+
*/
1446+
regmap_write(nau8825->regmap,
1447+
NAU8825_REG_INTERRUPT_DIS_CTRL, 0xffff);
1448+
/* Disable ADC needed for interruptions at audo mode */
1449+
regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL,
1450+
NAU8825_ENABLE_ADC, 0);
13071451
if (nau8825->mclk_freq)
13081452
clk_disable_unprepare(nau8825->mclk);
13091453
break;
@@ -1317,6 +1461,7 @@ static int nau8825_suspend(struct snd_soc_codec *codec)
13171461
struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
13181462

13191463
disable_irq(nau8825->irq);
1464+
snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
13201465
regcache_cache_only(nau8825->regmap, true);
13211466
regcache_mark_dirty(nau8825->regmap);
13221467

@@ -1327,32 +1472,10 @@ static int nau8825_resume(struct snd_soc_codec *codec)
13271472
{
13281473
struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
13291474

1330-
/* The chip may lose power and reset in S3. regcache_sync restores
1331-
* register values including configurations for sysclk, irq, and
1332-
* jack/button detection.
1333-
*/
13341475
regcache_cache_only(nau8825->regmap, false);
13351476
regcache_sync(nau8825->regmap);
1336-
1337-
/* Check the jack plug status directly. If the headset is unplugged
1338-
* during S3 when the chip has no power, there will be no jack
1339-
* detection irq even after the nau8825_restart_jack_detection below,
1340-
* because the chip just thinks no headset has ever been plugged in.
1341-
*/
1342-
if (!nau8825_is_jack_inserted(nau8825->regmap)) {
1343-
nau8825_eject_jack(nau8825);
1344-
snd_soc_jack_report(nau8825->jack, 0, SND_JACK_HEADSET);
1345-
}
1346-
13471477
enable_irq(nau8825->irq);
13481478

1349-
/* Run jack detection to check the type (OMTP or CTIA) of the headset
1350-
* if there is one. This handles the case where a different type of
1351-
* headset is plugged in during S3. This triggers an IRQ iff a headset
1352-
* is already plugged in.
1353-
*/
1354-
nau8825_restart_jack_detection(nau8825->regmap);
1355-
13561479
return 0;
13571480
}
13581481
#else
@@ -1461,20 +1584,8 @@ static int nau8825_read_device_properties(struct device *dev,
14611584

14621585
static int nau8825_setup_irq(struct nau8825 *nau8825)
14631586
{
1464-
struct regmap *regmap = nau8825->regmap;
14651587
int ret;
14661588

1467-
/* IRQ Output Enable */
1468-
regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
1469-
NAU8825_IRQ_OUTPUT_EN, NAU8825_IRQ_OUTPUT_EN);
1470-
1471-
/* Enable internal VCO needed for interruptions */
1472-
nau8825_configure_sysclk(nau8825, NAU8825_CLK_INTERNAL, 0);
1473-
1474-
/* Enable ADC needed for interrupts */
1475-
regmap_update_bits(regmap, NAU8825_REG_ENA_CTRL,
1476-
NAU8825_ENABLE_ADC, NAU8825_ENABLE_ADC);
1477-
14781589
ret = devm_request_threaded_irq(nau8825->dev, nau8825->irq, NULL,
14791590
nau8825_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
14801591
"nau8825", nau8825);

0 commit comments

Comments
 (0)