From b6d7974d10b30bf3baed7e50d8e574f5184cfdd1 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 7 Feb 2018 18:45:54 +0100 Subject: drm/tegra: gem: Reshuffle declarations Move declarations in the gem.h header file into the same order as the corresponding definitions in gem.c. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/gem.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/gem.h b/drivers/gpu/drm/tegra/gem.h index 8eb9fd24ef0e..1fcf94dce45c 100644 --- a/drivers/gpu/drm/tegra/gem.h +++ b/drivers/gpu/drm/tegra/gem.h @@ -68,10 +68,10 @@ void tegra_bo_free_object(struct drm_gem_object *gem); int tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm, struct drm_mode_create_dumb *args); -int tegra_drm_mmap(struct file *file, struct vm_area_struct *vma); - extern const struct vm_operations_struct tegra_bo_vm_ops; +int tegra_drm_mmap(struct file *file, struct vm_area_struct *vma); + struct dma_buf *tegra_gem_prime_export(struct drm_device *drm, struct drm_gem_object *gem, int flags); -- cgit v1.2.3 From 04c0746663bd3ae3cce5e02d5b32c8ade2a833b8 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 7 Feb 2018 18:45:55 +0100 Subject: drm/tegra: gem: Make __tegra_gem_mmap() available more widely This function allows mapping a GEM object into a virtual memory address space, which makes it useful outside of the GEM code. While at it, rename the function so it doesn't clash with the function that implements the DRM_TEGRA_GEM_MMAP IOCTL. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/gem.c | 7 +++---- drivers/gpu/drm/tegra/gem.h | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index 49b9bf28f872..d3b3aa148225 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -459,8 +459,7 @@ const struct vm_operations_struct tegra_bo_vm_ops = { .close = drm_gem_vm_close, }; -static int tegra_gem_mmap(struct drm_gem_object *gem, - struct vm_area_struct *vma) +int __tegra_gem_mmap(struct drm_gem_object *gem, struct vm_area_struct *vma) { struct tegra_bo *bo = to_tegra_bo(gem); @@ -507,7 +506,7 @@ int tegra_drm_mmap(struct file *file, struct vm_area_struct *vma) gem = vma->vm_private_data; - return tegra_gem_mmap(gem, vma); + return __tegra_gem_mmap(gem, vma); } static struct sg_table * @@ -600,7 +599,7 @@ static int tegra_gem_prime_mmap(struct dma_buf *buf, struct vm_area_struct *vma) if (err < 0) return err; - return tegra_gem_mmap(gem, vma); + return __tegra_gem_mmap(gem, vma); } static void *tegra_gem_prime_vmap(struct dma_buf *buf) diff --git a/drivers/gpu/drm/tegra/gem.h b/drivers/gpu/drm/tegra/gem.h index 1fcf94dce45c..6bd7dd7e55b4 100644 --- a/drivers/gpu/drm/tegra/gem.h +++ b/drivers/gpu/drm/tegra/gem.h @@ -70,6 +70,7 @@ int tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm, extern const struct vm_operations_struct tegra_bo_vm_ops; +int __tegra_gem_mmap(struct drm_gem_object *gem, struct vm_area_struct *vma); int tegra_drm_mmap(struct file *file, struct vm_area_struct *vma); struct dma_buf *tegra_gem_prime_export(struct drm_device *drm, -- cgit v1.2.3 From b8f3f500e09c2c457efc2fcbfe8b7f815f2e6a0e Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 7 Feb 2018 18:45:56 +0100 Subject: drm/tegra: fb: Implement ->fb_mmap() callback This fixes hangs with legacy applications that use the mmap() syscall on the fbdev device to map framebuffer memory. The fbdev implementation for mmap() creates a mapping that conflicts with DRM usage and causes a hang when the memory is accessed through the mapping. Reported-by: Marcel Ziswiler Signed-off-by: Thierry Reding Tested-by: Stefan Agner Tested-by: Marcel Ziswiler Reported-by: Marcel Ziswiler Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/fb.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index 001cb77e2f59..0786159edef3 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -224,12 +224,28 @@ unreference: } #ifdef CONFIG_DRM_FBDEV_EMULATION +static int tegra_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + struct drm_fb_helper *helper = info->par; + struct tegra_bo *bo; + int err; + + bo = tegra_fb_get_plane(helper->fb, 0); + + err = drm_gem_mmap_obj(&bo->gem, bo->gem.size, vma); + if (err < 0) + return err; + + return __tegra_gem_mmap(&bo->gem, vma); +} + static struct fb_ops tegra_fb_ops = { .owner = THIS_MODULE, DRM_FB_HELPER_DEFAULT_OPS, .fb_fillrect = drm_fb_helper_sys_fillrect, .fb_copyarea = drm_fb_helper_sys_copyarea, .fb_imageblit = drm_fb_helper_sys_imageblit, + .fb_mmap = tegra_fb_mmap, }; static int tegra_fbdev_probe(struct drm_fb_helper *helper, -- cgit v1.2.3 From 5e2e86f12cba9591b4ad3c1e7f76e149319af9e7 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Thu, 15 Mar 2018 11:37:05 +0100 Subject: drm/tegra: plane: Make tegra_plane_get_overlap_index() static This function is not used outside of the file and can be static. Signed-off-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/plane.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/plane.c b/drivers/gpu/drm/tegra/plane.c index 36a06a993698..95ffaae06f08 100644 --- a/drivers/gpu/drm/tegra/plane.c +++ b/drivers/gpu/drm/tegra/plane.c @@ -302,8 +302,8 @@ int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha) return -EINVAL; } -unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane, - struct tegra_plane *other) +static unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane, + struct tegra_plane *other) { unsigned int index = 0, i; -- cgit v1.2.3 From 9f446d83b233d2773fd934eed41d795484a08422 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Thu, 15 Mar 2018 04:00:25 +0300 Subject: drm/tegra: dc: Dedicate overlay plane to cursor on older Tegra's Older Tegra's do not support RGBA format for the cursor, but instead overlay plane could be used for it. Since there is no much use for the overlays on a regular desktop and HW-accelerated cursor is much better than a SW cursor, let's dedicate one overlay plane to the mouse cursor. Signed-off-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index b8403ed48285..ead13ac325b0 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -864,11 +864,13 @@ static const u32 tegra124_overlay_formats[] = { static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, struct tegra_dc *dc, - unsigned int index) + unsigned int index, + bool cursor) { unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm); struct tegra_plane *plane; unsigned int num_formats; + enum drm_plane_type type; const u32 *formats; int err; @@ -883,10 +885,14 @@ 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; + if (!cursor) + type = DRM_PLANE_TYPE_OVERLAY; + else + type = DRM_PLANE_TYPE_CURSOR; + err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, &tegra_plane_funcs, formats, - num_formats, NULL, - DRM_PLANE_TYPE_OVERLAY, NULL); + num_formats, NULL, type, NULL); if (err < 0) { kfree(plane); return ERR_PTR(err); @@ -938,6 +944,7 @@ static struct drm_plane *tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc) { struct drm_plane *planes[2], *primary; + unsigned int planes_num; unsigned int i; int err; @@ -945,8 +952,14 @@ static struct drm_plane *tegra_dc_add_planes(struct drm_device *drm, if (IS_ERR(primary)) return primary; - for (i = 0; i < 2; i++) { - planes[i] = tegra_dc_overlay_plane_create(drm, dc, 1 + i); + if (dc->soc->supports_cursor) + planes_num = 2; + else + planes_num = 1; + + for (i = 0; i < planes_num; i++) { + planes[i] = tegra_dc_overlay_plane_create(drm, dc, 1 + i, + false); if (IS_ERR(planes[i])) { err = PTR_ERR(planes[i]); @@ -1864,6 +1877,13 @@ static int tegra_dc_init(struct host1x_client *client) err = PTR_ERR(cursor); goto cleanup; } + } else { + /* dedicate one overlay to mouse cursor */ + cursor = tegra_dc_overlay_plane_create(drm, dc, 2, true); + if (IS_ERR(cursor)) { + err = PTR_ERR(cursor); + goto cleanup; + } } err = drm_crtc_init_with_planes(drm, &dc->base, primary, cursor, -- cgit v1.2.3 From e90124cb46bdb6b8dd642e0066207ace0fc3f972 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 15 Mar 2018 16:44:04 +0100 Subject: drm/tegra: plane: Support format modifiers Pass the list of valid format modifiers to planes upon initialization and implement the ->format_mod_supported() callback so that userspace can query for the valid combinations of formats and modifiers. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 26 +++++++++++++++++++++++++- drivers/gpu/drm/tegra/dc.h | 1 + drivers/gpu/drm/tegra/hub.c | 15 ++++++++++++++- drivers/gpu/drm/tegra/plane.c | 16 ++++++++++++++++ 4 files changed, 56 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index ead13ac325b0..dfa787e383cc 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -383,6 +383,12 @@ static const u32 tegra20_primary_formats[] = { DRM_FORMAT_XRGB8888, }; +static const u64 tegra20_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED, + DRM_FORMAT_MOD_INVALID +}; + static const u32 tegra114_primary_formats[] = { DRM_FORMAT_ARGB4444, DRM_FORMAT_ARGB1555, @@ -430,6 +436,17 @@ static const u32 tegra124_primary_formats[] = { DRM_FORMAT_BGRX8888, }; +static const u64 tegra124_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0), + DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1), + DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2), + DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3), + DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4), + DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5), + DRM_FORMAT_MOD_INVALID +}; + static int tegra_plane_atomic_check(struct drm_plane *plane, struct drm_plane_state *state) { @@ -596,6 +613,7 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm, enum drm_plane_type type = DRM_PLANE_TYPE_PRIMARY; struct tegra_plane *plane; unsigned int num_formats; + const u64 *modifiers; const u32 *formats; int err; @@ -610,10 +628,11 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm, num_formats = dc->soc->num_primary_formats; formats = dc->soc->primary_formats; + modifiers = dc->soc->modifiers; err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, &tegra_plane_funcs, formats, - num_formats, NULL, type, NULL); + num_formats, modifiers, type, NULL); if (err < 0) { kfree(plane); return ERR_PTR(err); @@ -1974,6 +1993,7 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = { .primary_formats = tegra20_primary_formats, .num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats), .overlay_formats = tegra20_overlay_formats, + .modifiers = tegra20_modifiers, }; static const struct tegra_dc_soc_info tegra30_dc_soc_info = { @@ -1990,6 +2010,7 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = { .primary_formats = tegra20_primary_formats, .num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats), .overlay_formats = tegra20_overlay_formats, + .modifiers = tegra20_modifiers, }; static const struct tegra_dc_soc_info tegra114_dc_soc_info = { @@ -2006,6 +2027,7 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = { .primary_formats = tegra114_primary_formats, .num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats), .overlay_formats = tegra114_overlay_formats, + .modifiers = tegra20_modifiers, }; static const struct tegra_dc_soc_info tegra124_dc_soc_info = { @@ -2022,6 +2044,7 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = { .primary_formats = tegra114_primary_formats, .num_overlay_formats = ARRAY_SIZE(tegra124_overlay_formats), .overlay_formats = tegra114_overlay_formats, + .modifiers = tegra124_modifiers, }; static const struct tegra_dc_soc_info tegra210_dc_soc_info = { @@ -2038,6 +2061,7 @@ static const struct tegra_dc_soc_info tegra210_dc_soc_info = { .primary_formats = tegra114_primary_formats, .num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats), .overlay_formats = tegra114_overlay_formats, + .modifiers = tegra124_modifiers, }; 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 096a81ad6d8d..d2b50d32de4d 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -66,6 +66,7 @@ struct tegra_dc_soc_info { unsigned int num_primary_formats; const u32 *overlay_formats; unsigned int num_overlay_formats; + const u64 *modifiers; }; struct tegra_dc { diff --git a/drivers/gpu/drm/tegra/hub.c b/drivers/gpu/drm/tegra/hub.c index e10a47d57313..094324daa917 100644 --- a/drivers/gpu/drm/tegra/hub.c +++ b/drivers/gpu/drm/tegra/hub.c @@ -49,6 +49,17 @@ static const u32 tegra_shared_plane_formats[] = { DRM_FORMAT_YUV422, }; +static const u64 tegra_shared_plane_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0), + DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1), + DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2), + DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3), + DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4), + DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5), + DRM_FORMAT_MOD_INVALID +}; + static inline unsigned int tegra_plane_offset(struct tegra_plane *plane, unsigned int offset) { @@ -527,6 +538,7 @@ struct drm_plane *tegra_shared_plane_create(struct drm_device *drm, unsigned int possible_crtcs = 0x7; struct tegra_shared_plane *plane; unsigned int num_formats; + const u64 *modifiers; struct drm_plane *p; const u32 *formats; int err; @@ -545,10 +557,11 @@ struct drm_plane *tegra_shared_plane_create(struct drm_device *drm, num_formats = ARRAY_SIZE(tegra_shared_plane_formats); formats = tegra_shared_plane_formats; + modifiers = tegra_shared_plane_modifiers; err = drm_universal_plane_init(drm, p, possible_crtcs, &tegra_plane_funcs, formats, - num_formats, NULL, type, NULL); + num_formats, modifiers, type, NULL); if (err < 0) { kfree(plane); return ERR_PTR(err); diff --git a/drivers/gpu/drm/tegra/plane.c b/drivers/gpu/drm/tegra/plane.c index 95ffaae06f08..fcf7b8e3032d 100644 --- a/drivers/gpu/drm/tegra/plane.c +++ b/drivers/gpu/drm/tegra/plane.c @@ -68,6 +68,21 @@ static void tegra_plane_atomic_destroy_state(struct drm_plane *plane, kfree(state); } +static bool tegra_plane_format_mod_supported(struct drm_plane *plane, + uint32_t format, + uint64_t modifier) +{ + const struct drm_format_info *info = drm_format_info(format); + + if (modifier == DRM_FORMAT_MOD_LINEAR) + return true; + + if (info->num_planes == 1) + return true; + + return false; +} + const struct drm_plane_funcs tegra_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, @@ -75,6 +90,7 @@ const struct drm_plane_funcs tegra_plane_funcs = { .reset = tegra_plane_reset, .atomic_duplicate_state = tegra_plane_atomic_duplicate_state, .atomic_destroy_state = tegra_plane_atomic_destroy_state, + .format_mod_supported = tegra_plane_format_mod_supported, }; int tegra_plane_state_add(struct tegra_plane *plane, -- cgit v1.2.3 From 4ae4b5c0dbaa499f2fd9215caac6e474c8dd477f Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 15 Mar 2018 16:45:45 +0100 Subject: drm/tegra: fb: Properly support linear modifier Instead of relying on the tiling attached to a buffer object, make sure to set the proper tiling for linear buffers. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/fb.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index 0786159edef3..e69434909a42 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -55,6 +55,11 @@ int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer, uint64_t modifier = fb->base.modifier; switch (modifier) { + case DRM_FORMAT_MOD_LINEAR: + tiling->mode = TEGRA_BO_TILING_MODE_PITCH; + tiling->value = 0; + break; + case DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED: tiling->mode = TEGRA_BO_TILING_MODE_TILED; tiling->value = 0; @@ -91,9 +96,7 @@ int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer, break; default: - /* TODO: handle YUV formats? */ - *tiling = fb->planes[0]->tiling; - break; + return -EINVAL; } return 0; -- cgit v1.2.3 From 0281c4149021376123b4ccdb1548692a3f6e70bd Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 28 Nov 2017 11:20:40 +0100 Subject: drm/tegra: hub: Use private object for global state Rather than subclass the global atomic state to store the hub display clock and rate, create a private object and store this data in its state. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 26 ----------- drivers/gpu/drm/tegra/drm.c | 36 ++------------- drivers/gpu/drm/tegra/drm.h | 14 ------ drivers/gpu/drm/tegra/hub.c | 110 ++++++++++++++++++++++++++++++++++++++++---- drivers/gpu/drm/tegra/hub.h | 17 +++++++ 5 files changed, 123 insertions(+), 80 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index dfa787e383cc..464495b46db2 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1736,31 +1736,6 @@ static void tegra_crtc_atomic_enable(struct drm_crtc *crtc, drm_crtc_vblank_on(crtc); } -static int tegra_crtc_atomic_check(struct drm_crtc *crtc, - struct drm_crtc_state *state) -{ - struct tegra_atomic_state *s = to_tegra_atomic_state(state->state); - struct tegra_dc_state *tegra = to_dc_state(state); - - /* - * The display hub display clock needs to be fed by the display clock - * with the highest frequency to ensure proper functioning of all the - * displays. - * - * Note that this isn't used before Tegra186, but it doesn't hurt and - * conditionalizing it would make the code less clean. - */ - if (state->active) { - if (!s->clk_disp || tegra->pclk > s->rate) { - s->dc = to_tegra_dc(crtc); - s->clk_disp = s->dc->clk; - s->rate = tegra->pclk; - } - } - - return 0; -} - static void tegra_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { @@ -1797,7 +1772,6 @@ static void tegra_crtc_atomic_flush(struct drm_crtc *crtc, } 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, diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index d50bddb2e447..e20e013151f0 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -42,6 +42,10 @@ static int tegra_atomic_check(struct drm_device *drm, if (err < 0) return err; + err = tegra_display_hub_atomic_check(drm, state); + if (err < 0) + return err; + err = drm_atomic_normalize_zpos(drm, state); if (err < 0) return err; @@ -56,35 +60,6 @@ static int tegra_atomic_check(struct drm_device *drm, return 0; } -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 @@ -92,9 +67,6 @@ static const struct drm_mode_config_funcs tegra_drm_mode_config_funcs = { #endif .atomic_check = tegra_atomic_check, .atomic_commit = drm_atomic_helper_commit, - .atomic_state_alloc = tegra_atomic_state_alloc, - .atomic_state_clear = tegra_atomic_state_clear, - .atomic_state_free = tegra_atomic_state_free, }; static void tegra_atomic_commit_tail(struct drm_atomic_state *old_state) diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index 73b661ce7086..4f41aaec8530 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -42,20 +42,6 @@ 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; diff --git a/drivers/gpu/drm/tegra/hub.c b/drivers/gpu/drm/tegra/hub.c index 094324daa917..9a3f23d4780f 100644 --- a/drivers/gpu/drm/tegra/hub.c +++ b/drivers/gpu/drm/tegra/hub.c @@ -573,6 +573,89 @@ struct drm_plane *tegra_shared_plane_create(struct drm_device *drm, return p; } +static struct drm_private_state * +tegra_display_hub_duplicate_state(struct drm_private_obj *obj) +{ + struct tegra_display_hub_state *state; + + state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL); + if (!state) + return NULL; + + __drm_atomic_helper_private_obj_duplicate_state(obj, &state->base); + + return &state->base; +} + +static void tegra_display_hub_destroy_state(struct drm_private_obj *obj, + struct drm_private_state *state) +{ + struct tegra_display_hub_state *hub_state = + to_tegra_display_hub_state(state); + + kfree(hub_state); +} + +static const struct drm_private_state_funcs tegra_display_hub_state_funcs = { + .atomic_duplicate_state = tegra_display_hub_duplicate_state, + .atomic_destroy_state = tegra_display_hub_destroy_state, +}; + +static struct tegra_display_hub_state * +tegra_display_hub_get_state(struct tegra_display_hub *hub, + struct drm_atomic_state *state) +{ + struct drm_device *drm = dev_get_drvdata(hub->client.parent); + struct drm_private_state *priv; + + WARN_ON(!drm_modeset_is_locked(&drm->mode_config.connection_mutex)); + + priv = drm_atomic_get_private_obj_state(state, &hub->base); + if (IS_ERR(priv)) + return ERR_CAST(priv); + + return to_tegra_display_hub_state(priv); +} + +int tegra_display_hub_atomic_check(struct drm_device *drm, + struct drm_atomic_state *state) +{ + struct tegra_drm *tegra = drm->dev_private; + struct tegra_display_hub_state *hub_state; + struct drm_crtc_state *old, *new; + struct drm_crtc *crtc; + unsigned int i; + + if (!tegra->hub) + return 0; + + hub_state = tegra_display_hub_get_state(tegra->hub, state); + if (IS_ERR(hub_state)) + return PTR_ERR(hub_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. + */ + for_each_oldnew_crtc_in_state(state, crtc, old, new, i) { + struct tegra_dc_state *dc = to_dc_state(new); + + if (new->active) { + if (!hub_state->clk || dc->pclk > hub_state->rate) { + hub_state->dc = to_tegra_dc(dc->base.crtc); + hub_state->clk = hub_state->dc->clk; + hub_state->rate = dc->pclk; + } + } + } + + return 0; +} + static void tegra_display_hub_update(struct tegra_dc *dc) { u32 value; @@ -598,26 +681,28 @@ static void tegra_display_hub_update(struct tegra_dc *dc) 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 tegra_display_hub_state *hub_state; struct device *dev = hub->client.dev; int err; - if (s->clk_disp) { - err = clk_set_rate(s->clk_disp, s->rate); + hub_state = tegra_display_hub_get_state(hub, state); + + if (hub_state->clk) { + err = clk_set_rate(hub_state->clk, hub_state->rate); if (err < 0) dev_err(dev, "failed to set rate of %pC to %lu Hz\n", - s->clk_disp, s->rate); + hub_state->clk, hub_state->rate); - err = clk_set_parent(hub->clk_disp, s->clk_disp); + err = clk_set_parent(hub->clk_disp, hub_state->clk); if (err < 0) dev_err(dev, "failed to set parent of %pC to %pC: %d\n", - hub->clk_disp, s->clk_disp, err); + hub->clk_disp, hub_state->clk, err); } - if (s->dc) - tegra_display_hub_update(s->dc); + if (hub_state->dc) + tegra_display_hub_update(hub_state->dc); } static int tegra_display_hub_init(struct host1x_client *client) @@ -625,6 +710,14 @@ 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; + struct tegra_display_hub_state *state; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + drm_atomic_private_obj_init(&hub->base, &state->base, + &tegra_display_hub_state_funcs); tegra->hub = hub; @@ -636,6 +729,7 @@ 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; + drm_atomic_private_obj_fini(&tegra->hub->base); tegra->hub = NULL; return 0; diff --git a/drivers/gpu/drm/tegra/hub.h b/drivers/gpu/drm/tegra/hub.h index 890a47cd05c3..85b8bf41a395 100644 --- a/drivers/gpu/drm/tegra/hub.h +++ b/drivers/gpu/drm/tegra/hub.h @@ -41,6 +41,7 @@ struct tegra_display_hub_soc { }; struct tegra_display_hub { + struct drm_private_obj base; struct host1x_client client; struct clk *clk_disp; struct clk *clk_dsc; @@ -57,6 +58,20 @@ to_tegra_display_hub(struct host1x_client *client) return container_of(client, struct tegra_display_hub, client); } +struct tegra_display_hub_state { + struct drm_private_state base; + + struct tegra_dc *dc; + unsigned long rate; + struct clk *clk; +}; + +static inline struct tegra_display_hub_state * +to_tegra_display_hub_state(struct drm_private_state *priv) +{ + return container_of(priv, struct tegra_display_hub_state, base); +} + struct tegra_dc; struct tegra_plane; @@ -68,6 +83,8 @@ struct drm_plane *tegra_shared_plane_create(struct drm_device *drm, unsigned int wgrp, unsigned int index); +int tegra_display_hub_atomic_check(struct drm_device *drm, + struct drm_atomic_state *state); void tegra_display_hub_atomic_commit(struct drm_device *drm, struct drm_atomic_state *state); -- cgit v1.2.3 From bd43c9f0fa1f664b58eefdc8aab7ac3c8b2026ec Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 13 Dec 2017 12:22:48 +0100 Subject: drm/tegra: gem: Map pages via the DMA API When allocating pages, map them with the DMA API in order to invalidate caches. This is the correct usage of the API and works just as well as faking up the SG table and using the dma_sync_sg_for_device() function. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/gem.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index d3b3aa148225..8fc5860ab19a 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -203,6 +203,8 @@ free: static void tegra_bo_free(struct drm_device *drm, struct tegra_bo *bo) { if (bo->pages) { + dma_unmap_sg(drm->dev, bo->sgt->sgl, bo->sgt->nents, + DMA_BIDIRECTIONAL); drm_gem_put_pages(&bo->gem, bo->pages, true, true); sg_free_table(bo->sgt); kfree(bo->sgt); @@ -213,8 +215,7 @@ static void tegra_bo_free(struct drm_device *drm, struct tegra_bo *bo) static int tegra_bo_get_pages(struct drm_device *drm, struct tegra_bo *bo) { - struct scatterlist *s; - unsigned int i; + int err; bo->pages = drm_gem_get_pages(&bo->gem); if (IS_ERR(bo->pages)) @@ -223,27 +224,26 @@ static int tegra_bo_get_pages(struct drm_device *drm, struct tegra_bo *bo) bo->num_pages = bo->gem.size >> PAGE_SHIFT; bo->sgt = drm_prime_pages_to_sg(bo->pages, bo->num_pages); - if (IS_ERR(bo->sgt)) + if (IS_ERR(bo->sgt)) { + err = PTR_ERR(bo->sgt); goto put_pages; + } - /* - * Fake up the SG table so that dma_sync_sg_for_device() can be used - * to flush the pages associated with it. - * - * TODO: Replace this by drm_clflash_sg() once it can be implemented - * without relying on symbols that are not exported. - */ - for_each_sg(bo->sgt->sgl, s, bo->sgt->nents, i) - sg_dma_address(s) = sg_phys(s); - - dma_sync_sg_for_device(drm->dev, bo->sgt->sgl, bo->sgt->nents, - DMA_TO_DEVICE); + err = dma_map_sg(drm->dev, bo->sgt->sgl, bo->sgt->nents, + DMA_BIDIRECTIONAL); + if (err == 0) { + err = -EFAULT; + goto free_sgt; + } return 0; +free_sgt: + sg_free_table(bo->sgt); + kfree(bo->sgt); put_pages: drm_gem_put_pages(&bo->gem, bo->pages, false, false); - return PTR_ERR(bo->sgt); + return err; } static int tegra_bo_alloc(struct drm_device *drm, struct tegra_bo *bo) -- cgit v1.2.3 From 27e92f1f1600c214bf649daddb9b88b68330a8d1 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 13 Dec 2017 12:25:14 +0100 Subject: drm/tegra: prime: Implement ->{begin,end}_cpu_access() These callbacks allow the exporter to swap in and pin the backing storage for buffers as well as invalidate the cache in preparation for accessing the buffer from the CPU, and flush the cache and unpin the backing storage when the CPU is done modifying the buffer. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/gem.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index 8fc5860ab19a..8b0b4ff64bb4 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -568,6 +568,34 @@ static void tegra_gem_prime_release(struct dma_buf *buf) drm_gem_dmabuf_release(buf); } +static int tegra_gem_prime_begin_cpu_access(struct dma_buf *buf, + enum dma_data_direction direction) +{ + struct drm_gem_object *gem = buf->priv; + struct tegra_bo *bo = to_tegra_bo(gem); + struct drm_device *drm = gem->dev; + + if (bo->pages) + dma_sync_sg_for_cpu(drm->dev, bo->sgt->sgl, bo->sgt->nents, + DMA_FROM_DEVICE); + + return 0; +} + +static int tegra_gem_prime_end_cpu_access(struct dma_buf *buf, + enum dma_data_direction direction) +{ + struct drm_gem_object *gem = buf->priv; + struct tegra_bo *bo = to_tegra_bo(gem); + struct drm_device *drm = gem->dev; + + if (bo->pages) + dma_sync_sg_for_device(drm->dev, bo->sgt->sgl, bo->sgt->nents, + DMA_TO_DEVICE); + + return 0; +} + static void *tegra_gem_prime_kmap_atomic(struct dma_buf *buf, unsigned long page) { @@ -618,6 +646,8 @@ static const struct dma_buf_ops tegra_gem_prime_dmabuf_ops = { .map_dma_buf = tegra_gem_prime_map_dma_buf, .unmap_dma_buf = tegra_gem_prime_unmap_dma_buf, .release = tegra_gem_prime_release, + .begin_cpu_access = tegra_gem_prime_begin_cpu_access, + .end_cpu_access = tegra_gem_prime_end_cpu_access, .map_atomic = tegra_gem_prime_kmap_atomic, .unmap_atomic = tegra_gem_prime_kunmap_atomic, .map = tegra_gem_prime_kmap, -- cgit v1.2.3