diff options
Diffstat (limited to 'drivers/gpu/drm/tegra/dc.c')
| -rw-r--r-- | drivers/gpu/drm/tegra/dc.c | 253 | 
1 files changed, 177 insertions, 76 deletions
| diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 0ae3a025efe9..f9120dc24682 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -604,23 +604,25 @@ static const u64 tegra124_modifiers[] = {  };  static int tegra_plane_atomic_check(struct drm_plane *plane, -				    struct drm_plane_state *state) +				    struct drm_atomic_state *state)  { -	struct tegra_plane_state *plane_state = to_tegra_plane_state(state); +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, +										 plane); +	struct tegra_plane_state *plane_state = to_tegra_plane_state(new_plane_state);  	unsigned int supported_rotation = DRM_MODE_ROTATE_0 |  					  DRM_MODE_REFLECT_X |  					  DRM_MODE_REFLECT_Y; -	unsigned int rotation = state->rotation; +	unsigned int rotation = new_plane_state->rotation;  	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); +	struct tegra_dc *dc = to_tegra_dc(new_plane_state->crtc);  	int err;  	/* no need for further checks if the plane is being disabled */ -	if (!state->crtc) +	if (!new_plane_state->crtc)  		return 0; -	err = tegra_plane_format(state->fb->format->format, +	err = tegra_plane_format(new_plane_state->fb->format->format,  				 &plane_state->format,  				 &plane_state->swap);  	if (err < 0) @@ -638,7 +640,7 @@ static int tegra_plane_atomic_check(struct drm_plane *plane,  			return err;  	} -	err = tegra_fb_get_tiling(state->fb, tiling); +	err = tegra_fb_get_tiling(new_plane_state->fb, tiling);  	if (err < 0)  		return err; @@ -654,7 +656,7 @@ static int tegra_plane_atomic_check(struct drm_plane *plane,  	 * property in order to achieve the same result.  The legacy BO flag  	 * duplicates the DRM rotation property when both are set.  	 */ -	if (tegra_fb_is_bottom_up(state->fb)) +	if (tegra_fb_is_bottom_up(new_plane_state->fb))  		rotation |= DRM_MODE_REFLECT_Y;  	rotation = drm_rotation_simplify(rotation, supported_rotation); @@ -674,14 +676,14 @@ static int tegra_plane_atomic_check(struct drm_plane *plane,  	 * error out if the user tries to display a framebuffer with such a  	 * configuration.  	 */ -	if (state->fb->format->num_planes > 2) { -		if (state->fb->pitches[2] != state->fb->pitches[1]) { +	if (new_plane_state->fb->format->num_planes > 2) { +		if (new_plane_state->fb->pitches[2] != new_plane_state->fb->pitches[1]) {  			DRM_ERROR("unsupported UV-plane configuration\n");  			return -EINVAL;  		}  	} -	err = tegra_plane_state_add(tegra, state); +	err = tegra_plane_state_add(tegra, new_plane_state);  	if (err < 0)  		return err; @@ -689,8 +691,10 @@ static int tegra_plane_atomic_check(struct drm_plane *plane,  }  static void tegra_plane_atomic_disable(struct drm_plane *plane, -				       struct drm_plane_state *old_state) +				       struct drm_atomic_state *state)  { +	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, +									   plane);  	struct tegra_plane *p = to_tegra_plane(plane);  	u32 value; @@ -704,42 +708,44 @@ static void tegra_plane_atomic_disable(struct drm_plane *plane,  }  static void tegra_plane_atomic_update(struct drm_plane *plane, -				      struct drm_plane_state *old_state) +				      struct drm_atomic_state *state)  { -	struct tegra_plane_state *state = to_tegra_plane_state(plane->state); -	struct drm_framebuffer *fb = plane->state->fb; +	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, +									   plane); +	struct tegra_plane_state *tegra_plane_state = to_tegra_plane_state(new_state); +	struct drm_framebuffer *fb = new_state->fb;  	struct tegra_plane *p = to_tegra_plane(plane);  	struct tegra_dc_window window;  	unsigned int i;  	/* rien ne va plus */ -	if (!plane->state->crtc || !plane->state->fb) +	if (!new_state->crtc || !new_state->fb)  		return; -	if (!plane->state->visible) -		return tegra_plane_atomic_disable(plane, old_state); +	if (!new_state->visible) +		return tegra_plane_atomic_disable(plane, state);  	memset(&window, 0, sizeof(window)); -	window.src.x = plane->state->src.x1 >> 16; -	window.src.y = plane->state->src.y1 >> 16; -	window.src.w = drm_rect_width(&plane->state->src) >> 16; -	window.src.h = drm_rect_height(&plane->state->src) >> 16; -	window.dst.x = plane->state->dst.x1; -	window.dst.y = plane->state->dst.y1; -	window.dst.w = drm_rect_width(&plane->state->dst); -	window.dst.h = drm_rect_height(&plane->state->dst); +	window.src.x = new_state->src.x1 >> 16; +	window.src.y = new_state->src.y1 >> 16; +	window.src.w = drm_rect_width(&new_state->src) >> 16; +	window.src.h = drm_rect_height(&new_state->src) >> 16; +	window.dst.x = new_state->dst.x1; +	window.dst.y = new_state->dst.y1; +	window.dst.w = drm_rect_width(&new_state->dst); +	window.dst.h = drm_rect_height(&new_state->dst);  	window.bits_per_pixel = fb->format->cpp[0] * 8; -	window.reflect_x = state->reflect_x; -	window.reflect_y = state->reflect_y; +	window.reflect_x = tegra_plane_state->reflect_x; +	window.reflect_y = tegra_plane_state->reflect_y;  	/* copy from state */ -	window.zpos = plane->state->normalized_zpos; -	window.tiling = state->tiling; -	window.format = state->format; -	window.swap = state->swap; +	window.zpos = new_state->normalized_zpos; +	window.tiling = tegra_plane_state->tiling; +	window.format = tegra_plane_state->format; +	window.swap = tegra_plane_state->swap;  	for (i = 0; i < fb->format->num_planes; i++) { -		window.base[i] = state->iova[i] + fb->offsets[i]; +		window.base[i] = tegra_plane_state->iova[i] + fb->offsets[i];  		/*  		 * Tegra uses a shared stride for UV planes. Framebuffers are @@ -826,34 +832,40 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm,  	return &plane->base;  } -static const u32 tegra_cursor_plane_formats[] = { +static const u32 tegra_legacy_cursor_plane_formats[] = {  	DRM_FORMAT_RGBA8888,  }; +static const u32 tegra_cursor_plane_formats[] = { +	DRM_FORMAT_ARGB8888, +}; +  static int tegra_cursor_atomic_check(struct drm_plane *plane, -				     struct drm_plane_state *state) +				     struct drm_atomic_state *state)  { +	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, +										 plane);  	struct tegra_plane *tegra = to_tegra_plane(plane);  	int err;  	/* no need for further checks if the plane is being disabled */ -	if (!state->crtc) +	if (!new_plane_state->crtc)  		return 0;  	/* scaling not supported for cursor */ -	if ((state->src_w >> 16 != state->crtc_w) || -	    (state->src_h >> 16 != state->crtc_h)) +	if ((new_plane_state->src_w >> 16 != new_plane_state->crtc_w) || +	    (new_plane_state->src_h >> 16 != new_plane_state->crtc_h))  		return -EINVAL;  	/* only square cursors supported */ -	if (state->src_w != state->src_h) +	if (new_plane_state->src_w != new_plane_state->src_h)  		return -EINVAL; -	if (state->crtc_w != 32 && state->crtc_w != 64 && -	    state->crtc_w != 128 && state->crtc_w != 256) +	if (new_plane_state->crtc_w != 32 && new_plane_state->crtc_w != 64 && +	    new_plane_state->crtc_w != 128 && new_plane_state->crtc_w != 256)  		return -EINVAL; -	err = tegra_plane_state_add(tegra, state); +	err = tegra_plane_state_add(tegra, new_plane_state);  	if (err < 0)  		return err; @@ -861,17 +873,31 @@ static int tegra_cursor_atomic_check(struct drm_plane *plane,  }  static void tegra_cursor_atomic_update(struct drm_plane *plane, -				       struct drm_plane_state *old_state) +				       struct drm_atomic_state *state)  { -	struct tegra_plane_state *state = to_tegra_plane_state(plane->state); -	struct tegra_dc *dc = to_tegra_dc(plane->state->crtc); -	u32 value = CURSOR_CLIP_DISPLAY; +	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, +									   plane); +	struct tegra_plane_state *tegra_plane_state = to_tegra_plane_state(new_state); +	struct tegra_dc *dc = to_tegra_dc(new_state->crtc); +	struct tegra_drm *tegra = plane->dev->dev_private; +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT +	u64 dma_mask = *dc->dev->dma_mask; +#endif +	unsigned int x, y; +	u32 value = 0;  	/* rien ne va plus */ -	if (!plane->state->crtc || !plane->state->fb) +	if (!new_state->crtc || !new_state->fb)  		return; -	switch (plane->state->crtc_w) { +	/* +	 * Legacy display supports hardware clipping of the cursor, but +	 * nvdisplay relies on software to clip the cursor to the screen. +	 */ +	if (!dc->soc->has_nvdisplay) +		value |= CURSOR_CLIP_DISPLAY; + +	switch (new_state->crtc_w) {  	case 32:  		value |= CURSOR_SIZE_32x32;  		break; @@ -890,15 +916,15 @@ static void tegra_cursor_atomic_update(struct drm_plane *plane,  	default:  		WARN(1, "cursor size %ux%u not supported\n", -		     plane->state->crtc_w, plane->state->crtc_h); +		     new_state->crtc_w, new_state->crtc_h);  		return;  	} -	value |= (state->iova[0] >> 10) & 0x3fffff; +	value |= (tegra_plane_state->iova[0] >> 10) & 0x3fffff;  	tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR);  #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT -	value = (state->iova[0] >> 32) & 0x3; +	value = (tegra_plane_state->iova[0] >> 32) & (dma_mask >> 32);  	tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR_HI);  #endif @@ -910,21 +936,47 @@ static void tegra_cursor_atomic_update(struct drm_plane *plane,  	value = tegra_dc_readl(dc, DC_DISP_BLEND_CURSOR_CONTROL);  	value &= ~CURSOR_DST_BLEND_MASK;  	value &= ~CURSOR_SRC_BLEND_MASK; -	value |= CURSOR_MODE_NORMAL; + +	if (dc->soc->has_nvdisplay) +		value &= ~CURSOR_COMPOSITION_MODE_XOR; +	else +		value |= CURSOR_MODE_NORMAL; +  	value |= CURSOR_DST_BLEND_NEG_K1_TIMES_SRC;  	value |= CURSOR_SRC_BLEND_K1_TIMES_SRC;  	value |= CURSOR_ALPHA;  	tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL); +	/* nvdisplay relies on software for clipping */ +	if (dc->soc->has_nvdisplay) { +		struct drm_rect src; + +		x = new_state->dst.x1; +		y = new_state->dst.y1; + +		drm_rect_fp_to_int(&src, &new_state->src); + +		value = (src.y1 & tegra->vmask) << 16 | (src.x1 & tegra->hmask); +		tegra_dc_writel(dc, value, DC_DISP_PCALC_HEAD_SET_CROPPED_POINT_IN_CURSOR); + +		value = (drm_rect_height(&src) & tegra->vmask) << 16 | +			(drm_rect_width(&src) & tegra->hmask); +		tegra_dc_writel(dc, value, DC_DISP_PCALC_HEAD_SET_CROPPED_SIZE_IN_CURSOR); +	} else { +		x = new_state->crtc_x; +		y = new_state->crtc_y; +	} +  	/* position the cursor */ -	value = (plane->state->crtc_y & 0x3fff) << 16 | -		(plane->state->crtc_x & 0x3fff); +	value = ((y & tegra->vmask) << 16) | (x & tegra->hmask);  	tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);  }  static void tegra_cursor_atomic_disable(struct drm_plane *plane, -					struct drm_plane_state *old_state) +					struct drm_atomic_state *state)  { +	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, +									   plane);  	struct tegra_dc *dc;  	u32 value; @@ -970,8 +1022,13 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,  	plane->index = 6;  	plane->dc = dc; -	num_formats = ARRAY_SIZE(tegra_cursor_plane_formats); -	formats = tegra_cursor_plane_formats; +	if (!dc->soc->has_nvdisplay) { +		num_formats = ARRAY_SIZE(tegra_legacy_cursor_plane_formats); +		formats = tegra_legacy_cursor_plane_formats; +	} else { +		num_formats = ARRAY_SIZE(tegra_cursor_plane_formats); +		formats = tegra_cursor_plane_formats; +	}  	err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,  				       &tegra_plane_funcs, formats, @@ -1688,6 +1745,11 @@ static void tegra_dc_commit_state(struct tegra_dc *dc,  			dev_err(dc->dev,  				"failed to set clock rate to %lu Hz\n",  				state->pclk); + +		err = clk_set_rate(dc->clk, state->pclk); +		if (err < 0) +			dev_err(dc->dev, "failed to set clock %pC to %lu Hz: %d\n", +				dc->clk, state->pclk, err);  	}  	DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk), @@ -1698,11 +1760,6 @@ static void tegra_dc_commit_state(struct tegra_dc *dc,  		value = SHIFT_CLK_DIVIDER(state->div) | PIXEL_CLK_DIVIDER_PCD1;  		tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);  	} - -	err = clk_set_rate(dc->clk, state->pclk); -	if (err < 0) -		dev_err(dc->dev, "failed to set clock %pC to %lu Hz: %d\n", -			dc->clk, state->pclk, err);  }  static void tegra_dc_stop(struct tegra_dc *dc) @@ -2023,6 +2080,16 @@ static bool tegra_dc_has_window_groups(struct tegra_dc *dc)  	return false;  } +static int tegra_dc_early_init(struct host1x_client *client) +{ +	struct drm_device *drm = dev_get_drvdata(client->host); +	struct tegra_drm *tegra = drm->dev_private; + +	tegra->num_crtcs++; + +	return 0; +} +  static int tegra_dc_init(struct host1x_client *client)  {  	struct drm_device *drm = dev_get_drvdata(client->host); @@ -2034,6 +2101,12 @@ static int tegra_dc_init(struct host1x_client *client)  	int err;  	/* +	 * DC has been reset by now, so VBLANK syncpoint can be released +	 * for general use. +	 */ +	host1x_syncpt_release_vblank_reservation(client, 26 + dc->pipe); + +	/*  	 * XXX do not register DCs with no window groups because we cannot  	 * assign a primary plane to them, which in turn will cause KMS to  	 * crash. @@ -2099,6 +2172,12 @@ static int tegra_dc_init(struct host1x_client *client)  	if (dc->soc->pitch_align > tegra->pitch_align)  		tegra->pitch_align = dc->soc->pitch_align; +	/* track maximum resolution */ +	if (dc->soc->has_nvdisplay) +		drm->mode_config.max_width = drm->mode_config.max_height = 16384; +	else +		drm->mode_config.max_width = drm->mode_config.max_height = 4096; +  	err = tegra_dc_rgb_init(drm, dc);  	if (err < 0 && err != -ENODEV) {  		dev_err(dc->dev, "failed to initialize RGB output: %d\n", err); @@ -2129,7 +2208,7 @@ cleanup:  		drm_plane_cleanup(primary);  	host1x_client_iommu_detach(client); -	host1x_syncpt_free(dc->syncpt); +	host1x_syncpt_put(dc->syncpt);  	return err;  } @@ -2154,7 +2233,17 @@ static int tegra_dc_exit(struct host1x_client *client)  	}  	host1x_client_iommu_detach(client); -	host1x_syncpt_free(dc->syncpt); +	host1x_syncpt_put(dc->syncpt); + +	return 0; +} + +static int tegra_dc_late_exit(struct host1x_client *client) +{ +	struct drm_device *drm = dev_get_drvdata(client->host); +	struct tegra_drm *tegra = drm->dev_private; + +	tegra->num_crtcs--;  	return 0;  } @@ -2223,8 +2312,10 @@ put_rpm:  }  static const struct host1x_client_ops dc_client_ops = { +	.early_init = tegra_dc_early_init,  	.init = tegra_dc_init,  	.exit = tegra_dc_exit, +	.late_exit = tegra_dc_late_exit,  	.suspend = tegra_dc_runtime_suspend,  	.resume = tegra_dc_runtime_resume,  }; @@ -2234,6 +2325,7 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = {  	.supports_interlacing = false,  	.supports_cursor = false,  	.supports_block_linear = false, +	.supports_sector_layout = false,  	.has_legacy_blending = true,  	.pitch_align = 8,  	.has_powergate = false, @@ -2253,6 +2345,7 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = {  	.supports_interlacing = false,  	.supports_cursor = false,  	.supports_block_linear = false, +	.supports_sector_layout = false,  	.has_legacy_blending = true,  	.pitch_align = 8,  	.has_powergate = false, @@ -2272,6 +2365,7 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = {  	.supports_interlacing = false,  	.supports_cursor = false,  	.supports_block_linear = false, +	.supports_sector_layout = false,  	.has_legacy_blending = true,  	.pitch_align = 64,  	.has_powergate = true, @@ -2291,6 +2385,7 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = {  	.supports_interlacing = true,  	.supports_cursor = true,  	.supports_block_linear = true, +	.supports_sector_layout = false,  	.has_legacy_blending = false,  	.pitch_align = 64,  	.has_powergate = true, @@ -2310,6 +2405,7 @@ static const struct tegra_dc_soc_info tegra210_dc_soc_info = {  	.supports_interlacing = true,  	.supports_cursor = true,  	.supports_block_linear = true, +	.supports_sector_layout = false,  	.has_legacy_blending = false,  	.pitch_align = 64,  	.has_powergate = true, @@ -2363,6 +2459,7 @@ static const struct tegra_dc_soc_info tegra186_dc_soc_info = {  	.supports_interlacing = true,  	.supports_cursor = true,  	.supports_block_linear = true, +	.supports_sector_layout = false,  	.has_legacy_blending = false,  	.pitch_align = 64,  	.has_powergate = false, @@ -2411,6 +2508,7 @@ static const struct tegra_dc_soc_info tegra194_dc_soc_info = {  	.supports_interlacing = true,  	.supports_cursor = true,  	.supports_block_linear = true, +	.supports_sector_layout = true,  	.has_legacy_blending = false,  	.pitch_align = 64,  	.has_powergate = false, @@ -2501,22 +2599,18 @@ static int tegra_dc_couple(struct tegra_dc *dc)  	 * POWER_CONTROL registers during CRTC enabling.  	 */  	if (dc->soc->coupled_pm && dc->pipe == 1) { -		u32 flags = DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_CONSUMER; -		struct device_link *link; -		struct device *partner; +		struct device *companion; +		struct tegra_dc *parent; -		partner = driver_find_device(dc->dev->driver, NULL, NULL, -					     tegra_dc_match_by_pipe); -		if (!partner) +		companion = driver_find_device(dc->dev->driver, NULL, (const void *)0, +					       tegra_dc_match_by_pipe); +		if (!companion)  			return -EPROBE_DEFER; -		link = device_link_add(dc->dev, partner, flags); -		if (!link) { -			dev_err(dc->dev, "failed to link controllers\n"); -			return -EINVAL; -		} +		parent = dev_get_drvdata(companion); +		dc->client.parent = &parent->client; -		dev_dbg(dc->dev, "coupled to %s\n", dev_name(partner)); +		dev_dbg(dc->dev, "coupled to %s\n", dev_name(companion));  	}  	return 0; @@ -2524,9 +2618,16 @@ static int tegra_dc_couple(struct tegra_dc *dc)  static int tegra_dc_probe(struct platform_device *pdev)  { +	u64 dma_mask = dma_get_mask(pdev->dev.parent);  	struct tegra_dc *dc;  	int err; +	err = dma_coerce_mask_and_coherent(&pdev->dev, dma_mask); +	if (err < 0) { +		dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err); +		return err; +	} +  	dc = devm_kzalloc(&pdev->dev, sizeof(*dc), GFP_KERNEL);  	if (!dc)  		return -ENOMEM; | 
