Skip to contentSkip to conversation

drm/tegra: Basic Tegra186 support

PATCH New Thierry Reding 2 participants 13 messages Nov 27, 2017 1 message not in the archive

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

cover drm/tegra: Basic Tegra186 support

From: Thierry Reding <[email protected]>

Hi,

this series adds support for display and HDMI on Tegra186. The initial 6 patches are preparatory work to move code around and make the subsequent patches easier to review.

Patch 7 introduces the display hub driver which controls resources shared between all display controllers and patch 8 extends the display controller driver with Tegra186 support. Some changes to the registers are backwards incompatible, but this can be dealt with with a bit of parameterization of code and register offsets.

Finally, the SOR driver is also parameterized to take into account some changes on Tegra186 and the last patch in the series enables support for HDMI 2.0 modes using the SCDC helpers.

Thierry

Thierry Reding (12):
drm/tegra: dc: Remove tegra_primary_plane_destroy() drm/tegra: dc: Remove duplicate plane funcs drm/tegra: dc: Remove tegra_overlay_plane_destroy() drm/tegra: dc: Remove duplicate plane funcs drm/tegra: dc: Move state definition to header drm/tegra: Move common plane code to separate file drm/tegra: Add Tegra186 display hub support drm/tegra: dc: Add Tegra186 support drm/tegra: Support ARGB and ABGR formats drm/tegra: sor: Parameterize register offsets drm/tegra: sor: Add Tegra186 support drm/tegra: sor: Support HDMI 2.0 modes

 drivers/gpu/drm/tegra/Makefile |   2 +
 drivers/gpu/drm/tegra/dc.c     | 549 +++++++++++++---------------
 drivers/gpu/drm/tegra/dc.h     | 237 ++++++++++--
 drivers/gpu/drm/tegra/drm.c    |  70 +++-
 drivers/gpu/drm/tegra/drm.h    |  21 ++
 drivers/gpu/drm/tegra/hub.c    | 808 +++++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/tegra/hub.h    |  82 +++++
 drivers/gpu/drm/tegra/output.c |  24 ++
 drivers/gpu/drm/tegra/plane.c  | 188 ++++++++++
 drivers/gpu/drm/tegra/plane.h  |  61 ++++
 drivers/gpu/drm/tegra/sor.c    | 805 +++++++++++++++++++++++++++++-----------
 drivers/gpu/drm/tegra/sor.h    |  16 +
 12 files changed, 2329 insertions(+), 534 deletions(-)
 create mode 100644 drivers/gpu/drm/tegra/hub.c
 create mode 100644 drivers/gpu/drm/tegra/hub.h
 create mode 100644 drivers/gpu/drm/tegra/plane.c
 create mode 100644 drivers/gpu/drm/tegra/plane.h
Signature · 1 line
2.15.0

A message that is not in the archive (sent off-list or not yet received): 20171127100952.22465-1-thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org

#2 lore raw

PATCH 1/12 drm/tegra: dc: Remove tegra_primary_plane_destroy()

From: Thierry Reding <[email protected]>

This function is a simple wrapper around tegra_plane_destroy(), so it can be dropped.

---
 drivers/gpu/drm/tegra/dc.c | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)
drivers/gpu/drm/tegra/dc.c +1 -6
index f825c39888f9..552710f5c386 100644
@@ -417,11 +417,6 @@ static const u32 tegra_primary_plane_formats[] = {
 	DRM_FORMAT_RGB565,
 };
 
-static void tegra_primary_plane_destroy(struct drm_plane *plane)
-{
-	tegra_plane_destroy(plane);
-}
-
 static void tegra_plane_reset(struct drm_plane *plane)
 {
 	struct tegra_plane_state *state;
@@ -466,7 +461,7 @@ static void tegra_plane_atomic_destroy_state(struct drm_plane *plane,
 static const struct drm_plane_funcs tegra_primary_plane_funcs = {
 	.update_plane = drm_atomic_helper_update_plane,
 	.disable_plane = drm_atomic_helper_disable_plane,
-	.destroy = tegra_primary_plane_destroy,
+	.destroy = tegra_plane_destroy,
 	.reset = tegra_plane_reset,
 	.atomic_duplicate_state = tegra_plane_atomic_duplicate_state,
 	.atomic_destroy_state = tegra_plane_atomic_destroy_state,
Signature · 1 line
2.15.0
#3 lore raw

PATCH 2/12 drm/tegra: dc: Remove duplicate plane funcs

In reply to a message not in the archive

From: Thierry Reding <[email protected]>

Both tegra_primary_plane_funcs and tegra_cursor_plane_funcs are identical. Get rid of the duplicate and use one set of function pointers for all planes.

---
 drivers/gpu/drm/tegra/dc.c | 15 +++------------
 1 file changed, 3 insertions(+), 12 deletions(-)
drivers/gpu/drm/tegra/dc.c +3 -12
index 552710f5c386..2b7ece1d0c18 100644
@@ -458,7 +458,7 @@ static void tegra_plane_atomic_destroy_state(struct drm_plane *plane,
 	kfree(state);
 }
 
-static const struct drm_plane_funcs tegra_primary_plane_funcs = {
+static const struct drm_plane_funcs tegra_plane_funcs = {
 	.update_plane = drm_atomic_helper_update_plane,
 	.disable_plane = drm_atomic_helper_disable_plane,
 	.destroy = tegra_plane_destroy,
@@ -673,7 +673,7 @@ static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm,
 	formats = tegra_primary_plane_formats;
 
 	err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
-				       &tegra_primary_plane_funcs, formats,
+				       &tegra_plane_funcs, formats,
 				       num_formats, NULL,
 				       DRM_PLANE_TYPE_PRIMARY, NULL);
 	if (err < 0) {
@@ -799,15 +799,6 @@ static void tegra_cursor_atomic_disable(struct drm_plane *plane,
 	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
 }
 
-static const struct drm_plane_funcs tegra_cursor_plane_funcs = {
-	.update_plane = drm_atomic_helper_update_plane,
-	.disable_plane = drm_atomic_helper_disable_plane,
-	.destroy = tegra_plane_destroy,
-	.reset = tegra_plane_reset,
-	.atomic_duplicate_state = tegra_plane_atomic_duplicate_state,
-	.atomic_destroy_state = tegra_plane_atomic_destroy_state,
-};
-
 static const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = {
 	.atomic_check = tegra_cursor_atomic_check,
 	.atomic_update = tegra_cursor_atomic_update,
@@ -840,7 +831,7 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
 	formats = tegra_cursor_plane_formats;
 
 	err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe,
-				       &tegra_cursor_plane_funcs, formats,
+				       &tegra_plane_funcs, formats,
 				       num_formats, NULL,
 				       DRM_PLANE_TYPE_CURSOR, NULL);
 	if (err < 0) {
Signature · 1 line
2.15.0
#4 lore raw

PATCH 3/12 drm/tegra: dc: Remove tegra_overlay_plane_destroy()

In reply to a message not in the archive

From: Thierry Reding <[email protected]>

This function is a simple wrapper around tegra_plane_destroy(), so it can be dropped.

---
 drivers/gpu/drm/tegra/dc.c | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)
drivers/gpu/drm/tegra/dc.c +1 -6
index 2b7ece1d0c18..fdffab7e41b9 100644
@@ -844,15 +844,10 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
 	return &plane->base;
 }
 
-static void tegra_overlay_plane_destroy(struct drm_plane *plane)
-{
-	tegra_plane_destroy(plane);
-}
-
 static const struct drm_plane_funcs tegra_overlay_plane_funcs = {
 	.update_plane = drm_atomic_helper_update_plane,
 	.disable_plane = drm_atomic_helper_disable_plane,
-	.destroy = tegra_overlay_plane_destroy,
+	.destroy = tegra_plane_destroy,
 	.reset = tegra_plane_reset,
 	.atomic_duplicate_state = tegra_plane_atomic_duplicate_state,
 	.atomic_destroy_state = tegra_plane_atomic_destroy_state,
Signature · 1 line
2.15.0
#5 lore raw

PATCH 5/12 drm/tegra: dc: Move state definition to header

In reply to a message not in the archive

From: Thierry Reding <[email protected]>

Move the display controller state definition to the header file so that it can be referenced by other files.

---
 drivers/gpu/drm/tegra/dc.c | 18 ------------------
 drivers/gpu/drm/tegra/dc.h | 18 ++++++++++++++++++
 2 files changed, 18 insertions(+), 18 deletions(-)
drivers/gpu/drm/tegra/dc.c +0 -18
index 1075b4e06834..88b8d137726e 100644
@@ -34,24 +34,6 @@ static inline struct tegra_plane *to_tegra_plane(struct drm_plane *plane)
 	return container_of(plane, struct tegra_plane, base);
 }
 
-struct tegra_dc_state {
-	struct drm_crtc_state base;
-
-	struct clk *clk;
-	unsigned long pclk;
-	unsigned int div;
-
-	u32 planes;
-};
-
-static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state)
-{
-	if (state)
-		return container_of(state, struct tegra_dc_state, base);
-
-	return NULL;
-}
-
 struct tegra_plane_state {
 	struct drm_plane_state base;
 
drivers/gpu/drm/tegra/dc.h +18 -0
index 6e46e4aed10b..b65dfbb0af89 100644
@@ -18,6 +18,24 @@
 
 struct tegra_output;
 
+struct tegra_dc_state {
+	struct drm_crtc_state base;
+
+	struct clk *clk;
+	unsigned long pclk;
+	unsigned int div;
+
+	u32 planes;
+};
+
+static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state)
+{
+	if (state)
+		return container_of(state, struct tegra_dc_state, base);
+
+	return NULL;
+}
+
 struct tegra_dc_stats {
 	unsigned long frames;
 	unsigned long vblank;
Signature · 1 line
2.15.0
#6 lore raw

PATCH 6/12 drm/tegra: Move common plane code to separate file

In reply to a message not in the archive

From: Thierry Reding <[email protected]>

Subsequent patches will add support for Tegra186 which has a different architecture and needs different plane code but which can share a lot of code with earlier Tegra support.

---
 drivers/gpu/drm/tegra/Makefile |   1 +
 drivers/gpu/drm/tegra/dc.c     | 199 ++---------------------------------------
 drivers/gpu/drm/tegra/plane.c  | 180 +++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/tegra/plane.h  |  59 ++++++++++++
 4 files changed, 245 insertions(+), 194 deletions(-)
 create mode 100644 drivers/gpu/drm/tegra/plane.c
 create mode 100644 drivers/gpu/drm/tegra/plane.h
drivers/gpu/drm/tegra/Makefile +1 -0
index 46d65d39214d..a47784765217 100644
@@ -5,6 +5,7 @@ tegra-drm-y := \
 	drm.o \
 	gem.o \
 	fb.o \
+	plane.o \
 	dc.o \
 	output.o \
 	rgb.o \
drivers/gpu/drm/tegra/dc.c +5 -194

240 lines not on this page · raw message

drivers/gpu/drm/tegra/plane.c new file +180 -0

180 lines not on this page · raw message

drivers/gpu/drm/tegra/plane.h new file +59 -0

59 lines not on this page · raw message

Signature · 1 line
2.15.0
#7 lore raw

PATCH 8/12 drm/tegra: dc: Add Tegra186 support

In reply to a message not in the archive

From: Thierry Reding <[email protected]>

The display architecture has changed in several signifcant ways with the new Tegra186 SoC. Display controllers are a completely different design, but have been given a frontend that simulates the register interface for earlier chips.

Unfortunately the frontend isn't completely backwards compatible, so the driver needs parameterization to take the changes into account.

One big change is that the total number of display controllers has been increased to three. At the same time the number of planes available has remained constant. However, planes can now be freely assigned between the display controllers, giving applications more flexibility in making the best use of the available resources.

---
 drivers/gpu/drm/tegra/dc.c  | 250 +++++++++++++++++++++++++++++++++++---------
 drivers/gpu/drm/tegra/dc.h  |  98 +++++++++++------
 drivers/gpu/drm/tegra/drm.c |   1 +
 3 files changed, 267 insertions(+), 82 deletions(-)
drivers/gpu/drm/tegra/dc.c +200 -50
index 0ba1bf7f7615..2d54657b538e 100644
@@ -19,6 +19,7 @@
 #include "dc.h"
 #include "drm.h"
 #include "gem.h"
+#include "hub.h"
 #include "plane.h"
 
 #include <drm/drm_atomic.h>
@@ -437,8 +438,8 @@ static const struct drm_plane_helper_funcs tegra_plane_helper_funcs = {
 	.prepare_fb = tegra_plane_prepare_fb,
 };
 
-static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm,
-						       struct tegra_dc *dc)
+static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm,
+						    struct tegra_dc *dc)
 {
 	/*
 	 * Ideally this would use drm_crtc_mask(), but that would require the
@@ -453,6 +454,7 @@ static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm,
 	 * the same as drm_crtc_index() after registration.
 	 */
 	unsigned long possible_crtcs = 1 << drm->mode_config.num_crtc;
+	enum drm_plane_type type = DRM_PLANE_TYPE_PRIMARY;
 	struct tegra_plane *plane;
 	unsigned int num_formats;
 	const u32 *formats;
@@ -476,8 +478,7 @@ static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm,
 
 	err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
 				       &tegra_plane_funcs, formats,
-				       num_formats, NULL,
-				       DRM_PLANE_TYPE_PRIMARY, NULL);
+				       num_formats, NULL, type, NULL);
 	if (err < 0) {
 		kfree(plane);
 		return ERR_PTR(err);
@@ -691,18 +692,61 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm,
 	return &plane->base;
 }
 
-static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc)
+static struct drm_plane *tegra_dc_add_shared_planes(struct drm_device *drm,
+						    struct tegra_dc *dc)
+{
+	struct drm_plane *plane, *primary = NULL;
+	unsigned int i, j;
+
+	for (i = 0; i < dc->soc->num_wgrps; i++) {
+		const struct tegra_windowgroup_soc *wgrp = &dc->soc->wgrps[i];
+
+		if (wgrp->dc == dc->pipe) {
+			for (j = 0; j < wgrp->num_windows; j++) {
+				unsigned int index = wgrp->windows[j];
+
+				plane = tegra_shared_plane_create(drm, dc,
+								  wgrp->index,
+								  index);
+				if (IS_ERR(plane))
+					return plane;
+
+				/*
+				 * Choose the first shared plane owned by this
+				 * head as the primary plane.
+				 */
+				if (!primary) {
+					plane->type = DRM_PLANE_TYPE_PRIMARY;
+					primary = plane;
+				}
+			}
+		}
+	}
+
+	return primary;
+}
+
+static struct drm_plane *tegra_dc_add_planes(struct drm_device *drm,
+					     struct tegra_dc *dc)
 {
-	struct drm_plane *plane;
+	struct drm_plane *plane, *primary;
 	unsigned int i;
 
+	primary = tegra_primary_plane_create(drm, dc);
+	if (IS_ERR(primary))
+		return primary;
+
 	for (i = 0; i < 2; i++) {
 		plane = tegra_dc_overlay_plane_create(drm, dc, 1 + i);
-		if (IS_ERR(plane))
-			return PTR_ERR(plane);
+		if (IS_ERR(plane)) {
+			/* XXX tegra_plane_destroy() */
+			drm_plane_cleanup(primary);
+			kfree(primary);
+			return plane;
+		}
 	}
 
-	return 0;
+	return primary;
 }
 
 static void tegra_dc_destroy(struct drm_crtc *crtc)
@@ -1092,7 +1136,8 @@ static u32 tegra_dc_get_vblank_counter(struct drm_crtc *crtc)
 {
 	struct tegra_dc *dc = to_tegra_dc(crtc);
 
-	if (dc->syncpt)
+	/* XXX vblank syncpoints don't work with nvdisplay yet */
+	if (dc->syncpt && !dc->soc->has_nvdisplay)
 		return host1x_syncpt_read(dc->syncpt);
 
 	/* fallback to software emulated VBLANK counter */
@@ -1150,10 +1195,12 @@ static int tegra_dc_set_timings(struct tegra_dc *dc,
 	unsigned int v_ref_to_sync = 1;
 	unsigned long value;
 
-	tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS);
+	if (!dc->soc->has_nvdisplay) {
+		tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS);
 
-	value = (v_ref_to_sync << 16) | h_ref_to_sync;
-	tegra_dc_writel(dc, value, DC_DISP_REF_TO_SYNC);
+		value = (v_ref_to_sync << 16) | h_ref_to_sync;
+		tegra_dc_writel(dc, value, DC_DISP_REF_TO_SYNC);
+	}
 
 	value = ((mode->vsync_end - mode->vsync_start) << 16) |
 		((mode->hsync_end - mode->hsync_start) <<  0);
@@ -1232,8 +1279,10 @@ static void tegra_dc_commit_state(struct tegra_dc *dc,
 		      state->div);
 	DRM_DEBUG_KMS("pclk: %lu\n", state->pclk);
 
-	value = SHIFT_CLK_DIVIDER(state->div) | PIXEL_CLK_DIVIDER_PCD1;
-	tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);
+	if (!dc->soc->has_nvdisplay) {
+		value = SHIFT_CLK_DIVIDER(state->div) | PIXEL_CLK_DIVIDER_PCD1;
+		tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);
+	}
 
 	err = clk_set_rate(dc->clk, state->pclk);
 	if (err < 0)
@@ -1343,39 +1392,66 @@ static void tegra_crtc_atomic_enable(struct drm_crtc *crtc,
 
 	/* initialize display controller */
 	if (dc->syncpt) {
-		u32 syncpt = host1x_syncpt_id(dc->syncpt);
+		u32 syncpt = host1x_syncpt_id(dc->syncpt), enable;
+
+		if (dc->soc->has_nvdisplay)
+			enable = 1 << 31;
+		else
+			enable = 1 << 8;
 
 		value = SYNCPT_CNTRL_NO_STALL;
 		tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
 
-		value = SYNCPT_VSYNC_ENABLE | syncpt;
+		value = enable | syncpt;
 		tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC);
 	}
 
-	value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
-		WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
-	tegra_dc_writel(dc, value, DC_CMD_INT_TYPE);
-
-	value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
-		WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
-	tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY);
+	if (dc->soc->has_nvdisplay) {
+		value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT |
+			DSC_OBUF_UF_INT;
+		tegra_dc_writel(dc, value, DC_CMD_INT_TYPE);
 
-	/* initialize timer */
-	value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) |
-		WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20);
-	tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY);
+		value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT |
+			DSC_OBUF_UF_INT | SD3_BUCKET_WALK_DONE_INT |
+			HEAD_UF_INT | MSF_INT | REG_TMOUT_INT |
+			REGION_CRC_INT | V_PULSE2_INT | V_PULSE3_INT |
+			VBLANK_INT | FRAME_END_INT;
+		tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY);
 
-	value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) |
-		WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1);
-	tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER);
+		value = SD3_BUCKET_WALK_DONE_INT | HEAD_UF_INT | VBLANK_INT |
+			FRAME_END_INT;
+		tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);
 
-	value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
-		WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
-	tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);
+		value = HEAD_UF_INT | REG_TMOUT_INT | FRAME_END_INT;
+		tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
 
-	value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
-		WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
-	tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
+		tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS);
+	} else {
+		value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
+			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
+		tegra_dc_writel(dc, value, DC_CMD_INT_TYPE);
+
+		value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
+			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
+		tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY);
+
+		/* initialize timer */
+		value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) |
+			WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20);
+		tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY);
+
+		value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) |
+			WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1);
+		tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER);
+
+		value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
+			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
+		tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);
+
+		value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
+			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
+		tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
+	}
 
 	if (dc->soc->supports_background_color)
 		tegra_dc_writel(dc, 0, DC_DISP_BLEND_BACKGROUND_COLOR);
@@ -1400,10 +1476,18 @@ static void tegra_crtc_atomic_enable(struct drm_crtc *crtc,
 	value |= DISP_CTRL_MODE_C_DISPLAY;
 	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
 
-	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);
+	if (!dc->soc->has_nvdisplay) {
+		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);
+	}
+
+	/* enable underflow reporting and display red for missing pixels */
+	if (dc->soc->has_nvdisplay) {
+		value = UNDERFLOW_MODE_RED | UNDERFLOW_REPORT_ENABLE;
+		tegra_dc_writel(dc, value, DC_COM_RG_UNDERFLOW);
+	}
 
 	tegra_dc_commit(dc);
 
@@ -1459,9 +1543,15 @@ static void tegra_crtc_atomic_flush(struct drm_crtc *crtc,
 {
 	struct tegra_dc_state *state = to_dc_state(crtc->state);
 	struct tegra_dc *dc = to_tegra_dc(crtc);
+	u32 value;
+
+	value = state->planes << 8 | GENERAL_UPDATE;
+	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
+	value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
 
-	tegra_dc_writel(dc, state->planes << 8, DC_CMD_STATE_CONTROL);
-	tegra_dc_writel(dc, state->planes, DC_CMD_STATE_CONTROL);
+	value = state->planes | GENERAL_ACT_REQ;
+	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
+	value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
 }
 
 static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = {
@@ -1509,6 +1599,11 @@ static irqreturn_t tegra_dc_irq(int irq, void *data)
 		dc->stats.overflow++;
 	}
 
+	if (status & HEAD_UF_INT) {
+		dev_dbg_ratelimited(dc->dev, "%s(): head underflow\n", __func__);
+		dc->stats.underflow++;
+	}
+
 	return IRQ_HANDLED;
 }
 
@@ -1543,7 +1638,11 @@ static int tegra_dc_init(struct host1x_client *client)
 		dc->domain = tegra->domain;
 	}
 
-	primary = tegra_dc_primary_plane_create(drm, dc);
+	if (dc->soc->wgrps)
+		primary = tegra_dc_add_shared_planes(drm, dc);
+	else
+		primary = tegra_dc_add_planes(drm, dc);
+
 	if (IS_ERR(primary)) {
 		err = PTR_ERR(primary);
 		goto cleanup;
@@ -1577,10 +1676,6 @@ static int tegra_dc_init(struct host1x_client *client)
 		goto cleanup;
 	}
 
-	err = tegra_dc_add_planes(drm, dc);
-	if (err < 0)
-		goto cleanup;
-
 	err = devm_request_irq(dc->dev, dc->irq, tegra_dc_irq, 0,
 			       dev_name(dc->dev), dc);
 	if (err < 0) {
@@ -1592,10 +1687,10 @@ static int tegra_dc_init(struct host1x_client *client)
 	return 0;
 
 cleanup:
-	if (cursor)
+	if (!IS_ERR_OR_NULL(cursor))
 		drm_plane_cleanup(cursor);
 
-	if (primary)
+	if (!IS_ERR(primary))
 		drm_plane_cleanup(primary);
 
 	if (group && tegra->domain) {
@@ -1643,6 +1738,7 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = {
 	.pitch_align = 8,
 	.has_powergate = false,
 	.broken_reset = true,
+	.has_nvdisplay = false,
 };
 
 static const struct tegra_dc_soc_info tegra30_dc_soc_info = {
@@ -1653,6 +1749,7 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = {
 	.pitch_align = 8,
 	.has_powergate = false,
 	.broken_reset = false,
+	.has_nvdisplay = false,
 };
 
 static const struct tegra_dc_soc_info tegra114_dc_soc_info = {
@@ -1663,6 +1760,7 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = {
 	.pitch_align = 64,
 	.has_powergate = true,
 	.broken_reset = false,
+	.has_nvdisplay = false,
 };
 
 static const struct tegra_dc_soc_info tegra124_dc_soc_info = {
@@ -1673,6 +1771,7 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = {
 	.pitch_align = 64,
 	.has_powergate = true,
 	.broken_reset = false,
+	.has_nvdisplay = false,
 };
 
 static const struct tegra_dc_soc_info tegra210_dc_soc_info = {
@@ -1683,10 +1782,61 @@ static const struct tegra_dc_soc_info tegra210_dc_soc_info = {
 	.pitch_align = 64,
 	.has_powergate = true,
 	.broken_reset = false,
+	.has_nvdisplay = false,
+};
+
+static const struct tegra_windowgroup_soc tegra186_dc_wgrps[] = {
+	{
+		.index = 0,
+		.dc = 0,
+		.windows = (const unsigned int[]) { 0 },
+		.num_windows = 1,
+	}, {
+		.index = 1,
+		.dc = 1,
+		.windows = (const unsigned int[]) { 1 },
+		.num_windows = 1,
+	}, {
+		.index = 2,
+		.dc = 1,
+		.windows = (const unsigned int[]) { 2 },
+		.num_windows = 1,
+	}, {
+		.index = 3,
+		.dc = 2,
+		.windows = (const unsigned int[]) { 3 },
+		.num_windows = 1,
+	}, {
+		.index = 4,
+		.dc = 2,
+		.windows = (const unsigned int[]) { 4 },
+		.num_windows = 1,
+	}, {
+		.index = 5,
+		.dc = 2,
+		.windows = (const unsigned int[]) { 5 },
+		.num_windows = 1,
+	},
+};
+
+static const struct tegra_dc_soc_info tegra186_dc_soc_info = {
+	.supports_background_color = true,
+	.supports_interlacing = true,
+	.supports_cursor = true,
+	.supports_block_linear = true,
+	.pitch_align = 64,
+	.has_powergate = false,
+	.broken_reset = false,
+	.has_nvdisplay = true,
+	.wgrps = tegra186_dc_wgrps,
+	.num_wgrps = ARRAY_SIZE(tegra186_dc_wgrps),
 };
 
 static const struct of_device_id tegra_dc_of_match[] = {
 	{
+		.compatible = "nvidia,tegra186-dc",
+		.data = &tegra186_dc_soc_info,
+	}, {
 		.compatible = "nvidia,tegra210-dc",
 		.data = &tegra210_dc_soc_info,
 	}, {
drivers/gpu/drm/tegra/dc.h +66 -32

156 lines not on this page · raw message

drivers/gpu/drm/tegra/drm.c +1 -0
index 8d5da4d583dd..edd440504876 100644
@@ -1346,6 +1346,7 @@ static const struct of_device_id host1x_drm_subdevs[] = {
 	{ .compatible = "nvidia,tegra210-sor1", },
 	{ .compatible = "nvidia,tegra210-vic", },
 	{ .compatible = "nvidia,tegra186-display", },
+	{ .compatible = "nvidia,tegra186-dc", },
 	{ .compatible = "nvidia,tegra186-vic", },
 	{ /* sentinel */ }
 };
Signature · 1 line
2.15.0
#8 lore raw

PATCH 9/12 drm/tegra: Support ARGB and ABGR formats

In reply to a message not in the archive

From: Thierry Reding <[email protected]>

These formats can easily be supported on all generations of Tegra.

Note that the XRGB and XBGR formats that we supported were in fact using the ARGB and ABGR Tegra formats. This happened to work in cases where no alpha was being considered. This change is also a fix for those formats.

---
 drivers/gpu/drm/tegra/dc.c    | 4 ++++
 drivers/gpu/drm/tegra/dc.h    | 2 ++
 drivers/gpu/drm/tegra/plane.c | 8 ++++++++
 3 files changed, 14 insertions(+)
drivers/gpu/drm/tegra/dc.c +4 -0
index 2d54657b538e..e40272493235 100644
@@ -287,7 +287,9 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
 
 static const u32 tegra_primary_plane_formats[] = {
 	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_ABGR8888,
 	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
 	DRM_FORMAT_RGB565,
 };
 
@@ -649,7 +651,9 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
 
 static const uint32_t tegra_overlay_plane_formats[] = {
 	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_ABGR8888,
 	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
 	DRM_FORMAT_RGB565,
 	DRM_FORMAT_UYVY,
 	DRM_FORMAT_YUYV,
drivers/gpu/drm/tegra/dc.h +2 -0
index 47f43663adcb..018fea74fb50 100644
@@ -598,6 +598,8 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
 #define WIN_COLOR_DEPTH_YUV422R        23
 #define WIN_COLOR_DEPTH_YCbCr422RA     24
 #define WIN_COLOR_DEPTH_YUV422RA       25
+#define WIN_COLOR_DEPTH_B8G8R8X8       37
+#define WIN_COLOR_DEPTH_R8G8B8X8       38
 
 #define DC_WIN_POSITION				0x704
 #define H_POSITION(x) (((x) & 0x1fff) <<  0) /* XXX 0x7fff on Tegra186 */
drivers/gpu/drm/tegra/plane.c +8 -0
index 1847d0204e46..1fff98fe6962 100644
@@ -111,10 +111,18 @@ int tegra_plane_format(u32 fourcc, u32 *format, u32 *swap)
 
 	switch (fourcc) {
 	case DRM_FORMAT_XBGR8888:
+		*format = WIN_COLOR_DEPTH_R8G8B8X8;
+		break;
+
+	case DRM_FORMAT_ABGR8888:
 		*format = WIN_COLOR_DEPTH_R8G8B8A8;
 		break;
 
 	case DRM_FORMAT_XRGB8888:
+		*format = WIN_COLOR_DEPTH_B8G8R8X8;
+		break;
+
+	case DRM_FORMAT_ARGB8888:
 		*format = WIN_COLOR_DEPTH_B8G8R8A8;
 		break;
 
Signature · 1 line
2.15.0
#9 lore raw

PATCH 10/12 drm/tegra: sor: Parameterize register offsets

In reply to a message not in the archive

From: Thierry Reding <[email protected]>

Future Tegra generations have an increased number of display controllers that can drive individual SORs. In order to support that, the offset and layout of some registers has changed in backwards-incompatible ways. Use parameterized register offsets to support this.

---
 drivers/gpu/drm/tegra/sor.c | 203 +++++++++++++++++++++++++++-----------------
 1 file changed, 127 insertions(+), 76 deletions(-)
drivers/gpu/drm/tegra/sor.c +127 -76
index 2fba6c2bd486..d51399587aca 100644
@@ -144,12 +144,29 @@ static const struct tegra_sor_hdmi_settings tegra210_sor_hdmi_defaults[] = {
 };
 #endif
 
+struct tegra_sor_regs {
+	unsigned int head_state0;
+	unsigned int head_state1;
+	unsigned int head_state2;
+	unsigned int head_state3;
+	unsigned int head_state4;
+	unsigned int head_state5;
+	unsigned int pll0;
+	unsigned int pll1;
+	unsigned int pll2;
+	unsigned int pll3;
+	unsigned int dp_padctl0;
+	unsigned int dp_padctl2;
+};
+
 struct tegra_sor_soc {
 	bool supports_edp;
 	bool supports_lvds;
 	bool supports_hdmi;
 	bool supports_dp;
 
+	const struct tegra_sor_regs *regs;
+
 	const struct tegra_sor_hdmi_settings *settings;
 	unsigned int num_settings;
 
@@ -387,23 +404,23 @@ static int tegra_sor_dp_train_fast(struct tegra_sor *sor,
 	/* disable LVDS mode */
 	tegra_sor_writel(sor, 0, SOR_LVDS);
 
-	value = tegra_sor_readl(sor, SOR_DP_PADCTL0);
+	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
 	value |= SOR_DP_PADCTL_TX_PU_ENABLE;
 	value &= ~SOR_DP_PADCTL_TX_PU_MASK;
 	value |= SOR_DP_PADCTL_TX_PU(2); /* XXX: don't hardcode? */
-	tegra_sor_writel(sor, value, SOR_DP_PADCTL0);
+	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
 
-	value = tegra_sor_readl(sor, SOR_DP_PADCTL0);
+	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
 	value |= SOR_DP_PADCTL_CM_TXD_3 | SOR_DP_PADCTL_CM_TXD_2 |
 		 SOR_DP_PADCTL_CM_TXD_1 | SOR_DP_PADCTL_CM_TXD_0;
-	tegra_sor_writel(sor, value, SOR_DP_PADCTL0);
+	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
 
 	usleep_range(10, 100);
 
-	value = tegra_sor_readl(sor, SOR_DP_PADCTL0);
+	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
 	value &= ~(SOR_DP_PADCTL_CM_TXD_3 | SOR_DP_PADCTL_CM_TXD_2 |
 		   SOR_DP_PADCTL_CM_TXD_1 | SOR_DP_PADCTL_CM_TXD_0);
-	tegra_sor_writel(sor, value, SOR_DP_PADCTL0);
+	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
 
 	err = drm_dp_aux_prepare(sor->aux, DP_SET_ANSI_8B10B);
 	if (err < 0)
@@ -895,31 +912,31 @@ static void tegra_sor_mode_set(struct tegra_sor *sor,
 	 */
 
 	value = ((mode->vtotal & 0x7fff) << 16) | (mode->htotal & 0x7fff);
-	tegra_sor_writel(sor, value, SOR_HEAD_STATE1(dc->pipe));
+	tegra_sor_writel(sor, value, sor->soc->regs->head_state1 + dc->pipe);
 
 	/* sync end = sync width - 1 */
 	vse = mode->vsync_end - mode->vsync_start - 1;
 	hse = mode->hsync_end - mode->hsync_start - 1;
 
 	value = ((vse & 0x7fff) << 16) | (hse & 0x7fff);
-	tegra_sor_writel(sor, value, SOR_HEAD_STATE2(dc->pipe));
+	tegra_sor_writel(sor, value, sor->soc->regs->head_state2 + dc->pipe);
 
 	/* blank end = sync end + back porch */
 	vbe = vse + (mode->vtotal - mode->vsync_end);
 	hbe = hse + (mode->htotal - mode->hsync_end);
 
 	value = ((vbe & 0x7fff) << 16) | (hbe & 0x7fff);
-	tegra_sor_writel(sor, value, SOR_HEAD_STATE3(dc->pipe));
+	tegra_sor_writel(sor, value, sor->soc->regs->head_state3 + dc->pipe);
 
 	/* blank start = blank end + active */
 	vbs = vbe + mode->vdisplay;
 	hbs = hbe + mode->hdisplay;
 
 	value = ((vbs & 0x7fff) << 16) | (hbs & 0x7fff);
-	tegra_sor_writel(sor, value, SOR_HEAD_STATE4(dc->pipe));
+	tegra_sor_writel(sor, value, sor->soc->regs->head_state4 + dc->pipe);
 
 	/* XXX interlacing support */
-	tegra_sor_writel(sor, 0x001, SOR_HEAD_STATE5(dc->pipe));
+	tegra_sor_writel(sor, 0x001, sor->soc->regs->head_state5 + dc->pipe);
 }
 
 static int tegra_sor_detach(struct tegra_sor *sor)
@@ -1001,10 +1018,10 @@ static int tegra_sor_power_down(struct tegra_sor *sor)
 		return err;
 	}
 
-	value = tegra_sor_readl(sor, SOR_DP_PADCTL0);
+	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
 	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_PADCTL0);
+	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
 
 	/* stop lane sequencer */
 	value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_UP |
@@ -1024,20 +1041,20 @@ static int tegra_sor_power_down(struct tegra_sor *sor)
 	if ((value & SOR_LANE_SEQ_CTL_TRIGGER) != 0)
 		return -ETIMEDOUT;
 
-	value = tegra_sor_readl(sor, SOR_PLL2);
+	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
 	value |= SOR_PLL2_PORT_POWERDOWN;
-	tegra_sor_writel(sor, value, SOR_PLL2);
+	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
 
 	usleep_range(20, 100);
 
-	value = tegra_sor_readl(sor, SOR_PLL0);
+	value = tegra_sor_readl(sor, sor->soc->regs->pll0);
 	value |= SOR_PLL0_VCOPD | SOR_PLL0_PWR;
-	tegra_sor_writel(sor, value, SOR_PLL0);
+	tegra_sor_writel(sor, value, sor->soc->regs->pll0);
 
-	value = tegra_sor_readl(sor, SOR_PLL2);
+	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
 	value |= SOR_PLL2_SEQ_PLLCAPPD;
 	value |= SOR_PLL2_SEQ_PLLCAPPD_ENFORCE;
-	tegra_sor_writel(sor, value, SOR_PLL2);
+	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
 
 	usleep_range(20, 100);
 
@@ -1528,40 +1545,40 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder)
 	value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK;
 	tegra_sor_writel(sor, value, SOR_CLK_CNTRL);
 
-	value = tegra_sor_readl(sor, SOR_PLL2);
+	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
 	value &= ~SOR_PLL2_BANDGAP_POWERDOWN;
-	tegra_sor_writel(sor, value, SOR_PLL2);
+	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
 	usleep_range(20, 100);
 
-	value = tegra_sor_readl(sor, SOR_PLL3);
+	value = tegra_sor_readl(sor, sor->soc->regs->pll3);
 	value |= SOR_PLL3_PLL_VDD_MODE_3V3;
-	tegra_sor_writel(sor, value, SOR_PLL3);
+	tegra_sor_writel(sor, value, sor->soc->regs->pll3);
 
 	value = SOR_PLL0_ICHPMP(0xf) | SOR_PLL0_VCOCAP_RST |
 		SOR_PLL0_PLLREG_LEVEL_V45 | SOR_PLL0_RESISTOR_EXT;
-	tegra_sor_writel(sor, value, SOR_PLL0);
+	tegra_sor_writel(sor, value, sor->soc->regs->pll0);
 
-	value = tegra_sor_readl(sor, SOR_PLL2);
+	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
 	value |= SOR_PLL2_SEQ_PLLCAPPD;
 	value &= ~SOR_PLL2_SEQ_PLLCAPPD_ENFORCE;
 	value |= SOR_PLL2_LVDS_ENABLE;
-	tegra_sor_writel(sor, value, SOR_PLL2);
+	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
 
 	value = SOR_PLL1_TERM_COMPOUT | SOR_PLL1_TMDS_TERM;
-	tegra_sor_writel(sor, value, SOR_PLL1);
+	tegra_sor_writel(sor, value, sor->soc->regs->pll1);
 
 	while (true) {
-		value = tegra_sor_readl(sor, SOR_PLL2);
+		value = tegra_sor_readl(sor, sor->soc->regs->pll2);
 		if ((value & SOR_PLL2_SEQ_PLLCAPPD_ENFORCE) == 0)
 			break;
 
 		usleep_range(250, 1000);
 	}
 
-	value = tegra_sor_readl(sor, SOR_PLL2);
+	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
 	value &= ~SOR_PLL2_POWERDOWN_OVERRIDE;
 	value &= ~SOR_PLL2_PORT_POWERDOWN;
-	tegra_sor_writel(sor, value, SOR_PLL2);
+	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
 
 	/*
 	 * power up
@@ -1574,18 +1591,18 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder)
 	tegra_sor_writel(sor, value, SOR_CLK_CNTRL);
 
 	/* step 1 */
-	value = tegra_sor_readl(sor, SOR_PLL2);
+	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
 	value |= SOR_PLL2_SEQ_PLLCAPPD_ENFORCE | SOR_PLL2_PORT_POWERDOWN |
 		 SOR_PLL2_BANDGAP_POWERDOWN;
-	tegra_sor_writel(sor, value, SOR_PLL2);
+	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
 
-	value = tegra_sor_readl(sor, SOR_PLL0);
+	value = tegra_sor_readl(sor, sor->soc->regs->pll0);
 	value |= SOR_PLL0_VCOPD | SOR_PLL0_PWR;
-	tegra_sor_writel(sor, value, SOR_PLL0);
+	tegra_sor_writel(sor, value, sor->soc->regs->pll0);
 
-	value = tegra_sor_readl(sor, SOR_DP_PADCTL0);
+	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
 	value &= ~SOR_DP_PADCTL_PAD_CAL_PD;
-	tegra_sor_writel(sor, value, SOR_DP_PADCTL0);
+	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
 
 	/* step 2 */
 	err = tegra_io_rail_power_on(TEGRA_IO_RAIL_LVDS);
@@ -1595,28 +1612,28 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder)
 	usleep_range(5, 100);
 
 	/* step 3 */
-	value = tegra_sor_readl(sor, SOR_PLL2);
+	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
 	value &= ~SOR_PLL2_BANDGAP_POWERDOWN;
-	tegra_sor_writel(sor, value, SOR_PLL2);
+	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
 
 	usleep_range(20, 100);
 
 	/* step 4 */
-	value = tegra_sor_readl(sor, SOR_PLL0);
+	value = tegra_sor_readl(sor, sor->soc->regs->pll0);
 	value &= ~SOR_PLL0_VCOPD;
 	value &= ~SOR_PLL0_PWR;
-	tegra_sor_writel(sor, value, SOR_PLL0);
+	tegra_sor_writel(sor, value, sor->soc->regs->pll0);
 
-	value = tegra_sor_readl(sor, SOR_PLL2);
+	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
 	value &= ~SOR_PLL2_SEQ_PLLCAPPD_ENFORCE;
-	tegra_sor_writel(sor, value, SOR_PLL2);
+	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
 
 	usleep_range(200, 1000);
 
 	/* step 5 */
-	value = tegra_sor_readl(sor, SOR_PLL2);
+	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
 	value &= ~SOR_PLL2_PORT_POWERDOWN;
-	tegra_sor_writel(sor, value, SOR_PLL2);
+	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
 
 	/* XXX not in TRM */
 	for (value = 0, i = 0; i < 5; i++)
@@ -1632,7 +1649,7 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder)
 		dev_err(sor->dev, "failed to set parent clock: %d\n", err);
 
 	/* power DP lanes */
-	value = tegra_sor_readl(sor, SOR_DP_PADCTL0);
+	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
 
 	if (link.num_lanes <= 2)
 		value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_2);
@@ -1649,7 +1666,7 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder)
 	else
 		value |= SOR_DP_PADCTL_PD_TXD_0;
 
-	tegra_sor_writel(sor, value, SOR_DP_PADCTL0);
+	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
 
 	value = tegra_sor_readl(sor, SOR_DP_LINKCTL0);
 	value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK;
@@ -1693,9 +1710,9 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder)
 	tegra_sor_writel(sor, value, SOR_DP_TPG);
 
 	/* enable pad calibration logic */
-	value = tegra_sor_readl(sor, SOR_DP_PADCTL0);
+	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
 	value |= SOR_DP_PADCTL_PAD_CAL_PD;
-	tegra_sor_writel(sor, value, SOR_DP_PADCTL0);
+	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
 
 	err = drm_dp_link_probe(sor->aux, &link);
 	if (err < 0)
@@ -2017,38 +2034,38 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder)
 
 	usleep_range(20, 100);
 
-	value = tegra_sor_readl(sor, SOR_PLL2);
+	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
 	value &= ~SOR_PLL2_BANDGAP_POWERDOWN;
-	tegra_sor_writel(sor, value, SOR_PLL2);
+	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
 
 	usleep_range(20, 100);
 
-	value = tegra_sor_readl(sor, SOR_PLL3);
+	value = tegra_sor_readl(sor, sor->soc->regs->pll3);
 	value &= ~SOR_PLL3_PLL_VDD_MODE_3V3;
-	tegra_sor_writel(sor, value, SOR_PLL3);
+	tegra_sor_writel(sor, value, sor->soc->regs->pll3);
 
-	value = tegra_sor_readl(sor, SOR_PLL0);
+	value = tegra_sor_readl(sor, sor->soc->regs->pll0);
 	value &= ~SOR_PLL0_VCOPD;
 	value &= ~SOR_PLL0_PWR;
-	tegra_sor_writel(sor, value, SOR_PLL0);
+	tegra_sor_writel(sor, value, sor->soc->regs->pll0);
 
-	value = tegra_sor_readl(sor, SOR_PLL2);
+	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
 	value &= ~SOR_PLL2_SEQ_PLLCAPPD_ENFORCE;
-	tegra_sor_writel(sor, value, SOR_PLL2);
+	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
 
 	usleep_range(200, 400);
 
-	value = tegra_sor_readl(sor, SOR_PLL2);
+	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
 	value &= ~SOR_PLL2_POWERDOWN_OVERRIDE;
 	value &= ~SOR_PLL2_PORT_POWERDOWN;
-	tegra_sor_writel(sor, value, SOR_PLL2);
+	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
 
 	usleep_range(20, 100);
 
-	value = tegra_sor_readl(sor, SOR_DP_PADCTL0);
+	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
 	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_PADCTL0);
+	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
 
 	while (true) {
 		value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL);
@@ -2166,9 +2183,9 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder)
 	tegra_sor_writel(sor, value, SOR_STATE1);
 
 	/* power up pad calibration */
-	value = tegra_sor_readl(sor, SOR_DP_PADCTL0);
+	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
 	value &= ~SOR_DP_PADCTL_PAD_CAL_PD;
-	tegra_sor_writel(sor, value, SOR_DP_PADCTL0);
+	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
 
 	/* production settings */
 	settings = tegra_sor_hdmi_find_settings(sor, mode->clock * 1000);
@@ -2178,24 +2195,24 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder)
 		return;
 	}
 
-	value = tegra_sor_readl(sor, SOR_PLL0);
+	value = tegra_sor_readl(sor, sor->soc->regs->pll0);
 	value &= ~SOR_PLL0_ICHPMP_MASK;
 	value &= ~SOR_PLL0_VCOCAP_MASK;
 	value |= SOR_PLL0_ICHPMP(settings->ichpmp);
 	value |= SOR_PLL0_VCOCAP(settings->vcocap);
-	tegra_sor_writel(sor, value, SOR_PLL0);
+	tegra_sor_writel(sor, value, sor->soc->regs->pll0);
 
 	tegra_sor_dp_term_calibrate(sor);
 
-	value = tegra_sor_readl(sor, SOR_PLL1);
+	value = tegra_sor_readl(sor, sor->soc->regs->pll1);
 	value &= ~SOR_PLL1_LOADADJ_MASK;
 	value |= SOR_PLL1_LOADADJ(settings->loadadj);
-	tegra_sor_writel(sor, value, SOR_PLL1);
+	tegra_sor_writel(sor, value, sor->soc->regs->pll1);
 
-	value = tegra_sor_readl(sor, SOR_PLL3);
+	value = tegra_sor_readl(sor, sor->soc->regs->pll3);
 	value &= ~SOR_PLL3_BG_VREF_LEVEL_MASK;
 	value |= SOR_PLL3_BG_VREF_LEVEL(settings->bg_vref);
-	tegra_sor_writel(sor, value, SOR_PLL3);
+	tegra_sor_writel(sor, value, sor->soc->regs->pll3);
 
 	value = settings->drive_current[0] << 24 |
 		settings->drive_current[1] << 16 |
@@ -2209,16 +2226,16 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder)
 		settings->preemphasis[3] <<  0;
 	tegra_sor_writel(sor, value, SOR_LANE_PREEMPHASIS0);
 
-	value = tegra_sor_readl(sor, SOR_DP_PADCTL0);
+	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
 	value &= ~SOR_DP_PADCTL_TX_PU_MASK;
 	value |= SOR_DP_PADCTL_TX_PU_ENABLE;
 	value |= SOR_DP_PADCTL_TX_PU(settings->tx_pu);
-	tegra_sor_writel(sor, value, SOR_DP_PADCTL0);
+	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
 
 	/* power down pad calibration */
-	value = tegra_sor_readl(sor, SOR_DP_PADCTL0);
+	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
 	value |= SOR_DP_PADCTL_PAD_CAL_PD;
-	tegra_sor_writel(sor, value, SOR_DP_PADCTL0);
+	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
 
 	/* miscellaneous display controller settings */
 	value = VSYNC_H_POSITION(1);
@@ -2250,16 +2267,16 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder)
 		dev_err(sor->dev, "failed to power up SOR: %d\n", err);
 
 	/* configure dynamic range of output */
-	value = tegra_sor_readl(sor, SOR_HEAD_STATE0(dc->pipe));
+	value = tegra_sor_readl(sor, sor->soc->regs->head_state0 + dc->pipe);
 	value &= ~SOR_HEAD_STATE_RANGECOMPRESS_MASK;
 	value &= ~SOR_HEAD_STATE_DYNRANGE_MASK;
-	tegra_sor_writel(sor, value, SOR_HEAD_STATE0(dc->pipe));
+	tegra_sor_writel(sor, value, sor->soc->regs->head_state0 + dc->pipe);
 
 	/* configure colorspace */
-	value = tegra_sor_readl(sor, SOR_HEAD_STATE0(dc->pipe));
+	value = tegra_sor_readl(sor, sor->soc->regs->head_state0 + dc->pipe);
 	value &= ~SOR_HEAD_STATE_COLORSPACE_MASK;
 	value |= SOR_HEAD_STATE_COLORSPACE_RGB;
-	tegra_sor_writel(sor, value, SOR_HEAD_STATE0(dc->pipe));
+	tegra_sor_writel(sor, value, sor->soc->regs->head_state0 + dc->pipe);
 
 	tegra_sor_mode_set(sor, mode, state);
 
@@ -2488,19 +2505,51 @@ static const u8 tegra124_sor_xbar_cfg[5] = {
 	0, 1, 2, 3, 4
 };
 
+static const struct tegra_sor_regs tegra124_sor_regs = {
+	.head_state0 = 0x05,
+	.head_state1 = 0x07,
+	.head_state2 = 0x09,
+	.head_state3 = 0x0b,
+	.head_state4 = 0x0d,
+	.head_state5 = 0x0f,
+	.pll0 = 0x17,
+	.pll1 = 0x18,
+	.pll2 = 0x19,
+	.pll3 = 0x1a,
+	.dp_padctl0 = 0x5c,
+	.dp_padctl2 = 0x73,
+};
+
 static const struct tegra_sor_soc tegra124_sor = {
 	.supports_edp = true,
 	.supports_lvds = true,
 	.supports_hdmi = false,
 	.supports_dp = false,
+	.regs = &tegra124_sor_regs,
 	.xbar_cfg = tegra124_sor_xbar_cfg,
 };
 
+static const struct tegra_sor_regs tegra210_sor_regs = {
+	.head_state0 = 0x05,
+	.head_state1 = 0x07,
+	.head_state2 = 0x09,
+	.head_state3 = 0x0b,
+	.head_state4 = 0x0d,
+	.head_state5 = 0x0f,
+	.pll0 = 0x17,
+	.pll1 = 0x18,
+	.pll2 = 0x19,
+	.pll3 = 0x1a,
+	.dp_padctl0 = 0x5c,
+	.dp_padctl2 = 0x73,
+};
+
 static const struct tegra_sor_soc tegra210_sor = {
 	.supports_edp = true,
 	.supports_lvds = false,
 	.supports_hdmi = false,
 	.supports_dp = false,
+	.regs = &tegra210_sor_regs,
 	.xbar_cfg = tegra124_sor_xbar_cfg,
 };
 
@@ -2514,6 +2563,8 @@ static const struct tegra_sor_soc tegra210_sor1 = {
 	.supports_hdmi = true,
 	.supports_dp = true,
 
+	.regs = &tegra210_sor_regs,
+
 	.num_settings = ARRAY_SIZE(tegra210_sor_hdmi_defaults),
 	.settings = tegra210_sor_hdmi_defaults,
 
Signature · 1 line
2.15.0
#10 lore raw

PATCH 11/12 drm/tegra: sor: Add Tegra186 support

In reply to a message not in the archive

From: Thierry Reding <[email protected]>

The SOR found on Tegra186 is very similar to the one found on Tegra210 and earlier. However, due to some changes in the display architecture, some programming sequences have changed and some register have moved around.

---
 drivers/gpu/drm/tegra/dc.c     |  13 ++
 drivers/gpu/drm/tegra/dc.h     |   5 +-
 drivers/gpu/drm/tegra/drm.c    |   2 +
 drivers/gpu/drm/tegra/drm.h    |   2 +
 drivers/gpu/drm/tegra/output.c |  24 +++
 drivers/gpu/drm/tegra/sor.c    | 479 ++++++++++++++++++++++++++++++-----------
 drivers/gpu/drm/tegra/sor.h    |  12 ++
 7 files changed, 414 insertions(+), 123 deletions(-)
drivers/gpu/drm/tegra/dc.c +13 -0
index e40272493235..bf7fcb93fc17 100644
@@ -54,6 +54,19 @@ static u32 tegra_dc_readl_active(struct tegra_dc *dc, unsigned long offset)
 	return value;
 }
 
+bool tegra_dc_has_output(struct tegra_dc *dc, struct device *dev)
+{
+	struct device_node *np = dc->dev->of_node;
+	struct of_phandle_iterator it;
+	int err;
+
+	of_for_each_phandle(&it, err, np, "nvidia,outputs", NULL, 0)
+		if (it.node == dev->of_node)
+			return true;
+
+	return false;
+}
+
 /*
  * Double-buffered registers have two copies: ASSEMBLY and ACTIVE. When the
  * *_ACT_REQ bits are set the ASSEMBLY copy is latched into the ACTIVE copy.
drivers/gpu/drm/tegra/dc.h +3 -2
index 018fea74fb50..336d2c22f521 100644
@@ -141,6 +141,7 @@ struct tegra_dc_window {
 };
 
 /* from dc.c */
+bool tegra_dc_has_output(struct tegra_dc *dc, struct device *dev);
 void tegra_dc_commit(struct tegra_dc *dc);
 int tegra_dc_state_setup_clock(struct tegra_dc *dc,
 			       struct drm_crtc_state *crtc_state,
@@ -289,10 +290,10 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
 #define HDMI_ENABLE	(1 << 30)
 #define DSI_ENABLE	(1 << 29)
 #define SOR1_TIMING_CYA	(1 << 27)
-#define SOR1_ENABLE	(1 << 26)
-#define SOR_ENABLE	(1 << 25)
 #define CURSOR_ENABLE	(1 << 16)
 
+#define SOR_ENABLE(x)	(1 << (25 + (x)))
+
 #define DC_DISP_DISP_MEM_HIGH_PRIORITY		0x403
 #define CURSOR_THRESHOLD(x)   (((x) & 0x03) << 24)
 #define WINDOW_A_THRESHOLD(x) (((x) & 0x7f) << 16)
drivers/gpu/drm/tegra/drm.c +2 -0
index edd440504876..5d9bfcf65161 100644
@@ -1347,6 +1347,8 @@ static const struct of_device_id host1x_drm_subdevs[] = {
 	{ .compatible = "nvidia,tegra210-vic", },
 	{ .compatible = "nvidia,tegra186-display", },
 	{ .compatible = "nvidia,tegra186-dc", },
+	{ .compatible = "nvidia,tegra186-sor", },
+	{ .compatible = "nvidia,tegra186-sor1", },
 	{ .compatible = "nvidia,tegra186-vic", },
 	{ /* sentinel */ }
 };
drivers/gpu/drm/tegra/drm.h +2 -0
index 4073bad48f14..da3d8c141aee 100644
@@ -165,6 +165,8 @@ int tegra_output_probe(struct tegra_output *output);
 void tegra_output_remove(struct tegra_output *output);
 int tegra_output_init(struct drm_device *drm, struct tegra_output *output);
 void tegra_output_exit(struct tegra_output *output);
+void tegra_output_find_possible_crtcs(struct tegra_output *output,
+				      struct drm_device *drm);
 
 int tegra_output_connector_get_modes(struct drm_connector *connector);
 enum drm_connector_status
drivers/gpu/drm/tegra/output.c +24 -0
index 1cfbacea8113..0ab076819df9 100644
@@ -9,7 +9,9 @@
 
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_panel.h>
+
 #include "drm.h"
+#include "dc.h"
 
 #include <media/cec-notifier.h>
 
@@ -219,3 +221,25 @@ void tegra_output_exit(struct tegra_output *output)
 	if (output->panel)
 		drm_panel_detach(output->panel);
 }
+
+void tegra_output_find_possible_crtcs(struct tegra_output *output,
+				      struct drm_device *drm)
+{
+	struct device *dev = output->dev;
+	struct drm_crtc *crtc;
+	unsigned int mask = 0;
+
+	drm_for_each_crtc(crtc, drm) {
+		struct tegra_dc *dc = to_tegra_dc(crtc);
+
+		if (tegra_dc_has_output(dc, dev))
+			mask |= drm_crtc_mask(crtc);
+	}
+
+	if (mask == 0) {
+		dev_warn(dev, "missing output definition for heads in DT\n");
+		mask = 0x3;
+	}
+
+	output->encoder.possible_crtcs = mask;
+}
drivers/gpu/drm/tegra/sor.c +358 -121

780 lines not on this page · raw message

drivers/gpu/drm/tegra/sor.h +12 -0
index 865c73b48968..e85ffc8d98e4 100644
@@ -89,6 +89,8 @@
 #define SOR_PLL0 0x17
 #define  SOR_PLL0_ICHPMP_MASK			(0xf << 24)
 #define  SOR_PLL0_ICHPMP(x)			(((x) & 0xf) << 24)
+#define  SOR_PLL0_FILTER_MASK			(0xf << 16)
+#define  SOR_PLL0_FILTER(x)			(((x) & 0xf) << 16)
 #define  SOR_PLL0_VCOCAP_MASK			(0xf << 8)
 #define  SOR_PLL0_VCOCAP(x)			(((x) & 0xf) << 8)
 #define  SOR_PLL0_VCOCAP_RST			SOR_PLL0_VCOCAP(3)
@@ -122,10 +124,16 @@
 #define  SOR_PLL2_SEQ_PLL_PULLDOWN		(1 << 16)
 
 #define SOR_PLL3 0x1a
+#define  SOR_PLL3_BG_TEMP_COEF_MASK		(0xf << 28)
+#define  SOR_PLL3_BG_TEMP_COEF(x)		(((x) & 0xf) << 28)
 #define  SOR_PLL3_BG_VREF_LEVEL_MASK		(0xf << 24)
 #define  SOR_PLL3_BG_VREF_LEVEL(x)		(((x) & 0xf) << 24)
 #define  SOR_PLL3_PLL_VDD_MODE_1V8		(0 << 13)
 #define  SOR_PLL3_PLL_VDD_MODE_3V3		(1 << 13)
+#define  SOR_PLL3_AVDD10_LEVEL_MASK		(0xf << 8)
+#define  SOR_PLL3_AVDD10_LEVEL(x)		(((x) & 0xf) << 8)
+#define  SOR_PLL3_AVDD14_LEVEL_MASK		(0xf << 4)
+#define  SOR_PLL3_AVDD14_LEVEL(x)		(((x) & 0xf) << 4)
 
 #define SOR_CSTM 0x1b
 #define  SOR_CSTM_ROTCLK_MASK			(0xf << 24)
@@ -334,6 +342,10 @@
 #define SOR_DP_LQ_CSTM1 0x70
 #define SOR_DP_LQ_CSTM2 0x71
 
+#define SOR_DP_PADCTL2 0x73
+#define  SOR_DP_PADCTL_SPAREPLL_MASK (0xff << 24)
+#define  SOR_DP_PADCTL_SPAREPLL(x) (((x) & 0xff) << 24)
+
 #define SOR_HDMI_AUDIO_INFOFRAME_CTRL 0x9a
 #define SOR_HDMI_AUDIO_INFOFRAME_STATUS 0x9b
 #define SOR_HDMI_AUDIO_INFOFRAME_HEADER 0x9c
Signature · 1 line
2.15.0
#11 lore raw

PATCH 12/12 drm/tegra: sor: Support HDMI 2.0 modes

In reply to a message not in the archive

From: Thierry Reding <[email protected]>

---
 drivers/gpu/drm/tegra/sor.c | 121 +++++++++++++++++++++++++++++++++++++++++---
 drivers/gpu/drm/tegra/sor.h |   4 ++
 2 files changed, 119 insertions(+), 6 deletions(-)
drivers/gpu/drm/tegra/sor.c +115 -6
index 91fd0a48dcaa..d3cc25fcccdd 100644
@@ -22,6 +22,7 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_dp_helper.h>
 #include <drm/drm_panel.h>
+#include <drm/drm_scdc_helper.h>
 
 #include "dc.h"
 #include "drm.h"
@@ -342,11 +343,16 @@ struct tegra_sor {
 	struct regulator *avdd_io_supply;
 	struct regulator *vdd_pll_supply;
 	struct regulator *hdmi_supply;
+
+	struct delayed_work scdc;
+	bool scdc_enabled;
 };
 
 struct tegra_sor_state {
 	struct drm_connector_state base;
 
+	unsigned int link_speed;
+	unsigned long pclk;
 	unsigned int bpc;
 };
 
@@ -1481,10 +1487,6 @@ static enum drm_mode_status
 tegra_sor_connector_mode_valid(struct drm_connector *connector,
 			       struct drm_display_mode *mode)
 {
-	/* HDMI 2.0 modes are not yet supported */
-	if (mode->clock > 340000)
-		return MODE_NOCLOCK;
-
 	return MODE_OK;
 }
 
@@ -1909,6 +1911,18 @@ tegra_sor_encoder_atomic_check(struct drm_encoder *encoder,
 
 	info = &output->connector.display_info;
 
+	/*
+	 * For HBR2 modes, the SOR brick needs to use the x20 multiplier, so
+	 * the pixel clock must be corrected accordingly.
+	 */
+	if (pclk >= 340000000) {
+		state->link_speed = 20;
+		state->pclk = pclk / 2;
+	} else {
+		state->link_speed = 10;
+		state->pclk = pclk;
+	}
+
 	err = tegra_dc_state_setup_clock(dc, crtc_state, sor->clk_parent,
 					 pclk, 0);
 	if (err < 0) {
@@ -2059,6 +2073,81 @@ tegra_sor_hdmi_find_settings(struct tegra_sor *sor, unsigned long frequency)
 	return NULL;
 }
 
+static void tegra_sor_hdmi_disable_scrambling(struct tegra_sor *sor)
+{
+	u32 value;
+
+	value = tegra_sor_readl(sor, SOR_HDMI2_CTRL);
+	value &= ~SOR_HDMI2_CTRL_CLOCK_MODE_DIV_BY_4;
+	value &= ~SOR_HDMI2_CTRL_SCRAMBLE;
+	tegra_sor_writel(sor, value, SOR_HDMI2_CTRL);
+}
+
+static void tegra_sor_hdmi_scdc_disable(struct tegra_sor *sor)
+{
+	struct i2c_adapter *ddc = sor->output.ddc;
+
+	drm_scdc_set_high_tmds_clock_ratio(ddc, false);
+	drm_scdc_set_scrambling(ddc, false);
+
+	tegra_sor_hdmi_disable_scrambling(sor);
+}
+
+static void tegra_sor_hdmi_scdc_stop(struct tegra_sor *sor)
+{
+	if (sor->scdc_enabled) {
+		cancel_delayed_work_sync(&sor->scdc);
+		tegra_sor_hdmi_scdc_disable(sor);
+	}
+}
+
+static void tegra_sor_hdmi_enable_scrambling(struct tegra_sor *sor)
+{
+	u32 value;
+
+	value = tegra_sor_readl(sor, SOR_HDMI2_CTRL);
+	value |= SOR_HDMI2_CTRL_CLOCK_MODE_DIV_BY_4;
+	value |= SOR_HDMI2_CTRL_SCRAMBLE;
+	tegra_sor_writel(sor, value, SOR_HDMI2_CTRL);
+}
+
+static void tegra_sor_hdmi_scdc_enable(struct tegra_sor *sor)
+{
+	struct i2c_adapter *ddc = sor->output.ddc;
+
+	drm_scdc_set_high_tmds_clock_ratio(ddc, true);
+	drm_scdc_set_scrambling(ddc, true);
+
+	tegra_sor_hdmi_enable_scrambling(sor);
+}
+
+static void tegra_sor_hdmi_scdc_work(struct work_struct *work)
+{
+	struct tegra_sor *sor = container_of(work, struct tegra_sor, scdc.work);
+	struct i2c_adapter *ddc = sor->output.ddc;
+
+	if (!drm_scdc_get_scrambling_status(ddc)) {
+		DRM_DEBUG_KMS("SCDC not scrambled\n");
+		tegra_sor_hdmi_scdc_enable(sor);
+	}
+
+	schedule_delayed_work(&sor->scdc, msecs_to_jiffies(5000));
+}
+
+static void tegra_sor_hdmi_scdc_start(struct tegra_sor *sor)
+{
+	struct drm_scdc *scdc = &sor->output.connector.display_info.hdmi.scdc;
+	struct drm_display_mode *mode;
+
+	mode = &sor->output.encoder.crtc->state->adjusted_mode;
+
+	if (mode->clock >= 340000 && scdc->supported) {
+		schedule_delayed_work(&sor->scdc, msecs_to_jiffies(5000));
+		tegra_sor_hdmi_scdc_enable(sor);
+		sor->scdc_enabled = true;
+	}
+}
+
 static void tegra_sor_hdmi_disable(struct drm_encoder *encoder)
 {
 	struct tegra_output *output = encoder_to_output(encoder);
@@ -2067,6 +2156,8 @@ static void tegra_sor_hdmi_disable(struct drm_encoder *encoder)
 	u32 value;
 	int err;
 
+	tegra_sor_hdmi_scdc_stop(sor);
+
 	err = tegra_sor_detach(sor);
 	if (err < 0)
 		dev_err(sor->dev, "failed to detach SOR: %d\n", err);
@@ -2106,12 +2197,14 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder)
 	struct tegra_sor *sor = to_sor(output);
 	struct tegra_sor_state *state;
 	struct drm_display_mode *mode;
+	unsigned long rate, pclk;
 	unsigned int div, i;
 	u32 value;
 	int err;
 
 	state = to_sor_state(output->connector.state);
 	mode = &encoder->crtc->state->adjusted_mode;
+	pclk = mode->clock * 1000;
 
 	pm_runtime_get_sync(sor->dev);
 
@@ -2187,10 +2280,13 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder)
 	value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK;
 	value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK;
 
-	if (mode->clock < 340000)
+	if (mode->clock < 340000) {
+		DRM_DEBUG_KMS("setting 2.7 GHz link speed\n");
 		value |= SOR_CLK_CNTRL_DP_LINK_SPEED_G2_70;
-	else
+	} else {
+		DRM_DEBUG_KMS("setting 5.4 GHz link speed\n");
 		value |= SOR_CLK_CNTRL_DP_LINK_SPEED_G5_40;
+	}
 
 	value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_PCLK;
 	tegra_sor_writel(sor, value, SOR_CLK_CNTRL);
@@ -2246,6 +2342,15 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder)
 		return;
 	}
 
+	/* adjust clock rate for HDMI 2.0 modes */
+	rate = clk_get_rate(sor->clk_parent);
+
+	if (mode->clock >= 340000)
+		rate /= 2;
+
+	DRM_DEBUG_KMS("setting clock to %lu Hz, mode: %lu Hz\n", rate, pclk);
+
+	clk_set_rate(sor->clk, rate);
 
 	if (!sor->soc->has_nvdisplay) {
 		value = SOR_INPUT_CONTROL_HDMI_SRC_SELECT(dc->pipe);
@@ -2457,6 +2562,8 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder)
 	err = tegra_sor_wakeup(sor);
 	if (err < 0)
 		dev_err(sor->dev, "failed to wakeup SOR: %d\n", err);
+
+	tegra_sor_hdmi_scdc_start(sor);
 }
 
 static const struct drm_encoder_helper_funcs tegra_sor_hdmi_helpers = {
@@ -2644,6 +2751,8 @@ static int tegra_sor_hdmi_probe(struct tegra_sor *sor)
 		return err;
 	}
 
+	INIT_DELAYED_WORK(&sor->scdc, tegra_sor_hdmi_scdc_work);
+
 	return 0;
 }
 
drivers/gpu/drm/tegra/sor.h +4 -0
index e85ffc8d98e4..fb0854d92a27 100644
@@ -382,4 +382,8 @@
 #define SOR_HDMI_VSI_INFOFRAME_STATUS 0x124
 #define SOR_HDMI_VSI_INFOFRAME_HEADER 0x125
 
+#define SOR_HDMI2_CTRL 0x13e
+#define  SOR_HDMI2_CTRL_CLOCK_MODE_DIV_BY_4 (1 << 1)
+#define  SOR_HDMI2_CTRL_SCRAMBLE (1 << 0)
+
 #endif
Signature · 1 line
2.15.0
#12 lore raw

PATCH 4/12 drm/tegra: dc: Remove duplicate plane funcs

In reply to Thierry Reding

From: Thierry Reding <[email protected]>

Both tegra_overlay_plane_funcs is identical to tegra_plane_funcs. Get rid of the duplicate and use one set of function pointers for all planes.

---
 drivers/gpu/drm/tegra/dc.c | 11 +----------
 1 file changed, 1 insertion(+), 10 deletions(-)
drivers/gpu/drm/tegra/dc.c +1 -10
index fdffab7e41b9..1075b4e06834 100644
@@ -844,15 +844,6 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
 	return &plane->base;
 }
 
-static const struct drm_plane_funcs tegra_overlay_plane_funcs = {
-	.update_plane = drm_atomic_helper_update_plane,
-	.disable_plane = drm_atomic_helper_disable_plane,
-	.destroy = tegra_plane_destroy,
-	.reset = tegra_plane_reset,
-	.atomic_duplicate_state = tegra_plane_atomic_duplicate_state,
-	.atomic_destroy_state = tegra_plane_atomic_destroy_state,
-};
-
 static const uint32_t tegra_overlay_plane_formats[] = {
 	DRM_FORMAT_XBGR8888,
 	DRM_FORMAT_XRGB8888,
@@ -882,7 +873,7 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm,
 	formats = tegra_overlay_plane_formats;
 
 	err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe,
-				       &tegra_overlay_plane_funcs, formats,
+				       &tegra_plane_funcs, formats,
 				       num_formats, NULL,
 				       DRM_PLANE_TYPE_OVERLAY, NULL);
 	if (err < 0) {
Signature · 6 lines
2.15.0

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel
#13 lore raw

PATCH 7/12 drm/tegra: Add Tegra186 display hub support

In reply to Thierry Reding

From: Thierry Reding <[email protected]>

The display architecture has changed in several significant ways with the new Tegra186 SoC. Shared between all display controllers is a set of common resources referred to as the display hub. The hub generates accesses to memory and feeds them into various composition pipelines, each of which being a window that can be assigned to arbitrary heads.

Atomic state is subclassed in order to track the global bandwidth requirements and select and adjust the hub clocks appropriately. The plane code is shared to a large degree with earlier SoC generations, except where the programming differs.

---
 drivers/gpu/drm/tegra/Makefile |   1 +
 drivers/gpu/drm/tegra/dc.c     |  31 ++
 drivers/gpu/drm/tegra/dc.h     | 114 ++++++
 drivers/gpu/drm/tegra/drm.c    |  67 +++-
 drivers/gpu/drm/tegra/drm.h    |  19 +
 drivers/gpu/drm/tegra/hub.c    | 808 +++++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/tegra/hub.h    |  82 +++++
 drivers/gpu/drm/tegra/plane.h  |   2 +
 8 files changed, 1121 insertions(+), 3 deletions(-)
 create mode 100644 drivers/gpu/drm/tegra/hub.c
 create mode 100644 drivers/gpu/drm/tegra/hub.h
drivers/gpu/drm/tegra/Makefile +1 -0
index a47784765217..2e0d6213f6bc 100644
@@ -5,6 +5,7 @@ tegra-drm-y := \
 	drm.o \
 	gem.o \
 	fb.o \
+	hub.o \
 	plane.o \
 	dc.o \
 	output.o \
drivers/gpu/drm/tegra/dc.c +31 -0
index d2c7a3481022..0ba1bf7f7615 100644
@@ -465,6 +465,15 @@ static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm,
 	num_formats = ARRAY_SIZE(tegra_primary_plane_formats);
 	formats = tegra_primary_plane_formats;
 
+	/*
+	 * XXX compute offset so that we can directly access windows.
+	 *
+	 * Always use window A as primary window.
+	 */
+	plane->offset = 0;
+	plane->index = 0;
+	plane->depth = 255;
+
 	err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
 				       &tegra_plane_funcs, formats,
 				       num_formats, NULL,
@@ -660,7 +669,10 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm,
 	if (!plane)
 		return ERR_PTR(-ENOMEM);
 
+	/* XXX compute offset so that we can directly access windows */
+	plane->offset = 0;
 	plane->index = index;
+	plane->depth = 0;
 
 	num_formats = ARRAY_SIZE(tegra_overlay_plane_formats);
 	formats = tegra_overlay_plane_formats;
@@ -1401,6 +1413,25 @@ static void tegra_crtc_atomic_enable(struct drm_crtc *crtc,
 static int tegra_crtc_atomic_check(struct drm_crtc *crtc,
 				   struct drm_crtc_state *state)
 {
+	struct tegra_atomic_state *s = to_tegra_atomic_state(state->state);
+	struct tegra_dc_state *tegra = to_dc_state(state);
+
+	/*
+	 * The display hub display clock needs to be fed by the display clock
+	 * with the highest frequency to ensure proper functioning of all the
+	 * displays.
+	 *
+	 * Note that this isn't used before Tegra186, but it doesn't hurt and
+	 * conditionalizing it would make the code less clean.
+	 */
+	if (state->active) {
+		if (!s->clk_disp || tegra->pclk > s->rate) {
+			s->dc = to_tegra_dc(crtc);
+			s->clk_disp = s->dc->clk;
+			s->rate = tegra->pclk;
+		}
+	}
+
 	return 0;
 }
 
drivers/gpu/drm/tegra/dc.h +114 -0

134 lines not on this page · raw message

drivers/gpu/drm/tegra/drm.c +64 -3

115 lines not on this page · raw message

drivers/gpu/drm/tegra/drm.h +19 -0
index 0da21cc207d2..4073bad48f14 100644
@@ -16,6 +16,7 @@
 #include <linux/of_gpio.h>
 
 #include <drm/drmP.h>
+#include <drm/drm_atomic.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_encoder.h>
@@ -23,6 +24,7 @@
 #include <drm/drm_fixed.h>
 
 #include "gem.h"
+#include "hub.h"
 #include "trace.h"
 
 struct reset_control;
@@ -40,6 +42,20 @@ struct tegra_fbdev {
 };
 #endif
 
+struct tegra_atomic_state {
+	struct drm_atomic_state base;
+
+	struct clk *clk_disp;
+	struct tegra_dc *dc;
+	unsigned long rate;
+};
+
+static inline struct tegra_atomic_state *
+to_tegra_atomic_state(struct drm_atomic_state *state)
+{
+	return container_of(state, struct tegra_atomic_state, base);
+}
+
 struct tegra_drm {
 	struct drm_device *drm;
 
@@ -63,6 +79,8 @@ struct tegra_drm {
 
 	unsigned int pitch_align;
 
+	struct tegra_display_hub *hub;
+
 	struct drm_atomic_state *state;
 };
 
@@ -188,6 +206,7 @@ void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev);
 void tegra_fb_output_poll_changed(struct drm_device *drm);
 #endif
 
+extern struct platform_driver tegra_display_hub_driver;
 extern struct platform_driver tegra_dc_driver;
 extern struct platform_driver tegra_hdmi_driver;
 extern struct platform_driver tegra_dsi_driver;
drivers/gpu/drm/tegra/hub.c new file +808 -0

808 lines not on this page · raw message

drivers/gpu/drm/tegra/hub.h new file +82 -0

82 lines not on this page · raw message

drivers/gpu/drm/tegra/plane.h +2 -0
index 8237b885acd7..fc7566f630fa 100644
@@ -15,7 +15,9 @@ struct tegra_bo;
 
 struct tegra_plane {
 	struct drm_plane base;
+	unsigned int offset;
 	unsigned int index;
+	unsigned int depth;
 };
 
 struct tegra_cursor {
Signature · 6 lines
2.15.0

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel