From 7f85fee1bc725d9bf7f150c8351ede32cd8b4810 Mon Sep 17 00:00:00 2001 From: Navid Bahrani Date: Wed, 7 Feb 2018 19:17:53 -0800 Subject: [PATCH] drm/msm: HPD_OFF Property for SDE HDMI Connector Adding a new "HPD_OFF" atomic property for the SDE Connector which enables or disables the hpd related clocks for the hdmi display Change-Id: Ie46c97ae0db9bad231acf7efc660499bac28f45a Signed-off-by: Navid Bahrani --- drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c | 50 +++++++++++++++++++-- drivers/gpu/drm/msm/hdmi/hdmi.h | 3 ++ drivers/gpu/drm/msm/msm_drv.h | 3 +- drivers/gpu/drm/msm/sde/sde_connector.c | 16 ++++++- drivers/gpu/drm/msm/sde/sde_connector.h | 6 ++- 5 files changed, 71 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c index 3c149f191871..9d7e6f1f14c3 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark * @@ -1215,6 +1215,9 @@ static int _sde_hdmi_gpio_config(struct hdmi *hdmi, bool on) gpio_free(config->hpd_gpio); + if (config->hpd5v_gpio != -1) + gpio_free(config->hpd5v_gpio); + if (config->mux_en_gpio != -1) { gpio_set_value_cansleep(config->mux_en_gpio, 0); gpio_free(config->mux_en_gpio); @@ -1336,6 +1339,8 @@ static int _sde_hdmi_hpd_enable(struct sde_hdmi *sde_hdmi) HDMI_HPD_CTRL_ENABLE | hpd_ctrl); spin_unlock_irqrestore(&hdmi->reg_lock, flags); + hdmi->hpd_off = false; + SDE_DEBUG("enabled hdmi hpd\n"); return 0; fail: @@ -1395,7 +1400,7 @@ int sde_hdmi_core_enable(struct sde_hdmi *sde_hdmi) return ret; } -static void _sde_hdmi_hdp_disable(struct sde_hdmi *sde_hdmi) +static void _sde_hdmi_hpd_disable(struct sde_hdmi *sde_hdmi) { struct hdmi *hdmi = sde_hdmi->ctrl.ctrl; const struct hdmi_platform_config *config = hdmi->config; @@ -1403,6 +1408,11 @@ static void _sde_hdmi_hdp_disable(struct sde_hdmi *sde_hdmi) int i, ret = 0; unsigned long flags; + if (hdmi->hpd_off) { + pr_warn("hdmi display hpd was already disabled\n"); + return; + } + spin_lock_irqsave(&hdmi->reg_lock, flags); /* Disable HPD interrupt */ hdmi_write(hdmi, REG_HDMI_HPD_CTRL, 0); @@ -1429,12 +1439,42 @@ static void _sde_hdmi_hdp_disable(struct sde_hdmi *sde_hdmi) pr_warn("failed to disable hpd regulator: %s (%d)\n", config->hpd_reg_names[i], ret); } + hdmi->hpd_off = true; + SDE_DEBUG("disabled hdmi hpd\n"); +} + +/** + * _sde_hdmi_update_hpd_state() - Update the HDMI HPD clock state + * + * @state: non-zero to disbale HPD clock, 0 to enable. + * return: 0 on success, non-zero in case of failure. + * + */ +static int +_sde_hdmi_update_hpd_state(struct sde_hdmi *hdmi_display, u64 state) +{ + struct hdmi *hdmi = hdmi_display->ctrl.ctrl; + int rc = 0; + + if (hdmi_display->non_pluggable) + return 0; + + SDE_DEBUG("changing hdmi hpd state to %llu\n", state); + + if (state == SDE_MODE_HPD_ON) { + if (!hdmi->hpd_off) + pr_warn("hdmi display hpd was already enabled\n"); + rc = _sde_hdmi_hpd_enable(hdmi_display); + } else + _sde_hdmi_hpd_disable(hdmi_display); + + return rc; } void sde_hdmi_core_disable(struct sde_hdmi *sde_hdmi) { /* HPD contains all the core clock and pwr */ - _sde_hdmi_hdp_disable(sde_hdmi); + _sde_hdmi_hpd_disable(sde_hdmi); } static void _sde_hdmi_cec_update_phys_addr(struct sde_hdmi *display) @@ -2204,6 +2244,8 @@ int sde_hdmi_set_property(struct drm_connector *connector, rc = _sde_hdmi_enable_pll_update(display, value); else if (property_index == CONNECTOR_PROP_PLL_DELTA) rc = _sde_hdmi_update_pll_delta(display, value); + else if (property_index == CONNECTOR_PROP_HPD_OFF) + rc = _sde_hdmi_update_hpd_state(display, value); return rc; } @@ -2281,7 +2323,7 @@ int sde_hdmi_connector_pre_deinit(struct drm_connector *connector, return -EINVAL; } - _sde_hdmi_hdp_disable(sde_hdmi); + _sde_hdmi_hpd_disable(sde_hdmi); return 0; } diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h index 8ca7b36ee0c8..9a0733bf81ff 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.h @@ -50,6 +50,9 @@ struct hdmi { const struct hdmi_platform_config *config; + /* hpd state: */ + bool hpd_off; + /* audio state: */ struct hdmi_audio audio; diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 25dc5f9ef561..fb24b833082c 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark * @@ -164,6 +164,7 @@ enum msm_mdp_conn_property { CONNECTOR_PROP_TOPOLOGY_NAME, CONNECTOR_PROP_TOPOLOGY_CONTROL, CONNECTOR_PROP_LP, + CONNECTOR_PROP_HPD_OFF, /* total # of properties */ CONNECTOR_PROP_COUNT diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c index 6a741a7ce0f6..e8dfd1f08236 100644 --- a/drivers/gpu/drm/msm/sde/sde_connector.c +++ b/drivers/gpu/drm/msm/sde/sde_connector.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -46,6 +46,11 @@ static const struct drm_prop_enum_list e_power_mode[] = { {SDE_MODE_DPMS_OFF, "OFF"}, }; +static const struct drm_prop_enum_list hpd_clock_state[] = { + {SDE_MODE_HPD_ON, "ON"}, + {SDE_MODE_HPD_OFF, "OFF"}, +}; + int sde_connector_get_info(struct drm_connector *connector, struct msm_display_info *info) { @@ -475,6 +480,9 @@ static int sde_connector_atomic_set_property(struct drm_connector *connector, _sde_connector_update_power_locked(c_conn); mutex_unlock(&c_conn->lock); break; + case CONNECTOR_PROP_HPD_OFF: + c_conn->hpd_mode = val; + break; default: break; } @@ -819,6 +827,7 @@ struct drm_connector *sde_connector_init(struct drm_device *dev, c_conn->display = display; c_conn->dpms_mode = DRM_MODE_DPMS_ON; + c_conn->hpd_mode = SDE_MODE_HPD_ON; c_conn->lp_mode = 0; c_conn->last_panel_power_mode = SDE_MODE_DPMS_ON; @@ -946,6 +955,11 @@ struct drm_connector *sde_connector_init(struct drm_device *dev, ARRAY_SIZE(e_power_mode), CONNECTOR_PROP_LP, 0); + msm_property_install_enum(&c_conn->property_info, "HPD_OFF", + DRM_MODE_PROP_ATOMIC, 0, hpd_clock_state, + ARRAY_SIZE(hpd_clock_state), + CONNECTOR_PROP_HPD_OFF, 0); + rc = msm_property_install_get_status(&c_conn->property_info); if (rc) { SDE_ERROR("failed to create one or more properties\n"); diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h index 0f563ac25da8..7db98afad713 100644 --- a/drivers/gpu/drm/msm/sde/sde_connector.h +++ b/drivers/gpu/drm/msm/sde/sde_connector.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -22,6 +22,9 @@ #include "sde_kms.h" #include "sde_fence.h" +#define SDE_MODE_HPD_ON 0 +#define SDE_MODE_HPD_OFF 1 + #define SDE_CONNECTOR_NAME_SIZE 16 struct sde_connector; @@ -207,6 +210,7 @@ struct sde_connector { struct sde_fence retire_fence; struct sde_connector_ops ops; int dpms_mode; + u64 hpd_mode; int lp_mode; int last_panel_power_mode;