summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/i915/display/intel_pfit.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/i915/display/intel_pfit.c')
-rw-r--r--drivers/gpu/drm/i915/display/intel_pfit.c172
1 files changed, 169 insertions, 3 deletions
diff --git a/drivers/gpu/drm/i915/display/intel_pfit.c b/drivers/gpu/drm/i915/display/intel_pfit.c
index 50861aa78a89..3c3ecf288570 100644
--- a/drivers/gpu/drm/i915/display/intel_pfit.c
+++ b/drivers/gpu/drm/i915/display/intel_pfit.c
@@ -3,13 +3,17 @@
* Copyright © 2024 Intel Corporation
*/
-#include "i915_drv.h"
+#include <drm/drm_print.h>
+
#include "i915_reg.h"
+#include "i915_utils.h"
+#include "intel_de.h"
#include "intel_display_core.h"
#include "intel_display_driver.h"
#include "intel_display_types.h"
#include "intel_lvds_regs.h"
#include "intel_pfit.h"
+#include "intel_pfit_regs.h"
static int intel_pch_pfit_check_dst_window(const struct intel_crtc_state *crtc_state)
{
@@ -542,8 +546,8 @@ out:
return intel_gmch_pfit_check_timings(crtc_state);
}
-int intel_panel_fitting(struct intel_crtc_state *crtc_state,
- const struct drm_connector_state *conn_state)
+int intel_pfit_compute_config(struct intel_crtc_state *crtc_state,
+ const struct drm_connector_state *conn_state)
{
struct intel_display *display = to_intel_display(crtc_state);
@@ -552,3 +556,165 @@ int intel_panel_fitting(struct intel_crtc_state *crtc_state,
else
return pch_panel_fitting(crtc_state, conn_state);
}
+
+void ilk_pfit_enable(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+ const struct drm_rect *dst = &crtc_state->pch_pfit.dst;
+ enum pipe pipe = crtc->pipe;
+ int width = drm_rect_width(dst);
+ int height = drm_rect_height(dst);
+ int x = dst->x1;
+ int y = dst->y1;
+
+ if (!crtc_state->pch_pfit.enabled)
+ return;
+
+ /*
+ * Force use of hard-coded filter coefficients as some pre-programmed
+ * values are broken, e.g. x201.
+ */
+ if (display->platform.ivybridge || display->platform.haswell)
+ intel_de_write_fw(display, PF_CTL(pipe), PF_ENABLE |
+ PF_FILTER_MED_3x3 | PF_PIPE_SEL_IVB(pipe));
+ else
+ intel_de_write_fw(display, PF_CTL(pipe), PF_ENABLE |
+ PF_FILTER_MED_3x3);
+ intel_de_write_fw(display, PF_WIN_POS(pipe),
+ PF_WIN_XPOS(x) | PF_WIN_YPOS(y));
+ intel_de_write_fw(display, PF_WIN_SZ(pipe),
+ PF_WIN_XSIZE(width) | PF_WIN_YSIZE(height));
+}
+
+void ilk_pfit_disable(const struct intel_crtc_state *old_crtc_state)
+{
+ struct intel_display *display = to_intel_display(old_crtc_state);
+ struct intel_crtc *crtc = to_intel_crtc(old_crtc_state->uapi.crtc);
+ enum pipe pipe = crtc->pipe;
+
+ /*
+ * To avoid upsetting the power well on haswell only disable the pfit if
+ * it's in use. The hw state code will make sure we get this right.
+ */
+ if (!old_crtc_state->pch_pfit.enabled)
+ return;
+
+ intel_de_write_fw(display, PF_CTL(pipe), 0);
+ intel_de_write_fw(display, PF_WIN_POS(pipe), 0);
+ intel_de_write_fw(display, PF_WIN_SZ(pipe), 0);
+}
+
+void ilk_pfit_get_config(struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+ u32 ctl, pos, size;
+ enum pipe pipe;
+
+ ctl = intel_de_read(display, PF_CTL(crtc->pipe));
+ if ((ctl & PF_ENABLE) == 0)
+ return;
+
+ if (display->platform.ivybridge || display->platform.haswell)
+ pipe = REG_FIELD_GET(PF_PIPE_SEL_MASK_IVB, ctl);
+ else
+ pipe = crtc->pipe;
+
+ crtc_state->pch_pfit.enabled = true;
+
+ pos = intel_de_read(display, PF_WIN_POS(crtc->pipe));
+ size = intel_de_read(display, PF_WIN_SZ(crtc->pipe));
+
+ drm_rect_init(&crtc_state->pch_pfit.dst,
+ REG_FIELD_GET(PF_WIN_XPOS_MASK, pos),
+ REG_FIELD_GET(PF_WIN_YPOS_MASK, pos),
+ REG_FIELD_GET(PF_WIN_XSIZE_MASK, size),
+ REG_FIELD_GET(PF_WIN_YSIZE_MASK, size));
+
+ /*
+ * We currently do not free assignments of panel fitters on
+ * ivb/hsw (since we don't use the higher upscaling modes which
+ * differentiates them) so just WARN about this case for now.
+ */
+ drm_WARN_ON(display->drm, pipe != crtc->pipe);
+}
+
+void i9xx_pfit_enable(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+
+ if (!crtc_state->gmch_pfit.control)
+ return;
+
+ /*
+ * The panel fitter should only be adjusted whilst the pipe is disabled,
+ * according to register description and PRM.
+ */
+ drm_WARN_ON(display->drm,
+ intel_de_read(display, PFIT_CONTROL(display)) & PFIT_ENABLE);
+ assert_transcoder_disabled(display, crtc_state->cpu_transcoder);
+
+ intel_de_write(display, PFIT_PGM_RATIOS(display),
+ crtc_state->gmch_pfit.pgm_ratios);
+ intel_de_write(display, PFIT_CONTROL(display),
+ crtc_state->gmch_pfit.control);
+
+ /*
+ * Border color in case we don't scale up to the full screen. Black by
+ * default, change to something else for debugging.
+ */
+ intel_de_write(display, BCLRPAT(display, crtc->pipe), 0);
+}
+
+void i9xx_pfit_disable(const struct intel_crtc_state *old_crtc_state)
+{
+ struct intel_display *display = to_intel_display(old_crtc_state);
+
+ if (!old_crtc_state->gmch_pfit.control)
+ return;
+
+ assert_transcoder_disabled(display, old_crtc_state->cpu_transcoder);
+
+ drm_dbg_kms(display->drm, "disabling pfit, current: 0x%08x\n",
+ intel_de_read(display, PFIT_CONTROL(display)));
+ intel_de_write(display, PFIT_CONTROL(display), 0);
+}
+
+static bool i9xx_has_pfit(struct intel_display *display)
+{
+ if (display->platform.i830)
+ return false;
+
+ return DISPLAY_VER(display) >= 4 ||
+ display->platform.pineview || display->platform.mobile;
+}
+
+void i9xx_pfit_get_config(struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+ enum pipe pipe;
+ u32 tmp;
+
+ if (!i9xx_has_pfit(display))
+ return;
+
+ tmp = intel_de_read(display, PFIT_CONTROL(display));
+ if (!(tmp & PFIT_ENABLE))
+ return;
+
+ /* Check whether the pfit is attached to our pipe. */
+ if (DISPLAY_VER(display) >= 4)
+ pipe = REG_FIELD_GET(PFIT_PIPE_MASK, tmp);
+ else
+ pipe = PIPE_B;
+
+ if (pipe != crtc->pipe)
+ return;
+
+ crtc_state->gmch_pfit.control = tmp;
+ crtc_state->gmch_pfit.pgm_ratios =
+ intel_de_read(display, PFIT_PGM_RATIOS(display));
+}