Unverified Commit 87ebcd42 authored by Maxime Ripard's avatar Maxime Ripard
Browse files

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: default avatarMaxime Ripard <maxime@cerno.tech>
Tested-by: default avatarChanwoo Choi <cw00.choi@samsung.com>
Tested-by: default avatarHoegeun Kwon <hoegeun.kwon@samsung.com>
Tested-by: default avatarStefan Wahren <stefan.wahren@i2se.com>
Reviewed-by: default avatarDave Stevenson <dave.stevenson@raspberrypi.com>
Link: https://patchwork.freedesktop.org/patch/msgid/f9aba3814ef37156ff36f310118cdd3954dd3dc5.1599120059.git-series.maxime@cerno.tech
parent 59635667
Loading
Loading
Loading
Loading
+8 −4
Original line number Diff line number Diff line
@@ -88,6 +88,7 @@ static bool vc4_crtc_get_scanout_position(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);
	unsigned int cob_size;
	u32 val;
	int fifo_lines;
@@ -104,7 +105,7 @@ static bool vc4_crtc_get_scanout_position(struct drm_crtc *crtc,
	 * 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)
@@ -124,7 +125,7 @@ static bool vc4_crtc_get_scanout_position(struct drm_crtc *crtc,
			*hpos += mode->crtc_htotal / 2;
	}

	cob_size = vc4_crtc_get_cob_allocation(vc4, vc4_crtc->channel);
	cob_size = vc4_crtc_get_cob_allocation(vc4, vc4_crtc_state->assigned_channel);
	/* This is the offset we need for translating hvs -> pv scanout pos. */
	fifo_lines = cob_size / mode->crtc_hdisplay;

@@ -520,7 +521,7 @@ static void vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc)
	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);
@@ -719,6 +720,7 @@ struct drm_crtc_state *vc4_crtc_duplicate_state(struct drm_crtc *crtc)
	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;
@@ -779,6 +781,7 @@ static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = {

static const struct vc4_pv_data bcm2835_pv0_data = {
	.base = {
		.hvs_available_channels = BIT(0),
		.hvs_output = 0,
	},
	.debugfs_name = "crtc0_regs",
@@ -791,6 +794,7 @@ static const struct vc4_pv_data bcm2835_pv0_data = {

static const struct vc4_pv_data bcm2835_pv1_data = {
	.base = {
		.hvs_available_channels = BIT(2),
		.hvs_output = 2,
	},
	.debugfs_name = "crtc1_regs",
@@ -803,6 +807,7 @@ static const struct vc4_pv_data bcm2835_pv1_data = {

static const struct vc4_pv_data bcm2835_pv2_data = {
	.base = {
		.hvs_available_channels = BIT(1),
		.hvs_output = 1,
	},
	.debugfs_name = "crtc2_regs",
@@ -866,7 +871,6 @@ int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc,
	drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL,
				  crtc_funcs, NULL);
	drm_crtc_helper_add(crtc, 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);

+4 −3
Original line number Diff line number Diff line
@@ -447,6 +447,9 @@ to_vc4_encoder(struct drm_encoder *encoder)
}

struct vc4_crtc_data {
	/* Bitmask of channels (FIFOs) of the HVS that the output can 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];
@@ -509,6 +509,7 @@ struct vc4_crtc_state {
	struct drm_mm_node mm;
	bool feed_txp;
	bool txp_armed;
	unsigned int assigned_channel;

	struct {
		unsigned int left;
+13 −15
Original line number Diff line number Diff line
@@ -161,6 +161,7 @@ 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_state = to_vc4_crtc_state(crtc->state);
	u32 i;

	/* The LUT memory is laid out with each HVS channel in order,
@@ -169,7 +170,7 @@ static void vc4_hvs_lut_load(struct drm_crtc *crtc)
	 */
	HVS_WRITE(SCALER_GAMADDR,
		  SCALER_GAMADDR_AUTOINC |
		  (vc4_crtc->channel * 3 * crtc->gamma_size));
		  (vc4_state->assigned_channel * 3 * crtc->gamma_size));

	for (i = 0; i < crtc->gamma_size; i++)
		HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_r[i]);
@@ -249,12 +250,12 @@ static void vc4_hvs_update_dlist(struct drm_crtc *crtc)
			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);
	}
}
@@ -264,7 +265,6 @@ void vc4_hvs_atomic_enable(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 oneshot = vc4_state->feed_txp;
@@ -292,7 +292,7 @@ void vc4_hvs_atomic_enable(struct drm_crtc *crtc,
					  SCALER5_DISPCTRLX_HEIGHT) |
			    (oneshot ? SCALER5_DISPCTRLX_ONESHOT : 0);

	HVS_WRITE(SCALER_DISPCTRLX(vc4_crtc->channel), dispctrl);
	HVS_WRITE(SCALER_DISPCTRLX(vc4_state->assigned_channel), dispctrl);
}

void vc4_hvs_atomic_disable(struct drm_crtc *crtc,
@@ -300,8 +300,8 @@ void vc4_hvs_atomic_disable(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);
	u32 chan = vc4_crtc->channel;
	struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(old_state);
	unsigned int chan = vc4_state->assigned_channel;

	if (HVS_READ(SCALER_DISPCTRLX(chan)) &
	    SCALER_DISPCTRLX_ENABLE) {
@@ -332,7 +332,6 @@ void vc4_hvs_atomic_flush(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_plane *plane;
	struct vc4_plane_state *vc4_plane_state;
@@ -374,8 +373,8 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
		/* 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
@@ -389,7 +388,7 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
		vc4_hvs_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_hvs_update_gamma_lut(crtc);
@@ -401,7 +400,7 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
			 */
			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) {
@@ -414,12 +413,11 @@ 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) {
	if (vc4_state->assigned_channel == 2) {
		u32 dispctrl;
		u32 dsp3_mux;

@@ -443,7 +441,7 @@ void vc4_hvs_mode_set_nofb(struct drm_crtc *crtc)
		HVS_WRITE(SCALER_DISPCTRL, dispctrl | dsp3_mux);
	}

	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));
+164 −4
Original line number Diff line number Diff line
@@ -146,6 +146,107 @@ vc4_ctm_commit(struct vc4_dev *vc4, struct drm_atomic_state *state)
		  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 int i;

	for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
		struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state);
		u32 dispctrl;
		u32 dsp3_mux;

		if (!crtc_state->active)
			continue;

		if (vc4_state->assigned_channel != 2)
			continue;

		/*
		 * 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);
	}
}

static void vc5_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) ? 0 : 1;
			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);
	HVS_WRITE(SCALER_DISPECTRL,
		  (reg & ~SCALER_DISPECTRL_DSP2_MUX_MASK) |
		  VC4_SET_FIELD(dsp2_mux, SCALER_DISPECTRL_DSP2_MUX));

	reg = HVS_READ(SCALER_DISPCTRL);
	HVS_WRITE(SCALER_DISPCTRL,
		  (reg & ~SCALER_DISPCTRL_DSP3_MUX_MASK) |
		  VC4_SET_FIELD(dsp3_mux, SCALER_DISPCTRL_DSP3_MUX));

	reg = HVS_READ(SCALER_DISPEOLN);
	HVS_WRITE(SCALER_DISPEOLN,
		  (reg & ~SCALER_DISPEOLN_DSP4_MUX_MASK) |
		  VC4_SET_FIELD(dsp4_mux, SCALER_DISPEOLN_DSP4_MUX));

	reg = HVS_READ(SCALER_DISPDITHER);
	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,12 +258,13 @@ vc4_atomic_complete_commit(struct drm_atomic_state *state)
	int i;

	for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
		struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
		struct vc4_crtc_state *vc4_crtc_state;

		if (!new_crtc_state->commit)
			continue;

		vc4_hvs_mask_underrun(dev, vc4_crtc->channel);
		vc4_crtc_state = to_vc4_crtc_state(new_crtc_state);
		vc4_hvs_mask_underrun(dev, vc4_crtc_state->assigned_channel);
	}

	if (vc4->hvs->hvs5)
@@ -176,6 +278,11 @@ vc4_atomic_complete_commit(struct drm_atomic_state *state)

	vc4_ctm_commit(vc4, state);

	if (vc4->hvs->hvs5)
		vc5_hvs_pv_muxing_commit(vc4, state);
	else
		vc4_hvs_pv_muxing_commit(vc4, state);

	drm_atomic_helper_commit_planes(dev, state, 0);

	drm_atomic_helper_commit_modeset_enables(dev, state);
@@ -385,8 +492,11 @@ vc4_ctm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)

		/* 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.
@@ -496,10 +606,60 @@ static const struct drm_private_state_funcs vc4_load_tracker_state_funcs = {
	.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);
		unsigned int matching_channels;

		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.
		 */
		matching_channels = unassigned_channels & vc4_crtc->data->hvs_available_channels;
		if (matching_channels) {
			unsigned int channel = ffs(matching_channels) - 1;

			vc4_crtc_state->assigned_channel = channel;
			unassigned_channels &= ~BIT(channel);
		} else {
			return -EINVAL;
		}
	}

	ret = vc4_ctm_atomic_check(dev, state);
	if (ret < 0)
+10 −0
Original line number Diff line number Diff line
@@ -286,9 +286,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
Loading