return container_of(priv, struct vc4_ctm_state, base);
}
+struct vc4_hvs_state {
+ struct drm_private_state base;
+ unsigned long unassigned_channels;
+};
+
+ static struct vc4_hvs_state *
+to_vc4_hvs_state(struct drm_private_state *priv)
+{
+ return container_of(priv, struct vc4_hvs_state, base);
+}
+
struct vc4_load_tracker_state {
struct drm_private_state base;
u64 hvs_load;
return 0;
}
+static void vc4_load_tracker_obj_fini(struct vc4_dev *vc4)
+{
+ if (!vc4->load_tracker_available)
+ return;
+
+ drm_atomic_private_obj_fini(&vc4->load_tracker);
+}
+
+ static struct drm_private_state *
+vc4_hvs_channels_duplicate_state(struct drm_private_obj *obj)
+{
+ struct vc4_hvs_state *state;
+
+ state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return NULL;
+
+ __drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
+
+ return &state->base;
+}
+
+static void vc4_hvs_channels_destroy_state(struct drm_private_obj *obj,
+ struct drm_private_state *state)
+{
+ struct vc4_hvs_state *hvs_state;
+
+ hvs_state = to_vc4_hvs_state(state);
+ kfree(hvs_state);
+}
+
+static const struct drm_private_state_funcs vc4_hvs_state_funcs = {
+ .atomic_duplicate_state = vc4_hvs_channels_duplicate_state,
+ .atomic_destroy_state = vc4_hvs_channels_destroy_state,
+};
+
+static int vc4_hvs_channels_obj_init(struct vc4_dev *vc4)
+{
+ struct vc4_hvs_state *state;
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ state->unassigned_channels = GENMASK(HVS_NUM_CHANNELS - 1, 0);
+ drm_atomic_private_obj_init(vc4->dev, &vc4->hvs_channels,
+ &state->base,
+ &vc4_hvs_state_funcs);
+
+ return 0;
+}
+
+ static struct vc4_hvs_state *
+vc4_hvs_get_global_state(struct drm_atomic_state *state)
+{
+ struct vc4_dev *vc4 = to_vc4_dev(state->dev);
+ struct drm_private_state *priv_state;
+
+ priv_state = drm_atomic_get_private_obj_state(state, &vc4->hvs_channels);
+ if (IS_ERR(priv_state))
+ return ERR_CAST(priv_state);
+
+ return to_vc4_hvs_state(priv_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).
* 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
static int vc4_pv_muxing_atomic_check(struct drm_device *dev,
struct drm_atomic_state *state)
{
- unsigned long unassigned_channels = GENMASK(HVS_NUM_CHANNELS - 1, 0);
- struct vc4_dev *vc4 = to_vc4_dev(state->dev);
+ struct vc4_hvs_state *hvs_state;
struct drm_crtc_state *old_crtc_state, *new_crtc_state;
struct drm_crtc *crtc;
unsigned int i;
- /*
- * Since the HVS FIFOs are shared across all the pixelvalves and
- * the TXP (and thus all the CRTCs), we need to pull the current
- * state of all the enabled CRTCs so that an update to a single
- * CRTC still keeps the previous FIFOs enabled and assigned to
- * 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;
- if (!crtc->state->enable)
- continue;
-
- crtc_state = drm_atomic_get_crtc_state(state, crtc);
- if (IS_ERR(crtc_state))
- return PTR_ERR(crtc_state);
- }
+ hvs_state = vc4_hvs_get_global_state(state);
+ if (!hvs_state)
+ return -EINVAL;
for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
struct vc4_crtc_state *new_vc4_crtc_state =
bool is_assigned = false;
unsigned int channel;
- if (old_crtc_state->enable && !new_crtc_state->enable)
+ if (old_crtc_state->enable && !new_crtc_state->enable) {
+ hvs_state->unassigned_channels |= BIT(old_vc4_crtc_state->assigned_channel);
new_vc4_crtc_state->assigned_channel = VC4_HVS_CHANNEL_DISABLED;
+ }
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
* the future, we will need to have something smarter,
* but it works so far.
*/
- for_each_set_bit(channel, &unassigned_channels,
- sizeof(unassigned_channels)) {
-
+ for_each_set_bit(channel, &hvs_state->unassigned_channels,
+ sizeof(hvs_state->unassigned_channels)) {
if (!(BIT(channel) & vc4_crtc->data->hvs_available_channels))
continue;
new_vc4_crtc_state->assigned_channel = channel;
- unassigned_channels &= ~BIT(channel);
+ hvs_state->unassigned_channels &= ~BIT(channel);
is_assigned = true;
break;
}
if (ret)
goto ctm_fini;
+ ret = vc4_hvs_channels_obj_init(vc4);
+ if (ret)
+ goto load_tracker_fini;
+
drm_mode_config_reset(dev);
drm_kms_helper_poll_init(dev);
return 0;
+load_tracker_fini:
+ vc4_load_tracker_obj_fini(vc4);
+
ctm_fini:
vc4_ctm_obj_fini(vc4);