From a3ce4038441cf66aa3e5d2cfbfe58ff4320229ca Mon Sep 17 00:00:00 2001 From: Tatenda Chipeperekwa Date: Tue, 26 Jul 2016 18:11:55 -0700 Subject: msm: mdss: add external display class helper Implement the external display class helper as a utility to manage display interface and audio codec interactions associated with the cable connect/disconnect events of the display interfaces. The helper class is responsible for routing operations called by the audio codec to a particular display, updating the hdmi switch node, and updating the audio switch node. CRs-Fixed: 1009284 Change-Id: Ie8d1006d3f11091a861733485cb67939ad47fdfe Signed-off-by: Tatenda Chipeperekwa --- .../devicetree/bindings/fb/msm-hdmi-tx.txt | 12 + drivers/video/fbdev/msm/Makefile | 1 + drivers/video/fbdev/msm/mdss_hdmi_audio.c | 101 +-- drivers/video/fbdev/msm/mdss_hdmi_audio.h | 21 - drivers/video/fbdev/msm/mdss_hdmi_tx.c | 199 +++--- drivers/video/fbdev/msm/mdss_hdmi_tx.h | 2 + drivers/video/fbdev/msm/msm_ext_display.c | 748 +++++++++++++++++++++ include/linux/msm_ext_display.h | 96 ++- 8 files changed, 935 insertions(+), 245 deletions(-) create mode 100644 drivers/video/fbdev/msm/msm_ext_display.c diff --git a/Documentation/devicetree/bindings/fb/msm-hdmi-tx.txt b/Documentation/devicetree/bindings/fb/msm-hdmi-tx.txt index 7f95ed4285ef..da4cdf253b2c 100644 --- a/Documentation/devicetree/bindings/fb/msm-hdmi-tx.txt +++ b/Documentation/devicetree/bindings/fb/msm-hdmi-tx.txt @@ -61,7 +61,17 @@ these devices will be disabled as well. Ex. HDMI Audio Codec device. Required properties: - compatible : "msm-hdmi-audio-codec-rx"; +msm_ext_disp is a device which manages the interaction between external +displays (HDMI and Display Port) and the audio and display frameworks. + +Required properties: +- compatible: Must be "qcom,msm-ext-disp" + Example: + msm_ext_disp: qcom,msm_ext_disp { + compatible = "qcom,msm-ext-disp"; + }; + mdss_hdmi_tx: qcom,hdmi_tx@fd922100 { cell-index = <0>; compatible = "qcom,hdmi-tx"; @@ -83,6 +93,8 @@ Example: qcom,enable-load = <0 0 0 1800000 0>; qcom,disable-load = <0 0 0 0 0>; + qcom,msm_ext_disp = <&msm_ext_disp>; + qcom,hdmi-tx-ddc-mux-sel = <&pma8084_gpios 6 0>; qcom,hdmi-tx-cec = <&msmgpio 31 0>; qcom,hdmi-tx-ddc-clk = <&msmgpio 32 0>; diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile index dccd7bcc7219..cac562cffe80 100644 --- a/drivers/video/fbdev/msm/Makefile +++ b/drivers/video/fbdev/msm/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_FB_MSM_MDSS_DP_PANEL) += mdss_dp.o mdss_dp_util.o obj-$(CONFIG_FB_MSM_MDSS_DP_PANEL) += mdss_dp_aux.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_io_util.o +obj-$(CONFIG_FB_MSM_MDSS) += msm_ext_display.o obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_tx.o obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_panel.o obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_hdcp.o diff --git a/drivers/video/fbdev/msm/mdss_hdmi_audio.c b/drivers/video/fbdev/msm/mdss_hdmi_audio.c index 23064279e286..46ca4e57e96c 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_audio.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_audio.c @@ -65,7 +65,6 @@ enum hdmi_audio_sample_rates { struct hdmi_audio { struct dss_io_data *io; struct msm_ext_disp_audio_setup_params params; - struct switch_dev sdev; u32 pclk; bool ack_enabled; bool audio_ack_enabled; @@ -370,76 +369,6 @@ static void hdmi_audio_off(void *ctx) pr_debug("HDMI Audio: Disabled\n"); } -static void hdmi_audio_notify(void *ctx, int val) -{ - struct hdmi_audio *audio = ctx; - int state = 0; - bool switched; - - if (!audio) { - pr_err("invalid input\n"); - return; - } - - state = audio->sdev.state; - if (state == val) - return; - - if (audio->ack_enabled && - atomic_read(&audio->ack_pending)) { - pr_err("%s ack pending, not notifying %s\n", - state ? "connect" : "disconnect", - val ? "connect" : "disconnect"); - return; - } - - switch_set_state(&audio->sdev, val); - switched = audio->sdev.state != state; - - if (audio->ack_enabled && switched) - atomic_set(&audio->ack_pending, 1); - - pr_debug("audio %s %s\n", switched ? "switched to" : "same as", - audio->sdev.state ? "HDMI" : "SPKR"); -} - -static void hdmi_audio_ack(void *ctx, u32 ack, u32 hpd) -{ - struct hdmi_audio *audio = ctx; - u32 ack_hpd; - - if (!audio) { - pr_err("invalid input\n"); - return; - } - - if (ack & AUDIO_ACK_SET_ENABLE) { - audio->ack_enabled = ack & AUDIO_ACK_ENABLE ? - true : false; - - pr_debug("audio ack feature %s\n", - audio->ack_enabled ? "enabled" : "disabled"); - return; - } - - if (!audio->ack_enabled) - return; - - atomic_set(&audio->ack_pending, 0); - - ack_hpd = ack & AUDIO_ACK_CONNECT; - - pr_debug("acknowledging %s\n", - ack_hpd ? "connect" : "disconnect"); - - if (ack_hpd != hpd) { - pr_debug("unbalanced audio state, ack %d, hpd %d\n", - ack_hpd, hpd); - - hdmi_audio_notify(ctx, hpd); - } -} - static void hdmi_audio_reset(void *ctx) { struct hdmi_audio *audio = ctx; @@ -452,20 +381,6 @@ static void hdmi_audio_reset(void *ctx) atomic_set(&audio->ack_pending, 0); } -static void hdmi_audio_status(void *ctx, struct hdmi_audio_status *status) -{ - struct hdmi_audio *audio = ctx; - - if (!audio || !status) { - pr_err("invalid input\n"); - return; - } - - status->ack_enabled = audio->ack_enabled; - status->ack_pending = atomic_read(&audio->ack_pending); - status->switched = audio->sdev.state; -} - /** * hdmi_audio_register() - audio registeration function * @data: registeration initialization data @@ -480,7 +395,6 @@ static void hdmi_audio_status(void *ctx, struct hdmi_audio_status *status) void *hdmi_audio_register(struct hdmi_audio_init_data *data) { struct hdmi_audio *audio = NULL; - int rc = 0; if (!data) goto end; @@ -489,22 +403,11 @@ void *hdmi_audio_register(struct hdmi_audio_init_data *data) if (!audio) goto end; - audio->sdev.name = "hdmi_audio"; - rc = switch_dev_register(&audio->sdev); - if (rc) { - pr_err("audio switch registration failed\n"); - kzfree(audio); - goto end; - } - audio->io = data->io; data->ops->on = hdmi_audio_on; data->ops->off = hdmi_audio_off; - data->ops->notify = hdmi_audio_notify; - data->ops->ack = hdmi_audio_ack; data->ops->reset = hdmi_audio_reset; - data->ops->status = hdmi_audio_status; end: return audio; } @@ -519,8 +422,6 @@ void hdmi_audio_unregister(void *ctx) { struct hdmi_audio *audio = ctx; - if (audio) { - switch_dev_unregister(&audio->sdev); + if (audio) kfree(ctx); - } } diff --git a/drivers/video/fbdev/msm/mdss_hdmi_audio.h b/drivers/video/fbdev/msm/mdss_hdmi_audio.h index 57670a84988c..8f8dd427bac5 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_audio.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_audio.h @@ -16,24 +16,6 @@ #include #include -#define AUDIO_ACK_SET_ENABLE BIT(5) -#define AUDIO_ACK_ENABLE BIT(4) -#define AUDIO_ACK_CONNECT BIT(0) - -/** - * struct hdmi_audio_status - hdmi audio current status info - * @ack_pending: notification acknowledgment status - * @ack_enabled: acknowledgment feature is enabled or disabled - * @switched: audio notification status for routing - * - * Data for client to query about the current status of audio - */ -struct hdmi_audio_status { - bool ack_pending; - bool ack_enabled; - bool switched; -}; - /** * struct hdmi_audio_ops - audio operations for clients to call * @on: function pointer to enable audio @@ -49,9 +31,6 @@ struct hdmi_audio_ops { struct msm_ext_disp_audio_setup_params *params); void (*off)(void *ctx); void (*reset)(void *ctx); - void (*status)(void *ctx, struct hdmi_audio_status *status); - void (*notify)(void *ctx, int val); - void (*ack)(void *ctx, u32 ack, u32 hpd); }; /** diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c index 10e7a2d1a940..cd3017219005 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c @@ -116,6 +116,11 @@ static void hdmi_tx_fps_work(struct work_struct *work); static int hdmi_tx_pinctrl_set_state(struct hdmi_tx_ctrl *hdmi_ctrl, enum hdmi_tx_power_module_type module, bool active); static void hdmi_panel_set_hdr_infoframe(struct hdmi_tx_ctrl *hdmi_ctrl); +static int hdmi_tx_audio_info_setup(struct platform_device *pdev, + struct msm_ext_disp_audio_setup_params *params); +static int hdmi_tx_get_audio_edid_blk(struct platform_device *pdev, + struct msm_ext_disp_audio_edid_blk *blk); +static int hdmi_tx_get_cable_status(struct platform_device *pdev, u32 vote); static struct mdss_hw hdmi_tx_hw = { .hw_ndx = MDSS_HW_HDMI, @@ -386,30 +391,17 @@ static inline void hdmi_tx_cec_device_suspend(struct hdmi_tx_ctrl *hdmi_ctrl) static inline void hdmi_tx_send_cable_notification( struct hdmi_tx_ctrl *hdmi_ctrl, int val) { - int state = 0; - - if (!hdmi_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); - return; - } - state = hdmi_ctrl->sdev.state; - - switch_set_state(&hdmi_ctrl->sdev, val); - - DEV_INFO("%s: cable state %s %d\n", __func__, - hdmi_ctrl->sdev.state == state ? - "is same" : "switched to", - hdmi_ctrl->sdev.state); - - /* Notify all registered modules of cable connection status */ - schedule_work(&hdmi_ctrl->cable_notify_work); -} /* hdmi_tx_send_cable_notification */ + if (hdmi_ctrl && hdmi_ctrl->ext_audio_data.intf_ops.hpd) + hdmi_ctrl->ext_audio_data.intf_ops.hpd(hdmi_ctrl->ext_pdev, + hdmi_ctrl->ext_audio_data.type, val); +} static inline void hdmi_tx_set_audio_switch_node( struct hdmi_tx_ctrl *hdmi_ctrl, int val) { - if (hdmi_ctrl && hdmi_ctrl->audio_ops.notify) - hdmi_ctrl->audio_ops.notify(hdmi_ctrl->audio_data, val); + if (hdmi_ctrl && hdmi_ctrl->ext_audio_data.intf_ops.notify) + hdmi_ctrl->ext_audio_data.intf_ops.notify(hdmi_ctrl->ext_pdev, + val); } static void hdmi_tx_wait_for_audio_engine(struct hdmi_tx_ctrl *hdmi_ctrl) @@ -638,34 +630,6 @@ static ssize_t hdmi_tx_sysfs_rda_edid(struct device *dev, return size; } -static ssize_t hdmi_tx_sysfs_wta_audio_cb(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - int ack, rc = 0; - ssize_t ret = strnlen(buf, PAGE_SIZE); - struct hdmi_tx_ctrl *hdmi_ctrl = NULL; - - hdmi_ctrl = hdmi_tx_get_drvdata_from_sysfs_dev(dev); - - if (!hdmi_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); - ret = -EINVAL; - goto end; - } - - rc = kstrtoint(buf, 10, &ack); - if (rc) { - DEV_ERR("%s: kstrtoint failed. rc=%d\n", __func__, rc); - goto end; - } - - if (hdmi_ctrl->audio_ops.ack) - hdmi_ctrl->audio_ops.ack(hdmi_ctrl->audio_data, - ack, hdmi_ctrl->hpd_state); -end: - return ret; -} - static int hdmi_tx_update_pixel_clk(struct hdmi_tx_ctrl *hdmi_ctrl) { struct dss_module_power *power_data = NULL; @@ -885,9 +849,10 @@ static ssize_t hdmi_tx_sysfs_wta_hpd(struct device *dev, } /* disable audio ack feature */ - if (hdmi_ctrl->audio_ops.ack) - hdmi_ctrl->audio_ops.ack(hdmi_ctrl->audio_data, - AUDIO_ACK_SET_ENABLE, hdmi_ctrl->hpd_state); + if (hdmi_ctrl->ext_audio_data.intf_ops.ack) + hdmi_ctrl->ext_audio_data.intf_ops.ack( + hdmi_ctrl->ext_pdev, + AUDIO_ACK_SET_ENABLE); if (hdmi_ctrl->panel_power_on) { hdmi_ctrl->hpd_off_pending = true; @@ -895,7 +860,6 @@ static ssize_t hdmi_tx_sysfs_wta_hpd(struct device *dev, } else { hdmi_tx_hpd_off(hdmi_ctrl); - hdmi_ctrl->sdev.state = 0; hdmi_tx_set_audio_switch_node(hdmi_ctrl, 0); } @@ -1339,7 +1303,6 @@ end: } static DEVICE_ATTR(connected, S_IRUGO, hdmi_tx_sysfs_rda_connected, NULL); -static DEVICE_ATTR(hdmi_audio_cb, S_IWUSR, NULL, hdmi_tx_sysfs_wta_audio_cb); static DEVICE_ATTR(hot_plug, S_IWUSR, NULL, hdmi_tx_sysfs_wta_hot_plug); static DEVICE_ATTR(sim_mode, S_IRUGO | S_IWUSR, hdmi_tx_sysfs_rda_sim_mode, hdmi_tx_sysfs_wta_sim_mode); @@ -1362,7 +1325,6 @@ static DEVICE_ATTR(hdr_stream, S_IWUSR, NULL, hdmi_tx_sysfs_wta_hdr_stream); static struct attribute *hdmi_tx_fs_attrs[] = { &dev_attr_connected.attr, - &dev_attr_hdmi_audio_cb.attr, &dev_attr_hot_plug.attr, &dev_attr_sim_mode.attr, &dev_attr_edid.attr, @@ -1937,6 +1899,56 @@ static int hdmi_tx_init_audio(struct hdmi_tx_ctrl *hdmi_ctrl) return rc; } +static int hdmi_tx_init_ext_disp(struct hdmi_tx_ctrl *hdmi_ctrl) +{ + int ret = 0; + struct device_node *pd_np; + const char *phandle = "qcom,msm_ext_disp"; + + if (!hdmi_ctrl) { + pr_err("%s: invalid input\n", __func__); + ret = -ENODEV; + goto end; + } + + hdmi_ctrl->ext_audio_data.type = EXT_DISPLAY_TYPE_HDMI; + hdmi_ctrl->ext_audio_data.kobj = hdmi_ctrl->kobj; + hdmi_ctrl->ext_audio_data.codec_ops.audio_info_setup = + hdmi_tx_audio_info_setup; + hdmi_ctrl->ext_audio_data.codec_ops.get_audio_edid_blk = + hdmi_tx_get_audio_edid_blk; + hdmi_ctrl->ext_audio_data.codec_ops.cable_status = + hdmi_tx_get_cable_status; + + if (!hdmi_ctrl->pdev->dev.of_node) { + pr_err("%s cannot find hdmi_ctrl dev.of_node\n", __func__); + ret = -ENODEV; + goto end; + } + + pd_np = of_parse_phandle(hdmi_ctrl->pdev->dev.of_node, phandle, 0); + if (!pd_np) { + pr_err("%s cannot find %s dev\n", __func__, phandle); + ret = -ENODEV; + goto end; + } + + hdmi_ctrl->ext_pdev = of_find_device_by_node(pd_np); + if (!hdmi_ctrl->ext_pdev) { + pr_err("%s cannot find %s pdev\n", __func__, phandle); + ret = -ENODEV; + goto end; + } + + ret = msm_ext_disp_register_intf(hdmi_ctrl->ext_pdev, + &hdmi_ctrl->ext_audio_data); + if (ret) + pr_err("%s: failed to register disp\n", __func__); + +end: + return ret; +} + static void hdmi_tx_deinit_features(struct hdmi_tx_ctrl *hdmi_ctrl, u32 features) { @@ -2029,6 +2041,12 @@ static int hdmi_tx_init_features(struct hdmi_tx_ctrl *hdmi_ctrl, goto err; } + ret = hdmi_tx_init_ext_disp(hdmi_ctrl); + if (ret) { + hdmi_audio_unregister(hdmi_ctrl->audio_data); + goto err; + } + return 0; err: hdmi_tx_deinit_features(hdmi_ctrl, deinit_features); @@ -2851,20 +2869,6 @@ static int hdmi_tx_audio_info_setup(struct platform_device *pdev, rc = -EPERM; } - if (rc) { - struct hdmi_audio_status status = {0}; - - if (hdmi_ctrl->audio_ops.status) - hdmi_ctrl->audio_ops.status(hdmi_ctrl->audio_data, - &status); - - dev_err_ratelimited(&hdmi_ctrl->pdev->dev, - "%s: hpd %d, ack %d, switch %d, mode %s, power %d\n", - __func__, hdmi_ctrl->hpd_state, - status.ack_pending, status.switched, - is_mode_dvi ? "dvi" : "hdmi", - hdmi_ctrl->panel_power_on); - } mutex_unlock(&hdmi_ctrl->tx_lock); return rc; } @@ -2961,25 +2965,6 @@ static int hdmi_tx_get_cable_status(struct platform_device *pdev, u32 vote) if (vote && hpd) hdmi_ctrl->vote_hdmi_core_on = true; - /* - * if cable is not connected and audio calls this function, - * consider this as an error as it will result in whole - * audio path to fail. - */ - if (!hpd) { - struct hdmi_audio_status status = {0}; - - if (hdmi_ctrl->audio_ops.status) - hdmi_ctrl->audio_ops.status(hdmi_ctrl->audio_data, - &status); - - dev_err_ratelimited(&hdmi_ctrl->pdev->dev, - "%s: hpd %d, ack %d, switch %d, power %d\n", - __func__, hdmi_ctrl->hpd_state, - status.ack_pending, status.switched, - hdmi_ctrl->panel_power_on); - } - return hpd; } @@ -2987,17 +2972,21 @@ int msm_hdmi_register_audio_codec(struct platform_device *pdev, struct msm_ext_disp_audio_codec_ops *ops) { struct hdmi_tx_ctrl *hdmi_ctrl = platform_get_drvdata(pdev); + int ret = 0; if (!hdmi_ctrl || !ops) { DEV_ERR("%s: invalid input\n", __func__); return -ENODEV; } - ops->audio_info_setup = hdmi_tx_audio_info_setup; - ops->get_audio_edid_blk = hdmi_tx_get_audio_edid_blk; - ops->cable_status = hdmi_tx_get_cable_status; + ret = msm_ext_disp_register_audio_codec(hdmi_ctrl->ext_pdev, ops); + if (ret) { + pr_err("%s: failed to register codec\n", __func__); + goto end; + } - return 0; +end: + return ret; } /* hdmi_tx_audio_register */ EXPORT_SYMBOL(msm_hdmi_register_audio_codec); @@ -3478,7 +3467,6 @@ static void hdmi_tx_dev_deinit(struct hdmi_tx_ctrl *hdmi_ctrl) hdmi_ctrl->hdcp_ops = NULL; hdmi_ctrl->hdcp_data = NULL; - switch_dev_unregister(&hdmi_ctrl->sdev); if (hdmi_ctrl->workq) destroy_workqueue(hdmi_ctrl->workq); mutex_destroy(&hdmi_ctrl->tx_lock); @@ -3571,25 +3559,6 @@ static int hdmi_tx_start_hdcp(struct hdmi_tx_ctrl *hdmi_ctrl) return rc; } -static int hdmi_tx_init_switch_dev(struct hdmi_tx_ctrl *hdmi_ctrl) -{ - int rc = -EINVAL; - - if (!hdmi_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); - goto end; - } - - hdmi_ctrl->sdev.name = "hdmi"; - rc = switch_dev_register(&hdmi_ctrl->sdev); - if (rc) { - DEV_ERR("%s: display switch registration failed\n", __func__); - goto end; - } -end: - return rc; -} - static int hdmi_tx_hdcp_off(struct hdmi_tx_ctrl *hdmi_ctrl) { int rc = 0; @@ -3728,12 +3697,6 @@ static int hdmi_tx_evt_handle_register(struct hdmi_tx_ctrl *hdmi_ctrl) goto init_err; } - rc = hdmi_tx_init_switch_dev(hdmi_ctrl); - if (rc) { - DEV_ERR("%s: init switch dev failed.rc=%d\n", __func__, rc); - goto switch_err; - } - if (hdmi_ctrl->pdata.primary || !hdmi_ctrl->pdata.pluggable) { reinit_completion(&hdmi_ctrl->hpd_int_done); rc = hdmi_tx_sysfs_enable_hpd(hdmi_ctrl, true); @@ -3748,8 +3711,6 @@ static int hdmi_tx_evt_handle_register(struct hdmi_tx_ctrl *hdmi_ctrl) return 0; primary_err: - switch_dev_unregister(&hdmi_ctrl->sdev); -switch_err: hdmi_tx_deinit_features(hdmi_ctrl, HDMI_TX_FEAT_MAX); init_err: hdmi_tx_sysfs_remove(hdmi_ctrl); diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.h b/drivers/video/fbdev/msm/mdss_hdmi_tx.h index 1c306df70c7e..665eeeb704dd 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.h @@ -88,6 +88,7 @@ struct hdmi_tx_hdr_stream_data { struct hdmi_tx_ctrl { struct platform_device *pdev; + struct platform_device *ext_pdev; struct hdmi_tx_platform_data pdata; struct mdss_panel_data panel_data; struct mdss_util_intf *mdss_util; @@ -112,6 +113,7 @@ struct hdmi_tx_ctrl { struct hdmi_panel_data panel; struct hdmi_panel_ops panel_ops; struct msm_ext_disp_audio_setup_params audio_params; + struct msm_ext_disp_init_data ext_audio_data; struct work_struct fps_work; struct hdmi_tx_hdr_stream_data hdr_data; diff --git a/drivers/video/fbdev/msm/msm_ext_display.c b/drivers/video/fbdev/msm/msm_ext_display.c new file mode 100644 index 000000000000..5474df66eefb --- /dev/null +++ b/drivers/video/fbdev/msm/msm_ext_display.c @@ -0,0 +1,748 @@ +/* Copyright (c) 2016, 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 + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mdss_hdmi_util.h" + +struct msm_ext_disp_list { + struct msm_ext_disp_init_data *data; + struct list_head list; +}; + +struct msm_ext_disp { + struct platform_device *pdev; + enum msm_ext_disp_type current_disp; + struct msm_ext_disp_audio_codec_ops *ops; + struct switch_dev hdmi_sdev; + struct switch_dev audio_sdev; + bool ack_enabled; + atomic_t ack_pending; + struct list_head display_list; + struct mutex lock; +}; + +static int msm_ext_disp_get_intf_data(struct msm_ext_disp *ext_disp, + enum msm_ext_disp_type type, + struct msm_ext_disp_init_data **data); +static int msm_ext_disp_audio_ack(struct platform_device *pdev, u32 ack); + +static int msm_ext_disp_switch_dev_register(struct msm_ext_disp *ext_disp) +{ + int ret = 0; + + if (!ext_disp) { + pr_err("Invalid params\n"); + ret = -EINVAL; + goto end; + } + + memset(&ext_disp->hdmi_sdev, 0x0, sizeof(ext_disp->hdmi_sdev)); + ext_disp->hdmi_sdev.name = "hdmi"; + ret = switch_dev_register(&ext_disp->hdmi_sdev); + if (ret) { + pr_err("hdmi switch registration failed\n"); + goto end; + } + + memset(&ext_disp->audio_sdev, 0x0, sizeof(ext_disp->audio_sdev)); + ext_disp->audio_sdev.name = "hdmi_audio"; + ret = switch_dev_register(&ext_disp->audio_sdev); + if (ret) { + pr_err("hdmi_audio switch registration failed"); + goto hdmi_audio_failure; + } + + pr_debug("Display switch registration pass\n"); + + return ret; + +hdmi_audio_failure: + switch_dev_unregister(&ext_disp->hdmi_sdev); +end: + return ret; +} + +static void msm_ext_disp_switch_dev_unregister(struct msm_ext_disp *ext_disp) +{ + if (!ext_disp) { + pr_err("Invalid params\n"); + goto end; + } + + switch_dev_unregister(&ext_disp->hdmi_sdev); + switch_dev_unregister(&ext_disp->audio_sdev); + +end: + return; +} + +static int msm_ext_disp_get_pdev(struct device *dev, + struct platform_device *pdev) { + int ret = 0; + struct device_node *pd_np; + const char *phandle = "qcom,msm_ext_disp"; + + if (!dev || !dev->of_node) { + ret = -ENODEV; + pr_err("Invalid params\n"); + goto end; + } + + pd_np = of_parse_phandle(dev->of_node, phandle, 0); + if (!pd_np) { + pr_err("Cannot find %s dev\n", phandle); + ret = -ENODEV; + goto end; + } + + pdev = of_find_device_by_node(pd_np); + if (!pdev) { + pr_err("Cannot find %s pdev\n", phandle); + ret = -ENODEV; + } + +end: + return ret; +} + +static ssize_t msm_ext_disp_sysfs_wta_audio_cb(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ack, ret = 0; + ssize_t size = strnlen(buf, PAGE_SIZE); + struct platform_device *pdev = NULL; + + ret = kstrtoint(buf, 10, &ack); + if (ret) { + pr_err("kstrtoint failed. ret=%d\n", ret); + goto end; + } + + ret = msm_ext_disp_get_pdev(dev, pdev); + if (ret) { + pr_err("Failed to get pdev. ret=%d\n", ret); + goto end; + } + + ret = msm_ext_disp_audio_ack(pdev, ack); + if (ret) + pr_err("Failed to process ack. ret=%d\n", ret); + +end: + return size; +} + +static DEVICE_ATTR(hdmi_audio_cb, S_IWUSR, NULL, + msm_ext_disp_sysfs_wta_audio_cb); + +static struct attribute *msm_ext_disp_fs_attrs[] = { + &dev_attr_hdmi_audio_cb.attr, + NULL, +}; + +static struct attribute_group msm_ext_disp_fs_attrs_group = { + .attrs = msm_ext_disp_fs_attrs, +}; + +static int msm_ext_disp_sysfs_create(struct msm_ext_disp_init_data *data) +{ + int ret = 0; + + if (!data || !data->kobj) { + pr_err("Invalid params\n"); + ret = -EINVAL; + goto end; + } + + ret = sysfs_create_group(data->kobj, &msm_ext_disp_fs_attrs_group); + if (ret) + pr_err("Failed, ret=%d\n", ret); + +end: + return ret; +} + +static void msm_ext_disp_sysfs_remove(struct msm_ext_disp_init_data *data) +{ + if (!data || !data->kobj) { + pr_err("Invalid params\n"); + return; + } + + sysfs_remove_group(data->kobj, &msm_ext_disp_fs_attrs_group); +} + +static const char *msm_ext_disp_name(enum msm_ext_disp_type type) +{ + switch (type) { + case EXT_DISPLAY_TYPE_HDMI: return "EXT_DISPLAY_TYPE_HDMI"; + case EXT_DISPLAY_TYPE_DP: return "EXT_DISPLAY_TYPE_DP"; + default: return "???"; + } +} + +static int msm_ext_disp_add_intf_data(struct msm_ext_disp *ext_disp, + struct msm_ext_disp_init_data *data) +{ + struct msm_ext_disp_list *node; + + if (!ext_disp && !data) { + pr_err("Invalid params\n"); + return -EINVAL; + } + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + return -ENOMEM; + + node->data = data; + list_add(&node->list, &ext_disp->display_list); + + pr_debug("Added new display (%s)\n", + msm_ext_disp_name(data->type)); + + return 0; +} + +static int msm_ext_disp_get_intf_data(struct msm_ext_disp *ext_disp, + enum msm_ext_disp_type type, + struct msm_ext_disp_init_data **data) +{ + int ret = 0; + struct msm_ext_disp_list *node; + struct list_head *position = NULL; + + if (!ext_disp || !data || type < EXT_DISPLAY_TYPE_HDMI || + type >= EXT_DISPLAY_TYPE_MAX) { + pr_err("Invalid params\n"); + ret = -EINVAL; + goto end; + } + + *data = NULL; + list_for_each(position, &ext_disp->display_list) { + node = list_entry(position, struct msm_ext_disp_list, list); + if (node->data->type == type) { + pr_debug("Found display (%s)\n", + msm_ext_disp_name(type)); + *data = node->data; + break; + } + } + + if (!*data) { + pr_debug("Display not found (%s)\n", + msm_ext_disp_name(type)); + ret = -ENODEV; + } + +end: + return ret; +} + +static void msm_ext_disp_remove_intf_data(struct msm_ext_disp *ext_disp, + enum msm_ext_disp_type type) +{ + struct msm_ext_disp_list *node; + struct list_head *position = NULL; + struct list_head *temp = NULL; + + if (!ext_disp) { + pr_err("Invalid params\n"); + return; + } + + list_for_each_safe(position, temp, &ext_disp->display_list) { + node = list_entry(position, struct msm_ext_disp_list, list); + if (node->data->type == type) { + msm_ext_disp_sysfs_remove(node->data); + list_del(&node->list); + pr_debug("Removed display (%s)\n", + msm_ext_disp_name(type)); + kfree(node); + break; + } + } +} + +static void msm_ext_disp_send_cable_notification(struct msm_ext_disp *ext_disp, + enum msm_ext_disp_cable_state new_state) +{ + int state = EXT_DISPLAY_CABLE_STATE_MAX; + + if (!ext_disp) { + pr_err("Invalid params\n"); + return; + } + + state = ext_disp->hdmi_sdev.state; + switch_set_state(&ext_disp->hdmi_sdev, !!new_state); + + pr_debug("Cable state %s %d\n", + ext_disp->hdmi_sdev.state == state ? + "is same" : "switched to", + ext_disp->hdmi_sdev.state); +} + +static int msm_ext_disp_hpd(struct platform_device *pdev, + enum msm_ext_disp_type type, + enum msm_ext_disp_cable_state state) +{ + int ret = 0; + struct msm_ext_disp_init_data *data = NULL; + struct msm_ext_disp *ext_disp = NULL; + + if (!pdev) { + pr_err("Invalid platform device\n"); + return -EINVAL; + } + + ext_disp = platform_get_drvdata(pdev); + if (!ext_disp) { + pr_err("Invalid drvdata\n"); + return -EINVAL; + } + + mutex_lock(&ext_disp->lock); + + pr_debug("HPD for display (%s), NEW STATE = %d\n", + msm_ext_disp_name(type), state); + + if (state < EXT_DISPLAY_CABLE_DISCONNECT || + state >= EXT_DISPLAY_CABLE_STATE_MAX) { + pr_err("Invalid HPD state (%d)\n", state); + ret = -EINVAL; + goto end; + } + + if ((state == EXT_DISPLAY_CABLE_CONNECT) && + (ext_disp->current_disp != EXT_DISPLAY_TYPE_MAX)) { + pr_err("Display interface (%s) already connected\n", + msm_ext_disp_name(ext_disp->current_disp)); + ret = -EINVAL; + goto end; + } + + if ((state == EXT_DISPLAY_CABLE_DISCONNECT) && + (ext_disp->current_disp != type)) { + pr_err("Display interface (%s) is not connected\n", + msm_ext_disp_name(type)); + ret = -EINVAL; + goto end; + } + + ret = msm_ext_disp_get_intf_data(ext_disp, type, &data); + if (ret) + goto end; + + if (state == EXT_DISPLAY_CABLE_CONNECT) { + ext_disp->current_disp = data->type; + } else if ((state == EXT_DISPLAY_CABLE_DISCONNECT) && + !ext_disp->ack_enabled) { + ext_disp->ops->audio_info_setup = NULL; + ext_disp->ops->get_audio_edid_blk = NULL; + ext_disp->ops->cable_status = NULL; + ext_disp->ops->get_intf_id = NULL; + ext_disp->current_disp = EXT_DISPLAY_TYPE_MAX; + } + + msm_ext_disp_send_cable_notification(ext_disp, state); + + pr_debug("Hpd (%d) for display (%s)\n", state, + msm_ext_disp_name(type)); + +end: + mutex_unlock(&ext_disp->lock); + + return ret; +} + +static int msm_ext_disp_get_intf_id(struct platform_device *pdev) +{ + int ret = 0; + struct msm_ext_disp *ext_disp = NULL; + + if (!pdev) { + pr_err("No platform device\n"); + ret = -ENODEV; + goto end; + } + + ext_disp = platform_get_drvdata(pdev); + if (!ext_disp) { + pr_err("No drvdata found\n"); + ret = -ENODEV; + goto end; + } + + mutex_lock(&ext_disp->lock); + ret = ext_disp->current_disp; + mutex_unlock(&ext_disp->lock); + +end: + return ret; +} + +static int msm_ext_disp_notify(struct platform_device *pdev, + enum msm_ext_disp_cable_state new_state) +{ + int ret = 0; + int state = 0; + bool switched; + struct msm_ext_disp_init_data *data = NULL; + struct msm_ext_disp *ext_disp = NULL; + + if (!pdev) { + pr_err("Invalid platform device\n"); + return -EINVAL; + } + + ext_disp = platform_get_drvdata(pdev); + if (!ext_disp) { + pr_err("Invalid drvdata\n"); + return -EINVAL; + } + + mutex_lock(&ext_disp->lock); + + if (state < EXT_DISPLAY_CABLE_DISCONNECT || + state >= EXT_DISPLAY_CABLE_STATE_MAX) { + pr_err("Invalid state (%d)\n", state); + ret = -EINVAL; + goto end; + } + + state = ext_disp->audio_sdev.state; + if (state == new_state) + goto end; + + if (ext_disp->ack_enabled && + atomic_read(&ext_disp->ack_pending)) { + ret = -EINVAL; + pr_err("%s ack pending, not notifying %s\n", + state ? "connect" : "disconnect", + new_state ? "connect" : "disconnect"); + goto end; + } + + ret = msm_ext_disp_get_intf_data(ext_disp, ext_disp->current_disp, + &data); + if (ret) + goto end; + + if (new_state == EXT_DISPLAY_CABLE_CONNECT) { + ext_disp->ops->audio_info_setup = + data->codec_ops.audio_info_setup; + ext_disp->ops->get_audio_edid_blk = + data->codec_ops.get_audio_edid_blk; + ext_disp->ops->cable_status = + data->codec_ops.cable_status; + ext_disp->ops->get_intf_id = + msm_ext_disp_get_intf_id; + } + + switch_set_state(&ext_disp->audio_sdev, (int)new_state); + switched = ext_disp->audio_sdev.state != state; + + if (ext_disp->ack_enabled && switched) + atomic_set(&ext_disp->ack_pending, 1); + + pr_debug("audio %s %s\n", switched ? "switched to" : "same as", + ext_disp->audio_sdev.state ? "HDMI" : "SPKR"); + +end: + mutex_unlock(&ext_disp->lock); + + return ret; +} + +static int msm_ext_disp_audio_ack(struct platform_device *pdev, u32 ack) +{ + u32 ack_hpd; + u32 hpd; + int ret = 0; + struct msm_ext_disp *ext_disp = NULL; + + if (!pdev) { + pr_err("Invalid platform device\n"); + return -EINVAL; + } + + ext_disp = platform_get_drvdata(pdev); + if (!ext_disp) { + pr_err("Invalid drvdata\n"); + return -EINVAL; + } + + mutex_lock(&ext_disp->lock); + + hpd = ext_disp->current_disp != EXT_DISPLAY_TYPE_MAX; + + if (ack & AUDIO_ACK_SET_ENABLE) { + ext_disp->ack_enabled = ack & AUDIO_ACK_ENABLE ? + true : false; + + pr_debug("audio ack feature %s\n", + ext_disp->ack_enabled ? "enabled" : "disabled"); + goto end; + } + + if (!ext_disp->ack_enabled) + goto end; + + atomic_set(&ext_disp->ack_pending, 0); + + ack_hpd = ack & AUDIO_ACK_CONNECT; + + pr_debug("acknowledging %s\n", + ack_hpd ? "connect" : "disconnect"); + + /** + * If the ack feature is enabled and we receive an ack for + * disconnect then we reset the current display state to + * empty. + */ + if (!ack_hpd) { + ext_disp->ops->audio_info_setup = NULL; + ext_disp->ops->get_audio_edid_blk = NULL; + ext_disp->ops->cable_status = NULL; + ext_disp->ops->get_intf_id = NULL; + ext_disp->current_disp = EXT_DISPLAY_TYPE_MAX; + } + + if (ack_hpd != hpd) { + pr_err("unbalanced audio state, ack %d, hpd %d\n", + ack_hpd, hpd); + + mutex_unlock(&ext_disp->lock); + + ret = msm_ext_disp_notify(pdev, hpd); + + return ret; + } + +end: + mutex_unlock(&ext_disp->lock); + + return ret; +} + +int msm_ext_disp_register_audio_codec(struct platform_device *pdev, + struct msm_ext_disp_audio_codec_ops *ops) +{ + int ret = 0; + struct msm_ext_disp *ext_disp = NULL; + + if (!pdev || !ops) { + pr_err("Invalid params\n"); + return -EINVAL; + } + + ext_disp = platform_get_drvdata(pdev); + if (!ext_disp) { + pr_err("Invalid drvdata\n"); + return -EINVAL; + } + + mutex_lock(&ext_disp->lock); + + if ((ext_disp->current_disp != EXT_DISPLAY_TYPE_MAX) + && ext_disp->ops) { + pr_err("Codec already registered\n"); + ret = -EINVAL; + goto end; + } + + ext_disp->ops = ops; + + pr_debug("audio codec registered\n"); + +end: + mutex_unlock(&ext_disp->lock); + + return ret; +} + +int msm_ext_disp_register_intf(struct platform_device *pdev, + struct msm_ext_disp_init_data *init_data) +{ + int ret = 0; + struct msm_ext_disp_init_data *data = NULL; + struct msm_ext_disp *ext_disp = NULL; + + if (!pdev || !init_data) { + pr_err("Invalid params\n"); + return -EINVAL; + } + + ext_disp = platform_get_drvdata(pdev); + if (!ext_disp) { + pr_err("Invalid drvdata\n"); + return -EINVAL; + } + + mutex_lock(&ext_disp->lock); + + ret = msm_ext_disp_get_intf_data(ext_disp, init_data->type, &data); + if (!ret) { + pr_debug("Display (%s) already registered\n", + msm_ext_disp_name(init_data->type)); + goto end; + } + + ret = msm_ext_disp_add_intf_data(ext_disp, init_data); + if (ret) + goto end; + + ret = msm_ext_disp_sysfs_create(init_data); + if (ret) + goto sysfs_failure; + + init_data->intf_ops.hpd = msm_ext_disp_hpd; + init_data->intf_ops.notify = msm_ext_disp_notify; + + pr_debug("Display (%s) registered\n", + msm_ext_disp_name(init_data->type)); + + mutex_unlock(&ext_disp->lock); + + return ret; + +sysfs_failure: + msm_ext_disp_remove_intf_data(ext_disp, init_data->type); +end: + mutex_unlock(&ext_disp->lock); + + return ret; +} + +static int msm_ext_disp_probe(struct platform_device *pdev) +{ + int ret = 0; + struct device_node *of_node = NULL; + struct msm_ext_disp *ext_disp = NULL; + + if (!pdev) { + pr_err("No platform device found\n"); + ret = -ENODEV; + goto end; + } + + of_node = pdev->dev.of_node; + if (!of_node) { + pr_err("No device node found\n"); + ret = -ENODEV; + goto end; + } + + ext_disp = devm_kzalloc(&pdev->dev, sizeof(*ext_disp), GFP_KERNEL); + if (!ext_disp) { + ret = -ENOMEM; + goto end; + } + + platform_set_drvdata(pdev, ext_disp); + ext_disp->pdev = pdev; + + ret = msm_ext_disp_switch_dev_register(ext_disp); + if (ret) + goto switch_dev_failure; + + mutex_init(&ext_disp->lock); + + INIT_LIST_HEAD(&ext_disp->display_list); + ext_disp->current_disp = EXT_DISPLAY_TYPE_MAX; + + return ret; + +switch_dev_failure: + devm_kfree(&ext_disp->pdev->dev, ext_disp); +end: + return ret; +} + +static int msm_ext_disp_remove(struct platform_device *pdev) +{ + int ret = 0; + struct msm_ext_disp *ext_disp = NULL; + + if (!pdev) { + pr_err("No platform device\n"); + ret = -ENODEV; + goto end; + } + + ext_disp = platform_get_drvdata(pdev); + if (!ext_disp) { + pr_err("No drvdata found\n"); + ret = -ENODEV; + goto end; + } + + msm_ext_disp_switch_dev_unregister(ext_disp); + + mutex_destroy(&ext_disp->lock); + devm_kfree(&ext_disp->pdev->dev, ext_disp); + +end: + return ret; +} + +static const struct of_device_id msm_ext_dt_match[] = { + {.compatible = "qcom,msm-ext-disp",}, + { /* Sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, msm_ext_dt_match); + +static struct platform_driver this_driver = { + .probe = msm_ext_disp_probe, + .remove = msm_ext_disp_remove, + .driver = { + .name = "msm-ext-disp", + .of_match_table = msm_ext_dt_match, + }, +}; + +static int __init msm_ext_disp_init(void) +{ + int ret = 0; + + ret = platform_driver_register(&this_driver); + if (ret) + pr_err("failed, ret = %d\n", ret); + + return ret; +} + +static void __exit msm_ext_disp_exit(void) +{ + platform_driver_unregister(&this_driver); +} + +module_init(msm_ext_disp_init); +module_exit(msm_ext_disp_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MSM External Display"); + diff --git a/include/linux/msm_ext_display.h b/include/linux/msm_ext_display.h index c0a506fa66ec..81a95657a719 100644 --- a/include/linux/msm_ext_display.h +++ b/include/linux/msm_ext_display.h @@ -17,11 +17,15 @@ #include #include -/* - * External display cable notify handler structure. - * link A link for the linked list - * status Current status of HDMI/DP cable connection - * hpd_notify Callback function to provide cable status +#define AUDIO_ACK_SET_ENABLE BIT(5) +#define AUDIO_ACK_ENABLE BIT(4) +#define AUDIO_ACK_CONNECT BIT(0) + +/** + * struct ext_disp_cable_notify - cable notify handler structure + * @link: a link for the linked list + * @status: current status of HDMI/DP cable connection + * @hpd_notify: callback function to provide cable status */ struct ext_disp_cable_notify { struct list_head link; @@ -45,14 +49,96 @@ struct msm_ext_disp_audio_setup_params { u32 sample_present; }; +/** + * External Display identifier for use to determine which interface + * the audio driver is interacting with. + */ +enum msm_ext_disp_type { + EXT_DISPLAY_TYPE_HDMI, + EXT_DISPLAY_TYPE_DP, + EXT_DISPLAY_TYPE_MAX +}; + +/** + * External Display cable state used by display interface to indicate + * connect/disconnect of interface. + */ +enum msm_ext_disp_cable_state { + EXT_DISPLAY_CABLE_DISCONNECT, + EXT_DISPLAY_CABLE_CONNECT, + EXT_DISPLAY_CABLE_STATE_MAX +}; + +/** + * External Display power state used by display interface to indicate + * power on/off of the interface. + */ +enum msm_ext_disp_power_state { + EXT_DISPLAY_POWER_OFF, + EXT_DISPLAY_POWER_ON, + EXT_DISPLAY_POWER_MAX +}; + +/** + * struct msm_ext_disp_intf_ops - operations exposed to display interface + * @hpd: updates external display interface state + * @notify: updates audio framework with interface state + */ +struct msm_ext_disp_intf_ops { + int (*hpd)(struct platform_device *pdev, + enum msm_ext_disp_type type, + enum msm_ext_disp_cable_state state); + int (*notify)(struct platform_device *pdev, + enum msm_ext_disp_cable_state state); + int (*ack)(struct platform_device *pdev, + u32 ack); +}; + +/** + * struct msm_ext_disp_audio_codec_ops - operations exposed to audio codec + * @audio_info_setup: configure audio on interface + * @get_audio_edid_blk: retrieve audio edid block + * @cable_status: cable connected/disconnected + * @get_intf_id: id of connected interface + */ struct msm_ext_disp_audio_codec_ops { int (*audio_info_setup)(struct platform_device *pdev, struct msm_ext_disp_audio_setup_params *params); int (*get_audio_edid_blk)(struct platform_device *pdev, struct msm_ext_disp_audio_edid_blk *blk); int (*cable_status)(struct platform_device *pdev, u32 vote); + int (*get_intf_id)(struct platform_device *pdev); +}; + +/* + * struct msm_ext_disp_init_data - data needed to register the display interface + * @disp: external display type + * @intf_ops: external display interface operations + * @codec_ops: audio codec operations + */ +struct msm_ext_disp_init_data { + enum msm_ext_disp_type type; + struct kobject *kobj; + struct msm_ext_disp_intf_ops intf_ops; + struct msm_ext_disp_audio_codec_ops codec_ops; }; +/* + * msm_ext_disp_register_audio_codec() - audio codec registration + * @pdev: platform device pointer + * @codec_ops: audio codec operations + */ +int msm_ext_disp_register_audio_codec(struct platform_device *pdev, + struct msm_ext_disp_audio_codec_ops *ops); + +/* + * msm_ext_disp_register_intf() - display interface registration + * @init_data: data needed to register the display interface + */ +int msm_ext_disp_register_intf(struct platform_device *pdev, + struct msm_ext_disp_init_data *init_data); + +/* TODO: remove all the display specific functions below */ #ifdef CONFIG_FB_MSM_MDSS_DP_PANEL int msm_dp_register_audio_codec(struct platform_device *pdev, struct msm_ext_disp_audio_codec_ops *ops); -- cgit v1.2.3 From d2d512e984cc9a75967599cb726b003393de7385 Mon Sep 17 00:00:00 2001 From: Tatenda Chipeperekwa Date: Wed, 3 Aug 2016 11:50:19 -0700 Subject: ARM: dts: msm: add MSM External display node for msmcobalt Add MSM External display node for msmcobalt which is needed to manage the interaction between external displays (HDMI and Display Port) and the audio and display frameworks. CRs-Fixed: 1009284 Change-Id: I54c1eee7a3e1d8f481a9bc629e28f13c5849a3db Signed-off-by: Tatenda Chipeperekwa --- arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi index a99ce727c195..ca2ae82a79ad 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi @@ -586,7 +586,11 @@ "mmss_noc_ahb_clk", "mmss_smmu_ahb_clk", "mmss_smmu_axi_clk"; - }; + }; + }; + + msm_ext_disp: qcom,msm_ext_disp { + compatible = "qcom,msm-ext-disp"; }; mdss_hdmi_tx: qcom,hdmi_tx@c9a0000 { @@ -606,6 +610,8 @@ 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>, -- cgit v1.2.3