brintos

brintos / linux-shallow public Read only

0
0
Text · 30.4 KiB · 62de248 Raw
1048 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2014-2015 The Linux Foundation. All rights reserved.4 * Copyright (C) 2013 Red Hat5 * Author: Rob Clark <robdclark@gmail.com>6 */7 8#include <drm/drm_atomic.h>9#include <drm/drm_blend.h>10#include <drm/drm_damage_helper.h>11#include <drm/drm_fourcc.h>12#include <drm/drm_framebuffer.h>13#include <drm/drm_gem_atomic_helper.h>14#include <drm/drm_print.h>15 16#include "mdp5_kms.h"17 18struct mdp5_plane {19	struct drm_plane base;20};21#define to_mdp5_plane(x) container_of(x, struct mdp5_plane, base)22 23static int mdp5_plane_mode_set(struct drm_plane *plane,24		struct drm_crtc *crtc, struct drm_framebuffer *fb,25		struct drm_rect *src, struct drm_rect *dest);26 27static struct mdp5_kms *get_kms(struct drm_plane *plane)28{29	struct msm_drm_private *priv = plane->dev->dev_private;30	return to_mdp5_kms(to_mdp_kms(priv->kms));31}32 33static bool plane_enabled(struct drm_plane_state *state)34{35	return state->visible;36}37 38/* helper to install properties which are common to planes and crtcs */39static void mdp5_plane_install_properties(struct drm_plane *plane,40		struct drm_mode_object *obj)41{42	unsigned int zpos;43 44	drm_plane_create_rotation_property(plane,45					   DRM_MODE_ROTATE_0,46					   DRM_MODE_ROTATE_0 |47					   DRM_MODE_ROTATE_180 |48					   DRM_MODE_REFLECT_X |49					   DRM_MODE_REFLECT_Y);50	drm_plane_create_alpha_property(plane);51	drm_plane_create_blend_mode_property(plane,52			BIT(DRM_MODE_BLEND_PIXEL_NONE) |53			BIT(DRM_MODE_BLEND_PREMULTI) |54			BIT(DRM_MODE_BLEND_COVERAGE));55 56	if (plane->type == DRM_PLANE_TYPE_PRIMARY)57		zpos = STAGE_BASE;58	else59		zpos = STAGE0 + drm_plane_index(plane);60	drm_plane_create_zpos_property(plane, zpos, 1, 255);61}62 63static void64mdp5_plane_atomic_print_state(struct drm_printer *p,65		const struct drm_plane_state *state)66{67	struct mdp5_plane_state *pstate = to_mdp5_plane_state(state);68	struct mdp5_kms *mdp5_kms = get_kms(state->plane);69 70	drm_printf(p, "\thwpipe=%s\n", pstate->hwpipe ?71			pstate->hwpipe->name : "(null)");72	if (mdp5_kms->caps & MDP_CAP_SRC_SPLIT)73		drm_printf(p, "\tright-hwpipe=%s\n",74			   pstate->r_hwpipe ? pstate->r_hwpipe->name :75					      "(null)");76	drm_printf(p, "\tblend_mode=%u\n", pstate->base.pixel_blend_mode);77	drm_printf(p, "\tzpos=%u\n", pstate->base.zpos);78	drm_printf(p, "\tnormalized_zpos=%u\n", pstate->base.normalized_zpos);79	drm_printf(p, "\talpha=%u\n", pstate->base.alpha);80	drm_printf(p, "\tstage=%s\n", stage2name(pstate->stage));81}82 83static void mdp5_plane_reset(struct drm_plane *plane)84{85	struct mdp5_plane_state *mdp5_state;86 87	if (plane->state)88		__drm_atomic_helper_plane_destroy_state(plane->state);89 90	kfree(to_mdp5_plane_state(plane->state));91	plane->state = NULL;92	mdp5_state = kzalloc(sizeof(*mdp5_state), GFP_KERNEL);93	if (!mdp5_state)94		return;95	__drm_atomic_helper_plane_reset(plane, &mdp5_state->base);96}97 98static struct drm_plane_state *99mdp5_plane_duplicate_state(struct drm_plane *plane)100{101	struct mdp5_plane_state *mdp5_state;102 103	if (WARN_ON(!plane->state))104		return NULL;105 106	mdp5_state = kmemdup(to_mdp5_plane_state(plane->state),107			sizeof(*mdp5_state), GFP_KERNEL);108	if (!mdp5_state)109		return NULL;110 111	__drm_atomic_helper_plane_duplicate_state(plane, &mdp5_state->base);112 113	return &mdp5_state->base;114}115 116static void mdp5_plane_destroy_state(struct drm_plane *plane,117		struct drm_plane_state *state)118{119	struct mdp5_plane_state *pstate = to_mdp5_plane_state(state);120 121	__drm_atomic_helper_plane_destroy_state(state);122 123	kfree(pstate);124}125 126static const struct drm_plane_funcs mdp5_plane_funcs = {127		.update_plane = drm_atomic_helper_update_plane,128		.disable_plane = drm_atomic_helper_disable_plane,129		.reset = mdp5_plane_reset,130		.atomic_duplicate_state = mdp5_plane_duplicate_state,131		.atomic_destroy_state = mdp5_plane_destroy_state,132		.atomic_print_state = mdp5_plane_atomic_print_state,133};134 135static int mdp5_plane_prepare_fb(struct drm_plane *plane,136				 struct drm_plane_state *new_state)137{138	struct msm_drm_private *priv = plane->dev->dev_private;139	struct msm_kms *kms = priv->kms;140	bool needs_dirtyfb = to_mdp5_plane_state(new_state)->needs_dirtyfb;141 142	if (!new_state->fb)143		return 0;144 145	drm_gem_plane_helper_prepare_fb(plane, new_state);146 147	return msm_framebuffer_prepare(new_state->fb, kms->aspace, needs_dirtyfb);148}149 150static void mdp5_plane_cleanup_fb(struct drm_plane *plane,151				  struct drm_plane_state *old_state)152{153	struct mdp5_kms *mdp5_kms = get_kms(plane);154	struct msm_kms *kms = &mdp5_kms->base.base;155	struct drm_framebuffer *fb = old_state->fb;156	bool needed_dirtyfb = to_mdp5_plane_state(old_state)->needs_dirtyfb;157 158	if (!fb)159		return;160 161	DBG("%s: cleanup: FB[%u]", plane->name, fb->base.id);162	msm_framebuffer_cleanup(fb, kms->aspace, needed_dirtyfb);163}164 165static int mdp5_plane_atomic_check_with_state(struct drm_crtc_state *crtc_state,166					      struct drm_plane_state *state)167{168	struct mdp5_plane_state *mdp5_state = to_mdp5_plane_state(state);169	struct drm_plane *plane = state->plane;170	struct drm_plane_state *old_state = plane->state;171	struct mdp5_cfg *config = mdp5_cfg_get_config(get_kms(plane)->cfg);172	bool new_hwpipe = false;173	bool need_right_hwpipe = false;174	uint32_t max_width, max_height;175	bool out_of_bounds = false;176	uint32_t caps = 0;177	int min_scale, max_scale;178	int ret;179 180	DBG("%s: check (%d -> %d)", plane->name,181			plane_enabled(old_state), plane_enabled(state));182 183	max_width = config->hw->lm.max_width << 16;184	max_height = config->hw->lm.max_height << 16;185 186	/* Make sure source dimensions are within bounds. */187	if (state->src_h > max_height)188		out_of_bounds = true;189 190	if (state->src_w > max_width) {191		/* If source split is supported, we can go up to 2x192		 * the max LM width, but we'd need to stage another193		 * hwpipe to the right LM. So, the drm_plane would194		 * consist of 2 hwpipes.195		 */196		if (config->hw->mdp.caps & MDP_CAP_SRC_SPLIT &&197		    (state->src_w <= 2 * max_width))198			need_right_hwpipe = true;199		else200			out_of_bounds = true;201	}202 203	if (out_of_bounds) {204		struct drm_rect src = drm_plane_state_src(state);205		DBG("Invalid source size "DRM_RECT_FP_FMT,206				DRM_RECT_FP_ARG(&src));207		return -ERANGE;208	}209 210	min_scale = FRAC_16_16(1, 8);211	max_scale = FRAC_16_16(8, 1);212 213	ret = drm_atomic_helper_check_plane_state(state, crtc_state,214						  min_scale, max_scale,215						  true, true);216	if (ret)217		return ret;218 219	if (plane_enabled(state)) {220		unsigned int rotation;221		const struct msm_format *format;222		struct mdp5_kms *mdp5_kms = get_kms(plane);223		uint32_t blkcfg = 0;224 225		format = msm_framebuffer_format(state->fb);226		if (MSM_FORMAT_IS_YUV(format))227			caps |= MDP_PIPE_CAP_SCALE | MDP_PIPE_CAP_CSC;228 229		if (((state->src_w >> 16) != state->crtc_w) ||230				((state->src_h >> 16) != state->crtc_h))231			caps |= MDP_PIPE_CAP_SCALE;232 233		rotation = drm_rotation_simplify(state->rotation,234						 DRM_MODE_ROTATE_0 |235						 DRM_MODE_REFLECT_X |236						 DRM_MODE_REFLECT_Y);237 238		if (rotation & DRM_MODE_REFLECT_X)239			caps |= MDP_PIPE_CAP_HFLIP;240 241		if (rotation & DRM_MODE_REFLECT_Y)242			caps |= MDP_PIPE_CAP_VFLIP;243 244		if (plane->type == DRM_PLANE_TYPE_CURSOR)245			caps |= MDP_PIPE_CAP_CURSOR;246 247		/* (re)allocate hw pipe if we don't have one or caps-mismatch: */248		if (!mdp5_state->hwpipe || (caps & ~mdp5_state->hwpipe->caps))249			new_hwpipe = true;250 251		/*252		 * (re)allocte hw pipe if we're either requesting for 2 hw pipes253		 * or we're switching from 2 hw pipes to 1 hw pipe because the254		 * new src_w can be supported by 1 hw pipe itself.255		 */256		if ((need_right_hwpipe && !mdp5_state->r_hwpipe) ||257		    (!need_right_hwpipe && mdp5_state->r_hwpipe))258			new_hwpipe = true;259 260		if (mdp5_kms->smp) {261			const struct msm_format *format =262				msm_framebuffer_format(state->fb);263 264			blkcfg = mdp5_smp_calculate(mdp5_kms->smp, format,265					state->src_w >> 16, false);266 267			if (mdp5_state->hwpipe && (mdp5_state->hwpipe->blkcfg != blkcfg))268				new_hwpipe = true;269		}270 271		/* (re)assign hwpipe if needed, otherwise keep old one: */272		if (new_hwpipe) {273			/* TODO maybe we want to re-assign hwpipe sometimes274			 * in cases when we no-longer need some caps to make275			 * it available for other planes?276			 */277			struct mdp5_hw_pipe *old_hwpipe = mdp5_state->hwpipe;278			struct mdp5_hw_pipe *old_right_hwpipe =279							  mdp5_state->r_hwpipe;280			struct mdp5_hw_pipe *new_hwpipe = NULL;281			struct mdp5_hw_pipe *new_right_hwpipe = NULL;282 283			ret = mdp5_pipe_assign(state->state, plane, caps,284					       blkcfg, &new_hwpipe,285					       need_right_hwpipe ?286					       &new_right_hwpipe : NULL);287			if (ret) {288				DBG("%s: failed to assign hwpipe(s)!",289				    plane->name);290				return ret;291			}292 293			mdp5_state->hwpipe = new_hwpipe;294			if (need_right_hwpipe)295				mdp5_state->r_hwpipe = new_right_hwpipe;296			else297				/*298				 * set it to NULL so that the driver knows we299				 * don't have a right hwpipe when committing a300				 * new state301				 */302				mdp5_state->r_hwpipe = NULL;303 304 305			ret = mdp5_pipe_release(state->state, old_hwpipe);306			if (ret)307				return ret;308 309			ret = mdp5_pipe_release(state->state, old_right_hwpipe);310			if (ret)311				return ret;312 313		}314	} else {315		ret = mdp5_pipe_release(state->state, mdp5_state->hwpipe);316		if (ret)317			return ret;318 319		ret = mdp5_pipe_release(state->state, mdp5_state->r_hwpipe);320		if (ret)321			return ret;322 323		mdp5_state->hwpipe = mdp5_state->r_hwpipe = NULL;324	}325 326	return 0;327}328 329static int mdp5_plane_atomic_check(struct drm_plane *plane,330				   struct drm_atomic_state *state)331{332	struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state,333										 plane);334	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,335										 plane);336	struct drm_crtc *crtc;337	struct drm_crtc_state *crtc_state;338 339	crtc = new_plane_state->crtc ? new_plane_state->crtc : old_plane_state->crtc;340	if (!crtc)341		return 0;342 343	crtc_state = drm_atomic_get_existing_crtc_state(state,344							crtc);345	if (WARN_ON(!crtc_state))346		return -EINVAL;347 348	return mdp5_plane_atomic_check_with_state(crtc_state, new_plane_state);349}350 351static void mdp5_plane_atomic_update(struct drm_plane *plane,352				     struct drm_atomic_state *state)353{354	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,355									   plane);356 357	DBG("%s: update", plane->name);358 359	if (plane_enabled(new_state)) {360		int ret;361 362		ret = mdp5_plane_mode_set(plane,363				new_state->crtc, new_state->fb,364				&new_state->src, &new_state->dst);365		/* atomic_check should have ensured that this doesn't fail */366		WARN_ON(ret < 0);367	}368}369 370static int mdp5_plane_atomic_async_check(struct drm_plane *plane,371					 struct drm_atomic_state *state)372{373	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,374										 plane);375	struct mdp5_plane_state *mdp5_state = to_mdp5_plane_state(new_plane_state);376	struct drm_crtc_state *crtc_state;377	int min_scale, max_scale;378	int ret;379 380	crtc_state = drm_atomic_get_existing_crtc_state(state,381							new_plane_state->crtc);382	if (WARN_ON(!crtc_state))383		return -EINVAL;384 385	if (!crtc_state->active)386		return -EINVAL;387 388	/* don't use fast path if we don't have a hwpipe allocated yet */389	if (!mdp5_state->hwpipe)390		return -EINVAL;391 392	/* only allow changing of position(crtc x/y or src x/y) in fast path */393	if (plane->state->crtc != new_plane_state->crtc ||394	    plane->state->src_w != new_plane_state->src_w ||395	    plane->state->src_h != new_plane_state->src_h ||396	    plane->state->crtc_w != new_plane_state->crtc_w ||397	    plane->state->crtc_h != new_plane_state->crtc_h ||398	    !plane->state->fb ||399	    plane->state->fb != new_plane_state->fb)400		return -EINVAL;401 402	min_scale = FRAC_16_16(1, 8);403	max_scale = FRAC_16_16(8, 1);404 405	ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,406						  min_scale, max_scale,407						  true, true);408	if (ret)409		return ret;410 411	/*412	 * if the visibility of the plane changes (i.e, if the cursor is413	 * clipped out completely, we can't take the async path because414	 * we need to stage/unstage the plane from the Layer Mixer(s). We415	 * also assign/unassign the hwpipe(s) tied to the plane. We avoid416	 * taking the fast path for both these reasons.417	 */418	if (new_plane_state->visible != plane->state->visible)419		return -EINVAL;420 421	return 0;422}423 424static void mdp5_plane_atomic_async_update(struct drm_plane *plane,425					   struct drm_atomic_state *state)426{427	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,428									   plane);429	struct drm_framebuffer *old_fb = plane->state->fb;430 431	plane->state->src_x = new_state->src_x;432	plane->state->src_y = new_state->src_y;433	plane->state->crtc_x = new_state->crtc_x;434	plane->state->crtc_y = new_state->crtc_y;435 436	if (plane_enabled(new_state)) {437		struct mdp5_ctl *ctl;438		struct mdp5_pipeline *pipeline =439					mdp5_crtc_get_pipeline(new_state->crtc);440		int ret;441 442		ret = mdp5_plane_mode_set(plane, new_state->crtc, new_state->fb,443				&new_state->src, &new_state->dst);444		WARN_ON(ret < 0);445 446		ctl = mdp5_crtc_get_ctl(new_state->crtc);447 448		mdp5_ctl_commit(ctl, pipeline, mdp5_plane_get_flush(plane), true);449	}450 451	*to_mdp5_plane_state(plane->state) =452		*to_mdp5_plane_state(new_state);453 454	new_state->fb = old_fb;455}456 457static const struct drm_plane_helper_funcs mdp5_plane_helper_funcs = {458		.prepare_fb = mdp5_plane_prepare_fb,459		.cleanup_fb = mdp5_plane_cleanup_fb,460		.atomic_check = mdp5_plane_atomic_check,461		.atomic_update = mdp5_plane_atomic_update,462		.atomic_async_check = mdp5_plane_atomic_async_check,463		.atomic_async_update = mdp5_plane_atomic_async_update,464};465 466static void set_scanout_locked(struct mdp5_kms *mdp5_kms,467			       enum mdp5_pipe pipe,468			       struct drm_framebuffer *fb)469{470	struct msm_kms *kms = &mdp5_kms->base.base;471 472	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_A(pipe),473			MDP5_PIPE_SRC_STRIDE_A_P0(fb->pitches[0]) |474			MDP5_PIPE_SRC_STRIDE_A_P1(fb->pitches[1]));475 476	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_B(pipe),477			MDP5_PIPE_SRC_STRIDE_B_P2(fb->pitches[2]) |478			MDP5_PIPE_SRC_STRIDE_B_P3(fb->pitches[3]));479 480	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC0_ADDR(pipe),481			msm_framebuffer_iova(fb, kms->aspace, 0));482	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC1_ADDR(pipe),483			msm_framebuffer_iova(fb, kms->aspace, 1));484	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC2_ADDR(pipe),485			msm_framebuffer_iova(fb, kms->aspace, 2));486	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC3_ADDR(pipe),487			msm_framebuffer_iova(fb, kms->aspace, 3));488}489 490/* Note: mdp5_plane->pipe_lock must be locked */491static void csc_disable(struct mdp5_kms *mdp5_kms, enum mdp5_pipe pipe)492{493	uint32_t value = mdp5_read(mdp5_kms, REG_MDP5_PIPE_OP_MODE(pipe)) &494			 ~MDP5_PIPE_OP_MODE_CSC_1_EN;495 496	mdp5_write(mdp5_kms, REG_MDP5_PIPE_OP_MODE(pipe), value);497}498 499/* Note: mdp5_plane->pipe_lock must be locked */500static void csc_enable(struct mdp5_kms *mdp5_kms, enum mdp5_pipe pipe,501		struct csc_cfg *csc)502{503	uint32_t  i, mode = 0; /* RGB, no CSC */504	uint32_t *matrix;505 506	if (unlikely(!csc))507		return;508 509	if ((csc->type == CSC_YUV2RGB) || (CSC_YUV2YUV == csc->type))510		mode |= MDP5_PIPE_OP_MODE_CSC_SRC_DATA_FORMAT(DATA_FORMAT_YUV);511	if ((csc->type == CSC_RGB2YUV) || (CSC_YUV2YUV == csc->type))512		mode |= MDP5_PIPE_OP_MODE_CSC_DST_DATA_FORMAT(DATA_FORMAT_YUV);513	mode |= MDP5_PIPE_OP_MODE_CSC_1_EN;514	mdp5_write(mdp5_kms, REG_MDP5_PIPE_OP_MODE(pipe), mode);515 516	matrix = csc->matrix;517	mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_0(pipe),518			MDP5_PIPE_CSC_1_MATRIX_COEFF_0_COEFF_11(matrix[0]) |519			MDP5_PIPE_CSC_1_MATRIX_COEFF_0_COEFF_12(matrix[1]));520	mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_1(pipe),521			MDP5_PIPE_CSC_1_MATRIX_COEFF_1_COEFF_13(matrix[2]) |522			MDP5_PIPE_CSC_1_MATRIX_COEFF_1_COEFF_21(matrix[3]));523	mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_2(pipe),524			MDP5_PIPE_CSC_1_MATRIX_COEFF_2_COEFF_22(matrix[4]) |525			MDP5_PIPE_CSC_1_MATRIX_COEFF_2_COEFF_23(matrix[5]));526	mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_3(pipe),527			MDP5_PIPE_CSC_1_MATRIX_COEFF_3_COEFF_31(matrix[6]) |528			MDP5_PIPE_CSC_1_MATRIX_COEFF_3_COEFF_32(matrix[7]));529	mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_4(pipe),530			MDP5_PIPE_CSC_1_MATRIX_COEFF_4_COEFF_33(matrix[8]));531 532	for (i = 0; i < ARRAY_SIZE(csc->pre_bias); i++) {533		uint32_t *pre_clamp = csc->pre_clamp;534		uint32_t *post_clamp = csc->post_clamp;535 536		mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_PRE_CLAMP(pipe, i),537			MDP5_PIPE_CSC_1_PRE_CLAMP_REG_HIGH(pre_clamp[2*i+1]) |538			MDP5_PIPE_CSC_1_PRE_CLAMP_REG_LOW(pre_clamp[2*i]));539 540		mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_POST_CLAMP(pipe, i),541			MDP5_PIPE_CSC_1_POST_CLAMP_REG_HIGH(post_clamp[2*i+1]) |542			MDP5_PIPE_CSC_1_POST_CLAMP_REG_LOW(post_clamp[2*i]));543 544		mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_PRE_BIAS(pipe, i),545			MDP5_PIPE_CSC_1_PRE_BIAS_REG_VALUE(csc->pre_bias[i]));546 547		mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_POST_BIAS(pipe, i),548			MDP5_PIPE_CSC_1_POST_BIAS_REG_VALUE(csc->post_bias[i]));549	}550}551 552#define PHASE_STEP_SHIFT	21553#define DOWN_SCALE_RATIO_MAX	32	/* 2^(26-21) */554 555static int calc_phase_step(uint32_t src, uint32_t dst, uint32_t *out_phase)556{557	uint32_t unit;558 559	if (src == 0 || dst == 0)560		return -EINVAL;561 562	/*563	 * PHASE_STEP_X/Y is coded on 26 bits (25:0),564	 * where 2^21 represents the unity "1" in fixed-point hardware design.565	 * This leaves 5 bits for the integer part (downscale case):566	 *	-> maximum downscale ratio = 0b1_1111 = 31567	 */568	if (src > (dst * DOWN_SCALE_RATIO_MAX))569		return -EOVERFLOW;570 571	unit = 1 << PHASE_STEP_SHIFT;572	*out_phase = mult_frac(unit, src, dst);573 574	return 0;575}576 577static int calc_scalex_steps(struct drm_plane *plane,578		uint32_t pixel_format, uint32_t src, uint32_t dest,579		uint32_t phasex_steps[COMP_MAX])580{581	const struct drm_format_info *info = drm_format_info(pixel_format);582	struct mdp5_kms *mdp5_kms = get_kms(plane);583	struct device *dev = mdp5_kms->dev->dev;584	uint32_t phasex_step;585	int ret;586 587	ret = calc_phase_step(src, dest, &phasex_step);588	if (ret) {589		DRM_DEV_ERROR(dev, "X scaling (%d->%d) failed: %d\n", src, dest, ret);590		return ret;591	}592 593	phasex_steps[COMP_0]   = phasex_step;594	phasex_steps[COMP_3]   = phasex_step;595	phasex_steps[COMP_1_2] = phasex_step / info->hsub;596 597	return 0;598}599 600static int calc_scaley_steps(struct drm_plane *plane,601		uint32_t pixel_format, uint32_t src, uint32_t dest,602		uint32_t phasey_steps[COMP_MAX])603{604	const struct drm_format_info *info = drm_format_info(pixel_format);605	struct mdp5_kms *mdp5_kms = get_kms(plane);606	struct device *dev = mdp5_kms->dev->dev;607	uint32_t phasey_step;608	int ret;609 610	ret = calc_phase_step(src, dest, &phasey_step);611	if (ret) {612		DRM_DEV_ERROR(dev, "Y scaling (%d->%d) failed: %d\n", src, dest, ret);613		return ret;614	}615 616	phasey_steps[COMP_0]   = phasey_step;617	phasey_steps[COMP_3]   = phasey_step;618	phasey_steps[COMP_1_2] = phasey_step / info->vsub;619 620	return 0;621}622 623static uint32_t get_scale_config(const struct msm_format *format,624		uint32_t src, uint32_t dst, bool horz)625{626	const struct drm_format_info *info = drm_format_info(format->pixel_format);627	bool yuv = MSM_FORMAT_IS_YUV(format);628	bool scaling = yuv ? true : (src != dst);629	uint32_t sub;630	uint32_t ya_filter, uv_filter;631 632	if (!scaling)633		return 0;634 635	if (yuv) {636		sub = horz ? info->hsub : info->vsub;637		uv_filter = ((src / sub) <= dst) ?638				   SCALE_FILTER_BIL : SCALE_FILTER_PCMN;639	}640	ya_filter = (src <= dst) ? SCALE_FILTER_BIL : SCALE_FILTER_PCMN;641 642	if (horz)643		return  MDP5_PIPE_SCALE_CONFIG_SCALEX_EN |644			MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_0(ya_filter) |645			MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_3(ya_filter) |646			COND(yuv, MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_1_2(uv_filter));647	else648		return  MDP5_PIPE_SCALE_CONFIG_SCALEY_EN |649			MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_0(ya_filter) |650			MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_3(ya_filter) |651			COND(yuv, MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_1_2(uv_filter));652}653 654static void calc_pixel_ext(const struct msm_format *format,655		uint32_t src, uint32_t dst, uint32_t phase_step[2],656		int pix_ext_edge1[COMP_MAX], int pix_ext_edge2[COMP_MAX],657		bool horz)658{659	bool scaling = MSM_FORMAT_IS_YUV(format) ? true : (src != dst);660	int i;661 662	/*663	 * Note:664	 * We assume here that:665	 *     1. PCMN filter is used for downscale666	 *     2. bilinear filter is used for upscale667	 *     3. we are in a single pipe configuration668	 */669 670	for (i = 0; i < COMP_MAX; i++) {671		pix_ext_edge1[i] = 0;672		pix_ext_edge2[i] = scaling ? 1 : 0;673	}674}675 676static void mdp5_write_pixel_ext(struct mdp5_kms *mdp5_kms, enum mdp5_pipe pipe,677	const struct msm_format *format,678	uint32_t src_w, int pe_left[COMP_MAX], int pe_right[COMP_MAX],679	uint32_t src_h, int pe_top[COMP_MAX], int pe_bottom[COMP_MAX])680{681	const struct drm_format_info *info = drm_format_info(format->pixel_format);682	uint32_t lr, tb, req;683	int i;684 685	for (i = 0; i < COMP_MAX; i++) {686		uint32_t roi_w = src_w;687		uint32_t roi_h = src_h;688 689		if (MSM_FORMAT_IS_YUV(format) && i == COMP_1_2) {690			roi_w /= info->hsub;691			roi_h /= info->vsub;692		}693 694		lr  = (pe_left[i] >= 0) ?695			MDP5_PIPE_SW_PIX_EXT_LR_LEFT_RPT(pe_left[i]) :696			MDP5_PIPE_SW_PIX_EXT_LR_LEFT_OVF(pe_left[i]);697 698		lr |= (pe_right[i] >= 0) ?699			MDP5_PIPE_SW_PIX_EXT_LR_RIGHT_RPT(pe_right[i]) :700			MDP5_PIPE_SW_PIX_EXT_LR_RIGHT_OVF(pe_right[i]);701 702		tb  = (pe_top[i] >= 0) ?703			MDP5_PIPE_SW_PIX_EXT_TB_TOP_RPT(pe_top[i]) :704			MDP5_PIPE_SW_PIX_EXT_TB_TOP_OVF(pe_top[i]);705 706		tb |= (pe_bottom[i] >= 0) ?707			MDP5_PIPE_SW_PIX_EXT_TB_BOTTOM_RPT(pe_bottom[i]) :708			MDP5_PIPE_SW_PIX_EXT_TB_BOTTOM_OVF(pe_bottom[i]);709 710		req  = MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS_LEFT_RIGHT(roi_w +711				pe_left[i] + pe_right[i]);712 713		req |= MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS_TOP_BOTTOM(roi_h +714				pe_top[i] + pe_bottom[i]);715 716		mdp5_write(mdp5_kms, REG_MDP5_PIPE_SW_PIX_EXT_LR(pipe, i), lr);717		mdp5_write(mdp5_kms, REG_MDP5_PIPE_SW_PIX_EXT_TB(pipe, i), tb);718		mdp5_write(mdp5_kms, REG_MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS(pipe, i), req);719 720		DBG("comp-%d (L/R): rpt=%d/%d, ovf=%d/%d, req=%d", i,721			FIELD(lr,  MDP5_PIPE_SW_PIX_EXT_LR_LEFT_RPT),722			FIELD(lr,  MDP5_PIPE_SW_PIX_EXT_LR_RIGHT_RPT),723			FIELD(lr,  MDP5_PIPE_SW_PIX_EXT_LR_LEFT_OVF),724			FIELD(lr,  MDP5_PIPE_SW_PIX_EXT_LR_RIGHT_OVF),725			FIELD(req, MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS_LEFT_RIGHT));726 727		DBG("comp-%d (T/B): rpt=%d/%d, ovf=%d/%d, req=%d", i,728			FIELD(tb,  MDP5_PIPE_SW_PIX_EXT_TB_TOP_RPT),729			FIELD(tb,  MDP5_PIPE_SW_PIX_EXT_TB_BOTTOM_RPT),730			FIELD(tb,  MDP5_PIPE_SW_PIX_EXT_TB_TOP_OVF),731			FIELD(tb,  MDP5_PIPE_SW_PIX_EXT_TB_BOTTOM_OVF),732			FIELD(req, MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS_TOP_BOTTOM));733	}734}735 736struct pixel_ext {737	int left[COMP_MAX];738	int right[COMP_MAX];739	int top[COMP_MAX];740	int bottom[COMP_MAX];741};742 743struct phase_step {744	u32 x[COMP_MAX];745	u32 y[COMP_MAX];746};747 748static void mdp5_hwpipe_mode_set(struct mdp5_kms *mdp5_kms,749				 struct mdp5_hw_pipe *hwpipe,750				 struct drm_framebuffer *fb,751				 struct phase_step *step,752				 struct pixel_ext *pe,753				 u32 scale_config, u32 hdecm, u32 vdecm,754				 bool hflip, bool vflip,755				 int crtc_x, int crtc_y,756				 unsigned int crtc_w, unsigned int crtc_h,757				 u32 src_img_w, u32 src_img_h,758				 u32 src_x, u32 src_y,759				 u32 src_w, u32 src_h)760{761	enum mdp5_pipe pipe = hwpipe->pipe;762	bool has_pe = hwpipe->caps & MDP_PIPE_CAP_SW_PIX_EXT;763	const struct msm_format *format =764			msm_framebuffer_format(fb);765 766	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_IMG_SIZE(pipe),767			MDP5_PIPE_SRC_IMG_SIZE_WIDTH(src_img_w) |768			MDP5_PIPE_SRC_IMG_SIZE_HEIGHT(src_img_h));769 770	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_SIZE(pipe),771			MDP5_PIPE_SRC_SIZE_WIDTH(src_w) |772			MDP5_PIPE_SRC_SIZE_HEIGHT(src_h));773 774	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_XY(pipe),775			MDP5_PIPE_SRC_XY_X(src_x) |776			MDP5_PIPE_SRC_XY_Y(src_y));777 778	mdp5_write(mdp5_kms, REG_MDP5_PIPE_OUT_SIZE(pipe),779			MDP5_PIPE_OUT_SIZE_WIDTH(crtc_w) |780			MDP5_PIPE_OUT_SIZE_HEIGHT(crtc_h));781 782	mdp5_write(mdp5_kms, REG_MDP5_PIPE_OUT_XY(pipe),783			MDP5_PIPE_OUT_XY_X(crtc_x) |784			MDP5_PIPE_OUT_XY_Y(crtc_y));785 786	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_FORMAT(pipe),787			MDP5_PIPE_SRC_FORMAT_A_BPC(format->bpc_a) |788			MDP5_PIPE_SRC_FORMAT_R_BPC(format->bpc_r_cr) |789			MDP5_PIPE_SRC_FORMAT_G_BPC(format->bpc_g_y) |790			MDP5_PIPE_SRC_FORMAT_B_BPC(format->bpc_b_cb) |791			COND(format->alpha_enable, MDP5_PIPE_SRC_FORMAT_ALPHA_ENABLE) |792			MDP5_PIPE_SRC_FORMAT_CPP(format->bpp - 1) |793			MDP5_PIPE_SRC_FORMAT_UNPACK_COUNT(format->unpack_count - 1) |794			COND(format->flags & MSM_FORMAT_FLAG_UNPACK_TIGHT,795			     MDP5_PIPE_SRC_FORMAT_UNPACK_TIGHT) |796			MDP5_PIPE_SRC_FORMAT_FETCH_TYPE(format->fetch_type) |797			MDP5_PIPE_SRC_FORMAT_CHROMA_SAMP(format->chroma_sample));798 799	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_UNPACK(pipe),800			MDP5_PIPE_SRC_UNPACK_ELEM0(format->element[0]) |801			MDP5_PIPE_SRC_UNPACK_ELEM1(format->element[1]) |802			MDP5_PIPE_SRC_UNPACK_ELEM2(format->element[2]) |803			MDP5_PIPE_SRC_UNPACK_ELEM3(format->element[3]));804 805	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_OP_MODE(pipe),806			(hflip ? MDP5_PIPE_SRC_OP_MODE_FLIP_LR : 0) |807			(vflip ? MDP5_PIPE_SRC_OP_MODE_FLIP_UD : 0) |808			COND(has_pe, MDP5_PIPE_SRC_OP_MODE_SW_PIX_EXT_OVERRIDE) |809			MDP5_PIPE_SRC_OP_MODE_BWC(BWC_LOSSLESS));810 811	/* not using secure mode: */812	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_ADDR_SW_STATUS(pipe), 0);813 814	if (hwpipe->caps & MDP_PIPE_CAP_SW_PIX_EXT)815		mdp5_write_pixel_ext(mdp5_kms, pipe, format,816				src_w, pe->left, pe->right,817				src_h, pe->top, pe->bottom);818 819	if (hwpipe->caps & MDP_PIPE_CAP_SCALE) {820		mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_X(pipe),821				step->x[COMP_0]);822		mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_Y(pipe),823				step->y[COMP_0]);824		mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CR_PHASE_STEP_X(pipe),825				step->x[COMP_1_2]);826		mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CR_PHASE_STEP_Y(pipe),827				step->y[COMP_1_2]);828		mdp5_write(mdp5_kms, REG_MDP5_PIPE_DECIMATION(pipe),829				MDP5_PIPE_DECIMATION_VERT(vdecm) |830				MDP5_PIPE_DECIMATION_HORZ(hdecm));831		mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CONFIG(pipe),832			   scale_config);833	}834 835	if (hwpipe->caps & MDP_PIPE_CAP_CSC) {836		if (MSM_FORMAT_IS_YUV(format))837			csc_enable(mdp5_kms, pipe,838					mdp_get_default_csc_cfg(CSC_YUV2RGB));839		else840			csc_disable(mdp5_kms, pipe);841	}842 843	set_scanout_locked(mdp5_kms, pipe, fb);844}845 846static int mdp5_plane_mode_set(struct drm_plane *plane,847		struct drm_crtc *crtc, struct drm_framebuffer *fb,848		struct drm_rect *src, struct drm_rect *dest)849{850	struct drm_plane_state *pstate = plane->state;851	struct mdp5_hw_pipe *hwpipe = to_mdp5_plane_state(pstate)->hwpipe;852	struct mdp5_kms *mdp5_kms = get_kms(plane);853	enum mdp5_pipe pipe = hwpipe->pipe;854	struct mdp5_hw_pipe *right_hwpipe;855	const struct msm_format *format;856	uint32_t nplanes, config = 0;857	struct phase_step step = { { 0 } };858	struct pixel_ext pe = { { 0 } };859	uint32_t hdecm = 0, vdecm = 0;860	uint32_t pix_format;861	unsigned int rotation;862	bool vflip, hflip;863	int crtc_x, crtc_y;864	unsigned int crtc_w, crtc_h;865	uint32_t src_x, src_y;866	uint32_t src_w, src_h;867	uint32_t src_img_w, src_img_h;868	int ret;869 870	nplanes = fb->format->num_planes;871 872	/* bad formats should already be rejected: */873	if (WARN_ON(nplanes > pipe2nclients(pipe)))874		return -EINVAL;875 876	format = msm_framebuffer_format(fb);877	pix_format = format->pixel_format;878 879	src_x = src->x1;880	src_y = src->y1;881	src_w = drm_rect_width(src);882	src_h = drm_rect_height(src);883 884	crtc_x = dest->x1;885	crtc_y = dest->y1;886	crtc_w = drm_rect_width(dest);887	crtc_h = drm_rect_height(dest);888 889	/* src values are in Q16 fixed point, convert to integer: */890	src_x = src_x >> 16;891	src_y = src_y >> 16;892	src_w = src_w >> 16;893	src_h = src_h >> 16;894 895	src_img_w = min(fb->width, src_w);896	src_img_h = min(fb->height, src_h);897 898	DBG("%s: FB[%u] %u,%u,%u,%u -> CRTC[%u] %d,%d,%u,%u", plane->name,899			fb->base.id, src_x, src_y, src_w, src_h,900			crtc->base.id, crtc_x, crtc_y, crtc_w, crtc_h);901 902	right_hwpipe = to_mdp5_plane_state(pstate)->r_hwpipe;903	if (right_hwpipe) {904		/*905		 * if the plane comprises of 2 hw pipes, assume that the width906		 * is split equally across them. The only parameters that varies907		 * between the 2 pipes are src_x and crtc_x908		 */909		crtc_w /= 2;910		src_w /= 2;911		src_img_w /= 2;912	}913 914	ret = calc_scalex_steps(plane, pix_format, src_w, crtc_w, step.x);915	if (ret)916		return ret;917 918	ret = calc_scaley_steps(plane, pix_format, src_h, crtc_h, step.y);919	if (ret)920		return ret;921 922	if (hwpipe->caps & MDP_PIPE_CAP_SW_PIX_EXT) {923		calc_pixel_ext(format, src_w, crtc_w, step.x,924			       pe.left, pe.right, true);925		calc_pixel_ext(format, src_h, crtc_h, step.y,926			       pe.top, pe.bottom, false);927	}928 929	/* TODO calc hdecm, vdecm */930 931	/* SCALE is used to both scale and up-sample chroma components */932	config |= get_scale_config(format, src_w, crtc_w, true);933	config |= get_scale_config(format, src_h, crtc_h, false);934	DBG("scale config = %x", config);935 936	rotation = drm_rotation_simplify(pstate->rotation,937					 DRM_MODE_ROTATE_0 |938					 DRM_MODE_REFLECT_X |939					 DRM_MODE_REFLECT_Y);940	hflip = !!(rotation & DRM_MODE_REFLECT_X);941	vflip = !!(rotation & DRM_MODE_REFLECT_Y);942 943	mdp5_hwpipe_mode_set(mdp5_kms, hwpipe, fb, &step, &pe,944			     config, hdecm, vdecm, hflip, vflip,945			     crtc_x, crtc_y, crtc_w, crtc_h,946			     src_img_w, src_img_h,947			     src_x, src_y, src_w, src_h);948	if (right_hwpipe)949		mdp5_hwpipe_mode_set(mdp5_kms, right_hwpipe, fb, &step, &pe,950				     config, hdecm, vdecm, hflip, vflip,951				     crtc_x + crtc_w, crtc_y, crtc_w, crtc_h,952				     src_img_w, src_img_h,953				     src_x + src_w, src_y, src_w, src_h);954 955	return ret;956}957 958/*959 * Use this func and the one below only after the atomic state has been960 * successfully swapped961 */962enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane)963{964	struct mdp5_plane_state *pstate = to_mdp5_plane_state(plane->state);965 966	if (WARN_ON(!pstate->hwpipe))967		return SSPP_NONE;968 969	return pstate->hwpipe->pipe;970}971 972enum mdp5_pipe mdp5_plane_right_pipe(struct drm_plane *plane)973{974	struct mdp5_plane_state *pstate = to_mdp5_plane_state(plane->state);975 976	if (!pstate->r_hwpipe)977		return SSPP_NONE;978 979	return pstate->r_hwpipe->pipe;980}981 982uint32_t mdp5_plane_get_flush(struct drm_plane *plane)983{984	struct mdp5_plane_state *pstate = to_mdp5_plane_state(plane->state);985	u32 mask;986 987	if (WARN_ON(!pstate->hwpipe))988		return 0;989 990	mask = pstate->hwpipe->flush_mask;991 992	if (pstate->r_hwpipe)993		mask |= pstate->r_hwpipe->flush_mask;994 995	return mask;996}997 998static const uint32_t mdp5_plane_formats[] = {999	DRM_FORMAT_ARGB8888,1000	DRM_FORMAT_ABGR8888,1001	DRM_FORMAT_RGBA8888,1002	DRM_FORMAT_BGRA8888,1003	DRM_FORMAT_XRGB8888,1004	DRM_FORMAT_XBGR8888,1005	DRM_FORMAT_RGBX8888,1006	DRM_FORMAT_BGRX8888,1007	DRM_FORMAT_RGB888,1008	DRM_FORMAT_BGR888,1009	DRM_FORMAT_RGB565,1010	DRM_FORMAT_BGR565,1011 1012	DRM_FORMAT_NV12,1013	DRM_FORMAT_NV21,1014	DRM_FORMAT_NV16,1015	DRM_FORMAT_NV61,1016	DRM_FORMAT_VYUY,1017	DRM_FORMAT_UYVY,1018	DRM_FORMAT_YUYV,1019	DRM_FORMAT_YVYU,1020	DRM_FORMAT_YUV420,1021	DRM_FORMAT_YVU420,1022};1023 1024/* initialize plane */1025struct drm_plane *mdp5_plane_init(struct drm_device *dev,1026				  enum drm_plane_type type)1027{1028	struct drm_plane *plane = NULL;1029	struct mdp5_plane *mdp5_plane;1030 1031	mdp5_plane = drmm_universal_plane_alloc(dev, struct mdp5_plane, base,1032						0xff, &mdp5_plane_funcs,1033						mdp5_plane_formats, ARRAY_SIZE(mdp5_plane_formats),1034						NULL, type, NULL);1035	if (IS_ERR(mdp5_plane))1036		return ERR_CAST(mdp5_plane);1037 1038	plane = &mdp5_plane->base;1039 1040	drm_plane_helper_add(plane, &mdp5_plane_helper_funcs);1041 1042	mdp5_plane_install_properties(plane, &plane->base);1043 1044	drm_plane_enable_fb_damage_clips(plane);1045 1046	return plane;1047}1048