diff options
Diffstat (limited to 'drivers/gpu/drm/tegra/dc.c')
| -rw-r--r-- | drivers/gpu/drm/tegra/dc.c | 300 | 
1 files changed, 219 insertions, 81 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,  | 
