[PATCH v3 041/105] drm/vc4: crtc: Move HVS mode config to HVS file
From: Maxime Ripard
Date: Wed May 27 2020 - 11:56:09 EST
Signed-off-by: Maxime Ripard <maxime@xxxxxxxxxx>
---
drivers/gpu/drm/vc4/vc4_crtc.c | 272 +-------------------------------
drivers/gpu/drm/vc4/vc4_drv.h | 5 +-
drivers/gpu/drm/vc4/vc4_hvs.c | 298 ++++++++++++++++++++++++++++++++++-
3 files changed, 309 insertions(+), 266 deletions(-)
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index 983ae476c203..93161b98e22a 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -206,48 +206,6 @@ static void vc4_crtc_destroy(struct drm_crtc *crtc)
drm_crtc_cleanup(crtc);
}
-static void
-vc4_crtc_lut_load(struct drm_crtc *crtc)
-{
- struct drm_device *dev = crtc->dev;
- struct vc4_dev *vc4 = to_vc4_dev(dev);
- struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
- struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(crtc->state);
- u32 i;
-
- /* The LUT memory is laid out with each HVS channel in order,
- * each of which takes 256 writes for R, 256 for G, then 256
- * for B.
- */
- HVS_WRITE(SCALER_GAMADDR,
- SCALER_GAMADDR_AUTOINC |
- (vc4_crtc_state->assigned_channel * 3 * crtc->gamma_size));
-
- for (i = 0; i < crtc->gamma_size; i++)
- HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_r[i]);
- for (i = 0; i < crtc->gamma_size; i++)
- HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_g[i]);
- for (i = 0; i < crtc->gamma_size; i++)
- HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_b[i]);
-}
-
-static void
-vc4_crtc_update_gamma_lut(struct drm_crtc *crtc)
-{
- struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
- struct drm_color_lut *lut = crtc->state->gamma_lut->data;
- u32 length = drm_color_lut_size(crtc->state->gamma_lut);
- u32 i;
-
- for (i = 0; i < length; i++) {
- vc4_crtc->lut_r[i] = drm_color_lut_extract(lut[i].red, 8);
- vc4_crtc->lut_g[i] = drm_color_lut_extract(lut[i].green, 8);
- vc4_crtc->lut_b[i] = drm_color_lut_extract(lut[i].blue, 8);
- }
-
- vc4_crtc_lut_load(crtc);
-}
-
static u32 vc4_get_fifo_full_level(struct vc4_crtc *vc4_crtc, u32 format)
{
u32 fifo_len_bytes = vc4_crtc->data->fifo_depth;
@@ -403,12 +361,8 @@ static void vc4_crtc_config_pv(struct drm_crtc *crtc)
static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
{
- struct drm_device *dev = crtc->dev;
- struct vc4_dev *vc4 = to_vc4_dev(dev);
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
- struct drm_display_mode *mode = &crtc->state->adjusted_mode;
- bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE;
bool debug_dump_regs = false;
if (debug_dump_regs) {
@@ -418,42 +372,10 @@ static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
drm_print_regset32(&p, &vc4_crtc->regset);
}
- if (vc4_crtc->data->hvs_output == 2) {
- u32 dispctrl;
- u32 dsp3_mux;
-
- /*
- * SCALER_DISPCTRL_DSP3 = X, where X < 2 means 'connect DSP3 to
- * FIFO X'.
- * SCALER_DISPCTRL_DSP3 = 3 means 'disable DSP 3'.
- *
- * DSP3 is connected to FIFO2 unless the transposer is
- * enabled. In this case, FIFO 2 is directly accessed by the
- * TXP IP, and we need to disable the FIFO2 -> pixelvalve1
- * route.
- */
- if (vc4_state->feed_txp)
- dsp3_mux = VC4_SET_FIELD(3, SCALER_DISPCTRL_DSP3_MUX);
- else
- dsp3_mux = VC4_SET_FIELD(2, SCALER_DISPCTRL_DSP3_MUX);
-
- dispctrl = HVS_READ(SCALER_DISPCTRL) &
- ~SCALER_DISPCTRL_DSP3_MUX_MASK;
- HVS_WRITE(SCALER_DISPCTRL, dispctrl | dsp3_mux);
- }
-
if (!vc4_state->feed_txp)
vc4_crtc_config_pv(crtc);
- HVS_WRITE(SCALER_DISPBKGNDX(vc4_state->assigned_channel),
- SCALER_DISPBKGND_AUTOHS |
- ((!vc4->hvs->hvs5) ? SCALER_DISPBKGND_GAMMA : 0) |
- (interlace ? SCALER_DISPBKGND_INTERLACE : 0));
-
- /* Reload the LUT, since the SRAMs would have been disabled if
- * all CRTCs had SCALER_DISPBKGND_GAMMA unset at once.
- */
- vc4_crtc_lut_load(crtc);
+ vc4_hvs_mode_set_nofb(crtc);
if (debug_dump_regs) {
struct drm_printer p = drm_info_printer(&vc4_crtc->pdev->dev);
@@ -475,11 +397,9 @@ static void vc4_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
struct drm_device *dev = crtc->dev;
- struct vc4_dev *vc4 = to_vc4_dev(dev);
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
- struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(old_state);
- u32 chan = vc4_crtc_state->assigned_channel;
int ret;
+
require_hvs_enabled(dev);
/* Disable vblank irq handling before crtc is disabled. */
@@ -492,28 +412,7 @@ static void vc4_crtc_atomic_disable(struct drm_crtc *crtc,
CRTC_WRITE(PV_CONTROL, CRTC_READ(PV_CONTROL) & ~PV_CONTROL_EN);
- if (HVS_READ(SCALER_DISPCTRLX(chan)) &
- SCALER_DISPCTRLX_ENABLE) {
- HVS_WRITE(SCALER_DISPCTRLX(chan),
- SCALER_DISPCTRLX_RESET);
-
- /* While the docs say that reset is self-clearing, it
- * seems it doesn't actually.
- */
- HVS_WRITE(SCALER_DISPCTRLX(chan), 0);
- }
-
- /* Once we leave, the scaler should be disabled and its fifo empty. */
-
- WARN_ON_ONCE(HVS_READ(SCALER_DISPCTRLX(chan)) & SCALER_DISPCTRLX_RESET);
-
- WARN_ON_ONCE(VC4_GET_FIELD(HVS_READ(SCALER_DISPSTATX(chan)),
- SCALER_DISPSTATX_MODE) !=
- SCALER_DISPSTATX_MODE_DISABLED);
-
- WARN_ON_ONCE((HVS_READ(SCALER_DISPSTATX(chan)) &
- (SCALER_DISPSTATX_FULL | SCALER_DISPSTATX_EMPTY)) !=
- SCALER_DISPSTATX_EMPTY);
+ vc4_hvs_atomic_disable(crtc, old_state);
/*
* Make sure we issue a vblank event after disabling the CRTC if
@@ -536,46 +435,12 @@ void vc4_crtc_txp_armed(struct drm_crtc_state *state)
vc4_state->txp_armed = true;
}
-static void vc4_crtc_update_dlist(struct drm_crtc *crtc)
-{
- struct drm_device *dev = crtc->dev;
- struct vc4_dev *vc4 = to_vc4_dev(dev);
- struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
- struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
-
- if (crtc->state->event) {
- unsigned long flags;
-
- crtc->state->event->pipe = drm_crtc_index(crtc);
-
- WARN_ON(drm_crtc_vblank_get(crtc) != 0);
-
- spin_lock_irqsave(&dev->event_lock, flags);
-
- if (!vc4_state->feed_txp || vc4_state->txp_armed) {
- vc4_crtc->event = crtc->state->event;
- crtc->state->event = NULL;
- }
-
- HVS_WRITE(SCALER_DISPLISTX(vc4_state->assigned_channel),
- vc4_state->mm.start);
-
- spin_unlock_irqrestore(&dev->event_lock, flags);
- } else {
- HVS_WRITE(SCALER_DISPLISTX(vc4_state->assigned_channel),
- vc4_state->mm.start);
- }
-}
-
static void vc4_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
struct drm_device *dev = crtc->dev;
- struct vc4_dev *vc4 = to_vc4_dev(dev);
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
- struct drm_display_mode *mode = &crtc->state->adjusted_mode;
- u32 dispctrl;
require_hvs_enabled(dev);
@@ -587,31 +452,8 @@ static void vc4_crtc_atomic_enable(struct drm_crtc *crtc,
* drm_crtc_get_vblank() fails in vc4_crtc_update_dlist().
*/
drm_crtc_vblank_on(crtc);
- vc4_crtc_update_dlist(crtc);
-
- /* Turn on the scaler, which will wait for vstart to start
- * compositing.
- * When feeding the transposer, we should operate in oneshot
- * mode.
- */
- dispctrl = SCALER_DISPCTRLX_ENABLE;
-
- if (!vc4->hvs->hvs5)
- dispctrl |= VC4_SET_FIELD(mode->hdisplay,
- SCALER_DISPCTRLX_WIDTH) |
- VC4_SET_FIELD(mode->vdisplay,
- SCALER_DISPCTRLX_HEIGHT) |
- (vc4_state->feed_txp ?
- SCALER_DISPCTRLX_ONESHOT : 0);
- else
- dispctrl |= VC4_SET_FIELD(mode->hdisplay,
- SCALER5_DISPCTRLX_WIDTH) |
- VC4_SET_FIELD(mode->vdisplay,
- SCALER5_DISPCTRLX_HEIGHT) |
- (vc4_state->feed_txp ?
- SCALER5_DISPCTRLX_ONESHOT : 0);
- HVS_WRITE(SCALER_DISPCTRLX(vc4_state->assigned_channel), dispctrl);
+ vc4_hvs_atomic_enable(crtc, old_state);
/* When feeding the transposer block the pixelvalve is unneeded and
* should not be enabled.
@@ -669,31 +511,11 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
- struct drm_device *dev = crtc->dev;
- struct vc4_dev *vc4 = to_vc4_dev(dev);
- struct drm_plane *plane;
- unsigned long flags;
- const struct drm_plane_state *plane_state;
struct drm_connector *conn;
struct drm_connector_state *conn_state;
- u32 dlist_count = 0;
int ret, i;
- /* The pixelvalve can only feed one encoder (and encoders are
- * 1:1 with connectors.)
- */
- if (hweight32(state->connector_mask) > 1)
- return -EINVAL;
-
- drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, state)
- dlist_count += vc4_plane_dlist_size(plane_state);
-
- dlist_count++; /* Account for SCALER_CTL0_END. */
-
- spin_lock_irqsave(&vc4->hvs->mm_lock, flags);
- ret = drm_mm_insert_node(&vc4->hvs->dlist_mm, &vc4_state->mm,
- dlist_count);
- spin_unlock_irqrestore(&vc4->hvs->mm_lock, flags);
+ ret = vc4_hvs_atomic_check(crtc, state);
if (ret)
return ret;
@@ -722,88 +544,6 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
return 0;
}
-static void vc4_crtc_atomic_flush(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state)
-{
- struct drm_device *dev = crtc->dev;
- struct vc4_dev *vc4 = to_vc4_dev(dev);
- struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
- struct drm_plane *plane;
- struct vc4_plane_state *vc4_plane_state;
- bool debug_dump_regs = false;
- bool enable_bg_fill = false;
- u32 __iomem *dlist_start = vc4->hvs->dlist + vc4_state->mm.start;
- u32 __iomem *dlist_next = dlist_start;
-
- if (debug_dump_regs) {
- DRM_INFO("CRTC %d HVS before:\n", drm_crtc_index(crtc));
- vc4_hvs_dump_state(dev);
- }
-
- /* Copy all the active planes' dlist contents to the hardware dlist. */
- drm_atomic_crtc_for_each_plane(plane, crtc) {
- /* Is this the first active plane? */
- if (dlist_next == dlist_start) {
- /* We need to enable background fill when a plane
- * could be alpha blending from the background, i.e.
- * where no other plane is underneath. It suffices to
- * consider the first active plane here since we set
- * needs_bg_fill such that either the first plane
- * already needs it or all planes on top blend from
- * the first or a lower plane.
- */
- vc4_plane_state = to_vc4_plane_state(plane->state);
- enable_bg_fill = vc4_plane_state->needs_bg_fill;
- }
-
- dlist_next += vc4_plane_write_dlist(plane, dlist_next);
- }
-
- writel(SCALER_CTL0_END, dlist_next);
- dlist_next++;
-
- WARN_ON_ONCE(dlist_next - dlist_start != vc4_state->mm.size);
-
- if (enable_bg_fill)
- /* This sets a black background color fill, as is the case
- * with other DRM drivers.
- */
- HVS_WRITE(SCALER_DISPBKGNDX(vc4_state->assigned_channel),
- HVS_READ(SCALER_DISPBKGNDX(vc4_state->assigned_channel)) |
- SCALER_DISPBKGND_FILL);
-
- /* Only update DISPLIST if the CRTC was already running and is not
- * being disabled.
- * vc4_crtc_enable() takes care of updating the dlist just after
- * re-enabling VBLANK interrupts and before enabling the engine.
- * If the CRTC is being disabled, there's no point in updating this
- * information.
- */
- if (crtc->state->active && old_state->active)
- vc4_crtc_update_dlist(crtc);
-
- if (crtc->state->color_mgmt_changed) {
- u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(vc4_state->assigned_channel));
-
- if (crtc->state->gamma_lut) {
- vc4_crtc_update_gamma_lut(crtc);
- dispbkgndx |= SCALER_DISPBKGND_GAMMA;
- } else {
- /* Unsetting DISPBKGND_GAMMA skips the gamma lut step
- * in hardware, which is the same as a linear lut that
- * DRM expects us to use in absence of a user lut.
- */
- dispbkgndx &= ~SCALER_DISPBKGND_GAMMA;
- }
- HVS_WRITE(SCALER_DISPBKGNDX(vc4_state->assigned_channel), dispbkgndx);
- }
-
- if (debug_dump_regs) {
- DRM_INFO("CRTC %d HVS after:\n", drm_crtc_index(crtc));
- vc4_hvs_dump_state(dev);
- }
-}
-
static int vc4_enable_vblank(struct drm_crtc *crtc)
{
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
@@ -1080,7 +820,7 @@ static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = {
.mode_set_nofb = vc4_crtc_mode_set_nofb,
.mode_valid = vc4_crtc_mode_valid,
.atomic_check = vc4_crtc_atomic_check,
- .atomic_flush = vc4_crtc_atomic_flush,
+ .atomic_flush = vc4_hvs_atomic_flush,
.atomic_enable = vc4_crtc_atomic_enable,
.atomic_disable = vc4_crtc_atomic_disable,
.get_scanout_position = vc4_crtc_get_scanout_position,
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index 974cda3c5292..5520a22f8126 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -872,6 +872,11 @@ void vc4_irq_reset(struct drm_device *dev);
/* vc4_hvs.c */
extern struct platform_driver vc4_hvs_driver;
+int vc4_hvs_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state);
+void vc4_hvs_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state);
+void vc4_hvs_atomic_disable(struct drm_crtc *crtc, struct drm_crtc_state *old_state);
+void vc4_hvs_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *state);
+void vc4_hvs_mode_set_nofb(struct drm_crtc *crtc);
void vc4_hvs_dump_state(struct drm_device *dev);
void vc4_hvs_unmask_underrun(struct drm_device *dev, int channel);
void vc4_hvs_mask_underrun(struct drm_device *dev, int channel);
diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c
index f4942667355b..0cd63d817a7e 100644
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
@@ -24,6 +24,7 @@
#include <linux/platform_device.h>
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_vblank.h>
#include "vc4_drv.h"
#include "vc4_regs.h"
@@ -155,6 +156,303 @@ static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs,
return 0;
}
+static void vc4_hvs_lut_load(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+ struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(crtc->state);
+ u32 i;
+
+ /* The LUT memory is laid out with each HVS channel in order,
+ * each of which takes 256 writes for R, 256 for G, then 256
+ * for B.
+ */
+ HVS_WRITE(SCALER_GAMADDR,
+ SCALER_GAMADDR_AUTOINC |
+ (vc4_crtc_state->assigned_channel * 3 * crtc->gamma_size));
+
+ for (i = 0; i < crtc->gamma_size; i++)
+ HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_r[i]);
+ for (i = 0; i < crtc->gamma_size; i++)
+ HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_g[i]);
+ for (i = 0; i < crtc->gamma_size; i++)
+ HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_b[i]);
+}
+
+static void vc4_hvs_update_gamma_lut(struct drm_crtc *crtc)
+{
+ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+ struct drm_color_lut *lut = crtc->state->gamma_lut->data;
+ u32 length = drm_color_lut_size(crtc->state->gamma_lut);
+ u32 i;
+
+ for (i = 0; i < length; i++) {
+ vc4_crtc->lut_r[i] = drm_color_lut_extract(lut[i].red, 8);
+ vc4_crtc->lut_g[i] = drm_color_lut_extract(lut[i].green, 8);
+ vc4_crtc->lut_b[i] = drm_color_lut_extract(lut[i].blue, 8);
+ }
+
+ vc4_hvs_lut_load(crtc);
+}
+
+int vc4_hvs_atomic_check(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+{
+ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
+ struct drm_device *dev = crtc->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct drm_plane *plane;
+ unsigned long flags;
+ const struct drm_plane_state *plane_state;
+ u32 dlist_count = 0;
+ int ret;
+
+ /* The pixelvalve can only feed one encoder (and encoders are
+ * 1:1 with connectors.)
+ */
+ if (hweight32(state->connector_mask) > 1)
+ return -EINVAL;
+
+ drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, state)
+ dlist_count += vc4_plane_dlist_size(plane_state);
+
+ dlist_count++; /* Account for SCALER_CTL0_END. */
+
+ spin_lock_irqsave(&vc4->hvs->mm_lock, flags);
+ ret = drm_mm_insert_node(&vc4->hvs->dlist_mm, &vc4_state->mm,
+ dlist_count);
+ spin_unlock_irqrestore(&vc4->hvs->mm_lock, flags);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void vc4_hvs_update_dlist(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
+
+ if (crtc->state->event) {
+ unsigned long flags;
+
+ crtc->state->event->pipe = drm_crtc_index(crtc);
+
+ WARN_ON(drm_crtc_vblank_get(crtc) != 0);
+
+ spin_lock_irqsave(&dev->event_lock, flags);
+
+ if (!vc4_state->feed_txp || vc4_state->txp_armed) {
+ vc4_crtc->event = crtc->state->event;
+ crtc->state->event = NULL;
+ }
+
+ HVS_WRITE(SCALER_DISPLISTX(vc4_state->assigned_channel),
+ vc4_state->mm.start);
+
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+ } else {
+ HVS_WRITE(SCALER_DISPLISTX(vc4_state->assigned_channel),
+ vc4_state->mm.start);
+ }
+}
+
+void vc4_hvs_atomic_enable(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_state)
+{
+ struct drm_device *dev = crtc->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
+ struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+ bool oneshot = vc4_state->feed_txp;
+ u32 dispctrl;
+
+ vc4_hvs_update_dlist(crtc);
+
+ /* Turn on the scaler, which will wait for vstart to start
+ * compositing.
+ * When feeding the transposer, we should operate in oneshot
+ * mode.
+ */
+ dispctrl = SCALER_DISPCTRLX_ENABLE;
+
+ if (!vc4->hvs->hvs5)
+ dispctrl |= VC4_SET_FIELD(mode->hdisplay,
+ SCALER_DISPCTRLX_WIDTH) |
+ VC4_SET_FIELD(mode->vdisplay,
+ SCALER_DISPCTRLX_HEIGHT) |
+ (oneshot ? SCALER_DISPCTRLX_ONESHOT : 0);
+ else
+ dispctrl |= VC4_SET_FIELD(mode->hdisplay,
+ SCALER5_DISPCTRLX_WIDTH) |
+ VC4_SET_FIELD(mode->vdisplay,
+ SCALER5_DISPCTRLX_HEIGHT) |
+ (oneshot ? SCALER5_DISPCTRLX_ONESHOT : 0);
+
+ HVS_WRITE(SCALER_DISPCTRLX(vc4_state->assigned_channel), dispctrl);
+}
+
+void vc4_hvs_atomic_disable(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_state)
+{
+ struct drm_device *dev = crtc->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(old_state);
+ u32 chan = vc4_crtc_state->assigned_channel;
+
+ if (HVS_READ(SCALER_DISPCTRLX(chan)) &
+ SCALER_DISPCTRLX_ENABLE) {
+ HVS_WRITE(SCALER_DISPCTRLX(chan),
+ SCALER_DISPCTRLX_RESET);
+
+ /* While the docs say that reset is self-clearing, it
+ * seems it doesn't actually.
+ */
+ HVS_WRITE(SCALER_DISPCTRLX(chan), 0);
+ }
+
+ /* Once we leave, the scaler should be disabled and its fifo empty. */
+
+ WARN_ON_ONCE(HVS_READ(SCALER_DISPCTRLX(chan)) & SCALER_DISPCTRLX_RESET);
+
+ WARN_ON_ONCE(VC4_GET_FIELD(HVS_READ(SCALER_DISPSTATX(chan)),
+ SCALER_DISPSTATX_MODE) !=
+ SCALER_DISPSTATX_MODE_DISABLED);
+
+ WARN_ON_ONCE((HVS_READ(SCALER_DISPSTATX(chan)) &
+ (SCALER_DISPSTATX_FULL | SCALER_DISPSTATX_EMPTY)) !=
+ SCALER_DISPSTATX_EMPTY);
+}
+
+void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_state)
+{
+ struct drm_device *dev = crtc->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
+ struct drm_plane *plane;
+ struct vc4_plane_state *vc4_plane_state;
+ bool debug_dump_regs = false;
+ bool enable_bg_fill = false;
+ u32 __iomem *dlist_start = vc4->hvs->dlist + vc4_state->mm.start;
+ u32 __iomem *dlist_next = dlist_start;
+
+ if (debug_dump_regs) {
+ DRM_INFO("CRTC %d HVS before:\n", drm_crtc_index(crtc));
+ vc4_hvs_dump_state(dev);
+ }
+
+ /* Copy all the active planes' dlist contents to the hardware dlist. */
+ drm_atomic_crtc_for_each_plane(plane, crtc) {
+ /* Is this the first active plane? */
+ if (dlist_next == dlist_start) {
+ /* We need to enable background fill when a plane
+ * could be alpha blending from the background, i.e.
+ * where no other plane is underneath. It suffices to
+ * consider the first active plane here since we set
+ * needs_bg_fill such that either the first plane
+ * already needs it or all planes on top blend from
+ * the first or a lower plane.
+ */
+ vc4_plane_state = to_vc4_plane_state(plane->state);
+ enable_bg_fill = vc4_plane_state->needs_bg_fill;
+ }
+
+ dlist_next += vc4_plane_write_dlist(plane, dlist_next);
+ }
+
+ writel(SCALER_CTL0_END, dlist_next);
+ dlist_next++;
+
+ WARN_ON_ONCE(dlist_next - dlist_start != vc4_state->mm.size);
+
+ if (enable_bg_fill)
+ /* This sets a black background color fill, as is the case
+ * with other DRM drivers.
+ */
+ HVS_WRITE(SCALER_DISPBKGNDX(vc4_state->assigned_channel),
+ HVS_READ(SCALER_DISPBKGNDX(vc4_state->assigned_channel)) |
+ SCALER_DISPBKGND_FILL);
+
+ /* Only update DISPLIST if the CRTC was already running and is not
+ * being disabled.
+ * vc4_crtc_enable() takes care of updating the dlist just after
+ * re-enabling VBLANK interrupts and before enabling the engine.
+ * If the CRTC is being disabled, there's no point in updating this
+ * information.
+ */
+ if (crtc->state->active && old_state->active)
+ vc4_hvs_update_dlist(crtc);
+
+ if (crtc->state->color_mgmt_changed) {
+ u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(vc4_state->assigned_channel));
+
+ if (crtc->state->gamma_lut) {
+ vc4_hvs_update_gamma_lut(crtc);
+ dispbkgndx |= SCALER_DISPBKGND_GAMMA;
+ } else {
+ /* Unsetting DISPBKGND_GAMMA skips the gamma lut step
+ * in hardware, which is the same as a linear lut that
+ * DRM expects us to use in absence of a user lut.
+ */
+ dispbkgndx &= ~SCALER_DISPBKGND_GAMMA;
+ }
+ HVS_WRITE(SCALER_DISPBKGNDX(vc4_state->assigned_channel), dispbkgndx);
+ }
+
+ if (debug_dump_regs) {
+ DRM_INFO("CRTC %d HVS after:\n", drm_crtc_index(crtc));
+ vc4_hvs_dump_state(dev);
+ }
+}
+
+void vc4_hvs_mode_set_nofb(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
+ struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+ bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE;
+
+ if (vc4_crtc->data->hvs_output == 2) {
+ u32 dispctrl;
+ u32 dsp3_mux;
+
+ /*
+ * SCALER_DISPCTRL_DSP3 = X, where X < 2 means 'connect DSP3 to
+ * FIFO X'.
+ * SCALER_DISPCTRL_DSP3 = 3 means 'disable DSP 3'.
+ *
+ * DSP3 is connected to FIFO2 unless the transposer is
+ * enabled. In this case, FIFO 2 is directly accessed by the
+ * TXP IP, and we need to disable the FIFO2 -> pixelvalve1
+ * route.
+ */
+ if (vc4_state->feed_txp)
+ dsp3_mux = VC4_SET_FIELD(3, SCALER_DISPCTRL_DSP3_MUX);
+ else
+ dsp3_mux = VC4_SET_FIELD(2, SCALER_DISPCTRL_DSP3_MUX);
+
+ dispctrl = HVS_READ(SCALER_DISPCTRL) &
+ ~SCALER_DISPCTRL_DSP3_MUX_MASK;
+ HVS_WRITE(SCALER_DISPCTRL, dispctrl | dsp3_mux);
+ }
+
+ HVS_WRITE(SCALER_DISPBKGNDX(vc4_state->assigned_channel),
+ SCALER_DISPBKGND_AUTOHS |
+ ((!vc4->hvs->hvs5) ? SCALER_DISPBKGND_GAMMA : 0) |
+ (interlace ? SCALER_DISPBKGND_INTERLACE : 0));
+
+ /* Reload the LUT, since the SRAMs would have been disabled if
+ * all CRTCs had SCALER_DISPBKGND_GAMMA unset at once.
+ */
+ vc4_hvs_lut_load(crtc);
+}
+
void vc4_hvs_mask_underrun(struct drm_device *dev, int channel)
{
struct vc4_dev *vc4 = to_vc4_dev(dev);
--
git-series 0.9.1