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

Commit 280f734d authored by Jin Li's avatar Jin Li
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>
parent f93ca2d7
Loading
Loading
Loading
Loading
+34 −0
Original line number Diff line number Diff line
@@ -7,6 +7,22 @@ Required properties:
Optional properties:
- qcom,display-type: display type of this manager. It could be "primary",
  "secondary", "tertiary", etc.
- qcom,non-pluggable: 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: Mode name.
- qcom,mode-h-active: Mode horizontal active pixels.
- qcom,mode-h-front-porch: Mode horizontal front porch in pixels.
- qcom,mode-h-pulse-width: Mode horizontal sync width in pixels.
- qcom,mode-h-back-porch: Mode horizontal back porch in pixels.
- qcom,mode-h-active-high: If mode horizontal polarity is active high.
- qcom,mode-v-active: Mode virtical active lines.
- qcom,mode-v-front-porch: Mode virtical front porch in lines.
- qcom,mode-v-pulse-width: Mode virtical sync width in lines.
- qcom,mode-v-back-porch: Mode virtical back porch in lines.
- qcom,mode-v-active-high: If mode virtical 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 +33,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>;
			};
		};
	};

};
+228 −22
Original line number Diff line number Diff line
@@ -475,15 +475,18 @@ int sde_hdmi_get_info(struct msm_display_info *info,
		return -EINVAL;
	}

	DBG("");
	SDE_DEBUG("\n");
	mutex_lock(&hdmi_display->display_lock);

	info->intf_type = DRM_MODE_CONNECTOR_HDMIA;
	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 = 4096;
	info->max_height = 2160;
	info->compression = MSM_DISPLAY_COMPRESS_NONE;
@@ -623,6 +626,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;

@@ -635,6 +639,19 @@ 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);

@@ -649,6 +666,7 @@ int sde_hdmi_connector_get_modes(struct drm_connector *connector, void *display)
			ret = drm_add_edid_modes(connector, edid);
			kfree(edid);
		}
	}

	return ret;
}
@@ -753,11 +771,191 @@ int sde_hdmi_unbind(struct sde_hdmi *display)
	return 0;
}

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, *n;
	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;

	SDE_DEBUG("\n");

	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) {
		mode = kzalloc(sizeof(*mode), GFP_KERNEL);
		if (!mode) {
			SDE_ERROR("Out of memory\n");
			rc =  -ENOMEM;
			goto end;
		}
		mode_count++;
		list_add_tail(&mode->head, head);
		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 end;
		}
		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 end;
		}

		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 end;
		}

		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 end;
		}

		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 end;
		}

		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 end;
		}

		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 end;
		}

		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 end;
		}

		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 end;
		}

		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 end;
		}

		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 end;
		}

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

	if (num_of_modes)
		*num_of_modes = mode_count;

end:
	if (rc && mode_count) {
		list_for_each_entry_safe(mode, n, head, head) {
			list_del(&mode->head);
			kfree(mode);
		}
	}

	return rc;
}

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

	SDE_DEBUG("\n");

	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;

	DBG("");
	SDE_DEBUG("\n");

	if (!pdev || !pdev->dev.of_node) {
		SDE_ERROR("pdev not found\n");
@@ -768,28 +966,31 @@ 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);
		goto out;
	}

	mutex_init(&display->display_lock);

	display->pdev = pdev;
	platform_set_drvdata(pdev, display);
	mutex_lock(&sde_hdmi_list_lock);
	list_add(&display->list, &sde_hdmi_list);
	mutex_unlock(&sde_hdmi_list_lock);
	return 0;

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");
@@ -807,6 +1008,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;