xref: /linux/drivers/gpu/drm/i915/display/intel_casf.c (revision 24f171c7e145f43b9f187578e89b0982ce87e54c)
174ad9ec9SNemesa Garg // SPDX-License-Identifier: MIT
274ad9ec9SNemesa Garg /* Copyright © 2025 Intel Corporation */
374ad9ec9SNemesa Garg 
474ad9ec9SNemesa Garg #include <drm/drm_print.h>
574ad9ec9SNemesa Garg 
674ad9ec9SNemesa Garg #include "i915_reg.h"
774ad9ec9SNemesa Garg #include "intel_casf.h"
874ad9ec9SNemesa Garg #include "intel_casf_regs.h"
974ad9ec9SNemesa Garg #include "intel_de.h"
1074ad9ec9SNemesa Garg #include "intel_display_regs.h"
1174ad9ec9SNemesa Garg #include "intel_display_types.h"
1282860cbaSNemesa Garg #include "skl_scaler.h"
1374ad9ec9SNemesa Garg 
1474ad9ec9SNemesa Garg #define MAX_PIXELS_FOR_3_TAP_FILTER (1920 * 1080)
1574ad9ec9SNemesa Garg #define MAX_PIXELS_FOR_5_TAP_FILTER (3840 * 2160)
1674ad9ec9SNemesa Garg 
17515d1c89SNemesa Garg #define FILTER_COEFF_0_125 125
18515d1c89SNemesa Garg #define FILTER_COEFF_0_25 250
19515d1c89SNemesa Garg #define FILTER_COEFF_0_5 500
20515d1c89SNemesa Garg #define FILTER_COEFF_1_0 1000
21515d1c89SNemesa Garg #define FILTER_COEFF_0_0 0
22515d1c89SNemesa Garg #define SET_POSITIVE_SIGN(x) ((x) & (~SIGN))
23515d1c89SNemesa Garg 
2474ad9ec9SNemesa Garg /**
2574ad9ec9SNemesa Garg  * DOC: Content Adaptive Sharpness Filter (CASF)
2674ad9ec9SNemesa Garg  *
2774ad9ec9SNemesa Garg  * Starting from LNL the display engine supports an
2874ad9ec9SNemesa Garg  * adaptive sharpening filter, enhancing the image
2974ad9ec9SNemesa Garg  * quality. The display hardware utilizes the second
3074ad9ec9SNemesa Garg  * pipe scaler for implementing CASF.
3174ad9ec9SNemesa Garg  * If sharpness is being enabled then pipe scaling
3274ad9ec9SNemesa Garg  * cannot be used.
3374ad9ec9SNemesa Garg  * This filter operates on a region of pixels based
3474ad9ec9SNemesa Garg  * on the tap size. Coefficients are used to generate
3574ad9ec9SNemesa Garg  * an alpha value which blends the sharpened image to
3674ad9ec9SNemesa Garg  * original image.
3774ad9ec9SNemesa Garg  */
3874ad9ec9SNemesa Garg 
39515d1c89SNemesa Garg /* Default LUT values to be loaded one time. */
40515d1c89SNemesa Garg static const u16 sharpness_lut[] = {
41515d1c89SNemesa Garg 	4095, 2047, 1364, 1022, 816, 678, 579,
42515d1c89SNemesa Garg 	504, 444, 397, 357, 323, 293, 268, 244, 224,
43515d1c89SNemesa Garg 	204, 187, 170, 154, 139, 125, 111, 98, 85,
44515d1c89SNemesa Garg 	73, 60, 48, 36, 24, 12, 0
45515d1c89SNemesa Garg };
46515d1c89SNemesa Garg 
47515d1c89SNemesa Garg const u16 filtercoeff_1[] = {
48515d1c89SNemesa Garg 	FILTER_COEFF_0_0, FILTER_COEFF_0_0, FILTER_COEFF_0_5,
49515d1c89SNemesa Garg 	FILTER_COEFF_1_0, FILTER_COEFF_0_5, FILTER_COEFF_0_0,
50515d1c89SNemesa Garg 	FILTER_COEFF_0_0,
51515d1c89SNemesa Garg };
52515d1c89SNemesa Garg 
53515d1c89SNemesa Garg const u16 filtercoeff_2[] = {
54515d1c89SNemesa Garg 	FILTER_COEFF_0_0, FILTER_COEFF_0_25, FILTER_COEFF_0_5,
55515d1c89SNemesa Garg 	FILTER_COEFF_1_0, FILTER_COEFF_0_5, FILTER_COEFF_0_25,
56515d1c89SNemesa Garg 	FILTER_COEFF_0_0,
57515d1c89SNemesa Garg };
58515d1c89SNemesa Garg 
59515d1c89SNemesa Garg const u16 filtercoeff_3[] = {
60515d1c89SNemesa Garg 	FILTER_COEFF_0_125, FILTER_COEFF_0_25, FILTER_COEFF_0_5,
61515d1c89SNemesa Garg 	FILTER_COEFF_1_0, FILTER_COEFF_0_5, FILTER_COEFF_0_25,
62515d1c89SNemesa Garg 	FILTER_COEFF_0_125,
63515d1c89SNemesa Garg };
64515d1c89SNemesa Garg 
65515d1c89SNemesa Garg static void intel_casf_filter_lut_load(struct intel_crtc *crtc,
66515d1c89SNemesa Garg 				       const struct intel_crtc_state *crtc_state)
67515d1c89SNemesa Garg {
68515d1c89SNemesa Garg 	struct intel_display *display = to_intel_display(crtc_state);
69515d1c89SNemesa Garg 	int i;
70515d1c89SNemesa Garg 
71515d1c89SNemesa Garg 	intel_de_write(display, SHRPLUT_INDEX(crtc->pipe),
72515d1c89SNemesa Garg 		       INDEX_AUTO_INCR | INDEX_VALUE(0));
73515d1c89SNemesa Garg 
74515d1c89SNemesa Garg 	for (i = 0; i < ARRAY_SIZE(sharpness_lut); i++)
75515d1c89SNemesa Garg 		intel_de_write(display, SHRPLUT_DATA(crtc->pipe),
76515d1c89SNemesa Garg 			       sharpness_lut[i]);
77515d1c89SNemesa Garg }
78515d1c89SNemesa Garg 
7974ad9ec9SNemesa Garg void intel_casf_update_strength(struct intel_crtc_state *crtc_state)
8074ad9ec9SNemesa Garg {
8174ad9ec9SNemesa Garg 	struct intel_display *display = to_intel_display(crtc_state);
8274ad9ec9SNemesa Garg 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
8374ad9ec9SNemesa Garg 	int win_size;
8474ad9ec9SNemesa Garg 
8574ad9ec9SNemesa Garg 	intel_de_rmw(display, SHARPNESS_CTL(crtc->pipe), FILTER_STRENGTH_MASK,
8674ad9ec9SNemesa Garg 		     FILTER_STRENGTH(crtc_state->hw.casf_params.strength));
8774ad9ec9SNemesa Garg 
8874ad9ec9SNemesa Garg 	win_size = intel_de_read(display, SKL_PS_WIN_SZ(crtc->pipe, 1));
8974ad9ec9SNemesa Garg 
9074ad9ec9SNemesa Garg 	intel_de_write_fw(display, SKL_PS_WIN_SZ(crtc->pipe, 1), win_size);
9174ad9ec9SNemesa Garg }
9274ad9ec9SNemesa Garg 
9374ad9ec9SNemesa Garg static void intel_casf_compute_win_size(struct intel_crtc_state *crtc_state)
9474ad9ec9SNemesa Garg {
9574ad9ec9SNemesa Garg 	const struct drm_display_mode *mode = &crtc_state->hw.adjusted_mode;
9674ad9ec9SNemesa Garg 	u32 total_pixels = mode->hdisplay * mode->vdisplay;
9774ad9ec9SNemesa Garg 
9874ad9ec9SNemesa Garg 	if (total_pixels <= MAX_PIXELS_FOR_3_TAP_FILTER)
9974ad9ec9SNemesa Garg 		crtc_state->hw.casf_params.win_size = SHARPNESS_FILTER_SIZE_3X3;
10074ad9ec9SNemesa Garg 	else if (total_pixels <= MAX_PIXELS_FOR_5_TAP_FILTER)
10174ad9ec9SNemesa Garg 		crtc_state->hw.casf_params.win_size = SHARPNESS_FILTER_SIZE_5X5;
10274ad9ec9SNemesa Garg 	else
10374ad9ec9SNemesa Garg 		crtc_state->hw.casf_params.win_size = SHARPNESS_FILTER_SIZE_7X7;
10474ad9ec9SNemesa Garg }
10574ad9ec9SNemesa Garg 
10674ad9ec9SNemesa Garg int intel_casf_compute_config(struct intel_crtc_state *crtc_state)
10774ad9ec9SNemesa Garg {
10874ad9ec9SNemesa Garg 	struct intel_display *display = to_intel_display(crtc_state);
10974ad9ec9SNemesa Garg 
11074ad9ec9SNemesa Garg 	if (!HAS_CASF(display))
11174ad9ec9SNemesa Garg 		return 0;
11274ad9ec9SNemesa Garg 
11374ad9ec9SNemesa Garg 	if (crtc_state->uapi.sharpness_strength == 0) {
11474ad9ec9SNemesa Garg 		crtc_state->hw.casf_params.casf_enable = false;
11574ad9ec9SNemesa Garg 		crtc_state->hw.casf_params.strength = 0;
11674ad9ec9SNemesa Garg 		return 0;
11774ad9ec9SNemesa Garg 	}
11874ad9ec9SNemesa Garg 
11974ad9ec9SNemesa Garg 	crtc_state->hw.casf_params.casf_enable = true;
12074ad9ec9SNemesa Garg 
12174ad9ec9SNemesa Garg 	/*
12274ad9ec9SNemesa Garg 	 * HW takes a value in form (1.0 + strength) in 4.4 fixed format.
12374ad9ec9SNemesa Garg 	 * Strength is from 0.0-14.9375 ie from 0-239.
12474ad9ec9SNemesa Garg 	 * User can give value from 0-255 but is clamped to 239.
12574ad9ec9SNemesa Garg 	 * Ex. User gives 85 which is 5.3125 and adding 1.0 gives 6.3125.
12674ad9ec9SNemesa Garg 	 * 6.3125 in 4.4 format is b01100101 which is equal to 101.
12774ad9ec9SNemesa Garg 	 * Also 85 + 16 = 101.
12874ad9ec9SNemesa Garg 	 */
12974ad9ec9SNemesa Garg 	crtc_state->hw.casf_params.strength =
13074ad9ec9SNemesa Garg 		min(crtc_state->uapi.sharpness_strength, 0xEF) + 0x10;
13174ad9ec9SNemesa Garg 
13274ad9ec9SNemesa Garg 	intel_casf_compute_win_size(crtc_state);
13374ad9ec9SNemesa Garg 
13476f51cdcSNemesa Garg 	intel_casf_scaler_compute_config(crtc_state);
13576f51cdcSNemesa Garg 
13674ad9ec9SNemesa Garg 	return 0;
13774ad9ec9SNemesa Garg }
13874ad9ec9SNemesa Garg 
13974ad9ec9SNemesa Garg void intel_casf_sharpness_get_config(struct intel_crtc_state *crtc_state)
14074ad9ec9SNemesa Garg {
14174ad9ec9SNemesa Garg 	struct intel_display *display = to_intel_display(crtc_state);
14274ad9ec9SNemesa Garg 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
14374ad9ec9SNemesa Garg 	u32 sharp;
14474ad9ec9SNemesa Garg 
14574ad9ec9SNemesa Garg 	sharp = intel_de_read(display, SHARPNESS_CTL(crtc->pipe));
14674ad9ec9SNemesa Garg 	if (sharp & FILTER_EN) {
14774ad9ec9SNemesa Garg 		if (drm_WARN_ON(display->drm,
14874ad9ec9SNemesa Garg 				REG_FIELD_GET(FILTER_STRENGTH_MASK, sharp) < 16))
14974ad9ec9SNemesa Garg 			crtc_state->hw.casf_params.strength = 0;
15074ad9ec9SNemesa Garg 		else
15174ad9ec9SNemesa Garg 			crtc_state->hw.casf_params.strength =
15274ad9ec9SNemesa Garg 				REG_FIELD_GET(FILTER_STRENGTH_MASK, sharp);
15374ad9ec9SNemesa Garg 		crtc_state->hw.casf_params.casf_enable = true;
15474ad9ec9SNemesa Garg 		crtc_state->hw.casf_params.win_size =
15574ad9ec9SNemesa Garg 			REG_FIELD_GET(FILTER_SIZE_MASK, sharp);
15674ad9ec9SNemesa Garg 	}
15774ad9ec9SNemesa Garg }
15874ad9ec9SNemesa Garg 
159*0672cf98SNemesa Garg bool intel_casf_needs_scaler(const struct intel_crtc_state *crtc_state)
160*0672cf98SNemesa Garg {
161*0672cf98SNemesa Garg 	if (crtc_state->hw.casf_params.casf_enable)
162*0672cf98SNemesa Garg 		return true;
163*0672cf98SNemesa Garg 
164*0672cf98SNemesa Garg 	return false;
165*0672cf98SNemesa Garg }
166*0672cf98SNemesa Garg 
16776f51cdcSNemesa Garg static int casf_coeff_tap(int i)
16876f51cdcSNemesa Garg {
16976f51cdcSNemesa Garg 	return i % SCALER_FILTER_NUM_TAPS;
17076f51cdcSNemesa Garg }
17176f51cdcSNemesa Garg 
17276f51cdcSNemesa Garg static u32 casf_coeff(struct intel_crtc_state *crtc_state, int t)
17376f51cdcSNemesa Garg {
17476f51cdcSNemesa Garg 	struct scaler_filter_coeff value;
17576f51cdcSNemesa Garg 	u32 coeff;
17676f51cdcSNemesa Garg 
17776f51cdcSNemesa Garg 	value = crtc_state->hw.casf_params.coeff[t];
17876f51cdcSNemesa Garg 	value.sign = 0;
17976f51cdcSNemesa Garg 
18076f51cdcSNemesa Garg 	coeff = value.sign << 15 | value.exp << 12 | value.mantissa << 3;
18176f51cdcSNemesa Garg 	return coeff;
18276f51cdcSNemesa Garg }
18376f51cdcSNemesa Garg 
18476f51cdcSNemesa Garg /*
18576f51cdcSNemesa Garg  * 17 phase of 7 taps requires 119 coefficients in 60 dwords per set.
18676f51cdcSNemesa Garg  * To enable casf:  program scaler coefficients with the coeffients
18776f51cdcSNemesa Garg  * that are calculated and stored in hw.casf_params.coeff as per
18876f51cdcSNemesa Garg  * SCALER_COEFFICIENT_FORMAT
18976f51cdcSNemesa Garg  */
19076f51cdcSNemesa Garg static void intel_casf_write_coeff(struct intel_crtc_state *crtc_state)
19176f51cdcSNemesa Garg {
19276f51cdcSNemesa Garg 	struct intel_display *display = to_intel_display(crtc_state);
19376f51cdcSNemesa Garg 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
19476f51cdcSNemesa Garg 	int id = crtc_state->scaler_state.scaler_id;
19576f51cdcSNemesa Garg 	int i;
19676f51cdcSNemesa Garg 
19776f51cdcSNemesa Garg 	if (id != 1) {
19876f51cdcSNemesa Garg 		drm_WARN(display->drm, 0, "Second scaler not enabled\n");
19976f51cdcSNemesa Garg 		return;
20076f51cdcSNemesa Garg 	}
20176f51cdcSNemesa Garg 
20276f51cdcSNemesa Garg 	intel_de_write_fw(display, GLK_PS_COEF_INDEX_SET(crtc->pipe, id, 0),
20376f51cdcSNemesa Garg 			  PS_COEF_INDEX_AUTO_INC);
20476f51cdcSNemesa Garg 
20576f51cdcSNemesa Garg 	for (i = 0; i < 17 * SCALER_FILTER_NUM_TAPS; i += 2) {
20676f51cdcSNemesa Garg 		u32 tmp;
20776f51cdcSNemesa Garg 		int t;
20876f51cdcSNemesa Garg 
20976f51cdcSNemesa Garg 		t = casf_coeff_tap(i);
21076f51cdcSNemesa Garg 		tmp = casf_coeff(crtc_state, t);
21176f51cdcSNemesa Garg 
21276f51cdcSNemesa Garg 		t = casf_coeff_tap(i + 1);
21376f51cdcSNemesa Garg 		tmp |= casf_coeff(crtc_state, t) << 16;
21476f51cdcSNemesa Garg 
21576f51cdcSNemesa Garg 		intel_de_write_fw(display, GLK_PS_COEF_DATA_SET(crtc->pipe, id, 0),
21676f51cdcSNemesa Garg 				  tmp);
21776f51cdcSNemesa Garg 	}
21876f51cdcSNemesa Garg }
21976f51cdcSNemesa Garg 
22076f51cdcSNemesa Garg static void convert_sharpness_coef_binary(struct scaler_filter_coeff *coeff,
22176f51cdcSNemesa Garg 					  u16 coefficient)
22276f51cdcSNemesa Garg {
22376f51cdcSNemesa Garg 	if (coefficient < 25) {
22476f51cdcSNemesa Garg 		coeff->mantissa = (coefficient * 2048) / 100;
22576f51cdcSNemesa Garg 		coeff->exp = 3;
22676f51cdcSNemesa Garg 	} else if (coefficient < 50) {
22776f51cdcSNemesa Garg 		coeff->mantissa = (coefficient * 1024) / 100;
22876f51cdcSNemesa Garg 		coeff->exp = 2;
22976f51cdcSNemesa Garg 	} else if (coefficient < 100) {
23076f51cdcSNemesa Garg 		coeff->mantissa = (coefficient * 512) / 100;
23176f51cdcSNemesa Garg 		coeff->exp = 1;
23276f51cdcSNemesa Garg 	} else {
23376f51cdcSNemesa Garg 		coeff->mantissa = (coefficient * 256) / 100;
23476f51cdcSNemesa Garg 		coeff->exp = 0;
23576f51cdcSNemesa Garg 	}
23676f51cdcSNemesa Garg }
23776f51cdcSNemesa Garg 
23876f51cdcSNemesa Garg void intel_casf_scaler_compute_config(struct intel_crtc_state *crtc_state)
23976f51cdcSNemesa Garg {
24076f51cdcSNemesa Garg 	const u16 *filtercoeff;
24176f51cdcSNemesa Garg 	u16 filter_coeff[SCALER_FILTER_NUM_TAPS];
24276f51cdcSNemesa Garg 	u16 sumcoeff = 0;
24376f51cdcSNemesa Garg 	int i;
24476f51cdcSNemesa Garg 
24576f51cdcSNemesa Garg 	if (crtc_state->hw.casf_params.win_size == 0)
24676f51cdcSNemesa Garg 		filtercoeff = filtercoeff_1;
24776f51cdcSNemesa Garg 	else if (crtc_state->hw.casf_params.win_size == 1)
24876f51cdcSNemesa Garg 		filtercoeff = filtercoeff_2;
24976f51cdcSNemesa Garg 	else
25076f51cdcSNemesa Garg 		filtercoeff = filtercoeff_3;
25176f51cdcSNemesa Garg 
25276f51cdcSNemesa Garg 	for (i = 0; i < SCALER_FILTER_NUM_TAPS; i++)
25376f51cdcSNemesa Garg 		sumcoeff += *(filtercoeff + i);
25476f51cdcSNemesa Garg 
25576f51cdcSNemesa Garg 	for (i = 0; i < SCALER_FILTER_NUM_TAPS; i++) {
25676f51cdcSNemesa Garg 		filter_coeff[i] = (*(filtercoeff + i) * 100 / sumcoeff);
25776f51cdcSNemesa Garg 		convert_sharpness_coef_binary(&crtc_state->hw.casf_params.coeff[i],
25876f51cdcSNemesa Garg 					      filter_coeff[i]);
25976f51cdcSNemesa Garg 	}
26076f51cdcSNemesa Garg }
26176f51cdcSNemesa Garg 
26274ad9ec9SNemesa Garg void intel_casf_enable(struct intel_crtc_state *crtc_state)
26374ad9ec9SNemesa Garg {
26474ad9ec9SNemesa Garg 	struct intel_display *display = to_intel_display(crtc_state);
26574ad9ec9SNemesa Garg 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
26674ad9ec9SNemesa Garg 	u32 sharpness_ctl;
26774ad9ec9SNemesa Garg 
268515d1c89SNemesa Garg 	intel_casf_filter_lut_load(crtc, crtc_state);
269515d1c89SNemesa Garg 
27076f51cdcSNemesa Garg 	intel_casf_write_coeff(crtc_state);
27176f51cdcSNemesa Garg 
27274ad9ec9SNemesa Garg 	sharpness_ctl = FILTER_EN | FILTER_STRENGTH(crtc_state->hw.casf_params.strength);
27374ad9ec9SNemesa Garg 
27474ad9ec9SNemesa Garg 	sharpness_ctl |= crtc_state->hw.casf_params.win_size;
27574ad9ec9SNemesa Garg 
27674ad9ec9SNemesa Garg 	intel_de_write(display, SHARPNESS_CTL(crtc->pipe), sharpness_ctl);
27782860cbaSNemesa Garg 
27882860cbaSNemesa Garg 	skl_scaler_setup_casf(crtc_state);
27974ad9ec9SNemesa Garg }
28074ad9ec9SNemesa Garg 
28174ad9ec9SNemesa Garg void intel_casf_disable(const struct intel_crtc_state *crtc_state)
28274ad9ec9SNemesa Garg {
28374ad9ec9SNemesa Garg 	struct intel_display *display = to_intel_display(crtc_state);
28474ad9ec9SNemesa Garg 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
28574ad9ec9SNemesa Garg 
28682860cbaSNemesa Garg 	intel_de_write(display, SKL_PS_CTRL(crtc->pipe, 1), 0);
28782860cbaSNemesa Garg 	intel_de_write(display, SKL_PS_WIN_POS(crtc->pipe, 1), 0);
28874ad9ec9SNemesa Garg 	intel_de_write(display, SHARPNESS_CTL(crtc->pipe), 0);
28982860cbaSNemesa Garg 	intel_de_write(display, SKL_PS_WIN_SZ(crtc->pipe, 1), 0);
29074ad9ec9SNemesa Garg }
291