diff options
author | Ben Zhang <benzh@chromium.org> | 2016-03-25 19:10:39 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2016-03-28 05:40:34 -0400 |
commit | e6cee90075c0ff261ff7eef8ad892429f028e194 (patch) | |
tree | ad2472f6c3e5958c970905dcab859627c10f00a3 | |
parent | f55532a0c0b8bb6148f4e07853b876ef73bc69ca (diff) |
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.
Signed-off-by: Ben Zhang <benzh@chromium.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r-- | sound/soc/codecs/nau8825.c | 126 |
1 files changed, 71 insertions, 55 deletions
diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index 1c8729984c2b..683769f0f246 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c | |||
@@ -343,9 +343,12 @@ static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = { | |||
343 | SND_SOC_DAPM_SUPPLY("ADC Power", NAU8825_REG_ANALOG_ADC_2, 6, 0, NULL, | 343 | SND_SOC_DAPM_SUPPLY("ADC Power", NAU8825_REG_ANALOG_ADC_2, 6, 0, NULL, |
344 | 0), | 344 | 0), |
345 | 345 | ||
346 | /* ADC for button press detection */ | 346 | /* ADC for button press detection. A dapm supply widget is used to |
347 | SND_SOC_DAPM_ADC("SAR", NULL, NAU8825_REG_SAR_CTRL, | 347 | * prevent dapm_power_widgets keeping the codec at SND_SOC_BIAS_ON |
348 | NAU8825_SAR_ADC_EN_SFT, 0), | 348 | * during suspend. |
349 | */ | ||
350 | SND_SOC_DAPM_SUPPLY("SAR", NAU8825_REG_SAR_CTRL, | ||
351 | NAU8825_SAR_ADC_EN_SFT, 0, NULL, 0), | ||
349 | 352 | ||
350 | SND_SOC_DAPM_PGA_S("ADACL", 2, NAU8825_REG_RDAC, 12, 0, NULL, 0), | 353 | SND_SOC_DAPM_PGA_S("ADACL", 2, NAU8825_REG_RDAC, 12, 0, NULL, 0), |
351 | SND_SOC_DAPM_PGA_S("ADACR", 2, NAU8825_REG_RDAC, 13, 0, NULL, 0), | 354 | SND_SOC_DAPM_PGA_S("ADACR", 2, NAU8825_REG_RDAC, 13, 0, NULL, 0), |
@@ -607,6 +610,16 @@ static bool nau8825_is_jack_inserted(struct regmap *regmap) | |||
607 | 610 | ||
608 | static void nau8825_restart_jack_detection(struct regmap *regmap) | 611 | static void nau8825_restart_jack_detection(struct regmap *regmap) |
609 | { | 612 | { |
613 | /* Chip needs one FSCLK cycle in order to generate interrupts, | ||
614 | * as we cannot guarantee one will be provided by the system. Turning | ||
615 | * master mode on then off enables us to generate that FSCLK cycle | ||
616 | * with a minimum of contention on the clock bus. | ||
617 | */ | ||
618 | regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2, | ||
619 | NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_MASTER); | ||
620 | regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2, | ||
621 | NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_SLAVE); | ||
622 | |||
610 | /* this will restart the entire jack detection process including MIC/GND | 623 | /* this will restart the entire jack detection process including MIC/GND |
611 | * switching and create interrupts. We have to go from 0 to 1 and back | 624 | * switching and create interrupts. We have to go from 0 to 1 and back |
612 | * to 0 to restart. | 625 | * to 0 to restart. |
@@ -728,7 +741,10 @@ static irqreturn_t nau8825_interrupt(int irq, void *data) | |||
728 | struct regmap *regmap = nau8825->regmap; | 741 | struct regmap *regmap = nau8825->regmap; |
729 | int active_irq, clear_irq = 0, event = 0, event_mask = 0; | 742 | int active_irq, clear_irq = 0, event = 0, event_mask = 0; |
730 | 743 | ||
731 | regmap_read(regmap, NAU8825_REG_IRQ_STATUS, &active_irq); | 744 | if (regmap_read(regmap, NAU8825_REG_IRQ_STATUS, &active_irq)) { |
745 | dev_err(nau8825->dev, "failed to read irq status\n"); | ||
746 | return IRQ_NONE; | ||
747 | } | ||
732 | 748 | ||
733 | if ((active_irq & NAU8825_JACK_EJECTION_IRQ_MASK) == | 749 | if ((active_irq & NAU8825_JACK_EJECTION_IRQ_MASK) == |
734 | NAU8825_JACK_EJECTION_DETECTED) { | 750 | NAU8825_JACK_EJECTION_DETECTED) { |
@@ -1141,33 +1157,74 @@ static int nau8825_set_bias_level(struct snd_soc_codec *codec, | |||
1141 | return ret; | 1157 | return ret; |
1142 | } | 1158 | } |
1143 | } | 1159 | } |
1144 | |||
1145 | ret = regcache_sync(nau8825->regmap); | ||
1146 | if (ret) { | ||
1147 | dev_err(codec->dev, | ||
1148 | "Failed to sync cache: %d\n", ret); | ||
1149 | return ret; | ||
1150 | } | ||
1151 | } | 1160 | } |
1152 | |||
1153 | break; | 1161 | break; |
1154 | 1162 | ||
1155 | case SND_SOC_BIAS_OFF: | 1163 | case SND_SOC_BIAS_OFF: |
1156 | if (nau8825->mclk_freq) | 1164 | if (nau8825->mclk_freq) |
1157 | clk_disable_unprepare(nau8825->mclk); | 1165 | clk_disable_unprepare(nau8825->mclk); |
1158 | |||
1159 | regcache_mark_dirty(nau8825->regmap); | ||
1160 | break; | 1166 | break; |
1161 | } | 1167 | } |
1162 | return 0; | 1168 | return 0; |
1163 | } | 1169 | } |
1164 | 1170 | ||
1171 | #ifdef CONFIG_PM | ||
1172 | static int nau8825_suspend(struct snd_soc_codec *codec) | ||
1173 | { | ||
1174 | struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); | ||
1175 | |||
1176 | disable_irq(nau8825->irq); | ||
1177 | regcache_cache_only(nau8825->regmap, true); | ||
1178 | regcache_mark_dirty(nau8825->regmap); | ||
1179 | |||
1180 | return 0; | ||
1181 | } | ||
1182 | |||
1183 | static int nau8825_resume(struct snd_soc_codec *codec) | ||
1184 | { | ||
1185 | struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); | ||
1186 | |||
1187 | /* The chip may lose power and reset in S3. regcache_sync restores | ||
1188 | * register values including configurations for sysclk, irq, and | ||
1189 | * jack/button detection. | ||
1190 | */ | ||
1191 | regcache_cache_only(nau8825->regmap, false); | ||
1192 | regcache_sync(nau8825->regmap); | ||
1193 | |||
1194 | /* Check the jack plug status directly. If the headset is unplugged | ||
1195 | * during S3 when the chip has no power, there will be no jack | ||
1196 | * detection irq even after the nau8825_restart_jack_detection below, | ||
1197 | * because the chip just thinks no headset has ever been plugged in. | ||
1198 | */ | ||
1199 | if (!nau8825_is_jack_inserted(nau8825->regmap)) { | ||
1200 | nau8825_eject_jack(nau8825); | ||
1201 | snd_soc_jack_report(nau8825->jack, 0, SND_JACK_HEADSET); | ||
1202 | } | ||
1203 | |||
1204 | enable_irq(nau8825->irq); | ||
1205 | |||
1206 | /* Run jack detection to check the type (OMTP or CTIA) of the headset | ||
1207 | * if there is one. This handles the case where a different type of | ||
1208 | * headset is plugged in during S3. This triggers an IRQ iff a headset | ||
1209 | * is already plugged in. | ||
1210 | */ | ||
1211 | nau8825_restart_jack_detection(nau8825->regmap); | ||
1212 | |||
1213 | return 0; | ||
1214 | } | ||
1215 | #else | ||
1216 | #define nau8825_suspend NULL | ||
1217 | #define nau8825_resume NULL | ||
1218 | #endif | ||
1219 | |||
1165 | static struct snd_soc_codec_driver nau8825_codec_driver = { | 1220 | static struct snd_soc_codec_driver nau8825_codec_driver = { |
1166 | .probe = nau8825_codec_probe, | 1221 | .probe = nau8825_codec_probe, |
1167 | .set_sysclk = nau8825_set_sysclk, | 1222 | .set_sysclk = nau8825_set_sysclk, |
1168 | .set_pll = nau8825_set_pll, | 1223 | .set_pll = nau8825_set_pll, |
1169 | .set_bias_level = nau8825_set_bias_level, | 1224 | .set_bias_level = nau8825_set_bias_level, |
1170 | .suspend_bias_off = true, | 1225 | .suspend_bias_off = true, |
1226 | .suspend = nau8825_suspend, | ||
1227 | .resume = nau8825_resume, | ||
1171 | 1228 | ||
1172 | .controls = nau8825_controls, | 1229 | .controls = nau8825_controls, |
1173 | .num_controls = ARRAY_SIZE(nau8825_controls), | 1230 | .num_controls = ARRAY_SIZE(nau8825_controls), |
@@ -1277,16 +1334,6 @@ static int nau8825_setup_irq(struct nau8825 *nau8825) | |||
1277 | regmap_update_bits(regmap, NAU8825_REG_ENA_CTRL, | 1334 | regmap_update_bits(regmap, NAU8825_REG_ENA_CTRL, |
1278 | NAU8825_ENABLE_DACR, NAU8825_ENABLE_DACR); | 1335 | NAU8825_ENABLE_DACR, NAU8825_ENABLE_DACR); |
1279 | 1336 | ||
1280 | /* Chip needs one FSCLK cycle in order to generate interrupts, | ||
1281 | * as we cannot guarantee one will be provided by the system. Turning | ||
1282 | * master mode on then off enables us to generate that FSCLK cycle | ||
1283 | * with a minimum of contention on the clock bus. | ||
1284 | */ | ||
1285 | regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2, | ||
1286 | NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_MASTER); | ||
1287 | regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2, | ||
1288 | NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_SLAVE); | ||
1289 | |||
1290 | ret = devm_request_threaded_irq(nau8825->dev, nau8825->irq, NULL, | 1337 | ret = devm_request_threaded_irq(nau8825->dev, nau8825->irq, NULL, |
1291 | nau8825_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT, | 1338 | nau8825_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT, |
1292 | "nau8825", nau8825); | 1339 | "nau8825", nau8825); |
@@ -1354,36 +1401,6 @@ static int nau8825_i2c_remove(struct i2c_client *client) | |||
1354 | return 0; | 1401 | return 0; |
1355 | } | 1402 | } |
1356 | 1403 | ||
1357 | #ifdef CONFIG_PM_SLEEP | ||
1358 | static int nau8825_suspend(struct device *dev) | ||
1359 | { | ||
1360 | struct i2c_client *client = to_i2c_client(dev); | ||
1361 | struct nau8825 *nau8825 = dev_get_drvdata(dev); | ||
1362 | |||
1363 | disable_irq(client->irq); | ||
1364 | regcache_cache_only(nau8825->regmap, true); | ||
1365 | regcache_mark_dirty(nau8825->regmap); | ||
1366 | |||
1367 | return 0; | ||
1368 | } | ||
1369 | |||
1370 | static int nau8825_resume(struct device *dev) | ||
1371 | { | ||
1372 | struct i2c_client *client = to_i2c_client(dev); | ||
1373 | struct nau8825 *nau8825 = dev_get_drvdata(dev); | ||
1374 | |||
1375 | regcache_cache_only(nau8825->regmap, false); | ||
1376 | regcache_sync(nau8825->regmap); | ||
1377 | enable_irq(client->irq); | ||
1378 | |||
1379 | return 0; | ||
1380 | } | ||
1381 | #endif | ||
1382 | |||
1383 | static const struct dev_pm_ops nau8825_pm = { | ||
1384 | SET_SYSTEM_SLEEP_PM_OPS(nau8825_suspend, nau8825_resume) | ||
1385 | }; | ||
1386 | |||
1387 | static const struct i2c_device_id nau8825_i2c_ids[] = { | 1404 | static const struct i2c_device_id nau8825_i2c_ids[] = { |
1388 | { "nau8825", 0 }, | 1405 | { "nau8825", 0 }, |
1389 | { } | 1406 | { } |
@@ -1410,7 +1427,6 @@ static struct i2c_driver nau8825_driver = { | |||
1410 | .name = "nau8825", | 1427 | .name = "nau8825", |
1411 | .of_match_table = of_match_ptr(nau8825_of_ids), | 1428 | .of_match_table = of_match_ptr(nau8825_of_ids), |
1412 | .acpi_match_table = ACPI_PTR(nau8825_acpi_match), | 1429 | .acpi_match_table = ACPI_PTR(nau8825_acpi_match), |
1413 | .pm = &nau8825_pm, | ||
1414 | }, | 1430 | }, |
1415 | .probe = nau8825_i2c_probe, | 1431 | .probe = nau8825_i2c_probe, |
1416 | .remove = nau8825_i2c_remove, | 1432 | .remove = nau8825_i2c_remove, |