summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-sde-display.dtsi10
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-sde.dtsi2
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c156
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h24
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c37
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 = {