drm/msm/dp: fix audio during link maintenance

During link maintenance, audio needs to be switched off and
on to adapt to new link configurations. Move the registration
of audio functionalities with external-display module to
audio sub-module's initialization. This is needed to separate
out the audio initialization and registration from audio enable
and disable. Now, audio initialization and registration happens
during DisplayPort initialization. For MST case, it happens during
connector installation for a new stream.
This gives the flexibility to independently enabled or disabled
audio. It can be switched along with the video stream during
hot plug or during link maintenance.

CRs-Fixed: 2305680
Change-Id: I22a056af75a9851056c97e8174319c0dc3022f29
Signed-off-by: Ajay Singh Parmar <aparmar@codeaurora.org>
tirimbino
Ajay Singh Parmar 6 years ago
parent b37a908353
commit fe4c67473c
  1. 98
      drivers/gpu/drm/msm/dp/dp_audio.c
  2. 24
      drivers/gpu/drm/msm/dp/dp_audio.h
  3. 4
      drivers/gpu/drm/msm/dp/dp_ctrl.c
  4. 30
      drivers/gpu/drm/msm/dp/dp_display.c
  5. 1
      drivers/gpu/drm/msm/dp/dp_panel.c
  6. 62
      drivers/platform/msm/msm_ext_display.c

@ -39,8 +39,11 @@ struct dp_audio_private {
struct completion hpd_comp;
struct workqueue_struct *notify_workqueue;
struct delayed_work notify_delayed_work;
struct mutex ops_lock;
struct dp_audio dp_audio;
atomic_t acked;
};
static u32 dp_audio_get_header(struct dp_catalog_audio *catalog,
@ -416,14 +419,14 @@ static int dp_audio_info_setup(struct platform_device *pdev,
return rc;
}
mutex_lock(&audio->dp_audio.ops_lock);
mutex_lock(&audio->ops_lock);
audio->channels = params->num_of_channels;
if (audio->panel->stream_id >= DP_STREAM_MAX) {
pr_err("invalid stream id: %d\n", audio->panel->stream_id);
rc = -EINVAL;
mutex_unlock(&audio->dp_audio.ops_lock);
mutex_unlock(&audio->ops_lock);
return rc;
}
@ -432,7 +435,7 @@ static int dp_audio_info_setup(struct platform_device *pdev,
dp_audio_safe_to_exit_level(audio);
dp_audio_enable(audio, true);
mutex_unlock(&audio->dp_audio.ops_lock);
mutex_unlock(&audio->ops_lock);
return rc;
}
@ -506,10 +509,11 @@ static void dp_audio_teardown_done(struct platform_device *pdev)
if (IS_ERR(audio))
return;
mutex_lock(&audio->dp_audio.ops_lock);
mutex_lock(&audio->ops_lock);
dp_audio_enable(audio, false);
mutex_unlock(&audio->dp_audio.ops_lock);
mutex_unlock(&audio->ops_lock);
atomic_set(&audio->acked, 1);
complete_all(&audio->hpd_comp);
pr_debug("audio engine disabled\n");
@ -542,8 +546,10 @@ static int dp_audio_ack_done(struct platform_device *pdev, u32 ack)
pr_debug("acknowledging audio (%d)\n", ack_hpd);
if (!audio->engine_on)
if (!audio->engine_on) {
atomic_set(&audio->acked, 1);
complete_all(&audio->hpd_comp);
}
end:
return rc;
}
@ -566,16 +572,13 @@ end:
return rc;
}
static int dp_audio_register_ext_disp(struct dp_audio *dp_audio)
static int dp_audio_register_ext_disp(struct dp_audio_private *audio)
{
int rc = 0;
struct device_node *pd = NULL;
const char *phandle = "qcom,ext-disp";
struct msm_ext_disp_init_data *ext;
struct msm_ext_disp_audio_codec_ops *ops;
struct dp_audio_private *audio;
audio = container_of(dp_audio, struct dp_audio_private, dp_audio);
ext = &audio->ext_audio_data;
ops = &ext->codec_ops;
@ -624,15 +627,12 @@ end:
return rc;
}
static int dp_audio_deregister_ext_disp(struct dp_audio *dp_audio)
static int dp_audio_deregister_ext_disp(struct dp_audio_private *audio)
{
int rc = 0;
struct device_node *pd = NULL;
const char *phandle = "qcom,ext-disp";
struct msm_ext_disp_init_data *ext;
struct dp_audio_private *audio;
audio = container_of(dp_audio, struct dp_audio_private, dp_audio);
ext = &audio->ext_audio_data;
@ -669,9 +669,14 @@ static int dp_audio_notify(struct dp_audio_private *audio, u32 state)
int rc = 0;
struct msm_ext_disp_init_data *ext = &audio->ext_audio_data;
if (!ext->intf_ops.audio_notify)
atomic_set(&audio->acked, 0);
if (!ext->intf_ops.audio_notify) {
pr_err("audio notify not defined\n");
goto end;
}
reinit_completion(&audio->hpd_comp);
rc = ext->intf_ops.audio_notify(audio->ext_pdev,
&ext->codec, state);
if (rc) {
@ -679,8 +684,10 @@ static int dp_audio_notify(struct dp_audio_private *audio, u32 state)
goto end;
}
reinit_completion(&audio->hpd_comp);
rc = wait_for_completion_timeout(&audio->hpd_comp, HZ * 5);
if (atomic_read(&audio->acked))
goto end;
rc = wait_for_completion_timeout(&audio->hpd_comp, HZ * 4);
if (!rc) {
pr_err("timeout. state=%d err=%d\n", state, rc);
rc = -ETIMEDOUT;
@ -692,6 +699,30 @@ end:
return rc;
}
static int dp_audio_config(struct dp_audio_private *audio, u32 state)
{
int rc = 0;
struct msm_ext_disp_init_data *ext = &audio->ext_audio_data;
if (!ext || !ext->intf_ops.audio_config) {
pr_err("audio_config not defined\n");
goto end;
}
/*
* DP Audio sets default STREAM_0 only, other streams are
* set by audio driver based on the hardware/software support.
*/
if (audio->panel->stream_id == DP_STREAM_0) {
rc = ext->intf_ops.audio_config(audio->ext_pdev,
&ext->codec, state);
if (rc)
pr_err("failed to config audio, err=%d\n", rc);
}
end:
return rc;
}
static int dp_audio_on(struct dp_audio *dp_audio)
{
int rc = 0;
@ -713,15 +744,9 @@ static int dp_audio_on(struct dp_audio *dp_audio)
audio->session_on = true;
if (ext->intf_ops.audio_config) {
rc = ext->intf_ops.audio_config(audio->ext_pdev,
&ext->codec,
EXT_DISPLAY_CABLE_CONNECT);
if (rc) {
pr_err("failed to config audio, err=%d\n", rc);
goto end;
}
}
rc = dp_audio_config(audio, EXT_DISPLAY_CABLE_CONNECT);
if (rc)
goto end;
rc = dp_audio_notify(audio, EXT_DISPLAY_CABLE_CONNECT);
if (rc)
@ -757,13 +782,7 @@ static int dp_audio_off(struct dp_audio *dp_audio)
pr_debug("success\n");
end:
if (ext->intf_ops.audio_config) {
rc = ext->intf_ops.audio_config(audio->ext_pdev,
&ext->codec,
EXT_DISPLAY_CABLE_DISCONNECT);
if (rc)
pr_err("failed to config audio, err=%d\n", rc);
}
dp_audio_config(audio, EXT_DISPLAY_CABLE_DISCONNECT);
audio->session_on = false;
audio->engine_on = false;
@ -830,17 +849,19 @@ struct dp_audio *dp_audio_get(struct platform_device *pdev,
audio->panel = panel;
audio->catalog = catalog;
atomic_set(&audio->acked, 0);
dp_audio = &audio->dp_audio;
mutex_init(&dp_audio->ops_lock);
mutex_init(&audio->ops_lock);
dp_audio->on = dp_audio_on;
dp_audio->off = dp_audio_off;
dp_audio->register_ext_disp = dp_audio_register_ext_disp;
dp_audio->deregister_ext_disp = dp_audio_deregister_ext_disp;
catalog->init(catalog);
dp_audio_register_ext_disp(audio);
return dp_audio;
error_notify_workqueue:
@ -857,7 +878,10 @@ void dp_audio_put(struct dp_audio *dp_audio)
return;
audio = container_of(dp_audio, struct dp_audio_private, dp_audio);
mutex_destroy(&dp_audio->ops_lock);
dp_audio_deregister_ext_disp(audio);
mutex_destroy(&audio->ops_lock);
dp_audio_destroy_notify_workqueue(audio);

@ -29,8 +29,6 @@ struct dp_audio {
u32 lane_count;
u32 bw_code;
struct mutex ops_lock;
/**
* on()
*
@ -52,28 +50,6 @@ struct dp_audio {
* Returns the error code in case of failure, 0 in success case.
*/
int (*off)(struct dp_audio *dp_audio);
/**
* register_ext_disp()
*
* Registers the audio with external display module.
*
* @dp_audio: an instance of struct dp_audio.
*
* Returns the error code in case of failure, 0 in success case.
*/
int (*register_ext_disp)(struct dp_audio *dp_audio);
/**
* deregister_ext_disp()
*
* Deregisters the audio with external display module.
*
* @dp_audio: an instance of struct dp_audio.
*
* Returns the error code in case of failure, 0 in success case.
*/
int (*deregister_ext_disp)(struct dp_audio *dp_audio);
};
/**

@ -470,10 +470,8 @@ static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl)
link_info.capabilities = ctrl->panel->link_info.capabilities;
ret = drm_dp_link_configure(ctrl->aux->drm_aux, &link_info);
if (ret) {
pr_err_ratelimited("link_configure failed, rc=%d\n", ret);
if (ret)
goto end;
}
ret = drm_dp_dpcd_write(ctrl->aux->drm_aux,
DP_MAIN_LINK_CHANNEL_CODING_SET, &encoding, 1);

@ -98,7 +98,6 @@ struct dp_display_private {
struct delayed_work hdcp_cb_work;
struct delayed_work connect_work;
struct work_struct attention_work;
struct mutex hdcp_mutex;
struct mutex session_lock;
u32 active_stream_cnt;
@ -293,7 +292,6 @@ static void dp_display_deinitialize_hdcp(struct dp_display_private *dp)
}
sde_dp_hdcp2p2_deinit(dp->hdcp.data);
mutex_destroy(&dp->hdcp_mutex);
}
static int dp_display_initialize_hdcp(struct dp_display_private *dp)
@ -309,8 +307,6 @@ static int dp_display_initialize_hdcp(struct dp_display_private *dp)
parser = dp->parser;
mutex_init(&dp->hdcp_mutex);
hdcp_init_data.client_id = HDCP_CLIENT_DP;
hdcp_init_data.drm_aux = dp->aux->drm_aux;
hdcp_init_data.cb_data = (void *)dp;
@ -857,21 +853,17 @@ static void dp_display_handle_maintenance_req(struct dp_display_private *dp)
int idx;
struct dp_panel *dp_panel;
mutex_lock(&dp->session_lock);
for (idx = DP_STREAM_0; idx < DP_STREAM_MAX; idx++) {
if (!dp->active_panels[idx])
continue;
dp_panel = dp->active_panels[idx];
dp->ctrl->stream_pre_off(dp->ctrl, dp_panel);
dp->ctrl->stream_off(dp->ctrl, dp_panel);
mutex_lock(&dp_panel->audio->ops_lock);
if (dp_panel->audio_supported)
dp_panel->audio->off(dp_panel->audio);
dp->ctrl->stream_pre_off(dp->ctrl, dp_panel);
dp->ctrl->stream_off(dp->ctrl, dp_panel);
}
dp->ctrl->link_maintenance(dp->ctrl);
@ -886,11 +878,7 @@ static void dp_display_handle_maintenance_req(struct dp_display_private *dp)
if (dp_panel->audio_supported)
dp_panel->audio->on(dp_panel->audio);
mutex_unlock(&dp_panel->audio->ops_lock);
}
mutex_unlock(&dp->session_lock);
}
static void dp_display_mst_attention(struct dp_display_private *dp)
@ -1411,8 +1399,6 @@ static void dp_display_stream_post_enable(struct dp_display_private *dp,
{
dp_panel->spd_config(dp_panel);
dp_panel->setup_hdr(dp_panel, NULL);
dp_panel->audio->register_ext_disp(dp_panel->audio);
}
static int dp_display_post_enable(struct dp_display *dp_display, void *panel)
@ -1436,10 +1422,8 @@ static int dp_display_post_enable(struct dp_display *dp_display, void *panel)
goto end;
}
if (atomic_read(&dp->aborted)) {
pr_err("aborted\n");
if (atomic_read(&dp->aborted))
goto end;
}
if (!dp_display_is_ready(dp) || !dp->core_initialized) {
pr_err("display not ready\n");
@ -1479,7 +1463,6 @@ end:
static int dp_display_stream_pre_disable(struct dp_display_private *dp,
struct dp_panel *dp_panel)
{
dp_panel->audio->deregister_ext_disp(dp_panel->audio);
dp->ctrl->stream_pre_off(dp->ctrl, dp_panel);
return 0;
@ -1770,13 +1753,8 @@ static void dp_display_convert_to_dp_mode(struct dp_display *dp_display,
dp = container_of(dp_display, struct dp_display_private, dp_display);
dp_panel = panel;
mutex_lock(&dp->session_lock);
memset(dp_mode, 0, sizeof(*dp_mode));
dp_panel->convert_to_dp_mode(dp_panel, drm_mode, dp_mode);
mutex_unlock(&dp->session_lock);
}
static int dp_display_config_hdr(struct dp_display *dp_display, void *panel,

@ -1683,7 +1683,6 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in)
dp_panel->spd_enabled = true;
memcpy(panel->spd_vendor_name, vendor_name, (sizeof(u8) * 8));
memcpy(panel->spd_product_description, product_desc, (sizeof(u8) * 16));
dp_panel->stream_id = DP_STREAM_MAX;
dp_panel->connector = in->connector;
if (in->base_panel) {

@ -95,7 +95,9 @@ static const char *msm_ext_disp_name(enum msm_ext_disp_type type)
static int msm_ext_disp_add_intf_data(struct msm_ext_disp *ext_disp,
struct msm_ext_disp_init_data *data)
{
int count = 0;
struct msm_ext_disp_list *node;
struct list_head *pos = NULL;
if (!ext_disp || !data) {
pr_err("Invalid params\n");
@ -107,10 +109,18 @@ static int msm_ext_disp_add_intf_data(struct msm_ext_disp *ext_disp,
return -ENOMEM;
node->data = data;
list_for_each(pos, &ext_disp->display_list)
count++;
data->codec.stream_id = count;
list_add(&node->list, &ext_disp->display_list);
pr_debug("Added new display (%s)\n",
msm_ext_disp_name(data->codec.type));
pr_debug("Added new display (%s) ctld (%d) stream (%d)\n",
msm_ext_disp_name(data->codec.type),
data->codec.ctrl_id, data->codec.stream_id);
return 0;
}
@ -165,12 +175,8 @@ static int msm_ext_disp_get_intf_data(struct msm_ext_disp *ext_disp,
}
}
if (!*data) {
pr_err("Display not found (%s) ctld (%d) stream (%d)\n",
msm_ext_disp_name(codec->type),
codec->ctrl_id, codec->stream_id);
if (!*data)
ret = -ENODEV;
}
end:
return ret;
}
@ -258,8 +264,9 @@ static int msm_ext_disp_update_audio_ops(struct msm_ext_disp *ext_disp,
ret = msm_ext_disp_get_intf_data(ext_disp, codec, &data);
if (ret || !data) {
pr_err("interface %s not found\n",
msm_ext_disp_name(codec->type));
pr_err("Display not found (%s) ctld (%d) stream (%d)\n",
msm_ext_disp_name(codec->type),
codec->ctrl_id, codec->stream_id);
goto end;
}
@ -286,25 +293,24 @@ static int msm_ext_disp_audio_config(struct platform_device *pdev,
ext_disp = msm_ext_disp_validate_and_get(pdev, codec, state);
if (IS_ERR(ext_disp)) {
ret = PTR_ERR(ext_disp);
}
if (!(ext_disp->current_codec.type == codec->type &&
ext_disp->current_codec.ctrl_id == codec->ctrl_id &&
ext_disp->current_codec.stream_id == codec->stream_id))
goto end;
}
if (state == EXT_DISPLAY_CABLE_DISCONNECT) {
if (state == EXT_DISPLAY_CABLE_CONNECT) {
ret = msm_ext_disp_select_audio_codec(pdev, codec);
if (ret)
pr_err("error setting audio codec\n");
} else {
mutex_lock(&ext_disp->lock);
if (ext_disp->ops) {
*ext_disp->ops =
(struct msm_ext_disp_audio_codec_ops){NULL};
pr_debug("mst. delete ops for current codec\n");
}
if (ext_disp->ops)
memset(ext_disp->ops, 0, sizeof(*ext_disp->ops));
pr_debug("codec ops cleared for %s\n",
msm_ext_disp_name(ext_disp->current_codec.type));
ext_disp->current_codec.type = EXT_DISPLAY_TYPE_MAX;
mutex_unlock(&ext_disp->lock);
}
end:
return ret;
}
@ -532,17 +538,8 @@ int msm_ext_disp_register_intf(struct platform_device *pdev,
msm_ext_disp_name(init_data->codec.type),
init_data->codec.ctrl_id,
init_data->codec.stream_id);
mutex_unlock(&ext_disp->lock);
if (ext_disp->current_codec.type == EXT_DISPLAY_TYPE_MAX)
msm_ext_disp_select_audio_codec(pdev, &init_data->codec);
return ret;
end:
mutex_unlock(&ext_disp->lock);
return ret;
}
@ -578,11 +575,6 @@ int msm_ext_disp_deregister_intf(struct platform_device *pdev,
pr_debug("%s deregistered\n",
msm_ext_disp_name(init_data->codec.type));
mutex_unlock(&ext_disp->lock);
return ret;
end:
mutex_unlock(&ext_disp->lock);

Loading…
Cancel
Save