aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
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)