diff options
Diffstat (limited to 'target/linux/bcm27xx/patches-5.4/950-0561-drm-vc4-crtc-Assign-output-to-channel-automatically.patch')
-rw-r--r-- | target/linux/bcm27xx/patches-5.4/950-0561-drm-vc4-crtc-Assign-output-to-channel-automatically.patch | 459 |
1 files changed, 459 insertions, 0 deletions
diff --git a/target/linux/bcm27xx/patches-5.4/950-0561-drm-vc4-crtc-Assign-output-to-channel-automatically.patch b/target/linux/bcm27xx/patches-5.4/950-0561-drm-vc4-crtc-Assign-output-to-channel-automatically.patch new file mode 100644 index 0000000000..d470f3b7f0 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0561-drm-vc4-crtc-Assign-output-to-channel-automatically.patch @@ -0,0 +1,459 @@ +From 9efecb2ccd14a6d226ba2afa04f6e70b96026b3e Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 26 Dec 2019 17:53:18 +0100 +Subject: [PATCH] drm/vc4: crtc: Assign output to channel automatically + +The HVS found in the BCM2711 has 6 outputs and 3 FIFOs, with each output +being connected to a pixelvalve, and some muxing between the FIFOs and +outputs. + +Any output cannot feed from any FIFO though, and they all have a bunch of +constraints. + +In order to support this, let's store the possible FIFOs each output can be +assigned to in the vc4_crtc_data, and use that information at atomic_check +time to iterate over all the CRTCs enabled and assign them FIFOs. + +The channel assigned is then set in the vc4_crtc_state so that the rest of +the driver can use it. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_crtc.c | 37 +++++---- + drivers/gpu/drm/vc4/vc4_drv.h | 7 +- + drivers/gpu/drm/vc4/vc4_kms.c | 146 +++++++++++++++++++++++++++++++-- + drivers/gpu/drm/vc4/vc4_regs.h | 10 +++ + 4 files changed, 175 insertions(+), 25 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -90,6 +90,7 @@ bool vc4_crtc_get_scanoutpos(struct drm_ + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id); + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); ++ struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(crtc->state); + unsigned int cob_size; + u32 val; + int fifo_lines; +@@ -106,7 +107,7 @@ bool vc4_crtc_get_scanoutpos(struct drm_ + * Read vertical scanline which is currently composed for our + * pixelvalve by the HVS, and also the scaler status. + */ +- val = HVS_READ(SCALER_DISPSTATX(vc4_crtc->channel)); ++ val = HVS_READ(SCALER_DISPSTATX(vc4_crtc_state->assigned_channel)); + + /* Get optional system timestamp after query. */ + if (etime) +@@ -126,7 +127,7 @@ bool vc4_crtc_get_scanoutpos(struct drm_ + *hpos += mode->crtc_htotal / 2; + } + +- cob_size = vc4_crtc_get_cob_allocation(vc4_crtc, vc4_crtc->channel); ++ cob_size = vc4_crtc_get_cob_allocation(vc4_crtc, vc4_crtc_state->assigned_channel); + /* This is the offset we need for translating hvs -> pv scanout pos. */ + fifo_lines = cob_size / mode->crtc_hdisplay; + +@@ -213,6 +214,7 @@ 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, +@@ -221,7 +223,7 @@ vc4_crtc_lut_load(struct drm_crtc *crtc) + */ + HVS_WRITE(SCALER_GAMADDR, + SCALER_GAMADDR_AUTOINC | +- (vc4_crtc->channel * 3 * crtc->gamma_size)); ++ (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]); +@@ -394,7 +396,7 @@ static void vc4_crtc_mode_set_nofb(struc + drm_print_regset32(&p, &vc4_crtc->regset); + } + +- if (vc4_crtc->channel == 2) { ++ if (vc4_crtc->data->hvs_output == 2) { + u32 dispctrl; + u32 dsp3_mux; + +@@ -421,7 +423,7 @@ static void vc4_crtc_mode_set_nofb(struc + if (!vc4_state->feed_txp) + vc4_crtc_config_pv(crtc); + +- HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel), ++ HVS_WRITE(SCALER_DISPBKGNDX(vc4_state->assigned_channel), + SCALER_DISPBKGND_AUTOHS | + SCALER_DISPBKGND_GAMMA | + (interlace ? SCALER_DISPBKGND_INTERLACE : 0)); +@@ -453,7 +455,8 @@ static void vc4_crtc_atomic_disable(stru + struct drm_device *dev = crtc->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); +- u32 chan = vc4_crtc->channel; ++ 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); + +@@ -532,12 +535,12 @@ static void vc4_crtc_update_dlist(struct + crtc->state->event = NULL; + } + +- HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel), ++ 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_crtc->channel), ++ HVS_WRITE(SCALER_DISPLISTX(vc4_state->assigned_channel), + vc4_state->mm.start); + } + } +@@ -586,7 +589,7 @@ static void vc4_crtc_atomic_enable(struc + (vc4_state->feed_txp ? + SCALER5_DISPCTRLX_ONESHOT : 0); + +- HVS_WRITE(SCALER_DISPCTRLX(vc4_crtc->channel), dispctrl); ++ HVS_WRITE(SCALER_DISPCTRLX(vc4_state->assigned_channel), dispctrl); + + /* When feeding the transposer block the pixelvalve is unneeded and + * should not be enabled. +@@ -702,7 +705,6 @@ static void vc4_crtc_atomic_flush(struct + { + 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_plane *plane; + struct vc4_plane_state *vc4_plane_state; +@@ -744,8 +746,8 @@ static void vc4_crtc_atomic_flush(struct + /* This sets a black background color fill, as is the case + * with other DRM drivers. + */ +- HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel), +- HVS_READ(SCALER_DISPBKGNDX(vc4_crtc->channel)) | ++ 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 +@@ -759,7 +761,7 @@ static void vc4_crtc_atomic_flush(struct + vc4_crtc_update_dlist(crtc); + + if (crtc->state->color_mgmt_changed) { +- u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(vc4_crtc->channel)); ++ u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(vc4_state->assigned_channel)); + + if (crtc->state->gamma_lut) { + vc4_crtc_update_gamma_lut(crtc); +@@ -771,7 +773,7 @@ static void vc4_crtc_atomic_flush(struct + */ + dispbkgndx &= ~SCALER_DISPBKGND_GAMMA; + } +- HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel), dispbkgndx); ++ HVS_WRITE(SCALER_DISPBKGNDX(vc4_state->assigned_channel), dispbkgndx); + } + + if (debug_dump_regs) { +@@ -802,7 +804,7 @@ static void vc4_crtc_handle_page_flip(st + 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); +- u32 chan = vc4_crtc->channel; ++ u32 chan = vc4_state->assigned_channel; + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); +@@ -1002,6 +1004,7 @@ static struct drm_crtc_state *vc4_crtc_d + old_vc4_state = to_vc4_crtc_state(crtc->state); + vc4_state->feed_txp = old_vc4_state->feed_txp; + vc4_state->margins = old_vc4_state->margins; ++ vc4_state->assigned_channel = old_vc4_state->assigned_channel; + + __drm_atomic_helper_crtc_duplicate_state(crtc, &vc4_state->base); + return &vc4_state->base; +@@ -1061,6 +1064,7 @@ static const struct drm_crtc_helper_func + }; + + static const struct vc4_crtc_data bcm2835_pv0_data = { ++ .hvs_available_channels = BIT(0), + .hvs_output = 0, + .debugfs_name = "crtc0_regs", + .pixels_per_clock = 1, +@@ -1071,6 +1075,7 @@ static const struct vc4_crtc_data bcm283 + }; + + static const struct vc4_crtc_data bcm2835_pv1_data = { ++ .hvs_available_channels = BIT(2), + .hvs_output = 2, + .debugfs_name = "crtc1_regs", + .pixels_per_clock = 1, +@@ -1081,6 +1086,7 @@ static const struct vc4_crtc_data bcm283 + }; + + static const struct vc4_crtc_data bcm2835_pv2_data = { ++ .hvs_available_channels = BIT(1), + .hvs_output = 1, + .debugfs_name = "crtc2_regs", + .pixels_per_clock = 1, +@@ -1172,7 +1178,6 @@ static int vc4_crtc_bind(struct device * + drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL, + &vc4_crtc_funcs, NULL); + drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs); +- vc4_crtc->channel = vc4_crtc->data->hvs_output; + drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r)); + drm_crtc_enable_color_mgmt(crtc, 0, false, crtc->gamma_size); + +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -452,6 +452,9 @@ to_vc4_encoder(struct drm_encoder *encod + } + + struct vc4_crtc_data { ++ /* Which channels of the HVS can the output source from */ ++ unsigned int hvs_available_channels; ++ + /* Which output of the HVS this pixelvalve sources from. */ + int hvs_output; + +@@ -471,9 +474,6 @@ struct vc4_crtc { + /* Timestamp at start of vblank irq - unaffected by lock delays. */ + ktime_t t_vblank; + +- /* Which HVS channel we're using for our CRTC. */ +- int channel; +- + u8 lut_r[256]; + u8 lut_g[256]; + u8 lut_b[256]; +@@ -495,6 +495,7 @@ struct vc4_crtc_state { + struct drm_mm_node mm; + bool feed_txp; + bool txp_armed; ++ unsigned int assigned_channel; + + struct { + unsigned int left; +--- a/drivers/gpu/drm/vc4/vc4_kms.c ++++ b/drivers/gpu/drm/vc4/vc4_kms.c +@@ -11,6 +11,9 @@ + * crtc, HDMI encoder). + */ + ++#include <linux/bitfield.h> ++#include <linux/bitops.h> ++ + #include <drm/drm_atomic.h> + #include <drm/drm_atomic_helper.h> + #include <drm/drm_crtc.h> +@@ -148,6 +151,72 @@ vc4_ctm_commit(struct vc4_dev *vc4, stru + VC4_SET_FIELD(ctm_state->fifo, SCALER_OLEDOFFS_DISPFIFO)); + } + ++static void vc4_hvs_pv_muxing_commit(struct vc4_dev *vc4, ++ struct drm_atomic_state *state) ++{ ++ struct drm_crtc_state *crtc_state; ++ struct drm_crtc *crtc; ++ unsigned char dsp2_mux = 0; ++ unsigned char dsp3_mux = 3; ++ unsigned char dsp4_mux = 3; ++ unsigned char dsp5_mux = 3; ++ unsigned int i; ++ u32 reg; ++ ++ for_each_new_crtc_in_state(state, crtc, crtc_state, i) { ++ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state); ++ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); ++ ++ if (!crtc_state->active) ++ continue; ++ ++ switch (vc4_crtc->data->hvs_output) { ++ case 2: ++ dsp2_mux = (vc4_state->assigned_channel == 2) ? 1 : 0; ++ break; ++ ++ case 3: ++ dsp3_mux = vc4_state->assigned_channel; ++ break; ++ ++ case 4: ++ dsp4_mux = vc4_state->assigned_channel; ++ break; ++ ++ case 5: ++ dsp5_mux = vc4_state->assigned_channel; ++ break; ++ ++ default: ++ break; ++ } ++ } ++ ++ reg = HVS_READ(SCALER_DISPECTRL); ++ if (FIELD_GET(SCALER_DISPECTRL_DSP2_MUX_MASK, reg) != dsp2_mux) ++ HVS_WRITE(SCALER_DISPECTRL, ++ (reg & ~SCALER_DISPECTRL_DSP2_MUX_MASK) | ++ VC4_SET_FIELD(dsp2_mux, SCALER_DISPECTRL_DSP2_MUX)); ++ ++ reg = HVS_READ(SCALER_DISPCTRL); ++ if (FIELD_GET(SCALER_DISPCTRL_DSP3_MUX_MASK, reg) != dsp3_mux) ++ HVS_WRITE(SCALER_DISPCTRL, ++ (reg & ~SCALER_DISPCTRL_DSP3_MUX_MASK) | ++ VC4_SET_FIELD(dsp3_mux, SCALER_DISPCTRL_DSP3_MUX)); ++ ++ reg = HVS_READ(SCALER_DISPEOLN); ++ if (FIELD_GET(SCALER_DISPEOLN_DSP4_MUX_MASK, reg) != dsp4_mux) ++ HVS_WRITE(SCALER_DISPEOLN, ++ (reg & ~SCALER_DISPEOLN_DSP4_MUX_MASK) | ++ VC4_SET_FIELD(dsp4_mux, SCALER_DISPEOLN_DSP4_MUX)); ++ ++ reg = HVS_READ(SCALER_DISPDITHER); ++ if (FIELD_GET(SCALER_DISPDITHER_DSP5_MUX_MASK, reg) != dsp5_mux) ++ HVS_WRITE(SCALER_DISPDITHER, ++ (reg & ~SCALER_DISPDITHER_DSP5_MUX_MASK) | ++ VC4_SET_FIELD(dsp5_mux, SCALER_DISPDITHER_DSP5_MUX)); ++} ++ + static void + vc4_atomic_complete_commit(struct drm_atomic_state *state) + { +@@ -157,11 +226,15 @@ vc4_atomic_complete_commit(struct drm_at + int i; + + for (i = 0; vc4->hvs && i < dev->mode_config.num_crtc; i++) { +- if (!state->crtcs[i].ptr || !state->crtcs[i].commit) ++ struct __drm_crtcs_state *_state = &state->crtcs[i]; ++ struct vc4_crtc_state *vc4_crtc_state; ++ ++ if (!_state->ptr || !_state->commit) + continue; + +- vc4_crtc = to_vc4_crtc(state->crtcs[i].ptr); +- vc4_hvs_mask_underrun(dev, vc4_crtc->channel); ++ vc4_crtc = to_vc4_crtc(_state->ptr); ++ vc4_crtc_state = to_vc4_crtc_state(_state->state); ++ vc4_hvs_mask_underrun(dev, vc4_crtc_state->assigned_channel); + } + + drm_atomic_helper_wait_for_fences(dev, state, false); +@@ -170,8 +243,10 @@ vc4_atomic_complete_commit(struct drm_at + + drm_atomic_helper_commit_modeset_disables(dev, state); + +- if (!vc4->firmware_kms) ++ if (!vc4->firmware_kms) { + vc4_ctm_commit(vc4, state); ++ vc4_hvs_pv_muxing_commit(vc4, state); ++ } + + drm_atomic_helper_commit_planes(dev, state, 0); + +@@ -380,8 +455,11 @@ vc4_ctm_atomic_check(struct drm_device * + + /* CTM is being enabled or the matrix changed. */ + if (new_crtc_state->ctm) { ++ struct vc4_crtc_state *vc4_crtc_state = ++ to_vc4_crtc_state(new_crtc_state); ++ + /* fifo is 1-based since 0 disables CTM. */ +- int fifo = to_vc4_crtc(crtc)->channel + 1; ++ int fifo = vc4_crtc_state->assigned_channel + 1; + + /* Check userland isn't trying to turn on CTM for more + * than one CRTC at a time. +@@ -494,10 +572,66 @@ static const struct drm_private_state_fu + .atomic_destroy_state = vc4_load_tracker_destroy_state, + }; + ++#define NUM_OUTPUTS 6 ++#define NUM_CHANNELS 3 ++ + static int + vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) + { +- int ret; ++ unsigned long unassigned_channels = GENMASK(NUM_CHANNELS - 1, 0); ++ struct drm_crtc_state *crtc_state; ++ struct drm_crtc *crtc; ++ int i, ret; ++ ++ for_each_new_crtc_in_state(state, crtc, crtc_state, i) { ++ struct vc4_crtc_state *vc4_crtc_state = ++ to_vc4_crtc_state(crtc_state); ++ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); ++ bool is_assigned = false; ++ unsigned int channel; ++ ++ if (!crtc_state->active) ++ continue; ++ ++ /* ++ * The problem we have to solve here is that we have ++ * up to 7 encoders, connected to up to 6 CRTCs. ++ * ++ * Those CRTCs, depending on the instance, can be ++ * routed to 1, 2 or 3 HVS FIFOs, and we need to set ++ * the change the muxing between FIFOs and outputs in ++ * the HVS accordingly. ++ * ++ * It would be pretty hard to come up with an ++ * algorithm that would generically solve ++ * this. However, the current routing trees we support ++ * allow us to simplify a bit the problem. ++ * ++ * Indeed, with the current supported layouts, if we ++ * try to assign in the ascending crtc index order the ++ * FIFOs, we can't fall into the situation where an ++ * earlier CRTC that had multiple routes is assigned ++ * one that was the only option for a later CRTC. ++ * ++ * If the layout changes and doesn't give us that in ++ * the future, we will need to have something smarter, ++ * but it works so far. ++ */ ++ for_each_set_bit(channel, &unassigned_channels, ++ sizeof(unassigned_channels)) { ++ ++ if (!(BIT(channel) & vc4_crtc->data->hvs_available_channels)) ++ continue; ++ ++ vc4_crtc_state->assigned_channel = channel; ++ unassigned_channels &= ~BIT(channel); ++ is_assigned = true; ++ break; ++ } ++ ++ if (!is_assigned) ++ return -EINVAL; ++ } + + ret = vc4_ctm_atomic_check(dev, state); + if (ret < 0) +--- a/drivers/gpu/drm/vc4/vc4_regs.h ++++ b/drivers/gpu/drm/vc4/vc4_regs.h +@@ -287,9 +287,19 @@ + + #define SCALER_DISPID 0x00000008 + #define SCALER_DISPECTRL 0x0000000c ++# define SCALER_DISPECTRL_DSP2_MUX_SHIFT 31 ++# define SCALER_DISPECTRL_DSP2_MUX_MASK VC4_MASK(31, 31) ++ + #define SCALER_DISPPROF 0x00000010 ++ + #define SCALER_DISPDITHER 0x00000014 ++# define SCALER_DISPDITHER_DSP5_MUX_SHIFT 30 ++# define SCALER_DISPDITHER_DSP5_MUX_MASK VC4_MASK(31, 30) ++ + #define SCALER_DISPEOLN 0x00000018 ++# define SCALER_DISPEOLN_DSP4_MUX_SHIFT 30 ++# define SCALER_DISPEOLN_DSP4_MUX_MASK VC4_MASK(31, 30) ++ + #define SCALER_DISPLIST0 0x00000020 + #define SCALER_DISPLIST1 0x00000024 + #define SCALER_DISPLIST2 0x00000028 |