drm/i915/dp: DP audio API changes for MST

DP MST provides the capability to send multiple video and audio streams
through a single port. This requires the API's between i915 and audio
drivers to distinguish between multiple audio capable displays that can be
connected to a port. Currently only the port identity is shared in the
APIs. This patch adds support for MST with an additional parameter
'int pipe'. The existing parameter 'port' does not change it's meaning.

pipe =
	MST	: display pipe that the stream originates from
	Non-MST	: -1

Affected APIs:
struct i915_audio_component_ops
-       int (*sync_audio_rate)(struct device *, int port, int rate);
+	int (*sync_audio_rate)(struct device *, int port, int pipe,
+	     int rate);

-       int (*get_eld)(struct device *, int port, bool *enabled,
-                       unsigned char *buf, int max_bytes);
+       int (*get_eld)(struct device *, int port, int pipe,
+		       bool *enabled, unsigned char *buf, int max_bytes);

struct i915_audio_component_audio_ops
-       void (*pin_eld_notify)(void *audio_ptr, int port);
+       void (*pin_eld_notify)(void *audio_ptr, int port, int pipe);

This patch makes dummy changes in the audio drivers (thanks Libin) for
build to succeed. The audio side drivers will send the right 'pipe' values
for MST in patches that will follow.

v2:
Renamed the new API parameter from 'dev_id' to 'pipe'. (Jim, Ville)
Included Asoc driver API compatibility changes from Jeeja.
Added WARN_ON() for invalid pipe in get_saved_encoder(). (Takashi)
Added comment for av_enc_map[] definition. (Takashi)

v3:
Fixed logic error introduced while renaming 'dev_id' as 'pipe' (Ville)
Renamed get_saved_encoder() to get_saved_enc() to reduce line length

v4:
Rebased.
Parameter check for pipe < -1 values in get_saved_enc() (Ville)
Switched to for_each_pipe() in get_saved_enc() (Ville)
Renamed 'pipe' to 'dev_id' in audio side code (Takashi)

v5:
Included a comment for the dev_id arg. (Libin)

Signed-off-by: Dhinakaran Pandiyan <dhinakaran.pandiyan@intel.com>
Reviewed-by: Takashi Iwai <tiwai@suse.de>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/1474488168-2343-1-git-send-email-dhinakaran.pandiyan@intel.com
This commit is contained in:
Pandiyan, Dhinakaran 2016-09-21 13:02:48 -07:00 committed by Rodrigo Vivi
parent efab0698f9
commit f931894194
7 changed files with 96 additions and 49 deletions

View File

@ -2080,7 +2080,8 @@ struct drm_i915_private {
/* perform PHY state sanity checks? */ /* perform PHY state sanity checks? */
bool chv_phy_assert[2]; bool chv_phy_assert[2];
struct intel_encoder *dig_port_map[I915_MAX_PORTS]; /* Used to save the pipe-to-encoder mapping for audio */
struct intel_encoder *av_enc_map[I915_MAX_PIPES];
/* /*
* NOTE: This is the dri1/ums dungeon, don't add stuff here. Your patch * NOTE: This is the dri1/ums dungeon, don't add stuff here. Your patch

View File

@ -491,6 +491,7 @@ void intel_audio_codec_enable(struct intel_encoder *intel_encoder)
struct drm_i915_private *dev_priv = to_i915(encoder->dev); struct drm_i915_private *dev_priv = to_i915(encoder->dev);
struct i915_audio_component *acomp = dev_priv->audio_component; struct i915_audio_component *acomp = dev_priv->audio_component;
enum port port = intel_encoder->port; enum port port = intel_encoder->port;
enum pipe pipe = crtc->pipe;
connector = drm_select_eld(encoder); connector = drm_select_eld(encoder);
if (!connector) if (!connector)
@ -515,12 +516,18 @@ void intel_audio_codec_enable(struct intel_encoder *intel_encoder)
mutex_lock(&dev_priv->av_mutex); mutex_lock(&dev_priv->av_mutex);
intel_encoder->audio_connector = connector; intel_encoder->audio_connector = connector;
/* referred in audio callbacks */ /* referred in audio callbacks */
dev_priv->dig_port_map[port] = intel_encoder; dev_priv->av_enc_map[pipe] = intel_encoder;
mutex_unlock(&dev_priv->av_mutex); mutex_unlock(&dev_priv->av_mutex);
/* audio drivers expect pipe = -1 to indicate Non-MST cases */
if (intel_encoder->type != INTEL_OUTPUT_DP_MST)
pipe = -1;
if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify) if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify)
acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr, (int) port); acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr,
(int) port, (int) pipe);
} }
/** /**
@ -536,17 +543,24 @@ void intel_audio_codec_disable(struct intel_encoder *intel_encoder)
struct drm_i915_private *dev_priv = to_i915(encoder->dev); struct drm_i915_private *dev_priv = to_i915(encoder->dev);
struct i915_audio_component *acomp = dev_priv->audio_component; struct i915_audio_component *acomp = dev_priv->audio_component;
enum port port = intel_encoder->port; enum port port = intel_encoder->port;
struct intel_crtc *crtc = to_intel_crtc(encoder->crtc);
enum pipe pipe = crtc->pipe;
if (dev_priv->display.audio_codec_disable) if (dev_priv->display.audio_codec_disable)
dev_priv->display.audio_codec_disable(intel_encoder); dev_priv->display.audio_codec_disable(intel_encoder);
mutex_lock(&dev_priv->av_mutex); mutex_lock(&dev_priv->av_mutex);
intel_encoder->audio_connector = NULL; intel_encoder->audio_connector = NULL;
dev_priv->dig_port_map[port] = NULL; dev_priv->av_enc_map[pipe] = NULL;
mutex_unlock(&dev_priv->av_mutex); mutex_unlock(&dev_priv->av_mutex);
/* audio drivers expect pipe = -1 to indicate Non-MST cases */
if (intel_encoder->type != INTEL_OUTPUT_DP_MST)
pipe = -1;
if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify) if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify)
acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr, (int) port); acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr,
(int) port, (int) pipe);
} }
/** /**
@ -621,15 +635,40 @@ static int i915_audio_component_get_cdclk_freq(struct device *kdev)
return dev_priv->cdclk_freq; return dev_priv->cdclk_freq;
} }
static int i915_audio_component_sync_audio_rate(struct device *kdev, static struct intel_encoder *get_saved_enc(struct drm_i915_private *dev_priv,
int port, int rate) int port, int pipe)
{
if (WARN_ON(pipe >= I915_MAX_PIPES))
return NULL;
/* MST */
if (pipe >= 0)
return dev_priv->av_enc_map[pipe];
/* Non-MST */
for_each_pipe(dev_priv, pipe) {
struct intel_encoder *encoder;
encoder = dev_priv->av_enc_map[pipe];
if (encoder == NULL)
continue;
if (port == encoder->port)
return encoder;
}
return NULL;
}
static int i915_audio_component_sync_audio_rate(struct device *kdev, int port,
int pipe, int rate)
{ {
struct drm_i915_private *dev_priv = kdev_to_i915(kdev); struct drm_i915_private *dev_priv = kdev_to_i915(kdev);
struct intel_encoder *intel_encoder; struct intel_encoder *intel_encoder;
struct intel_crtc *crtc; struct intel_crtc *crtc;
struct drm_display_mode *mode; struct drm_display_mode *mode;
struct i915_audio_component *acomp = dev_priv->audio_component; struct i915_audio_component *acomp = dev_priv->audio_component;
enum pipe pipe = INVALID_PIPE;
u32 tmp; u32 tmp;
int n; int n;
int err = 0; int err = 0;
@ -643,25 +682,20 @@ static int i915_audio_component_sync_audio_rate(struct device *kdev,
i915_audio_component_get_power(kdev); i915_audio_component_get_power(kdev);
mutex_lock(&dev_priv->av_mutex); mutex_lock(&dev_priv->av_mutex);
/* 1. get the pipe */ /* 1. get the pipe */
intel_encoder = dev_priv->dig_port_map[port]; intel_encoder = get_saved_enc(dev_priv, port, pipe);
/* intel_encoder might be NULL for DP MST */
if (!intel_encoder || !intel_encoder->base.crtc || if (!intel_encoder || !intel_encoder->base.crtc ||
intel_encoder->type != INTEL_OUTPUT_HDMI) { intel_encoder->type != INTEL_OUTPUT_HDMI) {
DRM_DEBUG_KMS("no valid port %c\n", port_name(port)); DRM_DEBUG_KMS("Not valid for port %c\n", port_name(port));
err = -ENODEV;
goto unlock;
}
crtc = to_intel_crtc(intel_encoder->base.crtc);
pipe = crtc->pipe;
if (pipe == INVALID_PIPE) {
DRM_DEBUG_KMS("no pipe for the port %c\n", port_name(port));
err = -ENODEV; err = -ENODEV;
goto unlock; goto unlock;
} }
DRM_DEBUG_KMS("pipe %c connects port %c\n", /* pipe passed from the audio driver will be -1 for Non-MST case */
pipe_name(pipe), port_name(port)); crtc = to_intel_crtc(intel_encoder->base.crtc);
pipe = crtc->pipe;
mode = &crtc->config->base.adjusted_mode; mode = &crtc->config->base.adjusted_mode;
/* port must be valid now, otherwise the pipe will be invalid */ /* port must be valid now, otherwise the pipe will be invalid */
@ -697,7 +731,7 @@ static int i915_audio_component_sync_audio_rate(struct device *kdev,
} }
static int i915_audio_component_get_eld(struct device *kdev, int port, static int i915_audio_component_get_eld(struct device *kdev, int port,
bool *enabled, int pipe, bool *enabled,
unsigned char *buf, int max_bytes) unsigned char *buf, int max_bytes)
{ {
struct drm_i915_private *dev_priv = kdev_to_i915(kdev); struct drm_i915_private *dev_priv = kdev_to_i915(kdev);
@ -706,16 +740,20 @@ static int i915_audio_component_get_eld(struct device *kdev, int port,
int ret = -EINVAL; int ret = -EINVAL;
mutex_lock(&dev_priv->av_mutex); mutex_lock(&dev_priv->av_mutex);
intel_encoder = dev_priv->dig_port_map[port];
/* intel_encoder might be NULL for DP MST */ intel_encoder = get_saved_enc(dev_priv, port, pipe);
if (intel_encoder) { if (!intel_encoder) {
ret = 0; DRM_DEBUG_KMS("Not valid for port %c\n", port_name(port));
*enabled = intel_encoder->audio_connector != NULL; mutex_unlock(&dev_priv->av_mutex);
if (*enabled) { return ret;
eld = intel_encoder->audio_connector->eld; }
ret = drm_eld_size(eld);
memcpy(buf, eld, min(max_bytes, ret)); ret = 0;
} *enabled = intel_encoder->audio_connector != NULL;
if (*enabled) {
eld = intel_encoder->audio_connector->eld;
ret = drm_eld_size(eld);
memcpy(buf, eld, min(max_bytes, ret));
} }
mutex_unlock(&dev_priv->av_mutex); mutex_unlock(&dev_priv->av_mutex);

View File

@ -64,7 +64,7 @@ struct i915_audio_component_ops {
* Called from audio driver. After audio driver sets the * Called from audio driver. After audio driver sets the
* sample rate, it will call this function to set n/cts * sample rate, it will call this function to set n/cts
*/ */
int (*sync_audio_rate)(struct device *, int port, int rate); int (*sync_audio_rate)(struct device *, int port, int pipe, int rate);
/** /**
* @get_eld: fill the audio state and ELD bytes for the given port * @get_eld: fill the audio state and ELD bytes for the given port
* *
@ -77,7 +77,7 @@ struct i915_audio_component_ops {
* Note that the returned size may be over @max_bytes. Then it * Note that the returned size may be over @max_bytes. Then it
* implies that only a part of ELD has been copied to the buffer. * implies that only a part of ELD has been copied to the buffer.
*/ */
int (*get_eld)(struct device *, int port, bool *enabled, int (*get_eld)(struct device *, int port, int pipe, bool *enabled,
unsigned char *buf, int max_bytes); unsigned char *buf, int max_bytes);
}; };
@ -97,7 +97,7 @@ struct i915_audio_component_audio_ops {
* status accordingly (even when the HDA controller is in power save * status accordingly (even when the HDA controller is in power save
* mode). * mode).
*/ */
void (*pin_eld_notify)(void *audio_ptr, int port); void (*pin_eld_notify)(void *audio_ptr, int port, int pipe);
}; };
/** /**

View File

@ -10,8 +10,9 @@
int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable); int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable);
int snd_hdac_display_power(struct hdac_bus *bus, bool enable); int snd_hdac_display_power(struct hdac_bus *bus, bool enable);
void snd_hdac_i915_set_bclk(struct hdac_bus *bus); void snd_hdac_i915_set_bclk(struct hdac_bus *bus);
int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid, int rate); int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid,
int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid, int dev_id, int rate);
int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid, int dev_id,
bool *audio_enabled, char *buffer, int max_bytes); bool *audio_enabled, char *buffer, int max_bytes);
int snd_hdac_i915_init(struct hdac_bus *bus); int snd_hdac_i915_init(struct hdac_bus *bus);
int snd_hdac_i915_exit(struct hdac_bus *bus); int snd_hdac_i915_exit(struct hdac_bus *bus);
@ -29,13 +30,13 @@ static inline void snd_hdac_i915_set_bclk(struct hdac_bus *bus)
{ {
} }
static inline int snd_hdac_sync_audio_rate(struct hdac_device *codec, static inline int snd_hdac_sync_audio_rate(struct hdac_device *codec,
hda_nid_t nid, int rate) hda_nid_t nid, int dev_id, int rate)
{ {
return 0; return 0;
} }
static inline int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid, static inline int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid,
bool *audio_enabled, char *buffer, int dev_id, bool *audio_enabled,
int max_bytes) char *buffer, int max_bytes)
{ {
return -ENODEV; return -ENODEV;
} }

View File

@ -193,6 +193,7 @@ static int pin2port(struct hdac_device *codec, hda_nid_t pin_nid)
* snd_hdac_sync_audio_rate - Set N/CTS based on the sample rate * snd_hdac_sync_audio_rate - Set N/CTS based on the sample rate
* @codec: HDA codec * @codec: HDA codec
* @nid: the pin widget NID * @nid: the pin widget NID
* @dev_id: device identifier
* @rate: the sample rate to set * @rate: the sample rate to set
* *
* This function is supposed to be used only by a HD-audio controller * This function is supposed to be used only by a HD-audio controller
@ -201,18 +202,20 @@ static int pin2port(struct hdac_device *codec, hda_nid_t pin_nid)
* This function sets N/CTS value based on the given sample rate. * This function sets N/CTS value based on the given sample rate.
* Returns zero for success, or a negative error code. * Returns zero for success, or a negative error code.
*/ */
int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid, int rate) int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid,
int dev_id, int rate)
{ {
struct hdac_bus *bus = codec->bus; struct hdac_bus *bus = codec->bus;
struct i915_audio_component *acomp = bus->audio_component; struct i915_audio_component *acomp = bus->audio_component;
int port; int port, pipe;
if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate) if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate)
return -ENODEV; return -ENODEV;
port = pin2port(codec, nid); port = pin2port(codec, nid);
if (port < 0) if (port < 0)
return -EINVAL; return -EINVAL;
return acomp->ops->sync_audio_rate(acomp->dev, port, rate); pipe = dev_id;
return acomp->ops->sync_audio_rate(acomp->dev, port, pipe, rate);
} }
EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate); EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate);
@ -220,6 +223,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate);
* snd_hdac_acomp_get_eld - Get the audio state and ELD via component * snd_hdac_acomp_get_eld - Get the audio state and ELD via component
* @codec: HDA codec * @codec: HDA codec
* @nid: the pin widget NID * @nid: the pin widget NID
* @dev_id: device identifier
* @audio_enabled: the pointer to store the current audio state * @audio_enabled: the pointer to store the current audio state
* @buffer: the buffer pointer to store ELD bytes * @buffer: the buffer pointer to store ELD bytes
* @max_bytes: the max bytes to be stored on @buffer * @max_bytes: the max bytes to be stored on @buffer
@ -236,12 +240,12 @@ EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate);
* thus it may be over @max_bytes. If it's over @max_bytes, it implies * thus it may be over @max_bytes. If it's over @max_bytes, it implies
* that only a part of ELD bytes have been fetched. * that only a part of ELD bytes have been fetched.
*/ */
int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid, int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid, int dev_id,
bool *audio_enabled, char *buffer, int max_bytes) bool *audio_enabled, char *buffer, int max_bytes)
{ {
struct hdac_bus *bus = codec->bus; struct hdac_bus *bus = codec->bus;
struct i915_audio_component *acomp = bus->audio_component; struct i915_audio_component *acomp = bus->audio_component;
int port; int port, pipe;
if (!acomp || !acomp->ops || !acomp->ops->get_eld) if (!acomp || !acomp->ops || !acomp->ops->get_eld)
return -ENODEV; return -ENODEV;
@ -249,7 +253,9 @@ int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid,
port = pin2port(codec, nid); port = pin2port(codec, nid);
if (port < 0) if (port < 0)
return -EINVAL; return -EINVAL;
return acomp->ops->get_eld(acomp->dev, port, audio_enabled,
pipe = dev_id;
return acomp->ops->get_eld(acomp->dev, port, pipe, audio_enabled,
buffer, max_bytes); buffer, max_bytes);
} }
EXPORT_SYMBOL_GPL(snd_hdac_acomp_get_eld); EXPORT_SYMBOL_GPL(snd_hdac_acomp_get_eld);

View File

@ -1485,7 +1485,7 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
mutex_lock(&per_pin->lock); mutex_lock(&per_pin->lock);
eld->monitor_present = false; eld->monitor_present = false;
size = snd_hdac_acomp_get_eld(&codec->core, per_pin->pin_nid, size = snd_hdac_acomp_get_eld(&codec->core, per_pin->pin_nid, -1,
&eld->monitor_present, eld->eld_buffer, &eld->monitor_present, eld->eld_buffer,
ELD_MAX_SIZE); ELD_MAX_SIZE);
if (size > 0) { if (size > 0) {
@ -1744,7 +1744,8 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
/* Call sync_audio_rate to set the N/CTS/M manually if necessary */ /* Call sync_audio_rate to set the N/CTS/M manually if necessary */
/* Todo: add DP1.2 MST audio support later */ /* Todo: add DP1.2 MST audio support later */
if (codec_has_acomp(codec)) if (codec_has_acomp(codec))
snd_hdac_sync_audio_rate(&codec->core, pin_nid, runtime->rate); snd_hdac_sync_audio_rate(&codec->core, pin_nid, -1,
runtime->rate);
non_pcm = check_non_pcm_per_cvt(codec, cvt_nid); non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
mutex_lock(&per_pin->lock); mutex_lock(&per_pin->lock);
@ -2290,7 +2291,7 @@ static void haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg,
snd_hda_codec_set_power_to_all(codec, fg, power_state); snd_hda_codec_set_power_to_all(codec, fg, power_state);
} }
static void intel_pin_eld_notify(void *audio_ptr, int port) static void intel_pin_eld_notify(void *audio_ptr, int port, int pipe)
{ {
struct hda_codec *codec = audio_ptr; struct hda_codec *codec = audio_ptr;
int pin_nid; int pin_nid;

View File

@ -1368,7 +1368,7 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev,
return hdac_hdmi_init_dai_map(edev); return hdac_hdmi_init_dai_map(edev);
} }
static void hdac_hdmi_eld_notify_cb(void *aptr, int port) static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe)
{ {
struct hdac_ext_device *edev = aptr; struct hdac_ext_device *edev = aptr;
struct hdac_hdmi_priv *hdmi = edev->private_data; struct hdac_hdmi_priv *hdmi = edev->private_data;