Skip to content

Commit 91597a8

Browse files
acgzxrkhuangtao
authored andcommitted
ASoC: rk3308_codec: add support low power mode in runtime
We need to close ADC or DAC mclk if they are not working, it may reduce a bit of power consumption for runtime. And, power off and close pclk_acodec during suspend. But the acodec may be needed by VAD during suspend, we can use the property 'rockchip,no-deep-low-power' to keep codec during suspend. The following is a comparison of power consumption before and after the modification on RK3308 EVB v10: [default] VCC_1V8_CODEC(V*mA=mW) VCC_3V3_CODEC(V*mA=mW) idle 1.794*0.9=1.614 3.354*2.8=9.3912 // the ADC and DAC mclk are opened playback 2ch 1.793*7.9=14.1647 3.354*2.8=9.3912 // the ADC and DAC mclk are opened capture 8ch 1.789*27.8=49.7342 3.324*2.8=9.3072 // the ADC and DAC mclk are opened suspend 1.8*0.7=1.26 3.356*2.8=9.3968 // the ADC and DAC mclk are opened [modified] VCC_1V8_CODEC(V*mA=mW) VCC_3V3_CODEC(V*mA=mW) idle 1.794*0.8=1.4352 3.354*2.8=9.3912 // just close ADC and DAC + close ADC / DAC mclk playback 2ch 1.793*7.8=13.9854 3.354*2.8=9.3912 // open DAC mclk, close ADC mclk capture 8ch 1.789*27.7=49.5553 3.323*2.8=9.3044 // open ADC mclk, close DAC mclk suspend 1.799*1=1.799 3.355*0.3=1.0065 // close 2 micbiases, close mclk tx/rx and pclk_acodec It seems that the light low power (close ADC and DAC mclk) can't bring us much help to reduce power (only ~1mA@VCC_1V8_CODEC), but the deep lower power can help us to reduce ~8mW@VCC_3V3_CODEC during suspend. However, it's that the power is increased ~0.5mW@VCC_1V8_CODEC in deep low power, it needs to EE's to help us to check this. Change-Id: I7982b4e20bcb894e1895f772816b2b188bcc314d Signed-off-by: Xing Zheng <zhengxing@rock-chips.com>
1 parent a4125a4 commit 91597a8

2 files changed

Lines changed: 228 additions & 11 deletions

File tree

sound/soc/codecs/rk3308_codec.c

Lines changed: 222 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,16 @@ enum {
105105
PATH_BUSY,
106106
};
107107

108+
enum {
109+
PM_NORMAL = 0,
110+
PM_LLP_DOWN, /* light low power down */
111+
PM_LLP_UP,
112+
PM_DLP_DOWN, /* deep low power down */
113+
PM_DLP_UP,
114+
PM_DLP_DOWN2,
115+
PM_DLP_UP2,
116+
};
117+
108118
struct rk3308_codec_priv {
109119
const struct device *plat_dev;
110120
struct device dev;
@@ -140,12 +150,15 @@ struct rk3308_codec_priv {
140150
int adc_path_state;
141151
int dac_path_state;
142152

153+
int pm_state;
154+
143155
/* Only hpout do fade-in and fade-out */
144156
unsigned int hpout_l_dgain;
145157
unsigned int hpout_r_dgain;
146158

147159
bool enable_all_adcs;
148160
bool hp_plugged;
161+
bool no_deep_low_power;
149162
struct delayed_work hpdet_work;
150163
struct delayed_work loopback_work;
151164

@@ -1555,9 +1568,8 @@ static int rk3308_codec_dac_disable(struct rk3308_codec_priv *rk3308)
15551568
return 0;
15561569
}
15571570

1558-
static int rk3308_codec_power_on(struct snd_soc_codec *codec)
1571+
static int rk3308_codec_power_on(struct rk3308_codec_priv *rk3308)
15591572
{
1560-
struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec);
15611573
unsigned int v;
15621574

15631575
/* 1. Supply the power of digital part and reset the Audio Codec */
@@ -1623,9 +1635,8 @@ static int rk3308_codec_power_on(struct snd_soc_codec *codec)
16231635
return 0;
16241636
}
16251637

1626-
static int rk3308_codec_power_off(struct snd_soc_codec *codec)
1638+
static int rk3308_codec_power_off(struct rk3308_codec_priv *rk3308)
16271639
{
1628-
struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec);
16291640
unsigned int v;
16301641

16311642
/*
@@ -2434,6 +2445,36 @@ static int rk3308_codec_open_capture(struct rk3308_codec_priv *rk3308)
24342445
return 0;
24352446
}
24362447

2448+
static void rk3308_codec_adc_mclk_disable(struct rk3308_codec_priv *rk3308)
2449+
{
2450+
regmap_update_bits(rk3308->regmap, RK3308_GLB_CON,
2451+
RK3308_ADC_MCLK_MSK,
2452+
RK3308_ADC_MCLK_DIS);
2453+
}
2454+
2455+
static void rk3308_codec_adc_mclk_enable(struct rk3308_codec_priv *rk3308)
2456+
{
2457+
regmap_update_bits(rk3308->regmap, RK3308_GLB_CON,
2458+
RK3308_ADC_MCLK_MSK,
2459+
RK3308_ADC_MCLK_EN);
2460+
udelay(20);
2461+
}
2462+
2463+
static void rk3308_codec_dac_mclk_disable(struct rk3308_codec_priv *rk3308)
2464+
{
2465+
regmap_update_bits(rk3308->regmap, RK3308_GLB_CON,
2466+
RK3308_DAC_MCLK_MSK,
2467+
RK3308_DAC_MCLK_DIS);
2468+
}
2469+
2470+
static void rk3308_codec_dac_mclk_enable(struct rk3308_codec_priv *rk3308)
2471+
{
2472+
regmap_update_bits(rk3308->regmap, RK3308_GLB_CON,
2473+
RK3308_DAC_MCLK_MSK,
2474+
RK3308_DAC_MCLK_EN);
2475+
udelay(20);
2476+
}
2477+
24372478
static int rk3308_codec_close_capture(struct rk3308_codec_priv *rk3308)
24382479
{
24392480
rk3308_codec_alc_disable(rk3308, ADC_TYPE_NORMAL);
@@ -2458,6 +2499,95 @@ static int rk3308_codec_close_playback(struct rk3308_codec_priv *rk3308)
24582499
return 0;
24592500
}
24602501

2502+
static int rk3308_codec_llp_down(struct rk3308_codec_priv *rk3308)
2503+
{
2504+
rk3308_codec_adc_mclk_disable(rk3308);
2505+
rk3308_codec_dac_mclk_disable(rk3308);
2506+
2507+
return 0;
2508+
}
2509+
2510+
static int rk3308_codec_llp_up(struct rk3308_codec_priv *rk3308)
2511+
{
2512+
rk3308_codec_adc_mclk_enable(rk3308);
2513+
rk3308_codec_dac_mclk_enable(rk3308);
2514+
2515+
return 0;
2516+
}
2517+
2518+
static int rk3308_codec_dlp_down(struct rk3308_codec_priv *rk3308)
2519+
{
2520+
rk3308_codec_micbias_disable(rk3308);
2521+
rk3308_codec_power_off(rk3308);
2522+
2523+
return 0;
2524+
}
2525+
2526+
static int rk3308_codec_dlp_up(struct rk3308_codec_priv *rk3308)
2527+
{
2528+
rk3308_codec_power_on(rk3308);
2529+
rk3308_codec_micbias_enable(rk3308, RK3308_ADC_MICBIAS_VOLT_0_85);
2530+
2531+
return 0;
2532+
}
2533+
2534+
/* Just used for debug and trace power state */
2535+
static void rk3308_codec_set_pm_state(struct rk3308_codec_priv *rk3308,
2536+
int pm_state)
2537+
{
2538+
int ret;
2539+
2540+
switch (pm_state) {
2541+
case PM_LLP_DOWN:
2542+
rk3308_codec_llp_down(rk3308);
2543+
break;
2544+
case PM_LLP_UP:
2545+
rk3308_codec_llp_up(rk3308);
2546+
break;
2547+
case PM_DLP_DOWN:
2548+
rk3308_codec_dlp_down(rk3308);
2549+
break;
2550+
case PM_DLP_UP:
2551+
rk3308_codec_dlp_up(rk3308);
2552+
break;
2553+
case PM_DLP_DOWN2:
2554+
clk_disable_unprepare(rk3308->mclk_rx);
2555+
clk_disable_unprepare(rk3308->mclk_tx);
2556+
clk_disable_unprepare(rk3308->pclk);
2557+
break;
2558+
case PM_DLP_UP2:
2559+
ret = clk_prepare_enable(rk3308->pclk);
2560+
if (ret < 0) {
2561+
dev_err(rk3308->plat_dev,
2562+
"Failed to enable acodec pclk: %d\n", ret);
2563+
goto err;
2564+
}
2565+
2566+
ret = clk_prepare_enable(rk3308->mclk_rx);
2567+
if (ret < 0) {
2568+
dev_err(rk3308->plat_dev,
2569+
"Failed to enable i2s mclk_rx: %d\n", ret);
2570+
goto err;
2571+
}
2572+
2573+
ret = clk_prepare_enable(rk3308->mclk_tx);
2574+
if (ret < 0) {
2575+
dev_err(rk3308->plat_dev,
2576+
"Failed to enable i2s mclk_tx: %d\n", ret);
2577+
goto err;
2578+
}
2579+
break;
2580+
default:
2581+
dev_err(rk3308->plat_dev, "Invalid pm_state: %d\n", pm_state);
2582+
goto err;
2583+
}
2584+
2585+
rk3308->pm_state = pm_state;
2586+
2587+
err:
2588+
return;
2589+
}
2590+
24612591
static int rk3308_hw_params(struct snd_pcm_substream *substream,
24622592
struct snd_pcm_hw_params *params,
24632593
struct snd_soc_dai *dai)
@@ -2468,9 +2598,11 @@ static int rk3308_hw_params(struct snd_pcm_substream *substream,
24682598

24692599
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
24702600
/* DAC only supports 2 channels */
2601+
rk3308_codec_dac_mclk_enable(rk3308);
24712602
rk3308_codec_open_playback(rk3308);
24722603
rk3308_codec_dac_dig_config(rk3308, params);
24732604
} else {
2605+
rk3308_codec_adc_mclk_enable(rk3308);
24742606
ret = rk3308_codec_update_adc_grps(rk3308, params);
24752607
if (ret < 0)
24762608
return ret;
@@ -2510,10 +2642,13 @@ static void rk3308_pcm_shutdown(struct snd_pcm_substream *substream,
25102642
struct snd_soc_codec *codec = dai->codec;
25112643
struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec);
25122644

2513-
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
2645+
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
25142646
rk3308_codec_close_playback(rk3308);
2515-
else
2647+
rk3308_codec_dac_mclk_disable(rk3308);
2648+
} else {
25162649
rk3308_codec_close_capture(rk3308);
2650+
rk3308_codec_adc_mclk_disable(rk3308);
2651+
}
25172652

25182653
regcache_cache_only(rk3308->regmap, false);
25192654
regcache_sync(rk3308->regmap);
@@ -2557,16 +2692,54 @@ static struct snd_soc_dai_driver rk3308_dai[] = {
25572692

25582693
static int rk3308_suspend(struct snd_soc_codec *codec)
25592694
{
2560-
rk3308_set_bias_level(codec, SND_SOC_BIAS_OFF);
2695+
struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec);
25612696

2697+
if (rk3308->no_deep_low_power)
2698+
goto out;
2699+
2700+
rk3308_codec_dlp_down(rk3308);
2701+
clk_disable_unprepare(rk3308->mclk_rx);
2702+
clk_disable_unprepare(rk3308->mclk_tx);
2703+
clk_disable_unprepare(rk3308->pclk);
2704+
2705+
out:
2706+
rk3308_set_bias_level(codec, SND_SOC_BIAS_OFF);
25622707
return 0;
25632708
}
25642709

25652710
static int rk3308_resume(struct snd_soc_codec *codec)
25662711
{
2567-
rk3308_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
2712+
struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec);
2713+
int ret = 0;
25682714

2569-
return 0;
2715+
if (rk3308->no_deep_low_power)
2716+
goto out;
2717+
2718+
ret = clk_prepare_enable(rk3308->pclk);
2719+
if (ret < 0) {
2720+
dev_err(rk3308->plat_dev,
2721+
"Failed to enable acodec pclk: %d\n", ret);
2722+
goto out;
2723+
}
2724+
2725+
ret = clk_prepare_enable(rk3308->mclk_rx);
2726+
if (ret < 0) {
2727+
dev_err(rk3308->plat_dev,
2728+
"Failed to enable i2s mclk_rx: %d\n", ret);
2729+
goto out;
2730+
}
2731+
2732+
ret = clk_prepare_enable(rk3308->mclk_tx);
2733+
if (ret < 0) {
2734+
dev_err(rk3308->plat_dev,
2735+
"Failed to enable i2s mclk_tx: %d\n", ret);
2736+
goto out;
2737+
}
2738+
2739+
rk3308_codec_dlp_up(rk3308);
2740+
out:
2741+
rk3308_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
2742+
return ret;
25702743
}
25712744

25722745
static int rk3308_codec_default_gains(struct rk3308_codec_priv *rk3308)
@@ -2628,6 +2801,7 @@ static int rk3308_codec_prepare(struct rk3308_codec_priv *rk3308)
26282801
rk3308_codec_close_playback(rk3308);
26292802
rk3308_codec_close_capture(rk3308);
26302803
rk3308_codec_default_gains(rk3308);
2804+
rk3308_codec_llp_down(rk3308);
26312805

26322806
return 0;
26332807
}
@@ -2640,7 +2814,7 @@ static int rk3308_probe(struct snd_soc_codec *codec)
26402814
rk3308_codec_set_dac_path_state(rk3308, PATH_IDLE);
26412815

26422816
rk3308_codec_reset(codec);
2643-
rk3308_codec_power_on(codec);
2817+
rk3308_codec_power_on(rk3308);
26442818

26452819
/* From vendor recommend */
26462820
rk3308_codec_micbias_enable(rk3308, RK3308_ADC_MICBIAS_VOLT_0_85);
@@ -2661,8 +2835,8 @@ static int rk3308_remove(struct snd_soc_codec *codec)
26612835
rk3308_headphone_ctl(rk3308, 0);
26622836
rk3308_speaker_ctl(rk3308, 0);
26632837
rk3308_codec_headset_detect_disable(rk3308);
2664-
rk3308_codec_power_off(codec);
26652838
rk3308_codec_micbias_disable(rk3308);
2839+
rk3308_codec_power_off(rk3308);
26662840

26672841
rk3308_codec_set_dac_path_state(rk3308, PATH_IDLE);
26682842

@@ -2792,6 +2966,38 @@ static const struct regmap_config rk3308_codec_regmap_config = {
27922966
.cache_type = REGCACHE_FLAT,
27932967
};
27942968

2969+
static ssize_t pm_state_show(struct device *dev,
2970+
struct device_attribute *attr,
2971+
char *buf)
2972+
{
2973+
struct rk3308_codec_priv *rk3308 =
2974+
container_of(dev, struct rk3308_codec_priv, dev);
2975+
2976+
return sprintf(buf, "pm_state: %d\n", rk3308->pm_state);
2977+
}
2978+
2979+
static ssize_t pm_state_store(struct device *dev,
2980+
struct device_attribute *attr,
2981+
const char *buf, size_t count)
2982+
{
2983+
struct rk3308_codec_priv *rk3308 =
2984+
container_of(dev, struct rk3308_codec_priv, dev);
2985+
unsigned long pm_state;
2986+
int ret = kstrtoul(buf, 10, &pm_state);
2987+
2988+
if (ret < 0) {
2989+
dev_err(dev, "Invalid pm_state: %ld, ret: %d\n",
2990+
pm_state, ret);
2991+
return -EINVAL;
2992+
}
2993+
2994+
rk3308_codec_set_pm_state(rk3308, pm_state);
2995+
2996+
dev_info(dev, "Store pm_state: %d\n", rk3308->pm_state);
2997+
2998+
return count;
2999+
}
3000+
27953001
static ssize_t adc_grps_show(struct device *dev,
27963002
struct device_attribute *attr,
27973003
char *buf)
@@ -3068,6 +3274,7 @@ static const struct device_attribute acodec_attrs[] = {
30683274
__ATTR_RW(adc_zerocross),
30693275
__ATTR_RW(dac_output),
30703276
__ATTR_RW(enable_all_adcs),
3277+
__ATTR_RW(pm_state),
30713278
};
30723279

30733280
static void rk3308_codec_device_release(struct device *dev)
@@ -3297,6 +3504,9 @@ static int rk3308_platform_probe(struct platform_device *pdev)
32973504
rk3308->enable_all_adcs =
32983505
of_property_read_bool(np, "rockchip,enable-all-adcs");
32993506

3507+
rk3308->no_deep_low_power =
3508+
of_property_read_bool(np, "rockchip,no-deep-low-power");
3509+
33003510
rk3308->loopback_grp = LOOPBACK_NO_USE;
33013511
ret = of_property_read_u32(np, "rockchip,loopback-grp",
33023512
&rk3308->loopback_grp);
@@ -3355,6 +3565,7 @@ static int rk3308_platform_probe(struct platform_device *pdev)
33553565
rk3308->adc_grp0_using_linein = ADC_GRP0_MICIN;
33563566
rk3308->dac_output = DAC_LINEOUT;
33573567
rk3308->adc_zerocross = 1;
3568+
rk3308->pm_state = PM_NORMAL;
33583569

33593570
platform_set_drvdata(pdev, rk3308);
33603571

sound/soc/codecs/rk3308_codec.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,12 @@
210210
#define RK3308_ADC_BIST_RESET (0 << 7)
211211
#define RK3308_DAC_BIST_WORK (1 << 6)
212212
#define RK3308_DAC_BIST_RESET (0 << 6)
213+
#define RK3308_ADC_MCLK_MSK (1 << 5)
214+
#define RK3308_ADC_MCLK_DIS (1 << 5)
215+
#define RK3308_ADC_MCLK_EN (0 << 5)
216+
#define RK3308_DAC_MCLK_MSK (1 << 4)
217+
#define RK3308_DAC_MCLK_DIS (1 << 4)
218+
#define RK3308_DAC_MCLK_EN (0 << 4)
213219
#define RK3308_CODEC_RST_MSK (0x7 << 0)
214220
#define RK3308_ADC_DIG_WORK (1 << 2)
215221
#define RK3308_ADC_DIG_RESET (0 << 2)

0 commit comments

Comments
 (0)