18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * (C) COPYRIGHT 2016 ARM Limited. All rights reserved.
48c2ecf20Sopenharmony_ci * Author: Liviu Dudau <Liviu.Dudau@arm.com>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * ARM Mali DP500/DP550/DP650 driver (crtc operations)
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/clk.h>
108c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <video/videomode.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h>
158c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h>
168c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h>
178c2ecf20Sopenharmony_ci#include <drm/drm_print.h>
188c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h>
198c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include "malidp_drv.h"
228c2ecf20Sopenharmony_ci#include "malidp_hw.h"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic enum drm_mode_status malidp_crtc_mode_valid(struct drm_crtc *crtc,
258c2ecf20Sopenharmony_ci						   const struct drm_display_mode *mode)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
288c2ecf20Sopenharmony_ci	struct malidp_hw_device *hwdev = malidp->dev;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	/*
318c2ecf20Sopenharmony_ci	 * check that the hardware can drive the required clock rate,
328c2ecf20Sopenharmony_ci	 * but skip the check if the clock is meant to be disabled (req_rate = 0)
338c2ecf20Sopenharmony_ci	 */
348c2ecf20Sopenharmony_ci	long rate, req_rate = mode->crtc_clock * 1000;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	if (req_rate) {
378c2ecf20Sopenharmony_ci		rate = clk_round_rate(hwdev->pxlclk, req_rate);
388c2ecf20Sopenharmony_ci		if (rate != req_rate) {
398c2ecf20Sopenharmony_ci			DRM_DEBUG_DRIVER("pxlclk doesn't support %ld Hz\n",
408c2ecf20Sopenharmony_ci					 req_rate);
418c2ecf20Sopenharmony_ci			return MODE_NOCLOCK;
428c2ecf20Sopenharmony_ci		}
438c2ecf20Sopenharmony_ci	}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	return MODE_OK;
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic void malidp_crtc_atomic_enable(struct drm_crtc *crtc,
498c2ecf20Sopenharmony_ci				      struct drm_crtc_state *old_state)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
528c2ecf20Sopenharmony_ci	struct malidp_hw_device *hwdev = malidp->dev;
538c2ecf20Sopenharmony_ci	struct videomode vm;
548c2ecf20Sopenharmony_ci	int err = pm_runtime_get_sync(crtc->dev->dev);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	if (err < 0) {
578c2ecf20Sopenharmony_ci		DRM_DEBUG_DRIVER("Failed to enable runtime power management: %d\n", err);
588c2ecf20Sopenharmony_ci		return;
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	drm_display_mode_to_videomode(&crtc->state->adjusted_mode, &vm);
628c2ecf20Sopenharmony_ci	clk_prepare_enable(hwdev->pxlclk);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	/* We rely on firmware to set mclk to a sensible level. */
658c2ecf20Sopenharmony_ci	clk_set_rate(hwdev->pxlclk, crtc->state->adjusted_mode.crtc_clock * 1000);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	hwdev->hw->modeset(hwdev, &vm);
688c2ecf20Sopenharmony_ci	hwdev->hw->leave_config_mode(hwdev);
698c2ecf20Sopenharmony_ci	drm_crtc_vblank_on(crtc);
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic void malidp_crtc_atomic_disable(struct drm_crtc *crtc,
738c2ecf20Sopenharmony_ci				       struct drm_crtc_state *old_state)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
768c2ecf20Sopenharmony_ci	struct malidp_hw_device *hwdev = malidp->dev;
778c2ecf20Sopenharmony_ci	int err;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	/* always disable planes on the CRTC that is being turned off */
808c2ecf20Sopenharmony_ci	drm_atomic_helper_disable_planes_on_crtc(old_state, false);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	drm_crtc_vblank_off(crtc);
838c2ecf20Sopenharmony_ci	hwdev->hw->enter_config_mode(hwdev);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	clk_disable_unprepare(hwdev->pxlclk);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	err = pm_runtime_put(crtc->dev->dev);
888c2ecf20Sopenharmony_ci	if (err < 0) {
898c2ecf20Sopenharmony_ci		DRM_DEBUG_DRIVER("Failed to disable runtime power management: %d\n", err);
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic const struct gamma_curve_segment {
948c2ecf20Sopenharmony_ci	u16 start;
958c2ecf20Sopenharmony_ci	u16 end;
968c2ecf20Sopenharmony_ci} segments[MALIDP_COEFFTAB_NUM_COEFFS] = {
978c2ecf20Sopenharmony_ci	/* sector 0 */
988c2ecf20Sopenharmony_ci	{    0,    0 }, {    1,    1 }, {    2,    2 }, {    3,    3 },
998c2ecf20Sopenharmony_ci	{    4,    4 }, {    5,    5 }, {    6,    6 }, {    7,    7 },
1008c2ecf20Sopenharmony_ci	{    8,    8 }, {    9,    9 }, {   10,   10 }, {   11,   11 },
1018c2ecf20Sopenharmony_ci	{   12,   12 }, {   13,   13 }, {   14,   14 }, {   15,   15 },
1028c2ecf20Sopenharmony_ci	/* sector 1 */
1038c2ecf20Sopenharmony_ci	{   16,   19 }, {   20,   23 }, {   24,   27 }, {   28,   31 },
1048c2ecf20Sopenharmony_ci	/* sector 2 */
1058c2ecf20Sopenharmony_ci	{   32,   39 }, {   40,   47 }, {   48,   55 }, {   56,   63 },
1068c2ecf20Sopenharmony_ci	/* sector 3 */
1078c2ecf20Sopenharmony_ci	{   64,   79 }, {   80,   95 }, {   96,  111 }, {  112,  127 },
1088c2ecf20Sopenharmony_ci	/* sector 4 */
1098c2ecf20Sopenharmony_ci	{  128,  159 }, {  160,  191 }, {  192,  223 }, {  224,  255 },
1108c2ecf20Sopenharmony_ci	/* sector 5 */
1118c2ecf20Sopenharmony_ci	{  256,  319 }, {  320,  383 }, {  384,  447 }, {  448,  511 },
1128c2ecf20Sopenharmony_ci	/* sector 6 */
1138c2ecf20Sopenharmony_ci	{  512,  639 }, {  640,  767 }, {  768,  895 }, {  896, 1023 },
1148c2ecf20Sopenharmony_ci	{ 1024, 1151 }, { 1152, 1279 }, { 1280, 1407 }, { 1408, 1535 },
1158c2ecf20Sopenharmony_ci	{ 1536, 1663 }, { 1664, 1791 }, { 1792, 1919 }, { 1920, 2047 },
1168c2ecf20Sopenharmony_ci	{ 2048, 2175 }, { 2176, 2303 }, { 2304, 2431 }, { 2432, 2559 },
1178c2ecf20Sopenharmony_ci	{ 2560, 2687 }, { 2688, 2815 }, { 2816, 2943 }, { 2944, 3071 },
1188c2ecf20Sopenharmony_ci	{ 3072, 3199 }, { 3200, 3327 }, { 3328, 3455 }, { 3456, 3583 },
1198c2ecf20Sopenharmony_ci	{ 3584, 3711 }, { 3712, 3839 }, { 3840, 3967 }, { 3968, 4095 },
1208c2ecf20Sopenharmony_ci};
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci#define DE_COEFTAB_DATA(a, b) ((((a) & 0xfff) << 16) | (((b) & 0xfff)))
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic void malidp_generate_gamma_table(struct drm_property_blob *lut_blob,
1258c2ecf20Sopenharmony_ci					u32 coeffs[MALIDP_COEFFTAB_NUM_COEFFS])
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	struct drm_color_lut *lut = (struct drm_color_lut *)lut_blob->data;
1288c2ecf20Sopenharmony_ci	int i;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	for (i = 0; i < MALIDP_COEFFTAB_NUM_COEFFS; ++i) {
1318c2ecf20Sopenharmony_ci		u32 a, b, delta_in, out_start, out_end;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci		delta_in = segments[i].end - segments[i].start;
1348c2ecf20Sopenharmony_ci		/* DP has 12-bit internal precision for its LUTs. */
1358c2ecf20Sopenharmony_ci		out_start = drm_color_lut_extract(lut[segments[i].start].green,
1368c2ecf20Sopenharmony_ci						  12);
1378c2ecf20Sopenharmony_ci		out_end = drm_color_lut_extract(lut[segments[i].end].green, 12);
1388c2ecf20Sopenharmony_ci		a = (delta_in == 0) ? 0 : ((out_end - out_start) * 256) / delta_in;
1398c2ecf20Sopenharmony_ci		b = out_start;
1408c2ecf20Sopenharmony_ci		coeffs[i] = DE_COEFTAB_DATA(a, b);
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci/*
1458c2ecf20Sopenharmony_ci * Check if there is a new gamma LUT and if it is of an acceptable size. Also,
1468c2ecf20Sopenharmony_ci * reject any LUTs that use distinct red, green, and blue curves.
1478c2ecf20Sopenharmony_ci */
1488c2ecf20Sopenharmony_cistatic int malidp_crtc_atomic_check_gamma(struct drm_crtc *crtc,
1498c2ecf20Sopenharmony_ci					  struct drm_crtc_state *state)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	struct malidp_crtc_state *mc = to_malidp_crtc_state(state);
1528c2ecf20Sopenharmony_ci	struct drm_color_lut *lut;
1538c2ecf20Sopenharmony_ci	size_t lut_size;
1548c2ecf20Sopenharmony_ci	int i;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	if (!state->color_mgmt_changed || !state->gamma_lut)
1578c2ecf20Sopenharmony_ci		return 0;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (crtc->state->gamma_lut &&
1608c2ecf20Sopenharmony_ci	    (crtc->state->gamma_lut->base.id == state->gamma_lut->base.id))
1618c2ecf20Sopenharmony_ci		return 0;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	if (state->gamma_lut->length % sizeof(struct drm_color_lut))
1648c2ecf20Sopenharmony_ci		return -EINVAL;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	lut_size = state->gamma_lut->length / sizeof(struct drm_color_lut);
1678c2ecf20Sopenharmony_ci	if (lut_size != MALIDP_GAMMA_LUT_SIZE)
1688c2ecf20Sopenharmony_ci		return -EINVAL;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	lut = (struct drm_color_lut *)state->gamma_lut->data;
1718c2ecf20Sopenharmony_ci	for (i = 0; i < lut_size; ++i)
1728c2ecf20Sopenharmony_ci		if (!((lut[i].red == lut[i].green) &&
1738c2ecf20Sopenharmony_ci		      (lut[i].red == lut[i].blue)))
1748c2ecf20Sopenharmony_ci			return -EINVAL;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	if (!state->mode_changed) {
1778c2ecf20Sopenharmony_ci		int ret;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci		state->mode_changed = true;
1808c2ecf20Sopenharmony_ci		/*
1818c2ecf20Sopenharmony_ci		 * Kerneldoc for drm_atomic_helper_check_modeset mandates that
1828c2ecf20Sopenharmony_ci		 * it be invoked when the driver sets ->mode_changed. Since
1838c2ecf20Sopenharmony_ci		 * changing the gamma LUT doesn't depend on any external
1848c2ecf20Sopenharmony_ci		 * resources, it is safe to call it only once.
1858c2ecf20Sopenharmony_ci		 */
1868c2ecf20Sopenharmony_ci		ret = drm_atomic_helper_check_modeset(crtc->dev, state->state);
1878c2ecf20Sopenharmony_ci		if (ret)
1888c2ecf20Sopenharmony_ci			return ret;
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	malidp_generate_gamma_table(state->gamma_lut, mc->gamma_coeffs);
1928c2ecf20Sopenharmony_ci	return 0;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci/*
1968c2ecf20Sopenharmony_ci * Check if there is a new CTM and if it contains valid input. Valid here means
1978c2ecf20Sopenharmony_ci * that the number is inside the representable range for a Q3.12 number,
1988c2ecf20Sopenharmony_ci * excluding truncating the fractional part of the input data.
1998c2ecf20Sopenharmony_ci *
2008c2ecf20Sopenharmony_ci * The COLORADJ registers can be changed atomically.
2018c2ecf20Sopenharmony_ci */
2028c2ecf20Sopenharmony_cistatic int malidp_crtc_atomic_check_ctm(struct drm_crtc *crtc,
2038c2ecf20Sopenharmony_ci					struct drm_crtc_state *state)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	struct malidp_crtc_state *mc = to_malidp_crtc_state(state);
2068c2ecf20Sopenharmony_ci	struct drm_color_ctm *ctm;
2078c2ecf20Sopenharmony_ci	int i;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	if (!state->color_mgmt_changed)
2108c2ecf20Sopenharmony_ci		return 0;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	if (!state->ctm)
2138c2ecf20Sopenharmony_ci		return 0;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	if (crtc->state->ctm && (crtc->state->ctm->base.id ==
2168c2ecf20Sopenharmony_ci				 state->ctm->base.id))
2178c2ecf20Sopenharmony_ci		return 0;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	/*
2208c2ecf20Sopenharmony_ci	 * The size of the ctm is checked in
2218c2ecf20Sopenharmony_ci	 * drm_atomic_replace_property_blob_from_id.
2228c2ecf20Sopenharmony_ci	 */
2238c2ecf20Sopenharmony_ci	ctm = (struct drm_color_ctm *)state->ctm->data;
2248c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ctm->matrix); ++i) {
2258c2ecf20Sopenharmony_ci		/* Convert from S31.32 to Q3.12. */
2268c2ecf20Sopenharmony_ci		s64 val = ctm->matrix[i];
2278c2ecf20Sopenharmony_ci		u32 mag = ((((u64)val) & ~BIT_ULL(63)) >> 20) &
2288c2ecf20Sopenharmony_ci			  GENMASK_ULL(14, 0);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci		/*
2318c2ecf20Sopenharmony_ci		 * Convert to 2s complement and check the destination's top bit
2328c2ecf20Sopenharmony_ci		 * for overflow. NB: Can't check before converting or it'd
2338c2ecf20Sopenharmony_ci		 * incorrectly reject the case:
2348c2ecf20Sopenharmony_ci		 * sign == 1
2358c2ecf20Sopenharmony_ci		 * mag == 0x2000
2368c2ecf20Sopenharmony_ci		 */
2378c2ecf20Sopenharmony_ci		if (val & BIT_ULL(63))
2388c2ecf20Sopenharmony_ci			mag = ~mag + 1;
2398c2ecf20Sopenharmony_ci		if (!!(val & BIT_ULL(63)) != !!(mag & BIT(14)))
2408c2ecf20Sopenharmony_ci			return -EINVAL;
2418c2ecf20Sopenharmony_ci		mc->coloradj_coeffs[i] = mag;
2428c2ecf20Sopenharmony_ci	}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	return 0;
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic int malidp_crtc_atomic_check_scaling(struct drm_crtc *crtc,
2488c2ecf20Sopenharmony_ci					    struct drm_crtc_state *state)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
2518c2ecf20Sopenharmony_ci	struct malidp_hw_device *hwdev = malidp->dev;
2528c2ecf20Sopenharmony_ci	struct malidp_crtc_state *cs = to_malidp_crtc_state(state);
2538c2ecf20Sopenharmony_ci	struct malidp_se_config *s = &cs->scaler_config;
2548c2ecf20Sopenharmony_ci	struct drm_plane *plane;
2558c2ecf20Sopenharmony_ci	struct videomode vm;
2568c2ecf20Sopenharmony_ci	const struct drm_plane_state *pstate;
2578c2ecf20Sopenharmony_ci	u32 h_upscale_factor = 0; /* U16.16 */
2588c2ecf20Sopenharmony_ci	u32 v_upscale_factor = 0; /* U16.16 */
2598c2ecf20Sopenharmony_ci	u8 scaling = cs->scaled_planes_mask;
2608c2ecf20Sopenharmony_ci	int ret;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	if (!scaling) {
2638c2ecf20Sopenharmony_ci		s->scale_enable = false;
2648c2ecf20Sopenharmony_ci		goto mclk_calc;
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	/* The scaling engine can only handle one plane at a time. */
2688c2ecf20Sopenharmony_ci	if (scaling & (scaling - 1))
2698c2ecf20Sopenharmony_ci		return -EINVAL;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) {
2728c2ecf20Sopenharmony_ci		struct malidp_plane *mp = to_malidp_plane(plane);
2738c2ecf20Sopenharmony_ci		u32 phase;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci		if (!(mp->layer->id & scaling))
2768c2ecf20Sopenharmony_ci			continue;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci		/*
2798c2ecf20Sopenharmony_ci		 * Convert crtc_[w|h] to U32.32, then divide by U16.16 src_[w|h]
2808c2ecf20Sopenharmony_ci		 * to get the U16.16 result.
2818c2ecf20Sopenharmony_ci		 */
2828c2ecf20Sopenharmony_ci		h_upscale_factor = div_u64((u64)pstate->crtc_w << 32,
2838c2ecf20Sopenharmony_ci					   pstate->src_w);
2848c2ecf20Sopenharmony_ci		v_upscale_factor = div_u64((u64)pstate->crtc_h << 32,
2858c2ecf20Sopenharmony_ci					   pstate->src_h);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci		s->enhancer_enable = ((h_upscale_factor >> 16) >= 2 ||
2888c2ecf20Sopenharmony_ci				      (v_upscale_factor >> 16) >= 2);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci		if (pstate->rotation & MALIDP_ROTATED_MASK) {
2918c2ecf20Sopenharmony_ci			s->input_w = pstate->src_h >> 16;
2928c2ecf20Sopenharmony_ci			s->input_h = pstate->src_w >> 16;
2938c2ecf20Sopenharmony_ci		} else {
2948c2ecf20Sopenharmony_ci			s->input_w = pstate->src_w >> 16;
2958c2ecf20Sopenharmony_ci			s->input_h = pstate->src_h >> 16;
2968c2ecf20Sopenharmony_ci		}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci		s->output_w = pstate->crtc_w;
2998c2ecf20Sopenharmony_ci		s->output_h = pstate->crtc_h;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci#define SE_N_PHASE 4
3028c2ecf20Sopenharmony_ci#define SE_SHIFT_N_PHASE 12
3038c2ecf20Sopenharmony_ci		/* Calculate initial_phase and delta_phase for horizontal. */
3048c2ecf20Sopenharmony_ci		phase = s->input_w;
3058c2ecf20Sopenharmony_ci		s->h_init_phase =
3068c2ecf20Sopenharmony_ci				((phase << SE_N_PHASE) / s->output_w + 1) / 2;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci		phase = s->input_w;
3098c2ecf20Sopenharmony_ci		phase <<= (SE_SHIFT_N_PHASE + SE_N_PHASE);
3108c2ecf20Sopenharmony_ci		s->h_delta_phase = phase / s->output_w;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci		/* Same for vertical. */
3138c2ecf20Sopenharmony_ci		phase = s->input_h;
3148c2ecf20Sopenharmony_ci		s->v_init_phase =
3158c2ecf20Sopenharmony_ci				((phase << SE_N_PHASE) / s->output_h + 1) / 2;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci		phase = s->input_h;
3188c2ecf20Sopenharmony_ci		phase <<= (SE_SHIFT_N_PHASE + SE_N_PHASE);
3198c2ecf20Sopenharmony_ci		s->v_delta_phase = phase / s->output_h;
3208c2ecf20Sopenharmony_ci#undef SE_N_PHASE
3218c2ecf20Sopenharmony_ci#undef SE_SHIFT_N_PHASE
3228c2ecf20Sopenharmony_ci		s->plane_src_id = mp->layer->id;
3238c2ecf20Sopenharmony_ci	}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	s->scale_enable = true;
3268c2ecf20Sopenharmony_ci	s->hcoeff = malidp_se_select_coeffs(h_upscale_factor);
3278c2ecf20Sopenharmony_ci	s->vcoeff = malidp_se_select_coeffs(v_upscale_factor);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_cimclk_calc:
3308c2ecf20Sopenharmony_ci	drm_display_mode_to_videomode(&state->adjusted_mode, &vm);
3318c2ecf20Sopenharmony_ci	ret = hwdev->hw->se_calc_mclk(hwdev, s, &vm);
3328c2ecf20Sopenharmony_ci	if (ret < 0)
3338c2ecf20Sopenharmony_ci		return -EINVAL;
3348c2ecf20Sopenharmony_ci	return 0;
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic int malidp_crtc_atomic_check(struct drm_crtc *crtc,
3388c2ecf20Sopenharmony_ci				    struct drm_crtc_state *state)
3398c2ecf20Sopenharmony_ci{
3408c2ecf20Sopenharmony_ci	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
3418c2ecf20Sopenharmony_ci	struct malidp_hw_device *hwdev = malidp->dev;
3428c2ecf20Sopenharmony_ci	struct drm_plane *plane;
3438c2ecf20Sopenharmony_ci	const struct drm_plane_state *pstate;
3448c2ecf20Sopenharmony_ci	u32 rot_mem_free, rot_mem_usable;
3458c2ecf20Sopenharmony_ci	int rotated_planes = 0;
3468c2ecf20Sopenharmony_ci	int ret;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	/*
3498c2ecf20Sopenharmony_ci	 * check if there is enough rotation memory available for planes
3508c2ecf20Sopenharmony_ci	 * that need 90° and 270° rotion or planes that are compressed.
3518c2ecf20Sopenharmony_ci	 * Each plane has set its required memory size in the ->plane_check()
3528c2ecf20Sopenharmony_ci	 * callback, here we only make sure that the sums are less that the
3538c2ecf20Sopenharmony_ci	 * total usable memory.
3548c2ecf20Sopenharmony_ci	 *
3558c2ecf20Sopenharmony_ci	 * The rotation memory allocation algorithm (for each plane):
3568c2ecf20Sopenharmony_ci	 *  a. If no more rotated or compressed planes exist, all remaining
3578c2ecf20Sopenharmony_ci	 *     rotate memory in the bank is available for use by the plane.
3588c2ecf20Sopenharmony_ci	 *  b. If other rotated or compressed planes exist, and plane's
3598c2ecf20Sopenharmony_ci	 *     layer ID is DE_VIDEO1, it can use all the memory from first bank
3608c2ecf20Sopenharmony_ci	 *     if secondary rotation memory bank is available, otherwise it can
3618c2ecf20Sopenharmony_ci	 *     use up to half the bank's memory.
3628c2ecf20Sopenharmony_ci	 *  c. If other rotated or compressed planes exist, and plane's layer ID
3638c2ecf20Sopenharmony_ci	 *     is not DE_VIDEO1, it can use half of the available memory.
3648c2ecf20Sopenharmony_ci	 *
3658c2ecf20Sopenharmony_ci	 * Note: this algorithm assumes that the order in which the planes are
3668c2ecf20Sopenharmony_ci	 * checked always has DE_VIDEO1 plane first in the list if it is
3678c2ecf20Sopenharmony_ci	 * rotated. Because that is how we create the planes in the first
3688c2ecf20Sopenharmony_ci	 * place, under current DRM version things work, but if ever the order
3698c2ecf20Sopenharmony_ci	 * in which drm_atomic_crtc_state_for_each_plane() iterates over planes
3708c2ecf20Sopenharmony_ci	 * changes, we need to pre-sort the planes before validation.
3718c2ecf20Sopenharmony_ci	 */
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	/* first count the number of rotated planes */
3748c2ecf20Sopenharmony_ci	drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) {
3758c2ecf20Sopenharmony_ci		struct drm_framebuffer *fb = pstate->fb;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci		if ((pstate->rotation & MALIDP_ROTATED_MASK) || fb->modifier)
3788c2ecf20Sopenharmony_ci			rotated_planes++;
3798c2ecf20Sopenharmony_ci	}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	rot_mem_free = hwdev->rotation_memory[0];
3828c2ecf20Sopenharmony_ci	/*
3838c2ecf20Sopenharmony_ci	 * if we have more than 1 plane using rotation memory, use the second
3848c2ecf20Sopenharmony_ci	 * block of rotation memory as well
3858c2ecf20Sopenharmony_ci	 */
3868c2ecf20Sopenharmony_ci	if (rotated_planes > 1)
3878c2ecf20Sopenharmony_ci		rot_mem_free += hwdev->rotation_memory[1];
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	/* now validate the rotation memory requirements */
3908c2ecf20Sopenharmony_ci	drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) {
3918c2ecf20Sopenharmony_ci		struct malidp_plane *mp = to_malidp_plane(plane);
3928c2ecf20Sopenharmony_ci		struct malidp_plane_state *ms = to_malidp_plane_state(pstate);
3938c2ecf20Sopenharmony_ci		struct drm_framebuffer *fb = pstate->fb;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci		if ((pstate->rotation & MALIDP_ROTATED_MASK) || fb->modifier) {
3968c2ecf20Sopenharmony_ci			/* process current plane */
3978c2ecf20Sopenharmony_ci			rotated_planes--;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci			if (!rotated_planes) {
4008c2ecf20Sopenharmony_ci				/* no more rotated planes, we can use what's left */
4018c2ecf20Sopenharmony_ci				rot_mem_usable = rot_mem_free;
4028c2ecf20Sopenharmony_ci			} else {
4038c2ecf20Sopenharmony_ci				if ((mp->layer->id != DE_VIDEO1) ||
4048c2ecf20Sopenharmony_ci				    (hwdev->rotation_memory[1] == 0))
4058c2ecf20Sopenharmony_ci					rot_mem_usable = rot_mem_free / 2;
4068c2ecf20Sopenharmony_ci				else
4078c2ecf20Sopenharmony_ci					rot_mem_usable = hwdev->rotation_memory[0];
4088c2ecf20Sopenharmony_ci			}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci			rot_mem_free -= rot_mem_usable;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci			if (ms->rotmem_size > rot_mem_usable)
4138c2ecf20Sopenharmony_ci				return -EINVAL;
4148c2ecf20Sopenharmony_ci		}
4158c2ecf20Sopenharmony_ci	}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	/* If only the writeback routing has changed, we don't need a modeset */
4188c2ecf20Sopenharmony_ci	if (state->connectors_changed) {
4198c2ecf20Sopenharmony_ci		u32 old_mask = crtc->state->connector_mask;
4208c2ecf20Sopenharmony_ci		u32 new_mask = state->connector_mask;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci		if ((old_mask ^ new_mask) ==
4238c2ecf20Sopenharmony_ci		    (1 << drm_connector_index(&malidp->mw_connector.base)))
4248c2ecf20Sopenharmony_ci			state->connectors_changed = false;
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	ret = malidp_crtc_atomic_check_gamma(crtc, state);
4288c2ecf20Sopenharmony_ci	ret = ret ? ret : malidp_crtc_atomic_check_ctm(crtc, state);
4298c2ecf20Sopenharmony_ci	ret = ret ? ret : malidp_crtc_atomic_check_scaling(crtc, state);
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	return ret;
4328c2ecf20Sopenharmony_ci}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_cistatic const struct drm_crtc_helper_funcs malidp_crtc_helper_funcs = {
4358c2ecf20Sopenharmony_ci	.mode_valid = malidp_crtc_mode_valid,
4368c2ecf20Sopenharmony_ci	.atomic_check = malidp_crtc_atomic_check,
4378c2ecf20Sopenharmony_ci	.atomic_enable = malidp_crtc_atomic_enable,
4388c2ecf20Sopenharmony_ci	.atomic_disable = malidp_crtc_atomic_disable,
4398c2ecf20Sopenharmony_ci};
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_cistatic struct drm_crtc_state *malidp_crtc_duplicate_state(struct drm_crtc *crtc)
4428c2ecf20Sopenharmony_ci{
4438c2ecf20Sopenharmony_ci	struct malidp_crtc_state *state, *old_state;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	if (WARN_ON(!crtc->state))
4468c2ecf20Sopenharmony_ci		return NULL;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	old_state = to_malidp_crtc_state(crtc->state);
4498c2ecf20Sopenharmony_ci	state = kmalloc(sizeof(*state), GFP_KERNEL);
4508c2ecf20Sopenharmony_ci	if (!state)
4518c2ecf20Sopenharmony_ci		return NULL;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
4548c2ecf20Sopenharmony_ci	memcpy(state->gamma_coeffs, old_state->gamma_coeffs,
4558c2ecf20Sopenharmony_ci	       sizeof(state->gamma_coeffs));
4568c2ecf20Sopenharmony_ci	memcpy(state->coloradj_coeffs, old_state->coloradj_coeffs,
4578c2ecf20Sopenharmony_ci	       sizeof(state->coloradj_coeffs));
4588c2ecf20Sopenharmony_ci	memcpy(&state->scaler_config, &old_state->scaler_config,
4598c2ecf20Sopenharmony_ci	       sizeof(state->scaler_config));
4608c2ecf20Sopenharmony_ci	state->scaled_planes_mask = 0;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	return &state->base;
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic void malidp_crtc_destroy_state(struct drm_crtc *crtc,
4668c2ecf20Sopenharmony_ci				      struct drm_crtc_state *state)
4678c2ecf20Sopenharmony_ci{
4688c2ecf20Sopenharmony_ci	struct malidp_crtc_state *mali_state = NULL;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	if (state) {
4718c2ecf20Sopenharmony_ci		mali_state = to_malidp_crtc_state(state);
4728c2ecf20Sopenharmony_ci		__drm_atomic_helper_crtc_destroy_state(state);
4738c2ecf20Sopenharmony_ci	}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	kfree(mali_state);
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_cistatic void malidp_crtc_reset(struct drm_crtc *crtc)
4798c2ecf20Sopenharmony_ci{
4808c2ecf20Sopenharmony_ci	struct malidp_crtc_state *state =
4818c2ecf20Sopenharmony_ci		kzalloc(sizeof(*state), GFP_KERNEL);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	if (crtc->state)
4848c2ecf20Sopenharmony_ci		malidp_crtc_destroy_state(crtc, crtc->state);
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	if (state)
4878c2ecf20Sopenharmony_ci		__drm_atomic_helper_crtc_reset(crtc, &state->base);
4888c2ecf20Sopenharmony_ci	else
4898c2ecf20Sopenharmony_ci		__drm_atomic_helper_crtc_reset(crtc, NULL);
4908c2ecf20Sopenharmony_ci}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_cistatic int malidp_crtc_enable_vblank(struct drm_crtc *crtc)
4938c2ecf20Sopenharmony_ci{
4948c2ecf20Sopenharmony_ci	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
4958c2ecf20Sopenharmony_ci	struct malidp_hw_device *hwdev = malidp->dev;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	malidp_hw_enable_irq(hwdev, MALIDP_DE_BLOCK,
4988c2ecf20Sopenharmony_ci			     hwdev->hw->map.de_irq_map.vsync_irq);
4998c2ecf20Sopenharmony_ci	return 0;
5008c2ecf20Sopenharmony_ci}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_cistatic void malidp_crtc_disable_vblank(struct drm_crtc *crtc)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
5058c2ecf20Sopenharmony_ci	struct malidp_hw_device *hwdev = malidp->dev;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	malidp_hw_disable_irq(hwdev, MALIDP_DE_BLOCK,
5088c2ecf20Sopenharmony_ci			      hwdev->hw->map.de_irq_map.vsync_irq);
5098c2ecf20Sopenharmony_ci}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_cistatic const struct drm_crtc_funcs malidp_crtc_funcs = {
5128c2ecf20Sopenharmony_ci	.gamma_set = drm_atomic_helper_legacy_gamma_set,
5138c2ecf20Sopenharmony_ci	.destroy = drm_crtc_cleanup,
5148c2ecf20Sopenharmony_ci	.set_config = drm_atomic_helper_set_config,
5158c2ecf20Sopenharmony_ci	.page_flip = drm_atomic_helper_page_flip,
5168c2ecf20Sopenharmony_ci	.reset = malidp_crtc_reset,
5178c2ecf20Sopenharmony_ci	.atomic_duplicate_state = malidp_crtc_duplicate_state,
5188c2ecf20Sopenharmony_ci	.atomic_destroy_state = malidp_crtc_destroy_state,
5198c2ecf20Sopenharmony_ci	.enable_vblank = malidp_crtc_enable_vblank,
5208c2ecf20Sopenharmony_ci	.disable_vblank = malidp_crtc_disable_vblank,
5218c2ecf20Sopenharmony_ci};
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ciint malidp_crtc_init(struct drm_device *drm)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	struct malidp_drm *malidp = drm->dev_private;
5268c2ecf20Sopenharmony_ci	struct drm_plane *primary = NULL, *plane;
5278c2ecf20Sopenharmony_ci	int ret;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	ret = malidp_de_planes_init(drm);
5308c2ecf20Sopenharmony_ci	if (ret < 0) {
5318c2ecf20Sopenharmony_ci		DRM_ERROR("Failed to initialise planes\n");
5328c2ecf20Sopenharmony_ci		return ret;
5338c2ecf20Sopenharmony_ci	}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	drm_for_each_plane(plane, drm) {
5368c2ecf20Sopenharmony_ci		if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
5378c2ecf20Sopenharmony_ci			primary = plane;
5388c2ecf20Sopenharmony_ci			break;
5398c2ecf20Sopenharmony_ci		}
5408c2ecf20Sopenharmony_ci	}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	if (!primary) {
5438c2ecf20Sopenharmony_ci		DRM_ERROR("no primary plane found\n");
5448c2ecf20Sopenharmony_ci		return -EINVAL;
5458c2ecf20Sopenharmony_ci	}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	ret = drm_crtc_init_with_planes(drm, &malidp->crtc, primary, NULL,
5488c2ecf20Sopenharmony_ci					&malidp_crtc_funcs, NULL);
5498c2ecf20Sopenharmony_ci	if (ret)
5508c2ecf20Sopenharmony_ci		return ret;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	drm_crtc_helper_add(&malidp->crtc, &malidp_crtc_helper_funcs);
5538c2ecf20Sopenharmony_ci	drm_mode_crtc_set_gamma_size(&malidp->crtc, MALIDP_GAMMA_LUT_SIZE);
5548c2ecf20Sopenharmony_ci	/* No inverse-gamma: it is per-plane. */
5558c2ecf20Sopenharmony_ci	drm_crtc_enable_color_mgmt(&malidp->crtc, 0, true, MALIDP_GAMMA_LUT_SIZE);
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	malidp_se_set_enh_coeffs(malidp->dev);
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	return 0;
5608c2ecf20Sopenharmony_ci}
561