int rc = 0;
struct dp_audio_private *audio;
struct platform_device *pdev;
+ struct msm_dp *dp_display;
pdev = to_platform_device(dev);
+ dp_display = platform_get_drvdata(pdev);
+
+ /*
+ * there could be cases where sound card can be opened even
+ * before OR even when DP is not connected . This can cause
+ * unclocked access as the audio subsystem relies on the DP
+ * driver to maintain the correct state of clocks. To protect
+ * such cases check for connection status and bail out if not
+ * connected.
+ */
+ if (!dp_display->power_on) {
+ rc = -EINVAL;
+ goto end;
+ }
audio = dp_audio_get_data(pdev);
if (IS_ERR(audio)) {
dp_audio_setup_acr(audio);
dp_audio_safe_to_exit_level(audio);
dp_audio_enable(audio, true);
+ dp_display->audio_enabled = true;
+
end:
return rc;
}
{
struct dp_audio_private *audio;
struct platform_device *pdev;
+ struct msm_dp *dp_display;
pdev = to_platform_device(dev);
+ dp_display = platform_get_drvdata(pdev);
audio = dp_audio_get_data(pdev);
if (IS_ERR(audio)) {
DRM_ERROR("failed to get audio data\n");
return;
}
+ /*
+ * if audio was not enabled there is no need
+ * to execute the shutdown and we can bail out early.
+ * This also makes sure that we dont cause an unclocked
+ * access when audio subsystem calls this without DP being
+ * connected. is_connected cannot be used here as its set
+ * to false earlier than this call
+ */
+ if (!dp_display->audio_enabled)
+ return;
+
dp_audio_enable(audio, false);
+ /* signal the dp display to safely shutdown clocks */
+ dp_display_signal_audio_complete(dp_display);
}
static const struct hdmi_codec_ops dp_audio_codec_ops = {
/* state variables */
bool core_initialized;
- bool power_on;
bool hpd_irq_on;
bool audio_supported;
struct dp_display_mode dp_mode;
struct msm_dp dp_display;
+ /* wait for audio signaling */
+ struct completion audio_comp;
+
/* event related only access by event thread */
struct mutex event_mutex;
wait_queue_head_t event_q;
return 0;
}
+void dp_display_signal_audio_complete(struct msm_dp *dp_display)
+{
+ struct dp_display_private *dp;
+
+ dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+ complete_all(&dp->audio_comp);
+}
+
static int dp_display_bind(struct device *dev, struct device *master,
void *data)
{
/* signal the disconnect event early to ensure proper teardown */
dp_display_handle_plugged_change(g_dp_display, false);
+ reinit_completion(&dp->audio_comp);
dp_catalog_hpd_config_intr(dp->catalog, DP_DP_HPD_PLUG_INT_MASK |
DP_DP_IRQ_HPD_INT_MASK, true);
static int dp_display_enable(struct dp_display_private *dp, u32 data)
{
int rc = 0;
+ struct msm_dp *dp_display;
- if (dp->power_on) {
+ dp_display = g_dp_display;
+
+ if (dp_display->power_on) {
DRM_DEBUG_DP("Link already setup, return\n");
return 0;
}
rc = dp_ctrl_on_stream(dp->ctrl);
if (!rc)
- dp->power_on = true;
+ dp_display->power_on = true;
/* complete resume_comp regardless it is armed or not */
complete(&dp->resume_comp);
static int dp_display_disable(struct dp_display_private *dp, u32 data)
{
- if (!dp->power_on)
+ struct msm_dp *dp_display;
+
+ dp_display = g_dp_display;
+
+ if (!dp_display->power_on)
return -EINVAL;
+ /* wait only if audio was enabled */
+ if (dp_display->audio_enabled) {
+ if (!wait_for_completion_timeout(&dp->audio_comp,
+ HZ * 5))
+ DRM_ERROR("audio comp timeout\n");
+ }
+
+ dp_display->audio_enabled = false;
+
dp_ctrl_off(dp->ctrl);
dp->core_initialized = false;
- dp->power_on = false;
+ dp_display->power_on = false;
return 0;
}
/* Store DP audio handle inside DP display */
g_dp_display->dp_audio = dp->audio;
+ init_completion(&dp->audio_comp);
+
platform_set_drvdata(pdev, g_dp_display);
rc = component_add(&pdev->dev, &dp_display_comp_ops);
struct drm_connector *connector;
struct drm_encoder *encoder;
bool is_connected;
+ bool audio_enabled;
+ bool power_on;
hdmi_codec_plugged_cb plugged_cb;
int dp_display_request_irq(struct msm_dp *dp_display);
bool dp_display_check_video_test(struct msm_dp *dp_display);
int dp_display_get_test_bpp(struct msm_dp *dp_display);
+void dp_display_signal_audio_complete(struct msm_dp *dp_display);
void __init msm_dp_pll_driver_register(void);
void __exit msm_dp_pll_driver_unregister(void);