diff options
author | Takashi Iwai <tiwai@suse.de> | 2015-02-27 12:17:28 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2015-03-03 05:28:12 -0500 |
commit | 9a6246ff78ac33af78f82704cde6fec361597eea (patch) | |
tree | abb77fd575a77c4fe97ac1d5b3d76682d465935a /sound/pci/hda | |
parent | e086e3035e0691b362755d1b5e24df631eee335a (diff) |
ALSA: hda - Implement unbind more safely
Now we have all pieces ready, and put them into places:
- add the hda_pcm refcount to azx_pcm_open() and azx_pcm_close(),
- call the most of cleanup code in hda_codec_reset() from the codec
driver remove,
- call the same code also from the hda_codec object free.
Then the codec driver can be unbound more safely now.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda')
-rw-r--r-- | sound/pci/hda/hda_bind.c | 3 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.c | 70 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.h | 1 | ||||
-rw-r--r-- | sound/pci/hda/hda_controller.c | 3 | ||||
-rw-r--r-- | sound/pci/hda/hda_local.h | 1 |
5 files changed, 43 insertions, 35 deletions
diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c index 2d00417494e2..311896a23cd1 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/pci/hda/hda_bind.c | |||
@@ -125,8 +125,7 @@ static int hda_codec_driver_remove(struct device *dev) | |||
125 | 125 | ||
126 | if (codec->patch_ops.free) | 126 | if (codec->patch_ops.free) |
127 | codec->patch_ops.free(codec); | 127 | codec->patch_ops.free(codec); |
128 | codec->preset = NULL; | 128 | snd_hda_codec_cleanup_for_unbind(codec); |
129 | memset(&codec->patch_ops, 0, sizeof(codec->patch_ops)); | ||
130 | module_put(dev->driver->owner); | 129 | module_put(dev->driver->owner); |
131 | return 0; | 130 | return 0; |
132 | } | 131 | } |
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 3bd9158addc2..2c7e481a9171 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c | |||
@@ -1160,36 +1160,62 @@ struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec, | |||
1160 | } | 1160 | } |
1161 | EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_new); | 1161 | EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_new); |
1162 | 1162 | ||
1163 | /* | ||
1164 | * codec destructor | ||
1165 | */ | ||
1163 | static void codec_release_pcms(struct hda_codec *codec) | 1166 | static void codec_release_pcms(struct hda_codec *codec) |
1164 | { | 1167 | { |
1165 | struct hda_pcm *pcm, *n; | 1168 | struct hda_pcm *pcm, *n; |
1166 | 1169 | ||
1167 | list_for_each_entry_safe(pcm, n, &codec->pcm_list_head, list) { | 1170 | list_for_each_entry_safe(pcm, n, &codec->pcm_list_head, list) { |
1168 | list_del_init(&pcm->list); | 1171 | list_del_init(&pcm->list); |
1172 | if (pcm->pcm) | ||
1173 | snd_device_disconnect(codec->card, pcm->pcm); | ||
1169 | snd_hda_codec_pcm_put(pcm); | 1174 | snd_hda_codec_pcm_put(pcm); |
1170 | } | 1175 | } |
1171 | } | 1176 | } |
1172 | 1177 | ||
1173 | /* | 1178 | void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec) |
1174 | * codec destructor | 1179 | { |
1175 | */ | 1180 | cancel_delayed_work_sync(&codec->jackpoll_work); |
1181 | flush_workqueue(codec->bus->workq); | ||
1182 | if (!codec->in_freeing) | ||
1183 | snd_hda_ctls_clear(codec); | ||
1184 | codec_release_pcms(codec); | ||
1185 | snd_hda_detach_beep_device(codec); | ||
1186 | memset(&codec->patch_ops, 0, sizeof(codec->patch_ops)); | ||
1187 | snd_hda_jack_tbl_clear(codec); | ||
1188 | codec->proc_widget_hook = NULL; | ||
1189 | codec->spec = NULL; | ||
1190 | |||
1191 | free_hda_cache(&codec->amp_cache); | ||
1192 | free_hda_cache(&codec->cmd_cache); | ||
1193 | init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); | ||
1194 | init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); | ||
1195 | |||
1196 | /* free only driver_pins so that init_pins + user_pins are restored */ | ||
1197 | snd_array_free(&codec->driver_pins); | ||
1198 | snd_array_free(&codec->cvt_setups); | ||
1199 | snd_array_free(&codec->spdif_out); | ||
1200 | snd_array_free(&codec->verbs); | ||
1201 | codec->preset = NULL; | ||
1202 | codec->slave_dig_outs = NULL; | ||
1203 | codec->spdif_status_reset = 0; | ||
1204 | snd_array_free(&codec->mixers); | ||
1205 | snd_array_free(&codec->nids); | ||
1206 | remove_conn_list(codec); | ||
1207 | } | ||
1208 | |||
1176 | static void snd_hda_codec_free(struct hda_codec *codec) | 1209 | static void snd_hda_codec_free(struct hda_codec *codec) |
1177 | { | 1210 | { |
1178 | if (!codec) | 1211 | if (!codec) |
1179 | return; | 1212 | return; |
1180 | cancel_delayed_work_sync(&codec->jackpoll_work); | 1213 | codec->in_freeing = 1; |
1181 | codec_release_pcms(codec); | ||
1182 | if (device_is_registered(hda_codec_dev(codec))) | 1214 | if (device_is_registered(hda_codec_dev(codec))) |
1183 | device_del(hda_codec_dev(codec)); | 1215 | device_del(hda_codec_dev(codec)); |
1184 | snd_hda_jack_tbl_clear(codec); | ||
1185 | free_init_pincfgs(codec); | 1216 | free_init_pincfgs(codec); |
1186 | flush_workqueue(codec->bus->workq); | 1217 | flush_workqueue(codec->bus->workq); |
1187 | list_del(&codec->list); | 1218 | list_del(&codec->list); |
1188 | snd_array_free(&codec->mixers); | ||
1189 | snd_array_free(&codec->nids); | ||
1190 | snd_array_free(&codec->cvt_setups); | ||
1191 | snd_array_free(&codec->spdif_out); | ||
1192 | remove_conn_list(codec); | ||
1193 | codec->bus->caddr_tbl[codec->addr] = NULL; | 1219 | codec->bus->caddr_tbl[codec->addr] = NULL; |
1194 | clear_bit(codec->addr, &codec->bus->codec_powered); | 1220 | clear_bit(codec->addr, &codec->bus->codec_powered); |
1195 | snd_hda_sysfs_clear(codec); | 1221 | snd_hda_sysfs_clear(codec); |
@@ -2479,31 +2505,9 @@ int snd_hda_codec_reset(struct hda_codec *codec) | |||
2479 | return -EBUSY; | 2505 | return -EBUSY; |
2480 | 2506 | ||
2481 | /* OK, let it free */ | 2507 | /* OK, let it free */ |
2482 | cancel_delayed_work_sync(&codec->jackpoll_work); | ||
2483 | flush_workqueue(bus->workq); | ||
2484 | snd_hda_ctls_clear(codec); | ||
2485 | codec_release_pcms(codec); | ||
2486 | snd_hda_detach_beep_device(codec); | ||
2487 | if (device_is_registered(hda_codec_dev(codec))) | 2508 | if (device_is_registered(hda_codec_dev(codec))) |
2488 | device_del(hda_codec_dev(codec)); | 2509 | device_del(hda_codec_dev(codec)); |
2489 | 2510 | ||
2490 | memset(&codec->patch_ops, 0, sizeof(codec->patch_ops)); | ||
2491 | snd_hda_jack_tbl_clear(codec); | ||
2492 | codec->proc_widget_hook = NULL; | ||
2493 | codec->spec = NULL; | ||
2494 | free_hda_cache(&codec->amp_cache); | ||
2495 | free_hda_cache(&codec->cmd_cache); | ||
2496 | init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); | ||
2497 | init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); | ||
2498 | /* free only driver_pins so that init_pins + user_pins are restored */ | ||
2499 | snd_array_free(&codec->driver_pins); | ||
2500 | snd_array_free(&codec->cvt_setups); | ||
2501 | snd_array_free(&codec->spdif_out); | ||
2502 | snd_array_free(&codec->verbs); | ||
2503 | codec->preset = NULL; | ||
2504 | codec->slave_dig_outs = NULL; | ||
2505 | codec->spdif_status_reset = 0; | ||
2506 | |||
2507 | /* allow device access again */ | 2511 | /* allow device access again */ |
2508 | snd_hda_unlock_devices(bus); | 2512 | snd_hda_unlock_devices(bus); |
2509 | return 0; | 2513 | return 0; |
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 2ccd6f9a91fe..fc62ca51fd35 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h | |||
@@ -350,6 +350,7 @@ struct hda_codec { | |||
350 | #endif | 350 | #endif |
351 | 351 | ||
352 | /* misc flags */ | 352 | /* misc flags */ |
353 | unsigned int in_freeing:1; /* being released */ | ||
353 | unsigned int spdif_status_reset :1; /* needs to toggle SPDIF for each | 354 | unsigned int spdif_status_reset :1; /* needs to toggle SPDIF for each |
354 | * status change | 355 | * status change |
355 | * (e.g. Realtek codecs) | 356 | * (e.g. Realtek codecs) |
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index ad85f9bfaf57..cae50d5ffb81 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c | |||
@@ -420,6 +420,7 @@ static int azx_pcm_close(struct snd_pcm_substream *substream) | |||
420 | hinfo->ops.close(hinfo, apcm->codec, substream); | 420 | hinfo->ops.close(hinfo, apcm->codec, substream); |
421 | snd_hda_power_down(apcm->codec); | 421 | snd_hda_power_down(apcm->codec); |
422 | mutex_unlock(&chip->open_mutex); | 422 | mutex_unlock(&chip->open_mutex); |
423 | snd_hda_codec_pcm_put(apcm->info); | ||
423 | return 0; | 424 | return 0; |
424 | } | 425 | } |
425 | 426 | ||
@@ -806,6 +807,7 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) | |||
806 | int err; | 807 | int err; |
807 | int buff_step; | 808 | int buff_step; |
808 | 809 | ||
810 | snd_hda_codec_pcm_get(apcm->info); | ||
809 | mutex_lock(&chip->open_mutex); | 811 | mutex_lock(&chip->open_mutex); |
810 | azx_dev = azx_assign_device(chip, substream); | 812 | azx_dev = azx_assign_device(chip, substream); |
811 | if (azx_dev == NULL) { | 813 | if (azx_dev == NULL) { |
@@ -887,6 +889,7 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) | |||
887 | snd_hda_power_down(apcm->codec); | 889 | snd_hda_power_down(apcm->codec); |
888 | unlock: | 890 | unlock: |
889 | mutex_unlock(&chip->open_mutex); | 891 | mutex_unlock(&chip->open_mutex); |
892 | snd_hda_codec_pcm_put(apcm->info); | ||
890 | return err; | 893 | return err; |
891 | } | 894 | } |
892 | 895 | ||
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 8588813163e3..1d001647fc47 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h | |||
@@ -150,6 +150,7 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name, | |||
150 | #define snd_hda_add_vmaster(codec, name, tlv, slaves, suffix) \ | 150 | #define snd_hda_add_vmaster(codec, name, tlv, slaves, suffix) \ |
151 | __snd_hda_add_vmaster(codec, name, tlv, slaves, suffix, true, NULL) | 151 | __snd_hda_add_vmaster(codec, name, tlv, slaves, suffix, true, NULL) |
152 | int snd_hda_codec_reset(struct hda_codec *codec); | 152 | int snd_hda_codec_reset(struct hda_codec *codec); |
153 | void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec); | ||
153 | 154 | ||
154 | enum { | 155 | enum { |
155 | HDA_VMUTE_OFF, | 156 | HDA_VMUTE_OFF, |