From 617dd7cc490b72345277e2666c8ed34d4f47f0da Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 30 Aug 2017 12:48:31 +0200 Subject: gpu: host1x: syncpt: Request syncpoints per client Rather than request syncpoints for a struct device *, request them for a struct host1x_client *. This is important because subsequent patches are going to break the assumption that host1x will always be the parent for devices requesting a syncpoint. It's also a more natural choice because host1x clients are really the only ones that will know how to deal with syncpoints. Note that host1x clients are always guaranteed to be children of host1x, regardless of their location in the device tree. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 4df39112e38e..bc51eb855bf1 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1756,7 +1756,7 @@ static int tegra_dc_init(struct host1x_client *client) struct drm_plane *cursor = NULL; int err; - dc->syncpt = host1x_syncpt_request(dc->dev, flags); + dc->syncpt = host1x_syncpt_request(client, flags); if (!dc->syncpt) dev_warn(dc->dev, "failed to allocate syncpoint\n"); -- cgit v1.2.3-59-g8ed1b From b9ff7aeaefbb39d9882547fa4b02e6e34a7b9463 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 21 Aug 2017 16:35:17 +0200 Subject: drm/tegra: dc: Use of_device_get_match_data() Avoid some boilerplate by calling of_device_get_match_data() instead of open-coding the equivalent in the driver. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index bc51eb855bf1..2b5e052fb8d3 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -1985,7 +1986,6 @@ static int tegra_dc_parse_dt(struct tegra_dc *dc) static int tegra_dc_probe(struct platform_device *pdev) { - const struct of_device_id *id; struct resource *regs; struct tegra_dc *dc; int err; @@ -1994,14 +1994,11 @@ static int tegra_dc_probe(struct platform_device *pdev) if (!dc) return -ENOMEM; - id = of_match_node(tegra_dc_of_match, pdev->dev.of_node); - if (!id) - return -ENODEV; + dc->soc = of_device_get_match_data(&pdev->dev); spin_lock_init(&dc->lock); INIT_LIST_HEAD(&dc->list); dc->dev = &pdev->dev; - dc->soc = id->data; err = tegra_dc_parse_dt(dc); if (err < 0) -- cgit v1.2.3-59-g8ed1b From 2d1c18fb0d1c566491dfaba09ed24881b1d82152 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 30 Aug 2017 17:25:34 +0200 Subject: drm/tegra: dc: Move some declarations to dc.h Move the DC related declarations in drm.h to dc.h where they belong. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 10 ---- drivers/gpu/drm/tegra/dc.h | 120 ++++++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/tegra/drm.h | 105 -------------------------------------- 3 files changed, 120 insertions(+), 115 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 2b5e052fb8d3..fd31974e2218 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -24,16 +24,6 @@ #include #include -struct tegra_dc_soc_info { - bool supports_border_color; - bool supports_interlacing; - bool supports_cursor; - bool supports_block_linear; - unsigned int pitch_align; - bool has_powergate; - bool broken_reset; -}; - struct tegra_plane { struct drm_plane base; unsigned int index; diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index 4a268635749b..cb100b6e3282 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -10,6 +10,126 @@ #ifndef TEGRA_DC_H #define TEGRA_DC_H 1 +#include + +#include + +#include "drm.h" + +struct tegra_output; + +struct tegra_dc_stats { + unsigned long frames; + unsigned long vblank; + unsigned long underflow; + unsigned long overflow; +}; + +struct tegra_dc_soc_info { + bool supports_border_color; + bool supports_interlacing; + bool supports_cursor; + bool supports_block_linear; + unsigned int pitch_align; + bool has_powergate; + bool broken_reset; +}; + +struct tegra_dc { + struct host1x_client client; + struct host1x_syncpt *syncpt; + struct device *dev; + spinlock_t lock; + + struct drm_crtc base; + unsigned int powergate; + int pipe; + + struct clk *clk; + struct reset_control *rst; + void __iomem *regs; + int irq; + + struct tegra_output *rgb; + + struct tegra_dc_stats stats; + 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; + + struct iommu_domain *domain; +}; + +static inline struct tegra_dc * +host1x_client_to_dc(struct host1x_client *client) +{ + return container_of(client, struct tegra_dc, client); +} + +static inline struct tegra_dc *to_tegra_dc(struct drm_crtc *crtc) +{ + return crtc ? container_of(crtc, struct tegra_dc, base) : NULL; +} + +static inline void tegra_dc_writel(struct tegra_dc *dc, u32 value, + unsigned int offset) +{ + trace_dc_writel(dc->dev, offset, value); + writel(value, dc->regs + (offset << 2)); +} + +static inline u32 tegra_dc_readl(struct tegra_dc *dc, unsigned int offset) +{ + u32 value = readl(dc->regs + (offset << 2)); + + trace_dc_readl(dc->dev, offset, value); + + return value; +} + +struct tegra_dc_window { + struct { + unsigned int x; + unsigned int y; + unsigned int w; + unsigned int h; + } src; + struct { + unsigned int x; + unsigned int y; + unsigned int w; + unsigned int h; + } dst; + unsigned int bits_per_pixel; + unsigned int stride[2]; + unsigned long base[3]; + bool bottom_up; + + struct tegra_bo_tiling tiling; + u32 format; + u32 swap; +}; + +/* from dc.c */ +void tegra_dc_commit(struct tegra_dc *dc); +int tegra_dc_state_setup_clock(struct tegra_dc *dc, + struct drm_crtc_state *crtc_state, + struct clk *clk, unsigned long pclk, + unsigned int div); + +/* from rgb.c */ +int tegra_dc_rgb_probe(struct tegra_dc *dc); +int tegra_dc_rgb_remove(struct tegra_dc *dc); +int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc); +int tegra_dc_rgb_exit(struct tegra_dc *dc); + #define DC_CMD_GENERAL_INCR_SYNCPT 0x000 #define DC_CMD_GENERAL_INCR_SYNCPT_CNTRL 0x001 #define SYNCPT_CNTRL_NO_STALL (1 << 8) diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index 063f5d397526..9bb6e8ebe40c 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -119,105 +119,6 @@ void *tegra_drm_alloc(struct tegra_drm *tegra, size_t size, dma_addr_t *iova); void tegra_drm_free(struct tegra_drm *tegra, size_t size, void *virt, dma_addr_t iova); -struct tegra_dc_soc_info; -struct tegra_output; - -struct tegra_dc_stats { - unsigned long frames; - unsigned long vblank; - unsigned long underflow; - unsigned long overflow; -}; - -struct tegra_dc { - struct host1x_client client; - struct host1x_syncpt *syncpt; - struct device *dev; - spinlock_t lock; - - struct drm_crtc base; - unsigned int powergate; - int pipe; - - struct clk *clk; - struct reset_control *rst; - void __iomem *regs; - int irq; - - struct tegra_output *rgb; - - struct tegra_dc_stats stats; - 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; - - struct iommu_domain *domain; -}; - -static inline struct tegra_dc * -host1x_client_to_dc(struct host1x_client *client) -{ - return container_of(client, struct tegra_dc, client); -} - -static inline struct tegra_dc *to_tegra_dc(struct drm_crtc *crtc) -{ - return crtc ? container_of(crtc, struct tegra_dc, base) : NULL; -} - -static inline void tegra_dc_writel(struct tegra_dc *dc, u32 value, - unsigned int offset) -{ - trace_dc_writel(dc->dev, offset, value); - writel(value, dc->regs + (offset << 2)); -} - -static inline u32 tegra_dc_readl(struct tegra_dc *dc, unsigned int offset) -{ - u32 value = readl(dc->regs + (offset << 2)); - - trace_dc_readl(dc->dev, offset, value); - - return value; -} - -struct tegra_dc_window { - struct { - unsigned int x; - unsigned int y; - unsigned int w; - unsigned int h; - } src; - struct { - unsigned int x; - unsigned int y; - unsigned int w; - unsigned int h; - } dst; - unsigned int bits_per_pixel; - unsigned int stride[2]; - unsigned long base[3]; - bool bottom_up; - - struct tegra_bo_tiling tiling; - u32 format; - u32 swap; -}; - -/* from dc.c */ -void tegra_dc_commit(struct tegra_dc *dc); -int tegra_dc_state_setup_clock(struct tegra_dc *dc, - struct drm_crtc_state *crtc_state, - struct clk *clk, unsigned long pclk, - unsigned int div); - struct tegra_output { struct device_node *of_node; struct device *dev; @@ -243,12 +144,6 @@ static inline struct tegra_output *connector_to_output(struct drm_connector *c) return container_of(c, struct tegra_output, connector); } -/* from rgb.c */ -int tegra_dc_rgb_probe(struct tegra_dc *dc); -int tegra_dc_rgb_remove(struct tegra_dc *dc); -int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc); -int tegra_dc_rgb_exit(struct tegra_dc *dc); - /* from output.c */ int tegra_output_probe(struct tegra_output *output); void tegra_output_remove(struct tegra_output *output); -- cgit v1.2.3-59-g8ed1b From a4bfa0961c4bccbfd5f23d1283fa3d40e6af1b59 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 30 Aug 2017 17:34:10 +0200 Subject: drm/tegra: dc: Simplify atomic plane helper functions Remove the tegra_dc_disable_window() function whose only purpose was to allow tegra_plane_atomic_update() to also call it. Fix that by shuffling tegra_plano_atomic_disable() to before tegra_plane_atomic_update(). While at it, also remove the overlay plane helper functions because they are exactly the same as the primary plane helper functions. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 42 ++++++++++++++---------------------------- 1 file changed, 14 insertions(+), 28 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index fd31974e2218..6a43dff70822 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -550,14 +550,21 @@ static int tegra_plane_atomic_check(struct drm_plane *plane, return 0; } -static void tegra_dc_disable_window(struct tegra_dc *dc, int index) +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 << index; + 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); @@ -582,7 +589,7 @@ static void tegra_plane_atomic_update(struct drm_plane *plane, return; if (!plane->state->visible) - return tegra_dc_disable_window(dc, p->index); + return tegra_plane_atomic_disable(plane, old_state); memset(&window, 0, sizeof(window)); window.src.x = plane->state->src.x1 >> 16; @@ -618,25 +625,10 @@ static void tegra_plane_atomic_update(struct drm_plane *plane, tegra_dc_setup_window(dc, p->index, &window); } -static void tegra_plane_atomic_disable(struct drm_plane *plane, - struct drm_plane_state *old_state) -{ - struct tegra_plane *p = to_tegra_plane(plane); - struct tegra_dc *dc; - - /* rien ne va plus */ - if (!old_state || !old_state->crtc) - return; - - dc = to_tegra_dc(old_state->crtc); - - tegra_dc_disable_window(dc, p->index); -} - -static const struct drm_plane_helper_funcs tegra_primary_plane_helper_funcs = { +static const struct drm_plane_helper_funcs tegra_plane_helper_funcs = { .atomic_check = tegra_plane_atomic_check, - .atomic_update = tegra_plane_atomic_update, .atomic_disable = tegra_plane_atomic_disable, + .atomic_update = tegra_plane_atomic_update, }; static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm, @@ -676,7 +668,7 @@ static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm, return ERR_PTR(err); } - drm_plane_helper_add(&plane->base, &tegra_primary_plane_helper_funcs); + drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs); return &plane->base; } @@ -871,12 +863,6 @@ static const uint32_t tegra_overlay_plane_formats[] = { DRM_FORMAT_YUV422, }; -static const struct drm_plane_helper_funcs tegra_overlay_plane_helper_funcs = { - .atomic_check = tegra_plane_atomic_check, - .atomic_update = tegra_plane_atomic_update, - .atomic_disable = tegra_plane_atomic_disable, -}; - static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, struct tegra_dc *dc, unsigned int index) @@ -904,7 +890,7 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, return ERR_PTR(err); } - drm_plane_helper_add(&plane->base, &tegra_overlay_plane_helper_funcs); + drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs); return &plane->base; } -- cgit v1.2.3-59-g8ed1b From 39e08affecf0998be1b01f4752016e33fa98eb9a Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 30 Aug 2017 17:38:39 +0200 Subject: drm/tegra: dc: Make sure to set the module clock rate When applying the PLL changes from the computed state object, make sure to set the rate of the display controller module clock. Failing to do so can yield to a situation where the parent will be set to the proper pixel clock, but the module clock will be divided down to the rate that is happened to be set to before the parent rate change. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 6a43dff70822..d5a63230e509 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1138,6 +1138,11 @@ static void tegra_dc_commit_state(struct tegra_dc *dc, 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) + dev_err(dc->dev, "failed to set clock %pC to %lu Hz: %d\n", + dc->clk, state->pclk, err); } static void tegra_dc_stop(struct tegra_dc *dc) -- cgit v1.2.3-59-g8ed1b From a2f2f7403e1ea192ce79584d7050c46e455409dd Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 30 Aug 2017 17:41:00 +0200 Subject: drm/tegra: dc: Perform a complete reset sequence In order for the reset to be applied properly, the module clock must be enabled during the assertion. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index d5a63230e509..24a5ef4f5bb8 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1997,8 +1997,22 @@ static int tegra_dc_probe(struct platform_device *pdev) return PTR_ERR(dc->rst); } - if (!dc->soc->broken_reset) - reset_control_assert(dc->rst); + /* assert reset and disable clock */ + if (!dc->soc->broken_reset) { + err = clk_prepare_enable(dc->clk); + if (err < 0) + return err; + + usleep_range(2000, 4000); + + err = reset_control_assert(dc->rst); + if (err < 0) + return err; + + usleep_range(2000, 4000); + + clk_disable_unprepare(dc->clk); + } if (dc->soc->has_powergate) { if (dc->pipe == 0) -- cgit v1.2.3-59-g8ed1b From 10b47ee02d1ae66160058241cf5b962f64e81b47 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 1 Nov 2017 22:15:58 +0200 Subject: drm: Check crtc_state->enable rather than crtc->enabled in drm_plane_helper_check_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drm_plane_helper_check_state() is supposed to do things the atomic way, so it should not be inspecting crtc->enabled. Rather we should be looking at crtc_state->enable. We have a slight complication due to drm_plane_helper_check_update() reusing drm_plane_helper_check_state() for non-atomic drivers. Thus we'll have to pass the crtc_state in manally and construct a fake crtc_state in drm_plane_helper_check_update(). v2: Fix the WARNs about plane_state->crtc matching crtc_state->crtc Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20171101201558.6059-1-ville.syrjala@linux.intel.com Reviewed-by: Daniel Vetter --- drivers/gpu/drm/arm/hdlcd_crtc.c | 2 +- drivers/gpu/drm/arm/malidp_planes.c | 3 +- drivers/gpu/drm/drm_plane_helper.c | 51 ++++++++++++++++------------- drivers/gpu/drm/drm_simple_kms_helper.c | 2 +- drivers/gpu/drm/i915/intel_display.c | 2 ++ drivers/gpu/drm/imx/ipuv3-plane.c | 2 +- drivers/gpu/drm/mediatek/mtk_drm_plane.c | 2 +- drivers/gpu/drm/meson/meson_plane.c | 2 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 4 +-- drivers/gpu/drm/nouveau/nv50_display.c | 6 ++-- drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 2 +- drivers/gpu/drm/tegra/dc.c | 4 +-- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 2 +- drivers/gpu/drm/zte/zx_plane.c | 4 +-- include/drm/drm_plane_helper.h | 3 +- 15 files changed, 52 insertions(+), 39 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/arm/hdlcd_crtc.c b/drivers/gpu/drm/arm/hdlcd_crtc.c index 72b22b805412..14721723fa8a 100644 --- a/drivers/gpu/drm/arm/hdlcd_crtc.c +++ b/drivers/gpu/drm/arm/hdlcd_crtc.c @@ -252,7 +252,7 @@ static int hdlcd_plane_atomic_check(struct drm_plane *plane, clip.x2 = crtc_state->adjusted_mode.hdisplay; clip.y2 = crtc_state->adjusted_mode.vdisplay; - return drm_plane_helper_check_state(state, &clip, + return drm_plane_helper_check_state(state, crtc_state, &clip, DRM_PLANE_HELPER_NO_SCALING, DRM_PLANE_HELPER_NO_SCALING, false, true); diff --git a/drivers/gpu/drm/arm/malidp_planes.c b/drivers/gpu/drm/arm/malidp_planes.c index 94e7e3fa3408..492d99dd55d4 100644 --- a/drivers/gpu/drm/arm/malidp_planes.c +++ b/drivers/gpu/drm/arm/malidp_planes.c @@ -150,7 +150,8 @@ static int malidp_se_check_scaling(struct malidp_plane *mp, clip.x2 = crtc_state->adjusted_mode.hdisplay; clip.y2 = crtc_state->adjusted_mode.vdisplay; - ret = drm_plane_helper_check_state(state, &clip, 0, INT_MAX, true, true); + ret = drm_plane_helper_check_state(state, crtc_state, &clip, + 0, INT_MAX, true, true); if (ret) return ret; diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index 759ed93f4ba8..eef0ffcd7ed5 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -101,7 +101,8 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc, /** * drm_plane_helper_check_state() - Check plane state for validity - * @state: plane state to check + * @plane_state: plane state to check + * @crtc_state: crtc state to check * @clip: integer clipping coordinates * @min_scale: minimum @src:@dest scaling factor in 16.16 fixed point * @max_scale: maximum @src:@dest scaling factor in 16.16 fixed point @@ -120,35 +121,37 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc, * RETURNS: * Zero if update appears valid, error code on failure */ -int drm_plane_helper_check_state(struct drm_plane_state *state, +int drm_plane_helper_check_state(struct drm_plane_state *plane_state, + const struct drm_crtc_state *crtc_state, const struct drm_rect *clip, int min_scale, int max_scale, bool can_position, bool can_update_disabled) { - struct drm_crtc *crtc = state->crtc; - struct drm_framebuffer *fb = state->fb; - struct drm_rect *src = &state->src; - struct drm_rect *dst = &state->dst; - unsigned int rotation = state->rotation; + struct drm_framebuffer *fb = plane_state->fb; + struct drm_rect *src = &plane_state->src; + struct drm_rect *dst = &plane_state->dst; + unsigned int rotation = plane_state->rotation; int hscale, vscale; - *src = drm_plane_state_src(state); - *dst = drm_plane_state_dest(state); + WARN_ON(plane_state->crtc && plane_state->crtc != crtc_state->crtc); + + *src = drm_plane_state_src(plane_state); + *dst = drm_plane_state_dest(plane_state); if (!fb) { - state->visible = false; + plane_state->visible = false; return 0; } /* crtc should only be NULL when disabling (i.e., !fb) */ - if (WARN_ON(!crtc)) { - state->visible = false; + if (WARN_ON(!plane_state->crtc)) { + plane_state->visible = false; return 0; } - if (!crtc->enabled && !can_update_disabled) { + if (!crtc_state->enable && !can_update_disabled) { DRM_DEBUG_KMS("Cannot update plane of a disabled CRTC.\n"); return -EINVAL; } @@ -160,16 +163,16 @@ int drm_plane_helper_check_state(struct drm_plane_state *state, vscale = drm_rect_calc_vscale(src, dst, min_scale, max_scale); if (hscale < 0 || vscale < 0) { DRM_DEBUG_KMS("Invalid scaling of plane\n"); - drm_rect_debug_print("src: ", &state->src, true); - drm_rect_debug_print("dst: ", &state->dst, false); + drm_rect_debug_print("src: ", &plane_state->src, true); + drm_rect_debug_print("dst: ", &plane_state->dst, false); return -ERANGE; } - state->visible = drm_rect_clip_scaled(src, dst, clip, hscale, vscale); + plane_state->visible = drm_rect_clip_scaled(src, dst, clip, hscale, vscale); drm_rect_rotate_inv(src, fb->width << 16, fb->height << 16, rotation); - if (!state->visible) + if (!plane_state->visible) /* * Plane isn't visible; some drivers can handle this * so we just return success here. Drivers that can't @@ -230,7 +233,7 @@ int drm_plane_helper_check_update(struct drm_plane *plane, bool can_update_disabled, bool *visible) { - struct drm_plane_state state = { + struct drm_plane_state plane_state = { .plane = plane, .crtc = crtc, .fb = fb, @@ -245,18 +248,22 @@ int drm_plane_helper_check_update(struct drm_plane *plane, .rotation = rotation, .visible = *visible, }; + struct drm_crtc_state crtc_state = { + .crtc = crtc, + .enable = crtc->enabled, + }; int ret; - ret = drm_plane_helper_check_state(&state, clip, + ret = drm_plane_helper_check_state(&plane_state, &crtc_state, clip, min_scale, max_scale, can_position, can_update_disabled); if (ret) return ret; - *src = state.src; - *dst = state.dst; - *visible = state.visible; + *src = plane_state.src; + *dst = plane_state.dst; + *visible = plane_state.visible; return 0; } diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c index dc9fd109de14..d428c805025c 100644 --- a/drivers/gpu/drm/drm_simple_kms_helper.c +++ b/drivers/gpu/drm/drm_simple_kms_helper.c @@ -103,7 +103,7 @@ static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane, clip.x2 = crtc_state->adjusted_mode.hdisplay; clip.y2 = crtc_state->adjusted_mode.vdisplay; - ret = drm_plane_helper_check_state(plane_state, &clip, + ret = drm_plane_helper_check_state(plane_state, crtc_state, &clip, DRM_PLANE_HELPER_NO_SCALING, DRM_PLANE_HELPER_NO_SCALING, false, true); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f780f39e0758..5e75d762745a 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -9419,6 +9419,7 @@ static int intel_check_cursor(struct intel_crtc_state *crtc_state, int ret; ret = drm_plane_helper_check_state(&plane_state->base, + &crtc_state->base, &plane_state->clip, DRM_PLANE_HELPER_NO_SCALING, DRM_PLANE_HELPER_NO_SCALING, @@ -12842,6 +12843,7 @@ intel_check_primary_plane(struct intel_plane *plane, } ret = drm_plane_helper_check_state(&state->base, + &crtc_state->base, &state->clip, min_scale, max_scale, can_position, true); diff --git a/drivers/gpu/drm/imx/ipuv3-plane.c b/drivers/gpu/drm/imx/ipuv3-plane.c index 247c60e6bed2..51b23ac175e3 100644 --- a/drivers/gpu/drm/imx/ipuv3-plane.c +++ b/drivers/gpu/drm/imx/ipuv3-plane.c @@ -342,7 +342,7 @@ static int ipu_plane_atomic_check(struct drm_plane *plane, clip.y1 = 0; clip.x2 = crtc_state->adjusted_mode.hdisplay; clip.y2 = crtc_state->adjusted_mode.vdisplay; - ret = drm_plane_helper_check_state(state, &clip, + ret = drm_plane_helper_check_state(state, crtc_state, &clip, DRM_PLANE_HELPER_NO_SCALING, DRM_PLANE_HELPER_NO_SCALING, can_position, true); diff --git a/drivers/gpu/drm/mediatek/mtk_drm_plane.c b/drivers/gpu/drm/mediatek/mtk_drm_plane.c index 6f121891430f..7ebb33657704 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_plane.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_plane.c @@ -111,7 +111,7 @@ static int mtk_plane_atomic_check(struct drm_plane *plane, clip.x2 = crtc_state->mode.hdisplay; clip.y2 = crtc_state->mode.vdisplay; - return drm_plane_helper_check_state(state, &clip, + return drm_plane_helper_check_state(state, crtc_state, &clip, DRM_PLANE_HELPER_NO_SCALING, DRM_PLANE_HELPER_NO_SCALING, true, true); diff --git a/drivers/gpu/drm/meson/meson_plane.c b/drivers/gpu/drm/meson/meson_plane.c index 17e96fa47868..2a00b720c371 100644 --- a/drivers/gpu/drm/meson/meson_plane.c +++ b/drivers/gpu/drm/meson/meson_plane.c @@ -61,7 +61,7 @@ static int meson_plane_atomic_check(struct drm_plane *plane, clip.x2 = crtc_state->mode.hdisplay; clip.y2 = crtc_state->mode.vdisplay; - return drm_plane_helper_check_state(state, &clip, + return drm_plane_helper_check_state(state, crtc_state, &clip, DRM_PLANE_HELPER_NO_SCALING, DRM_PLANE_HELPER_NO_SCALING, true, true); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index 4b22ac3413a1..1c194a9453d9 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -348,8 +348,8 @@ static int mdp5_plane_atomic_check_with_state(struct drm_crtc_state *crtc_state, min_scale = FRAC_16_16(1, 8); max_scale = FRAC_16_16(8, 1); - ret = drm_plane_helper_check_state(state, &clip, min_scale, - max_scale, true, true); + ret = drm_plane_helper_check_state(state, crtc_state, &clip, + min_scale, max_scale, true, true); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index d8ddb7396721..0738ae1f6cf4 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -1143,7 +1143,8 @@ nv50_curs_acquire(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw, { int ret; - ret = drm_plane_helper_check_state(&asyw->state, &asyw->clip, + ret = drm_plane_helper_check_state(&asyw->state, &asyh->state, + &asyw->clip, DRM_PLANE_HELPER_NO_SCALING, DRM_PLANE_HELPER_NO_SCALING, true, true); @@ -1432,7 +1433,8 @@ nv50_base_acquire(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw, if (!fb->format->depth) return -EINVAL; - ret = drm_plane_helper_check_state(&asyw->state, &asyw->clip, + ret = drm_plane_helper_check_state(&asyw->state, &asyh->state, + &asyw->clip, DRM_PLANE_HELPER_NO_SCALING, DRM_PLANE_HELPER_NO_SCALING, false, true); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 19128b4dea54..36d0f101e30d 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -659,7 +659,7 @@ static int vop_plane_atomic_check(struct drm_plane *plane, clip.x2 = crtc_state->adjusted_mode.hdisplay; clip.y2 = crtc_state->adjusted_mode.vdisplay; - ret = drm_plane_helper_check_state(state, &clip, + ret = drm_plane_helper_check_state(state, crtc_state, &clip, min_scale, max_scale, true, true); if (ret) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 4df39112e38e..08b899fd74fc 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -500,8 +500,8 @@ static int tegra_plane_state_add(struct tegra_plane *plane, 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); + err = drm_plane_helper_check_state(state, crtc_state, &clip, + 0, INT_MAX, true, true); if (err < 0) return err; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 9eb4d0bc1546..91555c0370e1 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -454,7 +454,7 @@ int vmw_du_primary_plane_atomic_check(struct drm_plane *plane, clip.y2 = crtc_state->adjusted_mode.vdisplay; } - ret = drm_plane_helper_check_state(state, &clip, + ret = drm_plane_helper_check_state(state, crtc_state, &clip, DRM_PLANE_HELPER_NO_SCALING, DRM_PLANE_HELPER_NO_SCALING, false, true); diff --git a/drivers/gpu/drm/zte/zx_plane.c b/drivers/gpu/drm/zte/zx_plane.c index 18e763493264..ee0002529b8f 100644 --- a/drivers/gpu/drm/zte/zx_plane.c +++ b/drivers/gpu/drm/zte/zx_plane.c @@ -80,7 +80,7 @@ static int zx_vl_plane_atomic_check(struct drm_plane *plane, clip.x2 = crtc_state->adjusted_mode.hdisplay; clip.y2 = crtc_state->adjusted_mode.vdisplay; - return drm_plane_helper_check_state(plane_state, &clip, + return drm_plane_helper_check_state(plane_state, crtc_state, &clip, min_scale, max_scale, true, true); } @@ -315,7 +315,7 @@ static int zx_gl_plane_atomic_check(struct drm_plane *plane, clip.x2 = crtc_state->adjusted_mode.hdisplay; clip.y2 = crtc_state->adjusted_mode.vdisplay; - return drm_plane_helper_check_state(plane_state, &clip, + return drm_plane_helper_check_state(plane_state, crtc_state, &clip, DRM_PLANE_HELPER_NO_SCALING, DRM_PLANE_HELPER_NO_SCALING, false, true); diff --git a/include/drm/drm_plane_helper.h b/include/drm/drm_plane_helper.h index 7c8a00ceadb7..41b8309b0a57 100644 --- a/include/drm/drm_plane_helper.h +++ b/include/drm/drm_plane_helper.h @@ -38,7 +38,8 @@ */ #define DRM_PLANE_HELPER_NO_SCALING (1<<16) -int drm_plane_helper_check_state(struct drm_plane_state *state, +int drm_plane_helper_check_state(struct drm_plane_state *plane_state, + const struct drm_crtc_state *crtc_state, const struct drm_rect *clip, int min_scale, int max_scale, bool can_position, -- cgit v1.2.3-59-g8ed1b From a01cb8ba3f6282934cff65e89ab36b18b14cbe27 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 1 Nov 2017 22:16:19 +0200 Subject: drm: Move drm_plane_helper_check_state() into drm_atomic_helper.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drm_plane_helper_check_update() isn't a transitional helper, so let's rename it to drm_atomic_helper_check_plane_state() and move it into drm_atomic_helper.c. v2: Fix the WARNs about plane_state->crtc matching crtc_state->crtc Cc: Daniel Vetter Suggested-by: Daniel Vetter Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20171101201619.6175-1-ville.syrjala@linux.intel.com Reviewed-by: Daniel Vetter --- drivers/gpu/drm/arm/hdlcd_crtc.c | 8 +-- drivers/gpu/drm/arm/malidp_planes.c | 4 +- drivers/gpu/drm/drm_atomic_helper.c | 94 +++++++++++++++++++++++++ drivers/gpu/drm/drm_plane_helper.c | 102 ++-------------------------- drivers/gpu/drm/drm_simple_kms_helper.c | 9 +-- drivers/gpu/drm/i915/intel_display.c | 22 +++--- drivers/gpu/drm/imx/ipuv3-plane.c | 8 +-- drivers/gpu/drm/mediatek/mtk_drm_plane.c | 8 +-- drivers/gpu/drm/meson/meson_plane.c | 8 +-- drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 5 +- drivers/gpu/drm/nouveau/nv50_display.c | 20 +++--- drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 6 +- drivers/gpu/drm/tegra/dc.c | 4 +- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 8 +-- drivers/gpu/drm/zte/zx_plane.c | 15 ++-- include/drm/drm_atomic_helper.h | 7 ++ include/drm/drm_plane_helper.h | 6 -- 17 files changed, 169 insertions(+), 165 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/arm/hdlcd_crtc.c b/drivers/gpu/drm/arm/hdlcd_crtc.c index 14721723fa8a..63511a3bbf6c 100644 --- a/drivers/gpu/drm/arm/hdlcd_crtc.c +++ b/drivers/gpu/drm/arm/hdlcd_crtc.c @@ -252,10 +252,10 @@ static int hdlcd_plane_atomic_check(struct drm_plane *plane, clip.x2 = crtc_state->adjusted_mode.hdisplay; clip.y2 = crtc_state->adjusted_mode.vdisplay; - return drm_plane_helper_check_state(state, crtc_state, &clip, - DRM_PLANE_HELPER_NO_SCALING, - DRM_PLANE_HELPER_NO_SCALING, - false, true); + return drm_atomic_helper_check_plane_state(state, crtc_state, &clip, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + false, true); } static void hdlcd_plane_atomic_update(struct drm_plane *plane, diff --git a/drivers/gpu/drm/arm/malidp_planes.c b/drivers/gpu/drm/arm/malidp_planes.c index 492d99dd55d4..72a07950167e 100644 --- a/drivers/gpu/drm/arm/malidp_planes.c +++ b/drivers/gpu/drm/arm/malidp_planes.c @@ -150,8 +150,8 @@ static int malidp_se_check_scaling(struct malidp_plane *mp, clip.x2 = crtc_state->adjusted_mode.hdisplay; clip.y2 = crtc_state->adjusted_mode.vdisplay; - ret = drm_plane_helper_check_state(state, crtc_state, &clip, - 0, INT_MAX, true, true); + ret = drm_atomic_helper_check_plane_state(state, crtc_state, &clip, + 0, INT_MAX, true, true); if (ret) return ret; diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 07d4058d97d0..ce6d11ae9e26 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -695,6 +695,100 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, } EXPORT_SYMBOL(drm_atomic_helper_check_modeset); +/** + * drm_atomic_helper_check_plane_state() - Check plane state for validity + * @plane_state: plane state to check + * @crtc_state: crtc state to check + * @clip: integer clipping coordinates + * @min_scale: minimum @src:@dest scaling factor in 16.16 fixed point + * @max_scale: maximum @src:@dest scaling factor in 16.16 fixed point + * @can_position: is it legal to position the plane such that it + * doesn't cover the entire crtc? This will generally + * only be false for primary planes. + * @can_update_disabled: can the plane be updated while the crtc + * is disabled? + * + * Checks that a desired plane update is valid, and updates various + * bits of derived state (clipped coordinates etc.). Drivers that provide + * their own plane handling rather than helper-provided implementations may + * still wish to call this function to avoid duplication of error checking + * code. + * + * RETURNS: + * Zero if update appears valid, error code on failure + */ +int drm_atomic_helper_check_plane_state(struct drm_plane_state *plane_state, + const struct drm_crtc_state *crtc_state, + const struct drm_rect *clip, + int min_scale, + int max_scale, + bool can_position, + bool can_update_disabled) +{ + struct drm_framebuffer *fb = plane_state->fb; + struct drm_rect *src = &plane_state->src; + struct drm_rect *dst = &plane_state->dst; + unsigned int rotation = plane_state->rotation; + int hscale, vscale; + + WARN_ON(plane_state->crtc && plane_state->crtc != crtc_state->crtc); + + *src = drm_plane_state_src(plane_state); + *dst = drm_plane_state_dest(plane_state); + + if (!fb) { + plane_state->visible = false; + return 0; + } + + /* crtc should only be NULL when disabling (i.e., !fb) */ + if (WARN_ON(!plane_state->crtc)) { + plane_state->visible = false; + return 0; + } + + if (!crtc_state->enable && !can_update_disabled) { + DRM_DEBUG_KMS("Cannot update plane of a disabled CRTC.\n"); + return -EINVAL; + } + + drm_rect_rotate(src, fb->width << 16, fb->height << 16, rotation); + + /* Check scaling */ + hscale = drm_rect_calc_hscale(src, dst, min_scale, max_scale); + vscale = drm_rect_calc_vscale(src, dst, min_scale, max_scale); + if (hscale < 0 || vscale < 0) { + DRM_DEBUG_KMS("Invalid scaling of plane\n"); + drm_rect_debug_print("src: ", &plane_state->src, true); + drm_rect_debug_print("dst: ", &plane_state->dst, false); + return -ERANGE; + } + + plane_state->visible = drm_rect_clip_scaled(src, dst, clip, hscale, vscale); + + drm_rect_rotate_inv(src, fb->width << 16, fb->height << 16, rotation); + + if (!plane_state->visible) + /* + * Plane isn't visible; some drivers can handle this + * so we just return success here. Drivers that can't + * (including those that use the primary plane helper's + * update function) will return an error from their + * update_plane handler. + */ + return 0; + + if (!can_position && !drm_rect_equals(dst, clip)) { + DRM_DEBUG_KMS("Plane must cover entire CRTC\n"); + drm_rect_debug_print("dst: ", dst, false); + drm_rect_debug_print("clip: ", clip, false); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(drm_atomic_helper_check_plane_state); + /** * drm_atomic_helper_check_planes - validate state object for planes changes * @dev: DRM device diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index eef0ffcd7ed5..f1be8cd4e387 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -99,100 +99,6 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc, return count; } -/** - * drm_plane_helper_check_state() - Check plane state for validity - * @plane_state: plane state to check - * @crtc_state: crtc state to check - * @clip: integer clipping coordinates - * @min_scale: minimum @src:@dest scaling factor in 16.16 fixed point - * @max_scale: maximum @src:@dest scaling factor in 16.16 fixed point - * @can_position: is it legal to position the plane such that it - * doesn't cover the entire crtc? This will generally - * only be false for primary planes. - * @can_update_disabled: can the plane be updated while the crtc - * is disabled? - * - * Checks that a desired plane update is valid, and updates various - * bits of derived state (clipped coordinates etc.). Drivers that provide - * their own plane handling rather than helper-provided implementations may - * still wish to call this function to avoid duplication of error checking - * code. - * - * RETURNS: - * Zero if update appears valid, error code on failure - */ -int drm_plane_helper_check_state(struct drm_plane_state *plane_state, - const struct drm_crtc_state *crtc_state, - const struct drm_rect *clip, - int min_scale, - int max_scale, - bool can_position, - bool can_update_disabled) -{ - struct drm_framebuffer *fb = plane_state->fb; - struct drm_rect *src = &plane_state->src; - struct drm_rect *dst = &plane_state->dst; - unsigned int rotation = plane_state->rotation; - int hscale, vscale; - - WARN_ON(plane_state->crtc && plane_state->crtc != crtc_state->crtc); - - *src = drm_plane_state_src(plane_state); - *dst = drm_plane_state_dest(plane_state); - - if (!fb) { - plane_state->visible = false; - return 0; - } - - /* crtc should only be NULL when disabling (i.e., !fb) */ - if (WARN_ON(!plane_state->crtc)) { - plane_state->visible = false; - return 0; - } - - if (!crtc_state->enable && !can_update_disabled) { - DRM_DEBUG_KMS("Cannot update plane of a disabled CRTC.\n"); - return -EINVAL; - } - - drm_rect_rotate(src, fb->width << 16, fb->height << 16, rotation); - - /* Check scaling */ - hscale = drm_rect_calc_hscale(src, dst, min_scale, max_scale); - vscale = drm_rect_calc_vscale(src, dst, min_scale, max_scale); - if (hscale < 0 || vscale < 0) { - DRM_DEBUG_KMS("Invalid scaling of plane\n"); - drm_rect_debug_print("src: ", &plane_state->src, true); - drm_rect_debug_print("dst: ", &plane_state->dst, false); - return -ERANGE; - } - - plane_state->visible = drm_rect_clip_scaled(src, dst, clip, hscale, vscale); - - drm_rect_rotate_inv(src, fb->width << 16, fb->height << 16, rotation); - - if (!plane_state->visible) - /* - * Plane isn't visible; some drivers can handle this - * so we just return success here. Drivers that can't - * (including those that use the primary plane helper's - * update function) will return an error from their - * update_plane handler. - */ - return 0; - - if (!can_position && !drm_rect_equals(dst, clip)) { - DRM_DEBUG_KMS("Plane must cover entire CRTC\n"); - drm_rect_debug_print("dst: ", dst, false); - drm_rect_debug_print("clip: ", clip, false); - return -EINVAL; - } - - return 0; -} -EXPORT_SYMBOL(drm_plane_helper_check_state); - /** * drm_plane_helper_check_update() - Check plane update for validity * @plane: plane object to update @@ -254,10 +160,10 @@ int drm_plane_helper_check_update(struct drm_plane *plane, }; int ret; - ret = drm_plane_helper_check_state(&plane_state, &crtc_state, clip, - min_scale, max_scale, - can_position, - can_update_disabled); + ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, + clip, min_scale, max_scale, + can_position, + can_update_disabled); if (ret) return ret; diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c index d428c805025c..9f3b1c94802b 100644 --- a/drivers/gpu/drm/drm_simple_kms_helper.c +++ b/drivers/gpu/drm/drm_simple_kms_helper.c @@ -103,10 +103,11 @@ static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane, clip.x2 = crtc_state->adjusted_mode.hdisplay; clip.y2 = crtc_state->adjusted_mode.vdisplay; - ret = drm_plane_helper_check_state(plane_state, crtc_state, &clip, - DRM_PLANE_HELPER_NO_SCALING, - DRM_PLANE_HELPER_NO_SCALING, - false, true); + ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state, + &clip, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + false, true); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 5e75d762745a..97406166417d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -9418,12 +9418,12 @@ static int intel_check_cursor(struct intel_crtc_state *crtc_state, u32 offset; int ret; - ret = drm_plane_helper_check_state(&plane_state->base, - &crtc_state->base, - &plane_state->clip, - DRM_PLANE_HELPER_NO_SCALING, - DRM_PLANE_HELPER_NO_SCALING, - true, true); + ret = drm_atomic_helper_check_plane_state(&plane_state->base, + &crtc_state->base, + &plane_state->clip, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + true, true); if (ret) return ret; @@ -12842,11 +12842,11 @@ intel_check_primary_plane(struct intel_plane *plane, can_position = true; } - ret = drm_plane_helper_check_state(&state->base, - &crtc_state->base, - &state->clip, - min_scale, max_scale, - can_position, true); + ret = drm_atomic_helper_check_plane_state(&state->base, + &crtc_state->base, + &state->clip, + min_scale, max_scale, + can_position, true); if (ret) return ret; diff --git a/drivers/gpu/drm/imx/ipuv3-plane.c b/drivers/gpu/drm/imx/ipuv3-plane.c index 51b23ac175e3..5a67daedcf4d 100644 --- a/drivers/gpu/drm/imx/ipuv3-plane.c +++ b/drivers/gpu/drm/imx/ipuv3-plane.c @@ -342,10 +342,10 @@ static int ipu_plane_atomic_check(struct drm_plane *plane, clip.y1 = 0; clip.x2 = crtc_state->adjusted_mode.hdisplay; clip.y2 = crtc_state->adjusted_mode.vdisplay; - ret = drm_plane_helper_check_state(state, crtc_state, &clip, - DRM_PLANE_HELPER_NO_SCALING, - DRM_PLANE_HELPER_NO_SCALING, - can_position, true); + ret = drm_atomic_helper_check_plane_state(state, crtc_state, &clip, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + can_position, true); if (ret) return ret; diff --git a/drivers/gpu/drm/mediatek/mtk_drm_plane.c b/drivers/gpu/drm/mediatek/mtk_drm_plane.c index 7ebb33657704..5ef898b93d8d 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_plane.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_plane.c @@ -111,10 +111,10 @@ static int mtk_plane_atomic_check(struct drm_plane *plane, clip.x2 = crtc_state->mode.hdisplay; clip.y2 = crtc_state->mode.vdisplay; - return drm_plane_helper_check_state(state, crtc_state, &clip, - DRM_PLANE_HELPER_NO_SCALING, - DRM_PLANE_HELPER_NO_SCALING, - true, true); + return drm_atomic_helper_check_plane_state(state, crtc_state, &clip, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + true, true); } static void mtk_plane_atomic_update(struct drm_plane *plane, diff --git a/drivers/gpu/drm/meson/meson_plane.c b/drivers/gpu/drm/meson/meson_plane.c index 2a00b720c371..d0a6ac8390f3 100644 --- a/drivers/gpu/drm/meson/meson_plane.c +++ b/drivers/gpu/drm/meson/meson_plane.c @@ -61,10 +61,10 @@ static int meson_plane_atomic_check(struct drm_plane *plane, clip.x2 = crtc_state->mode.hdisplay; clip.y2 = crtc_state->mode.vdisplay; - return drm_plane_helper_check_state(state, crtc_state, &clip, - DRM_PLANE_HELPER_NO_SCALING, - DRM_PLANE_HELPER_NO_SCALING, - true, true); + return drm_atomic_helper_check_plane_state(state, crtc_state, &clip, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + true, true); } /* Takes a fixed 16.16 number and converts it to integer. */ diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index 1c194a9453d9..df3360acacca 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -348,8 +348,9 @@ static int mdp5_plane_atomic_check_with_state(struct drm_crtc_state *crtc_state, min_scale = FRAC_16_16(1, 8); max_scale = FRAC_16_16(8, 1); - ret = drm_plane_helper_check_state(state, crtc_state, &clip, - min_scale, max_scale, true, true); + ret = drm_atomic_helper_check_plane_state(state, crtc_state, &clip, + min_scale, max_scale, + true, true); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 0738ae1f6cf4..9fc8fe017118 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -1143,11 +1143,11 @@ nv50_curs_acquire(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw, { int ret; - ret = drm_plane_helper_check_state(&asyw->state, &asyh->state, - &asyw->clip, - DRM_PLANE_HELPER_NO_SCALING, - DRM_PLANE_HELPER_NO_SCALING, - true, true); + ret = drm_atomic_helper_check_plane_state(&asyw->state, &asyh->state, + &asyw->clip, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + true, true); asyh->curs.visible = asyw->state.visible; if (ret || !asyh->curs.visible) return ret; @@ -1433,11 +1433,11 @@ nv50_base_acquire(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw, if (!fb->format->depth) return -EINVAL; - ret = drm_plane_helper_check_state(&asyw->state, &asyh->state, - &asyw->clip, - DRM_PLANE_HELPER_NO_SCALING, - DRM_PLANE_HELPER_NO_SCALING, - false, true); + ret = drm_atomic_helper_check_plane_state(&asyw->state, &asyh->state, + &asyw->clip, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + false, true); if (ret) return ret; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 36d0f101e30d..ba7505292b78 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -659,9 +659,9 @@ static int vop_plane_atomic_check(struct drm_plane *plane, clip.x2 = crtc_state->adjusted_mode.hdisplay; clip.y2 = crtc_state->adjusted_mode.vdisplay; - ret = drm_plane_helper_check_state(state, crtc_state, &clip, - min_scale, max_scale, - true, true); + ret = drm_atomic_helper_check_plane_state(state, crtc_state, &clip, + min_scale, max_scale, + true, true); if (ret) return ret; diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 08b899fd74fc..91919d52b207 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -500,8 +500,8 @@ static int tegra_plane_state_add(struct tegra_plane *plane, clip.y2 = crtc_state->mode.vdisplay; /* Check plane state for visibility and calculate clipping bounds */ - err = drm_plane_helper_check_state(state, crtc_state, &clip, - 0, INT_MAX, true, true); + err = drm_atomic_helper_check_plane_state(state, crtc_state, &clip, + 0, INT_MAX, true, true); if (err < 0) return err; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 91555c0370e1..13ac2c5a04ee 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -454,10 +454,10 @@ int vmw_du_primary_plane_atomic_check(struct drm_plane *plane, clip.y2 = crtc_state->adjusted_mode.vdisplay; } - ret = drm_plane_helper_check_state(state, crtc_state, &clip, - DRM_PLANE_HELPER_NO_SCALING, - DRM_PLANE_HELPER_NO_SCALING, - false, true); + ret = drm_atomic_helper_check_plane_state(state, crtc_state, &clip, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + false, true); if (!ret && new_fb) { struct drm_crtc *crtc = state->crtc; diff --git a/drivers/gpu/drm/zte/zx_plane.c b/drivers/gpu/drm/zte/zx_plane.c index ee0002529b8f..68fd2e2dc78a 100644 --- a/drivers/gpu/drm/zte/zx_plane.c +++ b/drivers/gpu/drm/zte/zx_plane.c @@ -80,9 +80,9 @@ static int zx_vl_plane_atomic_check(struct drm_plane *plane, clip.x2 = crtc_state->adjusted_mode.hdisplay; clip.y2 = crtc_state->adjusted_mode.vdisplay; - return drm_plane_helper_check_state(plane_state, crtc_state, &clip, - min_scale, max_scale, - true, true); + return drm_atomic_helper_check_plane_state(plane_state, crtc_state, + &clip, min_scale, max_scale, + true, true); } static int zx_vl_get_fmt(uint32_t format) @@ -315,10 +315,11 @@ static int zx_gl_plane_atomic_check(struct drm_plane *plane, clip.x2 = crtc_state->adjusted_mode.hdisplay; clip.y2 = crtc_state->adjusted_mode.vdisplay; - return drm_plane_helper_check_state(plane_state, crtc_state, &clip, - DRM_PLANE_HELPER_NO_SCALING, - DRM_PLANE_HELPER_NO_SCALING, - false, true); + return drm_atomic_helper_check_plane_state(plane_state, crtc_state, + &clip, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + false, true); } static int zx_gl_get_fmt(uint32_t format) diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index d2b56cc657e9..4842ee9485ce 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -38,6 +38,13 @@ struct drm_private_state; int drm_atomic_helper_check_modeset(struct drm_device *dev, struct drm_atomic_state *state); +int drm_atomic_helper_check_plane_state(struct drm_plane_state *plane_state, + const struct drm_crtc_state *crtc_state, + const struct drm_rect *clip, + int min_scale, + int max_scale, + bool can_position, + bool can_update_disabled); int drm_atomic_helper_check_planes(struct drm_device *dev, struct drm_atomic_state *state); int drm_atomic_helper_check(struct drm_device *dev, diff --git a/include/drm/drm_plane_helper.h b/include/drm/drm_plane_helper.h index 41b8309b0a57..8aa49c0ecd4d 100644 --- a/include/drm/drm_plane_helper.h +++ b/include/drm/drm_plane_helper.h @@ -38,12 +38,6 @@ */ #define DRM_PLANE_HELPER_NO_SCALING (1<<16) -int drm_plane_helper_check_state(struct drm_plane_state *plane_state, - const struct drm_crtc_state *crtc_state, - const struct drm_rect *clip, - int min_scale, int max_scale, - bool can_position, - bool can_update_disabled); int drm_plane_helper_check_update(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb, -- cgit v1.2.3-59-g8ed1b From cf6824ac7261c5e2cd98bf7aafe5d132431e0d7c Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 10 Nov 2017 12:13:22 +0100 Subject: drm/tegra: dc: Move register definitions into a table After commit 67e04d1ab19b ("drm/tegra: dc: Trace register accesses"), the debugfs register dump implementation causes excessive stack usage and can result in build warnings. To fix this, move the register definitions into a table and iterate over the table while dumping the registers to debugfs. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 442 +++++++++++++++++++++++---------------------- 1 file changed, 224 insertions(+), 218 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index fc70351b9017..0c77473d6510 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1383,10 +1383,228 @@ static irqreturn_t tegra_dc_irq(int irq, void *data) return IRQ_HANDLED; } +#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); @@ -1396,224 +1614,12 @@ static int tegra_dc_show_regs(struct seq_file *s, void *data) 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 + 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); -- cgit v1.2.3-59-g8ed1b From c49c81e21ce1a646c1402597d12137c679ac7f71 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 8 Nov 2017 13:32:05 +0100 Subject: drm/tegra: dc: Reshuffle some code Reshuffle some code so that functions are defined closer to where they are used. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 152 ++++++++++++++++++++++----------------------- 1 file changed, 76 insertions(+), 76 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 0c77473d6510..271357482401 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -909,82 +909,6 @@ static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc) 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); -} - -static int tegra_dc_enable_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); - - 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); - - spin_lock(&dc->lock); - - /* 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); - - spin_unlock(&dc->lock); - - 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; - } - - spin_unlock_irqrestore(&drm->event_lock, flags); -} - static void tegra_dc_destroy(struct drm_crtc *crtc) { drm_crtc_cleanup(crtc); @@ -1035,6 +959,47 @@ static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc, kfree(state); } +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); +} + +static int tegra_dc_enable_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); + + 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 const struct drm_crtc_funcs tegra_crtc_funcs = { .page_flip = drm_atomic_helper_page_flip, .set_config = drm_atomic_helper_set_config, @@ -1342,6 +1307,41 @@ static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { .atomic_disable = tegra_crtc_atomic_disable, }; +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); + + spin_lock(&dc->lock); + + /* 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); + + spin_unlock(&dc->lock); + + 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; + } + + spin_unlock_irqrestore(&drm->event_lock, flags); +} + static irqreturn_t tegra_dc_irq(int irq, void *data) { struct tegra_dc *dc = data; -- cgit v1.2.3-59-g8ed1b From b95800eeef09f4853904ec4e926d098f1ca4698a Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 8 Nov 2017 13:40:54 +0100 Subject: drm/tegra: dc: Register debugfs in ->late_register() The ->late_register() and ->early_unregister() callbacks are called at the right time to make sure userspace only accesses interfaces when it should. Move debugfs registration and unregistration to these callback functions to avoid potential races with userspace. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 1120 +++++++++++++++++++++----------------------- drivers/gpu/drm/tegra/dc.h | 2 - 2 files changed, 546 insertions(+), 576 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 271357482401..29777604df39 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -959,37 +959,370 @@ static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc, kfree(state); } -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); +#define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name } - /* fallback to software emulated VBLANK counter */ - return drm_crtc_vblank_count(&dc->base); -} +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_enable_vblank(struct drm_crtc *crtc) +static int tegra_dc_show_regs(struct seq_file *s, void *data) { - struct tegra_dc *dc = to_tegra_dc(crtc); - unsigned long value, flags; + struct drm_info_node *node = s->private; + struct tegra_dc *dc = node->info_ent->data; + unsigned int i; + int err = 0; - spin_lock_irqsave(&dc->lock, flags); + drm_modeset_lock(&dc->base.mutex, NULL); - value = tegra_dc_readl(dc, DC_CMD_INT_MASK); - value |= VBLANK_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_MASK); + if (!dc->base.state->active) { + err = -EBUSY; + goto unlock; + } - spin_unlock_irqrestore(&dc->lock, flags); + for (i = 0; i < ARRAY_SIZE(tegra_dc_regs); i++) { + unsigned int offset = tegra_dc_regs[i].offset; - return 0; + 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 void tegra_dc_disable_vblank(struct drm_crtc *crtc) +static int tegra_dc_show_crc(struct seq_file *s, void *data) { - struct tegra_dc *dc = to_tegra_dc(crtc); - unsigned long value, flags; + 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 = crtc->debugfs_entry; + struct tegra_dc *dc = to_tegra_dc(crtc); + int err; + + 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); + + if (dc->syncpt) + 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); + 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); @@ -1007,6 +1340,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, @@ -1148,590 +1483,239 @@ static int tegra_dc_wait_idle(struct tegra_dc *dc, unsigned long timeout) static void tegra_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) -{ - struct tegra_dc *dc = to_tegra_dc(crtc); - u32 value; - - if (!tegra_dc_idle(dc)) { - tegra_dc_stop(dc); - - /* - * Ignore the return value, there isn't anything useful to do - * in case this fails. - */ - tegra_dc_wait_idle(dc, 100); - } - - /* - * This should really be part of the RGB encoder driver, but clearing - * these bits has the side-effect of stopping the display controller. - * When that happens no VBLANK interrupts will be raised. At the same - * time the encoder is disabled before the display controller, so the - * above code is always going to timeout waiting for the controller - * to go idle. - * - * Given the close coupling between the RGB encoder and the display - * controller doing it here is still kind of okay. None of the other - * encoder drivers require these bits to be cleared. - * - * XXX: Perhaps given that the display controller is switched off at - * this point anyway maybe clearing these bits isn't even useful for - * the RGB encoder? - */ - if (dc->rgb) { - 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); - } - - tegra_dc_stats_reset(&dc->stats); - drm_crtc_vblank_off(crtc); - - pm_runtime_put_sync(dc->dev); -} - -static void tegra_crtc_atomic_enable(struct drm_crtc *crtc, - struct drm_crtc_state *old_state) -{ - struct drm_display_mode *mode = &crtc->state->adjusted_mode; - struct tegra_dc_state *state = to_dc_state(crtc->state); - struct tegra_dc *dc = to_tegra_dc(crtc); - u32 value; - - pm_runtime_get_sync(dc->dev); - - /* initialize display controller */ - if (dc->syncpt) { - u32 syncpt = host1x_syncpt_id(dc->syncpt); - - value = SYNCPT_CNTRL_NO_STALL; - tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); - - value = SYNCPT_VSYNC_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); - - /* 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) - tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR); - - /* apply PLL and pixel clock changes */ - tegra_dc_commit_state(dc, state); - - /* program display mode */ - tegra_dc_set_timings(dc, mode); - - /* interlacing isn't supported yet, so disable it */ - if (dc->soc->supports_interlacing) { - value = tegra_dc_readl(dc, DC_DISP_INTERLACE_CONTROL); - value &= ~INTERLACE_ENABLE; - tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL); - } - - value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); - value &= ~DISP_CTRL_MODE_MASK; - 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); - - tegra_dc_commit(dc); - - drm_crtc_vblank_on(crtc); -} - -static int tegra_crtc_atomic_check(struct drm_crtc *crtc, - struct drm_crtc_state *state) -{ - 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); - - if (crtc->state->event) { - crtc->state->event->pipe = drm_crtc_index(crtc); - - WARN_ON(drm_crtc_vblank_get(crtc) != 0); - - dc->event = crtc->state->event; - crtc->state->event = NULL; - } -} - -static void tegra_crtc_atomic_flush(struct drm_crtc *crtc, - struct drm_crtc_state *old_crtc_state) -{ - struct tegra_dc_state *state = to_dc_state(crtc->state); - struct tegra_dc *dc = to_tegra_dc(crtc); - - tegra_dc_writel(dc, state->planes << 8, DC_CMD_STATE_CONTROL); - tegra_dc_writel(dc, state->planes, DC_CMD_STATE_CONTROL); -} - -static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { - .atomic_check = tegra_crtc_atomic_check, - .atomic_begin = tegra_crtc_atomic_begin, - .atomic_flush = tegra_crtc_atomic_flush, - .atomic_enable = tegra_crtc_atomic_enable, - .atomic_disable = tegra_crtc_atomic_disable, -}; - -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); - - spin_lock(&dc->lock); - - /* 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); - - spin_unlock(&dc->lock); - - 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; - } - - spin_unlock_irqrestore(&drm->event_lock, flags); -} - -static irqreturn_t tegra_dc_irq(int irq, void *data) -{ - struct tegra_dc *dc = data; - unsigned long status; - - status = tegra_dc_readl(dc, DC_CMD_INT_STATUS); - tegra_dc_writel(dc, status, DC_CMD_INT_STATUS); - - if (status & FRAME_END_INT) { - /* - dev_dbg(dc->dev, "%s(): frame end\n", __func__); - */ - dc->stats.frames++; - } - - if (status & VBLANK_INT) { - /* - dev_dbg(dc->dev, "%s(): vertical blank\n", __func__); - */ - drm_crtc_handle_vblank(&dc->base); - tegra_dc_finish_page_flip(dc); - dc->stats.vblank++; - } - - if (status & (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT)) { - /* - dev_dbg(dc->dev, "%s(): underflow\n", __func__); - */ - dc->stats.underflow++; - } - - if (status & (WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT)) { - /* - dev_dbg(dc->dev, "%s(): overflow\n", __func__); - */ - dc->stats.overflow++; - } - - return IRQ_HANDLED; -} - -#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; +{ + struct tegra_dc *dc = to_tegra_dc(crtc); + u32 value; - drm_modeset_lock(&dc->base.mutex, NULL); + if (!tegra_dc_idle(dc)) { + tegra_dc_stop(dc); - if (!dc->base.state->active) { - err = -EBUSY; - goto unlock; + /* + * Ignore the return value, there isn't anything useful to do + * in case this fails. + */ + tegra_dc_wait_idle(dc, 100); } - 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)); + /* + * This should really be part of the RGB encoder driver, but clearing + * these bits has the side-effect of stopping the display controller. + * When that happens no VBLANK interrupts will be raised. At the same + * time the encoder is disabled before the display controller, so the + * above code is always going to timeout waiting for the controller + * to go idle. + * + * Given the close coupling between the RGB encoder and the display + * controller doing it here is still kind of okay. None of the other + * encoder drivers require these bits to be cleared. + * + * XXX: Perhaps given that the display controller is switched off at + * this point anyway maybe clearing these bits isn't even useful for + * the RGB encoder? + */ + if (dc->rgb) { + 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); } -unlock: - drm_modeset_unlock(&dc->base.mutex); - return err; + tegra_dc_stats_reset(&dc->stats); + drm_crtc_vblank_off(crtc); + + pm_runtime_put_sync(dc->dev); } -static int tegra_dc_show_crc(struct seq_file *s, void *data) +static void tegra_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) { - struct drm_info_node *node = s->private; - struct tegra_dc *dc = node->info_ent->data; - int err = 0; + struct drm_display_mode *mode = &crtc->state->adjusted_mode; + struct tegra_dc_state *state = to_dc_state(crtc->state); + struct tegra_dc *dc = to_tegra_dc(crtc); u32 value; - drm_modeset_lock(&dc->base.mutex, NULL); + pm_runtime_get_sync(dc->dev); - if (!dc->base.state->active) { - err = -EBUSY; - goto unlock; + /* initialize display controller */ + if (dc->syncpt) { + u32 syncpt = host1x_syncpt_id(dc->syncpt); + + value = SYNCPT_CNTRL_NO_STALL; + tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); + + value = SYNCPT_VSYNC_ENABLE | syncpt; + tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC); } - 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); + 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); - drm_crtc_wait_one_vblank(&dc->base); - drm_crtc_wait_one_vblank(&dc->base); + 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); - value = tegra_dc_readl(dc, DC_COM_CRC_CHECKSUM); - seq_printf(s, "%08x\n", value); + /* 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); - tegra_dc_writel(dc, 0, DC_COM_CRC_CONTROL); + 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); -unlock: - drm_modeset_unlock(&dc->base.mutex); - return err; + 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) + tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR); + + /* apply PLL and pixel clock changes */ + tegra_dc_commit_state(dc, state); + + /* program display mode */ + tegra_dc_set_timings(dc, mode); + + /* interlacing isn't supported yet, so disable it */ + if (dc->soc->supports_interlacing) { + value = tegra_dc_readl(dc, DC_DISP_INTERLACE_CONTROL); + value &= ~INTERLACE_ENABLE; + tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL); + } + + value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); + value &= ~DISP_CTRL_MODE_MASK; + 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); + + tegra_dc_commit(dc); + + drm_crtc_vblank_on(crtc); } -static int tegra_dc_show_stats(struct seq_file *s, void *data) +static int tegra_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) { - struct drm_info_node *node = s->private; - struct tegra_dc *dc = node->info_ent->data; + return 0; +} - 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); +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); - return 0; + if (crtc->state->event) { + crtc->state->event->pipe = drm_crtc_index(crtc); + + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + + dc->event = crtc->state->event; + crtc->state->event = NULL; + } } -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 void tegra_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct tegra_dc_state *state = to_dc_state(crtc->state); + struct tegra_dc *dc = to_tegra_dc(crtc); + + tegra_dc_writel(dc, state->planes << 8, DC_CMD_STATE_CONTROL); + tegra_dc_writel(dc, state->planes, DC_CMD_STATE_CONTROL); +} + +static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { + .atomic_check = tegra_crtc_atomic_check, + .atomic_begin = tegra_crtc_atomic_begin, + .atomic_flush = tegra_crtc_atomic_flush, + .atomic_enable = tegra_crtc_atomic_enable, + .atomic_disable = tegra_crtc_atomic_disable, }; -static int tegra_dc_debugfs_init(struct tegra_dc *dc, struct drm_minor *minor) +static void tegra_dc_finish_page_flip(struct tegra_dc *dc) { - 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); + struct drm_device *drm = dc->base.dev; + struct drm_crtc *crtc = &dc->base; + unsigned long flags, base; + struct tegra_bo *bo; - if (!dc->debugfs) - return -ENOMEM; + spin_lock_irqsave(&drm->event_lock, flags); - dc->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files), - GFP_KERNEL); - if (!dc->debugfs_files) { - err = -ENOMEM; - goto remove; + if (!dc->event) { + spin_unlock_irqrestore(&drm->event_lock, flags); + return; } - for (i = 0; i < ARRAY_SIZE(debugfs_files); i++) - dc->debugfs_files[i].data = dc; + bo = tegra_fb_get_plane(crtc->primary->fb, 0); - err = drm_debugfs_create_files(dc->debugfs_files, - ARRAY_SIZE(debugfs_files), - dc->debugfs, minor); - if (err < 0) - goto free; + spin_lock(&dc->lock); - dc->minor = minor; + /* 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); - return 0; + spin_unlock(&dc->lock); -free: - kfree(dc->debugfs_files); - dc->debugfs_files = NULL; -remove: - debugfs_remove(dc->debugfs); - dc->debugfs = NULL; + 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; + } - return err; + spin_unlock_irqrestore(&drm->event_lock, flags); } -static int tegra_dc_debugfs_exit(struct tegra_dc *dc) +static irqreturn_t tegra_dc_irq(int irq, void *data) { - drm_debugfs_remove_files(dc->debugfs_files, ARRAY_SIZE(debugfs_files), - dc->minor); - dc->minor = NULL; + struct tegra_dc *dc = data; + unsigned long status; - kfree(dc->debugfs_files); - dc->debugfs_files = NULL; + status = tegra_dc_readl(dc, DC_CMD_INT_STATUS); + tegra_dc_writel(dc, status, DC_CMD_INT_STATUS); + + if (status & FRAME_END_INT) { + /* + dev_dbg(dc->dev, "%s(): frame end\n", __func__); + */ + dc->stats.frames++; + } - debugfs_remove(dc->debugfs); - dc->debugfs = NULL; + if (status & VBLANK_INT) { + /* + dev_dbg(dc->dev, "%s(): vertical blank\n", __func__); + */ + drm_crtc_handle_vblank(&dc->base); + tegra_dc_finish_page_flip(dc); + dc->stats.vblank++; + } - return 0; + if (status & (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT)) { + /* + dev_dbg(dc->dev, "%s(): underflow\n", __func__); + */ + dc->stats.underflow++; + } + + if (status & (WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT)) { + /* + dev_dbg(dc->dev, "%s(): overflow\n", __func__); + */ + dc->stats.overflow++; + } + + return IRQ_HANDLED; } static int tegra_dc_init(struct host1x_client *client) @@ -1797,12 +1781,6 @@ static int tegra_dc_init(struct host1x_client *client) 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) { @@ -1835,12 +1813,6 @@ static int tegra_dc_exit(struct host1x_client *client) 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); diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index cb100b6e3282..032c734dd470 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -56,8 +56,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; -- cgit v1.2.3-59-g8ed1b From 7116e9a8022cd468a6a952e1d0d662da73f75ce5 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 13 Nov 2017 11:20:48 +0100 Subject: drm/tegra: dc: Support background color Starting with Tegra124, the interface to set the background color (the value generated for pixels that are not sourced from any window) is via a different register. Earlier generations called this the border color. Reverse the feature flag and assume that IP revisions that don't have support for background color will support border color instead. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 14 ++++++++------ drivers/gpu/drm/tegra/dc.h | 8 +++++++- 2 files changed, 15 insertions(+), 7 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 29777604df39..6cee248601a9 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1572,7 +1572,9 @@ static void tegra_crtc_atomic_enable(struct drm_crtc *crtc, 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 */ @@ -1835,7 +1837,7 @@ 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, @@ -1845,7 +1847,7 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = { }; 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, @@ -1855,7 +1857,7 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = { }; 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, @@ -1865,7 +1867,7 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = { }; 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, @@ -1875,7 +1877,7 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = { }; 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, diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index 032c734dd470..571a6c78619f 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -26,7 +26,7 @@ struct tegra_dc_stats { }; struct tegra_dc_soc_info { - bool supports_border_color; + bool supports_background_color; bool supports_interlacing; bool supports_cursor; bool supports_block_linear; @@ -447,6 +447,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) -- cgit v1.2.3-59-g8ed1b From 31b02caea3b30b044fe39301aad34886f7fd2556 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 12 Oct 2017 17:40:46 +0200 Subject: drm/tegra: Use atomic commit helpers There's no reason not to use them, and they already get all the semantics right, so rip out all of the custom code and replace it by the helpers. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 27 ++++++++----- drivers/gpu/drm/tegra/dc.h | 2 + drivers/gpu/drm/tegra/drm.c | 99 +++++---------------------------------------- drivers/gpu/drm/tegra/drm.h | 6 --- 4 files changed, 27 insertions(+), 107 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 6cee248601a9..b6aa669a7cd8 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1648,8 +1648,8 @@ 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; + u64 base = 0, phys = 0; + unsigned long flags; spin_lock_irqsave(&drm->event_lock, flags); @@ -1658,19 +1658,24 @@ static void tegra_dc_finish_page_flip(struct tegra_dc *dc) return; } - bo = tegra_fb_get_plane(crtc->primary->fb, 0); + if (crtc->primary->fb) { + struct tegra_bo *bo = tegra_fb_get_plane(crtc->primary->fb, 0); - spin_lock(&dc->lock); + phys = bo->paddr + crtc->primary->fb->offsets[0]; - /* 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); + spin_lock(&dc->lock); - spin_unlock(&dc->lock); + /* 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 = (u64)tegra_dc_readl(dc, DC_WINBUF_START_ADDR_HI) << 32; + base |= tegra_dc_readl(dc, DC_WINBUF_START_ADDR); + tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); + + spin_unlock(&dc->lock); + } - if (base == bo->paddr + crtc->primary->fb->offsets[0]) { + if (base == phys) { drm_crtc_send_vblank_event(crtc, dc->event); drm_crtc_vblank_put(crtc); dc->event = NULL; diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index 571a6c78619f..581818064b4f 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -579,6 +579,8 @@ 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_AD_UFLOW_STATUS 0xbca #define DC_WINBUF_BD_UFLOW_STATUS 0xdca #define DC_WINBUF_CD_UFLOW_STATUS 0xfca diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index de1d122ba820..90d876fc6ea7 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -33,97 +33,18 @@ struct tegra_drm_file { struct mutex lock; }; -static void tegra_atomic_schedule(struct tegra_drm *tegra, - struct drm_atomic_state *state) -{ - tegra->commit.state = state; - schedule_work(&tegra->commit.work); -} - -static void tegra_atomic_complete(struct tegra_drm *tegra, - struct drm_atomic_state *state) -{ - struct drm_device *drm = tegra->drm; - - /* - * 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. - */ - - 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); - - drm_atomic_helper_wait_for_vblanks(drm, state); - - drm_atomic_helper_cleanup_planes(drm, state); - drm_atomic_state_put(state); -} - -static void tegra_atomic_work(struct work_struct *work) -{ - struct tegra_drm *tegra = container_of(work, struct tegra_drm, - commit.work); - - tegra_atomic_complete(tegra, tegra->commit.state); -} - -static int tegra_atomic_commit(struct drm_device *drm, - struct drm_atomic_state *state, bool nonblock) -{ - struct tegra_drm *tegra = drm->dev_private; - int err; - - err = drm_atomic_helper_prepare_planes(drm, state); - if (err) - return err; - - /* 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); - - mutex_unlock(&tegra->commit.lock); - return 0; -} - -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, #endif .atomic_check = drm_atomic_helper_check, - .atomic_commit = tegra_atomic_commit, + .atomic_commit = drm_atomic_helper_commit, +}; + +static const struct drm_mode_config_helper_funcs +tegra_drm_mode_config_helpers = { + .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, }; static int tegra_drm_load(struct drm_device *drm, unsigned long flags) @@ -172,9 +93,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 +106,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) diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index ddae331ad8b6..a3d84c4af357 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -62,12 +62,6 @@ struct tegra_drm { unsigned int pitch_align; - struct { - struct drm_atomic_state *state; - struct work_struct work; - struct mutex lock; - } commit; - struct drm_atomic_state *state; }; -- cgit v1.2.3-59-g8ed1b From 9d99ab6e5fe27980a2a7cdd1957993099727530f Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 12 Oct 2017 17:40:46 +0200 Subject: drm/tegra: Remove custom page-flip handler Tegra display hardware has GO bits and meets all the requirements to use drm_crtc_arm_vblank_event(). Use it instead and get rid of the hand- rolled implementation. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 62 +++++++++++++--------------------------------- drivers/gpu/drm/tegra/dc.h | 3 --- 2 files changed, 17 insertions(+), 48 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index b6aa669a7cd8..05442d78e13d 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1523,6 +1523,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); } @@ -1614,14 +1623,18 @@ static int tegra_crtc_atomic_check(struct drm_crtc *crtc, 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); + + 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); - WARN_ON(drm_crtc_vblank_get(crtc) != 0); + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); - dc->event = crtc->state->event; crtc->state->event = NULL; } } @@ -1644,46 +1657,6 @@ static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { .atomic_disable = tegra_crtc_atomic_disable, }; -static void tegra_dc_finish_page_flip(struct tegra_dc *dc) -{ - struct drm_device *drm = dc->base.dev; - struct drm_crtc *crtc = &dc->base; - u64 base = 0, phys = 0; - unsigned long flags; - - spin_lock_irqsave(&drm->event_lock, flags); - - if (!dc->event) { - spin_unlock_irqrestore(&drm->event_lock, flags); - return; - } - - if (crtc->primary->fb) { - struct tegra_bo *bo = tegra_fb_get_plane(crtc->primary->fb, 0); - - phys = bo->paddr + crtc->primary->fb->offsets[0]; - - spin_lock(&dc->lock); - - /* 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 = (u64)tegra_dc_readl(dc, DC_WINBUF_START_ADDR_HI) << 32; - base |= tegra_dc_readl(dc, DC_WINBUF_START_ADDR); - tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); - - spin_unlock(&dc->lock); - } - - if (base == phys) { - drm_crtc_send_vblank_event(crtc, dc->event); - drm_crtc_vblank_put(crtc); - dc->event = NULL; - } - - spin_unlock_irqrestore(&drm->event_lock, flags); -} - static irqreturn_t tegra_dc_irq(int irq, void *data) { struct tegra_dc *dc = data; @@ -1704,7 +1677,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++; } diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index 581818064b4f..6e46e4aed10b 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -57,9 +57,6 @@ struct tegra_dc { struct drm_info_list *debugfs_files; - /* page-flip handling */ - struct drm_pending_vblank_event *event; - const struct tegra_dc_soc_info *soc; struct iommu_domain *domain; -- cgit v1.2.3-59-g8ed1b From 6f70ec54e1c11b916c16ec3caa71e4ed2c248aa3 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 30 Aug 2017 18:02:51 +0200 Subject: drm/tegra: dc: Remove tegra_primary_plane_destroy() This function is a simple wrapper around tegra_plane_destroy(), so it can be dropped. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 05442d78e13d..bcdcba78491b 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -417,11 +417,6 @@ static const u32 tegra_primary_plane_formats[] = { DRM_FORMAT_RGB565, }; -static void tegra_primary_plane_destroy(struct drm_plane *plane) -{ - tegra_plane_destroy(plane); -} - static void tegra_plane_reset(struct drm_plane *plane) { struct tegra_plane_state *state; @@ -466,7 +461,7 @@ static void tegra_plane_atomic_destroy_state(struct drm_plane *plane, static const struct drm_plane_funcs tegra_primary_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, - .destroy = tegra_primary_plane_destroy, + .destroy = tegra_plane_destroy, .reset = tegra_plane_reset, .atomic_duplicate_state = tegra_plane_atomic_duplicate_state, .atomic_destroy_state = tegra_plane_atomic_destroy_state, -- cgit v1.2.3-59-g8ed1b From c1cb4b6171aeaee06a8d70e93ef57f7923f50010 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 30 Aug 2017 18:04:12 +0200 Subject: drm/tegra: dc: Remove duplicate plane funcs Both tegra_primary_plane_funcs and tegra_cursor_plane_funcs are identical. Get rid of the duplicate and use one set of function pointers for all planes. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index bcdcba78491b..a3f2924824dc 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -458,7 +458,7 @@ static void tegra_plane_atomic_destroy_state(struct drm_plane *plane, kfree(state); } -static const struct drm_plane_funcs tegra_primary_plane_funcs = { +static const struct drm_plane_funcs tegra_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = tegra_plane_destroy, @@ -655,7 +655,7 @@ static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm, formats = tegra_primary_plane_formats; err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, - &tegra_primary_plane_funcs, formats, + &tegra_plane_funcs, formats, num_formats, NULL, DRM_PLANE_TYPE_PRIMARY, NULL); if (err < 0) { @@ -781,15 +781,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, @@ -821,7 +812,7 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, formats = tegra_cursor_plane_formats; err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe, - &tegra_cursor_plane_funcs, formats, + &tegra_plane_funcs, formats, num_formats, NULL, DRM_PLANE_TYPE_CURSOR, NULL); if (err < 0) { -- cgit v1.2.3-59-g8ed1b From b652ab1db477f8556c098adb2e5951f36ce1f0c4 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 30 Aug 2017 18:02:51 +0200 Subject: drm/tegra: dc: Remove tegra_overlay_plane_destroy() This function is a simple wrapper around tegra_plane_destroy(), so it can be dropped. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index a3f2924824dc..e60f44a186bb 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -825,15 +825,10 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, return &plane->base; } -static void tegra_overlay_plane_destroy(struct drm_plane *plane) -{ - tegra_plane_destroy(plane); -} - static const struct drm_plane_funcs tegra_overlay_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, - .destroy = tegra_overlay_plane_destroy, + .destroy = tegra_plane_destroy, .reset = tegra_plane_reset, .atomic_duplicate_state = tegra_plane_atomic_duplicate_state, .atomic_destroy_state = tegra_plane_atomic_destroy_state, -- cgit v1.2.3-59-g8ed1b From 301e0ddb3458255ed0fb3bb1f46b9caf186eafb5 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 30 Aug 2017 18:04:12 +0200 Subject: drm/tegra: dc: Remove duplicate plane funcs Both tegra_overlay_plane_funcs is identical to tegra_plane_funcs. Get rid of the duplicate and use one set of function pointers for all planes. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index e60f44a186bb..775e51c7a5c0 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -825,15 +825,6 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, return &plane->base; } -static const struct drm_plane_funcs tegra_overlay_plane_funcs = { - .update_plane = drm_atomic_helper_update_plane, - .disable_plane = drm_atomic_helper_disable_plane, - .destroy = tegra_plane_destroy, - .reset = tegra_plane_reset, - .atomic_duplicate_state = tegra_plane_atomic_duplicate_state, - .atomic_destroy_state = tegra_plane_atomic_destroy_state, -}; - static const uint32_t tegra_overlay_plane_formats[] = { DRM_FORMAT_XBGR8888, DRM_FORMAT_XRGB8888, @@ -863,7 +854,7 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, formats = tegra_overlay_plane_formats; err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe, - &tegra_overlay_plane_funcs, formats, + &tegra_plane_funcs, formats, num_formats, NULL, DRM_PLANE_TYPE_OVERLAY, NULL); if (err < 0) { -- cgit v1.2.3-59-g8ed1b From b1415ff21d43e264b32c18faf1c7ae7e53bc235e Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 10 Nov 2017 16:00:05 +0100 Subject: drm/tegra: dc: Move state definition to header Move the display controller state definition to the header file so that it can be referenced by other files. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 18 ------------------ drivers/gpu/drm/tegra/dc.h | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 18 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 775e51c7a5c0..7b017c03feb3 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -34,24 +34,6 @@ static inline struct tegra_plane *to_tegra_plane(struct drm_plane *plane) return container_of(plane, struct tegra_plane, base); } -struct tegra_dc_state { - struct drm_crtc_state base; - - struct clk *clk; - unsigned long pclk; - unsigned int div; - - u32 planes; -}; - -static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state) -{ - if (state) - return container_of(state, struct tegra_dc_state, base); - - return NULL; -} - struct tegra_plane_state { struct drm_plane_state base; diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index 6e46e4aed10b..b65dfbb0af89 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; -- cgit v1.2.3-59-g8ed1b From 5acd351427361131c583dfb11c7bf4c364c98a9b Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 10 Nov 2017 15:27:25 +0100 Subject: drm/tegra: Move common plane code to separate file Subsequent patches will add support for Tegra186 which has a different architecture and needs different plane code but which can share a lot of code with earlier Tegra support. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/Makefile | 1 + drivers/gpu/drm/tegra/dc.c | 199 ++--------------------------------------- drivers/gpu/drm/tegra/plane.c | 180 +++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/tegra/plane.h | 59 ++++++++++++ 4 files changed, 245 insertions(+), 194 deletions(-) create mode 100644 drivers/gpu/drm/tegra/plane.c create mode 100644 drivers/gpu/drm/tegra/plane.h (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/Makefile b/drivers/gpu/drm/tegra/Makefile index 46d65d39214d..a47784765217 100644 --- a/drivers/gpu/drm/tegra/Makefile +++ b/drivers/gpu/drm/tegra/Makefile @@ -5,6 +5,7 @@ tegra-drm-y := \ drm.o \ gem.o \ fb.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 7b017c03feb3..3aa0da6244db 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -19,38 +19,12 @@ #include "dc.h" #include "drm.h" #include "gem.h" +#include "plane.h" #include #include #include -struct tegra_plane { - struct drm_plane base; - unsigned int index; -}; - -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; -}; - -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; -} - static void tegra_dc_stats_reset(struct tegra_dc_stats *stats) { stats->frames = 0; @@ -97,81 +71,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) { @@ -223,7 +122,7 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, * 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 @@ -385,101 +284,12 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, spin_unlock_irqrestore(&dc->lock, flags); } -static void tegra_plane_destroy(struct drm_plane *plane) -{ - struct tegra_plane *p = to_tegra_plane(plane); - - drm_plane_cleanup(plane); - kfree(p); -} - static const u32 tegra_primary_plane_formats[] = { DRM_FORMAT_XBGR8888, DRM_FORMAT_XRGB8888, DRM_FORMAT_RGB565, }; -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_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 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; -} - static int tegra_plane_atomic_check(struct drm_plane *plane, struct drm_plane_state *state) { @@ -493,8 +303,9 @@ static int tegra_plane_atomic_check(struct drm_plane *plane, 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, + &plane_state->format, + &plane_state->swap); if (err < 0) return err; diff --git a/drivers/gpu/drm/tegra/plane.c b/drivers/gpu/drm/tegra/plane.c new file mode 100644 index 000000000000..78b24aa1ac83 --- /dev/null +++ b/drivers/gpu/drm/tegra/plane.c @@ -0,0 +1,180 @@ +/* + * 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 +#include +#include + +#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; + + 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); +} + +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_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) + 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; +} diff --git a/drivers/gpu/drm/tegra/plane.h b/drivers/gpu/drm/tegra/plane.h new file mode 100644 index 000000000000..8237b885acd7 --- /dev/null +++ b/drivers/gpu/drm/tegra/plane.h @@ -0,0 +1,59 @@ +/* + * 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 + +struct tegra_bo; + +struct tegra_plane { + struct drm_plane base; + 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; +}; + +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); + +#endif /* TEGRA_PLANE_H */ -- cgit v1.2.3-59-g8ed1b From c4755fb9064f64083fe559e92a46df817fc5e07b Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 13 Nov 2017 11:08:13 +0100 Subject: drm/tegra: Add Tegra186 display hub support The display architecture has changed in several significant ways with the new Tegra186 SoC. Shared between all display controllers is a set of common resources referred to as the display hub. The hub generates accesses to memory and feeds them into various composition pipelines, each of which being a window that can be assigned to arbitrary heads. Atomic state is subclassed in order to track the global bandwidth requirements and select and adjust the hub clocks appropriately. The plane code is shared to a large degree with earlier SoC generations, except where the programming differs. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/Makefile | 1 + drivers/gpu/drm/tegra/dc.c | 31 ++ drivers/gpu/drm/tegra/dc.h | 114 ++++++ drivers/gpu/drm/tegra/drm.c | 67 +++- drivers/gpu/drm/tegra/drm.h | 19 + drivers/gpu/drm/tegra/hub.c | 790 +++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/tegra/hub.h | 82 +++++ drivers/gpu/drm/tegra/plane.h | 2 + 8 files changed, 1103 insertions(+), 3 deletions(-) create mode 100644 drivers/gpu/drm/tegra/hub.c create mode 100644 drivers/gpu/drm/tegra/hub.h (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/Makefile b/drivers/gpu/drm/tegra/Makefile index a47784765217..2e0d6213f6bc 100644 --- a/drivers/gpu/drm/tegra/Makefile +++ b/drivers/gpu/drm/tegra/Makefile @@ -5,6 +5,7 @@ tegra-drm-y := \ drm.o \ gem.o \ fb.o \ + hub.o \ plane.o \ dc.o \ output.o \ diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 3aa0da6244db..25e998f8f339 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -447,6 +447,15 @@ static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm, num_formats = ARRAY_SIZE(tegra_primary_plane_formats); formats = tegra_primary_plane_formats; + /* + * XXX compute offset so that we can directly access windows. + * + * Always use window A as primary window. + */ + plane->offset = 0; + plane->index = 0; + plane->depth = 255; + err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, &tegra_plane_funcs, formats, num_formats, NULL, @@ -641,7 +650,10 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, if (!plane) return ERR_PTR(-ENOMEM); + /* XXX compute offset so that we can directly access windows */ + plane->offset = 0; plane->index = index; + plane->depth = 0; num_formats = ARRAY_SIZE(tegra_overlay_plane_formats); formats = tegra_overlay_plane_formats; @@ -1382,6 +1394,25 @@ static void tegra_crtc_atomic_enable(struct drm_crtc *crtc, static int tegra_crtc_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state) { + struct tegra_atomic_state *s = to_tegra_atomic_state(state->state); + struct tegra_dc_state *tegra = to_dc_state(state); + + /* + * The display hub display clock needs to be fed by the display clock + * with the highest frequency to ensure proper functioning of all the + * displays. + * + * Note that this isn't used before Tegra186, but it doesn't hurt and + * conditionalizing it would make the code less clean. + */ + if (state->active) { + if (!s->clk_disp || tegra->pclk > s->rate) { + s->dc = to_tegra_dc(crtc); + s->clk_disp = s->dc->clk; + s->rate = tegra->pclk; + } + } + return 0; } diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index b65dfbb0af89..22c5091006bc 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -209,6 +209,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 @@ -486,6 +488,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 @@ -596,8 +627,91 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #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_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/drm.c b/drivers/gpu/drm/tegra/drm.c index 90d876fc6ea7..98a2494b4ed0 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -33,6 +33,35 @@ struct tegra_drm_file { struct mutex lock; }; +static struct drm_atomic_state * +tegra_atomic_state_alloc(struct drm_device *drm) +{ + struct tegra_atomic_state *state = kzalloc(sizeof(*state), GFP_KERNEL); + + if (!state || drm_atomic_state_init(drm, &state->base) < 0) { + kfree(state); + return NULL; + } + + return &state->base; +} + +static void tegra_atomic_state_clear(struct drm_atomic_state *state) +{ + struct tegra_atomic_state *tegra = to_tegra_atomic_state(state); + + drm_atomic_state_default_clear(state); + tegra->clk_disp = NULL; + tegra->dc = NULL; + tegra->rate = 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_config_funcs = { .fb_create = tegra_fb_create, #ifdef CONFIG_DRM_FBDEV_EMULATION @@ -40,11 +69,32 @@ static const struct drm_mode_config_funcs tegra_drm_mode_config_funcs = { #endif .atomic_check = drm_atomic_helper_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 = drm_atomic_helper_commit_tail_rpm, + .atomic_commit_tail = tegra_atomic_commit_tail, }; static int tegra_drm_load(struct drm_device *drm, unsigned long flags) @@ -119,6 +169,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 @@ -131,16 +187,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: @@ -1235,6 +1294,7 @@ 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-vic", }, { /* sentinel */ } }; @@ -1250,6 +1310,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 a3d84c4af357..d4dfd239e2a5 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -23,6 +24,7 @@ #include #include "gem.h" +#include "hub.h" #include "trace.h" struct reset_control; @@ -40,6 +42,20 @@ struct tegra_fbdev { }; #endif +struct tegra_atomic_state { + struct drm_atomic_state base; + + struct clk *clk_disp; + struct tegra_dc *dc; + unsigned long rate; +}; + +static inline struct tegra_atomic_state * +to_tegra_atomic_state(struct drm_atomic_state *state) +{ + return container_of(state, struct tegra_atomic_state, base); +} + struct tegra_drm { struct drm_device *drm; @@ -62,6 +78,8 @@ struct tegra_drm { unsigned int pitch_align; + struct tegra_display_hub *hub; + struct drm_atomic_state *state; }; @@ -187,6 +205,7 @@ void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev); void tegra_fb_output_poll_changed(struct drm_device *drm); #endif +extern struct platform_driver tegra_display_hub_driver; extern struct platform_driver tegra_dc_driver; extern struct platform_driver tegra_hdmi_driver; extern struct platform_driver tegra_dsi_driver; diff --git a/drivers/gpu/drm/tegra/hub.c b/drivers/gpu/drm/tegra/hub.c new file mode 100644 index 000000000000..33d008fb8745 --- /dev/null +++ b/drivers/gpu/drm/tegra/hub.c @@ -0,0 +1,790 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "drm.h" +#include "dc.h" +#include "plane.h" + +static const u32 tegra_shared_plane_formats[] = { + DRM_FORMAT_XBGR8888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB565, +}; + +static inline unsigned int tegra_plane_offset(struct tegra_shared_plane *plane, + unsigned int offset) +{ + struct tegra_plane *p = &plane->base; + + if (offset >= 0x500 && offset <= 0x581) { + offset = 0x000 + (offset - 0x500); + return p->offset + offset; + } + + if (offset >= 0x700 && offset <= 0x73c) { + offset = 0x180 + (offset - 0x700); + return p->offset + offset; + } + + if (offset >= 0x800 && offset <= 0x83e) { + offset = 0x1c0 + (offset - 0x800); + return p->offset + offset; + } + + dev_WARN(plane->dc->dev, "invalid offset: %x\n", offset); + + return p->offset + offset; +} + +static inline u32 tegra_plane_readl(struct tegra_shared_plane *plane, + unsigned int offset) +{ + return tegra_dc_readl(plane->dc, tegra_plane_offset(plane, offset)); +} + +static inline void tegra_plane_writel(struct tegra_shared_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_shared_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_shared_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_shared_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_shared_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->base.index); + } + + return false; +} + +static int tegra_shared_plane_set_owner(struct tegra_shared_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->base.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_shared_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_shared_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_shared_plane *p = to_tegra_shared_plane(plane); + struct tegra_dc *dc = to_tegra_dc(old_state->crtc); + 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_shared_plane *p = to_tegra_shared_plane(plane); + struct tegra_dc *dc = to_tegra_dc(plane->state->crtc); + struct drm_framebuffer *fb = plane->state->fb; + 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(p->base.depth); + 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->base.depth = 0; + + 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); + + 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 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 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..965c333736b0 --- /dev/null +++ b/drivers/gpu/drm/tegra/hub.h @@ -0,0 +1,82 @@ +/* + * 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 +#include + +#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; + struct tegra_dc *dc; +}; + +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/plane.h b/drivers/gpu/drm/tegra/plane.h index 8237b885acd7..fc7566f630fa 100644 --- a/drivers/gpu/drm/tegra/plane.h +++ b/drivers/gpu/drm/tegra/plane.h @@ -15,7 +15,9 @@ struct tegra_bo; struct tegra_plane { struct drm_plane base; + unsigned int offset; unsigned int index; + unsigned int depth; }; struct tegra_cursor { -- cgit v1.2.3-59-g8ed1b From 473079549f27eab5ad449f2c4f079014f0fe74a5 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 30 Aug 2017 17:42:54 +0200 Subject: drm/tegra: dc: Add Tegra186 support The display architecture has changed in several signifcant ways with the new Tegra186 SoC. Display controllers are a completely different design, but have been given a frontend that simulates the register interface for earlier chips. Unfortunately the frontend isn't completely backwards compatible, so the driver needs parameterization to take the changes into account. One big change is that the total number of display controllers has been increased to three. At the same time the number of planes available has remained constant. However, planes can now be freely assigned between the display controllers, giving applications more flexibility in making the best use of the available resources. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 250 +++++++++++++++++++++++++++++++++++--------- drivers/gpu/drm/tegra/dc.h | 98 +++++++++++------ drivers/gpu/drm/tegra/drm.c | 1 + 3 files changed, 267 insertions(+), 82 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 25e998f8f339..5878d42d153c 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -19,6 +19,7 @@ #include "dc.h" #include "drm.h" #include "gem.h" +#include "hub.h" #include "plane.h" #include @@ -419,8 +420,8 @@ 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 struct drm_plane *tegra_primary_plane_create(struct drm_device *drm, + struct tegra_dc *dc) { /* * Ideally this would use drm_crtc_mask(), but that would require the @@ -435,6 +436,7 @@ static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm, * the same as drm_crtc_index() after registration. */ unsigned long possible_crtcs = 1 << drm->mode_config.num_crtc; + enum drm_plane_type type = DRM_PLANE_TYPE_PRIMARY; struct tegra_plane *plane; unsigned int num_formats; const u32 *formats; @@ -458,8 +460,7 @@ static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm, err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, &tegra_plane_funcs, formats, - num_formats, NULL, - DRM_PLANE_TYPE_PRIMARY, NULL); + num_formats, NULL, type, NULL); if (err < 0) { kfree(plane); return ERR_PTR(err); @@ -672,18 +673,61 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, return &plane->base; } -static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc) +static struct drm_plane *tegra_dc_add_shared_planes(struct drm_device *drm, + struct tegra_dc *dc) +{ + struct drm_plane *plane, *primary = NULL; + unsigned int i, j; + + for (i = 0; i < dc->soc->num_wgrps; i++) { + const struct tegra_windowgroup_soc *wgrp = &dc->soc->wgrps[i]; + + if (wgrp->dc == dc->pipe) { + for (j = 0; j < wgrp->num_windows; j++) { + unsigned int index = wgrp->windows[j]; + + plane = tegra_shared_plane_create(drm, dc, + wgrp->index, + index); + if (IS_ERR(plane)) + return plane; + + /* + * Choose the first shared plane owned by this + * head as the primary plane. + */ + if (!primary) { + plane->type = DRM_PLANE_TYPE_PRIMARY; + primary = plane; + } + } + } + } + + return primary; +} + +static struct drm_plane *tegra_dc_add_planes(struct drm_device *drm, + struct tegra_dc *dc) { - struct drm_plane *plane; + struct drm_plane *plane, *primary; unsigned int i; + primary = tegra_primary_plane_create(drm, dc); + if (IS_ERR(primary)) + return primary; + for (i = 0; i < 2; i++) { plane = tegra_dc_overlay_plane_create(drm, dc, 1 + i); - if (IS_ERR(plane)) - return PTR_ERR(plane); + if (IS_ERR(plane)) { + /* XXX tegra_plane_destroy() */ + drm_plane_cleanup(primary); + kfree(primary); + return plane; + } } - return 0; + return primary; } static void tegra_dc_destroy(struct drm_crtc *crtc) @@ -1073,7 +1117,8 @@ static u32 tegra_dc_get_vblank_counter(struct drm_crtc *crtc) { struct tegra_dc *dc = to_tegra_dc(crtc); - if (dc->syncpt) + /* XXX vblank syncpoints don't work with nvdisplay yet */ + if (dc->syncpt && !dc->soc->has_nvdisplay) return host1x_syncpt_read(dc->syncpt); /* fallback to software emulated VBLANK counter */ @@ -1131,10 +1176,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); @@ -1213,8 +1260,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) @@ -1324,39 +1373,66 @@ static void tegra_crtc_atomic_enable(struct drm_crtc *crtc, /* initialize display controller */ if (dc->syncpt) { - u32 syncpt = host1x_syncpt_id(dc->syncpt); + u32 syncpt = host1x_syncpt_id(dc->syncpt), enable; + + if (dc->soc->has_nvdisplay) + enable = 1 << 31; + else + enable = 1 << 8; value = SYNCPT_CNTRL_NO_STALL; tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); - value = SYNCPT_VSYNC_ENABLE | syncpt; + value = enable | syncpt; tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC); } - value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | - WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_TYPE); - - value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | - WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY); + if (dc->soc->has_nvdisplay) { + value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT | + DSC_OBUF_UF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_TYPE); - /* initialize timer */ - value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) | - WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20); - tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY); + value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT | + DSC_OBUF_UF_INT | SD3_BUCKET_WALK_DONE_INT | + HEAD_UF_INT | MSF_INT | REG_TMOUT_INT | + REGION_CRC_INT | V_PULSE2_INT | V_PULSE3_INT | + VBLANK_INT | FRAME_END_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY); - value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) | - WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1); - tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER); + value = SD3_BUCKET_WALK_DONE_INT | HEAD_UF_INT | VBLANK_INT | + FRAME_END_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); - value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | - WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); + value = HEAD_UF_INT | REG_TMOUT_INT | FRAME_END_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_MASK); - value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | - WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_MASK); + tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); + } else { + value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_TYPE); + + value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY); + + /* initialize timer */ + value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) | + WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20); + tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY); + + value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) | + WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1); + tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER); + + value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); + + value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_MASK); + } if (dc->soc->supports_background_color) tegra_dc_writel(dc, 0, DC_DISP_BLEND_BACKGROUND_COLOR); @@ -1381,10 +1457,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); @@ -1440,9 +1524,15 @@ static void tegra_crtc_atomic_flush(struct drm_crtc *crtc, { struct tegra_dc_state *state = to_dc_state(crtc->state); struct tegra_dc *dc = to_tegra_dc(crtc); + u32 value; + + value = state->planes << 8 | GENERAL_UPDATE; + tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); + value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); - tegra_dc_writel(dc, state->planes << 8, DC_CMD_STATE_CONTROL); - tegra_dc_writel(dc, state->planes, DC_CMD_STATE_CONTROL); + value = state->planes | GENERAL_ACT_REQ; + tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); + value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); } static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { @@ -1490,6 +1580,11 @@ static irqreturn_t tegra_dc_irq(int irq, void *data) dc->stats.overflow++; } + if (status & HEAD_UF_INT) { + dev_dbg_ratelimited(dc->dev, "%s(): head underflow\n", __func__); + dc->stats.underflow++; + } + return IRQ_HANDLED; } @@ -1518,7 +1613,11 @@ static int tegra_dc_init(struct host1x_client *client) dc->domain = tegra->domain; } - primary = tegra_dc_primary_plane_create(drm, dc); + if (dc->soc->wgrps) + primary = tegra_dc_add_shared_planes(drm, dc); + else + primary = tegra_dc_add_planes(drm, dc); + if (IS_ERR(primary)) { err = PTR_ERR(primary); goto cleanup; @@ -1552,10 +1651,6 @@ static int tegra_dc_init(struct host1x_client *client) goto cleanup; } - err = tegra_dc_add_planes(drm, dc); - if (err < 0) - goto cleanup; - err = devm_request_irq(dc->dev, dc->irq, tegra_dc_irq, 0, dev_name(dc->dev), dc); if (err < 0) { @@ -1567,10 +1662,10 @@ static int tegra_dc_init(struct host1x_client *client) return 0; cleanup: - if (cursor) + if (!IS_ERR_OR_NULL(cursor)) drm_plane_cleanup(cursor); - if (primary) + if (!IS_ERR(primary)) drm_plane_cleanup(primary); if (tegra->domain) { @@ -1617,6 +1712,7 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = { .pitch_align = 8, .has_powergate = false, .broken_reset = true, + .has_nvdisplay = false, }; static const struct tegra_dc_soc_info tegra30_dc_soc_info = { @@ -1627,6 +1723,7 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = { .pitch_align = 8, .has_powergate = false, .broken_reset = false, + .has_nvdisplay = false, }; static const struct tegra_dc_soc_info tegra114_dc_soc_info = { @@ -1637,6 +1734,7 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = { .pitch_align = 64, .has_powergate = true, .broken_reset = false, + .has_nvdisplay = false, }; static const struct tegra_dc_soc_info tegra124_dc_soc_info = { @@ -1647,6 +1745,7 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = { .pitch_align = 64, .has_powergate = true, .broken_reset = false, + .has_nvdisplay = false, }; static const struct tegra_dc_soc_info tegra210_dc_soc_info = { @@ -1657,10 +1756,61 @@ static const struct tegra_dc_soc_info tegra210_dc_soc_info = { .pitch_align = 64, .has_powergate = true, .broken_reset = false, + .has_nvdisplay = false, +}; + +static const struct tegra_windowgroup_soc tegra186_dc_wgrps[] = { + { + .index = 0, + .dc = 0, + .windows = (const unsigned int[]) { 0 }, + .num_windows = 1, + }, { + .index = 1, + .dc = 1, + .windows = (const unsigned int[]) { 1 }, + .num_windows = 1, + }, { + .index = 2, + .dc = 1, + .windows = (const unsigned int[]) { 2 }, + .num_windows = 1, + }, { + .index = 3, + .dc = 2, + .windows = (const unsigned int[]) { 3 }, + .num_windows = 1, + }, { + .index = 4, + .dc = 2, + .windows = (const unsigned int[]) { 4 }, + .num_windows = 1, + }, { + .index = 5, + .dc = 2, + .windows = (const unsigned int[]) { 5 }, + .num_windows = 1, + }, +}; + +static const struct tegra_dc_soc_info tegra186_dc_soc_info = { + .supports_background_color = true, + .supports_interlacing = true, + .supports_cursor = true, + .supports_block_linear = true, + .pitch_align = 64, + .has_powergate = false, + .broken_reset = false, + .has_nvdisplay = true, + .wgrps = tegra186_dc_wgrps, + .num_wgrps = ARRAY_SIZE(tegra186_dc_wgrps), }; static const struct of_device_id tegra_dc_of_match[] = { { + .compatible = "nvidia,tegra186-dc", + .data = &tegra186_dc_soc_info, + }, { .compatible = "nvidia,tegra210-dc", .data = &tegra210_dc_soc_info, }, { diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index 22c5091006bc..47f43663adcb 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -43,6 +43,13 @@ 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_background_color; bool supports_interlacing; @@ -51,6 +58,9 @@ struct tegra_dc_soc_info { unsigned int pitch_align; bool has_powergate; bool broken_reset; + bool has_nvdisplay; + const struct tegra_windowgroup_soc *wgrps; + unsigned int num_wgrps; }; struct tegra_dc { @@ -180,15 +190,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 @@ -253,6 +274,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) @@ -375,29 +400,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) @@ -571,16 +600,16 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define WIN_COLOR_DEPTH_YUV422RA 25 #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 @@ -596,6 +625,7 @@ 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 @@ -635,6 +665,10 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #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) diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 98a2494b4ed0..8ade997c2b6c 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -1295,6 +1295,7 @@ static const struct of_device_id host1x_drm_subdevs[] = { { .compatible = "nvidia,tegra210-sor1", }, { .compatible = "nvidia,tegra210-vic", }, { .compatible = "nvidia,tegra186-display", }, + { .compatible = "nvidia,tegra186-dc", }, { .compatible = "nvidia,tegra186-vic", }, { /* sentinel */ } }; -- cgit v1.2.3-59-g8ed1b From 7772fdaef939891d790c3461a4d1681417e0f51f Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 12 Oct 2017 17:30:55 +0200 Subject: drm/tegra: Support ARGB and ABGR formats These formats can easily be supported on all generations of Tegra. Note that the XRGB and XBGR formats that we supported were in fact using the ARGB and ABGR Tegra formats. This happened to work in cases where no alpha was being considered. This change is also a fix for those formats. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 4 ++++ drivers/gpu/drm/tegra/dc.h | 2 ++ drivers/gpu/drm/tegra/plane.c | 8 ++++++++ 3 files changed, 14 insertions(+) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 5878d42d153c..6790e2d869c4 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -287,7 +287,9 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, static const u32 tegra_primary_plane_formats[] = { DRM_FORMAT_XBGR8888, + DRM_FORMAT_ABGR8888, DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, DRM_FORMAT_RGB565, }; @@ -630,7 +632,9 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, static const uint32_t tegra_overlay_plane_formats[] = { DRM_FORMAT_XBGR8888, + DRM_FORMAT_ABGR8888, DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, DRM_FORMAT_RGB565, DRM_FORMAT_UYVY, DRM_FORMAT_YUYV, diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index 47f43663adcb..018fea74fb50 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -598,6 +598,8 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define WIN_COLOR_DEPTH_YUV422R 23 #define WIN_COLOR_DEPTH_YCbCr422RA 24 #define WIN_COLOR_DEPTH_YUV422RA 25 +#define WIN_COLOR_DEPTH_B8G8R8X8 37 +#define WIN_COLOR_DEPTH_R8G8B8X8 38 #define DC_WIN_POSITION 0x704 #define H_POSITION(x) (((x) & 0x1fff) << 0) /* XXX 0x7fff on Tegra186 */ diff --git a/drivers/gpu/drm/tegra/plane.c b/drivers/gpu/drm/tegra/plane.c index 78b24aa1ac83..88b5aea4a48e 100644 --- a/drivers/gpu/drm/tegra/plane.c +++ b/drivers/gpu/drm/tegra/plane.c @@ -111,10 +111,18 @@ int tegra_plane_format(u32 fourcc, u32 *format, u32 *swap) switch (fourcc) { case DRM_FORMAT_XBGR8888: + *format = WIN_COLOR_DEPTH_R8G8B8X8; + break; + + case DRM_FORMAT_ABGR8888: *format = WIN_COLOR_DEPTH_R8G8B8A8; break; case DRM_FORMAT_XRGB8888: + *format = WIN_COLOR_DEPTH_B8G8R8X8; + break; + + case DRM_FORMAT_ARGB8888: *format = WIN_COLOR_DEPTH_B8G8R8A8; break; -- cgit v1.2.3-59-g8ed1b From c57997bce423fb71334a1fefa524569e48a1718f Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 12 Oct 2017 19:12:57 +0200 Subject: drm/tegra: sor: Add Tegra186 support The SOR found on Tegra186 is very similar to the one found on Tegra210 and earlier. However, due to some changes in the display architecture, some programming sequences have changed and some register have moved around. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 13 ++ drivers/gpu/drm/tegra/dc.h | 5 +- drivers/gpu/drm/tegra/drm.c | 2 + drivers/gpu/drm/tegra/drm.h | 2 + drivers/gpu/drm/tegra/output.c | 24 ++ drivers/gpu/drm/tegra/sor.c | 487 +++++++++++++++++++++++++++++++---------- drivers/gpu/drm/tegra/sor.h | 12 + 7 files changed, 422 insertions(+), 123 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 6790e2d869c4..906752d86622 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -54,6 +54,19 @@ static u32 tegra_dc_readl_active(struct tegra_dc *dc, unsigned long offset) return value; } +bool tegra_dc_has_output(struct tegra_dc *dc, struct device *dev) +{ + struct device_node *np = dc->dev->of_node; + struct of_phandle_iterator it; + int err; + + of_for_each_phandle(&it, err, np, "nvidia,outputs", NULL, 0) + if (it.node == dev->of_node) + return true; + + return false; +} + /* * Double-buffered registers have two copies: ASSEMBLY and ACTIVE. When the * *_ACT_REQ bits are set the ASSEMBLY copy is latched into the ACTIVE copy. diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index 018fea74fb50..336d2c22f521 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -141,6 +141,7 @@ struct tegra_dc_window { }; /* from dc.c */ +bool tegra_dc_has_output(struct tegra_dc *dc, struct device *dev); void tegra_dc_commit(struct tegra_dc *dc); int tegra_dc_state_setup_clock(struct tegra_dc *dc, struct drm_crtc_state *crtc_state, @@ -289,10 +290,10 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define HDMI_ENABLE (1 << 30) #define DSI_ENABLE (1 << 29) #define SOR1_TIMING_CYA (1 << 27) -#define SOR1_ENABLE (1 << 26) -#define SOR_ENABLE (1 << 25) #define CURSOR_ENABLE (1 << 16) +#define SOR_ENABLE(x) (1 << (25 + (x))) + #define DC_DISP_DISP_MEM_HIGH_PRIORITY 0x403 #define CURSOR_THRESHOLD(x) (((x) & 0x03) << 24) #define WINDOW_A_THRESHOLD(x) (((x) & 0x7f) << 16) diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 8ade997c2b6c..2fa1b48e14d2 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -1296,6 +1296,8 @@ static const struct of_device_id host1x_drm_subdevs[] = { { .compatible = "nvidia,tegra210-vic", }, { .compatible = "nvidia,tegra186-display", }, { .compatible = "nvidia,tegra186-dc", }, + { .compatible = "nvidia,tegra186-sor", }, + { .compatible = "nvidia,tegra186-sor1", }, { .compatible = "nvidia,tegra186-vic", }, { /* sentinel */ } }; diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index d4dfd239e2a5..c52bc5978d1c 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -164,6 +164,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 diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index 24f8a3b712b4..ffe34bd0bb9d 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c @@ -9,7 +9,9 @@ #include #include + #include "drm.h" +#include "dc.h" #include @@ -218,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/sor.c b/drivers/gpu/drm/tegra/sor.c index d51399587aca..1d7f24df0b10 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -28,17 +28,30 @@ #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 +62,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,47 +141,146 @@ 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; @@ -166,6 +303,7 @@ struct tegra_sor_soc { bool supports_dp; const struct tegra_sor_regs *regs; + bool has_nvdisplay; const struct tegra_sor_hdmi_settings *settings; unsigned int num_settings; @@ -188,6 +326,7 @@ struct tegra_sor { const struct tegra_sor_soc *soc; void __iomem *regs; + unsigned int index; struct reset_control *rst; struct clk *clk_parent; @@ -202,6 +341,7 @@ struct tegra_sor { struct drm_info_list *debugfs_files; const struct tegra_sor_ops *ops; + enum tegra_io_pad pad; /* for HDMI 2.0 */ struct tegra_sor_hdmi_settings *settings; @@ -480,47 +620,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); @@ -1217,6 +1316,7 @@ static const struct debugfs_reg32 tegra_sor_regs[] = { 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), @@ -1429,7 +1529,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); @@ -1445,9 +1545,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); @@ -1605,9 +1705,9 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) 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); @@ -1785,7 +1885,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); @@ -1984,8 +2084,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); @@ -1994,9 +2098,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); } @@ -2028,9 +2132,9 @@ 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); @@ -2099,10 +2203,19 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) 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) | @@ -2114,9 +2227,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++) @@ -2139,13 +2254,16 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) return; } - 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; + 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); + tegra_sor_writel(sor, value, SOR_INPUT_CONTROL); + } max_ac = ((mode->htotal - mode->hdisplay) - SOR_REKEY - 18) / 32; @@ -2153,20 +2271,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); @@ -2197,49 +2318,66 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) 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->soc->regs->pll0); - tegra_sor_dp_term_calibrate(sor); - + /* 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); + 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->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); + 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[0] << 24 | - settings->drive_current[1] << 16 | - settings->drive_current[2] << 8 | - settings->drive_current[3] << 0; + 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->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); + 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->soc->regs->dp_padctl0); value |= SOR_DP_PADCTL_PAD_CAL_PD; 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; @@ -2254,6 +2392,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; @@ -2262,6 +2408,12 @@ 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); @@ -2282,15 +2434,32 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) 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); @@ -2356,7 +2525,7 @@ static int tegra_sor_init(struct host1x_client *client) return err; } - sor->output.encoder.possible_crtcs = 0x3; + tegra_output_find_possible_crtcs(&sor->output, drm); if (sor->aux) { err = drm_dp_aux_attach(sor->aux, &sor->output); @@ -2526,6 +2695,7 @@ static const struct tegra_sor_soc tegra124_sor = { .supports_hdmi = false, .supports_dp = false, .regs = &tegra124_sor_regs, + .has_nvdisplay = false, .xbar_cfg = tegra124_sor_xbar_cfg, }; @@ -2550,6 +2720,7 @@ static const struct tegra_sor_soc tegra210_sor = { .supports_hdmi = false, .supports_dp = false, .regs = &tegra210_sor_regs, + .has_nvdisplay = false, .xbar_cfg = tegra124_sor_xbar_cfg, }; @@ -2564,6 +2735,7 @@ static const struct tegra_sor_soc tegra210_sor1 = { .supports_dp = true, .regs = &tegra210_sor_regs, + .has_nvdisplay = false, .num_settings = ARRAY_SIZE(tegra210_sor_hdmi_defaults), .settings = tegra210_sor_hdmi_defaults, @@ -2571,7 +2743,51 @@ static const struct tegra_sor_soc tegra210_sor1 = { .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 }, @@ -2579,6 +2795,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; @@ -2614,6 +2853,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; @@ -2624,6 +2864,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; @@ -2633,6 +2874,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..e85ffc8d98e4 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 -- cgit v1.2.3-59-g8ed1b From 511c7023cf23421d1d0455b22c139fe1be1b9e87 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 14 Nov 2017 16:07:40 +0100 Subject: drm/tegra: dc: Support more formats Also, split up formats into per-SoC lists because not all generations support all of them. Note that the list is now exhaustive for all RGB formats, but not for YUV and indexed formats. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 147 +++++++++++++++++++++++++++++++++++++++--- drivers/gpu/drm/tegra/dc.h | 20 +++++- drivers/gpu/drm/tegra/hub.c | 22 ++++++- drivers/gpu/drm/tegra/plane.c | 68 ++++++++++++++++--- 4 files changed, 235 insertions(+), 22 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 906752d86622..eb005dce392a 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -298,12 +298,60 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, spin_unlock_irqrestore(&dc->lock, flags); } -static const u32 tegra_primary_plane_formats[] = { - DRM_FORMAT_XBGR8888, +static const u32 tegra20_primary_formats[] = { + DRM_FORMAT_ARGB4444, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGB565, + DRM_FORMAT_RGBA5551, DRM_FORMAT_ABGR8888, - DRM_FORMAT_XRGB8888, DRM_FORMAT_ARGB8888, +}; + +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 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, @@ -461,8 +509,8 @@ static struct drm_plane *tegra_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; + num_formats = dc->soc->num_primary_formats; + formats = dc->soc->primary_formats; /* * XXX compute offset so that we can directly access windows. @@ -643,12 +691,71 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, return &plane->base; } -static const uint32_t tegra_overlay_plane_formats[] = { - DRM_FORMAT_XBGR8888, +static const u32 tegra20_overlay_formats[] = { + DRM_FORMAT_ARGB4444, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGB565, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_ARGB8888, + /* planar formats */ + DRM_FORMAT_UYVY, + DRM_FORMAT_YUYV, + DRM_FORMAT_YUV420, + DRM_FORMAT_YUV422, +}; + +static const u32 tegra114_overlay_formats[] = { + DRM_FORMAT_ARGB4444, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGB565, + DRM_FORMAT_RGBA5551, DRM_FORMAT_ABGR8888, - DRM_FORMAT_XRGB8888, 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, @@ -673,8 +780,8 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, plane->index = index; plane->depth = 0; - 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_plane_funcs, formats, @@ -1730,6 +1837,10 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = { .has_powergate = false, .broken_reset = 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 = { @@ -1741,6 +1852,10 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = { .has_powergate = false, .broken_reset = 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 = { @@ -1752,6 +1867,10 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = { .has_powergate = true, .broken_reset = 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 = { @@ -1763,6 +1882,10 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = { .has_powergate = true, .broken_reset = 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 = { @@ -1774,6 +1897,10 @@ static const struct tegra_dc_soc_info tegra210_dc_soc_info = { .has_powergate = true, .broken_reset = 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[] = { diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index 336d2c22f521..d680027fa272 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -61,6 +61,10 @@ struct tegra_dc_soc_info { 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 { @@ -582,9 +586,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 @@ -599,8 +603,20 @@ 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) /* XXX 0x7fff on Tegra186 */ diff --git a/drivers/gpu/drm/tegra/hub.c b/drivers/gpu/drm/tegra/hub.c index 33d008fb8745..cccd44711d68 100644 --- a/drivers/gpu/drm/tegra/hub.c +++ b/drivers/gpu/drm/tegra/hub.c @@ -26,9 +26,27 @@ #include "plane.h" static const u32 tegra_shared_plane_formats[] = { - DRM_FORMAT_XBGR8888, - DRM_FORMAT_XRGB8888, + 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_shared_plane *plane, diff --git a/drivers/gpu/drm/tegra/plane.c b/drivers/gpu/drm/tegra/plane.c index 88b5aea4a48e..9146aead973b 100644 --- a/drivers/gpu/drm/tegra/plane.c +++ b/drivers/gpu/drm/tegra/plane.c @@ -110,24 +110,76 @@ int tegra_plane_format(u32 fourcc, u32 *format, u32 *swap) *swap = BYTE_SWAP_NOSWAP; switch (fourcc) { - case DRM_FORMAT_XBGR8888: - *format = WIN_COLOR_DEPTH_R8G8B8X8; + case DRM_FORMAT_ARGB4444: + *format = WIN_COLOR_DEPTH_B4G4R4A4; break; - case DRM_FORMAT_ABGR8888: - *format = WIN_COLOR_DEPTH_R8G8B8A8; + case DRM_FORMAT_ARGB1555: + *format = WIN_COLOR_DEPTH_B5G5R5A1; break; - case DRM_FORMAT_XRGB8888: - *format = WIN_COLOR_DEPTH_B8G8R8X8; + 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_RGB565: - *format = WIN_COLOR_DEPTH_B5G6R5; + 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: -- cgit v1.2.3-59-g8ed1b From 1087fac18b8e3ec8fadf1595bbc46dce7ff08a81 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 14 Dec 2017 13:37:53 +0100 Subject: drm/tegra: dc: Use direct offset to plane registers Traditionally, windows were accessed indirectly, through a register selection window that required a global register to be programmed with the index of the window to access. Since the global register could be written from modesetting functions as well as the interrupt handler concurrently, accesses had to be serialized using a lock. Using direct accesses to the window registers the lock can be avoided. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 173 ++++++++++++++++++++++-------------------- drivers/gpu/drm/tegra/hub.c | 45 +++++------ drivers/gpu/drm/tegra/hub.h | 1 - drivers/gpu/drm/tegra/plane.h | 2 + drivers/gpu/drm/tegra/sor.c | 2 + 5 files changed, 116 insertions(+), 107 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index eb005dce392a..edae21197fc6 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -34,26 +34,53 @@ static void tegra_dc_stats_reset(struct tegra_dc_stats *stats) stats->overflow = 0; } -/* - * 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. - */ +/* Reads the active copy of a register. */ static u32 tegra_dc_readl_active(struct tegra_dc *dc, unsigned long offset) { - unsigned long flags; u32 value; - spin_lock_irqsave(&dc->lock, flags); - 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); - spin_unlock_irqrestore(&dc->lock, flags); return value; } +static inline unsigned int tegra_plane_offset(struct tegra_plane *plane, + unsigned int offset) +{ + if (offset >= 0x500 && offset <= 0x638) { + offset = 0x000 + (offset - 0x500); + return plane->offset + offset; + } + + if (offset >= 0x700 && offset <= 0x719) { + offset = 0x180 + (offset - 0x700); + return plane->offset + offset; + } + + if (offset >= 0x800 && offset <= 0x839) { + 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)); +} + bool tegra_dc_has_output(struct tegra_dc *dc, struct device *dev) { struct device_node *np = dc->dev->of_node; @@ -125,12 +152,13 @@ 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_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 @@ -142,19 +170,14 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, 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; @@ -162,7 +185,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 @@ -175,33 +198,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; @@ -221,7 +244,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: @@ -242,21 +265,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) { @@ -266,36 +289,34 @@ 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); + tegra_plane_writel(plane, 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); + tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_NOKEY); + tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_1WIN); - switch (index) { + switch (plane->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); + tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_X); + tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y); + tegra_plane_writel(plane, 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); + tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X); + tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y); + tegra_plane_writel(plane, 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); + tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X); + tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_Y); + tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_3WIN_XY); break; } - - spin_unlock_irqrestore(&dc->lock, flags); } static const u32 tegra20_primary_formats[] = { @@ -405,32 +426,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; @@ -474,7 +485,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 = { @@ -509,17 +520,14 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm, if (!plane) return ERR_PTR(-ENOMEM); - num_formats = dc->soc->num_primary_formats; - formats = dc->soc->primary_formats; - - /* - * XXX compute offset so that we can directly access windows. - * - * Always use window A as primary window. - */ - plane->offset = 0; + /* Always use window A as primary window */ + plane->offset = 0xa00; plane->index = 0; plane->depth = 255; + 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_plane_funcs, formats, @@ -673,6 +681,7 @@ 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; @@ -775,10 +784,10 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, if (!plane) return ERR_PTR(-ENOMEM); - /* XXX compute offset so that we can directly access windows */ - plane->offset = 0; + plane->offset = 0xa00 + 0x200 * index; plane->index = index; plane->depth = 0; + plane->dc = dc; num_formats = dc->soc->num_overlay_formats; formats = dc->soc->overlay_formats; diff --git a/drivers/gpu/drm/tegra/hub.c b/drivers/gpu/drm/tegra/hub.c index cccd44711d68..63541dbd864d 100644 --- a/drivers/gpu/drm/tegra/hub.c +++ b/drivers/gpu/drm/tegra/hub.c @@ -49,39 +49,37 @@ static const u32 tegra_shared_plane_formats[] = { DRM_FORMAT_YUV422, }; -static inline unsigned int tegra_plane_offset(struct tegra_shared_plane *plane, +static inline unsigned int tegra_plane_offset(struct tegra_plane *plane, unsigned int offset) { - struct tegra_plane *p = &plane->base; - if (offset >= 0x500 && offset <= 0x581) { offset = 0x000 + (offset - 0x500); - return p->offset + offset; + return plane->offset + offset; } if (offset >= 0x700 && offset <= 0x73c) { offset = 0x180 + (offset - 0x700); - return p->offset + offset; + return plane->offset + offset; } if (offset >= 0x800 && offset <= 0x83e) { offset = 0x1c0 + (offset - 0x800); - return p->offset + offset; + return plane->offset + offset; } dev_WARN(plane->dc->dev, "invalid offset: %x\n", offset); - return p->offset + offset; + return plane->offset + offset; } -static inline u32 tegra_plane_readl(struct tegra_shared_plane *plane, +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_shared_plane *plane, - u32 value, unsigned int 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)); } @@ -155,7 +153,7 @@ void tegra_display_hub_cleanup(struct tegra_display_hub *hub) } } -static void tegra_shared_plane_update(struct tegra_shared_plane *plane) +static void tegra_shared_plane_update(struct tegra_plane *plane) { struct tegra_dc *dc = plane->dc; unsigned long timeout; @@ -175,7 +173,7 @@ static void tegra_shared_plane_update(struct tegra_shared_plane *plane) } } -static void tegra_shared_plane_activate(struct tegra_shared_plane *plane) +static void tegra_shared_plane_activate(struct tegra_plane *plane) { struct tegra_dc *dc = plane->dc; unsigned long timeout; @@ -196,8 +194,7 @@ static void tegra_shared_plane_activate(struct tegra_shared_plane *plane) } static unsigned int -tegra_shared_plane_get_owner(struct tegra_shared_plane *plane, - struct tegra_dc *dc) +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); @@ -206,7 +203,7 @@ tegra_shared_plane_get_owner(struct tegra_shared_plane *plane, } static bool tegra_dc_owns_shared_plane(struct tegra_dc *dc, - struct tegra_shared_plane *plane) + struct tegra_plane *plane) { struct device *dev = dc->dev; @@ -215,20 +212,20 @@ static bool tegra_dc_owns_shared_plane(struct tegra_dc *dc, return true; dev_WARN(dev, "head %u owns window %u but is not attached\n", - dc->pipe, plane->base.index); + dc->pipe, plane->index); } return false; } -static int tegra_shared_plane_set_owner(struct tegra_shared_plane *plane, +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->base.index; + unsigned int owner, index = plane->index; u32 value; value = tegra_dc_readl(dc, offset); @@ -246,7 +243,7 @@ static int tegra_shared_plane_set_owner(struct tegra_shared_plane *plane, */ if (old && owner == OWNER_MASK) dev_dbg(dev, "window %u not owned by head %u but %u\n", index, - old->pipe, owner); + old->pipe, owner); value &= ~OWNER_MASK; @@ -263,7 +260,7 @@ static int tegra_shared_plane_set_owner(struct tegra_shared_plane *plane, } static void tegra_dc_assign_shared_plane(struct tegra_dc *dc, - struct tegra_shared_plane *plane) + struct tegra_plane *plane) { u32 value; int err; @@ -312,7 +309,7 @@ static void tegra_dc_assign_shared_plane(struct tegra_dc *dc, } static void tegra_dc_remove_shared_plane(struct tegra_dc *dc, - struct tegra_shared_plane *plane) + struct tegra_plane *plane) { tegra_shared_plane_set_owner(plane, NULL); } @@ -370,8 +367,8 @@ static int tegra_shared_plane_atomic_check(struct drm_plane *plane, static void tegra_shared_plane_atomic_disable(struct drm_plane *plane, struct drm_plane_state *old_state) { - struct tegra_shared_plane *p = to_tegra_shared_plane(plane); struct tegra_dc *dc = to_tegra_dc(old_state->crtc); + struct tegra_plane *p = to_tegra_plane(plane); u32 value; /* rien ne va plus */ @@ -401,9 +398,9 @@ 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_shared_plane *p = to_tegra_shared_plane(plane); 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_bo *bo; dma_addr_t base; u32 value; @@ -434,7 +431,7 @@ static void tegra_shared_plane_atomic_update(struct drm_plane *plane, 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(p->base.depth); + value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(p->depth); tegra_plane_writel(p, value, DC_WIN_BLEND_LAYER_CONTROL); /* bypass scaling */ diff --git a/drivers/gpu/drm/tegra/hub.h b/drivers/gpu/drm/tegra/hub.h index 965c333736b0..890a47cd05c3 100644 --- a/drivers/gpu/drm/tegra/hub.h +++ b/drivers/gpu/drm/tegra/hub.h @@ -28,7 +28,6 @@ struct tegra_windowgroup { struct tegra_shared_plane { struct tegra_plane base; struct tegra_windowgroup *wgrp; - struct tegra_dc *dc; }; static inline struct tegra_shared_plane * diff --git a/drivers/gpu/drm/tegra/plane.h b/drivers/gpu/drm/tegra/plane.h index fc7566f630fa..13b900d777e8 100644 --- a/drivers/gpu/drm/tegra/plane.h +++ b/drivers/gpu/drm/tegra/plane.h @@ -12,9 +12,11 @@ #include struct tegra_bo; +struct tegra_dc; struct tegra_plane { struct drm_plane base; + struct tegra_dc *dc; unsigned int offset; unsigned int index; unsigned int depth; diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index f6313c4d612e..4be9edf9c6fe 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -3047,6 +3047,8 @@ static int tegra_sor_probe(struct platform_device *pdev) name, err); goto remove; } + } else { + sor->clk_out = sor->clk; } sor->clk_parent = devm_clk_get(&pdev->dev, "parent"); -- cgit v1.2.3-59-g8ed1b From 363541e8eeed16fdf696252008ddee823730f82c Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 14 Dec 2017 13:50:19 +0100 Subject: drm/tegra: dc: Remove redundant spinlock The spinlock is only used to serialize accesses to the DC_CMD_INT_MASK register. However, this register is accesses either with interrupts masked (in tegra_crtc_atomic_enable()) or protected by the vbl_lock and vblank_time_lock spinlocks of the DRM device. Therefore, these accesses don't need any extra serialization and the lock can be removed. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 13 ++----------- drivers/gpu/drm/tegra/dc.h | 1 - 2 files changed, 2 insertions(+), 12 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index edae21197fc6..cd84d8514d48 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1261,31 +1261,23 @@ static u32 tegra_dc_get_vblank_counter(struct drm_crtc *crtc) static int tegra_dc_enable_vblank(struct drm_crtc *crtc) { struct tegra_dc *dc = to_tegra_dc(crtc); - unsigned long value, flags; - - spin_lock_irqsave(&dc->lock, flags); + u32 value; 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); + u32 value; 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 const struct drm_crtc_funcs tegra_crtc_funcs = { @@ -2033,7 +2025,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; diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index d680027fa272..7dd02f07244f 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -71,7 +71,6 @@ struct tegra_dc { struct host1x_client client; struct host1x_syncpt *syncpt; struct device *dev; - spinlock_t lock; struct drm_crtc base; unsigned int powergate; -- cgit v1.2.3-59-g8ed1b From ab7d3f5826c55ad23101327eab435660caa83436 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 14 Dec 2017 13:46:20 +0100 Subject: drm/tegra: Implement zpos property Implement the standard zpos property for planes on Tegra124 and later. Earlier generations have a different blending unit that needs different programming. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 94 ++++++++++++++++++++++++++++++------------- drivers/gpu/drm/tegra/dc.h | 2 + drivers/gpu/drm/tegra/drm.c | 25 +++++++++++- drivers/gpu/drm/tegra/hub.c | 5 ++- drivers/gpu/drm/tegra/plane.h | 1 - 5 files changed, 95 insertions(+), 32 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index cd84d8514d48..d118094a96c1 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -152,6 +152,55 @@ static inline u32 compute_initial_dda(unsigned int in) return dfixed_frac(inf); } +static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane) +{ + /* + * 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, 0xffff00, DC_WIN_BLEND_NOKEY); + tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_1WIN); + + switch (plane->index) { + case 0: + tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_X); + tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y); + tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY); + break; + + case 1: + tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X); + tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y); + tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY); + break; + + case 2: + tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X); + tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_Y); + tegra_plane_writel(plane, 0xffff00, 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) { @@ -291,32 +340,10 @@ static void tegra_dc_setup_window(struct tegra_plane *plane, tegra_plane_writel(plane, 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_plane_writel(plane, 0xffff00, DC_WIN_BLEND_NOKEY); - tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_1WIN); - - switch (plane->index) { - case 0: - tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_X); - tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y); - tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY); - break; - - case 1: - tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X); - tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y); - tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY); - break; - - case 2: - tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X); - tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_Y); - tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_3WIN_XY); - break; - } + if (dc->soc->supports_blending) + tegra_plane_setup_blending(plane, window); + else + tegra_plane_setup_blending_legacy(plane); } static const u32 tegra20_primary_formats[] = { @@ -467,6 +494,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; @@ -523,7 +551,6 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm, /* Always use window A as primary window */ plane->offset = 0xa00; plane->index = 0; - plane->depth = 255; plane->dc = dc; num_formats = dc->soc->num_primary_formats; @@ -539,6 +566,9 @@ static struct drm_plane *tegra_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,7 +816,6 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, plane->offset = 0xa00 + 0x200 * index; plane->index = index; - plane->depth = 0; plane->dc = dc; num_formats = dc->soc->num_overlay_formats; @@ -803,6 +832,9 @@ 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; } @@ -1834,6 +1866,7 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = { .supports_interlacing = false, .supports_cursor = false, .supports_block_linear = false, + .supports_blending = false, .pitch_align = 8, .has_powergate = false, .broken_reset = true, @@ -1849,6 +1882,7 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = { .supports_interlacing = false, .supports_cursor = false, .supports_block_linear = false, + .supports_blending = false, .pitch_align = 8, .has_powergate = false, .broken_reset = false, @@ -1864,6 +1898,7 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = { .supports_interlacing = false, .supports_cursor = false, .supports_block_linear = false, + .supports_blending = false, .pitch_align = 64, .has_powergate = true, .broken_reset = false, @@ -1879,6 +1914,7 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = { .supports_interlacing = true, .supports_cursor = true, .supports_block_linear = true, + .supports_blending = true, .pitch_align = 64, .has_powergate = true, .broken_reset = false, @@ -1894,6 +1930,7 @@ static const struct tegra_dc_soc_info tegra210_dc_soc_info = { .supports_interlacing = true, .supports_cursor = true, .supports_block_linear = true, + .supports_blending = true, .pitch_align = 64, .has_powergate = true, .broken_reset = false, @@ -1943,6 +1980,7 @@ static const struct tegra_dc_soc_info tegra186_dc_soc_info = { .supports_interlacing = true, .supports_cursor = true, .supports_block_linear = true, + .supports_blending = true, .pitch_align = 64, .has_powergate = false, .broken_reset = false, diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index 7dd02f07244f..f4568e054a63 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -55,6 +55,7 @@ struct tegra_dc_soc_info { bool supports_interlacing; bool supports_cursor; bool supports_block_linear; + bool supports_blending; unsigned int pitch_align; bool has_powergate; bool broken_reset; @@ -136,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; diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 2fa1b48e14d2..52468f669277 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -33,6 +33,29 @@ struct tegra_drm_file { struct mutex lock; }; +static int tegra_atomic_check(struct drm_device *drm, + struct drm_atomic_state *state) +{ + int err; + + err = drm_atomic_helper_check_modeset(drm, state); + if (err < 0) + return err; + + err = drm_atomic_normalize_zpos(drm, state); + if (err < 0) + return err; + + err = drm_atomic_helper_check_planes(drm, state); + if (err < 0) + return err; + + if (state->legacy_cursor_update) + state->async_update = !drm_atomic_helper_async_check(drm, state); + + return 0; +} + static struct drm_atomic_state * tegra_atomic_state_alloc(struct drm_device *drm) { @@ -67,7 +90,7 @@ static const struct drm_mode_config_funcs tegra_drm_mode_config_funcs = { #ifdef CONFIG_DRM_FBDEV_EMULATION .output_poll_changed = tegra_fb_output_poll_changed, #endif - .atomic_check = drm_atomic_helper_check, + .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, diff --git a/drivers/gpu/drm/tegra/hub.c b/drivers/gpu/drm/tegra/hub.c index 63541dbd864d..90bcf24b1523 100644 --- a/drivers/gpu/drm/tegra/hub.c +++ b/drivers/gpu/drm/tegra/hub.c @@ -399,6 +399,7 @@ static void tegra_shared_plane_atomic_update(struct drm_plane *plane, { 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; @@ -431,7 +432,7 @@ static void tegra_shared_plane_atomic_update(struct drm_plane *plane, 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(p->depth); + value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(255 - zpos); tegra_plane_writel(p, value, DC_WIN_BLEND_LAYER_CONTROL); /* bypass scaling */ @@ -536,7 +537,6 @@ struct drm_plane *tegra_shared_plane_create(struct drm_device *drm, plane->base.offset = 0x0a00 + 0x0300 * index; plane->base.index = index; - plane->base.depth = 0; plane->wgrp = &hub->wgrps[wgrp]; plane->wgrp->parent = dc->dev; @@ -555,6 +555,7 @@ struct drm_plane *tegra_shared_plane_create(struct drm_device *drm, } drm_plane_helper_add(p, &tegra_shared_plane_helper_funcs); + drm_plane_create_zpos_property(p, 0, 0, 255); return p; } diff --git a/drivers/gpu/drm/tegra/plane.h b/drivers/gpu/drm/tegra/plane.h index 13b900d777e8..dca66cb95d25 100644 --- a/drivers/gpu/drm/tegra/plane.h +++ b/drivers/gpu/drm/tegra/plane.h @@ -19,7 +19,6 @@ struct tegra_plane { struct tegra_dc *dc; unsigned int offset; unsigned int index; - unsigned int depth; }; struct tegra_cursor { -- cgit v1.2.3-59-g8ed1b From bc8828bd08bd2a645caeb64d299d67faca7a3b4f Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 12 Oct 2017 17:43:33 +0200 Subject: drm/tegra: Use IOMMU groups In order to support IOMMUs more generically and transparently handle the ARM SMMU on Tegra186, move to using groups instead of devices for domain attachment. An IOMMU group is a set of devices that share the same IOMMU domain and is therefore a good match to represent what Tegra DRM needs. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 27 +++++++++++++++++---------- drivers/gpu/drm/tegra/drm.h | 1 + drivers/gpu/drm/tegra/vic.c | 18 ++++++++++-------- 3 files changed, 28 insertions(+), 18 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index d118094a96c1..dc91911094fc 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1748,6 +1748,7 @@ static irqreturn_t tegra_dc_irq(int irq, void *data) 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; @@ -1759,12 +1760,17 @@ 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; @@ -1825,8 +1831,8 @@ cleanup: 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; } @@ -1835,6 +1841,7 @@ 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; @@ -1846,8 +1853,8 @@ static int tegra_dc_exit(struct host1x_client *client) 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; } diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index c52bc5978d1c..da3d8c141aee 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -60,6 +60,7 @@ struct tegra_drm { struct drm_device *drm; struct iommu_domain *domain; + struct iommu_group *group; struct mutex mm_lock; struct drm_mm mm; diff --git a/drivers/gpu/drm/tegra/vic.c b/drivers/gpu/drm/tegra/vic.c index d9664a34fb43..f5794dd49f3b 100644 --- a/drivers/gpu/drm/tegra/vic.c +++ b/drivers/gpu/drm/tegra/vic.c @@ -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; } -- cgit v1.2.3-59-g8ed1b From 39f55c61da49ba41bba4185043cba6f39229467f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 18 Dec 2017 14:44:46 +0100 Subject: drm/tegra: Fix non-debugfs builds The new debugfs registration fails to build when CONFIG_DEBUGFS is disabled, because the drm_crtc structure is lacking a member in that configuration: drivers/gpu/drm/tegra/dc.c: In function 'tegra_dc_late_register': drivers/gpu/drm/tegra/dc.c:1204:28: error: 'struct drm_crtc' has no member named 'debugfs_entry' Without CONFIG_DEBUGFS, the rest of the function already degrades into nothing, so we just avoid the one assignment. Fixes: b95800eeef09 ("drm/tegra: dc: Register debugfs in ->late_register()") Signed-off-by: Arnd Bergmann Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index dc91911094fc..a5fd18c45800 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1242,10 +1242,16 @@ 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 = crtc->debugfs_entry; + 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) -- cgit v1.2.3-59-g8ed1b From f68ba6912bd24088f067da2470880892abc2ac58 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 20 Dec 2017 18:46:10 +0300 Subject: drm/tegra: dc: Link DC1 to DC0 on Tegra20 Hardware reset isn't actually broken on Tegra20, but there is a dependency on the first display controller to be taken out of reset for the second to be enabled successfully. Model this dependency using a PM device link. Signed-off-by: Dmitry Osipenko [treding@nvidia.com: minor cleanups, extend commit message] Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 94 +++++++++++++++++++++++++++++++--------------- drivers/gpu/drm/tegra/dc.h | 2 +- 2 files changed, 65 insertions(+), 31 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index a5fd18c45800..2a0c1e93f82e 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1882,7 +1882,7 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = { .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, @@ -1898,7 +1898,7 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = { .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, @@ -1914,7 +1914,7 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = { .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, @@ -1930,7 +1930,7 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = { .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, @@ -1946,7 +1946,7 @@ static const struct tegra_dc_soc_info tegra210_dc_soc_info = { .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, @@ -1996,7 +1996,7 @@ static const struct tegra_dc_soc_info tegra186_dc_soc_info = { .supports_blending = true, .pitch_align = 64, .has_powergate = false, - .broken_reset = false, + .coupled_pm = false, .has_nvdisplay = true, .wgrps = tegra186_dc_wgrps, .num_wgrps = ARRAY_SIZE(tegra186_dc_wgrps), @@ -2064,6 +2064,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; @@ -2083,6 +2120,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"); @@ -2096,21 +2137,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) @@ -2184,12 +2223,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) @@ -2219,13 +2256,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 f4568e054a63..e2831e96ea96 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -58,7 +58,7 @@ struct tegra_dc_soc_info { 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; -- cgit v1.2.3-59-g8ed1b From ebae8d07435ae91314f4a28d69b530d09c625815 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 20 Dec 2017 09:39:14 +0100 Subject: drm/tegra: dc: Implement legacy blending This implements alpha blending on legacy display controllers (Tegra20, Tegra30 and Tegra114). While it's theoretically possible to support the zpos property to enable userspace to specify the Z-order of each plane individually, this is not currently supported and the same fixed Z- order as previously defined is used. Reverts commit 71835caa00e8 ("drm/tegra: fb: Force alpha formats") since the opaque formats are now supported. Reported-by: Dmitry Osipenko Fixes: 7772fdaef939 ("drm/tegra: Support ARGB and ABGR formats") Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 81 +++++++++++++++++++++---- drivers/gpu/drm/tegra/dc.h | 12 ++++ drivers/gpu/drm/tegra/fb.c | 12 ---- drivers/gpu/drm/tegra/plane.c | 138 ++++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/tegra/plane.h | 8 +++ 5 files changed, 226 insertions(+), 25 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 2a0c1e93f82e..4507063029e0 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -154,30 +154,53 @@ static inline u32 compute_initial_dda(unsigned int in) 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, 0xffff00, DC_WIN_BLEND_NOKEY); - tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_1WIN); + 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, 0x000000, DC_WIN_BLEND_2WIN_X); - tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y); - tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY); + 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, 0xffff00, DC_WIN_BLEND_2WIN_X); - tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y); - tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY); + 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, 0xffff00, DC_WIN_BLEND_2WIN_X); - tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_Y); - tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_3WIN_XY); + 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; } } @@ -353,6 +376,11 @@ static const u32 tegra20_primary_formats[] = { DRM_FORMAT_RGBA5551, DRM_FORMAT_ABGR8888, DRM_FORMAT_ARGB8888, + /* non-native formats */ + DRM_FORMAT_XRGB1555, + DRM_FORMAT_RGBX5551, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_XRGB8888, }; static const u32 tegra114_primary_formats[] = { @@ -409,18 +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_plane_format(state->fb->format->format, - &plane_state->format, + 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; @@ -737,6 +787,11 @@ static const u32 tegra20_overlay_formats[] = { 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, diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index e2831e96ea96..096a81ad6d8d 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -649,8 +649,20 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #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 diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index 1af4ef9241f1..e05fde7172f8 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -254,18 +254,6 @@ static int tegra_fbdev_probe(struct drm_fb_helper *helper, cmd.pitches[0] = round_up(sizes->surface_width * bytes_per_pixel, tegra->pitch_align); - /* - * Early generations of Tegra (Tegra20 and Tegra30) do not support any - * of the X* or *X formats, only their A* or *A equivalents. Force the - * legacy framebuffer format to include an alpha component so that the - * framebuffer emulation can be supported on all generations. - */ - if (sizes->surface_bpp == 32 && sizes->surface_depth == 24) - sizes->surface_depth = 32; - - if (sizes->surface_bpp == 16 && sizes->surface_depth == 15) - sizes->surface_depth = 16; - cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth); diff --git a/drivers/gpu/drm/tegra/plane.c b/drivers/gpu/drm/tegra/plane.c index 9146aead973b..154b4d337d0a 100644 --- a/drivers/gpu/drm/tegra/plane.c +++ b/drivers/gpu/drm/tegra/plane.c @@ -43,6 +43,7 @@ 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) @@ -52,6 +53,10 @@ tegra_plane_atomic_duplicate_state(struct drm_plane *plane) 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; } @@ -238,3 +243,136 @@ bool tegra_plane_format_is_yuv(unsigned int format, bool *planar) 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) +{ + 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 index dca66cb95d25..6938719e7e5d 100644 --- a/drivers/gpu/drm/tegra/plane.h +++ b/drivers/gpu/drm/tegra/plane.h @@ -40,6 +40,10 @@ struct tegra_plane_state { 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 * @@ -58,5 +62,9 @@ int tegra_plane_state_add(struct tegra_plane *plane, 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 */ -- cgit v1.2.3-59-g8ed1b From 89f6501825b5bae3d4aaa2447636f9d3a4287a75 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 8 Jan 2018 16:11:00 +0100 Subject: drm/tegra: dc: Fix possible_crtcs mask for planes Cursor and overlay planes use a possible_crtcs mask based on the DC pipe number. However, DRM requires each bit in the mask to correspond to the index of the CRTC, which will be different from the DC pipe number for a configuration where the first display controller is disabled, or where a deferred probe leads to the first display controller being probed after the first. Suggested-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 4507063029e0..7a9c9ff8b4d7 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -572,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_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 @@ -587,7 +586,13 @@ static struct drm_plane *tegra_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; @@ -744,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; @@ -766,7 +772,7 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, num_formats = ARRAY_SIZE(tegra_cursor_plane_formats); formats = tegra_cursor_plane_formats; - err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe, + err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, &tegra_plane_funcs, formats, num_formats, NULL, DRM_PLANE_TYPE_CURSOR, NULL); @@ -860,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,7 +883,7 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, num_formats = dc->soc->num_overlay_formats; formats = dc->soc->overlay_formats; - err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe, + err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, &tegra_plane_funcs, formats, num_formats, NULL, DRM_PLANE_TYPE_OVERLAY, NULL); -- cgit v1.2.3-59-g8ed1b From 8f62142e490d761ceb92b55a7c05bb79294d6c6c Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 8 Jan 2018 16:16:06 +0100 Subject: drm/tegra: dc: Properly cleanup overlay planes The first overlay plane can leak if initialization of the second overlay plane fails. Fix this by properly destroying the first overlay plane on error. Suggested-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'drivers/gpu/drm/tegra/dc.c') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 7a9c9ff8b4d7..b8403ed48285 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -937,20 +937,24 @@ static struct drm_plane *tegra_dc_add_shared_planes(struct drm_device *drm, static struct drm_plane *tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc) { - struct drm_plane *plane, *primary; + struct drm_plane *planes[2], *primary; unsigned int i; + int err; primary = tegra_primary_plane_create(drm, dc); if (IS_ERR(primary)) return primary; for (i = 0; i < 2; i++) { - plane = tegra_dc_overlay_plane_create(drm, dc, 1 + i); - if (IS_ERR(plane)) { - /* XXX tegra_plane_destroy() */ - drm_plane_cleanup(primary); - kfree(primary); - return plane; + planes[i] = tegra_dc_overlay_plane_create(drm, dc, 1 + i); + if (IS_ERR(planes[i])) { + err = PTR_ERR(planes[i]); + + while (i--) + tegra_plane_funcs.destroy(planes[i]); + + tegra_plane_funcs.destroy(primary); + return ERR_PTR(err); } } -- cgit v1.2.3-59-g8ed1b