diff options
-rw-r--r-- | Documentation/devicetree/bindings/fb/msm-hdmi-tx.txt | 12 | ||||
-rw-r--r-- | arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi | 8 | ||||
-rw-r--r-- | drivers/video/fbdev/msm/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/fbdev/msm/mdss_hdmi_audio.c | 101 | ||||
-rw-r--r-- | drivers/video/fbdev/msm/mdss_hdmi_audio.h | 21 | ||||
-rw-r--r-- | drivers/video/fbdev/msm/mdss_hdmi_tx.c | 199 | ||||
-rw-r--r-- | drivers/video/fbdev/msm/mdss_hdmi_tx.h | 2 | ||||
-rw-r--r-- | drivers/video/fbdev/msm/msm_ext_display.c | 748 | ||||
-rw-r--r-- | include/linux/msm_ext_display.h | 96 |
9 files changed, 942 insertions, 246 deletions
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/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi index 7f1f2c10f6c4..6025d9b54351 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>, 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 <linux/mdss_io_util.h> #include <linux/msm_ext_display.h> -#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 <linux/slab.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/iopoll.h> +#include <linux/types.h> +#include <linux/switch.h> +#include <linux/of_platform.h> +#include <linux/msm_ext_display.h> + +#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 <linux/device.h> #include <linux/platform_device.h> -/* - * 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); |