diff options
Diffstat (limited to 'drivers/gpu/drm')
-rw-r--r-- | drivers/gpu/drm/tegra/dc.c | 300 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/dc.h | 11 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/drm.c | 133 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/drm.h | 13 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/fb.c | 99 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/gem.c | 20 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/gr2d.c | 57 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/gr3d.c | 60 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/hub.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/plane.c | 194 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/plane.h | 15 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/vic.c | 5 |
12 files changed, 594 insertions, 315 deletions
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 9f83a65b5ea9..c3afe7b2237e 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -163,28 +163,89 @@ static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane) BLEND_COLOR_KEY_NONE; u32 blendnokey = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255); struct tegra_plane_state *state; + u32 blending[2]; unsigned int i; + /* disable blending for non-overlapping case */ + tegra_plane_writel(plane, blendnokey, DC_WIN_BLEND_NOKEY); + tegra_plane_writel(plane, foreground, DC_WIN_BLEND_1WIN); + 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; - } + if (state->opaque) { + /* + * Since custom fix-weight blending isn't utilized and weight + * of top window is set to max, we can enforce dependent + * blending which in this case results in transparent bottom + * window if top window is opaque and if top window enables + * alpha blending, then bottom window is getting alpha value + * of 1 minus the sum of alpha components of the overlapping + * plane. + */ + background[0] |= BLEND_CONTROL_DEPENDENT; + background[1] |= BLEND_CONTROL_DEPENDENT; - /* enable alpha blending if pixel format has an alpha component */ - if (!state->opaque) + /* + * The region where three windows overlap is the intersection + * of the two regions where two windows overlap. It contributes + * to the area if all of the windows on top of it have an alpha + * component. + */ + switch (state->base.normalized_zpos) { + case 0: + if (state->blending[0].alpha && + state->blending[1].alpha) + background[2] |= BLEND_CONTROL_DEPENDENT; + break; + + case 1: + background[2] |= BLEND_CONTROL_DEPENDENT; + break; + } + } else { + /* + * Enable alpha blending if pixel format has an alpha + * component. + */ foreground |= BLEND_CONTROL_ALPHA; - /* - * Disable blending and assume Window A is the bottom-most window, - * Window C is the top-most window and Window B is in the middle. - */ - tegra_plane_writel(plane, blendnokey, DC_WIN_BLEND_NOKEY); - tegra_plane_writel(plane, foreground, DC_WIN_BLEND_1WIN); + /* + * If any of the windows on top of this window is opaque, it + * will completely conceal this window within that area. If + * top window has an alpha component, it is blended over the + * bottom window. + */ + for (i = 0; i < 2; i++) { + if (state->blending[i].alpha && + state->blending[i].top) + background[i] |= BLEND_CONTROL_DEPENDENT; + } + + switch (state->base.normalized_zpos) { + case 0: + if (state->blending[0].alpha && + state->blending[1].alpha) + background[2] |= BLEND_CONTROL_DEPENDENT; + break; - switch (plane->index) { + case 1: + /* + * When both middle and topmost windows have an alpha, + * these windows a mixed together and then the result + * is blended over the bottom window. + */ + if (state->blending[0].alpha && + state->blending[0].top) + background[2] |= BLEND_CONTROL_ALPHA; + + if (state->blending[1].alpha && + state->blending[1].top) + background[2] |= BLEND_CONTROL_ALPHA; + break; + } + } + + switch (state->base.normalized_zpos) { case 0: tegra_plane_writel(plane, background[0], DC_WIN_BLEND_2WIN_X); tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y); @@ -192,8 +253,21 @@ static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane) break; case 1: - tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_X); - tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y); + /* + * If window B / C is topmost, then X / Y registers are + * matching the order of blending[...] state indices, + * otherwise a swap is required. + */ + if (!state->blending[0].top && state->blending[1].top) { + blending[0] = foreground; + blending[1] = background[1]; + } else { + blending[0] = background[0]; + blending[1] = foreground; + } + + tegra_plane_writel(plane, blending[0], DC_WIN_BLEND_2WIN_X); + tegra_plane_writel(plane, blending[1], DC_WIN_BLEND_2WIN_Y); tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY); break; @@ -224,6 +298,39 @@ static void tegra_plane_setup_blending(struct tegra_plane *plane, tegra_plane_writel(plane, value, DC_WIN_BLEND_LAYER_CONTROL); } +static bool +tegra_plane_use_horizontal_filtering(struct tegra_plane *plane, + const struct tegra_dc_window *window) +{ + struct tegra_dc *dc = plane->dc; + + if (window->src.w == window->dst.w) + return false; + + if (plane->index == 0 && dc->soc->has_win_a_without_filters) + return false; + + return true; +} + +static bool +tegra_plane_use_vertical_filtering(struct tegra_plane *plane, + const struct tegra_dc_window *window) +{ + struct tegra_dc *dc = plane->dc; + + if (window->src.h == window->dst.h) + return false; + + if (plane->index == 0 && dc->soc->has_win_a_without_filters) + return false; + + if (plane->index == 2 && dc->soc->has_win_c_without_vert_filter) + return false; + + return true; +} + static void tegra_dc_setup_window(struct tegra_plane *plane, const struct tegra_dc_window *window) { @@ -361,12 +468,50 @@ static void tegra_dc_setup_window(struct tegra_plane *plane, if (window->bottom_up) value |= V_DIRECTION; + if (tegra_plane_use_horizontal_filtering(plane, window)) { + /* + * Enable horizontal 6-tap filter and set filtering + * coefficients to the default values defined in TRM. + */ + tegra_plane_writel(plane, 0x00008000, DC_WIN_H_FILTER_P(0)); + tegra_plane_writel(plane, 0x3e087ce1, DC_WIN_H_FILTER_P(1)); + tegra_plane_writel(plane, 0x3b117ac1, DC_WIN_H_FILTER_P(2)); + tegra_plane_writel(plane, 0x591b73aa, DC_WIN_H_FILTER_P(3)); + tegra_plane_writel(plane, 0x57256d9a, DC_WIN_H_FILTER_P(4)); + tegra_plane_writel(plane, 0x552f668b, DC_WIN_H_FILTER_P(5)); + tegra_plane_writel(plane, 0x73385e8b, DC_WIN_H_FILTER_P(6)); + tegra_plane_writel(plane, 0x72435583, DC_WIN_H_FILTER_P(7)); + tegra_plane_writel(plane, 0x714c4c8b, DC_WIN_H_FILTER_P(8)); + tegra_plane_writel(plane, 0x70554393, DC_WIN_H_FILTER_P(9)); + tegra_plane_writel(plane, 0x715e389b, DC_WIN_H_FILTER_P(10)); + tegra_plane_writel(plane, 0x71662faa, DC_WIN_H_FILTER_P(11)); + tegra_plane_writel(plane, 0x536d25ba, DC_WIN_H_FILTER_P(12)); + tegra_plane_writel(plane, 0x55731bca, DC_WIN_H_FILTER_P(13)); + tegra_plane_writel(plane, 0x387a11d9, DC_WIN_H_FILTER_P(14)); + tegra_plane_writel(plane, 0x3c7c08f1, DC_WIN_H_FILTER_P(15)); + + value |= H_FILTER; + } + + if (tegra_plane_use_vertical_filtering(plane, window)) { + unsigned int i, k; + + /* + * Enable vertical 2-tap filter and set filtering + * coefficients to the default values defined in TRM. + */ + for (i = 0, k = 128; i < 16; i++, k -= 8) + tegra_plane_writel(plane, k, DC_WIN_V_FILTER_P(i)); + + value |= V_FILTER; + } + tegra_plane_writel(plane, value, DC_WIN_WIN_OPTIONS); - if (dc->soc->supports_blending) - tegra_plane_setup_blending(plane, window); - else + if (dc->soc->has_legacy_blending) tegra_plane_setup_blending_legacy(plane); + else + tegra_plane_setup_blending(plane, window); } static const u32 tegra20_primary_formats[] = { @@ -451,17 +596,18 @@ static int tegra_plane_atomic_check(struct drm_plane *plane, struct drm_plane_state *state) { struct tegra_plane_state *plane_state = to_tegra_plane_state(state); + unsigned int rotation = DRM_MODE_ROTATE_0 | DRM_MODE_REFLECT_Y; 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, &format, + err = tegra_plane_format(state->fb->format->format, + &plane_state->format, &plane_state->swap); if (err < 0) return err; @@ -472,22 +618,12 @@ static int tegra_plane_atomic_check(struct drm_plane *plane, * 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); + if (dc->soc->has_legacy_blending) { + err = tegra_plane_setup_legacy_state(tegra, plane_state); + if (err < 0) + return err; } - plane_state->format = format; - err = tegra_fb_get_tiling(state->fb, tiling); if (err < 0) return err; @@ -498,6 +634,13 @@ static int tegra_plane_atomic_check(struct drm_plane *plane, return -EINVAL; } + rotation = drm_rotation_simplify(state->rotation, rotation); + + if (rotation & DRM_MODE_REFLECT_Y) + plane_state->bottom_up = true; + else + plane_state->bottom_up = false; + /* * 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 @@ -558,7 +701,7 @@ static void tegra_plane_atomic_update(struct drm_plane *plane, window.dst.w = drm_rect_width(&plane->state->dst); window.dst.h = drm_rect_height(&plane->state->dst); window.bits_per_pixel = fb->format->cpp[0] * 8; - window.bottom_up = tegra_fb_is_bottom_up(fb); + window.bottom_up = tegra_fb_is_bottom_up(fb) || state->bottom_up; /* copy from state */ window.zpos = plane->state->normalized_zpos; @@ -639,9 +782,15 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm, } drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs); + drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255); - if (dc->soc->supports_blending) - drm_plane_create_zpos_property(&plane->base, 0, 0, 255); + err = drm_plane_create_rotation_property(&plane->base, + DRM_MODE_ROTATE_0, + DRM_MODE_ROTATE_0 | + DRM_MODE_REFLECT_Y); + if (err < 0) + dev_err(dc->dev, "failed to create rotation property: %d\n", + err); return &plane->base; } @@ -918,9 +1067,15 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, } drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs); + drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255); - if (dc->soc->supports_blending) - drm_plane_create_zpos_property(&plane->base, 0, 0, 255); + err = drm_plane_create_rotation_property(&plane->base, + DRM_MODE_ROTATE_0, + DRM_MODE_ROTATE_0 | + DRM_MODE_REFLECT_Y); + if (err < 0) + dev_err(dc->dev, "failed to create rotation property: %d\n", + err); return &plane->base; } @@ -1826,7 +1981,6 @@ 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; @@ -1838,20 +1992,11 @@ static int tegra_dc_init(struct host1x_client *client) if (!dc->syncpt) dev_warn(dc->dev, "failed to allocate syncpoint\n"); - 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; + dc->group = host1x_client_iommu_attach(client, true); + if (IS_ERR(dc->group)) { + err = PTR_ERR(dc->group); + dev_err(client->dev, "failed to attach to domain: %d\n", err); + return err; } if (dc->soc->wgrps) @@ -1916,24 +2061,15 @@ cleanup: if (!IS_ERR(primary)) drm_plane_cleanup(primary); - if (group && dc->domain) { - if (group == tegra->group) { - iommu_detach_group(dc->domain, group); - tegra->group = NULL; - } - - dc->domain = NULL; - } + host1x_client_iommu_detach(client, dc->group); + host1x_syncpt_free(dc->syncpt); return err; } static int tegra_dc_exit(struct host1x_client *client) { - struct drm_device *drm = dev_get_drvdata(client->parent); - struct iommu_group *group = iommu_group_get(client->dev); struct tegra_dc *dc = host1x_client_to_dc(client); - struct tegra_drm *tegra = drm->dev_private; int err; devm_free_irq(dc->dev, dc->irq, dc); @@ -1944,15 +2080,7 @@ static int tegra_dc_exit(struct host1x_client *client) return err; } - if (group && dc->domain) { - if (group == tegra->group) { - iommu_detach_group(dc->domain, group); - tegra->group = NULL; - } - - dc->domain = NULL; - } - + host1x_client_iommu_detach(client, dc->group); host1x_syncpt_free(dc->syncpt); return 0; @@ -1968,7 +2096,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, + .has_legacy_blending = true, .pitch_align = 8, .has_powergate = false, .coupled_pm = true, @@ -1978,6 +2106,8 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = { .num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats), .overlay_formats = tegra20_overlay_formats, .modifiers = tegra20_modifiers, + .has_win_a_without_filters = true, + .has_win_c_without_vert_filter = true, }; static const struct tegra_dc_soc_info tegra30_dc_soc_info = { @@ -1985,7 +2115,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, + .has_legacy_blending = true, .pitch_align = 8, .has_powergate = false, .coupled_pm = false, @@ -1995,6 +2125,8 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = { .num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats), .overlay_formats = tegra20_overlay_formats, .modifiers = tegra20_modifiers, + .has_win_a_without_filters = false, + .has_win_c_without_vert_filter = false, }; static const struct tegra_dc_soc_info tegra114_dc_soc_info = { @@ -2002,7 +2134,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, + .has_legacy_blending = true, .pitch_align = 64, .has_powergate = true, .coupled_pm = false, @@ -2012,6 +2144,8 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = { .num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats), .overlay_formats = tegra114_overlay_formats, .modifiers = tegra20_modifiers, + .has_win_a_without_filters = false, + .has_win_c_without_vert_filter = false, }; static const struct tegra_dc_soc_info tegra124_dc_soc_info = { @@ -2019,7 +2153,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, + .has_legacy_blending = false, .pitch_align = 64, .has_powergate = true, .coupled_pm = false, @@ -2029,6 +2163,8 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = { .num_overlay_formats = ARRAY_SIZE(tegra124_overlay_formats), .overlay_formats = tegra124_overlay_formats, .modifiers = tegra124_modifiers, + .has_win_a_without_filters = false, + .has_win_c_without_vert_filter = false, }; static const struct tegra_dc_soc_info tegra210_dc_soc_info = { @@ -2036,7 +2172,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, + .has_legacy_blending = false, .pitch_align = 64, .has_powergate = true, .coupled_pm = false, @@ -2046,6 +2182,8 @@ static const struct tegra_dc_soc_info tegra210_dc_soc_info = { .num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats), .overlay_formats = tegra114_overlay_formats, .modifiers = tegra124_modifiers, + .has_win_a_without_filters = false, + .has_win_c_without_vert_filter = false, }; static const struct tegra_windowgroup_soc tegra186_dc_wgrps[] = { @@ -2087,7 +2225,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, + .has_legacy_blending = false, .pitch_align = 64, .has_powergate = false, .coupled_pm = false, diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index d2b50d32de4d..e96f582ca692 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -55,7 +55,7 @@ struct tegra_dc_soc_info { bool supports_interlacing; bool supports_cursor; bool supports_block_linear; - bool supports_blending; + bool has_legacy_blending; unsigned int pitch_align; bool has_powergate; bool coupled_pm; @@ -67,6 +67,8 @@ struct tegra_dc_soc_info { const u32 *overlay_formats; unsigned int num_overlay_formats; const u64 *modifiers; + bool has_win_a_without_filters; + bool has_win_c_without_vert_filter; }; struct tegra_dc { @@ -92,7 +94,7 @@ struct tegra_dc { const struct tegra_dc_soc_info *soc; - struct iommu_domain *domain; + struct iommu_group *group; }; static inline struct tegra_dc * @@ -553,6 +555,9 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define THREAD_NUM(x) (((x) & 0x1f) << 1) #define THREAD_GROUP_ENABLE (1 << 0) +#define DC_WIN_H_FILTER_P(p) (0x601 + (p)) +#define DC_WIN_V_FILTER_P(p) (0x619 + (p)) + #define DC_WIN_CSC_YOF 0x611 #define DC_WIN_CSC_KYRGB 0x612 #define DC_WIN_CSC_KUR 0x613 @@ -566,6 +571,8 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define H_DIRECTION (1 << 0) #define V_DIRECTION (1 << 2) #define COLOR_EXPAND (1 << 6) +#define H_FILTER (1 << 8) +#define V_FILTER (1 << 10) #define CSC_ENABLE (1 << 18) #define WIN_ENABLE (1 << 30) diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index a0519612ae2c..776c1513e582 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -98,6 +98,10 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) goto free; } + err = iova_cache_get(); + if (err < 0) + goto domain; + geometry = &tegra->domain->geometry; gem_start = geometry->aperture_start; gem_end = geometry->aperture_end - CARVEOUT_SZ; @@ -191,11 +195,14 @@ config: drm_mode_config_cleanup(drm); if (tegra->domain) { - iommu_domain_free(tegra->domain); - drm_mm_takedown(&tegra->mm); mutex_destroy(&tegra->mm_lock); + drm_mm_takedown(&tegra->mm); put_iova_domain(&tegra->carveout.domain); + iova_cache_put(); } +domain: + if (tegra->domain) + iommu_domain_free(tegra->domain); free: kfree(tegra); return err; @@ -217,10 +224,11 @@ static void tegra_drm_unload(struct drm_device *drm) return; if (tegra->domain) { - iommu_domain_free(tegra->domain); - drm_mm_takedown(&tegra->mm); mutex_destroy(&tegra->mm_lock); + drm_mm_takedown(&tegra->mm); put_iova_domain(&tegra->carveout.domain); + iova_cache_put(); + iommu_domain_free(tegra->domain); } kfree(tegra); @@ -300,46 +308,15 @@ static int host1x_reloc_copy_from_user(struct host1x_reloc *dest, return 0; } -static int host1x_waitchk_copy_from_user(struct host1x_waitchk *dest, - struct drm_tegra_waitchk __user *src, - struct drm_file *file) -{ - u32 cmdbuf; - int err; - - err = get_user(cmdbuf, &src->handle); - if (err < 0) - return err; - - err = get_user(dest->offset, &src->offset); - if (err < 0) - return err; - - err = get_user(dest->syncpt_id, &src->syncpt); - if (err < 0) - return err; - - err = get_user(dest->thresh, &src->thresh); - if (err < 0) - return err; - - dest->bo = host1x_bo_lookup(file, cmdbuf); - if (!dest->bo) - return -ENOENT; - - return 0; -} - int tegra_drm_submit(struct tegra_drm_context *context, struct drm_tegra_submit *args, struct drm_device *drm, struct drm_file *file) { + struct host1x_client *client = &context->client->base; unsigned int num_cmdbufs = args->num_cmdbufs; unsigned int num_relocs = args->num_relocs; - unsigned int num_waitchks = args->num_waitchks; struct drm_tegra_cmdbuf __user *user_cmdbufs; struct drm_tegra_reloc __user *user_relocs; - struct drm_tegra_waitchk __user *user_waitchks; struct drm_tegra_syncpt __user *user_syncpt; struct drm_tegra_syncpt syncpt; struct host1x *host1x = dev_get_drvdata(drm->dev->parent); @@ -351,7 +328,6 @@ int tegra_drm_submit(struct tegra_drm_context *context, user_cmdbufs = u64_to_user_ptr(args->cmdbufs); user_relocs = u64_to_user_ptr(args->relocs); - user_waitchks = u64_to_user_ptr(args->waitchks); user_syncpt = u64_to_user_ptr(args->syncpts); /* We don't yet support other than one syncpt_incr struct per submit */ @@ -363,21 +339,20 @@ int tegra_drm_submit(struct tegra_drm_context *context, return -EINVAL; job = host1x_job_alloc(context->channel, args->num_cmdbufs, - args->num_relocs, args->num_waitchks); + args->num_relocs); if (!job) return -ENOMEM; job->num_relocs = args->num_relocs; - job->num_waitchk = args->num_waitchks; - job->client = (u32)args->context; - job->class = context->client->base.class; + job->client = client; + job->class = client->class; job->serialize = true; /* * Track referenced BOs so that they can be unreferenced after the * submission is complete. */ - num_refs = num_cmdbufs + num_relocs * 2 + num_waitchks; + num_refs = num_cmdbufs + num_relocs * 2; refs = kmalloc_array(num_refs, sizeof(*refs), GFP_KERNEL); if (!refs) { @@ -438,13 +413,13 @@ int tegra_drm_submit(struct tegra_drm_context *context, struct host1x_reloc *reloc; struct tegra_bo *obj; - err = host1x_reloc_copy_from_user(&job->relocarray[num_relocs], + err = host1x_reloc_copy_from_user(&job->relocs[num_relocs], &user_relocs[num_relocs], drm, file); if (err < 0) goto fail; - reloc = &job->relocarray[num_relocs]; + reloc = &job->relocs[num_relocs]; obj = host1x_to_tegra_bo(reloc->cmdbuf.bo); refs[num_refs++] = &obj->gem; @@ -468,30 +443,6 @@ int tegra_drm_submit(struct tegra_drm_context *context, } } - /* copy and resolve waitchks from submit */ - while (num_waitchks--) { - struct host1x_waitchk *wait = &job->waitchk[num_waitchks]; - struct tegra_bo *obj; - - err = host1x_waitchk_copy_from_user( - wait, &user_waitchks[num_waitchks], file); - if (err < 0) - goto fail; - - obj = host1x_to_tegra_bo(wait->bo); - refs[num_refs++] = &obj->gem; - - /* - * The unaligned offset will cause an unaligned write during - * of the waitchks patching, corrupting the commands stream. - */ - if (wait->offset & 3 || - wait->offset >= obj->gem.size) { - err = -EINVAL; - goto fail; - } - } - if (copy_from_user(&syncpt, user_syncpt, sizeof(syncpt))) { err = -EFAULT; goto fail; @@ -1101,6 +1052,52 @@ int tegra_drm_unregister_client(struct tegra_drm *tegra, return 0; } +struct iommu_group *host1x_client_iommu_attach(struct host1x_client *client, + bool shared) +{ + struct drm_device *drm = dev_get_drvdata(client->parent); + struct tegra_drm *tegra = drm->dev_private; + struct iommu_group *group = NULL; + int err; + + if (tegra->domain) { + group = iommu_group_get(client->dev); + if (!group) { + dev_err(client->dev, "failed to get IOMMU group\n"); + return ERR_PTR(-ENODEV); + } + + if (!shared || (shared && (group != tegra->group))) { + err = iommu_attach_group(tegra->domain, group); + if (err < 0) { + iommu_group_put(group); + return ERR_PTR(err); + } + + if (shared && !tegra->group) + tegra->group = group; + } + } + + return group; +} + +void host1x_client_iommu_detach(struct host1x_client *client, + struct iommu_group *group) +{ + struct drm_device *drm = dev_get_drvdata(client->parent); + struct tegra_drm *tegra = drm->dev_private; + + if (group) { + if (group == tegra->group) { + iommu_detach_group(tegra->domain, group); + tegra->group = NULL; + } + + iommu_group_put(group); + } +} + void *tegra_drm_alloc(struct tegra_drm *tegra, size_t size, dma_addr_t *dma) { struct iova *alloc; diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index 4f41aaec8530..92d248784396 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -29,16 +29,10 @@ struct reset_control; -struct tegra_fb { - struct drm_framebuffer base; - struct tegra_bo **planes; - unsigned int num_planes; -}; - #ifdef CONFIG_DRM_FBDEV_EMULATION struct tegra_fbdev { struct drm_fb_helper base; - struct tegra_fb *fb; + struct drm_framebuffer *fb; }; #endif @@ -97,6 +91,7 @@ struct tegra_drm_client { struct host1x_client base; struct list_head list; + unsigned int version; const struct tegra_drm_client_ops *ops; }; @@ -110,6 +105,10 @@ int tegra_drm_register_client(struct tegra_drm *tegra, struct tegra_drm_client *client); int tegra_drm_unregister_client(struct tegra_drm *tegra, struct tegra_drm_client *client); +struct iommu_group *host1x_client_iommu_attach(struct host1x_client *client, + bool shared); +void host1x_client_iommu_detach(struct host1x_client *client, + struct iommu_group *group); int tegra_drm_init(struct tegra_drm *tegra, struct drm_device *drm); int tegra_drm_exit(struct tegra_drm *tegra); diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index e69434909a42..4c22cdded3c2 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -14,11 +14,7 @@ #include "drm.h" #include "gem.h" - -static inline struct tegra_fb *to_tegra_fb(struct drm_framebuffer *fb) -{ - return container_of(fb, struct tegra_fb, base); -} +#include <drm/drm_gem_framebuffer_helper.h> #ifdef CONFIG_DRM_FBDEV_EMULATION static inline struct tegra_fbdev *to_tegra_fbdev(struct drm_fb_helper *helper) @@ -30,19 +26,14 @@ static inline struct tegra_fbdev *to_tegra_fbdev(struct drm_fb_helper *helper) struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer, unsigned int index) { - struct tegra_fb *fb = to_tegra_fb(framebuffer); - - if (index >= framebuffer->format->num_planes) - return NULL; - - return fb->planes[index]; + return to_tegra_bo(drm_gem_fb_get_obj(framebuffer, index)); } bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer) { - struct tegra_fb *fb = to_tegra_fb(framebuffer); + struct tegra_bo *bo = tegra_fb_get_plane(framebuffer, 0); - if (fb->planes[0]->flags & TEGRA_BO_BOTTOM_UP) + if (bo->flags & TEGRA_BO_BOTTOM_UP) return true; return false; @@ -51,8 +42,7 @@ bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer) int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer, struct tegra_bo_tiling *tiling) { - struct tegra_fb *fb = to_tegra_fb(framebuffer); - uint64_t modifier = fb->base.modifier; + uint64_t modifier = framebuffer->modifier; switch (modifier) { case DRM_FORMAT_MOD_LINEAR: @@ -102,46 +92,17 @@ int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer, return 0; } -static void tegra_fb_destroy(struct drm_framebuffer *framebuffer) -{ - struct tegra_fb *fb = to_tegra_fb(framebuffer); - unsigned int i; - - for (i = 0; i < fb->num_planes; i++) { - struct tegra_bo *bo = fb->planes[i]; - - if (bo) { - if (bo->pages) - vunmap(bo->vaddr); - - drm_gem_object_put_unlocked(&bo->gem); - } - } - - drm_framebuffer_cleanup(framebuffer); - kfree(fb->planes); - kfree(fb); -} - -static int tegra_fb_create_handle(struct drm_framebuffer *framebuffer, - struct drm_file *file, unsigned int *handle) -{ - struct tegra_fb *fb = to_tegra_fb(framebuffer); - - return drm_gem_handle_create(file, &fb->planes[0]->gem, handle); -} - static const struct drm_framebuffer_funcs tegra_fb_funcs = { - .destroy = tegra_fb_destroy, - .create_handle = tegra_fb_create_handle, + .destroy = drm_gem_fb_destroy, + .create_handle = drm_gem_fb_create_handle, }; -static struct tegra_fb *tegra_fb_alloc(struct drm_device *drm, - const struct drm_mode_fb_cmd2 *mode_cmd, - struct tegra_bo **planes, - unsigned int num_planes) +static struct drm_framebuffer *tegra_fb_alloc(struct drm_device *drm, + const struct drm_mode_fb_cmd2 *mode_cmd, + struct tegra_bo **planes, + unsigned int num_planes) { - struct tegra_fb *fb; + struct drm_framebuffer *fb; unsigned int i; int err; @@ -149,24 +110,15 @@ static struct tegra_fb *tegra_fb_alloc(struct drm_device *drm, if (!fb) return ERR_PTR(-ENOMEM); - fb->planes = kzalloc(num_planes * sizeof(*planes), GFP_KERNEL); - if (!fb->planes) { - kfree(fb); - return ERR_PTR(-ENOMEM); - } - - fb->num_planes = num_planes; + drm_helper_mode_fill_fb_struct(drm, fb, mode_cmd); - drm_helper_mode_fill_fb_struct(drm, &fb->base, mode_cmd); + for (i = 0; i < fb->format->num_planes; i++) + fb->obj[i] = &planes[i]->gem; - for (i = 0; i < fb->num_planes; i++) - fb->planes[i] = planes[i]; - - err = drm_framebuffer_init(drm, &fb->base, &tegra_fb_funcs); + err = drm_framebuffer_init(drm, fb, &tegra_fb_funcs); if (err < 0) { dev_err(drm->dev, "failed to initialize framebuffer: %d\n", err); - kfree(fb->planes); kfree(fb); return ERR_PTR(err); } @@ -181,7 +133,7 @@ struct drm_framebuffer *tegra_fb_create(struct drm_device *drm, unsigned int hsub, vsub, i; struct tegra_bo *planes[4]; struct drm_gem_object *gem; - struct tegra_fb *fb; + struct drm_framebuffer *fb; int err; hsub = drm_format_horz_chroma_subsampling(cmd->pixel_format); @@ -217,7 +169,7 @@ struct drm_framebuffer *tegra_fb_create(struct drm_device *drm, goto unreference; } - return &fb->base; + return fb; unreference: while (i--) @@ -298,7 +250,7 @@ static int tegra_fbdev_probe(struct drm_fb_helper *helper, return PTR_ERR(fbdev->fb); } - fb = &fbdev->fb->base; + fb = fbdev->fb; helper->fb = fb; helper->fbdev = info; @@ -398,8 +350,17 @@ static void tegra_fbdev_exit(struct tegra_fbdev *fbdev) { drm_fb_helper_unregister_fbi(&fbdev->base); - if (fbdev->fb) - drm_framebuffer_remove(&fbdev->fb->base); + if (fbdev->fb) { + struct tegra_bo *bo = tegra_fb_get_plane(fbdev->fb, 0); + + /* Undo the special mapping we made in fbdev probe. */ + if (bo && bo->pages) { + vunmap(bo->vaddr); + bo->vaddr = 0; + } + + drm_framebuffer_remove(fbdev->fb); + } drm_fb_helper_fini(&fbdev->base); tegra_fbdev_free(fbdev); diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index 8b0b4ff64bb4..00a5c9f32254 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -422,14 +422,13 @@ int tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm, return 0; } -static int tegra_bo_fault(struct vm_fault *vmf) +static vm_fault_t tegra_bo_fault(struct vm_fault *vmf) { struct vm_area_struct *vma = vmf->vma; struct drm_gem_object *gem = vma->vm_private_data; struct tegra_bo *bo = to_tegra_bo(gem); struct page *page; pgoff_t offset; - int err; if (!bo->pages) return VM_FAULT_SIGBUS; @@ -437,20 +436,7 @@ static int tegra_bo_fault(struct vm_fault *vmf) offset = (vmf->address - vma->vm_start) >> PAGE_SHIFT; page = bo->pages[offset]; - err = vm_insert_page(vma, vmf->address, page); - switch (err) { - case -EAGAIN: - case 0: - case -ERESTARTSYS: - case -EINTR: - case -EBUSY: - return VM_FAULT_NOPAGE; - - case -ENOMEM: - return VM_FAULT_OOM; - } - - return VM_FAULT_SIGBUS; + return vmf_insert_page(vma, vmf->address, page); } const struct vm_operations_struct tegra_bo_vm_ops = { @@ -663,6 +649,8 @@ struct dma_buf *tegra_gem_prime_export(struct drm_device *drm, { DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + exp_info.exp_name = KBUILD_MODNAME; + exp_info.owner = drm->driver->fops->owner; exp_info.ops = &tegra_gem_prime_dmabuf_ops; exp_info.size = gem->size; exp_info.flags = flags; diff --git a/drivers/gpu/drm/tegra/gr2d.c b/drivers/gpu/drm/tegra/gr2d.c index 9a8ea93016a9..673059fd2fcb 100644 --- a/drivers/gpu/drm/tegra/gr2d.c +++ b/drivers/gpu/drm/tegra/gr2d.c @@ -7,16 +7,25 @@ */ #include <linux/clk.h> +#include <linux/iommu.h> +#include <linux/of_device.h> #include "drm.h" #include "gem.h" #include "gr2d.h" +struct gr2d_soc { + unsigned int version; +}; + struct gr2d { + struct iommu_group *group; struct tegra_drm_client client; struct host1x_channel *channel; struct clk *clk; + const struct gr2d_soc *soc; + DECLARE_BITMAP(addr_regs, GR2D_NUM_REGS); }; @@ -31,6 +40,7 @@ static int gr2d_init(struct host1x_client *client) struct drm_device *dev = dev_get_drvdata(client->parent); unsigned long flags = HOST1X_SYNCPT_HAS_BASE; struct gr2d *gr2d = to_gr2d(drm); + int err; gr2d->channel = host1x_channel_request(client->dev); if (!gr2d->channel) @@ -38,24 +48,48 @@ static int gr2d_init(struct host1x_client *client) client->syncpts[0] = host1x_syncpt_request(client, flags); if (!client->syncpts[0]) { - host1x_channel_put(gr2d->channel); - return -ENOMEM; + err = -ENOMEM; + dev_err(client->dev, "failed to request syncpoint: %d\n", err); + goto put; + } + + gr2d->group = host1x_client_iommu_attach(client, false); + if (IS_ERR(gr2d->group)) { + err = PTR_ERR(gr2d->group); + dev_err(client->dev, "failed to attach to domain: %d\n", err); + goto free; + } + + err = tegra_drm_register_client(dev->dev_private, drm); + if (err < 0) { + dev_err(client->dev, "failed to register client: %d\n", err); + goto detach; } - return tegra_drm_register_client(dev->dev_private, drm); + return 0; + +detach: + host1x_client_iommu_detach(client, gr2d->group); +free: + host1x_syncpt_free(client->syncpts[0]); +put: + host1x_channel_put(gr2d->channel); + return err; } static int gr2d_exit(struct host1x_client *client) { struct tegra_drm_client *drm = host1x_to_drm_client(client); struct drm_device *dev = dev_get_drvdata(client->parent); + struct tegra_drm *tegra = dev->dev_private; struct gr2d *gr2d = to_gr2d(drm); int err; - err = tegra_drm_unregister_client(dev->dev_private, drm); + err = tegra_drm_unregister_client(tegra, drm); if (err < 0) return err; + host1x_client_iommu_detach(client, gr2d->group); host1x_syncpt_free(client->syncpts[0]); host1x_channel_put(gr2d->channel); @@ -123,9 +157,17 @@ static const struct tegra_drm_client_ops gr2d_ops = { .submit = tegra_drm_submit, }; +static const struct gr2d_soc tegra20_gr2d_soc = { + .version = 0x20, +}; + +static const struct gr2d_soc tegra30_gr2d_soc = { + .version = 0x30, +}; + static const struct of_device_id gr2d_match[] = { - { .compatible = "nvidia,tegra30-gr2d" }, - { .compatible = "nvidia,tegra20-gr2d" }, + { .compatible = "nvidia,tegra30-gr2d", .data = &tegra20_gr2d_soc }, + { .compatible = "nvidia,tegra20-gr2d", .data = &tegra30_gr2d_soc }, { }, }; MODULE_DEVICE_TABLE(of, gr2d_match); @@ -158,6 +200,8 @@ static int gr2d_probe(struct platform_device *pdev) if (!gr2d) return -ENOMEM; + gr2d->soc = of_device_get_match_data(dev); + syncpts = devm_kzalloc(dev, sizeof(*syncpts), GFP_KERNEL); if (!syncpts) return -ENOMEM; @@ -182,6 +226,7 @@ static int gr2d_probe(struct platform_device *pdev) gr2d->client.base.num_syncpts = 1; INIT_LIST_HEAD(&gr2d->client.list); + gr2d->client.version = gr2d->soc->version; gr2d->client.ops = &gr2d_ops; err = host1x_client_register(&gr2d->client.base); diff --git a/drivers/gpu/drm/tegra/gr3d.c b/drivers/gpu/drm/tegra/gr3d.c index 28c4ef63065b..4778ae999668 100644 --- a/drivers/gpu/drm/tegra/gr3d.c +++ b/drivers/gpu/drm/tegra/gr3d.c @@ -9,7 +9,9 @@ #include <linux/clk.h> #include <linux/host1x.h> +#include <linux/iommu.h> #include <linux/module.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/reset.h> @@ -19,7 +21,12 @@ #include "gem.h" #include "gr3d.h" +struct gr3d_soc { + unsigned int version; +}; + struct gr3d { + struct iommu_group *group; struct tegra_drm_client client; struct host1x_channel *channel; struct clk *clk_secondary; @@ -27,6 +34,8 @@ struct gr3d { struct reset_control *rst_secondary; struct reset_control *rst; + const struct gr3d_soc *soc; + DECLARE_BITMAP(addr_regs, GR3D_NUM_REGS); }; @@ -41,6 +50,7 @@ static int gr3d_init(struct host1x_client *client) struct drm_device *dev = dev_get_drvdata(client->parent); unsigned long flags = HOST1X_SYNCPT_HAS_BASE; struct gr3d *gr3d = to_gr3d(drm); + int err; gr3d->channel = host1x_channel_request(client->dev); if (!gr3d->channel) @@ -48,11 +58,33 @@ static int gr3d_init(struct host1x_client *client) client->syncpts[0] = host1x_syncpt_request(client, flags); if (!client->syncpts[0]) { - host1x_channel_put(gr3d->channel); - return -ENOMEM; + err = -ENOMEM; + dev_err(client->dev, "failed to request syncpoint: %d\n", err); + goto put; } - return tegra_drm_register_client(dev->dev_private, drm); + gr3d->group = host1x_client_iommu_attach(client, false); + if (IS_ERR(gr3d->group)) { + err = PTR_ERR(gr3d->group); + dev_err(client->dev, "failed to attach to domain: %d\n", err); + goto free; + } + + err = tegra_drm_register_client(dev->dev_private, drm); + if (err < 0) { + dev_err(client->dev, "failed to register client: %d\n", err); + goto detach; + } + + return 0; + +detach: + host1x_client_iommu_detach(client, gr3d->group); +free: + host1x_syncpt_free(client->syncpts[0]); +put: + host1x_channel_put(gr3d->channel); + return err; } static int gr3d_exit(struct host1x_client *client) @@ -66,6 +98,7 @@ static int gr3d_exit(struct host1x_client *client) if (err < 0) return err; + host1x_client_iommu_detach(client, gr3d->group); host1x_syncpt_free(client->syncpts[0]); host1x_channel_put(gr3d->channel); @@ -125,10 +158,22 @@ static const struct tegra_drm_client_ops gr3d_ops = { .submit = tegra_drm_submit, }; +static const struct gr3d_soc tegra20_gr3d_soc = { + .version = 0x20, +}; + +static const struct gr3d_soc tegra30_gr3d_soc = { + .version = 0x30, +}; + +static const struct gr3d_soc tegra114_gr3d_soc = { + .version = 0x35, +}; + static const struct of_device_id tegra_gr3d_match[] = { - { .compatible = "nvidia,tegra114-gr3d" }, - { .compatible = "nvidia,tegra30-gr3d" }, - { .compatible = "nvidia,tegra20-gr3d" }, + { .compatible = "nvidia,tegra114-gr3d", .data = &tegra114_gr3d_soc }, + { .compatible = "nvidia,tegra30-gr3d", .data = &tegra30_gr3d_soc }, + { .compatible = "nvidia,tegra20-gr3d", .data = &tegra20_gr3d_soc }, { } }; MODULE_DEVICE_TABLE(of, tegra_gr3d_match); @@ -250,6 +295,8 @@ static int gr3d_probe(struct platform_device *pdev) if (!gr3d) return -ENOMEM; + gr3d->soc = of_device_get_match_data(&pdev->dev); + syncpts = devm_kzalloc(&pdev->dev, sizeof(*syncpts), GFP_KERNEL); if (!syncpts) return -ENOMEM; @@ -307,6 +354,7 @@ static int gr3d_probe(struct platform_device *pdev) gr3d->client.base.num_syncpts = 1; INIT_LIST_HEAD(&gr3d->client.list); + gr3d->client.version = gr3d->soc->version; gr3d->client.ops = &gr3d_ops; err = host1x_client_register(&gr3d->client.base); diff --git a/drivers/gpu/drm/tegra/hub.c b/drivers/gpu/drm/tegra/hub.c index 9a3f23d4780f..8f4fcbb515fb 100644 --- a/drivers/gpu/drm/tegra/hub.c +++ b/drivers/gpu/drm/tegra/hub.c @@ -687,7 +687,7 @@ void tegra_display_hub_atomic_commit(struct drm_device *drm, struct device *dev = hub->client.dev; int err; - hub_state = tegra_display_hub_get_state(hub, state); + hub_state = to_tegra_display_hub_state(hub->base.state); if (hub_state->clk) { err = clk_set_rate(hub_state->clk, hub_state->rate); diff --git a/drivers/gpu/drm/tegra/plane.c b/drivers/gpu/drm/tegra/plane.c index 176ef46c615c..d068e8aa3553 100644 --- a/drivers/gpu/drm/tegra/plane.c +++ b/drivers/gpu/drm/tegra/plane.c @@ -23,6 +23,7 @@ static void tegra_plane_destroy(struct drm_plane *plane) static void tegra_plane_reset(struct drm_plane *plane) { + struct tegra_plane *p = to_tegra_plane(plane); struct tegra_plane_state *state; if (plane->state) @@ -35,6 +36,8 @@ static void tegra_plane_reset(struct drm_plane *plane) if (state) { plane->state = &state->base; plane->state->plane = plane; + plane->state->zpos = p->index; + plane->state->normalized_zpos = p->index; } } @@ -53,10 +56,11 @@ tegra_plane_atomic_duplicate_state(struct drm_plane *plane) copy->tiling = state->tiling; copy->format = state->format; copy->swap = state->swap; + copy->bottom_up = state->bottom_up; copy->opaque = state->opaque; - for (i = 0; i < 3; i++) - copy->dependent[i] = state->dependent[i]; + for (i = 0; i < 2; i++) + copy->blending[i] = state->blending[i]; return ©->base; } @@ -267,24 +271,8 @@ static bool __drm_format_has_alpha(u32 format) 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) +static int tegra_plane_format_get_alpha(unsigned int opaque, + unsigned int *alpha) { if (tegra_plane_format_is_yuv(opaque, NULL)) { *alpha = opaque; @@ -316,6 +304,67 @@ int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha) return -EINVAL; } +/* + * This is applicable to Tegra20 and Tegra30 only where the opaque formats can + * be emulated using the alpha formats and alpha blending disabled. + */ +static int tegra_plane_setup_opacity(struct tegra_plane *tegra, + struct tegra_plane_state *state) +{ + unsigned int format; + int err; + + switch (state->format) { + case WIN_COLOR_DEPTH_B5G5R5A1: + case WIN_COLOR_DEPTH_A1B5G5R5: + case WIN_COLOR_DEPTH_R8G8B8A8: + case WIN_COLOR_DEPTH_B8G8R8A8: + state->opaque = false; + break; + + default: + err = tegra_plane_format_get_alpha(state->format, &format); + if (err < 0) + return err; + + state->format = format; + state->opaque = true; + break; + } + + return 0; +} + +static int tegra_plane_check_transparency(struct tegra_plane *tegra, + struct tegra_plane_state *state) +{ + struct drm_plane_state *old, *plane_state; + struct drm_plane *plane; + + old = drm_atomic_get_old_plane_state(state->base.state, &tegra->base); + + /* check if zpos / transparency changed */ + if (old->normalized_zpos == state->base.normalized_zpos && + to_tegra_plane_state(old)->opaque == state->opaque) + return 0; + + /* include all sibling planes into this commit */ + drm_for_each_plane(plane, tegra->base.dev) { + struct tegra_plane *p = to_tegra_plane(plane); + + /* skip this plane and planes on different CRTCs */ + if (p == tegra || p->dc != tegra->dc) + continue; + + plane_state = drm_atomic_get_plane_state(state->base.state, + plane); + if (IS_ERR(plane_state)) + return PTR_ERR(plane_state); + } + + return 1; +} + static unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane, struct tegra_plane *other) { @@ -336,61 +385,98 @@ static unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane, return index; } -void tegra_plane_check_dependent(struct tegra_plane *tegra, - struct tegra_plane_state *state) +static void tegra_plane_update_transparency(struct tegra_plane *tegra, + struct tegra_plane_state *state) { - struct drm_plane_state *old, *new; + struct drm_plane_state *new; struct drm_plane *plane; - unsigned int zpos[2]; unsigned int i; - for (i = 0; i < 2; i++) - zpos[i] = 0; - - for_each_oldnew_plane_in_state(state->base.state, plane, old, new, i) { + for_each_new_plane_in_state(state->base.state, plane, 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) + if (p == tegra || p->dc != tegra->dc) continue; index = tegra_plane_get_overlap_index(tegra, p); - state->dependent[index] = false; + if (new->fb && __drm_format_has_alpha(new->fb->format->format)) + state->blending[index].alpha = true; + else + state->blending[index].alpha = false; + + if (new->normalized_zpos > state->base.normalized_zpos) + state->blending[index].top = true; + else + state->blending[index].top = false; /* - * 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. + * Missing framebuffer means that plane is disabled, in this + * case mark B / C window as top to be able to differentiate + * windows indices order in regards to zPos for the middle + * window X / Y registers programming. */ - 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; - } + if (!new->fb) + state->blending[index].top = (index == 1); } +} + +static int tegra_plane_setup_transparency(struct tegra_plane *tegra, + struct tegra_plane_state *state) +{ + struct tegra_plane_state *tegra_state; + struct drm_plane_state *new; + struct drm_plane *plane; + int err; /* - * 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. + * If planes zpos / transparency changed, sibling planes blending + * state may require adjustment and in this case they will be included + * into this atom commit, otherwise blending state is unchanged. */ - for (i = 0; i < 2; i++) - state->dependent[2] = state->dependent[2] || - state->dependent[i]; + err = tegra_plane_check_transparency(tegra, state); + if (err <= 0) + return err; /* - * 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. + * All planes are now in the atomic state, walk them up and update + * transparency state for each plane. */ - for (i = 0; i < 2; i++) { - if (zpos[i] > tegra->index) - state->dependent[2] = state->dependent[2] && - state->dependent[i]; + drm_for_each_plane(plane, tegra->base.dev) { + struct tegra_plane *p = to_tegra_plane(plane); + + /* skip planes on different CRTCs */ + if (p->dc != tegra->dc) + continue; + + new = drm_atomic_get_new_plane_state(state->base.state, plane); + tegra_state = to_tegra_plane_state(new); + + /* + * There is no need to update blending state for the disabled + * plane. + */ + if (new->fb) + tegra_plane_update_transparency(p, tegra_state); } + + return 0; +} + +int tegra_plane_setup_legacy_state(struct tegra_plane *tegra, + struct tegra_plane_state *state) +{ + int err; + + err = tegra_plane_setup_opacity(tegra, state); + if (err < 0) + return err; + + err = tegra_plane_setup_transparency(tegra, state); + if (err < 0) + return err; + + return 0; } diff --git a/drivers/gpu/drm/tegra/plane.h b/drivers/gpu/drm/tegra/plane.h index 6938719e7e5d..e79e6b4a8e0a 100644 --- a/drivers/gpu/drm/tegra/plane.h +++ b/drivers/gpu/drm/tegra/plane.h @@ -34,6 +34,11 @@ static inline struct tegra_plane *to_tegra_plane(struct drm_plane *plane) return container_of(plane, struct tegra_plane, base); } +struct tegra_plane_legacy_blending_state { + bool alpha; + bool top; +}; + struct tegra_plane_state { struct drm_plane_state base; @@ -41,9 +46,11 @@ struct tegra_plane_state { u32 format; u32 swap; + bool bottom_up; + /* used for legacy blending support only */ + struct tegra_plane_legacy_blending_state blending[2]; bool opaque; - bool dependent[3]; }; static inline struct tegra_plane_state * @@ -62,9 +69,7 @@ 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); +int tegra_plane_setup_legacy_state(struct tegra_plane *tegra, + struct tegra_plane_state *state); #endif /* TEGRA_PLANE_H */ diff --git a/drivers/gpu/drm/tegra/vic.c b/drivers/gpu/drm/tegra/vic.c index f5794dd49f3b..9f657a63b0bb 100644 --- a/drivers/gpu/drm/tegra/vic.c +++ b/drivers/gpu/drm/tegra/vic.c @@ -25,6 +25,7 @@ struct vic_config { const char *firmware; + unsigned int version; }; struct vic { @@ -264,18 +265,21 @@ static const struct tegra_drm_client_ops vic_ops = { static const struct vic_config vic_t124_config = { .firmware = NVIDIA_TEGRA_124_VIC_FIRMWARE, + .version = 0x40, }; #define NVIDIA_TEGRA_210_VIC_FIRMWARE "nvidia/tegra210/vic04_ucode.bin" static const struct vic_config vic_t210_config = { .firmware = NVIDIA_TEGRA_210_VIC_FIRMWARE, + .version = 0x21, }; #define NVIDIA_TEGRA_186_VIC_FIRMWARE "nvidia/tegra186/vic04_ucode.bin" static const struct vic_config vic_t186_config = { .firmware = NVIDIA_TEGRA_186_VIC_FIRMWARE, + .version = 0x18, }; static const struct of_device_id vic_match[] = { @@ -342,6 +346,7 @@ static int vic_probe(struct platform_device *pdev) vic->dev = dev; INIT_LIST_HEAD(&vic->client.list); + vic->client.version = vic->config->version; vic->client.ops = &vic_ops; err = host1x_client_register(&vic->client.base); |