diff options
Diffstat (limited to 'drivers/gpu/drm/tegra')
-rw-r--r-- | drivers/gpu/drm/tegra/Makefile | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/dc.c | 1914 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/dc.h | 289 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/dpaux.c | 120 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/drm.c | 177 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/drm.h | 30 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/dsi.c | 228 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/fb.c | 50 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/gem.c | 15 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/hdmi.c | 504 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/hub.c | 806 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/hub.h | 81 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/output.c | 25 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/plane.c | 383 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/plane.h | 70 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/sor.c | 1117 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/sor.h | 16 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/vic.c | 20 |
18 files changed, 4019 insertions, 1828 deletions
diff --git a/drivers/gpu/drm/tegra/Makefile b/drivers/gpu/drm/tegra/Makefile index 46d65d39214d..2e0d6213f6bc 100644 --- a/drivers/gpu/drm/tegra/Makefile +++ b/drivers/gpu/drm/tegra/Makefile @@ -5,6 +5,8 @@ tegra-drm-y := \ drm.o \ gem.o \ fb.o \ + hub.o \ + plane.o \ dc.o \ output.o \ rgb.o \ diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 24a5ef4f5bb8..b8403ed48285 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -19,82 +19,79 @@ #include "dc.h" #include "drm.h" #include "gem.h" +#include "hub.h" +#include "plane.h" #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_plane_helper.h> -struct tegra_plane { - struct drm_plane base; - unsigned int index; -}; - -static inline struct tegra_plane *to_tegra_plane(struct drm_plane *plane) +static void tegra_dc_stats_reset(struct tegra_dc_stats *stats) { - return container_of(plane, struct tegra_plane, base); + stats->frames = 0; + stats->vblank = 0; + stats->underflow = 0; + stats->overflow = 0; } -struct tegra_dc_state { - struct drm_crtc_state base; +/* Reads the active copy of a register. */ +static u32 tegra_dc_readl_active(struct tegra_dc *dc, unsigned long offset) +{ + u32 value; - struct clk *clk; - unsigned long pclk; - unsigned int div; + tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); + value = tegra_dc_readl(dc, offset); + tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); - u32 planes; -}; + return value; +} -static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state) +static inline unsigned int tegra_plane_offset(struct tegra_plane *plane, + unsigned int offset) { - if (state) - return container_of(state, struct tegra_dc_state, base); - - return NULL; -} + if (offset >= 0x500 && offset <= 0x638) { + offset = 0x000 + (offset - 0x500); + return plane->offset + offset; + } -struct tegra_plane_state { - struct drm_plane_state base; + if (offset >= 0x700 && offset <= 0x719) { + offset = 0x180 + (offset - 0x700); + return plane->offset + offset; + } - struct tegra_bo_tiling tiling; - u32 format; - u32 swap; -}; + if (offset >= 0x800 && offset <= 0x839) { + offset = 0x1c0 + (offset - 0x800); + return plane->offset + offset; + } -static inline struct tegra_plane_state * -to_tegra_plane_state(struct drm_plane_state *state) -{ - if (state) - return container_of(state, struct tegra_plane_state, base); + dev_WARN(plane->dc->dev, "invalid offset: %x\n", offset); - return NULL; + return plane->offset + offset; } -static void tegra_dc_stats_reset(struct tegra_dc_stats *stats) +static inline u32 tegra_plane_readl(struct tegra_plane *plane, + unsigned int offset) { - stats->frames = 0; - stats->vblank = 0; - stats->underflow = 0; - stats->overflow = 0; + return tegra_dc_readl(plane->dc, tegra_plane_offset(plane, offset)); } -/* - * Reads the active copy of a register. This takes the dc->lock spinlock to - * prevent races with the VBLANK processing which also needs access to the - * active copy of some registers. - */ -static u32 tegra_dc_readl_active(struct tegra_dc *dc, unsigned long offset) +static inline void tegra_plane_writel(struct tegra_plane *plane, u32 value, + unsigned int offset) { - unsigned long flags; - u32 value; + tegra_dc_writel(plane->dc, value, tegra_plane_offset(plane, offset)); +} - spin_lock_irqsave(&dc->lock, flags); +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; - tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); - value = tegra_dc_readl(dc, offset); - tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); + of_for_each_phandle(&it, err, np, "nvidia,outputs", NULL, 0) + if (it.node == dev->of_node) + return true; - spin_unlock_irqrestore(&dc->lock, flags); - return value; + return false; } /* @@ -115,81 +112,6 @@ void tegra_dc_commit(struct tegra_dc *dc) tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); } -static int tegra_dc_format(u32 fourcc, u32 *format, u32 *swap) -{ - /* assume no swapping of fetched data */ - if (swap) - *swap = BYTE_SWAP_NOSWAP; - - switch (fourcc) { - case DRM_FORMAT_XBGR8888: - *format = WIN_COLOR_DEPTH_R8G8B8A8; - break; - - case DRM_FORMAT_XRGB8888: - *format = WIN_COLOR_DEPTH_B8G8R8A8; - break; - - case DRM_FORMAT_RGB565: - *format = WIN_COLOR_DEPTH_B5G6R5; - break; - - case DRM_FORMAT_UYVY: - *format = WIN_COLOR_DEPTH_YCbCr422; - break; - - case DRM_FORMAT_YUYV: - if (swap) - *swap = BYTE_SWAP_SWAP2; - - *format = WIN_COLOR_DEPTH_YCbCr422; - break; - - case DRM_FORMAT_YUV420: - *format = WIN_COLOR_DEPTH_YCbCr420P; - break; - - case DRM_FORMAT_YUV422: - *format = WIN_COLOR_DEPTH_YCbCr422P; - break; - - default: - return -EINVAL; - } - - return 0; -} - -static bool tegra_dc_format_is_yuv(unsigned int format, bool *planar) -{ - switch (format) { - case WIN_COLOR_DEPTH_YCbCr422: - case WIN_COLOR_DEPTH_YUV422: - if (planar) - *planar = false; - - return true; - - case WIN_COLOR_DEPTH_YCbCr420P: - case WIN_COLOR_DEPTH_YUV420P: - case WIN_COLOR_DEPTH_YCbCr422P: - case WIN_COLOR_DEPTH_YUV422P: - case WIN_COLOR_DEPTH_YCbCr422R: - case WIN_COLOR_DEPTH_YUV422R: - case WIN_COLOR_DEPTH_YCbCr422RA: - case WIN_COLOR_DEPTH_YUV422RA: - if (planar) - *planar = true; - - return true; - } - - if (planar) - *planar = false; - - return false; -} - static inline u32 compute_dda_inc(unsigned int in, unsigned int out, bool v, unsigned int bpp) { @@ -230,36 +152,104 @@ static inline u32 compute_initial_dda(unsigned int in) return dfixed_frac(inf); } -static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, +static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane) +{ + u32 background[3] = { + BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE, + BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE, + BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE, + }; + u32 foreground = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255) | + BLEND_COLOR_KEY_NONE; + u32 blendnokey = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255); + struct tegra_plane_state *state; + unsigned int i; + + state = to_tegra_plane_state(plane->base.state); + + /* alpha contribution is 1 minus sum of overlapping windows */ + for (i = 0; i < 3; i++) { + if (state->dependent[i]) + background[i] |= BLEND_CONTROL_DEPENDENT; + } + + /* enable alpha blending if pixel format has an alpha component */ + if (!state->opaque) + foreground |= BLEND_CONTROL_ALPHA; + + /* + * Disable blending and assume Window A is the bottom-most window, + * Window C is the top-most window and Window B is in the middle. + */ + tegra_plane_writel(plane, blendnokey, DC_WIN_BLEND_NOKEY); + tegra_plane_writel(plane, foreground, DC_WIN_BLEND_1WIN); + + switch (plane->index) { + case 0: + tegra_plane_writel(plane, background[0], DC_WIN_BLEND_2WIN_X); + tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y); + tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY); + break; + + case 1: + tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_X); + tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y); + tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY); + break; + + case 2: + tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_X); + tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_Y); + tegra_plane_writel(plane, foreground, DC_WIN_BLEND_3WIN_XY); + break; + } +} + +static void tegra_plane_setup_blending(struct tegra_plane *plane, + const struct tegra_dc_window *window) +{ + u32 value; + + value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 | + BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC | + BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC; + tegra_plane_writel(plane, value, DC_WIN_BLEND_MATCH_SELECT); + + value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 | + BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC | + BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC; + tegra_plane_writel(plane, value, DC_WIN_BLEND_NOMATCH_SELECT); + + value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(255 - window->zpos); + tegra_plane_writel(plane, value, DC_WIN_BLEND_LAYER_CONTROL); +} + +static void tegra_dc_setup_window(struct tegra_plane *plane, const struct tegra_dc_window *window) { unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp; - unsigned long value, flags; + struct tegra_dc *dc = plane->dc; bool yuv, planar; + u32 value; /* * For YUV planar modes, the number of bytes per pixel takes into * account only the luma component and therefore is 1. */ - yuv = tegra_dc_format_is_yuv(window->format, &planar); + yuv = tegra_plane_format_is_yuv(window->format, &planar); if (!yuv) bpp = window->bits_per_pixel / 8; else bpp = planar ? 1 : 2; - spin_lock_irqsave(&dc->lock, flags); - - value = WINDOW_A_SELECT << index; - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER); - - tegra_dc_writel(dc, window->format, DC_WIN_COLOR_DEPTH); - tegra_dc_writel(dc, window->swap, DC_WIN_BYTE_SWAP); + tegra_plane_writel(plane, window->format, DC_WIN_COLOR_DEPTH); + tegra_plane_writel(plane, window->swap, DC_WIN_BYTE_SWAP); value = V_POSITION(window->dst.y) | H_POSITION(window->dst.x); - tegra_dc_writel(dc, value, DC_WIN_POSITION); + tegra_plane_writel(plane, value, DC_WIN_POSITION); value = V_SIZE(window->dst.h) | H_SIZE(window->dst.w); - tegra_dc_writel(dc, value, DC_WIN_SIZE); + tegra_plane_writel(plane, value, DC_WIN_SIZE); h_offset = window->src.x * bpp; v_offset = window->src.y; @@ -267,7 +257,7 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, v_size = window->src.h; value = V_PRESCALED_SIZE(v_size) | H_PRESCALED_SIZE(h_size); - tegra_dc_writel(dc, value, DC_WIN_PRESCALED_SIZE); + tegra_plane_writel(plane, value, DC_WIN_PRESCALED_SIZE); /* * For DDA computations the number of bytes per pixel for YUV planar @@ -280,33 +270,33 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, v_dda = compute_dda_inc(window->src.h, window->dst.h, true, bpp); value = V_DDA_INC(v_dda) | H_DDA_INC(h_dda); - tegra_dc_writel(dc, value, DC_WIN_DDA_INC); + tegra_plane_writel(plane, value, DC_WIN_DDA_INC); h_dda = compute_initial_dda(window->src.x); v_dda = compute_initial_dda(window->src.y); - tegra_dc_writel(dc, h_dda, DC_WIN_H_INITIAL_DDA); - tegra_dc_writel(dc, v_dda, DC_WIN_V_INITIAL_DDA); + tegra_plane_writel(plane, h_dda, DC_WIN_H_INITIAL_DDA); + tegra_plane_writel(plane, v_dda, DC_WIN_V_INITIAL_DDA); - tegra_dc_writel(dc, 0, DC_WIN_UV_BUF_STRIDE); - tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE); + tegra_plane_writel(plane, 0, DC_WIN_UV_BUF_STRIDE); + tegra_plane_writel(plane, 0, DC_WIN_BUF_STRIDE); - tegra_dc_writel(dc, window->base[0], DC_WINBUF_START_ADDR); + tegra_plane_writel(plane, window->base[0], DC_WINBUF_START_ADDR); if (yuv && planar) { - tegra_dc_writel(dc, window->base[1], DC_WINBUF_START_ADDR_U); - tegra_dc_writel(dc, window->base[2], DC_WINBUF_START_ADDR_V); + tegra_plane_writel(plane, window->base[1], DC_WINBUF_START_ADDR_U); + tegra_plane_writel(plane, window->base[2], DC_WINBUF_START_ADDR_V); value = window->stride[1] << 16 | window->stride[0]; - tegra_dc_writel(dc, value, DC_WIN_LINE_STRIDE); + tegra_plane_writel(plane, value, DC_WIN_LINE_STRIDE); } else { - tegra_dc_writel(dc, window->stride[0], DC_WIN_LINE_STRIDE); + tegra_plane_writel(plane, window->stride[0], DC_WIN_LINE_STRIDE); } if (window->bottom_up) v_offset += window->src.h - 1; - tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET); - tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET); + tegra_plane_writel(plane, h_offset, DC_WINBUF_ADDR_H_OFFSET); + tegra_plane_writel(plane, v_offset, DC_WINBUF_ADDR_V_OFFSET); if (dc->soc->supports_block_linear) { unsigned long height = window->tiling.value; @@ -326,7 +316,7 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, break; } - tegra_dc_writel(dc, value, DC_WINBUF_SURFACE_KIND); + tegra_plane_writel(plane, value, DC_WINBUF_SURFACE_KIND); } else { switch (window->tiling.mode) { case TEGRA_BO_TILING_MODE_PITCH: @@ -347,21 +337,21 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, break; } - tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE); + tegra_plane_writel(plane, value, DC_WIN_BUFFER_ADDR_MODE); } value = WIN_ENABLE; if (yuv) { /* setup default colorspace conversion coefficients */ - tegra_dc_writel(dc, 0x00f0, DC_WIN_CSC_YOF); - tegra_dc_writel(dc, 0x012a, DC_WIN_CSC_KYRGB); - tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KUR); - tegra_dc_writel(dc, 0x0198, DC_WIN_CSC_KVR); - tegra_dc_writel(dc, 0x039b, DC_WIN_CSC_KUG); - tegra_dc_writel(dc, 0x032f, DC_WIN_CSC_KVG); - tegra_dc_writel(dc, 0x0204, DC_WIN_CSC_KUB); - tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KVB); + tegra_plane_writel(plane, 0x00f0, DC_WIN_CSC_YOF); + tegra_plane_writel(plane, 0x012a, DC_WIN_CSC_KYRGB); + tegra_plane_writel(plane, 0x0000, DC_WIN_CSC_KUR); + tegra_plane_writel(plane, 0x0198, DC_WIN_CSC_KVR); + tegra_plane_writel(plane, 0x039b, DC_WIN_CSC_KUG); + tegra_plane_writel(plane, 0x032f, DC_WIN_CSC_KVG); + tegra_plane_writel(plane, 0x0204, DC_WIN_CSC_KUB); + tegra_plane_writel(plane, 0x0000, DC_WIN_CSC_KVB); value |= CSC_ENABLE; } else if (window->bits_per_pixel < 24) { @@ -371,137 +361,74 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, if (window->bottom_up) value |= V_DIRECTION; - tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS); - - /* - * Disable blending and assume Window A is the bottom-most window, - * Window C is the top-most window and Window B is in the middle. - */ - tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_NOKEY); - tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_1WIN); - - switch (index) { - case 0: - tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_X); - tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y); - tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY); - break; - - case 1: - tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X); - tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y); - tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY); - break; - - case 2: - tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X); - tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_Y); - tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_3WIN_XY); - break; - } - - spin_unlock_irqrestore(&dc->lock, flags); -} + tegra_plane_writel(plane, value, DC_WIN_WIN_OPTIONS); -static void tegra_plane_destroy(struct drm_plane *plane) -{ - struct tegra_plane *p = to_tegra_plane(plane); - - drm_plane_cleanup(plane); - kfree(p); + if (dc->soc->supports_blending) + tegra_plane_setup_blending(plane, window); + else + tegra_plane_setup_blending_legacy(plane); } -static const u32 tegra_primary_plane_formats[] = { +static const u32 tegra20_primary_formats[] = { + DRM_FORMAT_ARGB4444, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGB565, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_ARGB8888, + /* non-native formats */ + DRM_FORMAT_XRGB1555, + DRM_FORMAT_RGBX5551, DRM_FORMAT_XBGR8888, DRM_FORMAT_XRGB8888, - 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; - - if (plane->state) - __drm_atomic_helper_plane_destroy_state(plane->state); - - kfree(plane->state); - plane->state = NULL; - - state = kzalloc(sizeof(*state), GFP_KERNEL); - if (state) { - plane->state = &state->base; - plane->state->plane = plane; - } -} - -static struct drm_plane_state *tegra_plane_atomic_duplicate_state(struct drm_plane *plane) -{ - struct tegra_plane_state *state = to_tegra_plane_state(plane->state); - struct tegra_plane_state *copy; - - copy = kmalloc(sizeof(*copy), GFP_KERNEL); - if (!copy) - return NULL; - - __drm_atomic_helper_plane_duplicate_state(plane, ©->base); - copy->tiling = state->tiling; - copy->format = state->format; - copy->swap = state->swap; - - return ©->base; -} - -static void tegra_plane_atomic_destroy_state(struct drm_plane *plane, - struct drm_plane_state *state) -{ - __drm_atomic_helper_plane_destroy_state(state); - kfree(state); -} - -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, - .reset = tegra_plane_reset, - .atomic_duplicate_state = tegra_plane_atomic_duplicate_state, - .atomic_destroy_state = tegra_plane_atomic_destroy_state, +static const u32 tegra114_primary_formats[] = { + DRM_FORMAT_ARGB4444, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGB565, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_ARGB8888, + /* new on Tegra114 */ + DRM_FORMAT_ABGR4444, + DRM_FORMAT_ABGR1555, + DRM_FORMAT_BGRA5551, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_RGBX5551, + DRM_FORMAT_XBGR1555, + DRM_FORMAT_BGRX5551, + DRM_FORMAT_BGR565, + DRM_FORMAT_BGRA8888, + DRM_FORMAT_RGBA8888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, }; -static int tegra_plane_state_add(struct tegra_plane *plane, - struct drm_plane_state *state) -{ - struct drm_crtc_state *crtc_state; - struct tegra_dc_state *tegra; - struct drm_rect clip; - int err; - - /* Propagate errors from allocation or locking failures. */ - crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); - if (IS_ERR(crtc_state)) - return PTR_ERR(crtc_state); - - clip.x1 = 0; - clip.y1 = 0; - clip.x2 = crtc_state->mode.hdisplay; - clip.y2 = crtc_state->mode.vdisplay; - - /* Check plane state for visibility and calculate clipping bounds */ - err = drm_plane_helper_check_state(state, &clip, 0, INT_MAX, - true, true); - if (err < 0) - return err; - - tegra = to_dc_state(crtc_state); - - tegra->planes |= WIN_A_ACT_REQ << plane->index; - - return 0; -} +static const u32 tegra124_primary_formats[] = { + DRM_FORMAT_ARGB4444, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGB565, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_ARGB8888, + /* new on Tegra114 */ + DRM_FORMAT_ABGR4444, + DRM_FORMAT_ABGR1555, + DRM_FORMAT_BGRA5551, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_RGBX5551, + DRM_FORMAT_XBGR1555, + DRM_FORMAT_BGRX5551, + DRM_FORMAT_BGR565, + DRM_FORMAT_BGRA8888, + DRM_FORMAT_RGBA8888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + /* new on Tegra124 */ + DRM_FORMAT_RGBX8888, + DRM_FORMAT_BGRX8888, +}; static int tegra_plane_atomic_check(struct drm_plane *plane, struct drm_plane_state *state) @@ -510,17 +437,40 @@ static int tegra_plane_atomic_check(struct drm_plane *plane, struct tegra_bo_tiling *tiling = &plane_state->tiling; struct tegra_plane *tegra = to_tegra_plane(plane); struct tegra_dc *dc = to_tegra_dc(state->crtc); + unsigned int format; int err; /* no need for further checks if the plane is being disabled */ if (!state->crtc) return 0; - err = tegra_dc_format(state->fb->format->format, &plane_state->format, - &plane_state->swap); + err = tegra_plane_format(state->fb->format->format, &format, + &plane_state->swap); if (err < 0) return err; + /* + * Tegra20 and Tegra30 are special cases here because they support + * only variants of specific formats with an alpha component, but not + * the corresponding opaque formats. However, the opaque formats can + * be emulated by disabling alpha blending for the plane. + */ + if (!dc->soc->supports_blending) { + if (!tegra_plane_format_has_alpha(format)) { + err = tegra_plane_format_get_alpha(format, &format); + if (err < 0) + return err; + + plane_state->opaque = true; + } else { + plane_state->opaque = false; + } + + tegra_plane_check_dependent(tegra, plane_state); + } + + plane_state->format = format; + err = tegra_fb_get_tiling(state->fb, tiling); if (err < 0) return err; @@ -553,32 +503,22 @@ static int tegra_plane_atomic_check(struct drm_plane *plane, static void tegra_plane_atomic_disable(struct drm_plane *plane, struct drm_plane_state *old_state) { - struct tegra_dc *dc = to_tegra_dc(old_state->crtc); struct tegra_plane *p = to_tegra_plane(plane); - unsigned long flags; u32 value; /* rien ne va plus */ if (!old_state || !old_state->crtc) return; - spin_lock_irqsave(&dc->lock, flags); - - value = WINDOW_A_SELECT << p->index; - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER); - - value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS); + value = tegra_plane_readl(p, DC_WIN_WIN_OPTIONS); value &= ~WIN_ENABLE; - tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS); - - spin_unlock_irqrestore(&dc->lock, flags); + tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS); } static void tegra_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *old_state) { struct tegra_plane_state *state = to_tegra_plane_state(plane->state); - struct tegra_dc *dc = to_tegra_dc(plane->state->crtc); struct drm_framebuffer *fb = plane->state->fb; struct tegra_plane *p = to_tegra_plane(plane); struct tegra_dc_window window; @@ -604,6 +544,7 @@ static void tegra_plane_atomic_update(struct drm_plane *plane, window.bottom_up = tegra_fb_is_bottom_up(fb); /* copy from state */ + window.zpos = plane->state->normalized_zpos; window.tiling = state->tiling; window.format = state->format; window.swap = state->swap; @@ -622,7 +563,7 @@ static void tegra_plane_atomic_update(struct drm_plane *plane, window.stride[i] = fb->pitches[i]; } - tegra_dc_setup_window(dc, p->index, &window); + tegra_dc_setup_window(p, &window); } static const struct drm_plane_helper_funcs tegra_plane_helper_funcs = { @@ -631,8 +572,7 @@ static const struct drm_plane_helper_funcs tegra_plane_helper_funcs = { .atomic_update = tegra_plane_atomic_update, }; -static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm, - struct tegra_dc *dc) +static unsigned long tegra_plane_get_possible_crtcs(struct drm_device *drm) { /* * Ideally this would use drm_crtc_mask(), but that would require the @@ -646,7 +586,14 @@ static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm, * of CRTCs that have been registered, and should therefore always be * the same as drm_crtc_index() after registration. */ - unsigned long possible_crtcs = 1 << drm->mode_config.num_crtc; + return 1 << drm->mode_config.num_crtc; +} + +static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm, + struct tegra_dc *dc) +{ + unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm); + enum drm_plane_type type = DRM_PLANE_TYPE_PRIMARY; struct tegra_plane *plane; unsigned int num_formats; const u32 *formats; @@ -656,13 +603,17 @@ static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm, if (!plane) return ERR_PTR(-ENOMEM); - num_formats = ARRAY_SIZE(tegra_primary_plane_formats); - formats = tegra_primary_plane_formats; + /* Always use window A as primary window */ + plane->offset = 0xa00; + plane->index = 0; + plane->dc = dc; + + num_formats = dc->soc->num_primary_formats; + formats = dc->soc->primary_formats; err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, - &tegra_primary_plane_funcs, formats, - num_formats, NULL, - DRM_PLANE_TYPE_PRIMARY, NULL); + &tegra_plane_funcs, formats, + num_formats, NULL, type, NULL); if (err < 0) { kfree(plane); return ERR_PTR(err); @@ -670,6 +621,9 @@ static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm, drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs); + if (dc->soc->supports_blending) + drm_plane_create_zpos_property(&plane->base, 0, 0, 255); + return &plane->base; } @@ -786,15 +740,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, @@ -804,6 +749,7 @@ static const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = { static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, struct tegra_dc *dc) { + unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm); struct tegra_plane *plane; unsigned int num_formats; const u32 *formats; @@ -821,12 +767,13 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, * need to special-casing the cursor plane. */ plane->index = 6; + plane->dc = dc; num_formats = ARRAY_SIZE(tegra_cursor_plane_formats); formats = tegra_cursor_plane_formats; - err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe, - &tegra_cursor_plane_funcs, formats, + err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, + &tegra_plane_funcs, formats, num_formats, NULL, DRM_PLANE_TYPE_CURSOR, NULL); if (err < 0) { @@ -839,24 +786,76 @@ 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, - .reset = tegra_plane_reset, - .atomic_duplicate_state = tegra_plane_atomic_duplicate_state, - .atomic_destroy_state = tegra_plane_atomic_destroy_state, +static const u32 tegra20_overlay_formats[] = { + DRM_FORMAT_ARGB4444, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGB565, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_ARGB8888, + /* non-native formats */ + DRM_FORMAT_XRGB1555, + DRM_FORMAT_RGBX5551, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_XRGB8888, + /* planar formats */ + DRM_FORMAT_UYVY, + DRM_FORMAT_YUYV, + DRM_FORMAT_YUV420, + DRM_FORMAT_YUV422, }; -static const uint32_t tegra_overlay_plane_formats[] = { - DRM_FORMAT_XBGR8888, +static const u32 tegra114_overlay_formats[] = { + DRM_FORMAT_ARGB4444, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGB565, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_ARGB8888, + /* new on Tegra114 */ + DRM_FORMAT_ABGR4444, + DRM_FORMAT_ABGR1555, + DRM_FORMAT_BGRA5551, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_RGBX5551, + DRM_FORMAT_XBGR1555, + DRM_FORMAT_BGRX5551, + DRM_FORMAT_BGR565, + DRM_FORMAT_BGRA8888, + DRM_FORMAT_RGBA8888, DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + /* planar formats */ + DRM_FORMAT_UYVY, + DRM_FORMAT_YUYV, + DRM_FORMAT_YUV420, + DRM_FORMAT_YUV422, +}; + +static const u32 tegra124_overlay_formats[] = { + DRM_FORMAT_ARGB4444, + DRM_FORMAT_ARGB1555, DRM_FORMAT_RGB565, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_ARGB8888, + /* new on Tegra114 */ + DRM_FORMAT_ABGR4444, + DRM_FORMAT_ABGR1555, + DRM_FORMAT_BGRA5551, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_RGBX5551, + DRM_FORMAT_XBGR1555, + DRM_FORMAT_BGRX5551, + DRM_FORMAT_BGR565, + DRM_FORMAT_BGRA8888, + DRM_FORMAT_RGBA8888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + /* new on Tegra124 */ + DRM_FORMAT_RGBX8888, + DRM_FORMAT_BGRX8888, + /* planar formats */ DRM_FORMAT_UYVY, DRM_FORMAT_YUYV, DRM_FORMAT_YUV420, @@ -867,6 +866,7 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, struct tegra_dc *dc, unsigned int index) { + unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm); struct tegra_plane *plane; unsigned int num_formats; const u32 *formats; @@ -876,13 +876,15 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, if (!plane) return ERR_PTR(-ENOMEM); + plane->offset = 0xa00 + 0x200 * index; plane->index = index; + plane->dc = dc; - num_formats = ARRAY_SIZE(tegra_overlay_plane_formats); - formats = tegra_overlay_plane_formats; + num_formats = dc->soc->num_overlay_formats; + formats = dc->soc->overlay_formats; - err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe, - &tegra_overlay_plane_funcs, formats, + err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, + &tegra_plane_funcs, formats, num_formats, NULL, DRM_PLANE_TYPE_OVERLAY, NULL); if (err < 0) { @@ -892,97 +894,71 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs); + if (dc->soc->supports_blending) + drm_plane_create_zpos_property(&plane->base, 0, 0, 255); + 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; - unsigned int i; - - for (i = 0; i < 2; i++) { - plane = tegra_dc_overlay_plane_create(drm, dc, 1 + i); - if (IS_ERR(plane)) - return PTR_ERR(plane); + 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 0; -} - -static u32 tegra_dc_get_vblank_counter(struct drm_crtc *crtc) -{ - struct tegra_dc *dc = to_tegra_dc(crtc); - - if (dc->syncpt) - return host1x_syncpt_read(dc->syncpt); - - /* fallback to software emulated VBLANK counter */ - return drm_crtc_vblank_count(&dc->base); + return primary; } -static int tegra_dc_enable_vblank(struct drm_crtc *crtc) +static struct drm_plane *tegra_dc_add_planes(struct drm_device *drm, + struct tegra_dc *dc) { - struct tegra_dc *dc = to_tegra_dc(crtc); - unsigned long value, flags; - - spin_lock_irqsave(&dc->lock, flags); - - value = tegra_dc_readl(dc, DC_CMD_INT_MASK); - value |= VBLANK_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_MASK); - - spin_unlock_irqrestore(&dc->lock, flags); - - return 0; -} - -static void tegra_dc_disable_vblank(struct drm_crtc *crtc) -{ - struct tegra_dc *dc = to_tegra_dc(crtc); - unsigned long value, flags; - - spin_lock_irqsave(&dc->lock, flags); - - value = tegra_dc_readl(dc, DC_CMD_INT_MASK); - value &= ~VBLANK_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_MASK); - - spin_unlock_irqrestore(&dc->lock, flags); -} - -static void tegra_dc_finish_page_flip(struct tegra_dc *dc) -{ - struct drm_device *drm = dc->base.dev; - struct drm_crtc *crtc = &dc->base; - unsigned long flags, base; - struct tegra_bo *bo; - - spin_lock_irqsave(&drm->event_lock, flags); - - if (!dc->event) { - spin_unlock_irqrestore(&drm->event_lock, flags); - return; - } - - bo = tegra_fb_get_plane(crtc->primary->fb, 0); + struct drm_plane *planes[2], *primary; + unsigned int i; + int err; - spin_lock(&dc->lock); + primary = tegra_primary_plane_create(drm, dc); + if (IS_ERR(primary)) + return primary; - /* check if new start address has been latched */ - tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER); - tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); - base = tegra_dc_readl(dc, DC_WINBUF_START_ADDR); - tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); + for (i = 0; i < 2; i++) { + planes[i] = tegra_dc_overlay_plane_create(drm, dc, 1 + i); + if (IS_ERR(planes[i])) { + err = PTR_ERR(planes[i]); - spin_unlock(&dc->lock); + while (i--) + tegra_plane_funcs.destroy(planes[i]); - if (base == bo->paddr + crtc->primary->fb->offsets[0]) { - drm_crtc_send_vblank_event(crtc, dc->event); - drm_crtc_vblank_put(crtc); - dc->event = NULL; + tegra_plane_funcs.destroy(primary); + return ERR_PTR(err); + } } - spin_unlock_irqrestore(&drm->event_lock, flags); + return primary; } static void tegra_dc_destroy(struct drm_crtc *crtc) @@ -1035,6 +1011,379 @@ static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc, kfree(state); } +#define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name } + +static const struct debugfs_reg32 tegra_dc_regs[] = { + DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT), + DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_CNTRL), + DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_ERROR), + DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT), + DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_CNTRL), + DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_ERROR), + DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT), + DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_CNTRL), + DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_ERROR), + DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT), + DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_CNTRL), + DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_ERROR), + DEBUGFS_REG32(DC_CMD_CONT_SYNCPT_VSYNC), + DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND_OPTION0), + DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND), + DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE), + DEBUGFS_REG32(DC_CMD_DISPLAY_POWER_CONTROL), + DEBUGFS_REG32(DC_CMD_INT_STATUS), + DEBUGFS_REG32(DC_CMD_INT_MASK), + DEBUGFS_REG32(DC_CMD_INT_ENABLE), + DEBUGFS_REG32(DC_CMD_INT_TYPE), + DEBUGFS_REG32(DC_CMD_INT_POLARITY), + DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE1), + DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE2), + DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE3), + DEBUGFS_REG32(DC_CMD_STATE_ACCESS), + DEBUGFS_REG32(DC_CMD_STATE_CONTROL), + DEBUGFS_REG32(DC_CMD_DISPLAY_WINDOW_HEADER), + DEBUGFS_REG32(DC_CMD_REG_ACT_CONTROL), + DEBUGFS_REG32(DC_COM_CRC_CONTROL), + DEBUGFS_REG32(DC_COM_CRC_CHECKSUM), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(0)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(1)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(2)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(3)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(0)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(1)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(2)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(3)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(0)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(1)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(2)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(3)), + DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(0)), + DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(1)), + DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(2)), + DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(3)), + DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(0)), + DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(1)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(0)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(1)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(2)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(3)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(4)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(5)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(6)), + DEBUGFS_REG32(DC_COM_PIN_MISC_CONTROL), + DEBUGFS_REG32(DC_COM_PIN_PM0_CONTROL), + DEBUGFS_REG32(DC_COM_PIN_PM0_DUTY_CYCLE), + DEBUGFS_REG32(DC_COM_PIN_PM1_CONTROL), + DEBUGFS_REG32(DC_COM_PIN_PM1_DUTY_CYCLE), + DEBUGFS_REG32(DC_COM_SPI_CONTROL), + DEBUGFS_REG32(DC_COM_SPI_START_BYTE), + DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_AB), + DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_CD), + DEBUGFS_REG32(DC_COM_HSPI_CS_DC), + DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_A), + DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_B), + DEBUGFS_REG32(DC_COM_GPIO_CTRL), + DEBUGFS_REG32(DC_COM_GPIO_DEBOUNCE_COUNTER), + DEBUGFS_REG32(DC_COM_CRC_CHECKSUM_LATCHED), + DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS0), + DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS1), + DEBUGFS_REG32(DC_DISP_DISP_WIN_OPTIONS), + DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY), + DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER), + DEBUGFS_REG32(DC_DISP_DISP_TIMING_OPTIONS), + DEBUGFS_REG32(DC_DISP_REF_TO_SYNC), + DEBUGFS_REG32(DC_DISP_SYNC_WIDTH), + DEBUGFS_REG32(DC_DISP_BACK_PORCH), + DEBUGFS_REG32(DC_DISP_ACTIVE), + DEBUGFS_REG32(DC_DISP_FRONT_PORCH), + DEBUGFS_REG32(DC_DISP_H_PULSE0_CONTROL), + DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_A), + DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_B), + DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_C), + DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_D), + DEBUGFS_REG32(DC_DISP_H_PULSE1_CONTROL), + DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_A), + DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_B), + DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_C), + DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_D), + DEBUGFS_REG32(DC_DISP_H_PULSE2_CONTROL), + DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_A), + DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_B), + DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_C), + DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_D), + DEBUGFS_REG32(DC_DISP_V_PULSE0_CONTROL), + DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_A), + DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_B), + DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_C), + DEBUGFS_REG32(DC_DISP_V_PULSE1_CONTROL), + DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_A), + DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_B), + DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_C), + DEBUGFS_REG32(DC_DISP_V_PULSE2_CONTROL), + DEBUGFS_REG32(DC_DISP_V_PULSE2_POSITION_A), + DEBUGFS_REG32(DC_DISP_V_PULSE3_CONTROL), + DEBUGFS_REG32(DC_DISP_V_PULSE3_POSITION_A), + DEBUGFS_REG32(DC_DISP_M0_CONTROL), + DEBUGFS_REG32(DC_DISP_M1_CONTROL), + DEBUGFS_REG32(DC_DISP_DI_CONTROL), + DEBUGFS_REG32(DC_DISP_PP_CONTROL), + DEBUGFS_REG32(DC_DISP_PP_SELECT_A), + DEBUGFS_REG32(DC_DISP_PP_SELECT_B), + DEBUGFS_REG32(DC_DISP_PP_SELECT_C), + DEBUGFS_REG32(DC_DISP_PP_SELECT_D), + DEBUGFS_REG32(DC_DISP_DISP_CLOCK_CONTROL), + DEBUGFS_REG32(DC_DISP_DISP_INTERFACE_CONTROL), + DEBUGFS_REG32(DC_DISP_DISP_COLOR_CONTROL), + DEBUGFS_REG32(DC_DISP_SHIFT_CLOCK_OPTIONS), + DEBUGFS_REG32(DC_DISP_DATA_ENABLE_OPTIONS), + DEBUGFS_REG32(DC_DISP_SERIAL_INTERFACE_OPTIONS), + DEBUGFS_REG32(DC_DISP_LCD_SPI_OPTIONS), + DEBUGFS_REG32(DC_DISP_BORDER_COLOR), + DEBUGFS_REG32(DC_DISP_COLOR_KEY0_LOWER), + DEBUGFS_REG32(DC_DISP_COLOR_KEY0_UPPER), + DEBUGFS_REG32(DC_DISP_COLOR_KEY1_LOWER), + DEBUGFS_REG32(DC_DISP_COLOR_KEY1_UPPER), + DEBUGFS_REG32(DC_DISP_CURSOR_FOREGROUND), + DEBUGFS_REG32(DC_DISP_CURSOR_BACKGROUND), + DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR), + DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_NS), + DEBUGFS_REG32(DC_DISP_CURSOR_POSITION), + DEBUGFS_REG32(DC_DISP_CURSOR_POSITION_NS), + DEBUGFS_REG32(DC_DISP_INIT_SEQ_CONTROL), + DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_A), + DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_B), + DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_C), + DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_D), + DEBUGFS_REG32(DC_DISP_DC_MCCIF_FIFOCTRL), + DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0A_HYST), + DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0B_HYST), + DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1A_HYST), + DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1B_HYST), + DEBUGFS_REG32(DC_DISP_DAC_CRT_CTRL), + DEBUGFS_REG32(DC_DISP_DISP_MISC_CONTROL), + DEBUGFS_REG32(DC_DISP_SD_CONTROL), + DEBUGFS_REG32(DC_DISP_SD_CSC_COEFF), + DEBUGFS_REG32(DC_DISP_SD_LUT(0)), + DEBUGFS_REG32(DC_DISP_SD_LUT(1)), + DEBUGFS_REG32(DC_DISP_SD_LUT(2)), + DEBUGFS_REG32(DC_DISP_SD_LUT(3)), + DEBUGFS_REG32(DC_DISP_SD_LUT(4)), + DEBUGFS_REG32(DC_DISP_SD_LUT(5)), + DEBUGFS_REG32(DC_DISP_SD_LUT(6)), + DEBUGFS_REG32(DC_DISP_SD_LUT(7)), + DEBUGFS_REG32(DC_DISP_SD_LUT(8)), + DEBUGFS_REG32(DC_DISP_SD_FLICKER_CONTROL), + DEBUGFS_REG32(DC_DISP_DC_PIXEL_COUNT), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(0)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(1)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(2)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(3)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(4)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(5)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(6)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(7)), + DEBUGFS_REG32(DC_DISP_SD_BL_TF(0)), + DEBUGFS_REG32(DC_DISP_SD_BL_TF(1)), + DEBUGFS_REG32(DC_DISP_SD_BL_TF(2)), + DEBUGFS_REG32(DC_DISP_SD_BL_TF(3)), + DEBUGFS_REG32(DC_DISP_SD_BL_CONTROL), + DEBUGFS_REG32(DC_DISP_SD_HW_K_VALUES), + DEBUGFS_REG32(DC_DISP_SD_MAN_K_VALUES), + DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_HI), + DEBUGFS_REG32(DC_DISP_BLEND_CURSOR_CONTROL), + DEBUGFS_REG32(DC_WIN_WIN_OPTIONS), + DEBUGFS_REG32(DC_WIN_BYTE_SWAP), + DEBUGFS_REG32(DC_WIN_BUFFER_CONTROL), + DEBUGFS_REG32(DC_WIN_COLOR_DEPTH), + DEBUGFS_REG32(DC_WIN_POSITION), + DEBUGFS_REG32(DC_WIN_SIZE), + DEBUGFS_REG32(DC_WIN_PRESCALED_SIZE), + DEBUGFS_REG32(DC_WIN_H_INITIAL_DDA), + DEBUGFS_REG32(DC_WIN_V_INITIAL_DDA), + DEBUGFS_REG32(DC_WIN_DDA_INC), + DEBUGFS_REG32(DC_WIN_LINE_STRIDE), + DEBUGFS_REG32(DC_WIN_BUF_STRIDE), + DEBUGFS_REG32(DC_WIN_UV_BUF_STRIDE), + DEBUGFS_REG32(DC_WIN_BUFFER_ADDR_MODE), + DEBUGFS_REG32(DC_WIN_DV_CONTROL), + DEBUGFS_REG32(DC_WIN_BLEND_NOKEY), + DEBUGFS_REG32(DC_WIN_BLEND_1WIN), + DEBUGFS_REG32(DC_WIN_BLEND_2WIN_X), + DEBUGFS_REG32(DC_WIN_BLEND_2WIN_Y), + DEBUGFS_REG32(DC_WIN_BLEND_3WIN_XY), + DEBUGFS_REG32(DC_WIN_HP_FETCH_CONTROL), + DEBUGFS_REG32(DC_WINBUF_START_ADDR), + DEBUGFS_REG32(DC_WINBUF_START_ADDR_NS), + DEBUGFS_REG32(DC_WINBUF_START_ADDR_U), + DEBUGFS_REG32(DC_WINBUF_START_ADDR_U_NS), + DEBUGFS_REG32(DC_WINBUF_START_ADDR_V), + DEBUGFS_REG32(DC_WINBUF_START_ADDR_V_NS), + DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET), + DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET_NS), + DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET), + DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET_NS), + DEBUGFS_REG32(DC_WINBUF_UFLOW_STATUS), + DEBUGFS_REG32(DC_WINBUF_AD_UFLOW_STATUS), + DEBUGFS_REG32(DC_WINBUF_BD_UFLOW_STATUS), + DEBUGFS_REG32(DC_WINBUF_CD_UFLOW_STATUS), +}; + +static int tegra_dc_show_regs(struct seq_file *s, void *data) +{ + struct drm_info_node *node = s->private; + struct tegra_dc *dc = node->info_ent->data; + unsigned int i; + int err = 0; + + drm_modeset_lock(&dc->base.mutex, NULL); + + if (!dc->base.state->active) { + err = -EBUSY; + goto unlock; + } + + for (i = 0; i < ARRAY_SIZE(tegra_dc_regs); i++) { + unsigned int offset = tegra_dc_regs[i].offset; + + seq_printf(s, "%-40s %#05x %08x\n", tegra_dc_regs[i].name, + offset, tegra_dc_readl(dc, offset)); + } + +unlock: + drm_modeset_unlock(&dc->base.mutex); + return err; +} + +static int tegra_dc_show_crc(struct seq_file *s, void *data) +{ + struct drm_info_node *node = s->private; + struct tegra_dc *dc = node->info_ent->data; + int err = 0; + u32 value; + + drm_modeset_lock(&dc->base.mutex, NULL); + + if (!dc->base.state->active) { + err = -EBUSY; + goto unlock; + } + + value = DC_COM_CRC_CONTROL_ACTIVE_DATA | DC_COM_CRC_CONTROL_ENABLE; + tegra_dc_writel(dc, value, DC_COM_CRC_CONTROL); + tegra_dc_commit(dc); + + drm_crtc_wait_one_vblank(&dc->base); + drm_crtc_wait_one_vblank(&dc->base); + + value = tegra_dc_readl(dc, DC_COM_CRC_CHECKSUM); + seq_printf(s, "%08x\n", value); + + tegra_dc_writel(dc, 0, DC_COM_CRC_CONTROL); + +unlock: + drm_modeset_unlock(&dc->base.mutex); + return err; +} + +static int tegra_dc_show_stats(struct seq_file *s, void *data) +{ + struct drm_info_node *node = s->private; + struct tegra_dc *dc = node->info_ent->data; + + seq_printf(s, "frames: %lu\n", dc->stats.frames); + seq_printf(s, "vblank: %lu\n", dc->stats.vblank); + seq_printf(s, "underflow: %lu\n", dc->stats.underflow); + seq_printf(s, "overflow: %lu\n", dc->stats.overflow); + + return 0; +} + +static struct drm_info_list debugfs_files[] = { + { "regs", tegra_dc_show_regs, 0, NULL }, + { "crc", tegra_dc_show_crc, 0, NULL }, + { "stats", tegra_dc_show_stats, 0, NULL }, +}; + +static int tegra_dc_late_register(struct drm_crtc *crtc) +{ + unsigned int i, count = ARRAY_SIZE(debugfs_files); + struct drm_minor *minor = crtc->dev->primary; + struct dentry *root; + struct tegra_dc *dc = to_tegra_dc(crtc); + int err; + +#ifdef CONFIG_DEBUG_FS + root = crtc->debugfs_entry; +#else + root = NULL; +#endif + + dc->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files), + GFP_KERNEL); + if (!dc->debugfs_files) + return -ENOMEM; + + for (i = 0; i < count; i++) + dc->debugfs_files[i].data = dc; + + err = drm_debugfs_create_files(dc->debugfs_files, count, root, minor); + if (err < 0) + goto free; + + return 0; + +free: + kfree(dc->debugfs_files); + dc->debugfs_files = NULL; + + return err; +} + +static void tegra_dc_early_unregister(struct drm_crtc *crtc) +{ + unsigned int count = ARRAY_SIZE(debugfs_files); + struct drm_minor *minor = crtc->dev->primary; + struct tegra_dc *dc = to_tegra_dc(crtc); + + drm_debugfs_remove_files(dc->debugfs_files, count, minor); + kfree(dc->debugfs_files); + dc->debugfs_files = NULL; +} + +static u32 tegra_dc_get_vblank_counter(struct drm_crtc *crtc) +{ + struct tegra_dc *dc = to_tegra_dc(crtc); + + /* 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 */ + return drm_crtc_vblank_count(&dc->base); +} + +static int tegra_dc_enable_vblank(struct drm_crtc *crtc) +{ + struct tegra_dc *dc = to_tegra_dc(crtc); + u32 value; + + value = tegra_dc_readl(dc, DC_CMD_INT_MASK); + value |= VBLANK_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_MASK); + + return 0; +} + +static void tegra_dc_disable_vblank(struct drm_crtc *crtc) +{ + struct tegra_dc *dc = to_tegra_dc(crtc); + u32 value; + + value = tegra_dc_readl(dc, DC_CMD_INT_MASK); + value &= ~VBLANK_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_MASK); +} + static const struct drm_crtc_funcs tegra_crtc_funcs = { .page_flip = drm_atomic_helper_page_flip, .set_config = drm_atomic_helper_set_config, @@ -1042,6 +1391,8 @@ static const struct drm_crtc_funcs tegra_crtc_funcs = { .reset = tegra_crtc_reset, .atomic_duplicate_state = tegra_crtc_atomic_duplicate_state, .atomic_destroy_state = tegra_crtc_atomic_destroy_state, + .late_register = tegra_dc_late_register, + .early_unregister = tegra_dc_early_unregister, .get_vblank_counter = tegra_dc_get_vblank_counter, .enable_vblank = tegra_dc_enable_vblank, .disable_vblank = tegra_dc_disable_vblank, @@ -1054,10 +1405,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); @@ -1136,8 +1489,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) @@ -1223,6 +1578,15 @@ static void tegra_crtc_atomic_disable(struct drm_crtc *crtc, tegra_dc_stats_reset(&dc->stats); drm_crtc_vblank_off(crtc); + spin_lock_irq(&crtc->dev->event_lock); + + if (crtc->state->event) { + drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; + } + + spin_unlock_irq(&crtc->dev->event_lock); + pm_runtime_put_sync(dc->dev); } @@ -1238,41 +1602,70 @@ 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_border_color) + if (dc->soc->supports_background_color) + tegra_dc_writel(dc, 0, DC_DISP_BLEND_BACKGROUND_COLOR); + else tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR); /* apply PLL and pixel clock changes */ @@ -1293,10 +1686,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); @@ -1306,20 +1707,43 @@ 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; } static void tegra_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { - struct tegra_dc *dc = to_tegra_dc(crtc); + unsigned long flags; if (crtc->state->event) { - crtc->state->event->pipe = drm_crtc_index(crtc); + spin_lock_irqsave(&crtc->dev->event_lock, flags); - WARN_ON(drm_crtc_vblank_get(crtc) != 0); + if (drm_crtc_vblank_get(crtc) != 0) + drm_crtc_send_vblank_event(crtc, crtc->state->event); + else + drm_crtc_arm_vblank_event(crtc, crtc->state->event); + + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); - dc->event = crtc->state->event; crtc->state->event = NULL; } } @@ -1329,9 +1753,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; - tegra_dc_writel(dc, state->planes << 8, DC_CMD_STATE_CONTROL); - tegra_dc_writel(dc, state->planes, DC_CMD_STATE_CONTROL); + value = state->planes << 8 | GENERAL_UPDATE; + tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); + value = tegra_dc_readl(dc, 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 = { @@ -1362,7 +1792,6 @@ static irqreturn_t tegra_dc_irq(int irq, void *data) dev_dbg(dc->dev, "%s(): vertical blank\n", __func__); */ drm_crtc_handle_vblank(&dc->base); - tegra_dc_finish_page_flip(dc); dc->stats.vblank++; } @@ -1380,357 +1809,18 @@ static irqreturn_t tegra_dc_irq(int irq, void *data) dc->stats.overflow++; } - return IRQ_HANDLED; -} - -static int tegra_dc_show_regs(struct seq_file *s, void *data) -{ - struct drm_info_node *node = s->private; - struct tegra_dc *dc = node->info_ent->data; - int err = 0; - - drm_modeset_lock(&dc->base.mutex, NULL); - - if (!dc->base.state->active) { - err = -EBUSY; - goto unlock; - } - -#define DUMP_REG(name) \ - seq_printf(s, "%-40s %#05x %08x\n", #name, name, \ - tegra_dc_readl(dc, name)) - - DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT); - DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); - DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT_ERROR); - DUMP_REG(DC_CMD_WIN_A_INCR_SYNCPT); - DUMP_REG(DC_CMD_WIN_A_INCR_SYNCPT_CNTRL); - DUMP_REG(DC_CMD_WIN_A_INCR_SYNCPT_ERROR); - DUMP_REG(DC_CMD_WIN_B_INCR_SYNCPT); - DUMP_REG(DC_CMD_WIN_B_INCR_SYNCPT_CNTRL); - DUMP_REG(DC_CMD_WIN_B_INCR_SYNCPT_ERROR); - DUMP_REG(DC_CMD_WIN_C_INCR_SYNCPT); - DUMP_REG(DC_CMD_WIN_C_INCR_SYNCPT_CNTRL); - DUMP_REG(DC_CMD_WIN_C_INCR_SYNCPT_ERROR); - DUMP_REG(DC_CMD_CONT_SYNCPT_VSYNC); - DUMP_REG(DC_CMD_DISPLAY_COMMAND_OPTION0); - DUMP_REG(DC_CMD_DISPLAY_COMMAND); - DUMP_REG(DC_CMD_SIGNAL_RAISE); - DUMP_REG(DC_CMD_DISPLAY_POWER_CONTROL); - DUMP_REG(DC_CMD_INT_STATUS); - DUMP_REG(DC_CMD_INT_MASK); - DUMP_REG(DC_CMD_INT_ENABLE); - DUMP_REG(DC_CMD_INT_TYPE); - DUMP_REG(DC_CMD_INT_POLARITY); - DUMP_REG(DC_CMD_SIGNAL_RAISE1); - DUMP_REG(DC_CMD_SIGNAL_RAISE2); - DUMP_REG(DC_CMD_SIGNAL_RAISE3); - DUMP_REG(DC_CMD_STATE_ACCESS); - DUMP_REG(DC_CMD_STATE_CONTROL); - DUMP_REG(DC_CMD_DISPLAY_WINDOW_HEADER); - DUMP_REG(DC_CMD_REG_ACT_CONTROL); - DUMP_REG(DC_COM_CRC_CONTROL); - DUMP_REG(DC_COM_CRC_CHECKSUM); - DUMP_REG(DC_COM_PIN_OUTPUT_ENABLE(0)); - DUMP_REG(DC_COM_PIN_OUTPUT_ENABLE(1)); - DUMP_REG(DC_COM_PIN_OUTPUT_ENABLE(2)); - DUMP_REG(DC_COM_PIN_OUTPUT_ENABLE(3)); - DUMP_REG(DC_COM_PIN_OUTPUT_POLARITY(0)); - DUMP_REG(DC_COM_PIN_OUTPUT_POLARITY(1)); - DUMP_REG(DC_COM_PIN_OUTPUT_POLARITY(2)); - DUMP_REG(DC_COM_PIN_OUTPUT_POLARITY(3)); - DUMP_REG(DC_COM_PIN_OUTPUT_DATA(0)); - DUMP_REG(DC_COM_PIN_OUTPUT_DATA(1)); - DUMP_REG(DC_COM_PIN_OUTPUT_DATA(2)); - DUMP_REG(DC_COM_PIN_OUTPUT_DATA(3)); - DUMP_REG(DC_COM_PIN_INPUT_ENABLE(0)); - DUMP_REG(DC_COM_PIN_INPUT_ENABLE(1)); - DUMP_REG(DC_COM_PIN_INPUT_ENABLE(2)); - DUMP_REG(DC_COM_PIN_INPUT_ENABLE(3)); - DUMP_REG(DC_COM_PIN_INPUT_DATA(0)); - DUMP_REG(DC_COM_PIN_INPUT_DATA(1)); - DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(0)); - DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(1)); - DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(2)); - DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(3)); - DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(4)); - DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(5)); - DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(6)); - DUMP_REG(DC_COM_PIN_MISC_CONTROL); - DUMP_REG(DC_COM_PIN_PM0_CONTROL); - DUMP_REG(DC_COM_PIN_PM0_DUTY_CYCLE); - DUMP_REG(DC_COM_PIN_PM1_CONTROL); - DUMP_REG(DC_COM_PIN_PM1_DUTY_CYCLE); - DUMP_REG(DC_COM_SPI_CONTROL); - DUMP_REG(DC_COM_SPI_START_BYTE); - DUMP_REG(DC_COM_HSPI_WRITE_DATA_AB); - DUMP_REG(DC_COM_HSPI_WRITE_DATA_CD); - DUMP_REG(DC_COM_HSPI_CS_DC); - DUMP_REG(DC_COM_SCRATCH_REGISTER_A); - DUMP_REG(DC_COM_SCRATCH_REGISTER_B); - DUMP_REG(DC_COM_GPIO_CTRL); - DUMP_REG(DC_COM_GPIO_DEBOUNCE_COUNTER); - DUMP_REG(DC_COM_CRC_CHECKSUM_LATCHED); - DUMP_REG(DC_DISP_DISP_SIGNAL_OPTIONS0); - DUMP_REG(DC_DISP_DISP_SIGNAL_OPTIONS1); - DUMP_REG(DC_DISP_DISP_WIN_OPTIONS); - DUMP_REG(DC_DISP_DISP_MEM_HIGH_PRIORITY); - DUMP_REG(DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER); - DUMP_REG(DC_DISP_DISP_TIMING_OPTIONS); - DUMP_REG(DC_DISP_REF_TO_SYNC); - DUMP_REG(DC_DISP_SYNC_WIDTH); - DUMP_REG(DC_DISP_BACK_PORCH); - DUMP_REG(DC_DISP_ACTIVE); - DUMP_REG(DC_DISP_FRONT_PORCH); - DUMP_REG(DC_DISP_H_PULSE0_CONTROL); - DUMP_REG(DC_DISP_H_PULSE0_POSITION_A); - DUMP_REG(DC_DISP_H_PULSE0_POSITION_B); - DUMP_REG(DC_DISP_H_PULSE0_POSITION_C); - DUMP_REG(DC_DISP_H_PULSE0_POSITION_D); - DUMP_REG(DC_DISP_H_PULSE1_CONTROL); - DUMP_REG(DC_DISP_H_PULSE1_POSITION_A); - DUMP_REG(DC_DISP_H_PULSE1_POSITION_B); - DUMP_REG(DC_DISP_H_PULSE1_POSITION_C); - DUMP_REG(DC_DISP_H_PULSE1_POSITION_D); - DUMP_REG(DC_DISP_H_PULSE2_CONTROL); - DUMP_REG(DC_DISP_H_PULSE2_POSITION_A); - DUMP_REG(DC_DISP_H_PULSE2_POSITION_B); - DUMP_REG(DC_DISP_H_PULSE2_POSITION_C); - DUMP_REG(DC_DISP_H_PULSE2_POSITION_D); - DUMP_REG(DC_DISP_V_PULSE0_CONTROL); - DUMP_REG(DC_DISP_V_PULSE0_POSITION_A); - DUMP_REG(DC_DISP_V_PULSE0_POSITION_B); - DUMP_REG(DC_DISP_V_PULSE0_POSITION_C); - DUMP_REG(DC_DISP_V_PULSE1_CONTROL); - DUMP_REG(DC_DISP_V_PULSE1_POSITION_A); - DUMP_REG(DC_DISP_V_PULSE1_POSITION_B); - DUMP_REG(DC_DISP_V_PULSE1_POSITION_C); - DUMP_REG(DC_DISP_V_PULSE2_CONTROL); - DUMP_REG(DC_DISP_V_PULSE2_POSITION_A); - DUMP_REG(DC_DISP_V_PULSE3_CONTROL); - DUMP_REG(DC_DISP_V_PULSE3_POSITION_A); - DUMP_REG(DC_DISP_M0_CONTROL); - DUMP_REG(DC_DISP_M1_CONTROL); - DUMP_REG(DC_DISP_DI_CONTROL); - DUMP_REG(DC_DISP_PP_CONTROL); - DUMP_REG(DC_DISP_PP_SELECT_A); - DUMP_REG(DC_DISP_PP_SELECT_B); - DUMP_REG(DC_DISP_PP_SELECT_C); - DUMP_REG(DC_DISP_PP_SELECT_D); - DUMP_REG(DC_DISP_DISP_CLOCK_CONTROL); - DUMP_REG(DC_DISP_DISP_INTERFACE_CONTROL); - DUMP_REG(DC_DISP_DISP_COLOR_CONTROL); - DUMP_REG(DC_DISP_SHIFT_CLOCK_OPTIONS); - DUMP_REG(DC_DISP_DATA_ENABLE_OPTIONS); - DUMP_REG(DC_DISP_SERIAL_INTERFACE_OPTIONS); - DUMP_REG(DC_DISP_LCD_SPI_OPTIONS); - DUMP_REG(DC_DISP_BORDER_COLOR); - DUMP_REG(DC_DISP_COLOR_KEY0_LOWER); - DUMP_REG(DC_DISP_COLOR_KEY0_UPPER); - DUMP_REG(DC_DISP_COLOR_KEY1_LOWER); - DUMP_REG(DC_DISP_COLOR_KEY1_UPPER); - DUMP_REG(DC_DISP_CURSOR_FOREGROUND); - DUMP_REG(DC_DISP_CURSOR_BACKGROUND); - DUMP_REG(DC_DISP_CURSOR_START_ADDR); - DUMP_REG(DC_DISP_CURSOR_START_ADDR_NS); - DUMP_REG(DC_DISP_CURSOR_POSITION); - DUMP_REG(DC_DISP_CURSOR_POSITION_NS); - DUMP_REG(DC_DISP_INIT_SEQ_CONTROL); - DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_A); - DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_B); - DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_C); - DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_D); - DUMP_REG(DC_DISP_DC_MCCIF_FIFOCTRL); - DUMP_REG(DC_DISP_MCCIF_DISPLAY0A_HYST); - DUMP_REG(DC_DISP_MCCIF_DISPLAY0B_HYST); - DUMP_REG(DC_DISP_MCCIF_DISPLAY1A_HYST); - DUMP_REG(DC_DISP_MCCIF_DISPLAY1B_HYST); - DUMP_REG(DC_DISP_DAC_CRT_CTRL); - DUMP_REG(DC_DISP_DISP_MISC_CONTROL); - DUMP_REG(DC_DISP_SD_CONTROL); - DUMP_REG(DC_DISP_SD_CSC_COEFF); - DUMP_REG(DC_DISP_SD_LUT(0)); - DUMP_REG(DC_DISP_SD_LUT(1)); - DUMP_REG(DC_DISP_SD_LUT(2)); - DUMP_REG(DC_DISP_SD_LUT(3)); - DUMP_REG(DC_DISP_SD_LUT(4)); - DUMP_REG(DC_DISP_SD_LUT(5)); - DUMP_REG(DC_DISP_SD_LUT(6)); - DUMP_REG(DC_DISP_SD_LUT(7)); - DUMP_REG(DC_DISP_SD_LUT(8)); - DUMP_REG(DC_DISP_SD_FLICKER_CONTROL); - DUMP_REG(DC_DISP_DC_PIXEL_COUNT); - DUMP_REG(DC_DISP_SD_HISTOGRAM(0)); - DUMP_REG(DC_DISP_SD_HISTOGRAM(1)); - DUMP_REG(DC_DISP_SD_HISTOGRAM(2)); - DUMP_REG(DC_DISP_SD_HISTOGRAM(3)); - DUMP_REG(DC_DISP_SD_HISTOGRAM(4)); - DUMP_REG(DC_DISP_SD_HISTOGRAM(5)); - DUMP_REG(DC_DISP_SD_HISTOGRAM(6)); - DUMP_REG(DC_DISP_SD_HISTOGRAM(7)); - DUMP_REG(DC_DISP_SD_BL_TF(0)); - DUMP_REG(DC_DISP_SD_BL_TF(1)); - DUMP_REG(DC_DISP_SD_BL_TF(2)); - DUMP_REG(DC_DISP_SD_BL_TF(3)); - DUMP_REG(DC_DISP_SD_BL_CONTROL); - DUMP_REG(DC_DISP_SD_HW_K_VALUES); - DUMP_REG(DC_DISP_SD_MAN_K_VALUES); - DUMP_REG(DC_DISP_CURSOR_START_ADDR_HI); - DUMP_REG(DC_DISP_BLEND_CURSOR_CONTROL); - DUMP_REG(DC_WIN_WIN_OPTIONS); - DUMP_REG(DC_WIN_BYTE_SWAP); - DUMP_REG(DC_WIN_BUFFER_CONTROL); - DUMP_REG(DC_WIN_COLOR_DEPTH); - DUMP_REG(DC_WIN_POSITION); - DUMP_REG(DC_WIN_SIZE); - DUMP_REG(DC_WIN_PRESCALED_SIZE); - DUMP_REG(DC_WIN_H_INITIAL_DDA); - DUMP_REG(DC_WIN_V_INITIAL_DDA); - DUMP_REG(DC_WIN_DDA_INC); - DUMP_REG(DC_WIN_LINE_STRIDE); - DUMP_REG(DC_WIN_BUF_STRIDE); - DUMP_REG(DC_WIN_UV_BUF_STRIDE); - DUMP_REG(DC_WIN_BUFFER_ADDR_MODE); - DUMP_REG(DC_WIN_DV_CONTROL); - DUMP_REG(DC_WIN_BLEND_NOKEY); - DUMP_REG(DC_WIN_BLEND_1WIN); - DUMP_REG(DC_WIN_BLEND_2WIN_X); - DUMP_REG(DC_WIN_BLEND_2WIN_Y); - DUMP_REG(DC_WIN_BLEND_3WIN_XY); - DUMP_REG(DC_WIN_HP_FETCH_CONTROL); - DUMP_REG(DC_WINBUF_START_ADDR); - DUMP_REG(DC_WINBUF_START_ADDR_NS); - DUMP_REG(DC_WINBUF_START_ADDR_U); - DUMP_REG(DC_WINBUF_START_ADDR_U_NS); - DUMP_REG(DC_WINBUF_START_ADDR_V); - DUMP_REG(DC_WINBUF_START_ADDR_V_NS); - DUMP_REG(DC_WINBUF_ADDR_H_OFFSET); - DUMP_REG(DC_WINBUF_ADDR_H_OFFSET_NS); - DUMP_REG(DC_WINBUF_ADDR_V_OFFSET); - DUMP_REG(DC_WINBUF_ADDR_V_OFFSET_NS); - DUMP_REG(DC_WINBUF_UFLOW_STATUS); - DUMP_REG(DC_WINBUF_AD_UFLOW_STATUS); - DUMP_REG(DC_WINBUF_BD_UFLOW_STATUS); - DUMP_REG(DC_WINBUF_CD_UFLOW_STATUS); - -#undef DUMP_REG - -unlock: - drm_modeset_unlock(&dc->base.mutex); - return err; -} - -static int tegra_dc_show_crc(struct seq_file *s, void *data) -{ - struct drm_info_node *node = s->private; - struct tegra_dc *dc = node->info_ent->data; - int err = 0; - u32 value; - - drm_modeset_lock(&dc->base.mutex, NULL); - - if (!dc->base.state->active) { - err = -EBUSY; - goto unlock; - } - - value = DC_COM_CRC_CONTROL_ACTIVE_DATA | DC_COM_CRC_CONTROL_ENABLE; - tegra_dc_writel(dc, value, DC_COM_CRC_CONTROL); - tegra_dc_commit(dc); - - drm_crtc_wait_one_vblank(&dc->base); - drm_crtc_wait_one_vblank(&dc->base); - - value = tegra_dc_readl(dc, DC_COM_CRC_CHECKSUM); - seq_printf(s, "%08x\n", value); - - tegra_dc_writel(dc, 0, DC_COM_CRC_CONTROL); - -unlock: - drm_modeset_unlock(&dc->base.mutex); - return err; -} - -static int tegra_dc_show_stats(struct seq_file *s, void *data) -{ - struct drm_info_node *node = s->private; - struct tegra_dc *dc = node->info_ent->data; - - seq_printf(s, "frames: %lu\n", dc->stats.frames); - seq_printf(s, "vblank: %lu\n", dc->stats.vblank); - seq_printf(s, "underflow: %lu\n", dc->stats.underflow); - seq_printf(s, "overflow: %lu\n", dc->stats.overflow); - - return 0; -} - -static struct drm_info_list debugfs_files[] = { - { "regs", tegra_dc_show_regs, 0, NULL }, - { "crc", tegra_dc_show_crc, 0, NULL }, - { "stats", tegra_dc_show_stats, 0, NULL }, -}; - -static int tegra_dc_debugfs_init(struct tegra_dc *dc, struct drm_minor *minor) -{ - unsigned int i; - char *name; - int err; - - name = kasprintf(GFP_KERNEL, "dc.%d", dc->pipe); - dc->debugfs = debugfs_create_dir(name, minor->debugfs_root); - kfree(name); - - if (!dc->debugfs) - return -ENOMEM; - - dc->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files), - GFP_KERNEL); - if (!dc->debugfs_files) { - err = -ENOMEM; - goto remove; + if (status & HEAD_UF_INT) { + dev_dbg_ratelimited(dc->dev, "%s(): head underflow\n", __func__); + dc->stats.underflow++; } - for (i = 0; i < ARRAY_SIZE(debugfs_files); i++) - dc->debugfs_files[i].data = dc; - - err = drm_debugfs_create_files(dc->debugfs_files, - ARRAY_SIZE(debugfs_files), - dc->debugfs, minor); - if (err < 0) - goto free; - - dc->minor = minor; - - return 0; - -free: - kfree(dc->debugfs_files); - dc->debugfs_files = NULL; -remove: - debugfs_remove(dc->debugfs); - dc->debugfs = NULL; - - return err; -} - -static int tegra_dc_debugfs_exit(struct tegra_dc *dc) -{ - drm_debugfs_remove_files(dc->debugfs_files, ARRAY_SIZE(debugfs_files), - dc->minor); - dc->minor = NULL; - - kfree(dc->debugfs_files); - dc->debugfs_files = NULL; - - debugfs_remove(dc->debugfs); - dc->debugfs = NULL; - - return 0; + return IRQ_HANDLED; } static int tegra_dc_init(struct host1x_client *client) { struct drm_device *drm = dev_get_drvdata(client->parent); + struct iommu_group *group = iommu_group_get(client->dev); unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED; struct tegra_dc *dc = host1x_client_to_dc(client); struct tegra_drm *tegra = drm->dev_private; @@ -1742,18 +1832,27 @@ static int tegra_dc_init(struct host1x_client *client) if (!dc->syncpt) dev_warn(dc->dev, "failed to allocate syncpoint\n"); - if (tegra->domain) { - err = iommu_attach_device(tegra->domain, dc->dev); - if (err < 0) { - dev_err(dc->dev, "failed to attach to domain: %d\n", - err); - return err; + if (group && tegra->domain) { + if (group != tegra->group) { + err = iommu_attach_group(tegra->domain, group); + if (err < 0) { + dev_err(dc->dev, + "failed to attach to domain: %d\n", + err); + return err; + } + + tegra->group = group; } 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; @@ -1787,16 +1886,6 @@ static int tegra_dc_init(struct host1x_client *client) goto cleanup; } - err = tegra_dc_add_planes(drm, dc); - if (err < 0) - goto cleanup; - - if (IS_ENABLED(CONFIG_DEBUG_FS)) { - err = tegra_dc_debugfs_init(dc, drm->primary); - if (err < 0) - dev_err(dc->dev, "debugfs setup failed: %d\n", err); - } - err = devm_request_irq(dc->dev, dc->irq, tegra_dc_irq, 0, dev_name(dc->dev), dc); if (err < 0) { @@ -1808,14 +1897,14 @@ 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 (tegra->domain) { - iommu_detach_device(tegra->domain, dc->dev); + if (group && tegra->domain) { + iommu_detach_group(tegra->domain, group); dc->domain = NULL; } @@ -1824,25 +1913,20 @@ cleanup: static int tegra_dc_exit(struct host1x_client *client) { + struct iommu_group *group = iommu_group_get(client->dev); struct tegra_dc *dc = host1x_client_to_dc(client); int err; devm_free_irq(dc->dev, dc->irq, dc); - if (IS_ENABLED(CONFIG_DEBUG_FS)) { - err = tegra_dc_debugfs_exit(dc); - if (err < 0) - dev_err(dc->dev, "debugfs cleanup failed: %d\n", err); - } - err = tegra_dc_rgb_exit(dc); if (err) { dev_err(dc->dev, "failed to shutdown RGB output: %d\n", err); return err; } - if (dc->domain) { - iommu_detach_device(dc->domain, dc->dev); + if (group && dc->domain) { + iommu_detach_group(dc->domain, group); dc->domain = NULL; } @@ -1857,57 +1941,138 @@ static const struct host1x_client_ops dc_client_ops = { }; static const struct tegra_dc_soc_info tegra20_dc_soc_info = { - .supports_border_color = true, + .supports_background_color = false, .supports_interlacing = false, .supports_cursor = false, .supports_block_linear = false, + .supports_blending = false, .pitch_align = 8, .has_powergate = false, - .broken_reset = true, + .coupled_pm = true, + .has_nvdisplay = false, + .num_primary_formats = ARRAY_SIZE(tegra20_primary_formats), + .primary_formats = tegra20_primary_formats, + .num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats), + .overlay_formats = tegra20_overlay_formats, }; static const struct tegra_dc_soc_info tegra30_dc_soc_info = { - .supports_border_color = true, + .supports_background_color = false, .supports_interlacing = false, .supports_cursor = false, .supports_block_linear = false, + .supports_blending = false, .pitch_align = 8, .has_powergate = false, - .broken_reset = false, + .coupled_pm = false, + .has_nvdisplay = false, + .num_primary_formats = ARRAY_SIZE(tegra20_primary_formats), + .primary_formats = tegra20_primary_formats, + .num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats), + .overlay_formats = tegra20_overlay_formats, }; static const struct tegra_dc_soc_info tegra114_dc_soc_info = { - .supports_border_color = true, + .supports_background_color = false, .supports_interlacing = false, .supports_cursor = false, .supports_block_linear = false, + .supports_blending = false, .pitch_align = 64, .has_powergate = true, - .broken_reset = false, + .coupled_pm = false, + .has_nvdisplay = false, + .num_primary_formats = ARRAY_SIZE(tegra114_primary_formats), + .primary_formats = tegra114_primary_formats, + .num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats), + .overlay_formats = tegra114_overlay_formats, }; static const struct tegra_dc_soc_info tegra124_dc_soc_info = { - .supports_border_color = false, + .supports_background_color = true, .supports_interlacing = true, .supports_cursor = true, .supports_block_linear = true, + .supports_blending = true, .pitch_align = 64, .has_powergate = true, - .broken_reset = false, + .coupled_pm = false, + .has_nvdisplay = false, + .num_primary_formats = ARRAY_SIZE(tegra124_primary_formats), + .primary_formats = tegra114_primary_formats, + .num_overlay_formats = ARRAY_SIZE(tegra124_overlay_formats), + .overlay_formats = tegra114_overlay_formats, }; static const struct tegra_dc_soc_info tegra210_dc_soc_info = { - .supports_border_color = false, + .supports_background_color = true, .supports_interlacing = true, .supports_cursor = true, .supports_block_linear = true, + .supports_blending = true, .pitch_align = 64, .has_powergate = true, - .broken_reset = false, + .coupled_pm = false, + .has_nvdisplay = false, + .num_primary_formats = ARRAY_SIZE(tegra114_primary_formats), + .primary_formats = tegra114_primary_formats, + .num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats), + .overlay_formats = tegra114_overlay_formats, +}; + +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, + .supports_blending = true, + .pitch_align = 64, + .has_powergate = false, + .coupled_pm = 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, }, { @@ -1965,6 +2130,43 @@ static int tegra_dc_parse_dt(struct tegra_dc *dc) return 0; } +static int tegra_dc_match_by_pipe(struct device *dev, void *data) +{ + struct tegra_dc *dc = dev_get_drvdata(dev); + unsigned int pipe = (unsigned long)data; + + return dc->pipe == pipe; +} + +static int tegra_dc_couple(struct tegra_dc *dc) +{ + /* + * On Tegra20, DC1 requires DC0 to be taken out of reset in order to + * be enabled, otherwise CPU hangs on writing to CMD_DISPLAY_COMMAND / + * POWER_CONTROL registers during CRTC enabling. + */ + if (dc->soc->coupled_pm && dc->pipe == 1) { + u32 flags = DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE; + struct device_link *link; + struct device *partner; + + partner = driver_find_device(dc->dev->driver, NULL, 0, + tegra_dc_match_by_pipe); + if (!partner) + return -EPROBE_DEFER; + + link = device_link_add(dc->dev, partner, flags); + if (!link) { + dev_err(dc->dev, "failed to link controllers\n"); + return -EINVAL; + } + + dev_dbg(dc->dev, "coupled to %s\n", dev_name(partner)); + } + + return 0; +} + static int tegra_dc_probe(struct platform_device *pdev) { struct resource *regs; @@ -1977,7 +2179,6 @@ static int tegra_dc_probe(struct platform_device *pdev) dc->soc = of_device_get_match_data(&pdev->dev); - spin_lock_init(&dc->lock); INIT_LIST_HEAD(&dc->list); dc->dev = &pdev->dev; @@ -1985,6 +2186,10 @@ static int tegra_dc_probe(struct platform_device *pdev) if (err < 0) return err; + err = tegra_dc_couple(dc); + if (err < 0) + return err; + dc->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(dc->clk)) { dev_err(&pdev->dev, "failed to get clock\n"); @@ -1998,21 +2203,19 @@ static int tegra_dc_probe(struct platform_device *pdev) } /* assert reset and disable clock */ - if (!dc->soc->broken_reset) { - err = clk_prepare_enable(dc->clk); - if (err < 0) - return err; + err = clk_prepare_enable(dc->clk); + if (err < 0) + return err; - usleep_range(2000, 4000); + usleep_range(2000, 4000); - err = reset_control_assert(dc->rst); - if (err < 0) - return err; + err = reset_control_assert(dc->rst); + if (err < 0) + return err; - usleep_range(2000, 4000); + usleep_range(2000, 4000); - clk_disable_unprepare(dc->clk); - } + clk_disable_unprepare(dc->clk); if (dc->soc->has_powergate) { if (dc->pipe == 0) @@ -2086,12 +2289,10 @@ static int tegra_dc_suspend(struct device *dev) struct tegra_dc *dc = dev_get_drvdata(dev); int err; - if (!dc->soc->broken_reset) { - err = reset_control_assert(dc->rst); - if (err < 0) { - dev_err(dev, "failed to assert reset: %d\n", err); - return err; - } + err = reset_control_assert(dc->rst); + if (err < 0) { + dev_err(dev, "failed to assert reset: %d\n", err); + return err; } if (dc->soc->has_powergate) @@ -2121,13 +2322,10 @@ static int tegra_dc_resume(struct device *dev) return err; } - if (!dc->soc->broken_reset) { - err = reset_control_deassert(dc->rst); - if (err < 0) { - dev_err(dev, - "failed to deassert reset: %d\n", err); - return err; - } + err = reset_control_deassert(dc->rst); + if (err < 0) { + dev_err(dev, "failed to deassert reset: %d\n", err); + return err; } } diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index cb100b6e3282..096a81ad6d8d 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -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; @@ -25,21 +43,35 @@ struct tegra_dc_stats { unsigned long overflow; }; +struct tegra_windowgroup_soc { + unsigned int index; + unsigned int dc; + const unsigned int *windows; + unsigned int num_windows; +}; + struct tegra_dc_soc_info { - bool supports_border_color; + bool supports_background_color; bool supports_interlacing; bool supports_cursor; bool supports_block_linear; + bool supports_blending; unsigned int pitch_align; bool has_powergate; - bool broken_reset; + bool coupled_pm; + bool has_nvdisplay; + const struct tegra_windowgroup_soc *wgrps; + unsigned int num_wgrps; + const u32 *primary_formats; + unsigned int num_primary_formats; + const u32 *overlay_formats; + unsigned int num_overlay_formats; }; struct tegra_dc { struct host1x_client client; struct host1x_syncpt *syncpt; struct device *dev; - spinlock_t lock; struct drm_crtc base; unsigned int powergate; @@ -56,11 +88,6 @@ struct tegra_dc { struct list_head list; struct drm_info_list *debugfs_files; - struct drm_minor *minor; - struct dentry *debugfs; - - /* page-flip handling */ - struct drm_pending_vblank_event *event; const struct tegra_dc_soc_info *soc; @@ -110,6 +137,7 @@ struct tegra_dc_window { unsigned int bits_per_pixel; unsigned int stride[2]; unsigned long base[3]; + unsigned int zpos; bool bottom_up; struct tegra_bo_tiling tiling; @@ -118,6 +146,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, @@ -167,15 +196,26 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define DC_CMD_INT_ENABLE 0x039 #define DC_CMD_INT_TYPE 0x03a #define DC_CMD_INT_POLARITY 0x03b -#define CTXSW_INT (1 << 0) -#define FRAME_END_INT (1 << 1) -#define VBLANK_INT (1 << 2) -#define WIN_A_UF_INT (1 << 8) -#define WIN_B_UF_INT (1 << 9) -#define WIN_C_UF_INT (1 << 10) -#define WIN_A_OF_INT (1 << 14) -#define WIN_B_OF_INT (1 << 15) -#define WIN_C_OF_INT (1 << 16) +#define CTXSW_INT (1 << 0) +#define FRAME_END_INT (1 << 1) +#define VBLANK_INT (1 << 2) +#define V_PULSE3_INT (1 << 4) +#define V_PULSE2_INT (1 << 5) +#define REGION_CRC_INT (1 << 6) +#define REG_TMOUT_INT (1 << 7) +#define WIN_A_UF_INT (1 << 8) +#define WIN_B_UF_INT (1 << 9) +#define WIN_C_UF_INT (1 << 10) +#define MSF_INT (1 << 12) +#define WIN_A_OF_INT (1 << 14) +#define WIN_B_OF_INT (1 << 15) +#define WIN_C_OF_INT (1 << 16) +#define HEAD_UF_INT (1 << 23) +#define SD3_BUCKET_WALK_DONE_INT (1 << 24) +#define DSC_OBUF_UF_INT (1 << 26) +#define DSC_RBUF_UF_INT (1 << 27) +#define DSC_BBUF_UF_INT (1 << 28) +#define DSC_TO_UF_INT (1 << 29) #define DC_CMD_SIGNAL_RAISE1 0x03c #define DC_CMD_SIGNAL_RAISE2 0x03d @@ -196,6 +236,8 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define WIN_B_UPDATE (1 << 10) #define WIN_C_UPDATE (1 << 11) #define CURSOR_UPDATE (1 << 15) +#define COMMON_ACTREQ (1 << 16) +#define COMMON_UPDATE (1 << 17) #define NC_HOST_TRIG (1 << 24) #define DC_CMD_DISPLAY_WINDOW_HEADER 0x042 @@ -238,6 +280,10 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define DC_COM_GPIO_DEBOUNCE_COUNTER 0x328 #define DC_COM_CRC_CHECKSUM_LATCHED 0x329 +#define DC_COM_RG_UNDERFLOW 0x365 +#define UNDERFLOW_MODE_RED (1 << 8) +#define UNDERFLOW_REPORT_ENABLE (1 << 0) + #define DC_DISP_DISP_SIGNAL_OPTIONS0 0x400 #define H_PULSE0_ENABLE (1 << 8) #define H_PULSE1_ENABLE (1 << 10) @@ -249,10 +295,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) @@ -360,29 +406,33 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define DISP_ORDER_BLUE_RED (1 << 9) #define DC_DISP_DISP_COLOR_CONTROL 0x430 -#define BASE_COLOR_SIZE666 (0 << 0) -#define BASE_COLOR_SIZE111 (1 << 0) -#define BASE_COLOR_SIZE222 (2 << 0) -#define BASE_COLOR_SIZE333 (3 << 0) -#define BASE_COLOR_SIZE444 (4 << 0) -#define BASE_COLOR_SIZE555 (5 << 0) -#define BASE_COLOR_SIZE565 (6 << 0) -#define BASE_COLOR_SIZE332 (7 << 0) -#define BASE_COLOR_SIZE888 (8 << 0) +#define BASE_COLOR_SIZE666 ( 0 << 0) +#define BASE_COLOR_SIZE111 ( 1 << 0) +#define BASE_COLOR_SIZE222 ( 2 << 0) +#define BASE_COLOR_SIZE333 ( 3 << 0) +#define BASE_COLOR_SIZE444 ( 4 << 0) +#define BASE_COLOR_SIZE555 ( 5 << 0) +#define BASE_COLOR_SIZE565 ( 6 << 0) +#define BASE_COLOR_SIZE332 ( 7 << 0) +#define BASE_COLOR_SIZE888 ( 8 << 0) +#define BASE_COLOR_SIZE101010 (10 << 0) +#define BASE_COLOR_SIZE121212 (12 << 0) #define DITHER_CONTROL_MASK (3 << 8) #define DITHER_CONTROL_DISABLE (0 << 8) #define DITHER_CONTROL_ORDERED (2 << 8) #define DITHER_CONTROL_ERRDIFF (3 << 8) #define BASE_COLOR_SIZE_MASK (0xf << 0) -#define BASE_COLOR_SIZE_666 (0 << 0) -#define BASE_COLOR_SIZE_111 (1 << 0) -#define BASE_COLOR_SIZE_222 (2 << 0) -#define BASE_COLOR_SIZE_333 (3 << 0) -#define BASE_COLOR_SIZE_444 (4 << 0) -#define BASE_COLOR_SIZE_555 (5 << 0) -#define BASE_COLOR_SIZE_565 (6 << 0) -#define BASE_COLOR_SIZE_332 (7 << 0) -#define BASE_COLOR_SIZE_888 (8 << 0) +#define BASE_COLOR_SIZE_666 ( 0 << 0) +#define BASE_COLOR_SIZE_111 ( 1 << 0) +#define BASE_COLOR_SIZE_222 ( 2 << 0) +#define BASE_COLOR_SIZE_333 ( 3 << 0) +#define BASE_COLOR_SIZE_444 ( 4 << 0) +#define BASE_COLOR_SIZE_555 ( 5 << 0) +#define BASE_COLOR_SIZE_565 ( 6 << 0) +#define BASE_COLOR_SIZE_332 ( 7 << 0) +#define BASE_COLOR_SIZE_888 ( 8 << 0) +#define BASE_COLOR_SIZE_101010 ( 10 << 0) +#define BASE_COLOR_SIZE_121212 ( 12 << 0) #define DC_DISP_SHIFT_CLOCK_OPTIONS 0x431 #define SC1_H_QUALIFIER_NONE (1 << 16) @@ -449,6 +499,12 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define DC_DISP_SD_HW_K_VALUES 0x4dd #define DC_DISP_SD_MAN_K_VALUES 0x4de +#define DC_DISP_BLEND_BACKGROUND_COLOR 0x4e4 +#define BACKGROUND_COLOR_ALPHA(x) (((x) & 0xff) << 24) +#define BACKGROUND_COLOR_BLUE(x) (((x) & 0xff) << 16) +#define BACKGROUND_COLOR_GREEN(x) (((x) & 0xff) << 8) +#define BACKGROUND_COLOR_RED(x) (((x) & 0xff) << 0) + #define DC_DISP_INTERLACE_CONTROL 0x4e5 #define INTERLACE_STATUS (1 << 2) #define INTERLACE_START (1 << 1) @@ -467,6 +523,35 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define CURSOR_SRC_BLEND_MASK (3 << 8) #define CURSOR_ALPHA 0xff +#define DC_WIN_CORE_ACT_CONTROL 0x50e +#define VCOUNTER (0 << 0) +#define HCOUNTER (1 << 0) + +#define DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLA 0x543 +#define LATENCY_CTL_MODE_ENABLE (1 << 2) + +#define DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLB 0x544 +#define WATERMARK_MASK 0x1fffffff + +#define DC_WIN_CORE_PRECOMP_WGRP_PIPE_METER 0x560 +#define PIPE_METER_INT(x) (((x) & 0xff) << 8) +#define PIPE_METER_FRAC(x) (((x) & 0xff) << 0) + +#define DC_WIN_CORE_IHUB_WGRP_POOL_CONFIG 0x561 +#define MEMPOOL_ENTRIES(x) (((x) & 0xffff) << 0) + +#define DC_WIN_CORE_IHUB_WGRP_FETCH_METER 0x562 +#define SLOTS(x) (((x) & 0xff) << 0) + +#define DC_WIN_CORE_IHUB_LINEBUF_CONFIG 0x563 +#define MODE_TWO_LINES (0 << 14) +#define MODE_FOUR_LINES (1 << 14) + +#define DC_WIN_CORE_IHUB_THREAD_GROUP 0x568 +#define THREAD_NUM_MASK (0x1f << 1) +#define THREAD_NUM(x) (((x) & 0x1f) << 1) +#define THREAD_GROUP_ENABLE (1 << 0) + #define DC_WIN_CSC_YOF 0x611 #define DC_WIN_CSC_KYRGB 0x612 #define DC_WIN_CSC_KUR 0x613 @@ -502,9 +587,9 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define WIN_COLOR_DEPTH_P4 2 #define WIN_COLOR_DEPTH_P8 3 #define WIN_COLOR_DEPTH_B4G4R4A4 4 -#define WIN_COLOR_DEPTH_B5G5R5A 5 +#define WIN_COLOR_DEPTH_B5G5R5A1 5 #define WIN_COLOR_DEPTH_B5G6R5 6 -#define WIN_COLOR_DEPTH_AB5G5R5 7 +#define WIN_COLOR_DEPTH_A1B5G5R5 7 #define WIN_COLOR_DEPTH_B8G8R8A8 12 #define WIN_COLOR_DEPTH_R8G8B8A8 13 #define WIN_COLOR_DEPTH_B6x2G6x2R6x2A8 14 @@ -519,18 +604,32 @@ 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_R4G4B4A4 27 +#define WIN_COLOR_DEPTH_R5G5B5A 28 +#define WIN_COLOR_DEPTH_AR5G5B5 29 +#define WIN_COLOR_DEPTH_B5G5R5X1 30 +#define WIN_COLOR_DEPTH_X1B5G5R5 31 +#define WIN_COLOR_DEPTH_R5G5B5X1 32 +#define WIN_COLOR_DEPTH_X1R5G5B5 33 +#define WIN_COLOR_DEPTH_R5G6B5 34 +#define WIN_COLOR_DEPTH_A8R8G8B8 35 +#define WIN_COLOR_DEPTH_A8B8G8R8 36 +#define WIN_COLOR_DEPTH_B8G8R8X8 37 +#define WIN_COLOR_DEPTH_R8G8B8X8 38 +#define WIN_COLOR_DEPTH_X8B8G8R8 65 +#define WIN_COLOR_DEPTH_X8R8G8B8 66 #define DC_WIN_POSITION 0x704 -#define H_POSITION(x) (((x) & 0x1fff) << 0) -#define V_POSITION(x) (((x) & 0x1fff) << 16) +#define H_POSITION(x) (((x) & 0x1fff) << 0) /* XXX 0x7fff on Tegra186 */ +#define V_POSITION(x) (((x) & 0x1fff) << 16) /* XXX 0x7fff on Tegra186 */ #define DC_WIN_SIZE 0x705 -#define H_SIZE(x) (((x) & 0x1fff) << 0) -#define V_SIZE(x) (((x) & 0x1fff) << 16) +#define H_SIZE(x) (((x) & 0x1fff) << 0) /* XXX 0x7fff on Tegra186 */ +#define V_SIZE(x) (((x) & 0x1fff) << 16) /* XXX 0x7fff on Tegra186 */ #define DC_WIN_PRESCALED_SIZE 0x706 #define H_PRESCALED_SIZE(x) (((x) & 0x7fff) << 0) -#define V_PRESCALED_SIZE(x) (((x) & 0x1fff) << 16) +#define V_PRESCALED_SIZE(x) (((x) & 0x1fff) << 16) /* XXX 0x7fff on Tegra186 */ #define DC_WIN_H_INITIAL_DDA 0x707 #define DC_WIN_V_INITIAL_DDA 0x708 @@ -546,11 +645,24 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define DC_WIN_BUFFER_ADDR_MODE_TILE (1 << 0) #define DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV (0 << 16) #define DC_WIN_BUFFER_ADDR_MODE_TILE_UV (1 << 16) + #define DC_WIN_DV_CONTROL 0x70e #define DC_WIN_BLEND_NOKEY 0x70f +#define BLEND_WEIGHT1(x) (((x) & 0xff) << 16) +#define BLEND_WEIGHT0(x) (((x) & 0xff) << 8) + #define DC_WIN_BLEND_1WIN 0x710 +#define BLEND_CONTROL_FIX (0 << 2) +#define BLEND_CONTROL_ALPHA (1 << 2) +#define BLEND_COLOR_KEY_NONE (0 << 0) +#define BLEND_COLOR_KEY_0 (1 << 0) +#define BLEND_COLOR_KEY_1 (2 << 0) +#define BLEND_COLOR_KEY_BOTH (3 << 0) + #define DC_WIN_BLEND_2WIN_X 0x711 +#define BLEND_CONTROL_DEPENDENT (2 << 2) + #define DC_WIN_BLEND_2WIN_Y 0x712 #define DC_WIN_BLEND_3WIN_XY 0x713 @@ -575,8 +687,97 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define DC_WINBUF_SURFACE_KIND_BLOCK (2 << 0) #define DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(x) (((x) & 0x7) << 4) +#define DC_WINBUF_START_ADDR_HI 0x80d + +#define DC_WINBUF_CDE_CONTROL 0x82f +#define ENABLE_SURFACE (1 << 0) + #define DC_WINBUF_AD_UFLOW_STATUS 0xbca #define DC_WINBUF_BD_UFLOW_STATUS 0xdca #define DC_WINBUF_CD_UFLOW_STATUS 0xfca +/* Tegra186 and later */ +#define DC_DISP_CORE_SOR_SET_CONTROL(x) (0x403 + (x)) +#define PROTOCOL_MASK (0xf << 8) +#define PROTOCOL_SINGLE_TMDS_A (0x1 << 8) + +#define DC_WIN_CORE_WINDOWGROUP_SET_CONTROL 0x702 +#define OWNER_MASK (0xf << 0) +#define OWNER(x) (((x) & 0xf) << 0) + +#define DC_WIN_CROPPED_SIZE 0x706 + +#define DC_WIN_PLANAR_STORAGE 0x709 +#define PITCH(x) (((x) >> 6) & 0x1fff) + +#define DC_WIN_SET_PARAMS 0x70d +#define CLAMP_BEFORE_BLEND (1 << 15) +#define DEGAMMA_NONE (0 << 13) +#define DEGAMMA_SRGB (1 << 13) +#define DEGAMMA_YUV8_10 (2 << 13) +#define DEGAMMA_YUV12 (3 << 13) +#define INPUT_RANGE_BYPASS (0 << 10) +#define INPUT_RANGE_LIMITED (1 << 10) +#define INPUT_RANGE_FULL (2 << 10) +#define COLOR_SPACE_RGB (0 << 8) +#define COLOR_SPACE_YUV_601 (1 << 8) +#define COLOR_SPACE_YUV_709 (2 << 8) +#define COLOR_SPACE_YUV_2020 (3 << 8) + +#define DC_WIN_WINDOWGROUP_SET_CONTROL_INPUT_SCALER 0x70e +#define HORIZONTAL_TAPS_2 (1 << 3) +#define HORIZONTAL_TAPS_5 (4 << 3) +#define VERTICAL_TAPS_2 (1 << 0) +#define VERTICAL_TAPS_5 (4 << 0) + +#define DC_WIN_WINDOWGROUP_SET_INPUT_SCALER_USAGE 0x711 +#define INPUT_SCALER_USE422 (1 << 2) +#define INPUT_SCALER_VBYPASS (1 << 1) +#define INPUT_SCALER_HBYPASS (1 << 0) + +#define DC_WIN_BLEND_LAYER_CONTROL 0x716 +#define COLOR_KEY_NONE (0 << 25) +#define COLOR_KEY_SRC (1 << 25) +#define COLOR_KEY_DST (2 << 25) +#define BLEND_BYPASS (1 << 24) +#define K2(x) (((x) & 0xff) << 16) +#define K1(x) (((x) & 0xff) << 8) +#define WINDOW_LAYER_DEPTH(x) (((x) & 0xff) << 0) + +#define DC_WIN_BLEND_MATCH_SELECT 0x717 +#define BLEND_FACTOR_DST_ALPHA_ZERO (0 << 12) +#define BLEND_FACTOR_DST_ALPHA_ONE (1 << 12) +#define BLEND_FACTOR_DST_ALPHA_NEG_K1_TIMES_SRC (2 << 12) +#define BLEND_FACTOR_DST_ALPHA_K2 (3 << 12) +#define BLEND_FACTOR_SRC_ALPHA_ZERO (0 << 8) +#define BLEND_FACTOR_SRC_ALPHA_K1 (1 << 8) +#define BLEND_FACTOR_SRC_ALPHA_K2 (2 << 8) +#define BLEND_FACTOR_SRC_ALPHA_NEG_K1_TIMES_DST (3 << 8) +#define BLEND_FACTOR_DST_COLOR_ZERO (0 << 4) +#define BLEND_FACTOR_DST_COLOR_ONE (1 << 4) +#define BLEND_FACTOR_DST_COLOR_K1 (2 << 4) +#define BLEND_FACTOR_DST_COLOR_K2 (3 << 4) +#define BLEND_FACTOR_DST_COLOR_K1_TIMES_DST (4 << 4) +#define BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_DST (5 << 4) +#define BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC (6 << 4) +#define BLEND_FACTOR_DST_COLOR_NEG_K1 (7 << 4) +#define BLEND_FACTOR_SRC_COLOR_ZERO (0 << 0) +#define BLEND_FACTOR_SRC_COLOR_ONE (1 << 0) +#define BLEND_FACTOR_SRC_COLOR_K1 (2 << 0) +#define BLEND_FACTOR_SRC_COLOR_K1_TIMES_DST (3 << 0) +#define BLEND_FACTOR_SRC_COLOR_NEG_K1_TIMES_DST (4 << 0) +#define BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC (5 << 0) + +#define DC_WIN_BLEND_NOMATCH_SELECT 0x718 + +#define DC_WIN_PRECOMP_WGRP_PARAMS 0x724 +#define SWAP_UV (1 << 0) + +#define DC_WIN_WINDOW_SET_CONTROL 0x730 +#define CONTROL_CSC_ENABLE (1 << 5) + +#define DC_WINBUF_CROPPED_POINT 0x806 +#define OFFSET_Y(x) (((x) & 0xffff) << 16) +#define OFFSET_X(x) (((x) & 0xffff) << 0) + #endif /* TEGRA_DC_H */ diff --git a/drivers/gpu/drm/tegra/dpaux.c b/drivers/gpu/drm/tegra/dpaux.c index e4da041ba89b..d84e81ff36ad 100644 --- a/drivers/gpu/drm/tegra/dpaux.c +++ b/drivers/gpu/drm/tegra/dpaux.c @@ -15,6 +15,7 @@ #include <linux/pinctrl/pinconf-generic.h> #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinmux.h> +#include <linux/pm_runtime.h> #include <linux/platform_device.h> #include <linux/reset.h> #include <linux/regulator/consumer.h> @@ -321,6 +322,9 @@ static int tegra_dpaux_pad_config(struct tegra_dpaux *dpaux, unsigned function) case DPAUX_PADCTL_FUNC_I2C: value = DPAUX_HYBRID_PADCTL_I2C_SDA_INPUT_RCV | DPAUX_HYBRID_PADCTL_I2C_SCL_INPUT_RCV | + DPAUX_HYBRID_PADCTL_AUX_CMH(2) | + DPAUX_HYBRID_PADCTL_AUX_DRVZ(4) | + DPAUX_HYBRID_PADCTL_AUX_DRVI(0x18) | DPAUX_HYBRID_PADCTL_MODE_I2C; break; @@ -467,52 +471,37 @@ static int tegra_dpaux_probe(struct platform_device *pdev) return PTR_ERR(dpaux->clk); } - err = clk_prepare_enable(dpaux->clk); - if (err < 0) { - dev_err(&pdev->dev, "failed to enable module clock: %d\n", - err); - return err; - } - - if (dpaux->rst) - reset_control_deassert(dpaux->rst); - dpaux->clk_parent = devm_clk_get(&pdev->dev, "parent"); if (IS_ERR(dpaux->clk_parent)) { dev_err(&pdev->dev, "failed to get parent clock: %ld\n", PTR_ERR(dpaux->clk_parent)); - err = PTR_ERR(dpaux->clk_parent); - goto assert_reset; - } - - err = clk_prepare_enable(dpaux->clk_parent); - if (err < 0) { - dev_err(&pdev->dev, "failed to enable parent clock: %d\n", - err); - goto assert_reset; + return PTR_ERR(dpaux->clk_parent); } err = clk_set_rate(dpaux->clk_parent, 270000000); if (err < 0) { dev_err(&pdev->dev, "failed to set clock to 270 MHz: %d\n", err); - goto disable_parent_clk; + return err; } dpaux->vdd = devm_regulator_get(&pdev->dev, "vdd"); if (IS_ERR(dpaux->vdd)) { dev_err(&pdev->dev, "failed to get VDD supply: %ld\n", PTR_ERR(dpaux->vdd)); - err = PTR_ERR(dpaux->vdd); - goto disable_parent_clk; + return PTR_ERR(dpaux->vdd); } + platform_set_drvdata(pdev, dpaux); + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + err = devm_request_irq(dpaux->dev, dpaux->irq, tegra_dpaux_irq, 0, dev_name(dpaux->dev), dpaux); if (err < 0) { dev_err(dpaux->dev, "failed to request IRQ#%u: %d\n", dpaux->irq, err); - goto disable_parent_clk; + return err; } disable_irq(dpaux->irq); @@ -522,7 +511,7 @@ static int tegra_dpaux_probe(struct platform_device *pdev) err = drm_dp_aux_register(&dpaux->aux); if (err < 0) - goto disable_parent_clk; + return err; /* * Assume that by default the DPAUX/I2C pads will be used for HDMI, @@ -560,47 +549,97 @@ static int tegra_dpaux_probe(struct platform_device *pdev) list_add_tail(&dpaux->list, &dpaux_list); mutex_unlock(&dpaux_lock); - platform_set_drvdata(pdev, dpaux); - return 0; - -disable_parent_clk: - clk_disable_unprepare(dpaux->clk_parent); -assert_reset: - if (dpaux->rst) - reset_control_assert(dpaux->rst); - - clk_disable_unprepare(dpaux->clk); - - return err; } static int tegra_dpaux_remove(struct platform_device *pdev) { struct tegra_dpaux *dpaux = platform_get_drvdata(pdev); + cancel_work_sync(&dpaux->work); + /* make sure pads are powered down when not in use */ tegra_dpaux_pad_power_down(dpaux); + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); + drm_dp_aux_unregister(&dpaux->aux); mutex_lock(&dpaux_lock); list_del(&dpaux->list); mutex_unlock(&dpaux_lock); - cancel_work_sync(&dpaux->work); + return 0; +} - clk_disable_unprepare(dpaux->clk_parent); +#ifdef CONFIG_PM +static int tegra_dpaux_suspend(struct device *dev) +{ + struct tegra_dpaux *dpaux = dev_get_drvdata(dev); + int err = 0; + + if (dpaux->rst) { + err = reset_control_assert(dpaux->rst); + if (err < 0) { + dev_err(dev, "failed to assert reset: %d\n", err); + return err; + } + } - if (dpaux->rst) - reset_control_assert(dpaux->rst); + usleep_range(1000, 2000); + clk_disable_unprepare(dpaux->clk_parent); clk_disable_unprepare(dpaux->clk); + return err; +} + +static int tegra_dpaux_resume(struct device *dev) +{ + struct tegra_dpaux *dpaux = dev_get_drvdata(dev); + int err; + + err = clk_prepare_enable(dpaux->clk); + if (err < 0) { + dev_err(dev, "failed to enable clock: %d\n", err); + return err; + } + + err = clk_prepare_enable(dpaux->clk_parent); + if (err < 0) { + dev_err(dev, "failed to enable parent clock: %d\n", err); + goto disable_clk; + } + + usleep_range(1000, 2000); + + if (dpaux->rst) { + err = reset_control_deassert(dpaux->rst); + if (err < 0) { + dev_err(dev, "failed to deassert reset: %d\n", err); + goto disable_parent; + } + + usleep_range(1000, 2000); + } + return 0; + +disable_parent: + clk_disable_unprepare(dpaux->clk_parent); +disable_clk: + clk_disable_unprepare(dpaux->clk); + return err; } +#endif + +static const struct dev_pm_ops tegra_dpaux_pm_ops = { + SET_RUNTIME_PM_OPS(tegra_dpaux_suspend, tegra_dpaux_resume, NULL) +}; static const struct of_device_id tegra_dpaux_of_match[] = { + { .compatible = "nvidia,tegra186-dpaux", }, { .compatible = "nvidia,tegra210-dpaux", }, { .compatible = "nvidia,tegra124-dpaux", }, { }, @@ -611,6 +650,7 @@ struct platform_driver tegra_dpaux_driver = { .driver = { .name = "tegra-dpaux", .of_match_table = tegra_dpaux_of_match, + .pm = &tegra_dpaux_pm_ops, }, .probe = tegra_dpaux_probe, .remove = tegra_dpaux_remove, diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 52552b9b89ef..d50bddb2e447 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -33,97 +33,91 @@ struct tegra_drm_file { struct mutex lock; }; -static void tegra_atomic_schedule(struct tegra_drm *tegra, - struct drm_atomic_state *state) +static int tegra_atomic_check(struct drm_device *drm, + struct drm_atomic_state *state) { - tegra->commit.state = state; - schedule_work(&tegra->commit.work); -} + int err; -static void tegra_atomic_complete(struct tegra_drm *tegra, - struct drm_atomic_state *state) -{ - struct drm_device *drm = tegra->drm; + err = drm_atomic_helper_check_modeset(drm, state); + if (err < 0) + return err; - /* - * Everything below can be run asynchronously without the need to grab - * any modeset locks at all under one condition: It must be guaranteed - * that the asynchronous work has either been cancelled (if the driver - * supports it, which at least requires that the framebuffers get - * cleaned up with drm_atomic_helper_cleanup_planes()) or completed - * before the new state gets committed on the software side with - * drm_atomic_helper_swap_state(). - * - * This scheme allows new atomic state updates to be prepared and - * checked in parallel to the asynchronous completion of the previous - * update. Which is important since compositors need to figure out the - * composition of the next frame right after having submitted the - * current layout. - */ + err = drm_atomic_normalize_zpos(drm, state); + if (err < 0) + return err; - drm_atomic_helper_commit_modeset_disables(drm, state); - drm_atomic_helper_commit_modeset_enables(drm, state); - drm_atomic_helper_commit_planes(drm, state, - DRM_PLANE_COMMIT_ACTIVE_ONLY); + err = drm_atomic_helper_check_planes(drm, state); + if (err < 0) + return err; - drm_atomic_helper_wait_for_vblanks(drm, state); + if (state->legacy_cursor_update) + state->async_update = !drm_atomic_helper_async_check(drm, state); - drm_atomic_helper_cleanup_planes(drm, state); - drm_atomic_state_put(state); + return 0; } -static void tegra_atomic_work(struct work_struct *work) +static struct drm_atomic_state * +tegra_atomic_state_alloc(struct drm_device *drm) { - struct tegra_drm *tegra = container_of(work, struct tegra_drm, - commit.work); + struct tegra_atomic_state *state = kzalloc(sizeof(*state), GFP_KERNEL); + + if (!state || drm_atomic_state_init(drm, &state->base) < 0) { + kfree(state); + return NULL; + } - tegra_atomic_complete(tegra, tegra->commit.state); + return &state->base; } -static int tegra_atomic_commit(struct drm_device *drm, - struct drm_atomic_state *state, bool nonblock) +static void tegra_atomic_state_clear(struct drm_atomic_state *state) { - struct tegra_drm *tegra = drm->dev_private; - int err; - - err = drm_atomic_helper_prepare_planes(drm, state); - if (err) - return err; + struct tegra_atomic_state *tegra = to_tegra_atomic_state(state); - /* serialize outstanding nonblocking commits */ - mutex_lock(&tegra->commit.lock); - flush_work(&tegra->commit.work); - - /* - * This is the point of no return - everything below never fails except - * when the hw goes bonghits. Which means we can commit the new state on - * the software side now. - */ - - err = drm_atomic_helper_swap_state(state, true); - if (err) { - mutex_unlock(&tegra->commit.lock); - drm_atomic_helper_cleanup_planes(drm, state); - return err; - } - - drm_atomic_state_get(state); - if (nonblock) - tegra_atomic_schedule(tegra, state); - else - tegra_atomic_complete(tegra, state); + drm_atomic_state_default_clear(state); + tegra->clk_disp = NULL; + tegra->dc = NULL; + tegra->rate = 0; +} - mutex_unlock(&tegra->commit.lock); - return 0; +static void tegra_atomic_state_free(struct drm_atomic_state *state) +{ + drm_atomic_state_default_release(state); + kfree(state); } -static const struct drm_mode_config_funcs tegra_drm_mode_funcs = { +static const struct drm_mode_config_funcs tegra_drm_mode_config_funcs = { .fb_create = tegra_fb_create, #ifdef CONFIG_DRM_FBDEV_EMULATION - .output_poll_changed = tegra_fb_output_poll_changed, + .output_poll_changed = drm_fb_helper_output_poll_changed, #endif - .atomic_check = drm_atomic_helper_check, - .atomic_commit = tegra_atomic_commit, + .atomic_check = tegra_atomic_check, + .atomic_commit = drm_atomic_helper_commit, + .atomic_state_alloc = tegra_atomic_state_alloc, + .atomic_state_clear = tegra_atomic_state_clear, + .atomic_state_free = tegra_atomic_state_free, +}; + +static void tegra_atomic_commit_tail(struct drm_atomic_state *old_state) +{ + struct drm_device *drm = old_state->dev; + struct tegra_drm *tegra = drm->dev_private; + + if (tegra->hub) { + drm_atomic_helper_commit_modeset_disables(drm, old_state); + tegra_display_hub_atomic_commit(drm, old_state); + drm_atomic_helper_commit_planes(drm, old_state, 0); + drm_atomic_helper_commit_modeset_enables(drm, old_state); + drm_atomic_helper_commit_hw_done(old_state); + drm_atomic_helper_wait_for_vblanks(drm, old_state); + drm_atomic_helper_cleanup_planes(drm, old_state); + } else { + drm_atomic_helper_commit_tail_rpm(old_state); + } +} + +static const struct drm_mode_config_helper_funcs +tegra_drm_mode_config_helpers = { + .atomic_commit_tail = tegra_atomic_commit_tail, }; static int tegra_drm_load(struct drm_device *drm, unsigned long flags) @@ -172,9 +166,6 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) mutex_init(&tegra->clients_lock); INIT_LIST_HEAD(&tegra->clients); - mutex_init(&tegra->commit.lock); - INIT_WORK(&tegra->commit.work, tegra_atomic_work); - drm->dev_private = tegra; tegra->drm = drm; @@ -188,7 +179,8 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) drm->mode_config.allow_fb_modifiers = true; - drm->mode_config.funcs = &tegra_drm_mode_funcs; + drm->mode_config.funcs = &tegra_drm_mode_config_funcs; + drm->mode_config.helper_private = &tegra_drm_mode_config_helpers; err = tegra_drm_fb_prepare(drm); if (err < 0) @@ -200,6 +192,12 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) if (err < 0) goto fbdev; + if (tegra->hub) { + err = tegra_display_hub_prepare(tegra->hub); + if (err < 0) + goto device; + } + /* * We don't use the drm_irq_install() helpers provided by the DRM * core, so we need to set this manually in order to allow the @@ -212,16 +210,19 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) err = drm_vblank_init(drm, drm->mode_config.num_crtc); if (err < 0) - goto device; + goto hub; drm_mode_config_reset(drm); err = tegra_drm_fb_init(drm); if (err < 0) - goto device; + goto hub; return 0; +hub: + if (tegra->hub) + tegra_display_hub_cleanup(tegra->hub); device: host1x_device_exit(device); fbdev: @@ -286,15 +287,6 @@ static void tegra_drm_context_free(struct tegra_drm_context *context) kfree(context); } -static void tegra_drm_lastclose(struct drm_device *drm) -{ -#ifdef CONFIG_DRM_FBDEV_EMULATION - struct tegra_drm *tegra = drm->dev_private; - - tegra_fbdev_restore_mode(tegra->fbdev); -#endif -} - static struct host1x_bo * host1x_bo_lookup(struct drm_file *file, u32 handle) { @@ -660,7 +652,8 @@ static int tegra_syncpt_wait(struct drm_device *drm, void *data, if (!sp) return -EINVAL; - return host1x_syncpt_wait(sp, args->thresh, args->timeout, + return host1x_syncpt_wait(sp, args->thresh, + msecs_to_jiffies(args->timeout), &args->value); } @@ -1100,7 +1093,7 @@ static struct drm_driver tegra_drm_driver = { .unload = tegra_drm_unload, .open = tegra_drm_open, .postclose = tegra_drm_postclose, - .lastclose = tegra_drm_lastclose, + .lastclose = drm_fb_helper_lastclose, #if defined(CONFIG_DEBUG_FS) .debugfs_init = tegra_debugfs_init, @@ -1148,8 +1141,7 @@ int tegra_drm_unregister_client(struct tegra_drm *tegra, return 0; } -void *tegra_drm_alloc(struct tegra_drm *tegra, size_t size, - dma_addr_t *dma) +void *tegra_drm_alloc(struct tegra_drm *tegra, size_t size, dma_addr_t *dma) { struct iova *alloc; void *virt; @@ -1317,6 +1309,10 @@ static const struct of_device_id host1x_drm_subdevs[] = { { .compatible = "nvidia,tegra210-sor", }, { .compatible = "nvidia,tegra210-sor1", }, { .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 */ } }; @@ -1332,6 +1328,7 @@ static struct host1x_driver host1x_drm_driver = { }; static struct platform_driver * const drivers[] = { + &tegra_display_hub_driver, &tegra_dc_driver, &tegra_hdmi_driver, &tegra_dsi_driver, diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index ddae331ad8b6..73b661ce7086 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -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,10 +42,25 @@ 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; struct iommu_domain *domain; + struct iommu_group *group; struct mutex mm_lock; struct drm_mm mm; @@ -62,11 +79,7 @@ struct tegra_drm { unsigned int pitch_align; - struct { - struct drm_atomic_state *state; - struct work_struct work; - struct mutex lock; - } commit; + struct tegra_display_hub *hub; struct drm_atomic_state *state; }; @@ -152,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 @@ -188,11 +203,8 @@ int tegra_drm_fb_init(struct drm_device *drm); void tegra_drm_fb_exit(struct drm_device *drm); void tegra_drm_fb_suspend(struct drm_device *drm); void tegra_drm_fb_resume(struct drm_device *drm); -#ifdef CONFIG_DRM_FBDEV_EMULATION -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; diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index 046649ec9441..4d2ed966f9e3 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -65,8 +65,6 @@ struct tegra_dsi { struct clk *clk; struct drm_info_list *debugfs_files; - struct drm_minor *minor; - struct dentry *debugfs; unsigned long flags; enum mipi_dsi_pixel_format format; @@ -122,12 +120,89 @@ static inline void tegra_dsi_writel(struct tegra_dsi *dsi, u32 value, writel(value, dsi->regs + (offset << 2)); } +#define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name } + +static const struct debugfs_reg32 tegra_dsi_regs[] = { + DEBUGFS_REG32(DSI_INCR_SYNCPT), + DEBUGFS_REG32(DSI_INCR_SYNCPT_CONTROL), + DEBUGFS_REG32(DSI_INCR_SYNCPT_ERROR), + DEBUGFS_REG32(DSI_CTXSW), + DEBUGFS_REG32(DSI_RD_DATA), + DEBUGFS_REG32(DSI_WR_DATA), + DEBUGFS_REG32(DSI_POWER_CONTROL), + DEBUGFS_REG32(DSI_INT_ENABLE), + DEBUGFS_REG32(DSI_INT_STATUS), + DEBUGFS_REG32(DSI_INT_MASK), + DEBUGFS_REG32(DSI_HOST_CONTROL), + DEBUGFS_REG32(DSI_CONTROL), + DEBUGFS_REG32(DSI_SOL_DELAY), + DEBUGFS_REG32(DSI_MAX_THRESHOLD), + DEBUGFS_REG32(DSI_TRIGGER), + DEBUGFS_REG32(DSI_TX_CRC), + DEBUGFS_REG32(DSI_STATUS), + DEBUGFS_REG32(DSI_INIT_SEQ_CONTROL), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_0), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_1), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_2), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_3), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_4), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_5), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_6), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_7), + DEBUGFS_REG32(DSI_PKT_SEQ_0_LO), + DEBUGFS_REG32(DSI_PKT_SEQ_0_HI), + DEBUGFS_REG32(DSI_PKT_SEQ_1_LO), + DEBUGFS_REG32(DSI_PKT_SEQ_1_HI), + DEBUGFS_REG32(DSI_PKT_SEQ_2_LO), + DEBUGFS_REG32(DSI_PKT_SEQ_2_HI), + DEBUGFS_REG32(DSI_PKT_SEQ_3_LO), + DEBUGFS_REG32(DSI_PKT_SEQ_3_HI), + DEBUGFS_REG32(DSI_PKT_SEQ_4_LO), + DEBUGFS_REG32(DSI_PKT_SEQ_4_HI), + DEBUGFS_REG32(DSI_PKT_SEQ_5_LO), + DEBUGFS_REG32(DSI_PKT_SEQ_5_HI), + DEBUGFS_REG32(DSI_DCS_CMDS), + DEBUGFS_REG32(DSI_PKT_LEN_0_1), + DEBUGFS_REG32(DSI_PKT_LEN_2_3), + DEBUGFS_REG32(DSI_PKT_LEN_4_5), + DEBUGFS_REG32(DSI_PKT_LEN_6_7), + DEBUGFS_REG32(DSI_PHY_TIMING_0), + DEBUGFS_REG32(DSI_PHY_TIMING_1), + DEBUGFS_REG32(DSI_PHY_TIMING_2), + DEBUGFS_REG32(DSI_BTA_TIMING), + DEBUGFS_REG32(DSI_TIMEOUT_0), + DEBUGFS_REG32(DSI_TIMEOUT_1), + DEBUGFS_REG32(DSI_TO_TALLY), + DEBUGFS_REG32(DSI_PAD_CONTROL_0), + DEBUGFS_REG32(DSI_PAD_CONTROL_CD), + DEBUGFS_REG32(DSI_PAD_CD_STATUS), + DEBUGFS_REG32(DSI_VIDEO_MODE_CONTROL), + DEBUGFS_REG32(DSI_PAD_CONTROL_1), + DEBUGFS_REG32(DSI_PAD_CONTROL_2), + DEBUGFS_REG32(DSI_PAD_CONTROL_3), + DEBUGFS_REG32(DSI_PAD_CONTROL_4), + DEBUGFS_REG32(DSI_GANGED_MODE_CONTROL), + DEBUGFS_REG32(DSI_GANGED_MODE_START), + DEBUGFS_REG32(DSI_GANGED_MODE_SIZE), + DEBUGFS_REG32(DSI_RAW_DATA_BYTE_COUNT), + DEBUGFS_REG32(DSI_ULTRA_LOW_POWER_CONTROL), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_8), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_9), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_10), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_11), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_12), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_13), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_14), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_15), +}; + static int tegra_dsi_show_regs(struct seq_file *s, void *data) { struct drm_info_node *node = s->private; struct tegra_dsi *dsi = node->info_ent->data; struct drm_crtc *crtc = dsi->output.encoder.crtc; struct drm_device *drm = node->minor->dev; + unsigned int i; int err = 0; drm_modeset_lock_all(drm); @@ -137,93 +212,12 @@ static int tegra_dsi_show_regs(struct seq_file *s, void *data) goto unlock; } -#define DUMP_REG(name) \ - seq_printf(s, "%-32s %#05x %08x\n", #name, name, \ - tegra_dsi_readl(dsi, name)) - - DUMP_REG(DSI_INCR_SYNCPT); - DUMP_REG(DSI_INCR_SYNCPT_CONTROL); - DUMP_REG(DSI_INCR_SYNCPT_ERROR); - DUMP_REG(DSI_CTXSW); - DUMP_REG(DSI_RD_DATA); - DUMP_REG(DSI_WR_DATA); - DUMP_REG(DSI_POWER_CONTROL); - DUMP_REG(DSI_INT_ENABLE); - DUMP_REG(DSI_INT_STATUS); - DUMP_REG(DSI_INT_MASK); - DUMP_REG(DSI_HOST_CONTROL); - DUMP_REG(DSI_CONTROL); - DUMP_REG(DSI_SOL_DELAY); - DUMP_REG(DSI_MAX_THRESHOLD); - DUMP_REG(DSI_TRIGGER); - DUMP_REG(DSI_TX_CRC); - DUMP_REG(DSI_STATUS); - - DUMP_REG(DSI_INIT_SEQ_CONTROL); - DUMP_REG(DSI_INIT_SEQ_DATA_0); - DUMP_REG(DSI_INIT_SEQ_DATA_1); - DUMP_REG(DSI_INIT_SEQ_DATA_2); - DUMP_REG(DSI_INIT_SEQ_DATA_3); - DUMP_REG(DSI_INIT_SEQ_DATA_4); - DUMP_REG(DSI_INIT_SEQ_DATA_5); - DUMP_REG(DSI_INIT_SEQ_DATA_6); - DUMP_REG(DSI_INIT_SEQ_DATA_7); - - DUMP_REG(DSI_PKT_SEQ_0_LO); - DUMP_REG(DSI_PKT_SEQ_0_HI); - DUMP_REG(DSI_PKT_SEQ_1_LO); - DUMP_REG(DSI_PKT_SEQ_1_HI); - DUMP_REG(DSI_PKT_SEQ_2_LO); - DUMP_REG(DSI_PKT_SEQ_2_HI); - DUMP_REG(DSI_PKT_SEQ_3_LO); - DUMP_REG(DSI_PKT_SEQ_3_HI); - DUMP_REG(DSI_PKT_SEQ_4_LO); - DUMP_REG(DSI_PKT_SEQ_4_HI); - DUMP_REG(DSI_PKT_SEQ_5_LO); - DUMP_REG(DSI_PKT_SEQ_5_HI); - - DUMP_REG(DSI_DCS_CMDS); - - DUMP_REG(DSI_PKT_LEN_0_1); - DUMP_REG(DSI_PKT_LEN_2_3); - DUMP_REG(DSI_PKT_LEN_4_5); - DUMP_REG(DSI_PKT_LEN_6_7); - - DUMP_REG(DSI_PHY_TIMING_0); - DUMP_REG(DSI_PHY_TIMING_1); - DUMP_REG(DSI_PHY_TIMING_2); - DUMP_REG(DSI_BTA_TIMING); - - DUMP_REG(DSI_TIMEOUT_0); - DUMP_REG(DSI_TIMEOUT_1); - DUMP_REG(DSI_TO_TALLY); - - DUMP_REG(DSI_PAD_CONTROL_0); - DUMP_REG(DSI_PAD_CONTROL_CD); - DUMP_REG(DSI_PAD_CD_STATUS); - DUMP_REG(DSI_VIDEO_MODE_CONTROL); - DUMP_REG(DSI_PAD_CONTROL_1); - DUMP_REG(DSI_PAD_CONTROL_2); - DUMP_REG(DSI_PAD_CONTROL_3); - DUMP_REG(DSI_PAD_CONTROL_4); - - DUMP_REG(DSI_GANGED_MODE_CONTROL); - DUMP_REG(DSI_GANGED_MODE_START); - DUMP_REG(DSI_GANGED_MODE_SIZE); - - DUMP_REG(DSI_RAW_DATA_BYTE_COUNT); - DUMP_REG(DSI_ULTRA_LOW_POWER_CONTROL); - - DUMP_REG(DSI_INIT_SEQ_DATA_8); - DUMP_REG(DSI_INIT_SEQ_DATA_9); - DUMP_REG(DSI_INIT_SEQ_DATA_10); - DUMP_REG(DSI_INIT_SEQ_DATA_11); - DUMP_REG(DSI_INIT_SEQ_DATA_12); - DUMP_REG(DSI_INIT_SEQ_DATA_13); - DUMP_REG(DSI_INIT_SEQ_DATA_14); - DUMP_REG(DSI_INIT_SEQ_DATA_15); - -#undef DUMP_REG + for (i = 0; i < ARRAY_SIZE(tegra_dsi_regs); i++) { + unsigned int offset = tegra_dsi_regs[i].offset; + + seq_printf(s, "%-32s %#05x %08x\n", tegra_dsi_regs[i].name, + offset, tegra_dsi_readl(dsi, offset)); + } unlock: drm_modeset_unlock_all(drm); @@ -234,58 +228,46 @@ static struct drm_info_list debugfs_files[] = { { "regs", tegra_dsi_show_regs, 0, NULL }, }; -static int tegra_dsi_debugfs_init(struct tegra_dsi *dsi, - struct drm_minor *minor) +static int tegra_dsi_late_register(struct drm_connector *connector) { - const char *name = dev_name(dsi->dev); - unsigned int i; + struct tegra_output *output = connector_to_output(connector); + unsigned int i, count = ARRAY_SIZE(debugfs_files); + struct drm_minor *minor = connector->dev->primary; + struct dentry *root = connector->debugfs_entry; + struct tegra_dsi *dsi = to_dsi(output); int err; - dsi->debugfs = debugfs_create_dir(name, minor->debugfs_root); - if (!dsi->debugfs) - return -ENOMEM; - dsi->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files), GFP_KERNEL); - if (!dsi->debugfs_files) { - err = -ENOMEM; - goto remove; - } + if (!dsi->debugfs_files) + return -ENOMEM; - for (i = 0; i < ARRAY_SIZE(debugfs_files); i++) + for (i = 0; i < count; i++) dsi->debugfs_files[i].data = dsi; - err = drm_debugfs_create_files(dsi->debugfs_files, - ARRAY_SIZE(debugfs_files), - dsi->debugfs, minor); + err = drm_debugfs_create_files(dsi->debugfs_files, count, root, minor); if (err < 0) goto free; - dsi->minor = minor; - return 0; free: kfree(dsi->debugfs_files); dsi->debugfs_files = NULL; -remove: - debugfs_remove(dsi->debugfs); - dsi->debugfs = NULL; return err; } -static void tegra_dsi_debugfs_exit(struct tegra_dsi *dsi) +static void tegra_dsi_early_unregister(struct drm_connector *connector) { - drm_debugfs_remove_files(dsi->debugfs_files, ARRAY_SIZE(debugfs_files), - dsi->minor); - dsi->minor = NULL; + struct tegra_output *output = connector_to_output(connector); + unsigned int count = ARRAY_SIZE(debugfs_files); + struct tegra_dsi *dsi = to_dsi(output); + drm_debugfs_remove_files(dsi->debugfs_files, count, + connector->dev->primary); kfree(dsi->debugfs_files); dsi->debugfs_files = NULL; - - debugfs_remove(dsi->debugfs); - dsi->debugfs = NULL; } #define PKT_ID0(id) ((((id) & 0x3f) << 3) | (1 << 9)) @@ -827,6 +809,8 @@ static const struct drm_connector_funcs tegra_dsi_connector_funcs = { .destroy = tegra_output_connector_destroy, .atomic_duplicate_state = tegra_dsi_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .late_register = tegra_dsi_late_register, + .early_unregister = tegra_dsi_early_unregister, }; static enum drm_mode_status @@ -1080,12 +1064,6 @@ static int tegra_dsi_init(struct host1x_client *client) dsi->output.encoder.possible_crtcs = 0x3; } - if (IS_ENABLED(CONFIG_DEBUG_FS)) { - err = tegra_dsi_debugfs_init(dsi, drm->primary); - if (err < 0) - dev_err(dsi->dev, "debugfs setup failed: %d\n", err); - } - return 0; } @@ -1094,10 +1072,6 @@ static int tegra_dsi_exit(struct host1x_client *client) struct tegra_dsi *dsi = host1x_client_to_dsi(client); tegra_output_exit(&dsi->output); - - if (IS_ENABLED(CONFIG_DEBUG_FS)) - tegra_dsi_debugfs_exit(dsi); - regulator_disable(dsi->vdd); return 0; diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index 80540c1c66dc..001cb77e2f59 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -54,17 +54,40 @@ int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer, struct tegra_fb *fb = to_tegra_fb(framebuffer); uint64_t modifier = fb->base.modifier; - switch (fourcc_mod_tegra_mod(modifier)) { - case NV_FORMAT_MOD_TEGRA_TILED: + switch (modifier) { + case DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED: tiling->mode = TEGRA_BO_TILING_MODE_TILED; tiling->value = 0; break; - case NV_FORMAT_MOD_TEGRA_16BX2_BLOCK(0): + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0): tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; - tiling->value = fourcc_mod_tegra_param(modifier); - if (tiling->value > 5) - return -EINVAL; + tiling->value = 0; + break; + + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1): + tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; + tiling->value = 1; + break; + + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2): + tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; + tiling->value = 2; + break; + + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3): + tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; + tiling->value = 3; + break; + + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4): + tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; + tiling->value = 4; + break; + + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5): + tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; + tiling->value = 5; break; default: @@ -230,6 +253,7 @@ static int tegra_fbdev_probe(struct drm_fb_helper *helper, cmd.height = sizes->surface_height; cmd.pitches[0] = round_up(sizes->surface_width * bytes_per_pixel, tegra->pitch_align); + cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth); @@ -361,20 +385,6 @@ static void tegra_fbdev_exit(struct tegra_fbdev *fbdev) drm_fb_helper_fini(&fbdev->base); tegra_fbdev_free(fbdev); } - -void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev) -{ - if (fbdev) - drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev->base); -} - -void tegra_fb_output_poll_changed(struct drm_device *drm) -{ - struct tegra_drm *tegra = drm->dev_private; - - if (tegra->fbdev) - drm_fb_helper_hotplug_event(&tegra->fbdev->base); -} #endif int tegra_drm_fb_prepare(struct drm_device *drm) diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index ab1e53d434e8..49b9bf28f872 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -114,7 +114,7 @@ static const struct host1x_bo_ops tegra_bo_ops = { static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo) { int prot = IOMMU_READ | IOMMU_WRITE; - ssize_t err; + int err; if (bo->mm) return -EBUSY; @@ -128,22 +128,21 @@ static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo) err = drm_mm_insert_node_generic(&tegra->mm, bo->mm, bo->gem.size, PAGE_SIZE, 0, 0); if (err < 0) { - dev_err(tegra->drm->dev, "out of I/O virtual memory: %zd\n", + dev_err(tegra->drm->dev, "out of I/O virtual memory: %d\n", err); goto unlock; } bo->paddr = bo->mm->start; - err = iommu_map_sg(tegra->domain, bo->paddr, bo->sgt->sgl, - bo->sgt->nents, prot); - if (err < 0) { - dev_err(tegra->drm->dev, "failed to map buffer: %zd\n", err); + bo->size = iommu_map_sg(tegra->domain, bo->paddr, bo->sgt->sgl, + bo->sgt->nents, prot); + if (!bo->size) { + dev_err(tegra->drm->dev, "failed to map buffer\n"); + err = -ENOMEM; goto remove; } - bo->size = err; - mutex_unlock(&tegra->mm_lock); return 0; diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index 6434b3d3d1ba..784739a9f497 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -79,8 +79,6 @@ struct tegra_hdmi { bool dvi; struct drm_info_list *debugfs_files; - struct drm_minor *minor; - struct dentry *debugfs; }; static inline struct tegra_hdmi * @@ -910,6 +908,249 @@ tegra_hdmi_connector_detect(struct drm_connector *connector, bool force) return status; } +#define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name } + +static const struct debugfs_reg32 tegra_hdmi_regs[] = { + DEBUGFS_REG32(HDMI_CTXSW), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_STATE0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_STATE1), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_STATE2), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_AN_MSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_AN_LSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CN_MSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CN_LSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_AKSV_MSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_AKSV_LSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_BKSV_MSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_BKSV_LSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CKSV_MSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CKSV_LSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_DKSV_MSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_DKSV_LSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CTRL), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CMODE), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_MPRIME_MSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_MPRIME_LSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_SPRIME_MSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_SPRIME_LSB2), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_SPRIME_LSB1), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_RI), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CS_MSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CS_LSB), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_EMU0), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_EMU_RDATA0), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_EMU1), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_EMU2), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_STATUS), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_STATUS), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_CTRL), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_STATUS), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_HEADER), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK0_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK0_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK1_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK1_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK2_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK2_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK3_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK3_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_CTRL), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_CTRL), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_VSYNC_KEEPOUT), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_VSYNC_WINDOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GCP_CTRL), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GCP_STATUS), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GCP_SUBPACK), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_CHANNEL_STATUS1), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_CHANNEL_STATUS2), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_EMU0), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_EMU1), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_EMU1_RDATA), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_SPARE), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_SPDIF_CHN_STATUS1), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_SPDIF_CHN_STATUS2), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_HDCPRIF_ROM_CTRL), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CAP), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_PWR), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_TEST), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_PLL0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_PLL1), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_PLL2), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CSTM), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_LVDS), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CRCA), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CRCB), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_BLANK), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_CTL), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(0)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(1)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(2)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(3)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(4)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(5)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(6)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(7)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(8)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(9)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(10)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(11)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(12)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(13)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(14)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(15)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_VCRCA0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_VCRCA1), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CCRCA0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CCRCA1), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_EDATAA0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_EDATAA1), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_COUNTA0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_COUNTA1), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_DEBUGA0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_DEBUGA1), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_TRIG), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_MSCHECK), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_DEBUG0), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_DEBUG1), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_DEBUG2), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(0)), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(1)), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(2)), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(3)), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(4)), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(5)), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(6)), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_PULSE_WIDTH), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_THRESHOLD), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_CNTRL0), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_N), + DEBUGFS_REG32(HDMI_NV_PDISP_HDCPRIF_ROM_TIMING), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_REFCLK), + DEBUGFS_REG32(HDMI_NV_PDISP_CRC_CONTROL), + DEBUGFS_REG32(HDMI_NV_PDISP_INPUT_CONTROL), + DEBUGFS_REG32(HDMI_NV_PDISP_SCRATCH), + DEBUGFS_REG32(HDMI_NV_PDISP_PE_CURRENT), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_CTRL), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_DEBUG0), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_DEBUG1), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_DEBUG2), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_HDCP_KEY_0), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_HDCP_KEY_1), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_HDCP_KEY_2), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_HDCP_KEY_3), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_HDCP_KEY_TRIG), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_SKEY_INDEX), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_CNTRL0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_SPARE0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH1), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE), + DEBUGFS_REG32(HDMI_NV_PDISP_INT_STATUS), + DEBUGFS_REG32(HDMI_NV_PDISP_INT_MASK), + DEBUGFS_REG32(HDMI_NV_PDISP_INT_ENABLE), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT), +}; + +static int tegra_hdmi_show_regs(struct seq_file *s, void *data) +{ + struct drm_info_node *node = s->private; + struct tegra_hdmi *hdmi = node->info_ent->data; + struct drm_crtc *crtc = hdmi->output.encoder.crtc; + struct drm_device *drm = node->minor->dev; + unsigned int i; + int err = 0; + + drm_modeset_lock_all(drm); + + if (!crtc || !crtc->state->active) { + err = -EBUSY; + goto unlock; + } + + for (i = 0; i < ARRAY_SIZE(tegra_hdmi_regs); i++) { + unsigned int offset = tegra_hdmi_regs[i].offset; + + seq_printf(s, "%-56s %#05x %08x\n", tegra_hdmi_regs[i].name, + offset, tegra_hdmi_readl(hdmi, offset)); + } + +unlock: + drm_modeset_unlock_all(drm); + return err; +} + +static struct drm_info_list debugfs_files[] = { + { "regs", tegra_hdmi_show_regs, 0, NULL }, +}; + +static int tegra_hdmi_late_register(struct drm_connector *connector) +{ + struct tegra_output *output = connector_to_output(connector); + unsigned int i, count = ARRAY_SIZE(debugfs_files); + struct drm_minor *minor = connector->dev->primary; + struct dentry *root = connector->debugfs_entry; + struct tegra_hdmi *hdmi = to_hdmi(output); + int err; + + hdmi->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files), + GFP_KERNEL); + if (!hdmi->debugfs_files) + return -ENOMEM; + + for (i = 0; i < count; i++) + hdmi->debugfs_files[i].data = hdmi; + + err = drm_debugfs_create_files(hdmi->debugfs_files, count, root, minor); + if (err < 0) + goto free; + + return 0; + +free: + kfree(hdmi->debugfs_files); + hdmi->debugfs_files = NULL; + + return err; +} + +static void tegra_hdmi_early_unregister(struct drm_connector *connector) +{ + struct tegra_output *output = connector_to_output(connector); + struct drm_minor *minor = connector->dev->primary; + unsigned int count = ARRAY_SIZE(debugfs_files); + struct tegra_hdmi *hdmi = to_hdmi(output); + + drm_debugfs_remove_files(hdmi->debugfs_files, count, minor); + kfree(hdmi->debugfs_files); + hdmi->debugfs_files = NULL; +} + static const struct drm_connector_funcs tegra_hdmi_connector_funcs = { .reset = drm_atomic_helper_connector_reset, .detect = tegra_hdmi_connector_detect, @@ -917,6 +1158,8 @@ static const struct drm_connector_funcs tegra_hdmi_connector_funcs = { .destroy = tegra_output_connector_destroy, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .late_register = tegra_hdmi_late_register, + .early_unregister = tegra_hdmi_early_unregister, }; static enum drm_mode_status @@ -1225,254 +1468,6 @@ static const struct drm_encoder_helper_funcs tegra_hdmi_encoder_helper_funcs = { .atomic_check = tegra_hdmi_encoder_atomic_check, }; -static int tegra_hdmi_show_regs(struct seq_file *s, void *data) -{ - struct drm_info_node *node = s->private; - struct tegra_hdmi *hdmi = node->info_ent->data; - struct drm_crtc *crtc = hdmi->output.encoder.crtc; - struct drm_device *drm = node->minor->dev; - int err = 0; - - drm_modeset_lock_all(drm); - - if (!crtc || !crtc->state->active) { - err = -EBUSY; - goto unlock; - } - -#define DUMP_REG(name) \ - seq_printf(s, "%-56s %#05x %08x\n", #name, name, \ - tegra_hdmi_readl(hdmi, name)) - - DUMP_REG(HDMI_CTXSW); - DUMP_REG(HDMI_NV_PDISP_SOR_STATE0); - DUMP_REG(HDMI_NV_PDISP_SOR_STATE1); - DUMP_REG(HDMI_NV_PDISP_SOR_STATE2); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_AN_MSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_AN_LSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_CN_MSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_CN_LSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_AKSV_MSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_AKSV_LSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_BKSV_MSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_BKSV_LSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_CKSV_MSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_CKSV_LSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_DKSV_MSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_DKSV_LSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_CTRL); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_CMODE); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_MPRIME_MSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_MPRIME_LSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_SPRIME_MSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_SPRIME_LSB2); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_SPRIME_LSB1); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_RI); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_CS_MSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_CS_LSB); - DUMP_REG(HDMI_NV_PDISP_HDMI_AUDIO_EMU0); - DUMP_REG(HDMI_NV_PDISP_HDMI_AUDIO_EMU_RDATA0); - DUMP_REG(HDMI_NV_PDISP_HDMI_AUDIO_EMU1); - DUMP_REG(HDMI_NV_PDISP_HDMI_AUDIO_EMU2); - DUMP_REG(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL); - DUMP_REG(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_STATUS); - DUMP_REG(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER); - DUMP_REG(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL); - DUMP_REG(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_STATUS); - DUMP_REG(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER); - DUMP_REG(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_CTRL); - DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_STATUS); - DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_HEADER); - DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK0_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK0_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK1_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK1_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK2_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK2_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK3_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK3_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_CTRL); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_CTRL); - DUMP_REG(HDMI_NV_PDISP_HDMI_VSYNC_KEEPOUT); - DUMP_REG(HDMI_NV_PDISP_HDMI_VSYNC_WINDOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_GCP_CTRL); - DUMP_REG(HDMI_NV_PDISP_HDMI_GCP_STATUS); - DUMP_REG(HDMI_NV_PDISP_HDMI_GCP_SUBPACK); - DUMP_REG(HDMI_NV_PDISP_HDMI_CHANNEL_STATUS1); - DUMP_REG(HDMI_NV_PDISP_HDMI_CHANNEL_STATUS2); - DUMP_REG(HDMI_NV_PDISP_HDMI_EMU0); - DUMP_REG(HDMI_NV_PDISP_HDMI_EMU1); - DUMP_REG(HDMI_NV_PDISP_HDMI_EMU1_RDATA); - DUMP_REG(HDMI_NV_PDISP_HDMI_SPARE); - DUMP_REG(HDMI_NV_PDISP_HDMI_SPDIF_CHN_STATUS1); - DUMP_REG(HDMI_NV_PDISP_HDMI_SPDIF_CHN_STATUS2); - DUMP_REG(HDMI_NV_PDISP_HDMI_HDCPRIF_ROM_CTRL); - DUMP_REG(HDMI_NV_PDISP_SOR_CAP); - DUMP_REG(HDMI_NV_PDISP_SOR_PWR); - DUMP_REG(HDMI_NV_PDISP_SOR_TEST); - DUMP_REG(HDMI_NV_PDISP_SOR_PLL0); - DUMP_REG(HDMI_NV_PDISP_SOR_PLL1); - DUMP_REG(HDMI_NV_PDISP_SOR_PLL2); - DUMP_REG(HDMI_NV_PDISP_SOR_CSTM); - DUMP_REG(HDMI_NV_PDISP_SOR_LVDS); - DUMP_REG(HDMI_NV_PDISP_SOR_CRCA); - DUMP_REG(HDMI_NV_PDISP_SOR_CRCB); - DUMP_REG(HDMI_NV_PDISP_SOR_BLANK); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_CTL); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(0)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(1)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(2)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(3)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(4)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(5)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(6)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(7)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(8)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(9)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(10)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(11)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(12)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(13)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(14)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(15)); - DUMP_REG(HDMI_NV_PDISP_SOR_VCRCA0); - DUMP_REG(HDMI_NV_PDISP_SOR_VCRCA1); - DUMP_REG(HDMI_NV_PDISP_SOR_CCRCA0); - DUMP_REG(HDMI_NV_PDISP_SOR_CCRCA1); - DUMP_REG(HDMI_NV_PDISP_SOR_EDATAA0); - DUMP_REG(HDMI_NV_PDISP_SOR_EDATAA1); - DUMP_REG(HDMI_NV_PDISP_SOR_COUNTA0); - DUMP_REG(HDMI_NV_PDISP_SOR_COUNTA1); - DUMP_REG(HDMI_NV_PDISP_SOR_DEBUGA0); - DUMP_REG(HDMI_NV_PDISP_SOR_DEBUGA1); - DUMP_REG(HDMI_NV_PDISP_SOR_TRIG); - DUMP_REG(HDMI_NV_PDISP_SOR_MSCHECK); - DUMP_REG(HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT); - DUMP_REG(HDMI_NV_PDISP_AUDIO_DEBUG0); - DUMP_REG(HDMI_NV_PDISP_AUDIO_DEBUG1); - DUMP_REG(HDMI_NV_PDISP_AUDIO_DEBUG2); - DUMP_REG(HDMI_NV_PDISP_AUDIO_FS(0)); - DUMP_REG(HDMI_NV_PDISP_AUDIO_FS(1)); - DUMP_REG(HDMI_NV_PDISP_AUDIO_FS(2)); - DUMP_REG(HDMI_NV_PDISP_AUDIO_FS(3)); - DUMP_REG(HDMI_NV_PDISP_AUDIO_FS(4)); - DUMP_REG(HDMI_NV_PDISP_AUDIO_FS(5)); - DUMP_REG(HDMI_NV_PDISP_AUDIO_FS(6)); - DUMP_REG(HDMI_NV_PDISP_AUDIO_PULSE_WIDTH); - DUMP_REG(HDMI_NV_PDISP_AUDIO_THRESHOLD); - DUMP_REG(HDMI_NV_PDISP_AUDIO_CNTRL0); - DUMP_REG(HDMI_NV_PDISP_AUDIO_N); - DUMP_REG(HDMI_NV_PDISP_HDCPRIF_ROM_TIMING); - DUMP_REG(HDMI_NV_PDISP_SOR_REFCLK); - DUMP_REG(HDMI_NV_PDISP_CRC_CONTROL); - DUMP_REG(HDMI_NV_PDISP_INPUT_CONTROL); - DUMP_REG(HDMI_NV_PDISP_SCRATCH); - DUMP_REG(HDMI_NV_PDISP_PE_CURRENT); - DUMP_REG(HDMI_NV_PDISP_KEY_CTRL); - DUMP_REG(HDMI_NV_PDISP_KEY_DEBUG0); - DUMP_REG(HDMI_NV_PDISP_KEY_DEBUG1); - DUMP_REG(HDMI_NV_PDISP_KEY_DEBUG2); - DUMP_REG(HDMI_NV_PDISP_KEY_HDCP_KEY_0); - DUMP_REG(HDMI_NV_PDISP_KEY_HDCP_KEY_1); - DUMP_REG(HDMI_NV_PDISP_KEY_HDCP_KEY_2); - DUMP_REG(HDMI_NV_PDISP_KEY_HDCP_KEY_3); - DUMP_REG(HDMI_NV_PDISP_KEY_HDCP_KEY_TRIG); - DUMP_REG(HDMI_NV_PDISP_KEY_SKEY_INDEX); - DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_CNTRL0); - DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_SPARE0); - DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0); - DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH1); - DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR); - DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE); - DUMP_REG(HDMI_NV_PDISP_INT_STATUS); - DUMP_REG(HDMI_NV_PDISP_INT_MASK); - DUMP_REG(HDMI_NV_PDISP_INT_ENABLE); - DUMP_REG(HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT); - -#undef DUMP_REG - -unlock: - drm_modeset_unlock_all(drm); - return err; -} - -static struct drm_info_list debugfs_files[] = { - { "regs", tegra_hdmi_show_regs, 0, NULL }, -}; - -static int tegra_hdmi_debugfs_init(struct tegra_hdmi *hdmi, - struct drm_minor *minor) -{ - unsigned int i; - int err; - - hdmi->debugfs = debugfs_create_dir("hdmi", minor->debugfs_root); - if (!hdmi->debugfs) - return -ENOMEM; - - hdmi->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files), - GFP_KERNEL); - if (!hdmi->debugfs_files) { - err = -ENOMEM; - goto remove; - } - - for (i = 0; i < ARRAY_SIZE(debugfs_files); i++) - hdmi->debugfs_files[i].data = hdmi; - - err = drm_debugfs_create_files(hdmi->debugfs_files, - ARRAY_SIZE(debugfs_files), - hdmi->debugfs, minor); - if (err < 0) - goto free; - - hdmi->minor = minor; - - return 0; - -free: - kfree(hdmi->debugfs_files); - hdmi->debugfs_files = NULL; -remove: - debugfs_remove(hdmi->debugfs); - hdmi->debugfs = NULL; - - return err; -} - -static void tegra_hdmi_debugfs_exit(struct tegra_hdmi *hdmi) -{ - drm_debugfs_remove_files(hdmi->debugfs_files, ARRAY_SIZE(debugfs_files), - hdmi->minor); - hdmi->minor = NULL; - - kfree(hdmi->debugfs_files); - hdmi->debugfs_files = NULL; - - debugfs_remove(hdmi->debugfs); - hdmi->debugfs = NULL; -} - static int tegra_hdmi_init(struct host1x_client *client) { struct drm_device *drm = dev_get_drvdata(client->parent); @@ -1505,12 +1500,6 @@ static int tegra_hdmi_init(struct host1x_client *client) hdmi->output.encoder.possible_crtcs = 0x3; - if (IS_ENABLED(CONFIG_DEBUG_FS)) { - err = tegra_hdmi_debugfs_init(hdmi, drm->primary); - if (err < 0) - dev_err(client->dev, "debugfs setup failed: %d\n", err); - } - err = regulator_enable(hdmi->hdmi); if (err < 0) { dev_err(client->dev, "failed to enable HDMI regulator: %d\n", @@ -1543,9 +1532,6 @@ static int tegra_hdmi_exit(struct host1x_client *client) regulator_disable(hdmi->pll); regulator_disable(hdmi->hdmi); - if (IS_ENABLED(CONFIG_DEBUG_FS)) - tegra_hdmi_debugfs_exit(hdmi); - return 0; } diff --git a/drivers/gpu/drm/tegra/hub.c b/drivers/gpu/drm/tegra/hub.c new file mode 100644 index 000000000000..e10a47d57313 --- /dev/null +++ b/drivers/gpu/drm/tegra/hub.c @@ -0,0 +1,806 @@ +/* + * Copyright (C) 2017 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/host1x.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> + +#include <drm/drmP.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc_helper.h> + +#include "drm.h" +#include "dc.h" +#include "plane.h" + +static const u32 tegra_shared_plane_formats[] = { + DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGB565, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888, + /* new on Tegra114 */ + DRM_FORMAT_ABGR4444, + DRM_FORMAT_ABGR1555, + DRM_FORMAT_BGRA5551, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_RGBX5551, + DRM_FORMAT_XBGR1555, + DRM_FORMAT_BGRX5551, + DRM_FORMAT_BGR565, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + /* planar formats */ + DRM_FORMAT_UYVY, + DRM_FORMAT_YUYV, + DRM_FORMAT_YUV420, + DRM_FORMAT_YUV422, +}; + +static inline unsigned int tegra_plane_offset(struct tegra_plane *plane, + unsigned int offset) +{ + if (offset >= 0x500 && offset <= 0x581) { + offset = 0x000 + (offset - 0x500); + return plane->offset + offset; + } + + if (offset >= 0x700 && offset <= 0x73c) { + offset = 0x180 + (offset - 0x700); + return plane->offset + offset; + } + + if (offset >= 0x800 && offset <= 0x83e) { + offset = 0x1c0 + (offset - 0x800); + return plane->offset + offset; + } + + dev_WARN(plane->dc->dev, "invalid offset: %x\n", offset); + + return plane->offset + offset; +} + +static inline u32 tegra_plane_readl(struct tegra_plane *plane, + unsigned int offset) +{ + return tegra_dc_readl(plane->dc, tegra_plane_offset(plane, offset)); +} + +static inline void tegra_plane_writel(struct tegra_plane *plane, u32 value, + unsigned int offset) +{ + tegra_dc_writel(plane->dc, value, tegra_plane_offset(plane, offset)); +} + +static int tegra_windowgroup_enable(struct tegra_windowgroup *wgrp) +{ + mutex_lock(&wgrp->lock); + + if (wgrp->usecount == 0) { + pm_runtime_get_sync(wgrp->parent); + reset_control_deassert(wgrp->rst); + } + + wgrp->usecount++; + mutex_unlock(&wgrp->lock); + + return 0; +} + +static void tegra_windowgroup_disable(struct tegra_windowgroup *wgrp) +{ + int err; + + mutex_lock(&wgrp->lock); + + if (wgrp->usecount == 1) { + err = reset_control_assert(wgrp->rst); + if (err < 0) { + pr_err("failed to assert reset for window group %u\n", + wgrp->index); + } + + pm_runtime_put(wgrp->parent); + } + + wgrp->usecount--; + mutex_unlock(&wgrp->lock); +} + +int tegra_display_hub_prepare(struct tegra_display_hub *hub) +{ + unsigned int i; + + /* + * XXX Enabling/disabling windowgroups needs to happen when the owner + * display controller is disabled. There's currently no good point at + * which this could be executed, so unconditionally enable all window + * groups for now. + */ + for (i = 0; i < hub->soc->num_wgrps; i++) { + struct tegra_windowgroup *wgrp = &hub->wgrps[i]; + + tegra_windowgroup_enable(wgrp); + } + + return 0; +} + +void tegra_display_hub_cleanup(struct tegra_display_hub *hub) +{ + unsigned int i; + + /* + * XXX Remove this once window groups can be more fine-grainedly + * enabled and disabled. + */ + for (i = 0; i < hub->soc->num_wgrps; i++) { + struct tegra_windowgroup *wgrp = &hub->wgrps[i]; + + tegra_windowgroup_disable(wgrp); + } +} + +static void tegra_shared_plane_update(struct tegra_plane *plane) +{ + struct tegra_dc *dc = plane->dc; + unsigned long timeout; + u32 mask, value; + + mask = COMMON_UPDATE | WIN_A_UPDATE << plane->base.index; + tegra_dc_writel(dc, mask, DC_CMD_STATE_CONTROL); + + timeout = jiffies + msecs_to_jiffies(1000); + + while (time_before(jiffies, timeout)) { + value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); + if ((value & mask) == 0) + break; + + usleep_range(100, 400); + } +} + +static void tegra_shared_plane_activate(struct tegra_plane *plane) +{ + struct tegra_dc *dc = plane->dc; + unsigned long timeout; + u32 mask, value; + + mask = COMMON_ACTREQ | WIN_A_ACT_REQ << plane->base.index; + tegra_dc_writel(dc, mask, DC_CMD_STATE_CONTROL); + + timeout = jiffies + msecs_to_jiffies(1000); + + while (time_before(jiffies, timeout)) { + value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); + if ((value & mask) == 0) + break; + + usleep_range(100, 400); + } +} + +static unsigned int +tegra_shared_plane_get_owner(struct tegra_plane *plane, struct tegra_dc *dc) +{ + unsigned int offset = + tegra_plane_offset(plane, DC_WIN_CORE_WINDOWGROUP_SET_CONTROL); + + return tegra_dc_readl(dc, offset) & OWNER_MASK; +} + +static bool tegra_dc_owns_shared_plane(struct tegra_dc *dc, + struct tegra_plane *plane) +{ + struct device *dev = dc->dev; + + if (tegra_shared_plane_get_owner(plane, dc) == dc->pipe) { + if (plane->dc == dc) + return true; + + dev_WARN(dev, "head %u owns window %u but is not attached\n", + dc->pipe, plane->index); + } + + return false; +} + +static int tegra_shared_plane_set_owner(struct tegra_plane *plane, + struct tegra_dc *new) +{ + unsigned int offset = + tegra_plane_offset(plane, DC_WIN_CORE_WINDOWGROUP_SET_CONTROL); + struct tegra_dc *old = plane->dc, *dc = new ? new : old; + struct device *dev = new ? new->dev : old->dev; + unsigned int owner, index = plane->index; + u32 value; + + value = tegra_dc_readl(dc, offset); + owner = value & OWNER_MASK; + + if (new && (owner != OWNER_MASK && owner != new->pipe)) { + dev_WARN(dev, "window %u owned by head %u\n", index, owner); + return -EBUSY; + } + + /* + * This seems to happen whenever the head has been disabled with one + * or more windows being active. This is harmless because we'll just + * reassign the window to the new head anyway. + */ + if (old && owner == OWNER_MASK) + dev_dbg(dev, "window %u not owned by head %u but %u\n", index, + old->pipe, owner); + + value &= ~OWNER_MASK; + + if (new) + value |= OWNER(new->pipe); + else + value |= OWNER_MASK; + + tegra_dc_writel(dc, value, offset); + + plane->dc = new; + + return 0; +} + +static void tegra_dc_assign_shared_plane(struct tegra_dc *dc, + struct tegra_plane *plane) +{ + u32 value; + int err; + + if (!tegra_dc_owns_shared_plane(dc, plane)) { + err = tegra_shared_plane_set_owner(plane, dc); + if (err < 0) + return; + } + + value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_LINEBUF_CONFIG); + value |= MODE_FOUR_LINES; + tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_LINEBUF_CONFIG); + + value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_FETCH_METER); + value = SLOTS(1); + tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_FETCH_METER); + + /* disable watermark */ + value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLA); + value &= ~LATENCY_CTL_MODE_ENABLE; + tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLA); + + value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLB); + value |= WATERMARK_MASK; + tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLB); + + /* pipe meter */ + value = tegra_plane_readl(plane, DC_WIN_CORE_PRECOMP_WGRP_PIPE_METER); + value = PIPE_METER_INT(0) | PIPE_METER_FRAC(0); + tegra_plane_writel(plane, value, DC_WIN_CORE_PRECOMP_WGRP_PIPE_METER); + + /* mempool entries */ + value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_POOL_CONFIG); + value = MEMPOOL_ENTRIES(0x331); + tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_POOL_CONFIG); + + value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_THREAD_GROUP); + value &= ~THREAD_NUM_MASK; + value |= THREAD_NUM(plane->base.index); + value |= THREAD_GROUP_ENABLE; + tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_THREAD_GROUP); + + tegra_shared_plane_update(plane); + tegra_shared_plane_activate(plane); +} + +static void tegra_dc_remove_shared_plane(struct tegra_dc *dc, + struct tegra_plane *plane) +{ + tegra_shared_plane_set_owner(plane, NULL); +} + +static int tegra_shared_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct tegra_plane_state *plane_state = to_tegra_plane_state(state); + struct tegra_shared_plane *tegra = to_tegra_shared_plane(plane); + struct tegra_bo_tiling *tiling = &plane_state->tiling; + struct tegra_dc *dc = to_tegra_dc(state->crtc); + int err; + + /* no need for further checks if the plane is being disabled */ + if (!state->crtc || !state->fb) + return 0; + + err = tegra_plane_format(state->fb->format->format, + &plane_state->format, + &plane_state->swap); + if (err < 0) + return err; + + err = tegra_fb_get_tiling(state->fb, tiling); + if (err < 0) + return err; + + if (tiling->mode == TEGRA_BO_TILING_MODE_BLOCK && + !dc->soc->supports_block_linear) { + DRM_ERROR("hardware doesn't support block linear mode\n"); + return -EINVAL; + } + + /* + * Tegra doesn't support different strides for U and V planes so we + * error out if the user tries to display a framebuffer with such a + * configuration. + */ + if (state->fb->format->num_planes > 2) { + if (state->fb->pitches[2] != state->fb->pitches[1]) { + DRM_ERROR("unsupported UV-plane configuration\n"); + return -EINVAL; + } + } + + /* XXX scaling is not yet supported, add a check here */ + + err = tegra_plane_state_add(&tegra->base, state); + if (err < 0) + return err; + + return 0; +} + +static void tegra_shared_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct tegra_dc *dc = to_tegra_dc(old_state->crtc); + struct tegra_plane *p = to_tegra_plane(plane); + u32 value; + + /* rien ne va plus */ + if (!old_state || !old_state->crtc) + return; + + /* + * XXX Legacy helpers seem to sometimes call ->atomic_disable() even + * on planes that are already disabled. Make sure we fallback to the + * head for this particular state instead of crashing. + */ + if (WARN_ON(p->dc == NULL)) + p->dc = dc; + + pm_runtime_get_sync(dc->dev); + + value = tegra_plane_readl(p, DC_WIN_WIN_OPTIONS); + value &= ~WIN_ENABLE; + tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS); + + tegra_dc_remove_shared_plane(dc, p); + + pm_runtime_put(dc->dev); +} + +static void tegra_shared_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct tegra_plane_state *state = to_tegra_plane_state(plane->state); + struct tegra_dc *dc = to_tegra_dc(plane->state->crtc); + unsigned int zpos = plane->state->normalized_zpos; + struct drm_framebuffer *fb = plane->state->fb; + struct tegra_plane *p = to_tegra_plane(plane); + struct tegra_bo *bo; + dma_addr_t base; + u32 value; + + /* rien ne va plus */ + if (!plane->state->crtc || !plane->state->fb) + return; + + if (!plane->state->visible) { + tegra_shared_plane_atomic_disable(plane, old_state); + return; + } + + pm_runtime_get_sync(dc->dev); + + tegra_dc_assign_shared_plane(dc, p); + + tegra_plane_writel(p, VCOUNTER, DC_WIN_CORE_ACT_CONTROL); + + /* blending */ + value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 | + BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC | + BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC; + tegra_plane_writel(p, value, DC_WIN_BLEND_MATCH_SELECT); + + value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 | + BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC | + BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC; + tegra_plane_writel(p, value, DC_WIN_BLEND_NOMATCH_SELECT); + + value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(255 - zpos); + tegra_plane_writel(p, value, DC_WIN_BLEND_LAYER_CONTROL); + + /* bypass scaling */ + value = HORIZONTAL_TAPS_5 | VERTICAL_TAPS_5; + tegra_plane_writel(p, value, DC_WIN_WINDOWGROUP_SET_CONTROL_INPUT_SCALER); + + value = INPUT_SCALER_VBYPASS | INPUT_SCALER_HBYPASS; + tegra_plane_writel(p, value, DC_WIN_WINDOWGROUP_SET_INPUT_SCALER_USAGE); + + /* disable compression */ + tegra_plane_writel(p, 0, DC_WINBUF_CDE_CONTROL); + + bo = tegra_fb_get_plane(fb, 0); + base = bo->paddr; + + tegra_plane_writel(p, state->format, DC_WIN_COLOR_DEPTH); + tegra_plane_writel(p, 0, DC_WIN_PRECOMP_WGRP_PARAMS); + + value = V_POSITION(plane->state->crtc_y) | + H_POSITION(plane->state->crtc_x); + tegra_plane_writel(p, value, DC_WIN_POSITION); + + value = V_SIZE(plane->state->crtc_h) | H_SIZE(plane->state->crtc_w); + tegra_plane_writel(p, value, DC_WIN_SIZE); + + value = WIN_ENABLE | COLOR_EXPAND; + tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS); + + value = V_SIZE(plane->state->crtc_h) | H_SIZE(plane->state->crtc_w); + tegra_plane_writel(p, value, DC_WIN_CROPPED_SIZE); + + tegra_plane_writel(p, upper_32_bits(base), DC_WINBUF_START_ADDR_HI); + tegra_plane_writel(p, lower_32_bits(base), DC_WINBUF_START_ADDR); + + value = PITCH(fb->pitches[0]); + tegra_plane_writel(p, value, DC_WIN_PLANAR_STORAGE); + + value = CLAMP_BEFORE_BLEND | DEGAMMA_SRGB | INPUT_RANGE_FULL; + tegra_plane_writel(p, value, DC_WIN_SET_PARAMS); + + value = OFFSET_X(plane->state->src_y >> 16) | + OFFSET_Y(plane->state->src_x >> 16); + tegra_plane_writel(p, value, DC_WINBUF_CROPPED_POINT); + + if (dc->soc->supports_block_linear) { + unsigned long height = state->tiling.value; + + /* XXX */ + switch (state->tiling.mode) { + case TEGRA_BO_TILING_MODE_PITCH: + value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(0) | + DC_WINBUF_SURFACE_KIND_PITCH; + break; + + /* XXX not supported on Tegra186 and later */ + case TEGRA_BO_TILING_MODE_TILED: + value = DC_WINBUF_SURFACE_KIND_TILED; + break; + + case TEGRA_BO_TILING_MODE_BLOCK: + value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) | + DC_WINBUF_SURFACE_KIND_BLOCK; + break; + } + + tegra_plane_writel(p, value, DC_WINBUF_SURFACE_KIND); + } + + /* disable gamut CSC */ + value = tegra_plane_readl(p, DC_WIN_WINDOW_SET_CONTROL); + value &= ~CONTROL_CSC_ENABLE; + tegra_plane_writel(p, value, DC_WIN_WINDOW_SET_CONTROL); + + pm_runtime_put(dc->dev); +} + +static const struct drm_plane_helper_funcs tegra_shared_plane_helper_funcs = { + .atomic_check = tegra_shared_plane_atomic_check, + .atomic_update = tegra_shared_plane_atomic_update, + .atomic_disable = tegra_shared_plane_atomic_disable, +}; + +struct drm_plane *tegra_shared_plane_create(struct drm_device *drm, + struct tegra_dc *dc, + unsigned int wgrp, + unsigned int index) +{ + enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY; + struct tegra_drm *tegra = drm->dev_private; + struct tegra_display_hub *hub = tegra->hub; + /* planes can be assigned to arbitrary CRTCs */ + unsigned int possible_crtcs = 0x7; + struct tegra_shared_plane *plane; + unsigned int num_formats; + struct drm_plane *p; + const u32 *formats; + int err; + + plane = kzalloc(sizeof(*plane), GFP_KERNEL); + if (!plane) + return ERR_PTR(-ENOMEM); + + plane->base.offset = 0x0a00 + 0x0300 * index; + plane->base.index = index; + + plane->wgrp = &hub->wgrps[wgrp]; + plane->wgrp->parent = dc->dev; + + p = &plane->base.base; + + num_formats = ARRAY_SIZE(tegra_shared_plane_formats); + formats = tegra_shared_plane_formats; + + err = drm_universal_plane_init(drm, p, possible_crtcs, + &tegra_plane_funcs, formats, + num_formats, NULL, type, NULL); + if (err < 0) { + kfree(plane); + return ERR_PTR(err); + } + + drm_plane_helper_add(p, &tegra_shared_plane_helper_funcs); + drm_plane_create_zpos_property(p, 0, 0, 255); + + return p; +} + +static void tegra_display_hub_update(struct tegra_dc *dc) +{ + u32 value; + + pm_runtime_get_sync(dc->dev); + + value = tegra_dc_readl(dc, DC_CMD_IHUB_COMMON_MISC_CTL); + value &= ~LATENCY_EVENT; + tegra_dc_writel(dc, value, DC_CMD_IHUB_COMMON_MISC_CTL); + + value = tegra_dc_readl(dc, DC_DISP_IHUB_COMMON_DISPLAY_FETCH_METER); + value = CURS_SLOTS(1) | WGRP_SLOTS(1); + tegra_dc_writel(dc, value, DC_DISP_IHUB_COMMON_DISPLAY_FETCH_METER); + + tegra_dc_writel(dc, COMMON_UPDATE, DC_CMD_STATE_CONTROL); + tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); + tegra_dc_writel(dc, COMMON_ACTREQ, DC_CMD_STATE_CONTROL); + tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); + + pm_runtime_put(dc->dev); +} + +void tegra_display_hub_atomic_commit(struct drm_device *drm, + struct drm_atomic_state *state) +{ + struct tegra_atomic_state *s = to_tegra_atomic_state(state); + struct tegra_drm *tegra = drm->dev_private; + struct tegra_display_hub *hub = tegra->hub; + struct device *dev = hub->client.dev; + int err; + + if (s->clk_disp) { + err = clk_set_rate(s->clk_disp, s->rate); + if (err < 0) + dev_err(dev, "failed to set rate of %pC to %lu Hz\n", + s->clk_disp, s->rate); + + err = clk_set_parent(hub->clk_disp, s->clk_disp); + if (err < 0) + dev_err(dev, "failed to set parent of %pC to %pC: %d\n", + hub->clk_disp, s->clk_disp, err); + } + + if (s->dc) + tegra_display_hub_update(s->dc); +} + +static int tegra_display_hub_init(struct host1x_client *client) +{ + struct tegra_display_hub *hub = to_tegra_display_hub(client); + struct drm_device *drm = dev_get_drvdata(client->parent); + struct tegra_drm *tegra = drm->dev_private; + + tegra->hub = hub; + + return 0; +} + +static int tegra_display_hub_exit(struct host1x_client *client) +{ + struct drm_device *drm = dev_get_drvdata(client->parent); + struct tegra_drm *tegra = drm->dev_private; + + tegra->hub = NULL; + + return 0; +} + +static const struct host1x_client_ops tegra_display_hub_ops = { + .init = tegra_display_hub_init, + .exit = tegra_display_hub_exit, +}; + +static int tegra_display_hub_probe(struct platform_device *pdev) +{ + struct tegra_display_hub *hub; + unsigned int i; + int err; + + hub = devm_kzalloc(&pdev->dev, sizeof(*hub), GFP_KERNEL); + if (!hub) + return -ENOMEM; + + hub->soc = of_device_get_match_data(&pdev->dev); + + hub->clk_disp = devm_clk_get(&pdev->dev, "disp"); + if (IS_ERR(hub->clk_disp)) { + err = PTR_ERR(hub->clk_disp); + return err; + } + + hub->clk_dsc = devm_clk_get(&pdev->dev, "dsc"); + if (IS_ERR(hub->clk_dsc)) { + err = PTR_ERR(hub->clk_dsc); + return err; + } + + hub->clk_hub = devm_clk_get(&pdev->dev, "hub"); + if (IS_ERR(hub->clk_hub)) { + err = PTR_ERR(hub->clk_hub); + return err; + } + + hub->rst = devm_reset_control_get(&pdev->dev, "misc"); + if (IS_ERR(hub->rst)) { + err = PTR_ERR(hub->rst); + return err; + } + + hub->wgrps = devm_kcalloc(&pdev->dev, hub->soc->num_wgrps, + sizeof(*hub->wgrps), GFP_KERNEL); + if (!hub->wgrps) + return -ENOMEM; + + for (i = 0; i < hub->soc->num_wgrps; i++) { + struct tegra_windowgroup *wgrp = &hub->wgrps[i]; + char id[8]; + + snprintf(id, sizeof(id), "wgrp%u", i); + mutex_init(&wgrp->lock); + wgrp->usecount = 0; + wgrp->index = i; + + wgrp->rst = devm_reset_control_get(&pdev->dev, id); + if (IS_ERR(wgrp->rst)) + return PTR_ERR(wgrp->rst); + + err = reset_control_assert(wgrp->rst); + if (err < 0) + return err; + } + + /* XXX: enable clock across reset? */ + err = reset_control_assert(hub->rst); + if (err < 0) + return err; + + platform_set_drvdata(pdev, hub); + pm_runtime_enable(&pdev->dev); + + INIT_LIST_HEAD(&hub->client.list); + hub->client.ops = &tegra_display_hub_ops; + hub->client.dev = &pdev->dev; + + err = host1x_client_register(&hub->client); + if (err < 0) + dev_err(&pdev->dev, "failed to register host1x client: %d\n", + err); + + return err; +} + +static int tegra_display_hub_remove(struct platform_device *pdev) +{ + struct tegra_display_hub *hub = platform_get_drvdata(pdev); + int err; + + err = host1x_client_unregister(&hub->client); + if (err < 0) { + dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", + err); + } + + pm_runtime_disable(&pdev->dev); + + return err; +} + +static int __maybe_unused tegra_display_hub_suspend(struct device *dev) +{ + struct tegra_display_hub *hub = dev_get_drvdata(dev); + int err; + + err = reset_control_assert(hub->rst); + if (err < 0) + return err; + + clk_disable_unprepare(hub->clk_hub); + clk_disable_unprepare(hub->clk_dsc); + clk_disable_unprepare(hub->clk_disp); + + return 0; +} + +static int __maybe_unused tegra_display_hub_resume(struct device *dev) +{ + struct tegra_display_hub *hub = dev_get_drvdata(dev); + int err; + + err = clk_prepare_enable(hub->clk_disp); + if (err < 0) + return err; + + err = clk_prepare_enable(hub->clk_dsc); + if (err < 0) + goto disable_disp; + + err = clk_prepare_enable(hub->clk_hub); + if (err < 0) + goto disable_dsc; + + err = reset_control_deassert(hub->rst); + if (err < 0) + goto disable_hub; + + return 0; + +disable_hub: + clk_disable_unprepare(hub->clk_hub); +disable_dsc: + clk_disable_unprepare(hub->clk_dsc); +disable_disp: + clk_disable_unprepare(hub->clk_disp); + return err; +} + +static const struct dev_pm_ops tegra_display_hub_pm_ops = { + SET_RUNTIME_PM_OPS(tegra_display_hub_suspend, + tegra_display_hub_resume, NULL) +}; + +static const struct tegra_display_hub_soc tegra186_display_hub = { + .num_wgrps = 6, +}; + +static const struct of_device_id tegra_display_hub_of_match[] = { + { + .compatible = "nvidia,tegra186-display", + .data = &tegra186_display_hub + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, tegra_display_hub_of_match); + +struct platform_driver tegra_display_hub_driver = { + .driver = { + .name = "tegra-display-hub", + .of_match_table = tegra_display_hub_of_match, + .pm = &tegra_display_hub_pm_ops, + }, + .probe = tegra_display_hub_probe, + .remove = tegra_display_hub_remove, +}; diff --git a/drivers/gpu/drm/tegra/hub.h b/drivers/gpu/drm/tegra/hub.h new file mode 100644 index 000000000000..890a47cd05c3 --- /dev/null +++ b/drivers/gpu/drm/tegra/hub.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2017 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef TEGRA_HUB_H +#define TEGRA_HUB_H 1 + +#include <drm/drmP.h> +#include <drm/drm_plane.h> + +#include "plane.h" + +struct tegra_dc; + +struct tegra_windowgroup { + unsigned int usecount; + struct mutex lock; + + unsigned int index; + struct device *parent; + struct reset_control *rst; +}; + +struct tegra_shared_plane { + struct tegra_plane base; + struct tegra_windowgroup *wgrp; +}; + +static inline struct tegra_shared_plane * +to_tegra_shared_plane(struct drm_plane *plane) +{ + return container_of(plane, struct tegra_shared_plane, base.base); +} + +struct tegra_display_hub_soc { + unsigned int num_wgrps; +}; + +struct tegra_display_hub { + struct host1x_client client; + struct clk *clk_disp; + struct clk *clk_dsc; + struct clk *clk_hub; + struct reset_control *rst; + + const struct tegra_display_hub_soc *soc; + struct tegra_windowgroup *wgrps; +}; + +static inline struct tegra_display_hub * +to_tegra_display_hub(struct host1x_client *client) +{ + return container_of(client, struct tegra_display_hub, client); +} + +struct tegra_dc; +struct tegra_plane; + +int tegra_display_hub_prepare(struct tegra_display_hub *hub); +void tegra_display_hub_cleanup(struct tegra_display_hub *hub); + +struct drm_plane *tegra_shared_plane_create(struct drm_device *drm, + struct tegra_dc *dc, + unsigned int wgrp, + unsigned int index); + +void tegra_display_hub_atomic_commit(struct drm_device *drm, + struct drm_atomic_state *state); + +#define DC_CMD_IHUB_COMMON_MISC_CTL 0x068 +#define LATENCY_EVENT (1 << 3) + +#define DC_DISP_IHUB_COMMON_DISPLAY_FETCH_METER 0x451 +#define CURS_SLOTS(x) (((x) & 0xff) << 8) +#define WGRP_SLOTS(x) (((x) & 0xff) << 0) + +#endif /* TEGRA_HUB_H */ diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index 1cfbacea8113..ffe34bd0bb9d 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c @@ -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> @@ -39,7 +41,6 @@ int tegra_output_connector_get_modes(struct drm_connector *connector) if (edid) { err = drm_add_edid_modes(connector, edid); - drm_edid_to_eld(connector, edid); kfree(edid); } @@ -219,3 +220,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; +} diff --git a/drivers/gpu/drm/tegra/plane.c b/drivers/gpu/drm/tegra/plane.c new file mode 100644 index 000000000000..36a06a993698 --- /dev/null +++ b/drivers/gpu/drm/tegra/plane.c @@ -0,0 +1,383 @@ +/* + * Copyright (C) 2017 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_plane_helper.h> + +#include "dc.h" +#include "plane.h" + +static void tegra_plane_destroy(struct drm_plane *plane) +{ + struct tegra_plane *p = to_tegra_plane(plane); + + drm_plane_cleanup(plane); + kfree(p); +} + +static void tegra_plane_reset(struct drm_plane *plane) +{ + struct tegra_plane_state *state; + + if (plane->state) + __drm_atomic_helper_plane_destroy_state(plane->state); + + kfree(plane->state); + plane->state = NULL; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (state) { + plane->state = &state->base; + plane->state->plane = plane; + } +} + +static struct drm_plane_state * +tegra_plane_atomic_duplicate_state(struct drm_plane *plane) +{ + struct tegra_plane_state *state = to_tegra_plane_state(plane->state); + struct tegra_plane_state *copy; + unsigned int i; + + copy = kmalloc(sizeof(*copy), GFP_KERNEL); + if (!copy) + return NULL; + + __drm_atomic_helper_plane_duplicate_state(plane, ©->base); + copy->tiling = state->tiling; + copy->format = state->format; + copy->swap = state->swap; + copy->opaque = state->opaque; + + for (i = 0; i < 3; i++) + copy->dependent[i] = state->dependent[i]; + + return ©->base; +} + +static void tegra_plane_atomic_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + __drm_atomic_helper_plane_destroy_state(state); + kfree(state); +} + +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, + .reset = tegra_plane_reset, + .atomic_duplicate_state = tegra_plane_atomic_duplicate_state, + .atomic_destroy_state = tegra_plane_atomic_destroy_state, +}; + +int tegra_plane_state_add(struct tegra_plane *plane, + struct drm_plane_state *state) +{ + struct drm_crtc_state *crtc_state; + struct tegra_dc_state *tegra; + struct drm_rect clip; + int err; + + /* Propagate errors from allocation or locking failures. */ + crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + clip.x1 = 0; + clip.y1 = 0; + clip.x2 = crtc_state->mode.hdisplay; + clip.y2 = crtc_state->mode.vdisplay; + + /* Check plane state for visibility and calculate clipping bounds */ + err = drm_atomic_helper_check_plane_state(state, crtc_state, &clip, + 0, INT_MAX, true, true); + if (err < 0) + return err; + + tegra = to_dc_state(crtc_state); + + tegra->planes |= WIN_A_ACT_REQ << plane->index; + + return 0; +} + +int tegra_plane_format(u32 fourcc, u32 *format, u32 *swap) +{ + /* assume no swapping of fetched data */ + if (swap) + *swap = BYTE_SWAP_NOSWAP; + + switch (fourcc) { + case DRM_FORMAT_ARGB4444: + *format = WIN_COLOR_DEPTH_B4G4R4A4; + break; + + case DRM_FORMAT_ARGB1555: + *format = WIN_COLOR_DEPTH_B5G5R5A1; + break; + + case DRM_FORMAT_RGB565: + *format = WIN_COLOR_DEPTH_B5G6R5; + break; + + case DRM_FORMAT_RGBA5551: + *format = WIN_COLOR_DEPTH_A1B5G5R5; + break; + + case DRM_FORMAT_ARGB8888: + *format = WIN_COLOR_DEPTH_B8G8R8A8; + break; + + case DRM_FORMAT_ABGR8888: + *format = WIN_COLOR_DEPTH_R8G8B8A8; + break; + + case DRM_FORMAT_ABGR4444: + *format = WIN_COLOR_DEPTH_R4G4B4A4; + break; + + case DRM_FORMAT_ABGR1555: + *format = WIN_COLOR_DEPTH_R5G5B5A; + break; + + case DRM_FORMAT_BGRA5551: + *format = WIN_COLOR_DEPTH_AR5G5B5; + break; + + case DRM_FORMAT_XRGB1555: + *format = WIN_COLOR_DEPTH_B5G5R5X1; + break; + + case DRM_FORMAT_RGBX5551: + *format = WIN_COLOR_DEPTH_X1B5G5R5; + break; + + case DRM_FORMAT_XBGR1555: + *format = WIN_COLOR_DEPTH_R5G5B5X1; + break; + + case DRM_FORMAT_BGRX5551: + *format = WIN_COLOR_DEPTH_X1R5G5B5; + break; + + case DRM_FORMAT_BGR565: + *format = WIN_COLOR_DEPTH_R5G6B5; + break; + + case DRM_FORMAT_BGRA8888: + *format = WIN_COLOR_DEPTH_A8R8G8B8; + break; + + case DRM_FORMAT_RGBA8888: + *format = WIN_COLOR_DEPTH_A8B8G8R8; + break; + + case DRM_FORMAT_XRGB8888: + *format = WIN_COLOR_DEPTH_B8G8R8X8; + break; + + case DRM_FORMAT_XBGR8888: + *format = WIN_COLOR_DEPTH_R8G8B8X8; + break; + + case DRM_FORMAT_UYVY: + *format = WIN_COLOR_DEPTH_YCbCr422; + break; + + case DRM_FORMAT_YUYV: + if (!swap) + return -EINVAL; + + *format = WIN_COLOR_DEPTH_YCbCr422; + *swap = BYTE_SWAP_SWAP2; + break; + + case DRM_FORMAT_YUV420: + *format = WIN_COLOR_DEPTH_YCbCr420P; + break; + + case DRM_FORMAT_YUV422: + *format = WIN_COLOR_DEPTH_YCbCr422P; + break; + + default: + return -EINVAL; + } + + return 0; +} + +bool tegra_plane_format_is_yuv(unsigned int format, bool *planar) +{ + switch (format) { + case WIN_COLOR_DEPTH_YCbCr422: + case WIN_COLOR_DEPTH_YUV422: + if (planar) + *planar = false; + + return true; + + case WIN_COLOR_DEPTH_YCbCr420P: + case WIN_COLOR_DEPTH_YUV420P: + case WIN_COLOR_DEPTH_YCbCr422P: + case WIN_COLOR_DEPTH_YUV422P: + case WIN_COLOR_DEPTH_YCbCr422R: + case WIN_COLOR_DEPTH_YUV422R: + case WIN_COLOR_DEPTH_YCbCr422RA: + case WIN_COLOR_DEPTH_YUV422RA: + if (planar) + *planar = true; + + return true; + } + + if (planar) + *planar = false; + + return false; +} + +static bool __drm_format_has_alpha(u32 format) +{ + switch (format) { + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_RGBA5551: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_ARGB8888: + return true; + } + + return false; +} + +/* + * This is applicable to Tegra20 and Tegra30 only where the opaque formats can + * be emulated using the alpha formats and alpha blending disabled. + */ +bool tegra_plane_format_has_alpha(unsigned int format) +{ + switch (format) { + case WIN_COLOR_DEPTH_B5G5R5A1: + case WIN_COLOR_DEPTH_A1B5G5R5: + case WIN_COLOR_DEPTH_R8G8B8A8: + case WIN_COLOR_DEPTH_B8G8R8A8: + return true; + } + + return false; +} + +int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha) +{ + if (tegra_plane_format_is_yuv(opaque, NULL)) { + *alpha = opaque; + return 0; + } + + switch (opaque) { + case WIN_COLOR_DEPTH_B5G5R5X1: + *alpha = WIN_COLOR_DEPTH_B5G5R5A1; + return 0; + + case WIN_COLOR_DEPTH_X1B5G5R5: + *alpha = WIN_COLOR_DEPTH_A1B5G5R5; + return 0; + + case WIN_COLOR_DEPTH_R8G8B8X8: + *alpha = WIN_COLOR_DEPTH_R8G8B8A8; + return 0; + + case WIN_COLOR_DEPTH_B8G8R8X8: + *alpha = WIN_COLOR_DEPTH_B8G8R8A8; + return 0; + } + + return -EINVAL; +} + +unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane, + struct tegra_plane *other) +{ + unsigned int index = 0, i; + + WARN_ON(plane == other); + + for (i = 0; i < 3; i++) { + if (i == plane->index) + continue; + + if (i == other->index) + break; + + index++; + } + + return index; +} + +void tegra_plane_check_dependent(struct tegra_plane *tegra, + struct tegra_plane_state *state) +{ + struct drm_plane_state *old, *new; + struct drm_plane *plane; + unsigned int zpos[2]; + unsigned int i; + + for (i = 0; i < 3; i++) + state->dependent[i] = false; + + for (i = 0; i < 2; i++) + zpos[i] = 0; + + for_each_oldnew_plane_in_state(state->base.state, plane, old, new, i) { + struct tegra_plane *p = to_tegra_plane(plane); + unsigned index; + + /* skip this plane and planes on different CRTCs */ + if (p == tegra || new->crtc != state->base.crtc) + continue; + + index = tegra_plane_get_overlap_index(tegra, p); + + /* + * If any of the other planes is on top of this plane and uses + * a format with an alpha component, mark this plane as being + * dependent, meaning it's alpha value will be 1 minus the sum + * of alpha components of the overlapping planes. + */ + if (p->index > tegra->index) { + if (__drm_format_has_alpha(new->fb->format->format)) + state->dependent[index] = true; + + /* keep track of the Z position */ + zpos[index] = p->index; + } + } + + /* + * The region where three windows overlap is the intersection of the + * two regions where two windows overlap. It contributes to the area + * if any of the windows on top of it have an alpha component. + */ + for (i = 0; i < 2; i++) + state->dependent[2] = state->dependent[2] || + state->dependent[i]; + + /* + * However, if any of the windows on top of this window is opaque, it + * will completely conceal this window within that area, so avoid the + * window from contributing to the area. + */ + for (i = 0; i < 2; i++) { + if (zpos[i] > tegra->index) + state->dependent[2] = state->dependent[2] && + state->dependent[i]; + } +} diff --git a/drivers/gpu/drm/tegra/plane.h b/drivers/gpu/drm/tegra/plane.h new file mode 100644 index 000000000000..6938719e7e5d --- /dev/null +++ b/drivers/gpu/drm/tegra/plane.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2017 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef TEGRA_PLANE_H +#define TEGRA_PLANE_H 1 + +#include <drm/drm_plane.h> + +struct tegra_bo; +struct tegra_dc; + +struct tegra_plane { + struct drm_plane base; + struct tegra_dc *dc; + unsigned int offset; + unsigned int index; +}; + +struct tegra_cursor { + struct tegra_plane base; + + struct tegra_bo *bo; + unsigned int width; + unsigned int height; +}; + +static inline struct tegra_plane *to_tegra_plane(struct drm_plane *plane) +{ + return container_of(plane, struct tegra_plane, base); +} + +struct tegra_plane_state { + struct drm_plane_state base; + + struct tegra_bo_tiling tiling; + u32 format; + u32 swap; + + /* used for legacy blending support only */ + bool opaque; + bool dependent[3]; +}; + +static inline struct tegra_plane_state * +to_tegra_plane_state(struct drm_plane_state *state) +{ + if (state) + return container_of(state, struct tegra_plane_state, base); + + return NULL; +} + +extern const struct drm_plane_funcs tegra_plane_funcs; + +int tegra_plane_state_add(struct tegra_plane *plane, + struct drm_plane_state *state); + +int tegra_plane_format(u32 fourcc, u32 *format, u32 *swap); +bool tegra_plane_format_is_yuv(unsigned int format, bool *planar); +bool tegra_plane_format_has_alpha(unsigned int format); +int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha); +void tegra_plane_check_dependent(struct tegra_plane *tegra, + struct tegra_plane_state *state); + +#endif /* TEGRA_PLANE_H */ diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 476079f1255f..7d2a955fc515 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -22,23 +22,37 @@ #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" #include "sor.h" #include "trace.h" +/* + * XXX Remove this after the commit adding it to soc/tegra/pmc.h has been + * merged. Having this around after the commit is merged should be safe since + * the preprocessor will effectively replace all occurrences and therefore no + * duplicate will be defined. + */ +#define TEGRA_IO_PAD_HDMI_DP0 26 + #define SOR_REKEY 0x38 struct tegra_sor_hdmi_settings { unsigned long frequency; u8 vcocap; + u8 filter; u8 ichpmp; u8 loadadj; - u8 termadj; - u8 tx_pu; - u8 bg_vref; + u8 tmds_termadj; + u8 tx_pu_value; + u8 bg_temp_coef; + u8 bg_vref_level; + u8 avdd10_level; + u8 avdd14_level; + u8 sparepll; u8 drive_current[4]; u8 preemphasis[4]; @@ -49,51 +63,76 @@ static const struct tegra_sor_hdmi_settings tegra210_sor_hdmi_defaults[] = { { .frequency = 54000000, .vcocap = 0x0, + .filter = 0x0, .ichpmp = 0x1, .loadadj = 0x3, - .termadj = 0x9, - .tx_pu = 0x10, - .bg_vref = 0x8, + .tmds_termadj = 0x9, + .tx_pu_value = 0x10, + .bg_temp_coef = 0x3, + .bg_vref_level = 0x8, + .avdd10_level = 0x4, + .avdd14_level = 0x4, + .sparepll = 0x0, .drive_current = { 0x33, 0x3a, 0x3a, 0x3a }, .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, }, { .frequency = 75000000, .vcocap = 0x3, + .filter = 0x0, .ichpmp = 0x1, .loadadj = 0x3, - .termadj = 0x9, - .tx_pu = 0x40, - .bg_vref = 0x8, + .tmds_termadj = 0x9, + .tx_pu_value = 0x40, + .bg_temp_coef = 0x3, + .bg_vref_level = 0x8, + .avdd10_level = 0x4, + .avdd14_level = 0x4, + .sparepll = 0x0, .drive_current = { 0x33, 0x3a, 0x3a, 0x3a }, .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, }, { .frequency = 150000000, .vcocap = 0x3, + .filter = 0x0, .ichpmp = 0x1, .loadadj = 0x3, - .termadj = 0x9, - .tx_pu = 0x66, - .bg_vref = 0x8, + .tmds_termadj = 0x9, + .tx_pu_value = 0x66, + .bg_temp_coef = 0x3, + .bg_vref_level = 0x8, + .avdd10_level = 0x4, + .avdd14_level = 0x4, + .sparepll = 0x0, .drive_current = { 0x33, 0x3a, 0x3a, 0x3a }, .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, }, { .frequency = 300000000, .vcocap = 0x3, + .filter = 0x0, .ichpmp = 0x1, .loadadj = 0x3, - .termadj = 0x9, - .tx_pu = 0x66, - .bg_vref = 0xa, + .tmds_termadj = 0x9, + .tx_pu_value = 0x66, + .bg_temp_coef = 0x3, + .bg_vref_level = 0xa, + .avdd10_level = 0x4, + .avdd14_level = 0x4, + .sparepll = 0x0, .drive_current = { 0x33, 0x3f, 0x3f, 0x3f }, .preemphasis = { 0x00, 0x17, 0x17, 0x17 }, }, { .frequency = 600000000, .vcocap = 0x3, + .filter = 0x0, .ichpmp = 0x1, .loadadj = 0x3, - .termadj = 0x9, - .tx_pu = 0x66, - .bg_vref = 0x8, + .tmds_termadj = 0x9, + .tx_pu_value = 0x66, + .bg_temp_coef = 0x3, + .bg_vref_level = 0x8, + .avdd10_level = 0x4, + .avdd14_level = 0x4, + .sparepll = 0x0, .drive_current = { 0x33, 0x3f, 0x3f, 0x3f }, .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, }, @@ -103,53 +142,170 @@ static const struct tegra_sor_hdmi_settings tegra210_sor_hdmi_defaults[] = { { .frequency = 75000000, .vcocap = 0x3, + .filter = 0x0, .ichpmp = 0x1, .loadadj = 0x3, - .termadj = 0x9, - .tx_pu = 0x40, - .bg_vref = 0x8, + .tmds_termadj = 0x9, + .tx_pu_value = 0x40, + .bg_temp_coef = 0x3, + .bg_vref_level = 0x8, + .avdd10_level = 0x4, + .avdd14_level = 0x4, + .sparepll = 0x0, .drive_current = { 0x29, 0x29, 0x29, 0x29 }, .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, }, { .frequency = 150000000, .vcocap = 0x3, + .filter = 0x0, .ichpmp = 0x1, .loadadj = 0x3, - .termadj = 0x9, - .tx_pu = 0x66, - .bg_vref = 0x8, + .tmds_termadj = 0x9, + .tx_pu_value = 0x66, + .bg_temp_coef = 0x3, + .bg_vref_level = 0x8, + .avdd10_level = 0x4, + .avdd14_level = 0x4, + .sparepll = 0x0, .drive_current = { 0x30, 0x37, 0x37, 0x37 }, .preemphasis = { 0x01, 0x02, 0x02, 0x02 }, }, { .frequency = 300000000, .vcocap = 0x3, + .filter = 0x0, .ichpmp = 0x6, .loadadj = 0x3, - .termadj = 0x9, - .tx_pu = 0x66, - .bg_vref = 0xf, + .tmds_termadj = 0x9, + .tx_pu_value = 0x66, + .bg_temp_coef = 0x3, + .bg_vref_level = 0xf, + .avdd10_level = 0x4, + .avdd14_level = 0x4, + .sparepll = 0x0, .drive_current = { 0x30, 0x37, 0x37, 0x37 }, .preemphasis = { 0x10, 0x3e, 0x3e, 0x3e }, }, { .frequency = 600000000, .vcocap = 0x3, + .filter = 0x0, .ichpmp = 0xa, .loadadj = 0x3, - .termadj = 0xb, - .tx_pu = 0x66, - .bg_vref = 0xe, + .tmds_termadj = 0xb, + .tx_pu_value = 0x66, + .bg_temp_coef = 0x3, + .bg_vref_level = 0xe, + .avdd10_level = 0x4, + .avdd14_level = 0x4, + .sparepll = 0x0, .drive_current = { 0x35, 0x3e, 0x3e, 0x3e }, .preemphasis = { 0x02, 0x3f, 0x3f, 0x3f }, }, }; #endif +static const struct tegra_sor_hdmi_settings tegra186_sor_hdmi_defaults[] = { + { + .frequency = 54000000, + .vcocap = 0, + .filter = 5, + .ichpmp = 5, + .loadadj = 3, + .tmds_termadj = 0xf, + .tx_pu_value = 0, + .bg_temp_coef = 3, + .bg_vref_level = 8, + .avdd10_level = 4, + .avdd14_level = 4, + .sparepll = 0x54, + .drive_current = { 0x3a, 0x3a, 0x3a, 0x33 }, + .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, + }, { + .frequency = 75000000, + .vcocap = 1, + .filter = 5, + .ichpmp = 5, + .loadadj = 3, + .tmds_termadj = 0xf, + .tx_pu_value = 0, + .bg_temp_coef = 3, + .bg_vref_level = 8, + .avdd10_level = 4, + .avdd14_level = 4, + .sparepll = 0x44, + .drive_current = { 0x3a, 0x3a, 0x3a, 0x33 }, + .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, + }, { + .frequency = 150000000, + .vcocap = 3, + .filter = 5, + .ichpmp = 5, + .loadadj = 3, + .tmds_termadj = 15, + .tx_pu_value = 0x66 /* 0 */, + .bg_temp_coef = 3, + .bg_vref_level = 8, + .avdd10_level = 4, + .avdd14_level = 4, + .sparepll = 0x00, /* 0x34 */ + .drive_current = { 0x3a, 0x3a, 0x3a, 0x37 }, + .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, + }, { + .frequency = 300000000, + .vcocap = 3, + .filter = 5, + .ichpmp = 5, + .loadadj = 3, + .tmds_termadj = 15, + .tx_pu_value = 64, + .bg_temp_coef = 3, + .bg_vref_level = 8, + .avdd10_level = 4, + .avdd14_level = 4, + .sparepll = 0x34, + .drive_current = { 0x3d, 0x3d, 0x3d, 0x33 }, + .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, + }, { + .frequency = 600000000, + .vcocap = 3, + .filter = 5, + .ichpmp = 5, + .loadadj = 3, + .tmds_termadj = 12, + .tx_pu_value = 96, + .bg_temp_coef = 3, + .bg_vref_level = 8, + .avdd10_level = 4, + .avdd14_level = 4, + .sparepll = 0x34, + .drive_current = { 0x3d, 0x3d, 0x3d, 0x33 }, + .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, + } +}; + +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; + bool has_nvdisplay; + const struct tegra_sor_hdmi_settings *settings; unsigned int num_settings; @@ -171,6 +327,7 @@ struct tegra_sor { const struct tegra_sor_soc *soc; void __iomem *regs; + unsigned int index; struct reset_control *rst; struct clk *clk_parent; @@ -183,10 +340,9 @@ struct tegra_sor { struct drm_dp_aux *aux; struct drm_info_list *debugfs_files; - struct drm_minor *minor; - struct dentry *debugfs; const struct tegra_sor_ops *ops; + enum tegra_io_pad pad; /* for HDMI 2.0 */ struct tegra_sor_hdmi_settings *settings; @@ -195,11 +351,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; }; @@ -389,23 +550,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) @@ -465,47 +626,6 @@ static int tegra_sor_dp_train_fast(struct tegra_sor *sor, return 0; } -static void tegra_sor_dp_term_calibrate(struct tegra_sor *sor) -{ - u32 mask = 0x08, adj = 0, value; - - /* enable pad calibration logic */ - value = tegra_sor_readl(sor, SOR_DP_PADCTL0); - value &= ~SOR_DP_PADCTL_PAD_CAL_PD; - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); - - value = tegra_sor_readl(sor, SOR_PLL1); - value |= SOR_PLL1_TMDS_TERM; - tegra_sor_writel(sor, value, SOR_PLL1); - - while (mask) { - adj |= mask; - - value = tegra_sor_readl(sor, SOR_PLL1); - value &= ~SOR_PLL1_TMDS_TERMADJ_MASK; - value |= SOR_PLL1_TMDS_TERMADJ(adj); - tegra_sor_writel(sor, value, SOR_PLL1); - - usleep_range(100, 200); - - value = tegra_sor_readl(sor, SOR_PLL1); - if (value & SOR_PLL1_TERM_COMPOUT) - adj &= ~mask; - - mask >>= 1; - } - - value = tegra_sor_readl(sor, SOR_PLL1); - value &= ~SOR_PLL1_TMDS_TERMADJ_MASK; - value |= SOR_PLL1_TMDS_TERMADJ(adj); - tegra_sor_writel(sor, value, SOR_PLL1); - - /* disable pad calibration logic */ - value = tegra_sor_readl(sor, SOR_DP_PADCTL0); - value |= SOR_DP_PADCTL_PAD_CAL_PD; - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); -} - static void tegra_sor_super_update(struct tegra_sor *sor) { tegra_sor_writel(sor, 0, SOR_SUPER_STATE0); @@ -897,31 +1017,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) @@ -1003,10 +1123,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 | @@ -1026,20 +1146,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); @@ -1105,12 +1225,133 @@ unlock: return err; } +#define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name } + +static const struct debugfs_reg32 tegra_sor_regs[] = { + DEBUGFS_REG32(SOR_CTXSW), + DEBUGFS_REG32(SOR_SUPER_STATE0), + DEBUGFS_REG32(SOR_SUPER_STATE1), + DEBUGFS_REG32(SOR_STATE0), + DEBUGFS_REG32(SOR_STATE1), + DEBUGFS_REG32(SOR_HEAD_STATE0(0)), + DEBUGFS_REG32(SOR_HEAD_STATE0(1)), + DEBUGFS_REG32(SOR_HEAD_STATE1(0)), + DEBUGFS_REG32(SOR_HEAD_STATE1(1)), + DEBUGFS_REG32(SOR_HEAD_STATE2(0)), + DEBUGFS_REG32(SOR_HEAD_STATE2(1)), + DEBUGFS_REG32(SOR_HEAD_STATE3(0)), + DEBUGFS_REG32(SOR_HEAD_STATE3(1)), + DEBUGFS_REG32(SOR_HEAD_STATE4(0)), + DEBUGFS_REG32(SOR_HEAD_STATE4(1)), + DEBUGFS_REG32(SOR_HEAD_STATE5(0)), + DEBUGFS_REG32(SOR_HEAD_STATE5(1)), + DEBUGFS_REG32(SOR_CRC_CNTRL), + DEBUGFS_REG32(SOR_DP_DEBUG_MVID), + DEBUGFS_REG32(SOR_CLK_CNTRL), + DEBUGFS_REG32(SOR_CAP), + DEBUGFS_REG32(SOR_PWR), + DEBUGFS_REG32(SOR_TEST), + DEBUGFS_REG32(SOR_PLL0), + DEBUGFS_REG32(SOR_PLL1), + DEBUGFS_REG32(SOR_PLL2), + DEBUGFS_REG32(SOR_PLL3), + DEBUGFS_REG32(SOR_CSTM), + DEBUGFS_REG32(SOR_LVDS), + DEBUGFS_REG32(SOR_CRCA), + DEBUGFS_REG32(SOR_CRCB), + DEBUGFS_REG32(SOR_BLANK), + DEBUGFS_REG32(SOR_SEQ_CTL), + DEBUGFS_REG32(SOR_LANE_SEQ_CTL), + DEBUGFS_REG32(SOR_SEQ_INST(0)), + DEBUGFS_REG32(SOR_SEQ_INST(1)), + DEBUGFS_REG32(SOR_SEQ_INST(2)), + DEBUGFS_REG32(SOR_SEQ_INST(3)), + DEBUGFS_REG32(SOR_SEQ_INST(4)), + DEBUGFS_REG32(SOR_SEQ_INST(5)), + DEBUGFS_REG32(SOR_SEQ_INST(6)), + DEBUGFS_REG32(SOR_SEQ_INST(7)), + DEBUGFS_REG32(SOR_SEQ_INST(8)), + DEBUGFS_REG32(SOR_SEQ_INST(9)), + DEBUGFS_REG32(SOR_SEQ_INST(10)), + DEBUGFS_REG32(SOR_SEQ_INST(11)), + DEBUGFS_REG32(SOR_SEQ_INST(12)), + DEBUGFS_REG32(SOR_SEQ_INST(13)), + DEBUGFS_REG32(SOR_SEQ_INST(14)), + DEBUGFS_REG32(SOR_SEQ_INST(15)), + DEBUGFS_REG32(SOR_PWM_DIV), + DEBUGFS_REG32(SOR_PWM_CTL), + DEBUGFS_REG32(SOR_VCRC_A0), + DEBUGFS_REG32(SOR_VCRC_A1), + DEBUGFS_REG32(SOR_VCRC_B0), + DEBUGFS_REG32(SOR_VCRC_B1), + DEBUGFS_REG32(SOR_CCRC_A0), + DEBUGFS_REG32(SOR_CCRC_A1), + DEBUGFS_REG32(SOR_CCRC_B0), + DEBUGFS_REG32(SOR_CCRC_B1), + DEBUGFS_REG32(SOR_EDATA_A0), + DEBUGFS_REG32(SOR_EDATA_A1), + DEBUGFS_REG32(SOR_EDATA_B0), + DEBUGFS_REG32(SOR_EDATA_B1), + DEBUGFS_REG32(SOR_COUNT_A0), + DEBUGFS_REG32(SOR_COUNT_A1), + DEBUGFS_REG32(SOR_COUNT_B0), + DEBUGFS_REG32(SOR_COUNT_B1), + DEBUGFS_REG32(SOR_DEBUG_A0), + DEBUGFS_REG32(SOR_DEBUG_A1), + DEBUGFS_REG32(SOR_DEBUG_B0), + DEBUGFS_REG32(SOR_DEBUG_B1), + DEBUGFS_REG32(SOR_TRIG), + DEBUGFS_REG32(SOR_MSCHECK), + DEBUGFS_REG32(SOR_XBAR_CTRL), + DEBUGFS_REG32(SOR_XBAR_POL), + DEBUGFS_REG32(SOR_DP_LINKCTL0), + DEBUGFS_REG32(SOR_DP_LINKCTL1), + DEBUGFS_REG32(SOR_LANE_DRIVE_CURRENT0), + DEBUGFS_REG32(SOR_LANE_DRIVE_CURRENT1), + DEBUGFS_REG32(SOR_LANE4_DRIVE_CURRENT0), + DEBUGFS_REG32(SOR_LANE4_DRIVE_CURRENT1), + DEBUGFS_REG32(SOR_LANE_PREEMPHASIS0), + DEBUGFS_REG32(SOR_LANE_PREEMPHASIS1), + DEBUGFS_REG32(SOR_LANE4_PREEMPHASIS0), + DEBUGFS_REG32(SOR_LANE4_PREEMPHASIS1), + DEBUGFS_REG32(SOR_LANE_POSTCURSOR0), + DEBUGFS_REG32(SOR_LANE_POSTCURSOR1), + DEBUGFS_REG32(SOR_DP_CONFIG0), + DEBUGFS_REG32(SOR_DP_CONFIG1), + DEBUGFS_REG32(SOR_DP_MN0), + DEBUGFS_REG32(SOR_DP_MN1), + DEBUGFS_REG32(SOR_DP_PADCTL0), + DEBUGFS_REG32(SOR_DP_PADCTL1), + DEBUGFS_REG32(SOR_DP_PADCTL2), + DEBUGFS_REG32(SOR_DP_DEBUG0), + DEBUGFS_REG32(SOR_DP_DEBUG1), + DEBUGFS_REG32(SOR_DP_SPARE0), + DEBUGFS_REG32(SOR_DP_SPARE1), + DEBUGFS_REG32(SOR_DP_AUDIO_CTRL), + DEBUGFS_REG32(SOR_DP_AUDIO_HBLANK_SYMBOLS), + DEBUGFS_REG32(SOR_DP_AUDIO_VBLANK_SYMBOLS), + DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_HEADER), + DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK0), + DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK1), + DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK2), + DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK3), + DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK4), + DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK5), + DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK6), + DEBUGFS_REG32(SOR_DP_TPG), + DEBUGFS_REG32(SOR_DP_TPG_CONFIG), + DEBUGFS_REG32(SOR_DP_LQ_CSTM0), + DEBUGFS_REG32(SOR_DP_LQ_CSTM1), + DEBUGFS_REG32(SOR_DP_LQ_CSTM2), +}; + static int tegra_sor_show_regs(struct seq_file *s, void *data) { struct drm_info_node *node = s->private; struct tegra_sor *sor = node->info_ent->data; struct drm_crtc *crtc = sor->output.encoder.crtc; struct drm_device *drm = node->minor->dev; + unsigned int i; int err = 0; drm_modeset_lock_all(drm); @@ -1120,126 +1361,12 @@ static int tegra_sor_show_regs(struct seq_file *s, void *data) goto unlock; } -#define DUMP_REG(name) \ - seq_printf(s, "%-38s %#05x %08x\n", #name, name, \ - tegra_sor_readl(sor, name)) - - DUMP_REG(SOR_CTXSW); - DUMP_REG(SOR_SUPER_STATE0); - DUMP_REG(SOR_SUPER_STATE1); - DUMP_REG(SOR_STATE0); - DUMP_REG(SOR_STATE1); - DUMP_REG(SOR_HEAD_STATE0(0)); - DUMP_REG(SOR_HEAD_STATE0(1)); - DUMP_REG(SOR_HEAD_STATE1(0)); - DUMP_REG(SOR_HEAD_STATE1(1)); - DUMP_REG(SOR_HEAD_STATE2(0)); - DUMP_REG(SOR_HEAD_STATE2(1)); - DUMP_REG(SOR_HEAD_STATE3(0)); - DUMP_REG(SOR_HEAD_STATE3(1)); - DUMP_REG(SOR_HEAD_STATE4(0)); - DUMP_REG(SOR_HEAD_STATE4(1)); - DUMP_REG(SOR_HEAD_STATE5(0)); - DUMP_REG(SOR_HEAD_STATE5(1)); - DUMP_REG(SOR_CRC_CNTRL); - DUMP_REG(SOR_DP_DEBUG_MVID); - DUMP_REG(SOR_CLK_CNTRL); - DUMP_REG(SOR_CAP); - DUMP_REG(SOR_PWR); - DUMP_REG(SOR_TEST); - DUMP_REG(SOR_PLL0); - DUMP_REG(SOR_PLL1); - DUMP_REG(SOR_PLL2); - DUMP_REG(SOR_PLL3); - DUMP_REG(SOR_CSTM); - DUMP_REG(SOR_LVDS); - DUMP_REG(SOR_CRCA); - DUMP_REG(SOR_CRCB); - DUMP_REG(SOR_BLANK); - DUMP_REG(SOR_SEQ_CTL); - DUMP_REG(SOR_LANE_SEQ_CTL); - DUMP_REG(SOR_SEQ_INST(0)); - DUMP_REG(SOR_SEQ_INST(1)); - DUMP_REG(SOR_SEQ_INST(2)); - DUMP_REG(SOR_SEQ_INST(3)); - DUMP_REG(SOR_SEQ_INST(4)); - DUMP_REG(SOR_SEQ_INST(5)); - DUMP_REG(SOR_SEQ_INST(6)); - DUMP_REG(SOR_SEQ_INST(7)); - DUMP_REG(SOR_SEQ_INST(8)); - DUMP_REG(SOR_SEQ_INST(9)); - DUMP_REG(SOR_SEQ_INST(10)); - DUMP_REG(SOR_SEQ_INST(11)); - DUMP_REG(SOR_SEQ_INST(12)); - DUMP_REG(SOR_SEQ_INST(13)); - DUMP_REG(SOR_SEQ_INST(14)); - DUMP_REG(SOR_SEQ_INST(15)); - DUMP_REG(SOR_PWM_DIV); - DUMP_REG(SOR_PWM_CTL); - DUMP_REG(SOR_VCRC_A0); - DUMP_REG(SOR_VCRC_A1); - DUMP_REG(SOR_VCRC_B0); - DUMP_REG(SOR_VCRC_B1); - DUMP_REG(SOR_CCRC_A0); - DUMP_REG(SOR_CCRC_A1); - DUMP_REG(SOR_CCRC_B0); - DUMP_REG(SOR_CCRC_B1); - DUMP_REG(SOR_EDATA_A0); - DUMP_REG(SOR_EDATA_A1); - DUMP_REG(SOR_EDATA_B0); - DUMP_REG(SOR_EDATA_B1); - DUMP_REG(SOR_COUNT_A0); - DUMP_REG(SOR_COUNT_A1); - DUMP_REG(SOR_COUNT_B0); - DUMP_REG(SOR_COUNT_B1); - DUMP_REG(SOR_DEBUG_A0); - DUMP_REG(SOR_DEBUG_A1); - DUMP_REG(SOR_DEBUG_B0); - DUMP_REG(SOR_DEBUG_B1); - DUMP_REG(SOR_TRIG); - DUMP_REG(SOR_MSCHECK); - DUMP_REG(SOR_XBAR_CTRL); - DUMP_REG(SOR_XBAR_POL); - DUMP_REG(SOR_DP_LINKCTL0); - DUMP_REG(SOR_DP_LINKCTL1); - DUMP_REG(SOR_LANE_DRIVE_CURRENT0); - DUMP_REG(SOR_LANE_DRIVE_CURRENT1); - DUMP_REG(SOR_LANE4_DRIVE_CURRENT0); - DUMP_REG(SOR_LANE4_DRIVE_CURRENT1); - DUMP_REG(SOR_LANE_PREEMPHASIS0); - DUMP_REG(SOR_LANE_PREEMPHASIS1); - DUMP_REG(SOR_LANE4_PREEMPHASIS0); - DUMP_REG(SOR_LANE4_PREEMPHASIS1); - DUMP_REG(SOR_LANE_POSTCURSOR0); - DUMP_REG(SOR_LANE_POSTCURSOR1); - DUMP_REG(SOR_DP_CONFIG0); - DUMP_REG(SOR_DP_CONFIG1); - DUMP_REG(SOR_DP_MN0); - DUMP_REG(SOR_DP_MN1); - DUMP_REG(SOR_DP_PADCTL0); - DUMP_REG(SOR_DP_PADCTL1); - DUMP_REG(SOR_DP_DEBUG0); - DUMP_REG(SOR_DP_DEBUG1); - DUMP_REG(SOR_DP_SPARE0); - DUMP_REG(SOR_DP_SPARE1); - DUMP_REG(SOR_DP_AUDIO_CTRL); - DUMP_REG(SOR_DP_AUDIO_HBLANK_SYMBOLS); - DUMP_REG(SOR_DP_AUDIO_VBLANK_SYMBOLS); - DUMP_REG(SOR_DP_GENERIC_INFOFRAME_HEADER); - DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK0); - DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK1); - DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK2); - DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK3); - DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK4); - DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK5); - DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK6); - DUMP_REG(SOR_DP_TPG); - DUMP_REG(SOR_DP_TPG_CONFIG); - DUMP_REG(SOR_DP_LQ_CSTM0); - DUMP_REG(SOR_DP_LQ_CSTM1); - DUMP_REG(SOR_DP_LQ_CSTM2); - -#undef DUMP_REG + for (i = 0; i < ARRAY_SIZE(tegra_sor_regs); i++) { + unsigned int offset = tegra_sor_regs[i].offset; + + seq_printf(s, "%-38s %#05x %08x\n", tegra_sor_regs[i].name, + offset, tegra_sor_readl(sor, offset)); + } unlock: drm_modeset_unlock_all(drm); @@ -1251,57 +1378,46 @@ static const struct drm_info_list debugfs_files[] = { { "regs", tegra_sor_show_regs, 0, NULL }, }; -static int tegra_sor_debugfs_init(struct tegra_sor *sor, - struct drm_minor *minor) +static int tegra_sor_late_register(struct drm_connector *connector) { - const char *name = sor->soc->supports_dp ? "sor1" : "sor"; - unsigned int i; + struct tegra_output *output = connector_to_output(connector); + unsigned int i, count = ARRAY_SIZE(debugfs_files); + struct drm_minor *minor = connector->dev->primary; + struct dentry *root = connector->debugfs_entry; + struct tegra_sor *sor = to_sor(output); int err; - sor->debugfs = debugfs_create_dir(name, minor->debugfs_root); - if (!sor->debugfs) - return -ENOMEM; - sor->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files), GFP_KERNEL); - if (!sor->debugfs_files) { - err = -ENOMEM; - goto remove; - } + if (!sor->debugfs_files) + return -ENOMEM; - for (i = 0; i < ARRAY_SIZE(debugfs_files); i++) + for (i = 0; i < count; i++) sor->debugfs_files[i].data = sor; - err = drm_debugfs_create_files(sor->debugfs_files, - ARRAY_SIZE(debugfs_files), - sor->debugfs, minor); + err = drm_debugfs_create_files(sor->debugfs_files, count, root, minor); if (err < 0) goto free; - sor->minor = minor; - return 0; free: kfree(sor->debugfs_files); sor->debugfs_files = NULL; -remove: - debugfs_remove_recursive(sor->debugfs); - sor->debugfs = NULL; + return err; } -static void tegra_sor_debugfs_exit(struct tegra_sor *sor) +static void tegra_sor_early_unregister(struct drm_connector *connector) { - drm_debugfs_remove_files(sor->debugfs_files, ARRAY_SIZE(debugfs_files), - sor->minor); - sor->minor = NULL; + struct tegra_output *output = connector_to_output(connector); + unsigned int count = ARRAY_SIZE(debugfs_files); + struct tegra_sor *sor = to_sor(output); + drm_debugfs_remove_files(sor->debugfs_files, count, + connector->dev->primary); kfree(sor->debugfs_files); sor->debugfs_files = NULL; - - debugfs_remove_recursive(sor->debugfs); - sor->debugfs = NULL; } static void tegra_sor_connector_reset(struct drm_connector *connector) @@ -1354,6 +1470,8 @@ static const struct drm_connector_funcs tegra_sor_connector_funcs = { .destroy = tegra_output_connector_destroy, .atomic_duplicate_state = tegra_sor_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .late_register = tegra_sor_late_register, + .early_unregister = tegra_sor_early_unregister, }; static int tegra_sor_connector_get_modes(struct drm_connector *connector) @@ -1377,10 +1495,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; } @@ -1417,7 +1531,7 @@ static void tegra_sor_edp_disable(struct drm_encoder *encoder) */ if (dc) { value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); - value &= ~SOR_ENABLE; + value &= ~SOR_ENABLE(0); tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); tegra_dc_commit(dc); @@ -1433,9 +1547,9 @@ static void tegra_sor_edp_disable(struct drm_encoder *encoder) dev_err(sor->dev, "failed to disable DP: %d\n", err); } - err = tegra_io_rail_power_off(TEGRA_IO_RAIL_LVDS); + err = tegra_io_pad_power_disable(sor->pad); if (err < 0) - dev_err(sor->dev, "failed to power off I/O rail: %d\n", err); + dev_err(sor->dev, "failed to power off I/O pad: %d\n", err); if (output->panel) drm_panel_unprepare(output->panel); @@ -1533,40 +1647,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 @@ -1579,49 +1693,49 @@ 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); + err = tegra_io_pad_power_enable(sor->pad); if (err < 0) - dev_err(sor->dev, "failed to power on I/O rail: %d\n", err); + dev_err(sor->dev, "failed to power on I/O pad: %d\n", err); 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++) @@ -1637,7 +1751,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); @@ -1654,7 +1768,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; @@ -1698,9 +1812,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) @@ -1773,7 +1887,7 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) tegra_sor_update(sor); value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); - value |= SOR_ENABLE; + value |= SOR_ENABLE(0); tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); tegra_dc_commit(dc); @@ -1805,6 +1919,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) { @@ -1955,6 +2081,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); @@ -1963,6 +2164,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); @@ -1972,8 +2175,12 @@ static void tegra_sor_hdmi_disable(struct drm_encoder *encoder) /* disable display to SOR clock */ value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); - value &= ~SOR1_TIMING_CYA; - value &= ~SOR1_ENABLE; + + if (!sor->soc->has_nvdisplay) + value &= ~(SOR1_TIMING_CYA | SOR_ENABLE(1)); + else + value &= ~SOR_ENABLE(sor->index); + tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); tegra_dc_commit(dc); @@ -1982,9 +2189,9 @@ static void tegra_sor_hdmi_disable(struct drm_encoder *encoder) if (err < 0) dev_err(sor->dev, "failed to power down SOR: %d\n", err); - err = tegra_io_rail_power_off(TEGRA_IO_RAIL_HDMI); + err = tegra_io_pad_power_disable(sor->pad); if (err < 0) - dev_err(sor->dev, "failed to power off HDMI rail: %d\n", err); + dev_err(sor->dev, "failed to power off I/O pad: %d\n", err); pm_runtime_put(sor->dev); } @@ -1998,12 +2205,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); @@ -2016,44 +2225,44 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) div = clk_get_rate(sor->clk) / 1000000 * 4; - err = tegra_io_rail_power_on(TEGRA_IO_RAIL_HDMI); + err = tegra_io_pad_power_enable(sor->pad); if (err < 0) - dev_err(sor->dev, "failed to power on HDMI rail: %d\n", err); + dev_err(sor->dev, "failed to power on I/O pad: %d\n", err); 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); @@ -2079,18 +2288,30 @@ 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); + /* SOR pad PLL stabilization time */ + usleep_range(250, 1000); + + value = tegra_sor_readl(sor, SOR_DP_LINKCTL0); + value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK; + value |= SOR_DP_LINKCTL_LANE_COUNT(4); + tegra_sor_writel(sor, value, SOR_DP_LINKCTL0); + value = tegra_sor_readl(sor, SOR_DP_SPARE0); - value |= SOR_DP_SPARE_DISP_VIDEO_PREAMBLE; + value &= ~SOR_DP_SPARE_DISP_VIDEO_PREAMBLE; value &= ~SOR_DP_SPARE_PANEL_INTERNAL; - value |= SOR_DP_SPARE_SEQ_ENABLE; + value &= ~SOR_DP_SPARE_SEQ_ENABLE; + value &= ~SOR_DP_SPARE_MACRO_SOR_CLK; tegra_sor_writel(sor, value, SOR_DP_SPARE0); value = SOR_SEQ_CTL_PU_PC(0) | SOR_SEQ_CTL_PU_PC_ALT(0) | @@ -2102,9 +2323,11 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) tegra_sor_writel(sor, value, SOR_SEQ_INST(0)); tegra_sor_writel(sor, value, SOR_SEQ_INST(8)); - /* program the reference clock */ - value = SOR_REFCLK_DIV_INT(div) | SOR_REFCLK_DIV_FRAC(div); - tegra_sor_writel(sor, value, SOR_REFCLK); + if (!sor->soc->has_nvdisplay) { + /* program the reference clock */ + value = SOR_REFCLK_DIV_INT(div) | SOR_REFCLK_DIV_FRAC(div); + tegra_sor_writel(sor, value, SOR_REFCLK); + } /* XXX not in TRM */ for (value = 0, i = 0; i < 5; i++) @@ -2127,13 +2350,25 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) return; } - value = SOR_INPUT_CONTROL_HDMI_SRC_SELECT(dc->pipe); + /* adjust clock rate for HDMI 2.0 modes */ + rate = clk_get_rate(sor->clk_parent); - /* XXX is this the proper check? */ - if (mode->clock < 75000) - value |= SOR_INPUT_CONTROL_ARM_VIDEO_RANGE_LIMITED; + if (mode->clock >= 340000) + rate /= 2; - tegra_sor_writel(sor, value, SOR_INPUT_CONTROL); + 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); + + /* XXX is this the proper check? */ + if (mode->clock < 75000) + value |= SOR_INPUT_CONTROL_ARM_VIDEO_RANGE_LIMITED; + + tegra_sor_writel(sor, value, SOR_INPUT_CONTROL); + } max_ac = ((mode->htotal - mode->hdisplay) - SOR_REKEY - 18) / 32; @@ -2141,20 +2376,23 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) SOR_HDMI_CTRL_AUDIO_LAYOUT | SOR_HDMI_CTRL_REKEY(SOR_REKEY); tegra_sor_writel(sor, value, SOR_HDMI_CTRL); - /* H_PULSE2 setup */ - pulse_start = h_ref_to_sync + (mode->hsync_end - mode->hsync_start) + - (mode->htotal - mode->hsync_end) - 10; + if (!dc->soc->has_nvdisplay) { + /* H_PULSE2 setup */ + pulse_start = h_ref_to_sync + + (mode->hsync_end - mode->hsync_start) + + (mode->htotal - mode->hsync_end) - 10; - value = PULSE_LAST_END_A | PULSE_QUAL_VACTIVE | - PULSE_POLARITY_HIGH | PULSE_MODE_NORMAL; - tegra_dc_writel(dc, value, DC_DISP_H_PULSE2_CONTROL); + value = PULSE_LAST_END_A | PULSE_QUAL_VACTIVE | + PULSE_POLARITY_HIGH | PULSE_MODE_NORMAL; + tegra_dc_writel(dc, value, DC_DISP_H_PULSE2_CONTROL); - value = PULSE_END(pulse_start + 8) | PULSE_START(pulse_start); - tegra_dc_writel(dc, value, DC_DISP_H_PULSE2_POSITION_A); + value = PULSE_END(pulse_start + 8) | PULSE_START(pulse_start); + tegra_dc_writel(dc, value, DC_DISP_H_PULSE2_POSITION_A); - value = tegra_dc_readl(dc, DC_DISP_DISP_SIGNAL_OPTIONS0); - value |= H_PULSE2_ENABLE; - tegra_dc_writel(dc, value, DC_DISP_DISP_SIGNAL_OPTIONS0); + value = tegra_dc_readl(dc, DC_DISP_DISP_SIGNAL_OPTIONS0); + value |= H_PULSE2_ENABLE; + tegra_dc_writel(dc, value, DC_DISP_DISP_SIGNAL_OPTIONS0); + } /* infoframe setup */ err = tegra_sor_hdmi_setup_avi_infoframe(sor, mode); @@ -2171,9 +2409,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); @@ -2183,51 +2421,68 @@ 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_FILTER_MASK; value &= ~SOR_PLL0_VCOCAP_MASK; value |= SOR_PLL0_ICHPMP(settings->ichpmp); + value |= SOR_PLL0_FILTER(settings->filter); 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); + /* XXX not in TRM */ + value = tegra_sor_readl(sor, sor->soc->regs->pll1); value &= ~SOR_PLL1_LOADADJ_MASK; + value &= ~SOR_PLL1_TMDS_TERMADJ_MASK; value |= SOR_PLL1_LOADADJ(settings->loadadj); - tegra_sor_writel(sor, value, SOR_PLL1); + value |= SOR_PLL1_TMDS_TERMADJ(settings->tmds_termadj); + value |= SOR_PLL1_TMDS_TERM; + 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_TEMP_COEF_MASK; value &= ~SOR_PLL3_BG_VREF_LEVEL_MASK; - value |= SOR_PLL3_BG_VREF_LEVEL(settings->bg_vref); - tegra_sor_writel(sor, value, SOR_PLL3); - - value = settings->drive_current[0] << 24 | - settings->drive_current[1] << 16 | - settings->drive_current[2] << 8 | - settings->drive_current[3] << 0; + value &= ~SOR_PLL3_AVDD10_LEVEL_MASK; + value &= ~SOR_PLL3_AVDD14_LEVEL_MASK; + value |= SOR_PLL3_BG_TEMP_COEF(settings->bg_temp_coef); + value |= SOR_PLL3_BG_VREF_LEVEL(settings->bg_vref_level); + value |= SOR_PLL3_AVDD10_LEVEL(settings->avdd10_level); + value |= SOR_PLL3_AVDD14_LEVEL(settings->avdd14_level); + tegra_sor_writel(sor, value, sor->soc->regs->pll3); + + value = settings->drive_current[3] << 24 | + settings->drive_current[2] << 16 | + settings->drive_current[1] << 8 | + settings->drive_current[0] << 0; tegra_sor_writel(sor, value, SOR_LANE_DRIVE_CURRENT0); - value = settings->preemphasis[0] << 24 | - settings->preemphasis[1] << 16 | - settings->preemphasis[2] << 8 | - settings->preemphasis[3] << 0; + value = settings->preemphasis[3] << 24 | + settings->preemphasis[2] << 16 | + settings->preemphasis[1] << 8 | + settings->preemphasis[0] << 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); + value |= SOR_DP_PADCTL_TX_PU(settings->tx_pu_value); + tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0); + + value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl2); + value &= ~SOR_DP_PADCTL_SPAREPLL_MASK; + value |= SOR_DP_PADCTL_SPAREPLL(settings->sparepll); + tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl2); /* 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); - tegra_dc_writel(dc, value, DC_DISP_DISP_TIMING_OPTIONS); + if (!dc->soc->has_nvdisplay) { + /* miscellaneous display controller settings */ + value = VSYNC_H_POSITION(1); + tegra_dc_writel(dc, value, DC_DISP_DISP_TIMING_OPTIONS); + } value = tegra_dc_readl(dc, DC_DISP_DISP_COLOR_CONTROL); value &= ~DITHER_CONTROL_MASK; @@ -2242,6 +2497,14 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) value |= BASE_COLOR_SIZE_888; break; + case 10: + value |= BASE_COLOR_SIZE_101010; + break; + + case 12: + value |= BASE_COLOR_SIZE_121212; + break; + default: WARN(1, "%u bits-per-color not supported\n", state->bpc); value |= BASE_COLOR_SIZE_888; @@ -2250,40 +2513,65 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) tegra_dc_writel(dc, value, DC_DISP_DISP_COLOR_CONTROL); + /* XXX set display head owner */ + value = tegra_sor_readl(sor, SOR_STATE1); + value &= ~SOR_STATE_ASY_OWNER_MASK; + value |= SOR_STATE_ASY_OWNER(1 + dc->pipe); + tegra_sor_writel(sor, value, SOR_STATE1); + err = tegra_sor_power_up(sor, 250); if (err < 0) 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); tegra_sor_update(sor); + /* program preamble timing in SOR (XXX) */ + value = tegra_sor_readl(sor, SOR_DP_SPARE0); + value &= ~SOR_DP_SPARE_DISP_VIDEO_PREAMBLE; + tegra_sor_writel(sor, value, SOR_DP_SPARE0); + err = tegra_sor_attach(sor); if (err < 0) dev_err(sor->dev, "failed to attach SOR: %d\n", err); /* enable display to SOR clock and generate HDMI preamble */ value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); - value |= SOR1_ENABLE | SOR1_TIMING_CYA; + + if (!sor->soc->has_nvdisplay) + value |= SOR_ENABLE(1) | SOR1_TIMING_CYA; + else + value |= SOR_ENABLE(sor->index); + tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); + if (dc->soc->has_nvdisplay) { + value = tegra_dc_readl(dc, DC_DISP_CORE_SOR_SET_CONTROL(sor->index)); + value &= ~PROTOCOL_MASK; + value |= PROTOCOL_SINGLE_TMDS_A; + tegra_dc_writel(dc, value, DC_DISP_CORE_SOR_SET_CONTROL(sor->index)); + } + tegra_dc_commit(dc); 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 = { @@ -2344,13 +2632,7 @@ static int tegra_sor_init(struct host1x_client *client) return err; } - sor->output.encoder.possible_crtcs = 0x3; - - if (IS_ENABLED(CONFIG_DEBUG_FS)) { - err = tegra_sor_debugfs_init(sor, drm->primary); - if (err < 0) - dev_err(sor->dev, "debugfs setup failed: %d\n", err); - } + tegra_output_find_possible_crtcs(&sor->output, drm); if (sor->aux) { err = drm_dp_aux_attach(sor->aux, &sor->output); @@ -2420,9 +2702,6 @@ static int tegra_sor_exit(struct host1x_client *client) clk_disable_unprepare(sor->clk_dp); clk_disable_unprepare(sor->clk); - if (IS_ENABLED(CONFIG_DEBUG_FS)) - tegra_sor_debugfs_exit(sor); - return 0; } @@ -2480,6 +2759,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; } @@ -2502,19 +2783,53 @@ 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, + .has_nvdisplay = false, .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, + .has_nvdisplay = false, .xbar_cfg = tegra124_sor_xbar_cfg, }; @@ -2528,13 +2843,60 @@ static const struct tegra_sor_soc tegra210_sor1 = { .supports_hdmi = true, .supports_dp = true, + .regs = &tegra210_sor_regs, + .has_nvdisplay = false, + .num_settings = ARRAY_SIZE(tegra210_sor_hdmi_defaults), .settings = tegra210_sor_hdmi_defaults, .xbar_cfg = tegra210_sor_xbar_cfg, }; +static const struct tegra_sor_regs tegra186_sor_regs = { + .head_state0 = 0x151, + .head_state1 = 0x154, + .head_state2 = 0x157, + .head_state3 = 0x15a, + .head_state4 = 0x15d, + .head_state5 = 0x160, + .pll0 = 0x163, + .pll1 = 0x164, + .pll2 = 0x165, + .pll3 = 0x166, + .dp_padctl0 = 0x168, + .dp_padctl2 = 0x16a, +}; + +static const struct tegra_sor_soc tegra186_sor = { + .supports_edp = false, + .supports_lvds = false, + .supports_hdmi = false, + .supports_dp = true, + + .regs = &tegra186_sor_regs, + .has_nvdisplay = true, + + .xbar_cfg = tegra124_sor_xbar_cfg, +}; + +static const struct tegra_sor_soc tegra186_sor1 = { + .supports_edp = false, + .supports_lvds = false, + .supports_hdmi = true, + .supports_dp = true, + + .regs = &tegra186_sor_regs, + .has_nvdisplay = true, + + .num_settings = ARRAY_SIZE(tegra186_sor_hdmi_defaults), + .settings = tegra186_sor_hdmi_defaults, + + .xbar_cfg = tegra124_sor_xbar_cfg, +}; + static const struct of_device_id tegra_sor_of_match[] = { + { .compatible = "nvidia,tegra186-sor1", .data = &tegra186_sor1 }, + { .compatible = "nvidia,tegra186-sor", .data = &tegra186_sor }, { .compatible = "nvidia,tegra210-sor1", .data = &tegra210_sor1 }, { .compatible = "nvidia,tegra210-sor", .data = &tegra210_sor }, { .compatible = "nvidia,tegra124-sor", .data = &tegra124_sor }, @@ -2542,6 +2904,29 @@ static const struct of_device_id tegra_sor_of_match[] = { }; MODULE_DEVICE_TABLE(of, tegra_sor_of_match); +static int tegra_sor_parse_dt(struct tegra_sor *sor) +{ + struct device_node *np = sor->dev->of_node; + u32 value; + int err; + + if (sor->soc->has_nvdisplay) { + err = of_property_read_u32(np, "nvidia,interface", &value); + if (err < 0) + return err; + + sor->index = value; + + /* + * override the default that we already set for Tegra210 and + * earlier + */ + sor->pad = TEGRA_IO_PAD_HDMI_DP0 + sor->index; + } + + return 0; +} + static int tegra_sor_probe(struct platform_device *pdev) { struct device_node *np; @@ -2577,6 +2962,7 @@ static int tegra_sor_probe(struct platform_device *pdev) if (!sor->aux) { if (sor->soc->supports_hdmi) { sor->ops = &tegra_sor_hdmi_ops; + sor->pad = TEGRA_IO_PAD_HDMI; } else if (sor->soc->supports_lvds) { dev_err(&pdev->dev, "LVDS not supported yet\n"); return -ENODEV; @@ -2587,6 +2973,7 @@ static int tegra_sor_probe(struct platform_device *pdev) } else { if (sor->soc->supports_edp) { sor->ops = &tegra_sor_edp_ops; + sor->pad = TEGRA_IO_PAD_LVDS; } else if (sor->soc->supports_dp) { dev_err(&pdev->dev, "DisplayPort not supported yet\n"); return -ENODEV; @@ -2596,6 +2983,10 @@ static int tegra_sor_probe(struct platform_device *pdev) } } + err = tegra_sor_parse_dt(sor); + if (err < 0) + return err; + err = tegra_output_probe(&sor->output); if (err < 0) { dev_err(&pdev->dev, "failed to probe output: %d\n", err); diff --git a/drivers/gpu/drm/tegra/sor.h b/drivers/gpu/drm/tegra/sor.h index 865c73b48968..fb0854d92a27 100644 --- a/drivers/gpu/drm/tegra/sor.h +++ b/drivers/gpu/drm/tegra/sor.h @@ -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 @@ -370,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 diff --git a/drivers/gpu/drm/tegra/vic.c b/drivers/gpu/drm/tegra/vic.c index 18024183aa2b..f5794dd49f3b 100644 --- a/drivers/gpu/drm/tegra/vic.c +++ b/drivers/gpu/drm/tegra/vic.c @@ -115,7 +115,7 @@ static int vic_boot(struct vic *vic) } static void *vic_falcon_alloc(struct falcon *falcon, size_t size, - dma_addr_t *iova) + dma_addr_t *iova) { struct tegra_drm *tegra = falcon->data; @@ -138,13 +138,14 @@ static const struct falcon_ops vic_falcon_ops = { static int vic_init(struct host1x_client *client) { struct tegra_drm_client *drm = host1x_to_drm_client(client); + struct iommu_group *group = iommu_group_get(client->dev); struct drm_device *dev = dev_get_drvdata(client->parent); struct tegra_drm *tegra = dev->dev_private; struct vic *vic = to_vic(drm); int err; - if (tegra->domain) { - err = iommu_attach_device(tegra->domain, vic->dev); + if (group && tegra->domain) { + err = iommu_attach_group(tegra->domain, group); if (err < 0) { dev_err(vic->dev, "failed to attach to domain: %d\n", err); @@ -158,13 +159,13 @@ static int vic_init(struct host1x_client *client) vic->falcon.data = tegra; err = falcon_load_firmware(&vic->falcon); if (err < 0) - goto detach_device; + goto detach; } vic->channel = host1x_channel_request(client->dev); if (!vic->channel) { err = -ENOMEM; - goto detach_device; + goto detach; } client->syncpts[0] = host1x_syncpt_request(client, 0); @@ -183,9 +184,9 @@ free_syncpt: host1x_syncpt_free(client->syncpts[0]); free_channel: host1x_channel_put(vic->channel); -detach_device: - if (tegra->domain) - iommu_detach_device(tegra->domain, vic->dev); +detach: + if (group && tegra->domain) + iommu_detach_group(tegra->domain, group); return err; } @@ -193,6 +194,7 @@ detach_device: static int vic_exit(struct host1x_client *client) { struct tegra_drm_client *drm = host1x_to_drm_client(client); + struct iommu_group *group = iommu_group_get(client->dev); struct drm_device *dev = dev_get_drvdata(client->parent); struct tegra_drm *tegra = dev->dev_private; struct vic *vic = to_vic(drm); @@ -206,7 +208,7 @@ static int vic_exit(struct host1x_client *client) host1x_channel_put(vic->channel); if (vic->domain) { - iommu_detach_device(vic->domain, vic->dev); + iommu_detach_group(vic->domain, group); vic->domain = NULL; } |