forked from luck/tmp_suning_uos_patched
ASoC: hdac_hda: add support for HDMI/DP as a HDA codec
Handle all HDA codecs using same logic, including HDMI/DP. Call to snd_hda_codec_build_controls() is delayed for HDMI/DP HDA devices. This is needed to discover the PCM device numbers as defined in topology. Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com> Reviewed-by: Takashi Iwai <tiwai@suse.de> Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> Link: https://lore.kernel.org/r/20191029134017.18901-3-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
2a2edfbbfe
commit
608b8c36c3
|
@ -14,13 +14,11 @@
|
||||||
#include <sound/pcm_params.h>
|
#include <sound/pcm_params.h>
|
||||||
#include <sound/soc.h>
|
#include <sound/soc.h>
|
||||||
#include <sound/hdaudio_ext.h>
|
#include <sound/hdaudio_ext.h>
|
||||||
|
#include <sound/hda_i915.h>
|
||||||
#include <sound/hda_codec.h>
|
#include <sound/hda_codec.h>
|
||||||
#include <sound/hda_register.h>
|
#include <sound/hda_register.h>
|
||||||
#include "hdac_hda.h"
|
|
||||||
|
|
||||||
#define HDAC_ANALOG_DAI_ID 0
|
#include "hdac_hda.h"
|
||||||
#define HDAC_DIGITAL_DAI_ID 1
|
|
||||||
#define HDAC_ALT_ANALOG_DAI_ID 2
|
|
||||||
|
|
||||||
#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
|
#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
|
||||||
SNDRV_PCM_FMTBIT_U8 | \
|
SNDRV_PCM_FMTBIT_U8 | \
|
||||||
|
@ -32,6 +30,11 @@
|
||||||
SNDRV_PCM_FMTBIT_U32_LE | \
|
SNDRV_PCM_FMTBIT_U32_LE | \
|
||||||
SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
|
SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
|
||||||
|
|
||||||
|
#define STUB_HDMI_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
|
||||||
|
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
|
||||||
|
SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\
|
||||||
|
SNDRV_PCM_RATE_192000)
|
||||||
|
|
||||||
static int hdac_hda_dai_open(struct snd_pcm_substream *substream,
|
static int hdac_hda_dai_open(struct snd_pcm_substream *substream,
|
||||||
struct snd_soc_dai *dai);
|
struct snd_soc_dai *dai);
|
||||||
static void hdac_hda_dai_close(struct snd_pcm_substream *substream,
|
static void hdac_hda_dai_close(struct snd_pcm_substream *substream,
|
||||||
|
@ -121,7 +124,46 @@ static struct snd_soc_dai_driver hdac_hda_dais[] = {
|
||||||
.formats = STUB_FORMATS,
|
.formats = STUB_FORMATS,
|
||||||
.sig_bits = 24,
|
.sig_bits = 24,
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
.id = HDAC_HDMI_0_DAI_ID,
|
||||||
|
.name = "intel-hdmi-hifi1",
|
||||||
|
.ops = &hdac_hda_dai_ops,
|
||||||
|
.playback = {
|
||||||
|
.stream_name = "hifi1",
|
||||||
|
.channels_min = 1,
|
||||||
|
.channels_max = 32,
|
||||||
|
.rates = STUB_HDMI_RATES,
|
||||||
|
.formats = STUB_FORMATS,
|
||||||
|
.sig_bits = 24,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.id = HDAC_HDMI_1_DAI_ID,
|
||||||
|
.name = "intel-hdmi-hifi2",
|
||||||
|
.ops = &hdac_hda_dai_ops,
|
||||||
|
.playback = {
|
||||||
|
.stream_name = "hifi2",
|
||||||
|
.channels_min = 1,
|
||||||
|
.channels_max = 32,
|
||||||
|
.rates = STUB_HDMI_RATES,
|
||||||
|
.formats = STUB_FORMATS,
|
||||||
|
.sig_bits = 24,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.id = HDAC_HDMI_2_DAI_ID,
|
||||||
|
.name = "intel-hdmi-hifi3",
|
||||||
|
.ops = &hdac_hda_dai_ops,
|
||||||
|
.playback = {
|
||||||
|
.stream_name = "hifi3",
|
||||||
|
.channels_min = 1,
|
||||||
|
.channels_max = 32,
|
||||||
|
.rates = STUB_HDMI_RATES,
|
||||||
|
.formats = STUB_FORMATS,
|
||||||
|
.sig_bits = 24,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -135,10 +177,11 @@ static int hdac_hda_dai_set_tdm_slot(struct snd_soc_dai *dai,
|
||||||
|
|
||||||
hda_pvt = snd_soc_component_get_drvdata(component);
|
hda_pvt = snd_soc_component_get_drvdata(component);
|
||||||
pcm = &hda_pvt->pcm[dai->id];
|
pcm = &hda_pvt->pcm[dai->id];
|
||||||
|
|
||||||
if (tx_mask)
|
if (tx_mask)
|
||||||
pcm[dai->id].stream_tag[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask;
|
pcm->stream_tag[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask;
|
||||||
else
|
else
|
||||||
pcm[dai->id].stream_tag[SNDRV_PCM_STREAM_CAPTURE] = rx_mask;
|
pcm->stream_tag[SNDRV_PCM_STREAM_CAPTURE] = rx_mask;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -278,6 +321,12 @@ static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt,
|
||||||
struct hda_pcm *cpcm;
|
struct hda_pcm *cpcm;
|
||||||
const char *pcm_name;
|
const char *pcm_name;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* map DAI ID to the closest matching PCM name, using the naming
|
||||||
|
* scheme used by hda-codec snd_hda_gen_build_pcms() and for
|
||||||
|
* HDMI in hda_codec patch_hdmi.c)
|
||||||
|
*/
|
||||||
|
|
||||||
switch (dai->id) {
|
switch (dai->id) {
|
||||||
case HDAC_ANALOG_DAI_ID:
|
case HDAC_ANALOG_DAI_ID:
|
||||||
pcm_name = "Analog";
|
pcm_name = "Analog";
|
||||||
|
@ -288,13 +337,22 @@ static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt,
|
||||||
case HDAC_ALT_ANALOG_DAI_ID:
|
case HDAC_ALT_ANALOG_DAI_ID:
|
||||||
pcm_name = "Alt Analog";
|
pcm_name = "Alt Analog";
|
||||||
break;
|
break;
|
||||||
|
case HDAC_HDMI_0_DAI_ID:
|
||||||
|
pcm_name = "HDMI 0";
|
||||||
|
break;
|
||||||
|
case HDAC_HDMI_1_DAI_ID:
|
||||||
|
pcm_name = "HDMI 1";
|
||||||
|
break;
|
||||||
|
case HDAC_HDMI_2_DAI_ID:
|
||||||
|
pcm_name = "HDMI 2";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
dev_err(&hcodec->core.dev, "invalid dai id %d\n", dai->id);
|
dev_err(&hcodec->core.dev, "invalid dai id %d\n", dai->id);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
list_for_each_entry(cpcm, &hcodec->pcm_list_head, list) {
|
list_for_each_entry(cpcm, &hcodec->pcm_list_head, list) {
|
||||||
if (strpbrk(cpcm->name, pcm_name))
|
if (strstr(cpcm->name, pcm_name))
|
||||||
return cpcm;
|
return cpcm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,6 +360,18 @@ static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool is_hdmi_codec(struct hda_codec *hcodec)
|
||||||
|
{
|
||||||
|
struct hda_pcm *cpcm;
|
||||||
|
|
||||||
|
list_for_each_entry(cpcm, &hcodec->pcm_list_head, list) {
|
||||||
|
if (cpcm->pcm_type == HDA_PCM_TYPE_HDMI)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static int hdac_hda_codec_probe(struct snd_soc_component *component)
|
static int hdac_hda_codec_probe(struct snd_soc_component *component)
|
||||||
{
|
{
|
||||||
struct hdac_hda_priv *hda_pvt =
|
struct hdac_hda_priv *hda_pvt =
|
||||||
|
@ -322,6 +392,15 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component)
|
||||||
|
|
||||||
snd_hdac_ext_bus_link_get(hdev->bus, hlink);
|
snd_hdac_ext_bus_link_get(hdev->bus, hlink);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure any HDA display is powered at codec probe.
|
||||||
|
* After snd_hda_codec_device_new(), display power is
|
||||||
|
* managed by runtime PM.
|
||||||
|
*/
|
||||||
|
if (hda_pvt->need_display_power)
|
||||||
|
snd_hdac_display_power(hdev->bus,
|
||||||
|
HDA_CODEC_IDX_CONTROLLER, true);
|
||||||
|
|
||||||
ret = snd_hda_codec_device_new(hcodec->bus, component->card->snd_card,
|
ret = snd_hda_codec_device_new(hcodec->bus, component->card->snd_card,
|
||||||
hdev->addr, hcodec);
|
hdev->addr, hcodec);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@ -366,20 +445,31 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component)
|
||||||
dev_dbg(&hdev->dev, "no patch file found\n");
|
dev_dbg(&hdev->dev, "no patch file found\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* configure codec for 1:1 PCM:DAI mapping */
|
||||||
|
hcodec->mst_no_extra_pcms = 1;
|
||||||
|
|
||||||
ret = snd_hda_codec_parse_pcms(hcodec);
|
ret = snd_hda_codec_parse_pcms(hcodec);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&hdev->dev, "unable to map pcms to dai %d\n", ret);
|
dev_err(&hdev->dev, "unable to map pcms to dai %d\n", ret);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = snd_hda_codec_build_controls(hcodec);
|
/* HDMI controls need to be created in machine drivers */
|
||||||
if (ret < 0) {
|
if (!is_hdmi_codec(hcodec)) {
|
||||||
dev_err(&hdev->dev, "unable to create controls %d\n", ret);
|
ret = snd_hda_codec_build_controls(hcodec);
|
||||||
goto error;
|
if (ret < 0) {
|
||||||
|
dev_err(&hdev->dev, "unable to create controls %d\n",
|
||||||
|
ret);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hcodec->core.lazy_cache = true;
|
hcodec->core.lazy_cache = true;
|
||||||
|
|
||||||
|
if (hda_pvt->need_display_power)
|
||||||
|
snd_hdac_display_power(hdev->bus,
|
||||||
|
HDA_CODEC_IDX_CONTROLLER, false);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* hdac_device core already sets the state to active and calls
|
* hdac_device core already sets the state to active and calls
|
||||||
* get_noresume. So enable runtime and set the device to suspend.
|
* get_noresume. So enable runtime and set the device to suspend.
|
||||||
|
|
|
@ -6,6 +6,16 @@
|
||||||
#ifndef __HDAC_HDA_H__
|
#ifndef __HDAC_HDA_H__
|
||||||
#define __HDAC_HDA_H__
|
#define __HDAC_HDA_H__
|
||||||
|
|
||||||
|
enum {
|
||||||
|
HDAC_ANALOG_DAI_ID = 0,
|
||||||
|
HDAC_DIGITAL_DAI_ID,
|
||||||
|
HDAC_ALT_ANALOG_DAI_ID,
|
||||||
|
HDAC_HDMI_0_DAI_ID,
|
||||||
|
HDAC_HDMI_1_DAI_ID,
|
||||||
|
HDAC_HDMI_2_DAI_ID,
|
||||||
|
HDAC_LAST_DAI_ID = HDAC_HDMI_2_DAI_ID,
|
||||||
|
};
|
||||||
|
|
||||||
struct hdac_hda_pcm {
|
struct hdac_hda_pcm {
|
||||||
int stream_tag[2];
|
int stream_tag[2];
|
||||||
unsigned int format_val[2];
|
unsigned int format_val[2];
|
||||||
|
@ -13,7 +23,8 @@ struct hdac_hda_pcm {
|
||||||
|
|
||||||
struct hdac_hda_priv {
|
struct hdac_hda_priv {
|
||||||
struct hda_codec codec;
|
struct hda_codec codec;
|
||||||
struct hdac_hda_pcm pcm[2];
|
struct hdac_hda_pcm pcm[HDAC_LAST_DAI_ID];
|
||||||
|
bool need_display_power;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define hdac_to_hda_priv(_hdac) \
|
#define hdac_to_hda_priv(_hdac) \
|
||||||
|
|
Loading…
Reference in New Issue
Block a user