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

Commit c033aef7 authored by Ajay Singh Parmar's avatar Ajay Singh Parmar
Browse files

drm/msm/dsi-staging: add support for secondary display



Add secondary DSI device tree node and add it in the list of
supported connectors. On probe, create a secondary DSI device
and add it to the list of components. This is needed to drive
an independent secondary display. In case secondary display is
not supported either by device tree or boot-loader, the subsequent
component binding will be ignored and corresponding connector
and encoder will not be created. So, this approach is designed
to support both single and dual DSI displays.

CRs-Fixed: 2254681
Change-Id: Ida37a80e07cff9ecdb6613af0f755ca5a64aaad4
Signed-off-by: default avatarAjay Singh Parmar <aparmar@codeaurora.org>
parent d14273cf
Loading
Loading
Loading
Loading
+91 −204
Original line number Diff line number Diff line
@@ -45,9 +45,11 @@

static char dsi_display_primary[MAX_CMDLINE_PARAM_LEN];
static char dsi_display_secondary[MAX_CMDLINE_PARAM_LEN];
static struct dsi_display_boot_param boot_displays[MAX_DSI_ACTIVE_DISPLAY];
static struct dsi_display *default_display;
static bool display_from_cmdline;
static struct dsi_display_boot_param boot_displays[MAX_DSI_ACTIVE_DISPLAY] = {
	{.boot_param = dsi_display_primary},
	{.boot_param = dsi_display_secondary},
};

static const struct of_device_id dsi_display_dt_match[] = {
	{.compatible = "qcom,dsi-display"},
	{}
@@ -1919,30 +1921,6 @@ static void dsi_display_parse_cmdline_topology(struct dsi_display *display,
	display->cmdline_timing = value;
}

/**
 * dsi_display_name_compare()- compare whether DSI display name matches.
 * @node:	Pointer to device node structure
 * @display_name: Name of display to validate
 *
 * Return:	returns a bool specifying whether given display is active
 */
static bool dsi_display_name_compare(struct device_node *node,
			const char *display_name, int index)
{
	if (index >= MAX_DSI_ACTIVE_DISPLAY) {
		pr_err("Invalid Index\n");
		return false;
	}

	if (boot_displays[index].boot_disp_en) {
		if (!(strcmp(&boot_displays[index].name[0], display_name))) {
			boot_displays[index].node = node;
			return true;
		}
	}
	return false;
}

/**
 * dsi_display_parse_boot_display_selection()- Parse DSI boot display name
 *
@@ -1952,39 +1930,14 @@ static int dsi_display_parse_boot_display_selection(void)
{
	char *pos = NULL;
	char disp_buf[MAX_CMDLINE_PARAM_LEN] = {'\0'};
	int i, j, num_displays;

	if (strlen(dsi_display_primary) == 0)
		return -EINVAL;
	int i, j;

	if ((strlen(dsi_display_secondary) > 0))
		num_displays = MAX_DSI_ACTIVE_DISPLAY;
	else {
		/*
		 * Initialize secondary dsi variables
		 * for the senario where dsi_display1
		 * is null but dsi_display0 is valid
		 */
	for (i = 0; i < MAX_DSI_ACTIVE_DISPLAY; i++) {
		strlcpy(disp_buf, boot_displays[i].boot_param,
			MAX_CMDLINE_PARAM_LEN);

		/* Max number of displays will be one->only Primary */
		num_displays = 1;
		boot_displays[DSI_SECONDARY].is_primary = false;
		boot_displays[DSI_SECONDARY].name[0] = '\0';
	}
		pos = strnstr(disp_buf, ":", MAX_CMDLINE_PARAM_LEN);

	for (i = 0; i < num_displays; i++) {
		boot_displays[i].is_primary = false;
		if (i == DSI_PRIMARY) {
			strlcpy(disp_buf, &dsi_display_primary[0],
				sizeof(dsi_display_primary));
			pos = strnstr(disp_buf, ":",
				sizeof(dsi_display_primary));
		} else {
			strlcpy(disp_buf, &dsi_display_secondary[0],
				sizeof(dsi_display_secondary));
			pos = strnstr(disp_buf, ":",
				sizeof(dsi_display_secondary));
		}
		/* Use ':' as a delimiter to retrieve the display name */
		if (!pos) {
			pr_debug("display name[%s]is not valid\n", disp_buf);
@@ -1993,69 +1946,13 @@ static int dsi_display_parse_boot_display_selection(void)

		for (j = 0; (disp_buf + j) < pos; j++)
			boot_displays[i].name[j] = *(disp_buf + j);

		boot_displays[i].name[j] = '\0';

		if (i == DSI_PRIMARY) {
			boot_displays[i].is_primary = true;
			/* Currently, secondary DSI display is not supported */
		boot_displays[i].boot_disp_en = true;
	}
	}
	return 0;
}

/**
 * validate_dsi_display_selection()- validate boot DSI display selection
 *
 * Return:	returns true when both displays have unique configurations
 */
static bool validate_dsi_display_selection(void)
{
	int i, j;
	int rc = 0;
	int phy_count = 0;
	int ctrl_count = 0;
	int index = 0;
	bool ctrl_flags[MAX_DSI_ACTIVE_DISPLAY] = {false, false};
	bool phy_flags[MAX_DSI_ACTIVE_DISPLAY] = {false, false};
	struct device_node *node, *ctrl_node, *phy_node;

	for (i = 0; i < MAX_DSI_ACTIVE_DISPLAY; i++) {
		node = boot_displays[i].node;
		ctrl_count = of_count_phandle_with_args(node, "qcom,dsi-ctrl",
								NULL);

		for (j = 0; j < ctrl_count; j++) {
			ctrl_node = of_parse_phandle(node, "qcom,dsi-ctrl", j);
			rc = of_property_read_u32(ctrl_node, "cell-index",
					&index);
			of_node_put(ctrl_node);
			if (rc) {
				pr_err("cell index not set for ctrl_nodes\n");
				return false;
			}
			if (ctrl_flags[index])
				return false;
			ctrl_flags[index] = true;
		}

		phy_count = of_count_phandle_with_args(node, "qcom,dsi-phy",
								NULL);
		for (j = 0; j < phy_count; j++) {
			phy_node = of_parse_phandle(node, "qcom,dsi-phy", j);
			rc = of_property_read_u32(phy_node, "cell-index",
					&index);
			of_node_put(phy_node);
			if (rc) {
				pr_err("cell index not set phy_nodes\n");
				return false;
			}
			if (phy_flags[index])
				return false;
			phy_flags[index] = true;
		}
	}
	return true;
	return 0;
}

static int dsi_display_phy_power_on(struct dsi_display *display)
@@ -3840,8 +3737,16 @@ static int _dsi_display_dev_init(struct dsi_display *display)
		return -EINVAL;
	}

	if (!display->disp_node)
		return 0;

	mutex_lock(&display->display_lock);

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

	display->parser = dsi_parser_get(&display->pdev->dev);
	if (display->fw && display->parser)
		display->parser_node = dsi_parser_get_head_node(
@@ -4272,6 +4177,9 @@ static int dsi_display_bind(struct device *dev,
	}
	priv = drm->dev_private;

	if (!display->disp_node)
		return 0;

	mutex_lock(&display->display_lock);

	rc = dsi_display_debugfs_init(display);
@@ -4518,45 +4426,10 @@ static struct platform_driver dsi_display_driver = {
	},
};

static void dsi_display_setup(struct dsi_display *display)
{
	struct platform_device *pdev = display->pdev;

	/* use default topology of every mode if not overridden */
	display->cmdline_topology = NO_OVERRIDE;
	display->cmdline_timing = 0;

	if (boot_displays[DSI_PRIMARY].boot_disp_en) {
		dsi_display_name_compare(pdev->dev.of_node,
			display->name, DSI_PRIMARY);

		dsi_display_parse_cmdline_topology(display, DSI_PRIMARY);
		boot_displays[DSI_PRIMARY].node = pdev->dev.of_node;
		boot_displays[DSI_PRIMARY].disp = display;
	}

	if (boot_displays[DSI_SECONDARY].boot_disp_en) {
		boot_displays[DSI_SECONDARY].node = pdev->dev.of_node;
		boot_displays[DSI_SECONDARY].disp = display;

		if (validate_dsi_display_selection())
			dsi_display_parse_cmdline_topology(display,
				DSI_SECONDARY);
		else
			boot_displays[DSI_SECONDARY].boot_disp_en = false;

	}

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

static int dsi_display_init(struct dsi_display *display,
			 struct platform_device *pdev)
static int dsi_display_init(struct dsi_display *display)
{
	int rc = 0;
	 struct platform_device *pdev = display->pdev;

	mutex_init(&display->display_lock);

@@ -4579,7 +4452,6 @@ static void dsi_display_firmware_display(const struct firmware *fw,
				void *context)
{
	struct dsi_display *display = context;
	struct platform_device *pdev = display->pdev;

	if (fw) {
		pr_debug("reading data from firmware, size=%zd\n",
@@ -4589,9 +4461,7 @@ static void dsi_display_firmware_display(const struct firmware *fw,
		display->name = "dsi_firmware_display";
	}

	dsi_display_setup(display);

	if (dsi_display_init(display, pdev))
	if (dsi_display_init(display))
		return;

	pr_debug("success\n");
@@ -4601,11 +4471,12 @@ int dsi_display_dev_probe(struct platform_device *pdev)
{
	struct dsi_display *display = NULL;
	struct device_node *node = NULL, *disp_node = NULL;
	const char *name = NULL;
	const char *dsi_type = NULL, *name = NULL;
	const char *disp_list = "qcom,dsi-display-list";
	const char *disp_active = "qcom,dsi-display-active";
	int i, count, rc = 0;
	int i, count, rc = 0, index;
	bool firm_req = false;
	struct dsi_display_boot_param *boot_disp;

	if (!pdev || !pdev->dev.of_node) {
		pr_err("pdev not found\n");
@@ -4613,31 +4484,53 @@ int dsi_display_dev_probe(struct platform_device *pdev)
		goto end;
	}

	dsi_type = of_get_property(pdev->dev.of_node, "label", NULL);
	if (!dsi_type)
		dsi_type = "primary";

	if (!strcmp(dsi_type, "primary"))
		index = DSI_PRIMARY;
	else
		index = DSI_SECONDARY;

	boot_disp = &boot_displays[index];

	display = devm_kzalloc(&pdev->dev, sizeof(*display), GFP_KERNEL);
	if (!display) {
		rc = -ENOMEM;
		goto end;
	}

	if (boot_displays[DSI_PRIMARY].boot_disp_en)
		display_from_cmdline = true;

	node = pdev->dev.of_node;
	count = of_count_phandle_with_args(node, disp_list,  NULL);

	for (i = 0; i < count; i++) {
		struct device_node *np;
		const char *disp_type = NULL;

		np = of_parse_phandle(node, disp_list, i);
		name = of_get_property(np, "label", NULL);
		if (!name) {
			pr_err("display name not defined\n");
			continue;
		}

		disp_type = of_get_property(np, "qcom,display-type", NULL);
		if (!disp_type) {
			pr_err("display type not defined for %s\n", name);
			continue;
		}

		/* primary/secondary display should match with current dsi */
		if (strcmp(dsi_type, disp_type))
			continue;

		if (display_from_cmdline) {
			if (name && !strcmp(boot_displays[0].name, name)) {
		if (boot_disp->boot_disp_en) {
			if (!strcmp(boot_disp->name, name)) {
				disp_node = np;
				break;
			}
		} else {
			if (of_property_read_bool(np, disp_active)) {
		} else if (of_property_read_bool(np, disp_active)) {
			disp_node = np;

			if (IS_ENABLED(CONFIG_DSI_PARSER))
@@ -4647,39 +4540,34 @@ int dsi_display_dev_probe(struct platform_device *pdev)
					dsi_display_firmware_display);
			break;
		}
		}

		of_node_put(np);
	}

	if (!name || !disp_node) {
		pr_err("display node not found\n");
		rc = -EINVAL;
		goto end;
	}

	/* decrement ref count */
	of_node_put(disp_node);
	boot_disp->node = pdev->dev.of_node;
	boot_disp->disp = display;

	display->disp_node = disp_node;
	display->name = name;
	display->pdev = pdev;
	display->boot_disp = boot_disp;

	dsi_display_parse_cmdline_topology(display, index);

	platform_set_drvdata(pdev, display);
	default_display = display;

	/* initialize display in firmware callback */
	if (!firm_req) {
		dsi_display_setup(display);

		rc = dsi_display_init(display, pdev);
		rc = dsi_display_init(display);
		if (rc)
			goto end;
	}

	return 0;
end:
	if (display)
		devm_kfree(&pdev->dev, display);

	return rc;
}

@@ -4695,6 +4583,9 @@ int dsi_display_dev_remove(struct platform_device *pdev)

	display = platform_get_drvdata(pdev);

	/* decrement ref count */
	of_node_put(display->disp_node);

	(void)_dsi_display_dev_deinit(display);

	platform_set_drvdata(pdev, NULL);
@@ -4704,13 +4595,12 @@ int dsi_display_dev_remove(struct platform_device *pdev)

int dsi_display_get_num_of_displays(void)
{
	int count = 0, i;

	if (!display_from_cmdline)
		return 1;
	int i, count = 0;

	for (i = 0; i < MAX_DSI_ACTIVE_DISPLAY; i++) {
		if (boot_displays[i].boot_disp_en)
		struct dsi_display *display = boot_displays[i].disp;

		if (display && display->disp_node)
			count++;
	}

@@ -4719,24 +4609,21 @@ int dsi_display_get_num_of_displays(void)

int dsi_display_get_active_displays(void **display_array, u32 max_display_count)
{
	int i = 0;
	int index = 0, count = 0;

	if (!display_array || !max_display_count) {
		if (!display_array)
		pr_err("invalid params\n");
		return 0;
	}

	if (!display_from_cmdline) {
		display_array[0] = default_display;
		return 1;
	}
	for (index = 0; index < MAX_DSI_ACTIVE_DISPLAY; index++) {
		struct dsi_display *display = boot_displays[index].disp;

	for (i = 0; i < max_display_count; i++) {
		if (boot_displays[i].boot_disp_en)
			display_array[i] = boot_displays[i].disp;
		if (display && display->disp_node)
			display_array[count++] = display;
	}
	return i;

	return count;
}

int dsi_display_drm_bridge_init(struct dsi_display *display,
+3 −1
Original line number Diff line number Diff line
@@ -103,8 +103,8 @@ struct dsi_display_ctrl {
 */
struct dsi_display_boot_param {
	char name[MAX_CMDLINE_PARAM_LEN];
	char *boot_param;
	bool boot_disp_en;
	bool is_primary;
	int length;
	struct device_node *node;
	int cmdline_topology;
@@ -246,6 +246,8 @@ struct dsi_display {
	/* firmware panel data */
	const struct firmware *fw;
	void *parser;

	struct dsi_display_boot_param *boot_disp;
};

int dsi_display_dev_probe(struct platform_device *pdev);