diff options
Diffstat (limited to 'drivers/gpu/drm/arm/display/komeda/komeda_kms.c')
-rw-r--r-- | drivers/gpu/drm/arm/display/komeda/komeda_kms.c | 130 |
1 files changed, 121 insertions, 9 deletions
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_kms.c b/drivers/gpu/drm/arm/display/komeda/komeda_kms.c index 86f6542afb40..419a8b0e5de8 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_kms.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_kms.c @@ -58,7 +58,6 @@ static struct drm_driver komeda_kms_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_PRIME | DRIVER_HAVE_IRQ, .lastclose = drm_fb_helper_lastclose, - .irq_handler = komeda_kms_irq_handler, .gem_free_object_unlocked = drm_gem_cma_free_object, .gem_vm_ops = &drm_gem_cma_vm_ops, .dumb_create = komeda_gem_cma_dumb_create, @@ -100,6 +99,108 @@ static const struct drm_mode_config_helper_funcs komeda_mode_config_helpers = { .atomic_commit_tail = komeda_kms_commit_tail, }; +static int komeda_plane_state_list_add(struct drm_plane_state *plane_st, + struct list_head *zorder_list) +{ + struct komeda_plane_state *new = to_kplane_st(plane_st); + struct komeda_plane_state *node, *last; + + last = list_empty(zorder_list) ? + NULL : list_last_entry(zorder_list, typeof(*last), zlist_node); + + /* Considering the list sequence is zpos increasing, so if list is empty + * or the zpos of new node bigger than the last node in list, no need + * loop and just insert the new one to the tail of the list. + */ + if (!last || (new->base.zpos > last->base.zpos)) { + list_add_tail(&new->zlist_node, zorder_list); + return 0; + } + + /* Build the list by zpos increasing */ + list_for_each_entry(node, zorder_list, zlist_node) { + if (new->base.zpos < node->base.zpos) { + list_add_tail(&new->zlist_node, &node->zlist_node); + break; + } else if (node->base.zpos == new->base.zpos) { + struct drm_plane *a = node->base.plane; + struct drm_plane *b = new->base.plane; + + /* Komeda doesn't support setting a same zpos for + * different planes. + */ + DRM_DEBUG_ATOMIC("PLANE: %s and PLANE: %s are configured same zpos: %d.\n", + a->name, b->name, node->base.zpos); + return -EINVAL; + } + } + + return 0; +} + +static int komeda_crtc_normalize_zpos(struct drm_crtc *crtc, + struct drm_crtc_state *crtc_st) +{ + struct drm_atomic_state *state = crtc_st->state; + struct komeda_crtc *kcrtc = to_kcrtc(crtc); + struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(crtc_st); + struct komeda_plane_state *kplane_st; + struct drm_plane_state *plane_st; + struct drm_framebuffer *fb; + struct drm_plane *plane; + struct list_head zorder_list; + int order = 0, err; + + DRM_DEBUG_ATOMIC("[CRTC:%d:%s] calculating normalized zpos values\n", + crtc->base.id, crtc->name); + + INIT_LIST_HEAD(&zorder_list); + + /* This loop also added all effected planes into the new state */ + drm_for_each_plane_mask(plane, crtc->dev, crtc_st->plane_mask) { + plane_st = drm_atomic_get_plane_state(state, plane); + if (IS_ERR(plane_st)) + return PTR_ERR(plane_st); + + /* Build a list by zpos increasing */ + err = komeda_plane_state_list_add(plane_st, &zorder_list); + if (err) + return err; + } + + kcrtc_st->max_slave_zorder = 0; + + list_for_each_entry(kplane_st, &zorder_list, zlist_node) { + plane_st = &kplane_st->base; + fb = plane_st->fb; + plane = plane_st->plane; + + plane_st->normalized_zpos = order++; + /* When layer_split has been enabled, one plane will be handled + * by two separated komeda layers (left/right), which may needs + * two zorders. + * - zorder: for left_layer for left display part. + * - zorder + 1: will be reserved for right layer. + */ + if (to_kplane_st(plane_st)->layer_split) + order++; + + DRM_DEBUG_ATOMIC("[PLANE:%d:%s] zpos:%d, normalized zpos: %d\n", + plane->base.id, plane->name, + plane_st->zpos, plane_st->normalized_zpos); + + /* calculate max slave zorder */ + if (has_bit(drm_plane_index(plane), kcrtc->slave_planes)) + kcrtc_st->max_slave_zorder = + max(plane_st->normalized_zpos, + kcrtc_st->max_slave_zorder); + } + + crtc_st->zpos_changed = true; + + return 0; +} + static int komeda_kms_check(struct drm_device *dev, struct drm_atomic_state *state) { @@ -111,7 +212,7 @@ static int komeda_kms_check(struct drm_device *dev, if (err) return err; - /* komeda need to re-calculate resource assumption in every commit + /* Komeda need to re-calculate resource assumption in every commit * so need to add all affected_planes (even unchanged) to * drm_atomic_state. */ @@ -119,6 +220,10 @@ static int komeda_kms_check(struct drm_device *dev, err = drm_atomic_add_affected_planes(state, crtc); if (err) return err; + + err = komeda_crtc_normalize_zpos(crtc, new_crtc_st); + if (err) + return err; } err = drm_atomic_helper_check_planes(dev, state); @@ -148,7 +253,7 @@ static void komeda_kms_mode_config_init(struct komeda_kms_dev *kms, config->min_height = 0; config->max_width = 4096; config->max_height = 4096; - config->allow_fb_modifiers = false; + config->allow_fb_modifiers = true; config->funcs = &komeda_mode_config_funcs; config->helper_private = &komeda_mode_config_helpers; @@ -188,29 +293,36 @@ struct komeda_kms_dev *komeda_kms_attach(struct komeda_dev *mdev) if (err) goto cleanup_mode_config; + err = komeda_kms_add_wb_connectors(kms, mdev); + if (err) + goto cleanup_mode_config; + err = component_bind_all(mdev->dev, kms); if (err) goto cleanup_mode_config; drm_mode_config_reset(drm); - err = drm_irq_install(drm, mdev->irq); + err = devm_request_irq(drm->dev, mdev->irq, + komeda_kms_irq_handler, IRQF_SHARED, + drm->driver->name, drm); if (err) goto cleanup_mode_config; err = mdev->funcs->enable_irq(mdev); if (err) - goto uninstall_irq; + goto cleanup_mode_config; + + drm->irq_enabled = true; err = drm_dev_register(drm, 0); if (err) - goto uninstall_irq; + goto cleanup_mode_config; return kms; -uninstall_irq: - drm_irq_uninstall(drm); cleanup_mode_config: + drm->irq_enabled = false; drm_mode_config_cleanup(drm); komeda_kms_cleanup_private_objs(kms); free_kms: @@ -223,9 +335,9 @@ void komeda_kms_detach(struct komeda_kms_dev *kms) struct drm_device *drm = &kms->base; struct komeda_dev *mdev = drm->dev_private; + drm->irq_enabled = false; mdev->funcs->disable_irq(mdev); drm_dev_unregister(drm); - drm_irq_uninstall(drm); component_unbind_all(mdev->dev, drm); komeda_kms_cleanup_private_objs(kms); drm_mode_config_cleanup(drm); |