Commit c9bfb5d7 authored by Jeeja KP's avatar Jeeja KP Committed by Mark Brown

ASoC: hdac_hdmi: Register widget event handlers

In case of hdmi connect/disconnect or when stream need to be route to
multiple monitors, corresponding port and audio infoframe needs to be
reconfigured. Currently all the configuration are done in DAI ops which
results in silence playback.

So use dapm widget event handlers to program audio infoframe and enable
/disable port configuration when widget is power on/off.
Signed-off-by: default avatarJeeja KP <jeeja.kp@intel.com>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent dbbc21bb
...@@ -97,6 +97,9 @@ struct hdac_hdmi_pcm { ...@@ -97,6 +97,9 @@ struct hdac_hdmi_pcm {
struct hdac_hdmi_pin *pin; struct hdac_hdmi_pin *pin;
struct hdac_hdmi_cvt *cvt; struct hdac_hdmi_cvt *cvt;
struct snd_jack *jack; struct snd_jack *jack;
int stream_tag;
int channels;
int format;
}; };
struct hdac_hdmi_dai_pin_map { struct hdac_hdmi_dai_pin_map {
...@@ -116,11 +119,19 @@ struct hdac_hdmi_priv { ...@@ -116,11 +119,19 @@ struct hdac_hdmi_priv {
struct hdac_chmap chmap; struct hdac_chmap chmap;
}; };
static void hdac_hdmi_enable_cvt(struct hdac_ext_device *edev, static struct hdac_hdmi_pcm *
struct hdac_hdmi_dai_pin_map *dai_map); hdac_hdmi_get_pcm_from_cvt(struct hdac_hdmi_priv *hdmi,
struct hdac_hdmi_cvt *cvt)
{
struct hdac_hdmi_pcm *pcm = NULL;
list_for_each_entry(pcm, &hdmi->pcm_list, head) {
if (pcm->cvt == cvt)
break;
}
static int hdac_hdmi_enable_pin(struct hdac_ext_device *hdac, return pcm;
struct hdac_hdmi_dai_pin_map *dai_map); }
static struct hdac_hdmi_pcm *get_hdmi_pcm_from_id(struct hdac_hdmi_priv *hdmi, static struct hdac_hdmi_pcm *get_hdmi_pcm_from_id(struct hdac_hdmi_priv *hdmi,
int pcm_idx) int pcm_idx)
...@@ -181,25 +192,6 @@ static int hdac_hdmi_eld_limit_formats(struct snd_pcm_runtime *runtime, ...@@ -181,25 +192,6 @@ static int hdac_hdmi_eld_limit_formats(struct snd_pcm_runtime *runtime,
} }
static int hdac_hdmi_setup_stream(struct hdac_ext_device *hdac,
hda_nid_t cvt_nid, hda_nid_t pin_nid,
u32 stream_tag, int format)
{
unsigned int val;
dev_dbg(&hdac->hdac.dev, "cvt nid %d pnid %d stream %d format 0x%x\n",
cvt_nid, pin_nid, stream_tag, format);
val = (stream_tag << 4);
snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
AC_VERB_SET_CHANNEL_STREAMID, val);
snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
AC_VERB_SET_STREAM_FORMAT, format);
return 0;
}
static void static void
hdac_hdmi_set_dip_index(struct hdac_ext_device *hdac, hda_nid_t pin_nid, hdac_hdmi_set_dip_index(struct hdac_ext_device *hdac, hda_nid_t pin_nid,
int packet_index, int byte_index) int packet_index, int byte_index)
...@@ -312,54 +304,25 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac, ...@@ -312,54 +304,25 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,
return 0; return 0;
} }
static void hdac_hdmi_set_power_state(struct hdac_ext_device *edev, static int hdac_hdmi_set_tdm_slot(struct snd_soc_dai *dai,
struct hdac_hdmi_dai_pin_map *dai_map, unsigned int pwr_state) unsigned int tx_mask, unsigned int rx_mask,
int slots, int slot_width)
{ {
/* Power up pin widget */ struct hdac_ext_device *edev = snd_soc_dai_get_drvdata(dai);
if (!snd_hdac_check_power_state(&edev->hdac, dai_map->pin->nid, struct hdac_hdmi_priv *hdmi = edev->private_data;
pwr_state))
snd_hdac_codec_write(&edev->hdac, dai_map->pin->nid, 0,
AC_VERB_SET_POWER_STATE, pwr_state);
/* Power up converter */
if (!snd_hdac_check_power_state(&edev->hdac, dai_map->cvt->nid,
pwr_state))
snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
AC_VERB_SET_POWER_STATE, pwr_state);
}
static int hdac_hdmi_playback_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
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_hdmi_pcm *pcm;
struct hdac_ext_dma_params *dd;
int ret; dev_dbg(&edev->hdac.dev, "%s: strm_tag: %d\n", __func__, tx_mask);
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); pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt);
dev_dbg(&hdac->hdac.dev, "stream tag from cpu dai %d format in cvt 0x%x\n",
dd->stream_tag, dd->format);
hdac_hdmi_enable_cvt(hdac, dai_map); if (pcm)
ret = hdac_hdmi_enable_pin(hdac, dai_map); pcm->stream_tag = (tx_mask << 4);
if (ret < 0)
return ret;
mutex_lock(&pin->lock);
pin->channels = substream->runtime->channels;
ret = hdac_hdmi_setup_audio_infoframe(hdac, dai_map->cvt->nid, return 0;
dai_map->pin->nid);
mutex_unlock(&pin->lock);
if (ret < 0)
return ret;
return hdac_hdmi_setup_stream(hdac, dai_map->cvt->nid,
dai_map->pin->nid, dd->stream_tag, dd->format);
} }
static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream, static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,
...@@ -369,7 +332,8 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream, ...@@ -369,7 +332,8 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,
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_hdmi_pin *pin;
struct hdac_ext_dma_params *dd; struct hdac_hdmi_pcm *pcm;
int format;
dai_map = &hdmi->dai_map[dai->id]; dai_map = &hdmi->dai_map[dai->id];
pin = dai_map->pin; pin = dai_map->pin;
...@@ -383,74 +347,16 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream, ...@@ -383,74 +347,16 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,
return -ENODEV; return -ENODEV;
} }
dd = snd_soc_dai_get_dma_data(dai, substream); format = snd_hdac_calc_stream_format(params_rate(hparams),
if (!dd) {
dd = kzalloc(sizeof(*dd), GFP_KERNEL);
if (!dd)
return -ENOMEM;
}
dd->format = snd_hdac_calc_stream_format(params_rate(hparams),
params_channels(hparams), params_format(hparams), params_channels(hparams), params_format(hparams),
24, 0); 24, 0);
snd_soc_dai_set_dma_data(dai, substream, (void *)dd); pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt);
if (!pcm)
return 0;
}
static int hdac_hdmi_playback_cleanup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct hdac_ext_dma_params *dd;
dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
if (dd) {
snd_soc_dai_set_dma_data(dai, substream, NULL);
kfree(dd);
}
return 0;
}
static void hdac_hdmi_enable_cvt(struct hdac_ext_device *edev,
struct hdac_hdmi_dai_pin_map *dai_map)
{
/* Enable transmission */
snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
AC_VERB_SET_DIGI_CONVERT_1, 1);
/* Category Code (CC) to zero */
snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
AC_VERB_SET_DIGI_CONVERT_2, 0);
}
static int hdac_hdmi_enable_pin(struct hdac_ext_device *hdac,
struct hdac_hdmi_dai_pin_map *dai_map)
{
int mux_idx;
struct hdac_hdmi_pin *pin = dai_map->pin;
for (mux_idx = 0; mux_idx < pin->num_mux_nids; mux_idx++) {
if (pin->mux_nids[mux_idx] == dai_map->cvt->nid) {
snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
AC_VERB_SET_CONNECT_SEL, mux_idx);
break;
}
}
if (mux_idx == pin->num_mux_nids)
return -EIO; return -EIO;
/* Enable out path for this pin widget */ pcm->format = format;
snd_hdac_codec_write(&hdac->hdac, pin->nid, 0, pcm->channels = params_channels(hparams);
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D0);
snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
return 0; return 0;
} }
...@@ -564,23 +470,6 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, ...@@ -564,23 +470,6 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
pin->eld.eld_buffer); pin->eld.eld_buffer);
} }
static int hdac_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
switch (cmd) {
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
return hdac_hdmi_playback_prepare(substream, dai);
default:
return 0;
}
return 0;
}
static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
...@@ -591,16 +480,6 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, ...@@ -591,16 +480,6 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
dai_map = &hdmi->dai_map[dai->id]; dai_map = &hdmi->dai_map[dai->id];
if (dai_map->pin) { if (dai_map->pin) {
snd_hdac_codec_write(&hdac->hdac, dai_map->cvt->nid, 0,
AC_VERB_SET_CHANNEL_STREAMID, 0);
snd_hdac_codec_write(&hdac->hdac, dai_map->cvt->nid, 0,
AC_VERB_SET_STREAM_FORMAT, 0);
hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D3);
snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
mutex_lock(&dai_map->pin->lock); mutex_lock(&dai_map->pin->lock);
dai_map->pin->chmap_set = false; dai_map->pin->chmap_set = false;
memset(dai_map->pin->chmap, 0, sizeof(dai_map->pin->chmap)); memset(dai_map->pin->chmap, 0, sizeof(dai_map->pin->chmap));
...@@ -641,10 +520,11 @@ hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt) ...@@ -641,10 +520,11 @@ hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt)
} }
static int hdac_hdmi_fill_widget_info(struct device *dev, static int hdac_hdmi_fill_widget_info(struct device *dev,
struct snd_soc_dapm_widget *w, struct snd_soc_dapm_widget *w, enum snd_soc_dapm_type id,
enum snd_soc_dapm_type id, void *priv, void *priv, const char *wname, const char *stream,
const char *wname, const char *stream, struct snd_kcontrol_new *wc, int numkc,
struct snd_kcontrol_new *wc, int numkc) int (*event)(struct snd_soc_dapm_widget *,
struct snd_kcontrol *, int), unsigned short event_flags)
{ {
w->id = id; w->id = id;
w->name = devm_kstrdup(dev, wname, GFP_KERNEL); w->name = devm_kstrdup(dev, wname, GFP_KERNEL);
...@@ -657,6 +537,8 @@ static int hdac_hdmi_fill_widget_info(struct device *dev, ...@@ -657,6 +537,8 @@ static int hdac_hdmi_fill_widget_info(struct device *dev,
w->kcontrol_news = wc; w->kcontrol_news = wc;
w->num_kcontrols = numkc; w->num_kcontrols = numkc;
w->priv = priv; w->priv = priv;
w->event = event;
w->event_flags = event_flags;
return 0; return 0;
} }
...@@ -686,6 +568,136 @@ static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_ext_device *edev, ...@@ -686,6 +568,136 @@ static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_ext_device *edev,
return NULL; return NULL;
} }
static void hdac_hdmi_set_power_state(struct hdac_ext_device *edev,
hda_nid_t nid, unsigned int pwr_state)
{
if (get_wcaps(&edev->hdac, nid) & AC_WCAP_POWER) {
if (!snd_hdac_check_power_state(&edev->hdac, nid, pwr_state))
snd_hdac_codec_write(&edev->hdac, nid, 0,
AC_VERB_SET_POWER_STATE, pwr_state);
}
}
static void hdac_hdmi_set_amp(struct hdac_ext_device *edev,
hda_nid_t nid, int val)
{
if (get_wcaps(&edev->hdac, nid) & AC_WCAP_OUT_AMP)
snd_hdac_codec_write(&edev->hdac, nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE, val);
}
static int hdac_hdmi_pin_output_widget_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kc, int event)
{
struct hdac_hdmi_pin *pin = w->priv;
struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev);
struct hdac_hdmi_pcm *pcm;
dev_dbg(&edev->hdac.dev, "%s: widget: %s event: %x\n",
__func__, w->name, event);
pcm = hdac_hdmi_get_pcm(edev, pin);
if (!pcm)
return -EIO;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
hdac_hdmi_set_power_state(edev, pin->nid, AC_PWRST_D0);
/* Enable out path for this pin widget */
snd_hdac_codec_write(&edev->hdac, pin->nid, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
hdac_hdmi_set_amp(edev, pin->nid, AMP_OUT_UNMUTE);
return hdac_hdmi_setup_audio_infoframe(edev, pcm->cvt->nid,
pin->nid);
case SND_SOC_DAPM_POST_PMD:
hdac_hdmi_set_amp(edev, pin->nid, AMP_OUT_MUTE);
/* Disable out path for this pin widget */
snd_hdac_codec_write(&edev->hdac, pin->nid, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
hdac_hdmi_set_power_state(edev, pin->nid, AC_PWRST_D3);
break;
}
return 0;
}
static int hdac_hdmi_cvt_output_widget_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kc, int event)
{
struct hdac_hdmi_cvt *cvt = w->priv;
struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev);
struct hdac_hdmi_priv *hdmi = edev->private_data;
struct hdac_hdmi_pcm *pcm;
dev_dbg(&edev->hdac.dev, "%s: widget: %s event: %x\n",
__func__, w->name, event);
pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, cvt);
if (!pcm)
return -EIO;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
hdac_hdmi_set_power_state(edev, cvt->nid, AC_PWRST_D0);
/* Enable transmission */
snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
AC_VERB_SET_DIGI_CONVERT_1, 1);
/* Category Code (CC) to zero */
snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
AC_VERB_SET_DIGI_CONVERT_2, 0);
snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
AC_VERB_SET_CHANNEL_STREAMID, pcm->stream_tag);
snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
AC_VERB_SET_STREAM_FORMAT, pcm->format);
break;
case SND_SOC_DAPM_POST_PMD:
snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
AC_VERB_SET_CHANNEL_STREAMID, 0);
snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
AC_VERB_SET_STREAM_FORMAT, 0);
hdac_hdmi_set_power_state(edev, cvt->nid, AC_PWRST_D3);
break;
}
return 0;
}
static int hdac_hdmi_pin_mux_widget_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kc, int event)
{
struct hdac_hdmi_pin *pin = w->priv;
struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev);
int mux_idx;
dev_dbg(&edev->hdac.dev, "%s: widget: %s event: %x\n",
__func__, w->name, event);
if (!kc)
kc = w->kcontrols[0];
mux_idx = dapm_kcontrol_get_value(kc);
if (mux_idx > 0) {
snd_hdac_codec_write(&edev->hdac, pin->nid, 0,
AC_VERB_SET_CONNECT_SEL, (mux_idx - 1));
}
return 0;
}
/* /*
* Based on user selection, map the PINs with the PCMs. * Based on user selection, map the PINs with the PCMs.
*/ */
...@@ -803,7 +815,9 @@ static int hdac_hdmi_create_pin_muxs(struct hdac_ext_device *edev, ...@@ -803,7 +815,9 @@ static int hdac_hdmi_create_pin_muxs(struct hdac_ext_device *edev,
return -ENOMEM; return -ENOMEM;
return hdac_hdmi_fill_widget_info(&edev->hdac.dev, widget, return hdac_hdmi_fill_widget_info(&edev->hdac.dev, widget,
snd_soc_dapm_mux, pin, widget_name, NULL, kc, 1); snd_soc_dapm_mux, pin, widget_name, NULL, kc, 1,
hdac_hdmi_pin_mux_widget_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_REG);
} }
/* Add cvt <- input <- mux route map */ /* Add cvt <- input <- mux route map */
...@@ -874,8 +888,10 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm) ...@@ -874,8 +888,10 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm)
list_for_each_entry(cvt, &hdmi->cvt_list, head) { list_for_each_entry(cvt, &hdmi->cvt_list, head) {
sprintf(widget_name, "Converter %d", cvt->nid); sprintf(widget_name, "Converter %d", cvt->nid);
ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i], ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
snd_soc_dapm_aif_in, &cvt->nid, snd_soc_dapm_aif_in, cvt,
widget_name, dai_drv[i].playback.stream_name, NULL, 0); widget_name, dai_drv[i].playback.stream_name, NULL, 0,
hdac_hdmi_cvt_output_widget_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD);
if (ret < 0) if (ret < 0)
return ret; return ret;
i++; i++;
...@@ -884,8 +900,10 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm) ...@@ -884,8 +900,10 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm)
list_for_each_entry(pin, &hdmi->pin_list, head) { list_for_each_entry(pin, &hdmi->pin_list, head) {
sprintf(widget_name, "hif%d Output", pin->nid); sprintf(widget_name, "hif%d Output", pin->nid);
ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i], ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
snd_soc_dapm_output, &pin->nid, snd_soc_dapm_output, pin,
widget_name, NULL, NULL, 0); widget_name, NULL, NULL, 0,
hdac_hdmi_pin_output_widget_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD);
if (ret < 0) if (ret < 0)
return ret; return ret;
i++; i++;
...@@ -1141,9 +1159,7 @@ static struct snd_soc_dai_ops hdmi_dai_ops = { ...@@ -1141,9 +1159,7 @@ static struct snd_soc_dai_ops hdmi_dai_ops = {
.startup = hdac_hdmi_pcm_open, .startup = hdac_hdmi_pcm_open,
.shutdown = hdac_hdmi_pcm_close, .shutdown = hdac_hdmi_pcm_close,
.hw_params = hdac_hdmi_set_hw_params, .hw_params = hdac_hdmi_set_hw_params,
.prepare = hdac_hdmi_playback_prepare, .set_tdm_slot = hdac_hdmi_set_tdm_slot,
.trigger = hdac_hdmi_trigger,
.hw_free = hdac_hdmi_playback_cleanup,
}; };
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment