diff mbox

[v3,4/7] drm/tegra: Implement panel support

Message ID 1384171235-2498-5-git-send-email-treding@nvidia.com (mailing list archive)
State New, archived
Headers show

Commit Message

Thierry Reding Nov. 11, 2013, noon UTC
Use the DRM panel framework to attach a panel to an output. If the panel
attached to a connector supports supports the backlight brightness
accessors, a property will be available to allow the brightness to be
modified from userspace.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
Changes in v3:
- allow panels to be hotplugged
- add brightness property

 .../bindings/gpu/nvidia,tegra20-host1x.txt         |  2 +
 drivers/gpu/drm/tegra/Kconfig                      |  1 +
 drivers/gpu/drm/tegra/drm.h                        |  3 +
 drivers/gpu/drm/tegra/output.c                     | 90 +++++++++++++++++++++-
 4 files changed, 92 insertions(+), 4 deletions(-)
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/gpu/nvidia,tegra20-host1x.txt b/Documentation/devicetree/bindings/gpu/nvidia,tegra20-host1x.txt
index b4fa934ae3a2..24f026e20154 100644
--- a/Documentation/devicetree/bindings/gpu/nvidia,tegra20-host1x.txt
+++ b/Documentation/devicetree/bindings/gpu/nvidia,tegra20-host1x.txt
@@ -67,6 +67,7 @@  of the following host1x client modules:
   - nvidia,ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
   - nvidia,hpd-gpio: specifies a GPIO used for hotplug detection
   - nvidia,edid: supplies a binary EDID blob
+  - nvidia,panel: phandle of a display panel
 
 - hdmi: High Definition Multimedia Interface
 
@@ -81,6 +82,7 @@  of the following host1x client modules:
   - nvidia,ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
   - nvidia,hpd-gpio: specifies a GPIO used for hotplug detection
   - nvidia,edid: supplies a binary EDID blob
+  - nvidia,panel: phandle of a display panel
 
 - tvo: TV encoder output
 
diff --git a/drivers/gpu/drm/tegra/Kconfig b/drivers/gpu/drm/tegra/Kconfig
index 8961ba6a34b8..da34dd1b9ddb 100644
--- a/drivers/gpu/drm/tegra/Kconfig
+++ b/drivers/gpu/drm/tegra/Kconfig
@@ -5,6 +5,7 @@  config DRM_TEGRA
 	select TEGRA_HOST1X
 	select DRM_KMS_HELPER
 	select DRM_KMS_FB_HELPER
+	select DRM_PANEL
 	select FB_SYS_FILLRECT
 	select FB_SYS_COPYAREA
 	select FB_SYS_IMAGEBLIT
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index 7da0b923131f..5aa7f2065c02 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -183,6 +183,7 @@  struct tegra_output {
 	const struct tegra_output_ops *ops;
 	enum tegra_output_type type;
 
+	struct drm_panel *panel;
 	struct i2c_adapter *ddc;
 	const struct edid *edid;
 	unsigned int hpd_irq;
@@ -190,6 +191,8 @@  struct tegra_output {
 
 	struct drm_encoder encoder;
 	struct drm_connector connector;
+
+	struct drm_property *brightness;
 };
 
 static inline struct tegra_output *encoder_to_output(struct drm_encoder *e)
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index 2cb0065e0578..c5e0a2a97bc8 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -9,6 +9,7 @@ 
 
 #include <linux/of_gpio.h>
 
+#include <drm/drm_panel.h>
 #include "drm.h"
 
 static int tegra_connector_get_modes(struct drm_connector *connector)
@@ -17,6 +18,9 @@  static int tegra_connector_get_modes(struct drm_connector *connector)
 	struct edid *edid = NULL;
 	int err = 0;
 
+	if (output->panel)
+		return output->panel->funcs->get_modes(output->panel);
+
 	if (output->edid)
 		edid = kmemdup(output->edid, sizeof(*edid), GFP_KERNEL);
 	else if (output->ddc)
@@ -60,6 +64,39 @@  static const struct drm_connector_helper_funcs connector_helper_funcs = {
 	.best_encoder = tegra_connector_best_encoder,
 };
 
+static void tegra_output_add_panel_properties(struct tegra_output *output)
+{
+	struct drm_device *drm = output->connector.dev;
+	uint64_t min, max, value;
+	int err;
+
+	if (output->brightness)
+		return;
+
+	err = drm_panel_get_brightness_range(output->panel, &min, &max);
+	if (err < 0)
+		return;
+
+	err = drm_panel_get_brightness(output->panel, &value);
+	if (err < 0)
+		return;
+
+	output->brightness = drm_property_create_range(drm, 0, "brightness",
+						       min, max);
+	drm_object_attach_property(&output->connector.base, output->brightness,
+				   value);
+}
+
+static void tegra_output_remove_panel_properties(struct tegra_output *output)
+{
+	struct drm_device *drm = output->connector.dev;
+
+	if (output->brightness) {
+		drm_property_destroy(drm, output->brightness);
+		output->brightness = NULL;
+	}
+}
+
 static enum drm_connector_status
 tegra_connector_detect(struct drm_connector *connector, bool force)
 {
@@ -72,6 +109,14 @@  tegra_connector_detect(struct drm_connector *connector, bool force)
 		else
 			status = connector_status_connected;
 	} else {
+		if (!output->panel) {
+			tegra_output_remove_panel_properties(output);
+			status = connector_status_disconnected;
+		} else {
+			tegra_output_add_panel_properties(output);
+			status = connector_status_connected;
+		}
+
 		if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
 			status = connector_status_connected;
 	}
@@ -79,6 +124,24 @@  tegra_connector_detect(struct drm_connector *connector, bool force)
 	return status;
 }
 
+static int tegra_connector_set_property(struct drm_connector *connector,
+					struct drm_property *property,
+					uint64_t value)
+{
+	struct tegra_output *output = connector_to_output(connector);
+	int err;
+
+	if (output->panel) {
+		if (property == output->brightness) {
+			err = drm_panel_set_brightness(output->panel, value);
+			if (err < 0)
+				return err;
+		}
+	}
+
+	return 0;
+}
+
 static void drm_connector_clear(struct drm_connector *connector)
 {
 	memset(connector, 0, sizeof(*connector));
@@ -95,6 +158,7 @@  static const struct drm_connector_funcs connector_funcs = {
 	.dpms = drm_helper_connector_dpms,
 	.detect = tegra_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
+	.set_property = tegra_connector_set_property,
 	.destroy = tegra_connector_destroy,
 };
 
@@ -115,6 +179,15 @@  static const struct drm_encoder_funcs encoder_funcs = {
 
 static void tegra_encoder_dpms(struct drm_encoder *encoder, int mode)
 {
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct drm_panel *panel = output->panel;
+
+	if (panel && panel->funcs) {
+		if (mode != DRM_MODE_DPMS_ON)
+			drm_panel_disable(panel);
+		else
+			drm_panel_enable(panel);
+	}
 }
 
 static bool tegra_encoder_mode_fixup(struct drm_encoder *encoder,
@@ -163,14 +236,23 @@  static irqreturn_t hpd_irq(int irq, void *data)
 
 int tegra_output_probe(struct tegra_output *output)
 {
+	struct device_node *ddc, *panel;
 	enum of_gpio_flags flags;
-	struct device_node *ddc;
 	size_t size;
 	int err;
 
 	if (!output->of_node)
 		output->of_node = output->dev->of_node;
 
+	panel = of_parse_phandle(output->of_node, "nvidia,panel", 0);
+	if (panel) {
+		output->panel = of_drm_find_panel(panel);
+		if (!output->panel)
+			return -EPROBE_DEFER;
+
+		of_node_put(panel);
+	}
+
 	output->edid = of_get_property(output->of_node, "nvidia,edid", &size);
 
 	ddc = of_parse_phandle(output->of_node, "nvidia,ddc-i2c-bus", 0);
@@ -185,9 +267,6 @@  int tegra_output_probe(struct tegra_output *output)
 		of_node_put(ddc);
 	}
 
-	if (!output->edid && !output->ddc)
-		return -ENODEV;
-
 	output->hpd_gpio = of_get_named_gpio_flags(output->of_node,
 						   "nvidia,hpd-gpio", 0,
 						   &flags);
@@ -267,6 +346,9 @@  int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
 	drm_connector_helper_add(&output->connector, &connector_helper_funcs);
 	output->connector.dpms = DRM_MODE_DPMS_OFF;
 
+	if (output->panel)
+		drm_panel_attach(output->panel, &output->connector);
+
 	drm_encoder_init(drm, &output->encoder, &encoder_funcs, encoder);
 	drm_encoder_helper_add(&output->encoder, &encoder_helper_funcs);