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