From 6da9f89172b94411896130c6e1acf159da3dc760 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Thu, 24 Oct 2013 09:50:50 +0300 Subject: drm/omap: fix (un)registering irqs inside an irq handler omapdrm (un)registers irqs inside an irq handler. The problem is that the (un)register function uses dispc_runtime_get/put() to enable the clocks, and those functions are not irq safe by default. This was kind of fixed in 48664b21aeeffb40c5fa06843f18052e2c4ec9ef (OMAPDSS: DISPC: set irq_safe for runtime PM), which makes dispc's runtime calls irq-safe. However, using pm_runtime_irq_safe in dispc makes the parent of dispc, dss, always enabled, effectively preventing PM for the whole DSS module. This patch makes omapdrm behave better by adding new irq (un)register functions that do not use dispc_runtime_get/put, and using those functions in interrupt context. Thus we can make dispc again non-irq-safe, allowing proper PM. Signed-off-by: Tomi Valkeinen Cc: Rob Clark diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index 0fd2eb1..e6241c2 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -411,7 +411,7 @@ static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus) struct drm_crtc *crtc = &omap_crtc->base; DRM_ERROR("%s: errors: %08x\n", omap_crtc->name, irqstatus); /* avoid getting in a flood, unregister the irq until next vblank */ - omap_irq_unregister(crtc->dev, &omap_crtc->error_irq); + __omap_irq_unregister(crtc->dev, &omap_crtc->error_irq); } static void omap_crtc_apply_irq(struct omap_drm_irq *irq, uint32_t irqstatus) @@ -421,13 +421,13 @@ static void omap_crtc_apply_irq(struct omap_drm_irq *irq, uint32_t irqstatus) struct drm_crtc *crtc = &omap_crtc->base; if (!omap_crtc->error_irq.registered) - omap_irq_register(crtc->dev, &omap_crtc->error_irq); + __omap_irq_register(crtc->dev, &omap_crtc->error_irq); if (!dispc_mgr_go_busy(omap_crtc->channel)) { struct omap_drm_private *priv = crtc->dev->dev_private; DBG("%s: apply done", omap_crtc->name); - omap_irq_unregister(crtc->dev, &omap_crtc->apply_irq); + __omap_irq_unregister(crtc->dev, &omap_crtc->apply_irq); queue_work(priv->wq, &omap_crtc->apply_work); } } diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h index 0784769..e835c27 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.h +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -145,6 +145,8 @@ irqreturn_t omap_irq_handler(DRM_IRQ_ARGS); void omap_irq_preinstall(struct drm_device *dev); int omap_irq_postinstall(struct drm_device *dev); void omap_irq_uninstall(struct drm_device *dev); +void __omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq); +void __omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq); void omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq); void omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq); int omap_drm_irq_uninstall(struct drm_device *dev); diff --git a/drivers/gpu/drm/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c index cb85860..da690d0 100644 --- a/drivers/gpu/drm/omapdrm/omap_irq.c +++ b/drivers/gpu/drm/omapdrm/omap_irq.c @@ -45,12 +45,11 @@ static void omap_irq_update(struct drm_device *dev) dispc_read_irqenable(); /* flush posted write */ } -void omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq) +void __omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq) { struct omap_drm_private *priv = dev->dev_private; unsigned long flags; - dispc_runtime_get(); spin_lock_irqsave(&list_lock, flags); if (!WARN_ON(irq->registered)) { @@ -60,14 +59,21 @@ void omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq) } spin_unlock_irqrestore(&list_lock, flags); +} + +void omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq) +{ + dispc_runtime_get(); + + __omap_irq_register(dev, irq); + dispc_runtime_put(); } -void omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq) +void __omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq) { unsigned long flags; - dispc_runtime_get(); spin_lock_irqsave(&list_lock, flags); if (!WARN_ON(!irq->registered)) { @@ -77,6 +83,14 @@ void omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq) } spin_unlock_irqrestore(&list_lock, flags); +} + +void omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq) +{ + dispc_runtime_get(); + + __omap_irq_unregister(dev, irq); + dispc_runtime_put(); } diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index 4ec59ca..91c687f 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -3691,7 +3691,6 @@ static int __init omap_dispchw_probe(struct platform_device *pdev) } pm_runtime_enable(&pdev->dev); - pm_runtime_irq_safe(&pdev->dev); r = dispc_runtime_get(); if (r) -- cgit v0.10.2 From 3a01ab255637fdc2fbc97cf356f1b971c3fc0c64 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 2 Jan 2014 14:49:51 +0530 Subject: drm/omap: fix: Defer probe if an omapdss device requests for it at connect With the omapdss device model changes. omapdrm is required to call dssdriver's connect() op to register a panel. This is currently done in omap_modeset_init() A call to connect() can fail if the omapdss panels or the encoders(HDMI/DPI) they connect to have some resource(like regulators, I2C adapter) missing. If this happens, the correct approach is to defer omapdrm's probe. omapdrm currently ignores those panels which return a non zero value when connected. This could result in omapdrm ignoring all panels on an omap board. The right approach would be for omapdrm to request for probe deferral when a panel's connect op returns -EPROBE_DEFER. In order to do this, we need to call connect() much earlier during omapdrm's probe to prevent too many things are already done by then. We now connect the panels during pdev_probe(), before anything else is initialized, so that we don't need to undo too many things if a defer was requested. Now when we enter omap_modeset_init(), we have a set of panels that have been connected. We now proceed with registering only those panels that are already connected. A special case has to be considered when no panels are available to connect when omapdrm probes. In this case too, we defer probe and expect that a panel will be available to connect the next time. Checking whether the panel has a driver or whether it has get_timing/read_edid ops in omap_modeset_init() are redundant with the new display model. These can be removed since a dssdev device will always have a driver associated with it, and all dssdev drivers have a get_timings op. This will mainly fix cases when omapdrm is built-in the kernel, since that's generally where resources like regulators or I2C are unavailable because of probe order dependencies. In particular this fixes boot with omapdrm built-in on an omap4 panda ES board. The regulators used by HDMI(provided by I2C based TWL regulators) aren't initialized because I2C isn't initialized, I2C isn't initialized as it's pins are not configured because pinctrl is yet to probe. Signed-off-by: Archit Taneja Signed-off-by: Tomi Valkeinen diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index e6241c2..4313bb0 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -623,6 +623,11 @@ void omap_crtc_pre_init(void) dss_install_mgr_ops(&mgr_ops); } +void omap_crtc_pre_uninit(void) +{ + dss_uninstall_mgr_ops(); +} + /* initialize crtc */ struct drm_crtc *omap_crtc_init(struct drm_device *dev, struct drm_plane *plane, enum omap_channel channel, int id) diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index e7fa3cd..651f902 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -87,6 +87,43 @@ static bool channel_used(struct drm_device *dev, enum omap_channel channel) return false; } +static int omap_connect_dssdevs(void) +{ + int r; + struct omap_dss_device *dssdev = NULL; + bool no_displays = true; + + for_each_dss_dev(dssdev) { + r = dssdev->driver->connect(dssdev); + if (r == -EPROBE_DEFER) { + omap_dss_put_device(dssdev); + goto cleanup; + } else if (r) { + dev_warn(dssdev->dev, "could not connect display: %s\n", + dssdev->name); + } else { + no_displays = false; + } + } + + if (no_displays) + return -EPROBE_DEFER; + + return 0; + +cleanup: + /* + * if we are deferring probe, we disconnect the devices we previously + * connected + */ + dssdev = NULL; + + for_each_dss_dev(dssdev) + dssdev->driver->disconnect(dssdev); + + return r; +} + static int omap_modeset_init(struct drm_device *dev) { struct omap_drm_private *priv = dev->dev_private; @@ -95,9 +132,6 @@ static int omap_modeset_init(struct drm_device *dev) int num_mgrs = dss_feat_get_num_mgrs(); int num_crtcs; int i, id = 0; - int r; - - omap_crtc_pre_init(); drm_mode_config_init(dev); @@ -119,26 +153,8 @@ static int omap_modeset_init(struct drm_device *dev) enum omap_channel channel; struct omap_overlay_manager *mgr; - if (!dssdev->driver) { - dev_warn(dev->dev, "%s has no driver.. skipping it\n", - dssdev->name); - continue; - } - - if (!(dssdev->driver->get_timings || - dssdev->driver->read_edid)) { - dev_warn(dev->dev, "%s driver does not support " - "get_timings or read_edid.. skipping it!\n", - dssdev->name); - continue; - } - - r = dssdev->driver->connect(dssdev); - if (r) { - dev_err(dev->dev, "could not connect display: %s\n", - dssdev->name); + if (!omapdss_device_is_connected(dssdev)) continue; - } encoder = omap_encoder_init(dev, dssdev); @@ -655,9 +671,19 @@ static void pdev_shutdown(struct platform_device *device) static int pdev_probe(struct platform_device *device) { + int r; + if (omapdss_is_initialized() == false) return -EPROBE_DEFER; + omap_crtc_pre_init(); + + r = omap_connect_dssdevs(); + if (r) { + omap_crtc_pre_uninit(); + return r; + } + DBG("%s", device->name); return drm_platform_init(&omap_drm_driver, device); } diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h index e835c27..26dfd21 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.h +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -160,6 +160,7 @@ enum omap_channel omap_crtc_channel(struct drm_crtc *crtc); int omap_crtc_apply(struct drm_crtc *crtc, struct omap_drm_apply *apply); void omap_crtc_pre_init(void); +void omap_crtc_pre_uninit(void); struct drm_crtc *omap_crtc_init(struct drm_device *dev, struct drm_plane *plane, enum omap_channel channel, int id); -- cgit v0.10.2 From cc823bdcfb468808467a9f23186a10404f573fc4 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 2 Jan 2014 14:49:52 +0530 Subject: drm/omap: fix: disconnect devices when omapdrm module is removed At omapdrm probe, we install manager ops and connect omapdss devices. This needs to be undone when omapdrm module is removed so that omapdss is in a clean state. This ensures that we can re-insert omapdrm module, or some other module which uses omapdss(like omapfb/omap_vout). Currently, omapdrm's remove neither uninstalls manager ops, or disconnects omapdss devices. We make sure that this is done in pdev_remove. omapdrm establishes connections for omap_dss_device devices when probed. It should also be responsible to disconnect the devices. Keeping the devices connected can prevent the panel driver modules from unloading, it also causes issues when we try to remove or re-insert omapdrm module. Signed-off-by: Archit Taneja Signed-off-by: Tomi Valkeinen diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 651f902..fca5667 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -86,6 +86,13 @@ static bool channel_used(struct drm_device *dev, enum omap_channel channel) return false; } +static void omap_disconnect_dssdevs(void) +{ + struct omap_dss_device *dssdev = NULL; + + for_each_dss_dev(dssdev) + dssdev->driver->disconnect(dssdev); +} static int omap_connect_dssdevs(void) { @@ -116,10 +123,7 @@ cleanup: * if we are deferring probe, we disconnect the devices we previously * connected */ - dssdev = NULL; - - for_each_dss_dev(dssdev) - dssdev->driver->disconnect(dssdev); + omap_disconnect_dssdevs(); return r; } @@ -693,6 +697,9 @@ static int pdev_remove(struct platform_device *device) DBG(""); drm_platform_exit(&omap_drm_driver, device); + omap_disconnect_dssdevs(); + omap_crtc_pre_uninit(); + platform_driver_unregister(&omap_dmm_driver); return 0; } -- cgit v0.10.2 From ec72a81e27d735eb0b5f9325be3d88aa4cb5510a Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 2 Jan 2014 14:49:53 +0530 Subject: drm/omap: fix: disable encoder before destroying it Currently, an encoder is disabled only when an apply work is queued for the corresponding crtc. This works fine for the paths where userspace explicitly disables crtc, this results in disabling the omapdss device in the crtc's pre_apply function. However, when the omapdrm module is removed, there is no work queued to ensure that the encoder is disabled. This can result in an enabled omapdss device when removing omapdrm. omapdss is left in an inconsistent state, and that prevents us from using that omapdss device being used again. Disable enabled encoders in omap_encoder_destroy, we could consider a better place for doing this later. Signed-off-by: Archit Taneja Signed-off-by: Tomi Valkeinen diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c index 6a12e89..5290a88 100644 --- a/drivers/gpu/drm/omapdrm/omap_encoder.c +++ b/drivers/gpu/drm/omapdrm/omap_encoder.c @@ -51,6 +51,9 @@ struct omap_dss_device *omap_encoder_get_dssdev(struct drm_encoder *encoder) static void omap_encoder_destroy(struct drm_encoder *encoder) { struct omap_encoder *omap_encoder = to_omap_encoder(encoder); + + omap_encoder_set_enabled(encoder, false); + drm_encoder_cleanup(encoder); kfree(omap_encoder); } -- cgit v0.10.2 From 80e4ed5411dddbafe90083a6c82dd87e88fde0d7 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 2 Jan 2014 14:49:54 +0530 Subject: drm/omap: fix: change dev_unload order The current dev_unload order uninits the irqs too early. In the current sequence, it's possible that a crtc queues work(apply_worker) to display a buffer, which registers to omap_crtc_apply_irq to notfiy the completion of the configuration we applied. Calling drm_vblank_cleanup and omap_drm_irq_uninstall here causes the crtc's apply handler to never get called, which results in an incorrect state of the apply_irq.registered parameter. This condition occurs where there is no mode set via omapdrm, and dev_lastclose tries to set a default fb mode via drm_fb_helper_restore_fbdev_mode. The apply work scheduled by restore_fbdev_mode is very close in time to the disabling of the irq handler, and hence leads to a race condition. We move the irq cleanup at the end of the unload sequence to prevent this. Also, the call to flush_workqueue is removed since it's called internally by destroy_workqueue. Signed-off-by: Archit Taneja Signed-off-by: Tomi Valkeinen diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index fca5667..c57e99c 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -517,16 +517,16 @@ static int dev_unload(struct drm_device *dev) DBG("unload: dev=%p", dev); drm_kms_helper_poll_fini(dev); - drm_vblank_cleanup(dev); - omap_drm_irq_uninstall(dev); omap_fbdev_free(dev); omap_modeset_free(dev); omap_gem_deinit(dev); - flush_workqueue(priv->wq); destroy_workqueue(priv->wq); + drm_vblank_cleanup(dev); + omap_drm_irq_uninstall(dev); + kfree(dev->dev_private); dev->dev_private = NULL; -- cgit v0.10.2 From 3d232346c5656b300028b6c920ddc10b229b5264 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Tue, 15 Oct 2013 12:34:20 +0530 Subject: drm/omap: Enable DT support for DMM Enable use of DT for DMM/Tiler. Originally worked on by Andy Gross Cc: Andy Gross Cc: DRI Development Signed-off-by: Archit Taneja [tomi.valkeinen@ti.com: use of_match_ptr()] Signed-off-by: Tomi Valkeinen diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c index 701c4c1..f926b4c 100644 --- a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c +++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c @@ -969,12 +969,21 @@ static const struct dev_pm_ops omap_dmm_pm_ops = { }; #endif +#if defined(CONFIG_OF) +static const struct of_device_id dmm_of_match[] = { + { .compatible = "ti,omap4-dmm", }, + { .compatible = "ti,omap5-dmm", }, + {}, +}; +#endif + struct platform_driver omap_dmm_driver = { .probe = omap_dmm_probe, .remove = omap_dmm_remove, .driver = { .owner = THIS_MODULE, .name = DMM_DRIVER_NAME, + .of_match_table = of_match_ptr(dmm_of_match), #ifdef CONFIG_PM .pm = &omap_dmm_pm_ops, #endif -- cgit v0.10.2