aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2010-08-06 07:48:11 -0400
committerTakashi Iwai <tiwai@suse.de>2010-08-06 07:48:11 -0400
commiteb541337b7a43822fce7d0c9d967ee149b2d9a96 (patch)
tree6f46656a2b8545720a818304050fbbeddd872989 /sound
parentfe3eb0a73ca052ee9346a1544f4c00e697cc9a4e (diff)
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>
Diffstat (limited to 'sound')
-rw-r--r--sound/pci/hda/hda_codec.c150
-rw-r--r--sound/pci/hda/hda_codec.h12
-rw-r--r--sound/pci/hda/hda_intel.c6
3 files changed, 157 insertions, 11 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index a7802b99436c..720a81d711e3 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -971,6 +971,36 @@ static void restore_init_pincfgs(struct hda_codec *codec)
971} 971}
972 972
973/* 973/*
974 * audio-converter setup caches
975 */
976struct 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 */
986static struct hda_cvt_setup *
987get_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 * codec destructor 1004 * codec destructor
975 */ 1005 */
976static void snd_hda_codec_free(struct hda_codec *codec) 1006static void snd_hda_codec_free(struct hda_codec *codec)
@@ -1038,12 +1068,14 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
1038 codec->addr = codec_addr; 1068 codec->addr = codec_addr;
1039 mutex_init(&codec->spdif_mutex); 1069 mutex_init(&codec->spdif_mutex);
1040 mutex_init(&codec->control_mutex); 1070 mutex_init(&codec->control_mutex);
1071 mutex_init(&codec->prepare_mutex);
1041 init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); 1072 init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
1042 init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); 1073 init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
1043 snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32); 1074 snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32);
1044 snd_array_init(&codec->nids, sizeof(struct hda_nid_item), 32); 1075 snd_array_init(&codec->nids, sizeof(struct hda_nid_item), 32);
1045 snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16); 1076 snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
1046 snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16); 1077 snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
1078 snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8);
1047 if (codec->bus->modelname) { 1079 if (codec->bus->modelname) {
1048 codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL); 1080 codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
1049 if (!codec->modelname) { 1081 if (!codec->modelname) {
@@ -1181,16 +1213,51 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
1181 u32 stream_tag, 1213 u32 stream_tag,
1182 int channel_id, int format) 1214 int channel_id, int format)
1183{ 1215{
1216 struct hda_cvt_setup *p;
1217 unsigned int oldval, newval;
1218 int i;
1219
1184 if (!nid) 1220 if (!nid)
1185 return; 1221 return;
1186 1222
1187 snd_printdd("hda_codec_setup_stream: " 1223 snd_printdd("hda_codec_setup_stream: "
1188 "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n", 1224 "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n",
1189 nid, stream_tag, channel_id, format); 1225 nid, stream_tag, channel_id, format);
1190 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 1226 p = get_hda_cvt_setup(codec, nid);
1191 (stream_tag << 4) | channel_id); 1227 if (!p)
1192 msleep(1); 1228 return;
1193 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format); 1229 /* update the stream-id if changed */
1230 if (p->stream_tag != stream_tag || p->channel_id != channel_id) {
1231 oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
1232 newval = (stream_tag << 4) | channel_id;
1233 if (oldval != newval)
1234 snd_hda_codec_write(codec, nid, 0,
1235 AC_VERB_SET_CHANNEL_STREAMID,
1236 newval);
1237 p->stream_tag = stream_tag;
1238 p->channel_id = channel_id;
1239 }
1240 /* update the format-id if changed */
1241 if (p->format_id != format) {
1242 oldval = snd_hda_codec_read(codec, nid, 0,
1243 AC_VERB_GET_STREAM_FORMAT, 0);
1244 if (oldval != format) {
1245 msleep(1);
1246 snd_hda_codec_write(codec, nid, 0,
1247 AC_VERB_SET_STREAM_FORMAT,
1248 format);
1249 }
1250 p->format_id = format;
1251 }
1252 p->active = 1;
1253 p->dirty = 0;
1254
1255 /* make other inactive cvts with the same stream-tag dirty */
1256 for (i = 0; i < codec->cvt_setups.used; i++) {
1257 p = snd_array_elem(&codec->cvt_setups, i);
1258 if (!p->active && p->stream_tag == stream_tag)
1259 p->dirty = 1;
1260 }
1194} 1261}
1195EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream); 1262EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream);
1196 1263
@@ -1201,17 +1268,54 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream);
1201 */ 1268 */
1202void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid) 1269void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
1203{ 1270{
1271 struct hda_cvt_setup *p;
1272
1204 if (!nid) 1273 if (!nid)
1205 return; 1274 return;
1206 1275
1207 snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid); 1276 snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid);
1277 /* here we just clear the active flag; actual clean-ups will be done
1278 * in purify_inactive_streams()
1279 */
1280 p = get_hda_cvt_setup(codec, nid);
1281 if (p)
1282 p->active = 0;
1283}
1284EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup_stream);
1285
1286static void really_cleanup_stream(struct hda_codec *codec,
1287 struct hda_cvt_setup *q)
1288{
1289 hda_nid_t nid = q->nid;
1208 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0); 1290 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
1209#if 0 /* keep the format */
1210 msleep(1);
1211 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0); 1291 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0);
1212#endif 1292 memset(q, 0, sizeof(*q));
1293 q->nid = nid;
1294}
1295
1296/* clean up the all conflicting obsolete streams */
1297static void purify_inactive_streams(struct hda_codec *codec)
1298{
1299 int i;
1300
1301 for (i = 0; i < codec->cvt_setups.used; i++) {
1302 struct hda_cvt_setup *p = snd_array_elem(&codec->cvt_setups, i);
1303 if (p->dirty)
1304 really_cleanup_stream(codec, p);
1305 }
1306}
1307
1308/* clean up all streams; called from suspend */
1309static void hda_cleanup_all_streams(struct hda_codec *codec)
1310{
1311 int i;
1312
1313 for (i = 0; i < codec->cvt_setups.used; i++) {
1314 struct hda_cvt_setup *p = snd_array_elem(&codec->cvt_setups, i);
1315 if (p->stream_tag)
1316 really_cleanup_stream(codec, p);
1317 }
1213} 1318}
1214EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup_stream);
1215 1319
1216/* 1320/*
1217 * amp access functions 1321 * amp access functions
@@ -2928,6 +3032,7 @@ static void hda_call_codec_suspend(struct hda_codec *codec)
2928{ 3032{
2929 if (codec->patch_ops.suspend) 3033 if (codec->patch_ops.suspend)
2930 codec->patch_ops.suspend(codec, PMSG_SUSPEND); 3034 codec->patch_ops.suspend(codec, PMSG_SUSPEND);
3035 hda_cleanup_all_streams(codec);
2931 hda_set_power_state(codec, 3036 hda_set_power_state(codec,
2932 codec->afg ? codec->afg : codec->mfg, 3037 codec->afg ? codec->afg : codec->mfg,
2933 AC_PWRST_D3); 3038 AC_PWRST_D3);
@@ -3377,6 +3482,35 @@ static int set_pcm_default_values(struct hda_codec *codec,
3377 return 0; 3482 return 0;
3378} 3483}
3379 3484
3485/*
3486 * codec prepare/cleanup entries
3487 */
3488int snd_hda_codec_prepare(struct hda_codec *codec,
3489 struct hda_pcm_stream *hinfo,
3490 unsigned int stream,
3491 unsigned int format,
3492 struct snd_pcm_substream *substream)
3493{
3494 int ret;
3495 mutex_lock(&codec->prepare_mutex);
3496 ret = hinfo->ops.prepare(hinfo, codec, stream, format, substream);
3497 if (ret >= 0)
3498 purify_inactive_streams(codec);
3499 mutex_unlock(&codec->prepare_mutex);
3500 return ret;
3501}
3502EXPORT_SYMBOL_HDA(snd_hda_codec_prepare);
3503
3504void snd_hda_codec_cleanup(struct hda_codec *codec,
3505 struct hda_pcm_stream *hinfo,
3506 struct snd_pcm_substream *substream)
3507{
3508 mutex_lock(&codec->prepare_mutex);
3509 hinfo->ops.cleanup(hinfo, codec, substream);
3510 mutex_unlock(&codec->prepare_mutex);
3511}
3512EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup);
3513
3380/* global */ 3514/* global */
3381const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = { 3515const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = {
3382 "Audio", "SPDIF", "HDMI", "Modem" 3516 "Audio", "SPDIF", "HDMI", "Modem"
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 0328cf55cdba..3f7a479881e5 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -826,12 +826,14 @@ struct hda_codec {
826 826
827 struct mutex spdif_mutex; 827 struct mutex spdif_mutex;
828 struct mutex control_mutex; 828 struct mutex control_mutex;
829 struct mutex prepare_mutex;
829 unsigned int spdif_status; /* IEC958 status bits */ 830 unsigned int spdif_status; /* IEC958 status bits */
830 unsigned short spdif_ctls; /* SPDIF control bits */ 831 unsigned short spdif_ctls; /* SPDIF control bits */
831 unsigned int spdif_in_enable; /* SPDIF input enable? */ 832 unsigned int spdif_in_enable; /* SPDIF input enable? */
832 hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */ 833 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 init_pins; /* initial (BIOS) pin configurations */
834 struct snd_array driver_pins; /* pin configs set by codec parser */ 835 struct snd_array driver_pins; /* pin configs set by codec parser */
836 struct snd_array cvt_setups; /* audio convert setups */
835 837
836#ifdef CONFIG_SND_HDA_HWDEP 838#ifdef CONFIG_SND_HDA_HWDEP
837 struct snd_hwdep *hwdep; /* assigned hwdep device */ 839 struct snd_hwdep *hwdep; /* assigned hwdep device */
@@ -948,6 +950,16 @@ int snd_hda_codec_build_controls(struct hda_codec *codec);
948 */ 950 */
949int snd_hda_build_pcms(struct hda_bus *bus); 951int snd_hda_build_pcms(struct hda_bus *bus);
950int snd_hda_codec_build_pcms(struct hda_codec *codec); 952int snd_hda_codec_build_pcms(struct hda_codec *codec);
953
954int snd_hda_codec_prepare(struct hda_codec *codec,
955 struct hda_pcm_stream *hinfo,
956 unsigned int stream,
957 unsigned int format,
958 struct snd_pcm_substream *substream);
959void snd_hda_codec_cleanup(struct hda_codec *codec,
960 struct hda_pcm_stream *hinfo,
961 struct snd_pcm_substream *substream);
962
951void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, 963void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
952 u32 stream_tag, 964 u32 stream_tag,
953 int channel_id, int format); 965 int channel_id, int format);
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 66d420212d9a..1053fff4bd0a 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -1634,7 +1634,7 @@ static int azx_pcm_hw_free(struct snd_pcm_substream *substream)
1634 azx_dev->period_bytes = 0; 1634 azx_dev->period_bytes = 0;
1635 azx_dev->format_val = 0; 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 return snd_pcm_lib_free_pages(substream); 1639 return snd_pcm_lib_free_pages(substream);
1640} 1640}
@@ -1688,8 +1688,8 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
1688 else 1688 else
1689 azx_dev->fifo_size = 0; 1689 azx_dev->fifo_size = 0;
1690 1690
1691 return hinfo->ops.prepare(hinfo, apcm->codec, azx_dev->stream_tag, 1691 return snd_hda_codec_prepare(apcm->codec, hinfo, azx_dev->stream_tag,
1692 azx_dev->format_val, substream); 1692 azx_dev->format_val, substream);
1693} 1693}
1694 1694
1695static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 1695static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)