Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions drivers/gpu/drm/vc4/vc4_hdmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -1780,10 +1780,12 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi)
const __be32 *addr;
int index;
int ret;
int len;

if (!of_find_property(dev->of_node, "dmas", NULL)) {
if (!of_find_property(dev->of_node, "dmas", &len) ||
len == 0) {
dev_warn(dev,
"'dmas' DT property is missing, no HDMI audio\n");
"'dmas' DT property is missing or empty, no HDMI audio\n");
return 0;
}

Expand Down
41 changes: 41 additions & 0 deletions drivers/gpu/drm/vc4/vc4_hvs.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,45 @@ static int vc4_hvs_debugfs_underrun(struct seq_file *m, void *data)
return 0;
}

static int vc4_hvs_debugfs_dlist(struct seq_file *m, void *data)
{
struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct vc4_dev *vc4 = to_vc4_dev(dev);
struct drm_printer p = drm_seq_file_printer(m);
unsigned int next_entry_start = 0;
unsigned int i, j;
u32 dlist_word, dispstat;

for (i = 0; i < SCALER_CHANNELS_COUNT; i++) {
dispstat = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTATX(i)),
SCALER_DISPSTATX_MODE);
if (dispstat == SCALER_DISPSTATX_MODE_DISABLED ||
dispstat == SCALER_DISPSTATX_MODE_EOF) {
drm_printf(&p, "HVS chan %u disabled\n", i);
continue;
}

drm_printf(&p, "HVS chan %u:\n", i);

for (j = HVS_READ(SCALER_DISPLISTX(i)); j < 256; j++) {
dlist_word = readl((u32 __iomem *)vc4->hvs->dlist + j);
drm_printf(&p, "dlist: %02d: 0x%08x\n", j,
dlist_word);
if (!next_entry_start ||
next_entry_start == j) {
if (dlist_word & SCALER_CTL0_END)
break;
next_entry_start = j +
VC4_GET_FIELD(dlist_word,
SCALER_CTL0_SIZE);
}
}
}

return 0;
}

/* The filter kernel is composed of dwords each containing 3 9-bit
* signed integers packed next to each other.
*/
Expand Down Expand Up @@ -673,6 +712,8 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
vc4_debugfs_add_regset32(drm, "hvs_regs", &hvs->regset);
vc4_debugfs_add_file(drm, "hvs_underrun", vc4_hvs_debugfs_underrun,
NULL);
vc4_debugfs_add_file(drm, "hvs_dlists", vc4_hvs_debugfs_dlist,
NULL);

return 0;
}
Expand Down
87 changes: 72 additions & 15 deletions drivers/gpu/drm/vc4/vc4_kms.c
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,23 @@ static void vc4_hvs_pv_muxing_commit(struct vc4_dev *vc4,
}
}

static struct drm_crtc_state *
drm_atomic_get_new_or_current_crtc_state(struct drm_atomic_state *state,
struct drm_crtc *crtc)
{
struct drm_crtc_state *crtc_state;

crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
if (crtc_state)
return crtc_state;

return crtc->state;
}

#define for_each_new_or_current_crtc_state(__state, crtc, crtc_state) \
list_for_each_entry(crtc, &__state->dev->mode_config.crtc_list, head) \
for_each_if(crtc_state = drm_atomic_get_new_or_current_crtc_state(__state, crtc))

static void vc5_hvs_pv_muxing_commit(struct vc4_dev *vc4,
struct drm_atomic_state *state)
{
Expand All @@ -197,16 +214,16 @@ static void vc5_hvs_pv_muxing_commit(struct vc4_dev *vc4,
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);
for_each_new_or_current_crtc_state(state, crtc, crtc_state) {
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
struct vc4_crtc_state *vc4_state;

if (!crtc_state->active)
continue;

vc4_state = to_vc4_crtc_state(crtc_state);
switch (vc4_crtc->data->hvs_output) {
case 2:
dsp2_mux = (vc4_state->assigned_channel == 2) ? 0 : 1;
Expand Down Expand Up @@ -618,14 +635,45 @@ static const struct drm_private_state_funcs vc4_load_tracker_state_funcs = {
#define NUM_OUTPUTS 6
#define NUM_CHANNELS 3

static int
vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
/*
* The BCM2711 HVS has up to 7 output connected to the pixelvalves and
* the TXP (and therefore all the CRTCs found on that platform).
*
* The naive (and our initial) implementation would just iterate over
* all the active CRTCs, try to find a suitable FIFO, and then remove it
* from the available FIFOs pool. However, there's a few corner cases
* that need to be considered:
*
* - When running in a dual-display setup (so with two CRTCs involved),
* we can update the state of a single CRTC (for example by changing
* its mode using xrandr under X11) without affecting the other. In
* this case, the other CRTC wouldn't be in the state at all, so we
* need to consider all the running CRTCs in the DRM device to assign
* a FIFO, not just the one in the state.
*
* - To fix the above, we can't use drm_atomic_get_crtc_state on all
* enabled CRTCs to pull their CRTC state into the global state, since
* a page flip would start considering their vblank to complete. Since
* we don't have a guarantee that they are actually active, that
* vblank might never happen, and shouldn't even be considered if we
* want to do a page flip on a single CRTC. That can be tested by
* doing a modetest -v first on HDMI1 and then on HDMI0.
*
* - Since we need the pixelvalve to be disabled and enabled back when
* the FIFO is changed, we should keep the FIFO assigned for as long
* as the CRTC is enabled, only considering it free again once that
* CRTC has been disabled. This can be tested by booting X11 on a
* single display, and changing the resolution down and then back up.
*/
static int vc4_pv_muxing_atomic_check(struct drm_device *dev,
struct drm_atomic_state *state)
{
unsigned long unassigned_channels = GENMASK(NUM_CHANNELS - 1, 0);
struct drm_crtc_state *old_crtc_state, *new_crtc_state;
struct vc4_dev *vc4 = to_vc4_dev(state->dev);
struct drm_crtc_state *crtc_state;
struct drm_crtc *crtc;
int i, ret;
unsigned int i;

/*
* Since the HVS FIFOs are shared across all the pixelvalves and
Expand All @@ -635,15 +683,14 @@ vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
* the same CRTCs, instead of evaluating only the CRTC being
* modified.
*/
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
struct drm_crtc_state *crtc_state;
for_each_new_or_current_crtc_state(state, crtc, crtc_state) {
struct vc4_crtc_state *vc4_crtc_state;

if (!crtc->state->enable)
if (!crtc_state->enable)
continue;

crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (IS_ERR(crtc_state))
return PTR_ERR(crtc_state);
vc4_crtc_state = to_vc4_crtc_state(crtc_state);
unassigned_channels &= ~BIT(vc4_crtc_state->assigned_channel);
}

for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
Expand All @@ -661,10 +708,8 @@ vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
if (!new_crtc_state->enable)
continue;

if (new_vc4_crtc_state->assigned_channel != VC4_HVS_CHANNEL_DISABLED) {
unassigned_channels &= ~BIT(new_vc4_crtc_state->assigned_channel);
if (new_vc4_crtc_state->assigned_channel != VC4_HVS_CHANNEL_DISABLED)
continue;
}

/*
* The problem we have to solve here is that we have
Expand Down Expand Up @@ -701,6 +746,18 @@ vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
}
}

return 0;
}

static int
vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
{
int ret;

ret = vc4_pv_muxing_atomic_check(dev, state);
if (ret)
return ret;

ret = vc4_ctm_atomic_check(dev, state);
if (ret < 0)
return ret;
Expand Down