diff mbox

[RFC,3/5] drm/panel: simple-panel: Add panel picker support.

Message ID 1439567419-13721-1-git-send-email-srinivas.kandagatla@linaro.org (mailing list archive)
State New, archived
Headers show

Commit Message

Srinivas Kandagatla Aug. 14, 2015, 3:50 p.m. UTC
This patch adds panel picker support to simple-panel.

The idea of panel picker is to select the correct panel timings if it
supports probing edid via DDC bus, edid contains manufacture ID
and Manufacturer product code, so it can match against the panel_picker
entries to get the correct panel timings.

From DT point of view the panel picker uses generic compatible string
"panel-simple", keeping the panel specific compatible strings still
supported.

Panels can be static entry in the DT, but practically development boards
like IFC6410 where developers can connect any LVDS panel which makes it
difficult to maintian the dt support for those panels in dts file.
With this dynamic probing via panel picker makes it easy to support such
use-cases.
This patch also adds panel presence detection based, if there is no
panel detected or panel picker could not find the panel then the driver
would mark the panel DT node as disabled so that the drm driver would
be able to take right decision based on that panel node status.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 drivers/gpu/drm/panel/panel-simple.c | 83 +++++++++++++++++++++++++++++++++++-
 1 file changed, 81 insertions(+), 2 deletions(-)
diff mbox

Patch

diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index f94201b..6c503b2 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -32,6 +32,7 @@ 
 #include <drm/drm_crtc.h>
 #include <drm/drm_mipi_dsi.h>
 #include <drm/drm_panel.h>
+#include <drm/drm_edid.h>
 
 #include <video/display_timing.h>
 #include <video/videomode.h>
@@ -70,6 +71,18 @@  struct panel_desc {
 	u32 bus_format;
 };
 
+#define PANEL_PICKER_ENTRY(vend, pid, pdesc) \
+		.vendor = vend, \
+		.product_id = (pid), \
+		.data = (pdesc)
+
+/* Panel picker entry with vendor and product id */
+struct panel_picker_entry {
+	char vendor[4]; /* Vendor string */
+	int product_id; /* product id field */
+	const struct panel_desc *data;
+};
+
 struct panel_simple {
 	struct drm_panel base;
 	bool prepared;
@@ -84,6 +97,8 @@  struct panel_simple {
 	struct gpio_desc *enable_gpio;
 };
 
+static const struct panel_desc *panel_picker_find_panel(struct edid *edid);
+
 static inline struct panel_simple *to_panel_simple(struct drm_panel *panel)
 {
 	return container_of(panel, struct panel_simple, base);
@@ -276,11 +291,28 @@  static const struct drm_panel_funcs panel_simple_funcs = {
 	.get_timings = panel_simple_get_timings,
 };
 
+static void __init simple_panel_node_disable(struct device_node *node)
+{
+	struct property *prop;
+
+	prop = kzalloc(sizeof(*prop), GFP_KERNEL);
+	if (!prop)
+		return;
+
+	prop->name = "status";
+	prop->value = "disabled";
+	prop->length = strlen((char *)prop->value)+1;
+
+	of_update_property(node, prop);
+}
+
+
 static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
 {
 	struct device_node *backlight, *ddc;
 	struct panel_simple *panel;
 	int err;
+	struct edid *edid;
 
 	panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
 	if (!panel)
@@ -288,7 +320,6 @@  static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
 
 	panel->enabled = false;
 	panel->prepared = false;
-	panel->desc = desc;
 
 	panel->supply = devm_regulator_get(dev, "power");
 	if (IS_ERR(panel->supply))
@@ -316,7 +347,25 @@  static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
 		panel->ddc = of_find_i2c_adapter_by_node(ddc);
 		of_node_put(ddc);
 
-		if (!panel->ddc) {
+		if (panel->ddc) {
+			/* detect panel presence */
+			if (!drm_probe_ddc(panel->ddc)) {
+				err = -ENODEV;
+				goto nodev;
+			}
+
+			/* get panel from edid */
+			if (of_device_is_compatible(dev->of_node,
+						"panel-simple")) {
+				edid = drm_get_edid_early(panel->ddc);
+				if (edid) {
+					desc = panel_picker_find_panel(edid);
+				} else {
+					err = -ENODEV;
+					goto nodev;
+				}
+			}
+		} else {
 			err = -EPROBE_DEFER;
 			goto free_backlight;
 		}
@@ -325,6 +374,7 @@  static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
 	drm_panel_init(&panel->base);
 	panel->base.dev = dev;
 	panel->base.funcs = &panel_simple_funcs;
+	panel->desc = desc;
 
 	err = drm_panel_add(&panel->base);
 	if (err < 0)
@@ -334,6 +384,10 @@  static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
 
 	return 0;
 
+nodev:
+	/* mark the dt as disabled */
+	simple_panel_node_disable(dev->of_node);
+
 free_ddc:
 	if (panel->ddc)
 		put_device(&panel->ddc->dev);
@@ -1036,6 +1090,10 @@  static const struct panel_desc shelly_sca07010_bfn_lnn = {
 	.bus_format = MEDIA_BUS_FMT_RGB666_1X18,
 };
 
+static const struct panel_picker_entry panel_picker_list[] = {
+	{ PANEL_PICKER_ENTRY("AUO", 0x10dc, &auo_b101xtn01) },
+};
+
 static const struct of_device_id platform_of_match[] = {
 	{
 		.compatible = "ampire,am800480r3tmqwa1h",
@@ -1125,11 +1183,32 @@  static const struct of_device_id platform_of_match[] = {
 		.compatible = "shelly,sca07010-bfn-lnn",
 		.data = &shelly_sca07010_bfn_lnn,
 	}, {
+		/* Panel Picker Vendor ID and Product ID based Lookup */
+		.compatible = "panel-simple",
+	}, {
 		/* sentinel */
 	}
 };
 MODULE_DEVICE_TABLE(of, platform_of_match);
 
+static const struct panel_desc *panel_picker_find_panel(struct edid *edid)
+{
+	int i;
+	const struct panel_desc *desc = NULL;
+
+	for (i = 0; i < ARRAY_SIZE(panel_picker_list); i++) {
+		const struct panel_picker_entry *vp = &panel_picker_list[i];
+
+		if (edid_vendor(edid, (char *)vp->vendor) &&
+				(EDID_PRODUCT_ID(edid) == vp->product_id)) {
+			desc = vp->data;
+			break;
+		}
+	}
+
+	return desc;
+}
+
 static int panel_simple_platform_probe(struct platform_device *pdev)
 {
 	const struct of_device_id *id;