Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a3ce4038 authored by Tatenda Chipeperekwa's avatar Tatenda Chipeperekwa
Browse files

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: default avatarTatenda Chipeperekwa <tatendac@codeaurora.org>
parent 1bdf3dcb
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -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>;
+1 −0
Original line number Diff line number Diff line
@@ -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
+1 −100
Original line number Diff line number Diff line
@@ -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);
}
}
+0 −21
Original line number Diff line number Diff line
@@ -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);
};

/**
+80 −119
Original line number Diff line number Diff line
@@ -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;
	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);
}
	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 */

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);
Loading