diff options
Diffstat (limited to 'drivers/gpu/drm/omapdrm/omap_drv.c')
-rw-r--r-- | drivers/gpu/drm/omapdrm/omap_drv.c | 307 |
1 files changed, 107 insertions, 200 deletions
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index e1f47f0b3ccf..cdf5b0601eba 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -17,7 +17,7 @@ * this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <linux/wait.h> +#include <linux/sys_soc.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> @@ -54,23 +54,16 @@ static void omap_fb_output_poll_changed(struct drm_device *dev) drm_fb_helper_hotplug_event(priv->fbdev); } -struct omap_atomic_state_commit { - struct work_struct work; - struct drm_device *dev; - struct drm_atomic_state *state; - u32 crtcs; -}; - static void omap_atomic_wait_for_completion(struct drm_device *dev, struct drm_atomic_state *old_state) { - struct drm_crtc_state *old_crtc_state; + struct drm_crtc_state *new_crtc_state; struct drm_crtc *crtc; unsigned int i; int ret; - for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { - if (!crtc->state->enable) + for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) { + if (!new_crtc_state->active) continue; ret = omap_crtc_wait_pending(crtc); @@ -81,128 +74,67 @@ static void omap_atomic_wait_for_completion(struct drm_device *dev, } } -static void omap_atomic_complete(struct omap_atomic_state_commit *commit) +static void omap_atomic_commit_tail(struct drm_atomic_state *old_state) { - struct drm_device *dev = commit->dev; + struct drm_device *dev = old_state->dev; struct omap_drm_private *priv = dev->dev_private; - struct drm_atomic_state *old_state = commit->state; - /* Apply the atomic update. */ priv->dispc_ops->runtime_get(); + /* Apply the atomic update. */ drm_atomic_helper_commit_modeset_disables(dev, old_state); - /* With the current dss dispc implementation we have to enable - * the new modeset before we can commit planes. The dispc ovl - * configuration relies on the video mode configuration been - * written into the HW when the ovl configuration is - * calculated. - * - * This approach is not ideal because after a mode change the - * plane update is executed only after the first vblank - * interrupt. The dispc implementation should be fixed so that - * it is able use uncommitted drm state information. - */ - drm_atomic_helper_commit_modeset_enables(dev, old_state); - omap_atomic_wait_for_completion(dev, old_state); - - drm_atomic_helper_commit_planes(dev, old_state, 0); - - omap_atomic_wait_for_completion(dev, old_state); - - drm_atomic_helper_cleanup_planes(dev, old_state); - - priv->dispc_ops->runtime_put(); - - drm_atomic_state_put(old_state); - - /* Complete the commit, wake up any waiter. */ - spin_lock(&priv->commit.lock); - priv->commit.pending &= ~commit->crtcs; - spin_unlock(&priv->commit.lock); + if (priv->omaprev != 0x3430) { + /* With the current dss dispc implementation we have to enable + * the new modeset before we can commit planes. The dispc ovl + * configuration relies on the video mode configuration been + * written into the HW when the ovl configuration is + * calculated. + * + * This approach is not ideal because after a mode change the + * plane update is executed only after the first vblank + * interrupt. The dispc implementation should be fixed so that + * it is able use uncommitted drm state information. + */ + drm_atomic_helper_commit_modeset_enables(dev, old_state); + omap_atomic_wait_for_completion(dev, old_state); - wake_up_all(&priv->commit.wait); + drm_atomic_helper_commit_planes(dev, old_state, 0); - kfree(commit); -} - -static void omap_atomic_work(struct work_struct *work) -{ - struct omap_atomic_state_commit *commit = - container_of(work, struct omap_atomic_state_commit, work); - - omap_atomic_complete(commit); -} - -static bool omap_atomic_is_pending(struct omap_drm_private *priv, - struct omap_atomic_state_commit *commit) -{ - bool pending; + drm_atomic_helper_commit_hw_done(old_state); + } else { + /* + * OMAP3 DSS seems to have issues with the work-around above, + * resulting in endless sync losts if a crtc is enabled without + * a plane. For now, skip the WA for OMAP3. + */ + drm_atomic_helper_commit_planes(dev, old_state, 0); - spin_lock(&priv->commit.lock); - pending = priv->commit.pending & commit->crtcs; - spin_unlock(&priv->commit.lock); + drm_atomic_helper_commit_modeset_enables(dev, old_state); - return pending; -} - -static int omap_atomic_commit(struct drm_device *dev, - struct drm_atomic_state *state, bool nonblock) -{ - struct omap_drm_private *priv = dev->dev_private; - struct omap_atomic_state_commit *commit; - struct drm_crtc *crtc; - struct drm_crtc_state *crtc_state; - int i, ret; - - ret = drm_atomic_helper_prepare_planes(dev, state); - if (ret) - return ret; - - /* Allocate the commit object. */ - commit = kzalloc(sizeof(*commit), GFP_KERNEL); - if (commit == NULL) { - ret = -ENOMEM; - goto error; + drm_atomic_helper_commit_hw_done(old_state); } - INIT_WORK(&commit->work, omap_atomic_work); - commit->dev = dev; - commit->state = state; - - /* Wait until all affected CRTCs have completed previous commits and - * mark them as pending. + /* + * Wait for completion of the page flips to ensure that old buffers + * can't be touched by the hardware anymore before cleaning up planes. */ - for_each_crtc_in_state(state, crtc, crtc_state, i) - commit->crtcs |= drm_crtc_mask(crtc); - - wait_event(priv->commit.wait, !omap_atomic_is_pending(priv, commit)); - - spin_lock(&priv->commit.lock); - priv->commit.pending |= commit->crtcs; - spin_unlock(&priv->commit.lock); - - /* Swap the state, this is the point of no return. */ - drm_atomic_helper_swap_state(state, true); - - drm_atomic_state_get(state); - if (nonblock) - schedule_work(&commit->work); - else - omap_atomic_complete(commit); + omap_atomic_wait_for_completion(dev, old_state); - return 0; + drm_atomic_helper_cleanup_planes(dev, old_state); -error: - drm_atomic_helper_cleanup_planes(dev, state); - return ret; + priv->dispc_ops->runtime_put(); } +static const struct drm_mode_config_helper_funcs omap_mode_config_helper_funcs = { + .atomic_commit_tail = omap_atomic_commit_tail, +}; + static const struct drm_mode_config_funcs omap_mode_config_funcs = { .fb_create = omap_framebuffer_create, .output_poll_changed = omap_fb_output_poll_changed, .atomic_check = drm_atomic_helper_check, - .atomic_commit = omap_atomic_commit, + .atomic_commit = drm_atomic_helper_commit, }; static int get_connector_type(struct omap_dss_device *dssdev) @@ -214,6 +146,14 @@ static int get_connector_type(struct omap_dss_device *dssdev) return DRM_MODE_CONNECTOR_DVID; case OMAP_DISPLAY_TYPE_DSI: return DRM_MODE_CONNECTOR_DSI; + case OMAP_DISPLAY_TYPE_DPI: + case OMAP_DISPLAY_TYPE_DBI: + return DRM_MODE_CONNECTOR_DPI; + case OMAP_DISPLAY_TYPE_VENC: + /* TODO: This could also be composite */ + return DRM_MODE_CONNECTOR_SVIDEO; + case OMAP_DISPLAY_TYPE_SDI: + return DRM_MODE_CONNECTOR_LVDS; default: return DRM_MODE_CONNECTOR_Unknown; } @@ -261,8 +201,10 @@ cleanup: static int omap_modeset_init_properties(struct drm_device *dev) { struct omap_drm_private *priv = dev->dev_private; + unsigned int num_planes = priv->dispc_ops->get_num_ovls(); - priv->zorder_prop = drm_property_create_range(dev, 0, "zorder", 0, 3); + priv->zorder_prop = drm_property_create_range(dev, 0, "zorder", 0, + num_planes - 1); if (!priv->zorder_prop) return -ENOMEM; @@ -385,6 +327,7 @@ static int omap_modeset_init(struct drm_device *dev) dev->mode_config.max_height = 2048; dev->mode_config.funcs = &omap_mode_config_funcs; + dev->mode_config.helper_private = &omap_mode_config_helper_funcs; drm_mode_config_reset(dev); @@ -394,6 +337,32 @@ static int omap_modeset_init(struct drm_device *dev) } /* + * Enable the HPD in external components if supported + */ +static void omap_modeset_enable_external_hpd(void) +{ + struct omap_dss_device *dssdev = NULL; + + for_each_dss_dev(dssdev) { + if (dssdev->driver->enable_hpd) + dssdev->driver->enable_hpd(dssdev); + } +} + +/* + * Disable the HPD in external components if supported + */ +static void omap_modeset_disable_external_hpd(void) +{ + struct omap_dss_device *dssdev = NULL; + + for_each_dss_dev(dssdev) { + if (dssdev->driver->disable_hpd) + dssdev->driver->disable_hpd(dssdev); + } +} + +/* * drm ioctl funcs */ @@ -447,53 +416,6 @@ static int ioctl_gem_new(struct drm_device *dev, void *data, &args->handle); } -static int ioctl_gem_cpu_prep(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_omap_gem_cpu_prep *args = data; - struct drm_gem_object *obj; - int ret; - - VERB("%p:%p: handle=%d, op=%x", dev, file_priv, args->handle, args->op); - - obj = drm_gem_object_lookup(file_priv, args->handle); - if (!obj) - return -ENOENT; - - ret = omap_gem_op_sync(obj, args->op); - - if (!ret) - ret = omap_gem_op_start(obj, args->op); - - drm_gem_object_unreference_unlocked(obj); - - return ret; -} - -static int ioctl_gem_cpu_fini(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_omap_gem_cpu_fini *args = data; - struct drm_gem_object *obj; - int ret; - - VERB("%p:%p: handle=%d", dev, file_priv, args->handle); - - obj = drm_gem_object_lookup(file_priv, args->handle); - if (!obj) - return -ENOENT; - - /* XXX flushy, flushy */ - ret = 0; - - if (!ret) - ret = omap_gem_op_finish(obj, args->op); - - drm_gem_object_unreference_unlocked(obj); - - return ret; -} - static int ioctl_gem_info(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -522,9 +444,11 @@ static const struct drm_ioctl_desc ioctls[DRM_COMMAND_END - DRM_COMMAND_BASE] = DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY), DRM_IOCTL_DEF_DRV(OMAP_GEM_NEW, ioctl_gem_new, DRM_AUTH | DRM_RENDER_ALLOW), - DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_PREP, ioctl_gem_cpu_prep, + /* Deprecated, to be removed. */ + DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_PREP, drm_noop, DRM_AUTH | DRM_RENDER_ALLOW), - DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_FINI, ioctl_gem_cpu_fini, + /* Deprecated, to be removed. */ + DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_FINI, drm_noop, DRM_AUTH | DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(OMAP_GEM_INFO, ioctl_gem_info, DRM_AUTH | DRM_RENDER_ALLOW), @@ -553,44 +477,11 @@ static int dev_open(struct drm_device *dev, struct drm_file *file) */ static void dev_lastclose(struct drm_device *dev) { - int i; - - /* we don't support vga_switcheroo.. so just make sure the fbdev - * mode is active - */ struct omap_drm_private *priv = dev->dev_private; int ret; DBG("lastclose: dev=%p", dev); - /* need to restore default rotation state.. not sure - * if there is a cleaner way to restore properties to - * default state? Maybe a flag that properties should - * automatically be restored to default state on - * lastclose? - */ - for (i = 0; i < priv->num_crtcs; i++) { - struct drm_crtc *crtc = priv->crtcs[i]; - - if (!crtc->primary->rotation_property) - continue; - - drm_object_property_set_value(&crtc->base, - crtc->primary->rotation_property, - DRM_ROTATE_0); - } - - for (i = 0; i < priv->num_planes; i++) { - struct drm_plane *plane = priv->planes[i]; - - if (!plane->rotation_property) - continue; - - drm_object_property_set_value(&plane->base, - plane->rotation_property, - DRM_ROTATE_0); - } - if (priv->fbdev) { ret = drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev); if (ret) @@ -608,6 +499,7 @@ static const struct file_operations omapdriver_fops = { .owner = THIS_MODULE, .open = drm_open, .unlocked_ioctl = drm_ioctl, + .compat_ioctl = drm_compat_ioctl, .release = drm_release, .mmap = omap_gem_mmap, .poll = drm_poll, @@ -631,7 +523,6 @@ static struct drm_driver omap_drm_driver = { .gem_vm_ops = &omap_gem_vm_ops, .dumb_create = omap_gem_dumb_create, .dumb_map_offset = omap_gem_dumb_map_offset, - .dumb_destroy = drm_gem_dumb_destroy, .ioctls = ioctls, .num_ioctls = DRM_OMAP_NUM_IOCTLS, .fops = &omapdriver_fops, @@ -643,9 +534,17 @@ static struct drm_driver omap_drm_driver = { .patchlevel = DRIVER_PATCHLEVEL, }; +static const struct soc_device_attribute omapdrm_soc_devices[] = { + { .family = "OMAP3", .data = (void *)0x3430 }, + { .family = "OMAP4", .data = (void *)0x4430 }, + { .family = "OMAP5", .data = (void *)0x5430 }, + { .family = "DRA7", .data = (void *)0x0752 }, + { /* sentinel */ } +}; + static int pdev_probe(struct platform_device *pdev) { - struct omap_drm_platform_data *pdata = pdev->dev.platform_data; + const struct soc_device_attribute *soc; struct omap_drm_private *priv; struct drm_device *ddev; unsigned int i; @@ -656,6 +555,12 @@ static int pdev_probe(struct platform_device *pdev) if (omapdss_is_initialized() == false) return -EPROBE_DEFER; + ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "Failed to set the DMA mask\n"); + return ret; + } + omap_crtc_pre_init(); ret = omap_connect_dssdevs(); @@ -671,11 +576,10 @@ static int pdev_probe(struct platform_device *pdev) priv->dispc_ops = dispc_get_ops(); - priv->omaprev = pdata->omaprev; + soc = soc_device_match(omapdrm_soc_devices); + priv->omaprev = soc ? (unsigned int)soc->data : 0; priv->wq = alloc_ordered_workqueue("omapdrm", 0); - init_waitqueue_head(&priv->commit.wait); - spin_lock_init(&priv->commit.lock); spin_lock_init(&priv->list_lock); INIT_LIST_HEAD(&priv->obj_list); @@ -710,6 +614,7 @@ static int pdev_probe(struct platform_device *pdev) priv->fbdev = omap_fbdev_init(ddev); drm_kms_helper_poll_init(ddev); + omap_modeset_enable_external_hpd(); /* * Register the DRM device with the core and the connectors with @@ -722,6 +627,7 @@ static int pdev_probe(struct platform_device *pdev) return 0; err_cleanup_helpers: + omap_modeset_disable_external_hpd(); drm_kms_helper_poll_fini(ddev); if (priv->fbdev) omap_fbdev_free(ddev); @@ -750,6 +656,7 @@ static int pdev_remove(struct platform_device *pdev) drm_dev_unregister(ddev); + omap_modeset_disable_external_hpd(); drm_kms_helper_poll_fini(ddev); if (priv->fbdev) @@ -841,7 +748,7 @@ static SIMPLE_DEV_PM_OPS(omapdrm_pm_ops, omap_drm_suspend, omap_drm_resume); static struct platform_driver pdev = { .driver = { - .name = DRIVER_NAME, + .name = "omapdrm", .pm = &omapdrm_pm_ops, }, .probe = pdev_probe, |