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

Commit 64e6fee5 authored by Jin Li's avatar Jin Li Committed by Abhinav Kumar
Browse files

drm/sde: add support for customized mode



Sometimes the HDMI is treated as non-pluggable display in auto
use cases. Add support to configure it through dtsi file, and
also provide timing parameters for the customized modes through
dtsi.

Change-Id: I2326b6c43cb7e6361be1f14d25f0e2e493c94177
Signed-off-by: default avatarJin Li <jinl@codeaurora.org>
Signed-off-by: default avatarAbhinav Kumar <abhinavk@codeaurora.org>
parent 95f9ae3d
Loading
Loading
Loading
Loading
+37 −0
Original line number Diff line number Diff line
@@ -7,6 +7,25 @@ Required properties:
Optional properties:
- qcom,display-type: display type of this manager. It could be "primary",
  "secondary", "tertiary", etc.
- qcom,non-pluggable: Boolean to indicate if display is non pluggable.
- qcom,customize-modes: Customized modes when it's non pluggable display.
- qcom,customize-mode-id: Customized mode node.
- qcom,mode-name: String which indicates the mode name which shall be used
   by the connector in non pluggable mode. Refer the example below for details.
   In pluggable mode, the modes shall be filled up
   after edid parsing.
- qcom,mode-h-active: Horizontal active pixels for this mode.
- qcom,mode-h-front-porch: Horizontal front porch in pixels for this mode.
- qcom,mode-h-pulse-width: Horizontal sync width in pixels for this mode.
- qcom,mode-h-back-porch: Horizontal back porch in pixels for this mode.
- qcom,mode-h-active-high: Boolean to indicate if mode horizontal polarity is active high.
- qcom,mode-v-active: Vertical active lines for this mode.
- qcom,mode-v-front-porch: Vertical front porch in lines for this mode.
- qcom,mode-v-pulse-width: Vertical sync width in lines for this mode.
- qcom,mode-v-back-porch: Vertical back porch in lines for this mode.
- qcom,mode-v-active-high: Boolean to indicate if mode vertical polarity is active high.
- qcom,mode-refersh-rate: Mode refresh rate in hertz.
- qcom,mode-clock-in-khz: Mode pixel clock in KHz.

Example:

@@ -17,6 +36,24 @@ Example:
		compatible = "qcom,hdmi-display";
		label = "hdmi_display";
		qcom,display-type = "secondary";
		qcom,non-pluggable;
		qcom,customize-modes {
			qcom,customize-mode-id@0 {
				qcom,mode-name = "3840x2160@30Hz";
				qcom,mode-h-active = <3840>;
				qcom,mode-h-front-porch = <176>;
				qcom,mode-h-pulse-width = <88>;
				qcom,mode-h-back-porch = <296>;
				qcom,mode-h-active-high;
				qcom,mode-v-active = <2160>;
				qcom,mode-v-front-porch = <8>;
				qcom,mode-v-pulse-width = <10>;
				qcom,mode-v-back-porch = <72>;
				qcom,mode-v-active-high;
				qcom,mode-refersh-rate = <30>;
				qcom,mode-clock-in-khz = <297000>;
			};
		};
	};

};
+231 −22
Original line number Diff line number Diff line
@@ -463,8 +463,11 @@ int sde_hdmi_get_info(struct msm_display_info *info,
	info->num_of_h_tiles = 1;
	info->h_tile_instance[0] = 0;
	info->is_connected = true;
	info->capabilities = MSM_DISPLAY_CAP_HOT_PLUG | MSM_DISPLAY_CAP_EDID |
				MSM_DISPLAY_CAP_VID_MODE;
	if (hdmi_display->non_pluggable)
		info->capabilities = MSM_DISPLAY_CAP_VID_MODE;
	else
		info->capabilities = MSM_DISPLAY_CAP_HOT_PLUG |
				MSM_DISPLAY_CAP_EDID | MSM_DISPLAY_CAP_VID_MODE;
	info->max_width = 1920;
	info->max_height = 1080;
	info->compression = MSM_DISPLAY_COMPRESS_NONE;
@@ -603,6 +606,7 @@ int sde_hdmi_connector_get_modes(struct drm_connector *connector, void *display)
	struct sde_hdmi *hdmi_display = (struct sde_hdmi *)display;
	struct hdmi *hdmi;
	struct edid *edid;
	struct drm_display_mode *mode, *m;
	uint32_t hdmi_ctrl;
	int ret = 0;

@@ -615,8 +619,22 @@ int sde_hdmi_connector_get_modes(struct drm_connector *connector, void *display)
	SDE_DEBUG("\n");

	hdmi = hdmi_display->ctrl.ctrl;
	if (hdmi_display->non_pluggable) {
		list_for_each_entry(mode, &hdmi_display->mode_list, head) {
			m = drm_mode_duplicate(connector->dev, mode);
			if (!m) {
				SDE_ERROR("failed to add hdmi mode %dx%d\n",
					mode->hdisplay, mode->vdisplay);
				break;
			}
			drm_mode_probed_add(connector, m);
		}
		ret = hdmi_display->num_of_modes;
	} else {
		/* Read EDID */
		hdmi_ctrl = hdmi_read(hdmi, REG_HDMI_CTRL);
		hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl | HDMI_CTRL_ENABLE);

		edid = drm_get_edid(connector, hdmi->i2c);

		hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl);
@@ -628,6 +646,7 @@ int sde_hdmi_connector_get_modes(struct drm_connector *connector, void *display)
			ret = drm_add_edid_modes(connector, edid);
			kfree(edid);
		}
	}

	return ret;
}
@@ -755,12 +774,194 @@ static const struct component_ops sde_hdmi_comp_ops = {
	.unbind = sde_hdmi_unbind,
};

static int _sde_hdmi_parse_dt_modes(struct device_node *np,
					struct list_head *head,
					u32 *num_of_modes)
{
	int rc = 0;
	struct drm_display_mode *mode;
	u32 mode_count = 0;
	struct device_node *node = NULL;
	struct device_node *root_node = NULL;
	const char *name;
	u32 h_front_porch, h_pulse_width, h_back_porch;
	u32 v_front_porch, v_pulse_width, v_back_porch;
	bool h_active_high, v_active_high;
	u32 flags = 0;

	root_node = of_get_child_by_name(np, "qcom,customize-modes");
	if (!root_node) {
		root_node = of_parse_phandle(np, "qcom,customize-modes", 0);
		if (!root_node) {
			DRM_INFO("No entry present for qcom,customize-modes");
			goto end;
		}
	}
	for_each_child_of_node(root_node, node) {
		rc = 0;
		mode = kzalloc(sizeof(*mode), GFP_KERNEL);
		if (!mode) {
			SDE_ERROR("Out of memory\n");
			rc =  -ENOMEM;
			continue;
		}

		rc = of_property_read_string(node, "qcom,mode-name",
						&name);
		if (rc) {
			SDE_ERROR("failed to read qcom,mode-name, rc=%d\n", rc);
			goto fail;
		}
		strlcpy(mode->name, name, DRM_DISPLAY_MODE_LEN);

		rc = of_property_read_u32(node, "qcom,mode-h-active",
						&mode->hdisplay);
		if (rc) {
			SDE_ERROR("failed to read h-active, rc=%d\n", rc);
			goto fail;
		}

		rc = of_property_read_u32(node, "qcom,mode-h-front-porch",
						&h_front_porch);
		if (rc) {
			SDE_ERROR("failed to read h-front-porch, rc=%d\n", rc);
			goto fail;
		}

		rc = of_property_read_u32(node, "qcom,mode-h-pulse-width",
						&h_pulse_width);
		if (rc) {
			SDE_ERROR("failed to read h-pulse-width, rc=%d\n", rc);
			goto fail;
		}

		rc = of_property_read_u32(node, "qcom,mode-h-back-porch",
						&h_back_porch);
		if (rc) {
			SDE_ERROR("failed to read h-back-porch, rc=%d\n", rc);
			goto fail;
		}

		h_active_high = of_property_read_bool(node,
						"qcom,mode-h-active-high");

		rc = of_property_read_u32(node, "qcom,mode-v-active",
						&mode->vdisplay);
		if (rc) {
			SDE_ERROR("failed to read v-active, rc=%d\n", rc);
			goto fail;
		}

		rc = of_property_read_u32(node, "qcom,mode-v-front-porch",
						&v_front_porch);
		if (rc) {
			SDE_ERROR("failed to read v-front-porch, rc=%d\n", rc);
			goto fail;
		}

		rc = of_property_read_u32(node, "qcom,mode-v-pulse-width",
						&v_pulse_width);
		if (rc) {
			SDE_ERROR("failed to read v-pulse-width, rc=%d\n", rc);
			goto fail;
		}

		rc = of_property_read_u32(node, "qcom,mode-v-back-porch",
						&v_back_porch);
		if (rc) {
			SDE_ERROR("failed to read v-back-porch, rc=%d\n", rc);
			goto fail;
		}

		v_active_high = of_property_read_bool(node,
						"qcom,mode-v-active-high");

		rc = of_property_read_u32(node, "qcom,mode-refersh-rate",
						&mode->vrefresh);
		if (rc) {
			SDE_ERROR("failed to read refersh-rate, rc=%d\n", rc);
			goto fail;
		}

		rc = of_property_read_u32(node, "qcom,mode-clock-in-khz",
						&mode->clock);
		if (rc) {
			SDE_ERROR("failed to read clock, rc=%d\n", rc);
			goto fail;
		}

		mode->hsync_start = mode->hdisplay + h_front_porch;
		mode->hsync_end = mode->hsync_start + h_pulse_width;
		mode->htotal = mode->hsync_end + h_back_porch;
		mode->vsync_start = mode->vdisplay + v_front_porch;
		mode->vsync_end = mode->vsync_start + v_pulse_width;
		mode->vtotal = mode->vsync_end + v_back_porch;
		if (h_active_high)
			flags |= DRM_MODE_FLAG_PHSYNC;
		else
			flags |= DRM_MODE_FLAG_NHSYNC;
		if (v_active_high)
			flags |= DRM_MODE_FLAG_PVSYNC;
		else
			flags |= DRM_MODE_FLAG_NVSYNC;
		mode->flags = flags;

		if (!rc) {
			mode_count++;
			list_add_tail(&mode->head, head);
		}

		SDE_DEBUG("mode[%d] h[%d,%d,%d,%d] v[%d,%d,%d,%d] %d %xH %d\n",
			mode_count - 1, mode->hdisplay, mode->hsync_start,
			mode->hsync_end, mode->htotal, mode->vdisplay,
			mode->vsync_start, mode->vsync_end, mode->vtotal,
			mode->vrefresh, mode->flags, mode->clock);
fail:
		if (rc) {
			kfree(mode);
			continue;
		}
	}

	if (num_of_modes)
		*num_of_modes = mode_count;

end:
	return rc;
}

static int _sde_hdmi_parse_dt(struct device_node *node,
				struct sde_hdmi *display)
{
	int rc = 0;

	display->name = of_get_property(node, "label", NULL);

	display->display_type = of_get_property(node,
						"qcom,display-type", NULL);
	if (!display->display_type)
		display->display_type = "unknown";

	display->non_pluggable = of_property_read_bool(node,
						"qcom,non-pluggable");

	rc = _sde_hdmi_parse_dt_modes(node, &display->mode_list,
					&display->num_of_modes);
	if (rc)
		SDE_ERROR("parse_dt_modes failed rc=%d\n", rc);

	return rc;
}

static int _sde_hdmi_dev_probe(struct platform_device *pdev)
{
	int rc;
	struct sde_hdmi *display;
	int ret = 0;

	DBG("");

	SDE_DEBUG("\n");

	if (!pdev || !pdev->dev.of_node) {
		SDE_ERROR("pdev not found\n");
		return -ENODEV;
@@ -770,16 +971,12 @@ static int _sde_hdmi_dev_probe(struct platform_device *pdev)
	if (!display)
		return -ENOMEM;

	DBG("");
	display->name = of_get_property(pdev->dev.of_node, "label", NULL);

	display->display_type = of_get_property(pdev->dev.of_node,
						"qcom,display-type", NULL);
	if (!display->display_type)
		display->display_type = "unknown";
	INIT_LIST_HEAD(&display->mode_list);
	rc = _sde_hdmi_parse_dt(pdev->dev.of_node, display);
	if (rc)
		SDE_ERROR("parse dt failed, rc=%d\n", rc);

	mutex_init(&display->display_lock);

	display->pdev = pdev;
	platform_set_drvdata(pdev, display);
	mutex_lock(&sde_hdmi_list_lock);
@@ -787,17 +984,24 @@ static int _sde_hdmi_dev_probe(struct platform_device *pdev)
	mutex_unlock(&sde_hdmi_list_lock);
	if (!sde_hdmi_dev_init(display)) {
		ret = component_add(&pdev->dev, &sde_hdmi_comp_ops);
		if (ret)
		if (ret) {
			pr_err("component add failed\n");
			goto out;
		}
	}
	return 0;

	return ret;
out:
	if (rc)
		devm_kfree(&pdev->dev, display);
	return rc;
}

static int _sde_hdmi_dev_remove(struct platform_device *pdev)
{
	struct sde_hdmi *display;
	struct sde_hdmi *pos, *tmp;
	struct drm_display_mode *mode, *n;

	if (!pdev) {
		SDE_ERROR("Invalid device\n");
@@ -815,6 +1019,11 @@ static int _sde_hdmi_dev_remove(struct platform_device *pdev)
	}
	mutex_unlock(&sde_hdmi_list_lock);

	list_for_each_entry_safe(mode, n, &display->mode_list, head) {
		list_del(&mode->head);
		kfree(mode);
	}

	platform_set_drvdata(pdev, NULL);
	devm_kfree(&pdev->dev, display);
	return 0;
+5 −1
Original line number Diff line number Diff line
@@ -68,7 +68,9 @@ struct sde_hdmi_ctrl {
 * @list:             List pointer.
 * @display_lock:     Mutex for sde_hdmi interface.
 * @ctrl:             Controller information for HDMI display.
 * @num_of_modes:     Number of modes supported by display.
 * @non_pluggable:    If HDMI display is non pluggable
 * @num_of_modes:     Number of modes supported by display if non pluggable.
 * @mode_list:        Mode list if non pluggable.
 * @is_tpg_enabled:   TPG state.
 * @hpd_work:         HPD work structure.
 * @root:             Debug fs root entry.
@@ -84,7 +86,9 @@ struct sde_hdmi {

	struct sde_hdmi_ctrl ctrl;

	bool non_pluggable;
	u32 num_of_modes;
	struct list_head mode_list;
	bool is_tpg_enabled;

	struct work_struct hpd_work;