diff mbox

[PATCH/RFC,386/390] drm/rcar-du: Add support for cloned mode on DU1

Message ID 1364525119-31791-387-git-send-email-horms+renesas@verge.net.au (mailing list archive)
State New, archived
Headers show

Commit Message

Simon Horman March 29, 2013, 2:45 a.m. UTC
From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>

The DU1 output can be driven by the superposition processor (CRTC) 0 in
cloned mode. Support this.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
(cherry picked from commit b73fdbb589a10eaabed773453579c2929436ac68)

Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
---
 drivers/gpu/drm/rcar-du/rcar_du_crtc.c |   44 ++++++++++++++++++++++++--------
 drivers/gpu/drm/rcar-du/rcar_du_crtc.h |    2 ++
 drivers/gpu/drm/rcar-du/rcar_du_drv.c  |    5 ++++
 drivers/gpu/drm/rcar-du/rcar_du_kms.c  |   38 +++++++++++++++++++++------
 drivers/gpu/drm/rcar-du/rcar_du_kms.h  |    1 +
 drivers/gpu/drm/rcar-du/rcar_du_lvds.c |   10 +++-----
 drivers/gpu/drm/rcar-du/rcar_du_lvds.h |    3 +--
 drivers/gpu/drm/rcar-du/rcar_du_regs.h |    2 ++
 drivers/gpu/drm/rcar-du/rcar_du_vga.c  |   10 +++-----
 drivers/gpu/drm/rcar-du/rcar_du_vga.h  |    2 +-
 include/linux/platform_data/rcar-du.h  |    5 +++-
 11 files changed, 87 insertions(+), 35 deletions(-)
diff mbox

Patch

diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
index 98c54ff..db53c25 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -112,6 +112,25 @@  static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
 	rcar_du_crtc_write(rcrtc, DEWR,  mode->hdisplay);
 }
 
+static void rcar_du_crtc_set_routing(struct rcar_du_crtc *rcrtc)
+{
+	struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
+	u32 dorcr = rcar_du_read(rcdu, DORCR);
+
+	dorcr &= ~(DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_MASK);
+
+	/* Set the DU1 pins sources. Select CRTC 0 if explicitly requested and
+	 * CRTC 1 in all other cases to avoid cloning CRTC 0 to DU0 and DU1 by
+	 * default.
+	 */
+	if (rcrtc->outputs & (1 << 1) && rcrtc->index == 0)
+		dorcr |= DORCR_PG2D_DS1;
+	else
+		dorcr |= DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_DS2;
+
+	rcar_du_write(rcdu, DORCR, dorcr);
+}
+
 static void __rcar_du_start_stop(struct rcar_du_device *rcdu, bool start)
 {
 	rcar_du_write(rcdu, DSYSR,
@@ -142,6 +161,16 @@  static void rcar_du_start_stop(struct rcar_du_device *rcdu, bool start)
 	}
 }
 
+void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output)
+{
+	struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+
+	/* Store the route from the CRTC output to the DU output. The DU will be
+	 * configured when starting the CRTC.
+	 */
+	rcrtc->outputs |= 1 << output;
+}
+
 void rcar_du_crtc_update_planes(struct drm_crtc *crtc)
 {
 	struct rcar_du_device *rcdu = crtc->dev->dev_private;
@@ -213,13 +242,6 @@  void rcar_du_crtc_update_planes(struct drm_crtc *crtc)
 	rcar_du_write(rcdu, rcrtc->index ? DS2PR : DS1PR, dspr);
 }
 
-/*
- * rcar_du_crtc_start - Configure and start the LCDC
- * @rcrtc: the SH Mobile CRTC
- *
- * Configure and start the LCDC device. External devices (clocks, MERAM, panels,
- * ...) are not touched by this function.
- */
 static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc)
 {
 	struct drm_crtc *crtc = &rcrtc->crtc;
@@ -236,11 +258,9 @@  static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc)
 	rcar_du_crtc_write(rcrtc, DOOR, DOOR_RGB(0, 0, 0));
 	rcar_du_crtc_write(rcrtc, BPOR, BPOR_RGB(0, 0, 0));
 
-	/* Configure output routing: enable both superposition processors */
-	rcar_du_write(rcdu, DORCR, DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_DS2 |
-		      DORCR_PG1D_DS1 | DORCR_DPRS);
-
+	/* Configure display timings and output routing */
 	rcar_du_crtc_set_display_timing(rcrtc);
+	rcar_du_crtc_set_routing(rcrtc);
 
 	mutex_lock(&rcdu->planes.lock);
 	rcrtc->plane->enabled = true;
@@ -397,6 +417,8 @@  static int rcar_du_crtc_mode_set(struct drm_crtc *crtc,
 
 	rcar_du_plane_compute_base(rcrtc->plane, crtc->fb);
 
+	rcrtc->outputs = 0;
+
 	return 0;
 
 error:
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
index c7b668b..2a0365b 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
@@ -30,6 +30,7 @@  struct rcar_du_crtc {
 	bool started;
 
 	struct drm_pending_vblank_event *event;
+	unsigned int outputs;
 	int dpms;
 
 	struct rcar_du_plane *plane;
@@ -43,6 +44,7 @@  void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc,
 void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc);
 void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc);
 
+void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output);
 void rcar_du_crtc_update_planes(struct drm_crtc *crtc);
 
 #endif /* __RCAR_DU_CRTC_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
index 1facacc..0fb82bf 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
@@ -60,6 +60,11 @@  int rcar_du_get(struct rcar_du_device *rcdu)
 	rcar_du_write(rcdu, DEFR3, DEFR3_CODE | DEFR3_DEFE3);
 	rcar_du_write(rcdu, DEFR4, DEFR4_CODE);
 
+	/* Use DS1PR and DS2PR to configure planes priorities and connects the
+	 * superposition 0 to DU0 pins. DU1 pins will be configured dynamically.
+	 */
+	rcar_du_write(rcdu, DORCR, DORCR_PG1D_DS1 | DORCR_DPRS);
+
 done:
 	rcdu->use_count++;
 	return 0;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
index e0f41ef..b6cd53f 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
@@ -119,6 +119,9 @@  void rcar_du_encoder_mode_set(struct drm_encoder *encoder,
 			      struct drm_display_mode *mode,
 			      struct drm_display_mode *adjusted_mode)
 {
+	struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
+
+	rcar_du_crtc_route_output(encoder->crtc, renc->output);
 }
 
 void rcar_du_encoder_mode_commit(struct drm_encoder *encoder)
@@ -165,7 +168,8 @@  static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = {
 
 int rcar_du_modeset_init(struct rcar_du_device *rcdu)
 {
-	const struct rcar_du_encoder_data *pdata;
+	struct drm_device *dev = rcdu->ddev;
+	struct drm_encoder *encoder;
 	unsigned int i;
 	int ret;
 
@@ -181,20 +185,30 @@  int rcar_du_modeset_init(struct rcar_du_device *rcdu)
 	if (ret < 0)
 		return ret;
 
-	for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i) {
+	for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i)
 		rcar_du_crtc_create(rcdu, i);
 
-		pdata = &rcdu->pdata->encoders[i];
-		if (pdata->encoder == RCAR_DU_ENCODER_UNUSED)
+	rcdu->used_crtcs = 0;
+	rcdu->num_crtcs = i;
+
+	for (i = 0; i < rcdu->pdata->num_encoders; ++i) {
+		const struct rcar_du_encoder_data *pdata =
+			&rcdu->pdata->encoders[i];
+
+		if (pdata->output >= ARRAY_SIZE(rcdu->crtcs)) {
+			dev_warn(rcdu->dev,
+				 "encoder %u references unexisting output %u, skipping\n",
+				 i, pdata->output);
 			continue;
+		}
 
 		switch (pdata->encoder) {
 		case RCAR_DU_ENCODER_VGA:
-			rcar_du_vga_init(rcdu, &pdata->u.vga, i);
+			rcar_du_vga_init(rcdu, &pdata->u.vga, pdata->output);
 			break;
 
 		case RCAR_DU_ENCODER_LVDS:
-			rcar_du_lvds_init(rcdu, &pdata->u.lvds, i);
+			rcar_du_lvds_init(rcdu, &pdata->u.lvds, pdata->output);
 			break;
 
 		default:
@@ -202,8 +216,16 @@  int rcar_du_modeset_init(struct rcar_du_device *rcdu)
 		}
 	}
 
-	rcdu->used_crtcs = 0;
-	rcdu->num_crtcs = i;
+	/* Set the possible CRTCs and possible clones. All encoders can be
+	 * driven by the CRTC associated with the output they're connected to,
+	 * as well as by CRTC 0.
+	 */
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+		struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
+
+		encoder->possible_crtcs = (1 << 0) | (1 << renc->output);
+		encoder->possible_clones = 1 << 0;
+	}
 
 	ret = rcar_du_plane_register(rcdu);
 	if (ret < 0)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.h b/drivers/gpu/drm/rcar-du/rcar_du_kms.h
index 2a94280..e4d8db0 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_kms.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.h
@@ -30,6 +30,7 @@  struct rcar_du_format_info {
 
 struct rcar_du_encoder {
 	struct drm_encoder encoder;
+	unsigned int output;
 };
 
 #define to_rcar_encoder(e) \
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvds.c b/drivers/gpu/drm/rcar-du/rcar_du_lvds.c
index c0c7b11..8a3ef36 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvds.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_lvds.c
@@ -188,25 +188,23 @@  static const struct drm_encoder_funcs encoder_funcs = {
 
 int rcar_du_lvds_init(struct rcar_du_device *rcdu,
 		      const struct rcar_du_encoder_lvds_data *data,
-		      unsigned int crtc)
+		      unsigned int output)
 {
 	struct rcar_du_encoder *renc;
-	struct drm_encoder *encoder;
 	int ret;
 
 	renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL);
 	if (renc == NULL)
 		return -ENOMEM;
 
-	encoder = &renc->encoder;
-	encoder->possible_crtcs = 1 << crtc;
+	renc->output = output;
 
-	ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs,
+	ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs,
 			       DRM_MODE_ENCODER_LVDS);
 	if (ret < 0)
 		return ret;
 
-	drm_encoder_helper_add(encoder, &encoder_helper_funcs);
+	drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs);
 
 	return rcar_du_lvds_connector_init(rcdu, renc, &data->panel);
 }
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvds.h b/drivers/gpu/drm/rcar-du/rcar_du_lvds.h
index a6e8ab9..b47f832 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvds.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_lvds.h
@@ -14,12 +14,11 @@ 
 #ifndef __RCAR_DU_LVDS_H__
 #define __RCAR_DU_LVDS_H__
 
-struct rcar_du_crtc;
 struct rcar_du_device;
 struct rcar_du_encoder_lvds_data;
 
 int rcar_du_lvds_init(struct rcar_du_device *rcdu,
 		      const struct rcar_du_encoder_lvds_data *data,
-		      unsigned int crtc);
+		      unsigned int output);
 
 #endif /* __RCAR_DU_LVDS_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_regs.h b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
index bf731de..69f21f1 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_regs.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
@@ -408,11 +408,13 @@ 
 #define DORCR_PG2D_DS2		(1 << 24)
 #define DORCR_PG2D_FIX0		(2 << 24)
 #define DORCR_PG2D_DOOR		(3 << 24)
+#define DORCR_PG2D_MASK		(3 << 24)
 #define DORCR_DR1D		(1 << 21)
 #define DORCR_PG1D_DS1		(0 << 16)
 #define DORCR_PG1D_DS2		(1 << 16)
 #define DORCR_PG1D_FIX0		(2 << 16)
 #define DORCR_PG1D_DOOR		(3 << 16)
+#define DORCR_PG1D_MASK		(3 << 16)
 #define DORCR_RGPV		(1 << 4)
 #define DORCR_DPRS		(1 << 0)
 
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vga.c b/drivers/gpu/drm/rcar-du/rcar_du_vga.c
index 6b286b2..2e488dd 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_vga.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vga.c
@@ -127,25 +127,23 @@  static const struct drm_encoder_funcs encoder_funcs = {
 
 int rcar_du_vga_init(struct rcar_du_device *rcdu,
 		     const struct rcar_du_encoder_vga_data *data,
-		     unsigned int crtc)
+		     unsigned int output)
 {
 	struct rcar_du_encoder *renc;
-	struct drm_encoder *encoder;
 	int ret;
 
 	renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL);
 	if (renc == NULL)
 		return -ENOMEM;
 
-	encoder = &renc->encoder;
-	encoder->possible_crtcs = 1 << crtc;
+	renc->output = output;
 
-	ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs,
+	ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs,
 			       DRM_MODE_ENCODER_DAC);
 	if (ret < 0)
 		return ret;
 
-	drm_encoder_helper_add(encoder, &encoder_helper_funcs);
+	drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs);
 
 	return rcar_du_vga_connector_init(rcdu, renc);
 }
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vga.h b/drivers/gpu/drm/rcar-du/rcar_du_vga.h
index d73f935..66b4d2d 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_vga.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vga.h
@@ -19,6 +19,6 @@  struct rcar_du_encoder_vga_data;
 
 int rcar_du_vga_init(struct rcar_du_device *rcdu,
 		     const struct rcar_du_encoder_vga_data *data,
-		     unsigned int crtc);
+		     unsigned int output);
 
 #endif /* __RCAR_DU_VGA_H__ */
diff --git a/include/linux/platform_data/rcar-du.h b/include/linux/platform_data/rcar-du.h
index c315d32..80587fd 100644
--- a/include/linux/platform_data/rcar-du.h
+++ b/include/linux/platform_data/rcar-du.h
@@ -38,6 +38,8 @@  struct rcar_du_encoder_vga_data {
 
 struct rcar_du_encoder_data {
 	enum rcar_du_encoder_type encoder;
+	unsigned int output;
+
 	union {
 		struct rcar_du_encoder_lvds_data lvds;
 		struct rcar_du_encoder_vga_data vga;
@@ -45,7 +47,8 @@  struct rcar_du_encoder_data {
 };
 
 struct rcar_du_platform_data {
-	struct rcar_du_encoder_data encoders[2];
+	struct rcar_du_encoder_data *encoders;
+	unsigned int num_encoders;
 };
 
 #endif /* __RCAR_DU_H__ */