diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/pci/hda/hda_codec.c | 150 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.h | 12 | ||||
-rw-r--r-- | sound/pci/hda/hda_intel.c | 6 |
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 | */ | ||
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 | * codec destructor | 1004 | * codec destructor |
975 | */ | 1005 | */ |
976 | static void snd_hda_codec_free(struct hda_codec *codec) | 1006 | static 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 | } |
1195 | EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream); | 1262 | EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream); |
1196 | 1263 | ||
@@ -1201,17 +1268,54 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream); | |||
1201 | */ | 1268 | */ |
1202 | void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid) | 1269 | void 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 | } | ||
1284 | EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup_stream); | ||
1285 | |||
1286 | static 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 */ | ||
1297 | static 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 */ | ||
1309 | static 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 | } |
1214 | EXPORT_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 | */ | ||
3488 | int 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 | } | ||
3502 | EXPORT_SYMBOL_HDA(snd_hda_codec_prepare); | ||
3503 | |||
3504 | void 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 | } | ||
3512 | EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup); | ||
3513 | |||
3380 | /* global */ | 3514 | /* global */ |
3381 | const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = { | 3515 | const 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 | */ |
949 | int snd_hda_build_pcms(struct hda_bus *bus); | 951 | int snd_hda_build_pcms(struct hda_bus *bus); |
950 | int snd_hda_codec_build_pcms(struct hda_codec *codec); | 952 | int snd_hda_codec_build_pcms(struct hda_codec *codec); |
953 | |||
954 | int 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); | ||
959 | void snd_hda_codec_cleanup(struct hda_codec *codec, | ||
960 | struct hda_pcm_stream *hinfo, | ||
961 | struct snd_pcm_substream *substream); | ||
962 | |||
951 | void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, | 963 | void 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 | ||
1695 | static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | 1695 | static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) |