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 971 } 972 972 973 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 + /* 974 1004 * codec destructor 975 1005 */ 976 1006 static void snd_hda_codec_free(struct hda_codec *codec) ··· 1068 1038 codec->addr = codec_addr; 1069 1039 mutex_init(&codec->spdif_mutex); 1070 1040 mutex_init(&codec->control_mutex); 1041 + mutex_init(&codec->prepare_mutex); 1071 1042 init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); 1072 1043 init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); 1073 1044 snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32); 1074 1045 snd_array_init(&codec->nids, sizeof(struct hda_nid_item), 32); 1075 1046 snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16); 1076 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); 1077 1049 if (codec->bus->modelname) { 1078 1050 codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL); 1079 1051 if (!codec->modelname) { ··· 1213 1181 u32 stream_tag, 1214 1182 int channel_id, int format) 1215 1183 { 1184 + struct hda_cvt_setup *p; 1185 + unsigned int oldval, newval; 1186 + int i; 1187 + 1216 1188 if (!nid) 1217 1189 return; 1218 1190 1219 1191 snd_printdd("hda_codec_setup_stream: " 1220 1192 "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n", 1221 1193 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); 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 + } 1226 1229 } 1227 1230 EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream); 1228 1231 ··· 1268 1201 */ 1269 1202 void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid) 1270 1203 { 1204 + struct hda_cvt_setup *p; 1205 + 1271 1206 if (!nid) 1272 1207 return; 1273 1208 1274 1209 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 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; 1280 1216 } 1281 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 + } 1282 1252 1283 1253 /* 1284 1254 * amp access functions ··· 3032 2928 { 3033 2929 if (codec->patch_ops.suspend) 3034 2930 codec->patch_ops.suspend(codec, PMSG_SUSPEND); 2931 + hda_cleanup_all_streams(codec); 3035 2932 hda_set_power_state(codec, 3036 2933 codec->afg ? codec->afg : codec->mfg, 3037 2934 AC_PWRST_D3); ··· 3481 3376 } 3482 3377 return 0; 3483 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); 3484 3408 3485 3409 /* global */ 3486 3410 const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = {
+12
sound/pci/hda/hda_codec.h
··· 826 826 827 827 struct mutex spdif_mutex; 828 828 struct mutex control_mutex; 829 + struct mutex prepare_mutex; 829 830 unsigned int spdif_status; /* IEC958 status bits */ 830 831 unsigned short spdif_ctls; /* SPDIF control bits */ 831 832 unsigned int spdif_in_enable; /* SPDIF input enable? */ 832 833 hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */ 833 834 struct snd_array init_pins; /* initial (BIOS) pin configurations */ 834 835 struct snd_array driver_pins; /* pin configs set by codec parser */ 836 + struct snd_array cvt_setups; /* audio convert setups */ 835 837 836 838 #ifdef CONFIG_SND_HDA_HWDEP 837 839 struct snd_hwdep *hwdep; /* assigned hwdep device */ ··· 950 948 */ 951 949 int snd_hda_build_pcms(struct hda_bus *bus); 952 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 + 953 961 void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, 954 962 u32 stream_tag, 955 963 int channel_id, int format);
+3 -3
sound/pci/hda/hda_intel.c
··· 1634 1634 azx_dev->period_bytes = 0; 1635 1635 azx_dev->format_val = 0; 1636 1636 1637 - hinfo->ops.cleanup(hinfo, apcm->codec, substream); 1637 + snd_hda_codec_cleanup(apcm->codec, hinfo, substream); 1638 1638 1639 1639 return snd_pcm_lib_free_pages(substream); 1640 1640 } ··· 1688 1688 else 1689 1689 azx_dev->fifo_size = 0; 1690 1690 1691 - return hinfo->ops.prepare(hinfo, apcm->codec, azx_dev->stream_tag, 1692 - azx_dev->format_val, substream); 1691 + return snd_hda_codec_prepare(apcm->codec, hinfo, azx_dev->stream_tag, 1692 + azx_dev->format_val, substream); 1693 1693 } 1694 1694 1695 1695 static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)