diff options
| -rw-r--r-- | arch/arm/boot/dts/qcom/msm8998-sde-display.dtsi | 10 | ||||
| -rw-r--r-- | arch/arm/boot/dts/qcom/msm8998-sde.dtsi | 2 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c | 156 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h | 24 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c | 37 |
5 files changed, 223 insertions, 6 deletions
diff --git a/arch/arm/boot/dts/qcom/msm8998-sde-display.dtsi b/arch/arm/boot/dts/qcom/msm8998-sde-display.dtsi index 6cef416eb5b0..6098a96db206 100644 --- a/arch/arm/boot/dts/qcom/msm8998-sde-display.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-sde-display.dtsi @@ -18,10 +18,20 @@ label = "wb_display"; }; + msm_ext_disp: qcom,msm_ext_disp { + compatible = "qcom,msm-ext-disp"; + + ext_disp_audio_codec: qcom,msm-ext-disp-audio-codec-rx { + compatible = "qcom,msm-ext-disp-audio-codec-rx"; + qcom,msm_ext_disp = <&msm_ext_disp>; + }; + }; + sde_hdmi: qcom,hdmi-display { compatible = "qcom,hdmi-display"; label = "sde_hdmi"; qcom,display-type = "secondary"; + qcom,msm_ext_disp = <&msm_ext_disp>; }; }; diff --git a/arch/arm/boot/dts/qcom/msm8998-sde.dtsi b/arch/arm/boot/dts/qcom/msm8998-sde.dtsi index 6db6ec2c3e92..ec70d0367160 100644 --- a/arch/arm/boot/dts/qcom/msm8998-sde.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-sde.dtsi @@ -193,8 +193,6 @@ qcom,enable-load = <0>; qcom,disable-load = <0>; - qcom,msm_ext_disp = <&msm_ext_disp>; - clocks = <&clock_mmss clk_mmss_mnoc_ahb_clk>, <&clock_mmss clk_mmss_mdss_ahb_clk>, <&clock_mmss clk_mmss_mdss_hdmi_clk>, diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c index 15e2d69827e7..a030af02454b 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c @@ -22,8 +22,10 @@ #include <linux/of.h> #include <linux/gpio.h> #include <linux/of_irq.h> +#include <linux/of_platform.h> #include "sde_kms.h" +#include "sde_connector.h" #include "msm_drv.h" #include "sde_hdmi.h" @@ -403,6 +405,8 @@ static void _sde_hdmi_hotplug_work(struct work_struct *work) connector = sde_hdmi->ctrl.ctrl->connector; drm_helper_hpd_irq_event(connector->dev); + + sde_hdmi_notify_clients(connector, connector->status); } static void _sde_hdmi_connector_irq(struct sde_hdmi *sde_hdmi) @@ -460,6 +464,149 @@ static irqreturn_t _sde_hdmi_irq(int irq, void *dev_id) return IRQ_HANDLED; } +static int _sde_hdmi_audio_info_setup(struct platform_device *pdev, + struct msm_ext_disp_audio_setup_params *params) +{ + int rc = -EPERM; + struct sde_hdmi *display = NULL; + struct hdmi *hdmi = NULL; + + display = platform_get_drvdata(pdev); + + if (!display || !params) { + SDE_ERROR("invalid param(s), display %pK, params %pK\n", + display, params); + return -ENODEV; + } + + hdmi = display->ctrl.ctrl; + + if (hdmi->hdmi_mode) + rc = sde_hdmi_audio_on(hdmi, params); + + return rc; +} + +static int _sde_hdmi_get_audio_edid_blk(struct platform_device *pdev, + struct msm_ext_disp_audio_edid_blk *blk) +{ + struct sde_hdmi *display = platform_get_drvdata(pdev); + + if (!display || !blk) { + SDE_ERROR("invalid param(s), display %pK, blk %pK\n", + display, blk); + return -ENODEV; + } + + /* TODO: add audio edid blk support */ + blk->audio_data_blk = NULL; + blk->audio_data_blk_size = 0; + + blk->spk_alloc_data_blk = NULL; + blk->spk_alloc_data_blk_size = 0; + + return 0; +} + +static int _sde_hdmi_get_cable_status(struct platform_device *pdev, u32 vote) +{ + struct sde_hdmi *display = NULL; + struct hdmi *hdmi = NULL; + + display = platform_get_drvdata(pdev); + + if (!display) { + SDE_ERROR("invalid param(s), display %pK\n", display); + return -ENODEV; + } + + hdmi = display->ctrl.ctrl; + + return hdmi->power_on && display->connected; +} + +static int _sde_hdmi_ext_disp_init(struct sde_hdmi *display) +{ + int rc = 0; + struct device_node *pd_np; + const char *phandle = "qcom,msm_ext_disp"; + + if (!display) { + SDE_ERROR("[%s]Invalid params\n", display->name); + return -EINVAL; + } + + display->ext_audio_data.type = EXT_DISPLAY_TYPE_HDMI; + display->ext_audio_data.pdev = display->pdev; + display->ext_audio_data.codec_ops.audio_info_setup = + _sde_hdmi_audio_info_setup; + display->ext_audio_data.codec_ops.get_audio_edid_blk = + _sde_hdmi_get_audio_edid_blk; + display->ext_audio_data.codec_ops.cable_status = + _sde_hdmi_get_cable_status; + + if (!display->pdev->dev.of_node) { + SDE_ERROR("[%s]cannot find sde_hdmi of_node\n", display->name); + return -ENODEV; + } + + pd_np = of_parse_phandle(display->pdev->dev.of_node, phandle, 0); + if (!pd_np) { + SDE_ERROR("[%s]cannot find %s device node\n", + display->name, phandle); + return -ENODEV; + } + + display->ext_pdev = of_find_device_by_node(pd_np); + if (!display->ext_pdev) { + SDE_ERROR("[%s]cannot find %s platform device\n", + display->name, phandle); + return -ENODEV; + } + + rc = msm_ext_disp_register_intf(display->ext_pdev, + &display->ext_audio_data); + if (rc) + SDE_ERROR("[%s]failed to register disp\n", display->name); + + return rc; +} + +void sde_hdmi_notify_clients(struct drm_connector *connector, + enum drm_connector_status status) +{ + struct sde_connector *c_conn = to_sde_connector(connector); + struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display; + int state = (status == connector_status_connected) ? + EXT_DISPLAY_CABLE_CONNECT : EXT_DISPLAY_CABLE_DISCONNECT; + + if (display && display->ext_audio_data.intf_ops.hpd) { + struct hdmi *hdmi = display->ctrl.ctrl; + u32 flags = MSM_EXT_DISP_HPD_VIDEO; + + if (hdmi->hdmi_mode) + flags |= MSM_EXT_DISP_HPD_AUDIO; + + display->ext_audio_data.intf_ops.hpd(display->ext_pdev, + display->ext_audio_data.type, state, flags); + } +} + +void sde_hdmi_ack_state(struct drm_connector *connector, + enum drm_connector_status status) +{ + struct sde_connector *c_conn = to_sde_connector(connector); + struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display; + + if (display) { + struct hdmi *hdmi = display->ctrl.ctrl; + + if (hdmi->hdmi_mode && display->ext_audio_data.intf_ops.notify) + display->ext_audio_data.intf_ops.notify( + display->ext_pdev, status); + } +} + void sde_hdmi_set_mode(struct hdmi *hdmi, bool power_on) { uint32_t ctrl = 0; @@ -778,6 +925,13 @@ static int sde_hdmi_bind(struct device *dev, struct device *master, void *data) if (rc) { SDE_ERROR("[%s]Debugfs init failed, rc=%d\n", display->name, rc); + goto debug_error; + } + + rc = _sde_hdmi_ext_disp_init(display); + if (rc) { + SDE_ERROR("[%s]Ext Disp init failed, rc=%d\n", + display->name, rc); goto error; } @@ -787,6 +941,8 @@ static int sde_hdmi_bind(struct device *dev, struct device *master, void *data) display->drm_dev = drm; error: + (void)_sde_hdmi_debugfs_deinit(display); +debug_error: mutex_unlock(&display->display_lock); return rc; } diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h index 1c13d9f875f2..1004959ceb83 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h @@ -88,6 +88,9 @@ struct sde_hdmi { struct sde_hdmi_ctrl ctrl; + struct platform_device *ext_pdev; + struct msm_ext_disp_init_data ext_audio_data; + bool non_pluggable; u32 num_of_modes; struct list_head mode_list; @@ -268,6 +271,27 @@ void sde_hdmi_audio_off(struct hdmi *hdmi); * Return: error code. */ int sde_hdmi_config_avmute(struct hdmi *hdmi, bool set); + +/** + * sde_hdmi_notify_clients() - notify hdmi clients of the connection status. + * @connector: Handle to the drm_connector. + * @status: connection status. + * + * Return: void. + */ +void sde_hdmi_notify_clients(struct drm_connector *connector, + enum drm_connector_status status); + +/** + * sde_hdmi_ack_state() - acknowledge the connection status. + * @connector: Handle to the drm_connector. + * @status: connection status. + * + * Return: void. + */ +void sde_hdmi_ack_state(struct drm_connector *connector, + enum drm_connector_status status); + #else /*#ifdef CONFIG_DRM_SDE_HDMI*/ static inline u32 sde_hdmi_get_num_of_displays(void) diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c index ecfff7e88689..681dca501f9b 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c @@ -18,6 +18,7 @@ #include "drm_edid.h" #include "sde_kms.h" +#include "sde_connector.h" #include "sde_hdmi.h" #include "hdmi.h" @@ -111,7 +112,6 @@ static void _sde_hdmi_bridge_pre_enable(struct drm_bridge *bridge) if (!hdmi->power_on) { _sde_hdmi_bridge_power_on(bridge); hdmi->power_on = true; - hdmi_audio_update(hdmi); } if (phy) @@ -121,14 +121,42 @@ static void _sde_hdmi_bridge_pre_enable(struct drm_bridge *bridge) if (hdmi->hdcp_ctrl && hdmi->is_hdcp_supported) hdmi_hdcp_ctrl_on(hdmi->hdcp_ctrl); + + sde_hdmi_ack_state(hdmi->connector, EXT_DISPLAY_CABLE_CONNECT); +} + +static void sde_hdmi_force_update_audio(struct drm_connector *connector, + enum drm_connector_status status) +{ + struct sde_connector *c_conn = to_sde_connector(connector); + struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display; + + if (display && display->non_pluggable) { + display->ext_audio_data.intf_ops.hpd(display->ext_pdev, + display->ext_audio_data.type, + status, + MSM_EXT_DISP_HPD_AUDIO); + } } static void _sde_hdmi_bridge_enable(struct drm_bridge *bridge) { + struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); + struct hdmi *hdmi = sde_hdmi_bridge->hdmi; + + /* force update audio ops when there's no HPD event */ + sde_hdmi_force_update_audio(hdmi->connector, + EXT_DISPLAY_CABLE_CONNECT); } static void _sde_hdmi_bridge_disable(struct drm_bridge *bridge) { + struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); + struct hdmi *hdmi = sde_hdmi_bridge->hdmi; + + /* force update audio ops when there's no HPD event */ + sde_hdmi_force_update_audio(hdmi->connector, + EXT_DISPLAY_CABLE_DISCONNECT); } static void _sde_hdmi_bridge_post_disable(struct drm_bridge *bridge) @@ -140,6 +168,8 @@ static void _sde_hdmi_bridge_post_disable(struct drm_bridge *bridge) if (hdmi->hdcp_ctrl && hdmi->is_hdcp_supported) hdmi_hdcp_ctrl_off(hdmi->hdcp_ctrl); + sde_hdmi_audio_off(hdmi); + DRM_DEBUG("power down"); sde_hdmi_set_mode(hdmi, false); @@ -149,8 +179,9 @@ static void _sde_hdmi_bridge_post_disable(struct drm_bridge *bridge) if (hdmi->power_on) { _sde_hdmi_bridge_power_off(bridge); hdmi->power_on = false; - hdmi_audio_update(hdmi); } + + sde_hdmi_ack_state(hdmi->connector, EXT_DISPLAY_CABLE_DISCONNECT); } static void _sde_hdmi_bridge_set_avi_infoframe(struct hdmi *hdmi, @@ -342,8 +373,6 @@ static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge, _sde_hdmi_bridge_set_spd_infoframe(hdmi, mode); DRM_DEBUG("hdmi setup info frame\n"); } - - hdmi_audio_update(hdmi); } static const struct drm_bridge_funcs _sde_hdmi_bridge_funcs = { |
