ALSA: hda - Make converter setups sticky

So far, we reset the converter setups like the stream-tag, the
channel-id and format-id in prepare callbacks, and clear them in
cleanup callbacks. This often causes a silence of the digital
receiver for a couple of seconds.

This patch tries to delay the converter setup changes as much as
possible. The converter setups are cached and aren't reset as long
as the same values are used. At suspend/resume, they are cleared
to be recovered properly, too.

Signed-off-by: Takashi Iwai <tiwai@suse.de>

+158 -12
+143 -9
sound/pci/hda/hda_codec.c
··· 971 } 972 973 /* 974 * codec destructor 975 */ 976 static void snd_hda_codec_free(struct hda_codec *codec) ··· 1068 codec->addr = codec_addr; 1069 mutex_init(&codec->spdif_mutex); 1070 mutex_init(&codec->control_mutex); 1071 init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); 1072 init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); 1073 snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32); 1074 snd_array_init(&codec->nids, sizeof(struct hda_nid_item), 32); 1075 snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16); 1076 snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16); 1077 if (codec->bus->modelname) { 1078 codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL); 1079 if (!codec->modelname) { ··· 1213 u32 stream_tag, 1214 int channel_id, int format) 1215 { 1216 if (!nid) 1217 return; 1218 1219 snd_printdd("hda_codec_setup_stream: " 1220 "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n", 1221 nid, stream_tag, channel_id, format); 1222 - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 1223 - (stream_tag << 4) | channel_id); 1224 - msleep(1); 1225 - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format); 1226 } 1227 EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream); 1228 ··· 1268 */ 1269 void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid) 1270 { 1271 if (!nid) 1272 return; 1273 1274 snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid); 1275 - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0); 1276 - #if 0 /* keep the format */ 1277 - msleep(1); 1278 - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0); 1279 - #endif 1280 } 1281 EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup_stream); 1282 1283 /* 1284 * amp access functions ··· 3032 { 3033 if (codec->patch_ops.suspend) 3034 codec->patch_ops.suspend(codec, PMSG_SUSPEND); 3035 hda_set_power_state(codec, 3036 codec->afg ? codec->afg : codec->mfg, 3037 AC_PWRST_D3); ··· 3481 } 3482 return 0; 3483 } 3484 3485 /* global */ 3486 const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = {
··· 971 } 972 973 /* 974 + * audio-converter setup caches 975 + */ 976 + struct hda_cvt_setup { 977 + hda_nid_t nid; 978 + u8 stream_tag; 979 + u8 channel_id; 980 + u16 format_id; 981 + unsigned char active; /* cvt is currently used */ 982 + unsigned char dirty; /* setups should be cleared */ 983 + }; 984 + 985 + /* get or create a cache entry for the given audio converter NID */ 986 + static struct hda_cvt_setup * 987 + get_hda_cvt_setup(struct hda_codec *codec, hda_nid_t nid) 988 + { 989 + struct hda_cvt_setup *p; 990 + int i; 991 + 992 + for (i = 0; i < codec->cvt_setups.used; i++) { 993 + p = snd_array_elem(&codec->cvt_setups, i); 994 + if (p->nid == nid) 995 + return p; 996 + } 997 + p = snd_array_new(&codec->cvt_setups); 998 + if (p) 999 + p->nid = nid; 1000 + return p; 1001 + } 1002 + 1003 + /* 1004 * codec destructor 1005 */ 1006 static void snd_hda_codec_free(struct hda_codec *codec) ··· 1038 codec->addr = codec_addr; 1039 mutex_init(&codec->spdif_mutex); 1040 mutex_init(&codec->control_mutex); 1041 + mutex_init(&codec->prepare_mutex); 1042 init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); 1043 init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); 1044 snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32); 1045 snd_array_init(&codec->nids, sizeof(struct hda_nid_item), 32); 1046 snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16); 1047 snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16); 1048 + snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8); 1049 if (codec->bus->modelname) { 1050 codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL); 1051 if (!codec->modelname) { ··· 1181 u32 stream_tag, 1182 int channel_id, int format) 1183 { 1184 + struct hda_cvt_setup *p; 1185 + unsigned int oldval, newval; 1186 + int i; 1187 + 1188 if (!nid) 1189 return; 1190 1191 snd_printdd("hda_codec_setup_stream: " 1192 "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n", 1193 nid, stream_tag, channel_id, format); 1194 + p = get_hda_cvt_setup(codec, nid); 1195 + if (!p) 1196 + return; 1197 + /* update the stream-id if changed */ 1198 + if (p->stream_tag != stream_tag || p->channel_id != channel_id) { 1199 + oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); 1200 + newval = (stream_tag << 4) | channel_id; 1201 + if (oldval != newval) 1202 + snd_hda_codec_write(codec, nid, 0, 1203 + AC_VERB_SET_CHANNEL_STREAMID, 1204 + newval); 1205 + p->stream_tag = stream_tag; 1206 + p->channel_id = channel_id; 1207 + } 1208 + /* update the format-id if changed */ 1209 + if (p->format_id != format) { 1210 + oldval = snd_hda_codec_read(codec, nid, 0, 1211 + AC_VERB_GET_STREAM_FORMAT, 0); 1212 + if (oldval != format) { 1213 + msleep(1); 1214 + snd_hda_codec_write(codec, nid, 0, 1215 + AC_VERB_SET_STREAM_FORMAT, 1216 + format); 1217 + } 1218 + p->format_id = format; 1219 + } 1220 + p->active = 1; 1221 + p->dirty = 0; 1222 + 1223 + /* make other inactive cvts with the same stream-tag dirty */ 1224 + for (i = 0; i < codec->cvt_setups.used; i++) { 1225 + p = snd_array_elem(&codec->cvt_setups, i); 1226 + if (!p->active && p->stream_tag == stream_tag) 1227 + p->dirty = 1; 1228 + } 1229 } 1230 EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream); 1231 ··· 1201 */ 1202 void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid) 1203 { 1204 + struct hda_cvt_setup *p; 1205 + 1206 if (!nid) 1207 return; 1208 1209 snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid); 1210 + /* here we just clear the active flag; actual clean-ups will be done 1211 + * in purify_inactive_streams() 1212 + */ 1213 + p = get_hda_cvt_setup(codec, nid); 1214 + if (p) 1215 + p->active = 0; 1216 } 1217 EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup_stream); 1218 + 1219 + static void really_cleanup_stream(struct hda_codec *codec, 1220 + struct hda_cvt_setup *q) 1221 + { 1222 + hda_nid_t nid = q->nid; 1223 + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0); 1224 + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0); 1225 + memset(q, 0, sizeof(*q)); 1226 + q->nid = nid; 1227 + } 1228 + 1229 + /* clean up the all conflicting obsolete streams */ 1230 + static void purify_inactive_streams(struct hda_codec *codec) 1231 + { 1232 + int i; 1233 + 1234 + for (i = 0; i < codec->cvt_setups.used; i++) { 1235 + struct hda_cvt_setup *p = snd_array_elem(&codec->cvt_setups, i); 1236 + if (p->dirty) 1237 + really_cleanup_stream(codec, p); 1238 + } 1239 + } 1240 + 1241 + /* clean up all streams; called from suspend */ 1242 + static void hda_cleanup_all_streams(struct hda_codec *codec) 1243 + { 1244 + int i; 1245 + 1246 + for (i = 0; i < codec->cvt_setups.used; i++) { 1247 + struct hda_cvt_setup *p = snd_array_elem(&codec->cvt_setups, i); 1248 + if (p->stream_tag) 1249 + really_cleanup_stream(codec, p); 1250 + } 1251 + } 1252 1253 /* 1254 * amp access functions ··· 2928 { 2929 if (codec->patch_ops.suspend) 2930 codec->patch_ops.suspend(codec, PMSG_SUSPEND); 2931 + hda_cleanup_all_streams(codec); 2932 hda_set_power_state(codec, 2933 codec->afg ? codec->afg : codec->mfg, 2934 AC_PWRST_D3); ··· 3376 } 3377 return 0; 3378 } 3379 + 3380 + /* 3381 + * codec prepare/cleanup entries 3382 + */ 3383 + int snd_hda_codec_prepare(struct hda_codec *codec, 3384 + struct hda_pcm_stream *hinfo, 3385 + unsigned int stream, 3386 + unsigned int format, 3387 + struct snd_pcm_substream *substream) 3388 + { 3389 + int ret; 3390 + mutex_lock(&codec->prepare_mutex); 3391 + ret = hinfo->ops.prepare(hinfo, codec, stream, format, substream); 3392 + if (ret >= 0) 3393 + purify_inactive_streams(codec); 3394 + mutex_unlock(&codec->prepare_mutex); 3395 + return ret; 3396 + } 3397 + EXPORT_SYMBOL_HDA(snd_hda_codec_prepare); 3398 + 3399 + void snd_hda_codec_cleanup(struct hda_codec *codec, 3400 + struct hda_pcm_stream *hinfo, 3401 + struct snd_pcm_substream *substream) 3402 + { 3403 + mutex_lock(&codec->prepare_mutex); 3404 + hinfo->ops.cleanup(hinfo, codec, substream); 3405 + mutex_unlock(&codec->prepare_mutex); 3406 + } 3407 + EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup); 3408 3409 /* global */ 3410 const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = {
+12
sound/pci/hda/hda_codec.h
··· 826 827 struct mutex spdif_mutex; 828 struct mutex control_mutex; 829 unsigned int spdif_status; /* IEC958 status bits */ 830 unsigned short spdif_ctls; /* SPDIF control bits */ 831 unsigned int spdif_in_enable; /* SPDIF input enable? */ 832 hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */ 833 struct snd_array init_pins; /* initial (BIOS) pin configurations */ 834 struct snd_array driver_pins; /* pin configs set by codec parser */ 835 836 #ifdef CONFIG_SND_HDA_HWDEP 837 struct snd_hwdep *hwdep; /* assigned hwdep device */ ··· 950 */ 951 int snd_hda_build_pcms(struct hda_bus *bus); 952 int snd_hda_codec_build_pcms(struct hda_codec *codec); 953 void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, 954 u32 stream_tag, 955 int channel_id, int format);
··· 826 827 struct mutex spdif_mutex; 828 struct mutex control_mutex; 829 + struct mutex prepare_mutex; 830 unsigned int spdif_status; /* IEC958 status bits */ 831 unsigned short spdif_ctls; /* SPDIF control bits */ 832 unsigned int spdif_in_enable; /* SPDIF input enable? */ 833 hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */ 834 struct snd_array init_pins; /* initial (BIOS) pin configurations */ 835 struct snd_array driver_pins; /* pin configs set by codec parser */ 836 + struct snd_array cvt_setups; /* audio convert setups */ 837 838 #ifdef CONFIG_SND_HDA_HWDEP 839 struct snd_hwdep *hwdep; /* assigned hwdep device */ ··· 948 */ 949 int snd_hda_build_pcms(struct hda_bus *bus); 950 int snd_hda_codec_build_pcms(struct hda_codec *codec); 951 + 952 + int snd_hda_codec_prepare(struct hda_codec *codec, 953 + struct hda_pcm_stream *hinfo, 954 + unsigned int stream, 955 + unsigned int format, 956 + struct snd_pcm_substream *substream); 957 + void snd_hda_codec_cleanup(struct hda_codec *codec, 958 + struct hda_pcm_stream *hinfo, 959 + struct snd_pcm_substream *substream); 960 + 961 void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, 962 u32 stream_tag, 963 int channel_id, int format);
+3 -3
sound/pci/hda/hda_intel.c
··· 1634 azx_dev->period_bytes = 0; 1635 azx_dev->format_val = 0; 1636 1637 - hinfo->ops.cleanup(hinfo, apcm->codec, substream); 1638 1639 return snd_pcm_lib_free_pages(substream); 1640 } ··· 1688 else 1689 azx_dev->fifo_size = 0; 1690 1691 - return hinfo->ops.prepare(hinfo, apcm->codec, azx_dev->stream_tag, 1692 - azx_dev->format_val, substream); 1693 } 1694 1695 static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
··· 1634 azx_dev->period_bytes = 0; 1635 azx_dev->format_val = 0; 1636 1637 + snd_hda_codec_cleanup(apcm->codec, hinfo, substream); 1638 1639 return snd_pcm_lib_free_pages(substream); 1640 } ··· 1688 else 1689 azx_dev->fifo_size = 0; 1690 1691 + return snd_hda_codec_prepare(apcm->codec, hinfo, azx_dev->stream_tag, 1692 + azx_dev->format_val, substream); 1693 } 1694 1695 static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)