diff options
Diffstat (limited to 'drivers/gpu/drm/vc4/vc4_plane.c')
| -rw-r--r-- | drivers/gpu/drm/vc4/vc4_plane.c | 145 | 
1 files changed, 118 insertions, 27 deletions
| diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index 75db62cbe468..d098337c10e9 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c @@ -258,6 +258,52 @@ static u32 vc4_get_scl_field(struct drm_plane_state *state, int plane)  	}  } +static int vc4_plane_margins_adj(struct drm_plane_state *pstate) +{ +	struct vc4_plane_state *vc4_pstate = to_vc4_plane_state(pstate); +	unsigned int left, right, top, bottom, adjhdisplay, adjvdisplay; +	struct drm_crtc_state *crtc_state; + +	crtc_state = drm_atomic_get_new_crtc_state(pstate->state, +						   pstate->crtc); + +	vc4_crtc_get_margins(crtc_state, &left, &right, &top, &bottom); +	if (!left && !right && !top && !bottom) +		return 0; + +	if (left + right >= crtc_state->mode.hdisplay || +	    top + bottom >= crtc_state->mode.vdisplay) +		return -EINVAL; + +	adjhdisplay = crtc_state->mode.hdisplay - (left + right); +	vc4_pstate->crtc_x = DIV_ROUND_CLOSEST(vc4_pstate->crtc_x * +					       adjhdisplay, +					       crtc_state->mode.hdisplay); +	vc4_pstate->crtc_x += left; +	if (vc4_pstate->crtc_x > crtc_state->mode.hdisplay - left) +		vc4_pstate->crtc_x = crtc_state->mode.hdisplay - left; + +	adjvdisplay = crtc_state->mode.vdisplay - (top + bottom); +	vc4_pstate->crtc_y = DIV_ROUND_CLOSEST(vc4_pstate->crtc_y * +					       adjvdisplay, +					       crtc_state->mode.vdisplay); +	vc4_pstate->crtc_y += top; +	if (vc4_pstate->crtc_y > crtc_state->mode.vdisplay - top) +		vc4_pstate->crtc_y = crtc_state->mode.vdisplay - top; + +	vc4_pstate->crtc_w = DIV_ROUND_CLOSEST(vc4_pstate->crtc_w * +					       adjhdisplay, +					       crtc_state->mode.hdisplay); +	vc4_pstate->crtc_h = DIV_ROUND_CLOSEST(vc4_pstate->crtc_h * +					       adjvdisplay, +					       crtc_state->mode.vdisplay); + +	if (!vc4_pstate->crtc_w || !vc4_pstate->crtc_h) +		return -EINVAL; + +	return 0; +} +  static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state)  {  	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); @@ -306,6 +352,10 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state)  	vc4_state->crtc_w = state->dst.x2 - state->dst.x1;  	vc4_state->crtc_h = state->dst.y2 - state->dst.y1; +	ret = vc4_plane_margins_adj(state); +	if (ret) +		return ret; +  	vc4_state->x_scaling[0] = vc4_get_scaling_mode(vc4_state->src_w[0],  						       vc4_state->crtc_w);  	vc4_state->y_scaling[0] = vc4_get_scaling_mode(vc4_state->src_h[0], @@ -492,8 +542,9 @@ static int vc4_plane_mode_set(struct drm_plane *plane,  	bool mix_plane_alpha;  	bool covers_screen;  	u32 scl0, scl1, pitch0; -	u32 tiling; +	u32 tiling, src_y;  	u32 hvs_format = format->hvs; +	unsigned int rotation;  	int ret, i;  	if (vc4_state->dlist_initialized) @@ -520,6 +571,16 @@ static int vc4_plane_mode_set(struct drm_plane *plane,  	h_subsample = drm_format_horz_chroma_subsampling(format->drm);  	v_subsample = drm_format_vert_chroma_subsampling(format->drm); +	rotation = drm_rotation_simplify(state->rotation, +					 DRM_MODE_ROTATE_0 | +					 DRM_MODE_REFLECT_X | +					 DRM_MODE_REFLECT_Y); + +	/* We must point to the last line when Y reflection is enabled. */ +	src_y = vc4_state->src_y; +	if (rotation & DRM_MODE_REFLECT_Y) +		src_y += vc4_state->src_h[0] - 1; +  	switch (base_format_mod) {  	case DRM_FORMAT_MOD_LINEAR:  		tiling = SCALER_CTL0_TILING_LINEAR; @@ -529,9 +590,10 @@ static int vc4_plane_mode_set(struct drm_plane *plane,  		 * out.  		 */  		for (i = 0; i < num_planes; i++) { -			vc4_state->offsets[i] += vc4_state->src_y / +			vc4_state->offsets[i] += src_y /  						 (i ? v_subsample : 1) *  						 fb->pitches[i]; +  			vc4_state->offsets[i] += vc4_state->src_x /  						 (i ? h_subsample : 1) *  						 fb->format->cpp[i]; @@ -557,22 +619,38 @@ static int vc4_plane_mode_set(struct drm_plane *plane,  		u32 tiles_w = fb->pitches[0] >> (tile_size_shift - tile_h_shift);  		u32 tiles_l = vc4_state->src_x >> tile_w_shift;  		u32 tiles_r = tiles_w - tiles_l; -		u32 tiles_t = vc4_state->src_y >> tile_h_shift; +		u32 tiles_t = src_y >> tile_h_shift;  		/* Intra-tile offsets, which modify the base address (the  		 * SCALER_PITCH0_TILE_Y_OFFSET tells HVS how to walk from that  		 * base address).  		 */ -		u32 tile_y = (vc4_state->src_y >> 4) & 1; -		u32 subtile_y = (vc4_state->src_y >> 2) & 3; -		u32 utile_y = vc4_state->src_y & 3; +		u32 tile_y = (src_y >> 4) & 1; +		u32 subtile_y = (src_y >> 2) & 3; +		u32 utile_y = src_y & 3;  		u32 x_off = vc4_state->src_x & tile_w_mask; -		u32 y_off = vc4_state->src_y & tile_h_mask; +		u32 y_off = src_y & tile_h_mask; + +		/* When Y reflection is requested we must set the +		 * SCALER_PITCH0_TILE_LINE_DIR flag to tell HVS that all lines +		 * after the initial one should be fetched in descending order, +		 * which makes sense since we start from the last line and go +		 * backward. +		 * Don't know why we need y_off = max_y_off - y_off, but it's +		 * definitely required (I guess it's also related to the "going +		 * backward" situation). +		 */ +		if (rotation & DRM_MODE_REFLECT_Y) { +			y_off = tile_h_mask - y_off; +			pitch0 = SCALER_PITCH0_TILE_LINE_DIR; +		} else { +			pitch0 = 0; +		}  		tiling = SCALER_CTL0_TILING_256B_OR_T; -		pitch0 = (VC4_SET_FIELD(x_off, SCALER_PITCH0_SINK_PIX) | -			  VC4_SET_FIELD(y_off, SCALER_PITCH0_TILE_Y_OFFSET) | -			  VC4_SET_FIELD(tiles_l, SCALER_PITCH0_TILE_WIDTH_L) | -			  VC4_SET_FIELD(tiles_r, SCALER_PITCH0_TILE_WIDTH_R)); +		pitch0 |= (VC4_SET_FIELD(x_off, SCALER_PITCH0_SINK_PIX) | +			   VC4_SET_FIELD(y_off, SCALER_PITCH0_TILE_Y_OFFSET) | +			   VC4_SET_FIELD(tiles_l, SCALER_PITCH0_TILE_WIDTH_L) | +			   VC4_SET_FIELD(tiles_r, SCALER_PITCH0_TILE_WIDTH_R));  		vc4_state->offsets[0] += tiles_t * (tiles_w << tile_size_shift);  		vc4_state->offsets[0] += subtile_y << 8;  		vc4_state->offsets[0] += utile_y << 4; @@ -595,31 +673,22 @@ static int vc4_plane_mode_set(struct drm_plane *plane,  	case DRM_FORMAT_MOD_BROADCOM_SAND128:  	case DRM_FORMAT_MOD_BROADCOM_SAND256: {  		uint32_t param = fourcc_mod_broadcom_param(fb->modifier); +		u32 tile_w, tile, x_off, pix_per_tile; -		/* Column-based NV12 or RGBA. -		 */ -		if (fb->format->num_planes > 1) { -			if (hvs_format != HVS_PIXEL_FORMAT_YCBCR_YUV420_2PLANE) { -				DRM_DEBUG_KMS("SAND format only valid for NV12/21"); -				return -EINVAL; -			} -			hvs_format = HVS_PIXEL_FORMAT_H264; -		} else { -			if (base_format_mod == DRM_FORMAT_MOD_BROADCOM_SAND256) { -				DRM_DEBUG_KMS("SAND256 format only valid for H.264"); -				return -EINVAL; -			} -		} +		hvs_format = HVS_PIXEL_FORMAT_H264;  		switch (base_format_mod) {  		case DRM_FORMAT_MOD_BROADCOM_SAND64:  			tiling = SCALER_CTL0_TILING_64B; +			tile_w = 64;  			break;  		case DRM_FORMAT_MOD_BROADCOM_SAND128:  			tiling = SCALER_CTL0_TILING_128B; +			tile_w = 128;  			break;  		case DRM_FORMAT_MOD_BROADCOM_SAND256:  			tiling = SCALER_CTL0_TILING_256B_OR_T; +			tile_w = 256;  			break;  		default:  			break; @@ -630,6 +699,23 @@ static int vc4_plane_mode_set(struct drm_plane *plane,  			return -EINVAL;  		} +		pix_per_tile = tile_w / fb->format->cpp[0]; +		tile = vc4_state->src_x / pix_per_tile; +		x_off = vc4_state->src_x % pix_per_tile; + +		/* Adjust the base pointer to the first pixel to be scanned +		 * out. +		 */ +		for (i = 0; i < num_planes; i++) { +			vc4_state->offsets[i] += param * tile_w * tile; +			vc4_state->offsets[i] += src_y / +						 (i ? v_subsample : 1) * +						 tile_w; +			vc4_state->offsets[i] += x_off / +						 (i ? h_subsample : 1) * +						 fb->format->cpp[i]; +		} +  		pitch0 = VC4_SET_FIELD(param, SCALER_TILE_HEIGHT);  		break;  	} @@ -643,6 +729,8 @@ static int vc4_plane_mode_set(struct drm_plane *plane,  	/* Control word */  	vc4_dlist_write(vc4_state,  			SCALER_CTL0_VALID | +			(rotation & DRM_MODE_REFLECT_X ? SCALER_CTL0_HFLIP : 0) | +			(rotation & DRM_MODE_REFLECT_Y ? SCALER_CTL0_VFLIP : 0) |  			VC4_SET_FIELD(SCALER_CTL0_RGBA_EXPAND_ROUND, SCALER_CTL0_RGBA_EXPAND) |  			(format->pixel_order << SCALER_CTL0_ORDER_SHIFT) |  			(hvs_format << SCALER_CTL0_PIXEL_FORMAT_SHIFT) | @@ -1050,8 +1138,6 @@ static bool vc4_format_mod_supported(struct drm_plane *plane,  		switch (fourcc_mod_broadcom_mod(modifier)) {  		case DRM_FORMAT_MOD_LINEAR:  		case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED: -		case DRM_FORMAT_MOD_BROADCOM_SAND64: -		case DRM_FORMAT_MOD_BROADCOM_SAND128:  			return true;  		default:  			return false; @@ -1123,6 +1209,11 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev,  	drm_plane_helper_add(plane, &vc4_plane_helper_funcs);  	drm_plane_create_alpha_property(plane); +	drm_plane_create_rotation_property(plane, DRM_MODE_ROTATE_0, +					   DRM_MODE_ROTATE_0 | +					   DRM_MODE_ROTATE_180 | +					   DRM_MODE_REFLECT_X | +					   DRM_MODE_REFLECT_Y);  	return plane;  } | 
