aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/hda
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2010-08-13 02:45:23 -0400
committerTakashi Iwai <tiwai@suse.de>2010-08-13 02:45:23 -0400
commitbbbe33900d1f3c4402148ccb85234a741a6606a3 (patch)
tree294143653bfb5a045dea0396b8fee00ed7164b6e /sound/pci/hda
parent8a345a042ae75097fd493633633382644257cfc3 (diff)
ALSA: hda - Restrict PCM parameters per ELD information over HDMI
When a device is plugged over HDMI, it passes some information in ELD including the supported PCM parameters like formats, rates, channels. This patch adds the check to PCM open callback of HDMI streams so that only valid parameters the device supports are used. When no device is plugged, the parameters the codec supports are used; it's mostly all parameters the hardware can work. This is for apps that are started before device plugging and do probing (e.g. a sound daemon), so that at least, probing would work even before the device plugging. Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda')
-rw-r--r--sound/pci/hda/hda_eld.c49
-rw-r--r--sound/pci/hda/hda_local.h2
-rw-r--r--sound/pci/hda/patch_hdmi.c42
-rw-r--r--sound/pci/hda/patch_intelhdmi.c1
-rw-r--r--sound/pci/hda/patch_nvhdmi.c4
5 files changed, 95 insertions, 3 deletions
diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c
index d8da18a9e98b..803b298f7411 100644
--- a/sound/pci/hda/hda_eld.c
+++ b/sound/pci/hda/hda_eld.c
@@ -596,4 +596,53 @@ void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
596} 596}
597EXPORT_SYMBOL_HDA(snd_hda_eld_proc_free); 597EXPORT_SYMBOL_HDA(snd_hda_eld_proc_free);
598 598
599/* update PCM info based on ELD */
600void hdmi_eld_update_pcm_info(struct hdmi_eld *eld, struct hda_pcm_stream *pcm,
601 struct hda_pcm_stream *codec_pars)
602{
603 int i;
604
605 pcm->rates = 0;
606 pcm->formats = 0;
607 pcm->maxbps = 0;
608 pcm->channels_min = -1;
609 pcm->channels_max = 0;
610 for (i = 0; i < eld->sad_count; i++) {
611 struct cea_sad *a = &eld->sad[i];
612 pcm->rates |= a->rates;
613 if (a->channels < pcm->channels_min)
614 pcm->channels_min = a->channels;
615 if (a->channels > pcm->channels_max)
616 pcm->channels_max = a->channels;
617 if (a->format == AUDIO_CODING_TYPE_LPCM) {
618 if (a->sample_bits & AC_SUPPCM_BITS_16) {
619 pcm->formats |= SNDRV_PCM_FMTBIT_S16_LE;
620 if (pcm->maxbps < 16)
621 pcm->maxbps = 16;
622 }
623 if (a->sample_bits & AC_SUPPCM_BITS_20) {
624 pcm->formats |= SNDRV_PCM_FMTBIT_S32_LE;
625 if (pcm->maxbps < 20)
626 pcm->maxbps = 20;
627 }
628 if (a->sample_bits & AC_SUPPCM_BITS_24) {
629 pcm->formats |= SNDRV_PCM_FMTBIT_S32_LE;
630 if (pcm->maxbps < 24)
631 pcm->maxbps = 24;
632 }
633 }
634 }
635
636 if (!codec_pars)
637 return;
638
639 /* restrict the parameters by the values the codec provides */
640 pcm->rates &= codec_pars->rates;
641 pcm->formats &= codec_pars->formats;
642 pcm->channels_min = max(pcm->channels_min, codec_pars->channels_min);
643 pcm->channels_max = min(pcm->channels_max, codec_pars->channels_max);
644 pcm->maxbps = min(pcm->maxbps, codec_pars->maxbps);
645}
646EXPORT_SYMBOL_HDA(hdmi_eld_update_pcm_info);
647
599#endif /* CONFIG_PROC_FS */ 648#endif /* CONFIG_PROC_FS */
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 7a97f126f6f7..28ab4aead48f 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -604,6 +604,8 @@ struct hdmi_eld {
604int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid); 604int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid);
605int snd_hdmi_get_eld(struct hdmi_eld *, struct hda_codec *, hda_nid_t); 605int snd_hdmi_get_eld(struct hdmi_eld *, struct hda_codec *, hda_nid_t);
606void snd_hdmi_show_eld(struct hdmi_eld *eld); 606void snd_hdmi_show_eld(struct hdmi_eld *eld);
607void hdmi_eld_update_pcm_info(struct hdmi_eld *eld, struct hda_pcm_stream *pcm,
608 struct hda_pcm_stream *codec_pars);
607 609
608#ifdef CONFIG_PROC_FS 610#ifdef CONFIG_PROC_FS
609int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld, 611int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 522e0748ee99..2bc0f07cf33f 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -46,6 +46,7 @@ struct hdmi_spec {
46 * export one pcm per pipe 46 * export one pcm per pipe
47 */ 47 */
48 struct hda_pcm pcm_rec[MAX_HDMI_CVTS]; 48 struct hda_pcm pcm_rec[MAX_HDMI_CVTS];
49 struct hda_pcm_stream codec_pcm_pars[MAX_HDMI_CVTS];
49 50
50 /* 51 /*
51 * nvhdmi specific 52 * nvhdmi specific
@@ -766,6 +767,47 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid,
766} 767}
767 768
768/* 769/*
770 * HDA PCM callbacks
771 */
772static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
773 struct hda_codec *codec,
774 struct snd_pcm_substream *substream)
775{
776 struct hdmi_spec *spec = codec->spec;
777 struct hdmi_eld *eld;
778 struct hda_pcm_stream *codec_pars;
779 unsigned int idx;
780
781 for (idx = 0; idx < spec->num_cvts; idx++)
782 if (hinfo->nid == spec->cvt[idx])
783 break;
784 if (snd_BUG_ON(idx >= spec->num_cvts) ||
785 snd_BUG_ON(idx >= spec->num_pins))
786 return -EINVAL;
787
788 /* save the PCM info the codec provides */
789 codec_pars = &spec->codec_pcm_pars[idx];
790 if (!codec_pars->rates)
791 *codec_pars = *hinfo;
792
793 eld = &spec->sink_eld[idx];
794 if (eld->sad_count > 0) {
795 hdmi_eld_update_pcm_info(eld, hinfo, codec_pars);
796 if (hinfo->channels_min > hinfo->channels_max ||
797 !hinfo->rates || !hinfo->formats)
798 return -ENODEV;
799 } else {
800 /* fallback to the codec default */
801 hinfo->channels_min = codec_pars->channels_min;
802 hinfo->channels_max = codec_pars->channels_max;
803 hinfo->rates = codec_pars->rates;
804 hinfo->formats = codec_pars->formats;
805 hinfo->maxbps = codec_pars->maxbps;
806 }
807 return 0;
808}
809
810/*
769 * HDA/HDMI auto parsing 811 * HDA/HDMI auto parsing
770 */ 812 */
771 813
diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c
index 5972d5e7d01f..d382d3c81c0f 100644
--- a/sound/pci/hda/patch_intelhdmi.c
+++ b/sound/pci/hda/patch_intelhdmi.c
@@ -80,6 +80,7 @@ static struct hda_pcm_stream intel_hdmi_pcm_playback = {
80 .substreams = 1, 80 .substreams = 1,
81 .channels_min = 2, 81 .channels_min = 2,
82 .ops = { 82 .ops = {
83 .open = hdmi_pcm_open,
83 .prepare = intel_hdmi_playback_pcm_prepare, 84 .prepare = intel_hdmi_playback_pcm_prepare,
84 .cleanup = intel_hdmi_playback_pcm_cleanup, 85 .cleanup = intel_hdmi_playback_pcm_cleanup,
85 }, 86 },
diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c
index 77e2b4028b9f..f636870dc718 100644
--- a/sound/pci/hda/patch_nvhdmi.c
+++ b/sound/pci/hda/patch_nvhdmi.c
@@ -347,10 +347,8 @@ static int nvhdmi_dig_playback_pcm_prepare_2ch(struct hda_pcm_stream *hinfo,
347static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch_89 = { 347static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch_89 = {
348 .substreams = 1, 348 .substreams = 1,
349 .channels_min = 2, 349 .channels_min = 2,
350 .rates = SUPPORTED_RATES,
351 .maxbps = SUPPORTED_MAXBPS,
352 .formats = SUPPORTED_FORMATS,
353 .ops = { 350 .ops = {
351 .open = hdmi_pcm_open,
354 .prepare = nvhdmi_dig_playback_pcm_prepare_8ch_89, 352 .prepare = nvhdmi_dig_playback_pcm_prepare_8ch_89,
355 .cleanup = nvhdmi_playback_pcm_cleanup, 353 .cleanup = nvhdmi_playback_pcm_cleanup,
356 }, 354 },