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>
This commit is contained in:
parent
b7756edeb7
commit
bcced70478
|
@ -29,6 +29,7 @@
|
||||||
#include <sound/hdaudio_ext.h>
|
#include <sound/hdaudio_ext.h>
|
||||||
#include <sound/hda_i915.h>
|
#include <sound/hda_i915.h>
|
||||||
#include <sound/pcm_drm_eld.h>
|
#include <sound/pcm_drm_eld.h>
|
||||||
|
#include <sound/hda_chmap.h>
|
||||||
#include "../../hda/local.h"
|
#include "../../hda/local.h"
|
||||||
#include "hdac_hdmi.h"
|
#include "hdac_hdmi.h"
|
||||||
|
|
||||||
|
@ -82,6 +83,10 @@ struct hdac_hdmi_pin {
|
||||||
struct hdac_ext_device *edev;
|
struct hdac_ext_device *edev;
|
||||||
int repoll_count;
|
int repoll_count;
|
||||||
struct delayed_work work;
|
struct delayed_work work;
|
||||||
|
struct mutex lock;
|
||||||
|
bool chmap_set;
|
||||||
|
unsigned char chmap[8]; /* ALSA API channel-map */
|
||||||
|
int channels; /* current number of channels */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct hdac_hdmi_pcm {
|
struct hdac_hdmi_pcm {
|
||||||
|
@ -106,6 +111,7 @@ struct hdac_hdmi_priv {
|
||||||
int num_pin;
|
int num_pin;
|
||||||
int num_cvt;
|
int num_cvt;
|
||||||
struct mutex pin_mutex;
|
struct mutex pin_mutex;
|
||||||
|
struct hdac_chmap chmap;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
|
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,
|
||||||
int i;
|
int i;
|
||||||
const u8 *eld_buf;
|
const u8 *eld_buf;
|
||||||
u8 conn_type;
|
u8 conn_type;
|
||||||
int channels = 2;
|
int channels, ca;
|
||||||
|
|
||||||
list_for_each_entry(pin, &hdmi->pin_list, head) {
|
list_for_each_entry(pin, &hdmi->pin_list, head) {
|
||||||
if (pin->nid == pin_nid)
|
if (pin->nid == pin_nid)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ca = snd_hdac_channel_allocation(&hdac->hdac, pin->eld.info.spk_alloc,
|
||||||
|
pin->channels, pin->chmap_set, true, pin->chmap);
|
||||||
|
|
||||||
|
channels = snd_hdac_get_active_channels(ca);
|
||||||
|
hdmi->chmap.ops.set_channel_count(&hdac->hdac, cvt_nid, channels);
|
||||||
|
|
||||||
|
snd_hdac_setup_channel_mapping(&hdmi->chmap, pin->nid, false, ca,
|
||||||
|
pin->channels, pin->chmap, pin->chmap_set);
|
||||||
|
|
||||||
eld_buf = pin->eld.eld_buffer;
|
eld_buf = pin->eld.eld_buffer;
|
||||||
conn_type = drm_eld_get_conn_type(eld_buf);
|
conn_type = drm_eld_get_conn_type(eld_buf);
|
||||||
|
|
||||||
/* setup channel count */
|
|
||||||
snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
|
|
||||||
AC_VERB_SET_CVT_CHAN_COUNT, channels - 1);
|
|
||||||
|
|
||||||
switch (conn_type) {
|
switch (conn_type) {
|
||||||
case DRM_ELD_CONN_TYPE_HDMI:
|
case DRM_ELD_CONN_TYPE_HDMI:
|
||||||
hdmi_audio_infoframe_init(&frame);
|
hdmi_audio_infoframe_init(&frame);
|
||||||
|
|
||||||
/* Default stereo for now */
|
|
||||||
frame.channels = channels;
|
frame.channels = channels;
|
||||||
|
frame.channel_allocation = ca;
|
||||||
|
|
||||||
ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
|
ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
@ -317,7 +328,7 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,
|
||||||
dp_ai.len = 0x1b;
|
dp_ai.len = 0x1b;
|
||||||
dp_ai.ver = 0x11 << 2;
|
dp_ai.ver = 0x11 << 2;
|
||||||
dp_ai.CC02_CT47 = channels - 1;
|
dp_ai.CC02_CT47 = channels - 1;
|
||||||
dp_ai.CA = 0;
|
dp_ai.CA = ca;
|
||||||
|
|
||||||
dip = (u8 *)&dp_ai;
|
dip = (u8 *)&dp_ai;
|
||||||
break;
|
break;
|
||||||
|
@ -376,17 +387,23 @@ static int hdac_hdmi_playback_prepare(struct snd_pcm_substream *substream,
|
||||||
struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
|
struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
|
||||||
struct hdac_hdmi_priv *hdmi = hdac->private_data;
|
struct hdac_hdmi_priv *hdmi = hdac->private_data;
|
||||||
struct hdac_hdmi_dai_pin_map *dai_map;
|
struct hdac_hdmi_dai_pin_map *dai_map;
|
||||||
|
struct hdac_hdmi_pin *pin;
|
||||||
struct hdac_ext_dma_params *dd;
|
struct hdac_ext_dma_params *dd;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
dai_map = &hdmi->dai_map[dai->id];
|
dai_map = &hdmi->dai_map[dai->id];
|
||||||
|
pin = dai_map->pin;
|
||||||
|
|
||||||
dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
|
dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
|
||||||
dev_dbg(&hdac->hdac.dev, "stream tag from cpu dai %d format in cvt 0x%x\n",
|
dev_dbg(&hdac->hdac.dev, "stream tag from cpu dai %d format in cvt 0x%x\n",
|
||||||
dd->stream_tag, dd->format);
|
dd->stream_tag, dd->format);
|
||||||
|
|
||||||
|
mutex_lock(&pin->lock);
|
||||||
|
pin->channels = substream->runtime->channels;
|
||||||
|
|
||||||
ret = hdac_hdmi_setup_audio_infoframe(hdac, dai_map->cvt->nid,
|
ret = hdac_hdmi_setup_audio_infoframe(hdac, dai_map->cvt->nid,
|
||||||
dai_map->pin->nid);
|
dai_map->pin->nid);
|
||||||
|
mutex_unlock(&pin->lock);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -646,6 +663,10 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
|
||||||
snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
|
snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
|
||||||
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
|
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
|
||||||
|
|
||||||
|
mutex_lock(&dai_map->pin->lock);
|
||||||
|
dai_map->pin->channels = 0;
|
||||||
|
mutex_unlock(&dai_map->pin->lock);
|
||||||
|
|
||||||
dai_map->pin = NULL;
|
dai_map->pin = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -653,10 +674,19 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
|
||||||
static int
|
static int
|
||||||
hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt)
|
hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt)
|
||||||
{
|
{
|
||||||
|
unsigned int chans;
|
||||||
|
struct hdac_ext_device *edev = to_ehdac_device(hdac);
|
||||||
|
struct hdac_hdmi_priv *hdmi = edev->private_data;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
/* Only stereo supported as of now */
|
chans = get_wcaps(hdac, cvt->nid);
|
||||||
cvt->params.channels_min = cvt->params.channels_max = 2;
|
chans = get_wcaps_channels(chans);
|
||||||
|
|
||||||
|
cvt->params.channels_min = 2;
|
||||||
|
|
||||||
|
cvt->params.channels_max = chans;
|
||||||
|
if (chans > hdmi->chmap.channels_max)
|
||||||
|
hdmi->chmap.channels_max = chans;
|
||||||
|
|
||||||
err = snd_hdac_query_supported_pcm(hdac, cvt->nid,
|
err = snd_hdac_query_supported_pcm(hdac, cvt->nid,
|
||||||
&cvt->params.rates,
|
&cvt->params.rates,
|
||||||
|
@ -1136,6 +1166,7 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
|
||||||
hdmi->num_pin++;
|
hdmi->num_pin++;
|
||||||
|
|
||||||
pin->edev = edev;
|
pin->edev = edev;
|
||||||
|
mutex_init(&pin->lock);
|
||||||
INIT_DELAYED_WORK(&pin->work, hdac_hdmi_repoll_eld);
|
INIT_DELAYED_WORK(&pin->work, hdac_hdmi_repoll_eld);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1506,6 +1537,7 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
edev->private_data = hdmi_priv;
|
edev->private_data = hdmi_priv;
|
||||||
|
snd_hdac_register_chmap_ops(codec, &hdmi_priv->chmap);
|
||||||
|
|
||||||
dev_set_drvdata(&codec->dev, edev);
|
dev_set_drvdata(&codec->dev, edev);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue