drm/msm/dsi-staging: add support for display selection from command-line
This change adds support to retrieve display nodes
from kernel command-line arguments. These nodes
can be used to choose active displays.
Change-Id: I1b2b64416cd445495de67f9c8eadc7ad82877bd3
Signed-off-by: Shashank Babu Chinta Venkata <sbchin@codeaurora.org>
Signed-off-by: Chandan Uddaraju <chandanu@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
index d6a7193..2af09fd 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
@@ -31,7 +31,10 @@
static DEFINE_MUTEX(dsi_display_list_lock);
static LIST_HEAD(dsi_display_list);
-
+static char dsi_display_primary[DSI_DISPLAY_MAX_LEN];
+static char dsi_display_secondary[DSI_DISPLAY_MAX_LEN];
+static struct dsi_display_boot_param boot_displays[MAX_DSI_ACTIVE_DISPLAY];
+static struct device_node *default_active_node;
static const struct of_device_id dsi_display_dt_match[] = {
{.compatible = "qcom,dsi-display"},
{}
@@ -554,6 +557,159 @@
return rc;
}
+/**
+ * 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
+ *
+ * Return: returns error status
+ */
+static int dsi_display_parse_boot_display_selection(void)
+{
+ char *pos = NULL;
+ char disp_buf[DSI_DISPLAY_MAX_LEN] = {'\0'};
+ int i, j, num_displays;
+
+ if (strlen(dsi_display_primary) == 0)
+ return -EINVAL;
+
+ 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
+ */
+
+ /* 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';
+ }
+
+ 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);
+ continue;
+ }
+
+ 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;
+}
+
+struct device_node *dsi_display_get_boot_display(int index)
+{
+
+ pr_err("index = %d\n", index);
+
+ if (boot_displays[index].node)
+ return boot_displays[index].node;
+ else if ((index == (MAX_DSI_ACTIVE_DISPLAY - 1))
+ && (default_active_node))
+ return default_active_node;
+ else
+ return NULL;
+}
+
static int dsi_display_phy_power_on(struct dsi_display *display)
{
int rc = 0;
@@ -2464,6 +2620,7 @@
goto error_panel_deinit;
}
+ pr_info("Successfully bind display panel '%s'\n", display->name);
display->drm_dev = drm;
goto error;
@@ -2561,6 +2718,9 @@
{
int rc = 0;
struct dsi_display *display;
+ static bool display_from_cmdline, boot_displays_parsed;
+ static bool comp_add_success;
+ static struct device_node *primary_np, *secondary_np;
if (!pdev || !pdev->dev.of_node) {
pr_err("pdev not found\n");
@@ -2573,9 +2733,59 @@
display->name = of_get_property(pdev->dev.of_node, "label", NULL);
- display->is_active = of_property_read_bool(pdev->dev.of_node,
- "qcom,dsi-display-active");
+ if (!boot_displays_parsed) {
+ boot_displays[DSI_PRIMARY].boot_disp_en = false;
+ boot_displays[DSI_SECONDARY].boot_disp_en = false;
+ if (dsi_display_parse_boot_display_selection())
+ pr_debug("Display Boot param not valid/available\n");
+ boot_displays_parsed = true;
+ }
+
+
+ if ((!display_from_cmdline) &&
+ (boot_displays[DSI_PRIMARY].boot_disp_en)) {
+ display->is_active = dsi_display_name_compare(pdev->dev.of_node,
+ display->name, DSI_PRIMARY);
+ if (display->is_active) {
+ if (comp_add_success) {
+ (void)_dsi_display_dev_deinit(main_display);
+ component_del(&main_display->pdev->dev,
+ &dsi_display_comp_ops);
+ comp_add_success = false;
+ default_active_node = NULL;
+ pr_debug("removed the existing comp ops\n");
+ }
+ /*
+ * Need to add component for
+ * the secondary DSI display
+ * when more than one DSI display
+ * is supported.
+ */
+ pr_debug("cmdline primary dsi: %s\n",
+ display->name);
+ display_from_cmdline = true;
+ primary_np = pdev->dev.of_node;
+ }
+ }
+
+ if (boot_displays[DSI_SECONDARY].boot_disp_en) {
+ if (!secondary_np) {
+ if (dsi_display_name_compare(pdev->dev.of_node,
+ display->name, DSI_SECONDARY)) {
+ pr_debug("cmdline secondary dsi: %s\n",
+ display->name);
+ secondary_np = pdev->dev.of_node;
+ if (primary_np) {
+ if (validate_dsi_display_selection())
+ display->is_active = true;
+ else
+ boot_displays[DSI_SECONDARY]
+ .boot_disp_en = false;
+ }
+ }
+ }
+ }
display->display_type = of_get_property(pdev->dev.of_node,
"qcom,display-type", NULL);
if (!display->display_type)
@@ -2588,6 +2798,10 @@
list_add(&display->list, &dsi_display_list);
mutex_unlock(&dsi_display_list_lock);
+ if (!display_from_cmdline)
+ display->is_active = of_property_read_bool(pdev->dev.of_node,
+ "qcom,dsi-display-active");
+
if (display->is_active) {
main_display = display;
rc = _dsi_display_dev_init(display);
@@ -2599,6 +2813,11 @@
rc = component_add(&pdev->dev, &dsi_display_comp_ops);
if (rc)
pr_err("component add failed, rc=%d\n", rc);
+
+ comp_add_success = true;
+ pr_debug("Component_add success: %s\n", display->name);
+ if (!display_from_cmdline)
+ default_active_node = pdev->dev.of_node;
}
return rc;
}
@@ -3450,6 +3669,13 @@
dsi_ctrl_drv_unregister();
dsi_phy_drv_unregister();
}
-
+module_param_string(dsi_display0, dsi_display_primary, DSI_DISPLAY_MAX_LEN,
+ 0600);
+MODULE_PARM_DESC(dsi_display0,
+ "msm_drm.dsi_display0=<display node>: where <display node> is 'primary dsi display node name'");
+module_param_string(dsi_display1, dsi_display_secondary, DSI_DISPLAY_MAX_LEN,
+ 0600);
+MODULE_PARM_DESC(dsi_display1,
+ "msm_drm.dsi_display1=<display node>: where <display node> is 'Secondary dsi display node name'");
module_init(dsi_display_register);
module_exit(dsi_display_unregister);
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
index 89f31af..15a3f46 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
@@ -30,6 +30,7 @@
#define MAX_DSI_CTRLS_PER_DISPLAY 2
#define DSI_CLIENT_NAME_SIZE 20
+#define DSI_DISPLAY_MAX_LEN 512
/*
* DSI Validate Mode modifiers
* @DSI_VALIDATE_FLAG_ALLOW_ADJUST: Allow mode validation to also do fixup
@@ -37,6 +38,18 @@
#define DSI_VALIDATE_FLAG_ALLOW_ADJUST 0x1
/**
+ * enum dsi_display_selection_type - enumerates DSI display selection types
+ * @DSI_PRIMARY: primary DSI display selected from module parameter
+ * @DSI_SECONDARY: Secondary DSI display selected from module parameter
+ * @MAX_DSI_ACTIVE_DISPLAY: Maximum acive displays that can be selected
+ */
+enum dsi_display_selection_type {
+ DSI_PRIMARY = 0,
+ DSI_SECONDARY,
+ MAX_DSI_ACTIVE_DISPLAY,
+};
+
+/**
* enum dsi_display_type - enumerates DSI display types
* @DSI_DISPLAY_SINGLE: A panel connected on a single DSI interface.
* @DSI_DISPLAY_EXT_BRIDGE: A bridge is connected between panel and DSI host.
@@ -78,6 +91,21 @@
bool phy_enabled;
};
+/**
+ * struct dsi_display_boot_param - defines DSI boot display selection
+ * @name:Name of DSI display selected as a boot param.
+ * @boot_disp_en:bool to indicate dtsi availability of display node
+ * @is_primary:bool to indicate whether current display is primary display
+ * @length:length of DSI display.
+ */
+struct dsi_display_boot_param {
+ char name[DSI_DISPLAY_MAX_LEN];
+ bool boot_disp_en;
+ bool is_primary;
+ int length;
+ struct device_node *node;
+
+};
/**
* struct dsi_display_clk_info - dsi display clock source information
@@ -193,8 +221,16 @@
u32 max_display_count);
/**
+ * dsi_display_get_boot_display()- get DSI boot display name
+ * @index: index of display selection
+ *
+ * Return: returns the display node pointer
+ */
+struct device_node *dsi_display_get_boot_display(int index);
+
+/**
* dsi_display_get_display_by_name()- finds display by name
- * @index: name of the display.
+ * @name: name of the display.
*
* Return: handle to the display or error code.
*/