Skip to content

Commit f2df84e

Browse files
committed
drm/vc4: kms: Store the unassigned channel list in the state
If a CRTC is enabled but not active, and that we're then doing a page flip on another CRTC, drm_atomic_get_crtc_state will bring the first CRTC state into the global state, and will make us wait for its vblank as well, even though that might never occur. Instead of creating the list of the free channels each time atomic_check is called, and calling drm_atomic_get_crtc_state to retrieve the allocated channels, let's create a private state object in the main atomic state, and use it to store the available channels. Since vc4 has a semaphore (with a value of 1, so a lock) in its commit implementation to serialize all the commits, even the nonblocking ones, we are free from the use-after-free race if two subsequent commits are not ran in their submission order. Fixes: 87ebcd4 ("drm/vc4: crtc: Assign output to channel automatically") Signed-off-by: Maxime Ripard <maxime@cerno.tech> Tested-by: Hoegeun Kwon <hoegeun.kwon@samsung.com> Reviewed-by: Hoegeun Kwon <hoegeun.kwon@samsung.com> Reviewed-by: Thomas Zimmermann <tzimmermann@suse.de> Link: https://patchwork.freedesktop.org/patch/msgid/20201120144245.398711-2-maxime@cerno.tech
1 parent 9fa1d7e commit f2df84e

2 files changed

Lines changed: 102 additions & 25 deletions

File tree

drivers/gpu/drm/vc4/vc4_drv.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ struct vc4_dev {
219219

220220
struct drm_modeset_lock ctm_state_lock;
221221
struct drm_private_obj ctm_manager;
222+
struct drm_private_obj hvs_channels;
222223
struct drm_private_obj load_tracker;
223224

224225
/* List of vc4_debugfs_info_entry for adding to debugfs once

drivers/gpu/drm/vc4/vc4_kms.c

Lines changed: 101 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,17 @@ static struct vc4_ctm_state *to_vc4_ctm_state(struct drm_private_state *priv)
3737
return container_of(priv, struct vc4_ctm_state, base);
3838
}
3939

40+
struct vc4_hvs_state {
41+
struct drm_private_state base;
42+
unsigned int unassigned_channels;
43+
};
44+
45+
static struct vc4_hvs_state *
46+
to_vc4_hvs_state(struct drm_private_state *priv)
47+
{
48+
return container_of(priv, struct vc4_hvs_state, base);
49+
}
50+
4051
struct vc4_load_tracker_state {
4152
struct drm_private_state base;
4253
u64 hvs_load;
@@ -171,6 +182,19 @@ vc4_ctm_commit(struct vc4_dev *vc4, struct drm_atomic_state *state)
171182
VC4_SET_FIELD(ctm_state->fifo, SCALER_OLEDOFFS_DISPFIFO));
172183
}
173184

185+
static struct vc4_hvs_state *
186+
vc4_hvs_get_global_state(struct drm_atomic_state *state)
187+
{
188+
struct vc4_dev *vc4 = to_vc4_dev(state->dev);
189+
struct drm_private_state *priv_state;
190+
191+
priv_state = drm_atomic_get_private_obj_state(state, &vc4->hvs_channels);
192+
if (IS_ERR(priv_state))
193+
return ERR_CAST(priv_state);
194+
195+
return to_vc4_hvs_state(priv_state);
196+
}
197+
174198
static void vc4_hvs_pv_muxing_commit(struct vc4_dev *vc4,
175199
struct drm_atomic_state *state)
176200
{
@@ -662,6 +686,59 @@ static int vc4_load_tracker_obj_init(struct vc4_dev *vc4)
662686
return drmm_add_action_or_reset(&vc4->base, vc4_load_tracker_obj_fini, NULL);
663687
}
664688

689+
static struct drm_private_state *
690+
vc4_hvs_channels_duplicate_state(struct drm_private_obj *obj)
691+
{
692+
struct vc4_hvs_state *old_state = to_vc4_hvs_state(obj->state);
693+
struct vc4_hvs_state *state;
694+
695+
state = kzalloc(sizeof(*state), GFP_KERNEL);
696+
if (!state)
697+
return NULL;
698+
699+
__drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
700+
701+
state->unassigned_channels = old_state->unassigned_channels;
702+
703+
return &state->base;
704+
}
705+
706+
static void vc4_hvs_channels_destroy_state(struct drm_private_obj *obj,
707+
struct drm_private_state *state)
708+
{
709+
struct vc4_hvs_state *hvs_state = to_vc4_hvs_state(state);
710+
711+
kfree(hvs_state);
712+
}
713+
714+
static const struct drm_private_state_funcs vc4_hvs_state_funcs = {
715+
.atomic_duplicate_state = vc4_hvs_channels_duplicate_state,
716+
.atomic_destroy_state = vc4_hvs_channels_destroy_state,
717+
};
718+
719+
static void vc4_hvs_channels_obj_fini(struct drm_device *dev, void *unused)
720+
{
721+
struct vc4_dev *vc4 = to_vc4_dev(dev);
722+
723+
drm_atomic_private_obj_fini(&vc4->hvs_channels);
724+
}
725+
726+
static int vc4_hvs_channels_obj_init(struct vc4_dev *vc4)
727+
{
728+
struct vc4_hvs_state *state;
729+
730+
state = kzalloc(sizeof(*state), GFP_KERNEL);
731+
if (!state)
732+
return -ENOMEM;
733+
734+
state->unassigned_channels = GENMASK(HVS_NUM_CHANNELS - 1, 0);
735+
drm_atomic_private_obj_init(&vc4->base, &vc4->hvs_channels,
736+
&state->base,
737+
&vc4_hvs_state_funcs);
738+
739+
return drmm_add_action_or_reset(&vc4->base, vc4_hvs_channels_obj_fini, NULL);
740+
}
741+
665742
/*
666743
* The BCM2711 HVS has up to 7 outputs connected to the pixelvalves and
667744
* the TXP (and therefore all the CRTCs found on that platform).
@@ -678,6 +755,14 @@ static int vc4_load_tracker_obj_init(struct vc4_dev *vc4)
678755
* need to consider all the running CRTCs in the DRM device to assign
679756
* a FIFO, not just the one in the state.
680757
*
758+
* - To fix the above, we can't use drm_atomic_get_crtc_state on all
759+
* enabled CRTCs to pull their CRTC state into the global state, since
760+
* a page flip would start considering their vblank to complete. Since
761+
* we don't have a guarantee that they are actually active, that
762+
* vblank might never happen, and shouldn't even be considered if we
763+
* want to do a page flip on a single CRTC. That can be tested by
764+
* doing a modetest -v first on HDMI1 and then on HDMI0.
765+
*
681766
* - Since we need the pixelvalve to be disabled and enabled back when
682767
* the FIFO is changed, we should keep the FIFO assigned for as long
683768
* as the CRTC is enabled, only considering it free again once that
@@ -687,46 +772,33 @@ static int vc4_load_tracker_obj_init(struct vc4_dev *vc4)
687772
static int vc4_pv_muxing_atomic_check(struct drm_device *dev,
688773
struct drm_atomic_state *state)
689774
{
690-
unsigned long unassigned_channels = GENMASK(HVS_NUM_CHANNELS - 1, 0);
775+
struct vc4_hvs_state *hvs_new_state;
691776
struct drm_crtc_state *old_crtc_state, *new_crtc_state;
692777
struct drm_crtc *crtc;
693778
unsigned int i;
694779

695-
/*
696-
* Since the HVS FIFOs are shared across all the pixelvalves and
697-
* the TXP (and thus all the CRTCs), we need to pull the current
698-
* state of all the enabled CRTCs so that an update to a single
699-
* CRTC still keeps the previous FIFOs enabled and assigned to
700-
* the same CRTCs, instead of evaluating only the CRTC being
701-
* modified.
702-
*/
703-
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
704-
struct drm_crtc_state *crtc_state;
705-
706-
if (!crtc->state->enable)
707-
continue;
708-
709-
crtc_state = drm_atomic_get_crtc_state(state, crtc);
710-
if (IS_ERR(crtc_state))
711-
return PTR_ERR(crtc_state);
712-
}
780+
hvs_new_state = vc4_hvs_get_global_state(state);
781+
if (!hvs_new_state)
782+
return -EINVAL;
713783

714784
for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
785+
struct vc4_crtc_state *old_vc4_crtc_state =
786+
to_vc4_crtc_state(old_crtc_state);
715787
struct vc4_crtc_state *new_vc4_crtc_state =
716788
to_vc4_crtc_state(new_crtc_state);
717789
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
718790
unsigned int matching_channels;
719791

720-
if (old_crtc_state->enable && !new_crtc_state->enable)
792+
if (old_crtc_state->enable && !new_crtc_state->enable) {
793+
hvs_new_state->unassigned_channels |= BIT(old_vc4_crtc_state->assigned_channel);
721794
new_vc4_crtc_state->assigned_channel = VC4_HVS_CHANNEL_DISABLED;
795+
}
722796

723797
if (!new_crtc_state->enable)
724798
continue;
725799

726-
if (new_vc4_crtc_state->assigned_channel != VC4_HVS_CHANNEL_DISABLED) {
727-
unassigned_channels &= ~BIT(new_vc4_crtc_state->assigned_channel);
800+
if (new_vc4_crtc_state->assigned_channel != VC4_HVS_CHANNEL_DISABLED)
728801
continue;
729-
}
730802

731803
/*
732804
* The problem we have to solve here is that we have
@@ -752,12 +824,12 @@ static int vc4_pv_muxing_atomic_check(struct drm_device *dev,
752824
* the future, we will need to have something smarter,
753825
* but it works so far.
754826
*/
755-
matching_channels = unassigned_channels & vc4_crtc->data->hvs_available_channels;
827+
matching_channels = hvs_new_state->unassigned_channels & vc4_crtc->data->hvs_available_channels;
756828
if (matching_channels) {
757829
unsigned int channel = ffs(matching_channels) - 1;
758830

759831
new_vc4_crtc_state->assigned_channel = channel;
760-
unassigned_channels &= ~BIT(channel);
832+
hvs_new_state->unassigned_channels &= ~BIT(channel);
761833
} else {
762834
return -EINVAL;
763835
}
@@ -841,6 +913,10 @@ int vc4_kms_load(struct drm_device *dev)
841913
if (ret)
842914
return ret;
843915

916+
ret = vc4_hvs_channels_obj_init(vc4);
917+
if (ret)
918+
return ret;
919+
844920
drm_mode_config_reset(dev);
845921

846922
drm_kms_helper_poll_init(dev);

0 commit comments

Comments
 (0)