Commit df91a210 authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branch 'asoc/topic/intel' into asoc-next

parents a1eb3000 e2304803
......@@ -403,6 +403,18 @@ static inline int drm_eld_size(const uint8_t *eld)
return DRM_ELD_HEADER_BLOCK_SIZE + eld[DRM_ELD_BASELINE_ELD_LEN] * 4;
}
/**
* drm_eld_get_conn_type - Get device type hdmi/dp connected
* @eld: pointer to an ELD memory structure
*
* The caller need to use %DRM_ELD_CONN_TYPE_HDMI or %DRM_ELD_CONN_TYPE_DP to
* identify the display type connected.
*/
static inline u8 drm_eld_get_conn_type(const uint8_t *eld)
{
return eld[DRM_ELD_SAD_COUNT_CONN_TYPE] & DRM_ELD_CONN_TYPE_MASK;
}
struct edid *drm_do_get_edid(struct drm_connector *connector,
int (*get_edid_block)(void *data, u8 *buf, unsigned int block,
size_t len),
......
......@@ -490,6 +490,7 @@ config SND_SOC_GTM601
config SND_SOC_HDAC_HDMI
tristate
select SND_HDA_EXT_CORE
select SND_PCM_ELD
select HDMI
config SND_SOC_ICS43432
......
......@@ -22,11 +22,17 @@
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/hdmi.h>
#include <drm/drm_edid.h>
#include <sound/pcm_params.h>
#include <sound/jack.h>
#include <sound/soc.h>
#include <sound/hdaudio_ext.h>
#include <sound/hda_i915.h>
#include <sound/pcm_drm_eld.h>
#include "../../hda/local.h"
#include "hdac_hdmi.h"
#define NAME_SIZE 32
#define AMP_OUT_MUTE 0xb080
#define AMP_OUT_UNMUTE 0xb000
......@@ -34,6 +40,11 @@
#define HDA_MAX_CONNECTIONS 32
#define HDA_MAX_CVTS 3
#define ELD_MAX_SIZE 256
#define ELD_FIXED_BYTES 20
struct hdac_hdmi_cvt_params {
unsigned int channels_min;
unsigned int channels_max;
......@@ -45,14 +56,34 @@ struct hdac_hdmi_cvt_params {
struct hdac_hdmi_cvt {
struct list_head head;
hda_nid_t nid;
const char *name;
struct hdac_hdmi_cvt_params params;
};
struct hdac_hdmi_eld {
bool monitor_present;
bool eld_valid;
int eld_size;
char eld_buffer[ELD_MAX_SIZE];
};
struct hdac_hdmi_pin {
struct list_head head;
hda_nid_t nid;
int num_mux_nids;
hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
struct hdac_hdmi_eld eld;
struct hdac_ext_device *edev;
int repoll_count;
struct delayed_work work;
};
struct hdac_hdmi_pcm {
struct list_head head;
int pcm_id;
struct hdac_hdmi_pin *pin;
struct hdac_hdmi_cvt *cvt;
struct snd_jack *jack;
};
struct hdac_hdmi_dai_pin_map {
......@@ -62,11 +93,13 @@ struct hdac_hdmi_dai_pin_map {
};
struct hdac_hdmi_priv {
struct hdac_hdmi_dai_pin_map dai_map[3];
struct hdac_hdmi_dai_pin_map dai_map[HDA_MAX_CVTS];
struct list_head pin_list;
struct list_head cvt_list;
struct list_head pcm_list;
int num_pin;
int num_cvt;
struct mutex pin_mutex;
};
static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
......@@ -76,6 +109,119 @@ static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
return to_ehdac_device(hdac);
}
static unsigned int sad_format(const u8 *sad)
{
return ((sad[0] >> 0x3) & 0x1f);
}
static unsigned int sad_sample_bits_lpcm(const u8 *sad)
{
return (sad[2] & 7);
}
static int hdac_hdmi_eld_limit_formats(struct snd_pcm_runtime *runtime,
void *eld)
{
u64 formats = SNDRV_PCM_FMTBIT_S16;
int i;
const u8 *sad, *eld_buf = eld;
sad = drm_eld_sad(eld_buf);
if (!sad)
goto format_constraint;
for (i = drm_eld_sad_count(eld_buf); i > 0; i--, sad += 3) {
if (sad_format(sad) == 1) { /* AUDIO_CODING_TYPE_LPCM */
/*
* the controller support 20 and 24 bits in 32 bit
* container so we set S32
*/
if (sad_sample_bits_lpcm(sad) & 0x6)
formats |= SNDRV_PCM_FMTBIT_S32;
}
}
format_constraint:
return snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT,
formats);
}
/* HDMI ELD routines */
static unsigned int hdac_hdmi_get_eld_data(struct hdac_device *codec,
hda_nid_t nid, int byte_index)
{
unsigned int val;
val = snd_hdac_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_ELDD,
byte_index);
dev_dbg(&codec->dev, "HDMI: ELD data byte %d: 0x%x\n",
byte_index, val);
return val;
}
static int hdac_hdmi_get_eld_size(struct hdac_device *codec, hda_nid_t nid)
{
return snd_hdac_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
AC_DIPSIZE_ELD_BUF);
}
/*
* This function queries the ELD size and ELD data and fills in the buffer
* passed by user
*/
static int hdac_hdmi_get_eld(struct hdac_device *codec, hda_nid_t nid,
unsigned char *buf, int *eld_size)
{
int i, size, ret = 0;
/*
* ELD size is initialized to zero in caller function. If no errors and
* ELD is valid, actual eld_size is assigned.
*/
size = hdac_hdmi_get_eld_size(codec, nid);
if (size < ELD_FIXED_BYTES || size > ELD_MAX_SIZE) {
dev_err(&codec->dev, "HDMI: invalid ELD buf size %d\n", size);
return -ERANGE;
}
/* set ELD buffer */
for (i = 0; i < size; i++) {
unsigned int val = hdac_hdmi_get_eld_data(codec, nid, i);
/*
* Graphics driver might be writing to ELD buffer right now.
* Just abort. The caller will repoll after a while.
*/
if (!(val & AC_ELDD_ELD_VALID)) {
dev_err(&codec->dev,
"HDMI: invalid ELD data byte %d\n", i);
ret = -EINVAL;
goto error;
}
val &= AC_ELDD_ELD_DATA;
/*
* The first byte cannot be zero. This can happen on some DVI
* connections. Some Intel chips may also need some 250ms delay
* to return non-zero ELD data, even when the graphics driver
* correctly writes ELD content before setting ELD_valid bit.
*/
if (!val && !i) {
dev_err(&codec->dev, "HDMI: 0 ELD data\n");
ret = -EINVAL;
goto error;
}
buf[i] = val;
}
*eld_size = size;
error:
return ret;
}
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)
......@@ -107,28 +253,76 @@ hdac_hdmi_set_dip_index(struct hdac_ext_device *hdac, hda_nid_t pin_nid,
AC_VERB_SET_HDMI_DIP_INDEX, val);
}
struct dp_audio_infoframe {
u8 type; /* 0x84 */
u8 len; /* 0x1b */
u8 ver; /* 0x11 << 2 */
u8 CC02_CT47; /* match with HDMI infoframe from this on */
u8 SS01_SF24;
u8 CXT04;
u8 CA;
u8 LFEPBL01_LSV36_DM_INH7;
};
static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,
hda_nid_t cvt_nid, hda_nid_t pin_nid)
{
uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE];
struct hdmi_audio_infoframe frame;
u8 *dip = (u8 *)&frame;
struct dp_audio_infoframe dp_ai;
struct hdac_hdmi_priv *hdmi = hdac->private_data;
struct hdac_hdmi_pin *pin;
u8 *dip;
int ret;
int i;
const u8 *eld_buf;
u8 conn_type;
int channels = 2;
hdmi_audio_infoframe_init(&frame);
list_for_each_entry(pin, &hdmi->pin_list, head) {
if (pin->nid == pin_nid)
break;
}
/* Default stereo for now */
frame.channels = 2;
eld_buf = pin->eld.eld_buffer;
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, frame.channels - 1);
AC_VERB_SET_CVT_CHAN_COUNT, channels - 1);
switch (conn_type) {
case DRM_ELD_CONN_TYPE_HDMI:
hdmi_audio_infoframe_init(&frame);
/* Default stereo for now */
frame.channels = channels;
ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
if (ret < 0)
return ret;
dip = (u8 *)&frame;
break;
case DRM_ELD_CONN_TYPE_DP:
memset(&dp_ai, 0, sizeof(dp_ai));
dp_ai.type = 0x84;
dp_ai.len = 0x1b;
dp_ai.ver = 0x11 << 2;
dp_ai.CC02_CT47 = channels - 1;
dp_ai.CA = 0;
dip = (u8 *)&dp_ai;
break;
default:
dev_err(&hdac->hdac.dev, "Invalid connection type: %d\n",
conn_type);
return -EIO;
}
/* stop infoframe transmission */
hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
......@@ -137,9 +331,15 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,
/* Fill infoframe. Index auto-incremented */
hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
if (conn_type == DRM_ELD_CONN_TYPE_HDMI) {
for (i = 0; i < sizeof(frame); i++)
snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
AC_VERB_SET_HDMI_DIP_DATA, dip[i]);
} else {
for (i = 0; i < sizeof(dp_ai); i++)
snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
AC_VERB_SET_HDMI_DIP_DATA, dip[i]);
}
/* Start infoframe */
hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
......@@ -174,11 +374,6 @@ static int hdac_hdmi_playback_prepare(struct snd_pcm_substream *substream,
struct hdac_ext_dma_params *dd;
int ret;
if (dai->id > 0) {
dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
return -ENODEV;
}
dai_map = &hdmi->dai_map[dai->id];
dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
......@@ -198,16 +393,30 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hparams, 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_pin *pin;
struct hdac_ext_dma_params *dd;
if (dai->id > 0) {
dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
dai_map = &hdmi->dai_map[dai->id];
pin = dai_map->pin;
if (!pin)
return -ENODEV;
if ((!pin->eld.monitor_present) || (!pin->eld.eld_valid)) {
dev_err(&hdac->hdac.dev, "device is not configured for this pin: %d\n",
pin->nid);
return -ENODEV;
}
dd = snd_soc_dai_get_dma_data(dai, substream);
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),
24, 0);
......@@ -227,50 +436,187 @@ static int hdac_hdmi_playback_cleanup(struct snd_pcm_substream *substream,
dai_map = &hdmi->dai_map[dai->id];
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_CHANNEL_STREAMID, 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_STREAM_FORMAT, 0);
AC_VERB_SET_DIGI_CONVERT_2, 0);
}
dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
snd_soc_dai_set_dma_data(dai, substream, NULL);
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;
kfree(dd);
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;
/* Enable out path for this pin widget */
snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
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;
}
static int hdac_hdmi_query_pin_connlist(struct hdac_ext_device *hdac,
struct hdac_hdmi_pin *pin)
{
if (!(get_wcaps(&hdac->hdac, pin->nid) & AC_WCAP_CONN_LIST)) {
dev_warn(&hdac->hdac.dev,
"HDMI: pin %d wcaps %#x does not support connection list\n",
pin->nid, get_wcaps(&hdac->hdac, pin->nid));
return -EINVAL;
}
pin->num_mux_nids = snd_hdac_get_connections(&hdac->hdac, pin->nid,
pin->mux_nids, HDA_MAX_CONNECTIONS);
if (pin->num_mux_nids == 0)
dev_warn(&hdac->hdac.dev, "No connections found for pin: %d\n",
pin->nid);
dev_dbg(&hdac->hdac.dev, "num_mux_nids %d for pin: %d\n",
pin->num_mux_nids, pin->nid);
return pin->num_mux_nids;
}
/*
* Query pcm list and return pin widget to which stream is routed.
*
* Also query connection list of the pin, to validate the cvt to pin map.
*
* Same stream rendering to multiple pins simultaneously can be done
* possibly, but not supported for now in driver. So return the first pin
* connected.
*/
static struct hdac_hdmi_pin *hdac_hdmi_get_pin_from_cvt(
struct hdac_ext_device *edev,
struct hdac_hdmi_priv *hdmi,
struct hdac_hdmi_cvt *cvt)
{
struct hdac_hdmi_pcm *pcm;
struct hdac_hdmi_pin *pin = NULL;
int ret, i;
list_for_each_entry(pcm, &hdmi->pcm_list, head) {
if (pcm->cvt == cvt) {
pin = pcm->pin;
break;
}
}
if (pin) {
ret = hdac_hdmi_query_pin_connlist(edev, pin);
if (ret < 0)
return NULL;
for (i = 0; i < pin->num_mux_nids; i++) {
if (pin->mux_nids[i] == cvt->nid)
return pin;
}
}
return NULL;
}
/*
* This tries to get a valid pin and set the HW constraints based on the
* ELD. Even if a valid pin is not found return success so that device open
* doesn't fail.
*/
static int hdac_hdmi_pcm_open(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;
int val;
if (dai->id > 0) {
dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
return -ENODEV;
}
struct hdac_hdmi_cvt *cvt;
struct hdac_hdmi_pin *pin;
int ret;
dai_map = &hdmi->dai_map[dai->id];
val = snd_hdac_codec_read(&hdac->hdac, dai_map->pin->nid, 0,
AC_VERB_GET_PIN_SENSE, 0);
dev_info(&hdac->hdac.dev, "Val for AC_VERB_GET_PIN_SENSE: %x\n", val);
cvt = dai_map->cvt;
pin = hdac_hdmi_get_pin_from_cvt(hdac, hdmi, cvt);
if ((!(val & AC_PINSENSE_PRESENCE)) || (!(val & AC_PINSENSE_ELDV))) {
dev_err(&hdac->hdac.dev, "Monitor presence invalid with val: %x\n", val);
return -ENODEV;
/*
* To make PA and other userland happy.
* userland scans devices so returning error does not help.
*/
if (!pin)
return 0;
if ((!pin->eld.monitor_present) ||
(!pin->eld.eld_valid)) {
dev_warn(&hdac->hdac.dev,
"Failed: montior present? %d ELD valid?: %d for pin: %d\n",
pin->eld.monitor_present, pin->eld.eld_valid, pin->nid);
return 0;
}
hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D0);
dai_map->pin = pin;
snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
hdac_hdmi_enable_cvt(hdac, dai_map);
ret = hdac_hdmi_enable_pin(hdac, dai_map);
if (ret < 0)
return ret;
snd_pcm_hw_constraint_step(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_CHANNELS, 2);
ret = hdac_hdmi_eld_limit_formats(substream->runtime,
pin->eld.eld_buffer);
if (ret < 0)
return ret;
return snd_pcm_hw_constraint_eld(substream->runtime,
pin->eld.eld_buffer);
}
static int hdac_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct hdac_hdmi_dai_pin_map *dai_map;
struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
struct hdac_hdmi_priv *hdmi = hdac->private_data;
int ret;
dai_map = &hdmi->dai_map[dai->id];
if (cmd == SNDRV_PCM_TRIGGER_RESUME) {
ret = hdac_hdmi_enable_pin(hdac, dai_map);
if (ret < 0)
return ret;
return hdac_hdmi_playback_prepare(substream, dai);
}
return 0;
}
......@@ -284,10 +630,19 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
dai_map = &hdmi->dai_map[dai->id];
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);
dai_map->pin = NULL;
}
}
static int
......@@ -310,85 +665,326 @@ hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt)
return err;
}
static void hdac_hdmi_fill_widget_info(struct snd_soc_dapm_widget *w,
enum snd_soc_dapm_type id,
const char *wname, const char *stream)
static int hdac_hdmi_fill_widget_info(struct device *dev,
struct snd_soc_dapm_widget *w,
enum snd_soc_dapm_type id, void *priv,
const char *wname, const char *stream,
struct snd_kcontrol_new *wc, int numkc)
{
w->id = id;
w->name = wname;
w->name = devm_kstrdup(dev, wname, GFP_KERNEL);
if (!w->name)
return -ENOMEM;
w->sname = stream;
w->reg = SND_SOC_NOPM;
w->shift = 0;
w->kcontrol_news = NULL;
w->num_kcontrols = 0;
w->priv = NULL;
w->kcontrol_news = wc;
w->num_kcontrols = numkc;
w->priv = priv;
return 0;
}
static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route,
const char *sink, const char *control, const char *src)
const char *sink, const char *control, const char *src,
int (*handler)(struct snd_soc_dapm_widget *src,
struct snd_soc_dapm_widget *sink))
{
route->sink = sink;
route->source = src;
route->control = control;
route->connected = NULL;
route->connected = handler;
}
static void create_fill_widget_route_map(struct snd_soc_dapm_context *dapm,
struct hdac_hdmi_dai_pin_map *dai_map)
static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_ext_device *edev,
struct hdac_hdmi_pin *pin)
{
struct snd_soc_dapm_route route[1];
struct snd_soc_dapm_widget widgets[2] = { {0} };
struct hdac_hdmi_priv *hdmi = edev->private_data;
struct hdac_hdmi_pcm *pcm = NULL;
list_for_each_entry(pcm, &hdmi->pcm_list, head) {
if (pcm->pin == pin)
return pcm;
}
return NULL;
}
/*
* Based on user selection, map the PINs with the PCMs.
*/
static int hdac_hdmi_set_pin_mux(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int ret;
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol);
struct snd_soc_dapm_context *dapm = w->dapm;
struct hdac_hdmi_pin *pin = w->priv;
struct hdac_ext_device *edev = to_hda_ext_device(dapm->dev);
struct hdac_hdmi_priv *hdmi = edev->private_data;
struct hdac_hdmi_pcm *pcm = NULL;
const char *cvt_name = e->texts[ucontrol->value.enumerated.item[0]];
memset(&route, 0, sizeof(route));
ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
if (ret < 0)
return ret;
hdac_hdmi_fill_widget_info(&widgets[0], snd_soc_dapm_output,
"hif1 Output", NULL);
hdac_hdmi_fill_widget_info(&widgets[1], snd_soc_dapm_aif_in,
"Coverter 1", "hif1");
mutex_lock(&hdmi->pin_mutex);
list_for_each_entry(pcm, &hdmi->pcm_list, head) {
if (pcm->pin == pin)
pcm->pin = NULL;
hdac_hdmi_fill_route(&route[0], "hif1 Output", NULL, "Coverter 1");
/*
* Jack status is not reported during device probe as the
* PCMs are not registered by then. So report it here.
*/
if (!strcmp(cvt_name, pcm->cvt->name) && !pcm->pin) {
pcm->pin = pin;
if (pin->eld.monitor_present && pin->eld.eld_valid) {
dev_dbg(&edev->hdac.dev,
"jack report for pcm=%d\n",
pcm->pcm_id);
snd_jack_report(pcm->jack, SND_JACK_AVOUT);
}
mutex_unlock(&hdmi->pin_mutex);
return ret;
}
}
mutex_unlock(&hdmi->pin_mutex);
snd_soc_dapm_new_controls(dapm, widgets, ARRAY_SIZE(widgets));
snd_soc_dapm_add_routes(dapm, route, ARRAY_SIZE(route));
return ret;
}
static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev)
/*
* Ideally the Mux inputs should be based on the num_muxs enumerated, but
* the display driver seem to be programming the connection list for the pin
* widget runtime.
*
* So programming all the possible inputs for the mux, the user has to take
* care of selecting the right one and leaving all other inputs selected to
* "NONE"
*/
static int hdac_hdmi_create_pin_muxs(struct hdac_ext_device *edev,
struct hdac_hdmi_pin *pin,
struct snd_soc_dapm_widget *widget,
const char *widget_name)
{
struct hdac_hdmi_priv *hdmi = edev->private_data;
struct snd_kcontrol_new *kc;
struct hdac_hdmi_cvt *cvt;
struct soc_enum *se;
char kc_name[NAME_SIZE];
char mux_items[NAME_SIZE];
/* To hold inputs to the Pin mux */
char *items[HDA_MAX_CONNECTIONS];
int i = 0;
int num_items = hdmi->num_cvt + 1;
kc = devm_kzalloc(&edev->hdac.dev, sizeof(*kc), GFP_KERNEL);
if (!kc)
return -ENOMEM;
se = devm_kzalloc(&edev->hdac.dev, sizeof(*se), GFP_KERNEL);
if (!se)
return -ENOMEM;
sprintf(kc_name, "Pin %d Input", pin->nid);
kc->name = devm_kstrdup(&edev->hdac.dev, kc_name, GFP_KERNEL);
if (!kc->name)
return -ENOMEM;
kc->private_value = (long)se;
kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
kc->access = 0;
kc->info = snd_soc_info_enum_double;
kc->put = hdac_hdmi_set_pin_mux;
kc->get = snd_soc_dapm_get_enum_double;
se->reg = SND_SOC_NOPM;
/* enum texts: ["NONE", "cvt #", "cvt #", ...] */
se->items = num_items;
se->mask = roundup_pow_of_two(se->items) - 1;
sprintf(mux_items, "NONE");
items[i] = devm_kstrdup(&edev->hdac.dev, mux_items, GFP_KERNEL);
if (!items[i])
return -ENOMEM;
list_for_each_entry(cvt, &hdmi->cvt_list, head) {
i++;
sprintf(mux_items, "cvt %d", cvt->nid);
items[i] = devm_kstrdup(&edev->hdac.dev, mux_items, GFP_KERNEL);
if (!items[i])
return -ENOMEM;
}
se->texts = devm_kmemdup(&edev->hdac.dev, items,
(num_items * sizeof(char *)), GFP_KERNEL);
if (!se->texts)
return -ENOMEM;
return hdac_hdmi_fill_widget_info(&edev->hdac.dev, widget,
snd_soc_dapm_mux, pin, widget_name, NULL, kc, 1);
}
/* Add cvt <- input <- mux route map */
static void hdac_hdmi_add_pinmux_cvt_route(struct hdac_ext_device *edev,
struct snd_soc_dapm_widget *widgets,
struct snd_soc_dapm_route *route, int rindex)
{
struct hdac_hdmi_priv *hdmi = edev->private_data;
const struct snd_kcontrol_new *kc;
struct soc_enum *se;
int mux_index = hdmi->num_cvt + hdmi->num_pin;
int i, j;
for (i = 0; i < hdmi->num_pin; i++) {
kc = widgets[mux_index].kcontrol_news;
se = (struct soc_enum *)kc->private_value;
for (j = 0; j < hdmi->num_cvt; j++) {
hdac_hdmi_fill_route(&route[rindex],
widgets[mux_index].name,
se->texts[j + 1],
widgets[j].name, NULL);
rindex++;
}
mux_index++;
}
}
/*
* Widgets are added in the below sequence
* Converter widgets for num converters enumerated
* Pin widgets for num pins enumerated
* Pin mux widgets to represent connenction list of pin widget
*
* Total widgets elements = num_cvt + num_pin + num_pin;
*
* Routes are added as below:
* pin mux -> pin (based on num_pins)
* cvt -> "Input sel control" -> pin_mux
*
* Total route elements:
* num_pins + (pin_muxes * num_cvt)
*/
static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm)
{
struct snd_soc_dapm_widget *widgets;
struct snd_soc_dapm_route *route;
struct hdac_ext_device *edev = to_hda_ext_device(dapm->dev);
struct hdac_hdmi_priv *hdmi = edev->private_data;
struct hdac_hdmi_dai_pin_map *dai_map = &hdmi->dai_map[0];
struct snd_soc_dai_driver *dai_drv = dapm->component->dai_drv;
char widget_name[NAME_SIZE];
struct hdac_hdmi_cvt *cvt;
struct hdac_hdmi_pin *pin;
int ret, i = 0, num_routes = 0;
if (list_empty(&hdmi->cvt_list) || list_empty(&hdmi->pin_list))
return -EINVAL;
/*
* Currently on board only 1 pin and 1 converter is enabled for
* simplification, more will be added eventually
* So using fixed map for dai_id:pin:cvt
*/
cvt = list_first_entry(&hdmi->cvt_list, struct hdac_hdmi_cvt, head);
pin = list_first_entry(&hdmi->pin_list, struct hdac_hdmi_pin, head);
widgets = devm_kzalloc(dapm->dev,
(sizeof(*widgets) * ((2 * hdmi->num_pin) + hdmi->num_cvt)),
GFP_KERNEL);
dai_map->dai_id = 0;
dai_map->pin = pin;
if (!widgets)
return -ENOMEM;
dai_map->cvt = cvt;
/* DAPM widgets to represent each converter widget */
list_for_each_entry(cvt, &hdmi->cvt_list, head) {
sprintf(widget_name, "Converter %d", cvt->nid);
ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
snd_soc_dapm_aif_in, &cvt->nid,
widget_name, dai_drv[i].playback.stream_name, NULL, 0);
if (ret < 0)
return ret;
i++;
}
/* Enable out path for this pin widget */
snd_hdac_codec_write(&edev->hdac, pin->nid, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
list_for_each_entry(pin, &hdmi->pin_list, head) {
sprintf(widget_name, "hif%d Output", pin->nid);
ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
snd_soc_dapm_output, &pin->nid,
widget_name, NULL, NULL, 0);
if (ret < 0)
return ret;
i++;
}
/* Enable transmission */
snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
AC_VERB_SET_DIGI_CONVERT_1, 1);
/* DAPM widgets to represent the connection list to pin widget */
list_for_each_entry(pin, &hdmi->pin_list, head) {
sprintf(widget_name, "Pin %d Mux", pin->nid);
ret = hdac_hdmi_create_pin_muxs(edev, pin, &widgets[i],
widget_name);
if (ret < 0)
return ret;
i++;
/* Category Code (CC) to zero */
snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
AC_VERB_SET_DIGI_CONVERT_2, 0);
/* For cvt to pin_mux mapping */
num_routes += hdmi->num_cvt;
/* For pin_mux to pin mapping */
num_routes++;
}
route = devm_kzalloc(dapm->dev, (sizeof(*route) * num_routes),
GFP_KERNEL);
if (!route)
return -ENOMEM;
i = 0;
/* Add pin <- NULL <- mux route map */
list_for_each_entry(pin, &hdmi->pin_list, head) {
int sink_index = i + hdmi->num_cvt;
int src_index = sink_index + hdmi->num_pin;
snd_hdac_codec_write(&edev->hdac, pin->nid, 0,
AC_VERB_SET_CONNECT_SEL, 0);
hdac_hdmi_fill_route(&route[i],
widgets[sink_index].name, NULL,
widgets[src_index].name, NULL);
i++;
}
hdac_hdmi_add_pinmux_cvt_route(edev, widgets, route, i);
snd_soc_dapm_new_controls(dapm, widgets,
((2 * hdmi->num_pin) + hdmi->num_cvt));
snd_soc_dapm_add_routes(dapm, route, num_routes);
snd_soc_dapm_new_widgets(dapm->card);
return 0;
}
static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev)
{
struct hdac_hdmi_priv *hdmi = edev->private_data;
struct hdac_hdmi_dai_pin_map *dai_map;
struct hdac_hdmi_cvt *cvt;
int dai_id = 0;
if (list_empty(&hdmi->cvt_list))
return -EINVAL;
list_for_each_entry(cvt, &hdmi->cvt_list, head) {
dai_map = &hdmi->dai_map[dai_id];
dai_map->dai_id = dai_id;
dai_map->cvt = cvt;
dai_id++;
if (dai_id == HDA_MAX_CVTS) {
dev_warn(&edev->hdac.dev,
"Max dais supported: %d\n", dai_id);
break;
}
}
return 0;
}
......@@ -397,12 +993,15 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid)
{
struct hdac_hdmi_priv *hdmi = edev->private_data;
struct hdac_hdmi_cvt *cvt;
char name[NAME_SIZE];
cvt = kzalloc(sizeof(*cvt), GFP_KERNEL);
if (!cvt)
return -ENOMEM;
cvt->nid = nid;
sprintf(name, "cvt %d", cvt->nid);
cvt->name = kstrdup(name, GFP_KERNEL);
list_add_tail(&cvt->head, &hdmi->cvt_list);
hdmi->num_cvt++;
......@@ -410,6 +1009,106 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid)
return hdac_hdmi_query_cvt_params(&edev->hdac, cvt);
}
static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
{
struct hdac_ext_device *edev = pin->edev;
struct hdac_hdmi_priv *hdmi = edev->private_data;
struct hdac_hdmi_pcm *pcm;
int val;
pin->repoll_count = repoll;
pm_runtime_get_sync(&edev->hdac.dev);
val = snd_hdac_codec_read(&edev->hdac, pin->nid, 0,
AC_VERB_GET_PIN_SENSE, 0);
dev_dbg(&edev->hdac.dev, "Pin sense val %x for pin: %d\n",
val, pin->nid);
mutex_lock(&hdmi->pin_mutex);
pin->eld.monitor_present = !!(val & AC_PINSENSE_PRESENCE);
pin->eld.eld_valid = !!(val & AC_PINSENSE_ELDV);
pcm = hdac_hdmi_get_pcm(edev, pin);
if (!pin->eld.monitor_present || !pin->eld.eld_valid) {
dev_dbg(&edev->hdac.dev, "%s: disconnect for pin %d\n",
__func__, pin->nid);
/*
* PCMs are not registered during device probe, so don't
* report jack here. It will be done in usermode mux
* control select.
*/
if (pcm) {
dev_dbg(&edev->hdac.dev,
"jack report for pcm=%d\n", pcm->pcm_id);
snd_jack_report(pcm->jack, 0);
}
mutex_unlock(&hdmi->pin_mutex);
goto put_hdac_device;
}
if (pin->eld.monitor_present && pin->eld.eld_valid) {
/* TODO: use i915 component for reading ELD later */
if (hdac_hdmi_get_eld(&edev->hdac, pin->nid,
pin->eld.eld_buffer,
&pin->eld.eld_size) == 0) {
if (pcm) {
dev_dbg(&edev->hdac.dev,
"jack report for pcm=%d\n",
pcm->pcm_id);
snd_jack_report(pcm->jack, SND_JACK_AVOUT);
}
print_hex_dump_bytes("ELD: ", DUMP_PREFIX_OFFSET,
pin->eld.eld_buffer, pin->eld.eld_size);
} else {
pin->eld.monitor_present = false;
pin->eld.eld_valid = false;
if (pcm) {
dev_dbg(&edev->hdac.dev,
"jack report for pcm=%d\n",
pcm->pcm_id);
snd_jack_report(pcm->jack, 0);
}
}
}
mutex_unlock(&hdmi->pin_mutex);
/*
* Sometimes the pin_sense may present invalid monitor
* present and eld_valid. If ELD data is not valid, loop few
* more times to get correct pin sense and valid ELD.
*/
if ((!pin->eld.monitor_present || !pin->eld.eld_valid) && repoll)
schedule_delayed_work(&pin->work, msecs_to_jiffies(300));
put_hdac_device:
pm_runtime_put_sync(&edev->hdac.dev);
}
static void hdac_hdmi_repoll_eld(struct work_struct *work)
{
struct hdac_hdmi_pin *pin =
container_of(to_delayed_work(work), struct hdac_hdmi_pin, work);
/* picked from legacy HDA driver */
if (pin->repoll_count++ > 6)
pin->repoll_count = 0;
hdac_hdmi_present_sense(pin, pin->repoll_count);
}
static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
{
struct hdac_hdmi_priv *hdmi = edev->private_data;
......@@ -424,6 +1123,120 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
list_add_tail(&pin->head, &hdmi->pin_list);
hdmi->num_pin++;
pin->edev = edev;
INIT_DELAYED_WORK(&pin->work, hdac_hdmi_repoll_eld);
return 0;
}
#define INTEL_VENDOR_NID 0x08
#define INTEL_GET_VENDOR_VERB 0xf81
#define INTEL_SET_VENDOR_VERB 0x781
#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */
#define INTEL_EN_ALL_PIN_CVTS 0x01 /* enable 2nd & 3rd pins and convertors */
static void hdac_hdmi_skl_enable_all_pins(struct hdac_device *hdac)
{
unsigned int vendor_param;
vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0,
INTEL_GET_VENDOR_VERB, 0);
if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS)
return;
vendor_param |= INTEL_EN_ALL_PIN_CVTS;
vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0,
INTEL_SET_VENDOR_VERB, vendor_param);
if (vendor_param == -1)
return;
}
static void hdac_hdmi_skl_enable_dp12(struct hdac_device *hdac)
{
unsigned int vendor_param;
vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0,
INTEL_GET_VENDOR_VERB, 0);
if (vendor_param == -1 || vendor_param & INTEL_EN_DP12)
return;
/* enable DP1.2 mode */
vendor_param |= INTEL_EN_DP12;
vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0,
INTEL_SET_VENDOR_VERB, vendor_param);
if (vendor_param == -1)
return;
}
static struct snd_soc_dai_ops hdmi_dai_ops = {
.startup = hdac_hdmi_pcm_open,
.shutdown = hdac_hdmi_pcm_close,
.hw_params = hdac_hdmi_set_hw_params,
.prepare = hdac_hdmi_playback_prepare,
.trigger = hdac_hdmi_trigger,
.hw_free = hdac_hdmi_playback_cleanup,
};
/*
* Each converter can support a stream independently. So a dai is created
* based on the number of converter queried.
*/
static int hdac_hdmi_create_dais(struct hdac_device *hdac,
struct snd_soc_dai_driver **dais,
struct hdac_hdmi_priv *hdmi, int num_dais)
{
struct snd_soc_dai_driver *hdmi_dais;
struct hdac_hdmi_cvt *cvt;
char name[NAME_SIZE], dai_name[NAME_SIZE];
int i = 0;
u32 rates, bps;
unsigned int rate_max = 384000, rate_min = 8000;
u64 formats;
int ret;
hdmi_dais = devm_kzalloc(&hdac->dev,
(sizeof(*hdmi_dais) * num_dais),
GFP_KERNEL);
if (!hdmi_dais)
return -ENOMEM;
list_for_each_entry(cvt, &hdmi->cvt_list, head) {
ret = snd_hdac_query_supported_pcm(hdac, cvt->nid,
&rates, &formats, &bps);
if (ret)
return ret;
sprintf(dai_name, "intel-hdmi-hifi%d", i+1);
hdmi_dais[i].name = devm_kstrdup(&hdac->dev,
dai_name, GFP_KERNEL);
if (!hdmi_dais[i].name)
return -ENOMEM;
snprintf(name, sizeof(name), "hifi%d", i+1);
hdmi_dais[i].playback.stream_name =
devm_kstrdup(&hdac->dev, name, GFP_KERNEL);
if (!hdmi_dais[i].playback.stream_name)
return -ENOMEM;
/*
* Set caps based on capability queried from the converter.
* It will be constrained runtime based on ELD queried.
*/
hdmi_dais[i].playback.formats = formats;
hdmi_dais[i].playback.rates = rates;
hdmi_dais[i].playback.rate_max = rate_max;
hdmi_dais[i].playback.rate_min = rate_min;
hdmi_dais[i].playback.channels_min = 2;
hdmi_dais[i].playback.channels_max = 2;
hdmi_dais[i].ops = &hdmi_dai_ops;
i++;
}
*dais = hdmi_dais;
return 0;
}
......@@ -431,7 +1244,8 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
* Parse all nodes and store the cvt/pin nids in array
* Add one time initialization for pin and cvt widgets
*/
static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev)
static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev,
struct snd_soc_dai_driver **dais, int *num_dais)
{
hda_nid_t nid;
int i, num_nodes;
......@@ -439,6 +1253,9 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev)
struct hdac_hdmi_priv *hdmi = edev->private_data;
int ret;
hdac_hdmi_skl_enable_all_pins(hdac);
hdac_hdmi_skl_enable_dp12(hdac);
num_nodes = snd_hdac_get_sub_nodes(hdac, hdac->afg, &nid);
if (!nid || num_nodes <= 0) {
dev_warn(&hdac->dev, "HDMI: failed to get afg sub nodes\n");
......@@ -479,19 +1296,107 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev)
if (!hdmi->num_pin || !hdmi->num_cvt)
return -EIO;
ret = hdac_hdmi_create_dais(hdac, dais, hdmi, hdmi->num_cvt);
if (ret) {
dev_err(&hdac->dev, "Failed to create dais with err: %d\n",
ret);
return ret;
}
*num_dais = hdmi->num_cvt;
return hdac_hdmi_init_dai_map(edev);
}
static void hdac_hdmi_eld_notify_cb(void *aptr, int port)
{
struct hdac_ext_device *edev = aptr;
struct hdac_hdmi_priv *hdmi = edev->private_data;
struct hdac_hdmi_pin *pin;
struct snd_soc_codec *codec = edev->scodec;
/* Don't know how this mapping is derived */
hda_nid_t pin_nid = port + 0x04;
dev_dbg(&edev->hdac.dev, "%s: for pin: %d\n", __func__, pin_nid);
/*
* skip notification during system suspend (but not in runtime PM);
* the state will be updated at resume. Also since the ELD and
* connection states are updated in anyway at the end of the resume,
* we can skip it when received during PM process.
*/
if (snd_power_get_state(codec->component.card->snd_card) !=
SNDRV_CTL_POWER_D0)
return;
if (atomic_read(&edev->hdac.in_pm))
return;
list_for_each_entry(pin, &hdmi->pin_list, head) {
if (pin->nid == pin_nid)
hdac_hdmi_present_sense(pin, 1);
}
}
static struct i915_audio_component_audio_ops aops = {
.pin_eld_notify = hdac_hdmi_eld_notify_cb,
};
int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device)
{
char jack_name[NAME_SIZE];
struct snd_soc_codec *codec = dai->codec;
struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
struct snd_soc_dapm_context *dapm =
snd_soc_component_get_dapm(&codec->component);
struct hdac_hdmi_priv *hdmi = edev->private_data;
struct hdac_hdmi_pcm *pcm;
/*
* this is a new PCM device, create new pcm and
* add to the pcm list
*/
pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
if (!pcm)
return -ENOMEM;
pcm->pcm_id = device;
pcm->cvt = hdmi->dai_map[dai->id].cvt;
list_add_tail(&pcm->head, &hdmi->pcm_list);
sprintf(jack_name, "HDMI/DP, pcm=%d Jack", device);
return snd_jack_new(dapm->card->snd_card, jack_name,
SND_JACK_AVOUT, &pcm->jack, true, false);
}
EXPORT_SYMBOL_GPL(hdac_hdmi_jack_init);
static int hdmi_codec_probe(struct snd_soc_codec *codec)
{
struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
struct hdac_hdmi_priv *hdmi = edev->private_data;
struct snd_soc_dapm_context *dapm =
snd_soc_component_get_dapm(&codec->component);
struct hdac_hdmi_pin *pin;
int ret;
edev->scodec = codec;
create_fill_widget_route_map(dapm, &hdmi->dai_map[0]);
ret = create_fill_widget_route_map(dapm);
if (ret < 0)
return ret;
aops.audio_ptr = edev;
ret = snd_hdac_i915_register_notifier(&aops);
if (ret < 0) {
dev_err(&edev->hdac.dev, "notifier register failed: err: %d\n",
ret);
return ret;
}
list_for_each_entry(pin, &hdmi->pin_list, head)
hdac_hdmi_present_sense(pin, 1);
/* Imp: Store the card pointer in hda_codec */
edev->card = dapm->card->snd_card;
......@@ -515,44 +1420,73 @@ static int hdmi_codec_remove(struct snd_soc_codec *codec)
return 0;
}
#ifdef CONFIG_PM
static int hdmi_codec_resume(struct snd_soc_codec *codec)
{
struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
struct hdac_hdmi_priv *hdmi = edev->private_data;
struct hdac_hdmi_pin *pin;
struct hdac_device *hdac = &edev->hdac;
struct hdac_bus *bus = hdac->bus;
int err;
unsigned long timeout;
hdac_hdmi_skl_enable_all_pins(&edev->hdac);
hdac_hdmi_skl_enable_dp12(&edev->hdac);
/* Power up afg */
if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0)) {
snd_hdac_codec_write(hdac, hdac->afg, 0,
AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
/* Wait till power state is set to D0 */
timeout = jiffies + msecs_to_jiffies(1000);
while (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0)
&& time_before(jiffies, timeout)) {
msleep(50);
}
}
/*
* As the ELD notify callback request is not entertained while the
* device is in suspend state. Need to manually check detection of
* all pins here.
*/
list_for_each_entry(pin, &hdmi->pin_list, head)
hdac_hdmi_present_sense(pin, 1);
/*
* Codec power is turned ON during controller resume.
* Turn it OFF here
*/
err = snd_hdac_display_power(bus, false);
if (err < 0) {
dev_err(bus->dev,
"Cannot turn OFF display power on i915, err: %d\n",
err);
return err;
}
return 0;
}
#else
#define hdmi_codec_resume NULL
#endif
static struct snd_soc_codec_driver hdmi_hda_codec = {
.probe = hdmi_codec_probe,
.remove = hdmi_codec_remove,
.resume = hdmi_codec_resume,
.idle_bias_off = true,
};
static struct snd_soc_dai_ops hdmi_dai_ops = {
.startup = hdac_hdmi_pcm_open,
.shutdown = hdac_hdmi_pcm_close,
.hw_params = hdac_hdmi_set_hw_params,
.prepare = hdac_hdmi_playback_prepare,
.hw_free = hdac_hdmi_playback_cleanup,
};
static struct snd_soc_dai_driver hdmi_dais[] = {
{ .name = "intel-hdmi-hif1",
.playback = {
.stream_name = "hif1",
.channels_min = 2,
.channels_max = 2,
.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,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE |
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
.ops = &hdmi_dai_ops,
},
};
static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
{
struct hdac_device *codec = &edev->hdac;
struct hdac_hdmi_priv *hdmi_priv;
struct snd_soc_dai_driver *hdmi_dais = NULL;
int num_dais = 0;
int ret = 0;
hdmi_priv = devm_kzalloc(&codec->dev, sizeof(*hdmi_priv), GFP_KERNEL);
......@@ -565,14 +1499,31 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
INIT_LIST_HEAD(&hdmi_priv->pin_list);
INIT_LIST_HEAD(&hdmi_priv->cvt_list);
INIT_LIST_HEAD(&hdmi_priv->pcm_list);
mutex_init(&hdmi_priv->pin_mutex);
ret = hdac_hdmi_parse_and_map_nid(edev);
if (ret < 0)
/*
* Turned off in the runtime_suspend during the first explicit
* pm_runtime_suspend call.
*/
ret = snd_hdac_display_power(edev->hdac.bus, true);
if (ret < 0) {
dev_err(&edev->hdac.dev,
"Cannot turn on display power on i915 err: %d\n",
ret);
return ret;
}
ret = hdac_hdmi_parse_and_map_nid(edev, &hdmi_dais, &num_dais);
if (ret < 0) {
dev_err(&codec->dev,
"Failed in parse and map nid with err: %d\n", ret);
return ret;
}
/* ASoC specific initialization */
return snd_soc_register_codec(&codec->dev, &hdmi_hda_codec,
hdmi_dais, ARRAY_SIZE(hdmi_dais));
hdmi_dais, num_dais);
}
static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)
......@@ -580,11 +1531,20 @@ static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)
struct hdac_hdmi_priv *hdmi = edev->private_data;
struct hdac_hdmi_pin *pin, *pin_next;
struct hdac_hdmi_cvt *cvt, *cvt_next;
struct hdac_hdmi_pcm *pcm, *pcm_next;
snd_soc_unregister_codec(&edev->hdac.dev);
list_for_each_entry_safe(pcm, pcm_next, &hdmi->pcm_list, head) {
pcm->cvt = NULL;
pcm->pin = NULL;
list_del(&pcm->head);
kfree(pcm);
}
list_for_each_entry_safe(cvt, cvt_next, &hdmi->cvt_list, head) {
list_del(&cvt->head);
kfree(cvt->name);
kfree(cvt);
}
......@@ -602,6 +1562,7 @@ static int hdac_hdmi_runtime_suspend(struct device *dev)
struct hdac_ext_device *edev = to_hda_ext_device(dev);
struct hdac_device *hdac = &edev->hdac;
struct hdac_bus *bus = hdac->bus;
unsigned long timeout;
int err;
dev_dbg(dev, "Enter: %s\n", __func__);
......@@ -611,10 +1572,19 @@ static int hdac_hdmi_runtime_suspend(struct device *dev)
return 0;
/* Power down afg */
if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3))
if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3)) {
snd_hdac_codec_write(hdac, hdac->afg, 0,
AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
/* Wait till power state is set to D3 */
timeout = jiffies + msecs_to_jiffies(1000);
while (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3)
&& time_before(jiffies, timeout)) {
msleep(50);
}
}
err = snd_hdac_display_power(bus, false);
if (err < 0) {
dev_err(bus->dev, "Cannot turn on display power on i915\n");
......@@ -643,6 +1613,9 @@ static int hdac_hdmi_runtime_resume(struct device *dev)
return err;
}
hdac_hdmi_skl_enable_all_pins(&edev->hdac);
hdac_hdmi_skl_enable_dp12(&edev->hdac);
/* Power up afg */
if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0))
snd_hdac_codec_write(hdac, hdac->afg, 0,
......@@ -661,6 +1634,7 @@ static const struct dev_pm_ops hdac_hdmi_pm = {
static const struct hda_device_id hdmi_list[] = {
HDA_CODEC_EXT_ENTRY(0x80862809, 0x100000, "Skylake HDMI", 0),
HDA_CODEC_EXT_ENTRY(0x8086280a, 0x100000, "Broxton HDMI", 0),
{}
};
......
#ifndef __HDAC_HDMI_H__
#define __HDAC_HDMI_H__
int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int pcm);
#endif /* __HDAC_HDMI_H__ */
......@@ -163,6 +163,7 @@ config SND_SOC_INTEL_SKYLAKE
tristate
select SND_HDA_EXT_CORE
select SND_SOC_TOPOLOGY
select SND_HDA_I915
select SND_SOC_INTEL_SST
config SND_SOC_INTEL_SKL_RT286_MACH
......@@ -172,6 +173,7 @@ config SND_SOC_INTEL_SKL_RT286_MACH
select SND_SOC_INTEL_SKYLAKE
select SND_SOC_RT286
select SND_SOC_DMIC
select SND_SOC_HDAC_HDMI
help
This adds support for ASoC machine driver for Skylake platforms
with RT286 I2S audio codec.
......@@ -186,6 +188,7 @@ config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH
select SND_SOC_NAU8825
select SND_SOC_SSM4567
select SND_SOC_DMIC
select SND_SOC_HDAC_HDMI
help
This adds support for ASoC Onboard Codec I2S machine driver. This will
create an alsa sound card for NAU88L25 + SSM4567.
......@@ -200,6 +203,7 @@ config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH
select SND_SOC_NAU8825
select SND_SOC_MAX98357A
select SND_SOC_DMIC
select SND_SOC_HDAC_HDMI
help
This adds support for ASoC Onboard Codec I2S machine driver. This will
create an alsa sound card for NAU88L25 + MAX98357A.
......
......@@ -342,6 +342,10 @@ static struct sst_acpi_mach sst_acpi_chv[] = {
&chv_platform_data },
{"193C9890", "cht-bsw-max98090", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
&chv_platform_data },
/* some CHT-T platforms rely on RT5640, use Baytrail machine driver */
{"10EC5640", "bytcr_rt5640", "intel/fw_sst_22a8.bin", "bytcr_rt5640", NULL,
&chv_platform_data },
{},
};
......
......@@ -318,7 +318,6 @@ void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx,
union ipc_header_high msg_high;
u32 msg_low;
struct ipc_dsp_hdr *dsp_hdr;
unsigned int cmd_id;
msg_high = msg->mrfld_header.p.header_high;
msg_low = msg->mrfld_header.p.header_low_payload;
......@@ -357,7 +356,6 @@ void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx,
return;
/* Copy command id so that we can use to put sst to reset */
dsp_hdr = (struct ipc_dsp_hdr *)data;
cmd_id = dsp_hdr->cmd_id;
dev_dbg(sst_drv_ctx->dev, "cmd_id %d\n", dsp_hdr->cmd_id);
if (sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
msg_high.part.drv_id,
......
......@@ -32,6 +32,18 @@
#include "../atom/sst-atom-controls.h"
#include "../common/sst-acpi.h"
enum {
BYT_RT5640_DMIC1_MAP,
BYT_RT5640_DMIC2_MAP,
BYT_RT5640_IN1_MAP,
};
#define BYT_RT5640_MAP(quirk) ((quirk) & 0xff)
#define BYT_RT5640_DMIC_EN BIT(16)
static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP |
BYT_RT5640_DMIC_EN;
static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
......@@ -70,18 +82,6 @@ static const struct snd_soc_dapm_route byt_rt5640_intmic_in1_map[] = {
{"IN1P", NULL, "Internal Mic"},
};
enum {
BYT_RT5640_DMIC1_MAP,
BYT_RT5640_DMIC2_MAP,
BYT_RT5640_IN1_MAP,
};
#define BYT_RT5640_MAP(quirk) ((quirk) & 0xff)
#define BYT_RT5640_DMIC_EN BIT(16)
static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP |
BYT_RT5640_DMIC_EN;
static const struct snd_kcontrol_new byt_rt5640_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
......@@ -174,7 +174,6 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
return ret;
}
dmi_check_system(byt_rt5640_quirk_table);
switch (BYT_RT5640_MAP(byt_rt5640_quirk)) {
case BYT_RT5640_IN1_MAP:
custom_map = byt_rt5640_intmic_in1_map;
......@@ -341,15 +340,34 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
{
int ret_val = 0;
struct sst_acpi_mach *mach;
const char *i2c_name = NULL;
int i;
int dai_index;
/* register the soc card */
byt_rt5640_card.dev = &pdev->dev;
mach = byt_rt5640_card.dev->platform_data;
/* fix index of codec dai */
dai_index = MERR_DPCM_COMPR + 1;
for (i = 0; i < ARRAY_SIZE(byt_rt5640_dais); i++) {
if (!strcmp(byt_rt5640_dais[i].codec_name, "i2c-10EC5640:00")) {
dai_index = i;
break;
}
}
/* fixup codec name based on HID */
i2c_name = sst_acpi_find_name_from_hid(mach->id);
if (i2c_name != NULL) {
snprintf(byt_rt5640_codec_name, sizeof(byt_rt5640_codec_name),
"%s%s%s", "i2c-", mach->id, ":00");
byt_rt5640_dais[MERR_DPCM_COMPR+1].codec_name = byt_rt5640_codec_name;
"%s%s", "i2c-", i2c_name);
byt_rt5640_dais[dai_index].codec_name = byt_rt5640_codec_name;
}
/* check quirks before creating card */
dmi_check_system(byt_rt5640_quirk_table);
ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5640_card);
......
......@@ -287,33 +287,20 @@ static struct snd_soc_card snd_soc_card_cht = {
.num_controls = ARRAY_SIZE(cht_mc_controls),
};
static acpi_status snd_acpi_codec_match(acpi_handle handle, u32 level,
void *context, void **ret)
{
*(bool *)context = true;
return AE_OK;
}
static int snd_cht_mc_probe(struct platform_device *pdev)
{
int ret_val = 0;
bool found = false;
struct cht_mc_private *drv;
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
if (!drv)
return -ENOMEM;
if (ACPI_SUCCESS(acpi_get_devices(
"104C227E",
snd_acpi_codec_match,
&found, NULL)) && found) {
drv->ts3a227e_present = true;
} else {
drv->ts3a227e_present = acpi_dev_present("104C227E");
if (!drv->ts3a227e_present) {
/* no need probe TI jack detection chip */
snd_soc_card_cht.aux_dev = NULL;
snd_soc_card_cht.num_aux_devs = 0;
drv->ts3a227e_present = false;
}
/* register the soc card */
......
......@@ -147,6 +147,17 @@ static const struct snd_kcontrol_new cht_mc_controls[] = {
SOC_DAPM_PIN_SWITCH("Ext Spk"),
};
static struct snd_soc_jack_pin cht_bsw_jack_pins[] = {
{
.pin = "Headphone",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
};
static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
......@@ -202,9 +213,9 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
else
jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE;
ret = snd_soc_card_jack_new(runtime->card, "Headset Jack",
ret = snd_soc_card_jack_new(runtime->card, "Headset",
jack_type, &ctx->jack,
NULL, 0);
cht_bsw_jack_pins, ARRAY_SIZE(cht_bsw_jack_pins));
if (ret) {
dev_err(runtime->dev, "Headset jack creation failed %d\n", ret);
return ret;
......@@ -333,20 +344,12 @@ static struct cht_acpi_card snd_soc_cards[] = {
{"10EC5650", CODEC_TYPE_RT5650, &snd_soc_card_chtrt5650},
};
static acpi_status snd_acpi_codec_match(acpi_handle handle, u32 level,
void *context, void **ret)
{
*(bool *)context = true;
return AE_OK;
}
static int snd_cht_mc_probe(struct platform_device *pdev)
{
int ret_val = 0;
int i;
struct cht_mc_private *drv;
struct snd_soc_card *card = snd_soc_cards[0].soc_card;
bool found = false;
char codec_name[16];
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
......@@ -354,10 +357,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(snd_soc_cards); i++) {
if (ACPI_SUCCESS(acpi_get_devices(
snd_soc_cards[i].codec_id,
snd_acpi_codec_match,
&found, NULL)) && found) {
if (acpi_dev_present(snd_soc_cards[i].codec_id)) {
dev_dbg(&pdev->dev,
"found codec %s\n", snd_soc_cards[i].codec_id);
card = snd_soc_cards[i].soc_card;
......
......@@ -22,6 +22,7 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "../../codecs/nau8825.h"
#include "../../codecs/hdac_hdmi.h"
#define SKL_NUVOTON_CODEC_DAI "nau8825-hifi"
#define SKL_MAXIM_CODEC_DAI "HiFi"
......@@ -29,6 +30,16 @@
static struct snd_soc_jack skylake_headset;
static struct snd_soc_card skylake_audio_card;
enum {
SKL_DPCM_AUDIO_PB = 0,
SKL_DPCM_AUDIO_CP,
SKL_DPCM_AUDIO_REF_CP,
SKL_DPCM_AUDIO_DMIC_CP,
SKL_DPCM_AUDIO_HDMI1_PB,
SKL_DPCM_AUDIO_HDMI2_PB,
SKL_DPCM_AUDIO_HDMI3_PB,
};
static inline struct snd_soc_dai *skl_get_codec_dai(struct snd_soc_card *card)
{
struct snd_soc_pcm_runtime *rtd;
......@@ -87,7 +98,6 @@ static const struct snd_soc_dapm_widget skylake_widgets[] = {
SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_SPK("Spk", NULL),
SND_SOC_DAPM_MIC("SoC DMIC", NULL),
SND_SOC_DAPM_SINK("WoV Sink"),
SND_SOC_DAPM_SPK("DP", NULL),
SND_SOC_DAPM_SPK("HDMI", NULL),
SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
......@@ -107,7 +117,6 @@ static const struct snd_soc_dapm_route skylake_map[] = {
{ "MIC", NULL, "Headset Mic" },
{ "DMic", NULL, "SoC DMIC" },
{"WoV Sink", NULL, "hwd_in sink"},
{"HDMI", NULL, "hif5 Output"},
{"DP", NULL, "hif6 Output"},
......@@ -124,8 +133,14 @@ static const struct snd_soc_dapm_route skylake_map[] = {
/* DMIC */
{ "dmic01_hifi", NULL, "DMIC01 Rx" },
{ "DMIC01 Rx", NULL, "DMIC AIF" },
{ "hifi1", NULL, "iDisp Tx"},
{ "iDisp Tx", NULL, "iDisp_out"},
{ "hifi3", NULL, "iDisp3 Tx"},
{ "iDisp3 Tx", NULL, "iDisp3_out"},
{ "hifi2", NULL, "iDisp2 Tx"},
{ "iDisp2 Tx", NULL, "iDisp2_out"},
{ "hifi1", NULL, "iDisp1 Tx"},
{ "iDisp1 Tx", NULL, "iDisp1_out"},
{ "Headphone Jack", NULL, "Platform Clock" },
{ "Headset Mic", NULL, "Platform Clock" },
};
......@@ -171,11 +186,31 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
nau8825_enable_jack_detect(codec, &skylake_headset);
snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "WoV Sink");
return ret;
}
static int skylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *dai = rtd->codec_dai;
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB);
}
static int skylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *dai = rtd->codec_dai;
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI2_PB);
}
static int skylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *dai = rtd->codec_dai;
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI3_PB);
}
static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dapm_context *dapm;
......@@ -318,7 +353,7 @@ static struct snd_soc_ops skylaye_refcap_ops = {
/* skylake digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link skylake_dais[] = {
/* Front End DAI links */
{
[SKL_DPCM_AUDIO_PB] = {
.name = "Skl Audio Port",
.stream_name = "Audio",
.cpu_dai_name = "System Pin",
......@@ -333,7 +368,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
.dpcm_playback = 1,
.ops = &skylake_nau8825_fe_ops,
},
{
[SKL_DPCM_AUDIO_CP] = {
.name = "Skl Audio Capture Port",
.stream_name = "Audio Record",
.cpu_dai_name = "System Pin",
......@@ -347,7 +382,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
.dpcm_capture = 1,
.ops = &skylake_nau8825_fe_ops,
},
{
[SKL_DPCM_AUDIO_REF_CP] = {
.name = "Skl Audio Reference cap",
.stream_name = "Wake on Voice",
.cpu_dai_name = "Reference Pin",
......@@ -361,7 +396,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
.dynamic = 1,
.ops = &skylaye_refcap_ops,
},
{
[SKL_DPCM_AUDIO_DMIC_CP] = {
.name = "Skl Audio DMIC cap",
.stream_name = "dmiccap",
.cpu_dai_name = "DMIC Pin",
......@@ -374,15 +409,45 @@ static struct snd_soc_dai_link skylake_dais[] = {
.dynamic = 1,
.ops = &skylake_dmic_ops,
},
{
.name = "Skl HDMI Port",
.stream_name = "Hdmi",
.cpu_dai_name = "HDMI Pin",
[SKL_DPCM_AUDIO_HDMI1_PB] = {
.name = "Skl HDMI Port1",
.stream_name = "Hdmi1",
.cpu_dai_name = "HDMI1 Pin",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.platform_name = "0000:00:1f.3",
.dpcm_playback = 1,
.init = NULL,
.trigger = {
SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.nonatomic = 1,
.dynamic = 1,
},
[SKL_DPCM_AUDIO_HDMI2_PB] = {
.name = "Skl HDMI Port2",
.stream_name = "Hdmi2",
.cpu_dai_name = "HDMI2 Pin",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.platform_name = "0000:00:1f.3",
.dpcm_playback = 1,
.init = NULL,
.trigger = {
SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.nonatomic = 1,
.dynamic = 1,
},
[SKL_DPCM_AUDIO_HDMI3_PB] = {
.name = "Skl HDMI Port3",
.stream_name = "Hdmi3",
.cpu_dai_name = "HDMI3 Pin",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.platform_name = "0000:00:1f.3",
.trigger = {
SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dpcm_playback = 1,
.init = NULL,
.nonatomic = 1,
.dynamic = 1,
},
......@@ -407,7 +472,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
{
/* SSP1 - Codec */
.name = "SSP1-Codec",
.be_id = 0,
.be_id = 1,
.cpu_dai_name = "SSP1 Pin",
.platform_name = "0000:00:1f.3",
.no_pcm = 1,
......@@ -424,7 +489,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
},
{
.name = "dmic01",
.be_id = 1,
.be_id = 2,
.cpu_dai_name = "DMIC01 Pin",
.codec_name = "dmic-codec",
.codec_dai_name = "dmic-hifi",
......@@ -435,13 +500,36 @@ static struct snd_soc_dai_link skylake_dais[] = {
.no_pcm = 1,
},
{
.name = "iDisp",
.name = "iDisp1",
.be_id = 3,
.cpu_dai_name = "iDisp Pin",
.cpu_dai_name = "iDisp1 Pin",
.codec_name = "ehdaudio0D2",
.codec_dai_name = "intel-hdmi-hifi1",
.platform_name = "0000:00:1f.3",
.dpcm_playback = 1,
.init = skylake_hdmi1_init,
.no_pcm = 1,
},
{
.name = "iDisp2",
.be_id = 4,
.cpu_dai_name = "iDisp2 Pin",
.codec_name = "ehdaudio0D2",
.codec_dai_name = "intel-hdmi-hifi2",
.platform_name = "0000:00:1f.3",
.init = skylake_hdmi2_init,
.dpcm_playback = 1,
.no_pcm = 1,
},
{
.name = "iDisp3",
.be_id = 5,
.cpu_dai_name = "iDisp3 Pin",
.codec_name = "ehdaudio0D2",
.codec_dai_name = "intel-hdmi-hifi3",
.platform_name = "0000:00:1f.3",
.init = skylake_hdmi3_init,
.dpcm_playback = 1,
.no_pcm = 1,
},
};
......
......@@ -26,6 +26,7 @@
#include <sound/jack.h>
#include <sound/pcm_params.h>
#include "../../codecs/nau8825.h"
#include "../../codecs/hdac_hdmi.h"
#define SKL_NUVOTON_CODEC_DAI "nau8825-hifi"
#define SKL_SSM_CODEC_DAI "ssm4567-hifi"
......@@ -33,6 +34,16 @@
static struct snd_soc_jack skylake_headset;
static struct snd_soc_card skylake_audio_card;
enum {
SKL_DPCM_AUDIO_PB = 0,
SKL_DPCM_AUDIO_CP,
SKL_DPCM_AUDIO_REF_CP,
SKL_DPCM_AUDIO_DMIC_CP,
SKL_DPCM_AUDIO_HDMI1_PB,
SKL_DPCM_AUDIO_HDMI2_PB,
SKL_DPCM_AUDIO_HDMI3_PB,
};
static inline struct snd_soc_dai *skl_get_codec_dai(struct snd_soc_card *card)
{
struct snd_soc_pcm_runtime *rtd;
......@@ -92,7 +103,6 @@ static const struct snd_soc_dapm_widget skylake_widgets[] = {
SND_SOC_DAPM_SPK("Left Speaker", NULL),
SND_SOC_DAPM_SPK("Right Speaker", NULL),
SND_SOC_DAPM_MIC("SoC DMIC", NULL),
SND_SOC_DAPM_SINK("WoV Sink"),
SND_SOC_DAPM_SPK("DP", NULL),
SND_SOC_DAPM_SPK("HDMI", NULL),
SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
......@@ -113,8 +123,6 @@ static const struct snd_soc_dapm_route skylake_map[] = {
{"MIC", NULL, "Headset Mic"},
{"DMic", NULL, "SoC DMIC"},
{"WoV Sink", NULL, "hwd_in sink"},
{"HDMI", NULL, "hif5 Output"},
{"DP", NULL, "hif6 Output"},
/* CODEC BE connections */
......@@ -122,6 +130,11 @@ static const struct snd_soc_dapm_route skylake_map[] = {
{ "Right Playback", NULL, "ssp0 Tx"},
{ "ssp0 Tx", NULL, "codec0_out"},
/* IV feedback path */
{ "codec0_lp_in", NULL, "ssp0 Rx"},
{ "ssp0 Rx", NULL, "Left Capture Sense" },
{ "ssp0 Rx", NULL, "Right Capture Sense" },
{ "Playback", NULL, "ssp1 Tx"},
{ "ssp1 Tx", NULL, "codec1_out"},
......@@ -131,8 +144,14 @@ static const struct snd_soc_dapm_route skylake_map[] = {
/* DMIC */
{ "dmic01_hifi", NULL, "DMIC01 Rx" },
{ "DMIC01 Rx", NULL, "DMIC AIF" },
{ "hifi1", NULL, "iDisp Tx"},
{ "iDisp Tx", NULL, "iDisp_out"},
{ "hifi3", NULL, "iDisp3 Tx"},
{ "iDisp3 Tx", NULL, "iDisp3_out"},
{ "hifi2", NULL, "iDisp2 Tx"},
{ "iDisp2 Tx", NULL, "iDisp2_out"},
{ "hifi1", NULL, "iDisp1 Tx"},
{ "iDisp1 Tx", NULL, "iDisp1_out"},
{ "Headphone Jack", NULL, "Platform Clock" },
{ "Headset Mic", NULL, "Platform Clock" },
};
......@@ -197,11 +216,32 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
nau8825_enable_jack_detect(codec, &skylake_headset);
snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "WoV Sink");
return ret;
}
static int skylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *dai = rtd->codec_dai;
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB);
}
static int skylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *dai = rtd->codec_dai;
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI2_PB);
}
static int skylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *dai = rtd->codec_dai;
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI3_PB);
}
static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dapm_context *dapm;
......@@ -362,7 +402,7 @@ static struct snd_soc_ops skylaye_refcap_ops = {
/* skylake digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link skylake_dais[] = {
/* Front End DAI links */
{
[SKL_DPCM_AUDIO_PB] = {
.name = "Skl Audio Port",
.stream_name = "Audio",
.cpu_dai_name = "System Pin",
......@@ -377,7 +417,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
.dpcm_playback = 1,
.ops = &skylake_nau8825_fe_ops,
},
{
[SKL_DPCM_AUDIO_CP] = {
.name = "Skl Audio Capture Port",
.stream_name = "Audio Record",
.cpu_dai_name = "System Pin",
......@@ -391,7 +431,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
.dpcm_capture = 1,
.ops = &skylake_nau8825_fe_ops,
},
{
[SKL_DPCM_AUDIO_REF_CP] = {
.name = "Skl Audio Reference cap",
.stream_name = "Wake on Voice",
.cpu_dai_name = "Reference Pin",
......@@ -405,7 +445,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
.dynamic = 1,
.ops = &skylaye_refcap_ops,
},
{
[SKL_DPCM_AUDIO_DMIC_CP] = {
.name = "Skl Audio DMIC cap",
.stream_name = "dmiccap",
.cpu_dai_name = "DMIC Pin",
......@@ -418,13 +458,43 @@ static struct snd_soc_dai_link skylake_dais[] = {
.dynamic = 1,
.ops = &skylake_dmic_ops,
},
{
.name = "Skl HDMI Port",
.stream_name = "Hdmi",
.cpu_dai_name = "HDMI Pin",
[SKL_DPCM_AUDIO_HDMI1_PB] = {
.name = "Skl HDMI Port1",
.stream_name = "Hdmi1",
.cpu_dai_name = "HDMI1 Pin",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.platform_name = "0000:00:1f.3",
.dpcm_playback = 1,
.init = NULL,
.trigger = {
SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.nonatomic = 1,
.dynamic = 1,
},
[SKL_DPCM_AUDIO_HDMI2_PB] = {
.name = "Skl HDMI Port2",
.stream_name = "Hdmi2",
.cpu_dai_name = "HDMI2 Pin",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.platform_name = "0000:00:1f.3",
.dpcm_playback = 1,
.init = NULL,
.trigger = {
SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.nonatomic = 1,
.dynamic = 1,
},
[SKL_DPCM_AUDIO_HDMI3_PB] = {
.name = "Skl HDMI Port3",
.stream_name = "Hdmi3",
.cpu_dai_name = "HDMI3 Pin",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.platform_name = "0000:00:1f.3",
.trigger = {
SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dpcm_playback = 1,
.init = NULL,
.nonatomic = 1,
......@@ -448,11 +518,12 @@ static struct snd_soc_dai_link skylake_dais[] = {
.ignore_pmdown_time = 1,
.be_hw_params_fixup = skylake_ssp_fixup,
.dpcm_playback = 1,
.dpcm_capture = 1,
},
{
/* SSP1 - Codec */
.name = "SSP1-Codec",
.be_id = 0,
.be_id = 1,
.cpu_dai_name = "SSP1 Pin",
.platform_name = "0000:00:1f.3",
.no_pcm = 1,
......@@ -469,7 +540,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
},
{
.name = "dmic01",
.be_id = 1,
.be_id = 2,
.cpu_dai_name = "DMIC01 Pin",
.codec_name = "dmic-codec",
.codec_dai_name = "dmic-hifi",
......@@ -480,13 +551,36 @@ static struct snd_soc_dai_link skylake_dais[] = {
.no_pcm = 1,
},
{
.name = "iDisp",
.name = "iDisp1",
.be_id = 3,
.cpu_dai_name = "iDisp Pin",
.cpu_dai_name = "iDisp1 Pin",
.codec_name = "ehdaudio0D2",
.codec_dai_name = "intel-hdmi-hifi1",
.platform_name = "0000:00:1f.3",
.dpcm_playback = 1,
.init = skylake_hdmi1_init,
.no_pcm = 1,
},
{
.name = "iDisp2",
.be_id = 4,
.cpu_dai_name = "iDisp2 Pin",
.codec_name = "ehdaudio0D2",
.codec_dai_name = "intel-hdmi-hifi2",
.platform_name = "0000:00:1f.3",
.init = skylake_hdmi2_init,
.dpcm_playback = 1,
.no_pcm = 1,
},
{
.name = "iDisp3",
.be_id = 5,
.cpu_dai_name = "iDisp3 Pin",
.codec_name = "ehdaudio0D2",
.codec_dai_name = "intel-hdmi-hifi3",
.platform_name = "0000:00:1f.3",
.init = skylake_hdmi3_init,
.dpcm_playback = 1,
.no_pcm = 1,
},
};
......
......@@ -26,8 +26,20 @@
#include <sound/jack.h>
#include <sound/pcm_params.h>
#include "../../codecs/rt286.h"
#include "../../codecs/hdac_hdmi.h"
static struct snd_soc_jack skylake_headset;
enum {
SKL_DPCM_AUDIO_PB = 0,
SKL_DPCM_AUDIO_CP,
SKL_DPCM_AUDIO_REF_CP,
SKL_DPCM_AUDIO_DMIC_CP,
SKL_DPCM_AUDIO_HDMI1_PB,
SKL_DPCM_AUDIO_HDMI2_PB,
SKL_DPCM_AUDIO_HDMI3_PB,
};
/* Headset jack detection DAPM pins */
static struct snd_soc_jack_pin skylake_headset_pins[] = {
{
......@@ -52,7 +64,9 @@ static const struct snd_soc_dapm_widget skylake_widgets[] = {
SND_SOC_DAPM_MIC("Mic Jack", NULL),
SND_SOC_DAPM_MIC("DMIC2", NULL),
SND_SOC_DAPM_MIC("SoC DMIC", NULL),
SND_SOC_DAPM_SINK("WoV Sink"),
SND_SOC_DAPM_SPK("HDMI1", NULL),
SND_SOC_DAPM_SPK("HDMI2", NULL),
SND_SOC_DAPM_SPK("HDMI3", NULL),
};
static const struct snd_soc_dapm_route skylake_rt286_map[] = {
......@@ -70,7 +84,9 @@ static const struct snd_soc_dapm_route skylake_rt286_map[] = {
{"DMIC1 Pin", NULL, "DMIC2"},
{"DMic", NULL, "SoC DMIC"},
{"WoV Sink", NULL, "hwd_in sink"},
{"HDMI1", NULL, "hif5 Output"},
{"HDMI2", NULL, "hif6 Output"},
{"HDMI3", NULL, "hif7 Output"},
/* CODEC BE connections */
{ "AIF1 Playback", NULL, "ssp0 Tx"},
......@@ -84,8 +100,12 @@ static const struct snd_soc_dapm_route skylake_rt286_map[] = {
{ "dmic01_hifi", NULL, "DMIC01 Rx" },
{ "DMIC01 Rx", NULL, "DMIC AIF" },
{ "hif1", NULL, "iDisp Tx"},
{ "iDisp Tx", NULL, "iDisp_out"},
{ "hifi3", NULL, "iDisp3 Tx"},
{ "iDisp3 Tx", NULL, "iDisp3_out"},
{ "hifi2", NULL, "iDisp2 Tx"},
{ "iDisp2 Tx", NULL, "iDisp2_out"},
{ "hifi1", NULL, "iDisp1 Tx"},
{ "iDisp1 Tx", NULL, "iDisp1_out"},
};
......@@ -116,11 +136,17 @@ static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
rt286_mic_detect(codec, &skylake_headset);
snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "WoV Sink");
return 0;
}
static int skylake_hdmi_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *dai = rtd->codec_dai;
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB + dai->id);
}
static unsigned int rates[] = {
48000,
};
......@@ -249,7 +275,7 @@ static struct snd_soc_ops skylake_dmic_ops = {
/* skylake digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link skylake_rt286_dais[] = {
/* Front End DAI links */
{
[SKL_DPCM_AUDIO_PB] = {
.name = "Skl Audio Port",
.stream_name = "Audio",
.cpu_dai_name = "System Pin",
......@@ -266,7 +292,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
.dpcm_playback = 1,
.ops = &skylake_rt286_fe_ops,
},
{
[SKL_DPCM_AUDIO_CP] = {
.name = "Skl Audio Capture Port",
.stream_name = "Audio Record",
.cpu_dai_name = "System Pin",
......@@ -282,7 +308,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
.dpcm_capture = 1,
.ops = &skylake_rt286_fe_ops,
},
{
[SKL_DPCM_AUDIO_REF_CP] = {
.name = "Skl Audio Reference cap",
.stream_name = "refcap",
.cpu_dai_name = "Reference Pin",
......@@ -295,7 +321,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
.nonatomic = 1,
.dynamic = 1,
},
{
[SKL_DPCM_AUDIO_DMIC_CP] = {
.name = "Skl Audio DMIC cap",
.stream_name = "dmiccap",
.cpu_dai_name = "DMIC Pin",
......@@ -308,6 +334,42 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
.dynamic = 1,
.ops = &skylake_dmic_ops,
},
[SKL_DPCM_AUDIO_HDMI1_PB] = {
.name = "Skl HDMI Port1",
.stream_name = "Hdmi1",
.cpu_dai_name = "HDMI1 Pin",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.platform_name = "0000:00:1f.3",
.dpcm_playback = 1,
.init = NULL,
.nonatomic = 1,
.dynamic = 1,
},
[SKL_DPCM_AUDIO_HDMI2_PB] = {
.name = "Skl HDMI Port2",
.stream_name = "Hdmi2",
.cpu_dai_name = "HDMI2 Pin",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.platform_name = "0000:00:1f.3",
.dpcm_playback = 1,
.init = NULL,
.nonatomic = 1,
.dynamic = 1,
},
[SKL_DPCM_AUDIO_HDMI3_PB] = {
.name = "Skl HDMI Port3",
.stream_name = "Hdmi3",
.cpu_dai_name = "HDMI3 Pin",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.platform_name = "0000:00:1f.3",
.dpcm_playback = 1,
.init = NULL,
.nonatomic = 1,
.dynamic = 1,
},
/* Back End DAI links */
{
......@@ -341,6 +403,39 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
.dpcm_capture = 1,
.no_pcm = 1,
},
{
.name = "iDisp1",
.be_id = 2,
.cpu_dai_name = "iDisp1 Pin",
.codec_name = "ehdaudio0D2",
.codec_dai_name = "intel-hdmi-hifi1",
.platform_name = "0000:00:1f.3",
.init = skylake_hdmi_init,
.dpcm_playback = 1,
.no_pcm = 1,
},
{
.name = "iDisp2",
.be_id = 3,
.cpu_dai_name = "iDisp2 Pin",
.codec_name = "ehdaudio0D2",
.codec_dai_name = "intel-hdmi-hifi2",
.platform_name = "0000:00:1f.3",
.init = skylake_hdmi_init,
.dpcm_playback = 1,
.no_pcm = 1,
},
{
.name = "iDisp3",
.be_id = 4,
.cpu_dai_name = "iDisp3 Pin",
.codec_name = "ehdaudio0D2",
.codec_dai_name = "intel-hdmi-hifi3",
.platform_name = "0000:00:1f.3",
.init = skylake_hdmi_init,
.dpcm_playback = 1,
.no_pcm = 1,
},
};
/* skylake audio machine driver for SPT + RT286S */
......
......@@ -14,6 +14,9 @@
#include <linux/acpi.h>
/* translation fron HID to I2C name, needed for DAI codec_name */
const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]);
/* acpi match */
struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines);
......
......@@ -317,6 +317,7 @@ struct sst_dsp {
struct skl_cl_dev cl_dev;
u32 intr_status;
const struct firmware *fw;
struct snd_dma_buffer dmab;
};
/* Size optimised DRAM/IRAM memcpy */
......
......@@ -13,17 +13,53 @@
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include "sst-acpi.h"
static acpi_status sst_acpi_find_name(acpi_handle handle, u32 level,
void *context, void **ret)
{
struct acpi_device *adev;
const char *name = NULL;
if (acpi_bus_get_device(handle, &adev))
return AE_OK;
if (adev->status.present && adev->status.functional) {
name = acpi_dev_name(adev);
*(const char **)ret = name;
return AE_CTRL_TERMINATE;
}
return AE_OK;
}
const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN])
{
const char *name = NULL;
acpi_status status;
status = acpi_get_devices(hid, sst_acpi_find_name, NULL,
(void **)&name);
if (ACPI_FAILURE(status) || name[0] == '\0')
return NULL;
return name;
}
EXPORT_SYMBOL_GPL(sst_acpi_find_name_from_hid);
static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level,
void *context, void **ret)
{
unsigned long long sta;
acpi_status status;
*(bool *)context = true;
status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_PRESENT))
*(bool *)context = false;
return AE_OK;
}
......@@ -37,7 +73,6 @@ struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines)
sst_acpi_mach_match,
&found, NULL)) && found)
return mach;
return NULL;
}
EXPORT_SYMBOL_GPL(sst_acpi_find_machine);
......
......@@ -72,17 +72,47 @@ static void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable)
skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)&mask);
}
static struct skl_dsp_loader_ops skl_get_loader_ops(void)
{
struct skl_dsp_loader_ops loader_ops;
memset(&loader_ops, 0, sizeof(struct skl_dsp_loader_ops));
loader_ops.alloc_dma_buf = skl_alloc_dma_buf;
loader_ops.free_dma_buf = skl_free_dma_buf;
return loader_ops;
};
static const struct skl_dsp_ops dsp_ops[] = {
{
.id = 0x9d70,
.loader_ops = skl_get_loader_ops,
.init = skl_sst_dsp_init,
.cleanup = skl_sst_dsp_cleanup
},
};
static int skl_get_dsp_ops(int pci_id)
{
int i;
for (i = 0; i < ARRAY_SIZE(dsp_ops); i++) {
if (dsp_ops[i].id == pci_id)
return i;
}
return -EINVAL;
}
int skl_init_dsp(struct skl *skl)
{
void __iomem *mmio_base;
struct hdac_ext_bus *ebus = &skl->ebus;
struct hdac_bus *bus = ebus_to_hbus(ebus);
int irq = bus->irq;
struct skl_dsp_loader_ops loader_ops;
int ret;
loader_ops.alloc_dma_buf = skl_alloc_dma_buf;
loader_ops.free_dma_buf = skl_free_dma_buf;
int irq = bus->irq;
int ret, index;
/* enable ppcap interrupt */
snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true);
......@@ -95,8 +125,14 @@ int skl_init_dsp(struct skl *skl)
return -ENXIO;
}
ret = skl_sst_dsp_init(bus->dev, mmio_base, irq,
index = skl_get_dsp_ops(skl->pci->device);
if (index < 0)
return -EINVAL;
loader_ops = dsp_ops[index].loader_ops();
ret = dsp_ops[index].init(bus->dev, mmio_base, irq,
skl->fw_name, loader_ops, &skl->skl_sst);
if (ret < 0)
return ret;
......@@ -106,18 +142,26 @@ int skl_init_dsp(struct skl *skl)
return ret;
}
void skl_free_dsp(struct skl *skl)
int skl_free_dsp(struct skl *skl)
{
struct hdac_ext_bus *ebus = &skl->ebus;
struct hdac_bus *bus = ebus_to_hbus(ebus);
struct skl_sst *ctx = skl->skl_sst;
int index;
/* disable ppcap interrupt */
snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, false);
skl_sst_dsp_cleanup(bus->dev, ctx);
index = skl_get_dsp_ops(skl->pci->device);
if (index < 0)
return -EIO;
dsp_ops[index].cleanup(bus->dev, ctx);
if (ctx->dsp->addr.lpe)
iounmap(ctx->dsp->addr.lpe);
return 0;
}
int skl_suspend_dsp(struct skl *skl)
......@@ -238,9 +282,8 @@ static void skl_copy_copier_caps(struct skl_module_cfg *mconfig,
* Calculate the gatewat settings required for copier module, type of
* gateway and index of gateway to use
*/
static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
struct skl_module_cfg *mconfig,
struct skl_cpr_cfg *cpr_mconfig)
static u32 skl_get_node_id(struct skl_sst *ctx,
struct skl_module_cfg *mconfig)
{
union skl_connector_node_id node_id = {0};
union skl_ssp_dma_node ssp_node = {0};
......@@ -289,13 +332,24 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
break;
default:
cpr_mconfig->gtw_cfg.node_id = SKL_NON_GATEWAY_CPR_NODE_ID;
node_id.val = 0xFFFFFFFF;
break;
}
return node_id.val;
}
static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
struct skl_module_cfg *mconfig,
struct skl_cpr_cfg *cpr_mconfig)
{
cpr_mconfig->gtw_cfg.node_id = skl_get_node_id(ctx, mconfig);
if (cpr_mconfig->gtw_cfg.node_id == SKL_NON_GATEWAY_CPR_NODE_ID) {
cpr_mconfig->cpr_feature_mask = 0;
return;
}
cpr_mconfig->gtw_cfg.node_id = node_id.val;
if (SKL_CONN_SOURCE == mconfig->hw_conn_type)
cpr_mconfig->gtw_cfg.dma_buffer_size = 2 * mconfig->obs;
else
......@@ -307,6 +361,46 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
skl_copy_copier_caps(mconfig, cpr_mconfig);
}
#define DMA_CONTROL_ID 5
int skl_dsp_set_dma_control(struct skl_sst *ctx, struct skl_module_cfg *mconfig)
{
struct skl_dma_control *dma_ctrl;
struct skl_i2s_config_blob config_blob;
struct skl_ipc_large_config_msg msg = {0};
int err = 0;
/*
* if blob size is same as capablity size, then no dma control
* present so return
*/
if (mconfig->formats_config.caps_size == sizeof(config_blob))
return 0;
msg.large_param_id = DMA_CONTROL_ID;
msg.param_data_size = sizeof(struct skl_dma_control) +
mconfig->formats_config.caps_size;
dma_ctrl = kzalloc(msg.param_data_size, GFP_KERNEL);
if (dma_ctrl == NULL)
return -ENOMEM;
dma_ctrl->node_id = skl_get_node_id(ctx, mconfig);
/* size in dwords */
dma_ctrl->config_length = sizeof(config_blob) / 4;
memcpy(dma_ctrl->config_data, mconfig->formats_config.caps,
mconfig->formats_config.caps_size);
err = skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)dma_ctrl);
kfree(dma_ctrl);
return err;
}
static void skl_setup_out_format(struct skl_sst *ctx,
struct skl_module_cfg *mconfig,
struct skl_audio_data_format *out_fmt)
......
......@@ -145,3 +145,37 @@ struct nhlt_specific_cfg
return NULL;
}
static void skl_nhlt_trim_space(struct skl *skl)
{
char *s = skl->tplg_name;
int cnt;
int i;
cnt = 0;
for (i = 0; s[i]; i++) {
if (!isspace(s[i]))
s[cnt++] = s[i];
}
s[cnt] = '\0';
}
int skl_nhlt_update_topology_bin(struct skl *skl)
{
struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt;
struct hdac_bus *bus = ebus_to_hbus(&skl->ebus);
struct device *dev = bus->dev;
dev_dbg(dev, "oem_id %.6s, oem_table_id %8s oem_revision %d\n",
nhlt->header.oem_id, nhlt->header.oem_table_id,
nhlt->header.oem_revision);
snprintf(skl->tplg_name, sizeof(skl->tplg_name), "%x-%.6s-%.8s-%d%s",
skl->pci_id, nhlt->header.oem_id, nhlt->header.oem_table_id,
nhlt->header.oem_revision, "-tplg.bin");
skl_nhlt_trim_space(skl);
return 0;
}
......@@ -206,6 +206,23 @@ static int skl_get_format(struct snd_pcm_substream *substream,
return format_val;
}
static int skl_be_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct skl *skl = get_skl_ctx(dai->dev);
struct skl_sst *ctx = skl->skl_sst;
struct skl_module_cfg *mconfig;
if ((dai->playback_active > 1) || (dai->capture_active > 1))
return 0;
mconfig = skl_tplg_be_get_cpr_module(dai, substream->stream);
if (mconfig == NULL)
return -EINVAL;
return skl_dsp_set_dma_control(ctx, mconfig);
}
static int skl_pcm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
......@@ -458,7 +475,7 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream,
struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
struct hdac_ext_stream *link_dev;
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
struct skl_dma_params *dma_params;
struct hdac_ext_dma_params *dma_params;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct skl_pipe_params p_params = {0};
......@@ -470,11 +487,9 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream,
snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev);
/* set the stream tag in the codec dai dma params */
dma_params = (struct skl_dma_params *)
snd_soc_dai_get_dma_data(codec_dai, substream);
dma_params = snd_soc_dai_get_dma_data(codec_dai, substream);
if (dma_params)
dma_params->stream_tag = hdac_stream(link_dev)->stream_tag;
snd_soc_dai_set_dma_data(codec_dai, substream, (void *)dma_params);
p_params.s_fmt = snd_pcm_format_width(params_format(params));
p_params.ch = params_channels(params);
......@@ -588,6 +603,7 @@ static struct snd_soc_dai_ops skl_dmic_dai_ops = {
static struct snd_soc_dai_ops skl_be_ssp_dai_ops = {
.hw_params = skl_be_hw_params,
.prepare = skl_be_prepare,
};
static struct snd_soc_dai_ops skl_link_dai_ops = {
......@@ -660,6 +676,51 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
},
},
{
.name = "HDMI1 Pin",
.ops = &skl_pcm_dai_ops,
.playback = {
.stream_name = "HDMI1 Playback",
.channels_min = HDA_STEREO,
.channels_max = HDA_STEREO,
.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,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
},
{
.name = "HDMI2 Pin",
.ops = &skl_pcm_dai_ops,
.playback = {
.stream_name = "HDMI2 Playback",
.channels_min = HDA_STEREO,
.channels_max = HDA_STEREO,
.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,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
},
{
.name = "HDMI3 Pin",
.ops = &skl_pcm_dai_ops,
.playback = {
.stream_name = "HDMI3 Playback",
.channels_min = HDA_STEREO,
.channels_max = HDA_STEREO,
.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,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
},
/* BE CPU Dais */
{
......@@ -699,14 +760,41 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
},
},
{
.name = "iDisp Pin",
.name = "iDisp1 Pin",
.ops = &skl_link_dai_ops,
.playback = {
.stream_name = "iDisp Tx",
.stream_name = "iDisp1 Tx",
.channels_min = HDA_STEREO,
.channels_max = HDA_STEREO,
.rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE |
SNDRV_PCM_FMTBIT_S24_LE,
},
},
{
.name = "iDisp2 Pin",
.ops = &skl_link_dai_ops,
.playback = {
.stream_name = "iDisp2 Tx",
.channels_min = HDA_STEREO,
.channels_max = HDA_STEREO,
.rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|
SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE |
SNDRV_PCM_FMTBIT_S24_LE,
},
},
{
.name = "iDisp3 Pin",
.ops = &skl_link_dai_ops,
.playback = {
.stream_name = "iDisp3 Tx",
.channels_min = HDA_STEREO,
.channels_max = HDA_STEREO,
.rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|
SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE |
SNDRV_PCM_FMTBIT_S24_LE,
},
},
{
......@@ -863,7 +951,9 @@ static int skl_get_delay_from_lpib(struct hdac_ext_bus *ebus,
else
delay += hstream->bufsize;
}
delay = (hstream->bufsize == delay) ? 0 : delay;
if (hstream->bufsize == delay)
delay = 0;
if (delay >= hstream->period_bytes) {
dev_info(bus->dev,
......
......@@ -181,7 +181,7 @@ static int skl_dsp_core_power_down(struct sst_dsp *ctx)
"Power down");
}
static int skl_dsp_enable_core(struct sst_dsp *ctx)
int skl_dsp_enable_core(struct sst_dsp *ctx)
{
int ret;
......
......@@ -53,6 +53,10 @@ struct sst_dsp_device;
/* HIPCT */
#define SKL_ADSP_REG_HIPCT_BUSY BIT(31)
/* FW base IDs */
#define SKL_INSTANCE_ID 0
#define SKL_BASE_FW_MODULE_ID 0
/* Intel HD Audio SRAM Window 1 */
#define SKL_ADSP_SRAM1_BASE 0xA000
......@@ -144,6 +148,7 @@ int skl_cldma_prepare(struct sst_dsp *ctx);
void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state);
struct sst_dsp *skl_dsp_ctx_init(struct device *dev,
struct sst_dsp_device *sst_dev, int irq);
int skl_dsp_enable_core(struct sst_dsp *ctx);
int skl_dsp_disable_core(struct sst_dsp *ctx);
bool is_skl_dsp_running(struct sst_dsp *ctx);
irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id);
......
......@@ -35,9 +35,6 @@
#define SKL_ADSP_FW_STATUS SKL_ADSP_SRAM0_BASE
#define SKL_ADSP_ERROR_CODE (SKL_ADSP_FW_STATUS + 0x4)
#define SKL_INSTANCE_ID 0
#define SKL_BASE_FW_MODULE_ID 0
#define SKL_NUM_MODULES 1
static bool skl_check_fw_status(struct sst_dsp *ctx, u32 status)
......
......@@ -260,6 +260,65 @@ static void skl_tplg_update_buffer_size(struct skl_sst *ctx,
multiplier;
}
static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w,
struct skl_sst *ctx)
{
struct skl_module_cfg *m_cfg = w->priv;
int link_type, dir;
u32 ch, s_freq, s_fmt;
struct nhlt_specific_cfg *cfg;
struct skl *skl = get_skl_ctx(ctx->dev);
/* check if we already have blob */
if (m_cfg->formats_config.caps_size > 0)
return 0;
dev_dbg(ctx->dev, "Applying default cfg blob\n");
switch (m_cfg->dev_type) {
case SKL_DEVICE_DMIC:
link_type = NHLT_LINK_DMIC;
dir = SNDRV_PCM_STREAM_CAPTURE;
s_freq = m_cfg->in_fmt[0].s_freq;
s_fmt = m_cfg->in_fmt[0].bit_depth;
ch = m_cfg->in_fmt[0].channels;
break;
case SKL_DEVICE_I2S:
link_type = NHLT_LINK_SSP;
if (m_cfg->hw_conn_type == SKL_CONN_SOURCE) {
dir = SNDRV_PCM_STREAM_PLAYBACK;
s_freq = m_cfg->out_fmt[0].s_freq;
s_fmt = m_cfg->out_fmt[0].bit_depth;
ch = m_cfg->out_fmt[0].channels;
} else {
dir = SNDRV_PCM_STREAM_CAPTURE;
s_freq = m_cfg->in_fmt[0].s_freq;
s_fmt = m_cfg->in_fmt[0].bit_depth;
ch = m_cfg->in_fmt[0].channels;
}
break;
default:
return -EINVAL;
}
/* update the blob based on virtual bus_id and default params */
cfg = skl_get_ep_blob(skl, m_cfg->vbus_id, link_type,
s_fmt, ch, s_freq, dir);
if (cfg) {
m_cfg->formats_config.caps_size = cfg->size;
m_cfg->formats_config.caps = (u32 *) &cfg->caps;
} else {
dev_err(ctx->dev, "Blob NULL for id %x type %d dirn %d\n",
m_cfg->vbus_id, link_type, dir);
dev_err(ctx->dev, "PCM: ch %d, freq %d, fmt %d\n",
ch, s_freq, s_fmt);
return -EIO;
}
return 0;
}
static void skl_tplg_update_module_params(struct snd_soc_dapm_widget *w,
struct skl_sst *ctx)
{
......@@ -433,6 +492,9 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
return ret;
}
/* update blob if blob is null for be with default value */
skl_tplg_update_be_blob(w, ctx);
/*
* apply fix/conversion to module params based on
* FE/BE params
......@@ -545,6 +607,66 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
return 0;
}
/*
* Some modules require params to be set after the module is bound to
* all pins connected.
*
* The module provider initializes set_param flag for such modules and we
* send params after binding
*/
static int skl_tplg_set_module_bind_params(struct snd_soc_dapm_widget *w,
struct skl_module_cfg *mcfg, struct skl_sst *ctx)
{
int i, ret;
struct skl_module_cfg *mconfig = w->priv;
const struct snd_kcontrol_new *k;
struct soc_bytes_ext *sb;
struct skl_algo_data *bc;
struct skl_specific_cfg *sp_cfg;
/*
* check all out/in pins are in bind state.
* if so set the module param
*/
for (i = 0; i < mcfg->max_out_queue; i++) {
if (mcfg->m_out_pin[i].pin_state != SKL_PIN_BIND_DONE)
return 0;
}
for (i = 0; i < mcfg->max_in_queue; i++) {
if (mcfg->m_in_pin[i].pin_state != SKL_PIN_BIND_DONE)
return 0;
}
if (mconfig->formats_config.caps_size > 0 &&
mconfig->formats_config.set_params == SKL_PARAM_BIND) {
sp_cfg = &mconfig->formats_config;
ret = skl_set_module_params(ctx, sp_cfg->caps,
sp_cfg->caps_size,
sp_cfg->param_id, mconfig);
if (ret < 0)
return ret;
}
for (i = 0; i < w->num_kcontrols; i++) {
k = &w->kcontrol_news[i];
if (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
sb = (void *) k->private_value;
bc = (struct skl_algo_data *)sb->dobj.private;
if (bc->set_params == SKL_PARAM_BIND) {
ret = skl_set_module_params(ctx,
(u32 *)bc->params, bc->max,
bc->param_id, mconfig);
if (ret < 0)
return ret;
}
}
}
return 0;
}
static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w,
struct skl *skl,
struct snd_soc_dapm_widget *src_w,
......@@ -579,11 +701,19 @@ static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w,
sink = p->sink;
sink_mconfig = sink->priv;
if (src_mconfig->m_state == SKL_MODULE_UNINIT ||
sink_mconfig->m_state == SKL_MODULE_UNINIT)
continue;
/* Bind source to sink, mixin is always source */
ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig);
if (ret)
return ret;
/* set module params after bind */
skl_tplg_set_module_bind_params(src_w, src_mconfig, ctx);
skl_tplg_set_module_bind_params(sink, sink_mconfig, ctx);
/* Start sinks pipe first */
if (sink_mconfig->pipe->state != SKL_PIPE_STARTED) {
if (sink_mconfig->pipe->conn_type !=
......@@ -714,6 +844,10 @@ static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w,
if (ret)
return ret;
/* set module params after bind */
skl_tplg_set_module_bind_params(source, src_mconfig, ctx);
skl_tplg_set_module_bind_params(sink, sink_mconfig, ctx);
if (sink_mconfig->pipe->conn_type != SKL_PIPE_CONN_TYPE_FE)
ret = skl_run_pipe(ctx, sink_mconfig->pipe);
}
......@@ -1091,6 +1225,66 @@ skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream)
return NULL;
}
static struct skl_module_cfg *skl_get_mconfig_pb_cpr(
struct snd_soc_dai *dai, struct snd_soc_dapm_widget *w)
{
struct snd_soc_dapm_path *p;
struct skl_module_cfg *mconfig = NULL;
snd_soc_dapm_widget_for_each_source_path(w, p) {
if (w->endpoints[SND_SOC_DAPM_DIR_OUT] > 0) {
if (p->connect &&
(p->sink->id == snd_soc_dapm_aif_out) &&
p->source->priv) {
mconfig = p->source->priv;
return mconfig;
}
mconfig = skl_get_mconfig_pb_cpr(dai, p->source);
if (mconfig)
return mconfig;
}
}
return mconfig;
}
static struct skl_module_cfg *skl_get_mconfig_cap_cpr(
struct snd_soc_dai *dai, struct snd_soc_dapm_widget *w)
{
struct snd_soc_dapm_path *p;
struct skl_module_cfg *mconfig = NULL;
snd_soc_dapm_widget_for_each_sink_path(w, p) {
if (w->endpoints[SND_SOC_DAPM_DIR_IN] > 0) {
if (p->connect &&
(p->source->id == snd_soc_dapm_aif_in) &&
p->sink->priv) {
mconfig = p->sink->priv;
return mconfig;
}
mconfig = skl_get_mconfig_cap_cpr(dai, p->sink);
if (mconfig)
return mconfig;
}
}
return mconfig;
}
struct skl_module_cfg *
skl_tplg_be_get_cpr_module(struct snd_soc_dai *dai, int stream)
{
struct snd_soc_dapm_widget *w;
struct skl_module_cfg *mconfig;
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
w = dai->playback_widget;
mconfig = skl_get_mconfig_pb_cpr(dai, w);
} else {
w = dai->capture_widget;
mconfig = skl_get_mconfig_cap_cpr(dai, w);
}
return mconfig;
}
static u8 skl_tplg_be_link_type(int dev_type)
{
int ret;
......@@ -1464,7 +1658,6 @@ static int skl_init_algo_data(struct device *dev, struct soc_bytes_ext *be,
if (!ac->params)
return -ENOMEM;
if (dfw_ac->params)
memcpy(ac->params, dfw_ac->params, ac->max);
}
......@@ -1523,12 +1716,17 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus)
struct hdac_bus *bus = ebus_to_hbus(ebus);
struct skl *skl = ebus_to_skl(ebus);
ret = request_firmware(&fw, "dfw_sst.bin", bus->dev);
ret = request_firmware(&fw, skl->tplg_name, bus->dev);
if (ret < 0) {
dev_err(bus->dev, "tplg fw %s load failed with %d\n",
skl->tplg_name, ret);
ret = request_firmware(&fw, "dfw_sst.bin", bus->dev);
if (ret < 0) {
dev_err(bus->dev, "Fallback tplg fw %s load failed with %d\n",
"dfw_sst.bin", ret);
return ret;
}
}
/*
* The complete tplg for SKL is loaded as index 0, we don't use
......
......@@ -113,6 +113,29 @@ struct skl_cpr_gtw_cfg {
u32 config_data[1];
} __packed;
struct skl_i2s_config_blob {
u32 gateway_attrib;
u32 tdm_ts_group[8];
u32 ssc0;
u32 ssc1;
u32 sscto;
u32 sspsp;
u32 sstsa;
u32 ssrsa;
u32 ssc2;
u32 sspsp2;
u32 ssc3;
u32 ssioc;
u32 mdivc;
u32 mdivr;
} __packed;
struct skl_dma_control {
u32 node_id;
u32 config_length;
u32 config_data[1];
} __packed;
struct skl_cpr_cfg {
struct skl_base_cfg base_cfg;
struct skl_audio_data_format out_fmt;
......@@ -313,6 +336,8 @@ static inline struct skl *get_skl_ctx(struct device *dev)
int skl_tplg_be_update_params(struct snd_soc_dai *dai,
struct skl_pipe_params *params);
int skl_dsp_set_dma_control(struct skl_sst *ctx,
struct skl_module_cfg *mconfig);
void skl_tplg_set_be_dmic_config(struct snd_soc_dai *dai,
struct skl_pipe_params *params, int stream);
int skl_tplg_init(struct snd_soc_platform *platform,
......@@ -345,5 +370,7 @@ int skl_set_module_params(struct skl_sst *ctx, u32 *params, int size,
int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size,
u32 param_id, struct skl_module_cfg *mcfg);
struct skl_module_cfg *skl_tplg_be_get_cpr_module(struct snd_soc_dai *dai,
int stream);
enum skl_bitdepth skl_get_bit_depth(int params);
#endif
......@@ -144,7 +144,8 @@ enum module_pin_type {
enum skl_module_param_type {
SKL_PARAM_DEFAULT = 0,
SKL_PARAM_INIT,
SKL_PARAM_SET
SKL_PARAM_SET,
SKL_PARAM_BIND
};
struct skl_dfw_module_pin {
......
......@@ -28,6 +28,9 @@
#include <linux/firmware.h>
#include <sound/pcm.h>
#include "../common/sst-acpi.h"
#include <sound/hda_register.h>
#include <sound/hdaudio.h>
#include <sound/hda_i915.h>
#include "skl.h"
#include "skl-sst-dsp.h"
#include "skl-sst-ipc.h"
......@@ -243,6 +246,16 @@ static int skl_resume(struct device *dev)
struct hdac_bus *bus = ebus_to_hbus(ebus);
int ret;
/* Turned OFF in HDMI codec driver after codec reconfiguration */
if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) {
ret = snd_hdac_display_power(bus, true);
if (ret < 0) {
dev_err(bus->dev,
"Cannot turn on display power on i915\n");
return ret;
}
}
/*
* resume only when we are not in suspend active, otherwise need to
* restore the device
......@@ -481,6 +494,27 @@ static int skl_create(struct pci_dev *pci,
return 0;
}
static int skl_i915_init(struct hdac_bus *bus)
{
int err;
/*
* The HDMI codec is in GPU so we need to ensure that it is powered
* up and ready for probe
*/
err = snd_hdac_i915_init(bus);
if (err < 0)
return err;
err = snd_hdac_display_power(bus, true);
if (err < 0) {
dev_err(bus->dev, "Cannot turn on display power on i915\n");
return err;
}
return err;
}
static int skl_first_init(struct hdac_ext_bus *ebus)
{
struct skl *skl = ebus_to_skl(ebus);
......@@ -543,6 +577,12 @@ static int skl_first_init(struct hdac_ext_bus *ebus)
/* initialize chip */
skl_init_pci(skl);
if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) {
err = skl_i915_init(bus);
if (err < 0)
return err;
}
skl_init_chip(bus, true);
/* codec detection */
......@@ -573,11 +613,15 @@ static int skl_probe(struct pci_dev *pci,
if (err < 0)
goto out_free;
skl->pci_id = pci->device;
skl->nhlt = skl_nhlt_init(bus->dev);
if (skl->nhlt == NULL)
goto out_free;
skl_nhlt_update_topology_bin(skl);
pci_set_drvdata(skl->pci, ebus);
/* check if dsp is there */
......@@ -613,6 +657,14 @@ static int skl_probe(struct pci_dev *pci,
if (err < 0)
goto out_unregister;
if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) {
err = snd_hdac_display_power(bus, false);
if (err < 0) {
dev_err(bus->dev, "Cannot turn off display power on i915\n");
return err;
}
}
/*configure PM */
pm_runtime_put_noidle(bus->dev);
pm_runtime_allow(bus->dev);
......@@ -634,6 +686,31 @@ static int skl_probe(struct pci_dev *pci,
return err;
}
static void skl_shutdown(struct pci_dev *pci)
{
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
struct hdac_bus *bus = ebus_to_hbus(ebus);
struct hdac_stream *s;
struct hdac_ext_stream *stream;
struct skl *skl;
if (ebus == NULL)
return;
skl = ebus_to_skl(ebus);
if (skl->init_failed)
return;
snd_hdac_ext_stop_streams(ebus);
list_for_each_entry(s, &bus->stream_list, list) {
stream = stream_to_hdac_ext_stream(s);
snd_hdac_ext_stream_decouple(ebus, stream, false);
}
snd_hdac_bus_stop_chip(bus);
}
static void skl_remove(struct pci_dev *pci)
{
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
......@@ -642,6 +719,9 @@ static void skl_remove(struct pci_dev *pci)
if (skl->tplg)
release_firmware(skl->tplg);
if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI))
snd_hdac_i915_exit(&ebus->bus);
if (pci_dev_run_wake(pci))
pm_runtime_get_noresume(&pci->dev);
pci_dev_put(pci);
......@@ -662,11 +742,18 @@ static struct sst_acpi_mach sst_skl_devdata[] = {
{}
};
static struct sst_acpi_mach sst_bxtp_devdata[] = {
{ "INT343A", "bxt_alc298s_i2s", "intel/dsp_fw_bxtn.bin", NULL, NULL, NULL },
};
/* PCI IDs */
static const struct pci_device_id skl_ids[] = {
/* Sunrise Point-LP */
{ PCI_DEVICE(0x8086, 0x9d70),
.driver_data = (unsigned long)&sst_skl_devdata},
/* BXT-P */
{ PCI_DEVICE(0x8086, 0x5a98),
.driver_data = (unsigned long)&sst_bxtp_devdata},
{ 0, }
};
MODULE_DEVICE_TABLE(pci, skl_ids);
......@@ -677,6 +764,7 @@ static struct pci_driver skl_driver = {
.id_table = skl_ids,
.probe = skl_probe,
.remove = skl_remove,
.shutdown = skl_shutdown,
.driver = {
.pm = &skl_pm,
},
......
......@@ -73,6 +73,8 @@ struct skl {
struct list_head ppl_list;
const char *fw_name;
char tplg_name[64];
unsigned short pci_id;
const struct firmware *tplg;
int supend_active;
......@@ -88,6 +90,16 @@ struct skl_dma_params {
u8 stream_tag;
};
struct skl_dsp_ops {
int id;
struct skl_dsp_loader_ops (*loader_ops)(void);
int (*init)(struct device *dev, void __iomem *mmio_base,
int irq, const char *fw_name,
struct skl_dsp_loader_ops loader_ops,
struct skl_sst **skl_sst);
void (*cleanup)(struct device *dev, struct skl_sst *ctx);
};
int skl_platform_unregister(struct device *dev);
int skl_platform_register(struct device *dev);
......@@ -96,8 +108,9 @@ void skl_nhlt_free(void *addr);
struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance,
u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn);
int skl_nhlt_update_topology_bin(struct skl *skl);
int skl_init_dsp(struct skl *skl);
void skl_free_dsp(struct skl *skl);
int skl_free_dsp(struct skl *skl);
int skl_suspend_dsp(struct skl *skl);
int skl_resume_dsp(struct skl *skl);
#endif /* __SOUND_SOC_SKL_H */
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