aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2012-05-08 11:08:10 -0400
committerTakashi Iwai <tiwai@suse.de>2012-05-08 12:01:30 -0400
commit5536c6d69376273d5210ce05b7d5d462f0dff9d6 (patch)
treed65dd8459225d8d6eb49ba8e3f321120274bea14 /sound/pci
parent339876d70a5794c0d5fe09d37827c63148d5017a (diff)
ALSA: hda - Protect the power-saving count with spinlock
To avoid some races. Still not perfect, but now a bit safer. Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci')
-rw-r--r--sound/pci/hda/hda_codec.c23
-rw-r--r--sound/pci/hda/hda_codec.h1
2 files changed, 22 insertions, 2 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 8bd34320ef91..30a3d8ff978b 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1266,6 +1266,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
1266 snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16); 1266 snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16);
1267 1267
1268#ifdef CONFIG_SND_HDA_POWER_SAVE 1268#ifdef CONFIG_SND_HDA_POWER_SAVE
1269 spin_lock_init(&codec->power_lock);
1269 INIT_DELAYED_WORK(&codec->power_work, hda_power_work); 1270 INIT_DELAYED_WORK(&codec->power_work, hda_power_work);
1270 /* snd_hda_codec_new() marks the codec as power-up, and leave it as is. 1271 /* snd_hda_codec_new() marks the codec as power-up, and leave it as is.
1271 * the caller has to power down appropriatley after initialization 1272 * the caller has to power down appropriatley after initialization
@@ -4292,12 +4293,16 @@ static void hda_power_work(struct work_struct *work)
4292 container_of(work, struct hda_codec, power_work.work); 4293 container_of(work, struct hda_codec, power_work.work);
4293 struct hda_bus *bus = codec->bus; 4294 struct hda_bus *bus = codec->bus;
4294 4295
4296 spin_lock(&codec->power_lock);
4295 if (!codec->power_on || codec->power_count) { 4297 if (!codec->power_on || codec->power_count) {
4296 codec->power_transition = 0; 4298 codec->power_transition = 0;
4299 spin_unlock(&codec->power_lock);
4297 return; 4300 return;
4298 } 4301 }
4299 4302
4300 trace_hda_power_down(codec); 4303 trace_hda_power_down(codec);
4304 spin_unlock(&codec->power_lock);
4305
4301 hda_call_codec_suspend(codec); 4306 hda_call_codec_suspend(codec);
4302 if (bus->ops.pm_notify) 4307 if (bus->ops.pm_notify)
4303 bus->ops.pm_notify(bus); 4308 bus->ops.pm_notify(bus);
@@ -4305,9 +4310,11 @@ static void hda_power_work(struct work_struct *work)
4305 4310
4306static void hda_keep_power_on(struct hda_codec *codec) 4311static void hda_keep_power_on(struct hda_codec *codec)
4307{ 4312{
4313 spin_lock(&codec->power_lock);
4308 codec->power_count++; 4314 codec->power_count++;
4309 codec->power_on = 1; 4315 codec->power_on = 1;
4310 codec->power_jiffies = jiffies; 4316 codec->power_jiffies = jiffies;
4317 spin_unlock(&codec->power_lock);
4311} 4318}
4312 4319
4313/* update the power on/off account with the current jiffies */ 4320/* update the power on/off account with the current jiffies */
@@ -4332,20 +4339,28 @@ void snd_hda_power_up(struct hda_codec *codec)
4332{ 4339{
4333 struct hda_bus *bus = codec->bus; 4340 struct hda_bus *bus = codec->bus;
4334 4341
4342 spin_lock(&codec->power_lock);
4335 codec->power_count++; 4343 codec->power_count++;
4336 if (codec->power_on || codec->power_transition) 4344 if (codec->power_on || codec->power_transition) {
4345 spin_unlock(&codec->power_lock);
4337 return; 4346 return;
4347 }
4338 4348
4339 trace_hda_power_up(codec); 4349 trace_hda_power_up(codec);
4340 snd_hda_update_power_acct(codec); 4350 snd_hda_update_power_acct(codec);
4341 codec->power_on = 1; 4351 codec->power_on = 1;
4342 codec->power_jiffies = jiffies; 4352 codec->power_jiffies = jiffies;
4343 codec->power_transition = 1; /* avoid reentrance */ 4353 codec->power_transition = 1; /* avoid reentrance */
4354 spin_unlock(&codec->power_lock);
4355
4344 if (bus->ops.pm_notify) 4356 if (bus->ops.pm_notify)
4345 bus->ops.pm_notify(bus); 4357 bus->ops.pm_notify(bus);
4346 hda_call_codec_resume(codec); 4358 hda_call_codec_resume(codec);
4359
4360 spin_lock(&codec->power_lock);
4347 cancel_delayed_work(&codec->power_work); 4361 cancel_delayed_work(&codec->power_work);
4348 codec->power_transition = 0; 4362 codec->power_transition = 0;
4363 spin_unlock(&codec->power_lock);
4349} 4364}
4350EXPORT_SYMBOL_HDA(snd_hda_power_up); 4365EXPORT_SYMBOL_HDA(snd_hda_power_up);
4351 4366
@@ -4361,14 +4376,18 @@ EXPORT_SYMBOL_HDA(snd_hda_power_up);
4361 */ 4376 */
4362void snd_hda_power_down(struct hda_codec *codec) 4377void snd_hda_power_down(struct hda_codec *codec)
4363{ 4378{
4379 spin_lock(&codec->power_lock);
4364 --codec->power_count; 4380 --codec->power_count;
4365 if (!codec->power_on || codec->power_count || codec->power_transition) 4381 if (!codec->power_on || codec->power_count || codec->power_transition) {
4382 spin_unlock(&codec->power_lock);
4366 return; 4383 return;
4384 }
4367 if (power_save(codec)) { 4385 if (power_save(codec)) {
4368 codec->power_transition = 1; /* avoid reentrance */ 4386 codec->power_transition = 1; /* avoid reentrance */
4369 queue_delayed_work(codec->bus->workq, &codec->power_work, 4387 queue_delayed_work(codec->bus->workq, &codec->power_work,
4370 msecs_to_jiffies(power_save(codec) * 1000)); 4388 msecs_to_jiffies(power_save(codec) * 1000));
4371 } 4389 }
4390 spin_unlock(&codec->power_lock);
4372} 4391}
4373EXPORT_SYMBOL_HDA(snd_hda_power_down); 4392EXPORT_SYMBOL_HDA(snd_hda_power_down);
4374 4393
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 0fe64912cefc..77cd99531aae 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -867,6 +867,7 @@ struct hda_codec {
867 unsigned long power_on_acct; 867 unsigned long power_on_acct;
868 unsigned long power_off_acct; 868 unsigned long power_off_acct;
869 unsigned long power_jiffies; 869 unsigned long power_jiffies;
870 spinlock_t power_lock;
870#endif 871#endif
871 872
872 /* codec-specific additional proc output */ 873 /* codec-specific additional proc output */