diff options
| author | Subhransu S. Prusty <subhransu.s.prusty@intel.com> | 2016-04-14 00:37:30 -0400 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2016-04-28 13:49:24 -0400 |
| commit | bcced704788312360c0413d13b11611ae00a91c8 (patch) | |
| tree | a9bea7d9bd5bdf539707cbcf4da2f8c17e7d8f5d | |
| parent | b7756edeb7d03b675e10b4862dccc8deb4b0ca17 (diff) | |
ASoC: hdac_hdmi: Add multichannel support
To support multichannel hdac hdmi driver registers with HDA
channel map framework. Channel count and channel slot verbs are
programmed by using the chmap helpers/ops. The channel
allocation is then programmed in the audio infoframe as per CEA
spec.
Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
| -rw-r--r-- | sound/soc/codecs/hdac_hdmi.c | 50 |
1 files changed, 41 insertions, 9 deletions
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 64ffe93b0f7b..034593bf2cd6 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c | |||
| @@ -29,6 +29,7 @@ | |||
| 29 | #include <sound/hdaudio_ext.h> | 29 | #include <sound/hdaudio_ext.h> |
| 30 | #include <sound/hda_i915.h> | 30 | #include <sound/hda_i915.h> |
| 31 | #include <sound/pcm_drm_eld.h> | 31 | #include <sound/pcm_drm_eld.h> |
| 32 | #include <sound/hda_chmap.h> | ||
| 32 | #include "../../hda/local.h" | 33 | #include "../../hda/local.h" |
| 33 | #include "hdac_hdmi.h" | 34 | #include "hdac_hdmi.h" |
| 34 | 35 | ||
| @@ -82,6 +83,10 @@ struct hdac_hdmi_pin { | |||
| 82 | struct hdac_ext_device *edev; | 83 | struct hdac_ext_device *edev; |
| 83 | int repoll_count; | 84 | int repoll_count; |
| 84 | struct delayed_work work; | 85 | struct delayed_work work; |
| 86 | struct mutex lock; | ||
| 87 | bool chmap_set; | ||
| 88 | unsigned char chmap[8]; /* ALSA API channel-map */ | ||
| 89 | int channels; /* current number of channels */ | ||
| 85 | }; | 90 | }; |
| 86 | 91 | ||
| 87 | struct hdac_hdmi_pcm { | 92 | struct hdac_hdmi_pcm { |
| @@ -106,6 +111,7 @@ struct hdac_hdmi_priv { | |||
| 106 | int num_pin; | 111 | int num_pin; |
| 107 | int num_cvt; | 112 | int num_cvt; |
| 108 | struct mutex pin_mutex; | 113 | struct mutex pin_mutex; |
| 114 | struct hdac_chmap chmap; | ||
| 109 | }; | 115 | }; |
| 110 | 116 | ||
| 111 | static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev) | 117 | static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev) |
| @@ -284,26 +290,31 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac, | |||
| 284 | int i; | 290 | int i; |
| 285 | const u8 *eld_buf; | 291 | const u8 *eld_buf; |
| 286 | u8 conn_type; | 292 | u8 conn_type; |
| 287 | int channels = 2; | 293 | int channels, ca; |
| 288 | 294 | ||
| 289 | list_for_each_entry(pin, &hdmi->pin_list, head) { | 295 | list_for_each_entry(pin, &hdmi->pin_list, head) { |
| 290 | if (pin->nid == pin_nid) | 296 | if (pin->nid == pin_nid) |
| 291 | break; | 297 | break; |
| 292 | } | 298 | } |
| 293 | 299 | ||
| 300 | ca = snd_hdac_channel_allocation(&hdac->hdac, pin->eld.info.spk_alloc, | ||
| 301 | pin->channels, pin->chmap_set, true, pin->chmap); | ||
| 302 | |||
| 303 | channels = snd_hdac_get_active_channels(ca); | ||
| 304 | hdmi->chmap.ops.set_channel_count(&hdac->hdac, cvt_nid, channels); | ||
| 305 | |||
| 306 | snd_hdac_setup_channel_mapping(&hdmi->chmap, pin->nid, false, ca, | ||
| 307 | pin->channels, pin->chmap, pin->chmap_set); | ||
| 308 | |||
| 294 | eld_buf = pin->eld.eld_buffer; | 309 | eld_buf = pin->eld.eld_buffer; |
| 295 | conn_type = drm_eld_get_conn_type(eld_buf); | 310 | conn_type = drm_eld_get_conn_type(eld_buf); |
| 296 | 311 | ||
| 297 | /* setup channel count */ | ||
| 298 | snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0, | ||
| 299 | AC_VERB_SET_CVT_CHAN_COUNT, channels - 1); | ||
| 300 | |||
| 301 | switch (conn_type) { | 312 | switch (conn_type) { |
| 302 | case DRM_ELD_CONN_TYPE_HDMI: | 313 | case DRM_ELD_CONN_TYPE_HDMI: |
| 303 | hdmi_audio_infoframe_init(&frame); | 314 | hdmi_audio_infoframe_init(&frame); |
| 304 | 315 | ||
| 305 | /* Default stereo for now */ | ||
| 306 | frame.channels = channels; | 316 | frame.channels = channels; |
| 317 | frame.channel_allocation = ca; | ||
| 307 | 318 | ||
| 308 | ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); | 319 | ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); |
| 309 | if (ret < 0) | 320 | if (ret < 0) |
| @@ -317,7 +328,7 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac, | |||
| 317 | dp_ai.len = 0x1b; | 328 | dp_ai.len = 0x1b; |
| 318 | dp_ai.ver = 0x11 << 2; | 329 | dp_ai.ver = 0x11 << 2; |
| 319 | dp_ai.CC02_CT47 = channels - 1; | 330 | dp_ai.CC02_CT47 = channels - 1; |
| 320 | dp_ai.CA = 0; | 331 | dp_ai.CA = ca; |
| 321 | 332 | ||
| 322 | dip = (u8 *)&dp_ai; | 333 | dip = (u8 *)&dp_ai; |
| 323 | break; | 334 | break; |
| @@ -376,17 +387,23 @@ static int hdac_hdmi_playback_prepare(struct snd_pcm_substream *substream, | |||
| 376 | struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai); | 387 | struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai); |
| 377 | struct hdac_hdmi_priv *hdmi = hdac->private_data; | 388 | struct hdac_hdmi_priv *hdmi = hdac->private_data; |
| 378 | struct hdac_hdmi_dai_pin_map *dai_map; | 389 | struct hdac_hdmi_dai_pin_map *dai_map; |
| 390 | struct hdac_hdmi_pin *pin; | ||
| 379 | struct hdac_ext_dma_params *dd; | 391 | struct hdac_ext_dma_params *dd; |
| 380 | int ret; | 392 | int ret; |
| 381 | 393 | ||
| 382 | dai_map = &hdmi->dai_map[dai->id]; | 394 | dai_map = &hdmi->dai_map[dai->id]; |
| 395 | pin = dai_map->pin; | ||
| 383 | 396 | ||
| 384 | dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream); | 397 | dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream); |
| 385 | dev_dbg(&hdac->hdac.dev, "stream tag from cpu dai %d format in cvt 0x%x\n", | 398 | dev_dbg(&hdac->hdac.dev, "stream tag from cpu dai %d format in cvt 0x%x\n", |
| 386 | dd->stream_tag, dd->format); | 399 | dd->stream_tag, dd->format); |
| 387 | 400 | ||
| 401 | mutex_lock(&pin->lock); | ||
| 402 | pin->channels = substream->runtime->channels; | ||
| 403 | |||
| 388 | ret = hdac_hdmi_setup_audio_infoframe(hdac, dai_map->cvt->nid, | 404 | ret = hdac_hdmi_setup_audio_infoframe(hdac, dai_map->cvt->nid, |
| 389 | dai_map->pin->nid); | 405 | dai_map->pin->nid); |
| 406 | mutex_unlock(&pin->lock); | ||
| 390 | if (ret < 0) | 407 | if (ret < 0) |
| 391 | return ret; | 408 | return ret; |
| 392 | 409 | ||
| @@ -646,6 +663,10 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, | |||
| 646 | snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0, | 663 | snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0, |
| 647 | AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); | 664 | AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); |
| 648 | 665 | ||
| 666 | mutex_lock(&dai_map->pin->lock); | ||
| 667 | dai_map->pin->channels = 0; | ||
| 668 | mutex_unlock(&dai_map->pin->lock); | ||
| 669 | |||
| 649 | dai_map->pin = NULL; | 670 | dai_map->pin = NULL; |
| 650 | } | 671 | } |
| 651 | } | 672 | } |
| @@ -653,10 +674,19 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, | |||
| 653 | static int | 674 | static int |
| 654 | hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt) | 675 | hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt) |
| 655 | { | 676 | { |
| 677 | unsigned int chans; | ||
| 678 | struct hdac_ext_device *edev = to_ehdac_device(hdac); | ||
| 679 | struct hdac_hdmi_priv *hdmi = edev->private_data; | ||
| 656 | int err; | 680 | int err; |
| 657 | 681 | ||
| 658 | /* Only stereo supported as of now */ | 682 | chans = get_wcaps(hdac, cvt->nid); |
| 659 | cvt->params.channels_min = cvt->params.channels_max = 2; | 683 | chans = get_wcaps_channels(chans); |
| 684 | |||
| 685 | cvt->params.channels_min = 2; | ||
| 686 | |||
| 687 | cvt->params.channels_max = chans; | ||
| 688 | if (chans > hdmi->chmap.channels_max) | ||
| 689 | hdmi->chmap.channels_max = chans; | ||
| 660 | 690 | ||
| 661 | err = snd_hdac_query_supported_pcm(hdac, cvt->nid, | 691 | err = snd_hdac_query_supported_pcm(hdac, cvt->nid, |
| 662 | &cvt->params.rates, | 692 | &cvt->params.rates, |
| @@ -1136,6 +1166,7 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid) | |||
| 1136 | hdmi->num_pin++; | 1166 | hdmi->num_pin++; |
| 1137 | 1167 | ||
| 1138 | pin->edev = edev; | 1168 | pin->edev = edev; |
| 1169 | mutex_init(&pin->lock); | ||
| 1139 | INIT_DELAYED_WORK(&pin->work, hdac_hdmi_repoll_eld); | 1170 | INIT_DELAYED_WORK(&pin->work, hdac_hdmi_repoll_eld); |
| 1140 | 1171 | ||
| 1141 | return 0; | 1172 | return 0; |
| @@ -1506,6 +1537,7 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) | |||
| 1506 | return -ENOMEM; | 1537 | return -ENOMEM; |
| 1507 | 1538 | ||
| 1508 | edev->private_data = hdmi_priv; | 1539 | edev->private_data = hdmi_priv; |
| 1540 | snd_hdac_register_chmap_ops(codec, &hdmi_priv->chmap); | ||
| 1509 | 1541 | ||
| 1510 | dev_set_drvdata(&codec->dev, edev); | 1542 | dev_set_drvdata(&codec->dev, edev); |
| 1511 | 1543 | ||
