linux-tegra.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Thierry Reding <thierry.reding@gmail.com>
To: dri-devel@lists.freedesktop.org
Cc: linux-tegra@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH 17/36] drm/tegra: sor: Demidlayer
Date: Tue, 20 Jan 2015 11:48:36 +0100	[thread overview]
Message-ID: <1421750935-4023-18-git-send-email-thierry.reding@gmail.com> (raw)
In-Reply-To: <1421750935-4023-1-git-send-email-thierry.reding@gmail.com>

From: Thierry Reding <treding@nvidia.com>

Implement encoder and connector within the eDP driver itself using the
Tegra output helpers rather than using the Tegra output as midlayer. By
doing so one level of indirection is removed and output drivers become
more flexible while keeping the majority of the advantages provided by
the common output helpers.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/drm.h    |   5 -
 drivers/gpu/drm/tegra/output.c |  15 +-
 drivers/gpu/drm/tegra/sor.c    | 810 ++++++++++++++++++++++-------------------
 3 files changed, 436 insertions(+), 394 deletions(-)

diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index ae3daa436ee6..c74d5db47537 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -192,16 +192,11 @@ struct tegra_output_ops {
 	enum drm_connector_status (*detect)(struct tegra_output *output);
 };
 
-enum tegra_output_type {
-	TEGRA_OUTPUT_EDP,
-};
-
 struct tegra_output {
 	struct device_node *of_node;
 	struct device *dev;
 
 	const struct tegra_output_ops *ops;
-	enum tegra_output_type type;
 
 	struct drm_panel *panel;
 	struct i2c_adapter *ddc;
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index bc5393fd03c7..042b421416a8 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -273,19 +273,8 @@ int tegra_output_remove(struct tegra_output *output)
 
 int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
 {
-	int connector, encoder;
-
-	switch (output->type) {
-	case TEGRA_OUTPUT_EDP:
-		connector = DRM_MODE_CONNECTOR_eDP;
-		encoder = DRM_MODE_ENCODER_TMDS;
-		break;
-
-	default:
-		connector = DRM_MODE_CONNECTOR_Unknown;
-		encoder = DRM_MODE_ENCODER_NONE;
-		break;
-	}
+	int connector = DRM_MODE_CONNECTOR_Unknown;
+	int encoder = DRM_MODE_ENCODER_NONE;
 
 	drm_connector_init(drm, &output->connector, &connector_funcs,
 			   connector);
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index 1fe801ee8eb0..be1ad42c69be 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -8,6 +8,7 @@
 
 #include <linux/clk.h>
 #include <linux/debugfs.h>
+#include <linux/gpio.h>
 #include <linux/io.h>
 #include <linux/platform_device.h>
 #include <linux/reset.h>
@@ -15,6 +16,7 @@
 #include <soc/tegra/pmc.h>
 
 #include <drm/drm_dp_helper.h>
+#include <drm/drm_panel.h>
 
 #include "dc.h"
 #include "drm.h"
@@ -481,10 +483,342 @@ static int tegra_sor_calc_config(struct tegra_sor *sor,
 	return 0;
 }
 
-static int tegra_output_sor_enable(struct tegra_output *output)
+static int tegra_sor_detach(struct tegra_sor *sor)
+{
+	unsigned long value, timeout;
+
+	/* switch to safe mode */
+	value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
+	value &= ~SOR_SUPER_STATE_MODE_NORMAL;
+	tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
+	tegra_sor_super_update(sor);
+
+	timeout = jiffies + msecs_to_jiffies(250);
+
+	while (time_before(jiffies, timeout)) {
+		value = tegra_sor_readl(sor, SOR_PWR);
+		if (value & SOR_PWR_MODE_SAFE)
+			break;
+	}
+
+	if ((value & SOR_PWR_MODE_SAFE) == 0)
+		return -ETIMEDOUT;
+
+	/* go to sleep */
+	value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
+	value &= ~SOR_SUPER_STATE_HEAD_MODE_MASK;
+	tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
+	tegra_sor_super_update(sor);
+
+	/* detach */
+	value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
+	value &= ~SOR_SUPER_STATE_ATTACHED;
+	tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
+	tegra_sor_super_update(sor);
+
+	timeout = jiffies + msecs_to_jiffies(250);
+
+	while (time_before(jiffies, timeout)) {
+		value = tegra_sor_readl(sor, SOR_TEST);
+		if ((value & SOR_TEST_ATTACHED) == 0)
+			break;
+
+		usleep_range(25, 100);
+	}
+
+	if ((value & SOR_TEST_ATTACHED) != 0)
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+static int tegra_sor_power_down(struct tegra_sor *sor)
+{
+	unsigned long value, timeout;
+	int err;
+
+	value = tegra_sor_readl(sor, SOR_PWR);
+	value &= ~SOR_PWR_NORMAL_STATE_PU;
+	value |= SOR_PWR_TRIGGER;
+	tegra_sor_writel(sor, value, SOR_PWR);
+
+	timeout = jiffies + msecs_to_jiffies(250);
+
+	while (time_before(jiffies, timeout)) {
+		value = tegra_sor_readl(sor, SOR_PWR);
+		if ((value & SOR_PWR_TRIGGER) == 0)
+			return 0;
+
+		usleep_range(25, 100);
+	}
+
+	if ((value & SOR_PWR_TRIGGER) != 0)
+		return -ETIMEDOUT;
+
+	err = clk_set_parent(sor->clk, sor->clk_safe);
+	if (err < 0)
+		dev_err(sor->dev, "failed to set safe parent clock: %d\n", err);
+
+	value = tegra_sor_readl(sor, SOR_DP_PADCTL_0);
+	value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 |
+		   SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2);
+	tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
+
+	/* stop lane sequencer */
+	value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_UP |
+		SOR_LANE_SEQ_CTL_POWER_STATE_DOWN;
+	tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL);
+
+	timeout = jiffies + msecs_to_jiffies(250);
+
+	while (time_before(jiffies, timeout)) {
+		value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL);
+		if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0)
+			break;
+
+		usleep_range(25, 100);
+	}
+
+	if ((value & SOR_LANE_SEQ_CTL_TRIGGER) != 0)
+		return -ETIMEDOUT;
+
+	value = tegra_sor_readl(sor, SOR_PLL_2);
+	value |= SOR_PLL_2_PORT_POWERDOWN;
+	tegra_sor_writel(sor, value, SOR_PLL_2);
+
+	usleep_range(20, 100);
+
+	value = tegra_sor_readl(sor, SOR_PLL_0);
+	value |= SOR_PLL_0_POWER_OFF;
+	value |= SOR_PLL_0_VCOPD;
+	tegra_sor_writel(sor, value, SOR_PLL_0);
+
+	value = tegra_sor_readl(sor, SOR_PLL_2);
+	value |= SOR_PLL_2_SEQ_PLLCAPPD;
+	value |= SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE;
+	tegra_sor_writel(sor, value, SOR_PLL_2);
+
+	usleep_range(20, 100);
+
+	return 0;
+}
+
+static int tegra_sor_crc_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+
+	return 0;
+}
+
+static int tegra_sor_crc_release(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static int tegra_sor_crc_wait(struct tegra_sor *sor, unsigned long timeout)
+{
+	u32 value;
+
+	timeout = jiffies + msecs_to_jiffies(timeout);
+
+	while (time_before(jiffies, timeout)) {
+		value = tegra_sor_readl(sor, SOR_CRC_A);
+		if (value & SOR_CRC_A_VALID)
+			return 0;
+
+		usleep_range(100, 200);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static ssize_t tegra_sor_crc_read(struct file *file, char __user *buffer,
+				  size_t size, loff_t *ppos)
+{
+	struct tegra_sor *sor = file->private_data;
+	ssize_t num, err;
+	char buf[10];
+	u32 value;
+
+	mutex_lock(&sor->lock);
+
+	if (!sor->enabled) {
+		err = -EAGAIN;
+		goto unlock;
+	}
+
+	value = tegra_sor_readl(sor, SOR_STATE_1);
+	value &= ~SOR_STATE_ASY_CRC_MODE_MASK;
+	tegra_sor_writel(sor, value, SOR_STATE_1);
+
+	value = tegra_sor_readl(sor, SOR_CRC_CNTRL);
+	value |= SOR_CRC_CNTRL_ENABLE;
+	tegra_sor_writel(sor, value, SOR_CRC_CNTRL);
+
+	value = tegra_sor_readl(sor, SOR_TEST);
+	value &= ~SOR_TEST_CRC_POST_SERIALIZE;
+	tegra_sor_writel(sor, value, SOR_TEST);
+
+	err = tegra_sor_crc_wait(sor, 100);
+	if (err < 0)
+		goto unlock;
+
+	tegra_sor_writel(sor, SOR_CRC_A_RESET, SOR_CRC_A);
+	value = tegra_sor_readl(sor, SOR_CRC_B);
+
+	num = scnprintf(buf, sizeof(buf), "%08x\n", value);
+
+	err = simple_read_from_buffer(buffer, size, ppos, buf, num);
+
+unlock:
+	mutex_unlock(&sor->lock);
+	return err;
+}
+
+static const struct file_operations tegra_sor_crc_fops = {
+	.owner = THIS_MODULE,
+	.open = tegra_sor_crc_open,
+	.read = tegra_sor_crc_read,
+	.release = tegra_sor_crc_release,
+};
+
+static int tegra_sor_debugfs_init(struct tegra_sor *sor,
+				  struct drm_minor *minor)
 {
-	struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
-	struct drm_display_mode *mode = &dc->base.mode;
+	struct dentry *entry;
+	int err = 0;
+
+	sor->debugfs = debugfs_create_dir("sor", minor->debugfs_root);
+	if (!sor->debugfs)
+		return -ENOMEM;
+
+	entry = debugfs_create_file("crc", 0644, sor->debugfs, sor,
+				    &tegra_sor_crc_fops);
+	if (!entry) {
+		dev_err(sor->dev,
+			"cannot create /sys/kernel/debug/dri/%s/sor/crc\n",
+			minor->debugfs_root->d_name.name);
+		err = -ENOMEM;
+		goto remove;
+	}
+
+	return err;
+
+remove:
+	debugfs_remove(sor->debugfs);
+	sor->debugfs = NULL;
+	return err;
+}
+
+static int tegra_sor_debugfs_exit(struct tegra_sor *sor)
+{
+	debugfs_remove_recursive(sor->debugfs);
+	sor->debugfs = NULL;
+
+	return 0;
+}
+
+static void tegra_sor_connector_dpms(struct drm_connector *connector, int mode)
+{
+}
+
+static enum drm_connector_status
+tegra_sor_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct tegra_output *output = connector_to_output(connector);
+	struct tegra_sor *sor = to_sor(output);
+
+	if (sor->dpaux)
+		return tegra_dpaux_detect(sor->dpaux);
+
+	return connector_status_unknown;
+}
+
+static const struct drm_connector_funcs tegra_sor_connector_funcs = {
+	.dpms = tegra_sor_connector_dpms,
+	.detect = tegra_sor_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = tegra_output_connector_destroy,
+};
+
+static int tegra_sor_connector_get_modes(struct drm_connector *connector)
+{
+	struct tegra_output *output = connector_to_output(connector);
+	struct tegra_sor *sor = to_sor(output);
+	int err;
+
+	if (sor->dpaux)
+		tegra_dpaux_enable(sor->dpaux);
+
+	err = tegra_output_connector_get_modes(connector);
+
+	if (sor->dpaux)
+		tegra_dpaux_disable(sor->dpaux);
+
+	return err;
+}
+
+static enum drm_mode_status
+tegra_sor_connector_mode_valid(struct drm_connector *connector,
+			       struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
+
+static const struct drm_connector_helper_funcs tegra_sor_connector_helper_funcs = {
+	.get_modes = tegra_sor_connector_get_modes,
+	.mode_valid = tegra_sor_connector_mode_valid,
+	.best_encoder = tegra_output_connector_best_encoder,
+};
+
+static const struct drm_encoder_funcs tegra_sor_encoder_funcs = {
+	.destroy = tegra_output_encoder_destroy,
+};
+
+static void tegra_sor_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+}
+
+static bool tegra_sor_encoder_mode_fixup(struct drm_encoder *encoder,
+					 const struct drm_display_mode *mode,
+					 struct drm_display_mode *adjusted)
+{
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
+	unsigned long pclk = mode->clock * 1000;
+	struct tegra_sor *sor = to_sor(output);
+	int err;
+
+	err = tegra_dc_setup_clock(dc, sor->clk_parent, pclk, 0);
+	if (err < 0) {
+		dev_err(output->dev, "failed to setup DC clock: %d\n", err);
+		return false;
+	}
+
+	err = clk_set_rate(sor->clk_parent, pclk);
+	if (err < 0) {
+		dev_err(output->dev, "failed to set clock rate to %lu Hz\n",
+			pclk);
+		return false;
+	}
+
+	return true;
+}
+
+static void tegra_sor_encoder_prepare(struct drm_encoder *encoder)
+{
+}
+
+static void tegra_sor_encoder_commit(struct drm_encoder *encoder)
+{
+}
+
+static void tegra_sor_encoder_mode_set(struct drm_encoder *encoder,
+				       struct drm_display_mode *mode,
+				       struct drm_display_mode *adjusted)
+{
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
 	unsigned int vbe, vse, hbe, hse, vbs, hbs, i;
 	struct tegra_sor *sor = to_sor(output);
 	struct tegra_sor_config config;
@@ -504,6 +838,9 @@ static int tegra_output_sor_enable(struct tegra_output *output)
 
 	reset_control_deassert(sor->rst);
 
+	if (output->panel)
+		drm_panel_prepare(output->panel);
+
 	/* FIXME: properly convert to struct drm_dp_aux */
 	aux = (struct drm_dp_aux *)sor->dpaux;
 
@@ -873,175 +1210,61 @@ static int tegra_output_sor_enable(struct tegra_output *output)
 	value = ((vbs & 0x7fff) << 16) | (hbs & 0x7fff);
 	tegra_sor_writel(sor, value, SOR_HEAD_STATE_4(0));
 
-	/* CSTM (LVDS, link A/B, upper) */
-	value = SOR_CSTM_LVDS | SOR_CSTM_LINK_ACT_A | SOR_CSTM_LINK_ACT_B |
-		SOR_CSTM_UPPER;
-	tegra_sor_writel(sor, value, SOR_CSTM);
-
-	/* PWM setup */
-	err = tegra_sor_setup_pwm(sor, 250);
-	if (err < 0) {
-		dev_err(sor->dev, "failed to setup PWM: %d\n", err);
-		goto unlock;
-	}
-
-	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
-	value |= SOR_ENABLE;
-	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
-
-	tegra_sor_update(sor);
-
-	err = tegra_sor_attach(sor);
-	if (err < 0) {
-		dev_err(sor->dev, "failed to attach SOR: %d\n", err);
-		goto unlock;
-	}
-
-	err = tegra_sor_wakeup(sor);
-	if (err < 0) {
-		dev_err(sor->dev, "failed to enable DC: %d\n", err);
-		goto unlock;
-	}
-
-	sor->enabled = true;
-
-unlock:
-	mutex_unlock(&sor->lock);
-	return err;
-}
-
-static int tegra_sor_detach(struct tegra_sor *sor)
-{
-	unsigned long value, timeout;
-
-	/* switch to safe mode */
-	value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
-	value &= ~SOR_SUPER_STATE_MODE_NORMAL;
-	tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
-	tegra_sor_super_update(sor);
-
-	timeout = jiffies + msecs_to_jiffies(250);
-
-	while (time_before(jiffies, timeout)) {
-		value = tegra_sor_readl(sor, SOR_PWR);
-		if (value & SOR_PWR_MODE_SAFE)
-			break;
-	}
-
-	if ((value & SOR_PWR_MODE_SAFE) == 0)
-		return -ETIMEDOUT;
-
-	/* go to sleep */
-	value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
-	value &= ~SOR_SUPER_STATE_HEAD_MODE_MASK;
-	tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
-	tegra_sor_super_update(sor);
-
-	/* detach */
-	value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
-	value &= ~SOR_SUPER_STATE_ATTACHED;
-	tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
-	tegra_sor_super_update(sor);
-
-	timeout = jiffies + msecs_to_jiffies(250);
-
-	while (time_before(jiffies, timeout)) {
-		value = tegra_sor_readl(sor, SOR_TEST);
-		if ((value & SOR_TEST_ATTACHED) == 0)
-			break;
-
-		usleep_range(25, 100);
-	}
-
-	if ((value & SOR_TEST_ATTACHED) != 0)
-		return -ETIMEDOUT;
-
-	return 0;
-}
-
-static int tegra_sor_power_down(struct tegra_sor *sor)
-{
-	unsigned long value, timeout;
-	int err;
-
-	value = tegra_sor_readl(sor, SOR_PWR);
-	value &= ~SOR_PWR_NORMAL_STATE_PU;
-	value |= SOR_PWR_TRIGGER;
-	tegra_sor_writel(sor, value, SOR_PWR);
-
-	timeout = jiffies + msecs_to_jiffies(250);
-
-	while (time_before(jiffies, timeout)) {
-		value = tegra_sor_readl(sor, SOR_PWR);
-		if ((value & SOR_PWR_TRIGGER) == 0)
-			return 0;
-
-		usleep_range(25, 100);
-	}
-
-	if ((value & SOR_PWR_TRIGGER) != 0)
-		return -ETIMEDOUT;
-
-	err = clk_set_parent(sor->clk, sor->clk_safe);
-	if (err < 0)
-		dev_err(sor->dev, "failed to set safe parent clock: %d\n", err);
-
-	value = tegra_sor_readl(sor, SOR_DP_PADCTL_0);
-	value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 |
-		   SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2);
-	tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
-
-	/* stop lane sequencer */
-	value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_UP |
-		SOR_LANE_SEQ_CTL_POWER_STATE_DOWN;
-	tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL);
-
-	timeout = jiffies + msecs_to_jiffies(250);
-
-	while (time_before(jiffies, timeout)) {
-		value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL);
-		if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0)
-			break;
-
-		usleep_range(25, 100);
+	/* CSTM (LVDS, link A/B, upper) */
+	value = SOR_CSTM_LVDS | SOR_CSTM_LINK_ACT_A | SOR_CSTM_LINK_ACT_B |
+		SOR_CSTM_UPPER;
+	tegra_sor_writel(sor, value, SOR_CSTM);
+
+	/* PWM setup */
+	err = tegra_sor_setup_pwm(sor, 250);
+	if (err < 0) {
+		dev_err(sor->dev, "failed to setup PWM: %d\n", err);
+		goto unlock;
 	}
 
-	if ((value & SOR_LANE_SEQ_CTL_TRIGGER) != 0)
-		return -ETIMEDOUT;
+	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
+	value |= SOR_ENABLE;
+	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
 
-	value = tegra_sor_readl(sor, SOR_PLL_2);
-	value |= SOR_PLL_2_PORT_POWERDOWN;
-	tegra_sor_writel(sor, value, SOR_PLL_2);
+	tegra_sor_update(sor);
 
-	usleep_range(20, 100);
+	err = tegra_sor_attach(sor);
+	if (err < 0) {
+		dev_err(sor->dev, "failed to attach SOR: %d\n", err);
+		goto unlock;
+	}
 
-	value = tegra_sor_readl(sor, SOR_PLL_0);
-	value |= SOR_PLL_0_POWER_OFF;
-	value |= SOR_PLL_0_VCOPD;
-	tegra_sor_writel(sor, value, SOR_PLL_0);
+	err = tegra_sor_wakeup(sor);
+	if (err < 0) {
+		dev_err(sor->dev, "failed to enable DC: %d\n", err);
+		goto unlock;
+	}
 
-	value = tegra_sor_readl(sor, SOR_PLL_2);
-	value |= SOR_PLL_2_SEQ_PLLCAPPD;
-	value |= SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE;
-	tegra_sor_writel(sor, value, SOR_PLL_2);
+	if (output->panel)
+		drm_panel_enable(output->panel);
 
-	usleep_range(20, 100);
+	sor->enabled = true;
 
-	return 0;
+unlock:
+	mutex_unlock(&sor->lock);
 }
 
-static int tegra_output_sor_disable(struct tegra_output *output)
+static void tegra_sor_encoder_disable(struct drm_encoder *encoder)
 {
-	struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
 	struct tegra_sor *sor = to_sor(output);
-	unsigned long value;
-	int err = 0;
+	u32 value;
+	int err;
 
 	mutex_lock(&sor->lock);
 
 	if (!sor->enabled)
 		goto unlock;
 
+	if (output->panel)
+		drm_panel_disable(output->panel);
+
 	err = tegra_sor_detach(sor);
 	if (err < 0) {
 		dev_err(sor->dev, "failed to detach SOR: %d\n", err);
@@ -1056,21 +1279,6 @@ static int tegra_output_sor_disable(struct tegra_output *output)
 	 * sure it's only executed when the output is attached to one.
 	 */
 	if (dc) {
-		/*
-		 * XXX: We can't do this here because it causes the SOR to go
-		 * into an erroneous state and the output will look scrambled
-		 * the next time it is enabled. Presumably this is because we
-		 * should be doing this only on the next VBLANK. A possible
-		 * solution would be to queue a "power-off" event to trigger
-		 * this code to be run during the next VBLANK.
-		 */
-		/*
-		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
-		value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
-			   PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
-		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
-		*/
-
 		value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
 		value &= ~SOR_ENABLE;
 		tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
@@ -1098,187 +1306,27 @@ static int tegra_output_sor_disable(struct tegra_output *output)
 		goto unlock;
 	}
 
-	reset_control_assert(sor->rst);
+	if (output->panel)
+		drm_panel_unprepare(output->panel);
+
 	clk_disable_unprepare(sor->clk);
+	reset_control_assert(sor->rst);
 
 	sor->enabled = false;
 
 unlock:
 	mutex_unlock(&sor->lock);
-	return err;
-}
-
-static int tegra_output_sor_setup_clock(struct tegra_output *output,
-					struct clk *clk, unsigned long pclk,
-					unsigned int *div)
-{
-	struct tegra_sor *sor = to_sor(output);
-	int err;
-
-	err = clk_set_parent(clk, sor->clk_parent);
-	if (err < 0) {
-		dev_err(sor->dev, "failed to set parent clock: %d\n", err);
-		return err;
-	}
-
-	err = clk_set_rate(sor->clk_parent, pclk);
-	if (err < 0) {
-		dev_err(sor->dev, "failed to set clock rate to %lu Hz\n", pclk);
-		return err;
-	}
-
-	*div = 0;
-
-	return 0;
-}
-
-static int tegra_output_sor_check_mode(struct tegra_output *output,
-				       struct drm_display_mode *mode,
-				       enum drm_mode_status *status)
-{
-	/*
-	 * FIXME: For now, always assume that the mode is okay.
-	 */
-
-	*status = MODE_OK;
-
-	return 0;
-}
-
-static enum drm_connector_status
-tegra_output_sor_detect(struct tegra_output *output)
-{
-	struct tegra_sor *sor = to_sor(output);
-
-	if (sor->dpaux)
-		return tegra_dpaux_detect(sor->dpaux);
-
-	return connector_status_unknown;
-}
-
-static const struct tegra_output_ops sor_ops = {
-	.enable = tegra_output_sor_enable,
-	.disable = tegra_output_sor_disable,
-	.setup_clock = tegra_output_sor_setup_clock,
-	.check_mode = tegra_output_sor_check_mode,
-	.detect = tegra_output_sor_detect,
-};
-
-static int tegra_sor_crc_open(struct inode *inode, struct file *file)
-{
-	file->private_data = inode->i_private;
-
-	return 0;
-}
-
-static int tegra_sor_crc_release(struct inode *inode, struct file *file)
-{
-	return 0;
-}
-
-static int tegra_sor_crc_wait(struct tegra_sor *sor, unsigned long timeout)
-{
-	u32 value;
-
-	timeout = jiffies + msecs_to_jiffies(timeout);
-
-	while (time_before(jiffies, timeout)) {
-		value = tegra_sor_readl(sor, SOR_CRC_A);
-		if (value & SOR_CRC_A_VALID)
-			return 0;
-
-		usleep_range(100, 200);
-	}
-
-	return -ETIMEDOUT;
-}
-
-static ssize_t tegra_sor_crc_read(struct file *file, char __user *buffer,
-				  size_t size, loff_t *ppos)
-{
-	struct tegra_sor *sor = file->private_data;
-	ssize_t num, err;
-	char buf[10];
-	u32 value;
-
-	mutex_lock(&sor->lock);
-
-	if (!sor->enabled) {
-		err = -EAGAIN;
-		goto unlock;
-	}
-
-	value = tegra_sor_readl(sor, SOR_STATE_1);
-	value &= ~SOR_STATE_ASY_CRC_MODE_MASK;
-	tegra_sor_writel(sor, value, SOR_STATE_1);
-
-	value = tegra_sor_readl(sor, SOR_CRC_CNTRL);
-	value |= SOR_CRC_CNTRL_ENABLE;
-	tegra_sor_writel(sor, value, SOR_CRC_CNTRL);
-
-	value = tegra_sor_readl(sor, SOR_TEST);
-	value &= ~SOR_TEST_CRC_POST_SERIALIZE;
-	tegra_sor_writel(sor, value, SOR_TEST);
-
-	err = tegra_sor_crc_wait(sor, 100);
-	if (err < 0)
-		goto unlock;
-
-	tegra_sor_writel(sor, SOR_CRC_A_RESET, SOR_CRC_A);
-	value = tegra_sor_readl(sor, SOR_CRC_B);
-
-	num = scnprintf(buf, sizeof(buf), "%08x\n", value);
-
-	err = simple_read_from_buffer(buffer, size, ppos, buf, num);
-
-unlock:
-	mutex_unlock(&sor->lock);
-	return err;
 }
 
-static const struct file_operations tegra_sor_crc_fops = {
-	.owner = THIS_MODULE,
-	.open = tegra_sor_crc_open,
-	.read = tegra_sor_crc_read,
-	.release = tegra_sor_crc_release,
+static const struct drm_encoder_helper_funcs tegra_sor_encoder_helper_funcs = {
+	.dpms = tegra_sor_encoder_dpms,
+	.mode_fixup = tegra_sor_encoder_mode_fixup,
+	.prepare = tegra_sor_encoder_prepare,
+	.commit = tegra_sor_encoder_commit,
+	.mode_set = tegra_sor_encoder_mode_set,
+	.disable = tegra_sor_encoder_disable,
 };
 
-static int tegra_sor_debugfs_init(struct tegra_sor *sor,
-				  struct drm_minor *minor)
-{
-	struct dentry *entry;
-	int err = 0;
-
-	sor->debugfs = debugfs_create_dir("sor", minor->debugfs_root);
-	if (!sor->debugfs)
-		return -ENOMEM;
-
-	entry = debugfs_create_file("crc", 0644, sor->debugfs, sor,
-				    &tegra_sor_crc_fops);
-	if (!entry) {
-		dev_err(sor->dev,
-			"cannot create /sys/kernel/debug/dri/%s/sor/crc\n",
-			minor->debugfs_root->d_name.name);
-		err = -ENOMEM;
-		goto remove;
-	}
-
-	return err;
-
-remove:
-	debugfs_remove(sor->debugfs);
-	sor->debugfs = NULL;
-	return err;
-}
-
-static int tegra_sor_debugfs_exit(struct tegra_sor *sor)
-{
-	debugfs_remove_recursive(sor->debugfs);
-	sor->debugfs = NULL;
-
-	return 0;
-}
-
 static int tegra_sor_init(struct host1x_client *client)
 {
 	struct drm_device *drm = dev_get_drvdata(client->parent);
@@ -1288,16 +1336,31 @@ static int tegra_sor_init(struct host1x_client *client)
 	if (!sor->dpaux)
 		return -ENODEV;
 
-	sor->output.type = TEGRA_OUTPUT_EDP;
-
 	sor->output.dev = sor->dev;
-	sor->output.ops = &sor_ops;
 
-	err = tegra_output_init(drm, &sor->output);
-	if (err < 0) {
-		dev_err(sor->dev, "output setup failed: %d\n", err);
-		return err;
-	}
+	drm_connector_init(drm, &sor->output.connector,
+			   &tegra_sor_connector_funcs,
+			   DRM_MODE_CONNECTOR_eDP);
+	drm_connector_helper_add(&sor->output.connector,
+				 &tegra_sor_connector_helper_funcs);
+	sor->output.connector.dpms = DRM_MODE_DPMS_OFF;
+
+	if (sor->output.panel)
+		drm_panel_attach(sor->output.panel, &sor->output.connector);
+
+	drm_encoder_init(drm, &sor->output.encoder, &tegra_sor_encoder_funcs,
+			 DRM_MODE_ENCODER_TMDS);
+	drm_encoder_helper_add(&sor->output.encoder,
+			       &tegra_sor_encoder_helper_funcs);
+
+	drm_mode_connector_attach_encoder(&sor->output.connector,
+					  &sor->output.encoder);
+	drm_connector_register(&sor->output.connector);
+
+	sor->output.encoder.possible_crtcs = 0x3;
+
+	if (gpio_is_valid(sor->output.hpd_gpio))
+		enable_irq(sor->output.hpd_irq);
 
 	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
 		err = tegra_sor_debugfs_init(sor, drm->primary);
@@ -1313,6 +1376,20 @@ static int tegra_sor_init(struct host1x_client *client)
 		}
 	}
 
+	err = clk_prepare_enable(sor->clk);
+	if (err < 0) {
+		dev_err(sor->dev, "failed to enable clock: %d\n", err);
+		return err;
+	}
+
+	err = clk_prepare_enable(sor->clk_safe);
+	if (err < 0)
+		return err;
+
+	err = clk_prepare_enable(sor->clk_dp);
+	if (err < 0)
+		return err;
+
 	return 0;
 }
 
@@ -1321,12 +1398,6 @@ static int tegra_sor_exit(struct host1x_client *client)
 	struct tegra_sor *sor = host1x_client_to_sor(client);
 	int err;
 
-	err = tegra_output_disable(&sor->output);
-	if (err < 0) {
-		dev_err(sor->dev, "output failed to disable: %d\n", err);
-		return err;
-	}
-
 	if (sor->dpaux) {
 		err = tegra_dpaux_detach(sor->dpaux);
 		if (err < 0) {
@@ -1335,18 +1406,16 @@ static int tegra_sor_exit(struct host1x_client *client)
 		}
 	}
 
+	clk_disable_unprepare(sor->clk_safe);
+	clk_disable_unprepare(sor->clk_dp);
+	clk_disable_unprepare(sor->clk);
+
 	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
 		err = tegra_sor_debugfs_exit(sor);
 		if (err < 0)
 			dev_err(sor->dev, "debugfs cleanup failed: %d\n", err);
 	}
 
-	err = tegra_output_exit(&sor->output);
-	if (err < 0) {
-		dev_err(sor->dev, "output cleanup failed: %d\n", err);
-		return err;
-	}
-
 	return 0;
 }
 
@@ -1398,26 +1467,14 @@ static int tegra_sor_probe(struct platform_device *pdev)
 	if (IS_ERR(sor->clk_parent))
 		return PTR_ERR(sor->clk_parent);
 
-	err = clk_prepare_enable(sor->clk_parent);
-	if (err < 0)
-		return err;
-
 	sor->clk_safe = devm_clk_get(&pdev->dev, "safe");
 	if (IS_ERR(sor->clk_safe))
 		return PTR_ERR(sor->clk_safe);
 
-	err = clk_prepare_enable(sor->clk_safe);
-	if (err < 0)
-		return err;
-
 	sor->clk_dp = devm_clk_get(&pdev->dev, "dp");
 	if (IS_ERR(sor->clk_dp))
 		return PTR_ERR(sor->clk_dp);
 
-	err = clk_prepare_enable(sor->clk_dp);
-	if (err < 0)
-		return err;
-
 	INIT_LIST_HEAD(&sor->client.list);
 	sor->client.ops = &sor_client_ops;
 	sor->client.dev = &pdev->dev;
@@ -1448,10 +1505,11 @@ static int tegra_sor_remove(struct platform_device *pdev)
 		return err;
 	}
 
-	clk_disable_unprepare(sor->clk_parent);
-	clk_disable_unprepare(sor->clk_safe);
-	clk_disable_unprepare(sor->clk_dp);
-	clk_disable_unprepare(sor->clk);
+	err = tegra_output_remove(&sor->output);
+	if (err < 0) {
+		dev_err(&pdev->dev, "failed to remove output: %d\n", err);
+		return err;
+	}
 
 	return 0;
 }
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

  parent reply	other threads:[~2015-01-20 10:48 UTC|newest]

Thread overview: 61+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-01-20 10:48 [PATCH 00/36] drm/tegra: Atomic mode-setting conversion Thierry Reding
2015-01-20 10:48 ` [PATCH 01/36] clk: Introduce clk_try_parent() Thierry Reding
2015-01-20 18:02   ` Mike Turquette
2015-01-20 18:16     ` Rob Clark
2015-01-21 15:12       ` Thierry Reding
     [not found]   ` <1421750935-4023-2-git-send-email-thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2015-01-20 19:21     ` Stephen Boyd
2015-01-21 15:05       ` Thierry Reding
2015-01-21 16:13   ` [PATCH v2] clk: Introduce clk_has_parent() Thierry Reding
     [not found]     ` <1421856780-32103-1-git-send-email-thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2015-01-22  0:16       ` Stephen Boyd
     [not found]         ` <54C04145.1010906-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2015-01-22  7:37           ` Thierry Reding
2015-01-22 20:25             ` Stephen Boyd
2015-01-23  9:34               ` Thierry Reding
2015-01-25  1:02                 ` Mike Turquette
2015-01-20 10:48 ` [PATCH 02/36] drm/plane: Make ->atomic_update() mandatory Thierry Reding
2015-01-20 11:10   ` Daniel Vetter
     [not found]   ` <1421750935-4023-3-git-send-email-thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2015-01-20 13:51     ` Rob Clark
2015-01-23  9:18       ` Thierry Reding
2015-01-23  9:26     ` [PATCH v2] " Thierry Reding
2015-01-20 10:48 ` [PATCH 03/36] drm/plane: Add optional ->atomic_disable() callback Thierry Reding
2015-01-20 11:13   ` Daniel Vetter
2015-01-22 18:57   ` Gustavo Padovan
2015-01-23  9:28   ` [PATCH v4] " Thierry Reding
2015-01-20 10:48 ` [PATCH 04/36] drm/atomic: Add ->atomic_check() to encoder helpers Thierry Reding
2015-01-20 11:07   ` Daniel Vetter
2015-01-20 10:48 ` [PATCH 05/36] drm/atomic: Add drm_atomic_plane_get_crtc_state() Thierry Reding
2015-01-20 11:10   ` Daniel Vetter
     [not found]     ` <20150120111011.GX10113-dv86pmgwkMBes7Z6vYuT8azUEOm+Xw19@public.gmane.org>
2015-01-23  9:32       ` Thierry Reding
2015-01-20 10:48 ` [PATCH 06/36] drm/tegra: Use tegra_commit_dc() in output drivers Thierry Reding
2015-01-20 10:48 ` [PATCH 07/36] drm/tegra: Stop CRTC at CRTC disable time Thierry Reding
2015-01-20 10:48 ` [PATCH 08/36] drm/tegra: dc: Wait for idle when disabled Thierry Reding
2015-01-20 10:48 ` [PATCH 09/36] drm/tegra: Move tegra_drm_mode_funcs to the core Thierry Reding
2015-01-20 10:48 ` [PATCH 10/36] drm/tegra: dc: No longer disable planes at CRTC disable Thierry Reding
2015-01-20 10:48 ` [PATCH 11/36] drm/tegra: Convert output midlayer to helpers Thierry Reding
2015-01-20 10:48 ` [PATCH 12/36] drm/tegra: output: Make ->setup_clock() optional Thierry Reding
2015-01-20 10:48 ` [PATCH 13/36] drm/tegra: Add tegra_dc_setup_clock() helper Thierry Reding
2015-01-20 10:48 ` [PATCH 14/36] drm/tegra: rgb: Demidlayer Thierry Reding
2015-01-20 10:48 ` [PATCH 15/36] drm/tegra: hdmi: Demidlayer Thierry Reding
2015-01-20 10:48 ` [PATCH 16/36] drm/tegra: dsi: Demidlayer Thierry Reding
2015-01-20 10:48 ` Thierry Reding [this message]
2015-01-20 10:48 ` [PATCH 18/36] drm/tegra: debugfs cleanup cannot fail Thierry Reding
2015-01-20 10:48 ` [PATCH 19/36] drm/tegra: Remove remnants of the output midlayer Thierry Reding
2015-01-20 10:48 ` [PATCH 20/36] drm/tegra: Output cleanup functions cannot fail Thierry Reding
2015-01-20 10:48 ` [PATCH 21/36] drm/tegra: dc: Do not needlessly deassert reset Thierry Reding
2015-01-20 10:48 ` [PATCH 22/36] drm/tegra: Atomic conversion, phase 1 Thierry Reding
2015-01-20 10:48 ` [PATCH 23/36] drm/tegra: Atomic conversion, phase 2 Thierry Reding
2015-01-20 10:48 ` [PATCH 24/36] drm/tegra: Atomic conversion, phase 3, step 1 Thierry Reding
2015-01-20 10:48 ` [PATCH 25/36] drm/tegra: dc: Store clock setup in atomic state Thierry Reding
2015-01-20 10:48 ` [PATCH 26/36] drm/tegra: rgb: Implement ->atomic_check() Thierry Reding
2015-01-20 10:48 ` [PATCH 27/36] drm/tegra: dsi: " Thierry Reding
2015-01-20 10:48 ` [PATCH 28/36] drm/tegra: hdmi: " Thierry Reding
2015-01-20 10:48 ` [PATCH 29/36] drm/tegra: sor: " Thierry Reding
2015-01-20 10:48 ` [PATCH 30/36] drm/tegra: dc: Use atomic clock state in modeset Thierry Reding
2015-01-20 10:48 ` [PATCH 31/36] drm/tegra: Atomic conversion, phase 3, step 2 Thierry Reding
2015-01-20 10:48 ` [PATCH 32/36] drm/tegra: Atomic conversion, phase 3, step 3 Thierry Reding
2015-01-20 10:48 ` [PATCH 33/36] drm/tegra: Remove unused ->mode_fixup() callbacks Thierry Reding
2015-01-20 10:48 ` [PATCH 34/36] drm/tegra: Track active planes in CRTC state Thierry Reding
2015-01-20 11:18   ` Daniel Vetter
2015-01-20 11:43     ` Thierry Reding
     [not found]   ` <1421750935-4023-35-git-send-email-thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2015-01-23  9:31     ` [PATCH v2] " Thierry Reding
2015-01-20 10:48 ` [PATCH 35/36] drm/tegra: Track tiling and format in plane state Thierry Reding
2015-01-20 10:48 ` [PATCH 36/36] drm/tegra: dc: Unify enabling the display controller Thierry Reding

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1421750935-4023-18-git-send-email-thierry.reding@gmail.com \
    --to=thierry.reding@gmail.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-tegra@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).